[Intel-gfx] [PATCH] Add modesetting pageflip ioctl and corresponding drm event

Kristian Høgsberg krh at bitplanet.net
Fri May 8 22:56:22 CEST 2009


From: Kristian Høgsberg <krh at redhat.com>

This patch adds a vblank synced pageflip ioctl for to the modesetting
family of ioctls.  The ioctl takes a crtc and an fb and schedules a
pageflip to the new fb at the next coming vertical blank event.  This
feature lets userspace implement tear-free updating of the screen contents
with hw-guaranteed low latency page flipping.

The ioctl is asynchronous in that it returns immediately and then later
notifies the client by making an event available for reading on the drm fd.
This lets applications add the drm fd to their main loop and handle other
tasks while waiting for the flip to happen.  The event includes the time
of the flip, the frame counter and a 64 bit opaque token provided by
user space in the ioctl.

Based on initial work and suggestions from
	Jesse Barnes <jbarnes at virtuousgeek.org> and
	Jakob Bornecrantz <wallbraker at gmail.com>.

Signed-off-by: Kristian Høgsberg <krh at redhat.com>
---
 drivers/gpu/drm/drm_crtc.c           |    3 +-
 drivers/gpu/drm/drm_crtc_helper.c    |  123 ++++++++++++++++++++++++++++++++++
 drivers/gpu/drm/drm_drv.c            |    1 +
 drivers/gpu/drm/drm_fops.c           |   72 +++++++++++++++++++-
 drivers/gpu/drm/drm_irq.c            |   44 ++++++++++++
 drivers/gpu/drm/i915/i915_drv.c      |    1 +
 drivers/gpu/drm/i915/intel_display.c |   22 ++++--
 include/drm/drm.h                    |   25 +++++++
 include/drm/drmP.h                   |   30 ++++++++
 include/drm/drm_crtc.h               |    5 ++
 include/drm/drm_crtc_helper.h        |   15 ++++-
 include/drm/drm_mode.h               |   16 +++++
 12 files changed, 345 insertions(+), 12 deletions(-)

diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c
index 94a7688..4c39350 100644
--- a/drivers/gpu/drm/drm_crtc.c
+++ b/drivers/gpu/drm/drm_crtc.c
@@ -352,11 +352,12 @@ EXPORT_SYMBOL(drm_framebuffer_cleanup);
  *
  * Inits a new object created as base part of an driver crtc object.
  */
