Commit c1ca727f authored by Imre Deak's avatar Imre Deak Committed by Daniel Vetter

drm/i915: support for multiple power wells

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: default avatarImre Deak <imre.deak@intel.com>
Reviewed-by: default avatarPaulo Zanoni <paulo.zanoni@intel.com>
Signed-off-by: default avatarDaniel Vetter <daniel.vetter@ffwll.ch>
parent fbeeaa23
...@@ -950,21 +950,27 @@ struct intel_ilk_power_mgmt { ...@@ -950,21 +950,27 @@ struct intel_ilk_power_mgmt {
/* Power well structure for haswell */ /* Power well structure for haswell */
struct i915_power_well { struct i915_power_well {
const char *name;
/* power well enable/disable usage count */ /* power well enable/disable usage count */
int count; int count;
unsigned long domains;
void *data;
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);
}; };
#define I915_MAX_POWER_WELLS 1
struct i915_power_domains { struct i915_power_domains {
/* /*
* Power wells needed for initialization at driver init and suspend * Power wells needed for initialization at driver init and suspend
* time are on. They are kept on until after the first modeset. * time are on. They are kept on until after the first modeset.
*/ */
bool init_power_on; bool init_power_on;
int power_well_count;
struct mutex lock; struct mutex lock;
struct i915_power_well power_wells[I915_MAX_POWER_WELLS]; struct i915_power_well *power_wells;
}; };
struct i915_dri1_state { struct i915_dri1_state {
......
...@@ -5627,15 +5627,41 @@ static bool is_always_on_power_domain(struct drm_device *dev, ...@@ -5627,15 +5627,41 @@ static bool is_always_on_power_domain(struct drm_device *dev,
return BIT(domain) & always_on_domains; 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 * 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 * enable it, so check if it's enabled and also check if we've requested it to
* be enabled. * be enabled.
*/ */
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;
return I915_READ(HSW_PWR_WELL_DRIVER) ==
(HSW_PWR_WELL_ENABLE_REQUEST | HSW_PWR_WELL_STATE_ENABLED);
}
bool intel_display_power_enabled(struct drm_device *dev, bool intel_display_power_enabled(struct drm_device *dev,
enum intel_display_power_domain domain) enum intel_display_power_domain domain)
{ {
struct drm_i915_private *dev_priv = dev->dev_private; 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)) if (!HAS_POWER_WELL(dev))
return true; return true;
...@@ -5643,11 +5669,24 @@ bool intel_display_power_enabled(struct drm_device *dev, ...@@ -5643,11 +5669,24 @@ bool intel_display_power_enabled(struct drm_device *dev,
if (is_always_on_power_domain(dev, domain)) if (is_always_on_power_domain(dev, domain))
return true; return true;
return I915_READ(HSW_PWR_WELL_DRIVER) == power_domains = &dev_priv->power_domains;
(HSW_PWR_WELL_ENABLE_REQUEST | HSW_PWR_WELL_STATE_ENABLED);
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 __intel_set_power_well(struct drm_device *dev, bool enable) 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; struct drm_i915_private *dev_priv = dev->dev_private;
bool is_enabled, enable_requested; bool is_enabled, enable_requested;
...@@ -5713,16 +5752,17 @@ static void __intel_set_power_well(struct drm_device *dev, bool enable) ...@@ -5713,16 +5752,17 @@ static void __intel_set_power_well(struct drm_device *dev, bool enable)
static void __intel_power_well_get(struct drm_device *dev, static void __intel_power_well_get(struct drm_device *dev,
struct i915_power_well *power_well) struct i915_power_well *power_well)
{ {
if (!power_well->count++) if (!power_well->count++ && power_well->set)
__intel_set_power_well(dev, true); power_well->set(dev, power_well, true);
} }
static void __intel_power_well_put(struct drm_device *dev, static void __intel_power_well_put(struct drm_device *dev,
struct i915_power_well *power_well) struct i915_power_well *power_well)
{ {
WARN_ON(!power_well->count); 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)
power_well->set(dev, power_well, false);
} }
void intel_display_power_get(struct drm_device *dev, void intel_display_power_get(struct drm_device *dev,
...@@ -5730,6 +5770,8 @@ void intel_display_power_get(struct drm_device *dev, ...@@ -5730,6 +5770,8 @@ void intel_display_power_get(struct drm_device *dev,
{ {
struct drm_i915_private *dev_priv = dev->dev_private; struct drm_i915_private *dev_priv = dev->dev_private;
struct i915_power_domains *power_domains; struct i915_power_domains *power_domains;
struct i915_power_well *power_well;
int i;
if (!HAS_POWER_WELL(dev)) if (!HAS_POWER_WELL(dev))
return; return;
...@@ -5740,7 +5782,8 @@ void intel_display_power_get(struct drm_device *dev, ...@@ -5740,7 +5782,8 @@ void intel_display_power_get(struct drm_device *dev,
power_domains = &dev_priv->power_domains; power_domains = &dev_priv->power_domains;
mutex_lock(&power_domains->lock); 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); mutex_unlock(&power_domains->lock);
} }
...@@ -5749,6 +5792,8 @@ void intel_display_power_put(struct drm_device *dev, ...@@ -5749,6 +5792,8 @@ void intel_display_power_put(struct drm_device *dev,
{ {
struct drm_i915_private *dev_priv = dev->dev_private; struct drm_i915_private *dev_priv = dev->dev_private;
struct i915_power_domains *power_domains; struct i915_power_domains *power_domains;
struct i915_power_well *power_well;
int i;
if (!HAS_POWER_WELL(dev)) if (!HAS_POWER_WELL(dev))
return; return;
...@@ -5759,7 +5804,8 @@ void intel_display_power_put(struct drm_device *dev, ...@@ -5759,7 +5804,8 @@ void intel_display_power_put(struct drm_device *dev,
power_domains = &dev_priv->power_domains; power_domains = &dev_priv->power_domains;
mutex_lock(&power_domains->lock); 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); mutex_unlock(&power_domains->lock);
} }
...@@ -5793,17 +5839,52 @@ void i915_release_power_well(void) ...@@ -5793,17 +5839,52 @@ void i915_release_power_well(void)
} }
EXPORT_SYMBOL_GPL(i915_release_power_well); 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); \
})
int intel_power_domains_init(struct drm_device *dev) int intel_power_domains_init(struct drm_device *dev)
{ {
struct drm_i915_private *dev_priv = dev->dev_private; struct drm_i915_private *dev_priv = dev->dev_private;
struct i915_power_domains *power_domains = &dev_priv->power_domains; 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); 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;
} else {
WARN_ON(1);
}
return 0; return 0;
} }
...@@ -5818,15 +5899,16 @@ static void intel_power_domains_resume(struct drm_device *dev) ...@@ -5818,15 +5899,16 @@ static void intel_power_domains_resume(struct drm_device *dev)
struct drm_i915_private *dev_priv = dev->dev_private; struct drm_i915_private *dev_priv = dev->dev_private;
struct i915_power_domains *power_domains = &dev_priv->power_domains; struct i915_power_domains *power_domains = &dev_priv->power_domains;
struct i915_power_well *power_well; struct i915_power_well *power_well;
int i;
if (!HAS_POWER_WELL(dev)) if (!HAS_POWER_WELL(dev))
return; return;
mutex_lock(&power_domains->lock); mutex_lock(&power_domains->lock);
for_each_power_well(i, power_well, POWER_DOMAIN_MASK, power_domains) {
power_well = &power_domains->power_wells[0]; if (power_well->set)
__intel_set_power_well(dev, power_well->count > 0); power_well->set(dev, power_well, power_well->count > 0);
}
mutex_unlock(&power_domains->lock); mutex_unlock(&power_domains->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