* [PATCH v2 1/3] drm/msm/a5xx: Reset the GPU when resuming it
2026-10-20 2:05 [PATCH v2 0/3] drm/msm/a5xx: Fix GPU stalls on A508, A509 and A512 Fabricio Akio via B4 Relay
@ 2026-10-20 2:05 ` Fabricio Akio via B4 Relay
2026-10-20 6:19 ` Konrad Dybcio
` (3 more replies)
2026-10-20 2:05 ` [PATCH v2 2/3] drm/msm/a5xx: Hold a CPU latency request while the GPU is powered Fabricio Akio via B4 Relay
2026-10-20 2:05 ` [PATCH v2 3/3] drm/msm/a5xx: Use the downstream GPU idle timeout on A508, A509 and A512 Fabricio Akio via B4 Relay
2 siblings, 4 replies; 10+ messages in thread
From: Fabricio Akio via B4 Relay @ 2026-10-20 2:05 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.
The reset also clears the busy counter, which msm_gpu_pm_resume() has
just sampled as devfreq's baseline. Take the baseline again after the
reset, or the next devfreq poll subtracts the old count from the new
one, wraps, and sets the GPU to its top frequency.
Signed-off-by: Fabricio Akio <fabricioakio@gmail.com>
---
Changes in v2:
- Take devfreq's busy-cycle baseline again after the reset, which
clears the busy counter (Sashiko)
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 | 15 +++++++++++++++
drivers/gpu/drm/msm/msm_gpu.h | 1 +
drivers/gpu/drm/msm/msm_gpu_devfreq.c | 17 +++++++++++++++++
3 files changed, 33 insertions(+)
diff --git a/drivers/gpu/drm/msm/adreno/a5xx_gpu.c b/drivers/gpu/drm/msm/adreno/a5xx_gpu.c
index 16c7335c4b1f..0aff8252a00f 100644
--- a/drivers/gpu/drm/msm/adreno/a5xx_gpu.c
+++ b/drivers/gpu/drm/msm/adreno/a5xx_gpu.c
@@ -1367,6 +1367,21 @@ 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);
+
+ /* The reset cleared the busy counter devfreq has just sampled */
+ msm_devfreq_resample_busy(gpu);
+
/* 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 */
diff --git a/drivers/gpu/drm/msm/msm_gpu.h b/drivers/gpu/drm/msm/msm_gpu.h
index d27d54bdb7a7..0e0768c15bcc 100644
--- a/drivers/gpu/drm/msm/msm_gpu.h
+++ b/drivers/gpu/drm/msm/msm_gpu.h
@@ -735,6 +735,7 @@ static inline struct msm_context *msm_context_get(
void msm_devfreq_init(struct msm_gpu *gpu);
void msm_devfreq_cleanup(struct msm_gpu *gpu);
void msm_devfreq_resume(struct msm_gpu *gpu);
+void msm_devfreq_resample_busy(struct msm_gpu *gpu);
void msm_devfreq_suspend(struct msm_gpu *gpu);
void msm_devfreq_boost(struct msm_gpu *gpu, unsigned factor);
void msm_devfreq_active(struct msm_gpu *gpu);
diff --git a/drivers/gpu/drm/msm/msm_gpu_devfreq.c b/drivers/gpu/drm/msm/msm_gpu_devfreq.c
index 2e1d5c343272..2e963bff3f61 100644
--- a/drivers/gpu/drm/msm/msm_gpu_devfreq.c
+++ b/drivers/gpu/drm/msm/msm_gpu_devfreq.c
@@ -242,6 +242,23 @@ void msm_devfreq_resume(struct msm_gpu *gpu)
devfreq_resume_device(df->devfreq);
}
+/*
+ * Take a new busy cycles baseline, for a GPU that resets its busy counter
+ * after msm_devfreq_resume() has sampled it.
+ */
+void msm_devfreq_resample_busy(struct msm_gpu *gpu)
+{
+ struct msm_gpu_devfreq *df = &gpu->devfreq;
+ unsigned long sample_rate;
+
+ if (!has_devfreq(gpu))
+ return;
+
+ mutex_lock(&df->lock);
+ df->busy_cycles = gpu->funcs->gpu_busy(gpu, &sample_rate);
+ mutex_unlock(&df->lock);
+}
+
void msm_devfreq_suspend(struct msm_gpu *gpu)
{
struct msm_gpu_devfreq *df = &gpu->devfreq;
--
2.56.0
^ permalink raw reply [flat|nested] 10+ messages in thread* Re: [PATCH v2 1/3] drm/msm/a5xx: Reset the GPU when resuming it
2026-10-20 2:05 ` [PATCH v2 1/3] drm/msm/a5xx: Reset the GPU when resuming it Fabricio Akio via B4 Relay
@ 2026-10-20 6:19 ` Konrad Dybcio
2026-10-21 16:59 ` Dmitry Baryshkov
` (2 subsequent siblings)
3 siblings, 0 replies; 10+ messages in thread
From: Konrad Dybcio @ 2026-10-20 6:19 UTC (permalink / raw)
To: fabricioakio, 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
On 10/20/26 4:05 AM, Fabricio Akio via B4 Relay wrote:
> 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.
This is because the DT part is a huge slop (artisinal free-range
grass-fed slop from before AI times which I partook in.. sorry!).
The ultimate fix would be to land:
https://lore.kernel.org/linux-arm-msm/20261014-topic-qc_clk_dvfs-v1-0-3041b788e9f7@oss.qualcomm.com/
on other SoCs we scrape by with overzealously assigning various
power domains, as all of the power requirements are managed by
RPM and other pieces of firmware. On msm8996/msm8998/sdm630/660,
that requires a long chain of HLOS-programmed values:
- VDDCX/MX from RPM
- GFX_LDO
- MEM_ACC-GFX
- CPR3/H-GFX
- GPMU on the bigger SoCs
I believe Dmitry is working on getting most of that in order and
should have something to show in the near future.
Konrad
^ permalink raw reply [flat|nested] 10+ messages in thread* Re: [PATCH v2 1/3] drm/msm/a5xx: Reset the GPU when resuming it
2026-10-20 2:05 ` [PATCH v2 1/3] drm/msm/a5xx: Reset the GPU when resuming it Fabricio Akio via B4 Relay
2026-10-20 6:19 ` Konrad Dybcio
@ 2026-10-21 16:59 ` Dmitry Baryshkov
2026-10-21 17:02 ` Dmitry Baryshkov
2026-10-22 21:59 ` Akhil P Oommen
3 siblings, 0 replies; 10+ messages in thread
From: Dmitry Baryshkov @ 2026-10-21 16:59 UTC (permalink / raw)
To: Fabricio Akio
Cc: Rob Clark, Sean Paul, Konrad Dybcio, Akhil P Oommen,
Dmitry Baryshkov, Abhinav Kumar, Jessica Zhang, Marijn Suijten,
David Airlie, Simona Vetter, linux-arm-msm, dri-devel, freedreno,
linux-kernel, Paul Sajna
On Mon, Oct 19, 2026 at 11:05:08PM -0300, Fabricio Akio wrote:
> 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.
>
> The reset also clears the busy counter, which msm_gpu_pm_resume() has
> just sampled as devfreq's baseline. Take the baseline again after the
> reset, or the next devfreq poll subtracts the old count from the new
> one, wraps, and sets the GPU to its top frequency.
>
> Signed-off-by: Fabricio Akio <fabricioakio@gmail.com>
> ---
> Changes in v2:
> - Take devfreq's busy-cycle baseline again after the reset, which
> clears the busy counter (Sashiko)
>
> 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 | 15 +++++++++++++++
> drivers/gpu/drm/msm/msm_gpu.h | 1 +
> drivers/gpu/drm/msm/msm_gpu_devfreq.c | 17 +++++++++++++++++
> 3 files changed, 33 insertions(+)
>
This matches the issue I had on MSM8998. For now it's:
Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
I'll update this with the T-B once the board is free from the previous
set of tests.
--
With best wishes
Dmitry
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH v2 1/3] drm/msm/a5xx: Reset the GPU when resuming it
2026-10-20 2:05 ` [PATCH v2 1/3] drm/msm/a5xx: Reset the GPU when resuming it Fabricio Akio via B4 Relay
2026-10-20 6:19 ` Konrad Dybcio
2026-10-21 16:59 ` Dmitry Baryshkov
@ 2026-10-21 17:02 ` Dmitry Baryshkov
2026-10-22 21:59 ` Akhil P Oommen
3 siblings, 0 replies; 10+ messages in thread
From: Dmitry Baryshkov @ 2026-10-21 17:02 UTC (permalink / raw)
To: Fabricio Akio
Cc: Rob Clark, Sean Paul, Konrad Dybcio, Akhil P Oommen,
Dmitry Baryshkov, Abhinav Kumar, Jessica Zhang, Marijn Suijten,
David Airlie, Simona Vetter, linux-arm-msm, dri-devel, freedreno,
linux-kernel, Paul Sajna
On Mon, Oct 19, 2026 at 11:05:08PM -0300, Fabricio Akio wrote:
> 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.
>
> The reset also clears the busy counter, which msm_gpu_pm_resume() has
> just sampled as devfreq's baseline. Take the baseline again after the
> reset, or the next devfreq poll subtracts the old count from the new
> one, wraps, and sets the GPU to its top frequency.
>
> Signed-off-by: Fabricio Akio <fabricioakio@gmail.com>
> ---
> Changes in v2:
> - Take devfreq's busy-cycle baseline again after the reset, which
> clears the busy counter (Sashiko)
>
> 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 | 15 +++++++++++++++
> drivers/gpu/drm/msm/msm_gpu.h | 1 +
> drivers/gpu/drm/msm/msm_gpu_devfreq.c | 17 +++++++++++++++++
> 3 files changed, 33 insertions(+)
>
Tested-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
--
With best wishes
Dmitry
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH v2 1/3] drm/msm/a5xx: Reset the GPU when resuming it
2026-10-20 2:05 ` [PATCH v2 1/3] drm/msm/a5xx: Reset the GPU when resuming it Fabricio Akio via B4 Relay
` (2 preceding siblings ...)
2026-10-21 17:02 ` Dmitry Baryshkov
@ 2026-10-22 21:59 ` Akhil P Oommen
3 siblings, 0 replies; 10+ messages in thread
From: Akhil P Oommen @ 2026-10-22 21:59 UTC (permalink / raw)
To: fabricioakio
Cc: linux-arm-msm, dri-devel, freedreno, linux-kernel, Paul Sajna,
Rob Clark, Sean Paul, Konrad Dybcio, Dmitry Baryshkov,
Abhinav Kumar, Jessica Zhang, Marijn Suijten, David Airlie,
Simona Vetter
On 10/20/2026 7:35 AM, Fabricio Akio via B4 Relay wrote:
> 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
This is incorrect. There is no Adreno GPU where SMMU is powered by GX. I looked
at the DT of SDM630 and I don't understand it. Seems like a creative workaround
to vote different power domains.
Please reword GX -> CX here. Adreno SMMU is powered by CX GDSC in every chipset.
In some chipsets, the core GPU blocks are also powered via CX GDSC. So it
makes sense to do a SW RESET in case CX didn't collapse due to a vote from the
SMMU driver or something.
> 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.
>
> The reset also clears the busy counter, which msm_gpu_pm_resume() has
> just sampled as devfreq's baseline. Take the baseline again after the
> reset, or the next devfreq poll subtracts the old count from the new
> one, wraps, and sets the GPU to its top frequency.
>
> Signed-off-by: Fabricio Akio <fabricioakio@gmail.com>
> ---
> Changes in v2:
> - Take devfreq's busy-cycle baseline again after the reset, which
> clears the busy counter (Sashiko)
>
> 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 | 15 +++++++++++++++
> drivers/gpu/drm/msm/msm_gpu.h | 1 +
> drivers/gpu/drm/msm/msm_gpu_devfreq.c | 17 +++++++++++++++++
> 3 files changed, 33 insertions(+)
>
> diff --git a/drivers/gpu/drm/msm/adreno/a5xx_gpu.c b/drivers/gpu/drm/msm/adreno/a5xx_gpu.c
> index 16c7335c4b1f..0aff8252a00f 100644
> --- a/drivers/gpu/drm/msm/adreno/a5xx_gpu.c
> +++ b/drivers/gpu/drm/msm/adreno/a5xx_gpu.c
> @@ -1367,6 +1367,21 @@ 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);
> +
> + /* The reset cleared the busy counter devfreq has just sampled */
> + msm_devfreq_resample_busy(gpu);
Instead of a new API, we can just move msm_devfreq_resume() here. That will align
with the A6xx sequence.
-Akhil.
> +
> /* 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 */
> diff --git a/drivers/gpu/drm/msm/msm_gpu.h b/drivers/gpu/drm/msm/msm_gpu.h
> index d27d54bdb7a7..0e0768c15bcc 100644
> --- a/drivers/gpu/drm/msm/msm_gpu.h
> +++ b/drivers/gpu/drm/msm/msm_gpu.h
> @@ -735,6 +735,7 @@ static inline struct msm_context *msm_context_get(
> void msm_devfreq_init(struct msm_gpu *gpu);
> void msm_devfreq_cleanup(struct msm_gpu *gpu);
> void msm_devfreq_resume(struct msm_gpu *gpu);
> +void msm_devfreq_resample_busy(struct msm_gpu *gpu);
> void msm_devfreq_suspend(struct msm_gpu *gpu);
> void msm_devfreq_boost(struct msm_gpu *gpu, unsigned factor);
> void msm_devfreq_active(struct msm_gpu *gpu);
> diff --git a/drivers/gpu/drm/msm/msm_gpu_devfreq.c b/drivers/gpu/drm/msm/msm_gpu_devfreq.c
> index 2e1d5c343272..2e963bff3f61 100644
> --- a/drivers/gpu/drm/msm/msm_gpu_devfreq.c
> +++ b/drivers/gpu/drm/msm/msm_gpu_devfreq.c
> @@ -242,6 +242,23 @@ void msm_devfreq_resume(struct msm_gpu *gpu)
> devfreq_resume_device(df->devfreq);
> }
>
> +/*
> + * Take a new busy cycles baseline, for a GPU that resets its busy counter
> + * after msm_devfreq_resume() has sampled it.
> + */
> +void msm_devfreq_resample_busy(struct msm_gpu *gpu)
> +{
> + struct msm_gpu_devfreq *df = &gpu->devfreq;
> + unsigned long sample_rate;
> +
> + if (!has_devfreq(gpu))
> + return;
> +
> + mutex_lock(&df->lock);
> + df->busy_cycles = gpu->funcs->gpu_busy(gpu, &sample_rate);
> + mutex_unlock(&df->lock);
> +}
> +
> void msm_devfreq_suspend(struct msm_gpu *gpu)
> {
> struct msm_gpu_devfreq *df = &gpu->devfreq;
>
^ permalink raw reply [flat|nested] 10+ messages in thread
* [PATCH v2 2/3] drm/msm/a5xx: Hold a CPU latency request while the GPU is powered
2026-10-20 2:05 [PATCH v2 0/3] drm/msm/a5xx: Fix GPU stalls on A508, A509 and A512 Fabricio Akio via B4 Relay
2026-10-20 2:05 ` [PATCH v2 1/3] drm/msm/a5xx: Reset the GPU when resuming it Fabricio Akio via B4 Relay
@ 2026-10-20 2:05 ` Fabricio Akio via B4 Relay
2026-10-21 17:42 ` Dmitry Baryshkov
2026-10-20 2:05 ` [PATCH v2 3/3] drm/msm/a5xx: Use the downstream GPU idle timeout on A508, A509 and A512 Fabricio Akio via B4 Relay
2 siblings, 1 reply; 10+ messages in thread
From: Fabricio Akio via B4 Relay @ 2026-10-20 2:05 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 0aff8252a00f..1d4993f8cb7c 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
@@ -1450,6 +1460,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 [flat|nested] 10+ messages in thread* Re: [PATCH v2 2/3] drm/msm/a5xx: Hold a CPU latency request while the GPU is powered
2026-10-20 2:05 ` [PATCH v2 2/3] drm/msm/a5xx: Hold a CPU latency request while the GPU is powered Fabricio Akio via B4 Relay
@ 2026-10-21 17:42 ` Dmitry Baryshkov
0 siblings, 0 replies; 10+ messages in thread
From: Dmitry Baryshkov @ 2026-10-21 17:42 UTC (permalink / raw)
To: Fabricio Akio
Cc: Rob Clark, Sean Paul, Konrad Dybcio, Akhil P Oommen,
Dmitry Baryshkov, Abhinav Kumar, Jessica Zhang, Marijn Suijten,
David Airlie, Simona Vetter, linux-arm-msm, dri-devel, freedreno,
linux-kernel, Paul Sajna
On Mon, Oct 19, 2026 at 11:05:09PM -0300, Fabricio Akio wrote:
> 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.
Do you have any extra patches that you are running with? I don't think
that currently Linux can hit system power collapse with the GPU still
running.
On SDM630 the GPU is running from the CX, but we don't vote on it (yet).
With the CPUs awake we keep a vote on CX. With the cores going to the
deep sleep, the CX might drop below the required leven, causing GPU to
die.
> 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:
Could you please check what actually stops in your setup (CX corner, the
BIMC vote, XO)? I think the proper fix is for the GPU to vote its real
rail, scaled per OPP, in both RPM sets, rather than keeping the CPUs out
of their idle states. I'll have it as a part of the CPR series, but it
needs a bit more of the cleanup and polishing.
> 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>
--
With best wishes
Dmitry
^ permalink raw reply [flat|nested] 10+ messages in thread
* [PATCH v2 3/3] drm/msm/a5xx: Use the downstream GPU idle timeout on A508, A509 and A512
2026-10-20 2:05 [PATCH v2 0/3] drm/msm/a5xx: Fix GPU stalls on A508, A509 and A512 Fabricio Akio via B4 Relay
2026-10-20 2:05 ` [PATCH v2 1/3] drm/msm/a5xx: Reset the GPU when resuming it Fabricio Akio via B4 Relay
2026-10-20 2:05 ` [PATCH v2 2/3] drm/msm/a5xx: Hold a CPU latency request while the GPU is powered Fabricio Akio via B4 Relay
@ 2026-10-20 2:05 ` Fabricio Akio via B4 Relay
2026-10-21 17:42 ` Dmitry Baryshkov
2 siblings, 1 reply; 10+ messages in thread
From: Fabricio Akio via B4 Relay @ 2026-10-20 2:05 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 [flat|nested] 10+ messages in thread* Re: [PATCH v2 3/3] drm/msm/a5xx: Use the downstream GPU idle timeout on A508, A509 and A512
2026-10-20 2:05 ` [PATCH v2 3/3] drm/msm/a5xx: Use the downstream GPU idle timeout on A508, A509 and A512 Fabricio Akio via B4 Relay
@ 2026-10-21 17:42 ` Dmitry Baryshkov
0 siblings, 0 replies; 10+ messages in thread
From: Dmitry Baryshkov @ 2026-10-21 17:42 UTC (permalink / raw)
To: Fabricio Akio
Cc: Rob Clark, Sean Paul, Konrad Dybcio, Akhil P Oommen,
Dmitry Baryshkov, Abhinav Kumar, Jessica Zhang, Marijn Suijten,
David Airlie, Simona Vetter, linux-arm-msm, dri-devel, freedreno,
linux-kernel, Paul Sajna
On Mon, Oct 19, 2026 at 11:05:10PM -0300, Fabricio Akio wrote:
> 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(-)
>
Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
Tested-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
--
With best wishes
Dmitry
^ permalink raw reply [flat|nested] 10+ messages in thread