diff mbox

[4/4] drm/i915: Add Baytrail PSR Support.

Message ID 1390938445-4666-1-git-send-email-rodrigo.vivi@gmail.com (mailing list archive)
State New, archived
Headers show

Commit Message

Rodrigo Vivi Jan. 28, 2014, 7:47 p.m. UTC
This patch adds PSR Support to Baytrail.

Baytrail cannot easily detect screen updates and force PSR exit.
So we inactivate it on {busy_ioctl, set_domain, sw_finish and mark_busy
and update to enable it back on next display mark_idle.

v2: Also inactivate PSR on cursor update.
v3: Inactivate PSR on mark_busy, dset_domain and sw_finish_ioctl, and
    early on page flip besides avoid initializing inactive/active flag
    more than once.
v4: Fix identation issues.
v5: Rebase and add Baytrail per pipe support although leaving PIPE_B
    support disabled by for now since it isn't working properly yet.

Cc: Ville Syrjälä <ville.syrjala@linux.intel.com>
Signed-off-by: Rodrigo Vivi <rodrigo.vivi@gmail.com>
---
 drivers/gpu/drm/i915/i915_debugfs.c  |  36 +++++-
 drivers/gpu/drm/i915/i915_drv.h      |   4 +-
 drivers/gpu/drm/i915/i915_gem.c      |   9 ++
 drivers/gpu/drm/i915/i915_reg.h      |  40 +++++++
 drivers/gpu/drm/i915/intel_display.c |  15 ++-
 drivers/gpu/drm/i915/intel_dp.c      | 208 +++++++++++++++++++++++++++++------
 drivers/gpu/drm/i915/intel_drv.h     |   1 +
 7 files changed, 274 insertions(+), 39 deletions(-)
diff mbox

Patch

diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c
index 4b852c6..c28de65 100644
--- a/drivers/gpu/drm/i915/i915_debugfs.c
+++ b/drivers/gpu/drm/i915/i915_debugfs.c
@@ -1900,6 +1900,8 @@  static int i915_edp_psr_status(struct seq_file *m, void *data)
 	struct drm_device *dev = node->minor->dev;
 	struct drm_i915_private *dev_priv = dev->dev_private;
 	u32 psrperf = 0;
+	u32 statA = 0;
+	u32 statB = 0;
 	bool enabled = false;
 
 	intel_runtime_pm_get(dev_priv);
@@ -1907,14 +1909,38 @@  static int i915_edp_psr_status(struct seq_file *m, void *data)
 	seq_printf(m, "Sink_Support: %s\n", yesno(dev_priv->psr.sink_support));
 	seq_printf(m, "Source_OK: %s\n", yesno(dev_priv->psr.source_ok));
 
-	enabled = HAS_PSR(dev) &&
-		I915_READ(EDP_PSR_CTL(dev)) & EDP_PSR_ENABLE;
-	seq_printf(m, "Enabled: %s\n", yesno(enabled));
+	if (HAS_PSR(dev)) {
+		if (IS_VALLEYVIEW(dev)) {
+			statA = I915_READ(VLV_EDP_PSR_STATUS_CTL(PIPE_A)) &
+				VLV_EDP_PSR_CURR_STATE_MASK;
+			statB = I915_READ(VLV_EDP_PSR_STATUS_CTL(PIPE_B)) &
+				VLV_EDP_PSR_CURR_STATE_MASK;
+			enabled = ((statA == VLV_EDP_PSR_ACTIVE_NORFB_UP) ||
+				   (statA == VLV_EDP_PSR_ACTIVE_SF_UPDATE) ||
+				   (statB == VLV_EDP_PSR_ACTIVE_NORFB_UP) ||
+				   (statB == VLV_EDP_PSR_ACTIVE_SF_UPDATE));
+		} else
+			enabled = I915_READ(EDP_PSR_CTL(dev)) & EDP_PSR_ENABLE;
+	}
+	seq_printf(m, "Enabled: %s", yesno(enabled));
 
-	if (HAS_PSR(dev))
+	if (IS_VALLEYVIEW(dev)) {
+		if ((statA == VLV_EDP_PSR_ACTIVE_NORFB_UP) ||
+		    (statA == VLV_EDP_PSR_ACTIVE_SF_UPDATE))
+			seq_puts(m, " pipe A");
+		if ((statB == VLV_EDP_PSR_ACTIVE_NORFB_UP) ||
+		    (statB == VLV_EDP_PSR_ACTIVE_SF_UPDATE))
+			seq_puts(m, " pipe B");
+	}
+
+	seq_puts(m, "\n");
+
+	/* VLV PSR has no kind of performance counter */
+	if (HAS_PSR(dev) && !IS_VALLEYVIEW(dev)) {
 		psrperf = I915_READ(EDP_PSR_PERF_CNT(dev)) &
 			EDP_PSR_PERF_CNT_MASK;
-	seq_printf(m, "Performance_Counter: %u\n", psrperf);
+		seq_printf(m, "Performance_Counter: %u\n", psrperf);
+	}
 
 	intel_runtime_pm_put(dev_priv);
 	return 0;
diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
index 7c53d4d..34dee24 100644
--- a/drivers/gpu/drm/i915/i915_drv.h
+++ b/drivers/gpu/drm/i915/i915_drv.h
@@ -747,6 +747,7 @@  struct i915_psr {
 	bool sink_support;
 	bool source_ok;
 	bool setup_done;
+	bool active;
 };
 
 enum intel_pch {
@@ -1866,7 +1867,8 @@  struct drm_i915_file_private {
 
 #define HAS_DDI(dev)		(INTEL_INFO(dev)->has_ddi)
 #define HAS_FPGA_DBG_UNCLAIMED(dev)	(INTEL_INFO(dev)->has_fpga_dbg)
-#define HAS_PSR(dev)		(IS_HASWELL(dev) || IS_BROADWELL(dev))
+#define HAS_PSR(dev)		(IS_HASWELL(dev) || IS_BROADWELL(dev) || \
+				 IS_VALLEYVIEW(dev))
 #define HAS_PC8(dev)		(IS_HASWELL(dev)) /* XXX HSW:ULX */
 #define HAS_RUNTIME_PM(dev)	(IS_HASWELL(dev))
 
diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c
index 39770f7..01137fe 100644
--- a/drivers/gpu/drm/i915/i915_gem.c
+++ b/drivers/gpu/drm/i915/i915_gem.c
@@ -1256,6 +1256,9 @@  i915_gem_set_domain_ioctl(struct drm_device *dev, void *data,
 		goto unlock;
 	}
 
+	if (IS_VALLEYVIEW(dev))
+		intel_edp_psr_inactivate(dev);
+
 	/* Try to flush the object off the GPU without holding the lock.
 	 * We will repeat the flush holding the lock in the normal manner
 	 * to catch cases where we are gazumped.
@@ -1299,6 +1302,9 @@  i915_gem_sw_finish_ioctl(struct drm_device *dev, void *data,
 	if (ret)
 		return ret;
 
+	if (IS_VALLEYVIEW(dev))
+		intel_edp_psr_inactivate(dev);
+
 	obj = to_intel_bo(drm_gem_object_lookup(dev, file, args->handle));
 	if (&obj->base == NULL) {
 		ret = -ENOENT;
@@ -4059,6 +4065,9 @@  i915_gem_busy_ioctl(struct drm_device *dev, void *data,
 	if (ret)
 		return ret;
 
+	if (IS_VALLEYVIEW(dev))
+		intel_edp_psr_inactivate(dev);
+
 	obj = to_intel_bo(drm_gem_object_lookup(dev, file, args->handle));
 	if (&obj->base == NULL) {
 		ret = -ENOENT;
diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h
index cbbaf26..3a34afb 100644
--- a/drivers/gpu/drm/i915/i915_reg.h
+++ b/drivers/gpu/drm/i915/i915_reg.h
@@ -1968,6 +1968,46 @@ 
 #define BCLRPAT(pipe) _PIPE(pipe, _BCLRPAT_A, _BCLRPAT_B)
 #define VSYNCSHIFT(trans) _TRANSCODER(trans, _VSYNCSHIFT_A, _VSYNCSHIFT_B)
 
+/* VLV eDP PSR registers */
+#define _PSRCTLA				(VLV_DISPLAY_BASE + 0x60090)
+#define _PSRCTLB				(VLV_DISPLAY_BASE + 0x61090)
+#define  VLV_EDP_PSR_ENABLE			(1<<0)
+#define  VLV_EDP_PSR_RESET			(1<<1)
+#define  VLV_EDP_PSR_MODE_MASK			(7<<2)
+#define  VLV_EDP_PSR_MODE_HW_TIMER		(1<<3)
+#define  VLV_EDP_PSR_MODE_SW_TIMER		(1<<2)
+#define  VLV_EDP_PSR_SINGLE_FRAME_UPDATE	(1<<7)
+#define  VLV_EDP_PSR_ACTIVE_ENTRY		(1<<8)
+#define  VLV_EDP_PSR_SRC_TRANSMITTER_STATE	(1<<9)
+#define  VLV_EDP_PSR_DBL_FRAME			(1<<10)
+#define  VLV_EDP_PSR_FRAME_COUNT_MASK		(0xff<<16)
+#define  VLV_EDP_PSR_IDLE_FRAME_SHIFT		16
+#define  VLV_EDP_PSR_INT_TRANSITION		(1<<24)
+#define VLV_EDP_PSR_CTL(pipe) _PIPE(pipe, _PSRCTLA, _PSRCTLB)
+
+#define _VSCSDPA			(VLV_DISPLAY_BASE + 0x600a0)
+#define _VSCSDPB			(VLV_DISPLAY_BASE + 0x610a0)
+#define  VLV_EDP_PSR_SDP_FREQ_MASK	(3<<30)
+#define  VLV_EDP_PSR_SDP_FREQ_ONCE	(1<<31)
+#define  VLV_EDP_PSR_SDP_FREQ_EVFRAME	(1<<30)
+#define VLV_EDP_VSC_SDP_REG(pipe)	_PIPE(pipe, _VSCSDPA, _VSCSDPB)
+
+#define _PSRSTATA			(VLV_DISPLAY_BASE + 0x60094)
+#define _PSRSTATB			(VLV_DISPLAY_BASE + 0x61094)
+#define  VLV_EDP_PSR_LAST_STATE_MASK	(7<<3)
+#define  VLV_EDP_PSR_CURR_STATE_MASK	7
+#define  VLV_EDP_PSR_DISABLED		(0<<0)
+#define  VLV_EDP_PSR_INACTIVE		(1<<0)
+#define  VLV_EDP_PSR_IN_TRANS_TO_ACTIVE	(2<<0)
+#define  VLV_EDP_PSR_ACTIVE_NORFB_UP	(3<<0)
+#define  VLV_EDP_PSR_ACTIVE_SF_UPDATE	(4<<0)
+#define  VLV_EDP_PSR_EXIT		(5<<0)
+#define  VLV_EDP_PSR_IN_TRANS		(1<<7)
+#define VLV_EDP_PSR_STATUS_CTL(pipe) _PIPE(pipe, _PSRSTATA, _PSRSTATB)
+
+#define VLV_PSR_CLK_GATE_DISABLE	(VLV_DISPLAY_BASE + 0x6204)
+#define  VLV_CLK_DISABLE_PIPE_B		(1<<30)
+
 /* HSW+ eDP PSR registers */
 #define EDP_PSR_BASE(dev)                       (IS_HASWELL(dev) ? 0x64800 : 0x6f800)
 #define EDP_PSR_CTL(dev)			(EDP_PSR_BASE(dev) + 0)
diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c
index 1a9aa19..081c8e2 100644
--- a/drivers/gpu/drm/i915/intel_display.c
+++ b/drivers/gpu/drm/i915/intel_display.c
@@ -7501,6 +7501,9 @@  static void intel_crtc_update_cursor(struct drm_crtc *crtc,
 	u32 base = 0, pos = 0;
 	bool visible;
 
+	if (IS_VALLEYVIEW(dev))
+		intel_edp_psr_inactivate(dev);
+
 	if (on)
 		base = intel_crtc->cursor_addr;
 
@@ -8228,16 +8231,20 @@  void intel_mark_idle(struct drm_device *dev)
 
 	if (dev_priv->info->gen >= 6)
 		gen6_rps_idle(dev->dev_private);
+
+	if (IS_VALLEYVIEW(dev))
+		intel_edp_psr_update(dev);
 }
 
+
 void intel_mark_fb_busy(struct drm_i915_gem_object *obj,
 			struct intel_ring_buffer *ring)
 {
 	struct drm_device *dev = obj->base.dev;
 	struct drm_crtc *crtc;
 
-	if (!i915.powersave)
-		return;
+	if (IS_VALLEYVIEW(dev))
+		intel_edp_psr_inactivate(dev);
 
 	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
 		if (!crtc->fb)
@@ -8688,6 +8695,10 @@  static int intel_crtc_page_flip(struct drm_crtc *crtc,
 	if (work == NULL)
 		return -ENOMEM;
 
+	/* Inactivate PSR early in page flip */
+	if (IS_VALLEYVIEW(dev))
+		intel_edp_psr_inactivate(dev);
+
 	work->event = event;
 	work->crtc = crtc;
 	work->old_fb_obj = to_intel_framebuffer(old_fb)->obj;
diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c
index 30d4350..b1164ff 100644
--- a/drivers/gpu/drm/i915/intel_dp.c
+++ b/drivers/gpu/drm/i915/intel_dp.c
@@ -1578,21 +1578,35 @@  static void intel_dp_get_config(struct intel_encoder *encoder,
 	}
 }
 
-static bool is_edp_psr(struct drm_device *dev)
+static bool is_edp_psr(struct intel_dp *intel_dp)
+{
+	return intel_dp->psr_dpcd[0] & DP_PSR_IS_SUPPORTED;
+}
+
+static bool vlv_edp_is_psr_enabled_on_pipe(struct drm_device *dev, int pipe)
 {
 	struct drm_i915_private *dev_priv = dev->dev_private;
+	uint32_t val;
 
-	return dev_priv->psr.sink_support;
+	val = I915_READ(VLV_EDP_PSR_STATUS_CTL(pipe)) &
+		VLV_EDP_PSR_CURR_STATE_MASK;
+	return (val == VLV_EDP_PSR_ACTIVE_NORFB_UP) ||
+		(val == VLV_EDP_PSR_ACTIVE_SF_UPDATE);
 }
 
 static bool intel_edp_is_psr_enabled(struct drm_device *dev)
 {
 	struct drm_i915_private *dev_priv = dev->dev_private;
 
-	if (!HAS_PSR(dev))
-		return false;
+	if (HAS_PSR(dev)) {
+		if (IS_VALLEYVIEW(dev))
+			return vlv_edp_is_psr_enabled_on_pipe(dev, PIPE_A) ||
+				vlv_edp_is_psr_enabled_on_pipe(dev, PIPE_B);
+		else
+			return I915_READ(EDP_PSR_CTL(dev)) & EDP_PSR_ENABLE;
+	}
 
-	return I915_READ(EDP_PSR_CTL(dev)) & EDP_PSR_ENABLE;
+	return false;
 }
 
 static void intel_edp_psr_write_vsc(struct intel_dp *intel_dp,
@@ -1626,28 +1640,53 @@  static void intel_edp_psr_write_vsc(struct intel_dp *intel_dp,
 
 static void intel_edp_psr_setup(struct intel_dp *intel_dp)
 {
-	struct drm_device *dev = intel_dp_to_dev(intel_dp);
+	struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp);
+	struct drm_device *dev = intel_dig_port->base.base.dev;
 	struct drm_i915_private *dev_priv = dev->dev_private;
 	struct edp_vsc_psr psr_vsc;
+	uint32_t val;
 
 	if (dev_priv->psr.setup_done)
 		return;
 
-	/* Prepare VSC packet as per EDP 1.3 spec, Table 3.10 */
-	memset(&psr_vsc, 0, sizeof(psr_vsc));
-	psr_vsc.sdp_header.HB0 = 0;
-	psr_vsc.sdp_header.HB1 = 0x7;
-	psr_vsc.sdp_header.HB2 = 0x2;
-	psr_vsc.sdp_header.HB3 = 0x8;
-	intel_edp_psr_write_vsc(intel_dp, &psr_vsc);
+	if (IS_VALLEYVIEW(dev)) {
+		val  = I915_READ(VLV_EDP_VSC_SDP_REG(PIPE_A));
+		val &= ~VLV_EDP_PSR_SDP_FREQ_MASK;
+		val |= VLV_EDP_PSR_SDP_FREQ_EVFRAME;
+		I915_WRITE(VLV_EDP_VSC_SDP_REG(PIPE_A), val);
+
+		val  = I915_READ(VLV_EDP_VSC_SDP_REG(PIPE_B));
+		val &= ~VLV_EDP_PSR_SDP_FREQ_MASK;
+		val |= VLV_EDP_PSR_SDP_FREQ_EVFRAME;
+		I915_WRITE(VLV_EDP_VSC_SDP_REG(PIPE_B), val);
+
+		/* val = I915_READ(VLV_PSR_CLK_GATE_DISABLE); */
+		/* val |= VLV_CLK_DISABLE_PIPE_B; */
+		/* I915_WRITE(VLV_PSR_CLK_GATE_DISABLE, val); */
+	} else {
+		/* Prepare VSC packet as per EDP 1.3 spec, Table 3.10 */
+		memset(&psr_vsc, 0, sizeof(psr_vsc));
+		psr_vsc.sdp_header.HB0 = 0;
+		psr_vsc.sdp_header.HB1 = 0x7;
+		psr_vsc.sdp_header.HB2 = 0x2;
+		psr_vsc.sdp_header.HB3 = 0x8;
+		intel_edp_psr_write_vsc(intel_dp, &psr_vsc);
 
-	/* Avoid continuous PSR exit by masking memup and hpd */
-	I915_WRITE(EDP_PSR_DEBUG_CTL(dev), EDP_PSR_DEBUG_MASK_MEMUP |
-		   EDP_PSR_DEBUG_MASK_HPD | EDP_PSR_DEBUG_MASK_LPSP);
+		/* Avoid continuous PSR exit by masking memup and hpd */
+		I915_WRITE(EDP_PSR_DEBUG_CTL(dev), EDP_PSR_DEBUG_MASK_MEMUP |
+			   EDP_PSR_DEBUG_MASK_HPD | EDP_PSR_DEBUG_MASK_LPSP);
+	}
 
 	dev_priv->psr.setup_done = true;
 }
 
+static void vlv_edp_psr_enable_sink(struct intel_dp *intel_dp)
+{
+	/* Enable PSR in sink */
+	intel_dp_aux_native_write_1(intel_dp, DP_PSR_EN_CFG,
+				    DP_PSR_ENABLE);
+}
+
 static void intel_edp_psr_enable_sink(struct intel_dp *intel_dp)
 {
 	struct drm_device *dev = intel_dp_to_dev(intel_dp);
@@ -1678,6 +1717,28 @@  static void intel_edp_psr_enable_sink(struct intel_dp *intel_dp)
 		   (aux_clock_divider << DP_AUX_CH_CTL_BIT_CLOCK_2X_SHIFT));
 }
 
+static void vlv_edp_psr_enable_source(struct intel_dp *intel_dp)
+{
+	struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp);
+	struct drm_device *dev = intel_dig_port->base.base.dev;
+	struct drm_i915_private *dev_priv = dev->dev_private;
+	struct intel_crtc *intel_crtc =
+		to_intel_crtc(intel_dig_port->base.base.crtc);
+
+	uint32_t idle_frames = 1;
+	uint32_t val;
+
+	val = I915_READ(VLV_EDP_PSR_CTL(intel_crtc->pipe));
+	val |= VLV_EDP_PSR_ENABLE;
+	val &= ~VLV_EDP_PSR_MODE_MASK;
+
+	val |= VLV_EDP_PSR_MODE_HW_TIMER;
+	val &= ~VLV_EDP_PSR_FRAME_COUNT_MASK;
+	val |= idle_frames << EDP_PSR_IDLE_FRAME_SHIFT;
+
+	I915_WRITE(VLV_EDP_PSR_CTL(intel_crtc->pipe), val);
+}
+
 static void intel_edp_psr_enable_source(struct intel_dp *intel_dp)
 {
 	struct drm_device *dev = intel_dp_to_dev(intel_dp);
@@ -1719,8 +1780,8 @@  static bool intel_edp_psr_match_conditions(struct intel_dp *intel_dp)
 		return false;
 	}
 
-	if ((intel_encoder->type != INTEL_OUTPUT_EDP) ||
-	    (dig_port->port != PORT_A)) {
+	if (HAS_DDI(dev) && ((intel_encoder->type != INTEL_OUTPUT_EDP) ||
+			     (dig_port->port != PORT_A))) {
 		DRM_DEBUG_KMS("HSW ties PSR to DDI A (eDP)\n");
 		return false;
 	}
@@ -1765,37 +1826,83 @@  static bool intel_edp_psr_match_conditions(struct intel_dp *intel_dp)
 		return false;
 	}
 
+	/* Baytrail supports per-pipe PSR configuration, however PSR on
+	* PIPE_B isn't working properly. So let's keep it disabled for now. */
+	if (IS_VALLEYVIEW(dev) && intel_crtc->pipe != PIPE_A) {
+		DRM_DEBUG_KMS("PSR on BYT isn't enabled on pipe B.\n");
+		return false;
+	}
+
 	dev_priv->psr.source_ok = true;
 	return true;
 }
 
 static void intel_edp_psr_do_enable(struct intel_dp *intel_dp)
 {
-	struct drm_device *dev = intel_dp_to_dev(intel_dp);
+	struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp);
+	struct drm_device *dev = intel_dig_port->base.base.dev;
+	struct drm_i915_private *dev_priv = dev->dev_private;
+	struct intel_crtc *intel_crtc =
+		to_intel_crtc(intel_dig_port->base.base.crtc);
 
-	if (!intel_edp_psr_match_conditions(intel_dp) ||
-	    intel_edp_is_psr_enabled(dev))
-		return;
+	if (IS_VALLEYVIEW(dev)) {
+		if (vlv_edp_is_psr_enabled_on_pipe(dev, intel_crtc->pipe))
+			return;
+	} else
+		if (intel_edp_is_psr_enabled(dev))
+			return;
 
 	/* Setup PSR once */
 	intel_edp_psr_setup(intel_dp);
 
 	/* Enable PSR on the panel */
-	intel_edp_psr_enable_sink(intel_dp);
+	if (IS_VALLEYVIEW(dev))
+		vlv_edp_psr_enable_sink(intel_dp);
+	else
+		intel_edp_psr_enable_sink(intel_dp);
 
 	/* Enable PSR on the host */
-	intel_edp_psr_enable_source(intel_dp);
+	if (IS_VALLEYVIEW(dev))
+		vlv_edp_psr_enable_source(intel_dp);
+	else
+		intel_edp_psr_enable_source(intel_dp);
+
+	dev_priv->psr.active = true;
 }
 
 void intel_edp_psr_enable(struct intel_dp *intel_dp)
 {
-	struct drm_device *dev = intel_dp_to_dev(intel_dp);
+	if (!is_edp_psr(intel_dp))
+		return;
 
-	if (intel_edp_psr_match_conditions(intel_dp) &&
-	    !intel_edp_is_psr_enabled(dev))
+	if (intel_edp_psr_match_conditions(intel_dp))
 		intel_edp_psr_do_enable(intel_dp);
 }
 
