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