i915 irq fixes
authorJesse Barnes <jbarnes@nietzche.virtuousgeek.org>
Tue, 22 Jan 2008 21:11:29 +0000 (13:11 -0800)
committerJesse Barnes <jbarnes@nietzche.virtuousgeek.org>
Tue, 22 Jan 2008 21:11:29 +0000 (13:11 -0800)
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

index 9b46b12..bef73b6 100644 (file)
@@ -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)
@@ -58,6 +58,46 @@ i915_get_pipe(struct drm_device *dev, int plane)
 }
 
 /**
+ * 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.
  *
  * This function must be called with the drawable spinlock held.
@@ -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;
        }