On Wed, 29 Apr 2009 18:02:59 -0700
Jesse Barnes <jbar...@virtuousgeek.org> wrote:

> On Wed, 29 Apr 2009 15:09:33 -0700
> Jesse Barnes <jbar...@virtuousgeek.org> wrote:
> > I'm still working through mutlihead issues on the kernel side; the
> > flip waits should wait for *both* vblank events before completing
> > the flip.  But other than that, I'm pretty happy with things.
> 
> This incremental set fixes up the multihead handling and adds swap
> interval support as a bonus.  It's nice to see flipping & no tearing
> on two heads at once!

Here's an update that makes all the code generic.  Jakob suggested that
we do the mode_set_base call out of the caller's context so we don't
block the caller waiting for the GTT domain transition (effectively
waiting until rendering finishes).  I definitely want to do this, but
I'll have to add a way to communicate the necessary flip data from the
ioctl to the new work function.

-- 
Jesse Barnes, Intel Open Source Technology Center
diff --git a/libdrm/xf86drmMode.c b/libdrm/xf86drmMode.c
index ea11207..ada5690 100644
--- a/libdrm/xf86drmMode.c
+++ b/libdrm/xf86drmMode.c
@@ -664,3 +664,16 @@ int drmModeCrtcSetGamma(int fd, uint32_t crtc_id, uint32_t size,
 
 	return 0;
 }
+
+int drmModePageFlip(int fd, uint32_t *crtc_ids, uint32_t crtc_count,
+		    uint32_t fb_id)
+{
+	struct drm_gem_page_flip flip;
+
+	flip.fb_id = fb_id;
+	flip.crtc_ids_ptr = (uint64_t)crtc_ids;
+	flip.crtc_count = (uint32_t)crtc_count;
+	flip.flags = 0;
+
+	return drmIoctl(fd, DRM_IOCTL_GEM_PAGE_FLIP, &flip);
+}
diff --git a/libdrm/xf86drmMode.h b/libdrm/xf86drmMode.h
index 62304bb..2406e2f 100644
--- a/libdrm/xf86drmMode.h
+++ b/libdrm/xf86drmMode.h
@@ -259,8 +259,6 @@ typedef struct _drmModeConnector {
 	uint32_t *encoders; /**< List of encoder ids */
 } drmModeConnector, *drmModeConnectorPtr;
 
-
-
 extern void drmModeFreeModeInfo( drmModeModeInfoPtr ptr );
 extern void drmModeFreeResources( drmModeResPtr ptr );
 extern void drmModeFreeFB( drmModeFBPtr ptr );
@@ -362,3 +360,5 @@ extern int drmModeCrtcSetGamma(int fd, uint32_t crtc_id, uint32_t size,
 			       uint16_t *red, uint16_t *green, uint16_t *blue);
 extern int drmModeCrtcGetGamma(int fd, uint32_t crtc_id, uint32_t size,
 			       uint16_t *red, uint16_t *green, uint16_t *blue);
+extern int drmModePageFlip(int fd, uint32_t *crtc_ids, uint32_t crtc_count,
+			   uint32_t fb_id);
diff --git a/shared-core/drm.h b/shared-core/drm.h
index b97ba09..2353ac4 100644
--- a/shared-core/drm.h
+++ b/shared-core/drm.h
@@ -1107,7 +1107,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, uint32_t)
-#define DRM_IOCTL_MODE_REPLACEFB	DRM_IOWR(0xB0, struct drm_mode_fb_cmd)
+#define DRM_IOCTL_GEM_PAGE_FLIP		DRM_IOW( 0xB0, struct drm_gem_page_flip)
 
 /*...@}*/
 
diff --git a/shared-core/drm_mode.h b/shared-core/drm_mode.h
index 9b92733..2354f77 100644
--- a/shared-core/drm_mode.h
+++ b/shared-core/drm_mode.h
@@ -270,4 +270,23 @@ struct drm_mode_crtc_lut {
 	uint64_t blue;
 };
 
