aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/gpu/drm/drm_fb_helper.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpu/drm/drm_fb_helper.c')
-rw-r--r--drivers/gpu/drm/drm_fb_helper.c391
1 files changed, 357 insertions, 34 deletions
diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c
index 2c4671314884..dc8e374a0b55 100644
--- a/drivers/gpu/drm/drm_fb_helper.c
+++ b/drivers/gpu/drm/drm_fb_helper.c
@@ -40,6 +40,199 @@ MODULE_LICENSE("GPL and additional rights");
40 40
41static LIST_HEAD(kernel_fb_helper_list); 41static LIST_HEAD(kernel_fb_helper_list);
42 42
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}
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;
92 struct drm_fb_helper_cmdline_mode *cmdline_mode;
93
94 if (!fb_help_conn)
95 return false;
96
97 cmdline_mode = &fb_help_conn->cmdline_mode;
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
43bool drm_fb_helper_force_kernel_mode(void) 236bool drm_fb_helper_force_kernel_mode(void)
44{ 237{
45 int i = 0; 238 int i = 0;
@@ -87,6 +280,7 @@ void drm_fb_helper_restore(void)
87} 280}
88EXPORT_SYMBOL(drm_fb_helper_restore); 281EXPORT_SYMBOL(drm_fb_helper_restore);
89 282
283#ifdef CONFIG_MAGIC_SYSRQ
90static void drm_fb_helper_restore_work_fn(struct work_struct *ignored) 284static void drm_fb_helper_restore_work_fn(struct work_struct *ignored)
91{ 285{
92 drm_fb_helper_restore(); 286 drm_fb_helper_restore();
@@ -103,6 +297,7 @@ static struct sysrq_key_op sysrq_drm_fb_helper_restore_op = {
103 .help_msg = "force-fb(V)", 297 .help_msg = "force-fb(V)",
104 .action_msg = "Restore framebuffer console", 298 .action_msg = "Restore framebuffer console",
105}; 299};
300#endif
106 301
107static void drm_fb_helper_on(struct fb_info *info) 302static void drm_fb_helper_on(struct fb_info *info)
108{ 303{
@@ -259,6 +454,109 @@ out_free:
259} 454}
260EXPORT_SYMBOL(drm_fb_helper_init_crtc_count); 455EXPORT_SYMBOL(drm_fb_helper_init_crtc_count);
261 456
457static int setcolreg(struct drm_crtc *crtc, u16 red, u16 green,
458 u16 blue, u16 regno, struct fb_info *info)
459{
460 struct drm_fb_helper *fb_helper = info->par;
461 struct drm_framebuffer *fb = fb_helper->fb;
462 int pindex;
463
464 if (info->fix.visual == FB_VISUAL_TRUECOLOR) {
465 u32 *palette;
466 u32 value;
467 /* place color in psuedopalette */
468 if (regno > 16)
469 return -EINVAL;
470 palette = (u32 *)info->pseudo_palette;
471 red >>= (16 - info->var.red.length);
472 green >>= (16 - info->var.green.length);
473 blue >>= (16 - info->var.blue.length);
474 value = (red << info->var.red.offset) |
475 (green << info->var.green.offset) |
476 (blue << info->var.blue.offset);
477 palette[regno] = value;
478 return 0;
479 }
480
481 pindex = regno;
482
483 if (fb->bits_per_pixel == 16) {
484 pindex = regno << 3;
485
486 if (fb->depth == 16 && regno > 63)
487 return -EINVAL;
488 if (fb->depth == 15 && regno > 31)
489 return -EINVAL;
490
491 if (fb->depth == 16) {
492 u16 r, g, b;
493 int i;
494 if (regno < 32) {
495 for (i = 0; i < 8; i++)
496 fb_helper->funcs->gamma_set(crtc, red,
497 green, blue, pindex + i);
498 }
499
500 fb_helper->funcs->gamma_get(crtc, &r,
501 &g, &b,
502 pindex >> 1);
503
504 for (i = 0; i < 4; i++)
505 fb_helper->funcs->gamma_set(crtc, r,
506 green, b,
507 (pindex >> 1) + i);
508 }
509 }
510
511 if (fb->depth != 16)
512 fb_helper->funcs->gamma_set(crtc, red, green, blue, pindex);
513 return 0;
514}
515
516int drm_fb_helper_setcmap(struct fb_cmap *cmap, struct fb_info *info)
517{
518 struct drm_fb_helper *fb_helper = info->par;
519 struct drm_device *dev = fb_helper->dev;
520 u16 *red, *green, *blue, *transp;
521 struct drm_crtc *crtc;
522 int i, rc = 0;
523 int start;
524
525 list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
526 struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private;
527 for (i = 0; i < fb_helper->crtc_count; i++) {
528 if (crtc->base.id == fb_helper->crtc_info[i].crtc_id)
529 break;
530 }
531 if (i == fb_helper->crtc_count)
532 continue;
533
534 red = cmap->red;
535 green = cmap->green;
536 blue = cmap->blue;
537 transp = cmap->transp;
538 start = cmap->start;
539
540 for (i = 0; i < cmap->len; i++) {
541 u16 hred, hgreen, hblue, htransp = 0xffff;
542
543 hred = *red++;
544 hgreen = *green++;
545 hblue = *blue++;
546
547 if (transp)
548 htransp = *transp++;
549
550 rc = setcolreg(crtc, hred, hgreen, hblue, start++, info);
551 if (rc)
552 return rc;
553 }
554 crtc_funcs->load_lut(crtc);
555 }
556 return rc;
557}
558EXPORT_SYMBOL(drm_fb_helper_setcmap);
559
262int drm_fb_helper_setcolreg(unsigned regno, 560int drm_fb_helper_setcolreg(unsigned regno,
263 unsigned red, 561 unsigned red,
264 unsigned green, 562 unsigned green,
@@ -270,10 +568,13 @@ int drm_fb_helper_setcolreg(unsigned regno,
270 struct drm_device *dev = fb_helper->dev; 568 struct drm_device *dev = fb_helper->dev;
271 struct drm_crtc *crtc; 569 struct drm_crtc *crtc;
272 int i; 570 int i;
571 int ret;
273 572
274 list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { 573 if (regno > 255)
275 struct drm_framebuffer *fb = fb_helper->fb; 574 return 1;
276 575
576 list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
577 struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private;
277 for (i = 0; i < fb_helper->crtc_count; i++) { 578 for (i = 0; i < fb_helper->crtc_count; i++) {
278 if (crtc->base.id == fb_helper->crtc_info[i].crtc_id) 579 if (crtc->base.id == fb_helper->crtc_info[i].crtc_id)
279 break; 580 break;
@@ -281,35 +582,11 @@ int drm_fb_helper_setcolreg(unsigned regno,
281 if (i == fb_helper->crtc_count) 582 if (i == fb_helper->crtc_count)
282 continue; 583 continue;
283 584
284 if (regno > 255) 585 ret = setcolreg(crtc, red, green, blue, regno, info);
285 return 1; 586 if (ret)
286 587 return ret;
287 if (fb->depth == 8) {
288 fb_helper->funcs->gamma_set(crtc, red, green, blue, regno);
289 return 0;
290 }
291 588
292 if (regno < 16) { 589 crtc_funcs->load_lut(crtc);
293 switch (fb->depth) {
294 case 15:
295 fb->pseudo_palette[regno] = ((red & 0xf800) >> 1) |
296 ((green & 0xf800) >> 6) |
297 ((blue & 0xf800) >> 11);
298 break;
299 case 16:
300 fb->pseudo_palette[regno] = (red & 0xf800) |
301 ((green & 0xfc00) >> 5) |
302 ((blue & 0xf800) >> 11);
303 break;
304 case 24:
305 case 32:
306 fb->pseudo_palette[regno] =
307 (((red >> 8) & 0xff) << info->var.red.offset) |
308 (((green >> 8) & 0xff) << info->var.green.offset) |
309 (((blue >> 8) & 0xff) << info->var.blue.offset);
310 break;
311 }
312 }
313 } 590 }
314 return 0; 591 return 0;
315} 592}
@@ -430,7 +707,7 @@ int drm_fb_helper_set_par(struct fb_info *info)
430 707
431 if (crtc->fb == fb_helper->crtc_info[i].mode_set.fb) { 708 if (crtc->fb == fb_helper->crtc_info[i].mode_set.fb) {
432 mutex_lock(&dev->mode_config.mutex); 709 mutex_lock(&dev->mode_config.mutex);
433 ret = crtc->funcs->set_config(&fb_helper->crtc_info->mode_set); 710 ret = crtc->funcs->set_config(&fb_helper->crtc_info[i].mode_set);
434 mutex_unlock(&dev->mode_config.mutex); 711 mutex_unlock(&dev->mode_config.mutex);
435 if (ret) 712 if (ret)
436 return ret; 713 return ret;
@@ -479,11 +756,14 @@ int drm_fb_helper_pan_display(struct fb_var_screeninfo *var,
479EXPORT_SYMBOL(drm_fb_helper_pan_display); 756EXPORT_SYMBOL(drm_fb_helper_pan_display);
480 757
481int drm_fb_helper_single_fb_probe(struct drm_device *dev, 758int drm_fb_helper_single_fb_probe(struct drm_device *dev,
759 int preferred_bpp,
482 int (*fb_create)(struct drm_device *dev, 760 int (*fb_create)(struct drm_device *dev,
483 uint32_t fb_width, 761 uint32_t fb_width,
484 uint32_t fb_height, 762 uint32_t fb_height,
485 uint32_t surface_width, 763 uint32_t surface_width,
486 uint32_t surface_height, 764 uint32_t surface_height,
765 uint32_t surface_depth,
766 uint32_t surface_bpp,
487 struct drm_framebuffer **fb_ptr)) 767 struct drm_framebuffer **fb_ptr))
488{ 768{
489 struct drm_crtc *crtc; 769 struct drm_crtc *crtc;
@@ -497,8 +777,48 @@ int drm_fb_helper_single_fb_probe(struct drm_device *dev,
497 struct drm_framebuffer *fb; 777 struct drm_framebuffer *fb;
498 struct drm_mode_set *modeset = NULL; 778 struct drm_mode_set *modeset = NULL;
499 struct drm_fb_helper *fb_helper; 779 struct drm_fb_helper *fb_helper;
780 uint32_t surface_depth = 24, surface_bpp = 32;
500 781
782 /* if driver picks 8 or 16 by default use that
783 for both depth/bpp */
784 if (preferred_bpp != surface_bpp) {
785 surface_depth = surface_bpp = preferred_bpp;
786 }
501 /* first up get a count of crtcs now in use and new min/maxes width/heights */ 787 /* first up get a count of crtcs now in use and new min/maxes width/heights */
788 list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
789 struct drm_fb_helper_connector *fb_help_conn = connector->fb_helper_private;
790
791 struct drm_fb_helper_cmdline_mode *cmdline_mode;
792
793 if (!fb_help_conn)
794 continue;
795
796 cmdline_mode = &fb_help_conn->cmdline_mode;
797
798 if (cmdline_mode->bpp_specified) {
799 switch (cmdline_mode->bpp) {
800 case 8:
801 surface_depth = surface_bpp = 8;
802 break;
803 case 15:
804 surface_depth = 15;
805 surface_bpp = 16;
806 break;
807 case 16:
808 surface_depth = surface_bpp = 16;
809 break;
810 case 24:
811 surface_depth = surface_bpp = 24;
812 break;
813 case 32:
814 surface_depth = 24;
815 surface_bpp = 32;
816 break;
817 }
818 break;
819 }
820 }
821
502 list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { 822 list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
503 if (drm_helper_crtc_in_use(crtc)) { 823 if (drm_helper_crtc_in_use(crtc)) {
504 if (crtc->desired_mode) { 824 if (crtc->desired_mode) {
@@ -527,7 +847,8 @@ int drm_fb_helper_single_fb_probe(struct drm_device *dev,
527 /* do we have an fb already? */ 847 /* do we have an fb already? */
528 if (list_empty(&dev->mode_config.fb_kernel_list)) { 848 if (list_empty(&dev->mode_config.fb_kernel_list)) {
529 ret = (*fb_create)(dev, fb_width, fb_height, surface_width, 849 ret = (*fb_create)(dev, fb_width, fb_height, surface_width,
530 surface_height, &fb); 850 surface_height, surface_depth, surface_bpp,
851 &fb);
531 if (ret) 852 if (ret)
532 return -EINVAL; 853 return -EINVAL;
533 new_fb = 1; 854 new_fb = 1;
@@ -618,10 +939,12 @@ void drm_fb_helper_free(struct drm_fb_helper *helper)
618} 939}
619EXPORT_SYMBOL(drm_fb_helper_free); 940EXPORT_SYMBOL(drm_fb_helper_free);
620 941
621void drm_fb_helper_fill_fix(struct fb_info *info, uint32_t pitch) 942void drm_fb_helper_fill_fix(struct fb_info *info, uint32_t pitch,
943 uint32_t depth)
622{ 944{
623 info->fix.type = FB_TYPE_PACKED_PIXELS; 945 info->fix.type = FB_TYPE_PACKED_PIXELS;
624 info->fix.visual = FB_VISUAL_TRUECOLOR; 946 info->fix.visual = depth == 8 ? FB_VISUAL_PSEUDOCOLOR :
947 FB_VISUAL_TRUECOLOR;
625 info->fix.type_aux = 0; 948 info->fix.type_aux = 0;
626 info->fix.xpanstep = 1; /* doing it in hw */ 949 info->fix.xpanstep = 1; /* doing it in hw */
627 info->fix.ypanstep = 1; /* doing it in hw */ 950 info->fix.ypanstep = 1; /* doing it in hw */