+void vlv_edp_psr_disable(struct intel_dp *intel_dp)
+{
+	struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp);
+	struct drm_device *dev = intel_dig_port->base.base.dev;
+	struct drm_i915_private *dev_priv = dev->dev_private;
+	struct intel_crtc *intel_crtc =
+		to_intel_crtc(intel_dig_port->base.base.crtc);
+	uint32_t val = I915_READ(VLV_EDP_PSR_STATUS_CTL(intel_crtc->pipe));
+
+	if (!dev_priv->psr.setup_done)
+		return;
+
+	intel_edp_psr_inactivate(dev);
+
+	if (val & VLV_EDP_PSR_IN_TRANS)
+		udelay(250);
+
+	val = I915_READ(VLV_EDP_PSR_CTL(intel_crtc->pipe));
+	val &= ~VLV_EDP_PSR_ACTIVE_ENTRY;
+	val &= ~VLV_EDP_PSR_ENABLE;
+	val &= ~VLV_EDP_PSR_MODE_MASK;
+	I915_WRITE(VLV_EDP_PSR_CTL(intel_crtc->pipe), val);
+}
+
 void intel_edp_psr_disable(struct intel_dp *intel_dp)
 {
 	struct drm_device *dev = intel_dp_to_dev(intel_dp);
@@ -1817,28 +1924,66 @@  void intel_edp_psr_update(struct drm_device *dev)
 {
 	struct intel_encoder *encoder;
 	struct intel_dp *intel_dp = NULL;
+	struct intel_crtc *intel_crtc;
 
 	list_for_each_entry(encoder, &dev->mode_config.encoder_list, base.head)
 		if (encoder->type == INTEL_OUTPUT_EDP) {
 			intel_dp = enc_to_intel_dp(&encoder->base);
 
-			if (!is_edp_psr(dev))
+			if (!is_edp_psr(intel_dp))
 				return;
 
-			if (!intel_edp_psr_match_conditions(intel_dp))
-				intel_edp_psr_disable(intel_dp);
-			else
+			intel_crtc = to_intel_crtc(encoder->base.crtc);
+
+			if (!intel_edp_psr_match_conditions(intel_dp)) {
+				if (IS_VALLEYVIEW(dev))
+					vlv_edp_psr_disable(intel_dp);
+				else
+					intel_edp_psr_disable(intel_dp);
+			} else
 				if (!intel_edp_is_psr_enabled(dev))
 					intel_edp_psr_do_enable(intel_dp);
 		}
 }
 
+void intel_edp_psr_inactivate(struct drm_device *dev)
+{
+	struct drm_i915_private *dev_priv = dev->dev_private;
+	struct intel_encoder *encoder;
+	struct intel_crtc *intel_crtc;
+	struct intel_dp *intel_dp = NULL;
+
+	if (!dev_priv->psr.setup_done || !dev_priv->psr.active)
+		return;
+
+	list_for_each_entry(encoder, &dev->mode_config.encoder_list, base.head)
+		if (encoder->type == INTEL_OUTPUT_EDP) {
+			intel_dp = enc_to_intel_dp(&encoder->base);
+			intel_crtc = to_intel_crtc(encoder->base.crtc);
+
+			if (!vlv_edp_is_psr_enabled_on_pipe(dev,
+							    intel_crtc->pipe))
+				continue;
+
+			dev_priv->psr.active = false;
+			I915_WRITE(VLV_EDP_PSR_CTL(intel_crtc->pipe),
+				   VLV_EDP_PSR_RESET);
+			/* WaClearPSRReset:vlv */
+			I915_WRITE(VLV_EDP_PSR_CTL(intel_crtc->pipe), 0);
+
+			intel_dp_sink_dpms(intel_dp, DRM_MODE_DPMS_ON);
+		}
+}
+
 static void intel_disable_dp(struct intel_encoder *encoder)
 {
 	struct intel_dp *intel_dp = enc_to_intel_dp(&encoder->base);
 	enum port port = dp_to_dig_port(intel_dp)->port;
 	struct drm_device *dev = encoder->base.dev;
 
+	if (IS_VALLEYVIEW(dev))
+		vlv_edp_psr_disable(intel_dp);
+
 	/* Make sure the panel is off before trying to change the mode. But also
 	 * ensure that we have vdd while we switch off the panel. */
 	intel_edp_backlight_off(intel_dp);
@@ -1895,6 +2040,7 @@  static void vlv_enable_dp(struct intel_encoder *encoder)
 	struct intel_dp *intel_dp = enc_to_intel_dp(&encoder->base);
 
 	intel_edp_backlight_on(intel_dp);
+	intel_edp_psr_enable(intel_dp);
 }
 
 static void g4x_pre_enable_dp(struct intel_encoder *encoder)
diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
index 71c1371..82026ef 100644
--- a/drivers/gpu/drm/i915/intel_drv.h
+++ b/drivers/gpu/drm/i915/intel_drv.h
@@ -748,6 +748,7 @@  void intel_edp_panel_off(struct intel_dp *intel_dp);
 void intel_edp_psr_enable(struct intel_dp *intel_dp);
 void intel_edp_psr_disable(struct intel_dp *intel_dp);
 void intel_edp_psr_update(struct drm_device *dev);
+void intel_edp_psr_inactivate(struct drm_device *dev);
 
 
 /* intel_dsi.c */