[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