drm/radeon/kms/atom: Make card_info per device
[deliverable/linux.git] / drivers / gpu / drm / drm_fb_helper.c
CommitLineData
785b93ef
DA
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
6fcefd56
DA
37MODULE_AUTHOR("David Airlie, Jesse Barnes");
38MODULE_DESCRIPTION("DRM KMS helper");
39MODULE_LICENSE("GPL and additional rights");
40
785b93ef
DA
41static LIST_HEAD(kernel_fb_helper_list);
42
d50ba256
DA
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;
d50ba256
DA
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 * drm_fb_helper_connector_parse_command_line - parse command line for connector
70 * @connector - connector to parse line for
71 * @mode_option - per connector mode option
72 *
73 * This parses the connector specific then generic command lines for
74 * modes and options to configure the connector.
75 *
76 * This uses the same parameters as the fb modedb.c, except for extra
77 * <xres>x<yres>[M][R][-<bpp>][@<refresh>][i][m][eDd]
78 *
79 * enable/enable Digital/disable bit at the end
80 */
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;
8ef8678c 92 struct drm_fb_helper_cmdline_mode *cmdline_mode;
d50ba256 93
8ef8678c
DA
94 if (!fb_help_conn)
95 return false;
96
97 cmdline_mode = &fb_help_conn->cmdline_mode;
d50ba256
DA
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 /* do something on return - turn off connector maybe */
228 if (fb_get_options(drm_get_connector_name(connector), &option))
229 continue;
230
231 drm_fb_helper_connector_parse_command_line(connector, option);
232 }
233 return 0;
234}
235
785b93ef
DA
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 * drm_fb_helper_restore - restore the framebuffer console (kernel) config
271 *
272 * Restore's the kernel's fbcon mode, used for lastclose & panic paths.
273 */
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
bea1d35b 283#ifdef CONFIG_MAGIC_SYSRQ
785b93ef
DA
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};
bea1d35b 300#endif
785b93ef
DA
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 * For each CRTC in this fb, turn the crtc on then,
312 * find all associated encoders and turn them on.
313 */
e87b2c42
JB
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;
785b93ef 318
e87b2c42
JB
319 /* Only mess with CRTCs in this fb */
320 if (crtc->base.id != fb_helper->crtc_info[i].crtc_id ||
321 !crtc->enabled)
322 continue;
785b93ef 323
e87b2c42
JB
324 mutex_lock(&dev->mode_config.mutex);
325 crtc_funcs->dpms(crtc, DRM_MODE_DPMS_ON);
326 mutex_unlock(&dev->mode_config.mutex);
785b93ef 327
e87b2c42
JB
328 /* Found a CRTC on this fb, now find encoders */
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;
785b93ef 332
e87b2c42
JB
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 }
785b93ef
DA
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 * For each CRTC in this fb, find all associated encoders
353 * and turn them off, then turn off the CRTC.
354 */
e87b2c42
JB
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 /* Only mess with CRTCs in this fb */
361 if (crtc->base.id != fb_helper->crtc_info[i].crtc_id ||
362 !crtc->enabled)
363 continue;
364
365 /* Found a CRTC on this fb, now find encoders */
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) {
785b93ef 377 mutex_lock(&dev->mode_config.mutex);
e87b2c42 378 crtc_funcs->dpms(crtc, dpms_mode);
785b93ef
DA
379 mutex_unlock(&dev->mode_config.mutex);
380 }
381 }
785b93ef
DA
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
c850cb78 457static int setcolreg(struct drm_crtc *crtc, u16 red, u16 green,
b8c00ac5
DA
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
c850cb78
DA
464 if (info->fix.visual == FB_VISUAL_TRUECOLOR) {
465 u32 *palette;
466 u32 value;
467 /* place color in psuedopalette */
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
b8c00ac5
DA
481 pindex = regno;
482
483 if (fb->bits_per_pixel == 16) {
484 pindex = regno << 3;
485
486 if (fb->depth == 16 && regno > 63)
c850cb78 487 return -EINVAL;
b8c00ac5 488 if (fb->depth == 15 && regno > 31)
c850cb78 489 return -EINVAL;
b8c00ac5
DA
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);
c850cb78 513 return 0;
b8c00ac5
DA
514}
515
068143d3
DA
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
c850cb78
DA
550 rc = setcolreg(crtc, hred, hgreen, hblue, start++, info);
551 if (rc)
552 return rc;
068143d3
DA
553 }
554 crtc_funcs->load_lut(crtc);
555 }
556 return rc;
557}
558EXPORT_SYMBOL(drm_fb_helper_setcmap);
559
785b93ef
DA
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;
c850cb78 571 int ret;
785b93ef 572
b8c00ac5
DA
573 if (regno > 255)
574 return 1;
785b93ef 575
b8c00ac5
DA
576 list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
577 struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private;
785b93ef
DA
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
c850cb78
DA
585 ret = setcolreg(crtc, red, green, blue, regno, info);
586 if (ret)
587 return ret;
785b93ef 588
b8c00ac5 589 crtc_funcs->load_lut(crtc);
785b93ef
DA
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 == -1 || !var->pixclock)
603 return -EINVAL;
604
605 /* Need to resize the fb object !!! */
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/* this will let fbcon do the mode init */
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 != -1) {
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->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,
b8c00ac5 759 int preferred_bpp,
785b93ef
DA
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,
d50ba256
DA
765 uint32_t surface_depth,
766 uint32_t surface_bpp,
785b93ef
DA
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;
d50ba256 780 uint32_t surface_depth = 24, surface_bpp = 32;
785b93ef 781
b8c00ac5
DA
782 /* if driver picks 8 or 16 by default use that
783 for both depth/bpp */
784 if (preferred_bpp != surface_bpp) {
785 surface_depth = surface_bpp = preferred_bpp;
786 }
785b93ef 787 /* first up get a count of crtcs now in use and new min/maxes width/heights */
d50ba256
DA
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;
8ef8678c
DA
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;
d50ba256
DA
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
785b93ef
DA
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 /* hmm everyone went away - assume VGA cable just fell out
843 and will come back later. */
844 return 0;
845 }
846
847 /* do we have an fb already? */
848 if (list_empty(&dev->mode_config.fb_kernel_list)) {
849 ret = (*fb_create)(dev, fb_width, fb_height, surface_width,
d50ba256
DA
850 surface_height, surface_depth, surface_bpp,
851 &fb);
785b93ef
DA
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 /* if someone hotplugs something bigger than we have already allocated, we are pwned.
860 As really we can't resize an fbdev that is in the wild currently due to fbdev
861 not really being designed for the lower layers moving stuff around under it.
862 - so in the grand style of things - punt. */
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 /* okay we need to setup new connector sets in the crtcs */
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 = -1;
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 /* Switch back to kernel console on panic */
917 /* multi card linked list maybe */
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
068143d3
DA
942void drm_fb_helper_fill_fix(struct fb_info *info, uint32_t pitch,
943 uint32_t depth)
785b93ef
DA
944{
945 info->fix.type = FB_TYPE_PACKED_PIXELS;
068143d3 946 info->fix.visual = depth == 8 ? FB_VISUAL_PSEUDOCOLOR :
c850cb78 947 FB_VISUAL_TRUECOLOR;
785b93ef
DA
948 info->fix.type_aux = 0;
949 info->fix.xpanstep = 1; /* doing it in hw */
950 info->fix.ypanstep = 1; /* doing it in hw */
951 info->fix.ywrapstep = 0;
3420e742 952 info->fix.accel = FB_ACCEL_NONE;
785b93ef
DA
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; /* 8bit DAC */
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);
This page took 0.073381 seconds and 5 git commands to generate.