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