drm/fb: add more correct 8/16/24/32 bpp fb support.
[linux-2.6/linux-acpi-2.6/ibm-acpi-2.6.git] / drivers / gpu / drm / drm_fb_helper.c
blob23dc9c115fd99dc4577e6444e11e866ceca488e8
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>
6 * DRM framebuffer helper functions
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.
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.
26 * Authors:
27 * Dave Airlie <airlied@linux.ie>
28 * Jesse Barnes <jesse.barnes@intel.com>
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"
37 MODULE_AUTHOR("David Airlie, Jesse Barnes");
38 MODULE_DESCRIPTION("DRM KMS helper");
39 MODULE_LICENSE("GPL and additional rights");
41 static LIST_HEAD(kernel_fb_helper_list);
43 int drm_fb_helper_add_connector(struct drm_connector *connector)
45 connector->fb_helper_private = kzalloc(sizeof(struct drm_fb_helper_connector), GFP_KERNEL);
46 if (!connector->fb_helper_private)
47 return -ENOMEM;
49 return 0;
51 EXPORT_SYMBOL(drm_fb_helper_add_connector);
53 static int my_atoi(const char *name)
55 int val = 0;
57 for (;; name++) {
58 switch (*name) {
59 case '0' ... '9':
60 val = 10*val+(*name-'0');
61 break;
62 default:
63 return val;
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
73 * This parses the connector specific then generic command lines for
74 * modes and options to configure the connector.
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]
79 * enable/enable Digital/disable bit at the end
81 static bool drm_fb_helper_connector_parse_command_line(struct drm_connector *connector,
82 const char *mode_option)
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;
94 if (!fb_help_conn)
95 return false;
97 cmdline_mode = &fb_help_conn->cmdline_mode;
98 if (!mode_option)
99 mode_option = fb_mode_option;
101 if (!mode_option) {
102 cmdline_mode->specified = false;
103 return false;
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;
172 if (i < 0 && yres_specified) {
173 xres = my_atoi(name);
174 res_specified = 1;
176 done:
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" : "");
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;
193 DRM_INFO("forcing %s connector %s\n",
194 drm_get_connector_name(connector), s);
195 connector->force = force;
198 if (res_specified) {
199 cmdline_mode->specified = true;
200 cmdline_mode->xres = xres;
201 cmdline_mode->yres = yres;
204 if (refresh_specified) {
205 cmdline_mode->refresh_specified = true;
206 cmdline_mode->refresh = refresh;
209 if (bpp_specified) {
210 cmdline_mode->bpp_specified = true;
211 cmdline_mode->bpp = bpp;
213 cmdline_mode->rb = rb ? true : false;
214 cmdline_mode->cvt = cvt ? true : false;
215 cmdline_mode->interlace = interlace ? true : false;
217 return true;
220 int drm_fb_helper_parse_command_line(struct drm_device *dev)
222 struct drm_connector *connector;
224 list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
225 char *option = NULL;
227 /* do something on return - turn off connector maybe */
228 if (fb_get_options(drm_get_connector_name(connector), &option))
229 continue;
231 drm_fb_helper_connector_parse_command_line(connector, option);
233 return 0;
236 bool drm_fb_helper_force_kernel_mode(void)
238 int i = 0;
239 bool ret, error = false;
240 struct drm_fb_helper *helper;
242 if (list_empty(&kernel_fb_helper_list))
243 return false;
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;
253 return error;
256 int drm_fb_helper_panic(struct notifier_block *n, unsigned long ununsed,
257 void *panic_str)
259 DRM_ERROR("panic occurred, switching back to text console\n");
260 return drm_fb_helper_force_kernel_mode();
261 return 0;
263 EXPORT_SYMBOL(drm_fb_helper_panic);
265 static struct notifier_block paniced = {
266 .notifier_call = drm_fb_helper_panic,
270 * drm_fb_helper_restore - restore the framebuffer console (kernel) config
272 * Restore's the kernel's fbcon mode, used for lastclose & panic paths.
274 void drm_fb_helper_restore(void)
276 bool ret;
277 ret = drm_fb_helper_force_kernel_mode();
278 if (ret == true)
279 DRM_ERROR("Failed to restore crtc configuration\n");
281 EXPORT_SYMBOL(drm_fb_helper_restore);
283 #ifdef CONFIG_MAGIC_SYSRQ
284 static void drm_fb_helper_restore_work_fn(struct work_struct *ignored)
286 drm_fb_helper_restore();
288 static DECLARE_WORK(drm_fb_helper_restore_work, drm_fb_helper_restore_work_fn);
290 static void drm_fb_helper_sysrq(int dummy1, struct tty_struct *dummy3)
292 schedule_work(&drm_fb_helper_restore_work);
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",
300 #endif
302 static void drm_fb_helper_on(struct fb_info *info)
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;
311 * For each CRTC in this fb, turn the crtc on then,
312 * find all associated encoders and turn them on.
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;
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;
324 mutex_lock(&dev->mode_config.mutex);
325 crtc_funcs->dpms(crtc, DRM_MODE_DPMS_ON);
326 mutex_unlock(&dev->mode_config.mutex);
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;
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);
343 static void drm_fb_helper_off(struct fb_info *info, int dpms_mode)
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;
352 * For each CRTC in this fb, find all associated encoders
353 * and turn them off, then turn off the CRTC.
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;
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;
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;
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);
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);
385 int drm_fb_helper_blank(int blank, struct fb_info *info)
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;
404 return 0;
406 EXPORT_SYMBOL(drm_fb_helper_blank);
408 static void drm_fb_helper_crtc_free(struct drm_fb_helper *helper)
410 int i;
412 for (i = 0; i < helper->crtc_count; i++)
413 kfree(helper->crtc_info[i].mode_set.connectors);
414 kfree(helper->crtc_info);
417 int drm_fb_helper_init_crtc_count(struct drm_fb_helper *helper, int crtc_count, int max_conn_count)
419 struct drm_device *dev = helper->dev;
420 struct drm_crtc *crtc;
421 int ret = 0;
422 int i;
424 helper->crtc_info = kcalloc(crtc_count, sizeof(struct drm_fb_helper_crtc), GFP_KERNEL);
425 if (!helper->crtc_info)
426 return -ENOMEM;
428 helper->crtc_count = crtc_count;
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);
436 if (!helper->crtc_info[i].mode_set.connectors) {
437 ret = -ENOMEM;
438 goto out_free;
440 helper->crtc_info[i].mode_set.num_connectors = 0;
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++;
449 helper->conn_limit = max_conn_count;
450 return 0;
451 out_free:
452 drm_fb_helper_crtc_free(helper);
453 return -ENOMEM;
455 EXPORT_SYMBOL(drm_fb_helper_init_crtc_count);
457 static void setcolreg(struct drm_crtc *crtc, u16 red, u16 green,
458 u16 blue, u16 regno, struct fb_info *info)
460 struct drm_fb_helper *fb_helper = info->par;
461 struct drm_framebuffer *fb = fb_helper->fb;
462 int pindex;
464 pindex = regno;
466 if (fb->bits_per_pixel == 16) {
467 pindex = regno << 3;
469 if (fb->depth == 16 && regno > 63)
470 return;
471 if (fb->depth == 15 && regno > 31)
472 return;
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);
483 fb_helper->funcs->gamma_get(crtc, &r,
484 &g, &b,
485 pindex >> 1);
487 for (i = 0; i < 4; i++)
488 fb_helper->funcs->gamma_set(crtc, r,
489 green, b,
490 (pindex >> 1) + i);
494 if (fb->depth != 16)
495 fb_helper->funcs->gamma_set(crtc, red, green, blue, pindex);
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);
505 int drm_fb_helper_setcmap(struct fb_cmap *cmap, struct fb_info *info)
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;
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;
520 if (i == fb_helper->crtc_count)
521 continue;
523 red = cmap->red;
524 green = cmap->green;
525 blue = cmap->blue;
526 transp = cmap->transp;
527 start = cmap->start;
529 for (i = 0; i < cmap->len; i++) {
530 u16 hred, hgreen, hblue, htransp = 0xffff;
532 hred = *red++;
533 hgreen = *green++;
534 hblue = *blue++;
536 if (transp)
537 htransp = *transp++;
539 setcolreg(crtc, hred, hgreen, hblue, start++, info);
541 crtc_funcs->load_lut(crtc);
543 return rc;
545 EXPORT_SYMBOL(drm_fb_helper_setcmap);
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)
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;
559 if (regno > 255)
560 return 1;
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;
568 if (i == fb_helper->crtc_count)
569 continue;
572 setcolreg(crtc, red, green, blue, regno, info);
573 crtc_funcs->load_lut(crtc);
575 return 0;
577 EXPORT_SYMBOL(drm_fb_helper_setcolreg);
579 int drm_fb_helper_check_var(struct fb_var_screeninfo *var,
580 struct fb_info *info)
582 struct drm_fb_helper *fb_helper = info->par;
583 struct drm_framebuffer *fb = fb_helper->fb;
584 int depth;
586 if (var->pixclock == -1 || !var->pixclock)
587 return -EINVAL;
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;
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;
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;
664 return 0;
666 EXPORT_SYMBOL(drm_fb_helper_check_var);
668 /* this will let fbcon do the mode init */
669 int drm_fb_helper_set_par(struct fb_info *info)
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;
678 if (var->pixclock != -1) {
679 DRM_ERROR("PIXEL CLCOK SET\n");
680 return -EINVAL;
683 list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
685 for (i = 0; i < fb_helper->crtc_count; i++) {
686 if (crtc->base.id == fb_helper->crtc_info[i].crtc_id)
687 break;
689 if (i == fb_helper->crtc_count)
690 continue;
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;
700 return 0;
702 EXPORT_SYMBOL(drm_fb_helper_set_par);
704 int drm_fb_helper_pan_display(struct fb_var_screeninfo *var,
705 struct fb_info *info)
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;
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;
720 if (i == fb_helper->crtc_count)
721 continue;
723 modeset = &fb_helper->crtc_info[i].mode_set;
725 modeset->x = var->xoffset;
726 modeset->y = var->yoffset;
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;
738 return ret;
740 EXPORT_SYMBOL(drm_fb_helper_pan_display);
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))
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;
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;
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;
775 struct drm_fb_helper_cmdline_mode *cmdline_mode;
777 if (!fb_help_conn)
778 continue;
780 cmdline_mode = &fb_help_conn->cmdline_mode;
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;
802 break;
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;
812 if (crtc->desired_mode->vdisplay < fb_height)
813 fb_height = crtc->desired_mode->vdisplay;
815 if (crtc->desired_mode->hdisplay > surface_width)
816 surface_width = crtc->desired_mode->hdisplay;
818 if (crtc->desired_mode->vdisplay > surface_height)
819 surface_height = crtc->desired_mode->vdisplay;
821 crtc_count++;
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;
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);
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;
854 info = fb->fbdev;
855 fb_helper = info->par;
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();
873 for (i = conn_count; i < fb_helper->conn_limit; i++)
874 modeset->connectors[i] = NULL;
876 modeset->crtc = crtc;
877 crtc_count++;
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);
887 fb_helper->crtc_count = crtc_count;
888 fb_helper->fb = fb;
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);
897 printk(KERN_INFO "fb%d: %s frame buffer device\n", info->node,
898 info->fix.id);
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);
908 list_add(&fb_helper->kernel_fb_list, &kernel_fb_helper_list);
909 return 0;
911 EXPORT_SYMBOL(drm_fb_helper_single_fb_probe);
913 void drm_fb_helper_free(struct drm_fb_helper *helper)
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);
922 drm_fb_helper_crtc_free(helper);
924 EXPORT_SYMBOL(drm_fb_helper_free);
926 void drm_fb_helper_fill_fix(struct fb_info *info, uint32_t pitch,
927 uint32_t depth)
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;
939 info->fix.line_length = pitch;
940 return;
942 EXPORT_SYMBOL(drm_fb_helper_fill_fix);
944 void drm_fb_helper_fill_var(struct fb_info *info, struct drm_framebuffer *fb,
945 uint32_t fb_width, uint32_t fb_height)
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;
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;
1011 info->var.xres = fb_width;
1012 info->var.yres = fb_height;
1014 EXPORT_SYMBOL(drm_fb_helper_fill_var);