When aux->no_zero_sized is set, drm_dp_i2c_xfer() cannot use a bare
address packet to close the transaction, so it clears MOT on the last
data packet instead. The check only looks at the position within the
current i2c_msg, so MOT is cleared at the end of every message and each
one is terminated by an I2C stop.

This breaks E-DDC reads of EDID blocks 2 and up. Those are read as three
messages: a write of the segment pointer to 0x30, a write of the offset
to 0x50, and the read. The stop after the first message resets the
segment pointer, so the read returns segment 0 again.

On a Pimax Dream Air (three EDID blocks, native timings in a DisplayID
extension in block 2) connected to an RTX 5070 Ti with nouveau, block 2
reads back as a copy of block 0. The checksum is valid, so nothing is
reported, and the connector only offers fallback modes.

Clear MOT only on the last packet of the last message, as the commit
that added the option describes. Trailing messages without data are
skipped when looking for that packet, since nothing is sent for them
and the stop would otherwise be lost. With this, block 2 reads
correctly and the native 7104x3840 modes are listed for the Dream Air.

The same problem was pointed out by the Sashiko review of the patch that
sets no_zero_sized for all DisplayPort connectors in nouveau.

Fixes: f0ddbb1eed18 ("drm/dp: add option to disable zero sized address only 
transactions.")
Link: https://lore.kernel.org/all/[email protected]/
Cc: [email protected]
Assisted-by: Claude:claude-opus-5-5
Signed-off-by: Finn Zeruhn <[email protected]>
---
Changes in v2:
- Clear MOT on the last message that carries data, so a transfer that
  ends in a zero-length message still gets its stop.

v1: https://lore.kernel.org/dri-devel/[email protected]/

 drivers/gpu/drm/display/drm_dp_helper.c | 9 +++++++--
 1 file changed, 7 insertions(+), 2 deletions(-)

diff --git a/drivers/gpu/drm/display/drm_dp_helper.c 
b/drivers/gpu/drm/display/drm_dp_helper.c
index 488fd123a01a..dfa64d20ab55 100644
--- a/drivers/gpu/drm/display/drm_dp_helper.c
+++ b/drivers/gpu/drm/display/drm_dp_helper.c
@@ -2149,7 +2149,7 @@ static int drm_dp_i2c_xfer(struct i2c_adapter *adapter, 
struct i2c_msg *msgs,
                           int num)
 {
        struct drm_dp_aux *aux = adapter->algo_data;
-       unsigned int i, j;
+       unsigned int i, j, last;
        unsigned transfer_size;
        struct drm_dp_aux_msg msg;
        int err = 0;
@@ -2159,6 +2159,10 @@ static int drm_dp_i2c_xfer(struct i2c_adapter *adapter, 
struct i2c_msg *msgs,
 
        dp_aux_i2c_transfer_size = clamp(dp_aux_i2c_transfer_size, 1, 
DP_AUX_MAX_PAYLOAD_BYTES);
 
+       last = num - 1;
+       while (last > 0 && !msgs[last].len)
+               last--;
+
        memset(&msg, 0, sizeof(msg));
 
        for (i = 0; i < num; i++) {
@@ -2192,7 +2196,8 @@ static int drm_dp_i2c_xfer(struct i2c_adapter *adapter, 
struct i2c_msg *msgs,
                        msg.buffer = msgs[i].buf + j;
                        msg.size = min(transfer_size, msgs[i].len - j);
 
-                       if (j + msg.size == msgs[i].len && aux->no_zero_sized)
+                       if (aux->no_zero_sized && i == last &&
+                           j + msg.size == msgs[i].len)
                                msg.request &= ~DP_AUX_I2C_MOT;
                        err = drm_dp_i2c_drain_msg(aux, &msg);
 

base-commit: bca45af5998a05f34b13a2ef11e639bac9c62643
-- 
2.56.0

Reply via email to