mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-05-05 14:40:12 -04:00
drm/i915/mtl: Power up TCSS
Add register writes to enable powering up Type-C subsystem i.e. TCSS. For MeteorLake we need to request TCSS to power up and check the TCSS power state after 500 us. In addition, for PICA we need to set/clear the Type-C PHY ownnership bit when Type-C device is connected/disconnected. Reviewed-by: Matt Atwood <matthew.s.atwood@intel.com> Signed-off-by: Mika Kahola <mika.kahola@intel.com> Signed-off-by: Imre Deak <imre.deak@intel.com> Signed-off-by: Radhakrishna Sripada <radhakrishna.sripada@intel.com> Link: https://patchwork.freedesktop.org/patch/msgid/20230428095433.4109054-11-mika.kahola@intel.com
This commit is contained in:
committed by
Radhakrishna Sripada
parent
c0f3faaf93
commit
6f0423b06a
@@ -2893,6 +2893,25 @@ void intel_mtl_pll_disable(struct intel_encoder *encoder)
|
||||
intel_cx0pll_disable(encoder);
|
||||
}
|
||||
|
||||
enum icl_port_dpll_id
|
||||
intel_mtl_port_pll_type(struct intel_encoder *encoder,
|
||||
const struct intel_crtc_state *crtc_state)
|
||||
{
|
||||
struct drm_i915_private *i915 = to_i915(encoder->base.dev);
|
||||
/*
|
||||
* TODO: Determine the PLL type from the SW state, once MTL PLL
|
||||
* handling is done via the standard shared DPLL framework.
|
||||
*/
|
||||
u32 val = intel_de_read(i915, XELPDP_PORT_CLOCK_CTL(encoder->port));
|
||||
u32 clock = REG_FIELD_GET(XELPDP_DDI_CLOCK_SELECT_MASK, val);
|
||||
|
||||
if (clock == XELPDP_DDI_CLOCK_SELECT_MAXPCLK ||
|
||||
clock == XELPDP_DDI_CLOCK_SELECT_DIV18CLK)
|
||||
return ICL_PORT_DPLL_MG_PHY;
|
||||
else
|
||||
return ICL_PORT_DPLL_DEFAULT;
|
||||
}
|
||||
|
||||
void intel_c10pll_state_verify(struct intel_atomic_state *state,
|
||||
struct intel_crtc_state *new_crtc_state)
|
||||
{
|
||||
|
||||
@@ -16,12 +16,16 @@
|
||||
struct drm_i915_private;
|
||||
struct intel_encoder;
|
||||
struct intel_crtc_state;
|
||||
enum icl_port_dpll_id;
|
||||
enum phy;
|
||||
|
||||
bool intel_is_c10phy(struct drm_i915_private *dev_priv, enum phy phy);
|
||||
void intel_mtl_pll_enable(struct intel_encoder *encoder,
|
||||
const struct intel_crtc_state *crtc_state);
|
||||
void intel_mtl_pll_disable(struct intel_encoder *encoder);
|
||||
enum icl_port_dpll_id
|
||||
intel_mtl_port_pll_type(struct intel_encoder *encoder,
|
||||
const struct intel_crtc_state *crtc_state);
|
||||
void intel_c10pll_readout_hw_state(struct intel_encoder *encoder, struct intel_c10pll_state *pll_state);
|
||||
int intel_cx0pll_calc_state(struct intel_crtc_state *crtc_state, struct intel_encoder *encoder);
|
||||
void intel_c10pll_dump_hw_state(struct drm_i915_private *dev_priv,
|
||||
|
||||
@@ -4784,6 +4784,7 @@ void intel_ddi_init(struct drm_i915_private *dev_priv, enum port port)
|
||||
if (DISPLAY_VER(dev_priv) >= 14) {
|
||||
encoder->enable_clock = intel_mtl_pll_enable;
|
||||
encoder->disable_clock = intel_mtl_pll_disable;
|
||||
encoder->port_pll_type = intel_mtl_port_pll_type;
|
||||
encoder->get_config = mtl_ddi_get_config;
|
||||
} else if (IS_DG2(dev_priv)) {
|
||||
encoder->enable_clock = intel_mpllb_enable;
|
||||
|
||||
@@ -1756,7 +1756,7 @@ bool intel_phy_is_tc(struct drm_i915_private *dev_priv, enum phy phy)
|
||||
if (IS_DG2(dev_priv))
|
||||
/* DG2's "TC1" output uses a SNPS PHY */
|
||||
return false;
|
||||
else if (IS_ALDERLAKE_P(dev_priv))
|
||||
else if (IS_ALDERLAKE_P(dev_priv) || IS_METEORLAKE(dev_priv))
|
||||
return phy >= PHY_F && phy <= PHY_I;
|
||||
else if (IS_TIGERLAKE(dev_priv))
|
||||
return phy >= PHY_D && phy <= PHY_I;
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
#include "i915_drv.h"
|
||||
#include "i915_reg.h"
|
||||
#include "intel_cx0_phy_regs.h"
|
||||
#include "intel_ddi.h"
|
||||
#include "intel_de.h"
|
||||
#include "intel_display.h"
|
||||
@@ -59,6 +60,7 @@ 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_wait_for_ready(struct intel_tc_port *tc);
|
||||
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)
|
||||
@@ -141,15 +143,23 @@ bool intel_tc_port_in_legacy_mode(struct intel_digital_port *dig_port)
|
||||
*
|
||||
* 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/DP-alt, TBT mode:
|
||||
* - TCSS/TBT: block TC-cold power state for using the (direct or
|
||||
* TBT DP-IN) AUX and main lanes.
|
||||
*
|
||||
* ICL, TGL, ADLP/TBT mode:
|
||||
* - TCSS/IOM,FIA access for HPD live state
|
||||
* TGL/all modes:
|
||||
* - TCSS/IOM,FIA access for PHY ready, owned and HPD live state
|
||||
* - TCSS/PHY: block TC-cold power state for using the (direct or
|
||||
* TBT DP-IN) AUX and main lanes.
|
||||
*
|
||||
* ADLP/TBT mode:
|
||||
* - TCSS/TBT: block TC-cold power state for using the (TBT DP-IN)
|
||||
* AUX and main lanes.
|
||||
*
|
||||
* XELPDP+/all modes:
|
||||
* - TCSS/IOM,FIA access for PHY ready, owned state
|
||||
* - TCSS/PHY: block TC-cold power state for using the (direct or
|
||||
* TBT DP-IN) AUX and main lanes.
|
||||
*/
|
||||
bool intel_tc_cold_requires_aux_pw(struct intel_digital_port *dig_port)
|
||||
{
|
||||
@@ -872,6 +882,172 @@ static const struct intel_tc_phy_ops adlp_tc_phy_ops = {
|
||||
.init = adlp_tc_phy_init,
|
||||
};
|
||||
|
||||
/*
|
||||
* XELPDP TC PHY handlers
|
||||
* ----------------------
|
||||
*/
|
||||
static bool
|
||||
xelpdp_tc_phy_tcss_power_is_enabled(struct intel_tc_port *tc)
|
||||
{
|
||||
struct drm_i915_private *i915 = tc_to_i915(tc);
|
||||
enum port port = tc->dig_port->base.port;
|
||||
|
||||
assert_tc_cold_blocked(tc);
|
||||
|
||||
return intel_de_read(i915, XELPDP_PORT_BUF_CTL1(port)) & XELPDP_TCSS_POWER_STATE;
|
||||
}
|
||||
|
||||
static bool
|
||||
xelpdp_tc_phy_wait_for_tcss_power(struct intel_tc_port *tc, bool enabled)
|
||||
{
|
||||
struct drm_i915_private *i915 = tc_to_i915(tc);
|
||||
|
||||
if (wait_for(xelpdp_tc_phy_tcss_power_is_enabled(tc) == enabled, 5)) {
|
||||
drm_dbg_kms(&i915->drm,
|
||||
"Port %s: timeout waiting for TCSS power to get %s\n",
|
||||
enabled ? "enabled" : "disabled",
|
||||
tc->port_name);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void __xelpdp_tc_phy_enable_tcss_power(struct intel_tc_port *tc, bool enable)
|
||||
{
|
||||
struct drm_i915_private *i915 = tc_to_i915(tc);
|
||||
enum port port = tc->dig_port->base.port;
|
||||
u32 val;
|
||||
|
||||
assert_tc_cold_blocked(tc);
|
||||
|
||||
val = intel_de_read(i915, XELPDP_PORT_BUF_CTL1(port));
|
||||
if (enable)
|
||||
val |= XELPDP_TCSS_POWER_REQUEST;
|
||||
else
|
||||
val &= ~XELPDP_TCSS_POWER_REQUEST;
|
||||
intel_de_write(i915, XELPDP_PORT_BUF_CTL1(port), val);
|
||||
}
|
||||
|
||||
static bool xelpdp_tc_phy_enable_tcss_power(struct intel_tc_port *tc, bool enable)
|
||||
{
|
||||
struct drm_i915_private *i915 = tc_to_i915(tc);
|
||||
|
||||
__xelpdp_tc_phy_enable_tcss_power(tc, enable);
|
||||
|
||||
if ((!tc_phy_wait_for_ready(tc) ||
|
||||
!xelpdp_tc_phy_wait_for_tcss_power(tc, enable)) &&
|
||||
!drm_WARN_ON(&i915->drm, tc->mode == TC_PORT_LEGACY)) {
|
||||
if (enable) {
|
||||
__xelpdp_tc_phy_enable_tcss_power(tc, false);
|
||||
xelpdp_tc_phy_wait_for_tcss_power(tc, false);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void xelpdp_tc_phy_take_ownership(struct intel_tc_port *tc, bool take)
|
||||
{
|
||||
struct drm_i915_private *i915 = tc_to_i915(tc);
|
||||
enum port port = tc->dig_port->base.port;
|
||||
u32 val;
|
||||
|
||||
assert_tc_cold_blocked(tc);
|
||||
|
||||
val = intel_de_read(i915, XELPDP_PORT_BUF_CTL1(port));
|
||||
if (take)
|
||||
val |= XELPDP_TC_PHY_OWNERSHIP;
|
||||
else
|
||||
val &= ~XELPDP_TC_PHY_OWNERSHIP;
|
||||
intel_de_write(i915, XELPDP_PORT_BUF_CTL1(port), val);
|
||||
}
|
||||
|
||||
static bool xelpdp_tc_phy_is_owned(struct intel_tc_port *tc)
|
||||
{
|
||||
struct drm_i915_private *i915 = tc_to_i915(tc);
|
||||
enum port port = tc->dig_port->base.port;
|
||||
|
||||
assert_tc_cold_blocked(tc);
|
||||
|
||||
return intel_de_read(i915, XELPDP_PORT_BUF_CTL1(port)) & XELPDP_TC_PHY_OWNERSHIP;
|
||||
}
|
||||
|
||||
static void xelpdp_tc_phy_get_hw_state(struct intel_tc_port *tc)
|
||||
{
|
||||
struct drm_i915_private *i915 = tc_to_i915(tc);
|
||||
intel_wakeref_t tc_cold_wref;
|
||||
enum intel_display_power_domain 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);
|
||||
|
||||
drm_WARN_ON(&i915->drm,
|
||||
(tc->mode == TC_PORT_DP_ALT || tc->mode == TC_PORT_LEGACY) &&
|
||||
!xelpdp_tc_phy_tcss_power_is_enabled(tc));
|
||||
|
||||
__tc_cold_unblock(tc, domain, tc_cold_wref);
|
||||
}
|
||||
|
||||
static bool xelpdp_tc_phy_connect(struct intel_tc_port *tc, int required_lanes)
|
||||
{
|
||||
tc->lock_wakeref = tc_cold_block(tc);
|
||||
|
||||
if (tc->mode == TC_PORT_TBT_ALT)
|
||||
return true;
|
||||
|
||||
if (!xelpdp_tc_phy_enable_tcss_power(tc, true))
|
||||
goto out_unblock_tccold;
|
||||
|
||||
xelpdp_tc_phy_take_ownership(tc, true);
|
||||
|
||||
if (!tc_phy_verify_legacy_or_dp_alt_mode(tc, required_lanes))
|
||||
goto out_release_phy;
|
||||
|
||||
return true;
|
||||
|
||||
out_release_phy:
|
||||
xelpdp_tc_phy_take_ownership(tc, false);
|
||||
xelpdp_tc_phy_wait_for_tcss_power(tc, false);
|
||||
|
||||
out_unblock_tccold:
|
||||
tc_cold_unblock(tc, fetch_and_zero(&tc->lock_wakeref));
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void xelpdp_tc_phy_disconnect(struct intel_tc_port *tc)
|
||||
{
|
||||
switch (tc->mode) {
|
||||
case TC_PORT_LEGACY:
|
||||
case TC_PORT_DP_ALT:
|
||||
xelpdp_tc_phy_take_ownership(tc, false);
|
||||
xelpdp_tc_phy_enable_tcss_power(tc, false);
|
||||
fallthrough;
|
||||
case TC_PORT_TBT_ALT:
|
||||
tc_cold_unblock(tc, fetch_and_zero(&tc->lock_wakeref));
|
||||
break;
|
||||
default:
|
||||
MISSING_CASE(tc->mode);
|
||||
}
|
||||
}
|
||||
|
||||
static const struct intel_tc_phy_ops xelpdp_tc_phy_ops = {
|
||||
.cold_off_domain = tgl_tc_phy_cold_off_domain,
|
||||
.hpd_live_status = adlp_tc_phy_hpd_live_status,
|
||||
.is_ready = adlp_tc_phy_is_ready,
|
||||
.is_owned = xelpdp_tc_phy_is_owned,
|
||||
.get_hw_state = xelpdp_tc_phy_get_hw_state,
|
||||
.connect = xelpdp_tc_phy_connect,
|
||||
.disconnect = xelpdp_tc_phy_disconnect,
|
||||
.init = adlp_tc_phy_init,
|
||||
};
|
||||
|
||||
/*
|
||||
* Generic TC PHY handlers
|
||||
* -----------------------
|
||||
@@ -945,13 +1121,18 @@ static bool tc_phy_is_connected(struct intel_tc_port *tc,
|
||||
return is_connected;
|
||||
}
|
||||
|
||||
static void tc_phy_wait_for_ready(struct intel_tc_port *tc)
|
||||
static bool tc_phy_wait_for_ready(struct intel_tc_port *tc)
|
||||
{
|
||||
struct drm_i915_private *i915 = tc_to_i915(tc);
|
||||
|
||||
if (wait_for(tc_phy_is_ready(tc), 100))
|
||||
if (wait_for(tc_phy_is_ready(tc), 500)) {
|
||||
drm_err(&i915->drm, "Port %s: timeout waiting for PHY ready\n",
|
||||
tc->port_name);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static enum tc_port_mode
|
||||
@@ -1442,7 +1623,9 @@ 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)
|
||||
if (DISPLAY_VER(i915) >= 14)
|
||||
tc->phy_ops = &xelpdp_tc_phy_ops;
|
||||
else 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;
|
||||
|
||||
Reference in New Issue
Block a user