aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJesse Barnes <jbarnes@virtuousgeek.org>2009-01-12 15:05:32 -0500
committerDave Airlie <airlied@linux.ie>2009-01-16 03:40:54 -0500
commit40a518d9f1fd8ed1061b8b4e2ce8a44794f4eb03 (patch)
treec085ac55dadbd78024b06a052f73e097777858b5
parent3a03ac1a0223f779a3de313523408ddb099e5679 (diff)
drm: initial KMS config fixes
When mode setting is first initialized, the driver will call into drm_helper_initial_config() to set up an initial output and framebuffer configuration. This routine is responsible for probing the available connectors, encoders, and crtcs, looking for modes and putting together something reasonable (where reasonable is defined as "allows kernel messages to be visible on as many displays as possible"). However, the code was a bit too aggressive in setting default modes when none were found on a given connector. Even if some connectors had modes, any connectors found lacking modes would have the default 800x600 mode added to their mode list, which in some cases could cause problems later down the line. In my case, the LVDS was perfectly available, but the initial config code added 800x600 modes to both of the detected but unavailable HDMI connectors (which are on my non-existent docking station). This ended up preventing later code from setting a mode on my LVDS, which is bad. This patch fixes that behavior by making the initial config code walk through the connectors first, counting the available modes, before it decides to add any default modes to a possibly connected output. It also fixes the logic in drm_target_preferred() that was causing zeroed out modes to be set as the preferred mode for a given connector, even if no modes were available. Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org> Signed-off-by: Eric Anholt <eric@anholt.net> Signed-off-by: Dave Airlie <airlied@linux.ie>
-rw-r--r--drivers/gpu/drm/drm_crtc_helper.c127
-rw-r--r--include/drm/drm_crtc.h2
-rw-r--r--include/drm/drm_crtc_helper.h2
3 files changed, 95 insertions, 36 deletions
diff --git a/drivers/gpu/drm/drm_crtc_helper.c b/drivers/gpu/drm/drm_crtc_helper.c
index d8a982b71296..e490e69db21e 100644
--- a/drivers/gpu/drm/drm_crtc_helper.c
+++ b/drivers/gpu/drm/drm_crtc_helper.c
@@ -36,7 +36,7 @@
36/* 36/*
37 * Detailed mode info for 800x600@60Hz 37 * Detailed mode info for 800x600@60Hz
38 */ 38 */
39static struct drm_display_mode std_mode[] = { 39static struct drm_display_mode std_modes[] = {
40 { DRM_MODE("800x600", DRM_MODE_TYPE_DEFAULT, 40000, 800, 840, 40 { DRM_MODE("800x600", DRM_MODE_TYPE_DEFAULT, 40000, 800, 840,
41 968, 1056, 0, 600, 601, 605, 628, 0, 41 968, 1056, 0, 600, 601, 605, 628, 0,
42 DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, 42 DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
@@ -60,15 +60,18 @@ static struct drm_display_mode std_mode[] = {
60 * changes have occurred. 60 * changes have occurred.
61 * 61 *
62 * FIXME: take into account monitor limits 62 * FIXME: take into account monitor limits
63 *
64 * RETURNS:
65 * Number of modes found on @connector.
63 */ 66 */
64void drm_helper_probe_single_connector_modes(struct drm_connector *connector, 67int drm_helper_probe_single_connector_modes(struct drm_connector *connector,
65 uint32_t maxX, uint32_t maxY) 68 uint32_t maxX, uint32_t maxY)
66{ 69{
67 struct drm_device *dev = connector->dev; 70 struct drm_device *dev = connector->dev;
68 struct drm_display_mode *mode, *t; 71 struct drm_display_mode *mode, *t;
69 struct drm_connector_helper_funcs *connector_funcs = 72 struct drm_connector_helper_funcs *connector_funcs =
70 connector->helper_private; 73 connector->helper_private;
71 int ret; 74 int count = 0;
72 75
73 DRM_DEBUG("%s\n", drm_get_connector_name(connector)); 76 DRM_DEBUG("%s\n", drm_get_connector_name(connector));
74 /* set all modes to the unverified state */ 77 /* set all modes to the unverified state */
@@ -81,14 +84,14 @@ void drm_helper_probe_single_connector_modes(struct drm_connector *connector,
81 DRM_DEBUG("%s is disconnected\n", 84 DRM_DEBUG("%s is disconnected\n",
82 drm_get_connector_name(connector)); 85 drm_get_connector_name(connector));
83 /* TODO set EDID to NULL */ 86 /* TODO set EDID to NULL */
84 return; 87 return 0;
85 } 88 }
86 89
87 ret = (*connector_funcs->get_modes)(connector); 90 count = (*connector_funcs->get_modes)(connector);
91 if (!count)
92 return 0;
88 93
89 if (ret) { 94 drm_mode_connector_list_update(connector);
90 drm_mode_connector_list_update(connector);
91 }
92 95
93 if (maxX && maxY) 96 if (maxX && maxY)
94 drm_mode_validate_size(dev, &connector->modes, maxX, 97 drm_mode_validate_size(dev, &connector->modes, maxX,
@@ -102,25 +105,8 @@ void drm_helper_probe_single_connector_modes(struct drm_connector *connector,
102 105
103 drm_mode_prune_invalid(dev, &connector->modes, true); 106 drm_mode_prune_invalid(dev, &connector->modes, true);
104 107
105 if (list_empty(&connector->modes)) { 108 if (list_empty(&connector->modes))
106 struct drm_display_mode *stdmode; 109 return 0;
107
108 DRM_DEBUG("No valid modes on %s\n",
109 drm_get_connector_name(connector));
110
111 /* Should we do this here ???
112 * When no valid EDID modes are available we end up
113 * here and bailed in the past, now we add a standard
114 * 640x480@60Hz mode and carry on.
115 */
116 stdmode = drm_mode_duplicate(dev, &std_mode[0]);
117 drm_mode_probed_add(connector, stdmode);
118 drm_mode_list_concat(&connector->probed_modes,
119 &connector->modes);
120
121 DRM_DEBUG("Adding standard 640x480 @ 60Hz to %s\n",
122 drm_get_connector_name(connector));
123 }
124 110
125 drm_mode_sort(&connector->modes); 111 drm_mode_sort(&connector->modes);
126 112
@@ -131,20 +117,58 @@ void drm_helper_probe_single_connector_modes(struct drm_connector *connector,
131 drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V); 117 drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V);
132 drm_mode_debug_printmodeline(mode); 118 drm_mode_debug_printmodeline(mode);
133 } 119 }
120
121 return count;
134} 122}
135EXPORT_SYMBOL(drm_helper_probe_single_connector_modes); 123EXPORT_SYMBOL(drm_helper_probe_single_connector_modes);
136 124
137void drm_helper_probe_connector_modes(struct drm_device *dev, uint32_t maxX, 125int drm_helper_probe_connector_modes(struct drm_device *dev, uint32_t maxX,
138 uint32_t maxY) 126 uint32_t maxY)
139{ 127{
140 struct drm_connector *connector; 128 struct drm_connector *connector;
129 int count = 0;
141 130
142 list_for_each_entry(connector, &dev->mode_config.connector_list, head) { 131 list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
143 drm_helper_probe_single_connector_modes(connector, maxX, maxY); 132 count += drm_helper_probe_single_connector_modes(connector,
133 maxX, maxY);
144 } 134 }
135
136 return count;
145} 137}
146EXPORT_SYMBOL(drm_helper_probe_connector_modes); 138EXPORT_SYMBOL(drm_helper_probe_connector_modes);
147 139
140static void drm_helper_add_std_modes(struct drm_device *dev,
141 struct drm_connector *connector)
142{
143 struct drm_display_mode *mode, *t;
144 int i;
145
146 for (i = 0; i < ARRAY_SIZE(std_modes); i++) {
147 struct drm_display_mode *stdmode;
148
149 /*
150 * When no valid EDID modes are available we end up
151 * here and bailed in the past, now we add some standard
152 * modes and move on.
153 */
154 stdmode = drm_mode_duplicate(dev, &std_modes[i]);
155 drm_mode_probed_add(connector, stdmode);
156 drm_mode_list_concat(&connector->probed_modes,
157 &connector->modes);
158
159 DRM_DEBUG("Adding mode %s to %s\n", stdmode->name,
160 drm_get_connector_name(connector));
161 }
162 drm_mode_sort(&connector->modes);
163
164 DRM_DEBUG("Added std modes on %s\n", drm_get_connector_name(connector));
165 list_for_each_entry_safe(mode, t, &connector->modes, head) {
166 mode->vrefresh = drm_mode_vrefresh(mode);
167
168 drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V);
169 drm_mode_debug_printmodeline(mode);
170 }
171}
148 172
149/** 173/**
150 * drm_helper_crtc_in_use - check if a given CRTC is in a mode_config 174 * drm_helper_crtc_in_use - check if a given CRTC is in a mode_config
@@ -237,6 +261,8 @@ static void drm_enable_connectors(struct drm_device *dev, bool *enabled)
237 261
238 list_for_each_entry(connector, &dev->mode_config.connector_list, head) { 262 list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
239 enabled[i] = drm_connector_enabled(connector, true); 263 enabled[i] = drm_connector_enabled(connector, true);
264 DRM_DEBUG("connector %d enabled? %s\n", connector->base.id,
265 enabled[i] ? "yes" : "no");
240 any_enabled |= enabled[i]; 266 any_enabled |= enabled[i];
241 i++; 267 i++;
242 } 268 }
@@ -265,11 +291,17 @@ static bool drm_target_preferred(struct drm_device *dev,
265 continue; 291 continue;
266 } 292 }
267 293
294 DRM_DEBUG("looking for preferred mode on connector %d\n",
295 connector->base.id);
296
268 modes[i] = drm_has_preferred_mode(connector, width, height); 297 modes[i] = drm_has_preferred_mode(connector, width, height);
269 if (!modes[i]) { 298 /* No preferred modes, pick one off the list */
299 if (!modes[i] && !list_empty(&connector->modes)) {
270 list_for_each_entry(modes[i], &connector->modes, head) 300 list_for_each_entry(modes[i], &connector->modes, head)
271 break; 301 break;
272 } 302 }
303 DRM_DEBUG("found mode %s\n", modes[i] ? modes[i]->name :
304 "none");
273 i++; 305 i++;
274 } 306 }
275 return true; 307 return true;
@@ -369,6 +401,8 @@ static void drm_setup_crtcs(struct drm_device *dev)
369 int width, height; 401 int width, height;
370 int i, ret; 402 int i, ret;
371 403
404 DRM_DEBUG("\n");
405
372 width = dev->mode_config.max_width; 406 width = dev->mode_config.max_width;
373 height = dev->mode_config.max_height; 407 height = dev->mode_config.max_height;
374 408
@@ -390,6 +424,8 @@ static void drm_setup_crtcs(struct drm_device *dev)
390 if (!ret) 424 if (!ret)
391 DRM_ERROR("Unable to find initial modes\n"); 425 DRM_ERROR("Unable to find initial modes\n");
392 426
427 DRM_DEBUG("picking CRTCs for %dx%d config\n", width, height);
428
393 drm_pick_crtcs(dev, crtcs, modes, 0, width, height); 429 drm_pick_crtcs(dev, crtcs, modes, 0, width, height);
394 430
395 i = 0; 431 i = 0;
@@ -403,6 +439,8 @@ static void drm_setup_crtcs(struct drm_device *dev)
403 } 439 }
404 440
405 if (mode && crtc) { 441 if (mode && crtc) {
442 DRM_DEBUG("desired mode %s set on crtc %d\n",
443 mode->name, crtc->base.id);
406 crtc->desired_mode = mode; 444 crtc->desired_mode = mode;
407 connector->encoder->crtc = crtc; 445 connector->encoder->crtc = crtc;
408 } else 446 } else
@@ -764,10 +802,31 @@ bool drm_helper_plugged_event(struct drm_device *dev)
764 */ 802 */
765bool drm_helper_initial_config(struct drm_device *dev, bool can_grow) 803bool drm_helper_initial_config(struct drm_device *dev, bool can_grow)
766{ 804{
767 int ret = false; 805 struct drm_connector *connector;
806 int count = 0;
768 807
769 drm_helper_plugged_event(dev); 808 count = drm_helper_probe_connector_modes(dev,
770 return ret; 809 dev->mode_config.max_width,
810 dev->mode_config.max_height);
811
812 /*
813 * None of the available connectors had any modes, so add some
814 * and try to light them up anyway
815 */
816 if (!count) {
817 DRM_ERROR("connectors have no modes, using standard modes\n");
818 list_for_each_entry(connector,
819 &dev->mode_config.connector_list,
820 head)
821 drm_helper_add_std_modes(dev, connector);
822 }
823
824 drm_setup_crtcs(dev);
825
826 /* alert the driver fb layer */
827 dev->mode_config.funcs->fb_changed(dev);
828
829 return 0;
771} 830}
772EXPORT_SYMBOL(drm_helper_initial_config); 831EXPORT_SYMBOL(drm_helper_initial_config);
773 832
diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h
index 0acb07f31fa4..47809ac94bc3 100644
--- a/include/drm/drm_crtc.h
+++ b/include/drm/drm_crtc.h
@@ -395,7 +395,7 @@ struct drm_connector_funcs {
395 void (*save)(struct drm_connector *connector); 395 void (*save)(struct drm_connector *connector);
396 void (*restore)(struct drm_connector *connector); 396 void (*restore)(struct drm_connector *connector);
397 enum drm_connector_status (*detect)(struct drm_connector *connector); 397 enum drm_connector_status (*detect)(struct drm_connector *connector);
398 void (*fill_modes)(struct drm_connector *connector, uint32_t max_width, uint32_t max_height); 398 int (*fill_modes)(struct drm_connector *connector, uint32_t max_width, uint32_t max_height);
399 int (*set_property)(struct drm_connector *connector, struct drm_property *property, 399 int (*set_property)(struct drm_connector *connector, struct drm_property *property,
400 uint64_t val); 400 uint64_t val);
401 void (*destroy)(struct drm_connector *connector); 401 void (*destroy)(struct drm_connector *connector);
diff --git a/include/drm/drm_crtc_helper.h b/include/drm/drm_crtc_helper.h
index 4bc04cf460a7..0c6f0e11b41b 100644
--- a/include/drm/drm_crtc_helper.h
+++ b/include/drm/drm_crtc_helper.h
@@ -88,7 +88,7 @@ struct drm_connector_helper_funcs {
88 struct drm_encoder *(*best_encoder)(struct drm_connector *connector); 88 struct drm_encoder *(*best_encoder)(struct drm_connector *connector);
89}; 89};
90 90
91extern void drm_helper_probe_single_connector_modes(struct drm_connector *connector, uint32_t maxX, uint32_t maxY); 91extern int drm_helper_probe_single_connector_modes(struct drm_connector *connector, uint32_t maxX, uint32_t maxY);
92extern void drm_helper_disable_unused_functions(struct drm_device *dev); 92extern void drm_helper_disable_unused_functions(struct drm_device *dev);
93extern int drm_helper_hotplug_stage_two(struct drm_device *dev); 93extern int drm_helper_hotplug_stage_two(struct drm_device *dev);
94extern bool drm_helper_initial_config(struct drm_device *dev, bool can_grow); 94extern bool drm_helper_initial_config(struct drm_device *dev, bool can_grow);