All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 00/15] drm/i915: Two stage watermarks for g4x
@ 2017-04-21 18:14 ville.syrjala
  2017-04-21 18:14 ` [PATCH 01/15] drm/i915: s/vlv_plane_wm_compute/vlv_raw_plane_wm_compute/ etc ville.syrjala
                   ` (16 more replies)
  0 siblings, 17 replies; 21+ messages in thread
From: ville.syrjala @ 2017-04-21 18:14 UTC (permalink / raw)
  To: intel-gfx

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

This makes g4x follow the two stage watermark programming approach as
well, and as a bonus exposes the video sprites on g4x.

There is one slight problem with merging the wms from multiple pipes;
If one pipe is currently enabled and we're about to enabled another one,
we should turn off CxSR before the second pipe gets enabled as the FIFO
will get repartitioned. This could also happen when there's a parallel
watermark update on the first pipe, so a dumb approach of just disabling
CxSR in the modeset path doesn't really work. I think the proper fix
will involve some shuffling of code in the modeset path because it's
currently a bit of a mess. So with the current code there could be an
occasional underrun reported when enabling the second pipe.

Entire series available here:
git://github.com/vsyrjala/linux.git g4x_atomic_wm_8

Ville Syrjälä (15):
  drm/i915: s/vlv_plane_wm_compute/vlv_raw_plane_wm_compute/ etc.
  drm/i915: Drop the debug message from vlv_get_fifo_size()
  drm/i915: s/vlv_num_wm_levels/intel_wm_num_levels/
  drm/i915: Rename bunch of vlv_ watermark structures to g4x_
  drm/i915: Make vlv/chv watermark debug print less cryptic
  drm/i915: Document CxSR
  drm/i915: Fix cursor 'cpp' in watermark calculatins for old platforms
  drm/i915: Fix the g4x watermark TLB miss workaround
  drm/i915: Refactor the g4x TLB miss w/a to a helper
  drm/i915: Refactor wm calculations
  drm/i915: Apply the g4x TLB miss w/a to SR watermarks as well
  drm/i915: Two stage watermarks for g4x
  drm/i915: Enable HPLL watermarks on g4x
  drm/i915: Add g4x watermark tracepoint
  drm/i915: Add support for sprites on g4x

 drivers/gpu/drm/i915/i915_debugfs.c      |   12 +-
 drivers/gpu/drm/i915/i915_drv.h          |   20 +-
 drivers/gpu/drm/i915/i915_trace.h        |   49 ++
 drivers/gpu/drm/i915/intel_device_info.c |    2 +-
 drivers/gpu/drm/i915/intel_display.c     |   29 +-
 drivers/gpu/drm/i915/intel_drv.h         |   34 +-
 drivers/gpu/drm/i915/intel_pm.c          | 1254 ++++++++++++++++++++++--------
 drivers/gpu/drm/i915/intel_sprite.c      |   18 +-
 8 files changed, 1081 insertions(+), 337 deletions(-)

-- 
2.10.2

_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 21+ messages in thread

* [PATCH 01/15] drm/i915: s/vlv_plane_wm_compute/vlv_raw_plane_wm_compute/ etc.
  2017-04-21 18:14 [PATCH 00/15] drm/i915: Two stage watermarks for g4x ville.syrjala
@ 2017-04-21 18:14 ` ville.syrjala
  2017-04-21 18:14 ` [PATCH 02/15] drm/i915: Drop the debug message from vlv_get_fifo_size() ville.syrjala
                   ` (15 subsequent siblings)
  16 siblings, 0 replies; 21+ messages in thread
From: ville.syrjala @ 2017-04-21 18:14 UTC (permalink / raw)
  To: intel-gfx

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

Rename some of the vlv wm functions to reflect the fact that they
operate on the "raw" watermarks.

Signed-off-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
---
 drivers/gpu/drm/i915/intel_pm.c | 22 +++++++++++-----------
 1 file changed, 11 insertions(+), 11 deletions(-)

diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c
index cacb65fa2dd5..23fff9167d77 100644
--- a/drivers/gpu/drm/i915/intel_pm.c
+++ b/drivers/gpu/drm/i915/intel_pm.c
@@ -1194,8 +1194,8 @@ static bool vlv_raw_plane_wm_set(struct intel_crtc_state *crtc_state,
 	return dirty;
 }
 
-static bool vlv_plane_wm_compute(struct intel_crtc_state *crtc_state,
-				 const struct intel_plane_state *plane_state)
+static bool vlv_raw_plane_wm_compute(struct intel_crtc_state *crtc_state,
+				     const struct intel_plane_state *plane_state)
 {
 	struct intel_plane *plane = to_intel_plane(plane_state->base.plane);
 	enum plane_id plane_id = plane->id;
@@ -1234,8 +1234,8 @@ static bool vlv_plane_wm_compute(struct intel_crtc_state *crtc_state,
 	return dirty;
 }
 
-static bool vlv_plane_wm_is_valid(const struct intel_crtc_state *crtc_state,
-				  enum plane_id plane_id, int level)
+static bool vlv_raw_plane_wm_is_valid(const struct intel_crtc_state *crtc_state,
+				      enum plane_id plane_id, int level)
 {
 	const struct vlv_pipe_wm *raw =
 		&crtc_state->wm.vlv.raw[level];
@@ -1245,12 +1245,12 @@ static bool vlv_plane_wm_is_valid(const struct intel_crtc_state *crtc_state,
 	return raw->plane[plane_id] <= fifo_state->plane[plane_id];
 }
 
-static bool vlv_crtc_wm_is_valid(const struct intel_crtc_state *crtc_state, int level)
+static bool vlv_raw_crtc_wm_is_valid(const struct intel_crtc_state *crtc_state, int level)
 {
-	return vlv_plane_wm_is_valid(crtc_state, PLANE_PRIMARY, level) &&
-		vlv_plane_wm_is_valid(crtc_state, PLANE_SPRITE0, level) &&
-		vlv_plane_wm_is_valid(crtc_state, PLANE_SPRITE1, level) &&
-		vlv_plane_wm_is_valid(crtc_state, PLANE_CURSOR, level);
+	return vlv_raw_plane_wm_is_valid(crtc_state, PLANE_PRIMARY, level) &&
+		vlv_raw_plane_wm_is_valid(crtc_state, PLANE_SPRITE0, level) &&
+		vlv_raw_plane_wm_is_valid(crtc_state, PLANE_SPRITE1, level) &&
+		vlv_raw_plane_wm_is_valid(crtc_state, PLANE_CURSOR, level);
 }
 
 static int vlv_compute_pipe_wm(struct intel_crtc_state *crtc_state)
@@ -1279,7 +1279,7 @@ static int vlv_compute_pipe_wm(struct intel_crtc_state *crtc_state)
 		    old_plane_state->base.crtc != &crtc->base)
 			continue;
 
-		if (vlv_plane_wm_compute(crtc_state, plane_state))
+		if (vlv_raw_plane_wm_compute(crtc_state, plane_state))
 			dirty |= BIT(plane->id);
 	}
 
@@ -1325,7 +1325,7 @@ static int vlv_compute_pipe_wm(struct intel_crtc_state *crtc_state)
 		const struct vlv_pipe_wm *raw = &crtc_state->wm.vlv.raw[level];
 		const int sr_fifo_size = INTEL_INFO(dev_priv)->num_pipes * 512 - 1;
 
-		if (!vlv_crtc_wm_is_valid(crtc_state, level))
+		if (!vlv_raw_crtc_wm_is_valid(crtc_state, level))
 			break;
 
 		for_each_plane_id_on_crtc(crtc, plane_id) {
-- 
2.10.2

_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH 02/15] drm/i915: Drop the debug message from vlv_get_fifo_size()
  2017-04-21 18:14 [PATCH 00/15] drm/i915: Two stage watermarks for g4x ville.syrjala
  2017-04-21 18:14 ` [PATCH 01/15] drm/i915: s/vlv_plane_wm_compute/vlv_raw_plane_wm_compute/ etc ville.syrjala
@ 2017-04-21 18:14 ` ville.syrjala
  2017-04-21 18:14 ` [PATCH 03/15] drm/i915: s/vlv_num_wm_levels/intel_wm_num_levels/ ville.syrjala
                   ` (14 subsequent siblings)
  16 siblings, 0 replies; 21+ messages in thread
From: ville.syrjala @ 2017-04-21 18:14 UTC (permalink / raw)
  To: intel-gfx

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

Seeing the display FIFO sizes at driver load time doesn't really provide
anything useful for us, so let's just drop the debug message. One can
always use eg. intel_watermarks to dump out the hardware settings prior
to loading the driver.

Signed-off-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
---
 drivers/gpu/drm/i915/intel_pm.c | 7 -------
 1 file changed, 7 deletions(-)

diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c
index 23fff9167d77..2c63abe2039c 100644
--- a/drivers/gpu/drm/i915/intel_pm.c
+++ b/drivers/gpu/drm/i915/intel_pm.c
@@ -454,13 +454,6 @@ static void vlv_get_fifo_size(struct intel_crtc_state *crtc_state)
 	fifo_state->plane[PLANE_SPRITE0] = sprite1_start - sprite0_start;
 	fifo_state->plane[PLANE_SPRITE1] = 511 - sprite1_start;
 	fifo_state->plane[PLANE_CURSOR] = 63;
-
-	DRM_DEBUG_KMS("Pipe %c FIFO size: %d/%d/%d/%d\n",
-		      pipe_name(pipe),
-		      fifo_state->plane[PLANE_PRIMARY],
-		      fifo_state->plane[PLANE_SPRITE0],
-		      fifo_state->plane[PLANE_SPRITE1],
-		      fifo_state->plane[PLANE_CURSOR]);
 }
 
 static int i9xx_get_fifo_size(struct drm_i915_private *dev_priv, int plane)
-- 
2.10.2

_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH 03/15] drm/i915: s/vlv_num_wm_levels/intel_wm_num_levels/
  2017-04-21 18:14 [PATCH 00/15] drm/i915: Two stage watermarks for g4x ville.syrjala
  2017-04-21 18:14 ` [PATCH 01/15] drm/i915: s/vlv_plane_wm_compute/vlv_raw_plane_wm_compute/ etc ville.syrjala
  2017-04-21 18:14 ` [PATCH 02/15] drm/i915: Drop the debug message from vlv_get_fifo_size() ville.syrjala
@ 2017-04-21 18:14 ` ville.syrjala
  2017-04-21 18:14 ` [PATCH 04/15] drm/i915: Rename bunch of vlv_ watermark structures to g4x_ ville.syrjala
                   ` (13 subsequent siblings)
  16 siblings, 0 replies; 21+ messages in thread
From: ville.syrjala @ 2017-04-21 18:14 UTC (permalink / raw)
  To: intel-gfx

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

Rename the VLV/CHV max_level->num_levels helper to have an intel_
prefix since it's not VLV/CHV specific and I'll want to use it on
other platforms as well.

Signed-off-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
---
 drivers/gpu/drm/i915/intel_pm.c | 18 +++++++++---------
 1 file changed, 9 insertions(+), 9 deletions(-)

diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c
index 2c63abe2039c..ee045be2a5e0 100644
--- a/drivers/gpu/drm/i915/intel_pm.c
+++ b/drivers/gpu/drm/i915/intel_pm.c
@@ -648,6 +648,11 @@ static unsigned long intel_calculate_wm(unsigned long clock_in_khz,
 	return wm_size;
 }
 
+static int intel_wm_num_levels(struct drm_i915_private *dev_priv)
+{
+	return dev_priv->wm.max_level + 1;
+}
+
 static bool intel_wm_plane_visible(const struct intel_crtc_state *crtc_state,
 				   const struct intel_plane_state *plane_state)
 {
@@ -1136,18 +1141,13 @@ static int vlv_compute_fifo(struct intel_crtc_state *crtc_state)
 	return 0;
 }
 
-static int vlv_num_wm_levels(struct drm_i915_private *dev_priv)
-{
-	return dev_priv->wm.max_level + 1;
-}
-
 /* mark all levels starting from 'level' as invalid */
 static void vlv_invalidate_wms(struct intel_crtc *crtc,
 			       struct vlv_wm_state *wm_state, int level)
 {
 	struct drm_i915_private *dev_priv = to_i915(crtc->base.dev);
 
-	for (; level < vlv_num_wm_levels(dev_priv); level++) {
+	for (; level < intel_wm_num_levels(dev_priv); level++) {
 		enum plane_id plane_id;
 
 		for_each_plane_id_on_crtc(crtc, plane_id)
@@ -1174,7 +1174,7 @@ static bool vlv_raw_plane_wm_set(struct intel_crtc_state *crtc_state,
 				 int level, enum plane_id plane_id, u16 value)
 {
 	struct drm_i915_private *dev_priv = to_i915(crtc_state->base.crtc->dev);
-	int num_levels = vlv_num_wm_levels(dev_priv);
+	int num_levels = intel_wm_num_levels(dev_priv);
 	bool dirty = false;
 
 	for (; level < num_levels; level++) {
@@ -1192,7 +1192,7 @@ static bool vlv_raw_plane_wm_compute(struct intel_crtc_state *crtc_state,
 {
 	struct intel_plane *plane = to_intel_plane(plane_state->base.plane);
 	enum plane_id plane_id = plane->id;
-	int num_levels = vlv_num_wm_levels(to_i915(plane->base.dev));
+	int num_levels = intel_wm_num_levels(to_i915(plane->base.dev));
 	int level;
 	bool dirty = false;
 
@@ -1306,7 +1306,7 @@ static int vlv_compute_pipe_wm(struct intel_crtc_state *crtc_state)
 	}
 
 	/* initially allow all levels */
-	wm_state->num_levels = vlv_num_wm_levels(dev_priv);
+	wm_state->num_levels = intel_wm_num_levels(dev_priv);
 	/*
 	 * Note that enabling cxsr with no primary/sprite planes
 	 * enabled can wedge the pipe. Hence we only allow cxsr
-- 
2.10.2

_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH 04/15] drm/i915: Rename bunch of vlv_ watermark structures to g4x_
  2017-04-21 18:14 [PATCH 00/15] drm/i915: Two stage watermarks for g4x ville.syrjala
                   ` (2 preceding siblings ...)
  2017-04-21 18:14 ` [PATCH 03/15] drm/i915: s/vlv_num_wm_levels/intel_wm_num_levels/ ville.syrjala
@ 2017-04-21 18:14 ` ville.syrjala
  2017-04-21 18:14 ` [PATCH 05/15] drm/i915: Make vlv/chv watermark debug print less cryptic ville.syrjala
                   ` (12 subsequent siblings)
  16 siblings, 0 replies; 21+ messages in thread
From: ville.syrjala @ 2017-04-21 18:14 UTC (permalink / raw)
  To: intel-gfx

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

We'll be wanting to share some of these watermark structures on g4x,
so let's rename them to have a g4x_ prefix instead of vlv_.

Signed-off-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
---
 drivers/gpu/drm/i915/i915_drv.h  |  8 ++++----
 drivers/gpu/drm/i915/intel_drv.h |  6 +++---
 drivers/gpu/drm/i915/intel_pm.c  | 14 +++++++-------
 3 files changed, 14 insertions(+), 14 deletions(-)

diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
index 357b6c6c2f04..0a393fdc53d1 100644
--- a/drivers/gpu/drm/i915/i915_drv.h
+++ b/drivers/gpu/drm/i915/i915_drv.h
@@ -1760,11 +1760,11 @@ struct ilk_wm_values {
 	enum intel_ddb_partitioning partitioning;
 };
 
-struct vlv_pipe_wm {
+struct g4x_pipe_wm {
 	uint16_t plane[I915_MAX_PLANES];
 };
 
-struct vlv_sr_wm {
+struct g4x_sr_wm {
 	uint16_t plane;
 	uint16_t cursor;
 };
@@ -1774,8 +1774,8 @@ struct vlv_wm_ddl_values {
 };
 
 struct vlv_wm_values {
-	struct vlv_pipe_wm pipe[3];
-	struct vlv_sr_wm sr;
+	struct g4x_pipe_wm pipe[3];
+	struct g4x_sr_wm sr;
 	struct vlv_wm_ddl_values ddl[3];
 	uint8_t level;
 	bool cxsr;
diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
index 54f3ff840812..d1cdd10998fa 100644
--- a/drivers/gpu/drm/i915/intel_drv.h
+++ b/drivers/gpu/drm/i915/intel_drv.h
@@ -512,8 +512,8 @@ enum vlv_wm_level {
 };
 
 struct vlv_wm_state {
-	struct vlv_pipe_wm wm[NUM_VLV_WM_LEVELS];
-	struct vlv_sr_wm sr[NUM_VLV_WM_LEVELS];
+	struct g4x_pipe_wm wm[NUM_VLV_WM_LEVELS];
+	struct g4x_sr_wm sr[NUM_VLV_WM_LEVELS];
 	uint8_t num_levels;
 	bool cxsr;
 };
@@ -549,7 +549,7 @@ struct intel_crtc_wm_state {
 
 		struct {
 			/* "raw" watermarks (not inverted) */
-			struct vlv_pipe_wm raw[NUM_VLV_WM_LEVELS];
+			struct g4x_pipe_wm raw[NUM_VLV_WM_LEVELS];
 			/* intermediate watermarks (inverted) */
 			struct vlv_wm_state intermediate;
 			/* optimal watermarks (inverted) */
diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c
index ee045be2a5e0..1cc13ccadee6 100644
--- a/drivers/gpu/drm/i915/intel_pm.c
+++ b/drivers/gpu/drm/i915/intel_pm.c
@@ -1062,7 +1062,7 @@ static bool vlv_need_sprite0_fifo_workaround(unsigned int active_planes)
 static int vlv_compute_fifo(struct intel_crtc_state *crtc_state)
 {
 	struct intel_crtc *crtc = to_intel_crtc(crtc_state->base.crtc);
-	const struct vlv_pipe_wm *raw =
+	const struct g4x_pipe_wm *raw =
 		&crtc_state->wm.vlv.raw[VLV_WM_LEVEL_PM2];
 	struct vlv_fifo_state *fifo_state = &crtc_state->wm.vlv.fifo_state;
 	unsigned int active_planes = crtc_state->active_planes & ~BIT(PLANE_CURSOR);
@@ -1178,7 +1178,7 @@ static bool vlv_raw_plane_wm_set(struct intel_crtc_state *crtc_state,
 	bool dirty = false;
 
 	for (; level < num_levels; level++) {
-		struct vlv_pipe_wm *raw = &crtc_state->wm.vlv.raw[level];
+		struct g4x_pipe_wm *raw = &crtc_state->wm.vlv.raw[level];
 
 		dirty |= raw->plane[plane_id] != value;
 		raw->plane[plane_id] = value;
@@ -1202,7 +1202,7 @@ static bool vlv_raw_plane_wm_compute(struct intel_crtc_state *crtc_state,
 	}
 
 	for (level = 0; level < num_levels; level++) {
-		struct vlv_pipe_wm *raw = &crtc_state->wm.vlv.raw[level];
+		struct g4x_pipe_wm *raw = &crtc_state->wm.vlv.raw[level];
 		int wm = vlv_compute_wm_level(crtc_state, plane_state, level);
 		int max_wm = plane_id == PLANE_CURSOR ? 63 : 511;
 
@@ -1230,7 +1230,7 @@ static bool vlv_raw_plane_wm_compute(struct intel_crtc_state *crtc_state,
 static bool vlv_raw_plane_wm_is_valid(const struct intel_crtc_state *crtc_state,
 				      enum plane_id plane_id, int level)
 {
-	const struct vlv_pipe_wm *raw =
+	const struct g4x_pipe_wm *raw =
 		&crtc_state->wm.vlv.raw[level];
 	const struct vlv_fifo_state *fifo_state =
 		&crtc_state->wm.vlv.fifo_state;
@@ -1315,7 +1315,7 @@ static int vlv_compute_pipe_wm(struct intel_crtc_state *crtc_state)
 	wm_state->cxsr = crtc->pipe != PIPE_C && num_active_planes == 1;
 
 	for (level = 0; level < wm_state->num_levels; level++) {
-		const struct vlv_pipe_wm *raw = &crtc_state->wm.vlv.raw[level];
+		const struct g4x_pipe_wm *raw = &crtc_state->wm.vlv.raw[level];
 		const int sr_fifo_size = INTEL_INFO(dev_priv)->num_pipes * 512 - 1;
 
 		if (!vlv_raw_crtc_wm_is_valid(crtc_state, level))
@@ -4785,7 +4785,7 @@ void vlv_wm_get_hw_state(struct drm_device *dev)
 		active->cxsr = wm->cxsr;
 
 		for (level = 0; level < active->num_levels; level++) {
-			struct vlv_pipe_wm *raw =
+			struct g4x_pipe_wm *raw =
 				&crtc_state->wm.vlv.raw[level];
 
 			active->sr[level].plane = wm->sr.plane;
@@ -4845,7 +4845,7 @@ void vlv_wm_sanitize(struct drm_i915_private *dev_priv)
 			continue;
 
 		for (level = 0; level < wm_state->num_levels; level++) {
-			struct vlv_pipe_wm *raw =
+			struct g4x_pipe_wm *raw =
 				&crtc_state->wm.vlv.raw[level];
 
 			raw->plane[plane_id] = 0;
-- 
2.10.2

_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH 05/15] drm/i915: Make vlv/chv watermark debug print less cryptic
  2017-04-21 18:14 [PATCH 00/15] drm/i915: Two stage watermarks for g4x ville.syrjala
                   ` (3 preceding siblings ...)
  2017-04-21 18:14 ` [PATCH 04/15] drm/i915: Rename bunch of vlv_ watermark structures to g4x_ ville.syrjala
@ 2017-04-21 18:14 ` ville.syrjala
  2017-04-21 18:14 ` [PATCH 06/15] drm/i915: Document CxSR ville.syrjala
                   ` (11 subsequent siblings)
  16 siblings, 0 replies; 21+ messages in thread
From: ville.syrjala @ 2017-04-21 18:14 UTC (permalink / raw)
  To: intel-gfx

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

The magic numbers 0,1,2 aren't all that interesting for users perhaps.
Since we know what these watermark levels mean for VLV/CHV let's print
their names.

Signed-off-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
---
 drivers/gpu/drm/i915/intel_pm.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c
index 1cc13ccadee6..da2072728df2 100644
--- a/drivers/gpu/drm/i915/intel_pm.c
+++ b/drivers/gpu/drm/i915/intel_pm.c
@@ -1218,7 +1218,7 @@ static bool vlv_raw_plane_wm_compute(struct intel_crtc_state *crtc_state,
 
 out:
 	if (dirty)
-		DRM_DEBUG_KMS("%s wms: [0]=%d,[1]=%d,[2]=%d\n",
+		DRM_DEBUG_KMS("%s watermarks: PM2=%d, PM5=%d, DDR DVFS=%d\n",
 			      plane->base.name,
 			      crtc_state->wm.vlv.raw[VLV_WM_LEVEL_PM2].plane[plane_id],
 			      crtc_state->wm.vlv.raw[VLV_WM_LEVEL_PM5].plane[plane_id],
-- 
2.10.2

_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH 06/15] drm/i915: Document CxSR
  2017-04-21 18:14 [PATCH 00/15] drm/i915: Two stage watermarks for g4x ville.syrjala
                   ` (4 preceding siblings ...)
  2017-04-21 18:14 ` [PATCH 05/15] drm/i915: Make vlv/chv watermark debug print less cryptic ville.syrjala
@ 2017-04-21 18:14 ` ville.syrjala
  2017-04-21 18:14 ` [PATCH 07/15] drm/i915: Fix cursor 'cpp' in watermark calculatins for old platforms ville.syrjala
                   ` (10 subsequent siblings)
  16 siblings, 0 replies; 21+ messages in thread
From: ville.syrjala @ 2017-04-21 18:14 UTC (permalink / raw)
  To: intel-gfx

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

Add some documentation explaining what CxSR actually is.

Signed-off-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
---
 drivers/gpu/drm/i915/intel_pm.c | 37 +++++++++++++++++++++++++++++++++++++
 1 file changed, 37 insertions(+)

diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c
index da2072728df2..7bd4c8688acb 100644
--- a/drivers/gpu/drm/i915/intel_pm.c
+++ b/drivers/gpu/drm/i915/intel_pm.c
@@ -386,6 +386,43 @@ static bool _intel_set_memory_cxsr(struct drm_i915_private *dev_priv, bool enabl
 	return was_enabled;
 }
 
+/**
+ * intel_set_memory_cxsr - Configure CxSR state
+ * @dev_priv: i915 device
+ * @enable: Allow vs. disallow CxSR
+ *
+ * Allow or disallow the system to enter a special CxSR
+ * (C-state self refresh) state. What typically happens in CxSR mode
+ * is that several display FIFOs may get combined into a single larger
+ * FIFO for a particular plane (so called max FIFO mode) to allow the
+ * system to defer memory fetches longer, and the memory will enter
+ * self refresh.
+ *
+ * Note that enabling CxSR does not guarantee that the system enter
+ * this special mode, nor does it guarantee that the system stays
+ * in that mode once entered. So this just allows/disallows the system
+ * to autonomously utilize the CxSR mode. Other factors such as core
+ * C-states will affect when/if the system actually enters/exits the
+ * CxSR mode.
+ *
+ * Note that on VLV/CHV this actually only controls the max FIFO mode,
+ * and the system is free to enter/exit memory self refresh at any time
+ * even when the use of CxSR has been disallowed.
+ *
+ * While the system is actually in the CxSR/max FIFO mode, some plane
+ * control registers will not get latched on vblank. Thus in order to
+ * guarantee the system will respond to changes in the plane registers
+ * we must always disallow CxSR prior to making changes to those registers.
+ * Unfortunately the system will re-evaluate the CxSR conditions at
+ * frame start which happens after vblank start (which is when the plane
+ * registers would get latched), so we can't proceed with the plane update
+ * during the same frame where we disallowed CxSR.
+ *
+ * Certain platforms also have a deeper HPLL SR mode. Fortunately the
+ * HPLL SR mode depends on CxSR itself, so we don't have to hand hold
+ * the hardware w.r.t. HPLL SR when writing to plane registers.
+ * Disallowing just CxSR is sufficient.
+ */
 bool intel_set_memory_cxsr(struct drm_i915_private *dev_priv, bool enable)
 {
 	bool ret;
-- 
2.10.2

_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH 07/15] drm/i915: Fix cursor 'cpp' in watermark calculatins for old platforms
  2017-04-21 18:14 [PATCH 00/15] drm/i915: Two stage watermarks for g4x ville.syrjala
                   ` (5 preceding siblings ...)
  2017-04-21 18:14 ` [PATCH 06/15] drm/i915: Document CxSR ville.syrjala
@ 2017-04-21 18:14 ` ville.syrjala
  2017-04-21 18:14 ` [PATCH 08/15] drm/i915: Fix the g4x watermark TLB miss workaround ville.syrjala
                   ` (9 subsequent siblings)
  16 siblings, 0 replies; 21+ messages in thread
From: ville.syrjala @ 2017-04-21 18:14 UTC (permalink / raw)
  To: intel-gfx

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

The watermark code for the old platforms (g4x and older) uses the
primary plane cpp when computing cursor watermarks. To keep the fix
simple let's just hardcode cpp=4 for the cursor on those platforms
since that's all we support.

Signed-off-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
---
 drivers/gpu/drm/i915/intel_pm.c | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c
index 7bd4c8688acb..7d0d1a0c4c63 100644
--- a/drivers/gpu/drm/i915/intel_pm.c
+++ b/drivers/gpu/drm/i915/intel_pm.c
@@ -768,7 +768,7 @@ static void pineview_update_wm(struct intel_crtc *unused_crtc)
 		/* cursor SR */
 		wm = intel_calculate_wm(clock, &pineview_cursor_wm,
 					pineview_display_wm.fifo_size,
-					cpp, latency->cursor_sr);
+					4, latency->cursor_sr);
 		reg = I915_READ(DSPFW3);
 		reg &= ~DSPFW_CURSOR_SR_MASK;
 		reg |= FW_WM(wm, CURSOR_SR);
@@ -786,7 +786,7 @@ static void pineview_update_wm(struct intel_crtc *unused_crtc)
 		/* cursor HPLL off SR */
 		wm = intel_calculate_wm(clock, &pineview_cursor_hplloff_wm,
 					pineview_display_hplloff_wm.fifo_size,
-					cpp, latency->cursor_hpll_disable);
+					4, latency->cursor_hpll_disable);
 		reg = I915_READ(DSPFW3);
 		reg &= ~DSPFW_HPLL_CURSOR_MASK;
 		reg |= FW_WM(wm, HPLL_CURSOR);
@@ -842,7 +842,7 @@ static bool g4x_compute_wm0(struct drm_i915_private *dev_priv,
 	/* Use the large buffer method to calculate cursor watermark */
 	line_time_us = max(htotal * 1000 / clock, 1);
 	line_count = (cursor_latency_ns / line_time_us + 1000) / 1000;
-	entries = line_count * crtc->base.cursor->state->crtc_w * cpp;
+	entries = line_count * crtc->base.cursor->state->crtc_w * 4;
 	tlb_miss = cursor->fifo_size*cursor->cacheline_size - hdisplay * 8;
 	if (tlb_miss > 0)
 		entries += tlb_miss;
@@ -930,7 +930,7 @@ static bool g4x_compute_srwm(struct drm_i915_private *dev_priv,
 	*display_wm = entries + display->guard_size;
 
 	/* calculate the self-refresh watermark for display cursor */
-	entries = line_count * cpp * crtc->base.cursor->state->crtc_w;
+	entries = line_count * 4 * crtc->base.cursor->state->crtc_w;
 	entries = DIV_ROUND_UP(entries, cursor->cacheline_size);
 	*cursor_wm = entries + cursor->guard_size;
 
@@ -1736,7 +1736,7 @@ static void i965_update_wm(struct intel_crtc *unused_crtc)
 			      entries, srwm);
 
 		entries = (((sr_latency_ns / line_time_us) + 1000) / 1000) *
-			cpp * crtc->base.cursor->state->crtc_w;
+			4 * crtc->base.cursor->state->crtc_w;
 		entries = DIV_ROUND_UP(entries,
 					  i965_cursor_wm_info.cacheline_size);
 		cursor_sr = i965_cursor_wm_info.fifo_size -
-- 
2.10.2

_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH 08/15] drm/i915: Fix the g4x watermark TLB miss workaround
  2017-04-21 18:14 [PATCH 00/15] drm/i915: Two stage watermarks for g4x ville.syrjala
                   ` (6 preceding siblings ...)
  2017-04-21 18:14 ` [PATCH 07/15] drm/i915: Fix cursor 'cpp' in watermark calculatins for old platforms ville.syrjala
@ 2017-04-21 18:14 ` ville.syrjala
  2017-04-21 18:14 ` [PATCH 09/15] drm/i915: Refactor the g4x TLB miss w/a to a helper ville.syrjala
                   ` (8 subsequent siblings)
  16 siblings, 0 replies; 21+ messages in thread
From: ville.syrjala @ 2017-04-21 18:14 UTC (permalink / raw)
  To: intel-gfx

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

The g4x watermark TLB miss workaround requires that we bump up the
watermark by the difference between 8 full lines and the FIFO size.
Unfortunately the way we compute it at the moment ignores the size
of the pixels. The code also used the primary plane width as the
cursor width when computing the TLB miss w/a for the cursor.
Let's fix both problems.

Signed-off-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
---
 drivers/gpu/drm/i915/intel_pm.c | 11 ++++++-----
 1 file changed, 6 insertions(+), 5 deletions(-)

diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c
index 7d0d1a0c4c63..09d4676419fb 100644
--- a/drivers/gpu/drm/i915/intel_pm.c
+++ b/drivers/gpu/drm/i915/intel_pm.c
@@ -811,7 +811,7 @@ static bool g4x_compute_wm0(struct drm_i915_private *dev_priv,
 	struct intel_crtc *crtc;
 	const struct drm_display_mode *adjusted_mode;
 	const struct drm_framebuffer *fb;
-	int htotal, hdisplay, clock, cpp;
+	int htotal, plane_width, cursor_width, clock, cpp;
 	int line_time_us, line_count;
 	int entries, tlb_miss;
 
@@ -826,12 +826,13 @@ static bool g4x_compute_wm0(struct drm_i915_private *dev_priv,
 	fb = crtc->base.primary->state->fb;
 	clock = adjusted_mode->crtc_clock;
 	htotal = adjusted_mode->crtc_htotal;
-	hdisplay = crtc->config->pipe_src_w;
+	plane_width = crtc->config->pipe_src_w;
+	cursor_width = crtc->base.cursor->state->crtc_w;
 	cpp = fb->format->cpp[0];
 
 	/* Use the small buffer method to calculate plane watermark */
 	entries = ((clock * cpp / 1000) * display_latency_ns) / 1000;
-	tlb_miss = display->fifo_size*display->cacheline_size - hdisplay * 8;
+	tlb_miss = display->fifo_size*display->cacheline_size - plane_width * cpp * 8;
 	if (tlb_miss > 0)
 		entries += tlb_miss;
 	entries = DIV_ROUND_UP(entries, display->cacheline_size);
@@ -842,8 +843,8 @@ static bool g4x_compute_wm0(struct drm_i915_private *dev_priv,
 	/* Use the large buffer method to calculate cursor watermark */
 	line_time_us = max(htotal * 1000 / clock, 1);
 	line_count = (cursor_latency_ns / line_time_us + 1000) / 1000;
-	entries = line_count * crtc->base.cursor->state->crtc_w * 4;
-	tlb_miss = cursor->fifo_size*cursor->cacheline_size - hdisplay * 8;
+	entries = line_count * cursor_width * 4;
+	tlb_miss = cursor->fifo_size*cursor->cacheline_size - cursor_width * 4 * 8;
 	if (tlb_miss > 0)
 		entries += tlb_miss;
 	entries = DIV_ROUND_UP(entries, cursor->cacheline_size);
-- 
2.10.2

_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH 09/15] drm/i915: Refactor the g4x TLB miss w/a to a helper
  2017-04-21 18:14 [PATCH 00/15] drm/i915: Two stage watermarks for g4x ville.syrjala
                   ` (7 preceding siblings ...)
  2017-04-21 18:14 ` [PATCH 08/15] drm/i915: Fix the g4x watermark TLB miss workaround ville.syrjala
@ 2017-04-21 18:14 ` ville.syrjala
  2017-04-21 18:14 ` [PATCH 10/15] drm/i915: Refactor wm calculations ville.syrjala
                   ` (7 subsequent siblings)
  16 siblings, 0 replies; 21+ messages in thread
From: ville.syrjala @ 2017-04-21 18:14 UTC (permalink / raw)
  To: intel-gfx

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

Pull the g4x TLB miss w/a calculation into a small helper.

Signed-off-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
---
 drivers/gpu/drm/i915/intel_pm.c | 27 ++++++++++++++++++++-------
 1 file changed, 20 insertions(+), 7 deletions(-)

diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c
index 09d4676419fb..c43fcd5d29b2 100644
--- a/drivers/gpu/drm/i915/intel_pm.c
+++ b/drivers/gpu/drm/i915/intel_pm.c
@@ -799,6 +799,23 @@ static void pineview_update_wm(struct intel_crtc *unused_crtc)
 	}
 }
 
+/*
+ * Documentation says:
+ * "If the line size is small, the TLB fetches can get in the way of the
+ *  data fetches, causing some lag in the pixel data return which is not
+ *  accounted for in the above formulas. The following adjustment only
+ *  needs to be applied if eight whole lines fit in the buffer at once.
+ *  The WM is adjusted upwards by the difference between the FIFO size
+ *  and the size of 8 whole lines. This adjustment is always performed
+ *  in the actual pixel depth regardless of whether FBC is enabled or not."
+ */
+static int g4x_tlb_miss_wa(int fifo_size, int width, int cpp)
+{
+	int tlb_miss = fifo_size * 64 - width * cpp * 8;
+
+	return max(0, tlb_miss);
+}
+
 static bool g4x_compute_wm0(struct drm_i915_private *dev_priv,
 			    int plane,
 			    const struct intel_watermark_params *display,
@@ -813,7 +830,7 @@ static bool g4x_compute_wm0(struct drm_i915_private *dev_priv,
 	const struct drm_framebuffer *fb;
 	int htotal, plane_width, cursor_width, clock, cpp;
 	int line_time_us, line_count;
-	int entries, tlb_miss;
+	int entries;
 
 	crtc = intel_get_crtc_for_plane(dev_priv, plane);
 	if (!intel_crtc_active(crtc)) {
@@ -832,9 +849,7 @@ static bool g4x_compute_wm0(struct drm_i915_private *dev_priv,
 
 	/* Use the small buffer method to calculate plane watermark */
 	entries = ((clock * cpp / 1000) * display_latency_ns) / 1000;
-	tlb_miss = display->fifo_size*display->cacheline_size - plane_width * cpp * 8;
-	if (tlb_miss > 0)
-		entries += tlb_miss;
+	entries += g4x_tlb_miss_wa(display->fifo_size, plane_width, cpp);
 	entries = DIV_ROUND_UP(entries, display->cacheline_size);
 	*plane_wm = entries + display->guard_size;
 	if (*plane_wm > (int)display->max_wm)
@@ -844,9 +859,7 @@ static bool g4x_compute_wm0(struct drm_i915_private *dev_priv,
 	line_time_us = max(htotal * 1000 / clock, 1);
 	line_count = (cursor_latency_ns / line_time_us + 1000) / 1000;
 	entries = line_count * cursor_width * 4;
-	tlb_miss = cursor->fifo_size*cursor->cacheline_size - cursor_width * 4 * 8;
-	if (tlb_miss > 0)
-		entries += tlb_miss;
+	entries += g4x_tlb_miss_wa(cursor->fifo_size, cursor_width, 4);
 	entries = DIV_ROUND_UP(entries, cursor->cacheline_size);
 	*cursor_wm = entries + cursor->guard_size;
 	if (*cursor_wm > (int)cursor->max_wm)
-- 
2.10.2

_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH 10/15] drm/i915: Refactor wm calculations
  2017-04-21 18:14 [PATCH 00/15] drm/i915: Two stage watermarks for g4x ville.syrjala
                   ` (8 preceding siblings ...)
  2017-04-21 18:14 ` [PATCH 09/15] drm/i915: Refactor the g4x TLB miss w/a to a helper ville.syrjala
@ 2017-04-21 18:14 ` ville.syrjala
  2017-04-21 18:14 ` [PATCH 11/15] drm/i915: Apply the g4x TLB miss w/a to SR watermarks as well ville.syrjala
                   ` (6 subsequent siblings)
  16 siblings, 0 replies; 21+ messages in thread
From: ville.syrjala @ 2017-04-21 18:14 UTC (permalink / raw)
  To: intel-gfx

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

All platforms until SKL compute their watermarks essentially
using the same method1/small buffer and method2/large buffer
formulas. Most just open code it in slightly different ways.
Let's pull it all into common helpers. This makes it a little
easier to spot the actual differences.

While at it try to add some docs explainign what the formulas
are trying to do.

Signed-off-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
---
 drivers/gpu/drm/i915/intel_pm.c | 221 +++++++++++++++++++++++++++-------------
 1 file changed, 149 insertions(+), 72 deletions(-)

diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c
index c43fcd5d29b2..c07f3b2b0972 100644
--- a/drivers/gpu/drm/i915/intel_pm.c
+++ b/drivers/gpu/drm/i915/intel_pm.c
@@ -626,8 +626,104 @@ static const struct intel_watermark_params i845_wm_info = {
 };
 
 /**
+ * intel_wm_method1 - Method 1 / "small buffer" watermark formula
+ * @pixel_rate: Pipe pixel rate in kHz
+ * @cpp: Plane bytes per pixel
+ * @latency: Memory wakeup latency in 0.1us units
+ *
+ * Compute the watermark using the method 1 or "small buffer"
+ * formula. The caller may additonally add extra cachelines
+ * to account for TLB misses and clock crossings.
+ *
+ * This method is concerned with the short term drain rate
+ * of the FIFO, ie. it does not account for blanking periods
+ * which would effectively reduce the average drain rate across
+ * a longer period. The name "small" refers to the fact the
+ * FIFO is relatively small compared to the amount of data
+ * fetched.
+ *
+ * The FIFO level vs. time graph might look something like:
+ *
+ *   |\   |\
+ *   | \  | \
+ * __---__---__ (- plane active, _ blanking)
+ * -> time
+ *
+ * or perhaps like this:
+ *
+ *   |\|\  |\|\
+ * __----__----__ (- plane active, _ blanking)
+ * -> time
+ *
+ * Returns:
+ * The watermark in bytes
+ */
+static unsigned int intel_wm_method1(unsigned int pixel_rate,
+				     unsigned int cpp,
+				     unsigned int latency)
+{
+	uint64_t ret;
+
+	ret = (uint64_t) pixel_rate * cpp * latency;
+	ret = DIV_ROUND_UP_ULL(ret, 10000);
+
+	return ret;
+}
+
+/**
+ * intel_wm_method2 - Method 2 / "large buffer" watermark formula
+ * @pixel_rate: Pipe pixel rate in kHz
+ * @htotal: Pipe horizontal total
+ * @width: Plane width in pixels
+ * @cpp: Plane bytes per pixel
+ * @latency: Memory wakeup latency in 0.1us units
+ *
+ * Compute the watermark using the method 2 or "large buffer"
+ * formula. The caller may additonally add extra cachelines
+ * to account for TLB misses and clock crossings.
+ *
+ * This method is concerned with the long term drain rate
+ * of the FIFO, ie. it does account for blanking periods
+ * which effectively reduce the average drain rate across
+ * a longer period. The name "large" refers to the fact the
+ * FIFO is relatively large compared to the amount of data
+ * fetched.
+ *
+ * The FIFO level vs. time graph might look something like:
+ *
+ *    |\___       |\___
+ *    |    \___   |    \___
+ *    |        \  |        \
+ * __ --__--__--__--__--__--__ (- plane active, _ blanking)
+ * -> time
+ *
+ * Returns:
+ * The watermark in bytes
+ */
+static unsigned int intel_wm_method2(unsigned int pixel_rate,
+				     unsigned int htotal,
+				     unsigned int width,
+				     unsigned int cpp,
+				     unsigned int latency)
+{
+	unsigned int ret;
+
+	/*
+	 * FIXME remove once all users are computing
+	 * watermarks in the correct place.
+	 */
+	if (WARN_ON_ONCE(htotal == 0))
+		htotal = 1;
+
+	ret = (latency * pixel_rate) / (htotal * 10000);
+	ret = (ret + 1) * width * cpp;
+
+	return ret;
+}
+
+/**
  * intel_calculate_wm - calculate watermark level
- * @clock_in_khz: pixel clock
+ * @pixel_rate: pixel clock
  * @wm: chip FIFO params
  * @cpp: bytes per pixel
  * @latency_ns: memory latency for the platform
@@ -643,12 +739,12 @@ static const struct intel_watermark_params i845_wm_info = {
  * past the watermark point.  If the FIFO drains completely, a FIFO underrun
  * will occur, and a display engine hang could result.
  */
-static unsigned long intel_calculate_wm(unsigned long clock_in_khz,
-					const struct intel_watermark_params *wm,
-					int fifo_size, int cpp,
-					unsigned long latency_ns)
+static unsigned int intel_calculate_wm(int pixel_rate,
+				       const struct intel_watermark_params *wm,
+				       int fifo_size, int cpp,
+				       unsigned int latency_ns)
 {
-	long entries_required, wm_size;
+	int entries, wm_size;
 
 	/*
 	 * Note: we need to make sure we don't overflow for various clock &
@@ -656,18 +752,17 @@ static unsigned long intel_calculate_wm(unsigned long clock_in_khz,
 	 * clocks go from a few thousand to several hundred thousand.
 	 * latency is usually a few thousand
 	 */
-	entries_required = ((clock_in_khz / 1000) * cpp * latency_ns) /
-		1000;
-	entries_required = DIV_ROUND_UP(entries_required, wm->cacheline_size);
+	entries = intel_wm_method1(pixel_rate, cpp,
+				   latency_ns / 100);
+	entries = DIV_ROUND_UP(entries, wm->cacheline_size) +
+		wm->guard_size;
+	DRM_DEBUG_KMS("FIFO entries required for mode: %d\n", entries);
 
-	DRM_DEBUG_KMS("FIFO entries required for mode: %ld\n", entries_required);
-
-	wm_size = fifo_size - (entries_required + wm->guard_size);
-
-	DRM_DEBUG_KMS("FIFO watermark level: %ld\n", wm_size);
+	wm_size = fifo_size - entries;
+	DRM_DEBUG_KMS("FIFO watermark level: %d\n", wm_size);
 
 	/* Don't promote wm_size to unsigned... */
-	if (wm_size > (long)wm->max_wm)
+	if (wm_size > wm->max_wm)
 		wm_size = wm->max_wm;
 	if (wm_size <= 0)
 		wm_size = wm->default_wm;
@@ -734,7 +829,7 @@ static void pineview_update_wm(struct intel_crtc *unused_crtc)
 	struct intel_crtc *crtc;
 	const struct cxsr_latency *latency;
 	u32 reg;
-	unsigned long wm;
+	unsigned int wm;
 
 	latency = intel_get_cxsr_latency(IS_PINEVIEW_G(dev_priv),
 					 dev_priv->is_ddr3,
@@ -829,7 +924,6 @@ static bool g4x_compute_wm0(struct drm_i915_private *dev_priv,
 	const struct drm_display_mode *adjusted_mode;
 	const struct drm_framebuffer *fb;
 	int htotal, plane_width, cursor_width, clock, cpp;
-	int line_time_us, line_count;
 	int entries;
 
 	crtc = intel_get_crtc_for_plane(dev_priv, plane);
@@ -848,7 +942,7 @@ static bool g4x_compute_wm0(struct drm_i915_private *dev_priv,
 	cpp = fb->format->cpp[0];
 
 	/* Use the small buffer method to calculate plane watermark */
-	entries = ((clock * cpp / 1000) * display_latency_ns) / 1000;
+	entries = intel_wm_method1(clock, cpp, display_latency_ns / 100);
 	entries += g4x_tlb_miss_wa(display->fifo_size, plane_width, cpp);
 	entries = DIV_ROUND_UP(entries, display->cacheline_size);
 	*plane_wm = entries + display->guard_size;
@@ -856,9 +950,8 @@ static bool g4x_compute_wm0(struct drm_i915_private *dev_priv,
 		*plane_wm = display->max_wm;
 
 	/* Use the large buffer method to calculate cursor watermark */
-	line_time_us = max(htotal * 1000 / clock, 1);
-	line_count = (cursor_latency_ns / line_time_us + 1000) / 1000;
-	entries = line_count * cursor_width * 4;
+	entries = intel_wm_method2(clock, htotal, cursor_width, 4,
+				   cursor_latency_ns / 100);
 	entries += g4x_tlb_miss_wa(cursor->fifo_size, cursor_width, 4);
 	entries = DIV_ROUND_UP(entries, cursor->cacheline_size);
 	*cursor_wm = entries + cursor->guard_size;
@@ -914,8 +1007,6 @@ static bool g4x_compute_srwm(struct drm_i915_private *dev_priv,
 	const struct drm_display_mode *adjusted_mode;
 	const struct drm_framebuffer *fb;
 	int hdisplay, htotal, cpp, clock;
-	unsigned long line_time_us;
-	int line_count, line_size;
 	int small, large;
 	int entries;
 
@@ -932,19 +1023,17 @@ static bool g4x_compute_srwm(struct drm_i915_private *dev_priv,
 	hdisplay = crtc->config->pipe_src_w;
 	cpp = fb->format->cpp[0];
 
-	line_time_us = max(htotal * 1000 / clock, 1);
-	line_count = (latency_ns / line_time_us + 1000) / 1000;
-	line_size = hdisplay * cpp;
-
 	/* Use the minimum of the small and large buffer method for primary */
-	small = ((clock * cpp / 1000) * latency_ns) / 1000;
-	large = line_count * line_size;
-
+	small = intel_wm_method1(clock, cpp, latency_ns / 100);
+	large = intel_wm_method2(clock, htotal, hdisplay, cpp,
+				 latency_ns / 100);
 	entries = DIV_ROUND_UP(min(small, large), display->cacheline_size);
 	*display_wm = entries + display->guard_size;
 
 	/* calculate the self-refresh watermark for display cursor */
-	entries = line_count * 4 * crtc->base.cursor->state->crtc_w;
+	entries = intel_wm_method2(clock, htotal,
+				   crtc->base.cursor->state->crtc_w, 4,
+				   latency_ns / 100);
 	entries = DIV_ROUND_UP(entries, cursor->cacheline_size);
 	*cursor_wm = entries + cursor->guard_size;
 
@@ -1036,15 +1125,15 @@ static void vlv_write_wm_values(struct drm_i915_private *dev_priv,
 
 /* latency must be in 0.1us units. */
 static unsigned int vlv_wm_method2(unsigned int pixel_rate,
-				   unsigned int pipe_htotal,
-				   unsigned int horiz_pixels,
+				   unsigned int htotal,
+				   unsigned int width,
 				   unsigned int cpp,
 				   unsigned int latency)
 {
 	unsigned int ret;
 
-	ret = (latency * pixel_rate) / (pipe_htotal * 10000);
-	ret = (ret + 1) * horiz_pixels * cpp;
+	ret = intel_wm_method2(pixel_rate, htotal,
+			       width, cpp, latency);
 	ret = DIV_ROUND_UP(ret, 64);
 
 	return ret;
@@ -1085,8 +1174,6 @@ static uint16_t vlv_compute_wm_level(const struct intel_crtc_state *crtc_state,
 	clock = adjusted_mode->crtc_clock;
 	htotal = adjusted_mode->crtc_htotal;
 	width = crtc_state->pipe_src_w;
-	if (WARN_ON(htotal == 0))
-		htotal = 1;
 
 	if (plane->id == PLANE_CURSOR) {
 		/*
@@ -1733,14 +1820,10 @@ static void i965_update_wm(struct intel_crtc *unused_crtc)
 		int htotal = adjusted_mode->crtc_htotal;
 		int hdisplay = crtc->config->pipe_src_w;
 		int cpp = fb->format->cpp[0];
-		unsigned long line_time_us;
 		int entries;
 
-		line_time_us = max(htotal * 1000 / clock, 1);
-
-		/* Use ns/us then divide to preserve precision */
-		entries = (((sr_latency_ns / line_time_us) + 1000) / 1000) *
-			cpp * hdisplay;
+		entries = intel_wm_method2(clock, htotal,
+					   hdisplay, cpp, sr_latency_ns / 100);
 		entries = DIV_ROUND_UP(entries, I915_FIFO_LINE_SIZE);
 		srwm = I965_FIFO_SIZE - entries;
 		if (srwm < 0)
@@ -1749,13 +1832,14 @@ static void i965_update_wm(struct intel_crtc *unused_crtc)
 		DRM_DEBUG_KMS("self-refresh entries: %d, wm: %d\n",
 			      entries, srwm);
 
-		entries = (((sr_latency_ns / line_time_us) + 1000) / 1000) *
-			4 * crtc->base.cursor->state->crtc_w;
+		entries = intel_wm_method2(clock, htotal,
+					   crtc->base.cursor->state->crtc_w, 4,
+					   sr_latency_ns / 100);
 		entries = DIV_ROUND_UP(entries,
-					  i965_cursor_wm_info.cacheline_size);
-		cursor_sr = i965_cursor_wm_info.fifo_size -
-			(entries + i965_cursor_wm_info.guard_size);
+				       i965_cursor_wm_info.cacheline_size) +
+			i965_cursor_wm_info.guard_size;
 
+		cursor_sr = i965_cursor_wm_info.fifo_size - entries;
 		if (cursor_sr > i965_cursor_wm_info.max_wm)
 			cursor_sr = i965_cursor_wm_info.max_wm;
 
@@ -1892,7 +1976,6 @@ static void i9xx_update_wm(struct intel_crtc *unused_crtc)
 		int htotal = adjusted_mode->crtc_htotal;
 		int hdisplay = enabled->config->pipe_src_w;
 		int cpp;
-		unsigned long line_time_us;
 		int entries;
 
 		if (IS_I915GM(dev_priv) || IS_I945GM(dev_priv))
@@ -1900,11 +1983,8 @@ static void i9xx_update_wm(struct intel_crtc *unused_crtc)
 		else
 			cpp = fb->format->cpp[0];
 
-		line_time_us = max(htotal * 1000 / clock, 1);
-
-		/* Use ns/us then divide to preserve precision */
-		entries = (((sr_latency_ns / line_time_us) + 1000) / 1000) *
-			cpp * hdisplay;
+		entries = intel_wm_method2(clock, htotal, hdisplay, cpp,
+					   sr_latency_ns / 100);
 		entries = DIV_ROUND_UP(entries, wm_info->cacheline_size);
 		DRM_DEBUG_KMS("self-refresh entries: %d\n", entries);
 		srwm = wm_info->fifo_size - entries;
@@ -1961,34 +2041,31 @@ static void i845_update_wm(struct intel_crtc *unused_crtc)
 }
 
 /* latency must be in 0.1us units. */
-static uint32_t ilk_wm_method1(uint32_t pixel_rate, uint8_t cpp, uint32_t latency)
+static unsigned int ilk_wm_method1(unsigned int pixel_rate,
+				   unsigned int cpp,
+				   unsigned int latency)
 {
-	uint64_t ret;
+	unsigned int ret;
 
-	if (WARN(latency == 0, "Latency value missing\n"))
-		return UINT_MAX;
-
-	ret = (uint64_t) pixel_rate * cpp * latency;
-	ret = DIV_ROUND_UP_ULL(ret, 64 * 10000) + 2;
+	ret = intel_wm_method1(pixel_rate, cpp, latency);
+	ret = DIV_ROUND_UP(ret, 64) + 2;
 
 	return ret;
 }
 
 /* latency must be in 0.1us units. */
-static uint32_t ilk_wm_method2(uint32_t pixel_rate, uint32_t pipe_htotal,
-			       uint32_t horiz_pixels, uint8_t cpp,
-			       uint32_t latency)
+static unsigned int ilk_wm_method2(unsigned int pixel_rate,
+				   unsigned int htotal,
+				   unsigned int width,
+				   unsigned int cpp,
+				   unsigned int latency)
 {
-	uint32_t ret;
+	unsigned int ret;
 
-	if (WARN(latency == 0, "Latency value missing\n"))
-		return UINT_MAX;
-	if (WARN_ON(!pipe_htotal))
-		return UINT_MAX;
-
-	ret = (latency * pixel_rate) / (pipe_htotal * 10000);
-	ret = (ret + 1) * horiz_pixels * cpp;
+	ret = intel_wm_method2(pixel_rate, htotal,
+			       width, cpp, latency);
 	ret = DIV_ROUND_UP(ret, 64) + 2;
+
 	return ret;
 }
 
-- 
2.10.2

_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH 11/15] drm/i915: Apply the g4x TLB miss w/a to SR watermarks as well
  2017-04-21 18:14 [PATCH 00/15] drm/i915: Two stage watermarks for g4x ville.syrjala
                   ` (9 preceding siblings ...)
  2017-04-21 18:14 ` [PATCH 10/15] drm/i915: Refactor wm calculations ville.syrjala
@ 2017-04-21 18:14 ` ville.syrjala
  2017-04-21 18:14 ` [PATCH 12/15] drm/i915: Two stage watermarks for g4x ville.syrjala
                   ` (5 subsequent siblings)
  16 siblings, 0 replies; 21+ messages in thread
From: ville.syrjala @ 2017-04-21 18:14 UTC (permalink / raw)
  To: intel-gfx

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

The documentation I've seen doesn't actually specify which watermarks
need the TLB miss w/a. Currently we only apply the w/a to the normal
watermarks for both primary and cursor planes. Since the documentation
doesn't explicitly say anything I'm going to assume that the w/a should
equally apply to the SR/HPLL watermarks. So let's do that.

Signed-off-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
---
 drivers/gpu/drm/i915/intel_pm.c | 15 +++++++++------
 1 file changed, 9 insertions(+), 6 deletions(-)

diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c
index c07f3b2b0972..61b67994c4a8 100644
--- a/drivers/gpu/drm/i915/intel_pm.c
+++ b/drivers/gpu/drm/i915/intel_pm.c
@@ -1006,7 +1006,7 @@ static bool g4x_compute_srwm(struct drm_i915_private *dev_priv,
 	struct intel_crtc *crtc;
 	const struct drm_display_mode *adjusted_mode;
 	const struct drm_framebuffer *fb;
-	int hdisplay, htotal, cpp, clock;
+	int plane_width, cursor_width, htotal, cpp, clock;
 	int small, large;
 	int entries;
 
@@ -1020,20 +1020,23 @@ static bool g4x_compute_srwm(struct drm_i915_private *dev_priv,
 	fb = crtc->base.primary->state->fb;
 	clock = adjusted_mode->crtc_clock;
 	htotal = adjusted_mode->crtc_htotal;
-	hdisplay = crtc->config->pipe_src_w;
+	plane_width = crtc->config->pipe_src_w;
+	cursor_width = crtc->base.cursor->state->crtc_w;
 	cpp = fb->format->cpp[0];
 
 	/* Use the minimum of the small and large buffer method for primary */
 	small = intel_wm_method1(clock, cpp, latency_ns / 100);
-	large = intel_wm_method2(clock, htotal, hdisplay, cpp,
+	large = intel_wm_method2(clock, htotal, plane_width, cpp,
 				 latency_ns / 100);
-	entries = DIV_ROUND_UP(min(small, large), display->cacheline_size);
+	entries = min(small, large);
+	entries += g4x_tlb_miss_wa(display->fifo_size, plane_width, cpp);
+	entries = DIV_ROUND_UP(entries, display->cacheline_size);
 	*display_wm = entries + display->guard_size;
 
 	/* calculate the self-refresh watermark for display cursor */
-	entries = intel_wm_method2(clock, htotal,
-				   crtc->base.cursor->state->crtc_w, 4,
+	entries = intel_wm_method2(clock, htotal, cursor_width, 4,
 				   latency_ns / 100);
+	entries += g4x_tlb_miss_wa(cursor->fifo_size, cursor_width, 4);
 	entries = DIV_ROUND_UP(entries, cursor->cacheline_size);
 	*cursor_wm = entries + cursor->guard_size;
 
-- 
2.10.2

_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH 12/15] drm/i915: Two stage watermarks for g4x
  2017-04-21 18:14 [PATCH 00/15] drm/i915: Two stage watermarks for g4x ville.syrjala
                   ` (10 preceding siblings ...)
  2017-04-21 18:14 ` [PATCH 11/15] drm/i915: Apply the g4x TLB miss w/a to SR watermarks as well ville.syrjala
@ 2017-04-21 18:14 ` ville.syrjala
  2017-04-24  7:34   ` Maarten Lankhorst
  2017-04-21 18:14 ` [PATCH 13/15] drm/i915: Enable HPLL watermarks on g4x ville.syrjala
                   ` (4 subsequent siblings)
  16 siblings, 1 reply; 21+ messages in thread
From: ville.syrjala @ 2017-04-21 18:14 UTC (permalink / raw)
  To: intel-gfx

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

Implement proper two stage watermark programming for g4x. As with
other pre-SKL platforms, the watermark registers aren't double
buffered on g4x. Hence we must sequence the watermark update
carefully around plane updates.

The code is quite heavily modelled on the VLV/CHV code, with some
fairly significant differences due to the different hardware
architecture:
* g4x doesn't use inverted watermark values
* CxSR actually affects the watermarks since it controls memory self
  refresh in addition to the max FIFO mode
* A further HPLL SR mode is possible with higher memory wakeup
  latency
* g4x has FBC2 and so it also has FBC watermarks
* max FIFO mode for primary plane only (cursor is allowed, sprite is not)
* g4x has no manual FIFO repartitioning
* some TLB miss related workarounds are needed for the watermarks

Actually the hardware is quite similar to ILK+ in many ways. The
most visible differences are in the actual watermakr register
layout. ILK revamped that part quite heavily whereas g4x is still
using the layout inherited from earlier platforms.

Note that we didn't previously enable the HPLL SR on g4x. So in order
to not introduce too many functional changes in this patch I've not
actually enabled it here either, even though the code is now fully
ready for it. We'll enable it separately later on.

Signed-off-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
---
 drivers/gpu/drm/i915/i915_debugfs.c  |  12 +-
 drivers/gpu/drm/i915/i915_drv.h      |  12 +
 drivers/gpu/drm/i915/intel_display.c |  25 +-
 drivers/gpu/drm/i915/intel_drv.h     |  28 ++
 drivers/gpu/drm/i915/intel_pm.c      | 942 +++++++++++++++++++++++++++--------
 5 files changed, 792 insertions(+), 227 deletions(-)

diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c
index 870c470177b5..69550d31099e 100644
--- a/drivers/gpu/drm/i915/i915_debugfs.c
+++ b/drivers/gpu/drm/i915/i915_debugfs.c
@@ -3898,6 +3898,8 @@ static void wm_latency_show(struct seq_file *m, const uint16_t wm[8])
 		num_levels = 3;
 	else if (IS_VALLEYVIEW(dev_priv))
 		num_levels = 1;
+	else if (IS_G4X(dev_priv))
+		num_levels = 3;
 	else
 		num_levels = ilk_wm_max_level(dev_priv) + 1;
 
@@ -3910,8 +3912,10 @@ static void wm_latency_show(struct seq_file *m, const uint16_t wm[8])
 		 * - WM1+ latency values in 0.5us units
 		 * - latencies are in us on gen9/vlv/chv
 		 */
-		if (INTEL_GEN(dev_priv) >= 9 || IS_VALLEYVIEW(dev_priv) ||
-		    IS_CHERRYVIEW(dev_priv))
+		if (INTEL_GEN(dev_priv) >= 9 ||
+		    IS_VALLEYVIEW(dev_priv) ||
+		    IS_CHERRYVIEW(dev_priv) ||
+		    IS_G4X(dev_priv))
 			latency *= 10;
 		else if (level > 0)
 			latency *= 5;
@@ -3972,7 +3976,7 @@ static int pri_wm_latency_open(struct inode *inode, struct file *file)
 {
 	struct drm_i915_private *dev_priv = inode->i_private;
 
-	if (INTEL_GEN(dev_priv) < 5)
+	if (INTEL_GEN(dev_priv) < 5 && !IS_G4X(dev_priv))
 		return -ENODEV;
 
 	return single_open(file, pri_wm_latency_show, dev_priv);
@@ -4014,6 +4018,8 @@ static ssize_t wm_latency_write(struct file *file, const char __user *ubuf,
 		num_levels = 3;
 	else if (IS_VALLEYVIEW(dev_priv))
 		num_levels = 1;
+	else if (IS_G4X(dev_priv))
+		num_levels = 3;
 	else
 		num_levels = ilk_wm_max_level(dev_priv) + 1;
 
diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
index 0a393fdc53d1..6df8bff7f5a7 100644
--- a/drivers/gpu/drm/i915/i915_drv.h
+++ b/drivers/gpu/drm/i915/i915_drv.h
@@ -1762,11 +1762,13 @@ struct ilk_wm_values {
 
 struct g4x_pipe_wm {
 	uint16_t plane[I915_MAX_PLANES];
+	uint16_t fbc;
 };
 
 struct g4x_sr_wm {
 	uint16_t plane;
 	uint16_t cursor;
+	uint16_t fbc;
 };
 
 struct vlv_wm_ddl_values {
@@ -1781,6 +1783,15 @@ struct vlv_wm_values {
 	bool cxsr;
 };
 
+struct g4x_wm_values {
+	struct g4x_pipe_wm pipe[2];
+	struct g4x_sr_wm sr;
+	struct g4x_sr_wm hpll;
+	bool cxsr;
+	bool hpll_en;
+	bool fbc_en;
+};
+
 struct skl_ddb_entry {
 	uint16_t start, end;	/* in number of blocks, 'end' is exclusive */
 };
@@ -2410,6 +2421,7 @@ struct drm_i915_private {
 			struct ilk_wm_values hw;
 			struct skl_wm_values skl_hw;
 			struct vlv_wm_values vlv;
+			struct g4x_wm_values g4x;
 		};
 
 		uint8_t max_level;
diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c
index 85b9e2f521a0..0f42263c3f76 100644
--- a/drivers/gpu/drm/i915/intel_display.c
+++ b/drivers/gpu/drm/i915/intel_display.c
@@ -5719,6 +5719,8 @@ static void i9xx_set_pll_dividers(struct intel_crtc *crtc)
 static void i9xx_crtc_enable(struct intel_crtc_state *pipe_config,
 			     struct drm_atomic_state *old_state)
 {
+	struct intel_atomic_state *old_intel_state =
+		to_intel_atomic_state(old_state);
 	struct drm_crtc *crtc = pipe_config->base.crtc;
 	struct drm_device *dev = crtc->dev;
 	struct drm_i915_private *dev_priv = to_i915(dev);
@@ -5751,7 +5753,11 @@ static void i9xx_crtc_enable(struct intel_crtc_state *pipe_config,
 
 	intel_color_load_luts(&pipe_config->base);
 
-	intel_update_watermarks(intel_crtc);
+	if (dev_priv->display.initial_watermarks != NULL)
+		dev_priv->display.initial_watermarks(old_intel_state,
+						     intel_crtc->config);
+	else
+		intel_update_watermarks(intel_crtc);
 	intel_enable_pipe(intel_crtc);
 
 	assert_vblank_disabled(crtc);
@@ -10852,21 +10858,21 @@ int intel_plane_atomic_calc_changes(struct drm_crtc_state *crtc_state,
 			 turn_off, turn_on, mode_changed);
 
 	if (turn_on) {
-		if (INTEL_GEN(dev_priv) < 5)
+		if (INTEL_GEN(dev_priv) < 5 && !IS_G4X(dev_priv))
 			pipe_config->update_wm_pre = true;
 
 		/* must disable cxsr around plane enable/disable */
 		if (plane->id != PLANE_CURSOR)
 			pipe_config->disable_cxsr = true;
 	} else if (turn_off) {
-		if (INTEL_GEN(dev_priv) < 5)
+		if (INTEL_GEN(dev_priv) < 5 && !IS_G4X(dev_priv))
 			pipe_config->update_wm_post = true;
 
 		/* must disable cxsr around plane enable/disable */
 		if (plane->id != PLANE_CURSOR)
 			pipe_config->disable_cxsr = true;
 	} else if (intel_wm_need_update(&plane->base, plane_state)) {
-		if (INTEL_GEN(dev_priv) < 5) {
+		if (INTEL_GEN(dev_priv) < 5 && !IS_G4X(dev_priv)) {
 			/* FIXME bollocks */
 			pipe_config->update_wm_pre = true;
 			pipe_config->update_wm_post = true;
@@ -11290,7 +11296,8 @@ clear_intel_crtc_state(struct intel_crtc_state *crtc_state)
 	shared_dpll = crtc_state->shared_dpll;
 	dpll_hw_state = crtc_state->dpll_hw_state;
 	force_thru = crtc_state->pch_pfit.force_thru;
-	if (IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv))
+	if (IS_G4X(dev_priv) ||
+	    IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv))
 		wm_state = crtc_state->wm;
 
 	/* Keep base drm_crtc_state intact, only clear our extended struct */
@@ -11302,7 +11309,8 @@ clear_intel_crtc_state(struct intel_crtc_state *crtc_state)
 	crtc_state->shared_dpll = shared_dpll;
 	crtc_state->dpll_hw_state = dpll_hw_state;
 	crtc_state->pch_pfit.force_thru = force_thru;
-	if (IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv))
+	if (IS_G4X(dev_priv) ||
+	    IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv))
 		crtc_state->wm = wm_state;
 }
 
@@ -15527,7 +15535,10 @@ intel_modeset_setup_hw_state(struct drm_device *dev)
 		pll->on = false;
 	}
 
-	if (IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv)) {
+	if (IS_G4X(dev_priv)) {
+		g4x_wm_get_hw_state(dev);
+		g4x_wm_sanitize(dev_priv);
+	} else if (IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv)) {
 		vlv_wm_get_hw_state(dev);
 		vlv_wm_sanitize(dev_priv);
 	} else if (IS_GEN9(dev_priv)) {
diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
index d1cdd10998fa..f530df71a480 100644
--- a/drivers/gpu/drm/i915/intel_drv.h
+++ b/drivers/gpu/drm/i915/intel_drv.h
@@ -522,6 +522,22 @@ struct vlv_fifo_state {
 	u16 plane[I915_MAX_PLANES];
 };
 
+enum g4x_wm_level {
+	G4X_WM_LEVEL_NORMAL,
+	G4X_WM_LEVEL_SR,
+	G4X_WM_LEVEL_HPLL,
+	NUM_G4X_WM_LEVELS,
+};
+
+struct g4x_wm_state {
+	struct g4x_pipe_wm wm;
+	struct g4x_sr_wm sr;
+	struct g4x_sr_wm hpll;
+	bool cxsr;
+	bool hpll_en;
+	bool fbc_en;
+};
+
 struct intel_crtc_wm_state {
 	union {
 		struct {
@@ -557,6 +573,15 @@ struct intel_crtc_wm_state {
 			/* display FIFO split */
 			struct vlv_fifo_state fifo_state;
 		} vlv;
+
+		struct {
+			/* "raw" watermarks */
+			struct g4x_pipe_wm raw[NUM_G4X_WM_LEVELS];
+			/* intermediate watermarks */
+			struct g4x_wm_state intermediate;
+			/* optimal watermarks */
+			struct g4x_wm_state optimal;
+		} g4x;
 	};
 
 	/*
@@ -794,6 +819,7 @@ struct intel_crtc {
 		union {
 			struct intel_pipe_wm ilk;
 			struct vlv_wm_state vlv;
+			struct g4x_wm_state g4x;
 		} active;
 	} wm;
 
@@ -1841,6 +1867,7 @@ void gen6_rps_boost(struct drm_i915_private *dev_priv,
 		    struct intel_rps_client *rps,
 		    unsigned long submitted);
 void intel_queue_rps_boost_for_request(struct drm_i915_gem_request *req);
+void g4x_wm_get_hw_state(struct drm_device *dev);
 void vlv_wm_get_hw_state(struct drm_device *dev);
 void ilk_wm_get_hw_state(struct drm_device *dev);
 void skl_wm_get_hw_state(struct drm_device *dev);
@@ -1848,6 +1875,7 @@ void skl_ddb_get_hw_state(struct drm_i915_private *dev_priv,
 			  struct skl_ddb_allocation *ddb /* out */);
 void skl_pipe_wm_get_hw_state(struct drm_crtc *crtc,
 			      struct skl_pipe_wm *out);
+void g4x_wm_sanitize(struct drm_i915_private *dev_priv);
 void vlv_wm_sanitize(struct drm_i915_private *dev_priv);
 bool intel_can_enable_sagv(struct drm_atomic_state *state);
 int intel_enable_sagv(struct drm_i915_private *dev_priv);
diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c
index 61b67994c4a8..9b0a6a4572ce 100644
--- a/drivers/gpu/drm/i915/intel_pm.c
+++ b/drivers/gpu/drm/i915/intel_pm.c
@@ -429,7 +429,10 @@ bool intel_set_memory_cxsr(struct drm_i915_private *dev_priv, bool enable)
 
 	mutex_lock(&dev_priv->wm.wm_mutex);
 	ret = _intel_set_memory_cxsr(dev_priv, enable);
-	dev_priv->wm.vlv.cxsr = enable;
+	if (IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv))
+		dev_priv->wm.vlv.cxsr = enable;
+	else if (IS_G4X(dev_priv))
+		dev_priv->wm.g4x.cxsr = enable;
 	mutex_unlock(&dev_priv->wm.wm_mutex);
 
 	return ret;
@@ -568,20 +571,6 @@ static const struct intel_watermark_params pineview_cursor_hplloff_wm = {
 	.guard_size = PINEVIEW_CURSOR_GUARD_WM,
 	.cacheline_size = PINEVIEW_FIFO_LINE_SIZE,
 };
-static const struct intel_watermark_params g4x_wm_info = {
-	.fifo_size = G4X_FIFO_SIZE,
-	.max_wm = G4X_MAX_WM,
-	.default_wm = G4X_MAX_WM,
-	.guard_size = 2,
-	.cacheline_size = G4X_FIFO_LINE_SIZE,
-};
-static const struct intel_watermark_params g4x_cursor_wm_info = {
-	.fifo_size = I965_CURSOR_FIFO,
-	.max_wm = I965_CURSOR_MAX_WM,
-	.default_wm = I965_CURSOR_DFT_WM,
-	.guard_size = 2,
-	.cacheline_size = G4X_FIFO_LINE_SIZE,
-};
 static const struct intel_watermark_params i965_cursor_wm_info = {
 	.fifo_size = I965_CURSOR_FIFO,
 	.max_wm = I965_CURSOR_MAX_WM,
@@ -780,6 +769,16 @@ static unsigned int intel_calculate_wm(int pixel_rate,
 	return wm_size;
 }
 
+static bool is_disabling(int old, int new, int threshold)
+{
+	return old >= threshold && new < threshold;
+}
+
+static bool is_enabling(int old, int new, int threshold)
+{
+	return old < threshold && new >= threshold;
+}
+
 static int intel_wm_num_levels(struct drm_i915_private *dev_priv)
 {
 	return dev_priv->wm.max_level + 1;
@@ -911,138 +910,28 @@ static int g4x_tlb_miss_wa(int fifo_size, int width, int cpp)
 	return max(0, tlb_miss);
 }
 
-static bool g4x_compute_wm0(struct drm_i915_private *dev_priv,
-			    int plane,
-			    const struct intel_watermark_params *display,
-			    int display_latency_ns,
-			    const struct intel_watermark_params *cursor,
-			    int cursor_latency_ns,
-			    int *plane_wm,
-			    int *cursor_wm)
+static void g4x_write_wm_values(struct drm_i915_private *dev_priv,
+				const struct g4x_wm_values *wm)
 {
-	struct intel_crtc *crtc;
-	const struct drm_display_mode *adjusted_mode;
-	const struct drm_framebuffer *fb;
-	int htotal, plane_width, cursor_width, clock, cpp;
-	int entries;
-
-	crtc = intel_get_crtc_for_plane(dev_priv, plane);
-	if (!intel_crtc_active(crtc)) {
-		*cursor_wm = cursor->guard_size;
-		*plane_wm = display->guard_size;
-		return false;
-	}
-
-	adjusted_mode = &crtc->config->base.adjusted_mode;
-	fb = crtc->base.primary->state->fb;
-	clock = adjusted_mode->crtc_clock;
-	htotal = adjusted_mode->crtc_htotal;
-	plane_width = crtc->config->pipe_src_w;
-	cursor_width = crtc->base.cursor->state->crtc_w;
-	cpp = fb->format->cpp[0];
-
-	/* Use the small buffer method to calculate plane watermark */
-	entries = intel_wm_method1(clock, cpp, display_latency_ns / 100);
-	entries += g4x_tlb_miss_wa(display->fifo_size, plane_width, cpp);
-	entries = DIV_ROUND_UP(entries, display->cacheline_size);
-	*plane_wm = entries + display->guard_size;
-	if (*plane_wm > (int)display->max_wm)
-		*plane_wm = display->max_wm;
-
-	/* Use the large buffer method to calculate cursor watermark */
-	entries = intel_wm_method2(clock, htotal, cursor_width, 4,
-				   cursor_latency_ns / 100);
-	entries += g4x_tlb_miss_wa(cursor->fifo_size, cursor_width, 4);
-	entries = DIV_ROUND_UP(entries, cursor->cacheline_size);
-	*cursor_wm = entries + cursor->guard_size;
-	if (*cursor_wm > (int)cursor->max_wm)
-		*cursor_wm = (int)cursor->max_wm;
-
-	return true;
-}
-
-/*
- * Check the wm result.
- *
- * If any calculated watermark values is larger than the maximum value that
- * can be programmed into the associated watermark register, that watermark
- * must be disabled.
- */
-static bool g4x_check_srwm(struct drm_i915_private *dev_priv,
-			   int display_wm, int cursor_wm,
-			   const struct intel_watermark_params *display,
-			   const struct intel_watermark_params *cursor)
-{
-	DRM_DEBUG_KMS("SR watermark: display plane %d, cursor %d\n",
-		      display_wm, cursor_wm);
-
-	if (display_wm > display->max_wm) {
-		DRM_DEBUG_KMS("display watermark is too large(%d/%u), disabling\n",
-			      display_wm, display->max_wm);
-		return false;
-	}
-
-	if (cursor_wm > cursor->max_wm) {
-		DRM_DEBUG_KMS("cursor watermark is too large(%d/%u), disabling\n",
-			      cursor_wm, cursor->max_wm);
-		return false;
-	}
-
-	if (!(display_wm || cursor_wm)) {
-		DRM_DEBUG_KMS("SR latency is 0, disabling\n");
-		return false;
-	}
-
-	return true;
-}
-
-static bool g4x_compute_srwm(struct drm_i915_private *dev_priv,
-			     int plane,
-			     int latency_ns,
-			     const struct intel_watermark_params *display,
-			     const struct intel_watermark_params *cursor,
-			     int *display_wm, int *cursor_wm)
-{
-	struct intel_crtc *crtc;
-	const struct drm_display_mode *adjusted_mode;
-	const struct drm_framebuffer *fb;
-	int plane_width, cursor_width, htotal, cpp, clock;
-	int small, large;
-	int entries;
-
-	if (!latency_ns) {
-		*display_wm = *cursor_wm = 0;
-		return false;
-	}
-
-	crtc = intel_get_crtc_for_plane(dev_priv, plane);
-	adjusted_mode = &crtc->config->base.adjusted_mode;
-	fb = crtc->base.primary->state->fb;
-	clock = adjusted_mode->crtc_clock;
-	htotal = adjusted_mode->crtc_htotal;
-	plane_width = crtc->config->pipe_src_w;
-	cursor_width = crtc->base.cursor->state->crtc_w;
-	cpp = fb->format->cpp[0];
-
-	/* Use the minimum of the small and large buffer method for primary */
-	small = intel_wm_method1(clock, cpp, latency_ns / 100);
-	large = intel_wm_method2(clock, htotal, plane_width, cpp,
-				 latency_ns / 100);
-	entries = min(small, large);
-	entries += g4x_tlb_miss_wa(display->fifo_size, plane_width, cpp);
-	entries = DIV_ROUND_UP(entries, display->cacheline_size);
-	*display_wm = entries + display->guard_size;
-
-	/* calculate the self-refresh watermark for display cursor */
-	entries = intel_wm_method2(clock, htotal, cursor_width, 4,
-				   latency_ns / 100);
-	entries += g4x_tlb_miss_wa(cursor->fifo_size, cursor_width, 4);
-	entries = DIV_ROUND_UP(entries, cursor->cacheline_size);
-	*cursor_wm = entries + cursor->guard_size;
-
-	return g4x_check_srwm(dev_priv,
-			      *display_wm, *cursor_wm,
-			      display, cursor);
+	I915_WRITE(DSPFW1,
+		   FW_WM(wm->sr.plane, SR) |
+		   FW_WM(wm->pipe[PIPE_B].plane[PLANE_CURSOR], CURSORB) |
+		   FW_WM(wm->pipe[PIPE_B].plane[PLANE_PRIMARY], PLANEB) |
+		   FW_WM(wm->pipe[PIPE_A].plane[PLANE_PRIMARY], PLANEA));
+	I915_WRITE(DSPFW2,
+		   (wm->fbc_en ? DSPFW_FBC_SR_EN : 0) |
+		   FW_WM(wm->sr.fbc, FBC_SR) |
+		   FW_WM(wm->hpll.fbc, FBC_HPLL_SR) |
+		   FW_WM(wm->pipe[PIPE_B].plane[PLANE_SPRITE0], SPRITEB) |
+		   FW_WM(wm->pipe[PIPE_A].plane[PLANE_CURSOR], CURSORA) |
+		   FW_WM(wm->pipe[PIPE_A].plane[PLANE_SPRITE0], SPRITEA));
+	I915_WRITE(DSPFW3,
+		   (wm->hpll_en ? DSPFW_HPLL_SR_EN : 0) |
+		   FW_WM(wm->sr.cursor, CURSOR_SR) |
+		   FW_WM(wm->hpll.cursor, HPLL_CURSOR) |
+		   FW_WM(wm->hpll.plane, HPLL_SR));
+
+	POSTING_READ(DSPFW1);
 }
 
 #define FW_WM_VLV(value, plane) \
@@ -1126,6 +1015,523 @@ static void vlv_write_wm_values(struct drm_i915_private *dev_priv,
 
 #undef FW_WM_VLV
 
+static void g4x_setup_wm_latency(struct drm_i915_private *dev_priv)
+{
+	/* all latencies in usec */
+	dev_priv->wm.pri_latency[G4X_WM_LEVEL_NORMAL] = 5;
+	dev_priv->wm.pri_latency[G4X_WM_LEVEL_SR] = 12;
+
+	dev_priv->wm.max_level = G4X_WM_LEVEL_SR;
+}
+
+static int g4x_plane_fifo_size(enum plane_id plane_id, int level)
+{
+	/*
+	 * DSPCNTR[13] supposedly controls whether the
+	 * primary plane can use the FIFO space otherwise
+	 * reserved for the sprite plane. It's not 100% clear
+	 * what the actual FIFO size is, but it looks like we
+	 * can happily set both primary and sprite watermarks
+	 * up to 127 cachelines. So that would seem to mean
+	 * that either DSPCNTR[13] doesn't do anything, or that
+	 * the total FIFO is >= 256 cachelines in size. Either
+	 * way, we don't seem to have to worry about this
+	 * repartitioning as the maximum watermark value the
+	 * register can hold for each plane is lower than the
+	 * minimum FIFO size.
+	 */
+	switch (plane_id) {
+	case PLANE_CURSOR:
+		return 63;
+	case PLANE_PRIMARY:
+		return level == G4X_WM_LEVEL_NORMAL ? 127 : 511;
+	case PLANE_SPRITE0:
+		return level == G4X_WM_LEVEL_NORMAL ? 127 : 0;
+	default:
+		MISSING_CASE(plane_id);
+		return 0;
+	}
+}
+
+static int g4x_fbc_fifo_size(int level)
+{
+	switch (level) {
+	case G4X_WM_LEVEL_SR:
+		return 7;
+	case G4X_WM_LEVEL_HPLL:
+		return 15;
+	default:
+		MISSING_CASE(level);
+		return 0;
+	}
+}
+
+static uint16_t g4x_compute_wm(const struct intel_crtc_state *crtc_state,
+			       const struct intel_plane_state *plane_state,
+			       int level)
+{
+	struct intel_plane *plane = to_intel_plane(plane_state->base.plane);
+	struct drm_i915_private *dev_priv = to_i915(plane->base.dev);
+	const struct drm_display_mode *adjusted_mode =
+		&crtc_state->base.adjusted_mode;
+	int clock, htotal, cpp, width, wm;
+	int latency = dev_priv->wm.pri_latency[level] * 10;
+
+	if (latency == 0)
+		return USHRT_MAX;
+
+	if (!intel_wm_plane_visible(crtc_state, plane_state))
+		return 0;
+
+	/*
+	 * Not 100% sure which way ELK should go here as the
+	 * spec only says CL/CTG should assume 32bpp and BW
+	 * doesn't need to. But as these things followed the
+	 * mobile vs. desktop lines on gen3 as well, let's
+	 * assume ELK doesn't need this.
+	 *
+	 * The spec also fails to list such a restriction for
+	 * the HPLL watermark, which seems a little strange.
+	 * Let's use 32bpp for the HPLL watermark as well.
+	 */
+	if (IS_GM45(dev_priv) && plane->id == PLANE_PRIMARY &&
+	    level != G4X_WM_LEVEL_NORMAL)
+		cpp = 4;
+	else
+		cpp = plane_state->base.fb->format->cpp[0];
+
+	clock = adjusted_mode->crtc_clock;
+	htotal = adjusted_mode->crtc_htotal;
+
+	if (plane->id == PLANE_CURSOR)
+		width = plane_state->base.crtc_w;
+	else
+		width = drm_rect_width(&plane_state->base.dst);
+
+	if (plane->id == PLANE_CURSOR) {
+		wm = intel_wm_method2(clock, htotal, width, cpp, latency);
+	} else if (plane->id == PLANE_PRIMARY &&
+		   level == G4X_WM_LEVEL_NORMAL) {
+		wm = intel_wm_method1(clock, cpp, latency);
+	} else {
+		int small, large;
+
+		small = intel_wm_method1(clock, cpp, latency);
+		large = intel_wm_method2(clock, htotal, width, cpp, latency);
+
+		wm = min(small, large);
+	}
+
+	wm += g4x_tlb_miss_wa(g4x_plane_fifo_size(plane->id, level),
+			      width, cpp);
+
+	wm = DIV_ROUND_UP(wm, 64) + 2;
+
+	return min_t(int, wm, USHRT_MAX);
+}
+
+static bool g4x_raw_plane_wm_set(struct intel_crtc_state *crtc_state,
+				 int level, enum plane_id plane_id, u16 value)
+{
+	struct drm_i915_private *dev_priv = to_i915(crtc_state->base.crtc->dev);
+	bool dirty = false;
+
+	for (; level < intel_wm_num_levels(dev_priv); level++) {
+		struct g4x_pipe_wm *raw = &crtc_state->wm.g4x.raw[level];
+
+		dirty |= raw->plane[plane_id] != value;
+		raw->plane[plane_id] = value;
+	}
+
+	return dirty;
+}
+
+static bool g4x_raw_fbc_wm_set(struct intel_crtc_state *crtc_state,
+			       int level, u16 value)
+{
+	struct drm_i915_private *dev_priv = to_i915(crtc_state->base.crtc->dev);
+	bool dirty = false;
+
+	/* NORMAL level doesn't have an FBC watermark */
+	level = max(level, G4X_WM_LEVEL_SR);
+
+	for (; level < intel_wm_num_levels(dev_priv); level++) {
+		struct g4x_pipe_wm *raw = &crtc_state->wm.g4x.raw[level];
+
+		dirty |= raw->fbc != value;
+		raw->fbc = value;
+	}
+
+	return dirty;
+}
+
+static uint32_t ilk_compute_fbc_wm(const struct intel_crtc_state *cstate,
+				   const struct intel_plane_state *pstate,
+				   uint32_t pri_val);
+
+static bool g4x_raw_plane_wm_compute(struct intel_crtc_state *crtc_state,
+				     const struct intel_plane_state *plane_state)
+{
+	struct intel_plane *plane = to_intel_plane(plane_state->base.plane);
+	int num_levels = intel_wm_num_levels(to_i915(plane->base.dev));
+	enum plane_id plane_id = plane->id;
+	bool dirty = false;
+	int level;
+
+	if (!intel_wm_plane_visible(crtc_state, plane_state)) {
+		dirty |= g4x_raw_plane_wm_set(crtc_state, 0, plane_id, 0);
+		if (plane_id == PLANE_PRIMARY)
+			dirty |= g4x_raw_fbc_wm_set(crtc_state, 0, 0);
+		goto out;
+	}
+
+	for (level = 0; level < num_levels; level++) {
+		struct g4x_pipe_wm *raw = &crtc_state->wm.g4x.raw[level];
+		int wm, max_wm;
+
+		wm = g4x_compute_wm(crtc_state, plane_state, level);
+		max_wm = g4x_plane_fifo_size(plane_id, level);
+
+		if (wm > max_wm)
+			break;
+
+		dirty |= raw->plane[plane_id] != wm;
+		raw->plane[plane_id] = wm;
+
+		if (plane_id != PLANE_PRIMARY ||
+		    level == G4X_WM_LEVEL_NORMAL)
+			continue;
+
+		wm = ilk_compute_fbc_wm(crtc_state, plane_state,
+					raw->plane[plane_id]);
+		max_wm = g4x_fbc_fifo_size(level);
+
+		/*
+		 * FBC wm is not mandatory as we
+		 * can always just disable its use.
+		 */
+		if (wm > max_wm)
+			wm = USHRT_MAX;
+
+		dirty |= raw->fbc != wm;
+		raw->fbc = wm;
+	}
+
+	/* mark watermarks as invalid */
+	dirty |= g4x_raw_plane_wm_set(crtc_state, level, plane_id, USHRT_MAX);
+
+	if (plane_id == PLANE_PRIMARY)
+		dirty |= g4x_raw_fbc_wm_set(crtc_state, level, USHRT_MAX);
+
+ out:
+	if (dirty) {
+		DRM_DEBUG_KMS("%s watermarks: normal=%d, SR=%d, HPLL=%d\n",
+			      plane->base.name,
+			      crtc_state->wm.g4x.raw[G4X_WM_LEVEL_NORMAL].plane[plane_id],
+			      crtc_state->wm.g4x.raw[G4X_WM_LEVEL_SR].plane[plane_id],
+			      crtc_state->wm.g4x.raw[G4X_WM_LEVEL_HPLL].plane[plane_id]);
+
+		if (plane_id == PLANE_PRIMARY)
+			DRM_DEBUG_KMS("FBC watermarks: SR=%d, HPLL=%d\n",
+				      crtc_state->wm.g4x.raw[G4X_WM_LEVEL_SR].fbc,
+				      crtc_state->wm.g4x.raw[G4X_WM_LEVEL_HPLL].fbc);
+	}
+
+	return dirty;
+}
+
+static bool g4x_raw_plane_wm_is_valid(const struct intel_crtc_state *crtc_state,
+				      enum plane_id plane_id, int level)
+{
+	const struct g4x_pipe_wm *raw = &crtc_state->wm.g4x.raw[level];
+
+	return raw->plane[plane_id] <= g4x_plane_fifo_size(plane_id, level);
+}
+
+static bool g4x_raw_crtc_wm_is_valid(const struct intel_crtc_state *crtc_state,
+				     int level)
+{
+	struct drm_i915_private *dev_priv = to_i915(crtc_state->base.crtc->dev);
+
+	if (level > dev_priv->wm.max_level)
+		return false;
+
+	return g4x_raw_plane_wm_is_valid(crtc_state, PLANE_PRIMARY, level) &&
+		g4x_raw_plane_wm_is_valid(crtc_state, PLANE_SPRITE0, level) &&
+		g4x_raw_plane_wm_is_valid(crtc_state, PLANE_CURSOR, level);
+}
+
+/* mark all levels starting from 'level' as invalid */
+static void g4x_invalidate_wms(struct intel_crtc *crtc,
+			       struct g4x_wm_state *wm_state, int level)
+{
+	if (level <= G4X_WM_LEVEL_NORMAL) {
+		enum plane_id plane_id;
+
+		for_each_plane_id_on_crtc(crtc, plane_id)
+			wm_state->wm.plane[plane_id] = USHRT_MAX;
+	}
+
+	if (level <= G4X_WM_LEVEL_SR) {
+		wm_state->cxsr = false;
+		wm_state->sr.cursor = USHRT_MAX;
+		wm_state->sr.plane = USHRT_MAX;
+		wm_state->sr.fbc = USHRT_MAX;
+	}
+
+	if (level <= G4X_WM_LEVEL_HPLL) {
+		wm_state->hpll_en = false;
+		wm_state->hpll.cursor = USHRT_MAX;
+		wm_state->hpll.plane = USHRT_MAX;
+		wm_state->hpll.fbc = USHRT_MAX;
+	}
+}
+
+static int g4x_compute_pipe_wm(struct intel_crtc_state *crtc_state)
+{
+	struct intel_crtc *crtc = to_intel_crtc(crtc_state->base.crtc);
+	struct intel_atomic_state *state =
+		to_intel_atomic_state(crtc_state->base.state);
+	struct g4x_wm_state *wm_state = &crtc_state->wm.g4x.optimal;
+	int num_active_planes = hweight32(crtc_state->active_planes &
+					  ~BIT(PLANE_CURSOR));
+	const struct g4x_pipe_wm *raw;
+	struct intel_plane_state *plane_state;
+	struct intel_plane *plane;
+	enum plane_id plane_id;
+	int i, level;
+	unsigned int dirty = 0;
+
+	for_each_intel_plane_in_state(state, plane, plane_state, i) {
+		const struct intel_plane_state *old_plane_state =
+			to_intel_plane_state(plane->base.state);
+
+		if (plane_state->base.crtc != &crtc->base &&
+		    old_plane_state->base.crtc != &crtc->base)
+			continue;
+
+		if (g4x_raw_plane_wm_compute(crtc_state, plane_state))
+			dirty |= BIT(plane->id);
+	}
+
+	if (!dirty)
+		return 0;
+
+	level = G4X_WM_LEVEL_NORMAL;
+	if (!g4x_raw_crtc_wm_is_valid(crtc_state, level))
+		goto out;
+
+	raw = &crtc_state->wm.g4x.raw[level];
+	for_each_plane_id_on_crtc(crtc, plane_id)
+		wm_state->wm.plane[plane_id] = raw->plane[plane_id];
+
+	level = G4X_WM_LEVEL_SR;
+
+	if (!g4x_raw_crtc_wm_is_valid(crtc_state, level))
+		goto out;
+
+	raw = &crtc_state->wm.g4x.raw[level];
+	wm_state->sr.plane = raw->plane[PLANE_PRIMARY];
+	wm_state->sr.cursor = raw->plane[PLANE_CURSOR];
+	wm_state->sr.fbc = raw->fbc;
+
+	wm_state->cxsr = num_active_planes == BIT(PLANE_PRIMARY);
+
+	level = G4X_WM_LEVEL_HPLL;
+
+	if (!g4x_raw_crtc_wm_is_valid(crtc_state, level))
+		goto out;
+
+	raw = &crtc_state->wm.g4x.raw[level];
+	wm_state->hpll.plane = raw->plane[PLANE_PRIMARY];
+	wm_state->hpll.cursor = raw->plane[PLANE_CURSOR];
+	wm_state->hpll.fbc = raw->fbc;
+
+	wm_state->hpll_en = wm_state->cxsr;
+
+	level++;
+
+ out:
+	if (level == G4X_WM_LEVEL_NORMAL)
+		return -EINVAL;
+
+	/* invalidate the higher levels */
+	g4x_invalidate_wms(crtc, wm_state, level);
+
+	/*
+	 * Determine if the FBC watermark(s) can be used. IF
+	 * this isn't the case we prefer to disable the FBC
+	 ( watermark(s) rather than disable the SR/HPLL
+	 * level(s) entirely.
+	 */
+	wm_state->fbc_en = level > G4X_WM_LEVEL_NORMAL;
+
+	if (level >= G4X_WM_LEVEL_SR &&
+	    wm_state->sr.fbc > g4x_fbc_fifo_size(G4X_WM_LEVEL_SR))
+		wm_state->fbc_en = false;
+	else if (level >= G4X_WM_LEVEL_HPLL &&
+		 wm_state->hpll.fbc > g4x_fbc_fifo_size(G4X_WM_LEVEL_HPLL))
+		wm_state->fbc_en = false;
+
+	return 0;
+}
+
+static int g4x_compute_intermediate_wm(struct drm_device *dev,
+				       struct intel_crtc *crtc,
+				       struct intel_crtc_state *crtc_state)
+{
+	struct g4x_wm_state *intermediate = &crtc_state->wm.g4x.intermediate;
+	const struct g4x_wm_state *optimal = &crtc_state->wm.g4x.optimal;
+	const struct g4x_wm_state *active = &crtc->wm.active.g4x;
+	enum plane_id plane_id;
+
+	intermediate->cxsr = optimal->cxsr && active->cxsr &&
+		!crtc_state->disable_cxsr;
+	intermediate->hpll_en = optimal->hpll_en && active->hpll_en &&
+		!crtc_state->disable_cxsr;
+	intermediate->fbc_en = optimal->fbc_en && active->fbc_en;
+
+	for_each_plane_id_on_crtc(crtc, plane_id) {
+		intermediate->wm.plane[plane_id] =
+			max(optimal->wm.plane[plane_id],
+			    active->wm.plane[plane_id]);
+
+		WARN_ON(intermediate->wm.plane[plane_id] >
+			g4x_plane_fifo_size(plane_id, G4X_WM_LEVEL_NORMAL));
+	}
+
+	intermediate->sr.plane = max(optimal->sr.plane,
+				     active->sr.plane);
+	intermediate->sr.cursor = max(optimal->sr.cursor,
+				      active->sr.cursor);
+	intermediate->sr.fbc = max(optimal->sr.fbc,
+				   active->sr.fbc);
+
+	intermediate->hpll.plane = max(optimal->hpll.plane,
+				       active->hpll.plane);
+	intermediate->hpll.cursor = max(optimal->hpll.cursor,
+					active->hpll.cursor);
+	intermediate->hpll.fbc = max(optimal->hpll.fbc,
+				     active->hpll.fbc);
+
+	WARN_ON((intermediate->sr.plane >
+		 g4x_plane_fifo_size(PLANE_PRIMARY, G4X_WM_LEVEL_SR) ||
+		 intermediate->sr.cursor >
+		 g4x_plane_fifo_size(PLANE_CURSOR, G4X_WM_LEVEL_SR)) &&
+		intermediate->cxsr);
+	WARN_ON((intermediate->sr.plane >
+		 g4x_plane_fifo_size(PLANE_PRIMARY, G4X_WM_LEVEL_HPLL) ||
+		 intermediate->sr.cursor >
+		 g4x_plane_fifo_size(PLANE_CURSOR, G4X_WM_LEVEL_HPLL)) &&
+		intermediate->hpll_en);
+
+	WARN_ON(intermediate->sr.fbc > g4x_fbc_fifo_size(1) &&
+		intermediate->fbc_en && intermediate->cxsr);
+	WARN_ON(intermediate->hpll.fbc > g4x_fbc_fifo_size(2) &&
+		intermediate->fbc_en && intermediate->hpll_en);
+
+	/*
+	 * If our intermediate WM are identical to the final WM, then we can
+	 * omit the post-vblank programming; only update if it's different.
+	 */
+	if (memcmp(intermediate, optimal, sizeof(*intermediate)) != 0)
+		crtc_state->wm.need_postvbl_update = true;
+
+	return 0;
+}
+
+static void g4x_merge_wm(struct drm_i915_private *dev_priv,
+			 struct g4x_wm_values *wm)
+{
+	struct intel_crtc *crtc;
+	int num_active_crtcs = 0;
+
+	wm->cxsr = true;
+	wm->hpll_en = true;
+	wm->fbc_en = true;
+
+	for_each_intel_crtc(&dev_priv->drm, crtc) {
+		const struct g4x_wm_state *wm_state = &crtc->wm.active.g4x;
+
+		if (!crtc->active)
+			continue;
+
+		if (!wm_state->cxsr)
+			wm->cxsr = false;
+		if (!wm_state->hpll_en)
+			wm->hpll_en = false;
+		if (!wm_state->fbc_en)
+			wm->fbc_en = false;
+
+		num_active_crtcs++;
+	}
+
+	if (num_active_crtcs != 1) {
+		wm->cxsr = false;
+		wm->hpll_en = false;
+		wm->fbc_en = false;
+	}
+
+	for_each_intel_crtc(&dev_priv->drm, crtc) {
+		const struct g4x_wm_state *wm_state = &crtc->wm.active.g4x;
+		enum pipe pipe = crtc->pipe;
+
+		wm->pipe[pipe] = wm_state->wm;
+		if (crtc->active && wm->cxsr)
+			wm->sr = wm_state->sr;
+		if (crtc->active && wm->hpll_en)
+			wm->hpll = wm_state->hpll;
+	}
+}
+
+static void g4x_program_watermarks(struct drm_i915_private *dev_priv)
+{
+	struct g4x_wm_values *old_wm = &dev_priv->wm.g4x;
+	struct g4x_wm_values new_wm = {};
+
+	g4x_merge_wm(dev_priv, &new_wm);
+
+	if (memcmp(old_wm, &new_wm, sizeof(new_wm)) == 0)
+		return;
+
+	if (is_disabling(old_wm->cxsr, new_wm.cxsr, true))
+		_intel_set_memory_cxsr(dev_priv, false);
+
+	g4x_write_wm_values(dev_priv, &new_wm);
+
+	if (is_enabling(old_wm->cxsr, new_wm.cxsr, true))
+		_intel_set_memory_cxsr(dev_priv, true);
+
+	*old_wm = new_wm;
+}
+
+static void g4x_initial_watermarks(struct intel_atomic_state *state,
+				   struct intel_crtc_state *crtc_state)
+{
+	struct drm_i915_private *dev_priv = to_i915(crtc_state->base.crtc->dev);
+	struct intel_crtc *crtc = to_intel_crtc(crtc_state->base.crtc);
+
+	mutex_lock(&dev_priv->wm.wm_mutex);
+	crtc->wm.active.g4x = crtc_state->wm.g4x.intermediate;
+	g4x_program_watermarks(dev_priv);
+	mutex_unlock(&dev_priv->wm.wm_mutex);
+}
+
+static void g4x_optimize_watermarks(struct intel_atomic_state *state,
+				    struct intel_crtc_state *crtc_state)
+{
+	struct drm_i915_private *dev_priv = to_i915(crtc_state->base.crtc->dev);
+	struct intel_crtc *intel_crtc = to_intel_crtc(crtc_state->base.crtc);
+
+	if (!crtc_state->wm.need_postvbl_update)
+		return;
+
+	mutex_lock(&dev_priv->wm.wm_mutex);
+	intel_crtc->wm.active.g4x = crtc_state->wm.g4x.optimal;
+	g4x_program_watermarks(dev_priv);
+	mutex_unlock(&dev_priv->wm.wm_mutex);
+}
+
 /* latency must be in 0.1us units. */
 static unsigned int vlv_wm_method2(unsigned int pixel_rate,
 				   unsigned int htotal,
@@ -1673,16 +2079,6 @@ static void vlv_merge_wm(struct drm_i915_private *dev_priv,
 	}
 }
 
-static bool is_disabling(int old, int new, int threshold)
-{
-	return old >= threshold && new < threshold;
-}
-
-static bool is_enabling(int old, int new, int threshold)
-{
-	return old < threshold && new >= threshold;
-}
-
 static void vlv_program_watermarks(struct drm_i915_private *dev_priv)
 {
 	struct vlv_wm_values *old_wm = &dev_priv->wm.vlv;
@@ -1743,65 +2139,6 @@ static void vlv_optimize_watermarks(struct intel_atomic_state *state,
 	mutex_unlock(&dev_priv->wm.wm_mutex);
 }
 
-#define single_plane_enabled(mask) is_power_of_2(mask)
-
-static void g4x_update_wm(struct intel_crtc *crtc)
-{
-	struct drm_i915_private *dev_priv = to_i915(crtc->base.dev);
-	static const int sr_latency_ns = 12000;
-	int planea_wm, planeb_wm, cursora_wm, cursorb_wm;
-	int plane_sr, cursor_sr;
-	unsigned int enabled = 0;
-	bool cxsr_enabled;
-
-	if (g4x_compute_wm0(dev_priv, PIPE_A,
-			    &g4x_wm_info, pessimal_latency_ns,
-			    &g4x_cursor_wm_info, pessimal_latency_ns,
-			    &planea_wm, &cursora_wm))
-		enabled |= 1 << PIPE_A;
-
-	if (g4x_compute_wm0(dev_priv, PIPE_B,
-			    &g4x_wm_info, pessimal_latency_ns,
-			    &g4x_cursor_wm_info, pessimal_latency_ns,
-			    &planeb_wm, &cursorb_wm))
-		enabled |= 1 << PIPE_B;
-
-	if (single_plane_enabled(enabled) &&
-	    g4x_compute_srwm(dev_priv, ffs(enabled) - 1,
-			     sr_latency_ns,
-			     &g4x_wm_info,
-			     &g4x_cursor_wm_info,
-			     &plane_sr, &cursor_sr)) {
-		cxsr_enabled = true;
-	} else {
-		cxsr_enabled = false;
-		intel_set_memory_cxsr(dev_priv, false);
-		plane_sr = cursor_sr = 0;
-	}
-
-	DRM_DEBUG_KMS("Setting FIFO watermarks - A: plane=%d, cursor=%d, "
-		      "B: plane=%d, cursor=%d, SR: plane=%d, cursor=%d\n",
-		      planea_wm, cursora_wm,
-		      planeb_wm, cursorb_wm,
-		      plane_sr, cursor_sr);
-
-	I915_WRITE(DSPFW1,
-		   FW_WM(plane_sr, SR) |
-		   FW_WM(cursorb_wm, CURSORB) |
-		   FW_WM(planeb_wm, PLANEB) |
-		   FW_WM(planea_wm, PLANEA));
-	I915_WRITE(DSPFW2,
-		   (I915_READ(DSPFW2) & ~DSPFW_CURSORA_MASK) |
-		   FW_WM(cursora_wm, CURSORA));
-	/* HPLL off in SR has some issues on G4x... disable it */
-	I915_WRITE(DSPFW3,
-		   (I915_READ(DSPFW3) & ~(DSPFW_HPLL_SR_EN | DSPFW_CURSOR_SR_MASK)) |
-		   FW_WM(cursor_sr, CURSOR_SR));
-
-	if (cxsr_enabled)
-		intel_set_memory_cxsr(dev_priv, true);
-}
-
 static void i965_update_wm(struct intel_crtc *unused_crtc)
 {
 	struct drm_i915_private *dev_priv = to_i915(unused_crtc->base.dev);
@@ -4778,6 +5115,32 @@ static void ilk_pipe_wm_get_hw_state(struct drm_crtc *crtc)
 #define _FW_WM_VLV(value, plane) \
 	(((value) & DSPFW_ ## plane ## _MASK_VLV) >> DSPFW_ ## plane ## _SHIFT)
 
+static void g4x_read_wm_values(struct drm_i915_private *dev_priv,
+			       struct g4x_wm_values *wm)
+{
+	uint32_t tmp;
+
+	tmp = I915_READ(DSPFW1);
+	wm->sr.plane = _FW_WM(tmp, SR);
+	wm->pipe[PIPE_B].plane[PLANE_CURSOR] = _FW_WM(tmp, CURSORB);
+	wm->pipe[PIPE_B].plane[PLANE_PRIMARY] = _FW_WM(tmp, PLANEB);
+	wm->pipe[PIPE_A].plane[PLANE_PRIMARY] = _FW_WM(tmp, PLANEA);
+
+	tmp = I915_READ(DSPFW2);
+	wm->fbc_en = tmp & DSPFW_FBC_SR_EN;
+	wm->sr.fbc = _FW_WM(tmp, FBC_SR);
+	wm->hpll.fbc = _FW_WM(tmp, FBC_HPLL_SR);
+	wm->pipe[PIPE_B].plane[PLANE_SPRITE0] = _FW_WM(tmp, SPRITEB);
+	wm->pipe[PIPE_A].plane[PLANE_CURSOR] = _FW_WM(tmp, CURSORA);
+	wm->pipe[PIPE_A].plane[PLANE_SPRITE0] = _FW_WM(tmp, SPRITEA);
+
+	tmp = I915_READ(DSPFW3);
+	wm->hpll_en = tmp & DSPFW_HPLL_SR_EN;
+	wm->sr.cursor = _FW_WM(tmp, CURSOR_SR);
+	wm->hpll.cursor = _FW_WM(tmp, HPLL_CURSOR);
+	wm->hpll.plane = _FW_WM(tmp, HPLL_SR);
+}
+
 static void vlv_read_wm_values(struct drm_i915_private *dev_priv,
 			       struct vlv_wm_values *wm)
 {
@@ -4854,6 +5217,147 @@ static void vlv_read_wm_values(struct drm_i915_private *dev_priv,
 #undef _FW_WM
 #undef _FW_WM_VLV
 
+void g4x_wm_get_hw_state(struct drm_device *dev)
+{
+	struct drm_i915_private *dev_priv = to_i915(dev);
+	struct g4x_wm_values *wm = &dev_priv->wm.g4x;
+	struct intel_crtc *crtc;
+
+	g4x_read_wm_values(dev_priv, wm);
+
+	wm->cxsr = I915_READ(FW_BLC_SELF) & FW_BLC_SELF_EN;
+
+	for_each_intel_crtc(dev, crtc) {
+		struct intel_crtc_state *crtc_state =
+			to_intel_crtc_state(crtc->base.state);
+		struct g4x_wm_state *active = &crtc->wm.active.g4x;
+		struct g4x_pipe_wm *raw;
+		enum pipe pipe = crtc->pipe;
+		enum plane_id plane_id;
+		int level, max_level;
+
+		active->cxsr = wm->cxsr;
+		active->hpll_en = wm->hpll_en;
+		active->fbc_en = wm->fbc_en;
+
+		active->sr = wm->sr;
+		active->hpll = wm->hpll;
+
+		for_each_plane_id_on_crtc(crtc, plane_id) {
+			active->wm.plane[plane_id] =
+				wm->pipe[pipe].plane[plane_id];
+		}
+
+		if (wm->cxsr && wm->hpll_en)
+			max_level = G4X_WM_LEVEL_HPLL;
+		else if (wm->cxsr)
+			max_level = G4X_WM_LEVEL_SR;
+		else
+			max_level = G4X_WM_LEVEL_NORMAL;
+
+		level = G4X_WM_LEVEL_NORMAL;
+		raw = &crtc_state->wm.g4x.raw[level];
+		for_each_plane_id_on_crtc(crtc, plane_id)
+			raw->plane[plane_id] = active->wm.plane[plane_id];
+
+		if (++level > max_level)
+			goto out;
+
+		raw = &crtc_state->wm.g4x.raw[level];
+		raw->plane[PLANE_PRIMARY] = active->sr.plane;
+		raw->plane[PLANE_CURSOR] = active->sr.cursor;
+		raw->plane[PLANE_SPRITE0] = 0;
+		raw->fbc = active->sr.fbc;
+
+		if (++level > max_level)
+			goto out;
+
+		raw = &crtc_state->wm.g4x.raw[level];
+		raw->plane[PLANE_PRIMARY] = active->hpll.plane;
+		raw->plane[PLANE_CURSOR] = active->hpll.cursor;
+		raw->plane[PLANE_SPRITE0] = 0;
+		raw->fbc = active->hpll.fbc;
+
+	out:
+		for_each_plane_id_on_crtc(crtc, plane_id)
+			g4x_raw_plane_wm_set(crtc_state, level,
+					     plane_id, USHRT_MAX);
+		g4x_raw_fbc_wm_set(crtc_state, level, USHRT_MAX);
+
+		crtc_state->wm.g4x.optimal = *active;
+		crtc_state->wm.g4x.intermediate = *active;
+
+		DRM_DEBUG_KMS("Initial watermarks: pipe %c, plane=%d, cursor=%d, sprite=%d\n",
+			      pipe_name(pipe),
+			      wm->pipe[pipe].plane[PLANE_PRIMARY],
+			      wm->pipe[pipe].plane[PLANE_CURSOR],
+			      wm->pipe[pipe].plane[PLANE_SPRITE0]);
+	}
+
+	DRM_DEBUG_KMS("Initial SR watermarks: plane=%d, cursor=%d fbc=%d\n",
+		      wm->sr.plane, wm->sr.cursor, wm->sr.fbc);
+	DRM_DEBUG_KMS("Initial HPLL watermarks: plane=%d, SR cursor=%d fbc=%d\n",
+		      wm->hpll.plane, wm->hpll.cursor, wm->hpll.fbc);
+	DRM_DEBUG_KMS("Initial SR=%s HPLL=%s FBC=%s\n",
+		      yesno(wm->cxsr), yesno(wm->hpll_en), yesno(wm->fbc_en));
+}
+
+void g4x_wm_sanitize(struct drm_i915_private *dev_priv)
+{
+	struct intel_plane *plane;
+	struct intel_crtc *crtc;
+
+	mutex_lock(&dev_priv->wm.wm_mutex);
+
+	for_each_intel_plane(&dev_priv->drm, plane) {
+		struct intel_crtc *crtc =
+			intel_get_crtc_for_pipe(dev_priv, plane->pipe);
+		struct intel_crtc_state *crtc_state =
+			to_intel_crtc_state(crtc->base.state);
+		struct intel_plane_state *plane_state =
+			to_intel_plane_state(plane->base.state);
+		struct g4x_wm_state *wm_state = &crtc_state->wm.g4x.optimal;
+		enum plane_id plane_id = plane->id;
+		int level;
+
+		if (plane_state->base.visible)
+			continue;
+
+		for (level = 0; level < 3; level++) {
+			struct g4x_pipe_wm *raw =
+				&crtc_state->wm.g4x.raw[level];
+
+			raw->plane[plane_id] = 0;
+			wm_state->wm.plane[plane_id] = 0;
+		}
+
+		if (plane_id == PLANE_PRIMARY) {
+			for (level = 0; level < 3; level++) {
+				struct g4x_pipe_wm *raw =
+					&crtc_state->wm.g4x.raw[level];
+				raw->fbc = 0;
+			}
+
+			wm_state->sr.fbc = 0;
+			wm_state->hpll.fbc = 0;
+			wm_state->fbc_en = false;
+		}
+	}
+
+	for_each_intel_crtc(&dev_priv->drm, crtc) {
+		struct intel_crtc_state *crtc_state =
+			to_intel_crtc_state(crtc->base.state);
+
+		crtc_state->wm.g4x.intermediate =
+			crtc_state->wm.g4x.optimal;
+		crtc->wm.active.g4x = crtc_state->wm.g4x.optimal;
+	}
+
+	g4x_program_watermarks(dev_priv);
+
+	mutex_unlock(&dev_priv->wm.wm_mutex);
+}
+
 void vlv_wm_get_hw_state(struct drm_device *dev)
 {
 	struct drm_i915_private *dev_priv = to_i915(dev);
@@ -8160,6 +8664,12 @@ void intel_init_pm(struct drm_i915_private *dev_priv)
 		dev_priv->display.initial_watermarks = vlv_initial_watermarks;
 		dev_priv->display.optimize_watermarks = vlv_optimize_watermarks;
 		dev_priv->display.atomic_update_watermarks = vlv_atomic_update_fifo;
+	} else if (IS_G4X(dev_priv)) {
+		g4x_setup_wm_latency(dev_priv);
+		dev_priv->display.compute_pipe_wm = g4x_compute_pipe_wm;
+		dev_priv->display.compute_intermediate_wm = g4x_compute_intermediate_wm;
+		dev_priv->display.initial_watermarks = g4x_initial_watermarks;
+		dev_priv->display.optimize_watermarks = g4x_optimize_watermarks;
 	} else if (IS_PINEVIEW(dev_priv)) {
 		if (!intel_get_cxsr_latency(IS_PINEVIEW_G(dev_priv),
 					    dev_priv->is_ddr3,
@@ -8175,8 +8685,6 @@ void intel_init_pm(struct drm_i915_private *dev_priv)
 			dev_priv->display.update_wm = NULL;
 		} else
 			dev_priv->display.update_wm = pineview_update_wm;
-	} else if (IS_G4X(dev_priv)) {
-		dev_priv->display.update_wm = g4x_update_wm;
 	} else if (IS_GEN4(dev_priv)) {
 		dev_priv->display.update_wm = i965_update_wm;
 	} else if (IS_GEN3(dev_priv)) {
-- 
2.10.2

_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH 13/15] drm/i915: Enable HPLL watermarks on g4x
  2017-04-21 18:14 [PATCH 00/15] drm/i915: Two stage watermarks for g4x ville.syrjala
                   ` (11 preceding siblings ...)
  2017-04-21 18:14 ` [PATCH 12/15] drm/i915: Two stage watermarks for g4x ville.syrjala
@ 2017-04-21 18:14 ` ville.syrjala
  2017-04-21 18:14 ` [PATCH 14/15] drm/i915: Add g4x watermark tracepoint ville.syrjala
                   ` (3 subsequent siblings)
  16 siblings, 0 replies; 21+ messages in thread
From: ville.syrjala @ 2017-04-21 18:14 UTC (permalink / raw)
  To: intel-gfx

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

I don't see why we couldn't use the HPLL watermarks on g4x. So let's
enable them. Let's assume a 35 usec memory latency for the HPLL mode.
That's roughly what PNV uses.

Based on the behaviour of the ELK box I have 35 usec is probably
overkill. Actually all the current latency values used seem overkill as
I can reduce them pretty drastically before I start to see underruns.
But let's play things a bit safe for now.

Signed-off-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
---
 drivers/gpu/drm/i915/intel_pm.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c
index 9b0a6a4572ce..957ef10b1569 100644
--- a/drivers/gpu/drm/i915/intel_pm.c
+++ b/drivers/gpu/drm/i915/intel_pm.c
@@ -1020,8 +1020,9 @@ static void g4x_setup_wm_latency(struct drm_i915_private *dev_priv)
 	/* all latencies in usec */
 	dev_priv->wm.pri_latency[G4X_WM_LEVEL_NORMAL] = 5;
 	dev_priv->wm.pri_latency[G4X_WM_LEVEL_SR] = 12;
+	dev_priv->wm.pri_latency[G4X_WM_LEVEL_HPLL] = 35;
 
-	dev_priv->wm.max_level = G4X_WM_LEVEL_SR;
+	dev_priv->wm.max_level = G4X_WM_LEVEL_HPLL;
 }
 
 static int g4x_plane_fifo_size(enum plane_id plane_id, int level)
-- 
2.10.2

_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH 14/15] drm/i915: Add g4x watermark tracepoint
  2017-04-21 18:14 [PATCH 00/15] drm/i915: Two stage watermarks for g4x ville.syrjala
                   ` (12 preceding siblings ...)
  2017-04-21 18:14 ` [PATCH 13/15] drm/i915: Enable HPLL watermarks on g4x ville.syrjala
@ 2017-04-21 18:14 ` ville.syrjala
  2017-04-21 18:14 ` [PATCH 15/15] drm/i915: Add support for sprites on g4x ville.syrjala
                   ` (2 subsequent siblings)
  16 siblings, 0 replies; 21+ messages in thread
From: ville.syrjala @ 2017-04-21 18:14 UTC (permalink / raw)
  To: intel-gfx

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

Add a tracepoint for watermark programming on g4x, similar to what we
have on vlv/chv. Should help in debugging watermark programming sequence
issues.

Signed-off-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
---
 drivers/gpu/drm/i915/i915_trace.h | 49 +++++++++++++++++++++++++++++++++++++++
 drivers/gpu/drm/i915/intel_pm.c   |  5 ++++
 2 files changed, 54 insertions(+)

diff --git a/drivers/gpu/drm/i915/i915_trace.h b/drivers/gpu/drm/i915/i915_trace.h
index 66404c5aee82..b24a83d43559 100644
--- a/drivers/gpu/drm/i915/i915_trace.h
+++ b/drivers/gpu/drm/i915/i915_trace.h
@@ -89,6 +89,55 @@ TRACE_EVENT(intel_memory_cxsr,
 		      __entry->frame[PIPE_C], __entry->scanline[PIPE_C])
 );
 
+TRACE_EVENT(g4x_wm,
+	    TP_PROTO(struct intel_crtc *crtc, const struct g4x_wm_values *wm),
+	    TP_ARGS(crtc, wm),
+
+	    TP_STRUCT__entry(
+			     __field(enum pipe, pipe)
+			     __field(u32, frame)
+			     __field(u32, scanline)
+			     __field(u16, primary)
+			     __field(u16, sprite)
+			     __field(u16, cursor)
+			     __field(u16, sr_plane)
+			     __field(u16, sr_cursor)
+			     __field(u16, sr_fbc)
+			     __field(u16, hpll_plane)
+			     __field(u16, hpll_cursor)
+			     __field(u16, hpll_fbc)
+			     __field(bool, cxsr)
+			     __field(bool, hpll)
+			     __field(bool, fbc)
+			     ),
+
+	    TP_fast_assign(
+			   __entry->pipe = crtc->pipe;
+			   __entry->frame = crtc->base.dev->driver->get_vblank_counter(crtc->base.dev,
+										       crtc->pipe);
+			   __entry->scanline = intel_get_crtc_scanline(crtc);
+			   __entry->primary = wm->pipe[crtc->pipe].plane[PLANE_PRIMARY];
+			   __entry->sprite = wm->pipe[crtc->pipe].plane[PLANE_SPRITE0];
+			   __entry->cursor = wm->pipe[crtc->pipe].plane[PLANE_CURSOR];
+			   __entry->sr_plane = wm->sr.plane;
+			   __entry->sr_cursor = wm->sr.cursor;
+			   __entry->sr_fbc = wm->sr.fbc;
+			   __entry->hpll_plane = wm->hpll.plane;
+			   __entry->hpll_cursor = wm->hpll.cursor;
+			   __entry->hpll_fbc = wm->hpll.fbc;
+			   __entry->cxsr = wm->cxsr;
+			   __entry->hpll = wm->hpll_en;
+			   __entry->fbc = wm->fbc_en;
+			   ),
+
+	    TP_printk("pipe %c, frame=%u, scanline=%u, wm %d/%d/%d, sr %s/%d/%d/%d, hpll %s/%d/%d/%d, fbc %s",
+		      pipe_name(__entry->pipe), __entry->frame, __entry->scanline,
+		      __entry->primary, __entry->sprite, __entry->cursor,
+		      yesno(__entry->cxsr), __entry->sr_plane, __entry->sr_cursor, __entry->sr_fbc,
+		      yesno(__entry->hpll), __entry->hpll_plane, __entry->hpll_cursor, __entry->hpll_fbc,
+		      yesno(__entry->fbc))
+);
+
 TRACE_EVENT(vlv_wm,
 	    TP_PROTO(struct intel_crtc *crtc, const struct vlv_wm_values *wm),
 	    TP_ARGS(crtc, wm),
diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c
index 957ef10b1569..ef0e9f8d4dbd 100644
--- a/drivers/gpu/drm/i915/intel_pm.c
+++ b/drivers/gpu/drm/i915/intel_pm.c
@@ -913,6 +913,11 @@ static int g4x_tlb_miss_wa(int fifo_size, int width, int cpp)
 static void g4x_write_wm_values(struct drm_i915_private *dev_priv,
 				const struct g4x_wm_values *wm)
 {
+	enum pipe pipe;
+
+	for_each_pipe(dev_priv, pipe)
+		trace_g4x_wm(intel_get_crtc_for_pipe(dev_priv, pipe), wm);
+
 	I915_WRITE(DSPFW1,
 		   FW_WM(wm->sr.plane, SR) |
 		   FW_WM(wm->pipe[PIPE_B].plane[PLANE_CURSOR], CURSORB) |
-- 
2.10.2

_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH 15/15] drm/i915: Add support for sprites on g4x
  2017-04-21 18:14 [PATCH 00/15] drm/i915: Two stage watermarks for g4x ville.syrjala
                   ` (13 preceding siblings ...)
  2017-04-21 18:14 ` [PATCH 14/15] drm/i915: Add g4x watermark tracepoint ville.syrjala
@ 2017-04-21 18:14 ` ville.syrjala
  2017-04-21 18:36 ` ✓ Fi.CI.BAT: success for drm/i915: Two stage watermarks for g4x Patchwork
  2017-04-24 14:20 ` [PATCH 00/15] " Lofstedt, Marta
  16 siblings, 0 replies; 21+ messages in thread
From: ville.syrjala @ 2017-04-21 18:14 UTC (permalink / raw)
  To: intel-gfx

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

Now that the watermarks are in order, it should be safe to enable sprite
planes on g4x. We alreday have the code in fact, we just call it ilk_.
Let's rename to g4x_ and let it loose.

Signed-off-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
---
 drivers/gpu/drm/i915/intel_device_info.c |  2 +-
 drivers/gpu/drm/i915/intel_display.c     |  4 ++--
 drivers/gpu/drm/i915/intel_sprite.c      | 18 +++++++++---------
 3 files changed, 12 insertions(+), 12 deletions(-)

diff --git a/drivers/gpu/drm/i915/intel_device_info.c b/drivers/gpu/drm/i915/intel_device_info.c
index 7d01dfe7faac..3718341662c2 100644
--- a/drivers/gpu/drm/i915/intel_device_info.c
+++ b/drivers/gpu/drm/i915/intel_device_info.c
@@ -337,7 +337,7 @@ void intel_device_info_runtime_init(struct drm_i915_private *dev_priv)
 	} else if (IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv)) {
 		for_each_pipe(dev_priv, pipe)
 			info->num_sprites[pipe] = 2;
-	} else if (INTEL_GEN(dev_priv) >= 5) {
+	} else if (INTEL_GEN(dev_priv) >= 5 || IS_G4X(dev_priv)) {
 		for_each_pipe(dev_priv, pipe)
 			info->num_sprites[pipe] = 1;
 	}
diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c
index 0f42263c3f76..3ccc39e46f8f 100644
--- a/drivers/gpu/drm/i915/intel_display.c
+++ b/drivers/gpu/drm/i915/intel_display.c
@@ -1277,7 +1277,7 @@ static void assert_sprites_disabled(struct drm_i915_private *dev_priv,
 		I915_STATE_WARN(val & SPRITE_ENABLE,
 		     "sprite %c assertion failure, should be off on pipe %c but is still active\n",
 		     plane_name(pipe), pipe_name(pipe));
-	} else if (INTEL_GEN(dev_priv) >= 5) {
+	} else if (INTEL_GEN(dev_priv) >= 5 || IS_G4X(dev_priv)) {
 		u32 val = I915_READ(DVSCNTR(pipe));
 		I915_STATE_WARN(val & DVS_ENABLE,
 		     "sprite %c assertion failure, should be off on pipe %c but is still active\n",
@@ -14429,7 +14429,7 @@ static int intel_framebuffer_init(struct intel_framebuffer *intel_fb,
 	case DRM_FORMAT_UYVY:
 	case DRM_FORMAT_YVYU:
 	case DRM_FORMAT_VYUY:
-		if (INTEL_GEN(dev_priv) < 5) {
+		if (INTEL_GEN(dev_priv) < 5 && !IS_G4X(dev_priv)) {
 			DRM_DEBUG_KMS("unsupported pixel format: %s\n",
 				      drm_get_format_name(mode_cmd->pixel_format, &format_name));
 			goto err;
diff --git a/drivers/gpu/drm/i915/intel_sprite.c b/drivers/gpu/drm/i915/intel_sprite.c
index f7d431427115..511f0e969f7a 100644
--- a/drivers/gpu/drm/i915/intel_sprite.c
+++ b/drivers/gpu/drm/i915/intel_sprite.c
@@ -629,7 +629,7 @@ ivb_disable_plane(struct drm_plane *plane, struct drm_crtc *crtc)
 	spin_unlock_irqrestore(&dev_priv->uncore.lock, irqflags);
 }
 
-static u32 ilk_sprite_ctl(const struct intel_crtc_state *crtc_state,
+static u32 g4x_sprite_ctl(const struct intel_crtc_state *crtc_state,
 			  const struct intel_plane_state *plane_state)
 {
 	struct drm_i915_private *dev_priv =
@@ -683,7 +683,7 @@ static u32 ilk_sprite_ctl(const struct intel_crtc_state *crtc_state,
 }
 
 static void
-ilk_update_plane(struct drm_plane *plane,
+g4x_update_plane(struct drm_plane *plane,
 		 const struct intel_crtc_state *crtc_state,
 		 const struct intel_plane_state *plane_state)
 {
@@ -744,7 +744,7 @@ ilk_update_plane(struct drm_plane *plane,
 }
 
 static void
-ilk_disable_plane(struct drm_plane *plane, struct drm_crtc *crtc)
+g4x_disable_plane(struct drm_plane *plane, struct drm_crtc *crtc)
 {
 	struct drm_device *dev = plane->dev;
 	struct drm_i915_private *dev_priv = to_i915(dev);
@@ -964,7 +964,7 @@ intel_check_sprite_plane(struct drm_plane *plane,
 		if (ret)
 			return ret;
 
-		state->ctl = ilk_sprite_ctl(crtc_state, state);
+		state->ctl = g4x_sprite_ctl(crtc_state, state);
 	}
 
 	return 0;
@@ -1024,7 +1024,7 @@ int intel_sprite_set_colorkey(struct drm_device *dev, void *data,
 	return ret;
 }
 
-static const uint32_t ilk_plane_formats[] = {
+static const uint32_t g4x_plane_formats[] = {
 	DRM_FORMAT_XRGB8888,
 	DRM_FORMAT_YUYV,
 	DRM_FORMAT_YVYU,
@@ -1128,15 +1128,15 @@ intel_sprite_plane_create(struct drm_i915_private *dev_priv,
 		intel_plane->can_scale = true;
 		intel_plane->max_downscale = 16;
 
-		intel_plane->update_plane = ilk_update_plane;
-		intel_plane->disable_plane = ilk_disable_plane;
+		intel_plane->update_plane = g4x_update_plane;
+		intel_plane->disable_plane = g4x_disable_plane;
 
 		if (IS_GEN6(dev_priv)) {
 			plane_formats = snb_plane_formats;
 			num_plane_formats = ARRAY_SIZE(snb_plane_formats);
 		} else {
-			plane_formats = ilk_plane_formats;
-			num_plane_formats = ARRAY_SIZE(ilk_plane_formats);
+			plane_formats = g4x_plane_formats;
+			num_plane_formats = ARRAY_SIZE(g4x_plane_formats);
 		}
 	}
 
-- 
2.10.2

_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply related	[flat|nested] 21+ messages in thread

* ✓ Fi.CI.BAT: success for drm/i915: Two stage watermarks for g4x
  2017-04-21 18:14 [PATCH 00/15] drm/i915: Two stage watermarks for g4x ville.syrjala
                   ` (14 preceding siblings ...)
  2017-04-21 18:14 ` [PATCH 15/15] drm/i915: Add support for sprites on g4x ville.syrjala
@ 2017-04-21 18:36 ` Patchwork
  2017-04-24 14:20 ` [PATCH 00/15] " Lofstedt, Marta
  16 siblings, 0 replies; 21+ messages in thread
From: Patchwork @ 2017-04-21 18:36 UTC (permalink / raw)
  To: ville.syrjala; +Cc: intel-gfx

== Series Details ==

Series: drm/i915: Two stage watermarks for g4x
URL   : https://patchwork.freedesktop.org/series/23358/
State : success

== Summary ==

Series 23358v1 drm/i915: Two stage watermarks for g4x
https://patchwork.freedesktop.org/api/1.0/series/23358/revisions/1/mbox/

fi-bdw-5557u     total:278  pass:267  dwarn:0   dfail:0   fail:0   skip:11  time:433s
fi-bdw-gvtdvm    total:278  pass:256  dwarn:8   dfail:0   fail:0   skip:14  time:429s
fi-bsw-n3050     total:278  pass:242  dwarn:0   dfail:0   fail:0   skip:36  time:579s
fi-bxt-j4205     total:278  pass:259  dwarn:0   dfail:0   fail:0   skip:19  time:515s
fi-bxt-t5700     total:278  pass:258  dwarn:0   dfail:0   fail:0   skip:20  time:554s
fi-byt-j1900     total:278  pass:254  dwarn:0   dfail:0   fail:0   skip:24  time:486s
fi-byt-n2820     total:278  pass:250  dwarn:0   dfail:0   fail:0   skip:28  time:479s
fi-hsw-4770      total:278  pass:262  dwarn:0   dfail:0   fail:0   skip:16  time:408s
fi-hsw-4770r     total:278  pass:262  dwarn:0   dfail:0   fail:0   skip:16  time:405s
fi-ilk-650       total:278  pass:228  dwarn:0   dfail:0   fail:0   skip:50  time:423s
fi-ivb-3520m     total:278  pass:260  dwarn:0   dfail:0   fail:0   skip:18  time:492s
fi-ivb-3770      total:278  pass:260  dwarn:0   dfail:0   fail:0   skip:18  time:471s
fi-kbl-7500u     total:278  pass:260  dwarn:0   dfail:0   fail:0   skip:18  time:459s
fi-kbl-7560u     total:278  pass:267  dwarn:1   dfail:0   fail:0   skip:10  time:567s
fi-skl-6260u     total:278  pass:268  dwarn:0   dfail:0   fail:0   skip:10  time:450s
fi-skl-6700hq    total:278  pass:261  dwarn:0   dfail:0   fail:0   skip:17  time:573s
fi-skl-6700k     total:278  pass:256  dwarn:4   dfail:0   fail:0   skip:18  time:457s
fi-skl-6770hq    total:278  pass:268  dwarn:0   dfail:0   fail:0   skip:10  time:498s
fi-skl-gvtdvm    total:278  pass:265  dwarn:0   dfail:0   fail:0   skip:13  time:430s
fi-snb-2520m     total:278  pass:250  dwarn:0   dfail:0   fail:0   skip:28  time:535s
fi-snb-2600      total:278  pass:249  dwarn:0   dfail:0   fail:0   skip:29  time:411s

d473a03c132969f33a6f95456a4dbbfb36b99373 drm-tip: 2017y-04m-21d-15h-42m-02s UTC integration manifest
ec327fa drm/i915: Add support for sprites on g4x
6fdd8f0 drm/i915: Add g4x watermark tracepoint
dddf36a drm/i915: Enable HPLL watermarks on g4x
9a73d73 drm/i915: Two stage watermarks for g4x
fe292bd drm/i915: Apply the g4x TLB miss w/a to SR watermarks as well
8adede1 drm/i915: Refactor wm calculations
79a7571 drm/i915: Refactor the g4x TLB miss w/a to a helper
e680001 drm/i915: Fix the g4x watermark TLB miss workaround
76a8ff6 drm/i915: Fix cursor 'cpp' in watermark calculatins for old platforms
9df15fa drm/i915: Document CxSR
29a6f48 drm/i915: Make vlv/chv watermark debug print less cryptic
abd09b7 drm/i915: Rename bunch of vlv_ watermark structures to g4x_
d05cc7f drm/i915: s/vlv_num_wm_levels/intel_wm_num_levels/
73841ae drm/i915: Drop the debug message from vlv_get_fifo_size()
d236600 drm/i915: s/vlv_plane_wm_compute/vlv_raw_plane_wm_compute/ etc.

== Logs ==

For more details see: https://intel-gfx-ci.01.org/CI/Patchwork_4531/
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH 12/15] drm/i915: Two stage watermarks for g4x
  2017-04-21 18:14 ` [PATCH 12/15] drm/i915: Two stage watermarks for g4x ville.syrjala
@ 2017-04-24  7:34   ` Maarten Lankhorst
  2017-04-24 13:16     ` Ville Syrjälä
  2017-05-10 16:40     ` Ville Syrjälä
  0 siblings, 2 replies; 21+ messages in thread
From: Maarten Lankhorst @ 2017-04-24  7:34 UTC (permalink / raw)
  To: ville.syrjala, intel-gfx

On 21-04-17 20:14, ville.syrjala@linux.intel.com wrote:
> From: Ville Syrjälä <ville.syrjala@linux.intel.com>
>
> Implement proper two stage watermark programming for g4x. As with
> other pre-SKL platforms, the watermark registers aren't double
> buffered on g4x. Hence we must sequence the watermark update
> carefully around plane updates.
>
> The code is quite heavily modelled on the VLV/CHV code, with some
> fairly significant differences due to the different hardware
> architecture:
> * g4x doesn't use inverted watermark values
> * CxSR actually affects the watermarks since it controls memory self
>   refresh in addition to the max FIFO mode
> * A further HPLL SR mode is possible with higher memory wakeup
>   latency
> * g4x has FBC2 and so it also has FBC watermarks
> * max FIFO mode for primary plane only (cursor is allowed, sprite is not)
> * g4x has no manual FIFO repartitioning
> * some TLB miss related workarounds are needed for the watermarks
>
> Actually the hardware is quite similar to ILK+ in many ways. The
> most visible differences are in the actual watermakr register
> layout. ILK revamped that part quite heavily whereas g4x is still
> using the layout inherited from earlier platforms.
>
> Note that we didn't previously enable the HPLL SR on g4x. So in order
> to not introduce too many functional changes in this patch I've not
> actually enabled it here either, even though the code is now fully
> ready for it. We'll enable it separately later on.
>
> Signed-off-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
> ---
>  drivers/gpu/drm/i915/i915_debugfs.c  |  12 +-
>  drivers/gpu/drm/i915/i915_drv.h      |  12 +
>  drivers/gpu/drm/i915/intel_display.c |  25 +-
>  drivers/gpu/drm/i915/intel_drv.h     |  28 ++
>  drivers/gpu/drm/i915/intel_pm.c      | 942 +++++++++++++++++++++++++++--------
>  5 files changed, 792 insertions(+), 227 deletions(-)
>
> diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c
> index 870c470177b5..69550d31099e 100644
> --- a/drivers/gpu/drm/i915/i915_debugfs.c
> +++ b/drivers/gpu/drm/i915/i915_debugfs.c
> @@ -3898,6 +3898,8 @@ static void wm_latency_show(struct seq_file *m, const uint16_t wm[8])
>  		num_levels = 3;
>  	else if (IS_VALLEYVIEW(dev_priv))
>  		num_levels = 1;
> +	else if (IS_G4X(dev_priv))
> +		num_levels = 3;
>  	else
>  		num_levels = ilk_wm_max_level(dev_priv) + 1;
>  
> @@ -3910,8 +3912,10 @@ static void wm_latency_show(struct seq_file *m, const uint16_t wm[8])
>  		 * - WM1+ latency values in 0.5us units
>  		 * - latencies are in us on gen9/vlv/chv
>  		 */
> -		if (INTEL_GEN(dev_priv) >= 9 || IS_VALLEYVIEW(dev_priv) ||
> -		    IS_CHERRYVIEW(dev_priv))
> +		if (INTEL_GEN(dev_priv) >= 9 ||
> +		    IS_VALLEYVIEW(dev_priv) ||
> +		    IS_CHERRYVIEW(dev_priv) ||
> +		    IS_G4X(dev_priv))
>  			latency *= 10;
>  		else if (level > 0)
>  			latency *= 5;
> @@ -3972,7 +3976,7 @@ static int pri_wm_latency_open(struct inode *inode, struct file *file)
>  {
>  	struct drm_i915_private *dev_priv = inode->i_private;
>  
> -	if (INTEL_GEN(dev_priv) < 5)
> +	if (INTEL_GEN(dev_priv) < 5 && !IS_G4X(dev_priv))
>  		return -ENODEV;
>  
>  	return single_open(file, pri_wm_latency_show, dev_priv);
> @@ -4014,6 +4018,8 @@ static ssize_t wm_latency_write(struct file *file, const char __user *ubuf,
>  		num_levels = 3;
>  	else if (IS_VALLEYVIEW(dev_priv))
>  		num_levels = 1;
> +	else if (IS_G4X(dev_priv))
> +		num_levels = 3;
>  	else
>  		num_levels = ilk_wm_max_level(dev_priv) + 1;
>  
> diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
> index 0a393fdc53d1..6df8bff7f5a7 100644
> --- a/drivers/gpu/drm/i915/i915_drv.h
> +++ b/drivers/gpu/drm/i915/i915_drv.h
> @@ -1762,11 +1762,13 @@ struct ilk_wm_values {
>  
>  struct g4x_pipe_wm {
>  	uint16_t plane[I915_MAX_PLANES];
> +	uint16_t fbc;
>  };
>  
>  struct g4x_sr_wm {
>  	uint16_t plane;
>  	uint16_t cursor;
> +	uint16_t fbc;
>  };
>  
>  struct vlv_wm_ddl_values {
> @@ -1781,6 +1783,15 @@ struct vlv_wm_values {
>  	bool cxsr;
>  };
>  
> +struct g4x_wm_values {
> +	struct g4x_pipe_wm pipe[2];
> +	struct g4x_sr_wm sr;
> +	struct g4x_sr_wm hpll;
> +	bool cxsr;
> +	bool hpll_en;
> +	bool fbc_en;
> +};
> +
>  struct skl_ddb_entry {
>  	uint16_t start, end;	/* in number of blocks, 'end' is exclusive */
>  };
> @@ -2410,6 +2421,7 @@ struct drm_i915_private {
>  			struct ilk_wm_values hw;
>  			struct skl_wm_values skl_hw;
>  			struct vlv_wm_values vlv;
> +			struct g4x_wm_values g4x;
>  		};
>  
>  		uint8_t max_level;
> diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c
> index 85b9e2f521a0..0f42263c3f76 100644
> --- a/drivers/gpu/drm/i915/intel_display.c
> +++ b/drivers/gpu/drm/i915/intel_display.c
> @@ -5719,6 +5719,8 @@ static void i9xx_set_pll_dividers(struct intel_crtc *crtc)
>  static void i9xx_crtc_enable(struct intel_crtc_state *pipe_config,
>  			     struct drm_atomic_state *old_state)
>  {
> +	struct intel_atomic_state *old_intel_state =
> +		to_intel_atomic_state(old_state);
>  	struct drm_crtc *crtc = pipe_config->base.crtc;
>  	struct drm_device *dev = crtc->dev;
>  	struct drm_i915_private *dev_priv = to_i915(dev);
> @@ -5751,7 +5753,11 @@ static void i9xx_crtc_enable(struct intel_crtc_state *pipe_config,
>  
>  	intel_color_load_luts(&pipe_config->base);
>  
> -	intel_update_watermarks(intel_crtc);
> +	if (dev_priv->display.initial_watermarks != NULL)
> +		dev_priv->display.initial_watermarks(old_intel_state,
> +						     intel_crtc->config);
> +	else
> +		intel_update_watermarks(intel_crtc);
>  	intel_enable_pipe(intel_crtc);
>  
>  	assert_vblank_disabled(crtc);
> @@ -10852,21 +10858,21 @@ int intel_plane_atomic_calc_changes(struct drm_crtc_state *crtc_state,
>  			 turn_off, turn_on, mode_changed);
>  
>  	if (turn_on) {
> -		if (INTEL_GEN(dev_priv) < 5)
> +		if (INTEL_GEN(dev_priv) < 5 && !IS_G4X(dev_priv))
>  			pipe_config->update_wm_pre = true;
>  
>  		/* must disable cxsr around plane enable/disable */
>  		if (plane->id != PLANE_CURSOR)
>  			pipe_config->disable_cxsr = true;
>  	} else if (turn_off) {
> -		if (INTEL_GEN(dev_priv) < 5)
> +		if (INTEL_GEN(dev_priv) < 5 && !IS_G4X(dev_priv))
>  			pipe_config->update_wm_post = true;
>  
>  		/* must disable cxsr around plane enable/disable */
>  		if (plane->id != PLANE_CURSOR)
>  			pipe_config->disable_cxsr = true;
>  	} else if (intel_wm_need_update(&plane->base, plane_state)) {
> -		if (INTEL_GEN(dev_priv) < 5) {
> +		if (INTEL_GEN(dev_priv) < 5 && !IS_G4X(dev_priv)) {
>  			/* FIXME bollocks */
>  			pipe_config->update_wm_pre = true;
>  			pipe_config->update_wm_post = true;
> @@ -11290,7 +11296,8 @@ clear_intel_crtc_state(struct intel_crtc_state *crtc_state)
>  	shared_dpll = crtc_state->shared_dpll;
>  	dpll_hw_state = crtc_state->dpll_hw_state;
>  	force_thru = crtc_state->pch_pfit.force_thru;
> -	if (IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv))
> +	if (IS_G4X(dev_priv) ||
> +	    IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv))
>  		wm_state = crtc_state->wm;
>  
>  	/* Keep base drm_crtc_state intact, only clear our extended struct */
> @@ -11302,7 +11309,8 @@ clear_intel_crtc_state(struct intel_crtc_state *crtc_state)
>  	crtc_state->shared_dpll = shared_dpll;
>  	crtc_state->dpll_hw_state = dpll_hw_state;
>  	crtc_state->pch_pfit.force_thru = force_thru;
> -	if (IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv))
> +	if (IS_G4X(dev_priv) ||
> +	    IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv))
>  		crtc_state->wm = wm_state;
>  }
>  
> @@ -15527,7 +15535,10 @@ intel_modeset_setup_hw_state(struct drm_device *dev)
>  		pll->on = false;
>  	}
>  
> -	if (IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv)) {
> +	if (IS_G4X(dev_priv)) {
> +		g4x_wm_get_hw_state(dev);
> +		g4x_wm_sanitize(dev_priv);
> +	} else if (IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv)) {
>  		vlv_wm_get_hw_state(dev);
>  		vlv_wm_sanitize(dev_priv);
>  	} else if (IS_GEN9(dev_priv)) {
> diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
> index d1cdd10998fa..f530df71a480 100644
> --- a/drivers/gpu/drm/i915/intel_drv.h
> +++ b/drivers/gpu/drm/i915/intel_drv.h
> @@ -522,6 +522,22 @@ struct vlv_fifo_state {
>  	u16 plane[I915_MAX_PLANES];
>  };
>  
> +enum g4x_wm_level {
> +	G4X_WM_LEVEL_NORMAL,
> +	G4X_WM_LEVEL_SR,
> +	G4X_WM_LEVEL_HPLL,
> +	NUM_G4X_WM_LEVELS,
> +};
> +
> +struct g4x_wm_state {
> +	struct g4x_pipe_wm wm;
> +	struct g4x_sr_wm sr;
> +	struct g4x_sr_wm hpll;
> +	bool cxsr;
> +	bool hpll_en;
> +	bool fbc_en;
> +};
> +
>  struct intel_crtc_wm_state {
>  	union {
>  		struct {
> @@ -557,6 +573,15 @@ struct intel_crtc_wm_state {
>  			/* display FIFO split */
>  			struct vlv_fifo_state fifo_state;
>  		} vlv;
> +
> +		struct {
> +			/* "raw" watermarks */
> +			struct g4x_pipe_wm raw[NUM_G4X_WM_LEVELS];
> +			/* intermediate watermarks */
> +			struct g4x_wm_state intermediate;
> +			/* optimal watermarks */
> +			struct g4x_wm_state optimal;
> +		} g4x;
>  	};
>  
>  	/*
> @@ -794,6 +819,7 @@ struct intel_crtc {
>  		union {
>  			struct intel_pipe_wm ilk;
>  			struct vlv_wm_state vlv;
> +			struct g4x_wm_state g4x;
>  		} active;
>  	} wm;
>  
> @@ -1841,6 +1867,7 @@ void gen6_rps_boost(struct drm_i915_private *dev_priv,
>  		    struct intel_rps_client *rps,
>  		    unsigned long submitted);
>  void intel_queue_rps_boost_for_request(struct drm_i915_gem_request *req);
> +void g4x_wm_get_hw_state(struct drm_device *dev);
>  void vlv_wm_get_hw_state(struct drm_device *dev);
>  void ilk_wm_get_hw_state(struct drm_device *dev);
>  void skl_wm_get_hw_state(struct drm_device *dev);
> @@ -1848,6 +1875,7 @@ void skl_ddb_get_hw_state(struct drm_i915_private *dev_priv,
>  			  struct skl_ddb_allocation *ddb /* out */);
>  void skl_pipe_wm_get_hw_state(struct drm_crtc *crtc,
>  			      struct skl_pipe_wm *out);
> +void g4x_wm_sanitize(struct drm_i915_private *dev_priv);
>  void vlv_wm_sanitize(struct drm_i915_private *dev_priv);
>  bool intel_can_enable_sagv(struct drm_atomic_state *state);
>  int intel_enable_sagv(struct drm_i915_private *dev_priv);
> diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c
> index 61b67994c4a8..9b0a6a4572ce 100644
> --- a/drivers/gpu/drm/i915/intel_pm.c
> +++ b/drivers/gpu/drm/i915/intel_pm.c
> @@ -429,7 +429,10 @@ bool intel_set_memory_cxsr(struct drm_i915_private *dev_priv, bool enable)
>  
>  	mutex_lock(&dev_priv->wm.wm_mutex);
>  	ret = _intel_set_memory_cxsr(dev_priv, enable);
> -	dev_priv->wm.vlv.cxsr = enable;
> +	if (IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv))
> +		dev_priv->wm.vlv.cxsr = enable;
> +	else if (IS_G4X(dev_priv))
> +		dev_priv->wm.g4x.cxsr = enable;
>  	mutex_unlock(&dev_priv->wm.wm_mutex);
>  
>  	return ret;
> @@ -568,20 +571,6 @@ static const struct intel_watermark_params pineview_cursor_hplloff_wm = {
>  	.guard_size = PINEVIEW_CURSOR_GUARD_WM,
>  	.cacheline_size = PINEVIEW_FIFO_LINE_SIZE,
>  };
> -static const struct intel_watermark_params g4x_wm_info = {
> -	.fifo_size = G4X_FIFO_SIZE,
> -	.max_wm = G4X_MAX_WM,
> -	.default_wm = G4X_MAX_WM,
> -	.guard_size = 2,
> -	.cacheline_size = G4X_FIFO_LINE_SIZE,
> -};
> -static const struct intel_watermark_params g4x_cursor_wm_info = {
> -	.fifo_size = I965_CURSOR_FIFO,
> -	.max_wm = I965_CURSOR_MAX_WM,
> -	.default_wm = I965_CURSOR_DFT_WM,
> -	.guard_size = 2,
> -	.cacheline_size = G4X_FIFO_LINE_SIZE,
> -};
>  static const struct intel_watermark_params i965_cursor_wm_info = {
>  	.fifo_size = I965_CURSOR_FIFO,
>  	.max_wm = I965_CURSOR_MAX_WM,
> @@ -780,6 +769,16 @@ static unsigned int intel_calculate_wm(int pixel_rate,
>  	return wm_size;
>  }
>  
> +static bool is_disabling(int old, int new, int threshold)
> +{
> +	return old >= threshold && new < threshold;
> +}
> +
> +static bool is_enabling(int old, int new, int threshold)
> +{
> +	return old < threshold && new >= threshold;
> +}
> +
>  static int intel_wm_num_levels(struct drm_i915_private *dev_priv)
>  {
>  	return dev_priv->wm.max_level + 1;
> @@ -911,138 +910,28 @@ static int g4x_tlb_miss_wa(int fifo_size, int width, int cpp)
>  	return max(0, tlb_miss);
>  }
>  
> -static bool g4x_compute_wm0(struct drm_i915_private *dev_priv,
> -			    int plane,
> -			    const struct intel_watermark_params *display,
> -			    int display_latency_ns,
> -			    const struct intel_watermark_params *cursor,
> -			    int cursor_latency_ns,
> -			    int *plane_wm,
> -			    int *cursor_wm)
> +static void g4x_write_wm_values(struct drm_i915_private *dev_priv,
> +				const struct g4x_wm_values *wm)
>  {
> -	struct intel_crtc *crtc;
> -	const struct drm_display_mode *adjusted_mode;
> -	const struct drm_framebuffer *fb;
> -	int htotal, plane_width, cursor_width, clock, cpp;
> -	int entries;
> -
> -	crtc = intel_get_crtc_for_plane(dev_priv, plane);
> -	if (!intel_crtc_active(crtc)) {
> -		*cursor_wm = cursor->guard_size;
> -		*plane_wm = display->guard_size;
> -		return false;
> -	}
> -
> -	adjusted_mode = &crtc->config->base.adjusted_mode;
> -	fb = crtc->base.primary->state->fb;
> -	clock = adjusted_mode->crtc_clock;
> -	htotal = adjusted_mode->crtc_htotal;
> -	plane_width = crtc->config->pipe_src_w;
> -	cursor_width = crtc->base.cursor->state->crtc_w;
> -	cpp = fb->format->cpp[0];
> -
> -	/* Use the small buffer method to calculate plane watermark */
> -	entries = intel_wm_method1(clock, cpp, display_latency_ns / 100);
> -	entries += g4x_tlb_miss_wa(display->fifo_size, plane_width, cpp);
> -	entries = DIV_ROUND_UP(entries, display->cacheline_size);
> -	*plane_wm = entries + display->guard_size;
> -	if (*plane_wm > (int)display->max_wm)
> -		*plane_wm = display->max_wm;
> -
> -	/* Use the large buffer method to calculate cursor watermark */
> -	entries = intel_wm_method2(clock, htotal, cursor_width, 4,
> -				   cursor_latency_ns / 100);
> -	entries += g4x_tlb_miss_wa(cursor->fifo_size, cursor_width, 4);
> -	entries = DIV_ROUND_UP(entries, cursor->cacheline_size);
> -	*cursor_wm = entries + cursor->guard_size;
> -	if (*cursor_wm > (int)cursor->max_wm)
> -		*cursor_wm = (int)cursor->max_wm;
> -
> -	return true;
> -}
> -
> -/*
> - * Check the wm result.
> - *
> - * If any calculated watermark values is larger than the maximum value that
> - * can be programmed into the associated watermark register, that watermark
> - * must be disabled.
> - */
> -static bool g4x_check_srwm(struct drm_i915_private *dev_priv,
> -			   int display_wm, int cursor_wm,
> -			   const struct intel_watermark_params *display,
> -			   const struct intel_watermark_params *cursor)
> -{
> -	DRM_DEBUG_KMS("SR watermark: display plane %d, cursor %d\n",
> -		      display_wm, cursor_wm);
> -
> -	if (display_wm > display->max_wm) {
> -		DRM_DEBUG_KMS("display watermark is too large(%d/%u), disabling\n",
> -			      display_wm, display->max_wm);
> -		return false;
> -	}
> -
> -	if (cursor_wm > cursor->max_wm) {
> -		DRM_DEBUG_KMS("cursor watermark is too large(%d/%u), disabling\n",
> -			      cursor_wm, cursor->max_wm);
> -		return false;
> -	}
> -
> -	if (!(display_wm || cursor_wm)) {
> -		DRM_DEBUG_KMS("SR latency is 0, disabling\n");
> -		return false;
> -	}
> -
> -	return true;
> -}
> -
> -static bool g4x_compute_srwm(struct drm_i915_private *dev_priv,
> -			     int plane,
> -			     int latency_ns,
> -			     const struct intel_watermark_params *display,
> -			     const struct intel_watermark_params *cursor,
> -			     int *display_wm, int *cursor_wm)
> -{
> -	struct intel_crtc *crtc;
> -	const struct drm_display_mode *adjusted_mode;
> -	const struct drm_framebuffer *fb;
> -	int plane_width, cursor_width, htotal, cpp, clock;
> -	int small, large;
> -	int entries;
> -
> -	if (!latency_ns) {
> -		*display_wm = *cursor_wm = 0;
> -		return false;
> -	}
> -
> -	crtc = intel_get_crtc_for_plane(dev_priv, plane);
> -	adjusted_mode = &crtc->config->base.adjusted_mode;
> -	fb = crtc->base.primary->state->fb;
> -	clock = adjusted_mode->crtc_clock;
> -	htotal = adjusted_mode->crtc_htotal;
> -	plane_width = crtc->config->pipe_src_w;
> -	cursor_width = crtc->base.cursor->state->crtc_w;
> -	cpp = fb->format->cpp[0];
> -
> -	/* Use the minimum of the small and large buffer method for primary */
> -	small = intel_wm_method1(clock, cpp, latency_ns / 100);
> -	large = intel_wm_method2(clock, htotal, plane_width, cpp,
> -				 latency_ns / 100);
> -	entries = min(small, large);
> -	entries += g4x_tlb_miss_wa(display->fifo_size, plane_width, cpp);
> -	entries = DIV_ROUND_UP(entries, display->cacheline_size);
> -	*display_wm = entries + display->guard_size;
> -
> -	/* calculate the self-refresh watermark for display cursor */
> -	entries = intel_wm_method2(clock, htotal, cursor_width, 4,
> -				   latency_ns / 100);
> -	entries += g4x_tlb_miss_wa(cursor->fifo_size, cursor_width, 4);
> -	entries = DIV_ROUND_UP(entries, cursor->cacheline_size);
> -	*cursor_wm = entries + cursor->guard_size;
> -
> -	return g4x_check_srwm(dev_priv,
> -			      *display_wm, *cursor_wm,
> -			      display, cursor);
> +	I915_WRITE(DSPFW1,
> +		   FW_WM(wm->sr.plane, SR) |
> +		   FW_WM(wm->pipe[PIPE_B].plane[PLANE_CURSOR], CURSORB) |
> +		   FW_WM(wm->pipe[PIPE_B].plane[PLANE_PRIMARY], PLANEB) |
> +		   FW_WM(wm->pipe[PIPE_A].plane[PLANE_PRIMARY], PLANEA));
> +	I915_WRITE(DSPFW2,
> +		   (wm->fbc_en ? DSPFW_FBC_SR_EN : 0) |
> +		   FW_WM(wm->sr.fbc, FBC_SR) |
> +		   FW_WM(wm->hpll.fbc, FBC_HPLL_SR) |
> +		   FW_WM(wm->pipe[PIPE_B].plane[PLANE_SPRITE0], SPRITEB) |
> +		   FW_WM(wm->pipe[PIPE_A].plane[PLANE_CURSOR], CURSORA) |
> +		   FW_WM(wm->pipe[PIPE_A].plane[PLANE_SPRITE0], SPRITEA));
> +	I915_WRITE(DSPFW3,
> +		   (wm->hpll_en ? DSPFW_HPLL_SR_EN : 0) |
> +		   FW_WM(wm->sr.cursor, CURSOR_SR) |
> +		   FW_WM(wm->hpll.cursor, HPLL_CURSOR) |
> +		   FW_WM(wm->hpll.plane, HPLL_SR));
> +
> +	POSTING_READ(DSPFW1);
>  }
>  
>  #define FW_WM_VLV(value, plane) \
> @@ -1126,6 +1015,523 @@ static void vlv_write_wm_values(struct drm_i915_private *dev_priv,
>  
>  #undef FW_WM_VLV
>  
> +static void g4x_setup_wm_latency(struct drm_i915_private *dev_priv)
> +{
> +	/* all latencies in usec */
> +	dev_priv->wm.pri_latency[G4X_WM_LEVEL_NORMAL] = 5;
> +	dev_priv->wm.pri_latency[G4X_WM_LEVEL_SR] = 12;
> +
> +	dev_priv->wm.max_level = G4X_WM_LEVEL_SR;
> +}
> +
> +static int g4x_plane_fifo_size(enum plane_id plane_id, int level)
> +{
> +	/*
> +	 * DSPCNTR[13] supposedly controls whether the
> +	 * primary plane can use the FIFO space otherwise
> +	 * reserved for the sprite plane. It's not 100% clear
> +	 * what the actual FIFO size is, but it looks like we
> +	 * can happily set both primary and sprite watermarks
> +	 * up to 127 cachelines. So that would seem to mean
> +	 * that either DSPCNTR[13] doesn't do anything, or that
> +	 * the total FIFO is >= 256 cachelines in size. Either
> +	 * way, we don't seem to have to worry about this
> +	 * repartitioning as the maximum watermark value the
> +	 * register can hold for each plane is lower than the
> +	 * minimum FIFO size.
> +	 */
> +	switch (plane_id) {
> +	case PLANE_CURSOR:
> +		return 63;
> +	case PLANE_PRIMARY:
> +		return level == G4X_WM_LEVEL_NORMAL ? 127 : 511;
> +	case PLANE_SPRITE0:
> +		return level == G4X_WM_LEVEL_NORMAL ? 127 : 0;
> +	default:
> +		MISSING_CASE(plane_id);
> +		return 0;
> +	}
> +}
> +
> +static int g4x_fbc_fifo_size(int level)
> +{
> +	switch (level) {
> +	case G4X_WM_LEVEL_SR:
> +		return 7;
> +	case G4X_WM_LEVEL_HPLL:
> +		return 15;
> +	default:
> +		MISSING_CASE(level);
> +		return 0;
> +	}
> +}
> +
> +static uint16_t g4x_compute_wm(const struct intel_crtc_state *crtc_state,
> +			       const struct intel_plane_state *plane_state,
> +			       int level)
> +{
> +	struct intel_plane *plane = to_intel_plane(plane_state->base.plane);
> +	struct drm_i915_private *dev_priv = to_i915(plane->base.dev);
> +	const struct drm_display_mode *adjusted_mode =
> +		&crtc_state->base.adjusted_mode;
> +	int clock, htotal, cpp, width, wm;
> +	int latency = dev_priv->wm.pri_latency[level] * 10;
> +
> +	if (latency == 0)
> +		return USHRT_MAX;
> +
> +	if (!intel_wm_plane_visible(crtc_state, plane_state))
> +		return 0;
> +
> +	/*
> +	 * Not 100% sure which way ELK should go here as the
> +	 * spec only says CL/CTG should assume 32bpp and BW
> +	 * doesn't need to. But as these things followed the
> +	 * mobile vs. desktop lines on gen3 as well, let's
> +	 * assume ELK doesn't need this.
> +	 *
> +	 * The spec also fails to list such a restriction for
> +	 * the HPLL watermark, which seems a little strange.
> +	 * Let's use 32bpp for the HPLL watermark as well.
> +	 */
> +	if (IS_GM45(dev_priv) && plane->id == PLANE_PRIMARY &&
> +	    level != G4X_WM_LEVEL_NORMAL)
> +		cpp = 4;
> +	else
> +		cpp = plane_state->base.fb->format->cpp[0];
> +
> +	clock = adjusted_mode->crtc_clock;
> +	htotal = adjusted_mode->crtc_htotal;
> +
> +	if (plane->id == PLANE_CURSOR)
> +		width = plane_state->base.crtc_w;
> +	else
> +		width = drm_rect_width(&plane_state->base.dst);
> +
> +	if (plane->id == PLANE_CURSOR) {
> +		wm = intel_wm_method2(clock, htotal, width, cpp, latency);
> +	} else if (plane->id == PLANE_PRIMARY &&
> +		   level == G4X_WM_LEVEL_NORMAL) {
> +		wm = intel_wm_method1(clock, cpp, latency);
> +	} else {
> +		int small, large;
> +
> +		small = intel_wm_method1(clock, cpp, latency);
> +		large = intel_wm_method2(clock, htotal, width, cpp, latency);
> +
> +		wm = min(small, large);
> +	}
> +
> +	wm += g4x_tlb_miss_wa(g4x_plane_fifo_size(plane->id, level),
> +			      width, cpp);
> +
> +	wm = DIV_ROUND_UP(wm, 64) + 2;
> +
> +	return min_t(int, wm, USHRT_MAX);
> +}
> +
> +static bool g4x_raw_plane_wm_set(struct intel_crtc_state *crtc_state,
> +				 int level, enum plane_id plane_id, u16 value)
> +{
> +	struct drm_i915_private *dev_priv = to_i915(crtc_state->base.crtc->dev);
> +	bool dirty = false;
> +
> +	for (; level < intel_wm_num_levels(dev_priv); level++) {
> +		struct g4x_pipe_wm *raw = &crtc_state->wm.g4x.raw[level];
> +
> +		dirty |= raw->plane[plane_id] != value;
> +		raw->plane[plane_id] = value;
> +	}
> +
> +	return dirty;
> +}
> +
> +static bool g4x_raw_fbc_wm_set(struct intel_crtc_state *crtc_state,
> +			       int level, u16 value)
> +{
> +	struct drm_i915_private *dev_priv = to_i915(crtc_state->base.crtc->dev);
> +	bool dirty = false;
> +
> +	/* NORMAL level doesn't have an FBC watermark */
> +	level = max(level, G4X_WM_LEVEL_SR);
> +
> +	for (; level < intel_wm_num_levels(dev_priv); level++) {
> +		struct g4x_pipe_wm *raw = &crtc_state->wm.g4x.raw[level];
> +
> +		dirty |= raw->fbc != value;
> +		raw->fbc = value;
> +	}
> +
> +	return dirty;
> +}
> +
> +static uint32_t ilk_compute_fbc_wm(const struct intel_crtc_state *cstate,
> +				   const struct intel_plane_state *pstate,
> +				   uint32_t pri_val);
> +
> +static bool g4x_raw_plane_wm_compute(struct intel_crtc_state *crtc_state,
> +				     const struct intel_plane_state *plane_state)
> +{
> +	struct intel_plane *plane = to_intel_plane(plane_state->base.plane);
> +	int num_levels = intel_wm_num_levels(to_i915(plane->base.dev));
> +	enum plane_id plane_id = plane->id;
> +	bool dirty = false;
> +	int level;
> +
> +	if (!intel_wm_plane_visible(crtc_state, plane_state)) {
> +		dirty |= g4x_raw_plane_wm_set(crtc_state, 0, plane_id, 0);
> +		if (plane_id == PLANE_PRIMARY)
> +			dirty |= g4x_raw_fbc_wm_set(crtc_state, 0, 0);
> +		goto out;
> +	}
> +
> +	for (level = 0; level < num_levels; level++) {
> +		struct g4x_pipe_wm *raw = &crtc_state->wm.g4x.raw[level];
> +		int wm, max_wm;
> +
> +		wm = g4x_compute_wm(crtc_state, plane_state, level);
> +		max_wm = g4x_plane_fifo_size(plane_id, level);
> +
> +		if (wm > max_wm)
> +			break;
> +
> +		dirty |= raw->plane[plane_id] != wm;
> +		raw->plane[plane_id] = wm;
> +
> +		if (plane_id != PLANE_PRIMARY ||
> +		    level == G4X_WM_LEVEL_NORMAL)
> +			continue;
> +
> +		wm = ilk_compute_fbc_wm(crtc_state, plane_state,
> +					raw->plane[plane_id]);
> +		max_wm = g4x_fbc_fifo_size(level);
> +
> +		/*
> +		 * FBC wm is not mandatory as we
> +		 * can always just disable its use.
> +		 */
> +		if (wm > max_wm)
> +			wm = USHRT_MAX;
> +
> +		dirty |= raw->fbc != wm;
> +		raw->fbc = wm;
> +	}
> +
> +	/* mark watermarks as invalid */
> +	dirty |= g4x_raw_plane_wm_set(crtc_state, level, plane_id, USHRT_MAX);
> +
> +	if (plane_id == PLANE_PRIMARY)
> +		dirty |= g4x_raw_fbc_wm_set(crtc_state, level, USHRT_MAX);
> +
> + out:
> +	if (dirty) {
> +		DRM_DEBUG_KMS("%s watermarks: normal=%d, SR=%d, HPLL=%d\n",
> +			      plane->base.name,
> +			      crtc_state->wm.g4x.raw[G4X_WM_LEVEL_NORMAL].plane[plane_id],
> +			      crtc_state->wm.g4x.raw[G4X_WM_LEVEL_SR].plane[plane_id],
> +			      crtc_state->wm.g4x.raw[G4X_WM_LEVEL_HPLL].plane[plane_id]);
> +
> +		if (plane_id == PLANE_PRIMARY)
> +			DRM_DEBUG_KMS("FBC watermarks: SR=%d, HPLL=%d\n",
> +				      crtc_state->wm.g4x.raw[G4X_WM_LEVEL_SR].fbc,
> +				      crtc_state->wm.g4x.raw[G4X_WM_LEVEL_HPLL].fbc);
> +	}
> +
> +	return dirty;
> +}
> +
> +static bool g4x_raw_plane_wm_is_valid(const struct intel_crtc_state *crtc_state,
> +				      enum plane_id plane_id, int level)
> +{
> +	const struct g4x_pipe_wm *raw = &crtc_state->wm.g4x.raw[level];
> +
> +	return raw->plane[plane_id] <= g4x_plane_fifo_size(plane_id, level);
> +}
> +
> +static bool g4x_raw_crtc_wm_is_valid(const struct intel_crtc_state *crtc_state,
> +				     int level)
> +{
> +	struct drm_i915_private *dev_priv = to_i915(crtc_state->base.crtc->dev);
> +
> +	if (level > dev_priv->wm.max_level)
> +		return false;
> +
> +	return g4x_raw_plane_wm_is_valid(crtc_state, PLANE_PRIMARY, level) &&
> +		g4x_raw_plane_wm_is_valid(crtc_state, PLANE_SPRITE0, level) &&
> +		g4x_raw_plane_wm_is_valid(crtc_state, PLANE_CURSOR, level);
> +}
> +
> +/* mark all levels starting from 'level' as invalid */
> +static void g4x_invalidate_wms(struct intel_crtc *crtc,
> +			       struct g4x_wm_state *wm_state, int level)
> +{
> +	if (level <= G4X_WM_LEVEL_NORMAL) {
> +		enum plane_id plane_id;
> +
> +		for_each_plane_id_on_crtc(crtc, plane_id)
> +			wm_state->wm.plane[plane_id] = USHRT_MAX;
> +	}
> +
> +	if (level <= G4X_WM_LEVEL_SR) {
> +		wm_state->cxsr = false;
> +		wm_state->sr.cursor = USHRT_MAX;
> +		wm_state->sr.plane = USHRT_MAX;
> +		wm_state->sr.fbc = USHRT_MAX;
> +	}
> +
> +	if (level <= G4X_WM_LEVEL_HPLL) {
> +		wm_state->hpll_en = false;
> +		wm_state->hpll.cursor = USHRT_MAX;
> +		wm_state->hpll.plane = USHRT_MAX;
> +		wm_state->hpll.fbc = USHRT_MAX;
> +	}
> +}
> +
> +static int g4x_compute_pipe_wm(struct intel_crtc_state *crtc_state)
> +{
> +	struct intel_crtc *crtc = to_intel_crtc(crtc_state->base.crtc);
> +	struct intel_atomic_state *state =
> +		to_intel_atomic_state(crtc_state->base.state);
> +	struct g4x_wm_state *wm_state = &crtc_state->wm.g4x.optimal;
> +	int num_active_planes = hweight32(crtc_state->active_planes &
> +					  ~BIT(PLANE_CURSOR));
> +	const struct g4x_pipe_wm *raw;
> +	struct intel_plane_state *plane_state;
> +	struct intel_plane *plane;
> +	enum plane_id plane_id;
> +	int i, level;
> +	unsigned int dirty = 0;
> +
> +	for_each_intel_plane_in_state(state, plane, plane_state, i) {
> +		const struct intel_plane_state *old_plane_state =
> +			to_intel_plane_state(plane->base.state);
> +
> +		if (plane_state->base.crtc != &crtc->base &&
> +		    old_plane_state->base.crtc != &crtc->base)
> +			continue;
> +
> +		if (g4x_raw_plane_wm_compute(crtc_state, plane_state))
> +			dirty |= BIT(plane->id);
> +	}
> +
> +	if (!dirty)
> +		return 0;
> +
> +	level = G4X_WM_LEVEL_NORMAL;
> +	if (!g4x_raw_crtc_wm_is_valid(crtc_state, level))
> +		goto out;
> +
> +	raw = &crtc_state->wm.g4x.raw[level];
> +	for_each_plane_id_on_crtc(crtc, plane_id)
> +		wm_state->wm.plane[plane_id] = raw->plane[plane_id];
> +
> +	level = G4X_WM_LEVEL_SR;
> +
> +	if (!g4x_raw_crtc_wm_is_valid(crtc_state, level))
> +		goto out;
> +
> +	raw = &crtc_state->wm.g4x.raw[level];
> +	wm_state->sr.plane = raw->plane[PLANE_PRIMARY];
> +	wm_state->sr.cursor = raw->plane[PLANE_CURSOR];
> +	wm_state->sr.fbc = raw->fbc;
> +
> +	wm_state->cxsr = num_active_planes == BIT(PLANE_PRIMARY);
> +
> +	level = G4X_WM_LEVEL_HPLL;
> +
> +	if (!g4x_raw_crtc_wm_is_valid(crtc_state, level))
> +		goto out;
> +
> +	raw = &crtc_state->wm.g4x.raw[level];
> +	wm_state->hpll.plane = raw->plane[PLANE_PRIMARY];
> +	wm_state->hpll.cursor = raw->plane[PLANE_CURSOR];
> +	wm_state->hpll.fbc = raw->fbc;
> +
> +	wm_state->hpll_en = wm_state->cxsr;
> +
> +	level++;
> +
> + out:
> +	if (level == G4X_WM_LEVEL_NORMAL)
> +		return -EINVAL;
> +
> +	/* invalidate the higher levels */
> +	g4x_invalidate_wms(crtc, wm_state, level);
> +
> +	/*
> +	 * Determine if the FBC watermark(s) can be used. IF
> +	 * this isn't the case we prefer to disable the FBC
> +	 ( watermark(s) rather than disable the SR/HPLL
> +	 * level(s) entirely.
> +	 */
> +	wm_state->fbc_en = level > G4X_WM_LEVEL_NORMAL;
> +
> +	if (level >= G4X_WM_LEVEL_SR &&
> +	    wm_state->sr.fbc > g4x_fbc_fifo_size(G4X_WM_LEVEL_SR))
> +		wm_state->fbc_en = false;
> +	else if (level >= G4X_WM_LEVEL_HPLL &&
> +		 wm_state->hpll.fbc > g4x_fbc_fifo_size(G4X_WM_LEVEL_HPLL))
> +		wm_state->fbc_en = false;
> +
> +	return 0;
> +}
> +
> +static int g4x_compute_intermediate_wm(struct drm_device *dev,
> +				       struct intel_crtc *crtc,
> +				       struct intel_crtc_state *crtc_state)
> +{
> +	struct g4x_wm_state *intermediate = &crtc_state->wm.g4x.intermediate;
> +	const struct g4x_wm_state *optimal = &crtc_state->wm.g4x.optimal;
> +	const struct g4x_wm_state *active = &crtc->wm.active.g4x;
> +	enum plane_id plane_id;

I would add a provision for modeset, no need to calculate intermediate watermarks with the pipe enabled, something like this?

	if (!newstate->base.active || drm_atomic_crtc_needs_modeset(&newstate->base)) {
		*intermediate = *optimal;
		return 0;
	}

The active watermarks are wrongly assigned here, if there's a pending update it could point to old old optimal state, or old intermediate state, it should be something like this:
old_crtc_state = drm_atomic_get_old_crtc_state(crtc_state->base.state, &crtc->base);
active = &to_intel_crtc_state(old_crtc_state)->wm.g4x.optimal

I know, vlv does this wrong too, should be fixed as well..
With that fixed all patches look good to me, so feel free to put this on them:

Reviewed-by: Maarten Lankhorst <maarten.lankhorst@linux.intel.com>

> +	intermediate->cxsr = optimal->cxsr && active->cxsr &&
> +		!crtc_state->disable_cxsr;
> +	intermediate->hpll_en = optimal->hpll_en && active->hpll_en &&
> +		!crtc_state->disable_cxsr;
> +	intermediate->fbc_en = optimal->fbc_en && active->fbc_en;
> +
> +	for_each_plane_id_on_crtc(crtc, plane_id) {
> +		intermediate->wm.plane[plane_id] =
> +			max(optimal->wm.plane[plane_id],
> +			    active->wm.plane[plane_id]);
> +
> +		WARN_ON(intermediate->wm.plane[plane_id] >
> +			g4x_plane_fifo_size(plane_id, G4X_WM_LEVEL_NORMAL));
> +	}
> +
> +	intermediate->sr.plane = max(optimal->sr.plane,
> +				     active->sr.plane);
> +	intermediate->sr.cursor = max(optimal->sr.cursor,
> +				      active->sr.cursor);
> +	intermediate->sr.fbc = max(optimal->sr.fbc,
> +				   active->sr.fbc);
> +
> +	intermediate->hpll.plane = max(optimal->hpll.plane,
> +				       active->hpll.plane);
> +	intermediate->hpll.cursor = max(optimal->hpll.cursor,
> +					active->hpll.cursor);
> +	intermediate->hpll.fbc = max(optimal->hpll.fbc,
> +				     active->hpll.fbc);
> +
> +	WARN_ON((intermediate->sr.plane >
> +		 g4x_plane_fifo_size(PLANE_PRIMARY, G4X_WM_LEVEL_SR) ||
> +		 intermediate->sr.cursor >
> +		 g4x_plane_fifo_size(PLANE_CURSOR, G4X_WM_LEVEL_SR)) &&
> +		intermediate->cxsr);
> +	WARN_ON((intermediate->sr.plane >
> +		 g4x_plane_fifo_size(PLANE_PRIMARY, G4X_WM_LEVEL_HPLL) ||
> +		 intermediate->sr.cursor >
> +		 g4x_plane_fifo_size(PLANE_CURSOR, G4X_WM_LEVEL_HPLL)) &&
> +		intermediate->hpll_en);
> +
> +	WARN_ON(intermediate->sr.fbc > g4x_fbc_fifo_size(1) &&
> +		intermediate->fbc_en && intermediate->cxsr);
> +	WARN_ON(intermediate->hpll.fbc > g4x_fbc_fifo_size(2) &&
> +		intermediate->fbc_en && intermediate->hpll_en);
> +
> +	/*
> +	 * If our intermediate WM are identical to the final WM, then we can
> +	 * omit the post-vblank programming; only update if it's different.
> +	 */
> +	if (memcmp(intermediate, optimal, sizeof(*intermediate)) != 0)
> +		crtc_state->wm.need_postvbl_update = true;
> +
> +	return 0;
> +}
> +
> +static void g4x_merge_wm(struct drm_i915_private *dev_priv,
> +			 struct g4x_wm_values *wm)
> +{
> +	struct intel_crtc *crtc;
> +	int num_active_crtcs = 0;
> +
> +	wm->cxsr = true;
> +	wm->hpll_en = true;
> +	wm->fbc_en = true;
> +
> +	for_each_intel_crtc(&dev_priv->drm, crtc) {
> +		const struct g4x_wm_state *wm_state = &crtc->wm.active.g4x;
> +
> +		if (!crtc->active)
> +			continue;
> +
> +		if (!wm_state->cxsr)
> +			wm->cxsr = false;
> +		if (!wm_state->hpll_en)
> +			wm->hpll_en = false;
> +		if (!wm_state->fbc_en)
> +			wm->fbc_en = false;
> +
> +		num_active_crtcs++;
> +	}
> +
> +	if (num_active_crtcs != 1) {
> +		wm->cxsr = false;
> +		wm->hpll_en = false;
> +		wm->fbc_en = false;
> +	}
> +
> +	for_each_intel_crtc(&dev_priv->drm, crtc) {
> +		const struct g4x_wm_state *wm_state = &crtc->wm.active.g4x;
> +		enum pipe pipe = crtc->pipe;
> +
> +		wm->pipe[pipe] = wm_state->wm;
> +		if (crtc->active && wm->cxsr)
> +			wm->sr = wm_state->sr;
> +		if (crtc->active && wm->hpll_en)
> +			wm->hpll = wm_state->hpll;
> +	}
> +}
> +
> +static void g4x_program_watermarks(struct drm_i915_private *dev_priv)
> +{
> +	struct g4x_wm_values *old_wm = &dev_priv->wm.g4x;
> +	struct g4x_wm_values new_wm = {};
> +
> +	g4x_merge_wm(dev_priv, &new_wm);
> +
> +	if (memcmp(old_wm, &new_wm, sizeof(new_wm)) == 0)
> +		return;
> +
> +	if (is_disabling(old_wm->cxsr, new_wm.cxsr, true))
> +		_intel_set_memory_cxsr(dev_priv, false);
> +
> +	g4x_write_wm_values(dev_priv, &new_wm);
> +
> +	if (is_enabling(old_wm->cxsr, new_wm.cxsr, true))
> +		_intel_set_memory_cxsr(dev_priv, true);
> +
> +	*old_wm = new_wm;
> +}
> +
> +static void g4x_initial_watermarks(struct intel_atomic_state *state,
> +				   struct intel_crtc_state *crtc_state)
> +{
> +	struct drm_i915_private *dev_priv = to_i915(crtc_state->base.crtc->dev);
> +	struct intel_crtc *crtc = to_intel_crtc(crtc_state->base.crtc);
> +
> +	mutex_lock(&dev_priv->wm.wm_mutex);
> +	crtc->wm.active.g4x = crtc_state->wm.g4x.intermediate;
> +	g4x_program_watermarks(dev_priv);
> +	mutex_unlock(&dev_priv->wm.wm_mutex);
> +}
> +
> +static void g4x_optimize_watermarks(struct intel_atomic_state *state,
> +				    struct intel_crtc_state *crtc_state)
> +{
> +	struct drm_i915_private *dev_priv = to_i915(crtc_state->base.crtc->dev);
> +	struct intel_crtc *intel_crtc = to_intel_crtc(crtc_state->base.crtc);
> +
> +	if (!crtc_state->wm.need_postvbl_update)
> +		return;
> +
> +	mutex_lock(&dev_priv->wm.wm_mutex);
> +	intel_crtc->wm.active.g4x = crtc_state->wm.g4x.optimal;
> +	g4x_program_watermarks(dev_priv);
> +	mutex_unlock(&dev_priv->wm.wm_mutex);
> +}
> +
>  /* latency must be in 0.1us units. */
>  static unsigned int vlv_wm_method2(unsigned int pixel_rate,
>  				   unsigned int htotal,
> @@ -1673,16 +2079,6 @@ static void vlv_merge_wm(struct drm_i915_private *dev_priv,
>  	}
>  }
>  
> -static bool is_disabling(int old, int new, int threshold)
> -{
> -	return old >= threshold && new < threshold;
> -}
> -
> -static bool is_enabling(int old, int new, int threshold)
> -{
> -	return old < threshold && new >= threshold;
> -}
> -
>  static void vlv_program_watermarks(struct drm_i915_private *dev_priv)
>  {
>  	struct vlv_wm_values *old_wm = &dev_priv->wm.vlv;
> @@ -1743,65 +2139,6 @@ static void vlv_optimize_watermarks(struct intel_atomic_state *state,
>  	mutex_unlock(&dev_priv->wm.wm_mutex);
>  }
>  
> -#define single_plane_enabled(mask) is_power_of_2(mask)
> -
> -static void g4x_update_wm(struct intel_crtc *crtc)
> -{
> -	struct drm_i915_private *dev_priv = to_i915(crtc->base.dev);
> -	static const int sr_latency_ns = 12000;
> -	int planea_wm, planeb_wm, cursora_wm, cursorb_wm;
> -	int plane_sr, cursor_sr;
> -	unsigned int enabled = 0;
> -	bool cxsr_enabled;
> -
> -	if (g4x_compute_wm0(dev_priv, PIPE_A,
> -			    &g4x_wm_info, pessimal_latency_ns,
> -			    &g4x_cursor_wm_info, pessimal_latency_ns,
> -			    &planea_wm, &cursora_wm))
> -		enabled |= 1 << PIPE_A;
> -
> -	if (g4x_compute_wm0(dev_priv, PIPE_B,
> -			    &g4x_wm_info, pessimal_latency_ns,
> -			    &g4x_cursor_wm_info, pessimal_latency_ns,
> -			    &planeb_wm, &cursorb_wm))
> -		enabled |= 1 << PIPE_B;
> -
> -	if (single_plane_enabled(enabled) &&
> -	    g4x_compute_srwm(dev_priv, ffs(enabled) - 1,
> -			     sr_latency_ns,
> -			     &g4x_wm_info,
> -			     &g4x_cursor_wm_info,
> -			     &plane_sr, &cursor_sr)) {
> -		cxsr_enabled = true;
> -	} else {
> -		cxsr_enabled = false;
> -		intel_set_memory_cxsr(dev_priv, false);
> -		plane_sr = cursor_sr = 0;
> -	}
> -
> -	DRM_DEBUG_KMS("Setting FIFO watermarks - A: plane=%d, cursor=%d, "
> -		      "B: plane=%d, cursor=%d, SR: plane=%d, cursor=%d\n",
> -		      planea_wm, cursora_wm,
> -		      planeb_wm, cursorb_wm,
> -		      plane_sr, cursor_sr);
> -
> -	I915_WRITE(DSPFW1,
> -		   FW_WM(plane_sr, SR) |
> -		   FW_WM(cursorb_wm, CURSORB) |
> -		   FW_WM(planeb_wm, PLANEB) |
> -		   FW_WM(planea_wm, PLANEA));
> -	I915_WRITE(DSPFW2,
> -		   (I915_READ(DSPFW2) & ~DSPFW_CURSORA_MASK) |
> -		   FW_WM(cursora_wm, CURSORA));
> -	/* HPLL off in SR has some issues on G4x... disable it */
> -	I915_WRITE(DSPFW3,
> -		   (I915_READ(DSPFW3) & ~(DSPFW_HPLL_SR_EN | DSPFW_CURSOR_SR_MASK)) |
> -		   FW_WM(cursor_sr, CURSOR_SR));
> -
> -	if (cxsr_enabled)
> -		intel_set_memory_cxsr(dev_priv, true);
> -}
> -
>  static void i965_update_wm(struct intel_crtc *unused_crtc)
>  {
>  	struct drm_i915_private *dev_priv = to_i915(unused_crtc->base.dev);
> @@ -4778,6 +5115,32 @@ static void ilk_pipe_wm_get_hw_state(struct drm_crtc *crtc)
>  #define _FW_WM_VLV(value, plane) \
>  	(((value) & DSPFW_ ## plane ## _MASK_VLV) >> DSPFW_ ## plane ## _SHIFT)
>  
> +static void g4x_read_wm_values(struct drm_i915_private *dev_priv,
> +			       struct g4x_wm_values *wm)
> +{
> +	uint32_t tmp;
> +
> +	tmp = I915_READ(DSPFW1);
> +	wm->sr.plane = _FW_WM(tmp, SR);
> +	wm->pipe[PIPE_B].plane[PLANE_CURSOR] = _FW_WM(tmp, CURSORB);
> +	wm->pipe[PIPE_B].plane[PLANE_PRIMARY] = _FW_WM(tmp, PLANEB);
> +	wm->pipe[PIPE_A].plane[PLANE_PRIMARY] = _FW_WM(tmp, PLANEA);
> +
> +	tmp = I915_READ(DSPFW2);
> +	wm->fbc_en = tmp & DSPFW_FBC_SR_EN;
> +	wm->sr.fbc = _FW_WM(tmp, FBC_SR);
> +	wm->hpll.fbc = _FW_WM(tmp, FBC_HPLL_SR);
> +	wm->pipe[PIPE_B].plane[PLANE_SPRITE0] = _FW_WM(tmp, SPRITEB);
> +	wm->pipe[PIPE_A].plane[PLANE_CURSOR] = _FW_WM(tmp, CURSORA);
> +	wm->pipe[PIPE_A].plane[PLANE_SPRITE0] = _FW_WM(tmp, SPRITEA);
> +
> +	tmp = I915_READ(DSPFW3);
> +	wm->hpll_en = tmp & DSPFW_HPLL_SR_EN;
> +	wm->sr.cursor = _FW_WM(tmp, CURSOR_SR);
> +	wm->hpll.cursor = _FW_WM(tmp, HPLL_CURSOR);
> +	wm->hpll.plane = _FW_WM(tmp, HPLL_SR);
> +}
> +
>  static void vlv_read_wm_values(struct drm_i915_private *dev_priv,
>  			       struct vlv_wm_values *wm)
>  {
> @@ -4854,6 +5217,147 @@ static void vlv_read_wm_values(struct drm_i915_private *dev_priv,
>  #undef _FW_WM
>  #undef _FW_WM_VLV
>  
> +void g4x_wm_get_hw_state(struct drm_device *dev)
> +{
> +	struct drm_i915_private *dev_priv = to_i915(dev);
> +	struct g4x_wm_values *wm = &dev_priv->wm.g4x;
> +	struct intel_crtc *crtc;
> +
> +	g4x_read_wm_values(dev_priv, wm);
> +
> +	wm->cxsr = I915_READ(FW_BLC_SELF) & FW_BLC_SELF_EN;
> +
> +	for_each_intel_crtc(dev, crtc) {
> +		struct intel_crtc_state *crtc_state =
> +			to_intel_crtc_state(crtc->base.state);
> +		struct g4x_wm_state *active = &crtc->wm.active.g4x;
> +		struct g4x_pipe_wm *raw;
> +		enum pipe pipe = crtc->pipe;
> +		enum plane_id plane_id;
> +		int level, max_level;
> +
> +		active->cxsr = wm->cxsr;
> +		active->hpll_en = wm->hpll_en;
> +		active->fbc_en = wm->fbc_en;
> +
> +		active->sr = wm->sr;
> +		active->hpll = wm->hpll;
> +
> +		for_each_plane_id_on_crtc(crtc, plane_id) {
> +			active->wm.plane[plane_id] =
> +				wm->pipe[pipe].plane[plane_id];
> +		}
> +
> +		if (wm->cxsr && wm->hpll_en)
> +			max_level = G4X_WM_LEVEL_HPLL;
> +		else if (wm->cxsr)
> +			max_level = G4X_WM_LEVEL_SR;
> +		else
> +			max_level = G4X_WM_LEVEL_NORMAL;
> +
> +		level = G4X_WM_LEVEL_NORMAL;
> +		raw = &crtc_state->wm.g4x.raw[level];
> +		for_each_plane_id_on_crtc(crtc, plane_id)
> +			raw->plane[plane_id] = active->wm.plane[plane_id];
> +
> +		if (++level > max_level)
> +			goto out;
> +
> +		raw = &crtc_state->wm.g4x.raw[level];
> +		raw->plane[PLANE_PRIMARY] = active->sr.plane;
> +		raw->plane[PLANE_CURSOR] = active->sr.cursor;
> +		raw->plane[PLANE_SPRITE0] = 0;
> +		raw->fbc = active->sr.fbc;
> +
> +		if (++level > max_level)
> +			goto out;
> +
> +		raw = &crtc_state->wm.g4x.raw[level];
> +		raw->plane[PLANE_PRIMARY] = active->hpll.plane;
> +		raw->plane[PLANE_CURSOR] = active->hpll.cursor;
> +		raw->plane[PLANE_SPRITE0] = 0;
> +		raw->fbc = active->hpll.fbc;
> +
> +	out:
> +		for_each_plane_id_on_crtc(crtc, plane_id)
> +			g4x_raw_plane_wm_set(crtc_state, level,
> +					     plane_id, USHRT_MAX);
> +		g4x_raw_fbc_wm_set(crtc_state, level, USHRT_MAX);
> +
> +		crtc_state->wm.g4x.optimal = *active;
> +		crtc_state->wm.g4x.intermediate = *active;
> +
> +		DRM_DEBUG_KMS("Initial watermarks: pipe %c, plane=%d, cursor=%d, sprite=%d\n",
> +			      pipe_name(pipe),
> +			      wm->pipe[pipe].plane[PLANE_PRIMARY],
> +			      wm->pipe[pipe].plane[PLANE_CURSOR],
> +			      wm->pipe[pipe].plane[PLANE_SPRITE0]);
> +	}
> +
> +	DRM_DEBUG_KMS("Initial SR watermarks: plane=%d, cursor=%d fbc=%d\n",
> +		      wm->sr.plane, wm->sr.cursor, wm->sr.fbc);
> +	DRM_DEBUG_KMS("Initial HPLL watermarks: plane=%d, SR cursor=%d fbc=%d\n",
> +		      wm->hpll.plane, wm->hpll.cursor, wm->hpll.fbc);
> +	DRM_DEBUG_KMS("Initial SR=%s HPLL=%s FBC=%s\n",
> +		      yesno(wm->cxsr), yesno(wm->hpll_en), yesno(wm->fbc_en));
> +}
> +
> +void g4x_wm_sanitize(struct drm_i915_private *dev_priv)
> +{
> +	struct intel_plane *plane;
> +	struct intel_crtc *crtc;
> +
> +	mutex_lock(&dev_priv->wm.wm_mutex);
> +
> +	for_each_intel_plane(&dev_priv->drm, plane) {
> +		struct intel_crtc *crtc =
> +			intel_get_crtc_for_pipe(dev_priv, plane->pipe);
> +		struct intel_crtc_state *crtc_state =
> +			to_intel_crtc_state(crtc->base.state);
> +		struct intel_plane_state *plane_state =
> +			to_intel_plane_state(plane->base.state);
> +		struct g4x_wm_state *wm_state = &crtc_state->wm.g4x.optimal;
> +		enum plane_id plane_id = plane->id;
> +		int level;
> +
> +		if (plane_state->base.visible)
> +			continue;
> +
> +		for (level = 0; level < 3; level++) {
> +			struct g4x_pipe_wm *raw =
> +				&crtc_state->wm.g4x.raw[level];
> +
> +			raw->plane[plane_id] = 0;
> +			wm_state->wm.plane[plane_id] = 0;
> +		}
> +
> +		if (plane_id == PLANE_PRIMARY) {
> +			for (level = 0; level < 3; level++) {
> +				struct g4x_pipe_wm *raw =
> +					&crtc_state->wm.g4x.raw[level];
> +				raw->fbc = 0;
> +			}
> +
> +			wm_state->sr.fbc = 0;
> +			wm_state->hpll.fbc = 0;
> +			wm_state->fbc_en = false;
> +		}
> +	}
> +
> +	for_each_intel_crtc(&dev_priv->drm, crtc) {
> +		struct intel_crtc_state *crtc_state =
> +			to_intel_crtc_state(crtc->base.state);
> +
> +		crtc_state->wm.g4x.intermediate =
> +			crtc_state->wm.g4x.optimal;
> +		crtc->wm.active.g4x = crtc_state->wm.g4x.optimal;
> +	}
> +
> +	g4x_program_watermarks(dev_priv);
> +
> +	mutex_unlock(&dev_priv->wm.wm_mutex);
> +}
> +
>  void vlv_wm_get_hw_state(struct drm_device *dev)
>  {
>  	struct drm_i915_private *dev_priv = to_i915(dev);
> @@ -8160,6 +8664,12 @@ void intel_init_pm(struct drm_i915_private *dev_priv)
>  		dev_priv->display.initial_watermarks = vlv_initial_watermarks;
>  		dev_priv->display.optimize_watermarks = vlv_optimize_watermarks;
>  		dev_priv->display.atomic_update_watermarks = vlv_atomic_update_fifo;
> +	} else if (IS_G4X(dev_priv)) {
> +		g4x_setup_wm_latency(dev_priv);
> +		dev_priv->display.compute_pipe_wm = g4x_compute_pipe_wm;
> +		dev_priv->display.compute_intermediate_wm = g4x_compute_intermediate_wm;
> +		dev_priv->display.initial_watermarks = g4x_initial_watermarks;
> +		dev_priv->display.optimize_watermarks = g4x_optimize_watermarks;
>  	} else if (IS_PINEVIEW(dev_priv)) {
>  		if (!intel_get_cxsr_latency(IS_PINEVIEW_G(dev_priv),
>  					    dev_priv->is_ddr3,
> @@ -8175,8 +8685,6 @@ void intel_init_pm(struct drm_i915_private *dev_priv)
>  			dev_priv->display.update_wm = NULL;
>  		} else
>  			dev_priv->display.update_wm = pineview_update_wm;
> -	} else if (IS_G4X(dev_priv)) {
> -		dev_priv->display.update_wm = g4x_update_wm;
>  	} else if (IS_GEN4(dev_priv)) {
>  		dev_priv->display.update_wm = i965_update_wm;
>  	} else if (IS_GEN3(dev_priv)) {


_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH 12/15] drm/i915: Two stage watermarks for g4x
  2017-04-24  7:34   ` Maarten Lankhorst
@ 2017-04-24 13:16     ` Ville Syrjälä
  2017-05-10 16:40     ` Ville Syrjälä
  1 sibling, 0 replies; 21+ messages in thread
From: Ville Syrjälä @ 2017-04-24 13:16 UTC (permalink / raw)
  To: Maarten Lankhorst; +Cc: intel-gfx

On Mon, Apr 24, 2017 at 09:34:42AM +0200, Maarten Lankhorst wrote:
> On 21-04-17 20:14, ville.syrjala@linux.intel.com wrote:
> > From: Ville Syrjälä <ville.syrjala@linux.intel.com>
> >
> > Implement proper two stage watermark programming for g4x. As with
> > other pre-SKL platforms, the watermark registers aren't double
> > buffered on g4x. Hence we must sequence the watermark update
> > carefully around plane updates.
> >
> > The code is quite heavily modelled on the VLV/CHV code, with some
> > fairly significant differences due to the different hardware
> > architecture:
> > * g4x doesn't use inverted watermark values
> > * CxSR actually affects the watermarks since it controls memory self
> >   refresh in addition to the max FIFO mode
> > * A further HPLL SR mode is possible with higher memory wakeup
> >   latency
> > * g4x has FBC2 and so it also has FBC watermarks
> > * max FIFO mode for primary plane only (cursor is allowed, sprite is not)
> > * g4x has no manual FIFO repartitioning
> > * some TLB miss related workarounds are needed for the watermarks
> >
> > Actually the hardware is quite similar to ILK+ in many ways. The
> > most visible differences are in the actual watermakr register
> > layout. ILK revamped that part quite heavily whereas g4x is still
> > using the layout inherited from earlier platforms.
> >
> > Note that we didn't previously enable the HPLL SR on g4x. So in order
> > to not introduce too many functional changes in this patch I've not
> > actually enabled it here either, even though the code is now fully
> > ready for it. We'll enable it separately later on.
> >
> > Signed-off-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
> > ---
> >  drivers/gpu/drm/i915/i915_debugfs.c  |  12 +-
> >  drivers/gpu/drm/i915/i915_drv.h      |  12 +
> >  drivers/gpu/drm/i915/intel_display.c |  25 +-
> >  drivers/gpu/drm/i915/intel_drv.h     |  28 ++
> >  drivers/gpu/drm/i915/intel_pm.c      | 942 +++++++++++++++++++++++++++--------
> >  5 files changed, 792 insertions(+), 227 deletions(-)
> >
> > diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c
> > index 870c470177b5..69550d31099e 100644
> > --- a/drivers/gpu/drm/i915/i915_debugfs.c
> > +++ b/drivers/gpu/drm/i915/i915_debugfs.c
> > @@ -3898,6 +3898,8 @@ static void wm_latency_show(struct seq_file *m, const uint16_t wm[8])
> >  		num_levels = 3;
> >  	else if (IS_VALLEYVIEW(dev_priv))
> >  		num_levels = 1;
> > +	else if (IS_G4X(dev_priv))
> > +		num_levels = 3;
> >  	else
> >  		num_levels = ilk_wm_max_level(dev_priv) + 1;
> >  
> > @@ -3910,8 +3912,10 @@ static void wm_latency_show(struct seq_file *m, const uint16_t wm[8])
> >  		 * - WM1+ latency values in 0.5us units
> >  		 * - latencies are in us on gen9/vlv/chv
> >  		 */
> > -		if (INTEL_GEN(dev_priv) >= 9 || IS_VALLEYVIEW(dev_priv) ||
> > -		    IS_CHERRYVIEW(dev_priv))
> > +		if (INTEL_GEN(dev_priv) >= 9 ||
> > +		    IS_VALLEYVIEW(dev_priv) ||
> > +		    IS_CHERRYVIEW(dev_priv) ||
> > +		    IS_G4X(dev_priv))
> >  			latency *= 10;
> >  		else if (level > 0)
> >  			latency *= 5;
> > @@ -3972,7 +3976,7 @@ static int pri_wm_latency_open(struct inode *inode, struct file *file)
> >  {
> >  	struct drm_i915_private *dev_priv = inode->i_private;
> >  
> > -	if (INTEL_GEN(dev_priv) < 5)
> > +	if (INTEL_GEN(dev_priv) < 5 && !IS_G4X(dev_priv))
> >  		return -ENODEV;
> >  
> >  	return single_open(file, pri_wm_latency_show, dev_priv);
> > @@ -4014,6 +4018,8 @@ static ssize_t wm_latency_write(struct file *file, const char __user *ubuf,
> >  		num_levels = 3;
> >  	else if (IS_VALLEYVIEW(dev_priv))
> >  		num_levels = 1;
> > +	else if (IS_G4X(dev_priv))
> > +		num_levels = 3;
> >  	else
> >  		num_levels = ilk_wm_max_level(dev_priv) + 1;
> >  
> > diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
> > index 0a393fdc53d1..6df8bff7f5a7 100644
> > --- a/drivers/gpu/drm/i915/i915_drv.h
> > +++ b/drivers/gpu/drm/i915/i915_drv.h
> > @@ -1762,11 +1762,13 @@ struct ilk_wm_values {
> >  
> >  struct g4x_pipe_wm {
> >  	uint16_t plane[I915_MAX_PLANES];
> > +	uint16_t fbc;
> >  };
> >  
> >  struct g4x_sr_wm {
> >  	uint16_t plane;
> >  	uint16_t cursor;
> > +	uint16_t fbc;
> >  };
> >  
> >  struct vlv_wm_ddl_values {
> > @@ -1781,6 +1783,15 @@ struct vlv_wm_values {
> >  	bool cxsr;
> >  };
> >  
> > +struct g4x_wm_values {
> > +	struct g4x_pipe_wm pipe[2];
> > +	struct g4x_sr_wm sr;
> > +	struct g4x_sr_wm hpll;
> > +	bool cxsr;
> > +	bool hpll_en;
> > +	bool fbc_en;
> > +};
> > +
> >  struct skl_ddb_entry {
> >  	uint16_t start, end;	/* in number of blocks, 'end' is exclusive */
> >  };
> > @@ -2410,6 +2421,7 @@ struct drm_i915_private {
> >  			struct ilk_wm_values hw;
> >  			struct skl_wm_values skl_hw;
> >  			struct vlv_wm_values vlv;
> > +			struct g4x_wm_values g4x;
> >  		};
> >  
> >  		uint8_t max_level;
> > diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c
> > index 85b9e2f521a0..0f42263c3f76 100644
> > --- a/drivers/gpu/drm/i915/intel_display.c
> > +++ b/drivers/gpu/drm/i915/intel_display.c
> > @@ -5719,6 +5719,8 @@ static void i9xx_set_pll_dividers(struct intel_crtc *crtc)
> >  static void i9xx_crtc_enable(struct intel_crtc_state *pipe_config,
> >  			     struct drm_atomic_state *old_state)
> >  {
> > +	struct intel_atomic_state *old_intel_state =
> > +		to_intel_atomic_state(old_state);
> >  	struct drm_crtc *crtc = pipe_config->base.crtc;
> >  	struct drm_device *dev = crtc->dev;
> >  	struct drm_i915_private *dev_priv = to_i915(dev);
> > @@ -5751,7 +5753,11 @@ static void i9xx_crtc_enable(struct intel_crtc_state *pipe_config,
> >  
> >  	intel_color_load_luts(&pipe_config->base);
> >  
> > -	intel_update_watermarks(intel_crtc);
> > +	if (dev_priv->display.initial_watermarks != NULL)
> > +		dev_priv->display.initial_watermarks(old_intel_state,
> > +						     intel_crtc->config);
> > +	else
> > +		intel_update_watermarks(intel_crtc);
> >  	intel_enable_pipe(intel_crtc);
> >  
> >  	assert_vblank_disabled(crtc);
> > @@ -10852,21 +10858,21 @@ int intel_plane_atomic_calc_changes(struct drm_crtc_state *crtc_state,
> >  			 turn_off, turn_on, mode_changed);
> >  
> >  	if (turn_on) {
> > -		if (INTEL_GEN(dev_priv) < 5)
> > +		if (INTEL_GEN(dev_priv) < 5 && !IS_G4X(dev_priv))
> >  			pipe_config->update_wm_pre = true;
> >  
> >  		/* must disable cxsr around plane enable/disable */
> >  		if (plane->id != PLANE_CURSOR)
> >  			pipe_config->disable_cxsr = true;
> >  	} else if (turn_off) {
> > -		if (INTEL_GEN(dev_priv) < 5)
> > +		if (INTEL_GEN(dev_priv) < 5 && !IS_G4X(dev_priv))
> >  			pipe_config->update_wm_post = true;
> >  
> >  		/* must disable cxsr around plane enable/disable */
> >  		if (plane->id != PLANE_CURSOR)
> >  			pipe_config->disable_cxsr = true;
> >  	} else if (intel_wm_need_update(&plane->base, plane_state)) {
> > -		if (INTEL_GEN(dev_priv) < 5) {
> > +		if (INTEL_GEN(dev_priv) < 5 && !IS_G4X(dev_priv)) {
> >  			/* FIXME bollocks */
> >  			pipe_config->update_wm_pre = true;
> >  			pipe_config->update_wm_post = true;
> > @@ -11290,7 +11296,8 @@ clear_intel_crtc_state(struct intel_crtc_state *crtc_state)
> >  	shared_dpll = crtc_state->shared_dpll;
> >  	dpll_hw_state = crtc_state->dpll_hw_state;
> >  	force_thru = crtc_state->pch_pfit.force_thru;
> > -	if (IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv))
> > +	if (IS_G4X(dev_priv) ||
> > +	    IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv))
> >  		wm_state = crtc_state->wm;
> >  
> >  	/* Keep base drm_crtc_state intact, only clear our extended struct */
> > @@ -11302,7 +11309,8 @@ clear_intel_crtc_state(struct intel_crtc_state *crtc_state)
> >  	crtc_state->shared_dpll = shared_dpll;
> >  	crtc_state->dpll_hw_state = dpll_hw_state;
> >  	crtc_state->pch_pfit.force_thru = force_thru;
> > -	if (IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv))
> > +	if (IS_G4X(dev_priv) ||
> > +	    IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv))
> >  		crtc_state->wm = wm_state;
> >  }
> >  
> > @@ -15527,7 +15535,10 @@ intel_modeset_setup_hw_state(struct drm_device *dev)
> >  		pll->on = false;
> >  	}
> >  
> > -	if (IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv)) {
> > +	if (IS_G4X(dev_priv)) {
> > +		g4x_wm_get_hw_state(dev);
> > +		g4x_wm_sanitize(dev_priv);
> > +	} else if (IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv)) {
> >  		vlv_wm_get_hw_state(dev);
> >  		vlv_wm_sanitize(dev_priv);
> >  	} else if (IS_GEN9(dev_priv)) {
> > diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
> > index d1cdd10998fa..f530df71a480 100644
> > --- a/drivers/gpu/drm/i915/intel_drv.h
> > +++ b/drivers/gpu/drm/i915/intel_drv.h
> > @@ -522,6 +522,22 @@ struct vlv_fifo_state {
> >  	u16 plane[I915_MAX_PLANES];
> >  };
> >  
> > +enum g4x_wm_level {
> > +	G4X_WM_LEVEL_NORMAL,
> > +	G4X_WM_LEVEL_SR,
> > +	G4X_WM_LEVEL_HPLL,
> > +	NUM_G4X_WM_LEVELS,
> > +};
> > +
> > +struct g4x_wm_state {
> > +	struct g4x_pipe_wm wm;
> > +	struct g4x_sr_wm sr;
> > +	struct g4x_sr_wm hpll;
> > +	bool cxsr;
> > +	bool hpll_en;
> > +	bool fbc_en;
> > +};
> > +
> >  struct intel_crtc_wm_state {
> >  	union {
> >  		struct {
> > @@ -557,6 +573,15 @@ struct intel_crtc_wm_state {
> >  			/* display FIFO split */
> >  			struct vlv_fifo_state fifo_state;
> >  		} vlv;
> > +
> > +		struct {
> > +			/* "raw" watermarks */
> > +			struct g4x_pipe_wm raw[NUM_G4X_WM_LEVELS];
> > +			/* intermediate watermarks */
> > +			struct g4x_wm_state intermediate;
> > +			/* optimal watermarks */
> > +			struct g4x_wm_state optimal;
> > +		} g4x;
> >  	};
> >  
> >  	/*
> > @@ -794,6 +819,7 @@ struct intel_crtc {
> >  		union {
> >  			struct intel_pipe_wm ilk;
> >  			struct vlv_wm_state vlv;
> > +			struct g4x_wm_state g4x;
> >  		} active;
> >  	} wm;
> >  
> > @@ -1841,6 +1867,7 @@ void gen6_rps_boost(struct drm_i915_private *dev_priv,
> >  		    struct intel_rps_client *rps,
> >  		    unsigned long submitted);
> >  void intel_queue_rps_boost_for_request(struct drm_i915_gem_request *req);
> > +void g4x_wm_get_hw_state(struct drm_device *dev);
> >  void vlv_wm_get_hw_state(struct drm_device *dev);
> >  void ilk_wm_get_hw_state(struct drm_device *dev);
> >  void skl_wm_get_hw_state(struct drm_device *dev);
> > @@ -1848,6 +1875,7 @@ void skl_ddb_get_hw_state(struct drm_i915_private *dev_priv,
> >  			  struct skl_ddb_allocation *ddb /* out */);
> >  void skl_pipe_wm_get_hw_state(struct drm_crtc *crtc,
> >  			      struct skl_pipe_wm *out);
> > +void g4x_wm_sanitize(struct drm_i915_private *dev_priv);
> >  void vlv_wm_sanitize(struct drm_i915_private *dev_priv);
> >  bool intel_can_enable_sagv(struct drm_atomic_state *state);
> >  int intel_enable_sagv(struct drm_i915_private *dev_priv);
> > diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c
> > index 61b67994c4a8..9b0a6a4572ce 100644
> > --- a/drivers/gpu/drm/i915/intel_pm.c
> > +++ b/drivers/gpu/drm/i915/intel_pm.c
> > @@ -429,7 +429,10 @@ bool intel_set_memory_cxsr(struct drm_i915_private *dev_priv, bool enable)
> >  
> >  	mutex_lock(&dev_priv->wm.wm_mutex);
> >  	ret = _intel_set_memory_cxsr(dev_priv, enable);
> > -	dev_priv->wm.vlv.cxsr = enable;
> > +	if (IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv))
> > +		dev_priv->wm.vlv.cxsr = enable;
> > +	else if (IS_G4X(dev_priv))
> > +		dev_priv->wm.g4x.cxsr = enable;
> >  	mutex_unlock(&dev_priv->wm.wm_mutex);
> >  
> >  	return ret;
> > @@ -568,20 +571,6 @@ static const struct intel_watermark_params pineview_cursor_hplloff_wm = {
> >  	.guard_size = PINEVIEW_CURSOR_GUARD_WM,
> >  	.cacheline_size = PINEVIEW_FIFO_LINE_SIZE,
> >  };
> > -static const struct intel_watermark_params g4x_wm_info = {
> > -	.fifo_size = G4X_FIFO_SIZE,
> > -	.max_wm = G4X_MAX_WM,
> > -	.default_wm = G4X_MAX_WM,
> > -	.guard_size = 2,
> > -	.cacheline_size = G4X_FIFO_LINE_SIZE,
> > -};
> > -static const struct intel_watermark_params g4x_cursor_wm_info = {
> > -	.fifo_size = I965_CURSOR_FIFO,
> > -	.max_wm = I965_CURSOR_MAX_WM,
> > -	.default_wm = I965_CURSOR_DFT_WM,
> > -	.guard_size = 2,
> > -	.cacheline_size = G4X_FIFO_LINE_SIZE,
> > -};
> >  static const struct intel_watermark_params i965_cursor_wm_info = {
> >  	.fifo_size = I965_CURSOR_FIFO,
> >  	.max_wm = I965_CURSOR_MAX_WM,
> > @@ -780,6 +769,16 @@ static unsigned int intel_calculate_wm(int pixel_rate,
> >  	return wm_size;
> >  }
> >  
> > +static bool is_disabling(int old, int new, int threshold)
> > +{
> > +	return old >= threshold && new < threshold;
> > +}
> > +
> > +static bool is_enabling(int old, int new, int threshold)
> > +{
> > +	return old < threshold && new >= threshold;
> > +}
> > +
> >  static int intel_wm_num_levels(struct drm_i915_private *dev_priv)
> >  {
> >  	return dev_priv->wm.max_level + 1;
> > @@ -911,138 +910,28 @@ static int g4x_tlb_miss_wa(int fifo_size, int width, int cpp)
> >  	return max(0, tlb_miss);
> >  }
> >  
> > -static bool g4x_compute_wm0(struct drm_i915_private *dev_priv,
> > -			    int plane,
> > -			    const struct intel_watermark_params *display,
> > -			    int display_latency_ns,
> > -			    const struct intel_watermark_params *cursor,
> > -			    int cursor_latency_ns,
> > -			    int *plane_wm,
> > -			    int *cursor_wm)
> > +static void g4x_write_wm_values(struct drm_i915_private *dev_priv,
> > +				const struct g4x_wm_values *wm)
> >  {
> > -	struct intel_crtc *crtc;
> > -	const struct drm_display_mode *adjusted_mode;
> > -	const struct drm_framebuffer *fb;
> > -	int htotal, plane_width, cursor_width, clock, cpp;
> > -	int entries;
> > -
> > -	crtc = intel_get_crtc_for_plane(dev_priv, plane);
> > -	if (!intel_crtc_active(crtc)) {
> > -		*cursor_wm = cursor->guard_size;
> > -		*plane_wm = display->guard_size;
> > -		return false;
> > -	}
> > -
> > -	adjusted_mode = &crtc->config->base.adjusted_mode;
> > -	fb = crtc->base.primary->state->fb;
> > -	clock = adjusted_mode->crtc_clock;
> > -	htotal = adjusted_mode->crtc_htotal;
> > -	plane_width = crtc->config->pipe_src_w;
> > -	cursor_width = crtc->base.cursor->state->crtc_w;
> > -	cpp = fb->format->cpp[0];
> > -
> > -	/* Use the small buffer method to calculate plane watermark */
> > -	entries = intel_wm_method1(clock, cpp, display_latency_ns / 100);
> > -	entries += g4x_tlb_miss_wa(display->fifo_size, plane_width, cpp);
> > -	entries = DIV_ROUND_UP(entries, display->cacheline_size);
> > -	*plane_wm = entries + display->guard_size;
> > -	if (*plane_wm > (int)display->max_wm)
> > -		*plane_wm = display->max_wm;
> > -
> > -	/* Use the large buffer method to calculate cursor watermark */
> > -	entries = intel_wm_method2(clock, htotal, cursor_width, 4,
> > -				   cursor_latency_ns / 100);
> > -	entries += g4x_tlb_miss_wa(cursor->fifo_size, cursor_width, 4);
> > -	entries = DIV_ROUND_UP(entries, cursor->cacheline_size);
> > -	*cursor_wm = entries + cursor->guard_size;
> > -	if (*cursor_wm > (int)cursor->max_wm)
> > -		*cursor_wm = (int)cursor->max_wm;
> > -
> > -	return true;
> > -}
> > -
> > -/*
> > - * Check the wm result.
> > - *
> > - * If any calculated watermark values is larger than the maximum value that
> > - * can be programmed into the associated watermark register, that watermark
> > - * must be disabled.
> > - */
> > -static bool g4x_check_srwm(struct drm_i915_private *dev_priv,
> > -			   int display_wm, int cursor_wm,
> > -			   const struct intel_watermark_params *display,
> > -			   const struct intel_watermark_params *cursor)
> > -{
> > -	DRM_DEBUG_KMS("SR watermark: display plane %d, cursor %d\n",
> > -		      display_wm, cursor_wm);
> > -
> > -	if (display_wm > display->max_wm) {
> > -		DRM_DEBUG_KMS("display watermark is too large(%d/%u), disabling\n",
> > -			      display_wm, display->max_wm);
> > -		return false;
> > -	}
> > -
> > -	if (cursor_wm > cursor->max_wm) {
> > -		DRM_DEBUG_KMS("cursor watermark is too large(%d/%u), disabling\n",
> > -			      cursor_wm, cursor->max_wm);
> > -		return false;
> > -	}
> > -
> > -	if (!(display_wm || cursor_wm)) {
> > -		DRM_DEBUG_KMS("SR latency is 0, disabling\n");
> > -		return false;
> > -	}
> > -
> > -	return true;
> > -}
> > -
> > -static bool g4x_compute_srwm(struct drm_i915_private *dev_priv,
> > -			     int plane,
> > -			     int latency_ns,
> > -			     const struct intel_watermark_params *display,
> > -			     const struct intel_watermark_params *cursor,
> > -			     int *display_wm, int *cursor_wm)
> > -{
> > -	struct intel_crtc *crtc;
> > -	const struct drm_display_mode *adjusted_mode;
> > -	const struct drm_framebuffer *fb;
> > -	int plane_width, cursor_width, htotal, cpp, clock;
> > -	int small, large;
> > -	int entries;
> > -
> > -	if (!latency_ns) {
> > -		*display_wm = *cursor_wm = 0;
> > -		return false;
> > -	}
> > -
> > -	crtc = intel_get_crtc_for_plane(dev_priv, plane);
> > -	adjusted_mode = &crtc->config->base.adjusted_mode;
> > -	fb = crtc->base.primary->state->fb;
> > -	clock = adjusted_mode->crtc_clock;
> > -	htotal = adjusted_mode->crtc_htotal;
> > -	plane_width = crtc->config->pipe_src_w;
> > -	cursor_width = crtc->base.cursor->state->crtc_w;
> > -	cpp = fb->format->cpp[0];
> > -
> > -	/* Use the minimum of the small and large buffer method for primary */
> > -	small = intel_wm_method1(clock, cpp, latency_ns / 100);
> > -	large = intel_wm_method2(clock, htotal, plane_width, cpp,
> > -				 latency_ns / 100);
> > -	entries = min(small, large);
> > -	entries += g4x_tlb_miss_wa(display->fifo_size, plane_width, cpp);
> > -	entries = DIV_ROUND_UP(entries, display->cacheline_size);
> > -	*display_wm = entries + display->guard_size;
> > -
> > -	/* calculate the self-refresh watermark for display cursor */
> > -	entries = intel_wm_method2(clock, htotal, cursor_width, 4,
> > -				   latency_ns / 100);
> > -	entries += g4x_tlb_miss_wa(cursor->fifo_size, cursor_width, 4);
> > -	entries = DIV_ROUND_UP(entries, cursor->cacheline_size);
> > -	*cursor_wm = entries + cursor->guard_size;
> > -
> > -	return g4x_check_srwm(dev_priv,
> > -			      *display_wm, *cursor_wm,
> > -			      display, cursor);
> > +	I915_WRITE(DSPFW1,
> > +		   FW_WM(wm->sr.plane, SR) |
> > +		   FW_WM(wm->pipe[PIPE_B].plane[PLANE_CURSOR], CURSORB) |
> > +		   FW_WM(wm->pipe[PIPE_B].plane[PLANE_PRIMARY], PLANEB) |
> > +		   FW_WM(wm->pipe[PIPE_A].plane[PLANE_PRIMARY], PLANEA));
> > +	I915_WRITE(DSPFW2,
> > +		   (wm->fbc_en ? DSPFW_FBC_SR_EN : 0) |
> > +		   FW_WM(wm->sr.fbc, FBC_SR) |
> > +		   FW_WM(wm->hpll.fbc, FBC_HPLL_SR) |
> > +		   FW_WM(wm->pipe[PIPE_B].plane[PLANE_SPRITE0], SPRITEB) |
> > +		   FW_WM(wm->pipe[PIPE_A].plane[PLANE_CURSOR], CURSORA) |
> > +		   FW_WM(wm->pipe[PIPE_A].plane[PLANE_SPRITE0], SPRITEA));
> > +	I915_WRITE(DSPFW3,
> > +		   (wm->hpll_en ? DSPFW_HPLL_SR_EN : 0) |
> > +		   FW_WM(wm->sr.cursor, CURSOR_SR) |
> > +		   FW_WM(wm->hpll.cursor, HPLL_CURSOR) |
> > +		   FW_WM(wm->hpll.plane, HPLL_SR));
> > +
> > +	POSTING_READ(DSPFW1);
> >  }
> >  
> >  #define FW_WM_VLV(value, plane) \
> > @@ -1126,6 +1015,523 @@ static void vlv_write_wm_values(struct drm_i915_private *dev_priv,
> >  
> >  #undef FW_WM_VLV
> >  
> > +static void g4x_setup_wm_latency(struct drm_i915_private *dev_priv)
> > +{
> > +	/* all latencies in usec */
> > +	dev_priv->wm.pri_latency[G4X_WM_LEVEL_NORMAL] = 5;
> > +	dev_priv->wm.pri_latency[G4X_WM_LEVEL_SR] = 12;
> > +
> > +	dev_priv->wm.max_level = G4X_WM_LEVEL_SR;
> > +}
> > +
> > +static int g4x_plane_fifo_size(enum plane_id plane_id, int level)
> > +{
> > +	/*
> > +	 * DSPCNTR[13] supposedly controls whether the
> > +	 * primary plane can use the FIFO space otherwise
> > +	 * reserved for the sprite plane. It's not 100% clear
> > +	 * what the actual FIFO size is, but it looks like we
> > +	 * can happily set both primary and sprite watermarks
> > +	 * up to 127 cachelines. So that would seem to mean
> > +	 * that either DSPCNTR[13] doesn't do anything, or that
> > +	 * the total FIFO is >= 256 cachelines in size. Either
> > +	 * way, we don't seem to have to worry about this
> > +	 * repartitioning as the maximum watermark value the
> > +	 * register can hold for each plane is lower than the
> > +	 * minimum FIFO size.
> > +	 */
> > +	switch (plane_id) {
> > +	case PLANE_CURSOR:
> > +		return 63;
> > +	case PLANE_PRIMARY:
> > +		return level == G4X_WM_LEVEL_NORMAL ? 127 : 511;
> > +	case PLANE_SPRITE0:
> > +		return level == G4X_WM_LEVEL_NORMAL ? 127 : 0;
> > +	default:
> > +		MISSING_CASE(plane_id);
> > +		return 0;
> > +	}
> > +}
> > +
> > +static int g4x_fbc_fifo_size(int level)
> > +{
> > +	switch (level) {
> > +	case G4X_WM_LEVEL_SR:
> > +		return 7;
> > +	case G4X_WM_LEVEL_HPLL:
> > +		return 15;
> > +	default:
> > +		MISSING_CASE(level);
> > +		return 0;
> > +	}
> > +}
> > +
> > +static uint16_t g4x_compute_wm(const struct intel_crtc_state *crtc_state,
> > +			       const struct intel_plane_state *plane_state,
> > +			       int level)
> > +{
> > +	struct intel_plane *plane = to_intel_plane(plane_state->base.plane);
> > +	struct drm_i915_private *dev_priv = to_i915(plane->base.dev);
> > +	const struct drm_display_mode *adjusted_mode =
> > +		&crtc_state->base.adjusted_mode;
> > +	int clock, htotal, cpp, width, wm;
> > +	int latency = dev_priv->wm.pri_latency[level] * 10;
> > +
> > +	if (latency == 0)
> > +		return USHRT_MAX;
> > +
> > +	if (!intel_wm_plane_visible(crtc_state, plane_state))
> > +		return 0;
> > +
> > +	/*
> > +	 * Not 100% sure which way ELK should go here as the
> > +	 * spec only says CL/CTG should assume 32bpp and BW
> > +	 * doesn't need to. But as these things followed the
> > +	 * mobile vs. desktop lines on gen3 as well, let's
> > +	 * assume ELK doesn't need this.
> > +	 *
> > +	 * The spec also fails to list such a restriction for
> > +	 * the HPLL watermark, which seems a little strange.
> > +	 * Let's use 32bpp for the HPLL watermark as well.
> > +	 */
> > +	if (IS_GM45(dev_priv) && plane->id == PLANE_PRIMARY &&
> > +	    level != G4X_WM_LEVEL_NORMAL)
> > +		cpp = 4;
> > +	else
> > +		cpp = plane_state->base.fb->format->cpp[0];
> > +
> > +	clock = adjusted_mode->crtc_clock;
> > +	htotal = adjusted_mode->crtc_htotal;
> > +
> > +	if (plane->id == PLANE_CURSOR)
> > +		width = plane_state->base.crtc_w;
> > +	else
> > +		width = drm_rect_width(&plane_state->base.dst);
> > +
> > +	if (plane->id == PLANE_CURSOR) {
> > +		wm = intel_wm_method2(clock, htotal, width, cpp, latency);
> > +	} else if (plane->id == PLANE_PRIMARY &&
> > +		   level == G4X_WM_LEVEL_NORMAL) {
> > +		wm = intel_wm_method1(clock, cpp, latency);
> > +	} else {
> > +		int small, large;
> > +
> > +		small = intel_wm_method1(clock, cpp, latency);
> > +		large = intel_wm_method2(clock, htotal, width, cpp, latency);
> > +
> > +		wm = min(small, large);
> > +	}
> > +
> > +	wm += g4x_tlb_miss_wa(g4x_plane_fifo_size(plane->id, level),
> > +			      width, cpp);
> > +
> > +	wm = DIV_ROUND_UP(wm, 64) + 2;
> > +
> > +	return min_t(int, wm, USHRT_MAX);
> > +}
> > +
> > +static bool g4x_raw_plane_wm_set(struct intel_crtc_state *crtc_state,
> > +				 int level, enum plane_id plane_id, u16 value)
> > +{
> > +	struct drm_i915_private *dev_priv = to_i915(crtc_state->base.crtc->dev);
> > +	bool dirty = false;
> > +
> > +	for (; level < intel_wm_num_levels(dev_priv); level++) {
> > +		struct g4x_pipe_wm *raw = &crtc_state->wm.g4x.raw[level];
> > +
> > +		dirty |= raw->plane[plane_id] != value;
> > +		raw->plane[plane_id] = value;
> > +	}
> > +
> > +	return dirty;
> > +}
> > +
> > +static bool g4x_raw_fbc_wm_set(struct intel_crtc_state *crtc_state,
> > +			       int level, u16 value)
> > +{
> > +	struct drm_i915_private *dev_priv = to_i915(crtc_state->base.crtc->dev);
> > +	bool dirty = false;
> > +
> > +	/* NORMAL level doesn't have an FBC watermark */
> > +	level = max(level, G4X_WM_LEVEL_SR);
> > +
> > +	for (; level < intel_wm_num_levels(dev_priv); level++) {
> > +		struct g4x_pipe_wm *raw = &crtc_state->wm.g4x.raw[level];
> > +
> > +		dirty |= raw->fbc != value;
> > +		raw->fbc = value;
> > +	}
> > +
> > +	return dirty;
> > +}
> > +
> > +static uint32_t ilk_compute_fbc_wm(const struct intel_crtc_state *cstate,
> > +				   const struct intel_plane_state *pstate,
> > +				   uint32_t pri_val);
> > +
> > +static bool g4x_raw_plane_wm_compute(struct intel_crtc_state *crtc_state,
> > +				     const struct intel_plane_state *plane_state)
> > +{
> > +	struct intel_plane *plane = to_intel_plane(plane_state->base.plane);
> > +	int num_levels = intel_wm_num_levels(to_i915(plane->base.dev));
> > +	enum plane_id plane_id = plane->id;
> > +	bool dirty = false;
> > +	int level;
> > +
> > +	if (!intel_wm_plane_visible(crtc_state, plane_state)) {
> > +		dirty |= g4x_raw_plane_wm_set(crtc_state, 0, plane_id, 0);
> > +		if (plane_id == PLANE_PRIMARY)
> > +			dirty |= g4x_raw_fbc_wm_set(crtc_state, 0, 0);
> > +		goto out;
> > +	}
> > +
> > +	for (level = 0; level < num_levels; level++) {
> > +		struct g4x_pipe_wm *raw = &crtc_state->wm.g4x.raw[level];
> > +		int wm, max_wm;
> > +
> > +		wm = g4x_compute_wm(crtc_state, plane_state, level);
> > +		max_wm = g4x_plane_fifo_size(plane_id, level);
> > +
> > +		if (wm > max_wm)
> > +			break;
> > +
> > +		dirty |= raw->plane[plane_id] != wm;
> > +		raw->plane[plane_id] = wm;
> > +
> > +		if (plane_id != PLANE_PRIMARY ||
> > +		    level == G4X_WM_LEVEL_NORMAL)
> > +			continue;
> > +
> > +		wm = ilk_compute_fbc_wm(crtc_state, plane_state,
> > +					raw->plane[plane_id]);
> > +		max_wm = g4x_fbc_fifo_size(level);
> > +
> > +		/*
> > +		 * FBC wm is not mandatory as we
> > +		 * can always just disable its use.
> > +		 */
> > +		if (wm > max_wm)
> > +			wm = USHRT_MAX;
> > +
> > +		dirty |= raw->fbc != wm;
> > +		raw->fbc = wm;
> > +	}
> > +
> > +	/* mark watermarks as invalid */
> > +	dirty |= g4x_raw_plane_wm_set(crtc_state, level, plane_id, USHRT_MAX);
> > +
> > +	if (plane_id == PLANE_PRIMARY)
> > +		dirty |= g4x_raw_fbc_wm_set(crtc_state, level, USHRT_MAX);
> > +
> > + out:
> > +	if (dirty) {
> > +		DRM_DEBUG_KMS("%s watermarks: normal=%d, SR=%d, HPLL=%d\n",
> > +			      plane->base.name,
> > +			      crtc_state->wm.g4x.raw[G4X_WM_LEVEL_NORMAL].plane[plane_id],
> > +			      crtc_state->wm.g4x.raw[G4X_WM_LEVEL_SR].plane[plane_id],
> > +			      crtc_state->wm.g4x.raw[G4X_WM_LEVEL_HPLL].plane[plane_id]);
> > +
> > +		if (plane_id == PLANE_PRIMARY)
> > +			DRM_DEBUG_KMS("FBC watermarks: SR=%d, HPLL=%d\n",
> > +				      crtc_state->wm.g4x.raw[G4X_WM_LEVEL_SR].fbc,
> > +				      crtc_state->wm.g4x.raw[G4X_WM_LEVEL_HPLL].fbc);
> > +	}
> > +
> > +	return dirty;
> > +}
> > +
> > +static bool g4x_raw_plane_wm_is_valid(const struct intel_crtc_state *crtc_state,
> > +				      enum plane_id plane_id, int level)
> > +{
> > +	const struct g4x_pipe_wm *raw = &crtc_state->wm.g4x.raw[level];
> > +
> > +	return raw->plane[plane_id] <= g4x_plane_fifo_size(plane_id, level);
> > +}
> > +
> > +static bool g4x_raw_crtc_wm_is_valid(const struct intel_crtc_state *crtc_state,
> > +				     int level)
> > +{
> > +	struct drm_i915_private *dev_priv = to_i915(crtc_state->base.crtc->dev);
> > +
> > +	if (level > dev_priv->wm.max_level)
> > +		return false;
> > +
> > +	return g4x_raw_plane_wm_is_valid(crtc_state, PLANE_PRIMARY, level) &&
> > +		g4x_raw_plane_wm_is_valid(crtc_state, PLANE_SPRITE0, level) &&
> > +		g4x_raw_plane_wm_is_valid(crtc_state, PLANE_CURSOR, level);
> > +}
> > +
> > +/* mark all levels starting from 'level' as invalid */
> > +static void g4x_invalidate_wms(struct intel_crtc *crtc,
> > +			       struct g4x_wm_state *wm_state, int level)
> > +{
> > +	if (level <= G4X_WM_LEVEL_NORMAL) {
> > +		enum plane_id plane_id;
> > +
> > +		for_each_plane_id_on_crtc(crtc, plane_id)
> > +			wm_state->wm.plane[plane_id] = USHRT_MAX;
> > +	}
> > +
> > +	if (level <= G4X_WM_LEVEL_SR) {
> > +		wm_state->cxsr = false;
> > +		wm_state->sr.cursor = USHRT_MAX;
> > +		wm_state->sr.plane = USHRT_MAX;
> > +		wm_state->sr.fbc = USHRT_MAX;
> > +	}
> > +
> > +	if (level <= G4X_WM_LEVEL_HPLL) {
> > +		wm_state->hpll_en = false;
> > +		wm_state->hpll.cursor = USHRT_MAX;
> > +		wm_state->hpll.plane = USHRT_MAX;
> > +		wm_state->hpll.fbc = USHRT_MAX;
> > +	}
> > +}
> > +
> > +static int g4x_compute_pipe_wm(struct intel_crtc_state *crtc_state)
> > +{
> > +	struct intel_crtc *crtc = to_intel_crtc(crtc_state->base.crtc);
> > +	struct intel_atomic_state *state =
> > +		to_intel_atomic_state(crtc_state->base.state);
> > +	struct g4x_wm_state *wm_state = &crtc_state->wm.g4x.optimal;
> > +	int num_active_planes = hweight32(crtc_state->active_planes &
> > +					  ~BIT(PLANE_CURSOR));
> > +	const struct g4x_pipe_wm *raw;
> > +	struct intel_plane_state *plane_state;
> > +	struct intel_plane *plane;
> > +	enum plane_id plane_id;
> > +	int i, level;
> > +	unsigned int dirty = 0;
> > +
> > +	for_each_intel_plane_in_state(state, plane, plane_state, i) {
> > +		const struct intel_plane_state *old_plane_state =
> > +			to_intel_plane_state(plane->base.state);
> > +
> > +		if (plane_state->base.crtc != &crtc->base &&
> > +		    old_plane_state->base.crtc != &crtc->base)
> > +			continue;
> > +
> > +		if (g4x_raw_plane_wm_compute(crtc_state, plane_state))
> > +			dirty |= BIT(plane->id);
> > +	}
> > +
> > +	if (!dirty)
> > +		return 0;
> > +
> > +	level = G4X_WM_LEVEL_NORMAL;
> > +	if (!g4x_raw_crtc_wm_is_valid(crtc_state, level))
> > +		goto out;
> > +
> > +	raw = &crtc_state->wm.g4x.raw[level];
> > +	for_each_plane_id_on_crtc(crtc, plane_id)
> > +		wm_state->wm.plane[plane_id] = raw->plane[plane_id];
> > +
> > +	level = G4X_WM_LEVEL_SR;
> > +
> > +	if (!g4x_raw_crtc_wm_is_valid(crtc_state, level))
> > +		goto out;
> > +
> > +	raw = &crtc_state->wm.g4x.raw[level];
> > +	wm_state->sr.plane = raw->plane[PLANE_PRIMARY];
> > +	wm_state->sr.cursor = raw->plane[PLANE_CURSOR];
> > +	wm_state->sr.fbc = raw->fbc;
> > +
> > +	wm_state->cxsr = num_active_planes == BIT(PLANE_PRIMARY);
> > +
> > +	level = G4X_WM_LEVEL_HPLL;
> > +
> > +	if (!g4x_raw_crtc_wm_is_valid(crtc_state, level))
> > +		goto out;
> > +
> > +	raw = &crtc_state->wm.g4x.raw[level];
> > +	wm_state->hpll.plane = raw->plane[PLANE_PRIMARY];
> > +	wm_state->hpll.cursor = raw->plane[PLANE_CURSOR];
> > +	wm_state->hpll.fbc = raw->fbc;
> > +
> > +	wm_state->hpll_en = wm_state->cxsr;
> > +
> > +	level++;
> > +
> > + out:
> > +	if (level == G4X_WM_LEVEL_NORMAL)
> > +		return -EINVAL;
> > +
> > +	/* invalidate the higher levels */
> > +	g4x_invalidate_wms(crtc, wm_state, level);
> > +
> > +	/*
> > +	 * Determine if the FBC watermark(s) can be used. IF
> > +	 * this isn't the case we prefer to disable the FBC
> > +	 ( watermark(s) rather than disable the SR/HPLL
> > +	 * level(s) entirely.
> > +	 */
> > +	wm_state->fbc_en = level > G4X_WM_LEVEL_NORMAL;
> > +
> > +	if (level >= G4X_WM_LEVEL_SR &&
> > +	    wm_state->sr.fbc > g4x_fbc_fifo_size(G4X_WM_LEVEL_SR))
> > +		wm_state->fbc_en = false;
> > +	else if (level >= G4X_WM_LEVEL_HPLL &&
> > +		 wm_state->hpll.fbc > g4x_fbc_fifo_size(G4X_WM_LEVEL_HPLL))
> > +		wm_state->fbc_en = false;
> > +
> > +	return 0;
> > +}
> > +
> > +static int g4x_compute_intermediate_wm(struct drm_device *dev,
> > +				       struct intel_crtc *crtc,
> > +				       struct intel_crtc_state *crtc_state)
> > +{
> > +	struct g4x_wm_state *intermediate = &crtc_state->wm.g4x.intermediate;
> > +	const struct g4x_wm_state *optimal = &crtc_state->wm.g4x.optimal;
> > +	const struct g4x_wm_state *active = &crtc->wm.active.g4x;
> > +	enum plane_id plane_id;
> 
> I would add a provision for modeset, no need to calculate intermediate watermarks with the pipe enabled, something like this?
> 
> 	if (!newstate->base.active || drm_atomic_crtc_needs_modeset(&newstate->base)) {
> 		*intermediate = *optimal;
> 		return 0;
> 	}

Hmm. Yeah, I suppose we should be doing somethig like that. Though I'd
still much more prefer if we had the crtc disable as a totally separate
atomic commit.

> 
> The active watermarks are wrongly assigned here, if there's a pending update it could point to old old optimal state, or old intermediate state, it should be something like this:
> old_crtc_state = drm_atomic_get_old_crtc_state(crtc_state->base.state, &crtc->base);
> active = &to_intel_crtc_state(old_crtc_state)->wm.g4x.optimal

Well, it should really be considering both active+new_optimal and
old_optimal+new_optimal. That's assuming we eventually want to allow
the fps>vrefresh thing. That's what I had in my original ILK code BTW,
just no one bothered to bring it over when the current code was merged.

But yeah, with the way things are currently I suppose we should be
only considering the old_optimal+new_optimal case.

> 
> I know, vlv does this wrong too, should be fixed as well..
> With that fixed all patches look good to me, so feel free to put this on them:
> 
> Reviewed-by: Maarten Lankhorst <maarten.lankhorst@linux.intel.com>
> 
> > +	intermediate->cxsr = optimal->cxsr && active->cxsr &&
> > +		!crtc_state->disable_cxsr;
> > +	intermediate->hpll_en = optimal->hpll_en && active->hpll_en &&
> > +		!crtc_state->disable_cxsr;
> > +	intermediate->fbc_en = optimal->fbc_en && active->fbc_en;
> > +
> > +	for_each_plane_id_on_crtc(crtc, plane_id) {
> > +		intermediate->wm.plane[plane_id] =
> > +			max(optimal->wm.plane[plane_id],
> > +			    active->wm.plane[plane_id]);
> > +
> > +		WARN_ON(intermediate->wm.plane[plane_id] >
> > +			g4x_plane_fifo_size(plane_id, G4X_WM_LEVEL_NORMAL));
> > +	}
> > +
> > +	intermediate->sr.plane = max(optimal->sr.plane,
> > +				     active->sr.plane);
> > +	intermediate->sr.cursor = max(optimal->sr.cursor,
> > +				      active->sr.cursor);
> > +	intermediate->sr.fbc = max(optimal->sr.fbc,
> > +				   active->sr.fbc);
> > +
> > +	intermediate->hpll.plane = max(optimal->hpll.plane,
> > +				       active->hpll.plane);
> > +	intermediate->hpll.cursor = max(optimal->hpll.cursor,
> > +					active->hpll.cursor);
> > +	intermediate->hpll.fbc = max(optimal->hpll.fbc,
> > +				     active->hpll.fbc);
> > +
> > +	WARN_ON((intermediate->sr.plane >
> > +		 g4x_plane_fifo_size(PLANE_PRIMARY, G4X_WM_LEVEL_SR) ||
> > +		 intermediate->sr.cursor >
> > +		 g4x_plane_fifo_size(PLANE_CURSOR, G4X_WM_LEVEL_SR)) &&
> > +		intermediate->cxsr);
> > +	WARN_ON((intermediate->sr.plane >
> > +		 g4x_plane_fifo_size(PLANE_PRIMARY, G4X_WM_LEVEL_HPLL) ||
> > +		 intermediate->sr.cursor >
> > +		 g4x_plane_fifo_size(PLANE_CURSOR, G4X_WM_LEVEL_HPLL)) &&
> > +		intermediate->hpll_en);
> > +
> > +	WARN_ON(intermediate->sr.fbc > g4x_fbc_fifo_size(1) &&
> > +		intermediate->fbc_en && intermediate->cxsr);
> > +	WARN_ON(intermediate->hpll.fbc > g4x_fbc_fifo_size(2) &&
> > +		intermediate->fbc_en && intermediate->hpll_en);
> > +
> > +	/*
> > +	 * If our intermediate WM are identical to the final WM, then we can
> > +	 * omit the post-vblank programming; only update if it's different.
> > +	 */
> > +	if (memcmp(intermediate, optimal, sizeof(*intermediate)) != 0)
> > +		crtc_state->wm.need_postvbl_update = true;
> > +
> > +	return 0;
> > +}
> > +
> > +static void g4x_merge_wm(struct drm_i915_private *dev_priv,
> > +			 struct g4x_wm_values *wm)
> > +{
> > +	struct intel_crtc *crtc;
> > +	int num_active_crtcs = 0;
> > +
> > +	wm->cxsr = true;
> > +	wm->hpll_en = true;
> > +	wm->fbc_en = true;
> > +
> > +	for_each_intel_crtc(&dev_priv->drm, crtc) {
> > +		const struct g4x_wm_state *wm_state = &crtc->wm.active.g4x;
> > +
> > +		if (!crtc->active)
> > +			continue;
> > +
> > +		if (!wm_state->cxsr)
> > +			wm->cxsr = false;
> > +		if (!wm_state->hpll_en)
> > +			wm->hpll_en = false;
> > +		if (!wm_state->fbc_en)
> > +			wm->fbc_en = false;
> > +
> > +		num_active_crtcs++;
> > +	}
> > +
> > +	if (num_active_crtcs != 1) {
> > +		wm->cxsr = false;
> > +		wm->hpll_en = false;
> > +		wm->fbc_en = false;
> > +	}
> > +
> > +	for_each_intel_crtc(&dev_priv->drm, crtc) {
> > +		const struct g4x_wm_state *wm_state = &crtc->wm.active.g4x;
> > +		enum pipe pipe = crtc->pipe;
> > +
> > +		wm->pipe[pipe] = wm_state->wm;
> > +		if (crtc->active && wm->cxsr)
> > +			wm->sr = wm_state->sr;
> > +		if (crtc->active && wm->hpll_en)
> > +			wm->hpll = wm_state->hpll;
> > +	}
> > +}
> > +
> > +static void g4x_program_watermarks(struct drm_i915_private *dev_priv)
> > +{
> > +	struct g4x_wm_values *old_wm = &dev_priv->wm.g4x;
> > +	struct g4x_wm_values new_wm = {};
> > +
> > +	g4x_merge_wm(dev_priv, &new_wm);
> > +
> > +	if (memcmp(old_wm, &new_wm, sizeof(new_wm)) == 0)
> > +		return;
> > +
> > +	if (is_disabling(old_wm->cxsr, new_wm.cxsr, true))
> > +		_intel_set_memory_cxsr(dev_priv, false);
> > +
> > +	g4x_write_wm_values(dev_priv, &new_wm);
> > +
> > +	if (is_enabling(old_wm->cxsr, new_wm.cxsr, true))
> > +		_intel_set_memory_cxsr(dev_priv, true);
> > +
> > +	*old_wm = new_wm;
> > +}
> > +
> > +static void g4x_initial_watermarks(struct intel_atomic_state *state,
> > +				   struct intel_crtc_state *crtc_state)
> > +{
> > +	struct drm_i915_private *dev_priv = to_i915(crtc_state->base.crtc->dev);
> > +	struct intel_crtc *crtc = to_intel_crtc(crtc_state->base.crtc);
> > +
> > +	mutex_lock(&dev_priv->wm.wm_mutex);
> > +	crtc->wm.active.g4x = crtc_state->wm.g4x.intermediate;
> > +	g4x_program_watermarks(dev_priv);
> > +	mutex_unlock(&dev_priv->wm.wm_mutex);
> > +}
> > +
> > +static void g4x_optimize_watermarks(struct intel_atomic_state *state,
> > +				    struct intel_crtc_state *crtc_state)
> > +{
> > +	struct drm_i915_private *dev_priv = to_i915(crtc_state->base.crtc->dev);
> > +	struct intel_crtc *intel_crtc = to_intel_crtc(crtc_state->base.crtc);
> > +
> > +	if (!crtc_state->wm.need_postvbl_update)
> > +		return;
> > +
> > +	mutex_lock(&dev_priv->wm.wm_mutex);
> > +	intel_crtc->wm.active.g4x = crtc_state->wm.g4x.optimal;
> > +	g4x_program_watermarks(dev_priv);
> > +	mutex_unlock(&dev_priv->wm.wm_mutex);
> > +}
> > +
> >  /* latency must be in 0.1us units. */
> >  static unsigned int vlv_wm_method2(unsigned int pixel_rate,
> >  				   unsigned int htotal,
> > @@ -1673,16 +2079,6 @@ static void vlv_merge_wm(struct drm_i915_private *dev_priv,
> >  	}
> >  }
> >  
> > -static bool is_disabling(int old, int new, int threshold)
> > -{
> > -	return old >= threshold && new < threshold;
> > -}
> > -
> > -static bool is_enabling(int old, int new, int threshold)
> > -{
> > -	return old < threshold && new >= threshold;
> > -}
> > -
> >  static void vlv_program_watermarks(struct drm_i915_private *dev_priv)
> >  {
> >  	struct vlv_wm_values *old_wm = &dev_priv->wm.vlv;
> > @@ -1743,65 +2139,6 @@ static void vlv_optimize_watermarks(struct intel_atomic_state *state,
> >  	mutex_unlock(&dev_priv->wm.wm_mutex);
> >  }
> >  
> > -#define single_plane_enabled(mask) is_power_of_2(mask)
> > -
> > -static void g4x_update_wm(struct intel_crtc *crtc)
> > -{
> > -	struct drm_i915_private *dev_priv = to_i915(crtc->base.dev);
> > -	static const int sr_latency_ns = 12000;
> > -	int planea_wm, planeb_wm, cursora_wm, cursorb_wm;
> > -	int plane_sr, cursor_sr;
> > -	unsigned int enabled = 0;
> > -	bool cxsr_enabled;
> > -
> > -	if (g4x_compute_wm0(dev_priv, PIPE_A,
> > -			    &g4x_wm_info, pessimal_latency_ns,
> > -			    &g4x_cursor_wm_info, pessimal_latency_ns,
> > -			    &planea_wm, &cursora_wm))
> > -		enabled |= 1 << PIPE_A;
> > -
> > -	if (g4x_compute_wm0(dev_priv, PIPE_B,
> > -			    &g4x_wm_info, pessimal_latency_ns,
> > -			    &g4x_cursor_wm_info, pessimal_latency_ns,
> > -			    &planeb_wm, &cursorb_wm))
> > -		enabled |= 1 << PIPE_B;
> > -
> > -	if (single_plane_enabled(enabled) &&
> > -	    g4x_compute_srwm(dev_priv, ffs(enabled) - 1,
> > -			     sr_latency_ns,
> > -			     &g4x_wm_info,
> > -			     &g4x_cursor_wm_info,
> > -			     &plane_sr, &cursor_sr)) {
> > -		cxsr_enabled = true;
> > -	} else {
> > -		cxsr_enabled = false;
> > -		intel_set_memory_cxsr(dev_priv, false);
> > -		plane_sr = cursor_sr = 0;
> > -	}
> > -
> > -	DRM_DEBUG_KMS("Setting FIFO watermarks - A: plane=%d, cursor=%d, "
> > -		      "B: plane=%d, cursor=%d, SR: plane=%d, cursor=%d\n",
> > -		      planea_wm, cursora_wm,
> > -		      planeb_wm, cursorb_wm,
> > -		      plane_sr, cursor_sr);
> > -
> > -	I915_WRITE(DSPFW1,
> > -		   FW_WM(plane_sr, SR) |
> > -		   FW_WM(cursorb_wm, CURSORB) |
> > -		   FW_WM(planeb_wm, PLANEB) |
> > -		   FW_WM(planea_wm, PLANEA));
> > -	I915_WRITE(DSPFW2,
> > -		   (I915_READ(DSPFW2) & ~DSPFW_CURSORA_MASK) |
> > -		   FW_WM(cursora_wm, CURSORA));
> > -	/* HPLL off in SR has some issues on G4x... disable it */
> > -	I915_WRITE(DSPFW3,
> > -		   (I915_READ(DSPFW3) & ~(DSPFW_HPLL_SR_EN | DSPFW_CURSOR_SR_MASK)) |
> > -		   FW_WM(cursor_sr, CURSOR_SR));
> > -
> > -	if (cxsr_enabled)
> > -		intel_set_memory_cxsr(dev_priv, true);
> > -}
> > -
> >  static void i965_update_wm(struct intel_crtc *unused_crtc)
> >  {
> >  	struct drm_i915_private *dev_priv = to_i915(unused_crtc->base.dev);
> > @@ -4778,6 +5115,32 @@ static void ilk_pipe_wm_get_hw_state(struct drm_crtc *crtc)
> >  #define _FW_WM_VLV(value, plane) \
> >  	(((value) & DSPFW_ ## plane ## _MASK_VLV) >> DSPFW_ ## plane ## _SHIFT)
> >  
> > +static void g4x_read_wm_values(struct drm_i915_private *dev_priv,
> > +			       struct g4x_wm_values *wm)
> > +{
> > +	uint32_t tmp;
> > +
> > +	tmp = I915_READ(DSPFW1);
> > +	wm->sr.plane = _FW_WM(tmp, SR);
> > +	wm->pipe[PIPE_B].plane[PLANE_CURSOR] = _FW_WM(tmp, CURSORB);
> > +	wm->pipe[PIPE_B].plane[PLANE_PRIMARY] = _FW_WM(tmp, PLANEB);
> > +	wm->pipe[PIPE_A].plane[PLANE_PRIMARY] = _FW_WM(tmp, PLANEA);
> > +
> > +	tmp = I915_READ(DSPFW2);
> > +	wm->fbc_en = tmp & DSPFW_FBC_SR_EN;
> > +	wm->sr.fbc = _FW_WM(tmp, FBC_SR);
> > +	wm->hpll.fbc = _FW_WM(tmp, FBC_HPLL_SR);
> > +	wm->pipe[PIPE_B].plane[PLANE_SPRITE0] = _FW_WM(tmp, SPRITEB);
> > +	wm->pipe[PIPE_A].plane[PLANE_CURSOR] = _FW_WM(tmp, CURSORA);
> > +	wm->pipe[PIPE_A].plane[PLANE_SPRITE0] = _FW_WM(tmp, SPRITEA);
> > +
> > +	tmp = I915_READ(DSPFW3);
> > +	wm->hpll_en = tmp & DSPFW_HPLL_SR_EN;
> > +	wm->sr.cursor = _FW_WM(tmp, CURSOR_SR);
> > +	wm->hpll.cursor = _FW_WM(tmp, HPLL_CURSOR);
> > +	wm->hpll.plane = _FW_WM(tmp, HPLL_SR);
> > +}
> > +
> >  static void vlv_read_wm_values(struct drm_i915_private *dev_priv,
> >  			       struct vlv_wm_values *wm)
> >  {
> > @@ -4854,6 +5217,147 @@ static void vlv_read_wm_values(struct drm_i915_private *dev_priv,
> >  #undef _FW_WM
> >  #undef _FW_WM_VLV
> >  
> > +void g4x_wm_get_hw_state(struct drm_device *dev)
> > +{
> > +	struct drm_i915_private *dev_priv = to_i915(dev);
> > +	struct g4x_wm_values *wm = &dev_priv->wm.g4x;
> > +	struct intel_crtc *crtc;
> > +
> > +	g4x_read_wm_values(dev_priv, wm);
> > +
> > +	wm->cxsr = I915_READ(FW_BLC_SELF) & FW_BLC_SELF_EN;
> > +
> > +	for_each_intel_crtc(dev, crtc) {
> > +		struct intel_crtc_state *crtc_state =
> > +			to_intel_crtc_state(crtc->base.state);
> > +		struct g4x_wm_state *active = &crtc->wm.active.g4x;
> > +		struct g4x_pipe_wm *raw;
> > +		enum pipe pipe = crtc->pipe;
> > +		enum plane_id plane_id;
> > +		int level, max_level;
> > +
> > +		active->cxsr = wm->cxsr;
> > +		active->hpll_en = wm->hpll_en;
> > +		active->fbc_en = wm->fbc_en;
> > +
> > +		active->sr = wm->sr;
> > +		active->hpll = wm->hpll;
> > +
> > +		for_each_plane_id_on_crtc(crtc, plane_id) {
> > +			active->wm.plane[plane_id] =
> > +				wm->pipe[pipe].plane[plane_id];
> > +		}
> > +
> > +		if (wm->cxsr && wm->hpll_en)
> > +			max_level = G4X_WM_LEVEL_HPLL;
> > +		else if (wm->cxsr)
> > +			max_level = G4X_WM_LEVEL_SR;
> > +		else
> > +			max_level = G4X_WM_LEVEL_NORMAL;
> > +
> > +		level = G4X_WM_LEVEL_NORMAL;
> > +		raw = &crtc_state->wm.g4x.raw[level];
> > +		for_each_plane_id_on_crtc(crtc, plane_id)
> > +			raw->plane[plane_id] = active->wm.plane[plane_id];
> > +
> > +		if (++level > max_level)
> > +			goto out;
> > +
> > +		raw = &crtc_state->wm.g4x.raw[level];
> > +		raw->plane[PLANE_PRIMARY] = active->sr.plane;
> > +		raw->plane[PLANE_CURSOR] = active->sr.cursor;
> > +		raw->plane[PLANE_SPRITE0] = 0;
> > +		raw->fbc = active->sr.fbc;
> > +
> > +		if (++level > max_level)
> > +			goto out;
> > +
> > +		raw = &crtc_state->wm.g4x.raw[level];
> > +		raw->plane[PLANE_PRIMARY] = active->hpll.plane;
> > +		raw->plane[PLANE_CURSOR] = active->hpll.cursor;
> > +		raw->plane[PLANE_SPRITE0] = 0;
> > +		raw->fbc = active->hpll.fbc;
> > +
> > +	out:
> > +		for_each_plane_id_on_crtc(crtc, plane_id)
> > +			g4x_raw_plane_wm_set(crtc_state, level,
> > +					     plane_id, USHRT_MAX);
> > +		g4x_raw_fbc_wm_set(crtc_state, level, USHRT_MAX);
> > +
> > +		crtc_state->wm.g4x.optimal = *active;
> > +		crtc_state->wm.g4x.intermediate = *active;
> > +
> > +		DRM_DEBUG_KMS("Initial watermarks: pipe %c, plane=%d, cursor=%d, sprite=%d\n",
> > +			      pipe_name(pipe),
> > +			      wm->pipe[pipe].plane[PLANE_PRIMARY],
> > +			      wm->pipe[pipe].plane[PLANE_CURSOR],
> > +			      wm->pipe[pipe].plane[PLANE_SPRITE0]);
> > +	}
> > +
> > +	DRM_DEBUG_KMS("Initial SR watermarks: plane=%d, cursor=%d fbc=%d\n",
> > +		      wm->sr.plane, wm->sr.cursor, wm->sr.fbc);
> > +	DRM_DEBUG_KMS("Initial HPLL watermarks: plane=%d, SR cursor=%d fbc=%d\n",
> > +		      wm->hpll.plane, wm->hpll.cursor, wm->hpll.fbc);
> > +	DRM_DEBUG_KMS("Initial SR=%s HPLL=%s FBC=%s\n",
> > +		      yesno(wm->cxsr), yesno(wm->hpll_en), yesno(wm->fbc_en));
> > +}
> > +
> > +void g4x_wm_sanitize(struct drm_i915_private *dev_priv)
> > +{
> > +	struct intel_plane *plane;
> > +	struct intel_crtc *crtc;
> > +
> > +	mutex_lock(&dev_priv->wm.wm_mutex);
> > +
> > +	for_each_intel_plane(&dev_priv->drm, plane) {
> > +		struct intel_crtc *crtc =
> > +			intel_get_crtc_for_pipe(dev_priv, plane->pipe);
> > +		struct intel_crtc_state *crtc_state =
> > +			to_intel_crtc_state(crtc->base.state);
> > +		struct intel_plane_state *plane_state =
> > +			to_intel_plane_state(plane->base.state);
> > +		struct g4x_wm_state *wm_state = &crtc_state->wm.g4x.optimal;
> > +		enum plane_id plane_id = plane->id;
> > +		int level;
> > +
> > +		if (plane_state->base.visible)
> > +			continue;
> > +
> > +		for (level = 0; level < 3; level++) {
> > +			struct g4x_pipe_wm *raw =
> > +				&crtc_state->wm.g4x.raw[level];
> > +
> > +			raw->plane[plane_id] = 0;
> > +			wm_state->wm.plane[plane_id] = 0;
> > +		}
> > +
> > +		if (plane_id == PLANE_PRIMARY) {
> > +			for (level = 0; level < 3; level++) {
> > +				struct g4x_pipe_wm *raw =
> > +					&crtc_state->wm.g4x.raw[level];
> > +				raw->fbc = 0;
> > +			}
> > +
> > +			wm_state->sr.fbc = 0;
> > +			wm_state->hpll.fbc = 0;
> > +			wm_state->fbc_en = false;
> > +		}
> > +	}
> > +
> > +	for_each_intel_crtc(&dev_priv->drm, crtc) {
> > +		struct intel_crtc_state *crtc_state =
> > +			to_intel_crtc_state(crtc->base.state);
> > +
> > +		crtc_state->wm.g4x.intermediate =
> > +			crtc_state->wm.g4x.optimal;
> > +		crtc->wm.active.g4x = crtc_state->wm.g4x.optimal;
> > +	}
> > +
> > +	g4x_program_watermarks(dev_priv);
> > +
> > +	mutex_unlock(&dev_priv->wm.wm_mutex);
> > +}
> > +
> >  void vlv_wm_get_hw_state(struct drm_device *dev)
> >  {
> >  	struct drm_i915_private *dev_priv = to_i915(dev);
> > @@ -8160,6 +8664,12 @@ void intel_init_pm(struct drm_i915_private *dev_priv)
> >  		dev_priv->display.initial_watermarks = vlv_initial_watermarks;
> >  		dev_priv->display.optimize_watermarks = vlv_optimize_watermarks;
> >  		dev_priv->display.atomic_update_watermarks = vlv_atomic_update_fifo;
> > +	} else if (IS_G4X(dev_priv)) {
> > +		g4x_setup_wm_latency(dev_priv);
> > +		dev_priv->display.compute_pipe_wm = g4x_compute_pipe_wm;
> > +		dev_priv->display.compute_intermediate_wm = g4x_compute_intermediate_wm;
> > +		dev_priv->display.initial_watermarks = g4x_initial_watermarks;
> > +		dev_priv->display.optimize_watermarks = g4x_optimize_watermarks;
> >  	} else if (IS_PINEVIEW(dev_priv)) {
> >  		if (!intel_get_cxsr_latency(IS_PINEVIEW_G(dev_priv),
> >  					    dev_priv->is_ddr3,
> > @@ -8175,8 +8685,6 @@ void intel_init_pm(struct drm_i915_private *dev_priv)
> >  			dev_priv->display.update_wm = NULL;
> >  		} else
> >  			dev_priv->display.update_wm = pineview_update_wm;
> > -	} else if (IS_G4X(dev_priv)) {
> > -		dev_priv->display.update_wm = g4x_update_wm;
> >  	} else if (IS_GEN4(dev_priv)) {
> >  		dev_priv->display.update_wm = i965_update_wm;
> >  	} else if (IS_GEN3(dev_priv)) {
> 

-- 
Ville Syrjälä
Intel OTC
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH 00/15] drm/i915: Two stage watermarks for g4x
  2017-04-21 18:14 [PATCH 00/15] drm/i915: Two stage watermarks for g4x ville.syrjala
                   ` (15 preceding siblings ...)
  2017-04-21 18:36 ` ✓ Fi.CI.BAT: success for drm/i915: Two stage watermarks for g4x Patchwork
@ 2017-04-24 14:20 ` Lofstedt, Marta
  16 siblings, 0 replies; 21+ messages in thread
From: Lofstedt, Marta @ 2017-04-24 14:20 UTC (permalink / raw)
  To: ville.syrjala, intel-gfx

Thanks Ville,

I have verified that below patch-set fix the CI regression on the Core2 duo, see bug: https://bugs.freedesktop.org/show_bug.cgi?id=100548

So, if/when this patch-set lands, I assume we could revert the revert of the "sched/clock: Fix broken stable to unstable transfer": git@841c8c9  

/Marta 

> -----Original Message-----
> From: Intel-gfx [mailto:intel-gfx-bounces@lists.freedesktop.org] On Behalf
> Of ville.syrjala@linux.intel.com
> Sent: Friday, April 21, 2017 9:14 PM
> To: intel-gfx@lists.freedesktop.org
> Subject: [Intel-gfx] [PATCH 00/15] drm/i915: Two stage watermarks for g4x
> 
> From: Ville Syrjälä <ville.syrjala@linux.intel.com>
> 
> This makes g4x follow the two stage watermark programming approach as
> well, and as a bonus exposes the video sprites on g4x.
> 
> There is one slight problem with merging the wms from multiple pipes; If one
> pipe is currently enabled and we're about to enabled another one, we
> should turn off CxSR before the second pipe gets enabled as the FIFO will get
> repartitioned. This could also happen when there's a parallel watermark
> update on the first pipe, so a dumb approach of just disabling CxSR in the
> modeset path doesn't really work. I think the proper fix will involve some
> shuffling of code in the modeset path because it's currently a bit of a mess.
> So with the current code there could be an occasional underrun reported
> when enabling the second pipe.
> 
> Entire series available here:
> git://github.com/vsyrjala/linux.git g4x_atomic_wm_8
> 
> Ville Syrjälä (15):
>   drm/i915: s/vlv_plane_wm_compute/vlv_raw_plane_wm_compute/ etc.
>   drm/i915: Drop the debug message from vlv_get_fifo_size()
>   drm/i915: s/vlv_num_wm_levels/intel_wm_num_levels/
>   drm/i915: Rename bunch of vlv_ watermark structures to g4x_
>   drm/i915: Make vlv/chv watermark debug print less cryptic
>   drm/i915: Document CxSR
>   drm/i915: Fix cursor 'cpp' in watermark calculatins for old platforms
>   drm/i915: Fix the g4x watermark TLB miss workaround
>   drm/i915: Refactor the g4x TLB miss w/a to a helper
>   drm/i915: Refactor wm calculations
>   drm/i915: Apply the g4x TLB miss w/a to SR watermarks as well
>   drm/i915: Two stage watermarks for g4x
>   drm/i915: Enable HPLL watermarks on g4x
>   drm/i915: Add g4x watermark tracepoint
>   drm/i915: Add support for sprites on g4x
> 
>  drivers/gpu/drm/i915/i915_debugfs.c      |   12 +-
>  drivers/gpu/drm/i915/i915_drv.h          |   20 +-
>  drivers/gpu/drm/i915/i915_trace.h        |   49 ++
>  drivers/gpu/drm/i915/intel_device_info.c |    2 +-
>  drivers/gpu/drm/i915/intel_display.c     |   29 +-
>  drivers/gpu/drm/i915/intel_drv.h         |   34 +-
>  drivers/gpu/drm/i915/intel_pm.c          | 1254 ++++++++++++++++++++++---
> -----
>  drivers/gpu/drm/i915/intel_sprite.c      |   18 +-
>  8 files changed, 1081 insertions(+), 337 deletions(-)
> 
> --
> 2.10.2
> 
> _______________________________________________
> Intel-gfx mailing list
> Intel-gfx@lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/intel-gfx
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH 12/15] drm/i915: Two stage watermarks for g4x
  2017-04-24  7:34   ` Maarten Lankhorst
  2017-04-24 13:16     ` Ville Syrjälä
@ 2017-05-10 16:40     ` Ville Syrjälä
  1 sibling, 0 replies; 21+ messages in thread
From: Ville Syrjälä @ 2017-05-10 16:40 UTC (permalink / raw)
  To: Maarten Lankhorst; +Cc: intel-gfx

On Mon, Apr 24, 2017 at 09:34:42AM +0200, Maarten Lankhorst wrote:
> On 21-04-17 20:14, ville.syrjala@linux.intel.com wrote:
> > From: Ville Syrjälä <ville.syrjala@linux.intel.com>
> >
> > Implement proper two stage watermark programming for g4x. As with
> > other pre-SKL platforms, the watermark registers aren't double
> > buffered on g4x. Hence we must sequence the watermark update
> > carefully around plane updates.
> >
> > The code is quite heavily modelled on the VLV/CHV code, with some
> > fairly significant differences due to the different hardware
> > architecture:
> > * g4x doesn't use inverted watermark values
> > * CxSR actually affects the watermarks since it controls memory self
> >   refresh in addition to the max FIFO mode
> > * A further HPLL SR mode is possible with higher memory wakeup
> >   latency
> > * g4x has FBC2 and so it also has FBC watermarks
> > * max FIFO mode for primary plane only (cursor is allowed, sprite is not)
> > * g4x has no manual FIFO repartitioning
> > * some TLB miss related workarounds are needed for the watermarks
> >
> > Actually the hardware is quite similar to ILK+ in many ways. The
> > most visible differences are in the actual watermakr register
> > layout. ILK revamped that part quite heavily whereas g4x is still
> > using the layout inherited from earlier platforms.
> >
> > Note that we didn't previously enable the HPLL SR on g4x. So in order
> > to not introduce too many functional changes in this patch I've not
> > actually enabled it here either, even though the code is now fully
> > ready for it. We'll enable it separately later on.
> >
> > Signed-off-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
> > ---
> >  drivers/gpu/drm/i915/i915_debugfs.c  |  12 +-
> >  drivers/gpu/drm/i915/i915_drv.h      |  12 +
> >  drivers/gpu/drm/i915/intel_display.c |  25 +-
> >  drivers/gpu/drm/i915/intel_drv.h     |  28 ++
> >  drivers/gpu/drm/i915/intel_pm.c      | 942 +++++++++++++++++++++++++++--------
> >  5 files changed, 792 insertions(+), 227 deletions(-)
> >
> > diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c
> > index 870c470177b5..69550d31099e 100644
> > --- a/drivers/gpu/drm/i915/i915_debugfs.c
> > +++ b/drivers/gpu/drm/i915/i915_debugfs.c
> > @@ -3898,6 +3898,8 @@ static void wm_latency_show(struct seq_file *m, const uint16_t wm[8])
> >  		num_levels = 3;
> >  	else if (IS_VALLEYVIEW(dev_priv))
> >  		num_levels = 1;
> > +	else if (IS_G4X(dev_priv))
> > +		num_levels = 3;
> >  	else
> >  		num_levels = ilk_wm_max_level(dev_priv) + 1;
> >  
> > @@ -3910,8 +3912,10 @@ static void wm_latency_show(struct seq_file *m, const uint16_t wm[8])
> >  		 * - WM1+ latency values in 0.5us units
> >  		 * - latencies are in us on gen9/vlv/chv
> >  		 */
> > -		if (INTEL_GEN(dev_priv) >= 9 || IS_VALLEYVIEW(dev_priv) ||
> > -		    IS_CHERRYVIEW(dev_priv))
> > +		if (INTEL_GEN(dev_priv) >= 9 ||
> > +		    IS_VALLEYVIEW(dev_priv) ||
> > +		    IS_CHERRYVIEW(dev_priv) ||
> > +		    IS_G4X(dev_priv))
> >  			latency *= 10;
> >  		else if (level > 0)
> >  			latency *= 5;
> > @@ -3972,7 +3976,7 @@ static int pri_wm_latency_open(struct inode *inode, struct file *file)
> >  {
> >  	struct drm_i915_private *dev_priv = inode->i_private;
> >  
> > -	if (INTEL_GEN(dev_priv) < 5)
> > +	if (INTEL_GEN(dev_priv) < 5 && !IS_G4X(dev_priv))
> >  		return -ENODEV;
> >  
> >  	return single_open(file, pri_wm_latency_show, dev_priv);
> > @@ -4014,6 +4018,8 @@ static ssize_t wm_latency_write(struct file *file, const char __user *ubuf,
> >  		num_levels = 3;
> >  	else if (IS_VALLEYVIEW(dev_priv))
> >  		num_levels = 1;
> > +	else if (IS_G4X(dev_priv))
> > +		num_levels = 3;
> >  	else
> >  		num_levels = ilk_wm_max_level(dev_priv) + 1;
> >  
> > diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
> > index 0a393fdc53d1..6df8bff7f5a7 100644
> > --- a/drivers/gpu/drm/i915/i915_drv.h
> > +++ b/drivers/gpu/drm/i915/i915_drv.h
> > @@ -1762,11 +1762,13 @@ struct ilk_wm_values {
> >  
> >  struct g4x_pipe_wm {
> >  	uint16_t plane[I915_MAX_PLANES];
> > +	uint16_t fbc;
> >  };
> >  
> >  struct g4x_sr_wm {
> >  	uint16_t plane;
> >  	uint16_t cursor;
> > +	uint16_t fbc;
> >  };
> >  
> >  struct vlv_wm_ddl_values {
> > @@ -1781,6 +1783,15 @@ struct vlv_wm_values {
> >  	bool cxsr;
> >  };
> >  
> > +struct g4x_wm_values {
> > +	struct g4x_pipe_wm pipe[2];
> > +	struct g4x_sr_wm sr;
> > +	struct g4x_sr_wm hpll;
> > +	bool cxsr;
> > +	bool hpll_en;
> > +	bool fbc_en;
> > +};
> > +
> >  struct skl_ddb_entry {
> >  	uint16_t start, end;	/* in number of blocks, 'end' is exclusive */
> >  };
> > @@ -2410,6 +2421,7 @@ struct drm_i915_private {
> >  			struct ilk_wm_values hw;
> >  			struct skl_wm_values skl_hw;
> >  			struct vlv_wm_values vlv;
> > +			struct g4x_wm_values g4x;
> >  		};
> >  
> >  		uint8_t max_level;
> > diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c
> > index 85b9e2f521a0..0f42263c3f76 100644
> > --- a/drivers/gpu/drm/i915/intel_display.c
> > +++ b/drivers/gpu/drm/i915/intel_display.c
> > @@ -5719,6 +5719,8 @@ static void i9xx_set_pll_dividers(struct intel_crtc *crtc)
> >  static void i9xx_crtc_enable(struct intel_crtc_state *pipe_config,
> >  			     struct drm_atomic_state *old_state)
> >  {
> > +	struct intel_atomic_state *old_intel_state =
> > +		to_intel_atomic_state(old_state);
> >  	struct drm_crtc *crtc = pipe_config->base.crtc;
> >  	struct drm_device *dev = crtc->dev;
> >  	struct drm_i915_private *dev_priv = to_i915(dev);
> > @@ -5751,7 +5753,11 @@ static void i9xx_crtc_enable(struct intel_crtc_state *pipe_config,
> >  
> >  	intel_color_load_luts(&pipe_config->base);
> >  
> > -	intel_update_watermarks(intel_crtc);
> > +	if (dev_priv->display.initial_watermarks != NULL)
> > +		dev_priv->display.initial_watermarks(old_intel_state,
> > +						     intel_crtc->config);
> > +	else
> > +		intel_update_watermarks(intel_crtc);
> >  	intel_enable_pipe(intel_crtc);
> >  
> >  	assert_vblank_disabled(crtc);
> > @@ -10852,21 +10858,21 @@ int intel_plane_atomic_calc_changes(struct drm_crtc_state *crtc_state,
> >  			 turn_off, turn_on, mode_changed);
> >  
> >  	if (turn_on) {
> > -		if (INTEL_GEN(dev_priv) < 5)
> > +		if (INTEL_GEN(dev_priv) < 5 && !IS_G4X(dev_priv))
> >  			pipe_config->update_wm_pre = true;
> >  
> >  		/* must disable cxsr around plane enable/disable */
> >  		if (plane->id != PLANE_CURSOR)
> >  			pipe_config->disable_cxsr = true;
> >  	} else if (turn_off) {
> > -		if (INTEL_GEN(dev_priv) < 5)
> > +		if (INTEL_GEN(dev_priv) < 5 && !IS_G4X(dev_priv))
> >  			pipe_config->update_wm_post = true;
> >  
> >  		/* must disable cxsr around plane enable/disable */
> >  		if (plane->id != PLANE_CURSOR)
> >  			pipe_config->disable_cxsr = true;
> >  	} else if (intel_wm_need_update(&plane->base, plane_state)) {
> > -		if (INTEL_GEN(dev_priv) < 5) {
> > +		if (INTEL_GEN(dev_priv) < 5 && !IS_G4X(dev_priv)) {
> >  			/* FIXME bollocks */
> >  			pipe_config->update_wm_pre = true;
> >  			pipe_config->update_wm_post = true;
> > @@ -11290,7 +11296,8 @@ clear_intel_crtc_state(struct intel_crtc_state *crtc_state)
> >  	shared_dpll = crtc_state->shared_dpll;
> >  	dpll_hw_state = crtc_state->dpll_hw_state;
> >  	force_thru = crtc_state->pch_pfit.force_thru;
> > -	if (IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv))
> > +	if (IS_G4X(dev_priv) ||
> > +	    IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv))
> >  		wm_state = crtc_state->wm;
> >  
> >  	/* Keep base drm_crtc_state intact, only clear our extended struct */
> > @@ -11302,7 +11309,8 @@ clear_intel_crtc_state(struct intel_crtc_state *crtc_state)
> >  	crtc_state->shared_dpll = shared_dpll;
> >  	crtc_state->dpll_hw_state = dpll_hw_state;
> >  	crtc_state->pch_pfit.force_thru = force_thru;
> > -	if (IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv))
> > +	if (IS_G4X(dev_priv) ||
> > +	    IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv))
> >  		crtc_state->wm = wm_state;
> >  }
> >  
> > @@ -15527,7 +15535,10 @@ intel_modeset_setup_hw_state(struct drm_device *dev)
> >  		pll->on = false;
> >  	}
> >  
> > -	if (IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv)) {
> > +	if (IS_G4X(dev_priv)) {
> > +		g4x_wm_get_hw_state(dev);
> > +		g4x_wm_sanitize(dev_priv);
> > +	} else if (IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv)) {
> >  		vlv_wm_get_hw_state(dev);
> >  		vlv_wm_sanitize(dev_priv);
> >  	} else if (IS_GEN9(dev_priv)) {
> > diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
> > index d1cdd10998fa..f530df71a480 100644
> > --- a/drivers/gpu/drm/i915/intel_drv.h
> > +++ b/drivers/gpu/drm/i915/intel_drv.h
> > @@ -522,6 +522,22 @@ struct vlv_fifo_state {
> >  	u16 plane[I915_MAX_PLANES];
> >  };
> >  
> > +enum g4x_wm_level {
> > +	G4X_WM_LEVEL_NORMAL,
> > +	G4X_WM_LEVEL_SR,
> > +	G4X_WM_LEVEL_HPLL,
> > +	NUM_G4X_WM_LEVELS,
> > +};
> > +
> > +struct g4x_wm_state {
> > +	struct g4x_pipe_wm wm;
> > +	struct g4x_sr_wm sr;
> > +	struct g4x_sr_wm hpll;
> > +	bool cxsr;
> > +	bool hpll_en;
> > +	bool fbc_en;
> > +};
> > +
> >  struct intel_crtc_wm_state {
> >  	union {
> >  		struct {
> > @@ -557,6 +573,15 @@ struct intel_crtc_wm_state {
> >  			/* display FIFO split */
> >  			struct vlv_fifo_state fifo_state;
> >  		} vlv;
> > +
> > +		struct {
> > +			/* "raw" watermarks */
> > +			struct g4x_pipe_wm raw[NUM_G4X_WM_LEVELS];
> > +			/* intermediate watermarks */
> > +			struct g4x_wm_state intermediate;
> > +			/* optimal watermarks */
> > +			struct g4x_wm_state optimal;
> > +		} g4x;
> >  	};
> >  
> >  	/*
> > @@ -794,6 +819,7 @@ struct intel_crtc {
> >  		union {
> >  			struct intel_pipe_wm ilk;
> >  			struct vlv_wm_state vlv;
> > +			struct g4x_wm_state g4x;
> >  		} active;
> >  	} wm;
> >  
> > @@ -1841,6 +1867,7 @@ void gen6_rps_boost(struct drm_i915_private *dev_priv,
> >  		    struct intel_rps_client *rps,
> >  		    unsigned long submitted);
> >  void intel_queue_rps_boost_for_request(struct drm_i915_gem_request *req);
> > +void g4x_wm_get_hw_state(struct drm_device *dev);
> >  void vlv_wm_get_hw_state(struct drm_device *dev);
> >  void ilk_wm_get_hw_state(struct drm_device *dev);
> >  void skl_wm_get_hw_state(struct drm_device *dev);
> > @@ -1848,6 +1875,7 @@ void skl_ddb_get_hw_state(struct drm_i915_private *dev_priv,
> >  			  struct skl_ddb_allocation *ddb /* out */);
> >  void skl_pipe_wm_get_hw_state(struct drm_crtc *crtc,
> >  			      struct skl_pipe_wm *out);
> > +void g4x_wm_sanitize(struct drm_i915_private *dev_priv);
> >  void vlv_wm_sanitize(struct drm_i915_private *dev_priv);
> >  bool intel_can_enable_sagv(struct drm_atomic_state *state);
> >  int intel_enable_sagv(struct drm_i915_private *dev_priv);
> > diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c
> > index 61b67994c4a8..9b0a6a4572ce 100644
> > --- a/drivers/gpu/drm/i915/intel_pm.c
> > +++ b/drivers/gpu/drm/i915/intel_pm.c
> > @@ -429,7 +429,10 @@ bool intel_set_memory_cxsr(struct drm_i915_private *dev_priv, bool enable)
> >  
> >  	mutex_lock(&dev_priv->wm.wm_mutex);
> >  	ret = _intel_set_memory_cxsr(dev_priv, enable);
> > -	dev_priv->wm.vlv.cxsr = enable;
> > +	if (IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv))
> > +		dev_priv->wm.vlv.cxsr = enable;
> > +	else if (IS_G4X(dev_priv))
> > +		dev_priv->wm.g4x.cxsr = enable;
> >  	mutex_unlock(&dev_priv->wm.wm_mutex);
> >  
> >  	return ret;
> > @@ -568,20 +571,6 @@ static const struct intel_watermark_params pineview_cursor_hplloff_wm = {
> >  	.guard_size = PINEVIEW_CURSOR_GUARD_WM,
> >  	.cacheline_size = PINEVIEW_FIFO_LINE_SIZE,
> >  };
> > -static const struct intel_watermark_params g4x_wm_info = {
> > -	.fifo_size = G4X_FIFO_SIZE,
> > -	.max_wm = G4X_MAX_WM,
> > -	.default_wm = G4X_MAX_WM,
> > -	.guard_size = 2,
> > -	.cacheline_size = G4X_FIFO_LINE_SIZE,
> > -};
> > -static const struct intel_watermark_params g4x_cursor_wm_info = {
> > -	.fifo_size = I965_CURSOR_FIFO,
> > -	.max_wm = I965_CURSOR_MAX_WM,
> > -	.default_wm = I965_CURSOR_DFT_WM,
> > -	.guard_size = 2,
> > -	.cacheline_size = G4X_FIFO_LINE_SIZE,
> > -};
> >  static const struct intel_watermark_params i965_cursor_wm_info = {
> >  	.fifo_size = I965_CURSOR_FIFO,
> >  	.max_wm = I965_CURSOR_MAX_WM,
> > @@ -780,6 +769,16 @@ static unsigned int intel_calculate_wm(int pixel_rate,
> >  	return wm_size;
> >  }
> >  
> > +static bool is_disabling(int old, int new, int threshold)
> > +{
> > +	return old >= threshold && new < threshold;
> > +}
> > +
> > +static bool is_enabling(int old, int new, int threshold)
> > +{
> > +	return old < threshold && new >= threshold;
> > +}
> > +
> >  static int intel_wm_num_levels(struct drm_i915_private *dev_priv)
> >  {
> >  	return dev_priv->wm.max_level + 1;
> > @@ -911,138 +910,28 @@ static int g4x_tlb_miss_wa(int fifo_size, int width, int cpp)
> >  	return max(0, tlb_miss);
> >  }
> >  
> > -static bool g4x_compute_wm0(struct drm_i915_private *dev_priv,
> > -			    int plane,
> > -			    const struct intel_watermark_params *display,
> > -			    int display_latency_ns,
> > -			    const struct intel_watermark_params *cursor,
> > -			    int cursor_latency_ns,
> > -			    int *plane_wm,
> > -			    int *cursor_wm)
> > +static void g4x_write_wm_values(struct drm_i915_private *dev_priv,
> > +				const struct g4x_wm_values *wm)
> >  {
> > -	struct intel_crtc *crtc;
> > -	const struct drm_display_mode *adjusted_mode;
> > -	const struct drm_framebuffer *fb;
> > -	int htotal, plane_width, cursor_width, clock, cpp;
> > -	int entries;
> > -
> > -	crtc = intel_get_crtc_for_plane(dev_priv, plane);
> > -	if (!intel_crtc_active(crtc)) {
> > -		*cursor_wm = cursor->guard_size;
> > -		*plane_wm = display->guard_size;
> > -		return false;
> > -	}
> > -
> > -	adjusted_mode = &crtc->config->base.adjusted_mode;
> > -	fb = crtc->base.primary->state->fb;
> > -	clock = adjusted_mode->crtc_clock;
> > -	htotal = adjusted_mode->crtc_htotal;
> > -	plane_width = crtc->config->pipe_src_w;
> > -	cursor_width = crtc->base.cursor->state->crtc_w;
> > -	cpp = fb->format->cpp[0];
> > -
> > -	/* Use the small buffer method to calculate plane watermark */
> > -	entries = intel_wm_method1(clock, cpp, display_latency_ns / 100);
> > -	entries += g4x_tlb_miss_wa(display->fifo_size, plane_width, cpp);
> > -	entries = DIV_ROUND_UP(entries, display->cacheline_size);
> > -	*plane_wm = entries + display->guard_size;
> > -	if (*plane_wm > (int)display->max_wm)
> > -		*plane_wm = display->max_wm;
> > -
> > -	/* Use the large buffer method to calculate cursor watermark */
> > -	entries = intel_wm_method2(clock, htotal, cursor_width, 4,
> > -				   cursor_latency_ns / 100);
> > -	entries += g4x_tlb_miss_wa(cursor->fifo_size, cursor_width, 4);
> > -	entries = DIV_ROUND_UP(entries, cursor->cacheline_size);
> > -	*cursor_wm = entries + cursor->guard_size;
> > -	if (*cursor_wm > (int)cursor->max_wm)
> > -		*cursor_wm = (int)cursor->max_wm;
> > -
> > -	return true;
> > -}
> > -
> > -/*
> > - * Check the wm result.
> > - *
> > - * If any calculated watermark values is larger than the maximum value that
> > - * can be programmed into the associated watermark register, that watermark
> > - * must be disabled.
> > - */
> > -static bool g4x_check_srwm(struct drm_i915_private *dev_priv,
> > -			   int display_wm, int cursor_wm,
> > -			   const struct intel_watermark_params *display,
> > -			   const struct intel_watermark_params *cursor)
> > -{
> > -	DRM_DEBUG_KMS("SR watermark: display plane %d, cursor %d\n",
> > -		      display_wm, cursor_wm);
> > -
> > -	if (display_wm > display->max_wm) {
> > -		DRM_DEBUG_KMS("display watermark is too large(%d/%u), disabling\n",
> > -			      display_wm, display->max_wm);
> > -		return false;
> > -	}
> > -
> > -	if (cursor_wm > cursor->max_wm) {
> > -		DRM_DEBUG_KMS("cursor watermark is too large(%d/%u), disabling\n",
> > -			      cursor_wm, cursor->max_wm);
> > -		return false;
> > -	}
> > -
> > -	if (!(display_wm || cursor_wm)) {
> > -		DRM_DEBUG_KMS("SR latency is 0, disabling\n");
> > -		return false;
> > -	}
> > -
> > -	return true;
> > -}
> > -
> > -static bool g4x_compute_srwm(struct drm_i915_private *dev_priv,
> > -			     int plane,
> > -			     int latency_ns,
> > -			     const struct intel_watermark_params *display,
> > -			     const struct intel_watermark_params *cursor,
> > -			     int *display_wm, int *cursor_wm)
> > -{
> > -	struct intel_crtc *crtc;
> > -	const struct drm_display_mode *adjusted_mode;
> > -	const struct drm_framebuffer *fb;
> > -	int plane_width, cursor_width, htotal, cpp, clock;
> > -	int small, large;
> > -	int entries;
> > -
> > -	if (!latency_ns) {
> > -		*display_wm = *cursor_wm = 0;
> > -		return false;
> > -	}
> > -
> > -	crtc = intel_get_crtc_for_plane(dev_priv, plane);
> > -	adjusted_mode = &crtc->config->base.adjusted_mode;
> > -	fb = crtc->base.primary->state->fb;
> > -	clock = adjusted_mode->crtc_clock;
> > -	htotal = adjusted_mode->crtc_htotal;
> > -	plane_width = crtc->config->pipe_src_w;
> > -	cursor_width = crtc->base.cursor->state->crtc_w;
> > -	cpp = fb->format->cpp[0];
> > -
> > -	/* Use the minimum of the small and large buffer method for primary */
> > -	small = intel_wm_method1(clock, cpp, latency_ns / 100);
> > -	large = intel_wm_method2(clock, htotal, plane_width, cpp,
> > -				 latency_ns / 100);
> > -	entries = min(small, large);
> > -	entries += g4x_tlb_miss_wa(display->fifo_size, plane_width, cpp);
> > -	entries = DIV_ROUND_UP(entries, display->cacheline_size);
> > -	*display_wm = entries + display->guard_size;
> > -
> > -	/* calculate the self-refresh watermark for display cursor */
> > -	entries = intel_wm_method2(clock, htotal, cursor_width, 4,
> > -				   latency_ns / 100);
> > -	entries += g4x_tlb_miss_wa(cursor->fifo_size, cursor_width, 4);
> > -	entries = DIV_ROUND_UP(entries, cursor->cacheline_size);
> > -	*cursor_wm = entries + cursor->guard_size;
> > -
> > -	return g4x_check_srwm(dev_priv,
> > -			      *display_wm, *cursor_wm,
> > -			      display, cursor);
> > +	I915_WRITE(DSPFW1,
> > +		   FW_WM(wm->sr.plane, SR) |
> > +		   FW_WM(wm->pipe[PIPE_B].plane[PLANE_CURSOR], CURSORB) |
> > +		   FW_WM(wm->pipe[PIPE_B].plane[PLANE_PRIMARY], PLANEB) |
> > +		   FW_WM(wm->pipe[PIPE_A].plane[PLANE_PRIMARY], PLANEA));
> > +	I915_WRITE(DSPFW2,
> > +		   (wm->fbc_en ? DSPFW_FBC_SR_EN : 0) |
> > +		   FW_WM(wm->sr.fbc, FBC_SR) |
> > +		   FW_WM(wm->hpll.fbc, FBC_HPLL_SR) |
> > +		   FW_WM(wm->pipe[PIPE_B].plane[PLANE_SPRITE0], SPRITEB) |
> > +		   FW_WM(wm->pipe[PIPE_A].plane[PLANE_CURSOR], CURSORA) |
> > +		   FW_WM(wm->pipe[PIPE_A].plane[PLANE_SPRITE0], SPRITEA));
> > +	I915_WRITE(DSPFW3,
> > +		   (wm->hpll_en ? DSPFW_HPLL_SR_EN : 0) |
> > +		   FW_WM(wm->sr.cursor, CURSOR_SR) |
> > +		   FW_WM(wm->hpll.cursor, HPLL_CURSOR) |
> > +		   FW_WM(wm->hpll.plane, HPLL_SR));
> > +
> > +	POSTING_READ(DSPFW1);
> >  }
> >  
> >  #define FW_WM_VLV(value, plane) \
> > @@ -1126,6 +1015,523 @@ static void vlv_write_wm_values(struct drm_i915_private *dev_priv,
> >  
> >  #undef FW_WM_VLV
> >  
> > +static void g4x_setup_wm_latency(struct drm_i915_private *dev_priv)
> > +{
> > +	/* all latencies in usec */
> > +	dev_priv->wm.pri_latency[G4X_WM_LEVEL_NORMAL] = 5;
> > +	dev_priv->wm.pri_latency[G4X_WM_LEVEL_SR] = 12;
> > +
> > +	dev_priv->wm.max_level = G4X_WM_LEVEL_SR;
> > +}
> > +
> > +static int g4x_plane_fifo_size(enum plane_id plane_id, int level)
> > +{
> > +	/*
> > +	 * DSPCNTR[13] supposedly controls whether the
> > +	 * primary plane can use the FIFO space otherwise
> > +	 * reserved for the sprite plane. It's not 100% clear
> > +	 * what the actual FIFO size is, but it looks like we
> > +	 * can happily set both primary and sprite watermarks
> > +	 * up to 127 cachelines. So that would seem to mean
> > +	 * that either DSPCNTR[13] doesn't do anything, or that
> > +	 * the total FIFO is >= 256 cachelines in size. Either
> > +	 * way, we don't seem to have to worry about this
> > +	 * repartitioning as the maximum watermark value the
> > +	 * register can hold for each plane is lower than the
> > +	 * minimum FIFO size.
> > +	 */
> > +	switch (plane_id) {
> > +	case PLANE_CURSOR:
> > +		return 63;
> > +	case PLANE_PRIMARY:
> > +		return level == G4X_WM_LEVEL_NORMAL ? 127 : 511;
> > +	case PLANE_SPRITE0:
> > +		return level == G4X_WM_LEVEL_NORMAL ? 127 : 0;
> > +	default:
> > +		MISSING_CASE(plane_id);
> > +		return 0;
> > +	}
> > +}
> > +
> > +static int g4x_fbc_fifo_size(int level)
> > +{
> > +	switch (level) {
> > +	case G4X_WM_LEVEL_SR:
> > +		return 7;
> > +	case G4X_WM_LEVEL_HPLL:
> > +		return 15;
> > +	default:
> > +		MISSING_CASE(level);
> > +		return 0;
> > +	}
> > +}
> > +
> > +static uint16_t g4x_compute_wm(const struct intel_crtc_state *crtc_state,
> > +			       const struct intel_plane_state *plane_state,
> > +			       int level)
> > +{
> > +	struct intel_plane *plane = to_intel_plane(plane_state->base.plane);
> > +	struct drm_i915_private *dev_priv = to_i915(plane->base.dev);
> > +	const struct drm_display_mode *adjusted_mode =
> > +		&crtc_state->base.adjusted_mode;
> > +	int clock, htotal, cpp, width, wm;
> > +	int latency = dev_priv->wm.pri_latency[level] * 10;
> > +
> > +	if (latency == 0)
> > +		return USHRT_MAX;
> > +
> > +	if (!intel_wm_plane_visible(crtc_state, plane_state))
> > +		return 0;
> > +
> > +	/*
> > +	 * Not 100% sure which way ELK should go here as the
> > +	 * spec only says CL/CTG should assume 32bpp and BW
> > +	 * doesn't need to. But as these things followed the
> > +	 * mobile vs. desktop lines on gen3 as well, let's
> > +	 * assume ELK doesn't need this.
> > +	 *
> > +	 * The spec also fails to list such a restriction for
> > +	 * the HPLL watermark, which seems a little strange.
> > +	 * Let's use 32bpp for the HPLL watermark as well.
> > +	 */
> > +	if (IS_GM45(dev_priv) && plane->id == PLANE_PRIMARY &&
> > +	    level != G4X_WM_LEVEL_NORMAL)
> > +		cpp = 4;
> > +	else
> > +		cpp = plane_state->base.fb->format->cpp[0];
> > +
> > +	clock = adjusted_mode->crtc_clock;
> > +	htotal = adjusted_mode->crtc_htotal;
> > +
> > +	if (plane->id == PLANE_CURSOR)
> > +		width = plane_state->base.crtc_w;
> > +	else
> > +		width = drm_rect_width(&plane_state->base.dst);
> > +
> > +	if (plane->id == PLANE_CURSOR) {
> > +		wm = intel_wm_method2(clock, htotal, width, cpp, latency);
> > +	} else if (plane->id == PLANE_PRIMARY &&
> > +		   level == G4X_WM_LEVEL_NORMAL) {
> > +		wm = intel_wm_method1(clock, cpp, latency);
> > +	} else {
> > +		int small, large;
> > +
> > +		small = intel_wm_method1(clock, cpp, latency);
> > +		large = intel_wm_method2(clock, htotal, width, cpp, latency);
> > +
> > +		wm = min(small, large);
> > +	}
> > +
> > +	wm += g4x_tlb_miss_wa(g4x_plane_fifo_size(plane->id, level),
> > +			      width, cpp);
> > +
> > +	wm = DIV_ROUND_UP(wm, 64) + 2;
> > +
> > +	return min_t(int, wm, USHRT_MAX);
> > +}
> > +
> > +static bool g4x_raw_plane_wm_set(struct intel_crtc_state *crtc_state,
> > +				 int level, enum plane_id plane_id, u16 value)
> > +{
> > +	struct drm_i915_private *dev_priv = to_i915(crtc_state->base.crtc->dev);
> > +	bool dirty = false;
> > +
> > +	for (; level < intel_wm_num_levels(dev_priv); level++) {
> > +		struct g4x_pipe_wm *raw = &crtc_state->wm.g4x.raw[level];
> > +
> > +		dirty |= raw->plane[plane_id] != value;
> > +		raw->plane[plane_id] = value;
> > +	}
> > +
> > +	return dirty;
> > +}
> > +
> > +static bool g4x_raw_fbc_wm_set(struct intel_crtc_state *crtc_state,
> > +			       int level, u16 value)
> > +{
> > +	struct drm_i915_private *dev_priv = to_i915(crtc_state->base.crtc->dev);
> > +	bool dirty = false;
> > +
> > +	/* NORMAL level doesn't have an FBC watermark */
> > +	level = max(level, G4X_WM_LEVEL_SR);
> > +
> > +	for (; level < intel_wm_num_levels(dev_priv); level++) {
> > +		struct g4x_pipe_wm *raw = &crtc_state->wm.g4x.raw[level];
> > +
> > +		dirty |= raw->fbc != value;
> > +		raw->fbc = value;
> > +	}
> > +
> > +	return dirty;
> > +}
> > +
> > +static uint32_t ilk_compute_fbc_wm(const struct intel_crtc_state *cstate,
> > +				   const struct intel_plane_state *pstate,
> > +				   uint32_t pri_val);
> > +
> > +static bool g4x_raw_plane_wm_compute(struct intel_crtc_state *crtc_state,
> > +				     const struct intel_plane_state *plane_state)
> > +{
> > +	struct intel_plane *plane = to_intel_plane(plane_state->base.plane);
> > +	int num_levels = intel_wm_num_levels(to_i915(plane->base.dev));
> > +	enum plane_id plane_id = plane->id;
> > +	bool dirty = false;
> > +	int level;
> > +
> > +	if (!intel_wm_plane_visible(crtc_state, plane_state)) {
> > +		dirty |= g4x_raw_plane_wm_set(crtc_state, 0, plane_id, 0);
> > +		if (plane_id == PLANE_PRIMARY)
> > +			dirty |= g4x_raw_fbc_wm_set(crtc_state, 0, 0);
> > +		goto out;
> > +	}
> > +
> > +	for (level = 0; level < num_levels; level++) {
> > +		struct g4x_pipe_wm *raw = &crtc_state->wm.g4x.raw[level];
> > +		int wm, max_wm;
> > +
> > +		wm = g4x_compute_wm(crtc_state, plane_state, level);
> > +		max_wm = g4x_plane_fifo_size(plane_id, level);
> > +
> > +		if (wm > max_wm)
> > +			break;
> > +
> > +		dirty |= raw->plane[plane_id] != wm;
> > +		raw->plane[plane_id] = wm;
> > +
> > +		if (plane_id != PLANE_PRIMARY ||
> > +		    level == G4X_WM_LEVEL_NORMAL)
> > +			continue;
> > +
> > +		wm = ilk_compute_fbc_wm(crtc_state, plane_state,
> > +					raw->plane[plane_id]);
> > +		max_wm = g4x_fbc_fifo_size(level);
> > +
> > +		/*
> > +		 * FBC wm is not mandatory as we
> > +		 * can always just disable its use.
> > +		 */
> > +		if (wm > max_wm)
> > +			wm = USHRT_MAX;
> > +
> > +		dirty |= raw->fbc != wm;
> > +		raw->fbc = wm;
> > +	}
> > +
> > +	/* mark watermarks as invalid */
> > +	dirty |= g4x_raw_plane_wm_set(crtc_state, level, plane_id, USHRT_MAX);
> > +
> > +	if (plane_id == PLANE_PRIMARY)
> > +		dirty |= g4x_raw_fbc_wm_set(crtc_state, level, USHRT_MAX);
> > +
> > + out:
> > +	if (dirty) {
> > +		DRM_DEBUG_KMS("%s watermarks: normal=%d, SR=%d, HPLL=%d\n",
> > +			      plane->base.name,
> > +			      crtc_state->wm.g4x.raw[G4X_WM_LEVEL_NORMAL].plane[plane_id],
> > +			      crtc_state->wm.g4x.raw[G4X_WM_LEVEL_SR].plane[plane_id],
> > +			      crtc_state->wm.g4x.raw[G4X_WM_LEVEL_HPLL].plane[plane_id]);
> > +
> > +		if (plane_id == PLANE_PRIMARY)
> > +			DRM_DEBUG_KMS("FBC watermarks: SR=%d, HPLL=%d\n",
> > +				      crtc_state->wm.g4x.raw[G4X_WM_LEVEL_SR].fbc,
> > +				      crtc_state->wm.g4x.raw[G4X_WM_LEVEL_HPLL].fbc);
> > +	}
> > +
> > +	return dirty;
> > +}
> > +
> > +static bool g4x_raw_plane_wm_is_valid(const struct intel_crtc_state *crtc_state,
> > +				      enum plane_id plane_id, int level)
> > +{
> > +	const struct g4x_pipe_wm *raw = &crtc_state->wm.g4x.raw[level];
> > +
> > +	return raw->plane[plane_id] <= g4x_plane_fifo_size(plane_id, level);
> > +}
> > +
> > +static bool g4x_raw_crtc_wm_is_valid(const struct intel_crtc_state *crtc_state,
> > +				     int level)
> > +{
> > +	struct drm_i915_private *dev_priv = to_i915(crtc_state->base.crtc->dev);
> > +
> > +	if (level > dev_priv->wm.max_level)
> > +		return false;
> > +
> > +	return g4x_raw_plane_wm_is_valid(crtc_state, PLANE_PRIMARY, level) &&
> > +		g4x_raw_plane_wm_is_valid(crtc_state, PLANE_SPRITE0, level) &&
> > +		g4x_raw_plane_wm_is_valid(crtc_state, PLANE_CURSOR, level);
> > +}
> > +
> > +/* mark all levels starting from 'level' as invalid */
> > +static void g4x_invalidate_wms(struct intel_crtc *crtc,
> > +			       struct g4x_wm_state *wm_state, int level)
> > +{
> > +	if (level <= G4X_WM_LEVEL_NORMAL) {
> > +		enum plane_id plane_id;
> > +
> > +		for_each_plane_id_on_crtc(crtc, plane_id)
> > +			wm_state->wm.plane[plane_id] = USHRT_MAX;
> > +	}
> > +
> > +	if (level <= G4X_WM_LEVEL_SR) {
> > +		wm_state->cxsr = false;
> > +		wm_state->sr.cursor = USHRT_MAX;
> > +		wm_state->sr.plane = USHRT_MAX;
> > +		wm_state->sr.fbc = USHRT_MAX;
> > +	}
> > +
> > +	if (level <= G4X_WM_LEVEL_HPLL) {
> > +		wm_state->hpll_en = false;
> > +		wm_state->hpll.cursor = USHRT_MAX;
> > +		wm_state->hpll.plane = USHRT_MAX;
> > +		wm_state->hpll.fbc = USHRT_MAX;
> > +	}
> > +}
> > +
> > +static int g4x_compute_pipe_wm(struct intel_crtc_state *crtc_state)
> > +{
> > +	struct intel_crtc *crtc = to_intel_crtc(crtc_state->base.crtc);
> > +	struct intel_atomic_state *state =
> > +		to_intel_atomic_state(crtc_state->base.state);
> > +	struct g4x_wm_state *wm_state = &crtc_state->wm.g4x.optimal;
> > +	int num_active_planes = hweight32(crtc_state->active_planes &
> > +					  ~BIT(PLANE_CURSOR));
> > +	const struct g4x_pipe_wm *raw;
> > +	struct intel_plane_state *plane_state;
> > +	struct intel_plane *plane;
> > +	enum plane_id plane_id;
> > +	int i, level;
> > +	unsigned int dirty = 0;
> > +
> > +	for_each_intel_plane_in_state(state, plane, plane_state, i) {
> > +		const struct intel_plane_state *old_plane_state =
> > +			to_intel_plane_state(plane->base.state);
> > +
> > +		if (plane_state->base.crtc != &crtc->base &&
> > +		    old_plane_state->base.crtc != &crtc->base)
> > +			continue;
> > +
> > +		if (g4x_raw_plane_wm_compute(crtc_state, plane_state))
> > +			dirty |= BIT(plane->id);
> > +	}
> > +
> > +	if (!dirty)
> > +		return 0;
> > +
> > +	level = G4X_WM_LEVEL_NORMAL;
> > +	if (!g4x_raw_crtc_wm_is_valid(crtc_state, level))
> > +		goto out;
> > +
> > +	raw = &crtc_state->wm.g4x.raw[level];
> > +	for_each_plane_id_on_crtc(crtc, plane_id)
> > +		wm_state->wm.plane[plane_id] = raw->plane[plane_id];
> > +
> > +	level = G4X_WM_LEVEL_SR;
> > +
> > +	if (!g4x_raw_crtc_wm_is_valid(crtc_state, level))
> > +		goto out;
> > +
> > +	raw = &crtc_state->wm.g4x.raw[level];
> > +	wm_state->sr.plane = raw->plane[PLANE_PRIMARY];
> > +	wm_state->sr.cursor = raw->plane[PLANE_CURSOR];
> > +	wm_state->sr.fbc = raw->fbc;
> > +
> > +	wm_state->cxsr = num_active_planes == BIT(PLANE_PRIMARY);
> > +
> > +	level = G4X_WM_LEVEL_HPLL;
> > +
> > +	if (!g4x_raw_crtc_wm_is_valid(crtc_state, level))
> > +		goto out;
> > +
> > +	raw = &crtc_state->wm.g4x.raw[level];
> > +	wm_state->hpll.plane = raw->plane[PLANE_PRIMARY];
> > +	wm_state->hpll.cursor = raw->plane[PLANE_CURSOR];
> > +	wm_state->hpll.fbc = raw->fbc;
> > +
> > +	wm_state->hpll_en = wm_state->cxsr;
> > +
> > +	level++;
> > +
> > + out:
> > +	if (level == G4X_WM_LEVEL_NORMAL)
> > +		return -EINVAL;
> > +
> > +	/* invalidate the higher levels */
> > +	g4x_invalidate_wms(crtc, wm_state, level);
> > +
> > +	/*
> > +	 * Determine if the FBC watermark(s) can be used. IF
> > +	 * this isn't the case we prefer to disable the FBC
> > +	 ( watermark(s) rather than disable the SR/HPLL
> > +	 * level(s) entirely.
> > +	 */
> > +	wm_state->fbc_en = level > G4X_WM_LEVEL_NORMAL;
> > +
> > +	if (level >= G4X_WM_LEVEL_SR &&
> > +	    wm_state->sr.fbc > g4x_fbc_fifo_size(G4X_WM_LEVEL_SR))
> > +		wm_state->fbc_en = false;
> > +	else if (level >= G4X_WM_LEVEL_HPLL &&
> > +		 wm_state->hpll.fbc > g4x_fbc_fifo_size(G4X_WM_LEVEL_HPLL))
> > +		wm_state->fbc_en = false;
> > +
> > +	return 0;
> > +}
> > +
> > +static int g4x_compute_intermediate_wm(struct drm_device *dev,
> > +				       struct intel_crtc *crtc,
> > +				       struct intel_crtc_state *crtc_state)
> > +{
> > +	struct g4x_wm_state *intermediate = &crtc_state->wm.g4x.intermediate;
> > +	const struct g4x_wm_state *optimal = &crtc_state->wm.g4x.optimal;
> > +	const struct g4x_wm_state *active = &crtc->wm.active.g4x;
> > +	enum plane_id plane_id;
> 
> I would add a provision for modeset, no need to calculate intermediate watermarks with the pipe enabled, something like this?
> 
> 	if (!newstate->base.active || drm_atomic_crtc_needs_modeset(&newstate->base)) {
> 		*intermediate = *optimal;
> 		return 0;
> 	}
> 
> The active watermarks are wrongly assigned here, if there's a pending update it could point to old old optimal state, or old intermediate state, it should be something like this:
> old_crtc_state = drm_atomic_get_old_crtc_state(crtc_state->base.state, &crtc->base);
> active = &to_intel_crtc_state(old_crtc_state)->wm.g4x.optimal
> 
> I know, vlv does this wrong too, should be fixed as well..
> With that fixed all patches look good to me, so feel free to put this on them:
> 
> Reviewed-by: Maarten Lankhorst <maarten.lankhorst@linux.intel.com>

I took a liberal interpretation of your r-b and slapped it onto the
original patch since that was reasonably well tested at least. And
I pushed the lot to dinq. Thanks for the review.

I'll post a followup with the old optimal vs. active thing now.


> 
> > +	intermediate->cxsr = optimal->cxsr && active->cxsr &&
> > +		!crtc_state->disable_cxsr;
> > +	intermediate->hpll_en = optimal->hpll_en && active->hpll_en &&
> > +		!crtc_state->disable_cxsr;
> > +	intermediate->fbc_en = optimal->fbc_en && active->fbc_en;
> > +
> > +	for_each_plane_id_on_crtc(crtc, plane_id) {
> > +		intermediate->wm.plane[plane_id] =
> > +			max(optimal->wm.plane[plane_id],
> > +			    active->wm.plane[plane_id]);
> > +
> > +		WARN_ON(intermediate->wm.plane[plane_id] >
> > +			g4x_plane_fifo_size(plane_id, G4X_WM_LEVEL_NORMAL));
> > +	}
> > +
> > +	intermediate->sr.plane = max(optimal->sr.plane,
> > +				     active->sr.plane);
> > +	intermediate->sr.cursor = max(optimal->sr.cursor,
> > +				      active->sr.cursor);
> > +	intermediate->sr.fbc = max(optimal->sr.fbc,
> > +				   active->sr.fbc);
> > +
> > +	intermediate->hpll.plane = max(optimal->hpll.plane,
> > +				       active->hpll.plane);
> > +	intermediate->hpll.cursor = max(optimal->hpll.cursor,
> > +					active->hpll.cursor);
> > +	intermediate->hpll.fbc = max(optimal->hpll.fbc,
> > +				     active->hpll.fbc);
> > +
> > +	WARN_ON((intermediate->sr.plane >
> > +		 g4x_plane_fifo_size(PLANE_PRIMARY, G4X_WM_LEVEL_SR) ||
> > +		 intermediate->sr.cursor >
> > +		 g4x_plane_fifo_size(PLANE_CURSOR, G4X_WM_LEVEL_SR)) &&
> > +		intermediate->cxsr);
> > +	WARN_ON((intermediate->sr.plane >
> > +		 g4x_plane_fifo_size(PLANE_PRIMARY, G4X_WM_LEVEL_HPLL) ||
> > +		 intermediate->sr.cursor >
> > +		 g4x_plane_fifo_size(PLANE_CURSOR, G4X_WM_LEVEL_HPLL)) &&
> > +		intermediate->hpll_en);
> > +
> > +	WARN_ON(intermediate->sr.fbc > g4x_fbc_fifo_size(1) &&
> > +		intermediate->fbc_en && intermediate->cxsr);
> > +	WARN_ON(intermediate->hpll.fbc > g4x_fbc_fifo_size(2) &&
> > +		intermediate->fbc_en && intermediate->hpll_en);
> > +
> > +	/*
> > +	 * If our intermediate WM are identical to the final WM, then we can
> > +	 * omit the post-vblank programming; only update if it's different.
> > +	 */
> > +	if (memcmp(intermediate, optimal, sizeof(*intermediate)) != 0)
> > +		crtc_state->wm.need_postvbl_update = true;
> > +
> > +	return 0;
> > +}
> > +
> > +static void g4x_merge_wm(struct drm_i915_private *dev_priv,
> > +			 struct g4x_wm_values *wm)
> > +{
> > +	struct intel_crtc *crtc;
> > +	int num_active_crtcs = 0;
> > +
> > +	wm->cxsr = true;
> > +	wm->hpll_en = true;
> > +	wm->fbc_en = true;
> > +
> > +	for_each_intel_crtc(&dev_priv->drm, crtc) {
> > +		const struct g4x_wm_state *wm_state = &crtc->wm.active.g4x;
> > +
> > +		if (!crtc->active)
> > +			continue;
> > +
> > +		if (!wm_state->cxsr)
> > +			wm->cxsr = false;
> > +		if (!wm_state->hpll_en)
> > +			wm->hpll_en = false;
> > +		if (!wm_state->fbc_en)
> > +			wm->fbc_en = false;
> > +
> > +		num_active_crtcs++;
> > +	}
> > +
> > +	if (num_active_crtcs != 1) {
> > +		wm->cxsr = false;
> > +		wm->hpll_en = false;
> > +		wm->fbc_en = false;
> > +	}
> > +
> > +	for_each_intel_crtc(&dev_priv->drm, crtc) {
> > +		const struct g4x_wm_state *wm_state = &crtc->wm.active.g4x;
> > +		enum pipe pipe = crtc->pipe;
> > +
> > +		wm->pipe[pipe] = wm_state->wm;
> > +		if (crtc->active && wm->cxsr)
> > +			wm->sr = wm_state->sr;
> > +		if (crtc->active && wm->hpll_en)
> > +			wm->hpll = wm_state->hpll;
> > +	}
> > +}
> > +
> > +static void g4x_program_watermarks(struct drm_i915_private *dev_priv)
> > +{
> > +	struct g4x_wm_values *old_wm = &dev_priv->wm.g4x;
> > +	struct g4x_wm_values new_wm = {};
> > +
> > +	g4x_merge_wm(dev_priv, &new_wm);
> > +
> > +	if (memcmp(old_wm, &new_wm, sizeof(new_wm)) == 0)
> > +		return;
> > +
> > +	if (is_disabling(old_wm->cxsr, new_wm.cxsr, true))
> > +		_intel_set_memory_cxsr(dev_priv, false);
> > +
> > +	g4x_write_wm_values(dev_priv, &new_wm);
> > +
> > +	if (is_enabling(old_wm->cxsr, new_wm.cxsr, true))
> > +		_intel_set_memory_cxsr(dev_priv, true);
> > +
> > +	*old_wm = new_wm;
> > +}
> > +
> > +static void g4x_initial_watermarks(struct intel_atomic_state *state,
> > +				   struct intel_crtc_state *crtc_state)
> > +{
> > +	struct drm_i915_private *dev_priv = to_i915(crtc_state->base.crtc->dev);
> > +	struct intel_crtc *crtc = to_intel_crtc(crtc_state->base.crtc);
> > +
> > +	mutex_lock(&dev_priv->wm.wm_mutex);
> > +	crtc->wm.active.g4x = crtc_state->wm.g4x.intermediate;
> > +	g4x_program_watermarks(dev_priv);
> > +	mutex_unlock(&dev_priv->wm.wm_mutex);
> > +}
> > +
> > +static void g4x_optimize_watermarks(struct intel_atomic_state *state,
> > +				    struct intel_crtc_state *crtc_state)
> > +{
> > +	struct drm_i915_private *dev_priv = to_i915(crtc_state->base.crtc->dev);
> > +	struct intel_crtc *intel_crtc = to_intel_crtc(crtc_state->base.crtc);
> > +
> > +	if (!crtc_state->wm.need_postvbl_update)
> > +		return;
> > +
> > +	mutex_lock(&dev_priv->wm.wm_mutex);
> > +	intel_crtc->wm.active.g4x = crtc_state->wm.g4x.optimal;
> > +	g4x_program_watermarks(dev_priv);
> > +	mutex_unlock(&dev_priv->wm.wm_mutex);
> > +}
> > +
> >  /* latency must be in 0.1us units. */
> >  static unsigned int vlv_wm_method2(unsigned int pixel_rate,
> >  				   unsigned int htotal,
> > @@ -1673,16 +2079,6 @@ static void vlv_merge_wm(struct drm_i915_private *dev_priv,
> >  	}
> >  }
> >  
> > -static bool is_disabling(int old, int new, int threshold)
> > -{
> > -	return old >= threshold && new < threshold;
> > -}
> > -
> > -static bool is_enabling(int old, int new, int threshold)
> > -{
> > -	return old < threshold && new >= threshold;
> > -}
> > -
> >  static void vlv_program_watermarks(struct drm_i915_private *dev_priv)
> >  {
> >  	struct vlv_wm_values *old_wm = &dev_priv->wm.vlv;
> > @@ -1743,65 +2139,6 @@ static void vlv_optimize_watermarks(struct intel_atomic_state *state,
> >  	mutex_unlock(&dev_priv->wm.wm_mutex);
> >  }
> >  
> > -#define single_plane_enabled(mask) is_power_of_2(mask)
> > -
> > -static void g4x_update_wm(struct intel_crtc *crtc)
> > -{
> > -	struct drm_i915_private *dev_priv = to_i915(crtc->base.dev);
> > -	static const int sr_latency_ns = 12000;
> > -	int planea_wm, planeb_wm, cursora_wm, cursorb_wm;
> > -	int plane_sr, cursor_sr;
> > -	unsigned int enabled = 0;
> > -	bool cxsr_enabled;
> > -
> > -	if (g4x_compute_wm0(dev_priv, PIPE_A,
> > -			    &g4x_wm_info, pessimal_latency_ns,
> > -			    &g4x_cursor_wm_info, pessimal_latency_ns,
> > -			    &planea_wm, &cursora_wm))
> > -		enabled |= 1 << PIPE_A;
> > -
> > -	if (g4x_compute_wm0(dev_priv, PIPE_B,
> > -			    &g4x_wm_info, pessimal_latency_ns,
> > -			    &g4x_cursor_wm_info, pessimal_latency_ns,
> > -			    &planeb_wm, &cursorb_wm))
> > -		enabled |= 1 << PIPE_B;
> > -
> > -	if (single_plane_enabled(enabled) &&
> > -	    g4x_compute_srwm(dev_priv, ffs(enabled) - 1,
> > -			     sr_latency_ns,
> > -			     &g4x_wm_info,
> > -			     &g4x_cursor_wm_info,
> > -			     &plane_sr, &cursor_sr)) {
> > -		cxsr_enabled = true;
> > -	} else {
> > -		cxsr_enabled = false;
> > -		intel_set_memory_cxsr(dev_priv, false);
> > -		plane_sr = cursor_sr = 0;
> > -	}
> > -
> > -	DRM_DEBUG_KMS("Setting FIFO watermarks - A: plane=%d, cursor=%d, "
> > -		      "B: plane=%d, cursor=%d, SR: plane=%d, cursor=%d\n",
> > -		      planea_wm, cursora_wm,
> > -		      planeb_wm, cursorb_wm,
> > -		      plane_sr, cursor_sr);
> > -
> > -	I915_WRITE(DSPFW1,
> > -		   FW_WM(plane_sr, SR) |
> > -		   FW_WM(cursorb_wm, CURSORB) |
> > -		   FW_WM(planeb_wm, PLANEB) |
> > -		   FW_WM(planea_wm, PLANEA));
> > -	I915_WRITE(DSPFW2,
> > -		   (I915_READ(DSPFW2) & ~DSPFW_CURSORA_MASK) |
> > -		   FW_WM(cursora_wm, CURSORA));
> > -	/* HPLL off in SR has some issues on G4x... disable it */
> > -	I915_WRITE(DSPFW3,
> > -		   (I915_READ(DSPFW3) & ~(DSPFW_HPLL_SR_EN | DSPFW_CURSOR_SR_MASK)) |
> > -		   FW_WM(cursor_sr, CURSOR_SR));
> > -
> > -	if (cxsr_enabled)
> > -		intel_set_memory_cxsr(dev_priv, true);
> > -}
> > -
> >  static void i965_update_wm(struct intel_crtc *unused_crtc)
> >  {
> >  	struct drm_i915_private *dev_priv = to_i915(unused_crtc->base.dev);
> > @@ -4778,6 +5115,32 @@ static void ilk_pipe_wm_get_hw_state(struct drm_crtc *crtc)
> >  #define _FW_WM_VLV(value, plane) \
> >  	(((value) & DSPFW_ ## plane ## _MASK_VLV) >> DSPFW_ ## plane ## _SHIFT)
> >  
> > +static void g4x_read_wm_values(struct drm_i915_private *dev_priv,
> > +			       struct g4x_wm_values *wm)
> > +{
> > +	uint32_t tmp;
> > +
> > +	tmp = I915_READ(DSPFW1);
> > +	wm->sr.plane = _FW_WM(tmp, SR);
> > +	wm->pipe[PIPE_B].plane[PLANE_CURSOR] = _FW_WM(tmp, CURSORB);
> > +	wm->pipe[PIPE_B].plane[PLANE_PRIMARY] = _FW_WM(tmp, PLANEB);
> > +	wm->pipe[PIPE_A].plane[PLANE_PRIMARY] = _FW_WM(tmp, PLANEA);
> > +
> > +	tmp = I915_READ(DSPFW2);
> > +	wm->fbc_en = tmp & DSPFW_FBC_SR_EN;
> > +	wm->sr.fbc = _FW_WM(tmp, FBC_SR);
> > +	wm->hpll.fbc = _FW_WM(tmp, FBC_HPLL_SR);
> > +	wm->pipe[PIPE_B].plane[PLANE_SPRITE0] = _FW_WM(tmp, SPRITEB);
> > +	wm->pipe[PIPE_A].plane[PLANE_CURSOR] = _FW_WM(tmp, CURSORA);
> > +	wm->pipe[PIPE_A].plane[PLANE_SPRITE0] = _FW_WM(tmp, SPRITEA);
> > +
> > +	tmp = I915_READ(DSPFW3);
> > +	wm->hpll_en = tmp & DSPFW_HPLL_SR_EN;
> > +	wm->sr.cursor = _FW_WM(tmp, CURSOR_SR);
> > +	wm->hpll.cursor = _FW_WM(tmp, HPLL_CURSOR);
> > +	wm->hpll.plane = _FW_WM(tmp, HPLL_SR);
> > +}
> > +
> >  static void vlv_read_wm_values(struct drm_i915_private *dev_priv,
> >  			       struct vlv_wm_values *wm)
> >  {
> > @@ -4854,6 +5217,147 @@ static void vlv_read_wm_values(struct drm_i915_private *dev_priv,
> >  #undef _FW_WM
> >  #undef _FW_WM_VLV
> >  
> > +void g4x_wm_get_hw_state(struct drm_device *dev)
> > +{
> > +	struct drm_i915_private *dev_priv = to_i915(dev);
> > +	struct g4x_wm_values *wm = &dev_priv->wm.g4x;
> > +	struct intel_crtc *crtc;
> > +
> > +	g4x_read_wm_values(dev_priv, wm);
> > +
> > +	wm->cxsr = I915_READ(FW_BLC_SELF) & FW_BLC_SELF_EN;
> > +
> > +	for_each_intel_crtc(dev, crtc) {
> > +		struct intel_crtc_state *crtc_state =
> > +			to_intel_crtc_state(crtc->base.state);
> > +		struct g4x_wm_state *active = &crtc->wm.active.g4x;
> > +		struct g4x_pipe_wm *raw;
> > +		enum pipe pipe = crtc->pipe;
> > +		enum plane_id plane_id;
> > +		int level, max_level;
> > +
> > +		active->cxsr = wm->cxsr;
> > +		active->hpll_en = wm->hpll_en;
> > +		active->fbc_en = wm->fbc_en;
> > +
> > +		active->sr = wm->sr;
> > +		active->hpll = wm->hpll;
> > +
> > +		for_each_plane_id_on_crtc(crtc, plane_id) {
> > +			active->wm.plane[plane_id] =
> > +				wm->pipe[pipe].plane[plane_id];
> > +		}
> > +
> > +		if (wm->cxsr && wm->hpll_en)
> > +			max_level = G4X_WM_LEVEL_HPLL;
> > +		else if (wm->cxsr)
> > +			max_level = G4X_WM_LEVEL_SR;
> > +		else
> > +			max_level = G4X_WM_LEVEL_NORMAL;
> > +
> > +		level = G4X_WM_LEVEL_NORMAL;
> > +		raw = &crtc_state->wm.g4x.raw[level];
> > +		for_each_plane_id_on_crtc(crtc, plane_id)
> > +			raw->plane[plane_id] = active->wm.plane[plane_id];
> > +
> > +		if (++level > max_level)
> > +			goto out;
> > +
> > +		raw = &crtc_state->wm.g4x.raw[level];
> > +		raw->plane[PLANE_PRIMARY] = active->sr.plane;
> > +		raw->plane[PLANE_CURSOR] = active->sr.cursor;
> > +		raw->plane[PLANE_SPRITE0] = 0;
> > +		raw->fbc = active->sr.fbc;
> > +
> > +		if (++level > max_level)
> > +			goto out;
> > +
> > +		raw = &crtc_state->wm.g4x.raw[level];
> > +		raw->plane[PLANE_PRIMARY] = active->hpll.plane;
> > +		raw->plane[PLANE_CURSOR] = active->hpll.cursor;
> > +		raw->plane[PLANE_SPRITE0] = 0;
> > +		raw->fbc = active->hpll.fbc;
> > +
> > +	out:
> > +		for_each_plane_id_on_crtc(crtc, plane_id)
> > +			g4x_raw_plane_wm_set(crtc_state, level,
> > +					     plane_id, USHRT_MAX);
> > +		g4x_raw_fbc_wm_set(crtc_state, level, USHRT_MAX);
> > +
> > +		crtc_state->wm.g4x.optimal = *active;
> > +		crtc_state->wm.g4x.intermediate = *active;
> > +
> > +		DRM_DEBUG_KMS("Initial watermarks: pipe %c, plane=%d, cursor=%d, sprite=%d\n",
> > +			      pipe_name(pipe),
> > +			      wm->pipe[pipe].plane[PLANE_PRIMARY],
> > +			      wm->pipe[pipe].plane[PLANE_CURSOR],
> > +			      wm->pipe[pipe].plane[PLANE_SPRITE0]);
> > +	}
> > +
> > +	DRM_DEBUG_KMS("Initial SR watermarks: plane=%d, cursor=%d fbc=%d\n",
> > +		      wm->sr.plane, wm->sr.cursor, wm->sr.fbc);
> > +	DRM_DEBUG_KMS("Initial HPLL watermarks: plane=%d, SR cursor=%d fbc=%d\n",
> > +		      wm->hpll.plane, wm->hpll.cursor, wm->hpll.fbc);
> > +	DRM_DEBUG_KMS("Initial SR=%s HPLL=%s FBC=%s\n",
> > +		      yesno(wm->cxsr), yesno(wm->hpll_en), yesno(wm->fbc_en));
> > +}
> > +
> > +void g4x_wm_sanitize(struct drm_i915_private *dev_priv)
> > +{
> > +	struct intel_plane *plane;
> > +	struct intel_crtc *crtc;
> > +
> > +	mutex_lock(&dev_priv->wm.wm_mutex);
> > +
> > +	for_each_intel_plane(&dev_priv->drm, plane) {
> > +		struct intel_crtc *crtc =
> > +			intel_get_crtc_for_pipe(dev_priv, plane->pipe);
> > +		struct intel_crtc_state *crtc_state =
> > +			to_intel_crtc_state(crtc->base.state);
> > +		struct intel_plane_state *plane_state =
> > +			to_intel_plane_state(plane->base.state);
> > +		struct g4x_wm_state *wm_state = &crtc_state->wm.g4x.optimal;
> > +		enum plane_id plane_id = plane->id;
> > +		int level;
> > +
> > +		if (plane_state->base.visible)
> > +			continue;
> > +
> > +		for (level = 0; level < 3; level++) {
> > +			struct g4x_pipe_wm *raw =
> > +				&crtc_state->wm.g4x.raw[level];
> > +
> > +			raw->plane[plane_id] = 0;
> > +			wm_state->wm.plane[plane_id] = 0;
> > +		}
> > +
> > +		if (plane_id == PLANE_PRIMARY) {
> > +			for (level = 0; level < 3; level++) {
> > +				struct g4x_pipe_wm *raw =
> > +					&crtc_state->wm.g4x.raw[level];
> > +				raw->fbc = 0;
> > +			}
> > +
> > +			wm_state->sr.fbc = 0;
> > +			wm_state->hpll.fbc = 0;
> > +			wm_state->fbc_en = false;
> > +		}
> > +	}
> > +
> > +	for_each_intel_crtc(&dev_priv->drm, crtc) {
> > +		struct intel_crtc_state *crtc_state =
> > +			to_intel_crtc_state(crtc->base.state);
> > +
> > +		crtc_state->wm.g4x.intermediate =
> > +			crtc_state->wm.g4x.optimal;
> > +		crtc->wm.active.g4x = crtc_state->wm.g4x.optimal;
> > +	}
> > +
> > +	g4x_program_watermarks(dev_priv);
> > +
> > +	mutex_unlock(&dev_priv->wm.wm_mutex);
> > +}
> > +
> >  void vlv_wm_get_hw_state(struct drm_device *dev)
> >  {
> >  	struct drm_i915_private *dev_priv = to_i915(dev);
> > @@ -8160,6 +8664,12 @@ void intel_init_pm(struct drm_i915_private *dev_priv)
> >  		dev_priv->display.initial_watermarks = vlv_initial_watermarks;
> >  		dev_priv->display.optimize_watermarks = vlv_optimize_watermarks;
> >  		dev_priv->display.atomic_update_watermarks = vlv_atomic_update_fifo;
> > +	} else if (IS_G4X(dev_priv)) {
> > +		g4x_setup_wm_latency(dev_priv);
> > +		dev_priv->display.compute_pipe_wm = g4x_compute_pipe_wm;
> > +		dev_priv->display.compute_intermediate_wm = g4x_compute_intermediate_wm;
> > +		dev_priv->display.initial_watermarks = g4x_initial_watermarks;
> > +		dev_priv->display.optimize_watermarks = g4x_optimize_watermarks;
> >  	} else if (IS_PINEVIEW(dev_priv)) {
> >  		if (!intel_get_cxsr_latency(IS_PINEVIEW_G(dev_priv),
> >  					    dev_priv->is_ddr3,
> > @@ -8175,8 +8685,6 @@ void intel_init_pm(struct drm_i915_private *dev_priv)
> >  			dev_priv->display.update_wm = NULL;
> >  		} else
> >  			dev_priv->display.update_wm = pineview_update_wm;
> > -	} else if (IS_G4X(dev_priv)) {
> > -		dev_priv->display.update_wm = g4x_update_wm;
> >  	} else if (IS_GEN4(dev_priv)) {
> >  		dev_priv->display.update_wm = i965_update_wm;
> >  	} else if (IS_GEN3(dev_priv)) {
> 

-- 
Ville Syrjälä
Intel OTC
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 21+ messages in thread

end of thread, other threads:[~2017-05-10 16:40 UTC | newest]

Thread overview: 21+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-04-21 18:14 [PATCH 00/15] drm/i915: Two stage watermarks for g4x ville.syrjala
2017-04-21 18:14 ` [PATCH 01/15] drm/i915: s/vlv_plane_wm_compute/vlv_raw_plane_wm_compute/ etc ville.syrjala
2017-04-21 18:14 ` [PATCH 02/15] drm/i915: Drop the debug message from vlv_get_fifo_size() ville.syrjala
2017-04-21 18:14 ` [PATCH 03/15] drm/i915: s/vlv_num_wm_levels/intel_wm_num_levels/ ville.syrjala
2017-04-21 18:14 ` [PATCH 04/15] drm/i915: Rename bunch of vlv_ watermark structures to g4x_ ville.syrjala
2017-04-21 18:14 ` [PATCH 05/15] drm/i915: Make vlv/chv watermark debug print less cryptic ville.syrjala
2017-04-21 18:14 ` [PATCH 06/15] drm/i915: Document CxSR ville.syrjala
2017-04-21 18:14 ` [PATCH 07/15] drm/i915: Fix cursor 'cpp' in watermark calculatins for old platforms ville.syrjala
2017-04-21 18:14 ` [PATCH 08/15] drm/i915: Fix the g4x watermark TLB miss workaround ville.syrjala
2017-04-21 18:14 ` [PATCH 09/15] drm/i915: Refactor the g4x TLB miss w/a to a helper ville.syrjala
2017-04-21 18:14 ` [PATCH 10/15] drm/i915: Refactor wm calculations ville.syrjala
2017-04-21 18:14 ` [PATCH 11/15] drm/i915: Apply the g4x TLB miss w/a to SR watermarks as well ville.syrjala
2017-04-21 18:14 ` [PATCH 12/15] drm/i915: Two stage watermarks for g4x ville.syrjala
2017-04-24  7:34   ` Maarten Lankhorst
2017-04-24 13:16     ` Ville Syrjälä
2017-05-10 16:40     ` Ville Syrjälä
2017-04-21 18:14 ` [PATCH 13/15] drm/i915: Enable HPLL watermarks on g4x ville.syrjala
2017-04-21 18:14 ` [PATCH 14/15] drm/i915: Add g4x watermark tracepoint ville.syrjala
2017-04-21 18:14 ` [PATCH 15/15] drm/i915: Add support for sprites on g4x ville.syrjala
2017-04-21 18:36 ` ✓ Fi.CI.BAT: success for drm/i915: Two stage watermarks for g4x Patchwork
2017-04-24 14:20 ` [PATCH 00/15] " Lofstedt, Marta

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.