diff mbox

[07/11] drm/i915: Baytrail PSR: regs setup, enable and disable functions.

Message ID 1393631086-3880-8-git-send-email-rodrigo.vivi@gmail.com (mailing list archive)
State New, archived
Headers show

Commit Message

Rodrigo Vivi Feb. 28, 2014, 11:44 p.m. UTC
This patch just introduce registers and functions for PSR Baytrail.

It is important to highlight this patch doesn't work alone.
Baytrail PSR has some critical PSR-exit trigger issues that are fixed
on following patches.
Also, only on last one we mark baytrail/valleyview as HAS_PSR.

Cc: Ville Syrjälä <ville.syrjala@linux.intel.com>
Signed-off-by: Rodrigo Vivi <rodrigo.vivi@gmail.com>
---
 drivers/gpu/drm/i915/i915_drv.h      |   5 ++
 drivers/gpu/drm/i915/i915_reg.h      |  37 +++++++++
 drivers/gpu/drm/i915/intel_display.c |   2 +
 drivers/gpu/drm/i915/intel_dp.c      | 151 +++++++++++++++++++++++++++++++----
 drivers/gpu/drm/i915/intel_drv.h     |   1 +
 5 files changed, 179 insertions(+), 17 deletions(-)
diff mbox

Patch

diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
index c5abff7..18f457e 100644
--- a/drivers/gpu/drm/i915/i915_drv.h
+++ b/drivers/gpu/drm/i915/i915_drv.h
@@ -779,10 +779,15 @@  struct i915_fbc {
 	} no_fbc_reason;
 };
 
+struct intel_dp;
+
 struct i915_psr {
 	bool sink_support;
 	bool source_ok;
 	bool setup_done;
+	void (*setup)(struct intel_dp *intel_dp);
+	void (*enable_sink)(struct intel_dp *intel_dp);
+	void (*enable_source)(struct intel_dp *intel_dp);
 };
 
 enum intel_pch {
diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h
index 2f564ce..d6b203f 100644
--- a/drivers/gpu/drm/i915/i915_reg.h
+++ b/drivers/gpu/drm/i915/i915_reg.h
@@ -1984,6 +1984,43 @@ 
 #define VSYNCSHIFT(trans) _TRANSCODER2(trans, _VSYNCSHIFT_A)
 #define PIPESRC(trans) _TRANSCODER2(trans, _PIPEASRC)
 
+/* 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_PSRCTL(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_VSCSDP(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_PSRSTAT(pipe) _PIPE(pipe, _PSRSTATA, _PSRSTATB)
+
 /* 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 e23ff4c..26196b8 100644
--- a/drivers/gpu/drm/i915/intel_display.c
+++ b/drivers/gpu/drm/i915/intel_display.c
@@ -10533,6 +10533,8 @@  static void intel_setup_outputs(struct drm_device *dev)
 	if (SUPPORTS_TV(dev))
 		intel_tv_init(dev);
 
+	intel_edp_psr_init(dev);
+
 	list_for_each_entry(encoder, &dev->mode_config.encoder_list, base.head) {
 		encoder->base.possible_crtcs = encoder->crtc_mask;
 		encoder->base.possible_clones =
diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c
index 0799882..aabab25 100644
--- a/drivers/gpu/drm/i915/intel_dp.c
+++ b/drivers/gpu/drm/i915/intel_dp.c
@@ -1610,14 +1610,30 @@  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;
+
+	val = I915_READ(VLV_PSRSTAT(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,
@@ -1649,6 +1665,29 @@  static void intel_edp_psr_write_vsc(struct intel_dp *intel_dp,
 	POSTING_READ(ctl_reg);
 }
 
+static void vlv_edp_psr_setup(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;
+	uint32_t val;
+
+	if (dev_priv->psr.setup_done)
+		return;
+
+	val  = I915_READ(VLV_VSCSDP(PIPE_A));
+	val &= ~VLV_EDP_PSR_SDP_FREQ_MASK;
+	val |= VLV_EDP_PSR_SDP_FREQ_EVFRAME;
+	I915_WRITE(VLV_VSCSDP(PIPE_A), val);
+
+	val  = I915_READ(VLV_VSCSDP(PIPE_B));
+	val &= ~VLV_EDP_PSR_SDP_FREQ_MASK;
+	val |= VLV_EDP_PSR_SDP_FREQ_EVFRAME;
+	I915_WRITE(VLV_VSCSDP(PIPE_B), val);
+
+	dev_priv->psr.setup_done = true;
+}
+
 static void intel_edp_psr_setup(struct intel_dp *intel_dp)
 {
 	struct drm_device *dev = intel_dp_to_dev(intel_dp);
@@ -1673,6 +1712,13 @@  static void intel_edp_psr_setup(struct intel_dp *intel_dp)
 	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);
@@ -1703,6 +1749,27 @@  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 = 0;
+
+	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_PSRCTL(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);
@@ -1739,8 +1806,8 @@  static bool intel_edp_psr_match_conditions(struct intel_dp *intel_dp)
 
 	dev_priv->psr.source_ok = 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;
 	}
@@ -1791,22 +1858,30 @@  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 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;
 
 	/* Enable PSR on the panel */
-	intel_edp_psr_enable_sink(intel_dp);
+	dev_priv->psr.enable_sink(intel_dp);
 
 	/* Enable PSR on the host */
-	intel_edp_psr_enable_source(intel_dp);
+	dev_priv->psr.enable_source(intel_dp);
 }
 
 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;
 
 	if (!HAS_PSR(dev)) {
 		DRM_DEBUG_KMS("PSR not supported on this platform\n");
@@ -1819,13 +1894,35 @@  void intel_edp_psr_enable(struct intel_dp *intel_dp)
 	}
 
 	/* Setup PSR once */
