@@ -578,7 +578,7 @@ static const struct drm_ioctl_desc drm_ioctls[] = {
DRM_IOCTL_DEF(DRM_IOCTL_SG_ALLOC, drm_legacy_sg_alloc, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
DRM_IOCTL_DEF(DRM_IOCTL_SG_FREE, drm_legacy_sg_free, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
- DRM_IOCTL_DEF(DRM_IOCTL_WAIT_VBLANK, drm_wait_vblank, DRM_UNLOCKED),
+ DRM_IOCTL_DEF(DRM_IOCTL_WAIT_VBLANK, drm_wait_vblank_ioctl, DRM_UNLOCKED),
DRM_IOCTL_DEF(DRM_IOCTL_MODESET_CTL, drm_modeset_ctl, 0),
@@ -882,7 +882,8 @@ EXPORT_SYMBOL(drm_vblank_count_and_time);
static void send_vblank_event(struct drm_device *dev,
struct drm_pending_vblank_event *e,
- unsigned long seq, struct timeval *now)
+ unsigned long seq, struct timeval *now,
+ bool premature)
{
WARN_ON_SMP(!spin_is_locked(&dev->event_lock));
e->event.sequence = seq;
@@ -918,7 +919,7 @@ void drm_send_vblank_event(struct drm_device *dev, int crtc,
now = get_drm_timestamp();
}
e->pipe = crtc;
- send_vblank_event(dev, e, seq, &now);
+ send_vblank_event(dev, e, seq, &now, false);
}
EXPORT_SYMBOL(drm_send_vblank_event);
@@ -1159,7 +1160,7 @@ void drm_vblank_off(struct drm_device *dev, int crtc)
e->event.sequence, seq);
list_del(&e->base.link);
drm_vblank_put(dev, e->pipe);
- send_vblank_event(dev, e, seq, &now);
+ e->callback(dev, e, seq, &now, true);
}
spin_unlock_irqrestore(&dev->event_lock, irqflags);
}
@@ -1373,7 +1374,8 @@ int drm_modeset_ctl(struct drm_device *dev, void *data,
static int drm_queue_vblank_event(struct drm_device *dev, int pipe,
union drm_wait_vblank *vblwait,
- struct drm_file *file_priv)
+ struct drm_file *file_priv,
+ drm_vblank_callback_t callback)
{
struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
struct drm_pending_vblank_event *e;
@@ -1396,6 +1398,7 @@ static int drm_queue_vblank_event(struct drm_device *dev, int pipe,
e->base.event = &e->event.base;
e->base.file_priv = file_priv;
e->base.destroy = (void (*) (struct drm_pending_event *)) kfree;
+ e->callback = callback;
spin_lock_irqsave(&dev->event_lock, flags);
@@ -1411,12 +1414,15 @@ static int drm_queue_vblank_event(struct drm_device *dev, int pipe,
goto err_unlock;
}
- if (file_priv->event_space < sizeof e->event) {
- ret = -EBUSY;
- goto err_unlock;
+ if (file_priv) {
+ if (file_priv->event_space < sizeof e->event) {
+ ret = -EBUSY;
+ goto err_unlock;
+ }
+
+ file_priv->event_space -= sizeof e->event;
}
- file_priv->event_space -= sizeof e->event;
seq = drm_vblank_count_and_time(dev, pipe, &now);
if ((vblwait->request.type & _DRM_VBLANK_NEXTONMISS) &&
@@ -1434,7 +1440,7 @@ static int drm_queue_vblank_event(struct drm_device *dev, int pipe,
e->event.sequence = vblwait->request.sequence;
if ((seq - vblwait->request.sequence) <= (1 << 23)) {
drm_vblank_put(dev, pipe);
- send_vblank_event(dev, e, seq, &now);
+ e->callback(dev, e, seq, &now, false);
vblwait->reply.sequence = seq;
} else {
/* drm_handle_vblank_events will call drm_vblank_put */
@@ -1468,11 +1474,12 @@ err_put:
* the vblank interrupt refcount afterwards. (vblank IRQ disable follows that
* after a timeout with no further vblank waits scheduled).
*/
-int drm_wait_vblank(struct drm_device *dev, void *data,
- struct drm_file *file_priv)
+static int __drm_wait_vblank(struct drm_device *dev,
+ union drm_wait_vblank *vblwait,
+ struct drm_file *file_priv,
+ drm_vblank_callback_t callback)
{
struct drm_vblank_crtc *vblank;
- union drm_wait_vblank *vblwait = data;
int ret;
unsigned int flags, seq, crtc, high_crtc;
@@ -1525,7 +1532,8 @@ int drm_wait_vblank(struct drm_device *dev, void *data,
/* must hold on to the vblank ref until the event fires
* drm_vblank_put will be called asynchronously
*/
- return drm_queue_vblank_event(dev, crtc, vblwait, file_priv);
+ return drm_queue_vblank_event(dev, crtc, vblwait, file_priv,
+ callback);
}
if ((flags & _DRM_VBLANK_NEXTONMISS) &&
@@ -1560,6 +1568,35 @@ done:
return ret;
}
+int drm_wait_vblank_ioctl(struct drm_device *dev, void *data,
+ struct drm_file *file_priv)
+{
+ union drm_wait_vblank *vblwait = data;
+
+ return __drm_wait_vblank(dev, vblwait, file_priv, send_vblank_event);
+}
+
+int drm_wait_vblank_kernel(struct drm_crtc *crtc, int count, bool absolute,
+ drm_vblank_callback_t callback,
+ unsigned long user_data)
+{
+ struct drm_device *dev = crtc->dev;
+ union drm_wait_vblank vblwait;
+ int type = 0;
+
+ type |= absolute ? _DRM_VBLANK_ABSOLUTE : _DRM_VBLANK_RELATIVE;
+ type |= drm_crtc_index(crtc) << _DRM_VBLANK_HIGH_CRTC_SHIFT;
+ if (callback)
+ type |= _DRM_VBLANK_EVENT;
+
+ vblwait.request.type = type;
+ vblwait.request.sequence = count;
+ vblwait.request.signal = user_data;
+
+ return __drm_wait_vblank(dev, &vblwait, NULL, callback);
+}
+EXPORT_SYMBOL(drm_wait_vblank_kernel);
+
static void drm_handle_vblank_events(struct drm_device *dev, int crtc)
{
struct drm_pending_vblank_event *e, *t;
@@ -1581,7 +1618,7 @@ static void drm_handle_vblank_events(struct drm_device *dev, int crtc)
list_del(&e->base.link);
drm_vblank_put(dev, e->pipe);
- send_vblank_event(dev, e, seq, &now);
+ e->callback(dev, e, seq, &now, false);
}
trace_drm_vblank_event(crtc, seq);
@@ -2715,6 +2715,67 @@ static int i915_ddb_info(struct seq_file *m, void *unused)
return 0;
}
+struct vblank_data {
+ int eight;
+ const char *message;
+};
+
+static void vblank_callback(struct drm_device *dev,
+ struct drm_pending_vblank_event *e,
+ unsigned long seq, struct timeval *now,
+ bool premature)
+{
+ struct vblank_data *data = (struct vblank_data *)e->event.user_data;
+
+ WARN_ON(data->eight != 8);
+ DRM_DEBUG_KMS("vblank callback, seq: %lu, premature: %s message:%s\n",
+ seq, yesno(premature), data->message);
+
+ e->base.destroy(&e->base);
+ kfree(data);
+}
+
+static int i915_vblank_queue_test_new(struct seq_file *m, void *unused)
+{
+ struct drm_info_node *node = m->private;
+ struct drm_device *dev = node->minor->dev;
+ struct intel_crtc *crtc = NULL;
+ int ret = 0;
+ struct vblank_data *data;
+
+ for_each_intel_crtc(dev, crtc)
+ if (crtc->pipe == PIPE_A)
+ break;
+ if (WARN_ON(!crtc))
+ return -EINVAL;
+
+ data = kzalloc(sizeof *data, GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->message = "hello world";
+ data->eight = 8;
+
+ DRM_DEBUG_KMS("waiting 60 vblanks (no callback)\n");
+ ret = drm_wait_vblank_kernel(&crtc->base, 60, false, NULL, 0);
+ if (ret) {
+ DRM_DEBUG_KMS("vblank wait error: %d\n", ret);
+ return ret;
+ }
+ DRM_DEBUG_KMS("vblank wait done\n");
+
+ DRM_DEBUG_KMS("scheduling 60 vblanks (with callback)\n");
+ ret = drm_wait_vblank_kernel(&crtc->base, 60, false, vblank_callback,
+ (unsigned long) data);
+ if (ret) {
+ DRM_DEBUG_KMS("vblank schedule error: %d\n", ret);
+ return ret;
+ }
+ DRM_DEBUG_KMS("vblanks scheduled\n");
+
+ return 0;
+}
+
struct pipe_crc_info {
const char *name;
struct drm_device *dev;
@@ -4289,6 +4350,7 @@ static const struct drm_info_list i915_debugfs_list[] = {
{"i915_dp_mst_info", i915_dp_mst_info, 0},
{"i915_wa_registers", i915_wa_registers, 0},
{"i915_ddb_info", i915_ddb_info, 0},
+ {"i915_vblank_queue_test_new", i915_vblank_queue_test_new, 0},
};
#define I915_DEBUGFS_ENTRIES ARRAY_SIZE(i915_debugfs_list)
@@ -660,11 +660,16 @@ struct drm_minor {
struct drm_mode_group mode_group;
};
+typedef void (*drm_vblank_callback_t)(struct drm_device *dev,
+ struct drm_pending_vblank_event *e,
+ unsigned long seq, struct timeval *now,
+ bool premature);
struct drm_pending_vblank_event {
struct drm_pending_event base;
int pipe;
struct drm_event_vblank event;
+ drm_vblank_callback_t callback;
};
struct drm_vblank_crtc {
@@ -898,8 +903,12 @@ extern int drm_irq_install(struct drm_device *dev, int irq);
extern int drm_irq_uninstall(struct drm_device *dev);
extern int drm_vblank_init(struct drm_device *dev, int num_crtcs);
-extern int drm_wait_vblank(struct drm_device *dev, void *data,
- struct drm_file *filp);
+extern int drm_wait_vblank_ioctl(struct drm_device *dev, void *data,
+ struct drm_file *filp);
+extern int drm_wait_vblank_kernel(struct drm_crtc *crtc, int count,
+ bool absolute,
+ drm_vblank_callback_t callback,
+ unsigned long user_data);
extern u32 drm_vblank_count(struct drm_device *dev, int crtc);
extern u32 drm_vblank_count_and_time(struct drm_device *dev, int crtc,
struct timeval *vblanktime);