drm/kms: fix spelling of "CLOCK"
[safe/jmp/linux-2.6] / drivers / gpu / drm / drm_fb_helper.c
1 /*
2  * Copyright (c) 2006-2009 Red Hat Inc.
3  * Copyright (c) 2006-2008 Intel Corporation
4  * Copyright (c) 2007 Dave Airlie <airlied@linux.ie>
5  *
6  * DRM framebuffer helper functions
7  *
8  * Permission to use, copy, modify, distribute, and sell this software and its
9  * documentation for any purpose is hereby granted without fee, provided that
10  * the above copyright notice appear in all copies and that both that copyright
11  * notice and this permission notice appear in supporting documentation, and
12  * that the name of the copyright holders not be used in advertising or
13  * publicity pertaining to distribution of the software without specific,
14  * written prior permission.  The copyright holders make no representations
15  * about the suitability of this software for any purpose.  It is provided "as
16  * is" without express or implied warranty.
17  *
18  * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
19  * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
20  * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
21  * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
22  * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
23  * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
24  * OF THIS SOFTWARE.
25  *
26  * Authors:
27  *      Dave Airlie <airlied@linux.ie>
28  *      Jesse Barnes <jesse.barnes@intel.com>
29  */
30 #include <linux/kernel.h>
31 #include <linux/sysrq.h>
32 #include <linux/fb.h>
33 #include "drmP.h"
34 #include "drm_crtc.h"
35 #include "drm_fb_helper.h"
36 #include "drm_crtc_helper.h"
37
38 MODULE_AUTHOR("David Airlie, Jesse Barnes");
39 MODULE_DESCRIPTION("DRM KMS helper");
40 MODULE_LICENSE("GPL and additional rights");
41
42 static LIST_HEAD(kernel_fb_helper_list);
43
44 int drm_fb_helper_add_connector(struct drm_connector *connector)
45 {
46         connector->fb_helper_private = kzalloc(sizeof(struct drm_fb_helper_connector), GFP_KERNEL);
47         if (!connector->fb_helper_private)
48                 return -ENOMEM;
49
50         return 0;
51 }
52 EXPORT_SYMBOL(drm_fb_helper_add_connector);
53
54 /**
55  * drm_fb_helper_connector_parse_command_line - parse command line for connector
56  * @connector - connector to parse line for
57  * @mode_option - per connector mode option
58  *
59  * This parses the connector specific then generic command lines for
60  * modes and options to configure the connector.
61  *
62  * This uses the same parameters as the fb modedb.c, except for extra
63  *      <xres>x<yres>[M][R][-<bpp>][@<refresh>][i][m][eDd]
64  *
65  * enable/enable Digital/disable bit at the end
66  */
67 static bool drm_fb_helper_connector_parse_command_line(struct drm_connector *connector,
68                                                        const char *mode_option)
69 {
70         const char *name;
71         unsigned int namelen;
72         int res_specified = 0, bpp_specified = 0, refresh_specified = 0;
73         unsigned int xres = 0, yres = 0, bpp = 32, refresh = 0;
74         int yres_specified = 0, cvt = 0, rb = 0, interlace = 0, margins = 0;
75         int i;
76         enum drm_connector_force force = DRM_FORCE_UNSPECIFIED;
77         struct drm_fb_helper_connector *fb_help_conn = connector->fb_helper_private;
78         struct drm_fb_helper_cmdline_mode *cmdline_mode;
79
80         if (!fb_help_conn)
81                 return false;
82
83         cmdline_mode = &fb_help_conn->cmdline_mode;
84         if (!mode_option)
85                 mode_option = fb_mode_option;
86
87         if (!mode_option) {
88                 cmdline_mode->specified = false;
89                 return false;
90         }
91
92         name = mode_option;
93         namelen = strlen(name);
94         for (i = namelen-1; i >= 0; i--) {
95                 switch (name[i]) {
96                 case '@':
97                         namelen = i;
98                         if (!refresh_specified && !bpp_specified &&
99                             !yres_specified) {
100                                 refresh = simple_strtol(&name[i+1], NULL, 10);
101                                 refresh_specified = 1;
102                                 if (cvt || rb)
103                                         cvt = 0;
104                         } else
105                                 goto done;
106                         break;
107                 case '-':
108                         namelen = i;
109                         if (!bpp_specified && !yres_specified) {
110                                 bpp = simple_strtol(&name[i+1], NULL, 10);
111                                 bpp_specified = 1;
112                                 if (cvt || rb)
113                                         cvt = 0;
114                         } else
115                                 goto done;
116                         break;
117                 case 'x':
118                         if (!yres_specified) {
119                                 yres = simple_strtol(&name[i+1], NULL, 10);
120                                 yres_specified = 1;
121                         } else
122                                 goto done;
123                 case '0' ... '9':
124                         break;
125                 case 'M':
126                         if (!yres_specified)
127                                 cvt = 1;
128                         break;
129                 case 'R':
130                         if (!cvt)
131                                 rb = 1;
132                         break;
133                 case 'm':
134                         if (!cvt)
135                                 margins = 1;
136                         break;
137                 case 'i':
138                         if (!cvt)
139                                 interlace = 1;
140                         break;
141                 case 'e':
142                         force = DRM_FORCE_ON;
143                         break;
144                 case 'D':
145                         if ((connector->connector_type != DRM_MODE_CONNECTOR_DVII) &&
146                             (connector->connector_type != DRM_MODE_CONNECTOR_HDMIB))
147                                 force = DRM_FORCE_ON;
148                         else
149                                 force = DRM_FORCE_ON_DIGITAL;
150                         break;
151                 case 'd':
152                         force = DRM_FORCE_OFF;
153                         break;
154                 default:
155                         goto done;
156                 }
157         }
158         if (i < 0 && yres_specified) {
159                 xres = simple_strtol(name, NULL, 10);
160                 res_specified = 1;
161         }
162 done:
163
164         DRM_DEBUG_KMS("cmdline mode for connector %s %dx%d@%dHz%s%s%s\n",
165                 drm_get_connector_name(connector), xres, yres,
166                 (refresh) ? refresh : 60, (rb) ? " reduced blanking" :
167                 "", (margins) ? " with margins" : "", (interlace) ?
168                 " interlaced" : "");
169
170         if (force) {
171                 const char *s;
172                 switch (force) {
173                 case DRM_FORCE_OFF: s = "OFF"; break;
174                 case DRM_FORCE_ON_DIGITAL: s = "ON - dig"; break;
175                 default:
176                 case DRM_FORCE_ON: s = "ON"; break;
177                 }
178
179                 DRM_INFO("forcing %s connector %s\n",
180                          drm_get_connector_name(connector), s);
181                 connector->force = force;
182         }
183
184         if (res_specified) {
185                 cmdline_mode->specified = true;
186                 cmdline_mode->xres = xres;
187                 cmdline_mode->yres = yres;
188         }
189
190         if (refresh_specified) {
191                 cmdline_mode->refresh_specified = true;
192                 cmdline_mode->refresh = refresh;
193         }
194
195         if (bpp_specified) {
196                 cmdline_mode->bpp_specified = true;
197                 cmdline_mode->bpp = bpp;
198         }
199         cmdline_mode->rb = rb ? true : false;
200         cmdline_mode->cvt = cvt  ? true : false;
201         cmdline_mode->interlace = interlace ? true : false;
202
203         return true;
204 }
205
206 int drm_fb_helper_parse_command_line(struct drm_device *dev)
207 {
208         struct drm_connector *connector;
209
210         list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
211                 char *option = NULL;
212
213                 /* do something on return - turn off connector maybe */
214                 if (fb_get_options(drm_get_connector_name(connector), &option))
215                         continue;
216
217                 drm_fb_helper_connector_parse_command_line(connector, option);
218         }
219         return 0;
220 }
221
222 bool drm_fb_helper_force_kernel_mode(void)
223 {
224         int i = 0;
225         bool ret, error = false;
226         struct drm_fb_helper *helper;
227
228         if (list_empty(&kernel_fb_helper_list))
229                 return false;
230
231         list_for_each_entry(helper, &kernel_fb_helper_list, kernel_fb_list) {
232                 for (i = 0; i < helper->crtc_count; i++) {
233                         struct drm_mode_set *mode_set = &helper->crtc_info[i].mode_set;
234                         ret = drm_crtc_helper_set_config(mode_set);
235                         if (ret)
236                                 error = true;
237                 }
238         }
239         return error;
240 }
241
242 int drm_fb_helper_panic(struct notifier_block *n, unsigned long ununsed,
243                         void *panic_str)
244 {
245         DRM_ERROR("panic occurred, switching back to text console\n");
246         return drm_fb_helper_force_kernel_mode();
247         return 0;
248 }
249 EXPORT_SYMBOL(drm_fb_helper_panic);
250
251 static struct notifier_block paniced = {
252         .notifier_call = drm_fb_helper_panic,
253 };
254
255 /**
256  * drm_fb_helper_restore - restore the framebuffer console (kernel) config
257  *
258  * Restore's the kernel's fbcon mode, used for lastclose & panic paths.
259  */
260 void drm_fb_helper_restore(void)
261 {
262         bool ret;
263         ret = drm_fb_helper_force_kernel_mode();
264         if (ret == true)
265                 DRM_ERROR("Failed to restore crtc configuration\n");
266 }
267 EXPORT_SYMBOL(drm_fb_helper_restore);
268
269 #ifdef CONFIG_MAGIC_SYSRQ
270 static void drm_fb_helper_restore_work_fn(struct work_struct *ignored)
271 {
272         drm_fb_helper_restore();
273 }
274 static DECLARE_WORK(drm_fb_helper_restore_work, drm_fb_helper_restore_work_fn);
275
276 static void drm_fb_helper_sysrq(int dummy1, struct tty_struct *dummy3)
277 {
278         schedule_work(&drm_fb_helper_restore_work);
279 }
280
281 static struct sysrq_key_op sysrq_drm_fb_helper_restore_op = {
282         .handler = drm_fb_helper_sysrq,
283         .help_msg = "force-fb(V)",
284         .action_msg = "Restore framebuffer console",
285 };
286 #endif
287
288 static void drm_fb_helper_on(struct fb_info *info)
289 {
290         struct drm_fb_helper *fb_helper = info->par;
291         struct drm_device *dev = fb_helper->dev;
292         struct drm_crtc *crtc;
293         struct drm_encoder *encoder;
294         int i;
295
296         /*
297          * For each CRTC in this fb, turn the crtc on then,
298          * find all associated encoders and turn them on.
299          */
300         for (i = 0; i < fb_helper->crtc_count; i++) {
301                 list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
302                         struct drm_crtc_helper_funcs *crtc_funcs =
303                                 crtc->helper_private;
304
305                         /* Only mess with CRTCs in this fb */
306                         if (crtc->base.id != fb_helper->crtc_info[i].crtc_id ||
307                             !crtc->enabled)
308                                 continue;
309
310                         mutex_lock(&dev->mode_config.mutex);
311                         crtc_funcs->dpms(crtc, DRM_MODE_DPMS_ON);
312                         mutex_unlock(&dev->mode_config.mutex);
313
314                         /* Found a CRTC on this fb, now find encoders */
315                         list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
316                                 if (encoder->crtc == crtc) {
317                                         struct drm_encoder_helper_funcs *encoder_funcs;
318
319                                         encoder_funcs = encoder->helper_private;
320                                         mutex_lock(&dev->mode_config.mutex);
321                                         encoder_funcs->dpms(encoder, DRM_MODE_DPMS_ON);
322                                         mutex_unlock(&dev->mode_config.mutex);
323                                 }
324                         }
325                 }
326         }
327 }
328
329 static void drm_fb_helper_off(struct fb_info *info, int dpms_mode)
330 {
331         struct drm_fb_helper *fb_helper = info->par;
332         struct drm_device *dev = fb_helper->dev;
333         struct drm_crtc *crtc;
334         struct drm_encoder *encoder;
335         int i;
336
337         /*
338          * For each CRTC in this fb, find all associated encoders
339          * and turn them off, then turn off the CRTC.
340          */
341         for (i = 0; i < fb_helper->crtc_count; i++) {
342                 list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
343                         struct drm_crtc_helper_funcs *crtc_funcs =
344                                 crtc->helper_private;
345
346                         /* Only mess with CRTCs in this fb */
347                         if (crtc->base.id != fb_helper->crtc_info[i].crtc_id ||
348                             !crtc->enabled)
349                                 continue;
350
351                         /* Found a CRTC on this fb, now find encoders */
352                         list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
353                                 if (encoder->crtc == crtc) {
354                                         struct drm_encoder_helper_funcs *encoder_funcs;
355
356                                         encoder_funcs = encoder->helper_private;
357                                         mutex_lock(&dev->mode_config.mutex);
358                                         encoder_funcs->dpms(encoder, dpms_mode);
359                                         mutex_unlock(&dev->mode_config.mutex);
360                                 }
361                         }
362                         mutex_lock(&dev->mode_config.mutex);
363                         crtc_funcs->dpms(crtc, DRM_MODE_DPMS_OFF);
364                         mutex_unlock(&dev->mode_config.mutex);
365                 }
366         }
367 }
368
369 int drm_fb_helper_blank(int blank, struct fb_info *info)
370 {
371         switch (blank) {
372         /* Display: On; HSync: On, VSync: On */
373         case FB_BLANK_UNBLANK:
374                 drm_fb_helper_on(info);
375                 break;
376         /* Display: Off; HSync: On, VSync: On */
377         case FB_BLANK_NORMAL:
378                 drm_fb_helper_off(info, DRM_MODE_DPMS_STANDBY);
379                 break;
380         /* Display: Off; HSync: Off, VSync: On */
381         case FB_BLANK_HSYNC_SUSPEND:
382                 drm_fb_helper_off(info, DRM_MODE_DPMS_STANDBY);
383                 break;
384         /* Display: Off; HSync: On, VSync: Off */
385         case FB_BLANK_VSYNC_SUSPEND:
386                 drm_fb_helper_off(info, DRM_MODE_DPMS_SUSPEND);
387                 break;
388         /* Display: Off; HSync: Off, VSync: Off */
389         case FB_BLANK_POWERDOWN:
390                 drm_fb_helper_off(info, DRM_MODE_DPMS_OFF);
391                 break;
392         }
393         return 0;
394 }
395 EXPORT_SYMBOL(drm_fb_helper_blank);
396
397 static void drm_fb_helper_crtc_free(struct drm_fb_helper *helper)
398 {
399         int i;
400
401         for (i = 0; i < helper->crtc_count; i++)
402                 kfree(helper->crtc_info[i].mode_set.connectors);
403         kfree(helper->crtc_info);
404 }
405
406 int drm_fb_helper_init_crtc_count(struct drm_fb_helper *helper, int crtc_count, int max_conn_count)
407 {
408         struct drm_device *dev = helper->dev;
409         struct drm_crtc *crtc;
410         int ret = 0;
411         int i;
412
413         helper->crtc_info = kcalloc(crtc_count, sizeof(struct drm_fb_helper_crtc), GFP_KERNEL);
414         if (!helper->crtc_info)
415                 return -ENOMEM;
416
417         helper->crtc_count = crtc_count;
418
419         for (i = 0; i < crtc_count; i++) {
420                 helper->crtc_info[i].mode_set.connectors =
421                         kcalloc(max_conn_count,
422                                 sizeof(struct drm_connector *),
423                                 GFP_KERNEL);
424
425                 if (!helper->crtc_info[i].mode_set.connectors) {
426                         ret = -ENOMEM;
427                         goto out_free;
428                 }
429                 helper->crtc_info[i].mode_set.num_connectors = 0;
430         }
431
432         i = 0;
433         list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
434                 helper->crtc_info[i].crtc_id = crtc->base.id;
435                 helper->crtc_info[i].mode_set.crtc = crtc;
436                 i++;
437         }
438         helper->conn_limit = max_conn_count;
439         return 0;
440 out_free:
441         drm_fb_helper_crtc_free(helper);
442         return -ENOMEM;
443 }
444 EXPORT_SYMBOL(drm_fb_helper_init_crtc_count);
445
446 static int setcolreg(struct drm_crtc *crtc, u16 red, u16 green,
447                      u16 blue, u16 regno, struct fb_info *info)
448 {
449         struct drm_fb_helper *fb_helper = info->par;
450         struct drm_framebuffer *fb = fb_helper->fb;
451         int pindex;
452
453         if (info->fix.visual == FB_VISUAL_TRUECOLOR) {
454                 u32 *palette;
455                 u32 value;
456                 /* place color in psuedopalette */
457                 if (regno > 16)
458                         return -EINVAL;
459                 palette = (u32 *)info->pseudo_palette;
460                 red >>= (16 - info->var.red.length);
461                 green >>= (16 - info->var.green.length);
462                 blue >>= (16 - info->var.blue.length);
463                 value = (red << info->var.red.offset) |
464                         (green << info->var.green.offset) |
465                         (blue << info->var.blue.offset);
466                 palette[regno] = value;
467                 return 0;
468         }
469
470         pindex = regno;
471
472         if (fb->bits_per_pixel == 16) {
473                 pindex = regno << 3;
474
475                 if (fb->depth == 16 && regno > 63)
476                         return -EINVAL;
477                 if (fb->depth == 15 && regno > 31)
478                         return -EINVAL;
479
480                 if (fb->depth == 16) {
481                         u16 r, g, b;
482                         int i;
483                         if (regno < 32) {
484                                 for (i = 0; i < 8; i++)
485                                         fb_helper->funcs->gamma_set(crtc, red,
486                                                 green, blue, pindex + i);
487                         }
488
489                         fb_helper->funcs->gamma_get(crtc, &r,
490                                                     &g, &b,
491                                                     pindex >> 1);
492
493                         for (i = 0; i < 4; i++)
494                                 fb_helper->funcs->gamma_set(crtc, r,
495                                                             green, b,
496                                                             (pindex >> 1) + i);
497                 }
498         }
499
500         if (fb->depth != 16)
501                 fb_helper->funcs->gamma_set(crtc, red, green, blue, pindex);
502         return 0;
503 }
504
505 int drm_fb_helper_setcmap(struct fb_cmap *cmap, struct fb_info *info)
506 {
507         struct drm_fb_helper *fb_helper = info->par;
508         struct drm_device *dev = fb_helper->dev;
509         u16 *red, *green, *blue, *transp;
510         struct drm_crtc *crtc;
511         int i, rc = 0;
512         int start;
513
514         list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
515                 struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private;
516                 for (i = 0; i < fb_helper->crtc_count; i++) {
517                         if (crtc->base.id == fb_helper->crtc_info[i].crtc_id)
518                                 break;
519                 }
520                 if (i == fb_helper->crtc_count)
521                         continue;
522
523                 red = cmap->red;
524                 green = cmap->green;
525                 blue = cmap->blue;
526                 transp = cmap->transp;
527                 start = cmap->start;
528
529                 for (i = 0; i < cmap->len; i++) {
530                         u16 hred, hgreen, hblue, htransp = 0xffff;
531
532                         hred = *red++;
533                         hgreen = *green++;
534                         hblue = *blue++;
535
536                         if (transp)
537                                 htransp = *transp++;
538
539                         rc = setcolreg(crtc, hred, hgreen, hblue, start++, info);
540                         if (rc)
541                                 return rc;
542                 }
543                 crtc_funcs->load_lut(crtc);
544         }
545         return rc;
546 }
547 EXPORT_SYMBOL(drm_fb_helper_setcmap);
548
549 int drm_fb_helper_setcolreg(unsigned regno,
550                             unsigned red,
551                             unsigned green,
552                             unsigned blue,
553                             unsigned transp,
554                             struct fb_info *info)
555 {
556         struct drm_fb_helper *fb_helper = info->par;
557         struct drm_device *dev = fb_helper->dev;
558         struct drm_crtc *crtc;
559         int i;
560         int ret;
561
562         if (regno > 255)
563                 return 1;
564
565         list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
566                 struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private;
567                 for (i = 0; i < fb_helper->crtc_count; i++) {
568                         if (crtc->base.id == fb_helper->crtc_info[i].crtc_id)
569                                 break;
570                 }
571                 if (i == fb_helper->crtc_count)
572                         continue;
573
574                 ret = setcolreg(crtc, red, green, blue, regno, info);
575                 if (ret)
576                         return ret;
577
578                 crtc_funcs->load_lut(crtc);
579         }
580         return 0;
581 }
582 EXPORT_SYMBOL(drm_fb_helper_setcolreg);
583
584 int drm_fb_helper_check_var(struct fb_var_screeninfo *var,
585                             struct fb_info *info)
586 {
587         struct drm_fb_helper *fb_helper = info->par;
588         struct drm_framebuffer *fb = fb_helper->fb;
589         int depth;
590
591         if (var->pixclock != 0)
592                 return -EINVAL;
593
594         /* Need to resize the fb object !!! */
595         if (var->bits_per_pixel > fb->bits_per_pixel || var->xres > fb->width || var->yres > fb->height) {
596                 DRM_DEBUG("fb userspace requested width/height/bpp is greater than current fb "
597                           "object %dx%d-%d > %dx%d-%d\n", var->xres, var->yres, var->bits_per_pixel,
598                           fb->width, fb->height, fb->bits_per_pixel);
599                 return -EINVAL;
600         }
601
602         switch (var->bits_per_pixel) {
603         case 16:
604                 depth = (var->green.length == 6) ? 16 : 15;
605                 break;
606         case 32:
607                 depth = (var->transp.length > 0) ? 32 : 24;
608                 break;
609         default:
610                 depth = var->bits_per_pixel;
611                 break;
612         }
613
614         switch (depth) {
615         case 8:
616                 var->red.offset = 0;
617                 var->green.offset = 0;
618                 var->blue.offset = 0;
619                 var->red.length = 8;
620                 var->green.length = 8;
621                 var->blue.length = 8;
622                 var->transp.length = 0;
623                 var->transp.offset = 0;
624                 break;
625         case 15:
626                 var->red.offset = 10;
627                 var->green.offset = 5;
628                 var->blue.offset = 0;
629                 var->red.length = 5;
630                 var->green.length = 5;
631                 var->blue.length = 5;
632                 var->transp.length = 1;
633                 var->transp.offset = 15;
634                 break;
635         case 16:
636                 var->red.offset = 11;
637                 var->green.offset = 5;
638                 var->blue.offset = 0;
639                 var->red.length = 5;
640                 var->green.length = 6;
641                 var->blue.length = 5;
642                 var->transp.length = 0;
643                 var->transp.offset = 0;
644                 break;
645         case 24:
646                 var->red.offset = 16;
647                 var->green.offset = 8;
648                 var->blue.offset = 0;
649                 var->red.length = 8;
650                 var->green.length = 8;
651                 var->blue.length = 8;
652                 var->transp.length = 0;
653                 var->transp.offset = 0;
654                 break;
655         case 32:
656                 var->red.offset = 16;
657                 var->green.offset = 8;
658                 var->blue.offset = 0;
659                 var->red.length = 8;
660                 var->green.length = 8;
661                 var->blue.length = 8;
662                 var->transp.length = 8;
663                 var->transp.offset = 24;
664                 break;
665         default:
666                 return -EINVAL;
667         }
668         return 0;
669 }
670 EXPORT_SYMBOL(drm_fb_helper_check_var);
671
672 /* this will let fbcon do the mode init */
673 int drm_fb_helper_set_par(struct fb_info *info)
674 {
675         struct drm_fb_helper *fb_helper = info->par;
676         struct drm_device *dev = fb_helper->dev;
677         struct fb_var_screeninfo *var = &info->var;
678         struct drm_crtc *crtc;
679         int ret;
680         int i;
681
682         if (var->pixclock != 0) {
683                 DRM_ERROR("PIXEL CLOCK SET\n");
684                 return -EINVAL;
685         }
686
687         list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
688
689                 for (i = 0; i < fb_helper->crtc_count; i++) {
690                         if (crtc->base.id == fb_helper->crtc_info[i].crtc_id)
691                                 break;
692                 }
693                 if (i == fb_helper->crtc_count)
694                         continue;
695
696                 if (crtc->fb == fb_helper->crtc_info[i].mode_set.fb) {
697                         mutex_lock(&dev->mode_config.mutex);
698                         ret = crtc->funcs->set_config(&fb_helper->crtc_info[i].mode_set);
699                         mutex_unlock(&dev->mode_config.mutex);
700                         if (ret)
701                                 return ret;
702                 }
703         }
704         return 0;
705 }
706 EXPORT_SYMBOL(drm_fb_helper_set_par);
707
708 int drm_fb_helper_pan_display(struct fb_var_screeninfo *var,
709                               struct fb_info *info)
710 {
711         struct drm_fb_helper *fb_helper = info->par;
712         struct drm_device *dev = fb_helper->dev;
713         struct drm_mode_set *modeset;
714         struct drm_crtc *crtc;
715         int ret = 0;
716         int i;
717
718         list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
719                 for (i = 0; i < fb_helper->crtc_count; i++) {
720                         if (crtc->base.id == fb_helper->crtc_info[i].crtc_id)
721                                 break;
722                 }
723
724                 if (i == fb_helper->crtc_count)
725                         continue;
726
727                 modeset = &fb_helper->crtc_info[i].mode_set;
728
729                 modeset->x = var->xoffset;
730                 modeset->y = var->yoffset;
731
732                 if (modeset->num_connectors) {
733                         mutex_lock(&dev->mode_config.mutex);
734                         ret = crtc->funcs->set_config(modeset);
735                         mutex_unlock(&dev->mode_config.mutex);
736                         if (!ret) {
737                                 info->var.xoffset = var->xoffset;
738                                 info->var.yoffset = var->yoffset;
739                         }
740                 }
741         }
742         return ret;
743 }
744 EXPORT_SYMBOL(drm_fb_helper_pan_display);
745
746 int drm_fb_helper_single_fb_probe(struct drm_device *dev,
747                                   int preferred_bpp,
748                                   int (*fb_create)(struct drm_device *dev,
749                                                    uint32_t fb_width,
750                                                    uint32_t fb_height,
751                                                    uint32_t surface_width,
752                                                    uint32_t surface_height,
753                                                    uint32_t surface_depth,
754                                                    uint32_t surface_bpp,
755                                                    struct drm_framebuffer **fb_ptr))
756 {
757         struct drm_crtc *crtc;
758         struct drm_connector *connector;
759         unsigned int fb_width = (unsigned)-1, fb_height = (unsigned)-1;
760         unsigned int surface_width = 0, surface_height = 0;
761         int new_fb = 0;
762         int crtc_count = 0;
763         int ret, i, conn_count = 0;
764         struct fb_info *info;
765         struct drm_framebuffer *fb;
766         struct drm_mode_set *modeset = NULL;
767         struct drm_fb_helper *fb_helper;
768         uint32_t surface_depth = 24, surface_bpp = 32;
769
770         /* if driver picks 8 or 16 by default use that
771            for both depth/bpp */
772         if (preferred_bpp != surface_bpp) {
773                 surface_depth = surface_bpp = preferred_bpp;
774         }
775         /* first up get a count of crtcs now in use and new min/maxes width/heights */
776         list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
777                 struct drm_fb_helper_connector *fb_help_conn = connector->fb_helper_private;
778
779                 struct drm_fb_helper_cmdline_mode *cmdline_mode;
780
781                 if (!fb_help_conn)
782                         continue;
783                 
784                 cmdline_mode = &fb_help_conn->cmdline_mode;
785
786                 if (cmdline_mode->bpp_specified) {
787                         switch (cmdline_mode->bpp) {
788                         case 8:
789                                 surface_depth = surface_bpp = 8;
790                                 break;
791                         case 15:
792                                 surface_depth = 15;
793                                 surface_bpp = 16;
794                                 break;
795                         case 16:
796                                 surface_depth = surface_bpp = 16;
797                                 break;
798                         case 24:
799                                 surface_depth = surface_bpp = 24;
800                                 break;
801                         case 32:
802                                 surface_depth = 24;
803                                 surface_bpp = 32;
804                                 break;
805                         }
806                         break;
807                 }
808         }
809
810         list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
811                 if (drm_helper_crtc_in_use(crtc)) {
812                         if (crtc->desired_mode) {
813                                 if (crtc->desired_mode->hdisplay < fb_width)
814                                         fb_width = crtc->desired_mode->hdisplay;
815
816                                 if (crtc->desired_mode->vdisplay < fb_height)
817                                         fb_height = crtc->desired_mode->vdisplay;
818
819                                 if (crtc->desired_mode->hdisplay > surface_width)
820                                         surface_width = crtc->desired_mode->hdisplay;
821
822                                 if (crtc->desired_mode->vdisplay > surface_height)
823                                         surface_height = crtc->desired_mode->vdisplay;
824                         }
825                         crtc_count++;
826                 }
827         }
828
829         if (crtc_count == 0 || fb_width == -1 || fb_height == -1) {
830                 /* hmm everyone went away - assume VGA cable just fell out
831                    and will come back later. */
832                 return 0;
833         }
834
835         /* do we have an fb already? */
836         if (list_empty(&dev->mode_config.fb_kernel_list)) {
837                 ret = (*fb_create)(dev, fb_width, fb_height, surface_width,
838                                    surface_height, surface_depth, surface_bpp,
839                                    &fb);
840                 if (ret)
841                         return -EINVAL;
842                 new_fb = 1;
843         } else {
844                 fb = list_first_entry(&dev->mode_config.fb_kernel_list,
845                                       struct drm_framebuffer, filp_head);
846
847                 /* if someone hotplugs something bigger than we have already allocated, we are pwned.
848                    As really we can't resize an fbdev that is in the wild currently due to fbdev
849                    not really being designed for the lower layers moving stuff around under it.
850                    - so in the grand style of things - punt. */
851                 if ((fb->width < surface_width) ||
852                     (fb->height < surface_height)) {
853                         DRM_ERROR("Framebuffer not large enough to scale console onto.\n");
854                         return -EINVAL;
855                 }
856         }
857
858         info = fb->fbdev;
859         fb_helper = info->par;
860
861         crtc_count = 0;
862         /* okay we need to setup new connector sets in the crtcs */
863         list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
864                 modeset = &fb_helper->crtc_info[crtc_count].mode_set;
865                 modeset->fb = fb;
866                 conn_count = 0;
867                 list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
868                         if (connector->encoder)
869                                 if (connector->encoder->crtc == modeset->crtc) {
870                                         modeset->connectors[conn_count] = connector;
871                                         conn_count++;
872                                         if (conn_count > fb_helper->conn_limit)
873                                                 BUG();
874                                 }
875                 }
876
877                 for (i = conn_count; i < fb_helper->conn_limit; i++)
878                         modeset->connectors[i] = NULL;
879
880                 modeset->crtc = crtc;
881                 crtc_count++;
882
883                 modeset->num_connectors = conn_count;
884                 if (modeset->crtc->desired_mode) {
885                         if (modeset->mode)
886                                 drm_mode_destroy(dev, modeset->mode);
887                         modeset->mode = drm_mode_duplicate(dev,
888                                                            modeset->crtc->desired_mode);
889                 }
890         }
891         fb_helper->crtc_count = crtc_count;
892         fb_helper->fb = fb;
893
894         if (new_fb) {
895                 info->var.pixclock = 0;
896                 ret = fb_alloc_cmap(&info->cmap, modeset->crtc->gamma_size, 0);
897                 if (ret)
898                         return ret;
899                 if (register_framebuffer(info) < 0) {
900                         fb_dealloc_cmap(&info->cmap);
901                         return -EINVAL;
902                 }
903         } else {
904                 drm_fb_helper_set_par(info);
905         }
906         printk(KERN_INFO "fb%d: %s frame buffer device\n", info->node,
907                info->fix.id);
908
909         /* Switch back to kernel console on panic */
910         /* multi card linked list maybe */
911         if (list_empty(&kernel_fb_helper_list)) {
912                 printk(KERN_INFO "registered panic notifier\n");
913                 atomic_notifier_chain_register(&panic_notifier_list,
914                                                &paniced);
915                 register_sysrq_key('v', &sysrq_drm_fb_helper_restore_op);
916         }
917         list_add(&fb_helper->kernel_fb_list, &kernel_fb_helper_list);
918         return 0;
919 }
920 EXPORT_SYMBOL(drm_fb_helper_single_fb_probe);
921
922 void drm_fb_helper_free(struct drm_fb_helper *helper)
923 {
924         list_del(&helper->kernel_fb_list);
925         if (list_empty(&kernel_fb_helper_list)) {
926                 printk(KERN_INFO "unregistered panic notifier\n");
927                 atomic_notifier_chain_unregister(&panic_notifier_list,
928                                                  &paniced);
929                 unregister_sysrq_key('v', &sysrq_drm_fb_helper_restore_op);
930         }
931         drm_fb_helper_crtc_free(helper);
932         fb_dealloc_cmap(&helper->fb->fbdev->cmap);
933 }
934 EXPORT_SYMBOL(drm_fb_helper_free);
935
936 void drm_fb_helper_fill_fix(struct fb_info *info, uint32_t pitch,
937                             uint32_t depth)
938 {
939         info->fix.type = FB_TYPE_PACKED_PIXELS;
940         info->fix.visual = depth == 8 ? FB_VISUAL_PSEUDOCOLOR :
941                 FB_VISUAL_TRUECOLOR;
942         info->fix.type_aux = 0;
943         info->fix.xpanstep = 1; /* doing it in hw */
944         info->fix.ypanstep = 1; /* doing it in hw */
945         info->fix.ywrapstep = 0;
946         info->fix.accel = FB_ACCEL_NONE;
947         info->fix.type_aux = 0;
948
949         info->fix.line_length = pitch;
950         return;
951 }
952 EXPORT_SYMBOL(drm_fb_helper_fill_fix);
953
954 void drm_fb_helper_fill_var(struct fb_info *info, struct drm_framebuffer *fb,
955                             uint32_t fb_width, uint32_t fb_height)
956 {
957         info->pseudo_palette = fb->pseudo_palette;
958         info->var.xres_virtual = fb->width;
959         info->var.yres_virtual = fb->height;
960         info->var.bits_per_pixel = fb->bits_per_pixel;
961         info->var.xoffset = 0;
962         info->var.yoffset = 0;
963         info->var.activate = FB_ACTIVATE_NOW;
964         info->var.height = -1;
965         info->var.width = -1;
966
967         switch (fb->depth) {
968         case 8:
969                 info->var.red.offset = 0;
970                 info->var.green.offset = 0;
971                 info->var.blue.offset = 0;
972                 info->var.red.length = 8; /* 8bit DAC */
973                 info->var.green.length = 8;
974                 info->var.blue.length = 8;
975                 info->var.transp.offset = 0;
976                 info->var.transp.length = 0;
977                 break;
978         case 15:
979                 info->var.red.offset = 10;
980                 info->var.green.offset = 5;
981                 info->var.blue.offset = 0;
982                 info->var.red.length = 5;
983                 info->var.green.length = 5;
984                 info->var.blue.length = 5;
985                 info->var.transp.offset = 15;
986                 info->var.transp.length = 1;
987                 break;
988         case 16:
989                 info->var.red.offset = 11;
990                 info->var.green.offset = 5;
991                 info->var.blue.offset = 0;
992                 info->var.red.length = 5;
993                 info->var.green.length = 6;
994                 info->var.blue.length = 5;
995                 info->var.transp.offset = 0;
996                 break;
997         case 24:
998                 info->var.red.offset = 16;
999                 info->var.green.offset = 8;
1000                 info->var.blue.offset = 0;
1001                 info->var.red.length = 8;
1002                 info->var.green.length = 8;
1003                 info->var.blue.length = 8;
1004                 info->var.transp.offset = 0;
1005                 info->var.transp.length = 0;
1006                 break;
1007         case 32:
1008                 info->var.red.offset = 16;
1009                 info->var.green.offset = 8;
1010                 info->var.blue.offset = 0;
1011                 info->var.red.length = 8;
1012                 info->var.green.length = 8;
1013                 info->var.blue.length = 8;
1014                 info->var.transp.offset = 24;
1015                 info->var.transp.length = 8;
1016                 break;
1017         default:
1018                 break;
1019         }
1020
1021         info->var.xres = fb_width;
1022         info->var.yres = fb_height;
1023 }
1024 EXPORT_SYMBOL(drm_fb_helper_fill_var);