Disable 1 and 2 lane link configurations on UHBR tunneled links,
according to DP Standard v2.1b "3.5.2.16.3 128b/132b DPRX Lane Count
Conversion Failure Indication and Corrective Action". A tunnel can
indicate if it's not affected by this limitation, check for that and
skip disabling the unsupported lanes for a well-behaving tunnel.

The Standard in the same section also describes a workaround for 2 lanes
which requires assisstance from the sink, where the sink indicates at
the end of the link training sequence if the link training must be
retried. This mechanism also requires quirking out some sinks - based on
the sink's DPCD OUI and EDID identifications - which doesn't implement
this link training feedback indication properly. This patch leaves the
implementation of this workaround for a follow-up, but prepares for it
already by detecting the supported number of lane counts at a place
where both DPCD OUI and EDID is available for the quirk detection.

Signed-off-by: Imre Deak <[email protected]>
---
 .../drm/i915/display/intel_display_types.h    |  1 +
 drivers/gpu/drm/i915/display/intel_dp.c       |  7 +++
 drivers/gpu/drm/i915/display/intel_dp_mst.c   |  5 +++
 .../gpu/drm/i915/display/intel_dp_tunnel.c    | 45 +++++++++++++++++++
 .../gpu/drm/i915/display/intel_dp_tunnel.h    | 17 +++++++
 5 files changed, 75 insertions(+)

diff --git a/drivers/gpu/drm/i915/display/intel_display_types.h 
b/drivers/gpu/drm/i915/display/intel_display_types.h
index c048da7d6fea7..adde53dc9dce3 100644
--- a/drivers/gpu/drm/i915/display/intel_display_types.h
+++ b/drivers/gpu/drm/i915/display/intel_display_types.h
@@ -1871,6 +1871,7 @@ struct intel_dp {
 
        struct drm_dp_tunnel *tunnel;
        bool tunnel_suspended:1;
+       u8 disabled_uhbr_lane_mask;
 
        struct {
                struct intel_dp_mst_encoder *stream_encoders[I915_MAX_PIPES];
diff --git a/drivers/gpu/drm/i915/display/intel_dp.c 
b/drivers/gpu/drm/i915/display/intel_dp.c
index 0d2c696b9e4b0..e0b95fbf7db0a 100644
--- a/drivers/gpu/drm/i915/display/intel_dp.c
+++ b/drivers/gpu/drm/i915/display/intel_dp.c
@@ -3629,6 +3629,8 @@ void intel_dp_reset_link_params(struct intel_dp *intel_dp)
         * was called.
         */
        intel_dp_link_caps_reset(intel_dp->link.caps);
+       intel_dp_tunnel_uhbr_lanes_wa_apply(intel_dp);
+
        intel_dp->link.mst_probed_lane_count = 0;
        intel_dp->link.mst_probed_rate = 0;
        intel_dp_link_training_reset(intel_dp->link.training);
@@ -6281,6 +6283,8 @@ intel_dp_detect(struct drm_connector *_connector,
 
                intel_dp_tunnel_disconnect(intel_dp);
 
+               intel_dp_tunnel_uhbr_lanes_wa_reset(intel_dp);
+
                goto out_unset_edid;
        }
 
@@ -6345,6 +6349,9 @@ intel_dp_detect(struct drm_connector *_connector,
        if (intel_dp_is_edp(intel_dp) || connector->detect_edid)
                status = connector_status_connected;
 
+       if (intel_dp_tunnel_uhbr_lanes_wa_setup(intel_dp))
+               intel_dp_tunnel_uhbr_lanes_wa_apply(intel_dp);
+
 out_unset_edid:
        if (status != connector_status_connected && !intel_dp->is_mst)
                intel_dp_unset_edid(intel_dp);
diff --git a/drivers/gpu/drm/i915/display/intel_dp_mst.c 
b/drivers/gpu/drm/i915/display/intel_dp_mst.c
index df6e54508e5d4..7cbef16ab38b1 100644
--- a/drivers/gpu/drm/i915/display/intel_dp_mst.c
+++ b/drivers/gpu/drm/i915/display/intel_dp_mst.c
@@ -1453,6 +1453,11 @@ static int mst_connector_get_ddc_modes(struct 
drm_connector *_connector)
 
        drm_edid_free(drm_edid);
 
+       if (intel_dp_tunnel_uhbr_lanes_wa_setup(intel_dp)) {
+               intel_dp_flush_connector_commits(connector);
+               intel_dp_tunnel_uhbr_lanes_wa_apply(intel_dp);
+       }
+
        return ret;
 }
 
diff --git a/drivers/gpu/drm/i915/display/intel_dp_tunnel.c 
b/drivers/gpu/drm/i915/display/intel_dp_tunnel.c
index 49fa4c9699b61..2199396345646 100644
--- a/drivers/gpu/drm/i915/display/intel_dp_tunnel.c
+++ b/drivers/gpu/drm/i915/display/intel_dp_tunnel.c
@@ -831,6 +831,51 @@ void intel_dp_tunnel_atomic_alloc_bw(struct 
intel_atomic_state *state)
        atomic_increase_bw(state);
 }
 
+static u8 lane_count_mask(int lane_count)
+{
+       return BIT(ilog2(lane_count));
+}
+
+void intel_dp_tunnel_uhbr_lanes_wa_apply(struct intel_dp *intel_dp)
+{
+       struct intel_connector *connector = intel_dp->attached_connector;
+       struct intel_dp_link_caps_order order =
+               intel_dp_link_caps_connector_compute_order(connector);
+       struct intel_dp_link_caps *link_caps = intel_dp->link.caps;
+       struct intel_dp_link_config link_config;
+       struct intel_dp_link_caps_iter iter;
+
+       if (!intel_dp->disabled_uhbr_lane_mask)
+               return;
+
+       intel_dp_link_caps_iter_start(&iter, link_caps, order, 
INTEL_DP_LINK_CAPS_FILTER_ALL);
+       for_each_dp_link_config(&iter, &link_config) {
+               if (drm_dp_is_uhbr_rate(link_config.rate) &&
+                   lane_count_mask(link_config.lane_count) & 
intel_dp->disabled_uhbr_lane_mask)
+                       intel_dp_link_caps_disable_config(link_caps, 
&link_config);
+       }
+       intel_dp_link_caps_iter_end(&iter);
+}
+
+bool intel_dp_tunnel_uhbr_lanes_wa_setup(struct intel_dp *intel_dp)
+{
+       u8 old_mask = intel_dp->disabled_uhbr_lane_mask;
+
+       if (!intel_dp_tunnel_bw_alloc_is_enabled(intel_dp) ||
+           drm_dp_tunnel_128b132b_lane0_mapping_supported(intel_dp->tunnel))
+               intel_dp->disabled_uhbr_lane_mask = 0;
+       else
+               /* TODO: Add support for keeping 2 lanes enabled as well. */
+               intel_dp->disabled_uhbr_lane_mask = lane_count_mask(1) | 
lane_count_mask(2);
+
+       return intel_dp->disabled_uhbr_lane_mask != old_mask;
+}
+
+void intel_dp_tunnel_uhbr_lanes_wa_reset(struct intel_dp *intel_dp)
+{
+       intel_dp->disabled_uhbr_lane_mask = 0;
+}
+
 /**
  * intel_dp_tunnel_mgr_init - Initialize the DP tunnel manager
  * @display: display device
diff --git a/drivers/gpu/drm/i915/display/intel_dp_tunnel.h 
b/drivers/gpu/drm/i915/display/intel_dp_tunnel.h
index 8273e681a5128..13fc8fbb6e306 100644
--- a/drivers/gpu/drm/i915/display/intel_dp_tunnel.h
+++ b/drivers/gpu/drm/i915/display/intel_dp_tunnel.h
@@ -54,6 +54,10 @@ int intel_dp_tunnel_atomic_check_state(struct 
intel_atomic_state *state,
 
 void intel_dp_tunnel_atomic_alloc_bw(struct intel_atomic_state *state);
 
+void intel_dp_tunnel_uhbr_lanes_wa_apply(struct intel_dp *intel_dp);
+bool intel_dp_tunnel_uhbr_lanes_wa_setup(struct intel_dp *intel_dp);
+void intel_dp_tunnel_uhbr_lanes_wa_reset(struct intel_dp *intel_dp);
+
 int intel_dp_tunnel_mgr_init(struct intel_display *display);
 void intel_dp_tunnel_mgr_cleanup(struct intel_display *display);
 
@@ -129,6 +133,19 @@ intel_dp_tunnel_atomic_alloc_bw(struct intel_atomic_state 
*state)
        return 0;
 }
 
+static inline void intel_dp_tunnel_uhbr_lanes_wa_apply(struct intel_dp 
*intel_dp)
+{
+}
+
+static inline bool intel_dp_tunnel_uhbr_lanes_wa_setup(struct intel_dp 
*intel_dp)
+{
+       return false;
+}
+
+static inline void intel_dp_tunnel_uhbr_lanes_wa_reset(struct intel_dp 
*intel_dp)
+{
+}
+
 static inline int
 intel_dp_tunnel_mgr_init(struct intel_display *display)
 {
-- 
2.49.1

Reply via email to