FEC is a link-wide property: DP_TP_CTL_FEC_ENABLE is a per-port HW bit
while crtc_state->fec_enable is per-stream. With DP MST several streams
share the same port, so if any sibling stream needs FEC the per-port HW
bit is on for every sibling. If sibling crtc_states disagree the
following two symptoms appear:

- intel_pipe_config_compare() rejects fastset on the sibling whose new
  crtc_state->fec_enable disagrees with the old (HW) value
  ("fastset requirement not met in fec_enable"), forcing an
  unnecessary full modeset.
- verify_crtc_state() after commit reports a fec_enable mismatch
  ("[CRTC:..] mismatch in fec_enable (expected no, found yes)") because
  the per-port HW bit is read back into every sibling's hw state.

Walk every MST connector on @mst_mgr, pulling currently-active siblings
into @state if they are not already in it (covers the case where the
user's commit touches only a subset of MST streams on the link). Then OR
all sibling fec_enable values together and write the unified result back
into every sibling crtc_state. The unification only widens
(false -> true), never narrows, so a stream that genuinely needs FEC
keeps it.

This runs from intel_dp_mst_atomic_check_link(), which is invoked after
intel_atomic_check_config_and_link() has finished all per-stream
compute_config and compute_config_late passes but before
intel_crtc_check_fastset() and the post-commit verify, so the unified
value is visible to both checks.

Closes: https://gitlab.freedesktop.org/drm/i915/kernel/-/work_items/16073
Signed-off-by: Arun R Murthy <[email protected]>
Tested-by: Stephen Fuhry <[email protected]>
---
 drivers/gpu/drm/i915/display/intel_dp_mst.c | 70 +++++++++++++++++++++++++++++
 1 file changed, 70 insertions(+)

diff --git a/drivers/gpu/drm/i915/display/intel_dp_mst.c 
b/drivers/gpu/drm/i915/display/intel_dp_mst.c
index 
bcdc504913471a1ac7d255cde49a907c9f3d88a6..d487f1c90dcd2671754e6c6f28f207f32ace9ee2
 100644
--- a/drivers/gpu/drm/i915/display/intel_dp_mst.c
+++ b/drivers/gpu/drm/i915/display/intel_dp_mst.c
@@ -881,6 +881,72 @@ static int intel_dp_mst_check_bw(struct intel_atomic_state 
*state,
        return ret ? : -EAGAIN;
 }
 
+/*
+ * Unify crtc_state->fec_enable across every MST sibling stream on @mst_mgr.
+ */
+static int intel_dp_mst_unify_fec_enable(struct intel_atomic_state *state,
+                                        struct drm_dp_mst_topology_mgr 
*mst_mgr)
+{
+       struct intel_display *display = to_intel_display(state);
+       struct drm_connector_list_iter connector_list_iter;
+       struct intel_connector *connector;
+       struct intel_crtc *crtcs[I915_MAX_PIPES];
+       int n_crtcs = 0;
+       bool need_fec = false;
+       int ret = 0;
+       int i;
+
+       drm_connector_list_iter_begin(display->drm, &connector_list_iter);
+       for_each_intel_connector_iter(connector, &connector_list_iter) {
+               struct intel_digital_connector_state *conn_state;
+               struct intel_crtc_state *crtc_state;
+               struct intel_crtc *crtc;
+
+               if (&connector->mst.dp->mst.mgr != mst_mgr)
+                       continue;
+
+               conn_state = intel_atomic_get_digital_connector_state(state,
+                                                                     
connector);
+               if (IS_ERR(conn_state)) {
+                       ret = PTR_ERR(conn_state);
+                       break;
+               }
+
+               if (!conn_state->base.crtc)
+                       continue;
+
+               crtc = to_intel_crtc(conn_state->base.crtc);
+               crtc_state = intel_atomic_get_crtc_state(&state->base, crtc);
+               if (IS_ERR(crtc_state)) {
+                       ret = PTR_ERR(crtc_state);
+                       break;
+               }
+
+               if (!crtc_state->hw.active)
+                       continue;
+
+               if (drm_WARN_ON(display->drm, n_crtcs >= ARRAY_SIZE(crtcs)))
+                       break;
+
+               crtcs[n_crtcs++] = crtc;
+               if (crtc_state->fec_enable)
+                       need_fec = true;
+       }
+       drm_connector_list_iter_end(&connector_list_iter);
+
+       if (ret || !need_fec)
+               return ret;
+
+       for (i = 0; i < n_crtcs; i++) {
+               struct intel_crtc_state *crtc_state =
+                       intel_atomic_get_new_crtc_state(state, crtcs[i]);
+
+               crtc_state->fec_enable = true;
+       }
+
+       return 0;
+}
+
 /**
  * intel_dp_mst_atomic_check_link - check all modeset MST link configuration
  * @state: intel atomic state
@@ -908,6 +974,10 @@ int intel_dp_mst_atomic_check_link(struct 
intel_atomic_state *state,
        int i;
 
        for_each_new_mst_mgr_in_state(&state->base, mgr, mst_state, i) {
+               ret = intel_dp_mst_unify_fec_enable(state, mgr);
+               if (ret)
+                       return ret;
+
                ret = intel_dp_mst_check_dsc_change(state, mgr, limits);
                if (ret)
                        return ret;

-- 
2.25.1

Reply via email to