From: José Roberto de Souza <jose.souza@xxxxxxxxx> ADL-P have basically the same TC connection and disconnection sequences as ICL and TGL, the major difference is the new registers. So here adding functions without the icl prefix in the name and making the new functions call the platform specific function to access the correct register. v2: - Retain DDI TC PHY ownership flag during modesetting. BSpec: 55480 Cc: Imre Deak <imre.deak@xxxxxxxxx> Signed-off-by: José Roberto de Souza <jose.souza@xxxxxxxxx> Signed-off-by: Clinton Taylor <Clinton.A.Taylor@xxxxxxxxx> Signed-off-by: Matt Roper <matthew.d.roper@xxxxxxxxx> --- drivers/gpu/drm/i915/display/intel_ddi.c | 6 ++ drivers/gpu/drm/i915/display/intel_tc.c | 132 +++++++++++++++++++++-- drivers/gpu/drm/i915/i915_reg.h | 10 ++ 3 files changed, 137 insertions(+), 11 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_ddi.c b/drivers/gpu/drm/i915/display/intel_ddi.c index eccbdd42d223..38a4f251b9c9 100644 --- a/drivers/gpu/drm/i915/display/intel_ddi.c +++ b/drivers/gpu/drm/i915/display/intel_ddi.c @@ -252,12 +252,18 @@ static u32 icl_pll_to_ddi_clk_sel(struct intel_encoder *encoder, static void intel_ddi_init_dp_buf_reg(struct intel_encoder *encoder, const struct intel_crtc_state *crtc_state) { + struct drm_i915_private *i915 = to_i915(encoder->base.dev); struct intel_dp *intel_dp = enc_to_intel_dp(encoder); struct intel_digital_port *dig_port = enc_to_dig_port(encoder); + enum phy phy = intel_port_to_phy(i915, encoder->port); intel_dp->DP = dig_port->saved_port_bits | DDI_BUF_CTL_ENABLE | DDI_BUF_TRANS_SELECT(0); intel_dp->DP |= DDI_PORT_WIDTH(crtc_state->lane_count); + + if (IS_ALDERLAKE_P(i915) && + intel_phy_is_tc(i915, phy) && dig_port->tc_mode != TC_PORT_TBT_ALT) + intel_dp->DP |= DDI_BUF_CTL_TC_PHY_OWNERSHIP; } static int icl_calc_tbt_pll_link(struct drm_i915_private *dev_priv, diff --git a/drivers/gpu/drm/i915/display/intel_tc.c b/drivers/gpu/drm/i915/display/intel_tc.c index 1b108dea5fed..718e38c46dc0 100644 --- a/drivers/gpu/drm/i915/display/intel_tc.c +++ b/drivers/gpu/drm/i915/display/intel_tc.c @@ -205,7 +205,7 @@ static void tc_port_fixup_legacy_flag(struct intel_digital_port *dig_port, dig_port->tc_legacy_port = !dig_port->tc_legacy_port; } -static u32 tc_port_live_status_mask(struct intel_digital_port *dig_port) +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); struct intel_uncore *uncore = &i915->uncore; @@ -238,6 +238,40 @@ static u32 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->hotplug.pch_hpd[dig_port->base.hpd_pin]; + struct intel_uncore *uncore = &i915->uncore; + u32 val, mask = 0; + + val = intel_uncore_read(uncore, 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_uncore_read(uncore, 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); +} + static bool icl_tc_phy_status_complete(struct intel_digital_port *dig_port) { struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev); @@ -256,6 +290,33 @@ 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); } +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); + struct intel_uncore *uncore = &i915->uncore; + u32 val; + + val = intel_uncore_read(uncore, TCSS_DDI_STATUS(dig_port->tc_phy_fia_idx)); + 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) { @@ -280,7 +341,7 @@ static bool icl_tc_phy_take_ownership(struct intel_digital_port *dig_port, intel_uncore_write(uncore, PORT_TX_DFLEXDPCSSS(dig_port->tc_phy_fia), val); - if (!take && wait_for(!icl_tc_phy_status_complete(dig_port), 10)) + if (!take && wait_for(!tc_phy_status_complete(dig_port), 10)) drm_dbg_kms(&i915->drm, "Port %s: PHY complete clear timed out\n", dig_port->tc_port_name); @@ -288,6 +349,34 @@ 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); + struct intel_uncore *uncore = &i915->uncore; + enum port port = dig_port->base.port; + u32 val; + + val = intel_uncore_read(uncore, DDI_BUF_CTL(port)); + if (take) + val |= DDI_BUF_CTL_TC_PHY_OWNERSHIP; + else + val &= ~DDI_BUF_CTL_TC_PHY_OWNERSHIP; + intel_uncore_write(uncore, DDI_BUF_CTL(port), val); + + 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); @@ -306,6 +395,27 @@ 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); + struct intel_uncore *uncore = &i915->uncore; + enum port port = dig_port->base.port; + u32 val; + + val = intel_uncore_read(uncore, 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 @@ -323,13 +433,13 @@ static void icl_tc_phy_connect(struct intel_digital_port *dig_port, struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev); int max_lanes; - if (!icl_tc_phy_status_complete(dig_port)) { + if (!tc_phy_status_complete(dig_port)) { drm_dbg_kms(&i915->drm, "Port %s: PHY not ready\n", dig_port->tc_port_name); goto out_set_tbt_alt_mode; } - if (!icl_tc_phy_take_ownership(dig_port, true) && + if (!tc_phy_take_ownership(dig_port, true) && !drm_WARN_ON(&i915->drm, dig_port->tc_legacy_port)) goto out_set_tbt_alt_mode; @@ -364,7 +474,7 @@ static void icl_tc_phy_connect(struct intel_digital_port *dig_port, return; out_release_phy: - icl_tc_phy_take_ownership(dig_port, false); + tc_phy_take_ownership(dig_port, false); out_set_tbt_alt_mode: dig_port->tc_mode = TC_PORT_TBT_ALT; } @@ -380,7 +490,7 @@ static void icl_tc_phy_disconnect(struct intel_digital_port *dig_port) /* Nothing to do, we never disconnect from legacy mode */ break; case TC_PORT_DP_ALT: - icl_tc_phy_take_ownership(dig_port, false); + tc_phy_take_ownership(dig_port, false); dig_port->tc_mode = TC_PORT_TBT_ALT; break; case TC_PORT_TBT_ALT: @@ -395,13 +505,13 @@ 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); - if (!icl_tc_phy_status_complete(dig_port)) { + 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; } - if (!icl_tc_phy_is_owned(dig_port)) { + if (!tc_phy_is_owned(dig_port)) { drm_dbg_kms(&i915->drm, "Port %s: PHY not owned\n", dig_port->tc_port_name); @@ -419,8 +529,8 @@ 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; - if (!icl_tc_phy_is_owned(dig_port) || - drm_WARN_ON(&i915->drm, !icl_tc_phy_status_complete(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; mode = dig_port->tc_legacy_port ? TC_PORT_LEGACY : TC_PORT_DP_ALT; @@ -442,7 +552,7 @@ intel_tc_port_get_target_mode(struct intel_digital_port *dig_port) if (live_status_mask) return fls(live_status_mask) - 1; - return icl_tc_phy_status_complete(dig_port) && + return tc_phy_status_complete(dig_port) && dig_port->tc_legacy_port ? TC_PORT_LEGACY : TC_PORT_TBT_ALT; } diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index ab6ffe036841..65af0d84d75b 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -10160,6 +10160,7 @@ enum skl_power_gate { #define DDI_BUF_EMP_MASK (0xf << 24) #define DDI_BUF_PORT_REVERSAL (1 << 16) #define DDI_BUF_IS_IDLE (1 << 7) +#define DDI_BUF_CTL_TC_PHY_OWNERSHIP REG_BIT(6) #define DDI_A_4_LANES (1 << 4) #define DDI_PORT_WIDTH(width) (((width) - 1) << 1) #define DDI_PORT_WIDTH_MASK (7 << 1) @@ -12596,6 +12597,15 @@ enum skl_power_gate { #define DP_PIN_ASSIGNMENT_MASK(idx) (0xf << ((idx) * 4)) #define DP_PIN_ASSIGNMENT(idx, x) ((x) << ((idx) * 4)) +#define _TCSS_DDI_STATUS_1 0x161500 +#define _TCSS_DDI_STATUS_2 0x161504 +#define TCSS_DDI_STATUS(tc) _MMIO(_PICK_EVEN(tc, \ + _TCSS_DDI_STATUS_1, \ + _TCSS_DDI_STATUS_2)) +#define TCSS_DDI_STATUS_READY REG_BIT(2) +#define TCSS_DDI_STATUS_HPD_LIVE_STATUS_TBT REG_BIT(1) +#define TCSS_DDI_STATUS_HPD_LIVE_STATUS_ALT REG_BIT(0) + /* This register controls the Display State Buffer (DSB) engines. */ #define _DSBSL_INSTANCE_BASE 0x70B00 #define DSBSL_INSTANCE(pipe, id) (_DSBSL_INSTANCE_BASE + \ -- 2.25.4 _______________________________________________ Intel-gfx mailing list Intel-gfx@xxxxxxxxxxxxxxxxxxxxx https://lists.freedesktop.org/mailman/listinfo/intel-gfx