aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRob Clark <robdclark@gmail.com>2014-01-11 16:25:08 -0500
committerRob Clark <robdclark@gmail.com>2014-03-31 10:27:46 -0400
commit37d77c3ab589bf8e3160a33b651fe6dd7439ba48 (patch)
tree12410f409382ba0557078e777f600e2377c08053
parent0963756fe51313a1e2d76885cd21624d3b2cfbf2 (diff)
drm/msm: crank down gpu when inactive
Shut down the clks when the gpu has nothing to do. A short inactivity timer is used to provide a low pass filter for power transitions. Signed-off-by: Rob Clark <robdclark@gmail.com>
-rw-r--r--drivers/gpu/drm/msm/adreno/a3xx_gpu.c10
-rw-r--r--drivers/gpu/drm/msm/msm_drv.c7
-rw-r--r--drivers/gpu/drm/msm/msm_gpu.c85
-rw-r--r--drivers/gpu/drm/msm/msm_gpu.h16
4 files changed, 113 insertions, 5 deletions
diff --git a/drivers/gpu/drm/msm/adreno/a3xx_gpu.c b/drivers/gpu/drm/msm/adreno/a3xx_gpu.c
index 59ed7620fc1a..e6cb2bc40ebe 100644
--- a/drivers/gpu/drm/msm/adreno/a3xx_gpu.c
+++ b/drivers/gpu/drm/msm/adreno/a3xx_gpu.c
@@ -395,9 +395,15 @@ static const unsigned int a3xx_registers[] = {
395#ifdef CONFIG_DEBUG_FS 395#ifdef CONFIG_DEBUG_FS
396static void a3xx_show(struct msm_gpu *gpu, struct seq_file *m) 396static void a3xx_show(struct msm_gpu *gpu, struct seq_file *m)
397{ 397{
398 struct drm_device *dev = gpu->dev;
398 int i; 399 int i;
399 400
400 adreno_show(gpu, m); 401 adreno_show(gpu, m);
402
403 mutex_lock(&dev->struct_mutex);
404
405 gpu->funcs->pm_resume(gpu);
406
401 seq_printf(m, "status: %08x\n", 407 seq_printf(m, "status: %08x\n",
402 gpu_read(gpu, REG_A3XX_RBBM_STATUS)); 408 gpu_read(gpu, REG_A3XX_RBBM_STATUS));
403 409
@@ -413,6 +419,10 @@ static void a3xx_show(struct msm_gpu *gpu, struct seq_file *m)
413 seq_printf(m, "IO:R %08x %08x\n", addr<<2, val); 419 seq_printf(m, "IO:R %08x %08x\n", addr<<2, val);
414 } 420 }
415 } 421 }
422
423 gpu->funcs->pm_suspend(gpu);
424
425 mutex_unlock(&dev->struct_mutex);
416} 426}
417#endif 427#endif
418 428
diff --git a/drivers/gpu/drm/msm/msm_drv.c b/drivers/gpu/drm/msm/msm_drv.c
index e6adafc7eff3..e913efa4ea0a 100644
--- a/drivers/gpu/drm/msm/msm_drv.c
+++ b/drivers/gpu/drm/msm/msm_drv.c
@@ -311,7 +311,6 @@ static void load_gpu(struct drm_device *dev)
311 gpu = NULL; 311 gpu = NULL;
312 /* not fatal */ 312 /* not fatal */
313 } 313 }
314 mutex_unlock(&dev->struct_mutex);
315 314
316 if (gpu) { 315 if (gpu) {
317 int ret; 316 int ret;
@@ -321,10 +320,16 @@ static void load_gpu(struct drm_device *dev)
321 dev_err(dev->dev, "gpu hw init failed: %d\n", ret); 320 dev_err(dev->dev, "gpu hw init failed: %d\n", ret);
322 gpu->funcs->destroy(gpu); 321 gpu->funcs->destroy(gpu);
323 gpu = NULL; 322 gpu = NULL;
323 } else {
324 /* give inactive pm a chance to kick in: */
325 msm_gpu_retire(gpu);
324 } 326 }
327
325 } 328 }
326 329
327 priv->gpu = gpu; 330 priv->gpu = gpu;
331
332 mutex_unlock(&dev->struct_mutex);
328} 333}
329 334
330static int msm_open(struct drm_device *dev, struct drm_file *file) 335static int msm_open(struct drm_device *dev, struct drm_file *file)
diff --git a/drivers/gpu/drm/msm/msm_gpu.c b/drivers/gpu/drm/msm/msm_gpu.c
index 0cfe3f426ee4..3e667ca1f2b9 100644
--- a/drivers/gpu/drm/msm/msm_gpu.c
+++ b/drivers/gpu/drm/msm/msm_gpu.c
@@ -154,9 +154,18 @@ static int disable_axi(struct msm_gpu *gpu)
154 154
155int msm_gpu_pm_resume(struct msm_gpu *gpu) 155int msm_gpu_pm_resume(struct msm_gpu *gpu)
156{ 156{
157 struct drm_device *dev = gpu->dev;
157 int ret; 158 int ret;
158 159
159 DBG("%s", gpu->name); 160 DBG("%s: active_cnt=%d", gpu->name, gpu->active_cnt);
161
162 WARN_ON(!mutex_is_locked(&dev->struct_mutex));
163
164 if (gpu->active_cnt++ > 0)
165 return 0;
166
167 if (WARN_ON(gpu->active_cnt <= 0))
168 return -EINVAL;
160 169
161 ret = enable_pwrrail(gpu); 170 ret = enable_pwrrail(gpu);
162 if (ret) 171 if (ret)
@@ -175,9 +184,18 @@ int msm_gpu_pm_resume(struct msm_gpu *gpu)
175 184
176int msm_gpu_pm_suspend(struct msm_gpu *gpu) 185int msm_gpu_pm_suspend(struct msm_gpu *gpu)
177{ 186{
187 struct drm_device *dev = gpu->dev;
178 int ret; 188 int ret;
179 189
180 DBG("%s", gpu->name); 190 DBG("%s: active_cnt=%d", gpu->name, gpu->active_cnt);
191
192 WARN_ON(!mutex_is_locked(&dev->struct_mutex));
193
194 if (--gpu->active_cnt > 0)
195 return 0;
196
197 if (WARN_ON(gpu->active_cnt < 0))
198 return -EINVAL;
181 199
182 ret = disable_axi(gpu); 200 ret = disable_axi(gpu);
183 if (ret) 201 if (ret)
@@ -195,6 +213,55 @@ int msm_gpu_pm_suspend(struct msm_gpu *gpu)
195} 213}
196 214
197/* 215/*
216 * Inactivity detection (for suspend):
217 */
218
219static void inactive_worker(struct work_struct *work)
220{
221 struct msm_gpu *gpu = container_of(work, struct msm_gpu, inactive_work);
222 struct drm_device *dev = gpu->dev;
223
224 if (gpu->inactive)
225 return;
226
227 DBG("%s: inactive!\n", gpu->name);
228 mutex_lock(&dev->struct_mutex);
229 if (!(msm_gpu_active(gpu) || gpu->inactive)) {
230 disable_axi(gpu);
231 disable_clk(gpu);
232 gpu->inactive = true;
233 }
234 mutex_unlock(&dev->struct_mutex);
235}
236
237static void inactive_handler(unsigned long data)
238{
239 struct msm_gpu *gpu = (struct msm_gpu *)data;
240 struct msm_drm_private *priv = gpu->dev->dev_private;
241
242 queue_work(priv->wq, &gpu->inactive_work);
243}
244
245/* cancel inactive timer and make sure we are awake: */
246static void inactive_cancel(struct msm_gpu *gpu)
247{
248 DBG("%s", gpu->name);
249 del_timer(&gpu->inactive_timer);
250 if (gpu->inactive) {
251 enable_clk(gpu);
252 enable_axi(gpu);
253 gpu->inactive = false;
254 }
255}
256
257static void inactive_start(struct msm_gpu *gpu)
258{
259 DBG("%s", gpu->name);
260 mod_timer(&gpu->inactive_timer,
261 round_jiffies_up(jiffies + DRM_MSM_INACTIVE_JIFFIES));
262}
263
264/*
198 * Hangcheck detection for locked gpu: 265 * Hangcheck detection for locked gpu:
199 */ 266 */
200 267
@@ -206,7 +273,10 @@ static void recover_worker(struct work_struct *work)
206 dev_err(dev->dev, "%s: hangcheck recover!\n", gpu->name); 273 dev_err(dev->dev, "%s: hangcheck recover!\n", gpu->name);
207 274
208 mutex_lock(&dev->struct_mutex); 275 mutex_lock(&dev->struct_mutex);
209 gpu->funcs->recover(gpu); 276 if (msm_gpu_active(gpu)) {
277 inactive_cancel(gpu);
278 gpu->funcs->recover(gpu);
279 }
210 mutex_unlock(&dev->struct_mutex); 280 mutex_unlock(&dev->struct_mutex);
211 281
212 msm_gpu_retire(gpu); 282 msm_gpu_retire(gpu);
@@ -281,6 +351,9 @@ static void retire_worker(struct work_struct *work)
281 } 351 }
282 352
283 mutex_unlock(&dev->struct_mutex); 353 mutex_unlock(&dev->struct_mutex);
354
355 if (!msm_gpu_active(gpu))
356 inactive_start(gpu);
284} 357}
285 358
286/* call from irq handler to schedule work to retire bo's */ 359/* call from irq handler to schedule work to retire bo's */
@@ -302,6 +375,8 @@ int msm_gpu_submit(struct msm_gpu *gpu, struct msm_gem_submit *submit,
302 375
303 gpu->submitted_fence = submit->fence; 376 gpu->submitted_fence = submit->fence;
304 377
378 inactive_cancel(gpu);
379
305 ret = gpu->funcs->submit(gpu, submit, ctx); 380 ret = gpu->funcs->submit(gpu, submit, ctx);
306 priv->lastctx = ctx; 381 priv->lastctx = ctx;
307 382
@@ -357,11 +432,15 @@ int msm_gpu_init(struct drm_device *drm, struct platform_device *pdev,
357 gpu->dev = drm; 432 gpu->dev = drm;
358 gpu->funcs = funcs; 433 gpu->funcs = funcs;
359 gpu->name = name; 434 gpu->name = name;
435 gpu->inactive = true;
360 436
361 INIT_LIST_HEAD(&gpu->active_list); 437 INIT_LIST_HEAD(&gpu->active_list);
362 INIT_WORK(&gpu->retire_work, retire_worker); 438 INIT_WORK(&gpu->retire_work, retire_worker);
439 INIT_WORK(&gpu->inactive_work, inactive_worker);
363 INIT_WORK(&gpu->recover_work, recover_worker); 440 INIT_WORK(&gpu->recover_work, recover_worker);
364 441
442 setup_timer(&gpu->inactive_timer, inactive_handler,
443 (unsigned long)gpu);
365 setup_timer(&gpu->hangcheck_timer, hangcheck_handler, 444 setup_timer(&gpu->hangcheck_timer, hangcheck_handler,
366 (unsigned long)gpu); 445 (unsigned long)gpu);
367 446
diff --git a/drivers/gpu/drm/msm/msm_gpu.h b/drivers/gpu/drm/msm/msm_gpu.h
index 458db8c64c28..fad27008922f 100644
--- a/drivers/gpu/drm/msm/msm_gpu.h
+++ b/drivers/gpu/drm/msm/msm_gpu.h
@@ -72,6 +72,10 @@ struct msm_gpu {
72 72
73 uint32_t submitted_fence; 73 uint32_t submitted_fence;
74 74
75 /* is gpu powered/active? */
76 int active_cnt;
77 bool inactive;
78
75 /* worker for handling active-list retiring: */ 79 /* worker for handling active-list retiring: */
76 struct work_struct retire_work; 80 struct work_struct retire_work;
77 81
@@ -91,7 +95,12 @@ struct msm_gpu {
91 uint32_t bsc; 95 uint32_t bsc;
92#endif 96#endif
93 97
94 /* Hang Detction: */ 98 /* Hang and Inactivity Detection:
99 */
100#define DRM_MSM_INACTIVE_PERIOD 66 /* in ms (roughly four frames) */
101#define DRM_MSM_INACTIVE_JIFFIES msecs_to_jiffies(DRM_MSM_INACTIVE_PERIOD)
102 struct timer_list inactive_timer;
103 struct work_struct inactive_work;
95#define DRM_MSM_HANGCHECK_PERIOD 500 /* in ms */ 104#define DRM_MSM_HANGCHECK_PERIOD 500 /* in ms */
96#define DRM_MSM_HANGCHECK_JIFFIES msecs_to_jiffies(DRM_MSM_HANGCHECK_PERIOD) 105#define DRM_MSM_HANGCHECK_JIFFIES msecs_to_jiffies(DRM_MSM_HANGCHECK_PERIOD)
97 struct timer_list hangcheck_timer; 106 struct timer_list hangcheck_timer;
@@ -99,6 +108,11 @@ struct msm_gpu {
99 struct work_struct recover_work; 108 struct work_struct recover_work;
100}; 109};
101 110
111static inline bool msm_gpu_active(struct msm_gpu *gpu)
112{
113 return gpu->submitted_fence > gpu->funcs->last_fence(gpu);
114}
115
102static inline void gpu_write(struct msm_gpu *gpu, u32 reg, u32 data) 116static inline void gpu_write(struct msm_gpu *gpu, u32 reg, u32 data)
103{ 117{
104 msm_writel(data, gpu->mmio + (reg << 2)); 118 msm_writel(data, gpu->mmio + (reg << 2));