Message ID | 20230406130221.2998457-9-mika.kahola@intel.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | drm/i915/mtl: Add Support for C10 phy | expand |
On Thu, Apr 06, 2023 at 04:02:21PM +0300, Mika Kahola wrote: Please move this patch before the HDMI one, as the latter has a dependency on this one. > From: José Roberto de Souza <jose.souza@intel.com> > > The differences between MTL and TGL DP sequences are big enough to > MTL have its own functions. > > Also it is much easier to follow MTL sequences against spec with > its own functions. > > One change worthy to mention is the move of > 'intel_display_power_get(dev_priv, dig_port->ddi_io_power_domain)'. > This call is not necessary for MTL but we have _put() counter part in > intel_ddi_post_disable_dp() that needs to balanced. > We could add a display version check on it but instead here it is > moving it to intel_ddi_pre_enable_dp() so it is executed for all > platforms in a single place and this will not cause any harm in MTL > and newer platforms. The DDI_IO power enabling has an ordering dependency wrt. a clock enabling step, so I'd prefer doing that in the order required by bspec. As in this version of the patch put() of this power reference can be skipped if it wasn't taken and so the corresponding wakeref is 0. > > v2: > - Fix logic to wait for buf idle. > - Use the right register to wait for ddi active.(RK) > v3: > - Increase wait timeout for ddi buf active (Mika) > v4: > - Increase idle timeout for ddi buf idle (Mika) > > BSpec: 65448 65505 > Acked-by: Matt Roper <matthew.d.roper@intel.com> > Signed-off-by: Satyeshwar Singh <satyeshwar.singh@intel.com> > Signed-off-by: Clint Taylor <clinton.a.taylor@intel.com> > Signed-off-by: Radhakrishna Sripada <radhakrishna.sripada@intel.com> > Signed-off-by: Ankit Nautiyal <ankit.k.nautiyal@intel.com> > Signed-off-by: José Roberto de Souza <jose.souza@intel.com> > Signed-off-by: Mika Kahola <mika.kahola@intel.com> > --- > .../gpu/drm/i915/display/intel_cx0_phy_regs.h | 8 + > drivers/gpu/drm/i915/display/intel_ddi.c | 375 +++++++++++++++++- > drivers/gpu/drm/i915/i915_reg.h | 5 + > 3 files changed, 373 insertions(+), 15 deletions(-) > > diff --git a/drivers/gpu/drm/i915/display/intel_cx0_phy_regs.h b/drivers/gpu/drm/i915/display/intel_cx0_phy_regs.h > index a72f79ea5e6c..e23f921a5168 100644 > --- a/drivers/gpu/drm/i915/display/intel_cx0_phy_regs.h > +++ b/drivers/gpu/drm/i915/display/intel_cx0_phy_regs.h > @@ -59,8 +59,16 @@ > _XELPDP_PORT_BUF_CTL1_LN0_B, \ > _XELPDP_PORT_BUF_CTL1_LN0_USBC1, \ > _XELPDP_PORT_BUF_CTL1_LN0_USBC2)) > +#define XELPDP_PORT_BUF_D2D_LINK_ENABLE REG_BIT(29) > +#define XELPDP_PORT_BUF_D2D_LINK_STATE REG_BIT(28) > #define XELPDP_PORT_BUF_SOC_PHY_READY REG_BIT(24) > +#define XELPDP_PORT_BUF_PORT_DATA_WIDTH_MASK REG_GENMASK(19, 18) > +#define XELPDP_PORT_BUF_PORT_DATA_10BIT REG_FIELD_PREP(XELPDP_PORT_BUF_PORT_DATA_WIDTH_MASK, 0) > +#define XELPDP_PORT_BUF_PORT_DATA_20BIT REG_FIELD_PREP(XELPDP_PORT_BUF_PORT_DATA_WIDTH_MASK, 1) > +#define XELPDP_PORT_BUF_PORT_DATA_40BIT REG_FIELD_PREP(XELPDP_PORT_BUF_PORT_DATA_WIDTH_MASK, 2) > #define XELPDP_PORT_REVERSAL REG_BIT(16) > +#define XELPDP_PORT_BUF_IO_SELECTION REG_BIT(11) The above is the name of the field but it should rather be the name of the field value that is XELPDP_PORT_BUF_IO_SELECT_TBT. > +#define XELPDP_PORT_BUF_PHY_IDLE REG_BIT(7) > #define XELPDP_TC_PHY_OWNERSHIP REG_BIT(6) > #define XELPDP_TCSS_POWER_REQUEST REG_BIT(5) > #define XELPDP_TCSS_POWER_STATE REG_BIT(4) > diff --git a/drivers/gpu/drm/i915/display/intel_ddi.c b/drivers/gpu/drm/i915/display/intel_ddi.c > index 20b0844b8240..c3178cac3dee 100644 > --- a/drivers/gpu/drm/i915/display/intel_ddi.c > +++ b/drivers/gpu/drm/i915/display/intel_ddi.c > @@ -40,6 +40,7 @@ > #include "intel_connector.h" > #include "intel_crtc.h" > #include "intel_cx0_phy.h" > +#include "intel_cx0_phy_regs.h" > #include "intel_ddi.h" > #include "intel_ddi_buf_trans.h" > #include "intel_de.h" > @@ -170,6 +171,18 @@ static void hsw_prepare_hdmi_ddi_buffers(struct intel_encoder *encoder, > trans->entries[level].hsw.trans2); > } > > +static void mtl_wait_ddi_buf_idle(struct drm_i915_private *i915, enum port port) > +{ > + int ret; > + > + /* FIXME: find out why Bspec's 100us timeout is too short */ > + ret = wait_for_us((intel_de_read(i915, XELPDP_PORT_BUF_CTL1(port)) & > + XELPDP_PORT_BUF_PHY_IDLE), 10000); > + if (ret) > + drm_err(&i915->drm, "Timeout waiting for DDI BUF %c to get idle\n", > + port_name(port)); > +} > + > void intel_wait_ddi_buf_idle(struct drm_i915_private *dev_priv, > enum port port) > { > @@ -197,7 +210,9 @@ static void intel_wait_ddi_buf_active(struct drm_i915_private *dev_priv, > return; > } > > - if (IS_DG2(dev_priv)) { > + if (DISPLAY_VER(dev_priv) >= 14) { > + timeout_us = 10000; > + } else if (IS_DG2(dev_priv)) { > timeout_us = 1200; > } else if (DISPLAY_VER(dev_priv) >= 12) { > if (intel_phy_is_tc(dev_priv, phy)) > @@ -208,8 +223,12 @@ static void intel_wait_ddi_buf_active(struct drm_i915_private *dev_priv, > timeout_us = 500; > } > > - ret = _wait_for(!(intel_de_read(dev_priv, DDI_BUF_CTL(port)) & > - DDI_BUF_IS_IDLE), timeout_us, 10, 10); > + if (DISPLAY_VER(dev_priv) >= 14) > + ret = _wait_for(!(intel_de_read(dev_priv, XELPDP_PORT_BUF_CTL1(port)) & XELPDP_PORT_BUF_PHY_IDLE), > + timeout_us, 10, 10); > + else > + ret = _wait_for(!(intel_de_read(dev_priv, DDI_BUF_CTL(port)) & DDI_BUF_IS_IDLE), > + timeout_us, 10, 10); > > if (ret) > drm_err(&dev_priv->drm, "Timeout waiting for DDI BUF %c to get active\n", > @@ -314,6 +333,13 @@ static void intel_ddi_init_dp_buf_reg(struct intel_encoder *encoder, > DDI_PORT_WIDTH(crtc_state->lane_count) | > DDI_BUF_TRANS_SELECT(0); > > + if (DISPLAY_VER(i915) >= 14) { > + if (intel_dp_is_uhbr(crtc_state)) > + intel_dp->DP |= DDI_BUF_PORT_DATA_40BIT; > + else > + intel_dp->DP |= DDI_BUF_PORT_DATA_10BIT; > + } > + > if (IS_ALDERLAKE_P(i915) && intel_phy_is_tc(i915, phy)) { > intel_dp->DP |= ddi_buf_phy_link_rate(crtc_state->port_clock); > if (!intel_tc_port_in_tbt_alt_mode(dig_port)) > @@ -2312,6 +2338,179 @@ static void intel_ddi_mso_configure(const struct intel_crtc_state *crtc_state) > OVERLAP_PIXELS_MASK, dss1); > } > > +static u8 mtl_get_port_width(u8 lane_count) > +{ > + switch (lane_count) { > + case 1: > + return 0; > + case 2: > + return 1; > + case 3: > + return 4; > + case 4: > + return 3; > + default: > + MISSING_CASE(lane_count); > + return 4; > + } > +} > + > +static void > +mtl_ddi_enable_d2d(struct intel_encoder *encoder) > +{ > + struct drm_i915_private *dev_priv = to_i915(encoder->base.dev); > + enum port port = encoder->port; > + > + intel_de_rmw(dev_priv, XELPDP_PORT_BUF_CTL1(port), 0, > + XELPDP_PORT_BUF_D2D_LINK_ENABLE); > + > + if (wait_for_us((intel_de_read(dev_priv, XELPDP_PORT_BUF_CTL1(port)) & > + XELPDP_PORT_BUF_D2D_LINK_STATE), 100)) { > + drm_err(&dev_priv->drm, "Timeout waiting for D2D Link enable for PORT_BUF_CTL %c\n", > + port_name(port)); > + } > +} > + > +static void mtl_port_buf_ctl_program(struct intel_encoder *encoder, > + const struct intel_crtc_state *crtc_state) > +{ > + struct drm_i915_private *i915 = to_i915(encoder->base.dev); > + struct intel_digital_port *dig_port = enc_to_dig_port(encoder); > + enum port port = encoder->port; > + u32 val; > + > + val = intel_de_read(i915, XELPDP_PORT_BUF_CTL1(port)); > + val &= ~XELPDP_PORT_WIDTH_MASK; > + val |= XELPDP_PORT_WIDTH(mtl_get_port_width(crtc_state->lane_count)); > + > + val &= ~XELPDP_PORT_BUF_PORT_DATA_WIDTH_MASK; > + if (intel_dp_is_uhbr(crtc_state)) > + val |= XELPDP_PORT_BUF_PORT_DATA_40BIT; > + else > + val |= XELPDP_PORT_BUF_PORT_DATA_10BIT; > + > + if (dig_port->saved_port_bits & DDI_BUF_PORT_REVERSAL) > + val |= XELPDP_PORT_REVERSAL; > + > + intel_de_write(i915, XELPDP_PORT_BUF_CTL1(port), val); > +} > + > +static void mtl_port_buf_ctl_io_selection(struct intel_encoder *encoder) > +{ > + struct drm_i915_private *i915 = to_i915(encoder->base.dev); > + struct intel_digital_port *dig_port = enc_to_dig_port(encoder); > + u32 val; > + > + val = intel_tc_port_in_tbt_alt_mode(dig_port) ? > + XELPDP_PORT_BUF_IO_SELECTION : 0; > + intel_de_rmw(i915, XELPDP_PORT_BUF_CTL1(encoder->port), > + XELPDP_PORT_BUF_IO_SELECTION, val); > +} > + > +static void mtl_ddi_pre_enable_dp(struct intel_atomic_state *state, > + struct intel_encoder *encoder, > + const struct intel_crtc_state *crtc_state, > + const struct drm_connector_state *conn_state) For now this is ok, but there is a lot of commonality with the hsw and tgl sequences, so these functions should be refactored/unified at one point. > +{ > + struct intel_dp *intel_dp = enc_to_intel_dp(encoder); > + bool is_mst = intel_crtc_has_type(crtc_state, INTEL_OUTPUT_DP_MST); > + > + intel_dp_set_link_params(intel_dp, > + crtc_state->port_clock, > + crtc_state->lane_count); > + > + /* > + * We only configure what the register value will be here. Actual > + * enabling happens during link training farther down. > + */ > + intel_ddi_init_dp_buf_reg(encoder, crtc_state); > + > + /* > + * 1. Enable Power Wells > + * > + * This was handled at the beginning of intel_atomic_commit_tail(), > + * before we called down into this function. > + */ > + > + /* 2. PMdemand was already set */ > + > + /* 3. Select Thunderbolt */ > + mtl_port_buf_ctl_io_selection(encoder); > + > + /* 4. Enable Panel Power if PPS is required */ > + intel_pps_on(intel_dp); > + > + /* 5. Enable the port PLL */ > + intel_ddi_enable_clock(encoder, crtc_state); > + > + /* > + * 6.a Configure Transcoder Clock Select to direct the Port clock to the > + * Transcoder. > + */ > + intel_ddi_enable_transcoder_clock(encoder, crtc_state); > + > + /* > + * 6.b If DP v2.0/128b mode - Configure TRANS_DP2_CTL register settings. > + */ > + intel_ddi_config_transcoder_dp2(encoder, crtc_state); > + > + /* > + * 6.c Configure TRANS_DDI_FUNC_CTL DDI Select, DDI Mode Select & MST > + * Transport Select > + */ > + intel_ddi_config_transcoder_func(encoder, crtc_state); > + > + /* > + * 6.e Program CoG/MSO configuration bits in DSS_CTL1 if selected. > + */ > + intel_ddi_mso_configure(crtc_state); > + > + if (!is_mst) > + intel_dp_set_power(intel_dp, DP_SET_POWER_D0); > + > + intel_dp_configure_protocol_converter(intel_dp, crtc_state); > + intel_dp_sink_set_decompression_state(intel_dp, crtc_state, true); > + /* > + * DDI FEC: "anticipates enabling FEC encoding sets the FEC_READY bit > + * in the FEC_CONFIGURATION register to 1 before initiating link > + * training > + */ > + intel_dp_sink_set_fec_ready(intel_dp, crtc_state); > + > + intel_dp_check_frl_training(intel_dp); > + intel_dp_pcon_dsc_configure(intel_dp, crtc_state); > + > + /* > + * 6. The rest of the below are substeps under the bspec's "Enable and > + * Train Display Port" step. Note that steps that are specific to > + * MST will be handled by intel_mst_pre_enable_dp() before/after it > + * calls into this function. Also intel_mst_pre_enable_dp() only calls > + * us when active_mst_links==0, so any steps designated for "single > + * stream or multi-stream master transcoder" can just be performed > + * unconditionally here. > + * > + * mtl_ddi_prepare_link_retrain() that is called by > + * intel_dp_start_link_train() will execute steps: 6.d, 6.f, 6.g, 6.h, > + * 6.i and 6.j > + * > + * 6.k Follow DisplayPort specification training sequence (see notes for > + * failure handling) > + * 6.m If DisplayPort multi-stream - Set DP_TP_CTL link training to Idle > + * Pattern, wait for 5 idle patterns (DP_TP_STATUS Min_Idles_Sent) > + * (timeout after 800 us) > + */ > + intel_dp_start_link_train(intel_dp, crtc_state); > + > + /* 6.n Set DP_TP_CTL link training to Normal */ > + if (!is_trans_port_sync_mode(crtc_state)) > + intel_dp_stop_link_train(intel_dp, crtc_state); > + > + /* 6.o Configure and enable FEC if needed */ > + intel_ddi_enable_fec(encoder, crtc_state); > + > + intel_dsc_dp_pps_write(encoder, crtc_state); > +} > + > static void tgl_ddi_pre_enable_dp(struct intel_atomic_state *state, > struct intel_encoder *encoder, > const struct intel_crtc_state *crtc_state, > @@ -2526,7 +2725,9 @@ static void intel_ddi_pre_enable_dp(struct intel_atomic_state *state, > intel_dp_128b132b_sdp_crc16(enc_to_intel_dp(encoder), > crtc_state); > > - if (DISPLAY_VER(dev_priv) >= 12) > + if (DISPLAY_VER(dev_priv) >= 14) > + mtl_ddi_pre_enable_dp(state, encoder, crtc_state, conn_state); > + else if (DISPLAY_VER(dev_priv) >= 12) > tgl_ddi_pre_enable_dp(state, encoder, crtc_state, conn_state); > else > hsw_ddi_pre_enable_dp(state, encoder, crtc_state, conn_state); > @@ -2607,8 +2808,55 @@ static void intel_ddi_pre_enable(struct intel_atomic_state *state, > } > } > > -static void intel_disable_ddi_buf(struct intel_encoder *encoder, > - const struct intel_crtc_state *crtc_state) > +static void > +mtl_ddi_disable_d2d_link(struct intel_encoder *encoder) > +{ > + struct drm_i915_private *dev_priv = to_i915(encoder->base.dev); > + enum port port = encoder->port; > + > + intel_de_rmw(dev_priv, XELPDP_PORT_BUF_CTL1(port), > + XELPDP_PORT_BUF_D2D_LINK_ENABLE, 0); > + > + if (wait_for_us(!(intel_de_read(dev_priv, XELPDP_PORT_BUF_CTL1(port)) & > + XELPDP_PORT_BUF_D2D_LINK_STATE), 100)) > + drm_err(&dev_priv->drm, "Timeout waiting for D2D Link disable for PORT_BUF_CTL %c\n", > + port_name(port)); > +} > + > +static void mtl_disable_ddi_buf(struct intel_encoder *encoder, > + const struct intel_crtc_state *crtc_state) > +{ > + struct drm_i915_private *dev_priv = to_i915(encoder->base.dev); > + enum port port = encoder->port; > + u32 val; > + > + /* 3.b Clear DDI_CTL_DE Enabel to 0. */ > + val = intel_de_read(dev_priv, DDI_BUF_CTL(port)); > + if (val & DDI_BUF_CTL_ENABLE) { > + val &= ~DDI_BUF_CTL_ENABLE; > + intel_de_write(dev_priv, DDI_BUF_CTL(port), val); > + > + /* 3.c Poll for PORT_BUF_CTL Idle Status == 1, timeout after 100us */ > + mtl_wait_ddi_buf_idle(dev_priv, port); > + } > + > + /* 3.d Disable D2D Link */ > + mtl_ddi_disable_d2d_link(encoder); > + > + /* 3.e Disable DP_TP_CTL */ > + if (intel_crtc_has_dp_encoder(crtc_state)) { > + val = intel_de_read(dev_priv, dp_tp_ctl_reg(encoder, crtc_state)); > + val &= ~(DP_TP_CTL_ENABLE | DP_TP_CTL_LINK_TRAIN_MASK); > + val |= DP_TP_CTL_LINK_TRAIN_PAT1; The spec doesn't say that that PAT1 is to be selected, so the above should just clear the enabled flag, leaving the rest of the register as-is. > + intel_de_write(dev_priv, dp_tp_ctl_reg(encoder, crtc_state), val); > + } > + > + /* 3.f Disable DP_TP_CTL FEC Enable if it is needed */ > + intel_ddi_disable_fec_state(encoder, crtc_state); > +} > + > +static void disable_ddi_buf(struct intel_encoder *encoder, > + const struct intel_crtc_state *crtc_state) > { > struct drm_i915_private *dev_priv = to_i915(encoder->base.dev); > enum port port = encoder->port; > @@ -2633,6 +2881,17 @@ static void intel_disable_ddi_buf(struct intel_encoder *encoder, > intel_wait_ddi_buf_idle(dev_priv, port); > } > > +static void intel_disable_ddi_buf(struct intel_encoder *encoder, > + const struct intel_crtc_state *crtc_state) > +{ > + struct drm_i915_private *dev_priv = to_i915(encoder->base.dev); > + > + if (DISPLAY_VER(dev_priv) >= 14) > + mtl_disable_ddi_buf(encoder, crtc_state); > + else > + disable_ddi_buf(encoder, crtc_state); > +} > + > static void intel_ddi_post_disable_dp(struct intel_atomic_state *state, > struct intel_encoder *encoder, > const struct intel_crtc_state *old_crtc_state, > @@ -2680,12 +2939,21 @@ static void intel_ddi_post_disable_dp(struct intel_atomic_state *state, > intel_pps_vdd_on(intel_dp); > intel_pps_off(intel_dp); > > - if (!intel_tc_port_in_tbt_alt_mode(dig_port)) > - intel_display_power_put(dev_priv, > - dig_port->ddi_io_power_domain, > - fetch_and_zero(&dig_port->ddi_io_wakeref)); > + if (!intel_tc_port_in_tbt_alt_mode(dig_port)) { No need for the above check. > + intel_wakeref_t wakeref = fetch_and_zero(&dig_port->ddi_io_wakeref); > + > + if (wakeref) > + intel_display_power_put(dev_priv, > + dig_port->ddi_io_power_domain, > + wakeref); > + } > > intel_ddi_disable_clock(encoder); > + > + /* De-select Thunderbolt */ > + if (DISPLAY_VER(dev_priv) >= 14) > + intel_de_rmw(dev_priv, XELPDP_PORT_BUF_CTL1(encoder->port), > + XELPDP_PORT_BUF_IO_SELECTION, 0); > } > > static void intel_ddi_post_disable_hdmi(struct intel_atomic_state *state, > @@ -2696,6 +2964,7 @@ static void intel_ddi_post_disable_hdmi(struct intel_atomic_state *state, > struct drm_i915_private *dev_priv = to_i915(encoder->base.dev); > struct intel_digital_port *dig_port = enc_to_dig_port(encoder); > struct intel_hdmi *intel_hdmi = &dig_port->hdmi; > + intel_wakeref_t wakeref; > > dig_port->set_infoframes(encoder, false, > old_crtc_state, old_conn_state); > @@ -2708,9 +2977,11 @@ static void intel_ddi_post_disable_hdmi(struct intel_atomic_state *state, > if (DISPLAY_VER(dev_priv) >= 12) > intel_ddi_disable_transcoder_clock(old_crtc_state); > > - intel_display_power_put(dev_priv, > - dig_port->ddi_io_power_domain, > - fetch_and_zero(&dig_port->ddi_io_wakeref)); > + wakeref = fetch_and_zero(&dig_port->ddi_io_wakeref); > + if (wakeref) > + intel_display_power_put(dev_priv, > + dig_port->ddi_io_power_domain, > + wakeref); > > intel_ddi_disable_clock(encoder); > > @@ -2948,13 +3219,16 @@ static void intel_enable_ddi_hdmi(struct intel_atomic_state *state, > * The same is required to be filled in PORT_BUF_CTL for C10/20 Phy. > */ > if (DISPLAY_VER(dev_priv) >= 14) { > + const struct intel_bios_encoder_data *devdata = > + intel_bios_encoder_data_lookup(dev_priv, port); > + > u32 ddi_buf = 0; > u8 lane_count = mtl_get_port_width(crtc_state->lane_count); > u32 port_buf = 0; > > port_buf |= XELPDP_PORT_WIDTH(lane_count); > > - if (intel_bios_is_lane_reversal_needed(dev_priv, port)) > + if (intel_bios_encoder_lane_reversal(devdata)) The above fixup belongs to the HDMI patch. > port_buf |= XELPDP_PORT_REVERSAL; > > intel_de_rmw(dev_priv, XELPDP_PORT_BUF_CTL1(port), 0, port_buf); > @@ -3141,6 +3415,73 @@ static void adlp_tbt_to_dp_alt_switch_wa(struct intel_encoder *encoder) > intel_dkl_phy_rmw(i915, DKL_PCS_DW5(tc_port, ln), DKL_PCS_DW5_CORE_SOFTRESET, 0); > } > > +static void mtl_ddi_prepare_link_retrain(struct intel_dp *intel_dp, > + const struct intel_crtc_state *crtc_state) > +{ > + struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp); > + struct intel_encoder *encoder = &dig_port->base; > + struct drm_i915_private *dev_priv = to_i915(encoder->base.dev); > + enum port port = encoder->port; > + u32 dp_tp_ctl, ddi_buf_ctl; > + > + /* > + * TODO: To train with only a different voltage swing entry is not > + * necessary disable and enable port > + */ > + dp_tp_ctl = intel_de_read(dev_priv, dp_tp_ctl_reg(encoder, crtc_state)); > + if (dp_tp_ctl & DP_TP_CTL_ENABLE) { > + /* Disable sequence: 3.b Clear DDI_CTL_DE enable to 0. */ > + ddi_buf_ctl = intel_de_read(dev_priv, DDI_BUF_CTL(port)); > + if (ddi_buf_ctl & DDI_BUF_CTL_ENABLE) { > + intel_de_write(dev_priv, DDI_BUF_CTL(port), > + ddi_buf_ctl & ~DDI_BUF_CTL_ENABLE); > + /* > + * Disable sequence: 3.c Poll for PORT_BUF_CTL > + * Idle Status == 1, timeout after 100us > + */ > + mtl_wait_ddi_buf_idle(dev_priv, port); > + } > + > + /* Disable sequence: 3.d Disable D2D Link */ > + mtl_ddi_disable_d2d_link(encoder); > + > + /* Disable sequence: 3.e Disable DP_TP_CTL */ > + dp_tp_ctl &= ~(DP_TP_CTL_ENABLE | DP_TP_CTL_LINK_TRAIN_MASK); > + dp_tp_ctl |= DP_TP_CTL_LINK_TRAIN_PAT1; > + intel_de_write(dev_priv, dp_tp_ctl_reg(encoder, crtc_state), dp_tp_ctl); > + intel_de_posting_read(dev_priv, dp_tp_ctl_reg(encoder, crtc_state)); The above looks like open-coding mtl_disable_ddi_buf(), should just call that function instead. > + } > + > + /* 6.d Configure and enable DP_TP_CTL with link training pattern 1 selected */ > + dp_tp_ctl = DP_TP_CTL_ENABLE | DP_TP_CTL_LINK_TRAIN_PAT1; > + if (intel_crtc_has_type(crtc_state, INTEL_OUTPUT_DP_MST)) { > + dp_tp_ctl |= DP_TP_CTL_MODE_MST; > + } else { > + dp_tp_ctl |= DP_TP_CTL_MODE_SST; > + if (drm_dp_enhanced_frame_cap(intel_dp->dpcd)) > + dp_tp_ctl |= DP_TP_CTL_ENHANCED_FRAME_ENABLE; > + } > + intel_de_write(dev_priv, dp_tp_ctl_reg(encoder, crtc_state), dp_tp_ctl); > + intel_de_posting_read(dev_priv, dp_tp_ctl_reg(encoder, crtc_state)); > + > + /* 6.f Enable D2D Link */ > + mtl_ddi_enable_d2d(encoder); > + > + /* 6.g Configure voltage swing and related IO settings */ > + encoder->set_signal_levels(encoder, crtc_state); > + > + /* 6.h Configure PORT_BUF_CTL1 */ > + mtl_port_buf_ctl_program(encoder, crtc_state); > + > + /* 6.i Configure and enable DDI_CTL_DE to start sending valid data to port slice */ > + intel_dp->DP |= DDI_BUF_CTL_ENABLE; > + intel_de_write(dev_priv, DDI_BUF_CTL(port), intel_dp->DP); > + intel_de_posting_read(dev_priv, DDI_BUF_CTL(port)); > + > + /* 6.j Poll for PORT_BUF_CTL Idle Status == 0, timeout after 100 us */ > + intel_wait_ddi_buf_active(dev_priv, port); > +} > + > static void intel_ddi_prepare_link_retrain(struct intel_dp *intel_dp, > const struct intel_crtc_state *crtc_state) > { > @@ -3912,6 +4253,7 @@ static const struct drm_encoder_funcs intel_ddi_funcs = { > static struct intel_connector * > intel_ddi_init_dp_connector(struct intel_digital_port *dig_port) > { > + struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev); > struct intel_connector *connector; > enum port port = dig_port->base.port; > > @@ -3920,7 +4262,10 @@ intel_ddi_init_dp_connector(struct intel_digital_port *dig_port) > return NULL; > > dig_port->dp.output_reg = DDI_BUF_CTL(port); > - dig_port->dp.prepare_link_retrain = intel_ddi_prepare_link_retrain; > + if (DISPLAY_VER(i915) >= 14) > + dig_port->dp.prepare_link_retrain = mtl_ddi_prepare_link_retrain; > + else > + dig_port->dp.prepare_link_retrain = intel_ddi_prepare_link_retrain; > dig_port->dp.set_link_train = intel_ddi_set_link_train; > dig_port->dp.set_idle_link_train = intel_ddi_set_idle_link_train; > > diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h > index c4d363248bd2..62f88c6c3a4c 100644 > --- a/drivers/gpu/drm/i915/i915_reg.h > +++ b/drivers/gpu/drm/i915/i915_reg.h > @@ -5658,11 +5658,16 @@ enum skl_power_gate { > /* DDI Buffer Control */ > #define _DDI_BUF_CTL_A 0x64000 > #define _DDI_BUF_CTL_B 0x64100 > +/* Known as DDI_CTL_DE in MTL+ */ > #define DDI_BUF_CTL(port) _MMIO_PORT(port, _DDI_BUF_CTL_A, _DDI_BUF_CTL_B) > #define DDI_BUF_CTL_ENABLE (1 << 31) > #define DDI_BUF_TRANS_SELECT(n) ((n) << 24) > #define DDI_BUF_EMP_MASK (0xf << 24) > #define DDI_BUF_PHY_LINK_RATE(r) ((r) << 20) > +#define DDI_BUF_PORT_DATA_MASK REG_GENMASK(19, 18) > +#define DDI_BUF_PORT_DATA_10BIT REG_FIELD_PREP(DDI_BUF_PORT_DATA_MASK, 0) > +#define DDI_BUF_PORT_DATA_20BIT REG_FIELD_PREP(DDI_BUF_PORT_DATA_MASK, 1) > +#define DDI_BUF_PORT_DATA_40BIT REG_FIELD_PREP(DDI_BUF_PORT_DATA_MASK, 2) > #define DDI_BUF_PORT_REVERSAL (1 << 16) > #define DDI_BUF_IS_IDLE (1 << 7) > #define DDI_BUF_CTL_TC_PHY_OWNERSHIP REG_BIT(6) > -- > 2.34.1 >
diff --git a/drivers/gpu/drm/i915/display/intel_cx0_phy_regs.h b/drivers/gpu/drm/i915/display/intel_cx0_phy_regs.h index a72f79ea5e6c..e23f921a5168 100644 --- a/drivers/gpu/drm/i915/display/intel_cx0_phy_regs.h +++ b/drivers/gpu/drm/i915/display/intel_cx0_phy_regs.h @@ -59,8 +59,16 @@ _XELPDP_PORT_BUF_CTL1_LN0_B, \ _XELPDP_PORT_BUF_CTL1_LN0_USBC1, \ _XELPDP_PORT_BUF_CTL1_LN0_USBC2)) +#define XELPDP_PORT_BUF_D2D_LINK_ENABLE REG_BIT(29) +#define XELPDP_PORT_BUF_D2D_LINK_STATE REG_BIT(28) #define XELPDP_PORT_BUF_SOC_PHY_READY REG_BIT(24) +#define XELPDP_PORT_BUF_PORT_DATA_WIDTH_MASK REG_GENMASK(19, 18) +#define XELPDP_PORT_BUF_PORT_DATA_10BIT REG_FIELD_PREP(XELPDP_PORT_BUF_PORT_DATA_WIDTH_MASK, 0) +#define XELPDP_PORT_BUF_PORT_DATA_20BIT REG_FIELD_PREP(XELPDP_PORT_BUF_PORT_DATA_WIDTH_MASK, 1) +#define XELPDP_PORT_BUF_PORT_DATA_40BIT REG_FIELD_PREP(XELPDP_PORT_BUF_PORT_DATA_WIDTH_MASK, 2) #define XELPDP_PORT_REVERSAL REG_BIT(16) +#define XELPDP_PORT_BUF_IO_SELECTION REG_BIT(11) +#define XELPDP_PORT_BUF_PHY_IDLE REG_BIT(7) #define XELPDP_TC_PHY_OWNERSHIP REG_BIT(6) #define XELPDP_TCSS_POWER_REQUEST REG_BIT(5) #define XELPDP_TCSS_POWER_STATE REG_BIT(4) diff --git a/drivers/gpu/drm/i915/display/intel_ddi.c b/drivers/gpu/drm/i915/display/intel_ddi.c index 20b0844b8240..c3178cac3dee 100644 --- a/drivers/gpu/drm/i915/display/intel_ddi.c +++ b/drivers/gpu/drm/i915/display/intel_ddi.c @@ -40,6 +40,7 @@ #include "intel_connector.h" #include "intel_crtc.h" #include "intel_cx0_phy.h" +#include "intel_cx0_phy_regs.h" #include "intel_ddi.h" #include "intel_ddi_buf_trans.h" #include "intel_de.h" @@ -170,6 +171,18 @@ static void hsw_prepare_hdmi_ddi_buffers(struct intel_encoder *encoder, trans->entries[level].hsw.trans2); } +static void mtl_wait_ddi_buf_idle(struct drm_i915_private *i915, enum port port) +{ + int ret; + + /* FIXME: find out why Bspec's 100us timeout is too short */ + ret = wait_for_us((intel_de_read(i915, XELPDP_PORT_BUF_CTL1(port)) & + XELPDP_PORT_BUF_PHY_IDLE), 10000); + if (ret) + drm_err(&i915->drm, "Timeout waiting for DDI BUF %c to get idle\n", + port_name(port)); +} + void intel_wait_ddi_buf_idle(struct drm_i915_private *dev_priv, enum port port) { @@ -197,7 +210,9 @@ static void intel_wait_ddi_buf_active(struct drm_i915_private *dev_priv, return; } - if (IS_DG2(dev_priv)) { + if (DISPLAY_VER(dev_priv) >= 14) { + timeout_us = 10000; + } else if (IS_DG2(dev_priv)) { timeout_us = 1200; } else if (DISPLAY_VER(dev_priv) >= 12) { if (intel_phy_is_tc(dev_priv, phy)) @@ -208,8 +223,12 @@ static void intel_wait_ddi_buf_active(struct drm_i915_private *dev_priv, timeout_us = 500; } - ret = _wait_for(!(intel_de_read(dev_priv, DDI_BUF_CTL(port)) & - DDI_BUF_IS_IDLE), timeout_us, 10, 10); + if (DISPLAY_VER(dev_priv) >= 14) + ret = _wait_for(!(intel_de_read(dev_priv, XELPDP_PORT_BUF_CTL1(port)) & XELPDP_PORT_BUF_PHY_IDLE), + timeout_us, 10, 10); + else + ret = _wait_for(!(intel_de_read(dev_priv, DDI_BUF_CTL(port)) & DDI_BUF_IS_IDLE), + timeout_us, 10, 10); if (ret) drm_err(&dev_priv->drm, "Timeout waiting for DDI BUF %c to get active\n", @@ -314,6 +333,13 @@ static void intel_ddi_init_dp_buf_reg(struct intel_encoder *encoder, DDI_PORT_WIDTH(crtc_state->lane_count) | DDI_BUF_TRANS_SELECT(0); + if (DISPLAY_VER(i915) >= 14) { + if (intel_dp_is_uhbr(crtc_state)) + intel_dp->DP |= DDI_BUF_PORT_DATA_40BIT; + else + intel_dp->DP |= DDI_BUF_PORT_DATA_10BIT; + } + if (IS_ALDERLAKE_P(i915) && intel_phy_is_tc(i915, phy)) { intel_dp->DP |= ddi_buf_phy_link_rate(crtc_state->port_clock); if (!intel_tc_port_in_tbt_alt_mode(dig_port)) @@ -2312,6 +2338,179 @@ static void intel_ddi_mso_configure(const struct intel_crtc_state *crtc_state) OVERLAP_PIXELS_MASK, dss1); } +static u8 mtl_get_port_width(u8 lane_count) +{ + switch (lane_count) { + case 1: + return 0; + case 2: + return 1; + case 3: + return 4; + case 4: + return 3; + default: + MISSING_CASE(lane_count); + return 4; + } +} + +static void +mtl_ddi_enable_d2d(struct intel_encoder *encoder) +{ + struct drm_i915_private *dev_priv = to_i915(encoder->base.dev); + enum port port = encoder->port; + + intel_de_rmw(dev_priv, XELPDP_PORT_BUF_CTL1(port), 0, + XELPDP_PORT_BUF_D2D_LINK_ENABLE); + + if (wait_for_us((intel_de_read(dev_priv, XELPDP_PORT_BUF_CTL1(port)) & + XELPDP_PORT_BUF_D2D_LINK_STATE), 100)) { + drm_err(&dev_priv->drm, "Timeout waiting for D2D Link enable for PORT_BUF_CTL %c\n", + port_name(port)); + } +} + +static void mtl_port_buf_ctl_program(struct intel_encoder *encoder, + const struct intel_crtc_state *crtc_state) +{ + struct drm_i915_private *i915 = to_i915(encoder->base.dev); + struct intel_digital_port *dig_port = enc_to_dig_port(encoder); + enum port port = encoder->port; + u32 val; + + val = intel_de_read(i915, XELPDP_PORT_BUF_CTL1(port)); + val &= ~XELPDP_PORT_WIDTH_MASK; + val |= XELPDP_PORT_WIDTH(mtl_get_port_width(crtc_state->lane_count)); + + val &= ~XELPDP_PORT_BUF_PORT_DATA_WIDTH_MASK; + if (intel_dp_is_uhbr(crtc_state)) + val |= XELPDP_PORT_BUF_PORT_DATA_40BIT; + else + val |= XELPDP_PORT_BUF_PORT_DATA_10BIT; + + if (dig_port->saved_port_bits & DDI_BUF_PORT_REVERSAL) + val |= XELPDP_PORT_REVERSAL; + + intel_de_write(i915, XELPDP_PORT_BUF_CTL1(port), val); +} + +static void mtl_port_buf_ctl_io_selection(struct intel_encoder *encoder) +{ + struct drm_i915_private *i915 = to_i915(encoder->base.dev); + struct intel_digital_port *dig_port = enc_to_dig_port(encoder); + u32 val; + + val = intel_tc_port_in_tbt_alt_mode(dig_port) ? + XELPDP_PORT_BUF_IO_SELECTION : 0; + intel_de_rmw(i915, XELPDP_PORT_BUF_CTL1(encoder->port), + XELPDP_PORT_BUF_IO_SELECTION, val); +} + +static void mtl_ddi_pre_enable_dp(struct intel_atomic_state *state, + struct intel_encoder *encoder, + const struct intel_crtc_state *crtc_state, + const struct drm_connector_state *conn_state) +{ + struct intel_dp *intel_dp = enc_to_intel_dp(encoder); + bool is_mst = intel_crtc_has_type(crtc_state, INTEL_OUTPUT_DP_MST); + + intel_dp_set_link_params(intel_dp, + crtc_state->port_clock, + crtc_state->lane_count); + + /* + * We only configure what the register value will be here. Actual + * enabling happens during link training farther down. + */ + intel_ddi_init_dp_buf_reg(encoder, crtc_state); + + /* + * 1. Enable Power Wells + * + * This was handled at the beginning of intel_atomic_commit_tail(), + * before we called down into this function. + */ + + /* 2. PMdemand was already set */ + + /* 3. Select Thunderbolt */ + mtl_port_buf_ctl_io_selection(encoder); + + /* 4. Enable Panel Power if PPS is required */ + intel_pps_on(intel_dp); + + /* 5. Enable the port PLL */ + intel_ddi_enable_clock(encoder, crtc_state); + + /* + * 6.a Configure Transcoder Clock Select to direct the Port clock to the + * Transcoder. + */ + intel_ddi_enable_transcoder_clock(encoder, crtc_state); + + /* + * 6.b If DP v2.0/128b mode - Configure TRANS_DP2_CTL register settings. + */ + intel_ddi_config_transcoder_dp2(encoder, crtc_state); + + /* + * 6.c Configure TRANS_DDI_FUNC_CTL DDI Select, DDI Mode Select & MST + * Transport Select + */ + intel_ddi_config_transcoder_func(encoder, crtc_state); + + /* + * 6.e Program CoG/MSO configuration bits in DSS_CTL1 if selected. + */ + intel_ddi_mso_configure(crtc_state); + + if (!is_mst) + intel_dp_set_power(intel_dp, DP_SET_POWER_D0); + + intel_dp_configure_protocol_converter(intel_dp, crtc_state); + intel_dp_sink_set_decompression_state(intel_dp, crtc_state, true); + /* + * DDI FEC: "anticipates enabling FEC encoding sets the FEC_READY bit + * in the FEC_CONFIGURATION register to 1 before initiating link + * training + */ + intel_dp_sink_set_fec_ready(intel_dp, crtc_state); + + intel_dp_check_frl_training(intel_dp); + intel_dp_pcon_dsc_configure(intel_dp, crtc_state); + + /* + * 6. The rest of the below are substeps under the bspec's "Enable and + * Train Display Port" step. Note that steps that are specific to + * MST will be handled by intel_mst_pre_enable_dp() before/after it + * calls into this function. Also intel_mst_pre_enable_dp() only calls + * us when active_mst_links==0, so any steps designated for "single + * stream or multi-stream master transcoder" can just be performed + * unconditionally here. + * + * mtl_ddi_prepare_link_retrain() that is called by + * intel_dp_start_link_train() will execute steps: 6.d, 6.f, 6.g, 6.h, + * 6.i and 6.j + * + * 6.k Follow DisplayPort specification training sequence (see notes for + * failure handling) + * 6.m If DisplayPort multi-stream - Set DP_TP_CTL link training to Idle + * Pattern, wait for 5 idle patterns (DP_TP_STATUS Min_Idles_Sent) + * (timeout after 800 us) + */ + intel_dp_start_link_train(intel_dp, crtc_state); + + /* 6.n Set DP_TP_CTL link training to Normal */ + if (!is_trans_port_sync_mode(crtc_state)) + intel_dp_stop_link_train(intel_dp, crtc_state); + + /* 6.o Configure and enable FEC if needed */ + intel_ddi_enable_fec(encoder, crtc_state); + + intel_dsc_dp_pps_write(encoder, crtc_state); +} + static void tgl_ddi_pre_enable_dp(struct intel_atomic_state *state, struct intel_encoder *encoder, const struct intel_crtc_state *crtc_state, @@ -2526,7 +2725,9 @@ static void intel_ddi_pre_enable_dp(struct intel_atomic_state *state, intel_dp_128b132b_sdp_crc16(enc_to_intel_dp(encoder), crtc_state); - if (DISPLAY_VER(dev_priv) >= 12) + if (DISPLAY_VER(dev_priv) >= 14) + mtl_ddi_pre_enable_dp(state, encoder, crtc_state, conn_state); + else if (DISPLAY_VER(dev_priv) >= 12) tgl_ddi_pre_enable_dp(state, encoder, crtc_state, conn_state); else hsw_ddi_pre_enable_dp(state, encoder, crtc_state, conn_state); @@ -2607,8 +2808,55 @@ static void intel_ddi_pre_enable(struct intel_atomic_state *state, } } -static void intel_disable_ddi_buf(struct intel_encoder *encoder, - const struct intel_crtc_state *crtc_state) +static void +mtl_ddi_disable_d2d_link(struct intel_encoder *encoder) +{ + struct drm_i915_private *dev_priv = to_i915(encoder->base.dev); + enum port port = encoder->port; + + intel_de_rmw(dev_priv, XELPDP_PORT_BUF_CTL1(port), + XELPDP_PORT_BUF_D2D_LINK_ENABLE, 0); + + if (wait_for_us(!(intel_de_read(dev_priv, XELPDP_PORT_BUF_CTL1(port)) & + XELPDP_PORT_BUF_D2D_LINK_STATE), 100)) + drm_err(&dev_priv->drm, "Timeout waiting for D2D Link disable for PORT_BUF_CTL %c\n", + port_name(port)); +} + +static void mtl_disable_ddi_buf(struct intel_encoder *encoder, + const struct intel_crtc_state *crtc_state) +{ + struct drm_i915_private *dev_priv = to_i915(encoder->base.dev); + enum port port = encoder->port; + u32 val; + + /* 3.b Clear DDI_CTL_DE Enabel to 0. */ + val = intel_de_read(dev_priv, DDI_BUF_CTL(port)); + if (val & DDI_BUF_CTL_ENABLE) { + val &= ~DDI_BUF_CTL_ENABLE; + intel_de_write(dev_priv, DDI_BUF_CTL(port), val); + + /* 3.c Poll for PORT_BUF_CTL Idle Status == 1, timeout after 100us */ + mtl_wait_ddi_buf_idle(dev_priv, port); + } + + /* 3.d Disable D2D Link */ + mtl_ddi_disable_d2d_link(encoder); + + /* 3.e Disable DP_TP_CTL */ + if (intel_crtc_has_dp_encoder(crtc_state)) { + val = intel_de_read(dev_priv, dp_tp_ctl_reg(encoder, crtc_state)); + val &= ~(DP_TP_CTL_ENABLE | DP_TP_CTL_LINK_TRAIN_MASK); + val |= DP_TP_CTL_LINK_TRAIN_PAT1; + intel_de_write(dev_priv, dp_tp_ctl_reg(encoder, crtc_state), val); + } + + /* 3.f Disable DP_TP_CTL FEC Enable if it is needed */ + intel_ddi_disable_fec_state(encoder, crtc_state); +} + +static void disable_ddi_buf(struct intel_encoder *encoder, + const struct intel_crtc_state *crtc_state) { struct drm_i915_private *dev_priv = to_i915(encoder->base.dev); enum port port = encoder->port; @@ -2633,6 +2881,17 @@ static void intel_disable_ddi_buf(struct intel_encoder *encoder, intel_wait_ddi_buf_idle(dev_priv, port); } +static void intel_disable_ddi_buf(struct intel_encoder *encoder, + const struct intel_crtc_state *crtc_state) +{ + struct drm_i915_private *dev_priv = to_i915(encoder->base.dev); + + if (DISPLAY_VER(dev_priv) >= 14) + mtl_disable_ddi_buf(encoder, crtc_state); + else + disable_ddi_buf(encoder, crtc_state); +} + static void intel_ddi_post_disable_dp(struct intel_atomic_state *state, struct intel_encoder *encoder, const struct intel_crtc_state *old_crtc_state, @@ -2680,12 +2939,21 @@ static void intel_ddi_post_disable_dp(struct intel_atomic_state *state, intel_pps_vdd_on(intel_dp); intel_pps_off(intel_dp); - if (!intel_tc_port_in_tbt_alt_mode(dig_port)) - intel_display_power_put(dev_priv, - dig_port->ddi_io_power_domain, - fetch_and_zero(&dig_port->ddi_io_wakeref)); + if (!intel_tc_port_in_tbt_alt_mode(dig_port)) { + intel_wakeref_t wakeref = fetch_and_zero(&dig_port->ddi_io_wakeref); + + if (wakeref) + intel_display_power_put(dev_priv, + dig_port->ddi_io_power_domain, + wakeref); + } intel_ddi_disable_clock(encoder); + + /* De-select Thunderbolt */ + if (DISPLAY_VER(dev_priv) >= 14) + intel_de_rmw(dev_priv, XELPDP_PORT_BUF_CTL1(encoder->port), + XELPDP_PORT_BUF_IO_SELECTION, 0); } static void intel_ddi_post_disable_hdmi(struct intel_atomic_state *state, @@ -2696,6 +2964,7 @@ static void intel_ddi_post_disable_hdmi(struct intel_atomic_state *state, struct drm_i915_private *dev_priv = to_i915(encoder->base.dev); struct intel_digital_port *dig_port = enc_to_dig_port(encoder); struct intel_hdmi *intel_hdmi = &dig_port->hdmi; + intel_wakeref_t wakeref; dig_port->set_infoframes(encoder, false, old_crtc_state, old_conn_state); @@ -2708,9 +2977,11 @@ static void intel_ddi_post_disable_hdmi(struct intel_atomic_state *state, if (DISPLAY_VER(dev_priv) >= 12) intel_ddi_disable_transcoder_clock(old_crtc_state); - intel_display_power_put(dev_priv, - dig_port->ddi_io_power_domain, - fetch_and_zero(&dig_port->ddi_io_wakeref)); + wakeref = fetch_and_zero(&dig_port->ddi_io_wakeref); + if (wakeref) + intel_display_power_put(dev_priv, + dig_port->ddi_io_power_domain, + wakeref); intel_ddi_disable_clock(encoder); @@ -2948,13 +3219,16 @@ static void intel_enable_ddi_hdmi(struct intel_atomic_state *state, * The same is required to be filled in PORT_BUF_CTL for C10/20 Phy. */ if (DISPLAY_VER(dev_priv) >= 14) { + const struct intel_bios_encoder_data *devdata = + intel_bios_encoder_data_lookup(dev_priv, port); + u32 ddi_buf = 0; u8 lane_count = mtl_get_port_width(crtc_state->lane_count); u32 port_buf = 0; port_buf |= XELPDP_PORT_WIDTH(lane_count); - if (intel_bios_is_lane_reversal_needed(dev_priv, port)) + if (intel_bios_encoder_lane_reversal(devdata)) port_buf |= XELPDP_PORT_REVERSAL; intel_de_rmw(dev_priv, XELPDP_PORT_BUF_CTL1(port), 0, port_buf); @@ -3141,6 +3415,73 @@ static void adlp_tbt_to_dp_alt_switch_wa(struct intel_encoder *encoder) intel_dkl_phy_rmw(i915, DKL_PCS_DW5(tc_port, ln), DKL_PCS_DW5_CORE_SOFTRESET, 0); } +static void mtl_ddi_prepare_link_retrain(struct intel_dp *intel_dp, + const struct intel_crtc_state *crtc_state) +{ + struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp); + struct intel_encoder *encoder = &dig_port->base; + struct drm_i915_private *dev_priv = to_i915(encoder->base.dev); + enum port port = encoder->port; + u32 dp_tp_ctl, ddi_buf_ctl; + + /* + * TODO: To train with only a different voltage swing entry is not + * necessary disable and enable port + */ + dp_tp_ctl = intel_de_read(dev_priv, dp_tp_ctl_reg(encoder, crtc_state)); + if (dp_tp_ctl & DP_TP_CTL_ENABLE) { + /* Disable sequence: 3.b Clear DDI_CTL_DE enable to 0. */ + ddi_buf_ctl = intel_de_read(dev_priv, DDI_BUF_CTL(port)); + if (ddi_buf_ctl & DDI_BUF_CTL_ENABLE) { + intel_de_write(dev_priv, DDI_BUF_CTL(port), + ddi_buf_ctl & ~DDI_BUF_CTL_ENABLE); + /* + * Disable sequence: 3.c Poll for PORT_BUF_CTL + * Idle Status == 1, timeout after 100us + */ + mtl_wait_ddi_buf_idle(dev_priv, port); + } + + /* Disable sequence: 3.d Disable D2D Link */ + mtl_ddi_disable_d2d_link(encoder); + + /* Disable sequence: 3.e Disable DP_TP_CTL */ + dp_tp_ctl &= ~(DP_TP_CTL_ENABLE | DP_TP_CTL_LINK_TRAIN_MASK); + dp_tp_ctl |= DP_TP_CTL_LINK_TRAIN_PAT1; + intel_de_write(dev_priv, dp_tp_ctl_reg(encoder, crtc_state), dp_tp_ctl); + intel_de_posting_read(dev_priv, dp_tp_ctl_reg(encoder, crtc_state)); + } + + /* 6.d Configure and enable DP_TP_CTL with link training pattern 1 selected */ + dp_tp_ctl = DP_TP_CTL_ENABLE | DP_TP_CTL_LINK_TRAIN_PAT1; + if (intel_crtc_has_type(crtc_state, INTEL_OUTPUT_DP_MST)) { + dp_tp_ctl |= DP_TP_CTL_MODE_MST; + } else { + dp_tp_ctl |= DP_TP_CTL_MODE_SST; + if (drm_dp_enhanced_frame_cap(intel_dp->dpcd)) + dp_tp_ctl |= DP_TP_CTL_ENHANCED_FRAME_ENABLE; + } + intel_de_write(dev_priv, dp_tp_ctl_reg(encoder, crtc_state), dp_tp_ctl); + intel_de_posting_read(dev_priv, dp_tp_ctl_reg(encoder, crtc_state)); + + /* 6.f Enable D2D Link */ + mtl_ddi_enable_d2d(encoder); + + /* 6.g Configure voltage swing and related IO settings */ + encoder->set_signal_levels(encoder, crtc_state); + + /* 6.h Configure PORT_BUF_CTL1 */ + mtl_port_buf_ctl_program(encoder, crtc_state); + + /* 6.i Configure and enable DDI_CTL_DE to start sending valid data to port slice */ + intel_dp->DP |= DDI_BUF_CTL_ENABLE; + intel_de_write(dev_priv, DDI_BUF_CTL(port), intel_dp->DP); + intel_de_posting_read(dev_priv, DDI_BUF_CTL(port)); + + /* 6.j Poll for PORT_BUF_CTL Idle Status == 0, timeout after 100 us */ + intel_wait_ddi_buf_active(dev_priv, port); +} + static void intel_ddi_prepare_link_retrain(struct intel_dp *intel_dp, const struct intel_crtc_state *crtc_state) { @@ -3912,6 +4253,7 @@ static const struct drm_encoder_funcs intel_ddi_funcs = { static struct intel_connector * intel_ddi_init_dp_connector(struct intel_digital_port *dig_port) { + struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev); struct intel_connector *connector; enum port port = dig_port->base.port; @@ -3920,7 +4262,10 @@ intel_ddi_init_dp_connector(struct intel_digital_port *dig_port) return NULL; dig_port->dp.output_reg = DDI_BUF_CTL(port); - dig_port->dp.prepare_link_retrain = intel_ddi_prepare_link_retrain; + if (DISPLAY_VER(i915) >= 14) + dig_port->dp.prepare_link_retrain = mtl_ddi_prepare_link_retrain; + else + dig_port->dp.prepare_link_retrain = intel_ddi_prepare_link_retrain; dig_port->dp.set_link_train = intel_ddi_set_link_train; dig_port->dp.set_idle_link_train = intel_ddi_set_idle_link_train; diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index c4d363248bd2..62f88c6c3a4c 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -5658,11 +5658,16 @@ enum skl_power_gate { /* DDI Buffer Control */ #define _DDI_BUF_CTL_A 0x64000 #define _DDI_BUF_CTL_B 0x64100 +/* Known as DDI_CTL_DE in MTL+ */ #define DDI_BUF_CTL(port) _MMIO_PORT(port, _DDI_BUF_CTL_A, _DDI_BUF_CTL_B) #define DDI_BUF_CTL_ENABLE (1 << 31) #define DDI_BUF_TRANS_SELECT(n) ((n) << 24) #define DDI_BUF_EMP_MASK (0xf << 24) #define DDI_BUF_PHY_LINK_RATE(r) ((r) << 20) +#define DDI_BUF_PORT_DATA_MASK REG_GENMASK(19, 18) +#define DDI_BUF_PORT_DATA_10BIT REG_FIELD_PREP(DDI_BUF_PORT_DATA_MASK, 0) +#define DDI_BUF_PORT_DATA_20BIT REG_FIELD_PREP(DDI_BUF_PORT_DATA_MASK, 1) +#define DDI_BUF_PORT_DATA_40BIT REG_FIELD_PREP(DDI_BUF_PORT_DATA_MASK, 2) #define DDI_BUF_PORT_REVERSAL (1 << 16) #define DDI_BUF_IS_IDLE (1 << 7) #define DDI_BUF_CTL_TC_PHY_OWNERSHIP REG_BIT(6)