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