Re: [PATCH v4] drm/bridge: lt9611uxc: support displays with up to 4 EDID blocks

From: Vishnu Saini

Date: Thu Sep 24 2026 - 15:46:01 EST


On Tue, Sep 22, 2026 at 11:55:01AM +0300, Jani Nikula wrote:
> On Tue, 22 Sep 2026, vishnu.saini@xxxxxxxxxxxxxxxx wrote:
> > From: Ravi Agola <raviagol@xxxxxxxxxxxxxxxx>
> >
> > The LT9611UXC bridge can fetch only 2 EDID blocks at a time, which
> > previously limited EDID reading to 2 blocks and prevented support
> > for displays exposing more than 2 EDID blocks.
> >
> > Add driver support to fetch up to 4 EDID blocks by re-triggering
> > EDID access after the first 2 blocks are read. For block 0 and 2,
> > set the EDID ready flag in 0xb028 so the bridge can expose the
> > corresponding EDID blocks, then retry the read until the expected
> > EDID is fetched.
> >
> > Reset the edid_read flag on HPD disconnect so that the next
> > connect event triggers a fresh EDID fetch.
> >
> > Increase EDID wait time from 500ms to 1000ms, On Qualcomm rb3gen2
> > platform sometimes edid read interrupt is coming 600-650 ms after
> > HPD interrupt resulting in edid read failure.
> >
> > Signed-off-by: Ravi Agola <raviagol@xxxxxxxxxxxxxxxx>
> > Signed-off-by: Vishnu Saini <vishnu.saini@xxxxxxxxxxxxxxxx>
> > ---
> > Changes in v4:
> > - Rebased onto latest drm-misc-next.
> > - No functional changes; replied inline to the two Sashiko-bot
> > review comments on v3
> > - Link to v3: https://lore.kernel.org/r/20260722-lt9611usc_edid34_misc_next-v3-1-7ec2bba6ff8e@xxxxxxxxxxxxxxxx
> >
> > Changes in v3:
> > - moved edid_read state change inside mutex lock [sashiko-bot]
> > - returned -ETIMEOUT when retry_cnt is exhausted without a match. [Sashiko-bot]
> > - Increased edid wait timeout from 500ms to 1000ms as edid interrupt is sometimes taking around 650ms time
> > - Link to v2: https://lore.kernel.org/r/20260624-lt9611usc_edid34_misc_next-v2-1-24ce3f5770b8@xxxxxxxxxxxxxxxx
> >
> > Changes in v2:
> > - Removed memcpy and doing edid fetch everytime. Required an update in firmware as well for edid block0 reset.
> > - Verified patch with existing firmware V5.0.21, patch is not causing any regression if V5.0.21 firmware is used.
> > - Firmware V5.0.22 is required with this patch to support edid fetch on more than 2 edid block monitors.
> > - Link to v1: https://lore.kernel.org/r/20260517-lt9611usc_edid34_misc_next-v1-1-5e2fd8c6399b@xxxxxxxxxxxxxxxx
> > ---
> > drivers/gpu/drm/bridge/lontium-lt9611uxc.c | 80 +++++++++++++++++++++++++-----
> > 1 file changed, 68 insertions(+), 12 deletions(-)
> >
> > diff --git a/drivers/gpu/drm/bridge/lontium-lt9611uxc.c b/drivers/gpu/drm/bridge/lontium-lt9611uxc.c
> > index b3bb7f2cebb3..a74077a65f62 100644
> > --- a/drivers/gpu/drm/bridge/lontium-lt9611uxc.c
> > +++ b/drivers/gpu/drm/bridge/lontium-lt9611uxc.c
> > @@ -28,7 +28,7 @@
> > #include <drm/display/drm_hdmi_audio_helper.h>
> >
> > #define EDID_BLOCK_SIZE 128
> > -#define EDID_NUM_BLOCKS 2
> > +#define EDID_NUM_BLOCKS 4
> >
> > #define FW_FILE "lt9611uxc_fw.bin"
> >
> > @@ -167,6 +167,8 @@ static void lt9611uxc_hpd_work(struct work_struct *work)
> >
> > mutex_lock(&lt9611uxc->ocm_lock);
> > connected = lt9611uxc->hdmi_connected;
> > + if (!connected)
> > + lt9611uxc->edid_read = false;
> > mutex_unlock(&lt9611uxc->ocm_lock);
> >
> > drm_bridge_hpd_notify(&lt9611uxc->bridge,
> > @@ -380,13 +382,39 @@ lt9611uxc_bridge_detect(struct drm_bridge *bridge, struct drm_connector *connect
> > static int lt9611uxc_wait_for_edid(struct lt9611uxc *lt9611uxc)
> > {
> > return wait_event_interruptible_timeout(lt9611uxc->wq, lt9611uxc->edid_read,
> > - msecs_to_jiffies(500));
> > + msecs_to_jiffies(1000));
> > +}
> > +
> > +static int lt9611uxc_read_edid_block(struct lt9611uxc *lt9611uxc, unsigned int block,
> > + u8 *buf, size_t len)
> > +{
> > + int ret;
> > +
> > + lt9611uxc_lock(lt9611uxc);
> > +
> > + regmap_write(lt9611uxc->regmap, 0xb00a, (block % 2) * EDID_BLOCK_SIZE);
> > +
> > + ret = regmap_noinc_read(lt9611uxc->regmap, 0xb0b0, buf, len);
> > + if (ret) {
> > + dev_err(lt9611uxc->dev, "edid block %d read failed: %d\n", block, ret);
> > + lt9611uxc_unlock(lt9611uxc);
> > + return -EINVAL;
> > + }
> > + lt9611uxc_unlock(lt9611uxc);
> > +
> > + return ret;
> > }
> >
> > static int lt9611uxc_get_edid_block(void *data, u8 *buf, unsigned int block, size_t len)
> > {
> > struct lt9611uxc *lt9611uxc = data;
> > - int ret;
> > + int ret = 0;
> > + int retry_cnt = 10;
> > + unsigned int edid_ready_flag = 0;
> > + bool header_matched;
> > + bool edid_valid = false;
> > + const u8 edid_header[8] = { 0x00, 0xFF, 0xFF, 0xFF,
> > + 0xFF, 0xFF, 0xFF, 0x00 };
> >
> > if (len > EDID_BLOCK_SIZE)
> > return -EINVAL;
> > @@ -394,20 +422,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);
> > + edid_valid = (block == 0 && header_matched) ||
> > + (block == 2 && !header_matched);
> > + if (edid_valid)
> > + break;
> > +
> > + } while (retry_cnt-- > 0);
>
> What's the purpose of the above dance? Why do you have to try ten times,
> and what's with the header matching? None of this is explained in the
> commit message.

