[Intel-gfx] [RFC 15/15] drm/i915: Add intermediate watermarks

Matt Roper matthew.d.roper at intel.com
Wed May 20 19:12:27 PDT 2015


From: Matt Roper <matt at mattrope.com>

In addition to calculating final watermarks, let's also pre-calculate a
set of 'intermediate' watermark values.  These intermediate watermarks
merge the watermarks of the old state and the new state such that they
will satisfy the requirements of both states.  This means they can be
programmed immediately (without waiting for a vblank).  Once the vblank
does happen, we can then re-program watermarks to the more optimal
target value.

Note that we add a new 'pending' set of watermark values that the
intermediate values are originally written into.  The watermark
programming routine is updated to use these 'pending' values.  When a
vblank happens, the final 'target' values are copied to the 'pending'
structure so that all subsequent operations will make use of those
values.

Signed-off-by: Matt Roper <matthew.d.roper at intel.com>
---
 drivers/gpu/drm/i915/i915_drv.h      |  3 ++
 drivers/gpu/drm/i915/intel_atomic.c  | 15 +++++++-
 drivers/gpu/drm/i915/intel_display.c | 38 +++++++++++++++++--
 drivers/gpu/drm/i915/intel_drv.h     | 16 ++++++--
 drivers/gpu/drm/i915/intel_pm.c      | 72 +++++++++++++++++++++++++++++++-----
 5 files changed, 126 insertions(+), 18 deletions(-)

diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
index 5134101..f5feffc 100644
--- a/drivers/gpu/drm/i915/i915_drv.h
+++ b/drivers/gpu/drm/i915/i915_drv.h
@@ -564,6 +564,9 @@ struct drm_i915_display_funcs {
 			  struct dpll *best_clock);
 	int (*compute_pipe_wm)(struct drm_crtc *crtc,
 			       struct drm_atomic_state *state);
