drm/i915: CPT+ pch transcoder workaround

We need to set the timing override chicken bit after fdi link training
has completed and before we enable the transcoder. We also have to
clear that bit again after disabling the pch transcoder.

See "Graphics BSpec: vol4g North Display Engine Registers [IVB],
Display Mode Set Sequence" and "Graphics BSpec: vol4h South Display
Engine Registers [CPT, PPT], South Display Engine Transcoder and FDI
Control, Transcoder Debug and DFT, TRANS_CHICKEN_2" bit 31:

"Workaround : Enable the override prior to enabling the transcoder.
Disable the override after disabling the transcoder."

While at it, use the _PIPE macro for the other TRANS_DP register.

v2: Keep the w/a as-is, but kill the original (but wrongly placed)
workaround introduced in

commit 3bcf603f6d
Author: Jesse Barnes <jbarnes@virtuousgeek.org>
Date:   Wed Jul 27 11:51:40 2011 -0700

    drm/i915: apply timing generator bug workaround on CPT and PPT

and

commit d4270e57ef
Author: Jesse Barnes <jbarnes@virtuousgeek.org>
Date:   Tue Oct 11 10:43:02 2011 -0700

    drm/i915: export a CPT mode set verification function

Note that this old code has unconditionally set the w/a, which might
explain why fdi link training sometimes silently fails, and especially
why the auto-train did not seem to work properly.

v3: Paulo Zanoni pointed out that this workaround is also required on
the LPT PCH. And Arthur Ranyan confirmed that this workaround is
requierd for all ports on the pch, not just DP: The important part
is that the bit is set whenever the pch transcoder is enabled, and
that it is _not_ set while the fdi link is trained. It is also
important that the pch transcoder is fully disabled, i.e. we have to
wait for bit 30 to clear before clearing the w/a bit.

Hence move to workaround into enable/disable_transcoder, where the pch
transcoder gets enabled/disabled.

v4: Whitespace changes dropped.

v5: Don't run the w/a on IBX, we only need it on CPT/PPT and LPT.

v6:
- resolve conflicts with Paulo's big hsw vga rework
- s/!IBX/CPT since hsw paths are now all separate, and Paulo's patch
  to implement the equivalent w/a for LPT is already merged.

Cc: Jesse Barnes <jbarnes@virtuousgeek.org>
Cc: Paulo Zanoni <przanoni@gmail.com>
Cc: Arthur Ranyan <arthur.j.runyan@intel.com>
Reviewed-by: Paulo Zanoni <paulo.r.zanoni@intel.com> (v5)
Reviewed-by: Jesse Barnes <jbarnes@virtuousgeek.org> (v5)
Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
This commit is contained in:
Daniel Vetter 2012-11-01 09:15:30 +01:00
parent 8f5718a6d9
commit 23670b322c
3 changed files with 27 additions and 17 deletions

View File

@ -3827,7 +3827,8 @@
#define _TRANSA_CHICKEN2 0xf0064 #define _TRANSA_CHICKEN2 0xf0064
#define _TRANSB_CHICKEN2 0xf1064 #define _TRANSB_CHICKEN2 0xf1064
#define TRANS_CHICKEN2(pipe) _PIPE(pipe, _TRANSA_CHICKEN2, _TRANSB_CHICKEN2) #define TRANS_CHICKEN2(pipe) _PIPE(pipe, _TRANSA_CHICKEN2, _TRANSB_CHICKEN2)
#define TRANS_AUTOTRAIN_GEN_STALL_DIS (1<<31) #define TRANS_CHICKEN2_TIMING_OVERRIDE (1<<31)
#define SOUTH_CHICKEN1 0xc2000 #define SOUTH_CHICKEN1 0xc2000
#define FDIA_PHASE_SYNC_SHIFT_OVR 19 #define FDIA_PHASE_SYNC_SHIFT_OVR 19
@ -4085,7 +4086,7 @@
#define TRANS_DP_CTL_A 0xe0300 #define TRANS_DP_CTL_A 0xe0300
#define TRANS_DP_CTL_B 0xe1300 #define TRANS_DP_CTL_B 0xe1300
#define TRANS_DP_CTL_C 0xe2300 #define TRANS_DP_CTL_C 0xe2300
#define TRANS_DP_CTL(pipe) (TRANS_DP_CTL_A + (pipe) * 0x01000) #define TRANS_DP_CTL(pipe) _PIPE(pipe, TRANS_DP_CTL_A, TRANS_DP_CTL_B)
#define TRANS_DP_OUTPUT_ENABLE (1<<31) #define TRANS_DP_OUTPUT_ENABLE (1<<31)
#define TRANS_DP_PORT_SEL_B (0<<29) #define TRANS_DP_PORT_SEL_B (0<<29)
#define TRANS_DP_PORT_SEL_C (1<<29) #define TRANS_DP_PORT_SEL_C (1<<29)

