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