Merge branch 'x86-txt-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git...
[linux-2.6-block.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 */
3b40a443 30#include <linux/kernel.h>
785b93ef 31#include <linux/sysrq.h>
5a0e3ad6 32#include <linux/slab.h>
785b93ef
DA
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
6fcefd56
DA
39MODULE_AUTHOR("David Airlie, Jesse Barnes");
40MODULE_DESCRIPTION("DRM KMS helper");
41MODULE_LICENSE("GPL and additional rights");
42
785b93ef
DA
43static LIST_HEAD(kernel_fb_helper_list);
44
d50ba256
DA
45int 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;
d50ba256
DA
52}
53EXPORT_SYMBOL(drm_fb_helper_add_connector);
54
d50ba256
DA
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 */
68static 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;
8ef8678c 79 struct drm_fb_helper_cmdline_mode *cmdline_mode;
d50ba256 80
8ef8678c
DA
81 if (!fb_help_conn)
82 return false;
83
84 cmdline_mode = &fb_help_conn->cmdline_mode;
d50ba256
DA
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) {
3b40a443 101 refresh = simple_strtol(&name[i+1], NULL, 10);
d50ba256
DA
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) {
3b40a443 111 bpp = simple_strtol(&name[i+1], NULL, 10);
d50ba256
DA
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) {
3b40a443 120 yres = simple_strtol(&name[i+1], NULL, 10);
d50ba256
DA
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':
e89a8c90 146 if ((connector->connector_type != DRM_MODE_CONNECTOR_DVII) &&
d50ba256
DA
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) {
3b40a443 160 xres = simple_strtol(name, NULL, 10);
d50ba256
DA
161 res_specified = 1;
162 }
163done:
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
207int 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
785b93ef
DA
223bool 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
243int 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}
250EXPORT_SYMBOL(drm_fb_helper_panic);
251
252static 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 */
261void 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}
268EXPORT_SYMBOL(drm_fb_helper_restore);
269
bea1d35b 270#ifdef CONFIG_MAGIC_SYSRQ
785b93ef
DA
271static void drm_fb_helper_restore_work_fn(struct work_struct *ignored)
272{
273 drm_fb_helper_restore();
274}
275static DECLARE_WORK(drm_fb_helper_restore_work, drm_fb_helper_restore_work_fn);
276
277static void drm_fb_helper_sysrq(int dummy1, struct tty_struct *dummy3)
278{
279 schedule_work(&drm_fb_helper_restore_work);
280}
281
282static 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};
b8c40d62
RD
287#else
288static struct sysrq_key_op sysrq_drm_fb_helper_restore_op = { };
bea1d35b 289#endif
785b93ef
DA
290
291static void drm_fb_helper_on(struct fb_info *info)
292{
293 struct drm_fb_helper *fb_helper = info->par;
294 struct drm_device *dev = fb_helper->dev;
295 struct drm_crtc *crtc;
296 struct drm_encoder *encoder;
297 int i;
298
299 /*
300 * For each CRTC in this fb, turn the crtc on then,
301 * find all associated encoders and turn them on.
302 */
e87b2c42
JB
303 for (i = 0; i < fb_helper->crtc_count; i++) {
304 list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
305 struct drm_crtc_helper_funcs *crtc_funcs =
306 crtc->helper_private;
785b93ef 307
e87b2c42
JB
308 /* Only mess with CRTCs in this fb */
309 if (crtc->base.id != fb_helper->crtc_info[i].crtc_id ||
310 !crtc->enabled)
311 continue;
785b93ef 312
e87b2c42
JB
313 mutex_lock(&dev->mode_config.mutex);
314 crtc_funcs->dpms(crtc, DRM_MODE_DPMS_ON);
315 mutex_unlock(&dev->mode_config.mutex);
785b93ef 316
e87b2c42
JB
317 /* Found a CRTC on this fb, now find encoders */
318 list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
319 if (encoder->crtc == crtc) {
320 struct drm_encoder_helper_funcs *encoder_funcs;
785b93ef 321
e87b2c42
JB
322 encoder_funcs = encoder->helper_private;
323 mutex_lock(&dev->mode_config.mutex);
324 encoder_funcs->dpms(encoder, DRM_MODE_DPMS_ON);
325 mutex_unlock(&dev->mode_config.mutex);
326 }
785b93ef
DA
327 }
328 }
329 }
330}
331
332static void drm_fb_helper_off(struct fb_info *info, int dpms_mode)
333{
334 struct drm_fb_helper *fb_helper = info->par;
335 struct drm_device *dev = fb_helper->dev;
336 struct drm_crtc *crtc;
337 struct drm_encoder *encoder;
338 int i;
339
340 /*
341 * For each CRTC in this fb, find all associated encoders
342 * and turn them off, then turn off the CRTC.
343 */
e87b2c42
JB
344 for (i = 0; i < fb_helper->crtc_count; i++) {
345 list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
346 struct drm_crtc_helper_funcs *crtc_funcs =
347 crtc->helper_private;
348
349 /* Only mess with CRTCs in this fb */
350 if (crtc->base.id != fb_helper->crtc_info[i].crtc_id ||
351 !crtc->enabled)
352 continue;
353
354 /* Found a CRTC on this fb, now find encoders */
355 list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
356 if (encoder->crtc == crtc) {
357 struct drm_encoder_helper_funcs *encoder_funcs;
358
359 encoder_funcs = encoder->helper_private;
360 mutex_lock(&dev->mode_config.mutex);
361 encoder_funcs->dpms(encoder, dpms_mode);
362 mutex_unlock(&dev->mode_config.mutex);
363 }
364 }
731b5a15
JS
365 mutex_lock(&dev->mode_config.mutex);
366 crtc_funcs->dpms(crtc, DRM_MODE_DPMS_OFF);
367 mutex_unlock(&dev->mode_config.mutex);
785b93ef 368 }
785b93ef
DA
369 }
370}
371
372int drm_fb_helper_blank(int blank, struct fb_info *info)
373{
374 switch (blank) {
731b5a15 375 /* Display: On; HSync: On, VSync: On */
785b93ef
DA
376 case FB_BLANK_UNBLANK:
377 drm_fb_helper_on(info);
378 break;
731b5a15 379 /* Display: Off; HSync: On, VSync: On */
785b93ef 380 case FB_BLANK_NORMAL:
5fd4df4d 381 drm_fb_helper_off(info, DRM_MODE_DPMS_STANDBY);
785b93ef 382 break;
731b5a15 383 /* Display: Off; HSync: Off, VSync: On */
785b93ef
DA
384 case FB_BLANK_HSYNC_SUSPEND:
385 drm_fb_helper_off(info, DRM_MODE_DPMS_STANDBY);
386 break;
731b5a15 387 /* Display: Off; HSync: On, VSync: Off */
785b93ef
DA
388 case FB_BLANK_VSYNC_SUSPEND:
389 drm_fb_helper_off(info, DRM_MODE_DPMS_SUSPEND);
390 break;
731b5a15 391 /* Display: Off; HSync: Off, VSync: Off */
785b93ef
DA
392 case FB_BLANK_POWERDOWN:
393 drm_fb_helper_off(info, DRM_MODE_DPMS_OFF);
394 break;
395 }
396 return 0;
397}
398EXPORT_SYMBOL(drm_fb_helper_blank);
399
400static void drm_fb_helper_crtc_free(struct drm_fb_helper *helper)
401{
402 int i;
403
404 for (i = 0; i < helper->crtc_count; i++)
405 kfree(helper->crtc_info[i].mode_set.connectors);
406 kfree(helper->crtc_info);
407}
408
409int drm_fb_helper_init_crtc_count(struct drm_fb_helper *helper, int crtc_count, int max_conn_count)
410{
411 struct drm_device *dev = helper->dev;
412 struct drm_crtc *crtc;
413 int ret = 0;
414 int i;
415
416 helper->crtc_info = kcalloc(crtc_count, sizeof(struct drm_fb_helper_crtc), GFP_KERNEL);
417 if (!helper->crtc_info)
418 return -ENOMEM;
419
420 helper->crtc_count = crtc_count;
421
422 for (i = 0; i < crtc_count; i++) {
423 helper->crtc_info[i].mode_set.connectors =
424 kcalloc(max_conn_count,
425 sizeof(struct drm_connector *),
426 GFP_KERNEL);
427
428 if (!helper->crtc_info[i].mode_set.connectors) {
429 ret = -ENOMEM;
430 goto out_free;
431 }
432 helper->crtc_info[i].mode_set.num_connectors = 0;
433 }
434
435 i = 0;
436 list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
437 helper->crtc_info[i].crtc_id = crtc->base.id;
438 helper->crtc_info[i].mode_set.crtc = crtc;
439 i++;
440 }
441 helper->conn_limit = max_conn_count;
442 return 0;
443out_free:
444 drm_fb_helper_crtc_free(helper);
445 return -ENOMEM;
446}
447EXPORT_SYMBOL(drm_fb_helper_init_crtc_count);
448
c850cb78 449static int setcolreg(struct drm_crtc *crtc, u16 red, u16 green,
b8c00ac5
DA
450 u16 blue, u16 regno, struct fb_info *info)
451{
452 struct drm_fb_helper *fb_helper = info->par;
453 struct drm_framebuffer *fb = fb_helper->fb;
454 int pindex;
455
c850cb78
DA
456 if (info->fix.visual == FB_VISUAL_TRUECOLOR) {
457 u32 *palette;
458 u32 value;
459 /* place color in psuedopalette */
460 if (regno > 16)
461 return -EINVAL;
462 palette = (u32 *)info->pseudo_palette;
463 red >>= (16 - info->var.red.length);
464 green >>= (16 - info->var.green.length);
465 blue >>= (16 - info->var.blue.length);
466 value = (red << info->var.red.offset) |
467 (green << info->var.green.offset) |
468 (blue << info->var.blue.offset);
469 palette[regno] = value;
470 return 0;
471 }
472
b8c00ac5
DA
473 pindex = regno;
474
475 if (fb->bits_per_pixel == 16) {
476 pindex = regno << 3;
477
478 if (fb->depth == 16 && regno > 63)
c850cb78 479 return -EINVAL;
b8c00ac5 480 if (fb->depth == 15 && regno > 31)
c850cb78 481 return -EINVAL;
b8c00ac5
DA
482
483 if (fb->depth == 16) {
484 u16 r, g, b;
485 int i;
486 if (regno < 32) {
487 for (i = 0; i < 8; i++)
488 fb_helper->funcs->gamma_set(crtc, red,
489 green, blue, pindex + i);
490 }
491
492 fb_helper->funcs->gamma_get(crtc, &r,
493 &g, &b,
494 pindex >> 1);
495
496 for (i = 0; i < 4; i++)
497 fb_helper->funcs->gamma_set(crtc, r,
498 green, b,
499 (pindex >> 1) + i);
500 }
501 }
502
503 if (fb->depth != 16)
504 fb_helper->funcs->gamma_set(crtc, red, green, blue, pindex);
c850cb78 505 return 0;
b8c00ac5
DA
506}
507
068143d3
DA
508int drm_fb_helper_setcmap(struct fb_cmap *cmap, struct fb_info *info)
509{
510 struct drm_fb_helper *fb_helper = info->par;
511 struct drm_device *dev = fb_helper->dev;
512 u16 *red, *green, *blue, *transp;
513 struct drm_crtc *crtc;
514 int i, rc = 0;
515 int start;
516
517 list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
518 struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private;
519 for (i = 0; i < fb_helper->crtc_count; i++) {
520 if (crtc->base.id == fb_helper->crtc_info[i].crtc_id)
521 break;
522 }
523 if (i == fb_helper->crtc_count)
524 continue;
525
526 red = cmap->red;
527 green = cmap->green;
528 blue = cmap->blue;
529 transp = cmap->transp;
530 start = cmap->start;
531
532 for (i = 0; i < cmap->len; i++) {
533 u16 hred, hgreen, hblue, htransp = 0xffff;
534
535 hred = *red++;
536 hgreen = *green++;
537 hblue = *blue++;
538
539 if (transp)
540 htransp = *transp++;
541
c850cb78
DA
542 rc = setcolreg(crtc, hred, hgreen, hblue, start++, info);
543 if (rc)
544 return rc;
068143d3
DA
545 }
546 crtc_funcs->load_lut(crtc);
547 }
548 return rc;
549}
550EXPORT_SYMBOL(drm_fb_helper_setcmap);
551
785b93ef
DA
552int drm_fb_helper_setcolreg(unsigned regno,
553 unsigned red,
554 unsigned green,
555 unsigned blue,
556 unsigned transp,
557 struct fb_info *info)
558{
559 struct drm_fb_helper *fb_helper = info->par;
560 struct drm_device *dev = fb_helper->dev;
561 struct drm_crtc *crtc;
562 int i;
c850cb78 563 int ret;
785b93ef 564
b8c00ac5
DA
565 if (regno > 255)
566 return 1;
785b93ef 567
b8c00ac5
DA
568 list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
569 struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private;
785b93ef
DA
570 for (i = 0; i < fb_helper->crtc_count; i++) {
571 if (crtc->base.id == fb_helper->crtc_info[i].crtc_id)
572 break;
573 }
574 if (i == fb_helper->crtc_count)
575 continue;
576
c850cb78
DA
577 ret = setcolreg(crtc, red, green, blue, regno, info);
578 if (ret)
579 return ret;
785b93ef 580
b8c00ac5 581 crtc_funcs->load_lut(crtc);
785b93ef
DA
582 }
583 return 0;
584}
585EXPORT_SYMBOL(drm_fb_helper_setcolreg);
586
587int drm_fb_helper_check_var(struct fb_var_screeninfo *var,
588 struct fb_info *info)
589{
590 struct drm_fb_helper *fb_helper = info->par;
591 struct drm_framebuffer *fb = fb_helper->fb;
592 int depth;
593
5349ef31 594 if (var->pixclock != 0)
785b93ef
DA
595 return -EINVAL;
596
597 /* Need to resize the fb object !!! */
509c7d83
DA
598 if (var->bits_per_pixel > fb->bits_per_pixel || var->xres > fb->width || var->yres > fb->height) {
599 DRM_DEBUG("fb userspace requested width/height/bpp is greater than current fb "
600 "object %dx%d-%d > %dx%d-%d\n", var->xres, var->yres, var->bits_per_pixel,
601 fb->width, fb->height, fb->bits_per_pixel);
785b93ef
DA
602 return -EINVAL;
603 }
604
605 switch (var->bits_per_pixel) {
606 case 16:
607 depth = (var->green.length == 6) ? 16 : 15;
608 break;
609 case 32:
610 depth = (var->transp.length > 0) ? 32 : 24;
611 break;
612 default:
613 depth = var->bits_per_pixel;
614 break;
615 }
616
617 switch (depth) {
618 case 8:
619 var->red.offset = 0;
620 var->green.offset = 0;
621 var->blue.offset = 0;
622 var->red.length = 8;
623 var->green.length = 8;
624 var->blue.length = 8;
625 var->transp.length = 0;
626 var->transp.offset = 0;
627 break;
628 case 15:
629 var->red.offset = 10;
630 var->green.offset = 5;
631 var->blue.offset = 0;
632 var->red.length = 5;
633 var->green.length = 5;
634 var->blue.length = 5;
635 var->transp.length = 1;
636 var->transp.offset = 15;
637 break;
638 case 16:
639 var->red.offset = 11;
640 var->green.offset = 5;
641 var->blue.offset = 0;
642 var->red.length = 5;
643 var->green.length = 6;
644 var->blue.length = 5;
645 var->transp.length = 0;
646 var->transp.offset = 0;
647 break;
648 case 24:
649 var->red.offset = 16;
650 var->green.offset = 8;
651 var->blue.offset = 0;
652 var->red.length = 8;
653 var->green.length = 8;
654 var->blue.length = 8;
655 var->transp.length = 0;
656 var->transp.offset = 0;
657 break;
658 case 32:
659 var->red.offset = 16;
660 var->green.offset = 8;
661 var->blue.offset = 0;
662 var->red.length = 8;
663 var->green.length = 8;
664 var->blue.length = 8;
665 var->transp.length = 8;
666 var->transp.offset = 24;
667 break;
668 default:
669 return -EINVAL;
670 }
671 return 0;
672}
673EXPORT_SYMBOL(drm_fb_helper_check_var);
674
675/* this will let fbcon do the mode init */
676int drm_fb_helper_set_par(struct fb_info *info)
677{
678 struct drm_fb_helper *fb_helper = info->par;
679 struct drm_device *dev = fb_helper->dev;
680 struct fb_var_screeninfo *var = &info->var;
681 struct drm_crtc *crtc;
682 int ret;
683 int i;
684
5349ef31 685 if (var->pixclock != 0) {
172e91f5 686 DRM_ERROR("PIXEL CLOCK SET\n");
785b93ef
DA
687 return -EINVAL;
688 }
689
690 list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
691
692 for (i = 0; i < fb_helper->crtc_count; i++) {
693 if (crtc->base.id == fb_helper->crtc_info[i].crtc_id)
694 break;
695 }
696 if (i == fb_helper->crtc_count)
697 continue;
698
699 if (crtc->fb == fb_helper->crtc_info[i].mode_set.fb) {
700 mutex_lock(&dev->mode_config.mutex);
a2d49ae7 701 ret = crtc->funcs->set_config(&fb_helper->crtc_info[i].mode_set);
785b93ef
DA
702 mutex_unlock(&dev->mode_config.mutex);
703 if (ret)
704 return ret;
705 }
706 }
707 return 0;
708}
709EXPORT_SYMBOL(drm_fb_helper_set_par);
710
711int drm_fb_helper_pan_display(struct fb_var_screeninfo *var,
712 struct fb_info *info)
713{
714 struct drm_fb_helper *fb_helper = info->par;
715 struct drm_device *dev = fb_helper->dev;
716 struct drm_mode_set *modeset;
717 struct drm_crtc *crtc;
718 int ret = 0;
719 int i;
720
721 list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
722 for (i = 0; i < fb_helper->crtc_count; i++) {
723 if (crtc->base.id == fb_helper->crtc_info[i].crtc_id)
724 break;
725 }
726
727 if (i == fb_helper->crtc_count)
728 continue;
729
730 modeset = &fb_helper->crtc_info[i].mode_set;
731
732 modeset->x = var->xoffset;
733 modeset->y = var->yoffset;
734
735 if (modeset->num_connectors) {
736 mutex_lock(&dev->mode_config.mutex);
737 ret = crtc->funcs->set_config(modeset);
738 mutex_unlock(&dev->mode_config.mutex);
739 if (!ret) {
740 info->var.xoffset = var->xoffset;
741 info->var.yoffset = var->yoffset;
742 }
743 }
744 }
745 return ret;
746}
747EXPORT_SYMBOL(drm_fb_helper_pan_display);
748
749int drm_fb_helper_single_fb_probe(struct drm_device *dev,
b8c00ac5 750 int preferred_bpp,
785b93ef
DA
751 int (*fb_create)(struct drm_device *dev,
752 uint32_t fb_width,
753 uint32_t fb_height,
754 uint32_t surface_width,
755 uint32_t surface_height,
d50ba256
DA
756 uint32_t surface_depth,
757 uint32_t surface_bpp,
785b93ef
DA
758 struct drm_framebuffer **fb_ptr))
759{
760 struct drm_crtc *crtc;
761 struct drm_connector *connector;
762 unsigned int fb_width = (unsigned)-1, fb_height = (unsigned)-1;
763 unsigned int surface_width = 0, surface_height = 0;
764 int new_fb = 0;
765 int crtc_count = 0;
766 int ret, i, conn_count = 0;
767 struct fb_info *info;
768 struct drm_framebuffer *fb;
769 struct drm_mode_set *modeset = NULL;
770 struct drm_fb_helper *fb_helper;
d50ba256 771 uint32_t surface_depth = 24, surface_bpp = 32;
785b93ef 772
b8c00ac5
DA
773 /* if driver picks 8 or 16 by default use that
774 for both depth/bpp */
775 if (preferred_bpp != surface_bpp) {
776 surface_depth = surface_bpp = preferred_bpp;
777 }
785b93ef 778 /* first up get a count of crtcs now in use and new min/maxes width/heights */
d50ba256
DA
779 list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
780 struct drm_fb_helper_connector *fb_help_conn = connector->fb_helper_private;
8ef8678c
DA
781
782 struct drm_fb_helper_cmdline_mode *cmdline_mode;
783
784 if (!fb_help_conn)
785 continue;
786
787 cmdline_mode = &fb_help_conn->cmdline_mode;
d50ba256
DA
788
789 if (cmdline_mode->bpp_specified) {
790 switch (cmdline_mode->bpp) {
791 case 8:
792 surface_depth = surface_bpp = 8;
793 break;
794 case 15:
795 surface_depth = 15;
796 surface_bpp = 16;
797 break;
798 case 16:
799 surface_depth = surface_bpp = 16;
800 break;
801 case 24:
802 surface_depth = surface_bpp = 24;
803 break;
804 case 32:
805 surface_depth = 24;
806 surface_bpp = 32;
807 break;
808 }
809 break;
810 }
811 }
812
785b93ef
DA
813 list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
814 if (drm_helper_crtc_in_use(crtc)) {
815 if (crtc->desired_mode) {
816 if (crtc->desired_mode->hdisplay < fb_width)
817 fb_width = crtc->desired_mode->hdisplay;
818
819 if (crtc->desired_mode->vdisplay < fb_height)
820 fb_height = crtc->desired_mode->vdisplay;
821
822 if (crtc->desired_mode->hdisplay > surface_width)
823 surface_width = crtc->desired_mode->hdisplay;
824
825 if (crtc->desired_mode->vdisplay > surface_height)
826 surface_height = crtc->desired_mode->vdisplay;
827 }
828 crtc_count++;
829 }
830 }
831
832 if (crtc_count == 0 || fb_width == -1 || fb_height == -1) {
833 /* hmm everyone went away - assume VGA cable just fell out
834 and will come back later. */
835 return 0;
836 }
837
838 /* do we have an fb already? */
839 if (list_empty(&dev->mode_config.fb_kernel_list)) {
840 ret = (*fb_create)(dev, fb_width, fb_height, surface_width,
d50ba256
DA
841 surface_height, surface_depth, surface_bpp,
842 &fb);
785b93ef
DA
843 if (ret)
844 return -EINVAL;
845 new_fb = 1;
846 } else {
847 fb = list_first_entry(&dev->mode_config.fb_kernel_list,
848 struct drm_framebuffer, filp_head);
849
850 /* if someone hotplugs something bigger than we have already allocated, we are pwned.
851 As really we can't resize an fbdev that is in the wild currently due to fbdev
852 not really being designed for the lower layers moving stuff around under it.
853 - so in the grand style of things - punt. */
854 if ((fb->width < surface_width) ||
855 (fb->height < surface_height)) {
856 DRM_ERROR("Framebuffer not large enough to scale console onto.\n");
857 return -EINVAL;
858 }
859 }
860
861 info = fb->fbdev;
862 fb_helper = info->par;
863
864 crtc_count = 0;
865 /* okay we need to setup new connector sets in the crtcs */
866 list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
867 modeset = &fb_helper->crtc_info[crtc_count].mode_set;
868 modeset->fb = fb;
869 conn_count = 0;
870 list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
871 if (connector->encoder)
872 if (connector->encoder->crtc == modeset->crtc) {
873 modeset->connectors[conn_count] = connector;
874 conn_count++;
875 if (conn_count > fb_helper->conn_limit)
876 BUG();
877 }
878 }
879
880 for (i = conn_count; i < fb_helper->conn_limit; i++)
881 modeset->connectors[i] = NULL;
882
883 modeset->crtc = crtc;
884 crtc_count++;
885
886 modeset->num_connectors = conn_count;
887 if (modeset->crtc->desired_mode) {
888 if (modeset->mode)
889 drm_mode_destroy(dev, modeset->mode);
890 modeset->mode = drm_mode_duplicate(dev,
891 modeset->crtc->desired_mode);
892 }
893 }
894 fb_helper->crtc_count = crtc_count;
895 fb_helper->fb = fb;
896
897 if (new_fb) {
5349ef31 898 info->var.pixclock = 0;
b0a007dc 899 ret = fb_alloc_cmap(&info->cmap, modeset->crtc->gamma_size, 0);
3bea21b6
CL
900 if (ret)
901 return ret;
902 if (register_framebuffer(info) < 0) {
903 fb_dealloc_cmap(&info->cmap);
785b93ef 904 return -EINVAL;
3bea21b6 905 }
785b93ef
DA
906 } else {
907 drm_fb_helper_set_par(info);
908 }
909 printk(KERN_INFO "fb%d: %s frame buffer device\n", info->node,
910 info->fix.id);
911
912 /* Switch back to kernel console on panic */
913 /* multi card linked list maybe */
914 if (list_empty(&kernel_fb_helper_list)) {
915 printk(KERN_INFO "registered panic notifier\n");
916 atomic_notifier_chain_register(&panic_notifier_list,
917 &paniced);
918 register_sysrq_key('v', &sysrq_drm_fb_helper_restore_op);
919 }
920 list_add(&fb_helper->kernel_fb_list, &kernel_fb_helper_list);
921 return 0;
922}
923EXPORT_SYMBOL(drm_fb_helper_single_fb_probe);
924
925void drm_fb_helper_free(struct drm_fb_helper *helper)
926{
927 list_del(&helper->kernel_fb_list);
928 if (list_empty(&kernel_fb_helper_list)) {
929 printk(KERN_INFO "unregistered panic notifier\n");
930 atomic_notifier_chain_unregister(&panic_notifier_list,
931 &paniced);
932 unregister_sysrq_key('v', &sysrq_drm_fb_helper_restore_op);
933 }
934 drm_fb_helper_crtc_free(helper);
3bea21b6 935 fb_dealloc_cmap(&helper->fb->fbdev->cmap);
785b93ef
DA
936}
937EXPORT_SYMBOL(drm_fb_helper_free);
938
068143d3
DA
939void drm_fb_helper_fill_fix(struct fb_info *info, uint32_t pitch,
940 uint32_t depth)
785b93ef
DA
941{
942 info->fix.type = FB_TYPE_PACKED_PIXELS;
068143d3 943 info->fix.visual = depth == 8 ? FB_VISUAL_PSEUDOCOLOR :
c850cb78 944 FB_VISUAL_TRUECOLOR;
785b93ef
DA
945 info->fix.type_aux = 0;
946 info->fix.xpanstep = 1; /* doing it in hw */
947 info->fix.ypanstep = 1; /* doing it in hw */
948 info->fix.ywrapstep = 0;
3420e742 949 info->fix.accel = FB_ACCEL_NONE;
785b93ef
DA
950 info->fix.type_aux = 0;
951
952 info->fix.line_length = pitch;
953 return;
954}
955EXPORT_SYMBOL(drm_fb_helper_fill_fix);
956
957void drm_fb_helper_fill_var(struct fb_info *info, struct drm_framebuffer *fb,
958 uint32_t fb_width, uint32_t fb_height)
959{
960 info->pseudo_palette = fb->pseudo_palette;
961 info->var.xres_virtual = fb->width;
962 info->var.yres_virtual = fb->height;
963 info->var.bits_per_pixel = fb->bits_per_pixel;
964 info->var.xoffset = 0;
965 info->var.yoffset = 0;
966 info->var.activate = FB_ACTIVATE_NOW;
967 info->var.height = -1;
968 info->var.width = -1;
969
970 switch (fb->depth) {
971 case 8:
972 info->var.red.offset = 0;
973 info->var.green.offset = 0;
974 info->var.blue.offset = 0;
975 info->var.red.length = 8; /* 8bit DAC */
976 info->var.green.length = 8;
977 info->var.blue.length = 8;
978 info->var.transp.offset = 0;
979 info->var.transp.length = 0;
980 break;
981 case 15:
982 info->var.red.offset = 10;
983 info->var.green.offset = 5;
984 info->var.blue.offset = 0;
985 info->var.red.length = 5;
986 info->var.green.length = 5;
987 info->var.blue.length = 5;
988 info->var.transp.offset = 15;
989 info->var.transp.length = 1;
990 break;
991 case 16:
992 info->var.red.offset = 11;
993 info->var.green.offset = 5;
994 info->var.blue.offset = 0;
995 info->var.red.length = 5;
996 info->var.green.length = 6;
997 info->var.blue.length = 5;
998 info->var.transp.offset = 0;
999 break;
1000 case 24:
1001 info->var.red.offset = 16;
1002 info->var.green.offset = 8;
1003 info->var.blue.offset = 0;
1004 info->var.red.length = 8;
1005 info->var.green.length = 8;
1006 info->var.blue.length = 8;
1007 info->var.transp.offset = 0;
1008 info->var.transp.length = 0;
1009 break;
1010 case 32:
1011 info->var.red.offset = 16;
1012 info->var.green.offset = 8;
1013 info->var.blue.offset = 0;
1014 info->var.red.length = 8;
1015 info->var.green.length = 8;
1016 info->var.blue.length = 8;
1017 info->var.transp.offset = 24;
1018 info->var.transp.length = 8;
1019 break;
1020 default:
1021 break;
1022 }
1023
1024 info->var.xres = fb_width;
1025 info->var.yres = fb_height;
1026}
1027EXPORT_SYMBOL(drm_fb_helper_fill_var);