]> bbs.cooldavid.org Git - net-next-2.6.git/blame - drivers/gpu/drm/drm_fb_helper.c
include cleanup: Update gfp.h and slab.h includes to prepare for breaking implicit...
[net-next-2.6.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};
bea1d35b 287#endif
785b93ef
DA
288
289static 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 */
e87b2c42
JB
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;
785b93ef 305
e87b2c42
JB
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;
785b93ef 310
e87b2c42
JB
311 mutex_lock(&dev->mode_config.mutex);
312 crtc_funcs->dpms(crtc, DRM_MODE_DPMS_ON);
313 mutex_unlock(&dev->mode_config.mutex);
785b93ef 314
e87b2c42
JB
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;
785b93ef 319
e87b2c42
JB
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 }
785b93ef
DA
325 }
326 }
327 }
328}
329
330static 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 */
e87b2c42
JB
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 }
731b5a15
JS
363 mutex_lock(&dev->mode_config.mutex);
364 crtc_funcs->dpms(crtc, DRM_MODE_DPMS_OFF);
365 mutex_unlock(&dev->mode_config.mutex);
785b93ef 366 }
785b93ef
DA
367 }
368}
369
370int drm_fb_helper_blank(int blank, struct fb_info *info)
371{
372 switch (blank) {
731b5a15 373 /* Display: On; HSync: On, VSync: On */
785b93ef
DA
374 case FB_BLANK_UNBLANK:
375 drm_fb_helper_on(info);
376 break;
731b5a15 377 /* Display: Off; HSync: On, VSync: On */
785b93ef 378 case FB_BLANK_NORMAL:
5fd4df4d 379 drm_fb_helper_off(info, DRM_MODE_DPMS_STANDBY);
785b93ef 380 break;
731b5a15 381 /* Display: Off; HSync: Off, VSync: On */
785b93ef
DA
382 case FB_BLANK_HSYNC_SUSPEND:
383 drm_fb_helper_off(info, DRM_MODE_DPMS_STANDBY);
384 break;
731b5a15 385 /* Display: Off; HSync: On, VSync: Off */
785b93ef
DA
386 case FB_BLANK_VSYNC_SUSPEND:
387 drm_fb_helper_off(info, DRM_MODE_DPMS_SUSPEND);
388 break;
731b5a15 389 /* Display: Off; HSync: Off, VSync: Off */
785b93ef
DA
390 case FB_BLANK_POWERDOWN:
391 drm_fb_helper_off(info, DRM_MODE_DPMS_OFF);
392 break;
393 }
394 return 0;
395}
396EXPORT_SYMBOL(drm_fb_helper_blank);
397
398static 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
407int 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;
441out_free:
442 drm_fb_helper_crtc_free(helper);
443 return -ENOMEM;
444}
445EXPORT_SYMBOL(drm_fb_helper_init_crtc_count);
446
c850cb78 447static int setcolreg(struct drm_crtc *crtc, u16 red, u16 green,
b8c00ac5
DA
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
c850cb78
DA
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
b8c00ac5
DA
471 pindex = regno;
472
473 if (fb->bits_per_pixel == 16) {
474 pindex = regno << 3;
475
476 if (fb->depth == 16 && regno > 63)
c850cb78 477 return -EINVAL;
b8c00ac5 478 if (fb->depth == 15 && regno > 31)
c850cb78 479 return -EINVAL;
b8c00ac5
DA
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);
c850cb78 503 return 0;
b8c00ac5
DA
504}
505
068143d3
DA
506int 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
c850cb78
DA
540 rc = setcolreg(crtc, hred, hgreen, hblue, start++, info);
541 if (rc)
542 return rc;
068143d3
DA
543 }
544 crtc_funcs->load_lut(crtc);
545 }
546 return rc;
547}
548EXPORT_SYMBOL(drm_fb_helper_setcmap);
549
785b93ef
DA
550int 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;
c850cb78 561 int ret;
785b93ef 562
b8c00ac5
DA
563 if (regno > 255)
564 return 1;
785b93ef 565
b8c00ac5
DA
566 list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
567 struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private;
785b93ef
DA
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
c850cb78
DA
575 ret = setcolreg(crtc, red, green, blue, regno, info);
576 if (ret)
577 return ret;
785b93ef 578
b8c00ac5 579 crtc_funcs->load_lut(crtc);
785b93ef
DA
580 }
581 return 0;
582}
583EXPORT_SYMBOL(drm_fb_helper_setcolreg);
584
585int 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
5349ef31 592 if (var->pixclock != 0)
785b93ef
DA
593 return -EINVAL;
594
595 /* Need to resize the fb object !!! */
509c7d83
DA
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);
785b93ef
DA
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}
671EXPORT_SYMBOL(drm_fb_helper_check_var);
672
673/* this will let fbcon do the mode init */
674int 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
5349ef31 683 if (var->pixclock != 0) {
172e91f5 684 DRM_ERROR("PIXEL CLOCK SET\n");
785b93ef
DA
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);
a2d49ae7 699 ret = crtc->funcs->set_config(&fb_helper->crtc_info[i].mode_set);
785b93ef
DA
700 mutex_unlock(&dev->mode_config.mutex);
701 if (ret)
702 return ret;
703 }
704 }
705 return 0;
706}
707EXPORT_SYMBOL(drm_fb_helper_set_par);
708
709int 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}
745EXPORT_SYMBOL(drm_fb_helper_pan_display);
746
747int drm_fb_helper_single_fb_probe(struct drm_device *dev,
b8c00ac5 748 int preferred_bpp,
785b93ef
DA
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,
d50ba256
DA
754 uint32_t surface_depth,
755 uint32_t surface_bpp,
785b93ef
DA
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;
d50ba256 769 uint32_t surface_depth = 24, surface_bpp = 32;
785b93ef 770
b8c00ac5
DA
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 }
785b93ef 776 /* first up get a count of crtcs now in use and new min/maxes width/heights */
d50ba256
DA
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;
8ef8678c
DA
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;
d50ba256
DA
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
785b93ef
DA
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,
d50ba256
DA
839 surface_height, surface_depth, surface_bpp,
840 &fb);
785b93ef
DA
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) {
5349ef31 896 info->var.pixclock = 0;
b0a007dc 897 ret = fb_alloc_cmap(&info->cmap, modeset->crtc->gamma_size, 0);
3bea21b6
CL
898 if (ret)
899 return ret;
900 if (register_framebuffer(info) < 0) {
901 fb_dealloc_cmap(&info->cmap);
785b93ef 902 return -EINVAL;
3bea21b6 903 }
785b93ef
DA
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}
921EXPORT_SYMBOL(drm_fb_helper_single_fb_probe);
922
923void 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);
3bea21b6 933 fb_dealloc_cmap(&helper->fb->fbdev->cmap);
785b93ef
DA
934}
935EXPORT_SYMBOL(drm_fb_helper_free);
936
068143d3
DA
937void drm_fb_helper_fill_fix(struct fb_info *info, uint32_t pitch,
938 uint32_t depth)
785b93ef
DA
939{
940 info->fix.type = FB_TYPE_PACKED_PIXELS;
068143d3 941 info->fix.visual = depth == 8 ? FB_VISUAL_PSEUDOCOLOR :
c850cb78 942 FB_VISUAL_TRUECOLOR;
785b93ef
DA
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;
3420e742 947 info->fix.accel = FB_ACCEL_NONE;
785b93ef
DA
948 info->fix.type_aux = 0;
949
950 info->fix.line_length = pitch;
951 return;
952}
953EXPORT_SYMBOL(drm_fb_helper_fill_fix);
954
955void 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}
1025EXPORT_SYMBOL(drm_fb_helper_fill_var);