This patch attempts at following the modeset sequence closely, retrying with different voltages if the DP_TP_STATUS reports a failed training. For training, we add a table of recommended settings for FDI, HDMI and DP connections. For FDI and DP modes, we also add the HDMI buffer translation as the last item. Those are ignored in such modes, so there is no harm in having them set. Initially, we use DDI E for FDI connectivity. This is the suggested configuration, and this seems to be what should work the best with FDI. Signed-off-by: Eugeni Dodonov <eugeni.dodonov at intel.com> --- drivers/gpu/drm/i915/intel_display.c | 170 ++++++++++++++++++++++++++++++++++ 1 file changed, 170 insertions(+) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index b1ad10a..04625d5 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -1409,6 +1409,16 @@ static void intel_enable_pipe(struct drm_i915_private *dev_priv, enum pipe pipe, /* FIXME: assert CPU port conditions for SNB+ */ } + /* On HSW, enable PIPE_DDI_FUNC_CTL for the pipe */ + if (IS_HASWELL(dev_priv->dev)) { + I915_WRITE(DDI_FUNC_CTL(pipe), + I915_READ(DDI_FUNC_CTL(pipe)) | + PIPE_DDI_SELECT_DDI_E | + PIPE_DDI_PORT_WIDTH_X2 | + PIPE_DDI_MODE_SELECT_FDI | + PIPE_DDI_FUNC_ENABLE); + } + reg = PIPECONF(pipe); val = I915_READ(reg); if (val & PIPECONF_ENABLE) @@ -2698,6 +2708,151 @@ static void gen6_fdi_link_train(struct drm_crtc *crtc) DRM_DEBUG_KMS("FDI train done.\n"); } +static const long hsw_ddi_translations_dp[] = { + 0x00FFFFFF, 0x0006000E, + 0x00D75FFF, 0x0005000A, + 0x00C30FFF, 0x00040006, + 0x80AAAFFF, 0x000B0000, + 0x00FFFFFF, 0x0005000A, + 0x00D75FFF, 0x000C0004, + 0x80C30FFF, 0x000B0000, + 0x00FFFFFF, 0x00040006, + 0x80D75FFF, 0x000B0000, + 0x00FFFFFF, 0x00040006 +}; + +static const long hsw_ddi_translations_fdi[] = { + 0x00FFFFFF, 0x0007000E, + 0x00D75FFF, 0x000F000A, + 0x00C30FFF, 0x00060006, + 0x00AAAFFF, 0x001E0000, + 0x00FFFFFF, 0x000F000A, + 0x00D75FFF, 0x00160004, + 0x00C30FFF, 0x001E0000, + 0x00FFFFFF, 0x00060006, + 0x00D75FFF, 0x001E0000, + 0x00FFFFFF, 0x00040006 +}; + +/* For HDMI, we are only interested in the last entry */ +static const long hsw_ddi_translations_hdmi[] = { + 0x0, 0x0, + 0x0, 0x0, + 0x0, 0x0, + 0x0, 0x0, + 0x0, 0x0, + 0x0, 0x0, + 0x0, 0x0, + 0x0, 0x0, + 0x0, 0x0, + 0x00FFFFFF, 0x00040006 +}; + + +static const long hsw_ddi_buf_ctl_values[] = { + DDI_BUF_EMP_400MV_0DB_HSW, + DDI_BUF_EMP_400MV_3_5DB_HSW, + DDI_BUF_EMP_400MV_6DB_HSW, + DDI_BUF_EMP_400MV_9_5DB_HSW, + DDI_BUF_EMP_600MV_0DB_HSW, + DDI_BUF_EMP_600MV_3_5DB_HSW, + DDI_BUF_EMP_600MV_6DB_HSW, + DDI_BUF_EMP_800MV_0DB_HSW, + DDI_BUF_EMP_800MV_3_5DB_HSW +}; + + +/* Link training for HSW parts */ +static void hsw_fdi_link_train(struct drm_crtc *crtc) +{ + struct drm_device *dev = crtc->dev; + struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + int pipe = intel_crtc->pipe; + u32 reg, temp, i; + + /* Prior to enabling DDI, configure buffer translation with FDI values */ + for (i=0, reg=DDI_BUF_TRANS_E; i < ARRAY_SIZE(hsw_ddi_translations_fdi); i++) { + I915_WRITE(reg, hsw_ddi_translations_fdi[i]); + reg += 4; + } + + /* Configure CPU PLL, wait for warmup */ + I915_WRITE(SPLL_CTL, + SPLL_PLL_ENABLE | + SPLL_PLL_FREQ_1350MHz | + SPLL_PLL_SCC); + + /* Use SPLL to drive the output when in FDI mode */ + I915_WRITE(PORT_CLK_SEL_E, + PORT_CLK_SEL_SPLL); + I915_WRITE(PIPE_CLK_SEL(pipe), + PIPE_CLK_SEL_DDIE); + + udelay(20); + + /* Start the training iterating through available voltages and emphasis */ + for (i=0; i < ARRAY_SIZE(hsw_ddi_buf_ctl_values); i++) { + /* Configure DP_TP_CTL with auto-training */ + I915_WRITE(DP_TP_CTL_E, + DP_TP_CTL_FDI_AUTOTRAIN | + DP_TP_CTL_ENHANCED_FRAME_ENABLE | + DP_TP_CTL_LINK_TRAIN_PAT1 | + DP_TP_CTL_ENABLE); + + /* Configure and enable DDI_BUF_CTL for DDI E with next voltage */ + temp = I915_READ(DDI_BUF_CTL_E); + temp = (temp & ~DDI_BUF_EMP_MASK); + I915_WRITE(DDI_BUF_CTL_E, + temp | + DDI_BUF_CTL_ENABLE | + DDI_PORT_WIDTH_X2 | + hsw_ddi_buf_ctl_values[i]); + + udelay(600); + + /* Enable CPU FDI Receiver with auto-training */ + I915_WRITE(FDI_RX_CTL(pipe), + I915_READ(FDI_RX_CTL(pipe)) | + FDI_LINK_TRAIN_AUTO | + FDI_RX_ENABLE | + FDI_LINK_TRAIN_PATTERN_1_CPT | + FDI_RX_ENHANCE_FRAME_ENABLE | + LPT_FDI_PORT_WIDTH_2X | + FDI_RX_PLL_ENABLE); + POSTING_READ(reg); + udelay(100); + + temp = I915_READ(DP_TP_STATUS_E); + if (temp & DP_TP_STATUS_AUTOTRAIN_DONE) { + DRM_INFO("BUF_CTL training done on %d step\n", i); + + /* Enable normal pixel sending for FDI */ + I915_WRITE(DP_TP_CTL_E, + DP_TP_CTL_FDI_AUTOTRAIN | + DP_TP_CTL_LINK_TRAIN_NORMAL | + DP_TP_CTL_ENHANCED_FRAME_ENABLE | + DP_TP_CTL_ENABLE); + + break; + } else { + DRM_ERROR("Error training BUF_CTL %d\n", i); + + /* Disable DP_TP_CTL and FDI_RX_CTL) and retry */ + I915_WRITE(DP_TP_CTL_E, + I915_READ(DP_TP_CTL_E) & + ~DP_TP_CTL_ENABLE); + I915_WRITE(FDI_RX_CTL(pipe), + I915_READ(FDI_RX_CTL(pipe)) & + ~FDI_RX_PLL_ENABLE); + continue; + } + } + + + DRM_DEBUG_KMS("FDI train done.\n"); +} + /* Manual link training for Ivy Bridge A0 parts */ static void ivb_manual_fdi_link_train(struct drm_crtc *crtc) { @@ -3019,6 +3174,10 @@ static void ironlake_pch_enable(struct drm_crtc *crtc) temp |= (TRANSC_DPLL_ENABLE | transc_sel); } I915_WRITE(PCH_DPLL_SEL, temp); + } else if (HAS_PCH_LPT(dev)) { + /* DDIE clock is recommented for FDI connections */ + I915_WRITE(PIPE_CLK_SEL(pipe), + PIPE_CLK_SEL_DDIE); } /* set transcoder timing, panel must allow it */ @@ -8975,6 +9134,17 @@ static void intel_init_display(struct drm_device *dev) } dev_priv->display.init_clock_gating = ivybridge_init_clock_gating; dev_priv->display.write_eld = ironlake_write_eld; + } else if (IS_HASWELL(dev)) { + dev_priv->display.fdi_link_train = hsw_fdi_link_train; + if (SNB_READ_WM0_LATENCY()) { + dev_priv->display.update_wm = sandybridge_update_wm; + } else { + DRM_DEBUG_KMS("Failed to read display plane latency. " + "Disable CxSR\n"); + dev_priv->display.update_wm = NULL; + } + dev_priv->display.init_clock_gating = ivybridge_init_clock_gating; + dev_priv->display.write_eld = ironlake_write_eld; } else dev_priv->display.update_wm = NULL; } else if (IS_PINEVIEW(dev)) { -- 1.7.9.2