Linux-ARM-Kernel Archive on lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v2 0/5] media: rkvdec: Enable multi-core support
@ 2026-08-10 15:22 Detlev Casanova
  2026-08-10 15:22 ` [PATCH v2 1/5] media: rkvdec: Keep RCB to the correct size Detlev Casanova
                   ` (4 more replies)
  0 siblings, 5 replies; 6+ messages in thread
From: Detlev Casanova @ 2026-08-10 15:22 UTC (permalink / raw)
  To: Mauro Carvalho Chehab, Ezequiel Garcia, Heiko Stuebner,
	Nicolas Dufresne, Hans Verkuil, Jonas Karlman
  Cc: Sven Püschel, kernel, linux-media, linux-kernel,
	linux-rockchip, linux-arm-kernel, Detlev Casanova

Since the driver is used for decoding on rk3588 and that the SoC has
2 identical decoding cores, enable support for it.

Instead of exposing 2 v4l2 devices to userspace, the driver will only
expose one and handle the 2 cores transparently.

The 2 cores are able to work in parallel, but only contexts are
parallelized: 1 stream, that uses 1 context, will only be able to use
1 core as it usually needs previous frames already decoded to use as
reference frames.
To avoid complex scheduling, only different streams can use cores at the
same time.

The implementation is based on Sven Püschel's RGA multi-core support[1]
(and this patch set depends on it).
It uses the maximum parallel jobs implementation in v4l2-core and also
uses the same components mechanism to handle core probing.

A fix for the RCB (Row and Cols Buffer) size computation is also provided
as it was causing issues with some fluster tests.

The v4l2 commit is not signed as it should be part of Sven's patch set
v2.

Performance-wise, fluster doesn't seem to run much faster, but I tested
with an HEVC test video from Jellyfin and observed that frames start
dropping with 6 concurrent gstreamer instances, instead of 4 without
multi-core enabled.

[1]: https://lore.kernel.org/all/20260606-spu-rga3multicore-v1-0-3ec2b15675f7@pengutronix.de/

Changes in v2:
 - Remove job_finish/buf_done dance and use Sven's max parallel
   implementation instead.
 - Rework history to have a working driver at each commit.
 - Remove the 2 cores limitation, any number of cores can be used now.

Signed-off-by: Detlev Casanova <detlev.casanova@collabora.com>
---
Detlev Casanova (5):
      media: rkvdec: Keep RCB to the correct size
      media: rkvdec: Remove unused need_reset
      v4l2: export v4l2_m2m_set_max_parallel_jobs
      media: rkvdec: Split into core and master platform drivers
      media: rkvdec: Add multicore support

 .../media/platform/rockchip/rkvdec/rkvdec-h264.c   |  17 +-
 .../media/platform/rockchip/rkvdec/rkvdec-hevc.c   |  16 +-
 .../media/platform/rockchip/rkvdec/rkvdec-rcb.c    |  77 ++-
 .../media/platform/rockchip/rkvdec/rkvdec-rcb.h    |   8 +-
 .../platform/rockchip/rkvdec/rkvdec-vdpu381-h264.c |  24 +-
 .../platform/rockchip/rkvdec/rkvdec-vdpu381-hevc.c |  24 +-
 .../platform/rockchip/rkvdec/rkvdec-vdpu383-h264.c |  24 +-
 .../platform/rockchip/rkvdec/rkvdec-vdpu383-hevc.c |  26 +-
 .../media/platform/rockchip/rkvdec/rkvdec-vp9.c    |  27 +-
 drivers/media/platform/rockchip/rkvdec/rkvdec.c    | 625 +++++++++++++++------
 drivers/media/platform/rockchip/rkvdec/rkvdec.h    |  31 +-
 drivers/media/v4l2-core/v4l2-mem2mem.c             |   1 +
 12 files changed, 596 insertions(+), 304 deletions(-)
---
base-commit: 6a75e3d4f6428b90f398354212e3a2e0172851d6
change-id: 20260408-rkvdec-multicore-98831da51072
prerequisite-change-id: 20260602-spu-rga3multicore-ae8c8caf01e9:v1
prerequisite-patch-id: 57496def46f4b8bf13d70a6f73dd9005b59d9ede
prerequisite-patch-id: e89e8a899373aaaa8cd1242d30f86a957b4d7e27
prerequisite-patch-id: d1f2bfc2a3ca3aef12a9678fe15bb93f346caf95
prerequisite-patch-id: f46ff26bb00afaf45122256d30f68464589d9943
prerequisite-patch-id: cd802c9f5c1106d08738f92a0a7c3d74fe2c89c1
prerequisite-patch-id: 1ec6b5db4df41bbe96f4ab8e83feeff1392a622f
prerequisite-patch-id: cb6a14d144c4efa77ad6396b005fa1f97d3a834e
prerequisite-patch-id: f8d48c1836f2287305d93af37a8c2fdb13c8a1d9
prerequisite-patch-id: 125ab4c4cc472f7a47f1ef41182d751b17056bf4
prerequisite-patch-id: eec4c1acdf5754b2faae9c8b18b93b8fb6b3aea3
prerequisite-patch-id: 4b4d935e09abc95aeb7e8aedffd3eb179b36d5a0
prerequisite-patch-id: 01df8a8e2a7d25a098483c03b18b6da425f63361
prerequisite-patch-id: 9a9071eca6bce3f693580756d5077d4bc8968a95
prerequisite-patch-id: e3f5d215b7207701cb331e720aada3fe504a0f8b
prerequisite-patch-id: ec44f51ffb6beb8c2b7854debc1b0b8be127d30a
prerequisite-patch-id: 14dad2504e1293cd4c5dc215ae99b2c6fc5e5877
prerequisite-patch-id: 10c6c977c0f71400931941b42da73adcaf63e810

Best regards,
--  
Detlev Casanova <detlev.casanova@collabora.com>



^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCH v2 1/5] media: rkvdec: Keep RCB to the correct size
  2026-08-10 15:22 [PATCH v2 0/5] media: rkvdec: Enable multi-core support Detlev Casanova
@ 2026-08-10 15:22 ` Detlev Casanova
  2026-08-10 15:22 ` [PATCH v2 2/5] media: rkvdec: Remove unused need_reset Detlev Casanova
                   ` (3 subsequent siblings)
  4 siblings, 0 replies; 6+ messages in thread
From: Detlev Casanova @ 2026-08-10 15:22 UTC (permalink / raw)
  To: Mauro Carvalho Chehab, Ezequiel Garcia, Heiko Stuebner,
	Nicolas Dufresne, Hans Verkuil, Jonas Karlman
  Cc: Sven Püschel, kernel, linux-media, linux-kernel,
	linux-rockchip, linux-arm-kernel, Detlev Casanova

Currently, if a video changes resolution, the RCB size might be too small
and the HW could try to write out of the allocated buffer.

To fix that, make sure that the RCB size is validated for each run and
increase the buffer size when needed.

Fixes: e5640dbb991c ("media: rkvdec: Add RCB and SRAM support")
Signed-off-by: Detlev Casanova <detlev.casanova@collabora.com>
---
 .../media/platform/rockchip/rkvdec/rkvdec-rcb.c    | 26 +++++++++++++++---
 .../media/platform/rockchip/rkvdec/rkvdec-rcb.h    |  3 ++-
 drivers/media/platform/rockchip/rkvdec/rkvdec.c    | 31 +++++++++++-----------
 3 files changed, 40 insertions(+), 20 deletions(-)

diff --git a/drivers/media/platform/rockchip/rkvdec/rkvdec-rcb.c b/drivers/media/platform/rockchip/rkvdec/rkvdec-rcb.c
index fdcf1f177379..191f78278c01 100644
--- a/drivers/media/platform/rockchip/rkvdec/rkvdec-rcb.c
+++ b/drivers/media/platform/rockchip/rkvdec/rkvdec-rcb.c
@@ -17,6 +17,8 @@
 struct rkvdec_rcb_config {
 	struct rkvdec_aux_buf *rcb_bufs;
 	size_t rcb_count;
+	u32 width;
+	u32 height;
 };
 
 static size_t rkvdec_rcb_size(const struct rcb_size_info *size_info,
@@ -40,6 +42,21 @@ int rkvdec_rcb_buf_count(struct rkvdec_ctx *ctx)
 	return ctx->rcb_config->rcb_count;
 }
 
+bool rkvdec_rcb_buf_validate_size(struct rkvdec_ctx *ctx)
+{
+	struct rkvdec_rcb_config *cfg = ctx->rcb_config;
+
+	bool ret = cfg && cfg->height >= ctx->decoded_fmt.fmt.pix_mp.height &&
+		   cfg->width >= ctx->decoded_fmt.fmt.pix_mp.width;
+
+	if (!ret && cfg) {
+		dev_dbg(ctx->dev->dev, "RCB size %ux%u -> %ux%u\n", cfg->width, cfg->height,
+			ctx->decoded_fmt.fmt.pix_mp.width, ctx->decoded_fmt.fmt.pix_mp.height);
+	}
+
+	return ret;
+}
+
 void rkvdec_free_rcb(struct rkvdec_ctx *ctx)
 {
 	struct rkvdec_dev *dev = ctx->dev;
@@ -77,14 +94,15 @@ void rkvdec_free_rcb(struct rkvdec_ctx *ctx)
 		devm_kfree(dev->dev, cfg->rcb_bufs);
 
 	devm_kfree(dev->dev, cfg);
+
+	ctx->rcb_config = NULL;
 }
 
-int rkvdec_allocate_rcb(struct rkvdec_ctx *ctx,
+int rkvdec_allocate_rcb(struct rkvdec_ctx *ctx, u32 width, u32 height,
 			const struct rcb_size_info *size_info,
 			size_t rcb_count)
 {
 	int ret, i;
-	u32 width, height;
 	struct rkvdec_dev *rkvdec = ctx->dev;
 	struct rkvdec_rcb_config *cfg;
 
@@ -105,8 +123,8 @@ int rkvdec_allocate_rcb(struct rkvdec_ctx *ctx,
 		goto err_alloc;
 	}
 
-	width = ctx->decoded_fmt.fmt.pix_mp.width;
-	height = ctx->decoded_fmt.fmt.pix_mp.height;
+	cfg->width = width;
+	cfg->height = height;
 
 	for (i = 0; i < rcb_count; i++) {
 		void *cpu = NULL;
diff --git a/drivers/media/platform/rockchip/rkvdec/rkvdec-rcb.h b/drivers/media/platform/rockchip/rkvdec/rkvdec-rcb.h
index 30e8002555c8..0662a4359bdf 100644
--- a/drivers/media/platform/rockchip/rkvdec/rkvdec-rcb.h
+++ b/drivers/media/platform/rockchip/rkvdec/rkvdec-rcb.h
@@ -20,10 +20,11 @@ struct rcb_size_info {
 	enum rcb_axis axis;
 };
 
-int rkvdec_allocate_rcb(struct rkvdec_ctx *ctx,
+int rkvdec_allocate_rcb(struct rkvdec_ctx *ctx, u32 width, u32 height,
 			const struct rcb_size_info *size_info,
 			size_t rcb_count);
 dma_addr_t rkvdec_rcb_buf_dma_addr(struct rkvdec_ctx *ctx, int id);
 size_t rkvdec_rcb_buf_size(struct rkvdec_ctx *ctx, int id);
 int rkvdec_rcb_buf_count(struct rkvdec_ctx *ctx);
+bool rkvdec_rcb_buf_validate_size(struct rkvdec_ctx *ctx);
 void rkvdec_free_rcb(struct rkvdec_ctx *ctx);
diff --git a/drivers/media/platform/rockchip/rkvdec/rkvdec.c b/drivers/media/platform/rockchip/rkvdec/rkvdec.c
index 1d1e9bfef8e9..31ddfcc58894 100644
--- a/drivers/media/platform/rockchip/rkvdec/rkvdec.c
+++ b/drivers/media/platform/rockchip/rkvdec/rkvdec.c
@@ -978,8 +978,7 @@ static int rkvdec_start_streaming(struct vb2_queue *q, unsigned int count)
 {
 	struct rkvdec_ctx *ctx = vb2_get_drv_priv(q);
 	const struct rkvdec_coded_fmt_desc *desc;
-	const struct rkvdec_variant *variant = ctx->dev->variant;
-	int ret;
+	int ret = 0;
 
 	if (V4L2_TYPE_IS_CAPTURE(q->type))
 		return 0;
@@ -988,20 +987,8 @@ static int rkvdec_start_streaming(struct vb2_queue *q, unsigned int count)
 	if (WARN_ON(!desc))
 		return -EINVAL;
 
-	ret = rkvdec_allocate_rcb(ctx, variant->rcb_sizes, variant->num_rcb_sizes);
-	if (ret)
-		return ret;
-
-	if (desc->ops->start) {
+	if (desc->ops->start)
 		ret = desc->ops->start(ctx);
-		if (ret)
-			goto err_ops_start;
-	}
-
-	return 0;
-
-err_ops_start:
-	rkvdec_free_rcb(ctx);
 
 	return ret;
 }
@@ -1174,6 +1161,20 @@ static void rkvdec_device_run(void *priv)
 		return;
 	}
 
+	if (!rkvdec_rcb_buf_validate_size(ctx)) {
+		rkvdec_free_rcb(ctx);
+
+		ret = rkvdec_allocate_rcb(ctx,
+					  ctx->decoded_fmt.fmt.pix_mp.width,
+					  ctx->decoded_fmt.fmt.pix_mp.height,
+					  ctx->dev->variant->rcb_sizes,
+					  ctx->dev->variant->num_rcb_sizes);
+		if (ret) {
+			rkvdec_job_finish(ctx, VB2_BUF_STATE_ERROR);
+			return;
+		}
+	}
+
 	ret = desc->ops->run(ctx);
 	if (ret)
 		rkvdec_job_finish(ctx, VB2_BUF_STATE_ERROR);

-- 
2.55.0



^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH v2 2/5] media: rkvdec: Remove unused need_reset
  2026-08-10 15:22 [PATCH v2 0/5] media: rkvdec: Enable multi-core support Detlev Casanova
  2026-08-10 15:22 ` [PATCH v2 1/5] media: rkvdec: Keep RCB to the correct size Detlev Casanova
