diff options
author | Rob Clark <robdclark@gmail.com> | 2014-01-11 16:25:08 -0500 |
---|---|---|
committer | Rob Clark <robdclark@gmail.com> | 2014-03-31 10:27:46 -0400 |
commit | 37d77c3ab589bf8e3160a33b651fe6dd7439ba48 (patch) | |
tree | 12410f409382ba0557078e777f600e2377c08053 | |
parent | 0963756fe51313a1e2d76885cd21624d3b2cfbf2 (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.c | 10 | ||||
-rw-r--r-- | drivers/gpu/drm/msm/msm_drv.c | 7 | ||||
-rw-r--r-- | drivers/gpu/drm/msm/msm_gpu.c | 85 | ||||
-rw-r--r-- | drivers/gpu/drm/msm/msm_gpu.h | 16 |
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 |
396 | static void a3xx_show(struct msm_gpu *gpu, struct seq_file *m) | 396 | static 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 | ||
330 | static int msm_open(struct drm_device *dev, struct drm_file *file) | 335 | static 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 | ||
155 | int msm_gpu_pm_resume(struct msm_gpu *gpu) | 155 | int 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 | ||
176 | int msm_gpu_pm_suspend(struct msm_gpu *gpu) | 185 | int 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 | |||
219 | static 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 | |||
237 | static 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: */ | ||
246 | static 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 | |||
257 | static 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 | ||
111 | static inline bool msm_gpu_active(struct msm_gpu *gpu) | ||
112 | { | ||
113 | return gpu->submitted_fence > gpu->funcs->last_fence(gpu); | ||
114 | } | ||
115 | |||
102 | static inline void gpu_write(struct msm_gpu *gpu, u32 reg, u32 data) | 116 | static 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)); |