+#define DRM_PAGE_FLIP_WAIT		(1<<0) /* block on previous page flip */
+#define DRM_PAGE_FLIP_FLAGS_MASK	(DRM_PAGE_FLIP_WAIT)
+
+struct drm_gem_page_flip {
+	/** Handle of new front buffer */
+	uint32_t fb_id;
+
+	/**
+	 * crtcs to flip
+	 */
+	uint32_t crtc_count;
+	uint64_t crtc_ids_ptr;
+
+	/**
+	 * page flip flags (wait on flip only for now)
+	 */
+	uint32_t flags;
+};
+
 #endif
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..7b9bc80 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,140 @@ int drm_helper_resume_force_mode(struct drm_device *dev)
 	return 0;
 }
 EXPORT_SYMBOL(drm_helper_resume_force_mode);
+
+/**
+ * drm_gem_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_gem_page_flip(struct drm_device *dev, void *data,
+		      struct drm_file *file_priv)
+{
+	struct drm_gem_page_flip *flip_data = data;
+	struct drm_mode_object *drm_obj, *fb_obj;
+	struct drm_crtc *crtc;
+	struct drm_framebuffer *old_fb;
+	struct drm_gem_object *obj = NULL;
+	unsigned long flags;
+	u32 frame, crtc_ids[2];
+	int i, ret = 0;
+	struct drm_crtc_helper_funcs *crtc_funcs;
+
+	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_PAGE_FLIP_FLAGS_MASK)) {
+		DRM_DEBUG("bad page flip flags\n");
+		return -EINVAL;
+	}
+
+	if (flip_data->crtc_count > dev->num_crtcs) {
+		DRM_DEBUG("bad crtc count\n");
+		return -EINVAL;
+	}
+
+	ret = copy_from_user(crtc_ids, (u32 __user *)flip_data->crtc_ids_ptr,
+			     sizeof(u32) * flip_data->crtc_count);
+	if (ret)
+		return -EFAULT;
+
+	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;
+	}
+
+	for (i = 0; i < flip_data->crtc_count; i++) {
+		drm_obj = drm_mode_object_find(dev, crtc_ids[i],
+					       DRM_MODE_OBJECT_CRTC);
+		if (!drm_obj) {
+			DRM_DEBUG("unknown crtc %d\n", crtc_ids[i]);
+			ret = -ENOENT;
+			goto out_unlock;
+		}
+		crtc = obj_to_crtc(drm_obj);
+		if (!crtc->enabled) {
+			DRM_DEBUG("crtc %d not enabled\n", crtc_ids[i]);
+			ret = -EINVAL;
+			goto out_unlock;
+		}
+		old_fb = crtc->fb;
+		crtc_funcs = crtc->helper_private;
+
+		/* 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;
+		}
+
+		crtc->fb = obj_to_fb(fb_obj);
+		obj = crtc_funcs->framebuffer_to_obj(crtc->fb);
+
+		/*
+		 * This prevents two threads from queueing a flip on
+		 * the same buffer and clobbering one another.  We could
+		 * also just do a wait_for_completion here, but that
+		 * could cause any thread who loses the race to the mutex
+		 * to clobber the previous flip.
+		 */
+	retry:
+		if (atomic_read(&obj->flips_pending)) {
+			mutex_unlock(&dev->struct_mutex);
+			wait_for_completion(&obj->flip);
+			mutex_lock(&dev->struct_mutex);
+			goto retry;
+		}
+
+		spin_lock_irqsave(&dev->flip_lock, flags);
+
+		/* Should be in the clear at this point, check */
+		WARN(atomic_read(&obj->flips_pending),
+		     "object already queued for flip\n");
+
+		frame = drm_vblank_count(dev, crtc->pipe);
+		obj->flip_frame[crtc->pipe] = frame + 1;
+		/* Only need to add it once */
+		if (list_empty(&obj->flip_head))
+		    list_add_tail(&obj->flip_head, &dev->flip_list);
+		atomic_inc(&obj->flips_pending);
+		spin_unlock_irqrestore(&dev->flip_lock, flags);
+
+		ret = crtc_funcs->mode_set_base(crtc, 0, 0, old_fb);
+		if (ret) {
+			DRM_ERROR("mode_set_base failed: %d\n", ret);
+			goto out_unlock;
+		}
+	}
+
+	mutex_unlock(&dev->struct_mutex);
+
+	if (obj && atomic_read(&obj->flips_pending))
+		wait_for_completion(&obj->flip);
+
+	return 0;
+
+out_unlock:
+	mutex_unlock(&dev->struct_mutex);
+
+	return ret;
+}
diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c
index c4ada8b..b71c53f 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_GEM_PAGE_FLIP, drm_gem_page_flip, DRM_AUTH|DRM_MASTER),
 };
 
 #define DRM_CORE_IOCTL_COUNT	ARRAY_SIZE( drm_ioctls )