@ 2026-08-10 15:22 ` Detlev Casanova
  2026-08-10 15:22 ` [PATCH v2 3/5] v4l2: export v4l2_m2m_set_max_parallel_jobs Detlev Casanova
                   ` (2 subsequent siblings)
  4 siblings, 0 replies; 6+ messages in thread
From: Detlev Casanova @ 2026-08-10 15:22 UTC (permalink / raw)
  To: Mauro Carvalho Chehab, Ezequiel Garcia, Heiko Stuebner,
	Nicolas Dufresne, Hans Verkuil, Jonas Karlman
  Cc: Sven Püschel, kernel, linux-media, linux-kernel,
	linux-rockchip, linux-arm-kernel, Detlev Casanova

A left-over from the iommu restore mecanism was forgotten.
As need_reset is never set to true, the if has no use.

The actual restore function is called above it vase the IRQ isn't in a
success status.

Signed-off-by: Detlev Casanova <detlev.casanova@collabora.com>
---
 drivers/media/platform/rockchip/rkvdec/rkvdec.c | 8 --------
 1 file changed, 8 deletions(-)

diff --git a/drivers/media/platform/rockchip/rkvdec/rkvdec.c b/drivers/media/platform/rockchip/rkvdec/rkvdec.c
index 31ddfcc58894..db2731af06cf 100644
--- a/drivers/media/platform/rockchip/rkvdec/rkvdec.c
+++ b/drivers/media/platform/rockchip/rkvdec/rkvdec.c
@@ -1462,7 +1462,6 @@ static irqreturn_t vdpu381_irq_handler(struct rkvdec_ctx *ctx)
 {
 	struct rkvdec_dev *rkvdec = ctx->dev;
 	enum vb2_buffer_state state;
-	bool need_reset = 0;
 	u32 status;
 
 	status = readl(rkvdec->regs + VDPU381_REG_STA_INT);
@@ -1478,9 +1477,6 @@ static irqreturn_t vdpu381_irq_handler(struct rkvdec_ctx *ctx)
 			rkvdec_iommu_restore(rkvdec);
 	}
 
-	if (need_reset)
-		rkvdec_iommu_restore(rkvdec);
-
 	if (cancel_delayed_work(&rkvdec->watchdog_work))
 		rkvdec_job_finish(ctx, state);
 
@@ -1491,7 +1487,6 @@ static irqreturn_t vdpu383_irq_handler(struct rkvdec_ctx *ctx)
 {
 	struct rkvdec_dev *rkvdec = ctx->dev;
 	enum vb2_buffer_state state;
-	bool need_reset = 0;
 	u32 status;
 
 	status = readl(rkvdec->link + VDPU383_LINK_STA_INT);
@@ -1507,9 +1502,6 @@ static irqreturn_t vdpu383_irq_handler(struct rkvdec_ctx *ctx)
 		rkvdec_iommu_restore(rkvdec);
 	}
 
-	if (need_reset)
-		rkvdec_iommu_restore(rkvdec);
-
 	if (cancel_delayed_work(&rkvdec->watchdog_work))
 		rkvdec_job_finish(ctx, state);
 

-- 
2.55.0



^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH v2 3/5] v4l2: export v4l2_m2m_set_max_parallel_jobs
  2026-08-10 15:22 [PATCH v2 0/5] media: rkvdec: Enable multi-core support Detlev Casanova
  2026-08-10 15:22 ` [PATCH v2 1/5] media: rkvdec: Keep RCB to the correct size Detlev Casanova
  2026-08-10 15:22 ` [PATCH v2 2/5] media: rkvdec: Remove unused need_reset Detlev Casanova
@ 2026-08-10 15:22 ` Detlev Casanova
  2026-08-10 15:22 ` [PATCH v2 4/5] media: rkvdec: Split into core and master platform drivers Detlev Casanova
  2026-08-10 15:22 ` [PATCH v2 5/5] media: rkvdec: Add multicore support Detlev Casanova
  4 siblings, 0 replies; 6+ messages in thread
From: Detlev Casanova @ 2026-08-10 15:22 UTC (permalink / raw)
  To: Mauro Carvalho Chehab, Ezequiel Garcia, Heiko Stuebner,
	Nicolas Dufresne, Hans Verkuil, Jonas Karlman
  Cc: Sven Püschel, kernel, linux-media, linux-kernel,
	linux-rockchip, linux-arm-kernel, Detlev Casanova

---
 drivers/media/v4l2-core/v4l2-mem2mem.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/drivers/media/v4l2-core/v4l2-mem2mem.c b/drivers/media/v4l2-core/v4l2-mem2mem.c
index 14ac9c85803d..f6f8864f23d2 100644
--- a/drivers/media/v4l2-core/v4l2-mem2mem.c
+++ b/drivers/media/v4l2-core/v4l2-mem2mem.c
@@ -128,6 +128,7 @@ void v4l2_m2m_set_max_parallel_jobs(struct v4l2_m2m_dev *m2m_dev,
 {
 	m2m_dev->max_parallel_jobs = max_parallel_jobs;
 }
+EXPORT_SYMBOL(v4l2_m2m_set_max_parallel_jobs);
 
 struct vb2_queue *v4l2_m2m_get_vq(struct v4l2_m2m_ctx *m2m_ctx,
 				       enum v4l2_buf_type type)

-- 
2.55.0



^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH v2 4/5] media: rkvdec: Split into core and master platform drivers
  2026-08-10 15:22 [PATCH v2 0/5] media: rkvdec: Enable multi-core support Detlev Casanova
                   ` (2 preceding siblings ...)
  2026-08-10 15:22 ` [PATCH v2 3/5] v4l2: export v4l2_m2m_set_max_parallel_jobs Detlev Casanova
@ 2026-08-10 15:22 ` Detlev Casanova
  2026-08-10 15:22 ` [PATCH v2 5/5] media: rkvdec: Add multicore support Detlev Casanova
  4 siblings, 0 replies; 6+ messages in thread
From: Detlev Casanova @ 2026-08-10 15:22 UTC (permalink / raw)
  To: Mauro Carvalho Chehab, Ezequiel Garcia, Heiko Stuebner,
	Nicolas Dufresne, Hans Verkuil, Jonas Karlman
  Cc: Sven Püschel, kernel, linux-media, linux-kernel,
	linux-rockchip, linux-arm-kernel, Detlev Casanova

Prepare the driver for multi-core support by restructuring around the
component framework, based on what rga does.
This commit only adds the plumbing and does not yet enable more
than a single core to run (Each additionnal core adding will return
-EBUSY).

Core specific resources are moved to a rkvdec_core struct and rkvdec_dev
only retains v4l2 device info, variant info and the main_core pointer.

Even though only 1 core is being used, a core pointer is added to the
rkvdec_ctx struct to prepare the variant backends to retrieve information
based on the core selected for the job.

Signed-off-by: Detlev Casanova <detlev.casanova@collabora.com>
---
 .../media/platform/rockchip/rkvdec/rkvdec-h264.c   |  17 +-
 .../media/platform/rockchip/rkvdec/rkvdec-hevc.c   |  16 +-
 .../media/platform/rockchip/rkvdec/rkvdec-rcb.c    |  61 +--
 .../media/platform/rockchip/rkvdec/rkvdec-rcb.h    |   7 +-
 .../platform/rockchip/rkvdec/rkvdec-vdpu381-h264.c |  24 +-
 .../platform/rockchip/rkvdec/rkvdec-vdpu381-hevc.c |  24 +-
 .../platform/rockchip/rkvdec/rkvdec-vdpu383-h264.c |  24 +-
 .../platform/rockchip/rkvdec/rkvdec-vdpu383-hevc.c |  26 +-
 .../media/platform/rockchip/rkvdec/rkvdec-vp9.c    |  27 +-
 drivers/media/platform/rockchip/rkvdec/rkvdec.c    | 506 ++++++++++++++-------
 drivers/media/platform/rockchip/rkvdec/rkvdec.h    |  23 +-
 11 files changed, 472 insertions(+), 283 deletions(-)

