On Tue, Dec 19, 2017 at 05:26:58AM +0000, Dhinakaran Pandiyan wrote:
> When DC states are enabled and PSR is active, the hardware enters DC5/DC6
> states resulting in frame counter resets. The frame counter resets mess
> up the vblank counting logic. In order to disable DC states when
> vblank interrupts are required and to disallow DC states when vblanks
> interrupts are already enabled, introduce a new VBLANK power domain.
> Since this power domain reference needs to be acquired and released in
> atomic context, _vblank_get() and _vblank_put() methods skip the
> power_domain mutex.
> 
> v2: Fix deadlock by switching irqsave spinlock.
>     Implement atomic version of get_if_enabled.
>     Modify power_domain_verify_state to check power well use count and
> enabled status atomically.
>     Rewrite of intel_power_well_{get,put}
> 
> Cc: Daniel Vetter <daniel.vet...@ffwll.ch>
> Cc: Ville Syrjälä <ville.syrj...@linux.intel.com>
> Cc: Rodrigo Vivi <rodrigo.v...@intel.com>

+ Cc: Imre Deak <imre.d...@intel.com>

> Signed-off-by: Dhinakaran Pandiyan <dhinakaran.pandi...@intel.com>
> ---
>  drivers/gpu/drm/i915/i915_drv.h         |  18 ++++
>  drivers/gpu/drm/i915/intel_drv.h        |   3 +
>  drivers/gpu/drm/i915/intel_runtime_pm.c | 184 
> +++++++++++++++++++++++++++++---
>  3 files changed, 192 insertions(+), 13 deletions(-)
> 
> diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
> index ddadeb9eaf49..db597c5ebaed 100644
> --- a/drivers/gpu/drm/i915/i915_drv.h
> +++ b/drivers/gpu/drm/i915/i915_drv.h
> @@ -397,6 +397,7 @@ enum intel_display_power_domain {
>       POWER_DOMAIN_AUX_C,
>       POWER_DOMAIN_AUX_D,
>       POWER_DOMAIN_GMBUS,
> +     POWER_DOMAIN_VBLANK,
>       POWER_DOMAIN_MODESET,
>       POWER_DOMAIN_GT_IRQ,
>       POWER_DOMAIN_INIT,
> @@ -1476,6 +1477,23 @@ struct i915_power_well {
>                       bool has_fuses:1;
>               } hsw;
>       };
> +
> +     /* Lock to serialize access to count, hw_enabled and ops, used for
> +      * power wells that have supports_atomix_ctx set to True.
> +      */
> +     spinlock_t lock;
> +
> +     /* Indicates that the get/put methods for this power well can be called
> +      * in atomic contexts, requires .ops to not sleep. This is valid
> +      * only for the DC_OFF power well currently.
> +      */
> +     bool supports_atomic_ctx;
> +
> +     /* DC_OFF power well was disabled since the last time vblanks were
> +      * disabled.
> +      */
> +     bool dc_off_disabled;
> +
>       const struct i915_power_well_ops *ops;
>  };
>  
> diff --git a/drivers/gpu/drm/i915/intel_drv.h 
> b/drivers/gpu/drm/i915/intel_drv.h
> index 48676e99316e..6822118f3c4d 100644
> --- a/drivers/gpu/drm/i915/intel_drv.h
> +++ b/drivers/gpu/drm/i915/intel_drv.h
> @@ -1798,6 +1798,9 @@ bool intel_display_power_get_if_enabled(struct 
> drm_i915_private *dev_priv,
>                                       enum intel_display_power_domain domain);
>  void intel_display_power_put(struct drm_i915_private *dev_priv,
>                            enum intel_display_power_domain domain);
> +void intel_display_power_vblank_get(struct drm_i915_private *dev_priv,
> +                                 bool *needs_restore);
> +void intel_display_power_vblank_put(struct drm_i915_private *dev_priv);
>  
>  static inline void
>  assert_rpm_device_not_suspended(struct drm_i915_private *dev_priv)
> diff --git a/drivers/gpu/drm/i915/intel_runtime_pm.c 
> b/drivers/gpu/drm/i915/intel_runtime_pm.c
> index 992caec1fbc4..fc6812ed6137 100644
> --- a/drivers/gpu/drm/i915/intel_runtime_pm.c
> +++ b/drivers/gpu/drm/i915/intel_runtime_pm.c
> @@ -56,6 +56,19 @@ static struct i915_power_well *
>  lookup_power_well(struct drm_i915_private *dev_priv,
>                 enum i915_power_well_id power_well_id);
>  
> +/* Optimize for the case when this is called from atomic contexts,
> + * although the case is unlikely.
> + */
> +#define power_well_lock(power_well, flags) do {                      \
> +     if (likely(power_well->supports_atomic_ctx))            \

