drm/i915/dp: Don't switch the LTTPR mode on an active link
authorjsg <jsg@openbsd.org>
Mon, 5 Aug 2024 02:54:42 +0000 (02:54 +0000)
committerjsg <jsg@openbsd.org>
Mon, 5 Aug 2024 02:54:42 +0000 (02:54 +0000)
From Imre Deak
12880cc086deef91e62e2f010750087a3c23fae3 in linux-6.6.y/6.6.44
509580fad7323b6a5da27e8365cd488f3b57210e in mainline linux

sys/dev/pci/drm/i915/display/intel_dp_link_training.c

index a62bca6..eb5559e 100644 (file)
@@ -114,10 +114,24 @@ intel_dp_set_lttpr_transparent_mode(struct intel_dp *intel_dp, bool enable)
        return drm_dp_dpcd_write(&intel_dp->aux, DP_PHY_REPEATER_MODE, &val, 1) == 1;
 }
 
-static int intel_dp_init_lttpr(struct intel_dp *intel_dp, const u8 dpcd[DP_RECEIVER_CAP_SIZE])
+static bool intel_dp_lttpr_transparent_mode_enabled(struct intel_dp *intel_dp)
+{
+       return intel_dp->lttpr_common_caps[DP_PHY_REPEATER_MODE -
+                                          DP_LT_TUNABLE_PHY_REPEATER_FIELD_DATA_STRUCTURE_REV] ==
+               DP_PHY_REPEATER_MODE_TRANSPARENT;
+}
+
+/*
+ * Read the LTTPR common capabilities and switch the LTTPR PHYs to
+ * non-transparent mode if this is supported. Preserve the
+ * transparent/non-transparent mode on an active link.
+ *
+ * Return the number of detected LTTPRs in non-transparent mode or 0 if the
+ * LTTPRs are in transparent mode or the detection failed.
+ */
+static int intel_dp_init_lttpr_phys(struct intel_dp *intel_dp, const u8 dpcd[DP_RECEIVER_CAP_SIZE])
 {
        int lttpr_count;
-       int i;
 
        if (!intel_dp_read_lttpr_common_caps(intel_dp, dpcd))
                return 0;
@@ -131,6 +145,19 @@ static int intel_dp_init_lttpr(struct intel_dp *intel_dp, const u8 dpcd[DP_RECEI
        if (lttpr_count == 0)
                return 0;
 
+       /*
+        * Don't change the mode on an active link, to prevent a loss of link
+        * synchronization. See DP Standard v2.0 3.6.7. about the LTTPR
+        * resetting its internal state when the mode is changed from
+        * non-transparent to transparent.
+        */
+       if (intel_dp->link_trained) {
+               if (lttpr_count < 0 || intel_dp_lttpr_transparent_mode_enabled(intel_dp))
+                       goto out_reset_lttpr_count;
+
+               return lttpr_count;
+       }
+
        /*
         * See DP Standard v2.0 3.6.6.1. about the explicit disabling of
         * non-transparent mode and the disable->enable non-transparent mode
@@ -151,11 +178,25 @@ static int intel_dp_init_lttpr(struct intel_dp *intel_dp, const u8 dpcd[DP_RECEI
                       "Switching to LTTPR non-transparent LT mode failed, fall-back to transparent mode\n");
 
                intel_dp_set_lttpr_transparent_mode(intel_dp, true);
-               intel_dp_reset_lttpr_count(intel_dp);
 
-               return 0;
+               goto out_reset_lttpr_count;
        }
 
+       return lttpr_count;
+
+out_reset_lttpr_count:
+       intel_dp_reset_lttpr_count(intel_dp);
+
+       return 0;
+}
+
+static int intel_dp_init_lttpr(struct intel_dp *intel_dp, const u8 dpcd[DP_RECEIVER_CAP_SIZE])
+{
+       int lttpr_count;
+       int i;
+
+       lttpr_count = intel_dp_init_lttpr_phys(intel_dp, dpcd);
+
        for (i = 0; i < lttpr_count; i++)
                intel_dp_read_lttpr_phy_caps(intel_dp, dpcd, DP_PHY_LTTPR(i));
 
@@ -1353,10 +1394,10 @@ void intel_dp_start_link_train(struct intel_dp *intel_dp,
 {
        struct drm_i915_private *i915 = dp_to_i915(intel_dp);
        bool passed;
-
        /*
-        * TODO: Reiniting LTTPRs here won't be needed once proper connector
-        * HW state readout is added.
+        * Reinit the LTTPRs here to ensure that they are switched to
+        * non-transparent mode. During an earlier LTTPR detection this
+        * could've been prevented by an active link.
         */
        int lttpr_count = intel_dp_init_lttpr_and_dprx_caps(intel_dp);