drm: fix drm_fb_helper warning when !CONFIG_MAGIC_SYSRQ
[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 int drm_fb_helper_setcolreg(unsigned regno,
458                             unsigned red,
459                             unsigned green,
460                             unsigned blue,
461                             unsigned transp,
462                             struct fb_info *info)
463 {
464         struct drm_fb_helper *fb_helper = info->par;
465         struct drm_device *dev = fb_helper->dev;
466         struct drm_crtc *crtc;
467         int i;
468
469         list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
470                 struct drm_framebuffer *fb = fb_helper->fb;
471
472                 for (i = 0; i < fb_helper->crtc_count; i++) {
473                         if (crtc->base.id == fb_helper->crtc_info[i].crtc_id)
474                                 break;
475                 }
476                 if (i == fb_helper->crtc_count)
477                         continue;
478
479                 if (regno > 255)
480                         return 1;
481
482                 if (fb->depth == 8) {
483                         fb_helper->funcs->gamma_set(crtc, red, green, blue, regno);
484                         return 0;
485                 }
486
487                 if (regno < 16) {
488                         switch (fb->depth) {
489                         case 15:
490                                 fb->pseudo_palette[regno] = ((red & 0xf800) >> 1) |
491                                         ((green & 0xf800) >>  6) |
492                                         ((blue & 0xf800) >> 11);
493                                 break;
494                         case 16:
495                                 fb->pseudo_palette[regno] = (red & 0xf800) |
496                                         ((green & 0xfc00) >>  5) |
497                                         ((blue  & 0xf800) >> 11);
498                                 break;
499                         case 24:
500                         case 32:
501                                 fb->pseudo_palette[regno] =
502                                         (((red >> 8) & 0xff) << info->var.red.offset) |
503                                         (((green >> 8) & 0xff) << info->var.green.offset) |
504                                         (((blue >> 8) & 0xff) << info->var.blue.offset);
505                                 break;
506                         }
507                 }
508         }
509         return 0;
510 }
511 EXPORT_SYMBOL(drm_fb_helper_setcolreg);
512
513 int drm_fb_helper_check_var(struct fb_var_screeninfo *var,
514                             struct fb_info *info)
515 {
516         struct drm_fb_helper *fb_helper = info->par;
517         struct drm_framebuffer *fb = fb_helper->fb;
518         int depth;
519
520         if (var->pixclock == -1 || !var->pixclock)
521                 return -EINVAL;
522
523         /* Need to resize the fb object !!! */
524         if (var->xres > fb->width || var->yres > fb->height) {
525                 DRM_ERROR("Requested width/height is greater than current fb "
526                            "object %dx%d > %dx%d\n", var->xres, var->yres,
527                            fb->width, fb->height);
528                 DRM_ERROR("Need resizing code.\n");
529                 return -EINVAL;
530         }
531
532         switch (var->bits_per_pixel) {
533         case 16:
534                 depth = (var->green.length == 6) ? 16 : 15;
535                 break;
536         case 32:
537                 depth = (var->transp.length > 0) ? 32 : 24;
538                 break;
539         default:
540                 depth = var->bits_per_pixel;
541                 break;
542         }
543
544         switch (depth) {
545         case 8:
546                 var->red.offset = 0;
547                 var->green.offset = 0;
548                 var->blue.offset = 0;
549                 var->red.length = 8;
550                 var->green.length = 8;
551                 var->blue.length = 8;
552                 var->transp.length = 0;
553                 var->transp.offset = 0;
554                 break;
555         case 15:
556                 var->red.offset = 10;
557                 var->green.offset = 5;
558                 var->blue.offset = 0;
559                 var->red.length = 5;
560                 var->green.length = 5;
561                 var->blue.length = 5;
562                 var->transp.length = 1;
563                 var->transp.offset = 15;
564                 break;
565         case 16:
566                 var->red.offset = 11;
567                 var->green.offset = 5;
568                 var->blue.offset = 0;
569                 var->red.length = 5;
570                 var->green.length = 6;
571                 var->blue.length = 5;
572                 var->transp.length = 0;
573                 var->transp.offset = 0;
574                 break;
575         case 24:
576                 var->red.offset = 16;
577                 var->green.offset = 8;
578                 var->blue.offset = 0;
579                 var->red.length = 8;
580                 var->green.length = 8;
581                 var->blue.length = 8;
582                 var->transp.length = 0;
583                 var->transp.offset = 0;
584                 break;
585         case 32:
586                 var->red.offset = 16;
587                 var->green.offset = 8;
588                 var->blue.offset = 0;
589                 var->red.length = 8;
590                 var->green.length = 8;
591                 var->blue.length = 8;
592                 var->transp.length = 8;
593                 var->transp.offset = 24;
594                 break;
595         default:
596                 return -EINVAL;
597         }
598         return 0;
599 }
600 EXPORT_SYMBOL(drm_fb_helper_check_var);
601
602 /* this will let fbcon do the mode init */
603 int drm_fb_helper_set_par(struct fb_info *info)
604 {
605         struct drm_fb_helper *fb_helper = info->par;
606         struct drm_device *dev = fb_helper->dev;
607         struct fb_var_screeninfo *var = &info->var;
608         struct drm_crtc *crtc;
609         int ret;
610         int i;
611
612         if (var->pixclock != -1) {
613                 DRM_ERROR("PIXEL CLCOK SET\n");
614                 return -EINVAL;
615         }
616
617         list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
618
619                 for (i = 0; i < fb_helper->crtc_count; i++) {
620                         if (crtc->base.id == fb_helper->crtc_info[i].crtc_id)
621                                 break;
622                 }
623                 if (i == fb_helper->crtc_count)
624                         continue;
625
626                 if (crtc->fb == fb_helper->crtc_info[i].mode_set.fb) {
627                         mutex_lock(&dev->mode_config.mutex);
628                         ret = crtc->funcs->set_config(&fb_helper->crtc_info->mode_set);
629                         mutex_unlock(&dev->mode_config.mutex);
630                         if (ret)
631                                 return ret;
632                 }
633         }
634         return 0;
635 }
636 EXPORT_SYMBOL(drm_fb_helper_set_par);
637
638 int drm_fb_helper_pan_display(struct fb_var_screeninfo *var,
639                               struct fb_info *info)
640 {
641         struct drm_fb_helper *fb_helper = info->par;
642         struct drm_device *dev = fb_helper->dev;
643         struct drm_mode_set *modeset;
644         struct drm_crtc *crtc;
645         int ret = 0;
646         int i;
647
648         list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
649                 for (i = 0; i < fb_helper->crtc_count; i++) {
650                         if (crtc->base.id == fb_helper->crtc_info[i].crtc_id)
651                                 break;
652                 }
653
654                 if (i == fb_helper->crtc_count)
655                         continue;
656
657                 modeset = &fb_helper->crtc_info[i].mode_set;
658
659                 modeset->x = var->xoffset;
660                 modeset->y = var->yoffset;
661
662                 if (modeset->num_connectors) {
663                         mutex_lock(&dev->mode_config.mutex);
664                         ret = crtc->funcs->set_config(modeset);
665                         mutex_unlock(&dev->mode_config.mutex);
666                         if (!ret) {
667                                 info->var.xoffset = var->xoffset;
668                                 info->var.yoffset = var->yoffset;
669                         }
670                 }
671         }
672         return ret;
673 }
674 EXPORT_SYMBOL(drm_fb_helper_pan_display);
675
676 int drm_fb_helper_single_fb_probe(struct drm_device *dev,
677                                   int (*fb_create)(struct drm_device *dev,
678                                                    uint32_t fb_width,
679                                                    uint32_t fb_height,
680                                                    uint32_t surface_width,
681                                                    uint32_t surface_height,
682                                                    uint32_t surface_depth,
683                                                    uint32_t surface_bpp,
684                                                    struct drm_framebuffer **fb_ptr))
685 {
686         struct drm_crtc *crtc;
687         struct drm_connector *connector;
688         unsigned int fb_width = (unsigned)-1, fb_height = (unsigned)-1;
689         unsigned int surface_width = 0, surface_height = 0;
690         int new_fb = 0;
691         int crtc_count = 0;
692         int ret, i, conn_count = 0;
693         struct fb_info *info;
694         struct drm_framebuffer *fb;
695         struct drm_mode_set *modeset = NULL;
696         struct drm_fb_helper *fb_helper;
697         uint32_t surface_depth = 24, surface_bpp = 32;
698
699         /* first up get a count of crtcs now in use and new min/maxes width/heights */
700         list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
701                 struct drm_fb_helper_connector *fb_help_conn = connector->fb_helper_private;
702
703                 struct drm_fb_helper_cmdline_mode *cmdline_mode;
704
705                 if (!fb_help_conn)
706                         continue;
707                 
708                 cmdline_mode = &fb_help_conn->cmdline_mode;
709
710                 if (cmdline_mode->bpp_specified) {
711                         switch (cmdline_mode->bpp) {
712                         case 8:
713                                 surface_depth = surface_bpp = 8;
714                                 break;
715                         case 15:
716                                 surface_depth = 15;
717                                 surface_bpp = 16;
718                                 break;
719                         case 16:
720                                 surface_depth = surface_bpp = 16;
721                                 break;
722                         case 24:
723                                 surface_depth = surface_bpp = 24;
724                                 break;
725                         case 32:
726                                 surface_depth = 24;
727                                 surface_bpp = 32;
728                                 break;
729                         }
730                         break;
731                 }
732         }
733
734         list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
735                 if (drm_helper_crtc_in_use(crtc)) {
736                         if (crtc->desired_mode) {
737                                 if (crtc->desired_mode->hdisplay < fb_width)
738                                         fb_width = crtc->desired_mode->hdisplay;
739
740                                 if (crtc->desired_mode->vdisplay < fb_height)
741                                         fb_height = crtc->desired_mode->vdisplay;
742
743                                 if (crtc->desired_mode->hdisplay > surface_width)
744                                         surface_width = crtc->desired_mode->hdisplay;
745
746                                 if (crtc->desired_mode->vdisplay > surface_height)
747                                         surface_height = crtc->desired_mode->vdisplay;
748                         }
749                         crtc_count++;
750                 }
751         }
752
753         if (crtc_count == 0 || fb_width == -1 || fb_height == -1) {
754                 /* hmm everyone went away - assume VGA cable just fell out
755                    and will come back later. */
756                 return 0;
757         }
758
759         /* do we have an fb already? */
760         if (list_empty(&dev->mode_config.fb_kernel_list)) {
761                 ret = (*fb_create)(dev, fb_width, fb_height, surface_width,
762                                    surface_height, surface_depth, surface_bpp,
763                                    &fb);
764                 if (ret)
765                         return -EINVAL;
766                 new_fb = 1;
767         } else {
768                 fb = list_first_entry(&dev->mode_config.fb_kernel_list,
769                                       struct drm_framebuffer, filp_head);
770
771                 /* if someone hotplugs something bigger than we have already allocated, we are pwned.
772                    As really we can't resize an fbdev that is in the wild currently due to fbdev
773                    not really being designed for the lower layers moving stuff around under it.
774                    - so in the grand style of things - punt. */
775                 if ((fb->width < surface_width) ||
776                     (fb->height < surface_height)) {
777                         DRM_ERROR("Framebuffer not large enough to scale console onto.\n");
778                         return -EINVAL;
779                 }
780         }
781
782         info = fb->fbdev;
783         fb_helper = info->par;
784
785         crtc_count = 0;
786         /* okay we need to setup new connector sets in the crtcs */
787         list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
788                 modeset = &fb_helper->crtc_info[crtc_count].mode_set;
789                 modeset->fb = fb;
790                 conn_count = 0;
791                 list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
792                         if (connector->encoder)
793                                 if (connector->encoder->crtc == modeset->crtc) {
794                                         modeset->connectors[conn_count] = connector;
795                                         conn_count++;
796                                         if (conn_count > fb_helper->conn_limit)
797                                                 BUG();
798                                 }
799                 }
800
801                 for (i = conn_count; i < fb_helper->conn_limit; i++)
802                         modeset->connectors[i] = NULL;
803
804                 modeset->crtc = crtc;
805                 crtc_count++;
806
807                 modeset->num_connectors = conn_count;
808                 if (modeset->crtc->desired_mode) {
809                         if (modeset->mode)
810                                 drm_mode_destroy(dev, modeset->mode);
811                         modeset->mode = drm_mode_duplicate(dev,
812                                                            modeset->crtc->desired_mode);
813                 }
814         }
815         fb_helper->crtc_count = crtc_count;
816         fb_helper->fb = fb;
817
818         if (new_fb) {
819                 info->var.pixclock = -1;
820                 if (register_framebuffer(info) < 0)
821                         return -EINVAL;
822         } else {
823                 drm_fb_helper_set_par(info);
824         }
825         printk(KERN_INFO "fb%d: %s frame buffer device\n", info->node,
826                info->fix.id);
827
828         /* Switch back to kernel console on panic */
829         /* multi card linked list maybe */
830         if (list_empty(&kernel_fb_helper_list)) {
831                 printk(KERN_INFO "registered panic notifier\n");
832                 atomic_notifier_chain_register(&panic_notifier_list,
833                                                &paniced);
834                 register_sysrq_key('v', &sysrq_drm_fb_helper_restore_op);
835         }
836         list_add(&fb_helper->kernel_fb_list, &kernel_fb_helper_list);
837         return 0;
838 }
839 EXPORT_SYMBOL(drm_fb_helper_single_fb_probe);
840
841 void drm_fb_helper_free(struct drm_fb_helper *helper)
842 {
843         list_del(&helper->kernel_fb_list);
844         if (list_empty(&kernel_fb_helper_list)) {
845                 printk(KERN_INFO "unregistered panic notifier\n");
846                 atomic_notifier_chain_unregister(&panic_notifier_list,
847                                                  &paniced);
848                 unregister_sysrq_key('v', &sysrq_drm_fb_helper_restore_op);
849         }
850         drm_fb_helper_crtc_free(helper);
851 }
852 EXPORT_SYMBOL(drm_fb_helper_free);
853
854 void drm_fb_helper_fill_fix(struct fb_info *info, uint32_t pitch)
855 {
856         info->fix.type = FB_TYPE_PACKED_PIXELS;
857         info->fix.visual = FB_VISUAL_TRUECOLOR;
858         info->fix.type_aux = 0;
859         info->fix.xpanstep = 1; /* doing it in hw */
860         info->fix.ypanstep = 1; /* doing it in hw */
861         info->fix.ywrapstep = 0;
862         info->fix.accel = FB_ACCEL_NONE;
863         info->fix.type_aux = 0;
864
865         info->fix.line_length = pitch;
866         return;
867 }
868 EXPORT_SYMBOL(drm_fb_helper_fill_fix);
869
870 void drm_fb_helper_fill_var(struct fb_info *info, struct drm_framebuffer *fb,
871                             uint32_t fb_width, uint32_t fb_height)
872 {
873         info->pseudo_palette = fb->pseudo_palette;
874         info->var.xres_virtual = fb->width;
875         info->var.yres_virtual = fb->height;
876         info->var.bits_per_pixel = fb->bits_per_pixel;
877         info->var.xoffset = 0;
878         info->var.yoffset = 0;
879         info->var.activate = FB_ACTIVATE_NOW;
880         info->var.height = -1;
881         info->var.width = -1;
882
883         switch (fb->depth) {
884         case 8:
885                 info->var.red.offset = 0;
886                 info->var.green.offset = 0;
887                 info->var.blue.offset = 0;
888                 info->var.red.length = 8; /* 8bit DAC */
889                 info->var.green.length = 8;
890                 info->var.blue.length = 8;
891                 info->var.transp.offset = 0;
892                 info->var.transp.length = 0;
893                 break;
894         case 15:
895                 info->var.red.offset = 10;
896                 info->var.green.offset = 5;
897                 info->var.blue.offset = 0;
898                 info->var.red.length = 5;
899                 info->var.green.length = 5;
900                 info->var.blue.length = 5;
901                 info->var.transp.offset = 15;
902                 info->var.transp.length = 1;
903                 break;
904         case 16:
905                 info->var.red.offset = 11;
906                 info->var.green.offset = 5;
907                 info->var.blue.offset = 0;
908                 info->var.red.length = 5;
909                 info->var.green.length = 6;
910                 info->var.blue.length = 5;
911                 info->var.transp.offset = 0;
912                 break;
913         case 24:
914                 info->var.red.offset = 16;
915                 info->var.green.offset = 8;
916                 info->var.blue.offset = 0;
917                 info->var.red.length = 8;
918                 info->var.green.length = 8;
919                 info->var.blue.length = 8;
920                 info->var.transp.offset = 0;
921                 info->var.transp.length = 0;
922                 break;
923         case 32:
924                 info->var.red.offset = 16;
925                 info->var.green.offset = 8;
926                 info->var.blue.offset = 0;
927                 info->var.red.length = 8;
928                 info->var.green.length = 8;
929                 info->var.blue.length = 8;
930                 info->var.transp.offset = 24;
931                 info->var.transp.length = 8;
932                 break;
933         default:
934                 break;
935         }
936
937         info->var.xres = fb_width;
938         info->var.yres = fb_height;
939 }
940 EXPORT_SYMBOL(drm_fb_helper_fill_var);