[PATCH 4/8] drm/msm/a6xx: Add A6xx IFPC support
From: Konrad Dybcio
Date: Fri Oct 30 2026 - 06:12:21 EST
From: Konrad Dybcio <konrad.dybcio@xxxxxxxxxxxxxxxx>
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@xxxxxxxxxxxxxxxx>
---
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