drm/kms: make fb helper work for all drivers.
[deliverable/linux.git] / drivers / gpu / drm / drm_fb_helper.c
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>
5 *
6 * DRM framebuffer helper functions
7 *
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.
17 *
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.
25 *
26 * Authors:
27 * Dave Airlie <airlied@linux.ie>
28 * Jesse Barnes <jesse.barnes@intel.com>
29 */
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"
36
37 MODULE_AUTHOR("David Airlie, Jesse Barnes");
38 MODULE_DESCRIPTION("DRM KMS helper");
39 MODULE_LICENSE("GPL and additional rights");
40
41 static LIST_HEAD(kernel_fb_helper_list);
42
43 int drm_fb_helper_add_connector(struct drm_connector *connector)
44 {
45 connector->fb_helper_private = kzalloc(sizeof(struct drm_fb_helper_connector), GFP_KERNEL);
46 if (!connector->fb_helper_private)
47 return -ENOMEM;
48
49 return 0;
50 }
51 EXPORT_SYMBOL(drm_fb_helper_add_connector);
52
53 static int my_atoi(const char *name)
54 {
55 int val = 0;
56
57 for (;; name++) {
58 switch (*name) {
59 case '0' ... '9':
60 val = 10*val+(*name-'0');
61 break;
62 default:
63 return val;
64 }
65 }
66 }
67
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
72 *
73 * This parses the connector specific then generic command lines for
74 * modes and options to configure the connector.
75 *
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]
78 *
79 * enable/enable Digital/disable bit at the end
80 */
81 static bool drm_fb_helper_connector_parse_command_line(struct drm_connector *connector,
82 const char *mode_option)
83 {
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;
93
94 if (!fb_help_conn)
95 return false;
96
97 cmdline_mode = &fb_help_conn->cmdline_mode;
98 if (!mode_option)
99 mode_option = fb_mode_option;
100
101 if (!mode_option) {
102 cmdline_mode->specified = false;
103 return false;
104 }
105
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;
170 }
171 }
172 if (i < 0 && yres_specified) {
173 xres = my_atoi(name);
174 res_specified = 1;
175 }
176 done:
177
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" : "");
183
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;
191 }
192
193 DRM_INFO("forcing %s connector %s\n",
194 drm_get_connector_name(connector), s);
195 connector->force = force;
196 }
197
198 if (res_specified) {
199 cmdline_mode->specified = true;
200 cmdline_mode->xres = xres;
201 cmdline_mode->yres = yres;
202 }
203
204 if (refresh_specified) {
205 cmdline_mode->refresh_specified = true;
206 cmdline_mode->refresh = refresh;
207 }
208
209 if (bpp_specified) {
210 cmdline_mode->bpp_specified = true;
211 cmdline_mode->bpp = bpp;
212 }
213 cmdline_mode->rb = rb ? true : false;
214 cmdline_mode->cvt = cvt ? true : false;
215 cmdline_mode->interlace = interlace ? true : false;
216
217 return true;
218 }
219
220 int drm_fb_helper_parse_command_line(struct drm_device *dev)
221 {
222 struct drm_connector *connector;
223
224 list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
225 char *option = NULL;
226
227 /* do something on return - turn off connector maybe */
228 if (fb_get_options(drm_get_connector_name(connector), &option))
229 continue;
230
231 drm_fb_helper_connector_parse_command_line(connector, option);
232 }
233 return 0;
234 }
235
236 bool drm_fb_helper_force_kernel_mode(void)
237 {
238 int i = 0;
239 bool ret, error = false;
240 struct drm_fb_helper *helper;
241
242 if (list_empty(&kernel_fb_helper_list))
243 return false;
244
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;
251 }
252 }
253 return error;
254 }
255
256 int drm_fb_helper_panic(struct notifier_block *n, unsigned long ununsed,
257 void *panic_str)
258 {
259 DRM_ERROR("panic occurred, switching back to text console\n");
260 return drm_fb_helper_force_kernel_mode();
261 return 0;
262 }
263 EXPORT_SYMBOL(drm_fb_helper_panic);
264
265 static struct notifier_block paniced = {
266 .notifier_call = drm_fb_helper_panic,
267 };
268
269 /**
270 * drm_fb_helper_restore - restore the framebuffer console (kernel) config
271 *
272 * Restore's the kernel's fbcon mode, used for lastclose & panic paths.
273 */
274 void drm_fb_helper_restore(void)
275 {
276 bool ret;
277 ret = drm_fb_helper_force_kernel_mode();
278 if (ret == true)
279 DRM_ERROR("Failed to restore crtc configuration\n");
280 }
281 EXPORT_SYMBOL(drm_fb_helper_restore);
282
283 static void drm_fb_helper_restore_work_fn(struct work_struct *ignored)
284 {
285 drm_fb_helper_restore();
286 }
287 static DECLARE_WORK(drm_fb_helper_restore_work, drm_fb_helper_restore_work_fn);
288
289 static void drm_fb_helper_sysrq(int dummy1, struct tty_struct *dummy3)
290 {
291 schedule_work(&drm_fb_helper_restore_work);
292 }
293
294 static struct sysrq_key_op sysrq_drm_fb_helper_restore_op = {
295 .handler = drm_fb_helper_sysrq,
296 .help_msg = "force-fb(V)",
297 .action_msg = "Restore framebuffer console",
298 };
299
300 static void drm_fb_helper_on(struct fb_info *info)
301 {
302 struct drm_fb_helper *fb_helper = info->par;
303 struct drm_device *dev = fb_helper->dev;
304 struct drm_crtc *crtc;
305 struct drm_encoder *encoder;
306 int i;
307
308 /*
309 * For each CRTC in this fb, turn the crtc on then,
310 * find all associated encoders and turn them on.
311 */
312 for (i = 0; i < fb_helper->crtc_count; i++) {
313 list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
314 struct drm_crtc_helper_funcs *crtc_funcs =
315 crtc->helper_private;
316
317 /* Only mess with CRTCs in this fb */
318 if (crtc->base.id != fb_helper->crtc_info[i].crtc_id ||
319 !crtc->enabled)
320 continue;
321
322 mutex_lock(&dev->mode_config.mutex);
323 crtc_funcs->dpms(crtc, DRM_MODE_DPMS_ON);
324 mutex_unlock(&dev->mode_config.mutex);
325
326 /* Found a CRTC on this fb, now find encoders */
327 list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
328 if (encoder->crtc == crtc) {
329 struct drm_encoder_helper_funcs *encoder_funcs;
330
331 encoder_funcs = encoder->helper_private;
332 mutex_lock(&dev->mode_config.mutex);
333 encoder_funcs->dpms(encoder, DRM_MODE_DPMS_ON);
334 mutex_unlock(&dev->mode_config.mutex);
335 }
336 }
337 }
338 }
339 }
340
341 static void drm_fb_helper_off(struct fb_info *info, int dpms_mode)
342 {
343 struct drm_fb_helper *fb_helper = info->par;
344 struct drm_device *dev = fb_helper->dev;
345 struct drm_crtc *crtc;
346 struct drm_encoder *encoder;
347 int i;
348
349 /*
350 * For each CRTC in this fb, find all associated encoders
351 * and turn them off, then turn off the CRTC.
352 */
353 for (i = 0; i < fb_helper->crtc_count; i++) {
354 list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
355 struct drm_crtc_helper_funcs *crtc_funcs =
356 crtc->helper_private;
357
358 /* Only mess with CRTCs in this fb */
359 if (crtc->base.id != fb_helper->crtc_info[i].crtc_id ||
360 !crtc->enabled)
361 continue;
362
363 /* Found a CRTC on this fb, now find encoders */
364 list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
365 if (encoder->crtc == crtc) {
366 struct drm_encoder_helper_funcs *encoder_funcs;
367
368 encoder_funcs = encoder->helper_private;
369 mutex_lock(&dev->mode_config.mutex);
370 encoder_funcs->dpms(encoder, dpms_mode);
371 mutex_unlock(&dev->mode_config.mutex);
372 }
373 }
374 if (dpms_mode == DRM_MODE_DPMS_OFF) {
375 mutex_lock(&dev->mode_config.mutex);
376 crtc_funcs->dpms(crtc, dpms_mode);
377 mutex_unlock(&dev->mode_config.mutex);
378 }
379 }
380 }
381 }
382
383 int drm_fb_helper_blank(int blank, struct fb_info *info)
384 {
385 switch (blank) {
386 case FB_BLANK_UNBLANK:
387 drm_fb_helper_on(info);
388 break;
389 case FB_BLANK_NORMAL:
390 drm_fb_helper_off(info, DRM_MODE_DPMS_STANDBY);
391 break;
392 case FB_BLANK_HSYNC_SUSPEND:
393 drm_fb_helper_off(info, DRM_MODE_DPMS_STANDBY);
394 break;
395 case FB_BLANK_VSYNC_SUSPEND:
396 drm_fb_helper_off(info, DRM_MODE_DPMS_SUSPEND);
397 break;
398 case FB_BLANK_POWERDOWN:
399 drm_fb_helper_off(info, DRM_MODE_DPMS_OFF);
400 break;
401 }
402 return 0;
403 }
404 EXPORT_SYMBOL(drm_fb_helper_blank);
405
406 static void drm_fb_helper_crtc_free(struct drm_fb_helper *helper)
407 {
408 int i;
409
410 for (i = 0; i < helper->crtc_count; i++)
411 kfree(helper->crtc_info[i].mode_set.connectors);
412 kfree(helper->crtc_info);
413 }
414
415 int drm_fb_helper_init_crtc_count(struct drm_fb_helper *helper, int crtc_count, int max_conn_count)
416 {
417 struct drm_device *dev = helper->dev;
418 struct drm_crtc *crtc;
419 int ret = 0;
420 int i;
421
422 helper->crtc_info = kcalloc(crtc_count, sizeof(struct drm_fb_helper_crtc), GFP_KERNEL);
423 if (!helper->crtc_info)
424 return -ENOMEM;
425
426 helper->crtc_count = crtc_count;
427
428 for (i = 0; i < crtc_count; i++) {
429 helper->crtc_info[i].mode_set.connectors =
430 kcalloc(max_conn_count,
431 sizeof(struct drm_connector *),
432 GFP_KERNEL);
433
434 if (!helper->crtc_info[i].mode_set.connectors) {
435 ret = -ENOMEM;
436 goto out_free;
437 }
438 helper->crtc_info[i].mode_set.num_connectors = 0;
439 }
440
441 i = 0;
442 list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
443 helper->crtc_info[i].crtc_id = crtc->base.id;
444 helper->crtc_info[i].mode_set.crtc = crtc;
445 i++;
446 }
447 helper->conn_limit = max_conn_count;
448 return 0;
449 out_free:
450 drm_fb_helper_crtc_free(helper);
451 return -ENOMEM;
452 }
453 EXPORT_SYMBOL(drm_fb_helper_init_crtc_count);
454
455 int drm_fb_helper_setcolreg(unsigned regno,
456 unsigned red,
457 unsigned green,
458 unsigned blue,
459 unsigned transp,
460 struct fb_info *info)
461 {
462 struct drm_fb_helper *fb_helper = info->par;
463 struct drm_device *dev = fb_helper->dev;
464 struct drm_crtc *crtc;
465 int i;
466
467 list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
468 struct drm_framebuffer *fb = fb_helper->fb;
469
470 for (i = 0; i < fb_helper->crtc_count; i++) {
471 if (crtc->base.id == fb_helper->crtc_info[i].crtc_id)
472 break;
473 }
474 if (i == fb_helper->crtc_count)
475 continue;
476
477 if (regno > 255)
478 return 1;
479
480 if (fb->depth == 8) {
481 fb_helper->funcs->gamma_set(crtc, red, green, blue, regno);
482 return 0;
483 }
484
485 if (regno < 16) {
486 switch (fb->depth) {
487 case 15:
488 fb->pseudo_palette[regno] = ((red & 0xf800) >> 1) |
489 ((green & 0xf800) >> 6) |
490 ((blue & 0xf800) >> 11);
491 break;
492 case 16:
493 fb->pseudo_palette[regno] = (red & 0xf800) |
494 ((green & 0xfc00) >> 5) |
495 ((blue & 0xf800) >> 11);
496 break;
497 case 24:
498 case 32:
499 fb->pseudo_palette[regno] =
500 (((red >> 8) & 0xff) << info->var.red.offset) |
501 (((green >> 8) & 0xff) << info->var.green.offset) |
502 (((blue >> 8) & 0xff) << info->var.blue.offset);
503 break;
504 }
505 }
506 }
507 return 0;
508 }
509 EXPORT_SYMBOL(drm_fb_helper_setcolreg);
510
511 int drm_fb_helper_check_var(struct fb_var_screeninfo *var,
512 struct fb_info *info)
513 {
514 struct drm_fb_helper *fb_helper = info->par;
515 struct drm_framebuffer *fb = fb_helper->fb;
516 int depth;
517
518 if (var->pixclock == -1 || !var->pixclock)
519 return -EINVAL;
520
521 /* Need to resize the fb object !!! */
522 if (var->xres > fb->width || var->yres > fb->height) {
523 DRM_ERROR("Requested width/height is greater than current fb "
524 "object %dx%d > %dx%d\n", var->xres, var->yres,
525 fb->width, fb->height);
526 DRM_ERROR("Need resizing code.\n");
527 return -EINVAL;
528 }
529
530 switch (var->bits_per_pixel) {
531 case 16:
532 depth = (var->green.length == 6) ? 16 : 15;
533 break;
534 case 32:
535 depth = (var->transp.length > 0) ? 32 : 24;
536 break;
537 default:
538 depth = var->bits_per_pixel;
539 break;
540 }
541
542 switch (depth) {
543 case 8:
544 var->red.offset = 0;
545 var->green.offset = 0;
546 var->blue.offset = 0;
547 var->red.length = 8;
548 var->green.length = 8;
549 var->blue.length = 8;
550 var->transp.length = 0;
551 var->transp.offset = 0;
552 break;
553 case 15:
554 var->red.offset = 10;
555 var->green.offset = 5;
556 var->blue.offset = 0;
557 var->red.length = 5;
558 var->green.length = 5;
559 var->blue.length = 5;
560 var->transp.length = 1;
561 var->transp.offset = 15;
562 break;
563 case 16:
564 var->red.offset = 11;
565 var->green.offset = 5;
566 var->blue.offset = 0;
567 var->red.length = 5;
568 var->green.length = 6;
569 var->blue.length = 5;
570 var->transp.length = 0;
571 var->transp.offset = 0;
572 break;
573 case 24:
574 var->red.offset = 16;
575 var->green.offset = 8;
576 var->blue.offset = 0;
577 var->red.length = 8;
578 var->green.length = 8;
579 var->blue.length = 8;
580 var->transp.length = 0;
581 var->transp.offset = 0;
582 break;
583 case 32:
584 var->red.offset = 16;
585 var->green.offset = 8;
586 var->blue.offset = 0;
587 var->red.length = 8;
588 var->green.length = 8;
589 var->blue.length = 8;
590 var->transp.length = 8;
591 var->transp.offset = 24;
592 break;
593 default:
594 return -EINVAL;
595 }
596 return 0;
597 }
598 EXPORT_SYMBOL(drm_fb_helper_check_var);
599
600 /* this will let fbcon do the mode init */
601 int drm_fb_helper_set_par(struct fb_info *info)
602 {
603 struct drm_fb_helper *fb_helper = info->par;
604 struct drm_device *dev = fb_helper->dev;
605 struct fb_var_screeninfo *var = &info->var;
606 struct drm_crtc *crtc;
607 int ret;
608 int i;
609
610 if (var->pixclock != -1) {
611 DRM_ERROR("PIXEL CLCOK SET\n");
612 return -EINVAL;
613 }
614
615 list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
616
617 for (i = 0; i < fb_helper->crtc_count; i++) {
618 if (crtc->base.id == fb_helper->crtc_info[i].crtc_id)
619 break;
620 }
621 if (i == fb_helper->crtc_count)
622 continue;
623
624 if (crtc->fb == fb_helper->crtc_info[i].mode_set.fb) {
625 mutex_lock(&dev->mode_config.mutex);
626 ret = crtc->funcs->set_config(&fb_helper->crtc_info->mode_set);
627 mutex_unlock(&dev->mode_config.mutex);
628 if (ret)
629 return ret;
630 }
631 }
632 return 0;
633 }
634 EXPORT_SYMBOL(drm_fb_helper_set_par);
635
636 int drm_fb_helper_pan_display(struct fb_var_screeninfo *var,
637 struct fb_info *info)
638 {
639 struct drm_fb_helper *fb_helper = info->par;
640 struct drm_device *dev = fb_helper->dev;
641 struct drm_mode_set *modeset;
642 struct drm_crtc *crtc;
643 int ret = 0;
644 int i;
645
646 list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
647 for (i = 0; i < fb_helper->crtc_count; i++) {
648 if (crtc->base.id == fb_helper->crtc_info[i].crtc_id)
649 break;
650 }
651
652 if (i == fb_helper->crtc_count)
653 continue;
654
655 modeset = &fb_helper->crtc_info[i].mode_set;
656
657 modeset->x = var->xoffset;
658 modeset->y = var->yoffset;
659
660 if (modeset->num_connectors) {
661 mutex_lock(&dev->mode_config.mutex);
662 ret = crtc->funcs->set_config(modeset);
663 mutex_unlock(&dev->mode_config.mutex);
664 if (!ret) {
665 info->var.xoffset = var->xoffset;
666 info->var.yoffset = var->yoffset;
667 }
668 }
669 }
670 return ret;
671 }
672 EXPORT_SYMBOL(drm_fb_helper_pan_display);
673
674 int drm_fb_helper_single_fb_probe(struct drm_device *dev,
675 int (*fb_create)(struct drm_device *dev,
676 uint32_t fb_width,
677 uint32_t fb_height,
678 uint32_t surface_width,
679 uint32_t surface_height,
680 uint32_t surface_depth,
681 uint32_t surface_bpp,
682 struct drm_framebuffer **fb_ptr))
683 {
684 struct drm_crtc *crtc;
685 struct drm_connector *connector;
686 unsigned int fb_width = (unsigned)-1, fb_height = (unsigned)-1;
687 unsigned int surface_width = 0, surface_height = 0;
688 int new_fb = 0;
689 int crtc_count = 0;
690 int ret, i, conn_count = 0;
691 struct fb_info *info;
692 struct drm_framebuffer *fb;
693 struct drm_mode_set *modeset = NULL;
694 struct drm_fb_helper *fb_helper;
695 uint32_t surface_depth = 24, surface_bpp = 32;
696
697 /* first up get a count of crtcs now in use and new min/maxes width/heights */
698 list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
699 struct drm_fb_helper_connector *fb_help_conn = connector->fb_helper_private;
700
701 struct drm_fb_helper_cmdline_mode *cmdline_mode;
702
703 if (!fb_help_conn)
704 continue;
705
706 cmdline_mode = &fb_help_conn->cmdline_mode;
707
708 if (cmdline_mode->bpp_specified) {
709 switch (cmdline_mode->bpp) {
710 case 8:
711 surface_depth = surface_bpp = 8;
712 break;
713 case 15:
714 surface_depth = 15;
715 surface_bpp = 16;
716 break;
717 case 16:
718 surface_depth = surface_bpp = 16;
719 break;
720 case 24:
721 surface_depth = surface_bpp = 24;
722 break;
723 case 32:
724 surface_depth = 24;
725 surface_bpp = 32;
726 break;
727 }
728 break;
729 }
730 }
731
732 list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
733 if (drm_helper_crtc_in_use(crtc)) {
734 if (crtc->desired_mode) {
735 if (crtc->desired_mode->hdisplay < fb_width)
736 fb_width = crtc->desired_mode->hdisplay;
737
738 if (crtc->desired_mode->vdisplay < fb_height)
739 fb_height = crtc->desired_mode->vdisplay;
740
741 if (crtc->desired_mode->hdisplay > surface_width)
742 surface_width = crtc->desired_mode->hdisplay;
743
744 if (crtc->desired_mode->vdisplay > surface_height)
745 surface_height = crtc->desired_mode->vdisplay;
746 }
747 crtc_count++;
748 }
749 }
750
751 if (crtc_count == 0 || fb_width == -1 || fb_height == -1) {
752 /* hmm everyone went away - assume VGA cable just fell out
753 and will come back later. */
754 return 0;
755 }
756
757 /* do we have an fb already? */
758 if (list_empty(&dev->mode_config.fb_kernel_list)) {
759 ret = (*fb_create)(dev, fb_width, fb_height, surface_width,
760 surface_height, surface_depth, surface_bpp,
761 &fb);
762 if (ret)
763 return -EINVAL;
764 new_fb = 1;
765 } else {
766 fb = list_first_entry(&dev->mode_config.fb_kernel_list,
767 struct drm_framebuffer, filp_head);
768
769 /* if someone hotplugs something bigger than we have already allocated, we are pwned.
770 As really we can't resize an fbdev that is in the wild currently due to fbdev
771 not really being designed for the lower layers moving stuff around under it.
772 - so in the grand style of things - punt. */
773 if ((fb->width < surface_width) ||
774 (fb->height < surface_height)) {
775 DRM_ERROR("Framebuffer not large enough to scale console onto.\n");
776 return -EINVAL;
777 }
778 }
779
780 info = fb->fbdev;
781 fb_helper = info->par;
782
783 crtc_count = 0;
784 /* okay we need to setup new connector sets in the crtcs */
785 list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
786 modeset = &fb_helper->crtc_info[crtc_count].mode_set;
787 modeset->fb = fb;
788 conn_count = 0;
789 list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
790 if (connector->encoder)
791 if (connector->encoder->crtc == modeset->crtc) {
792 modeset->connectors[conn_count] = connector;
793 conn_count++;
794 if (conn_count > fb_helper->conn_limit)
795 BUG();
796 }
797 }
798
799 for (i = conn_count; i < fb_helper->conn_limit; i++)
800 modeset->connectors[i] = NULL;
801
802 modeset->crtc = crtc;
803 crtc_count++;
804
805 modeset->num_connectors = conn_count;
806 if (modeset->crtc->desired_mode) {
807 if (modeset->mode)
808 drm_mode_destroy(dev, modeset->mode);
809 modeset->mode = drm_mode_duplicate(dev,
810 modeset->crtc->desired_mode);
811 }
812 }
813 fb_helper->crtc_count = crtc_count;
814 fb_helper->fb = fb;
815
816 if (new_fb) {
817 info->var.pixclock = -1;
818 if (register_framebuffer(info) < 0)
819 return -EINVAL;
820 } else {
821 drm_fb_helper_set_par(info);
822 }
823 printk(KERN_INFO "fb%d: %s frame buffer device\n", info->node,
824 info->fix.id);
825
826 /* Switch back to kernel console on panic */
827 /* multi card linked list maybe */
828 if (list_empty(&kernel_fb_helper_list)) {
829 printk(KERN_INFO "registered panic notifier\n");
830 atomic_notifier_chain_register(&panic_notifier_list,
831 &paniced);
832 register_sysrq_key('v', &sysrq_drm_fb_helper_restore_op);
833 }
834 list_add(&fb_helper->kernel_fb_list, &kernel_fb_helper_list);
835 return 0;
836 }
837 EXPORT_SYMBOL(drm_fb_helper_single_fb_probe);
838
839 void drm_fb_helper_free(struct drm_fb_helper *helper)
840 {
841 list_del(&helper->kernel_fb_list);
842 if (list_empty(&kernel_fb_helper_list)) {
843 printk(KERN_INFO "unregistered panic notifier\n");
844 atomic_notifier_chain_unregister(&panic_notifier_list,
845 &paniced);
846 unregister_sysrq_key('v', &sysrq_drm_fb_helper_restore_op);
847 }
848 drm_fb_helper_crtc_free(helper);
849 }
850 EXPORT_SYMBOL(drm_fb_helper_free);
851
852 void drm_fb_helper_fill_fix(struct fb_info *info, uint32_t pitch)
853 {
854 info->fix.type = FB_TYPE_PACKED_PIXELS;
855 info->fix.visual = FB_VISUAL_TRUECOLOR;
856 info->fix.type_aux = 0;
857 info->fix.xpanstep = 1; /* doing it in hw */
858 info->fix.ypanstep = 1; /* doing it in hw */
859 info->fix.ywrapstep = 0;
860 info->fix.accel = FB_ACCEL_NONE;
861 info->fix.type_aux = 0;
862
863 info->fix.line_length = pitch;
864 return;
865 }
866 EXPORT_SYMBOL(drm_fb_helper_fill_fix);
867
868 void drm_fb_helper_fill_var(struct fb_info *info, struct drm_framebuffer *fb,
869 uint32_t fb_width, uint32_t fb_height)
870 {
871 info->pseudo_palette = fb->pseudo_palette;
872 info->var.xres_virtual = fb->width;
873 info->var.yres_virtual = fb->height;
874 info->var.bits_per_pixel = fb->bits_per_pixel;
875 info->var.xoffset = 0;
876 info->var.yoffset = 0;
877 info->var.activate = FB_ACTIVATE_NOW;
878 info->var.height = -1;
879 info->var.width = -1;
880
881 switch (fb->depth) {
882 case 8:
883 info->var.red.offset = 0;
884 info->var.green.offset = 0;
885 info->var.blue.offset = 0;
886 info->var.red.length = 8; /* 8bit DAC */
887 info->var.green.length = 8;
888 info->var.blue.length = 8;
889 info->var.transp.offset = 0;
890 info->var.transp.length = 0;
891 break;
892 case 15:
893 info->var.red.offset = 10;
894 info->var.green.offset = 5;
895 info->var.blue.offset = 0;
896 info->var.red.length = 5;
897 info->var.green.length = 5;
898 info->var.blue.length = 5;
899 info->var.transp.offset = 15;
900 info->var.transp.length = 1;
901 break;
902 case 16:
903 info->var.red.offset = 11;
904 info->var.green.offset = 5;
905 info->var.blue.offset = 0;
906 info->var.red.length = 5;
907 info->var.green.length = 6;
908 info->var.blue.length = 5;
909 info->var.transp.offset = 0;
910 break;
911 case 24:
912 info->var.red.offset = 16;
913 info->var.green.offset = 8;
914 info->var.blue.offset = 0;
915 info->var.red.length = 8;
916 info->var.green.length = 8;
917 info->var.blue.length = 8;
918 info->var.transp.offset = 0;
919 info->var.transp.length = 0;
920 break;
921 case 32:
922 info->var.red.offset = 16;
923 info->var.green.offset = 8;
924 info->var.blue.offset = 0;
925 info->var.red.length = 8;
926 info->var.green.length = 8;
927 info->var.blue.length = 8;
928 info->var.transp.offset = 24;
929 info->var.transp.length = 8;
930 break;
931 default:
932 break;
933 }
934
935 info->var.xres = fb_width;
936 info->var.yres = fb_height;
937 }
938 EXPORT_SYMBOL(drm_fb_helper_fill_var);
This page took 0.068278 seconds and 5 git commands to generate.