From 893e311999d1565943899d73c56c674fc9b6e502 Mon Sep 17 00:00:00 2001 From: Jesse Barnes Date: Tue, 22 Jan 2008 13:11:29 -0800 Subject: i915 irq fixes Ack the IRQs correctly (PIPExSTAT first followed by IIR). Don't read vblank counter registers on disabled pipes (might hang otherwise). And deal with flipped pipe/plane mappings if present. --- shared-core/i915_irq.c | 119 ++++++++++++++++++++++++++++++++++++------------- 1 file changed, 87 insertions(+), 32 deletions(-) (limited to 'shared-core') diff --git a/shared-core/i915_irq.c b/shared-core/i915_irq.c index 9b46b127..bef73b62 100644 --- a/shared-core/i915_irq.c +++ b/shared-core/i915_irq.c @@ -42,9 +42,9 @@ * @dev: DRM device * @plane: plane to look for * - * We need to get the pipe associated with a given plane to correctly perform - * vblank driven swapping, and they may not always be equal. So look up the - * pipe associated with @plane here. + * The Intel Mesa & 2D drivers call the vblank routines with a plane number + * rather than a pipe number, since they may not always be equal. This routine + * maps the given @plane back to a pipe number. */ static int i915_get_pipe(struct drm_device *dev, int plane) @@ -57,6 +57,46 @@ i915_get_pipe(struct drm_device *dev, int plane) return dspcntr & DISPPLANE_SEL_PIPE_MASK ? 1 : 0; } +/** + * i915_get_plane - return the the plane associated with a given pipe + * @dev: DRM device + * @pipe: pipe to look for + * + * The Intel Mesa & 2D drivers call the vblank routines with a plane number + * rather than a plane number, since they may not always be equal. This routine + * maps the given @pipe back to a plane number. + */ +static int +i915_get_plane(struct drm_device *dev, int pipe) +{ + drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; + + if (i915_get_pipe(dev, 0) == pipe) + return 0; + return 1; +} + +/** + * i915_pipe_enabled - check if a pipe is enabled + * @dev: DRM device + * @pipe: pipe to check + * + * Reading certain registers when the pipe is disabled can hang the chip. + * Use this routine to make sure the PLL is running and the pipe is active + * before reading such registers if unsure. + */ +static int +i915_pipe_enabled(struct drm_device *dev, int pipe) +{ + drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; + unsigned long pipeconf = pipe ? PIPEBCONF : PIPEACONF; + + if (I915_READ(pipeconf) & PIPEACONF_ENABLE) + return 1; + + return 0; +} + /** * Emit a synchronous flip. * @@ -146,14 +186,14 @@ static void i915_vblank_tasklet(struct drm_device *dev) list_for_each_safe(list, tmp, &dev_priv->vbl_swaps.head) { drm_i915_vbl_swap_t *vbl_swap = list_entry(list, drm_i915_vbl_swap_t, head); - int crtc = i915_get_pipe(dev, vbl_swap->plane); + int pipe = i915_get_pipe(dev, vbl_swap->plane); - if ((counter[crtc] - vbl_swap->sequence) > (1<<23)) + if ((counter[pipe] - vbl_swap->sequence) > (1<<23)) continue; list_del(list); dev_priv->swaps_pending--; - drm_vblank_put(dev, crtc); + drm_vblank_put(dev, pipe); DRM_SPINUNLOCK(&dev_priv->swaps_lock); DRM_SPINLOCK(&dev->drw_lock); @@ -304,12 +344,23 @@ static void i915_vblank_tasklet(struct drm_device *dev) } } -u32 i915_get_vblank_counter(struct drm_device *dev, int crtc) +u32 i915_get_vblank_counter(struct drm_device *dev, int plane) { drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; - unsigned long high_frame = crtc ? PIPEBFRAMEHIGH : PIPEAFRAMEHIGH; - unsigned long low_frame = crtc ? PIPEBFRAMEPIXEL : PIPEAFRAMEPIXEL; + unsigned long high_frame; + unsigned long low_frame; u32 high1, high2, low, count; + int pipe; + + pipe = i915_get_pipe(dev, plane); + high_frame = pipe ? PIPEBFRAMEHIGH : PIPEAFRAMEHIGH; + low_frame = pipe ? PIPEBFRAMEPIXEL : PIPEAFRAMEPIXEL; + + if (!i915_pipe_enabled(dev, pipe)) { + printk(KERN_ERR "trying to get vblank count for disabled " + "pipe %d\n", pipe); + return 0; + } /* * High & low register fields aren't synchronized, so make sure @@ -348,6 +399,23 @@ irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS) if (temp == 0) return IRQ_NONE; + /* + * Clear the PIPE(A|B)STAT regs before the IIR otherwise + * we may get extra interrupts. + */ + if (temp & VSYNC_PIPEA_FLAG) { + drm_handle_vblank(dev, i915_get_plane(dev, 0)); + I915_WRITE(I915REG_PIPEASTAT, + pipea_stats | I915_VBLANK_INTERRUPT_ENABLE | + I915_VBLANK_CLEAR); + } + if (temp & VSYNC_PIPEB_FLAG) { + drm_handle_vblank(dev, i915_get_plane(dev, 1)); + I915_WRITE(I915REG_PIPEBSTAT, + pipeb_stats | I915_VBLANK_INTERRUPT_ENABLE | + I915_VBLANK_CLEAR); + } + I915_WRITE16(I915REG_INT_IDENTITY_R, temp); (void) I915_READ16(I915REG_INT_IDENTITY_R); /* Flush posted write */ @@ -363,24 +431,9 @@ irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS) #endif } - /* - * Use drm_update_vblank_counter here to deal with potential lost - * interrupts - */ - if (temp & VSYNC_PIPEA_FLAG) - drm_handle_vblank(dev, 0); - if (temp & VSYNC_PIPEB_FLAG) - drm_handle_vblank(dev, 1); - if (temp & (VSYNC_PIPEA_FLAG | VSYNC_PIPEB_FLAG)) { if (dev_priv->swaps_pending > 0) drm_locked_tasklet(dev, i915_vblank_tasklet); - I915_WRITE(I915REG_PIPEASTAT, - pipea_stats|I915_VBLANK_INTERRUPT_ENABLE| - I915_VBLANK_CLEAR); - I915_WRITE(I915REG_PIPEBSTAT, - pipeb_stats|I915_VBLANK_INTERRUPT_ENABLE| - I915_VBLANK_CLEAR); } return IRQ_HANDLED; @@ -494,11 +547,12 @@ int i915_irq_wait(struct drm_device *dev, void *data, return i915_wait_irq(dev, irqwait->irq_seq); } -int i915_enable_vblank(struct drm_device *dev, int crtc) +int i915_enable_vblank(struct drm_device *dev, int plane) { drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; + int pipe = i915_get_pipe(dev, plane); - switch (crtc) { + switch (pipe) { case 0: dev_priv->irq_enable_reg |= VSYNC_PIPEA_FLAG; break; @@ -506,8 +560,8 @@ int i915_enable_vblank(struct drm_device *dev, int crtc) dev_priv->irq_enable_reg |= VSYNC_PIPEB_FLAG; break; default: - DRM_ERROR("tried to enable vblank on non-existent crtc %d\n", - crtc); + DRM_ERROR("tried to enable vblank on non-existent pipe %d\n", + pipe); break; } @@ -516,11 +570,12 @@ int i915_enable_vblank(struct drm_device *dev, int crtc) return 0; } -void i915_disable_vblank(struct drm_device *dev, int crtc) +void i915_disable_vblank(struct drm_device *dev, int plane) { drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; + int pipe = i915_get_pipe(dev, plane); - switch (crtc) { + switch (pipe) { case 0: dev_priv->irq_enable_reg &= ~VSYNC_PIPEA_FLAG; break; @@ -528,8 +583,8 @@ void i915_disable_vblank(struct drm_device *dev, int crtc) dev_priv->irq_enable_reg &= ~VSYNC_PIPEB_FLAG; break; default: - DRM_ERROR("tried to disable vblank on non-existent crtc %d\n", - crtc); + DRM_ERROR("tried to disable vblank on non-existent pipe %d\n", + pipe); break; } -- cgit v1.2.3