[Intel-gfx] [PATCH 3/5] drm/i915/mtl: Add support for C10 phy programming
Jani Nikula
jani.nikula at linux.intel.com
Fri Sep 30 09:32:01 UTC 2022
On Thu, 29 Sep 2022, Mika Kahola <mika.kahola at intel.com> wrote:
> From: Radhakrishna Sripada <radhakrishna.sripada at 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 at intel.com>
> Cc: Mika Kahola <mika.kahola at intel.com>
> Cc: Uma Shankar <uma.shankar at intel.com>
> Signed-off-by: Radhakrishna Sripada <radhakrishna.sripada at intel.com>
> Signed-off-by: Mika Kahola <mika.kahola at 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_ */
--
Jani Nikula, Intel Open Source Graphics Center
More information about the Intel-gfx
mailing list