DisplayPort 2.1 defines the CABLE_ATTRIBUTES_UPDATED_BY_DPRX register
(DPCD 0x2217), in which a UHBRx-capable DPRX reports the UHBR link rates
the attached cable can carry. The DPRX determines this via the CONFIG2
voltage handshake (for a DP receptacle) or by parsing the cable eMarker
(for a USB-C receptacle), and updates the register before asserting HPD.
Add the register definitions and a helper,
drm_dp_128b132b_cable_uhbr_rates(), that reads the register and returns
the set of UHBR rates (DP_UHBR10, DP_UHBR13_5, DP_UHBR20) the cable
permits, and intersect the sink's 128b/132b rates with it. This avoids
attempting link training at a UHBR rate the cable cannot carry and then
falling back, e.g. a UHBR20-capable sink connected through a cable that
only supports UHBR10/UHBR13.5.
A register value of 0 ("not UHBR-capable or capability unknown", or a
sink that does not implement the register) yields all UHBR rates, so no
restriction is imposed and the normal rate fallback applies.
See DP Standard v2.1b, Table 2-260 (DPCD 0x2217).
Signed-off-by: Xiao Lu <[email protected]>
---
drivers/gpu/drm/display/drm_dp_helper.c | 49 +++++++++++++++++++++++++
drivers/gpu/drm/i915/display/intel_dp.c | 7 ++++
include/drm/display/drm_dp.h | 12 ++++++
include/drm/display/drm_dp_helper.h | 1 +
4 files changed, 69 insertions(+)
diff --git a/drivers/gpu/drm/display/drm_dp_helper.c
b/drivers/gpu/drm/display/drm_dp_helper.c
index e623ccb4c..5898d21cc 100644
--- a/drivers/gpu/drm/display/drm_dp_helper.c
+++ b/drivers/gpu/drm/display/drm_dp_helper.c
@@ -382,6 +382,55 @@ int drm_dp_128b132b_read_aux_rd_interval(struct drm_dp_aux
*aux)
}
EXPORT_SYMBOL(drm_dp_128b132b_read_aux_rd_interval);
+/**
+ * drm_dp_128b132b_cable_uhbr_rates() - read the UHBR rates a cable can carry
+ * @aux: DisplayPort AUX channel
+ *
+ * Read the CABLE_ATTRIBUTES_UPDATED_BY_DPRX register (DPCD 0x2217) and return
+ * the set of 128b/132b (UHBR) link rates the attached cable is capable of
+ * carrying, as a mask of %DP_UHBR10, %DP_UHBR13_5 and %DP_UHBR20.
+ *
+ * A UHBRx-capable DPRX updates this register with the cable's capability (via
+ * the CONFIG2 voltage handshake for a DP receptacle, or by parsing the eMarker
+ * for a USB-C receptacle). A source can intersect the returned mask with the
+ * sink's (and its own) supported UHBR rates to avoid attempting link training
+ * at a rate the cable cannot carry, saving the time otherwise spent failing
+ * and falling back.
+ *
+ * A register value of 0 means the cable capability is unknown, or the sink
does
+ * not implement the register. No restriction can be derived in that case, so
+ * all UHBR rates are returned and the caller's normal rate fallback applies.
+ *
+ * Returns a mask of DP_UHBR* rates the cable permits.
+ */
+u8 drm_dp_128b132b_cable_uhbr_rates(struct drm_dp_aux *aux)
+{
+ u8 cable, rates = DP_UHBR10 | DP_UHBR13_5 | DP_UHBR20;
+
+ if (drm_dp_dpcd_read_byte(aux, DP_CABLE_ATTRIBUTES_UPDATED_BY_DPRX,
&cable) < 0)
+ return rates;
+
+ /* Unknown / not implemented: impose no restriction. */
+ if (!cable)
+ return rates;
+
+ rates = 0;
+ switch (cable & DP_CABLE_UHBR10_20_CAPABILITY_MASK) {
+ case DP_CABLE_UHBR10_20_CAPABLE:
+ rates |= DP_UHBR20;
+ fallthrough;
+ case DP_CABLE_UHBR10_CAPABLE:
+ rates |= DP_UHBR10;
+ break;
+ }
+
+ if (cable & DP_CABLE_UHBR13_5_CAPABILITY)
+ rates |= DP_UHBR13_5;
+
+ return rates;
+}
+EXPORT_SYMBOL(drm_dp_128b132b_cable_uhbr_rates);
+
void drm_dp_link_train_clock_recovery_delay(const struct drm_dp_aux *aux,
const u8 dpcd[DP_RECEIVER_CAP_SIZE])
{
diff --git a/drivers/gpu/drm/i915/display/intel_dp.c
b/drivers/gpu/drm/i915/display/intel_dp.c
index 113d767e6..de8f38754 100644
--- a/drivers/gpu/drm/i915/display/intel_dp.c
+++ b/drivers/gpu/drm/i915/display/intel_dp.c
@@ -278,6 +278,13 @@ static void intel_dp_set_dpcd_sink_rates(struct intel_dp
*intel_dp)
}
}
+ /*
+ * Intersect with the UHBR rates the cable can actually carry
+ * (DPCD 0x2217), to avoid training at a rate the cable can't
+ * support. Unknown/unimplemented leaves the rates untouched.
+ */
+ uhbr_rates &= drm_dp_128b132b_cable_uhbr_rates(&intel_dp->aux);
+
if (uhbr_rates & DP_UHBR10)
intel_dp->sink_rates[i++] = 1000000;
if (uhbr_rates & DP_UHBR13_5)
diff --git a/include/drm/display/drm_dp.h b/include/drm/display/drm_dp.h
index e63c7f075..70463572e 100644
--- a/include/drm/display/drm_dp.h
+++ b/include/drm/display/drm_dp.h
@@ -1240,6 +1240,18 @@
# define DP_128B132B_TRAINING_AUX_RD_INTERVAL_32_MS 0x05
# define DP_128B132B_TRAINING_AUX_RD_INTERVAL_64_MS 0x06
+#define DP_CABLE_ATTRIBUTES_UPDATED_BY_DPRX 0x2217 /* 2.1 */
+# define DP_CABLE_UHBR10_20_CAPABILITY_MASK (3 << 0)
+# define DP_CABLE_NOT_UHBR_CAPABLE (0 << 0)
+# define DP_CABLE_UHBR10_CAPABLE (1 << 0)
+# define DP_CABLE_UHBR10_20_CAPABLE (2 << 0)
+# define DP_CABLE_UHBR13_5_CAPABILITY (1 << 2)
+# define DP_CABLE_TYPE_MASK (7 << 3)
+# define DP_CABLE_TYPE_UNKNOWN (0 << 3)
+# define DP_CABLE_TYPE_PASSIVE (1 << 3)
+# define DP_CABLE_TYPE_LRD (2 << 3)
+# define DP_CABLE_TYPE_LTTPR_RETIMER_ACTIVE (3 << 3)
+
#define DP_TEST_264BIT_CUSTOM_PATTERN_7_0 0x2230
#define DP_TEST_264BIT_CUSTOM_PATTERN_263_256 0x2250
diff --git a/include/drm/display/drm_dp_helper.h
b/include/drm/display/drm_dp_helper.h
index 5b05a7fbe..a70530696 100644
--- a/include/drm/display/drm_dp_helper.h
+++ b/include/drm/display/drm_dp_helper.h
@@ -59,6 +59,7 @@ void drm_dp_lttpr_link_train_channel_eq_delay(const struct
drm_dp_aux *aux,
const u8
caps[DP_LTTPR_PHY_CAP_SIZE]);
int drm_dp_128b132b_read_aux_rd_interval(struct drm_dp_aux *aux);
+u8 drm_dp_128b132b_cable_uhbr_rates(struct drm_dp_aux *aux);
bool drm_dp_128b132b_lane_channel_eq_done(const u8
link_status[DP_LINK_STATUS_SIZE],
int lane_count);
bool drm_dp_128b132b_lane_symbol_locked(const u8
link_status[DP_LINK_STATUS_SIZE],
--
2.43.0