[Intel-gfx] [PATCH 05/13] drm/i915: Enable/Disable PSR

Rodrigo Vivi rodrigo.vivi at gmail.com
Wed Jun 12 22:55:46 CEST 2013


Adding Enable and Disable PSR functionalities. 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.

This patch is heavily based on initial PSR code by Sateesh Kavuri and
Kumar Shobhit but in a different implementation.

v2: * moved functions around and changed its names.
    * removed VSC DIP unset from disable.
    * remove FBC wa.
    * don't mask LSPS anymore.
    * incorporate new crtc usage after a rebase.

Credits-by: Sateesh Kavuri <sateesh.kavuri at intel.com>
Credits-by: Shobhit Kumar <shobhit.kumar at intel.com>
Signed-off-by: Rodrigo Vivi <rodrigo.vivi at gmail.com>
---
 drivers/gpu/drm/i915/i915_reg.h  |  42 ++++++++++
 drivers/gpu/drm/i915/intel_dp.c  | 172 +++++++++++++++++++++++++++++++++++++++
 drivers/gpu/drm/i915/intel_drv.h |   3 +
 3 files changed, 217 insertions(+)

diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h
index b1fdca9..4df97b4 100644
--- a/drivers/gpu/drm/i915/i915_reg.h
+++ b/drivers/gpu/drm/i915/i915_reg.h
@@ -1775,6 +1775,47 @@
 #define BCLRPAT(pipe) _PIPE(pipe, _BCLRPAT_A, _BCLRPAT_B)
 #define VSYNCSHIFT(trans) _TRANSCODER(trans, _VSYNCSHIFT_A, _VSYNCSHIFT_B)
 
+/* HSW eDP PSR registers */
+#define EDP_PSR_CTL				0x64800
+#define   EDP_PSR_ENABLE			(1<<31)
+#define   EDP_PSR_LINK_DISABLE			(0<<27)
+#define   EDP_PSR_LINK_STANDBY			(1<<27)
+#define   EDP_PSR_MIN_LINK_ENTRY_TIME_MASK	(3<<25)
+#define   EDP_PSR_MIN_LINK_ENTRY_TIME_8_LINES	(0<<25)
+#define   EDP_PSR_MIN_LINK_ENTRY_TIME_4_LINES	(1<<25)
+#define   EDP_PSR_MIN_LINK_ENTRY_TIME_2_LINES	(2<<25)
+#define   EDP_PSR_MIN_LINK_ENTRY_TIME_0_LINES	(3<<25)
+#define   EDP_PSR_MAX_SLEEP_TIME_SHIFT		20
+#define   EDP_PSR_SKIP_AUX_EXIT			(1<<12)
+#define   EDP_PSR_TP1_TP2_SEL			(0<<11)
+#define   EDP_PSR_TP1_TP3_SEL			(1<<11)
+#define   EDP_PSR_TP2_TP3_TIME_500us		(0<<8)
+#define   EDP_PSR_TP2_TP3_TIME_100us		(1<<8)
+#define   EDP_PSR_TP2_TP3_TIME_2500us		(2<<8)
+#define   EDP_PSR_TP2_TP3_TIME_0us		(3<<8)
+#define   EDP_PSR_TP1_TIME_500us		(0<<4)
+#define   EDP_PSR_TP1_TIME_100us		(1<<4)
+#define   EDP_PSR_TP1_TIME_2500us		(2<<4)
+#define   EDP_PSR_TP1_TIME_0us			(3<<4)
+#define   EDP_PSR_IDLE_FRAME_SHIFT		0
+
+#define EDP_PSR_AUX_CTL			0x64810
+#define EDP_PSR_AUX_DATA1		0x64814
+#define   EDP_PSR_DPCD_COMMAND		0x80060000
+#define EDP_PSR_AUX_DATA2		0x64818
+#define   EDP_PSR_DPCD_NORMAL_OPERATION	(1<<24)
+#define EDP_PSR_AUX_DATA3		0x6481c
+#define EDP_PSR_AUX_DATA4		0x64820
+#define EDP_PSR_AUX_DATA5		0x64824
+
+#define EDP_PSR_STATUS_CTL			0x64840
+#define   EDP_PSR_STATUS_STATE_MASK		(7<<29)
+
+#define EDP_PSR_DEBUG_CTL		0x64860
+#define   EDP_PSR_DEBUG_MASK_LPSP	(1<<27)
+#define   EDP_PSR_DEBUG_MASK_MEMUP	(1<<26)
+#define   EDP_PSR_DEBUG_MASK_HPD	(1<<25)
+
 /* VGA port control */
 #define ADPA			0x61100
 #define PCH_ADPA                0xe1100
