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