[Intel-gfx] [PATCH v2 2/8] drm/i915: support for multiple power wells
Imre Deak
imre.deak at intel.com
Fri Nov 22 19:36:22 CET 2013
On Fri, 2013-11-22 at 13:53 -0200, Paulo Zanoni wrote:
> 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.
These are for platforms that handle multiple power wells with a
single .set handler, determining the exact power well via the .data
member. This should've been in the log as it's not clear from the
current patchset.
> > };
> >
> > -#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.
These are for the default power wells w/o any need to poke at the HW as
you later point out.
> > }
> >
> > 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.
At the moment yes, but we'll have quite a few more power wells later
where we need to do the same thing. Again not obvious from this patchset
alone.
> > +
> > 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.
They are the same except a different .domains mask.
--Imre
> > + } 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
>
>
>
More information about the Intel-gfx
mailing list