[PATCH] drm/etnaviv: hold the core in reset while runtime suspended
From: Jerome Brunet
Date: Fri Oct 30 2026 - 07:39:53 EST
The reset is asserted at probe and released once, in etnaviv_gpu_init(),
and is never asserted again. Runtime suspend only stops the clocks, and the
power domain may then cut power to the core. On the next runtime resume,
the domain powers the core up again while the reset is still deasserted.
On the Amlogic A311D NPU, the power domain lifts isolation on a core that
is out of reset, which locks up the SoC on the first runtime resume after
a runtime suspend. Toggling the reset again solves the problem.
>From an existing comment in the driver, the reset must be deasserted with
the clock running, so handle it together with the clocks. Rename the clock
helpers accordingly, as they now handle the reset as well. This way the
reset is properly handled whether or not CONFIG_PM is enabled.
Fixes: 6bde14ba5f7e ("drm/etnaviv: add optional reset support")
Signed-off-by: Jerome Brunet <jbrunet@xxxxxxxxxxxx>
---
This was found while running the MobileNetV1 demo with Teflon on the
Khadas VIM3 (Amlogic A311D).
---
drivers/gpu/drm/etnaviv/etnaviv_gpu.c | 64 +++++++++++++++--------------------
1 file changed, 28 insertions(+), 36 deletions(-)
diff --git a/drivers/gpu/drm/etnaviv/etnaviv_gpu.c b/drivers/gpu/drm/etnaviv/etnaviv_gpu.c
index e75dab5e407d..b372f454f676 100644
--- a/drivers/gpu/drm/etnaviv/etnaviv_gpu.c
+++ b/drivers/gpu/drm/etnaviv/etnaviv_gpu.c
@@ -175,29 +175,6 @@ int etnaviv_gpu_get_param(struct etnaviv_gpu *gpu, u32 param, u64 *value)
return 0;
}
-static int etnaviv_gpu_reset_deassert(struct etnaviv_gpu *gpu)
-{
- int ret;
-
- /*
- * 32 core clock cycles (slowest clock) required before deassertion
- * 1 microsecond might match all implementations without computation
- */
- usleep_range(1, 2);
-
- ret = reset_control_deassert(gpu->rst);
- if (ret)
- return ret;
-
- /*
- * 128 core clock cycles (slowest clock) required before any activity on AHB
- * 1 microsecond might match all implementations without computation
- */
- usleep_range(1, 2);
-
- return 0;
-}
-
static inline bool etnaviv_is_model_rev(struct etnaviv_gpu *gpu, u32 model, u32 revision)
{
return gpu->identity.model == model &&
@@ -842,12 +819,6 @@ int etnaviv_gpu_init(struct etnaviv_gpu *gpu)
goto pm_put;
}
- ret = etnaviv_gpu_reset_deassert(gpu);
- if (ret) {
- dev_err(gpu->dev, "GPU reset deassert failed\n");
- goto fail;
- }
-
etnaviv_hw_identify(gpu);
if (gpu->identity.model == 0) {
@@ -1649,7 +1620,7 @@ static irqreturn_t irq_handler(int irq, void *data)
return ret;
}
-static int etnaviv_gpu_clk_enable(struct etnaviv_gpu *gpu)
+static int etnaviv_gpu_clk_rst_enable(struct etnaviv_gpu *gpu)
{
int ret;
@@ -1669,8 +1640,26 @@ static int etnaviv_gpu_clk_enable(struct etnaviv_gpu *gpu)
if (ret)
goto disable_clk_core;
+ /*
+ * 32 core clock cycles (slowest clock) required before deassertion
+ * 1 microsecond might match all implementations without computation
+ */
+ usleep_range(1, 2);
+
+ ret = reset_control_deassert(gpu->rst);
+ if (ret)
+ goto disable_clk_shader;
+
+ /*
+ * 128 core clock cycles (slowest clock) required before any activity on AHB
+ * 1 microsecond might match all implementations without computation
+ */
+ usleep_range(1, 2);
+
return 0;
+disable_clk_shader:
+ clk_disable_unprepare(gpu->clk_shader);
disable_clk_core:
clk_disable_unprepare(gpu->clk_core);
disable_clk_bus:
@@ -1681,8 +1670,11 @@ static int etnaviv_gpu_clk_enable(struct etnaviv_gpu *gpu)
return ret;
}
-static int etnaviv_gpu_clk_disable(struct etnaviv_gpu *gpu)
+static int etnaviv_gpu_clk_rst_disable(struct etnaviv_gpu *gpu)
{
+ /* The core may lose power: keep it in reset until it is clocked again */
+ reset_control_assert(gpu->rst);
+
clk_disable_unprepare(gpu->clk_shader);
clk_disable_unprepare(gpu->clk_core);
clk_disable_unprepare(gpu->clk_bus);
@@ -1817,7 +1809,7 @@ static int etnaviv_gpu_bind(struct device *dev, struct device *master,
goto out_workqueue;
if (!IS_ENABLED(CONFIG_PM)) {
- ret = etnaviv_gpu_clk_enable(gpu);
+ ret = etnaviv_gpu_clk_rst_enable(gpu);
if (ret < 0)
goto out_sched;
}
@@ -1863,7 +1855,7 @@ static void etnaviv_gpu_unbind(struct device *dev, struct device *master,
pm_runtime_put_sync_suspend(gpu->dev);
} else {
etnaviv_gpu_hw_suspend(gpu);
- etnaviv_gpu_clk_disable(gpu);
+ etnaviv_gpu_clk_rst_disable(gpu);
}
if (gpu->mmu_context)
@@ -2013,7 +2005,7 @@ static int etnaviv_gpu_rpm_suspend(struct device *dev)
gpu->state = ETNA_GPU_STATE_IDENTIFIED;
- return etnaviv_gpu_clk_disable(gpu);
+ return etnaviv_gpu_clk_rst_disable(gpu);
}
static int etnaviv_gpu_rpm_resume(struct device *dev)
@@ -2021,7 +2013,7 @@ static int etnaviv_gpu_rpm_resume(struct device *dev)
struct etnaviv_gpu *gpu = dev_get_drvdata(dev);
int ret;
- ret = etnaviv_gpu_clk_enable(gpu);
+ ret = etnaviv_gpu_clk_rst_enable(gpu);
if (ret)
return ret;
@@ -2029,7 +2021,7 @@ static int etnaviv_gpu_rpm_resume(struct device *dev)
if (gpu->state == ETNA_GPU_STATE_IDENTIFIED) {
ret = etnaviv_gpu_hw_resume(gpu);
if (ret) {
- etnaviv_gpu_clk_disable(gpu);
+ etnaviv_gpu_clk_rst_disable(gpu);
return ret;
}
}
---
base-commit: 14c3f1cbcec18927f1b6faab03f0d95c0bd1da74
base-tree: https://gitlab.freedesktop.org/drm/misc/kernel.git drm-misc-next
change-id: 20261023-etnaviv-rpm-reset-4259b2c517c4
Best regards,
--
Jerome