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