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

Reply via email to