diff options
Diffstat (limited to 'drivers/gpu/drm/drm_fb_helper.c')
-rw-r--r-- | drivers/gpu/drm/drm_fb_helper.c | 235 |
1 files changed, 234 insertions, 1 deletions
diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index 2c4671314884..819ddcbfcce5 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 | { |
@@ -484,6 +679,8 @@ int drm_fb_helper_single_fb_probe(struct drm_device *dev, | |||
484 | uint32_t fb_height, | 679 | uint32_t fb_height, |
485 | uint32_t surface_width, | 680 | uint32_t surface_width, |
486 | uint32_t surface_height, | 681 | uint32_t surface_height, |
682 | uint32_t surface_depth, | ||
683 | uint32_t surface_bpp, | ||
487 | struct drm_framebuffer **fb_ptr)) | 684 | struct drm_framebuffer **fb_ptr)) |
488 | { | 685 | { |
489 | struct drm_crtc *crtc; | 686 | struct drm_crtc *crtc; |
@@ -497,8 +694,43 @@ int drm_fb_helper_single_fb_probe(struct drm_device *dev, | |||
497 | struct drm_framebuffer *fb; | 694 | struct drm_framebuffer *fb; |
498 | struct drm_mode_set *modeset = NULL; | 695 | struct drm_mode_set *modeset = NULL; |
499 | struct drm_fb_helper *fb_helper; | 696 | struct drm_fb_helper *fb_helper; |
697 | uint32_t surface_depth = 24, surface_bpp = 32; | ||
500 | 698 | ||
501 | /* first up get a count of crtcs now in use and new min/maxes width/heights */ | 699 | /* first up get a count of crtcs now in use and new min/maxes width/heights */ |
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; | ||
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; | ||
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 | |||
502 | list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { | 734 | list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { |
503 | if (drm_helper_crtc_in_use(crtc)) { | 735 | if (drm_helper_crtc_in_use(crtc)) { |
504 | if (crtc->desired_mode) { | 736 | if (crtc->desired_mode) { |
@@ -527,7 +759,8 @@ int drm_fb_helper_single_fb_probe(struct drm_device *dev, | |||
527 | /* do we have an fb already? */ | 759 | /* do we have an fb already? */ |
528 | if (list_empty(&dev->mode_config.fb_kernel_list)) { | 760 | if (list_empty(&dev->mode_config.fb_kernel_list)) { |
529 | ret = (*fb_create)(dev, fb_width, fb_height, surface_width, | 761 | ret = (*fb_create)(dev, fb_width, fb_height, surface_width, |
530 | surface_height, &fb); | 762 | surface_height, surface_depth, surface_bpp, |
763 | &fb); | ||
531 | if (ret) | 764 | if (ret) |
532 | return -EINVAL; | 765 | return -EINVAL; |
533 | new_fb = 1; | 766 | new_fb = 1; |