]> www.infradead.org Git - users/dwmw2/linux.git/commitdiff
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)
committerImre Deak <imre.deak@intel.com>
Tue, 27 Sep 2022 13:04:06 +0000 (16:04 +0300)
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
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 0301a7745ad091132690f77c823b04a8e06f5ae2..971356237eca36213069440243ea5ff6efe65d37 100644 (file)
@@ -3591,7 +3591,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);
@@ -3801,11 +3801,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 e5af955b5600f0fefe0da6b5753e3423916cc888..b0aa1edd830289b5d317a988c96940dbc3141b4f 100644 (file)
@@ -686,19 +686,59 @@ static void intel_tc_port_update_mode(struct intel_digital_port *dig_port,
 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 6b47b29f551c937dfa19a1fd6a885385774f7136..d54082e2d5e8d75ea4a22929a525670961322d3f 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);