[Intel-gfx] [PATCH v2 07/16] drm/i915: Add vblank based delayed watermark update mechanism

ville.syrjala at linux.intel.com ville.syrjala at linux.intel.com
Thu May 22 16:48:12 CEST 2014


From: Ville Syrjälä <ville.syrjala at linux.intel.com>

Add a mechanism by which you can queue up watermark update to happen
after the vblank counter has reached a certain value. The vblank
interrupt handler will schedule a work which will do the actual
watermark programming in process context.

v2: Rebase and s/intel_crtc/crtc/

Signed-off-by: Ville Syrjälä <ville.syrjala at linux.intel.com>
---
 drivers/gpu/drm/i915/i915_drv.h      |   2 +
 drivers/gpu/drm/i915/i915_irq.c      |  12 ++-
 drivers/gpu/drm/i915/intel_display.c |   1 +
 drivers/gpu/drm/i915/intel_drv.h     |  27 +++++++
 drivers/gpu/drm/i915/intel_pm.c      | 144 +++++++++++++++++++++++++++++++++++
 5 files changed, 183 insertions(+), 3 deletions(-)

diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
index a2302a7..c90d5ac 100644
--- a/drivers/gpu/drm/i915/i915_drv.h
+++ b/drivers/gpu/drm/i915/i915_drv.h
@@ -1541,6 +1541,8 @@ struct drm_i915_private {
 		 * state as well as the actual hardware registers
 		 */
 		struct mutex mutex;
+
+		struct work_struct work;
 	} wm;
 
 	struct i915_runtime_pm pm;
diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c
index 304f86a..c680020 100644
--- a/drivers/gpu/drm/i915/i915_irq.c
+++ b/drivers/gpu/drm/i915/i915_irq.c
@@ -2067,8 +2067,10 @@ static void ilk_display_irq_handler(struct drm_device *dev, u32 de_iir)
 		DRM_ERROR("Poison interrupt\n");
 
 	for_each_pipe(pipe) {
-		if (de_iir & DE_PIPE_VBLANK(pipe))
+		if (de_iir & DE_PIPE_VBLANK(pipe)) {
 			intel_pipe_handle_vblank(dev, pipe);
+			ilk_update_pipe_wm(dev, pipe);
+		}
 
 		if (de_iir & DE_PIPE_FIFO_UNDERRUN(pipe))
 			if (intel_set_cpu_fifo_underrun_reporting(dev, pipe, false))
@@ -2117,8 +2119,10 @@ static void ivb_display_irq_handler(struct drm_device *dev, u32 de_iir)
 		intel_opregion_asle_intr(dev);
 
 	for_each_pipe(pipe) {
-		if (de_iir & (DE_PIPE_VBLANK_IVB(pipe)))
+		if (de_iir & (DE_PIPE_VBLANK_IVB(pipe))) {
 			intel_pipe_handle_vblank(dev, pipe);
+			ilk_update_pipe_wm(dev, pipe);
+		}
 
 		/* plane/pipes map 1:1 on ilk+ */
 		if (de_iir & DE_PLANE_FLIP_DONE_IVB(pipe)) {
@@ -2260,8 +2264,10 @@ static irqreturn_t gen8_irq_handler(int irq, void *arg)
 			continue;
 
 		pipe_iir = I915_READ(GEN8_DE_PIPE_IIR(pipe));
-		if (pipe_iir & GEN8_PIPE_VBLANK)
+		if (pipe_iir & GEN8_PIPE_VBLANK) {
 			intel_pipe_handle_vblank(dev, pipe);
+			ilk_update_pipe_wm(dev, pipe);
+		}
 
 		if (pipe_iir & GEN8_PIPE_PRIMARY_FLIP_DONE) {
 			intel_prepare_page_flip(dev, pipe);
diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c
index a11bd78..408b238 100644
--- a/drivers/gpu/drm/i915/intel_display.c
+++ b/drivers/gpu/drm/i915/intel_display.c
@@ -10961,6 +10961,7 @@ static void intel_crtc_init(struct drm_device *dev, int pipe)
 		intel_crtc->plane = !pipe;
 	}
 
+	spin_lock_init(&intel_crtc->wm.lock);
 	init_waitqueue_head(&intel_crtc->vbl_wait);
 
 	BUG_ON(pipe >= ARRAY_SIZE(dev_priv->plane_to_crtc_mapping) ||
diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
index d75cc2b..72f01b1 100644
--- a/drivers/gpu/drm/i915/intel_drv.h
+++ b/drivers/gpu/drm/i915/intel_drv.h
@@ -409,6 +409,32 @@ struct intel_crtc {
 		 * protected by dev_priv->wm.mutex
 		 */
 		struct intel_pipe_wm active;
+		/*
+		 * watermarks queued for next vblank
+		 * protected by dev_priv->wm.mutex
+		 */
+		struct intel_pipe_wm pending;
+
+		/*
+		 * the vblank count after which we can switch over to 'pending'
+		 * protected by intel_crtc->wm.lock
+		 */
+		u32 pending_vbl_count;
+		/*
+		 * indicates that 'pending' contains changed watermarks
+		 * protected by intel_crtc->wm.lock
+		 */
+		bool dirty;
+		/*
+		 * watermark update has a vblank reference?
+		 * protected by intel_crtc->wm.lock
+		 */
+		bool vblank;
+
+		/*
+		 * protects some intel_crtc->wm state
+		 */
+		spinlock_t lock;
 	} wm;
 
 	wait_queue_head_t vbl_wait;
@@ -974,6 +1000,7 @@ void intel_runtime_pm_put(struct drm_i915_private *dev_priv);
 void intel_init_runtime_pm(struct drm_i915_private *dev_priv);
 void intel_fini_runtime_pm(struct drm_i915_private *dev_priv);
 void ilk_wm_get_hw_state(struct drm_device *dev);
+void ilk_update_pipe_wm(struct drm_device *dev, enum pipe pipe);
 
 
 /* intel_sdvo.c */
diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c
index 2a63418..6fc6416 100644
--- a/drivers/gpu/drm/i915/intel_pm.c
+++ b/drivers/gpu/drm/i915/intel_pm.c
@@ -2752,6 +2752,65 @@ static bool ilk_disable_lp_wm(struct drm_device *dev)
 	return changed;
 }
 
+static bool vbl_count_after_eq(struct drm_device *dev, u32 a, u32 b)
+{
+	u32 mask = dev->max_vblank_count;
+
+	/* just the msb please */
+	mask &= ~(mask >> 1);
+
+	return !((a - b) & mask);
+}
+
+static bool ilk_pending_watermarks_ready(struct intel_crtc *crtc)
+{
+	struct drm_device *dev = crtc->base.dev;
+	u32 vbl_count;
+
+	assert_spin_locked(&crtc->wm.lock);
+
+	if (!crtc->wm.dirty)
+		return false;
+
+	vbl_count = dev->driver->get_vblank_counter(dev, crtc->pipe);
+
+	if (!vbl_count_after_eq(dev, vbl_count, crtc->wm.pending_vbl_count))
+		return false;
+
+	if (crtc->wm.vblank) {
+		drm_vblank_put(dev, crtc->pipe);
+		crtc->wm.vblank = false;
+	}
+
+	return true;
+}
+
+static bool ilk_refresh_pending_watermarks(struct drm_device *dev)
+{
+	struct intel_crtc *crtc;
+	bool changed = false;
+
+	for_each_intel_crtc(dev, crtc) {
+		bool ready;
+
+		spin_lock_irq(&crtc->wm.lock);
+
+		ready = ilk_pending_watermarks_ready(crtc);
+		if (ready)
+			crtc->wm.dirty = false;
+
+		spin_unlock_irq(&crtc->wm.lock);
+
+		if (!ready)
+			continue;
+
+		crtc->wm.active = crtc->wm.pending;
+		changed = true;
+	}
+
+	return changed;
+}
+
 static void ilk_program_watermarks(struct drm_device *dev)
 {
 	struct drm_i915_private *dev_priv = dev->dev_private;
@@ -2810,6 +2869,87 @@ static void ilk_update_wm(struct drm_crtc *crtc)
 	mutex_unlock(&dev_priv->wm.mutex);
 }
 
+static void ilk_update_watermarks(struct drm_device *dev)
+{
+	bool changed;
+
+	changed = ilk_refresh_pending_watermarks(dev);
+
+	if (changed)
+		ilk_program_watermarks(dev);
+}
+
+static void ilk_setup_pending_watermarks(struct intel_crtc *crtc,
+					 const struct intel_pipe_wm *pipe_wm,
+					 u32 vbl_count)
+{
+	struct drm_device *dev = crtc->base.dev;
+	enum pipe pipe = crtc->pipe;
+
+	WARN(!crtc->active, "pipe %c should be enabled\n",
+	     pipe_name(pipe));
+
+	/* do the watermarks actually need changing? */
+	if (!memcmp(&crtc->wm.pending, pipe_wm, sizeof(*pipe_wm)))
+		return;
+
+	crtc->wm.pending = *pipe_wm;
+
+	spin_lock_irq(&crtc->wm.lock);
+	crtc->wm.pending_vbl_count = (vbl_count + 1) & dev->max_vblank_count;
+	crtc->wm.dirty = true;
+	spin_unlock_irq(&crtc->wm.lock);
+
+	/* try to update immediately */
+	ilk_update_watermarks(dev);
+
+	spin_lock_irq(&crtc->wm.lock);
+
+	/* did the immediate update succeed? */
+	if (!crtc->wm.dirty)
+		goto unlock;
+
+	/*
+	 * We might already have a pending watermark update, in
+	 * which case we shouldn't grab another vblank reference.
+	 */
+	if (!crtc->wm.vblank && drm_vblank_get(dev, pipe) == 0)
+		crtc->wm.vblank = true;
+
+	WARN(!crtc->wm.vblank,
+	     "unable to set up watermarks for pipe %c\n", pipe_name(pipe));
+
+ unlock:
+	spin_unlock_irq(&crtc->wm.lock);
+}
+
+static void ilk_watermark_work(struct work_struct *work)
+{
+	struct drm_i915_private *dev_priv =
+		container_of(work, struct drm_i915_private, wm.work);
+
+	mutex_lock(&dev_priv->wm.mutex);
+
+	ilk_update_watermarks(dev_priv->dev);
+
+	mutex_unlock(&dev_priv->wm.mutex);
+}
+
+/* Called from vblank irq */
+void ilk_update_pipe_wm(struct drm_device *dev, enum pipe pipe)
+{
+	struct drm_i915_private *dev_priv = dev->dev_private;
+	struct intel_crtc *crtc =
+		to_intel_crtc(dev_priv->pipe_to_crtc_mapping[pipe]);
+
+	spin_lock(&crtc->wm.lock);
+
+	if (ilk_pending_watermarks_ready(crtc))
+		schedule_work(&dev_priv->wm.work);
+
+	spin_unlock(&crtc->wm.lock);
+}
+
 static void ilk_update_sprite_wm(struct drm_plane *plane,
 				     struct drm_crtc *crtc,
 				     uint32_t sprite_width, int pixel_size,
@@ -2872,6 +3012,9 @@ static void _ilk_pipe_wm_hw_to_sw(struct drm_crtc *crtc)
 		for (level = 0; level <= max_level; level++)
 			active->wm[level].enable = true;
 	}
+
+	/* no update pending */
+	intel_crtc->wm.pending = intel_crtc->wm.active;
 }
 
 void ilk_wm_get_hw_state(struct drm_device *dev)
@@ -6385,6 +6528,7 @@ void intel_init_pm(struct drm_device *dev)
 	struct drm_i915_private *dev_priv = dev->dev_private;
 
 	mutex_init(&dev_priv->wm.mutex);
+	INIT_WORK(&dev_priv->wm.work, ilk_watermark_work);
 
 	if (HAS_FBC(dev)) {
 		if (INTEL_INFO(dev)->gen >= 7) {
-- 
1.8.5.5




More information about the Intel-gfx mailing list