drm/i915: Fix TypeC mode initialization during system resume
authorImre Deak <imre.deak@intel.com>
Thu, 22 Sep 2022 17:21:48 +0000 (20:21 +0300)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 19 Jul 2023 14:22:16 +0000 (16:22 +0200)
commit a82796a2e332d108b2d3aff38509caad370f69b5 upstream.

During system resume DP MST requires AUX to be working already before
the HW state readout of the given encoder. Since AUX requires the
encoder/PHY TypeC mode to be initialized, which atm only happens during
HW state readout, these AUX transfers can change the TypeC mode
incorrectly (disconnecting the PHY for an enabled encoder) and trigger
the state check WARNs in intel_tc_port_sanitize().

Fix this by initializing the TypeC mode earlier both during driver
loading and system resume and making sure that the mode can't change
until the encoder's state is read out. While at it add the missing
DocBook comments and rename
intel_tc_port_sanitize()->intel_tc_port_sanitize_mode() for consistency.

Signed-off-by: Imre Deak <imre.deak@intel.com>
Reviewed-by: Mika Kahola <mika.kahola@intel.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20220922172148.2913088-1-imre.deak@intel.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/gpu/drm/i915/display/intel_ddi.c
drivers/gpu/drm/i915/display/intel_tc.c
drivers/gpu/drm/i915/display/intel_tc.h

