[PATCH v2 06/17] drm/msm/a6xx: Fix gpu init from secure world
From: Akhil P Oommen
Date: Thu Mar 26 2026 - 20:17:34 EST
A7XX_GEN2 and newer GPUs requires initialization of few configurations
related to features/power from secure world. The SCM call to do this
should be triggered after GDSC and clocks are enabled. So, keep this
sequence to a6xx_gmu_resume instead of the probe.
Also, simplify the error handling in a6xx_gmu_resume() using 'goto'
labels.
Fixes: 14b27d5df3ea ("drm/msm/a7xx: Initialize a750 "software fuse"")
Signed-off-by: Akhil P Oommen <akhilpo@xxxxxxxxxxxxxxxx>
---
drivers/gpu/drm/msm/adreno/a6xx_gmu.c | 93 +++++++++++++++++++++++++++++------
drivers/gpu/drm/msm/adreno/a6xx_gmu.h | 2 +
drivers/gpu/drm/msm/adreno/a6xx_gpu.c | 59 ----------------------
3 files changed, 80 insertions(+), 74 deletions(-)
diff --git a/drivers/gpu/drm/msm/adreno/a6xx_gmu.c b/drivers/gpu/drm/msm/adreno/a6xx_gmu.c
index b41dbca1ebc6..1b44b9e21ad8 100644
--- a/drivers/gpu/drm/msm/adreno/a6xx_gmu.c
+++ b/drivers/gpu/drm/msm/adreno/a6xx_gmu.c
@@ -3,6 +3,7 @@
#include <linux/bitfield.h>
#include <linux/clk.h>
+#include <linux/firmware/qcom/qcom_scm.h>
#include <linux/interconnect.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
@@ -1191,6 +1192,65 @@ static void a6xx_gmu_set_initial_bw(struct msm_gpu *gpu, struct a6xx_gmu *gmu)
dev_pm_opp_put(gpu_opp);
}
+static int a6xx_gmu_secure_init(struct a6xx_gpu *a6xx_gpu)
+{
+ struct adreno_gpu *adreno_gpu = &a6xx_gpu->base;
+ struct msm_gpu *gpu = &adreno_gpu->base;
+ struct a6xx_gmu *gmu = &a6xx_gpu->gmu;
+ u32 fuse_val;
+ int ret;
+
+ if (test_bit(GMU_STATUS_SECURE_INIT, &gmu->status))
+ return 0;
+
+ if (adreno_is_a750(adreno_gpu) || adreno_is_a8xx(adreno_gpu)) {
+ /*
+ * Assume that if qcom scm isn't available, that whatever
+ * replacement allows writing the fuse register ourselves.
+ * Users of alternative firmware need to make sure this
+ * register is writeable or indicate that it's not somehow.
+ * Print a warning because if you mess this up you're about to
+ * crash horribly.
+ */
+ if (!qcom_scm_is_available()) {
+ dev_warn_once(gpu->dev->dev,
+ "SCM is not available, poking fuse register\n");
+ a6xx_llc_write(a6xx_gpu, REG_A7XX_CX_MISC_SW_FUSE_VALUE,
+ A7XX_CX_MISC_SW_FUSE_VALUE_RAYTRACING |
+ A7XX_CX_MISC_SW_FUSE_VALUE_FASTBLEND |
+ A7XX_CX_MISC_SW_FUSE_VALUE_LPAC);
+ adreno_gpu->has_ray_tracing = true;
+ goto done;
+ }
+
+ ret = qcom_scm_gpu_init_regs(QCOM_SCM_GPU_ALWAYS_EN_REQ |
+ QCOM_SCM_GPU_TSENSE_EN_REQ);
+ if (ret) {
+ dev_warn_once(gpu->dev->dev,
+ "SCM call failed\n");
+ return ret;
+ }
+
+ /*
+ * On A7XX_GEN3 and newer, raytracing may be disabled by the
+ * firmware, find out whether that's the case. The scm call
+ * above sets the fuse register.
+ */
+ fuse_val = a6xx_llc_read(a6xx_gpu,
+ REG_A7XX_CX_MISC_SW_FUSE_VALUE);
+ adreno_gpu->has_ray_tracing =
+ !!(fuse_val & A7XX_CX_MISC_SW_FUSE_VALUE_RAYTRACING);
+ } else if (adreno_is_a740(adreno_gpu)) {
+ /* Raytracing is always enabled on a740 */
+ adreno_gpu->has_ray_tracing = true;
+ }
+
+done:
+ set_bit(GMU_STATUS_SECURE_INIT, &gmu->status);
+ return 0;
+}
+
+
int a6xx_gmu_resume(struct a6xx_gpu *a6xx_gpu)
{
struct adreno_gpu *adreno_gpu = &a6xx_gpu->base;
@@ -1219,11 +1279,12 @@ int a6xx_gmu_resume(struct a6xx_gpu *a6xx_gpu)
clk_set_rate(gmu->hub_clk, adreno_is_a740_family(adreno_gpu) ?
200000000 : 150000000);
ret = clk_bulk_prepare_enable(gmu->nr_clocks, gmu->clocks);
- if (ret) {
- pm_runtime_put(gmu->gxpd);
- pm_runtime_put(gmu->dev);
- return ret;
- }
+ if (ret)
+ goto rpm_put;
+
+ ret = a6xx_gmu_secure_init(a6xx_gpu);
+ if (ret)
+ goto disable_clk;
/* Read the slice info on A8x GPUs */
a8xx_gpu_get_slice_info(gpu);
@@ -1253,11 +1314,11 @@ int a6xx_gmu_resume(struct a6xx_gpu *a6xx_gpu)
ret = a6xx_gmu_fw_start(gmu, status);
if (ret)
- goto out;
+ goto disable_irq;
ret = a6xx_hfi_start(gmu, status);
if (ret)
- goto out;
+ goto disable_irq;
/*
* Turn on the GMU firmware fault interrupt after we know the boot
@@ -1270,14 +1331,16 @@ int a6xx_gmu_resume(struct a6xx_gpu *a6xx_gpu)
/* Set the GPU to the current freq */
a6xx_gmu_set_initial_freq(gpu, gmu);
-out:
- /* On failure, shut down the GMU to leave it in a good state */
- if (ret) {
- disable_irq(gmu->gmu_irq);
- a6xx_rpmh_stop(gmu);
- pm_runtime_put(gmu->gxpd);
- pm_runtime_put(gmu->dev);
- }
+ return 0;
+
+disable_irq:
+ disable_irq(gmu->gmu_irq);
+ a6xx_rpmh_stop(gmu);
+disable_clk:
+ clk_bulk_disable_unprepare(gmu->nr_clocks, gmu->clocks);
+rpm_put:
+ pm_runtime_put(gmu->gxpd);
+ pm_runtime_put(gmu->dev);
return ret;
}
diff --git a/drivers/gpu/drm/msm/adreno/a6xx_gmu.h b/drivers/gpu/drm/msm/adreno/a6xx_gmu.h
index 9f09daf45ab2..0cd8ae1b4f5c 100644
--- a/drivers/gpu/drm/msm/adreno/a6xx_gmu.h
+++ b/drivers/gpu/drm/msm/adreno/a6xx_gmu.h
@@ -130,6 +130,8 @@ struct a6xx_gmu {
#define GMU_STATUS_PDC_SLEEP 1
/* To track Perfcounter OOB set status */
#define GMU_STATUS_OOB_PERF_SET 2
+/* To track whether secure world init was done */
+#define GMU_STATUS_SECURE_INIT 3
unsigned long status;
};
diff --git a/drivers/gpu/drm/msm/adreno/a6xx_gpu.c b/drivers/gpu/drm/msm/adreno/a6xx_gpu.c
index 8718919c7e19..5cddfc03828f 100644
--- a/drivers/gpu/drm/msm/adreno/a6xx_gpu.c
+++ b/drivers/gpu/drm/msm/adreno/a6xx_gpu.c
@@ -10,7 +10,6 @@
#include <linux/bitfield.h>
#include <linux/devfreq.h>
-#include <linux/firmware/qcom/qcom_scm.h>
#include <linux/pm_domain.h>
#include <linux/soc/qcom/llcc-qcom.h>
@@ -2160,56 +2159,6 @@ static void a6xx_llc_slices_init(struct platform_device *pdev,
a6xx_gpu->llc_mmio = ERR_PTR(-EINVAL);
}
-static int a7xx_cx_mem_init(struct a6xx_gpu *a6xx_gpu)
-{
- struct adreno_gpu *adreno_gpu = &a6xx_gpu->base;
- struct msm_gpu *gpu = &adreno_gpu->base;
- u32 fuse_val;
- int ret;
-
- if (adreno_is_a750(adreno_gpu) || adreno_is_a8xx(adreno_gpu)) {
- /*
- * Assume that if qcom scm isn't available, that whatever
- * replacement allows writing the fuse register ourselves.
- * Users of alternative firmware need to make sure this
- * register is writeable or indicate that it's not somehow.
- * Print a warning because if you mess this up you're about to
- * crash horribly.
- */
- if (!qcom_scm_is_available()) {
- dev_warn_once(gpu->dev->dev,
- "SCM is not available, poking fuse register\n");
- a6xx_llc_write(a6xx_gpu, REG_A7XX_CX_MISC_SW_FUSE_VALUE,
- A7XX_CX_MISC_SW_FUSE_VALUE_RAYTRACING |
- A7XX_CX_MISC_SW_FUSE_VALUE_FASTBLEND |
- A7XX_CX_MISC_SW_FUSE_VALUE_LPAC);
- adreno_gpu->has_ray_tracing = true;
- return 0;
- }
-
- ret = qcom_scm_gpu_init_regs(QCOM_SCM_GPU_ALWAYS_EN_REQ |
- QCOM_SCM_GPU_TSENSE_EN_REQ);
- if (ret)
- return ret;
-
- /*
- * On A7XX_GEN3 and newer, raytracing may be disabled by the
- * firmware, find out whether that's the case. The scm call
- * above sets the fuse register.
- */
- fuse_val = a6xx_llc_read(a6xx_gpu,
- REG_A7XX_CX_MISC_SW_FUSE_VALUE);
- adreno_gpu->has_ray_tracing =
- !!(fuse_val & A7XX_CX_MISC_SW_FUSE_VALUE_RAYTRACING);
- } else if (adreno_is_a740(adreno_gpu)) {
- /* Raytracing is always enabled on a740 */
- adreno_gpu->has_ray_tracing = true;
- }
-
- return 0;
-}
-
-
#define GBIF_CLIENT_HALT_MASK BIT(0)
#define GBIF_ARB_HALT_MASK BIT(1)
#define VBIF_XIN_HALT_CTRL0_MASK GENMASK(3, 0)
@@ -2705,14 +2654,6 @@ static struct msm_gpu *a6xx_gpu_init(struct drm_device *dev)
return ERR_PTR(ret);
}
- if (adreno_is_a7xx(adreno_gpu) || adreno_is_a8xx(adreno_gpu)) {
- ret = a7xx_cx_mem_init(a6xx_gpu);
- if (ret) {
- a6xx_destroy(&(a6xx_gpu->base.base));
- return ERR_PTR(ret);
- }
- }
-
adreno_gpu->uche_trap_base = 0x1fffffffff000ull;
msm_mmu_set_fault_handler(to_msm_vm(gpu->vm)->mmu, gpu,
--
2.51.0