-	intel_edp_psr_setup(intel_dp);
+	dev_priv->psr.setup(intel_dp);
 
-	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;
+
+	if (!dev_priv->psr.setup_done)
+		return;
+
+	if (wait_for((I915_READ(VLV_PSRSTAT(intel_crtc->pipe)) &
+		      VLV_EDP_PSR_IN_TRANS) == 0, 250))
+		WARN(1, "PSR transition took longer than expected\n");
+
+	val = I915_READ(VLV_PSRCTL(intel_crtc->pipe));
+	val &= ~VLV_EDP_PSR_ACTIVE_ENTRY;
+	val &= ~VLV_EDP_PSR_ENABLE;
+	val &= ~VLV_EDP_PSR_MODE_MASK;
+	I915_WRITE(VLV_PSRCTL(intel_crtc->pipe), val);
+}
+
 void intel_edp_psr_disable(struct intel_dp *intel_dp)
 {
 	struct drm_device *dev = intel_dp_to_dev(intel_dp);
@@ -1860,13 +1957,33 @@  void intel_edp_psr_update(struct drm_device *dev)
 			intel_dp = enc_to_intel_dp(&encoder->base);
 
 			if (!intel_edp_psr_match_conditions(intel_dp))
-				intel_edp_psr_disable(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);
+				intel_edp_psr_do_enable(intel_dp);
 		}
 }
 
+void intel_edp_psr_init(struct drm_device *dev)
+{
+	struct drm_i915_private *dev_priv = dev->dev_private;
+
+	if (!HAS_PSR(dev))
+		return;
+
+	if (IS_VALLEYVIEW(dev)) {
+		dev_priv->psr.setup = vlv_edp_psr_setup;
+		dev_priv->psr.enable_sink = vlv_edp_psr_enable_sink;
+		dev_priv->psr.enable_source = vlv_edp_psr_enable_source;
+	} else {
+		dev_priv->psr.setup = intel_edp_psr_setup;
+		dev_priv->psr.enable_sink = intel_edp_psr_enable_sink;
+		dev_priv->psr.enable_source = intel_edp_psr_enable_source;
+	}
+}
+
 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 180d602..2e4516d 100644
--- a/drivers/gpu/drm/i915/intel_drv.h
+++ b/drivers/gpu/drm/i915/intel_drv.h
@@ -757,6 +757,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_init(struct drm_device *dev);
 
 
 /* intel_dsi.c */