[PATCH] drm/i915: make sprite buffer switching asynchronous

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Move the vblank wait (needed to avoid displaying an unpinned frame) to a
work handler.  Special case benchmarks and other apps that may call
set_plane at a high frequency: we can unpin their objects directly
unless they're "live".

Signed-off-by: Jesse Barnes <jbarnes at virtuousgeek.org>
---
 drivers/gpu/drm/i915/i915_reg.h     |    4 ++
 drivers/gpu/drm/i915/intel_drv.h    |    1 +
 drivers/gpu/drm/i915/intel_sprite.c |   80 ++++++++++++++++++++++++++++++++++-
 3 files changed, 83 insertions(+), 2 deletions(-)

diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h
index addad69..8b95dd9 100644
--- a/drivers/gpu/drm/i915/i915_reg.h
+++ b/drivers/gpu/drm/i915/i915_reg.h
@@ -2820,6 +2820,7 @@
 #define DVSSTRIDE(pipe) _PIPE(pipe, _DVSASTRIDE, _DVSBSTRIDE)
 #define DVSPOS(pipe) _PIPE(pipe, _DVSAPOS, _DVSBPOS)
 #define DVSSURF(pipe) _PIPE(pipe, _DVSASURF, _DVSBSURF)
+#define DVSSURFLIVE(pipe) _PIPE(pipe, _DVSASURFLIVE, _DVSBSURFLIVE)
 #define DVSKEYMAX(pipe) _PIPE(pipe, _DVSAKEYMAXVAL, _DVSBKEYMAXVAL)
 #define DVSSIZE(pipe) _PIPE(pipe, _DVSASIZE, _DVSBSIZE)
 #define DVSSCALE(pipe) _PIPE(pipe, _DVSASCALE, _DVSBSCALE)
@@ -2860,6 +2861,7 @@
 #define _SPRA_SURF		0x7029c
 #define _SPRA_KEYMAX		0x702a0
 #define _SPRA_TILEOFF		0x702a4
+#define _SPRA_SURFLIVE		0x702ac
 #define _SPRA_SCALE		0x70304
 #define   SPRITE_SCALE_ENABLE	(1<<31)
 #define   SPRITE_FILTER_MASK	(3<<29)
@@ -2880,6 +2882,7 @@
 #define _SPRB_SURF		0x7129c
 #define _SPRB_KEYMAX		0x712a0
 #define _SPRB_TILEOFF		0x712a4
+#define _SPRB_SURFLIVE		0x712ac
 #define _SPRB_SCALE		0x71304
 #define _SPRB_GAMC		0x71400
 
@@ -2891,6 +2894,7 @@
 #define SPRKEYVAL(pipe) _PIPE(pipe, _SPRA_KEYVAL, _SPRB_KEYVAL)
 #define SPRKEYMSK(pipe) _PIPE(pipe, _SPRA_KEYMSK, _SPRB_KEYMSK)
 #define SPRSURF(pipe) _PIPE(pipe, _SPRA_SURF, _SPRB_SURF)
+#define SPRSURFLIVE(pipe) _PIPE(pipe, _SPRA_SURFLIVE, _SPRB_SURFLIVE)
 #define SPRKEYMAX(pipe) _PIPE(pipe, _SPRA_KEYMAX, _SPRB_KEYMAX)
 #define SPRTILEOFF(pipe) _PIPE(pipe, _SPRA_TILEOFF, _SPRB_TILEOFF)
 #define SPRSCALE(pipe) _PIPE(pipe, _SPRA_SCALE, _SPRB_SCALE)
diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
index 9cec6c3..badfdf4 100644
--- a/drivers/gpu/drm/i915/intel_drv.h
+++ b/drivers/gpu/drm/i915/intel_drv.h
@@ -190,6 +190,7 @@ struct intel_plane {
 			       struct drm_intel_sprite_colorkey *key);
 	void (*get_colorkey)(struct drm_plane *plane,
 			     struct drm_intel_sprite_colorkey *key);
+	u32 (*current_surface)(struct drm_plane *plane);
 };
 
 #define to_intel_crtc(x) container_of(x, struct intel_crtc, base)
diff --git a/drivers/gpu/drm/i915/intel_sprite.c b/drivers/gpu/drm/i915/intel_sprite.c
index dbdc5c0..dfe4f14 100644
--- a/drivers/gpu/drm/i915/intel_sprite.c
+++ b/drivers/gpu/drm/i915/intel_sprite.c
@@ -36,6 +36,13 @@
 #include "i915_drm.h"
 #include "i915_drv.h"
 
