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

Jesse Barnes jbarnes at virtuousgeek.org
Wed Apr 15 00:22:51 CEST 2009


Add a new page flipping ioctl to the i915 driver, using KMS
functionality.

Internally, the new flip ioctl will use the mode_set_base function,
which will simply update the front buffer pointer, taking care to pin
the new buffer and unpin the old one.  This means userspace needs to
create an FB id for the new front buffer prior to calling in.

The flip will take place at the next vblank period automatically (since
the display base regs are double buffered), and any drawing to a
buffer with a pending flip will be blocked at execbuf time until the
next vblank interrupt fires and runs its completion handler.

diff --git a/drivers/gpu/drm/i915/i915_dma.c
b/drivers/gpu/drm/i915/i915_dma.c index c23b3a9..5273ce0 100644
--- a/drivers/gpu/drm/i915/i915_dma.c
+++ b/drivers/gpu/drm/i915/i915_dma.c
@@ -871,6 +871,127 @@ static int i915_set_status_page(struct drm_device
*dev, void *data, return 0;
 }
 
+bool
+i915_gem_flip_pending(struct drm_gem_object *obj)
+{
+	struct drm_device *dev = obj->dev;
+	drm_i915_private_t *dev_priv = dev->dev_private;
+	struct drm_i915_gem_object *obj_priv = obj->driver_private;
+	unsigned long flags;
+	bool pending;
+
+	spin_lock_irqsave(&dev_priv->vblank_lock, flags);
+	pending = !list_empty(&obj_priv->vblank_head);
+	spin_unlock_irqrestore(&dev_priv->vblank_lock, flags);
+
+	return pending;
+}
+
+static int i915_gem_page_flip(struct drm_device *dev, void *data,
+			      struct drm_file *file_priv)
+{
+	struct drm_i915_gem_page_flip *flip_data = data;
+	drm_i915_private_t *dev_priv = dev->dev_private;
+	struct drm_mode_object *drm_obj;
+	struct drm_crtc *crtc;
+	struct intel_crtc *intel_crtc;
+	struct intel_framebuffer *intel_fb;
+	struct drm_framebuffer *old_fb;
+	struct drm_i915_gem_object *obj_priv;
+	unsigned long flags;
+	unsigned int pipe;
+	int ret = 0, seqno;
+	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 & (~I915_PAGE_FLIP_WAIT)) {
+		DRM_ERROR("bad page flip flags\n");
+		return -EINVAL;
+	}
+
+	mutex_lock(&dev->struct_mutex);
+
+	/* Find the CRTC & FB ids */
+	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 = -EINVAL;
+		goto out_unlock;
+	}
+	crtc = obj_to_crtc(drm_obj);
+	if (!crtc->enabled) {
+		DRM_ERROR("crtc %d not enabled\n", flip_data->crtc_id);
+		ret = -EINVAL;
+		goto out_unlock;
+	}
+	old_fb = crtc->fb;
+	intel_crtc = to_intel_crtc(crtc);
+	pipe = intel_crtc->pipe;
+
+	drm_obj = drm_mode_object_find(dev, flip_data->fb_id,
+				       DRM_MODE_OBJECT_FB);
+	if (!drm_obj) {
+		DRM_DEBUG("unknown fb %d\n", flip_data->fb_id);
+		ret = -EINVAL;
+		goto out_unlock;
+	}
+	crtc->fb = obj_to_fb(drm_obj);
+	intel_fb = to_intel_framebuffer(crtc->fb);
+	obj_priv = intel_fb->obj->driver_private;
+
+	if (i915_gem_flip_pending(intel_fb->obj))
+		wait_for_completion(&obj_priv->vblank);
+
+	/* Sanity check tiling */
+	if (obj_priv->tiling_mode != I915_TILING_NONE &&
+	    obj_priv->tiling_mode != I915_TILING_X) {
+		DRM_ERROR("can only flip non-tiled or X tiled
pages\n");
+		ret = -EINVAL;
+		goto out_unlock;
+	}
+
+	/* Get vblank ref for completion handling */
+	ret = drm_vblank_get(dev, pipe);
+	if (ret) {
+		DRM_ERROR("failed to take vblank ref\n");
+		goto out_unlock;
+	}
+
+	mutex_unlock(&dev->struct_mutex);
+
+	crtc_funcs = crtc->helper_private;
+	crtc_funcs->mode_set_base(crtc, 0, 0, old_fb);
+	seqno = i915_add_request(dev, 0);
+
+	/*
+	 * This is a bit racy; the flip above may have already happened
+	 * by the time we get here.  If that happens, the new back
buffer
+	 * won't be available for rendering for one extra frame, since
+	 * the vblank list won't have the object.
+	 */
+	spin_lock_irqsave(&dev_priv->vblank_lock, flags);
+	list_add_tail(&obj_priv->vblank_head,
&dev_priv->mm.vblank_list[pipe]);
+	obj_priv->flip_seqno = seqno;
+	spin_unlock_irqrestore(&dev_priv->vblank_lock, flags);
+
+	if (flip_data->flags & I915_PAGE_FLIP_WAIT)
+		wait_for_completion(&obj_priv->vblank);
+
+	return 0;
+
+out_unlock:
+	mutex_unlock(&dev->struct_mutex);
+
+	return ret;
+}
+
 /**
  * i915_probe_agp - get AGP bootup configuration
  * @pdev: PCI device
@@ -1349,6 +1470,7 @@ struct drm_ioctl_desc i915_ioctls[] = {
 	DRM_IOCTL_DEF(DRM_I915_GEM_SET_TILING, i915_gem_set_tiling, 0),
 	DRM_IOCTL_DEF(DRM_I915_GEM_GET_TILING, i915_gem_get_tiling, 0),
 	DRM_IOCTL_DEF(DRM_I915_GEM_GET_APERTURE,
i915_gem_get_aperture_ioctl, 0),
+	DRM_IOCTL_DEF(DRM_I915_GEM_PAGE_FLIP, i915_gem_page_flip,
DRM_AUTH|DRM_MASTER), };
 
 int i915_max_ioctl = DRM_ARRAY_SIZE(i915_ioctls);
diff --git a/drivers/gpu/drm/i915/i915_drv.h
b/drivers/gpu/drm/i915/i915_drv.h index efcd610..9f40a13 100644
--- a/drivers/gpu/drm/i915/i915_drv.h
+++ b/drivers/gpu/drm/i915/i915_drv.h
@@ -162,6 +162,10 @@ typedef struct drm_i915_private {
 	u32 hotplug_supported_mask;
 	struct work_struct hotplug_work;
 
+	/** Protects vblank list */
+	spinlock_t vblank_lock;
+	struct work_struct vblank_work;
+
 	int tex_lru_log_granularity;
 	int allow_batchbuffer;
 	struct mem_block *agp_heap;
