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