drm: fix build error when SYSRQ is disabled
[linux-2.6/linux-acpi-2.6/ibm-acpi-2.6.git] / drivers / gpu / drm / drm_fb_helper.c
blob99487237111d152c2748edf1662bcf7187379883
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/fb.h>
33 #include "drmP.h"
34 #include "drm_crtc.h"
35 #include "drm_fb_helper.h"
36 #include "drm_crtc_helper.h"
38 MODULE_AUTHOR("David Airlie, Jesse Barnes");
39 MODULE_DESCRIPTION("DRM KMS helper");
40 MODULE_LICENSE("GPL and additional rights");
42 static LIST_HEAD(kernel_fb_helper_list);
44 int drm_fb_helper_add_connector(struct drm_connector *connector)
46 connector->fb_helper_private = kzalloc(sizeof(struct drm_fb_helper_connector), GFP_KERNEL);
47 if (!connector->fb_helper_private)
48 return -ENOMEM;
50 return 0;
52 EXPORT_SYMBOL(drm_fb_helper_add_connector);
54 /**
55 * drm_fb_helper_connector_parse_command_line - parse command line for connector
56 * @connector - connector to parse line for
57 * @mode_option - per connector mode option
59 * This parses the connector specific then generic command lines for
60 * modes and options to configure the connector.
62 * This uses the same parameters as the fb modedb.c, except for extra
63 * <xres>x<yres>[M][R][-<bpp>][@<refresh>][i][m][eDd]
65 * enable/enable Digital/disable bit at the end
67 static bool drm_fb_helper_connector_parse_command_line(struct drm_connector *connector,
68 const char *mode_option)
70 const char *name;
71 unsigned int namelen;
72 int res_specified = 0, bpp_specified = 0, refresh_specified = 0;
73 unsigned int xres = 0, yres = 0, bpp = 32, refresh = 0;
74 int yres_specified = 0, cvt = 0, rb = 0, interlace = 0, margins = 0;
75 int i;
76 enum drm_connector_force force = DRM_FORCE_UNSPECIFIED;
77 struct drm_fb_helper_connector *fb_help_conn = connector->fb_helper_private;
78 struct drm_fb_helper_cmdline_mode *cmdline_mode;
80 if (!fb_help_conn)
81 return false;
83 cmdline_mode = &fb_help_conn->cmdline_mode;
84 if (!mode_option)
85 mode_option = fb_mode_option;
87 if (!mode_option) {
88 cmdline_mode->specified = false;
89 return false;
92 name = mode_option;
93 namelen = strlen(name);
94 for (i = namelen-1; i >= 0; i--) {
95 switch (name[i]) {
96 case '@':
97 namelen = i;
98 if (!refresh_specified && !bpp_specified &&
99 !yres_specified) {
100 refresh = simple_strtol(&name[i+1], NULL, 10);
101 refresh_specified = 1;
102 if (cvt || rb)
103 cvt = 0;
104 } else
105 goto done;
106 break;
107 case '-':
108 namelen = i;
109 if (!bpp_specified && !yres_specified) {
110 bpp = simple_strtol(&name[i+1], NULL, 10);
111 bpp_specified = 1;
112 if (cvt || rb)
113 cvt = 0;
114 } else
115 goto done;
116 break;
117 case 'x':
118 if (!yres_specified) {
119 yres = simple_strtol(&name[i+1], NULL, 10);
120 yres_specified = 1;
121 } else
122 goto done;
123 case '0' ... '9':
124 break;
125 case 'M':
126 if (!yres_specified)
127 cvt = 1;
128 break;
129 case 'R':
130 if (!cvt)
131 rb = 1;
132 break;
133 case 'm':
134 if (!cvt)
135 margins = 1;
136 break;
137 case 'i':
138 if (!cvt)
139 interlace = 1;
140 break;
141 case 'e':
142 force = DRM_FORCE_ON;
143 break;
144 case 'D':
145 if ((connector->connector_type != DRM_MODE_CONNECTOR_DVII) &&
146 (connector->connector_type != DRM_MODE_CONNECTOR_HDMIB))
147 force = DRM_FORCE_ON;
148 else
149 force = DRM_FORCE_ON_DIGITAL;
150 break;
151 case 'd':
152 force = DRM_FORCE_OFF;
153 break;
154 default:
155 goto done;
158 if (i < 0 && yres_specified) {
159 xres = simple_strtol(name, NULL, 10);
160 res_specified = 1;
162 done:
164 DRM_DEBUG_KMS("cmdline mode for connector %s %dx%d@%dHz%s%s%s\n",
165 drm_get_connector_name(connector), xres, yres,
166 (refresh) ? refresh : 60, (rb) ? " reduced blanking" :
167 "", (margins) ? " with margins" : "", (interlace) ?
168 " interlaced" : "");
170 if (force) {
171 const char *s;
172 switch (force) {
173 case DRM_FORCE_OFF: s = "OFF"; break;
174 case DRM_FORCE_ON_DIGITAL: s = "ON - dig"; break;
175 default:
176 case DRM_FORCE_ON: s = "ON"; break;
179 DRM_INFO("forcing %s connector %s\n",
180 drm_get_connector_name(connector), s);
181 connector->force = force;
184 if (res_specified) {
185 cmdline_mode->specified = true;
186 cmdline_mode->xres = xres;
187 cmdline_mode->yres = yres;
190 if (refresh_specified) {
191 cmdline_mode->refresh_specified = true;
192 cmdline_mode->refresh = refresh;
195 if (bpp_specified) {
196 cmdline_mode->bpp_specified = true;
197 cmdline_mode->bpp = bpp;
199 cmdline_mode->rb = rb ? true : false;
200 cmdline_mode->cvt = cvt ? true : false;
201 cmdline_mode->interlace = interlace ? true : false;
203 return true;
206 int drm_fb_helper_parse_command_line(struct drm_device *dev)
208 struct drm_connector *connector;
210 list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
211 char *option = NULL;
213 /* do something on return - turn off connector maybe */
214 if (fb_get_options(drm_get_connector_name(connector), &option))
215 continue;
217 drm_fb_helper_connector_parse_command_line(connector, option);
219 return 0;
222 bool drm_fb_helper_force_kernel_mode(void)
224 int i = 0;
225 bool ret, error = false;
226 struct drm_fb_helper *helper;
228 if (list_empty(&kernel_fb_helper_list))
229 return false;
231 list_for_each_entry(helper, &kernel_fb_helper_list, kernel_fb_list) {
232 for (i = 0; i < helper->crtc_count; i++) {
233 struct drm_mode_set *mode_set = &helper->crtc_info[i].mode_set;
234 ret = drm_crtc_helper_set_config(mode_set);
235 if (ret)
236 error = true;
239 return error;
242 int drm_fb_helper_panic(struct notifier_block *n, unsigned long ununsed,
243 void *panic_str)
245 DRM_ERROR("panic occurred, switching back to text console\n");
246 return drm_fb_helper_force_kernel_mode();
247 return 0;
249 EXPORT_SYMBOL(drm_fb_helper_panic);
251 static struct notifier_block paniced = {
252 .notifier_call = drm_fb_helper_panic,
256 * drm_fb_helper_restore - restore the framebuffer console (kernel) config
258 * Restore's the kernel's fbcon mode, used for lastclose & panic paths.
260 void drm_fb_helper_restore(void)
262 bool ret;
263 ret = drm_fb_helper_force_kernel_mode();
264 if (ret == true)
265 DRM_ERROR("Failed to restore crtc configuration\n");
267 EXPORT_SYMBOL(drm_fb_helper_restore);
269 #ifdef CONFIG_MAGIC_SYSRQ
270 static void drm_fb_helper_restore_work_fn(struct work_struct *ignored)
272 drm_fb_helper_restore();
274 static DECLARE_WORK(drm_fb_helper_restore_work, drm_fb_helper_restore_work_fn);
276 static void drm_fb_helper_sysrq(int dummy1, struct tty_struct *dummy3)
278 schedule_work(&drm_fb_helper_restore_work);
281 static struct sysrq_key_op sysrq_drm_fb_helper_restore_op = {
282 .handler = drm_fb_helper_sysrq,
283 .help_msg = "force-fb(V)",
284 .action_msg = "Restore framebuffer console",
286 #else
287 static struct sysrq_key_op sysrq_drm_fb_helper_restore_op = { };
288 #endif
290 static void drm_fb_helper_on(struct fb_info *info)
292 struct drm_fb_helper *fb_helper = info->par;
293 struct drm_device *dev = fb_helper->dev;
294 struct drm_crtc *crtc;
295 struct drm_encoder *encoder;
296 int i;
299 * For each CRTC in this fb, turn the crtc on then,
300 * find all associated encoders and turn them on.
302 for (i = 0; i < fb_helper->crtc_count; i++) {
303 list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
304 struct drm_crtc_helper_funcs *crtc_funcs =
305 crtc->helper_private;
307 /* Only mess with CRTCs in this fb */
308 if (crtc->base.id != fb_helper->crtc_info[i].crtc_id ||
309 !crtc->enabled)
310 continue;
312 mutex_lock(&dev->mode_config.mutex);
313 crtc_funcs->dpms(crtc, DRM_MODE_DPMS_ON);
314 mutex_unlock(&dev->mode_config.mutex);
316 /* Found a CRTC on this fb, now find encoders */
317 list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
318 if (encoder->crtc == crtc) {
319 struct drm_encoder_helper_funcs *encoder_funcs;
321 encoder_funcs = encoder->helper_private;
322 mutex_lock(&dev->mode_config.mutex);
323 encoder_funcs->dpms(encoder, DRM_MODE_DPMS_ON);
324 mutex_unlock(&dev->mode_config.mutex);
331 static void drm_fb_helper_off(struct fb_info *info, int dpms_mode)
333 struct drm_fb_helper *fb_helper = info->par;
334 struct drm_device *dev = fb_helper->dev;
335 struct drm_crtc *crtc;
336 struct drm_encoder *encoder;
337 int i;
340 * For each CRTC in this fb, find all associated encoders
341 * and turn them off, then turn off the CRTC.
343 for (i = 0; i < fb_helper->crtc_count; i++) {
344 list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
345 struct drm_crtc_helper_funcs *crtc_funcs =
346 crtc->helper_private;
348 /* Only mess with CRTCs in this fb */
349 if (crtc->base.id != fb_helper->crtc_info[i].crtc_id ||
350 !crtc->enabled)
351 continue;
353 /* Found a CRTC on this fb, now find encoders */
354 list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
355 if (encoder->crtc == crtc) {
356 struct drm_encoder_helper_funcs *encoder_funcs;
358 encoder_funcs = encoder->helper_private;
359 mutex_lock(&dev->mode_config.mutex);
360 encoder_funcs->dpms(encoder, dpms_mode);
361 mutex_unlock(&dev->mode_config.mutex);
364 mutex_lock(&dev->mode_config.mutex);
365 crtc_funcs->dpms(crtc, DRM_MODE_DPMS_OFF);
366 mutex_unlock(&dev->mode_config.mutex);
371 int drm_fb_helper_blank(int blank, struct fb_info *info)
373 switch (blank) {
374 /* Display: On; HSync: On, VSync: On */
375 case FB_BLANK_UNBLANK:
376 drm_fb_helper_on(info);
377 break;
378 /* Display: Off; HSync: On, VSync: On */
379 case FB_BLANK_NORMAL:
380 drm_fb_helper_off(info, DRM_MODE_DPMS_STANDBY);
381 break;
382 /* Display: Off; HSync: Off, VSync: On */
383 case FB_BLANK_HSYNC_SUSPEND:
384 drm_fb_helper_off(info, DRM_MODE_DPMS_STANDBY);
385 break;
386 /* Display: Off; HSync: On, VSync: Off */
387 case FB_BLANK_VSYNC_SUSPEND:
388 drm_fb_helper_off(info, DRM_MODE_DPMS_SUSPEND);
389 break;
390 /* Display: Off; HSync: Off, VSync: Off */
391 case FB_BLANK_POWERDOWN:
392 drm_fb_helper_off(info, DRM_MODE_DPMS_OFF);
393 break;
395 return 0;
397 EXPORT_SYMBOL(drm_fb_helper_blank);
399 static void drm_fb_helper_crtc_free(struct drm_fb_helper *helper)
401 int i;
403 for (i = 0; i < helper->crtc_count; i++)
404 kfree(helper->crtc_info[i].mode_set.connectors);
405 kfree(helper->crtc_info);
408 int drm_fb_helper_init_crtc_count(struct drm_fb_helper *helper, int crtc_count, int max_conn_count)
410 struct drm_device *dev = helper->dev;
411 struct drm_crtc *crtc;
412 int ret = 0;
413 int i;
415 helper->crtc_info = kcalloc(crtc_count, sizeof(struct drm_fb_helper_crtc), GFP_KERNEL);
416 if (!helper->crtc_info)
417 return -ENOMEM;
419 helper->crtc_count = crtc_count;
421 for (i = 0; i < crtc_count; i++) {
422 helper->crtc_info[i].mode_set.connectors =
423 kcalloc(max_conn_count,
424 sizeof(struct drm_connector *),
425 GFP_KERNEL);
427 if (!helper->crtc_info[i].mode_set.connectors) {
428 ret = -ENOMEM;
429 goto out_free;
431 helper->crtc_info[i].mode_set.num_connectors = 0;
434 i = 0;
435 list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
436 helper->crtc_info[i].crtc_id = crtc->base.id;
437 helper->crtc_info[i].mode_set.crtc = crtc;
438 i++;
440 helper->conn_limit = max_conn_count;
441 return 0;
442 out_free:
443 drm_fb_helper_crtc_free(helper);
444 return -ENOMEM;
446 EXPORT_SYMBOL(drm_fb_helper_init_crtc_count);
448 static int setcolreg(struct drm_crtc *crtc, u16 red, u16 green,
449 u16 blue, u16 regno, struct fb_info *info)
451 struct drm_fb_helper *fb_helper = info->par;
452 struct drm_framebuffer *fb = fb_helper->fb;
453 int pindex;
455 if (info->fix.visual == FB_VISUAL_TRUECOLOR) {
456 u32 *palette;
457 u32 value;
458 /* place color in psuedopalette */
459 if (regno > 16)
460 return -EINVAL;
461 palette = (u32 *)info->pseudo_palette;
462 red >>= (16 - info->var.red.length);
463 green >>= (16 - info->var.green.length);
464 blue >>= (16 - info->var.blue.length);
465 value = (red << info->var.red.offset) |
466 (green << info->var.green.offset) |
467 (blue << info->var.blue.offset);
468 palette[regno] = value;
469 return 0;
472 pindex = regno;
474 if (fb->bits_per_pixel == 16) {
475 pindex = regno << 3;
477 if (fb->depth == 16 && regno > 63)
478 return -EINVAL;
479 if (fb->depth == 15 && regno > 31)
480 return -EINVAL;
482 if (fb->depth == 16) {
483 u16 r, g, b;
484 int i;
485 if (regno < 32) {
486 for (i = 0; i < 8; i++)
487 fb_helper->funcs->gamma_set(crtc, red,
488 green, blue, pindex + i);
491 fb_helper->funcs->gamma_get(crtc, &r,
492 &g, &b,
493 pindex >> 1);
495 for (i = 0; i < 4; i++)
496 fb_helper->funcs->gamma_set(crtc, r,
497 green, b,
498 (pindex >> 1) + i);
502 if (fb->depth != 16)
503 fb_helper->funcs->gamma_set(crtc, red, green, blue, pindex);
504 return 0;
507 int drm_fb_helper_setcmap(struct fb_cmap *cmap, struct fb_info *info)
509 struct drm_fb_helper *fb_helper = info->par;
510 struct drm_device *dev = fb_helper->dev;
511 u16 *red, *green, *blue, *transp;
512 struct drm_crtc *crtc;
513 int i, rc = 0;
514 int start;
516 list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
517 struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private;
518 for (i = 0; i < fb_helper->crtc_count; i++) {
519 if (crtc->base.id == fb_helper->crtc_info[i].crtc_id)
520 break;
522 if (i == fb_helper->crtc_count)
523 continue;
525 red = cmap->red;
526 green = cmap->green;
527 blue = cmap->blue;
528 transp = cmap->transp;
529 start = cmap->start;
531 for (i = 0; i < cmap->len; i++) {
532 u16 hred, hgreen, hblue, htransp = 0xffff;
534 hred = *red++;
535 hgreen = *green++;
536 hblue = *blue++;
538 if (transp)
539 htransp = *transp++;
541 rc = setcolreg(crtc, hred, hgreen, hblue, start++, info);
542 if (rc)
543 return rc;
545 crtc_funcs->load_lut(crtc);
547 return rc;
549 EXPORT_SYMBOL(drm_fb_helper_setcmap);
551 int drm_fb_helper_setcolreg(unsigned regno,
552 unsigned red,
553 unsigned green,
554 unsigned blue,
555 unsigned transp,
556 struct fb_info *info)
558 struct drm_fb_helper *fb_helper = info->par;
559 struct drm_device *dev = fb_helper->dev;
560 struct drm_crtc *crtc;
561 int i;
562 int ret;
564 if (regno > 255)
565 return 1;
567 list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
568 struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private;
569 for (i = 0; i < fb_helper->crtc_count; i++) {
570 if (crtc->base.id == fb_helper->crtc_info[i].crtc_id)
571 break;
573 if (i == fb_helper->crtc_count)
574 continue;
576 ret = setcolreg(crtc, red, green, blue, regno, info);
577 if (ret)
578 return ret;
580 crtc_funcs->load_lut(crtc);
582 return 0;
584 EXPORT_SYMBOL(drm_fb_helper_setcolreg);
586 int drm_fb_helper_check_var(struct fb_var_screeninfo *var,
587 struct fb_info *info)
589 struct drm_fb_helper *fb_helper = info->par;
590 struct drm_framebuffer *fb = fb_helper->fb;
591 int depth;
593 if (var->pixclock != 0)
594 return -EINVAL;
596 /* Need to resize the fb object !!! */
597 if (var->bits_per_pixel > fb->bits_per_pixel || var->xres > fb->width || var->yres > fb->height) {
598 DRM_DEBUG("fb userspace requested width/height/bpp is greater than current fb "
599 "object %dx%d-%d > %dx%d-%d\n", var->xres, var->yres, var->bits_per_pixel,
600 fb->width, fb->height, fb->bits_per_pixel);
601 return -EINVAL;
604 switch (var->bits_per_pixel) {
605 case 16:
606 depth = (var->green.length == 6) ? 16 : 15;
607 break;
608 case 32:
609 depth = (var->transp.length > 0) ? 32 : 24;
610 break;
611 default:
612 depth = var->bits_per_pixel;
613 break;
616 switch (depth) {
617 case 8:
618 var->red.offset = 0;
619 var->green.offset = 0;
620 var->blue.offset = 0;
621 var->red.length = 8;
622 var->green.length = 8;
623 var->blue.length = 8;
624 var->transp.length = 0;
625 var->transp.offset = 0;
626 break;
627 case 15:
628 var->red.offset = 10;
629 var->green.offset = 5;
630 var->blue.offset = 0;
631 var->red.length = 5;
632 var->green.length = 5;
633 var->blue.length = 5;
634 var->transp.length = 1;
635 var->transp.offset = 15;
636 break;
637 case 16:
638 var->red.offset = 11;
639 var->green.offset = 5;
640 var->blue.offset = 0;
641 var->red.length = 5;
642 var->green.length = 6;
643 var->blue.length = 5;
644 var->transp.length = 0;
645 var->transp.offset = 0;
646 break;
647 case 24:
648 var->red.offset = 16;
649 var->green.offset = 8;
650 var->blue.offset = 0;
651 var->red.length = 8;
652 var->green.length = 8;
653 var->blue.length = 8;
654 var->transp.length = 0;
655 var->transp.offset = 0;
656 break;
657 case 32:
658 var->red.offset = 16;
659 var->green.offset = 8;
660 var->blue.offset = 0;
661 var->red.length = 8;
662 var->green.length = 8;
663 var->blue.length = 8;
664 var->transp.length = 8;
665 var->transp.offset = 24;
666 break;
667 default:
668 return -EINVAL;
670 return 0;
672 EXPORT_SYMBOL(drm_fb_helper_check_var);
674 /* this will let fbcon do the mode init */
675 int drm_fb_helper_set_par(struct fb_info *info)
677 struct drm_fb_helper *fb_helper = info->par;
678 struct drm_device *dev = fb_helper->dev;
679 struct fb_var_screeninfo *var = &info->var;
680 struct drm_crtc *crtc;
681 int ret;
682 int i;
684 if (var->pixclock != 0) {
685 DRM_ERROR("PIXEL CLOCK SET\n");
686 return -EINVAL;
689 list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
691 for (i = 0; i < fb_helper->crtc_count; i++) {
692 if (crtc->base.id == fb_helper->crtc_info[i].crtc_id)
693 break;
695 if (i == fb_helper->crtc_count)
696 continue;
698 if (crtc->fb == fb_helper->crtc_info[i].mode_set.fb) {
699 mutex_lock(&dev->mode_config.mutex);
700 ret = crtc->funcs->set_config(&fb_helper->crtc_info[i].mode_set);
701 mutex_unlock(&dev->mode_config.mutex);
702 if (ret)
703 return ret;
706 return 0;
708 EXPORT_SYMBOL(drm_fb_helper_set_par);
710 int drm_fb_helper_pan_display(struct fb_var_screeninfo *var,
711 struct fb_info *info)
713 struct drm_fb_helper *fb_helper = info->par;
714 struct drm_device *dev = fb_helper->dev;
715 struct drm_mode_set *modeset;
716 struct drm_crtc *crtc;
717 int ret = 0;
718 int i;
720 list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
721 for (i = 0; i < fb_helper->crtc_count; i++) {
722 if (crtc->base.id == fb_helper->crtc_info[i].crtc_id)
723 break;
726 if (i == fb_helper->crtc_count)
727 continue;
729 modeset = &fb_helper->crtc_info[i].mode_set;
731 modeset->x = var->xoffset;
732 modeset->y = var->yoffset;
734 if (modeset->num_connectors) {
735 mutex_lock(&dev->mode_config.mutex);
736 ret = crtc->funcs->set_config(modeset);
737 mutex_unlock(&dev->mode_config.mutex);
738 if (!ret) {
739 info->var.xoffset = var->xoffset;
740 info->var.yoffset = var->yoffset;
744 return ret;
746 EXPORT_SYMBOL(drm_fb_helper_pan_display);
748 int drm_fb_helper_single_fb_probe(struct drm_device *dev,
749 int preferred_bpp,
750 int (*fb_create)(struct drm_device *dev,
751 uint32_t fb_width,
752 uint32_t fb_height,
753 uint32_t surface_width,
754 uint32_t surface_height,
755 uint32_t surface_depth,
756 uint32_t surface_bpp,
757 struct drm_framebuffer **fb_ptr))
759 struct drm_crtc *crtc;
760 struct drm_connector *connector;
761 unsigned int fb_width = (unsigned)-1, fb_height = (unsigned)-1;
762 unsigned int surface_width = 0, surface_height = 0;
763 int new_fb = 0;
764 int crtc_count = 0;
765 int ret, i, conn_count = 0;
766 struct fb_info *info;
767 struct drm_framebuffer *fb;
768 struct drm_mode_set *modeset = NULL;
769 struct drm_fb_helper *fb_helper;
770 uint32_t surface_depth = 24, surface_bpp = 32;
772 /* if driver picks 8 or 16 by default use that
773 for both depth/bpp */
774 if (preferred_bpp != surface_bpp) {
775 surface_depth = surface_bpp = preferred_bpp;
777 /* first up get a count of crtcs now in use and new min/maxes width/heights */
778 list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
779 struct drm_fb_helper_connector *fb_help_conn = connector->fb_helper_private;
781 struct drm_fb_helper_cmdline_mode *cmdline_mode;
783 if (!fb_help_conn)
784 continue;
786 cmdline_mode = &fb_help_conn->cmdline_mode;
788 if (cmdline_mode->bpp_specified) {
789 switch (cmdline_mode->bpp) {
790 case 8:
791 surface_depth = surface_bpp = 8;
792 break;
793 case 15:
794 surface_depth = 15;
795 surface_bpp = 16;
796 break;
797 case 16:
798 surface_depth = surface_bpp = 16;
799 break;
800 case 24:
801 surface_depth = surface_bpp = 24;
802 break;
803 case 32:
804 surface_depth = 24;
805 surface_bpp = 32;
806 break;
808 break;
812 list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
813 if (drm_helper_crtc_in_use(crtc)) {
814 if (crtc->desired_mode) {
815 if (crtc->desired_mode->hdisplay < fb_width)
816 fb_width = crtc->desired_mode->hdisplay;
818 if (crtc->desired_mode->vdisplay < fb_height)
819 fb_height = crtc->desired_mode->vdisplay;
821 if (crtc->desired_mode->hdisplay > surface_width)
822 surface_width = crtc->desired_mode->hdisplay;
824 if (crtc->desired_mode->vdisplay > surface_height)
825 surface_height = crtc->desired_mode->vdisplay;
827 crtc_count++;
831 if (crtc_count == 0 || fb_width == -1 || fb_height == -1) {
832 /* hmm everyone went away - assume VGA cable just fell out
833 and will come back later. */
834 return 0;
837 /* do we have an fb already? */
838 if (list_empty(&dev->mode_config.fb_kernel_list)) {
839 ret = (*fb_create)(dev, fb_width, fb_height, surface_width,
840 surface_height, surface_depth, surface_bpp,
841 &fb);
842 if (ret)
843 return -EINVAL;
844 new_fb = 1;
845 } else {
846 fb = list_first_entry(&dev->mode_config.fb_kernel_list,
847 struct drm_framebuffer, filp_head);
849 /* if someone hotplugs something bigger than we have already allocated, we are pwned.
850 As really we can't resize an fbdev that is in the wild currently due to fbdev
851 not really being designed for the lower layers moving stuff around under it.
852 - so in the grand style of things - punt. */
853 if ((fb->width < surface_width) ||
854 (fb->height < surface_height)) {
855 DRM_ERROR("Framebuffer not large enough to scale console onto.\n");
856 return -EINVAL;
860 info = fb->fbdev;
861 fb_helper = info->par;
863 crtc_count = 0;
864 /* okay we need to setup new connector sets in the crtcs */
865 list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
866 modeset = &fb_helper->crtc_info[crtc_count].mode_set;
867 modeset->fb = fb;
868 conn_count = 0;
869 list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
870 if (connector->encoder)
871 if (connector->encoder->crtc == modeset->crtc) {
872 modeset->connectors[conn_count] = connector;
873 conn_count++;
874 if (conn_count > fb_helper->conn_limit)
875 BUG();
879 for (i = conn_count; i < fb_helper->conn_limit; i++)
880 modeset->connectors[i] = NULL;
882 modeset->crtc = crtc;
883 crtc_count++;
885 modeset->num_connectors = conn_count;
886 if (modeset->crtc->desired_mode) {
887 if (modeset->mode)
888 drm_mode_destroy(dev, modeset->mode);
889 modeset->mode = drm_mode_duplicate(dev,
890 modeset->crtc->desired_mode);
893 fb_helper->crtc_count = crtc_count;
894 fb_helper->fb = fb;
896 if (new_fb) {
897 info->var.pixclock = 0;
898 ret = fb_alloc_cmap(&info->cmap, modeset->crtc->gamma_size, 0);
899 if (ret)
900 return ret;
901 if (register_framebuffer(info) < 0) {
902 fb_dealloc_cmap(&info->cmap);
903 return -EINVAL;
905 } else {
906 drm_fb_helper_set_par(info);
908 printk(KERN_INFO "fb%d: %s frame buffer device\n", info->node,
909 info->fix.id);
911 /* Switch back to kernel console on panic */
912 /* multi card linked list maybe */
913 if (list_empty(&kernel_fb_helper_list)) {
914 printk(KERN_INFO "registered panic notifier\n");
915 atomic_notifier_chain_register(&panic_notifier_list,
916 &paniced);
917 register_sysrq_key('v', &sysrq_drm_fb_helper_restore_op);
919 list_add(&fb_helper->kernel_fb_list, &kernel_fb_helper_list);
920 return 0;
922 EXPORT_SYMBOL(drm_fb_helper_single_fb_probe);
924 void drm_fb_helper_free(struct drm_fb_helper *helper)
926 list_del(&helper->kernel_fb_list);
927 if (list_empty(&kernel_fb_helper_list)) {
928 printk(KERN_INFO "unregistered panic notifier\n");
929 atomic_notifier_chain_unregister(&panic_notifier_list,
930 &paniced);
931 unregister_sysrq_key('v', &sysrq_drm_fb_helper_restore_op);
933 drm_fb_helper_crtc_free(helper);
934 fb_dealloc_cmap(&helper->fb->fbdev->cmap);
936 EXPORT_SYMBOL(drm_fb_helper_free);
938 void drm_fb_helper_fill_fix(struct fb_info *info, uint32_t pitch,
939 uint32_t depth)
941 info->fix.type = FB_TYPE_PACKED_PIXELS;
942 info->fix.visual = depth == 8 ? FB_VISUAL_PSEUDOCOLOR :
943 FB_VISUAL_TRUECOLOR;
944 info->fix.type_aux = 0;
945 info->fix.xpanstep = 1; /* doing it in hw */
946 info->fix.ypanstep = 1; /* doing it in hw */
947 info->fix.ywrapstep = 0;
948 info->fix.accel = FB_ACCEL_NONE;
949 info->fix.type_aux = 0;
951 info->fix.line_length = pitch;
952 return;
954 EXPORT_SYMBOL(drm_fb_helper_fill_fix);
956 void drm_fb_helper_fill_var(struct fb_info *info, struct drm_framebuffer *fb,
957 uint32_t fb_width, uint32_t fb_height)
959 info->pseudo_palette = fb->pseudo_palette;
960 info->var.xres_virtual = fb->width;
961 info->var.yres_virtual = fb->height;
962 info->var.bits_per_pixel = fb->bits_per_pixel;
963 info->var.xoffset = 0;
964 info->var.yoffset = 0;
965 info->var.activate = FB_ACTIVATE_NOW;
966 info->var.height = -1;
967 info->var.width = -1;
969 switch (fb->depth) {
970 case 8:
971 info->var.red.offset = 0;
972 info->var.green.offset = 0;
973 info->var.blue.offset = 0;
974 info->var.red.length = 8; /* 8bit DAC */
975 info->var.green.length = 8;
976 info->var.blue.length = 8;
977 info->var.transp.offset = 0;
978 info->var.transp.length = 0;
979 break;
980 case 15:
981 info->var.red.offset = 10;
982 info->var.green.offset = 5;
983 info->var.blue.offset = 0;
984 info->var.red.length = 5;
985 info->var.green.length = 5;
986 info->var.blue.length = 5;
987 info->var.transp.offset = 15;
988 info->var.transp.length = 1;
989 break;
990 case 16:
991 info->var.red.offset = 11;
992 info->var.green.offset = 5;
993 info->var.blue.offset = 0;
994 info->var.red.length = 5;
995 info->var.green.length = 6;
996 info->var.blue.length = 5;
997 info->var.transp.offset = 0;
998 break;
999 case 24:
1000 info->var.red.offset = 16;
1001 info->var.green.offset = 8;
1002 info->var.blue.offset = 0;
1003 info->var.red.length = 8;
1004 info->var.green.length = 8;
1005 info->var.blue.length = 8;
1006 info->var.transp.offset = 0;
1007 info->var.transp.length = 0;
1008 break;
1009 case 32:
1010 info->var.red.offset = 16;
1011 info->var.green.offset = 8;
1012 info->var.blue.offset = 0;
1013 info->var.red.length = 8;
1014 info->var.green.length = 8;
1015 info->var.blue.length = 8;
1016 info->var.transp.offset = 24;
1017 info->var.transp.length = 8;
1018 break;
1019 default:
1020 break;
1023 info->var.xres = fb_width;
1024 info->var.yres = fb_height;
1026 EXPORT_SYMBOL(drm_fb_helper_fill_var);