+	void (*compute_intermediate_wm)(struct drm_device *dev,
+					struct intel_crtc_state *newstate,
+					const struct intel_crtc_state *oldstate);
 	void (*update_wm)(struct drm_crtc *crtc);
 	void (*update_sprite_wm)(struct drm_plane *plane,
 				 struct drm_crtc *crtc,
diff --git a/drivers/gpu/drm/i915/intel_atomic.c b/drivers/gpu/drm/i915/intel_atomic.c
index 5294840..92c29b5 100644
--- a/drivers/gpu/drm/i915/intel_atomic.c
+++ b/drivers/gpu/drm/i915/intel_atomic.c
@@ -435,12 +435,25 @@ intel_check_crtc(struct drm_crtc *crtc,
 		 struct drm_crtc_state *state)
 {
 	struct drm_i915_private *dev_priv = to_i915(crtc->dev);
+	struct intel_crtc_state *new_state = to_intel_crtc_state(state);
+	struct intel_crtc_state *old_state = to_intel_crtc_state(crtc->state);
 	int ret;
 
 	if (!dev_priv->display.compute_pipe_wm)
 		return 0;
+	if (WARN_ON(!dev_priv->display.compute_intermediate_wm))
+		return 0;
 
 	ret = dev_priv->display.compute_pipe_wm(crtc, state->state);
+	if (ret)
+		return ret;
 
-	return ret;
+	/*
+	 * Calculate 'intermediate' watermarks that satisfy both the old state
+	 * and the new state.  We can program these immediately.
+	 */
+	dev_priv->display.compute_intermediate_wm(crtc->dev, new_state,
+						  old_state);
+
+	return 0;
 }
diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c
index b2012c9..45fdbec 100644
--- a/drivers/gpu/drm/i915/intel_display.c
+++ b/drivers/gpu/drm/i915/intel_display.c
@@ -13227,6 +13227,17 @@ do_update_watermarks(struct intel_crtc *intel_crtc,
 	dev_priv->display.program_watermarks(dev_priv);
 }
 
+static void
+do_latch_watermarks(struct intel_crtc *crtc,
+		    void *data,
+		    bool early,
+		    u32 seq)
+{
+	struct intel_crtc_state *cstate = to_intel_crtc_state(crtc->base.state);
+
+	cstate->wm.pending = cstate->wm.target;
+}
+
 static void intel_begin_crtc_commit(struct drm_crtc *crtc)
 {
 	struct drm_device *dev = crtc->dev;
@@ -13269,8 +13280,21 @@ static void intel_begin_crtc_commit(struct drm_crtc *crtc)
 	if (intel_crtc->atomic.pre_disable_primary)
 		intel_pre_disable_primary(crtc);
 
-	if (!HAS_ATOMIC_WM(dev_priv) && intel_crtc->atomic.update_wm)
+	/*
+	 * For platforms that support atomic watermarks, program the
+	 * intermediate watermarks immediately.  When vblank happens, 'pending'
+	 * values will be set to the final 'target' values and we'll do this
+	 * again to get the optimal watermarks.
+	 *
+	 * If a platform hasn't been transitioned to atomic watermarks yet,
+	 * we'll continue to update watermarks the old way, if flags tell
+	 * us to.
+	 */
+	if (HAS_ATOMIC_WM(dev_priv)) {
+		dev_priv->display.program_watermarks(dev_priv);
+	} else if (intel_crtc->atomic.update_wm) {
 		intel_update_watermarks(crtc);
+	}
 
 	intel_runtime_pm_get(dev_priv);
 
@@ -13291,11 +13315,19 @@ static void intel_finish_crtc_commit(struct drm_crtc *crtc)
 	/*
 	 * If this platform supports atomic watermarks, schedule a job to
 	 * update watermarks when the next vblank occurs.  Otherwise, just
-	 * update watermarks the old-fashioned way.
+	 * update watermarks the old-fashioned way.  Also, schedule a second
+	 * task that runs directly in the interrupt handler (rather than
+	 * in a wq task) that copies the 'target' watermark values to the
+	 * 'pending' watermark structure so that the proper values will be
+	 * used for all subsequent calculations.
+	 *
 	 */
-	if (HAS_ATOMIC_WM(dev_priv))
+	if (HAS_ATOMIC_WM(dev_priv)) {
 		intel_schedule_vblank_job(intel_crtc, do_update_watermarks,
 					  NULL, dev_priv->wq, 1);
+		intel_schedule_vblank_job(intel_crtc, do_latch_watermarks,
+					  NULL, NULL, 1);
+	}
 
 	if (intel_crtc->atomic.evade)
 		intel_pipe_update_end(intel_crtc,
diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
index 630e7c1..3f455ce 100644
--- a/drivers/gpu/drm/i915/intel_drv.h
+++ b/drivers/gpu/drm/i915/intel_drv.h
@@ -334,6 +334,11 @@ struct skl_pipe_wm {
 	uint32_t linetime;
 };
 
+union pipe_wm {
+	struct intel_pipe_wm ilk;
+	struct skl_pipe_wm skl;
+};
+
 struct intel_crtc_state {
 	struct drm_crtc_state base;
 
@@ -465,10 +470,13 @@ struct intel_crtc_state {
 
 	struct {
 		/* final, target watermarks for state */
-		union {
-			struct intel_pipe_wm ilk;
-			struct skl_pipe_wm skl;
-		} target;
+		union pipe_wm target;
+
+		/*
+		 * pending watermark (intermediate before vblank, target after
+		 * vblank fires)
+		 */
+		union pipe_wm pending;
 	} wm;
 };
 
diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c
index 7f0a0c1..887d39a 100644
--- a/drivers/gpu/drm/i915/intel_pm.c
+++ b/drivers/gpu/drm/i915/intel_pm.c
@@ -2060,7 +2060,7 @@ static void ilk_compute_wm_config(struct drm_device *dev,
 	for_each_intel_crtc(dev, intel_crtc) {
 		const struct intel_crtc_state *cstate =
 			to_intel_crtc_state(intel_crtc->base.state);
-		const struct intel_pipe_wm *wm = &cstate->wm.target.ilk;
+		const struct intel_pipe_wm *wm = &cstate->wm.pending.ilk;
 
 		if (!wm->pipe_enabled)
 			continue;
@@ -2071,6 +2071,24 @@ static void ilk_compute_wm_config(struct drm_device *dev,
 	}
 }
 
+static bool ilk_validate_pipe_wm(struct drm_device *dev,
+				 struct intel_wm_config *config,
+				 struct intel_pipe_wm *pipe_wm)
+{
+	struct ilk_wm_maximums max;
+
+	/* LP0 watermarks always use 1/2 DDB partitioning */
+	ilk_compute_wm_maximums(dev, 0, config, INTEL_DDB_PART_1_2, &max);
+
+	/* At least LP0 must be valid */
+	if (!ilk_validate_wm_level(0, &max, &pipe_wm->wm[0])) {
+		DRM_DEBUG_KMS("LP0 watermark invalid\n");
+		return false;
+	}
+
+	return true;
+}
+
 /* Compute new watermarks for the pipe */
 static int ilk_compute_pipe_wm(struct drm_crtc *crtc,
 			       struct drm_atomic_state *state)
@@ -2138,12 +2156,8 @@ static int ilk_compute_pipe_wm(struct drm_crtc *crtc,
 	if (IS_HASWELL(dev) || IS_BROADWELL(dev))
 		pipe_wm->linetime = hsw_compute_linetime_wm(dev, crtc);
 
-	/* LP0 watermarks always use 1/2 DDB partitioning */
-	ilk_compute_wm_maximums(dev, 0, &config, INTEL_DDB_PART_1_2, &max);
-
-	/* At least LP0 must be valid */
-	if (!ilk_validate_wm_level(0, &max, &pipe_wm->wm[0]))
-		return -EINVAL;
+	if (!ilk_validate_pipe_wm(dev, &config, pipe_wm))
+		return false;
 
 	ilk_compute_wm_reg_maximums(dev, 1, &max);
 
@@ -2168,6 +2182,40 @@ static int ilk_compute_pipe_wm(struct drm_crtc *crtc,
 }
 
 /*
+ * Build a set of 'intermediate' watermark values that satisfy both the old
+ * state and the new state.  These can be programmed to the hardware
+ * immediately.
+ */
+void ilk_compute_intermediate_wm(struct drm_device *dev,
+				 struct intel_crtc_state *newstate,
+				 const struct intel_crtc_state *oldstate)
+{
+	struct intel_pipe_wm *a = &newstate->wm.pending.ilk;
+	const struct intel_pipe_wm *b = &oldstate->wm.pending.ilk;
+	int level, max_level = ilk_wm_max_level(dev);
+
+	/*
+	 * Start with the final, target watermarks, then combine with the
+	 * current state's watermarks.
+	 */
+	*a = newstate->wm.target.ilk;
+	a->pipe_enabled |= b->pipe_enabled;
+	a->sprites_enabled |= b->sprites_enabled;
+	a->sprites_scaled |= b->sprites_scaled;
+
+	for (level = 0; level <= max_level; level++) {
+		struct intel_wm_level *a_wm = &a->wm[level];
+		const struct intel_wm_level *b_wm = &b->wm[level];
+
+		a_wm->enable &= b_wm->enable;
+		a_wm->pri_val = max(a_wm->pri_val, b_wm->pri_val);
+		a_wm->spr_val = max(a_wm->spr_val, b_wm->spr_val);
+		a_wm->cur_val = max(a_wm->cur_val, b_wm->cur_val);
+		a_wm->fbc_val = max(a_wm->fbc_val, b_wm->fbc_val);
+	}
+}
+
+/*
  * Merge the watermarks from all active pipes for a specific level.
  */
 static void ilk_merge_wm_level(struct drm_device *dev,
@@ -2181,7 +2229,7 @@ static void ilk_merge_wm_level(struct drm_device *dev,
 	for_each_intel_crtc(dev, intel_crtc) {
 		const struct intel_crtc_state *cstate =
 			to_intel_crtc_state(intel_crtc->base.state);
-		const struct intel_pipe_wm *active = &cstate->wm.target.ilk;
+		const struct intel_pipe_wm *active = &cstate->wm.pending.ilk;
 		const struct intel_wm_level *wm = &active->wm[level];
 
 		if (!active->pipe_enabled)
@@ -2330,12 +2378,12 @@ static void ilk_compute_wm_results(struct drm_device *dev,
 		const struct intel_crtc_state *cstate =
 			to_intel_crtc_state(intel_crtc->base.state);
 		enum pipe pipe = intel_crtc->pipe;
-		const struct intel_wm_level *r = &cstate->wm.target.ilk.wm[0];
+		const struct intel_wm_level *r = &cstate->wm.pending.ilk.wm[0];
 
 		if (WARN_ON(!r->enable))
 			continue;
 
-		results->wm_linetime[pipe] = cstate->wm.target.ilk.linetime;
+		results->wm_linetime[pipe] = cstate->wm.pending.ilk.linetime;
 
 		results->wm_pipe[pipe] =
 			(r->pri_val << WM0_PIPE_PLANE_SHIFT) |
@@ -3669,6 +3717,8 @@ static void ilk_pipe_wm_get_hw_state(struct drm_crtc *crtc)
 		for (level = 0; level <= max_level; level++)
 			active->wm[level].enable = true;
 	}
+
+	cstate->wm.pending.ilk = *active;
 }
 
 void ilk_wm_get_hw_state(struct drm_device *dev)
@@ -6626,6 +6676,8 @@ void intel_init_pm(struct drm_device *dev)
 			dev_priv->display.update_wm = ilk_update_wm;
 			dev_priv->display.update_sprite_wm = ilk_update_sprite_wm;
 			dev_priv->display.compute_pipe_wm = ilk_compute_pipe_wm;
+			dev_priv->display.compute_intermediate_wm =
+				ilk_compute_intermediate_wm;
 			dev_priv->display.program_watermarks = ilk_program_watermarks;
 		} else {
 			DRM_DEBUG_KMS("Failed to read display plane latency. "
-- 
1.8.5.1



More information about the Intel-gfx mailing list