From a972cd3f0eb50bde3823e8d1df8f6c1b0c673ecc Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Thu, 16 Mar 2023 15:17:11 +0200 Subject: drm/i915/tc: Abort DP AUX transfer on a disconnected TC port On TC ports the 4ms AUX timeout combined with the 5 * 32 retry attempts during DPCD accesses adds a 640ms delay to each access if the sink is disconnected. This in turn slows down a modeset during which the sink is disconnected (for instance a disabling modeset). Prevent the above delay by aborting AUX transfers on a TC port with a disconnected sink. The DP 1.4a link CTS (4.2.1.5 Source Device Inactive HPD / Inactive AUX Test") also requires not to initiate AUX transfers on disconnected DP ports in general, however this patch doesn't change the behavior on non-TC ports, leaving that for a follow-up. Reported-and-tested-by: Chris Chiu Closes: https://gitlab.freedesktop.org/drm/intel/-/issues/8279 Reviewed-by: Mika Kahola Signed-off-by: Imre Deak Link: https://patchwork.freedesktop.org/patch/msgid/20230316131724.359612-2-imre.deak@intel.com --- drivers/gpu/drm/i915/display/intel_tc.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) (limited to 'drivers/gpu/drm/i915/display/intel_tc.c') diff --git a/drivers/gpu/drm/i915/display/intel_tc.c b/drivers/gpu/drm/i915/display/intel_tc.c index f45328712bff..050f99828459 100644 --- a/drivers/gpu/drm/i915/display/intel_tc.c +++ b/drivers/gpu/drm/i915/display/intel_tc.c @@ -768,16 +768,23 @@ void intel_tc_port_sanitize_mode(struct intel_digital_port *dig_port) * connected ports are usable, and avoids exposing to the users objects they * can't really use. */ +bool intel_tc_port_connected_locked(struct intel_encoder *encoder) +{ + struct intel_digital_port *dig_port = enc_to_dig_port(encoder); + struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev); + + drm_WARN_ON(&i915->drm, !intel_tc_port_ref_held(dig_port)); + + return tc_port_live_status_mask(dig_port) & BIT(dig_port->tc_mode); +} + bool intel_tc_port_connected(struct intel_encoder *encoder) { struct intel_digital_port *dig_port = enc_to_dig_port(encoder); bool is_connected; intel_tc_port_lock(dig_port); - - is_connected = tc_port_live_status_mask(dig_port) & - BIT(dig_port->tc_mode); - + is_connected = intel_tc_port_connected_locked(encoder); intel_tc_port_unlock(dig_port); return is_connected; -- cgit From 67165722c27cc46de112a4e10b450170c8980a6f Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Thu, 16 Mar 2023 15:17:12 +0200 Subject: drm/i915/tc: Fix TC port link ref init for DP MST during HW readout MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit An enabled TC MST port holds one TC port link reference, regardless of the number of enabled streams on it, but the TC port HW readout takes one reference for each active MST stream. Fix the HW readout, taking only one reference for MST ports. This didn't cause an actual problem, since the encoder HW readout doesn't yet support reading out the MST HW state. Signed-off-by: Imre Deak Reviewed-by: Ville Syrjälä Reviewed-by: Mika Kahola Reviewed-by: Andrzej Hajda Link: https://patchwork.freedesktop.org/patch/msgid/20230316131724.359612-3-imre.deak@intel.com --- drivers/gpu/drm/i915/display/intel_tc.c | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) (limited to 'drivers/gpu/drm/i915/display/intel_tc.c') diff --git a/drivers/gpu/drm/i915/display/intel_tc.c b/drivers/gpu/drm/i915/display/intel_tc.c index 050f99828459..0b6fe96ab475 100644 --- a/drivers/gpu/drm/i915/display/intel_tc.c +++ b/drivers/gpu/drm/i915/display/intel_tc.c @@ -660,11 +660,14 @@ static void intel_tc_port_update_mode(struct intel_digital_port *dig_port, tc_cold_unblock(dig_port, domain, wref); } -static void -intel_tc_port_link_init_refcount(struct intel_digital_port *dig_port, - int refcount) +static void __intel_tc_port_get_link(struct intel_digital_port *dig_port) { - dig_port->tc_link_refcount = refcount; + dig_port->tc_link_refcount++; +} + +static void __intel_tc_port_put_link(struct intel_digital_port *dig_port) +{ + dig_port->tc_link_refcount--; } /** @@ -690,7 +693,7 @@ void intel_tc_port_init_mode(struct intel_digital_port *dig_port) 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); + __intel_tc_port_get_link(dig_port); 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); @@ -726,8 +729,6 @@ void intel_tc_port_sanitize_mode(struct intel_digital_port *dig_port) active_links = to_intel_crtc(encoder->base.crtc)->active; drm_WARN_ON(&i915->drm, dig_port->tc_link_refcount != 1); - intel_tc_port_link_init_refcount(dig_port, active_links); - if (active_links) { if (!icl_tc_phy_is_connected(dig_port)) drm_dbg_kms(&i915->drm, @@ -746,6 +747,7 @@ void intel_tc_port_sanitize_mode(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); + __intel_tc_port_put_link(dig_port); tc_cold_unblock(dig_port, dig_port->tc_lock_power_domain, fetch_and_zero(&dig_port->tc_lock_wakeref)); @@ -864,14 +866,14 @@ void intel_tc_port_get_link(struct intel_digital_port *dig_port, int required_lanes) { __intel_tc_port_lock(dig_port, required_lanes); - dig_port->tc_link_refcount++; + __intel_tc_port_get_link(dig_port); intel_tc_port_unlock(dig_port); } void intel_tc_port_put_link(struct intel_digital_port *dig_port) { intel_tc_port_lock(dig_port); - --dig_port->tc_link_refcount; + __intel_tc_port_put_link(dig_port); intel_tc_port_unlock(dig_port); /* -- cgit From f2c7959dda614d9b7c6a41510492de39d31705ec Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Thu, 16 Mar 2023 15:17:13 +0200 Subject: drm/i915/tc: Fix the ICL PHY ownership check in TC-cold state MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The commit renaming icl_tc_phy_is_in_safe_mode() to icl_tc_phy_take_ownership() didn't flip the function's return value accordingly, fix this up. This didn't cause an actual problem besides state check errors, since the function is only used during HW readout. Cc: José Roberto de Souza Fixes: f53979d68a77 ("drm/i915/display/tc: Rename safe_mode functions ownership") Reviewed-by: José Roberto de Souza Reviewed-by: Ville Syrjälä Signed-off-by: Imre Deak Link: https://patchwork.freedesktop.org/patch/msgid/20230316131724.359612-4-imre.deak@intel.com --- drivers/gpu/drm/i915/display/intel_tc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/gpu/drm/i915/display/intel_tc.c') diff --git a/drivers/gpu/drm/i915/display/intel_tc.c b/drivers/gpu/drm/i915/display/intel_tc.c index 0b6fe96ab475..fd826b9657e9 100644 --- a/drivers/gpu/drm/i915/display/intel_tc.c +++ b/drivers/gpu/drm/i915/display/intel_tc.c @@ -418,9 +418,9 @@ static bool icl_tc_phy_is_owned(struct intel_digital_port *dig_port) val = intel_de_read(i915, PORT_TX_DFLEXDPCSSS(dig_port->tc_phy_fia)); if (val == 0xffffffff) { drm_dbg_kms(&i915->drm, - "Port %s: PHY in TCCOLD, assume safe mode\n", + "Port %s: PHY in TCCOLD, assume not owned\n", dig_port->tc_port_name); - return true; + return false; } return val & DP_PHY_MODE_STATUS_NOT_SAFE(dig_port->tc_phy_fia_idx); -- cgit From 06f66261a1567d66b9d35c87393b6edfbea4c8f8 Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Thu, 16 Mar 2023 15:17:14 +0200 Subject: drm/i915/tc: Fix system resume MST mode restore for DP-alt sinks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit At least restoring the MST topology during system resume needs to use AUX before the display HW readout->sanitization sequence is complete, but on TC ports the PHY may be in the wrong mode for this, resulting in the AUX transfers to fail. The initial TC port mode is kept fixed as BIOS left it for the above HW readout sequence (to prevent changing the mode on an enabled port). If the port is disabled this initial mode is TBT - as in any case the PHY ownership is not held - even if a DP-alt sink is connected. Thus, the AUX transfers during this time will use TBT mode instead of the expected DP-alt mode and so time out. Fix the above by connecting the PHY during port initialization if the port is disabled, which will switch to the expected mode (DP-alt in the above case). As the encoder/pipe HW state isn't read-out yet at this point, check if the port is enabled based on the DDI_BUF enabled flag. Save the read-out initial mode, so intel_tc_port_sanitize_mode() can check this wrt. the read-out encoder HW state. Signed-off-by: Imre Deak Reviewed-by: Ville Syrjälä Link: https://patchwork.freedesktop.org/patch/msgid/20230316131724.359612-5-imre.deak@intel.com --- drivers/gpu/drm/i915/display/intel_display_types.h | 1 + drivers/gpu/drm/i915/display/intel_tc.c | 48 ++++++++++++++++++++-- 2 files changed, 46 insertions(+), 3 deletions(-) (limited to 'drivers/gpu/drm/i915/display/intel_tc.c') diff --git a/drivers/gpu/drm/i915/display/intel_display_types.h b/drivers/gpu/drm/i915/display/intel_display_types.h index c32bfba06ca1..06bbfd426ac7 100644 --- a/drivers/gpu/drm/i915/display/intel_display_types.h +++ b/drivers/gpu/drm/i915/display/intel_display_types.h @@ -1783,6 +1783,7 @@ struct intel_digital_port { bool tc_legacy_port:1; char tc_port_name[8]; enum tc_port_mode tc_mode; + enum tc_port_mode tc_init_mode; enum phy_fia tc_phy_fia; u8 tc_phy_fia_idx; diff --git a/drivers/gpu/drm/i915/display/intel_tc.c b/drivers/gpu/drm/i915/display/intel_tc.c index fd826b9657e9..e8cf3b506fb7 100644 --- a/drivers/gpu/drm/i915/display/intel_tc.c +++ b/drivers/gpu/drm/i915/display/intel_tc.c @@ -118,6 +118,24 @@ assert_tc_cold_blocked(struct intel_digital_port *dig_port) drm_WARN_ON(&i915->drm, !enabled); } +static enum intel_display_power_domain +tc_port_power_domain(struct intel_digital_port *dig_port) +{ + struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev); + enum tc_port tc_port = intel_port_to_tc(i915, dig_port->base.port); + + return POWER_DOMAIN_PORT_DDI_LANES_TC1 + tc_port - TC_PORT_1; +} + +static void +assert_tc_port_power_enabled(struct intel_digital_port *dig_port) +{ + struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev); + + drm_WARN_ON(&i915->drm, + !intel_display_power_is_enabled(i915, tc_port_power_domain(dig_port))); +} + u32 intel_tc_port_get_lane_mask(struct intel_digital_port *dig_port) { struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev); @@ -670,6 +688,16 @@ static void __intel_tc_port_put_link(struct intel_digital_port *dig_port) dig_port->tc_link_refcount--; } +static bool tc_port_is_enabled(struct intel_digital_port *dig_port) +{ + struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev); + + assert_tc_port_power_enabled(dig_port); + + return intel_de_read(i915, DDI_BUF_CTL(dig_port->base.port)) & + DDI_BUF_CTL_ENABLE; +} + /** * intel_tc_port_init_mode: Read out HW state and init the given port's TypeC mode * @dig_port: digital port @@ -692,9 +720,23 @@ void intel_tc_port_init_mode(struct intel_digital_port *dig_port) tc_cold_wref = tc_cold_block(dig_port, &domain); dig_port->tc_mode = intel_tc_port_get_current_mode(dig_port); + /* + * Save the initial mode for the state check in + * intel_tc_port_sanitize_mode(). + */ + dig_port->tc_init_mode = dig_port->tc_mode; + dig_port->tc_lock_wakeref = tc_cold_block(dig_port, &dig_port->tc_lock_power_domain); + + /* + * The PHY needs to be connected for AUX to work during HW readout and + * MST topology resume, but the PHY mode can only be changed if the + * port is disabled. + */ + if (!tc_port_is_enabled(dig_port)) + intel_tc_port_update_mode(dig_port, 1, false); + /* Prevent changing dig_port->tc_mode until intel_tc_port_sanitize_mode() is called. */ __intel_tc_port_get_link(dig_port); - 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); @@ -741,11 +783,11 @@ void intel_tc_port_sanitize_mode(struct intel_digital_port *dig_port) * we'll just switch to disconnected mode from it here without * a note. */ - if (dig_port->tc_mode != TC_PORT_TBT_ALT) + if (dig_port->tc_init_mode != TC_PORT_TBT_ALT) drm_dbg_kms(&i915->drm, "Port %s: PHY left in %s mode on disabled port, disconnecting it\n", dig_port->tc_port_name, - tc_port_mode_name(dig_port->tc_mode)); + tc_port_mode_name(dig_port->tc_init_mode)); icl_tc_phy_disconnect(dig_port); __intel_tc_port_put_link(dig_port); -- cgit From b25f551ae197293e1efdf991f47a70e8da616845 Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Thu, 16 Mar 2023 15:17:15 +0200 Subject: drm/i915/tc: Wait for IOM/FW PHY initialization of legacy TC ports MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit During boot-up/system resume, the TC PHY on legacy ports will be initialized by the IOM/TCSS firmware regardless of a sink being connected or not (as opposed to DP-alt/TBT ports, which the FW only inits once a sink is connected). Wait for the above initialization to complete during HW readout, so that connecting the PHY later will already see the expected PHY ready state. Signed-off-by: Imre Deak Reviewed-by: Ville Syrjälä Link: https://patchwork.freedesktop.org/patch/msgid/20230316131724.359612-6-imre.deak@intel.com --- drivers/gpu/drm/i915/display/intel_tc.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) (limited to 'drivers/gpu/drm/i915/display/intel_tc.c') diff --git a/drivers/gpu/drm/i915/display/intel_tc.c b/drivers/gpu/drm/i915/display/intel_tc.c index e8cf3b506fb7..2116c82831a5 100644 --- a/drivers/gpu/drm/i915/display/intel_tc.c +++ b/drivers/gpu/drm/i915/display/intel_tc.c @@ -582,6 +582,15 @@ static bool icl_tc_phy_is_connected(struct intel_digital_port *dig_port) dig_port->tc_mode == TC_PORT_LEGACY; } +static void tc_phy_wait_for_ready(struct intel_digital_port *dig_port) +{ + struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev); + + if (wait_for(tc_phy_status_complete(dig_port), 100)) + drm_err(&i915->drm, "Port %s: timeout waiting for PHY ready\n", + dig_port->tc_port_name); +} + static enum tc_port_mode intel_tc_port_get_current_mode(struct intel_digital_port *dig_port) { @@ -589,6 +598,14 @@ intel_tc_port_get_current_mode(struct intel_digital_port *dig_port) u32 live_status_mask = tc_port_live_status_mask(dig_port); enum tc_port_mode mode; + /* + * For legacy ports the IOM firmware initializes the PHY during boot-up + * and system resume whether or not a sink is connected. Wait here for + * the initialization to get ready. + */ + if (dig_port->tc_legacy_port) + tc_phy_wait_for_ready(dig_port); + if (!tc_phy_is_owned(dig_port) || drm_WARN_ON(&i915->drm, !tc_phy_status_complete(dig_port))) return TC_PORT_TBT_ALT; -- cgit From 4e936b65211a578ad1291967fb2344abd9488cc6 Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Thu, 16 Mar 2023 15:17:16 +0200 Subject: drm/i915/tc: Factor out helpers converting HPD mask to TC mode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Factor out helpers used later in the patchset to convert an HPD status mask to TC mode or target TC mode. No functional changes. Signed-off-by: Imre Deak Reviewed-by: Ville Syrjälä Link: https://patchwork.freedesktop.org/patch/msgid/20230316131724.359612-7-imre.deak@intel.com --- drivers/gpu/drm/i915/display/intel_tc.c | 44 ++++++++++++++++++++++++--------- 1 file changed, 33 insertions(+), 11 deletions(-) (limited to 'drivers/gpu/drm/i915/display/intel_tc.c') diff --git a/drivers/gpu/drm/i915/display/intel_tc.c b/drivers/gpu/drm/i915/display/intel_tc.c index 2116c82831a5..002e142cc746 100644 --- a/drivers/gpu/drm/i915/display/intel_tc.c +++ b/drivers/gpu/drm/i915/display/intel_tc.c @@ -591,11 +591,28 @@ static void tc_phy_wait_for_ready(struct intel_digital_port *dig_port) dig_port->tc_port_name); } +static enum tc_port_mode +hpd_mask_to_tc_mode(u32 live_status_mask) +{ + if (live_status_mask) + return fls(live_status_mask) - 1; + + return TC_PORT_DISCONNECTED; +} + +static enum tc_port_mode +tc_phy_hpd_live_mode(struct intel_digital_port *dig_port) +{ + u32 live_status_mask = tc_port_live_status_mask(dig_port); + + return hpd_mask_to_tc_mode(live_status_mask); +} + static enum tc_port_mode intel_tc_port_get_current_mode(struct intel_digital_port *dig_port) { struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev); - u32 live_status_mask = tc_port_live_status_mask(dig_port); + enum tc_port_mode live_mode = tc_phy_hpd_live_mode(dig_port); enum tc_port_mode mode; /* @@ -611,27 +628,32 @@ intel_tc_port_get_current_mode(struct intel_digital_port *dig_port) return TC_PORT_TBT_ALT; mode = dig_port->tc_legacy_port ? TC_PORT_LEGACY : TC_PORT_DP_ALT; - if (live_status_mask) { - enum tc_port_mode live_mode = fls(live_status_mask) - 1; - - if (!drm_WARN_ON(&i915->drm, live_mode == TC_PORT_TBT_ALT)) - mode = live_mode; - } + if (live_mode != TC_PORT_DISCONNECTED && + !drm_WARN_ON(&i915->drm, live_mode == TC_PORT_TBT_ALT)) + mode = live_mode; return mode; } static enum tc_port_mode -intel_tc_port_get_target_mode(struct intel_digital_port *dig_port) +hpd_mask_to_target_mode(u32 live_status_mask) { - u32 live_status_mask = tc_port_live_status_mask(dig_port); + enum tc_port_mode mode = hpd_mask_to_tc_mode(live_status_mask); - if (live_status_mask) - return fls(live_status_mask) - 1; + if (mode != TC_PORT_DISCONNECTED) + return mode; return TC_PORT_TBT_ALT; } +static enum tc_port_mode +intel_tc_port_get_target_mode(struct intel_digital_port *dig_port) +{ + u32 live_status_mask = tc_port_live_status_mask(dig_port); + + return hpd_mask_to_target_mode(live_status_mask); +} + static void intel_tc_port_reset_mode(struct intel_digital_port *dig_port, int required_lanes, bool force_disconnect) { -- cgit From a8da6c18b481efbe78618dbba18c2db3241f4fea Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Thu, 16 Mar 2023 15:17:17 +0200 Subject: drm/i915/tc: Fix target TC mode for a disconnected legacy port MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Atm, the target TC mode - which the PHY should be switched to at any point it's used - is TBT in case there is no sink connected. However legacy ports are only used in the legacy mode regardless of the sink connected state. Fix the mode returned by intel_tc_port_get_target_mode() accordingly. Despite of the above issue, the PHY got disconnected as expected in response to a sink disconnect event, causing only a redundant PHY disconnect->reconnect sequence whenever the port was used. Signed-off-by: Imre Deak Reviewed-by: Ville Syrjälä Link: https://patchwork.freedesktop.org/patch/msgid/20230316131724.359612-8-imre.deak@intel.com --- drivers/gpu/drm/i915/display/intel_tc.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) (limited to 'drivers/gpu/drm/i915/display/intel_tc.c') diff --git a/drivers/gpu/drm/i915/display/intel_tc.c b/drivers/gpu/drm/i915/display/intel_tc.c index 002e142cc746..e39c8a870df0 100644 --- a/drivers/gpu/drm/i915/display/intel_tc.c +++ b/drivers/gpu/drm/i915/display/intel_tc.c @@ -635,15 +635,23 @@ intel_tc_port_get_current_mode(struct intel_digital_port *dig_port) return mode; } +static enum tc_port_mode default_tc_mode(struct intel_digital_port *dig_port) +{ + if (dig_port->tc_legacy_port) + return TC_PORT_LEGACY; + + return TC_PORT_TBT_ALT; +} + static enum tc_port_mode -hpd_mask_to_target_mode(u32 live_status_mask) +hpd_mask_to_target_mode(struct intel_digital_port *dig_port, u32 live_status_mask) { enum tc_port_mode mode = hpd_mask_to_tc_mode(live_status_mask); if (mode != TC_PORT_DISCONNECTED) return mode; - return TC_PORT_TBT_ALT; + return default_tc_mode(dig_port); } static enum tc_port_mode @@ -651,7 +659,7 @@ intel_tc_port_get_target_mode(struct intel_digital_port *dig_port) { u32 live_status_mask = tc_port_live_status_mask(dig_port); - return hpd_mask_to_target_mode(live_status_mask); + return hpd_mask_to_target_mode(dig_port, live_status_mask); } static void intel_tc_port_reset_mode(struct intel_digital_port *dig_port, -- cgit From c173a91b760844074abcd636eda47d3b2c107a64 Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Thu, 16 Mar 2023 15:17:18 +0200 Subject: drm/i915/tc: Fix TC mode for a legacy port if the PHY is not ready MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A legacy TC port can't be switched to TBT mode, even if the PHY initialization wasn't ready yet for some reason, so prevent this. This shouldn't normally happen as the driver waits for the IOM/TCSS PHY initialization during driver loading and system resume. Signed-off-by: Imre Deak Reviewed-by: Ville Syrjälä Link: https://patchwork.freedesktop.org/patch/msgid/20230316131724.359612-9-imre.deak@intel.com --- drivers/gpu/drm/i915/display/intel_tc.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/gpu/drm/i915/display/intel_tc.c') diff --git a/drivers/gpu/drm/i915/display/intel_tc.c b/drivers/gpu/drm/i915/display/intel_tc.c index e39c8a870df0..f66129494cc4 100644 --- a/drivers/gpu/drm/i915/display/intel_tc.c +++ b/drivers/gpu/drm/i915/display/intel_tc.c @@ -482,7 +482,8 @@ static void icl_tc_phy_connect(struct intel_digital_port *dig_port, u32 live_status_mask; int max_lanes; - if (!tc_phy_status_complete(dig_port)) { + if (!tc_phy_status_complete(dig_port) && + !drm_WARN_ON(&i915->drm, dig_port->tc_legacy_port)) { drm_dbg_kms(&i915->drm, "Port %s: PHY not ready\n", dig_port->tc_port_name); goto out_set_tbt_alt_mode; -- cgit From 2983b869881b169288909b4ac93f407fe804a75a Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Thu, 16 Mar 2023 15:17:19 +0200 Subject: drm/i915/tc: Fix initial TC mode on disabled legacy ports MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Atm, a TC port's initial mode will be read out as TBT mode in any case the PHY ownership is not held. This isn't correct for legacy ports which should be used only in legacy mode. Fix the above initial mode to be disconnected mode for a legacy port and TBT mode for DP-alt/TBT ports. Determine the port type by checking first the HPD state and then the legacy VBT flag (so the HPD state can correct a bogus VBT flag). If a sink is connected on a disabled port the PHY will get also connected (switching it to legacy mode on a legacy port). Also connect the PHY on a legacy port if it's enabled but BIOS incorrectly left it in the disconnected state for some reason. Signed-off-by: Imre Deak Reviewed-by: Ville Syrjälä Link: https://patchwork.freedesktop.org/patch/msgid/20230316131724.359612-10-imre.deak@intel.com --- drivers/gpu/drm/i915/display/intel_tc.c | 67 ++++++++++++++++++++++++++++++--- 1 file changed, 61 insertions(+), 6 deletions(-) (limited to 'drivers/gpu/drm/i915/display/intel_tc.c') diff --git a/drivers/gpu/drm/i915/display/intel_tc.c b/drivers/gpu/drm/i915/display/intel_tc.c index f66129494cc4..35e6339caa32 100644 --- a/drivers/gpu/drm/i915/display/intel_tc.c +++ b/drivers/gpu/drm/i915/display/intel_tc.c @@ -558,6 +558,16 @@ static void icl_tc_phy_disconnect(struct intel_digital_port *dig_port) } } +static bool tc_phy_is_ready_and_owned(struct intel_digital_port *dig_port, + bool phy_is_ready, bool phy_is_owned) +{ + struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev); + + drm_WARN_ON(&i915->drm, phy_is_owned && !phy_is_ready); + + return phy_is_ready && phy_is_owned; +} + static bool icl_tc_phy_is_connected(struct intel_digital_port *dig_port) { struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev); @@ -609,11 +619,34 @@ tc_phy_hpd_live_mode(struct intel_digital_port *dig_port) return hpd_mask_to_tc_mode(live_status_mask); } +static enum tc_port_mode +get_tc_mode_in_phy_not_owned_state(struct intel_digital_port *dig_port, + enum tc_port_mode live_mode) +{ + switch (live_mode) { + case TC_PORT_LEGACY: + return TC_PORT_DISCONNECTED; + case TC_PORT_DP_ALT: + case TC_PORT_TBT_ALT: + return TC_PORT_TBT_ALT; + default: + MISSING_CASE(live_mode); + fallthrough; + case TC_PORT_DISCONNECTED: + if (dig_port->tc_legacy_port) + return TC_PORT_DISCONNECTED; + else + return TC_PORT_TBT_ALT; + } +} + static enum tc_port_mode intel_tc_port_get_current_mode(struct intel_digital_port *dig_port) { struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev); enum tc_port_mode live_mode = tc_phy_hpd_live_mode(dig_port); + bool phy_is_ready; + bool phy_is_owned; enum tc_port_mode mode; /* @@ -624,9 +657,11 @@ intel_tc_port_get_current_mode(struct intel_digital_port *dig_port) if (dig_port->tc_legacy_port) tc_phy_wait_for_ready(dig_port); - if (!tc_phy_is_owned(dig_port) || - drm_WARN_ON(&i915->drm, !tc_phy_status_complete(dig_port))) - return TC_PORT_TBT_ALT; + phy_is_ready = tc_phy_status_complete(dig_port); + phy_is_owned = tc_phy_is_owned(dig_port); + + if (!tc_phy_is_ready_and_owned(dig_port, phy_is_ready, phy_is_owned)) + return get_tc_mode_in_phy_not_owned_state(dig_port, live_mode); mode = dig_port->tc_legacy_port ? TC_PORT_LEGACY : TC_PORT_DP_ALT; if (live_mode != TC_PORT_DISCONNECTED && @@ -758,6 +793,7 @@ 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; + bool update_mode = false; mutex_lock(&dig_port->tc_lock); @@ -773,14 +809,32 @@ void intel_tc_port_init_mode(struct intel_digital_port *dig_port) * intel_tc_port_sanitize_mode(). */ dig_port->tc_init_mode = dig_port->tc_mode; - dig_port->tc_lock_wakeref = tc_cold_block(dig_port, &dig_port->tc_lock_power_domain); + if (dig_port->tc_mode != TC_PORT_DISCONNECTED) + dig_port->tc_lock_wakeref = + tc_cold_block(dig_port, &dig_port->tc_lock_power_domain); /* * The PHY needs to be connected for AUX to work during HW readout and * MST topology resume, but the PHY mode can only be changed if the * port is disabled. + * + * An exception is the case where BIOS leaves the PHY incorrectly + * disconnected on an enabled legacy port. Work around that by + * connecting the PHY even though the port is enabled. This doesn't + * cause a problem as the PHY ownership state is ignored by the + * IOM/TCSS firmware (only display can own the PHY in that case). */ - if (!tc_port_is_enabled(dig_port)) + if (!tc_port_is_enabled(dig_port)) { + update_mode = true; + } else if (dig_port->tc_mode == TC_PORT_DISCONNECTED) { + drm_WARN_ON(&i915->drm, !dig_port->tc_legacy_port); + drm_err(&i915->drm, + "Port %s: PHY disconnected on enabled port, connecting it\n", + dig_port->tc_port_name); + update_mode = true; + } + + if (update_mode) intel_tc_port_update_mode(dig_port, 1, false); /* Prevent changing dig_port->tc_mode until intel_tc_port_sanitize_mode() is called. */ @@ -831,7 +885,8 @@ void intel_tc_port_sanitize_mode(struct intel_digital_port *dig_port) * we'll just switch to disconnected mode from it here without * a note. */ - if (dig_port->tc_init_mode != TC_PORT_TBT_ALT) + if (dig_port->tc_init_mode != TC_PORT_TBT_ALT && + dig_port->tc_init_mode != TC_PORT_DISCONNECTED) drm_dbg_kms(&i915->drm, "Port %s: PHY left in %s mode on disabled port, disconnecting it\n", dig_port->tc_port_name, -- cgit From a8b4114d112530440c00fd5bc01e4497480fa4e8 Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Thu, 16 Mar 2023 15:17:20 +0200 Subject: drm/i915/tc: Make the TC mode readout consistent in all PHY states MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For consistency detect the initial TC mode in the PHY owned state the same way this is done in the not owned state (w/o changing the behavior). While at it, add more details to the PHY state debug print. Signed-off-by: Imre Deak Reviewed-by: Ville Syrjälä Link: https://patchwork.freedesktop.org/patch/msgid/20230316131724.359612-11-imre.deak@intel.com --- drivers/gpu/drm/i915/display/intel_tc.c | 43 +++++++++++++++++++++++++-------- 1 file changed, 33 insertions(+), 10 deletions(-) (limited to 'drivers/gpu/drm/i915/display/intel_tc.c') diff --git a/drivers/gpu/drm/i915/display/intel_tc.c b/drivers/gpu/drm/i915/display/intel_tc.c index 35e6339caa32..5d040f0c5f63 100644 --- a/drivers/gpu/drm/i915/display/intel_tc.c +++ b/drivers/gpu/drm/i915/display/intel_tc.c @@ -619,6 +619,26 @@ tc_phy_hpd_live_mode(struct intel_digital_port *dig_port) return hpd_mask_to_tc_mode(live_status_mask); } +static enum tc_port_mode +get_tc_mode_in_phy_owned_state(struct intel_digital_port *dig_port, + enum tc_port_mode live_mode) +{ + switch (live_mode) { + case TC_PORT_LEGACY: + case TC_PORT_DP_ALT: + return live_mode; + default: + MISSING_CASE(live_mode); + fallthrough; + case TC_PORT_TBT_ALT: + case TC_PORT_DISCONNECTED: + if (dig_port->tc_legacy_port) + return TC_PORT_LEGACY; + else + return TC_PORT_DP_ALT; + } +} + static enum tc_port_mode get_tc_mode_in_phy_not_owned_state(struct intel_digital_port *dig_port, enum tc_port_mode live_mode) @@ -660,13 +680,20 @@ intel_tc_port_get_current_mode(struct intel_digital_port *dig_port) phy_is_ready = tc_phy_status_complete(dig_port); phy_is_owned = tc_phy_is_owned(dig_port); - if (!tc_phy_is_ready_and_owned(dig_port, phy_is_ready, phy_is_owned)) - return get_tc_mode_in_phy_not_owned_state(dig_port, live_mode); + if (!tc_phy_is_ready_and_owned(dig_port, phy_is_ready, phy_is_owned)) { + mode = get_tc_mode_in_phy_not_owned_state(dig_port, live_mode); + } else { + drm_WARN_ON(&i915->drm, live_mode == TC_PORT_TBT_ALT); + mode = get_tc_mode_in_phy_owned_state(dig_port, live_mode); + } - mode = dig_port->tc_legacy_port ? TC_PORT_LEGACY : TC_PORT_DP_ALT; - if (live_mode != TC_PORT_DISCONNECTED && - !drm_WARN_ON(&i915->drm, live_mode == TC_PORT_TBT_ALT)) - mode = live_mode; + drm_dbg_kms(&i915->drm, + "Port %s: PHY mode: %s (ready: %s, owned: %s, HPD: %s)\n", + dig_port->tc_port_name, + tc_port_mode_name(mode), + str_yes_no(phy_is_ready), + str_yes_no(phy_is_owned), + tc_port_mode_name(live_mode)); return mode; } @@ -842,10 +869,6 @@ void intel_tc_port_init_mode(struct intel_digital_port *dig_port) 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); } -- cgit From 2a4d292f056b35f54cd7788e124937fe598369c4 Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Wed, 22 Mar 2023 00:01:00 +0200 Subject: drm/i915/tc: Factor out a function querying active links on a TC port MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For clarity factor out the function to determine if there are active links on a TC port. This prepares for the next patch also checking the port's PLL type. While at it pass crtc_state to intel_tc_port_sanitize_mode(), and check hw.active in that, instead of the deprecated crtc->active flag. v2: Check crtc_state->hw.active instead of crtc->active. (Ville) Reviewed-by: Ville Syrjälä Signed-off-by: Imre Deak Link: https://patchwork.freedesktop.org/patch/msgid/20230321220101.983366-2-imre.deak@intel.com --- drivers/gpu/drm/i915/display/intel_ddi.c | 3 ++- drivers/gpu/drm/i915/display/intel_tc.c | 39 ++++++++++++++++++++------------ drivers/gpu/drm/i915/display/intel_tc.h | 4 +++- 3 files changed, 30 insertions(+), 16 deletions(-) (limited to 'drivers/gpu/drm/i915/display/intel_tc.c') diff --git a/drivers/gpu/drm/i915/display/intel_ddi.c b/drivers/gpu/drm/i915/display/intel_ddi.c index 6f48a7b8dcff..73240cf78c8b 100644 --- a/drivers/gpu/drm/i915/display/intel_ddi.c +++ b/drivers/gpu/drm/i915/display/intel_ddi.c @@ -3642,7 +3642,8 @@ 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_mode(enc_to_dig_port(encoder)); + intel_tc_port_sanitize_mode(enc_to_dig_port(encoder), + crtc_state); if (crtc_state && intel_crtc_has_dp_encoder(crtc_state)) intel_dp_sync_state(encoder, crtc_state); diff --git a/drivers/gpu/drm/i915/display/intel_tc.c b/drivers/gpu/drm/i915/display/intel_tc.c index 5d040f0c5f63..c5bfd9f11d7d 100644 --- a/drivers/gpu/drm/i915/display/intel_tc.c +++ b/drivers/gpu/drm/i915/display/intel_tc.c @@ -872,36 +872,47 @@ void intel_tc_port_init_mode(struct intel_digital_port *dig_port) mutex_unlock(&dig_port->tc_lock); } +static bool tc_port_has_active_links(struct intel_digital_port *dig_port, + const struct intel_crtc_state *crtc_state) +{ + struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev); + int active_links = 0; + + if (dig_port->dp.is_mst) { + active_links = intel_dp_mst_encoder_active_links(dig_port); + } else if (crtc_state && crtc_state->hw.active) { + active_links = 1; + } + + if (active_links && !icl_tc_phy_is_connected(dig_port)) + drm_err(&i915->drm, + "Port %s: PHY disconnected with %d active link(s)\n", + dig_port->tc_port_name, active_links); + + return active_links; +} + /** * intel_tc_port_sanitize_mode: Sanitize the given port's TypeC mode * @dig_port: digital port + * @crtc_state: atomic state of CRTC connected to @dig_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. + * @crtc_state is valid if @dig_port is enabled, NULL otherwise. */ -void intel_tc_port_sanitize_mode(struct intel_digital_port *dig_port) +void intel_tc_port_sanitize_mode(struct intel_digital_port *dig_port, + const struct intel_crtc_state *crtc_state) { 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); - if (dig_port->dp.is_mst) - active_links = intel_dp_mst_encoder_active_links(dig_port); - else if (encoder->base.crtc) - active_links = to_intel_crtc(encoder->base.crtc)->active; - drm_WARN_ON(&i915->drm, dig_port->tc_link_refcount != 1); - 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); - } else { + if (!tc_port_has_active_links(dig_port, crtc_state)) { /* * TBT-alt is the default mode in any case the PHY ownership is not * held (regardless of the sink's connected live state), so diff --git a/drivers/gpu/drm/i915/display/intel_tc.h b/drivers/gpu/drm/i915/display/intel_tc.h index 93813056043a..79667d977508 100644 --- a/drivers/gpu/drm/i915/display/intel_tc.h +++ b/drivers/gpu/drm/i915/display/intel_tc.h @@ -9,6 +9,7 @@ #include #include +struct intel_crtc_state; struct intel_digital_port; struct intel_encoder; @@ -26,7 +27,8 @@ void intel_tc_port_set_fia_lane_count(struct intel_digital_port *dig_port, int required_lanes); 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_sanitize_mode(struct intel_digital_port *dig_port, + const struct intel_crtc_state *crtc_state); 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); -- cgit From 10d29bdceef79602af4136c14a6ec391ec3a2e7f Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Wed, 22 Mar 2023 00:01:01 +0200 Subject: drm/i915/tc: Check the PLL type used by an enabled TC port MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The current way to determine during HW state sanitization if a PHY is connected in the expected way doesn't work in all cases. The check for this considers only the PHY ready/owned state and the initial TC mode which was determined earlier by the TC port HW readout - using the sink's HPD and the same PHY ready/owned states. For instance for an enabled DP-alt/TBT port without the PHY ready/owned flags set the initial mode will be TBT, and this will be regarded as a valid PHY state. However it's possible that the port is actually enabled in DP-alt mode, but for some reason the PHY ownership was not acquired. Make sure the driver can detect invalid PHY states as in the above example by checking the PHY ready/owned state wrt. the PLL type used. This should be the TBT PLL if the PHY is not owned and the MG (non-TBT) PLL if the PHY is owned. v2: Rebased on change passing crtc_state in the previous patch. Reviewed-by: Ville Syrjälä Signed-off-by: Imre Deak Link: https://patchwork.freedesktop.org/patch/msgid/20230321220101.983366-3-imre.deak@intel.com --- drivers/gpu/drm/i915/display/intel_tc.c | 44 ++++++++++++++++++--------------- 1 file changed, 24 insertions(+), 20 deletions(-) (limited to 'drivers/gpu/drm/i915/display/intel_tc.c') diff --git a/drivers/gpu/drm/i915/display/intel_tc.c b/drivers/gpu/drm/i915/display/intel_tc.c index c5bfd9f11d7d..bd8c9df5f98f 100644 --- a/drivers/gpu/drm/i915/display/intel_tc.c +++ b/drivers/gpu/drm/i915/display/intel_tc.c @@ -5,6 +5,7 @@ #include "i915_drv.h" #include "i915_reg.h" +#include "intel_ddi.h" #include "intel_de.h" #include "intel_display.h" #include "intel_display_power_map.h" @@ -568,29 +569,29 @@ static bool tc_phy_is_ready_and_owned(struct intel_digital_port *dig_port, return phy_is_ready && phy_is_owned; } -static bool icl_tc_phy_is_connected(struct intel_digital_port *dig_port) +static bool tc_phy_is_connected(struct intel_digital_port *dig_port, + enum icl_port_dpll_id port_pll_type) { - struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev); - - if (!tc_phy_status_complete(dig_port)) { - drm_dbg_kms(&i915->drm, "Port %s: PHY status not complete\n", - dig_port->tc_port_name); - return dig_port->tc_mode == TC_PORT_TBT_ALT; - } - - /* On ADL-P the PHY complete flag is set in TBT mode as well. */ - if (IS_ALDERLAKE_P(i915) && dig_port->tc_mode == TC_PORT_TBT_ALT) - return true; + struct intel_encoder *encoder = &dig_port->base; + struct drm_i915_private *i915 = to_i915(encoder->base.dev); + bool phy_is_ready = tc_phy_status_complete(dig_port); + bool phy_is_owned = tc_phy_is_owned(dig_port); + bool is_connected; - if (!tc_phy_is_owned(dig_port)) { - drm_dbg_kms(&i915->drm, "Port %s: PHY not owned\n", - dig_port->tc_port_name); + if (tc_phy_is_ready_and_owned(dig_port, phy_is_ready, phy_is_owned)) + is_connected = port_pll_type == ICL_PORT_DPLL_MG_PHY; + else + is_connected = port_pll_type == ICL_PORT_DPLL_DEFAULT; - return false; - } + drm_dbg_kms(&i915->drm, + "Port %s: PHY connected: %s (ready: %s, owned: %s, pll_type: %s)\n", + dig_port->tc_port_name, + str_yes_no(is_connected), + str_yes_no(phy_is_ready), + str_yes_no(phy_is_owned), + port_pll_type == ICL_PORT_DPLL_DEFAULT ? "tbt" : "non-tbt"); - return dig_port->tc_mode == TC_PORT_DP_ALT || - dig_port->tc_mode == TC_PORT_LEGACY; + return is_connected; } static void tc_phy_wait_for_ready(struct intel_digital_port *dig_port) @@ -876,15 +877,18 @@ static bool tc_port_has_active_links(struct intel_digital_port *dig_port, const struct intel_crtc_state *crtc_state) { struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev); + enum icl_port_dpll_id pll_type = ICL_PORT_DPLL_DEFAULT; int active_links = 0; if (dig_port->dp.is_mst) { + /* TODO: get the PLL type for MST, once HW readout is done for it. */ active_links = intel_dp_mst_encoder_active_links(dig_port); } else if (crtc_state && crtc_state->hw.active) { + pll_type = intel_ddi_port_pll_type(&dig_port->base, crtc_state); active_links = 1; } - if (active_links && !icl_tc_phy_is_connected(dig_port)) + if (active_links && !tc_phy_is_connected(dig_port, pll_type)) drm_err(&i915->drm, "Port %s: PHY disconnected with %d active link(s)\n", dig_port->tc_port_name, active_links); -- cgit From a33c8f71d3d85ff1c0284270f710b071d480d547 Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Thu, 23 Mar 2023 16:20:07 +0200 Subject: drm/i915/tc: Group the TC PHY setup/query functions per platform Arrange the TC PHY HW state setup/query functions into platform specific and generic groups. This prepares for upcoming patches adding generic TC PHY handlers and platform specific hooks for these, replacing the corresponding if ladders. No functional changes. v2: Fix non kernel-doc multiline comments. (Jani) Cc: Jani Nikula Reviewed-by: Mika Kahola Signed-off-by: Imre Deak Link: https://patchwork.freedesktop.org/patch/msgid/20230323142035.1432621-2-imre.deak@intel.com --- drivers/gpu/drm/i915/display/intel_tc.c | 244 +++++++++++++++++--------------- 1 file changed, 130 insertions(+), 114 deletions(-) (limited to 'drivers/gpu/drm/i915/display/intel_tc.c') diff --git a/drivers/gpu/drm/i915/display/intel_tc.c b/drivers/gpu/drm/i915/display/intel_tc.c index bd8c9df5f98f..37b1f174301e 100644 --- a/drivers/gpu/drm/i915/display/intel_tc.c +++ b/drivers/gpu/drm/i915/display/intel_tc.c @@ -15,6 +15,10 @@ #include "intel_mg_phy_regs.h" #include "intel_tc.h" +static u32 tc_port_live_status_mask(struct intel_digital_port *dig_port); +static bool tc_phy_status_complete(struct intel_digital_port *dig_port); +static bool tc_phy_take_ownership(struct intel_digital_port *dig_port, bool take); + static const char *tc_port_mode_name(enum tc_port_mode mode) { static const char * const names[] = { @@ -256,6 +260,10 @@ static void tc_port_fixup_legacy_flag(struct intel_digital_port *dig_port, dig_port->tc_legacy_port = !dig_port->tc_legacy_port; } +/* + * ICL TC PHY handlers + * ------------------- + */ static u32 icl_tc_port_live_status_mask(struct intel_digital_port *dig_port) { struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev); @@ -287,44 +295,6 @@ static u32 icl_tc_port_live_status_mask(struct intel_digital_port *dig_port) return mask; } -static u32 adl_tc_port_live_status_mask(struct intel_digital_port *dig_port) -{ - struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev); - enum tc_port tc_port = intel_port_to_tc(i915, dig_port->base.port); - u32 isr_bit = i915->display.hotplug.pch_hpd[dig_port->base.hpd_pin]; - u32 val, mask = 0; - - /* - * On ADL-P HW/FW will wake from TCCOLD to complete the read access of - * registers in IOM. Note that this doesn't apply to PHY and FIA - * registers. - */ - val = intel_de_read(i915, TCSS_DDI_STATUS(tc_port)); - if (val & TCSS_DDI_STATUS_HPD_LIVE_STATUS_ALT) - mask |= BIT(TC_PORT_DP_ALT); - if (val & TCSS_DDI_STATUS_HPD_LIVE_STATUS_TBT) - mask |= BIT(TC_PORT_TBT_ALT); - - if (intel_de_read(i915, SDEISR) & isr_bit) - mask |= BIT(TC_PORT_LEGACY); - - /* The sink can be connected only in a single mode. */ - if (!drm_WARN_ON(&i915->drm, hweight32(mask) > 1)) - tc_port_fixup_legacy_flag(dig_port, mask); - - return mask; -} - -static u32 tc_port_live_status_mask(struct intel_digital_port *dig_port) -{ - struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev); - - if (IS_ALDERLAKE_P(i915)) - return adl_tc_port_live_status_mask(dig_port); - - return icl_tc_port_live_status_mask(dig_port); -} - /* * Return the PHY status complete flag indicating that display can acquire the * PHY ownership. The IOM firmware sets this flag when a DP-alt or legacy sink @@ -349,40 +319,6 @@ static bool icl_tc_phy_status_complete(struct intel_digital_port *dig_port) return val & DP_PHY_MODE_STATUS_COMPLETED(dig_port->tc_phy_fia_idx); } -/* - * Return the PHY status complete flag indicating that display can acquire the - * PHY ownership. The IOM firmware sets this flag when it's ready to switch - * the ownership to display, regardless of what sink is connected (TBT-alt, - * DP-alt, legacy or nothing). For TBT-alt sinks the PHY is owned by the TBT - * subsystem and so switching the ownership to display is not required. - */ -static bool adl_tc_phy_status_complete(struct intel_digital_port *dig_port) -{ - struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev); - enum tc_port tc_port = intel_port_to_tc(i915, dig_port->base.port); - u32 val; - - val = intel_de_read(i915, TCSS_DDI_STATUS(tc_port)); - if (val == 0xffffffff) { - drm_dbg_kms(&i915->drm, - "Port %s: PHY in TCCOLD, assuming not complete\n", - dig_port->tc_port_name); - return false; - } - - return val & TCSS_DDI_STATUS_READY; -} - -static bool tc_phy_status_complete(struct intel_digital_port *dig_port) -{ - struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev); - - if (IS_ALDERLAKE_P(i915)) - return adl_tc_phy_status_complete(dig_port); - - return icl_tc_phy_status_complete(dig_port); -} - static bool icl_tc_phy_take_ownership(struct intel_digital_port *dig_port, bool take) { @@ -407,28 +343,6 @@ static bool icl_tc_phy_take_ownership(struct intel_digital_port *dig_port, return true; } -static bool adl_tc_phy_take_ownership(struct intel_digital_port *dig_port, - bool take) -{ - struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev); - enum port port = dig_port->base.port; - - intel_de_rmw(i915, DDI_BUF_CTL(port), DDI_BUF_CTL_TC_PHY_OWNERSHIP, - take ? DDI_BUF_CTL_TC_PHY_OWNERSHIP : 0); - - return true; -} - -static bool tc_phy_take_ownership(struct intel_digital_port *dig_port, bool take) -{ - struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev); - - if (IS_ALDERLAKE_P(i915)) - return adl_tc_phy_take_ownership(dig_port, take); - - return icl_tc_phy_take_ownership(dig_port, take); -} - static bool icl_tc_phy_is_owned(struct intel_digital_port *dig_port) { struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev); @@ -445,26 +359,6 @@ static bool icl_tc_phy_is_owned(struct intel_digital_port *dig_port) return val & DP_PHY_MODE_STATUS_NOT_SAFE(dig_port->tc_phy_fia_idx); } -static bool adl_tc_phy_is_owned(struct intel_digital_port *dig_port) -{ - struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev); - enum port port = dig_port->base.port; - u32 val; - - val = intel_de_read(i915, DDI_BUF_CTL(port)); - return val & DDI_BUF_CTL_TC_PHY_OWNERSHIP; -} - -static bool tc_phy_is_owned(struct intel_digital_port *dig_port) -{ - struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev); - - if (IS_ALDERLAKE_P(i915)) - return adl_tc_phy_is_owned(dig_port); - - return icl_tc_phy_is_owned(dig_port); -} - /* * This function implements the first part of the Connect Flow described by our * specification, Gen11 TypeC Programming chapter. The rest of the flow (reading @@ -559,6 +453,128 @@ static void icl_tc_phy_disconnect(struct intel_digital_port *dig_port) } } +/* + * ADLP TC PHY handlers + * -------------------- + */ +static u32 adl_tc_port_live_status_mask(struct intel_digital_port *dig_port) +{ + struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev); + enum tc_port tc_port = intel_port_to_tc(i915, dig_port->base.port); + u32 isr_bit = i915->display.hotplug.pch_hpd[dig_port->base.hpd_pin]; + u32 val, mask = 0; + + /* + * On ADL-P HW/FW will wake from TCCOLD to complete the read access of + * registers in IOM. Note that this doesn't apply to PHY and FIA + * registers. + */ + val = intel_de_read(i915, TCSS_DDI_STATUS(tc_port)); + if (val & TCSS_DDI_STATUS_HPD_LIVE_STATUS_ALT) + mask |= BIT(TC_PORT_DP_ALT); + if (val & TCSS_DDI_STATUS_HPD_LIVE_STATUS_TBT) + mask |= BIT(TC_PORT_TBT_ALT); + + if (intel_de_read(i915, SDEISR) & isr_bit) + mask |= BIT(TC_PORT_LEGACY); + + /* The sink can be connected only in a single mode. */ + if (!drm_WARN_ON(&i915->drm, hweight32(mask) > 1)) + tc_port_fixup_legacy_flag(dig_port, mask); + + return mask; +} + +/* + * Return the PHY status complete flag indicating that display can acquire the + * PHY ownership. The IOM firmware sets this flag when it's ready to switch + * the ownership to display, regardless of what sink is connected (TBT-alt, + * DP-alt, legacy or nothing). For TBT-alt sinks the PHY is owned by the TBT + * subsystem and so switching the ownership to display is not required. + */ +static bool adl_tc_phy_status_complete(struct intel_digital_port *dig_port) +{ + struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev); + enum tc_port tc_port = intel_port_to_tc(i915, dig_port->base.port); + u32 val; + + val = intel_de_read(i915, TCSS_DDI_STATUS(tc_port)); + if (val == 0xffffffff) { + drm_dbg_kms(&i915->drm, + "Port %s: PHY in TCCOLD, assuming not complete\n", + dig_port->tc_port_name); + return false; + } + + return val & TCSS_DDI_STATUS_READY; +} + +static bool adl_tc_phy_take_ownership(struct intel_digital_port *dig_port, + bool take) +{ + struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev); + enum port port = dig_port->base.port; + + intel_de_rmw(i915, DDI_BUF_CTL(port), DDI_BUF_CTL_TC_PHY_OWNERSHIP, + take ? DDI_BUF_CTL_TC_PHY_OWNERSHIP : 0); + + return true; +} + +static bool adl_tc_phy_is_owned(struct intel_digital_port *dig_port) +{ + struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev); + enum port port = dig_port->base.port; + u32 val; + + val = intel_de_read(i915, DDI_BUF_CTL(port)); + return val & DDI_BUF_CTL_TC_PHY_OWNERSHIP; +} + +/* + * Generic TC PHY handlers + * ----------------------- + */ +static u32 tc_port_live_status_mask(struct intel_digital_port *dig_port) +{ + struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev); + + if (IS_ALDERLAKE_P(i915)) + return adl_tc_port_live_status_mask(dig_port); + + return icl_tc_port_live_status_mask(dig_port); +} + +static bool tc_phy_status_complete(struct intel_digital_port *dig_port) +{ + struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev); + + if (IS_ALDERLAKE_P(i915)) + return adl_tc_phy_status_complete(dig_port); + + return icl_tc_phy_status_complete(dig_port); +} + +static bool tc_phy_is_owned(struct intel_digital_port *dig_port) +{ + struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev); + + if (IS_ALDERLAKE_P(i915)) + return adl_tc_phy_is_owned(dig_port); + + return icl_tc_phy_is_owned(dig_port); +} + +static bool tc_phy_take_ownership(struct intel_digital_port *dig_port, bool take) +{ + struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev); + + if (IS_ALDERLAKE_P(i915)) + return adl_tc_phy_take_ownership(dig_port, take); + + return icl_tc_phy_take_ownership(dig_port, take); +} + static bool tc_phy_is_ready_and_owned(struct intel_digital_port *dig_port, bool phy_is_ready, bool phy_is_owned) { -- cgit From 359d36e67da9de877c13c4b4ea6209a5e07264b3 Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Thu, 23 Mar 2023 16:20:08 +0200 Subject: drm/i915/tc: Use the adlp prefix for ADLP TC PHY functions Use the usual adlp prefix for all ADLP specific TC PHY functions. Other ADL platforms don't support TC. Reviewed-by: Mika Kahola Signed-off-by: Imre Deak Link: https://patchwork.freedesktop.org/patch/msgid/20230323142035.1432621-3-imre.deak@intel.com --- drivers/gpu/drm/i915/display/intel_tc.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) (limited to 'drivers/gpu/drm/i915/display/intel_tc.c') diff --git a/drivers/gpu/drm/i915/display/intel_tc.c b/drivers/gpu/drm/i915/display/intel_tc.c index 37b1f174301e..09b834d781b1 100644 --- a/drivers/gpu/drm/i915/display/intel_tc.c +++ b/drivers/gpu/drm/i915/display/intel_tc.c @@ -457,7 +457,7 @@ static void icl_tc_phy_disconnect(struct intel_digital_port *dig_port) * ADLP TC PHY handlers * -------------------- */ -static u32 adl_tc_port_live_status_mask(struct intel_digital_port *dig_port) +static u32 adlp_tc_port_live_status_mask(struct intel_digital_port *dig_port) { struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev); enum tc_port tc_port = intel_port_to_tc(i915, dig_port->base.port); @@ -492,7 +492,7 @@ static u32 adl_tc_port_live_status_mask(struct intel_digital_port *dig_port) * DP-alt, legacy or nothing). For TBT-alt sinks the PHY is owned by the TBT * subsystem and so switching the ownership to display is not required. */ -static bool adl_tc_phy_status_complete(struct intel_digital_port *dig_port) +static bool adlp_tc_phy_status_complete(struct intel_digital_port *dig_port) { struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev); enum tc_port tc_port = intel_port_to_tc(i915, dig_port->base.port); @@ -509,8 +509,8 @@ static bool adl_tc_phy_status_complete(struct intel_digital_port *dig_port) return val & TCSS_DDI_STATUS_READY; } -static bool adl_tc_phy_take_ownership(struct intel_digital_port *dig_port, - bool take) +static bool adlp_tc_phy_take_ownership(struct intel_digital_port *dig_port, + bool take) { struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev); enum port port = dig_port->base.port; @@ -521,7 +521,7 @@ static bool adl_tc_phy_take_ownership(struct intel_digital_port *dig_port, return true; } -static bool adl_tc_phy_is_owned(struct intel_digital_port *dig_port) +static bool adlp_tc_phy_is_owned(struct intel_digital_port *dig_port) { struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev); enum port port = dig_port->base.port; @@ -540,7 +540,7 @@ static u32 tc_port_live_status_mask(struct intel_digital_port *dig_port) struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev); if (IS_ALDERLAKE_P(i915)) - return adl_tc_port_live_status_mask(dig_port); + return adlp_tc_port_live_status_mask(dig_port); return icl_tc_port_live_status_mask(dig_port); } @@ -550,7 +550,7 @@ static bool tc_phy_status_complete(struct intel_digital_port *dig_port) struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev); if (IS_ALDERLAKE_P(i915)) - return adl_tc_phy_status_complete(dig_port); + return adlp_tc_phy_status_complete(dig_port); return icl_tc_phy_status_complete(dig_port); } @@ -560,7 +560,7 @@ static bool tc_phy_is_owned(struct intel_digital_port *dig_port) struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev); if (IS_ALDERLAKE_P(i915)) - return adl_tc_phy_is_owned(dig_port); + return adlp_tc_phy_is_owned(dig_port); return icl_tc_phy_is_owned(dig_port); } @@ -570,7 +570,7 @@ static bool tc_phy_take_ownership(struct intel_digital_port *dig_port, bool take struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev); if (IS_ALDERLAKE_P(i915)) - return adl_tc_phy_take_ownership(dig_port, take); + return adlp_tc_phy_take_ownership(dig_port, take); return icl_tc_phy_take_ownership(dig_port, take); } -- cgit From 89b154091ab44098668614b52553ae3917eb8215 Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Thu, 23 Mar 2023 16:20:09 +0200 Subject: drm/i915/tc: Rename tc_phy_status_complete() to tc_phy_is_ready() For consistency rename tc_phy_status_complete() to tc_phy_is_ready() following the terminology of new platforms. Reviewed-by: Mika Kahola Signed-off-by: Imre Deak Link: https://patchwork.freedesktop.org/patch/msgid/20230323142035.1432621-4-imre.deak@intel.com --- drivers/gpu/drm/i915/display/intel_tc.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) (limited to 'drivers/gpu/drm/i915/display/intel_tc.c') diff --git a/drivers/gpu/drm/i915/display/intel_tc.c b/drivers/gpu/drm/i915/display/intel_tc.c index 09b834d781b1..1705ca4d3ebc 100644 --- a/drivers/gpu/drm/i915/display/intel_tc.c +++ b/drivers/gpu/drm/i915/display/intel_tc.c @@ -16,7 +16,7 @@ #include "intel_tc.h" static u32 tc_port_live_status_mask(struct intel_digital_port *dig_port); -static bool tc_phy_status_complete(struct intel_digital_port *dig_port); +static bool tc_phy_is_ready(struct intel_digital_port *dig_port); static bool tc_phy_take_ownership(struct intel_digital_port *dig_port, bool take); static const char *tc_port_mode_name(enum tc_port_mode mode) @@ -303,7 +303,7 @@ static u32 icl_tc_port_live_status_mask(struct intel_digital_port *dig_port) * owned by the TBT subsystem and so switching the ownership to display is not * required. */ -static bool icl_tc_phy_status_complete(struct intel_digital_port *dig_port) +static bool icl_tc_phy_is_ready(struct intel_digital_port *dig_port) { struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev); u32 val; @@ -311,7 +311,7 @@ static bool icl_tc_phy_status_complete(struct intel_digital_port *dig_port) val = intel_de_read(i915, PORT_TX_DFLEXDPPMS(dig_port->tc_phy_fia)); if (val == 0xffffffff) { drm_dbg_kms(&i915->drm, - "Port %s: PHY in TCCOLD, assuming not complete\n", + "Port %s: PHY in TCCOLD, assuming not ready\n", dig_port->tc_port_name); return false; } @@ -377,7 +377,7 @@ static void icl_tc_phy_connect(struct intel_digital_port *dig_port, u32 live_status_mask; int max_lanes; - if (!tc_phy_status_complete(dig_port) && + if (!tc_phy_is_ready(dig_port) && !drm_WARN_ON(&i915->drm, dig_port->tc_legacy_port)) { drm_dbg_kms(&i915->drm, "Port %s: PHY not ready\n", dig_port->tc_port_name); @@ -492,7 +492,7 @@ static u32 adlp_tc_port_live_status_mask(struct intel_digital_port *dig_port) * DP-alt, legacy or nothing). For TBT-alt sinks the PHY is owned by the TBT * subsystem and so switching the ownership to display is not required. */ -static bool adlp_tc_phy_status_complete(struct intel_digital_port *dig_port) +static bool adlp_tc_phy_is_ready(struct intel_digital_port *dig_port) { struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev); enum tc_port tc_port = intel_port_to_tc(i915, dig_port->base.port); @@ -501,7 +501,7 @@ static bool adlp_tc_phy_status_complete(struct intel_digital_port *dig_port) val = intel_de_read(i915, TCSS_DDI_STATUS(tc_port)); if (val == 0xffffffff) { drm_dbg_kms(&i915->drm, - "Port %s: PHY in TCCOLD, assuming not complete\n", + "Port %s: PHY in TCCOLD, assuming not ready\n", dig_port->tc_port_name); return false; } @@ -545,14 +545,14 @@ static u32 tc_port_live_status_mask(struct intel_digital_port *dig_port) return icl_tc_port_live_status_mask(dig_port); } -static bool tc_phy_status_complete(struct intel_digital_port *dig_port) +static bool tc_phy_is_ready(struct intel_digital_port *dig_port) { struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev); if (IS_ALDERLAKE_P(i915)) - return adlp_tc_phy_status_complete(dig_port); + return adlp_tc_phy_is_ready(dig_port); - return icl_tc_phy_status_complete(dig_port); + return icl_tc_phy_is_ready(dig_port); } static bool tc_phy_is_owned(struct intel_digital_port *dig_port) @@ -590,7 +590,7 @@ static bool tc_phy_is_connected(struct intel_digital_port *dig_port, { struct intel_encoder *encoder = &dig_port->base; struct drm_i915_private *i915 = to_i915(encoder->base.dev); - bool phy_is_ready = tc_phy_status_complete(dig_port); + bool phy_is_ready = tc_phy_is_ready(dig_port); bool phy_is_owned = tc_phy_is_owned(dig_port); bool is_connected; @@ -614,7 +614,7 @@ static void tc_phy_wait_for_ready(struct intel_digital_port *dig_port) { struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev); - if (wait_for(tc_phy_status_complete(dig_port), 100)) + if (wait_for(tc_phy_is_ready(dig_port), 100)) drm_err(&i915->drm, "Port %s: timeout waiting for PHY ready\n", dig_port->tc_port_name); } @@ -694,7 +694,7 @@ intel_tc_port_get_current_mode(struct intel_digital_port *dig_port) if (dig_port->tc_legacy_port) tc_phy_wait_for_ready(dig_port); - phy_is_ready = tc_phy_status_complete(dig_port); + phy_is_ready = tc_phy_is_ready(dig_port); phy_is_owned = tc_phy_is_owned(dig_port); if (!tc_phy_is_ready_and_owned(dig_port, phy_is_ready, phy_is_owned)) { -- cgit From 39feb7b16b107625a0071e5bb8ea19ee89be2a9f Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Thu, 23 Mar 2023 16:20:10 +0200 Subject: drm/i915/tc: Use the tc_phy prefix for all TC PHY functions For consistency use the tc_phy prefix for all TC PHY functions. Reviewed-by: Mika Kahola Signed-off-by: Imre Deak Link: https://patchwork.freedesktop.org/patch/msgid/20230323142035.1432621-5-imre.deak@intel.com --- drivers/gpu/drm/i915/display/intel_tc.c | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) (limited to 'drivers/gpu/drm/i915/display/intel_tc.c') diff --git a/drivers/gpu/drm/i915/display/intel_tc.c b/drivers/gpu/drm/i915/display/intel_tc.c index 1705ca4d3ebc..129e360bd36c 100644 --- a/drivers/gpu/drm/i915/display/intel_tc.c +++ b/drivers/gpu/drm/i915/display/intel_tc.c @@ -15,7 +15,7 @@ #include "intel_mg_phy_regs.h" #include "intel_tc.h" -static u32 tc_port_live_status_mask(struct intel_digital_port *dig_port); +static u32 tc_phy_hpd_live_status(struct intel_digital_port *dig_port); static bool tc_phy_is_ready(struct intel_digital_port *dig_port); static bool tc_phy_take_ownership(struct intel_digital_port *dig_port, bool take); @@ -264,7 +264,7 @@ static void tc_port_fixup_legacy_flag(struct intel_digital_port *dig_port, * ICL TC PHY handlers * ------------------- */ -static u32 icl_tc_port_live_status_mask(struct intel_digital_port *dig_port) +static u32 icl_tc_phy_hpd_live_status(struct intel_digital_port *dig_port) { struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev); u32 isr_bit = i915->display.hotplug.pch_hpd[dig_port->base.hpd_pin]; @@ -384,7 +384,7 @@ static void icl_tc_phy_connect(struct intel_digital_port *dig_port, goto out_set_tbt_alt_mode; } - live_status_mask = tc_port_live_status_mask(dig_port); + live_status_mask = tc_phy_hpd_live_status(dig_port); if (!(live_status_mask & (BIT(TC_PORT_DP_ALT) | BIT(TC_PORT_LEGACY))) && !dig_port->tc_legacy_port) { drm_dbg_kms(&i915->drm, "Port %s: PHY ownership not required (live status %02x)\n", @@ -408,7 +408,7 @@ static void icl_tc_phy_connect(struct intel_digital_port *dig_port, * Now we have to re-check the live state, in case the port recently * became disconnected. Not necessary for legacy mode. */ - if (!(tc_port_live_status_mask(dig_port) & BIT(TC_PORT_DP_ALT))) { + if (!(tc_phy_hpd_live_status(dig_port) & BIT(TC_PORT_DP_ALT))) { drm_dbg_kms(&i915->drm, "Port %s: PHY sudden disconnect\n", dig_port->tc_port_name); goto out_release_phy; @@ -457,7 +457,7 @@ static void icl_tc_phy_disconnect(struct intel_digital_port *dig_port) * ADLP TC PHY handlers * -------------------- */ -static u32 adlp_tc_port_live_status_mask(struct intel_digital_port *dig_port) +static u32 adlp_tc_phy_hpd_live_status(struct intel_digital_port *dig_port) { struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev); enum tc_port tc_port = intel_port_to_tc(i915, dig_port->base.port); @@ -535,14 +535,14 @@ static bool adlp_tc_phy_is_owned(struct intel_digital_port *dig_port) * Generic TC PHY handlers * ----------------------- */ -static u32 tc_port_live_status_mask(struct intel_digital_port *dig_port) +static u32 tc_phy_hpd_live_status(struct intel_digital_port *dig_port) { struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev); if (IS_ALDERLAKE_P(i915)) - return adlp_tc_port_live_status_mask(dig_port); + return adlp_tc_phy_hpd_live_status(dig_port); - return icl_tc_port_live_status_mask(dig_port); + return icl_tc_phy_hpd_live_status(dig_port); } static bool tc_phy_is_ready(struct intel_digital_port *dig_port) @@ -631,7 +631,7 @@ hpd_mask_to_tc_mode(u32 live_status_mask) static enum tc_port_mode tc_phy_hpd_live_mode(struct intel_digital_port *dig_port) { - u32 live_status_mask = tc_port_live_status_mask(dig_port); + u32 live_status_mask = tc_phy_hpd_live_status(dig_port); return hpd_mask_to_tc_mode(live_status_mask); } @@ -678,7 +678,7 @@ get_tc_mode_in_phy_not_owned_state(struct intel_digital_port *dig_port, } static enum tc_port_mode -intel_tc_port_get_current_mode(struct intel_digital_port *dig_port) +tc_phy_get_current_mode(struct intel_digital_port *dig_port) { struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev); enum tc_port_mode live_mode = tc_phy_hpd_live_mode(dig_port); @@ -735,9 +735,9 @@ hpd_mask_to_target_mode(struct intel_digital_port *dig_port, u32 live_status_mas } static enum tc_port_mode -intel_tc_port_get_target_mode(struct intel_digital_port *dig_port) +tc_phy_get_target_mode(struct intel_digital_port *dig_port) { - u32 live_status_mask = tc_port_live_status_mask(dig_port); + u32 live_status_mask = tc_phy_hpd_live_status(dig_port); return hpd_mask_to_target_mode(dig_port, live_status_mask); } @@ -770,7 +770,7 @@ static void intel_tc_port_reset_mode(struct intel_digital_port *dig_port, static bool intel_tc_port_needs_reset(struct intel_digital_port *dig_port) { - return intel_tc_port_get_target_mode(dig_port) != dig_port->tc_mode; + return tc_phy_get_target_mode(dig_port) != dig_port->tc_mode; } static void intel_tc_port_update_mode(struct intel_digital_port *dig_port, @@ -847,7 +847,7 @@ void intel_tc_port_init_mode(struct intel_digital_port *dig_port) tc_cold_wref = tc_cold_block(dig_port, &domain); - dig_port->tc_mode = intel_tc_port_get_current_mode(dig_port); + dig_port->tc_mode = tc_phy_get_current_mode(dig_port); /* * Save the initial mode for the state check in * intel_tc_port_sanitize_mode(). @@ -976,7 +976,7 @@ bool intel_tc_port_connected_locked(struct intel_encoder *encoder) drm_WARN_ON(&i915->drm, !intel_tc_port_ref_held(dig_port)); - return tc_port_live_status_mask(dig_port) & BIT(dig_port->tc_mode); + return tc_phy_hpd_live_status(dig_port) & BIT(dig_port->tc_mode); } bool intel_tc_port_connected(struct intel_encoder *encoder) -- cgit From 3eafcddf766b6bb8e6308ec2d587d4ef7a4381e4 Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Thu, 23 Mar 2023 16:20:11 +0200 Subject: drm/i915/tc: Move TC port fields to a new intel_tc_port struct Move the TC port specific fields from intel_digital_port to a new intel_tc_port struct. Pass an intel_tc_port pointer to all static functions in intel_tc.c keeping dig_port accessible for these via a pointer stored in the new struct. The next patch will allocate the intel_tc_port dynamically, allowing moving the struct definition to intel_tc.c. Reviewed-by: Mika Kahola Signed-off-by: Imre Deak Link: https://patchwork.freedesktop.org/patch/msgid/20230323142035.1432621-6-imre.deak@intel.com --- drivers/gpu/drm/i915/display/intel_display.h | 7 - drivers/gpu/drm/i915/display/intel_display_types.h | 13 +- drivers/gpu/drm/i915/display/intel_tc.c | 578 +++++++++++---------- drivers/gpu/drm/i915/display/intel_tc.h | 26 + 4 files changed, 335 insertions(+), 289 deletions(-) (limited to 'drivers/gpu/drm/i915/display/intel_tc.c') diff --git a/drivers/gpu/drm/i915/display/intel_display.h b/drivers/gpu/drm/i915/display/intel_display.h index 596fd3ec1983..287159bdeb0d 100644 --- a/drivers/gpu/drm/i915/display/intel_display.h +++ b/drivers/gpu/drm/i915/display/intel_display.h @@ -164,13 +164,6 @@ enum tc_port { I915_MAX_TC_PORTS }; -enum tc_port_mode { - TC_PORT_DISCONNECTED, - TC_PORT_TBT_ALT, - TC_PORT_DP_ALT, - TC_PORT_LEGACY, -}; - enum aux_ch { AUX_CH_NONE = -1, diff --git a/drivers/gpu/drm/i915/display/intel_display_types.h b/drivers/gpu/drm/i915/display/intel_display_types.h index d84818fc94a4..3dfc8eb45020 100644 --- a/drivers/gpu/drm/i915/display/intel_display_types.h +++ b/drivers/gpu/drm/i915/display/intel_display_types.h @@ -54,6 +54,7 @@ #include "intel_display_power.h" #include "intel_dpll_mgr.h" #include "intel_wm_types.h" +#include "intel_tc.h" struct drm_printer; struct __intel_global_objs_state; @@ -1782,17 +1783,7 @@ struct intel_digital_port { intel_wakeref_t ddi_io_wakeref; intel_wakeref_t aux_wakeref; - struct mutex tc_lock; /* protects the TypeC port mode */ - intel_wakeref_t tc_lock_wakeref; - enum intel_display_power_domain tc_lock_power_domain; - struct delayed_work tc_disconnect_phy_work; - int tc_link_refcount; - bool tc_legacy_port:1; - char tc_port_name[8]; - enum tc_port_mode tc_mode; - enum tc_port_mode tc_init_mode; - enum phy_fia tc_phy_fia; - u8 tc_phy_fia_idx; + struct intel_tc_port tc; /* protects num_hdcp_streams reference count, hdcp_port_data and hdcp_auth_status */ struct mutex hdcp_mutex; diff --git a/drivers/gpu/drm/i915/display/intel_tc.c b/drivers/gpu/drm/i915/display/intel_tc.c index 129e360bd36c..d6fc9c5bd979 100644 --- a/drivers/gpu/drm/i915/display/intel_tc.c +++ b/drivers/gpu/drm/i915/display/intel_tc.c @@ -15,9 +15,9 @@ #include "intel_mg_phy_regs.h" #include "intel_tc.h" -static u32 tc_phy_hpd_live_status(struct intel_digital_port *dig_port); -static bool tc_phy_is_ready(struct intel_digital_port *dig_port); -static bool tc_phy_take_ownership(struct intel_digital_port *dig_port, bool take); +static u32 tc_phy_hpd_live_status(struct intel_tc_port *tc); +static bool tc_phy_is_ready(struct intel_tc_port *tc); +static bool tc_phy_take_ownership(struct intel_tc_port *tc, bool take); static const char *tc_port_mode_name(enum tc_port_mode mode) { @@ -34,13 +34,24 @@ static const char *tc_port_mode_name(enum tc_port_mode mode) return names[mode]; } +static struct intel_tc_port *to_tc_port(struct intel_digital_port *dig_port) +{ + return &dig_port->tc; +} + +static struct drm_i915_private *tc_to_i915(struct intel_tc_port *tc) +{ + return to_i915(tc->dig_port->base.base.dev); +} + static bool intel_tc_port_in_mode(struct intel_digital_port *dig_port, enum tc_port_mode mode) { struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev); enum phy phy = intel_port_to_phy(i915, dig_port->base.port); + struct intel_tc_port *tc = to_tc_port(dig_port); - return intel_phy_is_tc(i915, phy) && dig_port->tc_mode == mode; + return intel_phy_is_tc(i915, phy) && tc->mode == mode; } bool intel_tc_port_in_tbt_alt_mode(struct intel_digital_port *dig_port) @@ -61,15 +72,17 @@ bool intel_tc_port_in_legacy_mode(struct intel_digital_port *dig_port) bool intel_tc_cold_requires_aux_pw(struct intel_digital_port *dig_port) { struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev); + struct intel_tc_port *tc = to_tc_port(dig_port); - return (DISPLAY_VER(i915) == 11 && dig_port->tc_legacy_port) || + return (DISPLAY_VER(i915) == 11 && tc->legacy_port) || IS_ALDERLAKE_P(i915); } static enum intel_display_power_domain -tc_cold_get_power_domain(struct intel_digital_port *dig_port, enum tc_port_mode mode) +tc_cold_get_power_domain(struct intel_tc_port *tc, enum tc_port_mode mode) { - struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev); + struct drm_i915_private *i915 = tc_to_i915(tc); + struct intel_digital_port *dig_port = tc->dig_port; if (mode == TC_PORT_TBT_ALT || !intel_tc_cold_requires_aux_pw(dig_port)) return POWER_DOMAIN_TC_COLD_OFF; @@ -78,27 +91,27 @@ tc_cold_get_power_domain(struct intel_digital_port *dig_port, enum tc_port_mode } static intel_wakeref_t -tc_cold_block_in_mode(struct intel_digital_port *dig_port, enum tc_port_mode mode, +tc_cold_block_in_mode(struct intel_tc_port *tc, enum tc_port_mode mode, enum intel_display_power_domain *domain) { - struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev); + struct drm_i915_private *i915 = tc_to_i915(tc); - *domain = tc_cold_get_power_domain(dig_port, mode); + *domain = tc_cold_get_power_domain(tc, mode); return intel_display_power_get(i915, *domain); } static intel_wakeref_t -tc_cold_block(struct intel_digital_port *dig_port, enum intel_display_power_domain *domain) +tc_cold_block(struct intel_tc_port *tc, enum intel_display_power_domain *domain) { - return tc_cold_block_in_mode(dig_port, dig_port->tc_mode, domain); + return tc_cold_block_in_mode(tc, tc->mode, domain); } static void -tc_cold_unblock(struct intel_digital_port *dig_port, enum intel_display_power_domain domain, +tc_cold_unblock(struct intel_tc_port *tc, enum intel_display_power_domain domain, intel_wakeref_t wakeref) { - struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev); + struct drm_i915_private *i915 = tc_to_i915(tc); /* * wakeref == -1, means some error happened saving save_depot_stack but @@ -112,73 +125,76 @@ tc_cold_unblock(struct intel_digital_port *dig_port, enum intel_display_power_do } static void -assert_tc_cold_blocked(struct intel_digital_port *dig_port) +assert_tc_cold_blocked(struct intel_tc_port *tc) { - struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev); + struct drm_i915_private *i915 = tc_to_i915(tc); bool enabled; enabled = intel_display_power_is_enabled(i915, - tc_cold_get_power_domain(dig_port, - dig_port->tc_mode)); + tc_cold_get_power_domain(tc, + tc->mode)); drm_WARN_ON(&i915->drm, !enabled); } static enum intel_display_power_domain -tc_port_power_domain(struct intel_digital_port *dig_port) +tc_port_power_domain(struct intel_tc_port *tc) { - struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev); - enum tc_port tc_port = intel_port_to_tc(i915, dig_port->base.port); + struct drm_i915_private *i915 = tc_to_i915(tc); + enum tc_port tc_port = intel_port_to_tc(i915, tc->dig_port->base.port); return POWER_DOMAIN_PORT_DDI_LANES_TC1 + tc_port - TC_PORT_1; } static void -assert_tc_port_power_enabled(struct intel_digital_port *dig_port) +assert_tc_port_power_enabled(struct intel_tc_port *tc) { - struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev); + struct drm_i915_private *i915 = tc_to_i915(tc); drm_WARN_ON(&i915->drm, - !intel_display_power_is_enabled(i915, tc_port_power_domain(dig_port))); + !intel_display_power_is_enabled(i915, tc_port_power_domain(tc))); } u32 intel_tc_port_get_lane_mask(struct intel_digital_port *dig_port) { struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev); + struct intel_tc_port *tc = to_tc_port(dig_port); u32 lane_mask; - lane_mask = intel_de_read(i915, PORT_TX_DFLEXDPSP(dig_port->tc_phy_fia)); + lane_mask = intel_de_read(i915, PORT_TX_DFLEXDPSP(tc->phy_fia)); drm_WARN_ON(&i915->drm, lane_mask == 0xffffffff); - assert_tc_cold_blocked(dig_port); + assert_tc_cold_blocked(tc); - lane_mask &= DP_LANE_ASSIGNMENT_MASK(dig_port->tc_phy_fia_idx); - return lane_mask >> DP_LANE_ASSIGNMENT_SHIFT(dig_port->tc_phy_fia_idx); + lane_mask &= DP_LANE_ASSIGNMENT_MASK(tc->phy_fia_idx); + return lane_mask >> DP_LANE_ASSIGNMENT_SHIFT(tc->phy_fia_idx); } u32 intel_tc_port_get_pin_assignment_mask(struct intel_digital_port *dig_port) { struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev); + struct intel_tc_port *tc = to_tc_port(dig_port); u32 pin_mask; - pin_mask = intel_de_read(i915, PORT_TX_DFLEXPA1(dig_port->tc_phy_fia)); + pin_mask = intel_de_read(i915, PORT_TX_DFLEXPA1(tc->phy_fia)); drm_WARN_ON(&i915->drm, pin_mask == 0xffffffff); - assert_tc_cold_blocked(dig_port); + assert_tc_cold_blocked(tc); - return (pin_mask & DP_PIN_ASSIGNMENT_MASK(dig_port->tc_phy_fia_idx)) >> - DP_PIN_ASSIGNMENT_SHIFT(dig_port->tc_phy_fia_idx); + return (pin_mask & DP_PIN_ASSIGNMENT_MASK(tc->phy_fia_idx)) >> + DP_PIN_ASSIGNMENT_SHIFT(tc->phy_fia_idx); } int intel_tc_port_fia_max_lane_count(struct intel_digital_port *dig_port) { struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev); + struct intel_tc_port *tc = to_tc_port(dig_port); intel_wakeref_t wakeref; u32 lane_mask; - if (dig_port->tc_mode != TC_PORT_DP_ALT) + if (tc->mode != TC_PORT_DP_ALT) return 4; - assert_tc_cold_blocked(dig_port); + assert_tc_cold_blocked(tc); lane_mask = 0; with_intel_display_power(i915, POWER_DOMAIN_DISPLAY_CORE, wakeref) @@ -205,45 +221,46 @@ void intel_tc_port_set_fia_lane_count(struct intel_digital_port *dig_port, int required_lanes) { struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev); + struct intel_tc_port *tc = to_tc_port(dig_port); bool lane_reversal = dig_port->saved_port_bits & DDI_BUF_PORT_REVERSAL; u32 val; drm_WARN_ON(&i915->drm, - lane_reversal && dig_port->tc_mode != TC_PORT_LEGACY); + lane_reversal && tc->mode != TC_PORT_LEGACY); - assert_tc_cold_blocked(dig_port); + assert_tc_cold_blocked(tc); - val = intel_de_read(i915, PORT_TX_DFLEXDPMLE1(dig_port->tc_phy_fia)); - val &= ~DFLEXDPMLE1_DPMLETC_MASK(dig_port->tc_phy_fia_idx); + val = intel_de_read(i915, PORT_TX_DFLEXDPMLE1(tc->phy_fia)); + val &= ~DFLEXDPMLE1_DPMLETC_MASK(tc->phy_fia_idx); switch (required_lanes) { case 1: val |= lane_reversal ? - DFLEXDPMLE1_DPMLETC_ML3(dig_port->tc_phy_fia_idx) : - DFLEXDPMLE1_DPMLETC_ML0(dig_port->tc_phy_fia_idx); + DFLEXDPMLE1_DPMLETC_ML3(tc->phy_fia_idx) : + DFLEXDPMLE1_DPMLETC_ML0(tc->phy_fia_idx); break; case 2: val |= lane_reversal ? - DFLEXDPMLE1_DPMLETC_ML3_2(dig_port->tc_phy_fia_idx) : - DFLEXDPMLE1_DPMLETC_ML1_0(dig_port->tc_phy_fia_idx); + DFLEXDPMLE1_DPMLETC_ML3_2(tc->phy_fia_idx) : + DFLEXDPMLE1_DPMLETC_ML1_0(tc->phy_fia_idx); break; case 4: - val |= DFLEXDPMLE1_DPMLETC_ML3_0(dig_port->tc_phy_fia_idx); + val |= DFLEXDPMLE1_DPMLETC_ML3_0(tc->phy_fia_idx); break; default: MISSING_CASE(required_lanes); } - intel_de_write(i915, PORT_TX_DFLEXDPMLE1(dig_port->tc_phy_fia), val); + intel_de_write(i915, PORT_TX_DFLEXDPMLE1(tc->phy_fia), val); } -static void tc_port_fixup_legacy_flag(struct intel_digital_port *dig_port, +static void tc_port_fixup_legacy_flag(struct intel_tc_port *tc, u32 live_status_mask) { - struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev); + struct drm_i915_private *i915 = tc_to_i915(tc); u32 valid_hpd_mask; - if (dig_port->tc_legacy_port) + if (tc->legacy_port) valid_hpd_mask = BIT(TC_PORT_LEGACY); else valid_hpd_mask = BIT(TC_PORT_DP_ALT) | @@ -255,34 +272,35 @@ static void tc_port_fixup_legacy_flag(struct intel_digital_port *dig_port, /* If live status mismatches the VBT flag, trust the live status. */ drm_dbg_kms(&i915->drm, "Port %s: live status %08x mismatches the legacy port flag %08x, fixing flag\n", - dig_port->tc_port_name, live_status_mask, valid_hpd_mask); + tc->port_name, live_status_mask, valid_hpd_mask); - dig_port->tc_legacy_port = !dig_port->tc_legacy_port; + tc->legacy_port = !tc->legacy_port; } /* * ICL TC PHY handlers * ------------------- */ -static u32 icl_tc_phy_hpd_live_status(struct intel_digital_port *dig_port) +static u32 icl_tc_phy_hpd_live_status(struct intel_tc_port *tc) { - struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev); + struct drm_i915_private *i915 = tc_to_i915(tc); + struct intel_digital_port *dig_port = tc->dig_port; u32 isr_bit = i915->display.hotplug.pch_hpd[dig_port->base.hpd_pin]; u32 mask = 0; u32 val; - val = intel_de_read(i915, PORT_TX_DFLEXDPSP(dig_port->tc_phy_fia)); + val = intel_de_read(i915, PORT_TX_DFLEXDPSP(tc->phy_fia)); if (val == 0xffffffff) { drm_dbg_kms(&i915->drm, "Port %s: PHY in TCCOLD, nothing connected\n", - dig_port->tc_port_name); + tc->port_name); return mask; } - if (val & TC_LIVE_STATE_TBT(dig_port->tc_phy_fia_idx)) + if (val & TC_LIVE_STATE_TBT(tc->phy_fia_idx)) mask |= BIT(TC_PORT_TBT_ALT); - if (val & TC_LIVE_STATE_TC(dig_port->tc_phy_fia_idx)) + if (val & TC_LIVE_STATE_TC(tc->phy_fia_idx)) mask |= BIT(TC_PORT_DP_ALT); if (intel_de_read(i915, SDEISR) & isr_bit) @@ -290,7 +308,7 @@ static u32 icl_tc_phy_hpd_live_status(struct intel_digital_port *dig_port) /* The sink can be connected only in a single mode. */ if (!drm_WARN_ON_ONCE(&i915->drm, hweight32(mask) > 1)) - tc_port_fixup_legacy_flag(dig_port, mask); + tc_port_fixup_legacy_flag(tc, mask); return mask; } @@ -303,60 +321,60 @@ static u32 icl_tc_phy_hpd_live_status(struct intel_digital_port *dig_port) * owned by the TBT subsystem and so switching the ownership to display is not * required. */ -static bool icl_tc_phy_is_ready(struct intel_digital_port *dig_port) +static bool icl_tc_phy_is_ready(struct intel_tc_port *tc) { - struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev); + struct drm_i915_private *i915 = tc_to_i915(tc); u32 val; - val = intel_de_read(i915, PORT_TX_DFLEXDPPMS(dig_port->tc_phy_fia)); + val = intel_de_read(i915, PORT_TX_DFLEXDPPMS(tc->phy_fia)); if (val == 0xffffffff) { drm_dbg_kms(&i915->drm, "Port %s: PHY in TCCOLD, assuming not ready\n", - dig_port->tc_port_name); + tc->port_name); return false; } - return val & DP_PHY_MODE_STATUS_COMPLETED(dig_port->tc_phy_fia_idx); + return val & DP_PHY_MODE_STATUS_COMPLETED(tc->phy_fia_idx); } -static bool icl_tc_phy_take_ownership(struct intel_digital_port *dig_port, +static bool icl_tc_phy_take_ownership(struct intel_tc_port *tc, bool take) { - struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev); + struct drm_i915_private *i915 = tc_to_i915(tc); u32 val; - val = intel_de_read(i915, PORT_TX_DFLEXDPCSSS(dig_port->tc_phy_fia)); + val = intel_de_read(i915, PORT_TX_DFLEXDPCSSS(tc->phy_fia)); if (val == 0xffffffff) { drm_dbg_kms(&i915->drm, "Port %s: PHY in TCCOLD, can't %s ownership\n", - dig_port->tc_port_name, take ? "take" : "release"); + tc->port_name, take ? "take" : "release"); return false; } - val &= ~DP_PHY_MODE_STATUS_NOT_SAFE(dig_port->tc_phy_fia_idx); + val &= ~DP_PHY_MODE_STATUS_NOT_SAFE(tc->phy_fia_idx); if (take) - val |= DP_PHY_MODE_STATUS_NOT_SAFE(dig_port->tc_phy_fia_idx); + val |= DP_PHY_MODE_STATUS_NOT_SAFE(tc->phy_fia_idx); - intel_de_write(i915, PORT_TX_DFLEXDPCSSS(dig_port->tc_phy_fia), val); + intel_de_write(i915, PORT_TX_DFLEXDPCSSS(tc->phy_fia), val); return true; } -static bool icl_tc_phy_is_owned(struct intel_digital_port *dig_port) +static bool icl_tc_phy_is_owned(struct intel_tc_port *tc) { - struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev); + struct drm_i915_private *i915 = tc_to_i915(tc); u32 val; - val = intel_de_read(i915, PORT_TX_DFLEXDPCSSS(dig_port->tc_phy_fia)); + val = intel_de_read(i915, PORT_TX_DFLEXDPCSSS(tc->phy_fia)); if (val == 0xffffffff) { drm_dbg_kms(&i915->drm, "Port %s: PHY in TCCOLD, assume not owned\n", - dig_port->tc_port_name); + tc->port_name); return false; } - return val & DP_PHY_MODE_STATUS_NOT_SAFE(dig_port->tc_phy_fia_idx); + return val & DP_PHY_MODE_STATUS_NOT_SAFE(tc->phy_fia_idx); } /* @@ -370,36 +388,37 @@ static bool icl_tc_phy_is_owned(struct intel_digital_port *dig_port) * connect and disconnect to cleanly transfer ownership with the controller and * set the type-C power state. */ -static void icl_tc_phy_connect(struct intel_digital_port *dig_port, +static void icl_tc_phy_connect(struct intel_tc_port *tc, int required_lanes) { - struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev); + struct drm_i915_private *i915 = tc_to_i915(tc); + struct intel_digital_port *dig_port = tc->dig_port; u32 live_status_mask; int max_lanes; - if (!tc_phy_is_ready(dig_port) && - !drm_WARN_ON(&i915->drm, dig_port->tc_legacy_port)) { + if (!tc_phy_is_ready(tc) && + !drm_WARN_ON(&i915->drm, tc->legacy_port)) { drm_dbg_kms(&i915->drm, "Port %s: PHY not ready\n", - dig_port->tc_port_name); + tc->port_name); goto out_set_tbt_alt_mode; } - live_status_mask = tc_phy_hpd_live_status(dig_port); + live_status_mask = tc_phy_hpd_live_status(tc); if (!(live_status_mask & (BIT(TC_PORT_DP_ALT) | BIT(TC_PORT_LEGACY))) && - !dig_port->tc_legacy_port) { + !tc->legacy_port) { drm_dbg_kms(&i915->drm, "Port %s: PHY ownership not required (live status %02x)\n", - dig_port->tc_port_name, live_status_mask); + tc->port_name, live_status_mask); goto out_set_tbt_alt_mode; } - if (!tc_phy_take_ownership(dig_port, true) && - !drm_WARN_ON(&i915->drm, dig_port->tc_legacy_port)) + if (!tc_phy_take_ownership(tc, true) && + !drm_WARN_ON(&i915->drm, tc->legacy_port)) goto out_set_tbt_alt_mode; max_lanes = intel_tc_port_fia_max_lane_count(dig_port); - if (dig_port->tc_legacy_port) { + if (tc->legacy_port) { drm_WARN_ON(&i915->drm, max_lanes != 4); - dig_port->tc_mode = TC_PORT_LEGACY; + tc->mode = TC_PORT_LEGACY; return; } @@ -408,48 +427,48 @@ static void icl_tc_phy_connect(struct intel_digital_port *dig_port, * Now we have to re-check the live state, in case the port recently * became disconnected. Not necessary for legacy mode. */ - if (!(tc_phy_hpd_live_status(dig_port) & BIT(TC_PORT_DP_ALT))) { + if (!(tc_phy_hpd_live_status(tc) & BIT(TC_PORT_DP_ALT))) { drm_dbg_kms(&i915->drm, "Port %s: PHY sudden disconnect\n", - dig_port->tc_port_name); + tc->port_name); goto out_release_phy; } if (max_lanes < required_lanes) { drm_dbg_kms(&i915->drm, "Port %s: PHY max lanes %d < required lanes %d\n", - dig_port->tc_port_name, + tc->port_name, max_lanes, required_lanes); goto out_release_phy; } - dig_port->tc_mode = TC_PORT_DP_ALT; + tc->mode = TC_PORT_DP_ALT; return; out_release_phy: - tc_phy_take_ownership(dig_port, false); + tc_phy_take_ownership(tc, false); out_set_tbt_alt_mode: - dig_port->tc_mode = TC_PORT_TBT_ALT; + tc->mode = TC_PORT_TBT_ALT; } /* * See the comment at the connect function. This implements the Disconnect * Flow. */ -static void icl_tc_phy_disconnect(struct intel_digital_port *dig_port) +static void icl_tc_phy_disconnect(struct intel_tc_port *tc) { - switch (dig_port->tc_mode) { + switch (tc->mode) { case TC_PORT_LEGACY: case TC_PORT_DP_ALT: - tc_phy_take_ownership(dig_port, false); + tc_phy_take_ownership(tc, false); fallthrough; case TC_PORT_TBT_ALT: - dig_port->tc_mode = TC_PORT_DISCONNECTED; + tc->mode = TC_PORT_DISCONNECTED; fallthrough; case TC_PORT_DISCONNECTED: break; default: - MISSING_CASE(dig_port->tc_mode); + MISSING_CASE(tc->mode); } } @@ -457,9 +476,10 @@ static void icl_tc_phy_disconnect(struct intel_digital_port *dig_port) * ADLP TC PHY handlers * -------------------- */ -static u32 adlp_tc_phy_hpd_live_status(struct intel_digital_port *dig_port) +static u32 adlp_tc_phy_hpd_live_status(struct intel_tc_port *tc) { - struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev); + struct drm_i915_private *i915 = tc_to_i915(tc); + struct intel_digital_port *dig_port = tc->dig_port; enum tc_port tc_port = intel_port_to_tc(i915, dig_port->base.port); u32 isr_bit = i915->display.hotplug.pch_hpd[dig_port->base.hpd_pin]; u32 val, mask = 0; @@ -480,7 +500,7 @@ static u32 adlp_tc_phy_hpd_live_status(struct intel_digital_port *dig_port) /* The sink can be connected only in a single mode. */ if (!drm_WARN_ON(&i915->drm, hweight32(mask) > 1)) - tc_port_fixup_legacy_flag(dig_port, mask); + tc_port_fixup_legacy_flag(tc, mask); return mask; } @@ -492,28 +512,28 @@ static u32 adlp_tc_phy_hpd_live_status(struct intel_digital_port *dig_port) * DP-alt, legacy or nothing). For TBT-alt sinks the PHY is owned by the TBT * subsystem and so switching the ownership to display is not required. */ -static bool adlp_tc_phy_is_ready(struct intel_digital_port *dig_port) +static bool adlp_tc_phy_is_ready(struct intel_tc_port *tc) { - struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev); - enum tc_port tc_port = intel_port_to_tc(i915, dig_port->base.port); + struct drm_i915_private *i915 = tc_to_i915(tc); + enum tc_port tc_port = intel_port_to_tc(i915, tc->dig_port->base.port); u32 val; val = intel_de_read(i915, TCSS_DDI_STATUS(tc_port)); if (val == 0xffffffff) { drm_dbg_kms(&i915->drm, "Port %s: PHY in TCCOLD, assuming not ready\n", - dig_port->tc_port_name); + tc->port_name); return false; } return val & TCSS_DDI_STATUS_READY; } -static bool adlp_tc_phy_take_ownership(struct intel_digital_port *dig_port, +static bool adlp_tc_phy_take_ownership(struct intel_tc_port *tc, bool take) { - struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev); - enum port port = dig_port->base.port; + struct drm_i915_private *i915 = tc_to_i915(tc); + enum port port = tc->dig_port->base.port; intel_de_rmw(i915, DDI_BUF_CTL(port), DDI_BUF_CTL_TC_PHY_OWNERSHIP, take ? DDI_BUF_CTL_TC_PHY_OWNERSHIP : 0); @@ -521,10 +541,10 @@ static bool adlp_tc_phy_take_ownership(struct intel_digital_port *dig_port, return true; } -static bool adlp_tc_phy_is_owned(struct intel_digital_port *dig_port) +static bool adlp_tc_phy_is_owned(struct intel_tc_port *tc) { - struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev); - enum port port = dig_port->base.port; + struct drm_i915_private *i915 = tc_to_i915(tc); + enum port port = tc->dig_port->base.port; u32 val; val = intel_de_read(i915, DDI_BUF_CTL(port)); @@ -535,73 +555,73 @@ static bool adlp_tc_phy_is_owned(struct intel_digital_port *dig_port) * Generic TC PHY handlers * ----------------------- */ -static u32 tc_phy_hpd_live_status(struct intel_digital_port *dig_port) +static u32 tc_phy_hpd_live_status(struct intel_tc_port *tc) { - struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev); + struct drm_i915_private *i915 = tc_to_i915(tc); if (IS_ALDERLAKE_P(i915)) - return adlp_tc_phy_hpd_live_status(dig_port); + return adlp_tc_phy_hpd_live_status(tc); - return icl_tc_phy_hpd_live_status(dig_port); + return icl_tc_phy_hpd_live_status(tc); } -static bool tc_phy_is_ready(struct intel_digital_port *dig_port) +static bool tc_phy_is_ready(struct intel_tc_port *tc) { - struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev); + struct drm_i915_private *i915 = tc_to_i915(tc); if (IS_ALDERLAKE_P(i915)) - return adlp_tc_phy_is_ready(dig_port); + return adlp_tc_phy_is_ready(tc); - return icl_tc_phy_is_ready(dig_port); + return icl_tc_phy_is_ready(tc); } -static bool tc_phy_is_owned(struct intel_digital_port *dig_port) +static bool tc_phy_is_owned(struct intel_tc_port *tc) { - struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev); + struct drm_i915_private *i915 = tc_to_i915(tc); if (IS_ALDERLAKE_P(i915)) - return adlp_tc_phy_is_owned(dig_port); + return adlp_tc_phy_is_owned(tc); - return icl_tc_phy_is_owned(dig_port); + return icl_tc_phy_is_owned(tc); } -static bool tc_phy_take_ownership(struct intel_digital_port *dig_port, bool take) +static bool tc_phy_take_ownership(struct intel_tc_port *tc, bool take) { - struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev); + struct drm_i915_private *i915 = tc_to_i915(tc); if (IS_ALDERLAKE_P(i915)) - return adlp_tc_phy_take_ownership(dig_port, take); + return adlp_tc_phy_take_ownership(tc, take); - return icl_tc_phy_take_ownership(dig_port, take); + return icl_tc_phy_take_ownership(tc, take); } -static bool tc_phy_is_ready_and_owned(struct intel_digital_port *dig_port, +static bool tc_phy_is_ready_and_owned(struct intel_tc_port *tc, bool phy_is_ready, bool phy_is_owned) { - struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev); + struct drm_i915_private *i915 = tc_to_i915(tc); drm_WARN_ON(&i915->drm, phy_is_owned && !phy_is_ready); return phy_is_ready && phy_is_owned; } -static bool tc_phy_is_connected(struct intel_digital_port *dig_port, +static bool tc_phy_is_connected(struct intel_tc_port *tc, enum icl_port_dpll_id port_pll_type) { - struct intel_encoder *encoder = &dig_port->base; + struct intel_encoder *encoder = &tc->dig_port->base; struct drm_i915_private *i915 = to_i915(encoder->base.dev); - bool phy_is_ready = tc_phy_is_ready(dig_port); - bool phy_is_owned = tc_phy_is_owned(dig_port); + bool phy_is_ready = tc_phy_is_ready(tc); + bool phy_is_owned = tc_phy_is_owned(tc); bool is_connected; - if (tc_phy_is_ready_and_owned(dig_port, phy_is_ready, phy_is_owned)) + if (tc_phy_is_ready_and_owned(tc, phy_is_ready, phy_is_owned)) is_connected = port_pll_type == ICL_PORT_DPLL_MG_PHY; else is_connected = port_pll_type == ICL_PORT_DPLL_DEFAULT; drm_dbg_kms(&i915->drm, "Port %s: PHY connected: %s (ready: %s, owned: %s, pll_type: %s)\n", - dig_port->tc_port_name, + tc->port_name, str_yes_no(is_connected), str_yes_no(phy_is_ready), str_yes_no(phy_is_owned), @@ -610,13 +630,13 @@ static bool tc_phy_is_connected(struct intel_digital_port *dig_port, return is_connected; } -static void tc_phy_wait_for_ready(struct intel_digital_port *dig_port) +static void tc_phy_wait_for_ready(struct intel_tc_port *tc) { - struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev); + struct drm_i915_private *i915 = tc_to_i915(tc); - if (wait_for(tc_phy_is_ready(dig_port), 100)) + if (wait_for(tc_phy_is_ready(tc), 100)) drm_err(&i915->drm, "Port %s: timeout waiting for PHY ready\n", - dig_port->tc_port_name); + tc->port_name); } static enum tc_port_mode @@ -629,15 +649,15 @@ hpd_mask_to_tc_mode(u32 live_status_mask) } static enum tc_port_mode -tc_phy_hpd_live_mode(struct intel_digital_port *dig_port) +tc_phy_hpd_live_mode(struct intel_tc_port *tc) { - u32 live_status_mask = tc_phy_hpd_live_status(dig_port); + u32 live_status_mask = tc_phy_hpd_live_status(tc); return hpd_mask_to_tc_mode(live_status_mask); } static enum tc_port_mode -get_tc_mode_in_phy_owned_state(struct intel_digital_port *dig_port, +get_tc_mode_in_phy_owned_state(struct intel_tc_port *tc, enum tc_port_mode live_mode) { switch (live_mode) { @@ -649,7 +669,7 @@ get_tc_mode_in_phy_owned_state(struct intel_digital_port *dig_port, fallthrough; case TC_PORT_TBT_ALT: case TC_PORT_DISCONNECTED: - if (dig_port->tc_legacy_port) + if (tc->legacy_port) return TC_PORT_LEGACY; else return TC_PORT_DP_ALT; @@ -657,7 +677,7 @@ get_tc_mode_in_phy_owned_state(struct intel_digital_port *dig_port, } static enum tc_port_mode -get_tc_mode_in_phy_not_owned_state(struct intel_digital_port *dig_port, +get_tc_mode_in_phy_not_owned_state(struct intel_tc_port *tc, enum tc_port_mode live_mode) { switch (live_mode) { @@ -670,7 +690,7 @@ get_tc_mode_in_phy_not_owned_state(struct intel_digital_port *dig_port, MISSING_CASE(live_mode); fallthrough; case TC_PORT_DISCONNECTED: - if (dig_port->tc_legacy_port) + if (tc->legacy_port) return TC_PORT_DISCONNECTED; else return TC_PORT_TBT_ALT; @@ -678,10 +698,10 @@ get_tc_mode_in_phy_not_owned_state(struct intel_digital_port *dig_port, } static enum tc_port_mode -tc_phy_get_current_mode(struct intel_digital_port *dig_port) +tc_phy_get_current_mode(struct intel_tc_port *tc) { - struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev); - enum tc_port_mode live_mode = tc_phy_hpd_live_mode(dig_port); + struct drm_i915_private *i915 = tc_to_i915(tc); + enum tc_port_mode live_mode = tc_phy_hpd_live_mode(tc); bool phy_is_ready; bool phy_is_owned; enum tc_port_mode mode; @@ -691,22 +711,22 @@ tc_phy_get_current_mode(struct intel_digital_port *dig_port) * and system resume whether or not a sink is connected. Wait here for * the initialization to get ready. */ - if (dig_port->tc_legacy_port) - tc_phy_wait_for_ready(dig_port); + if (tc->legacy_port) + tc_phy_wait_for_ready(tc); - phy_is_ready = tc_phy_is_ready(dig_port); - phy_is_owned = tc_phy_is_owned(dig_port); + phy_is_ready = tc_phy_is_ready(tc); + phy_is_owned = tc_phy_is_owned(tc); - if (!tc_phy_is_ready_and_owned(dig_port, phy_is_ready, phy_is_owned)) { - mode = get_tc_mode_in_phy_not_owned_state(dig_port, live_mode); + if (!tc_phy_is_ready_and_owned(tc, phy_is_ready, phy_is_owned)) { + mode = get_tc_mode_in_phy_not_owned_state(tc, live_mode); } else { drm_WARN_ON(&i915->drm, live_mode == TC_PORT_TBT_ALT); - mode = get_tc_mode_in_phy_owned_state(dig_port, live_mode); + mode = get_tc_mode_in_phy_owned_state(tc, live_mode); } drm_dbg_kms(&i915->drm, "Port %s: PHY mode: %s (ready: %s, owned: %s, HPD: %s)\n", - dig_port->tc_port_name, + tc->port_name, tc_port_mode_name(mode), str_yes_no(phy_is_ready), str_yes_no(phy_is_owned), @@ -715,38 +735,39 @@ tc_phy_get_current_mode(struct intel_digital_port *dig_port) return mode; } -static enum tc_port_mode default_tc_mode(struct intel_digital_port *dig_port) +static enum tc_port_mode default_tc_mode(struct intel_tc_port *tc) { - if (dig_port->tc_legacy_port) + if (tc->legacy_port) return TC_PORT_LEGACY; return TC_PORT_TBT_ALT; } static enum tc_port_mode -hpd_mask_to_target_mode(struct intel_digital_port *dig_port, u32 live_status_mask) +hpd_mask_to_target_mode(struct intel_tc_port *tc, u32 live_status_mask) { enum tc_port_mode mode = hpd_mask_to_tc_mode(live_status_mask); if (mode != TC_PORT_DISCONNECTED) return mode; - return default_tc_mode(dig_port); + return default_tc_mode(tc); } static enum tc_port_mode -tc_phy_get_target_mode(struct intel_digital_port *dig_port) +tc_phy_get_target_mode(struct intel_tc_port *tc) { - u32 live_status_mask = tc_phy_hpd_live_status(dig_port); + u32 live_status_mask = tc_phy_hpd_live_status(tc); - return hpd_mask_to_target_mode(dig_port, live_status_mask); + return hpd_mask_to_target_mode(tc, live_status_mask); } -static void intel_tc_port_reset_mode(struct intel_digital_port *dig_port, +static void intel_tc_port_reset_mode(struct intel_tc_port *tc, int required_lanes, bool force_disconnect) { - struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev); - enum tc_port_mode old_tc_mode = dig_port->tc_mode; + struct drm_i915_private *i915 = tc_to_i915(tc); + struct intel_digital_port *dig_port = tc->dig_port; + enum tc_port_mode old_tc_mode = tc->mode; intel_display_power_flush_work(i915); if (!intel_tc_cold_requires_aux_pw(dig_port)) { @@ -758,22 +779,22 @@ static void intel_tc_port_reset_mode(struct intel_digital_port *dig_port, drm_WARN_ON(&i915->drm, aux_powered); } - icl_tc_phy_disconnect(dig_port); + icl_tc_phy_disconnect(tc); if (!force_disconnect) - icl_tc_phy_connect(dig_port, required_lanes); + icl_tc_phy_connect(tc, required_lanes); drm_dbg_kms(&i915->drm, "Port %s: TC port mode reset (%s -> %s)\n", - dig_port->tc_port_name, + tc->port_name, tc_port_mode_name(old_tc_mode), - tc_port_mode_name(dig_port->tc_mode)); + tc_port_mode_name(tc->mode)); } -static bool intel_tc_port_needs_reset(struct intel_digital_port *dig_port) +static bool intel_tc_port_needs_reset(struct intel_tc_port *tc) { - return tc_phy_get_target_mode(dig_port) != dig_port->tc_mode; + return tc_phy_get_target_mode(tc) != tc->mode; } -static void intel_tc_port_update_mode(struct intel_digital_port *dig_port, +static void intel_tc_port_update_mode(struct intel_tc_port *tc, int required_lanes, bool force_disconnect) { enum intel_display_power_domain domain; @@ -782,44 +803,44 @@ static void intel_tc_port_update_mode(struct intel_digital_port *dig_port, if (!needs_reset) { /* Get power domain required to check the hotplug live status. */ - wref = tc_cold_block(dig_port, &domain); - needs_reset = intel_tc_port_needs_reset(dig_port); - tc_cold_unblock(dig_port, domain, wref); + wref = tc_cold_block(tc, &domain); + needs_reset = intel_tc_port_needs_reset(tc); + tc_cold_unblock(tc, domain, wref); } if (!needs_reset) return; /* Get power domain required for resetting the mode. */ - wref = tc_cold_block_in_mode(dig_port, TC_PORT_DISCONNECTED, &domain); + wref = tc_cold_block_in_mode(tc, TC_PORT_DISCONNECTED, &domain); - intel_tc_port_reset_mode(dig_port, required_lanes, force_disconnect); + intel_tc_port_reset_mode(tc, required_lanes, force_disconnect); /* Get power domain matching the new mode after reset. */ - tc_cold_unblock(dig_port, dig_port->tc_lock_power_domain, - fetch_and_zero(&dig_port->tc_lock_wakeref)); - if (dig_port->tc_mode != TC_PORT_DISCONNECTED) - dig_port->tc_lock_wakeref = tc_cold_block(dig_port, - &dig_port->tc_lock_power_domain); + tc_cold_unblock(tc, tc->lock_power_domain, + fetch_and_zero(&tc->lock_wakeref)); + if (tc->mode != TC_PORT_DISCONNECTED) + tc->lock_wakeref = tc_cold_block(tc, &tc->lock_power_domain); - tc_cold_unblock(dig_port, domain, wref); + tc_cold_unblock(tc, domain, wref); } -static void __intel_tc_port_get_link(struct intel_digital_port *dig_port) +static void __intel_tc_port_get_link(struct intel_tc_port *tc) { - dig_port->tc_link_refcount++; + tc->link_refcount++; } -static void __intel_tc_port_put_link(struct intel_digital_port *dig_port) +static void __intel_tc_port_put_link(struct intel_tc_port *tc) { - dig_port->tc_link_refcount--; + tc->link_refcount--; } -static bool tc_port_is_enabled(struct intel_digital_port *dig_port) +static bool tc_port_is_enabled(struct intel_tc_port *tc) { - struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev); + struct drm_i915_private *i915 = tc_to_i915(tc); + struct intel_digital_port *dig_port = tc->dig_port; - assert_tc_port_power_enabled(dig_port); + assert_tc_port_power_enabled(tc); return intel_de_read(i915, DDI_BUF_CTL(dig_port->base.port)) & DDI_BUF_CTL_ENABLE; @@ -835,27 +856,28 @@ static bool tc_port_is_enabled(struct intel_digital_port *dig_port) 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_tc_port *tc = to_tc_port(dig_port); intel_wakeref_t tc_cold_wref; enum intel_display_power_domain domain; bool update_mode = false; - mutex_lock(&dig_port->tc_lock); + mutex_lock(&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, tc->mode != TC_PORT_DISCONNECTED); + drm_WARN_ON(&i915->drm, tc->lock_wakeref); + drm_WARN_ON(&i915->drm, tc->link_refcount); - tc_cold_wref = tc_cold_block(dig_port, &domain); + tc_cold_wref = tc_cold_block(tc, &domain); - dig_port->tc_mode = tc_phy_get_current_mode(dig_port); + tc->mode = tc_phy_get_current_mode(tc); /* * Save the initial mode for the state check in * intel_tc_port_sanitize_mode(). */ - dig_port->tc_init_mode = dig_port->tc_mode; - if (dig_port->tc_mode != TC_PORT_DISCONNECTED) - dig_port->tc_lock_wakeref = - tc_cold_block(dig_port, &dig_port->tc_lock_power_domain); + tc->init_mode = tc->mode; + if (tc->mode != TC_PORT_DISCONNECTED) + tc->lock_wakeref = + tc_cold_block(tc, &tc->lock_power_domain); /* * The PHY needs to be connected for AUX to work during HW readout and @@ -868,31 +890,32 @@ void intel_tc_port_init_mode(struct intel_digital_port *dig_port) * cause a problem as the PHY ownership state is ignored by the * IOM/TCSS firmware (only display can own the PHY in that case). */ - if (!tc_port_is_enabled(dig_port)) { + if (!tc_port_is_enabled(tc)) { update_mode = true; - } else if (dig_port->tc_mode == TC_PORT_DISCONNECTED) { - drm_WARN_ON(&i915->drm, !dig_port->tc_legacy_port); + } else if (tc->mode == TC_PORT_DISCONNECTED) { + drm_WARN_ON(&i915->drm, !tc->legacy_port); drm_err(&i915->drm, "Port %s: PHY disconnected on enabled port, connecting it\n", - dig_port->tc_port_name); + tc->port_name); update_mode = true; } if (update_mode) - intel_tc_port_update_mode(dig_port, 1, false); + intel_tc_port_update_mode(tc, 1, false); - /* Prevent changing dig_port->tc_mode until intel_tc_port_sanitize_mode() is called. */ - __intel_tc_port_get_link(dig_port); + /* Prevent changing tc->mode until intel_tc_port_sanitize_mode() is called. */ + __intel_tc_port_get_link(tc); - tc_cold_unblock(dig_port, domain, tc_cold_wref); + tc_cold_unblock(tc, domain, tc_cold_wref); - mutex_unlock(&dig_port->tc_lock); + mutex_unlock(&tc->lock); } -static bool tc_port_has_active_links(struct intel_digital_port *dig_port, +static bool tc_port_has_active_links(struct intel_tc_port *tc, const struct intel_crtc_state *crtc_state) { - struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev); + struct drm_i915_private *i915 = tc_to_i915(tc); + struct intel_digital_port *dig_port = tc->dig_port; enum icl_port_dpll_id pll_type = ICL_PORT_DPLL_DEFAULT; int active_links = 0; @@ -904,10 +927,10 @@ static bool tc_port_has_active_links(struct intel_digital_port *dig_port, active_links = 1; } - if (active_links && !tc_phy_is_connected(dig_port, pll_type)) + if (active_links && !tc_phy_is_connected(tc, pll_type)) drm_err(&i915->drm, "Port %s: PHY disconnected with %d active link(s)\n", - dig_port->tc_port_name, active_links); + tc->port_name, active_links); return active_links; } @@ -928,35 +951,36 @@ void intel_tc_port_sanitize_mode(struct intel_digital_port *dig_port, const struct intel_crtc_state *crtc_state) { struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev); + struct intel_tc_port *tc = to_tc_port(dig_port); - mutex_lock(&dig_port->tc_lock); + mutex_lock(&tc->lock); - drm_WARN_ON(&i915->drm, dig_port->tc_link_refcount != 1); - if (!tc_port_has_active_links(dig_port, crtc_state)) { + drm_WARN_ON(&i915->drm, tc->link_refcount != 1); + if (!tc_port_has_active_links(tc, crtc_state)) { /* * TBT-alt is the default mode in any case the PHY ownership is not * held (regardless of the sink's connected live state), so * we'll just switch to disconnected mode from it here without * a note. */ - if (dig_port->tc_init_mode != TC_PORT_TBT_ALT && - dig_port->tc_init_mode != TC_PORT_DISCONNECTED) + if (tc->init_mode != TC_PORT_TBT_ALT && + tc->init_mode != TC_PORT_DISCONNECTED) drm_dbg_kms(&i915->drm, "Port %s: PHY left in %s mode on disabled port, disconnecting it\n", - dig_port->tc_port_name, - tc_port_mode_name(dig_port->tc_init_mode)); - icl_tc_phy_disconnect(dig_port); - __intel_tc_port_put_link(dig_port); + tc->port_name, + tc_port_mode_name(tc->init_mode)); + icl_tc_phy_disconnect(tc); + __intel_tc_port_put_link(tc); - tc_cold_unblock(dig_port, dig_port->tc_lock_power_domain, - fetch_and_zero(&dig_port->tc_lock_wakeref)); + tc_cold_unblock(tc, tc->lock_power_domain, + fetch_and_zero(&tc->lock_wakeref)); } drm_dbg_kms(&i915->drm, "Port %s: sanitize mode (%s)\n", - dig_port->tc_port_name, - tc_port_mode_name(dig_port->tc_mode)); + tc->port_name, + tc_port_mode_name(tc->mode)); - mutex_unlock(&dig_port->tc_lock); + mutex_unlock(&tc->lock); } /* @@ -973,10 +997,11 @@ bool intel_tc_port_connected_locked(struct intel_encoder *encoder) { struct intel_digital_port *dig_port = enc_to_dig_port(encoder); struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev); + struct intel_tc_port *tc = to_tc_port(dig_port); drm_WARN_ON(&i915->drm, !intel_tc_port_ref_held(dig_port)); - return tc_phy_hpd_live_status(dig_port) & BIT(dig_port->tc_mode); + return tc_phy_hpd_live_status(tc) & BIT(tc->mode); } bool intel_tc_port_connected(struct intel_encoder *encoder) @@ -991,27 +1016,27 @@ bool intel_tc_port_connected(struct intel_encoder *encoder) return is_connected; } -static void __intel_tc_port_lock(struct intel_digital_port *dig_port, +static void __intel_tc_port_lock(struct intel_tc_port *tc, int required_lanes) { - struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev); + struct drm_i915_private *i915 = tc_to_i915(tc); - mutex_lock(&dig_port->tc_lock); + mutex_lock(&tc->lock); - cancel_delayed_work(&dig_port->tc_disconnect_phy_work); + cancel_delayed_work(&tc->disconnect_phy_work); - if (!dig_port->tc_link_refcount) - intel_tc_port_update_mode(dig_port, required_lanes, + if (!tc->link_refcount) + intel_tc_port_update_mode(tc, required_lanes, false); - drm_WARN_ON(&i915->drm, dig_port->tc_mode == TC_PORT_DISCONNECTED); - drm_WARN_ON(&i915->drm, dig_port->tc_mode != TC_PORT_TBT_ALT && - !tc_phy_is_owned(dig_port)); + drm_WARN_ON(&i915->drm, tc->mode == TC_PORT_DISCONNECTED); + drm_WARN_ON(&i915->drm, tc->mode != TC_PORT_TBT_ALT && + !tc_phy_is_owned(tc)); } void intel_tc_port_lock(struct intel_digital_port *dig_port) { - __intel_tc_port_lock(dig_port, 1); + __intel_tc_port_lock(to_tc_port(dig_port), 1); } /** @@ -1024,15 +1049,15 @@ void intel_tc_port_lock(struct intel_digital_port *dig_port) */ static void intel_tc_port_disconnect_phy_work(struct work_struct *work) { - struct intel_digital_port *dig_port = - container_of(work, struct intel_digital_port, tc_disconnect_phy_work.work); + struct intel_tc_port *tc = + container_of(work, struct intel_tc_port, disconnect_phy_work.work); - mutex_lock(&dig_port->tc_lock); + mutex_lock(&tc->lock); - if (!dig_port->tc_link_refcount) - intel_tc_port_update_mode(dig_port, 1, true); + if (!tc->link_refcount) + intel_tc_port_update_mode(tc, 1, true); - mutex_unlock(&dig_port->tc_lock); + mutex_unlock(&tc->lock); } /** @@ -1043,36 +1068,44 @@ static void intel_tc_port_disconnect_phy_work(struct work_struct *work) */ void intel_tc_port_flush_work(struct intel_digital_port *dig_port) { - flush_delayed_work(&dig_port->tc_disconnect_phy_work); + flush_delayed_work(&to_tc_port(dig_port)->disconnect_phy_work); } void intel_tc_port_unlock(struct intel_digital_port *dig_port) { - if (!dig_port->tc_link_refcount && dig_port->tc_mode != TC_PORT_DISCONNECTED) - queue_delayed_work(system_unbound_wq, &dig_port->tc_disconnect_phy_work, + struct intel_tc_port *tc = to_tc_port(dig_port); + + if (!tc->link_refcount && tc->mode != TC_PORT_DISCONNECTED) + queue_delayed_work(system_unbound_wq, &tc->disconnect_phy_work, msecs_to_jiffies(1000)); - mutex_unlock(&dig_port->tc_lock); + mutex_unlock(&tc->lock); } bool intel_tc_port_ref_held(struct intel_digital_port *dig_port) { - return mutex_is_locked(&dig_port->tc_lock) || - dig_port->tc_link_refcount; + struct intel_tc_port *tc = to_tc_port(dig_port); + + return mutex_is_locked(&tc->lock) || + tc->link_refcount; } void intel_tc_port_get_link(struct intel_digital_port *dig_port, int required_lanes) { - __intel_tc_port_lock(dig_port, required_lanes); - __intel_tc_port_get_link(dig_port); + struct intel_tc_port *tc = to_tc_port(dig_port); + + __intel_tc_port_lock(tc, required_lanes); + __intel_tc_port_get_link(tc); intel_tc_port_unlock(dig_port); } void intel_tc_port_put_link(struct intel_digital_port *dig_port) { + struct intel_tc_port *tc = to_tc_port(dig_port); + intel_tc_port_lock(dig_port); - __intel_tc_port_put_link(dig_port); + __intel_tc_port_put_link(tc); intel_tc_port_unlock(dig_port); /* @@ -1085,7 +1118,7 @@ void intel_tc_port_put_link(struct intel_digital_port *dig_port) } static bool -tc_has_modular_fia(struct drm_i915_private *i915, struct intel_digital_port *dig_port) +tc_has_modular_fia(struct drm_i915_private *i915, struct intel_tc_port *tc) { enum intel_display_power_domain domain; intel_wakeref_t wakeref; @@ -1094,11 +1127,11 @@ tc_has_modular_fia(struct drm_i915_private *i915, struct intel_digital_port *dig if (!INTEL_INFO(i915)->display.has_modular_fia) return false; - mutex_lock(&dig_port->tc_lock); - wakeref = tc_cold_block(dig_port, &domain); + mutex_lock(&tc->lock); + wakeref = tc_cold_block(tc, &domain); val = intel_de_read(i915, PORT_TX_DFLEXDPSP(FIA1)); - tc_cold_unblock(dig_port, domain, wakeref); - mutex_unlock(&dig_port->tc_lock); + tc_cold_unblock(tc, domain, wakeref); + mutex_unlock(&tc->lock); drm_WARN_ON(&i915->drm, val == 0xffffffff); @@ -1106,42 +1139,45 @@ tc_has_modular_fia(struct drm_i915_private *i915, struct intel_digital_port *dig } static void -tc_port_load_fia_params(struct drm_i915_private *i915, struct intel_digital_port *dig_port) +tc_port_load_fia_params(struct drm_i915_private *i915, struct intel_tc_port *tc) { - enum port port = dig_port->base.port; + enum port port = tc->dig_port->base.port; enum tc_port tc_port = intel_port_to_tc(i915, port); /* * Each Modular FIA instance houses 2 TC ports. In SOC that has more * than two TC ports, there are multiple instances of Modular FIA. */ - if (tc_has_modular_fia(i915, dig_port)) { - dig_port->tc_phy_fia = tc_port / 2; - dig_port->tc_phy_fia_idx = tc_port % 2; + if (tc_has_modular_fia(i915, tc)) { + tc->phy_fia = tc_port / 2; + tc->phy_fia_idx = tc_port % 2; } else { - dig_port->tc_phy_fia = FIA1; - dig_port->tc_phy_fia_idx = tc_port; + tc->phy_fia = FIA1; + tc->phy_fia_idx = tc_port; } } void intel_tc_port_init(struct intel_digital_port *dig_port, bool is_legacy) { struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev); + struct intel_tc_port *tc = to_tc_port(dig_port); enum port port = dig_port->base.port; enum tc_port tc_port = intel_port_to_tc(i915, port); if (drm_WARN_ON(&i915->drm, tc_port == TC_PORT_NONE)) return; - snprintf(dig_port->tc_port_name, sizeof(dig_port->tc_port_name), + tc->dig_port = dig_port; + + snprintf(tc->port_name, sizeof(tc->port_name), "%c/TC#%d", port_name(port), tc_port + 1); - mutex_init(&dig_port->tc_lock); - INIT_DELAYED_WORK(&dig_port->tc_disconnect_phy_work, intel_tc_port_disconnect_phy_work); - dig_port->tc_legacy_port = is_legacy; - dig_port->tc_mode = TC_PORT_DISCONNECTED; - dig_port->tc_link_refcount = 0; - tc_port_load_fia_params(i915, dig_port); + mutex_init(&tc->lock); + INIT_DELAYED_WORK(&tc->disconnect_phy_work, intel_tc_port_disconnect_phy_work); + tc->legacy_port = is_legacy; + tc->mode = TC_PORT_DISCONNECTED; + tc->link_refcount = 0; + tc_port_load_fia_params(i915, tc); 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 79667d977508..cc3a7fd4ac10 100644 --- a/drivers/gpu/drm/i915/display/intel_tc.h +++ b/drivers/gpu/drm/i915/display/intel_tc.h @@ -9,10 +9,36 @@ #include #include +#include "intel_display.h" +#include "intel_display_power.h" +#include "intel_wakeref.h" + struct intel_crtc_state; struct intel_digital_port; struct intel_encoder; +enum tc_port_mode { + TC_PORT_DISCONNECTED, + TC_PORT_TBT_ALT, + TC_PORT_DP_ALT, + TC_PORT_LEGACY, +}; + +struct intel_tc_port { + struct intel_digital_port *dig_port; + struct mutex lock; /* protects the TypeC port mode */ + intel_wakeref_t lock_wakeref; + enum intel_display_power_domain lock_power_domain; + struct delayed_work disconnect_phy_work; + int link_refcount; + bool legacy_port:1; + char port_name[8]; + enum tc_port_mode mode; + enum tc_port_mode init_mode; + enum phy_fia phy_fia; + u8 phy_fia_idx; +}; + bool intel_tc_port_in_tbt_alt_mode(struct intel_digital_port *dig_port); bool intel_tc_port_in_dp_alt_mode(struct intel_digital_port *dig_port); bool intel_tc_port_in_legacy_mode(struct intel_digital_port *dig_port); -- cgit From 711762415ddacf54b1c973b208073438de5879b4 Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Thu, 23 Mar 2023 16:20:12 +0200 Subject: drm/i915/tc: Check for TC PHY explicitly in intel_tc_port_fia_max_lane_count() Check explicitly if the port passed to intel_tc_port_fia_max_lane_count() has a TC PHY, instead of relying on the default TC mode value set for non-TC PHY ports. Reviewed-by: Mika Kahola Signed-off-by: Imre Deak Link: https://patchwork.freedesktop.org/patch/msgid/20230323142035.1432621-7-imre.deak@intel.com --- drivers/gpu/drm/i915/display/intel_tc.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/gpu/drm/i915/display/intel_tc.c') diff --git a/drivers/gpu/drm/i915/display/intel_tc.c b/drivers/gpu/drm/i915/display/intel_tc.c index d6fc9c5bd979..4995c635d445 100644 --- a/drivers/gpu/drm/i915/display/intel_tc.c +++ b/drivers/gpu/drm/i915/display/intel_tc.c @@ -188,10 +188,11 @@ int intel_tc_port_fia_max_lane_count(struct intel_digital_port *dig_port) { struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev); struct intel_tc_port *tc = to_tc_port(dig_port); + enum phy phy = intel_port_to_phy(i915, dig_port->base.port); intel_wakeref_t wakeref; u32 lane_mask; - if (tc->mode != TC_PORT_DP_ALT) + if (!intel_phy_is_tc(i915, phy) || tc->mode != TC_PORT_DP_ALT) return 4; assert_tc_cold_blocked(tc); -- cgit From c5879999641f7860495cb9655de6775c96999103 Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Thu, 23 Mar 2023 16:20:13 +0200 Subject: drm/i915/tc: Move the intel_tc_port struct declaration to intel_tc.c Move the intel_tc_port struct to intel_tc.c for better isolation. This requires allocating the struct dynamically. Reviewed-by: Mika Kahola Signed-off-by: Imre Deak Link: https://patchwork.freedesktop.org/patch/msgid/20230323142035.1432621-8-imre.deak@intel.com --- drivers/gpu/drm/i915/display/intel_ddi.c | 7 ++-- drivers/gpu/drm/i915/display/intel_display_types.h | 4 +- drivers/gpu/drm/i915/display/intel_tc.c | 45 ++++++++++++++++++++-- drivers/gpu/drm/i915/display/intel_tc.h | 30 +-------------- 4 files changed, 49 insertions(+), 37 deletions(-) (limited to 'drivers/gpu/drm/i915/display/intel_tc.c') diff --git a/drivers/gpu/drm/i915/display/intel_ddi.c b/drivers/gpu/drm/i915/display/intel_ddi.c index 73240cf78c8b..dac3ec8fbbc1 100644 --- a/drivers/gpu/drm/i915/display/intel_ddi.c +++ b/drivers/gpu/drm/i915/display/intel_ddi.c @@ -3843,7 +3843,7 @@ static void intel_ddi_encoder_destroy(struct drm_encoder *encoder) intel_dp_encoder_flush_work(encoder); if (intel_phy_is_tc(i915, phy)) - intel_tc_port_flush_work(dig_port); + intel_tc_port_cleanup(dig_port); intel_display_power_flush_work(i915); drm_encoder_cleanup(encoder); @@ -4284,7 +4284,7 @@ static void intel_ddi_encoder_shutdown(struct intel_encoder *encoder) if (!intel_phy_is_tc(i915, phy)) return; - intel_tc_port_flush_work(dig_port); + intel_tc_port_cleanup(dig_port); } #define port_tc_name(port) ((port) - PORT_TC1 + '1') @@ -4541,7 +4541,8 @@ void intel_ddi_init(struct drm_i915_private *dev_priv, enum port port) is_legacy ? "legacy" : "non-legacy"); } - intel_tc_port_init(dig_port, is_legacy); + if (intel_tc_port_init(dig_port, is_legacy) < 0) + goto err; encoder->update_prepare = intel_ddi_update_prepare; encoder->update_complete = intel_ddi_update_complete; diff --git a/drivers/gpu/drm/i915/display/intel_display_types.h b/drivers/gpu/drm/i915/display/intel_display_types.h index 3dfc8eb45020..a3ea929b0535 100644 --- a/drivers/gpu/drm/i915/display/intel_display_types.h +++ b/drivers/gpu/drm/i915/display/intel_display_types.h @@ -54,13 +54,13 @@ #include "intel_display_power.h" #include "intel_dpll_mgr.h" #include "intel_wm_types.h" -#include "intel_tc.h" struct drm_printer; struct __intel_global_objs_state; struct intel_ddi_buf_trans; struct intel_fbc; struct intel_connector; +struct intel_tc_port; /* * Display related stuff @@ -1783,7 +1783,7 @@ struct intel_digital_port { intel_wakeref_t ddi_io_wakeref; intel_wakeref_t aux_wakeref; - struct intel_tc_port tc; + struct intel_tc_port *tc; /* protects num_hdcp_streams reference count, hdcp_port_data and hdcp_auth_status */ struct mutex hdcp_mutex; diff --git a/drivers/gpu/drm/i915/display/intel_tc.c b/drivers/gpu/drm/i915/display/intel_tc.c index 4995c635d445..f43e48c9a067 100644 --- a/drivers/gpu/drm/i915/display/intel_tc.c +++ b/drivers/gpu/drm/i915/display/intel_tc.c @@ -15,6 +15,28 @@ #include "intel_mg_phy_regs.h" #include "intel_tc.h" +enum tc_port_mode { + TC_PORT_DISCONNECTED, + TC_PORT_TBT_ALT, + TC_PORT_DP_ALT, + TC_PORT_LEGACY, +}; + +struct intel_tc_port { + struct intel_digital_port *dig_port; + struct mutex lock; /* protects the TypeC port mode */ + intel_wakeref_t lock_wakeref; + enum intel_display_power_domain lock_power_domain; + struct delayed_work disconnect_phy_work; + int link_refcount; + bool legacy_port:1; + char port_name[8]; + enum tc_port_mode mode; + enum tc_port_mode init_mode; + enum phy_fia phy_fia; + u8 phy_fia_idx; +}; + static u32 tc_phy_hpd_live_status(struct intel_tc_port *tc); static bool tc_phy_is_ready(struct intel_tc_port *tc); static bool tc_phy_take_ownership(struct intel_tc_port *tc, bool take); @@ -36,7 +58,7 @@ static const char *tc_port_mode_name(enum tc_port_mode mode) static struct intel_tc_port *to_tc_port(struct intel_digital_port *dig_port) { - return &dig_port->tc; + return dig_port->tc; } static struct drm_i915_private *tc_to_i915(struct intel_tc_port *tc) @@ -1158,16 +1180,21 @@ tc_port_load_fia_params(struct drm_i915_private *i915, struct intel_tc_port *tc) } } -void intel_tc_port_init(struct intel_digital_port *dig_port, bool is_legacy) +int intel_tc_port_init(struct intel_digital_port *dig_port, bool is_legacy) { struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev); - struct intel_tc_port *tc = to_tc_port(dig_port); + struct intel_tc_port *tc; enum port port = dig_port->base.port; enum tc_port tc_port = intel_port_to_tc(i915, port); if (drm_WARN_ON(&i915->drm, tc_port == TC_PORT_NONE)) - return; + return -EINVAL; + + tc = kzalloc(sizeof(*tc), GFP_KERNEL); + if (!tc) + return -ENOMEM; + dig_port->tc = tc; tc->dig_port = dig_port; snprintf(tc->port_name, sizeof(tc->port_name), @@ -1181,4 +1208,14 @@ void intel_tc_port_init(struct intel_digital_port *dig_port, bool is_legacy) tc_port_load_fia_params(i915, tc); intel_tc_port_init_mode(dig_port); + + return 0; +} + +void intel_tc_port_cleanup(struct intel_digital_port *dig_port) +{ + intel_tc_port_flush_work(dig_port); + + kfree(dig_port->tc); + dig_port->tc = NULL; } diff --git a/drivers/gpu/drm/i915/display/intel_tc.h b/drivers/gpu/drm/i915/display/intel_tc.h index cc3a7fd4ac10..dd0810f9ea95 100644 --- a/drivers/gpu/drm/i915/display/intel_tc.h +++ b/drivers/gpu/drm/i915/display/intel_tc.h @@ -6,39 +6,12 @@ #ifndef __INTEL_TC_H__ #define __INTEL_TC_H__ -#include #include -#include "intel_display.h" -#include "intel_display_power.h" -#include "intel_wakeref.h" - struct intel_crtc_state; struct intel_digital_port; struct intel_encoder; -enum tc_port_mode { - TC_PORT_DISCONNECTED, - TC_PORT_TBT_ALT, - TC_PORT_DP_ALT, - TC_PORT_LEGACY, -}; - -struct intel_tc_port { - struct intel_digital_port *dig_port; - struct mutex lock; /* protects the TypeC port mode */ - intel_wakeref_t lock_wakeref; - enum intel_display_power_domain lock_power_domain; - struct delayed_work disconnect_phy_work; - int link_refcount; - bool legacy_port:1; - char port_name[8]; - enum tc_port_mode mode; - enum tc_port_mode init_mode; - enum phy_fia phy_fia; - u8 phy_fia_idx; -}; - bool intel_tc_port_in_tbt_alt_mode(struct intel_digital_port *dig_port); bool intel_tc_port_in_dp_alt_mode(struct intel_digital_port *dig_port); bool intel_tc_port_in_legacy_mode(struct intel_digital_port *dig_port); @@ -63,7 +36,8 @@ void intel_tc_port_get_link(struct intel_digital_port *dig_port, void intel_tc_port_put_link(struct intel_digital_port *dig_port); bool intel_tc_port_ref_held(struct intel_digital_port *dig_port); -void intel_tc_port_init(struct intel_digital_port *dig_port, bool is_legacy); +int intel_tc_port_init(struct intel_digital_port *dig_port, bool is_legacy); +void intel_tc_port_cleanup(struct intel_digital_port *dig_port); bool intel_tc_cold_requires_aux_pw(struct intel_digital_port *dig_port); -- cgit From 34a658b7e7128b35daf71cc003fbb659f914f7a7 Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Thu, 23 Mar 2023 16:20:14 +0200 Subject: drm/i915/tc: Add TC PHY hook to get the PHY HPD live status Add a table of TC PHY hooks which can be used to call platform specific TC PHY handlers, replacing the corresponding if ladders. Add the hook to retrieve the PHY's HPD live status. Move the common part fixing up the VBT legacy port flag to the generic helper. Reviewed-by: Mika Kahola Signed-off-by: Imre Deak Link: https://patchwork.freedesktop.org/patch/msgid/20230323142035.1432621-9-imre.deak@intel.com --- drivers/gpu/drm/i915/display/intel_tc.c | 40 ++++++++++++++++++++++++--------- 1 file changed, 29 insertions(+), 11 deletions(-) (limited to 'drivers/gpu/drm/i915/display/intel_tc.c') diff --git a/drivers/gpu/drm/i915/display/intel_tc.c b/drivers/gpu/drm/i915/display/intel_tc.c index f43e48c9a067..2556fea500d7 100644 --- a/drivers/gpu/drm/i915/display/intel_tc.c +++ b/drivers/gpu/drm/i915/display/intel_tc.c @@ -22,8 +22,17 @@ enum tc_port_mode { TC_PORT_LEGACY, }; +struct intel_tc_port; + +struct intel_tc_phy_ops { + u32 (*hpd_live_status)(struct intel_tc_port *tc); +}; + struct intel_tc_port { struct intel_digital_port *dig_port; + + const struct intel_tc_phy_ops *phy_ops; + struct mutex lock; /* protects the TypeC port mode */ intel_wakeref_t lock_wakeref; enum intel_display_power_domain lock_power_domain; @@ -329,10 +338,6 @@ static u32 icl_tc_phy_hpd_live_status(struct intel_tc_port *tc) if (intel_de_read(i915, SDEISR) & isr_bit) mask |= BIT(TC_PORT_LEGACY); - /* The sink can be connected only in a single mode. */ - if (!drm_WARN_ON_ONCE(&i915->drm, hweight32(mask) > 1)) - tc_port_fixup_legacy_flag(tc, mask); - return mask; } @@ -495,6 +500,10 @@ static void icl_tc_phy_disconnect(struct intel_tc_port *tc) } } +static const struct intel_tc_phy_ops icl_tc_phy_ops = { + .hpd_live_status = icl_tc_phy_hpd_live_status, +}; + /* * ADLP TC PHY handlers * -------------------- @@ -521,10 +530,6 @@ static u32 adlp_tc_phy_hpd_live_status(struct intel_tc_port *tc) if (intel_de_read(i915, SDEISR) & isr_bit) mask |= BIT(TC_PORT_LEGACY); - /* The sink can be connected only in a single mode. */ - if (!drm_WARN_ON(&i915->drm, hweight32(mask) > 1)) - tc_port_fixup_legacy_flag(tc, mask); - return mask; } @@ -574,6 +579,10 @@ static bool adlp_tc_phy_is_owned(struct intel_tc_port *tc) return val & DDI_BUF_CTL_TC_PHY_OWNERSHIP; } +static const struct intel_tc_phy_ops adlp_tc_phy_ops = { + .hpd_live_status = adlp_tc_phy_hpd_live_status, +}; + /* * Generic TC PHY handlers * ----------------------- @@ -581,11 +590,15 @@ static bool adlp_tc_phy_is_owned(struct intel_tc_port *tc) static u32 tc_phy_hpd_live_status(struct intel_tc_port *tc) { struct drm_i915_private *i915 = tc_to_i915(tc); + u32 mask; - if (IS_ALDERLAKE_P(i915)) - return adlp_tc_phy_hpd_live_status(tc); + mask = tc->phy_ops->hpd_live_status(tc); + + /* The sink can be connected only in a single mode. */ + if (!drm_WARN_ON_ONCE(&i915->drm, hweight32(mask) > 1)) + tc_port_fixup_legacy_flag(tc, mask); - return icl_tc_phy_hpd_live_status(tc); + return mask; } static bool tc_phy_is_ready(struct intel_tc_port *tc) @@ -1197,6 +1210,11 @@ int intel_tc_port_init(struct intel_digital_port *dig_port, bool is_legacy) dig_port->tc = tc; tc->dig_port = dig_port; + if (DISPLAY_VER(i915) >= 13) + tc->phy_ops = &adlp_tc_phy_ops; + else + tc->phy_ops = &icl_tc_phy_ops; + snprintf(tc->port_name, sizeof(tc->port_name), "%c/TC#%d", port_name(port), tc_port + 1); -- cgit From 87107261bb73d7a8945f3cd37a6abc7befc327bf Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Thu, 23 Mar 2023 16:20:15 +0200 Subject: drm/i915/tc: Add TC PHY hooks to get the PHY ready/owned state Add TC PHY hooks to get the PHY ready/owned state on each platform, replacing the corresponding if ladder. Reviewed-by: Mika Kahola Signed-off-by: Imre Deak Link: https://patchwork.freedesktop.org/patch/msgid/20230323142035.1432621-10-imre.deak@intel.com --- drivers/gpu/drm/i915/display/intel_tc.c | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) (limited to 'drivers/gpu/drm/i915/display/intel_tc.c') diff --git a/drivers/gpu/drm/i915/display/intel_tc.c b/drivers/gpu/drm/i915/display/intel_tc.c index 2556fea500d7..052103c87a39 100644 --- a/drivers/gpu/drm/i915/display/intel_tc.c +++ b/drivers/gpu/drm/i915/display/intel_tc.c @@ -26,6 +26,8 @@ struct intel_tc_port; struct intel_tc_phy_ops { u32 (*hpd_live_status)(struct intel_tc_port *tc); + bool (*is_ready)(struct intel_tc_port *tc); + bool (*is_owned)(struct intel_tc_port *tc); }; struct intel_tc_port { @@ -502,6 +504,8 @@ static void icl_tc_phy_disconnect(struct intel_tc_port *tc) static const struct intel_tc_phy_ops icl_tc_phy_ops = { .hpd_live_status = icl_tc_phy_hpd_live_status, + .is_ready = icl_tc_phy_is_ready, + .is_owned = icl_tc_phy_is_owned, }; /* @@ -581,6 +585,8 @@ static bool adlp_tc_phy_is_owned(struct intel_tc_port *tc) static const struct intel_tc_phy_ops adlp_tc_phy_ops = { .hpd_live_status = adlp_tc_phy_hpd_live_status, + .is_ready = adlp_tc_phy_is_ready, + .is_owned = adlp_tc_phy_is_owned, }; /* @@ -603,22 +609,12 @@ static u32 tc_phy_hpd_live_status(struct intel_tc_port *tc) static bool tc_phy_is_ready(struct intel_tc_port *tc) { - struct drm_i915_private *i915 = tc_to_i915(tc); - - if (IS_ALDERLAKE_P(i915)) - return adlp_tc_phy_is_ready(tc); - - return icl_tc_phy_is_ready(tc); + return tc->phy_ops->is_ready(tc); } static bool tc_phy_is_owned(struct intel_tc_port *tc) { - struct drm_i915_private *i915 = tc_to_i915(tc); - - if (IS_ALDERLAKE_P(i915)) - return adlp_tc_phy_is_owned(tc); - - return icl_tc_phy_is_owned(tc); + return tc->phy_ops->is_owned(tc); } static bool tc_phy_take_ownership(struct intel_tc_port *tc, bool take) -- cgit From ab639f326e48385eeaf65fd129d3ff6d006cbeef Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Thu, 23 Mar 2023 16:20:16 +0200 Subject: drm/i915/tc: Add TC PHY hook to read out the PHY HW state Add a TC PHY hook to read out the PHY HW state on each platform, move the common parts to the generic helper. Reviewed-by: Mika Kahola Signed-off-by: Imre Deak Link: https://patchwork.freedesktop.org/patch/msgid/20230323142035.1432621-11-imre.deak@intel.com --- drivers/gpu/drm/i915/display/intel_tc.c | 34 +++++++++++++++++++++++---------- 1 file changed, 24 insertions(+), 10 deletions(-) (limited to 'drivers/gpu/drm/i915/display/intel_tc.c') diff --git a/drivers/gpu/drm/i915/display/intel_tc.c b/drivers/gpu/drm/i915/display/intel_tc.c index 052103c87a39..433f3c435126 100644 --- a/drivers/gpu/drm/i915/display/intel_tc.c +++ b/drivers/gpu/drm/i915/display/intel_tc.c @@ -28,6 +28,7 @@ struct intel_tc_phy_ops { u32 (*hpd_live_status)(struct intel_tc_port *tc); bool (*is_ready)(struct intel_tc_port *tc); bool (*is_owned)(struct intel_tc_port *tc); + void (*get_hw_state)(struct intel_tc_port *tc); }; struct intel_tc_port { @@ -51,6 +52,7 @@ struct intel_tc_port { static u32 tc_phy_hpd_live_status(struct intel_tc_port *tc); static bool tc_phy_is_ready(struct intel_tc_port *tc); static bool tc_phy_take_ownership(struct intel_tc_port *tc, bool take); +static enum tc_port_mode tc_phy_get_current_mode(struct intel_tc_port *tc); static const char *tc_port_mode_name(enum tc_port_mode mode) { @@ -407,6 +409,20 @@ static bool icl_tc_phy_is_owned(struct intel_tc_port *tc) return val & DP_PHY_MODE_STATUS_NOT_SAFE(tc->phy_fia_idx); } +static void icl_tc_phy_get_hw_state(struct intel_tc_port *tc) +{ + enum intel_display_power_domain domain; + intel_wakeref_t tc_cold_wref; + + tc_cold_wref = tc_cold_block(tc, &domain); + + tc->mode = tc_phy_get_current_mode(tc); + if (tc->mode != TC_PORT_DISCONNECTED) + tc->lock_wakeref = tc_cold_block(tc, &tc->lock_power_domain); + + tc_cold_unblock(tc, domain, tc_cold_wref); +} + /* * This function implements the first part of the Connect Flow described by our * specification, Gen11 TypeC Programming chapter. The rest of the flow (reading @@ -506,6 +522,7 @@ static const struct intel_tc_phy_ops icl_tc_phy_ops = { .hpd_live_status = icl_tc_phy_hpd_live_status, .is_ready = icl_tc_phy_is_ready, .is_owned = icl_tc_phy_is_owned, + .get_hw_state = icl_tc_phy_get_hw_state, }; /* @@ -587,6 +604,7 @@ static const struct intel_tc_phy_ops adlp_tc_phy_ops = { .hpd_live_status = adlp_tc_phy_hpd_live_status, .is_ready = adlp_tc_phy_is_ready, .is_owned = adlp_tc_phy_is_owned, + .get_hw_state = icl_tc_phy_get_hw_state, }; /* @@ -617,6 +635,11 @@ static bool tc_phy_is_owned(struct intel_tc_port *tc) return tc->phy_ops->is_owned(tc); } +static void tc_phy_get_hw_state(struct intel_tc_port *tc) +{ + tc->phy_ops->get_hw_state(tc); +} + static bool tc_phy_take_ownership(struct intel_tc_port *tc, bool take) { struct drm_i915_private *i915 = tc_to_i915(tc); @@ -889,8 +912,6 @@ 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_tc_port *tc = to_tc_port(dig_port); - intel_wakeref_t tc_cold_wref; - enum intel_display_power_domain domain; bool update_mode = false; mutex_lock(&tc->lock); @@ -899,17 +920,12 @@ void intel_tc_port_init_mode(struct intel_digital_port *dig_port) drm_WARN_ON(&i915->drm, tc->lock_wakeref); drm_WARN_ON(&i915->drm, tc->link_refcount); - tc_cold_wref = tc_cold_block(tc, &domain); - - tc->mode = tc_phy_get_current_mode(tc); + tc_phy_get_hw_state(tc); /* * Save the initial mode for the state check in * intel_tc_port_sanitize_mode(). */ tc->init_mode = tc->mode; - if (tc->mode != TC_PORT_DISCONNECTED) - tc->lock_wakeref = - tc_cold_block(tc, &tc->lock_power_domain); /* * The PHY needs to be connected for AUX to work during HW readout and @@ -938,8 +954,6 @@ void intel_tc_port_init_mode(struct intel_digital_port *dig_port) /* Prevent changing tc->mode until intel_tc_port_sanitize_mode() is called. */ __intel_tc_port_get_link(tc); - tc_cold_unblock(tc, domain, tc_cold_wref); - mutex_unlock(&tc->lock); } -- cgit From d1fc4e391fbd321dff166208e58e30d3196626ba Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Thu, 23 Mar 2023 16:20:17 +0200 Subject: drm/i915/tc: Add generic TC PHY connect/disconnect handlers Add generic handlers to connect/disconnect a PHY. Setting the TC mode to the target mode deducted from the HPD state and - if connecting to this mode fails - falling back to connecting to the default (TBT) mode are common to all platforms; move the logic for this from the ICL specific connect / disconnect handlers to the generic ones. Reviewed-by: Mika Kahola Signed-off-by: Imre Deak Link: https://patchwork.freedesktop.org/patch/msgid/20230323142035.1432621-12-imre.deak@intel.com --- drivers/gpu/drm/i915/display/intel_tc.c | 65 ++++++++++++++++++++------------- 1 file changed, 39 insertions(+), 26 deletions(-) (limited to 'drivers/gpu/drm/i915/display/intel_tc.c') diff --git a/drivers/gpu/drm/i915/display/intel_tc.c b/drivers/gpu/drm/i915/display/intel_tc.c index 433f3c435126..2910921483c0 100644 --- a/drivers/gpu/drm/i915/display/intel_tc.c +++ b/drivers/gpu/drm/i915/display/intel_tc.c @@ -434,41 +434,35 @@ static void icl_tc_phy_get_hw_state(struct intel_tc_port *tc) * connect and disconnect to cleanly transfer ownership with the controller and * set the type-C power state. */ -static void icl_tc_phy_connect(struct intel_tc_port *tc, +static bool icl_tc_phy_connect(struct intel_tc_port *tc, int required_lanes) { struct drm_i915_private *i915 = tc_to_i915(tc); struct intel_digital_port *dig_port = tc->dig_port; - u32 live_status_mask; int max_lanes; + if (tc->mode == TC_PORT_TBT_ALT) + return true; + if (!tc_phy_is_ready(tc) && !drm_WARN_ON(&i915->drm, tc->legacy_port)) { drm_dbg_kms(&i915->drm, "Port %s: PHY not ready\n", tc->port_name); - goto out_set_tbt_alt_mode; - } - - live_status_mask = tc_phy_hpd_live_status(tc); - if (!(live_status_mask & (BIT(TC_PORT_DP_ALT) | BIT(TC_PORT_LEGACY))) && - !tc->legacy_port) { - drm_dbg_kms(&i915->drm, "Port %s: PHY ownership not required (live status %02x)\n", - tc->port_name, live_status_mask); - goto out_set_tbt_alt_mode; + return false; } if (!tc_phy_take_ownership(tc, true) && !drm_WARN_ON(&i915->drm, tc->legacy_port)) - goto out_set_tbt_alt_mode; + return false; max_lanes = intel_tc_port_fia_max_lane_count(dig_port); if (tc->legacy_port) { drm_WARN_ON(&i915->drm, max_lanes != 4); - tc->mode = TC_PORT_LEGACY; - - return; + return true; } + drm_WARN_ON(&i915->drm, tc->mode != TC_PORT_DP_ALT); + /* * Now we have to re-check the live state, in case the port recently * became disconnected. Not necessary for legacy mode. @@ -487,14 +481,12 @@ static void icl_tc_phy_connect(struct intel_tc_port *tc, goto out_release_phy; } - tc->mode = TC_PORT_DP_ALT; - - return; + return true; out_release_phy: tc_phy_take_ownership(tc, false); -out_set_tbt_alt_mode: - tc->mode = TC_PORT_TBT_ALT; + + return false; } /* @@ -509,9 +501,6 @@ static void icl_tc_phy_disconnect(struct intel_tc_port *tc) tc_phy_take_ownership(tc, false); fallthrough; case TC_PORT_TBT_ALT: - tc->mode = TC_PORT_DISCONNECTED; - fallthrough; - case TC_PORT_DISCONNECTED: break; default: MISSING_CASE(tc->mode); @@ -817,6 +806,30 @@ tc_phy_get_target_mode(struct intel_tc_port *tc) return hpd_mask_to_target_mode(tc, live_status_mask); } +static void tc_phy_connect(struct intel_tc_port *tc, int required_lanes) +{ + struct drm_i915_private *i915 = tc_to_i915(tc); + bool connected; + + tc->mode = tc_phy_get_target_mode(tc); + + connected = icl_tc_phy_connect(tc, required_lanes); + if (!connected && tc->mode != default_tc_mode(tc)) { + tc->mode = default_tc_mode(tc); + connected = icl_tc_phy_connect(tc, required_lanes); + } + + drm_WARN_ON(&i915->drm, !connected); +} + +static void tc_phy_disconnect(struct intel_tc_port *tc) +{ + if (tc->mode != TC_PORT_DISCONNECTED) { + icl_tc_phy_disconnect(tc); + tc->mode = TC_PORT_DISCONNECTED; + } +} + static void intel_tc_port_reset_mode(struct intel_tc_port *tc, int required_lanes, bool force_disconnect) { @@ -834,9 +847,9 @@ static void intel_tc_port_reset_mode(struct intel_tc_port *tc, drm_WARN_ON(&i915->drm, aux_powered); } - icl_tc_phy_disconnect(tc); + tc_phy_disconnect(tc); if (!force_disconnect) - icl_tc_phy_connect(tc, required_lanes); + tc_phy_connect(tc, required_lanes); drm_dbg_kms(&i915->drm, "Port %s: TC port mode reset (%s -> %s)\n", tc->port_name, @@ -1015,7 +1028,7 @@ void intel_tc_port_sanitize_mode(struct intel_digital_port *dig_port, "Port %s: PHY left in %s mode on disabled port, disconnecting it\n", tc->port_name, tc_port_mode_name(tc->init_mode)); - icl_tc_phy_disconnect(tc); + tc_phy_disconnect(tc); __intel_tc_port_put_link(tc); tc_cold_unblock(tc, tc->lock_power_domain, -- cgit From 712f422ed70c6778018152896d6b4e5ff70fb2b4 Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Thu, 23 Mar 2023 16:20:18 +0200 Subject: drm/i915/tc: Factor out tc_phy_verify_legacy_or_dp_alt_mode() Factor out a function verifying the PHY connected state in legacy or DP-alt mode. This is common to all platforms, which can be reused in platform specific connect hooks added in follow-up patches. No functional changes. Reviewed-by: Mika Kahola Signed-off-by: Imre Deak Link: https://patchwork.freedesktop.org/patch/msgid/20230323142035.1432621-13-imre.deak@intel.com --- drivers/gpu/drm/i915/display/intel_tc.c | 47 ++++++++++++++++++++------------- 1 file changed, 29 insertions(+), 18 deletions(-) (limited to 'drivers/gpu/drm/i915/display/intel_tc.c') diff --git a/drivers/gpu/drm/i915/display/intel_tc.c b/drivers/gpu/drm/i915/display/intel_tc.c index 2910921483c0..bf83e6794054 100644 --- a/drivers/gpu/drm/i915/display/intel_tc.c +++ b/drivers/gpu/drm/i915/display/intel_tc.c @@ -434,27 +434,13 @@ static void icl_tc_phy_get_hw_state(struct intel_tc_port *tc) * connect and disconnect to cleanly transfer ownership with the controller and * set the type-C power state. */ -static bool icl_tc_phy_connect(struct intel_tc_port *tc, - int required_lanes) +static bool tc_phy_verify_legacy_or_dp_alt_mode(struct intel_tc_port *tc, + int required_lanes) { struct drm_i915_private *i915 = tc_to_i915(tc); struct intel_digital_port *dig_port = tc->dig_port; int max_lanes; - if (tc->mode == TC_PORT_TBT_ALT) - return true; - - if (!tc_phy_is_ready(tc) && - !drm_WARN_ON(&i915->drm, tc->legacy_port)) { - drm_dbg_kms(&i915->drm, "Port %s: PHY not ready\n", - tc->port_name); - return false; - } - - if (!tc_phy_take_ownership(tc, true) && - !drm_WARN_ON(&i915->drm, tc->legacy_port)) - return false; - max_lanes = intel_tc_port_fia_max_lane_count(dig_port); if (tc->legacy_port) { drm_WARN_ON(&i915->drm, max_lanes != 4); @@ -470,7 +456,7 @@ static bool icl_tc_phy_connect(struct intel_tc_port *tc, if (!(tc_phy_hpd_live_status(tc) & BIT(TC_PORT_DP_ALT))) { drm_dbg_kms(&i915->drm, "Port %s: PHY sudden disconnect\n", tc->port_name); - goto out_release_phy; + return false; } if (max_lanes < required_lanes) { @@ -478,9 +464,34 @@ static bool icl_tc_phy_connect(struct intel_tc_port *tc, "Port %s: PHY max lanes %d < required lanes %d\n", tc->port_name, max_lanes, required_lanes); - goto out_release_phy; + return false; + } + + return true; +} + +static bool icl_tc_phy_connect(struct intel_tc_port *tc, + int required_lanes) +{ + struct drm_i915_private *i915 = tc_to_i915(tc); + + if (tc->mode == TC_PORT_TBT_ALT) + return true; + + if (!tc_phy_is_ready(tc) && + !drm_WARN_ON(&i915->drm, tc->legacy_port)) { + drm_dbg_kms(&i915->drm, "Port %s: PHY not ready\n", + tc->port_name); + return false; } + if (!tc_phy_take_ownership(tc, true) && + !drm_WARN_ON(&i915->drm, tc->legacy_port)) + return false; + + if (!tc_phy_verify_legacy_or_dp_alt_mode(tc, required_lanes)) + goto out_release_phy; + return true; out_release_phy: -- cgit From bd0fdd31c1023b9d94a470ed5bc301c3f9d80519 Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Thu, 23 Mar 2023 16:20:19 +0200 Subject: drm/i915/tc: Add TC PHY hooks to connect/disconnect the PHY Add TC PHY hooks to connect/disconnect the PHY. A follow-up patch will add the ADLP specific hooks for these. Reviewed-by: Mika Kahola Signed-off-by: Imre Deak Link: https://patchwork.freedesktop.org/patch/msgid/20230323142035.1432621-14-imre.deak@intel.com --- drivers/gpu/drm/i915/display/intel_tc.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) (limited to 'drivers/gpu/drm/i915/display/intel_tc.c') diff --git a/drivers/gpu/drm/i915/display/intel_tc.c b/drivers/gpu/drm/i915/display/intel_tc.c index bf83e6794054..cdac808aac3c 100644 --- a/drivers/gpu/drm/i915/display/intel_tc.c +++ b/drivers/gpu/drm/i915/display/intel_tc.c @@ -29,6 +29,8 @@ struct intel_tc_phy_ops { bool (*is_ready)(struct intel_tc_port *tc); bool (*is_owned)(struct intel_tc_port *tc); void (*get_hw_state)(struct intel_tc_port *tc); + bool (*connect)(struct intel_tc_port *tc, int required_lanes); + void (*disconnect)(struct intel_tc_port *tc); }; struct intel_tc_port { @@ -523,6 +525,8 @@ static const struct intel_tc_phy_ops icl_tc_phy_ops = { .is_ready = icl_tc_phy_is_ready, .is_owned = icl_tc_phy_is_owned, .get_hw_state = icl_tc_phy_get_hw_state, + .connect = icl_tc_phy_connect, + .disconnect = icl_tc_phy_disconnect, }; /* @@ -605,6 +609,8 @@ static const struct intel_tc_phy_ops adlp_tc_phy_ops = { .is_ready = adlp_tc_phy_is_ready, .is_owned = adlp_tc_phy_is_owned, .get_hw_state = icl_tc_phy_get_hw_state, + .connect = icl_tc_phy_connect, + .disconnect = icl_tc_phy_disconnect, }; /* @@ -824,10 +830,10 @@ static void tc_phy_connect(struct intel_tc_port *tc, int required_lanes) tc->mode = tc_phy_get_target_mode(tc); - connected = icl_tc_phy_connect(tc, required_lanes); + connected = tc->phy_ops->connect(tc, required_lanes); if (!connected && tc->mode != default_tc_mode(tc)) { tc->mode = default_tc_mode(tc); - connected = icl_tc_phy_connect(tc, required_lanes); + connected = tc->phy_ops->connect(tc, required_lanes); } drm_WARN_ON(&i915->drm, !connected); @@ -836,7 +842,7 @@ static void tc_phy_connect(struct intel_tc_port *tc, int required_lanes) static void tc_phy_disconnect(struct intel_tc_port *tc) { if (tc->mode != TC_PORT_DISCONNECTED) { - icl_tc_phy_disconnect(tc); + tc->phy_ops->disconnect(tc); tc->mode = TC_PORT_DISCONNECTED; } } -- cgit From 3b7d5663702373358d58987a3684f6c59443d9d4 Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Thu, 23 Mar 2023 16:20:20 +0200 Subject: drm/i915/tc: Fix up the legacy VBT flag only in disconnected mode A follow-up patch simplifies the tc_cold_block()/unblock() functions, dropping the power domain parameter. For this it must be ensured that the power domain - which depends on the actual TC mode and so the VBT legacy port flag - can't change while the PHY is in a connected state and accordingly TC-cold is blocked. Make this so, by fixing up the VBT legacy flag only in the disconnected TC mode, instead of whenever the HPD state is retrieved. Reviewed-by: Mika Kahola Signed-off-by: Imre Deak Link: https://patchwork.freedesktop.org/patch/msgid/20230323142035.1432621-15-imre.deak@intel.com --- drivers/gpu/drm/i915/display/intel_tc.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) (limited to 'drivers/gpu/drm/i915/display/intel_tc.c') diff --git a/drivers/gpu/drm/i915/display/intel_tc.c b/drivers/gpu/drm/i915/display/intel_tc.c index cdac808aac3c..b4e5de676816 100644 --- a/drivers/gpu/drm/i915/display/intel_tc.c +++ b/drivers/gpu/drm/i915/display/intel_tc.c @@ -298,6 +298,11 @@ static void tc_port_fixup_legacy_flag(struct intel_tc_port *tc, struct drm_i915_private *i915 = tc_to_i915(tc); u32 valid_hpd_mask; + drm_WARN_ON(&i915->drm, tc->mode != TC_PORT_DISCONNECTED); + + if (hweight32(live_status_mask) != 1) + return; + if (tc->legacy_port) valid_hpd_mask = BIT(TC_PORT_LEGACY); else @@ -625,8 +630,7 @@ static u32 tc_phy_hpd_live_status(struct intel_tc_port *tc) mask = tc->phy_ops->hpd_live_status(tc); /* The sink can be connected only in a single mode. */ - if (!drm_WARN_ON_ONCE(&i915->drm, hweight32(mask) > 1)) - tc_port_fixup_legacy_flag(tc, mask); + drm_WARN_ON_ONCE(&i915->drm, hweight32(mask) > 1); return mask; } @@ -826,9 +830,12 @@ tc_phy_get_target_mode(struct intel_tc_port *tc) static void tc_phy_connect(struct intel_tc_port *tc, int required_lanes) { struct drm_i915_private *i915 = tc_to_i915(tc); + u32 live_status_mask = tc_phy_hpd_live_status(tc); bool connected; - tc->mode = tc_phy_get_target_mode(tc); + tc_port_fixup_legacy_flag(tc, live_status_mask); + + tc->mode = hpd_mask_to_target_mode(tc, live_status_mask); connected = tc->phy_ops->connect(tc, required_lanes); if (!connected && tc->mode != default_tc_mode(tc)) { -- cgit From e0b1ef58d98ae0feba98190c9faf192aabceb811 Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Thu, 23 Mar 2023 16:20:21 +0200 Subject: drm/i915/tc: Check TC mode instead of the VBT legacy flag After the previous patch the TC mode in the connect/disconnect functions is always in sync with the VBT legacy port flag, so for consistency with the rest of the function check the TC mode instead of the VBT flag. Reviewed-by: Mika Kahola Signed-off-by: Imre Deak Link: https://patchwork.freedesktop.org/patch/msgid/20230323142035.1432621-16-imre.deak@intel.com --- drivers/gpu/drm/i915/display/intel_tc.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) (limited to 'drivers/gpu/drm/i915/display/intel_tc.c') diff --git a/drivers/gpu/drm/i915/display/intel_tc.c b/drivers/gpu/drm/i915/display/intel_tc.c index b4e5de676816..8cae650d2880 100644 --- a/drivers/gpu/drm/i915/display/intel_tc.c +++ b/drivers/gpu/drm/i915/display/intel_tc.c @@ -449,7 +449,7 @@ static bool tc_phy_verify_legacy_or_dp_alt_mode(struct intel_tc_port *tc, int max_lanes; max_lanes = intel_tc_port_fia_max_lane_count(dig_port); - if (tc->legacy_port) { + if (tc->mode == TC_PORT_LEGACY) { drm_WARN_ON(&i915->drm, max_lanes != 4); return true; } @@ -485,16 +485,15 @@ static bool icl_tc_phy_connect(struct intel_tc_port *tc, if (tc->mode == TC_PORT_TBT_ALT) return true; - if (!tc_phy_is_ready(tc) && - !drm_WARN_ON(&i915->drm, tc->legacy_port)) { - drm_dbg_kms(&i915->drm, "Port %s: PHY not ready\n", - tc->port_name); + if ((!tc_phy_is_ready(tc) || + !tc_phy_take_ownership(tc, true)) && + !drm_WARN_ON(&i915->drm, tc->mode == TC_PORT_LEGACY)) { + drm_dbg_kms(&i915->drm, "Port %s: can't take PHY ownership (ready %s)\n", + tc->port_name, + str_yes_no(tc_phy_is_ready(tc))); return false; } - if (!tc_phy_take_ownership(tc, true) && - !drm_WARN_ON(&i915->drm, tc->legacy_port)) - return false; if (!tc_phy_verify_legacy_or_dp_alt_mode(tc, required_lanes)) goto out_release_phy; -- cgit From 976a368b7198bf666ad3d273e10ed82b3713af3b Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Thu, 23 Mar 2023 16:20:22 +0200 Subject: drm/i915/tc: Block/unblock TC-cold in the PHY connect/disconnect hooks Move blocking/unblocking the TC-cold power state to the platform specific PHY connect / disconnect hooks. This allows for adjusting the connect/disconnect sequence as required for each platform. Reviewed-by: Mika Kahola Signed-off-by: Imre Deak Link: https://patchwork.freedesktop.org/patch/msgid/20230323142035.1432621-17-imre.deak@intel.com --- drivers/gpu/drm/i915/display/intel_tc.c | 43 ++++++++++----------------------- 1 file changed, 13 insertions(+), 30 deletions(-) (limited to 'drivers/gpu/drm/i915/display/intel_tc.c') diff --git a/drivers/gpu/drm/i915/display/intel_tc.c b/drivers/gpu/drm/i915/display/intel_tc.c index 8cae650d2880..7b1b4d68753d 100644 --- a/drivers/gpu/drm/i915/display/intel_tc.c +++ b/drivers/gpu/drm/i915/display/intel_tc.c @@ -482,6 +482,8 @@ static bool icl_tc_phy_connect(struct intel_tc_port *tc, { struct drm_i915_private *i915 = tc_to_i915(tc); + tc->lock_wakeref = tc_cold_block(tc, &tc->lock_power_domain); + if (tc->mode == TC_PORT_TBT_ALT) return true; @@ -491,7 +493,7 @@ static bool icl_tc_phy_connect(struct intel_tc_port *tc, drm_dbg_kms(&i915->drm, "Port %s: can't take PHY ownership (ready %s)\n", tc->port_name, str_yes_no(tc_phy_is_ready(tc))); - return false; + goto out_unblock_tc_cold; } @@ -502,6 +504,10 @@ static bool icl_tc_phy_connect(struct intel_tc_port *tc, out_release_phy: tc_phy_take_ownership(tc, false); +out_unblock_tc_cold: + tc_cold_unblock(tc, + tc->lock_power_domain, + fetch_and_zero(&tc->lock_wakeref)); return false; } @@ -518,6 +524,9 @@ static void icl_tc_phy_disconnect(struct intel_tc_port *tc) tc_phy_take_ownership(tc, false); fallthrough; case TC_PORT_TBT_ALT: + tc_cold_unblock(tc, + tc->lock_power_domain, + fetch_and_zero(&tc->lock_wakeref)); break; default: MISSING_CASE(tc->mode); @@ -888,32 +897,9 @@ static bool intel_tc_port_needs_reset(struct intel_tc_port *tc) static void intel_tc_port_update_mode(struct intel_tc_port *tc, int required_lanes, bool force_disconnect) { - enum intel_display_power_domain domain; - intel_wakeref_t wref; - bool needs_reset = force_disconnect; - - if (!needs_reset) { - /* Get power domain required to check the hotplug live status. */ - wref = tc_cold_block(tc, &domain); - needs_reset = intel_tc_port_needs_reset(tc); - tc_cold_unblock(tc, domain, wref); - } - - if (!needs_reset) - return; - - /* Get power domain required for resetting the mode. */ - wref = tc_cold_block_in_mode(tc, TC_PORT_DISCONNECTED, &domain); - - intel_tc_port_reset_mode(tc, required_lanes, force_disconnect); - - /* Get power domain matching the new mode after reset. */ - tc_cold_unblock(tc, tc->lock_power_domain, - fetch_and_zero(&tc->lock_wakeref)); - if (tc->mode != TC_PORT_DISCONNECTED) - tc->lock_wakeref = tc_cold_block(tc, &tc->lock_power_domain); - - tc_cold_unblock(tc, domain, wref); + if (force_disconnect || + intel_tc_port_needs_reset(tc)) + intel_tc_port_reset_mode(tc, required_lanes, force_disconnect); } static void __intel_tc_port_get_link(struct intel_tc_port *tc) @@ -1053,9 +1039,6 @@ void intel_tc_port_sanitize_mode(struct intel_digital_port *dig_port, tc_port_mode_name(tc->init_mode)); tc_phy_disconnect(tc); __intel_tc_port_put_link(tc); - - tc_cold_unblock(tc, tc->lock_power_domain, - fetch_and_zero(&tc->lock_wakeref)); } drm_dbg_kms(&i915->drm, "Port %s: sanitize mode (%s)\n", -- cgit From 430ce0c7d3a3b2cdfdafb527d2f89be4267ac45c Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Thu, 23 Mar 2023 16:20:23 +0200 Subject: drm/i915/tc: Remove redundant wakeref=0 check from unblock_tc_cold() After the previous patch unblock_tc_cold() will not be called in a disconnected mode, so the wakeref passed to it will be always non-zero. Remove the redundant check. Reviewed-by: Mika Kahola Signed-off-by: Imre Deak Link: https://patchwork.freedesktop.org/patch/msgid/20230323142035.1432621-18-imre.deak@intel.com --- drivers/gpu/drm/i915/display/intel_tc.c | 8 -------- 1 file changed, 8 deletions(-) (limited to 'drivers/gpu/drm/i915/display/intel_tc.c') diff --git a/drivers/gpu/drm/i915/display/intel_tc.c b/drivers/gpu/drm/i915/display/intel_tc.c index 7b1b4d68753d..02fdb8d37f3e 100644 --- a/drivers/gpu/drm/i915/display/intel_tc.c +++ b/drivers/gpu/drm/i915/display/intel_tc.c @@ -150,14 +150,6 @@ tc_cold_unblock(struct intel_tc_port *tc, enum intel_display_power_domain domain { struct drm_i915_private *i915 = tc_to_i915(tc); - /* - * wakeref == -1, means some error happened saving save_depot_stack but - * power should still be put down and 0 is a invalid save_depot_stack - * id so can be used to skip it for non TC legacy ports. - */ - if (wakeref == 0) - return; - intel_display_power_put(i915, domain, wakeref); } -- cgit From bc5f983a3d8d8e5a5eddfaf11275bbcef04017f5 Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Thu, 23 Mar 2023 16:20:24 +0200 Subject: drm/i915/tc: Drop tc_cold_block()/unblock()'s power domain parameter Simplify tc_cold_block()/unblock() by dropping their power domain parameter. The power domain depends on the current TC mode, which - after the previous patch - can't change while the PHY is connected, holding a TC-cold-off power domain reference. Based on this the domain can be deducted from the current TC mode instead of having to pass this as a parameter. Blocking TC-cold for the PHY HW readout happens before the current TC mode is determined, so here the initial power domain must be still manually passed. For debugging still keep track of the domain used for tc_cold_block() and verify that it remained the same until tc_cold_unblock(). While at it rename tc_cold_get_power_domain() to tc_phy_cold_off_domain(), reflecting the name of platform specific hook added in the next patch. Reviewed-by: Mika Kahola Signed-off-by: Imre Deak Link: https://patchwork.freedesktop.org/patch/msgid/20230323142035.1432621-19-imre.deak@intel.com --- drivers/gpu/drm/i915/display/intel_tc.c | 61 ++++++++++++++++++++------------- 1 file changed, 37 insertions(+), 24 deletions(-) (limited to 'drivers/gpu/drm/i915/display/intel_tc.c') diff --git a/drivers/gpu/drm/i915/display/intel_tc.c b/drivers/gpu/drm/i915/display/intel_tc.c index 02fdb8d37f3e..63886d719fa4 100644 --- a/drivers/gpu/drm/i915/display/intel_tc.c +++ b/drivers/gpu/drm/i915/display/intel_tc.c @@ -40,7 +40,9 @@ struct intel_tc_port { struct mutex lock; /* protects the TypeC port mode */ intel_wakeref_t lock_wakeref; +#if IS_ENABLED(CONFIG_DRM_I915_DEBUG_RUNTIME_PM) enum intel_display_power_domain lock_power_domain; +#endif struct delayed_work disconnect_phy_work; int link_refcount; bool legacy_port:1; @@ -116,43 +118,60 @@ bool intel_tc_cold_requires_aux_pw(struct intel_digital_port *dig_port) } static enum intel_display_power_domain -tc_cold_get_power_domain(struct intel_tc_port *tc, enum tc_port_mode mode) +tc_phy_cold_off_domain(struct intel_tc_port *tc) { struct drm_i915_private *i915 = tc_to_i915(tc); struct intel_digital_port *dig_port = tc->dig_port; - if (mode == TC_PORT_TBT_ALT || !intel_tc_cold_requires_aux_pw(dig_port)) + if (tc->mode == TC_PORT_TBT_ALT || !intel_tc_cold_requires_aux_pw(dig_port)) return POWER_DOMAIN_TC_COLD_OFF; return intel_display_power_legacy_aux_domain(i915, dig_port->aux_ch); } static intel_wakeref_t -tc_cold_block_in_mode(struct intel_tc_port *tc, enum tc_port_mode mode, - enum intel_display_power_domain *domain) +__tc_cold_block(struct intel_tc_port *tc, enum intel_display_power_domain *domain) { struct drm_i915_private *i915 = tc_to_i915(tc); - *domain = tc_cold_get_power_domain(tc, mode); + *domain = tc_phy_cold_off_domain(tc); return intel_display_power_get(i915, *domain); } static intel_wakeref_t -tc_cold_block(struct intel_tc_port *tc, enum intel_display_power_domain *domain) +tc_cold_block(struct intel_tc_port *tc) { - return tc_cold_block_in_mode(tc, tc->mode, domain); + enum intel_display_power_domain domain; + intel_wakeref_t wakeref; + + wakeref = __tc_cold_block(tc, &domain); +#if IS_ENABLED(CONFIG_DRM_I915_DEBUG_RUNTIME_PM) + tc->lock_power_domain = domain; +#endif + return wakeref; } static void -tc_cold_unblock(struct intel_tc_port *tc, enum intel_display_power_domain domain, - intel_wakeref_t wakeref) +__tc_cold_unblock(struct intel_tc_port *tc, enum intel_display_power_domain domain, + intel_wakeref_t wakeref) { struct drm_i915_private *i915 = tc_to_i915(tc); intel_display_power_put(i915, domain, wakeref); } +static void +tc_cold_unblock(struct intel_tc_port *tc, intel_wakeref_t wakeref) +{ + enum intel_display_power_domain domain = tc_phy_cold_off_domain(tc); + +#if IS_ENABLED(CONFIG_DRM_I915_DEBUG_RUNTIME_PM) + drm_WARN_ON(&tc_to_i915(tc)->drm, tc->lock_power_domain != domain); +#endif + __tc_cold_unblock(tc, domain, wakeref); +} + static void assert_tc_cold_blocked(struct intel_tc_port *tc) { @@ -160,8 +179,7 @@ assert_tc_cold_blocked(struct intel_tc_port *tc) bool enabled; enabled = intel_display_power_is_enabled(i915, - tc_cold_get_power_domain(tc, - tc->mode)); + tc_phy_cold_off_domain(tc)); drm_WARN_ON(&i915->drm, !enabled); } @@ -413,13 +431,13 @@ static void icl_tc_phy_get_hw_state(struct intel_tc_port *tc) enum intel_display_power_domain domain; intel_wakeref_t tc_cold_wref; - tc_cold_wref = tc_cold_block(tc, &domain); + tc_cold_wref = __tc_cold_block(tc, &domain); tc->mode = tc_phy_get_current_mode(tc); if (tc->mode != TC_PORT_DISCONNECTED) - tc->lock_wakeref = tc_cold_block(tc, &tc->lock_power_domain); + tc->lock_wakeref = tc_cold_block(tc); - tc_cold_unblock(tc, domain, tc_cold_wref); + __tc_cold_unblock(tc, domain, tc_cold_wref); } /* @@ -474,7 +492,7 @@ static bool icl_tc_phy_connect(struct intel_tc_port *tc, { struct drm_i915_private *i915 = tc_to_i915(tc); - tc->lock_wakeref = tc_cold_block(tc, &tc->lock_power_domain); + tc->lock_wakeref = tc_cold_block(tc); if (tc->mode == TC_PORT_TBT_ALT) return true; @@ -497,9 +515,7 @@ static bool icl_tc_phy_connect(struct intel_tc_port *tc, out_release_phy: tc_phy_take_ownership(tc, false); out_unblock_tc_cold: - tc_cold_unblock(tc, - tc->lock_power_domain, - fetch_and_zero(&tc->lock_wakeref)); + tc_cold_unblock(tc, fetch_and_zero(&tc->lock_wakeref)); return false; } @@ -516,9 +532,7 @@ static void icl_tc_phy_disconnect(struct intel_tc_port *tc) tc_phy_take_ownership(tc, false); fallthrough; case TC_PORT_TBT_ALT: - tc_cold_unblock(tc, - tc->lock_power_domain, - fetch_and_zero(&tc->lock_wakeref)); + tc_cold_unblock(tc, fetch_and_zero(&tc->lock_wakeref)); break; default: MISSING_CASE(tc->mode); @@ -1177,7 +1191,6 @@ void intel_tc_port_put_link(struct intel_digital_port *dig_port) static bool tc_has_modular_fia(struct drm_i915_private *i915, struct intel_tc_port *tc) { - enum intel_display_power_domain domain; intel_wakeref_t wakeref; u32 val; @@ -1185,9 +1198,9 @@ tc_has_modular_fia(struct drm_i915_private *i915, struct intel_tc_port *tc) return false; mutex_lock(&tc->lock); - wakeref = tc_cold_block(tc, &domain); + wakeref = tc_cold_block(tc); val = intel_de_read(i915, PORT_TX_DFLEXDPSP(FIA1)); - tc_cold_unblock(tc, domain, wakeref); + tc_cold_unblock(tc, wakeref); mutex_unlock(&tc->lock); drm_WARN_ON(&i915->drm, val == 0xffffffff); -- cgit From 16cf693e31bdb423f35ab24081575ec9699303fc Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Thu, 23 Mar 2023 16:20:25 +0200 Subject: drm/i915/tc: Add TC PHY hook to get the TC-cold blocking power domain Instead of the corresponding if ladder, add a TC PHY hook to get the platform and TC mode specific power domain used for blocking the TC-cold power state. Reviewed-by: Mika Kahola Signed-off-by: Imre Deak Link: https://patchwork.freedesktop.org/patch/msgid/20230323142035.1432621-20-imre.deak@intel.com --- drivers/gpu/drm/i915/display/intel_tc.c | 73 ++++++++++++++++++++++++++------- 1 file changed, 59 insertions(+), 14 deletions(-) (limited to 'drivers/gpu/drm/i915/display/intel_tc.c') diff --git a/drivers/gpu/drm/i915/display/intel_tc.c b/drivers/gpu/drm/i915/display/intel_tc.c index 63886d719fa4..3972b2457d11 100644 --- a/drivers/gpu/drm/i915/display/intel_tc.c +++ b/drivers/gpu/drm/i915/display/intel_tc.c @@ -25,6 +25,7 @@ enum tc_port_mode { struct intel_tc_port; struct intel_tc_phy_ops { + enum intel_display_power_domain (*cold_off_domain)(struct intel_tc_port *tc); u32 (*hpd_live_status)(struct intel_tc_port *tc); bool (*is_ready)(struct intel_tc_port *tc); bool (*is_owned)(struct intel_tc_port *tc); @@ -53,6 +54,8 @@ struct intel_tc_port { u8 phy_fia_idx; }; +static enum intel_display_power_domain +tc_phy_cold_off_domain(struct intel_tc_port *); static u32 tc_phy_hpd_live_status(struct intel_tc_port *tc); static bool tc_phy_is_ready(struct intel_tc_port *tc); static bool tc_phy_take_ownership(struct intel_tc_port *tc, bool take); @@ -113,20 +116,8 @@ bool intel_tc_cold_requires_aux_pw(struct intel_digital_port *dig_port) struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev); struct intel_tc_port *tc = to_tc_port(dig_port); - return (DISPLAY_VER(i915) == 11 && tc->legacy_port) || - IS_ALDERLAKE_P(i915); -} - -static enum intel_display_power_domain -tc_phy_cold_off_domain(struct intel_tc_port *tc) -{ - struct drm_i915_private *i915 = tc_to_i915(tc); - struct intel_digital_port *dig_port = tc->dig_port; - - if (tc->mode == TC_PORT_TBT_ALT || !intel_tc_cold_requires_aux_pw(dig_port)) - return POWER_DOMAIN_TC_COLD_OFF; - - return intel_display_power_legacy_aux_domain(i915, dig_port->aux_ch); + return tc_phy_cold_off_domain(tc) == + intel_display_power_legacy_aux_domain(i915, dig_port->aux_ch); } static intel_wakeref_t @@ -334,6 +325,18 @@ static void tc_port_fixup_legacy_flag(struct intel_tc_port *tc, * ICL TC PHY handlers * ------------------- */ +static enum intel_display_power_domain +icl_tc_phy_cold_off_domain(struct intel_tc_port *tc) +{ + struct drm_i915_private *i915 = tc_to_i915(tc); + struct intel_digital_port *dig_port = tc->dig_port; + + if (tc->legacy_port) + return intel_display_power_legacy_aux_domain(i915, dig_port->aux_ch); + + return POWER_DOMAIN_TC_COLD_OFF; +} + static u32 icl_tc_phy_hpd_live_status(struct intel_tc_port *tc) { struct drm_i915_private *i915 = tc_to_i915(tc); @@ -540,6 +543,27 @@ static void icl_tc_phy_disconnect(struct intel_tc_port *tc) } static const struct intel_tc_phy_ops icl_tc_phy_ops = { + .cold_off_domain = icl_tc_phy_cold_off_domain, + .hpd_live_status = icl_tc_phy_hpd_live_status, + .is_ready = icl_tc_phy_is_ready, + .is_owned = icl_tc_phy_is_owned, + .get_hw_state = icl_tc_phy_get_hw_state, + .connect = icl_tc_phy_connect, + .disconnect = icl_tc_phy_disconnect, +}; + +/* + * TGL TC PHY handlers + * ------------------- + */ +static enum intel_display_power_domain +tgl_tc_phy_cold_off_domain(struct intel_tc_port *tc) +{ + return POWER_DOMAIN_TC_COLD_OFF; +} + +static const struct intel_tc_phy_ops tgl_tc_phy_ops = { + .cold_off_domain = tgl_tc_phy_cold_off_domain, .hpd_live_status = icl_tc_phy_hpd_live_status, .is_ready = icl_tc_phy_is_ready, .is_owned = icl_tc_phy_is_owned, @@ -552,6 +576,18 @@ static const struct intel_tc_phy_ops icl_tc_phy_ops = { * ADLP TC PHY handlers * -------------------- */ +static enum intel_display_power_domain +adlp_tc_phy_cold_off_domain(struct intel_tc_port *tc) +{ + struct drm_i915_private *i915 = tc_to_i915(tc); + struct intel_digital_port *dig_port = tc->dig_port; + + if (tc->mode != TC_PORT_TBT_ALT) + return intel_display_power_legacy_aux_domain(i915, dig_port->aux_ch); + + return POWER_DOMAIN_TC_COLD_OFF; +} + static u32 adlp_tc_phy_hpd_live_status(struct intel_tc_port *tc) { struct drm_i915_private *i915 = tc_to_i915(tc); @@ -624,6 +660,7 @@ static bool adlp_tc_phy_is_owned(struct intel_tc_port *tc) } static const struct intel_tc_phy_ops adlp_tc_phy_ops = { + .cold_off_domain = adlp_tc_phy_cold_off_domain, .hpd_live_status = adlp_tc_phy_hpd_live_status, .is_ready = adlp_tc_phy_is_ready, .is_owned = adlp_tc_phy_is_owned, @@ -636,6 +673,12 @@ static const struct intel_tc_phy_ops adlp_tc_phy_ops = { * Generic TC PHY handlers * ----------------------- */ +static enum intel_display_power_domain +tc_phy_cold_off_domain(struct intel_tc_port *tc) +{ + return tc->phy_ops->cold_off_domain(tc); +} + static u32 tc_phy_hpd_live_status(struct intel_tc_port *tc) { struct drm_i915_private *i915 = tc_to_i915(tc); @@ -1246,6 +1289,8 @@ int intel_tc_port_init(struct intel_digital_port *dig_port, bool is_legacy) if (DISPLAY_VER(i915) >= 13) tc->phy_ops = &adlp_tc_phy_ops; + else if (DISPLAY_VER(i915) >= 12) + tc->phy_ops = &tgl_tc_phy_ops; else tc->phy_ops = &icl_tc_phy_ops; -- cgit From 7e696546353f659f60fd10616e04a2aa59ab2ac0 Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Thu, 23 Mar 2023 16:20:26 +0200 Subject: drm/i915/tc: Add asserts in TC PHY hooks that the required power is on Add an assert to each TC PHY hook that their required power domain is enabled. While at it add a comment describing the domains used on each platform and TC mode. v2: Fix non kernel-doc multiline comments. (Jani) Cc: Jani Nikula Reviewed-by: Mika Kahola Signed-off-by: Imre Deak Link: https://patchwork.freedesktop.org/patch/msgid/20230323142035.1432621-21-imre.deak@intel.com --- drivers/gpu/drm/i915/display/intel_tc.c | 61 +++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) (limited to 'drivers/gpu/drm/i915/display/intel_tc.c') diff --git a/drivers/gpu/drm/i915/display/intel_tc.c b/drivers/gpu/drm/i915/display/intel_tc.c index 3972b2457d11..391a4111fcfd 100644 --- a/drivers/gpu/drm/i915/display/intel_tc.c +++ b/drivers/gpu/drm/i915/display/intel_tc.c @@ -111,6 +111,46 @@ bool intel_tc_port_in_legacy_mode(struct intel_digital_port *dig_port) return intel_tc_port_in_mode(dig_port, TC_PORT_LEGACY); } +/* + * The display power domains used for TC ports depending on the + * platform and TC mode (legacy, DP-alt, TBT): + * + * POWER_DOMAIN_DISPLAY_CORE: + * -------------------------- + * ADLP/all modes: + * - TCSS/IOM access for PHY ready state. + * ADLP+/all modes: + * - DE/north-,south-HPD ISR access for HPD live state. + * + * POWER_DOMAIN_PORT_DDI_LANES_: + * ----------------------------------- + * ICL+/all modes: + * - DE/DDI_BUF access for port enabled state. + * ADLP/all modes: + * - DE/DDI_BUF access for PHY owned state. + * + * POWER_DOMAIN_AUX_USBC: + * ------------------------------------- + * ICL/legacy mode: + * - TCSS/IOM,FIA access for PHY ready, owned and HPD live state + * - TCSS/PHY: block TC-cold power state for using the PHY AUX and + * main lanes. + * ADLP/legacy, DP-alt modes: + * - TCSS/PHY: block TC-cold power state for using the PHY AUX and + * main lanes. + * + * POWER_DOMAIN_TC_COLD_OFF: + * ------------------------- + * TGL/legacy, DP-alt modes: + * - TCSS/IOM,FIA access for PHY ready, owned and HPD live state + * - TCSS/PHY: block TC-cold power state for using the PHY AUX and + * main lanes. + * + * ICL, TGL, ADLP/TBT mode: + * - TCSS/IOM,FIA access for HPD live state + * - TCSS/TBT: block TC-cold power state for using the (TBT DP-IN) + * AUX and main lanes. + */ bool intel_tc_cold_requires_aux_pw(struct intel_digital_port *dig_port) { struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev); @@ -163,6 +203,15 @@ tc_cold_unblock(struct intel_tc_port *tc, intel_wakeref_t wakeref) __tc_cold_unblock(tc, domain, wakeref); } +static void +assert_display_core_power_enabled(struct intel_tc_port *tc) +{ + struct drm_i915_private *i915 = tc_to_i915(tc); + + drm_WARN_ON(&i915->drm, + !intel_display_power_is_enabled(i915, POWER_DOMAIN_DISPLAY_CORE)); +} + static void assert_tc_cold_blocked(struct intel_tc_port *tc) { @@ -378,6 +427,8 @@ static bool icl_tc_phy_is_ready(struct intel_tc_port *tc) struct drm_i915_private *i915 = tc_to_i915(tc); u32 val; + assert_tc_cold_blocked(tc); + val = intel_de_read(i915, PORT_TX_DFLEXDPPMS(tc->phy_fia)); if (val == 0xffffffff) { drm_dbg_kms(&i915->drm, @@ -395,6 +446,8 @@ static bool icl_tc_phy_take_ownership(struct intel_tc_port *tc, struct drm_i915_private *i915 = tc_to_i915(tc); u32 val; + assert_tc_cold_blocked(tc); + val = intel_de_read(i915, PORT_TX_DFLEXDPCSSS(tc->phy_fia)); if (val == 0xffffffff) { drm_dbg_kms(&i915->drm, @@ -418,6 +471,8 @@ static bool icl_tc_phy_is_owned(struct intel_tc_port *tc) struct drm_i915_private *i915 = tc_to_i915(tc); u32 val; + assert_tc_cold_blocked(tc); + val = intel_de_read(i915, PORT_TX_DFLEXDPCSSS(tc->phy_fia)); if (val == 0xffffffff) { drm_dbg_kms(&i915->drm, @@ -626,6 +681,8 @@ static bool adlp_tc_phy_is_ready(struct intel_tc_port *tc) enum tc_port tc_port = intel_port_to_tc(i915, tc->dig_port->base.port); u32 val; + assert_display_core_power_enabled(tc); + val = intel_de_read(i915, TCSS_DDI_STATUS(tc_port)); if (val == 0xffffffff) { drm_dbg_kms(&i915->drm, @@ -643,6 +700,8 @@ static bool adlp_tc_phy_take_ownership(struct intel_tc_port *tc, struct drm_i915_private *i915 = tc_to_i915(tc); enum port port = tc->dig_port->base.port; + assert_tc_port_power_enabled(tc); + intel_de_rmw(i915, DDI_BUF_CTL(port), DDI_BUF_CTL_TC_PHY_OWNERSHIP, take ? DDI_BUF_CTL_TC_PHY_OWNERSHIP : 0); @@ -655,6 +714,8 @@ static bool adlp_tc_phy_is_owned(struct intel_tc_port *tc) enum port port = tc->dig_port->base.port; u32 val; + assert_tc_port_power_enabled(tc); + val = intel_de_read(i915, DDI_BUF_CTL(port)); return val & DDI_BUF_CTL_TC_PHY_OWNERSHIP; } -- cgit From c55b73f391a726a45cc014464ba7ebea5f1d7386 Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Thu, 23 Mar 2023 16:20:27 +0200 Subject: drm/i915/tc: Add TC PHY hook to init the PHY Add a hook for platform specific PHY initialization. Move the detection of modular FIAs to the TGL handler, skipping this on ADLP+ where the FIAs are always modular, not requiring a detection. Reviewed-by: Mika Kahola Signed-off-by: Imre Deak Link: https://patchwork.freedesktop.org/patch/msgid/20230323142035.1432621-22-imre.deak@intel.com --- drivers/gpu/drm/i915/display/intel_tc.c | 96 +++++++++++++++++++------------- drivers/gpu/drm/i915/i915_pci.c | 3 - drivers/gpu/drm/i915/intel_device_info.h | 1 - 3 files changed, 56 insertions(+), 44 deletions(-) (limited to 'drivers/gpu/drm/i915/display/intel_tc.c') diff --git a/drivers/gpu/drm/i915/display/intel_tc.c b/drivers/gpu/drm/i915/display/intel_tc.c index 391a4111fcfd..2cbdecead522 100644 --- a/drivers/gpu/drm/i915/display/intel_tc.c +++ b/drivers/gpu/drm/i915/display/intel_tc.c @@ -32,6 +32,7 @@ struct intel_tc_phy_ops { void (*get_hw_state)(struct intel_tc_port *tc); bool (*connect)(struct intel_tc_port *tc, int required_lanes); void (*disconnect)(struct intel_tc_port *tc); + void (*init)(struct intel_tc_port *tc); }; struct intel_tc_port { @@ -370,6 +371,25 @@ static void tc_port_fixup_legacy_flag(struct intel_tc_port *tc, tc->legacy_port = !tc->legacy_port; } +static void tc_phy_load_fia_params(struct intel_tc_port *tc, bool modular_fia) +{ + struct drm_i915_private *i915 = tc_to_i915(tc); + enum port port = tc->dig_port->base.port; + enum tc_port tc_port = intel_port_to_tc(i915, port); + + /* + * Each Modular FIA instance houses 2 TC ports. In SOC that has more + * than two TC ports, there are multiple instances of Modular FIA. + */ + if (modular_fia) { + tc->phy_fia = tc_port / 2; + tc->phy_fia_idx = tc_port % 2; + } else { + tc->phy_fia = FIA1; + tc->phy_fia_idx = tc_port; + } +} + /* * ICL TC PHY handlers * ------------------- @@ -597,6 +617,11 @@ static void icl_tc_phy_disconnect(struct intel_tc_port *tc) } } +static void icl_tc_phy_init(struct intel_tc_port *tc) +{ + tc_phy_load_fia_params(tc, false); +} + static const struct intel_tc_phy_ops icl_tc_phy_ops = { .cold_off_domain = icl_tc_phy_cold_off_domain, .hpd_live_status = icl_tc_phy_hpd_live_status, @@ -605,6 +630,7 @@ static const struct intel_tc_phy_ops icl_tc_phy_ops = { .get_hw_state = icl_tc_phy_get_hw_state, .connect = icl_tc_phy_connect, .disconnect = icl_tc_phy_disconnect, + .init = icl_tc_phy_init, }; /* @@ -617,6 +643,20 @@ tgl_tc_phy_cold_off_domain(struct intel_tc_port *tc) return POWER_DOMAIN_TC_COLD_OFF; } +static void tgl_tc_phy_init(struct intel_tc_port *tc) +{ + struct drm_i915_private *i915 = tc_to_i915(tc); + intel_wakeref_t wakeref; + u32 val; + + with_intel_display_power(i915, tc_phy_cold_off_domain(tc), wakeref) + val = intel_de_read(i915, PORT_TX_DFLEXDPSP(FIA1)); + + drm_WARN_ON(&i915->drm, val == 0xffffffff); + + tc_phy_load_fia_params(tc, val & MODULAR_FIA_MASK); +} + static const struct intel_tc_phy_ops tgl_tc_phy_ops = { .cold_off_domain = tgl_tc_phy_cold_off_domain, .hpd_live_status = icl_tc_phy_hpd_live_status, @@ -625,6 +665,7 @@ static const struct intel_tc_phy_ops tgl_tc_phy_ops = { .get_hw_state = icl_tc_phy_get_hw_state, .connect = icl_tc_phy_connect, .disconnect = icl_tc_phy_disconnect, + .init = tgl_tc_phy_init, }; /* @@ -720,6 +761,11 @@ static bool adlp_tc_phy_is_owned(struct intel_tc_port *tc) return val & DDI_BUF_CTL_TC_PHY_OWNERSHIP; } +static void adlp_tc_phy_init(struct intel_tc_port *tc) +{ + tc_phy_load_fia_params(tc, true); +} + static const struct intel_tc_phy_ops adlp_tc_phy_ops = { .cold_off_domain = adlp_tc_phy_cold_off_domain, .hpd_live_status = adlp_tc_phy_hpd_live_status, @@ -728,6 +774,7 @@ static const struct intel_tc_phy_ops adlp_tc_phy_ops = { .get_hw_state = icl_tc_phy_get_hw_state, .connect = icl_tc_phy_connect, .disconnect = icl_tc_phy_disconnect, + .init = adlp_tc_phy_init, }; /* @@ -972,6 +1019,13 @@ static void tc_phy_disconnect(struct intel_tc_port *tc) } } +static void tc_phy_init(struct intel_tc_port *tc) +{ + mutex_lock(&tc->lock); + tc->phy_ops->init(tc); + mutex_unlock(&tc->lock); +} + static void intel_tc_port_reset_mode(struct intel_tc_port *tc, int required_lanes, bool force_disconnect) { @@ -1292,45 +1346,6 @@ void intel_tc_port_put_link(struct intel_digital_port *dig_port) intel_tc_port_flush_work(dig_port); } -static bool -tc_has_modular_fia(struct drm_i915_private *i915, struct intel_tc_port *tc) -{ - intel_wakeref_t wakeref; - u32 val; - - if (!INTEL_INFO(i915)->display.has_modular_fia) - return false; - - mutex_lock(&tc->lock); - wakeref = tc_cold_block(tc); - val = intel_de_read(i915, PORT_TX_DFLEXDPSP(FIA1)); - tc_cold_unblock(tc, wakeref); - mutex_unlock(&tc->lock); - - drm_WARN_ON(&i915->drm, val == 0xffffffff); - - return val & MODULAR_FIA_MASK; -} - -static void -tc_port_load_fia_params(struct drm_i915_private *i915, struct intel_tc_port *tc) -{ - enum port port = tc->dig_port->base.port; - enum tc_port tc_port = intel_port_to_tc(i915, port); - - /* - * Each Modular FIA instance houses 2 TC ports. In SOC that has more - * than two TC ports, there are multiple instances of Modular FIA. - */ - if (tc_has_modular_fia(i915, tc)) { - tc->phy_fia = tc_port / 2; - tc->phy_fia_idx = tc_port % 2; - } else { - tc->phy_fia = FIA1; - tc->phy_fia_idx = tc_port; - } -} - int intel_tc_port_init(struct intel_digital_port *dig_port, bool is_legacy) { struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev); @@ -1363,7 +1378,8 @@ int intel_tc_port_init(struct intel_digital_port *dig_port, bool is_legacy) tc->legacy_port = is_legacy; tc->mode = TC_PORT_DISCONNECTED; tc->link_refcount = 0; - tc_port_load_fia_params(i915, tc); + + tc_phy_init(tc); intel_tc_port_init_mode(dig_port); diff --git a/drivers/gpu/drm/i915/i915_pci.c b/drivers/gpu/drm/i915/i915_pci.c index a8d942b16223..bbc4d62e490e 100644 --- a/drivers/gpu/drm/i915/i915_pci.c +++ b/drivers/gpu/drm/i915/i915_pci.c @@ -896,7 +896,6 @@ static const struct intel_device_info jsl_info = { static const struct intel_device_info tgl_info = { GEN12_FEATURES, PLATFORM(INTEL_TIGERLAKE), - .display.has_modular_fia = 1, .__runtime.platform_engine_mask = BIT(RCS0) | BIT(BCS0) | BIT(VECS0) | BIT(VCS0) | BIT(VCS2), }; @@ -996,7 +995,6 @@ static const struct intel_device_info adl_p_info = { BIT(TRANSCODER_C) | BIT(TRANSCODER_D) | BIT(TRANSCODER_DSI_0) | BIT(TRANSCODER_DSI_1), .display.has_cdclk_crawl = 1, - .display.has_modular_fia = 1, .display.has_psr_hw_tracking = 0, .__runtime.platform_engine_mask = BIT(RCS0) | BIT(BCS0) | BIT(VECS0) | BIT(VCS0) | BIT(VCS2), @@ -1143,7 +1141,6 @@ static const struct intel_device_info mtl_info = { .__runtime.graphics.ip.rel = 70, .__runtime.media.ip.ver = 13, PLATFORM(INTEL_METEORLAKE), - .display.has_modular_fia = 1, .extra_gt_list = xelpmp_extra_gt, .has_flat_ccs = 0, .has_gmd_id = 1, diff --git a/drivers/gpu/drm/i915/intel_device_info.h b/drivers/gpu/drm/i915/intel_device_info.h index b30cc8b97c3a..dd8b17c15566 100644 --- a/drivers/gpu/drm/i915/intel_device_info.h +++ b/drivers/gpu/drm/i915/intel_device_info.h @@ -192,7 +192,6 @@ enum intel_ppgtt_type { func(has_hotplug); \ func(has_hti); \ func(has_ipc); \ - func(has_modular_fia); \ func(has_overlay); \ func(has_psr); \ func(has_psr_hw_tracking); \ -- cgit From 825535f44fef606e5b4484ebb8cb3827db59037e Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Thu, 23 Mar 2023 16:20:28 +0200 Subject: drm/i915/adlp/tc: Use the DE HPD ISR register for hotplug detection The spec says to use the CPU ISR registers for DP-alt/TBT HPD detection on ADLP, so do that instead of using the related IOM/TCSS registers. Bspec: 55480, 55482, 49212, 49305 Reviewed-by: Mika Kahola Signed-off-by: Imre Deak Link: https://patchwork.freedesktop.org/patch/msgid/20230323142035.1432621-23-imre.deak@intel.com --- drivers/gpu/drm/i915/display/intel_tc.c | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) (limited to 'drivers/gpu/drm/i915/display/intel_tc.c') diff --git a/drivers/gpu/drm/i915/display/intel_tc.c b/drivers/gpu/drm/i915/display/intel_tc.c index 2cbdecead522..24cee1b5629b 100644 --- a/drivers/gpu/drm/i915/display/intel_tc.c +++ b/drivers/gpu/drm/i915/display/intel_tc.c @@ -688,22 +688,19 @@ static u32 adlp_tc_phy_hpd_live_status(struct intel_tc_port *tc) { struct drm_i915_private *i915 = tc_to_i915(tc); struct intel_digital_port *dig_port = tc->dig_port; - enum tc_port tc_port = intel_port_to_tc(i915, dig_port->base.port); - u32 isr_bit = i915->display.hotplug.pch_hpd[dig_port->base.hpd_pin]; - u32 val, mask = 0; + enum hpd_pin hpd_pin = dig_port->base.hpd_pin; + u32 cpu_isr_bits = i915->display.hotplug.hpd[hpd_pin]; + u32 pch_isr_bit = i915->display.hotplug.pch_hpd[hpd_pin]; + u32 cpu_isr; + u32 mask = 0; - /* - * On ADL-P HW/FW will wake from TCCOLD to complete the read access of - * registers in IOM. Note that this doesn't apply to PHY and FIA - * registers. - */ - val = intel_de_read(i915, TCSS_DDI_STATUS(tc_port)); - if (val & TCSS_DDI_STATUS_HPD_LIVE_STATUS_ALT) + cpu_isr = intel_de_read(i915, GEN11_DE_HPD_ISR); + if (cpu_isr & (cpu_isr_bits & GEN11_DE_TC_HOTPLUG_MASK)) mask |= BIT(TC_PORT_DP_ALT); - if (val & TCSS_DDI_STATUS_HPD_LIVE_STATUS_TBT) + if (cpu_isr & (cpu_isr_bits & GEN11_DE_TBT_HOTPLUG_MASK)) mask |= BIT(TC_PORT_TBT_ALT); - if (intel_de_read(i915, SDEISR) & isr_bit) + if (intel_de_read(i915, SDEISR) & pch_isr_bit) mask |= BIT(TC_PORT_LEGACY); return mask; -- cgit From 8979918af711b057620c7c5f9d29a0043f927753 Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Thu, 23 Mar 2023 16:20:29 +0200 Subject: drm/i915/tc: Get power ref for reading the HPD live status register Enable the power required for the HPD live status register access instead of depending on the caller blocking the TC-cold power state (during HW readout and connector probing). A follow up patch will remove connecting/disconnecting the PHY around connector probing, so querying the HPD status can happen in this case without TC-cold being blocked. Reviewed-by: Mika Kahola Signed-off-by: Imre Deak Link: https://patchwork.freedesktop.org/patch/msgid/20230323142035.1432621-24-imre.deak@intel.com --- drivers/gpu/drm/i915/display/intel_tc.c | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) (limited to 'drivers/gpu/drm/i915/display/intel_tc.c') diff --git a/drivers/gpu/drm/i915/display/intel_tc.c b/drivers/gpu/drm/i915/display/intel_tc.c index 24cee1b5629b..f9a09b1bb329 100644 --- a/drivers/gpu/drm/i915/display/intel_tc.c +++ b/drivers/gpu/drm/i915/display/intel_tc.c @@ -411,24 +411,29 @@ static u32 icl_tc_phy_hpd_live_status(struct intel_tc_port *tc) struct drm_i915_private *i915 = tc_to_i915(tc); struct intel_digital_port *dig_port = tc->dig_port; u32 isr_bit = i915->display.hotplug.pch_hpd[dig_port->base.hpd_pin]; + intel_wakeref_t wakeref; + u32 fia_isr; + u32 pch_isr; u32 mask = 0; - u32 val; - val = intel_de_read(i915, PORT_TX_DFLEXDPSP(tc->phy_fia)); + with_intel_display_power(i915, tc_phy_cold_off_domain(tc), wakeref) { + fia_isr = intel_de_read(i915, PORT_TX_DFLEXDPSP(tc->phy_fia)); + pch_isr = intel_de_read(i915, SDEISR); + } - if (val == 0xffffffff) { + if (fia_isr == 0xffffffff) { drm_dbg_kms(&i915->drm, "Port %s: PHY in TCCOLD, nothing connected\n", tc->port_name); return mask; } - if (val & TC_LIVE_STATE_TBT(tc->phy_fia_idx)) + if (fia_isr & TC_LIVE_STATE_TBT(tc->phy_fia_idx)) mask |= BIT(TC_PORT_TBT_ALT); - if (val & TC_LIVE_STATE_TC(tc->phy_fia_idx)) + if (fia_isr & TC_LIVE_STATE_TC(tc->phy_fia_idx)) mask |= BIT(TC_PORT_DP_ALT); - if (intel_de_read(i915, SDEISR) & isr_bit) + if (pch_isr & isr_bit) mask |= BIT(TC_PORT_LEGACY); return mask; @@ -691,16 +696,22 @@ static u32 adlp_tc_phy_hpd_live_status(struct intel_tc_port *tc) enum hpd_pin hpd_pin = dig_port->base.hpd_pin; u32 cpu_isr_bits = i915->display.hotplug.hpd[hpd_pin]; u32 pch_isr_bit = i915->display.hotplug.pch_hpd[hpd_pin]; + intel_wakeref_t wakeref; u32 cpu_isr; + u32 pch_isr; u32 mask = 0; - cpu_isr = intel_de_read(i915, GEN11_DE_HPD_ISR); + with_intel_display_power(i915, POWER_DOMAIN_DISPLAY_CORE, wakeref) { + cpu_isr = intel_de_read(i915, GEN11_DE_HPD_ISR); + pch_isr = intel_de_read(i915, SDEISR); + } + if (cpu_isr & (cpu_isr_bits & GEN11_DE_TC_HOTPLUG_MASK)) mask |= BIT(TC_PORT_DP_ALT); if (cpu_isr & (cpu_isr_bits & GEN11_DE_TBT_HOTPLUG_MASK)) mask |= BIT(TC_PORT_TBT_ALT); - if (intel_de_read(i915, SDEISR) & pch_isr_bit) + if (pch_isr & pch_isr_bit) mask |= BIT(TC_PORT_LEGACY); return mask; -- cgit From ebcabb8b15708023b71b7044fdf928454613d118 Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Thu, 23 Mar 2023 16:20:30 +0200 Subject: drm/i915/tc: Don't connect the PHY in intel_tc_port_connected() Connecting the PHY for connector probing - also blocking TC-cold - isn't required and has some overhead. Taking only the mutex is sufficient, so do that. Reviewed-by: Mika Kahola Signed-off-by: Imre Deak Link: https://patchwork.freedesktop.org/patch/msgid/20230323142035.1432621-25-imre.deak@intel.com --- drivers/gpu/drm/i915/display/intel_tc.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) (limited to 'drivers/gpu/drm/i915/display/intel_tc.c') diff --git a/drivers/gpu/drm/i915/display/intel_tc.c b/drivers/gpu/drm/i915/display/intel_tc.c index f9a09b1bb329..40bd34a633c4 100644 --- a/drivers/gpu/drm/i915/display/intel_tc.c +++ b/drivers/gpu/drm/i915/display/intel_tc.c @@ -1235,20 +1235,25 @@ bool intel_tc_port_connected_locked(struct intel_encoder *encoder) struct intel_digital_port *dig_port = enc_to_dig_port(encoder); struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev); struct intel_tc_port *tc = to_tc_port(dig_port); + u32 mask = ~0; drm_WARN_ON(&i915->drm, !intel_tc_port_ref_held(dig_port)); - return tc_phy_hpd_live_status(tc) & BIT(tc->mode); + if (tc->mode != TC_PORT_DISCONNECTED) + mask = BIT(tc->mode); + + return tc_phy_hpd_live_status(tc) & mask; } bool intel_tc_port_connected(struct intel_encoder *encoder) { struct intel_digital_port *dig_port = enc_to_dig_port(encoder); + struct intel_tc_port *tc = to_tc_port(dig_port); bool is_connected; - intel_tc_port_lock(dig_port); + mutex_lock(&tc->lock); is_connected = intel_tc_port_connected_locked(encoder); - intel_tc_port_unlock(dig_port); + mutex_unlock(&tc->lock); return is_connected; } -- cgit From 9796a5b2725d1b3ddbbe7b1f3dec56af8cc6af22 Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Thu, 23 Mar 2023 16:20:31 +0200 Subject: drm/i915/adlp/tc: Align the connect/disconnect PHY sequence with bspec Bspec has updated the TC connect/disconnect sequences, add the required platform hooks for these. The difference wrt. the old sequence is the order of taking the PHY ownership - while holding a port power reference this requires - and blocking the TC-cold power state. Bspec: 49294 Reviewed-by: Mika Kahola Signed-off-by: Imre Deak Link: https://patchwork.freedesktop.org/patch/msgid/20230323142035.1432621-26-imre.deak@intel.com --- drivers/gpu/drm/i915/display/intel_tc.c | 111 +++++++++++++++++++++++++++----- 1 file changed, 94 insertions(+), 17 deletions(-) (limited to 'drivers/gpu/drm/i915/display/intel_tc.c') diff --git a/drivers/gpu/drm/i915/display/intel_tc.c b/drivers/gpu/drm/i915/display/intel_tc.c index 40bd34a633c4..0f147f94341d 100644 --- a/drivers/gpu/drm/i915/display/intel_tc.c +++ b/drivers/gpu/drm/i915/display/intel_tc.c @@ -59,7 +59,6 @@ static enum intel_display_power_domain tc_phy_cold_off_domain(struct intel_tc_port *); static u32 tc_phy_hpd_live_status(struct intel_tc_port *tc); static bool tc_phy_is_ready(struct intel_tc_port *tc); -static bool tc_phy_take_ownership(struct intel_tc_port *tc, bool take); static enum tc_port_mode tc_phy_get_current_mode(struct intel_tc_port *tc); static const char *tc_port_mode_name(enum tc_port_mode mode) @@ -581,7 +580,7 @@ static bool icl_tc_phy_connect(struct intel_tc_port *tc, return true; if ((!tc_phy_is_ready(tc) || - !tc_phy_take_ownership(tc, true)) && + !icl_tc_phy_take_ownership(tc, true)) && !drm_WARN_ON(&i915->drm, tc->mode == TC_PORT_LEGACY)) { drm_dbg_kms(&i915->drm, "Port %s: can't take PHY ownership (ready %s)\n", tc->port_name, @@ -596,7 +595,7 @@ static bool icl_tc_phy_connect(struct intel_tc_port *tc, return true; out_release_phy: - tc_phy_take_ownership(tc, false); + icl_tc_phy_take_ownership(tc, false); out_unblock_tc_cold: tc_cold_unblock(tc, fetch_and_zero(&tc->lock_wakeref)); @@ -612,7 +611,7 @@ static void icl_tc_phy_disconnect(struct intel_tc_port *tc) switch (tc->mode) { case TC_PORT_LEGACY: case TC_PORT_DP_ALT: - tc_phy_take_ownership(tc, false); + icl_tc_phy_take_ownership(tc, false); fallthrough; case TC_PORT_TBT_ALT: tc_cold_unblock(tc, fetch_and_zero(&tc->lock_wakeref)); @@ -769,6 +768,94 @@ static bool adlp_tc_phy_is_owned(struct intel_tc_port *tc) return val & DDI_BUF_CTL_TC_PHY_OWNERSHIP; } +static void adlp_tc_phy_get_hw_state(struct intel_tc_port *tc) +{ + struct drm_i915_private *i915 = tc_to_i915(tc); + enum intel_display_power_domain port_power_domain = + tc_port_power_domain(tc); + intel_wakeref_t port_wakeref; + + port_wakeref = intel_display_power_get(i915, port_power_domain); + + tc->mode = tc_phy_get_current_mode(tc); + if (tc->mode != TC_PORT_DISCONNECTED) + tc->lock_wakeref = tc_cold_block(tc); + + intel_display_power_put(i915, port_power_domain, port_wakeref); +} + +static bool adlp_tc_phy_connect(struct intel_tc_port *tc, int required_lanes) +{ + struct drm_i915_private *i915 = tc_to_i915(tc); + enum intel_display_power_domain port_power_domain = + tc_port_power_domain(tc); + intel_wakeref_t port_wakeref; + + if (tc->mode == TC_PORT_TBT_ALT) { + tc->lock_wakeref = tc_cold_block(tc); + return true; + } + + port_wakeref = intel_display_power_get(i915, port_power_domain); + + if (!adlp_tc_phy_take_ownership(tc, true) && + !drm_WARN_ON(&i915->drm, tc->mode == TC_PORT_LEGACY)) { + drm_dbg_kms(&i915->drm, "Port %s: can't take PHY ownership\n", + tc->port_name); + goto out_put_port_power; + } + + if (!tc_phy_is_ready(tc) && + !drm_WARN_ON(&i915->drm, tc->mode == TC_PORT_LEGACY)) { + drm_dbg_kms(&i915->drm, "Port %s: PHY not ready\n", + tc->port_name); + goto out_release_phy; + } + + tc->lock_wakeref = tc_cold_block(tc); + + if (!tc_phy_verify_legacy_or_dp_alt_mode(tc, required_lanes)) + goto out_unblock_tc_cold; + + intel_display_power_put(i915, port_power_domain, port_wakeref); + + return true; + +out_unblock_tc_cold: + tc_cold_unblock(tc, fetch_and_zero(&tc->lock_wakeref)); +out_release_phy: + adlp_tc_phy_take_ownership(tc, false); +out_put_port_power: + intel_display_power_put(i915, port_power_domain, port_wakeref); + + return false; +} + +static void adlp_tc_phy_disconnect(struct intel_tc_port *tc) +{ + struct drm_i915_private *i915 = tc_to_i915(tc); + enum intel_display_power_domain port_power_domain = + tc_port_power_domain(tc); + intel_wakeref_t port_wakeref; + + port_wakeref = intel_display_power_get(i915, port_power_domain); + + tc_cold_unblock(tc, fetch_and_zero(&tc->lock_wakeref)); + + switch (tc->mode) { + case TC_PORT_LEGACY: + case TC_PORT_DP_ALT: + adlp_tc_phy_take_ownership(tc, false); + fallthrough; + case TC_PORT_TBT_ALT: + break; + default: + MISSING_CASE(tc->mode); + } + + intel_display_power_put(i915, port_power_domain, port_wakeref); +} + static void adlp_tc_phy_init(struct intel_tc_port *tc) { tc_phy_load_fia_params(tc, true); @@ -779,9 +866,9 @@ static const struct intel_tc_phy_ops adlp_tc_phy_ops = { .hpd_live_status = adlp_tc_phy_hpd_live_status, .is_ready = adlp_tc_phy_is_ready, .is_owned = adlp_tc_phy_is_owned, - .get_hw_state = icl_tc_phy_get_hw_state, - .connect = icl_tc_phy_connect, - .disconnect = icl_tc_phy_disconnect, + .get_hw_state = adlp_tc_phy_get_hw_state, + .connect = adlp_tc_phy_connect, + .disconnect = adlp_tc_phy_disconnect, .init = adlp_tc_phy_init, }; @@ -823,16 +910,6 @@ static void tc_phy_get_hw_state(struct intel_tc_port *tc) tc->phy_ops->get_hw_state(tc); } -static bool tc_phy_take_ownership(struct intel_tc_port *tc, bool take) -{ - struct drm_i915_private *i915 = tc_to_i915(tc); - - if (IS_ALDERLAKE_P(i915)) - return adlp_tc_phy_take_ownership(tc, take); - - return icl_tc_phy_take_ownership(tc, take); -} - static bool tc_phy_is_ready_and_owned(struct intel_tc_port *tc, bool phy_is_ready, bool phy_is_owned) { -- cgit From 450c27fc9e9cf74ff9b96dd813817133e0f50cf1 Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Thu, 23 Mar 2023 16:20:34 +0200 Subject: drm/i915: Remove TC PHY disconnect workaround After the previous patch the workaround for a TC PHY hang issue is not required any more, remove it. Reviewed-by: Mika Kahola Signed-off-by: Imre Deak Link: https://patchwork.freedesktop.org/patch/msgid/20230323142035.1432621-29-imre.deak@intel.com --- drivers/gpu/drm/i915/display/intel_tc.c | 8 -------- 1 file changed, 8 deletions(-) (limited to 'drivers/gpu/drm/i915/display/intel_tc.c') diff --git a/drivers/gpu/drm/i915/display/intel_tc.c b/drivers/gpu/drm/i915/display/intel_tc.c index 0f147f94341d..2bb02d4e6859 100644 --- a/drivers/gpu/drm/i915/display/intel_tc.c +++ b/drivers/gpu/drm/i915/display/intel_tc.c @@ -1426,14 +1426,6 @@ void intel_tc_port_put_link(struct intel_digital_port *dig_port) intel_tc_port_lock(dig_port); __intel_tc_port_put_link(tc); intel_tc_port_unlock(dig_port); - - /* - * Disconnecting the PHY after the PHY's PLL gets disabled may - * hang the system on ADL-P, so disconnect the PHY here synchronously. - * TODO: remove this once the root cause of the ordering requirement - * is found/fixed. - */ - intel_tc_port_flush_work(dig_port); } int intel_tc_port_init(struct intel_digital_port *dig_port, bool is_legacy) -- cgit From efd8127454054d6d52040c17f7fa1656ac3befdc Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Wed, 5 Apr 2023 13:41:41 +0300 Subject: drm/i915/tc: demote a kernel-doc comment to a regular comment There's not much point in a static work function having a kernel-doc comment. Just clean it up and make it a regular comment. This fixes the kernel-doc warnings: drivers/gpu/drm/i915/display/intel_tc.c:1370: warning: Function parameter or member 'work' not described in 'intel_tc_port_disconnect_phy_work' drivers/gpu/drm/i915/display/intel_tc.c:1370: warning: Excess function parameter 'dig_port' description in 'intel_tc_port_disconnect_phy_work' Signed-off-by: Jani Nikula Reviewed-by: Vinod Govindapillai Link: https://patchwork.freedesktop.org/patch/msgid/20230405104142.766598-1-jani.nikula@intel.com --- drivers/gpu/drm/i915/display/intel_tc.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'drivers/gpu/drm/i915/display/intel_tc.c') diff --git a/drivers/gpu/drm/i915/display/intel_tc.c b/drivers/gpu/drm/i915/display/intel_tc.c index 2bb02d4e6859..3b60995e9dfb 100644 --- a/drivers/gpu/drm/i915/display/intel_tc.c +++ b/drivers/gpu/drm/i915/display/intel_tc.c @@ -1358,10 +1358,7 @@ void intel_tc_port_lock(struct intel_digital_port *dig_port) __intel_tc_port_lock(to_tc_port(dig_port), 1); } -/** - * intel_tc_port_disconnect_phy_work: disconnect TypeC PHY from display port - * @dig_port: digital port - * +/* * Disconnect the given digital port from its TypeC PHY (handing back the * control of the PHY to the TypeC subsystem). This will happen in a delayed * manner after each aux transactions and modeset disables. -- cgit