From 62b02a4ede83d3c54a55e1c5ef0043ab13c34ad4 Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Thu, 2 May 2019 13:22:53 -0700 Subject: [PATCH] drm/v3d: Clock V3D down when not in use. My various attempts at re-enabling runtime PM have failed, so just crank the clock down when V3D is idle to reduce power consumption. Signed-off-by: Eric Anholt --- drivers/gpu/drm/v3d/v3d_drv.c | 18 +++++++++++++ drivers/gpu/drm/v3d/v3d_drv.h | 6 +++++ drivers/gpu/drm/v3d/v3d_gem.c | 50 +++++++++++++++++++++++++++++++++-- 3 files changed, 72 insertions(+), 2 deletions(-) --- a/drivers/gpu/drm/v3d/v3d_drv.c +++ b/drivers/gpu/drm/v3d/v3d_drv.c @@ -260,6 +260,21 @@ static int v3d_platform_drm_probe(struct } } + v3d->clk = devm_clk_get(dev, NULL); + if (IS_ERR(v3d->clk)) { + if (ret != -EPROBE_DEFER) + dev_err(dev, "Failed to get clock\n"); + goto dev_free; + } + v3d->clk_up_rate = clk_get_rate(v3d->clk); + /* For downclocking, drop it to the minimum frequency we can get from + * the CPRMAN clock generator dividing off our parent. The divider is + * 4 bits, but ask for just higher than that so that rounding doesn't + * make cprman reject our rate. + */ + v3d->clk_down_rate = + (clk_get_rate(clk_get_parent(v3d->clk)) / (1 << 4)) + 10000; + if (v3d->ver < 41) { ret = map_regs(v3d, &v3d->gca_regs, "gca"); if (ret) @@ -285,6 +300,9 @@ static int v3d_platform_drm_probe(struct if (ret) goto irq_disable; + ret = clk_set_rate(v3d->clk, v3d->clk_down_rate); + WARN_ON_ONCE(ret != 0); + return 0; irq_disable: --- a/drivers/gpu/drm/v3d/v3d_drv.h +++ b/drivers/gpu/drm/v3d/v3d_drv.h @@ -76,6 +76,12 @@ struct v3d_dev { void __iomem *bridge_regs; void __iomem *gca_regs; struct clk *clk; + struct delayed_work clk_down_work; + unsigned long clk_up_rate, clk_down_rate; + struct mutex clk_lock; + u32 clk_refcount; + bool clk_up; + struct reset_control *reset; /* Virtual and DMA addresses of the single shared page table. */ --- a/drivers/gpu/drm/v3d/v3d_gem.c +++ b/drivers/gpu/drm/v3d/v3d_gem.c @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include @@ -18,6 +19,47 @@ #include "v3d_trace.h" static void +v3d_clock_down_work(struct work_struct *work) +{ + struct v3d_dev *v3d = + container_of(work, struct v3d_dev, clk_down_work.work); + int ret; + + ret = clk_set_rate(v3d->clk, v3d->clk_down_rate); + v3d->clk_up = false; + WARN_ON_ONCE(ret != 0); +} + +static void +v3d_clock_up_get(struct v3d_dev *v3d) +{ + mutex_lock(&v3d->clk_lock); + if (v3d->clk_refcount++ == 0) { + cancel_delayed_work_sync(&v3d->clk_down_work); + if (!v3d->clk_up) { + int ret; + + ret = clk_set_rate(v3d->clk, v3d->clk_up_rate); + WARN_ON_ONCE(ret != 0); + v3d->clk_up = true; + } + } + mutex_unlock(&v3d->clk_lock); +} + +static void +v3d_clock_up_put(struct v3d_dev *v3d) +{ + mutex_lock(&v3d->clk_lock); + if (--v3d->clk_refcount == 0) { + schedule_delayed_work(&v3d->clk_down_work, + msecs_to_jiffies(100)); + } + mutex_unlock(&v3d->clk_lock); +} + + +static void v3d_init_core(struct v3d_dev *v3d, int core) { /* Set OVRTMUOUT, which means that the texture sampler uniform @@ -360,6 +402,7 @@ static void v3d_job_free(struct kref *ref) { struct v3d_job *job = container_of(ref, struct v3d_job, refcount); + struct v3d_dev *v3d = job->v3d; int i; for (i = 0; i < job->bo_count; i++) { @@ -368,8 +411,7 @@ v3d_job_free(struct kref *ref) } kvfree(job->bo); - dma_fence_put(job->irq_fence); - dma_fence_put(job->done_fence); + v3d_clock_up_put(v3d); if (job->perfmon) v3d_perfmon_put(job->perfmon); @@ -500,6 +542,7 @@ v3d_job_init(struct v3d_dev *v3d, struct goto fail_deps; } + v3d_clock_up_get(v3d); kref_init(&job->refcount); return 0; @@ -1080,6 +1123,9 @@ v3d_gem_init(struct drm_device *dev) mutex_init(&v3d->sched_lock); mutex_init(&v3d->cache_clean_lock); + mutex_init(&v3d->clk_lock); + INIT_DELAYED_WORK(&v3d->clk_down_work, v3d_clock_down_work); + /* Note: We don't allocate address 0. Various bits of HW * treat 0 as special, such as the occlusion query counters * where 0 means "disabled".