|
@@ -523,6 +523,62 @@ out:
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
|
+static int drm_queue_vblank_event(struct drm_device *dev, int pipe,
|
|
|
+ union drm_wait_vblank *vblwait,
|
|
|
+ struct drm_file *file_priv)
|
|
|
+{
|
|
|
+ struct drm_pending_vblank_event *e;
|
|
|
+ struct timeval now;
|
|
|
+ unsigned long flags;
|
|
|
+ unsigned int seq;
|
|
|
+
|
|
|
+ e = kzalloc(sizeof *e, GFP_KERNEL);
|
|
|
+ if (e == NULL)
|
|
|
+ return -ENOMEM;
|
|
|
+
|
|
|
+ e->pipe = pipe;
|
|
|
+ e->event.base.type = DRM_EVENT_VBLANK;
|
|
|
+ e->event.base.length = sizeof e->event;
|
|
|
+ e->event.user_data = vblwait->request.signal;
|
|
|
+ e->base.event = &e->event.base;
|
|
|
+ e->base.file_priv = file_priv;
|
|
|
+ e->base.destroy = (void (*) (struct drm_pending_event *)) kfree;
|
|
|
+
|
|
|
+ do_gettimeofday(&now);
|
|
|
+ spin_lock_irqsave(&dev->event_lock, flags);
|
|
|
+
|
|
|
+ if (file_priv->event_space < sizeof e->event) {
|
|
|
+ spin_unlock_irqrestore(&dev->event_lock, flags);
|
|
|
+ kfree(e);
|
|
|
+ return -ENOMEM;
|
|
|
+ }
|
|
|
+
|
|
|
+ file_priv->event_space -= sizeof e->event;
|
|
|
+ seq = drm_vblank_count(dev, pipe);
|
|
|
+ if ((vblwait->request.type & _DRM_VBLANK_NEXTONMISS) &&
|
|
|
+ (seq - vblwait->request.sequence) <= (1 << 23)) {
|
|
|
+ vblwait->request.sequence = seq + 1;
|
|
|
+ }
|
|
|
+
|
|
|
+ DRM_DEBUG("event on vblank count %d, current %d, crtc %d\n",
|
|
|
+ vblwait->request.sequence, seq, pipe);
|
|
|
+
|
|
|
+ e->event.sequence = vblwait->request.sequence;
|
|
|
+ if ((seq - vblwait->request.sequence) <= (1 << 23)) {
|
|
|
+ e->event.tv_sec = now.tv_sec;
|
|
|
+ e->event.tv_usec = now.tv_usec;
|
|
|
+ drm_vblank_put(dev, e->pipe);
|
|
|
+ list_add_tail(&e->base.link, &e->base.file_priv->event_list);
|
|
|
+ wake_up_interruptible(&e->base.file_priv->event_wait);
|
|
|
+ } else {
|
|
|
+ list_add_tail(&e->base.link, &dev->vblank_event_list);
|
|
|
+ }
|
|
|
+
|
|
|
+ spin_unlock_irqrestore(&dev->event_lock, flags);
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
/**
|
|
|
* Wait for VBLANK.
|
|
|
*
|
|
@@ -582,6 +638,9 @@ int drm_wait_vblank(struct drm_device *dev, void *data,
|
|
|
goto done;
|
|
|
}
|
|
|
|
|
|
+ if (flags & _DRM_VBLANK_EVENT)
|
|
|
+ return drm_queue_vblank_event(dev, crtc, vblwait, file_priv);
|
|
|
+
|
|
|
if ((flags & _DRM_VBLANK_NEXTONMISS) &&
|
|
|
(seq - vblwait->request.sequence) <= (1<<23)) {
|
|
|
vblwait->request.sequence = seq + 1;
|
|
@@ -614,6 +673,38 @@ done:
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
|
+void drm_handle_vblank_events(struct drm_device *dev, int crtc)
|
|
|
+{
|
|
|
+ struct drm_pending_vblank_event *e, *t;
|
|
|
+ struct timeval now;
|
|
|
+ unsigned long flags;
|
|
|
+ unsigned int seq;
|
|
|
+
|
|
|
+ do_gettimeofday(&now);
|
|
|
+ seq = drm_vblank_count(dev, crtc);
|
|
|
+
|
|
|
+ spin_lock_irqsave(&dev->event_lock, flags);
|
|
|
+
|
|
|
+ list_for_each_entry_safe(e, t, &dev->vblank_event_list, base.link) {
|
|
|
+ if (e->pipe != crtc)
|
|
|
+ continue;
|
|
|
+ if ((seq - e->event.sequence) > (1<<23))
|
|
|
+ continue;
|
|
|
+
|
|
|
+ DRM_DEBUG("vblank event on %d, current %d\n",
|
|
|
+ e->event.sequence, seq);
|
|
|
+
|
|
|
+ e->event.sequence = seq;
|
|
|
+ e->event.tv_sec = now.tv_sec;
|
|
|
+ e->event.tv_usec = now.tv_usec;
|
|
|
+ drm_vblank_put(dev, e->pipe);
|
|
|
+ list_move_tail(&e->base.link, &e->base.file_priv->event_list);
|
|
|
+ wake_up_interruptible(&e->base.file_priv->event_wait);
|
|
|
+ }
|
|
|
+
|
|
|
+ spin_unlock_irqrestore(&dev->event_lock, flags);
|
|
|
+}
|
|
|
+
|
|
|
/**
|
|
|
* drm_handle_vblank - handle a vblank event
|
|
|
* @dev: DRM device
|
|
@@ -624,7 +715,11 @@ done:
|
|
|
*/
|
|
|
void drm_handle_vblank(struct drm_device *dev, int crtc)
|
|
|
{
|
|
|
+ if (!dev->num_crtcs)
|
|
|
+ return;
|
|
|
+
|
|
|
atomic_inc(&dev->_vblank_count[crtc]);
|
|
|
DRM_WAKEUP(&dev->vbl_queue[crtc]);
|
|
|
+ drm_handle_vblank_events(dev, crtc);
|
|
|
}
|
|
|
EXPORT_SYMBOL(drm_handle_vblank);
|