From 3e6b687bae81bdf5a430ffaa04aa04ee195a866c 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 | 49 +++++++++++++++++++++++++++++++++++ 3 files changed, 73 insertions(+) --- a/drivers/gpu/drm/v3d/v3d_drv.c +++ b/drivers/gpu/drm/v3d/v3d_drv.c @@ -297,6 +297,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) @@ -331,6 +346,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 @@ -45,6 +45,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 @@ -3,6 +3,7 @@ #include #include +#include #include #include #include @@ -17,6 +18,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 @@ -490,6 +532,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++) { @@ -505,6 +548,8 @@ v3d_job_free(struct kref *ref) dma_fence_put(job->irq_fence); dma_fence_put(job->done_fence); + v3d_clock_up_put(v3d); + kfree(job); } @@ -596,6 +641,7 @@ v3d_job_init(struct v3d_dev *v3d, struct if (ret) return ret; + v3d_clock_up_get(v3d); kref_init(&job->refcount); return 0; @@ -963,6 +1009,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".