From 89cdfc8912ff1c9230668d2e81187a8279d0be46 Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Thu, 2 May 2019 13:22:53 -0700 Subject: [PATCH 172/634] 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 | 53 +++++++++++++++++++++++++++++++---- 3 files changed, 72 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/v3d/v3d_drv.c b/drivers/gpu/drm/v3d/v3d_drv.c index 3969b64127e0..e202870873a4 100644 --- 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 platform_device *pdev) } } + 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) @@ -323,6 +338,9 @@ static int v3d_platform_drm_probe(struct platform_device *pdev) if (ret) goto irq_disable; + ret = clk_set_rate(v3d->clk, v3d->clk_down_rate); + WARN_ON_ONCE(ret != 0); + return 0; irq_disable: diff --git a/drivers/gpu/drm/v3d/v3d_drv.h b/drivers/gpu/drm/v3d/v3d_drv.h index 270134779073..37b353a81ed0 100644 --- a/drivers/gpu/drm/v3d/v3d_drv.h +++ b/drivers/gpu/drm/v3d/v3d_drv.h @@ -85,6 +85,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. */ diff --git a/drivers/gpu/drm/v3d/v3d_gem.c b/drivers/gpu/drm/v3d/v3d_gem.c index 772b5831bcc6..d369221642c5 100644 --- 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_regs.h" #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) { @@ -358,6 +400,7 @@ v3d_job_free(struct kref *ref) struct v3d_job *job = container_of(ref, struct v3d_job, refcount); unsigned long index; struct dma_fence *fence; + struct v3d_dev *v3d = job->v3d; int i; for (i = 0; i < job->bo_count; i++) { @@ -371,11 +414,7 @@ v3d_job_free(struct kref *ref) } xa_destroy(&job->deps); - dma_fence_put(job->irq_fence); - dma_fence_put(job->done_fence); - - pm_runtime_mark_last_busy(job->v3d->drm.dev); - pm_runtime_put_autosuspend(job->v3d->drm.dev); + v3d_clock_up_put(v3d); if (job->perfmon) v3d_perfmon_put(job->perfmon); @@ -460,6 +499,7 @@ v3d_job_init(struct v3d_dev *v3d, struct drm_file *file_priv, if (ret) goto fail; + v3d_clock_up_get(v3d); kref_init(&job->refcount); return 0; @@ -912,6 +952,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". -- 2.33.1