[Intel-gfx] [PATCH v2 2/8] drm/i915: support for multiple power wells

Paulo Zanoni przanoni at gmail.com
Fri Nov 22 16:53:10 CET 2013


2013/11/14 Imre Deak <imre.deak at intel.com>:
> HW generations so far had only one always-on power well and optionally
> one dynamic power well. Upcoming HW gens may have multiple dynamic power
> wells, so add some infrastructure to support them.
>
> The idea is to keep the existing power domain API used by the rest of
> the driver and create a mapping between these power domains and the
> underlying power wells. This mapping can differ from one HW to another
> but high level driver code doesn't need to know about this. Through the
> existing get/put API it would just ask for a given power domain and the
> power domain framework would make sure the relevant power wells get
> enabled in the right order.
>
> Signed-off-by: Imre Deak <imre.deak at intel.com>
> ---
>  drivers/gpu/drm/i915/i915_drv.h |  12 +++-
>  drivers/gpu/drm/i915/intel_pm.c | 128 ++++++++++++++++++++++++++++++++--------
>  2 files changed, 114 insertions(+), 26 deletions(-)
>
> diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
> index 7007b9b..b20016c 100644
> --- a/drivers/gpu/drm/i915/i915_drv.h
> +++ b/drivers/gpu/drm/i915/i915_drv.h
> @@ -945,21 +945,27 @@ struct intel_ilk_power_mgmt {
>
>  /* Power well structure for haswell */
>  struct i915_power_well {
> +       const char *name;
>         /* power well enable/disable usage count */
>         int count;
> +       unsigned long domains;
> +       void *data;

This "data" field is completely unused.


> +       void (*set)(struct drm_device *dev, struct i915_power_well *power_well,
> +                   bool enable);
> +       bool (*is_enabled)(struct drm_device *dev,
> +                          struct i915_power_well *power_well);

The "power_well" argument of both functions is also unused.


>  };
>
> -#define I915_MAX_POWER_WELLS 1
> -
>  struct i915_power_domains {
>         /*
>          * Power wells needed for initialization at driver init and suspend
>          * time are on. They are kept on until after the first modeset.
>          */
>         bool init_power_on;
> +       int power_well_count;
>
>         struct mutex lock;
> -       struct i915_power_well power_wells[I915_MAX_POWER_WELLS];
> +       struct i915_power_well *power_wells;
>  };
>
>  struct i915_dri1_state {
> diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c
> index bcca995..2b39a9c 100644
> --- a/drivers/gpu/drm/i915/intel_pm.c
> +++ b/drivers/gpu/drm/i915/intel_pm.c
> @@ -5625,27 +5625,66 @@ static bool is_always_on_power_domain(struct drm_device *dev,
>         return BIT(domain) & always_on_domains;
>  }
>
> +#define for_each_power_well(i, power_well, domain_mask, power_domains) \
> +       for (i = 0;                                                     \
> +            i < (power_domains)->power_well_count &&                   \
> +                ((power_well) = &(power_domains)->power_wells[i]);     \
> +            i++)                                                       \
> +               if ((power_well)->domains & (domain_mask))
> +
> +#define for_each_power_well_rev(i, power_well, domain_mask, power_domains) \
> +       for (i = (power_domains)->power_well_count - 1;                  \
> +            i >= 0 && ((power_well) = &(power_domains)->power_wells[i]);\
> +            i--)                                                        \
> +               if ((power_well)->domains & (domain_mask))
> +
>  /**
>   * We should only use the power well if we explicitly asked the hardware to
>   * enable it, so check if it's enabled and also check if we've requested it to
>   * be enabled.
>   */
> -bool intel_display_power_enabled(struct drm_device *dev,
> -                                enum intel_display_power_domain domain)
> +static bool hsw_power_well_enabled(struct drm_device *dev,
> +                                  struct i915_power_well *power_well)
>  {
>         struct drm_i915_private *dev_priv = dev->dev_private;
>
> -       if (!HAS_POWER_WELL(dev))
> -               return true;
> -
> -       if (is_always_on_power_domain(dev, domain))
> -               return true;
> -
>         return I915_READ(HSW_PWR_WELL_DRIVER) ==
>                      (HSW_PWR_WELL_ENABLE_REQUEST | HSW_PWR_WELL_STATE_ENABLED);
>  }
>
> -static void __intel_set_power_well(struct drm_device *dev, bool enable)
> +bool intel_display_power_enabled(struct drm_device *dev,
> +                                enum intel_display_power_domain domain)
> +{
> +       struct drm_i915_private *dev_priv = dev->dev_private;
> +       struct i915_power_domains *power_domains;
> +       struct i915_power_well *power_well;
> +       bool is_enabled;
> +       int i;
> +
> +       if (!HAS_POWER_WELL(dev))
> +               return true;
> +
> +       if (is_always_on_power_domain(dev, domain))
> +               return true;
> +
> +       power_domains = &dev_priv->power_domains;
> +
> +       is_enabled = true;
> +
> +       mutex_lock(&power_domains->lock);
> +       for_each_power_well_rev(i, power_well, BIT(domain), power_domains) {
> +               if (!power_well->is_enabled(dev, power_well)) {
> +                       is_enabled = false;
> +                       break;
> +               }
> +       }
> +       mutex_unlock(&power_domains->lock);
> +
> +       return is_enabled;
> +}
> +
> +static void hsw_set_power_well(struct drm_device *dev,
> +                              struct i915_power_well *power_well, bool enable)
>  {
>         struct drm_i915_private *dev_priv = dev->dev_private;
>         bool is_enabled, enable_requested;
> @@ -5711,16 +5750,17 @@ static void __intel_set_power_well(struct drm_device *dev, bool enable)
>  static void __intel_power_well_get(struct drm_device *dev,
>                                    struct i915_power_well *power_well)
>  {
> -       if (!power_well->count++)
> -               __intel_set_power_well(dev, true);
> +       if (!power_well->count++ && power_well->set)
> +               power_well->set(dev, power_well, true);

The "set" function always exists for all power wells, so we shouldn't
check for it. It's better if we get a segfault that tells us we forgot
to implement the set() function than if we just silently not run
anything.


>  }
>
>  static void __intel_power_well_put(struct drm_device *dev,
>                                    struct i915_power_well *power_well)
>  {
>         WARN_ON(!power_well->count);
> -       if (!--power_well->count && i915_disable_power_well)
> -               __intel_set_power_well(dev, false);
> +
> +       if (!--power_well->count && power_well->set && i915_disable_power_well)

Same here.


> +               power_well->set(dev, power_well, false);
>  }
>
>  void intel_display_power_get(struct drm_device *dev,
> @@ -5728,6 +5768,8 @@ void intel_display_power_get(struct drm_device *dev,
>  {
>         struct drm_i915_private *dev_priv = dev->dev_private;
>         struct i915_power_domains *power_domains;
> +       struct i915_power_well *power_well;
> +       int i;
>
>         if (!HAS_POWER_WELL(dev))
>                 return;
> @@ -5738,7 +5780,8 @@ void intel_display_power_get(struct drm_device *dev,
>         power_domains = &dev_priv->power_domains;
>
>         mutex_lock(&power_domains->lock);
> -       __intel_power_well_get(dev, &power_domains->power_wells[0]);
> +       for_each_power_well(i, power_well, BIT(domain), power_domains)
> +               __intel_power_well_get(dev, power_well);
>         mutex_unlock(&power_domains->lock);
>  }
>
> @@ -5747,6 +5790,8 @@ void intel_display_power_put(struct drm_device *dev,
>  {
>         struct drm_i915_private *dev_priv = dev->dev_private;
>         struct i915_power_domains *power_domains;
> +       struct i915_power_well *power_well;
> +       int i;
>
>         if (!HAS_POWER_WELL(dev))
>                 return;
> @@ -5757,7 +5802,8 @@ void intel_display_power_put(struct drm_device *dev,
>         power_domains = &dev_priv->power_domains;
>
>         mutex_lock(&power_domains->lock);
> -       __intel_power_well_put(dev, &power_domains->power_wells[0]);
> +       for_each_power_well_rev(i, power_well, BIT(domain), power_domains)
> +               __intel_power_well_put(dev, power_well);
>         mutex_unlock(&power_domains->lock);
>  }
>
> @@ -5791,17 +5837,52 @@ void i915_release_power_well(void)
>  }
>  EXPORT_SYMBOL_GPL(i915_release_power_well);
>
> +static struct i915_power_well hsw_power_wells[] = {
> +       {
> +               .name = "display",
> +               .domains = POWER_DOMAIN_MASK & ~HSW_ALWAYS_ON_POWER_DOMAINS,
> +               .is_enabled = hsw_power_well_enabled,
> +               .set = hsw_set_power_well,
> +       },
> +};
> +
> +static struct i915_power_well bdw_power_wells[] = {
> +       {
> +               .name = "display",
> +               .domains = POWER_DOMAIN_MASK & ~BDW_ALWAYS_ON_POWER_DOMAINS,
> +               .is_enabled = hsw_power_well_enabled,
> +               .set = hsw_set_power_well,
> +       },
> +};
> +
> +#define set_power_wells(power_domains, __power_wells) ({               \
> +       (power_domains)->power_wells = (__power_wells);                 \
> +       (power_domains)->power_well_count = ARRAY_SIZE(__power_wells);  \
> +})

IMHO this macro is just over-complicating things. Please just do the
two assignments directly instead of calling the macro.


> +
>  int intel_power_domains_init(struct drm_device *dev)
>  {
>         struct drm_i915_private *dev_priv = dev->dev_private;
>         struct i915_power_domains *power_domains = &dev_priv->power_domains;
> -       struct i915_power_well *power_well;
> +
> +       if (!HAS_POWER_WELL(dev))
> +               return 0;
>
>         mutex_init(&power_domains->lock);
> -       hsw_pwr = power_domains;
>
> -       power_well = &power_domains->power_wells[0];
> -       power_well->count = 0;
> +       /*
> +        * The enabling order will be from lower to higher indexed wells,
> +        * the disabling order is reversed.
> +        */
> +       if (IS_HASWELL(dev)) {
> +               set_power_wells(power_domains, hsw_power_wells);
> +               hsw_pwr = power_domains;
> +       } else if (IS_BROADWELL(dev)) {
> +               set_power_wells(power_domains, bdw_power_wells);
> +               hsw_pwr = power_domains;

I wonder if there's a way to treat Haswell and Broadwell as exactly
the same thing, except for one single "if" statement somewhere.

> +       } else {
> +               WARN_ON(1);
> +       }
>
>         return 0;
>  }
> @@ -5816,15 +5897,16 @@ static void intel_power_domains_resume(struct drm_device *dev)
>         struct drm_i915_private *dev_priv = dev->dev_private;
>         struct i915_power_domains *power_domains = &dev_priv->power_domains;
>         struct i915_power_well *power_well;
> +       int i;
>
>         if (!HAS_POWER_WELL(dev))
>                 return;
>
>         mutex_lock(&power_domains->lock);
> -
> -       power_well = &power_domains->power_wells[0];
> -       __intel_set_power_well(dev, power_well->count > 0);
> -
> +       for_each_power_well(i, power_well, POWER_DOMAIN_MASK, power_domains) {
> +               if (power_well->set)

Same here: the set() function should always exist.

> +                       power_well->set(dev, power_well, power_well->count > 0);
> +       }
>         mutex_unlock(&power_domains->lock);
>  }
>
> --
> 1.8.4
>
> _______________________________________________
> Intel-gfx mailing list
> Intel-gfx at lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/intel-gfx



-- 
Paulo Zanoni



More information about the Intel-gfx mailing list