From c19b0669925cb00dc1c7b2362bfa85128afba882 Mon Sep 17 00:00:00 2001 From: Paulo Zanoni Date: Mon, 15 Oct 2012 15:51:41 -0300 Subject: [PATCH] drm/i915: implement Haswell DP link train sequence Previous patch "drm/i915: add basic Haswell DP link train bits" implemented the basic structure to set the voltage levels and training patterns. This patch adds the higher-level bits that are part of the mode set sequence and hot plug. Signed-off-by: Paulo Zanoni Reviewed-by: Damien Lespiau Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_ddi.c | 53 ++++++++++++++++++++++++++++++++++++++-- drivers/gpu/drm/i915/intel_dp.c | 32 +++++++++++++++++++----- drivers/gpu/drm/i915/intel_drv.h | 4 +++ 3 files changed, 81 insertions(+), 8 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_ddi.c b/drivers/gpu/drm/i915/intel_ddi.c index 601ffc2..81cca482 100644 --- a/drivers/gpu/drm/i915/intel_ddi.c +++ b/drivers/gpu/drm/i915/intel_ddi.c @@ -1108,14 +1108,23 @@ void intel_ddi_disable_pipe_clock(struct intel_crtc *intel_crtc) void intel_ddi_pre_enable(struct intel_encoder *intel_encoder) { - struct drm_crtc *crtc = intel_encoder->base.crtc; - struct drm_i915_private *dev_priv = crtc->dev->dev_private; + struct drm_encoder *encoder = &intel_encoder->base; + struct drm_crtc *crtc = encoder->crtc; + struct drm_i915_private *dev_priv = encoder->dev->dev_private; struct intel_crtc *intel_crtc = to_intel_crtc(crtc); enum port port = intel_ddi_get_encoder_port(intel_encoder); WARN_ON(intel_crtc->ddi_pll_sel == PORT_CLK_SEL_NONE); I915_WRITE(PORT_CLK_SEL(port), intel_crtc->ddi_pll_sel); + + if (intel_encoder->type == INTEL_OUTPUT_DISPLAYPORT) { + struct intel_dp *intel_dp = enc_to_intel_dp(encoder); + + intel_dp_sink_dpms(intel_dp, DRM_MODE_DPMS_ON); + intel_dp_start_link_train(intel_dp); + intel_dp_complete_link_train(intel_dp); + } } static void intel_wait_ddi_buf_idle(struct drm_i915_private *dev_priv, @@ -1210,3 +1219,43 @@ void intel_ddi_pll_init(struct drm_device *dev) if (val & LCPLL_PLL_DISABLE) DRM_ERROR("LCPLL is disabled\n"); } + +void intel_ddi_prepare_link_retrain(struct drm_encoder *encoder) +{ + struct intel_dp *intel_dp = enc_to_intel_dp(encoder); + struct drm_i915_private *dev_priv = encoder->dev->dev_private; + enum port port = intel_dp->port; + bool wait; + uint32_t val; + + if (I915_READ(DP_TP_CTL(port)) & DP_TP_CTL_ENABLE) { + val = I915_READ(DDI_BUF_CTL(port)); + if (val & DDI_BUF_CTL_ENABLE) { + val &= ~DDI_BUF_CTL_ENABLE; + I915_WRITE(DDI_BUF_CTL(port), val); + wait = true; + } + + val = I915_READ(DP_TP_CTL(port)); + val &= ~(DP_TP_CTL_ENABLE | DP_TP_CTL_LINK_TRAIN_MASK); + val |= DP_TP_CTL_LINK_TRAIN_PAT1; + I915_WRITE(DP_TP_CTL(port), val); + POSTING_READ(DP_TP_CTL(port)); + + if (wait) + intel_wait_ddi_buf_idle(dev_priv, port); + } + + val = DP_TP_CTL_ENABLE | DP_TP_CTL_MODE_SST | + DP_TP_CTL_LINK_TRAIN_PAT1 | DP_TP_CTL_SCRAMBLE_DISABLE; + if (intel_dp->link_configuration[1] & DP_LANE_COUNT_ENHANCED_FRAME_EN) + val |= DP_TP_CTL_ENHANCED_FRAME_ENABLE; + I915_WRITE(DP_TP_CTL(port), val); + POSTING_READ(DP_TP_CTL(port)); + + intel_dp->DP |= DDI_BUF_CTL_ENABLE; + I915_WRITE(DDI_BUF_CTL(port), intel_dp->DP); + POSTING_READ(DDI_BUF_CTL(port)); + + udelay(600); +} diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index db6ef13..f6d8649 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -102,8 +102,6 @@ bool intel_encoder_is_pch_edp(struct drm_encoder *encoder) return is_pch_edp(intel_dp); } -static void intel_dp_start_link_train(struct intel_dp *intel_dp); -static void intel_dp_complete_link_train(struct intel_dp *intel_dp); static void intel_dp_link_down(struct intel_dp *intel_dp); void @@ -1266,7 +1264,7 @@ static void ironlake_edp_pll_off(struct intel_dp *intel_dp) } /* If the sink supports it, try to set the power state appropriately */ -static void intel_dp_sink_dpms(struct intel_dp *intel_dp, int mode) +void intel_dp_sink_dpms(struct intel_dp *intel_dp, int mode) { int ret, i; @@ -1854,16 +1852,20 @@ intel_dp_set_link_train(struct intel_dp *intel_dp, } /* Enable corresponding port and start training pattern 1 */ -static void +void intel_dp_start_link_train(struct intel_dp *intel_dp) { - struct drm_device *dev = intel_dp->base.base.dev; + struct drm_encoder *encoder = &intel_dp->base.base; + struct drm_device *dev = encoder->dev; int i; uint8_t voltage; bool clock_recovery = false; int voltage_tries, loop_tries; uint32_t DP = intel_dp->DP; + if (IS_HASWELL(dev)) + intel_ddi_prepare_link_retrain(encoder); + /* Write the link configuration data */ intel_dp_aux_native_write(intel_dp, DP_LINK_BW_SET, intel_dp->link_configuration, @@ -1949,7 +1951,7 @@ intel_dp_start_link_train(struct intel_dp *intel_dp) intel_dp->DP = DP; } -static void +void intel_dp_complete_link_train(struct intel_dp *intel_dp) { struct drm_device *dev = intel_dp->base.base.dev; @@ -2035,6 +2037,24 @@ intel_dp_link_down(struct intel_dp *intel_dp) struct drm_i915_private *dev_priv = dev->dev_private; uint32_t DP = intel_dp->DP; + /* + * DDI code has a strict mode set sequence and we should try to respect + * it, otherwise we might hang the machine in many different ways. So we + * really should be disabling the port only on a complete crtc_disable + * sequence. This function is just called under two conditions on DDI + * code: + * - Link train failed while doing crtc_enable, and on this case we + * really should respect the mode set sequence and wait for a + * crtc_disable. + * - Someone turned the monitor off and intel_dp_check_link_status + * called us. We don't need to disable the whole port on this case, so + * when someone turns the monitor on again, + * intel_ddi_prepare_link_retrain will take care of redoing the link + * train. + */ + if (IS_HASWELL(dev)) + return; + if (WARN_ON((I915_READ(intel_dp->output_reg) & DP_PORT_EN) == 0)) return; diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index d89d428..95cbd67 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -423,6 +423,9 @@ void intel_dp_set_m_n(struct drm_crtc *crtc, struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode); extern void intel_dp_init_link_config(struct intel_dp *intel_dp); +extern void intel_dp_start_link_train(struct intel_dp *intel_dp); +extern void intel_dp_complete_link_train(struct intel_dp *intel_dp); +extern void intel_dp_sink_dpms(struct intel_dp *intel_dp, int mode); extern bool intel_dpd_is_edp(struct drm_device *dev); extern void intel_edp_link_config(struct intel_encoder *, int *, int *); extern int intel_edp_target_clock(struct intel_encoder *, @@ -599,5 +602,6 @@ extern void intel_ddi_pre_enable(struct intel_encoder *intel_encoder); extern void intel_ddi_post_disable(struct intel_encoder *intel_encoder); extern void intel_ddi_put_crtc_pll(struct drm_crtc *crtc); extern void intel_ddi_set_pipe_settings(struct drm_crtc *crtc); +extern void intel_ddi_prepare_link_retrain(struct drm_encoder *encoder); #endif /* __INTEL_DRV_H__ */ -- 2.7.4