drm/i915/dp: Wait for FEC detected status in the sink
authorImre Deak <imre.deak@intel.com>
Tue, 24 Oct 2023 01:09:14 +0000 (04:09 +0300)
committerImre Deak <imre.deak@intel.com>
Wed, 8 Nov 2023 15:22:16 +0000 (17:22 +0200)
As required by the DP standard wait for the sink to detect the FEC
decode enabling symbol sent by the source.

There is a difference between SST and MST when the source enables
the FEC encoding: on SST this happens only after enabling the
transcoder, whereas on MST it happens already after enabling the
transcoder function (before enabling the transcoder). Wait for the
detected status at the earliest spot accordingly.

v2:
- Wait for the FEC detected status on SST after the transcoder is
  enabled.

Reviewed-by: Stanislav Lisovskiy <stanislav.lisovskiy@intel.com>
Signed-off-by: Imre Deak <imre.deak@intel.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20231030155843.2251023-20-imre.deak@intel.com
drivers/gpu/drm/i915/display/intel_ddi.c
drivers/gpu/drm/i915/display/intel_ddi.h
drivers/gpu/drm/i915/display/intel_dp_mst.c

index c6cdac30bb4b6572f78fa794a52891483aa7c092..c2ca459d9ca1472df37341158979430e44194cc3 100644 (file)
@@ -25,6 +25,7 @@
  *
  */
 
+#include <linux/iopoll.h>
 #include <linux/string_helpers.h>
 
 #include <drm/display/drm_scdc_helper.h>
@@ -2220,6 +2221,74 @@ static void intel_dp_sink_set_fec_ready(struct intel_dp *intel_dp,
        if (drm_dp_dpcd_writeb(&intel_dp->aux, DP_FEC_CONFIGURATION, DP_FEC_READY) <= 0)
                drm_dbg_kms(&i915->drm,
                            "Failed to set FEC_READY in the sink\n");
+
+       if (drm_dp_dpcd_writeb(&intel_dp->aux, DP_FEC_STATUS,
+                              DP_FEC_DECODE_EN_DETECTED | DP_FEC_DECODE_DIS_DETECTED) <= 0)
+               drm_dbg_kms(&i915->drm, "Failed to clear FEC detected flags\n");
+}
+
+static int read_fec_detected_status(struct drm_dp_aux *aux)
+{
+       int ret;
+       u8 status;
+
+       ret = drm_dp_dpcd_readb(aux, DP_FEC_STATUS, &status);
+       if (ret < 0)
+               return ret;
+
+       return status;
+}
+
+static void wait_for_fec_detected(struct drm_dp_aux *aux, bool enabled)
+{
+       struct drm_i915_private *i915 = to_i915(aux->drm_dev);
+       int mask = enabled ? DP_FEC_DECODE_EN_DETECTED : DP_FEC_DECODE_DIS_DETECTED;
+       int status;
+       int err;
+
+       err = readx_poll_timeout(read_fec_detected_status, aux, status,
+                                status & mask || status < 0,
+                                10000, 200000);
+
+       if (!err && status >= 0)
+               return;
+
+       if (err == -ETIMEDOUT)
+               drm_err(&i915->drm, "Timeout waiting for FEC %s to get detected\n",
+                       str_enabled_disabled(enabled));
+       else
+               drm_dbg_kms(&i915->drm, "FEC detected status read error: %d\n", status);
+}
+
+void intel_ddi_wait_for_fec_status(struct intel_encoder *encoder,
+                                  const struct intel_crtc_state *crtc_state,
+                                  bool enabled)
+{
+       struct drm_i915_private *i915 = to_i915(crtc_state->uapi.crtc->dev);
+       struct intel_dp *intel_dp = enc_to_intel_dp(encoder);
+       int ret;
+
+       if (!crtc_state->fec_enable)
+               return;
+
+       if (enabled)
+               ret = intel_de_wait_for_set(i915, dp_tp_status_reg(encoder, crtc_state),
+                                           DP_TP_STATUS_FEC_ENABLE_LIVE, 1);
+       else
+               ret = intel_de_wait_for_clear(i915, dp_tp_status_reg(encoder, crtc_state),
+                                             DP_TP_STATUS_FEC_ENABLE_LIVE, 1);
+
+       if (ret)
+               drm_err(&i915->drm,
+                       "Timeout waiting for FEC live state to get %s\n",
+                       str_enabled_disabled(enabled));
+
+       /*
+        * At least the Synoptics MST hub doesn't set the detected flag for
+        * FEC decoding disabling so skip waiting for that.
+        */
+       if (enabled)
+               wait_for_fec_detected(&intel_dp->aux, enabled);
 }
 
 static void intel_ddi_enable_fec(struct intel_encoder *encoder,
@@ -2887,6 +2956,8 @@ static void intel_disable_ddi_buf(struct intel_encoder *encoder,
        } else {
                disable_ddi_buf(encoder, crtc_state);
        }
+
+       intel_ddi_wait_for_fec_status(encoder, crtc_state, false);
 }
 
 static void intel_ddi_post_disable_dp(struct intel_atomic_state *state,
@@ -3262,6 +3333,8 @@ static void intel_enable_ddi(struct intel_atomic_state *state,
 
        intel_enable_transcoder(crtc_state);
 
+       intel_ddi_wait_for_fec_status(encoder, crtc_state, true);
+
        intel_crtc_vblank_on(crtc_state);
 
        if (intel_crtc_has_type(crtc_state, INTEL_OUTPUT_HDMI))
index db845f2aca0c69a991c9f1dd04d60d2633eee5d0..63853a1f6582c83551c8ba1872579c5415ecba4a 100644 (file)
@@ -62,6 +62,9 @@ void intel_ddi_disable_transcoder_func(const struct intel_crtc_state *crtc_state
 void intel_ddi_enable_transcoder_clock(struct intel_encoder *encoder,
                                       const struct intel_crtc_state *crtc_state);
 void intel_ddi_disable_transcoder_clock(const  struct intel_crtc_state *crtc_state);
+void intel_ddi_wait_for_fec_status(struct intel_encoder *encoder,
+                                  const struct intel_crtc_state *crtc_state,
+                                  bool enabled);
 void intel_ddi_set_dp_msa(const struct intel_crtc_state *crtc_state,
                          const struct drm_connector_state *conn_state);
 bool intel_ddi_connector_get_hw_state(struct intel_connector *intel_connector);
index 5214cdb049b30f9938c2ac53740745a0b50b84bd..4d0827a3cd9b1788ace4185addd9ac0193b48862 100644 (file)
@@ -878,6 +878,7 @@ static void intel_mst_enable_dp(struct intel_atomic_state *state,
        struct drm_dp_mst_topology_state *mst_state =
                drm_atomic_get_new_mst_topology_state(&state->base, &intel_dp->mst_mgr);
        enum transcoder trans = pipe_config->cpu_transcoder;
+       bool first_mst_stream = intel_dp->active_mst_links == 1;
 
        drm_WARN_ON(&dev_priv->drm, pipe_config->has_pch_encoder);
 
@@ -904,6 +905,9 @@ static void intel_mst_enable_dp(struct intel_atomic_state *state,
 
        wait_for_act_sent(encoder, pipe_config);
 
+       if (first_mst_stream)
+               intel_ddi_wait_for_fec_status(encoder, pipe_config, true);
+
        drm_dp_add_payload_part2(&intel_dp->mst_mgr, &state->base,
                                 drm_atomic_get_mst_payload_state(mst_state, connector->port));