diff options
author | Chris Wilson <chris@chris-wilson.co.uk> | 2011-03-15 11:04:41 +0000 |
---|---|---|
committer | Dave Airlie <airlied@redhat.com> | 2011-03-16 11:25:13 +1000 |
commit | 4819d2e4310796c4e9eef674499af9b9caf36b5a (patch) | |
tree | f2896e922639aa5813646b75ef6d0d4832591b4d | |
parent | 942b0e95c34f1ba432d08e1c0288ed032d32c3b2 (diff) | |
download | linux-stable-4819d2e4310796c4e9eef674499af9b9caf36b5a.tar.gz linux-stable-4819d2e4310796c4e9eef674499af9b9caf36b5a.tar.bz2 linux-stable-4819d2e4310796c4e9eef674499af9b9caf36b5a.zip |
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>
-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, int block, int len) { unsigned char start = block * EDID_LENGTH; - struct i2c_msg msgs[] = { - { - .addr = DDC_ADDR, - .flags = 0, - .len = 1, - .buf = &start, - }, { - .addr = DDC_ADDR, - .flags = I2C_M_RD, - .len = len, - .buf = buf, - } - }; + int ret, retries = 5; - if (i2c_transfer(adapter, msgs, 2) == 2) - return 0; + /* The core i2c driver will automatically retry the transfer if the + * adapter reports EAGAIN. However, we find that bit-banging transfers + * are susceptible to errors under a heavily loaded machine and + * generate spurious NAKs and timeouts. Retrying the transfer + * of the individual block a few times seems to overcome this. + */ + do { + struct i2c_msg msgs[] = { + { + .addr = DDC_ADDR, + .flags = 0, + .len = 1, + .buf = &start, + }, { + .addr = DDC_ADDR, + .flags = I2C_M_RD, + .len = len, + .buf = buf, + } + }; + ret = i2c_transfer(adapter, msgs, 2); + } while (ret != 2 && --retries); - return -1; + return ret == 2 ? 0 : -1; } static u8 * |