Fair, sorry for the missing explanation. The bridge only buffers 2
EDID blocks at a time at a fixed offset (block % 2). Writing 0xb028
re-triggers an asynchronous fetch of a fresh pair of blocks from the
sink, and the bridge has no "fetch done" status to wait on, so we
poll the buffer instead: 10 retries at 100ms apart, i.e. up to 1s,
which covers the fetch latency we've observed on this bridge (same
ballpark as the 600-650ms EDID-interrupt delay mentioned in the
commit message).

The header check is how we detect the new pair actually landed rather
than stale data from the previous fetch: block 0 always starts with
the fixed EDID header magic, so we accept it once that magic appears;
block 2 (an extension block) never carries that magic, so we accept it
once the previous block 0's leftover header is gone.

This will allow EDID fetch for more than 2 block monitors like Dell Curved Monitor (Model - U4021QW)

I will improve the commit msg in next revision.
> BR,
> Jani.
>
>
> > +
> > + 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;
> > +}
> >
> > static const struct drm_edid *lt9611uxc_bridge_edid_read(struct drm_bridge *bridge,
> > struct drm_connector *connector)
> >
> > ---
> > base-commit: 8ef59ee794076e2b58cff357b12de2ba5d441271
> > change-id: 20260517-lt9611usc_edid34_misc_next-b02592de0b25
> >
> > Best regards,
>
> --
> Jani Nikula, Intel