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:
parent
0963756fe5
commit
37d77c3ab5
@ -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
|
||||||
|
|
||||||
|
@ -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)
|
||||||
|
@ -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);
|
||||||
|
if (msm_gpu_active(gpu)) {
|
||||||
|
inactive_cancel(gpu);
|
||||||
gpu->funcs->recover(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);
|
||||||
|
|
||||||
|
@ -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));
|
||||||
|
Loading…
Reference in New Issue
Block a user