[Intel-gfx] [PATCH v8 6/7] drm/i915/tgl: switch between dc3co and dc5 based on display idleness

Imre Deak imre.deak at intel.com
Mon Sep 23 16:46:37 UTC 2019


On Fri, Sep 13, 2019 at 01:53:38PM +0530, Anshuman Gupta wrote:
> DC3CO is useful power state, when DMC detects PSR2 idle frame
> while an active video playback, playing 30fps video on 60hz panel
> is the classic example of this use case.
> 
> B.Specs:49196 has a restriction to enable DC3CO only for Video Playback.
> It will be worthy to enable DC3CO after completion of each pageflip
> and switch back to DC5 when display is idle because driver doesn't
> differentiate between video playback and a normal pageflip.
> We will use Frontbuffer flush call tgl_dc3co_flush() to enable DC3CO
> state only for ORIGIN_FLIP flush call, because DC3CO state has primarily
> targeted for VPB use case. We are not interested here for frontbuffer
> invalidates calls because that triggers PSR2 exit, which will
> explicitly disable DC3CO.
> 
> DC5 and DC6 saves more power, but can't be entered during video
> playback because there are not enough idle frames in a row to meet
> most PSR2 panel deep sleep entry requirement typically 4 frames.
> As PSR2 existing implementation is using minimum 6 idle frames for
> deep sleep, it is safer to enable DC5/6 after 6 idle frames
> (By scheduling a delayed work of 6 idle frames, once DC3CO has been
> enabled after a pageflip).
> 
> After manually waiting for 6 idle frames DC5/6 will be enabled and
> PSR2 deep sleep idle frames will be restored to 6 idle frames, at this
> point DMC will triggers DC5/6 once PSR2 enters to deep sleep after
> 6 idle frames.
> In future when we will enable S/W PSR2 tracking, we can change the
> PSR2 required deep sleep idle frames to 1 so DMC can trigger the
> DC5/6 immediately after S/W manual waiting of 6 idle frames get
> complete.
> 
> v2: calculated s/w state to switch over dc3co when there is an
>     update. [Imre]
>     Used cancel_delayed_work_sync() in order to avoid any race
>     with already scheduled delayed work. [Imre]
> v3: Cancel_delayed_work_sync() may blocked the commit work.
>     hence dropping it, dc5_idle_thread() checks the valid wakeref before
>     putting the reference count, which avoids any chances of dropping
>     a zero wakeref. [Imre (IRC)]
> v4: Used frontbuffer flush mechanism. [Imre]
> v5: Used psr.pipe to extract frontbuffer busy bits. [Imre]
>     Used cancel_delayed_work_sync() in encoder disable path. [Imre]
>     Used mod_delayed_work() instead of cancelling and scheduling a
>     delayed work. [Imre]
>     Used psr.lock in tgl_dc5_idle_thread() to enable psr2 deep
>     sleep. [Imre]
>     Removed DC5_REQ_IDLE_FRAMES macro. [Imre]
> 
> Cc: Jani Nikula <jani.nikula at intel.com>
> Cc: Imre Deak <imre.deak at intel.com>
> Cc: Animesh Manna <animesh.manna at intel.com>
> Signed-off-by: Anshuman Gupta <anshuman.gupta at intel.com>
> ---
>  .../drm/i915/display/intel_display_power.c    | 97 +++++++++++++++++++
>  .../drm/i915/display/intel_display_power.h    |  4 +
>  .../gpu/drm/i915/display/intel_frontbuffer.c  |  1 +
>  drivers/gpu/drm/i915/display/intel_psr.c      |  1 +
>  drivers/gpu/drm/i915/i915_drv.h               |  1 +
>  5 files changed, 104 insertions(+)
> 
> diff --git a/drivers/gpu/drm/i915/display/intel_display_power.c b/drivers/gpu/drm/i915/display/intel_display_power.c
> index 42eabcdecf00..a605047cb28a 100644
> --- a/drivers/gpu/drm/i915/display/intel_display_power.c
> +++ b/drivers/gpu/drm/i915/display/intel_display_power.c
> @@ -20,6 +20,7 @@
>  #include "intel_sideband.h"
>  #include "intel_tc.h"
>  #include "intel_pm.h"
> +#include "intel_psr.h"
>  
>  bool intel_display_power_well_is_enabled(struct drm_i915_private *dev_priv,
>  					 enum i915_power_well_id power_well_id);
> @@ -773,6 +774,27 @@ static void gen9_set_dc_state(struct drm_i915_private *dev_priv, u32 state)
>  	dev_priv->csr.dc_state = val & mask;
>  }
>  
> +static u32 intel_get_frame_time_us(const struct intel_crtc_state *cstate)
> +{
> +	u32 pixel_rate, crtc_htotal, crtc_vtotal;
> +	u32 frametime_us;
> +
> +	if (!cstate || !cstate->base.active)
> +		return 0;
> +
> +	pixel_rate = cstate->pixel_rate;
> +
> +	if (WARN_ON(pixel_rate == 0))
> +		return 0;
> +
> +	crtc_htotal = cstate->base.adjusted_mode.crtc_htotal;
> +	crtc_vtotal = cstate->base.adjusted_mode.crtc_vtotal;
> +	frametime_us = DIV_ROUND_UP(crtc_htotal * crtc_vtotal * 1000ULL,
> +				    pixel_rate);
> +
> +	return frametime_us;
> +}
> +
>  void tgl_disable_psr2_transcoder_exitline(const struct intel_crtc_state *cstate)
>  {
>  	struct drm_i915_private *dev_priv = to_i915(cstate->base.crtc->dev);
> @@ -784,6 +806,9 @@ void tgl_disable_psr2_transcoder_exitline(const struct intel_crtc_state *cstate)
>  	val = I915_READ(EXITLINE(cstate->cpu_transcoder));
>  	val &= ~EXITLINE_ENABLE;
>  	I915_WRITE(EXITLINE(cstate->cpu_transcoder), val);
> +
> +	/* As psr2 encoder has disabled, cancel the dc5 idle delayed work */
> +	cancel_delayed_work_sync(&dev_priv->csr.idle_work);
>  }
>  
>  void tgl_enable_psr2_transcoder_exitline(const struct intel_crtc_state *cstate)
> @@ -817,6 +842,60 @@ void tgl_enable_psr2_transcoder_exitline(const struct intel_crtc_state *cstate)
>  	I915_WRITE(EXITLINE(cstate->cpu_transcoder), val);
>  }
>  
> +/*
> + * When we will enable manual PSR2 S/W tracking in future
> + * we will implement this entire DC3CO flush logic in
> + * intel_psr_flush().
> + */
> +void tgl_dc3co_flush(struct drm_i915_private *dev_priv,
> +		     unsigned int frontbuffer_bits, enum fb_op_origin origin)
> +{
> +	struct intel_crtc_state *cstate;
> +	struct intel_crtc *crtc;
> +	u32 delay;
> +	unsigned int busy_frontbuffer_bits;
> +
> +	if (!IS_TIGERLAKE(dev_priv))
> +		return;
> +
> +	if (!(dev_priv->csr.allowed_dc_mask & DC_STATE_EN_DC3CO))
> +		return;

Could we use crtc_state->dc3co_exitline instead of the two checks above?

> +
> +	if (origin != ORIGIN_FLIP)
> +		return;
> +
> +	mutex_lock(&dev_priv->psr.lock);
> +	crtc = intel_get_crtc_for_pipe(dev_priv, dev_priv->psr.pipe);
> +	cstate = to_intel_crtc_state(crtc->base.state);
> +
> +	frontbuffer_bits &=
> +		INTEL_FRONTBUFFER_ALL_MASK(dev_priv->psr.pipe);
> +
> +	busy_frontbuffer_bits &= ~frontbuffer_bits;

This is still wrong, busy_frontbuffer_bits is not inited.

> +
> +	if (!dev_priv->psr.psr2_enabled || !dev_priv->psr.active)
> +		goto unlock;
> +
> +	/*
> +	 * At every flip frontbuffer flush modified delay of delayed work,
> +	 * when delayed schedules that means display has been idle.
> +	 */
> +	if (!busy_frontbuffer_bits) {
> +		if (!dev_priv->psr.psr2_deep_slp_disabled) {

I think reenabling PSR deep sleep if it was already enabled is not a
problem, we don't need a flag just to avoid that.

> +			tgl_psr2_deep_sleep_disable(dev_priv);
> +			dev_priv->psr.psr2_deep_slp_disabled = true;
> +		}
> +		tgl_set_target_dc_state(dev_priv, DC_STATE_EN_DC3CO);
> +		/* DC5/DC6 required idle frames = 6 */
> +		delay = 6 * intel_get_frame_time_us(cstate);
> +		mod_delayed_work(system_wq, &dev_priv->csr.idle_work,
> +				 usecs_to_jiffies(delay));
> +	}
> +
> +unlock:
> +	mutex_unlock(&dev_priv->psr.lock);
> +}
> +
>  static bool tgl_dc3co_is_edp_connected(struct intel_crtc_state  *crtc_state)
>  {
>  	struct drm_atomic_state *state = crtc_state->base.state;
> @@ -876,6 +955,23 @@ void tgl_dc3co_crtc_get_config(struct intel_crtc_state *crtc_state)
>  		crtc_state->has_dc3co_exitline = true;
>  }
>  
> +void tgl_dc5_idle_thread(struct work_struct *work)
> +{
> +	struct drm_i915_private *dev_priv =
> +		container_of(work, typeof(*dev_priv), csr.idle_work.work);
> +
> +	/* If delayed work is pending, it is not idle */
> +	if (delayed_work_pending(&dev_priv->csr.idle_work))
> +		return;

The above should be checked with the psr.lock held to avoid a race.

> +
> +	DRM_DEBUG_KMS("DC5/6 idle thread\n");
> +	mutex_lock(&dev_priv->psr.lock);
> +	tgl_set_target_dc_state(dev_priv, DC_STATE_EN_UPTO_DC6);
> +	tgl_psr2_deep_sleep_enable(dev_priv);
> +	dev_priv->psr.psr2_deep_slp_disabled = false;
> +	mutex_unlock(&dev_priv->psr.lock);
> +}
> +
>  static void
>  allowed_dc_mask_to_target_dc_state(struct drm_i915_private *dev_priv)
>  {
> @@ -4237,6 +4333,7 @@ int intel_power_domains_init(struct drm_i915_private *dev_priv)
>  
>  	INIT_DELAYED_WORK(&power_domains->async_put_work,
>  			  intel_display_power_put_async_work);
> +	INIT_DELAYED_WORK(&dev_priv->csr.idle_work, tgl_dc5_idle_thread);
>  
>  	/*
>  	 * The enabling order will be from lower to higher indexed wells,
> diff --git a/drivers/gpu/drm/i915/display/intel_display_power.h b/drivers/gpu/drm/i915/display/intel_display_power.h
> index bf68f26a5a37..87725a23fead 100644
> --- a/drivers/gpu/drm/i915/display/intel_display_power.h
> +++ b/drivers/gpu/drm/i915/display/intel_display_power.h
> @@ -9,6 +9,7 @@
>  #include "intel_display.h"
>  #include "intel_runtime_pm.h"
>  #include "i915_reg.h"
> +#include "intel_frontbuffer.h"
>  
>  struct drm_i915_private;
>  struct intel_encoder;
> @@ -265,6 +266,9 @@ void tgl_dc3co_crtc_compute_config(struct drm_i915_private *dev_priv,
>  void tgl_dc3co_crtc_get_config(struct intel_crtc_state *crtc_state);
>  void tgl_disable_psr2_transcoder_exitline(const struct intel_crtc_state *state);
>  void tgl_enable_psr2_transcoder_exitline(const struct intel_crtc_state *state);
> +void tgl_dc3co_flush(struct drm_i915_private *dev_priv,
> +		     unsigned int frontbuffer_bits, enum fb_op_origin origin);
> +void tgl_dc5_idle_thread(struct work_struct *work);
>  
>  const char *
>  intel_display_power_domain_str(enum intel_display_power_domain domain);
> diff --git a/drivers/gpu/drm/i915/display/intel_frontbuffer.c b/drivers/gpu/drm/i915/display/intel_frontbuffer.c
> index fc40dc1fdbcc..c3b10f6e4382 100644
> --- a/drivers/gpu/drm/i915/display/intel_frontbuffer.c
> +++ b/drivers/gpu/drm/i915/display/intel_frontbuffer.c
> @@ -90,6 +90,7 @@ static void frontbuffer_flush(struct drm_i915_private *i915,
>  	might_sleep();
>  	intel_edp_drrs_flush(i915, frontbuffer_bits);
>  	intel_psr_flush(i915, frontbuffer_bits, origin);
> +	tgl_dc3co_flush(i915, frontbuffer_bits, origin);
>  	intel_fbc_flush(i915, frontbuffer_bits, origin);
>  }
>  
> diff --git a/drivers/gpu/drm/i915/display/intel_psr.c b/drivers/gpu/drm/i915/display/intel_psr.c
> index 11d37f96ce71..552eaf2bd16f 100644
> --- a/drivers/gpu/drm/i915/display/intel_psr.c
> +++ b/drivers/gpu/drm/i915/display/intel_psr.c
> @@ -574,6 +574,7 @@ static void tgl_disallow_dc3co_on_psr2_exit(struct drm_i915_private *dev_priv)
>  	cancel_delayed_work(&dev_priv->csr.idle_work);
>  	/* Before PSR2 exit disallow dc3co*/
>  	tgl_set_target_dc_state(dev_priv, DC_STATE_EN_UPTO_DC6);
> +	dev_priv->psr.psr2_deep_slp_disabled = false;
>  }
>  
>  static bool intel_psr2_config_valid(struct intel_dp *intel_dp,
> diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
> index 4521b9381db3..25591c128c59 100644
> --- a/drivers/gpu/drm/i915/i915_drv.h
> +++ b/drivers/gpu/drm/i915/i915_drv.h
> @@ -339,6 +339,7 @@ struct intel_csr {
>  	u32 dc_state;
>  	u32 target_dc_state;
>  	u32 allowed_dc_mask;
> +	struct delayed_work idle_work;
>  	intel_wakeref_t wakeref;
>  };
>  
> -- 
> 2.21.0
> 


More information about the Intel-gfx mailing list