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