@@ -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 {
@@ -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)
@@ -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 =
@@ -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);
@@ -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 */
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(-)