diff mbox

drm/i915: Add Baytrail PSR Support.

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

Commit Message

Rodrigo Vivi Jan. 23, 2014, 7:19 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.

Signed-off-by: Rodrigo Vivi <rodrigo.vivi@gmail.com>
---
 drivers/gpu/drm/i915/i915_debugfs.c  |  18 +++-
 drivers/gpu/drm/i915/i915_drv.h      |   4 +-
 drivers/gpu/drm/i915/i915_gem.c      |   9 ++
 drivers/gpu/drm/i915/i915_reg.h      |  34 ++++++++
 drivers/gpu/drm/i915/intel_ddi.c     |   3 +-
 drivers/gpu/drm/i915/intel_display.c |  14 ++++
 drivers/gpu/drm/i915/intel_dp.c      | 154 +++++++++++++++++++++++++++++------
 drivers/gpu/drm/i915/intel_drv.h     |   1 +
 8 files changed, 206 insertions(+), 31 deletions(-)

Comments

Ville Syrjälä Jan. 24, 2014, 2:53 p.m. UTC | #1
On Thu, Jan 23, 2014 at 05:19:53PM -0200, Rodrigo Vivi wrote:
<snip>
> index 76126e0..f5501ab 100644
> --- a/drivers/gpu/drm/i915/i915_reg.h
> +++ b/drivers/gpu/drm/i915/i915_reg.h
> @@ -1969,6 +1969,40 @@
>  #define BCLRPAT(pipe) _PIPE(pipe, _BCLRPAT_A, _BCLRPAT_B)
>  #define VSYNCSHIFT(trans) _TRANSCODER(trans, _VSYNCSHIFT_A, _VSYNCSHIFT_B)
>  
> +/* VLV eDP PSR registers */
> +#define VLV_EDP_PSR_CTL				(VLV_DISPLAY_BASE + 0x60090)

VLV has per-pipe PSR registers. The ones you have here are just for
pipe A. Seems like some rework is needed to make it work on either
pipe.
Rodrigo Vivi Jan. 24, 2014, 4:05 p.m. UTC | #2
On Fri, Jan 24, 2014 at 12:53 PM, Ville Syrjälä
<ville.syrjala@linux.intel.com> wrote:
> On Thu, Jan 23, 2014 at 05:19:53PM -0200, Rodrigo Vivi wrote:
> <snip>
>> index 76126e0..f5501ab 100644
>> --- a/drivers/gpu/drm/i915/i915_reg.h
>> +++ b/drivers/gpu/drm/i915/i915_reg.h
>> @@ -1969,6 +1969,40 @@
>>  #define BCLRPAT(pipe) _PIPE(pipe, _BCLRPAT_A, _BCLRPAT_B)
>>  #define VSYNCSHIFT(trans) _TRANSCODER(trans, _VSYNCSHIFT_A, _VSYNCSHIFT_B)
>>
>> +/* VLV eDP PSR registers */
>> +#define VLV_EDP_PSR_CTL                              (VLV_DISPLAY_BASE + 0x60090)
>
> VLV has per-pipe PSR registers. The ones you have here are just for
> pipe A. Seems like some rework is needed to make it work on either
> pipe.

Yes, but since I don't have any hw with two eDPs here I decided to let
the limitation we had for HSW, PSR only on pipe A.
In my point of view we could go ahead with this one eDP scenario and
implement psr on pipe b support later.

>
>
> --
> Ville Syrjälä
> Intel OTC
Ville Syrjälä Jan. 24, 2014, 5:41 p.m. UTC | #3
On Fri, Jan 24, 2014 at 02:05:57PM -0200, Rodrigo Vivi wrote:
> On Fri, Jan 24, 2014 at 12:53 PM, Ville Syrjälä
> <ville.syrjala@linux.intel.com> wrote:
> > On Thu, Jan 23, 2014 at 05:19:53PM -0200, Rodrigo Vivi wrote:
> > <snip>
> >> index 76126e0..f5501ab 100644
> >> --- a/drivers/gpu/drm/i915/i915_reg.h
> >> +++ b/drivers/gpu/drm/i915/i915_reg.h
> >> @@ -1969,6 +1969,40 @@
> >>  #define BCLRPAT(pipe) _PIPE(pipe, _BCLRPAT_A, _BCLRPAT_B)
> >>  #define VSYNCSHIFT(trans) _TRANSCODER(trans, _VSYNCSHIFT_A, _VSYNCSHIFT_B)
> >>
> >> +/* VLV eDP PSR registers */
> >> +#define VLV_EDP_PSR_CTL                              (VLV_DISPLAY_BASE + 0x60090)
> >
> > VLV has per-pipe PSR registers. The ones you have here are just for
> > pipe A. Seems like some rework is needed to make it work on either
> > pipe.
> 
> Yes, but since I don't have any hw with two eDPs here I decided to let
> the limitation we had for HSW, PSR only on pipe A.
> In my point of view we could go ahead with this one eDP scenario and
> implement psr on pipe b support later.

