aboutsummaryrefslogtreecommitdiffstats
path: root/target/linux/bcm27xx/patches-5.15/950-0161-drm-v3d-Clock-V3D-down-when-not-in-use.patch
blob: c9d039097256c371656cf7909809e79db116a578 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
From e796ee16390e63ed5d8713d301d609b32843342d Mon Sep 17 00:00:00 2001
From: Eric Anholt <eric@anholt.net>
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 <eric@anholt.net>
---
 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(-)

--- a/drivers/gpu/drm/v3d/v3d_drv.c
+++ b/drivers/gpu/drm/v3d/v3d_drv.c
@@ -301,6 +301,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)
@@ -327,6 +342,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
@@ -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. */
--- a/drivers/gpu/drm/v3d/v3d_gem.c
+++ b/drivers/gpu/drm/v3d/v3d_gem.c
@@ -4,6 +4,7 @@
 #include <linux/device.h>
 #include <linux/dma-mapping.h>
 #include <linux/io.h>
+#include <linux/clk.h>
 #include <linux/module.h>
 #include <linux/platform_device.h>
 #include <linux/pm_runtime.h>
@@ -19,6 +20,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
@@ -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
 	if (ret)
 		goto fail;
 
+	v3d_clock_up_get(v3d);
 	kref_init(&job->refcount);
 
 	return 0;
@@ -914,6 +954,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".