summaryrefslogtreecommitdiff
path: root/drivers/gpu/drm/drm_vblank.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpu/drm/drm_vblank.c')
-rw-r--r--drivers/gpu/drm/drm_vblank.c101
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;
}
+