@@ -341,6 +345,11 @@ typedef struct drm_i915_private {
 		 */
 		struct delayed_work retire_work;
 
+		/**
+		 * List of objects waiting on vblank events (one per
pipe)
+		 */
+		struct list_head vblank_list[2];
+
 		uint32_t next_gem_seqno;
 
 		/**
@@ -392,6 +401,13 @@ struct drm_i915_gem_object {
 	/** This object's place on the active/flushing/inactive lists
*/ struct list_head list;
 
+	/** Object's place on the vblank list (protected by
vblank_lock)*/
+	struct list_head vblank_head;
+	/** sequence number for flip (when it passes the flip is done),
+	 *  protected by vblank lock
+	 */
+	int flip_seqno;
+
 	/**
 	 * This is set if the object is on the active or flushing lists
 	 * (has pending rendering), and is not set if it's on inactive
(ready @@ -462,6 +478,9 @@ struct drm_i915_gem_object {
 	/** for phy allocated objects */
 	struct drm_i915_gem_phys_object *phys_obj;
 
+	/** for page flips and other vblank related blocks */
+	struct completion vblank;
+
 	/**
 	 * Used for checking the object doesn't appear more than once
 	 * in an execbuffer object list.
@@ -526,6 +545,7 @@ extern long i915_compat_ioctl(struct file *filp,
unsigned int cmd, extern int i915_emit_box(struct drm_device *dev,
 			 struct drm_clip_rect *boxes,
 			 int i, int DR1, int DR4);
+extern bool i915_gem_flip_pending(struct drm_gem_object *obj);
 
 /* i915_irq.c */
 extern int i915_irq_emit(struct drm_device *dev, void *data,
@@ -637,6 +657,9 @@ void i915_gem_detach_phys_object(struct drm_device
*dev, void i915_gem_free_all_phys_object(struct drm_device *dev);
 int i915_gem_object_get_pages(struct drm_gem_object *obj);
 void i915_gem_object_put_pages(struct drm_gem_object *obj);
+uint32_t i915_add_request(struct drm_device *dev, uint32_t
flush_domains); +int i915_seqno_passed(uint32_t seq1, uint32_t seq2);
+uint32_t i915_get_gem_seqno(struct drm_device *dev);
 
 /* i915_gem_tiling.c */
 void i915_gem_detect_bit_6_swizzle(struct drm_device *dev);
diff --git a/drivers/gpu/drm/i915/i915_gem.c
b/drivers/gpu/drm/i915/i915_gem.c index 6f7d0e2..136dfa0 100644
--- a/drivers/gpu/drm/i915/i915_gem.c
+++ b/drivers/gpu/drm/i915/i915_gem.c
@@ -1374,7 +1374,7 @@ i915_gem_object_move_to_inactive(struct
drm_gem_object *obj) *
  * Returned sequence numbers are nonzero on success.
  */
-static uint32_t
+uint32_t
 i915_add_request(struct drm_device *dev, uint32_t flush_domains)
 {
 	drm_i915_private_t *dev_priv = dev->dev_private;
@@ -1505,7 +1505,7 @@ out:
 /**
  * Returns true if seq1 is later than seq2.
  */
-static int
+int
 i915_seqno_passed(uint32_t seq1, uint32_t seq2)
 {
 	return (int32_t)(seq1 - seq2) >= 0;
@@ -3116,6 +3116,26 @@ 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 (i915_gem_flip_pending(object_list[i])) {
+			struct drm_i915_gem_object *obj_priv;
+
+			obj_priv = object_list[i]->driver_private;
+			wait_for_completion(&obj_priv->vblank);
+		}
+	}
+
 	mutex_lock(&dev->struct_mutex);
 
 	i915_verify_inactive(dev, __FILE__, __LINE__);
@@ -3134,17 +3154,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", @@ -3581,6 +3592,9 @@ int i915_gem_init_object(struct
drm_gem_object *obj) obj_priv->obj = obj;
 	obj_priv->fence_reg = I915_FENCE_REG_NONE;
 	INIT_LIST_HEAD(&obj_priv->list);
+	INIT_LIST_HEAD(&obj_priv->vblank_head);
+
+	init_completion(&obj_priv->vblank);
 
 	return 0;
 }
@@ -3641,8 +3655,10 @@ int
 i915_gem_idle(struct drm_device *dev)
 {
 	drm_i915_private_t *dev_priv = dev->dev_private;
+	struct drm_i915_gem_object *obj_priv, *tmp;
+	unsigned long irqflags;
 	uint32_t seqno, cur_seqno, last_seqno;
-	int stuck, ret;
+	int stuck, ret, pipe;
 
 	mutex_lock(&dev->struct_mutex);
 
@@ -3656,9 +3672,23 @@ i915_gem_idle(struct drm_device *dev)
 	 */
 	dev_priv->mm.suspended = 1;
 
+	mutex_unlock(&dev->struct_mutex);
+
+	/* Wait for any outstanding flips */
+	spin_lock_irqsave(&dev_priv->vblank_lock, irqflags);
+	for (pipe = 0; pipe < 2; pipe++) {
+		list_for_each_entry_safe(obj_priv, tmp,
+
&dev_priv->mm.vblank_list[pipe],
+					 vblank_head) {
+			spin_unlock_irqrestore(&dev_priv->vblank_lock,
irqflags);
+			wait_for_completion(&obj_priv->vblank);
+			spin_lock_irqsave(&dev_priv->vblank_lock,
irqflags);
+		}
+	}
+	spin_unlock_irqrestore(&dev_priv->vblank_lock, irqflags);
+
 	/* 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);
 
@@ -4025,9 +4055,12 @@ i915_gem_load(struct drm_device *dev)
 	INIT_LIST_HEAD(&dev_priv->mm.flushing_list);
 	INIT_LIST_HEAD(&dev_priv->mm.inactive_list);
 	INIT_LIST_HEAD(&dev_priv->mm.request_list);
+	INIT_LIST_HEAD(&dev_priv->mm.vblank_list[0]);
+	INIT_LIST_HEAD(&dev_priv->mm.vblank_list[1]);
 	INIT_DELAYED_WORK(&dev_priv->mm.retire_work,
 			  i915_gem_retire_work_handler);
 	dev_priv->mm.next_gem_seqno = 1;
+	spin_lock_init(&dev_priv->vblank_lock);
 
 	/* Old X drivers will take 0-2 for front, back, depth buffers
*/ dev_priv->fence_reg_start = 3;
diff --git a/drivers/gpu/drm/i915/i915_irq.c
b/drivers/gpu/drm/i915/i915_irq.c index ee7ce7b..94aee6b 100644
--- a/drivers/gpu/drm/i915/i915_irq.c
+++ b/drivers/gpu/drm/i915/i915_irq.c
@@ -196,6 +196,35 @@ static void i915_hotplug_work_func(struct
work_struct *work) drm_sysfs_hotplug_event(dev);
 }
 
+static void i915_vblank_work_func(struct work_struct *work)
+{
+	drm_i915_private_t *dev_priv = container_of(work,
drm_i915_private_t,
+						    vblank_work);
+	struct drm_device *dev = dev_priv->dev;
+	struct drm_i915_gem_object *obj_priv, *tmp;
+	unsigned long irqflags;
+	int pipe, cur_seqno;
+
+	mutex_lock(&dev->struct_mutex);
+
+	spin_lock_irqsave(&dev_priv->vblank_lock, irqflags);
+	for (pipe = 0; pipe < 2; pipe++) {
+		list_for_each_entry_safe(obj_priv, tmp,
+
&dev_priv->mm.vblank_list[pipe],
+					 vblank_head) {
+			cur_seqno = i915_get_gem_seqno(dev);
+			if (i915_seqno_passed(cur_seqno,
+					      obj_priv->flip_seqno)) {
+				list_del_init(&obj_priv->vblank_head);
+				drm_vblank_put(dev, pipe);
+				complete(&obj_priv->vblank);
+			}
+		}
+	}
+	spin_unlock_irqrestore(&dev_priv->vblank_lock, irqflags);
+	mutex_unlock(&dev->struct_mutex);
+}
+
 irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS)
 {
 	struct drm_device *dev = (struct drm_device *) arg;
@@ -292,6 +321,9 @@ irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS)
 			drm_handle_vblank(dev, 1);
 		}
 
