On Fri, May 29, 2026 at 04:13:41PM +0300, Jani Nikula wrote:
> On Fri, 09 Jan 2026, Mario Limonciello <[email protected]> wrote:
> > On 12/3/25 8:46 PM, Chia-Lin Kao (AceLan) wrote:
> >> Some USB-C hubs and adapters have buggy firmware where multi-byte AUX
> >> reads consistently timeout, while single-byte reads from the same address
> >> work correctly.
> >>
> >> Known affected devices that exhibit this issue:
> >> - Lenovo USB-C to VGA adapter (VIA VL817 chipset)
> >> idVendor=17ef, idProduct=7217
> >> - Dell DA310 USB-C mobile adapter hub
> >> idVendor=413c, idProduct=c010
> >>
> >> Analysis of the failure pattern shows:
> >> - Single-byte probes to 0xf0000 (LTTPR) succeed
> >> - Single-byte probes to 0x00102 (TRAINING_AUX_RD_INTERVAL) succeed
> >> - Multi-byte reads from 0x00000 (DPCD capabilities) timeout with -ETIMEDOUT
> >> - Retrying does not help - the failure is consistent across all attempts
> >>
> >> The issue appears to be a firmware bug in the AUX transaction handling
> >> that specifically affects multi-byte reads.
> >>
> >> Add a fallback mechanism in drm_dp_dpcd_read_data() that attempts
> >> byte-by-byte reading when the normal multi-byte read fails. This
> >> workaround only activates for adapters that fail the standard read path,
> >> ensuring no impact on correctly functioning hardware.
> >>
> >> Tested with:
> >> - Lenovo USB-C to VGA adapter (VIA VL817) - now works with fallback
> >> - Dell DA310 USB-C hub - now works with fallback
> >> - Dell/Analogix Slimport adapter - continues to work with normal path
> >>
> >> Signed-off-by: Chia-Lin Kao (AceLan) <[email protected]>
> >
> > Reviewed-by: Mario Limonciello (AMD) <[email protected]>
> >
> > As this fixes reads for some existing hardware on the market and is just
> > in fallback path I feel this is low risk. I've applied this to
> > drm-misc-fixes.
>
> I've stumbled on this when I was looking at drm_dp_dpcd_read_data(). I
> never received the original patch, for whatever reason, even though Lore
> says I was Cc'd.
This should be my problem that some receivers rejects my email,
because I'm using another email account to send the email.
I still have issues to send the email via company email account, and
hope this email won't be rejected.
>
> > a8f49a0043011 (HEAD -> drm-misc-fixes) drm/dp: Add byte-by-byte fallback
> > for broken USB-C adapters
> >
> >> ---
> >> v2. 1. Move the workaround from intel_dp_read_dprx_caps() to
> >> drm_dp_dpcd_read_data(), so that it applies to all DPCD reads
> >> across
> >> all DRM drivers benefit from this fix, not just i915.
> >> 2. Move the definition of drm_dp_dpcd_readb() before
> >> drm_dp_dpcd_read_data()
> >> ---
> >> include/drm/display/drm_dp_helper.h | 57 +++++++++++++++++++----------
> >> 1 file changed, 37 insertions(+), 20 deletions(-)
> >>
> >> diff --git a/include/drm/display/drm_dp_helper.h
> >> b/include/drm/display/drm_dp_helper.h
> >> index df2f24b950e4..14d2859f0bda 100644
> >> --- a/include/drm/display/drm_dp_helper.h
> >> +++ b/include/drm/display/drm_dp_helper.h
> >> @@ -551,6 +551,22 @@ ssize_t drm_dp_dpcd_read(struct drm_dp_aux *aux,
> >> unsigned int offset,
> >> ssize_t drm_dp_dpcd_write(struct drm_dp_aux *aux, unsigned int offset,
> >> void *buffer, size_t size);
> >>
> >> +/**
> >> + * drm_dp_dpcd_readb() - read a single byte from the DPCD
> >> + * @aux: DisplayPort AUX channel
> >> + * @offset: address of the register to read
> >> + * @valuep: location where the value of the register will be stored
> >> + *
> >> + * Returns the number of bytes transferred (1) on success, or a negative
> >> + * error code on failure. In most of the cases you should be using
> >> + * drm_dp_dpcd_read_byte() instead.
> >> + */
> >> +static inline ssize_t drm_dp_dpcd_readb(struct drm_dp_aux *aux,
> >> + unsigned int offset, u8 *valuep)
> >> +{
> >> + return drm_dp_dpcd_read(aux, offset, valuep, 1);
> >> +}
> >> +
> >> /**
> >> * drm_dp_dpcd_read_data() - read a series of bytes from the DPCD
> >> * @aux: DisplayPort AUX channel (SST or MST)
> >> @@ -570,12 +586,29 @@ static inline int drm_dp_dpcd_read_data(struct
> >> drm_dp_aux *aux,
> >> void *buffer, size_t size)
> >> {
> >> int ret;
> >> + size_t i;
> >> + u8 *buf = buffer;
> >>
> >> ret = drm_dp_dpcd_read(aux, offset, buffer, size);
> >> - if (ret < 0)
> >> - return ret;
> >> - if (ret < size)
> >> - return -EPROTO;
> >> + if (ret >= 0) {
> >> + if (ret < size)
> >> + return -EPROTO;
> >> + return 0;
> >> + }
> >> +
> >> + /*
> >> + * Workaround for USB-C hubs/adapters with buggy firmware that fail
> >> + * multi-byte AUX reads but work with single-byte reads.
> >> + * Known affected devices:
> >> + * - Lenovo USB-C to VGA adapter (VIA VL817, idVendor=17ef,
> >> idProduct=7217)
> >> + * - Dell DA310 USB-C hub (idVendor=413c, idProduct=c010)
> >> + * Attempt byte-by-byte reading as a fallback.
> >> + */
> >> + for (i = 0; i < size; i++) {
> >> + ret = drm_dp_dpcd_readb(aux, offset + i, &buf[i]);
>
> drm_dp_dpcd_read_byte() should be preferred over drm_dp_dpcd_readb()...
>
> >> + if (ret < 0)
>
> ...because drm_dp_dpcd_readb() might return 0 on failures. You need to
> use drm_dp_dpcd_readb() == 1 to check for success, which is why
> drm_dp_dpcd_read_byte() and drm_dp_dpcd_read_data() were introduced in
> the first place.
>
> Moreover, this ugly workaround only impacts drm_dp_dpcd_read_data()
> callers, but there are lots and lots of direct drm_dp_dpcd_read() calls
> all over the place, which go unfixed.
>
> It should be emphasized that DP AUX changes that affect absolutely all
> drivers should go through more scrutiny, and require more acks.
>
> This needs follow-up fixes.
I'll do some study and submit the follow-up fixes later.
>
>
> BR,
> Jani.
>
>
> >> + return ret;
> >> + }
> >>
> >> return 0;
> >> }
> >> @@ -609,22 +642,6 @@ static inline int drm_dp_dpcd_write_data(struct
> >> drm_dp_aux *aux,
> >> return 0;
> >> }
> >>
> >> -/**
> >> - * drm_dp_dpcd_readb() - read a single byte from the DPCD
> >> - * @aux: DisplayPort AUX channel
> >> - * @offset: address of the register to read
> >> - * @valuep: location where the value of the register will be stored
> >> - *
> >> - * Returns the number of bytes transferred (1) on success, or a negative
> >> - * error code on failure. In most of the cases you should be using
> >> - * drm_dp_dpcd_read_byte() instead.
> >> - */
> >> -static inline ssize_t drm_dp_dpcd_readb(struct drm_dp_aux *aux,
> >> - unsigned int offset, u8 *valuep)
> >> -{
> >> - return drm_dp_dpcd_read(aux, offset, valuep, 1);
> >> -}
> >> -
> >> /**
> >> * drm_dp_dpcd_writeb() - write a single byte to the DPCD
> >> * @aux: DisplayPort AUX channel
> >
>
> --
> Jani Nikula, Intel