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

Jesse Barnes jbarnes at virtuousgeek.org
Fri Feb 13 01:52:48 CET 2009


This adds a new ioctl for queueing a page flip with GEM objects.  There's a
completion per private object, that we use at execbuf time to wait for any
pending flips; it's cleared at vblank interrupt time when the flip occurs.
The kernel will figure out the pitch of the new frontbuffer automatically,
but the caller has to pass in dimensions and pipe information.

Signed-off-by: Jesse Barnes <jbarnes at virtuousgeek.org>

diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c
index cbb8224..2518ebd 100644
--- a/drivers/gpu/drm/i915/i915_dma.c
+++ b/drivers/gpu/drm/i915/i915_dma.c
@@ -829,6 +829,117 @@ static int i915_set_status_page(struct drm_device *dev, void *data,
 	return 0;
 }
 
+static int i915_pipe_to_plane(struct drm_device *dev, int pipe)
+{
+	drm_i915_private_t *dev_priv = dev->dev_private;
+	u32 reg, pipe_mask;
+
+	if (pipe == 0)
+		pipe_mask = DISPPLANE_SEL_PIPE_A;
+	else
+		pipe_mask = DISPPLANE_SEL_PIPE_B;
+
+	reg = I915_READ(DSPACNTR);
+	if ((reg & DISPLAY_PLANE_ENABLE) &&
+	    ((reg & DISPPLANE_SEL_PIPE_MASK) == pipe_mask))
+			return 0;
+
+	reg = I915_READ(DSPBCNTR);
+	if ((reg & DISPLAY_PLANE_ENABLE) &&
+	    ((reg & DISPPLANE_SEL_PIPE_MASK) == pipe_mask))
+			return 1;
+
+	return -1;
+}
+
+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_gem_object *obj;
+	struct drm_i915_gem_object *obj_priv;
+	unsigned long flags;
+	uint32_t offset;
+	int pitch, pipe, plane, tiled;
+	int ret = 0;
+	RING_LOCALS;
+
+	if (!(dev->driver->driver_features & DRIVER_GEM))
+		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;
+	}
+
+	if ((pipe = flip_data->pipe) > 1) {
+		DRM_ERROR("bad pipe\n");
+		return -EINVAL;
+	}
+
+	plane = i915_pipe_to_plane(dev, pipe);
+	if (plane < 0) {
+		DRM_ERROR("bad pipe (no planes enabled?)\n");
+		return -EINVAL;
+	}
+
+	obj = drm_gem_object_lookup(dev, file_priv, flip_data->handle);
+	if (obj == NULL)
+		return -EBADF;
+
+	obj_priv = obj->driver_private;
+	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_unref;
+	}
+
+	ret = i915_gem_object_pin(obj, 0);
+	if (ret) {
+		DRM_ERROR("failed to pin object for flip\n");
+		ret = -EBUSY;
+		goto out_unref;
+	}
+
+	offset = obj_priv->gtt_offset;
+	pitch = obj_priv->stride;
+	tiled = !!(obj_priv->tiling_mode == I915_TILING_X);
+
+	/*
+	 * Queue the flip with the lock held in case the flip happens
+	 * immediately.
+	 */
+	spin_lock_irqsave(&dev_priv->vblank_lock, flags);
+
+	BEGIN_LP_RING(4);
+	OUT_RING(CMD_OP_DISPLAYBUFFER_INFO | (plane << 20));
+	OUT_RING((pitch / 8) << 3); /* flip queue and/or pitch */
+	OUT_RING(offset | tiled);
+	OUT_RING(((flip_data->x - 1) << 16) | (flip_data->y - 1));
+	ADVANCE_LP_RING();
+
+	list_add_tail(&obj_priv->vblank_head, &dev_priv->mm.vblank_list[pipe]);
+	drm_vblank_get(dev, pipe);
+
+	spin_unlock_irqrestore(&dev_priv->vblank_lock, flags);
+
+	if (flip_data->flags & I915_PAGE_FLIP_WAIT)
+		wait_for_completion(&obj_priv->vblank);
+
+out_unref:
+	mutex_lock(&dev->struct_mutex);
+	drm_gem_object_unreference(obj);
+	mutex_unlock(&dev->struct_mutex);
+
+	return ret;
+}
+
 /**
  * i915_probe_agp - get AGP bootup configuration
  * @pdev: PCI device
@@ -1300,6 +1411,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 a8d3256..c77730b 100644
--- a/drivers/gpu/drm/i915/i915_drv.h
+++ b/drivers/gpu/drm/i915/i915_drv.h
@@ -161,6 +161,9 @@ typedef struct drm_i915_private {
 	u32 irq_mask_reg;
 	u32 pipestat[2];
 
+	/** Protects vblank list */
+	spinlock_t vblank_lock;
+
 	u32 hotplug_supported_mask;
 	struct work_struct hotplug_work;
 
