Message ID | 20220929131747.2592092-4-mika.kahola@intel.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | drm/i915/mtl: Add C10 phy support | expand |
On Thu, 29 Sep 2022, Mika Kahola <mika.kahola@intel.com> wrote: > From: Radhakrishna Sripada <radhakrishna.sripada@intel.com> > > Add sequences for C10 phy enable/disable phy lane reset, > powerdown change sequence and phy lane programming. > > Bspec: 64539, 67636, 65451, 65450, 64568 > > Cc: Imre Deak <imre.deak@intel.com> > Cc: Mika Kahola <mika.kahola@intel.com> > Cc: Uma Shankar <uma.shankar@intel.com> > Signed-off-by: Radhakrishna Sripada <radhakrishna.sripada@intel.com> > Signed-off-by: Mika Kahola <mika.kahola@intel.com> (v9) > --- > drivers/gpu/drm/i915/Makefile | 1 + > drivers/gpu/drm/i915/display/intel_cx0_phy.c | 352 ++++++++++++++++++- > drivers/gpu/drm/i915/display/intel_cx0_phy.h | 17 + > drivers/gpu/drm/i915/display/intel_ddi.c | 2 + > drivers/gpu/drm/i915/display/intel_dp.c | 15 +- > drivers/gpu/drm/i915/display/intel_dpll.c | 2 + > drivers/gpu/drm/i915/i915_reg.h | 141 ++++++++ > 7 files changed, 526 insertions(+), 4 deletions(-) > > diff --git a/drivers/gpu/drm/i915/Makefile b/drivers/gpu/drm/i915/Makefile > index a26edcdadc21..994f87a12782 100644 > --- a/drivers/gpu/drm/i915/Makefile > +++ b/drivers/gpu/drm/i915/Makefile > @@ -279,6 +279,7 @@ i915-y += \ > display/icl_dsi.o \ > display/intel_backlight.o \ > display/intel_crt.o \ > + display/intel_cx0_phy.o \ This belongs where intel_cx0_phy.c is added. > display/intel_ddi.o \ > display/intel_ddi_buf_trans.o \ > display/intel_display_trace.o \ > diff --git a/drivers/gpu/drm/i915/display/intel_cx0_phy.c b/drivers/gpu/drm/i915/display/intel_cx0_phy.c > index 2f401116d1d0..6ba11cd7cd75 100644 > --- a/drivers/gpu/drm/i915/display/intel_cx0_phy.c > +++ b/drivers/gpu/drm/i915/display/intel_cx0_phy.c > @@ -526,9 +526,9 @@ void intel_c10mpllb_readout_hw_state(struct intel_encoder *encoder, > tx0, cmn, phy_name(phy)); > } > > -__maybe_unused static void intel_c10_pll_program(struct drm_i915_private *i915, > - const struct intel_crtc_state *crtc_state, > - struct intel_encoder *encoder) > +static void intel_c10_pll_program(struct drm_i915_private *i915, > + const struct intel_crtc_state *crtc_state, > + struct intel_encoder *encoder) > { > const struct intel_c10mpllb_state *pll_state = &crtc_state->c10mpllb_state; > struct intel_digital_port *dig_port = enc_to_dig_port(encoder); > @@ -633,6 +633,352 @@ int intel_c10mpllb_calc_port_clock(struct intel_encoder *encoder, > 10 << (tx_clk_div + 16)); > } > > +#define PHY_LANES_VAL_ARG(FIELD, lanes, arg) ({u32 __val; switch(lanes) {\ > + case INTEL_CX0_BOTH_LANES: \ > + __val = ((XELPDP_LANE0_##FIELD(arg)) |\ > + (XELPDP_LANE1_##FIELD(arg))); \ > + break; \ > + case INTEL_CX0_LANE0: \ > + __val = (XELPDP_LANE0_##FIELD(arg));\ > + break; \ > + case INTEL_CX0_LANE1: \ > + __val = (XELPDP_LANE1_##FIELD(arg));\ > + break; \ > + }; __val; }) > + > +#define PHY_LANES_VAL(FIELD, lanes) ({u32 __val; switch(lanes) {\ > + case INTEL_CX0_BOTH_LANES: \ > + __val = (XELPDP_LANE0_##FIELD | \ > + XELPDP_LANE1_##FIELD); \ > + break; \ > + case INTEL_CX0_LANE0: \ > + __val = (XELPDP_LANE0_##FIELD); \ > + break; \ > + case INTEL_CX0_LANE1: \ > + __val = (XELPDP_LANE1_##FIELD);\ > + break; \ > + }; __val; }) Ugh that's ugly. I'll try to look the other way. > + > +static void intel_program_port_clock_ctl(struct intel_encoder *encoder, > + const struct intel_crtc_state *crtc_state, > + bool lane_reversal) > +{ > + struct drm_i915_private *i915 = to_i915(encoder->base.dev); > + struct intel_dp *intel_dp; > + bool ssc_enabled; > + u32 val = 0; > + > + intel_de_rmw(i915, XELPDP_PORT_BUF_CTL1(encoder->port), XELPDP_PORT_REVERSAL, > + lane_reversal ? XELPDP_PORT_REVERSAL : 0); > + > + if (lane_reversal) > + val |= XELPDP_LANE1_PHY_CLOCK_SELECT; > + > + val |= XELPDP_FORWARD_CLOCK_UNGATE; > + val |= XELPDP_DDI_CLOCK_SELECT(XELPDP_DDI_CLOCK_SELECT_MAXPCLK); > + > + if (intel_crtc_has_dp_encoder(crtc_state)) { > + intel_dp = enc_to_intel_dp(encoder); > + ssc_enabled = intel_dp->dpcd[DP_MAX_DOWNSPREAD] & > + DP_MAX_DOWNSPREAD_0_5; It is almost certainly the wrong thing to do to look at sink DPCD register values at the low level PHY code. Smells like something that should be added to crtc state. > + > + /* TODO: DP2.0 10G and 20G rates enable MPLLA*/ > + val |= ssc_enabled ? XELPDP_SSC_ENABLE_PLLB : 0; > + } > + intel_de_rmw(i915, XELPDP_PORT_CLOCK_CTL(encoder->port), > + XELPDP_LANE1_PHY_CLOCK_SELECT | > + XELPDP_FORWARD_CLOCK_UNGATE | > + XELPDP_DDI_CLOCK_SELECT_MASK | > + XELPDP_SSC_ENABLE_PLLB, val); > +} > + > +static void intel_cx0_powerdown_change_sequence(struct drm_i915_private *i915, > + enum port port, > + enum intel_cx0_lanes lane, u8 state) > +{ > + enum phy phy = intel_port_to_phy(i915, port); > + > + intel_de_rmw(i915, XELPDP_PORT_BUF_CTL2(port), > + PHY_LANES_VAL(POWERDOWN_NEW_STATE_MASK, lane), > + PHY_LANES_VAL_ARG(POWERDOWN_NEW_STATE, lane, state)); > + intel_de_rmw(i915, XELPDP_PORT_BUF_CTL2(port), > + PHY_LANES_VAL(POWERDOWN_UPDATE, lane), > + PHY_LANES_VAL(POWERDOWN_UPDATE, lane)); > + > + /* Update Timeout Value */ > + if (__intel_wait_for_register(&i915->uncore, XELPDP_PORT_BUF_CTL2(port), > + PHY_LANES_VAL(POWERDOWN_UPDATE, lane), 0, > + XELPDP_PORT_RESET_START_TIMEOUT_US, 0, NULL)) > + drm_warn(&i915->drm, "PHY %c failed to bring out of Lane reset after %dus.\n", > + phy_name(phy), XELPDP_PORT_RESET_START_TIMEOUT_US); > +} > + > +static void intel_cx0_setup_powerdown(struct drm_i915_private *i915, enum port port) > +{ > + intel_de_rmw(i915, XELPDP_PORT_BUF_CTL2(port), > + XELPDP_POWER_STATE_READY_MASK, > + XELPDP_POWER_STATE_READY(CX0_P2_STATE_READY)); > + intel_de_rmw(i915, XELPDP_PORT_BUF_CTL3(port), > + XELPDP_POWER_STATE_ACTIVE_MASK | > + XELPDP_PLL_LANE_STAGGERING_DELAY_MASK, > + XELPDP_POWER_STATE_ACTIVE(CX0_P0_STATE_ACTIVE) | > + XELPDP_PLL_LANE_STAGGERING_DELAY(0)); > +} > + > +/* FIXME: Some Type-C cases need not reset both the lanes. Handle those cases. */ > +static void intel_cx0_phy_lane_reset(struct drm_i915_private *i915, enum port port, > + bool lane_reversal) > +{ > + enum phy phy = intel_port_to_phy(i915, port); > + enum intel_cx0_lanes lane = lane_reversal ? INTEL_CX0_LANE1 : > + INTEL_CX0_LANE0; > + > + if (__intel_wait_for_register(&i915->uncore, XELPDP_PORT_BUF_CTL1(port), > + XELPDP_PORT_BUF_SOC_PHY_READY, > + XELPDP_PORT_BUF_SOC_PHY_READY, > + XELPDP_PORT_BUF_SOC_READY_TIMEOUT_US, 0, NULL)) > + drm_warn(&i915->drm, "PHY %c failed to bring out of SOC reset after %dus.\n", > + phy_name(phy), XELPDP_PORT_BUF_SOC_READY_TIMEOUT_US); > + > + intel_de_rmw(i915, XELPDP_PORT_BUF_CTL2(port), > + PHY_LANES_VAL(PIPE_RESET, INTEL_CX0_BOTH_LANES), > + PHY_LANES_VAL(PIPE_RESET, INTEL_CX0_BOTH_LANES)); > + > + if (__intel_wait_for_register(&i915->uncore, XELPDP_PORT_BUF_CTL2(port), > + PHY_LANES_VAL(PHY_CURRENT_STATUS, INTEL_CX0_BOTH_LANES), > + PHY_LANES_VAL(PHY_CURRENT_STATUS, INTEL_CX0_BOTH_LANES), > + XELPDP_PORT_RESET_START_TIMEOUT_US, 0, NULL)) > + drm_warn(&i915->drm, "PHY %c failed to bring out of Lane reset after %dus.\n", > + phy_name(phy), XELPDP_PORT_RESET_START_TIMEOUT_US); > + > + intel_de_rmw(i915, XELPDP_PORT_CLOCK_CTL(port), > + PHY_LANES_VAL(PCLK_REFCLK_REQUEST, lane), > + PHY_LANES_VAL(PCLK_REFCLK_REQUEST, lane)); > + > + if (__intel_wait_for_register(&i915->uncore, XELPDP_PORT_CLOCK_CTL(port), > + PHY_LANES_VAL(PCLK_REFCLK_ACK, lane), > + PHY_LANES_VAL(PCLK_REFCLK_ACK, lane), > + XELPDP_REFCLK_ENABLE_TIMEOUT_US, 0, NULL)) > + drm_warn(&i915->drm, "PHY %c failed to request refclk after %dus.\n", > + phy_name(phy), XELPDP_REFCLK_ENABLE_TIMEOUT_US); > + > + intel_cx0_powerdown_change_sequence(i915, port, INTEL_CX0_BOTH_LANES, > + CX0_P2_STATE_RESET); > + intel_cx0_setup_powerdown(i915, port); > + > + intel_de_rmw(i915, XELPDP_PORT_BUF_CTL2(port), > + PHY_LANES_VAL(PIPE_RESET, INTEL_CX0_BOTH_LANES), 0); > + > + if (intel_de_wait_for_clear(i915, XELPDP_PORT_BUF_CTL2(port), > + PHY_LANES_VAL(PHY_CURRENT_STATUS, > + INTEL_CX0_BOTH_LANES), > + XELPDP_PORT_RESET_END_TIMEOUT)) > + drm_warn(&i915->drm, "PHY %c failed to bring out of Lane reset after %dms.\n", > + phy_name(phy), XELPDP_PORT_RESET_END_TIMEOUT); > +} > + > +static void intel_c10_program_phy_lane(struct drm_i915_private *i915, > + enum port port, int lane_count, > + bool lane_reversal) > +{ > + u8 l0t1, l0t2, l1t1, l1t2; > + > + intel_cx0_rmw(i915, port, INTEL_CX0_BOTH_LANES, PHY_C10_VDR_CONTROL(1), > + C10_VDR_CTRL_MSGBUS_ACCESS, C10_VDR_CTRL_MSGBUS_ACCESS, > + MB_WRITE_COMMITTED); > + > + l0t1 = intel_cx0_read(i915, port, 0, PHY_CX0_TX_CONTROL(1, 2)); > + l0t2 = intel_cx0_read(i915, port, 0, PHY_CX0_TX_CONTROL(2, 2)); > + l1t1 = intel_cx0_read(i915, port, 1, PHY_CX0_TX_CONTROL(1, 2)); > + l1t2 = intel_cx0_read(i915, port, 1, PHY_CX0_TX_CONTROL(2, 2)); > + > + if (lane_reversal) { > + switch (lane_count) { > + case 1: > + /* Disable MLs 1(lane0), 2(lane0), 3(lane1) */ > + intel_cx0_write(i915, port, 1, PHY_CX0_TX_CONTROL(1, 2), > + l1t1 | CONTROL2_DISABLE_SINGLE_TX, > + MB_WRITE_COMMITTED); > + fallthrough; > + case 2: > + /* Disable MLs 1(lane0), 2(lane0) */ > + intel_cx0_write(i915, port, 0, PHY_CX0_TX_CONTROL(2, 2), > + l0t2 | CONTROL2_DISABLE_SINGLE_TX, > + MB_WRITE_COMMITTED); > + fallthrough; > + case 3: > + /* Disable MLs 1(lane0) */ > + intel_cx0_write(i915, port, 0, PHY_CX0_TX_CONTROL(1, 2), > + l0t1 | CONTROL2_DISABLE_SINGLE_TX, > + MB_WRITE_COMMITTED); > + break; > + } > + } else { > + switch (lane_count) { > + case 1: > + /* Disable MLs 2(lane0), 3(lane1), 4(lane1) */ > + intel_cx0_write(i915, port, 0, PHY_CX0_TX_CONTROL(2, 2), > + l0t2 | CONTROL2_DISABLE_SINGLE_TX, > + MB_WRITE_COMMITTED); > + fallthrough; > + case 2: > + /* Disable MLs 3(lane1), 4(lane1) */ > + intel_cx0_write(i915, port, 1, PHY_CX0_TX_CONTROL(1, 2), > + l1t1 | CONTROL2_DISABLE_SINGLE_TX, > + MB_WRITE_COMMITTED); > + fallthrough; > + case 3: > + /* Disable MLs 4(lane1) */ > + intel_cx0_write(i915, port, 1, PHY_CX0_TX_CONTROL(2, 2), > + l1t2 | CONTROL2_DISABLE_SINGLE_TX, > + MB_WRITE_COMMITTED); > + break; > + } > + } > + > + intel_cx0_rmw(i915, port, INTEL_CX0_BOTH_LANES, PHY_C10_VDR_CONTROL(1), > + C10_VDR_CTRL_UPDATE_CFG, C10_VDR_CTRL_UPDATE_CFG, MB_WRITE_COMMITTED); > +} > + > +static void intel_c10pll_enable(struct intel_encoder *encoder, > + const struct intel_crtc_state *crtc_state) > +{ > + struct drm_i915_private *i915 = to_i915(encoder->base.dev); > + enum phy phy = intel_port_to_phy(i915, encoder->port); > + struct intel_digital_port *dig_port = enc_to_dig_port(encoder); > + bool lane_reversal = dig_port->saved_port_bits & DDI_BUF_PORT_REVERSAL; > + enum intel_cx0_lanes maxpclk_lane = lane_reversal ? INTEL_CX0_LANE1 : > + INTEL_CX0_LANE0; > + > + /* > + * 1. Program PORT_CLOCK_CTL REGISTER to configure > + * clock muxes, gating and SSC > + */ > + intel_program_port_clock_ctl(encoder, crtc_state, lane_reversal); > + > + /* 2. Bring PHY out of reset. */ > + intel_cx0_phy_lane_reset(i915, encoder->port, lane_reversal); > + > + /* > + * 3. Change Phy power state to Ready. > + * TODO: For DP alt mode use only one lane. > + */ > + intel_cx0_powerdown_change_sequence(i915, encoder->port, INTEL_CX0_BOTH_LANES, > + CX0_P2_STATE_READY); > + > + /* 4. Program PHY internal PLL internal registers. */ > + intel_c10_pll_program(i915, crtc_state, encoder); > + > + /* > + * 5. Program the enabled and disabled owned PHY lane > + * transmitters over message bus > + */ > + intel_c10_program_phy_lane(i915, encoder->port, crtc_state->lane_count, lane_reversal); > + > + /* > + * 6. Follow the Display Voltage Frequency Switching - Sequence > + * Before Frequency Change. We handle this step in bxt_set_cdclk(). > + */ > + > + /* > + * 7. Program DDI_CLK_VALFREQ to match intended DDI > + * clock frequency. > + */ > + intel_de_write(i915, DDI_CLK_VALFREQ(encoder->port), > + crtc_state->port_clock); > + /* > + * 8. Set PORT_CLOCK_CTL register PCLK PLL Request > + * LN<Lane for maxPCLK> to "1" to enable PLL. > + */ > + intel_de_rmw(i915, XELPDP_PORT_CLOCK_CTL(encoder->port), 0, > + PHY_LANES_VAL(PCLK_PLL_REQUEST, maxpclk_lane)); > + > + /* 9. Poll on PORT_CLOCK_CTL PCLK PLL Ack LN<Lane for maxPCLK> == "1". */ > + if (__intel_wait_for_register(&i915->uncore, XELPDP_PORT_CLOCK_CTL(encoder->port), > + PHY_LANES_VAL(PCLK_PLL_ACK, maxpclk_lane), > + PHY_LANES_VAL(PCLK_PLL_ACK, maxpclk_lane), > + XELPDP_PCLK_PLL_ENABLE_TIMEOUT_US, 0, NULL)) > + drm_warn(&i915->drm, "Port %c PLL not locked after %dus.\n", > + phy_name(phy), XELPDP_PCLK_PLL_ENABLE_TIMEOUT_US); > + > + /* > + * 10. Follow the Display Voltage Frequency Switching Sequence After > + * Frequency Change. We handle this step in bxt_set_cdclk(). > + */ > +} > + > +void intel_cx0pll_enable(struct intel_encoder *encoder, > + const struct intel_crtc_state *crtc_state) > +{ > + struct drm_i915_private *i915 = to_i915(encoder->base.dev); > + enum phy phy = intel_port_to_phy(i915, encoder->port); > + > + drm_WARN_ON(&i915->drm, !intel_is_c10phy(i915, phy)); > + intel_c10pll_enable(encoder, crtc_state); > +} > + > +static void intel_c10pll_disable(struct intel_encoder *encoder) > +{ > + struct drm_i915_private *i915 = to_i915(encoder->base.dev); > + enum phy phy = intel_port_to_phy(i915, encoder->port); > + struct intel_digital_port *dig_port = enc_to_dig_port(encoder); > + bool lane_reversal = dig_port->saved_port_bits & DDI_BUF_PORT_REVERSAL; > + enum intel_cx0_lanes lane = lane_reversal ? INTEL_CX0_LANE1 : > + INTEL_CX0_LANE0; > + > + /* 1. Change owned PHY lane power to Disable state. */ > + intel_cx0_powerdown_change_sequence(i915, encoder->port, INTEL_CX0_BOTH_LANES, > + CX0_P2PG_STATE_DISABLE); > + > + /* > + * 2. Follow the Display Voltage Frequency Switching Sequence Before > + * Frequency Change. We handle this step in bxt_set_cdclk(). > + */ > + > + /* > + * 3. Set PORT_CLOCK_CTL register PCLK PLL Request LN<Lane for maxPCLK> > + * to "0" to disable PLL. > + */ > + intel_de_rmw(i915, XELPDP_PORT_CLOCK_CTL(encoder->port), > + PHY_LANES_VAL(PCLK_PLL_REQUEST, INTEL_CX0_BOTH_LANES) | > + PHY_LANES_VAL(PCLK_REFCLK_REQUEST, INTEL_CX0_BOTH_LANES), 0); > + > + /* 4. Program DDI_CLK_VALFREQ to 0. */ > + intel_de_write(i915, DDI_CLK_VALFREQ(encoder->port), 0); > + > + /* > + * 5. Poll on PORT_CLOCK_CTL PCLK PLL Ack LN<Lane for maxPCLK**> == "0". > + */ > + if (__intel_wait_for_register(&i915->uncore, XELPDP_PORT_CLOCK_CTL(encoder->port), > + PHY_LANES_VAL(PCLK_PLL_ACK, lane) | > + PHY_LANES_VAL(PCLK_REFCLK_ACK, lane), 0, > + XELPDP_PCLK_PLL_DISABLE_TIMEOUT_US, 0, NULL)) > + drm_warn(&i915->drm, "Port %c PLL not unlocked after %dus.\n", > + phy_name(phy), XELPDP_PCLK_PLL_DISABLE_TIMEOUT_US); > + > + /* > + * 6. Follow the Display Voltage Frequency Switching Sequence After > + * Frequency Change. We handle this step in bxt_set_cdclk(). > + */ > + > + /* 7. Program PORT_CLOCK_CTL register to disable and gate clocks. */ > + intel_de_rmw(i915, XELPDP_PORT_CLOCK_CTL(encoder->port), > + XELPDP_DDI_CLOCK_SELECT_MASK | > + XELPDP_FORWARD_CLOCK_UNGATE, 0); > +} > + > +void intel_cx0pll_disable(struct intel_encoder *encoder) > +{ > + struct drm_i915_private *i915 = to_i915(encoder->base.dev); > + enum phy phy = intel_port_to_phy(i915, encoder->port); > + > + drm_WARN_ON(&i915->drm, !intel_is_c10phy(i915, phy)); > + intel_c10pll_disable(encoder); > +} > + > +#undef PHY_LANES_VAL_ARG > +#undef PHY_LANES_VAL > + > void intel_c10mpllb_state_verify(struct intel_atomic_state *state, > struct intel_crtc_state *new_crtc_state) > { > diff --git a/drivers/gpu/drm/i915/display/intel_cx0_phy.h b/drivers/gpu/drm/i915/display/intel_cx0_phy.h > index cf1f300b6a7b..d12d2e2f02ee 100644 > --- a/drivers/gpu/drm/i915/display/intel_cx0_phy.h > +++ b/drivers/gpu/drm/i915/display/intel_cx0_phy.h > @@ -106,6 +106,19 @@ enum intel_cx0_lanes { > #define C10_VDR_CTRL_UPDATE_CFG REG_BIT8(0) > #define PHY_C10_VDR_CUSTOM_WIDTH 0xD02 > > +#define CX0_P0_STATE_ACTIVE 0x0 > +#define CX0_P2_STATE_READY 0x2 > +#define CX0_P2PG_STATE_DISABLE 0x9 > +#define CX0_P4PG_STATE_DISABLE 0xC > +#define CX0_P2_STATE_RESET 0x2 > + > +/* PHY_C10_VDR_PLL0 */ > +#define PLL_C10_MPLL_SSC_EN REG_BIT8(0) > + > +/* PIPE SPEC Defined Registers */ > +#define PHY_CX0_TX_CONTROL(tx, control) (0x400 + ((tx) - 1) * 0x200 + (control)) > +#define CONTROL2_DISABLE_SINGLE_TX REG_BIT(6) > + Again, register definitions don't belong here. > static inline bool intel_is_c10phy(struct drm_i915_private *dev_priv, enum phy phy) > { > if (!IS_METEORLAKE(dev_priv)) > @@ -114,6 +127,10 @@ static inline bool intel_is_c10phy(struct drm_i915_private *dev_priv, enum phy p > return (phy < PHY_C); > } > > +void intel_cx0pll_enable(struct intel_encoder *encoder, > + const struct intel_crtc_state *crtc_state); > +void intel_cx0pll_disable(struct intel_encoder *encoder); > + > void intel_c10mpllb_readout_hw_state(struct intel_encoder *encoder, > struct intel_c10mpllb_state *pll_state); > int intel_cx0mpllb_calc_state(struct intel_crtc_state *crtc_state, > diff --git a/drivers/gpu/drm/i915/display/intel_ddi.c b/drivers/gpu/drm/i915/display/intel_ddi.c > index aaa8846c3b18..639ec604babf 100644 > --- a/drivers/gpu/drm/i915/display/intel_ddi.c > +++ b/drivers/gpu/drm/i915/display/intel_ddi.c > @@ -4384,6 +4384,8 @@ void intel_ddi_init(struct drm_i915_private *dev_priv, enum port port) > encoder->pipe_mask = ~0; > > if (DISPLAY_VER(dev_priv) >= 14) { > + encoder->enable_clock = intel_cx0pll_enable; > + encoder->disable_clock = intel_cx0pll_disable; > encoder->get_config = mtl_ddi_get_config; > } else if (IS_DG2(dev_priv)) { > encoder->enable_clock = intel_mpllb_enable; > diff --git a/drivers/gpu/drm/i915/display/intel_dp.c b/drivers/gpu/drm/i915/display/intel_dp.c > index 70b06806ec0d..db32799b5f46 100644 > --- a/drivers/gpu/drm/i915/display/intel_dp.c > +++ b/drivers/gpu/drm/i915/display/intel_dp.c > @@ -420,6 +420,11 @@ static int ehl_max_source_rate(struct intel_dp *intel_dp) > return 810000; > } > > +static int mtl_max_source_rate(struct intel_dp *intel_dp) > +{ > + return intel_dp_is_edp(intel_dp) ? 675000 : 810000; > +} > + > static int vbt_max_link_rate(struct intel_dp *intel_dp) > { > struct intel_encoder *encoder = &dp_to_dig_port(intel_dp)->base; > @@ -444,6 +449,10 @@ static void > intel_dp_set_source_rates(struct intel_dp *intel_dp) > { > /* The values must be in increasing order */ > + static const int mtl_rates[] = { > + 162000, 216000, 243000, 270000, 324000, 432000, 540000, 675000, > + 810000, > + }; > static const int icl_rates[] = { > 162000, 216000, 270000, 324000, 432000, 540000, 648000, 810000, > 1000000, 1350000, > @@ -469,7 +478,11 @@ intel_dp_set_source_rates(struct intel_dp *intel_dp) > drm_WARN_ON(&dev_priv->drm, > intel_dp->source_rates || intel_dp->num_source_rates); > > - if (DISPLAY_VER(dev_priv) >= 11) { > + if (DISPLAY_VER(dev_priv) >= 14) { > + source_rates = mtl_rates; > + size = ARRAY_SIZE(mtl_rates); > + max_rate = mtl_max_source_rate(intel_dp); > + } else if (DISPLAY_VER(dev_priv) >= 11) { > source_rates = icl_rates; > size = ARRAY_SIZE(icl_rates); > if (IS_DG2(dev_priv)) All of the changes to intel_dp.c should be a separate patch. > diff --git a/drivers/gpu/drm/i915/display/intel_dpll.c b/drivers/gpu/drm/i915/display/intel_dpll.c > index 73f541050913..d6fcdf4eba0e 100644 > --- a/drivers/gpu/drm/i915/display/intel_dpll.c > +++ b/drivers/gpu/drm/i915/display/intel_dpll.c > @@ -1533,6 +1533,8 @@ intel_dpll_init_clock_hook(struct drm_i915_private *dev_priv) > { > if (DISPLAY_VER(dev_priv) >= 14) > dev_priv->display.funcs.dpll = &mtl_dpll_funcs; > + else if (DISPLAY_VER(dev_priv) >= 14) > + dev_priv->display.funcs.dpll = &mtl_dpll_funcs; > else if (IS_DG2(dev_priv)) > dev_priv->display.funcs.dpll = &dg2_dpll_funcs; > else if (DISPLAY_VER(dev_priv) >= 9 || HAS_DDI(dev_priv)) > diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h > index 5003a5ffbc6a..5e6ff9f2aa10 100644 > --- a/drivers/gpu/drm/i915/i915_reg.h > +++ b/drivers/gpu/drm/i915/i915_reg.h > @@ -2121,6 +2121,11 @@ > #define TRANS_PUSH_EN REG_BIT(31) > #define TRANS_PUSH_SEND REG_BIT(30) > > +/* DDI Buffer Control */ > +#define _DDI_CLK_VALFREQ_A 0x64030 > +#define _DDI_CLK_VALFREQ_B 0x64130 > +#define DDI_CLK_VALFREQ(port) _MMIO_PORT(port, _DDI_CLK_VALFREQ_A, _DDI_CLK_VALFREQ_B) > + > /* > * HSW+ eDP PSR registers > * > @@ -8375,4 +8380,140 @@ enum skl_power_gate { > > #define MTL_MEDIA_GSI_BASE 0x380000 > > +#define PUNIT_MMIO_CR_POC_STRAPS _MMIO(0x281078) > +#define NUM_TILES_MASK REG_GENMASK(1, 0) > +#define CD_ALIVE REG_BIT(2) > +#define SOCKET_ID_MASK REG_GENMASK(7, 3) > + > +/* Define the BAR and offset for the accelerator fabric CSRs */ > +#define CD_BASE_OFFSET 0x291000 > +#define CD_BAR_SIZE (256 * 1024) > + > +/* > + * In general, the i915 should not touch the IAF registers. The registers > + * will be passed as an IO resource via the MFD interface. However, it > + * is necessary to put the IRQ bits in a known state, before the MFD cell > + * is registered. > + * > + * So define these registers for i915 usage. These should probably be split to a separate _regs file, like we've been doing for other registers. Especially because "In general, the i915 should not touch the IAF registers." > + */ > +#define CPORT_MBDB_CSRS (CD_BASE_OFFSET + 0x6000) > +#define CPORT_MBDB_CSRS_END (CPORT_MBDB_CSRS + 0x1000) > +#define CPORT_MBDB_INT_ENABLE_MASK _MMIO(CPORT_MBDB_CSRS + 0x8) > + > +#define _XELPDP_PORT_M2P_MSGBUS_CTL_LN0_A 0x64040 > +#define _XELPDP_PORT_M2P_MSGBUS_CTL_LN0_B 0x64140 > +#define _XELPDP_PORT_M2P_MSGBUS_CTL_LN0_USBC1 0x16F240 > +#define _XELPDP_PORT_M2P_MSGBUS_CTL_LN0_USBC2 0x16F440 > +#define _XELPDP_PORT_M2P_MSGBUS_CTL_LN0_USBC3 0x16F640 > +#define _XELPDP_PORT_M2P_MSGBUS_CTL_LN0_USBC4 0x16F840 > +#define _XELPDP_PORT_M2P_MSGBUS_CTL(port, lane) (_PICK(port, \ > + [PORT_A] = _XELPDP_PORT_M2P_MSGBUS_CTL_LN0_A, \ > + [PORT_B] = _XELPDP_PORT_M2P_MSGBUS_CTL_LN0_B, \ > + [PORT_TC1] = _XELPDP_PORT_M2P_MSGBUS_CTL_LN0_USBC1, \ > + [PORT_TC2] = _XELPDP_PORT_M2P_MSGBUS_CTL_LN0_USBC2, \ > + [PORT_TC3] = _XELPDP_PORT_M2P_MSGBUS_CTL_LN0_USBC3, \ > + [PORT_TC4] = _XELPDP_PORT_M2P_MSGBUS_CTL_LN0_USBC4) + ((lane) * 4)) > + > +#define XELPDP_PORT_M2P_MSGBUS_CTL(port, lane) _MMIO(_XELPDP_PORT_M2P_MSGBUS_CTL(port, lane)) > +#define XELPDP_PORT_M2P_TRANSACTION_PENDING REG_BIT(31) > +#define XELPDP_PORT_M2P_COMMAND_TYPE_MASK REG_GENMASK(30, 27) > +#define XELPDP_PORT_M2P_COMMAND_WRITE_UNCOMMITTED REG_FIELD_PREP(XELPDP_PORT_M2P_COMMAND_TYPE_MASK, 0x1) > +#define XELPDP_PORT_M2P_COMMAND_WRITE_COMMITTED REG_FIELD_PREP(XELPDP_PORT_M2P_COMMAND_TYPE_MASK, 0x2) > +#define XELPDP_PORT_M2P_COMMAND_READ REG_FIELD_PREP(XELPDP_PORT_M2P_COMMAND_TYPE_MASK, 0x3) > +#define XELPDP_PORT_M2P_DATA_MASK REG_GENMASK(23, 16) > +#define XELPDP_PORT_M2P_DATA(val) REG_FIELD_PREP(XELPDP_PORT_M2P_DATA_MASK, val) > +#define XELPDP_PORT_M2P_TRANSACTION_RESET REG_BIT(15) > +#define XELPDP_PORT_M2P_ADDRESS_MASK REG_GENMASK(11, 0) > +#define XELPDP_PORT_M2P_ADDRESS(val) REG_FIELD_PREP(XELPDP_PORT_M2P_ADDRESS_MASK, val) > + > +#define XELPDP_PORT_P2M_MSGBUS_STATUS(port, lane) _MMIO(_XELPDP_PORT_M2P_MSGBUS_CTL(port, lane) + 8) > +#define XELPDP_PORT_P2M_RESPONSE_READY REG_BIT(31) > +#define XELPDP_PORT_P2M_COMMAND_TYPE_MASK REG_GENMASK(30, 27) > +#define XELPDP_PORT_P2M_COMMAND_READ_ACK 0x4 > +#define XELPDP_PORT_P2M_COMMAND_WRITE_ACK 0x5 > +#define XELPDP_PORT_P2M_DATA_MASK REG_GENMASK(23, 16) > +#define XELPDP_PORT_P2M_DATA(val) REG_FIELD_PREP(XELPDP_PORT_P2M_DATA_MASK, val) > +#define XELPDP_PORT_P2M_ERROR_SET REG_BIT(15) > + > +#define XELPDP_MSGBUS_TIMEOUT_SLOW 1 > +#define XELPDP_MSGBUS_TIMEOUT_FAST_US 2 > +#define XELPDP_PCLK_PLL_ENABLE_TIMEOUT_US 3200 > +#define XELPDP_PCLK_PLL_DISABLE_TIMEOUT_US 20 > +#define XELPDP_PORT_BUF_SOC_READY_TIMEOUT_US 100 > +#define XELPDP_PORT_RESET_START_TIMEOUT_US 5 > +#define XELPDP_PORT_RESET_END_TIMEOUT 15 > +#define XELPDP_REFCLK_ENABLE_TIMEOUT_US 1 > + > +#define _XELPDP_PORT_BUF_CTL1_LN0_A 0x64004 > +#define _XELPDP_PORT_BUF_CTL1_LN0_B 0x64104 > +#define _XELPDP_PORT_BUF_CTL1_LN0_USBC1 0x16F200 > +#define _XELPDP_PORT_BUF_CTL1_LN0_USBC2 0x16F400 > +#define _XELPDP_PORT_BUF_CTL1_LN0_USBC3 0x16F600 > +#define _XELPDP_PORT_BUF_CTL1_LN0_USBC4 0x16F800 > +#define _XELPDP_PORT_BUF_CTL1(port) (_PICK(port, \ > + [PORT_A] = _XELPDP_PORT_BUF_CTL1_LN0_A, \ > + [PORT_B] = _XELPDP_PORT_BUF_CTL1_LN0_B, \ > + [PORT_TC1] = _XELPDP_PORT_BUF_CTL1_LN0_USBC1, \ > + [PORT_TC2] = _XELPDP_PORT_BUF_CTL1_LN0_USBC2, \ > + [PORT_TC3] = _XELPDP_PORT_BUF_CTL1_LN0_USBC3, \ > + [PORT_TC4] = _XELPDP_PORT_BUF_CTL1_LN0_USBC4)) > + > +#define XELPDP_PORT_BUF_CTL1(port) _MMIO(_XELPDP_PORT_BUF_CTL1(port)) > +#define XELPDP_PORT_BUF_SOC_PHY_READY REG_BIT(24) > +#define XELPDP_PORT_REVERSAL REG_BIT(16) > +#define XELPDP_PORT_WIDTH_MASK REG_GENMASK(3, 1) > +#define XELPDP_PORT_WIDTH(val) REG_FIELD_PREP(XELPDP_PORT_WIDTH_MASK, val) > + > +#define XELPDP_PORT_BUF_CTL2(port) _MMIO(_XELPDP_PORT_BUF_CTL1(port) + 4) > +#define XELPDP_LANE0_PIPE_RESET REG_BIT(31) > +#define XELPDP_LANE1_PIPE_RESET REG_BIT(30) > +#define XELPDP_LANE0_PHY_CURRENT_STATUS REG_BIT(29) > +#define XELPDP_LANE1_PHY_CURRENT_STATUS REG_BIT(28) > +#define XELPDP_LANE0_POWERDOWN_UPDATE REG_BIT(25) > +#define XELPDP_LANE1_POWERDOWN_UPDATE REG_BIT(24) > +#define XELPDP_LANE0_POWERDOWN_NEW_STATE_MASK REG_GENMASK(23, 20) > +#define XELPDP_LANE0_POWERDOWN_NEW_STATE(val) REG_FIELD_PREP(XELPDP_LANE0_POWERDOWN_NEW_STATE_MASK, val) > +#define XELPDP_LANE1_POWERDOWN_NEW_STATE_MASK REG_GENMASK(19, 16) > +#define XELPDP_LANE1_POWERDOWN_NEW_STATE(val) REG_FIELD_PREP(XELPDP_LANE1_POWERDOWN_NEW_STATE_MASK, val) > +#define XELPDP_POWER_STATE_READY_MASK REG_GENMASK(7, 4) > +#define XELPDP_POWER_STATE_READY(val) REG_FIELD_PREP(XELPDP_POWER_STATE_READY_MASK, val) > + > +#define XELPDP_PORT_BUF_CTL3(port) _MMIO(_XELPDP_PORT_BUF_CTL1(port) + 8) > +#define XELPDP_PLL_LANE_STAGGERING_DELAY_MASK REG_GENMASK(15, 8) > +#define XELPDP_PLL_LANE_STAGGERING_DELAY(val) REG_FIELD_PREP(XELPDP_PLL_LANE_STAGGERING_DELAY_MASK, val) > +#define XELPDP_POWER_STATE_ACTIVE_MASK REG_GENMASK(3, 0) > +#define XELPDP_POWER_STATE_ACTIVE(val) REG_FIELD_PREP(XELPDP_POWER_STATE_ACTIVE_MASK, val) > + > +#define _XELPDP_PORT_CLOCK_CTL_A 0x640E0 > +#define _XELPDP_PORT_CLOCK_CTL_B 0x641E0 > +#define _XELPDP_PORT_CLOCK_CTL_USBC1 0x16F260 > +#define _XELPDP_PORT_CLOCK_CTL_USBC2 0x16F460 > +#define _XELPDP_PORT_CLOCK_CTL_USBC3 0x16F660 > +#define _XELPDP_PORT_CLOCK_CTL_USBC4 0x16F860 > +#define XELPDP_PORT_CLOCK_CTL(port) _MMIO(_PICK(port, \ > + [PORT_A] = _XELPDP_PORT_CLOCK_CTL_A, \ > + [PORT_B] = _XELPDP_PORT_CLOCK_CTL_B, \ > + [PORT_TC1] = _XELPDP_PORT_CLOCK_CTL_USBC1, \ > + [PORT_TC2] = _XELPDP_PORT_CLOCK_CTL_USBC2, \ > + [PORT_TC3] = _XELPDP_PORT_CLOCK_CTL_USBC3, \ > + [PORT_TC4] = _XELPDP_PORT_CLOCK_CTL_USBC4)) > + > +#define XELPDP_LANE0_PCLK_PLL_REQUEST REG_BIT(31) > +#define XELPDP_LANE0_PCLK_PLL_ACK REG_BIT(30) > +#define XELPDP_LANE0_PCLK_REFCLK_REQUEST REG_BIT(29) > +#define XELPDP_LANE0_PCLK_REFCLK_ACK REG_BIT(28) > +#define XELPDP_LANE1_PCLK_PLL_REQUEST REG_BIT(27) > +#define XELPDP_LANE1_PCLK_PLL_ACK REG_BIT(26) > +#define XELPDP_LANE1_PCLK_REFCLK_REQUEST REG_BIT(25) > +#define XELPDP_LANE1_PCLK_REFCLK_ACK REG_BIT(24) > +#define XELPDP_DDI_CLOCK_SELECT_MASK REG_GENMASK(15, 12) > +#define XELPDP_DDI_CLOCK_SELECT(val) REG_FIELD_PREP(XELPDP_DDI_CLOCK_SELECT_MASK, val) > +#define XELPDP_DDI_CLOCK_SELECT_NONE 0x0 > +#define XELPDP_DDI_CLOCK_SELECT_MAXPCLK 0x8 > +#define XELPDP_FORWARD_CLOCK_UNGATE REG_BIT(10) > +#define XELPDP_LANE1_PHY_CLOCK_SELECT REG_BIT(8) > +#define XELPDP_SSC_ENABLE_PLLA REG_BIT(1) > +#define XELPDP_SSC_ENABLE_PLLB REG_BIT(0) > + > #endif /* _I915_REG_H_ */
> -----Original Message----- > From: Jani Nikula <jani.nikula@linux.intel.com> > Sent: Friday, September 30, 2022 12:32 PM > To: Kahola, Mika <mika.kahola@intel.com>; intel-gfx@lists.freedesktop.org > Subject: Re: [Intel-gfx] [PATCH 3/5] drm/i915/mtl: Add support for C10 phy > programming > > On Thu, 29 Sep 2022, Mika Kahola <mika.kahola@intel.com> wrote: > > From: Radhakrishna Sripada <radhakrishna.sripada@intel.com> > > > > Add sequences for C10 phy enable/disable phy lane reset, powerdown > > change sequence and phy lane programming. > > > > Bspec: 64539, 67636, 65451, 65450, 64568 > > > > Cc: Imre Deak <imre.deak@intel.com> > > Cc: Mika Kahola <mika.kahola@intel.com> > > Cc: Uma Shankar <uma.shankar@intel.com> > > Signed-off-by: Radhakrishna Sripada <radhakrishna.sripada@intel.com> > > Signed-off-by: Mika Kahola <mika.kahola@intel.com> (v9) > > --- > > drivers/gpu/drm/i915/Makefile | 1 + > > drivers/gpu/drm/i915/display/intel_cx0_phy.c | 352 > > ++++++++++++++++++- drivers/gpu/drm/i915/display/intel_cx0_phy.h | 17 + > > drivers/gpu/drm/i915/display/intel_ddi.c | 2 + > > drivers/gpu/drm/i915/display/intel_dp.c | 15 +- > > drivers/gpu/drm/i915/display/intel_dpll.c | 2 + > > drivers/gpu/drm/i915/i915_reg.h | 141 ++++++++ > > 7 files changed, 526 insertions(+), 4 deletions(-) > > > > diff --git a/drivers/gpu/drm/i915/Makefile > > b/drivers/gpu/drm/i915/Makefile index a26edcdadc21..994f87a12782 > > 100644 > > --- a/drivers/gpu/drm/i915/Makefile > > +++ b/drivers/gpu/drm/i915/Makefile > > @@ -279,6 +279,7 @@ i915-y += \ > > display/icl_dsi.o \ > > display/intel_backlight.o \ > > display/intel_crt.o \ > > + display/intel_cx0_phy.o \ > > This belongs where intel_cx0_phy.c is added. > > > display/intel_ddi.o \ > > display/intel_ddi_buf_trans.o \ > > display/intel_display_trace.o \ > > diff --git a/drivers/gpu/drm/i915/display/intel_cx0_phy.c > > b/drivers/gpu/drm/i915/display/intel_cx0_phy.c > > index 2f401116d1d0..6ba11cd7cd75 100644 > > --- a/drivers/gpu/drm/i915/display/intel_cx0_phy.c > > +++ b/drivers/gpu/drm/i915/display/intel_cx0_phy.c > > @@ -526,9 +526,9 @@ void intel_c10mpllb_readout_hw_state(struct > intel_encoder *encoder, > > tx0, cmn, phy_name(phy)); > > } > > > > -__maybe_unused static void intel_c10_pll_program(struct drm_i915_private > *i915, > > - const struct intel_crtc_state > *crtc_state, > > - struct intel_encoder > *encoder) > > +static void intel_c10_pll_program(struct drm_i915_private *i915, > > + const struct intel_crtc_state *crtc_state, > > + struct intel_encoder *encoder) > > { > > const struct intel_c10mpllb_state *pll_state = &crtc_state- > >c10mpllb_state; > > struct intel_digital_port *dig_port = enc_to_dig_port(encoder); @@ > > -633,6 +633,352 @@ int intel_c10mpllb_calc_port_clock(struct intel_encoder > *encoder, > > 10 << (tx_clk_div + 16)); > > } > > > > +#define PHY_LANES_VAL_ARG(FIELD, lanes, arg) ({u32 __val; > switch(lanes) {\ > > + case > INTEL_CX0_BOTH_LANES: \ > > + __val = > ((XELPDP_LANE0_##FIELD(arg)) |\ > > + > (XELPDP_LANE1_##FIELD(arg))); \ > > + break; > \ > > + case INTEL_CX0_LANE0: > \ > > + __val = > (XELPDP_LANE0_##FIELD(arg));\ > > + break; > \ > > + case INTEL_CX0_LANE1: > \ > > + __val = > (XELPDP_LANE1_##FIELD(arg));\ > > + break; \ > > + }; __val; }) > > + > > +#define PHY_LANES_VAL(FIELD, lanes) ({u32 __val; switch(lanes) {\ > > + case > INTEL_CX0_BOTH_LANES: \ > > + __val = > (XELPDP_LANE0_##FIELD | \ > > + > XELPDP_LANE1_##FIELD); \ > > + break; > \ > > + case INTEL_CX0_LANE0: > \ > > + __val = > (XELPDP_LANE0_##FIELD); \ > > + break; > \ > > + case INTEL_CX0_LANE1: > \ > > + __val = > (XELPDP_LANE1_##FIELD);\ > > + break; \ > > + }; __val; }) > > Ugh that's ugly. I'll try to look the other way. > > > + > > +static void intel_program_port_clock_ctl(struct intel_encoder *encoder, > > + const struct intel_crtc_state > *crtc_state, > > + bool lane_reversal) > > +{ > > + struct drm_i915_private *i915 = to_i915(encoder->base.dev); > > + struct intel_dp *intel_dp; > > + bool ssc_enabled; > > + u32 val = 0; > > + > > + intel_de_rmw(i915, XELPDP_PORT_BUF_CTL1(encoder->port), > XELPDP_PORT_REVERSAL, > > + lane_reversal ? XELPDP_PORT_REVERSAL : 0); > > + > > + if (lane_reversal) > > + val |= XELPDP_LANE1_PHY_CLOCK_SELECT; > > + > > + val |= XELPDP_FORWARD_CLOCK_UNGATE; > > + val |= > XELPDP_DDI_CLOCK_SELECT(XELPDP_DDI_CLOCK_SELECT_MAXPCLK); > > + > > + if (intel_crtc_has_dp_encoder(crtc_state)) { > > + intel_dp = enc_to_intel_dp(encoder); > > + ssc_enabled = intel_dp->dpcd[DP_MAX_DOWNSPREAD] & > > + DP_MAX_DOWNSPREAD_0_5; > > It is almost certainly the wrong thing to do to look at sink DPCD register values > at the low level PHY code. Smells like something that should be added to crtc > state. > > > + > > + /* TODO: DP2.0 10G and 20G rates enable MPLLA*/ > > + val |= ssc_enabled ? XELPDP_SSC_ENABLE_PLLB : 0; > > + } > > + intel_de_rmw(i915, XELPDP_PORT_CLOCK_CTL(encoder->port), > > + XELPDP_LANE1_PHY_CLOCK_SELECT | > > + XELPDP_FORWARD_CLOCK_UNGATE | > > + XELPDP_DDI_CLOCK_SELECT_MASK | > > + XELPDP_SSC_ENABLE_PLLB, val); > > +} > > + > > +static void intel_cx0_powerdown_change_sequence(struct drm_i915_private > *i915, > > + enum port port, > > + enum intel_cx0_lanes lane, u8 > state) { > > + enum phy phy = intel_port_to_phy(i915, port); > > + > > + intel_de_rmw(i915, XELPDP_PORT_BUF_CTL2(port), > > + PHY_LANES_VAL(POWERDOWN_NEW_STATE_MASK, lane), > > + PHY_LANES_VAL_ARG(POWERDOWN_NEW_STATE, lane, > state)); > > + intel_de_rmw(i915, XELPDP_PORT_BUF_CTL2(port), > > + PHY_LANES_VAL(POWERDOWN_UPDATE, lane), > > + PHY_LANES_VAL(POWERDOWN_UPDATE, lane)); > > + > > + /* Update Timeout Value */ > > + if (__intel_wait_for_register(&i915->uncore, > XELPDP_PORT_BUF_CTL2(port), > > + PHY_LANES_VAL(POWERDOWN_UPDATE, > lane), 0, > > + > XELPDP_PORT_RESET_START_TIMEOUT_US, 0, NULL)) > > + drm_warn(&i915->drm, "PHY %c failed to bring out of Lane > reset after %dus.\n", > > + phy_name(phy), > XELPDP_PORT_RESET_START_TIMEOUT_US); > > +} > > + > > +static void intel_cx0_setup_powerdown(struct drm_i915_private *i915, > > +enum port port) { > > + intel_de_rmw(i915, XELPDP_PORT_BUF_CTL2(port), > > + XELPDP_POWER_STATE_READY_MASK, > > + XELPDP_POWER_STATE_READY(CX0_P2_STATE_READY)); > > + intel_de_rmw(i915, XELPDP_PORT_BUF_CTL3(port), > > + XELPDP_POWER_STATE_ACTIVE_MASK | > > + XELPDP_PLL_LANE_STAGGERING_DELAY_MASK, > > + XELPDP_POWER_STATE_ACTIVE(CX0_P0_STATE_ACTIVE) | > > + XELPDP_PLL_LANE_STAGGERING_DELAY(0)); > > +} > > + > > +/* FIXME: Some Type-C cases need not reset both the lanes. Handle > > +those cases. */ static void intel_cx0_phy_lane_reset(struct drm_i915_private > *i915, enum port port, > > + bool lane_reversal) > > +{ > > + enum phy phy = intel_port_to_phy(i915, port); > > + enum intel_cx0_lanes lane = lane_reversal ? INTEL_CX0_LANE1 : > > + INTEL_CX0_LANE0; > > + > > + if (__intel_wait_for_register(&i915->uncore, > XELPDP_PORT_BUF_CTL1(port), > > + XELPDP_PORT_BUF_SOC_PHY_READY, > > + XELPDP_PORT_BUF_SOC_PHY_READY, > > + > XELPDP_PORT_BUF_SOC_READY_TIMEOUT_US, 0, NULL)) > > + drm_warn(&i915->drm, "PHY %c failed to bring out of SOC reset > after %dus.\n", > > + phy_name(phy), > XELPDP_PORT_BUF_SOC_READY_TIMEOUT_US); > > + > > + intel_de_rmw(i915, XELPDP_PORT_BUF_CTL2(port), > > + PHY_LANES_VAL(PIPE_RESET, INTEL_CX0_BOTH_LANES), > > + PHY_LANES_VAL(PIPE_RESET, INTEL_CX0_BOTH_LANES)); > > + > > + if (__intel_wait_for_register(&i915->uncore, > XELPDP_PORT_BUF_CTL2(port), > > + PHY_LANES_VAL(PHY_CURRENT_STATUS, > INTEL_CX0_BOTH_LANES), > > + PHY_LANES_VAL(PHY_CURRENT_STATUS, > INTEL_CX0_BOTH_LANES), > > + > XELPDP_PORT_RESET_START_TIMEOUT_US, 0, NULL)) > > + drm_warn(&i915->drm, "PHY %c failed to bring out of Lane > reset after %dus.\n", > > + phy_name(phy), > XELPDP_PORT_RESET_START_TIMEOUT_US); > > + > > + intel_de_rmw(i915, XELPDP_PORT_CLOCK_CTL(port), > > + PHY_LANES_VAL(PCLK_REFCLK_REQUEST, lane), > > + PHY_LANES_VAL(PCLK_REFCLK_REQUEST, lane)); > > + > > + if (__intel_wait_for_register(&i915->uncore, > XELPDP_PORT_CLOCK_CTL(port), > > + PHY_LANES_VAL(PCLK_REFCLK_ACK, lane), > > + PHY_LANES_VAL(PCLK_REFCLK_ACK, lane), > > + XELPDP_REFCLK_ENABLE_TIMEOUT_US, 0, > NULL)) > > + drm_warn(&i915->drm, "PHY %c failed to request refclk after > %dus.\n", > > + phy_name(phy), > XELPDP_REFCLK_ENABLE_TIMEOUT_US); > > + > > + intel_cx0_powerdown_change_sequence(i915, port, > INTEL_CX0_BOTH_LANES, > > + CX0_P2_STATE_RESET); > > + intel_cx0_setup_powerdown(i915, port); > > + > > + intel_de_rmw(i915, XELPDP_PORT_BUF_CTL2(port), > > + PHY_LANES_VAL(PIPE_RESET, INTEL_CX0_BOTH_LANES), 0); > > + > > + if (intel_de_wait_for_clear(i915, XELPDP_PORT_BUF_CTL2(port), > > + PHY_LANES_VAL(PHY_CURRENT_STATUS, > > + INTEL_CX0_BOTH_LANES), > > + XELPDP_PORT_RESET_END_TIMEOUT)) > > + drm_warn(&i915->drm, "PHY %c failed to bring out of Lane > reset after %dms.\n", > > + phy_name(phy), > XELPDP_PORT_RESET_END_TIMEOUT); } > > + > > +static void intel_c10_program_phy_lane(struct drm_i915_private *i915, > > + enum port port, int lane_count, > > + bool lane_reversal) > > +{ > > + u8 l0t1, l0t2, l1t1, l1t2; > > + > > + intel_cx0_rmw(i915, port, INTEL_CX0_BOTH_LANES, > PHY_C10_VDR_CONTROL(1), > > + C10_VDR_CTRL_MSGBUS_ACCESS, > C10_VDR_CTRL_MSGBUS_ACCESS, > > + MB_WRITE_COMMITTED); > > + > > + l0t1 = intel_cx0_read(i915, port, 0, PHY_CX0_TX_CONTROL(1, 2)); > > + l0t2 = intel_cx0_read(i915, port, 0, PHY_CX0_TX_CONTROL(2, 2)); > > + l1t1 = intel_cx0_read(i915, port, 1, PHY_CX0_TX_CONTROL(1, 2)); > > + l1t2 = intel_cx0_read(i915, port, 1, PHY_CX0_TX_CONTROL(2, 2)); > > + > > + if (lane_reversal) { > > + switch (lane_count) { > > + case 1: > > + /* Disable MLs 1(lane0), 2(lane0), 3(lane1) */ > > + intel_cx0_write(i915, port, 1, > PHY_CX0_TX_CONTROL(1, 2), > > + l1t1 | > CONTROL2_DISABLE_SINGLE_TX, > > + MB_WRITE_COMMITTED); > > + fallthrough; > > + case 2: > > + /* Disable MLs 1(lane0), 2(lane0) */ > > + intel_cx0_write(i915, port, 0, > PHY_CX0_TX_CONTROL(2, 2), > > + l0t2 | > CONTROL2_DISABLE_SINGLE_TX, > > + MB_WRITE_COMMITTED); > > + fallthrough; > > + case 3: > > + /* Disable MLs 1(lane0) */ > > + intel_cx0_write(i915, port, 0, > PHY_CX0_TX_CONTROL(1, 2), > > + l0t1 | > CONTROL2_DISABLE_SINGLE_TX, > > + MB_WRITE_COMMITTED); > > + break; > > + } > > + } else { > > + switch (lane_count) { > > + case 1: > > + /* Disable MLs 2(lane0), 3(lane1), 4(lane1) */ > > + intel_cx0_write(i915, port, 0, > PHY_CX0_TX_CONTROL(2, 2), > > + l0t2 | > CONTROL2_DISABLE_SINGLE_TX, > > + MB_WRITE_COMMITTED); > > + fallthrough; > > + case 2: > > + /* Disable MLs 3(lane1), 4(lane1) */ > > + intel_cx0_write(i915, port, 1, > PHY_CX0_TX_CONTROL(1, 2), > > + l1t1 | > CONTROL2_DISABLE_SINGLE_TX, > > + MB_WRITE_COMMITTED); > > + fallthrough; > > + case 3: > > + /* Disable MLs 4(lane1) */ > > + intel_cx0_write(i915, port, 1, > PHY_CX0_TX_CONTROL(2, 2), > > + l1t2 | > CONTROL2_DISABLE_SINGLE_TX, > > + MB_WRITE_COMMITTED); > > + break; > > + } > > + } > > + > > + intel_cx0_rmw(i915, port, INTEL_CX0_BOTH_LANES, > PHY_C10_VDR_CONTROL(1), > > + C10_VDR_CTRL_UPDATE_CFG, > C10_VDR_CTRL_UPDATE_CFG, > > +MB_WRITE_COMMITTED); } > > + > > +static void intel_c10pll_enable(struct intel_encoder *encoder, > > + const struct intel_crtc_state *crtc_state) { > > + struct drm_i915_private *i915 = to_i915(encoder->base.dev); > > + enum phy phy = intel_port_to_phy(i915, encoder->port); > > + struct intel_digital_port *dig_port = enc_to_dig_port(encoder); > > + bool lane_reversal = dig_port->saved_port_bits & > DDI_BUF_PORT_REVERSAL; > > + enum intel_cx0_lanes maxpclk_lane = lane_reversal ? > INTEL_CX0_LANE1 : > > + INTEL_CX0_LANE0; > > + > > + /* > > + * 1. Program PORT_CLOCK_CTL REGISTER to configure > > + * clock muxes, gating and SSC > > + */ > > + intel_program_port_clock_ctl(encoder, crtc_state, lane_reversal); > > + > > + /* 2. Bring PHY out of reset. */ > > + intel_cx0_phy_lane_reset(i915, encoder->port, lane_reversal); > > + > > + /* > > + * 3. Change Phy power state to Ready. > > + * TODO: For DP alt mode use only one lane. > > + */ > > + intel_cx0_powerdown_change_sequence(i915, encoder->port, > INTEL_CX0_BOTH_LANES, > > + CX0_P2_STATE_READY); > > + > > + /* 4. Program PHY internal PLL internal registers. */ > > + intel_c10_pll_program(i915, crtc_state, encoder); > > + > > + /* > > + * 5. Program the enabled and disabled owned PHY lane > > + * transmitters over message bus > > + */ > > + intel_c10_program_phy_lane(i915, encoder->port, > > +crtc_state->lane_count, lane_reversal); > > + > > + /* > > + * 6. Follow the Display Voltage Frequency Switching - Sequence > > + * Before Frequency Change. We handle this step in bxt_set_cdclk(). > > + */ > > + > > + /* > > + * 7. Program DDI_CLK_VALFREQ to match intended DDI > > + * clock frequency. > > + */ > > + intel_de_write(i915, DDI_CLK_VALFREQ(encoder->port), > > + crtc_state->port_clock); > > + /* > > + * 8. Set PORT_CLOCK_CTL register PCLK PLL Request > > + * LN<Lane for maxPCLK> to "1" to enable PLL. > > + */ > > + intel_de_rmw(i915, XELPDP_PORT_CLOCK_CTL(encoder->port), 0, > > + PHY_LANES_VAL(PCLK_PLL_REQUEST, maxpclk_lane)); > > + > > + /* 9. Poll on PORT_CLOCK_CTL PCLK PLL Ack LN<Lane for maxPCLK> == > "1". */ > > + if (__intel_wait_for_register(&i915->uncore, > XELPDP_PORT_CLOCK_CTL(encoder->port), > > + PHY_LANES_VAL(PCLK_PLL_ACK, > maxpclk_lane), > > + PHY_LANES_VAL(PCLK_PLL_ACK, > maxpclk_lane), > > + XELPDP_PCLK_PLL_ENABLE_TIMEOUT_US, > 0, NULL)) > > + drm_warn(&i915->drm, "Port %c PLL not locked after %dus.\n", > > + phy_name(phy), > XELPDP_PCLK_PLL_ENABLE_TIMEOUT_US); > > + > > + /* > > + * 10. Follow the Display Voltage Frequency Switching Sequence After > > + * Frequency Change. We handle this step in bxt_set_cdclk(). > > + */ > > +} > > + > > +void intel_cx0pll_enable(struct intel_encoder *encoder, > > + const struct intel_crtc_state *crtc_state) { > > + struct drm_i915_private *i915 = to_i915(encoder->base.dev); > > + enum phy phy = intel_port_to_phy(i915, encoder->port); > > + > > + drm_WARN_ON(&i915->drm, !intel_is_c10phy(i915, phy)); > > + intel_c10pll_enable(encoder, crtc_state); } > > + > > +static void intel_c10pll_disable(struct intel_encoder *encoder) { > > + struct drm_i915_private *i915 = to_i915(encoder->base.dev); > > + enum phy phy = intel_port_to_phy(i915, encoder->port); > > + struct intel_digital_port *dig_port = enc_to_dig_port(encoder); > > + bool lane_reversal = dig_port->saved_port_bits & > DDI_BUF_PORT_REVERSAL; > > + enum intel_cx0_lanes lane = lane_reversal ? INTEL_CX0_LANE1 : > > + INTEL_CX0_LANE0; > > + > > + /* 1. Change owned PHY lane power to Disable state. */ > > + intel_cx0_powerdown_change_sequence(i915, encoder->port, > INTEL_CX0_BOTH_LANES, > > + CX0_P2PG_STATE_DISABLE); > > + > > + /* > > + * 2. Follow the Display Voltage Frequency Switching Sequence Before > > + * Frequency Change. We handle this step in bxt_set_cdclk(). > > + */ > > + > > + /* > > + * 3. Set PORT_CLOCK_CTL register PCLK PLL Request LN<Lane for > maxPCLK> > > + * to "0" to disable PLL. > > + */ > > + intel_de_rmw(i915, XELPDP_PORT_CLOCK_CTL(encoder->port), > > + PHY_LANES_VAL(PCLK_PLL_REQUEST, > INTEL_CX0_BOTH_LANES) | > > + PHY_LANES_VAL(PCLK_REFCLK_REQUEST, > INTEL_CX0_BOTH_LANES), 0); > > + > > + /* 4. Program DDI_CLK_VALFREQ to 0. */ > > + intel_de_write(i915, DDI_CLK_VALFREQ(encoder->port), 0); > > + > > + /* > > + * 5. Poll on PORT_CLOCK_CTL PCLK PLL Ack LN<Lane for maxPCLK**> > == "0". > > + */ > > + if (__intel_wait_for_register(&i915->uncore, > XELPDP_PORT_CLOCK_CTL(encoder->port), > > + PHY_LANES_VAL(PCLK_PLL_ACK, lane) | > > + PHY_LANES_VAL(PCLK_REFCLK_ACK, lane), > 0, > > + XELPDP_PCLK_PLL_DISABLE_TIMEOUT_US, > 0, NULL)) > > + drm_warn(&i915->drm, "Port %c PLL not unlocked after > %dus.\n", > > + phy_name(phy), > XELPDP_PCLK_PLL_DISABLE_TIMEOUT_US); > > + > > + /* > > + * 6. Follow the Display Voltage Frequency Switching Sequence After > > + * Frequency Change. We handle this step in bxt_set_cdclk(). > > + */ > > + > > + /* 7. Program PORT_CLOCK_CTL register to disable and gate clocks. */ > > + intel_de_rmw(i915, XELPDP_PORT_CLOCK_CTL(encoder->port), > > + XELPDP_DDI_CLOCK_SELECT_MASK | > > + XELPDP_FORWARD_CLOCK_UNGATE, 0); } > > + > > +void intel_cx0pll_disable(struct intel_encoder *encoder) { > > + struct drm_i915_private *i915 = to_i915(encoder->base.dev); > > + enum phy phy = intel_port_to_phy(i915, encoder->port); > > + > > + drm_WARN_ON(&i915->drm, !intel_is_c10phy(i915, phy)); > > + intel_c10pll_disable(encoder); > > +} > > + > > +#undef PHY_LANES_VAL_ARG > > +#undef PHY_LANES_VAL > > + > > void intel_c10mpllb_state_verify(struct intel_atomic_state *state, > > struct intel_crtc_state *new_crtc_state) { diff > --git > > a/drivers/gpu/drm/i915/display/intel_cx0_phy.h > > b/drivers/gpu/drm/i915/display/intel_cx0_phy.h > > index cf1f300b6a7b..d12d2e2f02ee 100644 > > --- a/drivers/gpu/drm/i915/display/intel_cx0_phy.h > > +++ b/drivers/gpu/drm/i915/display/intel_cx0_phy.h > > @@ -106,6 +106,19 @@ enum intel_cx0_lanes { > > #define C10_VDR_CTRL_UPDATE_CFG REG_BIT8(0) > > #define PHY_C10_VDR_CUSTOM_WIDTH 0xD02 > > > > +#define CX0_P0_STATE_ACTIVE 0x0 > > +#define CX0_P2_STATE_READY 0x2 > > +#define CX0_P2PG_STATE_DISABLE 0x9 > > +#define CX0_P4PG_STATE_DISABLE 0xC > > +#define CX0_P2_STATE_RESET 0x2 > > + > > +/* PHY_C10_VDR_PLL0 */ > > +#define PLL_C10_MPLL_SSC_EN REG_BIT8(0) > > + > > +/* PIPE SPEC Defined Registers */ > > +#define PHY_CX0_TX_CONTROL(tx, control) (0x400 + ((tx) - 1) * 0x200 + > (control)) > > +#define CONTROL2_DISABLE_SINGLE_TX REG_BIT(6) > > + > > Again, register definitions don't belong here. Yes, I will move these to a separate file. > > > static inline bool intel_is_c10phy(struct drm_i915_private *dev_priv, > > enum phy phy) { > > if (!IS_METEORLAKE(dev_priv)) > > @@ -114,6 +127,10 @@ static inline bool intel_is_c10phy(struct > drm_i915_private *dev_priv, enum phy p > > return (phy < PHY_C); > > } > > > > +void intel_cx0pll_enable(struct intel_encoder *encoder, > > + const struct intel_crtc_state *crtc_state); void > > +intel_cx0pll_disable(struct intel_encoder *encoder); > > + > > void intel_c10mpllb_readout_hw_state(struct intel_encoder *encoder, > > struct intel_c10mpllb_state *pll_state); int > > intel_cx0mpllb_calc_state(struct intel_crtc_state *crtc_state, diff > > --git a/drivers/gpu/drm/i915/display/intel_ddi.c > > b/drivers/gpu/drm/i915/display/intel_ddi.c > > index aaa8846c3b18..639ec604babf 100644 > > --- a/drivers/gpu/drm/i915/display/intel_ddi.c > > +++ b/drivers/gpu/drm/i915/display/intel_ddi.c > > @@ -4384,6 +4384,8 @@ void intel_ddi_init(struct drm_i915_private > *dev_priv, enum port port) > > encoder->pipe_mask = ~0; > > > > if (DISPLAY_VER(dev_priv) >= 14) { > > + encoder->enable_clock = intel_cx0pll_enable; > > + encoder->disable_clock = intel_cx0pll_disable; > > encoder->get_config = mtl_ddi_get_config; > > } else if (IS_DG2(dev_priv)) { > > encoder->enable_clock = intel_mpllb_enable; diff --git > > a/drivers/gpu/drm/i915/display/intel_dp.c > > b/drivers/gpu/drm/i915/display/intel_dp.c > > index 70b06806ec0d..db32799b5f46 100644 > > --- a/drivers/gpu/drm/i915/display/intel_dp.c > > +++ b/drivers/gpu/drm/i915/display/intel_dp.c > > @@ -420,6 +420,11 @@ static int ehl_max_source_rate(struct intel_dp > *intel_dp) > > return 810000; > > } > > > > +static int mtl_max_source_rate(struct intel_dp *intel_dp) { > > + return intel_dp_is_edp(intel_dp) ? 675000 : 810000; } > > + > > static int vbt_max_link_rate(struct intel_dp *intel_dp) { > > struct intel_encoder *encoder = &dp_to_dig_port(intel_dp)->base; @@ > > -444,6 +449,10 @@ static void intel_dp_set_source_rates(struct > > intel_dp *intel_dp) { > > /* The values must be in increasing order */ > > + static const int mtl_rates[] = { > > + 162000, 216000, 243000, 270000, 324000, 432000, 540000, > 675000, > > + 810000, > > + }; > > static const int icl_rates[] = { > > 162000, 216000, 270000, 324000, 432000, 540000, 648000, > 810000, > > 1000000, 1350000, > > @@ -469,7 +478,11 @@ intel_dp_set_source_rates(struct intel_dp *intel_dp) > > drm_WARN_ON(&dev_priv->drm, > > intel_dp->source_rates || intel_dp->num_source_rates); > > > > - if (DISPLAY_VER(dev_priv) >= 11) { > > + if (DISPLAY_VER(dev_priv) >= 14) { > > + source_rates = mtl_rates; > > + size = ARRAY_SIZE(mtl_rates); > > + max_rate = mtl_max_source_rate(intel_dp); > > + } else if (DISPLAY_VER(dev_priv) >= 11) { > > source_rates = icl_rates; > > size = ARRAY_SIZE(icl_rates); > > if (IS_DG2(dev_priv)) > > All of the changes to intel_dp.c should be a separate patch. Ok. I will split this patch so this one is separated from this patch > > > diff --git a/drivers/gpu/drm/i915/display/intel_dpll.c > > b/drivers/gpu/drm/i915/display/intel_dpll.c > > index 73f541050913..d6fcdf4eba0e 100644 > > --- a/drivers/gpu/drm/i915/display/intel_dpll.c > > +++ b/drivers/gpu/drm/i915/display/intel_dpll.c > > @@ -1533,6 +1533,8 @@ intel_dpll_init_clock_hook(struct > > drm_i915_private *dev_priv) { > > if (DISPLAY_VER(dev_priv) >= 14) > > dev_priv->display.funcs.dpll = &mtl_dpll_funcs; > > + else if (DISPLAY_VER(dev_priv) >= 14) > > + dev_priv->display.funcs.dpll = &mtl_dpll_funcs; > > else if (IS_DG2(dev_priv)) > > dev_priv->display.funcs.dpll = &dg2_dpll_funcs; > > else if (DISPLAY_VER(dev_priv) >= 9 || HAS_DDI(dev_priv)) diff --git > > a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h > > index 5003a5ffbc6a..5e6ff9f2aa10 100644 > > --- a/drivers/gpu/drm/i915/i915_reg.h > > +++ b/drivers/gpu/drm/i915/i915_reg.h > > @@ -2121,6 +2121,11 @@ > > #define TRANS_PUSH_EN REG_BIT(31) > > #define TRANS_PUSH_SEND REG_BIT(30) > > > > +/* DDI Buffer Control */ > > +#define _DDI_CLK_VALFREQ_A 0x64030 > > +#define _DDI_CLK_VALFREQ_B 0x64130 > > +#define DDI_CLK_VALFREQ(port) _MMIO_PORT(port, > _DDI_CLK_VALFREQ_A, _DDI_CLK_VALFREQ_B) > > + > > /* > > * HSW+ eDP PSR registers > > * > > @@ -8375,4 +8380,140 @@ enum skl_power_gate { > > > > #define MTL_MEDIA_GSI_BASE 0x380000 > > > > +#define PUNIT_MMIO_CR_POC_STRAPS _MMIO(0x281078) > > +#define NUM_TILES_MASK REG_GENMASK(1, 0) > > +#define CD_ALIVE REG_BIT(2) > > +#define SOCKET_ID_MASK REG_GENMASK(7, 3) > > + > > +/* Define the BAR and offset for the accelerator fabric CSRs */ > > +#define CD_BASE_OFFSET 0x291000 #define CD_BAR_SIZE (256 * 1024) > > + > > +/* > > + * In general, the i915 should not touch the IAF registers. The > > +registers > > + * will be passed as an IO resource via the MFD interface. However, > > +it > > + * is necessary to put the IRQ bits in a known state, before the MFD > > +cell > > + * is registered. > > + * > > + * So define these registers for i915 usage. > > These should probably be split to a separate _regs file, like we've been doing for > other registers. Especially because "In general, the i915 should not touch the IAF > registers." Maybe these could be moved into intel_cx0_reg_defs.h file? > > > + */ > > +#define CPORT_MBDB_CSRS (CD_BASE_OFFSET + 0x6000) #define > > +CPORT_MBDB_CSRS_END (CPORT_MBDB_CSRS + 0x1000) #define > > +CPORT_MBDB_INT_ENABLE_MASK _MMIO(CPORT_MBDB_CSRS + 0x8) > > + > > +#define _XELPDP_PORT_M2P_MSGBUS_CTL_LN0_A 0x64040 > > +#define _XELPDP_PORT_M2P_MSGBUS_CTL_LN0_B 0x64140 > > +#define _XELPDP_PORT_M2P_MSGBUS_CTL_LN0_USBC1 > 0x16F240 > > +#define _XELPDP_PORT_M2P_MSGBUS_CTL_LN0_USBC2 > 0x16F440 > > +#define _XELPDP_PORT_M2P_MSGBUS_CTL_LN0_USBC3 > 0x16F640 > > +#define _XELPDP_PORT_M2P_MSGBUS_CTL_LN0_USBC4 > 0x16F840 > > +#define _XELPDP_PORT_M2P_MSGBUS_CTL(port, lane) > (_PICK(port, \ > > + [PORT_A] = > _XELPDP_PORT_M2P_MSGBUS_CTL_LN0_A, \ > > + [PORT_B] = > _XELPDP_PORT_M2P_MSGBUS_CTL_LN0_B, \ > > + [PORT_TC1] = > _XELPDP_PORT_M2P_MSGBUS_CTL_LN0_USBC1, \ > > + [PORT_TC2] = > _XELPDP_PORT_M2P_MSGBUS_CTL_LN0_USBC2, \ > > + [PORT_TC3] = > _XELPDP_PORT_M2P_MSGBUS_CTL_LN0_USBC3, \ > > + [PORT_TC4] = > _XELPDP_PORT_M2P_MSGBUS_CTL_LN0_USBC4) + ((lane) > > +* 4)) > > + > > +#define XELPDP_PORT_M2P_MSGBUS_CTL(port, lane) > _MMIO(_XELPDP_PORT_M2P_MSGBUS_CTL(port, lane)) > > +#define XELPDP_PORT_M2P_TRANSACTION_PENDING > REG_BIT(31) > > +#define XELPDP_PORT_M2P_COMMAND_TYPE_MASK > REG_GENMASK(30, 27) > > +#define XELPDP_PORT_M2P_COMMAND_WRITE_UNCOMMITTED > REG_FIELD_PREP(XELPDP_PORT_M2P_COMMAND_TYPE_MASK, 0x1) > > +#define XELPDP_PORT_M2P_COMMAND_WRITE_COMMITTED > REG_FIELD_PREP(XELPDP_PORT_M2P_COMMAND_TYPE_MASK, 0x2) > > +#define XELPDP_PORT_M2P_COMMAND_READ > REG_FIELD_PREP(XELPDP_PORT_M2P_COMMAND_TYPE_MASK, 0x3) > > +#define XELPDP_PORT_M2P_DATA_MASK > REG_GENMASK(23, 16) > > +#define XELPDP_PORT_M2P_DATA(val) > REG_FIELD_PREP(XELPDP_PORT_M2P_DATA_MASK, val) > > +#define XELPDP_PORT_M2P_TRANSACTION_RESET REG_BIT(15) > > +#define XELPDP_PORT_M2P_ADDRESS_MASK > REG_GENMASK(11, 0) > > +#define XELPDP_PORT_M2P_ADDRESS(val) > REG_FIELD_PREP(XELPDP_PORT_M2P_ADDRESS_MASK, val) > > + > > +#define XELPDP_PORT_P2M_MSGBUS_STATUS(port, lane) > _MMIO(_XELPDP_PORT_M2P_MSGBUS_CTL(port, lane) + 8) > > +#define XELPDP_PORT_P2M_RESPONSE_READY > REG_BIT(31) > > +#define XELPDP_PORT_P2M_COMMAND_TYPE_MASK > REG_GENMASK(30, 27) > > +#define XELPDP_PORT_P2M_COMMAND_READ_ACK 0x4 > > +#define XELPDP_PORT_P2M_COMMAND_WRITE_ACK 0x5 > > +#define XELPDP_PORT_P2M_DATA_MASK > REG_GENMASK(23, 16) > > +#define XELPDP_PORT_P2M_DATA(val) > REG_FIELD_PREP(XELPDP_PORT_P2M_DATA_MASK, val) > > +#define XELPDP_PORT_P2M_ERROR_SET REG_BIT(15) > > + > > +#define XELPDP_MSGBUS_TIMEOUT_SLOW 1 > > +#define XELPDP_MSGBUS_TIMEOUT_FAST_US 2 > > +#define XELPDP_PCLK_PLL_ENABLE_TIMEOUT_US 3200 > > +#define XELPDP_PCLK_PLL_DISABLE_TIMEOUT_US 20 > > +#define XELPDP_PORT_BUF_SOC_READY_TIMEOUT_US 100 > > +#define XELPDP_PORT_RESET_START_TIMEOUT_US 5 > > +#define XELPDP_PORT_RESET_END_TIMEOUT 15 > > +#define XELPDP_REFCLK_ENABLE_TIMEOUT_US 1 > > + > > +#define _XELPDP_PORT_BUF_CTL1_LN0_A 0x64004 > > +#define _XELPDP_PORT_BUF_CTL1_LN0_B 0x64104 > > +#define _XELPDP_PORT_BUF_CTL1_LN0_USBC1 > 0x16F200 > > +#define _XELPDP_PORT_BUF_CTL1_LN0_USBC2 > 0x16F400 > > +#define _XELPDP_PORT_BUF_CTL1_LN0_USBC3 > 0x16F600 > > +#define _XELPDP_PORT_BUF_CTL1_LN0_USBC4 > 0x16F800 > > +#define _XELPDP_PORT_BUF_CTL1(port) (_PICK(port, \ > > + [PORT_A] = > _XELPDP_PORT_BUF_CTL1_LN0_A, \ > > + [PORT_B] = > _XELPDP_PORT_BUF_CTL1_LN0_B, \ > > + [PORT_TC1] = > _XELPDP_PORT_BUF_CTL1_LN0_USBC1, \ > > + [PORT_TC2] = > _XELPDP_PORT_BUF_CTL1_LN0_USBC2, \ > > + [PORT_TC3] = > _XELPDP_PORT_BUF_CTL1_LN0_USBC3, \ > > + [PORT_TC4] = > _XELPDP_PORT_BUF_CTL1_LN0_USBC4)) > > + > > +#define XELPDP_PORT_BUF_CTL1(port) > _MMIO(_XELPDP_PORT_BUF_CTL1(port)) > > +#define XELPDP_PORT_BUF_SOC_PHY_READY > REG_BIT(24) > > +#define XELPDP_PORT_REVERSAL REG_BIT(16) > > +#define XELPDP_PORT_WIDTH_MASK > REG_GENMASK(3, 1) > > +#define XELPDP_PORT_WIDTH(val) > REG_FIELD_PREP(XELPDP_PORT_WIDTH_MASK, val) > > + > > +#define XELPDP_PORT_BUF_CTL2(port) > _MMIO(_XELPDP_PORT_BUF_CTL1(port) + 4) > > +#define XELPDP_LANE0_PIPE_RESET REG_BIT(31) > > +#define XELPDP_LANE1_PIPE_RESET REG_BIT(30) > > +#define XELPDP_LANE0_PHY_CURRENT_STATUS REG_BIT(29) > > +#define XELPDP_LANE1_PHY_CURRENT_STATUS REG_BIT(28) > > +#define XELPDP_LANE0_POWERDOWN_UPDATE > REG_BIT(25) > > +#define XELPDP_LANE1_POWERDOWN_UPDATE > REG_BIT(24) > > +#define XELPDP_LANE0_POWERDOWN_NEW_STATE_MASK > REG_GENMASK(23, 20) > > +#define XELPDP_LANE0_POWERDOWN_NEW_STATE(val) > REG_FIELD_PREP(XELPDP_LANE0_POWERDOWN_NEW_STATE_MASK, > val) > > +#define XELPDP_LANE1_POWERDOWN_NEW_STATE_MASK > REG_GENMASK(19, 16) > > +#define XELPDP_LANE1_POWERDOWN_NEW_STATE(val) > REG_FIELD_PREP(XELPDP_LANE1_POWERDOWN_NEW_STATE_MASK, > val) > > +#define XELPDP_POWER_STATE_READY_MASK > REG_GENMASK(7, 4) > > +#define XELPDP_POWER_STATE_READY(val) > REG_FIELD_PREP(XELPDP_POWER_STATE_READY_MASK, val) > > + > > +#define XELPDP_PORT_BUF_CTL3(port) > _MMIO(_XELPDP_PORT_BUF_CTL1(port) + 8) > > +#define XELPDP_PLL_LANE_STAGGERING_DELAY_MASK > REG_GENMASK(15, 8) > > +#define XELPDP_PLL_LANE_STAGGERING_DELAY(val) > REG_FIELD_PREP(XELPDP_PLL_LANE_STAGGERING_DELAY_MASK, val) > > +#define XELPDP_POWER_STATE_ACTIVE_MASK > REG_GENMASK(3, 0) > > +#define XELPDP_POWER_STATE_ACTIVE(val) > REG_FIELD_PREP(XELPDP_POWER_STATE_ACTIVE_MASK, val) > > + > > +#define _XELPDP_PORT_CLOCK_CTL_A 0x640E0 > > +#define _XELPDP_PORT_CLOCK_CTL_B 0x641E0 > > +#define _XELPDP_PORT_CLOCK_CTL_USBC1 0x16F260 > > +#define _XELPDP_PORT_CLOCK_CTL_USBC2 0x16F460 > > +#define _XELPDP_PORT_CLOCK_CTL_USBC3 0x16F660 > > +#define _XELPDP_PORT_CLOCK_CTL_USBC4 0x16F860 > > +#define XELPDP_PORT_CLOCK_CTL(port) > _MMIO(_PICK(port, \ > > + [PORT_A] = > _XELPDP_PORT_CLOCK_CTL_A, \ > > + [PORT_B] = > _XELPDP_PORT_CLOCK_CTL_B, \ > > + [PORT_TC1] = > _XELPDP_PORT_CLOCK_CTL_USBC1, \ > > + [PORT_TC2] = > _XELPDP_PORT_CLOCK_CTL_USBC2, \ > > + [PORT_TC3] = > _XELPDP_PORT_CLOCK_CTL_USBC3, \ > > + [PORT_TC4] = > _XELPDP_PORT_CLOCK_CTL_USBC4)) > > + > > +#define XELPDP_LANE0_PCLK_PLL_REQUEST REG_BIT(31) > > +#define XELPDP_LANE0_PCLK_PLL_ACK REG_BIT(30) > > +#define XELPDP_LANE0_PCLK_REFCLK_REQUEST REG_BIT(29) > > +#define XELPDP_LANE0_PCLK_REFCLK_ACK REG_BIT(28) > > +#define XELPDP_LANE1_PCLK_PLL_REQUEST REG_BIT(27) > > +#define XELPDP_LANE1_PCLK_PLL_ACK REG_BIT(26) > > +#define XELPDP_LANE1_PCLK_REFCLK_REQUEST REG_BIT(25) > > +#define XELPDP_LANE1_PCLK_REFCLK_ACK REG_BIT(24) > > +#define XELPDP_DDI_CLOCK_SELECT_MASK > REG_GENMASK(15, 12) > > +#define XELPDP_DDI_CLOCK_SELECT(val) > REG_FIELD_PREP(XELPDP_DDI_CLOCK_SELECT_MASK, val) > > +#define XELPDP_DDI_CLOCK_SELECT_NONE 0x0 > > +#define XELPDP_DDI_CLOCK_SELECT_MAXPCLK 0x8 > > +#define XELPDP_FORWARD_CLOCK_UNGATE REG_BIT(10) > > +#define XELPDP_LANE1_PHY_CLOCK_SELECT REG_BIT(8) > > +#define XELPDP_SSC_ENABLE_PLLA REG_BIT(1) > > +#define XELPDP_SSC_ENABLE_PLLB REG_BIT(0) > > + > > #endif /* _I915_REG_H_ */ > > -- > Jani Nikula, Intel Open Source Graphics Center
On Fri, 14 Oct 2022, "Kahola, Mika" <mika.kahola@intel.com> wrote:
> Maybe these could be moved into intel_cx0_reg_defs.h file?
Register definitions to intel_cx0_regs.h. See
$ find drivers/gpu/drm/i915/ -name "*_regs.h"
Any common helpers such as REG_FIELD_GET8() and friends to
i915_reg_defs.h where we already have some other sized helpers.
BR,
Jani.
diff --git a/drivers/gpu/drm/i915/Makefile b/drivers/gpu/drm/i915/Makefile index a26edcdadc21..994f87a12782 100644 --- a/drivers/gpu/drm/i915/Makefile +++ b/drivers/gpu/drm/i915/Makefile @@ -279,6 +279,7 @@ i915-y += \ display/icl_dsi.o \ display/intel_backlight.o \ display/intel_crt.o \ + display/intel_cx0_phy.o \ display/intel_ddi.o \ display/intel_ddi_buf_trans.o \ display/intel_display_trace.o \ diff --git a/drivers/gpu/drm/i915/display/intel_cx0_phy.c b/drivers/gpu/drm/i915/display/intel_cx0_phy.c index 2f401116d1d0..6ba11cd7cd75 100644 --- a/drivers/gpu/drm/i915/display/intel_cx0_phy.c +++ b/drivers/gpu/drm/i915/display/intel_cx0_phy.c @@ -526,9 +526,9 @@ void intel_c10mpllb_readout_hw_state(struct intel_encoder *encoder, tx0, cmn, phy_name(phy)); } -__maybe_unused static void intel_c10_pll_program(struct drm_i915_private *i915, - const struct intel_crtc_state *crtc_state, - struct intel_encoder *encoder) +static void intel_c10_pll_program(struct drm_i915_private *i915, + const struct intel_crtc_state *crtc_state, + struct intel_encoder *encoder) { const struct intel_c10mpllb_state *pll_state = &crtc_state->c10mpllb_state; struct intel_digital_port *dig_port = enc_to_dig_port(encoder); @@ -633,6 +633,352 @@ int intel_c10mpllb_calc_port_clock(struct intel_encoder *encoder, 10 << (tx_clk_div + 16)); } +#define PHY_LANES_VAL_ARG(FIELD, lanes, arg) ({u32 __val; switch(lanes) {\ + case INTEL_CX0_BOTH_LANES: \ + __val = ((XELPDP_LANE0_##FIELD(arg)) |\ + (XELPDP_LANE1_##FIELD(arg))); \ + break; \ + case INTEL_CX0_LANE0: \ + __val = (XELPDP_LANE0_##FIELD(arg));\ + break; \ + case INTEL_CX0_LANE1: \ + __val = (XELPDP_LANE1_##FIELD(arg));\ + break; \ + }; __val; }) + +#define PHY_LANES_VAL(FIELD, lanes) ({u32 __val; switch(lanes) {\ + case INTEL_CX0_BOTH_LANES: \ + __val = (XELPDP_LANE0_##FIELD | \ + XELPDP_LANE1_##FIELD); \ + break; \ + case INTEL_CX0_LANE0: \ + __val = (XELPDP_LANE0_##FIELD); \ + break; \ + case INTEL_CX0_LANE1: \ + __val = (XELPDP_LANE1_##FIELD);\ + break; \ + }; __val; }) + +static void intel_program_port_clock_ctl(struct intel_encoder *encoder, + const struct intel_crtc_state *crtc_state, + bool lane_reversal) +{ + struct drm_i915_private *i915 = to_i915(encoder->base.dev); + struct intel_dp *intel_dp; + bool ssc_enabled; + u32 val = 0; + + intel_de_rmw(i915, XELPDP_PORT_BUF_CTL1(encoder->port), XELPDP_PORT_REVERSAL, + lane_reversal ? XELPDP_PORT_REVERSAL : 0); + + if (lane_reversal) + val |= XELPDP_LANE1_PHY_CLOCK_SELECT; + + val |= XELPDP_FORWARD_CLOCK_UNGATE; + val |= XELPDP_DDI_CLOCK_SELECT(XELPDP_DDI_CLOCK_SELECT_MAXPCLK); + + if (intel_crtc_has_dp_encoder(crtc_state)) { + intel_dp = enc_to_intel_dp(encoder); + ssc_enabled = intel_dp->dpcd[DP_MAX_DOWNSPREAD] & + DP_MAX_DOWNSPREAD_0_5; + + /* TODO: DP2.0 10G and 20G rates enable MPLLA*/ + val |= ssc_enabled ? XELPDP_SSC_ENABLE_PLLB : 0; + } + intel_de_rmw(i915, XELPDP_PORT_CLOCK_CTL(encoder->port), + XELPDP_LANE1_PHY_CLOCK_SELECT | + XELPDP_FORWARD_CLOCK_UNGATE | + XELPDP_DDI_CLOCK_SELECT_MASK | + XELPDP_SSC_ENABLE_PLLB, val); +} + +static void intel_cx0_powerdown_change_sequence(struct drm_i915_private *i915, + enum port port, + enum intel_cx0_lanes lane, u8 state) +{ + enum phy phy = intel_port_to_phy(i915, port); + + intel_de_rmw(i915, XELPDP_PORT_BUF_CTL2(port), + PHY_LANES_VAL(POWERDOWN_NEW_STATE_MASK, lane), + PHY_LANES_VAL_ARG(POWERDOWN_NEW_STATE, lane, state)); + intel_de_rmw(i915, XELPDP_PORT_BUF_CTL2(port), + PHY_LANES_VAL(POWERDOWN_UPDATE, lane), + PHY_LANES_VAL(POWERDOWN_UPDATE, lane)); + + /* Update Timeout Value */ + if (__intel_wait_for_register(&i915->uncore, XELPDP_PORT_BUF_CTL2(port), + PHY_LANES_VAL(POWERDOWN_UPDATE, lane), 0, + XELPDP_PORT_RESET_START_TIMEOUT_US, 0, NULL)) + drm_warn(&i915->drm, "PHY %c failed to bring out of Lane reset after %dus.\n", + phy_name(phy), XELPDP_PORT_RESET_START_TIMEOUT_US); +} + +static void intel_cx0_setup_powerdown(struct drm_i915_private *i915, enum port port) +{ + intel_de_rmw(i915, XELPDP_PORT_BUF_CTL2(port), + XELPDP_POWER_STATE_READY_MASK, + XELPDP_POWER_STATE_READY(CX0_P2_STATE_READY)); + intel_de_rmw(i915, XELPDP_PORT_BUF_CTL3(port), + XELPDP_POWER_STATE_ACTIVE_MASK | + XELPDP_PLL_LANE_STAGGERING_DELAY_MASK, + XELPDP_POWER_STATE_ACTIVE(CX0_P0_STATE_ACTIVE) | + XELPDP_PLL_LANE_STAGGERING_DELAY(0)); +} + +/* FIXME: Some Type-C cases need not reset both the lanes. Handle those cases. */ +static void intel_cx0_phy_lane_reset(struct drm_i915_private *i915, enum port port, + bool lane_reversal) +{ + enum phy phy = intel_port_to_phy(i915, port); + enum intel_cx0_lanes lane = lane_reversal ? INTEL_CX0_LANE1 : + INTEL_CX0_LANE0; + + if (__intel_wait_for_register(&i915->uncore, XELPDP_PORT_BUF_CTL1(port), + XELPDP_PORT_BUF_SOC_PHY_READY, + XELPDP_PORT_BUF_SOC_PHY_READY, + XELPDP_PORT_BUF_SOC_READY_TIMEOUT_US, 0, NULL)) + drm_warn(&i915->drm, "PHY %c failed to bring out of SOC reset after %dus.\n", + phy_name(phy), XELPDP_PORT_BUF_SOC_READY_TIMEOUT_US); + + intel_de_rmw(i915, XELPDP_PORT_BUF_CTL2(port), + PHY_LANES_VAL(PIPE_RESET, INTEL_CX0_BOTH_LANES), + PHY_LANES_VAL(PIPE_RESET, INTEL_CX0_BOTH_LANES)); + + if (__intel_wait_for_register(&i915->uncore, XELPDP_PORT_BUF_CTL2(port), + PHY_LANES_VAL(PHY_CURRENT_STATUS, INTEL_CX0_BOTH_LANES), + PHY_LANES_VAL(PHY_CURRENT_STATUS, INTEL_CX0_BOTH_LANES), + XELPDP_PORT_RESET_START_TIMEOUT_US, 0, NULL)) + drm_warn(&i915->drm, "PHY %c failed to bring out of Lane reset after %dus.\n", + phy_name(phy), XELPDP_PORT_RESET_START_TIMEOUT_US); + + intel_de_rmw(i915, XELPDP_PORT_CLOCK_CTL(port), + PHY_LANES_VAL(PCLK_REFCLK_REQUEST, lane), + PHY_LANES_VAL(PCLK_REFCLK_REQUEST, lane)); + + if (__intel_wait_for_register(&i915->uncore, XELPDP_PORT_CLOCK_CTL(port), + PHY_LANES_VAL(PCLK_REFCLK_ACK, lane), + PHY_LANES_VAL(PCLK_REFCLK_ACK, lane), + XELPDP_REFCLK_ENABLE_TIMEOUT_US, 0, NULL)) + drm_warn(&i915->drm, "PHY %c failed to request refclk after %dus.\n", + phy_name(phy), XELPDP_REFCLK_ENABLE_TIMEOUT_US); + + intel_cx0_powerdown_change_sequence(i915, port, INTEL_CX0_BOTH_LANES, + CX0_P2_STATE_RESET); + intel_cx0_setup_powerdown(i915, port); + + intel_de_rmw(i915, XELPDP_PORT_BUF_CTL2(port), + PHY_LANES_VAL(PIPE_RESET, INTEL_CX0_BOTH_LANES), 0); + + if (intel_de_wait_for_clear(i915, XELPDP_PORT_BUF_CTL2(port), + PHY_LANES_VAL(PHY_CURRENT_STATUS, + INTEL_CX0_BOTH_LANES), + XELPDP_PORT_RESET_END_TIMEOUT)) + drm_warn(&i915->drm, "PHY %c failed to bring out of Lane reset after %dms.\n", + phy_name(phy), XELPDP_PORT_RESET_END_TIMEOUT); +} + +static void intel_c10_program_phy_lane(struct drm_i915_private *i915, + enum port port, int lane_count, + bool lane_reversal) +{ + u8 l0t1, l0t2, l1t1, l1t2; + + intel_cx0_rmw(i915, port, INTEL_CX0_BOTH_LANES, PHY_C10_VDR_CONTROL(1), + C10_VDR_CTRL_MSGBUS_ACCESS, C10_VDR_CTRL_MSGBUS_ACCESS, + MB_WRITE_COMMITTED); + + l0t1 = intel_cx0_read(i915, port, 0, PHY_CX0_TX_CONTROL(1, 2)); + l0t2 = intel_cx0_read(i915, port, 0, PHY_CX0_TX_CONTROL(2, 2)); + l1t1 = intel_cx0_read(i915, port, 1, PHY_CX0_TX_CONTROL(1, 2)); + l1t2 = intel_cx0_read(i915, port, 1, PHY_CX0_TX_CONTROL(2, 2)); + + if (lane_reversal) { + switch (lane_count) { + case 1: + /* Disable MLs 1(lane0), 2(lane0), 3(lane1) */ + intel_cx0_write(i915, port, 1, PHY_CX0_TX_CONTROL(1, 2), + l1t1 | CONTROL2_DISABLE_SINGLE_TX, + MB_WRITE_COMMITTED); + fallthrough; + case 2: + /* Disable MLs 1(lane0), 2(lane0) */ + intel_cx0_write(i915, port, 0, PHY_CX0_TX_CONTROL(2, 2), + l0t2 | CONTROL2_DISABLE_SINGLE_TX, + MB_WRITE_COMMITTED); + fallthrough; + case 3: + /* Disable MLs 1(lane0) */ + intel_cx0_write(i915, port, 0, PHY_CX0_TX_CONTROL(1, 2), + l0t1 | CONTROL2_DISABLE_SINGLE_TX, + MB_WRITE_COMMITTED); + break; + } + } else { + switch (lane_count) { + case 1: + /* Disable MLs 2(lane0), 3(lane1), 4(lane1) */ + intel_cx0_write(i915, port, 0, PHY_CX0_TX_CONTROL(2, 2), + l0t2 | CONTROL2_DISABLE_SINGLE_TX, + MB_WRITE_COMMITTED); + fallthrough; + case 2: + /* Disable MLs 3(lane1), 4(lane1) */ + intel_cx0_write(i915, port, 1, PHY_CX0_TX_CONTROL(1, 2), + l1t1 | CONTROL2_DISABLE_SINGLE_TX, + MB_WRITE_COMMITTED); + fallthrough; + case 3: + /* Disable MLs 4(lane1) */ + intel_cx0_write(i915, port, 1, PHY_CX0_TX_CONTROL(2, 2), + l1t2 | CONTROL2_DISABLE_SINGLE_TX, + MB_WRITE_COMMITTED); + break; + } + } + + intel_cx0_rmw(i915, port, INTEL_CX0_BOTH_LANES, PHY_C10_VDR_CONTROL(1), + C10_VDR_CTRL_UPDATE_CFG, C10_VDR_CTRL_UPDATE_CFG, MB_WRITE_COMMITTED); +} + +static void intel_c10pll_enable(struct intel_encoder *encoder, + const struct intel_crtc_state *crtc_state) +{ + struct drm_i915_private *i915 = to_i915(encoder->base.dev); + enum phy phy = intel_port_to_phy(i915, encoder->port); + struct intel_digital_port *dig_port = enc_to_dig_port(encoder); + bool lane_reversal = dig_port->saved_port_bits & DDI_BUF_PORT_REVERSAL; + enum intel_cx0_lanes maxpclk_lane = lane_reversal ? INTEL_CX0_LANE1 : + INTEL_CX0_LANE0; + + /* + * 1. Program PORT_CLOCK_CTL REGISTER to configure + * clock muxes, gating and SSC + */ + intel_program_port_clock_ctl(encoder, crtc_state, lane_reversal); + + /* 2. Bring PHY out of reset. */ + intel_cx0_phy_lane_reset(i915, encoder->port, lane_reversal); + + /* + * 3. Change Phy power state to Ready. + * TODO: For DP alt mode use only one lane. + */ + intel_cx0_powerdown_change_sequence(i915, encoder->port, INTEL_CX0_BOTH_LANES, + CX0_P2_STATE_READY); + + /* 4. Program PHY internal PLL internal registers. */ + intel_c10_pll_program(i915, crtc_state, encoder); + + /* + * 5. Program the enabled and disabled owned PHY lane + * transmitters over message bus + */ + intel_c10_program_phy_lane(i915, encoder->port, crtc_state->lane_count, lane_reversal); + + /* + * 6. Follow the Display Voltage Frequency Switching - Sequence + * Before Frequency Change. We handle this step in bxt_set_cdclk(). + */ + + /* + * 7. Program DDI_CLK_VALFREQ to match intended DDI + * clock frequency. + */ + intel_de_write(i915, DDI_CLK_VALFREQ(encoder->port), + crtc_state->port_clock); + /* + * 8. Set PORT_CLOCK_CTL register PCLK PLL Request + * LN<Lane for maxPCLK> to "1" to enable PLL. + */ + intel_de_rmw(i915, XELPDP_PORT_CLOCK_CTL(encoder->port), 0, + PHY_LANES_VAL(PCLK_PLL_REQUEST, maxpclk_lane)); + + /* 9. Poll on PORT_CLOCK_CTL PCLK PLL Ack LN<Lane for maxPCLK> == "1". */ + if (__intel_wait_for_register(&i915->uncore, XELPDP_PORT_CLOCK_CTL(encoder->port), + PHY_LANES_VAL(PCLK_PLL_ACK, maxpclk_lane), + PHY_LANES_VAL(PCLK_PLL_ACK, maxpclk_lane), + XELPDP_PCLK_PLL_ENABLE_TIMEOUT_US, 0, NULL)) + drm_warn(&i915->drm, "Port %c PLL not locked after %dus.\n", + phy_name(phy), XELPDP_PCLK_PLL_ENABLE_TIMEOUT_US); + + /* + * 10. Follow the Display Voltage Frequency Switching Sequence After + * Frequency Change. We handle this step in bxt_set_cdclk(). + */ +} + +void intel_cx0pll_enable(struct intel_encoder *encoder, + const struct intel_crtc_state *crtc_state) +{ + struct drm_i915_private *i915 = to_i915(encoder->base.dev); + enum phy phy = intel_port_to_phy(i915, encoder->port); + + drm_WARN_ON(&i915->drm, !intel_is_c10phy(i915, phy)); + intel_c10pll_enable(encoder, crtc_state); +} + +static void intel_c10pll_disable(struct intel_encoder *encoder) +{ + struct drm_i915_private *i915 = to_i915(encoder->base.dev); + enum phy phy = intel_port_to_phy(i915, encoder->port); + struct intel_digital_port *dig_port = enc_to_dig_port(encoder); + bool lane_reversal = dig_port->saved_port_bits & DDI_BUF_PORT_REVERSAL; + enum intel_cx0_lanes lane = lane_reversal ? INTEL_CX0_LANE1 : + INTEL_CX0_LANE0; + + /* 1. Change owned PHY lane power to Disable state. */ + intel_cx0_powerdown_change_sequence(i915, encoder->port, INTEL_CX0_BOTH_LANES, + CX0_P2PG_STATE_DISABLE); + + /* + * 2. Follow the Display Voltage Frequency Switching Sequence Before + * Frequency Change. We handle this step in bxt_set_cdclk(). + */ + + /* + * 3. Set PORT_CLOCK_CTL register PCLK PLL Request LN<Lane for maxPCLK> + * to "0" to disable PLL. + */ + intel_de_rmw(i915, XELPDP_PORT_CLOCK_CTL(encoder->port), + PHY_LANES_VAL(PCLK_PLL_REQUEST, INTEL_CX0_BOTH_LANES) | + PHY_LANES_VAL(PCLK_REFCLK_REQUEST, INTEL_CX0_BOTH_LANES), 0); + + /* 4. Program DDI_CLK_VALFREQ to 0. */ + intel_de_write(i915, DDI_CLK_VALFREQ(encoder->port), 0); + + /* + * 5. Poll on PORT_CLOCK_CTL PCLK PLL Ack LN<Lane for maxPCLK**> == "0". + */ + if (__intel_wait_for_register(&i915->uncore, XELPDP_PORT_CLOCK_CTL(encoder->port), + PHY_LANES_VAL(PCLK_PLL_ACK, lane) | + PHY_LANES_VAL(PCLK_REFCLK_ACK, lane), 0, + XELPDP_PCLK_PLL_DISABLE_TIMEOUT_US, 0, NULL)) + drm_warn(&i915->drm, "Port %c PLL not unlocked after %dus.\n", + phy_name(phy), XELPDP_PCLK_PLL_DISABLE_TIMEOUT_US); + + /* + * 6. Follow the Display Voltage Frequency Switching Sequence After + * Frequency Change. We handle this step in bxt_set_cdclk(). + */ + + /* 7. Program PORT_CLOCK_CTL register to disable and gate clocks. */ + intel_de_rmw(i915, XELPDP_PORT_CLOCK_CTL(encoder->port), + XELPDP_DDI_CLOCK_SELECT_MASK | + XELPDP_FORWARD_CLOCK_UNGATE, 0); +} + +void intel_cx0pll_disable(struct intel_encoder *encoder) +{ + struct drm_i915_private *i915 = to_i915(encoder->base.dev); + enum phy phy = intel_port_to_phy(i915, encoder->port); + + drm_WARN_ON(&i915->drm, !intel_is_c10phy(i915, phy)); + intel_c10pll_disable(encoder); +} + +#undef PHY_LANES_VAL_ARG +#undef PHY_LANES_VAL + void intel_c10mpllb_state_verify(struct intel_atomic_state *state, struct intel_crtc_state *new_crtc_state) { diff --git a/drivers/gpu/drm/i915/display/intel_cx0_phy.h b/drivers/gpu/drm/i915/display/intel_cx0_phy.h index cf1f300b6a7b..d12d2e2f02ee 100644 --- a/drivers/gpu/drm/i915/display/intel_cx0_phy.h +++ b/drivers/gpu/drm/i915/display/intel_cx0_phy.h @@ -106,6 +106,19 @@ enum intel_cx0_lanes { #define C10_VDR_CTRL_UPDATE_CFG REG_BIT8(0) #define PHY_C10_VDR_CUSTOM_WIDTH 0xD02 +#define CX0_P0_STATE_ACTIVE 0x0 +#define CX0_P2_STATE_READY 0x2 +#define CX0_P2PG_STATE_DISABLE 0x9 +#define CX0_P4PG_STATE_DISABLE 0xC +#define CX0_P2_STATE_RESET 0x2 + +/* PHY_C10_VDR_PLL0 */ +#define PLL_C10_MPLL_SSC_EN REG_BIT8(0) + +/* PIPE SPEC Defined Registers */ +#define PHY_CX0_TX_CONTROL(tx, control) (0x400 + ((tx) - 1) * 0x200 + (control)) +#define CONTROL2_DISABLE_SINGLE_TX REG_BIT(6) + static inline bool intel_is_c10phy(struct drm_i915_private *dev_priv, enum phy phy) { if (!IS_METEORLAKE(dev_priv)) @@ -114,6 +127,10 @@ static inline bool intel_is_c10phy(struct drm_i915_private *dev_priv, enum phy p return (phy < PHY_C); } +void intel_cx0pll_enable(struct intel_encoder *encoder, + const struct intel_crtc_state *crtc_state); +void intel_cx0pll_disable(struct intel_encoder *encoder); + void intel_c10mpllb_readout_hw_state(struct intel_encoder *encoder, struct intel_c10mpllb_state *pll_state); int intel_cx0mpllb_calc_state(struct intel_crtc_state *crtc_state, diff --git a/drivers/gpu/drm/i915/display/intel_ddi.c b/drivers/gpu/drm/i915/display/intel_ddi.c index aaa8846c3b18..639ec604babf 100644 --- a/drivers/gpu/drm/i915/display/intel_ddi.c +++ b/drivers/gpu/drm/i915/display/intel_ddi.c @@ -4384,6 +4384,8 @@ void intel_ddi_init(struct drm_i915_private *dev_priv, enum port port) encoder->pipe_mask = ~0; if (DISPLAY_VER(dev_priv) >= 14) { + encoder->enable_clock = intel_cx0pll_enable; + encoder->disable_clock = intel_cx0pll_disable; encoder->get_config = mtl_ddi_get_config; } else if (IS_DG2(dev_priv)) { encoder->enable_clock = intel_mpllb_enable; diff --git a/drivers/gpu/drm/i915/display/intel_dp.c b/drivers/gpu/drm/i915/display/intel_dp.c index 70b06806ec0d..db32799b5f46 100644 --- a/drivers/gpu/drm/i915/display/intel_dp.c +++ b/drivers/gpu/drm/i915/display/intel_dp.c @@ -420,6 +420,11 @@ static int ehl_max_source_rate(struct intel_dp *intel_dp) return 810000; } +static int mtl_max_source_rate(struct intel_dp *intel_dp) +{ + return intel_dp_is_edp(intel_dp) ? 675000 : 810000; +} + static int vbt_max_link_rate(struct intel_dp *intel_dp) { struct intel_encoder *encoder = &dp_to_dig_port(intel_dp)->base; @@ -444,6 +449,10 @@ static void intel_dp_set_source_rates(struct intel_dp *intel_dp) { /* The values must be in increasing order */ + static const int mtl_rates[] = { + 162000, 216000, 243000, 270000, 324000, 432000, 540000, 675000, + 810000, + }; static const int icl_rates[] = { 162000, 216000, 270000, 324000, 432000, 540000, 648000, 810000, 1000000, 1350000, @@ -469,7 +478,11 @@ intel_dp_set_source_rates(struct intel_dp *intel_dp) drm_WARN_ON(&dev_priv->drm, intel_dp->source_rates || intel_dp->num_source_rates); - if (DISPLAY_VER(dev_priv) >= 11) { + if (DISPLAY_VER(dev_priv) >= 14) { + source_rates = mtl_rates; + size = ARRAY_SIZE(mtl_rates); + max_rate = mtl_max_source_rate(intel_dp); + } else if (DISPLAY_VER(dev_priv) >= 11) { source_rates = icl_rates; size = ARRAY_SIZE(icl_rates); if (IS_DG2(dev_priv)) diff --git a/drivers/gpu/drm/i915/display/intel_dpll.c b/drivers/gpu/drm/i915/display/intel_dpll.c index 73f541050913..d6fcdf4eba0e 100644 --- a/drivers/gpu/drm/i915/display/intel_dpll.c +++ b/drivers/gpu/drm/i915/display/intel_dpll.c @@ -1533,6 +1533,8 @@ intel_dpll_init_clock_hook(struct drm_i915_private *dev_priv) { if (DISPLAY_VER(dev_priv) >= 14) dev_priv->display.funcs.dpll = &mtl_dpll_funcs; + else if (DISPLAY_VER(dev_priv) >= 14) + dev_priv->display.funcs.dpll = &mtl_dpll_funcs; else if (IS_DG2(dev_priv)) dev_priv->display.funcs.dpll = &dg2_dpll_funcs; else if (DISPLAY_VER(dev_priv) >= 9 || HAS_DDI(dev_priv)) diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index 5003a5ffbc6a..5e6ff9f2aa10 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -2121,6 +2121,11 @@ #define TRANS_PUSH_EN REG_BIT(31) #define TRANS_PUSH_SEND REG_BIT(30) +/* DDI Buffer Control */ +#define _DDI_CLK_VALFREQ_A 0x64030 +#define _DDI_CLK_VALFREQ_B 0x64130 +#define DDI_CLK_VALFREQ(port) _MMIO_PORT(port, _DDI_CLK_VALFREQ_A, _DDI_CLK_VALFREQ_B) + /* * HSW+ eDP PSR registers * @@ -8375,4 +8380,140 @@ enum skl_power_gate { #define MTL_MEDIA_GSI_BASE 0x380000 +#define PUNIT_MMIO_CR_POC_STRAPS _MMIO(0x281078) +#define NUM_TILES_MASK REG_GENMASK(1, 0) +#define CD_ALIVE REG_BIT(2) +#define SOCKET_ID_MASK REG_GENMASK(7, 3) + +/* Define the BAR and offset for the accelerator fabric CSRs */ +#define CD_BASE_OFFSET 0x291000 +#define CD_BAR_SIZE (256 * 1024) + +/* + * In general, the i915 should not touch the IAF registers. The registers + * will be passed as an IO resource via the MFD interface. However, it + * is necessary to put the IRQ bits in a known state, before the MFD cell + * is registered. + * + * So define these registers for i915 usage. + */ +#define CPORT_MBDB_CSRS (CD_BASE_OFFSET + 0x6000) +#define CPORT_MBDB_CSRS_END (CPORT_MBDB_CSRS + 0x1000) +#define CPORT_MBDB_INT_ENABLE_MASK _MMIO(CPORT_MBDB_CSRS + 0x8) + +#define _XELPDP_PORT_M2P_MSGBUS_CTL_LN0_A 0x64040 +#define _XELPDP_PORT_M2P_MSGBUS_CTL_LN0_B 0x64140 +#define _XELPDP_PORT_M2P_MSGBUS_CTL_LN0_USBC1 0x16F240 +#define _XELPDP_PORT_M2P_MSGBUS_CTL_LN0_USBC2 0x16F440 +#define _XELPDP_PORT_M2P_MSGBUS_CTL_LN0_USBC3 0x16F640 +#define _XELPDP_PORT_M2P_MSGBUS_CTL_LN0_USBC4 0x16F840 +#define _XELPDP_PORT_M2P_MSGBUS_CTL(port, lane) (_PICK(port, \ + [PORT_A] = _XELPDP_PORT_M2P_MSGBUS_CTL_LN0_A, \ + [PORT_B] = _XELPDP_PORT_M2P_MSGBUS_CTL_LN0_B, \ + [PORT_TC1] = _XELPDP_PORT_M2P_MSGBUS_CTL_LN0_USBC1, \ + [PORT_TC2] = _XELPDP_PORT_M2P_MSGBUS_CTL_LN0_USBC2, \ + [PORT_TC3] = _XELPDP_PORT_M2P_MSGBUS_CTL_LN0_USBC3, \ + [PORT_TC4] = _XELPDP_PORT_M2P_MSGBUS_CTL_LN0_USBC4) + ((lane) * 4)) + +#define XELPDP_PORT_M2P_MSGBUS_CTL(port, lane) _MMIO(_XELPDP_PORT_M2P_MSGBUS_CTL(port, lane)) +#define XELPDP_PORT_M2P_TRANSACTION_PENDING REG_BIT(31) +#define XELPDP_PORT_M2P_COMMAND_TYPE_MASK REG_GENMASK(30, 27) +#define XELPDP_PORT_M2P_COMMAND_WRITE_UNCOMMITTED REG_FIELD_PREP(XELPDP_PORT_M2P_COMMAND_TYPE_MASK, 0x1) +#define XELPDP_PORT_M2P_COMMAND_WRITE_COMMITTED REG_FIELD_PREP(XELPDP_PORT_M2P_COMMAND_TYPE_MASK, 0x2) +#define XELPDP_PORT_M2P_COMMAND_READ REG_FIELD_PREP(XELPDP_PORT_M2P_COMMAND_TYPE_MASK, 0x3) +#define XELPDP_PORT_M2P_DATA_MASK REG_GENMASK(23, 16) +#define XELPDP_PORT_M2P_DATA(val) REG_FIELD_PREP(XELPDP_PORT_M2P_DATA_MASK, val) +#define XELPDP_PORT_M2P_TRANSACTION_RESET REG_BIT(15) +#define XELPDP_PORT_M2P_ADDRESS_MASK REG_GENMASK(11, 0) +#define XELPDP_PORT_M2P_ADDRESS(val) REG_FIELD_PREP(XELPDP_PORT_M2P_ADDRESS_MASK, val) + +#define XELPDP_PORT_P2M_MSGBUS_STATUS(port, lane) _MMIO(_XELPDP_PORT_M2P_MSGBUS_CTL(port, lane) + 8) +#define XELPDP_PORT_P2M_RESPONSE_READY REG_BIT(31) +#define XELPDP_PORT_P2M_COMMAND_TYPE_MASK REG_GENMASK(30, 27) +#define XELPDP_PORT_P2M_COMMAND_READ_ACK 0x4 +#define XELPDP_PORT_P2M_COMMAND_WRITE_ACK 0x5 +#define XELPDP_PORT_P2M_DATA_MASK REG_GENMASK(23, 16) +#define XELPDP_PORT_P2M_DATA(val) REG_FIELD_PREP(XELPDP_PORT_P2M_DATA_MASK, val) +#define XELPDP_PORT_P2M_ERROR_SET REG_BIT(15) + +#define XELPDP_MSGBUS_TIMEOUT_SLOW 1 +#define XELPDP_MSGBUS_TIMEOUT_FAST_US 2 +#define XELPDP_PCLK_PLL_ENABLE_TIMEOUT_US 3200 +#define XELPDP_PCLK_PLL_DISABLE_TIMEOUT_US 20 +#define XELPDP_PORT_BUF_SOC_READY_TIMEOUT_US 100 +#define XELPDP_PORT_RESET_START_TIMEOUT_US 5 +#define XELPDP_PORT_RESET_END_TIMEOUT 15 +#define XELPDP_REFCLK_ENABLE_TIMEOUT_US 1 + +#define _XELPDP_PORT_BUF_CTL1_LN0_A 0x64004 +#define _XELPDP_PORT_BUF_CTL1_LN0_B 0x64104 +#define _XELPDP_PORT_BUF_CTL1_LN0_USBC1 0x16F200 +#define _XELPDP_PORT_BUF_CTL1_LN0_USBC2 0x16F400 +#define _XELPDP_PORT_BUF_CTL1_LN0_USBC3 0x16F600 +#define _XELPDP_PORT_BUF_CTL1_LN0_USBC4 0x16F800 +#define _XELPDP_PORT_BUF_CTL1(port) (_PICK(port, \ + [PORT_A] = _XELPDP_PORT_BUF_CTL1_LN0_A, \ + [PORT_B] = _XELPDP_PORT_BUF_CTL1_LN0_B, \ + [PORT_TC1] = _XELPDP_PORT_BUF_CTL1_LN0_USBC1, \ + [PORT_TC2] = _XELPDP_PORT_BUF_CTL1_LN0_USBC2, \ + [PORT_TC3] = _XELPDP_PORT_BUF_CTL1_LN0_USBC3, \ + [PORT_TC4] = _XELPDP_PORT_BUF_CTL1_LN0_USBC4)) + +#define XELPDP_PORT_BUF_CTL1(port) _MMIO(_XELPDP_PORT_BUF_CTL1(port)) +#define XELPDP_PORT_BUF_SOC_PHY_READY REG_BIT(24) +#define XELPDP_PORT_REVERSAL REG_BIT(16) +#define XELPDP_PORT_WIDTH_MASK REG_GENMASK(3, 1) +#define XELPDP_PORT_WIDTH(val) REG_FIELD_PREP(XELPDP_PORT_WIDTH_MASK, val) + +#define XELPDP_PORT_BUF_CTL2(port) _MMIO(_XELPDP_PORT_BUF_CTL1(port) + 4) +#define XELPDP_LANE0_PIPE_RESET REG_BIT(31) +#define XELPDP_LANE1_PIPE_RESET REG_BIT(30) +#define XELPDP_LANE0_PHY_CURRENT_STATUS REG_BIT(29) +#define XELPDP_LANE1_PHY_CURRENT_STATUS REG_BIT(28) +#define XELPDP_LANE0_POWERDOWN_UPDATE REG_BIT(25) +#define XELPDP_LANE1_POWERDOWN_UPDATE REG_BIT(24) +#define XELPDP_LANE0_POWERDOWN_NEW_STATE_MASK REG_GENMASK(23, 20) +#define XELPDP_LANE0_POWERDOWN_NEW_STATE(val) REG_FIELD_PREP(XELPDP_LANE0_POWERDOWN_NEW_STATE_MASK, val) +#define XELPDP_LANE1_POWERDOWN_NEW_STATE_MASK REG_GENMASK(19, 16) +#define XELPDP_LANE1_POWERDOWN_NEW_STATE(val) REG_FIELD_PREP(XELPDP_LANE1_POWERDOWN_NEW_STATE_MASK, val) +#define XELPDP_POWER_STATE_READY_MASK REG_GENMASK(7, 4) +#define XELPDP_POWER_STATE_READY(val) REG_FIELD_PREP(XELPDP_POWER_STATE_READY_MASK, val) + +#define XELPDP_PORT_BUF_CTL3(port) _MMIO(_XELPDP_PORT_BUF_CTL1(port) + 8) +#define XELPDP_PLL_LANE_STAGGERING_DELAY_MASK REG_GENMASK(15, 8) +#define XELPDP_PLL_LANE_STAGGERING_DELAY(val) REG_FIELD_PREP(XELPDP_PLL_LANE_STAGGERING_DELAY_MASK, val) +#define XELPDP_POWER_STATE_ACTIVE_MASK REG_GENMASK(3, 0) +#define XELPDP_POWER_STATE_ACTIVE(val) REG_FIELD_PREP(XELPDP_POWER_STATE_ACTIVE_MASK, val) + +#define _XELPDP_PORT_CLOCK_CTL_A 0x640E0 +#define _XELPDP_PORT_CLOCK_CTL_B 0x641E0 +#define _XELPDP_PORT_CLOCK_CTL_USBC1 0x16F260 +#define _XELPDP_PORT_CLOCK_CTL_USBC2 0x16F460 +#define _XELPDP_PORT_CLOCK_CTL_USBC3 0x16F660 +#define _XELPDP_PORT_CLOCK_CTL_USBC4 0x16F860 +#define XELPDP_PORT_CLOCK_CTL(port) _MMIO(_PICK(port, \ + [PORT_A] = _XELPDP_PORT_CLOCK_CTL_A, \ + [PORT_B] = _XELPDP_PORT_CLOCK_CTL_B, \ + [PORT_TC1] = _XELPDP_PORT_CLOCK_CTL_USBC1, \ + [PORT_TC2] = _XELPDP_PORT_CLOCK_CTL_USBC2, \ + [PORT_TC3] = _XELPDP_PORT_CLOCK_CTL_USBC3, \ + [PORT_TC4] = _XELPDP_PORT_CLOCK_CTL_USBC4)) + +#define XELPDP_LANE0_PCLK_PLL_REQUEST REG_BIT(31) +#define XELPDP_LANE0_PCLK_PLL_ACK REG_BIT(30) +#define XELPDP_LANE0_PCLK_REFCLK_REQUEST REG_BIT(29) +#define XELPDP_LANE0_PCLK_REFCLK_ACK REG_BIT(28) +#define XELPDP_LANE1_PCLK_PLL_REQUEST REG_BIT(27) +#define XELPDP_LANE1_PCLK_PLL_ACK REG_BIT(26) +#define XELPDP_LANE1_PCLK_REFCLK_REQUEST REG_BIT(25) +#define XELPDP_LANE1_PCLK_REFCLK_ACK REG_BIT(24) +#define XELPDP_DDI_CLOCK_SELECT_MASK REG_GENMASK(15, 12) +#define XELPDP_DDI_CLOCK_SELECT(val) REG_FIELD_PREP(XELPDP_DDI_CLOCK_SELECT_MASK, val) +#define XELPDP_DDI_CLOCK_SELECT_NONE 0x0 +#define XELPDP_DDI_CLOCK_SELECT_MAXPCLK 0x8 +#define XELPDP_FORWARD_CLOCK_UNGATE REG_BIT(10) +#define XELPDP_LANE1_PHY_CLOCK_SELECT REG_BIT(8) +#define XELPDP_SSC_ENABLE_PLLA REG_BIT(1) +#define XELPDP_SSC_ENABLE_PLLB REG_BIT(0) + #endif /* _I915_REG_H_ */