-void drm_crtc_init(struct drm_device *dev, struct drm_crtc *crtc,
+void drm_crtc_init(struct drm_device *dev, struct drm_crtc *crtc, int pipe,
 		   const struct drm_crtc_funcs *funcs)
 {
 	crtc->dev = dev;
 	crtc->funcs = funcs;
+	crtc->pipe = pipe;
 
 	mutex_lock(&dev->mode_config.mutex);
 	drm_mode_object_get(dev, &crtc->base, DRM_MODE_OBJECT_CRTC);
diff --git a/drivers/gpu/drm/drm_crtc_helper.c b/drivers/gpu/drm/drm_crtc_helper.c
index a04639d..d53417e 100644
--- a/drivers/gpu/drm/drm_crtc_helper.c
+++ b/drivers/gpu/drm/drm_crtc_helper.c
@@ -593,8 +593,10 @@ bool drm_crtc_helper_set_mode(struct drm_crtc *crtc,
 	if (drm_mode_equal(&saved_mode, &crtc->mode)) {
 		if (saved_x != crtc->x || saved_y != crtc->y ||
 		    depth_changed || bpp_changed) {
+			mutex_lock(&dev->struct_mutex);
 			ret = !crtc_funcs->mode_set_base(crtc, crtc->x, crtc->y,
 							 old_fb);
+			mutex_unlock(&dev->struct_mutex);
 			goto done;
 		}
 	}
@@ -864,8 +866,10 @@ int drm_crtc_helper_set_config(struct drm_mode_set *set)
 		old_fb = set->crtc->fb;
 		if (set->crtc->fb != set->fb)
 			set->crtc->fb = set->fb;
+		mutex_lock(&dev->struct_mutex);
 		ret = crtc_funcs->mode_set_base(set->crtc,
 						set->x, set->y, old_fb);
+		mutex_unlock(&dev->struct_mutex);
 		if (ret != 0)
 		    goto fail_set_mode;
 	}
@@ -1007,3 +1011,122 @@ int drm_helper_resume_force_mode(struct drm_device *dev)
 	return 0;
 }
 EXPORT_SYMBOL(drm_helper_resume_force_mode);
+
+/**
+ * drm_mode_page_flip - page flip ioctl
+ * @dev: DRM device
+ * @data: ioctl args
+ * @file_priv: file private data
+ *
+ * The page flip ioctl replaces the current front buffer with a new one,
+ * using the CRTC's mode_set_base function, which should just update
+ * the front buffer base pointer.  It's up to mode_set_base to make
+ * sure the update doesn't result in tearing (on some hardware the base
+ * register is double buffered, so this is easy).
+ *
+ * Note that this covers just the simple case of flipping the front
+ * buffer immediately.  Interval handling and interlaced modes have to
+ * be handled by userspace, or with new ioctls.
+ */
+int drm_mode_page_flip(struct drm_device *dev, void *data,
+		       struct drm_file *file_priv)
+{
+	struct drm_pending_flip *pending;
+	struct drm_mode_page_flip *flip_data = data;
+	struct drm_mode_object *drm_obj, *fb_obj;
+	struct drm_crtc *crtc;
+	struct drm_crtc_helper_funcs *crtc_funcs;
+	int ret = 0;
+
+	if (!(drm_core_check_feature(dev, DRIVER_MODESET)))
+		return -ENODEV;
+
+	/*
+	 * Reject unknown flags so future userspace knows what we (don't)
+	 * support
+	 */
+	if (flip_data->flags & (~DRM_MODE_PAGE_FLIP_FLAGS_MASK)) {
+		DRM_DEBUG("bad page flip flags\n");
+		return -EINVAL;
+	}
+
+	pending = kzalloc(sizeof *pending, GFP_KERNEL);
+	if (pending == NULL)
+		return -ENOMEM;
+
+	mutex_lock(&dev->struct_mutex);
+
+	fb_obj = drm_mode_object_find(dev, flip_data->fb_id,
+				      DRM_MODE_OBJECT_FB);
+	if (!fb_obj) {
+		DRM_DEBUG("unknown fb %d\n", flip_data->fb_id);
+		ret = -ENOENT;
+		goto out_unlock;
+	}
+
+	drm_obj = drm_mode_object_find(dev, flip_data->crtc_id,
+				       DRM_MODE_OBJECT_CRTC);
+	if (!drm_obj) {
+		DRM_DEBUG("unknown crtc %d\n", flip_data->crtc_id);
+		ret = -ENOENT;
+		goto out_unlock;
+	}
+	crtc = obj_to_crtc(drm_obj);
+	if (!crtc->enabled) {
+		DRM_DEBUG("crtc %d not enabled\n", flip_data->crtc_id);
+		ret = -EINVAL;
+		goto out_unlock;
+	}
+
+	crtc_funcs = crtc->helper_private;
+	if (crtc_funcs->mode_unpin_fb == NULL) {
+		DRM_DEBUG("crtc %d does not support delayed unpin\n",
+			  flip_data->crtc_id);
+		ret = -ENODEV;
+		goto out_unlock;
+	}
+
+	pending->crtc = crtc;
+	pending->old_fb = crtc->fb;
+	pending->pipe = crtc->pipe;
+	pending->event.base.type = DRM_EVENT_MODE_PAGE_FLIP;
+	pending->event.base.length = sizeof pending->event;
+	pending->event.base.user_data = flip_data->user_data;
+	pending->pending_event.event = &pending->event.base;
+	pending->pending_event.file_priv = file_priv;
+	pending->pending_event.destroy =
+		(void (*) (struct drm_pending_event *)) kfree;
+
+	/* Get vblank ref for completion handling */
+	ret = drm_vblank_get(dev, crtc->pipe);
+	if (ret) {
+		DRM_DEBUG("failed to take vblank ref\n");
+		goto out_unlock;
+	}
+
+	pending->frame = drm_vblank_count(dev, crtc->pipe);
+	list_add_tail(&pending->link, &dev->flip_list);
+
+	/*
+	 * The mode_set_base call will change the domain on the new
+	 * fb, which will force the rendering to finish and block the
+	 * ioctl.  We need to do this last part from a work queue, to
+	 * avoid blocking userspace here.
+	 */
+	crtc->fb = obj_to_fb(fb_obj);
+	ret = crtc_funcs->mode_set_base(crtc, 0, 0, NULL);
+	if (ret) {
+		DRM_ERROR("mode_set_base failed: %d\n", ret);
+		goto out_unlock;
+	}
+
+	mutex_unlock(&dev->struct_mutex);
+
+	return 0;
+
+out_unlock:
+	mutex_unlock(&dev->struct_mutex);
+	kfree(pending);
+
+	return ret;
+}
diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c
index c4ada8b..352893c 100644
--- a/drivers/gpu/drm/drm_drv.c
+++ b/drivers/gpu/drm/drm_drv.c
@@ -145,6 +145,7 @@ static struct drm_ioctl_desc drm_ioctls[] = {
 	DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETFB, drm_mode_getfb, DRM_MASTER|DRM_CONTROL_ALLOW),
 	DRM_IOCTL_DEF(DRM_IOCTL_MODE_ADDFB, drm_mode_addfb, DRM_MASTER|DRM_CONTROL_ALLOW),
 	DRM_IOCTL_DEF(DRM_IOCTL_MODE_RMFB, drm_mode_rmfb, DRM_MASTER|DRM_CONTROL_ALLOW),
+	DRM_IOCTL_DEF(DRM_IOCTL_MODE_PAGE_FLIP, drm_mode_page_flip, DRM_MASTER|DRM_CONTROL_ALLOW),
 };
 
 #define DRM_CORE_IOCTL_COUNT	ARRAY_SIZE( drm_ioctls )
