diff options
-rw-r--r-- | drivers/gpu/drm/drm_crtc.c | 1 | ||||
-rw-r--r-- | drivers/gpu/drm/drm_crtc_helper.c | 79 | ||||
-rw-r--r-- | drivers/gpu/drm/drm_edid.c | 6 | ||||
-rw-r--r-- | drivers/gpu/drm/drm_fb_helper.c | 224 | ||||
-rw-r--r-- | drivers/gpu/drm/drm_modes.c | 3 | ||||
-rw-r--r-- | drivers/gpu/drm/i915/intel_fb.c | 5 | ||||
-rw-r--r-- | drivers/gpu/drm/radeon/radeon_connectors.c | 25 | ||||
-rw-r--r-- | drivers/gpu/drm/radeon/radeon_fb.c | 26 | ||||
-rw-r--r-- | include/drm/drm_crtc.h | 14 | ||||
-rw-r--r-- | include/drm/drm_fb_helper.h | 24 |
10 files changed, 386 insertions, 21 deletions
diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index ba728ad77f2a..8e7b0ebece0c 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c | |||
@@ -482,6 +482,7 @@ void drm_connector_cleanup(struct drm_connector *connector) | |||
482 | list_for_each_entry_safe(mode, t, &connector->user_modes, head) | 482 | list_for_each_entry_safe(mode, t, &connector->user_modes, head) |
483 | drm_mode_remove(connector, mode); | 483 | drm_mode_remove(connector, mode); |
484 | 484 | ||
485 | kfree(connector->fb_helper_private); | ||
485 | mutex_lock(&dev->mode_config.mutex); | 486 | mutex_lock(&dev->mode_config.mutex); |
486 | drm_mode_object_put(dev, &connector->base); | 487 | drm_mode_object_put(dev, &connector->base); |
487 | list_del(&connector->head); | 488 | list_del(&connector->head); |
diff --git a/drivers/gpu/drm/drm_crtc_helper.c b/drivers/gpu/drm/drm_crtc_helper.c index fe8697447f32..82fd6e82450f 100644 --- a/drivers/gpu/drm/drm_crtc_helper.c +++ b/drivers/gpu/drm/drm_crtc_helper.c | |||
@@ -32,6 +32,7 @@ | |||
32 | #include "drmP.h" | 32 | #include "drmP.h" |
33 | #include "drm_crtc.h" | 33 | #include "drm_crtc.h" |
34 | #include "drm_crtc_helper.h" | 34 | #include "drm_crtc_helper.h" |
35 | #include "drm_fb_helper.h" | ||
35 | 36 | ||
36 | static void drm_mode_validate_flag(struct drm_connector *connector, | 37 | static void drm_mode_validate_flag(struct drm_connector *connector, |
37 | int flags) | 38 | int flags) |
@@ -90,7 +91,15 @@ int drm_helper_probe_single_connector_modes(struct drm_connector *connector, | |||
90 | list_for_each_entry_safe(mode, t, &connector->modes, head) | 91 | list_for_each_entry_safe(mode, t, &connector->modes, head) |
91 | mode->status = MODE_UNVERIFIED; | 92 | mode->status = MODE_UNVERIFIED; |
92 | 93 | ||
93 | connector->status = connector->funcs->detect(connector); | 94 | if (connector->force) { |
95 | if (connector->force == DRM_FORCE_ON) | ||
96 | connector->status = connector_status_connected; | ||
97 | else | ||
98 | connector->status = connector_status_disconnected; | ||
99 | if (connector->funcs->force) | ||
100 | connector->funcs->force(connector); | ||
101 | } else | ||
102 | connector->status = connector->funcs->detect(connector); | ||
94 | 103 | ||
95 | if (connector->status == connector_status_disconnected) { | 104 | if (connector->status == connector_status_disconnected) { |
96 | DRM_DEBUG_KMS("%s is disconnected\n", | 105 | DRM_DEBUG_KMS("%s is disconnected\n", |
@@ -267,6 +276,56 @@ static struct drm_display_mode *drm_has_preferred_mode(struct drm_connector *con | |||
267 | return NULL; | 276 | return NULL; |
268 | } | 277 | } |
269 | 278 | ||
279 | static bool drm_has_cmdline_mode(struct drm_connector *connector) | ||
280 | { | ||
281 | struct drm_fb_helper_connector *fb_help_conn = connector->fb_helper_private; | ||
282 | struct drm_fb_helper_cmdline_mode *cmdline_mode = &fb_help_conn->cmdline_mode; | ||
283 | return cmdline_mode->specified; | ||
284 | } | ||
285 | |||
286 | static struct drm_display_mode *drm_pick_cmdline_mode(struct drm_connector *connector, int width, int height) | ||
287 | { | ||
288 | struct drm_fb_helper_connector *fb_help_conn = connector->fb_helper_private; | ||
289 | struct drm_fb_helper_cmdline_mode *cmdline_mode = &fb_help_conn->cmdline_mode; | ||
290 | struct drm_display_mode *mode = NULL; | ||
291 | |||
292 | if (cmdline_mode->specified == false) | ||
293 | return mode; | ||
294 | |||
295 | /* attempt to find a matching mode in the list of modes | ||
296 | * we have gotten so far, if not add a CVT mode that conforms | ||
297 | */ | ||
298 | if (cmdline_mode->rb || cmdline_mode->margins) | ||
299 | goto create_mode; | ||
300 | |||
301 | list_for_each_entry(mode, &connector->modes, head) { | ||
302 | /* check width/height */ | ||
303 | if (mode->hdisplay != cmdline_mode->xres || | ||
304 | mode->vdisplay != cmdline_mode->yres) | ||
305 | continue; | ||
306 | |||
307 | if (cmdline_mode->refresh_specified) { | ||
308 | if (mode->vrefresh != cmdline_mode->refresh) | ||
309 | continue; | ||
310 | } | ||
311 | |||
312 | if (cmdline_mode->interlace) { | ||
313 | if (!(mode->flags & DRM_MODE_FLAG_INTERLACE)) | ||
314 | continue; | ||
315 | } | ||
316 | return mode; | ||
317 | } | ||
318 | |||
319 | create_mode: | ||
320 | mode = drm_cvt_mode(connector->dev, cmdline_mode->xres, | ||
321 | cmdline_mode->yres, | ||
322 | cmdline_mode->refresh_specified ? cmdline_mode->refresh : 60, | ||
323 | cmdline_mode->rb, cmdline_mode->interlace, | ||
324 | cmdline_mode->margins); | ||
325 | list_add(&mode->head, &connector->modes); | ||
326 | return mode; | ||
327 | } | ||
328 | |||
270 | static bool drm_connector_enabled(struct drm_connector *connector, bool strict) | 329 | static bool drm_connector_enabled(struct drm_connector *connector, bool strict) |
271 | { | 330 | { |
272 | bool enable; | 331 | bool enable; |
@@ -317,10 +376,16 @@ static bool drm_target_preferred(struct drm_device *dev, | |||
317 | continue; | 376 | continue; |
318 | } | 377 | } |
319 | 378 | ||
320 | DRM_DEBUG_KMS("looking for preferred mode on connector %d\n", | 379 | DRM_DEBUG_KMS("looking for cmdline mode on connector %d\n", |
321 | connector->base.id); | 380 | connector->base.id); |
322 | 381 | ||
323 | modes[i] = drm_has_preferred_mode(connector, width, height); | 382 | /* got for command line mode first */ |
383 | modes[i] = drm_pick_cmdline_mode(connector, width, height); | ||
384 | if (!modes[i]) { | ||
385 | DRM_DEBUG_KMS("looking for preferred mode on connector %d\n", | ||
386 | connector->base.id); | ||
387 | modes[i] = drm_has_preferred_mode(connector, width, height); | ||
388 | } | ||
324 | /* No preferred modes, pick one off the list */ | 389 | /* No preferred modes, pick one off the list */ |
325 | if (!modes[i] && !list_empty(&connector->modes)) { | 390 | if (!modes[i] && !list_empty(&connector->modes)) { |
326 | list_for_each_entry(modes[i], &connector->modes, head) | 391 | list_for_each_entry(modes[i], &connector->modes, head) |
@@ -369,6 +434,8 @@ static int drm_pick_crtcs(struct drm_device *dev, | |||
369 | my_score = 1; | 434 | my_score = 1; |
370 | if (connector->status == connector_status_connected) | 435 | if (connector->status == connector_status_connected) |
371 | my_score++; | 436 | my_score++; |
437 | if (drm_has_cmdline_mode(connector)) | ||
438 | my_score++; | ||
372 | if (drm_has_preferred_mode(connector, width, height)) | 439 | if (drm_has_preferred_mode(connector, width, height)) |
373 | my_score++; | 440 | my_score++; |
374 | 441 | ||
@@ -943,6 +1010,8 @@ bool drm_helper_initial_config(struct drm_device *dev) | |||
943 | { | 1010 | { |
944 | int count = 0; | 1011 | int count = 0; |
945 | 1012 | ||
1013 | drm_fb_helper_parse_command_line(dev); | ||
1014 | |||
946 | count = drm_helper_probe_connector_modes(dev, | 1015 | count = drm_helper_probe_connector_modes(dev, |
947 | dev->mode_config.max_width, | 1016 | dev->mode_config.max_width, |
948 | dev->mode_config.max_height); | 1017 | dev->mode_config.max_height); |
@@ -950,7 +1019,7 @@ bool drm_helper_initial_config(struct drm_device *dev) | |||
950 | /* | 1019 | /* |
951 | * we shouldn't end up with no modes here. | 1020 | * we shouldn't end up with no modes here. |
952 | */ | 1021 | */ |
953 | WARN(!count, "Connected connector with 0 modes\n"); | 1022 | WARN(!count, "No connectors reported connected with modes\n"); |
954 | 1023 | ||
955 | drm_setup_crtcs(dev); | 1024 | drm_setup_crtcs(dev); |
956 | 1025 | ||
diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index 9888c2076b2e..3c0d2b3aed76 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c | |||
@@ -560,7 +560,8 @@ struct drm_display_mode *drm_mode_std(struct drm_device *dev, | |||
560 | vsize = (hsize * 9) / 16; | 560 | vsize = (hsize * 9) / 16; |
561 | /* HDTV hack */ | 561 | /* HDTV hack */ |
562 | if (hsize == 1360 && vsize == 765 && vrefresh_rate == 60) { | 562 | if (hsize == 1360 && vsize == 765 && vrefresh_rate == 60) { |
563 | mode = drm_cvt_mode(dev, hsize, vsize, vrefresh_rate, 0, 0); | 563 | mode = drm_cvt_mode(dev, hsize, vsize, vrefresh_rate, 0, 0, |
564 | false); | ||
564 | mode->hdisplay = 1366; | 565 | mode->hdisplay = 1366; |
565 | mode->vsync_start = mode->vsync_start - 1; | 566 | mode->vsync_start = mode->vsync_start - 1; |
566 | mode->vsync_end = mode->vsync_end - 1; | 567 | mode->vsync_end = mode->vsync_end - 1; |
@@ -579,7 +580,8 @@ struct drm_display_mode *drm_mode_std(struct drm_device *dev, | |||
579 | mode = drm_gtf_mode(dev, hsize, vsize, vrefresh_rate, 0, 0); | 580 | mode = drm_gtf_mode(dev, hsize, vsize, vrefresh_rate, 0, 0); |
580 | break; | 581 | break; |
581 | case LEVEL_CVT: | 582 | case LEVEL_CVT: |
582 | mode = drm_cvt_mode(dev, hsize, vsize, vrefresh_rate, 0, 0); | 583 | mode = drm_cvt_mode(dev, hsize, vsize, vrefresh_rate, 0, 0, |
584 | false); | ||
583 | break; | 585 | break; |
584 | } | 586 | } |
585 | return mode; | 587 | return mode; |
diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index 2c4671314884..2537d2e81849 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c | |||
@@ -40,6 +40,196 @@ 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 | } | ||
52 | EXPORT_SYMBOL(drm_fb_helper_add_connector); | ||
53 | |||
54 | static 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 | */ | ||
82 | static 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 | } | ||
173 | done: | ||
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 | |||
217 | int 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 | |||
43 | bool drm_fb_helper_force_kernel_mode(void) | 233 | bool drm_fb_helper_force_kernel_mode(void) |
44 | { | 234 | { |
45 | int i = 0; | 235 | int i = 0; |
@@ -484,6 +674,8 @@ int drm_fb_helper_single_fb_probe(struct drm_device *dev, | |||
484 | uint32_t fb_height, | 674 | uint32_t fb_height, |
485 | uint32_t surface_width, | 675 | uint32_t surface_width, |
486 | uint32_t surface_height, | 676 | uint32_t surface_height, |
677 | uint32_t surface_depth, | ||
678 | uint32_t surface_bpp, | ||
487 | struct drm_framebuffer **fb_ptr)) | 679 | struct drm_framebuffer **fb_ptr)) |
488 | { | 680 | { |
489 | struct drm_crtc *crtc; | 681 | struct drm_crtc *crtc; |
@@ -497,8 +689,37 @@ int drm_fb_helper_single_fb_probe(struct drm_device *dev, | |||
497 | struct drm_framebuffer *fb; | 689 | struct drm_framebuffer *fb; |
498 | struct drm_mode_set *modeset = NULL; | 690 | struct drm_mode_set *modeset = NULL; |
499 | struct drm_fb_helper *fb_helper; | 691 | struct drm_fb_helper *fb_helper; |
692 | uint32_t surface_depth = 24, surface_bpp = 32; | ||
500 | 693 | ||
501 | /* first up get a count of crtcs now in use and new min/maxes width/heights */ | 694 | /* first up get a count of crtcs now in use and new min/maxes width/heights */ |
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 | |||
502 | list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { | 723 | list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { |
503 | if (drm_helper_crtc_in_use(crtc)) { | 724 | if (drm_helper_crtc_in_use(crtc)) { |
504 | if (crtc->desired_mode) { | 725 | if (crtc->desired_mode) { |
@@ -527,7 +748,8 @@ int drm_fb_helper_single_fb_probe(struct drm_device *dev, | |||
527 | /* do we have an fb already? */ | 748 | /* do we have an fb already? */ |
528 | if (list_empty(&dev->mode_config.fb_kernel_list)) { | 749 | if (list_empty(&dev->mode_config.fb_kernel_list)) { |
529 | ret = (*fb_create)(dev, fb_width, fb_height, surface_width, | 750 | ret = (*fb_create)(dev, fb_width, fb_height, surface_width, |
530 | surface_height, &fb); | 751 | surface_height, surface_depth, surface_bpp, |
752 | &fb); | ||
531 | if (ret) | 753 | if (ret) |
532 | return -EINVAL; | 754 | return -EINVAL; |
533 | new_fb = 1; | 755 | new_fb = 1; |
diff --git a/drivers/gpu/drm/drm_modes.c b/drivers/gpu/drm/drm_modes.c index 49404ce1666e..51f677215f1d 100644 --- a/drivers/gpu/drm/drm_modes.c +++ b/drivers/gpu/drm/drm_modes.c | |||
@@ -88,7 +88,7 @@ EXPORT_SYMBOL(drm_mode_debug_printmodeline); | |||
88 | #define HV_FACTOR 1000 | 88 | #define HV_FACTOR 1000 |
89 | struct drm_display_mode *drm_cvt_mode(struct drm_device *dev, int hdisplay, | 89 | struct drm_display_mode *drm_cvt_mode(struct drm_device *dev, int hdisplay, |
90 | int vdisplay, int vrefresh, | 90 | int vdisplay, int vrefresh, |
91 | bool reduced, bool interlaced) | 91 | bool reduced, bool interlaced, bool margins) |
92 | { | 92 | { |
93 | /* 1) top/bottom margin size (% of height) - default: 1.8, */ | 93 | /* 1) top/bottom margin size (% of height) - default: 1.8, */ |
94 | #define CVT_MARGIN_PERCENTAGE 18 | 94 | #define CVT_MARGIN_PERCENTAGE 18 |
@@ -101,7 +101,6 @@ struct drm_display_mode *drm_cvt_mode(struct drm_device *dev, int hdisplay, | |||
101 | /* Pixel Clock step (kHz) */ | 101 | /* Pixel Clock step (kHz) */ |
102 | #define CVT_CLOCK_STEP 250 | 102 | #define CVT_CLOCK_STEP 250 |
103 | struct drm_display_mode *drm_mode; | 103 | struct drm_display_mode *drm_mode; |
104 | bool margins = false; | ||
105 | unsigned int vfieldrate, hperiod; | 104 | unsigned int vfieldrate, hperiod; |
106 | int hdisplay_rnd, hmargin, vdisplay_rnd, vmargin, vsync; | 105 | int hdisplay_rnd, hmargin, vdisplay_rnd, vmargin, vsync; |
107 | int interlace; | 106 | int interlace; |
diff --git a/drivers/gpu/drm/i915/intel_fb.c b/drivers/gpu/drm/i915/intel_fb.c index 7ba4a232a97f..e85d7e9eed7d 100644 --- a/drivers/gpu/drm/i915/intel_fb.c +++ b/drivers/gpu/drm/i915/intel_fb.c | |||
@@ -110,6 +110,7 @@ EXPORT_SYMBOL(intelfb_resize); | |||
110 | static int intelfb_create(struct drm_device *dev, uint32_t fb_width, | 110 | static int intelfb_create(struct drm_device *dev, uint32_t fb_width, |
111 | uint32_t fb_height, uint32_t surface_width, | 111 | uint32_t fb_height, uint32_t surface_width, |
112 | uint32_t surface_height, | 112 | uint32_t surface_height, |
113 | uint32_t surface_depth, uint32_t surface_bpp, | ||
113 | struct drm_framebuffer **fb_p) | 114 | struct drm_framebuffer **fb_p) |
114 | { | 115 | { |
115 | struct fb_info *info; | 116 | struct fb_info *info; |
@@ -125,9 +126,9 @@ static int intelfb_create(struct drm_device *dev, uint32_t fb_width, | |||
125 | mode_cmd.width = surface_width; | 126 | mode_cmd.width = surface_width; |
126 | mode_cmd.height = surface_height; | 127 | mode_cmd.height = surface_height; |
127 | 128 | ||
128 | mode_cmd.bpp = 32; | 129 | mode_cmd.bpp = surface_bpp; |
129 | mode_cmd.pitch = ALIGN(mode_cmd.width * ((mode_cmd.bpp + 1) / 8), 64); | 130 | mode_cmd.pitch = ALIGN(mode_cmd.width * ((mode_cmd.bpp + 1) / 8), 64); |
130 | mode_cmd.depth = 24; | 131 | mode_cmd.depth = surface_depth; |
131 | 132 | ||
132 | size = mode_cmd.pitch * mode_cmd.height; | 133 | size = mode_cmd.pitch * mode_cmd.height; |
133 | size = ALIGN(size, PAGE_SIZE); | 134 | size = ALIGN(size, PAGE_SIZE); |
diff --git a/drivers/gpu/drm/radeon/radeon_connectors.c b/drivers/gpu/drm/radeon/radeon_connectors.c index f8cb8b4e2b17..db155d5e60ce 100644 --- a/drivers/gpu/drm/radeon/radeon_connectors.c +++ b/drivers/gpu/drm/radeon/radeon_connectors.c | |||
@@ -26,6 +26,7 @@ | |||
26 | #include "drmP.h" | 26 | #include "drmP.h" |
27 | #include "drm_edid.h" | 27 | #include "drm_edid.h" |
28 | #include "drm_crtc_helper.h" | 28 | #include "drm_crtc_helper.h" |
29 | #include "drm_fb_helper.h" | ||
29 | #include "radeon_drm.h" | 30 | #include "radeon_drm.h" |
30 | #include "radeon.h" | 31 | #include "radeon.h" |
31 | #include "atom.h" | 32 | #include "atom.h" |
@@ -245,7 +246,7 @@ static void radeon_add_common_modes(struct drm_encoder *encoder, struct drm_conn | |||
245 | if (common_modes[i].w < 320 || common_modes[i].h < 200) | 246 | if (common_modes[i].w < 320 || common_modes[i].h < 200) |
246 | continue; | 247 | continue; |
247 | 248 | ||
248 | mode = drm_cvt_mode(dev, common_modes[i].w, common_modes[i].h, 60, false, false); | 249 | mode = drm_cvt_mode(dev, common_modes[i].w, common_modes[i].h, 60, false, false, false); |
249 | drm_mode_probed_add(connector, mode); | 250 | drm_mode_probed_add(connector, mode); |
250 | } | 251 | } |
251 | } | 252 | } |
@@ -559,7 +560,7 @@ static int radeon_tv_get_modes(struct drm_connector *connector) | |||
559 | radeon_add_common_modes(encoder, connector); | 560 | radeon_add_common_modes(encoder, connector); |
560 | else { | 561 | else { |
561 | /* only 800x600 is supported right now on pre-avivo chips */ | 562 | /* only 800x600 is supported right now on pre-avivo chips */ |
562 | tv_mode = drm_cvt_mode(dev, 800, 600, 60, false, false); | 563 | tv_mode = drm_cvt_mode(dev, 800, 600, 60, false, false, false); |
563 | tv_mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED; | 564 | tv_mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED; |
564 | drm_mode_probed_add(connector, tv_mode); | 565 | drm_mode_probed_add(connector, tv_mode); |
565 | } | 566 | } |
@@ -743,6 +744,15 @@ struct drm_encoder *radeon_dvi_encoder(struct drm_connector *connector) | |||
743 | return NULL; | 744 | return NULL; |
744 | } | 745 | } |
745 | 746 | ||
747 | static void radeon_dvi_force(struct drm_connector *connector) | ||
748 | { | ||
749 | struct radeon_connector *radeon_connector = to_radeon_connector(connector); | ||
750 | if (connector->force == DRM_FORCE_ON) | ||
751 | radeon_connector->use_digital = false; | ||
752 | if (connector->force == DRM_FORCE_ON_DIGITAL) | ||
753 | radeon_connector->use_digital = true; | ||
754 | } | ||
755 | |||
746 | struct drm_connector_helper_funcs radeon_dvi_connector_helper_funcs = { | 756 | struct drm_connector_helper_funcs radeon_dvi_connector_helper_funcs = { |
747 | .get_modes = radeon_dvi_get_modes, | 757 | .get_modes = radeon_dvi_get_modes, |
748 | .mode_valid = radeon_vga_mode_valid, | 758 | .mode_valid = radeon_vga_mode_valid, |
@@ -755,6 +765,7 @@ struct drm_connector_funcs radeon_dvi_connector_funcs = { | |||
755 | .fill_modes = drm_helper_probe_single_connector_modes, | 765 | .fill_modes = drm_helper_probe_single_connector_modes, |
756 | .set_property = radeon_connector_set_property, | 766 | .set_property = radeon_connector_set_property, |
757 | .destroy = radeon_connector_destroy, | 767 | .destroy = radeon_connector_destroy, |
768 | .force = radeon_dvi_force, | ||
758 | }; | 769 | }; |
759 | 770 | ||
760 | void | 771 | void |
@@ -771,6 +782,7 @@ radeon_add_atom_connector(struct drm_device *dev, | |||
771 | struct radeon_connector *radeon_connector; | 782 | struct radeon_connector *radeon_connector; |
772 | struct radeon_connector_atom_dig *radeon_dig_connector; | 783 | struct radeon_connector_atom_dig *radeon_dig_connector; |
773 | uint32_t subpixel_order = SubPixelNone; | 784 | uint32_t subpixel_order = SubPixelNone; |
785 | int ret; | ||
774 | 786 | ||
775 | /* fixme - tv/cv/din */ | 787 | /* fixme - tv/cv/din */ |
776 | if (connector_type == DRM_MODE_CONNECTOR_Unknown) | 788 | if (connector_type == DRM_MODE_CONNECTOR_Unknown) |
@@ -914,6 +926,10 @@ radeon_add_atom_connector(struct drm_device *dev, | |||
914 | break; | 926 | break; |
915 | } | 927 | } |
916 | 928 | ||
929 | ret = drm_fb_helper_add_connector(connector); | ||
930 | if (ret) | ||
931 | goto failed; | ||
932 | |||
917 | connector->display_info.subpixel_order = subpixel_order; | 933 | connector->display_info.subpixel_order = subpixel_order; |
918 | drm_sysfs_connector_add(connector); | 934 | drm_sysfs_connector_add(connector); |
919 | return; | 935 | return; |
@@ -936,6 +952,7 @@ radeon_add_legacy_connector(struct drm_device *dev, | |||
936 | struct drm_connector *connector; | 952 | struct drm_connector *connector; |
937 | struct radeon_connector *radeon_connector; | 953 | struct radeon_connector *radeon_connector; |
938 | uint32_t subpixel_order = SubPixelNone; | 954 | uint32_t subpixel_order = SubPixelNone; |
955 | int ret; | ||
939 | 956 | ||
940 | /* fixme - tv/cv/din */ | 957 | /* fixme - tv/cv/din */ |
941 | if (connector_type == DRM_MODE_CONNECTOR_Unknown) | 958 | if (connector_type == DRM_MODE_CONNECTOR_Unknown) |
@@ -1027,6 +1044,10 @@ radeon_add_legacy_connector(struct drm_device *dev, | |||
1027 | break; | 1044 | break; |
1028 | } | 1045 | } |
1029 | 1046 | ||
1047 | ret = drm_fb_helper_add_connector(connector); | ||
1048 | if (ret) | ||
1049 | goto failed; | ||
1050 | |||
1030 | connector->display_info.subpixel_order = subpixel_order; | 1051 | connector->display_info.subpixel_order = subpixel_order; |
1031 | drm_sysfs_connector_add(connector); | 1052 | drm_sysfs_connector_add(connector); |
1032 | return; | 1053 | return; |
diff --git a/drivers/gpu/drm/radeon/radeon_fb.c b/drivers/gpu/drm/radeon/radeon_fb.c index 944e4fa78db5..1ba704eedefb 100644 --- a/drivers/gpu/drm/radeon/radeon_fb.c +++ b/drivers/gpu/drm/radeon/radeon_fb.c | |||
@@ -128,6 +128,7 @@ static struct drm_fb_helper_funcs radeon_fb_helper_funcs = { | |||
128 | int radeonfb_create(struct drm_device *dev, | 128 | int radeonfb_create(struct drm_device *dev, |
129 | uint32_t fb_width, uint32_t fb_height, | 129 | uint32_t fb_width, uint32_t fb_height, |
130 | uint32_t surface_width, uint32_t surface_height, | 130 | uint32_t surface_width, uint32_t surface_height, |
131 | uint32_t surface_depth, uint32_t surface_bpp, | ||
131 | struct drm_framebuffer **fb_p) | 132 | struct drm_framebuffer **fb_p) |
132 | { | 133 | { |
133 | struct radeon_device *rdev = dev->dev_private; | 134 | struct radeon_device *rdev = dev->dev_private; |
@@ -148,10 +149,10 @@ int radeonfb_create(struct drm_device *dev, | |||
148 | 149 | ||
149 | mode_cmd.width = surface_width; | 150 | mode_cmd.width = surface_width; |
150 | mode_cmd.height = surface_height; | 151 | mode_cmd.height = surface_height; |
151 | mode_cmd.bpp = 32; | 152 | mode_cmd.bpp = surface_bpp; |
152 | /* need to align pitch with crtc limits */ | 153 | /* need to align pitch with crtc limits */ |
153 | mode_cmd.pitch = radeon_align_pitch(rdev, mode_cmd.width, mode_cmd.bpp, fb_tiled) * ((mode_cmd.bpp + 1) / 8); | 154 | mode_cmd.pitch = radeon_align_pitch(rdev, mode_cmd.width, mode_cmd.bpp, fb_tiled) * ((mode_cmd.bpp + 1) / 8); |
154 | mode_cmd.depth = 24; | 155 | mode_cmd.depth = surface_depth; |
155 | 156 | ||
156 | size = mode_cmd.pitch * mode_cmd.height; | 157 | size = mode_cmd.pitch * mode_cmd.height; |
157 | aligned_size = ALIGN(size, PAGE_SIZE); | 158 | aligned_size = ALIGN(size, PAGE_SIZE); |
@@ -290,13 +291,26 @@ out: | |||
290 | return ret; | 291 | return ret; |
291 | } | 292 | } |
292 | 293 | ||
294 | static char *mode_option; | ||
295 | int radeon_parse_options(char *options) | ||
296 | { | ||
297 | char *this_opt; | ||
298 | |||
299 | if (!options || !*options) | ||
300 | return 0; | ||
301 | |||
302 | while ((this_opt = strsep(&options, ",")) != NULL) { | ||
303 | if (!*this_opt) | ||
304 | continue; | ||
305 | mode_option = this_opt; | ||
306 | } | ||
307 | return 0; | ||
308 | } | ||
309 | |||
293 | int radeonfb_probe(struct drm_device *dev) | 310 | int radeonfb_probe(struct drm_device *dev) |
294 | { | 311 | { |
295 | int ret; | 312 | return drm_fb_helper_single_fb_probe(dev, &radeonfb_create); |
296 | ret = drm_fb_helper_single_fb_probe(dev, &radeonfb_create); | ||
297 | return ret; | ||
298 | } | 313 | } |
299 | EXPORT_SYMBOL(radeonfb_probe); | ||
300 | 314 | ||
301 | int radeonfb_remove(struct drm_device *dev, struct drm_framebuffer *fb) | 315 | int radeonfb_remove(struct drm_device *dev, struct drm_framebuffer *fb) |
302 | { | 316 | { |
diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index ae1e9e166959..b69347b8904f 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h | |||
@@ -387,6 +387,7 @@ struct drm_crtc { | |||
387 | * @get_modes: get mode list for this connector | 387 | * @get_modes: get mode list for this connector |
388 | * @set_property: property for this connector may need update | 388 | * @set_property: property for this connector may need update |
389 | * @destroy: make object go away | 389 | * @destroy: make object go away |
390 | * @force: notify the driver the connector is forced on | ||
390 | * | 391 | * |
391 | * Each CRTC may have one or more connectors attached to it. The functions | 392 | * Each CRTC may have one or more connectors attached to it. The functions |
392 | * below allow the core DRM code to control connectors, enumerate available modes, | 393 | * below allow the core DRM code to control connectors, enumerate available modes, |
@@ -401,6 +402,7 @@ struct drm_connector_funcs { | |||
401 | int (*set_property)(struct drm_connector *connector, struct drm_property *property, | 402 | int (*set_property)(struct drm_connector *connector, struct drm_property *property, |
402 | uint64_t val); | 403 | uint64_t val); |
403 | void (*destroy)(struct drm_connector *connector); | 404 | void (*destroy)(struct drm_connector *connector); |
405 | void (*force)(struct drm_connector *connector); | ||
404 | }; | 406 | }; |
405 | 407 | ||
406 | struct drm_encoder_funcs { | 408 | struct drm_encoder_funcs { |
@@ -429,6 +431,13 @@ struct drm_encoder { | |||
429 | void *helper_private; | 431 | void *helper_private; |
430 | }; | 432 | }; |
431 | 433 | ||
434 | enum drm_connector_force { | ||
435 | DRM_FORCE_UNSPECIFIED, | ||
436 | DRM_FORCE_OFF, | ||
437 | DRM_FORCE_ON, /* force on analog part normally */ | ||
438 | DRM_FORCE_ON_DIGITAL, /* for DVI-I use digital connector */ | ||
439 | }; | ||
440 | |||
432 | /** | 441 | /** |
433 | * drm_connector - central DRM connector control structure | 442 | * drm_connector - central DRM connector control structure |
434 | * @crtc: CRTC this connector is currently connected to, NULL if none | 443 | * @crtc: CRTC this connector is currently connected to, NULL if none |
@@ -478,9 +487,12 @@ struct drm_connector { | |||
478 | 487 | ||
479 | void *helper_private; | 488 | void *helper_private; |
480 | 489 | ||
490 | /* forced on connector */ | ||
491 | enum drm_connector_force force; | ||
481 | uint32_t encoder_ids[DRM_CONNECTOR_MAX_ENCODER]; | 492 | uint32_t encoder_ids[DRM_CONNECTOR_MAX_ENCODER]; |
482 | uint32_t force_encoder_id; | 493 | uint32_t force_encoder_id; |
483 | struct drm_encoder *encoder; /* currently active encoder */ | 494 | struct drm_encoder *encoder; /* currently active encoder */ |
495 | void *fb_helper_private; | ||
484 | }; | 496 | }; |
485 | 497 | ||
486 | /** | 498 | /** |
@@ -746,7 +758,7 @@ extern int drm_mode_gamma_set_ioctl(struct drm_device *dev, | |||
746 | extern bool drm_detect_hdmi_monitor(struct edid *edid); | 758 | extern bool drm_detect_hdmi_monitor(struct edid *edid); |
747 | extern struct drm_display_mode *drm_cvt_mode(struct drm_device *dev, | 759 | extern struct drm_display_mode *drm_cvt_mode(struct drm_device *dev, |
748 | int hdisplay, int vdisplay, int vrefresh, | 760 | int hdisplay, int vdisplay, int vrefresh, |
749 | bool reduced, bool interlaced); | 761 | bool reduced, bool interlaced, bool margins); |
750 | extern struct drm_display_mode *drm_gtf_mode(struct drm_device *dev, | 762 | extern struct drm_display_mode *drm_gtf_mode(struct drm_device *dev, |
751 | int hdisplay, int vdisplay, int vrefresh, | 763 | int hdisplay, int vdisplay, int vrefresh, |
752 | bool interlaced, int margins); | 764 | bool interlaced, int margins); |
diff --git a/include/drm/drm_fb_helper.h b/include/drm/drm_fb_helper.h index 88fffbdfa26f..4aa5740ce59f 100644 --- a/include/drm/drm_fb_helper.h +++ b/include/drm/drm_fb_helper.h | |||
@@ -35,11 +35,30 @@ struct drm_fb_helper_crtc { | |||
35 | struct drm_mode_set mode_set; | 35 | struct drm_mode_set mode_set; |
36 | }; | 36 | }; |
37 | 37 | ||
38 | |||
38 | struct drm_fb_helper_funcs { | 39 | struct drm_fb_helper_funcs { |
39 | void (*gamma_set)(struct drm_crtc *crtc, u16 red, u16 green, | 40 | void (*gamma_set)(struct drm_crtc *crtc, u16 red, u16 green, |
40 | u16 blue, int regno); | 41 | u16 blue, int regno); |
41 | }; | 42 | }; |
42 | 43 | ||
44 | /* mode specified on the command line */ | ||
45 | struct drm_fb_helper_cmdline_mode { | ||
46 | bool specified; | ||
47 | bool refresh_specified; | ||
48 | bool bpp_specified; | ||
49 | int xres, yres; | ||
50 | int bpp; | ||
51 | int refresh; | ||
52 | bool rb; | ||
53 | bool interlace; | ||
54 | bool cvt; | ||
55 | bool margins; | ||
56 | }; | ||
57 | |||
58 | struct drm_fb_helper_connector { | ||
59 | struct drm_fb_helper_cmdline_mode cmdline_mode; | ||
60 | }; | ||
61 | |||
43 | struct drm_fb_helper { | 62 | struct drm_fb_helper { |
44 | struct drm_framebuffer *fb; | 63 | struct drm_framebuffer *fb; |
45 | struct drm_device *dev; | 64 | struct drm_device *dev; |
@@ -57,6 +76,8 @@ int drm_fb_helper_single_fb_probe(struct drm_device *dev, | |||
57 | uint32_t fb_height, | 76 | uint32_t fb_height, |
58 | uint32_t surface_width, | 77 | uint32_t surface_width, |
59 | uint32_t surface_height, | 78 | uint32_t surface_height, |
79 | uint32_t surface_depth, | ||
80 | uint32_t surface_bpp, | ||
60 | struct drm_framebuffer **fb_ptr)); | 81 | struct drm_framebuffer **fb_ptr)); |
61 | int drm_fb_helper_init_crtc_count(struct drm_fb_helper *helper, int crtc_count, | 82 | int drm_fb_helper_init_crtc_count(struct drm_fb_helper *helper, int crtc_count, |
62 | int max_conn); | 83 | int max_conn); |
@@ -79,4 +100,7 @@ void drm_fb_helper_fill_var(struct fb_info *info, struct drm_framebuffer *fb, | |||
79 | uint32_t fb_width, uint32_t fb_height); | 100 | uint32_t fb_width, uint32_t fb_height); |
80 | void drm_fb_helper_fill_fix(struct fb_info *info, uint32_t pitch); | 101 | void drm_fb_helper_fill_fix(struct fb_info *info, uint32_t pitch); |
81 | 102 | ||
103 | int drm_fb_helper_add_connector(struct drm_connector *connector); | ||
104 | int drm_fb_helper_parse_command_line(struct drm_device *dev); | ||
105 | |||
82 | #endif | 106 | #endif |