diff options
Diffstat (limited to 'drivers/gpu/drm/drm_vblank.c')
-rw-r--r-- | drivers/gpu/drm/drm_vblank.c | 101 |
1 files changed, 53 insertions, 48 deletions
diff --git a/drivers/gpu/drm/drm_vblank.c b/drivers/gpu/drm/drm_vblank.c index 42a84eb4cc8c..b18e1efbbae1 100644 --- a/drivers/gpu/drm/drm_vblank.c +++ b/drivers/gpu/drm/drm_vblank.c @@ -25,6 +25,7 @@ */ #include <linux/export.h> +#include <linux/kthread.h> #include <linux/moduleparam.h> #include <drm/drm_crtc.h> @@ -363,7 +364,7 @@ static void drm_update_vblank_count(struct drm_device *dev, unsigned int pipe, store_vblank(dev, pipe, diff, t_vblank, cur_vblank); } -static u64 drm_vblank_count(struct drm_device *dev, unsigned int pipe) +u64 drm_vblank_count(struct drm_device *dev, unsigned int pipe) { struct drm_vblank_crtc *vblank = &dev->vblank[pipe]; u64 count; @@ -492,16 +493,13 @@ static void vblank_disable_fn(struct timer_list *t) static void drm_vblank_init_release(struct drm_device *dev, void *ptr) { - unsigned int pipe; - - for (pipe = 0; pipe < dev->num_crtcs; pipe++) { - struct drm_vblank_crtc *vblank = &dev->vblank[pipe]; + struct drm_vblank_crtc *vblank = ptr; - drm_WARN_ON(dev, READ_ONCE(vblank->enabled) && - drm_core_check_feature(dev, DRIVER_MODESET)); + drm_WARN_ON(dev, READ_ONCE(vblank->enabled) && + drm_core_check_feature(dev, DRIVER_MODESET)); - del_timer_sync(&vblank->disable_timer); - } + drm_vblank_destroy_worker(vblank); + del_timer_sync(&vblank->disable_timer); } /** @@ -511,7 +509,7 @@ static void drm_vblank_init_release(struct drm_device *dev, void *ptr) * * This function initializes vblank support for @num_crtcs display pipelines. * Cleanup is handled automatically through a cleanup function added with - * drmm_add_action(). + * drmm_add_action_or_reset(). * * Returns: * Zero on success or a negative error code on failure. @@ -530,10 +528,6 @@ int drm_vblank_init(struct drm_device *dev, unsigned int num_crtcs) dev->num_crtcs = num_crtcs; - ret = drmm_add_action(dev, drm_vblank_init_release, NULL); - if (ret) - return ret; - for (i = 0; i < num_crtcs; i++) { struct drm_vblank_crtc *vblank = &dev->vblank[i]; @@ -542,6 +536,15 @@ int drm_vblank_init(struct drm_device *dev, unsigned int num_crtcs) init_waitqueue_head(&vblank->queue); timer_setup(&vblank->disable_timer, vblank_disable_fn, 0); seqlock_init(&vblank->seqlock); + + ret = drmm_add_action_or_reset(dev, drm_vblank_init_release, + vblank); + if (ret) + return ret; + + ret = drm_vblank_worker_init(vblank); + if (ret) + return ret; } return 0; @@ -1138,7 +1141,7 @@ static int drm_vblank_enable(struct drm_device *dev, unsigned int pipe) return ret; } -static int drm_vblank_get(struct drm_device *dev, unsigned int pipe) +int drm_vblank_get(struct drm_device *dev, unsigned int pipe) { struct drm_vblank_crtc *vblank = &dev->vblank[pipe]; unsigned long irqflags; @@ -1181,7 +1184,7 @@ int drm_crtc_vblank_get(struct drm_crtc *crtc) } EXPORT_SYMBOL(drm_crtc_vblank_get); -static void drm_vblank_put(struct drm_device *dev, unsigned int pipe) +void drm_vblank_put(struct drm_device *dev, unsigned int pipe) { struct drm_vblank_crtc *vblank = &dev->vblank[pipe]; @@ -1284,15 +1287,17 @@ void drm_crtc_vblank_off(struct drm_crtc *crtc) unsigned int pipe = drm_crtc_index(crtc); struct drm_vblank_crtc *vblank = &dev->vblank[pipe]; struct drm_pending_vblank_event *e, *t; - ktime_t now; - unsigned long irqflags; u64 seq; if (drm_WARN_ON(dev, pipe >= dev->num_crtcs)) return; - spin_lock_irqsave(&dev->event_lock, irqflags); + /* + * Grab event_lock early to prevent vblank work from being scheduled + * while we're in the middle of shutting down vblank interrupts + */ + spin_lock_irq(&dev->event_lock); spin_lock(&dev->vbl_lock); drm_dbg_vbl(dev, "crtc %d, vblank enabled %d, inmodeset %d\n", @@ -1328,11 +1333,18 @@ void drm_crtc_vblank_off(struct drm_crtc *crtc) drm_vblank_put(dev, pipe); send_vblank_event(dev, e, seq, now); } - spin_unlock_irqrestore(&dev->event_lock, irqflags); + + /* Cancel any leftover pending vblank work */ + drm_vblank_cancel_pending_works(vblank); + + spin_unlock_irq(&dev->event_lock); /* Will be reset by the modeset helpers when re-enabling the crtc by * calling drm_calc_timestamping_constants(). */ vblank->hwmode.crtc_clock = 0; + + /* Wait for any vblank work that's still executing to finish */ + drm_vblank_flush_worker(vblank); } EXPORT_SYMBOL(drm_crtc_vblank_off); @@ -1351,11 +1363,10 @@ EXPORT_SYMBOL(drm_crtc_vblank_off); void drm_crtc_vblank_reset(struct drm_crtc *crtc) { struct drm_device *dev = crtc->dev; - unsigned long irqflags; unsigned int pipe = drm_crtc_index(crtc); struct drm_vblank_crtc *vblank = &dev->vblank[pipe]; - spin_lock_irqsave(&dev->vbl_lock, irqflags); + spin_lock_irq(&dev->vbl_lock); /* * Prevent subsequent drm_vblank_get() from enabling the vblank * interrupt by bumping the refcount. @@ -1364,9 +1375,10 @@ void drm_crtc_vblank_reset(struct drm_crtc *crtc) atomic_inc(&vblank->refcount); vblank->inmodeset = 1; } - spin_unlock_irqrestore(&dev->vbl_lock, irqflags); + spin_unlock_irq(&dev->vbl_lock); drm_WARN_ON(dev, !list_empty(&dev->vblank_event_list)); + drm_WARN_ON(dev, !list_empty(&vblank->pending_work)); } EXPORT_SYMBOL(drm_crtc_vblank_reset); @@ -1416,12 +1428,11 @@ void drm_crtc_vblank_on(struct drm_crtc *crtc) struct drm_device *dev = crtc->dev; unsigned int pipe = drm_crtc_index(crtc); struct drm_vblank_crtc *vblank = &dev->vblank[pipe]; - unsigned long irqflags; if (drm_WARN_ON(dev, pipe >= dev->num_crtcs)) return; - spin_lock_irqsave(&dev->vbl_lock, irqflags); + spin_lock_irq(&dev->vbl_lock); drm_dbg_vbl(dev, "crtc %d, vblank enabled %d, inmodeset %d\n", pipe, vblank->enabled, vblank->inmodeset); @@ -1439,7 +1450,7 @@ void drm_crtc_vblank_on(struct drm_crtc *crtc) */ if (atomic_read(&vblank->refcount) != 0 || drm_vblank_offdelay == 0) drm_WARN_ON(dev, drm_vblank_enable(dev, pipe)); - spin_unlock_irqrestore(&dev->vbl_lock, irqflags); + spin_unlock_irq(&dev->vbl_lock); } EXPORT_SYMBOL(drm_crtc_vblank_on); @@ -1540,7 +1551,6 @@ static void drm_legacy_vblank_post_modeset(struct drm_device *dev, unsigned int pipe) { struct drm_vblank_crtc *vblank = &dev->vblank[pipe]; - unsigned long irqflags; /* vblank is not initialized (IRQ not installed ?), or has been freed */ if (!drm_dev_has_vblank(dev)) @@ -1550,9 +1560,9 @@ static void drm_legacy_vblank_post_modeset(struct drm_device *dev, return; if (vblank->inmodeset) { - spin_lock_irqsave(&dev->vbl_lock, irqflags); + spin_lock_irq(&dev->vbl_lock); drm_reset_vblank_timestamp(dev, pipe); - spin_unlock_irqrestore(&dev->vbl_lock, irqflags); + spin_unlock_irq(&dev->vbl_lock); if (vblank->inmodeset & 0x2) drm_vblank_put(dev, pipe); @@ -1593,11 +1603,6 @@ int drm_legacy_modeset_ctl_ioctl(struct drm_device *dev, void *data, return 0; } -static inline bool vblank_passed(u64 seq, u64 ref) -{ - return (seq - ref) <= (1 << 23); -} - static int drm_queue_vblank_event(struct drm_device *dev, unsigned int pipe, u64 req_seq, union drm_wait_vblank *vblwait, @@ -1606,7 +1611,6 @@ static int drm_queue_vblank_event(struct drm_device *dev, unsigned int pipe, struct drm_vblank_crtc *vblank = &dev->vblank[pipe]; struct drm_pending_vblank_event *e; ktime_t now; - unsigned long flags; u64 seq; int ret; @@ -1628,7 +1632,7 @@ static int drm_queue_vblank_event(struct drm_device *dev, unsigned int pipe, e->event.vbl.crtc_id = crtc->base.id; } - spin_lock_irqsave(&dev->event_lock, flags); + spin_lock_irq(&dev->event_lock); /* * drm_crtc_vblank_off() might have been called after we called @@ -1655,7 +1659,7 @@ static int drm_queue_vblank_event(struct drm_device *dev, unsigned int pipe, trace_drm_vblank_event_queued(file_priv, pipe, req_seq); e->sequence = req_seq; - if (vblank_passed(seq, req_seq)) { + if (drm_vblank_passed(seq, req_seq)) { drm_vblank_put(dev, pipe); send_vblank_event(dev, e, seq, now); vblwait->reply.sequence = seq; @@ -1665,12 +1669,12 @@ static int drm_queue_vblank_event(struct drm_device *dev, unsigned int pipe, vblwait->reply.sequence = req_seq; } - spin_unlock_irqrestore(&dev->event_lock, flags); + spin_unlock_irq(&dev->event_lock); return 0; err_unlock: - spin_unlock_irqrestore(&dev->event_lock, flags); + spin_unlock_irq(&dev->event_lock); kfree(e); err_put: drm_vblank_put(dev, pipe); @@ -1810,7 +1814,7 @@ int drm_wait_vblank_ioctl(struct drm_device *dev, void *data, } if ((flags & _DRM_VBLANK_NEXTONMISS) && - vblank_passed(seq, req_seq)) { + drm_vblank_passed(seq, req_seq)) { req_seq = seq + 1; vblwait->request.type &= ~_DRM_VBLANK_NEXTONMISS; vblwait->request.sequence = req_seq; @@ -1829,7 +1833,7 @@ int drm_wait_vblank_ioctl(struct drm_device *dev, void *data, drm_dbg_core(dev, "waiting on vblank count %llu, crtc %u\n", req_seq, pipe); wait = wait_event_interruptible_timeout(vblank->queue, - vblank_passed(drm_vblank_count(dev, pipe), req_seq) || + drm_vblank_passed(drm_vblank_count(dev, pipe), req_seq) || !READ_ONCE(vblank->enabled), msecs_to_jiffies(3000)); @@ -1878,7 +1882,7 @@ static void drm_handle_vblank_events(struct drm_device *dev, unsigned int pipe) list_for_each_entry_safe(e, t, &dev->vblank_event_list, base.link) { if (e->pipe != pipe) continue; - if (!vblank_passed(seq, e->sequence)) + if (!drm_vblank_passed(seq, e->sequence)) continue; drm_dbg_core(dev, "vblank event on %llu, current %llu\n", @@ -1948,6 +1952,7 @@ bool drm_handle_vblank(struct drm_device *dev, unsigned int pipe) !atomic_read(&vblank->refcount)); drm_handle_vblank_events(dev, pipe); + drm_handle_vblank_works(vblank); spin_unlock_irqrestore(&dev->event_lock, irqflags); @@ -2061,7 +2066,6 @@ int drm_crtc_queue_sequence_ioctl(struct drm_device *dev, void *data, u64 seq; u64 req_seq; int ret; - unsigned long spin_flags; if (!drm_core_check_feature(dev, DRIVER_MODESET)) return -EOPNOTSUPP; @@ -2101,7 +2105,7 @@ int drm_crtc_queue_sequence_ioctl(struct drm_device *dev, void *data, if (flags & DRM_CRTC_SEQUENCE_RELATIVE) req_seq += seq; - if ((flags & DRM_CRTC_SEQUENCE_NEXT_ON_MISS) && vblank_passed(seq, req_seq)) + if ((flags & DRM_CRTC_SEQUENCE_NEXT_ON_MISS) && drm_vblank_passed(seq, req_seq)) req_seq = seq + 1; e->pipe = pipe; @@ -2109,7 +2113,7 @@ int drm_crtc_queue_sequence_ioctl(struct drm_device *dev, void *data, e->event.base.length = sizeof(e->event.seq); e->event.seq.user_data = queue_seq->user_data; - spin_lock_irqsave(&dev->event_lock, spin_flags); + spin_lock_irq(&dev->event_lock); /* * drm_crtc_vblank_off() might have been called after we called @@ -2130,7 +2134,7 @@ int drm_crtc_queue_sequence_ioctl(struct drm_device *dev, void *data, e->sequence = req_seq; - if (vblank_passed(seq, req_seq)) { + if (drm_vblank_passed(seq, req_seq)) { drm_crtc_vblank_put(crtc); send_vblank_event(dev, e, seq, now); queue_seq->sequence = seq; @@ -2140,13 +2144,14 @@ int drm_crtc_queue_sequence_ioctl(struct drm_device *dev, void *data, queue_seq->sequence = req_seq; } - spin_unlock_irqrestore(&dev->event_lock, spin_flags); + spin_unlock_irq(&dev->event_lock); return 0; err_unlock: - spin_unlock_irqrestore(&dev->event_lock, spin_flags); + spin_unlock_irq(&dev->event_lock); drm_crtc_vblank_put(crtc); err_free: kfree(e); return ret; } + |