diff --git a/drivers/gpu/drm/drm_gem.c b/drivers/gpu/drm/drm_gem.c
index 4984aa8..cb8b202 100644
--- a/drivers/gpu/drm/drm_gem.c
+++ b/drivers/gpu/drm/drm_gem.c
@@ -88,6 +88,8 @@ drm_gem_init(struct drm_device *dev)
 	atomic_set(&dev->pin_memory, 0);
 	atomic_set(&dev->gtt_count, 0);
 	atomic_set(&dev->gtt_memory, 0);
+	INIT_LIST_HEAD(&dev->flip_list);
+	spin_lock_init(&dev->flip_lock);
 
 	mm = drm_calloc(1, sizeof(struct drm_gem_mm), DRM_MEM_MM);
 	if (!mm) {
@@ -144,6 +146,16 @@ drm_gem_object_alloc(struct drm_device *dev, size_t size)
 
 	kref_init(&obj->refcount);
 	kref_init(&obj->handlecount);
+
+	INIT_LIST_HEAD(&obj->flip_head);
+	obj->flip_frame = kcalloc(1, sizeof(u32) * dev->num_crtcs, GFP_KERNEL);
+	if (!obj->flip_frame) {
+		kfree(obj);
+		return NULL;
+	}
+	init_completion(&obj->flip);
+	atomic_set(&obj->flips_pending, 0);
+
 	obj->size = size;
 	if (dev->driver->gem_init_object != NULL &&
 	    dev->driver->gem_init_object(obj) != 0) {
diff --git a/drivers/gpu/drm/drm_irq.c b/drivers/gpu/drm/drm_irq.c
index 93e677a..d3aa656 100644
--- a/drivers/gpu/drm/drm_irq.c
+++ b/drivers/gpu/drm/drm_irq.c
@@ -71,6 +71,44 @@ 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_handle_flip_locked(struct drm_device *dev,
+				   struct drm_gem_object *obj,
+				   int pipe)
+{
+	if (vblank_after(drm_vblank_count(dev, pipe), obj->flip_frame[pipe])) {
+		atomic_dec(&obj->flips_pending);
+		drm_vblank_put(dev, pipe);
+		/*
+		 * If all the vblanks for this object have completed,
+		 * we can wake up any waiters.
+		 */
+		if (!atomic_read(&obj->flips_pending)) {
+			list_del_init(&obj->flip_head);
+			complete(&obj->flip);
+		}
+	}
+}
+
+static void drm_flip_work_func(struct work_struct *work)
+{
+	struct drm_device *dev = container_of(work, struct drm_device,
+					      flip_complete_work);
+	struct drm_gem_object *obj, *tmp;
+	unsigned long irqflags;
+	int pipe;
+
+	mutex_lock(&dev->struct_mutex);
+	spin_lock_irqsave(&dev->flip_lock, irqflags);
+	for (pipe = 0; pipe < dev->num_crtcs; pipe++) {
+		list_for_each_entry_safe(obj, tmp, &dev->flip_list, flip_head)
+			drm_handle_flip_locked(dev, obj, pipe);
+	}
+	spin_unlock_irqrestore(&dev->flip_lock, irqflags);
+	mutex_unlock(&dev->struct_mutex);
+}
+
 static void vblank_disable_fn(unsigned long arg)
 {
 	struct drm_device *dev = (struct drm_device *)arg;
@@ -173,6 +211,9 @@ int drm_vblank_init(struct drm_device *dev, int num_crtcs)
 		atomic_set(&dev->vblank_refcount[i], 0);
 	}
 
+	spin_lock_init(&dev->flip_lock);
+//	INIT_WORK(&dev->flip_work, );
+	INIT_WORK(&dev->flip_complete_work, drm_flip_work_func);
 	dev->vblank_disable_allowed = 0;
 
 	return 0;
@@ -632,5 +673,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_complete_work);
 }
 EXPORT_SYMBOL(drm_handle_vblank);
