Commit 095163ba authored by Ville Syrjälä's avatar Ville Syrjälä

drm/i915: Add a kludge for DSL incrementing too late and ISR not working

On pre-PCH platforms ISR doesn't seem to be an actual ISR, at least as
far as display interrupts are concerned. Instead it sort of looks like
some ISR bits just directly reflect the corresponding bit from PIPESTAT.
The bit appears in the ISR only if the PIPESTAT interrupt is enabled. So
in that sense it sort of looks a bit like the south interrupt scheme on
PCH platforms. So it goes something a bit like this:
PIPESTAT.status & PIPESTAT.enable -> ISR -> IMR -> IIR -> IER -> actual
interrupt

In any case that means the intel_pipe_in_vblank_locked() doesn't actually
work for pre-PCH platforms. As a last resort, add a similar kludge as radeon
has that fixes things up if we got called from the vblank interrupt,
but the scanline counter value indicates that we're not quite there yet.
We know that the scanline counter increments at hsync but is otherwise
accurate, so we can limit the kludge to the line just prior to vblank
start, instead of the relative distance that radeon uses.

Reviewed-by: mario.kleiner.de@gmail.com
Signed-off-by: default avatarVille Syrjälä <ville.syrjala@linux.intel.com>
parent 8072bfa6
...@@ -621,36 +621,15 @@ static u32 gm45_get_vblank_counter(struct drm_device *dev, int pipe) ...@@ -621,36 +621,15 @@ static u32 gm45_get_vblank_counter(struct drm_device *dev, int pipe)
#define __raw_i915_read32(dev_priv__, reg__) readl((dev_priv__)->regs + (reg__)) #define __raw_i915_read32(dev_priv__, reg__) readl((dev_priv__)->regs + (reg__))
#define __raw_i915_read16(dev_priv__, reg__) readw((dev_priv__)->regs + (reg__)) #define __raw_i915_read16(dev_priv__, reg__) readw((dev_priv__)->regs + (reg__))
static bool intel_pipe_in_vblank_locked(struct drm_device *dev, enum pipe pipe) static bool ilk_pipe_in_vblank_locked(struct drm_device *dev, enum pipe pipe)
{ {
struct drm_i915_private *dev_priv = dev->dev_private; struct drm_i915_private *dev_priv = dev->dev_private;
uint32_t status; uint32_t status;
int reg;
if (IS_VALLEYVIEW(dev)) { if (INTEL_INFO(dev)->gen < 7) {
status = pipe == PIPE_A ?
I915_DISPLAY_PIPE_A_VBLANK_INTERRUPT :
I915_DISPLAY_PIPE_B_VBLANK_INTERRUPT;
reg = VLV_ISR;
} else if (IS_GEN2(dev)) {
status = pipe == PIPE_A ?
I915_DISPLAY_PIPE_A_VBLANK_INTERRUPT :
I915_DISPLAY_PIPE_B_VBLANK_INTERRUPT;
reg = ISR;
} else if (INTEL_INFO(dev)->gen < 5) {
status = pipe == PIPE_A ?
I915_DISPLAY_PIPE_A_VBLANK_INTERRUPT :
I915_DISPLAY_PIPE_B_VBLANK_INTERRUPT;
reg = ISR;
} else if (INTEL_INFO(dev)->gen < 7) {
status = pipe == PIPE_A ? status = pipe == PIPE_A ?
DE_PIPEA_VBLANK : DE_PIPEA_VBLANK :
DE_PIPEB_VBLANK; DE_PIPEB_VBLANK;
reg = DEISR;
} else { } else {
switch (pipe) { switch (pipe) {
default: default:
...@@ -664,14 +643,9 @@ static bool intel_pipe_in_vblank_locked(struct drm_device *dev, enum pipe pipe) ...@@ -664,14 +643,9 @@ static bool intel_pipe_in_vblank_locked(struct drm_device *dev, enum pipe pipe)
status = DE_PIPEC_VBLANK_IVB; status = DE_PIPEC_VBLANK_IVB;
break; break;
} }
reg = DEISR;
} }
if (IS_GEN2(dev)) return __raw_i915_read32(dev_priv, DEISR) & status;
return __raw_i915_read16(dev_priv, reg) & status;
else
return __raw_i915_read32(dev_priv, reg) & status;
} }
static int i915_get_crtc_scanoutpos(struct drm_device *dev, int pipe, static int i915_get_crtc_scanoutpos(struct drm_device *dev, int pipe,
...@@ -729,17 +703,42 @@ static int i915_get_crtc_scanoutpos(struct drm_device *dev, int pipe, ...@@ -729,17 +703,42 @@ static int i915_get_crtc_scanoutpos(struct drm_device *dev, int pipe,
else else
position = __raw_i915_read32(dev_priv, PIPEDSL(pipe)) & DSL_LINEMASK_GEN3; position = __raw_i915_read32(dev_priv, PIPEDSL(pipe)) & DSL_LINEMASK_GEN3;
/* if (HAS_PCH_SPLIT(dev)) {
* The scanline counter increments at the leading edge /*
* of hsync, ie. it completely misses the active portion * The scanline counter increments at the leading edge
* of the line. Fix up the counter at both edges of vblank * of hsync, ie. it completely misses the active portion
* to get a more accurate picture whether we're in vblank * of the line. Fix up the counter at both edges of vblank
* or not. * to get a more accurate picture whether we're in vblank
*/ * or not.
in_vbl = intel_pipe_in_vblank_locked(dev, pipe); */
if ((in_vbl && position == vbl_start - 1) || in_vbl = ilk_pipe_in_vblank_locked(dev, pipe);
(!in_vbl && position == vbl_end - 1)) if ((in_vbl && position == vbl_start - 1) ||
position = (position + 1) % vtotal; (!in_vbl && position == vbl_end - 1))
position = (position + 1) % vtotal;
} else {
/*
* ISR vblank status bits don't work the way we'd want
* them to work on non-PCH platforms (for
* ilk_pipe_in_vblank_locked()), and there doesn't
* appear any other way to determine if we're currently
* in vblank.
*
* Instead let's assume that we're already in vblank if
* we got called from the vblank interrupt and the
* scanline counter value indicates that we're on the
* line just prior to vblank start. This should result
* in the correct answer, unless the vblank interrupt
* delivery really got delayed for almost exactly one
* full frame/field.
*/
if (flags & DRM_CALLED_FROM_VBLIRQ &&
position == vbl_start - 1) {
position = (position + 1) % vtotal;
/* Signal this correction as "applied". */
ret |= 0x8;
}
}
} else { } else {
/* Have access to pixelcount since start of frame. /* Have access to pixelcount since start of frame.
* We can split this into vertical and horizontal * We can split this into vertical and horizontal
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment