diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c
index 4991ef2ac77d..c7d295a0895d 100644
--- a/drivers/gpu/drm/i915/intel_display.c
+++ b/drivers/gpu/drm/i915/intel_display.c
@@ -15518,7 +15518,8 @@ intel_modeset_setup_hw_state(struct drm_device *dev)
 		skl_wm_get_hw_state(dev);
 	} else if (HAS_PCH_SPLIT(dev_priv)) {
 		ilk_wm_get_hw_state(dev);
-	}
+	} else if (INTEL_GEN(dev_priv) <= 3 && !IS_PINEVIEW(dev_priv))
+		i9xx_wm_get_hw_state(dev);
 
 	for_each_intel_crtc(dev, crtc) {
 		u64 put_domains;
diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
index ae9173707959..d9e49f2b3c22 100644
--- a/drivers/gpu/drm/i915/intel_drv.h
+++ b/drivers/gpu/drm/i915/intel_drv.h
@@ -546,6 +546,15 @@ struct g4x_wm_state {
 	bool fbc_en;
 };
 
+struct i9xx_wm_state {
+	uint16_t plane_wm;
+	bool cxsr;
+
+	struct {
+		uint16_t plane;
+	} sr;
+};
+
 struct intel_crtc_wm_state {
 	union {
 		struct {
@@ -590,6 +599,9 @@ struct intel_crtc_wm_state {
 			/* optimal watermarks */
 			struct g4x_wm_state optimal;
 		} g4x;
+		struct {
+			struct i9xx_wm_state optimal;
+		} i9xx;
 	};
 
 	/*
@@ -828,6 +840,7 @@ struct intel_crtc {
 			struct intel_pipe_wm ilk;
 			struct vlv_wm_state vlv;
 			struct g4x_wm_state g4x;
+			struct i9xx_wm_state i9xx;
 		} active;
 	} wm;
 
@@ -1868,6 +1881,7 @@ void gen6_rps_boost(struct drm_i915_private *dev_priv,
 		    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 i9xx_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);
diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c
index d2cec3249e87..0c933cfad02c 100644
--- a/drivers/gpu/drm/i915/intel_pm.c
+++ b/drivers/gpu/drm/i915/intel_pm.c
@@ -2226,89 +2226,154 @@ static void i965_update_wm(struct intel_crtc *unused_crtc)
 
 #undef FW_WM
 
-static void i9xx_update_wm(struct intel_crtc *unused_crtc)
+static const struct intel_watermark_params *i9xx_get_wm_info(struct drm_i915_private *dev_priv,
+							     struct intel_crtc *crtc)
 {
-	struct drm_i915_private *dev_priv = to_i915(unused_crtc->base.dev);
-	const struct intel_watermark_params *wm_info;
-	uint32_t fwater_lo;
-	uint32_t fwater_hi;
-	int cwm, srwm = 1;
-	int fifo_size;
-	int planea_wm, planeb_wm;
-	struct intel_crtc *crtc, *enabled = NULL;
+	struct intel_plane *plane = to_intel_plane(crtc->base.primary);
 
 	if (IS_I945GM(dev_priv))
-		wm_info = &i945_wm_info;
+		return &i945_wm_info;
 	else if (!IS_GEN2(dev_priv))
-		wm_info = &i915_wm_info;
+		return &i915_wm_info;
+	else if (plane->plane == PLANE_A)
+		return &i830_a_wm_info;
 	else
-		wm_info = &i830_a_wm_info;
+		return &i830_bc_wm_info;
+}
 
-	fifo_size = dev_priv->display.get_fifo_size(dev_priv, 0);
-	crtc = intel_get_crtc_for_plane(dev_priv, 0);
-	if (intel_crtc_active(crtc)) {
+static int i9xx_compute_pipe_wm(struct intel_crtc_state *crtc_state)
+{
+	struct intel_crtc *crtc = to_intel_crtc(crtc_state->base.crtc);
+	struct drm_i915_private *dev_priv = to_i915(crtc->base.dev);
+	struct intel_atomic_state *state =
+		to_intel_atomic_state(crtc_state->base.state);
+	struct i9xx_wm_state *wm_state = &crtc_state->wm.i9xx.optimal;
+	struct intel_plane *plane = to_intel_plane(crtc->base.primary);
+	const struct drm_plane_state *plane_state = NULL;
+	int fifo_size;
+	const struct intel_watermark_params *wm_info;
+
+	fifo_size = dev_priv->display.get_fifo_size(dev_priv, plane->plane);
+
+	wm_info = i9xx_get_wm_info(dev_priv, crtc);
+
+	wm_state->cxsr = false;
+	memset(&wm_state->sr, 0, sizeof(wm_state->sr));
+
+	if (crtc_state->base.plane_mask & BIT(drm_plane_index(&plane->base)))
+		plane_state = __drm_atomic_get_current_plane_state(&state->base, &plane->base);
+
+	if (!plane_state || !intel_wm_plane_visible(crtc_state, to_intel_plane_state(plane_state))) {
+		wm_state->plane_wm = fifo_size - wm_info->guard_size;
+		if (wm_state->plane_wm > (long)wm_info->max_wm)
+			wm_state->plane_wm = wm_info->max_wm;
+	} else if (HAS_FW_BLC(dev_priv)) {
+		struct drm_framebuffer *fb = plane_state->fb;
+		unsigned cpp = IS_GEN2(dev_priv) ? 4 : fb->format->cpp[0];
 		const struct drm_display_mode *adjusted_mode =
-			&crtc->config->base.adjusted_mode;
-		const struct drm_framebuffer *fb =
-			crtc->base.primary->state->fb;
-		int cpp;
+			&crtc_state->base.adjusted_mode;
+		unsigned active_crtcs;
+		bool may_cxsr;
 
-		if (IS_GEN2(dev_priv))
-			cpp = 4;
+		if (state->modeset)
+			active_crtcs = state->active_crtcs;
 		else
-			cpp = fb->format->cpp[0];
+			active_crtcs = dev_priv->active_crtcs;
 
-		planea_wm = intel_calculate_wm(adjusted_mode->crtc_clock,
-					       wm_info, fifo_size, cpp,
-					       pessimal_latency_ns);
-		enabled = crtc;
-	} else {
-		planea_wm = fifo_size - wm_info->guard_size;
-		if (planea_wm > (long)wm_info->max_wm)
-			planea_wm = wm_info->max_wm;
+		may_cxsr = active_crtcs == drm_crtc_mask(&crtc->base);
+
+		wm_state->plane_wm = intel_calculate_wm(adjusted_mode->crtc_clock,
+							wm_info, fifo_size, cpp,
+							pessimal_latency_ns);
+
+		DRM_DEBUG_KMS("FIFO watermarks - plane watermarks: %d\n", wm_state->plane_wm);
+
+		if (IS_I915GM(dev_priv) && i915_gem_object_is_tiled(intel_fb_obj(fb)))
+			may_cxsr = false;
+
+		if (may_cxsr) {
+			static const int sr_latency_ns = 6000;
+			unsigned long entries;
+
+			entries = intel_wm_method2(adjusted_mode->crtc_clock,
+						   adjusted_mode->crtc_htotal,
+						   crtc_state->pipe_src_w, cpp,
+						   sr_latency_ns / 100);
+			entries = DIV_ROUND_UP(entries, wm_info->cacheline_size);
+
+			DRM_DEBUG_KMS("self-refresh entries: %ld\n", entries);
+
+			if (wm_info->fifo_size >= entries) {
+				wm_state->cxsr = true;
+				wm_state->sr.plane = wm_info->fifo_size - entries;
+			} else
+				may_cxsr = false;
+		}
+
+		DRM_DEBUG_KMS("FIFO watermarks - plane watermarks: %d, can cxsr: %s, SR size: %d\n",
+			      wm_state->plane_wm, yesno(wm_state->cxsr), wm_state->sr.plane);
 	}
 
-	if (IS_GEN2(dev_priv))
-		wm_info = &i830_bc_wm_info;
+	return 0;
+}
 
-	fifo_size = dev_priv->display.get_fifo_size(dev_priv, 1);
-	crtc = intel_get_crtc_for_plane(dev_priv, 1);
-	if (intel_crtc_active(crtc)) {
-		const struct drm_display_mode *adjusted_mode =
-			&crtc->config->base.adjusted_mode;
-		const struct drm_framebuffer *fb =
-			crtc->base.primary->state->fb;
-		int cpp;
+void i9xx_wm_get_hw_state(struct drm_device *dev)
+{
+	struct drm_i915_private *dev_priv = to_i915(dev);
+	struct intel_crtc *crtc;
+	uint32_t fwater_lo, planea_wm, planeb_wm;
+
+	fwater_lo = I915_READ(FW_BLC);
+
+	planea_wm = fwater_lo & 0x3f;
+	planeb_wm = (fwater_lo >> 16) & 0x3f;
+
+	for_each_intel_crtc(dev, crtc) {
+		struct intel_crtc_state *cstate = to_intel_crtc_state(crtc->base.state);
+		struct i9xx_wm_state *wm_state = &cstate->wm.i9xx.optimal;
+		struct intel_plane *plane = to_intel_plane(crtc->base.primary);
+
+		memset(&wm_state->sr, 0, sizeof(wm_state->sr));
+		wm_state->cxsr = false;
 
-		if (IS_GEN2(dev_priv))
-			cpp = 4;
+		if (plane == PLANE_A)
+			wm_state->plane_wm = planea_wm;
 		else
-			cpp = fb->format->cpp[0];
+			wm_state->plane_wm = planeb_wm;
+
+		crtc->wm.active.i9xx = *wm_state;
+	}
+}
 
-		planeb_wm = intel_calculate_wm(adjusted_mode->crtc_clock,
-					       wm_info, fifo_size, cpp,
-					       pessimal_latency_ns);
+static void i9xx_update_wm(struct intel_crtc *crtc)
+{
+	struct drm_i915_private *dev_priv = to_i915(crtc->base.dev);
+	uint32_t fwater_lo;
+	uint32_t fwater_hi;
+	int cwm, srwm = -1;
+	int planea_wm, planeb_wm;
+	struct intel_crtc *enabled = NULL;
+
+	crtc->wm.active.i9xx = crtc->config->wm.i9xx.optimal;
+
+	crtc = intel_get_crtc_for_plane(dev_priv, 0);
+	planea_wm = crtc->wm.active.i9xx.plane_wm;
+	if (intel_crtc_active(crtc))
+		enabled = crtc;
+
+	crtc = intel_get_crtc_for_plane(dev_priv, 1);
+	if (intel_crtc_active(crtc)) {
 		if (enabled == NULL)
 			enabled = crtc;
 		else
 			enabled = NULL;
-	} else {
-		planeb_wm = fifo_size - wm_info->guard_size;
-		if (planeb_wm > (long)wm_info->max_wm)
-			planeb_wm = wm_info->max_wm;
 	}
+	planeb_wm = crtc->wm.active.i9xx.plane_wm;
 
 	DRM_DEBUG_KMS("FIFO watermarks - A: %d, B: %d\n", planea_wm, planeb_wm);
 
-	if (IS_I915GM(dev_priv) && enabled) {
-		struct drm_i915_gem_object *obj;
-
-		obj = intel_fb_obj(enabled->base.primary->state->fb);
-
-		/* self-refresh seems busted with untiled */
-		if (!i915_gem_object_is_tiled(obj))
-			enabled = NULL;
-	}
+	if (!enabled->wm.active.i9xx.cxsr)
+		enabled = NULL;
 
 	/*
 	 * Overlay gets an aggressive default since video jitter is bad.
@@ -2319,31 +2384,8 @@ static void i9xx_update_wm(struct intel_crtc *unused_crtc)
 	intel_set_memory_cxsr(dev_priv, false);
 
 	/* Calc sr entries for one plane configs */
-	if (HAS_FW_BLC(dev_priv) && enabled) {
-		/* self-refresh has much higher latency */
-		static const int sr_latency_ns = 6000;
-		const struct drm_display_mode *adjusted_mode =
-			&enabled->config->base.adjusted_mode;
-		const struct drm_framebuffer *fb =
-			enabled->base.primary->state->fb;
-		int clock = adjusted_mode->crtc_clock;
-		int htotal = adjusted_mode->crtc_htotal;
-		int hdisplay = enabled->config->pipe_src_w;
-		int cpp;
-		int entries;
-
-		if (IS_I915GM(dev_priv) || IS_I945GM(dev_priv))
-			cpp = 4;
-		else
-			cpp = fb->format->cpp[0];
-
-		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;
-		if (srwm < 0)
-			srwm = 1;
+	if (enabled) {
+		srwm = enabled->wm.active.i9xx.sr.plane;
 
 		if (IS_I945G(dev_priv) || IS_I945GM(dev_priv))
 			I915_WRITE(FW_BLC_SELF,
@@ -2369,23 +2411,18 @@ static void i9xx_update_wm(struct intel_crtc *unused_crtc)
 		intel_set_memory_cxsr(dev_priv, true);
 }
 
-static void i845_update_wm(struct intel_crtc *unused_crtc)
+static void i845_update_wm(struct intel_crtc *crtc)
 {
-	struct drm_i915_private *dev_priv = to_i915(unused_crtc->base.dev);
-	struct intel_crtc *crtc;
-	const struct drm_display_mode *adjusted_mode;
+	struct drm_i915_private *dev_priv = to_i915(crtc->base.dev);
 	uint32_t fwater_lo;
 	int planea_wm;
 
-	crtc = single_enabled_crtc(dev_priv);
-	if (crtc == NULL)
+	if (!intel_crtc_active(crtc))
 		return;
 
-	adjusted_mode = &crtc->config->base.adjusted_mode;
-	planea_wm = intel_calculate_wm(adjusted_mode->crtc_clock,
-				       &i845_wm_info,
-				       dev_priv->display.get_fifo_size(dev_priv, 0),
-				       4, pessimal_latency_ns);
+	crtc->wm.active.i9xx = crtc->config->wm.i9xx.optimal;
+	planea_wm = crtc->wm.active.i9xx.plane_wm;
+
 	fwater_lo = I915_READ(FW_BLC) & ~0xfff;
 	fwater_lo |= (3<<8) | planea_wm;
 
@@ -8705,9 +8742,13 @@ void intel_init_pm(struct drm_i915_private *dev_priv)
 	} else if (IS_GEN4(dev_priv)) {
 		dev_priv->display.update_wm = i965_update_wm;
 	} else if (IS_GEN3(dev_priv)) {
+		dev_priv->display.compute_pipe_wm = i9xx_compute_pipe_wm;
 		dev_priv->display.update_wm = i9xx_update_wm;
+
 		dev_priv->display.get_fifo_size = i9xx_get_fifo_size;
 	} else if (IS_GEN2(dev_priv)) {
+		dev_priv->display.compute_pipe_wm = i9xx_compute_pipe_wm;
+
 		if (INTEL_INFO(dev_priv)->num_pipes == 1) {
 			dev_priv->display.update_wm = i845_update_wm;
 			dev_priv->display.get_fifo_size = i845_get_fifo_size;