+
diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c
index 4642115..ff1f452 100644
--- a/drivers/gpu/drm/i915/i915_gem.c
+++ b/drivers/gpu/drm/i915/i915_gem.c
@@ -3232,6 +3232,22 @@ i915_gem_execbuffer(struct drm_device *dev, void *data,
 	if (ret != 0)
 		goto pre_mutex_err;
 
+	/* Look up object handles */
+	for (i = 0; i < args->buffer_count; i++) {
+		object_list[i] = drm_gem_object_lookup(dev, file_priv,
+						       exec_list[i].handle);
+		if (object_list[i] == NULL) {
+			DRM_ERROR("Invalid object handle %d at index %d\n",
+				   exec_list[i].handle, i);
+			ret = -EBADF;
+			mutex_lock(&dev->struct_mutex);
+			goto err;
+		}
+
+		if (atomic_read(&object_list[i]->flips_pending))
+			wait_for_completion(&object_list[i]->flip);
+	}
+
 	mutex_lock(&dev->struct_mutex);
 
 	i915_verify_inactive(dev, __FILE__, __LINE__);
@@ -3250,17 +3266,8 @@ i915_gem_execbuffer(struct drm_device *dev, void *data,
 		goto pre_mutex_err;
 	}
 
-	/* Look up object handles */
+	/* Sanity check list for double entries */
 	for (i = 0; i < args->buffer_count; i++) {
-		object_list[i] = drm_gem_object_lookup(dev, file_priv,
-						       exec_list[i].handle);
-		if (object_list[i] == NULL) {
-			DRM_ERROR("Invalid object handle %d at index %d\n",
-				   exec_list[i].handle, i);
-			ret = -EBADF;
-			goto err;
-		}
-
 		obj_priv = object_list[i]->driver_private;
 		if (obj_priv->in_execbuffer) {
 			DRM_ERROR("Object %p appears more than once in object list\n",
@@ -3773,9 +3780,13 @@ i915_gem_idle(struct drm_device *dev)
 	 */
 	dev_priv->mm.suspended = 1;
 
+	mutex_unlock(&dev->struct_mutex);
+
+	/* Wait for any outstanding flips */
+	/* FIXME */
+
 	/* Cancel the retire work handler, wait for it to finish if running
 	 */
-	mutex_unlock(&dev->struct_mutex);
 	cancel_delayed_work_sync(&dev_priv->mm.retire_work);
 	mutex_lock(&dev->struct_mutex);
 
diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c
index bdcda36..f2c144a 100644
--- a/drivers/gpu/drm/i915/intel_display.c
+++ b/drivers/gpu/drm/i915/intel_display.c
@@ -641,6 +641,15 @@ intel_wait_for_vblank(struct drm_device *dev)
 	mdelay(20);
 }
 
+struct drm_gem_object *intel_framebuffer_to_obj(struct drm_framebuffer *fb)
+{
+	struct intel_framebuffer *intel_fb;
+
+	intel_fb = to_intel_framebuffer(fb);
+
+	return intel_fb->obj;
+}
+
 static int
 intel_pipe_set_base(struct drm_crtc *crtc, int x, int y,
 		    struct drm_framebuffer *old_fb)
@@ -662,6 +671,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 +708,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 +739,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)) {
@@ -765,7 +772,6 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y,
 		intel_fb = to_intel_framebuffer(old_fb);
 		i915_gem_object_unpin(intel_fb->obj);
 	}
-	mutex_unlock(&dev->struct_mutex);
 
 	if (!dev->primary->master)
 		return 0;
@@ -1314,7 +1320,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 +1765,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,
+	.framebuffer_to_obj = intel_framebuffer_to_obj,
 	.prepare = intel_crtc_prepare,
 	.commit = intel_crtc_commit,
 };
