diff --git a/linux-core/drm_irq.c b/linux-core/drm_irq.c index abedbe7..55a2544 100644 --- a/linux-core/drm_irq.c +++ b/linux-core/drm_irq.c @@ -71,16 +71,29 @@ int drm_irq_by_busid(struct drm_device *dev, void *data, return 0; } +/* + * At load time, disabling the vblank interrupt won't be allowed since + * old clients may not call the modeset ioctl and therefore misbehave. + * Once the modeset ioctl *has* been called though, we can safely + * disable them when unused. + */ +static int vblank_disable_allowed; + static void vblank_disable_fn(unsigned long arg) { struct drm_device *dev = (struct drm_device *)arg; unsigned long irqflags; int i; + if (!vblank_disable_allowed) + return; + for (i = 0; i < dev->num_crtcs; i++) { spin_lock_irqsave(&dev->vbl_lock, irqflags); if (atomic_read(&dev->vblank_refcount[i]) == 0 && dev->vblank_enabled[i]) { + DRM_DEBUG("disabling vblank interrupts for crtc %d\n", + i); dev->driver->disable_vblank(dev, i); dev->vblank_enabled[i] = 0; } @@ -175,6 +188,8 @@ int drm_vblank_init(struct drm_device *dev, int num_crtcs) atomic_set(&dev->vblank_refcount[i], 0); } + vblank_disable_allowed = 0; + return 0; err: @@ -344,10 +359,15 @@ EXPORT_SYMBOL(drm_vblank_count); * (specified by @crtc). Deal with wraparound, if it occurred, and * update the last read value so we can deal with wraparound on the next * call if necessary. + * + * Only necessary when going from off->on, to account for frames we + * didn't get an interrupt for. + * + * Note: caller must hold dev->vbl_lock since this reads & writes + * device vblank fields. */ void drm_update_vblank_count(struct drm_device *dev, int crtc) { - unsigned long irqflags; u32 cur_vblank, diff; if (dev->vblank_suspend[crtc]) @@ -361,7 +381,6 @@ void drm_update_vblank_count(struct drm_device *dev, int crtc) * a long time. */ cur_vblank = dev->driver->get_vblank_counter(dev, crtc); - spin_lock_irqsave(&dev->vbl_lock, irqflags); if (cur_vblank < dev->last_vblank[crtc]) { if (cur_vblank == dev->last_vblank[crtc] - 1) { diff = 0; @@ -377,11 +396,12 @@ void drm_update_vblank_count(struct drm_device *dev, int crtc) diff = cur_vblank - dev->last_vblank[crtc]; } dev->last_vblank[crtc] = cur_vblank; - spin_unlock_irqrestore(&dev->vbl_lock, irqflags); + + DRM_DEBUG("enabling vblank interrupts on crtc %d, missed %d\n", + crtc, diff); atomic_add(diff, &dev->_vblank_count[crtc]); } -EXPORT_SYMBOL(drm_update_vblank_count); /** * drm_vblank_get - get a reference count on vblank events @@ -389,9 +409,7 @@ EXPORT_SYMBOL(drm_update_vblank_count); * @crtc: which CRTC to own * * Acquire a reference count on vblank events to avoid having them disabled - * while in use. Note callers will probably want to update the master counter - * using drm_update_vblank_count() above before calling this routine so that - * wakeups occur on the right vblank event. + * while in use. * * RETURNS * Zero on success, nonzero on failure. @@ -408,8 +426,10 @@ int drm_vblank_get(struct drm_device *dev, int crtc) ret = dev->driver->enable_vblank(dev, crtc); if (ret) atomic_dec(&dev->vblank_refcount[crtc]); - else + else { dev->vblank_enabled[crtc] = 1; + drm_update_vblank_count(dev, crtc); + } } spin_unlock_irqrestore(&dev->vbl_lock, irqflags); @@ -457,6 +477,7 @@ int drm_modeset_ctl(struct drm_device *dev, void *data, dev->vblank_premodeset[crtc] = dev->driver->get_vblank_counter(dev, crtc); dev->vblank_suspend[crtc] = 1; + vblank_disable_allowed = 1; break; case _DRM_POST_MODESET: if (dev->vblank_suspend[crtc]) { @@ -475,6 +496,7 @@ int drm_modeset_ctl(struct drm_device *dev, void *data, } } dev->vblank_suspend[crtc] = 0; + vblank_disable_allowed = 1; break; default: ret = -EINVAL; @@ -528,7 +550,6 @@ int drm_wait_vblank(struct drm_device *dev, void *data, if (crtc >= dev->num_crtcs) return -EINVAL; - drm_update_vblank_count(dev, crtc); seq = drm_vblank_count(dev, crtc); switch (vblwait->request.type & _DRM_VBLANK_TYPES_MASK) { @@ -680,7 +701,7 @@ static void drm_vbl_send_signals(struct drm_device * dev, int crtc) */ void drm_handle_vblank(struct drm_device *dev, int crtc) { - drm_update_vblank_count(dev, crtc); + atomic_inc(&dev->_vblank_count[crtc]); DRM_WAKEUP(&dev->vbl_queue[crtc]); drm_vbl_send_signals(dev, crtc); } diff --git a/shared-core/i915_irq.c b/shared-core/i915_irq.c index d7fa47d..d0e3be2 100644 --- a/shared-core/i915_irq.c +++ b/shared-core/i915_irq.c @@ -361,28 +361,7 @@ static void i915_vblank_tasklet(struct drm_device *dev) drm_free(swap_hit, sizeof(*swap_hit), DRM_MEM_DRIVER); } } -#if 0 -static int i915_in_vblank(struct drm_device *dev, int pipe) -{ - drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; - unsigned long pipedsl, vblank, vtotal; - unsigned long vbl_start, vbl_end, cur_line; - - pipedsl = pipe ? PIPEBDSL : PIPEADSL; - vblank = pipe ? VBLANK_B : VBLANK_A; - vtotal = pipe ? VTOTAL_B : VTOTAL_A; - vbl_start = I915_READ(vblank) & VBLANK_START_MASK; - vbl_end = (I915_READ(vblank) >> VBLANK_END_SHIFT) & VBLANK_END_MASK; - - cur_line = I915_READ(pipedsl); - - if (cur_line >= vbl_start) - return 1; - - return 0; -} -#endif u32 i915_get_vblank_counter(struct drm_device *dev, int plane) { drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; @@ -416,22 +395,6 @@ u32 i915_get_vblank_counter(struct drm_device *dev, int plane) count = (high1 << 8) | low; - /* - * If we're in the middle of the vblank period, the - * above regs won't have been updated yet, so return - * an incremented count to stay accurate - */ -#if 0 - if (i915_in_vblank(dev, pipe)) - count++; -#endif - /* count may be reset by other driver(e.g. 2D driver), - we have no way to know if it is wrapped or resetted - when count is zero. do a rough guess. - */ - if (count == 0 && dev->last_vblank[pipe] < dev->max_vblank_count/2) - dev->last_vblank[pipe] = 0; - return count; } @@ -444,18 +407,8 @@ irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS) int vblank = 0; iir = I915_READ(IIR); -#if 0 - DRM_DEBUG("flag=%08x\n", iir); -#endif - if (iir == 0) { - DRM_DEBUG ("iir 0x%08x im 0x%08x ie 0x%08x pipea 0x%08x pipeb 0x%08x\n", - iir, - I915_READ(IMR), - I915_READ(IER), - I915_READ(PIPEASTAT), - I915_READ(PIPEBSTAT)); + if (iir == 0) return IRQ_NONE; - } /* * Clear the PIPE(A|B)STAT regs before the IIR otherwise @@ -742,13 +695,6 @@ int i915_vblank_pipe_set(struct drm_device *dev, void *data, return -EINVAL; } - if (pipe->pipe & ~(DRM_I915_VBLANK_PIPE_A|DRM_I915_VBLANK_PIPE_B)) { - DRM_ERROR("called with invalid pipe 0x%x\n", pipe->pipe); - return -EINVAL; - } - - dev_priv->vblank_pipe = pipe->pipe; - return 0; } @@ -764,13 +710,7 @@ int i915_vblank_pipe_get(struct drm_device *dev, void *data, return -EINVAL; } - flag = I915_READ(IER); - - pipe->pipe = 0; - if (flag & I915_DISPLAY_PIPE_A_EVENT_INTERRUPT) - pipe->pipe |= DRM_I915_VBLANK_PIPE_A; - if (flag & I915_DISPLAY_PIPE_B_EVENT_INTERRUPT) - pipe->pipe |= DRM_I915_VBLANK_PIPE_B; + pipe->pipe = DRM_I915_VBLANK_PIPE_A | DRM_I915_VBLANK_PIPE_B; return 0; } @@ -831,7 +771,6 @@ int i915_vblank_swap(struct drm_device *dev, void *data, DRM_SPINUNLOCK_IRQRESTORE(&dev->drw_lock, irqflags); - drm_update_vblank_count(dev, pipe); curseq = drm_vblank_count(dev, pipe); if (seqtype == _DRM_VBLANK_RELATIVE) @@ -957,6 +896,7 @@ int i915_driver_irq_postinstall(struct drm_device * dev) if (ret) return ret; + dev_priv->vblank_pipe = DRM_I915_VBLANK_PIPE_A | DRM_I915_VBLANK_PIPE_B; dev->max_vblank_count = 0xffffff; /* only 24 bits of frame count */ i915_enable_interrupt(dev); @@ -978,6 +918,8 @@ void i915_driver_irq_uninstall(struct drm_device * dev) if (!dev_priv) return; + dev_priv->vblank_pipe = 0; + dev_priv->irq_enabled = 0; I915_WRITE(HWSTAM, 0xffffffff); I915_WRITE(IMR, 0xffffffff);