I don't see any pipe checks in the code, so you will happily enable PSR
on pipe A even if eDP is being fed by pipe B at the time.

Also even if you enable PSR on pipe A, you set the trunk clock gate
disable for pipe B, which seems weird. Setting that bit might actually
be the reason the pipe, port and PLL remain enabled during PSR.
diff mbox

Patch

diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c
index a1d29d1..5dfde84 100644
--- a/drivers/gpu/drm/i915/i915_debugfs.c
+++ b/drivers/gpu/drm/i915/i915_debugfs.c
@@ -1902,6 +1902,7 @@  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 psrstatus;
 	bool enabled = false;
 
 	intel_runtime_pm_get(dev_priv);
@@ -1909,14 +1910,23 @@  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;
+	if (HAS_PSR(dev)) {
+		if (IS_VALLEYVIEW(dev)) {
+			psrstatus = I915_READ(VLV_EDP_PSR_STATUS_CTL) &
+				VLV_EDP_PSR_CURR_STATE_MASK;
+			enabled = ((psrstatus == VLV_EDP_PSR_ACTIVE_NORFB_UP) ||
+				   (psrstatus == VLV_EDP_PSR_ACTIVE_SF_UPDATE));
+		} else
+			enabled = I915_READ(EDP_PSR_CTL(dev)) & EDP_PSR_ENABLE;
+	}
 	seq_printf(m, "Enabled: %s\n", yesno(enabled));
 
-	if (HAS_PSR(dev))
+	/* 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 fd5fc4b..e4c7157 100644
--- a/drivers/gpu/drm/i915/i915_drv.h
+++ b/drivers/gpu/drm/i915/i915_drv.h
@@ -745,6 +745,7 @@  struct i915_psr {
 	bool sink_support;
 	bool source_ok;
 	bool setup_done;
+	bool active;
 };
 
 enum intel_pch {
@@ -1871,7 +1872,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 bd93534..454d16e 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;
@@ -4047,6 +4053,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 76126e0..f5501ab 100644
--- a/drivers/gpu/drm/i915/i915_reg.h
+++ b/drivers/gpu/drm/i915/i915_reg.h
@@ -1969,6 +1969,40 @@ 
 #define BCLRPAT(pipe) _PIPE(pipe, _BCLRPAT_A, _BCLRPAT_B)
 #define VSYNCSHIFT(trans) _TRANSCODER(trans, _VSYNCSHIFT_A, _VSYNCSHIFT_B)
 
+/* VLV eDP PSR registers */
+#define VLV_EDP_PSR_CTL				(VLV_DISPLAY_BASE + 0x60090)
+#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_PIPEA_VSC_SDP_REG		(VLV_DISPLAY_BASE + 0x600a0)
+#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_PSR_STATUS_CTL		(VLV_DISPLAY_BASE + 0x60094)
+#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_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_ddi.c b/drivers/gpu/drm/i915/intel_ddi.c
index 74749c6..b40775c 100644
--- a/drivers/gpu/drm/i915/intel_ddi.c
+++ b/drivers/gpu/drm/i915/intel_ddi.c
@@ -1281,7 +1281,8 @@  static void intel_enable_ddi(struct intel_encoder *intel_encoder)
 			intel_dp_stop_link_train(intel_dp);
 
 		ironlake_edp_backlight_on(intel_dp);
-		intel_edp_psr_enable(intel_dp);
+		if (!IS_VALLEYVIEW(dev))
+			intel_edp_psr_enable(intel_dp);
 	}
 
 	if (intel_crtc->eld_vld && type != INTEL_OUTPUT_EDP) {
diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c
index ed5ffce..5be75c2 100644
--- a/drivers/gpu/drm/i915/intel_display.c
+++ b/drivers/gpu/drm/i915/intel_display.c
@@ -7474,6 +7474,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;
 
@@ -8175,6 +8178,9 @@  void intel_mark_idle(struct drm_device *dev)
 	struct drm_i915_private *dev_priv = dev->dev_private;
 	struct drm_crtc *crtc;
 
+	if (IS_VALLEYVIEW(dev))
+		intel_edp_psr_update(dev);
+
 	hsw_package_c8_gpu_idle(dev_priv);
 
 	if (!i915_powersave)
@@ -8191,12 +8197,16 @@  void intel_mark_idle(struct drm_device *dev)
 		gen6_rps_idle(dev->dev_private);
 }
 
+
 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 (IS_VALLEYVIEW(dev))