diff --git a/drivers/gpu/drm/drm_fops.c b/drivers/gpu/drm/drm_fops.c
index 09a3571..07028b8 100644
--- a/drivers/gpu/drm/drm_fops.c
+++ b/drivers/gpu/drm/drm_fops.c
@@ -257,6 +257,8 @@ static int drm_open_helper(struct inode *inode, struct file *filp,
 
 	INIT_LIST_HEAD(&priv->lhead);
 	INIT_LIST_HEAD(&priv->fbs);
+	INIT_LIST_HEAD(&priv->event_list);
+	init_waitqueue_head(&priv->event_wait);
 
 	if (dev->driver->driver_features & DRIVER_GEM)
 		drm_gem_open(dev, priv);
@@ -429,6 +431,9 @@ int drm_release(struct inode *inode, struct file *filp)
 {
 	struct drm_file *file_priv = filp->private_data;
 	struct drm_device *dev = file_priv->minor->dev;
+	struct drm_pending_flip *f, *ft;
+	struct drm_pending_event *e, *et;
+
 	int retcode = 0;
 
 	lock_kernel();
@@ -451,6 +456,23 @@ int drm_release(struct inode *inode, struct file *filp)
 	if (file_priv->minor->master)
 		drm_master_release(dev, filp);
 
+	mutex_lock(&dev->struct_mutex);
+
+	/* Remove pending flips */
+	list_for_each_entry_safe(f, ft, &dev->flip_list, link) {
+		if (f->pending_event.file_priv == file_priv) {
+			drm_vblank_put(dev, f->pipe);
+			list_del(&f->link);
+			kfree(f);
+		}
+	}
+
+	/* Remove unconsumed events */
+	list_for_each_entry_safe(e, et, &file_priv->event_list, link)
+		kfree(e);
+
+	mutex_unlock(&dev->struct_mutex);
+
 	if (dev->driver->driver_features & DRIVER_GEM)
 		drm_gem_release(dev, file_priv);
 
@@ -544,9 +566,55 @@ int drm_release(struct inode *inode, struct file *filp)
 }
 EXPORT_SYMBOL(drm_release);
 
-/** No-op. */
+ssize_t drm_read(struct file *filp, char __user *buffer,
+		 size_t count, loff_t *offset)
+{
+	struct drm_file *file_priv = filp->private_data;
+	struct drm_device *dev = file_priv->minor->dev;
+	struct drm_pending_event *event;
+	ssize_t total, ret;
+
+	ret = wait_event_interruptible(file_priv->event_wait,
+				       !list_empty(&file_priv->event_list));
+	if (ret < 0)
+		return ret;
+
+	total = 0;
+	while (!list_empty(&file_priv->event_list)) {
+		mutex_lock(&dev->struct_mutex);
+		event = list_first_entry(&file_priv->event_list,
+					 struct drm_pending_event, link);
+		if (total + event->event->length > count) {
+			mutex_unlock(&dev->struct_mutex);
+			break;
+		}
+		list_del(&event->link);
+		mutex_unlock(&dev->struct_mutex);
+
+		if (copy_to_user(buffer + total,
+				 event->event, event->event->length)) {
+			total = -EFAULT;
+			break;
+		}
+
+		total += event->event->length;
+		event->destroy(event);
+	}
+
+	return total;
+}
+EXPORT_SYMBOL(drm_read);
+
 unsigned int drm_poll(struct file *filp, struct poll_table_struct *wait)
 {
-	return 0;
+	struct drm_file *file_priv = filp->private_data;
+	unsigned int mask = 0;
+
+	poll_wait(filp, &file_priv->event_wait, wait);
+
+	if (!list_empty(&file_priv->event_list))
+		mask |= POLLIN | POLLRDNORM;
+
+	return mask;
 }
 EXPORT_SYMBOL(drm_poll);
diff --git a/drivers/gpu/drm/drm_irq.c b/drivers/gpu/drm/drm_irq.c
index 93e677a..eb66110 100644
--- a/drivers/gpu/drm/drm_irq.c
+++ b/drivers/gpu/drm/drm_irq.c
@@ -34,6 +34,7 @@
  */
 
 #include "drmP.h"
+#include "drm_crtc_helper.h"
 
 #include <linux/interrupt.h>	/* For task queue support */
 
@@ -71,6 +72,45 @@ int drm_irq_by_busid(struct drm_device *dev, void *data,
 	return 0;
 }
 
+#define vblank_after(a,b) ((long)(b) - (long)(a) < 0)
+
+static void drm_finish_pending_flip(struct drm_device *dev,
+				    struct drm_pending_flip *f, u32 frame)
+{
+	struct drm_crtc_helper_funcs *crtc_funcs;
+	struct timeval now;
+
+	f->event.frame = frame;
+	do_gettimeofday(&now);
+	f->event.tv_sec = now.tv_sec;
+	f->event.tv_usec = now.tv_usec;	
+	drm_vblank_put(dev, f->pipe);
+	list_del_init(&f->link);
+	list_add_tail(&f->pending_event.link,
+		      &f->pending_event.file_priv->event_list);
+	crtc_funcs = f->crtc->helper_private;
+	crtc_funcs->mode_unpin_fb(f->crtc, f->old_fb);
+	wake_up_interruptible(&f->pending_event.file_priv->event_wait);
+}
+
+static void drm_flip_work_func(struct work_struct *work)
+{
+	struct drm_device *dev =
+		container_of(work, struct drm_device, flip_work);
+	struct drm_pending_flip *f, *t;
+	u32 frame;
+
+	mutex_lock(&dev->struct_mutex);
+
+	list_for_each_entry_safe(f, t, &dev->flip_list, link) {
+		frame = drm_vblank_count(dev, f->pipe);
+		if (vblank_after(frame, f->frame))
+			drm_finish_pending_flip(dev, f, frame);
+	}
+
+	mutex_unlock(&dev->struct_mutex);
+}
+
 static void vblank_disable_fn(unsigned long arg)
 {
 	struct drm_device *dev = (struct drm_device *)arg;
@@ -173,6 +213,8 @@ int drm_vblank_init(struct drm_device *dev, int num_crtcs)
 		atomic_set(&dev->vblank_refcount[i], 0);
 	}
 
+	INIT_LIST_HEAD(&dev->flip_list);
+	INIT_WORK(&dev->flip_work, drm_flip_work_func);
 	dev->vblank_disable_allowed = 0;
 
 	return 0;
@@ -632,5 +674,7 @@ void drm_handle_vblank(struct drm_device *dev, int crtc)
 {
 	atomic_inc(&dev->_vblank_count[crtc]);
 	DRM_WAKEUP(&dev->vbl_queue[crtc]);
+	schedule_work(&dev->flip_work);
 }
 EXPORT_SYMBOL(drm_handle_vblank);
+
diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c
index 98560e1..85dde09 100644
--- a/drivers/gpu/drm/i915/i915_drv.c
+++ b/drivers/gpu/drm/i915/i915_drv.c
@@ -198,6 +198,7 @@ static struct drm_driver driver = {
 		 .mmap = drm_gem_mmap,
 		 .poll = drm_poll,
 		 .fasync = drm_fasync,
+		 .read = drm_read,
 #ifdef CONFIG_COMPAT
 		 .compat_ioctl = i915_compat_ioctl,
 #endif
diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c
index bdcda36..ba53acb 100644
--- a/drivers/gpu/drm/i915/intel_display.c
+++ b/drivers/gpu/drm/i915/intel_display.c
@@ -662,6 +662,8 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y,
 	u32 dspcntr, alignment;
 	int ret;
 
+	BUG_ON(!mutex_is_locked(&dev->struct_mutex));
+
 	/* no fb bound */
 	if (!crtc->fb) {
 		DRM_DEBUG("No FB bound\n");
@@ -697,17 +699,14 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y,
 		BUG();
 	}
 
-	mutex_lock(&dev->struct_mutex);
 	ret = i915_gem_object_pin(intel_fb->obj, alignment);
 	if (ret != 0) {
-		mutex_unlock(&dev->struct_mutex);
 		return ret;
 	}
 
 	ret = i915_gem_object_set_to_gtt_domain(intel_fb->obj, 1);
 	if (ret != 0) {
 		i915_gem_object_unpin(intel_fb->obj);
-		mutex_unlock(&dev->struct_mutex);
 		return ret;
 	}
 
@@ -731,7 +730,6 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y,
 	default:
 		DRM_ERROR("Unknown color depth\n");
 		i915_gem_object_unpin(intel_fb->obj);
-		mutex_unlock(&dev->struct_mutex);
 		return -EINVAL;
 	}
 	if (IS_I965G(dev)) {
@@ -759,13 +757,11 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y,
 		I915_READ(dspbase);
 	}
 
-	intel_wait_for_vblank(dev);
-
 	if (old_fb) {
 		intel_fb = to_intel_framebuffer(old_fb);
+		intel_wait_for_vblank(dev);
 		i915_gem_object_unpin(intel_fb->obj);
 	}
-	mutex_unlock(&dev->struct_mutex);
 
 	if (!dev->primary->master)
 		return 0;
@@ -785,7 +781,14 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y,
 	return 0;
 }
 
