[Intel-gfx] [PATCH] i915: support page flipping

Jesse Barnes jbarnes at virtuousgeek.org
Tue Jun 2 14:33:23 CEST 2009


This is an updated version of Kristian's patch, fixed up a bit to work
with X.  We weren't handling restarts correctly in some places (which X
tends to cause quite a few of), and there are still some issues with
the new event code (it's easy to panic if you don't consume the events).

Anyway, review appreciated.  Now that it's fairly stable in my
environment, I'll look at adding the event handling stuff we talked
about to the server.

Jesse

diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c
index 94a7688..3ad2a3c 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);
@@ -2447,3 +2448,124 @@ out:
 	mutex_unlock(&dev->mode_config.mutex);
 	return ret;
 }
+
+/**
+ * drm_mode_page_flip_ioctl - 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 set_base function, which should just update
+ * the front buffer base pointer.  It's up to 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_ioctl(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;
+	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;
+	}
+
+	if (crtc->fb->funcs->unpin == NULL) {
+		DRM_DEBUG("fb for 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.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 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);
+retry_set:
+	ret = (*crtc->funcs->set_base)(crtc, 0, 0, NULL);
+	if (ret == -ERESTARTSYS)
+		goto retry_set;
+
+	if (ret) {
+		DRM_ERROR("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_crtc_helper.c b/drivers/gpu/drm/drm_crtc_helper.c
index 4589044..55cd868 100644
--- a/drivers/gpu/drm/drm_crtc_helper.c
+++ b/drivers/gpu/drm/drm_crtc_helper.c
@@ -817,7 +817,7 @@ int drm_crtc_helper_set_config(struct drm_mode_set *set)
 	}
 
 	/* mode_set_base is not a required function */
-	if (fb_changed && !crtc_funcs->mode_set_base)
+	if (fb_changed && !set->crtc->funcs->set_base)
 		mode_changed = true;
 
 	if (mode_changed) {
@@ -845,8 +845,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;
-		ret = crtc_funcs->mode_set_base(set->crtc,
-						set->x, set->y, old_fb);
+		mutex_lock(&dev->struct_mutex);
+		ret = (*set->crtc->funcs->set_base)(set->crtc,
+						    set->x, set->y, old_fb);
+		mutex_unlock(&dev->struct_mutex);
 		if (ret != 0)
 		    goto fail_set_mode;
 	}
diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c
index 019b7c5..ffa2ec8 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_ioctl, 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..4b1b461 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,19 @@ 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_finish_pending_flip(dev, f, 0);
+
+	/* Remove unconsumed events */
+	list_for_each_entry_safe(e, et, &file_priv->event_list, link)
+		e->destroy(e);
+
+	mutex_unlock(&dev->struct_mutex);
+
 	if (dev->driver->driver_features & DRIVER_GEM)
 		drm_gem_release(dev, file_priv);
 
@@ -544,9 +562,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..2825666 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,43 @@ int drm_irq_by_busid(struct drm_device *dev, void *data,
 	return 0;
 }
 
+#define vblank_after(a,b) ((long)(b) - (long)(a) < 0)
+
+void drm_finish_pending_flip(struct drm_device *dev,
+			     struct drm_pending_flip *f, u32 frame)
+{
+	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);
+	f->old_fb->funcs->unpin(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 +211,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 +672,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 c9d6f10..1b794f4 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,8 +781,6 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y,
 	return 0;
 }
 
-
-
 /**
  * Sets the power management mode of the pipe and plane.
  *
@@ -1314,7 +1308,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;
 
@@ -1770,7 +1766,6 @@ static const struct drm_crtc_helper_funcs intel_helper_funcs = {
 	.dpms = intel_crtc_dpms,
 	.mode_fixup = intel_crtc_mode_fixup,
 	.mode_set = intel_crtc_mode_set,
-	.mode_set_base = intel_pipe_set_base,
 	.prepare = intel_crtc_prepare,
 	.commit = intel_crtc_commit,
 };
@@ -1781,6 +1776,7 @@ static const struct drm_crtc_funcs intel_crtc_funcs = {
 	.gamma_set = intel_crtc_gamma_set,
 	.set_config = drm_crtc_helper_set_config,
 	.destroy = intel_crtc_destroy,
+	.set_base = intel_pipe_set_base,
 };
 
 
@@ -1793,7 +1789,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;
@@ -1983,9 +1979,18 @@ static int intel_user_framebuffer_create_handle(struct drm_framebuffer *fb,
 	return drm_gem_handle_create(file_priv, object, handle);
 }
 
+static void intel_user_framebuffer_unpin(struct drm_framebuffer *fb)
+{
+	struct intel_framebuffer *intel_fb;
+
+	intel_fb = to_intel_framebuffer(fb);
+	i915_gem_object_unpin(intel_fb->obj);
+}
+
 static const struct drm_framebuffer_funcs intel_fb_funcs = {
 	.destroy = intel_user_framebuffer_destroy,
 	.create_handle = intel_user_framebuffer_create_handle,
+	.unpin = intel_user_framebuffer_unpin
 };
 
 int intel_framebuffer_create(struct drm_device *dev,
diff --git a/include/drm/drm.h b/include/drm/drm.h
index 7cb50bd..1920323 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;
+};
+
+#define DRM_EVENT_MODE_PAGE_FLIP 0x01
+
+struct drm_event_page_flip {
+	struct drm_event base;
+	__u64 user_data;
+	__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 b84d8ae..2fdd76b 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) */
@@ -1270,6 +1300,8 @@ extern void drm_vblank_pre_modeset(struct drm_device *dev, int crtc);
 extern void drm_vblank_post_modeset(struct drm_device *dev, int crtc);
 extern int drm_modeset_ctl(struct drm_device *dev, void *data,
 			   struct drm_file *file_priv);
+extern void drm_finish_pending_flip(struct drm_device *dev,
+				   struct drm_pending_flip *f, u32 frame);
 
 				/* AGP/GART support (drm_agpsupport.h) */
 extern struct drm_agp_head *drm_agp_init(struct drm_device *dev);
diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h
index 3c1924c..a1be471 100644
--- a/include/drm/drm_crtc.h
+++ b/include/drm/drm_crtc.h
@@ -238,6 +238,12 @@ struct drm_display_info {
 };
 
 struct drm_framebuffer_funcs {
+	/*
+	 * 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 (*unpin)(struct drm_framebuffer *fb);
 	void (*destroy)(struct drm_framebuffer *framebuffer);
 	int (*create_handle)(struct drm_framebuffer *fb,
 			     struct drm_file *file_priv,
@@ -331,11 +337,22 @@ struct drm_crtc_funcs {
 	void (*destroy)(struct drm_crtc *crtc);
 
 	int (*set_config)(struct drm_mode_set *set);
+
+	/*
+	 * 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 (*set_base)(struct drm_crtc *crtc, int x, int y,
+			struct drm_framebuffer *old_fb);
 };
 
 /**
  * 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 +376,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 +604,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 +752,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_ioctl(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..81fc313 100644
--- a/include/drm/drm_crtc_helper.h
+++ b/include/drm/drm_crtc_helper.h
@@ -56,10 +56,6 @@ struct drm_crtc_helper_funcs {
 	int (*mode_set)(struct drm_crtc *crtc, struct drm_display_mode *mode,
 			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* */
-	int (*mode_set_base)(struct drm_crtc *crtc, int x, int y,
-			     struct drm_framebuffer *old_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



More information about the Intel-gfx mailing list