@@ -341,6 +344,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 +400,9 @@ 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 */
+	struct list_head vblank_head;
+
 	/**
 	 * 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
@@ -460,6 +471,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;
 };
 
 /**
diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c
index a3df37c..48a556f 100644
--- a/drivers/gpu/drm/i915/i915_gem.c
+++ b/drivers/gpu/drm/i915/i915_gem.c
@@ -2459,6 +2459,7 @@ i915_gem_execbuffer(struct drm_device *dev, void *data,
 	struct drm_i915_gem_exec_object *exec_list = NULL;
 	struct drm_gem_object **object_list = NULL;
 	struct drm_gem_object *batch_obj;
+	unsigned long irqflags;
 	int ret, i, pinned = 0;
 	uint64_t exec_offset;
 	uint32_t seqno, flush_domains;
@@ -2529,6 +2530,17 @@ i915_gem_execbuffer(struct drm_device *dev, void *data,
 	for (pin_tries = 0; ; pin_tries++) {
 		ret = 0;
 		for (i = 0; i < args->buffer_count; i++) {
+			struct drm_i915_gem_object *obj_priv;
+
+			obj_priv = object_list[i]->driver_private;
+			spin_lock_irqsave(&dev_priv->vblank_lock, irqflags);
+			if (!list_empty(&obj_priv->vblank_head)) {
+				spin_unlock_irqrestore(&dev_priv->vblank_lock,
+						       irqflags);
+				wait_for_completion(&obj_priv->vblank);
+			} else
+				spin_unlock_irqrestore(&dev_priv->vblank_lock,
+						       irqflags);
 			object_list[i]->pending_read_domains = 0;
 			object_list[i]->pending_write_domain = 0;
 			ret = i915_gem_object_pin_and_relocate(object_list[i],
@@ -2906,6 +2918,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;
 }
@@ -3327,6 +3342,8 @@ 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;
diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c
index 019140e..96a7b38 100644
--- a/drivers/gpu/drm/i915/i915_irq.c
+++ b/drivers/gpu/drm/i915/i915_irq.c
@@ -196,6 +196,22 @@ static void i915_hotplug_work_func(struct work_struct *work)
 	drm_sysfs_hotplug_event(dev);
 }
 
+static void i915_complete_vblank(struct drm_device *dev, int pipe)
+{
+	drm_i915_private_t *dev_priv = dev->dev_private;
+	struct drm_i915_gem_object *obj_priv, *tmp;
+	unsigned long irqflags;
+
+	spin_lock_irqsave(&dev_priv->vblank_lock,irqflags);
+	list_for_each_entry_safe(obj_priv, tmp, &dev_priv->mm.vblank_list[pipe],
+				 vblank_head) {
+		complete(&obj_priv->vblank);
+		list_del_init(&obj_priv->vblank_head);
+		drm_vblank_put(dev, pipe);
+	}
+	spin_unlock_irqrestore(&dev_priv->vblank_lock,irqflags);
+}
+
 irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS)
 {
 	struct drm_device *dev = (struct drm_device *) arg;
@@ -284,11 +300,13 @@ irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS)
 
 		if (pipea_stats & vblank_status) {
 			vblank++;
+			i915_complete_vblank(dev, 0);
 			drm_handle_vblank(dev, 0);
 		}
 
 		if (pipeb_stats & vblank_status) {
 			vblank++;
+			i915_complete_vblank(dev, 1);
 			drm_handle_vblank(dev, 1);
 		}
 
diff --git a/include/drm/i915_drm.h b/include/drm/i915_drm.h
index 912cd52..0678e47 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,27 @@ struct drm_i915_gem_get_aperture {
 	uint64_t 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 handle;
+
+	/**
+	 * page flip flags (wait on flip only for now)
+	 */
+	uint32_t flags;
+
+	/**
+	 * pipe to flip
+	 */
+	uint32_t pipe;
+
+	/**
+	 * screen dimensions for flip
+	 */
+	uint32_t x;
+	uint32_t y;
+};
+
 #endif				/* _I915_DRM_H_ */



More information about the Intel-gfx mailing list