* [PATCH 1/8] drm/msm/a6xx: Polish up the IFPC counter enable
2026-10-30 10:03 [PATCH 0/8] IFPC support for A6xx Konrad Dybcio
@ 2026-10-30 10:03 ` Konrad Dybcio
2026-10-30 10:03 ` [PATCH 2/8] drm/msm/adreno: Register GPU debugfs only on the primary minor Konrad Dybcio
` (6 subsequent siblings)
7 siblings, 0 replies; 14+ messages in thread
From: Konrad Dybcio @ 2026-10-30 10:03 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, Konrad Dybcio
From: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
Extend it to all targets with a real GMU and use a rmw instead of a
full write to preserve other things within that register.
Signed-off-by: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
---
drivers/gpu/drm/msm/adreno/a6xx_gpu.c | 6 +++---
drivers/gpu/drm/msm/registers/adreno/a6xx_gmu.xml | 1 +
2 files changed, 4 insertions(+), 3 deletions(-)
diff --git a/drivers/gpu/drm/msm/adreno/a6xx_gpu.c b/drivers/gpu/drm/msm/adreno/a6xx_gpu.c
index 92e8c8abb86d..48d3ee4d55d9 100644
--- a/drivers/gpu/drm/msm/adreno/a6xx_gpu.c
+++ b/drivers/gpu/drm/msm/adreno/a6xx_gpu.c
@@ -1332,10 +1332,10 @@ static int hw_init(struct msm_gpu *gpu)
/* Turn on performance counters */
gpu_write(gpu, REG_A6XX_RBBM_PERFCTR_CNTL, 0x1);
- if (adreno_is_a7xx(adreno_gpu)) {
+ if (!adreno_has_gmu_wrapper(adreno_gpu)) {
/* Turn on the IFPC counter (countable 4 on XOCLK4) */
- gmu_write(&a6xx_gpu->gmu, REG_A6XX_GMU_CX_GMU_POWER_COUNTER_SELECT_1,
- FIELD_PREP(GENMASK(7, 0), 0x4));
+ gmu_rmw(&a6xx_gpu->gmu, REG_A6XX_GMU_CX_GMU_POWER_COUNTER_SELECT_1,
+ GENMASK(7, 0), 0x4);
}
/* Select CP0 to always count cycles */
diff --git a/drivers/gpu/drm/msm/registers/adreno/a6xx_gmu.xml b/drivers/gpu/drm/msm/registers/adreno/a6xx_gmu.xml
index b3082738c99d..4f934c194f73 100644
--- a/drivers/gpu/drm/msm/registers/adreno/a6xx_gmu.xml
+++ b/drivers/gpu/drm/msm/registers/adreno/a6xx_gmu.xml
@@ -83,6 +83,7 @@ xsi:schemaLocation="https://gitlab.freedesktop.org/freedreno/ rules-fd.xsd">
<reg32 offset="0x1f84a" name="GMU_CX_GMU_POWER_COUNTER_XOCLK_3_L"/>
<reg32 offset="0x1f84b" name="GMU_CX_GMU_POWER_COUNTER_XOCLK_3_H"/>
<reg32 offset="0x1f84c" name="GMU_CX_GMU_POWER_COUNTER_XOCLK_4_L"/>
+ <reg32 offset="0x1fca8" name="GMU_CX_GMU_POWER_COUNTER_XOCLK_4_L" variants="A8XX-"/>
<reg32 offset="0x1f84d" name="GMU_CX_GMU_POWER_COUNTER_XOCLK_4_H"/>
<reg32 offset="0x1f84e" name="GMU_CX_GMU_POWER_COUNTER_XOCLK_5_L"/>
<reg32 offset="0x1f84f" name="GMU_CX_GMU_POWER_COUNTER_XOCLK_5_H"/>
--
2.56.0
^ permalink raw reply related [flat|nested] 14+ messages in thread* [PATCH 2/8] drm/msm/adreno: Register GPU debugfs only on the primary minor
2026-10-30 10:03 [PATCH 0/8] IFPC support for A6xx Konrad Dybcio
2026-10-30 10:03 ` [PATCH 1/8] drm/msm/a6xx: Polish up the IFPC counter enable Konrad Dybcio
@ 2026-10-30 10:03 ` Konrad Dybcio
2026-10-30 10:03 ` [PATCH 3/8] drm/msm/a6xx: Expose GMU IFPC entry count in debugfs Konrad Dybcio
` (5 subsequent siblings)
7 siblings, 0 replies; 14+ messages in thread
From: Konrad Dybcio @ 2026-10-30 10:03 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, Konrad Dybcio
From: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
adreno_load_gpu() calls the GPU's debugfs_init hook for both the
primary and the render minor. Since the DRM debugfs rework both minors
share one debugfs root, so every file the hook creates is created
twice, and the second attempt fails with "already exists".
Since the DRM debugfs rework:(commit 0b30d57acafc ("drm/debugfs: rework
debugfs directory creation v5")) both minors share one root, and
commit e76e7ec8f162 ("drm/debugfs: drop debugfs_init() for the render
and accel node v2") made the core call a driver's debugfs_init only for
the primary minor. Adreno's second call predates that: commit
331dc0bc195b ("drm/msm: add a5xx specific debugfs") added it in 2017,
when each minor had its own debugfs root and the render node needed its
own copy of the files.
Remove the odd call to fix 'already exists' warnings.
Signed-off-by: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
---
drivers/gpu/drm/msm/adreno/adreno_device.c | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)
diff --git a/drivers/gpu/drm/msm/adreno/adreno_device.c b/drivers/gpu/drm/msm/adreno/adreno_device.c
index 7108d4aa59b5..8154371e04eb 100644
--- a/drivers/gpu/drm/msm/adreno/adreno_device.c
+++ b/drivers/gpu/drm/msm/adreno/adreno_device.c
@@ -123,10 +123,8 @@ struct msm_gpu *adreno_load_gpu(struct drm_device *dev)
pm_runtime_put_autosuspend(&pdev->dev);
#ifdef CONFIG_DEBUG_FS
- if (gpu->funcs->debugfs_init) {
+ if (gpu->funcs->debugfs_init)
gpu->funcs->debugfs_init(gpu, dev->primary);
- gpu->funcs->debugfs_init(gpu, dev->render);
- }
#endif
return gpu;
--
2.56.0
^ permalink raw reply related [flat|nested] 14+ messages in thread* [PATCH 3/8] drm/msm/a6xx: Expose GMU IFPC entry count in debugfs
2026-10-30 10:03 [PATCH 0/8] IFPC support for A6xx Konrad Dybcio
2026-10-30 10:03 ` [PATCH 1/8] drm/msm/a6xx: Polish up the IFPC counter enable Konrad Dybcio
2026-10-30 10:03 ` [PATCH 2/8] drm/msm/adreno: Register GPU debugfs only on the primary minor Konrad Dybcio
@ 2026-10-30 10:03 ` Konrad Dybcio
2026-10-30 10:20 ` sashiko-bot
2026-10-30 10:04 ` [PATCH 4/8] drm/msm/a6xx: Add A6xx IFPC support Konrad Dybcio
` (4 subsequent siblings)
7 siblings, 1 reply; 14+ messages in thread
From: Konrad Dybcio @ 2026-10-30 10:03 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, Konrad Dybcio
From: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
Add a read-only debugfs file, ifpc_count, that reports how many times
the GMU has entered IFPC.
The count is accumulated from the GMU XOCLK_4 power counter. The
counter is selected in hw_init, and the delta is folded into the total
from the busy callback, so the value advances whenever devfreq samples
the GPU.
Signed-off-by: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
---
drivers/gpu/drm/msm/Makefile | 1 +
drivers/gpu/drm/msm/adreno/a6xx_debugfs.c | 23 +++++++++++++++++++++++
drivers/gpu/drm/msm/adreno/a6xx_gpu.c | 30 ++++++++++++++++++++++++++++++
drivers/gpu/drm/msm/adreno/a6xx_gpu.h | 10 ++++++++++
drivers/gpu/drm/msm/adreno/a8xx_gpu.c | 9 +++++++++
5 files changed, 73 insertions(+)
diff --git a/drivers/gpu/drm/msm/Makefile b/drivers/gpu/drm/msm/Makefile
index 1e246616f83f..afd42e5bda00 100644
--- a/drivers/gpu/drm/msm/Makefile
+++ b/drivers/gpu/drm/msm/Makefile
@@ -30,6 +30,7 @@ adreno-y := \
adreno/a8xx_preempt.o \
adreno-$(CONFIG_DEBUG_FS) += adreno/a5xx_debugfs.o \
+ adreno/a6xx_debugfs.o \
adreno-$(CONFIG_DRM_MSM_GPU_STATE) += adreno/a6xx_gpu_state.o
diff --git a/drivers/gpu/drm/msm/adreno/a6xx_debugfs.c b/drivers/gpu/drm/msm/adreno/a6xx_debugfs.c
new file mode 100644
index 000000000000..a3f622cb189f
--- /dev/null
+++ b/drivers/gpu/drm/msm/adreno/a6xx_debugfs.c
@@ -0,0 +1,23 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include <linux/types.h>
+#include <linux/debugfs.h>
+
+#include <drm/drm_file.h>
+
+#include "a6xx_gpu.h"
+
+void a6xx_debugfs_init(struct msm_gpu *gpu, struct drm_minor *minor)
+{
+ struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu);
+ struct a6xx_gpu *a6xx_gpu = to_a6xx_gpu(adreno_gpu);
+
+ if (!minor)
+ return;
+
+ if (!(adreno_gpu->info->quirks & ADRENO_QUIRK_IFPC))
+ return;
+
+ debugfs_create_u32("ifpc_count", 0444, minor->debugfs_root,
+ &a6xx_gpu->ifpc_count);
+}
diff --git a/drivers/gpu/drm/msm/adreno/a6xx_gpu.c b/drivers/gpu/drm/msm/adreno/a6xx_gpu.c
index 48d3ee4d55d9..e7c3da5d3ac6 100644
--- a/drivers/gpu/drm/msm/adreno/a6xx_gpu.c
+++ b/drivers/gpu/drm/msm/adreno/a6xx_gpu.c
@@ -1338,6 +1338,11 @@ static int hw_init(struct msm_gpu *gpu)
GENMASK(7, 0), 0x4);
}
+ if (adreno_gpu->info->quirks & ADRENO_QUIRK_IFPC) {
+ a6xx_gpu->ifpc_counter_last =
+ gmu_read(&a6xx_gpu->gmu, REG_A6XX_GMU_CX_GMU_POWER_COUNTER_XOCLK_4_L);
+ }
+
/* Select CP0 to always count cycles */
gpu_write(gpu, REG_A6XX_CP_PERFCTR_CP_SEL(0), PERF_CP_ALWAYS_COUNT);
@@ -2334,6 +2339,17 @@ static void a6xx_destroy(struct msm_gpu *gpu)
kfree(a6xx_gpu);
}
+void a6xx_ifpc_count_update(struct a6xx_gpu *a6xx_gpu, u32 cur)
+{
+ /* A counter below the last sample means the GMU restarted it */
+ if (cur >= a6xx_gpu->ifpc_counter_last)
+ a6xx_gpu->ifpc_count += cur - a6xx_gpu->ifpc_counter_last;
+ else
+ a6xx_gpu->ifpc_count += cur;
+
+ a6xx_gpu->ifpc_counter_last = cur;
+}
+
static u64 a6xx_gpu_busy(struct msm_gpu *gpu, unsigned long *out_sample_rate)
{
struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu);
@@ -2347,6 +2363,11 @@ static u64 a6xx_gpu_busy(struct msm_gpu *gpu, unsigned long *out_sample_rate)
REG_A6XX_GMU_CX_GMU_POWER_COUNTER_XOCLK_0_L,
REG_A6XX_GMU_CX_GMU_POWER_COUNTER_XOCLK_0_H);
+ if (adreno_gpu->info->quirks & ADRENO_QUIRK_IFPC)
+ a6xx_ifpc_count_update(a6xx_gpu,
+ gmu_read(&a6xx_gpu->gmu,
+ REG_A6XX_GMU_CX_GMU_POWER_COUNTER_XOCLK_4_L));
+
return busy_cycles;
}
@@ -2818,6 +2839,9 @@ const struct adreno_gpu_funcs a6xx_gpu_funcs = {
#if defined(CONFIG_DRM_MSM_GPU_STATE)
.gpu_state_get = a6xx_gpu_state_get,
.gpu_state_put = a6xx_gpu_state_put,
+#endif
+#if defined(CONFIG_DEBUG_FS)
+ .debugfs_init = a6xx_debugfs_init,
#endif
.create_vm = a6xx_create_vm,
.create_private_vm = a6xx_create_private_vm,
@@ -2889,6 +2913,9 @@ const struct adreno_gpu_funcs a7xx_gpu_funcs = {
#if defined(CONFIG_DRM_MSM_GPU_STATE)
.gpu_state_get = a6xx_gpu_state_get,
.gpu_state_put = a6xx_gpu_state_put,
+#endif
+#if defined(CONFIG_DEBUG_FS)
+ .debugfs_init = a6xx_debugfs_init,
#endif
.create_vm = a6xx_create_vm,
.create_private_vm = a6xx_create_private_vm,
@@ -2921,6 +2948,9 @@ const struct adreno_gpu_funcs a8xx_gpu_funcs = {
.gpu_busy = a8xx_gpu_busy,
.gpu_get_freq = a6xx_gmu_get_freq,
.gpu_set_freq = a6xx_gpu_set_freq,
+#if defined(CONFIG_DEBUG_FS)
+ .debugfs_init = a6xx_debugfs_init,
+#endif
.create_vm = a6xx_create_vm,
.create_private_vm = a6xx_create_private_vm,
.get_rptr = a6xx_get_rptr,
diff --git a/drivers/gpu/drm/msm/adreno/a6xx_gpu.h b/drivers/gpu/drm/msm/adreno/a6xx_gpu.h
index 79912a33e17c..8f0cb168a535 100644
--- a/drivers/gpu/drm/msm/adreno/a6xx_gpu.h
+++ b/drivers/gpu/drm/msm/adreno/a6xx_gpu.h
@@ -139,6 +139,10 @@ struct a6xx_gpu {
spinlock_t aperture_lock;
u32 slice_mask;
+
+ /* Total GMU IFPC entries, accumulated from the XOCLK_4 counter */
+ u32 ifpc_count;
+ u32 ifpc_counter_last;
};
#define to_a6xx_gpu(x) container_of(x, struct a6xx_gpu, base)
@@ -254,6 +258,12 @@ struct a7xx_cp_smmu_info {
#define A6XX_PROTECT_RDONLY(_reg, _len) \
((((_len) & 0x3FFF) << 18) | ((_reg) & 0x3FFFF))
+#ifdef CONFIG_DEBUG_FS
+void a6xx_debugfs_init(struct msm_gpu *gpu, struct drm_minor *minor);
+#endif
+
+void a6xx_ifpc_count_update(struct a6xx_gpu *a6xx_gpu, u32 cur);
+
extern const struct adreno_gpu_funcs a6xx_gpu_funcs;
extern const struct adreno_gpu_funcs a6xx_gmuwrapper_funcs;
extern const struct adreno_gpu_funcs a7xx_gpu_funcs;
diff --git a/drivers/gpu/drm/msm/adreno/a8xx_gpu.c b/drivers/gpu/drm/msm/adreno/a8xx_gpu.c
index f892731cd3d3..ba81a1c9c551 100644
--- a/drivers/gpu/drm/msm/adreno/a8xx_gpu.c
+++ b/drivers/gpu/drm/msm/adreno/a8xx_gpu.c
@@ -680,6 +680,10 @@ static int hw_init(struct msm_gpu *gpu)
gmu_write(&a6xx_gpu->gmu, REG_A8XX_GMU_CX_GMU_POWER_COUNTER_SELECT_XOCLK_1,
FIELD_PREP(GENMASK(7, 0), 0x4));
+ if (adreno_gpu->info->quirks & ADRENO_QUIRK_IFPC)
+ a6xx_gpu->ifpc_counter_last =
+ gmu_read(gmu, REG_A8XX_GMU_CX_GMU_POWER_COUNTER_XOCLK_4_L);
+
/* Select CP0 to always count cycles */
gpu_write(gpu, REG_A8XX_CP_PERFCTR_CP_SEL(0), 1);
@@ -1354,6 +1358,11 @@ u64 a8xx_gpu_busy(struct msm_gpu *gpu, unsigned long *out_sample_rate)
REG_A8XX_GMU_CX_GMU_POWER_COUNTER_XOCLK_0_L,
REG_A8XX_GMU_CX_GMU_POWER_COUNTER_XOCLK_0_H);
+ if (adreno_gpu->info->quirks & ADRENO_QUIRK_IFPC)
+ a6xx_ifpc_count_update(a6xx_gpu,
+ gmu_read(&a6xx_gpu->gmu,
+ REG_A8XX_GMU_CX_GMU_POWER_COUNTER_XOCLK_4_L));
+
return busy_cycles;
}
--
2.56.0
^ permalink raw reply related [flat|nested] 14+ messages in thread* Re: [PATCH 3/8] drm/msm/a6xx: Expose GMU IFPC entry count in debugfs
2026-10-30 10:03 ` [PATCH 3/8] drm/msm/a6xx: Expose GMU IFPC entry count in debugfs Konrad Dybcio
@ 2026-10-30 10:20 ` sashiko-bot
0 siblings, 0 replies; 14+ messages in thread
From: sashiko-bot @ 2026-10-30 10:20 UTC (permalink / raw)
To: Konrad Dybcio; +Cc: dri-devel
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [Medium] drm/msm/a6xx: unsynchronized ifpc_counter_last access in hw_init()
- [Medium] drm/msm/a6xx: lost IFPC counts on suspend and pre-hw_init() resume sample
--
commit dc6d6671e40dbc56d3fc3f26cdac4aed610caf63
Author: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
drm/msm/a6xx: Expose GMU IFPC entry count in debugfs
This commit adds a read-only ifpc_count debugfs file for Adreno GPUs with
ADRENO_QUIRK_IFPC to report the total number of GMU IFPC entries. It initializes
ifpc_counter_last from the GMU XOCLK_4 power counter during hw_init() and
accumulates deltas into ifpc_count from a6xx_gpu_busy() and a8xx_gpu_busy().
> diff --git a/drivers/gpu/drm/msm/adreno/a6xx_gpu.c b/drivers/gpu/drm/msm/adreno/a6xx_gpu.c
> index 48d3ee4d55d9a..e7c3da5d3ac64 100644
> --- a/drivers/gpu/drm/msm/adreno/a6xx_gpu.c
> +++ b/drivers/gpu/drm/msm/adreno/a6xx_gpu.c
> @@ -1338,6 +1338,11 @@ static int hw_init(struct msm_gpu *gpu)
> GENMASK(7, 0), 0x4);
> }
>
> + if (adreno_gpu->info->quirks & ADRENO_QUIRK_IFPC) {
> + a6xx_gpu->ifpc_counter_last =
> + gmu_read(&a6xx_gpu->gmu, REG_A6XX_GMU_CX_GMU_POWER_COUNTER_XOCLK_4_L);
> + }
[Severity: Medium]
Can this write to a6xx_gpu->ifpc_counter_last in hw_init() (and the matching
write in a8xx_gpu.c:hw_init()) race with a6xx_ifpc_count_update()?
During runtime resume (for example in msm_gpu_submit(), adreno_load_gpu(), or
a6xx_recover()), pm_runtime_get_sync() runs adreno_runtime_resume() ->
a6xx_gmu_pm_resume() before the caller invokes msm_gpu_hw_init() -> hw_init():
a6xx_gmu_pm_resume()
a6xx_gmu_resume()
msm_devfreq_resume()
df->suspended = false;
devfreq_resume_device()
When msm_gpu_hw_init() subsequently runs, hw_init() holds gpu->lock and
a6xx_gpu->gmu.lock, whereas msm_devfreq_get_dev_status() calls
a6xx_gpu_busy() / a8xx_gpu_busy() -> a6xx_ifpc_count_update() under
gpu->devfreq.lock without holding gpu->lock or gmu.lock.
If devfreq polls while hw_init() is executing, could a6xx_ifpc_count_update()
race with hw_init() updating a6xx_gpu->ifpc_counter_last and corrupt
a6xx_gpu->ifpc_count?
> +
> /* Select CP0 to always count cycles */
> gpu_write(gpu, REG_A6XX_CP_PERFCTR_CP_SEL(0), PERF_CP_ALWAYS_COUNT);
>
> @@ -2334,6 +2339,17 @@ static void a6xx_destroy(struct msm_gpu *gpu)
> kfree(a6xx_gpu);
> }
>
> +void a6xx_ifpc_count_update(struct a6xx_gpu *a6xx_gpu, u32 cur)
> +{
> + /* A counter below the last sample means the GMU restarted it */
> + if (cur >= a6xx_gpu->ifpc_counter_last)
> + a6xx_gpu->ifpc_count += cur - a6xx_gpu->ifpc_counter_last;
> + else
> + a6xx_gpu->ifpc_count += cur;
> +
> + a6xx_gpu->ifpc_counter_last = cur;
> +}
[Severity: Medium]
Does this accounting lose IFPC entries across GMU suspend/resume cycles and
misorder the initial sample on resume?
In a6xx_gmu_pm_suspend(), msm_devfreq_suspend() stops devfreq without calling
gpu_busy(), and a6xx_gmu_stop() powers down the GMU CX domain without reading
REG_A6XX_GMU_CX_GMU_POWER_COUNTER_XOCLK_4_L (or the A8xx equivalent). Any IFPC
entries that occurred after the last periodic devfreq poll, such as the GPU
entering IFPC after retiring its last submission before autosuspend fires, are
discarded when the GMU power counter resets on shutdown.
In addition, on resume, a6xx_gmu_pm_resume() calls msm_devfreq_resume() ->
gpu->funcs->gpu_busy() -> a6xx_ifpc_count_update() before msm_gpu_hw_init() ->
hw_init() has selected countable 0x4 on XOCLK_4 or initialized
a6xx_gpu->ifpc_counter_last. When hw_init() then overwrites
a6xx_gpu->ifpc_counter_last directly with gmu_read(), any unaccumulated delta
is overwritten rather than folded into a6xx_gpu->ifpc_count.
Should XOCLK_4_L be sampled before stopping the GMU in a6xx_gmu_pm_suspend(),
and should counter initialization happen before msm_devfreq_resume() samples
gpu_busy()?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261030-topic-a6xx_ifpc-v1-0-6992841a9a32@oss.qualcomm.com?part=3
^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH 4/8] drm/msm/a6xx: Add A6xx IFPC support
2026-10-30 10:03 [PATCH 0/8] IFPC support for A6xx Konrad Dybcio
` (2 preceding siblings ...)
2026-10-30 10:03 ` [PATCH 3/8] drm/msm/a6xx: Expose GMU IFPC entry count in debugfs Konrad Dybcio
@ 2026-10-30 10:04 ` Konrad Dybcio
2026-10-30 10:46 ` sashiko-bot
2026-10-30 10:04 ` [PATCH 5/8] drm/msm/a6xx: Add IFPC tables for Adreno 630 Konrad Dybcio
` (3 subsequent siblings)
7 siblings, 1 reply; 14+ messages in thread
From: Konrad Dybcio @ 2026-10-30 10:04 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, Konrad Dybcio
From: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
IFPC is largely supported by the current upstream driver, add the
missing plumbing that's specific to A6xx, based on kgsl.
Signed-off-by: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
---
drivers/gpu/drm/msm/adreno/a6xx_gpu.c | 97 +++++++++++++++++++++--
drivers/gpu/drm/msm/adreno/a6xx_hfi.c | 11 ++-
drivers/gpu/drm/msm/registers/adreno/a6xx_gmu.xml | 1 +
3 files changed, 98 insertions(+), 11 deletions(-)
diff --git a/drivers/gpu/drm/msm/adreno/a6xx_gpu.c b/drivers/gpu/drm/msm/adreno/a6xx_gpu.c
index e7c3da5d3ac6..8ea9ab5b9886 100644
--- a/drivers/gpu/drm/msm/adreno/a6xx_gpu.c
+++ b/drivers/gpu/drm/msm/adreno/a6xx_gpu.c
@@ -647,6 +647,7 @@ static void a6xx_set_hwcg(struct msm_gpu *gpu, bool state)
unsigned int i;
u32 cgc_delay, cgc_hyst;
u32 val, clock_cntl_on;
+ u32 gmu_wfi_config;
if (!(adreno_gpu->info->a6xx->hwcg || adreno_is_a7xx(adreno_gpu)))
return;
@@ -674,12 +675,20 @@ static void a6xx_set_hwcg(struct msm_gpu *gpu, bool state)
else
cgc_hyst = 0x5555;
+ /* TODO: Also A620 */
+ if (adreno_is_a640(adreno_gpu) || adreno_is_a650(adreno_gpu))
+ gmu_wfi_config = 0x2;
+ else
+ gmu_wfi_config = 0;
+
gmu_write(&a6xx_gpu->gmu, REG_A6XX_GPU_GMU_AO_GMU_CGC_MODE_CNTL,
state ? adreno_gpu->info->a6xx->gmu_cgc_mode : 0);
gmu_write(&a6xx_gpu->gmu, REG_A6XX_GPU_GMU_AO_GMU_CGC_DELAY_CNTL,
state ? cgc_delay : 0);
gmu_write(&a6xx_gpu->gmu, REG_A6XX_GPU_GMU_AO_GMU_CGC_HYST_CNTL,
state ? cgc_hyst : 0);
+ gmu_write(&a6xx_gpu->gmu, REG_A6XX_GMU_CX_GMU_WFI_CONFIG,
+ state ? gmu_wfi_config : 0);
if (!adreno_gpu->info->a6xx->hwcg) {
gpu_write(gpu, REG_A7XX_RBBM_CLOCK_CNTL_GLOBAL, 1);
@@ -878,6 +887,43 @@ static void a7xx_patch_pwrup_reglist(struct msm_gpu *gpu)
a6xx_gpu->dynamic_sel_reglist_offset = dyn_pwrup_reglist_count;
}
+static void a6xx_patch_pwrup_reglist(struct msm_gpu *gpu)
+{
+ struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu);
+ struct a6xx_gpu *a6xx_gpu = to_a6xx_gpu(adreno_gpu);
+ const struct adreno_reglist_list *reglist;
+ void *ptr = a6xx_gpu->pwrup_reglist_ptr;
+ struct cpu_gpu_lock *lock = ptr;
+ u32 *dest = (u32 *)&lock->regs[0];
+ int i;
+
+ lock->gpu_req = lock->cpu_req = lock->turn = 0;
+ lock->list_length = 0;
+
+ /* Static IFPC-only registers */
+ reglist = adreno_gpu->info->a6xx->ifpc_reglist;
+ if (reglist) {
+ for (i = 0; i < reglist->count; i++) {
+ *dest++ = reglist->regs[i];
+ *dest++ = gpu_read(gpu, reglist->regs[i]);
+ }
+ lock->list_length += reglist->count * 2;
+ }
+
+ /* The CP sees IFPC-only and IFPC + preemption as one list from list_offset */
+ lock->list_offset = lock->list_length;
+
+ /* Static IFPC + preemption registers */
+ reglist = adreno_gpu->info->a6xx->pwrup_reglist;
+ if (reglist) {
+ for (i = 0; i < reglist->count; i++) {
+ *dest++ = reglist->regs[i];
+ *dest++ = gpu_read(gpu, reglist->regs[i]);
+ }
+ lock->list_length += reglist->count * 2;
+ }
+}
+
static int a7xx_preempt_start(struct msm_gpu *gpu)
{
struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu);
@@ -898,13 +944,43 @@ static int a7xx_preempt_start(struct msm_gpu *gpu)
return a6xx_idle(gpu, ring) ? 0 : -EINVAL;
}
+/* Select whether multiple hardware contexts can run at once or are serialized */
+#define CP_INIT_MAX_CONTEXT BIT(0)
+/* Enable register protection mode */
+#define CP_INIT_ERROR_DETECTION_CONTROL BIT(1)
+/* Header dump information, reserved */
+#define CP_INIT_HEADER_DUMP BIT(2)
+/* Default Reset states enabled for PFP and ME */
+#define CP_INIT_DEFAULT_RESET_STATE BIT(3)
+/* Drawcall filter range */
+#define CP_INIT_DRAWCALL_FILTER_RANGE BIT(4)
+/* Ucode workaround masks */
+#define CP_INIT_UCODE_WORKAROUND_MASK BIT(5)
+/* Disable the save/restore of performance counters across preemption. */
+#define CP_INIT_OPERATION_MODE_MASK BIT(6)
+/* Register initialization list */
+#define CP_INIT_REGISTER_INIT_LIST BIT(7)
+/* Register initialization list with spinlock */
+#define CP_INIT_REGISTER_INIT_LIST_WITH_SPINLOCK BIT(8)
+
+#define CP_INIT_MASK (CP_INIT_MAX_CONTEXT | \
+ CP_INIT_ERROR_DETECTION_CONTROL | \
+ CP_INIT_HEADER_DUMP | \
+ CP_INIT_DEFAULT_RESET_STATE | \
+ CP_INIT_UCODE_WORKAROUND_MASK | \
+ CP_INIT_OPERATION_MODE_MASK | \
+ CP_INIT_REGISTER_INIT_LIST_WITH_SPINLOCK)
+
static int a6xx_cp_init(struct msm_gpu *gpu)
{
+ struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu);
+ struct a6xx_gpu *a6xx_gpu = to_a6xx_gpu(adreno_gpu);
struct msm_ringbuffer *ring = gpu->rb[0];
- OUT_PKT7(ring, CP_ME_INIT, 8);
+ OUT_PKT7(ring, CP_ME_INIT, 10);
- OUT_RING(ring, 0x0000002f);
+ /* Enabled ordinal mask */
+ OUT_RING(ring, CP_INIT_MASK);
/* Enable multiple hardware contexts */
OUT_RING(ring, 0x00000003);
@@ -919,9 +995,12 @@ static int a6xx_cp_init(struct msm_gpu *gpu)
/* No workarounds enabled */
OUT_RING(ring, 0x00000000);
- /* Pad rest of the cmds with 0's */
- OUT_RING(ring, 0x00000000);
- OUT_RING(ring, 0x00000000);
+ /* Operation mode mask */
+ OUT_RING(ring, 0x00000002);
+
+ OUT_RING(ring, lower_32_bits(a6xx_gpu->pwrup_reglist_iova));
+ OUT_RING(ring, upper_32_bits(a6xx_gpu->pwrup_reglist_iova));
+ OUT_RING(ring, 0);
a6xx_flush(gpu, ring);
return a6xx_idle(gpu, ring) ? 0 : -EINVAL;
@@ -1485,8 +1564,12 @@ static int hw_init(struct msm_gpu *gpu)
/* Enable the SQE_to start the CP engine */
gpu_write(gpu, REG_A6XX_CP_SQE_CNTL, 1);
- if (adreno_is_a7xx(adreno_gpu) && !a6xx_gpu->pwrup_reglist_emitted) {
- a7xx_patch_pwrup_reglist(gpu);
+ if (!a6xx_gpu->pwrup_reglist_emitted) {
+ if (adreno_is_a7xx(adreno_gpu))
+ a7xx_patch_pwrup_reglist(gpu);
+ else
+ a6xx_patch_pwrup_reglist(gpu);
+
a6xx_gpu->pwrup_reglist_emitted = true;
}
diff --git a/drivers/gpu/drm/msm/adreno/a6xx_hfi.c b/drivers/gpu/drm/msm/adreno/a6xx_hfi.c
index 6b34e704a70d..b07ba7233c2e 100644
--- a/drivers/gpu/drm/msm/adreno/a6xx_hfi.c
+++ b/drivers/gpu/drm/msm/adreno/a6xx_hfi.c
@@ -855,7 +855,7 @@ static int a6xx_hfi_feature_ctrl_msg(struct a6xx_gmu *gmu, u32 feature, u32 enab
#define IFPC_LONG_HYST 0x1680
-static int a6xx_hfi_enable_ifpc(struct a6xx_gmu *gmu)
+static int a7xx_hfi_enable_ifpc(struct a6xx_gmu *gmu)
{
if (gmu->idle_level != GMU_IDLE_STATE_IFPC)
return 0;
@@ -1059,6 +1059,7 @@ static int a6xx_hfi_start_v1(struct a6xx_gmu *gmu, int boot_state)
int a6xx_hfi_start(struct a6xx_gmu *gmu, int boot_state)
{
+ struct a6xx_gpu *a6xx_gpu = container_of(gmu, struct a6xx_gpu, gmu);
int ret;
if (gmu->legacy)
@@ -1081,9 +1082,11 @@ int a6xx_hfi_start(struct a6xx_gmu *gmu, int boot_state)
if (ret)
return ret;
- ret = a6xx_hfi_enable_ifpc(gmu);
- if (ret)
- return ret;
+ if (!adreno_is_a6xx(&a6xx_gpu->base)) {
+ ret = a7xx_hfi_enable_ifpc(gmu);
+ if (ret)
+ return ret;
+ }
ret = a6xx_hfi_send_thinmem_config(gmu);
if (ret)
diff --git a/drivers/gpu/drm/msm/registers/adreno/a6xx_gmu.xml b/drivers/gpu/drm/msm/registers/adreno/a6xx_gmu.xml
index 4f934c194f73..e5fe940d6cbf 100644
--- a/drivers/gpu/drm/msm/registers/adreno/a6xx_gmu.xml
+++ b/drivers/gpu/drm/msm/registers/adreno/a6xx_gmu.xml
@@ -68,6 +68,7 @@ xsi:schemaLocation="https://gitlab.freedesktop.org/freedreno/ rules-fd.xsd">
<reg32 offset="0x1f82d" name="GMU_CM3_CFG"/>
<reg32 offset="0x1f840" name="GMU_CX_GMU_POWER_COUNTER_ENABLE"/>
<reg32 offset="0x1fc10" name="GMU_CX_GMU_POWER_COUNTER_ENABLE" variants="A8XX"/>
+ <reg32 offset="0x1f802" name="GMU_CX_GMU_WFI_CONFIG"/>
<reg32 offset="0x1f841" name="GMU_CX_GMU_POWER_COUNTER_SELECT_0"/>
<reg32 offset="0x1f842" name="GMU_CX_GMU_POWER_COUNTER_SELECT_1"/>
<reg32 offset="0x1fc40" name="GMU_CX_GMU_POWER_COUNTER_SELECT_XOCLK_0" variants="A8XX-"/>
--
2.56.0
^ permalink raw reply related [flat|nested] 14+ messages in thread* Re: [PATCH 4/8] drm/msm/a6xx: Add A6xx IFPC support
2026-10-30 10:04 ` [PATCH 4/8] drm/msm/a6xx: Add A6xx IFPC support Konrad Dybcio
@ 2026-10-30 10:46 ` sashiko-bot
0 siblings, 0 replies; 14+ messages in thread
From: sashiko-bot @ 2026-10-30 10:46 UTC (permalink / raw)
To: Konrad Dybcio; +Cc: dri-devel
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [High] drm/msm/a6xx: missing IFPC CP_SET_MARKER packets in a6xx_submit()
- [Medium] drm/msm/a6xx: perfcntr SELECT regs lost on IFPC in a6xx_perfcntr_configure()
--
commit bfad38c351383c6a537cd26f28ef21f13067df68
Author: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
drm/msm/a6xx: Add A6xx IFPC support
This commit adds A6xx-specific IFPC plumbing, including GMU WFI
configuration, power-up register list patching, CP_ME_INIT register list
programming, and skipping the A7xx HFI IFPC enable message on A6xx.
> diff --git a/drivers/gpu/drm/msm/adreno/a6xx_gpu.c b/drivers/gpu/drm/msm/adreno/a6xx_gpu.c
> index e7c3da5d3ac64..8ea9ab5b98861 100644
> --- a/drivers/gpu/drm/msm/adreno/a6xx_gpu.c
> +++ b/drivers/gpu/drm/msm/adreno/a6xx_gpu.c
[ ... ]
> @@ -878,6 +887,43 @@ static void a7xx_patch_pwrup_reglist(struct msm_gpu *gpu)
> a6xx_gpu->dynamic_sel_reglist_offset = dyn_pwrup_reglist_count;
> }
>
> +static void a6xx_patch_pwrup_reglist(struct msm_gpu *gpu)
> +{
> + struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu);
> + struct a6xx_gpu *a6xx_gpu = to_a6xx_gpu(adreno_gpu);
> + const struct adreno_reglist_list *reglist;
> + void *ptr = a6xx_gpu->pwrup_reglist_ptr;
> + struct cpu_gpu_lock *lock = ptr;
> + u32 *dest = (u32 *)&lock->regs[0];
> + int i;
> +
> + lock->gpu_req = lock->cpu_req = lock->turn = 0;
> + lock->list_length = 0;
> +
> + /* Static IFPC-only registers */
> + reglist = adreno_gpu->info->a6xx->ifpc_reglist;
> + if (reglist) {
> + for (i = 0; i < reglist->count; i++) {
> + *dest++ = reglist->regs[i];
> + *dest++ = gpu_read(gpu, reglist->regs[i]);
> + }
> + lock->list_length += reglist->count * 2;
> + }
> +
> + /* The CP sees IFPC-only and IFPC + preemption as one list from list_offset */
> + lock->list_offset = lock->list_length;
> +
> + /* Static IFPC + preemption registers */
> + reglist = adreno_gpu->info->a6xx->pwrup_reglist;
> + if (reglist) {
> + for (i = 0; i < reglist->count; i++) {
> + *dest++ = reglist->regs[i];
> + *dest++ = gpu_read(gpu, reglist->regs[i]);
> + }
> + lock->list_length += reglist->count * 2;
> + }
> +}
[Severity: Medium]
When a performance counter stream is configured via
DRM_IOCTL_MSM_PERFCNTR_CONFIG, msm_gpu_sysprof_no_ifpc() allows the GPU to
enter IFPC between samples under the assumption that a6xx_perfcntr_configure()
appends the configured SELECT registers to pwrup_reglist_ptr.
However, a6xx_perfcntr_configure() only populates pwrup_reglist_ptr when
info->family >= ADRENO_7XX_GEN1:
drivers/gpu/drm/msm/adreno/a6xx_gpu.c:a6xx_perfcntr_configure() {
if (to_adreno_gpu(gpu)->info->family >= ADRENO_7XX_GEN1) {
WARN_ON(!a6xx_gpu->pwrup_reglist_emitted);
struct cpu_gpu_lock *lock = a6xx_gpu->pwrup_reglist_ptr;
int off = (2 * lock->ifpc_list_len) +
(2 * lock->preemption_list_len) +
(3 * a6xx_gpu->dynamic_sel_reglist_offset);
...
On A6xx, a6xx_patch_pwrup_reglist() uses the A6xx cpu_gpu_lock layout
(lock->list_length and lock->list_offset with register/value pairs) without
recording the base static offset in dynamic_sel_reglist_offset, and
a6xx_perfcntr_configure() leaves reglist as NULL.
Will entering IFPC on A6xx while a performance counter stream is active lose
the GX-domain performance counter SELECT registers when the GX power domain
collapses?
[ ... ]
> @@ -1485,8 +1564,12 @@ static int hw_init(struct msm_gpu *gpu)
> /* Enable the SQE_to start the CP engine */
> gpu_write(gpu, REG_A6XX_CP_SQE_CNTL, 1);
>
> - if (adreno_is_a7xx(adreno_gpu) && !a6xx_gpu->pwrup_reglist_emitted) {
> - a7xx_patch_pwrup_reglist(gpu);
> + if (!a6xx_gpu->pwrup_reglist_emitted) {
> + if (adreno_is_a7xx(adreno_gpu))
> + a7xx_patch_pwrup_reglist(gpu);
> + else
> + a6xx_patch_pwrup_reglist(gpu);
> +
> a6xx_gpu->pwrup_reglist_emitted = true;
> }
[Severity: High]
In a7xx_submit(), the driver emits CP_SET_MARKER packets around command
submissions to disable IFPC before indirect buffers execute and re-enable it
after completion:
drivers/gpu/drm/msm/adreno/a6xx_gpu.c:a7xx_submit() {
...
OUT_PKT7(ring, CP_SET_MARKER, 1);
OUT_RING(ring, 0x101); /* IFPC disable */
...
OUT_PKT7(ring, CP_SET_MARKER, 1);
OUT_RING(ring, 0x100); /* IFPC enable */
}
However, a6xx_submit() in drivers/gpu/drm/msm/adreno/a6xx_gpu.c does not emit
these CP_SET_MARKER packets before the indirect buffer loop or after the
CACHE_FLUSH_TS event write.
Should a6xx_submit() also emit CP_SET_MARKER(0x101) and CP_SET_MARKER(0x100)
so that REG_A6XX_CP_CP2GMU_STATUS properly tracks IFPC eligibility across
submissions on A6xx?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261030-topic-a6xx_ifpc-v1-0-6992841a9a32@oss.qualcomm.com?part=4
^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH 5/8] drm/msm/a6xx: Add IFPC tables for Adreno 630
2026-10-30 10:03 [PATCH 0/8] IFPC support for A6xx Konrad Dybcio
` (3 preceding siblings ...)
2026-10-30 10:04 ` [PATCH 4/8] drm/msm/a6xx: Add A6xx IFPC support Konrad Dybcio
@ 2026-10-30 10:04 ` Konrad Dybcio
2026-10-30 10:23 ` sashiko-bot
2026-10-30 10:04 ` [PATCH 6/8] drm/msm/a6xx: Add IFPC tables for Adreno 640/680 Konrad Dybcio
` (2 subsequent siblings)
7 siblings, 1 reply; 14+ messages in thread
From: Konrad Dybcio @ 2026-10-30 10:04 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, Konrad Dybcio
From: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
Fill out the register tables to enable IFPC on this SKU.
Signed-off-by: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
---
drivers/gpu/drm/msm/adreno/a6xx_catalog.c | 83 ++++++++++++++++++++++++++++++-
1 file changed, 82 insertions(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/msm/adreno/a6xx_catalog.c b/drivers/gpu/drm/msm/adreno/a6xx_catalog.c
index 4ef2bd6e7ad2..5e1d91a73a4e 100644
--- a/drivers/gpu/drm/msm/adreno/a6xx_catalog.c
+++ b/drivers/gpu/drm/msm/adreno/a6xx_catalog.c
@@ -736,6 +736,84 @@ static const struct adreno_reglist a702_hwcg[] = {
{}
};
+static const u32 a630_ifpc_reglist_regs[] = {
+ REG_A6XX_CP_CHICKEN_DBG,
+ REG_A6XX_CP_DBG_ECO_CNTL,
+ REG_A6XX_CP_PROTECT_CNTL,
+ REG_A6XX_CP_PROTECT(0),
+ REG_A6XX_CP_PROTECT(1),
+ REG_A6XX_CP_PROTECT(2),
+ REG_A6XX_CP_PROTECT(3),
+ REG_A6XX_CP_PROTECT(4),
+ REG_A6XX_CP_PROTECT(5),
+ REG_A6XX_CP_PROTECT(6),
+ REG_A6XX_CP_PROTECT(7),
+ REG_A6XX_CP_PROTECT(8),
+ REG_A6XX_CP_PROTECT(9),
+ REG_A6XX_CP_PROTECT(10),
+ REG_A6XX_CP_PROTECT(11),
+ REG_A6XX_CP_PROTECT(12),
+ REG_A6XX_CP_PROTECT(13),
+ REG_A6XX_CP_PROTECT(14),
+ REG_A6XX_CP_PROTECT(15),
+ REG_A6XX_CP_PROTECT(16),
+ REG_A6XX_CP_PROTECT(17),
+ REG_A6XX_CP_PROTECT(18),
+ REG_A6XX_CP_PROTECT(19),
+ REG_A6XX_CP_PROTECT(20),
+ REG_A6XX_CP_PROTECT(21),
+ REG_A6XX_CP_PROTECT(22),
+ REG_A6XX_CP_PROTECT(23),
+ REG_A6XX_CP_PROTECT(24),
+ REG_A6XX_CP_PROTECT(25),
+ REG_A6XX_CP_PROTECT(26),
+ REG_A6XX_CP_PROTECT(27),
+ REG_A6XX_CP_PROTECT(28),
+ REG_A6XX_CP_PROTECT(29),
+ REG_A6XX_CP_PROTECT(30),
+ REG_A6XX_CP_PROTECT(31),
+ REG_A6XX_CP_AHB_CNTL,
+};
+
+DECLARE_ADRENO_REGLIST_LIST(a630_ifpc_reglist);
+
+static const u32 a630_pwrup_reglist_regs[] = {
+ REG_A6XX_VSC_ADDR_MODE_CNTL,
+ REG_A6XX_GRAS_ADDR_MODE_CNTL,
+ REG_A6XX_RB_ADDR_MODE_CNTL,
+ REG_A6XX_PC_ADDR_MODE_CNTL,
+ REG_A6XX_HLSQ_ADDR_MODE_CNTL,
+ REG_A6XX_VFD_ADDR_MODE_CNTL,
+ REG_A6XX_VPC_ADDR_MODE_CNTL,
+ REG_A6XX_UCHE_ADDR_MODE_CNTL,
+ REG_A6XX_SP_ADDR_MODE_CNTL,
+ REG_A6XX_TPL1_ADDR_MODE_CNTL,
+ REG_A6XX_UCHE_WRITE_RANGE_MAX,
+ REG_A6XX_UCHE_WRITE_RANGE_MAX + 1,
+ REG_A6XX_UCHE_TRAP_BASE,
+ REG_A6XX_UCHE_TRAP_BASE + 1,
+ REG_A6XX_UCHE_WRITE_THRU_BASE,
+ REG_A6XX_UCHE_WRITE_THRU_BASE + 1,
+ REG_A6XX_UCHE_GMEM_RANGE_MIN,
+ REG_A6XX_UCHE_GMEM_RANGE_MIN + 1,
+ REG_A6XX_UCHE_GMEM_RANGE_MAX,
+ REG_A6XX_UCHE_GMEM_RANGE_MAX + 1,
+ REG_A6XX_UCHE_FILTER_CNTL,
+ REG_A6XX_UCHE_CACHE_WAYS,
+ REG_A6XX_UCHE_MODE_CNTL,
+ REG_A6XX_RB_NC_MODE_CNTL,
+ REG_A6XX_TPL1_NC_MODE_CNTL,
+ REG_A6XX_SP_NC_MODE_CNTL,
+ REG_A6XX_PC_DBG_ECO_CNTL,
+ REG_A6XX_RB_CONTEXT_SWITCH_GMEM_SAVE_RESTORE_ENABLE,
+ REG_A6XX_UCHE_GBIF_GX_CONFIG,
+ REG_A6XX_UCHE_CLIENT_PF,
+ REG_A6XX_RBBM_VBIF_CLIENT_QOS_CNTL,
+ REG_A6XX_RBBM_PERFCTR_CNTL,
+};
+
+DECLARE_ADRENO_REGLIST_LIST(a630_pwrup_reglist);
+
static const struct adreno_info a6xx_gpus[] = {
{
.chip_ids = ADRENO_CHIP_IDS(0x06010000),
@@ -1014,7 +1092,8 @@ static const struct adreno_info a6xx_gpus[] = {
},
.gmem = SZ_1M,
.quirks = ADRENO_QUIRK_HAS_CACHED_COHERENT |
- ADRENO_QUIRK_4GB_VA,
+ ADRENO_QUIRK_4GB_VA |
+ ADRENO_QUIRK_IFPC,
.inactive_period = DRM_MSM_INACTIVE_PERIOD,
.funcs = &a6xx_gpu_funcs,
.zapfw = "a630_zap.mdt",
@@ -1023,6 +1102,8 @@ static const struct adreno_info a6xx_gpus[] = {
.protect = &a630_protect,
.gmu_cgc_mode = 0x00020202,
.prim_fifo_threshold = 0x00180000,
+ .ifpc_reglist = &a630_ifpc_reglist,
+ .pwrup_reglist = &a630_pwrup_reglist,
},
}, {
.chip_ids = ADRENO_CHIP_IDS(0x06040001),
--
2.56.0
^ permalink raw reply related [flat|nested] 14+ messages in thread* Re: [PATCH 5/8] drm/msm/a6xx: Add IFPC tables for Adreno 630
2026-10-30 10:04 ` [PATCH 5/8] drm/msm/a6xx: Add IFPC tables for Adreno 630 Konrad Dybcio
@ 2026-10-30 10:23 ` sashiko-bot
0 siblings, 0 replies; 14+ messages in thread
From: sashiko-bot @ 2026-10-30 10:23 UTC (permalink / raw)
To: Konrad Dybcio; +Cc: dri-devel
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [Medium] drm/msm/a6xx: perfcntr select regs lost across IFPC on A630
- [Medium] drm/msm/a6xx: shutdown idle timeout in a6xx_gmu_shutdown() on A630
--
commit e4f747633a2babaf9194dc940b8cefe42bf15c0f
Author: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
drm/msm/a6xx: Add IFPC tables for Adreno 630
This commit adds the IFPC and power-up register list tables for Adreno 630
and enables ADRENO_QUIRK_IFPC in its catalog entry.
> diff --git a/drivers/gpu/drm/msm/adreno/a6xx_catalog.c b/drivers/gpu/drm/msm/adreno/a6xx_catalog.c
> index 4ef2bd6e7ad26..5e1d91a73a4e1 100644
> --- a/drivers/gpu/drm/msm/adreno/a6xx_catalog.c
> +++ b/drivers/gpu/drm/msm/adreno/a6xx_catalog.c
[ ... ]
> @@ -1014,7 +1092,8 @@ static const struct adreno_info a6xx_gpus[] = {
> },
> .gmem = SZ_1M,
> .quirks = ADRENO_QUIRK_HAS_CACHED_COHERENT |
> - ADRENO_QUIRK_4GB_VA,
> + ADRENO_QUIRK_4GB_VA |
> + ADRENO_QUIRK_IFPC,
[Severity: Medium]
Does enabling ADRENO_QUIRK_IFPC on Adreno 630 cause a6xx_gmu_shutdown() to
time out waiting for idle when the GPU is runtime-resumed and suspended
without running hw_init()?
On Adreno 630 (ADRENO_6XX_GEN1), a6xx_gmu_init() sets gmu->legacy = true, and
with ADRENO_QUIRK_IFPC set it now also configures gmu->idle_level =
GMU_IDLE_STATE_IFPC. When the GPU is resumed via
a6xx_gmu_pm_resume() -> a6xx_gmu_resume() -> a6xx_gmu_fw_start(),
a6xx_gmu_gfx_rail_on() asserts GMU_OOB_BOOT_SLUMBER:
drivers/gpu/drm/msm/adreno/a6xx_gmu.c:a6xx_gmu_gfx_rail_on() {
...
/* Let the GMU know the boot sequence has started */
return a6xx_gmu_set_oob(gmu, GMU_OOB_BOOT_SLUMBER);
}
Normally, GMU_OOB_BOOT_SLUMBER is cleared at the end of hw_init():
drivers/gpu/drm/msm/adreno/a6xx_gpu.c:hw_init() {
...
if (a6xx_gpu->gmu.legacy) {
/* Take the GMU out of its special boot mode */
a6xx_gmu_clear_oob(&a6xx_gpu->gmu, GMU_OOB_BOOT_SLUMBER);
}
}
However, if the GPU is woken without running msm_gpu_hw_init() (for example,
when userspace queries MSM_PARAM_TIMESTAMP in adreno_get_param() or
configures sysprof in msm_context_set_sysprof()), adreno_gpu->base.needs_hw_init
remains true when runtime suspend calls a6xx_gmu_shutdown():
drivers/gpu/drm/msm/adreno/a6xx_gmu.c:a6xx_gmu_shutdown() {
...
if (adreno_gpu->base.needs_hw_init) {
if (a6xx_gmu_set_oob(&a6xx_gpu->gmu, GMU_OOB_GPU_SET))
goto force_off;
a6xx_gmu_clear_oob(&a6xx_gpu->gmu, GMU_OOB_GPU_SET);
}
if (test_and_clear_bit(GMU_STATUS_OOB_PERF_SET, &gmu->status))
a6xx_gmu_clear_oob(gmu, GMU_OOB_PERFCOUNTER_SET);
ret = a6xx_gmu_wait_for_idle(gmu);
if (ret)
goto force_off;
}
Because a6xx_gmu_shutdown() does not clear GMU_OOB_BOOT_SLUMBER when
needs_hw_init is true on a legacy GMU, the GX rail stays powered on while
a6xx_gmu_wait_for_idle() -> a6xx_gmu_check_idle_level() polls for
GMU_IDLE_STATE_IFPC and !adreno_gpu->funcs->gx_is_on(adreno_gpu), which times
out and falls back to force_off.
> .inactive_period = DRM_MSM_INACTIVE_PERIOD,
> .funcs = &a6xx_gpu_funcs,
> .zapfw = "a630_zap.mdt",
> @@ -1023,6 +1102,8 @@ static const struct adreno_info a6xx_gpus[] = {
> .protect = &a630_protect,
> .gmu_cgc_mode = 0x00020202,
> .prim_fifo_threshold = 0x00180000,
> + .ifpc_reglist = &a630_ifpc_reglist,
> + .pwrup_reglist = &a630_pwrup_reglist,
[Severity: Medium]
With IFPC enabled on Adreno 630, will performance counter select registers
configured for global performance counter streaming (MSM_PERFCNTR_STREAM) be
lost across IFPC power collapse?
Commit 632ad5510114 ("drm/msm/a6xx: Restore perfcntr Selects on IFPC") changed
msm_gpu_sysprof_no_ifpc() to only check refcount_read(&gpu->sysprof_active) > 1,
allowing IFPC while gpu->perfcntrs->stream is active and relying on
a6xx_perfcntr_configure() to append the configured select registers to
pwrup_reglist so the CP restores them on IFPC exit.
However, a6xx_perfcntr_configure() only populates reglist for ADRENO_7XX_GEN1
and newer:
drivers/gpu/drm/msm/adreno/a6xx_gpu.c:a6xx_perfcntr_configure() {
...
if (to_adreno_gpu(gpu)->info->family >= ADRENO_7XX_GEN1) {
WARN_ON(!a6xx_gpu->pwrup_reglist_emitted);
struct cpu_gpu_lock *lock = a6xx_gpu->pwrup_reglist_ptr;
int off = (2 * lock->ifpc_list_len) +
(2 * lock->preemption_list_len) +
(3 * a6xx_gpu->dynamic_sel_reglist_offset);
reglist = (uint32_t *)&lock->regs[0];
reglist += off;
reglist_sel_start = reglist;
...
}
}
Similarly, a6xx_patch_pwrup_reglist() populates the A6xx 2-dword
(offset, value) list in lock->list_length without tracking dynamic select
register entries. On Adreno 630 (ADRENO_6XX_GEN1), reglist remains NULL in
a6xx_perfcntr_configure(), so entering IFPC between sample intervals will
reset the PERFCTR_*_SEL registers without restoring them on wakeup.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261030-topic-a6xx_ifpc-v1-0-6992841a9a32@oss.qualcomm.com?part=5
^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH 6/8] drm/msm/a6xx: Add IFPC tables for Adreno 640/680
2026-10-30 10:03 [PATCH 0/8] IFPC support for A6xx Konrad Dybcio
` (4 preceding siblings ...)
2026-10-30 10:04 ` [PATCH 5/8] drm/msm/a6xx: Add IFPC tables for Adreno 630 Konrad Dybcio
@ 2026-10-30 10:04 ` Konrad Dybcio
2026-10-30 10:19 ` sashiko-bot
2026-10-30 10:04 ` [PATCH RFT 7/8] drm/msm/a6xx: Add IFPC tables for Adreno 650 family Konrad Dybcio
2026-10-30 10:04 ` [PATCH RFT 8/8] drm/msm/a6xx: Wire up IFPC on A615-family GPUs Konrad Dybcio
7 siblings, 1 reply; 14+ messages in thread
From: Konrad Dybcio @ 2026-10-30 10:04 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, Konrad Dybcio
From: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
Fill out the register tables to enable IFPC on these SKUs.
Signed-off-by: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
---
drivers/gpu/drm/msm/adreno/a6xx_catalog.c | 47 +++++++++++++++++++++++++++++--
1 file changed, 45 insertions(+), 2 deletions(-)
diff --git a/drivers/gpu/drm/msm/adreno/a6xx_catalog.c b/drivers/gpu/drm/msm/adreno/a6xx_catalog.c
index 5e1d91a73a4e..89829fa8f4f4 100644
--- a/drivers/gpu/drm/msm/adreno/a6xx_catalog.c
+++ b/drivers/gpu/drm/msm/adreno/a6xx_catalog.c
@@ -814,6 +814,43 @@ static const u32 a630_pwrup_reglist_regs[] = {
DECLARE_ADRENO_REGLIST_LIST(a630_pwrup_reglist);
+static const u32 a640_pwrup_reglist_regs[] = {
+ REG_A6XX_VSC_ADDR_MODE_CNTL,
+ REG_A6XX_GRAS_ADDR_MODE_CNTL,
+ REG_A6XX_RB_ADDR_MODE_CNTL,
+ REG_A6XX_PC_ADDR_MODE_CNTL,
+ REG_A6XX_HLSQ_ADDR_MODE_CNTL,
+ REG_A6XX_VFD_ADDR_MODE_CNTL,
+ REG_A6XX_VPC_ADDR_MODE_CNTL,
+ REG_A6XX_UCHE_ADDR_MODE_CNTL,
+ REG_A6XX_SP_ADDR_MODE_CNTL,
+ REG_A6XX_TPL1_ADDR_MODE_CNTL,
+ REG_A6XX_UCHE_WRITE_RANGE_MAX,
+ REG_A6XX_UCHE_WRITE_RANGE_MAX + 1,
+ REG_A6XX_UCHE_TRAP_BASE,
+ REG_A6XX_UCHE_TRAP_BASE + 1,
+ REG_A6XX_UCHE_WRITE_THRU_BASE,
+ REG_A6XX_UCHE_WRITE_THRU_BASE + 1,
+ REG_A6XX_UCHE_GMEM_RANGE_MIN,
+ REG_A6XX_UCHE_GMEM_RANGE_MIN + 1,
+ REG_A6XX_UCHE_GMEM_RANGE_MAX,
+ REG_A6XX_UCHE_GMEM_RANGE_MAX + 1,
+ REG_A6XX_UCHE_FILTER_CNTL,
+ REG_A6XX_UCHE_CACHE_WAYS,
+ REG_A6XX_UCHE_MODE_CNTL,
+ REG_A6XX_RB_NC_MODE_CNTL,
+ REG_A6XX_TPL1_NC_MODE_CNTL,
+ REG_A6XX_SP_NC_MODE_CNTL,
+ REG_A6XX_PC_DBG_ECO_CNTL,
+ REG_A6XX_RB_CONTEXT_SWITCH_GMEM_SAVE_RESTORE_ENABLE,
+ REG_A6XX_UCHE_GBIF_GX_CONFIG,
+ REG_A6XX_UCHE_CLIENT_PF,
+ REG_A6XX_RBBM_GBIF_CLIENT_QOS_CNTL,
+ REG_A6XX_RBBM_PERFCTR_CNTL,
+};
+
+DECLARE_ADRENO_REGLIST_LIST(a640_pwrup_reglist);
+
static const struct adreno_info a6xx_gpus[] = {
{
.chip_ids = ADRENO_CHIP_IDS(0x06010000),
@@ -1115,7 +1152,8 @@ static const struct adreno_info a6xx_gpus[] = {
},
.gmem = SZ_1M,
.quirks = ADRENO_QUIRK_HAS_CACHED_COHERENT |
- ADRENO_QUIRK_4GB_VA,
+ ADRENO_QUIRK_4GB_VA |
+ ADRENO_QUIRK_IFPC,
.inactive_period = DRM_MSM_INACTIVE_PERIOD,
.funcs = &a6xx_gpu_funcs,
.zapfw = "a640_zap.mdt",
@@ -1124,6 +1162,8 @@ static const struct adreno_info a6xx_gpus[] = {
.protect = &a630_protect,
.gmu_cgc_mode = 0x00020202,
.prim_fifo_threshold = 0x00180000,
+ .ifpc_reglist = &a630_ifpc_reglist,
+ .pwrup_reglist = &a640_pwrup_reglist,
},
.speedbins = ADRENO_SPEEDBINS(
{ 0, 0 },
@@ -1238,7 +1278,8 @@ static const struct adreno_info a6xx_gpus[] = {
},
.gmem = SZ_2M,
.quirks = ADRENO_QUIRK_HAS_CACHED_COHERENT |
- ADRENO_QUIRK_4GB_VA,
+ ADRENO_QUIRK_4GB_VA |
+ ADRENO_QUIRK_IFPC,
.inactive_period = DRM_MSM_INACTIVE_PERIOD,
.funcs = &a6xx_gpu_funcs,
.zapfw = "a640_zap.mdt",
@@ -1247,6 +1288,8 @@ static const struct adreno_info a6xx_gpus[] = {
.protect = &a630_protect,
.gmu_cgc_mode = 0x00020202,
.prim_fifo_threshold = 0x00200200,
+ .ifpc_reglist = &a630_ifpc_reglist,
+ .pwrup_reglist = &a640_pwrup_reglist,
},
}, {
.chip_ids = ADRENO_CHIP_IDS(0x06090000),
--
2.56.0
^ permalink raw reply related [flat|nested] 14+ messages in thread* Re: [PATCH 6/8] drm/msm/a6xx: Add IFPC tables for Adreno 640/680
2026-10-30 10:04 ` [PATCH 6/8] drm/msm/a6xx: Add IFPC tables for Adreno 640/680 Konrad Dybcio
@ 2026-10-30 10:19 ` sashiko-bot
0 siblings, 0 replies; 14+ messages in thread
From: sashiko-bot @ 2026-10-30 10:19 UTC (permalink / raw)
To: Konrad Dybcio; +Cc: dri-devel
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [Medium] drm/msm/a6xx: Adreno 680 omitted from WFI config in a6xx_set_hwcg()
- [Medium] drm/msm/a6xx: perfcntr SELECT regs lost across IFPC on A6xx
--
commit bea4548efa02d7657c1bf9c8e91e5ce4b34bbd1a
Author: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
drm/msm/a6xx: Add IFPC tables for Adreno 640/680
This commit adds the powerup register list for Adreno 640 and 680 and
enables ADRENO_QUIRK_IFPC along with the IFPC and powerup register tables
in the catalog entries for both GPUs.
> diff --git a/drivers/gpu/drm/msm/adreno/a6xx_catalog.c b/drivers/gpu/drm/msm/adreno/a6xx_catalog.c
> index 5e1d91a73a4e1..89829fa8f4f46 100644
> --- a/drivers/gpu/drm/msm/adreno/a6xx_catalog.c
> +++ b/drivers/gpu/drm/msm/adreno/a6xx_catalog.c
[ ... ]
> @@ -1115,7 +1152,8 @@ static const struct adreno_info a6xx_gpus[] = {
> },
> .gmem = SZ_1M,
> .quirks = ADRENO_QUIRK_HAS_CACHED_COHERENT |
> - ADRENO_QUIRK_4GB_VA,
> + ADRENO_QUIRK_4GB_VA |
> + ADRENO_QUIRK_IFPC,
> .inactive_period = DRM_MSM_INACTIVE_PERIOD,
> .funcs = &a6xx_gpu_funcs,
> .zapfw = "a640_zap.mdt",
> @@ -1124,6 +1162,8 @@ static const struct adreno_info a6xx_gpus[] = {
> .protect = &a630_protect,
> .gmu_cgc_mode = 0x00020202,
> .prim_fifo_threshold = 0x00180000,
> + .ifpc_reglist = &a630_ifpc_reglist,
> + .pwrup_reglist = &a640_pwrup_reglist,
[Severity: Medium]
When global performance counter streaming (MSM_PERFCNTR_STREAM) is active
via DRM_IOCTL_MSM_PERFCNTR_CONFIG, sample_worker() only inhibits IFPC while
reading the counter registers and re-enables IFPC between sampling
intervals, relying on pwrup_reglist to restore the configured SELECT
registers on IFPC exit.
However, in drivers/gpu/drm/msm/adreno/a6xx_gpu.c,
a6xx_perfcntr_configure() only populates pwrup_reglist_ptr on A7xx:
if (to_adreno_gpu(gpu)->info->family >= ADRENO_7XX_GEN1) {
WARN_ON(!a6xx_gpu->pwrup_reglist_emitted);
struct cpu_gpu_lock *lock = a6xx_gpu->pwrup_reglist_ptr;
...
With ADRENO_QUIRK_IFPC enabled on Adreno 640 and 680, won't GX power
collapse during IFPC reset the performance counter SELECT registers without
SQE restoring them when exiting IFPC?
> },
> .speedbins = ADRENO_SPEEDBINS(
> { 0, 0 },
> @@ -1238,7 +1278,8 @@ static const struct adreno_info a6xx_gpus[] = {
> },
> .gmem = SZ_2M,
> .quirks = ADRENO_QUIRK_HAS_CACHED_COHERENT |
> - ADRENO_QUIRK_4GB_VA,
> + ADRENO_QUIRK_4GB_VA |
> + ADRENO_QUIRK_IFPC,
> .inactive_period = DRM_MSM_INACTIVE_PERIOD,
> .funcs = &a6xx_gpu_funcs,
> .zapfw = "a640_zap.mdt",
> @@ -1247,6 +1288,8 @@ static const struct adreno_info a6xx_gpus[] = {
> .protect = &a630_protect,
> .gmu_cgc_mode = 0x00020202,
> .prim_fifo_threshold = 0x00200200,
> + .ifpc_reglist = &a630_ifpc_reglist,
> + .pwrup_reglist = &a640_pwrup_reglist,
[Severity: Medium]
During hardware initialization (hw_init() -> a6xx_set_hwcg()),
drivers/gpu/drm/msm/adreno/a6xx_gpu.c configures
REG_A6XX_GMU_CX_GMU_WFI_CONFIG based on the GPU revision:
/* TODO: Also A620 */
if (adreno_is_a640(adreno_gpu) || adreno_is_a650(adreno_gpu))
gmu_wfi_config = 0x2;
else
gmu_wfi_config = 0;
...
gmu_write(&a6xx_gpu->gmu, REG_A6XX_GMU_CX_GMU_WFI_CONFIG,
state ? gmu_wfi_config : 0);
Because adreno_is_a640() only matches revn 640 rather than
adreno_is_a640_family() (which matches ADRENO_6XX_GEN2 for both Adreno 640
and Adreno 680), Adreno 680 will program REG_A6XX_GMU_CX_GMU_WFI_CONFIG to
0 instead of 0x2 despite sharing a640_gmu.bin, a640_hwcg, gmu_cgc_mode, and
a640_pwrup_reglist with Adreno 640.
Should a6xx_set_hwcg() check adreno_is_a640_family(adreno_gpu) so that
Adreno 680 also sets gmu_wfi_config = 0x2 with IFPC enabled?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261030-topic-a6xx_ifpc-v1-0-6992841a9a32@oss.qualcomm.com?part=6
^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH RFT 7/8] drm/msm/a6xx: Add IFPC tables for Adreno 650 family
2026-10-30 10:03 [PATCH 0/8] IFPC support for A6xx Konrad Dybcio
` (5 preceding siblings ...)
2026-10-30 10:04 ` [PATCH 6/8] drm/msm/a6xx: Add IFPC tables for Adreno 640/680 Konrad Dybcio
@ 2026-10-30 10:04 ` Konrad Dybcio
2026-10-30 10:04 ` [PATCH RFT 8/8] drm/msm/a6xx: Wire up IFPC on A615-family GPUs Konrad Dybcio
7 siblings, 0 replies; 14+ messages in thread
From: Konrad Dybcio @ 2026-10-30 10:04 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, Konrad Dybcio
From: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
Fill out the register tables to enable IFPC on these SKUs.
Signed-off-by: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
---
drivers/gpu/drm/msm/adreno/a6xx_catalog.c | 122 +++++++++++++++++++++++++++++-
1 file changed, 118 insertions(+), 4 deletions(-)
diff --git a/drivers/gpu/drm/msm/adreno/a6xx_catalog.c b/drivers/gpu/drm/msm/adreno/a6xx_catalog.c
index 89829fa8f4f4..2d59f7735c90 100644
--- a/drivers/gpu/drm/msm/adreno/a6xx_catalog.c
+++ b/drivers/gpu/drm/msm/adreno/a6xx_catalog.c
@@ -851,6 +851,108 @@ static const u32 a640_pwrup_reglist_regs[] = {
DECLARE_ADRENO_REGLIST_LIST(a640_pwrup_reglist);
+/* A650-family IFPC state also includes CP_PROTECT registers 32-47. */
+static const u32 a650_ifpc_reglist_regs[] = {
+ REG_A6XX_CP_CHICKEN_DBG,
+ REG_A6XX_CP_DBG_ECO_CNTL,
+ REG_A6XX_CP_PROTECT_CNTL,
+ REG_A6XX_CP_PROTECT(0),
+ REG_A6XX_CP_PROTECT(1),
+ REG_A6XX_CP_PROTECT(2),
+ REG_A6XX_CP_PROTECT(3),
+ REG_A6XX_CP_PROTECT(4),
+ REG_A6XX_CP_PROTECT(5),
+ REG_A6XX_CP_PROTECT(6),
+ REG_A6XX_CP_PROTECT(7),
+ REG_A6XX_CP_PROTECT(8),
+ REG_A6XX_CP_PROTECT(9),
+ REG_A6XX_CP_PROTECT(10),
+ REG_A6XX_CP_PROTECT(11),
+ REG_A6XX_CP_PROTECT(12),
+ REG_A6XX_CP_PROTECT(13),
+ REG_A6XX_CP_PROTECT(14),
+ REG_A6XX_CP_PROTECT(15),
+ REG_A6XX_CP_PROTECT(16),
+ REG_A6XX_CP_PROTECT(17),
+ REG_A6XX_CP_PROTECT(18),
+ REG_A6XX_CP_PROTECT(19),
+ REG_A6XX_CP_PROTECT(20),
+ REG_A6XX_CP_PROTECT(21),
+ REG_A6XX_CP_PROTECT(22),
+ REG_A6XX_CP_PROTECT(23),
+ REG_A6XX_CP_PROTECT(24),
+ REG_A6XX_CP_PROTECT(25),
+ REG_A6XX_CP_PROTECT(26),
+ REG_A6XX_CP_PROTECT(27),
+ REG_A6XX_CP_PROTECT(28),
+ REG_A6XX_CP_PROTECT(29),
+ REG_A6XX_CP_PROTECT(30),
+ REG_A6XX_CP_PROTECT(31),
+ REG_A6XX_CP_AHB_CNTL,
+ REG_A6XX_CP_PROTECT(32),
+ REG_A6XX_CP_PROTECT(33),
+ REG_A6XX_CP_PROTECT(34),
+ REG_A6XX_CP_PROTECT(35),
+ REG_A6XX_CP_PROTECT(36),
+ REG_A6XX_CP_PROTECT(37),
+ REG_A6XX_CP_PROTECT(38),
+ REG_A6XX_CP_PROTECT(39),
+ REG_A6XX_CP_PROTECT(40),
+ REG_A6XX_CP_PROTECT(41),
+ REG_A6XX_CP_PROTECT(42),
+ REG_A6XX_CP_PROTECT(43),
+ REG_A6XX_CP_PROTECT(44),
+ REG_A6XX_CP_PROTECT(45),
+ REG_A6XX_CP_PROTECT(46),
+ REG_A6XX_CP_PROTECT(47),
+};
+
+DECLARE_ADRENO_REGLIST_LIST(a650_ifpc_reglist);
+
+/* A650-family restore state adds bicubic weights and UCHE_CMDQ_CONFIG. */
+static const u32 a650_pwrup_reglist_regs[] = {
+ REG_A6XX_VSC_ADDR_MODE_CNTL,
+ REG_A6XX_GRAS_ADDR_MODE_CNTL,
+ REG_A6XX_RB_ADDR_MODE_CNTL,
+ REG_A6XX_PC_ADDR_MODE_CNTL,
+ REG_A6XX_HLSQ_ADDR_MODE_CNTL,
+ REG_A6XX_VFD_ADDR_MODE_CNTL,
+ REG_A6XX_VPC_ADDR_MODE_CNTL,
+ REG_A6XX_UCHE_ADDR_MODE_CNTL,
+ REG_A6XX_SP_ADDR_MODE_CNTL,
+ REG_A6XX_TPL1_ADDR_MODE_CNTL,
+ REG_A6XX_UCHE_WRITE_RANGE_MAX,
+ REG_A6XX_UCHE_WRITE_RANGE_MAX + 1,
+ REG_A6XX_UCHE_TRAP_BASE,
+ REG_A6XX_UCHE_TRAP_BASE + 1,
+ REG_A6XX_UCHE_WRITE_THRU_BASE,
+ REG_A6XX_UCHE_WRITE_THRU_BASE + 1,
+ REG_A6XX_UCHE_GMEM_RANGE_MIN,
+ REG_A6XX_UCHE_GMEM_RANGE_MIN + 1,
+ REG_A6XX_UCHE_GMEM_RANGE_MAX,
+ REG_A6XX_UCHE_GMEM_RANGE_MAX + 1,
+ REG_A6XX_UCHE_FILTER_CNTL,
+ REG_A6XX_UCHE_CACHE_WAYS,
+ REG_A6XX_UCHE_MODE_CNTL,
+ REG_A6XX_RB_NC_MODE_CNTL,
+ REG_A6XX_TPL1_NC_MODE_CNTL,
+ REG_A6XX_SP_NC_MODE_CNTL,
+ REG_A6XX_PC_DBG_ECO_CNTL,
+ REG_A6XX_RB_CONTEXT_SWITCH_GMEM_SAVE_RESTORE_ENABLE,
+ REG_A6XX_UCHE_GBIF_GX_CONFIG,
+ REG_A6XX_UCHE_CLIENT_PF,
+ REG_A6XX_RBBM_GBIF_CLIENT_QOS_CNTL,
+ REG_A6XX_RBBM_PERFCTR_CNTL,
+ REG_A6XX_TPL1_BICUBIC_WEIGHTS_TABLE(0),
+ REG_A6XX_TPL1_BICUBIC_WEIGHTS_TABLE(1),
+ REG_A6XX_TPL1_BICUBIC_WEIGHTS_TABLE(2),
+ REG_A6XX_TPL1_BICUBIC_WEIGHTS_TABLE(3),
+ REG_A6XX_TPL1_BICUBIC_WEIGHTS_TABLE(4),
+ REG_A6XX_UCHE_CMDQ_CONFIG,
+};
+
+DECLARE_ADRENO_REGLIST_LIST(a650_pwrup_reglist);
+
static const struct adreno_info a6xx_gpus[] = {
{
.chip_ids = ADRENO_CHIP_IDS(0x06010000),
@@ -1068,7 +1170,8 @@ static const struct adreno_info a6xx_gpus[] = {
.gmem = SZ_512K,
.inactive_period = DRM_MSM_INACTIVE_PERIOD,
.quirks = ADRENO_QUIRK_HAS_CACHED_COHERENT |
- ADRENO_QUIRK_HAS_HW_APRIV,
+ ADRENO_QUIRK_HAS_HW_APRIV |
+ ADRENO_QUIRK_IFPC,
.funcs = &a6xx_gpu_funcs,
.zapfw = "a620_zap.mbn",
.a6xx = &(const struct a6xx_info) {
@@ -1077,6 +1180,8 @@ static const struct adreno_info a6xx_gpus[] = {
.gbif_cx = a640_gbif,
.gmu_cgc_mode = 0x00020200,
.prim_fifo_threshold = 0x00010000,
+ .ifpc_reglist = &a650_ifpc_reglist,
+ .pwrup_reglist = &a650_pwrup_reglist,
},
.speedbins = ADRENO_SPEEDBINS(
{ 0, 0 },
@@ -1180,7 +1285,8 @@ static const struct adreno_info a6xx_gpus[] = {
.gmem = SZ_1M + SZ_128K,
.inactive_period = DRM_MSM_INACTIVE_PERIOD,
.quirks = ADRENO_QUIRK_HAS_CACHED_COHERENT |
- ADRENO_QUIRK_HAS_HW_APRIV,
+ ADRENO_QUIRK_HAS_HW_APRIV |
+ ADRENO_QUIRK_IFPC,
.funcs = &a6xx_gpu_funcs,
.zapfw = "a650_zap.mdt",
.a6xx = &(const struct a6xx_info) {
@@ -1189,6 +1295,8 @@ static const struct adreno_info a6xx_gpus[] = {
.gbif_cx = a640_gbif,
.gmu_cgc_mode = 0x00020202,
.prim_fifo_threshold = 0x00300200,
+ .ifpc_reglist = &a650_ifpc_reglist,
+ .pwrup_reglist = &a650_pwrup_reglist,
},
.speedbins = ADRENO_SPEEDBINS(
{ 0, 0 },
@@ -1207,7 +1315,8 @@ static const struct adreno_info a6xx_gpus[] = {
.gmem = SZ_1M + SZ_512K,
.inactive_period = DRM_MSM_INACTIVE_PERIOD,
.quirks = ADRENO_QUIRK_HAS_CACHED_COHERENT |
- ADRENO_QUIRK_HAS_HW_APRIV,
+ ADRENO_QUIRK_HAS_HW_APRIV |
+ ADRENO_QUIRK_IFPC,
.funcs = &a6xx_gpu_funcs,
.zapfw = "a660_zap.mdt",
.a6xx = &(const struct a6xx_info) {
@@ -1216,6 +1325,8 @@ static const struct adreno_info a6xx_gpus[] = {
.gbif_cx = a640_gbif,
.gmu_cgc_mode = 0x00020000,
.prim_fifo_threshold = 0x00300200,
+ .ifpc_reglist = &a650_ifpc_reglist,
+ .pwrup_reglist = &a650_pwrup_reglist,
},
}, {
.chip_ids = ADRENO_CHIP_IDS(0x06060300),
@@ -1227,7 +1338,8 @@ static const struct adreno_info a6xx_gpus[] = {
.gmem = SZ_1M + SZ_512K,
.inactive_period = DRM_MSM_INACTIVE_PERIOD,
.quirks = ADRENO_QUIRK_HAS_CACHED_COHERENT |
- ADRENO_QUIRK_HAS_HW_APRIV,
+ ADRENO_QUIRK_HAS_HW_APRIV |
+ ADRENO_QUIRK_IFPC,
.funcs = &a6xx_gpu_funcs,
.a6xx = &(const struct a6xx_info) {
.hwcg = a690_hwcg,
@@ -1235,6 +1347,8 @@ static const struct adreno_info a6xx_gpus[] = {
.gbif_cx = a640_gbif,
.gmu_cgc_mode = 0x00020200,
.prim_fifo_threshold = 0x00300200,
+ .ifpc_reglist = &a650_ifpc_reglist,
+ .pwrup_reglist = &a650_pwrup_reglist,
},
.speedbins = ADRENO_SPEEDBINS(
{ 0, 0 },
--
2.56.0
^ permalink raw reply related [flat|nested] 14+ messages in thread* [PATCH RFT 8/8] drm/msm/a6xx: Wire up IFPC on A615-family GPUs
2026-10-30 10:03 [PATCH 0/8] IFPC support for A6xx Konrad Dybcio
` (6 preceding siblings ...)
2026-10-30 10:04 ` [PATCH RFT 7/8] drm/msm/a6xx: Add IFPC tables for Adreno 650 family Konrad Dybcio
@ 2026-10-30 10:04 ` Konrad Dybcio
2026-10-30 10:29 ` sashiko-bot
7 siblings, 1 reply; 14+ messages in thread
From: Konrad Dybcio @ 2026-10-30 10:04 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, Konrad Dybcio
From: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
They reuse the existing tables, so just wire them up.
Signed-off-by: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
---
drivers/gpu/drm/msm/adreno/a6xx_catalog.c | 24 ++++++++++++++++++++----
1 file changed, 20 insertions(+), 4 deletions(-)
diff --git a/drivers/gpu/drm/msm/adreno/a6xx_catalog.c b/drivers/gpu/drm/msm/adreno/a6xx_catalog.c
index 2d59f7735c90..257771d4eb4d 100644
--- a/drivers/gpu/drm/msm/adreno/a6xx_catalog.c
+++ b/drivers/gpu/drm/msm/adreno/a6xx_catalog.c
@@ -1012,7 +1012,8 @@ static const struct adreno_info a6xx_gpus[] = {
[ADRENO_FW_GMU] = "a630_gmu.bin",
},
.gmem = SZ_512K,
- .quirks = ADRENO_QUIRK_4GB_VA,
+ .quirks = ADRENO_QUIRK_4GB_VA |
+ ADRENO_QUIRK_IFPC,
.inactive_period = DRM_MSM_INACTIVE_PERIOD,
.funcs = &a6xx_gpu_funcs,
.zapfw = "a615_zap.mdt",
@@ -1021,6 +1022,8 @@ static const struct adreno_info a6xx_gpus[] = {
.protect = &a630_protect,
.gmu_cgc_mode = 0x00000222,
.prim_fifo_threshold = 0x0018000,
+ .ifpc_reglist = &a630_ifpc_reglist,
+ .pwrup_reglist = &a640_pwrup_reglist,
},
.speedbins = ADRENO_SPEEDBINS(
/*
@@ -1044,7 +1047,8 @@ static const struct adreno_info a6xx_gpus[] = {
.gmem = SZ_512K,
.inactive_period = DRM_MSM_INACTIVE_PERIOD,
.quirks = ADRENO_QUIRK_HAS_CACHED_COHERENT |
- ADRENO_QUIRK_4GB_VA,
+ ADRENO_QUIRK_4GB_VA |
+ ADRENO_QUIRK_IFPC,
.funcs = &a6xx_gpu_funcs,
.zapfw = "a615_zap.mbn",
.a6xx = &(const struct a6xx_info) {
@@ -1052,6 +1056,8 @@ static const struct adreno_info a6xx_gpus[] = {
.protect = &a630_protect,
.gmu_cgc_mode = 0x00000222,
.prim_fifo_threshold = 0x00180000,
+ .ifpc_reglist = &a630_ifpc_reglist,
+ .pwrup_reglist = &a640_pwrup_reglist,
},
.speedbins = ADRENO_SPEEDBINS(
{ 0, 0 },
@@ -1071,12 +1077,15 @@ static const struct adreno_info a6xx_gpus[] = {
.gmem = SZ_512K,
.inactive_period = DRM_MSM_INACTIVE_PERIOD,
.quirks = ADRENO_QUIRK_HAS_CACHED_COHERENT |
- ADRENO_QUIRK_4GB_VA,
+ ADRENO_QUIRK_4GB_VA |
+ ADRENO_QUIRK_IFPC,
.funcs = &a6xx_gpu_funcs,
.a6xx = &(const struct a6xx_info) {
.protect = &a630_protect,
.gmu_cgc_mode = 0x00000222,
.prim_fifo_threshold = 0x00180000,
+ .ifpc_reglist = &a630_ifpc_reglist,
+ .pwrup_reglist = &a640_pwrup_reglist,
},
.speedbins = ADRENO_SPEEDBINS(
{ 0, 0 },
@@ -1102,6 +1111,8 @@ static const struct adreno_info a6xx_gpus[] = {
.protect = &a630_protect,
.gmu_cgc_mode = 0x00000222,
.prim_fifo_threshold = 0x00018000,
+ .ifpc_reglist = &a630_ifpc_reglist,
+ .pwrup_reglist = &a640_pwrup_reglist,
},
.speedbins = ADRENO_SPEEDBINS(
{ 0, 0 },
@@ -1127,6 +1138,8 @@ static const struct adreno_info a6xx_gpus[] = {
.protect = &a630_protect,
.gmu_cgc_mode = 0x00000222,
.prim_fifo_threshold = 0x00018000,
+ .ifpc_reglist = &a630_ifpc_reglist,
+ .pwrup_reglist = &a640_pwrup_reglist,
},
.speedbins = ADRENO_SPEEDBINS(
{ 0, 0 },
@@ -1143,7 +1156,8 @@ static const struct adreno_info a6xx_gpus[] = {
},
.gmem = SZ_512K,
.quirks = ADRENO_QUIRK_HAS_CACHED_COHERENT |
- ADRENO_QUIRK_4GB_VA,
+ ADRENO_QUIRK_4GB_VA |
+ ADRENO_QUIRK_IFPC,
.inactive_period = DRM_MSM_INACTIVE_PERIOD,
.funcs = &a6xx_gpu_funcs,
.zapfw = "a615_zap.mdt",
@@ -1152,6 +1166,8 @@ static const struct adreno_info a6xx_gpus[] = {
.protect = &a630_protect,
.gmu_cgc_mode = 0x00000222,
.prim_fifo_threshold = 0x00018000,
+ .ifpc_reglist = &a630_ifpc_reglist,
+ .pwrup_reglist = &a640_pwrup_reglist,
},
.speedbins = ADRENO_SPEEDBINS(
{ 0, 0 },
--
2.56.0
^ permalink raw reply related [flat|nested] 14+ messages in thread* Re: [PATCH RFT 8/8] drm/msm/a6xx: Wire up IFPC on A615-family GPUs
2026-10-30 10:04 ` [PATCH RFT 8/8] drm/msm/a6xx: Wire up IFPC on A615-family GPUs Konrad Dybcio
@ 2026-10-30 10:29 ` sashiko-bot
0 siblings, 0 replies; 14+ messages in thread
From: sashiko-bot @ 2026-10-30 10:29 UTC (permalink / raw)
To: Konrad Dybcio; +Cc: dri-devel
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [High] drm/msm/a6xx: missing revn 618 on sm7150 and revn 615 in adreno_is_a615_family()
- [Medium] drm/msm/a6xx: QoS register mismatch between a640_pwrup_reglist and hw_init()
--
commit c21799f522c1e10807d8ac16ef0e01ab2fd9a092
Author: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
drm/msm/a6xx: Wire up IFPC on A615-family GPUs
This commit enables ADRENO_QUIRK_IFPC and wires up a630_ifpc_reglist and
a640_pwrup_reglist for the A615-family entries in a6xx_gpus[].
> diff --git a/drivers/gpu/drm/msm/adreno/a6xx_catalog.c b/drivers/gpu/drm/msm/adreno/a6xx_catalog.c
> index 2d59f7735c907..257771d4eb4d5 100644
> --- a/drivers/gpu/drm/msm/adreno/a6xx_catalog.c
> +++ b/drivers/gpu/drm/msm/adreno/a6xx_catalog.c
> @@ -1012,7 +1012,8 @@ static const struct adreno_info a6xx_gpus[] = {
> [ADRENO_FW_GMU] = "a630_gmu.bin",
> },
> .gmem = SZ_512K,
> - .quirks = ADRENO_QUIRK_4GB_VA,
> + .quirks = ADRENO_QUIRK_4GB_VA |
> + ADRENO_QUIRK_IFPC,
> .inactive_period = DRM_MSM_INACTIVE_PERIOD,
> .funcs = &a6xx_gpu_funcs,
> .zapfw = "a615_zap.mdt",
> @@ -1021,6 +1022,8 @@ static const struct adreno_info a6xx_gpus[] = {
> .protect = &a630_protect,
> .gmu_cgc_mode = 0x00000222,
> .prim_fifo_threshold = 0x0018000,
> + .ifpc_reglist = &a630_ifpc_reglist,
> + .pwrup_reglist = &a640_pwrup_reglist,
[Severity: Medium]
Should the A615-family entries in a6xx_gpus[] either use a630_pwrup_reglist
or have hw_init() updated to program REG_A6XX_RBBM_GBIF_CLIENT_QOS_CNTL for
this family?
In drivers/gpu/drm/msm/adreno/a6xx_gpu.c:hw_init(), all six A615-family
entries have .family = ADRENO_6XX_GEN1 and fall into the final else branch,
which writes 0x3 to REG_A6XX_RBBM_VBIF_CLIENT_QOS_CNTL and leaves
REG_A6XX_RBBM_GBIF_CLIENT_QOS_CNTL unprogrammed:
drivers/gpu/drm/msm/adreno/a6xx_gpu.c:hw_init() {
...
if (adreno_is_a610_family(adreno_gpu) ||
adreno_is_a640_family(adreno_gpu) ||
adreno_is_a650_family(adreno_gpu)) {
gpu_write(gpu, REG_A6XX_RBBM_GBIF_CLIENT_QOS_CNTL, 0x3);
} else if (adreno_is_a7xx(adreno_gpu)) {
gpu_write(gpu, REG_A6XX_RBBM_GBIF_CLIENT_QOS_CNTL, 0x2120212);
} else {
gpu_write(gpu, REG_A6XX_RBBM_VBIF_CLIENT_QOS_CNTL, 0x3);
}
...
}
Later in hw_init(), a6xx_patch_pwrup_reglist() snapshots the current values
of the registers listed in info->a6xx->pwrup_reglist via gpu_read() so the CP
can restore them after each IFPC GX power collapse:
drivers/gpu/drm/msm/adreno/a6xx_gpu.c:a6xx_patch_pwrup_reglist() {
...
reglist = adreno_gpu->info->a6xx->pwrup_reglist;
if (reglist) {
for (i = 0; i < reglist->count; i++) {
*dest++ = reglist->regs[i];
*dest++ = gpu_read(gpu, reglist->regs[i]);
}
lock->list_length += reglist->count * 2;
}
...
}
Because a640_pwrup_reglist_regs lists REG_A6XX_RBBM_GBIF_CLIENT_QOS_CNTL
rather than REG_A6XX_RBBM_VBIF_CLIENT_QOS_CNTL (which is the only difference
between a640_pwrup_reglist and a630_pwrup_reglist), won't every IFPC cycle
lose the programmed REG_A6XX_RBBM_VBIF_CLIENT_QOS_CNTL value and replay the
uninitialized REG_A6XX_RBBM_GBIF_CLIENT_QOS_CNTL register instead?
> },
> .speedbins = ADRENO_SPEEDBINS(
> /*
> @@ -1044,7 +1047,8 @@ static const struct adreno_info a6xx_gpus[] = {
> .gmem = SZ_512K,
> .inactive_period = DRM_MSM_INACTIVE_PERIOD,
> .quirks = ADRENO_QUIRK_HAS_CACHED_COHERENT |
> - ADRENO_QUIRK_4GB_VA,
> + ADRENO_QUIRK_4GB_VA |
> + ADRENO_QUIRK_IFPC,
[Severity: High]
Does enabling ADRENO_QUIRK_IFPC on the qcom,sm7150 (0x06010800) and A615
(0x06010500) entries cause them to run IFPC with A630 PDC and GMU clock
gating settings instead of A618/A615-family settings?
The qcom,sm7150 entry in drivers/gpu/drm/msm/adreno/a6xx_catalog.c omits
.revn = 618, unlike the generic 0x06010800 entry right below it. In addition,
adreno_is_a615_family() in drivers/gpu/drm/msm/adreno/adreno_gpu.h does not
check for revn 615:
drivers/gpu/drm/msm/adreno/adreno_gpu.h:
/* TODO: 615/616 */
static inline int adreno_is_a615_family(const struct adreno_gpu *gpu)
{
return adreno_is_a618(gpu) ||
adreno_is_a619(gpu);
}
Because adreno_is_a618() checks adreno_is_revn(gpu, 618), both
adreno_is_a618() and adreno_is_a615_family() evaluate to false on
qcom,sm7150. In drivers/gpu/drm/msm/adreno/a6xx_gmu.c:a6xx_gmu_rpmh_init(),
this causes the driver to program A630 PDC sleep/wakeup values instead of
A618 values:
drivers/gpu/drm/msm/adreno/a6xx_gmu.c:a6xx_gmu_rpmh_init() {
...
else if (adreno_is_a618(adreno_gpu) || adreno_is_a640_family(adreno_gpu))
pdc_address_offset = 0x30090;
else if (adreno_is_a619(adreno_gpu))
pdc_address_offset = 0x300a0;
else
pdc_address_offset = 0x30080;
...
if (adreno_is_a618(adreno_gpu) || adreno_is_a619(adreno_gpu) ||
adreno_is_a650_family(adreno_gpu))
pdc_write(pdcptr, REG_A6XX_PDC_GPU_TCS3_CMD0_DATA + 4, 0x2);
else
pdc_write(pdcptr, REG_A6XX_PDC_GPU_TCS3_CMD0_DATA + 4, 0x3);
...
}
With ADRENO_QUIRK_IFPC setting gmu->idle_level = GMU_IDLE_STATE_IFPC, the GMU
will execute those PDC sequences on every IFPC power collapse. Missing revn
618 on qcom,sm7150 and revn 615 in adreno_is_a615_family() also causes
a6xx_set_hwcg() in drivers/gpu/drm/msm/adreno/a6xx_gpu.c to program A630 GMU
CGC delay and hysteresis values (0x10111 / 0x5555) instead of A615-family
values (0x111 / 0x555), and causes a6xx_hfi_send_bw_table() to skip
a618_build_bw_table() on qcom,sm7150.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261030-topic-a6xx_ifpc-v1-0-6992841a9a32@oss.qualcomm.com?part=8
^ permalink raw reply [flat|nested] 14+ messages in thread