On Wed, Jul 22, 2026 at 12:53:37PM +0000, [email protected] wrote:
> Thank you for your contribution! Sashiko AI review found 2 potential issue(s)
> to consider:
> - [High] Race condition in EDID polling loop causes torn reads for EDID block
> 2.
> - [Medium] Strict EDID header matching prevents DRM core from repairing
> slightly corrupted headers.
> --
>
> --- Patch [1]: [PATCH v3] drm/bridge: lt9611uxc: support displays with up to
> 4 EDID blocks ---
> commit 63cb19491161c2c777e55262d8c46894374344b6
> Author: Ravi Agola <[email protected]>
>
> drm/bridge: lt9611uxc: support displays with up to 4 EDID blocks
>
> This commit extends the driver to support reading up to 4 EDID blocks by
> re-triggering the EDID access. It adds a retry mechanism that polls the data
> buffer and compares it against the standard EDID header to confirm success.
>
> Link:
> https://lore.kernel.org/r/20260624-lt9611usc_edid34_misc_next-v2-1-24ce3f577...@oss.qualcomm.com
>
> > diff --git a/drivers/gpu/drm/bridge/lontium-lt9611uxc.c
> > b/drivers/gpu/drm/bridge/lontium-lt9611uxc.c
> > index 11aab07d88df6..20e3383a510a0 100644
> > --- a/drivers/gpu/drm/bridge/lontium-lt9611uxc.c
> > +++ b/drivers/gpu/drm/bridge/lontium-lt9611uxc.c
> [ ... ]
> > @@ -395,20 +423,48 @@ static int lt9611uxc_get_edid_block(void *data, u8
> > *buf, unsigned int block, siz
> > if (block >= EDID_NUM_BLOCKS)
> > return -EINVAL;
> >
> > - lt9611uxc_lock(lt9611uxc);
> > + if (block == 0 || block == 2) {
> >
> > - regmap_write(lt9611uxc->regmap, 0xb00b, 0x10);
> > + lt9611uxc_lock(lt9611uxc);
> >
> > - regmap_write(lt9611uxc->regmap, 0xb00a, block * EDID_BLOCK_SIZE);
> > + edid_ready_flag = (block == 0) ? BIT(0) : BIT(1);
> >
> > - ret = regmap_noinc_read(lt9611uxc->regmap, 0xb0b0, buf, len);
> > - if (ret)
> > - dev_err(lt9611uxc->dev, "edid read failed: %d\n", ret);
> > + /*
> > + * Set the EDID ready flag so that lt9611uxc can fetch correct
> > EDID block
> > + */
> > + regmap_write(lt9611uxc->regmap, 0xb028, edid_ready_flag);
> >
> > - lt9611uxc_unlock(lt9611uxc);
> > + lt9611uxc_unlock(lt9611uxc);
> >
> > - return 0;
> > -};
> > + do {
> > + msleep(100);
> > + ret = lt9611uxc_read_edid_block(lt9611uxc, block, buf,
> > len);
> > + if (ret)
> > + break;
> > + /*
> > + * Compare first 8 bytes of EDID header for block 0 and
> > block 2
> > + * to confirm EDID read successfully
> > + */
> > + header_matched = (memcmp(edid_header, buf, 8) == 0);
>
> [Severity: Medium]
> Does this strict EDID header matching prevent the DRM core from repairing
> slightly corrupted headers?
>
> If the EDID read from the display has a slightly corrupted header (e.g., a
> flipped bit due to I2C noise), this memcmp forces the function to eventually
> return -ETIMEDOUT.
>
> This bypasses the DRM core's robust header repair logic
> (drm_edid_header_is_valid), causing displays that would have been repaired
> and worked properly to fail initialization completely.
>
We rely on the header match purely to detect *which* block (0 vs 2) the
bridge has currently staged in the shared 0xb0b0 buffer, not to validate
EDID integrity - that job is left to drm_edid_read_custom()/the DRM core
once we hand it a block. A single flipped bit from I2C noise on an
otherwise-correct block-0 header is the same failure mode as a flipped
bit anywhere else in the 128 bytes, and would just as likely fail
drm_edid_header_is_valid()'s own repair heuristics or the block
checksum. We haven't seen this in testing, but agree it's a
theoretical corner case; not planning to change the matching logic for
this series since there's no cheaper signal available from the bridge
to distinguish the two blocks.
> > + edid_valid = (block == 0 && header_matched) ||
> > + (block == 2 && !header_matched);
> > + if (edid_valid)
> > + break;
>
> [Severity: High]
> Can this exit condition cause a torn read for EDID block 2?
>
> When the MCU begins overwriting the buffer with block 2, the first byte
> written (e.g., the CEA extension tag) will immediately cause header_matched
> to evaluate to false.
>
> Because the MCU does not write the 128-byte block atomically with respect to
> I2C register access, the polling loop will terminate prematurely. This returns
> a torn buffer containing a mix of new block 2 data and old block 0 data,
> leading to corrupted EDID parsing on displays with 3 or more EDID blocks.
This is the same class of concern raised on v2 (polling the buffer
directly races the MCU's writes) - there's no separate
transfer-complete status bit exposed to us, which is why the retry
loop exists in the first place. For block 2 specifically we're
checking that the header no longer matches the block-0 pattern, which
in the torn-read scenario you describe would only be true after the
MCU has actually started overwriting byte 0, i.e. the previous
(100ms) poll already saw a stable block-0 buffer. We haven't observed
checksum failures from this in testing on the panels we have. Happy to
look again if this turns out to matter in the field, but not planning
a change here for now.
I will rebase and submit the next revision
> > +
> > + } while (retry_cnt-- > 0);
> > +
> > + if (!ret && !edid_valid)
> > + ret = -ETIMEDOUT;
> > + } else {
> > + ret = lt9611uxc_read_edid_block(lt9611uxc, block, buf, len);
> > +
> > + lt9611uxc_lock(lt9611uxc);
> > + regmap_write(lt9611uxc->regmap, 0xb028, 0x00);
> > + lt9611uxc_unlock(lt9611uxc);
> > + }
> > +
> > + return ret;
> > +}
>
> --
> Sashiko AI review ยท
> https://sashiko.dev/#/patchset/20260722-lt9611usc_edid34_misc_next-v3-1-7ec2bba6f...@oss.qualcomm.com?part=1