Skip to content

Commit 5954acb

Browse files
srettvsyrjala
authored andcommitted
drm/display: Don't assume dual mode adaptors support i2c sub-addressing
Current dual mode adaptor ("DP++") detection code assumes that all adaptors support i2c sub-addressing for read operations from the DP-HDMI adaptor ID buffer. It has been observed that multiple adaptors do not in fact support this, and always return data starting at register 0. On affected adaptors, the code fails to read the proper registers that would identify the device as a type 2 adaptor, and handles those as type 1, limiting the TMDS clock to 165MHz, even if the according register would announce a higher TMDS clock. Fix this by always reading the ID buffer starting from offset 0, and discarding any bytes before the actual offset of interest. We tried finding authoritative documentation on whether or not this is allowed behaviour, but since all the official VESA docs are paywalled, the best we could come up with was the spec sheet for Texas Instruments' SNx5DP149 chip family.[1] It explicitly mentions that sub-addressing is supported for register writes, but *not* for reads (See NOTE in section 8.5.3). Unless TI openly decided to violate the VESA spec, one could take that as a hint that sub-addressing is in fact not mandated by VESA. The other two adaptors affected used the PS8409(A) and the LT8611, according to the data returned from their ID buffers. [1] https://www.ti.com/lit/ds/symlink/sn75dp149.pdf Cc: [email protected] Signed-off-by: Simon Rettberg <[email protected]> Reviewed-by: Rafael Gieschke <[email protected]> Signed-off-by: Ville Syrjälä <[email protected]> Link: https://patchwork.freedesktop.org/patch/msgid/20221006113314.41101987@computer Acked-by: Jani Nikula <[email protected]>
1 parent 0a3e0fb commit 5954acb

File tree

1 file changed

+29
-22
lines changed

1 file changed

+29
-22
lines changed

drivers/gpu/drm/display/drm_dp_dual_mode_helper.c

Lines changed: 29 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -63,23 +63,45 @@
6363
ssize_t drm_dp_dual_mode_read(struct i2c_adapter *adapter,
6464
u8 offset, void *buffer, size_t size)
6565
{
66+
u8 zero = 0;
67+
char *tmpbuf = NULL;
68+
/*
69+
* As sub-addressing is not supported by all adaptors,
70+
* always explicitly read from the start and discard
71+
* any bytes that come before the requested offset.
72+
* This way, no matter whether the adaptor supports it
73+
* or not, we'll end up reading the proper data.
74+
*/
6675
struct i2c_msg msgs[] = {
6776
{
6877
.addr = DP_DUAL_MODE_SLAVE_ADDRESS,
6978
.flags = 0,
7079
.len = 1,
71-
.buf = &offset,
80+
.buf = &zero,
7281
},
7382
{
7483
.addr = DP_DUAL_MODE_SLAVE_ADDRESS,
7584
.flags = I2C_M_RD,
76-
.len = size,
85+
.len = size + offset,
7786
.buf = buffer,
7887
},
7988
};
8089
int ret;
8190

91+
if (offset) {
92+
tmpbuf = kmalloc(size + offset, GFP_KERNEL);
93+
if (!tmpbuf)
94+
return -ENOMEM;
95+
96+
msgs[1].buf = tmpbuf;
97+
}
98+
8299
ret = i2c_transfer(adapter, msgs, ARRAY_SIZE(msgs));
100+
if (tmpbuf)
101+
memcpy(buffer, tmpbuf + offset, size);
102+
103+
kfree(tmpbuf);
104+
83105
if (ret < 0)
84106
return ret;
85107
if (ret != ARRAY_SIZE(msgs))
@@ -208,18 +230,6 @@ enum drm_dp_dual_mode_type drm_dp_dual_mode_detect(const struct drm_device *dev,
208230
if (ret)
209231
return DRM_DP_DUAL_MODE_UNKNOWN;
210232

211-
/*
212-
* Sigh. Some (maybe all?) type 1 adaptors are broken and ack
213-
* the offset but ignore it, and instead they just always return
214-
* data from the start of the HDMI ID buffer. So for a broken
215-
* type 1 HDMI adaptor a single byte read will always give us
216-
* 0x44, and for a type 1 DVI adaptor it should give 0x00
217-
* (assuming it implements any registers). Fortunately neither
218-
* of those values will match the type 2 signature of the
219-
* DP_DUAL_MODE_ADAPTOR_ID register so we can proceed with
220-
* the type 2 adaptor detection safely even in the presence
221-
* of broken type 1 adaptors.
222-
*/
223233
ret = drm_dp_dual_mode_read(adapter, DP_DUAL_MODE_ADAPTOR_ID,
224234
&adaptor_id, sizeof(adaptor_id));
225235
drm_dbg_kms(dev, "DP dual mode adaptor ID: %02x (err %zd)\n", adaptor_id, ret);
@@ -233,11 +243,10 @@ enum drm_dp_dual_mode_type drm_dp_dual_mode_detect(const struct drm_device *dev,
233243
return DRM_DP_DUAL_MODE_TYPE2_DVI;
234244
}
235245
/*
236-
* If neither a proper type 1 ID nor a broken type 1 adaptor
237-
* as described above, assume type 1, but let the user know
238-
* that we may have misdetected the type.
246+
* If not a proper type 1 ID, still assume type 1, but let
247+
* the user know that we may have misdetected the type.
239248
*/
240-
if (!is_type1_adaptor(adaptor_id) && adaptor_id != hdmi_id[0])
249+
if (!is_type1_adaptor(adaptor_id))
241250
drm_err(dev, "Unexpected DP dual mode adaptor ID %02x\n", adaptor_id);
242251

243252
}
@@ -343,10 +352,8 @@ EXPORT_SYMBOL(drm_dp_dual_mode_get_tmds_output);
343352
* @enable: enable (as opposed to disable) the TMDS output buffers
344353
*
345354
* Set the state of the TMDS output buffers in the adaptor. For
346-
* type2 this is set via the DP_DUAL_MODE_TMDS_OEN register. As
347-
* some type 1 adaptors have problems with registers (see comments
348-
* in drm_dp_dual_mode_detect()) we avoid touching the register,
349-
* making this function a no-op on type 1 adaptors.
355+
* type2 this is set via the DP_DUAL_MODE_TMDS_OEN register.
356+
* Type1 adaptors do not support any register writes.
350357
*
351358
* Returns:
352359
* 0 on success, negative error code on failure

0 commit comments

Comments
 (0)