diff options
author | Adam Jackson <ajax@redhat.com> | 2010-03-29 17:43:18 -0400 |
---|---|---|
committer | Dave Airlie <airlied@redhat.com> | 2010-04-05 20:40:16 -0400 |
commit | 61e57a8d72f2336faf39b5d940215cf085e01e6e (patch) | |
tree | 8c9c6713b4f1ff4c610771860c6cca411cb28cfc | |
parent | 95beb690170e6ce918fe53c73a0fcc7cf64d704a (diff) |
drm/edid: Fix secondary block fetch.
This makes fetching the second EDID block on HDMI monitors actually
work. DDC can't transfer more than 128 bytes at a time. Also,
rearrange the code so the pure DDC bits are separate from block parse.
Signed-off-by: Adam Jackson <ajax@redhat.com>
Signed-off-by: Dave Airlie <airlied@redhat.com>
-rw-r--r-- | drivers/gpu/drm/drm_edid.c | 330 | ||||
-rw-r--r-- | include/drm/drm_crtc.h | 2 |
2 files changed, 187 insertions, 145 deletions
diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index 7e608f4a0df9..5e60a6129641 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c | |||
@@ -2,6 +2,7 @@ | |||
2 | * Copyright (c) 2006 Luc Verhaegen (quirks list) | 2 | * Copyright (c) 2006 Luc Verhaegen (quirks list) |
3 | * Copyright (c) 2007-2008 Intel Corporation | 3 | * Copyright (c) 2007-2008 Intel Corporation |
4 | * Jesse Barnes <jesse.barnes@intel.com> | 4 | * Jesse Barnes <jesse.barnes@intel.com> |
5 | * Copyright 2010 Red Hat, Inc. | ||
5 | * | 6 | * |
6 | * DDC probing routines (drm_ddc_read & drm_do_probe_ddc_edid) originally from | 7 | * DDC probing routines (drm_ddc_read & drm_do_probe_ddc_edid) originally from |
7 | * FB layer. | 8 | * FB layer. |
@@ -106,36 +107,38 @@ static struct edid_quirk { | |||
106 | { "SAM", 638, EDID_QUIRK_PREFER_LARGE_60 }, | 107 | { "SAM", 638, EDID_QUIRK_PREFER_LARGE_60 }, |
107 | }; | 108 | }; |
108 | 109 | ||
110 | /*** DDC fetch and block validation ***/ | ||
109 | 111 | ||
110 | /* Valid EDID header has these bytes */ | ||
111 | static const u8 edid_header[] = { | 112 | static const u8 edid_header[] = { |
112 | 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00 | 113 | 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00 |
113 | }; | 114 | }; |
114 | 115 | ||
115 | /** | 116 | /* |
116 | * drm_edid_is_valid - sanity check EDID data | 117 | * Sanity check the EDID block (base or extension). Return 0 if the block |
117 | * @edid: EDID data | 118 | * doesn't check out, or 1 if it's valid. |
118 | * | ||
119 | * Sanity check the EDID block by looking at the header, the version number | ||
120 | * and the checksum. Return 0 if the EDID doesn't check out, or 1 if it's | ||
121 | * valid. | ||
122 | */ | 119 | */ |
123 | bool drm_edid_is_valid(struct edid *edid) | 120 | static bool |
121 | drm_edid_block_valid(u8 *raw_edid) | ||
124 | { | 122 | { |
125 | int i, score = 0; | 123 | int i; |
126 | u8 csum = 0; | 124 | u8 csum = 0; |
127 | u8 *raw_edid = (u8 *)edid; | 125 | struct edid *edid = (struct edid *)raw_edid; |
128 | 126 | ||
129 | for (i = 0; i < sizeof(edid_header); i++) | 127 | if (raw_edid[0] == 0x00) { |
130 | if (raw_edid[i] == edid_header[i]) | 128 | int score = 0; |
131 | score++; | ||
132 | 129 | ||
133 | if (score == 8) ; | 130 | for (i = 0; i < sizeof(edid_header); i++) |
134 | else if (score >= 6) { | 131 | if (raw_edid[i] == edid_header[i]) |
135 | DRM_DEBUG("Fixing EDID header, your hardware may be failing\n"); | 132 | score++; |
136 | memcpy(raw_edid, edid_header, sizeof(edid_header)); | 133 | |
137 | } else | 134 | if (score == 8) ; |
138 | goto bad; | 135 | else if (score >= 6) { |
136 | DRM_DEBUG("Fixing EDID header, your hardware may be failing\n"); | ||
137 | memcpy(raw_edid, edid_header, sizeof(edid_header)); | ||
138 | } else { | ||
139 | goto bad; | ||
140 | } | ||
141 | } | ||
139 | 142 | ||
140 | for (i = 0; i < EDID_LENGTH; i++) | 143 | for (i = 0; i < EDID_LENGTH; i++) |
141 | csum += raw_edid[i]; | 144 | csum += raw_edid[i]; |
@@ -144,13 +147,21 @@ bool drm_edid_is_valid(struct edid *edid) | |||
144 | goto bad; | 147 | goto bad; |
145 | } | 148 | } |
146 | 149 | ||
147 | if (edid->version != 1) { | 150 | /* per-block-type checks */ |
148 | DRM_ERROR("EDID has major version %d, instead of 1\n", edid->version); | 151 | switch (raw_edid[0]) { |
149 | goto bad; | 152 | case 0: /* base */ |
150 | } | 153 | if (edid->version != 1) { |
154 | DRM_ERROR("EDID has major version %d, instead of 1\n", edid->version); | ||
155 | goto bad; | ||
156 | } | ||
151 | 157 | ||
152 | if (edid->revision > 4) | 158 | if (edid->revision > 4) |
153 | DRM_DEBUG("EDID minor > 4, assuming backward compatibility\n"); | 159 | DRM_DEBUG("EDID minor > 4, assuming backward compatibility\n"); |
160 | break; | ||
161 | |||
162 | default: | ||
163 | break; | ||
164 | } | ||
154 | 165 | ||
155 | return 1; | 166 | return 1; |
156 | 167 | ||
@@ -162,8 +173,158 @@ bad: | |||
162 | } | 173 | } |
163 | return 0; | 174 | return 0; |
164 | } | 175 | } |
176 | |||
177 | /** | ||
178 | * drm_edid_is_valid - sanity check EDID data | ||
179 | * @edid: EDID data | ||
180 | * | ||
181 | * Sanity-check an entire EDID record (including extensions) | ||
182 | */ | ||
183 | bool drm_edid_is_valid(struct edid *edid) | ||
184 | { | ||
185 | int i; | ||
186 | u8 *raw = (u8 *)edid; | ||
187 | |||
188 | if (!edid) | ||
189 | return false; | ||
190 | |||
191 | for (i = 0; i <= edid->extensions; i++) | ||
192 | if (!drm_edid_block_valid(raw + i * EDID_LENGTH)) | ||
193 | return false; | ||
194 | |||
195 | return true; | ||
196 | } | ||
165 | EXPORT_SYMBOL(drm_edid_is_valid); | 197 | EXPORT_SYMBOL(drm_edid_is_valid); |
166 | 198 | ||
199 | #define DDC_ADDR 0x50 | ||
200 | #define DDC_SEGMENT_ADDR 0x30 | ||
201 | /** | ||
202 | * Get EDID information via I2C. | ||
203 | * | ||
204 | * \param adapter : i2c device adaptor | ||
205 | * \param buf : EDID data buffer to be filled | ||
206 | * \param len : EDID data buffer length | ||
207 | * \return 0 on success or -1 on failure. | ||
208 | * | ||
209 | * Try to fetch EDID information by calling i2c driver function. | ||
210 | */ | ||
211 | static int | ||
212 | drm_do_probe_ddc_edid(struct i2c_adapter *adapter, unsigned char *buf, | ||
213 | int block, int len) | ||
214 | { | ||
215 | unsigned char start = block * EDID_LENGTH; | ||
216 | struct i2c_msg msgs[] = { | ||
217 | { | ||
218 | .addr = DDC_ADDR, | ||
219 | .flags = 0, | ||
220 | .len = 1, | ||
221 | .buf = &start, | ||
222 | }, { | ||
223 | .addr = DDC_ADDR, | ||
224 | .flags = I2C_M_RD, | ||
225 | .len = len, | ||
226 | .buf = buf + start, | ||
227 | } | ||
228 | }; | ||
229 | |||
230 | if (i2c_transfer(adapter, msgs, 2) == 2) | ||
231 | return 0; | ||
232 | |||
233 | return -1; | ||
234 | } | ||
235 | |||
236 | static u8 * | ||
237 | drm_do_get_edid(struct drm_connector *connector, struct i2c_adapter *adapter) | ||
238 | { | ||
239 | int i, j = 0; | ||
240 | u8 *block, *new; | ||
241 | |||
242 | if ((block = kmalloc(EDID_LENGTH, GFP_KERNEL)) == NULL) | ||
243 | return NULL; | ||
244 | |||
245 | /* base block fetch */ | ||
246 | for (i = 0; i < 4; i++) { | ||
247 | if (drm_do_probe_ddc_edid(adapter, block, 0, EDID_LENGTH)) | ||
248 | goto out; | ||
249 | if (drm_edid_block_valid(block)) | ||
250 | break; | ||
251 | } | ||
252 | if (i == 4) | ||
253 | goto carp; | ||
254 | |||
255 | /* if there's no extensions, we're done */ | ||
256 | if (block[0x7e] == 0) | ||
257 | return block; | ||
258 | |||
259 | new = krealloc(block, (block[0x7e] + 1) * EDID_LENGTH, GFP_KERNEL); | ||
260 | if (!new) | ||
261 | goto out; | ||
262 | block = new; | ||
263 | |||
264 | for (j = 1; j <= block[0x7e]; j++) { | ||
265 | for (i = 0; i < 4; i++) { | ||
266 | if (drm_do_probe_ddc_edid(adapter, block, j, | ||
267 | EDID_LENGTH)) | ||
268 | goto out; | ||
269 | if (drm_edid_block_valid(block + j * EDID_LENGTH)) | ||
270 | break; | ||
271 | } | ||
272 | if (i == 4) | ||
273 | goto carp; | ||
274 | } | ||
275 | |||
276 | return block; | ||
277 | |||
278 | carp: | ||
279 | dev_warn(&connector->dev->pdev->dev, "%s: EDID block %d invalid.\n", | ||
280 | drm_get_connector_name(connector), j); | ||
281 | |||
282 | out: | ||
283 | kfree(block); | ||
284 | return NULL; | ||
285 | } | ||
286 | |||
287 | /** | ||
288 | * Probe DDC presence. | ||
289 | * | ||
290 | * \param adapter : i2c device adaptor | ||
291 | * \return 1 on success | ||
292 | */ | ||
293 | static bool | ||
294 | drm_probe_ddc(struct i2c_adapter *adapter) | ||
295 | { | ||
296 | unsigned char out; | ||
297 | |||
298 | return (drm_do_probe_ddc_edid(adapter, &out, 0, 1) == 0); | ||
299 | } | ||
300 | |||
301 | /** | ||
302 | * drm_get_edid - get EDID data, if available | ||
303 | * @connector: connector we're probing | ||
304 | * @adapter: i2c adapter to use for DDC | ||
305 | * | ||
306 | * Poke the given i2c channel to grab EDID data if possible. If found, | ||
307 | * attach it to the connector. | ||
308 | * | ||
309 | * Return edid data or NULL if we couldn't find any. | ||
310 | */ | ||
311 | struct edid *drm_get_edid(struct drm_connector *connector, | ||
312 | struct i2c_adapter *adapter) | ||
313 | { | ||
314 | struct edid *edid = NULL; | ||
315 | |||
316 | if (drm_probe_ddc(adapter)) | ||
317 | edid = (struct edid *)drm_do_get_edid(connector, adapter); | ||
318 | |||
319 | connector->display_info.raw_edid = (char *)edid; | ||
320 | |||
321 | return edid; | ||
322 | |||
323 | } | ||
324 | EXPORT_SYMBOL(drm_get_edid); | ||
325 | |||
326 | /*** EDID parsing ***/ | ||
327 | |||
167 | /** | 328 | /** |
168 | * edid_vendor - match a string against EDID's obfuscated vendor field | 329 | * edid_vendor - match a string against EDID's obfuscated vendor field |
169 | * @edid: EDID to match | 330 | * @edid: EDID to match |
@@ -1141,123 +1302,6 @@ static int add_detailed_info_eedid(struct drm_connector *connector, | |||
1141 | return modes; | 1302 | return modes; |
1142 | } | 1303 | } |
1143 | 1304 | ||
1144 | #define DDC_ADDR 0x50 | ||
1145 | /** | ||
1146 | * Get EDID information via I2C. | ||
1147 | * | ||
1148 | * \param adapter : i2c device adaptor | ||
1149 | * \param buf : EDID data buffer to be filled | ||
1150 | * \param len : EDID data buffer length | ||
1151 | * \return 0 on success or -1 on failure. | ||
1152 | * | ||
1153 | * Try to fetch EDID information by calling i2c driver function. | ||
1154 | */ | ||
1155 | int drm_do_probe_ddc_edid(struct i2c_adapter *adapter, | ||
1156 | unsigned char *buf, int len) | ||
1157 | { | ||
1158 | unsigned char start = 0x0; | ||
1159 | struct i2c_msg msgs[] = { | ||
1160 | { | ||
1161 | .addr = DDC_ADDR, | ||
1162 | .flags = 0, | ||
1163 | .len = 1, | ||
1164 | .buf = &start, | ||
1165 | }, { | ||
1166 | .addr = DDC_ADDR, | ||
1167 | .flags = I2C_M_RD, | ||
1168 | .len = len, | ||
1169 | .buf = buf, | ||
1170 | } | ||
1171 | }; | ||
1172 | |||
1173 | if (i2c_transfer(adapter, msgs, 2) == 2) | ||
1174 | return 0; | ||
1175 | |||
1176 | return -1; | ||
1177 | } | ||
1178 | EXPORT_SYMBOL(drm_do_probe_ddc_edid); | ||
1179 | |||
1180 | static int drm_ddc_read_edid(struct drm_connector *connector, | ||
1181 | struct i2c_adapter *adapter, | ||
1182 | char *buf, int len) | ||
1183 | { | ||
1184 | int i; | ||
1185 | |||
1186 | for (i = 0; i < 4; i++) { | ||
1187 | if (drm_do_probe_ddc_edid(adapter, buf, len)) | ||
1188 | return -1; | ||
1189 | if (drm_edid_is_valid((struct edid *)buf)) | ||
1190 | return 0; | ||
1191 | } | ||
1192 | |||
1193 | /* repeated checksum failures; warn, but carry on */ | ||
1194 | dev_warn(&connector->dev->pdev->dev, "%s: EDID invalid.\n", | ||
1195 | drm_get_connector_name(connector)); | ||
1196 | return -1; | ||
1197 | } | ||
1198 | |||
1199 | /** | ||
1200 | * drm_get_edid - get EDID data, if available | ||
1201 | * @connector: connector we're probing | ||
1202 | * @adapter: i2c adapter to use for DDC | ||
1203 | * | ||
1204 | * Poke the given connector's i2c channel to grab EDID data if possible. | ||
1205 | * | ||
1206 | * Return edid data or NULL if we couldn't find any. | ||
1207 | */ | ||
1208 | struct edid *drm_get_edid(struct drm_connector *connector, | ||
1209 | struct i2c_adapter *adapter) | ||
1210 | { | ||
1211 | int ret; | ||
1212 | struct edid *edid; | ||
1213 | |||
1214 | edid = kmalloc(EDID_LENGTH * (DRM_MAX_EDID_EXT_NUM + 1), | ||
1215 | GFP_KERNEL); | ||
1216 | if (edid == NULL) { | ||
1217 | dev_warn(&connector->dev->pdev->dev, | ||
1218 | "Failed to allocate EDID\n"); | ||
1219 | goto end; | ||
1220 | } | ||
1221 | |||
1222 | /* Read first EDID block */ | ||
1223 | ret = drm_ddc_read_edid(connector, adapter, | ||
1224 | (unsigned char *)edid, EDID_LENGTH); | ||
1225 | if (ret != 0) | ||
1226 | goto clean_up; | ||
1227 | |||
1228 | /* There are EDID extensions to be read */ | ||
1229 | if (edid->extensions != 0) { | ||
1230 | int edid_ext_num = edid->extensions; | ||
1231 | |||
1232 | if (edid_ext_num > DRM_MAX_EDID_EXT_NUM) { | ||
1233 | dev_warn(&connector->dev->pdev->dev, | ||
1234 | "The number of extension(%d) is " | ||
1235 | "over max (%d), actually read number (%d)\n", | ||
1236 | edid_ext_num, DRM_MAX_EDID_EXT_NUM, | ||
1237 | DRM_MAX_EDID_EXT_NUM); | ||
1238 | /* Reset EDID extension number to be read */ | ||
1239 | edid_ext_num = DRM_MAX_EDID_EXT_NUM; | ||
1240 | } | ||
1241 | /* Read EDID including extensions too */ | ||
1242 | ret = drm_ddc_read_edid(connector, adapter, (char *)edid, | ||
1243 | EDID_LENGTH * (edid_ext_num + 1)); | ||
1244 | if (ret != 0) | ||
1245 | goto clean_up; | ||
1246 | |||
1247 | } | ||
1248 | |||
1249 | connector->display_info.raw_edid = (char *)edid; | ||
1250 | goto end; | ||
1251 | |||
1252 | clean_up: | ||
1253 | kfree(edid); | ||
1254 | edid = NULL; | ||
1255 | end: | ||
1256 | return edid; | ||
1257 | |||
1258 | } | ||
1259 | EXPORT_SYMBOL(drm_get_edid); | ||
1260 | |||
1261 | #define HDMI_IDENTIFIER 0x000C03 | 1305 | #define HDMI_IDENTIFIER 0x000C03 |
1262 | #define VENDOR_BLOCK 0x03 | 1306 | #define VENDOR_BLOCK 0x03 |
1263 | /** | 1307 | /** |
diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index 1347524a8e30..f74523a299c9 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h | |||
@@ -666,8 +666,6 @@ extern void drm_fb_release(struct drm_file *file_priv); | |||
666 | extern int drm_mode_group_init_legacy_group(struct drm_device *dev, struct drm_mode_group *group); | 666 | extern int drm_mode_group_init_legacy_group(struct drm_device *dev, struct drm_mode_group *group); |
667 | extern struct edid *drm_get_edid(struct drm_connector *connector, | 667 | extern struct edid *drm_get_edid(struct drm_connector *connector, |
668 | struct i2c_adapter *adapter); | 668 | struct i2c_adapter *adapter); |
669 | extern int drm_do_probe_ddc_edid(struct i2c_adapter *adapter, | ||
670 | unsigned char *buf, int len); | ||
671 | extern int drm_add_edid_modes(struct drm_connector *connector, struct edid *edid); | 669 | extern int drm_add_edid_modes(struct drm_connector *connector, struct edid *edid); |
672 | extern void drm_mode_probed_add(struct drm_connector *connector, struct drm_display_mode *mode); | 670 | extern void drm_mode_probed_add(struct drm_connector *connector, struct drm_display_mode *mode); |
673 | extern void drm_mode_remove(struct drm_connector *connector, struct drm_display_mode *mode); | 671 | extern void drm_mode_remove(struct drm_connector *connector, struct drm_display_mode *mode); |