Commit e2bbc343 authored by Rodrigo Vivi's avatar Rodrigo Vivi Committed by Daniel Vetter

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: default avatarRodrigo Vivi <rodrigo.vivi@intel.com>
Reviewed-by: default avatarDurgadoss R <durgadoss.r@intel.com>
Signed-off-by: default avatarDaniel Vetter <daniel.vetter@ffwll.ch>
parent c8f7df58
......@@ -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);
/* Enable PSR on the panel */
intel_psr_enable_sink(intel_dp);
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);
}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment