aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJesse Barnes <jbarnes@nietzche.virtuousgeek.org>2008-01-22 13:11:29 -0800
committerJesse Barnes <jbarnes@nietzche.virtuousgeek.org>2008-01-22 13:11:29 -0800
commit893e311999d1565943899d73c56c674fc9b6e502 (patch)
tree27d49eb0b8d1814d8ca4bfd3fa66a6d24b7fd223
parent0cd4cbc9a6330bd619608f274592082de7c05bcf (diff)
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.
-rw-r--r--shared-core/i915_irq.c119
1 files changed, 87 insertions, 32 deletions
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)
@@ -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;
}