[RFC 3/8] drm/i915: dynamically allocate forcewake domains

Daniele Ceraolo Spurio
2019-06-06T21:52:45Z

In an upcoming patch we will introduce a display uncore with no forcewake
domains, so let's avoid wasting memory and be ready to allocate only what
we need.

Signed-off-by: Daniele Ceraolo Spurio <daniele.ceraolospurio@intel.com>
---
drivers/gpu/drm/i915/intel_uncore.c | 141 +++++++++++++++++-----------
drivers/gpu/drm/i915/intel_uncore.h | 13 +--
2 files changed, 92 insertions(+), 62 deletions(-)

diff --git a/drivers/gpu/drm/i915/intel_uncore.c b/drivers/gpu/drm/i915/intel_uncore.c
index ef7eed9237a0..30650e6e2f54 100644
--- a/drivers/gpu/drm/i915/intel_uncore.c
+++ b/drivers/gpu/drm/i915/intel_uncore.c
@@ -344,7 +344,7 @@ intel_uncore_fw_release_timer(struct hrtimer *timer)
{
struct intel_uncore_forcewake_domain *domain =
container_of(timer, struct intel_uncore_forcewake_domain, timer);
- struct intel_uncore *uncore = forcewake_domain_to_uncore(domain);
+ struct intel_uncore *uncore = domain->uncore;
unsigned long irqflags;

assert_rpm_device_not_suspended(uncore->rpm);
@@ -1283,23 +1283,24 @@ do { \
(uncore)->funcs.read_fw_domains = x##_reg_read_fw_domains; \
} while (0)

-static void fw_domain_init(struct intel_uncore *uncore,
+static int fw_domain_init(struct intel_uncore *uncore,
enum forcewake_domain_id domain_id,
i915_reg_t reg_set,
i915_reg_t reg_ack)
{
struct intel_uncore_forcewake_domain *d;

- if (WARN_ON(domain_id >= FW_DOMAIN_ID_COUNT))
- return;
-
- d = &uncore->fw_domain[domain_id];
+ GEM_BUG_ON(domain_id >= FW_DOMAIN_ID_COUNT);
+ GEM_BUG_ON(uncore->fw_domain[domain_id]);

- WARN_ON(d->wake_count);
+ d = kzalloc(sizeof(*d), GFP_KERNEL);
+ if (!d)
+ return -ENOMEM;

WARN_ON(!i915_mmio_reg_valid(reg_set));
WARN_ON(!i915_mmio_reg_valid(reg_ack));

+ d->uncore = uncore;
d->wake_count = 0;
d->reg_set = uncore->regs + i915_mmio_reg_offset(reg_set);
d->reg_ack = uncore->regs + i915_mmio_reg_offset(reg_ack);
@@ -1325,6 +1326,10 @@ static void fw_domain_init(struct intel_uncore *uncore,
uncore->fw_domains |= BIT(domain_id);

fw_domain_reset(d);
+
+ uncore->fw_domain[domain_id] = d;
+
+ return 0;
}

static void fw_domain_fini(struct intel_uncore *uncore,
@@ -1332,78 +1337,93 @@ static void fw_domain_fini(struct intel_uncore *uncore,
{
struct intel_uncore_forcewake_domain *d;

- if (WARN_ON(domain_id >= FW_DOMAIN_ID_COUNT))
- return;
+ GEM_BUG_ON(domain_id >= FW_DOMAIN_ID_COUNT);

- d = &uncore->fw_domain[domain_id];
+ d = fetch_and_zero(&uncore->fw_domain[domain_id]);
+ uncore->fw_domains &= ~BIT(domain_id);

- WARN_ON(d->wake_count);
- WARN_ON(hrtimer_cancel(&d->timer));
- memset(d, 0, sizeof(*d));
+ if (d) {
+ WARN_ON(d->wake_count);
+ WARN_ON(hrtimer_cancel(&d->timer));
+ kfree(d);
+ }
+}

- uncore->fw_domains &= ~BIT(domain_id);
+static void intel_uncore_fw_domains_fini(struct intel_uncore *uncore)
+{
+ struct intel_uncore_forcewake_domain *d;
+ int tmp;
+
+ for_each_fw_domain(d, uncore, tmp)
+ fw_domain_fini(uncore, d->id);
}

-static void intel_uncore_fw_domains_init(struct intel_uncore *uncore)
+static int intel_uncore_fw_domains_init(struct intel_uncore *uncore)
{
struct drm_i915_private *i915 = uncore_to_i915(uncore);
+ int ret;
+
+#define __fw_domain_init(id, set, ack) \
+ ret = fw_domain_init(uncore, id, set, ack); \
+ if (ret) \
+ goto out_clean;

if (!intel_uncore_has_forcewake(uncore))
- return;
+ return 0;

if (INTEL_GEN(i915) >= 11) {
int i;

- uncore->funcs.force_wake_get =
- fw_domains_get_with_fallback;
+ uncore->funcs.force_wake_get = fw_domains_get_with_fallback;
uncore->funcs.force_wake_put = fw_domains_put;
- fw_domain_init(uncore, FW_DOMAIN_ID_RENDER,
- FORCEWAKE_RENDER_GEN9,
- FORCEWAKE_ACK_RENDER_GEN9);
- fw_domain_init(uncore, FW_DOMAIN_ID_BLITTER,
- FORCEWAKE_BLITTER_GEN9,
- FORCEWAKE_ACK_BLITTER_GEN9);
+ __fw_domain_init(FW_DOMAIN_ID_RENDER,
+ FORCEWAKE_RENDER_GEN9,
+ FORCEWAKE_ACK_RENDER_GEN9);
+ __fw_domain_init(FW_DOMAIN_ID_BLITTER,
+ FORCEWAKE_BLITTER_GEN9,
+ FORCEWAKE_ACK_BLITTER_GEN9);
+
for (i = 0; i < I915_MAX_VCS; i++) {
if (!HAS_ENGINE(i915, _VCS(i)))
continue;

- fw_domain_init(uncore, FW_DOMAIN_ID_MEDIA_VDBOX0 + i,
- FORCEWAKE_MEDIA_VDBOX_GEN11(i),
- FORCEWAKE_ACK_MEDIA_VDBOX_GEN11(i));
+ __fw_domain_init(FW_DOMAIN_ID_MEDIA_VDBOX0 + i,
+ FORCEWAKE_MEDIA_VDBOX_GEN11(i),
+ FORCEWAKE_ACK_MEDIA_VDBOX_GEN11(i));
}
for (i = 0; i < I915_MAX_VECS; i++) {
if (!HAS_ENGINE(i915, _VECS(i)))
continue;

- fw_domain_init(uncore, FW_DOMAIN_ID_MEDIA_VEBOX0 + i,
- FORCEWAKE_MEDIA_VEBOX_GEN11(i),
- FORCEWAKE_ACK_MEDIA_VEBOX_GEN11(i));
+ __fw_domain_init(FW_DOMAIN_ID_MEDIA_VEBOX0 + i,
+ FORCEWAKE_MEDIA_VEBOX_GEN11(i),
+ FORCEWAKE_ACK_MEDIA_VEBOX_GEN11(i));
}
} else if (IS_GEN_RANGE(i915, 9, 10)) {
- uncore->funcs.force_wake_get =
- fw_domains_get_with_fallback;
+ uncore->funcs.force_wake_get = fw_domains_get_with_fallback;
uncore->funcs.force_wake_put = fw_domains_put;
- fw_domain_init(uncore, FW_DOMAIN_ID_RENDER,
- FORCEWAKE_RENDER_GEN9,
- FORCEWAKE_ACK_RENDER_GEN9);
- fw_domain_init(uncore, FW_DOMAIN_ID_BLITTER,
- FORCEWAKE_BLITTER_GEN9,
- FORCEWAKE_ACK_BLITTER_GEN9);
- fw_domain_init(uncore, FW_DOMAIN_ID_MEDIA,
- FORCEWAKE_MEDIA_GEN9, FORCEWAKE_ACK_MEDIA_GEN9);
+ __fw_domain_init(FW_DOMAIN_ID_RENDER,
+ FORCEWAKE_RENDER_GEN9,
+ FORCEWAKE_ACK_RENDER_GEN9);
+ __fw_domain_init(FW_DOMAIN_ID_BLITTER,
+ FORCEWAKE_BLITTER_GEN9,
+ FORCEWAKE_ACK_BLITTER_GEN9);
+ __fw_domain_init(FW_DOMAIN_ID_MEDIA,
+ FORCEWAKE_MEDIA_GEN9,
+ FORCEWAKE_ACK_MEDIA_GEN9);
} else if (IS_VALLEYVIEW(i915) || IS_CHERRYVIEW(i915)) {
uncore->funcs.force_wake_get = fw_domains_get;
uncore->funcs.force_wake_put = fw_domains_put;
- fw_domain_init(uncore, FW_DOMAIN_ID_RENDER,
- FORCEWAKE_VLV, FORCEWAKE_ACK_VLV);
- fw_domain_init(uncore, FW_DOMAIN_ID_MEDIA,
- FORCEWAKE_MEDIA_VLV, FORCEWAKE_ACK_MEDIA_VLV);
+ __fw_domain_init(FW_DOMAIN_ID_RENDER,
+ FORCEWAKE_VLV, FORCEWAKE_ACK_VLV);
+ __fw_domain_init(FW_DOMAIN_ID_MEDIA,
+ FORCEWAKE_MEDIA_VLV, FORCEWAKE_ACK_MEDIA_VLV);
} else if (IS_HASWELL(i915) || IS_BROADWELL(i915)) {
uncore->funcs.force_wake_get =
fw_domains_get_with_thread_status;
uncore->funcs.force_wake_put = fw_domains_put;
- fw_domain_init(uncore, FW_DOMAIN_ID_RENDER,
- FORCEWAKE_MT, FORCEWAKE_ACK_HSW);
+ __fw_domain_init(FW_DOMAIN_ID_RENDER,
+ FORCEWAKE_MT, FORCEWAKE_ACK_HSW);
} else if (IS_IVYBRIDGE(i915)) {
u32 ecobus;

@@ -1430,8 +1450,8 @@ static void intel_uncore_fw_domains_init(struct intel_uncore *uncore)
__raw_uncore_write32(uncore, FORCEWAKE, 0);
__raw_posting_read(uncore, ECOBUS);

- fw_domain_init(uncore, FW_DOMAIN_ID_RENDER,
- FORCEWAKE_MT, FORCEWAKE_MT_ACK);
+ __fw_domain_init(FW_DOMAIN_ID_RENDER,
+ FORCEWAKE_MT, FORCEWAKE_MT_ACK);

spin_lock_irq(&uncore->lock);
fw_domains_get_with_thread_status(uncore, FORCEWAKE_RENDER);
@@ -1442,19 +1462,28 @@ static void intel_uncore_fw_domains_init(struct intel_uncore *uncore)
if (!(ecobus & FORCEWAKE_MT_ENABLE)) {
DRM_INFO("No MT forcewake available on Ivybridge, this can result in issues\n");
DRM_INFO("when using vblank-synced partial screen updates.\n");
- fw_domain_init(uncore, FW_DOMAIN_ID_RENDER,
- FORCEWAKE, FORCEWAKE_ACK);
+ __fw_domain_init(FW_DOMAIN_ID_RENDER,
+ FORCEWAKE, FORCEWAKE_ACK);
}
} else if (IS_GEN(i915, 6)) {
uncore->funcs.force_wake_get =
fw_domains_get_with_thread_status;
uncore->funcs.force_wake_put = fw_domains_put;
- fw_domain_init(uncore, FW_DOMAIN_ID_RENDER,
- FORCEWAKE, FORCEWAKE_ACK);
+ __fw_domain_init(FW_DOMAIN_ID_RENDER,
+ FORCEWAKE, FORCEWAKE_ACK);
}

+#undef __fw_domain_init
+
/* All future platforms are expected to require complex power gating */
WARN_ON(uncore->fw_domains == 0);
+
+ return 0;
+
+out_clean:
+ intel_uncore_fw_domains_fini(uncore);
+ return ret;
+
}

#define ASSIGN_FW_DOMAINS_TABLE(uncore, d) \
@@ -1554,7 +1583,12 @@ int intel_uncore_init_mmio(struct intel_uncore *uncore)
if (INTEL_GEN(i915) > 5 && !intel_vgpu_active(i915))
uncore->flags |= UNCORE_HAS_FORCEWAKE;

- intel_uncore_fw_domains_init(uncore);
+ ret = intel_uncore_fw_domains_init(uncore);
+ if (ret) {
+ uncore_mmio_cleanup(uncore);
+ return ret;
+ }
+
__intel_uncore_early_sanitize(uncore, 0);

uncore->unclaimed_mmio_check = 1;
@@ -1662,6 +1696,7 @@ void intel_uncore_fini_mmio(struct intel_uncore *uncore)
iosf_mbi_unregister_pmic_bus_access_notifier_unlocked(
&uncore->pmic_bus_access_nb);
intel_uncore_forcewake_reset(uncore);
+ intel_uncore_fw_domains_fini(uncore);
iosf_mbi_punit_release();
uncore_mmio_cleanup(uncore);
}
diff --git a/drivers/gpu/drm/i915/intel_uncore.h b/drivers/gpu/drm/i915/intel_uncore.h
index bf06b6b16892..2bb80962e7c5 100644
--- a/drivers/gpu/drm/i915/intel_uncore.h
+++ b/drivers/gpu/drm/i915/intel_uncore.h
@@ -125,6 +125,7 @@ struct intel_uncore {
enum forcewake_domains fw_domains_saved; /* user domains saved for S3 */

struct intel_uncore_forcewake_domain {
+ struct intel_uncore *uncore;
enum forcewake_domain_id id;
enum forcewake_domains mask;
unsigned int wake_count;
@@ -132,7 +133,7 @@ struct intel_uncore {
struct hrtimer timer;
u32 __iomem *reg_set;
u32 __iomem *reg_ack;
- } fw_domain[FW_DOMAIN_ID_COUNT];
+ } *fw_domain[FW_DOMAIN_ID_COUNT];

struct {
unsigned int count;
@@ -146,18 +147,12 @@ struct intel_uncore {

/* Iterate over initialised fw domains */
#define for_each_fw_domain_masked(domain__, mask__, uncore__, tmp__) \
- for (tmp__ = (mask__); \
- tmp__ ? (domain__ = &(uncore__)->fw_domain[__mask_next_bit(tmp__)]), 1 : 0;)
+ for (tmp__ = (mask__); tmp__ ;) \
+ for_each_if(domain__ = (uncore__)->fw_domain[__mask_next_bit(tmp__)])

#define for_each_fw_domain(domain__, uncore__, tmp__) \
for_each_fw_domain_masked(domain__, (uncore__)->fw_domains, uncore__, tmp__)

-static inline struct intel_uncore *
-forcewake_domain_to_uncore(const struct intel_uncore_forcewake_domain *d)
-{
- return container_of(d, struct intel_uncore, fw_domain[d->id]);
-}
-
static inline bool
intel_uncore_has_forcewake(const struct intel_uncore *uncore)
{