diff options
author | Chris Wilson <chris@chris-wilson.co.uk> | 2011-03-15 07:04:41 -0400 |
---|---|---|
committer | Dave Airlie <airlied@redhat.com> | 2011-03-15 21:25:13 -0400 |
commit | 4819d2e4310796c4e9eef674499af9b9caf36b5a (patch) | |
tree | f2896e922639aa5813646b75ef6d0d4832591b4d /drivers/gpu | |
parent | 942b0e95c34f1ba432d08e1c0288ed032d32c3b2 (diff) |
drm: Retry i2c transfer of EDID block after failure
Usually EDID retrieval is fine. However, sometimes, especially when the
machine is loaded, it fails, but succeeds after a few retries.
Based on a patch by Michael Buesch.
Reported-by: Michael Buesch <mb@bu3sch.de>
Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
Reviewed-by: Alex Deucher <alexdeucher@gmail.com>
Signed-off-by: Dave Airlie <airlied@redhat.com>
Diffstat (limited to 'drivers/gpu')
-rw-r--r-- | drivers/gpu/drm/drm_edid.c | 40 |
1 files changed, 24 insertions, 16 deletions
diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index af60d9be9632..9c595e3b9c20 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c | |||
@@ -230,24 +230,32 @@ drm_do_probe_ddc_edid(struct i2c_adapter *adapter, unsigned char *buf, | |||
230 | int block, int len) | 230 | int block, int len) |
231 | { | 231 | { |
232 | unsigned char start = block * EDID_LENGTH; | 232 | unsigned char start = block * EDID_LENGTH; |
233 | struct i2c_msg msgs[] = { | 233 | int ret, retries = 5; |
234 | { | ||
235 | .addr = DDC_ADDR, | ||
236 | .flags = 0, | ||
237 | .len = 1, | ||
238 | .buf = &start, | ||
239 | }, { | ||
240 | .addr = DDC_ADDR, | ||
241 | .flags = I2C_M_RD, | ||
242 | .len = len, | ||
243 | .buf = buf, | ||
244 | } | ||
245 | }; | ||
246 | 234 | ||
247 | if (i2c_transfer(adapter, msgs, 2) == 2) | 235 | /* The core i2c driver will automatically retry the transfer if the |
248 | return 0; | 236 | * adapter reports EAGAIN. However, we find that bit-banging transfers |
237 | * are susceptible to errors under a heavily loaded machine and | ||
238 | * generate spurious NAKs and timeouts. Retrying the transfer | ||
239 | * of the individual block a few times seems to overcome this. | ||
240 | */ | ||
241 | do { | ||
242 | struct i2c_msg msgs[] = { | ||
243 | { | ||
244 | .addr = DDC_ADDR, | ||
245 | .flags = 0, | ||
246 | .len = 1, | ||
247 | .buf = &start, | ||
248 | }, { | ||
249 | .addr = DDC_ADDR, | ||
250 | .flags = I2C_M_RD, | ||
251 | .len = len, | ||
252 | .buf = buf, | ||
253 | } | ||
254 | }; | ||
255 | ret = i2c_transfer(adapter, msgs, 2); | ||
256 | } while (ret != 2 && --retries); | ||
249 | 257 | ||
250 | return -1; | 258 | return ret == 2 ? 0 : -1; |
251 | } | 259 | } |
252 | 260 | ||
253 | static u8 * | 261 | static u8 * |