View File

@ -1673,9 +1673,9 @@ static void intel_disable_pch_pll(struct intel_crtc *intel_crtc)
static void ironlake_enable_pch_transcoder(struct drm_i915_private *dev_priv, static void ironlake_enable_pch_transcoder(struct drm_i915_private *dev_priv,
enum pipe pipe) enum pipe pipe)
{ {
int reg; struct drm_device *dev = dev_priv->dev;
u32 val, pipeconf_val;
struct drm_crtc *crtc = dev_priv->pipe_to_crtc_mapping[pipe]; struct drm_crtc *crtc = dev_priv->pipe_to_crtc_mapping[pipe];
uint32_t reg, val, pipeconf_val;
/* PCH only available on ILK+ */ /* PCH only available on ILK+ */
BUG_ON(dev_priv->info->gen < 5); BUG_ON(dev_priv->info->gen < 5);
@ -1689,6 +1689,15 @@ static void ironlake_enable_pch_transcoder(struct drm_i915_private *dev_priv,
assert_fdi_tx_enabled(dev_priv, pipe); assert_fdi_tx_enabled(dev_priv, pipe);
assert_fdi_rx_enabled(dev_priv, pipe); assert_fdi_rx_enabled(dev_priv, pipe);
if (HAS_PCH_CPT(dev)) {
/* Workaround: Set the timing override bit before enabling the
* pch transcoder. */
reg = TRANS_CHICKEN2(pipe);
val = I915_READ(reg);
val |= TRANS_CHICKEN2_TIMING_OVERRIDE;
I915_WRITE(reg, val);
}
reg = TRANSCONF(pipe); reg = TRANSCONF(pipe);
val = I915_READ(reg); val = I915_READ(reg);
pipeconf_val = I915_READ(PIPECONF(pipe)); pipeconf_val = I915_READ(PIPECONF(pipe));
@ -1731,7 +1740,7 @@ static void lpt_enable_pch_transcoder(struct drm_i915_private *dev_priv,
/* Workaround: set timing override bit. */ /* Workaround: set timing override bit. */
val = I915_READ(_TRANSA_CHICKEN2); val = I915_READ(_TRANSA_CHICKEN2);
val |= TRANS_AUTOTRAIN_GEN_STALL_DIS; val |= TRANS_CHICKEN2_TIMING_OVERRIDE;
I915_WRITE(_TRANSA_CHICKEN2, val); I915_WRITE(_TRANSA_CHICKEN2, val);
val = TRANS_ENABLE; val = TRANS_ENABLE;
@ -1751,8 +1760,8 @@ static void lpt_enable_pch_transcoder(struct drm_i915_private *dev_priv,
static void ironlake_disable_pch_transcoder(struct drm_i915_private *dev_priv, static void ironlake_disable_pch_transcoder(struct drm_i915_private *dev_priv,
enum pipe pipe) enum pipe pipe)
{ {
int reg; struct drm_device *dev = dev_priv->dev;
u32 val; uint32_t reg, val;
/* FDI relies on the transcoder */ /* FDI relies on the transcoder */
assert_fdi_tx_disabled(dev_priv, pipe); assert_fdi_tx_disabled(dev_priv, pipe);
@ -1768,6 +1777,14 @@ static void ironlake_disable_pch_transcoder(struct drm_i915_private *dev_priv,
/* wait for PCH transcoder off, transcoder state */ /* wait for PCH transcoder off, transcoder state */
if (wait_for((I915_READ(reg) & TRANS_STATE_ENABLE) == 0, 50)) if (wait_for((I915_READ(reg) & TRANS_STATE_ENABLE) == 0, 50))
DRM_ERROR("failed to disable transcoder %d\n", pipe); DRM_ERROR("failed to disable transcoder %d\n", pipe);
if (!HAS_PCH_IBX(dev)) {
/* Workaround: Clear the timing override chicken bit again. */
reg = TRANS_CHICKEN2(pipe);
val = I915_READ(reg);
val &= ~TRANS_CHICKEN2_TIMING_OVERRIDE;
I915_WRITE(reg, val);
}
} }
static void lpt_disable_pch_transcoder(struct drm_i915_private *dev_priv) static void lpt_disable_pch_transcoder(struct drm_i915_private *dev_priv)
@ -1783,7 +1800,7 @@ static void lpt_disable_pch_transcoder(struct drm_i915_private *dev_priv)
/* Workaround: clear timing override bit. */ /* Workaround: clear timing override bit. */
val = I915_READ(_TRANSA_CHICKEN2); val = I915_READ(_TRANSA_CHICKEN2);
val &= ~TRANS_AUTOTRAIN_GEN_STALL_DIS; val &= ~TRANS_CHICKEN2_TIMING_OVERRIDE;
I915_WRITE(_TRANSA_CHICKEN2, val); I915_WRITE(_TRANSA_CHICKEN2, val);
} }
@ -3327,16 +3344,12 @@ static struct intel_pch_pll *intel_get_pch_pll(struct intel_crtc *intel_crtc, u3
void intel_cpt_verify_modeset(struct drm_device *dev, int pipe) void intel_cpt_verify_modeset(struct drm_device *dev, int pipe)
{ {
struct drm_i915_private *dev_priv = dev->dev_private; struct drm_i915_private *dev_priv = dev->dev_private;
int dslreg = PIPEDSL(pipe), tc2reg = TRANS_CHICKEN2(pipe); int dslreg = PIPEDSL(pipe);
u32 temp; u32 temp;
temp = I915_READ(dslreg); temp = I915_READ(dslreg);
udelay(500); udelay(500);
if (wait_for(I915_READ(dslreg) != temp, 5)) { if (wait_for(I915_READ(dslreg) != temp, 5)) {
/* Without this, mode sets may fail silently on FDI */
I915_WRITE(tc2reg, TRANS_AUTOTRAIN_GEN_STALL_DIS);
udelay(250);
I915_WRITE(tc2reg, 0);
if (wait_for(I915_READ(dslreg) != temp, 5)) if (wait_for(I915_READ(dslreg) != temp, 5))
DRM_ERROR("mode set failed: pipe %d stuck\n", pipe); DRM_ERROR("mode set failed: pipe %d stuck\n", pipe);
} }

View File

@ -3794,7 +3794,6 @@ static void ibx_init_clock_gating(struct drm_device *dev)
static void cpt_init_clock_gating(struct drm_device *dev) static void cpt_init_clock_gating(struct drm_device *dev)
{ {
struct drm_i915_private *dev_priv = dev->dev_private; struct drm_i915_private *dev_priv = dev->dev_private;
int pipe;
/* /*
* On Ibex Peak and Cougar Point, we need to disable clock * On Ibex Peak and Cougar Point, we need to disable clock
@ -3804,9 +3803,6 @@ static void cpt_init_clock_gating(struct drm_device *dev)
I915_WRITE(SOUTH_DSPCLK_GATE_D, PCH_DPLSUNIT_CLOCK_GATE_DISABLE); I915_WRITE(SOUTH_DSPCLK_GATE_D, PCH_DPLSUNIT_CLOCK_GATE_DISABLE);
I915_WRITE(SOUTH_CHICKEN2, I915_READ(SOUTH_CHICKEN2) | I915_WRITE(SOUTH_CHICKEN2, I915_READ(SOUTH_CHICKEN2) |
DPLS_EDP_PPS_FIX_DIS); DPLS_EDP_PPS_FIX_DIS);
/* Without this, mode sets may fail silently on FDI */
for_each_pipe(pipe)
I915_WRITE(TRANS_CHICKEN2(pipe), TRANS_AUTOTRAIN_GEN_STALL_DIS);
} }
void intel_init_clock_gating(struct drm_device *dev) void intel_init_clock_gating(struct drm_device *dev)