[Intel-gfx] [PATCH 6/8] drm/i915: Enable/Disable PSR on HSW

Rodrigo Vivi rodrigo.vivi at gmail.com
Fri Jan 11 20:57:55 CET 2013


From: Shobhit Kumar <shobhit.kumar at intel.com>

Added eDP PSR enable functionality. This includes setting the PSR
configuration over AUX, sending SDP VSC DIP over the eDP PIPE config,
enabling PSR in the sink via DPCD register and finally enabling PSR on
the host. PSR works only in LPSP mode, so put the PIPE_DDI in DDIA
always on

This patch is heavily based on initial PSR code by Sateesh Kavuri but is
quite different in implementation. Makes use of VBT parsed data and also
the code has been cleaned up.

Credits-by: Sateesh Kavuri <sateesh.kavuri at intel.com>
Signed-off-by: Shobhit Kumar <shobhit.kumar at intel.com>

v2: fix getting base.crtc from intel_dp and fix DDI_EDP_INPUT_A_ON entry
Signed-off-by: Rodrigo Vivi <rodrigo.vivi at gmail.com>
---
 drivers/gpu/drm/i915/i915_reg.h  |   3 +
 drivers/gpu/drm/i915/intel_ddi.c |   6 +-
 drivers/gpu/drm/i915/intel_dp.c  | 170 +++++++++++++++++++++++++++++++++++++++
 drivers/gpu/drm/i915/intel_drv.h |   3 +
 4 files changed, 181 insertions(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h
index 61de724..289c6c0 100644
--- a/drivers/gpu/drm/i915/i915_reg.h
+++ b/drivers/gpu/drm/i915/i915_reg.h
@@ -1594,6 +1594,9 @@
 #define EDP_PSR_STATUS_CTL	0x64840
 #define  EDP_PSR_STATUS_MASK	(7<<29)
 #define EDP_PSR_PERF_CNT	0x64844
+#define EDP_PSR_DEBUG_CTL	0x64860
+#define  EDP_PSR_DEBUG_MASK_MEMUP	(1<<26)
+#define  EDP_PSR_DEBUG_MASK_HPD		(1<<25)
 
 /* VGA port control */
 #define ADPA			0x61100
diff --git a/drivers/gpu/drm/i915/intel_ddi.c b/drivers/gpu/drm/i915/intel_ddi.c
index f02b3fe..05d3a30 100644
--- a/drivers/gpu/drm/i915/intel_ddi.c
+++ b/drivers/gpu/drm/i915/intel_ddi.c
@@ -958,9 +958,13 @@ void intel_ddi_enable_pipe_func(struct drm_crtc *crtc)
 		temp |= TRANS_DDI_PHSYNC;
 
 	if (cpu_transcoder == TRANSCODER_EDP) {
+		struct intel_dp *intel_dp = enc_to_intel_dp(encoder);
 		switch (pipe) {
 		case PIPE_A:
-			temp |= TRANS_DDI_EDP_INPUT_A_ONOFF;
+			if (intel_dp->psr_dpcd[0] & 0x1)
+				temp |= TRANS_DDI_EDP_INPUT_A_ON;
+			else
+				temp |= TRANS_DDI_EDP_INPUT_A_ONOFF;
 			break;
 		case PIPE_B:
 			temp |= TRANS_DDI_EDP_INPUT_B_ONOFF;
diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c
index c35caf1..208128c 100644
--- a/drivers/gpu/drm/i915/intel_dp.c
+++ b/drivers/gpu/drm/i915/intel_dp.c
@@ -83,6 +83,13 @@ static struct drm_device *intel_dp_to_dev(struct intel_dp *intel_dp)
 	return intel_dig_port->base.base.dev;
 }
 
+static struct drm_crtc *intel_dp_to_crtc(struct intel_dp *intel_dp)
+{
+	struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp);
+
+	return intel_dig_port->base.base.crtc;
+}
+
 static struct intel_dp *intel_attached_dp(struct drm_connector *connector)
 {
 	return enc_to_intel_dp(&intel_attached_encoder(connector)->base);
@@ -1472,6 +1479,169 @@ static void intel_edp_psr_setup(struct intel_dp *intel_dp)
 	intel_dp->psr_setup = 1;
 }
 
+static bool
+intel_edp_is_psr_enabled(struct intel_dp* intel_dp)
+{
+	struct drm_device *dev = intel_dp_to_dev(intel_dp);
+	struct drm_i915_private *dev_priv = dev->dev_private;
+	return (I915_READ(EDP_PSR_CTL) & (1<<31)) ? true : false;
+}
+
+
+static void
+intel_edp_psr_enable_src(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 max_sleep_time = 0x1f;
+	uint32_t val = 0x0;
+
+	/* Use VBT values which are parsed in
+	 * dev_priv->idle_frames,
+	 * but the BIOS initializes this to zero today
+	 * so hardcode
+	 */
+	uint32_t idle_frames = 6;
+
+	if (intel_dp->psr_dpcd[1] & 0x1) {
+		/* No link training on PSR Exit required */
+		val |= EDP_PSR_TP2_TP3_TIME_0us;
+		val |= EDP_PSR_TP1_TIME_0us;
+		val |= EDP_PSR_SKIP_AUX_EXIT;
+	} else {
+		/* Use these Values from VBT
+		 * Case values are timings for HSW as of now
+		 * in multiple of 100us
+		 */
+		switch(dev_priv->wakeup_tp1) {
+			case 1:
+				val |= EDP_PSR_TP1_TIME_100us;
+				break;
+			case 5:
+				val |= EDP_PSR_TP1_TIME_500us;
+				break;
+			case 25:
+				val |= EDP_PSR_TP1_TIME_2500us;
+				break;
+			default:
+				val |= EDP_PSR_TP1_TIME_500us;
+				break;
+		};
+		switch(dev_priv->wakeup_tp2_tp3) {
+			case 1:
+				val |= EDP_PSR_TP2_TP3_TIME_100us;
+				break;
+			case 5:
+				val |= EDP_PSR_TP2_TP3_TIME_500us;
+				break;
+			case 25:
+				val |= EDP_PSR_TP2_TP3_TIME_2500us;
+				break;
+			default:
+				val |= EDP_PSR_TP2_TP3_TIME_500us;
+				break;
+		};
+	}
+
+	/* Disable main link. Anyway in HSW steppings today
+	 * link standby does not work
+	 *
+	 * Later used VBT info (already parsed and available)
+	 * while supporting standby we need to program
+	 * val |= EDP_PSR_MIN_LINK_ENTRY_TIME_X_LINES based on VBT
+	 */
+	val = (val & ~EDP_PSR_LINK_STANDBY) |
+		(max_sleep_time << EDP_PSR_MAX_SLEEP_TIME_SHIFT) |
+		(idle_frames << EDP_PSR_IDLE_FRAME_SHIFT) |
+		EDP_PSR_ENABLE;
+
+	I915_WRITE(EDP_PSR_CTL, val);
+}
+
+void intel_edp_enable_psr(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 intel_crtc *intel_crtc = to_intel_crtc(intel_dp_to_crtc(intel_dp));
+	struct edp_vsc_psr psr_vsc;
+	uint32_t *vsc_data = (uint32_t *) &psr_vsc;
+	int i = 0, vsc_len = sizeof(struct edp_vsc_psr);
+
+	if (!is_edp_psr(intel_dp))
+		return;
+
+	/* setup AUX registers in case returned from pm states */
+	intel_edp_psr_setup(intel_dp);
+
+	/* Check if PSR is already enabled */
+	if (!intel_edp_is_psr_enabled(intel_dp)) {
+		/* Prepare VSC packet as per EDP 1.3 spec, Table 3.10 */
+		memset(&psr_vsc, 0, sizeof(psr_vsc));
+		psr_vsc.sdp_header.id = 0;
+		psr_vsc.sdp_header.type = 0x7;
+		psr_vsc.sdp_header.revision = 0x2;
+		psr_vsc.sdp_header.valid_payload_bytes = 0x8;
+
+		/* As per eDP spec, wait for vblank to send SDP VSC packet */
+		intel_wait_for_vblank(dev, intel_crtc->pipe);
+
+		/* Load the VSC DIP packet */
+		for(i = 0; i < vsc_len; i += 4)
+			I915_WRITE((VIDEO_DIP_VSC_DATA_EDP + i), vsc_data[i]);
+#if 0
+		/* TBD:
+		 * We might not have to do explicitely as hardware will take care of this */
+		/* Enable the DIP register */
+		val = I915_READ(VIDEO_DIP_CTL_EDP);
+		I915_WRITE(VIDEO_DIP_CTL_EDP, val | VIDEOP_DIP_VSC);
+#endif
+		/* Enable PSR in sink by setting bit 0 in DPCD config reg
+		 * along with the transmitter state during PSR active
+		 * Transmitter state later can be ready from VBT. As of now
+		 * program the full link down
+		 *
+		 */
+		intel_dp_aux_native_write_1(intel_dp, DP_PSR_EN_CFG,
+						DP_PSR_ENABLE &
+						~DP_PSR_MAIN_LINK_ACTIVE);
+
+		/* Enable PSR on the host */
+		intel_edp_psr_enable_src(intel_dp);
+	}
+}
+
+void intel_edp_disable_psr(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 intel_crtc *intel_crtc = to_intel_crtc(intel_dp_to_crtc(intel_dp));
+	uint32_t val;
+	if (!intel_edp_is_psr_enabled(intel_dp))
+		return;
+
+	val = I915_READ(EDP_PSR_CTL);
+	I915_WRITE(EDP_PSR_CTL, (val & ~EDP_PSR_ENABLE));
+
+	/* Wait till PSR is idle */
+	if (_wait_for((I915_READ(EDP_PSR_STATUS_CTL) & EDP_PSR_STATUS_MASK) == 0, 2000, 10))
+		DRM_ERROR("Timed out waiting for PSR Idle State\n");
+
+	intel_wait_for_vblank(dev, intel_crtc->pipe);
+
+#if 0
+	/* TBD:
+	 * Following is not yet confirmed from H/W team.
+	 * As per last discussion we do not need to disable
+	 * VSC DIP explicitely. Just maintaining the code in
+	 * case we have to do this later at some point
+	 */
+
+	/* Disable VSC DIP */
+	val = I915_READ(VIDEO_DIP_CTL_EDP);
+	I915_WRITE(VIDEO_DIP_CTL_EDP, val & ~VIDEOP_DIP_VSC);
+#endif
+}
+
 static void intel_enable_dp(struct intel_encoder *encoder)
 {
 	struct intel_dp *intel_dp = enc_to_intel_dp(&encoder->base);
diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
index 82a85ad..a9960be 100644
--- a/drivers/gpu/drm/i915/intel_drv.h
+++ b/drivers/gpu/drm/i915/intel_drv.h
@@ -704,4 +704,7 @@ extern bool
 intel_ddi_connector_get_hw_state(struct intel_connector *intel_connector);
 extern void intel_ddi_fdi_disable(struct drm_crtc *crtc);
 
+extern void intel_edp_enable_psr(struct intel_dp* intel_dp);
+extern void intel_edp_disable_psr(struct intel_dp* intel_dp);
+
 #endif /* __INTEL_DRV_H__ */
-- 
1.7.11.7




More information about the Intel-gfx mailing list