On 11/3/2026 4:15 PM, Abel Vesa wrote:
> The GMU puts the RSC to sleep when the GPU is suspended, and remembers
> that it has to request the RSC to wake up again on the next resume. A
> hibernation image is restored after a power cycle though, and the RSC
> then starts from scratch, in a state where it does not answer that
> request:
> 
>   adreno_gmu 3d6c000.gmu: [drm:a6xx_gmu_resume [msm]] *ERROR* Unable to power 
> on the GPU RSC
>   adreno_gmu 3d6c000.gmu: [drm:a6xx_gmu_set_oob [msm]] *ERROR* Timeout 
> waiting for GMU OOB set GPU_SET: 0x0
>   msm_dpu ae01000.display-controller: [drm:hangcheck_handler [msm]] *ERROR* 
> 44070041: hangcheck detected gpu lockup rb 0!
> 
> The first job submitted after the resume hangs, and the compositor is
> left with nothing to show.
> 
> The GMU secure init has the same problem: it is done once, but the
> registers it has the secure world program are reset by the power cycle
> as well, so the restored kernel only gets them set up if the kernel
> that loaded the image happened to initialize the GPU.
> 
> Forget about both when restoring, so that the resume starts the GMU
> like after probe.
> 
> Signed-off-by: Abel Vesa <[email protected]>
> ---
> Sent as an RFC, as this only resets the GMU state the driver keeps
> track of. That is enough on the Glymur CRD (A8xx), where the AOP
> configures the RSC and PDC. On A6xx and A7xx, a6xx_gmu_rpmh_init()
> programs the RSCC sequence, and on older parts the PDC, only at probe,
> so after a restore they are only set up if the kernel that loaded the
> image probed the GPU.
> 
> The zap shader has a similar problem: it is loaded once per boot (the
> static "loaded" in a6xx_zap_shader_init()), while the secure world and
> the zap memory region start out empty after the power cycle.
> 
> Would a full GMU re-initialization on restore be preferred over
> resetting the status bits?
> 
> Tested on the Glymur CRD over 9 hibernation and restore cycles, with
> msm in the initramfs of the kernel that loads the image. The GPU renders
> after each restore, with no RSC, OOB or hangcheck errors.
> 
> Prepared with help from an LLM-based assistant.
> ---
>  drivers/gpu/drm/msm/adreno/a6xx_gmu.c      | 10 ++++++++++
>  drivers/gpu/drm/msm/adreno/a6xx_gpu.c      |  8 ++++++++
>  drivers/gpu/drm/msm/adreno/a6xx_gpu.h      |  1 +
>  drivers/gpu/drm/msm/adreno/adreno_device.c | 22 +++++++++++++++++++++-
>  drivers/gpu/drm/msm/adreno/adreno_gpu.h    |  2 ++
>  5 files changed, 42 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/gpu/drm/msm/adreno/a6xx_gmu.c 
> b/drivers/gpu/drm/msm/adreno/a6xx_gmu.c
> index 662f09bec9d5..1e6e47b635f0 100644
> --- a/drivers/gpu/drm/msm/adreno/a6xx_gmu.c
> +++ b/drivers/gpu/drm/msm/adreno/a6xx_gmu.c
> @@ -1319,6 +1319,16 @@ static int a6xx_gmu_gxpd_put(struct a6xx_gmu *gmu)
>       return 0;
>  }
>  
> +/*
> + * After a power cycle the RSC does not expect a wakeup request and the GMU
> + * secure init is gone, so start the GMU like after probe.
> + */
> +void a6xx_gmu_power_cycled(struct a6xx_gpu *a6xx_gpu)
> +{

Need a check to skip this on gmu_wrapper.
> +     clear_bit(GMU_STATUS_PDC_SLEEP, &a6xx_gpu->gmu.status);
> +     clear_bit(GMU_STATUS_SECURE_INIT, &a6xx_gpu->gmu.status);
> +}
> +
>  int a6xx_gmu_resume(struct a6xx_gpu *a6xx_gpu)
>  {
>       struct adreno_gpu *adreno_gpu = &a6xx_gpu->base;
> diff --git a/drivers/gpu/drm/msm/adreno/a6xx_gpu.c 
> b/drivers/gpu/drm/msm/adreno/a6xx_gpu.c
> index 92e8c8abb86d..962b497868da 100644
> --- a/drivers/gpu/drm/msm/adreno/a6xx_gpu.c
> +++ b/drivers/gpu/drm/msm/adreno/a6xx_gpu.c
> @@ -2796,6 +2796,11 @@ static struct msm_gpu *a6xx_gpu_init(struct drm_device 
> *dev)
>       return gpu;
>  }
>  
> +static void a6xx_gpu_power_cycled(struct adreno_gpu *adreno_gpu)
> +{

This wrapper is unnecessary.

> +     a6xx_gmu_power_cycled(to_a6xx_gpu(adreno_gpu));
> +}
> +
>  const struct adreno_gpu_funcs a6xx_gpu_funcs = {
>       .base = {
>               .get_param = adreno_get_param,
> @@ -2831,6 +2836,7 @@ const struct adreno_gpu_funcs a6xx_gpu_funcs = {
>       .bus_halt = a6xx_bus_clear_pending_transactions,
>       .mmu_fault_handler = a6xx_fault_handler,
>       .gx_is_on = a6xx_gmu_gx_is_on,
> +     .power_cycled = a6xx_gpu_power_cycled,
>  };
>  
>  const struct adreno_gpu_funcs a6xx_gmuwrapper_funcs = {
> @@ -2903,6 +2909,7 @@ const struct adreno_gpu_funcs a7xx_gpu_funcs = {
>       .mmu_fault_handler = a6xx_fault_handler,
>       .gx_is_on = a7xx_gmu_gx_is_on,
>       .aqe_is_enabled = a6xx_aqe_is_enabled,
> +     .power_cycled = a6xx_gpu_power_cycled,
>  };
>  
>  const struct adreno_gpu_funcs a8xx_gpu_funcs = {
> @@ -2935,4 +2942,5 @@ const struct adreno_gpu_funcs a8xx_gpu_funcs = {
>       .mmu_fault_handler = a8xx_fault_handler,
>       .gx_is_on = a8xx_gmu_gx_is_on,
>       .aqe_is_enabled = a6xx_aqe_is_enabled,
> +     .power_cycled = a6xx_gpu_power_cycled,
>  };
> diff --git a/drivers/gpu/drm/msm/adreno/a6xx_gpu.h 
> b/drivers/gpu/drm/msm/adreno/a6xx_gpu.h
> index 79912a33e17c..37266d64427c 100644
> --- a/drivers/gpu/drm/msm/adreno/a6xx_gpu.h
> +++ b/drivers/gpu/drm/msm/adreno/a6xx_gpu.h
> @@ -286,6 +286,7 @@ static inline void a6xx_cx_misc_write(struct a6xx_gpu 
> *a6xx_gpu, u32 reg, u32 va
>               ((_ring)->id * sizeof(uint32_t)))
>  
>  int a6xx_gmu_resume(struct a6xx_gpu *gpu);
> +void a6xx_gmu_power_cycled(struct a6xx_gpu *gpu);
>  int a6xx_gmu_stop(struct a6xx_gpu *gpu);
>  
>  int a6xx_gmu_wait_for_idle(struct a6xx_gmu *gmu);
> diff --git a/drivers/gpu/drm/msm/adreno/adreno_device.c 
> b/drivers/gpu/drm/msm/adreno/adreno_device.c
> index 7108d4aa59b5..b91b32e61fab 100644
> --- a/drivers/gpu/drm/msm/adreno/adreno_device.c
> +++ b/drivers/gpu/drm/msm/adreno/adreno_device.c
> @@ -402,8 +402,28 @@ static int adreno_system_resume(struct device *dev)
>       return pm_runtime_force_resume(dev);
>  }
>  
> +static int adreno_system_restore(struct device *dev)
> +{
> +     struct msm_gpu *gpu = dev_to_gpu(dev);
> +     struct adreno_gpu *adreno_gpu;
> +
> +     if (!gpu)
> +             return 0;
> +
> +     adreno_gpu = to_adreno_gpu(gpu);
> +     if (adreno_gpu->funcs->power_cycled)
> +             adreno_gpu->funcs->power_cycled(adreno_gpu);
> +

Can we please reset the zap loaded state here even though Glymur doesn't
require it at the moment?

Also, please ensure all registers are reloaded on the smmu driver side.
for eg: PRR configuration.

-Akhil.

> +     return adreno_system_resume(dev);
> +}
> +
>  static const struct dev_pm_ops adreno_pm_ops = {
> -     SYSTEM_SLEEP_PM_OPS(adreno_system_suspend, adreno_system_resume)
> +     .suspend = pm_sleep_ptr(adreno_system_suspend),
> +     .resume = pm_sleep_ptr(adreno_system_resume),
> +     .freeze = pm_sleep_ptr(adreno_system_suspend),
> +     .thaw = pm_sleep_ptr(adreno_system_resume),
> +     .poweroff = pm_sleep_ptr(adreno_system_suspend),
> +     .restore = pm_sleep_ptr(adreno_system_restore),
>       RUNTIME_PM_OPS(adreno_runtime_suspend, adreno_runtime_resume, NULL)
>  };
>  
> diff --git a/drivers/gpu/drm/msm/adreno/adreno_gpu.h 
> b/drivers/gpu/drm/msm/adreno/adreno_gpu.h
> index 4b2156825bdf..7ad8cbcfede4 100644
> --- a/drivers/gpu/drm/msm/adreno/adreno_gpu.h
> +++ b/drivers/gpu/drm/msm/adreno/adreno_gpu.h
> @@ -81,6 +81,8 @@ struct adreno_gpu_funcs {
>       int (*mmu_fault_handler)(void *arg, unsigned long iova, int flags, void 
> *data);
>       bool (*gx_is_on)(struct adreno_gpu *adreno_gpu);
>       bool (*aqe_is_enabled)(struct adreno_gpu *adreno_gpu);
> +     /* Called on restore from hibernation, after the hardware lost its 
> state */
> +     void (*power_cycled)(struct adreno_gpu *adreno_gpu);
>  };
>  
>  struct adreno_reglist {
> 
> ---
> base-commit: 2637bdec0535a2044cd830eba9a79363651d9043
> change-id: 20261023-b4-drm-msm-a6xx-rsc-restore-hibernation-912730e2de2d
> 
> Best regards,
> --  
> 
> Abel

Reply via email to