diff options
Diffstat (limited to 'shared-core/i915_irq.c')
-rw-r--r-- | shared-core/i915_irq.c | 172 |
1 files changed, 107 insertions, 65 deletions
diff --git a/shared-core/i915_irq.c b/shared-core/i915_irq.c index 4a968002..9b46b127 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("flag=%08x\n", 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, @@ -431,38 +452,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("called with no initialization\n"); - 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, @@ -505,15 +494,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; @@ -539,8 +566,6 @@ int i915_vblank_pipe_set(struct drm_device *dev, void *data, dev_priv->vblank_pipe = pipe->pipe; - i915_enable_interrupt (dev); - return 0; } @@ -578,6 +603,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__); @@ -621,7 +647,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; @@ -694,6 +721,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; @@ -723,9 +756,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); @@ -733,6 +767,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); @@ -742,6 +783,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) |