1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
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
37MODULE_AUTHOR("David Airlie, Jesse Barnes");
38MODULE_DESCRIPTION("DRM KMS helper");
39MODULE_LICENSE("GPL and additional rights");
40
41static LIST_HEAD(kernel_fb_helper_list);
42
43int 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}
51EXPORT_SYMBOL(drm_fb_helper_add_connector);
52
53static 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
70
71
72
73
74
75
76
77
78
79
80
81static 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 }
176done:
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
220int 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
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
236bool 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
256int 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}
263EXPORT_SYMBOL(drm_fb_helper_panic);
264
265static struct notifier_block paniced = {
266 .notifier_call = drm_fb_helper_panic,
267};
268
269
270
271
272
273
274void 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}
281EXPORT_SYMBOL(drm_fb_helper_restore);
282
283#ifdef CONFIG_MAGIC_SYSRQ
284static void drm_fb_helper_restore_work_fn(struct work_struct *ignored)
285{
286 drm_fb_helper_restore();
287}
288static DECLARE_WORK(drm_fb_helper_restore_work, drm_fb_helper_restore_work_fn);
289
290static void drm_fb_helper_sysrq(int dummy1, struct tty_struct *dummy3)
291{
292 schedule_work(&drm_fb_helper_restore_work);
293}
294
295static 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#endif
301
302static void drm_fb_helper_on(struct fb_info *info)
303{
304 struct drm_fb_helper *fb_helper = info->par;
305 struct drm_device *dev = fb_helper->dev;
306 struct drm_crtc *crtc;
307 struct drm_encoder *encoder;
308 int i;
309
310
311
312
313
314 for (i = 0; i < fb_helper->crtc_count; i++) {
315 list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
316 struct drm_crtc_helper_funcs *crtc_funcs =
317 crtc->helper_private;
318
319
320 if (crtc->base.id != fb_helper->crtc_info[i].crtc_id ||
321 !crtc->enabled)
322 continue;
323
324 mutex_lock(&dev->mode_config.mutex);
325 crtc_funcs->dpms(crtc, DRM_MODE_DPMS_ON);
326 mutex_unlock(&dev->mode_config.mutex);
327
328
329 list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
330 if (encoder->crtc == crtc) {
331 struct drm_encoder_helper_funcs *encoder_funcs;
332
333 encoder_funcs = encoder->helper_private;
334 mutex_lock(&dev->mode_config.mutex);
335 encoder_funcs->dpms(encoder, DRM_MODE_DPMS_ON);
336 mutex_unlock(&dev->mode_config.mutex);
337 }
338 }
339 }
340 }
341}
342
343static void drm_fb_helper_off(struct fb_info *info, int dpms_mode)
344{
345 struct drm_fb_helper *fb_helper = info->par;
346 struct drm_device *dev = fb_helper->dev;
347 struct drm_crtc *crtc;
348 struct drm_encoder *encoder;
349 int i;
350
351
352
353
354
355 for (i = 0; i < fb_helper->crtc_count; i++) {
356 list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
357 struct drm_crtc_helper_funcs *crtc_funcs =
358 crtc->helper_private;
359
360
361 if (crtc->base.id != fb_helper->crtc_info[i].crtc_id ||
362 !crtc->enabled)
363 continue;
364
365
366 list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
367 if (encoder->crtc == crtc) {
368 struct drm_encoder_helper_funcs *encoder_funcs;
369
370 encoder_funcs = encoder->helper_private;
371 mutex_lock(&dev->mode_config.mutex);
372 encoder_funcs->dpms(encoder, dpms_mode);
373 mutex_unlock(&dev->mode_config.mutex);
374 }
375 }
376 if (dpms_mode == DRM_MODE_DPMS_OFF) {
377 mutex_lock(&dev->mode_config.mutex);
378 crtc_funcs->dpms(crtc, dpms_mode);
379 mutex_unlock(&dev->mode_config.mutex);
380 }
381 }
382 }
383}
384
385int drm_fb_helper_blank(int blank, struct fb_info *info)
386{
387 switch (blank) {
388 case FB_BLANK_UNBLANK:
389 drm_fb_helper_on(info);
390 break;
391 case FB_BLANK_NORMAL:
392 drm_fb_helper_off(info, DRM_MODE_DPMS_STANDBY);
393 break;
394 case FB_BLANK_HSYNC_SUSPEND:
395 drm_fb_helper_off(info, DRM_MODE_DPMS_STANDBY);
396 break;
397 case FB_BLANK_VSYNC_SUSPEND:
398 drm_fb_helper_off(info, DRM_MODE_DPMS_SUSPEND);
399 break;
400 case FB_BLANK_POWERDOWN:
401 drm_fb_helper_off(info, DRM_MODE_DPMS_OFF);
402 break;
403 }
404 return 0;
405}
406EXPORT_SYMBOL(drm_fb_helper_blank);
407
408static void drm_fb_helper_crtc_free(struct drm_fb_helper *helper)
409{
410 int i;
411
412 for (i = 0; i < helper->crtc_count; i++)
413 kfree(helper->crtc_info[i].mode_set.connectors);
414 kfree(helper->crtc_info);
415}
416
417int drm_fb_helper_init_crtc_count(struct drm_fb_helper *helper, int crtc_count, int max_conn_count)
418{
419 struct drm_device *dev = helper->dev;
420 struct drm_crtc *crtc;
421 int ret = 0;
422 int i;
423
424 helper->crtc_info = kcalloc(crtc_count, sizeof(struct drm_fb_helper_crtc), GFP_KERNEL);
425 if (!helper->crtc_info)
426 return -ENOMEM;
427
428 helper->crtc_count = crtc_count;
429
430 for (i = 0; i < crtc_count; i++) {
431 helper->crtc_info[i].mode_set.connectors =
432 kcalloc(max_conn_count,
433 sizeof(struct drm_connector *),
434 GFP_KERNEL);
435
436 if (!helper->crtc_info[i].mode_set.connectors) {
437 ret = -ENOMEM;
438 goto out_free;
439 }
440 helper->crtc_info[i].mode_set.num_connectors = 0;
441 }
442
443 i = 0;
444 list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
445 helper->crtc_info[i].crtc_id = crtc->base.id;
446 helper->crtc_info[i].mode_set.crtc = crtc;
447 i++;
448 }
449 helper->conn_limit = max_conn_count;
450 return 0;
451out_free:
452 drm_fb_helper_crtc_free(helper);
453 return -ENOMEM;
454}
455EXPORT_SYMBOL(drm_fb_helper_init_crtc_count);
456
457static int setcolreg(struct drm_crtc *crtc, u16 red, u16 green,
458 u16 blue, u16 regno, struct fb_info *info)
459{
460 struct drm_fb_helper *fb_helper = info->par;
461 struct drm_framebuffer *fb = fb_helper->fb;
462 int pindex;
463
464 if (info->fix.visual == FB_VISUAL_TRUECOLOR) {
465 u32 *palette;
466 u32 value;
467
468 if (regno > 16)
469 return -EINVAL;
470 palette = (u32 *)info->pseudo_palette;
471 red >>= (16 - info->var.red.length);
472 green >>= (16 - info->var.green.length);
473 blue >>= (16 - info->var.blue.length);
474 value = (red << info->var.red.offset) |
475 (green << info->var.green.offset) |
476 (blue << info->var.blue.offset);
477 palette[regno] = value;
478 return 0;
479 }
480
481 pindex = regno;
482
483 if (fb->bits_per_pixel == 16) {
484 pindex = regno << 3;
485
486 if (fb->depth == 16 && regno > 63)
487 return -EINVAL;
488 if (fb->depth == 15 && regno > 31)
489 return -EINVAL;
490
491 if (fb->depth == 16) {
492 u16 r, g, b;
493 int i;
494 if (regno < 32) {
495 for (i = 0; i < 8; i++)
496 fb_helper->funcs->gamma_set(crtc, red,
497 green, blue, pindex + i);
498 }
499
500 fb_helper->funcs->gamma_get(crtc, &r,
501 &g, &b,
502 pindex >> 1);
503
504 for (i = 0; i < 4; i++)
505 fb_helper->funcs->gamma_set(crtc, r,
506 green, b,
507 (pindex >> 1) + i);
508 }
509 }
510
511 if (fb->depth != 16)
512 fb_helper->funcs->gamma_set(crtc, red, green, blue, pindex);
513 return 0;
514}
515
516int drm_fb_helper_setcmap(struct fb_cmap *cmap, struct fb_info *info)
517{
518 struct drm_fb_helper *fb_helper = info->par;
519 struct drm_device *dev = fb_helper->dev;
520 u16 *red, *green, *blue, *transp;
521 struct drm_crtc *crtc;
522 int i, rc = 0;
523 int start;
524
525 list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
526 struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private;
527 for (i = 0; i < fb_helper->crtc_count; i++) {
528 if (crtc->base.id == fb_helper->crtc_info[i].crtc_id)
529 break;
530 }
531 if (i == fb_helper->crtc_count)
532 continue;
533
534 red = cmap->red;
535 green = cmap->green;
536 blue = cmap->blue;
537 transp = cmap->transp;
538 start = cmap->start;
539
540 for (i = 0; i < cmap->len; i++) {
541 u16 hred, hgreen, hblue, htransp = 0xffff;
542
543 hred = *red++;
544 hgreen = *green++;
545 hblue = *blue++;
546
547 if (transp)
548 htransp = *transp++;
549
550 rc = setcolreg(crtc, hred, hgreen, hblue, start++, info);
551 if (rc)
552 return rc;
553 }
554 crtc_funcs->load_lut(crtc);
555 }
556 return rc;
557}
558EXPORT_SYMBOL(drm_fb_helper_setcmap);
559
560int drm_fb_helper_setcolreg(unsigned regno,
561 unsigned red,
562 unsigned green,
563 unsigned blue,
564 unsigned transp,
565 struct fb_info *info)
566{
567 struct drm_fb_helper *fb_helper = info->par;
568 struct drm_device *dev = fb_helper->dev;
569 struct drm_crtc *crtc;
570 int i;
571 int ret;
572
573 if (regno > 255)
574 return 1;
575
576 list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
577 struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private;
578 for (i = 0; i < fb_helper->crtc_count; i++) {
579 if (crtc->base.id == fb_helper->crtc_info[i].crtc_id)
580 break;
581 }
582 if (i == fb_helper->crtc_count)
583 continue;
584
585 ret = setcolreg(crtc, red, green, blue, regno, info);
586 if (ret)
587 return ret;
588
589 crtc_funcs->load_lut(crtc);
590 }
591 return 0;
592}
593EXPORT_SYMBOL(drm_fb_helper_setcolreg);
594
595int drm_fb_helper_check_var(struct fb_var_screeninfo *var,
596 struct fb_info *info)
597{
598 struct drm_fb_helper *fb_helper = info->par;
599 struct drm_framebuffer *fb = fb_helper->fb;
600 int depth;
601
602 if (var->pixclock != 0)
603 return -EINVAL;
604
605
606 if (var->xres > fb->width || var->yres > fb->height) {
607 DRM_ERROR("Requested width/height is greater than current fb "
608 "object %dx%d > %dx%d\n", var->xres, var->yres,
609 fb->width, fb->height);
610 DRM_ERROR("Need resizing code.\n");
611 return -EINVAL;
612 }
613
614 switch (var->bits_per_pixel) {
615 case 16:
616 depth = (var->green.length == 6) ? 16 : 15;
617 break;
618 case 32:
619 depth = (var->transp.length > 0) ? 32 : 24;
620 break;
621 default:
622 depth = var->bits_per_pixel;
623 break;
624 }
625
626 switch (depth) {
627 case 8:
628 var->red.offset = 0;
629 var->green.offset = 0;
630 var->blue.offset = 0;
631 var->red.length = 8;
632 var->green.length = 8;
633 var->blue.length = 8;
634 var->transp.length = 0;
635 var->transp.offset = 0;
636 break;
637 case 15:
638 var->red.offset = 10;
639 var->green.offset = 5;
640 var->blue.offset = 0;
641 var->red.length = 5;
642 var->green.length = 5;
643 var->blue.length = 5;
644 var->transp.length = 1;
645 var->transp.offset = 15;
646 break;
647 case 16:
648 var->red.offset = 11;
649 var->green.offset = 5;
650 var->blue.offset = 0;
651 var->red.length = 5;
652 var->green.length = 6;
653 var->blue.length = 5;
654 var->transp.length = 0;
655 var->transp.offset = 0;
656 break;
657 case 24:
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 = 0;
665 var->transp.offset = 0;
666 break;
667 case 32:
668 var->red.offset = 16;
669 var->green.offset = 8;
670 var->blue.offset = 0;
671 var->red.length = 8;
672 var->green.length = 8;
673 var->blue.length = 8;
674 var->transp.length = 8;
675 var->transp.offset = 24;
676 break;
677 default:
678 return -EINVAL;
679 }
680 return 0;
681}
682EXPORT_SYMBOL(drm_fb_helper_check_var);
683
684
685int drm_fb_helper_set_par(struct fb_info *info)
686{
687 struct drm_fb_helper *fb_helper = info->par;
688 struct drm_device *dev = fb_helper->dev;
689 struct fb_var_screeninfo *var = &info->var;
690 struct drm_crtc *crtc;
691 int ret;
692 int i;
693
694 if (var->pixclock != 0) {
695 DRM_ERROR("PIXEL CLCOK SET\n");
696 return -EINVAL;
697 }
698
699 list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
700
701 for (i = 0; i < fb_helper->crtc_count; i++) {
702 if (crtc->base.id == fb_helper->crtc_info[i].crtc_id)
703 break;
704 }
705 if (i == fb_helper->crtc_count)
706 continue;
707
708 if (crtc->fb == fb_helper->crtc_info[i].mode_set.fb) {
709 mutex_lock(&dev->mode_config.mutex);
710 ret = crtc->funcs->set_config(&fb_helper->crtc_info[i].mode_set);
711 mutex_unlock(&dev->mode_config.mutex);
712 if (ret)
713 return ret;
714 }
715 }
716 return 0;
717}
718EXPORT_SYMBOL(drm_fb_helper_set_par);
719
720int drm_fb_helper_pan_display(struct fb_var_screeninfo *var,
721 struct fb_info *info)
722{
723 struct drm_fb_helper *fb_helper = info->par;
724 struct drm_device *dev = fb_helper->dev;
725 struct drm_mode_set *modeset;
726 struct drm_crtc *crtc;
727 int ret = 0;
728 int i;
729
730 list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
731 for (i = 0; i < fb_helper->crtc_count; i++) {
732 if (crtc->base.id == fb_helper->crtc_info[i].crtc_id)
733 break;
734 }
735
736 if (i == fb_helper->crtc_count)
737 continue;
738
739 modeset = &fb_helper->crtc_info[i].mode_set;
740
741 modeset->x = var->xoffset;
742 modeset->y = var->yoffset;
743
744 if (modeset->num_connectors) {
745 mutex_lock(&dev->mode_config.mutex);
746 ret = crtc->funcs->set_config(modeset);
747 mutex_unlock(&dev->mode_config.mutex);
748 if (!ret) {
749 info->var.xoffset = var->xoffset;
750 info->var.yoffset = var->yoffset;
751 }
752 }
753 }
754 return ret;
755}
756EXPORT_SYMBOL(drm_fb_helper_pan_display);
757
758int drm_fb_helper_single_fb_probe(struct drm_device *dev,
759 int preferred_bpp,
760 int (*fb_create)(struct drm_device *dev,
761 uint32_t fb_width,
762 uint32_t fb_height,
763 uint32_t surface_width,
764 uint32_t surface_height,
765 uint32_t surface_depth,
766 uint32_t surface_bpp,
767 struct drm_framebuffer **fb_ptr))
768{
769 struct drm_crtc *crtc;
770 struct drm_connector *connector;
771 unsigned int fb_width = (unsigned)-1, fb_height = (unsigned)-1;
772 unsigned int surface_width = 0, surface_height = 0;
773 int new_fb = 0;
774 int crtc_count = 0;
775 int ret, i, conn_count = 0;
776 struct fb_info *info;
777 struct drm_framebuffer *fb;
778 struct drm_mode_set *modeset = NULL;
779 struct drm_fb_helper *fb_helper;
780 uint32_t surface_depth = 24, surface_bpp = 32;
781
782
783
784 if (preferred_bpp != surface_bpp) {
785 surface_depth = surface_bpp = preferred_bpp;
786 }
787
788 list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
789 struct drm_fb_helper_connector *fb_help_conn = connector->fb_helper_private;
790
791 struct drm_fb_helper_cmdline_mode *cmdline_mode;
792
793 if (!fb_help_conn)
794 continue;
795
796 cmdline_mode = &fb_help_conn->cmdline_mode;
797
798 if (cmdline_mode->bpp_specified) {
799 switch (cmdline_mode->bpp) {
800 case 8:
801 surface_depth = surface_bpp = 8;
802 break;
803 case 15:
804 surface_depth = 15;
805 surface_bpp = 16;
806 break;
807 case 16:
808 surface_depth = surface_bpp = 16;
809 break;
810 case 24:
811 surface_depth = surface_bpp = 24;
812 break;
813 case 32:
814 surface_depth = 24;
815 surface_bpp = 32;
816 break;
817 }
818 break;
819 }
820 }
821
822 list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
823 if (drm_helper_crtc_in_use(crtc)) {
824 if (crtc->desired_mode) {
825 if (crtc->desired_mode->hdisplay < fb_width)
826 fb_width = crtc->desired_mode->hdisplay;
827
828 if (crtc->desired_mode->vdisplay < fb_height)
829 fb_height = crtc->desired_mode->vdisplay;
830
831 if (crtc->desired_mode->hdisplay > surface_width)
832 surface_width = crtc->desired_mode->hdisplay;
833
834 if (crtc->desired_mode->vdisplay > surface_height)
835 surface_height = crtc->desired_mode->vdisplay;
836 }
837 crtc_count++;
838 }
839 }
840
841 if (crtc_count == 0 || fb_width == -1 || fb_height == -1) {
842
843
844 return 0;
845 }
846
847
848 if (list_empty(&dev->mode_config.fb_kernel_list)) {
849 ret = (*fb_create)(dev, fb_width, fb_height, surface_width,
850 surface_height, surface_depth, surface_bpp,
851 &fb);
852 if (ret)
853 return -EINVAL;
854 new_fb = 1;
855 } else {
856 fb = list_first_entry(&dev->mode_config.fb_kernel_list,
857 struct drm_framebuffer, filp_head);
858
859
860
861
862
863 if ((fb->width < surface_width) ||
864 (fb->height < surface_height)) {
865 DRM_ERROR("Framebuffer not large enough to scale console onto.\n");
866 return -EINVAL;
867 }
868 }
869
870 info = fb->fbdev;
871 fb_helper = info->par;
872
873 crtc_count = 0;
874
875 list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
876 modeset = &fb_helper->crtc_info[crtc_count].mode_set;
877 modeset->fb = fb;
878 conn_count = 0;
879 list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
880 if (connector->encoder)
881 if (connector->encoder->crtc == modeset->crtc) {
882 modeset->connectors[conn_count] = connector;
883 conn_count++;
884 if (conn_count > fb_helper->conn_limit)
885 BUG();
886 }
887 }
888
889 for (i = conn_count; i < fb_helper->conn_limit; i++)
890 modeset->connectors[i] = NULL;
891
892 modeset->crtc = crtc;
893 crtc_count++;
894
895 modeset->num_connectors = conn_count;
896 if (modeset->crtc->desired_mode) {
897 if (modeset->mode)
898 drm_mode_destroy(dev, modeset->mode);
899 modeset->mode = drm_mode_duplicate(dev,
900 modeset->crtc->desired_mode);
901 }
902 }
903 fb_helper->crtc_count = crtc_count;
904 fb_helper->fb = fb;
905
906 if (new_fb) {
907 info->var.pixclock = 0;
908 if (register_framebuffer(info) < 0)
909 return -EINVAL;
910 } else {
911 drm_fb_helper_set_par(info);
912 }
913 printk(KERN_INFO "fb%d: %s frame buffer device\n", info->node,
914 info->fix.id);
915
916
917
918 if (list_empty(&kernel_fb_helper_list)) {
919 printk(KERN_INFO "registered panic notifier\n");
920 atomic_notifier_chain_register(&panic_notifier_list,
921 &paniced);
922 register_sysrq_key('v', &sysrq_drm_fb_helper_restore_op);
923 }
924 list_add(&fb_helper->kernel_fb_list, &kernel_fb_helper_list);
925 return 0;
926}
927EXPORT_SYMBOL(drm_fb_helper_single_fb_probe);
928
929void drm_fb_helper_free(struct drm_fb_helper *helper)
930{
931 list_del(&helper->kernel_fb_list);
932 if (list_empty(&kernel_fb_helper_list)) {
933 printk(KERN_INFO "unregistered panic notifier\n");
934 atomic_notifier_chain_unregister(&panic_notifier_list,
935 &paniced);
936 unregister_sysrq_key('v', &sysrq_drm_fb_helper_restore_op);
937 }
938 drm_fb_helper_crtc_free(helper);
939}
940EXPORT_SYMBOL(drm_fb_helper_free);
941
942void drm_fb_helper_fill_fix(struct fb_info *info, uint32_t pitch,
943 uint32_t depth)
944{
945 info->fix.type = FB_TYPE_PACKED_PIXELS;
946 info->fix.visual = depth == 8 ? FB_VISUAL_PSEUDOCOLOR :
947 FB_VISUAL_TRUECOLOR;
948 info->fix.type_aux = 0;
949 info->fix.xpanstep = 1;
950 info->fix.ypanstep = 1;
951 info->fix.ywrapstep = 0;
952 info->fix.accel = FB_ACCEL_NONE;
953 info->fix.type_aux = 0;
954
955 info->fix.line_length = pitch;
956 return;
957}
958EXPORT_SYMBOL(drm_fb_helper_fill_fix);
959
960void drm_fb_helper_fill_var(struct fb_info *info, struct drm_framebuffer *fb,
961 uint32_t fb_width, uint32_t fb_height)
962{
963 info->pseudo_palette = fb->pseudo_palette;
964 info->var.xres_virtual = fb->width;
965 info->var.yres_virtual = fb->height;
966 info->var.bits_per_pixel = fb->bits_per_pixel;
967 info->var.xoffset = 0;
968 info->var.yoffset = 0;
969 info->var.activate = FB_ACTIVATE_NOW;
970 info->var.height = -1;
971 info->var.width = -1;
972
973 switch (fb->depth) {
974 case 8:
975 info->var.red.offset = 0;
976 info->var.green.offset = 0;
977 info->var.blue.offset = 0;
978 info->var.red.length = 8;
979 info->var.green.length = 8;
980 info->var.blue.length = 8;
981 info->var.transp.offset = 0;
982 info->var.transp.length = 0;
983 break;
984 case 15:
985 info->var.red.offset = 10;
986 info->var.green.offset = 5;
987 info->var.blue.offset = 0;
988 info->var.red.length = 5;
989 info->var.green.length = 5;
990 info->var.blue.length = 5;
991 info->var.transp.offset = 15;
992 info->var.transp.length = 1;
993 break;
994 case 16:
995 info->var.red.offset = 11;
996 info->var.green.offset = 5;
997 info->var.blue.offset = 0;
998 info->var.red.length = 5;
999 info->var.green.length = 6;
1000 info->var.blue.length = 5;
1001 info->var.transp.offset = 0;
1002 break;
1003 case 24:
1004 info->var.red.offset = 16;
1005 info->var.green.offset = 8;
1006 info->var.blue.offset = 0;
1007 info->var.red.length = 8;
1008 info->var.green.length = 8;
1009 info->var.blue.length = 8;
1010 info->var.transp.offset = 0;
1011 info->var.transp.length = 0;
1012 break;
1013 case 32:
1014 info->var.red.offset = 16;
1015 info->var.green.offset = 8;
1016 info->var.blue.offset = 0;
1017 info->var.red.length = 8;
1018 info->var.green.length = 8;
1019 info->var.blue.length = 8;
1020 info->var.transp.offset = 24;
1021 info->var.transp.length = 8;
1022 break;
1023 default:
1024 break;
1025 }
1026
1027 info->var.xres = fb_width;
1028 info->var.yres = fb_height;
1029}
1030EXPORT_SYMBOL(drm_fb_helper_fill_var);
1031