+static void
+intel_pipe_unpin_fb(struct drm_crtc *crtc, struct drm_framebuffer *old_fb)
+{
+	struct intel_framebuffer *intel_fb;
 
+	intel_fb = to_intel_framebuffer(old_fb);
+	i915_gem_object_unpin(intel_fb->obj);
+}
 
 /**
  * Sets the power management mode of the pipe and plane.
@@ -1314,7 +1317,9 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
 	I915_WRITE(dspcntr_reg, dspcntr);
 
 	/* Flush the plane changes */
+	mutex_lock(&dev->struct_mutex);
 	ret = intel_pipe_set_base(crtc, x, y, old_fb);
+	mutex_unlock(&dev->struct_mutex);
 	if (ret != 0)
 	    return ret;
 
@@ -1757,6 +1762,7 @@ static const struct drm_crtc_helper_funcs intel_helper_funcs = {
 	.mode_fixup = intel_crtc_mode_fixup,
 	.mode_set = intel_crtc_mode_set,
 	.mode_set_base = intel_pipe_set_base,
+	.mode_unpin_fb = intel_pipe_unpin_fb,
 	.prepare = intel_crtc_prepare,
 	.commit = intel_crtc_commit,
 };
@@ -1779,7 +1785,7 @@ static void intel_crtc_init(struct drm_device *dev, int pipe)
 	if (intel_crtc == NULL)
 		return;
 