diff --git a/drivers/media/platform/rockchip/rkvdec/rkvdec-h264.c b/drivers/media/platform/rockchip/rkvdec/rkvdec-h264.c
index ffa606038192..d3841dc84d35 100644
--- a/drivers/media/platform/rockchip/rkvdec/rkvdec-h264.c
+++ b/drivers/media/platform/rockchip/rkvdec/rkvdec-h264.c
@@ -227,6 +227,7 @@ static void set_poc_reg(struct rkvdec_regs *regs, uint32_t poc, int id, bool bot
 static void config_registers(struct rkvdec_ctx *ctx,
 			     struct rkvdec_h264_run *run)
 {
+	struct rkvdec_core *core = ctx->core;
 	struct rkvdec_dev *rkvdec = ctx->dev;
 	const struct v4l2_ctrl_h264_decode_params *dec_params = run->decode_params;
 	const struct v4l2_ctrl_h264_sps *sps = run->sps;
@@ -333,7 +334,7 @@ static void config_registers(struct rkvdec_ctx *ctx,
 	offset = offsetof(struct rkvdec_h264_priv_tbl, err_info);
 	regs->h26x.errorinfo_base = priv_start_addr + offset;
 
-	rkvdec_memcpy_toio(rkvdec->regs, regs,
+	rkvdec_memcpy_toio(core->regs, regs,
 			   MIN(sizeof(*regs), sizeof(u32) * rkvdec->variant->num_regs));
 }
 
@@ -358,7 +359,7 @@ static int rkvdec_h264_start(struct rkvdec_ctx *ctx)
 	if (!h264_ctx)
 		return -ENOMEM;
 
-	priv_tbl = dma_alloc_coherent(rkvdec->dev, sizeof(*priv_tbl),
+	priv_tbl = dma_alloc_coherent(rkvdec->main_core->dev, sizeof(*priv_tbl),
 				      &h264_ctx->priv_tbl.dma, GFP_KERNEL);
 	if (!priv_tbl) {
 		ret = -ENOMEM;
@@ -383,7 +384,7 @@ static void rkvdec_h264_stop(struct rkvdec_ctx *ctx)
 	struct rkvdec_h264_ctx *h264_ctx = ctx->priv;
 	struct rkvdec_dev *rkvdec = ctx->dev;
 
-	dma_free_coherent(rkvdec->dev, h264_ctx->priv_tbl.size,
+	dma_free_coherent(rkvdec->main_core->dev, h264_ctx->priv_tbl.size,
 			  h264_ctx->priv_tbl.cpu, h264_ctx->priv_tbl.dma);
 	kfree(h264_ctx);
 }
@@ -391,7 +392,7 @@ static void rkvdec_h264_stop(struct rkvdec_ctx *ctx)
 static int rkvdec_h264_run(struct rkvdec_ctx *ctx)
 {
 	struct v4l2_h264_reflist_builder reflist_builder;
-	struct rkvdec_dev *rkvdec = ctx->dev;
+	struct rkvdec_core *core = ctx->core;
 	struct rkvdec_h264_ctx *h264_ctx = ctx->priv;
 	struct rkvdec_h264_run run;
 	struct rkvdec_h264_priv_tbl *tbl = h264_ctx->priv_tbl.cpu;
@@ -413,15 +414,15 @@ static int rkvdec_h264_run(struct rkvdec_ctx *ctx)
 
 	rkvdec_run_postamble(ctx, &run.base);
 
-	schedule_delayed_work(&rkvdec->watchdog_work, msecs_to_jiffies(2000));
+	schedule_delayed_work(&core->watchdog_work, msecs_to_jiffies(2000));
 
-	writel(1, rkvdec->regs + RKVDEC_REG_PREF_LUMA_CACHE_COMMAND);
-	writel(1, rkvdec->regs + RKVDEC_REG_PREF_CHR_CACHE_COMMAND);
+	writel(1, core->regs + RKVDEC_REG_PREF_LUMA_CACHE_COMMAND);
+	writel(1, core->regs + RKVDEC_REG_PREF_CHR_CACHE_COMMAND);
 
 	/* Start decoding! */
 	writel(RKVDEC_INTERRUPT_DEC_E | RKVDEC_CONFIG_DEC_CLK_GATE_E |
 	       RKVDEC_TIMEOUT_E | RKVDEC_BUF_EMPTY_E,
-	       rkvdec->regs + RKVDEC_REG_INTERRUPT);
+	       core->regs + RKVDEC_REG_INTERRUPT);
 
 	return 0;
 }
diff --git a/drivers/media/platform/rockchip/rkvdec/rkvdec-hevc.c b/drivers/media/platform/rockchip/rkvdec/rkvdec-hevc.c
index 87abf93dfd5e..c9d0913e90ee 100644
--- a/drivers/media/platform/rockchip/rkvdec/rkvdec-hevc.c
+++ b/drivers/media/platform/rockchip/rkvdec/rkvdec-hevc.c
@@ -381,6 +381,7 @@ static void assemble_sw_rps(struct rkvdec_ctx *ctx,
 static void config_registers(struct rkvdec_ctx *ctx,
 			     struct rkvdec_hevc_run *run)
 {
+	struct rkvdec_core *core = ctx->core;
 	struct rkvdec_dev *rkvdec = ctx->dev;
 	const struct v4l2_ctrl_hevc_decode_params *decode_params = run->decode_params;
 	const struct v4l2_ctrl_hevc_sps *sps = run->sps;
@@ -477,7 +478,7 @@ static void config_registers(struct rkvdec_ctx *ctx,
 	offset = offsetof(struct rkvdec_hevc_priv_tbl, rps);
 	regs->h26x.rps_base = priv_start_addr + offset;
 
-	rkvdec_memcpy_toio(rkvdec->regs, regs,
+	rkvdec_memcpy_toio(core->regs, regs,
 			   MIN(sizeof(*regs), sizeof(u32) * rkvdec->variant->num_regs));
 }
 
@@ -511,7 +512,7 @@ static int rkvdec_hevc_start(struct rkvdec_ctx *ctx)
 	if (!hevc_ctx)
 		return -ENOMEM;
 
-	priv_tbl = dma_alloc_coherent(rkvdec->dev, sizeof(*priv_tbl),
+	priv_tbl = dma_alloc_coherent(rkvdec->main_core->dev, sizeof(*priv_tbl),
 				      &hevc_ctx->priv_tbl.dma, GFP_KERNEL);
 	if (!priv_tbl) {
 		kfree(hevc_ctx);
@@ -532,13 +533,14 @@ static void rkvdec_hevc_stop(struct rkvdec_ctx *ctx)
 	struct rkvdec_hevc_ctx *hevc_ctx = ctx->priv;
 	struct rkvdec_dev *rkvdec = ctx->dev;
 
-	dma_free_coherent(rkvdec->dev, hevc_ctx->priv_tbl.size,
+	dma_free_coherent(rkvdec->main_core->dev, hevc_ctx->priv_tbl.size,
 			  hevc_ctx->priv_tbl.cpu, hevc_ctx->priv_tbl.dma);
 	kfree(hevc_ctx);
 }
 
 static int rkvdec_hevc_run(struct rkvdec_ctx *ctx)
 {
+	struct rkvdec_core *core = ctx->core;
 	struct rkvdec_dev *rkvdec = ctx->dev;
 	struct rkvdec_hevc_run run;
 	struct rkvdec_hevc_ctx *hevc_ctx = ctx->priv;
@@ -555,10 +557,10 @@ static int rkvdec_hevc_run(struct rkvdec_ctx *ctx)
 
 	rkvdec_run_postamble(ctx, &run.base);
 
-	schedule_delayed_work(&rkvdec->watchdog_work, msecs_to_jiffies(2000));
+	schedule_delayed_work(&core->watchdog_work, msecs_to_jiffies(2000));
 
-	writel(1, rkvdec->regs + RKVDEC_REG_PREF_LUMA_CACHE_COMMAND);
-	writel(1, rkvdec->regs + RKVDEC_REG_PREF_CHR_CACHE_COMMAND);
+	writel(1, core->regs + RKVDEC_REG_PREF_LUMA_CACHE_COMMAND);
+	writel(1, core->regs + RKVDEC_REG_PREF_CHR_CACHE_COMMAND);
 
 	if (rkvdec->variant->quirks & RKVDEC_QUIRK_DISABLE_QOS)
 		rkvdec_quirks_disable_qos(ctx);
@@ -568,7 +570,7 @@ static int rkvdec_hevc_run(struct rkvdec_ctx *ctx)
 		0 : RKVDEC_WR_DDR_ALIGN_EN;
 	writel(RKVDEC_INTERRUPT_DEC_E | RKVDEC_CONFIG_DEC_CLK_GATE_E |
 	       RKVDEC_TIMEOUT_E | RKVDEC_BUF_EMPTY_E | reg,
-	       rkvdec->regs + RKVDEC_REG_INTERRUPT);
+	       core->regs + RKVDEC_REG_INTERRUPT);
 
 	return 0;
 }
diff --git a/drivers/media/platform/rockchip/rkvdec/rkvdec-rcb.c b/drivers/media/platform/rockchip/rkvdec/rkvdec-rcb.c
index 191f78278c01..44df5b6df80c 100644
--- a/drivers/media/platform/rockchip/rkvdec/rkvdec-rcb.c
+++ b/drivers/media/platform/rockchip/rkvdec/rkvdec-rcb.c
@@ -29,38 +29,37 @@ static size_t rkvdec_rcb_size(const struct rcb_size_info *size_info,
 
 dma_addr_t rkvdec_rcb_buf_dma_addr(struct rkvdec_ctx *ctx, int id)
 {
-	return ctx->rcb_config->rcb_bufs[id].dma;
+	return ctx->core->rcb_config->rcb_bufs[id].dma;
 }
 
 size_t rkvdec_rcb_buf_size(struct rkvdec_ctx *ctx, int id)
 {
-	return ctx->rcb_config->rcb_bufs[id].size;
+	return ctx->core->rcb_config->rcb_bufs[id].size;
 }
 
 int rkvdec_rcb_buf_count(struct rkvdec_ctx *ctx)
 {
-	return ctx->rcb_config->rcb_count;
+	return ctx->core->rcb_config->rcb_count;
 }
 
 bool rkvdec_rcb_buf_validate_size(struct rkvdec_ctx *ctx)
 {
-	struct rkvdec_rcb_config *cfg = ctx->rcb_config;
+	struct rkvdec_rcb_config *cfg = ctx->core->rcb_config;
 
 	bool ret = cfg && cfg->height >= ctx->decoded_fmt.fmt.pix_mp.height &&
 		   cfg->width >= ctx->decoded_fmt.fmt.pix_mp.width;
 
 	if (!ret && cfg) {
-		dev_dbg(ctx->dev->dev, "RCB size %ux%u -> %ux%u\n", cfg->width, cfg->height,
+		dev_dbg(ctx->core->dev, "RCB size %ux%u -> %ux%u\n", cfg->width, cfg->height,
 			ctx->decoded_fmt.fmt.pix_mp.width, ctx->decoded_fmt.fmt.pix_mp.height);
 	}
 
 	return ret;
 }
 
-void rkvdec_free_rcb(struct rkvdec_ctx *ctx)
+void rkvdec_free_rcb(struct rkvdec_dev *rkvdec, struct rkvdec_core *core)
 {
-	struct rkvdec_dev *dev = ctx->dev;
-	struct rkvdec_rcb_config *cfg = ctx->rcb_config;
+	struct rkvdec_rcb_config *cfg = core->rcb_config;
 	unsigned long virt_addr;
 	int i;
 
@@ -77,12 +76,13 @@ void rkvdec_free_rcb(struct rkvdec_ctx *ctx)
 		case RKVDEC_ALLOC_SRAM:
 			virt_addr = (unsigned long)cfg->rcb_bufs[i].cpu;
 
-			if (dev->iommu_domain)
-				iommu_unmap(dev->iommu_domain, virt_addr, rcb_size);
-			gen_pool_free(dev->sram_pool, virt_addr, rcb_size);
+			if (iommu_get_domain_for_dev(core->dev))
+				iommu_unmap(iommu_get_domain_for_dev(core->dev),
+					    virt_addr, rcb_size);
+			gen_pool_free(core->sram_pool, virt_addr, rcb_size);
 			break;
 		case RKVDEC_ALLOC_DMA:
-			dma_free_coherent(dev->dev,
+			dma_free_coherent(rkvdec->main_core->dev,
 					  rcb_size,
 					  cfg->rcb_bufs[i].cpu,
 					  cfg->rcb_bufs[i].dma);
@@ -91,33 +91,33 @@ void rkvdec_free_rcb(struct rkvdec_ctx *ctx)
 	}
 
 	if (cfg->rcb_bufs)
-		devm_kfree(dev->dev, cfg->rcb_bufs);
+		devm_kfree(core->dev, cfg->rcb_bufs);
 
-	devm_kfree(dev->dev, cfg);
+	devm_kfree(core->dev, cfg);
 
-	ctx->rcb_config = NULL;
+	core->rcb_config = NULL;
 }
 
-int rkvdec_allocate_rcb(struct rkvdec_ctx *ctx, u32 width, u32 height,
+int rkvdec_allocate_rcb(struct rkvdec_dev *rkvdec, struct rkvdec_core *core,
+			u32 width, u32 height,
 			const struct rcb_size_info *size_info,
 			size_t rcb_count)
 {
 	int ret, i;
-	struct rkvdec_dev *rkvdec = ctx->dev;
 	struct rkvdec_rcb_config *cfg;
 
 	if (!size_info || !rcb_count) {
-		ctx->rcb_config = NULL;
+		core->rcb_config = NULL;
 		return 0;
 	}
 
-	ctx->rcb_config = devm_kzalloc(rkvdec->dev, sizeof(*ctx->rcb_config), GFP_KERNEL);
-	if (!ctx->rcb_config)
+	core->rcb_config = devm_kzalloc(core->dev, sizeof(*core->rcb_config), GFP_KERNEL);
+	if (!core->rcb_config)
 		return -ENOMEM;
 
-	cfg = ctx->rcb_config;
+	cfg = core->rcb_config;
 
-	cfg->rcb_bufs = devm_kzalloc(rkvdec->dev, sizeof(*cfg->rcb_bufs) * rcb_count, GFP_KERNEL);
+	cfg->rcb_bufs = devm_kzalloc(core->dev, sizeof(*cfg->rcb_bufs) * rcb_count, GFP_KERNEL);
 	if (!cfg->rcb_bufs) {
 		ret = -ENOMEM;
 		goto err_alloc;
@@ -133,25 +133,26 @@ int rkvdec_allocate_rcb(struct rkvdec_ctx *ctx, u32 width, u32 height,
 		enum rkvdec_alloc_type alloc_type = RKVDEC_ALLOC_SRAM;
 
 		/* Try allocating an SRAM buffer */
-		if (ctx->dev->sram_pool) {
-			if (rkvdec->iommu_domain)
+		if (core->sram_pool) {
+			if (iommu_get_domain_for_dev(core->dev))
 				rcb_size = ALIGN(rcb_size, SZ_4K);
 
-			cpu = gen_pool_dma_zalloc_align(ctx->dev->sram_pool,
+			cpu = gen_pool_dma_zalloc_align(core->sram_pool,
 							rcb_size,
 							&dma,
 							SZ_4K);
 		}
 
 		/* If an IOMMU is used, map the SRAM address through it */
-		if (cpu && rkvdec->iommu_domain) {
+		if (cpu && iommu_get_domain_for_dev(core->dev)) {
 			unsigned long virt_addr = (unsigned long)cpu;
 			phys_addr_t phys_addr = dma;
 
-			ret = iommu_map(rkvdec->iommu_domain, virt_addr, phys_addr,
+			ret = iommu_map(iommu_get_domain_for_dev(core->dev),
+					virt_addr, phys_addr,
 					rcb_size, IOMMU_READ | IOMMU_WRITE, 0);
 			if (ret) {
-				gen_pool_free(ctx->dev->sram_pool,
+				gen_pool_free(core->sram_pool,
 					      (unsigned long)cpu,
 					      rcb_size);
 				cpu = NULL;
@@ -168,7 +169,7 @@ int rkvdec_allocate_rcb(struct rkvdec_ctx *ctx, u32 width, u32 height,
 ram_fallback:
 		/* Fallback to RAM */
 		if (!cpu) {
-			cpu = dma_alloc_coherent(ctx->dev->dev,
+			cpu = dma_alloc_coherent(rkvdec->main_core->dev,
 						 rcb_size,
 						 &dma,
 						 GFP_KERNEL);
@@ -191,7 +192,7 @@ int rkvdec_allocate_rcb(struct rkvdec_ctx *ctx, u32 width, u32 height,
 	return 0;
 
 err_alloc:
-	rkvdec_free_rcb(ctx);
+	rkvdec_free_rcb(rkvdec, core);
 
 	return ret;
 }
diff --git a/drivers/media/platform/rockchip/rkvdec/rkvdec-rcb.h b/drivers/media/platform/rockchip/rkvdec/rkvdec-rcb.h
index 0662a4359bdf..d1149afe7fda 100644
--- a/drivers/media/platform/rockchip/rkvdec/rkvdec-rcb.h
+++ b/drivers/media/platform/rockchip/rkvdec/rkvdec-rcb.h
@@ -8,7 +8,9 @@
 
 #include <linux/types.h>
 
+struct rkvdec_dev;
 struct rkvdec_ctx;
+struct rkvdec_core;
 
 enum rcb_axis {
 	PIC_WIDTH = 0,
@@ -20,11 +22,12 @@ struct rcb_size_info {
 	enum rcb_axis axis;
 };
 
-int rkvdec_allocate_rcb(struct rkvdec_ctx *ctx, u32 width, u32 height,
+int rkvdec_allocate_rcb(struct rkvdec_dev *rkvdec, struct rkvdec_core *core,
+			u32 width, u32 height,
 			const struct rcb_size_info *size_info,
 			size_t rcb_count);
 dma_addr_t rkvdec_rcb_buf_dma_addr(struct rkvdec_ctx *ctx, int id);
 size_t rkvdec_rcb_buf_size(struct rkvdec_ctx *ctx, int id);
 int rkvdec_rcb_buf_count(struct rkvdec_ctx *ctx);
 bool rkvdec_rcb_buf_validate_size(struct rkvdec_ctx *ctx);
-void rkvdec_free_rcb(struct rkvdec_ctx *ctx);
+void rkvdec_free_rcb(struct rkvdec_dev *rkvdec, struct rkvdec_core *core);
diff --git a/drivers/media/platform/rockchip/rkvdec/rkvdec-vdpu381-h264.c b/drivers/media/platform/rockchip/rkvdec/rkvdec-vdpu381-h264.c
index b961fddc8583..667c5d36f3ea 100644
--- a/drivers/media/platform/rockchip/rkvdec/rkvdec-vdpu381-h264.c
+++ b/drivers/media/platform/rockchip/rkvdec/rkvdec-vdpu381-h264.c
@@ -185,22 +185,22 @@ static void assemble_hw_pps(struct rkvdec_ctx *ctx,
 
 static void rkvdec_write_regs(struct rkvdec_ctx *ctx)
 {
-	struct rkvdec_dev *rkvdec = ctx->dev;
+	struct rkvdec_core *core = ctx->core;
 	struct rkvdec_h264_ctx *h264_ctx = ctx->priv;
 
-	rkvdec_memcpy_toio(rkvdec->regs + OFFSET_COMMON_REGS,
+	rkvdec_memcpy_toio(core->regs + OFFSET_COMMON_REGS,
 			   &h264_ctx->regs.common,
 			   sizeof(h264_ctx->regs.common));
-	rkvdec_memcpy_toio(rkvdec->regs + OFFSET_CODEC_PARAMS_REGS,
+	rkvdec_memcpy_toio(core->regs + OFFSET_CODEC_PARAMS_REGS,
 			   &h264_ctx->regs.h264_param,
 			   sizeof(h264_ctx->regs.h264_param));
-	rkvdec_memcpy_toio(rkvdec->regs + OFFSET_COMMON_ADDR_REGS,
+	rkvdec_memcpy_toio(core->regs + OFFSET_COMMON_ADDR_REGS,
 			   &h264_ctx->regs.common_addr,
 			   sizeof(h264_ctx->regs.common_addr));
-	rkvdec_memcpy_toio(rkvdec->regs + OFFSET_CODEC_ADDR_REGS,
+	rkvdec_memcpy_toio(core->regs + OFFSET_CODEC_ADDR_REGS,
 			   &h264_ctx->regs.h264_addr,
 			   sizeof(h264_ctx->regs.h264_addr));
-	rkvdec_memcpy_toio(rkvdec->regs + OFFSET_POC_HIGHBIT_REGS,
+	rkvdec_memcpy_toio(core->regs + OFFSET_POC_HIGHBIT_REGS,
 			   &h264_ctx->regs.h264_highpoc,
 			   sizeof(h264_ctx->regs.h264_highpoc));
 }
@@ -368,7 +368,6 @@ static void config_registers(struct rkvdec_ctx *ctx,
 
 static int rkvdec_h264_start(struct rkvdec_ctx *ctx)
 {
-	struct rkvdec_dev *rkvdec = ctx->dev;
 	struct rkvdec_h264_priv_tbl *priv_tbl;
 	struct rkvdec_h264_ctx *h264_ctx;
 	struct v4l2_ctrl *ctrl;
@@ -387,7 +386,7 @@ static int rkvdec_h264_start(struct rkvdec_ctx *ctx)
 	if (!h264_ctx)
 		return -ENOMEM;
 
-	priv_tbl = dma_alloc_coherent(rkvdec->dev, sizeof(*priv_tbl),
+	priv_tbl = dma_alloc_coherent(ctx->dev->main_core->dev, sizeof(*priv_tbl),
 				      &h264_ctx->priv_tbl.dma, GFP_KERNEL);
 	if (!priv_tbl) {
 		ret = -ENOMEM;
@@ -410,9 +409,8 @@ static int rkvdec_h264_start(struct rkvdec_ctx *ctx)
 static void rkvdec_h264_stop(struct rkvdec_ctx *ctx)
 {
 	struct rkvdec_h264_ctx *h264_ctx = ctx->priv;
-	struct rkvdec_dev *rkvdec = ctx->dev;
 
-	dma_free_coherent(rkvdec->dev, h264_ctx->priv_tbl.size,
+	dma_free_coherent(ctx->dev->main_core->dev, h264_ctx->priv_tbl.size,
 			  h264_ctx->priv_tbl.cpu, h264_ctx->priv_tbl.dma);
 	kfree(h264_ctx);
 }
@@ -420,7 +418,7 @@ static void rkvdec_h264_stop(struct rkvdec_ctx *ctx)
 static int rkvdec_h264_run(struct rkvdec_ctx *ctx)
 {
 	struct v4l2_h264_reflist_builder reflist_builder;
-	struct rkvdec_dev *rkvdec = ctx->dev;
+	struct rkvdec_core *core = ctx->core;
 	struct rkvdec_h264_ctx *h264_ctx = ctx->priv;
 	struct rkvdec_h264_priv_tbl *tbl = h264_ctx->priv_tbl.cpu;
 	struct rkvdec_h264_run run;
@@ -443,10 +441,10 @@ static int rkvdec_h264_run(struct rkvdec_ctx *ctx)
 
 	rkvdec_run_postamble(ctx, &run.base);
 
-	rkvdec_schedule_watchdog(rkvdec, h264_ctx->regs.common.reg032_timeout_threshold);
+	rkvdec_schedule_watchdog(core, h264_ctx->regs.common.reg032_timeout_threshold);
 
 	/* Start decoding! */
-	writel(VDPU381_DEC_E_BIT, rkvdec->regs + VDPU381_REG_DEC_E);
+	writel(VDPU381_DEC_E_BIT, core->regs + VDPU381_REG_DEC_E);
 
 	return 0;
 }
diff --git a/drivers/media/platform/rockchip/rkvdec/rkvdec-vdpu381-hevc.c b/drivers/media/platform/rockchip/rkvdec/rkvdec-vdpu381-hevc.c
index fe6414a17551..bd68120b74c6 100644
--- a/drivers/media/platform/rockchip/rkvdec/rkvdec-vdpu381-hevc.c
+++ b/drivers/media/platform/rockchip/rkvdec/rkvdec-vdpu381-hevc.c
@@ -356,22 +356,22 @@ static void set_ref_valid(struct rkvdec_vdpu381_regs_hevc *regs, int id, u32 val
 
 static void rkvdec_write_regs(struct rkvdec_ctx *ctx)
 {
-	struct rkvdec_dev *rkvdec = ctx->dev;
+	struct rkvdec_core *core = ctx->core;
 	struct rkvdec_hevc_ctx *hevc_ctx = ctx->priv;
 
-	rkvdec_memcpy_toio(rkvdec->regs + OFFSET_COMMON_REGS,
+	rkvdec_memcpy_toio(core->regs + OFFSET_COMMON_REGS,
 			   &hevc_ctx->regs.common,
 			   sizeof(hevc_ctx->regs.common));
-	rkvdec_memcpy_toio(rkvdec->regs + OFFSET_CODEC_PARAMS_REGS,
+	rkvdec_memcpy_toio(core->regs + OFFSET_CODEC_PARAMS_REGS,
 			   &hevc_ctx->regs.hevc_param,
 			   sizeof(hevc_ctx->regs.hevc_param));
-	rkvdec_memcpy_toio(rkvdec->regs + OFFSET_COMMON_ADDR_REGS,
+	rkvdec_memcpy_toio(core->regs + OFFSET_COMMON_ADDR_REGS,
 			   &hevc_ctx->regs.common_addr,
 			   sizeof(hevc_ctx->regs.common_addr));
-	rkvdec_memcpy_toio(rkvdec->regs + OFFSET_CODEC_ADDR_REGS,
+	rkvdec_memcpy_toio(core->regs + OFFSET_CODEC_ADDR_REGS,
 			   &hevc_ctx->regs.hevc_addr,
 			   sizeof(hevc_ctx->regs.hevc_addr));
-	rkvdec_memcpy_toio(rkvdec->regs + OFFSET_POC_HIGHBIT_REGS,
+	rkvdec_memcpy_toio(core->regs + OFFSET_POC_HIGHBIT_REGS,
 			   &hevc_ctx->regs.hevc_highpoc,
 			   sizeof(hevc_ctx->regs.hevc_highpoc));
 }
@@ -555,7 +555,7 @@ static int rkvdec_hevc_start(struct rkvdec_ctx *ctx)
 	if (!hevc_ctx)
 		return -ENOMEM;
 
-	priv_tbl = dma_alloc_coherent(rkvdec->dev, sizeof(*priv_tbl),
+	priv_tbl = dma_alloc_coherent(rkvdec->main_core->dev, sizeof(*priv_tbl),
 				      &hevc_ctx->priv_tbl.dma, GFP_KERNEL);
 	if (!priv_tbl) {
 		ret = -ENOMEM;
@@ -580,14 +580,14 @@ static void rkvdec_hevc_stop(struct rkvdec_ctx *ctx)
 	struct rkvdec_hevc_ctx *hevc_ctx = ctx->priv;
 	struct rkvdec_dev *rkvdec = ctx->dev;
 
-	dma_free_coherent(rkvdec->dev, hevc_ctx->priv_tbl.size,
+	dma_free_coherent(rkvdec->main_core->dev, hevc_ctx->priv_tbl.size,
 			  hevc_ctx->priv_tbl.cpu, hevc_ctx->priv_tbl.dma);
 	kfree(hevc_ctx);
 }
 
 static int rkvdec_hevc_run(struct rkvdec_ctx *ctx)
 {
-	struct rkvdec_dev *rkvdec = ctx->dev;
+	struct rkvdec_core *core = ctx->core;
 	struct rkvdec_hevc_run run;
 	struct rkvdec_hevc_ctx *hevc_ctx = ctx->priv;
 	struct rkvdec_hevc_priv_tbl *tbl = hevc_ctx->priv_tbl.cpu;
@@ -604,7 +604,7 @@ static int rkvdec_hevc_run(struct rkvdec_ctx *ctx)
 	 */
 	if ((!ctx->has_sps_lt_rps && run.sps->num_long_term_ref_pics_sps) ||
 	    (!ctx->has_sps_st_rps && run.sps->num_short_term_ref_pic_sets)) {
-		dev_warn_ratelimited(rkvdec->dev, "Long and short term RPS not set\n");
+		dev_warn_ratelimited(core->dev, "Long and short term RPS not set\n");
 	} else {
 		rkvdec_hevc_assemble_hw_rps(&run, &tbl->rps, &hevc_ctx->st_cache);
 	}
@@ -613,10 +613,10 @@ static int rkvdec_hevc_run(struct rkvdec_ctx *ctx)
 
 	rkvdec_run_postamble(ctx, &run.base);
 
-	rkvdec_schedule_watchdog(rkvdec, hevc_ctx->regs.common.reg032_timeout_threshold);
+	rkvdec_schedule_watchdog(core, hevc_ctx->regs.common.reg032_timeout_threshold);
 
 	/* Start decoding! */
-	writel(VDPU381_DEC_E_BIT, rkvdec->regs + VDPU381_REG_DEC_E);
+	writel(VDPU381_DEC_E_BIT, ctx->core->regs + VDPU381_REG_DEC_E);
 
 	return 0;
 }
diff --git a/drivers/media/platform/rockchip/rkvdec/rkvdec-vdpu383-h264.c b/drivers/media/platform/rockchip/rkvdec/rkvdec-vdpu383-h264.c
index 5ec755733916..a737a7799b71 100644
--- a/drivers/media/platform/rockchip/rkvdec/rkvdec-vdpu383-h264.c
+++ b/drivers/media/platform/rockchip/rkvdec/rkvdec-vdpu383-h264.c
@@ -216,19 +216,19 @@ static void assemble_hw_pps(struct rkvdec_ctx *ctx,
 
 static void rkvdec_write_regs(struct rkvdec_ctx *ctx)
 {
-	struct rkvdec_dev *rkvdec = ctx->dev;
+	struct rkvdec_core *core = ctx->core;
 	struct rkvdec_h264_ctx *h264_ctx = ctx->priv;
 
-	rkvdec_memcpy_toio(rkvdec->regs + VDPU383_OFFSET_COMMON_REGS,
+	rkvdec_memcpy_toio(core->regs + VDPU383_OFFSET_COMMON_REGS,
 			   &h264_ctx->regs.common,
 			   sizeof(h264_ctx->regs.common));
-	rkvdec_memcpy_toio(rkvdec->regs + VDPU383_OFFSET_COMMON_ADDR_REGS,
+	rkvdec_memcpy_toio(core->regs + VDPU383_OFFSET_COMMON_ADDR_REGS,
 			   &h264_ctx->regs.common_addr,
 			   sizeof(h264_ctx->regs.common_addr));
-	rkvdec_memcpy_toio(rkvdec->regs + VDPU383_OFFSET_CODEC_PARAMS_REGS,
+	rkvdec_memcpy_toio(core->regs + VDPU383_OFFSET_CODEC_PARAMS_REGS,
 			   &h264_ctx->regs.h26x_params,
 			   sizeof(h264_ctx->regs.h26x_params));
-	rkvdec_memcpy_toio(rkvdec->regs + VDPU383_OFFSET_CODEC_ADDR_REGS,
+	rkvdec_memcpy_toio(core->regs + VDPU383_OFFSET_CODEC_ADDR_REGS,
 			   &h264_ctx->regs.h26x_addr,
 			   sizeof(h264_ctx->regs.h26x_addr));
 }
@@ -380,7 +380,7 @@ static int rkvdec_h264_start(struct rkvdec_ctx *ctx)
 	if (!h264_ctx)
 		return -ENOMEM;
 
-	priv_tbl = dma_alloc_coherent(rkvdec->dev, sizeof(*priv_tbl),
+	priv_tbl = dma_alloc_coherent(rkvdec->main_core->dev, sizeof(*priv_tbl),
 				      &h264_ctx->priv_tbl.dma, GFP_KERNEL);
 	if (!priv_tbl) {
 		ret = -ENOMEM;
@@ -406,7 +406,7 @@ static void rkvdec_h264_stop(struct rkvdec_ctx *ctx)
 	struct rkvdec_h264_ctx *h264_ctx = ctx->priv;
 	struct rkvdec_dev *rkvdec = ctx->dev;
 
-	dma_free_coherent(rkvdec->dev, h264_ctx->priv_tbl.size,
+	dma_free_coherent(rkvdec->main_core->dev, h264_ctx->priv_tbl.size,
 			  h264_ctx->priv_tbl.cpu, h264_ctx->priv_tbl.dma);
 	kfree(h264_ctx);
 }
@@ -414,7 +414,7 @@ static void rkvdec_h264_stop(struct rkvdec_ctx *ctx)
 static int rkvdec_h264_run(struct rkvdec_ctx *ctx)
 {
 	struct v4l2_h264_reflist_builder reflist_builder;
-	struct rkvdec_dev *rkvdec = ctx->dev;
+	struct rkvdec_core *core = ctx->core;
 	struct rkvdec_h264_ctx *h264_ctx = ctx->priv;
 	struct rkvdec_h264_run run;
 	struct rkvdec_h264_priv_tbl *tbl = h264_ctx->priv_tbl.cpu;
@@ -439,12 +439,12 @@ static int rkvdec_h264_run(struct rkvdec_ctx *ctx)
 	rkvdec_run_postamble(ctx, &run.base);
 
 	timeout_threshold = h264_ctx->regs.common.reg013_core_timeout_threshold;
-	rkvdec_schedule_watchdog(rkvdec, timeout_threshold);
+	rkvdec_schedule_watchdog(core, timeout_threshold);
 
 	/* Start decoding! */
-	writel(timeout_threshold, rkvdec->link + VDPU383_LINK_TIMEOUT_THRESHOLD);
-	writel(0, rkvdec->link + VDPU383_LINK_IP_ENABLE);
-	writel(VDPU383_DEC_E_BIT, rkvdec->link + VDPU383_LINK_DEC_ENABLE);
+	writel(timeout_threshold, core->link + VDPU383_LINK_TIMEOUT_THRESHOLD);
+	writel(0, core->link + VDPU383_LINK_IP_ENABLE);
+	writel(VDPU383_DEC_E_BIT, core->link + VDPU383_LINK_DEC_ENABLE);
 
 	return 0;
 }
diff --git a/drivers/media/platform/rockchip/rkvdec/rkvdec-vdpu383-hevc.c b/drivers/media/platform/rockchip/rkvdec/rkvdec-vdpu383-hevc.c
index 3575338a531a..2862231b17f2 100644
--- a/drivers/media/platform/rockchip/rkvdec/rkvdec-vdpu383-hevc.c
+++ b/drivers/media/platform/rockchip/rkvdec/rkvdec-vdpu383-hevc.c
@@ -323,19 +323,19 @@ static void assemble_hw_pps(struct rkvdec_ctx *ctx,
 
 static void rkvdec_write_regs(struct rkvdec_ctx *ctx)
 {
-	struct rkvdec_dev *rkvdec = ctx->dev;
+	struct rkvdec_core *core = ctx->core;
 	struct rkvdec_hevc_ctx *h265_ctx = ctx->priv;
 
-	rkvdec_memcpy_toio(rkvdec->regs + VDPU383_OFFSET_COMMON_REGS,
+	rkvdec_memcpy_toio(core->regs + VDPU383_OFFSET_COMMON_REGS,
 			   &h265_ctx->regs.common,
 			   sizeof(h265_ctx->regs.common));
-	rkvdec_memcpy_toio(rkvdec->regs + VDPU383_OFFSET_COMMON_ADDR_REGS,
+	rkvdec_memcpy_toio(core->regs + VDPU383_OFFSET_COMMON_ADDR_REGS,
 			   &h265_ctx->regs.common_addr,
 			   sizeof(h265_ctx->regs.common_addr));
-	rkvdec_memcpy_toio(rkvdec->regs + VDPU383_OFFSET_CODEC_PARAMS_REGS,
+	rkvdec_memcpy_toio(core->regs + VDPU383_OFFSET_CODEC_PARAMS_REGS,
 			   &h265_ctx->regs.h26x_params,
 			   sizeof(h265_ctx->regs.h26x_params));
-	rkvdec_memcpy_toio(rkvdec->regs + VDPU383_OFFSET_CODEC_ADDR_REGS,
+	rkvdec_memcpy_toio(core->regs + VDPU383_OFFSET_CODEC_ADDR_REGS,
 			   &h265_ctx->regs.h26x_addr,
 			   sizeof(h265_ctx->regs.h26x_addr));
 }
@@ -505,7 +505,7 @@ static int rkvdec_hevc_start(struct rkvdec_ctx *ctx)
 	if (!hevc_ctx)
 		return -ENOMEM;
 
-	priv_tbl = dma_alloc_coherent(rkvdec->dev, sizeof(*priv_tbl),
+	priv_tbl = dma_alloc_coherent(rkvdec->main_core->dev, sizeof(*priv_tbl),
 				      &hevc_ctx->priv_tbl.dma, GFP_KERNEL);
 	if (!priv_tbl) {
 		ret = -ENOMEM;
@@ -530,14 +530,14 @@ static void rkvdec_hevc_stop(struct rkvdec_ctx *ctx)
 	struct rkvdec_hevc_ctx *hevc_ctx = ctx->priv;
 	struct rkvdec_dev *rkvdec = ctx->dev;
 
-	dma_free_coherent(rkvdec->dev, hevc_ctx->priv_tbl.size,
+	dma_free_coherent(rkvdec->main_core->dev, hevc_ctx->priv_tbl.size,
 			  hevc_ctx->priv_tbl.cpu, hevc_ctx->priv_tbl.dma);
 	kfree(hevc_ctx);
 }
 
 static int rkvdec_hevc_run(struct rkvdec_ctx *ctx)
 {
-	struct rkvdec_dev *rkvdec = ctx->dev;
+	struct rkvdec_core *core = ctx->core;
 	struct rkvdec_hevc_run run;
 	struct rkvdec_hevc_ctx *hevc_ctx = ctx->priv;
 	struct rkvdec_hevc_priv_tbl *tbl = hevc_ctx->priv_tbl.cpu;
@@ -552,7 +552,7 @@ static int rkvdec_hevc_run(struct rkvdec_ctx *ctx)
 	 */
 	if ((!ctx->has_sps_lt_rps && run.sps->num_long_term_ref_pics_sps) ||
 	    (!ctx->has_sps_st_rps && run.sps->num_short_term_ref_pic_sets)) {
-		dev_err_ratelimited(rkvdec->dev, "Long and short term RPS not set\n");
+		dev_err_ratelimited(core->dev, "Long and short term RPS not set\n");
 		return -EINVAL;
 	}
 
@@ -566,12 +566,12 @@ static int rkvdec_hevc_run(struct rkvdec_ctx *ctx)
 	rkvdec_run_postamble(ctx, &run.base);
 
 	timeout_threshold = hevc_ctx->regs.common.reg013_core_timeout_threshold;
-	rkvdec_schedule_watchdog(rkvdec, timeout_threshold);
+	rkvdec_schedule_watchdog(core, timeout_threshold);
 
 	/* Start decoding! */
-	writel(timeout_threshold, rkvdec->link + VDPU383_LINK_TIMEOUT_THRESHOLD);
-	writel(VDPU383_IP_CRU_MODE, rkvdec->link + VDPU383_LINK_IP_ENABLE);
-	writel(VDPU383_DEC_E_BIT, rkvdec->link + VDPU383_LINK_DEC_ENABLE);
+	writel(timeout_threshold, core->link + VDPU383_LINK_TIMEOUT_THRESHOLD);
+	writel(VDPU383_IP_CRU_MODE, core->link + VDPU383_LINK_IP_ENABLE);
+	writel(VDPU383_DEC_E_BIT, core->link + VDPU383_LINK_DEC_ENABLE);
 
 	return 0;
 }
diff --git a/drivers/media/platform/rockchip/rkvdec/rkvdec-vp9.c b/drivers/media/platform/rockchip/rkvdec/rkvdec-vp9.c
index 2751f5396ee8..0b7d6b29bcfa 100644
--- a/drivers/media/platform/rockchip/rkvdec/rkvdec-vp9.c
+++ b/drivers/media/platform/rockchip/rkvdec/rkvdec-vp9.c
@@ -482,6 +482,7 @@ static void config_registers(struct rkvdec_ctx *ctx,
 	struct rkvdec_vp9_ctx *vp9_ctx = ctx->priv;
 	struct rkvdec_regs *regs = &vp9_ctx->regs;
 	const struct v4l2_vp9_segmentation *seg;
+	struct rkvdec_core *core = ctx->core;
 	struct rkvdec_dev *rkvdec = ctx->dev;
 	dma_addr_t addr;
 	bool intra_only;
@@ -657,18 +658,19 @@ static void config_registers(struct rkvdec_ctx *ctx,
 
 	regs->vp9.reg44.strmd_error_e = 0xe;
 
-	rkvdec_memcpy_toio(rkvdec->regs, regs,
+	rkvdec_memcpy_toio(core->regs, regs,
 			   MIN(sizeof(*regs), sizeof(u32) * rkvdec->variant->num_regs));
 }
 
 static int validate_dec_params(struct rkvdec_ctx *ctx,
 			       const struct v4l2_ctrl_vp9_frame *dec_params)
 {
+	struct rkvdec_core *core = ctx->core;
 	unsigned int aligned_width, aligned_height;
 
 	/* We only support profile 0. */
 	if (dec_params->profile != 0) {
-		dev_err(ctx->dev->dev, "unsupported profile %d\n",
+		dev_err(core->dev, "unsupported profile %d\n",
 			dec_params->profile);
 		return -EINVAL;
 	}
@@ -682,7 +684,7 @@ static int validate_dec_params(struct rkvdec_ctx *ctx,
 	 */
 	if (aligned_width != ctx->decoded_fmt.fmt.pix_mp.width ||
 	    aligned_height != ctx->decoded_fmt.fmt.pix_mp.height) {
-		dev_err(ctx->dev->dev,
+		dev_err(core->dev,
 			"unexpected bitstream resolution %dx%d\n",
 			dec_params->frame_width_minus_1 + 1,
 			dec_params->frame_height_minus_1 + 1);
@@ -768,6 +770,7 @@ static int rkvdec_vp9_run_preamble(struct rkvdec_ctx *ctx,
 
 static int rkvdec_vp9_run(struct rkvdec_ctx *ctx)
 {
+	struct rkvdec_core *core = ctx->core;
 	struct rkvdec_dev *rkvdec = ctx->dev;
 	struct rkvdec_vp9_run run = { };
 	int ret;
@@ -786,10 +789,10 @@ static int rkvdec_vp9_run(struct rkvdec_ctx *ctx)
 
 	rkvdec_run_postamble(ctx, &run.base);
 
-	schedule_delayed_work(&rkvdec->watchdog_work, msecs_to_jiffies(2000));
+	schedule_delayed_work(&core->watchdog_work, msecs_to_jiffies(2000));
 
-	writel(1, rkvdec->regs + RKVDEC_REG_PREF_LUMA_CACHE_COMMAND);
-	writel(1, rkvdec->regs + RKVDEC_REG_PREF_CHR_CACHE_COMMAND);
+	writel(1, core->regs + RKVDEC_REG_PREF_LUMA_CACHE_COMMAND);
+	writel(1, core->regs + RKVDEC_REG_PREF_CHR_CACHE_COMMAND);
 
 	if (rkvdec->variant->quirks & RKVDEC_QUIRK_DISABLE_QOS)
 		rkvdec_quirks_disable_qos(ctx);
@@ -797,7 +800,7 @@ static int rkvdec_vp9_run(struct rkvdec_ctx *ctx)
 	/* Start decoding! */
 	writel(RKVDEC_INTERRUPT_DEC_E | RKVDEC_CONFIG_DEC_CLK_GATE_E |
 	       RKVDEC_TIMEOUT_E | RKVDEC_BUF_EMPTY_E,
-	       rkvdec->regs + RKVDEC_REG_INTERRUPT);
+	       core->regs + RKVDEC_REG_INTERRUPT);
 
 	return 0;
 }
@@ -979,7 +982,7 @@ static int rkvdec_vp9_start(struct rkvdec_ctx *ctx)
 	ctx->priv = vp9_ctx;
 
 	BUILD_BUG_ON(sizeof(priv_tbl->probs) % 16); /* ensure probs size is 128-bit aligned */
-	priv_tbl = dma_alloc_coherent(rkvdec->dev, sizeof(*priv_tbl),
+	priv_tbl = dma_alloc_coherent(rkvdec->main_core->dev, sizeof(*priv_tbl),
 				      &vp9_ctx->priv_tbl.dma, GFP_KERNEL);
 	if (!priv_tbl) {
 		ret = -ENOMEM;
@@ -989,7 +992,7 @@ static int rkvdec_vp9_start(struct rkvdec_ctx *ctx)
 	vp9_ctx->priv_tbl.size = sizeof(*priv_tbl);
 	vp9_ctx->priv_tbl.cpu = priv_tbl;
 
-	count_tbl = dma_alloc_coherent(rkvdec->dev, RKVDEC_VP9_COUNT_SIZE,
+	count_tbl = dma_alloc_coherent(rkvdec->main_core->dev, RKVDEC_VP9_COUNT_SIZE,
 				       &vp9_ctx->count_tbl.dma, GFP_KERNEL);
 	if (!count_tbl) {
 		ret = -ENOMEM;
@@ -1003,7 +1006,7 @@ static int rkvdec_vp9_start(struct rkvdec_ctx *ctx)
 	return 0;
 
 err_free_priv_tbl:
-	dma_free_coherent(rkvdec->dev, vp9_ctx->priv_tbl.size,
+	dma_free_coherent(rkvdec->main_core->dev, vp9_ctx->priv_tbl.size,
 			  vp9_ctx->priv_tbl.cpu, vp9_ctx->priv_tbl.dma);
 
 err_free_ctx:
@@ -1016,9 +1019,9 @@ static void rkvdec_vp9_stop(struct rkvdec_ctx *ctx)
 	struct rkvdec_vp9_ctx *vp9_ctx = ctx->priv;
 	struct rkvdec_dev *rkvdec = ctx->dev;
 
-	dma_free_coherent(rkvdec->dev, vp9_ctx->count_tbl.size,
+	dma_free_coherent(rkvdec->main_core->dev, vp9_ctx->count_tbl.size,
 			  vp9_ctx->count_tbl.cpu, vp9_ctx->count_tbl.dma);
-	dma_free_coherent(rkvdec->dev, vp9_ctx->priv_tbl.size,
+	dma_free_coherent(rkvdec->main_core->dev, vp9_ctx->priv_tbl.size,
 			  vp9_ctx->priv_tbl.cpu, vp9_ctx->priv_tbl.dma);
 	kfree(vp9_ctx);
 }
diff --git a/drivers/media/platform/rockchip/rkvdec/rkvdec.c b/drivers/media/platform/rockchip/rkvdec/rkvdec.c
index db2731af06cf..d3b13132b751 100644
--- a/drivers/media/platform/rockchip/rkvdec/rkvdec.c
+++ b/drivers/media/platform/rockchip/rkvdec/rkvdec.c
@@ -11,12 +11,14 @@
 
 #include <linux/hw_bitfield.h>
 #include <linux/clk.h>
+#include <linux/component.h>
 #include <linux/genalloc.h>
 #include <linux/interrupt.h>
 #include <linux/iommu.h>
 #include <linux/module.h>
 #include <linux/of.h>
 #include <linux/of_device.h>
+#include <linux/of_platform.h>
 #include <linux/platform_device.h>
 #include <linux/pm.h>
 #include <linux/pm_runtime.h>
@@ -652,14 +654,11 @@ static int rkvdec_enum_framesizes(struct file *file, void *priv,
 static int rkvdec_querycap(struct file *file, void *priv,
 			   struct v4l2_capability *cap)
 {
-	struct rkvdec_dev *rkvdec = video_drvdata(file);
 	struct video_device *vdev = video_devdata(file);
 
-	strscpy(cap->driver, rkvdec->dev->driver->name,
-		sizeof(cap->driver));
+	strscpy(cap->driver, "rkvdec", sizeof(cap->driver));
 	strscpy(cap->card, vdev->name, sizeof(cap->card));
-	snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s",
-		 rkvdec->dev->driver->name);
+	snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:rkvdec");
 	return 0;
 }
 
@@ -1026,8 +1025,6 @@ static void rkvdec_stop_streaming(struct vb2_queue *q)
 
 		if (desc->ops->stop)
 			desc->ops->stop(ctx);
-
-		rkvdec_free_rcb(ctx);
 	}
 
 	rkvdec_queue_cleanup(q, VB2_BUF_STATE_ERROR);
@@ -1062,26 +1059,28 @@ static const struct media_device_ops rkvdec_media_ops = {
 };
 
 static void rkvdec_job_finish_no_pm(struct rkvdec_ctx *ctx,
-				    enum vb2_buffer_state result)
+				  enum vb2_buffer_state result)
 {
+	struct v4l2_m2m_ctx *m2m_ctx = ctx->fh.m2m_ctx;
+	struct v4l2_m2m_dev *m2m_dev = m2m_ctx->m2m_dev;
+
 	if (ctx->coded_fmt_desc->ops->done) {
 		struct vb2_v4l2_buffer *src_buf, *dst_buf;
 
-		src_buf = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx);
-		dst_buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx);
+		src_buf = v4l2_m2m_next_src_buf(m2m_ctx);
+		dst_buf = v4l2_m2m_next_dst_buf(m2m_ctx);
 		ctx->coded_fmt_desc->ops->done(ctx, src_buf, dst_buf, result);
 	}
 
-	v4l2_m2m_buf_done_and_job_finish(ctx->dev->m2m_dev, ctx->fh.m2m_ctx,
-					 result);
+	v4l2_m2m_buf_done_and_job_finish(m2m_dev, m2m_ctx, result);
 }
 
 static void rkvdec_job_finish(struct rkvdec_ctx *ctx,
-			      enum vb2_buffer_state result)
+			    enum vb2_buffer_state result)
 {
-	struct rkvdec_dev *rkvdec = ctx->dev;
+	pm_runtime_mark_last_busy(ctx->core->dev);
+	pm_runtime_put_autosuspend(ctx->core->dev);
 
-	pm_runtime_put_autosuspend(rkvdec->dev);
 	rkvdec_job_finish_no_pm(ctx, result);
 }
 
@@ -1112,14 +1111,14 @@ void rkvdec_run_postamble(struct rkvdec_ctx *ctx, struct rkvdec_run *run)
 
 void rkvdec_quirks_disable_qos(struct rkvdec_ctx *ctx)
 {
-	struct rkvdec_dev *rkvdec = ctx->dev;
+	struct rkvdec_core *core = ctx->core;
 	u32 reg;
 
 	/* Set undocumented swreg_block_gating_e field */
-	reg = readl(rkvdec->regs + RKVDEC_REG_QOS_CTRL);
+	reg = readl(core->regs + RKVDEC_REG_QOS_CTRL);
 	reg &= GENMASK(31, 16);
 	reg |= 0xEFFF;
-	writel(reg, rkvdec->regs + RKVDEC_REG_QOS_CTRL);
+	writel(reg, core->regs + RKVDEC_REG_QOS_CTRL);
 }
 
 void rkvdec_memcpy_toio(void __iomem *dst, void *src, size_t len)
@@ -1131,40 +1130,42 @@ void rkvdec_memcpy_toio(void __iomem *dst, void *src, size_t len)
 #endif
 }
 
-void rkvdec_schedule_watchdog(struct rkvdec_dev *rkvdec, u32 timeout_threshold)
+void rkvdec_schedule_watchdog(struct rkvdec_core *core, u32 timeout_threshold)
 {
 	/* Set watchdog at 2 times the hardware timeout threshold */
 	u32 watchdog_time;
-	unsigned long axi_rate = clk_get_rate(rkvdec->axi_clk);
+	unsigned long axi_rate = clk_get_rate(core->axi_clk);
 
 	if (axi_rate)
 		watchdog_time = 2 * div_u64(1000 * (u64)timeout_threshold, axi_rate);
 	else
 		watchdog_time = 2000;
 
-	schedule_delayed_work(&rkvdec->watchdog_work, msecs_to_jiffies(watchdog_time));
+	schedule_delayed_work(&core->watchdog_work, msecs_to_jiffies(watchdog_time));
 }
 
 static void rkvdec_device_run(void *priv)
 {
 	struct rkvdec_ctx *ctx = priv;
-	struct rkvdec_dev *rkvdec = ctx->dev;
 	const struct rkvdec_coded_fmt_desc *desc = ctx->coded_fmt_desc;
 	int ret;
 
 	if (WARN_ON(!desc))
 		return;
 
-	ret = pm_runtime_resume_and_get(rkvdec->dev);
+	ctx->core = ctx->dev->main_core;
+	ctx->core->curr_ctx = ctx;
+
+	ret = pm_runtime_resume_and_get(ctx->core->dev);
 	if (ret < 0) {
 		rkvdec_job_finish_no_pm(ctx, VB2_BUF_STATE_ERROR);
 		return;
 	}
 
 	if (!rkvdec_rcb_buf_validate_size(ctx)) {
-		rkvdec_free_rcb(ctx);
+		rkvdec_free_rcb(ctx->dev, ctx->core);
 
-		ret = rkvdec_allocate_rcb(ctx,
+		ret = rkvdec_allocate_rcb(ctx->dev, ctx->core,
 					  ctx->decoded_fmt.fmt.pix_mp.width,
 					  ctx->decoded_fmt.fmt.pix_mp.height,
 					  ctx->dev->variant->rcb_sizes,
@@ -1340,10 +1341,11 @@ static const struct v4l2_file_operations rkvdec_fops = {
 static int rkvdec_v4l2_init(struct rkvdec_dev *rkvdec)
 {
 	int ret;
+	struct device *dev = rkvdec->main_core->dev;
 
-	ret = v4l2_device_register(rkvdec->dev, &rkvdec->v4l2_dev);
+	ret = v4l2_device_register(dev, &rkvdec->v4l2_dev);
 	if (ret) {
-		dev_err(rkvdec->dev, "Failed to register V4L2 device\n");
+		dev_err(dev, "Failed to register V4L2 device\n");
 		return ret;
 	}
 
@@ -1354,7 +1356,7 @@ static int rkvdec_v4l2_init(struct rkvdec_dev *rkvdec)
 		goto err_unregister_v4l2;
 	}
 
-	rkvdec->mdev.dev = rkvdec->dev;
+	rkvdec->mdev.dev = dev;
 	strscpy(rkvdec->mdev.model, "rkvdec", sizeof(rkvdec->mdev.model));
 	strscpy(rkvdec->mdev.bus_info, "platform:rkvdec",
 		sizeof(rkvdec->mdev.bus_info));
@@ -1420,9 +1422,9 @@ static void rkvdec_v4l2_cleanup(struct rkvdec_dev *rkvdec)
 	v4l2_device_unregister(&rkvdec->v4l2_dev);
 }
 
-static void rkvdec_iommu_restore(struct rkvdec_dev *rkvdec)
+static void rkvdec_iommu_restore(struct rkvdec_core *core)
 {
-	if (rkvdec->empty_domain) {
+	if (core->empty_domain) {
 		/*
 		 * To rewrite mapping into the attached IOMMU core, attach a new empty domain that
 		 * will program an empty table, then detach it to restore the default domain and
@@ -1430,29 +1432,29 @@ static void rkvdec_iommu_restore(struct rkvdec_dev *rkvdec)
 		 * This is safely done in this interrupt handler to make sure no memory get mapped
 		 * through the IOMMU while the empty domain is attached.
 		 */
-		iommu_attach_device(rkvdec->empty_domain, rkvdec->dev);
-		iommu_detach_device(rkvdec->empty_domain, rkvdec->dev);
+		iommu_attach_device(core->empty_domain, core->dev);
+		iommu_detach_device(core->empty_domain, core->dev);
 	}
 }
 
 static irqreturn_t rk3399_irq_handler(struct rkvdec_ctx *ctx)
 {
-	struct rkvdec_dev *rkvdec = ctx->dev;
+	struct rkvdec_core *core = ctx->core;
 	enum vb2_buffer_state state;
 	u32 status;
 
-	status = readl(rkvdec->regs + RKVDEC_REG_INTERRUPT);
-	writel(0, rkvdec->regs + RKVDEC_REG_INTERRUPT);
+	status = readl(core->regs + RKVDEC_REG_INTERRUPT);
+	writel(0, core->regs + RKVDEC_REG_INTERRUPT);
 
 	if (status & RKVDEC_RDY_STA) {
 		state = VB2_BUF_STATE_DONE;
 	} else {
 		state = VB2_BUF_STATE_ERROR;
 		if (status & RKVDEC_SOFTRESET_RDY)
-			rkvdec_iommu_restore(rkvdec);
+			rkvdec_iommu_restore(core);
 	}
 
-	if (cancel_delayed_work(&rkvdec->watchdog_work))
+	if (cancel_delayed_work(&core->watchdog_work))
 		rkvdec_job_finish(ctx, state);
 
 	return IRQ_HANDLED;
@@ -1460,12 +1462,12 @@ static irqreturn_t rk3399_irq_handler(struct rkvdec_ctx *ctx)
 
 static irqreturn_t vdpu381_irq_handler(struct rkvdec_ctx *ctx)
 {
-	struct rkvdec_dev *rkvdec = ctx->dev;
+	struct rkvdec_core *core = ctx->core;
 	enum vb2_buffer_state state;
 	u32 status;
 
-	status = readl(rkvdec->regs + VDPU381_REG_STA_INT);
-	writel(0, rkvdec->regs + VDPU381_REG_STA_INT);
+	status = readl(core->regs + VDPU381_REG_STA_INT);
+	writel(0, core->regs + VDPU381_REG_STA_INT);
 
 	if (status & VDPU381_STA_INT_DEC_RDY_STA) {
 		state = VB2_BUF_STATE_DONE;
@@ -1474,10 +1476,10 @@ static irqreturn_t vdpu381_irq_handler(struct rkvdec_ctx *ctx)
 		if (status & (VDPU381_STA_INT_SOFTRESET_RDY |
 			      VDPU381_STA_INT_TIMEOUT |
 			      VDPU381_STA_INT_ERROR))
-			rkvdec_iommu_restore(rkvdec);
+			rkvdec_iommu_restore(core);
 	}
 
-	if (cancel_delayed_work(&rkvdec->watchdog_work))
+	if (cancel_delayed_work(&core->watchdog_work))
 		rkvdec_job_finish(ctx, state);
 
 	return IRQ_HANDLED;
@@ -1485,24 +1487,24 @@ static irqreturn_t vdpu381_irq_handler(struct rkvdec_ctx *ctx)
 
 static irqreturn_t vdpu383_irq_handler(struct rkvdec_ctx *ctx)
 {
-	struct rkvdec_dev *rkvdec = ctx->dev;
+	struct rkvdec_core *core = ctx->core;
 	enum vb2_buffer_state state;
 	u32 status;
 
-	status = readl(rkvdec->link + VDPU383_LINK_STA_INT);
-	writel(FIELD_PREP_WM16(VDPU383_STA_INT_ALL, 0), rkvdec->link + VDPU383_LINK_STA_INT);
+	status = readl(core->link + VDPU383_LINK_STA_INT);
+	writel(FIELD_PREP_WM16(VDPU383_STA_INT_ALL, 0), core->link + VDPU383_LINK_STA_INT);
 	/* On vdpu383, the interrupts must be disabled */
 	writel(FIELD_PREP_WM16(VDPU383_INT_EN_IRQ | VDPU383_INT_EN_LINE_IRQ, 0),
-	       rkvdec->link + VDPU383_LINK_INT_EN);
+	       core->link + VDPU383_LINK_INT_EN);
 
 	if (status & VDPU383_STA_INT_DEC_RDY_STA) {
 		state = VB2_BUF_STATE_DONE;
 	} else {
 		state = VB2_BUF_STATE_ERROR;
-		rkvdec_iommu_restore(rkvdec);
+		rkvdec_iommu_restore(core);
 	}
 
-	if (cancel_delayed_work(&rkvdec->watchdog_work))
+	if (cancel_delayed_work(&core->watchdog_work))
 		rkvdec_job_finish(ctx, state);
 
 	return IRQ_HANDLED;
@@ -1510,11 +1512,14 @@ static irqreturn_t vdpu383_irq_handler(struct rkvdec_ctx *ctx)
 
 static irqreturn_t rkvdec_irq_handler(int irq, void *priv)
 {
-	struct rkvdec_dev *rkvdec = priv;
-	struct rkvdec_ctx *ctx = v4l2_m2m_get_curr_priv(rkvdec->m2m_dev);
-	const struct rkvdec_variant *variant = rkvdec->variant;
+	irqreturn_t ret;
+	struct rkvdec_core *core = priv;
+	struct rkvdec_ctx *ctx = core->curr_ctx;
+	const struct rkvdec_variant *variant = ctx->dev->variant;
+
+	ret = variant->ops->irq_handler(ctx);
 
-	return variant->ops->irq_handler(ctx);
+	return ret;
 }
 
 /*
@@ -1591,62 +1596,19 @@ static void vdpu383_flatten_matrices(u8 *output, const u8 *input, int matrices,
 
 static void rkvdec_watchdog_func(struct work_struct *work)
 {
-	struct rkvdec_dev *rkvdec;
+	struct rkvdec_core *core;
 	struct rkvdec_ctx *ctx;
 
-	rkvdec = container_of(to_delayed_work(work), struct rkvdec_dev,
+	core = container_of(to_delayed_work(work), struct rkvdec_core,
 			      watchdog_work);
-	ctx = v4l2_m2m_get_curr_priv(rkvdec->m2m_dev);
+	ctx = core->curr_ctx;
 	if (ctx) {
-		dev_err(rkvdec->dev, "Frame processing timed out!\n");
-		writel(RKVDEC_IRQ_DIS, rkvdec->regs + RKVDEC_REG_INTERRUPT);
+		dev_err(core->dev, "Frame processing timed out!\n");
+		writel(RKVDEC_IRQ_DIS, core->regs + RKVDEC_REG_INTERRUPT);
 		rkvdec_job_finish(ctx, VB2_BUF_STATE_ERROR);
 	}
 }
 
-/*
- * Some SoCs, like RK3588 have multiple identical VDPU cores, but the
- * kernel is currently missing support for multi-core handling. Exposing
- * separate devices for each core to userspace is bad, since that does
- * not allow scheduling tasks properly (and creates ABI). With this workaround
- * the driver will only probe for the first core and early exit for the other
- * cores. Once the driver gains multi-core support, the same technique
- * for detecting the first core can be used to cluster all cores together.
- */
-static int rkvdec_disable_multicore(struct rkvdec_dev *rkvdec)
-{
-	struct device_node *node = NULL;
-	const char *compatible;
-	bool is_first_core;
-	int ret;
-
-	/* Intentionally ignores the fallback strings */
-	ret = of_property_read_string(rkvdec->dev->of_node, "compatible", &compatible);
-	if (ret)
-		return ret;
-
-	/* The first compatible and available node found is considered the main core */
-	do {
-		node = of_find_compatible_node(node, NULL, compatible);
-		if (of_device_is_available(node))
-			break;
-	} while (node);
-
-	if (!node)
-		return -EINVAL;
-
-	is_first_core = (rkvdec->dev->of_node == node);
-
-	of_node_put(node);
-
-	if (!is_first_core) {
-		dev_info(rkvdec->dev, "missing multi-core support, ignoring this instance\n");
-		return -ENODEV;
-	}
-
-	return 0;
-}
-
 static const struct rkvdec_variant_ops rk3399_variant_ops = {
 	.irq_handler = rk3399_irq_handler,
 	.colmv_size = rkvdec_colmv_size,
@@ -1757,49 +1719,75 @@ static const struct of_device_id of_rkvdec_match[] = {
 };
 MODULE_DEVICE_TABLE(of, of_rkvdec_match);
 
-static int rkvdec_probe(struct platform_device *pdev)
+static int rkvdec_core_bind(struct device *dev, struct device *master, void *data)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct rkvdec_core *core = platform_get_drvdata(pdev);
+	struct rkvdec_dev *rkvdec = data;
+
+	if (rkvdec->main_core)
+		return dev_err_probe(dev, -EBUSY,
+				     "rkvdec already has a bound core\n");
+
+	rkvdec->main_core = core;
+
+	dev_info(dev, "Registered core\n");
+
+	return 0;
+}
+
+static const struct component_ops rkvdec_core_ops = {
+	.bind = rkvdec_core_bind,
+};
+
+static int rkvdec_core_probe(struct platform_device *pdev)
 {
 	const struct rkvdec_variant *variant;
-	struct rkvdec_dev *rkvdec;
+	struct rkvdec_core *core;
 	int ret, irq;
 
+	if (!pdev->dev.of_node)
+		return -ENODEV;
+
 	variant = of_device_get_match_data(&pdev->dev);
 	if (!variant)
-		return -EINVAL;
+		return dev_err_probe(&pdev->dev, -ENODEV, "failed to get match data\n");
 
-	rkvdec = devm_kzalloc(&pdev->dev, sizeof(*rkvdec), GFP_KERNEL);
-	if (!rkvdec)
+	core = devm_kzalloc(&pdev->dev, sizeof(*core), GFP_KERNEL);
+	if (!core)
 		return -ENOMEM;
 
-	platform_set_drvdata(pdev, rkvdec);
-	rkvdec->dev = &pdev->dev;
-	rkvdec->variant = variant;
-	mutex_init(&rkvdec->vdev_lock);
-	INIT_DELAYED_WORK(&rkvdec->watchdog_work, rkvdec_watchdog_func);
-
-	ret = rkvdec_disable_multicore(rkvdec);
-	if (ret)
-		return ret;
+	core->dev = &pdev->dev;
+	INIT_DELAYED_WORK(&core->watchdog_work, rkvdec_watchdog_func);
 
-	ret = devm_clk_bulk_get_all_enabled(&pdev->dev, &rkvdec->clocks);
+	ret = devm_clk_bulk_get_all_enabled(&pdev->dev, &core->clocks);
 	if (ret < 0)
 		return ret;
+	core->num_clocks = ret;
 
-	rkvdec->num_clocks = ret;
-	rkvdec->axi_clk = devm_clk_get(&pdev->dev, "axi");
+	core->axi_clk = devm_clk_get(&pdev->dev, "axi");
 
-	if (rkvdec->variant->has_single_reg_region) {
-		rkvdec->regs = devm_platform_ioremap_resource(pdev, 0);
-		if (IS_ERR(rkvdec->regs))
-			return PTR_ERR(rkvdec->regs);
+	if (variant->has_single_reg_region) {
+		core->regs = devm_platform_ioremap_resource(pdev, 0);
+		if (IS_ERR(core->regs))
+			return PTR_ERR(core->regs);
 	} else {
-		rkvdec->regs = devm_platform_ioremap_resource_byname(pdev, "function");
-		if (IS_ERR(rkvdec->regs))
-			return PTR_ERR(rkvdec->regs);
+		core->regs = devm_platform_ioremap_resource_byname(pdev, "function");
+		if (IS_ERR(core->regs))
+			return PTR_ERR(core->regs);
+
+		core->link = devm_platform_ioremap_resource_byname(pdev, "link");
+		if (IS_ERR(core->link))
+			return PTR_ERR(core->link);
+	}
+
+	if (iommu_get_domain_for_dev(&pdev->dev)) {
+		core->empty_domain = iommu_paging_domain_alloc(core->dev);
 
-		rkvdec->link = devm_platform_ioremap_resource_byname(pdev, "link");
-		if (IS_ERR(rkvdec->link))
-			return PTR_ERR(rkvdec->link);
+		if (IS_ERR(core->empty_domain)) {
+			dev_warn(core->dev, "cannot alloc new empty domain\n");
+			core->empty_domain = NULL;
+		}
 	}
 
 	ret = dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32));
@@ -1816,32 +1804,26 @@ static int rkvdec_probe(struct platform_device *pdev)
 
 	ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
 					rkvdec_irq_handler, IRQF_ONESHOT,
-					dev_name(&pdev->dev), rkvdec);
+					dev_name(&pdev->dev), core);
 	if (ret) {
-		dev_err(&pdev->dev, "Could not request vdec IRQ\n");
+		dev_err(&pdev->dev, "Could not request core IRQ\n");
 		return ret;
 	}
 
-	rkvdec->sram_pool = of_gen_pool_get(pdev->dev.of_node, "sram", 0);
-	if (!rkvdec->sram_pool && rkvdec->variant->num_rcb_sizes > 0)
+	core->sram_pool = of_gen_pool_get(pdev->dev.of_node, "sram", 0);
+	if (!core->sram_pool && variant->num_rcb_sizes > 0)
 		dev_info(&pdev->dev, "No sram node, RCB will be stored in RAM\n");
 
 	pm_runtime_set_autosuspend_delay(&pdev->dev, 100);
 	pm_runtime_use_autosuspend(&pdev->dev);
 	pm_runtime_enable(&pdev->dev);
 
-	ret = rkvdec_v4l2_init(rkvdec);
-	if (ret)
-		goto err_disable_runtime_pm;
+	platform_set_drvdata(pdev, core);
 
-	rkvdec->iommu_domain = iommu_get_domain_for_dev(&pdev->dev);
-	if (rkvdec->iommu_domain) {
-		rkvdec->empty_domain = iommu_paging_domain_alloc(rkvdec->dev);
-
-		if (IS_ERR(rkvdec->empty_domain)) {
-			rkvdec->empty_domain = NULL;
-			dev_warn(rkvdec->dev, "cannot alloc new empty domain\n");
-		}
+	ret = component_add(&pdev->dev, &rkvdec_core_ops);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "failed to register component: %d\n", ret);
+		goto err_disable_runtime_pm;
 	}
 
 	return 0;
@@ -1849,60 +1831,254 @@ static int rkvdec_probe(struct platform_device *pdev)
 err_disable_runtime_pm:
 	pm_runtime_dont_use_autosuspend(&pdev->dev);
 	pm_runtime_disable(&pdev->dev);
-
-	if (rkvdec->sram_pool)
-		gen_pool_destroy(rkvdec->sram_pool);
+	if (core->empty_domain)
+		iommu_domain_free(core->empty_domain);
 
 	return ret;
 }
 
-static void rkvdec_remove(struct platform_device *pdev)
+static void rkvdec_core_remove(struct platform_device *pdev)
 {
-	struct rkvdec_dev *rkvdec = platform_get_drvdata(pdev);
+	struct rkvdec_core *core = platform_get_drvdata(pdev);
 
-	cancel_delayed_work_sync(&rkvdec->watchdog_work);
+	component_del(&pdev->dev, &rkvdec_core_ops);
 
-	rkvdec_v4l2_cleanup(rkvdec);
-	pm_runtime_disable(&pdev->dev);
 	pm_runtime_dont_use_autosuspend(&pdev->dev);
+	pm_runtime_disable(&pdev->dev);
 
-	if (rkvdec->empty_domain)
-		iommu_domain_free(rkvdec->empty_domain);
+	if (core->empty_domain)
+		iommu_domain_free(core->empty_domain);
 }
 
 #ifdef CONFIG_PM
-static int rkvdec_runtime_resume(struct device *dev)
+static int rkvdec_core_runtime_resume(struct device *dev)
 {
-	struct rkvdec_dev *rkvdec = dev_get_drvdata(dev);
+	struct rkvdec_core *core = dev_get_drvdata(dev);
 
-	return clk_bulk_prepare_enable(rkvdec->num_clocks, rkvdec->clocks);
+	return clk_bulk_prepare_enable(core->num_clocks, core->clocks);
 }
 
-static int rkvdec_runtime_suspend(struct device *dev)
+static int rkvdec_core_runtime_suspend(struct device *dev)
 {
-	struct rkvdec_dev *rkvdec = dev_get_drvdata(dev);
+	struct rkvdec_core *core = dev_get_drvdata(dev);
+
+	clk_bulk_disable_unprepare(core->num_clocks, core->clocks);
 
-	clk_bulk_disable_unprepare(rkvdec->num_clocks, rkvdec->clocks);
 	return 0;
 }
 #endif
 
-static const struct dev_pm_ops rkvdec_pm_ops = {
+static const struct dev_pm_ops rkvdec_core_pm_ops = {
 	SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
 				pm_runtime_force_resume)
-	SET_RUNTIME_PM_OPS(rkvdec_runtime_suspend, rkvdec_runtime_resume, NULL)
+	SET_RUNTIME_PM_OPS(rkvdec_core_runtime_suspend,
+			   rkvdec_core_runtime_resume, NULL)
+};
+
+static struct platform_driver rkvdec_core_pdrv = {
+	.probe = rkvdec_core_probe,
+	.remove = rkvdec_core_remove,
+	.driver = {
+		   .name = "rkvdec-core",
+		   .of_match_table = of_rkvdec_match,
+		   .pm = &rkvdec_core_pm_ops,
+	},
+};
+
+static int rkvdec_bind(struct device *dev)
+{
+	struct rkvdec_dev *rkvdec = dev_get_drvdata(dev);
+	int ret;
+
+	ret = component_bind_all(dev, rkvdec);
+	if (ret) {
+		dev_err(dev, "component bind failed\n");
+		return ret;
+	}
+
+	ret = rkvdec_v4l2_init(rkvdec);
+	if (ret)
+		goto err_unbind;
+
+	return 0;
+
+err_unbind:
+	component_unbind_all(dev, NULL);
+	return ret;
+}
+
+static void rkvdec_unbind(struct device *dev)
+{
+	struct rkvdec_dev *rkvdec = dev_get_drvdata(dev);
+
+	cancel_delayed_work_sync(&rkvdec->main_core->watchdog_work);
+
+	rkvdec_v4l2_cleanup(rkvdec);
+
+	rkvdec_free_rcb(rkvdec, rkvdec->main_core);
+
+	component_unbind_all(dev, NULL);
+}
+
+static const struct component_master_ops rkvdec_master_ops = {
+	.bind = rkvdec_bind,
+	.unbind = rkvdec_unbind,
 };
 
-static struct platform_driver rkvdec_driver = {
+static int rkvdec_probe(struct platform_device *pdev)
+{
+	const struct of_device_id *match_desc = pdev->dev.platform_data;
+	struct device *dev = &pdev->dev;
+	struct component_match *match = NULL;
+	struct device_node *core_node;
+	struct rkvdec_dev *rkvdec;
+
+	if (!match_desc)
+		return dev_err_probe(dev, -ENODEV, "missing platform data\n");
+
+	for_each_compatible_node(core_node, NULL, match_desc->compatible) {
+		if (!of_device_is_available(core_node))
+			continue;
+
+		of_node_get(core_node);
+		component_match_add_release(dev, &match, component_release_of,
+					    component_compare_of, core_node);
+	}
+
+	if (!match)
+		return dev_err_probe(dev, -ENODEV,
+				     "no matching available component devices found\n");
+
+	rkvdec = devm_kzalloc(dev, sizeof(*rkvdec), GFP_KERNEL);
+	if (!rkvdec)
+		return -ENOMEM;
+
+	rkvdec->variant = match_desc->data;
+	if (!rkvdec->variant)
+		return dev_err_probe(dev, -ENODEV, "failed to get match data\n");
+
+	mutex_init(&rkvdec->vdev_lock);
+
+	dev_set_drvdata(dev, rkvdec);
+
+	return component_master_add_with_match(dev, &rkvdec_master_ops, match);
+}
+
+static void rkvdec_remove(struct platform_device *pdev)
+{
+	component_master_del(&pdev->dev, &rkvdec_master_ops);
+}
+
+static struct platform_driver rkvdec_pdrv = {
 	.probe = rkvdec_probe,
 	.remove = rkvdec_remove,
 	.driver = {
-		   .name = "rkvdec",
-		   .of_match_table = of_rkvdec_match,
-		   .pm = &rkvdec_pm_ops,
+		.name = "rkvdec",
 	},
 };
-module_platform_driver(rkvdec_driver);
+
+static bool rkvdec_of_has_available_node(const char *compat)
+{
+	struct device_node *node;
+
+	for_each_compatible_node(node, NULL, compat) {
+		if (of_device_is_available(node)) {
+			of_node_put(node);
+			return true;
+		}
+	}
+
+	return false;
+}
+
+static int rkvdec_create_platform_device(struct platform_device **ppdev,
+					 const struct of_device_id *match)
+{
+	struct platform_device *pdev;
+	int ret;
+
+	pdev = platform_device_alloc(match->compatible, PLATFORM_DEVID_NONE);
+	if (!pdev)
+		return -ENOMEM;
+
+	ret = platform_device_add_data(pdev, match, sizeof(*match));
+	if (ret)
+		goto free_platform_device;
+
+	ret = platform_device_add(pdev);
+	if (ret)
+		goto free_platform_device;
+
+	ret = device_driver_attach(&rkvdec_pdrv.driver, &pdev->dev);
+	if (ret)
+		goto del_platform_device;
+
+	*ppdev = pdev;
+
+	return 0;
+
+del_platform_device:
+	platform_device_del(pdev);
+free_platform_device:
+	platform_device_put(pdev);
+	return ret;
+}
+
+static struct platform_device *master_pdevs[ARRAY_SIZE(of_rkvdec_match) - 1];
+
+static int __init rkvdec_init(void)
+{
+	unsigned int i;
+	int ret;
+
+	ret = platform_driver_register(&rkvdec_core_pdrv);
+	if (ret)
+		return ret;
+
+	ret = platform_driver_register(&rkvdec_pdrv);
+	if (ret)
+		goto unregister_core_driver;
+
+	for (i = 0; i < ARRAY_SIZE(master_pdevs); i++) {
+		if (!rkvdec_of_has_available_node(of_rkvdec_match[i].compatible))
+			continue;
+
+		ret = rkvdec_create_platform_device(&master_pdevs[i],
+						    &of_rkvdec_match[i]);
+		if (ret)
+			goto unregister_platform_devices;
+	}
+
+	return 0;
+
+unregister_platform_devices:
+	for (i = 0; i < ARRAY_SIZE(master_pdevs); i++) {
+		if (master_pdevs[i]) {
+			platform_device_unregister(master_pdevs[i]);
+			master_pdevs[i] = NULL;
+		}
+	}
+	platform_driver_unregister(&rkvdec_pdrv);
+unregister_core_driver:
+	platform_driver_unregister(&rkvdec_core_pdrv);
+	return ret;
+}
+module_init(rkvdec_init);
+
+static void __exit rkvdec_exit(void)
+{
+	unsigned int i;
+
+	for (i = 0; i < ARRAY_SIZE(master_pdevs); i++) {
+		if (master_pdevs[i]) {
+			platform_device_unregister(master_pdevs[i]);
+			master_pdevs[i] = NULL;
+		}
+	}
+	platform_driver_unregister(&rkvdec_pdrv);
+	platform_driver_unregister(&rkvdec_core_pdrv);
+}
+module_exit(rkvdec_exit);
 
 MODULE_AUTHOR("Boris Brezillon <boris.brezillon@collabora.com>");
 MODULE_DESCRIPTION("Rockchip Video Decoder driver");
diff --git a/drivers/media/platform/rockchip/rkvdec/rkvdec.h b/drivers/media/platform/rockchip/rkvdec/rkvdec.h
index a24be6638b6b..36eadcfcabdb 100644
--- a/drivers/media/platform/rockchip/rkvdec/rkvdec.h
+++ b/drivers/media/platform/rockchip/rkvdec/rkvdec.h
@@ -125,23 +125,28 @@ struct rkvdec_coded_fmt_desc {
 	u32 subsystem_flags;
 };
 
-struct rkvdec_dev {
-	struct v4l2_device v4l2_dev;
-	struct media_device mdev;
-	struct video_device vdev;
-	struct v4l2_m2m_dev *m2m_dev;
+struct rkvdec_core {
 	struct device *dev;
 	struct clk_bulk_data *clocks;
 	unsigned int num_clocks;
 	struct clk *axi_clk;
 	void __iomem *regs;
 	void __iomem *link;
-	struct mutex vdev_lock; /* serializes ioctls */
 	struct delayed_work watchdog_work;
 	struct gen_pool *sram_pool;
-	struct iommu_domain *iommu_domain;
 	struct iommu_domain *empty_domain;
+	struct rkvdec_rcb_config *rcb_config;
+	struct rkvdec_ctx *curr_ctx;
+};
+
+struct rkvdec_dev {
+	struct v4l2_device v4l2_dev;
+	struct media_device mdev;
+	struct video_device vdev;
+	struct v4l2_m2m_dev *m2m_dev;
+	struct mutex vdev_lock; /* serializes ioctls */
 	const struct rkvdec_variant *variant;
+	struct rkvdec_core *main_core;
 };
 
 struct rkvdec_ctx {
@@ -152,8 +157,8 @@ struct rkvdec_ctx {
 	struct v4l2_ctrl_handler ctrl_hdl;
 	struct rkvdec_dev *dev;
 	enum rkvdec_image_fmt image_fmt;
-	struct rkvdec_rcb_config *rcb_config;
 	u32 colmv_offset;
+	struct rkvdec_core *core;
 	void *priv;
 	u8 has_sps_st_rps: 1;
 	u8 has_sps_lt_rps: 1;
@@ -179,7 +184,7 @@ struct rkvdec_aux_buf {
 void rkvdec_run_preamble(struct rkvdec_ctx *ctx, struct rkvdec_run *run);
 void rkvdec_run_postamble(struct rkvdec_ctx *ctx, struct rkvdec_run *run);
 void rkvdec_memcpy_toio(void __iomem *dst, void *src, size_t len);
-void rkvdec_schedule_watchdog(struct rkvdec_dev *rkvdec, u32 timeout_threshold);
+void rkvdec_schedule_watchdog(struct rkvdec_core *core, u32 timeout_threshold);
 
 void rkvdec_quirks_disable_qos(struct rkvdec_ctx *ctx);
 

-- 
2.55.0



^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH v2 5/5] media: rkvdec: Add multicore support
  2026-08-10 15:22 [PATCH v2 0/5] media: rkvdec: Enable multi-core support Detlev Casanova
                   ` (3 preceding siblings ...)
  2026-08-10 15:22 ` [PATCH v2 4/5] media: rkvdec: Split into core and master platform drivers Detlev Casanova
@ 2026-08-10 15:22 ` Detlev Casanova
  4 siblings, 0 replies; 6+ messages in thread
From: Detlev Casanova @ 2026-08-10 15:22 UTC (permalink / raw)
  To: Mauro Carvalho Chehab, Ezequiel Garcia, Heiko Stuebner,
	Nicolas Dufresne, Hans Verkuil, Jonas Karlman
  Cc: Sven Püschel, kernel, linux-media, linux-kernel,
	linux-rockchip, linux-arm-kernel, Detlev Casanova

Rockchip SoCs like the RK3588 have multiple independent decoder cores
defined in the device tree as separate nodes sharing the same
compatible.
Extend the component-based driver to actually drive all of them from the
same v4l2 device.

It uses the v4l2_m2m_set_max_parallel_jobs() function to set the number
of available cores, making sure that only 1 job per m2m context is
running at a given time.
This is crucial for a stateless decoder as each job may depend on the
completion of previous jobs.
This avoids adding a complex scheduler that iwould have to ensure that
all needed reference frames are fully decoded before decoding a frame.
Instead, cores will work in parallel on different streams.

As each core has its own IOMMU core, buffers must be mapped in each
core's IOMMU so that any run() call can use any core without having to
remap everything.

To do that, we use rockchip iommu domain's iommu devices list.
With that, one IOMMU domain can be mapped on multiple devices, meaning
that each call to iommu_map() will flush the new mapping on all devices
in the list.
The IOMMU domain that will have all devices in its list is the first
core's default domain.

Another domain cannot be used because VB2 allocates buffers through the
DMA engine, which uses iommu_get_dma_domain() to find the domain to map
buffers through.

The IOMMU restore function can still work as before, but needs to be more
explicit in what domain to attach the device to.
That is because detaching the empty domain will reattach the core's default
domain, which is wrong (except for the first "main" core).

The RCB temporary buffers are allocated in a dedicated SRAM, and each
core has its own SRAM, so the mapping for each core's SRAM is added in the
global domain.

Everything else is mapped through the first core's default domain, making
the driver write the mappings on both IOMMU cores.

Signed-off-by: Detlev Casanova <detlev.casanova@collabora.com>
---
 .../media/platform/rockchip/rkvdec/rkvdec-rcb.c    |  12 +--
 drivers/media/platform/rockchip/rkvdec/rkvdec.c    | 104 +++++++++++++++++++--
 drivers/media/platform/rockchip/rkvdec/rkvdec.h    |   8 ++
 3 files changed, 107 insertions(+), 17 deletions(-)

diff --git a/drivers/media/platform/rockchip/rkvdec/rkvdec-rcb.c b/drivers/media/platform/rockchip/rkvdec/rkvdec-rcb.c
index 44df5b6df80c..977e37cf209b 100644
--- a/drivers/media/platform/rockchip/rkvdec/rkvdec-rcb.c
+++ b/drivers/media/platform/rockchip/rkvdec/rkvdec-rcb.c
@@ -76,9 +76,8 @@ void rkvdec_free_rcb(struct rkvdec_dev *rkvdec, struct rkvdec_core *core)
 		case RKVDEC_ALLOC_SRAM:
 			virt_addr = (unsigned long)cfg->rcb_bufs[i].cpu;
 
-			if (iommu_get_domain_for_dev(core->dev))
-				iommu_unmap(iommu_get_domain_for_dev(core->dev),
-					    virt_addr, rcb_size);
+			if (rkvdec->iommu_global_domain)
+				iommu_unmap(rkvdec->iommu_global_domain, virt_addr, rcb_size);
 			gen_pool_free(core->sram_pool, virt_addr, rcb_size);
 			break;
 		case RKVDEC_ALLOC_DMA:
@@ -134,7 +133,7 @@ int rkvdec_allocate_rcb(struct rkvdec_dev *rkvdec, struct rkvdec_core *core,
 
 		/* Try allocating an SRAM buffer */
 		if (core->sram_pool) {
-			if (iommu_get_domain_for_dev(core->dev))
+			if (rkvdec->iommu_global_domain)
 				rcb_size = ALIGN(rcb_size, SZ_4K);
 
 			cpu = gen_pool_dma_zalloc_align(core->sram_pool,
@@ -144,12 +143,11 @@ int rkvdec_allocate_rcb(struct rkvdec_dev *rkvdec, struct rkvdec_core *core,
 		}
 
 		/* If an IOMMU is used, map the SRAM address through it */
-		if (cpu && iommu_get_domain_for_dev(core->dev)) {
+		if (cpu && rkvdec->iommu_global_domain) {
 			unsigned long virt_addr = (unsigned long)cpu;
 			phys_addr_t phys_addr = dma;
 
-			ret = iommu_map(iommu_get_domain_for_dev(core->dev),
-					virt_addr, phys_addr,
+			ret = iommu_map(rkvdec->iommu_global_domain, virt_addr, phys_addr,
 					rcb_size, IOMMU_READ | IOMMU_WRITE, 0);
 			if (ret) {
 				gen_pool_free(core->sram_pool,
diff --git a/drivers/media/platform/rockchip/rkvdec/rkvdec.c b/drivers/media/platform/rockchip/rkvdec/rkvdec.c
index d3b13132b751..35850231922e 100644
--- a/drivers/media/platform/rockchip/rkvdec/rkvdec.c
+++ b/drivers/media/platform/rockchip/rkvdec/rkvdec.c
@@ -1058,6 +1058,37 @@ static const struct media_device_ops rkvdec_media_ops = {
 	.req_queue = v4l2_m2m_request_queue,
 };
 
+/**
+ * Return a core that is available for decoding or null if no core is found.
+ * The caller should make sure to call release_core() when the core is no longer needed.
+ */
+static struct rkvdec_core *acquire_core(struct rkvdec_dev *rkvdec, struct rkvdec_ctx *ctx)
+{
+	struct rkvdec_core *core = NULL;
+
+	guard(spinlock_irqsave)(&rkvdec->cores_lock);
+
+	if (rkvdec->available_core_count) {
+		core = rkvdec->available_cores[--rkvdec->available_core_count];
+
+		// Set the current core's ctx to this ctx
+		core->curr_ctx = ctx;
+	}
+
+	return core;
+}
+
+/**
+ * Release the core to make it available for a next job.
+ */
+static void release_core(struct rkvdec_dev *rkvdec, struct rkvdec_core *core)
+{
+	guard(spinlock_irqsave)(&rkvdec->cores_lock);
+
+	core->curr_ctx = NULL;
+	rkvdec->available_cores[rkvdec->available_core_count++] = core;
+}
+
 static void rkvdec_job_finish_no_pm(struct rkvdec_ctx *ctx,
 				  enum vb2_buffer_state result)
 {
@@ -1072,6 +1103,11 @@ static void rkvdec_job_finish_no_pm(struct rkvdec_ctx *ctx,
 		ctx->coded_fmt_desc->ops->done(ctx, src_buf, dst_buf, result);
 	}
 
+	if (ctx->core) {
+		release_core(ctx->dev, ctx->core);
+		ctx->core = NULL;
+	}
+
 	v4l2_m2m_buf_done_and_job_finish(m2m_dev, m2m_ctx, result);
 }
 
@@ -1153,8 +1189,9 @@ static void rkvdec_device_run(void *priv)
 	if (WARN_ON(!desc))
 		return;
 
-	ctx->core = ctx->dev->main_core;
-	ctx->core->curr_ctx = ctx;
+	ctx->core = acquire_core(ctx->dev, ctx);
+	if (WARN_ON(!ctx->core))
+		return;
 
 	ret = pm_runtime_resume_and_get(ctx->core->dev);
 	if (ret < 0) {
@@ -1424,6 +1461,7 @@ static void rkvdec_v4l2_cleanup(struct rkvdec_dev *rkvdec)
 
 static void rkvdec_iommu_restore(struct rkvdec_core *core)
 {
+	int ret;
 	if (core->empty_domain) {
 		/*
 		 * To rewrite mapping into the attached IOMMU core, attach a new empty domain that
@@ -1432,8 +1470,14 @@ static void rkvdec_iommu_restore(struct rkvdec_core *core)
 		 * This is safely done in this interrupt handler to make sure no memory get mapped
 		 * through the IOMMU while the empty domain is attached.
 		 */
-		iommu_attach_device(core->empty_domain, core->dev);
+		iommu_detach_device(core->curr_ctx->dev->iommu_global_domain, core->dev);
+		ret = iommu_attach_device(core->empty_domain, core->dev);
+		if (ret)
+			dev_warn(core->dev, "Cannot attach empty domain: %d\n", ret);
 		iommu_detach_device(core->empty_domain, core->dev);
+		ret = iommu_attach_device(core->curr_ctx->dev->iommu_global_domain, core->dev);
+		if (ret)
+			dev_warn(core->dev, "Cannot attach global domain: %d\n", ret);
 	}
 }
 
@@ -1724,14 +1768,35 @@ static int rkvdec_core_bind(struct device *dev, struct device *master, void *dat
 	struct platform_device *pdev = to_platform_device(dev);
 	struct rkvdec_core *core = platform_get_drvdata(pdev);
 	struct rkvdec_dev *rkvdec = data;
+	int id, ret;
+
+	id = rkvdec->core_count;
+	core->id = id;
+	rkvdec->cores[id] = core;
+
+	if (id == 0)
+		rkvdec->main_core = core;
+
+	if (iommu_get_domain_for_dev(dev)) {
+		if (!rkvdec->iommu_global_domain) {
+			rkvdec->iommu_global_domain = iommu_get_domain_for_dev(dev);
+			if (IS_ERR(rkvdec->iommu_global_domain)) {
+				rkvdec->iommu_global_domain = NULL;
+				dev_warn_once(dev, "cannot get global domain\n");
+			}
+		}
 
-	if (rkvdec->main_core)
-		return dev_err_probe(dev, -EBUSY,
-				     "rkvdec already has a bound core\n");
+		if (rkvdec->iommu_global_domain) {
+			ret = iommu_attach_device(rkvdec->iommu_global_domain, dev);
+			if (ret)
+				dev_warn(dev, "cannot attach global domain to core %d\n", id);
+		}
+	}
 
-	rkvdec->main_core = core;
+	release_core(rkvdec, core);
+	rkvdec->core_count++;
 
-	dev_info(dev, "Registered core\n");
+	dev_info(dev, "Registered core %d\n", id);
 
 	return 0;
 }
@@ -1900,6 +1965,8 @@ static int rkvdec_bind(struct device *dev)
 	if (ret)
 		goto err_unbind;
 
+	v4l2_m2m_set_max_parallel_jobs(rkvdec->m2m_dev, rkvdec->core_count);
+
 	return 0;
 
 err_unbind:
@@ -1910,12 +1977,15 @@ static int rkvdec_bind(struct device *dev)
 static void rkvdec_unbind(struct device *dev)
 {
 	struct rkvdec_dev *rkvdec = dev_get_drvdata(dev);
+	int i;
 
-	cancel_delayed_work_sync(&rkvdec->main_core->watchdog_work);
+	for (i = 0; i < rkvdec->core_count; i++)
+		cancel_delayed_work_sync(&rkvdec->cores[i]->watchdog_work);
 
 	rkvdec_v4l2_cleanup(rkvdec);
 
-	rkvdec_free_rcb(rkvdec, rkvdec->main_core);
+	for (i = 0; i < rkvdec->core_count; i++)
+		rkvdec_free_rcb(rkvdec, rkvdec->cores[i]);
 
 	component_unbind_all(dev, NULL);
 }
@@ -1932,6 +2002,7 @@ static int rkvdec_probe(struct platform_device *pdev)
 	struct component_match *match = NULL;
 	struct device_node *core_node;
 	struct rkvdec_dev *rkvdec;
+	unsigned int num_cores = 0;
 
 	if (!match_desc)
 		return dev_err_probe(dev, -ENODEV, "missing platform data\n");
@@ -1943,6 +2014,7 @@ static int rkvdec_probe(struct platform_device *pdev)
 		of_node_get(core_node);
 		component_match_add_release(dev, &match, component_release_of,
 					    component_compare_of, core_node);
+		num_cores++;
 	}
 
 	if (!match)
@@ -1953,11 +2025,23 @@ static int rkvdec_probe(struct platform_device *pdev)
 	if (!rkvdec)
 		return -ENOMEM;
 
+	rkvdec->cores = devm_kcalloc(dev, num_cores, sizeof(*rkvdec->cores),
+				     GFP_KERNEL);
+	if (!rkvdec->cores)
+		return -ENOMEM;
+
+	rkvdec->available_cores = devm_kcalloc(dev, num_cores,
+					       sizeof(*rkvdec->available_cores),
+					       GFP_KERNEL);
+	if (!rkvdec->available_cores)
+		return -ENOMEM;
+
 	rkvdec->variant = match_desc->data;
 	if (!rkvdec->variant)
 		return dev_err_probe(dev, -ENODEV, "failed to get match data\n");
 
 	mutex_init(&rkvdec->vdev_lock);
+	spin_lock_init(&rkvdec->cores_lock);
 
 	dev_set_drvdata(dev, rkvdec);
 
diff --git a/drivers/media/platform/rockchip/rkvdec/rkvdec.h b/drivers/media/platform/rockchip/rkvdec/rkvdec.h
index 36eadcfcabdb..d5977cef6df7 100644
--- a/drivers/media/platform/rockchip/rkvdec/rkvdec.h
+++ b/drivers/media/platform/rockchip/rkvdec/rkvdec.h
@@ -15,6 +15,7 @@
 #include <linux/videodev2.h>
 #include <linux/wait.h>
 #include <linux/clk.h>
+#include <linux/spinlock.h>
 
 #include <media/v4l2-ctrls.h>
 #include <media/v4l2-device.h>
@@ -137,6 +138,7 @@ struct rkvdec_core {
 	struct iommu_domain *empty_domain;
 	struct rkvdec_rcb_config *rcb_config;
 	struct rkvdec_ctx *curr_ctx;
+	int id;
 };
 
 struct rkvdec_dev {
@@ -146,7 +148,13 @@ struct rkvdec_dev {
 	struct v4l2_m2m_dev *m2m_dev;
 	struct mutex vdev_lock; /* serializes ioctls */
 	const struct rkvdec_variant *variant;
+	struct rkvdec_core **cores;
+	int core_count;
+	struct rkvdec_core **available_cores;
+	unsigned int available_core_count;
+	spinlock_t cores_lock; /* serializes core list access */
 	struct rkvdec_core *main_core;
+	struct iommu_domain *iommu_global_domain;
 };
 
 struct rkvdec_ctx {

-- 
2.55.0



^ permalink raw reply related	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2026-08-10 15:22 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-10 15:22 [PATCH v2 0/5] media: rkvdec: Enable multi-core support Detlev Casanova
2026-08-10 15:22 ` [PATCH v2 1/5] media: rkvdec: Keep RCB to the correct size Detlev Casanova
2026-08-10 15:22 ` [PATCH v2 2/5] media: rkvdec: Remove unused need_reset Detlev Casanova
2026-08-10 15:22 ` [PATCH v2 3/5] v4l2: export v4l2_m2m_set_max_parallel_jobs Detlev Casanova
2026-08-10 15:22 ` [PATCH v2 4/5] media: rkvdec: Split into core and master platform drivers Detlev Casanova
2026-08-10 15:22 ` [PATCH v2 5/5] media: rkvdec: Add multicore support Detlev Casanova

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox