From: Shobhit Kumar <shobhit.kumar at intel.com> In Haswell the DDIs have moved on to CPU. The AUX control is on LPT. Had to split the code in such a way so as to be able to do source training using DDI_BUF_CTL and DP_TP_CTL and sink side using AUX on LPT. Also added new routine to get the Volatge and pre-emphashis values. This is now done for DDI_BUF_CTL which now has common bits indicating a combination of voltage and pre-emphasis values Signed-off-by: Shobhit Kumar <shobhit.kumar at intel.com> Signed-off-by: Eugeni Dodonov <eugeni.dodonov at intel.com> Reviewed-by: Eugeni Dodonov <eugeni.dodonov at intel.com> --- drivers/gpu/drm/i915/i915_reg.h | 4 ++ drivers/gpu/drm/i915/intel_ddi.c | 23 +++++++++ drivers/gpu/drm/i915/intel_dp.c | 100 ++++++++++++++++++++++++++++++++++++--- drivers/gpu/drm/i915/intel_drv.h | 3 ++ 4 files changed, 123 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index 51398a8..5b0c5f6 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -4308,7 +4308,10 @@ #define DP_TP_CTL_LINK_TRAIN_MASK (7<<8) #define DP_TP_CTL_LINK_TRAIN_PAT1 (0<<8) #define DP_TP_CTL_LINK_TRAIN_PAT2 (1<<8) +#define DP_TP_CTL_LINK_TRAIN_IDLE (2<<8) #define DP_TP_CTL_LINK_TRAIN_NORMAL (3<<8) +#define DP_TP_CTL_LINK_TRAIN_PAT3 (4<<8) +#define DP_TP_CTL_SCRAMBLE_DISABLE (1<<7) /* DisplayPort Transport Status */ #define DP_TP_STATUS_A 0x64044 @@ -4317,6 +4320,7 @@ DP_TP_STATUS_A, \ DP_TP_STATUS_B) #define DP_TP_STATUS_AUTOTRAIN_DONE (1<<12) +#define DP_TP_STATUS_IDLE_DONE (1<<25) /* DDI Buffer Control */ #define DDI_BUF_CTL_A 0x64000 diff --git a/drivers/gpu/drm/i915/intel_ddi.c b/drivers/gpu/drm/i915/intel_ddi.c index 91ed708..f08ce6c 100644 --- a/drivers/gpu/drm/i915/intel_ddi.c +++ b/drivers/gpu/drm/i915/intel_ddi.c @@ -907,3 +907,26 @@ void intel_ddi_dpms(struct drm_encoder *encoder, int mode) temp); } } + +void intel_ddi_dp_set_link_train_src(struct intel_dp *intel_dp, u32 dp_reg_value) +{ + struct drm_device *dev = intel_dp->base.base.dev; + struct drm_i915_private *dev_priv = dev->dev_private; + int port = intel_dp->ddi_port; + + /* Configure DP_TP_CTL with training pattern + * TBD: Only SST now; Enable MST later + * */ + + I915_WRITE(DP_TP_CTL(port), + intel_dp->TP | DP_TP_CTL_SCRAMBLE_DISABLE | + DP_TP_CTL_MODE_SST | + DP_TP_CTL_ENABLE); + + /* Configure and enable DDI_BUF_CTL for DDI with next voltage */ + I915_WRITE(DDI_BUF_CTL(port), + DDI_BUF_CTL_ENABLE | + dp_reg_value); + + udelay(600); +} diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index f4c6e98..38a1d3b 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -1437,6 +1437,18 @@ intel_dp_pre_emphasis_max(struct intel_dp *intel_dp, uint8_t voltage_swing) default: return DP_TRAIN_PRE_EMPHASIS_0; } + } else if (IS_HASWELL(dev)) { + switch (voltage_swing & DP_TRAIN_VOLTAGE_SWING_MASK) { + case DP_TRAIN_VOLTAGE_SWING_400: + return DP_TRAIN_PRE_EMPHASIS_9_5; + case DP_TRAIN_VOLTAGE_SWING_600: + return DP_TRAIN_PRE_EMPHASIS_6; + case DP_TRAIN_VOLTAGE_SWING_800: + return DP_TRAIN_PRE_EMPHASIS_3_5; + case DP_TRAIN_VOLTAGE_SWING_1200: + default: + return DP_TRAIN_PRE_EMPHASIS_0; + } } else { switch (voltage_swing & DP_TRAIN_VOLTAGE_SWING_MASK) { case DP_TRAIN_VOLTAGE_SWING_400: @@ -1581,6 +1593,40 @@ intel_gen7_edp_signal_levels(uint8_t train_set) } } +/* Gen7.5's (HSW) DP voltage swing and pre-emphasis control */ +static uint32_t +intel_dp_signal_levels_hsw(uint8_t train_set) +{ + int signal_levels = train_set & (DP_TRAIN_VOLTAGE_SWING_MASK | + DP_TRAIN_PRE_EMPHASIS_MASK); + switch (signal_levels) { + case DP_TRAIN_VOLTAGE_SWING_400 | DP_TRAIN_PRE_EMPHASIS_0: + return DDI_BUF_EMP_400MV_0DB_HSW; + case DP_TRAIN_VOLTAGE_SWING_400 | DP_TRAIN_PRE_EMPHASIS_3_5: + return DDI_BUF_EMP_400MV_3_5DB_HSW; + case DP_TRAIN_VOLTAGE_SWING_400 | DP_TRAIN_PRE_EMPHASIS_6: + return DDI_BUF_EMP_400MV_6DB_HSW; + case DP_TRAIN_VOLTAGE_SWING_400 | DP_TRAIN_PRE_EMPHASIS_9_5: + return DDI_BUF_EMP_400MV_9_5DB_HSW; + + case DP_TRAIN_VOLTAGE_SWING_600 | DP_TRAIN_PRE_EMPHASIS_0: + return DDI_BUF_EMP_600MV_0DB_HSW; + case DP_TRAIN_VOLTAGE_SWING_600 | DP_TRAIN_PRE_EMPHASIS_3_5: + return DDI_BUF_EMP_600MV_3_5DB_HSW; + case DP_TRAIN_VOLTAGE_SWING_600 | DP_TRAIN_PRE_EMPHASIS_6: + return DDI_BUF_EMP_600MV_6DB_HSW; + + case DP_TRAIN_VOLTAGE_SWING_800 | DP_TRAIN_PRE_EMPHASIS_0: + return DDI_BUF_EMP_800MV_0DB_HSW; + case DP_TRAIN_VOLTAGE_SWING_800 | DP_TRAIN_PRE_EMPHASIS_3_5: + return DDI_BUF_EMP_800MV_3_5DB_HSW; + default: + DRM_DEBUG_KMS("Unsupported voltage swing/pre-emphasis level:" + "0x%x\n", signal_levels); + return DDI_BUF_EMP_400MV_0DB_HSW; + } +} + static uint8_t intel_get_lane_status(uint8_t link_status[DP_LINK_STATUS_SIZE], int lane) @@ -1638,8 +1684,12 @@ intel_dp_set_link_train(struct intel_dp *intel_dp, struct drm_i915_private *dev_priv = dev->dev_private; int ret; - I915_WRITE(intel_dp->output_reg, dp_reg_value); - POSTING_READ(intel_dp->output_reg); + if (IS_HASWELL(dev)) + intel_ddi_dp_set_link_train_src(intel_dp, dp_reg_value); + else { + I915_WRITE(intel_dp->output_reg, dp_reg_value); + POSTING_READ(intel_dp->output_reg); + } intel_dp_aux_native_write_1(intel_dp, DP_TRAINING_PATTERN_SET, @@ -1674,7 +1724,7 @@ intel_dp_start_link_train(struct intel_dp *intel_dp) * will happen below in intel_dp_set_link_train. Otherwise, enable * the port and wait for it to become active. */ - if (!HAS_PCH_CPT(dev)) { + if (!HAS_PCH_CPT(dev) && !IS_HASWELL(dev)) { I915_WRITE(intel_dp->output_reg, intel_dp->DP); POSTING_READ(intel_dp->output_reg); intel_wait_for_vblank(dev, intel_crtc->pipe); @@ -1689,8 +1739,9 @@ intel_dp_start_link_train(struct intel_dp *intel_dp) if (HAS_PCH_CPT(dev) && (IS_GEN7(dev) || !is_cpu_edp(intel_dp))) DP &= ~DP_LINK_TRAIN_MASK_CPT; - else + else if (!IS_HASWELL(dev)) DP &= ~DP_LINK_TRAIN_MASK; + memset(intel_dp->train_set, 0, 4); voltage = 0xff; voltage_tries = 0; @@ -1708,6 +1759,10 @@ intel_dp_start_link_train(struct intel_dp *intel_dp) } else if (IS_GEN6(dev) && is_cpu_edp(intel_dp)) { signal_levels = intel_gen6_edp_signal_levels(intel_dp->train_set[0]); DP = (DP & ~EDP_LINK_TRAIN_VOL_EMP_MASK_SNB) | signal_levels; + } else if (IS_HASWELL(dev)) { + signal_levels = intel_dp_signal_levels_hsw(intel_dp->train_set[0]); + DRM_DEBUG_KMS("training pattern 1 signal levels hsw %08x\n", signal_levels); + DP = (DP & ~DDI_BUF_EMP_MASK) | signal_levels; } else { signal_levels = intel_dp_signal_levels(intel_dp->train_set[0]); DRM_DEBUG_KMS("training pattern 1 signal levels %08x\n", signal_levels); @@ -1716,6 +1771,10 @@ intel_dp_start_link_train(struct intel_dp *intel_dp) if (HAS_PCH_CPT(dev) && (IS_GEN7(dev) || !is_cpu_edp(intel_dp))) reg = DP | DP_LINK_TRAIN_PAT_1_CPT; + else if (IS_HASWELL(dev)) { + reg = DP; + intel_dp->TP = (intel_dp->TP & ~DP_TP_CTL_LINK_TRAIN_MASK) | DP_TP_CTL_LINK_TRAIN_PAT1; + } else reg = DP | DP_LINK_TRAIN_PAT_1; @@ -1779,6 +1838,7 @@ intel_dp_complete_link_train(struct intel_dp *intel_dp) int tries, cr_tries; u32 reg; uint32_t DP = intel_dp->DP; + uint32_t TP = intel_dp->TP; /* channel equalization */ tries = 0; @@ -1801,6 +1861,9 @@ intel_dp_complete_link_train(struct intel_dp *intel_dp) } else if (IS_GEN6(dev) && is_cpu_edp(intel_dp)) { signal_levels = intel_gen6_edp_signal_levels(intel_dp->train_set[0]); DP = (DP & ~EDP_LINK_TRAIN_VOL_EMP_MASK_SNB) | signal_levels; + } else if (IS_HASWELL(dev)) { + signal_levels = intel_dp_signal_levels_hsw(intel_dp->train_set[0]); + DP = (DP & ~DDI_BUF_EMP_MASK) | signal_levels; } else { signal_levels = intel_dp_signal_levels(intel_dp->train_set[0]); DP = (DP & ~(DP_VOLTAGE_MASK|DP_PRE_EMPHASIS_MASK)) | signal_levels; @@ -1808,6 +1871,10 @@ intel_dp_complete_link_train(struct intel_dp *intel_dp) if (HAS_PCH_CPT(dev) && (IS_GEN7(dev) || !is_cpu_edp(intel_dp))) reg = DP | DP_LINK_TRAIN_PAT_2_CPT; + else if (IS_HASWELL(dev)) { + reg = DP; + intel_dp->TP = (intel_dp->TP & ~DP_TP_CTL_LINK_TRAIN_MASK) | DP_TP_CTL_LINK_TRAIN_PAT2; + } else reg = DP | DP_LINK_TRAIN_PAT_2; @@ -1847,15 +1914,34 @@ intel_dp_complete_link_train(struct intel_dp *intel_dp) ++tries; } + if (channel_eq) + DRM_DEBUG_KMS("Channel EQ done. DP Training successfull\n"); + if (HAS_PCH_CPT(dev) && (IS_GEN7(dev) || !is_cpu_edp(intel_dp))) reg = DP | DP_LINK_TRAIN_OFF_CPT; + else if (IS_HASWELL(dev)) { + TP &= ~DP_TP_CTL_LINK_TRAIN_MASK; + reg = TP | DP_TP_CTL_LINK_TRAIN_IDLE; + I915_WRITE(DP_TP_CTL(intel_dp->ddi_port), reg); + if (wait_for((I915_READ(DP_TP_STATUS(intel_dp->ddi_port)) & DP_TP_STATUS_IDLE_DONE) == 0, + 800)) + DRM_ERROR("Timed out waiting for DP min idle patterns\n"); + + TP &= ~DP_TP_CTL_LINK_TRAIN_MASK; + reg = TP | DP_TP_CTL_LINK_TRAIN_NORMAL; + I915_WRITE(DP_TP_CTL(intel_dp->ddi_port), reg); + } else reg = DP | DP_LINK_TRAIN_OFF; - I915_WRITE(intel_dp->output_reg, reg); - POSTING_READ(intel_dp->output_reg); + if (!IS_HASWELL(dev)) { + I915_WRITE(intel_dp->output_reg, reg); + POSTING_READ(intel_dp->output_reg); + } + intel_dp_aux_native_write_1(intel_dp, - DP_TRAINING_PATTERN_SET, DP_TRAINING_PATTERN_DISABLE); + DP_TRAINING_PATTERN_SET, + DP_TRAINING_PATTERN_DISABLE); } static void diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 65b5b7c..5ca133d 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -543,4 +543,7 @@ intel_ddi_mode_set_dp(struct drm_encoder *encoder, struct drm_display_mode *adjusted_mode); +extern void intel_ddi_dp_set_link_train_src(struct intel_dp *intel_dp, + u32 dp_reg_value); + #endif /* __INTEL_DRV_H__ */ -- 1.7.11.1