you mention unlikely on commend and call likely here, why?

> +             spin_lock_irqsave(&power_well->lock, flags);    \
> +     } while (0)
> +
> +#define power_well_unlock(power_well, flags) do {                    \
> +     if (likely(power_well->supports_atomic_ctx))                    \
> +             spin_unlock_irqrestore(&power_well->lock, flags);       \
> +     } while (0)
> +
>  const char *
>  intel_display_power_domain_str(enum intel_display_power_domain domain)
>  {
> @@ -126,6 +139,8 @@ intel_display_power_domain_str(enum 
> intel_display_power_domain domain)
>               return "AUX_D";
>       case POWER_DOMAIN_GMBUS:
>               return "GMBUS";
> +     case POWER_DOMAIN_VBLANK:
> +             return "VBLANK";
>       case POWER_DOMAIN_INIT:
>               return "INIT";
>       case POWER_DOMAIN_MODESET:
> @@ -141,6 +156,9 @@ intel_display_power_domain_str(enum 
> intel_display_power_domain domain)
>  static void intel_power_well_enable(struct drm_i915_private *dev_priv,
>                                   struct i915_power_well *power_well)
>  {
> +     if (power_well->supports_atomic_ctx)

why not (un)likely check here as well?

> +             assert_spin_locked(&power_well->lock);
> +
>       DRM_DEBUG_KMS("enabling %s\n", power_well->name);
>       power_well->ops->enable(dev_priv, power_well);
>       power_well->hw_enabled = true;
> @@ -149,26 +167,43 @@ static void intel_power_well_enable(struct 
> drm_i915_private *dev_priv,
>  static void intel_power_well_disable(struct drm_i915_private *dev_priv,
>                                    struct i915_power_well *power_well)
>  {
> +     if (power_well->supports_atomic_ctx)
> +             assert_spin_locked(&power_well->lock);
> +
>       DRM_DEBUG_KMS("disabling %s\n", power_well->name);
>       power_well->hw_enabled = false;
>       power_well->ops->disable(dev_priv, power_well);
>  }
>  
> -static void intel_power_well_get(struct drm_i915_private *dev_priv,
> +static void __intel_power_well_get(struct drm_i915_private *dev_priv,
>                                struct i915_power_well *power_well)
>  {
>       if (!power_well->count++)
>               intel_power_well_enable(dev_priv, power_well);
>  }
>  
> +static void intel_power_well_get(struct drm_i915_private *dev_priv,
> +                              struct i915_power_well *power_well)
> +{
> +     unsigned long flags = 0;
> +
> +     power_well_lock(power_well, flags);
> +     __intel_power_well_get(dev_priv, power_well);
> +     power_well_unlock(power_well, flags);
> +}
> +
>  static void intel_power_well_put(struct drm_i915_private *dev_priv,
>                                struct i915_power_well *power_well)
>  {
> +     unsigned long flags = 0;
> +
> +     power_well_lock(power_well, flags);
>       WARN(!power_well->count, "Use count on power well %s is already zero",
>            power_well->name);
>  
>       if (!--power_well->count)
>               intel_power_well_disable(dev_priv, power_well);
> +     power_well_unlock(power_well, flags);
>  }
>  
>  /**
> @@ -726,6 +761,7 @@ static void gen9_dc_off_power_well_disable(struct 
> drm_i915_private *dev_priv,
>               skl_enable_dc6(dev_priv);
>       else if (dev_priv->csr.allowed_dc_mask & DC_STATE_EN_UPTO_DC5)
>               gen9_enable_dc5(dev_priv);
> +     power_well->dc_off_disabled = true;

why do we need to track this like this?

Seems a big deviation of the hole generic power well state...

>  }
>  
>  static void i9xx_power_well_sync_hw_noop(struct drm_i915_private *dev_priv,
> @@ -1443,6 +1479,58 @@ static void chv_pipe_power_well_disable(struct 
> drm_i915_private *dev_priv,
>       chv_set_pipe_power_well(dev_priv, power_well, false);
>  }
>  
> +void intel_display_power_vblank_get(struct drm_i915_private *dev_priv,
> +                                 bool *needs_restore)
> +{
> +     struct i915_power_domains *power_domains  = &dev_priv->power_domains;
> +     struct i915_power_well *power_well;
> +     enum intel_display_power_domain domain = POWER_DOMAIN_VBLANK;
> +
> +     *needs_restore = false;
> +
> +     if (!HAS_CSR(dev_priv))
> +             return;
> +
> +     if (!CAN_PSR(dev_priv))
> +             return;
> +
> +     intel_runtime_pm_get_noresume(dev_priv);
> +
> +     for_each_power_domain_well(dev_priv, power_well, BIT_ULL(domain)) {
> +             unsigned long flags = 0;
> +
> +             power_well_lock(power_well, flags);
> +             __intel_power_well_get(dev_priv, power_well);
> +             *needs_restore = power_well->dc_off_disabled;
> +             power_well->dc_off_disabled = false;
> +             power_well_unlock(power_well, flags);
> +     }
> +
> +     atomic_inc(&power_domains->domain_use_count[domain]);
> +}
> +
> +void intel_display_power_vblank_put(struct drm_i915_private *dev_priv)
> +{
> +     struct i915_power_domains *power_domains = &dev_priv->power_domains;
> +     struct i915_power_well *power_well;
> +     enum intel_display_power_domain domain = POWER_DOMAIN_VBLANK;
> +
> +     if (!HAS_CSR(dev_priv))
> +             return;
> +
> +     if (!CAN_PSR(dev_priv))
> +             return;
> +
> +     WARN(atomic_dec_return(&power_domains->domain_use_count[domain]) < 0,
> +          "Use count on domain %s was already zero\n",
> +          intel_display_power_domain_str(domain));
> +
> +     for_each_power_domain_well_rev(dev_priv, power_well, BIT_ULL(domain))
> +             intel_power_well_put(dev_priv, power_well);
> +
> +     intel_runtime_pm_put(dev_priv);
> +}
> +
>  static void
>  __intel_display_power_get_domain(struct drm_i915_private *dev_priv,
>                                enum intel_display_power_domain domain)
> @@ -1482,6 +1570,38 @@ void intel_display_power_get(struct drm_i915_private 
> *dev_priv,
>       mutex_unlock(&power_domains->lock);
>  }
>  
> +static bool dc_off_get_if_enabled(struct drm_i915_private *dev_priv,
> +                               enum intel_display_power_domain domain)
> +{
> +     struct i915_power_well *power_well;
> +     bool is_enabled;
> +     unsigned long flags = 0;
> +
> +     power_well = lookup_power_well(dev_priv, SKL_DISP_PW_DC_OFF);
> +     if (!power_well || !(power_well->domains & domain))
> +             return true;
> +
> +     power_well_lock(power_well, flags);
> +     is_enabled = power_well->hw_enabled;
> +     if (is_enabled)
> +             __intel_power_well_get(dev_priv, power_well);
> +     power_well_unlock(power_well, flags);
> +
> +     return is_enabled;
> +}
> +
> +static void dc_off_put(struct drm_i915_private *dev_priv,
> +                    enum intel_display_power_domain domain)
> +{
> +     struct i915_power_well *power_well;
> +
> +     power_well = lookup_power_well(dev_priv, SKL_DISP_PW_DC_OFF);
> +     if (!power_well || !(power_well->domains & domain))
> +             return;
> +
> +     intel_power_well_put(dev_priv, power_well);
> +}
> +
>  /**
>   * intel_display_power_get_if_enabled - grab a reference for an enabled 
> display power domain
>   * @dev_priv: i915 device instance
> @@ -1498,20 +1618,24 @@ bool intel_display_power_get_if_enabled(struct 
> drm_i915_private *dev_priv,
>                                       enum intel_display_power_domain domain)
>  {
>       struct i915_power_domains *power_domains = &dev_priv->power_domains;
> -     bool is_enabled;
> +     bool is_enabled = false;
>  
>       if (!intel_runtime_pm_get_if_in_use(dev_priv))
>               return false;
>  
>       mutex_lock(&power_domains->lock);
>  
> +     if (!dc_off_get_if_enabled(dev_priv, domain))
> +             goto out;
> +
>       if (__intel_display_power_is_enabled(dev_priv, domain)) {
>               __intel_display_power_get_domain(dev_priv, domain);
>               is_enabled = true;
> -     } else {
> -             is_enabled = false;
>       }
>  
> +     dc_off_put(dev_priv, domain);
> +
> +out:
>       mutex_unlock(&power_domains->lock);
>  
>       if (!is_enabled)
> @@ -1709,6 +1833,7 @@ void intel_display_power_put(struct drm_i915_private 
> *dev_priv,
>       BIT_ULL(POWER_DOMAIN_GT_IRQ) |                  \
>       BIT_ULL(POWER_DOMAIN_MODESET) |                 \
>       BIT_ULL(POWER_DOMAIN_AUX_A) |                   \
> +     BIT_ULL(POWER_DOMAIN_VBLANK) |                  \
>       BIT_ULL(POWER_DOMAIN_INIT))
>  
>  #define BXT_DISPLAY_POWERWELL_2_POWER_DOMAINS (              \
> @@ -1732,6 +1857,7 @@ void intel_display_power_put(struct drm_i915_private 
> *dev_priv,
>       BIT_ULL(POWER_DOMAIN_GT_IRQ) |                  \
>       BIT_ULL(POWER_DOMAIN_MODESET) |                 \
>       BIT_ULL(POWER_DOMAIN_AUX_A) |                   \
> +     BIT_ULL(POWER_DOMAIN_VBLANK) |                  \
>       BIT_ULL(POWER_DOMAIN_INIT))
>  #define BXT_DPIO_CMN_A_POWER_DOMAINS (                       \
>       BIT_ULL(POWER_DOMAIN_PORT_DDI_A_LANES) |                \
> @@ -1791,6 +1917,7 @@ void intel_display_power_put(struct drm_i915_private 
> *dev_priv,
>       BIT_ULL(POWER_DOMAIN_GT_IRQ) |                  \
>       BIT_ULL(POWER_DOMAIN_MODESET) |                 \
>       BIT_ULL(POWER_DOMAIN_AUX_A) |                   \
> +     BIT_ULL(POWER_DOMAIN_VBLANK) |                  \
>       BIT_ULL(POWER_DOMAIN_INIT))
>  
>  #define CNL_DISPLAY_POWERWELL_2_POWER_DOMAINS (              \
> @@ -1838,6 +1965,7 @@ void intel_display_power_put(struct drm_i915_private 
> *dev_priv,
>       CNL_DISPLAY_POWERWELL_2_POWER_DOMAINS |         \
>       BIT_ULL(POWER_DOMAIN_MODESET) |                 \
>       BIT_ULL(POWER_DOMAIN_AUX_A) |                   \
> +     BIT_ULL(POWER_DOMAIN_VBLANK) |                  \
>       BIT_ULL(POWER_DOMAIN_INIT))
>  
>  static const struct i915_power_well_ops i9xx_always_on_power_well_ops = {
> @@ -2071,9 +2199,12 @@ bool intel_display_power_well_is_enabled(struct 
> drm_i915_private *dev_priv,
>  {
>       struct i915_power_well *power_well;
>       bool ret;
> +     unsigned long flags = 0;
>  
>       power_well = lookup_power_well(dev_priv, power_well_id);
> +     power_well_lock(power_well, flags);
>       ret = power_well->ops->is_enabled(dev_priv, power_well);
> +     power_well_unlock(power_well, flags);
>  
>       return ret;
>  }
> @@ -2108,6 +2239,8 @@ static struct i915_power_well skl_power_wells[] = {
>               .domains = SKL_DISPLAY_DC_OFF_POWER_DOMAINS,
>               .ops = &gen9_dc_off_power_well_ops,
>               .id = SKL_DISP_PW_DC_OFF,
> +             .supports_atomic_ctx = true,
> +             .dc_off_disabled = false,
>       },
>       {
>               .name = "power well 2",
> @@ -2168,6 +2301,8 @@ static struct i915_power_well bxt_power_wells[] = {
>               .domains = BXT_DISPLAY_DC_OFF_POWER_DOMAINS,
>               .ops = &gen9_dc_off_power_well_ops,
>               .id = SKL_DISP_PW_DC_OFF,
> +             .supports_atomic_ctx = true,
> +             .dc_off_disabled = false,
>       },
>       {
>               .name = "power well 2",
> @@ -2223,6 +2358,8 @@ static struct i915_power_well glk_power_wells[] = {
>               .domains = GLK_DISPLAY_DC_OFF_POWER_DOMAINS,
>               .ops = &gen9_dc_off_power_well_ops,
>               .id = SKL_DISP_PW_DC_OFF,
> +             .supports_atomic_ctx = true,
> +             .dc_off_disabled = false,
>       },
>       {
>               .name = "power well 2",
> @@ -2347,6 +2484,8 @@ static struct i915_power_well cnl_power_wells[] = {
>               .domains = CNL_DISPLAY_DC_OFF_POWER_DOMAINS,
>               .ops = &gen9_dc_off_power_well_ops,
>               .id = SKL_DISP_PW_DC_OFF,
> +             .supports_atomic_ctx = true,
> +             .dc_off_disabled = false,
>       },
>       {
>               .name = "power well 2",
> @@ -2475,6 +2614,7 @@ static void assert_power_well_ids_unique(struct 
> drm_i915_private *dev_priv)
>  int intel_power_domains_init(struct drm_i915_private *dev_priv)
>  {
>       struct i915_power_domains *power_domains = &dev_priv->power_domains;
> +     struct i915_power_well *power_well;
>  
>       i915_modparams.disable_power_well =
>               sanitize_disable_power_well_option(dev_priv,
> @@ -2512,6 +2652,10 @@ int intel_power_domains_init(struct drm_i915_private 
> *dev_priv)
>               set_power_wells(power_domains, i9xx_always_on_power_well);
>       }
>  
> +     for_each_power_well(dev_priv, power_well)
> +             if (power_well->supports_atomic_ctx)
> +                     spin_lock_init(&power_well->lock);
> +
>       assert_power_well_ids_unique(dev_priv);
>  
>       return 0;
> @@ -2559,9 +2703,13 @@ static void intel_power_domains_sync_hw(struct 
> drm_i915_private *dev_priv)
>  
>       mutex_lock(&power_domains->lock);
>       for_each_power_well(dev_priv, power_well) {
> +             unsigned long flags = 0;
> +
> +             power_well_lock(power_well, flags);
>               power_well->ops->sync_hw(dev_priv, power_well);
>               power_well->hw_enabled = power_well->ops->is_enabled(dev_priv,
>                                                                    
> power_well);
> +             power_well_unlock(power_well, flags);
>       }
>       mutex_unlock(&power_domains->lock);
>  }
> @@ -3034,16 +3182,18 @@ void intel_power_domains_suspend(struct 
> drm_i915_private *dev_priv)
>               bxt_display_core_uninit(dev_priv);
>  }
>  
> -static void intel_power_domains_dump_info(struct drm_i915_private *dev_priv)
> +static void intel_power_domains_dump_info(struct drm_i915_private *dev_priv,
> +                                       const int *power_well_use)
>  {
>       struct i915_power_domains *power_domains = &dev_priv->power_domains;
>       struct i915_power_well *power_well;
> +     int i = 0;
>  
>       for_each_power_well(dev_priv, power_well) {
>               enum intel_display_power_domain domain;
>  
>               DRM_DEBUG_DRIVER("%-25s %d\n",
> -                              power_well->name, power_well->count);
> +                              power_well->name, power_well_use[i++]);
>  
>               for_each_power_domain(domain, power_well->domains)
>                       DRM_DEBUG_DRIVER("  %-23s %d\n",
> @@ -3067,6 +3217,7 @@ void intel_power_domains_verify_state(struct 
> drm_i915_private *dev_priv)
>       struct i915_power_domains *power_domains = &dev_priv->power_domains;
>       struct i915_power_well *power_well;
>       bool dump_domain_info;
> +     int power_well_use[dev_priv->power_domains.power_well_count];
>  
>       mutex_lock(&power_domains->lock);
>  
> @@ -3075,6 +3226,14 @@ void intel_power_domains_verify_state(struct 
> drm_i915_private *dev_priv)
>               enum intel_display_power_domain domain;
>               int domains_count;
>               bool enabled;
> +             int well_count, i = 0;
> +             unsigned long flags = 0;
> +
> +             power_well_lock(power_well, flags);
> +             well_count = power_well->count;
> +             enabled = power_well->ops->is_enabled(dev_priv, power_well);
> +             power_well_unlock(power_well, flags);
> +             power_well_use[i++] = well_count;
>  
>               /*
>                * Power wells not belonging to any domain (like the MISC_IO
> @@ -3084,20 +3243,19 @@ void intel_power_domains_verify_state(struct 
> drm_i915_private *dev_priv)
>               if (!power_well->domains)
>                       continue;
>  
> -             enabled = power_well->ops->is_enabled(dev_priv, power_well);
> -             if ((power_well->count || power_well->always_on) != enabled)
> +
> +             if ((well_count || power_well->always_on) != enabled)
>                       DRM_ERROR("power well %s state mismatch (refcount 
> %d/enabled %d)",
> -                               power_well->name, power_well->count, enabled);
> +                               power_well->name, well_count, enabled);
>  
>               domains_count = 0;
>               for_each_power_domain(domain, power_well->domains)
>                       domains_count += 
> atomic_read(&power_domains->domain_use_count[domain]);
>  
> -             if (power_well->count != domains_count) {
> +             if (well_count != domains_count) {
>                       DRM_ERROR("power well %s refcount/domain refcount 
> mismatch "
>                                 "(refcount %d/domains refcount %d)\n",
> -                               power_well->name, power_well->count,
> -                               domains_count);
> +                               power_well->name, well_count, domains_count);
>                       dump_domain_info = true;
>               }
>       }
> @@ -3106,7 +3264,7 @@ void intel_power_domains_verify_state(struct 
> drm_i915_private *dev_priv)
>               static bool dumped;
>  
>               if (!dumped) {
> -                     intel_power_domains_dump_info(dev_priv);
> +                     intel_power_domains_dump_info(dev_priv, power_well_use);
>                       dumped = true;
>               }
>       }
> -- 
> 2.11.0
> 
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

Reply via email to