[PATCH 4/7] drm/msm: crank down gpu when inactive
Jordan Crouse
jcrouse at codeaurora.org
Mon Mar 10 13:24:13 PDT 2014
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 at gmail.com>
Acked-by: Jordan Crouse <jcrouse at 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));
>
--
The Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
hosted by The Linux Foundation
More information about the dri-devel
mailing list