summaryrefslogtreecommitdiff
path: root/shared-core/i915_irq.c
diff options
context:
space:
mode:
Diffstat (limited to 'shared-core/i915_irq.c')
-rw-r--r--shared-core/i915_irq.c174
1 files changed, 108 insertions, 66 deletions
diff --git a/shared-core/i915_irq.c b/shared-core/i915_irq.c
index db18a895..46d09663 100644
--- a/shared-core/i915_irq.c
+++ b/shared-core/i915_irq.c
@@ -114,8 +114,7 @@ static void i915_vblank_tasklet(struct drm_device *dev)
drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
struct list_head *list, *tmp, hits, *hit;
int nhits, nrects, slice[2], upper[2], lower[2], i, num_pages;
- unsigned counter[2] = { atomic_read(&dev->vbl_received),
- atomic_read(&dev->vbl_received2) };
+ unsigned counter[2];
struct drm_drawable_info *drw;
drm_i915_sarea_t *sarea_priv = dev_priv->sarea_priv;
u32 cpp = dev_priv->cpp, offsets[3];
@@ -127,6 +126,9 @@ static void i915_vblank_tasklet(struct drm_device *dev)
(cpp << 23) | (1 << 24);
RING_LOCALS;
+ counter[0] = drm_vblank_count(dev, 0);
+ counter[1] = drm_vblank_count(dev, 1);
+
DRM_DEBUG("\n");
INIT_LIST_HEAD(&hits);
@@ -144,13 +146,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 pipe = i915_get_pipe(dev, vbl_swap->plane);
+ int crtc = i915_get_pipe(dev, vbl_swap->plane);
- if ((counter[pipe] - vbl_swap->sequence) > (1<<23))
+ if ((counter[crtc] - vbl_swap->sequence) > (1<<23))
continue;
list_del(list);
dev_priv->swaps_pending--;
+ drm_vblank_put(dev, crtc);
DRM_SPINUNLOCK(&dev_priv->swaps_lock);
DRM_SPINLOCK(&dev->drw_lock);
@@ -301,6 +304,32 @@ static void i915_vblank_tasklet(struct drm_device *dev)
}
}
+u32 i915_get_vblank_counter(struct drm_device *dev, int crtc)
+{
+ 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;
+ u32 high1, high2, low, count;
+
+ /*
+ * High & low register fields aren't synchronized, so make sure
+ * we get a low value that's stable across two reads of the high
+ * register.
+ */
+ do {
+ high1 = ((I915_READ(high_frame) & PIPE_FRAME_HIGH_MASK) >>
+ PIPE_FRAME_HIGH_SHIFT);
+ low = ((I915_READ(low_frame) & PIPE_FRAME_LOW_MASK) >>
+ PIPE_FRAME_LOW_SHIFT);
+ high2 = ((I915_READ(high_frame) & PIPE_FRAME_HIGH_MASK) >>
+ PIPE_FRAME_HIGH_SHIFT);
+ } while (high1 != high2);
+
+ count = (high1 << 8) | low;
+
+ return count;
+}
+
irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS)
{
struct drm_device *dev = (struct drm_device *) arg;
@@ -312,7 +341,6 @@ irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS)
pipeb_stats = I915_READ(I915REG_PIPEBSTAT);
temp = I915_READ16(I915REG_INT_IDENTITY_R);
- temp &= (dev_priv->irq_enable_reg | USER_INT_FLAG);
#if 0
DRM_DEBUG("%s flag=%08x\n", __FUNCTION__, temp);
@@ -321,8 +349,10 @@ irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS)
return IRQ_NONE;
I915_WRITE16(I915REG_INT_IDENTITY_R, temp);
- (void) I915_READ16(I915REG_INT_IDENTITY_R);
- DRM_READMEMORYBARRIER();
+ (void) I915_READ16(I915REG_INT_IDENTITY_R); /* Flush posted write */
+
+ temp &= (dev_priv->irq_enable_reg | USER_INT_FLAG | VSYNC_PIPEA_FLAG |
+ VSYNC_PIPEB_FLAG);
dev_priv->sarea_priv->last_dispatch = READ_BREADCRUMB(dev_priv);
@@ -333,25 +363,16 @@ irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS)
#endif
}
- if (temp & (VSYNC_PIPEA_FLAG | VSYNC_PIPEB_FLAG)) {
- int vblank_pipe = dev_priv->vblank_pipe;
-
- if ((vblank_pipe &
- (DRM_I915_VBLANK_PIPE_A | DRM_I915_VBLANK_PIPE_B))
- == (DRM_I915_VBLANK_PIPE_A | DRM_I915_VBLANK_PIPE_B)) {
- if (temp & VSYNC_PIPEA_FLAG)
- atomic_inc(&dev->vbl_received);
- if (temp & VSYNC_PIPEB_FLAG)
- atomic_inc(&dev->vbl_received2);
- } else if (((temp & VSYNC_PIPEA_FLAG) &&
- (vblank_pipe & DRM_I915_VBLANK_PIPE_A)) ||
- ((temp & VSYNC_PIPEB_FLAG) &&
- (vblank_pipe & DRM_I915_VBLANK_PIPE_B)))
- atomic_inc(&dev->vbl_received);
-
- DRM_WAKEUP(&dev->vbl_queue);
- drm_vbl_send_signals(dev);
+ /*
+ * 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,
@@ -437,38 +458,6 @@ static int i915_wait_irq(struct drm_device * dev, int irq_nr)
return ret;
}
-static int i915_driver_vblank_do_wait(struct drm_device *dev,
- unsigned int *sequence,
- atomic_t *counter)
-{
- drm_i915_private_t *dev_priv = dev->dev_private;
- unsigned int cur_vblank;
- int ret = 0;
-
- if (!dev_priv) {
- DRM_ERROR("%s called with no initialization\n", __FUNCTION__);
- return -EINVAL;
- }
-
- DRM_WAIT_ON(ret, dev->vbl_queue, 3 * DRM_HZ,
- (((cur_vblank = atomic_read(counter))
- - *sequence) <= (1<<23)));
-
- *sequence = cur_vblank;
-
- return ret;
-}
-
-int i915_driver_vblank_wait(struct drm_device *dev, unsigned int *sequence)
-{
- return i915_driver_vblank_do_wait(dev, sequence, &dev->vbl_received);
-}
-
-int i915_driver_vblank_wait2(struct drm_device *dev, unsigned int *sequence)
-{
- return i915_driver_vblank_do_wait(dev, sequence, &dev->vbl_received2);
-}
-
/* Needs the lock as it touches the ring.
*/
int i915_irq_emit(struct drm_device *dev, void *data, struct drm_file *file_priv)
@@ -510,15 +499,53 @@ int i915_irq_wait(struct drm_device *dev, void *data,
return i915_wait_irq(dev, irqwait->irq_seq);
}
-static void i915_enable_interrupt (struct drm_device *dev)
+int i915_enable_vblank(struct drm_device *dev, int crtc)
{
drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
-
- dev_priv->irq_enable_reg = USER_INT_FLAG;
- if (dev_priv->vblank_pipe & DRM_I915_VBLANK_PIPE_A)
+
+ switch (crtc) {
+ case 0:
dev_priv->irq_enable_reg |= VSYNC_PIPEA_FLAG;
- if (dev_priv->vblank_pipe & DRM_I915_VBLANK_PIPE_B)
+ break;
+ case 1:
dev_priv->irq_enable_reg |= VSYNC_PIPEB_FLAG;
+ break;
+ default:
+ DRM_ERROR("tried to enable vblank on non-existent crtc %d\n",
+ crtc);
+ break;
+ }
+
+ I915_WRITE16(I915REG_INT_ENABLE_R, dev_priv->irq_enable_reg);
+
+ return 0;
+}
+
+void i915_disable_vblank(struct drm_device *dev, int crtc)
+{
+ drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
+
+ switch (crtc) {
+ case 0:
+ dev_priv->irq_enable_reg &= ~VSYNC_PIPEA_FLAG;
+ break;
+ case 1:
+ dev_priv->irq_enable_reg &= ~VSYNC_PIPEB_FLAG;
+ break;
+ default:
+ DRM_ERROR("tried to disable vblank on non-existent crtc %d\n",
+ crtc);
+ break;
+ }
+
+ I915_WRITE16(I915REG_INT_ENABLE_R, dev_priv->irq_enable_reg);
+}
+
+static void i915_enable_interrupt (struct drm_device *dev)
+{
+ drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
+
+ dev_priv->irq_enable_reg |= USER_INT_FLAG;
I915_WRITE16(I915REG_INT_ENABLE_R, dev_priv->irq_enable_reg);
dev_priv->irq_enabled = 1;
@@ -545,8 +572,6 @@ int i915_vblank_pipe_set(struct drm_device *dev, void *data,
dev_priv->vblank_pipe = pipe->pipe;
- i915_enable_interrupt (dev);
-
return 0;
}
@@ -584,6 +609,7 @@ int i915_vblank_swap(struct drm_device *dev, void *data,
unsigned int pipe, seqtype, curseq, plane;
unsigned long irqflags;
struct list_head *list;
+ int ret;
if (!dev_priv) {
DRM_ERROR("%s called with no initialization\n", __func__);
@@ -627,7 +653,8 @@ int i915_vblank_swap(struct drm_device *dev, void *data,
DRM_SPINUNLOCK_IRQRESTORE(&dev->drw_lock, irqflags);
- curseq = atomic_read(pipe ? &dev->vbl_received2 : &dev->vbl_received);
+ drm_update_vblank_count(dev, pipe);
+ curseq = drm_vblank_count(dev, pipe);
if (seqtype == _DRM_VBLANK_RELATIVE)
swap->sequence += curseq;
@@ -700,6 +727,12 @@ int i915_vblank_swap(struct drm_device *dev, void *data,
DRM_DEBUG("\n");
+ ret = drm_vblank_get(dev, pipe);
+ if (ret) {
+ drm_free(vbl_swap, sizeof(*vbl_swap), DRM_MEM_DRIVER);
+ return ret;
+ }
+
vbl_swap->drw_id = swap->drawable;
vbl_swap->plane = plane;
vbl_swap->sequence = swap->sequence;
@@ -729,9 +762,10 @@ void i915_driver_irq_preinstall(struct drm_device * dev)
I915_WRITE16(I915REG_INT_ENABLE_R, 0x0);
}
-void i915_driver_irq_postinstall(struct drm_device * dev)
+int i915_driver_irq_postinstall(struct drm_device * dev)
{
drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
+ int ret, num_pipes = 2;
DRM_SPININIT(&dev_priv->swaps_lock, "swap");
INIT_LIST_HEAD(&dev_priv->vbl_swaps.head);
@@ -739,6 +773,13 @@ void i915_driver_irq_postinstall(struct drm_device * dev)
DRM_SPININIT(&dev_priv->user_irq_lock, "userirq");
dev_priv->user_irq_refcount = 0;
+ dev_priv->irq_enable_reg = 0;
+
+ ret = drm_vblank_init(dev, num_pipes);
+ if (ret)
+ return ret;
+
+ dev->max_vblank_count = 0xffffff; /* only 24 bits of frame count */
i915_enable_interrupt(dev);
DRM_INIT_WAITQUEUE(&dev_priv->irq_queue);
@@ -748,6 +789,7 @@ void i915_driver_irq_postinstall(struct drm_device * dev)
*/
I915_WRITE(I915REG_INSTPM, ( 1 << 5) | ( 1 << 21));
+ return 0;
}
void i915_driver_irq_uninstall(struct drm_device * dev)