diff options
author | Rodrigo Vivi <rodrigo.vivi@intel.com> | 2014-11-19 07:37:00 -0800 |
---|---|---|
committer | Daniel Vetter <daniel.vetter@ffwll.ch> | 2014-12-03 09:35:08 +0100 |
commit | e2bbc343de8ea463af9da810b6e5e32dbd636b44 (patch) | |
tree | 6be98742806f0e921aff1a6f8ef1bc049e1d9577 /drivers/gpu/drm/i915/intel_psr.c | |
parent | c8f7df58f711e02b0788b75c054c6d32056ea170 (diff) |
drm/i915: PSR VLV/CHV: Introduce setup, enable and disable functions
The biggest difference from HSW/BDW PSR here is that VLV enable_source
function enables PSR but let it in Inactive state. So it might be called
on early stage along with setup and enable_sink ones.
v2: Rebase over intel_psr.c;
Remove docs from static functions;
Merge vlv_psr_active_on_pipe;
Timeout for psr transition is 250us;
Remove SRC_TRASMITTER_STATE;
v3: Rebase after is_psr_enabled function got removed;
Get SRC_TRANSMITTER_STATE back to be on the safe side since
default for panels is to require link training on exit when
main link off;
As pointed out by Durgadoss msecs_to_jiffies used on wait_for only uses int,
so let's use 1 instead. Althought the 1/4 of this is needed for the
transition let's use 1 for simplicity;
Cc: Durgadoss R <durgadoss.r@intel.com>
Signed-off-by: Rodrigo Vivi <rodrigo.vivi@intel.com>
Reviewed-by: Durgadoss R <durgadoss.r@intel.com>
Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
Diffstat (limited to 'drivers/gpu/drm/i915/intel_psr.c')
-rw-r--r-- | drivers/gpu/drm/i915/intel_psr.c | 155 |
1 files changed, 130 insertions, 25 deletions
diff --git a/drivers/gpu/drm/i915/intel_psr.c b/drivers/gpu/drm/i915/intel_psr.c index 576ad0222221..30f341ace83d 100644 --- a/drivers/gpu/drm/i915/intel_psr.c +++ b/drivers/gpu/drm/i915/intel_psr.c @@ -61,6 +61,17 @@ static bool is_edp_psr(struct intel_dp *intel_dp) return intel_dp->psr_dpcd[0] & DP_PSR_IS_SUPPORTED; } +static bool vlv_is_psr_active_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 void intel_psr_write_vsc(struct intel_dp *intel_dp, struct edp_vsc_psr *vsc_psr) { @@ -90,7 +101,23 @@ static void intel_psr_write_vsc(struct intel_dp *intel_dp, POSTING_READ(ctl_reg); } -static void intel_psr_setup_vsc(struct intel_dp *intel_dp) +static void vlv_psr_setup_vsc(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 drm_crtc *crtc = intel_dig_port->base.base.crtc; + enum pipe pipe = to_intel_crtc(crtc)->pipe; + uint32_t val; + + /* VLV auto-generate VSC package as per EDP 1.3 spec, Table 3.10 */ + val = I915_READ(VLV_VSCSDP(pipe)); + val &= ~VLV_EDP_PSR_SDP_FREQ_MASK; + val |= VLV_EDP_PSR_SDP_FREQ_EVFRAME; + I915_WRITE(VLV_VSCSDP(pipe), val); +} + +static void hsw_psr_setup_vsc(struct intel_dp *intel_dp) { struct edp_vsc_psr psr_vsc; @@ -103,7 +130,13 @@ static void intel_psr_setup_vsc(struct intel_dp *intel_dp) intel_psr_write_vsc(intel_dp, &psr_vsc); } -static void intel_psr_enable_sink(struct intel_dp *intel_dp) +static void vlv_psr_enable_sink(struct intel_dp *intel_dp) +{ + drm_dp_dpcd_writeb(&intel_dp->aux, DP_PSR_EN_CFG, + DP_PSR_ENABLE); +} + +static void hsw_psr_enable_sink(struct intel_dp *intel_dp) { struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp); struct drm_device *dev = dig_port->base.base.dev; @@ -147,7 +180,22 @@ static void intel_psr_enable_sink(struct intel_dp *intel_dp) (aux_clock_divider << DP_AUX_CH_CTL_BIT_CLOCK_2X_SHIFT)); } -static void intel_psr_enable_source(struct intel_dp *intel_dp) +static void vlv_psr_enable_source(struct intel_dp *intel_dp) +{ + struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp); + struct drm_device *dev = dig_port->base.base.dev; + struct drm_i915_private *dev_priv = dev->dev_private; + struct drm_crtc *crtc = dig_port->base.base.crtc; + enum pipe pipe = to_intel_crtc(crtc)->pipe; + + /* Transition from PSR_state 0 to PSR_state 1, i.e. PSR Inactive */ + I915_WRITE(VLV_PSRCTL(pipe), + VLV_EDP_PSR_MODE_SW_TIMER | + VLV_EDP_PSR_SRC_TRANSMITTER_STATE | + VLV_EDP_PSR_ENABLE); +} + +static void hsw_psr_enable_source(struct intel_dp *intel_dp) { struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp); struct drm_device *dev = dig_port->base.base.dev; @@ -225,7 +273,7 @@ static bool intel_psr_match_conditions(struct intel_dp *intel_dp) return true; } -static void intel_psr_do_enable(struct intel_dp *intel_dp) +static void intel_psr_activate(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; @@ -235,9 +283,12 @@ static void intel_psr_do_enable(struct intel_dp *intel_dp) WARN_ON(dev_priv->psr.active); lockdep_assert_held(&dev_priv->psr.lock); - /* Enable/Re-enable PSR on the host */ - intel_psr_enable_source(intel_dp); - + /* Enable/Re-enable PSR on the host + * On HSW+ after we enable PSR on source it will activate it + * as soon as it match configure idle_frame count. So + * we just actually enable it here on activation time. + */ + hsw_psr_enable_source(intel_dp); dev_priv->psr.active = true; } @@ -274,37 +325,67 @@ void intel_psr_enable(struct intel_dp *intel_dp) dev_priv->psr.busy_frontbuffer_bits = 0; - intel_psr_setup_vsc(intel_dp); + if (HAS_DDI(dev)) { + hsw_psr_setup_vsc(intel_dp); - /* 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); - /* Enable PSR on the panel */ - intel_psr_enable_sink(intel_dp); + /* Enable PSR on the panel */ + hsw_psr_enable_sink(intel_dp); + } else { + vlv_psr_setup_vsc(intel_dp); + + /* Enable PSR on the panel */ + vlv_psr_enable_sink(intel_dp); + + /* On HSW+ enable_source also means go to PSR entry/active + * state as soon as idle_frame achieved and here would be + * to soon. However on VLV enable_source just enable PSR + * but let it on inactive state. So we might do this prior + * to active transition, i.e. here. + */ + vlv_psr_enable_source(intel_dp); + } dev_priv->psr.enabled = intel_dp; unlock: mutex_unlock(&dev_priv->psr.lock); } -/** - * intel_psr_disable - Disable PSR - * @intel_dp: Intel DP - * - * This function needs to be called before disabling pipe. - */ -void intel_psr_disable(struct intel_dp *intel_dp) +static void vlv_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; - mutex_lock(&dev_priv->psr.lock); - if (!dev_priv->psr.enabled) { - mutex_unlock(&dev_priv->psr.lock); - return; + if (dev_priv->psr.active) { + /* Put VLV PSR back to PSR_state 0 that is PSR Disabled. */ + if (wait_for((I915_READ(VLV_PSRSTAT(intel_crtc->pipe)) & + VLV_EDP_PSR_IN_TRANS) == 0, 1)) + 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); + + dev_priv->psr.active = false; + } else { + WARN_ON(vlv_is_psr_active_on_pipe(dev, intel_crtc->pipe)); } +} + +static void hsw_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; if (dev_priv->psr.active) { I915_WRITE(EDP_PSR_CTL(dev), @@ -319,6 +400,30 @@ void intel_psr_disable(struct intel_dp *intel_dp) } else { WARN_ON(I915_READ(EDP_PSR_CTL(dev)) & EDP_PSR_ENABLE); } +} + +/** + * intel_psr_disable - Disable PSR + * @intel_dp: Intel DP + * + * This function needs to be called before disabling pipe. + */ +void intel_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; + + mutex_lock(&dev_priv->psr.lock); + if (!dev_priv->psr.enabled) { + mutex_unlock(&dev_priv->psr.lock); + return; + } + + if (HAS_DDI(dev)) + hsw_psr_disable(intel_dp); + else + vlv_psr_disable(intel_dp); dev_priv->psr.enabled = NULL; mutex_unlock(&dev_priv->psr.lock); @@ -357,7 +462,7 @@ static void intel_psr_work(struct work_struct *work) if (dev_priv->psr.busy_frontbuffer_bits) goto unlock; - intel_psr_do_enable(intel_dp); + intel_psr_activate(intel_dp); unlock: mutex_unlock(&dev_priv->psr.lock); } |