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 static LIST_HEAD(kernel_fb_helper_list);
39 bool drm_fb_helper_force_kernel_mode(void)
42 bool ret, error = false;
43 struct drm_fb_helper *helper;
45 if (list_empty(&kernel_fb_helper_list))
48 list_for_each_entry(helper, &kernel_fb_helper_list, kernel_fb_list) {
49 for (i = 0; i < helper->crtc_count; i++) {
50 struct drm_mode_set *mode_set = &helper->crtc_info[i].mode_set;
51 ret = drm_crtc_helper_set_config(mode_set);
59 int drm_fb_helper_panic(struct notifier_block *n, unsigned long ununsed,
62 DRM_ERROR("panic occurred, switching back to text console\n");
63 return drm_fb_helper_force_kernel_mode();
66 EXPORT_SYMBOL(drm_fb_helper_panic);
68 static struct notifier_block paniced = {
69 .notifier_call = drm_fb_helper_panic,
73 * drm_fb_helper_restore - restore the framebuffer console (kernel) config
75 * Restore's the kernel's fbcon mode, used for lastclose & panic paths.
77 void drm_fb_helper_restore(void)
80 ret = drm_fb_helper_force_kernel_mode();
82 DRM_ERROR("Failed to restore crtc configuration\n");
84 EXPORT_SYMBOL(drm_fb_helper_restore);
86 static void drm_fb_helper_restore_work_fn(struct work_struct *ignored)
88 drm_fb_helper_restore();
90 static DECLARE_WORK(drm_fb_helper_restore_work, drm_fb_helper_restore_work_fn);
92 static void drm_fb_helper_sysrq(int dummy1, struct tty_struct *dummy3)
94 schedule_work(&drm_fb_helper_restore_work);
97 static struct sysrq_key_op sysrq_drm_fb_helper_restore_op = {
98 .handler = drm_fb_helper_sysrq,
99 .help_msg = "force-fb(V)",
100 .action_msg = "Restore framebuffer console",
103 static void drm_fb_helper_on(struct fb_info *info)
105 struct drm_fb_helper *fb_helper = info->par;
106 struct drm_device *dev = fb_helper->dev;
107 struct drm_crtc *crtc;
108 struct drm_encoder *encoder;
112 * For each CRTC in this fb, turn the crtc on then,
113 * find all associated encoders and turn them on.
115 list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
116 struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private;
118 for (i = 0; i < fb_helper->crtc_count; i++) {
119 if (crtc->base.id == fb_helper->crtc_info[i].crtc_id)
123 mutex_lock(&dev->mode_config.mutex);
124 crtc_funcs->dpms(crtc, DRM_MODE_DPMS_ON);
125 mutex_unlock(&dev->mode_config.mutex);
127 /* Found a CRTC on this fb, now find encoders */
128 list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
129 if (encoder->crtc == crtc) {
130 struct drm_encoder_helper_funcs *encoder_funcs;
132 encoder_funcs = encoder->helper_private;
133 mutex_lock(&dev->mode_config.mutex);
134 encoder_funcs->dpms(encoder, DRM_MODE_DPMS_ON);
135 mutex_unlock(&dev->mode_config.mutex);
141 static void drm_fb_helper_off(struct fb_info *info, int dpms_mode)
143 struct drm_fb_helper *fb_helper = info->par;
144 struct drm_device *dev = fb_helper->dev;
145 struct drm_crtc *crtc;
146 struct drm_encoder *encoder;
150 * For each CRTC in this fb, find all associated encoders
151 * and turn them off, then turn off the CRTC.
153 list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
154 struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private;
156 for (i = 0; i < fb_helper->crtc_count; i++) {
157 if (crtc->base.id == fb_helper->crtc_info[i].crtc_id)
161 /* Found a CRTC on this fb, now find encoders */
162 list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
163 if (encoder->crtc == crtc) {
164 struct drm_encoder_helper_funcs *encoder_funcs;
166 encoder_funcs = encoder->helper_private;
167 mutex_lock(&dev->mode_config.mutex);
168 encoder_funcs->dpms(encoder, dpms_mode);
169 mutex_unlock(&dev->mode_config.mutex);
172 if (dpms_mode == DRM_MODE_DPMS_OFF) {
173 mutex_lock(&dev->mode_config.mutex);
174 crtc_funcs->dpms(crtc, dpms_mode);
175 mutex_unlock(&dev->mode_config.mutex);
180 int drm_fb_helper_blank(int blank, struct fb_info *info)
183 case FB_BLANK_UNBLANK:
184 drm_fb_helper_on(info);
186 case FB_BLANK_NORMAL:
187 drm_fb_helper_off(info, DRM_MODE_DPMS_STANDBY);
189 case FB_BLANK_HSYNC_SUSPEND:
190 drm_fb_helper_off(info, DRM_MODE_DPMS_STANDBY);
192 case FB_BLANK_VSYNC_SUSPEND:
193 drm_fb_helper_off(info, DRM_MODE_DPMS_SUSPEND);
195 case FB_BLANK_POWERDOWN:
196 drm_fb_helper_off(info, DRM_MODE_DPMS_OFF);
201 EXPORT_SYMBOL(drm_fb_helper_blank);
203 static void drm_fb_helper_crtc_free(struct drm_fb_helper *helper)
207 for (i = 0; i < helper->crtc_count; i++)
208 kfree(helper->crtc_info[i].mode_set.connectors);
209 kfree(helper->crtc_info);
212 int drm_fb_helper_init_crtc_count(struct drm_fb_helper *helper, int crtc_count, int max_conn_count)
214 struct drm_device *dev = helper->dev;
215 struct drm_crtc *crtc;
219 helper->crtc_info = kcalloc(crtc_count, sizeof(struct drm_fb_helper_crtc), GFP_KERNEL);
220 if (!helper->crtc_info)
223 helper->crtc_count = crtc_count;
225 for (i = 0; i < crtc_count; i++) {
226 helper->crtc_info[i].mode_set.connectors =
227 kcalloc(max_conn_count,
228 sizeof(struct drm_connector *),
231 if (!helper->crtc_info[i].mode_set.connectors) {
235 helper->crtc_info[i].mode_set.num_connectors = 0;
239 list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
240 helper->crtc_info[i].crtc_id = crtc->base.id;
241 helper->crtc_info[i].mode_set.crtc = crtc;
244 helper->conn_limit = max_conn_count;
247 drm_fb_helper_crtc_free(helper);
250 EXPORT_SYMBOL(drm_fb_helper_init_crtc_count);
252 int drm_fb_helper_setcolreg(unsigned regno,
257 struct fb_info *info)
259 struct drm_fb_helper *fb_helper = info->par;
260 struct drm_device *dev = fb_helper->dev;
261 struct drm_crtc *crtc;
264 list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
265 struct drm_framebuffer *fb = fb_helper->fb;
267 for (i = 0; i < fb_helper->crtc_count; i++) {
268 if (crtc->base.id == fb_helper->crtc_info[i].crtc_id)
271 if (i == fb_helper->crtc_count)
277 if (fb->depth == 8) {
278 fb_helper->funcs->gamma_set(crtc, red, green, blue, regno);
285 fb->pseudo_palette[regno] = ((red & 0xf800) >> 1) |
286 ((green & 0xf800) >> 6) |
287 ((blue & 0xf800) >> 11);
290 fb->pseudo_palette[regno] = (red & 0xf800) |
291 ((green & 0xfc00) >> 5) |
292 ((blue & 0xf800) >> 11);
296 fb->pseudo_palette[regno] =
297 (((red >> 8) & 0xff) << info->var.red.offset) |
298 (((green >> 8) & 0xff) << info->var.green.offset) |
299 (((blue >> 8) & 0xff) << info->var.blue.offset);
306 EXPORT_SYMBOL(drm_fb_helper_setcolreg);
308 int drm_fb_helper_check_var(struct fb_var_screeninfo *var,
309 struct fb_info *info)
311 struct drm_fb_helper *fb_helper = info->par;
312 struct drm_framebuffer *fb = fb_helper->fb;
315 if (var->pixclock == -1 || !var->pixclock)
318 /* Need to resize the fb object !!! */
319 if (var->xres > fb->width || var->yres > fb->height) {
320 DRM_ERROR("Requested width/height is greater than current fb "
321 "object %dx%d > %dx%d\n", var->xres, var->yres,
322 fb->width, fb->height);
323 DRM_ERROR("Need resizing code.\n");
327 switch (var->bits_per_pixel) {
329 depth = (var->green.length == 6) ? 16 : 15;
332 depth = (var->transp.length > 0) ? 32 : 24;
335 depth = var->bits_per_pixel;
342 var->green.offset = 0;
343 var->blue.offset = 0;
345 var->green.length = 8;
346 var->blue.length = 8;
347 var->transp.length = 0;
348 var->transp.offset = 0;
351 var->red.offset = 10;
352 var->green.offset = 5;
353 var->blue.offset = 0;
355 var->green.length = 5;
356 var->blue.length = 5;
357 var->transp.length = 1;
358 var->transp.offset = 15;
361 var->red.offset = 11;
362 var->green.offset = 5;
363 var->blue.offset = 0;
365 var->green.length = 6;
366 var->blue.length = 5;
367 var->transp.length = 0;
368 var->transp.offset = 0;
371 var->red.offset = 16;
372 var->green.offset = 8;
373 var->blue.offset = 0;
375 var->green.length = 8;
376 var->blue.length = 8;
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 = 8;
388 var->transp.offset = 24;
395 EXPORT_SYMBOL(drm_fb_helper_check_var);
397 /* this will let fbcon do the mode init */
398 int drm_fb_helper_set_par(struct fb_info *info)
400 struct drm_fb_helper *fb_helper = info->par;
401 struct drm_device *dev = fb_helper->dev;
402 struct fb_var_screeninfo *var = &info->var;
403 struct drm_crtc *crtc;
407 if (var->pixclock != -1) {
408 DRM_ERROR("PIXEL CLCOK SET\n");
412 list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
414 for (i = 0; i < fb_helper->crtc_count; i++) {
415 if (crtc->base.id == fb_helper->crtc_info[i].crtc_id)
418 if (i == fb_helper->crtc_count)
421 if (crtc->fb == fb_helper->crtc_info[i].mode_set.fb) {
422 mutex_lock(&dev->mode_config.mutex);
423 ret = crtc->funcs->set_config(&fb_helper->crtc_info->mode_set);
424 mutex_unlock(&dev->mode_config.mutex);
431 EXPORT_SYMBOL(drm_fb_helper_set_par);
433 int drm_fb_helper_pan_display(struct fb_var_screeninfo *var,
434 struct fb_info *info)
436 struct drm_fb_helper *fb_helper = info->par;
437 struct drm_device *dev = fb_helper->dev;
438 struct drm_mode_set *modeset;
439 struct drm_crtc *crtc;
443 list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
444 for (i = 0; i < fb_helper->crtc_count; i++) {
445 if (crtc->base.id == fb_helper->crtc_info[i].crtc_id)
449 if (i == fb_helper->crtc_count)
452 modeset = &fb_helper->crtc_info[i].mode_set;
454 modeset->x = var->xoffset;
455 modeset->y = var->yoffset;
457 if (modeset->num_connectors) {
458 mutex_lock(&dev->mode_config.mutex);
459 ret = crtc->funcs->set_config(modeset);
460 mutex_unlock(&dev->mode_config.mutex);
462 info->var.xoffset = var->xoffset;
463 info->var.yoffset = var->yoffset;
469 EXPORT_SYMBOL(drm_fb_helper_pan_display);
471 int drm_fb_helper_single_fb_probe(struct drm_device *dev,
472 int (*fb_create)(struct drm_device *dev,
475 uint32_t surface_width,
476 uint32_t surface_height,
477 struct drm_framebuffer **fb_ptr))
479 struct drm_crtc *crtc;
480 struct drm_connector *connector;
481 unsigned int fb_width = (unsigned)-1, fb_height = (unsigned)-1;
482 unsigned int surface_width = 0, surface_height = 0;
485 int ret, i, conn_count = 0;
486 struct fb_info *info;
487 struct drm_framebuffer *fb;
488 struct drm_mode_set *modeset = NULL;
489 struct drm_fb_helper *fb_helper;
491 /* first up get a count of crtcs now in use and new min/maxes width/heights */
492 list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
493 if (drm_helper_crtc_in_use(crtc)) {
494 if (crtc->desired_mode) {
495 if (crtc->desired_mode->hdisplay < fb_width)
496 fb_width = crtc->desired_mode->hdisplay;
498 if (crtc->desired_mode->vdisplay < fb_height)
499 fb_height = crtc->desired_mode->vdisplay;
501 if (crtc->desired_mode->hdisplay > surface_width)
502 surface_width = crtc->desired_mode->hdisplay;
504 if (crtc->desired_mode->vdisplay > surface_height)
505 surface_height = crtc->desired_mode->vdisplay;
511 if (crtc_count == 0 || fb_width == -1 || fb_height == -1) {
512 /* hmm everyone went away - assume VGA cable just fell out
513 and will come back later. */
517 /* do we have an fb already? */
518 if (list_empty(&dev->mode_config.fb_kernel_list)) {
519 ret = (*fb_create)(dev, fb_width, fb_height, surface_width,
520 surface_height, &fb);
525 fb = list_first_entry(&dev->mode_config.fb_kernel_list,
526 struct drm_framebuffer, filp_head);
528 /* if someone hotplugs something bigger than we have already allocated, we are pwned.
529 As really we can't resize an fbdev that is in the wild currently due to fbdev
530 not really being designed for the lower layers moving stuff around under it.
531 - so in the grand style of things - punt. */
532 if ((fb->width < surface_width) ||
533 (fb->height < surface_height)) {
534 DRM_ERROR("Framebuffer not large enough to scale console onto.\n");
540 fb_helper = info->par;
543 /* okay we need to setup new connector sets in the crtcs */
544 list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
545 modeset = &fb_helper->crtc_info[crtc_count].mode_set;
548 list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
549 if (connector->encoder)
550 if (connector->encoder->crtc == modeset->crtc) {
551 modeset->connectors[conn_count] = connector;
553 if (conn_count > fb_helper->conn_limit)
558 for (i = conn_count; i < fb_helper->conn_limit; i++)
559 modeset->connectors[i] = NULL;
561 modeset->crtc = crtc;
564 modeset->num_connectors = conn_count;
565 if (modeset->crtc->desired_mode) {
567 drm_mode_destroy(dev, modeset->mode);
568 modeset->mode = drm_mode_duplicate(dev,
569 modeset->crtc->desired_mode);
572 fb_helper->crtc_count = crtc_count;
576 info->var.pixclock = -1;
577 if (register_framebuffer(info) < 0)
580 drm_fb_helper_set_par(info);
582 printk(KERN_INFO "fb%d: %s frame buffer device\n", info->node,
585 /* Switch back to kernel console on panic */
586 /* multi card linked list maybe */
587 if (list_empty(&kernel_fb_helper_list)) {
588 printk(KERN_INFO "registered panic notifier\n");
589 atomic_notifier_chain_register(&panic_notifier_list,
591 register_sysrq_key('v', &sysrq_drm_fb_helper_restore_op);
593 list_add(&fb_helper->kernel_fb_list, &kernel_fb_helper_list);
596 EXPORT_SYMBOL(drm_fb_helper_single_fb_probe);
598 void drm_fb_helper_free(struct drm_fb_helper *helper)
600 list_del(&helper->kernel_fb_list);
601 if (list_empty(&kernel_fb_helper_list)) {
602 printk(KERN_INFO "unregistered panic notifier\n");
603 atomic_notifier_chain_unregister(&panic_notifier_list,
605 unregister_sysrq_key('v', &sysrq_drm_fb_helper_restore_op);
607 drm_fb_helper_crtc_free(helper);
609 EXPORT_SYMBOL(drm_fb_helper_free);
611 void drm_fb_helper_fill_fix(struct fb_info *info, uint32_t pitch)
613 info->fix.type = FB_TYPE_PACKED_PIXELS;
614 info->fix.visual = FB_VISUAL_TRUECOLOR;
615 info->fix.type_aux = 0;
616 info->fix.xpanstep = 1; /* doing it in hw */
617 info->fix.ypanstep = 1; /* doing it in hw */
618 info->fix.ywrapstep = 0;
619 info->fix.accel = FB_ACCEL_NONE;
620 info->fix.type_aux = 0;
622 info->fix.line_length = pitch;
625 EXPORT_SYMBOL(drm_fb_helper_fill_fix);
627 void drm_fb_helper_fill_var(struct fb_info *info, struct drm_framebuffer *fb,
628 uint32_t fb_width, uint32_t fb_height)
630 info->pseudo_palette = fb->pseudo_palette;
631 info->var.xres_virtual = fb->width;
632 info->var.yres_virtual = fb->height;
633 info->var.bits_per_pixel = fb->bits_per_pixel;
634 info->var.xoffset = 0;
635 info->var.yoffset = 0;
636 info->var.activate = FB_ACTIVATE_NOW;
637 info->var.height = -1;
638 info->var.width = -1;
642 info->var.red.offset = 0;
643 info->var.green.offset = 0;
644 info->var.blue.offset = 0;
645 info->var.red.length = 8; /* 8bit DAC */
646 info->var.green.length = 8;
647 info->var.blue.length = 8;
648 info->var.transp.offset = 0;
649 info->var.transp.length = 0;
652 info->var.red.offset = 10;
653 info->var.green.offset = 5;
654 info->var.blue.offset = 0;
655 info->var.red.length = 5;
656 info->var.green.length = 5;
657 info->var.blue.length = 5;
658 info->var.transp.offset = 15;
659 info->var.transp.length = 1;
662 info->var.red.offset = 11;
663 info->var.green.offset = 5;
664 info->var.blue.offset = 0;
665 info->var.red.length = 5;
666 info->var.green.length = 6;
667 info->var.blue.length = 5;
668 info->var.transp.offset = 0;
671 info->var.red.offset = 16;
672 info->var.green.offset = 8;
673 info->var.blue.offset = 0;
674 info->var.red.length = 8;
675 info->var.green.length = 8;
676 info->var.blue.length = 8;
677 info->var.transp.offset = 0;
678 info->var.transp.length = 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 = 24;
688 info->var.transp.length = 8;
694 info->var.xres = fb_width;
695 info->var.yres = fb_height;
697 EXPORT_SYMBOL(drm_fb_helper_fill_var);