@@ -1779,7 +1788,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..fca0728 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_GEM_PAGE_FLIP		DRM_IOW( 0xB0, struct drm_gem_page_flip)
 
 /**
  * Device specific ioctls should only be in their respective headers
diff --git a/include/drm/drmP.h b/include/drm/drmP.h
index c8c4221..7ba8781 100644
--- a/include/drm/drmP.h
+++ b/include/drm/drmP.h
@@ -617,6 +617,14 @@ struct drm_gem_object {
 	 */
 	int name;
 
+	/** Object's place on the flip lists (protected by vblank_lock) */
+	struct list_head flip_head;
+	/** frame to wait for (both of these are per-pipe values) */
+	u32 *flip_frame;
+	/** for page flips */
+	struct completion flip;
+	atomic_t flips_pending;
+
 	/**
 	 * Memory domains. These monitor which caches contain read/write data
 	 * related to the object. When transitioning from one set of domains
@@ -968,6 +976,16 @@ struct drm_device {
 
 	u32 max_vblank_count;           /**< size of vblank counter register */
 
+	/** Protects flip list */
+	spinlock_t flip_lock;
+	struct work_struct flip_work;
+	struct work_struct flip_complete_work;
+
+	/**
+	 * List of objects waiting on flip completion
+	 */
+	struct list_head flip_list;
+
 	/*...@} */
 	cycles_t ctx_start;
 	cycles_t lck_start;
diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h
index 3c1924c..f352f6f 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_gem_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..1d2335c 100644
--- a/include/drm/drm_crtc_helper.h
+++ b/include/drm/drm_crtc_helper.h
@@ -60,6 +60,8 @@ struct drm_crtc_helper_funcs {
 	/* 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_gem_object *(*framebuffer_to_obj)(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..f4f030e 100644
--- a/include/drm/drm_mode.h
+++ b/include/drm/drm_mode.h
@@ -265,4 +265,23 @@ struct drm_mode_crtc_lut {
 	__u64 blue;
 };
 
+#define DRM_PAGE_FLIP_WAIT		(1<<0) /* block on previous page flip */
+#define DRM_PAGE_FLIP_FLAGS_MASK	(DRM_PAGE_FLIP_WAIT)
+
+struct drm_gem_page_flip {
+	/** Handle of new front buffer */
+	uint32_t fb_id;
+
+	/**
+	 * crtcs to flip
+	 */
+	uint32_t crtc_count;
+	uint64_t crtc_ids_ptr;
+
+	/**
+	 * page flip flags (wait on flip only for now)
+	 */
+	uint32_t flags;
+};
+
 #endif
------------------------------------------------------------------------------
Register Now & Save for Velocity, the Web Performance & Operations 
Conference from O'Reilly Media. Velocity features a full day of 
expert-led, hands-on workshops and two days of sessions from industry 
leaders in dedicated Performance & Operations tracks. Use code vel09scf 
and Save an extra 15% before 5/3. http://p.sf.net/sfu/velocityconf
--
_______________________________________________
Dri-devel mailing list
Dri-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/dri-devel

Reply via email to