Message ID | 20201209042144.2281-5-airlied@gmail.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | [1/4] drm/i915: refactor cursor code out of i915_display.c | expand |
On Wed, Dec 9, 2020 at 5:22 AM Dave Airlie <airlied@gmail.com> wrote: > > From: Dave Airlie <airlied@redhat.com> > > This just refactors out the fdi code to a separate file. > > Signed-off-by: Dave Airlie <airlied@redhat.com> There's also hsw_fdi_link_train from intel_ddi.c (another fairly big file), I think that also belongs in here. It's not in the vtable because it's directly called from the hsw crt encoder. With that this looks reasonable to me. -Daniel > --- > drivers/gpu/drm/i915/Makefile | 1 + > drivers/gpu/drm/i915/display/intel_display.c | 684 +----------------- > drivers/gpu/drm/i915/display/intel_display.h | 10 + > .../drm/i915/display/intel_display_types.h | 9 + > drivers/gpu/drm/i915/display/intel_fdi.c | 682 +++++++++++++++++ > 5 files changed, 704 insertions(+), 682 deletions(-) > create mode 100644 drivers/gpu/drm/i915/display/intel_fdi.c > > diff --git a/drivers/gpu/drm/i915/Makefile b/drivers/gpu/drm/i915/Makefile > index 8b357c212ae2..f74b7c13ffbd 100644 > --- a/drivers/gpu/drm/i915/Makefile > +++ b/drivers/gpu/drm/i915/Makefile > @@ -206,6 +206,7 @@ i915-y += \ > display/intel_dpll_mgr.o \ > display/intel_dsb.o \ > display/intel_fbc.o \ > + display/intel_fdi.o \ > display/intel_fifo_underrun.o \ > display/intel_frontbuffer.o \ > display/intel_global_state.o \ > diff --git a/drivers/gpu/drm/i915/display/intel_display.c b/drivers/gpu/drm/i915/display/intel_display.c > index 788b1def61ee..5119a0040e36 100644 > --- a/drivers/gpu/drm/i915/display/intel_display.c > +++ b/drivers/gpu/drm/i915/display/intel_display.c > @@ -170,16 +170,6 @@ static void intel_update_czclk(struct drm_i915_private *dev_priv) > dev_priv->czclk_freq); > } > > -/* units of 100MHz */ > -static u32 intel_fdi_link_freq(struct drm_i915_private *dev_priv, > - const struct intel_crtc_state *pipe_config) > -{ > - if (HAS_DDI(dev_priv)) > - return pipe_config->port_clock; /* SPLL */ > - else > - return dev_priv->fdi_pll_freq; > -} > - > /* WA Display #0827: Gen9:all */ > static void > skl_wa_827(struct drm_i915_private *dev_priv, enum pipe pipe, bool enable) > @@ -3776,532 +3766,6 @@ static void icl_set_pipe_chicken(struct intel_crtc *crtc) > intel_de_write(dev_priv, PIPE_CHICKEN(pipe), tmp); > } > > -static void intel_fdi_normal_train(struct intel_crtc *crtc) > -{ > - struct drm_device *dev = crtc->base.dev; > - struct drm_i915_private *dev_priv = to_i915(dev); > - enum pipe pipe = crtc->pipe; > - i915_reg_t reg; > - u32 temp; > - > - /* enable normal train */ > - reg = FDI_TX_CTL(pipe); > - temp = intel_de_read(dev_priv, reg); > - if (IS_IVYBRIDGE(dev_priv)) { > - temp &= ~FDI_LINK_TRAIN_NONE_IVB; > - temp |= FDI_LINK_TRAIN_NONE_IVB | FDI_TX_ENHANCE_FRAME_ENABLE; > - } else { > - temp &= ~FDI_LINK_TRAIN_NONE; > - temp |= FDI_LINK_TRAIN_NONE | FDI_TX_ENHANCE_FRAME_ENABLE; > - } > - intel_de_write(dev_priv, reg, temp); > - > - reg = FDI_RX_CTL(pipe); > - temp = intel_de_read(dev_priv, reg); > - if (HAS_PCH_CPT(dev_priv)) { > - temp &= ~FDI_LINK_TRAIN_PATTERN_MASK_CPT; > - temp |= FDI_LINK_TRAIN_NORMAL_CPT; > - } else { > - temp &= ~FDI_LINK_TRAIN_NONE; > - temp |= FDI_LINK_TRAIN_NONE; > - } > - intel_de_write(dev_priv, reg, temp | FDI_RX_ENHANCE_FRAME_ENABLE); > - > - /* wait one idle pattern time */ > - intel_de_posting_read(dev_priv, reg); > - udelay(1000); > - > - /* IVB wants error correction enabled */ > - if (IS_IVYBRIDGE(dev_priv)) > - intel_de_write(dev_priv, reg, > - intel_de_read(dev_priv, reg) | FDI_FS_ERRC_ENABLE | FDI_FE_ERRC_ENABLE); > -} > - > -/* The FDI link training functions for ILK/Ibexpeak. */ > -static void ilk_fdi_link_train(struct intel_crtc *crtc, > - const struct intel_crtc_state *crtc_state) > -{ > - struct drm_device *dev = crtc->base.dev; > - struct drm_i915_private *dev_priv = to_i915(dev); > - enum pipe pipe = crtc->pipe; > - i915_reg_t reg; > - u32 temp, tries; > - > - /* FDI needs bits from pipe first */ > - assert_pipe_enabled(dev_priv, crtc_state->cpu_transcoder); > - > - /* Train 1: umask FDI RX Interrupt symbol_lock and bit_lock bit > - for train result */ > - reg = FDI_RX_IMR(pipe); > - temp = intel_de_read(dev_priv, reg); > - temp &= ~FDI_RX_SYMBOL_LOCK; > - temp &= ~FDI_RX_BIT_LOCK; > - intel_de_write(dev_priv, reg, temp); > - intel_de_read(dev_priv, reg); > - udelay(150); > - > - /* enable CPU FDI TX and PCH FDI RX */ > - reg = FDI_TX_CTL(pipe); > - temp = intel_de_read(dev_priv, reg); > - temp &= ~FDI_DP_PORT_WIDTH_MASK; > - temp |= FDI_DP_PORT_WIDTH(crtc_state->fdi_lanes); > - temp &= ~FDI_LINK_TRAIN_NONE; > - temp |= FDI_LINK_TRAIN_PATTERN_1; > - intel_de_write(dev_priv, reg, temp | FDI_TX_ENABLE); > - > - reg = FDI_RX_CTL(pipe); > - temp = intel_de_read(dev_priv, reg); > - temp &= ~FDI_LINK_TRAIN_NONE; > - temp |= FDI_LINK_TRAIN_PATTERN_1; > - intel_de_write(dev_priv, reg, temp | FDI_RX_ENABLE); > - > - intel_de_posting_read(dev_priv, reg); > - udelay(150); > - > - /* Ironlake workaround, enable clock pointer after FDI enable*/ > - intel_de_write(dev_priv, FDI_RX_CHICKEN(pipe), > - FDI_RX_PHASE_SYNC_POINTER_OVR); > - intel_de_write(dev_priv, FDI_RX_CHICKEN(pipe), > - FDI_RX_PHASE_SYNC_POINTER_OVR | FDI_RX_PHASE_SYNC_POINTER_EN); > - > - reg = FDI_RX_IIR(pipe); > - for (tries = 0; tries < 5; tries++) { > - temp = intel_de_read(dev_priv, reg); > - drm_dbg_kms(&dev_priv->drm, "FDI_RX_IIR 0x%x\n", temp); > - > - if ((temp & FDI_RX_BIT_LOCK)) { > - drm_dbg_kms(&dev_priv->drm, "FDI train 1 done.\n"); > - intel_de_write(dev_priv, reg, temp | FDI_RX_BIT_LOCK); > - break; > - } > - } > - if (tries == 5) > - drm_err(&dev_priv->drm, "FDI train 1 fail!\n"); > - > - /* Train 2 */ > - reg = FDI_TX_CTL(pipe); > - temp = intel_de_read(dev_priv, reg); > - temp &= ~FDI_LINK_TRAIN_NONE; > - temp |= FDI_LINK_TRAIN_PATTERN_2; > - intel_de_write(dev_priv, reg, temp); > - > - reg = FDI_RX_CTL(pipe); > - temp = intel_de_read(dev_priv, reg); > - temp &= ~FDI_LINK_TRAIN_NONE; > - temp |= FDI_LINK_TRAIN_PATTERN_2; > - intel_de_write(dev_priv, reg, temp); > - > - intel_de_posting_read(dev_priv, reg); > - udelay(150); > - > - reg = FDI_RX_IIR(pipe); > - for (tries = 0; tries < 5; tries++) { > - temp = intel_de_read(dev_priv, reg); > - drm_dbg_kms(&dev_priv->drm, "FDI_RX_IIR 0x%x\n", temp); > - > - if (temp & FDI_RX_SYMBOL_LOCK) { > - intel_de_write(dev_priv, reg, > - temp | FDI_RX_SYMBOL_LOCK); > - drm_dbg_kms(&dev_priv->drm, "FDI train 2 done.\n"); > - break; > - } > - } > - if (tries == 5) > - drm_err(&dev_priv->drm, "FDI train 2 fail!\n"); > - > - drm_dbg_kms(&dev_priv->drm, "FDI train done\n"); > - > -} > - > -static const int snb_b_fdi_train_param[] = { > - FDI_LINK_TRAIN_400MV_0DB_SNB_B, > - FDI_LINK_TRAIN_400MV_6DB_SNB_B, > - FDI_LINK_TRAIN_600MV_3_5DB_SNB_B, > - FDI_LINK_TRAIN_800MV_0DB_SNB_B, > -}; > - > -/* The FDI link training functions for SNB/Cougarpoint. */ > -static void gen6_fdi_link_train(struct intel_crtc *crtc, > - const struct intel_crtc_state *crtc_state) > -{ > - struct drm_device *dev = crtc->base.dev; > - struct drm_i915_private *dev_priv = to_i915(dev); > - enum pipe pipe = crtc->pipe; > - i915_reg_t reg; > - u32 temp, i, retry; > - > - /* Train 1: umask FDI RX Interrupt symbol_lock and bit_lock bit > - for train result */ > - reg = FDI_RX_IMR(pipe); > - temp = intel_de_read(dev_priv, reg); > - temp &= ~FDI_RX_SYMBOL_LOCK; > - temp &= ~FDI_RX_BIT_LOCK; > - intel_de_write(dev_priv, reg, temp); > - > - intel_de_posting_read(dev_priv, reg); > - udelay(150); > - > - /* enable CPU FDI TX and PCH FDI RX */ > - reg = FDI_TX_CTL(pipe); > - temp = intel_de_read(dev_priv, reg); > - temp &= ~FDI_DP_PORT_WIDTH_MASK; > - temp |= FDI_DP_PORT_WIDTH(crtc_state->fdi_lanes); > - temp &= ~FDI_LINK_TRAIN_NONE; > - temp |= FDI_LINK_TRAIN_PATTERN_1; > - temp &= ~FDI_LINK_TRAIN_VOL_EMP_MASK; > - /* SNB-B */ > - temp |= FDI_LINK_TRAIN_400MV_0DB_SNB_B; > - intel_de_write(dev_priv, reg, temp | FDI_TX_ENABLE); > - > - intel_de_write(dev_priv, FDI_RX_MISC(pipe), > - FDI_RX_TP1_TO_TP2_48 | FDI_RX_FDI_DELAY_90); > - > - reg = FDI_RX_CTL(pipe); > - temp = intel_de_read(dev_priv, reg); > - if (HAS_PCH_CPT(dev_priv)) { > - temp &= ~FDI_LINK_TRAIN_PATTERN_MASK_CPT; > - temp |= FDI_LINK_TRAIN_PATTERN_1_CPT; > - } else { > - temp &= ~FDI_LINK_TRAIN_NONE; > - temp |= FDI_LINK_TRAIN_PATTERN_1; > - } > - intel_de_write(dev_priv, reg, temp | FDI_RX_ENABLE); > - > - intel_de_posting_read(dev_priv, reg); > - udelay(150); > - > - for (i = 0; i < 4; i++) { > - reg = FDI_TX_CTL(pipe); > - temp = intel_de_read(dev_priv, reg); > - temp &= ~FDI_LINK_TRAIN_VOL_EMP_MASK; > - temp |= snb_b_fdi_train_param[i]; > - intel_de_write(dev_priv, reg, temp); > - > - intel_de_posting_read(dev_priv, reg); > - udelay(500); > - > - for (retry = 0; retry < 5; retry++) { > - reg = FDI_RX_IIR(pipe); > - temp = intel_de_read(dev_priv, reg); > - drm_dbg_kms(&dev_priv->drm, "FDI_RX_IIR 0x%x\n", temp); > - if (temp & FDI_RX_BIT_LOCK) { > - intel_de_write(dev_priv, reg, > - temp | FDI_RX_BIT_LOCK); > - drm_dbg_kms(&dev_priv->drm, > - "FDI train 1 done.\n"); > - break; > - } > - udelay(50); > - } > - if (retry < 5) > - break; > - } > - if (i == 4) > - drm_err(&dev_priv->drm, "FDI train 1 fail!\n"); > - > - /* Train 2 */ > - reg = FDI_TX_CTL(pipe); > - temp = intel_de_read(dev_priv, reg); > - temp &= ~FDI_LINK_TRAIN_NONE; > - temp |= FDI_LINK_TRAIN_PATTERN_2; > - if (IS_GEN(dev_priv, 6)) { > - temp &= ~FDI_LINK_TRAIN_VOL_EMP_MASK; > - /* SNB-B */ > - temp |= FDI_LINK_TRAIN_400MV_0DB_SNB_B; > - } > - intel_de_write(dev_priv, reg, temp); > - > - reg = FDI_RX_CTL(pipe); > - temp = intel_de_read(dev_priv, reg); > - if (HAS_PCH_CPT(dev_priv)) { > - temp &= ~FDI_LINK_TRAIN_PATTERN_MASK_CPT; > - temp |= FDI_LINK_TRAIN_PATTERN_2_CPT; > - } else { > - temp &= ~FDI_LINK_TRAIN_NONE; > - temp |= FDI_LINK_TRAIN_PATTERN_2; > - } > - intel_de_write(dev_priv, reg, temp); > - > - intel_de_posting_read(dev_priv, reg); > - udelay(150); > - > - for (i = 0; i < 4; i++) { > - reg = FDI_TX_CTL(pipe); > - temp = intel_de_read(dev_priv, reg); > - temp &= ~FDI_LINK_TRAIN_VOL_EMP_MASK; > - temp |= snb_b_fdi_train_param[i]; > - intel_de_write(dev_priv, reg, temp); > - > - intel_de_posting_read(dev_priv, reg); > - udelay(500); > - > - for (retry = 0; retry < 5; retry++) { > - reg = FDI_RX_IIR(pipe); > - temp = intel_de_read(dev_priv, reg); > - drm_dbg_kms(&dev_priv->drm, "FDI_RX_IIR 0x%x\n", temp); > - if (temp & FDI_RX_SYMBOL_LOCK) { > - intel_de_write(dev_priv, reg, > - temp | FDI_RX_SYMBOL_LOCK); > - drm_dbg_kms(&dev_priv->drm, > - "FDI train 2 done.\n"); > - break; > - } > - udelay(50); > - } > - if (retry < 5) > - break; > - } > - if (i == 4) > - drm_err(&dev_priv->drm, "FDI train 2 fail!\n"); > - > - drm_dbg_kms(&dev_priv->drm, "FDI train done.\n"); > -} > - > -/* Manual link training for Ivy Bridge A0 parts */ > -static void ivb_manual_fdi_link_train(struct intel_crtc *crtc, > - const struct intel_crtc_state *crtc_state) > -{ > - struct drm_device *dev = crtc->base.dev; > - struct drm_i915_private *dev_priv = to_i915(dev); > - enum pipe pipe = crtc->pipe; > - i915_reg_t reg; > - u32 temp, i, j; > - > - /* Train 1: umask FDI RX Interrupt symbol_lock and bit_lock bit > - for train result */ > - reg = FDI_RX_IMR(pipe); > - temp = intel_de_read(dev_priv, reg); > - temp &= ~FDI_RX_SYMBOL_LOCK; > - temp &= ~FDI_RX_BIT_LOCK; > - intel_de_write(dev_priv, reg, temp); > - > - intel_de_posting_read(dev_priv, reg); > - udelay(150); > - > - drm_dbg_kms(&dev_priv->drm, "FDI_RX_IIR before link train 0x%x\n", > - intel_de_read(dev_priv, FDI_RX_IIR(pipe))); > - > - /* Try each vswing and preemphasis setting twice before moving on */ > - for (j = 0; j < ARRAY_SIZE(snb_b_fdi_train_param) * 2; j++) { > - /* disable first in case we need to retry */ > - reg = FDI_TX_CTL(pipe); > - temp = intel_de_read(dev_priv, reg); > - temp &= ~(FDI_LINK_TRAIN_AUTO | FDI_LINK_TRAIN_NONE_IVB); > - temp &= ~FDI_TX_ENABLE; > - intel_de_write(dev_priv, reg, temp); > - > - reg = FDI_RX_CTL(pipe); > - temp = intel_de_read(dev_priv, reg); > - temp &= ~FDI_LINK_TRAIN_AUTO; > - temp &= ~FDI_LINK_TRAIN_PATTERN_MASK_CPT; > - temp &= ~FDI_RX_ENABLE; > - intel_de_write(dev_priv, reg, temp); > - > - /* enable CPU FDI TX and PCH FDI RX */ > - reg = FDI_TX_CTL(pipe); > - temp = intel_de_read(dev_priv, reg); > - temp &= ~FDI_DP_PORT_WIDTH_MASK; > - temp |= FDI_DP_PORT_WIDTH(crtc_state->fdi_lanes); > - temp |= FDI_LINK_TRAIN_PATTERN_1_IVB; > - temp &= ~FDI_LINK_TRAIN_VOL_EMP_MASK; > - temp |= snb_b_fdi_train_param[j/2]; > - temp |= FDI_COMPOSITE_SYNC; > - intel_de_write(dev_priv, reg, temp | FDI_TX_ENABLE); > - > - intel_de_write(dev_priv, FDI_RX_MISC(pipe), > - FDI_RX_TP1_TO_TP2_48 | FDI_RX_FDI_DELAY_90); > - > - reg = FDI_RX_CTL(pipe); > - temp = intel_de_read(dev_priv, reg); > - temp |= FDI_LINK_TRAIN_PATTERN_1_CPT; > - temp |= FDI_COMPOSITE_SYNC; > - intel_de_write(dev_priv, reg, temp | FDI_RX_ENABLE); > - > - intel_de_posting_read(dev_priv, reg); > - udelay(1); /* should be 0.5us */ > - > - for (i = 0; i < 4; i++) { > - reg = FDI_RX_IIR(pipe); > - temp = intel_de_read(dev_priv, reg); > - drm_dbg_kms(&dev_priv->drm, "FDI_RX_IIR 0x%x\n", temp); > - > - if (temp & FDI_RX_BIT_LOCK || > - (intel_de_read(dev_priv, reg) & FDI_RX_BIT_LOCK)) { > - intel_de_write(dev_priv, reg, > - temp | FDI_RX_BIT_LOCK); > - drm_dbg_kms(&dev_priv->drm, > - "FDI train 1 done, level %i.\n", > - i); > - break; > - } > - udelay(1); /* should be 0.5us */ > - } > - if (i == 4) { > - drm_dbg_kms(&dev_priv->drm, > - "FDI train 1 fail on vswing %d\n", j / 2); > - continue; > - } > - > - /* Train 2 */ > - reg = FDI_TX_CTL(pipe); > - temp = intel_de_read(dev_priv, reg); > - temp &= ~FDI_LINK_TRAIN_NONE_IVB; > - temp |= FDI_LINK_TRAIN_PATTERN_2_IVB; > - intel_de_write(dev_priv, reg, temp); > - > - reg = FDI_RX_CTL(pipe); > - temp = intel_de_read(dev_priv, reg); > - temp &= ~FDI_LINK_TRAIN_PATTERN_MASK_CPT; > - temp |= FDI_LINK_TRAIN_PATTERN_2_CPT; > - intel_de_write(dev_priv, reg, temp); > - > - intel_de_posting_read(dev_priv, reg); > - udelay(2); /* should be 1.5us */ > - > - for (i = 0; i < 4; i++) { > - reg = FDI_RX_IIR(pipe); > - temp = intel_de_read(dev_priv, reg); > - drm_dbg_kms(&dev_priv->drm, "FDI_RX_IIR 0x%x\n", temp); > - > - if (temp & FDI_RX_SYMBOL_LOCK || > - (intel_de_read(dev_priv, reg) & FDI_RX_SYMBOL_LOCK)) { > - intel_de_write(dev_priv, reg, > - temp | FDI_RX_SYMBOL_LOCK); > - drm_dbg_kms(&dev_priv->drm, > - "FDI train 2 done, level %i.\n", > - i); > - goto train_done; > - } > - udelay(2); /* should be 1.5us */ > - } > - if (i == 4) > - drm_dbg_kms(&dev_priv->drm, > - "FDI train 2 fail on vswing %d\n", j / 2); > - } > - > -train_done: > - drm_dbg_kms(&dev_priv->drm, "FDI train done.\n"); > -} > - > -static void ilk_fdi_pll_enable(const struct intel_crtc_state *crtc_state) > -{ > - struct intel_crtc *intel_crtc = to_intel_crtc(crtc_state->uapi.crtc); > - struct drm_i915_private *dev_priv = to_i915(intel_crtc->base.dev); > - enum pipe pipe = intel_crtc->pipe; > - i915_reg_t reg; > - u32 temp; > - > - /* enable PCH FDI RX PLL, wait warmup plus DMI latency */ > - reg = FDI_RX_CTL(pipe); > - temp = intel_de_read(dev_priv, reg); > - temp &= ~(FDI_DP_PORT_WIDTH_MASK | (0x7 << 16)); > - temp |= FDI_DP_PORT_WIDTH(crtc_state->fdi_lanes); > - temp |= (intel_de_read(dev_priv, PIPECONF(pipe)) & PIPECONF_BPC_MASK) << 11; > - intel_de_write(dev_priv, reg, temp | FDI_RX_PLL_ENABLE); > - > - intel_de_posting_read(dev_priv, reg); > - udelay(200); > - > - /* Switch from Rawclk to PCDclk */ > - temp = intel_de_read(dev_priv, reg); > - intel_de_write(dev_priv, reg, temp | FDI_PCDCLK); > - > - intel_de_posting_read(dev_priv, reg); > - udelay(200); > - > - /* Enable CPU FDI TX PLL, always on for Ironlake */ > - reg = FDI_TX_CTL(pipe); > - temp = intel_de_read(dev_priv, reg); > - if ((temp & FDI_TX_PLL_ENABLE) == 0) { > - intel_de_write(dev_priv, reg, temp | FDI_TX_PLL_ENABLE); > - > - intel_de_posting_read(dev_priv, reg); > - udelay(100); > - } > -} > - > -static void ilk_fdi_pll_disable(struct intel_crtc *intel_crtc) > -{ > - struct drm_device *dev = intel_crtc->base.dev; > - struct drm_i915_private *dev_priv = to_i915(dev); > - enum pipe pipe = intel_crtc->pipe; > - i915_reg_t reg; > - u32 temp; > - > - /* Switch from PCDclk to Rawclk */ > - reg = FDI_RX_CTL(pipe); > - temp = intel_de_read(dev_priv, reg); > - intel_de_write(dev_priv, reg, temp & ~FDI_PCDCLK); > - > - /* Disable CPU FDI TX PLL */ > - reg = FDI_TX_CTL(pipe); > - temp = intel_de_read(dev_priv, reg); > - intel_de_write(dev_priv, reg, temp & ~FDI_TX_PLL_ENABLE); > - > - intel_de_posting_read(dev_priv, reg); > - udelay(100); > - > - reg = FDI_RX_CTL(pipe); > - temp = intel_de_read(dev_priv, reg); > - intel_de_write(dev_priv, reg, temp & ~FDI_RX_PLL_ENABLE); > - > - /* Wait for the clocks to turn off. */ > - intel_de_posting_read(dev_priv, reg); > - udelay(100); > -} > - > -static void ilk_fdi_disable(struct intel_crtc *crtc) > -{ > - struct drm_i915_private *dev_priv = to_i915(crtc->base.dev); > - enum pipe pipe = crtc->pipe; > - i915_reg_t reg; > - u32 temp; > - > - /* disable CPU FDI tx and PCH FDI rx */ > - reg = FDI_TX_CTL(pipe); > - temp = intel_de_read(dev_priv, reg); > - intel_de_write(dev_priv, reg, temp & ~FDI_TX_ENABLE); > - intel_de_posting_read(dev_priv, reg); > - > - reg = FDI_RX_CTL(pipe); > - temp = intel_de_read(dev_priv, reg); > - temp &= ~(0x7 << 16); > - temp |= (intel_de_read(dev_priv, PIPECONF(pipe)) & PIPECONF_BPC_MASK) << 11; > - intel_de_write(dev_priv, reg, temp & ~FDI_RX_ENABLE); > - > - intel_de_posting_read(dev_priv, reg); > - udelay(100); > - > - /* Ironlake workaround, disable clock pointer after downing FDI */ > - if (HAS_PCH_IBX(dev_priv)) > - intel_de_write(dev_priv, FDI_RX_CHICKEN(pipe), > - FDI_RX_PHASE_SYNC_POINTER_OVR); > - > - /* still set train pattern 1 */ > - reg = FDI_TX_CTL(pipe); > - temp = intel_de_read(dev_priv, reg); > - temp &= ~FDI_LINK_TRAIN_NONE; > - temp |= FDI_LINK_TRAIN_PATTERN_1; > - intel_de_write(dev_priv, reg, temp); > - > - reg = FDI_RX_CTL(pipe); > - temp = intel_de_read(dev_priv, reg); > - if (HAS_PCH_CPT(dev_priv)) { > - temp &= ~FDI_LINK_TRAIN_PATTERN_MASK_CPT; > - temp |= FDI_LINK_TRAIN_PATTERN_1_CPT; > - } else { > - temp &= ~FDI_LINK_TRAIN_NONE; > - temp |= FDI_LINK_TRAIN_PATTERN_1; > - } > - /* BPC in FDI rx is consistent with that in PIPECONF */ > - temp &= ~(0x07 << 16); > - temp |= (intel_de_read(dev_priv, PIPECONF(pipe)) & PIPECONF_BPC_MASK) << 11; > - intel_de_write(dev_priv, reg, temp); > - > - intel_de_posting_read(dev_priv, reg); > - udelay(100); > -} > - > bool intel_has_pending_fb_unpin(struct drm_i915_private *dev_priv) > { > struct drm_crtc *crtc; > @@ -6708,143 +6172,6 @@ static void intel_connector_verify_state(struct intel_crtc_state *crtc_state, > } > } > > -static int pipe_required_fdi_lanes(struct intel_crtc_state *crtc_state) > -{ > - if (crtc_state->hw.enable && crtc_state->has_pch_encoder) > - return crtc_state->fdi_lanes; > - > - return 0; > -} > - > -static int ilk_check_fdi_lanes(struct drm_device *dev, enum pipe pipe, > - struct intel_crtc_state *pipe_config) > -{ > - struct drm_i915_private *dev_priv = to_i915(dev); > - struct drm_atomic_state *state = pipe_config->uapi.state; > - struct intel_crtc *other_crtc; > - struct intel_crtc_state *other_crtc_state; > - > - drm_dbg_kms(&dev_priv->drm, > - "checking fdi config on pipe %c, lanes %i\n", > - pipe_name(pipe), pipe_config->fdi_lanes); > - if (pipe_config->fdi_lanes > 4) { > - drm_dbg_kms(&dev_priv->drm, > - "invalid fdi lane config on pipe %c: %i lanes\n", > - pipe_name(pipe), pipe_config->fdi_lanes); > - return -EINVAL; > - } > - > - if (IS_HASWELL(dev_priv) || IS_BROADWELL(dev_priv)) { > - if (pipe_config->fdi_lanes > 2) { > - drm_dbg_kms(&dev_priv->drm, > - "only 2 lanes on haswell, required: %i lanes\n", > - pipe_config->fdi_lanes); > - return -EINVAL; > - } else { > - return 0; > - } > - } > - > - if (INTEL_NUM_PIPES(dev_priv) == 2) > - return 0; > - > - /* Ivybridge 3 pipe is really complicated */ > - switch (pipe) { > - case PIPE_A: > - return 0; > - case PIPE_B: > - if (pipe_config->fdi_lanes <= 2) > - return 0; > - > - other_crtc = intel_get_crtc_for_pipe(dev_priv, PIPE_C); > - other_crtc_state = > - intel_atomic_get_crtc_state(state, other_crtc); > - if (IS_ERR(other_crtc_state)) > - return PTR_ERR(other_crtc_state); > - > - if (pipe_required_fdi_lanes(other_crtc_state) > 0) { > - drm_dbg_kms(&dev_priv->drm, > - "invalid shared fdi lane config on pipe %c: %i lanes\n", > - pipe_name(pipe), pipe_config->fdi_lanes); > - return -EINVAL; > - } > - return 0; > - case PIPE_C: > - if (pipe_config->fdi_lanes > 2) { > - drm_dbg_kms(&dev_priv->drm, > - "only 2 lanes on pipe %c: required %i lanes\n", > - pipe_name(pipe), pipe_config->fdi_lanes); > - return -EINVAL; > - } > - > - other_crtc = intel_get_crtc_for_pipe(dev_priv, PIPE_B); > - other_crtc_state = > - intel_atomic_get_crtc_state(state, other_crtc); > - if (IS_ERR(other_crtc_state)) > - return PTR_ERR(other_crtc_state); > - > - if (pipe_required_fdi_lanes(other_crtc_state) > 2) { > - drm_dbg_kms(&dev_priv->drm, > - "fdi link B uses too many lanes to enable link C\n"); > - return -EINVAL; > - } > - return 0; > - default: > - BUG(); > - } > -} > - > -#define RETRY 1 > -static int ilk_fdi_compute_config(struct intel_crtc *intel_crtc, > - struct intel_crtc_state *pipe_config) > -{ > - struct drm_device *dev = intel_crtc->base.dev; > - struct drm_i915_private *i915 = to_i915(dev); > - const struct drm_display_mode *adjusted_mode = &pipe_config->hw.adjusted_mode; > - int lane, link_bw, fdi_dotclock, ret; > - bool needs_recompute = false; > - > -retry: > - /* FDI is a binary signal running at ~2.7GHz, encoding > - * each output octet as 10 bits. The actual frequency > - * is stored as a divider into a 100MHz clock, and the > - * mode pixel clock is stored in units of 1KHz. > - * Hence the bw of each lane in terms of the mode signal > - * is: > - */ > - link_bw = intel_fdi_link_freq(i915, pipe_config); > - > - fdi_dotclock = adjusted_mode->crtc_clock; > - > - lane = ilk_get_lanes_required(fdi_dotclock, link_bw, > - pipe_config->pipe_bpp); > - > - pipe_config->fdi_lanes = lane; > - > - intel_link_compute_m_n(pipe_config->pipe_bpp, lane, fdi_dotclock, > - link_bw, &pipe_config->fdi_m_n, false, false); > - > - ret = ilk_check_fdi_lanes(dev, intel_crtc->pipe, pipe_config); > - if (ret == -EDEADLK) > - return ret; > - > - if (ret == -EINVAL && pipe_config->pipe_bpp > 6*3) { > - pipe_config->pipe_bpp -= 2*3; > - drm_dbg_kms(&i915->drm, > - "fdi link bw constraint, reducing pipe bpp to %i\n", > - pipe_config->pipe_bpp); > - needs_recompute = true; > - pipe_config->bw_constrained = true; > - > - goto retry; > - } > - > - if (needs_recompute) > - return RETRY; > - > - return ret; > -} > - > bool hsw_crtc_state_ips_capable(const struct intel_crtc_state *crtc_state) > { > struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc); > @@ -11379,7 +10706,7 @@ intel_modeset_pipe_config(struct intel_atomic_state *state, > return ret; > } > > - if (ret == RETRY) { > + if (ret == I915_DISPLAY_CONFIG_RETRY) { > if (drm_WARN(&i915->drm, !retry, > "loop in pipe configuration computation\n")) > return -EINVAL; > @@ -15178,14 +14505,7 @@ void intel_init_display_hooks(struct drm_i915_private *dev_priv) > dev_priv->display.crtc_disable = i9xx_crtc_disable; > } > > - if (IS_GEN(dev_priv, 5)) { > - dev_priv->display.fdi_link_train = ilk_fdi_link_train; > - } else if (IS_GEN(dev_priv, 6)) { > - dev_priv->display.fdi_link_train = gen6_fdi_link_train; > - } else if (IS_IVYBRIDGE(dev_priv)) { > - /* FIXME: detect B0+ stepping and use auto training */ > - dev_priv->display.fdi_link_train = ivb_manual_fdi_link_train; > - } > + intel_init_fdi_hook(dev_priv); > > if (INTEL_GEN(dev_priv) >= 9) { > dev_priv->display.commit_modeset_enables = skl_commit_modeset_enables; > diff --git a/drivers/gpu/drm/i915/display/intel_display.h b/drivers/gpu/drm/i915/display/intel_display.h > index f1e36cca86c1..731ea6f7777a 100644 > --- a/drivers/gpu/drm/i915/display/intel_display.h > +++ b/drivers/gpu/drm/i915/display/intel_display.h > @@ -677,6 +677,16 @@ void vlv_compute_dpll(struct intel_crtc *crtc, > void chv_compute_dpll(struct intel_crtc *crtc, > struct intel_crtc_state *pipe_config); > > +/* fdi */ > +#define I915_DISPLAY_CONFIG_RETRY 1 > +int ilk_fdi_compute_config(struct intel_crtc *intel_crtc, > + struct intel_crtc_state *pipe_config); > +void intel_fdi_normal_train(struct intel_crtc *crtc); > +void ilk_fdi_disable(struct intel_crtc *crtc); > +void ilk_fdi_pll_disable(struct intel_crtc *intel_crtc); > +void ilk_fdi_pll_enable(const struct intel_crtc_state *crtc_state); > +void intel_init_fdi_hook(struct drm_i915_private *dev_priv); > + > /* modesetting */ > void intel_modeset_init_hw(struct drm_i915_private *i915); > int intel_modeset_init_noirq(struct drm_i915_private *i915); > diff --git a/drivers/gpu/drm/i915/display/intel_display_types.h b/drivers/gpu/drm/i915/display/intel_display_types.h > index 823083b231bc..9c684552903c 100644 > --- a/drivers/gpu/drm/i915/display/intel_display_types.h > +++ b/drivers/gpu/drm/i915/display/intel_display_types.h > @@ -1804,4 +1804,13 @@ static inline u32 i9xx_dpll_compute_fp(struct dpll *dpll) > return dpll->n << 16 | dpll->m1 << 8 | dpll->m2; > } > > +static inline u32 intel_fdi_link_freq(struct drm_i915_private *dev_priv, > + const struct intel_crtc_state *pipe_config) > +{ > + if (HAS_DDI(dev_priv)) > + return pipe_config->port_clock; /* SPLL */ > + else > + return dev_priv->fdi_pll_freq; > +} > + > #endif /* __INTEL_DISPLAY_TYPES_H__ */ > diff --git a/drivers/gpu/drm/i915/display/intel_fdi.c b/drivers/gpu/drm/i915/display/intel_fdi.c > new file mode 100644 > index 000000000000..bf4ef84686ef > --- /dev/null > +++ b/drivers/gpu/drm/i915/display/intel_fdi.c > @@ -0,0 +1,682 @@ > +// SPDX-License-Identifier: MIT > +/* > + * Copyright © 2020 Intel Corporation > + */ > +#include "intel_atomic.h" > +#include "intel_display_types.h" > + > +/* units of 100MHz */ > +static int pipe_required_fdi_lanes(struct intel_crtc_state *crtc_state) > +{ > + if (crtc_state->hw.enable && crtc_state->has_pch_encoder) > + return crtc_state->fdi_lanes; > + > + return 0; > +} > + > +static int ilk_check_fdi_lanes(struct drm_device *dev, enum pipe pipe, > + struct intel_crtc_state *pipe_config) > +{ > + struct drm_i915_private *dev_priv = to_i915(dev); > + struct drm_atomic_state *state = pipe_config->uapi.state; > + struct intel_crtc *other_crtc; > + struct intel_crtc_state *other_crtc_state; > + > + drm_dbg_kms(&dev_priv->drm, > + "checking fdi config on pipe %c, lanes %i\n", > + pipe_name(pipe), pipe_config->fdi_lanes); > + if (pipe_config->fdi_lanes > 4) { > + drm_dbg_kms(&dev_priv->drm, > + "invalid fdi lane config on pipe %c: %i lanes\n", > + pipe_name(pipe), pipe_config->fdi_lanes); > + return -EINVAL; > + } > + > + if (IS_HASWELL(dev_priv) || IS_BROADWELL(dev_priv)) { > + if (pipe_config->fdi_lanes > 2) { > + drm_dbg_kms(&dev_priv->drm, > + "only 2 lanes on haswell, required: %i lanes\n", > + pipe_config->fdi_lanes); > + return -EINVAL; > + } else { > + return 0; > + } > + } > + > + if (INTEL_NUM_PIPES(dev_priv) == 2) > + return 0; > + > + /* Ivybridge 3 pipe is really complicated */ > + switch (pipe) { > + case PIPE_A: > + return 0; > + case PIPE_B: > + if (pipe_config->fdi_lanes <= 2) > + return 0; > + > + other_crtc = intel_get_crtc_for_pipe(dev_priv, PIPE_C); > + other_crtc_state = > + intel_atomic_get_crtc_state(state, other_crtc); > + if (IS_ERR(other_crtc_state)) > + return PTR_ERR(other_crtc_state); > + > + if (pipe_required_fdi_lanes(other_crtc_state) > 0) { > + drm_dbg_kms(&dev_priv->drm, > + "invalid shared fdi lane config on pipe %c: %i lanes\n", > + pipe_name(pipe), pipe_config->fdi_lanes); > + return -EINVAL; > + } > + return 0; > + case PIPE_C: > + if (pipe_config->fdi_lanes > 2) { > + drm_dbg_kms(&dev_priv->drm, > + "only 2 lanes on pipe %c: required %i lanes\n", > + pipe_name(pipe), pipe_config->fdi_lanes); > + return -EINVAL; > + } > + > + other_crtc = intel_get_crtc_for_pipe(dev_priv, PIPE_B); > + other_crtc_state = > + intel_atomic_get_crtc_state(state, other_crtc); > + if (IS_ERR(other_crtc_state)) > + return PTR_ERR(other_crtc_state); > + > + if (pipe_required_fdi_lanes(other_crtc_state) > 2) { > + drm_dbg_kms(&dev_priv->drm, > + "fdi link B uses too many lanes to enable link C\n"); > + return -EINVAL; > + } > + return 0; > + default: > + BUG(); > + } > +} > + > +int ilk_fdi_compute_config(struct intel_crtc *intel_crtc, > + struct intel_crtc_state *pipe_config) > +{ > + struct drm_device *dev = intel_crtc->base.dev; > + struct drm_i915_private *i915 = to_i915(dev); > + const struct drm_display_mode *adjusted_mode = &pipe_config->hw.adjusted_mode; > + int lane, link_bw, fdi_dotclock, ret; > + bool needs_recompute = false; > + > +retry: > + /* FDI is a binary signal running at ~2.7GHz, encoding > + * each output octet as 10 bits. The actual frequency > + * is stored as a divider into a 100MHz clock, and the > + * mode pixel clock is stored in units of 1KHz. > + * Hence the bw of each lane in terms of the mode signal > + * is: > + */ > + link_bw = intel_fdi_link_freq(i915, pipe_config); > + > + fdi_dotclock = adjusted_mode->crtc_clock; > + > + lane = ilk_get_lanes_required(fdi_dotclock, link_bw, > + pipe_config->pipe_bpp); > + > + pipe_config->fdi_lanes = lane; > + > + intel_link_compute_m_n(pipe_config->pipe_bpp, lane, fdi_dotclock, > + link_bw, &pipe_config->fdi_m_n, false, false); > + > + ret = ilk_check_fdi_lanes(dev, intel_crtc->pipe, pipe_config); > + if (ret == -EDEADLK) > + return ret; > + > + if (ret == -EINVAL && pipe_config->pipe_bpp > 6*3) { > + pipe_config->pipe_bpp -= 2*3; > + drm_dbg_kms(&i915->drm, > + "fdi link bw constraint, reducing pipe bpp to %i\n", > + pipe_config->pipe_bpp); > + needs_recompute = true; > + pipe_config->bw_constrained = true; > + > + goto retry; > + } > + > + if (needs_recompute) > + return I915_DISPLAY_CONFIG_RETRY; > + > + return ret; > +} > + > +void intel_fdi_normal_train(struct intel_crtc *crtc) > +{ > + struct drm_device *dev = crtc->base.dev; > + struct drm_i915_private *dev_priv = to_i915(dev); > + enum pipe pipe = crtc->pipe; > + i915_reg_t reg; > + u32 temp; > + > + /* enable normal train */ > + reg = FDI_TX_CTL(pipe); > + temp = intel_de_read(dev_priv, reg); > + if (IS_IVYBRIDGE(dev_priv)) { > + temp &= ~FDI_LINK_TRAIN_NONE_IVB; > + temp |= FDI_LINK_TRAIN_NONE_IVB | FDI_TX_ENHANCE_FRAME_ENABLE; > + } else { > + temp &= ~FDI_LINK_TRAIN_NONE; > + temp |= FDI_LINK_TRAIN_NONE | FDI_TX_ENHANCE_FRAME_ENABLE; > + } > + intel_de_write(dev_priv, reg, temp); > + > + reg = FDI_RX_CTL(pipe); > + temp = intel_de_read(dev_priv, reg); > + if (HAS_PCH_CPT(dev_priv)) { > + temp &= ~FDI_LINK_TRAIN_PATTERN_MASK_CPT; > + temp |= FDI_LINK_TRAIN_NORMAL_CPT; > + } else { > + temp &= ~FDI_LINK_TRAIN_NONE; > + temp |= FDI_LINK_TRAIN_NONE; > + } > + intel_de_write(dev_priv, reg, temp | FDI_RX_ENHANCE_FRAME_ENABLE); > + > + /* wait one idle pattern time */ > + intel_de_posting_read(dev_priv, reg); > + udelay(1000); > + > + /* IVB wants error correction enabled */ > + if (IS_IVYBRIDGE(dev_priv)) > + intel_de_write(dev_priv, reg, > + intel_de_read(dev_priv, reg) | FDI_FS_ERRC_ENABLE | FDI_FE_ERRC_ENABLE); > +} > + > +/* The FDI link training functions for ILK/Ibexpeak. */ > +static void ilk_fdi_link_train(struct intel_crtc *crtc, > + const struct intel_crtc_state *crtc_state) > +{ > + struct drm_device *dev = crtc->base.dev; > + struct drm_i915_private *dev_priv = to_i915(dev); > + enum pipe pipe = crtc->pipe; > + i915_reg_t reg; > + u32 temp, tries; > + > + /* FDI needs bits from pipe first */ > + assert_pipe_enabled(dev_priv, crtc_state->cpu_transcoder); > + > + /* Train 1: umask FDI RX Interrupt symbol_lock and bit_lock bit > + for train result */ > + reg = FDI_RX_IMR(pipe); > + temp = intel_de_read(dev_priv, reg); > + temp &= ~FDI_RX_SYMBOL_LOCK; > + temp &= ~FDI_RX_BIT_LOCK; > + intel_de_write(dev_priv, reg, temp); > + intel_de_read(dev_priv, reg); > + udelay(150); > + > + /* enable CPU FDI TX and PCH FDI RX */ > + reg = FDI_TX_CTL(pipe); > + temp = intel_de_read(dev_priv, reg); > + temp &= ~FDI_DP_PORT_WIDTH_MASK; > + temp |= FDI_DP_PORT_WIDTH(crtc_state->fdi_lanes); > + temp &= ~FDI_LINK_TRAIN_NONE; > + temp |= FDI_LINK_TRAIN_PATTERN_1; > + intel_de_write(dev_priv, reg, temp | FDI_TX_ENABLE); > + > + reg = FDI_RX_CTL(pipe); > + temp = intel_de_read(dev_priv, reg); > + temp &= ~FDI_LINK_TRAIN_NONE; > + temp |= FDI_LINK_TRAIN_PATTERN_1; > + intel_de_write(dev_priv, reg, temp | FDI_RX_ENABLE); > + > + intel_de_posting_read(dev_priv, reg); > + udelay(150); > + > + /* Ironlake workaround, enable clock pointer after FDI enable*/ > + intel_de_write(dev_priv, FDI_RX_CHICKEN(pipe), > + FDI_RX_PHASE_SYNC_POINTER_OVR); > + intel_de_write(dev_priv, FDI_RX_CHICKEN(pipe), > + FDI_RX_PHASE_SYNC_POINTER_OVR | FDI_RX_PHASE_SYNC_POINTER_EN); > + > + reg = FDI_RX_IIR(pipe); > + for (tries = 0; tries < 5; tries++) { > + temp = intel_de_read(dev_priv, reg); > + drm_dbg_kms(&dev_priv->drm, "FDI_RX_IIR 0x%x\n", temp); > + > + if ((temp & FDI_RX_BIT_LOCK)) { > + drm_dbg_kms(&dev_priv->drm, "FDI train 1 done.\n"); > + intel_de_write(dev_priv, reg, temp | FDI_RX_BIT_LOCK); > + break; > + } > + } > + if (tries == 5) > + drm_err(&dev_priv->drm, "FDI train 1 fail!\n"); > + > + /* Train 2 */ > + reg = FDI_TX_CTL(pipe); > + temp = intel_de_read(dev_priv, reg); > + temp &= ~FDI_LINK_TRAIN_NONE; > + temp |= FDI_LINK_TRAIN_PATTERN_2; > + intel_de_write(dev_priv, reg, temp); > + > + reg = FDI_RX_CTL(pipe); > + temp = intel_de_read(dev_priv, reg); > + temp &= ~FDI_LINK_TRAIN_NONE; > + temp |= FDI_LINK_TRAIN_PATTERN_2; > + intel_de_write(dev_priv, reg, temp); > + > + intel_de_posting_read(dev_priv, reg); > + udelay(150); > + > + reg = FDI_RX_IIR(pipe); > + for (tries = 0; tries < 5; tries++) { > + temp = intel_de_read(dev_priv, reg); > + drm_dbg_kms(&dev_priv->drm, "FDI_RX_IIR 0x%x\n", temp); > + > + if (temp & FDI_RX_SYMBOL_LOCK) { > + intel_de_write(dev_priv, reg, > + temp | FDI_RX_SYMBOL_LOCK); > + drm_dbg_kms(&dev_priv->drm, "FDI train 2 done.\n"); > + break; > + } > + } > + if (tries == 5) > + drm_err(&dev_priv->drm, "FDI train 2 fail!\n"); > + > + drm_dbg_kms(&dev_priv->drm, "FDI train done\n"); > + > +} > + > +static const int snb_b_fdi_train_param[] = { > + FDI_LINK_TRAIN_400MV_0DB_SNB_B, > + FDI_LINK_TRAIN_400MV_6DB_SNB_B, > + FDI_LINK_TRAIN_600MV_3_5DB_SNB_B, > + FDI_LINK_TRAIN_800MV_0DB_SNB_B, > +}; > + > +/* The FDI link training functions for SNB/Cougarpoint. */ > +static void gen6_fdi_link_train(struct intel_crtc *crtc, > + const struct intel_crtc_state *crtc_state) > +{ > + struct drm_device *dev = crtc->base.dev; > + struct drm_i915_private *dev_priv = to_i915(dev); > + enum pipe pipe = crtc->pipe; > + i915_reg_t reg; > + u32 temp, i, retry; > + > + /* Train 1: umask FDI RX Interrupt symbol_lock and bit_lock bit > + for train result */ > + reg = FDI_RX_IMR(pipe); > + temp = intel_de_read(dev_priv, reg); > + temp &= ~FDI_RX_SYMBOL_LOCK; > + temp &= ~FDI_RX_BIT_LOCK; > + intel_de_write(dev_priv, reg, temp); > + > + intel_de_posting_read(dev_priv, reg); > + udelay(150); > + > + /* enable CPU FDI TX and PCH FDI RX */ > + reg = FDI_TX_CTL(pipe); > + temp = intel_de_read(dev_priv, reg); > + temp &= ~FDI_DP_PORT_WIDTH_MASK; > + temp |= FDI_DP_PORT_WIDTH(crtc_state->fdi_lanes); > + temp &= ~FDI_LINK_TRAIN_NONE; > + temp |= FDI_LINK_TRAIN_PATTERN_1; > + temp &= ~FDI_LINK_TRAIN_VOL_EMP_MASK; > + /* SNB-B */ > + temp |= FDI_LINK_TRAIN_400MV_0DB_SNB_B; > + intel_de_write(dev_priv, reg, temp | FDI_TX_ENABLE); > + > + intel_de_write(dev_priv, FDI_RX_MISC(pipe), > + FDI_RX_TP1_TO_TP2_48 | FDI_RX_FDI_DELAY_90); > + > + reg = FDI_RX_CTL(pipe); > + temp = intel_de_read(dev_priv, reg); > + if (HAS_PCH_CPT(dev_priv)) { > + temp &= ~FDI_LINK_TRAIN_PATTERN_MASK_CPT; > + temp |= FDI_LINK_TRAIN_PATTERN_1_CPT; > + } else { > + temp &= ~FDI_LINK_TRAIN_NONE; > + temp |= FDI_LINK_TRAIN_PATTERN_1; > + } > + intel_de_write(dev_priv, reg, temp | FDI_RX_ENABLE); > + > + intel_de_posting_read(dev_priv, reg); > + udelay(150); > + > + for (i = 0; i < 4; i++) { > + reg = FDI_TX_CTL(pipe); > + temp = intel_de_read(dev_priv, reg); > + temp &= ~FDI_LINK_TRAIN_VOL_EMP_MASK; > + temp |= snb_b_fdi_train_param[i]; > + intel_de_write(dev_priv, reg, temp); > + > + intel_de_posting_read(dev_priv, reg); > + udelay(500); > + > + for (retry = 0; retry < 5; retry++) { > + reg = FDI_RX_IIR(pipe); > + temp = intel_de_read(dev_priv, reg); > + drm_dbg_kms(&dev_priv->drm, "FDI_RX_IIR 0x%x\n", temp); > + if (temp & FDI_RX_BIT_LOCK) { > + intel_de_write(dev_priv, reg, > + temp | FDI_RX_BIT_LOCK); > + drm_dbg_kms(&dev_priv->drm, > + "FDI train 1 done.\n"); > + break; > + } > + udelay(50); > + } > + if (retry < 5) > + break; > + } > + if (i == 4) > + drm_err(&dev_priv->drm, "FDI train 1 fail!\n"); > + > + /* Train 2 */ > + reg = FDI_TX_CTL(pipe); > + temp = intel_de_read(dev_priv, reg); > + temp &= ~FDI_LINK_TRAIN_NONE; > + temp |= FDI_LINK_TRAIN_PATTERN_2; > + if (IS_GEN(dev_priv, 6)) { > + temp &= ~FDI_LINK_TRAIN_VOL_EMP_MASK; > + /* SNB-B */ > + temp |= FDI_LINK_TRAIN_400MV_0DB_SNB_B; > + } > + intel_de_write(dev_priv, reg, temp); > + > + reg = FDI_RX_CTL(pipe); > + temp = intel_de_read(dev_priv, reg); > + if (HAS_PCH_CPT(dev_priv)) { > + temp &= ~FDI_LINK_TRAIN_PATTERN_MASK_CPT; > + temp |= FDI_LINK_TRAIN_PATTERN_2_CPT; > + } else { > + temp &= ~FDI_LINK_TRAIN_NONE; > + temp |= FDI_LINK_TRAIN_PATTERN_2; > + } > + intel_de_write(dev_priv, reg, temp); > + > + intel_de_posting_read(dev_priv, reg); > + udelay(150); > + > + for (i = 0; i < 4; i++) { > + reg = FDI_TX_CTL(pipe); > + temp = intel_de_read(dev_priv, reg); > + temp &= ~FDI_LINK_TRAIN_VOL_EMP_MASK; > + temp |= snb_b_fdi_train_param[i]; > + intel_de_write(dev_priv, reg, temp); > + > + intel_de_posting_read(dev_priv, reg); > + udelay(500); > + > + for (retry = 0; retry < 5; retry++) { > + reg = FDI_RX_IIR(pipe); > + temp = intel_de_read(dev_priv, reg); > + drm_dbg_kms(&dev_priv->drm, "FDI_RX_IIR 0x%x\n", temp); > + if (temp & FDI_RX_SYMBOL_LOCK) { > + intel_de_write(dev_priv, reg, > + temp | FDI_RX_SYMBOL_LOCK); > + drm_dbg_kms(&dev_priv->drm, > + "FDI train 2 done.\n"); > + break; > + } > + udelay(50); > + } > + if (retry < 5) > + break; > + } > + if (i == 4) > + drm_err(&dev_priv->drm, "FDI train 2 fail!\n"); > + > + drm_dbg_kms(&dev_priv->drm, "FDI train done.\n"); > +} > + > +/* Manual link training for Ivy Bridge A0 parts */ > +static void ivb_manual_fdi_link_train(struct intel_crtc *crtc, > + const struct intel_crtc_state *crtc_state) > +{ > + struct drm_device *dev = crtc->base.dev; > + struct drm_i915_private *dev_priv = to_i915(dev); > + enum pipe pipe = crtc->pipe; > + i915_reg_t reg; > + u32 temp, i, j; > + > + /* Train 1: umask FDI RX Interrupt symbol_lock and bit_lock bit > + for train result */ > + reg = FDI_RX_IMR(pipe); > + temp = intel_de_read(dev_priv, reg); > + temp &= ~FDI_RX_SYMBOL_LOCK; > + temp &= ~FDI_RX_BIT_LOCK; > + intel_de_write(dev_priv, reg, temp); > + > + intel_de_posting_read(dev_priv, reg); > + udelay(150); > + > + drm_dbg_kms(&dev_priv->drm, "FDI_RX_IIR before link train 0x%x\n", > + intel_de_read(dev_priv, FDI_RX_IIR(pipe))); > + > + /* Try each vswing and preemphasis setting twice before moving on */ > + for (j = 0; j < ARRAY_SIZE(snb_b_fdi_train_param) * 2; j++) { > + /* disable first in case we need to retry */ > + reg = FDI_TX_CTL(pipe); > + temp = intel_de_read(dev_priv, reg); > + temp &= ~(FDI_LINK_TRAIN_AUTO | FDI_LINK_TRAIN_NONE_IVB); > + temp &= ~FDI_TX_ENABLE; > + intel_de_write(dev_priv, reg, temp); > + > + reg = FDI_RX_CTL(pipe); > + temp = intel_de_read(dev_priv, reg); > + temp &= ~FDI_LINK_TRAIN_AUTO; > + temp &= ~FDI_LINK_TRAIN_PATTERN_MASK_CPT; > + temp &= ~FDI_RX_ENABLE; > + intel_de_write(dev_priv, reg, temp); > + > + /* enable CPU FDI TX and PCH FDI RX */ > + reg = FDI_TX_CTL(pipe); > + temp = intel_de_read(dev_priv, reg); > + temp &= ~FDI_DP_PORT_WIDTH_MASK; > + temp |= FDI_DP_PORT_WIDTH(crtc_state->fdi_lanes); > + temp |= FDI_LINK_TRAIN_PATTERN_1_IVB; > + temp &= ~FDI_LINK_TRAIN_VOL_EMP_MASK; > + temp |= snb_b_fdi_train_param[j/2]; > + temp |= FDI_COMPOSITE_SYNC; > + intel_de_write(dev_priv, reg, temp | FDI_TX_ENABLE); > + > + intel_de_write(dev_priv, FDI_RX_MISC(pipe), > + FDI_RX_TP1_TO_TP2_48 | FDI_RX_FDI_DELAY_90); > + > + reg = FDI_RX_CTL(pipe); > + temp = intel_de_read(dev_priv, reg); > + temp |= FDI_LINK_TRAIN_PATTERN_1_CPT; > + temp |= FDI_COMPOSITE_SYNC; > + intel_de_write(dev_priv, reg, temp | FDI_RX_ENABLE); > + > + intel_de_posting_read(dev_priv, reg); > + udelay(1); /* should be 0.5us */ > + > + for (i = 0; i < 4; i++) { > + reg = FDI_RX_IIR(pipe); > + temp = intel_de_read(dev_priv, reg); > + drm_dbg_kms(&dev_priv->drm, "FDI_RX_IIR 0x%x\n", temp); > + > + if (temp & FDI_RX_BIT_LOCK || > + (intel_de_read(dev_priv, reg) & FDI_RX_BIT_LOCK)) { > + intel_de_write(dev_priv, reg, > + temp | FDI_RX_BIT_LOCK); > + drm_dbg_kms(&dev_priv->drm, > + "FDI train 1 done, level %i.\n", > + i); > + break; > + } > + udelay(1); /* should be 0.5us */ > + } > + if (i == 4) { > + drm_dbg_kms(&dev_priv->drm, > + "FDI train 1 fail on vswing %d\n", j / 2); > + continue; > + } > + > + /* Train 2 */ > + reg = FDI_TX_CTL(pipe); > + temp = intel_de_read(dev_priv, reg); > + temp &= ~FDI_LINK_TRAIN_NONE_IVB; > + temp |= FDI_LINK_TRAIN_PATTERN_2_IVB; > + intel_de_write(dev_priv, reg, temp); > + > + reg = FDI_RX_CTL(pipe); > + temp = intel_de_read(dev_priv, reg); > + temp &= ~FDI_LINK_TRAIN_PATTERN_MASK_CPT; > + temp |= FDI_LINK_TRAIN_PATTERN_2_CPT; > + intel_de_write(dev_priv, reg, temp); > + > + intel_de_posting_read(dev_priv, reg); > + udelay(2); /* should be 1.5us */ > + > + for (i = 0; i < 4; i++) { > + reg = FDI_RX_IIR(pipe); > + temp = intel_de_read(dev_priv, reg); > + drm_dbg_kms(&dev_priv->drm, "FDI_RX_IIR 0x%x\n", temp); > + > + if (temp & FDI_RX_SYMBOL_LOCK || > + (intel_de_read(dev_priv, reg) & FDI_RX_SYMBOL_LOCK)) { > + intel_de_write(dev_priv, reg, > + temp | FDI_RX_SYMBOL_LOCK); > + drm_dbg_kms(&dev_priv->drm, > + "FDI train 2 done, level %i.\n", > + i); > + goto train_done; > + } > + udelay(2); /* should be 1.5us */ > + } > + if (i == 4) > + drm_dbg_kms(&dev_priv->drm, > + "FDI train 2 fail on vswing %d\n", j / 2); > + } > + > +train_done: > + drm_dbg_kms(&dev_priv->drm, "FDI train done.\n"); > +} > + > +void ilk_fdi_pll_enable(const struct intel_crtc_state *crtc_state) > +{ > + struct intel_crtc *intel_crtc = to_intel_crtc(crtc_state->uapi.crtc); > + struct drm_i915_private *dev_priv = to_i915(intel_crtc->base.dev); > + enum pipe pipe = intel_crtc->pipe; > + i915_reg_t reg; > + u32 temp; > + > + /* enable PCH FDI RX PLL, wait warmup plus DMI latency */ > + reg = FDI_RX_CTL(pipe); > + temp = intel_de_read(dev_priv, reg); > + temp &= ~(FDI_DP_PORT_WIDTH_MASK | (0x7 << 16)); > + temp |= FDI_DP_PORT_WIDTH(crtc_state->fdi_lanes); > + temp |= (intel_de_read(dev_priv, PIPECONF(pipe)) & PIPECONF_BPC_MASK) << 11; > + intel_de_write(dev_priv, reg, temp | FDI_RX_PLL_ENABLE); > + > + intel_de_posting_read(dev_priv, reg); > + udelay(200); > + > + /* Switch from Rawclk to PCDclk */ > + temp = intel_de_read(dev_priv, reg); > + intel_de_write(dev_priv, reg, temp | FDI_PCDCLK); > + > + intel_de_posting_read(dev_priv, reg); > + udelay(200); > + > + /* Enable CPU FDI TX PLL, always on for Ironlake */ > + reg = FDI_TX_CTL(pipe); > + temp = intel_de_read(dev_priv, reg); > + if ((temp & FDI_TX_PLL_ENABLE) == 0) { > + intel_de_write(dev_priv, reg, temp | FDI_TX_PLL_ENABLE); > + > + intel_de_posting_read(dev_priv, reg); > + udelay(100); > + } > +} > + > +void ilk_fdi_pll_disable(struct intel_crtc *intel_crtc) > +{ > + struct drm_device *dev = intel_crtc->base.dev; > + struct drm_i915_private *dev_priv = to_i915(dev); > + enum pipe pipe = intel_crtc->pipe; > + i915_reg_t reg; > + u32 temp; > + > + /* Switch from PCDclk to Rawclk */ > + reg = FDI_RX_CTL(pipe); > + temp = intel_de_read(dev_priv, reg); > + intel_de_write(dev_priv, reg, temp & ~FDI_PCDCLK); > + > + /* Disable CPU FDI TX PLL */ > + reg = FDI_TX_CTL(pipe); > + temp = intel_de_read(dev_priv, reg); > + intel_de_write(dev_priv, reg, temp & ~FDI_TX_PLL_ENABLE); > + > + intel_de_posting_read(dev_priv, reg); > + udelay(100); > + > + reg = FDI_RX_CTL(pipe); > + temp = intel_de_read(dev_priv, reg); > + intel_de_write(dev_priv, reg, temp & ~FDI_RX_PLL_ENABLE); > + > + /* Wait for the clocks to turn off. */ > + intel_de_posting_read(dev_priv, reg); > + udelay(100); > +} > + > +void ilk_fdi_disable(struct intel_crtc *crtc) > +{ > + struct drm_i915_private *dev_priv = to_i915(crtc->base.dev); > + enum pipe pipe = crtc->pipe; > + i915_reg_t reg; > + u32 temp; > + > + /* disable CPU FDI tx and PCH FDI rx */ > + reg = FDI_TX_CTL(pipe); > + temp = intel_de_read(dev_priv, reg); > + intel_de_write(dev_priv, reg, temp & ~FDI_TX_ENABLE); > + intel_de_posting_read(dev_priv, reg); > + > + reg = FDI_RX_CTL(pipe); > + temp = intel_de_read(dev_priv, reg); > + temp &= ~(0x7 << 16); > + temp |= (intel_de_read(dev_priv, PIPECONF(pipe)) & PIPECONF_BPC_MASK) << 11; > + intel_de_write(dev_priv, reg, temp & ~FDI_RX_ENABLE); > + > + intel_de_posting_read(dev_priv, reg); > + udelay(100); > + > + /* Ironlake workaround, disable clock pointer after downing FDI */ > + if (HAS_PCH_IBX(dev_priv)) > + intel_de_write(dev_priv, FDI_RX_CHICKEN(pipe), > + FDI_RX_PHASE_SYNC_POINTER_OVR); > + > + /* still set train pattern 1 */ > + reg = FDI_TX_CTL(pipe); > + temp = intel_de_read(dev_priv, reg); > + temp &= ~FDI_LINK_TRAIN_NONE; > + temp |= FDI_LINK_TRAIN_PATTERN_1; > + intel_de_write(dev_priv, reg, temp); > + > + reg = FDI_RX_CTL(pipe); > + temp = intel_de_read(dev_priv, reg); > + if (HAS_PCH_CPT(dev_priv)) { > + temp &= ~FDI_LINK_TRAIN_PATTERN_MASK_CPT; > + temp |= FDI_LINK_TRAIN_PATTERN_1_CPT; > + } else { > + temp &= ~FDI_LINK_TRAIN_NONE; > + temp |= FDI_LINK_TRAIN_PATTERN_1; > + } > + /* BPC in FDI rx is consistent with that in PIPECONF */ > + temp &= ~(0x07 << 16); > + temp |= (intel_de_read(dev_priv, PIPECONF(pipe)) & PIPECONF_BPC_MASK) << 11; > + intel_de_write(dev_priv, reg, temp); > + > + intel_de_posting_read(dev_priv, reg); > + udelay(100); > +} > + > +void > +intel_init_fdi_hook(struct drm_i915_private *dev_priv) > +{ > + if (IS_GEN(dev_priv, 5)) { > + dev_priv->display.fdi_link_train = ilk_fdi_link_train; > + } else if (IS_GEN(dev_priv, 6)) { > + dev_priv->display.fdi_link_train = gen6_fdi_link_train; > + } else if (IS_IVYBRIDGE(dev_priv)) { > + /* FIXME: detect B0+ stepping and use auto training */ > + dev_priv->display.fdi_link_train = ivb_manual_fdi_link_train; > + } > +} > -- > 2.27.0 > > _______________________________________________ > Intel-gfx mailing list > Intel-gfx@lists.freedesktop.org > https://lists.freedesktop.org/mailman/listinfo/intel-gfx
On Wed, 9 Dec 2020 at 20:48, Daniel Vetter <daniel@ffwll.ch> wrote: > > On Wed, Dec 9, 2020 at 5:22 AM Dave Airlie <airlied@gmail.com> wrote: > > > > From: Dave Airlie <airlied@redhat.com> > > > > This just refactors out the fdi code to a separate file. > > > > Signed-off-by: Dave Airlie <airlied@redhat.com> > > There's also hsw_fdi_link_train from intel_ddi.c (another fairly big > file), I think that also belongs in here. It's not in the vtable > because it's directly called from the hsw crt encoder. With that this > looks reasonable to me. It does but it's definitely a lot messier to move, it has a lot of links into the ddi code and tables. I'll add another patch to the end that we can look at to decide if there's a cleaner way to do it. Dave.
diff --git a/drivers/gpu/drm/i915/Makefile b/drivers/gpu/drm/i915/Makefile index 8b357c212ae2..f74b7c13ffbd 100644 --- a/drivers/gpu/drm/i915/Makefile +++ b/drivers/gpu/drm/i915/Makefile @@ -206,6 +206,7 @@ i915-y += \ display/intel_dpll_mgr.o \ display/intel_dsb.o \ display/intel_fbc.o \ + display/intel_fdi.o \ display/intel_fifo_underrun.o \ display/intel_frontbuffer.o \ display/intel_global_state.o \ diff --git a/drivers/gpu/drm/i915/display/intel_display.c b/drivers/gpu/drm/i915/display/intel_display.c index 788b1def61ee..5119a0040e36 100644 --- a/drivers/gpu/drm/i915/display/intel_display.c +++ b/drivers/gpu/drm/i915/display/intel_display.c @@ -170,16 +170,6 @@ static void intel_update_czclk(struct drm_i915_private *dev_priv) dev_priv->czclk_freq); } -/* units of 100MHz */ -static u32 intel_fdi_link_freq(struct drm_i915_private *dev_priv, - const struct intel_crtc_state *pipe_config) -{ - if (HAS_DDI(dev_priv)) - return pipe_config->port_clock; /* SPLL */ - else - return dev_priv->fdi_pll_freq; -} - /* WA Display #0827: Gen9:all */ static void skl_wa_827(struct drm_i915_private *dev_priv, enum pipe pipe, bool enable) @@ -3776,532 +3766,6 @@ static void icl_set_pipe_chicken(struct intel_crtc *crtc) intel_de_write(dev_priv, PIPE_CHICKEN(pipe), tmp); } -static void intel_fdi_normal_train(struct intel_crtc *crtc) -{ - struct drm_device *dev = crtc->base.dev; - struct drm_i915_private *dev_priv = to_i915(dev); - enum pipe pipe = crtc->pipe; - i915_reg_t reg; - u32 temp; - - /* enable normal train */ - reg = FDI_TX_CTL(pipe); - temp = intel_de_read(dev_priv, reg); - if (IS_IVYBRIDGE(dev_priv)) { - temp &= ~FDI_LINK_TRAIN_NONE_IVB; - temp |= FDI_LINK_TRAIN_NONE_IVB | FDI_TX_ENHANCE_FRAME_ENABLE; - } else { - temp &= ~FDI_LINK_TRAIN_NONE; - temp |= FDI_LINK_TRAIN_NONE | FDI_TX_ENHANCE_FRAME_ENABLE; - } - intel_de_write(dev_priv, reg, temp); - - reg = FDI_RX_CTL(pipe); - temp = intel_de_read(dev_priv, reg); - if (HAS_PCH_CPT(dev_priv)) { - temp &= ~FDI_LINK_TRAIN_PATTERN_MASK_CPT; - temp |= FDI_LINK_TRAIN_NORMAL_CPT; - } else { - temp &= ~FDI_LINK_TRAIN_NONE; - temp |= FDI_LINK_TRAIN_NONE; - } - intel_de_write(dev_priv, reg, temp | FDI_RX_ENHANCE_FRAME_ENABLE); - - /* wait one idle pattern time */ - intel_de_posting_read(dev_priv, reg); - udelay(1000); - - /* IVB wants error correction enabled */ - if (IS_IVYBRIDGE(dev_priv)) - intel_de_write(dev_priv, reg, - intel_de_read(dev_priv, reg) | FDI_FS_ERRC_ENABLE | FDI_FE_ERRC_ENABLE); -} - -/* The FDI link training functions for ILK/Ibexpeak. */ -static void ilk_fdi_link_train(struct intel_crtc *crtc, - const struct intel_crtc_state *crtc_state) -{ - struct drm_device *dev = crtc->base.dev; - struct drm_i915_private *dev_priv = to_i915(dev); - enum pipe pipe = crtc->pipe; - i915_reg_t reg; - u32 temp, tries; - - /* FDI needs bits from pipe first */ - assert_pipe_enabled(dev_priv, crtc_state->cpu_transcoder); - - /* Train 1: umask FDI RX Interrupt symbol_lock and bit_lock bit - for train result */ - reg = FDI_RX_IMR(pipe); - temp = intel_de_read(dev_priv, reg); - temp &= ~FDI_RX_SYMBOL_LOCK; - temp &= ~FDI_RX_BIT_LOCK; - intel_de_write(dev_priv, reg, temp); - intel_de_read(dev_priv, reg); - udelay(150); - - /* enable CPU FDI TX and PCH FDI RX */ - reg = FDI_TX_CTL(pipe); - temp = intel_de_read(dev_priv, reg); - temp &= ~FDI_DP_PORT_WIDTH_MASK; - temp |= FDI_DP_PORT_WIDTH(crtc_state->fdi_lanes); - temp &= ~FDI_LINK_TRAIN_NONE; - temp |= FDI_LINK_TRAIN_PATTERN_1; - intel_de_write(dev_priv, reg, temp | FDI_TX_ENABLE); - - reg = FDI_RX_CTL(pipe); - temp = intel_de_read(dev_priv, reg); - temp &= ~FDI_LINK_TRAIN_NONE; - temp |= FDI_LINK_TRAIN_PATTERN_1; - intel_de_write(dev_priv, reg, temp | FDI_RX_ENABLE); - - intel_de_posting_read(dev_priv, reg); - udelay(150); - - /* Ironlake workaround, enable clock pointer after FDI enable*/ - intel_de_write(dev_priv, FDI_RX_CHICKEN(pipe), - FDI_RX_PHASE_SYNC_POINTER_OVR); - intel_de_write(dev_priv, FDI_RX_CHICKEN(pipe), - FDI_RX_PHASE_SYNC_POINTER_OVR | FDI_RX_PHASE_SYNC_POINTER_EN); - - reg = FDI_RX_IIR(pipe); - for (tries = 0; tries < 5; tries++) { - temp = intel_de_read(dev_priv, reg); - drm_dbg_kms(&dev_priv->drm, "FDI_RX_IIR 0x%x\n", temp); - - if ((temp & FDI_RX_BIT_LOCK)) { - drm_dbg_kms(&dev_priv->drm, "FDI train 1 done.\n"); - intel_de_write(dev_priv, reg, temp | FDI_RX_BIT_LOCK); - break; - } - } - if (tries == 5) - drm_err(&dev_priv->drm, "FDI train 1 fail!\n"); - - /* Train 2 */ - reg = FDI_TX_CTL(pipe); - temp = intel_de_read(dev_priv, reg); - temp &= ~FDI_LINK_TRAIN_NONE; - temp |= FDI_LINK_TRAIN_PATTERN_2; - intel_de_write(dev_priv, reg, temp); - - reg = FDI_RX_CTL(pipe); - temp = intel_de_read(dev_priv, reg); - temp &= ~FDI_LINK_TRAIN_NONE; - temp |= FDI_LINK_TRAIN_PATTERN_2; - intel_de_write(dev_priv, reg, temp); - - intel_de_posting_read(dev_priv, reg); - udelay(150); - - reg = FDI_RX_IIR(pipe); - for (tries = 0; tries < 5; tries++) { - temp = intel_de_read(dev_priv, reg); - drm_dbg_kms(&dev_priv->drm, "FDI_RX_IIR 0x%x\n", temp); - - if (temp & FDI_RX_SYMBOL_LOCK) { - intel_de_write(dev_priv, reg, - temp | FDI_RX_SYMBOL_LOCK); - drm_dbg_kms(&dev_priv->drm, "FDI train 2 done.\n"); - break; - } - } - if (tries == 5) - drm_err(&dev_priv->drm, "FDI train 2 fail!\n"); - - drm_dbg_kms(&dev_priv->drm, "FDI train done\n"); - -} - -static const int snb_b_fdi_train_param[] = { - FDI_LINK_TRAIN_400MV_0DB_SNB_B, - FDI_LINK_TRAIN_400MV_6DB_SNB_B, - FDI_LINK_TRAIN_600MV_3_5DB_SNB_B, - FDI_LINK_TRAIN_800MV_0DB_SNB_B, -}; - -/* The FDI link training functions for SNB/Cougarpoint. */ -static void gen6_fdi_link_train(struct intel_crtc *crtc, - const struct intel_crtc_state *crtc_state) -{ - struct drm_device *dev = crtc->base.dev; - struct drm_i915_private *dev_priv = to_i915(dev); - enum pipe pipe = crtc->pipe; - i915_reg_t reg; - u32 temp, i, retry; - - /* Train 1: umask FDI RX Interrupt symbol_lock and bit_lock bit - for train result */ - reg = FDI_RX_IMR(pipe); - temp = intel_de_read(dev_priv, reg); - temp &= ~FDI_RX_SYMBOL_LOCK; - temp &= ~FDI_RX_BIT_LOCK; - intel_de_write(dev_priv, reg, temp); - - intel_de_posting_read(dev_priv, reg); - udelay(150); - - /* enable CPU FDI TX and PCH FDI RX */ - reg = FDI_TX_CTL(pipe); - temp = intel_de_read(dev_priv, reg); - temp &= ~FDI_DP_PORT_WIDTH_MASK; - temp |= FDI_DP_PORT_WIDTH(crtc_state->fdi_lanes); - temp &= ~FDI_LINK_TRAIN_NONE; - temp |= FDI_LINK_TRAIN_PATTERN_1; - temp &= ~FDI_LINK_TRAIN_VOL_EMP_MASK; - /* SNB-B */ - temp |= FDI_LINK_TRAIN_400MV_0DB_SNB_B; - intel_de_write(dev_priv, reg, temp | FDI_TX_ENABLE); - - intel_de_write(dev_priv, FDI_RX_MISC(pipe), - FDI_RX_TP1_TO_TP2_48 | FDI_RX_FDI_DELAY_90); - - reg = FDI_RX_CTL(pipe); - temp = intel_de_read(dev_priv, reg); - if (HAS_PCH_CPT(dev_priv)) { - temp &= ~FDI_LINK_TRAIN_PATTERN_MASK_CPT; - temp |= FDI_LINK_TRAIN_PATTERN_1_CPT; - } else { - temp &= ~FDI_LINK_TRAIN_NONE; - temp |= FDI_LINK_TRAIN_PATTERN_1; - } - intel_de_write(dev_priv, reg, temp | FDI_RX_ENABLE); - - intel_de_posting_read(dev_priv, reg); - udelay(150); - - for (i = 0; i < 4; i++) { - reg = FDI_TX_CTL(pipe); - temp = intel_de_read(dev_priv, reg); - temp &= ~FDI_LINK_TRAIN_VOL_EMP_MASK; - temp |= snb_b_fdi_train_param[i]; - intel_de_write(dev_priv, reg, temp); - - intel_de_posting_read(dev_priv, reg); - udelay(500); - - for (retry = 0; retry < 5; retry++) { - reg = FDI_RX_IIR(pipe); - temp = intel_de_read(dev_priv, reg); - drm_dbg_kms(&dev_priv->drm, "FDI_RX_IIR 0x%x\n", temp); - if (temp & FDI_RX_BIT_LOCK) { - intel_de_write(dev_priv, reg, - temp | FDI_RX_BIT_LOCK); - drm_dbg_kms(&dev_priv->drm, - "FDI train 1 done.\n"); - break; - } - udelay(50); - } - if (retry < 5) - break; - } - if (i == 4) - drm_err(&dev_priv->drm, "FDI train 1 fail!\n"); - - /* Train 2 */ - reg = FDI_TX_CTL(pipe); - temp = intel_de_read(dev_priv, reg); - temp &= ~FDI_LINK_TRAIN_NONE; - temp |= FDI_LINK_TRAIN_PATTERN_2; - if (IS_GEN(dev_priv, 6)) { - temp &= ~FDI_LINK_TRAIN_VOL_EMP_MASK; - /* SNB-B */ - temp |= FDI_LINK_TRAIN_400MV_0DB_SNB_B; - } - intel_de_write(dev_priv, reg, temp); - - reg = FDI_RX_CTL(pipe); - temp = intel_de_read(dev_priv, reg); - if (HAS_PCH_CPT(dev_priv)) { - temp &= ~FDI_LINK_TRAIN_PATTERN_MASK_CPT; - temp |= FDI_LINK_TRAIN_PATTERN_2_CPT; - } else { - temp &= ~FDI_LINK_TRAIN_NONE; - temp |= FDI_LINK_TRAIN_PATTERN_2; - } - intel_de_write(dev_priv, reg, temp); - - intel_de_posting_read(dev_priv, reg); - udelay(150); - - for (i = 0; i < 4; i++) { - reg = FDI_TX_CTL(pipe); - temp = intel_de_read(dev_priv, reg); - temp &= ~FDI_LINK_TRAIN_VOL_EMP_MASK; - temp |= snb_b_fdi_train_param[i]; - intel_de_write(dev_priv, reg, temp); - - intel_de_posting_read(dev_priv, reg); - udelay(500); - - for (retry = 0; retry < 5; retry++) { - reg = FDI_RX_IIR(pipe); - temp = intel_de_read(dev_priv, reg); - drm_dbg_kms(&dev_priv->drm, "FDI_RX_IIR 0x%x\n", temp); - if (temp & FDI_RX_SYMBOL_LOCK) { - intel_de_write(dev_priv, reg, - temp | FDI_RX_SYMBOL_LOCK); - drm_dbg_kms(&dev_priv->drm, - "FDI train 2 done.\n"); - break; - } - udelay(50); - } - if (retry < 5) - break; - } - if (i == 4) - drm_err(&dev_priv->drm, "FDI train 2 fail!\n"); - - drm_dbg_kms(&dev_priv->drm, "FDI train done.\n"); -} - -/* Manual link training for Ivy Bridge A0 parts */ -static void ivb_manual_fdi_link_train(struct intel_crtc *crtc, - const struct intel_crtc_state *crtc_state) -{ - struct drm_device *dev = crtc->base.dev; - struct drm_i915_private *dev_priv = to_i915(dev); - enum pipe pipe = crtc->pipe; - i915_reg_t reg; - u32 temp, i, j; - - /* Train 1: umask FDI RX Interrupt symbol_lock and bit_lock bit - for train result */ - reg = FDI_RX_IMR(pipe); - temp = intel_de_read(dev_priv, reg); - temp &= ~FDI_RX_SYMBOL_LOCK; - temp &= ~FDI_RX_BIT_LOCK; - intel_de_write(dev_priv, reg, temp); - - intel_de_posting_read(dev_priv, reg); - udelay(150); - - drm_dbg_kms(&dev_priv->drm, "FDI_RX_IIR before link train 0x%x\n", - intel_de_read(dev_priv, FDI_RX_IIR(pipe))); - - /* Try each vswing and preemphasis setting twice before moving on */ - for (j = 0; j < ARRAY_SIZE(snb_b_fdi_train_param) * 2; j++) { - /* disable first in case we need to retry */ - reg = FDI_TX_CTL(pipe); - temp = intel_de_read(dev_priv, reg); - temp &= ~(FDI_LINK_TRAIN_AUTO | FDI_LINK_TRAIN_NONE_IVB); - temp &= ~FDI_TX_ENABLE; - intel_de_write(dev_priv, reg, temp); - - reg = FDI_RX_CTL(pipe); - temp = intel_de_read(dev_priv, reg); - temp &= ~FDI_LINK_TRAIN_AUTO; - temp &= ~FDI_LINK_TRAIN_PATTERN_MASK_CPT; - temp &= ~FDI_RX_ENABLE; - intel_de_write(dev_priv, reg, temp); - - /* enable CPU FDI TX and PCH FDI RX */ - reg = FDI_TX_CTL(pipe); - temp = intel_de_read(dev_priv, reg); - temp &= ~FDI_DP_PORT_WIDTH_MASK; - temp |= FDI_DP_PORT_WIDTH(crtc_state->fdi_lanes); - temp |= FDI_LINK_TRAIN_PATTERN_1_IVB; - temp &= ~FDI_LINK_TRAIN_VOL_EMP_MASK; - temp |= snb_b_fdi_train_param[j/2]; - temp |= FDI_COMPOSITE_SYNC; - intel_de_write(dev_priv, reg, temp | FDI_TX_ENABLE); - - intel_de_write(dev_priv, FDI_RX_MISC(pipe), - FDI_RX_TP1_TO_TP2_48 | FDI_RX_FDI_DELAY_90); - - reg = FDI_RX_CTL(pipe); - temp = intel_de_read(dev_priv, reg); - temp |= FDI_LINK_TRAIN_PATTERN_1_CPT; - temp |= FDI_COMPOSITE_SYNC; - intel_de_write(dev_priv, reg, temp | FDI_RX_ENABLE); - - intel_de_posting_read(dev_priv, reg); - udelay(1); /* should be 0.5us */ - - for (i = 0; i < 4; i++) { - reg = FDI_RX_IIR(pipe); - temp = intel_de_read(dev_priv, reg); - drm_dbg_kms(&dev_priv->drm, "FDI_RX_IIR 0x%x\n", temp); - - if (temp & FDI_RX_BIT_LOCK || - (intel_de_read(dev_priv, reg) & FDI_RX_BIT_LOCK)) { - intel_de_write(dev_priv, reg, - temp | FDI_RX_BIT_LOCK); - drm_dbg_kms(&dev_priv->drm, - "FDI train 1 done, level %i.\n", - i); - break; - } - udelay(1); /* should be 0.5us */ - } - if (i == 4) { - drm_dbg_kms(&dev_priv->drm, - "FDI train 1 fail on vswing %d\n", j / 2); - continue; - } - - /* Train 2 */ - reg = FDI_TX_CTL(pipe); - temp = intel_de_read(dev_priv, reg); - temp &= ~FDI_LINK_TRAIN_NONE_IVB; - temp |= FDI_LINK_TRAIN_PATTERN_2_IVB; - intel_de_write(dev_priv, reg, temp); - - reg = FDI_RX_CTL(pipe); - temp = intel_de_read(dev_priv, reg); - temp &= ~FDI_LINK_TRAIN_PATTERN_MASK_CPT; - temp |= FDI_LINK_TRAIN_PATTERN_2_CPT; - intel_de_write(dev_priv, reg, temp); - - intel_de_posting_read(dev_priv, reg); - udelay(2); /* should be 1.5us */ - - for (i = 0; i < 4; i++) { - reg = FDI_RX_IIR(pipe); - temp = intel_de_read(dev_priv, reg); - drm_dbg_kms(&dev_priv->drm, "FDI_RX_IIR 0x%x\n", temp); - - if (temp & FDI_RX_SYMBOL_LOCK || - (intel_de_read(dev_priv, reg) & FDI_RX_SYMBOL_LOCK)) { - intel_de_write(dev_priv, reg, - temp | FDI_RX_SYMBOL_LOCK); - drm_dbg_kms(&dev_priv->drm, - "FDI train 2 done, level %i.\n", - i); - goto train_done; - } - udelay(2); /* should be 1.5us */ - } - if (i == 4) - drm_dbg_kms(&dev_priv->drm, - "FDI train 2 fail on vswing %d\n", j / 2); - } - -train_done: - drm_dbg_kms(&dev_priv->drm, "FDI train done.\n"); -} - -static void ilk_fdi_pll_enable(const struct intel_crtc_state *crtc_state) -{ - struct intel_crtc *intel_crtc = to_intel_crtc(crtc_state->uapi.crtc); - struct drm_i915_private *dev_priv = to_i915(intel_crtc->base.dev); - enum pipe pipe = intel_crtc->pipe; - i915_reg_t reg; - u32 temp; - - /* enable PCH FDI RX PLL, wait warmup plus DMI latency */ - reg = FDI_RX_CTL(pipe); - temp = intel_de_read(dev_priv, reg); - temp &= ~(FDI_DP_PORT_WIDTH_MASK | (0x7 << 16)); - temp |= FDI_DP_PORT_WIDTH(crtc_state->fdi_lanes); - temp |= (intel_de_read(dev_priv, PIPECONF(pipe)) & PIPECONF_BPC_MASK) << 11; - intel_de_write(dev_priv, reg, temp | FDI_RX_PLL_ENABLE); - - intel_de_posting_read(dev_priv, reg); - udelay(200); - - /* Switch from Rawclk to PCDclk */ - temp = intel_de_read(dev_priv, reg); - intel_de_write(dev_priv, reg, temp | FDI_PCDCLK); - - intel_de_posting_read(dev_priv, reg); - udelay(200); - - /* Enable CPU FDI TX PLL, always on for Ironlake */ - reg = FDI_TX_CTL(pipe); - temp = intel_de_read(dev_priv, reg); - if ((temp & FDI_TX_PLL_ENABLE) == 0) { - intel_de_write(dev_priv, reg, temp | FDI_TX_PLL_ENABLE); - - intel_de_posting_read(dev_priv, reg); - udelay(100); - } -} - -static void ilk_fdi_pll_disable(struct intel_crtc *intel_crtc) -{ - struct drm_device *dev = intel_crtc->base.dev; - struct drm_i915_private *dev_priv = to_i915(dev); - enum pipe pipe = intel_crtc->pipe; - i915_reg_t reg; - u32 temp; - - /* Switch from PCDclk to Rawclk */ - reg = FDI_RX_CTL(pipe); - temp = intel_de_read(dev_priv, reg); - intel_de_write(dev_priv, reg, temp & ~FDI_PCDCLK); - - /* Disable CPU FDI TX PLL */ - reg = FDI_TX_CTL(pipe); - temp = intel_de_read(dev_priv, reg); - intel_de_write(dev_priv, reg, temp & ~FDI_TX_PLL_ENABLE); - - intel_de_posting_read(dev_priv, reg); - udelay(100); - - reg = FDI_RX_CTL(pipe); - temp = intel_de_read(dev_priv, reg); - intel_de_write(dev_priv, reg, temp & ~FDI_RX_PLL_ENABLE); - - /* Wait for the clocks to turn off. */ - intel_de_posting_read(dev_priv, reg); - udelay(100); -} - -static void ilk_fdi_disable(struct intel_crtc *crtc) -{ - struct drm_i915_private *dev_priv = to_i915(crtc->base.dev); - enum pipe pipe = crtc->pipe; - i915_reg_t reg; - u32 temp; - - /* disable CPU FDI tx and PCH FDI rx */ - reg = FDI_TX_CTL(pipe); - temp = intel_de_read(dev_priv, reg); - intel_de_write(dev_priv, reg, temp & ~FDI_TX_ENABLE); - intel_de_posting_read(dev_priv, reg); - - reg = FDI_RX_CTL(pipe); - temp = intel_de_read(dev_priv, reg); - temp &= ~(0x7 << 16); - temp |= (intel_de_read(dev_priv, PIPECONF(pipe)) & PIPECONF_BPC_MASK) << 11; - intel_de_write(dev_priv, reg, temp & ~FDI_RX_ENABLE); - - intel_de_posting_read(dev_priv, reg); - udelay(100); - - /* Ironlake workaround, disable clock pointer after downing FDI */ - if (HAS_PCH_IBX(dev_priv)) - intel_de_write(dev_priv, FDI_RX_CHICKEN(pipe), - FDI_RX_PHASE_SYNC_POINTER_OVR); - - /* still set train pattern 1 */ - reg = FDI_TX_CTL(pipe); - temp = intel_de_read(dev_priv, reg); - temp &= ~FDI_LINK_TRAIN_NONE; - temp |= FDI_LINK_TRAIN_PATTERN_1; - intel_de_write(dev_priv, reg, temp); - - reg = FDI_RX_CTL(pipe); - temp = intel_de_read(dev_priv, reg); - if (HAS_PCH_CPT(dev_priv)) { - temp &= ~FDI_LINK_TRAIN_PATTERN_MASK_CPT; - temp |= FDI_LINK_TRAIN_PATTERN_1_CPT; - } else { - temp &= ~FDI_LINK_TRAIN_NONE; - temp |= FDI_LINK_TRAIN_PATTERN_1; - } - /* BPC in FDI rx is consistent with that in PIPECONF */ - temp &= ~(0x07 << 16); - temp |= (intel_de_read(dev_priv, PIPECONF(pipe)) & PIPECONF_BPC_MASK) << 11; - intel_de_write(dev_priv, reg, temp); - - intel_de_posting_read(dev_priv, reg); - udelay(100); -} - bool intel_has_pending_fb_unpin(struct drm_i915_private *dev_priv) { struct drm_crtc *crtc; @@ -6708,143 +6172,6 @@ static void intel_connector_verify_state(struct intel_crtc_state *crtc_state, } } -static int pipe_required_fdi_lanes(struct intel_crtc_state *crtc_state) -{ - if (crtc_state->hw.enable && crtc_state->has_pch_encoder) - return crtc_state->fdi_lanes; - - return 0; -} - -static int ilk_check_fdi_lanes(struct drm_device *dev, enum pipe pipe, - struct intel_crtc_state *pipe_config) -{ - struct drm_i915_private *dev_priv = to_i915(dev); - struct drm_atomic_state *state = pipe_config->uapi.state; - struct intel_crtc *other_crtc; - struct intel_crtc_state *other_crtc_state; - - drm_dbg_kms(&dev_priv->drm, - "checking fdi config on pipe %c, lanes %i\n", - pipe_name(pipe), pipe_config->fdi_lanes); - if (pipe_config->fdi_lanes > 4) { - drm_dbg_kms(&dev_priv->drm, - "invalid fdi lane config on pipe %c: %i lanes\n", - pipe_name(pipe), pipe_config->fdi_lanes); - return -EINVAL; - } - - if (IS_HASWELL(dev_priv) || IS_BROADWELL(dev_priv)) { - if (pipe_config->fdi_lanes > 2) { - drm_dbg_kms(&dev_priv->drm, - "only 2 lanes on haswell, required: %i lanes\n", - pipe_config->fdi_lanes); - return -EINVAL; - } else { - return 0; - } - } - - if (INTEL_NUM_PIPES(dev_priv) == 2) - return 0; - - /* Ivybridge 3 pipe is really complicated */ - switch (pipe) { - case PIPE_A: - return 0; - case PIPE_B: - if (pipe_config->fdi_lanes <= 2) - return 0; - - other_crtc = intel_get_crtc_for_pipe(dev_priv, PIPE_C); - other_crtc_state = - intel_atomic_get_crtc_state(state, other_crtc); - if (IS_ERR(other_crtc_state)) - return PTR_ERR(other_crtc_state); - - if (pipe_required_fdi_lanes(other_crtc_state) > 0) { - drm_dbg_kms(&dev_priv->drm, - "invalid shared fdi lane config on pipe %c: %i lanes\n", - pipe_name(pipe), pipe_config->fdi_lanes); - return -EINVAL; - } - return 0; - case PIPE_C: - if (pipe_config->fdi_lanes > 2) { - drm_dbg_kms(&dev_priv->drm, - "only 2 lanes on pipe %c: required %i lanes\n", - pipe_name(pipe), pipe_config->fdi_lanes); - return -EINVAL; - } - - other_crtc = intel_get_crtc_for_pipe(dev_priv, PIPE_B); - other_crtc_state = - intel_atomic_get_crtc_state(state, other_crtc); - if (IS_ERR(other_crtc_state)) - return PTR_ERR(other_crtc_state); - - if (pipe_required_fdi_lanes(other_crtc_state) > 2) { - drm_dbg_kms(&dev_priv->drm, - "fdi link B uses too many lanes to enable link C\n"); - return -EINVAL; - } - return 0; - default: - BUG(); - } -} - -#define RETRY 1 -static int ilk_fdi_compute_config(struct intel_crtc *intel_crtc, - struct intel_crtc_state *pipe_config) -{ - struct drm_device *dev = intel_crtc->base.dev; - struct drm_i915_private *i915 = to_i915(dev); - const struct drm_display_mode *adjusted_mode = &pipe_config->hw.adjusted_mode; - int lane, link_bw, fdi_dotclock, ret; - bool needs_recompute = false; - -retry: - /* FDI is a binary signal running at ~2.7GHz, encoding - * each output octet as 10 bits. The actual frequency - * is stored as a divider into a 100MHz clock, and the - * mode pixel clock is stored in units of 1KHz. - * Hence the bw of each lane in terms of the mode signal - * is: - */ - link_bw = intel_fdi_link_freq(i915, pipe_config); - - fdi_dotclock = adjusted_mode->crtc_clock; - - lane = ilk_get_lanes_required(fdi_dotclock, link_bw, - pipe_config->pipe_bpp); - - pipe_config->fdi_lanes = lane; - - intel_link_compute_m_n(pipe_config->pipe_bpp, lane, fdi_dotclock, - link_bw, &pipe_config->fdi_m_n, false, false); - - ret = ilk_check_fdi_lanes(dev, intel_crtc->pipe, pipe_config); - if (ret == -EDEADLK) - return ret; - - if (ret == -EINVAL && pipe_config->pipe_bpp > 6*3) { - pipe_config->pipe_bpp -= 2*3; - drm_dbg_kms(&i915->drm, - "fdi link bw constraint, reducing pipe bpp to %i\n", - pipe_config->pipe_bpp); - needs_recompute = true; - pipe_config->bw_constrained = true; - - goto retry; - } - - if (needs_recompute) - return RETRY; - - return ret; -} - bool hsw_crtc_state_ips_capable(const struct intel_crtc_state *crtc_state) { struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc); @@ -11379,7 +10706,7 @@ intel_modeset_pipe_config(struct intel_atomic_state *state, return ret; } - if (ret == RETRY) { + if (ret == I915_DISPLAY_CONFIG_RETRY) { if (drm_WARN(&i915->drm, !retry, "loop in pipe configuration computation\n")) return -EINVAL; @@ -15178,14 +14505,7 @@ void intel_init_display_hooks(struct drm_i915_private *dev_priv) dev_priv->display.crtc_disable = i9xx_crtc_disable; } - if (IS_GEN(dev_priv, 5)) { - dev_priv->display.fdi_link_train = ilk_fdi_link_train; - } else if (IS_GEN(dev_priv, 6)) { - dev_priv->display.fdi_link_train = gen6_fdi_link_train; - } else if (IS_IVYBRIDGE(dev_priv)) { - /* FIXME: detect B0+ stepping and use auto training */ - dev_priv->display.fdi_link_train = ivb_manual_fdi_link_train; - } + intel_init_fdi_hook(dev_priv); if (INTEL_GEN(dev_priv) >= 9) { dev_priv->display.commit_modeset_enables = skl_commit_modeset_enables; diff --git a/drivers/gpu/drm/i915/display/intel_display.h b/drivers/gpu/drm/i915/display/intel_display.h index f1e36cca86c1..731ea6f7777a 100644 --- a/drivers/gpu/drm/i915/display/intel_display.h +++ b/drivers/gpu/drm/i915/display/intel_display.h @@ -677,6 +677,16 @@ void vlv_compute_dpll(struct intel_crtc *crtc, void chv_compute_dpll(struct intel_crtc *crtc, struct intel_crtc_state *pipe_config); +/* fdi */ +#define I915_DISPLAY_CONFIG_RETRY 1 +int ilk_fdi_compute_config(struct intel_crtc *intel_crtc, + struct intel_crtc_state *pipe_config); +void intel_fdi_normal_train(struct intel_crtc *crtc); +void ilk_fdi_disable(struct intel_crtc *crtc); +void ilk_fdi_pll_disable(struct intel_crtc *intel_crtc); +void ilk_fdi_pll_enable(const struct intel_crtc_state *crtc_state); +void intel_init_fdi_hook(struct drm_i915_private *dev_priv); + /* modesetting */ void intel_modeset_init_hw(struct drm_i915_private *i915); int intel_modeset_init_noirq(struct drm_i915_private *i915); diff --git a/drivers/gpu/drm/i915/display/intel_display_types.h b/drivers/gpu/drm/i915/display/intel_display_types.h index 823083b231bc..9c684552903c 100644 --- a/drivers/gpu/drm/i915/display/intel_display_types.h +++ b/drivers/gpu/drm/i915/display/intel_display_types.h @@ -1804,4 +1804,13 @@ static inline u32 i9xx_dpll_compute_fp(struct dpll *dpll) return dpll->n << 16 | dpll->m1 << 8 | dpll->m2; } +static inline u32 intel_fdi_link_freq(struct drm_i915_private *dev_priv, + const struct intel_crtc_state *pipe_config) +{ + if (HAS_DDI(dev_priv)) + return pipe_config->port_clock; /* SPLL */ + else + return dev_priv->fdi_pll_freq; +} + #endif /* __INTEL_DISPLAY_TYPES_H__ */ diff --git a/drivers/gpu/drm/i915/display/intel_fdi.c b/drivers/gpu/drm/i915/display/intel_fdi.c new file mode 100644 index 000000000000..bf4ef84686ef --- /dev/null +++ b/drivers/gpu/drm/i915/display/intel_fdi.c @@ -0,0 +1,682 @@ +// SPDX-License-Identifier: MIT +/* + * Copyright © 2020 Intel Corporation + */ +#include "intel_atomic.h" +#include "intel_display_types.h" + +/* units of 100MHz */ +static int pipe_required_fdi_lanes(struct intel_crtc_state *crtc_state) +{ + if (crtc_state->hw.enable && crtc_state->has_pch_encoder) + return crtc_state->fdi_lanes; + + return 0; +} + +static int ilk_check_fdi_lanes(struct drm_device *dev, enum pipe pipe, + struct intel_crtc_state *pipe_config) +{ + struct drm_i915_private *dev_priv = to_i915(dev); + struct drm_atomic_state *state = pipe_config->uapi.state; + struct intel_crtc *other_crtc; + struct intel_crtc_state *other_crtc_state; + + drm_dbg_kms(&dev_priv->drm, + "checking fdi config on pipe %c, lanes %i\n", + pipe_name(pipe), pipe_config->fdi_lanes); + if (pipe_config->fdi_lanes > 4) { + drm_dbg_kms(&dev_priv->drm, + "invalid fdi lane config on pipe %c: %i lanes\n", + pipe_name(pipe), pipe_config->fdi_lanes); + return -EINVAL; + } + + if (IS_HASWELL(dev_priv) || IS_BROADWELL(dev_priv)) { + if (pipe_config->fdi_lanes > 2) { + drm_dbg_kms(&dev_priv->drm, + "only 2 lanes on haswell, required: %i lanes\n", + pipe_config->fdi_lanes); + return -EINVAL; + } else { + return 0; + } + } + + if (INTEL_NUM_PIPES(dev_priv) == 2) + return 0; + + /* Ivybridge 3 pipe is really complicated */ + switch (pipe) { + case PIPE_A: + return 0; + case PIPE_B: + if (pipe_config->fdi_lanes <= 2) + return 0; + + other_crtc = intel_get_crtc_for_pipe(dev_priv, PIPE_C); + other_crtc_state = + intel_atomic_get_crtc_state(state, other_crtc); + if (IS_ERR(other_crtc_state)) + return PTR_ERR(other_crtc_state); + + if (pipe_required_fdi_lanes(other_crtc_state) > 0) { + drm_dbg_kms(&dev_priv->drm, + "invalid shared fdi lane config on pipe %c: %i lanes\n", + pipe_name(pipe), pipe_config->fdi_lanes); + return -EINVAL; + } + return 0; + case PIPE_C: + if (pipe_config->fdi_lanes > 2) { + drm_dbg_kms(&dev_priv->drm, + "only 2 lanes on pipe %c: required %i lanes\n", + pipe_name(pipe), pipe_config->fdi_lanes); + return -EINVAL; + } + + other_crtc = intel_get_crtc_for_pipe(dev_priv, PIPE_B); + other_crtc_state = + intel_atomic_get_crtc_state(state, other_crtc); + if (IS_ERR(other_crtc_state)) + return PTR_ERR(other_crtc_state); + + if (pipe_required_fdi_lanes(other_crtc_state) > 2) { + drm_dbg_kms(&dev_priv->drm, + "fdi link B uses too many lanes to enable link C\n"); + return -EINVAL; + } + return 0; + default: + BUG(); + } +} + +int ilk_fdi_compute_config(struct intel_crtc *intel_crtc, + struct intel_crtc_state *pipe_config) +{ + struct drm_device *dev = intel_crtc->base.dev; + struct drm_i915_private *i915 = to_i915(dev); + const struct drm_display_mode *adjusted_mode = &pipe_config->hw.adjusted_mode; + int lane, link_bw, fdi_dotclock, ret; + bool needs_recompute = false; + +retry: + /* FDI is a binary signal running at ~2.7GHz, encoding + * each output octet as 10 bits. The actual frequency + * is stored as a divider into a 100MHz clock, and the + * mode pixel clock is stored in units of 1KHz. + * Hence the bw of each lane in terms of the mode signal + * is: + */ + link_bw = intel_fdi_link_freq(i915, pipe_config); + + fdi_dotclock = adjusted_mode->crtc_clock; + + lane = ilk_get_lanes_required(fdi_dotclock, link_bw, + pipe_config->pipe_bpp); + + pipe_config->fdi_lanes = lane; + + intel_link_compute_m_n(pipe_config->pipe_bpp, lane, fdi_dotclock, + link_bw, &pipe_config->fdi_m_n, false, false); + + ret = ilk_check_fdi_lanes(dev, intel_crtc->pipe, pipe_config); + if (ret == -EDEADLK) + return ret; + + if (ret == -EINVAL && pipe_config->pipe_bpp > 6*3) { + pipe_config->pipe_bpp -= 2*3; + drm_dbg_kms(&i915->drm, + "fdi link bw constraint, reducing pipe bpp to %i\n", + pipe_config->pipe_bpp); + needs_recompute = true; + pipe_config->bw_constrained = true; + + goto retry; + } + + if (needs_recompute) + return I915_DISPLAY_CONFIG_RETRY; + + return ret; +} + +void intel_fdi_normal_train(struct intel_crtc *crtc) +{ + struct drm_device *dev = crtc->base.dev; + struct drm_i915_private *dev_priv = to_i915(dev); + enum pipe pipe = crtc->pipe; + i915_reg_t reg; + u32 temp; + + /* enable normal train */ + reg = FDI_TX_CTL(pipe); + temp = intel_de_read(dev_priv, reg); + if (IS_IVYBRIDGE(dev_priv)) { + temp &= ~FDI_LINK_TRAIN_NONE_IVB; + temp |= FDI_LINK_TRAIN_NONE_IVB | FDI_TX_ENHANCE_FRAME_ENABLE; + } else { + temp &= ~FDI_LINK_TRAIN_NONE; + temp |= FDI_LINK_TRAIN_NONE | FDI_TX_ENHANCE_FRAME_ENABLE; + } + intel_de_write(dev_priv, reg, temp); + + reg = FDI_RX_CTL(pipe); + temp = intel_de_read(dev_priv, reg); + if (HAS_PCH_CPT(dev_priv)) { + temp &= ~FDI_LINK_TRAIN_PATTERN_MASK_CPT; + temp |= FDI_LINK_TRAIN_NORMAL_CPT; + } else { + temp &= ~FDI_LINK_TRAIN_NONE; + temp |= FDI_LINK_TRAIN_NONE; + } + intel_de_write(dev_priv, reg, temp | FDI_RX_ENHANCE_FRAME_ENABLE); + + /* wait one idle pattern time */ + intel_de_posting_read(dev_priv, reg); + udelay(1000); + + /* IVB wants error correction enabled */ + if (IS_IVYBRIDGE(dev_priv)) + intel_de_write(dev_priv, reg, + intel_de_read(dev_priv, reg) | FDI_FS_ERRC_ENABLE | FDI_FE_ERRC_ENABLE); +} + +/* The FDI link training functions for ILK/Ibexpeak. */ +static void ilk_fdi_link_train(struct intel_crtc *crtc, + const struct intel_crtc_state *crtc_state) +{ + struct drm_device *dev = crtc->base.dev; + struct drm_i915_private *dev_priv = to_i915(dev); + enum pipe pipe = crtc->pipe; + i915_reg_t reg; + u32 temp, tries; + + /* FDI needs bits from pipe first */ + assert_pipe_enabled(dev_priv, crtc_state->cpu_transcoder); + + /* Train 1: umask FDI RX Interrupt symbol_lock and bit_lock bit + for train result */ + reg = FDI_RX_IMR(pipe); + temp = intel_de_read(dev_priv, reg); + temp &= ~FDI_RX_SYMBOL_LOCK; + temp &= ~FDI_RX_BIT_LOCK; + intel_de_write(dev_priv, reg, temp); + intel_de_read(dev_priv, reg); + udelay(150); + + /* enable CPU FDI TX and PCH FDI RX */ + reg = FDI_TX_CTL(pipe); + temp = intel_de_read(dev_priv, reg); + temp &= ~FDI_DP_PORT_WIDTH_MASK; + temp |= FDI_DP_PORT_WIDTH(crtc_state->fdi_lanes); + temp &= ~FDI_LINK_TRAIN_NONE; + temp |= FDI_LINK_TRAIN_PATTERN_1; + intel_de_write(dev_priv, reg, temp | FDI_TX_ENABLE); + + reg = FDI_RX_CTL(pipe); + temp = intel_de_read(dev_priv, reg); + temp &= ~FDI_LINK_TRAIN_NONE; + temp |= FDI_LINK_TRAIN_PATTERN_1; + intel_de_write(dev_priv, reg, temp | FDI_RX_ENABLE); + + intel_de_posting_read(dev_priv, reg); + udelay(150); + + /* Ironlake workaround, enable clock pointer after FDI enable*/ + intel_de_write(dev_priv, FDI_RX_CHICKEN(pipe), + FDI_RX_PHASE_SYNC_POINTER_OVR); + intel_de_write(dev_priv, FDI_RX_CHICKEN(pipe), + FDI_RX_PHASE_SYNC_POINTER_OVR | FDI_RX_PHASE_SYNC_POINTER_EN); + + reg = FDI_RX_IIR(pipe); + for (tries = 0; tries < 5; tries++) { + temp = intel_de_read(dev_priv, reg); + drm_dbg_kms(&dev_priv->drm, "FDI_RX_IIR 0x%x\n", temp); + + if ((temp & FDI_RX_BIT_LOCK)) { + drm_dbg_kms(&dev_priv->drm, "FDI train 1 done.\n"); + intel_de_write(dev_priv, reg, temp | FDI_RX_BIT_LOCK); + break; + } + } + if (tries == 5) + drm_err(&dev_priv->drm, "FDI train 1 fail!\n"); + + /* Train 2 */ + reg = FDI_TX_CTL(pipe); + temp = intel_de_read(dev_priv, reg); + temp &= ~FDI_LINK_TRAIN_NONE; + temp |= FDI_LINK_TRAIN_PATTERN_2; + intel_de_write(dev_priv, reg, temp); + + reg = FDI_RX_CTL(pipe); + temp = intel_de_read(dev_priv, reg); + temp &= ~FDI_LINK_TRAIN_NONE; + temp |= FDI_LINK_TRAIN_PATTERN_2; + intel_de_write(dev_priv, reg, temp); + + intel_de_posting_read(dev_priv, reg); + udelay(150); + + reg = FDI_RX_IIR(pipe); + for (tries = 0; tries < 5; tries++) { + temp = intel_de_read(dev_priv, reg); + drm_dbg_kms(&dev_priv->drm, "FDI_RX_IIR 0x%x\n", temp); + + if (temp & FDI_RX_SYMBOL_LOCK) { + intel_de_write(dev_priv, reg, + temp | FDI_RX_SYMBOL_LOCK); + drm_dbg_kms(&dev_priv->drm, "FDI train 2 done.\n"); + break; + } + } + if (tries == 5) + drm_err(&dev_priv->drm, "FDI train 2 fail!\n"); + + drm_dbg_kms(&dev_priv->drm, "FDI train done\n"); + +} + +static const int snb_b_fdi_train_param[] = { + FDI_LINK_TRAIN_400MV_0DB_SNB_B, + FDI_LINK_TRAIN_400MV_6DB_SNB_B, + FDI_LINK_TRAIN_600MV_3_5DB_SNB_B, + FDI_LINK_TRAIN_800MV_0DB_SNB_B, +}; + +/* The FDI link training functions for SNB/Cougarpoint. */ +static void gen6_fdi_link_train(struct intel_crtc *crtc, + const struct intel_crtc_state *crtc_state) +{ + struct drm_device *dev = crtc->base.dev; + struct drm_i915_private *dev_priv = to_i915(dev); + enum pipe pipe = crtc->pipe; + i915_reg_t reg; + u32 temp, i, retry; + + /* Train 1: umask FDI RX Interrupt symbol_lock and bit_lock bit + for train result */ + reg = FDI_RX_IMR(pipe); + temp = intel_de_read(dev_priv, reg); + temp &= ~FDI_RX_SYMBOL_LOCK; + temp &= ~FDI_RX_BIT_LOCK; + intel_de_write(dev_priv, reg, temp); + + intel_de_posting_read(dev_priv, reg); + udelay(150); + + /* enable CPU FDI TX and PCH FDI RX */ + reg = FDI_TX_CTL(pipe); + temp = intel_de_read(dev_priv, reg); + temp &= ~FDI_DP_PORT_WIDTH_MASK; + temp |= FDI_DP_PORT_WIDTH(crtc_state->fdi_lanes); + temp &= ~FDI_LINK_TRAIN_NONE; + temp |= FDI_LINK_TRAIN_PATTERN_1; + temp &= ~FDI_LINK_TRAIN_VOL_EMP_MASK; + /* SNB-B */ + temp |= FDI_LINK_TRAIN_400MV_0DB_SNB_B; + intel_de_write(dev_priv, reg, temp | FDI_TX_ENABLE); + + intel_de_write(dev_priv, FDI_RX_MISC(pipe), + FDI_RX_TP1_TO_TP2_48 | FDI_RX_FDI_DELAY_90); + + reg = FDI_RX_CTL(pipe); + temp = intel_de_read(dev_priv, reg); + if (HAS_PCH_CPT(dev_priv)) { + temp &= ~FDI_LINK_TRAIN_PATTERN_MASK_CPT; + temp |= FDI_LINK_TRAIN_PATTERN_1_CPT; + } else { + temp &= ~FDI_LINK_TRAIN_NONE; + temp |= FDI_LINK_TRAIN_PATTERN_1; + } + intel_de_write(dev_priv, reg, temp | FDI_RX_ENABLE); + + intel_de_posting_read(dev_priv, reg); + udelay(150); + + for (i = 0; i < 4; i++) { + reg = FDI_TX_CTL(pipe); + temp = intel_de_read(dev_priv, reg); + temp &= ~FDI_LINK_TRAIN_VOL_EMP_MASK; + temp |= snb_b_fdi_train_param[i]; + intel_de_write(dev_priv, reg, temp); + + intel_de_posting_read(dev_priv, reg); + udelay(500); + + for (retry = 0; retry < 5; retry++) { + reg = FDI_RX_IIR(pipe); + temp = intel_de_read(dev_priv, reg); + drm_dbg_kms(&dev_priv->drm, "FDI_RX_IIR 0x%x\n", temp); + if (temp & FDI_RX_BIT_LOCK) { + intel_de_write(dev_priv, reg, + temp | FDI_RX_BIT_LOCK); + drm_dbg_kms(&dev_priv->drm, + "FDI train 1 done.\n"); + break; + } + udelay(50); + } + if (retry < 5) + break; + } + if (i == 4) + drm_err(&dev_priv->drm, "FDI train 1 fail!\n"); + + /* Train 2 */ + reg = FDI_TX_CTL(pipe); + temp = intel_de_read(dev_priv, reg); + temp &= ~FDI_LINK_TRAIN_NONE; + temp |= FDI_LINK_TRAIN_PATTERN_2; + if (IS_GEN(dev_priv, 6)) { + temp &= ~FDI_LINK_TRAIN_VOL_EMP_MASK; + /* SNB-B */ + temp |= FDI_LINK_TRAIN_400MV_0DB_SNB_B; + } + intel_de_write(dev_priv, reg, temp); + + reg = FDI_RX_CTL(pipe); + temp = intel_de_read(dev_priv, reg); + if (HAS_PCH_CPT(dev_priv)) { + temp &= ~FDI_LINK_TRAIN_PATTERN_MASK_CPT; + temp |= FDI_LINK_TRAIN_PATTERN_2_CPT; + } else { + temp &= ~FDI_LINK_TRAIN_NONE; + temp |= FDI_LINK_TRAIN_PATTERN_2; + } + intel_de_write(dev_priv, reg, temp); + + intel_de_posting_read(dev_priv, reg); + udelay(150); + + for (i = 0; i < 4; i++) { + reg = FDI_TX_CTL(pipe); + temp = intel_de_read(dev_priv, reg); + temp &= ~FDI_LINK_TRAIN_VOL_EMP_MASK; + temp |= snb_b_fdi_train_param[i]; + intel_de_write(dev_priv, reg, temp); + + intel_de_posting_read(dev_priv, reg); + udelay(500); + + for (retry = 0; retry < 5; retry++) { + reg = FDI_RX_IIR(pipe); + temp = intel_de_read(dev_priv, reg); + drm_dbg_kms(&dev_priv->drm, "FDI_RX_IIR 0x%x\n", temp); + if (temp & FDI_RX_SYMBOL_LOCK) { + intel_de_write(dev_priv, reg, + temp | FDI_RX_SYMBOL_LOCK); + drm_dbg_kms(&dev_priv->drm, + "FDI train 2 done.\n"); + break; + } + udelay(50); + } + if (retry < 5) + break; + } + if (i == 4) + drm_err(&dev_priv->drm, "FDI train 2 fail!\n"); + + drm_dbg_kms(&dev_priv->drm, "FDI train done.\n"); +} + +/* Manual link training for Ivy Bridge A0 parts */ +static void ivb_manual_fdi_link_train(struct intel_crtc *crtc, + const struct intel_crtc_state *crtc_state) +{ + struct drm_device *dev = crtc->base.dev; + struct drm_i915_private *dev_priv = to_i915(dev); + enum pipe pipe = crtc->pipe; + i915_reg_t reg; + u32 temp, i, j; + + /* Train 1: umask FDI RX Interrupt symbol_lock and bit_lock bit + for train result */ + reg = FDI_RX_IMR(pipe); + temp = intel_de_read(dev_priv, reg); + temp &= ~FDI_RX_SYMBOL_LOCK; + temp &= ~FDI_RX_BIT_LOCK; + intel_de_write(dev_priv, reg, temp); + + intel_de_posting_read(dev_priv, reg); + udelay(150); + + drm_dbg_kms(&dev_priv->drm, "FDI_RX_IIR before link train 0x%x\n", + intel_de_read(dev_priv, FDI_RX_IIR(pipe))); + + /* Try each vswing and preemphasis setting twice before moving on */ + for (j = 0; j < ARRAY_SIZE(snb_b_fdi_train_param) * 2; j++) { + /* disable first in case we need to retry */ + reg = FDI_TX_CTL(pipe); + temp = intel_de_read(dev_priv, reg); + temp &= ~(FDI_LINK_TRAIN_AUTO | FDI_LINK_TRAIN_NONE_IVB); + temp &= ~FDI_TX_ENABLE; + intel_de_write(dev_priv, reg, temp); + + reg = FDI_RX_CTL(pipe); + temp = intel_de_read(dev_priv, reg); + temp &= ~FDI_LINK_TRAIN_AUTO; + temp &= ~FDI_LINK_TRAIN_PATTERN_MASK_CPT; + temp &= ~FDI_RX_ENABLE; + intel_de_write(dev_priv, reg, temp); + + /* enable CPU FDI TX and PCH FDI RX */ + reg = FDI_TX_CTL(pipe); + temp = intel_de_read(dev_priv, reg); + temp &= ~FDI_DP_PORT_WIDTH_MASK; + temp |= FDI_DP_PORT_WIDTH(crtc_state->fdi_lanes); + temp |= FDI_LINK_TRAIN_PATTERN_1_IVB; + temp &= ~FDI_LINK_TRAIN_VOL_EMP_MASK; + temp |= snb_b_fdi_train_param[j/2]; + temp |= FDI_COMPOSITE_SYNC; + intel_de_write(dev_priv, reg, temp | FDI_TX_ENABLE); + + intel_de_write(dev_priv, FDI_RX_MISC(pipe), + FDI_RX_TP1_TO_TP2_48 | FDI_RX_FDI_DELAY_90); + + reg = FDI_RX_CTL(pipe); + temp = intel_de_read(dev_priv, reg); + temp |= FDI_LINK_TRAIN_PATTERN_1_CPT; + temp |= FDI_COMPOSITE_SYNC; + intel_de_write(dev_priv, reg, temp | FDI_RX_ENABLE); + + intel_de_posting_read(dev_priv, reg); + udelay(1); /* should be 0.5us */ + + for (i = 0; i < 4; i++) { + reg = FDI_RX_IIR(pipe); + temp = intel_de_read(dev_priv, reg); + drm_dbg_kms(&dev_priv->drm, "FDI_RX_IIR 0x%x\n", temp); + + if (temp & FDI_RX_BIT_LOCK || + (intel_de_read(dev_priv, reg) & FDI_RX_BIT_LOCK)) { + intel_de_write(dev_priv, reg, + temp | FDI_RX_BIT_LOCK); + drm_dbg_kms(&dev_priv->drm, + "FDI train 1 done, level %i.\n", + i); + break; + } + udelay(1); /* should be 0.5us */ + } + if (i == 4) { + drm_dbg_kms(&dev_priv->drm, + "FDI train 1 fail on vswing %d\n", j / 2); + continue; + } + + /* Train 2 */ + reg = FDI_TX_CTL(pipe); + temp = intel_de_read(dev_priv, reg); + temp &= ~FDI_LINK_TRAIN_NONE_IVB; + temp |= FDI_LINK_TRAIN_PATTERN_2_IVB; + intel_de_write(dev_priv, reg, temp); + + reg = FDI_RX_CTL(pipe); + temp = intel_de_read(dev_priv, reg); + temp &= ~FDI_LINK_TRAIN_PATTERN_MASK_CPT; + temp |= FDI_LINK_TRAIN_PATTERN_2_CPT; + intel_de_write(dev_priv, reg, temp); + + intel_de_posting_read(dev_priv, reg); + udelay(2); /* should be 1.5us */ + + for (i = 0; i < 4; i++) { + reg = FDI_RX_IIR(pipe); + temp = intel_de_read(dev_priv, reg); + drm_dbg_kms(&dev_priv->drm, "FDI_RX_IIR 0x%x\n", temp); + + if (temp & FDI_RX_SYMBOL_LOCK || + (intel_de_read(dev_priv, reg) & FDI_RX_SYMBOL_LOCK)) { + intel_de_write(dev_priv, reg, + temp | FDI_RX_SYMBOL_LOCK); + drm_dbg_kms(&dev_priv->drm, + "FDI train 2 done, level %i.\n", + i); + goto train_done; + } + udelay(2); /* should be 1.5us */ + } + if (i == 4) + drm_dbg_kms(&dev_priv->drm, + "FDI train 2 fail on vswing %d\n", j / 2); + } + +train_done: + drm_dbg_kms(&dev_priv->drm, "FDI train done.\n"); +} + +void ilk_fdi_pll_enable(const struct intel_crtc_state *crtc_state) +{ + struct intel_crtc *intel_crtc = to_intel_crtc(crtc_state->uapi.crtc); + struct drm_i915_private *dev_priv = to_i915(intel_crtc->base.dev); + enum pipe pipe = intel_crtc->pipe; + i915_reg_t reg; + u32 temp; + + /* enable PCH FDI RX PLL, wait warmup plus DMI latency */ + reg = FDI_RX_CTL(pipe); + temp = intel_de_read(dev_priv, reg); + temp &= ~(FDI_DP_PORT_WIDTH_MASK | (0x7 << 16)); + temp |= FDI_DP_PORT_WIDTH(crtc_state->fdi_lanes); + temp |= (intel_de_read(dev_priv, PIPECONF(pipe)) & PIPECONF_BPC_MASK) << 11; + intel_de_write(dev_priv, reg, temp | FDI_RX_PLL_ENABLE); + + intel_de_posting_read(dev_priv, reg); + udelay(200); + + /* Switch from Rawclk to PCDclk */ + temp = intel_de_read(dev_priv, reg); + intel_de_write(dev_priv, reg, temp | FDI_PCDCLK); + + intel_de_posting_read(dev_priv, reg); + udelay(200); + + /* Enable CPU FDI TX PLL, always on for Ironlake */ + reg = FDI_TX_CTL(pipe); + temp = intel_de_read(dev_priv, reg); + if ((temp & FDI_TX_PLL_ENABLE) == 0) { + intel_de_write(dev_priv, reg, temp | FDI_TX_PLL_ENABLE); + + intel_de_posting_read(dev_priv, reg); + udelay(100); + } +} + +void ilk_fdi_pll_disable(struct intel_crtc *intel_crtc) +{ + struct drm_device *dev = intel_crtc->base.dev; + struct drm_i915_private *dev_priv = to_i915(dev); + enum pipe pipe = intel_crtc->pipe; + i915_reg_t reg; + u32 temp; + + /* Switch from PCDclk to Rawclk */ + reg = FDI_RX_CTL(pipe); + temp = intel_de_read(dev_priv, reg); + intel_de_write(dev_priv, reg, temp & ~FDI_PCDCLK); + + /* Disable CPU FDI TX PLL */ + reg = FDI_TX_CTL(pipe); + temp = intel_de_read(dev_priv, reg); + intel_de_write(dev_priv, reg, temp & ~FDI_TX_PLL_ENABLE); + + intel_de_posting_read(dev_priv, reg); + udelay(100); + + reg = FDI_RX_CTL(pipe); + temp = intel_de_read(dev_priv, reg); + intel_de_write(dev_priv, reg, temp & ~FDI_RX_PLL_ENABLE); + + /* Wait for the clocks to turn off. */ + intel_de_posting_read(dev_priv, reg); + udelay(100); +} + +void ilk_fdi_disable(struct intel_crtc *crtc) +{ + struct drm_i915_private *dev_priv = to_i915(crtc->base.dev); + enum pipe pipe = crtc->pipe; + i915_reg_t reg; + u32 temp; + + /* disable CPU FDI tx and PCH FDI rx */ + reg = FDI_TX_CTL(pipe); + temp = intel_de_read(dev_priv, reg); + intel_de_write(dev_priv, reg, temp & ~FDI_TX_ENABLE); + intel_de_posting_read(dev_priv, reg); + + reg = FDI_RX_CTL(pipe); + temp = intel_de_read(dev_priv, reg); + temp &= ~(0x7 << 16); + temp |= (intel_de_read(dev_priv, PIPECONF(pipe)) & PIPECONF_BPC_MASK) << 11; + intel_de_write(dev_priv, reg, temp & ~FDI_RX_ENABLE); + + intel_de_posting_read(dev_priv, reg); + udelay(100); + + /* Ironlake workaround, disable clock pointer after downing FDI */ + if (HAS_PCH_IBX(dev_priv)) + intel_de_write(dev_priv, FDI_RX_CHICKEN(pipe), + FDI_RX_PHASE_SYNC_POINTER_OVR); + + /* still set train pattern 1 */ + reg = FDI_TX_CTL(pipe); + temp = intel_de_read(dev_priv, reg); + temp &= ~FDI_LINK_TRAIN_NONE; + temp |= FDI_LINK_TRAIN_PATTERN_1; + intel_de_write(dev_priv, reg, temp); + + reg = FDI_RX_CTL(pipe); + temp = intel_de_read(dev_priv, reg); + if (HAS_PCH_CPT(dev_priv)) { + temp &= ~FDI_LINK_TRAIN_PATTERN_MASK_CPT; + temp |= FDI_LINK_TRAIN_PATTERN_1_CPT; + } else { + temp &= ~FDI_LINK_TRAIN_NONE; + temp |= FDI_LINK_TRAIN_PATTERN_1; + } + /* BPC in FDI rx is consistent with that in PIPECONF */ + temp &= ~(0x07 << 16); + temp |= (intel_de_read(dev_priv, PIPECONF(pipe)) & PIPECONF_BPC_MASK) << 11; + intel_de_write(dev_priv, reg, temp); + + intel_de_posting_read(dev_priv, reg); + udelay(100); +} + +void +intel_init_fdi_hook(struct drm_i915_private *dev_priv) +{ + if (IS_GEN(dev_priv, 5)) { + dev_priv->display.fdi_link_train = ilk_fdi_link_train; + } else if (IS_GEN(dev_priv, 6)) { + dev_priv->display.fdi_link_train = gen6_fdi_link_train; + } else if (IS_IVYBRIDGE(dev_priv)) { + /* FIXME: detect B0+ stepping and use auto training */ + dev_priv->display.fdi_link_train = ivb_manual_fdi_link_train; + } +}