* [PATCH 0/3] drm/msm/a5xx: Fix GPU stalls on A508, A509 and A512
@ 2026-10-19 22:58 ` Fabricio Akio via B4 Relay
0 siblings, 0 replies; 9+ messages in thread
From: Fabricio Akio @ 2026-10-19 22:58 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, Paul Sajna,
Fabricio Akio
Three changes to how the A508, A509 and A512 power up and down, found
running a phone shell on an SDM630 phone. The first resets the GPU on
every resume: on SDM630, SDM660 and MSM8998 the GPU's GX power domain
belongs to its SMMU, so a GPU resumed soon after suspending keeps its
state, and its CP runs stale ringbuffer contents and hangs. The second
holds a CPU latency request while the GPU is powered, as the
downstream driver does: on SDM630 the GPU stops executing while the
SoC is in system-level power collapse, so a submission overlapping
such a state would stall and trip the hang detector. The third powers
these GPUs down after downstream's 80 ms idle instead of the A530's
250 ms, so the request is released when downstream releases it.
The second patch adds code next to the first's, and the third sits on
the second.
Tested on a Motorola Moto X4 (SDM630, A508), whose board file is not
in mainline yet, on sdm660-mainline's 7.2 kernel. The stall the second
patch prevents was measured with an SDM630 system-level idle state
that is not in mainline.
---
Fabricio Akio (3):
drm/msm/a5xx: Reset the GPU when resuming it
drm/msm/a5xx: Hold a CPU latency request while the GPU is powered
drm/msm/a5xx: Use the downstream GPU idle timeout on A508, A509 and A512
drivers/gpu/drm/msm/adreno/a5xx_catalog.c | 21 ++++++---------------
drivers/gpu/drm/msm/adreno/a5xx_gpu.c | 25 +++++++++++++++++++++++++
drivers/gpu/drm/msm/adreno/a5xx_gpu.h | 4 ++++
drivers/gpu/drm/msm/adreno/adreno_gpu.h | 5 +++++
4 files changed, 40 insertions(+), 15 deletions(-)
---
base-commit: 2efb7a11f1aa4d2dc285fe2680cca22cb9f72f64
change-id: 20261019-a5xx-gpu-pm-f58bc4ee2387
Best regards,
--
Fabricio Akio <fabricioakio@gmail.com>
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH 0/3] drm/msm/a5xx: Fix GPU stalls on A508, A509 and A512
@ 2026-10-19 22:58 ` Fabricio Akio via B4 Relay
0 siblings, 0 replies; 9+ messages in thread
From: Fabricio Akio via B4 Relay @ 2026-10-19 22:58 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, Paul Sajna,
Fabricio Akio
Three changes to how the A508, A509 and A512 power up and down, found
running a phone shell on an SDM630 phone. The first resets the GPU on
every resume: on SDM630, SDM660 and MSM8998 the GPU's GX power domain
belongs to its SMMU, so a GPU resumed soon after suspending keeps its
state, and its CP runs stale ringbuffer contents and hangs. The second
holds a CPU latency request while the GPU is powered, as the
downstream driver does: on SDM630 the GPU stops executing while the
SoC is in system-level power collapse, so a submission overlapping
such a state would stall and trip the hang detector. The third powers
these GPUs down after downstream's 80 ms idle instead of the A530's
250 ms, so the request is released when downstream releases it.
The second patch adds code next to the first's, and the third sits on
the second.
Tested on a Motorola Moto X4 (SDM630, A508), whose board file is not
in mainline yet, on sdm660-mainline's 7.2 kernel. The stall the second
patch prevents was measured with an SDM630 system-level idle state
that is not in mainline.
---
Fabricio Akio (3):
drm/msm/a5xx: Reset the GPU when resuming it
drm/msm/a5xx: Hold a CPU latency request while the GPU is powered
drm/msm/a5xx: Use the downstream GPU idle timeout on A508, A509 and A512
drivers/gpu/drm/msm/adreno/a5xx_catalog.c | 21 ++++++---------------
drivers/gpu/drm/msm/adreno/a5xx_gpu.c | 25 +++++++++++++++++++++++++
drivers/gpu/drm/msm/adreno/a5xx_gpu.h | 4 ++++
drivers/gpu/drm/msm/adreno/adreno_gpu.h | 5 +++++
4 files changed, 40 insertions(+), 15 deletions(-)
---
base-commit: 2efb7a11f1aa4d2dc285fe2680cca22cb9f72f64
change-id: 20261019-a5xx-gpu-pm-f58bc4ee2387
Best regards,
--
Fabricio Akio <fabricioakio@gmail.com>
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH 1/3] drm/msm/a5xx: Reset the GPU when resuming it
2026-10-19 22:58 ` Fabricio Akio via B4 Relay
@ 2026-10-19 22:58 ` Fabricio Akio via B4 Relay
-1 siblings, 0 replies; 9+ messages in thread
From: Fabricio Akio @ 2026-10-19 22:58 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, Paul Sajna,
Fabricio Akio
On SDM630, SDM660 and MSM8998 the GPU's GX power domain belongs to its
SMMU, not to the GPU, so GX switches off only once the SMMU suspends as
well. A GPU resumed with GX still on keeps its state, and the CP keeps
its ringbuffer read pointer. a5xx_hw_init() then rewinds the ringbuffer
and writes CP_ME_INIT at its start, while the CP goes on from its old
position, executes stale ringbuffer contents and hangs:
timeout waiting to drain ringbuffer 0 rptr/wptr = 23/C
Soft reset the GPU in a5xx_pm_resume() once it is powered, so that
every resume starts from the reset state. The downstream driver resets
only when it finds the GPU's power left on; resetting a GPU that was
powered off changes nothing, and on A508 the reset takes about 2 us of
a resume of about 1.5 ms, so do it every time.
Signed-off-by: Fabricio Akio <fabricioakio@gmail.com>
---
Tested on A508 only; the reset runs on every A5xx.
A stall with the same signature, the CP's read pointer past the write
pointer just after CP_ME_INIT, was reported on an A509:
https://lore.kernel.org/linux-arm-msm/20260308-adreno-ringbuffer-drain-timeout-recovery-v1-1-985a33faf108@postmarketos.org/
This may be the root cause of the A509 stall Paul Sajna reported there.
---
drivers/gpu/drm/msm/adreno/a5xx_gpu.c | 12 ++++++++++++
1 file changed, 12 insertions(+)
diff --git a/drivers/gpu/drm/msm/adreno/a5xx_gpu.c b/drivers/gpu/drm/msm/adreno/a5xx_gpu.c
index 16c7335c4b1f..dd0d731df138 100644
--- a/drivers/gpu/drm/msm/adreno/a5xx_gpu.c
+++ b/drivers/gpu/drm/msm/adreno/a5xx_gpu.c
@@ -1367,6 +1367,18 @@ static int a5xx_pm_resume(struct msm_gpu *gpu)
if (ret)
return ret;
+ /*
+ * The GX power domain is not necessarily switched off while we are
+ * suspended: on SDM630, SDM660 and MSM8998 it follows the GPU's SMMU
+ * rather than the GPU. If it stayed on, the CP still holds its old
+ * ringbuffer read pointer and would execute stale ringbuffer contents
+ * once hw_init restarts it. Soft reset the GPU so that it always
+ * resumes from its reset state.
+ */
+ gpu_write(gpu, REG_A5XX_RBBM_SW_RESET_CMD, 1);
+ gpu_read(gpu, REG_A5XX_RBBM_SW_RESET_CMD);
+ gpu_write(gpu, REG_A5XX_RBBM_SW_RESET_CMD, 0);
+
/* Adreno 505, 506, 508, 509, 510, 512 needs manual RBBM sus/res control */
if (!(adreno_is_a530(adreno_gpu) || adreno_is_a540(adreno_gpu))) {
/* Halt the sp_input_clk at HM level */
--
2.56.0
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH 1/3] drm/msm/a5xx: Reset the GPU when resuming it
@ 2026-10-19 22:58 ` Fabricio Akio via B4 Relay
0 siblings, 0 replies; 9+ messages in thread
From: Fabricio Akio via B4 Relay @ 2026-10-19 22:58 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, Paul Sajna,
Fabricio Akio
From: Fabricio Akio <fabricioakio@gmail.com>
On SDM630, SDM660 and MSM8998 the GPU's GX power domain belongs to its
SMMU, not to the GPU, so GX switches off only once the SMMU suspends as
well. A GPU resumed with GX still on keeps its state, and the CP keeps
its ringbuffer read pointer. a5xx_hw_init() then rewinds the ringbuffer
and writes CP_ME_INIT at its start, while the CP goes on from its old
position, executes stale ringbuffer contents and hangs:
timeout waiting to drain ringbuffer 0 rptr/wptr = 23/C
Soft reset the GPU in a5xx_pm_resume() once it is powered, so that
every resume starts from the reset state. The downstream driver resets
only when it finds the GPU's power left on; resetting a GPU that was
powered off changes nothing, and on A508 the reset takes about 2 us of
a resume of about 1.5 ms, so do it every time.
Signed-off-by: Fabricio Akio <fabricioakio@gmail.com>
---
Tested on A508 only; the reset runs on every A5xx.
A stall with the same signature, the CP's read pointer past the write
pointer just after CP_ME_INIT, was reported on an A509:
https://lore.kernel.org/linux-arm-msm/20260308-adreno-ringbuffer-drain-timeout-recovery-v1-1-985a33faf108@postmarketos.org/
This may be the root cause of the A509 stall Paul Sajna reported there.
---
drivers/gpu/drm/msm/adreno/a5xx_gpu.c | 12 ++++++++++++
1 file changed, 12 insertions(+)
diff --git a/drivers/gpu/drm/msm/adreno/a5xx_gpu.c b/drivers/gpu/drm/msm/adreno/a5xx_gpu.c
index 16c7335c4b1f..dd0d731df138 100644
--- a/drivers/gpu/drm/msm/adreno/a5xx_gpu.c
+++ b/drivers/gpu/drm/msm/adreno/a5xx_gpu.c
@@ -1367,6 +1367,18 @@ static int a5xx_pm_resume(struct msm_gpu *gpu)
if (ret)
return ret;
+ /*
+ * The GX power domain is not necessarily switched off while we are
+ * suspended: on SDM630, SDM660 and MSM8998 it follows the GPU's SMMU
+ * rather than the GPU. If it stayed on, the CP still holds its old
+ * ringbuffer read pointer and would execute stale ringbuffer contents
+ * once hw_init restarts it. Soft reset the GPU so that it always
+ * resumes from its reset state.
+ */
+ gpu_write(gpu, REG_A5XX_RBBM_SW_RESET_CMD, 1);
+ gpu_read(gpu, REG_A5XX_RBBM_SW_RESET_CMD);
+ gpu_write(gpu, REG_A5XX_RBBM_SW_RESET_CMD, 0);
+
/* Adreno 505, 506, 508, 509, 510, 512 needs manual RBBM sus/res control */
if (!(adreno_is_a530(adreno_gpu) || adreno_is_a540(adreno_gpu))) {
/* Halt the sp_input_clk at HM level */
--
2.56.0
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH 2/3] drm/msm/a5xx: Hold a CPU latency request while the GPU is powered
2026-10-19 22:58 ` Fabricio Akio via B4 Relay
@ 2026-10-19 22:58 ` Fabricio Akio via B4 Relay
-1 siblings, 0 replies; 9+ messages in thread
From: Fabricio Akio @ 2026-10-19 22:58 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, Paul Sajna,
Fabricio Akio
On SDM630 the GPU was observed to stop executing while the SoC is in
system-level power collapse, and to continue once the SoC wakes. Its
interrupts have no wakeup route through the MPM, so it cannot wake the
SoC itself. A submission overlapping such a state would stall until a
CPU wakes, and a stall longer than the hang detector's threshold makes
the driver recover the GPU, which can lose a frame.
Qualcomm's downstream driver holds a CPU latency request while the GPU
is active, qcom,pm-qos-active-latency, which keeps the CPU idle
governors out of cluster and system-level power collapse. Do the same:
take the request when the GPU resumes and drop it when it suspends, so
those states remain available once the GPU has gone idle, and on
destroy, which unbind reaches even when suspending the GPU failed. The
limit is a per-GPU catalog value, downstream's for each SoC: 424 us
for the A508 in SDM630, and 518 us for the A509 in SDM636 and the A512
in SDM660.
Signed-off-by: Fabricio Akio <fabricioakio@gmail.com>
---
The values are downstream's CPU latency while the GPU is active, per
SoC:
https://github.com/LineageOS/android_kernel_motorola_msm8998/blob/ce9ea8f125c7302ae9b2fec84f23a69271e957c3/arch/arm/boot/dts/qcom/sdm630-gpu.dtsi#L117
https://github.com/LineageOS/android_kernel_motorola_msm8998/blob/ce9ea8f125c7302ae9b2fec84f23a69271e957c3/arch/arm/boot/dts/qcom/sdm660-gpu.dtsi#L126
SDM636 takes SDM660's by including sdm660.dtsi:
https://github.com/LineageOS/android_kernel_motorola_msm8998/blob/ce9ea8f125c7302ae9b2fec84f23a69271e957c3/arch/arm/boot/dts/qcom/sdm636.dtsi#L13
Downstream reads it at probe, applies it when the GPU starts and drops
it when the GPU slumbers:
https://github.com/LineageOS/android_kernel_motorola_msm8998/blob/ce9ea8f125c7302ae9b2fec84f23a69271e957c3/drivers/gpu/msm/adreno.c#L887
https://github.com/LineageOS/android_kernel_motorola_msm8998/blob/ce9ea8f125c7302ae9b2fec84f23a69271e957c3/drivers/gpu/msm/adreno.c#L1451
https://github.com/LineageOS/android_kernel_motorola_msm8998/blob/ce9ea8f125c7302ae9b2fec84f23a69271e957c3/drivers/gpu/msm/kgsl_pwrctrl.c#L2753
The A509 and A512 values are downstream's for SDM636 and SDM660;
tested on an A508 only.
---
drivers/gpu/drm/msm/adreno/a5xx_catalog.c | 3 +++
drivers/gpu/drm/msm/adreno/a5xx_gpu.c | 13 +++++++++++++
drivers/gpu/drm/msm/adreno/a5xx_gpu.h | 4 ++++
drivers/gpu/drm/msm/adreno/adreno_gpu.h | 5 +++++
4 files changed, 25 insertions(+)
diff --git a/drivers/gpu/drm/msm/adreno/a5xx_catalog.c b/drivers/gpu/drm/msm/adreno/a5xx_catalog.c
index babd320f3b73..7b261f045eae 100644
--- a/drivers/gpu/drm/msm/adreno/a5xx_catalog.c
+++ b/drivers/gpu/drm/msm/adreno/a5xx_catalog.c
@@ -55,6 +55,7 @@ static const struct adreno_info a5xx_gpus[] = {
* the GDSC which appears to make it grumpy
*/
.inactive_period = 250,
+ .cpu_latency_us = 424,
.quirks = ADRENO_QUIRK_LMLOADKILL_DISABLE,
.funcs = &a5xx_gpu_funcs,
.zapfw = "a508_zap.mdt",
@@ -72,6 +73,7 @@ static const struct adreno_info a5xx_gpus[] = {
* the GDSC which appears to make it grumpy
*/
.inactive_period = 250,
+ .cpu_latency_us = 518,
.quirks = ADRENO_QUIRK_LMLOADKILL_DISABLE,
.funcs = &a5xx_gpu_funcs,
/* Adreno 509 uses the same ZAP as 512 */
@@ -105,6 +107,7 @@ static const struct adreno_info a5xx_gpus[] = {
* the GDSC which appears to make it grumpy
*/
.inactive_period = 250,
+ .cpu_latency_us = 518,
.quirks = ADRENO_QUIRK_LMLOADKILL_DISABLE,
.funcs = &a5xx_gpu_funcs,
.zapfw = "a512_zap.mdt",
diff --git a/drivers/gpu/drm/msm/adreno/a5xx_gpu.c b/drivers/gpu/drm/msm/adreno/a5xx_gpu.c
index dd0d731df138..055b2bc9df2f 100644
--- a/drivers/gpu/drm/msm/adreno/a5xx_gpu.c
+++ b/drivers/gpu/drm/msm/adreno/a5xx_gpu.c
@@ -1060,6 +1060,10 @@ static void a5xx_destroy(struct msm_gpu *gpu)
drm_gem_object_put(a5xx_gpu->shadow_bo);
}
+ /* Still held if suspending the GPU failed before it was destroyed */
+ if (cpu_latency_qos_request_active(&a5xx_gpu->pm_qos_req))
+ cpu_latency_qos_remove_request(&a5xx_gpu->pm_qos_req);
+
adreno_gpu_cleanup(adreno_gpu);
kfree(a5xx_gpu);
}
@@ -1360,6 +1364,7 @@ static void a5xx_dump(struct msm_gpu *gpu)
static int a5xx_pm_resume(struct msm_gpu *gpu)
{
struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu);
+ struct a5xx_gpu *a5xx_gpu = to_a5xx_gpu(adreno_gpu);
int ret;
/* Turn on the core power */
@@ -1367,6 +1372,11 @@ static int a5xx_pm_resume(struct msm_gpu *gpu)
if (ret)
return ret;
+ if (adreno_gpu->info->cpu_latency_us &&
+ !cpu_latency_qos_request_active(&a5xx_gpu->pm_qos_req))
+ cpu_latency_qos_add_request(&a5xx_gpu->pm_qos_req,
+ adreno_gpu->info->cpu_latency_us);
+
/*
* The GX power domain is not necessarily switched off while we are
* suspended: on SDM630, SDM660 and MSM8998 it follows the GPU's SMMU
@@ -1447,6 +1457,9 @@ static int a5xx_pm_suspend(struct msm_gpu *gpu)
if (ret)
return ret;
+ if (cpu_latency_qos_request_active(&a5xx_gpu->pm_qos_req))
+ cpu_latency_qos_remove_request(&a5xx_gpu->pm_qos_req);
+
if (a5xx_gpu->has_whereami)
for (i = 0; i < gpu->nr_rings; i++)
a5xx_gpu->shadow[i] = 0;
diff --git a/drivers/gpu/drm/msm/adreno/a5xx_gpu.h b/drivers/gpu/drm/msm/adreno/a5xx_gpu.h
index fb05f1ca3e81..07a36c05be25 100644
--- a/drivers/gpu/drm/msm/adreno/a5xx_gpu.h
+++ b/drivers/gpu/drm/msm/adreno/a5xx_gpu.h
@@ -4,6 +4,8 @@
#ifndef __A5XX_GPU_H__
#define __A5XX_GPU_H__
+#include <linux/pm_qos.h>
+
#include "adreno_gpu.h"
/* Bringing over the hack from the previous targets */
@@ -48,6 +50,8 @@ struct a5xx_gpu {
/* True if the microcode supports the WHERE_AM_I opcode */
bool has_whereami;
+
+ struct pm_qos_request pm_qos_req;
};
#define to_a5xx_gpu(x) container_of(x, struct a5xx_gpu, base)
diff --git a/drivers/gpu/drm/msm/adreno/adreno_gpu.h b/drivers/gpu/drm/msm/adreno/adreno_gpu.h
index 4b2156825bdf..fcc382f4a9a1 100644
--- a/drivers/gpu/drm/msm/adreno/adreno_gpu.h
+++ b/drivers/gpu/drm/msm/adreno/adreno_gpu.h
@@ -118,6 +118,11 @@ struct adreno_info {
const struct adreno_gpu_funcs *funcs;
const char *zapfw;
u32 inactive_period;
+ /**
+ * @cpu_latency_us: CPU latency limit to hold while the GPU is
+ * powered, in microseconds, or 0 for none
+ */
+ u32 cpu_latency_us;
union {
const struct a6xx_info *a6xx;
};
--
2.56.0
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH 2/3] drm/msm/a5xx: Hold a CPU latency request while the GPU is powered
@ 2026-10-19 22:58 ` Fabricio Akio via B4 Relay
0 siblings, 0 replies; 9+ messages in thread
From: Fabricio Akio via B4 Relay @ 2026-10-19 22:58 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, Paul Sajna,
Fabricio Akio
From: Fabricio Akio <fabricioakio@gmail.com>
On SDM630 the GPU was observed to stop executing while the SoC is in
system-level power collapse, and to continue once the SoC wakes. Its
interrupts have no wakeup route through the MPM, so it cannot wake the
SoC itself. A submission overlapping such a state would stall until a
CPU wakes, and a stall longer than the hang detector's threshold makes
the driver recover the GPU, which can lose a frame.
Qualcomm's downstream driver holds a CPU latency request while the GPU
is active, qcom,pm-qos-active-latency, which keeps the CPU idle
governors out of cluster and system-level power collapse. Do the same:
take the request when the GPU resumes and drop it when it suspends, so
those states remain available once the GPU has gone idle, and on
destroy, which unbind reaches even when suspending the GPU failed. The
limit is a per-GPU catalog value, downstream's for each SoC: 424 us
for the A508 in SDM630, and 518 us for the A509 in SDM636 and the A512
in SDM660.
Signed-off-by: Fabricio Akio <fabricioakio@gmail.com>
---
The values are downstream's CPU latency while the GPU is active, per
SoC:
https://github.com/LineageOS/android_kernel_motorola_msm8998/blob/ce9ea8f125c7302ae9b2fec84f23a69271e957c3/arch/arm/boot/dts/qcom/sdm630-gpu.dtsi#L117
https://github.com/LineageOS/android_kernel_motorola_msm8998/blob/ce9ea8f125c7302ae9b2fec84f23a69271e957c3/arch/arm/boot/dts/qcom/sdm660-gpu.dtsi#L126
SDM636 takes SDM660's by including sdm660.dtsi:
https://github.com/LineageOS/android_kernel_motorola_msm8998/blob/ce9ea8f125c7302ae9b2fec84f23a69271e957c3/arch/arm/boot/dts/qcom/sdm636.dtsi#L13
Downstream reads it at probe, applies it when the GPU starts and drops
it when the GPU slumbers:
https://github.com/LineageOS/android_kernel_motorola_msm8998/blob/ce9ea8f125c7302ae9b2fec84f23a69271e957c3/drivers/gpu/msm/adreno.c#L887
https://github.com/LineageOS/android_kernel_motorola_msm8998/blob/ce9ea8f125c7302ae9b2fec84f23a69271e957c3/drivers/gpu/msm/adreno.c#L1451
https://github.com/LineageOS/android_kernel_motorola_msm8998/blob/ce9ea8f125c7302ae9b2fec84f23a69271e957c3/drivers/gpu/msm/kgsl_pwrctrl.c#L2753
The A509 and A512 values are downstream's for SDM636 and SDM660;
tested on an A508 only.
---
drivers/gpu/drm/msm/adreno/a5xx_catalog.c | 3 +++
drivers/gpu/drm/msm/adreno/a5xx_gpu.c | 13 +++++++++++++
drivers/gpu/drm/msm/adreno/a5xx_gpu.h | 4 ++++
drivers/gpu/drm/msm/adreno/adreno_gpu.h | 5 +++++
4 files changed, 25 insertions(+)
diff --git a/drivers/gpu/drm/msm/adreno/a5xx_catalog.c b/drivers/gpu/drm/msm/adreno/a5xx_catalog.c
index babd320f3b73..7b261f045eae 100644
--- a/drivers/gpu/drm/msm/adreno/a5xx_catalog.c
+++ b/drivers/gpu/drm/msm/adreno/a5xx_catalog.c
@@ -55,6 +55,7 @@ static const struct adreno_info a5xx_gpus[] = {
* the GDSC which appears to make it grumpy
*/
.inactive_period = 250,
+ .cpu_latency_us = 424,
.quirks = ADRENO_QUIRK_LMLOADKILL_DISABLE,
.funcs = &a5xx_gpu_funcs,
.zapfw = "a508_zap.mdt",
@@ -72,6 +73,7 @@ static const struct adreno_info a5xx_gpus[] = {
* the GDSC which appears to make it grumpy
*/
.inactive_period = 250,
+ .cpu_latency_us = 518,
.quirks = ADRENO_QUIRK_LMLOADKILL_DISABLE,
.funcs = &a5xx_gpu_funcs,
/* Adreno 509 uses the same ZAP as 512 */
@@ -105,6 +107,7 @@ static const struct adreno_info a5xx_gpus[] = {
* the GDSC which appears to make it grumpy
*/
.inactive_period = 250,
+ .cpu_latency_us = 518,
.quirks = ADRENO_QUIRK_LMLOADKILL_DISABLE,
.funcs = &a5xx_gpu_funcs,
.zapfw = "a512_zap.mdt",
diff --git a/drivers/gpu/drm/msm/adreno/a5xx_gpu.c b/drivers/gpu/drm/msm/adreno/a5xx_gpu.c
index dd0d731df138..055b2bc9df2f 100644
--- a/drivers/gpu/drm/msm/adreno/a5xx_gpu.c
+++ b/drivers/gpu/drm/msm/adreno/a5xx_gpu.c
@@ -1060,6 +1060,10 @@ static void a5xx_destroy(struct msm_gpu *gpu)
drm_gem_object_put(a5xx_gpu->shadow_bo);
}
+ /* Still held if suspending the GPU failed before it was destroyed */
+ if (cpu_latency_qos_request_active(&a5xx_gpu->pm_qos_req))
+ cpu_latency_qos_remove_request(&a5xx_gpu->pm_qos_req);
+
adreno_gpu_cleanup(adreno_gpu);
kfree(a5xx_gpu);
}
@@ -1360,6 +1364,7 @@ static void a5xx_dump(struct msm_gpu *gpu)
static int a5xx_pm_resume(struct msm_gpu *gpu)
{
struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu);
+ struct a5xx_gpu *a5xx_gpu = to_a5xx_gpu(adreno_gpu);
int ret;
/* Turn on the core power */
@@ -1367,6 +1372,11 @@ static int a5xx_pm_resume(struct msm_gpu *gpu)
if (ret)
return ret;
+ if (adreno_gpu->info->cpu_latency_us &&
+ !cpu_latency_qos_request_active(&a5xx_gpu->pm_qos_req))
+ cpu_latency_qos_add_request(&a5xx_gpu->pm_qos_req,
+ adreno_gpu->info->cpu_latency_us);
+
/*
* The GX power domain is not necessarily switched off while we are
* suspended: on SDM630, SDM660 and MSM8998 it follows the GPU's SMMU
@@ -1447,6 +1457,9 @@ static int a5xx_pm_suspend(struct msm_gpu *gpu)
if (ret)
return ret;
+ if (cpu_latency_qos_request_active(&a5xx_gpu->pm_qos_req))
+ cpu_latency_qos_remove_request(&a5xx_gpu->pm_qos_req);
+
if (a5xx_gpu->has_whereami)
for (i = 0; i < gpu->nr_rings; i++)
a5xx_gpu->shadow[i] = 0;
diff --git a/drivers/gpu/drm/msm/adreno/a5xx_gpu.h b/drivers/gpu/drm/msm/adreno/a5xx_gpu.h
index fb05f1ca3e81..07a36c05be25 100644
--- a/drivers/gpu/drm/msm/adreno/a5xx_gpu.h
+++ b/drivers/gpu/drm/msm/adreno/a5xx_gpu.h
@@ -4,6 +4,8 @@
#ifndef __A5XX_GPU_H__
#define __A5XX_GPU_H__
+#include <linux/pm_qos.h>
+
#include "adreno_gpu.h"
/* Bringing over the hack from the previous targets */
@@ -48,6 +50,8 @@ struct a5xx_gpu {
/* True if the microcode supports the WHERE_AM_I opcode */
bool has_whereami;
+
+ struct pm_qos_request pm_qos_req;
};
#define to_a5xx_gpu(x) container_of(x, struct a5xx_gpu, base)
diff --git a/drivers/gpu/drm/msm/adreno/adreno_gpu.h b/drivers/gpu/drm/msm/adreno/adreno_gpu.h
index 4b2156825bdf..fcc382f4a9a1 100644
--- a/drivers/gpu/drm/msm/adreno/adreno_gpu.h
+++ b/drivers/gpu/drm/msm/adreno/adreno_gpu.h
@@ -118,6 +118,11 @@ struct adreno_info {
const struct adreno_gpu_funcs *funcs;
const char *zapfw;
u32 inactive_period;
+ /**
+ * @cpu_latency_us: CPU latency limit to hold while the GPU is
+ * powered, in microseconds, or 0 for none
+ */
+ u32 cpu_latency_us;
union {
const struct a6xx_info *a6xx;
};
--
2.56.0
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH 3/3] drm/msm/a5xx: Use the downstream GPU idle timeout on A508, A509 and A512
2026-10-19 22:58 ` Fabricio Akio via B4 Relay
@ 2026-10-19 22:58 ` Fabricio Akio via B4 Relay
-1 siblings, 0 replies; 9+ messages in thread
From: Fabricio Akio @ 2026-10-19 22:58 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, Paul Sajna,
Fabricio Akio
The A508, A509 and A512 took the 250 ms runtime-PM autosuspend delay
of the A530, raised there because quickly cycling that GPU's power
hung the system. Qualcomm's downstream driver powers these GPUs down
after 80 ms idle, qcom,idle-timeout, on SDM630, SDM636 and SDM660
alike.
Use 80 ms for these three, so the GPU powers down, and releases its
CPU latency request, sooner after its last work.
Signed-off-by: Fabricio Akio <fabricioakio@gmail.com>
---
The value is downstream's idle timeout for these SoCs:
https://github.com/LineageOS/android_kernel_motorola_msm8998/blob/ce9ea8f125c7302ae9b2fec84f23a69271e957c3/arch/arm/boot/dts/qcom/sdm630-gpu.dtsi#L71
https://github.com/LineageOS/android_kernel_motorola_msm8998/blob/ce9ea8f125c7302ae9b2fec84f23a69271e957c3/arch/arm/boot/dts/qcom/sdm660-gpu.dtsi#L73
SDM636 includes sdm660.dtsi:
https://github.com/LineageOS/android_kernel_motorola_msm8998/blob/ce9ea8f125c7302ae9b2fec84f23a69271e957c3/arch/arm/boot/dts/qcom/sdm636.dtsi#L13
Downstream reads it at probe, restarts the idle timer when the last
work completes, and slumbers the GPU when it fires, dropping its CPU
latency request:
https://github.com/LineageOS/android_kernel_motorola_msm8998/blob/ce9ea8f125c7302ae9b2fec84f23a69271e957c3/drivers/gpu/msm/adreno.c#L901
https://github.com/LineageOS/android_kernel_motorola_msm8998/blob/ce9ea8f125c7302ae9b2fec84f23a69271e957c3/drivers/gpu/msm/kgsl_pwrctrl.c#L2981
https://github.com/LineageOS/android_kernel_motorola_msm8998/blob/ce9ea8f125c7302ae9b2fec84f23a69271e957c3/drivers/gpu/msm/kgsl_pwrctrl.c#L2467
https://github.com/LineageOS/android_kernel_motorola_msm8998/blob/ce9ea8f125c7302ae9b2fec84f23a69271e957c3/drivers/gpu/msm/kgsl_pwrctrl.c#L2753
Tested on an A508 with the previous patch: 1381 GPU power cycles at
gaps from 50 ms to 3 s without a fault. The A509 and A512 values are
downstream's for SDM636 and SDM660; tested on an A508 only.
---
drivers/gpu/drm/msm/adreno/a5xx_catalog.c | 18 +++---------------
1 file changed, 3 insertions(+), 15 deletions(-)
diff --git a/drivers/gpu/drm/msm/adreno/a5xx_catalog.c b/drivers/gpu/drm/msm/adreno/a5xx_catalog.c
index 7b261f045eae..254bae06af39 100644
--- a/drivers/gpu/drm/msm/adreno/a5xx_catalog.c
+++ b/drivers/gpu/drm/msm/adreno/a5xx_catalog.c
@@ -50,11 +50,7 @@ static const struct adreno_info a5xx_gpus[] = {
[ADRENO_FW_PFP] = "a530_pfp.fw",
},
.gmem = (SZ_128K + SZ_8K),
- /*
- * Increase inactive period to 250 to avoid bouncing
- * the GDSC which appears to make it grumpy
- */
- .inactive_period = 250,
+ .inactive_period = 80,
.cpu_latency_us = 424,
.quirks = ADRENO_QUIRK_LMLOADKILL_DISABLE,
.funcs = &a5xx_gpu_funcs,
@@ -68,11 +64,7 @@ static const struct adreno_info a5xx_gpus[] = {
[ADRENO_FW_PFP] = "a530_pfp.fw",
},
.gmem = (SZ_256K + SZ_16K),
- /*
- * Increase inactive period to 250 to avoid bouncing
- * the GDSC which appears to make it grumpy
- */
- .inactive_period = 250,
+ .inactive_period = 80,
.cpu_latency_us = 518,
.quirks = ADRENO_QUIRK_LMLOADKILL_DISABLE,
.funcs = &a5xx_gpu_funcs,
@@ -102,11 +94,7 @@ static const struct adreno_info a5xx_gpus[] = {
[ADRENO_FW_PFP] = "a530_pfp.fw",
},
.gmem = (SZ_256K + SZ_16K),
- /*
- * Increase inactive period to 250 to avoid bouncing
- * the GDSC which appears to make it grumpy
- */
- .inactive_period = 250,
+ .inactive_period = 80,
.cpu_latency_us = 518,
.quirks = ADRENO_QUIRK_LMLOADKILL_DISABLE,
.funcs = &a5xx_gpu_funcs,
--
2.56.0
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH 3/3] drm/msm/a5xx: Use the downstream GPU idle timeout on A508, A509 and A512
@ 2026-10-19 22:58 ` Fabricio Akio via B4 Relay
0 siblings, 0 replies; 9+ messages in thread
From: Fabricio Akio via B4 Relay @ 2026-10-19 22:58 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, Paul Sajna,
Fabricio Akio
From: Fabricio Akio <fabricioakio@gmail.com>
The A508, A509 and A512 took the 250 ms runtime-PM autosuspend delay
of the A530, raised there because quickly cycling that GPU's power
hung the system. Qualcomm's downstream driver powers these GPUs down
after 80 ms idle, qcom,idle-timeout, on SDM630, SDM636 and SDM660
alike.
Use 80 ms for these three, so the GPU powers down, and releases its
CPU latency request, sooner after its last work.
Signed-off-by: Fabricio Akio <fabricioakio@gmail.com>
---
The value is downstream's idle timeout for these SoCs:
https://github.com/LineageOS/android_kernel_motorola_msm8998/blob/ce9ea8f125c7302ae9b2fec84f23a69271e957c3/arch/arm/boot/dts/qcom/sdm630-gpu.dtsi#L71
https://github.com/LineageOS/android_kernel_motorola_msm8998/blob/ce9ea8f125c7302ae9b2fec84f23a69271e957c3/arch/arm/boot/dts/qcom/sdm660-gpu.dtsi#L73
SDM636 includes sdm660.dtsi:
https://github.com/LineageOS/android_kernel_motorola_msm8998/blob/ce9ea8f125c7302ae9b2fec84f23a69271e957c3/arch/arm/boot/dts/qcom/sdm636.dtsi#L13
Downstream reads it at probe, restarts the idle timer when the last
work completes, and slumbers the GPU when it fires, dropping its CPU
latency request:
https://github.com/LineageOS/android_kernel_motorola_msm8998/blob/ce9ea8f125c7302ae9b2fec84f23a69271e957c3/drivers/gpu/msm/adreno.c#L901
https://github.com/LineageOS/android_kernel_motorola_msm8998/blob/ce9ea8f125c7302ae9b2fec84f23a69271e957c3/drivers/gpu/msm/kgsl_pwrctrl.c#L2981
https://github.com/LineageOS/android_kernel_motorola_msm8998/blob/ce9ea8f125c7302ae9b2fec84f23a69271e957c3/drivers/gpu/msm/kgsl_pwrctrl.c#L2467
https://github.com/LineageOS/android_kernel_motorola_msm8998/blob/ce9ea8f125c7302ae9b2fec84f23a69271e957c3/drivers/gpu/msm/kgsl_pwrctrl.c#L2753
Tested on an A508 with the previous patch: 1381 GPU power cycles at
gaps from 50 ms to 3 s without a fault. The A509 and A512 values are
downstream's for SDM636 and SDM660; tested on an A508 only.
---
drivers/gpu/drm/msm/adreno/a5xx_catalog.c | 18 +++---------------
1 file changed, 3 insertions(+), 15 deletions(-)
diff --git a/drivers/gpu/drm/msm/adreno/a5xx_catalog.c b/drivers/gpu/drm/msm/adreno/a5xx_catalog.c
index 7b261f045eae..254bae06af39 100644
--- a/drivers/gpu/drm/msm/adreno/a5xx_catalog.c
+++ b/drivers/gpu/drm/msm/adreno/a5xx_catalog.c
@@ -50,11 +50,7 @@ static const struct adreno_info a5xx_gpus[] = {
[ADRENO_FW_PFP] = "a530_pfp.fw",
},
.gmem = (SZ_128K + SZ_8K),
- /*
- * Increase inactive period to 250 to avoid bouncing
- * the GDSC which appears to make it grumpy
- */
- .inactive_period = 250,
+ .inactive_period = 80,
.cpu_latency_us = 424,
.quirks = ADRENO_QUIRK_LMLOADKILL_DISABLE,
.funcs = &a5xx_gpu_funcs,
@@ -68,11 +64,7 @@ static const struct adreno_info a5xx_gpus[] = {
[ADRENO_FW_PFP] = "a530_pfp.fw",
},
.gmem = (SZ_256K + SZ_16K),
- /*
- * Increase inactive period to 250 to avoid bouncing
- * the GDSC which appears to make it grumpy
- */
- .inactive_period = 250,
+ .inactive_period = 80,
.cpu_latency_us = 518,
.quirks = ADRENO_QUIRK_LMLOADKILL_DISABLE,
.funcs = &a5xx_gpu_funcs,
@@ -102,11 +94,7 @@ static const struct adreno_info a5xx_gpus[] = {
[ADRENO_FW_PFP] = "a530_pfp.fw",
},
.gmem = (SZ_256K + SZ_16K),
- /*
- * Increase inactive period to 250 to avoid bouncing
- * the GDSC which appears to make it grumpy
- */
- .inactive_period = 250,
+ .inactive_period = 80,
.cpu_latency_us = 518,
.quirks = ADRENO_QUIRK_LMLOADKILL_DISABLE,
.funcs = &a5xx_gpu_funcs,
--
2.56.0
^ permalink raw reply related [flat|nested] 9+ messages in thread
* Re: [PATCH 1/3] drm/msm/a5xx: Reset the GPU when resuming it
2026-10-19 22:58 ` Fabricio Akio via B4 Relay
(?)
@ 2026-10-19 23:15 ` sashiko-bot
-1 siblings, 0 replies; 9+ messages in thread
From: sashiko-bot @ 2026-10-19 23:15 UTC (permalink / raw)
To: Fabricio Akio; +Cc: dri-devel
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] drm/msm/a5xx: soft reset in a5xx_pm_resume() underflows devfreq busy_cycles
--
commit 19124e467b5360709bf4e69d8b941ff7d1098156
Author: Fabricio Akio <fabricioakio@gmail.com>
drm/msm/a5xx: Reset the GPU when resuming it
This patch adds a soft reset via REG_A5XX_RBBM_SW_RESET_CMD in
a5xx_pm_resume() so that the GPU always resumes from a reset state even
when the GX power domain remained on during suspend.
> diff --git a/drivers/gpu/drm/msm/adreno/a5xx_gpu.c b/drivers/gpu/drm/msm/adreno/a5xx_gpu.c
> index 16c7335c4b1f8..dd0d731df138c 100644
> --- a/drivers/gpu/drm/msm/adreno/a5xx_gpu.c
> +++ b/drivers/gpu/drm/msm/adreno/a5xx_gpu.c
> @@ -1367,6 +1367,18 @@ static int a5xx_pm_resume(struct msm_gpu *gpu)
> if (ret)
> return ret;
>
> + /*
> + * The GX power domain is not necessarily switched off while we are
> + * suspended: on SDM630, SDM660 and MSM8998 it follows the GPU's SMMU
> + * rather than the GPU. If it stayed on, the CP still holds its old
> + * ringbuffer read pointer and would execute stale ringbuffer contents
> + * once hw_init restarts it. Soft reset the GPU so that it always
> + * resumes from its reset state.
> + */
> + gpu_write(gpu, REG_A5XX_RBBM_SW_RESET_CMD, 1);
> + gpu_read(gpu, REG_A5XX_RBBM_SW_RESET_CMD);
> + gpu_write(gpu, REG_A5XX_RBBM_SW_RESET_CMD, 0);
[Severity: Medium]
Can pulsing REG_A5XX_RBBM_SW_RESET_CMD after msm_gpu_pm_resume() underflow
df->busy_cycles in msm_devfreq_get_dev_status() when the GX power domain
remains powered across suspend?
Earlier in a5xx_pm_resume(), msm_gpu_pm_resume() runs and calls
msm_devfreq_resume() -> a5xx_gpu_busy():
drivers/gpu/drm/msm/msm_gpu_devfreq.c:msm_devfreq_resume() {
...
mutex_lock(&df->lock);
df->busy_cycles = gpu->funcs->gpu_busy(gpu, &sample_rate);
df->time = ktime_get();
df->suspended = false;
mutex_unlock(&df->lock);
devfreq_resume_device(df->devfreq);
}
drivers/gpu/drm/msm/adreno/a5xx_gpu.c:a5xx_gpu_busy() {
...
busy_cycles = gpu_read64(gpu, REG_A5XX_RBBM_PERFCTR_RBBM_0_LO);
*out_sample_rate = clk_get_rate(gpu->core_clk);
return busy_cycles;
}
When GX stays on during suspend (such as on SDM630, SDM660, and MSM8998),
REG_A5XX_RBBM_PERFCTR_RBBM_0_LO retains its non-zero pre-suspend counter
value when msm_devfreq_resume() samples it into df->busy_cycles.
Immediately after msm_gpu_pm_resume() returns, a5xx_pm_resume() writes
REG_A5XX_RBBM_SW_RESET_CMD, which resets the RBBM registers including
REG_A5XX_RBBM_PERFCTR_RBBM_0_LO back to 0 while devfreq polling is already
active.
On the next devfreq poll in msm_devfreq_get_dev_status():
drivers/gpu/drm/msm/msm_gpu_devfreq.c:msm_devfreq_get_dev_status() {
...
busy_cycles = gpu->funcs->gpu_busy(gpu, &sample_rate);
busy_time = busy_cycles - df->busy_cycles;
df->busy_cycles = busy_cycles;
mutex_unlock(&df->lock);
busy_time *= USEC_PER_SEC;
busy_time = div64_ul(busy_time, sample_rate);
if (WARN_ON(busy_time > ~0LU))
busy_time = ~0LU;
...
}
Wouldn't busy_cycles - df->busy_cycles underflow the 64-bit unsigned value,
causing a bogus maximum busy_time spike (and triggering the WARN_ON on
32-bit builds)?
> +
> /* Adreno 505, 506, 508, 509, 510, 512 needs manual RBBM sus/res control */
> if (!(adreno_is_a530(adreno_gpu) || adreno_is_a540(adreno_gpu))) {
> /* Halt the sp_input_clk at HM level */
[ ... ]
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261019-a5xx-gpu-pm-v1-0-1d0c313a1412@gmail.com?part=1
^ permalink raw reply [flat|nested] 9+ messages in thread
end of thread, other threads:[~2026-10-19 23:15 UTC | newest]
Thread overview: 9+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-19 22:58 [PATCH 0/3] drm/msm/a5xx: Fix GPU stalls on A508, A509 and A512 Fabricio Akio
2026-10-19 22:58 ` Fabricio Akio via B4 Relay
2026-10-19 22:58 ` [PATCH 1/3] drm/msm/a5xx: Reset the GPU when resuming it Fabricio Akio
2026-10-19 22:58 ` Fabricio Akio via B4 Relay
2026-10-19 23:15 ` sashiko-bot
2026-10-19 22:58 ` [PATCH 2/3] drm/msm/a5xx: Hold a CPU latency request while the GPU is powered Fabricio Akio
2026-10-19 22:58 ` Fabricio Akio via B4 Relay
2026-10-19 22:58 ` [PATCH 3/3] drm/msm/a5xx: Use the downstream GPU idle timeout on A508, A509 and A512 Fabricio Akio
2026-10-19 22:58 ` Fabricio Akio via B4 Relay
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.