+		if (vblank)
+			schedule_work(&dev_priv->vblank_work);
+
 		if ((pipeb_stats & I915_LEGACY_BLC_EVENT_STATUS) ||
 		    (iir & I915_ASLE_INTERRUPT))
 			opregion_asle_intr(dev);
@@ -563,6 +595,7 @@ void i915_driver_irq_preinstall(struct drm_device *
dev) I915_WRITE(IER, 0x0);
 	(void) I915_READ(IER);
 	INIT_WORK(&dev_priv->hotplug_work, i915_hotplug_work_func);
+	INIT_WORK(&dev_priv->vblank_work, i915_vblank_work_func);
 }
 
 int i915_driver_irq_postinstall(struct drm_device *dev)
diff --git a/include/drm/i915_drm.h b/include/drm/i915_drm.h
index 67e3353..b022ba5 100644
--- a/include/drm/i915_drm.h
+++ b/include/drm/i915_drm.h
@@ -184,6 +184,7 @@ typedef struct _drm_i915_sarea {
 #define DRM_I915_GEM_GET_TILING	0x22
 #define DRM_I915_GEM_GET_APERTURE 0x23
 #define DRM_I915_GEM_MMAP_GTT	0x24
+#define DRM_I915_GEM_PAGE_FLIP	0x25
 
 #define DRM_IOCTL_I915_INIT		DRM_IOW( DRM_COMMAND_BASE +
DRM_I915_INIT, drm_i915_init_t) #define
DRM_IOCTL_I915_FLUSH		DRM_IO ( DRM_COMMAND_BASE +
DRM_I915_FLUSH) @@ -219,6 +220,7 @@ typedef struct _drm_i915_sarea
{ #define DRM_IOCTL_I915_GEM_SET_TILING	DRM_IOWR
(DRM_COMMAND_BASE + DRM_I915_GEM_SET_TILING, struct
drm_i915_gem_set_tiling) #define DRM_IOCTL_I915_GEM_GET_TILING
DRM_IOWR (DRM_COMMAND_BASE + DRM_I915_GEM_GET_TILING, struct
drm_i915_gem_get_tiling) #define DRM_IOCTL_I915_GEM_GET_APERTURE
DRM_IOR  (DRM_COMMAND_BASE + DRM_I915_GEM_GET_APERTURE, struct
drm_i915_gem_get_aperture) +#define DRM_IOCTL_I915_GEM_PAGE_FLIP
DRM_IOW (DRM_COMMAND_BASE + DRM_I915_GEM_PAGE_FLIP, struct
drm_i915_gem_page_flip) /* Allow drivers to submit batchbuffers
directly to hardware, relying
  * on the security mechanisms provided by hardware.
@@ -654,4 +656,20 @@ struct drm_i915_gem_get_aperture {
 	__u64 aper_available_size;
 };
 
+#define I915_PAGE_FLIP_WAIT (1<<0) /* block on page flip completion */
+
+struct drm_i915_gem_page_flip {
+	/** Handle of new front buffer */
+	uint32_t fb_id;
+	/**
+	 * crtc to flip
+	 */
+	uint32_t crtc_id;
+
+	/**
+	 * page flip flags (wait on flip only for now)
+	 */
+	uint32_t flags;
+};
+
 #endif				/* _I915_DRM_H_ */



More information about the Intel-gfx mailing list