+		intel_edp_psr_inactivate(dev);
+
 	if (!i915_powersave)
 		return;
 
@@ -8647,6 +8657,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 b082973..163170c 100644
--- a/drivers/gpu/drm/i915/intel_dp.c
+++ b/drivers/gpu/drm/i915/intel_dp.c
@@ -1526,11 +1526,19 @@  static bool is_edp_psr(struct drm_device *dev)
 static bool intel_edp_is_psr_enabled(struct drm_device *dev)
 {
 	struct drm_i915_private *dev_priv = dev->dev_private;
+	uint32_t val;
 
-	if (!HAS_PSR(dev))
-		return false;
+	if (HAS_PSR(dev)) {
+		if (IS_VALLEYVIEW(dev)) {
+			val = I915_READ(VLV_EDP_PSR_STATUS_CTL) &
+				VLV_EDP_PSR_CURR_STATE_MASK;
+			return (val == VLV_EDP_PSR_ACTIVE_NORFB_UP) ||
+				(val == VLV_EDP_PSR_ACTIVE_SF_UPDATE);
+		} 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,
@@ -1567,25 +1575,44 @@  static void intel_edp_psr_setup(struct intel_dp *intel_dp)
 	struct drm_device *dev = intel_dp_to_dev(intel_dp);
 	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_PIPEA_VSC_SDP_REG);
+		val &= ~VLV_EDP_PSR_SDP_FREQ_MASK;
+		val |= VLV_EDP_PSR_SDP_FREQ_EVFRAME;
+		I915_WRITE(VLV_PIPEA_VSC_SDP_REG, 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);
@@ -1614,6 +1641,24 @@  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 drm_device *dev = intel_dp_to_dev(intel_dp);
+	struct drm_i915_private *dev_priv = dev->dev_private;
+	uint32_t idle_frames = 1;
+	uint32_t val;
+
+	val = I915_READ(VLV_EDP_PSR_CTL);
+	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, val);
+}
+
 static void intel_edp_psr_enable_source(struct intel_dp *intel_dp)
 {
 	struct drm_device *dev = intel_dp_to_dev(intel_dp);
@@ -1655,8 +1700,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;
 	}
@@ -1708,28 +1753,54 @@  static bool intel_edp_psr_match_conditions(struct intel_dp *intel_dp)
 static void intel_edp_psr_do_enable(struct intel_dp *intel_dp)
 {
 	struct drm_device *dev = intel_dp_to_dev(intel_dp);
+	struct drm_i915_private *dev_priv = dev->dev_private;
 
-	if (!intel_edp_psr_match_conditions(intel_dp) ||
-	    intel_edp_is_psr_enabled(dev))
+	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)
 {
+	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 drm_device *dev = intel_dp_to_dev(intel_dp);
+	struct drm_i915_private *dev_priv = dev->dev_private;
+	uint32_t val = I915_READ(VLV_EDP_PSR_STATUS_CTL);
 
-	if (intel_edp_psr_match_conditions(intel_dp) &&
-	    !intel_edp_is_psr_enabled(dev))
-		intel_edp_psr_do_enable(intel_dp);
+	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);
+	val &= ~VLV_EDP_PSR_ACTIVE_ENTRY;
+	val &= ~VLV_EDP_PSR_ENABLE;
+	val &= ~VLV_EDP_PSR_MODE_MASK;
+	I915_WRITE(VLV_EDP_PSR_CTL, val);
 }
 
 void intel_edp_psr_disable(struct intel_dp *intel_dp)
@@ -1761,20 +1832,52 @@  void intel_edp_psr_update(struct drm_device *dev)
 			if (!is_edp_psr(dev))
 				return;
 
-			if (!intel_edp_psr_match_conditions(intel_dp))
-				intel_edp_psr_disable(intel_dp);
-			else
+			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_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);
+
+			if (!is_edp_psr(dev))
+				return;
+
+			dev_priv->psr.active = false;
+
+			I915_WRITE(VLV_EDP_PSR_CTL, VLV_EDP_PSR_RESET);
+			I915_WRITE(VLV_EDP_PSR_CTL, 0);
+			POSTING_READ(VLV_EDP_PSR_CTL);
+
+			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. */
 	ironlake_edp_backlight_off(intel_dp);
@@ -1831,6 +1934,7 @@  static void vlv_enable_dp(struct intel_encoder *encoder)
 	struct intel_dp *intel_dp = enc_to_intel_dp(&encoder->base);
 
 	ironlake_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 f3959f1..75131a2 100644
--- a/drivers/gpu/drm/i915/intel_drv.h
+++ b/drivers/gpu/drm/i915/intel_drv.h
@@ -735,6 +735,7 @@  void ironlake_edp_panel_vdd_off(struct intel_dp *intel_dp, bool sync);
 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 */