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>

authored by

Chris Wilson and committed by
Dave Airlie
4819d2e4 942b0e95

+24 -16
+24 -16
drivers/gpu/drm/drm_edid.c
··· 230 230 int block, int len) 231 231 { 232 232 unsigned char start = block * EDID_LENGTH; 233 - struct i2c_msg msgs[] = { 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 - }; 233 + int ret, retries = 5; 246 234 247 - if (i2c_transfer(adapter, msgs, 2) == 2) 248 - return 0; 235 + /* The core i2c driver will automatically retry the transfer if the 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 261 static u8 *