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