diff options
Diffstat (limited to 'drivers/gpu/drm/drm_irq.c')
-rw-r--r-- | drivers/gpu/drm/drm_irq.c | 67 |
1 files changed, 51 insertions, 16 deletions
diff --git a/drivers/gpu/drm/drm_irq.c b/drivers/gpu/drm/drm_irq.c index 10574a0c3a55..af9662e58272 100644 --- a/drivers/gpu/drm/drm_irq.c +++ b/drivers/gpu/drm/drm_irq.c @@ -131,12 +131,11 @@ static void drm_update_vblank_count(struct drm_device *dev, int crtc) /* Reinitialize corresponding vblank timestamp if high-precision query * available. Skip this step if query unsupported or failed. Will - * reinitialize delayed at next vblank interrupt in that case. + * reinitialize delayed at next vblank interrupt in that case and + * assign 0 for now, to mark the vblanktimestamp as invalid. */ - if (rc) { - tslot = atomic_read(&vblank->count) + diff; - vblanktimestamp(dev, crtc, tslot) = t_vblank; - } + tslot = atomic_read(&vblank->count) + diff; + vblanktimestamp(dev, crtc, tslot) = rc ? t_vblank : (struct timeval) {0, 0}; smp_mb__before_atomic(); atomic_add(diff, &vblank->count); @@ -276,7 +275,6 @@ static void vblank_disable_fn(unsigned long arg) void drm_vblank_cleanup(struct drm_device *dev) { int crtc; - unsigned long irqflags; /* Bail if the driver didn't call drm_vblank_init() */ if (dev->num_crtcs == 0) @@ -285,11 +283,10 @@ void drm_vblank_cleanup(struct drm_device *dev) for (crtc = 0; crtc < dev->num_crtcs; crtc++) { struct drm_vblank_crtc *vblank = &dev->vblank[crtc]; - del_timer_sync(&vblank->disable_timer); + WARN_ON(vblank->enabled && + drm_core_check_feature(dev, DRIVER_MODESET)); - spin_lock_irqsave(&dev->vbl_lock, irqflags); - vblank_disable_and_save(dev, crtc); - spin_unlock_irqrestore(&dev->vbl_lock, irqflags); + del_timer_sync(&vblank->disable_timer); } kfree(dev->vblank); @@ -475,17 +472,23 @@ int drm_irq_uninstall(struct drm_device *dev) dev->irq_enabled = false; /* - * Wake up any waiters so they don't hang. + * Wake up any waiters so they don't hang. This is just to paper over + * isssues for UMS drivers which aren't in full control of their + * vblank/irq handling. KMS drivers must ensure that vblanks are all + * disabled when uninstalling the irq handler. */ if (dev->num_crtcs) { spin_lock_irqsave(&dev->vbl_lock, irqflags); for (i = 0; i < dev->num_crtcs; i++) { struct drm_vblank_crtc *vblank = &dev->vblank[i]; + if (!vblank->enabled) + continue; + + WARN_ON(drm_core_check_feature(dev, DRIVER_MODESET)); + + vblank_disable_and_save(dev, i); wake_up(&vblank->queue); - vblank->enabled = false; - vblank->last = - dev->driver->get_vblank_counter(dev, i); } spin_unlock_irqrestore(&dev->vbl_lock, irqflags); } @@ -1052,7 +1055,7 @@ EXPORT_SYMBOL(drm_vblank_get); * Acquire a reference count on vblank events to avoid having them disabled * while in use. * - * This is the native kms version of drm_vblank_off(). + * This is the native kms version of drm_vblank_get(). * * Returns: * Zero on success, nonzero on failure. @@ -1233,6 +1236,38 @@ void drm_crtc_vblank_off(struct drm_crtc *crtc) EXPORT_SYMBOL(drm_crtc_vblank_off); /** + * drm_crtc_vblank_reset - reset vblank state to off on a CRTC + * @crtc: CRTC in question + * + * Drivers can use this function to reset the vblank state to off at load time. + * Drivers should use this together with the drm_crtc_vblank_off() and + * drm_crtc_vblank_on() functions. The difference compared to + * drm_crtc_vblank_off() is that this function doesn't save the vblank counter + * and hence doesn't need to call any driver hooks. + */ +void drm_crtc_vblank_reset(struct drm_crtc *drm_crtc) +{ + struct drm_device *dev = drm_crtc->dev; + unsigned long irqflags; + int crtc = drm_crtc_index(drm_crtc); + struct drm_vblank_crtc *vblank = &dev->vblank[crtc]; + + spin_lock_irqsave(&dev->vbl_lock, irqflags); + /* + * Prevent subsequent drm_vblank_get() from enabling the vblank + * interrupt by bumping the refcount. + */ + if (!vblank->inmodeset) { + atomic_inc(&vblank->refcount); + vblank->inmodeset = 1; + } + spin_unlock_irqrestore(&dev->vbl_lock, irqflags); + + WARN_ON(!list_empty(&dev->vblank_event_list)); +} +EXPORT_SYMBOL(drm_crtc_vblank_reset); + +/** * drm_vblank_on - enable vblank events on a CRTC * @dev: DRM device * @crtc: CRTC in question @@ -1653,7 +1688,7 @@ bool drm_handle_vblank(struct drm_device *dev, int crtc) struct timeval tvblank; unsigned long irqflags; - if (!dev->num_crtcs) + if (WARN_ON_ONCE(!dev->num_crtcs)) return false; if (WARN_ON(crtc >= dev->num_crtcs)) |