+struct intel_sprite_unpin_work {
+	struct work_struct work;
+	struct drm_device *dev;
+	struct drm_i915_gem_object *old_fb_obj;
+	int pipe;
+};
+
 static void
 ivb_update_plane(struct drm_plane *plane, struct drm_framebuffer *fb,
 		 struct drm_i915_gem_object *obj, int crtc_x, int crtc_y,
@@ -208,6 +215,16 @@ ivb_get_colorkey(struct drm_plane *plane, struct drm_intel_sprite_colorkey *key)
 		key->flags = I915_SET_COLORKEY_NONE;
 }
 
+static u32
+ivb_current_surface(struct drm_plane *plane)
+{
+	struct intel_plane *intel_plane;
+
+	intel_plane = to_intel_plane(plane);
+
+	return SPRSURFLIVE(intel_plane->pipe);
+}
+
 static void
 snb_update_plane(struct drm_plane *plane, struct drm_framebuffer *fb,
 		 struct drm_i915_gem_object *obj, int crtc_x, int crtc_y,
@@ -387,6 +404,63 @@ snb_get_colorkey(struct drm_plane *plane, struct drm_intel_sprite_colorkey *key)
 		key->flags = I915_SET_COLORKEY_NONE;
 }
 
+static u32
+snb_current_surface(struct drm_plane *plane)
+{
+	struct intel_plane *intel_plane;
+
+	intel_plane = to_intel_plane(plane);
+
+	return DVSSURFLIVE(intel_plane->pipe);
+}
+
+static void intel_sprite_unpin_work_fn(struct work_struct *__work)
+{
+	struct intel_sprite_unpin_work *work =
+		container_of(__work, struct intel_sprite_unpin_work, work);
+
+	intel_wait_for_vblank(work->dev, work->pipe);
+
+	mutex_lock(&work->dev->struct_mutex);
+	intel_unpin_fb_obj(work->old_fb_obj);
+	mutex_unlock(&work->dev->struct_mutex);
+	kfree(work);
+}
+
+static void
+intel_queue_unpin(struct drm_plane *plane,
+		  struct drm_i915_gem_object *obj, int pipe)
+{
+	struct intel_plane *intel_plane = to_intel_plane(plane);
+	struct intel_sprite_unpin_work *work;
+
+	/*
+	 * If the surface is currently being scanned out, we need to
+	 * wait until the next vblank event latches in the new base address
+	 * before we unpin it, or we may end up displaying the wrong data.
+	 * However, if the old object isn't currently 'live', we can just
+	 * unpin right away.
+	 */
+	if (intel_plane->current_surface(plane) != obj->gtt_offset) {
+		intel_unpin_fb_obj(obj);
+		return;
+	}
+
+	work = kzalloc(sizeof *work, GFP_KERNEL);
+	if (!work) {
+		/* Avoids a memory leak, but may result in a corrupt frame */
+		intel_unpin_fb_obj(obj);
+		return;
+	}
+
+	INIT_WORK(&work->work, intel_sprite_unpin_work_fn);
+	work->old_fb_obj = obj;
+	work->dev = obj->base.dev;
+	work->pipe = pipe;
+
+	schedule_work(&work->work);
+}
+
 static int
 intel_update_plane(struct drm_plane *plane, struct drm_crtc *crtc,
 		   struct drm_framebuffer *fb, int crtc_x, int crtc_y,
@@ -490,7 +564,7 @@ intel_update_plane(struct drm_plane *plane, struct drm_crtc *crtc,
 
 	/* Unpin old obj after new one is active to avoid ugliness */
 	if (old_obj)
-		intel_unpin_fb_obj(old_obj);
+		intel_queue_unpin(plane, old_obj, pipe);
 
 out_unlock:
 	mutex_unlock(&dev->struct_mutex);
@@ -516,7 +590,7 @@ intel_disable_plane(struct drm_plane *plane)
 		goto out;
 
 	mutex_lock(&dev->struct_mutex);
-	intel_unpin_fb_obj(intel_plane->obj);
+	intel_queue_unpin(plane, intel_plane->obj, intel_plane->pipe);
 	intel_plane->obj = NULL;
 	mutex_unlock(&dev->struct_mutex);
 out:
@@ -631,12 +705,14 @@ intel_plane_init(struct drm_device *dev, enum pipe pipe)
 		intel_plane->disable_plane = snb_disable_plane;
 		intel_plane->update_colorkey = snb_update_colorkey;
 		intel_plane->get_colorkey = snb_get_colorkey;
+		intel_plane->current_surface = snb_current_surface;
 	} else if (IS_GEN7(dev)) {
 		intel_plane->max_downscale = 2;
 		intel_plane->update_plane = ivb_update_plane;
 		intel_plane->disable_plane = ivb_disable_plane;
 		intel_plane->update_colorkey = ivb_update_colorkey;
 		intel_plane->get_colorkey = ivb_get_colorkey;
+		intel_plane->current_surface = ivb_current_surface;
 	}
 
 	intel_plane->pipe = pipe;
-- 
1.7.4.1



[Index of Archives]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]
  Powered by Linux