mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH RFC] drm/msm/a6xx: Reset the GMU state when restoring from hibernation
@ 2026-11-03 10:45 Abel Vesa
  2026-11-05 20:18 ` Akhil P Oommen
  0 siblings, 1 reply; 2+ messages in thread
From: Abel Vesa @ 2026-11-03 10:45 UTC (permalink / raw)
  To: Rob Clark, Sean Paul, Konrad Dybcio, Akhil P Oommen,
	Dmitry Baryshkov, Abhinav Kumar, Jessica Zhang, Marijn Suijten,
	David Airlie, Simona Vetter
  Cc: linux-arm-msm, dri-devel, freedreno, linux-kernel, Abel Vesa

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 <abel.vesa@oss.qualcomm.com>
---
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)
+{
+	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)
+{
+	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);
+
+	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


^ permalink raw reply	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2026-11-05 20:19 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-11-03 10:45 [PATCH RFC] drm/msm/a6xx: Reset the GMU state when restoring from hibernation Abel Vesa
2026-11-05 20:18 ` Akhil P Oommen

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®