Commit a82796a2 authored by Imre Deak's avatar Imre Deak

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: default avatarImre Deak <imre.deak@intel.com>
Reviewed-by: default avatarMika Kahola <mika.kahola@intel.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20220922172148.2913088-1-imre.deak@intel.com
parent e731a2d2
...@@ -3591,7 +3591,7 @@ static void intel_ddi_sync_state(struct intel_encoder *encoder, ...@@ -3591,7 +3591,7 @@ static void intel_ddi_sync_state(struct intel_encoder *encoder,
enum phy phy = intel_port_to_phy(i915, encoder->port); enum phy phy = intel_port_to_phy(i915, encoder->port);
if (intel_phy_is_tc(i915, phy)) 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)) if (crtc_state && intel_crtc_has_dp_encoder(crtc_state))
intel_dp_sync_state(encoder, crtc_state); intel_dp_sync_state(encoder, crtc_state);
...@@ -3801,11 +3801,17 @@ static void intel_ddi_encoder_destroy(struct drm_encoder *encoder) ...@@ -3801,11 +3801,17 @@ static void intel_ddi_encoder_destroy(struct drm_encoder *encoder)
static void intel_ddi_encoder_reset(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_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_dp->reset_link_params = true;
intel_pps_encoder_reset(intel_dp); 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 = { static const struct drm_encoder_funcs intel_ddi_funcs = {
......
...@@ -686,19 +686,59 @@ static void intel_tc_port_update_mode(struct intel_digital_port *dig_port, ...@@ -686,19 +686,59 @@ static void intel_tc_port_update_mode(struct intel_digital_port *dig_port,
static void static void
intel_tc_port_link_init_refcount(struct intel_digital_port *dig_port, intel_tc_port_link_init_refcount(struct intel_digital_port *dig_port,
int refcount) 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); 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); 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 drm_i915_private *i915 = to_i915(dig_port->base.base.dev);
struct intel_encoder *encoder = &dig_port->base; struct intel_encoder *encoder = &dig_port->base;
intel_wakeref_t tc_cold_wref;
enum intel_display_power_domain domain;
int active_links = 0; int active_links = 0;
mutex_lock(&dig_port->tc_lock); mutex_lock(&dig_port->tc_lock);
...@@ -708,21 +748,14 @@ void intel_tc_port_sanitize(struct intel_digital_port *dig_port) ...@@ -708,21 +748,14 @@ void intel_tc_port_sanitize(struct intel_digital_port *dig_port)
else if (encoder->base.crtc) else if (encoder->base.crtc)
active_links = to_intel_crtc(encoder->base.crtc)->active; 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_link_refcount != 1);
drm_WARN_ON(&i915->drm, dig_port->tc_lock_wakeref); 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 (active_links) {
if (!icl_tc_phy_is_connected(dig_port)) if (!icl_tc_phy_is_connected(dig_port))
drm_dbg_kms(&i915->drm, drm_dbg_kms(&i915->drm,
"Port %s: PHY disconnected with %d active link(s)\n", "Port %s: PHY disconnected with %d active link(s)\n",
dig_port->tc_port_name, active_links); 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 { } else {
/* /*
* TBT-alt is the default mode in any case the PHY ownership is not * 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) ...@@ -736,9 +769,10 @@ void intel_tc_port_sanitize(struct intel_digital_port *dig_port)
dig_port->tc_port_name, dig_port->tc_port_name,
tc_port_mode_name(dig_port->tc_mode)); tc_port_mode_name(dig_port->tc_mode));
icl_tc_phy_disconnect(dig_port); 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", drm_dbg_kms(&i915->drm, "Port %s: sanitize mode (%s)\n",
dig_port->tc_port_name, dig_port->tc_port_name,
...@@ -923,4 +957,6 @@ void intel_tc_port_init(struct intel_digital_port *dig_port, bool is_legacy) ...@@ -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_mode = TC_PORT_DISCONNECTED;
dig_port->tc_link_refcount = 0; dig_port->tc_link_refcount = 0;
tc_port_load_fia_params(i915, dig_port); tc_port_load_fia_params(i915, dig_port);
intel_tc_port_init_mode(dig_port);
} }
...@@ -24,7 +24,8 @@ int intel_tc_port_fia_max_lane_count(struct intel_digital_port *dig_port); ...@@ -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, void intel_tc_port_set_fia_lane_count(struct intel_digital_port *dig_port,
int required_lanes); 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_lock(struct intel_digital_port *dig_port);
void intel_tc_port_unlock(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); void intel_tc_port_flush_work(struct intel_digital_port *dig_port);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment