diff options
Diffstat (limited to 'drivers/gpu/drm/drm_fb_helper.c')
-rw-r--r-- | drivers/gpu/drm/drm_fb_helper.c | 391 |
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 | ||
41 | static LIST_HEAD(kernel_fb_helper_list); | 41 | static LIST_HEAD(kernel_fb_helper_list); |
42 | 42 | ||
43 | int 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 | EXPORT_SYMBOL(drm_fb_helper_add_connector); | ||
52 | |||
53 | static 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 | */ | ||
81 | static 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 | } | ||
176 | done: | ||
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 | |||
220 | int 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 | |||
43 | bool drm_fb_helper_force_kernel_mode(void) | 236 | bool 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 | } |
88 | EXPORT_SYMBOL(drm_fb_helper_restore); | 281 | EXPORT_SYMBOL(drm_fb_helper_restore); |
89 | 282 | ||
283 | #ifdef CONFIG_MAGIC_SYSRQ | ||
90 | static void drm_fb_helper_restore_work_fn(struct work_struct *ignored) | 284 | static 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 | ||
107 | static void drm_fb_helper_on(struct fb_info *info) | 302 | static void drm_fb_helper_on(struct fb_info *info) |
108 | { | 303 | { |
@@ -259,6 +454,109 @@ out_free: | |||
259 | } | 454 | } |
260 | EXPORT_SYMBOL(drm_fb_helper_init_crtc_count); | 455 | EXPORT_SYMBOL(drm_fb_helper_init_crtc_count); |
261 | 456 | ||
457 | static 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 | |||
516 | int 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 | } | ||
558 | EXPORT_SYMBOL(drm_fb_helper_setcmap); | ||
559 | |||
262 | int drm_fb_helper_setcolreg(unsigned regno, | 560 | int 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, | |||
479 | EXPORT_SYMBOL(drm_fb_helper_pan_display); | 756 | EXPORT_SYMBOL(drm_fb_helper_pan_display); |
480 | 757 | ||
481 | int drm_fb_helper_single_fb_probe(struct drm_device *dev, | 758 | int 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 | } |
619 | EXPORT_SYMBOL(drm_fb_helper_free); | 940 | EXPORT_SYMBOL(drm_fb_helper_free); |
620 | 941 | ||
621 | void drm_fb_helper_fill_fix(struct fb_info *info, uint32_t pitch) | 942 | void 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 */ |