[Intel-gfx] [PATCH 30/35] drm/i915: Replace the ILK/SNB/IVB/HSW watermark code

ville.syrjala at linux.intel.com ville.syrjala at linux.intel.com
Fri Jul 5 10:57:42 CEST 2013


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

There is a major problem with the watermark registers; they're not
double buffered. So we need to make sure we update them at the correct
time when messing about with planes. The correct time is the beginning
of vblank.

So when we determine that the watermarks need to updated hand in hand
with the next vblank, we store the pre-computed watermarks under
intel_crtc, and when the vblank happens, we promote the pending
watermarks to active status.

on HSW when the watermarks for any pipe change, we must merge the
watermarks from all pipes so that we can determine the correct LP1+
watermark levels. For simplicity we follow the same codepaths for
pre-HSW hardware as well, but there all the LP1+ watermarks will be
disabled when multiple pipes are enabled. Once the watermarks are
merged we check them for validity, disabling any invalid levels.

Touching the watermark registers causes the hardware to re-evaluate the
watermarks, which expeds some power. So after merging the watermarks
we check which watermark registers actually need to be changed. And
finally we write the watermarks registers in the correct order.

Signed-off-by: Ville Syrjälä <ville.syrjala at linux.intel.com>
---
 drivers/gpu/drm/i915/i915_drv.h  |   12 +
 drivers/gpu/drm/i915/i915_irq.c  |   12 +-
 drivers/gpu/drm/i915/i915_reg.h  |    2 +
 drivers/gpu/drm/i915/intel_drv.h |   22 +
 drivers/gpu/drm/i915/intel_pm.c  | 1546 ++++++++++++++++----------------------
 5 files changed, 686 insertions(+), 908 deletions(-)

diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
index fd57bed..446be9a 100644
--- a/drivers/gpu/drm/i915/i915_drv.h
+++ b/drivers/gpu/drm/i915/i915_drv.h
@@ -1198,6 +1198,7 @@ typedef struct drm_i915_private {
 
 	struct i915_suspend_saved_registers regfile;
 
+	/* per-device watermark state */
 	struct {
 		/* watermark latency values for primary */
 		uint16_t pri_latency[5];
@@ -1205,6 +1206,17 @@ typedef struct drm_i915_private {
 		uint16_t spr_latency[5];
 		/* watermark latency values for cursor */
 		uint16_t cur_latency[5];
+		/* protects all watermark state */
+		spinlock_t lock;
+		/* current state of FBC_WM */
+		bool fbc_wm_enabled;
+		/* current state of DDB partitioning */
+		enum intel_ddb_partitioning ddb_partitioning;
+		/*
+		 * LP1+ values currently programmed into the hardware
+		 * [0] = LP1, [1] = LP2, [2] = LP3
+		 */
+		struct intel_wm_level hw[3];
 	} wm;
 
 	/* Old dri1 support infrastructure, beware the dragons ya fools entering
diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c
index 4c1b1e3..ee5127f 100644
--- a/drivers/gpu/drm/i915/i915_irq.c
+++ b/drivers/gpu/drm/i915/i915_irq.c
@@ -1240,8 +1240,10 @@ static irqreturn_t ivybridge_irq_handler(int irq, void *arg)
 			intel_opregion_asle_intr(dev);
 
 		for (i = 0; i < 3; i++) {
-			if (de_iir & (DE_PIPEA_VBLANK_IVB << (5 * i)))
+			if (de_iir & (DE_PIPEA_VBLANK_IVB << (5 * i))) {
+				ilk_update_pipe_wm(dev, i);
 				drm_handle_vblank(dev, i);
+			}
 			if (de_iir & (DE_PLANEA_FLIP_DONE_IVB << (5 * i))) {
 				intel_prepare_page_flip(dev, i);
 				intel_finish_page_flip_plane(dev, i);
@@ -1343,11 +1345,15 @@ static irqreturn_t ironlake_irq_handler(int irq, void *arg)
 	if (de_iir & DE_GSE)
 		intel_opregion_asle_intr(dev);
 
-	if (de_iir & DE_PIPEA_VBLANK)
+	if (de_iir & DE_PIPEA_VBLANK) {
+		ilk_update_pipe_wm(dev, 0);
 		drm_handle_vblank(dev, 0);
+	}
 
-	if (de_iir & DE_PIPEB_VBLANK)
+	if (de_iir & DE_PIPEB_VBLANK) {
+		ilk_update_pipe_wm(dev, 1);
 		drm_handle_vblank(dev, 1);
+	}
 
 	if (de_iir & DE_POISON)
 		DRM_ERROR("Poison interrupt\n");
diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h
index 9b51be8..e595f54 100644
--- a/drivers/gpu/drm/i915/i915_reg.h
+++ b/drivers/gpu/drm/i915/i915_reg.h
@@ -3762,6 +3762,8 @@
 #define DISP_ARB_CTL	0x45000
 #define  DISP_TILE_SURFACE_SWIZZLING	(1<<13)
 #define  DISP_FBC_WM_DIS		(1<<15)
+#define DISP_ARB_CTL2	0x45004
+#define  DISP_DATA_PARTITION_5_6	(1<<6)
 #define GEN7_MSG_CTL	0x45010
 #define  WAIT_FOR_PCH_RESET_ACK		(1<<1)
 #define  WAIT_FOR_PCH_FLR_ACK		(1<<0)
diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
index 4afaeb2..a5c15ab 100644
--- a/drivers/gpu/drm/i915/intel_drv.h
+++ b/drivers/gpu/drm/i915/intel_drv.h
@@ -292,6 +292,15 @@ struct intel_crtc_config {
 	bool ips_enabled;
 };
 
+struct intel_pipe_wm {
+	bool pipe_enabled;
+	bool sprites_enabled;
+	bool sprites_scaled;
+	bool fbc_wm_enabled;
+	uint32_t linetime;
+	struct intel_wm_level wm[5];
+};
+
 struct intel_crtc {
 	struct drm_crtc base;
 	enum pipe pipe;
@@ -332,6 +341,18 @@ struct intel_crtc {
 	/* Access to these should be protected by dev_priv->irq_lock. */
 	bool cpu_fifo_underrun_disabled;
 	bool pch_fifo_underrun_disabled;
+
+	/* per-pipe watermark state */
+	struct {
+		/* watermarks queued for next vblank */
+		struct intel_pipe_wm pending;
+		/* watermarks currently being used  */
+		struct intel_pipe_wm active;
+		/* LP0 values currently programmed into the hardware */
+		struct intel_wm_level hw;
+		/* indicates that 'pending' contains changed watermarks */
+		bool dirty;
+	} wm;
 };
 
 struct intel_plane_wm_parameters {
@@ -838,5 +859,6 @@ extern bool intel_set_cpu_fifo_underrun_reporting(struct drm_device *dev,
 extern bool intel_set_pch_fifo_underrun_reporting(struct drm_device *dev,
 						 enum transcoder pch_transcoder,
 						 bool enable);
+extern void ilk_update_pipe_wm(struct drm_device *dev, enum pipe pipe);
 
 #endif /* __INTEL_DRV_H__ */
diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c
index c4e94bb..7fd922dc 100644
--- a/drivers/gpu/drm/i915/intel_pm.c
+++ b/drivers/gpu/drm/i915/intel_pm.c
@@ -32,6 +32,11 @@
 #include <linux/module.h>
 #include <drm/i915_powerwell.h>
 
+static const char *yesno(int v)
+{
+	return v ? "yes" : "no";
+}
+
 #define FORCEWAKE_ACK_TIMEOUT_MS 2
 
 /* FBC, or Frame Buffer Compression, is a technique employed to compress the
@@ -1668,424 +1673,6 @@ static void i830_update_wm(struct drm_crtc *crtc)
 	I915_WRITE(FW_BLC, fwater_lo);
 }
 
-#define ILK_LP0_PLANE_LATENCY		700
-#define ILK_LP0_CURSOR_LATENCY		1300
-
-/*
- * 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 ironlake_check_srwm(struct drm_device *dev, int level,
-				int fbc_wm, int display_wm, int cursor_wm,
-				const struct intel_watermark_params *display,
-				const struct intel_watermark_params *cursor)
-{
-	struct drm_i915_private *dev_priv = dev->dev_private;
-
-	DRM_DEBUG_KMS("watermark %d: display plane %d, fbc lines %d,"
-		      " cursor %d\n", level, display_wm, fbc_wm, cursor_wm);
-
-	if (fbc_wm > SNB_FBC_MAX_SRWM) {
-		DRM_DEBUG_KMS("fbc watermark(%d) is too large(%d), disabling wm%d+\n",
-			      fbc_wm, SNB_FBC_MAX_SRWM, level);
-
-		/* fbc has it's own way to disable FBC WM */
-		I915_WRITE(DISP_ARB_CTL,
-			   I915_READ(DISP_ARB_CTL) | DISP_FBC_WM_DIS);
-		return false;
-	} else if (INTEL_INFO(dev)->gen >= 6) {
-		/* enable FBC WM (except on ILK, where it must remain off) */
-		I915_WRITE(DISP_ARB_CTL,
-			   I915_READ(DISP_ARB_CTL) & ~DISP_FBC_WM_DIS);
-	}
-
-	if (display_wm > display->max_wm) {
-		DRM_DEBUG_KMS("display watermark(%d) is too large(%d), disabling wm%d+\n",
-			      display_wm, SNB_DISPLAY_MAX_SRWM, level);
-		return false;
-	}
-
-	if (cursor_wm > cursor->max_wm) {
-		DRM_DEBUG_KMS("cursor watermark(%d) is too large(%d), disabling wm%d+\n",
-			      cursor_wm, SNB_CURSOR_MAX_SRWM, level);
-		return false;
-	}
-
-	if (!(fbc_wm || display_wm || cursor_wm)) {
-		DRM_DEBUG_KMS("latency %d is 0, disabling wm%d+\n", level, level);
-		return false;
-	}
-
-	return true;
-}
-
-/*
- * Compute watermark values of WM[1-3],
- */
-static bool ironlake_compute_srwm(struct drm_device *dev, int level, int plane,
-				  int latency_ns,
-				  const struct intel_watermark_params *display,
-				  const struct intel_watermark_params *cursor,
-				  int *fbc_wm, int *display_wm, int *cursor_wm)
-{
-	struct drm_crtc *crtc;
-	unsigned long line_time_us;
-	int hdisplay, htotal, pixel_size, clock;
-	int line_count, line_size;
-	int small, large;
-	int entries;
-
-	if (!latency_ns) {
-		*fbc_wm = *display_wm = *cursor_wm = 0;
-		return false;
-	}
-
-	crtc = intel_get_crtc_for_plane(dev, plane);
-	hdisplay = crtc->mode.hdisplay;
-	htotal = crtc->mode.htotal;
-	clock = crtc->mode.clock;
-	pixel_size = crtc->fb->bits_per_pixel / 8;
-
-	line_time_us = (htotal * 1000) / clock;
-	line_count = (latency_ns / line_time_us + 1000) / 1000;
-	line_size = hdisplay * pixel_size;
-
-	/* Use the minimum of the small and large buffer method for primary */
-	small = ((clock * pixel_size / 1000) * latency_ns) / 1000;
-	large = line_count * line_size;
-
-	entries = DIV_ROUND_UP(min(small, large), display->cacheline_size);
-	*display_wm = entries + display->guard_size;
-
-	/*
-	 * Spec says:
-	 * FBC WM = ((Final Primary WM * 64) / number of bytes per line) + 2
-	 */
-	*fbc_wm = DIV_ROUND_UP(*display_wm * 64, line_size) + 2;
-
-	/* calculate the self-refresh watermark for display cursor */
-	entries = line_count * pixel_size * 64;
-	entries = DIV_ROUND_UP(entries, cursor->cacheline_size);
-	*cursor_wm = entries + cursor->guard_size;
-
-	return ironlake_check_srwm(dev, level,
-				   *fbc_wm, *display_wm, *cursor_wm,
-				   display, cursor);
-}
-
-static void ironlake_update_wm(struct drm_crtc *crtc)
-{
-	struct drm_device *dev = crtc->dev;
-	struct drm_i915_private *dev_priv = dev->dev_private;
-	int fbc_wm, plane_wm, cursor_wm;
-	unsigned int enabled;
-
-	enabled = 0;
-	if (g4x_compute_wm0(dev, PIPE_A,
-			    &ironlake_display_wm_info,
-			    ILK_LP0_PLANE_LATENCY,
-			    &ironlake_cursor_wm_info,
-			    ILK_LP0_CURSOR_LATENCY,
-			    &plane_wm, &cursor_wm)) {
-		I915_WRITE(WM0_PIPEA_ILK,
-			   (plane_wm << WM0_PIPE_PLANE_SHIFT) | cursor_wm);
-		DRM_DEBUG_KMS("FIFO watermarks For pipe A -"
-			      " plane %d, " "cursor: %d\n",
-			      plane_wm, cursor_wm);
-		enabled |= 1 << PIPE_A;
-	}
-
-	if (g4x_compute_wm0(dev, PIPE_B,
-			    &ironlake_display_wm_info,
-			    ILK_LP0_PLANE_LATENCY,
-			    &ironlake_cursor_wm_info,
-			    ILK_LP0_CURSOR_LATENCY,
-			    &plane_wm, &cursor_wm)) {
-		I915_WRITE(WM0_PIPEB_ILK,
-			   (plane_wm << WM0_PIPE_PLANE_SHIFT) | cursor_wm);
-		DRM_DEBUG_KMS("FIFO watermarks For pipe B -"
-			      " plane %d, cursor: %d\n",
-			      plane_wm, cursor_wm);
-		enabled |= 1 << PIPE_B;
-	}
-
-	/*
-	 * Calculate and update the self-refresh watermark only when one
-	 * display plane is used.
-	 */
-	I915_WRITE(WM3_LP_ILK, 0);
-	I915_WRITE(WM2_LP_ILK, 0);
-	I915_WRITE(WM1_LP_ILK, 0);
-
-	if (!single_plane_enabled(enabled))
-		return;
-	enabled = ffs(enabled) - 1;
-
-	/* WM1 */
-	if (!ironlake_compute_srwm(dev, 1, enabled,
-				   ILK_READ_WM1_LATENCY() * 500,
-				   &ironlake_display_srwm_info,
-				   &ironlake_cursor_srwm_info,
-				   &fbc_wm, &plane_wm, &cursor_wm))
-		return;
-
-	I915_WRITE(WM1_LP_ILK,
-		   WM1_LP_SR_EN |
-		   (ILK_READ_WM1_LATENCY() << WM1_LP_LATENCY_SHIFT) |
-		   (fbc_wm << WM1_LP_FBC_SHIFT) |
-		   (plane_wm << WM1_LP_SR_SHIFT) |
-		   cursor_wm);
-
-	/* WM2 */
-	if (!ironlake_compute_srwm(dev, 2, enabled,
-				   ILK_READ_WM2_LATENCY() * 500,
-				   &ironlake_display_srwm_info,
-				   &ironlake_cursor_srwm_info,
-				   &fbc_wm, &plane_wm, &cursor_wm))
-		return;
-
-	I915_WRITE(WM2_LP_ILK,
-		   WM2_LP_EN |
-		   (ILK_READ_WM2_LATENCY() << WM1_LP_LATENCY_SHIFT) |
-		   (fbc_wm << WM1_LP_FBC_SHIFT) |
-		   (plane_wm << WM1_LP_SR_SHIFT) |
-		   cursor_wm);
-
-	/*
-	 * WM3 is unsupported on ILK, probably because we don't have latency
-	 * data for that power state
-	 */
-}
-
-static void sandybridge_update_wm(struct drm_crtc *crtc)
-{
-	struct drm_device *dev = crtc->dev;
-	struct drm_i915_private *dev_priv = dev->dev_private;
-	int latency = SNB_READ_WM0_LATENCY() * 100;	/* In unit 0.1us */
-	u32 val;
-	int fbc_wm, plane_wm, cursor_wm;
-	unsigned int enabled;
-
-	enabled = 0;
-	if (g4x_compute_wm0(dev, PIPE_A,
-			    &sandybridge_display_wm_info, latency,
-			    &sandybridge_cursor_wm_info, latency,
-			    &plane_wm, &cursor_wm)) {
-		val = I915_READ(WM0_PIPEA_ILK);
-		val &= ~(WM0_PIPE_PLANE_MASK | WM0_PIPE_CURSOR_MASK);
-		I915_WRITE(WM0_PIPEA_ILK, val |
-			   ((plane_wm << WM0_PIPE_PLANE_SHIFT) | cursor_wm));
-		DRM_DEBUG_KMS("FIFO watermarks For pipe A -"
-			      " plane %d, " "cursor: %d\n",
-			      plane_wm, cursor_wm);
-		enabled |= 1 << PIPE_A;
-	}
-
-	if (g4x_compute_wm0(dev, PIPE_B,
-			    &sandybridge_display_wm_info, latency,
-			    &sandybridge_cursor_wm_info, latency,
-			    &plane_wm, &cursor_wm)) {
-		val = I915_READ(WM0_PIPEB_ILK);
-		val &= ~(WM0_PIPE_PLANE_MASK | WM0_PIPE_CURSOR_MASK);
-		I915_WRITE(WM0_PIPEB_ILK, val |
-			   ((plane_wm << WM0_PIPE_PLANE_SHIFT) | cursor_wm));
-		DRM_DEBUG_KMS("FIFO watermarks For pipe B -"
-			      " plane %d, cursor: %d\n",
-			      plane_wm, cursor_wm);
-		enabled |= 1 << PIPE_B;
-	}
-
-	/*
-	 * Calculate and update the self-refresh watermark only when one
-	 * display plane is used.
-	 *
-	 * SNB support 3 levels of watermark.
-	 *
-	 * WM1/WM2/WM2 watermarks have to be enabled in the ascending order,
-	 * and disabled in the descending order
-	 *
-	 */
-	I915_WRITE(WM3_LP_ILK, 0);
-	I915_WRITE(WM2_LP_ILK, 0);
-	I915_WRITE(WM1_LP_ILK, 0);
-
-	if (!single_plane_enabled(enabled) ||
-	    dev_priv->sprite_scaling_enabled)
-		return;
-	enabled = ffs(enabled) - 1;
-
-	/* WM1 */
-	if (!ironlake_compute_srwm(dev, 1, enabled,
-				   SNB_READ_WM1_LATENCY() * 500,
-				   &sandybridge_display_srwm_info,
-				   &sandybridge_cursor_srwm_info,
-				   &fbc_wm, &plane_wm, &cursor_wm))
-		return;
-
-	I915_WRITE(WM1_LP_ILK,
-		   WM1_LP_SR_EN |
-		   (SNB_READ_WM1_LATENCY() << WM1_LP_LATENCY_SHIFT) |
-		   (fbc_wm << WM1_LP_FBC_SHIFT) |
-		   (plane_wm << WM1_LP_SR_SHIFT) |
-		   cursor_wm);
-
-	/* WM2 */
-	if (!ironlake_compute_srwm(dev, 2, enabled,
-				   SNB_READ_WM2_LATENCY() * 500,
-				   &sandybridge_display_srwm_info,
-				   &sandybridge_cursor_srwm_info,
-				   &fbc_wm, &plane_wm, &cursor_wm))
-		return;
-
-	I915_WRITE(WM2_LP_ILK,
-		   WM2_LP_EN |
-		   (SNB_READ_WM2_LATENCY() << WM1_LP_LATENCY_SHIFT) |
-		   (fbc_wm << WM1_LP_FBC_SHIFT) |
-		   (plane_wm << WM1_LP_SR_SHIFT) |
-		   cursor_wm);
-
-	/* WM3 */
-	if (!ironlake_compute_srwm(dev, 3, enabled,
-				   SNB_READ_WM3_LATENCY() * 500,
-				   &sandybridge_display_srwm_info,
-				   &sandybridge_cursor_srwm_info,
-				   &fbc_wm, &plane_wm, &cursor_wm))
-		return;
-
-	I915_WRITE(WM3_LP_ILK,
-		   WM3_LP_EN |
-		   (SNB_READ_WM3_LATENCY() << WM1_LP_LATENCY_SHIFT) |
-		   (fbc_wm << WM1_LP_FBC_SHIFT) |
-		   (plane_wm << WM1_LP_SR_SHIFT) |
-		   cursor_wm);
-}
-
-static void ivybridge_update_wm(struct drm_crtc *crtc)
-{
-	struct drm_device *dev = crtc->dev;
-	struct drm_i915_private *dev_priv = dev->dev_private;
-	int latency = SNB_READ_WM0_LATENCY() * 100;	/* In unit 0.1us */
-	u32 val;
-	int fbc_wm, plane_wm, cursor_wm;
-	int ignore_fbc_wm, ignore_plane_wm, ignore_cursor_wm;
-	unsigned int enabled;
-
-	enabled = 0;
-	if (g4x_compute_wm0(dev, PIPE_A,
-			    &sandybridge_display_wm_info, latency,
-			    &sandybridge_cursor_wm_info, latency,
-			    &plane_wm, &cursor_wm)) {
-		val = I915_READ(WM0_PIPEA_ILK);
-		val &= ~(WM0_PIPE_PLANE_MASK | WM0_PIPE_CURSOR_MASK);
-		I915_WRITE(WM0_PIPEA_ILK, val |
-			   ((plane_wm << WM0_PIPE_PLANE_SHIFT) | cursor_wm));
-		DRM_DEBUG_KMS("FIFO watermarks For pipe A -"
-			      " plane %d, " "cursor: %d\n",
-			      plane_wm, cursor_wm);
-		enabled |= 1 << PIPE_A;
-	}
-
-	if (g4x_compute_wm0(dev, PIPE_B,
-			    &sandybridge_display_wm_info, latency,
-			    &sandybridge_cursor_wm_info, latency,
-			    &plane_wm, &cursor_wm)) {
-		val = I915_READ(WM0_PIPEB_ILK);
-		val &= ~(WM0_PIPE_PLANE_MASK | WM0_PIPE_CURSOR_MASK);
-		I915_WRITE(WM0_PIPEB_ILK, val |
-			   ((plane_wm << WM0_PIPE_PLANE_SHIFT) | cursor_wm));
-		DRM_DEBUG_KMS("FIFO watermarks For pipe B -"
-			      " plane %d, cursor: %d\n",
-			      plane_wm, cursor_wm);
-		enabled |= 1 << PIPE_B;
-	}
-
-	if (g4x_compute_wm0(dev, PIPE_C,
-			    &sandybridge_display_wm_info, latency,
-			    &sandybridge_cursor_wm_info, latency,
-			    &plane_wm, &cursor_wm)) {
-		val = I915_READ(WM0_PIPEC_IVB);
-		val &= ~(WM0_PIPE_PLANE_MASK | WM0_PIPE_CURSOR_MASK);
-		I915_WRITE(WM0_PIPEC_IVB, val |
-			   ((plane_wm << WM0_PIPE_PLANE_SHIFT) | cursor_wm));
-		DRM_DEBUG_KMS("FIFO watermarks For pipe C -"
-			      " plane %d, cursor: %d\n",
-			      plane_wm, cursor_wm);
-		enabled |= 1 << PIPE_C;
-	}
-
-	/*
-	 * Calculate and update the self-refresh watermark only when one
-	 * display plane is used.
-	 *
-	 * SNB support 3 levels of watermark.
-	 *
-	 * WM1/WM2/WM2 watermarks have to be enabled in the ascending order,
-	 * and disabled in the descending order
-	 *
-	 */
-	I915_WRITE(WM3_LP_ILK, 0);
-	I915_WRITE(WM2_LP_ILK, 0);
-	I915_WRITE(WM1_LP_ILK, 0);
-
-	if (!single_plane_enabled(enabled) ||
-	    dev_priv->sprite_scaling_enabled)
-		return;
-	enabled = ffs(enabled) - 1;
-
-	/* WM1 */
-	if (!ironlake_compute_srwm(dev, 1, enabled,
-				   SNB_READ_WM1_LATENCY() * 500,
-				   &sandybridge_display_srwm_info,
-				   &sandybridge_cursor_srwm_info,
-				   &fbc_wm, &plane_wm, &cursor_wm))
-		return;
-
-	I915_WRITE(WM1_LP_ILK,
-		   WM1_LP_SR_EN |
-		   (SNB_READ_WM1_LATENCY() << WM1_LP_LATENCY_SHIFT) |
-		   (fbc_wm << WM1_LP_FBC_SHIFT) |
-		   (plane_wm << WM1_LP_SR_SHIFT) |
-		   cursor_wm);
-
-	/* WM2 */
-	if (!ironlake_compute_srwm(dev, 2, enabled,
-				   SNB_READ_WM2_LATENCY() * 500,
-				   &sandybridge_display_srwm_info,
-				   &sandybridge_cursor_srwm_info,
-				   &fbc_wm, &plane_wm, &cursor_wm))
-		return;
-
-	I915_WRITE(WM2_LP_ILK,
-		   WM2_LP_EN |
-		   (SNB_READ_WM2_LATENCY() << WM1_LP_LATENCY_SHIFT) |
-		   (fbc_wm << WM1_LP_FBC_SHIFT) |
-		   (plane_wm << WM1_LP_SR_SHIFT) |
-		   cursor_wm);
-
-	/* WM3, note we have to correct the cursor latency */
-	if (!ironlake_compute_srwm(dev, 3, enabled,
-				   SNB_READ_WM3_LATENCY() * 500,
-				   &sandybridge_display_srwm_info,
-				   &sandybridge_cursor_srwm_info,
-				   &fbc_wm, &plane_wm, &ignore_cursor_wm) ||
-	    !ironlake_compute_srwm(dev, 3, enabled,
-				   2 * SNB_READ_WM3_LATENCY() * 500,
-				   &sandybridge_display_srwm_info,
-				   &sandybridge_cursor_srwm_info,
-				   &ignore_fbc_wm, &ignore_plane_wm, &cursor_wm))
-		return;
-
-	I915_WRITE(WM3_LP_ILK,
-		   WM3_LP_EN |
-		   (SNB_READ_WM3_LATENCY() << WM1_LP_LATENCY_SHIFT) |
-		   (fbc_wm << WM1_LP_FBC_SHIFT) |
-		   (plane_wm << WM1_LP_SR_SHIFT) |
-		   cursor_wm);
-}
-
 static uint32_t ilk_pipe_pixel_rate(struct drm_device *dev,
 				    struct drm_crtc *crtc)
 {
@@ -2168,14 +1755,6 @@ struct hsw_wm_maximums {
 	uint16_t fbc;
 };
 
-struct hsw_wm_values {
-	uint32_t wm_pipe[3];
-	uint32_t wm_lp[3];
-	uint32_t wm_lp_spr[3];
-	uint32_t wm_linetime[3];
-	bool enable_fbc_wm;
-};
-
 /* used in computing the new watermarks state */
 struct intel_wm_config {
 	unsigned int pipes_active;
@@ -2351,6 +1930,16 @@ static void ilk_wm_max(struct drm_device *dev,
 	max->fbc = ilk_fbc_wm_max();
 }
 
+/*
+ * We use memcmp() to determine if watermarks need updating,
+ * so keeping all disabled watermark levels consistently
+ * zeroed avoids false positives.
+ */
+static void ilk_disable_wm_level(struct intel_wm_level *wm)
+{
+	memset(wm, 0, sizeof(*wm));
+}
+
 static bool ilk_check_wm(int level,
 			 const struct hsw_wm_maximums *max,
 			 struct intel_wm_level *result)
@@ -2381,6 +1970,9 @@ static bool ilk_check_wm(int level,
 
 	DRM_DEBUG_KMS("WM%d: %sabled\n", level, result->enable ? "en" : "dis");
 
+	if (!result->enable)
+		ilk_disable_wm_level(result);
+
 	return ret;
 }
 
@@ -2406,52 +1998,603 @@ static void ilk_compute_wm_level(struct drm_i915_private *dev_priv,
 	result->enable = true;
 }
 
-static bool hsw_compute_lp_wm(struct drm_i915_private *dev_priv,
-			      int level, struct hsw_wm_maximums *max,
-			      struct hsw_pipe_wm_parameters *params,
-			      struct intel_wm_level *result)
-{
-	enum pipe pipe;
-	struct intel_wm_level res[3];
-
-	for (pipe = PIPE_A; pipe <= PIPE_C; pipe++)
-		ilk_compute_wm_level(dev_priv, level, &params[pipe], &res[pipe]);
-
-	result->pri_val = max3(res[0].pri_val, res[1].pri_val, res[2].pri_val);
-	result->spr_val = max3(res[0].spr_val, res[1].spr_val, res[2].spr_val);
-	result->cur_val = max3(res[0].cur_val, res[1].cur_val, res[2].cur_val);
-	result->fbc_val = max3(res[0].fbc_val, res[1].fbc_val, res[2].fbc_val);
-	result->enable = true;
-
-	return ilk_check_wm(level, max, result);
-}
-
-static uint32_t hsw_compute_wm_pipe(struct drm_i915_private *dev_priv,
-				    enum pipe pipe,
-				    struct hsw_pipe_wm_parameters *params)
-{
-	uint32_t pri_val, cur_val, spr_val;
-	uint16_t pri_latency = dev_priv->wm.pri_latency[0];
-	uint16_t spr_latency = dev_priv->wm.spr_latency[0];
-	uint16_t cur_latency = dev_priv->wm.cur_latency[0];
-
-	pri_val = ilk_compute_pri_wm(params, pri_latency, 0);
-	spr_val = ilk_compute_spr_wm(params, spr_latency);
-	cur_val = ilk_compute_cur_wm(params, cur_latency);
-
-	WARN(pri_val > 127,
-	     "Primary WM error, mode not supported for pipe %c\n",
-	     pipe_name(pipe));
-	WARN(spr_val > 127,
-	     "Sprite WM error, mode not supported for pipe %c\n",
-	     pipe_name(pipe));
-	WARN(cur_val > 63,
-	     "Cursor WM error, mode not supported for pipe %c\n",
-	     pipe_name(pipe));
-
-	return (pri_val << WM0_PIPE_PLANE_SHIFT) |
-	       (spr_val << WM0_PIPE_SPRITE_SHIFT) |
-	       cur_val;
+/* The value we need to program into the WM_LPx latency field */
+static unsigned int ilk_wm_lp_latency(struct drm_device *dev, int level)
+{
+	struct drm_i915_private *dev_priv = dev->dev_private;
+
+	if (INTEL_INFO(dev)->gen > 7 || IS_HASWELL(dev))
+		return 2 * level;
+	else
+		return dev_priv->wm.pri_latency[level];
+}
+
+/*
+ * Merge the watermarks from all active pipes for a specific level.
+ */
+static void ilk_merge_wm_level(struct drm_device *dev,
+			       int level,
+			       struct intel_wm_level *ret_wm)
+{
+	struct intel_crtc *intel_crtc;
+
+	list_for_each_entry(intel_crtc, &dev->mode_config.crtc_list, base.head) {
+		const struct intel_pipe_wm *pipe_wm = &intel_crtc->wm.active;
+		const struct intel_wm_level *wm = &pipe_wm->wm[level];
+
+		if (!pipe_wm->pipe_enabled)
+			continue;
+
+		if (!wm->enable)
+			break;
+
+		ret_wm->enable = true;
+		ret_wm->pri_val = max(ret_wm->pri_val, wm->pri_val);
+		ret_wm->spr_val = max(ret_wm->spr_val, wm->spr_val);
+		ret_wm->cur_val = max(ret_wm->cur_val, wm->cur_val);
+		ret_wm->fbc_val = max(ret_wm->fbc_val, wm->fbc_val);
+	}
+}
+
+/*
+ * Merge all low power watermarks for all active pipes.
+ */
+static void ilk_wm_merge(struct drm_device *dev,
+			 struct intel_wm_config *config,
+			 struct intel_pipe_wm *merged)
+{
+	int level;
+
+	/* ILK/SNB/IVB: LP1+ watermarks only w/ single pipe */
+	if (config->pipes_active > 1 &&
+	    (INTEL_INFO(dev)->gen <= 6 || IS_IVYBRIDGE(dev)))
+		return;
+
+	/* ILK: FBC WM must remain disabled */
+	merged->fbc_wm_enabled = INTEL_INFO(dev)->gen >= 6;
+
+	/* merge each level */
+	for (level = 1; level <= 4; level++) {
+		struct intel_wm_level *wm = &merged->wm[level];
+
+		ilk_merge_wm_level(dev, level, wm);
+	}
+}
+
+static void ilk_wm_limit(struct drm_device *dev,
+			 const struct intel_wm_config *config,
+			 const struct hsw_wm_maximums *max,
+			 struct intel_pipe_wm *merged)
+{
+	int level;
+
+	for (level = 1; level <= 4; level++) {
+		struct intel_wm_level *wm = &merged->wm[level];
+
+		if (!ilk_check_wm(level, max, wm))
+			break;
+
+		/*
+		 * If FBC WM is exceeded, disable just FBC WM
+		 * instead if the whole level.
+		 */
+		if (wm->fbc_val > max->fbc)
+			merged->fbc_wm_enabled = false;
+	}
+
+	if (merged->fbc_wm_enabled)
+		return;
+
+	/* Make sure we don't write garbage to the registers, */
+	for (level = 1; level <= 4; level++) {
+		struct intel_wm_level *wm = &merged->wm[level];
+
+		wm->fbc_val = 0;
+	}
+
+	/*
+	 * LP2+ watermarks must be disabled when FBC watermark is disabled on ILK
+	 * In practice this is always happens since we always disable FBC WM on ILK.
+	 */
+	if (INTEL_INFO(dev)->gen == 5 && intel_fbc_enabled(dev)) {
+		for (level = 2; level <= 4; level++) {
+			struct intel_wm_level *wm = &merged->wm[level];
+
+			ilk_disable_wm_level(wm);
+		}
+	}
+}
+
+static int ilk_wm_lp_to_level(int wm_lp, const struct intel_pipe_wm *merged)
+{
+	/* LP1,LP2,LP3 levels are either 1,2,3 or 1,3,4  */
+	return wm_lp + (wm_lp >= 2 && merged->wm[4].enable);
+}
+
+/* dirty bits used to track which watermarks need changes */
+#define WM_DIRTY_PIPE(pipe) (1 << (pipe))
+#define WM_DIRTY_LP(wm_lp) (1 << (15 + (wm_lp)))
+#define WM_DIRTY_FBC (1 << 24)
+#define WM_DIRTY_DDB (1 << 25)
+
+/* Determine which watermarks registers need reprogramming. */
+static unsigned int ilk_compute_wm_dirty(struct drm_device *dev,
+					 const struct intel_wm_config *config,
+					 enum intel_ddb_partitioning ddb_partitioning,
+					 const struct intel_pipe_wm *merged)
+{
+	struct drm_i915_private *dev_priv = dev->dev_private;
+	struct intel_crtc *intel_crtc;
+	unsigned int dirty = 0;
+	int wm_lp;
+
+	/* Do LP0 watermarks need updates? */
+	list_for_each_entry(intel_crtc, &dev->mode_config.crtc_list, base.head) {
+		const struct intel_wm_level *wm = &intel_crtc->wm.active.wm[0];
+
+		if (memcmp(&intel_crtc->wm.hw, wm, sizeof(*wm)))
+			dirty |= WM_DIRTY_PIPE(intel_crtc->pipe);
+	}
+
+	if (config->fbc_wm_enabled != dev_priv->wm.fbc_wm_enabled)
+		dirty |= WM_DIRTY_FBC;
+
+	if (ddb_partitioning != dev_priv->wm.ddb_partitioning)
+		dirty |= WM_DIRTY_DDB;
+
+	/* Need to disable LP1+ watermarks when changing DDB/FBC WM */
+	if (dirty & (WM_DIRTY_FBC | WM_DIRTY_DDB)) {
+		dirty |= WM_DIRTY_LP(1) | WM_DIRTY_LP(2) | WM_DIRTY_LP(3);
+
+		/* LP1+ watermarks already deemed dirty, no need to continue */
+		return dirty;
+	}
+
+	/* Find the lowest numbered LP1+ watermark in need of an update... */
+	for (wm_lp = 1; wm_lp <= 3; wm_lp++) {
+		int level = ilk_wm_lp_to_level(wm_lp, merged);
+		const struct intel_wm_level *wm = &merged->wm[level];
+
+		if (memcmp(&dev_priv->wm.hw[wm_lp - 1], wm, sizeof(*wm)))
+			break;
+	}
+
+	/* ...and mark it and all higher numbered LP1+ watermarks as dirty */
+	for (; wm_lp <= 3; wm_lp++)
+		dirty |= WM_DIRTY_LP(wm_lp);
+
+	return dirty;
+}
+
+/* Disable LP1+ watermarks */
+static void ilk_disable_lp_wm(struct drm_i915_private *dev_priv,
+			      unsigned int dirty)
+{
+	int wm_lp;
+
+	for (wm_lp = 3; wm_lp >= 1; wm_lp--) {
+		static const unsigned int ilk_wm_lp_reg[] = {
+			[1] = WM1_LP_ILK,
+			[2] = WM2_LP_ILK,
+			[3] = WM3_LP_ILK,
+		};
+		struct intel_wm_level *hw = &dev_priv->wm.hw[wm_lp - 1];
+
+		if (!(dirty & WM_DIRTY_LP(wm_lp))) {
+			/* all lower dirty bits must be set when a higher bit is set */
+			WARN_ON(wm_lp > 1 && dirty & WM_DIRTY_LP(wm_lp - 1));
+			WARN_ON(wm_lp > 2 && dirty & WM_DIRTY_LP(wm_lp - 2));
+			break;
+		}
+
+		/* This LP watermark already disabled? */
+		if (!hw->enable)
+			continue;
+
+		DRM_DEBUG_KMS("disabling LP%d watermark\n", wm_lp);
+
+		/*
+		 * Simply writing 0 to the register in the middle of the frame
+		 * can cause immediate underruns on ILK. Instead we must clear
+		 * only the enable bit. I assume the hardware will take a while
+		 * to get out of low power mode, during which time it still
+		 * consults the watermarks levels.
+		 */
+		I915_WRITE(ilk_wm_lp_reg[wm_lp],
+			   I915_READ(ilk_wm_lp_reg[wm_lp]) & ~WM1_LP_SR_EN);
+
+		/* ILK/SNB have a separate enable bit for sprite LP1 watermark */
+		if (hw->spr_val && INTEL_INFO(dev_priv->dev)->gen <= 6) {
+			WARN_ON(wm_lp != 1);
+			I915_WRITE(WM1S_LP_ILK, I915_READ(WM1S_LP_ILK) & ~WM1S_LP_EN);
+		}
+
+		ilk_disable_wm_level(hw);
+	}
+}
+
+struct intel_pipe_wm *ivb_find_best_wm(struct intel_pipe_wm *r1,
+				       struct intel_pipe_wm *r2)
+{
+	int level, level1 = 0, level2 = 0;
+
+	for (level = 1; level <= 4; level++) {
+		if (r1->wm[level].enable)
+			level1 = level;
+		if (r2->wm[level].enable)
+			level2 = level;
+	}
+
+	if (level1 == level2) {
+		if (r2->fbc_wm_enabled && !r1->fbc_wm_enabled)
+			return r2;
+		else
+			return r1;
+	} else if (level1 > level2) {
+		return r1;
+	} else {
+		return r2;
+	}
+}
+
+/* Program the watermark registers */
+static void ilk_program_watermarks(struct drm_device *dev)
+{
+	struct drm_i915_private *dev_priv = dev->dev_private;
+	struct drm_crtc *crtc;
+	struct intel_pipe_wm merged_1_2 = {}, merged_5_6 = {}, *merged;
+	struct hsw_wm_maximums lp_max_1_2, lp_max_5_6;
+	int wm_lp;
+	unsigned int dirty;
+	struct intel_wm_config config = {};
+	struct intel_crtc *intel_crtc;
+	enum intel_ddb_partitioning ddb_partitioning;
+
+	/* ILK: FBC WM must remain disabled */
+	config.fbc_wm_enabled = INTEL_INFO(dev)->gen >= 6 && intel_fbc_enabled(dev);
+
+	list_for_each_entry(intel_crtc, &dev->mode_config.crtc_list, base.head) {
+		const struct intel_pipe_wm *wm = &intel_crtc->wm.active;
+
+		if (!wm->pipe_enabled)
+			continue;
+
+		config.sprites_enabled |= wm->sprites_enabled;
+		config.sprites_scaled |= wm->sprites_scaled;
+		config.pipes_active++;
+	}
+
+	ilk_wm_max(dev, 1, &config, INTEL_DDB_PART_1_2, &lp_max_1_2);
+
+	ilk_wm_merge(dev, &config, &merged_1_2);
+
+	/* 5/6 DDB partitioning only in single pipe config on IVB+ */
+	if (INTEL_INFO(dev)->gen >= 7 && config.pipes_active <= 1) {
+		ilk_wm_max(dev, 1, &config, INTEL_DDB_PART_5_6, &lp_max_5_6);
+
+		merged_5_6 = merged_1_2;
+
+		ilk_wm_limit(dev, &config, &lp_max_1_2, &merged_1_2);
+		ilk_wm_limit(dev, &config, &lp_max_5_6, &merged_5_6);
+
+		merged = ivb_find_best_wm(&merged_1_2, &merged_5_6);
+	} else {
+		ilk_wm_limit(dev, &config, &lp_max_1_2, &merged_1_2);
+		merged = &merged_1_2;
+	}
+
+	ddb_partitioning = merged == &merged_1_2 ?
+		INTEL_DDB_PART_1_2 : INTEL_DDB_PART_5_6;
+
+	DRM_DEBUG_KMS("WM: pipes  active=%u, sprites enabled=%s, "
+		      "sprites scaled=%s, FBC WM enabled=%s, "
+		      "DDB partitioning=%s\n",
+		      config.pipes_active,
+		      yesno(config.sprites_enabled),
+		      yesno(config.sprites_scaled),
+		      yesno(config.fbc_wm_enabled),
+		      ddb_partitioning == INTEL_DDB_PART_1_2 ? "1/2" : "5/6");
+
+	dirty = ilk_compute_wm_dirty(dev, &config, ddb_partitioning, merged);
+	if (!dirty) {
+		DRM_DEBUG_KMS("WM: all clean\n");
+		return;
+	}
+
+	DRM_DEBUG_KMS("WM: dirty %s%s%s%s%s%s%s%s\n",
+		      dirty & WM_DIRTY_DDB ? "DDB," : "",
+		      dirty & WM_DIRTY_FBC ? "FBC," : "",
+		      dirty & WM_DIRTY_LP(3) ? "LP3," : "",
+		      dirty & WM_DIRTY_LP(2) ? "LP2," : "",
+		      dirty & WM_DIRTY_LP(1) ? "LP1," : "",
+		      dirty & WM_DIRTY_PIPE(PIPE_A) ? "LP0(A)," : "",
+		      dirty & WM_DIRTY_PIPE(PIPE_B) ? "LP0(B)," : "",
+		      dirty & WM_DIRTY_PIPE(PIPE_C) ? "LP0(C)," : "");
+
+	/*
+	 * LP1+ watermarks need to be disabled prior to
+	 * changing DDB partitioning, FBC watermark disable,
+	 * or lower numbered LP1+ watermarks. "dirty" knows
+	 * which watermarks need disabling.
+	 */
+	ilk_disable_lp_wm(dev_priv, dirty);
+
+	/* program LP0 watermarks */
+	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
+		static const unsigned int ilk_wm0_pipe_reg[] = {
+			[PIPE_A] = WM0_PIPEA_ILK,
+			[PIPE_B] = WM0_PIPEB_ILK,
+			[PIPE_C] = WM0_PIPEC_IVB,
+		};
+		struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+		const struct intel_pipe_wm *pipe_wm = &intel_crtc->wm.active;
+		const struct intel_wm_level *wm = &pipe_wm->wm[0];
+
+		if (!(dirty & WM_DIRTY_PIPE(intel_crtc->pipe)))
+			continue;
+
+		DRM_DEBUG_KMS("Pipe %c LP0: pri=%u, spr=%u, cur=%u\n",
+			      pipe_name(intel_crtc->pipe),
+			      wm->pri_val, wm->spr_val, wm->cur_val);
+
+		I915_WRITE(ilk_wm0_pipe_reg[intel_crtc->pipe],
+			   wm->pri_val << WM0_PIPE_PLANE_SHIFT |
+			   wm->spr_val << WM0_PIPE_SPRITE_SHIFT |
+			   wm->cur_val);
+
+		if (INTEL_INFO(dev)->gen > 7 || IS_HASWELL(dev))
+			I915_WRITE(PIPE_WM_LINETIME(intel_crtc->pipe),
+				   pipe_wm->linetime);
+
+		intel_crtc->wm.hw = *wm;
+	}
+
+	/* configure FBC watermark */
+	if (dirty & WM_DIRTY_FBC) {
+		uint32_t tmp = I915_READ(DISP_ARB_CTL);
+
+		DRM_DEBUG_KMS("WM: FBC watermark = %s\n", yesno(config.fbc_wm_enabled));
+
+		if (config.fbc_wm_enabled)
+			tmp &= ~DISP_FBC_WM_DIS;
+		else
+			tmp |= DISP_FBC_WM_DIS;
+
+		I915_WRITE(DISP_ARB_CTL, tmp);
+
+		dev_priv->wm.fbc_wm_enabled = config.fbc_wm_enabled;
+	}
+
+	/* configure DDB partitioning */
+	if (dirty & WM_DIRTY_DDB) {
+		DRM_DEBUG_KMS("WM: DDB partitioning = %s\n",
+			      ddb_partitioning == INTEL_DDB_PART_1_2 ? "1/2" : "5/6");
+
+		if (INTEL_INFO(dev)->gen > 7 || IS_HASWELL(dev)) {
+			uint32_t tmp = I915_READ(WM_MISC);
+
+			if (ddb_partitioning == INTEL_DDB_PART_5_6)
+				tmp |= WM_MISC_DATA_PARTITION_5_6;
+			else
+				tmp &= ~WM_MISC_DATA_PARTITION_5_6;
+
+			I915_WRITE(DISP_ARB_CTL, tmp);
+		} else {
+			uint32_t tmp = I915_READ(DISP_ARB_CTL2);
+
+			if (ddb_partitioning == INTEL_DDB_PART_5_6)
+				tmp |= DISP_DATA_PARTITION_5_6;
+			else
+				tmp &= ~DISP_DATA_PARTITION_5_6;
+
+			I915_WRITE(DISP_ARB_CTL2, tmp);
+		}
+
+		dev_priv->wm.ddb_partitioning = ddb_partitioning;
+	}
+
+	/* program LP1+ watermarks */
+	for (wm_lp = 1; wm_lp <= 3; wm_lp++) {
+		static const unsigned int wm_lp_reg[] = {
+			[1] = WM1_LP_ILK,
+			[2] = WM2_LP_ILK,
+			[3] = WM3_LP_ILK,
+		};
+		static const unsigned int wm_spr_lp_reg[] = {
+			[1] = WM1S_LP_ILK,
+			[2] = WM2S_LP_IVB,
+			[3] = WM3S_LP_IVB,
+		};
+		int level = ilk_wm_lp_to_level(wm_lp, merged);
+		const struct intel_wm_level *wm = &merged->wm[level];
+
+		if (!wm->enable)
+			break;
+
+		if (!(dirty & WM_DIRTY_LP(wm_lp)))
+			continue;
+
+		DRM_DEBUG_KMS("LP%d (level %d): pri=%u, spr=%u, cur=%u, fbc=%u, latency=%u\n",
+			      wm_lp, level, wm->pri_val, wm->spr_val,
+			      wm->cur_val, wm->fbc_val, ilk_wm_lp_latency(dev, level));
+
+		if (wm->spr_val) {
+			if (INTEL_INFO(dev)->gen <= 6) {
+				WARN_ON(wm_lp != 1);
+				I915_WRITE(WM1S_LP_ILK, WM1S_LP_EN | wm->spr_val);
+			} else {
+				I915_WRITE(wm_spr_lp_reg[wm_lp], wm->spr_val);
+			}
+		}
+
+		I915_WRITE(wm_lp_reg[wm_lp], WM1_LP_SR_EN |
+			   ilk_wm_lp_latency(dev, level) << WM1_LP_LATENCY_SHIFT |
+			   wm->fbc_val << WM1_LP_FBC_SHIFT |
+			   wm->pri_val << WM1_LP_SR_SHIFT |
+			   wm->cur_val);
+
+		dev_priv->wm.hw[wm_lp - 1] = *wm;
+	}
+}
+
+static uint32_t
+hsw_compute_linetime_wm(struct drm_device *dev, struct drm_crtc *crtc);
+
+static int ilk_max_wm_level(const struct drm_device *dev,
+			    const struct intel_pipe_wm *pipe_wm)
+{
+	/* HSW: LP1+ watermarks allowed even with multiple pipes */
+	if (INTEL_INFO(dev)->gen > 7 || IS_HASWELL(dev)) {
+		WARN_ON(pipe_wm->sprites_scaled);
+		return 4;
+	}
+
+	/* ILK/SNB/IVB: LP1+ watermarks only w/o scaling */
+	if (pipe_wm->sprites_scaled)
+		return 0;
+
+	/* ILK/SNB: LP2+ watermarks only w/o sprites */
+	if (INTEL_INFO(dev)->gen <= 6 && pipe_wm->sprites_enabled)
+		return 1;
+
+	return 3;
+}
+
+/* Compute new watermarks for the pipe */
+static bool intel_compute_pipe_wm(struct drm_crtc *crtc,
+				  struct intel_pipe_wm *pipe_wm)
+{
+	struct drm_device *dev = crtc->dev;
+	struct drm_i915_private *dev_priv = dev->dev_private;
+	struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+	struct hsw_wm_maximums max;
+	struct drm_plane *plane;
+	struct hsw_pipe_wm_parameters p = {};
+	struct intel_wm_config config = {};
+	int level, max_level;
+
+	p.active = intel_crtc_active(crtc);
+
+	if (p.active) {
+		p.pipe_htotal = intel_crtc->config.adjusted_mode.htotal;
+		p.pixel_rate = ilk_pipe_pixel_rate(dev, crtc);
+
+		/* TODO: for now, assume primary and cursor planes are always enabled. */
+		p.pri.enabled = true;
+		p.pri.bytes_per_pixel = crtc->fb->bits_per_pixel / 8;
+		p.pri.horiz_pixels = intel_crtc->config.requested_mode.hdisplay;
+
+		p.cur.enabled = true;
+		p.cur.bytes_per_pixel = 4;
+		p.cur.horiz_pixels = 64;
+
+		config.pipes_active = 1;
+	}
+
+	list_for_each_entry(plane, &dev->mode_config.plane_list, head) {
+		struct intel_plane *intel_plane = to_intel_plane(plane);
+
+		if (intel_plane->pipe != intel_crtc->pipe)
+			continue;
+
+		p.spr = intel_plane->wm;
+
+		config.sprites_enabled |= p.spr.enabled;
+		config.sprites_scaled |= p.spr.scaled;
+	}
+
+	pipe_wm->pipe_enabled = p.active;
+	pipe_wm->sprites_enabled = config.sprites_enabled;
+	pipe_wm->sprites_scaled = config.sprites_scaled;
+
+	max_level = ilk_max_wm_level(dev, pipe_wm);
+
+	for (level = 0; level <= max_level; level++)
+		ilk_compute_wm_level(dev_priv, level, &p, &pipe_wm->wm[level]);
+
+	if (IS_HASWELL(dev))
+		pipe_wm->linetime = hsw_compute_linetime_wm(dev, crtc);
+
+	ilk_wm_max(dev, 0, &config, false, &max);
+
+	/* At least LP0 must be valid */
+	return ilk_check_wm(0, &max, &pipe_wm->wm[0]);
+}
+
+/* Prepare the pipe to update the its watermarks on the next vblank */
+static void intel_setup_pipe_wm(struct intel_crtc *intel_crtc,
+				const struct intel_pipe_wm *pipe_wm)
+{
+	struct drm_device *dev = intel_crtc->base.dev;
+	struct drm_i915_private *dev_priv = dev->dev_private;
+	int pipe = intel_crtc->pipe;
+	unsigned long flags;
+
+	spin_lock_irqsave(&dev_priv->wm.lock, flags);
+
+	/* do the watermarks actually need changing? */
+	if (!memcmp(&intel_crtc->wm.pending, pipe_wm, sizeof(*pipe_wm)))
+		goto out;
+
+	/*
+	 * We might already have a pending watermark update, in
+	 * which case we shouldn't grab another vblank reference.
+	 */
+	if (!intel_crtc->wm.dirty && drm_vblank_get(dev, pipe)) {
+		if (intel_crtc->active)
+			DRM_ERROR("can't setup watermarks for pipe %c\n", pipe_name(pipe));
+		/* copy the new stuff over anyway. */
+		intel_crtc->wm.pending = *pipe_wm;
+		goto out;
+	}
+
+	/*
+	 * When going from no-scaling to scaling, disable LP1+
+	 * watermarks ahead of time.
+	 *
+	 * WaCxSRDisabledForSpriteScaling:ivb
+	 */
+	/*
+	 * FIXME is this sufficient of do we need extra vbl waits?
+	 * Something like this is needed on IVB. Do we need this on ILK/SNB too?
+	 */
+	if (!intel_crtc->wm.active.sprites_scaled && pipe_wm->sprites_scaled) {
+		DRM_DEBUG_KMS("going to enable scaling, disabling LP1+ watermarks\n");
+		ilk_disable_lp_wm(dev_priv, WM_DIRTY_LP(1) |
+				  WM_DIRTY_LP(2) | WM_DIRTY_LP(3));
+	}
+
+	intel_crtc->wm.pending = *pipe_wm;
+	intel_crtc->wm.dirty = true;
+	DRM_DEBUG_KMS("pipe %c new watermarks are pending\n", pipe_name(pipe));
+
+ out:
+	spin_unlock_irqrestore(&dev_priv->wm.lock, flags);
+}
+
+/* Call from vblank irq */
+void ilk_update_pipe_wm(struct drm_device *dev, enum pipe pipe)
+{
+	struct drm_i915_private *dev_priv = dev->dev_private;
+	struct drm_crtc *crtc = dev_priv->pipe_to_crtc_mapping[pipe];
+	struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+	unsigned long flags;
+
+	spin_lock_irqsave(&dev_priv->wm.lock, flags);
+
+	if (intel_crtc->wm.dirty) {
+		DRM_DEBUG_KMS("pipe %c vblank, programming new watermarks\n",
+			      pipe_name(pipe));
+
+		drm_vblank_put(dev, pipe);
+
+		intel_crtc->wm.active = intel_crtc->wm.pending;
+		intel_crtc->wm.dirty = false;
+
+		ilk_program_watermarks(dev);
+	}
+
+	spin_unlock_irqrestore(&dev_priv->wm.lock, flags);
 }
 
 static uint32_t
@@ -2565,271 +2708,24 @@ static void intel_setup_wm_latency(struct drm_device *dev)
 	intel_print_wm_latency(dev, dev_priv->wm.cur_latency);
 }
 
-static void hsw_compute_wm_parameters(struct drm_device *dev,
-				      struct hsw_pipe_wm_parameters *params,
-				      struct hsw_wm_maximums *lp_max_1_2,
-				      struct hsw_wm_maximums *lp_max_5_6)
+static void ilk_update_wm(struct drm_crtc *crtc)
 {
-	struct drm_crtc *crtc;
-	struct drm_plane *plane;
-	enum pipe pipe;
-	struct intel_wm_config config = {};
+	struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+	struct intel_pipe_wm pipe_wm = {};
+	bool ret;
 
-	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
-		struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
-		struct hsw_pipe_wm_parameters *p;
+	ret = intel_compute_pipe_wm(crtc, &pipe_wm);
+	if (!ret)
+		DRM_ERROR("crtc %u (pipe %c) LP0 watermarks invalid\n",
+			  crtc->base.id, pipe_name(intel_crtc->pipe));
 
-		pipe = intel_crtc->pipe;
-		p = &params[pipe];
-
-		p->active = intel_crtc_active(crtc);
-		if (!p->active)
-			continue;
-
-		config.pipes_active++;
-
-		p->pipe_htotal = intel_crtc->config.adjusted_mode.htotal;
-		p->pixel_rate = ilk_pipe_pixel_rate(dev, crtc);
-		p->pri.bytes_per_pixel = crtc->fb->bits_per_pixel / 8;
-		p->cur.bytes_per_pixel = 4;
-		p->pri.horiz_pixels =
-			intel_crtc->config.requested_mode.hdisplay;
-		p->cur.horiz_pixels = 64;
-		/* TODO: for now, assume primary and cursor planes are always enabled. */
-		p->pri.enabled = true;
-		p->cur.enabled = true;
-	}
-
-	list_for_each_entry(plane, &dev->mode_config.plane_list, head) {
-		struct intel_plane *intel_plane = to_intel_plane(plane);
-		struct hsw_pipe_wm_parameters *p;
-
-		pipe = intel_plane->pipe;
-		p = &params[pipe];
-
-		p->spr = intel_plane->wm;
-
-		config.sprites_enabled |= p->spr.enabled;
-		config.sprites_scaled |= p->spr.scaled;
-	}
-
-	ilk_wm_max(dev, 1, &config, INTEL_DDB_PART_1_2, lp_max_1_2);
-
-	/* 5/6 split only in single pipe config on IVB+ */
-	if (INTEL_INFO(dev)->gen >= 7 && config.pipes_active <= 1)
-		ilk_wm_max(dev, 1, &config, INTEL_DDB_PART_5_6, lp_max_5_6);
-	else
-		*lp_max_5_6 = *lp_max_1_2;
-}
-
-static void hsw_compute_wm_results(struct drm_device *dev,
-				   struct hsw_pipe_wm_parameters *params,
-				   struct hsw_wm_maximums *lp_maximums,
-				   struct hsw_wm_values *results)
-{
-	struct drm_i915_private *dev_priv = dev->dev_private;
-	struct drm_crtc *crtc;
-	struct intel_wm_level lp_results[4] = {};
-	enum pipe pipe;
-	int level, max_level, wm_lp;
-
-	for (level = 1; level <= 4; level++)
-		if (!hsw_compute_lp_wm(dev_priv, level,
-				       lp_maximums, params,
-				       &lp_results[level - 1]))
-			break;
-	max_level = level - 1;
-
-	/* The spec says it is preferred to disable FBC WMs instead of disabling
-	 * a WM level. */
-	results->enable_fbc_wm = true;
-	for (level = 1; level <= max_level; level++) {
-		if (!lp_results[level - 1].fbc_val > lp_maximums->fbc) {
-			results->enable_fbc_wm = false;
-			lp_results[level - 1].fbc_val = 0;
-		}
-	}
-
-	memset(results, 0, sizeof(*results));
-	for (wm_lp = 1; wm_lp <= 3; wm_lp++) {
-		const struct intel_wm_level *r;
-
-		level = (max_level == 4 && wm_lp > 1) ? wm_lp + 1 : wm_lp;
-		if (level > max_level)
-			break;
-
-		r = &lp_results[level - 1];
-		results->wm_lp[wm_lp - 1] = HSW_WM_LP_VAL(level * 2,
-							  r->fbc_val,
-							  r->pri_val,
-							  r->cur_val);
-		results->wm_lp_spr[wm_lp - 1] = r->spr_val;
-	}
-
-	for_each_pipe(pipe)
-		results->wm_pipe[pipe] = hsw_compute_wm_pipe(dev_priv, pipe,
-							     &params[pipe]);
-
-	for_each_pipe(pipe) {
-		crtc = dev_priv->pipe_to_crtc_mapping[pipe];
-		results->wm_linetime[pipe] = hsw_compute_linetime_wm(dev, crtc);
-	}
-}
-
-/* Find the result with the highest level enabled. Check for enable_fbc_wm in
- * case both are at the same level. Prefer r1 in case they're the same. */
-static struct hsw_wm_values *hsw_find_best_result(struct hsw_wm_values *r1,
-						  struct hsw_wm_values *r2)
-{
-	int i, val_r1 = 0, val_r2 = 0;
-
-	for (i = 0; i < 3; i++) {
-		if (r1->wm_lp[i] & WM3_LP_EN)
-			val_r1 = r1->wm_lp[i] & WM1_LP_LATENCY_MASK;
-		if (r2->wm_lp[i] & WM3_LP_EN)
-			val_r2 = r2->wm_lp[i] & WM1_LP_LATENCY_MASK;
-	}
-
-	if (val_r1 == val_r2) {
-		if (r2->enable_fbc_wm && !r1->enable_fbc_wm)
-			return r2;
-		else
-			return r1;
-	} else if (val_r1 > val_r2) {
-		return r1;
-	} else {
-		return r2;
-	}
-}
-
-/*
- * The spec says we shouldn't write when we don't need, because every write
- * causes WMs to be re-evaluated, expending some power.
- */
-static void hsw_write_wm_values(struct drm_i915_private *dev_priv,
-				struct hsw_wm_values *results,
-				enum intel_ddb_partitioning partitioning)
-{
-	struct hsw_wm_values previous;
-	uint32_t val;
-	enum intel_ddb_partitioning prev_partitioning;
-	bool prev_enable_fbc_wm;
-
-	previous.wm_pipe[0] = I915_READ(WM0_PIPEA_ILK);
-	previous.wm_pipe[1] = I915_READ(WM0_PIPEB_ILK);
-	previous.wm_pipe[2] = I915_READ(WM0_PIPEC_IVB);
-	previous.wm_lp[0] = I915_READ(WM1_LP_ILK);
-	previous.wm_lp[1] = I915_READ(WM2_LP_ILK);
-	previous.wm_lp[2] = I915_READ(WM3_LP_ILK);
-	previous.wm_lp_spr[0] = I915_READ(WM1S_LP_ILK);
-	previous.wm_lp_spr[1] = I915_READ(WM2S_LP_IVB);
-	previous.wm_lp_spr[2] = I915_READ(WM3S_LP_IVB);
-	previous.wm_linetime[0] = I915_READ(PIPE_WM_LINETIME(PIPE_A));
-	previous.wm_linetime[1] = I915_READ(PIPE_WM_LINETIME(PIPE_B));
-	previous.wm_linetime[2] = I915_READ(PIPE_WM_LINETIME(PIPE_C));
-
-	prev_partitioning = (I915_READ(WM_MISC) & WM_MISC_DATA_PARTITION_5_6) ?
-				INTEL_DDB_PART_5_6 : INTEL_DDB_PART_1_2;
-
-	prev_enable_fbc_wm = !(I915_READ(DISP_ARB_CTL) & DISP_FBC_WM_DIS);
-
-	if (memcmp(results->wm_pipe, previous.wm_pipe,
-		   sizeof(results->wm_pipe)) == 0 &&
-	    memcmp(results->wm_lp, previous.wm_lp,
-		   sizeof(results->wm_lp)) == 0 &&
-	    memcmp(results->wm_lp_spr, previous.wm_lp_spr,
-		   sizeof(results->wm_lp_spr)) == 0 &&
-	    memcmp(results->wm_linetime, previous.wm_linetime,
-		   sizeof(results->wm_linetime)) == 0 &&
-	    partitioning == prev_partitioning &&
-	    results->enable_fbc_wm == prev_enable_fbc_wm)
-		return;
-
-	if (previous.wm_lp[2] != 0)
-		I915_WRITE(WM3_LP_ILK, 0);
-	if (previous.wm_lp[1] != 0)
-		I915_WRITE(WM2_LP_ILK, 0);
-	if (previous.wm_lp[0] != 0)
-		I915_WRITE(WM1_LP_ILK, 0);
-
-	if (previous.wm_pipe[0] != results->wm_pipe[0])
-		I915_WRITE(WM0_PIPEA_ILK, results->wm_pipe[0]);
-	if (previous.wm_pipe[1] != results->wm_pipe[1])
-		I915_WRITE(WM0_PIPEB_ILK, results->wm_pipe[1]);
-	if (previous.wm_pipe[2] != results->wm_pipe[2])
-		I915_WRITE(WM0_PIPEC_IVB, results->wm_pipe[2]);
-
-	if (previous.wm_linetime[0] != results->wm_linetime[0])
-		I915_WRITE(PIPE_WM_LINETIME(PIPE_A), results->wm_linetime[0]);
-	if (previous.wm_linetime[1] != results->wm_linetime[1])
-		I915_WRITE(PIPE_WM_LINETIME(PIPE_B), results->wm_linetime[1]);
-	if (previous.wm_linetime[2] != results->wm_linetime[2])
-		I915_WRITE(PIPE_WM_LINETIME(PIPE_C), results->wm_linetime[2]);
-
-	if (prev_partitioning != partitioning) {
-		val = I915_READ(WM_MISC);
-		if (partitioning == INTEL_DDB_PART_1_2)
-			val &= ~WM_MISC_DATA_PARTITION_5_6;
-		else
-			val |= WM_MISC_DATA_PARTITION_5_6;
-		I915_WRITE(WM_MISC, val);
-	}
-
-	if (prev_enable_fbc_wm != results->enable_fbc_wm) {
-		val = I915_READ(DISP_ARB_CTL);
-		if (results->enable_fbc_wm)
-			val &= ~DISP_FBC_WM_DIS;
-		else
-			val |= DISP_FBC_WM_DIS;
-		I915_WRITE(DISP_ARB_CTL, val);
-	}
-
-	if (previous.wm_lp_spr[0] != results->wm_lp_spr[0])
-		I915_WRITE(WM1S_LP_ILK, results->wm_lp_spr[0]);
-	if (previous.wm_lp_spr[1] != results->wm_lp_spr[1])
-		I915_WRITE(WM2S_LP_IVB, results->wm_lp_spr[1]);
-	if (previous.wm_lp_spr[2] != results->wm_lp_spr[2])
-		I915_WRITE(WM3S_LP_IVB, results->wm_lp_spr[2]);
-
-	if (results->wm_lp[0] != 0)
-		I915_WRITE(WM1_LP_ILK, results->wm_lp[0]);
-	if (results->wm_lp[1] != 0)
-		I915_WRITE(WM2_LP_ILK, results->wm_lp[1]);
-	if (results->wm_lp[2] != 0)
-		I915_WRITE(WM3_LP_ILK, results->wm_lp[2]);
-}
-
-static void haswell_update_wm(struct drm_crtc *crtc)
-{
-	struct drm_device *dev = crtc->dev;
-	struct drm_i915_private *dev_priv = dev->dev_private;
-	struct hsw_wm_maximums lp_max_1_2, lp_max_5_6;
-	struct hsw_pipe_wm_parameters params[3];
-	struct hsw_wm_values results_1_2, results_5_6, *best_results;
-	enum intel_ddb_partitioning partitioning;
-
-	hsw_compute_wm_parameters(dev, params, &lp_max_1_2, &lp_max_5_6);
-
-	hsw_compute_wm_results(dev, params,
-			       &lp_max_1_2, &results_1_2);
-	if (lp_max_1_2.pri != lp_max_5_6.pri) {
-		hsw_compute_wm_results(dev, params,
-				       &lp_max_5_6, &results_5_6);
-		best_results = hsw_find_best_result(&results_1_2, &results_5_6);
-	} else {
-		best_results = &results_1_2;
-	}
-
-	partitioning = (best_results == &results_1_2) ?
-		       INTEL_DDB_PART_1_2 : INTEL_DDB_PART_5_6;
-
-	hsw_write_wm_values(dev_priv, best_results, partitioning);
+	intel_setup_pipe_wm(intel_crtc, &pipe_wm);
 }
 
-static void haswell_update_sprite_wm(struct drm_plane *plane,
-				     struct drm_crtc *crtc,
-				     uint32_t sprite_width, int pixel_size,
-				     bool enabled, bool scaled)
+static void ilk_update_sprite_wm(struct drm_plane *plane,
+				 struct drm_crtc *crtc,
+				 uint32_t sprite_width, int pixel_size,
+				 bool enabled, bool scaled)
 {
 	struct intel_plane *intel_plane = to_intel_plane(plane);
 
@@ -2838,169 +2734,7 @@ static void haswell_update_sprite_wm(struct drm_plane *plane,
 	intel_plane->wm.horiz_pixels = sprite_width;
 	intel_plane->wm.bytes_per_pixel = pixel_size;
 
-	haswell_update_wm(crtc);
-}
-
-static bool
-sandybridge_compute_sprite_wm(struct drm_device *dev, int plane,
-			      uint32_t sprite_width, int pixel_size,
-			      const struct intel_watermark_params *display,
-			      int display_latency_ns, int *sprite_wm)
-{
-	struct drm_crtc *crtc;
-	int clock;
-	int entries, tlb_miss;
-
-	crtc = intel_get_crtc_for_plane(dev, plane);
-	if (!intel_crtc_active(crtc)) {
-		*sprite_wm = display->guard_size;
-		return false;
-	}
-
-	clock = crtc->mode.clock;
-
-	/* Use the small buffer method to calculate the sprite watermark */
-	entries = ((clock * pixel_size / 1000) * display_latency_ns) / 1000;
-	tlb_miss = display->fifo_size*display->cacheline_size -
-		sprite_width * 8;
-	if (tlb_miss > 0)
-		entries += tlb_miss;
-	entries = DIV_ROUND_UP(entries, display->cacheline_size);
-	*sprite_wm = entries + display->guard_size;
-	if (*sprite_wm > (int)display->max_wm)
-		*sprite_wm = display->max_wm;
-
-	return true;
-}
-
-static bool
-sandybridge_compute_sprite_srwm(struct drm_device *dev, int plane,
-				uint32_t sprite_width, int pixel_size,
-				const struct intel_watermark_params *display,
-				int latency_ns, int *sprite_wm)
-{
-	struct drm_crtc *crtc;
-	unsigned long line_time_us;
-	int clock;
-	int line_count, line_size;
-	int small, large;
-	int entries;
-
-	if (!latency_ns) {
-		*sprite_wm = 0;
-		return false;
-	}
-
-	crtc = intel_get_crtc_for_plane(dev, plane);
-	clock = crtc->mode.clock;
-	if (!clock) {
-		*sprite_wm = 0;
-		return false;
-	}
-
-	line_time_us = (sprite_width * 1000) / clock;
-	if (!line_time_us) {
-		*sprite_wm = 0;
-		return false;
-	}
-
-	line_count = (latency_ns / line_time_us + 1000) / 1000;
-	line_size = sprite_width * pixel_size;
-
-	/* Use the minimum of the small and large buffer method for primary */
-	small = ((clock * pixel_size / 1000) * latency_ns) / 1000;
-	large = line_count * line_size;
-
-	entries = DIV_ROUND_UP(min(small, large), display->cacheline_size);
-	*sprite_wm = entries + display->guard_size;
-
-	return *sprite_wm > 0x3ff ? false : true;
-}
-
-static void sandybridge_update_sprite_wm(struct drm_plane *plane,
-					 struct drm_crtc *crtc,
-					 uint32_t sprite_width, int pixel_size,
-					 bool enable, bool scaled)
-{
-	struct drm_device *dev = plane->dev;
-	struct drm_i915_private *dev_priv = dev->dev_private;
-	int pipe = to_intel_plane(plane)->pipe;
-	int latency = SNB_READ_WM0_LATENCY() * 100;	/* In unit 0.1us */
-	u32 val;
-	int sprite_wm, reg;
-	int ret;
-
-	if (!enable)
-		return;
-
-	switch (pipe) {
-	case 0:
-		reg = WM0_PIPEA_ILK;
-		break;
-	case 1:
-		reg = WM0_PIPEB_ILK;
-		break;
-	case 2:
-		reg = WM0_PIPEC_IVB;
-		break;
-	default:
-		return; /* bad pipe */
-	}
-
-	ret = sandybridge_compute_sprite_wm(dev, pipe, sprite_width, pixel_size,
-					    &sandybridge_display_wm_info,
-					    latency, &sprite_wm);
-	if (!ret) {
-		DRM_DEBUG_KMS("failed to compute sprite wm for pipe %c\n",
-			      pipe_name(pipe));
-		return;
-	}
-
-	val = I915_READ(reg);
-	val &= ~WM0_PIPE_SPRITE_MASK;
-	I915_WRITE(reg, val | (sprite_wm << WM0_PIPE_SPRITE_SHIFT));
-	DRM_DEBUG_KMS("sprite watermarks For pipe %c - %d\n", pipe_name(pipe), sprite_wm);
-
-
-	ret = sandybridge_compute_sprite_srwm(dev, pipe, sprite_width,
-					      pixel_size,
-					      &sandybridge_display_srwm_info,
-					      SNB_READ_WM1_LATENCY() * 500,
-					      &sprite_wm);
-	if (!ret) {
-		DRM_DEBUG_KMS("failed to compute sprite lp1 wm on pipe %c\n",
-			      pipe_name(pipe));
-		return;
-	}
-	I915_WRITE(WM1S_LP_ILK, sprite_wm);
-
-	/* Only IVB has two more LP watermarks for sprite */
-	if (!IS_IVYBRIDGE(dev))
-		return;
-
-	ret = sandybridge_compute_sprite_srwm(dev, pipe, sprite_width,
-					      pixel_size,
-					      &sandybridge_display_srwm_info,
-					      SNB_READ_WM2_LATENCY() * 500,
-					      &sprite_wm);
-	if (!ret) {
-		DRM_DEBUG_KMS("failed to compute sprite lp2 wm on pipe %c\n",
-			      pipe_name(pipe));
-		return;
-	}
-	I915_WRITE(WM2S_LP_IVB, sprite_wm);
-
-	ret = sandybridge_compute_sprite_srwm(dev, pipe, sprite_width,
-					      pixel_size,
-					      &sandybridge_display_srwm_info,
-					      SNB_READ_WM3_LATENCY() * 500,
-					      &sprite_wm);
-	if (!ret) {
-		DRM_DEBUG_KMS("failed to compute sprite lp3 wm on pipe %c\n",
-			      pipe_name(pipe));
-		return;
-	}
-	I915_WRITE(WM3S_LP_IVB, sprite_wm);
+	ilk_update_wm(crtc);
 }
 
 /**
@@ -5372,6 +5106,8 @@ void intel_init_pm(struct drm_device *dev)
 {
 	struct drm_i915_private *dev_priv = dev->dev_private;
 
+	spin_lock_init(&dev_priv->wm.lock);
+
 	if (I915_HAS_FBC(dev)) {
 		if (HAS_PCH_SPLIT(dev)) {
 			dev_priv->display.fbc_enabled = ironlake_fbc_enabled;
@@ -5407,9 +5143,10 @@ void intel_init_pm(struct drm_device *dev)
 		if (IS_GEN5(dev)) {
 			if (dev_priv->wm.pri_latency[1] &&
 			    dev_priv->wm.spr_latency[1] &&
-			    dev_priv->wm.cur_latency[1])
-				dev_priv->display.update_wm = ironlake_update_wm;
-			else {
+			    dev_priv->wm.cur_latency[1]) {
+				dev_priv->display.update_wm = ilk_update_wm;
+				dev_priv->display.update_sprite_wm = ilk_update_sprite_wm;
+			} else {
 				DRM_DEBUG_KMS("Failed to get proper latency. "
 					      "Disable CxSR\n");
 				dev_priv->display.update_wm = NULL;
@@ -5419,8 +5156,8 @@ void intel_init_pm(struct drm_device *dev)
 			if (dev_priv->wm.pri_latency[0] &&
 			    dev_priv->wm.spr_latency[0] &&
 			    dev_priv->wm.cur_latency[0]) {
-				dev_priv->display.update_wm = sandybridge_update_wm;
-				dev_priv->display.update_sprite_wm = sandybridge_update_sprite_wm;
+				dev_priv->display.update_wm = ilk_update_wm;
+				dev_priv->display.update_sprite_wm = ilk_update_sprite_wm;
 			} else {
 				DRM_DEBUG_KMS("Failed to read display plane latency. "
 					      "Disable CxSR\n");
@@ -5431,8 +5168,8 @@ void intel_init_pm(struct drm_device *dev)
 			if (dev_priv->wm.pri_latency[0] &&
 			    dev_priv->wm.spr_latency[0] &&
 			    dev_priv->wm.cur_latency[0]) {
-				dev_priv->display.update_wm = ivybridge_update_wm;
-				dev_priv->display.update_sprite_wm = sandybridge_update_sprite_wm;
+				dev_priv->display.update_wm = ilk_update_wm;
+				dev_priv->display.update_sprite_wm = ilk_update_sprite_wm;
 			} else {
 				DRM_DEBUG_KMS("Failed to read display plane latency. "
 					      "Disable CxSR\n");
@@ -5443,9 +5180,8 @@ void intel_init_pm(struct drm_device *dev)
 			if (dev_priv->wm.pri_latency[0] &&
 			    dev_priv->wm.spr_latency[0] &&
 			    dev_priv->wm.cur_latency[0]) {
-				dev_priv->display.update_wm = haswell_update_wm;
-				dev_priv->display.update_sprite_wm =
-					haswell_update_sprite_wm;
+				dev_priv->display.update_wm = ilk_update_wm;
+				dev_priv->display.update_sprite_wm = ilk_update_sprite_wm;
 			} else {
 				DRM_DEBUG_KMS("Failed to read display plane latency. "
 					      "Disable CxSR\n");
-- 
1.8.1.5




More information about the Intel-gfx mailing list