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
27 * Dave Airlie <airlied@linux.ie>
28 * Jesse Barnes <jesse.barnes@intel.com>
30 #include <linux/sysrq.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 bool drm_fb_helper_force_kernel_mode(void)
46 bool ret, error = false;
47 struct drm_fb_helper *helper;
49 if (list_empty(&kernel_fb_helper_list))
52 list_for_each_entry(helper, &kernel_fb_helper_list, kernel_fb_list) {
53 for (i = 0; i < helper->crtc_count; i++) {
54 struct drm_mode_set *mode_set = &helper->crtc_info[i].mode_set;
55 ret = drm_crtc_helper_set_config(mode_set);
63 int drm_fb_helper_panic(struct notifier_block *n, unsigned long ununsed,
66 DRM_ERROR("panic occurred, switching back to text console\n");
67 return drm_fb_helper_force_kernel_mode();
70 EXPORT_SYMBOL(drm_fb_helper_panic);
72 static struct notifier_block paniced = {
73 .notifier_call = drm_fb_helper_panic,
77 * drm_fb_helper_restore - restore the framebuffer console (kernel) config
79 * Restore's the kernel's fbcon mode, used for lastclose & panic paths.
81 void drm_fb_helper_restore(void)
84 ret = drm_fb_helper_force_kernel_mode();
86 DRM_ERROR("Failed to restore crtc configuration\n");
88 EXPORT_SYMBOL(drm_fb_helper_restore);
90 static void drm_fb_helper_restore_work_fn(struct work_struct *ignored)
92 drm_fb_helper_restore();
94 static DECLARE_WORK(drm_fb_helper_restore_work, drm_fb_helper_restore_work_fn);
96 static void drm_fb_helper_sysrq(int dummy1, struct tty_struct *dummy3)
98 schedule_work(&drm_fb_helper_restore_work);
101 static struct sysrq_key_op sysrq_drm_fb_helper_restore_op = {
102 .handler = drm_fb_helper_sysrq,
103 .help_msg = "force-fb(V)",
104 .action_msg = "Restore framebuffer console",
107 static void drm_fb_helper_on(struct fb_info *info)
109 struct drm_fb_helper *fb_helper = info->par;
110 struct drm_device *dev = fb_helper->dev;
111 struct drm_crtc *crtc;
112 struct drm_encoder *encoder;
116 * For each CRTC in this fb, turn the crtc on then,
117 * find all associated encoders and turn them on.
119 for (i = 0; i < fb_helper->crtc_count; i++) {
120 list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
121 struct drm_crtc_helper_funcs *crtc_funcs =
122 crtc->helper_private;
124 /* Only mess with CRTCs in this fb */
125 if (crtc->base.id != fb_helper->crtc_info[i].crtc_id ||
129 mutex_lock(&dev->mode_config.mutex);
130 crtc_funcs->dpms(crtc, DRM_MODE_DPMS_ON);
131 mutex_unlock(&dev->mode_config.mutex);
133 /* Found a CRTC on this fb, now find encoders */
134 list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
135 if (encoder->crtc == crtc) {
136 struct drm_encoder_helper_funcs *encoder_funcs;
138 encoder_funcs = encoder->helper_private;
139 mutex_lock(&dev->mode_config.mutex);
140 encoder_funcs->dpms(encoder, DRM_MODE_DPMS_ON);
141 mutex_unlock(&dev->mode_config.mutex);
148 static void drm_fb_helper_off(struct fb_info *info, int dpms_mode)
150 struct drm_fb_helper *fb_helper = info->par;
151 struct drm_device *dev = fb_helper->dev;
152 struct drm_crtc *crtc;
153 struct drm_encoder *encoder;
157 * For each CRTC in this fb, find all associated encoders
158 * and turn them off, then turn off the CRTC.
160 for (i = 0; i < fb_helper->crtc_count; i++) {
161 list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
162 struct drm_crtc_helper_funcs *crtc_funcs =
163 crtc->helper_private;
165 /* Only mess with CRTCs in this fb */
166 if (crtc->base.id != fb_helper->crtc_info[i].crtc_id ||
170 /* Found a CRTC on this fb, now find encoders */
171 list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
172 if (encoder->crtc == crtc) {
173 struct drm_encoder_helper_funcs *encoder_funcs;
175 encoder_funcs = encoder->helper_private;
176 mutex_lock(&dev->mode_config.mutex);
177 encoder_funcs->dpms(encoder, dpms_mode);
178 mutex_unlock(&dev->mode_config.mutex);
181 if (dpms_mode == DRM_MODE_DPMS_OFF) {
182 mutex_lock(&dev->mode_config.mutex);
183 crtc_funcs->dpms(crtc, dpms_mode);
184 mutex_unlock(&dev->mode_config.mutex);
190 int drm_fb_helper_blank(int blank, struct fb_info *info)
193 case FB_BLANK_UNBLANK:
194 drm_fb_helper_on(info);
196 case FB_BLANK_NORMAL:
197 drm_fb_helper_off(info, DRM_MODE_DPMS_STANDBY);
199 case FB_BLANK_HSYNC_SUSPEND:
200 drm_fb_helper_off(info, DRM_MODE_DPMS_STANDBY);
202 case FB_BLANK_VSYNC_SUSPEND:
203 drm_fb_helper_off(info, DRM_MODE_DPMS_SUSPEND);
205 case FB_BLANK_POWERDOWN:
206 drm_fb_helper_off(info, DRM_MODE_DPMS_OFF);
211 EXPORT_SYMBOL(drm_fb_helper_blank);
213 static void drm_fb_helper_crtc_free(struct drm_fb_helper *helper)
217 for (i = 0; i < helper->crtc_count; i++)
218 kfree(helper->crtc_info[i].mode_set.connectors);
219 kfree(helper->crtc_info);
222 int drm_fb_helper_init_crtc_count(struct drm_fb_helper *helper, int crtc_count, int max_conn_count)
224 struct drm_device *dev = helper->dev;
225 struct drm_crtc *crtc;
229 helper->crtc_info = kcalloc(crtc_count, sizeof(struct drm_fb_helper_crtc), GFP_KERNEL);
230 if (!helper->crtc_info)
233 helper->crtc_count = crtc_count;
235 for (i = 0; i < crtc_count; i++) {
236 helper->crtc_info[i].mode_set.connectors =
237 kcalloc(max_conn_count,
238 sizeof(struct drm_connector *),
241 if (!helper->crtc_info[i].mode_set.connectors) {
245 helper->crtc_info[i].mode_set.num_connectors = 0;
249 list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
250 helper->crtc_info[i].crtc_id = crtc->base.id;
251 helper->crtc_info[i].mode_set.crtc = crtc;
254 helper->conn_limit = max_conn_count;
257 drm_fb_helper_crtc_free(helper);
260 EXPORT_SYMBOL(drm_fb_helper_init_crtc_count);
262 int drm_fb_helper_setcolreg(unsigned regno,
267 struct fb_info *info)
269 struct drm_fb_helper *fb_helper = info->par;
270 struct drm_device *dev = fb_helper->dev;
271 struct drm_crtc *crtc;
274 list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
275 struct drm_framebuffer *fb = fb_helper->fb;
277 for (i = 0; i < fb_helper->crtc_count; i++) {
278 if (crtc->base.id == fb_helper->crtc_info[i].crtc_id)
281 if (i == fb_helper->crtc_count)
287 if (fb->depth == 8) {
288 fb_helper->funcs->gamma_set(crtc, red, green, blue, regno);
295 fb->pseudo_palette[regno] = ((red & 0xf800) >> 1) |
296 ((green & 0xf800) >> 6) |
297 ((blue & 0xf800) >> 11);
300 fb->pseudo_palette[regno] = (red & 0xf800) |
301 ((green & 0xfc00) >> 5) |
302 ((blue & 0xf800) >> 11);
306 fb->pseudo_palette[regno] =
307 (((red >> 8) & 0xff) << info->var.red.offset) |
308 (((green >> 8) & 0xff) << info->var.green.offset) |
309 (((blue >> 8) & 0xff) << info->var.blue.offset);
316 EXPORT_SYMBOL(drm_fb_helper_setcolreg);
318 int drm_fb_helper_check_var(struct fb_var_screeninfo *var,
319 struct fb_info *info)
321 struct drm_fb_helper *fb_helper = info->par;
322 struct drm_framebuffer *fb = fb_helper->fb;
325 if (var->pixclock == -1 || !var->pixclock)
328 /* Need to resize the fb object !!! */
329 if (var->xres > fb->width || var->yres > fb->height) {
330 DRM_ERROR("Requested width/height is greater than current fb "
331 "object %dx%d > %dx%d\n", var->xres, var->yres,
332 fb->width, fb->height);
333 DRM_ERROR("Need resizing code.\n");
337 switch (var->bits_per_pixel) {
339 depth = (var->green.length == 6) ? 16 : 15;
342 depth = (var->transp.length > 0) ? 32 : 24;
345 depth = var->bits_per_pixel;
352 var->green.offset = 0;
353 var->blue.offset = 0;
355 var->green.length = 8;
356 var->blue.length = 8;
357 var->transp.length = 0;
358 var->transp.offset = 0;
361 var->red.offset = 10;
362 var->green.offset = 5;
363 var->blue.offset = 0;
365 var->green.length = 5;
366 var->blue.length = 5;
367 var->transp.length = 1;
368 var->transp.offset = 15;
371 var->red.offset = 11;
372 var->green.offset = 5;
373 var->blue.offset = 0;
375 var->green.length = 6;
376 var->blue.length = 5;
377 var->transp.length = 0;
378 var->transp.offset = 0;
381 var->red.offset = 16;
382 var->green.offset = 8;
383 var->blue.offset = 0;
385 var->green.length = 8;
386 var->blue.length = 8;
387 var->transp.length = 0;
388 var->transp.offset = 0;
391 var->red.offset = 16;
392 var->green.offset = 8;
393 var->blue.offset = 0;
395 var->green.length = 8;
396 var->blue.length = 8;
397 var->transp.length = 8;
398 var->transp.offset = 24;
405 EXPORT_SYMBOL(drm_fb_helper_check_var);
407 /* this will let fbcon do the mode init */
408 int drm_fb_helper_set_par(struct fb_info *info)
410 struct drm_fb_helper *fb_helper = info->par;
411 struct drm_device *dev = fb_helper->dev;
412 struct fb_var_screeninfo *var = &info->var;
413 struct drm_crtc *crtc;
417 if (var->pixclock != -1) {
418 DRM_ERROR("PIXEL CLCOK SET\n");
422 list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
424 for (i = 0; i < fb_helper->crtc_count; i++) {
425 if (crtc->base.id == fb_helper->crtc_info[i].crtc_id)
428 if (i == fb_helper->crtc_count)
431 if (crtc->fb == fb_helper->crtc_info[i].mode_set.fb) {
432 mutex_lock(&dev->mode_config.mutex);
433 ret = crtc->funcs->set_config(&fb_helper->crtc_info->mode_set);
434 mutex_unlock(&dev->mode_config.mutex);
441 EXPORT_SYMBOL(drm_fb_helper_set_par);
443 int drm_fb_helper_pan_display(struct fb_var_screeninfo *var,
444 struct fb_info *info)
446 struct drm_fb_helper *fb_helper = info->par;
447 struct drm_device *dev = fb_helper->dev;
448 struct drm_mode_set *modeset;
449 struct drm_crtc *crtc;
453 list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
454 for (i = 0; i < fb_helper->crtc_count; i++) {
455 if (crtc->base.id == fb_helper->crtc_info[i].crtc_id)
459 if (i == fb_helper->crtc_count)
462 modeset = &fb_helper->crtc_info[i].mode_set;
464 modeset->x = var->xoffset;
465 modeset->y = var->yoffset;
467 if (modeset->num_connectors) {
468 mutex_lock(&dev->mode_config.mutex);
469 ret = crtc->funcs->set_config(modeset);
470 mutex_unlock(&dev->mode_config.mutex);
472 info->var.xoffset = var->xoffset;
473 info->var.yoffset = var->yoffset;
479 EXPORT_SYMBOL(drm_fb_helper_pan_display);
481 int drm_fb_helper_single_fb_probe(struct drm_device *dev,
482 int (*fb_create)(struct drm_device *dev,
485 uint32_t surface_width,
486 uint32_t surface_height,
487 struct drm_framebuffer **fb_ptr))
489 struct drm_crtc *crtc;
490 struct drm_connector *connector;
491 unsigned int fb_width = (unsigned)-1, fb_height = (unsigned)-1;
492 unsigned int surface_width = 0, surface_height = 0;
495 int ret, i, conn_count = 0;
496 struct fb_info *info;
497 struct drm_framebuffer *fb;
498 struct drm_mode_set *modeset = NULL;
499 struct drm_fb_helper *fb_helper;
501 /* first up get a count of crtcs now in use and new min/maxes width/heights */
502 list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
503 if (drm_helper_crtc_in_use(crtc)) {
504 if (crtc->desired_mode) {
505 if (crtc->desired_mode->hdisplay < fb_width)
506 fb_width = crtc->desired_mode->hdisplay;
508 if (crtc->desired_mode->vdisplay < fb_height)
509 fb_height = crtc->desired_mode->vdisplay;
511 if (crtc->desired_mode->hdisplay > surface_width)
512 surface_width = crtc->desired_mode->hdisplay;
514 if (crtc->desired_mode->vdisplay > surface_height)
515 surface_height = crtc->desired_mode->vdisplay;
521 if (crtc_count == 0 || fb_width == -1 || fb_height == -1) {
522 /* hmm everyone went away - assume VGA cable just fell out
523 and will come back later. */
527 /* do we have an fb already? */
528 if (list_empty(&dev->mode_config.fb_kernel_list)) {
529 ret = (*fb_create)(dev, fb_width, fb_height, surface_width,
530 surface_height, &fb);
535 fb = list_first_entry(&dev->mode_config.fb_kernel_list,
536 struct drm_framebuffer, filp_head);
538 /* if someone hotplugs something bigger than we have already allocated, we are pwned.
539 As really we can't resize an fbdev that is in the wild currently due to fbdev
540 not really being designed for the lower layers moving stuff around under it.
541 - so in the grand style of things - punt. */
542 if ((fb->width < surface_width) ||
543 (fb->height < surface_height)) {
544 DRM_ERROR("Framebuffer not large enough to scale console onto.\n");
550 fb_helper = info->par;
553 /* okay we need to setup new connector sets in the crtcs */
554 list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
555 modeset = &fb_helper->crtc_info[crtc_count].mode_set;
558 list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
559 if (connector->encoder)
560 if (connector->encoder->crtc == modeset->crtc) {
561 modeset->connectors[conn_count] = connector;
563 if (conn_count > fb_helper->conn_limit)
568 for (i = conn_count; i < fb_helper->conn_limit; i++)
569 modeset->connectors[i] = NULL;
571 modeset->crtc = crtc;
574 modeset->num_connectors = conn_count;
575 if (modeset->crtc->desired_mode) {
577 drm_mode_destroy(dev, modeset->mode);
578 modeset->mode = drm_mode_duplicate(dev,
579 modeset->crtc->desired_mode);
582 fb_helper->crtc_count = crtc_count;
586 info->var.pixclock = -1;
587 if (register_framebuffer(info) < 0)
590 drm_fb_helper_set_par(info);
592 printk(KERN_INFO "fb%d: %s frame buffer device\n", info->node,
595 /* Switch back to kernel console on panic */
596 /* multi card linked list maybe */
597 if (list_empty(&kernel_fb_helper_list)) {
598 printk(KERN_INFO "registered panic notifier\n");
599 atomic_notifier_chain_register(&panic_notifier_list,
601 register_sysrq_key('v', &sysrq_drm_fb_helper_restore_op);
603 list_add(&fb_helper->kernel_fb_list, &kernel_fb_helper_list);
606 EXPORT_SYMBOL(drm_fb_helper_single_fb_probe);
608 void drm_fb_helper_free(struct drm_fb_helper *helper)
610 list_del(&helper->kernel_fb_list);
611 if (list_empty(&kernel_fb_helper_list)) {
612 printk(KERN_INFO "unregistered panic notifier\n");
613 atomic_notifier_chain_unregister(&panic_notifier_list,
615 unregister_sysrq_key('v', &sysrq_drm_fb_helper_restore_op);
617 drm_fb_helper_crtc_free(helper);
619 EXPORT_SYMBOL(drm_fb_helper_free);
621 void drm_fb_helper_fill_fix(struct fb_info *info, uint32_t pitch)
623 info->fix.type = FB_TYPE_PACKED_PIXELS;
624 info->fix.visual = FB_VISUAL_TRUECOLOR;
625 info->fix.type_aux = 0;
626 info->fix.xpanstep = 1; /* doing it in hw */
627 info->fix.ypanstep = 1; /* doing it in hw */
628 info->fix.ywrapstep = 0;
629 info->fix.accel = FB_ACCEL_NONE;
630 info->fix.type_aux = 0;
632 info->fix.line_length = pitch;
635 EXPORT_SYMBOL(drm_fb_helper_fill_fix);
637 void drm_fb_helper_fill_var(struct fb_info *info, struct drm_framebuffer *fb,
638 uint32_t fb_width, uint32_t fb_height)
640 info->pseudo_palette = fb->pseudo_palette;
641 info->var.xres_virtual = fb->width;
642 info->var.yres_virtual = fb->height;
643 info->var.bits_per_pixel = fb->bits_per_pixel;
644 info->var.xoffset = 0;
645 info->var.yoffset = 0;
646 info->var.activate = FB_ACTIVATE_NOW;
647 info->var.height = -1;
648 info->var.width = -1;
652 info->var.red.offset = 0;
653 info->var.green.offset = 0;
654 info->var.blue.offset = 0;
655 info->var.red.length = 8; /* 8bit DAC */
656 info->var.green.length = 8;
657 info->var.blue.length = 8;
658 info->var.transp.offset = 0;
659 info->var.transp.length = 0;
662 info->var.red.offset = 10;
663 info->var.green.offset = 5;
664 info->var.blue.offset = 0;
665 info->var.red.length = 5;
666 info->var.green.length = 5;
667 info->var.blue.length = 5;
668 info->var.transp.offset = 15;
669 info->var.transp.length = 1;
672 info->var.red.offset = 11;
673 info->var.green.offset = 5;
674 info->var.blue.offset = 0;
675 info->var.red.length = 5;
676 info->var.green.length = 6;
677 info->var.blue.length = 5;
678 info->var.transp.offset = 0;
681 info->var.red.offset = 16;
682 info->var.green.offset = 8;
683 info->var.blue.offset = 0;
684 info->var.red.length = 8;
685 info->var.green.length = 8;
686 info->var.blue.length = 8;
687 info->var.transp.offset = 0;
688 info->var.transp.length = 0;
691 info->var.red.offset = 16;
692 info->var.green.offset = 8;
693 info->var.blue.offset = 0;
694 info->var.red.length = 8;
695 info->var.green.length = 8;
696 info->var.blue.length = 8;
697 info->var.transp.offset = 24;
698 info->var.transp.length = 8;
704 info->var.xres = fb_width;
705 info->var.yres = fb_height;
707 EXPORT_SYMBOL(drm_fb_helper_fill_var);