Merge branch 'master' into export-slabh
[linux-2.6/linux-acpi-2.6/ibm-acpi-2.6.git] / drivers / gpu / drm / drm_fb_helper.c
blob288ea2f327720b17992d7332f333f717f34f1adc
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/kernel.h>
31 #include <linux/sysrq.h>
32 #include <linux/slab.h>
33 #include <linux/fb.h>
34 #include "drmP.h"
35 #include "drm_crtc.h"
36 #include "drm_fb_helper.h"
37 #include "drm_crtc_helper.h"
39 MODULE_AUTHOR("David Airlie, Jesse Barnes");
40 MODULE_DESCRIPTION("DRM KMS helper");
41 MODULE_LICENSE("GPL and additional rights");
43 static LIST_HEAD(kernel_fb_helper_list);
45 int drm_fb_helper_add_connector(struct drm_connector *connector)
47 connector->fb_helper_private = kzalloc(sizeof(struct drm_fb_helper_connector), GFP_KERNEL);
48 if (!connector->fb_helper_private)
49 return -ENOMEM;
51 return 0;
53 EXPORT_SYMBOL(drm_fb_helper_add_connector);
55 /**
56 * drm_fb_helper_connector_parse_command_line - parse command line for connector
57 * @connector - connector to parse line for
58 * @mode_option - per connector mode option
60 * This parses the connector specific then generic command lines for
61 * modes and options to configure the connector.
63 * This uses the same parameters as the fb modedb.c, except for extra
64 * <xres>x<yres>[M][R][-<bpp>][@<refresh>][i][m][eDd]
66 * enable/enable Digital/disable bit at the end
68 static bool drm_fb_helper_connector_parse_command_line(struct drm_connector *connector,
69 const char *mode_option)
71 const char *name;
72 unsigned int namelen;
73 int res_specified = 0, bpp_specified = 0, refresh_specified = 0;
74 unsigned int xres = 0, yres = 0, bpp = 32, refresh = 0;
75 int yres_specified = 0, cvt = 0, rb = 0, interlace = 0, margins = 0;
76 int i;
77 enum drm_connector_force force = DRM_FORCE_UNSPECIFIED;
78 struct drm_fb_helper_connector *fb_help_conn = connector->fb_helper_private;
79 struct drm_fb_helper_cmdline_mode *cmdline_mode;
81 if (!fb_help_conn)
82 return false;
84 cmdline_mode = &fb_help_conn->cmdline_mode;
85 if (!mode_option)
86 mode_option = fb_mode_option;
88 if (!mode_option) {
89 cmdline_mode->specified = false;
90 return false;
93 name = mode_option;
94 namelen = strlen(name);
95 for (i = namelen-1; i >= 0; i--) {
96 switch (name[i]) {
97 case '@':
98 namelen = i;
99 if (!refresh_specified && !bpp_specified &&
100 !yres_specified) {
101 refresh = simple_strtol(&name[i+1], NULL, 10);
102 refresh_specified = 1;
103 if (cvt || rb)
104 cvt = 0;
105 } else
106 goto done;
107 break;
108 case '-':
109 namelen = i;
110 if (!bpp_specified && !yres_specified) {
111 bpp = simple_strtol(&name[i+1], NULL, 10);
112 bpp_specified = 1;
113 if (cvt || rb)
114 cvt = 0;
115 } else
116 goto done;
117 break;
118 case 'x':
119 if (!yres_specified) {
120 yres = simple_strtol(&name[i+1], NULL, 10);
121 yres_specified = 1;
122 } else
123 goto done;
124 case '0' ... '9':
125 break;
126 case 'M':
127 if (!yres_specified)
128 cvt = 1;
129 break;
130 case 'R':
131 if (!cvt)
132 rb = 1;
133 break;
134 case 'm':
135 if (!cvt)
136 margins = 1;
137 break;
138 case 'i':
139 if (!cvt)
140 interlace = 1;
141 break;
142 case 'e':
143 force = DRM_FORCE_ON;
144 break;
145 case 'D':
146 if ((connector->connector_type != DRM_MODE_CONNECTOR_DVII) &&
147 (connector->connector_type != DRM_MODE_CONNECTOR_HDMIB))
148 force = DRM_FORCE_ON;
149 else
150 force = DRM_FORCE_ON_DIGITAL;
151 break;
152 case 'd':
153 force = DRM_FORCE_OFF;
154 break;
155 default:
156 goto done;
159 if (i < 0 && yres_specified) {
160 xres = simple_strtol(name, NULL, 10);
161 res_specified = 1;
163 done:
165 DRM_DEBUG_KMS("cmdline mode for connector %s %dx%d@%dHz%s%s%s\n",
166 drm_get_connector_name(connector), xres, yres,
167 (refresh) ? refresh : 60, (rb) ? " reduced blanking" :
168 "", (margins) ? " with margins" : "", (interlace) ?
169 " interlaced" : "");
171 if (force) {
172 const char *s;
173 switch (force) {
174 case DRM_FORCE_OFF: s = "OFF"; break;
175 case DRM_FORCE_ON_DIGITAL: s = "ON - dig"; break;
176 default:
177 case DRM_FORCE_ON: s = "ON"; break;
180 DRM_INFO("forcing %s connector %s\n",
181 drm_get_connector_name(connector), s);
182 connector->force = force;
185 if (res_specified) {
186 cmdline_mode->specified = true;
187 cmdline_mode->xres = xres;
188 cmdline_mode->yres = yres;
191 if (refresh_specified) {
192 cmdline_mode->refresh_specified = true;
193 cmdline_mode->refresh = refresh;
196 if (bpp_specified) {
197 cmdline_mode->bpp_specified = true;
198 cmdline_mode->bpp = bpp;
200 cmdline_mode->rb = rb ? true : false;
201 cmdline_mode->cvt = cvt ? true : false;
202 cmdline_mode->interlace = interlace ? true : false;
204 return true;
207 int drm_fb_helper_parse_command_line(struct drm_device *dev)
209 struct drm_connector *connector;
211 list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
212 char *option = NULL;
214 /* do something on return - turn off connector maybe */
215 if (fb_get_options(drm_get_connector_name(connector), &option))
216 continue;
218 drm_fb_helper_connector_parse_command_line(connector, option);
220 return 0;
223 bool drm_fb_helper_force_kernel_mode(void)
225 int i = 0;
226 bool ret, error = false;
227 struct drm_fb_helper *helper;
229 if (list_empty(&kernel_fb_helper_list))
230 return false;
232 list_for_each_entry(helper, &kernel_fb_helper_list, kernel_fb_list) {
233 for (i = 0; i < helper->crtc_count; i++) {
234 struct drm_mode_set *mode_set = &helper->crtc_info[i].mode_set;
235 ret = drm_crtc_helper_set_config(mode_set);
236 if (ret)
237 error = true;
240 return error;
243 int drm_fb_helper_panic(struct notifier_block *n, unsigned long ununsed,
244 void *panic_str)
246 DRM_ERROR("panic occurred, switching back to text console\n");
247 return drm_fb_helper_force_kernel_mode();
248 return 0;
250 EXPORT_SYMBOL(drm_fb_helper_panic);
252 static struct notifier_block paniced = {
253 .notifier_call = drm_fb_helper_panic,
257 * drm_fb_helper_restore - restore the framebuffer console (kernel) config
259 * Restore's the kernel's fbcon mode, used for lastclose & panic paths.
261 void drm_fb_helper_restore(void)
263 bool ret;
264 ret = drm_fb_helper_force_kernel_mode();
265 if (ret == true)
266 DRM_ERROR("Failed to restore crtc configuration\n");
268 EXPORT_SYMBOL(drm_fb_helper_restore);
270 #ifdef CONFIG_MAGIC_SYSRQ
271 static void drm_fb_helper_restore_work_fn(struct work_struct *ignored)
273 drm_fb_helper_restore();
275 static DECLARE_WORK(drm_fb_helper_restore_work, drm_fb_helper_restore_work_fn);
277 static void drm_fb_helper_sysrq(int dummy1, struct tty_struct *dummy3)
279 schedule_work(&drm_fb_helper_restore_work);
282 static struct sysrq_key_op sysrq_drm_fb_helper_restore_op = {
283 .handler = drm_fb_helper_sysrq,
284 .help_msg = "force-fb(V)",
285 .action_msg = "Restore framebuffer console",
287 #else
288 static struct sysrq_key_op sysrq_drm_fb_helper_restore_op = { };
289 #endif
291 static void drm_fb_helper_on(struct fb_info *info)
293 struct drm_fb_helper *fb_helper = info->par;
294 struct drm_device *dev = fb_helper->dev;
295 struct drm_crtc *crtc;
296 struct drm_encoder *encoder;
297 int i;
300 * For each CRTC in this fb, turn the crtc on then,
301 * find all associated encoders and turn them on.
303 for (i = 0; i < fb_helper->crtc_count; i++) {
304 list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
305 struct drm_crtc_helper_funcs *crtc_funcs =
306 crtc->helper_private;
308 /* Only mess with CRTCs in this fb */
309 if (crtc->base.id != fb_helper->crtc_info[i].crtc_id ||
310 !crtc->enabled)
311 continue;
313 mutex_lock(&dev->mode_config.mutex);
314 crtc_funcs->dpms(crtc, DRM_MODE_DPMS_ON);
315 mutex_unlock(&dev->mode_config.mutex);
317 /* Found a CRTC on this fb, now find encoders */
318 list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
319 if (encoder->crtc == crtc) {
320 struct drm_encoder_helper_funcs *encoder_funcs;
322 encoder_funcs = encoder->helper_private;
323 mutex_lock(&dev->mode_config.mutex);
324 encoder_funcs->dpms(encoder, DRM_MODE_DPMS_ON);
325 mutex_unlock(&dev->mode_config.mutex);
332 static void drm_fb_helper_off(struct fb_info *info, int dpms_mode)
334 struct drm_fb_helper *fb_helper = info->par;
335 struct drm_device *dev = fb_helper->dev;
336 struct drm_crtc *crtc;
337 struct drm_encoder *encoder;
338 int i;
341 * For each CRTC in this fb, find all associated encoders
342 * and turn them off, then turn off the CRTC.
344 for (i = 0; i < fb_helper->crtc_count; i++) {
345 list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
346 struct drm_crtc_helper_funcs *crtc_funcs =
347 crtc->helper_private;
349 /* Only mess with CRTCs in this fb */
350 if (crtc->base.id != fb_helper->crtc_info[i].crtc_id ||
351 !crtc->enabled)
352 continue;
354 /* Found a CRTC on this fb, now find encoders */
355 list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
356 if (encoder->crtc == crtc) {
357 struct drm_encoder_helper_funcs *encoder_funcs;
359 encoder_funcs = encoder->helper_private;
360 mutex_lock(&dev->mode_config.mutex);
361 encoder_funcs->dpms(encoder, dpms_mode);
362 mutex_unlock(&dev->mode_config.mutex);
365 mutex_lock(&dev->mode_config.mutex);
366 crtc_funcs->dpms(crtc, DRM_MODE_DPMS_OFF);
367 mutex_unlock(&dev->mode_config.mutex);
372 int drm_fb_helper_blank(int blank, struct fb_info *info)
374 switch (blank) {
375 /* Display: On; HSync: On, VSync: On */
376 case FB_BLANK_UNBLANK:
377 drm_fb_helper_on(info);
378 break;
379 /* Display: Off; HSync: On, VSync: On */
380 case FB_BLANK_NORMAL:
381 drm_fb_helper_off(info, DRM_MODE_DPMS_STANDBY);
382 break;
383 /* Display: Off; HSync: Off, VSync: On */
384 case FB_BLANK_HSYNC_SUSPEND:
385 drm_fb_helper_off(info, DRM_MODE_DPMS_STANDBY);
386 break;
387 /* Display: Off; HSync: On, VSync: Off */
388 case FB_BLANK_VSYNC_SUSPEND:
389 drm_fb_helper_off(info, DRM_MODE_DPMS_SUSPEND);
390 break;
391 /* Display: Off; HSync: Off, VSync: Off */
392 case FB_BLANK_POWERDOWN:
393 drm_fb_helper_off(info, DRM_MODE_DPMS_OFF);
394 break;
396 return 0;
398 EXPORT_SYMBOL(drm_fb_helper_blank);
400 static void drm_fb_helper_crtc_free(struct drm_fb_helper *helper)
402 int i;
404 for (i = 0; i < helper->crtc_count; i++)
405 kfree(helper->crtc_info[i].mode_set.connectors);
406 kfree(helper->crtc_info);
409 int drm_fb_helper_init_crtc_count(struct drm_fb_helper *helper, int crtc_count, int max_conn_count)
411 struct drm_device *dev = helper->dev;
412 struct drm_crtc *crtc;
413 int ret = 0;
414 int i;
416 helper->crtc_info = kcalloc(crtc_count, sizeof(struct drm_fb_helper_crtc), GFP_KERNEL);
417 if (!helper->crtc_info)
418 return -ENOMEM;
420 helper->crtc_count = crtc_count;
422 for (i = 0; i < crtc_count; i++) {
423 helper->crtc_info[i].mode_set.connectors =
424 kcalloc(max_conn_count,
425 sizeof(struct drm_connector *),
426 GFP_KERNEL);
428 if (!helper->crtc_info[i].mode_set.connectors) {
429 ret = -ENOMEM;
430 goto out_free;
432 helper->crtc_info[i].mode_set.num_connectors = 0;
435 i = 0;
436 list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
437 helper->crtc_info[i].crtc_id = crtc->base.id;
438 helper->crtc_info[i].mode_set.crtc = crtc;
439 i++;
441 helper->conn_limit = max_conn_count;
442 return 0;
443 out_free:
444 drm_fb_helper_crtc_free(helper);
445 return -ENOMEM;
447 EXPORT_SYMBOL(drm_fb_helper_init_crtc_count);
449 static int setcolreg(struct drm_crtc *crtc, u16 red, u16 green,
450 u16 blue, u16 regno, struct fb_info *info)
452 struct drm_fb_helper *fb_helper = info->par;
453 struct drm_framebuffer *fb = fb_helper->fb;
454 int pindex;
456 if (info->fix.visual == FB_VISUAL_TRUECOLOR) {
457 u32 *palette;
458 u32 value;
459 /* place color in psuedopalette */
460 if (regno > 16)
461 return -EINVAL;
462 palette = (u32 *)info->pseudo_palette;
463 red >>= (16 - info->var.red.length);
464 green >>= (16 - info->var.green.length);
465 blue >>= (16 - info->var.blue.length);
466 value = (red << info->var.red.offset) |
467 (green << info->var.green.offset) |
468 (blue << info->var.blue.offset);
469 palette[regno] = value;
470 return 0;
473 pindex = regno;
475 if (fb->bits_per_pixel == 16) {
476 pindex = regno << 3;
478 if (fb->depth == 16 && regno > 63)
479 return -EINVAL;
480 if (fb->depth == 15 && regno > 31)
481 return -EINVAL;
483 if (fb->depth == 16) {
484 u16 r, g, b;
485 int i;
486 if (regno < 32) {
487 for (i = 0; i < 8; i++)
488 fb_helper->funcs->gamma_set(crtc, red,
489 green, blue, pindex + i);
492 fb_helper->funcs->gamma_get(crtc, &r,
493 &g, &b,
494 pindex >> 1);
496 for (i = 0; i < 4; i++)
497 fb_helper->funcs->gamma_set(crtc, r,
498 green, b,
499 (pindex >> 1) + i);
503 if (fb->depth != 16)
504 fb_helper->funcs->gamma_set(crtc, red, green, blue, pindex);
505 return 0;
508 int drm_fb_helper_setcmap(struct fb_cmap *cmap, struct fb_info *info)
510 struct drm_fb_helper *fb_helper = info->par;
511 struct drm_device *dev = fb_helper->dev;
512 u16 *red, *green, *blue, *transp;
513 struct drm_crtc *crtc;
514 int i, rc = 0;
515 int start;
517 list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
518 struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private;
519 for (i = 0; i < fb_helper->crtc_count; i++) {
520 if (crtc->base.id == fb_helper->crtc_info[i].crtc_id)
521 break;
523 if (i == fb_helper->crtc_count)
524 continue;
526 red = cmap->red;
527 green = cmap->green;
528 blue = cmap->blue;
529 transp = cmap->transp;
530 start = cmap->start;
532 for (i = 0; i < cmap->len; i++) {
533 u16 hred, hgreen, hblue, htransp = 0xffff;
535 hred = *red++;
536 hgreen = *green++;
537 hblue = *blue++;
539 if (transp)
540 htransp = *transp++;
542 rc = setcolreg(crtc, hred, hgreen, hblue, start++, info);
543 if (rc)
544 return rc;
546 crtc_funcs->load_lut(crtc);
548 return rc;
550 EXPORT_SYMBOL(drm_fb_helper_setcmap);
552 int drm_fb_helper_setcolreg(unsigned regno,
553 unsigned red,
554 unsigned green,
555 unsigned blue,
556 unsigned transp,
557 struct fb_info *info)
559 struct drm_fb_helper *fb_helper = info->par;
560 struct drm_device *dev = fb_helper->dev;
561 struct drm_crtc *crtc;
562 int i;
563 int ret;
565 if (regno > 255)
566 return 1;
568 list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
569 struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private;
570 for (i = 0; i < fb_helper->crtc_count; i++) {
571 if (crtc->base.id == fb_helper->crtc_info[i].crtc_id)
572 break;
574 if (i == fb_helper->crtc_count)
575 continue;
577 ret = setcolreg(crtc, red, green, blue, regno, info);
578 if (ret)
579 return ret;
581 crtc_funcs->load_lut(crtc);
583 return 0;
585 EXPORT_SYMBOL(drm_fb_helper_setcolreg);
587 int drm_fb_helper_check_var(struct fb_var_screeninfo *var,
588 struct fb_info *info)
590 struct drm_fb_helper *fb_helper = info->par;
591 struct drm_framebuffer *fb = fb_helper->fb;
592 int depth;
594 if (var->pixclock != 0)
595 return -EINVAL;
597 /* Need to resize the fb object !!! */
598 if (var->bits_per_pixel > fb->bits_per_pixel || var->xres > fb->width || var->yres > fb->height) {
599 DRM_DEBUG("fb userspace requested width/height/bpp is greater than current fb "
600 "object %dx%d-%d > %dx%d-%d\n", var->xres, var->yres, var->bits_per_pixel,
601 fb->width, fb->height, fb->bits_per_pixel);
602 return -EINVAL;
605 switch (var->bits_per_pixel) {
606 case 16:
607 depth = (var->green.length == 6) ? 16 : 15;
608 break;
609 case 32:
610 depth = (var->transp.length > 0) ? 32 : 24;
611 break;
612 default:
613 depth = var->bits_per_pixel;
614 break;
617 switch (depth) {
618 case 8:
619 var->red.offset = 0;
620 var->green.offset = 0;
621 var->blue.offset = 0;
622 var->red.length = 8;
623 var->green.length = 8;
624 var->blue.length = 8;
625 var->transp.length = 0;
626 var->transp.offset = 0;
627 break;
628 case 15:
629 var->red.offset = 10;
630 var->green.offset = 5;
631 var->blue.offset = 0;
632 var->red.length = 5;
633 var->green.length = 5;
634 var->blue.length = 5;
635 var->transp.length = 1;
636 var->transp.offset = 15;
637 break;
638 case 16:
639 var->red.offset = 11;
640 var->green.offset = 5;
641 var->blue.offset = 0;
642 var->red.length = 5;
643 var->green.length = 6;
644 var->blue.length = 5;
645 var->transp.length = 0;
646 var->transp.offset = 0;
647 break;
648 case 24:
649 var->red.offset = 16;
650 var->green.offset = 8;
651 var->blue.offset = 0;
652 var->red.length = 8;
653 var->green.length = 8;
654 var->blue.length = 8;
655 var->transp.length = 0;
656 var->transp.offset = 0;
657 break;
658 case 32:
659 var->red.offset = 16;
660 var->green.offset = 8;
661 var->blue.offset = 0;
662 var->red.length = 8;
663 var->green.length = 8;
664 var->blue.length = 8;
665 var->transp.length = 8;
666 var->transp.offset = 24;
667 break;
668 default:
669 return -EINVAL;
671 return 0;
673 EXPORT_SYMBOL(drm_fb_helper_check_var);
675 /* this will let fbcon do the mode init */
676 int drm_fb_helper_set_par(struct fb_info *info)
678 struct drm_fb_helper *fb_helper = info->par;
679 struct drm_device *dev = fb_helper->dev;
680 struct fb_var_screeninfo *var = &info->var;
681 struct drm_crtc *crtc;
682 int ret;
683 int i;
685 if (var->pixclock != 0) {
686 DRM_ERROR("PIXEL CLOCK SET\n");
687 return -EINVAL;
690 list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
692 for (i = 0; i < fb_helper->crtc_count; i++) {
693 if (crtc->base.id == fb_helper->crtc_info[i].crtc_id)
694 break;
696 if (i == fb_helper->crtc_count)
697 continue;
699 if (crtc->fb == fb_helper->crtc_info[i].mode_set.fb) {
700 mutex_lock(&dev->mode_config.mutex);
701 ret = crtc->funcs->set_config(&fb_helper->crtc_info[i].mode_set);
702 mutex_unlock(&dev->mode_config.mutex);
703 if (ret)
704 return ret;
707 return 0;
709 EXPORT_SYMBOL(drm_fb_helper_set_par);
711 int drm_fb_helper_pan_display(struct fb_var_screeninfo *var,
712 struct fb_info *info)
714 struct drm_fb_helper *fb_helper = info->par;
715 struct drm_device *dev = fb_helper->dev;
716 struct drm_mode_set *modeset;
717 struct drm_crtc *crtc;
718 int ret = 0;
719 int i;
721 list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
722 for (i = 0; i < fb_helper->crtc_count; i++) {
723 if (crtc->base.id == fb_helper->crtc_info[i].crtc_id)
724 break;
727 if (i == fb_helper->crtc_count)
728 continue;
730 modeset = &fb_helper->crtc_info[i].mode_set;
732 modeset->x = var->xoffset;
733 modeset->y = var->yoffset;
735 if (modeset->num_connectors) {
736 mutex_lock(&dev->mode_config.mutex);
737 ret = crtc->funcs->set_config(modeset);
738 mutex_unlock(&dev->mode_config.mutex);
739 if (!ret) {
740 info->var.xoffset = var->xoffset;
741 info->var.yoffset = var->yoffset;
745 return ret;
747 EXPORT_SYMBOL(drm_fb_helper_pan_display);
749 int drm_fb_helper_single_fb_probe(struct drm_device *dev,
750 int preferred_bpp,
751 int (*fb_create)(struct drm_device *dev,
752 uint32_t fb_width,
753 uint32_t fb_height,
754 uint32_t surface_width,
755 uint32_t surface_height,
756 uint32_t surface_depth,
757 uint32_t surface_bpp,
758 struct drm_framebuffer **fb_ptr))
760 struct drm_crtc *crtc;
761 struct drm_connector *connector;
762 unsigned int fb_width = (unsigned)-1, fb_height = (unsigned)-1;
763 unsigned int surface_width = 0, surface_height = 0;
764 int new_fb = 0;
765 int crtc_count = 0;
766 int ret, i, conn_count = 0;
767 struct fb_info *info;
768 struct drm_framebuffer *fb;
769 struct drm_mode_set *modeset = NULL;
770 struct drm_fb_helper *fb_helper;
771 uint32_t surface_depth = 24, surface_bpp = 32;
773 /* if driver picks 8 or 16 by default use that
774 for both depth/bpp */
775 if (preferred_bpp != surface_bpp) {
776 surface_depth = surface_bpp = preferred_bpp;
778 /* first up get a count of crtcs now in use and new min/maxes width/heights */
779 list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
780 struct drm_fb_helper_connector *fb_help_conn = connector->fb_helper_private;
782 struct drm_fb_helper_cmdline_mode *cmdline_mode;
784 if (!fb_help_conn)
785 continue;
787 cmdline_mode = &fb_help_conn->cmdline_mode;
789 if (cmdline_mode->bpp_specified) {
790 switch (cmdline_mode->bpp) {
791 case 8:
792 surface_depth = surface_bpp = 8;
793 break;
794 case 15:
795 surface_depth = 15;
796 surface_bpp = 16;
797 break;
798 case 16:
799 surface_depth = surface_bpp = 16;
800 break;
801 case 24:
802 surface_depth = surface_bpp = 24;
803 break;
804 case 32:
805 surface_depth = 24;
806 surface_bpp = 32;
807 break;
809 break;
813 list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
814 if (drm_helper_crtc_in_use(crtc)) {
815 if (crtc->desired_mode) {
816 if (crtc->desired_mode->hdisplay < fb_width)
817 fb_width = crtc->desired_mode->hdisplay;
819 if (crtc->desired_mode->vdisplay < fb_height)
820 fb_height = crtc->desired_mode->vdisplay;
822 if (crtc->desired_mode->hdisplay > surface_width)
823 surface_width = crtc->desired_mode->hdisplay;
825 if (crtc->desired_mode->vdisplay > surface_height)
826 surface_height = crtc->desired_mode->vdisplay;
828 crtc_count++;
832 if (crtc_count == 0 || fb_width == -1 || fb_height == -1) {
833 /* hmm everyone went away - assume VGA cable just fell out
834 and will come back later. */
835 return 0;
838 /* do we have an fb already? */
839 if (list_empty(&dev->mode_config.fb_kernel_list)) {
840 ret = (*fb_create)(dev, fb_width, fb_height, surface_width,
841 surface_height, surface_depth, surface_bpp,
842 &fb);
843 if (ret)
844 return -EINVAL;
845 new_fb = 1;
846 } else {
847 fb = list_first_entry(&dev->mode_config.fb_kernel_list,
848 struct drm_framebuffer, filp_head);
850 /* if someone hotplugs something bigger than we have already allocated, we are pwned.
851 As really we can't resize an fbdev that is in the wild currently due to fbdev
852 not really being designed for the lower layers moving stuff around under it.
853 - so in the grand style of things - punt. */
854 if ((fb->width < surface_width) ||
855 (fb->height < surface_height)) {
856 DRM_ERROR("Framebuffer not large enough to scale console onto.\n");
857 return -EINVAL;
861 info = fb->fbdev;
862 fb_helper = info->par;
864 crtc_count = 0;
865 /* okay we need to setup new connector sets in the crtcs */
866 list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
867 modeset = &fb_helper->crtc_info[crtc_count].mode_set;
868 modeset->fb = fb;
869 conn_count = 0;
870 list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
871 if (connector->encoder)
872 if (connector->encoder->crtc == modeset->crtc) {
873 modeset->connectors[conn_count] = connector;
874 conn_count++;
875 if (conn_count > fb_helper->conn_limit)
876 BUG();
880 for (i = conn_count; i < fb_helper->conn_limit; i++)
881 modeset->connectors[i] = NULL;
883 modeset->crtc = crtc;
884 crtc_count++;
886 modeset->num_connectors = conn_count;
887 if (modeset->crtc->desired_mode) {
888 if (modeset->mode)
889 drm_mode_destroy(dev, modeset->mode);
890 modeset->mode = drm_mode_duplicate(dev,
891 modeset->crtc->desired_mode);
894 fb_helper->crtc_count = crtc_count;
895 fb_helper->fb = fb;
897 if (new_fb) {
898 info->var.pixclock = 0;
899 ret = fb_alloc_cmap(&info->cmap, modeset->crtc->gamma_size, 0);
900 if (ret)
901 return ret;
902 if (register_framebuffer(info) < 0) {
903 fb_dealloc_cmap(&info->cmap);
904 return -EINVAL;
906 } else {
907 drm_fb_helper_set_par(info);
909 printk(KERN_INFO "fb%d: %s frame buffer device\n", info->node,
910 info->fix.id);
912 /* Switch back to kernel console on panic */
913 /* multi card linked list maybe */
914 if (list_empty(&kernel_fb_helper_list)) {
915 printk(KERN_INFO "registered panic notifier\n");
916 atomic_notifier_chain_register(&panic_notifier_list,
917 &paniced);
918 register_sysrq_key('v', &sysrq_drm_fb_helper_restore_op);
920 list_add(&fb_helper->kernel_fb_list, &kernel_fb_helper_list);
921 return 0;
923 EXPORT_SYMBOL(drm_fb_helper_single_fb_probe);
925 void drm_fb_helper_free(struct drm_fb_helper *helper)
927 list_del(&helper->kernel_fb_list);
928 if (list_empty(&kernel_fb_helper_list)) {
929 printk(KERN_INFO "unregistered panic notifier\n");
930 atomic_notifier_chain_unregister(&panic_notifier_list,
931 &paniced);
932 unregister_sysrq_key('v', &sysrq_drm_fb_helper_restore_op);
934 drm_fb_helper_crtc_free(helper);
935 fb_dealloc_cmap(&helper->fb->fbdev->cmap);
937 EXPORT_SYMBOL(drm_fb_helper_free);
939 void drm_fb_helper_fill_fix(struct fb_info *info, uint32_t pitch,
940 uint32_t depth)
942 info->fix.type = FB_TYPE_PACKED_PIXELS;
943 info->fix.visual = depth == 8 ? FB_VISUAL_PSEUDOCOLOR :
944 FB_VISUAL_TRUECOLOR;
945 info->fix.type_aux = 0;
946 info->fix.xpanstep = 1; /* doing it in hw */
947 info->fix.ypanstep = 1; /* doing it in hw */
948 info->fix.ywrapstep = 0;
949 info->fix.accel = FB_ACCEL_NONE;
950 info->fix.type_aux = 0;
952 info->fix.line_length = pitch;
953 return;
955 EXPORT_SYMBOL(drm_fb_helper_fill_fix);
957 void drm_fb_helper_fill_var(struct fb_info *info, struct drm_framebuffer *fb,
958 uint32_t fb_width, uint32_t fb_height)
960 info->pseudo_palette = fb->pseudo_palette;
961 info->var.xres_virtual = fb->width;
962 info->var.yres_virtual = fb->height;
963 info->var.bits_per_pixel = fb->bits_per_pixel;
964 info->var.xoffset = 0;
965 info->var.yoffset = 0;
966 info->var.activate = FB_ACTIVATE_NOW;
967 info->var.height = -1;
968 info->var.width = -1;
970 switch (fb->depth) {
971 case 8:
972 info->var.red.offset = 0;
973 info->var.green.offset = 0;
974 info->var.blue.offset = 0;
975 info->var.red.length = 8; /* 8bit DAC */
976 info->var.green.length = 8;
977 info->var.blue.length = 8;
978 info->var.transp.offset = 0;
979 info->var.transp.length = 0;
980 break;
981 case 15:
982 info->var.red.offset = 10;
983 info->var.green.offset = 5;
984 info->var.blue.offset = 0;
985 info->var.red.length = 5;
986 info->var.green.length = 5;
987 info->var.blue.length = 5;
988 info->var.transp.offset = 15;
989 info->var.transp.length = 1;
990 break;
991 case 16:
992 info->var.red.offset = 11;
993 info->var.green.offset = 5;
994 info->var.blue.offset = 0;
995 info->var.red.length = 5;
996 info->var.green.length = 6;
997 info->var.blue.length = 5;
998 info->var.transp.offset = 0;
999 break;
1000 case 24:
1001 info->var.red.offset = 16;
1002 info->var.green.offset = 8;
1003 info->var.blue.offset = 0;
1004 info->var.red.length = 8;
1005 info->var.green.length = 8;
1006 info->var.blue.length = 8;
1007 info->var.transp.offset = 0;
1008 info->var.transp.length = 0;
1009 break;
1010 case 32:
1011 info->var.red.offset = 16;
1012 info->var.green.offset = 8;
1013 info->var.blue.offset = 0;
1014 info->var.red.length = 8;
1015 info->var.green.length = 8;
1016 info->var.blue.length = 8;
1017 info->var.transp.offset = 24;
1018 info->var.transp.length = 8;
1019 break;
1020 default:
1021 break;
1024 info->var.xres = fb_width;
1025 info->var.yres = fb_height;
1027 EXPORT_SYMBOL(drm_fb_helper_fill_var);