summaryrefslogtreecommitdiff
path: root/shared-core/i915_irq.c
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 /shared-core/i915_irq.c
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.
Diffstat (limited to 'shared-core/i915_irq.c')
-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;
}