drm/i915/dp_mst: Fix MST state after a sink reset
authorImre Deak <imre.deak@intel.com>
Fri, 23 Aug 2024 16:29:18 +0000 (19:29 +0300)
committerJoonas Lahtinen <joonas.lahtinen@linux.intel.com>
Wed, 28 Aug 2024 08:32:25 +0000 (11:32 +0300)
In some cases the sink can reset itself after it was configured into MST
mode, without the driver noticing the disconnected state. For instance
the reset may happen in the middle of a modeset, or the (long) HPD pulse
generated may be not long enough for the encoder detect handler to
observe the HPD's deasserted state. In this case the sink's DPCD
register programmed to enable MST will be reset, while the driver still
assumes MST is still enabled. Detect this condition, which will tear
down and recreate/re-enable the MST topology.

v2:
- Add a code comment about adjusting the expected DP_MSTM_CTRL register
  value for SST + SideBand. (Suraj, Jani)
- Print a debug message about detecting the link reset. (Jani)
- Verify the DPCD MST state only if it wasn't already determined that
  the sink is disconnected.

Cc: stable@vger.kernel.org
Cc: Jani Nikula <jani.nikula@intel.com>
Closes: https://gitlab.freedesktop.org/drm/i915/kernel/-/issues/11195
Reviewed-by: Suraj Kandpal <suraj.kandpal@intel.com> (v1)
Signed-off-by: Imre Deak <imre.deak@intel.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20240823162918.1211875-1-imre.deak@intel.com
(cherry picked from commit 594cf78dc36f31c0c7e0de4567e644f406d46bae)
Signed-off-by: Joonas Lahtinen <joonas.lahtinen@linux.intel.com>
drivers/gpu/drm/i915/display/intel_dp.c
drivers/gpu/drm/i915/display/intel_dp_mst.c
drivers/gpu/drm/i915/display/intel_dp_mst.h

index 59f11af3b0a1dca622cf9e8ecb5be3c715533c22..dc75a929d3ed68db028c2044ed8ebd6f91df877c 100644 (file)
@@ -5935,6 +5935,18 @@ intel_dp_detect(struct drm_connector *connector,
        else
                status = connector_status_disconnected;
 
+       if (status != connector_status_disconnected &&
+           !intel_dp_mst_verify_dpcd_state(intel_dp))
+               /*
+                * This requires retrying detection for instance to re-enable
+                * the MST mode that got reset via a long HPD pulse. The retry
+                * will happen either via the hotplug handler's retry logic,
+                * ensured by setting the connector here to SST/disconnected,
+                * or via a userspace connector probing in response to the
+                * hotplug uevent sent when removing the MST connectors.
+                */
+               status = connector_status_disconnected;
+
        if (status == connector_status_disconnected) {
                memset(&intel_dp->compliance, 0, sizeof(intel_dp->compliance));
                memset(intel_connector->dp.dsc_dpcd, 0, sizeof(intel_connector->dp.dsc_dpcd));
index 27ce5c3f5951e5111933d1c994be89dcde1a4042..17978a1f9ab0a07727ecf29e3da9950287abd5c9 100644 (file)
@@ -1998,3 +1998,43 @@ bool intel_dp_mst_crtc_needs_modeset(struct intel_atomic_state *state,
 
        return false;
 }
+
+/*
+ * intel_dp_mst_verify_dpcd_state - verify the MST SW enabled state wrt. the DPCD
+ * @intel_dp: DP port object
+ *
+ * Verify if @intel_dp's MST enabled SW state matches the corresponding DPCD
+ * state. A long HPD pulse - not long enough to be detected as a disconnected
+ * state - could've reset the DPCD state, which requires tearing
+ * down/recreating the MST topology.
+ *
+ * Returns %true if the SW MST enabled and DPCD states match, %false
+ * otherwise.
+ */
+bool intel_dp_mst_verify_dpcd_state(struct intel_dp *intel_dp)
+{
+       struct intel_display *display = to_intel_display(intel_dp);
+       struct intel_connector *connector = intel_dp->attached_connector;
+       struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp);
+       struct intel_encoder *encoder = &dig_port->base;
+       int ret;
+       u8 val;
+
+       if (!intel_dp->is_mst)
+               return true;
+
+       ret = drm_dp_dpcd_readb(intel_dp->mst_mgr.aux, DP_MSTM_CTRL, &val);
+
+       /* Adjust the expected register value for SST + SideBand. */
+       if (ret < 0 || val != (DP_MST_EN | DP_UP_REQ_EN | DP_UPSTREAM_IS_SRC)) {
+               drm_dbg_kms(display->drm,
+                           "[CONNECTOR:%d:%s][ENCODER:%d:%s] MST mode got reset, removing topology (ret=%d, ctrl=0x%02x)\n",
+                           connector->base.base.id, connector->base.name,
+                           encoder->base.base.id, encoder->base.name,
+                           ret, val);
+
+               return false;
+       }
+
+       return true;
+}
index 8ca1d599091c69e176fe3b600dd4283be64c432f..9e4c7679f1c3a38dc0e86aaad31ca090dc1369c1 100644 (file)
@@ -27,5 +27,6 @@ int intel_dp_mst_atomic_check_link(struct intel_atomic_state *state,
                                   struct intel_link_bw_limits *limits);
 bool intel_dp_mst_crtc_needs_modeset(struct intel_atomic_state *state,
                                     struct intel_crtc *crtc);
+bool intel_dp_mst_verify_dpcd_state(struct intel_dp *intel_dp);
 
 #endif /* __INTEL_DP_MST_H__ */