-	drm_crtc_init(dev, &intel_crtc->base, &intel_crtc_funcs);
+	drm_crtc_init(dev, &intel_crtc->base, pipe, &intel_crtc_funcs);
 
 	drm_mode_crtc_set_gamma_size(&intel_crtc->base, 256);
 	intel_crtc->pipe = pipe;
diff --git a/include/drm/drm.h b/include/drm/drm.h
index 7cb50bd..e385fe2 100644
--- a/include/drm/drm.h
+++ b/include/drm/drm.h
@@ -686,6 +686,7 @@ struct drm_gem_open {
 #define DRM_IOCTL_MODE_GETFB		DRM_IOWR(0xAD, struct drm_mode_fb_cmd)
 #define DRM_IOCTL_MODE_ADDFB		DRM_IOWR(0xAE, struct drm_mode_fb_cmd)
 #define DRM_IOCTL_MODE_RMFB		DRM_IOWR(0xAF, unsigned int)
+#define DRM_IOCTL_MODE_PAGE_FLIP	DRM_IOW( 0xB0, struct drm_mode_page_flip)
 
 /**
  * Device specific ioctls should only be in their respective headers
@@ -698,6 +699,30 @@ struct drm_gem_open {
 #define DRM_COMMAND_BASE                0x40
 #define DRM_COMMAND_END			0xA0
 
+/**
+ * Header for events written back to userspace on the drm fd.  The
+ * type defines the type of event, the length specifies the total
+ * length of the event (including the header), and user_data is
+ * typically a 64 bit value passed with the ioctl that triggered the
+ * event.  A read on the drm fd will always only return complete
+ * events, that is, if for example the read buffer is 100 bytes, and
+ * there are two 64 byte events pending, only one will be returned.
+ */
+struct drm_event {
+	__u32 type;
+	__u32 length;
+	__u64 user_data;
+};
+
+#define DRM_EVENT_MODE_PAGE_FLIP 0x01
+
+struct drm_event_page_flip {
+	struct drm_event base;
+	__u32 tv_sec;
+	__u32 tv_usec;
+	__u32 frame;
+};
+
 /* typedef area */
 #ifndef __KERNEL__
 typedef struct drm_clip_rect drm_clip_rect_t;
diff --git a/include/drm/drmP.h b/include/drm/drmP.h
index c8c4221..47dcdb9 100644
--- a/include/drm/drmP.h
+++ b/include/drm/drmP.h
@@ -378,6 +378,14 @@ struct drm_buf_entry {
 	struct drm_freelist freelist;
 };
 
+/* Event queued up for userspace to read */
+struct drm_pending_event {
+	struct drm_event *event;
+	struct list_head link;
+	struct drm_file *file_priv;
+	void (*destroy) (struct drm_pending_event *event);
+};
+
 /** File private data */
 struct drm_file {
 	int authenticated;
@@ -401,6 +409,9 @@ struct drm_file {
 	struct drm_master *master; /* master this node is currently associated with
 				      N.B. not always minor->master */
 	struct list_head fbs;
+
+	wait_queue_head_t event_wait;
+	struct list_head event_list;
 };
 
 /** Wait queue */
@@ -869,6 +880,16 @@ struct drm_minor {
 	struct drm_mode_group mode_group;
 };
 
+struct drm_pending_flip {
+	struct drm_pending_event pending_event;
+	struct drm_framebuffer *old_fb;
+	struct drm_crtc *crtc;
+	u32 frame;
+	int pipe;
+	struct list_head link;
+	struct drm_event_page_flip event;
+};
+
 /**
  * DRM device structure. This structure represent a complete card that
  * may contain multiple heads.
@@ -968,6 +989,13 @@ struct drm_device {
 
 	u32 max_vblank_count;           /**< size of vblank counter register */
 
+	struct work_struct flip_work;
+
+	/**
+	 * List of objects waiting on flip completion
+	 */
+	struct list_head flip_list;
+
 	/*@} */
 	cycles_t ctx_start;
 	cycles_t lck_start;
@@ -1104,6 +1132,8 @@ extern int drm_lastclose(struct drm_device *dev);
 extern int drm_open(struct inode *inode, struct file *filp);
 extern int drm_stub_open(struct inode *inode, struct file *filp);
 extern int drm_fasync(int fd, struct file *filp, int on);
+extern ssize_t drm_read(struct file *filp, char __user *buffer,
+			size_t count, loff_t *offset);
 extern int drm_release(struct inode *inode, struct file *filp);
 
 				/* Mapping support (drm_vm.h) */
diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h
index 3c1924c..5c5c10f 100644
--- a/include/drm/drm_crtc.h
+++ b/include/drm/drm_crtc.h
@@ -336,6 +336,7 @@ struct drm_crtc_funcs {
 /**
  * drm_crtc - central CRTC control structure
  * @enabled: is this CRTC enabled?
+ * @pipe: pipe number (as seen by DRM vblank functions)
  * @x: x position on screen
  * @y: y position on screen
  * @desired_mode: new desired mode
@@ -359,6 +360,7 @@ struct drm_crtc {
 
 	struct drm_display_mode mode;
 
+	int pipe;
 	int x, y;
 	struct drm_display_mode *desired_mode;
 	int desired_x, desired_y;
@@ -586,6 +588,7 @@ struct drm_mode_config {
 
 extern void drm_crtc_init(struct drm_device *dev,
 			  struct drm_crtc *crtc,
+			  int pipe,
 			  const struct drm_crtc_funcs *funcs);
 extern void drm_crtc_cleanup(struct drm_crtc *crtc);
 
@@ -733,4 +736,6 @@ extern int drm_mode_gamma_get_ioctl(struct drm_device *dev,
 extern int drm_mode_gamma_set_ioctl(struct drm_device *dev,
 				    void *data, struct drm_file *file_priv);
 extern bool drm_detect_hdmi_monitor(struct edid *edid);
+extern int drm_mode_page_flip(struct drm_device *dev, void *data,
+			      struct drm_file *file_priv);
 #endif /* __DRM_CRTC_H__ */
diff --git a/include/drm/drm_crtc_helper.h b/include/drm/drm_crtc_helper.h
index ec073d8..a0ae240 100644
--- a/include/drm/drm_crtc_helper.h
+++ b/include/drm/drm_crtc_helper.h
@@ -57,9 +57,22 @@ struct drm_crtc_helper_funcs {
 			struct drm_display_mode *adjusted_mode, int x, int y,
 			struct drm_framebuffer *old_fb);
 
-	/* Move the crtc on the current fb to the given position *optional* */
+	/*
+	 * Move the crtc on the current fb to the given position.
+	 * This function is optional.  If old_fb is provided, the
+	 * function will wait for vblank and unpin it.  If old_fb is
+	 * NULL, nothing is unpinned and the caller must call
+	 * mode_unpin_fb to release the old framebuffer.
+	 */
 	int (*mode_set_base)(struct drm_crtc *crtc, int x, int y,
 			     struct drm_framebuffer *old_fb);
+
+	/*
+	 * Unpin the old fb after setting a mode.  Must be called
+	 * after the old framebuffer is no longer visible, ie, after
+	 * the next vblank, typically.
+	 */
+	void (*mode_unpin_fb)(struct drm_crtc *crtc, struct drm_framebuffer *fb);
 };
 
 struct drm_encoder_helper_funcs {
diff --git a/include/drm/drm_mode.h b/include/drm/drm_mode.h
index ae304cc..464b779 100644
--- a/include/drm/drm_mode.h
+++ b/include/drm/drm_mode.h
@@ -265,4 +265,20 @@ struct drm_mode_crtc_lut {
 	__u64 blue;
 };
 
+#define DRM_MODE_PAGE_FLIP_WAIT		(1<<0) /* block on previous page flip */
+#define DRM_MODE_PAGE_FLIP_FLAGS_MASK	(DRM_MODE_PAGE_FLIP_WAIT)
+
+struct drm_mode_page_flip {
+	/** Handle of new front buffer */
+	__u32 fb_id;
+	__u32 crtc_id;
+
+	/* 64 bit cookie returned to userspace in the page flip event. */
+	__u64 user_data;
+	/**
+	 * page flip flags (wait on flip only for now)
+	 */
+	__u32 flags;
+};
+
 #endif
-- 
1.6.2




More information about the Intel-gfx mailing list