index 69ecf2a..706e2d9 100644 (file)
@@ -3579,7 +3579,7 @@ static void intel_ddi_sync_state(struct intel_encoder *encoder,
        enum phy phy = intel_port_to_phy(i915, encoder->port);
 
        if (intel_phy_is_tc(i915, phy))
-               intel_tc_port_sanitize(enc_to_dig_port(encoder));
+               intel_tc_port_sanitize_mode(enc_to_dig_port(encoder));
 
        if (crtc_state && intel_crtc_has_dp_encoder(crtc_state))
                intel_dp_sync_state(encoder, crtc_state);
@@ -3789,11 +3789,17 @@ static void intel_ddi_encoder_destroy(struct drm_encoder *encoder)
 
 static void intel_ddi_encoder_reset(struct drm_encoder *encoder)
 {
+       struct drm_i915_private *i915 = to_i915(encoder->dev);
        struct intel_dp *intel_dp = enc_to_intel_dp(to_intel_encoder(encoder));
+       struct intel_digital_port *dig_port = enc_to_dig_port(to_intel_encoder(encoder));
+       enum phy phy = intel_port_to_phy(i915, dig_port->base.port);
 
        intel_dp->reset_link_params = true;
 
        intel_pps_encoder_reset(intel_dp);
+
+       if (intel_phy_is_tc(i915, phy))
+               intel_tc_port_init_mode(dig_port);
 }
 
 static const struct drm_encoder_funcs intel_ddi_funcs = {
index 8d6dac3..eecdf5a 100644 (file)
@@ -687,18 +687,58 @@ static void
 intel_tc_port_link_init_refcount(struct intel_digital_port *dig_port,
                                 int refcount)
 {
+       dig_port->tc_link_refcount = refcount;
+}
+
+/**
+ * intel_tc_port_init_mode: Read out HW state and init the given port's TypeC mode
+ * @dig_port: digital port
+ *
+ * Read out the HW state and initialize the TypeC mode of @dig_port. The mode
+ * will be locked until intel_tc_port_sanitize_mode() is called.
+ */
+void intel_tc_port_init_mode(struct intel_digital_port *dig_port)
+{
        struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev);
+       intel_wakeref_t tc_cold_wref;
+       enum intel_display_power_domain domain;
+
+       mutex_lock(&dig_port->tc_lock);
 
+       drm_WARN_ON(&i915->drm, dig_port->tc_mode != TC_PORT_DISCONNECTED);
+       drm_WARN_ON(&i915->drm, dig_port->tc_lock_wakeref);
        drm_WARN_ON(&i915->drm, dig_port->tc_link_refcount);
-       dig_port->tc_link_refcount = refcount;
+
+       tc_cold_wref = tc_cold_block(dig_port, &domain);
+
+       dig_port->tc_mode = intel_tc_port_get_current_mode(dig_port);
+       /* Prevent changing dig_port->tc_mode until intel_tc_port_sanitize_mode() is called. */
+       intel_tc_port_link_init_refcount(dig_port, 1);
+       dig_port->tc_lock_wakeref = tc_cold_block(dig_port, &dig_port->tc_lock_power_domain);
+
+       tc_cold_unblock(dig_port, domain, tc_cold_wref);
+
+       drm_dbg_kms(&i915->drm, "Port %s: init mode (%s)\n",
+                   dig_port->tc_port_name,
+                   tc_port_mode_name(dig_port->tc_mode));
+
+       mutex_unlock(&dig_port->tc_lock);
 }
 
-void intel_tc_port_sanitize(struct intel_digital_port *dig_port)
+/**
+ * intel_tc_port_sanitize_mode: Sanitize the given port's TypeC mode
+ * @dig_port: digital port
+ *
+ * Sanitize @dig_port's TypeC mode wrt. the encoder's state right after driver
+ * loading and system resume:
+ * If the encoder is enabled keep the TypeC mode/PHY connected state locked until
+ * the encoder is disabled.
+ * If the encoder is disabled make sure the PHY is disconnected.
+ */
+void intel_tc_port_sanitize_mode(struct intel_digital_port *dig_port)
 {
        struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev);
        struct intel_encoder *encoder = &dig_port->base;
-       intel_wakeref_t tc_cold_wref;
-       enum intel_display_power_domain domain;
        int active_links = 0;
 
        mutex_lock(&dig_port->tc_lock);
@@ -708,21 +748,14 @@ void intel_tc_port_sanitize(struct intel_digital_port *dig_port)
        else if (encoder->base.crtc)
                active_links = to_intel_crtc(encoder->base.crtc)->active;
 
-       drm_WARN_ON(&i915->drm, dig_port->tc_mode != TC_PORT_DISCONNECTED);
-       drm_WARN_ON(&i915->drm, dig_port->tc_lock_wakeref);
-
-       tc_cold_wref = tc_cold_block(dig_port, &domain);
+       drm_WARN_ON(&i915->drm, dig_port->tc_link_refcount != 1);
+       intel_tc_port_link_init_refcount(dig_port, active_links);
 
-       dig_port->tc_mode = intel_tc_port_get_current_mode(dig_port);
        if (active_links) {
                if (!icl_tc_phy_is_connected(dig_port))
                        drm_dbg_kms(&i915->drm,
                                    "Port %s: PHY disconnected with %d active link(s)\n",
                                    dig_port->tc_port_name, active_links);
-               intel_tc_port_link_init_refcount(dig_port, active_links);
-
-               dig_port->tc_lock_wakeref = tc_cold_block(dig_port,
-                                                         &dig_port->tc_lock_power_domain);
        } else {
                /*
                 * TBT-alt is the default mode in any case the PHY ownership is not
@@ -736,9 +769,10 @@ void intel_tc_port_sanitize(struct intel_digital_port *dig_port)
                                    dig_port->tc_port_name,
                                    tc_port_mode_name(dig_port->tc_mode));
                icl_tc_phy_disconnect(dig_port);
-       }
 
-       tc_cold_unblock(dig_port, domain, tc_cold_wref);
+               tc_cold_unblock(dig_port, dig_port->tc_lock_power_domain,
+                               fetch_and_zero(&dig_port->tc_lock_wakeref));
+       }
 
        drm_dbg_kms(&i915->drm, "Port %s: sanitize mode (%s)\n",
                    dig_port->tc_port_name,
@@ -923,4 +957,6 @@ void intel_tc_port_init(struct intel_digital_port *dig_port, bool is_legacy)
        dig_port->tc_mode = TC_PORT_DISCONNECTED;
        dig_port->tc_link_refcount = 0;
        tc_port_load_fia_params(i915, dig_port);
+
+       intel_tc_port_init_mode(dig_port);
 }
index 6b47b29..d54082e 100644 (file)
@@ -24,7 +24,8 @@ int intel_tc_port_fia_max_lane_count(struct intel_digital_port *dig_port);
 void intel_tc_port_set_fia_lane_count(struct intel_digital_port *dig_port,
                                      int required_lanes);
 
-void intel_tc_port_sanitize(struct intel_digital_port *dig_port);
+void intel_tc_port_init_mode(struct intel_digital_port *dig_port);
+void intel_tc_port_sanitize_mode(struct intel_digital_port *dig_port);
 void intel_tc_port_lock(struct intel_digital_port *dig_port);
 void intel_tc_port_unlock(struct intel_digital_port *dig_port);
 void intel_tc_port_flush_work(struct intel_digital_port *dig_port);