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>
This commit is contained in:
Rob Clark 2014-01-11 16:25:08 -05:00
parent 0963756fe5
commit 37d77c3ab5
4 changed files with 113 additions and 5 deletions

View File

@ -395,9 +395,15 @@ static const unsigned int a3xx_registers[] = {
#ifdef CONFIG_DEBUG_FS #ifdef CONFIG_DEBUG_FS
static void a3xx_show(struct msm_gpu *gpu, struct seq_file *m) static void a3xx_show(struct msm_gpu *gpu, struct seq_file *m)
{ {
struct drm_device *dev = gpu->dev;
int i; int i;
adreno_show(gpu, m); adreno_show(gpu, m);
mutex_lock(&dev->struct_mutex);
gpu->funcs->pm_resume(gpu);
seq_printf(m, "status: %08x\n", seq_printf(m, "status: %08x\n",
gpu_read(gpu, REG_A3XX_RBBM_STATUS)); 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); seq_printf(m, "IO:R %08x %08x\n", addr<<2, val);
} }
} }
gpu->funcs->pm_suspend(gpu);
mutex_unlock(&dev->struct_mutex);
} }
#endif #endif

View File

@ -311,7 +311,6 @@ static void load_gpu(struct drm_device *dev)
gpu = NULL; gpu = NULL;
/* not fatal */ /* not fatal */
} }
mutex_unlock(&dev->struct_mutex);
if (gpu) { if (gpu) {
int ret; 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); dev_err(dev->dev, "gpu hw init failed: %d\n", ret);
gpu->funcs->destroy(gpu); gpu->funcs->destroy(gpu);
gpu = NULL; gpu = NULL;
} else {
/* give inactive pm a chance to kick in: */
msm_gpu_retire(gpu);
} }
} }
priv->gpu = gpu; priv->gpu = gpu;
mutex_unlock(&dev->struct_mutex);
} }
static int msm_open(struct drm_device *dev, struct drm_file *file) static int msm_open(struct drm_device *dev, struct drm_file *file)

View File

@ -154,9 +154,18 @@ static int disable_axi(struct msm_gpu *gpu)
int msm_gpu_pm_resume(struct msm_gpu *gpu) int msm_gpu_pm_resume(struct msm_gpu *gpu)
{ {
struct drm_device *dev = gpu->dev;
int ret; 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); ret = enable_pwrrail(gpu);
if (ret) if (ret)
@ -175,9 +184,18 @@ int msm_gpu_pm_resume(struct msm_gpu *gpu)
int msm_gpu_pm_suspend(struct msm_gpu *gpu) int msm_gpu_pm_suspend(struct msm_gpu *gpu)
{ {
struct drm_device *dev = gpu->dev;
int ret; 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); ret = disable_axi(gpu);
if (ret) if (ret)
@ -194,6 +212,55 @@ int msm_gpu_pm_suspend(struct msm_gpu *gpu)
return 0; return 0;
} }
/*
* 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: * 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); dev_err(dev->dev, "%s: hangcheck recover!\n", gpu->name);
mutex_lock(&dev->struct_mutex); 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); mutex_unlock(&dev->struct_mutex);
msm_gpu_retire(gpu); msm_gpu_retire(gpu);
@ -281,6 +351,9 @@ static void retire_worker(struct work_struct *work)
} }
mutex_unlock(&dev->struct_mutex); mutex_unlock(&dev->struct_mutex);
if (!msm_gpu_active(gpu))
inactive_start(gpu);
} }
/* call from irq handler to schedule work to retire bo's */ /* 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; gpu->submitted_fence = submit->fence;
inactive_cancel(gpu);
ret = gpu->funcs->submit(gpu, submit, ctx); ret = gpu->funcs->submit(gpu, submit, ctx);
priv->lastctx = ctx; priv->lastctx = ctx;
@ -357,11 +432,15 @@ int msm_gpu_init(struct drm_device *drm, struct platform_device *pdev,
gpu->dev = drm; gpu->dev = drm;
gpu->funcs = funcs; gpu->funcs = funcs;
gpu->name = name; gpu->name = name;
gpu->inactive = true;
INIT_LIST_HEAD(&gpu->active_list); INIT_LIST_HEAD(&gpu->active_list);
INIT_WORK(&gpu->retire_work, retire_worker); INIT_WORK(&gpu->retire_work, retire_worker);
INIT_WORK(&gpu->inactive_work, inactive_worker);
INIT_WORK(&gpu->recover_work, recover_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, setup_timer(&gpu->hangcheck_timer, hangcheck_handler,
(unsigned long)gpu); (unsigned long)gpu);

View File

@ -72,6 +72,10 @@ struct msm_gpu {
uint32_t submitted_fence; uint32_t submitted_fence;
/* is gpu powered/active? */
int active_cnt;
bool inactive;
/* worker for handling active-list retiring: */ /* worker for handling active-list retiring: */
struct work_struct retire_work; struct work_struct retire_work;
@ -91,7 +95,12 @@ struct msm_gpu {
uint32_t bsc; uint32_t bsc;
#endif #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_PERIOD 500 /* in ms */
#define DRM_MSM_HANGCHECK_JIFFIES msecs_to_jiffies(DRM_MSM_HANGCHECK_PERIOD) #define DRM_MSM_HANGCHECK_JIFFIES msecs_to_jiffies(DRM_MSM_HANGCHECK_PERIOD)
struct timer_list hangcheck_timer; struct timer_list hangcheck_timer;
@ -99,6 +108,11 @@ struct msm_gpu {
struct work_struct recover_work; 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) static inline void gpu_write(struct msm_gpu *gpu, u32 reg, u32 data)
{ {
msm_writel(data, gpu->mmio + (reg << 2)); msm_writel(data, gpu->mmio + (reg << 2));