From 27968a72ae91aa1cf97f6e520afc14fe2d1f3724 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Wed, 17 Apr 2013 13:38:36 +0100 Subject: [PATCH] drm/i915: Propagate errors back from FDI link training Signed-off-by: Chris Wilson --- drivers/gpu/drm/i915/i915_drv.h | 4 +- drivers/gpu/drm/i915/intel_ddi.c | 5 +- drivers/gpu/drm/i915/intel_display.c | 310 +++++++++++++++++++++------------- drivers/gpu/drm/i915/intel_drv.h | 2 +- 4 files changed, 198 insertions(+), 123 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 86871e5..db53086 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -307,12 +307,12 @@ struct drm_i915_display_funcs { int (*crtc_mode_set)(struct drm_crtc *crtc, int x, int y, struct drm_framebuffer *old_fb); - void (*crtc_enable)(struct drm_crtc *crtc); + int (*crtc_enable)(struct drm_crtc *crtc); void (*crtc_disable)(struct drm_crtc *crtc); void (*off)(struct drm_crtc *crtc); void (*write_eld)(struct drm_connector *connector, struct drm_crtc *crtc); - void (*fdi_link_train)(struct drm_crtc *crtc); + bool (*fdi_link_train)(struct drm_crtc *crtc); void (*init_clock_gating)(struct drm_device *dev); int (*queue_flip)(struct drm_device *dev, struct drm_crtc *crtc, struct drm_framebuffer *fb, diff --git a/drivers/gpu/drm/i915/intel_ddi.c b/drivers/gpu/drm/i915/intel_ddi.c index 22524cb..e0c635f 100644 --- a/drivers/gpu/drm/i915/intel_ddi.c +++ b/drivers/gpu/drm/i915/intel_ddi.c @@ -163,7 +163,7 @@ static void intel_wait_ddi_buf_idle(struct drm_i915_private *dev_priv, * DDI A (which is used for eDP) */ -void hsw_fdi_link_train(struct drm_crtc *crtc) +bool hsw_fdi_link_train(struct drm_crtc *crtc) { struct drm_device *dev = crtc->dev; struct drm_i915_private *dev_priv = dev->dev_private; @@ -246,7 +246,7 @@ void hsw_fdi_link_train(struct drm_crtc *crtc) DP_TP_CTL_ENHANCED_FRAME_ENABLE | DP_TP_CTL_ENABLE); - return; + return true; } temp = I915_READ(DDI_BUF_CTL(PORT_E)); @@ -276,6 +276,7 @@ void hsw_fdi_link_train(struct drm_crtc *crtc) } DRM_ERROR("FDI link training failed!\n"); + return false; } /* WRPLL clock dividers */ diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 714b2a3..d972460 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -2496,7 +2496,7 @@ static void ivb_modeset_global_resources(struct drm_device *dev) } /* The FDI link training functions for ILK/Ibexpeak. */ -static void ironlake_fdi_link_train(struct drm_crtc *crtc) +static bool ironlake_fdi_link_train(struct drm_crtc *crtc) { struct drm_device *dev = crtc->dev; struct drm_i915_private *dev_priv = dev->dev_private; @@ -2553,8 +2553,10 @@ static void ironlake_fdi_link_train(struct drm_crtc *crtc) break; } } - if (tries == 5) + if (tries == 5) { DRM_ERROR("FDI train 1 fail!\n"); + goto error; + } /* Train 2 */ reg = FDI_TX_CTL(pipe); @@ -2583,10 +2585,25 @@ static void ironlake_fdi_link_train(struct drm_crtc *crtc) break; } } - if (tries == 5) + if (tries == 5) { DRM_ERROR("FDI train 2 fail!\n"); + goto error; + } DRM_DEBUG_KMS("FDI train done\n"); + return true; + +error: + reg = FDI_TX_CTL(pipe); + temp = I915_READ(reg); + temp &= ~FDI_LINK_TRAIN_NONE; + I915_WRITE(reg, temp); + + reg = FDI_RX_CTL(pipe); + temp = I915_READ(reg); + temp &= ~FDI_LINK_TRAIN_NONE; + I915_WRITE(reg, temp); + return false; } @@ -2598,7 +2615,7 @@ static const int snb_b_fdi_train_param[] = { }; /* The FDI link training functions for SNB/Cougarpoint. */ -static void gen6_fdi_link_train(struct drm_crtc *crtc) +static bool gen6_fdi_link_train(struct drm_crtc *crtc) { struct drm_device *dev = crtc->dev; struct drm_i915_private *dev_priv = dev->dev_private; @@ -2670,8 +2687,10 @@ static void gen6_fdi_link_train(struct drm_crtc *crtc) if (retry < 5) break; } - if (i == 4) + if (i == 4) { DRM_ERROR("FDI train 1 fail!\n"); + goto error; + } /* Train 2 */ reg = FDI_TX_CTL(pipe); @@ -2723,14 +2742,33 @@ static void gen6_fdi_link_train(struct drm_crtc *crtc) if (retry < 5) break; } - if (i == 4) + if (i == 4) { DRM_ERROR("FDI train 2 fail!\n"); + goto error; + } DRM_DEBUG_KMS("FDI train done.\n"); + return true; + +error: + reg = FDI_TX_CTL(pipe); + temp = I915_READ(reg); + temp &= ~FDI_LINK_TRAIN_NONE; + I915_WRITE(reg, temp); + + reg = FDI_RX_CTL(pipe); + temp = I915_READ(reg); + if (HAS_PCH_CPT(dev)) { + temp &= ~FDI_LINK_TRAIN_PATTERN_MASK_CPT; + } else { + temp &= ~FDI_LINK_TRAIN_NONE; + } + I915_WRITE(reg, temp); + return false; } /* Manual link training for Ivy Bridge A0 parts */ -static void ivb_manual_fdi_link_train(struct drm_crtc *crtc) +static bool ivb_manual_fdi_link_train(struct drm_crtc *crtc) { struct drm_device *dev = crtc->dev; struct drm_i915_private *dev_priv = dev->dev_private; @@ -2799,8 +2837,10 @@ static void ivb_manual_fdi_link_train(struct drm_crtc *crtc) break; } } - if (i == 4) + if (i == 4) { DRM_ERROR("FDI train 1 fail!\n"); + goto error; + } /* Train 2 */ reg = FDI_TX_CTL(pipe); @@ -2840,10 +2880,27 @@ static void ivb_manual_fdi_link_train(struct drm_crtc *crtc) break; } } - if (i == 4) + if (i == 4) { DRM_ERROR("FDI train 2 fail!\n"); + goto error; + } DRM_DEBUG_KMS("FDI train done.\n"); + return true; + +error: + reg = FDI_TX_CTL(pipe); + temp = I915_READ(reg); + temp &= ~FDI_LINK_TRAIN_NONE_IVB; + temp &= ~FDI_LINK_TRAIN_VOL_EMP_MASK; + I915_WRITE(reg, temp); + + reg = FDI_RX_CTL(pipe); + temp = I915_READ(reg); + temp &= ~FDI_LINK_TRAIN_PATTERN_MASK_CPT; + temp |= FDI_LINK_TRAIN_PATTERN_2_CPT; + I915_WRITE(reg, temp); + return false; } static void ironlake_fdi_pll_enable(struct intel_crtc *intel_crtc) @@ -3098,7 +3155,7 @@ static void lpt_program_iclkip(struct drm_crtc *crtc) * - DP transcoding bits * - transcoder */ -static void ironlake_pch_enable(struct drm_crtc *crtc) +static bool ironlake_pch_enable(struct drm_crtc *crtc) { struct drm_device *dev = crtc->dev; struct drm_i915_private *dev_priv = dev->dev_private; @@ -3114,7 +3171,8 @@ static void ironlake_pch_enable(struct drm_crtc *crtc) I915_READ(PIPE_DATA_M1(pipe)) & TU_SIZE_MASK); /* For PCH output, training FDI link */ - dev_priv->display.fdi_link_train(crtc); + if (!dev_priv->display.fdi_link_train(crtc)) + return false; /* XXX: pch pll's can be enabled any time before we enable the PCH * transcoder, and we actually should do this to not upset any PCH @@ -3201,6 +3259,7 @@ static void ironlake_pch_enable(struct drm_crtc *crtc) } ironlake_enable_pch_transcoder(dev_priv, pipe); + return true; } static void lpt_pch_enable(struct drm_crtc *crtc) @@ -3328,7 +3387,87 @@ void intel_cpt_verify_modeset(struct drm_device *dev, int pipe) } } -static void ironlake_crtc_enable(struct drm_crtc *crtc) +static void ironlake_crtc_disable(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); + struct intel_encoder *encoder; + int pipe = intel_crtc->pipe; + int plane = intel_crtc->plane; + u32 reg, temp; + + + if (!intel_crtc->active) + return; + + for_each_encoder_on_crtc(dev, crtc, encoder) + encoder->disable(encoder); + + intel_crtc_wait_for_pending_flips(crtc); + drm_vblank_off(dev, pipe); + intel_crtc_update_cursor(crtc, false); + + intel_disable_plane(dev_priv, plane, pipe); + + if (dev_priv->cfb_plane == plane) + intel_disable_fbc(dev); + + intel_disable_pipe(dev_priv, pipe); + + /* Disable PF */ + I915_WRITE(PF_CTL(pipe), 0); + I915_WRITE(PF_WIN_SZ(pipe), 0); + + for_each_encoder_on_crtc(dev, crtc, encoder) + if (encoder->post_disable) + encoder->post_disable(encoder); + + ironlake_fdi_disable(crtc); + + ironlake_disable_pch_transcoder(dev_priv, pipe); + + if (HAS_PCH_CPT(dev)) { + /* disable TRANS_DP_CTL */ + reg = TRANS_DP_CTL(pipe); + temp = I915_READ(reg); + temp &= ~(TRANS_DP_OUTPUT_ENABLE | TRANS_DP_PORT_SEL_MASK); + temp |= TRANS_DP_PORT_SEL_NONE; + I915_WRITE(reg, temp); + + /* disable DPLL_SEL */ + temp = I915_READ(PCH_DPLL_SEL); + switch (pipe) { + case 0: + temp &= ~(TRANSA_DPLL_ENABLE | TRANSA_DPLLB_SEL); + break; + case 1: + temp &= ~(TRANSB_DPLL_ENABLE | TRANSB_DPLLB_SEL); + break; + case 2: + /* C shares PLL A or B */ + temp &= ~(TRANSC_DPLL_ENABLE | TRANSC_DPLLB_SEL); + break; + default: + BUG(); /* wtf */ + } + I915_WRITE(PCH_DPLL_SEL, temp); + } + + /* disable PCH DPLL */ + intel_disable_pch_pll(intel_crtc); + + ironlake_fdi_pll_disable(intel_crtc); + + intel_crtc->active = false; + intel_update_watermarks(dev); + + mutex_lock(&dev->struct_mutex); + intel_update_fbc(dev); + mutex_unlock(&dev->struct_mutex); +} + +static int ironlake_crtc_enable(struct drm_crtc *crtc) { struct drm_device *dev = crtc->dev; struct drm_i915_private *dev_priv = dev->dev_private; @@ -3341,7 +3480,7 @@ static void ironlake_crtc_enable(struct drm_crtc *crtc) WARN_ON(!crtc->enabled); if (intel_crtc->active) - return; + return 0; intel_crtc->active = true; intel_update_watermarks(dev); @@ -3394,8 +3533,12 @@ static void ironlake_crtc_enable(struct drm_crtc *crtc) intel_crtc->config.has_pch_encoder); intel_enable_plane(dev_priv, plane, pipe); - if (intel_crtc->config.has_pch_encoder) - ironlake_pch_enable(crtc); + if (intel_crtc->config.has_pch_encoder) { + if (!ironlake_pch_enable(crtc)) { + ironlake_crtc_disable(crtc); + return -EIO; + } + } mutex_lock(&dev->struct_mutex); intel_update_fbc(dev); @@ -3418,9 +3561,10 @@ static void ironlake_crtc_enable(struct drm_crtc *crtc) * happening. */ intel_wait_for_vblank(dev, intel_crtc->pipe); + return 0; } -static void haswell_crtc_enable(struct drm_crtc *crtc) +static int haswell_crtc_enable(struct drm_crtc *crtc) { struct drm_device *dev = crtc->dev; struct drm_i915_private *dev_priv = dev->dev_private; @@ -3432,13 +3576,18 @@ static void haswell_crtc_enable(struct drm_crtc *crtc) WARN_ON(!crtc->enabled); if (intel_crtc->active) - return; + return 0; intel_crtc->active = true; intel_update_watermarks(dev); - if (intel_crtc->config.has_pch_encoder) - dev_priv->display.fdi_link_train(crtc); + if (intel_crtc->config.has_pch_encoder) { + if (!dev_priv->display.fdi_link_train(crtc)) { + intel_crtc->active = false; + intel_update_watermarks(dev); + return -EIO; + } + } for_each_encoder_on_crtc(dev, crtc, encoder) if (encoder->pre_enable) @@ -3493,86 +3642,7 @@ static void haswell_crtc_enable(struct drm_crtc *crtc) * happening. */ intel_wait_for_vblank(dev, intel_crtc->pipe); -} - -static void ironlake_crtc_disable(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); - struct intel_encoder *encoder; - int pipe = intel_crtc->pipe; - int plane = intel_crtc->plane; - u32 reg, temp; - - - if (!intel_crtc->active) - return; - - for_each_encoder_on_crtc(dev, crtc, encoder) - encoder->disable(encoder); - - intel_crtc_wait_for_pending_flips(crtc); - drm_vblank_off(dev, pipe); - intel_crtc_update_cursor(crtc, false); - - intel_disable_plane(dev_priv, plane, pipe); - - if (dev_priv->cfb_plane == plane) - intel_disable_fbc(dev); - - intel_disable_pipe(dev_priv, pipe); - - /* Disable PF */ - I915_WRITE(PF_CTL(pipe), 0); - I915_WRITE(PF_WIN_SZ(pipe), 0); - - for_each_encoder_on_crtc(dev, crtc, encoder) - if (encoder->post_disable) - encoder->post_disable(encoder); - - ironlake_fdi_disable(crtc); - - ironlake_disable_pch_transcoder(dev_priv, pipe); - - if (HAS_PCH_CPT(dev)) { - /* disable TRANS_DP_CTL */ - reg = TRANS_DP_CTL(pipe); - temp = I915_READ(reg); - temp &= ~(TRANS_DP_OUTPUT_ENABLE | TRANS_DP_PORT_SEL_MASK); - temp |= TRANS_DP_PORT_SEL_NONE; - I915_WRITE(reg, temp); - - /* disable DPLL_SEL */ - temp = I915_READ(PCH_DPLL_SEL); - switch (pipe) { - case 0: - temp &= ~(TRANSA_DPLL_ENABLE | TRANSA_DPLLB_SEL); - break; - case 1: - temp &= ~(TRANSB_DPLL_ENABLE | TRANSB_DPLLB_SEL); - break; - case 2: - /* C shares PLL A or B */ - temp &= ~(TRANSC_DPLL_ENABLE | TRANSC_DPLLB_SEL); - break; - default: - BUG(); /* wtf */ - } - I915_WRITE(PCH_DPLL_SEL, temp); - } - - /* disable PCH DPLL */ - intel_disable_pch_pll(intel_crtc); - - ironlake_fdi_pll_disable(intel_crtc); - - intel_crtc->active = false; - intel_update_watermarks(dev); - - mutex_lock(&dev->struct_mutex); - intel_update_fbc(dev); - mutex_unlock(&dev->struct_mutex); + return 0; } static void haswell_crtc_disable(struct drm_crtc *crtc) @@ -3690,7 +3760,7 @@ g4x_fixup_plane(struct drm_i915_private *dev_priv, enum pipe pipe) } } -static void i9xx_crtc_enable(struct drm_crtc *crtc) +static int i9xx_crtc_enable(struct drm_crtc *crtc) { struct drm_device *dev = crtc->dev; struct drm_i915_private *dev_priv = dev->dev_private; @@ -3702,7 +3772,7 @@ static void i9xx_crtc_enable(struct drm_crtc *crtc) WARN_ON(!crtc->enabled); if (intel_crtc->active) - return; + return 0; intel_crtc->active = true; intel_update_watermarks(dev); @@ -3727,6 +3797,8 @@ static void i9xx_crtc_enable(struct drm_crtc *crtc) for_each_encoder_on_crtc(dev, crtc, encoder) encoder->enable(encoder); + + return 0; } static void i9xx_pfit_disable(struct intel_crtc *crtc) @@ -3833,8 +3905,8 @@ void intel_crtc_update_dpms(struct drm_crtc *crtc) enable |= intel_encoder->connectors_active; if (enable) - dev_priv->display.crtc_enable(crtc); - else + enable = dev_priv->display.crtc_enable(crtc) == 0; + if (!enable) dev_priv->display.crtc_disable(crtc); intel_crtc_update_sarea(crtc, enable); @@ -3901,15 +3973,8 @@ void intel_encoder_destroy(struct drm_encoder *encoder) * state of the entire output pipe. */ void intel_encoder_dpms(struct intel_encoder *encoder, int mode) { - if (mode == DRM_MODE_DPMS_ON) { - encoder->connectors_active = true; - - intel_crtc_update_dpms(encoder->base.crtc); - } else { - encoder->connectors_active = false; - - intel_crtc_update_dpms(encoder->base.crtc); - } + encoder->connectors_active = mode == DRM_MODE_DPMS_ON; + intel_crtc_update_dpms(encoder->base.crtc); } /* Cross check the actual hw state with our own modeset state tracking (and it's @@ -8013,8 +8078,11 @@ static int __intel_set_mode(struct drm_crtc *crtc, } /* Now enable the clocks, plane, pipe, and connectors that we set up. */ - for_each_intel_crtc_masked(dev, prepare_pipes, intel_crtc) - dev_priv->display.crtc_enable(&intel_crtc->base); + for_each_intel_crtc_masked(dev, prepare_pipes, intel_crtc) { + ret = dev_priv->display.crtc_enable(&intel_crtc->base); + if (ret) + goto done; + } if (modeset_pipes) { /* Store real post-adjustment hardware mode. */ @@ -8029,10 +8097,16 @@ static int __intel_set_mode(struct drm_crtc *crtc, /* FIXME: add subpixel order */ done: - if (ret && crtc->enabled) { - crtc->hwmode = *saved_hwmode; - crtc->mode = *saved_mode; - } + if (ret) { + for_each_intel_crtc_masked(dev, prepare_pipes, intel_crtc) + if (intel_crtc->base.enabled) + dev_priv->display.crtc_disable(&intel_crtc->base); + if (crtc->enabled) { + crtc->hwmode = *saved_hwmode; + crtc->mode = *saved_mode; + } + } else + intel_modeset_check_state(dev); out: kfree(pipe_config); diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index a124e05..75d9cc7 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -660,7 +660,7 @@ extern void intel_cpu_transcoder_set_m_n(struct intel_crtc *crtc, extern void intel_pch_transcoder_set_m_n(struct intel_crtc *crtc, struct intel_link_m_n *m_n); extern void intel_prepare_ddi(struct drm_device *dev); -extern void hsw_fdi_link_train(struct drm_crtc *crtc); +extern bool hsw_fdi_link_train(struct drm_crtc *crtc); extern void intel_ddi_init(struct drm_device *dev, enum port port); /* For use by IVB LP watermark workaround in intel_sprite.c */ -- 1.7.10.4