> -----Original Message-----
> From: Intel-gfx <intel-gfx-boun...@lists.freedesktop.org> On Behalf Of Imre
> Deak
> Sent: Thursday, September 22, 2022 8:22 PM
> To: intel-gfx@lists.freedesktop.org
> Subject: [Intel-gfx] [PATCH] drm/i915: Fix TypeC mode initialization during
> system resume
> 
> 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.d...@intel.com>
> ---
>  drivers/gpu/drm/i915/display/intel_ddi.c |  8 ++-
> drivers/gpu/drm/i915/display/intel_tc.c  | 68 ++++++++++++++++++------
> drivers/gpu/drm/i915/display/intel_tc.h  |  3 +-
>  3 files changed, 61 insertions(+), 18 deletions(-)
> 
> diff --git a/drivers/gpu/drm/i915/display/intel_ddi.c
> b/drivers/gpu/drm/i915/display/intel_ddi.c
> index 643832d55c282..5ce5e885694c8 100644
> --- a/drivers/gpu/drm/i915/display/intel_ddi.c
> +++ b/drivers/gpu/drm/i915/display/intel_ddi.c
> @@ -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); @@ -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 = { diff --git
> a/drivers/gpu/drm/i915/display/intel_tc.c
> b/drivers/gpu/drm/i915/display/intel_tc.c
> index e5af955b5600f..b0aa1edd83028 100644
> --- a/drivers/gpu/drm/i915/display/intel_tc.c
> +++ b/drivers/gpu/drm/i915/display/intel_tc.c
> @@ -687,18 +687,58 @@ static void
>  intel_tc_port_link_init_refcount(struct intel_digital_port *dig_port,
>                                int refcount)
>  {
> -     struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev);
> -
> -     drm_WARN_ON(&i915->drm, dig_port->tc_link_refcount);

I was thinking whether we should drop this function or not? This is now reduced 
as one liner.

Anyway, patch looks ok to me.

Reviewed-by: Mika Kahola <mika.kah...@intel.com>

>       dig_port->tc_link_refcount = refcount;  }
> 
> -void intel_tc_port_sanitize(struct intel_digital_port *dig_port)
> +/**
> + * 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);
> -     struct intel_encoder *encoder = &dig_port->base;
>       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);
> +
> +     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);
> +}
> +
> +/**
> + * 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;
>       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);
> +     drm_WARN_ON(&i915->drm, dig_port->tc_link_refcount != 1);
> +     intel_tc_port_link_init_refcount(dig_port, active_links);
> 
> -     tc_cold_wref = tc_cold_block(dig_port, &domain);
> -
> -     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);
>  }
> diff --git a/drivers/gpu/drm/i915/display/intel_tc.h
> b/drivers/gpu/drm/i915/display/intel_tc.h
> index 6b47b29f551c9..d54082e2d5e8d 100644
> --- a/drivers/gpu/drm/i915/display/intel_tc.h
> +++ b/drivers/gpu/drm/i915/display/intel_tc.h
> @@ -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);
> --
> 2.37.1

Reply via email to