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 ---
I was going to suggest tuning the i2c_adapter.retries of the affected components, but that only covers EGAIN and not EREMOTEIO/ETIMEDOUT that afflicts us. With the alteration to only retry the transfer of the affected block, this looks to be a reasonable idea and complements the logic to retry corrupted transfers. -Chris
--- drivers/gpu/drm/drm_edid.c | 44 ++++++++++++++++++++++++++------------------ 1 files changed, 26 insertions(+), 18 deletions(-)
diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index a245d17..ddc3da9 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 *