@@ -2045,6 +2086,7 @@
  * (Haswell and newer) to see which VIDEO_DIP_DATA byte corresponds to each byte
  * of the infoframe structure specified by CEA-861. */
 #define   VIDEO_DIP_DATA_SIZE	32
+#define   VIDEO_DIP_VSC_DATA_SIZE	36
 #define VIDEO_DIP_CTL		0x61170
 /* Pre HSW: */
 #define   VIDEO_DIP_ENABLE		(1 << 31)
diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c
index 02b2865..e9c18bd 100644
--- a/drivers/gpu/drm/i915/intel_dp.c
+++ b/drivers/gpu/drm/i915/intel_dp.c
@@ -1356,6 +1356,178 @@ static bool is_edp_psr(struct intel_dp *intel_dp)
 		(intel_dp->psr_dpcd[0] & DP_PSR_IS_SUPPORTED));
 }
 
+static bool intel_edp_is_psr_enabled(struct drm_device *dev)
+{
+	struct drm_i915_private *dev_priv = dev->dev_private;
+	return I915_READ(EDP_PSR_CTL) & EDP_PSR_ENABLE;
+}
+
+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;
+
+	if (dev_priv->vbt.psr_idle_frames)
+	    val |= dev_priv->vbt.psr_idle_frames << EDP_PSR_IDLE_FRAME_SHIFT;
+	else
+	    val |= 1 << EDP_PSR_IDLE_FRAME_SHIFT;
+
+	if (intel_dp->psr_dpcd[1] & DP_PSR_NO_TRAIN_ON_EXIT) {
+		val |= EDP_PSR_LINK_STANDBY;
+		val |= EDP_PSR_TP2_TP3_TIME_0us;
+		val |= EDP_PSR_TP1_TIME_0us;
+		val |= EDP_PSR_SKIP_AUX_EXIT;
+	} else {
+		val |= EDP_PSR_LINK_DISABLE;
+		/* Use these Values from VBT
+		 * Case values are timings for HSW as of now
+		 * in multiple of 100us
+		 */
+		switch(dev_priv->vbt.psr_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->vbt.psr_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;
+		};
+	}
+
+	I915_WRITE(EDP_PSR_CTL, val |
+		   EDP_PSR_MIN_LINK_ENTRY_TIME_8_LINES |
+		   max_sleep_time << EDP_PSR_MAX_SLEEP_TIME_SHIFT |
+		   EDP_PSR_ENABLE);
+}
+
+void intel_edp_psr_write_vsc(struct intel_dp* intel_dp,
+			     struct edp_vsc_psr *vsc_psr)
+{
+	struct drm_device *dev = intel_dp_to_dev(intel_dp);
+	struct drm_i915_private *dev_priv = dev->dev_private;
+	struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp);
+	struct intel_crtc *crtc = to_intel_crtc(dig_port->base.base.crtc);
+
+	u32 ctl_reg = HSW_TVIDEO_DIP_CTL(crtc->config.cpu_transcoder);
+	u32 data_reg = HSW_TVIDEO_DIP_VSC_DATA(crtc->config.cpu_transcoder);
+	uint32_t *data = (uint32_t *) vsc_psr;
+	unsigned int i;
+	u32 val = I915_READ(ctl_reg);
+
+	/* As per eDP spec, wait for vblank to send SDP VSC packet */
+	intel_wait_for_vblank(dev, crtc->pipe);
+
+	/* As per BSPec (Pipe Video Data Island Packet), besides wait for
+	   vsync we need to disable the video DIP being updated before program
+	   video DIP data buffer registers for DIP being updated.*/
+	I915_WRITE(ctl_reg, val & ~VIDEO_DIP_ENABLE_VSC_HSW);
+	POSTING_READ(ctl_reg);
+
+	for (i = 0; i < VIDEO_DIP_VSC_DATA_SIZE; i += 4) {
+                if (i < sizeof(struct edp_vsc_psr))
+                        I915_WRITE(data_reg + i, *data++);
+                else
+                        I915_WRITE(data_reg + i, 0);
+        }
+
+	I915_WRITE(ctl_reg, val | VIDEO_DIP_ENABLE_VSC_HSW);
+	POSTING_READ(ctl_reg);
+}
+
+void intel_edp_psr_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;
+	struct edp_vsc_psr psr_vsc;
+	uint32_t aux_clock_divider = get_aux_clock_divider(intel_dp);
+	int precharge = 0x3;
+	int msg_size = 5;       /* Header(4) + Message(1) */
+
+	if (!is_edp_psr(intel_dp) || intel_edp_is_psr_enabled(dev))
+		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);
+
+	/* Enable PSR in sink */
+	if (intel_dp->psr_dpcd[1] & DP_PSR_NO_TRAIN_ON_EXIT)
+		intel_dp_aux_native_write_1(intel_dp, DP_PSR_EN_CFG,
+					    DP_PSR_ENABLE &
+					    ~DP_PSR_MAIN_LINK_ACTIVE);
+	else
+		intel_dp_aux_native_write_1(intel_dp, DP_PSR_EN_CFG,
+					    DP_PSR_ENABLE |
+					    DP_PSR_MAIN_LINK_ACTIVE);
+
+	/* Setup AUX registers */
+	I915_WRITE(EDP_PSR_AUX_DATA1, EDP_PSR_DPCD_COMMAND);
+	I915_WRITE(EDP_PSR_AUX_DATA2, EDP_PSR_DPCD_NORMAL_OPERATION);
+	I915_WRITE(EDP_PSR_AUX_CTL,
+		   DP_AUX_CH_CTL_TIME_OUT_400us |
+		   (msg_size << DP_AUX_CH_CTL_MESSAGE_SIZE_SHIFT) |
+		   (precharge << DP_AUX_CH_CTL_PRECHARGE_2US_SHIFT) |
+		   (aux_clock_divider << DP_AUX_CH_CTL_BIT_CLOCK_2X_SHIFT));
+
+	/* Avoid continuous PSR exit by masking memup and hpd */
+	I915_WRITE(EDP_PSR_DEBUG_CTL, EDP_PSR_DEBUG_MASK_MEMUP |
+		   EDP_PSR_DEBUG_MASK_HPD);
+
+	/* Disable unused interrupts */
+	I915_WRITE(GEN6_PMINTRMSK, GEN6_PM_RP_UP_EI_EXPIRED |
+		   GEN6_PM_RP_DOWN_EI_EXPIRED);
+
+	/* Enable PSR on the host */
+	intel_edp_psr_enable_src(intel_dp);
+}
+
+void intel_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;
+	struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp);
+	struct intel_crtc *intel_crtc = to_intel_crtc(dig_port->base.base.crtc);
+	uint32_t val;
+
+	if (!intel_edp_is_psr_enabled(dev))
+		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_STATE_MASK) == 0, 2000, 10))
+		DRM_ERROR("Timed out waiting for PSR Idle State\n");
+
+	intel_wait_for_vblank(dev, intel_crtc->pipe);
+}
+
 static void intel_disable_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 18d9dea..5113572 100644
--- a/drivers/gpu/drm/i915/intel_drv.h
+++ b/drivers/gpu/drm/i915/intel_drv.h
@@ -809,4 +809,7 @@ extern bool intel_set_pch_fifo_underrun_reporting(struct drm_device *dev,
 						 enum transcoder pch_transcoder,
 						 bool enable);
 
+extern void intel_edp_psr_enable(struct intel_dp* intel_dp);
+extern void intel_edp_psr_disable(struct intel_dp* intel_dp);
+
 #endif /* __INTEL_DRV_H__ */
-- 
1.7.11.7




More information about the Intel-gfx mailing list