[Intel-gfx] [PATCH 15/24] drm/i915: Add vblank based delayed watermark update mechanism

ville.syrjala at linux.intel.com ville.syrjala at linux.intel.com
Fri Mar 7 17:32:22 CET 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.

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 0b19723..f79f1b4 100644
--- a/drivers/gpu/drm/i915/i915_drv.h
+++ b/drivers/gpu/drm/i915/i915_drv.h
@@ -1606,6 +1606,8 @@ typedef struct drm_i915_private {
 		 * state as well as the actual hardware registers
 		 */
 		struct mutex mutex;
+
+		struct work_struct work;
 	} wm;
 
 	struct i915_package_c8 pc8;
diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c
index bd79224..052da8c 100644
--- a/drivers/gpu/drm/i915/i915_irq.c
+++ b/drivers/gpu/drm/i915/i915_irq.c
@@ -1909,8 +1909,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))
@@ -1959,8 +1961,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)) {
@@ -2102,8 +2106,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_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 c028b5c..aa9acc5 100644
--- a/drivers/gpu/drm/i915/intel_display.c
+++ b/drivers/gpu/drm/i915/intel_display.c
@@ -10283,6 +10283,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 8e32d69..c9d2603 100644
--- a/drivers/gpu/drm/i915/intel_drv.h
+++ b/drivers/gpu/drm/i915/intel_drv.h
@@ -389,6 +389,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;
@@ -904,6 +930,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 d8adcb3..de8fb65 100644
--- a/drivers/gpu/drm/i915/intel_pm.c
+++ b/drivers/gpu/drm/i915/intel_pm.c
@@ -2701,6 +2701,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 *intel_crtc)
+{
+	struct drm_device *dev = intel_crtc->base.dev;
+	u32 vbl_count;
+
+	assert_spin_locked(&intel_crtc->wm.lock);
+
+	if (!intel_crtc->wm.dirty)
+		return false;
+
+	vbl_count = dev->driver->get_vblank_counter(dev, intel_crtc->pipe);
+
+	if (!vbl_count_after_eq(dev, vbl_count, intel_crtc->wm.pending_vbl_count))
+		return false;
+
+	if (intel_crtc->wm.vblank) {
+		drm_vblank_put(dev, intel_crtc->pipe);
+		intel_crtc->wm.vblank = false;
+	}
+
+	return true;
+}
+
+static bool ilk_refresh_pending_watermarks(struct drm_device *dev)
+{
+	struct intel_crtc *intel_crtc;
+	bool changed = false;
+
+	list_for_each_entry(intel_crtc, &dev->mode_config.crtc_list, base.head) {
+		bool ready;
+
+		spin_lock_irq(&intel_crtc->wm.lock);
+
+		ready = ilk_pending_watermarks_ready(intel_crtc);
+		if (ready)
+			intel_crtc->wm.dirty = false;
+
+		spin_unlock_irq(&intel_crtc->wm.lock);
+
+		if (!ready)
+			continue;
+
+		intel_crtc->wm.active = intel_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;
@@ -2759,6 +2818,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 *intel_crtc,
+					 const struct intel_pipe_wm *pipe_wm,
+					 u32 vbl_count)
+{
+	struct drm_device *dev = intel_crtc->base.dev;
+	enum pipe pipe = intel_crtc->pipe;
+
+	WARN(!intel_crtc->active, "pipe %c should be enabled\n",
+	     pipe_name(pipe));
+
+	/* do the watermarks actually need changing? */
+	if (!memcmp(&intel_crtc->wm.pending, pipe_wm, sizeof(*pipe_wm)))
+		return;
+
+	intel_crtc->wm.pending = *pipe_wm;
+
+	spin_lock_irq(&intel_crtc->wm.lock);
+	intel_crtc->wm.pending_vbl_count = (vbl_count + 1) & dev->max_vblank_count;
+	intel_crtc->wm.dirty = true;
+	spin_unlock_irq(&intel_crtc->wm.lock);
+
+	/* try to update immediately */
+	ilk_update_watermarks(dev);
+
+	spin_lock_irq(&intel_crtc->wm.lock);
+
+	/* did the immediate update succeed? */
+	if (!intel_crtc->wm.dirty)
+		goto unlock;
+
+	/*
+	 * We might already have a pending watermark update, in
+	 * which case we shouldn't grab another vblank reference.
+	 */
+	if (!intel_crtc->wm.vblank && drm_vblank_get(dev, pipe) == 0)
+		intel_crtc->wm.vblank = true;
+
+	WARN(!intel_crtc->wm.vblank,
+	     "unable to set up watermarks for pipe %c\n", pipe_name(pipe));
+
+ unlock:
+	spin_unlock_irq(&intel_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 *intel_crtc =
+		to_intel_crtc(dev_priv->pipe_to_crtc_mapping[pipe]);
+
+	spin_lock(&intel_crtc->wm.lock);
+
+	if (ilk_pending_watermarks_ready(intel_crtc))
+		schedule_work(&dev_priv->wm.work);
+
+	spin_unlock(&intel_crtc->wm.lock);
+}
+
 static void ilk_update_sprite_wm(struct drm_plane *plane,
 				     struct drm_crtc *crtc,
 				     uint32_t sprite_width, int pixel_size,
@@ -2821,6 +2961,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)
@@ -5776,6 +5919,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.3.2




More information about the Intel-gfx mailing list