* [PATCH v2 1/6] dt-bindings: media: rockchip,rk3568-vepu: Add the reset lines
2026-11-12 8:30 [PATCH v2 0/6] media: verisilicon: Recover the Rockchip VPUs from faults and timeouts Arun Iyengar via B4 Relay
@ 2026-11-12 8:30 ` Arun Iyengar via B4 Relay
2026-11-12 8:30 ` [PATCH v2 2/6] media: verisilicon: Don't hold the VPU in reset after remove Arun Iyengar via B4 Relay
` (4 subsequent siblings)
5 siblings, 0 replies; 7+ messages in thread
From: Arun Iyengar via B4 Relay @ 2026-11-12 8:30 UTC (permalink / raw)
To: Nicolas Dufresne, Benjamin Gaignard, Philipp Zabel,
Mauro Carvalho Chehab, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Heiko Stuebner, Nicolas Frattaroli,
Andrzej Pietrasiewicz, Ezequiel Garcia, Hans Verkuil,
Jernej Skrabec
Cc: linux-media, linux-rockchip, devicetree, linux-arm-kernel,
linux-kernel, Sergei Khomenkov, Tharit Tangkijwanichakul,
Arun Iyengar
From: Arun Iyengar <aiyengar1990@gmail.com>
The VEPU121 encoder cores of the RK3588 each have their own AXI and AHB
reset lines in the CRU (SRST_A_JPEG_ENCODERx and SRST_H_JPEG_ENCODERx).
They are needed to recover a core that is stuck on the bus, which its
registers can't stop.
Describe them as optional, so that existing device trees stay valid.
Signed-off-by: Arun Iyengar <aiyengar1990@gmail.com>
---
.../devicetree/bindings/media/rockchip,rk3568-vepu.yaml | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/Documentation/devicetree/bindings/media/rockchip,rk3568-vepu.yaml b/Documentation/devicetree/bindings/media/rockchip,rk3568-vepu.yaml
index d246f5d38427..e9fda4373f0f 100644
--- a/Documentation/devicetree/bindings/media/rockchip,rk3568-vepu.yaml
+++ b/Documentation/devicetree/bindings/media/rockchip,rk3568-vepu.yaml
@@ -32,6 +32,16 @@ properties:
- const: aclk
- const: hclk
+ resets:
+ items:
+ - description: AXI reset line
+ - description: AHB reset line
+
+ reset-names:
+ items:
+ - const: axi
+ - const: ahb
+
power-domains:
maxItems: 1
--
2.55.0
^ permalink raw reply [flat|nested] 7+ messages in thread* [PATCH v2 2/6] media: verisilicon: Don't hold the VPU in reset after remove
2026-11-12 8:30 [PATCH v2 0/6] media: verisilicon: Recover the Rockchip VPUs from faults and timeouts Arun Iyengar via B4 Relay
2026-11-12 8:30 ` [PATCH v2 1/6] dt-bindings: media: rockchip,rk3568-vepu: Add the reset lines Arun Iyengar via B4 Relay
@ 2026-11-12 8:30 ` Arun Iyengar via B4 Relay
2026-11-12 8:30 ` [PATCH v2 3/6] media: verisilicon: Enable the Rockchip encoder interrupt again after a reset Arun Iyengar via B4 Relay
` (3 subsequent siblings)
5 siblings, 0 replies; 7+ messages in thread
From: Arun Iyengar via B4 Relay @ 2026-11-12 8:30 UTC (permalink / raw)
To: Nicolas Dufresne, Benjamin Gaignard, Philipp Zabel,
Mauro Carvalho Chehab, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Heiko Stuebner, Nicolas Frattaroli,
Andrzej Pietrasiewicz, Ezequiel Garcia, Hans Verkuil,
Jernej Skrabec
Cc: linux-media, linux-rockchip, devicetree, linux-arm-kernel,
linux-kernel, Sergei Khomenkov, Tharit Tangkijwanichakul,
Arun Iyengar, stable
From: Arun Iyengar <aiyengar1990@gmail.com>
On the RK3588, the resets of the AV1 decoder include the AXI and APB
bus interface resets (SRST_A_AV1_BIU, SRST_P_AV1_BIU), which also cover
the Verisilicon IOMMU of the decoder. When the driver is unbound, the
driver core runtime resumes the device and, through the runtime PM
device link, its IOMMU. hantro_remove() then asserts the resets, and
when the IOMMU autosuspends 100 ms later, vsi_iommu_suspend() writes to
its registers while they are held in reset, which raises an SError and
panics the system:
SError Interrupt on CPU4, code 0x00000000be000411 -- SError
Workqueue: pm pm_runtime_work
Call trace:
clk_disable+0x30/0x90 (P)
clk_bulk_disable+0x44/0x90
vsi_iommu_suspend+0x4c/0xc8 [vsi_iommu]
pm_generic_runtime_suspend+0x38/0x80
genpd_runtime_suspend+0xc4/0x348
...
Kernel panic - not syncing: Asynchronous SError Interrupt
This was hit when reloading the module, unless the decoder happened to
be probed again within the autosuspend delay of its IOMMU.
The IOMMU is not the only one to access the bus interface after remove.
Commit 8e26d4c20ed2 ("media: hantro: disable runtime PM before hardware
teardown") moved the assert after the runtime PM teardown because the
power domain code faulted on it when powering the domain off. With the
resets left asserted, the same domain code runs again when the domain
is powered on for the next bind, before hantro_probe() deasserts the
resets, and when the domain is powered off after a failed probe.
A runtime suspended VPU has its resets deasserted and its clocks off.
Leave it in that state on remove and on the probe error path instead of
asserting the resets; hantro_probe() deasserts them anyway.
Fixes: ea71631b7129 ("media: hantro: add support for reset lines")
Cc: stable@vger.kernel.org
Signed-off-by: Arun Iyengar <aiyengar1990@gmail.com>
---
drivers/media/platform/verisilicon/hantro_drv.c | 8 +-------
1 file changed, 1 insertion(+), 7 deletions(-)
diff --git a/drivers/media/platform/verisilicon/hantro_drv.c b/drivers/media/platform/verisilicon/hantro_drv.c
index 26e5d5547466..4542322dba71 100644
--- a/drivers/media/platform/verisilicon/hantro_drv.c
+++ b/drivers/media/platform/verisilicon/hantro_drv.c
@@ -1210,7 +1210,7 @@ static int hantro_probe(struct platform_device *pdev)
ret = clk_bulk_prepare(vpu->variant->num_clocks, vpu->clocks);
if (ret) {
dev_err(&pdev->dev, "Failed to prepare clocks\n");
- goto err_pm_disable_assert_reset;
+ goto err_pm_disable;
}
ret = v4l2_device_register(&pdev->dev, &vpu->v4l2_dev);
@@ -1264,11 +1264,6 @@ static int hantro_probe(struct platform_device *pdev)
v4l2_device_unregister(&vpu->v4l2_dev);
err_clk_unprepare:
clk_bulk_unprepare(vpu->variant->num_clocks, vpu->clocks);
-err_pm_disable_assert_reset:
- pm_runtime_dont_use_autosuspend(vpu->dev);
- pm_runtime_disable(vpu->dev);
- reset_control_assert(vpu->resets);
- return ret;
err_pm_disable:
pm_runtime_dont_use_autosuspend(vpu->dev);
pm_runtime_disable(vpu->dev);
@@ -1290,7 +1285,6 @@ static void hantro_remove(struct platform_device *pdev)
clk_bulk_unprepare(vpu->variant->num_clocks, vpu->clocks);
pm_runtime_dont_use_autosuspend(vpu->dev);
pm_runtime_disable(vpu->dev);
- reset_control_assert(vpu->resets);
}
static int hantro_runtime_resume(struct device *dev)
--
2.55.0
^ permalink raw reply [flat|nested] 7+ messages in thread* [PATCH v2 3/6] media: verisilicon: Enable the Rockchip encoder interrupt again after a reset
2026-11-12 8:30 [PATCH v2 0/6] media: verisilicon: Recover the Rockchip VPUs from faults and timeouts Arun Iyengar via B4 Relay
2026-11-12 8:30 ` [PATCH v2 1/6] dt-bindings: media: rockchip,rk3568-vepu: Add the reset lines Arun Iyengar via B4 Relay
2026-11-12 8:30 ` [PATCH v2 2/6] media: verisilicon: Don't hold the VPU in reset after remove Arun Iyengar via B4 Relay
@ 2026-11-12 8:30 ` Arun Iyengar via B4 Relay
2026-11-12 8:30 ` [PATCH v2 4/6] media: verisilicon: Reset the Rockchip encoder cores on timeout Arun Iyengar via B4 Relay
` (2 subsequent siblings)
5 siblings, 0 replies; 7+ messages in thread
From: Arun Iyengar via B4 Relay @ 2026-11-12 8:30 UTC (permalink / raw)
To: Nicolas Dufresne, Benjamin Gaignard, Philipp Zabel,
Mauro Carvalho Chehab, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Heiko Stuebner, Nicolas Frattaroli,
Andrzej Pietrasiewicz, Ezequiel Garcia, Hans Verkuil,
Jernej Skrabec
Cc: linux-media, linux-rockchip, devicetree, linux-arm-kernel,
linux-kernel, Sergei Khomenkov, Tharit Tangkijwanichakul,
Arun Iyengar, stable
From: Arun Iyengar <aiyengar1990@gmail.com>
The reset operations of the Rockchip VEPU1 and VEPU2 encoders, which
hantro_watchdog() calls when a job times out, disable the encoder
interrupt before stopping the encoder. Nothing enables it again: the
encoder run functions don't write the interrupt register, only the
interrupt handler does, and it no longer runs. Every later job on the
encoder then times out as well.
Clear the interrupt register at the end of the reset, as the interrupt
handler does after each frame.
Fixes: 775fec69008d ("media: add Rockchip VPU JPEG encoder driver")
Cc: stable@vger.kernel.org
Signed-off-by: Arun Iyengar <aiyengar1990@gmail.com>
---
drivers/media/platform/verisilicon/rockchip_vpu_hw.c | 12 ++++++++++++
1 file changed, 12 insertions(+)
diff --git a/drivers/media/platform/verisilicon/rockchip_vpu_hw.c b/drivers/media/platform/verisilicon/rockchip_vpu_hw.c
index 02673be9878e..5d4e46bd9cd6 100644
--- a/drivers/media/platform/verisilicon/rockchip_vpu_hw.c
+++ b/drivers/media/platform/verisilicon/rockchip_vpu_hw.c
@@ -475,6 +475,12 @@ static void rockchip_vpu1_enc_reset(struct hantro_ctx *ctx)
vepu_write(vpu, H1_REG_INTERRUPT_DIS_BIT, H1_REG_INTERRUPT);
vepu_write(vpu, 0, H1_REG_ENC_CTRL);
vepu_write(vpu, 0, H1_REG_AXI_CTRL);
+ /*
+ * The encoder runs don't write the interrupt register, only the
+ * interrupt handler clears it after each frame. Clear it here too,
+ * or the interrupt stays disabled and all later jobs time out.
+ */
+ vepu_write(vpu, 0, H1_REG_INTERRUPT);
}
static void rockchip_vpu2_dec_reset(struct hantro_ctx *ctx)
@@ -493,6 +499,12 @@ static void rockchip_vpu2_enc_reset(struct hantro_ctx *ctx)
vepu_write(vpu, VEPU_REG_INTERRUPT_DIS_BIT, VEPU_REG_INTERRUPT);
vepu_write(vpu, 0, VEPU_REG_ENCODE_START);
vepu_write(vpu, 0, VEPU_REG_AXI_CTRL);
+ /*
+ * The encoder runs don't write the interrupt register, only the
+ * interrupt handler clears it after each frame. Clear it here too,
+ * or the interrupt stays disabled and all later jobs time out.
+ */
+ vepu_write(vpu, 0, VEPU_REG_INTERRUPT);
}
/*
--
2.55.0
^ permalink raw reply [flat|nested] 7+ messages in thread* [PATCH v2 4/6] media: verisilicon: Reset the Rockchip encoder cores on timeout
2026-11-12 8:30 [PATCH v2 0/6] media: verisilicon: Recover the Rockchip VPUs from faults and timeouts Arun Iyengar via B4 Relay
` (2 preceding siblings ...)
2026-11-12 8:30 ` [PATCH v2 3/6] media: verisilicon: Enable the Rockchip encoder interrupt again after a reset Arun Iyengar via B4 Relay
@ 2026-11-12 8:30 ` Arun Iyengar via B4 Relay
2026-11-12 8:30 ` [PATCH v2 5/6] media: verisilicon: Reinitialize the IOMMU after resetting an encoder core Arun Iyengar via B4 Relay
2026-11-12 8:30 ` [PATCH v2 6/6] arm64: dts: rockchip: Add the reset lines of the RK3588 VEPU121 cores Arun Iyengar via B4 Relay
5 siblings, 0 replies; 7+ messages in thread
From: Arun Iyengar via B4 Relay @ 2026-11-12 8:30 UTC (permalink / raw)
To: Nicolas Dufresne, Benjamin Gaignard, Philipp Zabel,
Mauro Carvalho Chehab, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Heiko Stuebner, Nicolas Frattaroli,
Andrzej Pietrasiewicz, Ezequiel Garcia, Hans Verkuil,
Jernej Skrabec
Cc: linux-media, linux-rockchip, devicetree, linux-arm-kernel,
linux-kernel, Sergei Khomenkov, Tharit Tangkijwanichakul,
Arun Iyengar
From: Arun Iyengar <aiyengar1990@gmail.com>
On a job timeout, the Rockchip VEPU1 and VEPU2 reset operations mask the
interrupt and clear the encode enable and AXI control registers. That
only stops a core that is still responsive. On RK3588, when a VEPU121
core reads an IOVA that isn't mapped, the IOMMU reports a page fault and
the core never becomes idle again: its AXI transactions stay
outstanding, the IOMMU can't be stalled anymore ("Enable stall request
timed out", then "Disable paging request timed out" on runtime
suspend), every later job times out as well, and unbinding the driver
can lock up the system.
The driver already gets the optional reset lines of the device and
deasserts them at probe. Pulse them in the encoder reset operations,
after stopping the core through its registers, to return it to a clean
idle state. The clocks are still enabled at that point, as the job that
timed out holds them. This is a no-op on device trees that don't
describe the resets, which is the case for all of them so far.
The Rockchip BSP MPP driver resets these cores the same way, through
their AXI and AHB reset lines.
Signed-off-by: Arun Iyengar <aiyengar1990@gmail.com>
---
.../media/platform/verisilicon/rockchip_vpu_hw.c | 30 ++++++++++++++++++++++
1 file changed, 30 insertions(+)
diff --git a/drivers/media/platform/verisilicon/rockchip_vpu_hw.c b/drivers/media/platform/verisilicon/rockchip_vpu_hw.c
index 5d4e46bd9cd6..114010d6a5c9 100644
--- a/drivers/media/platform/verisilicon/rockchip_vpu_hw.c
+++ b/drivers/media/platform/verisilicon/rockchip_vpu_hw.c
@@ -7,6 +7,7 @@
*/
#include <linux/clk.h>
+#include <linux/delay.h>
#include "hantro.h"
#include "hantro_jpeg.h"
@@ -468,6 +469,33 @@ static void rk3066_vpu_dec_reset(struct hantro_ctx *ctx)
vdpu_write(vpu, G1_REG_CONFIG_DEC_CLK_GATE_E, G1_REG_CONFIG);
}
+/*
+ * Stopping the encoder through its registers isn't enough when it is stuck
+ * on the bus, for instance after a translation fault in its IOMMU: it keeps
+ * AXI transactions outstanding and never becomes idle, which also prevents
+ * the IOMMU from stalling. Pulse the reset lines of the core, if the device
+ * tree describes them, to return it to its idle state.
+ */
+static void rockchip_vpu_enc_hw_reset(struct hantro_dev *vpu)
+{
+ int ret;
+
+ if (!vpu->resets)
+ return;
+
+ ret = reset_control_assert(vpu->resets);
+ if (ret) {
+ dev_warn(vpu->dev, "Failed to assert resets: %d\n", ret);
+ return;
+ }
+
+ udelay(5);
+
+ ret = reset_control_deassert(vpu->resets);
+ if (ret)
+ dev_err(vpu->dev, "Failed to deassert resets: %d\n", ret);
+}
+
static void rockchip_vpu1_enc_reset(struct hantro_ctx *ctx)
{
struct hantro_dev *vpu = ctx->dev;
@@ -475,6 +503,7 @@ static void rockchip_vpu1_enc_reset(struct hantro_ctx *ctx)
vepu_write(vpu, H1_REG_INTERRUPT_DIS_BIT, H1_REG_INTERRUPT);
vepu_write(vpu, 0, H1_REG_ENC_CTRL);
vepu_write(vpu, 0, H1_REG_AXI_CTRL);
+ rockchip_vpu_enc_hw_reset(vpu);
/*
* The encoder runs don't write the interrupt register, only the
* interrupt handler clears it after each frame. Clear it here too,
@@ -499,6 +528,7 @@ static void rockchip_vpu2_enc_reset(struct hantro_ctx *ctx)
vepu_write(vpu, VEPU_REG_INTERRUPT_DIS_BIT, VEPU_REG_INTERRUPT);
vepu_write(vpu, 0, VEPU_REG_ENCODE_START);
vepu_write(vpu, 0, VEPU_REG_AXI_CTRL);
+ rockchip_vpu_enc_hw_reset(vpu);
/*
* The encoder runs don't write the interrupt register, only the
* interrupt handler clears it after each frame. Clear it here too,
--
2.55.0
^ permalink raw reply [flat|nested] 7+ messages in thread* [PATCH v2 5/6] media: verisilicon: Reinitialize the IOMMU after resetting an encoder core
2026-11-12 8:30 [PATCH v2 0/6] media: verisilicon: Recover the Rockchip VPUs from faults and timeouts Arun Iyengar via B4 Relay
` (3 preceding siblings ...)
2026-11-12 8:30 ` [PATCH v2 4/6] media: verisilicon: Reset the Rockchip encoder cores on timeout Arun Iyengar via B4 Relay
@ 2026-11-12 8:30 ` Arun Iyengar via B4 Relay
2026-11-12 8:30 ` [PATCH v2 6/6] arm64: dts: rockchip: Add the reset lines of the RK3588 VEPU121 cores Arun Iyengar via B4 Relay
5 siblings, 0 replies; 7+ messages in thread
From: Arun Iyengar via B4 Relay @ 2026-11-12 8:30 UTC (permalink / raw)
To: Nicolas Dufresne, Benjamin Gaignard, Philipp Zabel,
Mauro Carvalho Chehab, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Heiko Stuebner, Nicolas Frattaroli,
Andrzej Pietrasiewicz, Ezequiel Garcia, Hans Verkuil,
Jernej Skrabec
Cc: linux-media, linux-rockchip, devicetree, linux-arm-kernel,
linux-kernel, Sergei Khomenkov, Tharit Tangkijwanichakul,
Arun Iyengar
From: Arun Iyengar <aiyengar1990@gmail.com>
A Rockchip encoder core that faulted on an unmapped IOVA leaves its
IOMMU with the fault pending, and pulsing the reset lines of the core,
as the previous patch does, can leave the IOMMU out of sync with what
its driver expects. The IOMMU is only reinitialized when it runtime
suspends and resumes, which doesn't happen between back-to-back jobs,
so the next jobs can run with a broken IOMMU.
Do like rkvdec: allocate an empty paging domain at probe, and after the
reset pulse attach it and detach it again, which returns the device to
its default DMA domain. The IOMMU driver reinitializes the IOMMU on each
domain change (on Rockchip: stall, force reset, program the directory
table and enable paging), and the default domain keeps all the existing
mappings. This runs in the watchdog work, right after the core was
stopped and reset, while the timed out job still holds the runtime PM
reference.
Only do it there, where the core is known to be idle, and only for a
device with reset lines whose current domain is a DMA domain, the one
the detach returns to: not with an identity domain or with the ARM32
DMA mapping domains. If the attach fails, the IOMMU driver may leave the
IOMMU without translation (rockchip-iommu falls back to its identity
domain), so refuse all further jobs of the device instead of letting
the core access memory with untranslated addresses.
Signed-off-by: Arun Iyengar <aiyengar1990@gmail.com>
---
drivers/media/platform/verisilicon/hantro.h | 5 ++
drivers/media/platform/verisilicon/hantro_drv.c | 73 ++++++++++++++++++++++
drivers/media/platform/verisilicon/hantro_hw.h | 1 +
.../media/platform/verisilicon/rockchip_vpu_hw.c | 6 +-
4 files changed, 84 insertions(+), 1 deletion(-)
diff --git a/drivers/media/platform/verisilicon/hantro.h b/drivers/media/platform/verisilicon/hantro.h
index 0353de154a1e..e3881ca21c56 100644
--- a/drivers/media/platform/verisilicon/hantro.h
+++ b/drivers/media/platform/verisilicon/hantro.h
@@ -193,6 +193,9 @@ hantro_vdev_to_func(struct video_device *vdev)
* shared with interrupt handlers.
* @variant: Hardware variant-specific parameters.
* @watchdog_work: Delayed work for hardware timeout handling.
+ * @empty_domain: Empty IOMMU domain, used to reinitialize the IOMMU after
+ * a reset of the core. NULL if not needed.
+ * @iommu_lost: The IOMMU couldn't be reinitialized, no job can run.
*/
struct hantro_dev {
struct v4l2_device v4l2_dev;
@@ -213,6 +216,8 @@ struct hantro_dev {
spinlock_t irqlock;
const struct hantro_variant *variant;
struct delayed_work watchdog_work;
+ struct iommu_domain *empty_domain;
+ bool iommu_lost;
};
/**
diff --git a/drivers/media/platform/verisilicon/hantro_drv.c b/drivers/media/platform/verisilicon/hantro_drv.c
index 4542322dba71..6d027c277038 100644
--- a/drivers/media/platform/verisilicon/hantro_drv.c
+++ b/drivers/media/platform/verisilicon/hantro_drv.c
@@ -11,6 +11,7 @@
*/
#include <linux/clk.h>
+#include <linux/iommu.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_platform.h>
@@ -112,6 +113,35 @@ void hantro_irq_done(struct hantro_dev *vpu,
}
}
+/*
+ * Pulsing the reset lines of a core can also reset its IOMMU, behind the back
+ * of the IOMMU driver. Attach the empty domain and detach it again to return
+ * to the default DMA domain: the IOMMU driver then fully reinitializes the
+ * IOMMU, and the default domain keeps all its mappings. Called after the core
+ * was reset, so the IOMMU can be stalled.
+ *
+ * If the IOMMU can't be attached to the empty domain, its driver may leave it
+ * without translation. Refuse all later jobs then, rather than let the core
+ * access memory with untranslated addresses.
+ */
+void hantro_iommu_restore(struct hantro_dev *vpu)
+{
+ int ret;
+
+ if (!vpu->empty_domain)
+ return;
+
+ ret = iommu_attach_device(vpu->empty_domain, vpu->dev);
+ if (ret) {
+ dev_err(vpu->dev, "Failed to reinitialize the IOMMU (%d), refusing further jobs\n",
+ ret);
+ vpu->iommu_lost = true;
+ return;
+ }
+
+ iommu_detach_device(vpu->empty_domain, vpu->dev);
+}
+
void hantro_watchdog(struct work_struct *work)
{
struct hantro_dev *vpu;
@@ -174,6 +204,11 @@ static void device_run(void *priv)
src = hantro_get_src_buf(ctx);
dst = hantro_get_dst_buf(ctx);
+ if (unlikely(vpu->iommu_lost)) {
+ hantro_job_finish_no_pm(ctx, VB2_BUF_STATE_ERROR);
+ return;
+ }
+
ret = pm_runtime_resume_and_get(vpu->dev);
if (ret < 0) {
hantro_job_finish_no_pm(ctx, VB2_BUF_STATE_ERROR);
@@ -1069,6 +1104,40 @@ static struct v4l2_m2m_dev *hantro_get_v4l2_m2m_dev(struct hantro_dev *vpu)
return v4l2_m2m_init(&vpu_m2m_ops);
}
+static void hantro_free_empty_domain(void *data)
+{
+ iommu_domain_free(data);
+}
+
+static int hantro_alloc_empty_domain(struct hantro_dev *vpu)
+{
+ struct iommu_domain *domain;
+
+ /*
+ * Only needed if the core can be reset, and only possible with a DMA
+ * domain to return to.
+ */
+ if (!vpu->resets)
+ return 0;
+
+ domain = iommu_get_domain_for_dev(vpu->dev);
+ if (!domain || !iommu_is_dma_domain(domain))
+ return 0;
+
+ domain = iommu_paging_domain_alloc(vpu->dev);
+ if (IS_ERR(domain)) {
+ /* Not fatal, the IOMMU just won't be restored after a reset. */
+ dev_warn(vpu->dev, "Failed to allocate an empty IOMMU domain: %pe\n",
+ domain);
+ return 0;
+ }
+
+ vpu->empty_domain = domain;
+
+ return devm_add_action_or_reset(vpu->dev, hantro_free_empty_domain,
+ domain);
+}
+
static int hantro_probe(struct platform_device *pdev)
{
const struct of_device_id *match;
@@ -1130,6 +1199,10 @@ static int hantro_probe(struct platform_device *pdev)
if (IS_ERR(vpu->resets))
return PTR_ERR(vpu->resets);
+ ret = hantro_alloc_empty_domain(vpu);
+ if (ret)
+ return ret;
+
num_bases = vpu->variant->num_regs ?: 1;
vpu->reg_bases = devm_kcalloc(&pdev->dev, num_bases,
sizeof(*vpu->reg_bases), GFP_KERNEL);
diff --git a/drivers/media/platform/verisilicon/hantro_hw.h b/drivers/media/platform/verisilicon/hantro_hw.h
index 13e573f1f19d..aa610dd5beef 100644
--- a/drivers/media/platform/verisilicon/hantro_hw.h
+++ b/drivers/media/platform/verisilicon/hantro_hw.h
@@ -427,6 +427,7 @@ extern const struct hantro_postproc_ops rockchip_vpu981_postproc_ops;
extern const u32 hantro_vp8_dec_mc_filter[8][6];
void hantro_watchdog(struct work_struct *work);
+void hantro_iommu_restore(struct hantro_dev *vpu);
void hantro_irq_done(struct hantro_dev *vpu,
enum vb2_buffer_state result);
void hantro_start_prepare_run(struct hantro_ctx *ctx);
diff --git a/drivers/media/platform/verisilicon/rockchip_vpu_hw.c b/drivers/media/platform/verisilicon/rockchip_vpu_hw.c
index 114010d6a5c9..309b39ca70da 100644
--- a/drivers/media/platform/verisilicon/rockchip_vpu_hw.c
+++ b/drivers/media/platform/verisilicon/rockchip_vpu_hw.c
@@ -492,8 +492,12 @@ static void rockchip_vpu_enc_hw_reset(struct hantro_dev *vpu)
udelay(5);
ret = reset_control_deassert(vpu->resets);
- if (ret)
+ if (ret) {
dev_err(vpu->dev, "Failed to deassert resets: %d\n", ret);
+ return;
+ }
+
+ hantro_iommu_restore(vpu);
}
static void rockchip_vpu1_enc_reset(struct hantro_ctx *ctx)
--
2.55.0
^ permalink raw reply [flat|nested] 7+ messages in thread* [PATCH v2 6/6] arm64: dts: rockchip: Add the reset lines of the RK3588 VEPU121 cores
2026-11-12 8:30 [PATCH v2 0/6] media: verisilicon: Recover the Rockchip VPUs from faults and timeouts Arun Iyengar via B4 Relay
` (4 preceding siblings ...)
2026-11-12 8:30 ` [PATCH v2 5/6] media: verisilicon: Reinitialize the IOMMU after resetting an encoder core Arun Iyengar via B4 Relay
@ 2026-11-12 8:30 ` Arun Iyengar via B4 Relay
5 siblings, 0 replies; 7+ messages in thread
From: Arun Iyengar via B4 Relay @ 2026-11-12 8:30 UTC (permalink / raw)
To: Nicolas Dufresne, Benjamin Gaignard, Philipp Zabel,
Mauro Carvalho Chehab, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Heiko Stuebner, Nicolas Frattaroli,
Andrzej Pietrasiewicz, Ezequiel Garcia, Hans Verkuil,
Jernej Skrabec
Cc: linux-media, linux-rockchip, devicetree, linux-arm-kernel,
linux-kernel, Sergei Khomenkov, Tharit Tangkijwanichakul,
Arun Iyengar
From: Arun Iyengar <aiyengar1990@gmail.com>
Describe the AXI and AHB reset lines of the four VEPU121 JPEG encoder
cores, so that the driver can reset a core that is stuck after a fault
of its IOMMU.
The IOMMU of each core sits in the same register block and is clocked
by the same clocks, so these resets likely cover its bus interface as
well. This relies on the driver not leaving the VPU in reset after
remove ("media: verisilicon: Don't hold the VPU in reset after remove").
Signed-off-by: Arun Iyengar <aiyengar1990@gmail.com>
---
arch/arm64/boot/dts/rockchip/rk3588-base.dtsi | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/arch/arm64/boot/dts/rockchip/rk3588-base.dtsi b/arch/arm64/boot/dts/rockchip/rk3588-base.dtsi
index aa69fd3793fd..227465d446a2 100644
--- a/arch/arm64/boot/dts/rockchip/rk3588-base.dtsi
+++ b/arch/arm64/boot/dts/rockchip/rk3588-base.dtsi
@@ -1325,6 +1325,8 @@ vepu121_0: video-codec@fdba0000 {
clock-names = "aclk", "hclk";
iommus = <&vepu121_0_mmu>;
power-domains = <&power RK3588_PD_VDPU>;
+ resets = <&cru SRST_A_JPEG_ENCODER0>, <&cru SRST_H_JPEG_ENCODER0>;
+ reset-names = "axi", "ahb";
};
vepu121_0_mmu: iommu@fdba0800 {
@@ -1345,6 +1347,8 @@ vepu121_1: video-codec@fdba4000 {
clock-names = "aclk", "hclk";
iommus = <&vepu121_1_mmu>;
power-domains = <&power RK3588_PD_VDPU>;
+ resets = <&cru SRST_A_JPEG_ENCODER1>, <&cru SRST_H_JPEG_ENCODER1>;
+ reset-names = "axi", "ahb";
};
vepu121_1_mmu: iommu@fdba4800 {
@@ -1365,6 +1369,8 @@ vepu121_2: video-codec@fdba8000 {
clock-names = "aclk", "hclk";
iommus = <&vepu121_2_mmu>;
power-domains = <&power RK3588_PD_VDPU>;
+ resets = <&cru SRST_A_JPEG_ENCODER2>, <&cru SRST_H_JPEG_ENCODER2>;
+ reset-names = "axi", "ahb";
};
vepu121_2_mmu: iommu@fdba8800 {
@@ -1385,6 +1391,8 @@ vepu121_3: video-codec@fdbac000 {
clock-names = "aclk", "hclk";
iommus = <&vepu121_3_mmu>;
power-domains = <&power RK3588_PD_VDPU>;
+ resets = <&cru SRST_A_JPEG_ENCODER3>, <&cru SRST_H_JPEG_ENCODER3>;
+ reset-names = "axi", "ahb";
};
vepu121_3_mmu: iommu@fdbac800 {
--
2.55.0
^ permalink raw reply [flat|nested] 7+ messages in thread