On 03/10/2014 10:47 AM, Rob Clark wrote:
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.
Good luck. Power management will take years off your life.
Signed-off-by: Rob Clark robdclark@gmail.com
Acked-by: Jordan Crouse jcrouse@codeaurora.org
drivers/gpu/drm/msm/adreno/a3xx_gpu.c | 10 +++++ drivers/gpu/drm/msm/msm_drv.c | 7 ++- drivers/gpu/drm/msm/msm_gpu.c | 85 +++++++++++++++++++++++++++++++++-- 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 59ed762..e6cb2bc 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[] = { #ifdef CONFIG_DEBUG_FS static void a3xx_show(struct msm_gpu *gpu, struct seq_file *m) {
struct drm_device *dev = gpu->dev; int i;
adreno_show(gpu, m);
mutex_lock(&dev->struct_mutex);
gpu->funcs->pm_resume(gpu);
seq_printf(m, "status: %08x\n", gpu_read(gpu, REG_A3XX_RBBM_STATUS));
@@ -413,6 +419,10 @@ static void a3xx_show(struct msm_gpu *gpu, struct seq_file *m) seq_printf(m, "IO:R %08x %08x\n", addr<<2, val); } }
- gpu->funcs->pm_suspend(gpu);
- mutex_unlock(&dev->struct_mutex); } #endif
diff --git a/drivers/gpu/drm/msm/msm_drv.c b/drivers/gpu/drm/msm/msm_drv.c index e6adafc..e913efa 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) gpu = NULL; /* not fatal */ }
mutex_unlock(&dev->struct_mutex);
if (gpu) { int ret;
@@ -321,10 +320,16 @@ static void load_gpu(struct drm_device *dev) dev_err(dev->dev, "gpu hw init failed: %d\n", ret); gpu->funcs->destroy(gpu); gpu = NULL;
} else {
/* give inactive pm a chance to kick in: */
msm_gpu_retire(gpu);
}
}
priv->gpu = gpu;
mutex_unlock(&dev->struct_mutex); }
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 0cfe3f4..3e667ca 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)
int msm_gpu_pm_resume(struct msm_gpu *gpu) {
- struct drm_device *dev = gpu->dev; int ret;
- DBG("%s", gpu->name);
DBG("%s: active_cnt=%d", gpu->name, gpu->active_cnt);
WARN_ON(!mutex_is_locked(&dev->struct_mutex));
if (gpu->active_cnt++ > 0)
return 0;
if (WARN_ON(gpu->active_cnt <= 0))
return -EINVAL;
ret = enable_pwrrail(gpu); if (ret)
@@ -175,9 +184,18 @@ int msm_gpu_pm_resume(struct msm_gpu *gpu)
int msm_gpu_pm_suspend(struct msm_gpu *gpu) {
- struct drm_device *dev = gpu->dev; int ret;
- DBG("%s", gpu->name);
DBG("%s: active_cnt=%d", gpu->name, gpu->active_cnt);
WARN_ON(!mutex_is_locked(&dev->struct_mutex));
if (--gpu->active_cnt > 0)
return 0;
if (WARN_ON(gpu->active_cnt < 0))
return -EINVAL;
ret = disable_axi(gpu); if (ret)
@@ -195,6 +213,55 @@ int msm_gpu_pm_suspend(struct msm_gpu *gpu) }
/*
- Inactivity detection (for suspend):
- */
+static void inactive_worker(struct work_struct *work) +{
- struct msm_gpu *gpu = container_of(work, struct msm_gpu, inactive_work);
- struct drm_device *dev = gpu->dev;
- if (gpu->inactive)
return;
- DBG("%s: inactive!\n", gpu->name);
- mutex_lock(&dev->struct_mutex);
- if (!(msm_gpu_active(gpu) || gpu->inactive)) {
disable_axi(gpu);
disable_clk(gpu);
gpu->inactive = true;
- }
- mutex_unlock(&dev->struct_mutex);
+}
+static void inactive_handler(unsigned long data) +{
- struct msm_gpu *gpu = (struct msm_gpu *)data;
- struct msm_drm_private *priv = gpu->dev->dev_private;
- queue_work(priv->wq, &gpu->inactive_work);
+}
+/* cancel inactive timer and make sure we are awake: */ +static void inactive_cancel(struct msm_gpu *gpu) +{
- DBG("%s", gpu->name);
- del_timer(&gpu->inactive_timer);
- if (gpu->inactive) {
enable_clk(gpu);
enable_axi(gpu);
gpu->inactive = false;
- }
+}
+static void inactive_start(struct msm_gpu *gpu) +{
- DBG("%s", gpu->name);
- mod_timer(&gpu->inactive_timer,
round_jiffies_up(jiffies + DRM_MSM_INACTIVE_JIFFIES));
+}
+/*
- Hangcheck detection for locked gpu:
*/
@@ -206,7 +273,10 @@ static void recover_worker(struct work_struct *work) dev_err(dev->dev, "%s: hangcheck recover!\n", gpu->name);
mutex_lock(&dev->struct_mutex);
- gpu->funcs->recover(gpu);
if (msm_gpu_active(gpu)) {
inactive_cancel(gpu);
gpu->funcs->recover(gpu);
} mutex_unlock(&dev->struct_mutex);
msm_gpu_retire(gpu);
@@ -281,6 +351,9 @@ static void retire_worker(struct work_struct *work) }
mutex_unlock(&dev->struct_mutex);
if (!msm_gpu_active(gpu))
inactive_start(gpu);
}
/* 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,
gpu->submitted_fence = submit->fence;
- inactive_cancel(gpu);
- ret = gpu->funcs->submit(gpu, submit, ctx); priv->lastctx = ctx;
@@ -357,11 +432,15 @@ int msm_gpu_init(struct drm_device *drm, struct platform_device *pdev, gpu->dev = drm; gpu->funcs = funcs; gpu->name = name;
gpu->inactive = true;
INIT_LIST_HEAD(&gpu->active_list); INIT_WORK(&gpu->retire_work, retire_worker);
INIT_WORK(&gpu->inactive_work, inactive_worker); INIT_WORK(&gpu->recover_work, recover_worker);
setup_timer(&gpu->inactive_timer, inactive_handler,
(unsigned long)gpu);
setup_timer(&gpu->hangcheck_timer, hangcheck_handler, (unsigned long)gpu);
diff --git a/drivers/gpu/drm/msm/msm_gpu.h b/drivers/gpu/drm/msm/msm_gpu.h index 458db8c..fad2700 100644 --- a/drivers/gpu/drm/msm/msm_gpu.h +++ b/drivers/gpu/drm/msm/msm_gpu.h @@ -72,6 +72,10 @@ struct msm_gpu {
uint32_t submitted_fence;
- /* is gpu powered/active? */
- int active_cnt;
- bool inactive;
- /* worker for handling active-list retiring: */ struct work_struct retire_work;
@@ -91,7 +95,12 @@ struct msm_gpu { uint32_t bsc; #endif
- /* Hang Detction: */
- /* Hang and Inactivity Detection:
*/
+#define DRM_MSM_INACTIVE_PERIOD 66 /* in ms (roughly four frames) */ +#define DRM_MSM_INACTIVE_JIFFIES msecs_to_jiffies(DRM_MSM_INACTIVE_PERIOD)
- struct timer_list inactive_timer;
- struct work_struct inactive_work; #define DRM_MSM_HANGCHECK_PERIOD 500 /* in ms */ #define DRM_MSM_HANGCHECK_JIFFIES msecs_to_jiffies(DRM_MSM_HANGCHECK_PERIOD) struct timer_list hangcheck_timer;
@@ -99,6 +108,11 @@ struct msm_gpu { struct work_struct recover_work; };
+static inline bool msm_gpu_active(struct msm_gpu *gpu) +{
- return gpu->submitted_fence > gpu->funcs->last_fence(gpu);
+}
- static inline void gpu_write(struct msm_gpu *gpu, u32 reg, u32 data) { msm_writel(data, gpu->mmio + (reg << 2));