diff mbox series

[v2,12/34] media: iris: add video processing unit(VPU) specific register handling

Message ID 1702899149-21321-13-git-send-email-quic_dikshita@quicinc.com (mailing list archive)
State New, archived
Headers show
Series Qualcomm video encoder and decoder driver | expand

Commit Message

Dikshita Agarwal Dec. 18, 2023, 11:32 a.m. UTC
Registers are defined differently for different VPUs.
Define ops for VPU specific handling to accommodate
different VPUs. Implement boot sequence of firmware and interrupt
programming.

Signed-off-by: Dikshita Agarwal <quic_dikshita@quicinc.com>
---
 drivers/media/platform/qcom/vcodec/iris/Makefile   |   4 +-
 .../media/platform/qcom/vcodec/iris/iris_core.h    |   3 +
 drivers/media/platform/qcom/vcodec/iris/iris_hfi.c |   7 +
 .../media/platform/qcom/vcodec/iris/iris_probe.c   |   7 +
 .../media/platform/qcom/vcodec/iris/vpu_common.c   |  71 +++++++++
 .../media/platform/qcom/vcodec/iris/vpu_common.h   |  32 ++++
 .../media/platform/qcom/vcodec/iris/vpu_iris3.c    | 166 +++++++++++++++++++++
 .../media/platform/qcom/vcodec/iris/vpu_iris3.h    |  13 ++
 8 files changed, 302 insertions(+), 1 deletion(-)
 create mode 100644 drivers/media/platform/qcom/vcodec/iris/vpu_common.c
 create mode 100644 drivers/media/platform/qcom/vcodec/iris/vpu_common.h
 create mode 100644 drivers/media/platform/qcom/vcodec/iris/vpu_iris3.c
 create mode 100644 drivers/media/platform/qcom/vcodec/iris/vpu_iris3.h

Comments

Krzysztof Kozlowski Dec. 18, 2023, 4:19 p.m. UTC | #1
On 18/12/2023 12:32, Dikshita Agarwal wrote:
> Registers are defined differently for different VPUs.
> Define ops for VPU specific handling to accommodate
> different VPUs. Implement boot sequence of firmware and interrupt
> programming.
> 

...

> +
> +int write_register(struct iris_core *core, u32 reg, u32 value)
> +{
> +	void __iomem *base_addr;
> +	int ret;
> +
> +	ret = check_core_lock(core);
> +	if (ret)
> +		return ret;
> +
> +	base_addr = core->reg_base;
> +	base_addr += reg;
> +	writel_relaxed(value, base_addr);
> +
> +	/* Make sure value is written into the register */
> +	wmb();

Just don't use relaxed method. The same applies to other places like that.

> +
> +	return ret;
> +}
> +
> +int read_register(struct iris_core *core, u32 reg, u32 *value)
> +{
> +	void __iomem *base_addr;
> +
> +	base_addr = core->reg_base;
> +
> +	*value = readl_relaxed(base_addr + reg);
> +
> +	/* Make sure value is read correctly from the register */
> +	rmb();
> +
> +	return 0;
> +}
> +
> +static const struct compat_handle compat_handle[] = {
> +	{
> +		.compat                  = "qcom,sm8550-iris",
> +		.init                    = init_iris3,

Uh...

> +	},
> +};
> +
> +int init_vpu(struct iris_core *core)
> +{
> +	struct device *dev = NULL;
> +	int i, ret = 0;
> +
> +	dev = core->dev;
> +
> +	for (i = 0; i < ARRAY_SIZE(compat_handle); i++) {
> +		if (of_device_is_compatible(dev->of_node, compat_handle[i].compat)) {
> +			ret = compat_handle[i].init(core);


This does not look good. Use flags, quirks, type, pointer ops in
structures. Just look at existing code in Linux kernel. Do not
reimplement driver match data.



Best regards,
Krzysztof
Konrad Dybcio Dec. 18, 2023, 10 p.m. UTC | #2
On 12/18/23 12:32, Dikshita Agarwal wrote:
> Registers are defined differently for different VPUs.
> Define ops for VPU specific handling to accommodate
> different VPUs. Implement boot sequence of firmware and interrupt
> programming.
> 
> Signed-off-by: Dikshita Agarwal <quic_dikshita@quicinc.com>
> ---
[...]

> +int write_register(struct iris_core *core, u32 reg, u32 value)
> +{
> +	void __iomem *base_addr;
> +	int ret;
> +
> +	ret = check_core_lock(core);
> +	if (ret)
> +		return ret;
> +
> +	base_addr = core->reg_base;
> +	base_addr += reg;
> +	writel_relaxed(value, base_addr);> +
> +	/* Make sure value is written into the register */
IIUC barriers only ensure the prior writes need to be submitted
before the next ones, they don't actually guarantee the value
arrives at the destination. You would probably want to read the
register back here to guarantee that.

> +	wmb();
> +
> +	return ret;
> +}
> +
> +int read_register(struct iris_core *core, u32 reg, u32 *value)
> +{
> +	void __iomem *base_addr;
> +
> +	base_addr = core->reg_base;
> +
> +	*value = readl_relaxed(base_addr + reg);
> +
> +	/* Make sure value is read correctly from the register */
> +	rmb();
You can drop _relaxed for that and simply use readl() instead of
this entire wrapper..

> +
> +	return 0;
> +}
> +
> +static const struct compat_handle compat_handle[] = {
> +	{
> +		.compat                  = "qcom,sm8550-iris",
> +		.init                    = init_iris3,
> +	},
> +};
> +
> +int init_vpu(struct iris_core *core)
> +{
> +	struct device *dev = NULL;
> +	int i, ret = 0;
> +
> +	dev = core->dev;
> +
> +	for (i = 0; i < ARRAY_SIZE(compat_handle); i++) {
> +		if (of_device_is_compatible(dev->of_node, compat_handle[i].compat)) {
> +			ret = compat_handle[i].init(core);
> +			if (ret)
> +				return ret;
> +			break;
> +		}
> +	}
> +
> +	if (i == ARRAY_SIZE(compat_handle))
> +		return -EINVAL;
> +
> +	return ret;
> +}
what's wrong with of_match_data?

[...]

> +
> +#define call_vpu_op(d, op, ...)			\
> +	(((d) && (d)->vpu_ops && (d)->vpu_ops->op) ? \
> +	((d)->vpu_ops->op(__VA_ARGS__)) : 0)
> +
> +struct compat_handle {
> +	const char *compat;
> +	int (*init)(struct iris_core *core);
> +};
> +
> +struct vpu_ops {
> +	int (*boot_firmware)(struct iris_core *core);
> +	int (*raise_interrupt)(struct iris_core *core);
> +};
or you can simply create functions like

boot_firmware(...)
raise_interrupt(...)

that call other functions as needed (unless there's no need if e.g.
the cores are so similar)

and drop this sugar (well, bitter sugar at least to my taste) syntax
[...]

> +	int ret;
> +	u32 value;
reverse-Christmas-tree, please
(Christmas is in a week, get festive! :D)

> +
> +	value = (u32)core->iface_q_table.device_addr;
> +	ret = write_register(core, UC_REGION_ADDR_IRIS3, value);
> +	if (ret)
> +		return ret;
> +
> +	value = SHARED_QSIZE;
> +	ret = write_register(core, UC_REGION_SIZE_IRIS3, value);
> +	if (ret)
> +		return ret;
> +
> +	value = (u32)core->iface_q_table.device_addr;
> +	ret = write_register(core, QTBL_ADDR_IRIS3, value);
> +	if (ret)
> +		return ret;
> +
> +	ret = write_register(core, QTBL_INFO_IRIS3, 0x01);
> +	if (ret)
> +		return ret;
> +
> +	value = (u32)((u64)core->iface_q_table.kernel_vaddr);
lower_32_bits()

> +	ret = write_register(core, CPU_CS_VCICMDARG0_IRIS3, value);
> +	if (ret)
> +		return ret;
> +
> +	value = (u32)((u64)core->iface_q_table.kernel_vaddr >> 32);
upper_32_bits()

> +	ret = write_register(core, CPU_CS_VCICMDARG1_IRIS3, value);
> +	if (ret)
> +		return ret;
> +
> +	if (core->sfr.device_addr) {
> +		value = (u32)core->sfr.device_addr + VIDEO_ARCH_LX;
> +		ret = write_register(core, SFR_ADDR_IRIS3, value);
> +		if (ret)
> +			return ret;
you're returning ret 3 lines below anyway

> +	}
> +
> +	return ret;
> +}
> +
> +static int boot_firmware_iris3(struct iris_core *core)
> +{
> +	u32 ctrl_init = 0, ctrl_status = 0, count = 0, max_tries = 1000;
> +	int ret;
> +
> +	ret = setup_ucregion_memory_map_iris3(core);
> +	if (ret)
> +		return ret;
> +
> +	ctrl_init = BIT(0);
this should be a named #define used inline

> +
> +	ret = write_register(core, CTRL_INIT_IRIS3, ctrl_init);
> +	if (ret)
> +		return ret;
> +
> +	while (!ctrl_status && count < max_tries) {
if you take the previous feedback into account, this can become
readl_poll_timeout()

> +		ret = read_register(core, CTRL_STATUS_IRIS3, &ctrl_status);
> +		if (ret)
> +			return ret;
> +
> +		if ((ctrl_status & CTRL_ERROR_STATUS__M_IRIS3) == 0x4) {
> +			dev_err(core->dev, "invalid setting for UC_REGION\n");
> +			break;
> +		}
> +
> +		usleep_range(50, 100);
> +		count++;
> +	}
> +
> +	if (count >= max_tries) {
> +		dev_err(core->dev, "Error booting up vidc firmware\n");
> +		return -ETIME;
> +	}

> +
> +	ret = write_register(core, CPU_CS_H2XSOFTINTEN_IRIS3, 0x1);
0x1? BIT(0)? probably a named BIT(0) that deserves its own #define?
> +	if (ret)
> +		return ret;
> +
> +	ret = write_register(core, CPU_CS_X2RPMH_IRIS3, 0x0);
similarly here

btw you can just return this

> +
> +	return ret;
> +}
> +
> +static int raise_interrupt_iris3(struct iris_core *core)
> +{
> +	return write_register(core, CPU_IC_SOFTINT_IRIS3, 1 << CPU_IC_SOFTINT_H2A_SHFT_IRIS3);
FIELD_PREP/GET, please

> +}
> +
> +static const struct vpu_ops iris3_ops = {
> +	.boot_firmware = boot_firmware_iris3,
> +	.raise_interrupt = raise_interrupt_iris3,
> +};
> +
> +int init_iris3(struct iris_core *core)
> +{
> +	core->vpu_ops = &iris3_ops;
> +
> +	return 0;
> +}
what is this dead function for?

Konrad
diff mbox series

Patch

diff --git a/drivers/media/platform/qcom/vcodec/iris/Makefile b/drivers/media/platform/qcom/vcodec/iris/Makefile
index a2d5d74..90241b5 100644
--- a/drivers/media/platform/qcom/vcodec/iris/Makefile
+++ b/drivers/media/platform/qcom/vcodec/iris/Makefile
@@ -7,6 +7,8 @@  iris-objs += iris_probe.o \
              iris_helpers.o \
              iris_hfi.o \
              iris_hfi_packet.o \
-             resources.o
+             resources.o \
+             vpu_common.o \
+             vpu_iris3.o
 
 obj-$(CONFIG_VIDEO_QCOM_IRIS) += iris.o
diff --git a/drivers/media/platform/qcom/vcodec/iris/iris_core.h b/drivers/media/platform/qcom/vcodec/iris/iris_core.h
index de0cfef..64678fd 100644
--- a/drivers/media/platform/qcom/vcodec/iris/iris_core.h
+++ b/drivers/media/platform/qcom/vcodec/iris/iris_core.h
@@ -12,6 +12,7 @@ 
 #include "../hfi_queue.h"
 #include "iris_state.h"
 #include "resources.h"
+#include "vpu_common.h"
 
 /**
  * struct iris_core - holds core parameters valid for all instances
@@ -44,6 +45,7 @@ 
  * @sys_init_id: id of sys init packet
  * @header_id: id of packet header
  * @packet_id: id of packet
+ * @vpu_ops: a pointer to vpu ops
  */
 
 struct iris_core {
@@ -75,6 +77,7 @@  struct iris_core {
 	u32					sys_init_id;
 	u32					header_id;
 	u32					packet_id;
+	const struct vpu_ops			*vpu_ops;
 };
 
 int iris_core_init(struct iris_core *core);
diff --git a/drivers/media/platform/qcom/vcodec/iris/iris_hfi.c b/drivers/media/platform/qcom/vcodec/iris/iris_hfi.c
index fe16448..7b3cbbc 100644
--- a/drivers/media/platform/qcom/vcodec/iris/iris_hfi.c
+++ b/drivers/media/platform/qcom/vcodec/iris/iris_hfi.c
@@ -8,6 +8,7 @@ 
 #include "iris_helpers.h"
 #include "iris_hfi.h"
 #include "iris_hfi_packet.h"
+#include "vpu_common.h"
 
 static int iris_hfi_queue_cmd_write(struct iris_core *core, void *pkt)
 {
@@ -33,6 +34,8 @@  static int iris_hfi_queue_cmd_write(struct iris_core *core, void *pkt)
 	packet_size = header->size;
 
 	if (!write_queue(q_info, pkt, packet_size, &rx_req)) {
+		call_vpu_op(core, raise_interrupt, core);
+	} else {
 		dev_err(core->dev, "queue full\n");
 		return -ENODATA;
 	}
@@ -108,6 +111,10 @@  int iris_hfi_core_init(struct iris_core *core)
 	if (ret)
 		goto error;
 
+	ret = call_vpu_op(core, boot_firmware, core);
+	if (ret)
+		goto error;
+
 	ret = sys_init(core);
 	if (ret)
 		goto error;
diff --git a/drivers/media/platform/qcom/vcodec/iris/iris_probe.c b/drivers/media/platform/qcom/vcodec/iris/iris_probe.c
index 570c64e..773481f 100644
--- a/drivers/media/platform/qcom/vcodec/iris/iris_probe.c
+++ b/drivers/media/platform/qcom/vcodec/iris/iris_probe.c
@@ -92,6 +92,13 @@  static int iris_probe(struct platform_device *pdev)
 	if (core->irq < 0)
 		return core->irq;
 
+	ret = init_vpu(core);
+	if (ret) {
+		dev_err_probe(core->dev, ret,
+			      "%s: init vpu failed with %d\n", __func__, ret);
+		return ret;
+	}
+
 	ret = init_resources(core);
 	if (ret) {
 		dev_err_probe(core->dev, ret,
diff --git a/drivers/media/platform/qcom/vcodec/iris/vpu_common.c b/drivers/media/platform/qcom/vcodec/iris/vpu_common.c
new file mode 100644
index 0000000..3282510
--- /dev/null
+++ b/drivers/media/platform/qcom/vcodec/iris/vpu_common.c
@@ -0,0 +1,71 @@ 
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#include "vpu_iris3.h"
+#include "iris_core.h"
+#include "iris_helpers.h"
+#include "vpu_common.h"
+
+int write_register(struct iris_core *core, u32 reg, u32 value)
+{
+	void __iomem *base_addr;
+	int ret;
+
+	ret = check_core_lock(core);
+	if (ret)
+		return ret;
+
+	base_addr = core->reg_base;
+	base_addr += reg;
+	writel_relaxed(value, base_addr);
+
+	/* Make sure value is written into the register */
+	wmb();
+
+	return ret;
+}
+
+int read_register(struct iris_core *core, u32 reg, u32 *value)
+{
+	void __iomem *base_addr;
+
+	base_addr = core->reg_base;
+
+	*value = readl_relaxed(base_addr + reg);
+
+	/* Make sure value is read correctly from the register */
+	rmb();
+
+	return 0;
+}
+
+static const struct compat_handle compat_handle[] = {
+	{
+		.compat                  = "qcom,sm8550-iris",
+		.init                    = init_iris3,
+	},
+};
+
+int init_vpu(struct iris_core *core)
+{
+	struct device *dev = NULL;
+	int i, ret = 0;
+
+	dev = core->dev;
+
+	for (i = 0; i < ARRAY_SIZE(compat_handle); i++) {
+		if (of_device_is_compatible(dev->of_node, compat_handle[i].compat)) {
+			ret = compat_handle[i].init(core);
+			if (ret)
+				return ret;
+			break;
+		}
+	}
+
+	if (i == ARRAY_SIZE(compat_handle))
+		return -EINVAL;
+
+	return ret;
+}
diff --git a/drivers/media/platform/qcom/vcodec/iris/vpu_common.h b/drivers/media/platform/qcom/vcodec/iris/vpu_common.h
new file mode 100644
index 0000000..790496a
--- /dev/null
+++ b/drivers/media/platform/qcom/vcodec/iris/vpu_common.h
@@ -0,0 +1,32 @@ 
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#ifndef _VPU_COMMON_H_
+#define _VPU_COMMON_H_
+
+#include <linux/types.h>
+
+struct iris_core;
+
+#define call_vpu_op(d, op, ...)			\
+	(((d) && (d)->vpu_ops && (d)->vpu_ops->op) ? \
+	((d)->vpu_ops->op(__VA_ARGS__)) : 0)
+
+struct compat_handle {
+	const char *compat;
+	int (*init)(struct iris_core *core);
+};
+
+struct vpu_ops {
+	int (*boot_firmware)(struct iris_core *core);
+	int (*raise_interrupt)(struct iris_core *core);
+};
+
+int init_vpu(struct iris_core *core);
+
+int write_register(struct iris_core *core, u32 reg, u32 value);
+int read_register(struct iris_core *core, u32 reg, u32 *value);
+
+#endif
diff --git a/drivers/media/platform/qcom/vcodec/iris/vpu_iris3.c b/drivers/media/platform/qcom/vcodec/iris/vpu_iris3.c
new file mode 100644
index 0000000..95bf223
--- /dev/null
+++ b/drivers/media/platform/qcom/vcodec/iris/vpu_iris3.c
@@ -0,0 +1,166 @@ 
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#include <linux/delay.h>
+
+#include "vpu_iris3.h"
+
+#define VIDEO_ARCH_LX 1
+
+#define CPU_BASE_OFFS_IRIS3     0x000A0000
+
+#define CPU_CS_BASE_OFFS_IRIS3      (CPU_BASE_OFFS_IRIS3)
+#define CPU_IC_BASE_OFFS_IRIS3      (CPU_BASE_OFFS_IRIS3)
+
+#define CPU_CS_VCICMDARG0_IRIS3     (CPU_CS_BASE_OFFS_IRIS3 + 0x24)
+#define CPU_CS_VCICMDARG1_IRIS3     (CPU_CS_BASE_OFFS_IRIS3 + 0x28)
+
+/* HFI_CTRL_INIT */
+#define CPU_CS_SCIACMD_IRIS3        (CPU_CS_BASE_OFFS_IRIS3 + 0x48)
+
+/* HFI_CTRL_STATUS */
+#define CPU_CS_SCIACMDARG0_IRIS3    (CPU_CS_BASE_OFFS_IRIS3 + 0x4C)
+#define CPU_CS_SCIACMDARG0_HFI_CTRL_INIT_IDLE_MSG_BMSK_IRIS3     0x40000000
+
+#define CPU_CS_H2XSOFTINTEN_IRIS3   (CPU_CS_BASE_OFFS_IRIS3 + 0x148)
+
+#define CPU_CS_X2RPMH_IRIS3         (CPU_CS_BASE_OFFS_IRIS3 + 0x168)
+
+/* UC_REGION_ADDR */
+#define CPU_CS_SCIBARG1_IRIS3       (CPU_CS_BASE_OFFS_IRIS3 + 0x64)
+
+/* UC_REGION_ADDR */
+#define CPU_CS_SCIBARG2_IRIS3       (CPU_CS_BASE_OFFS_IRIS3 + 0x68)
+
+/* HFI_QTBL_INFO */
+#define CPU_CS_SCIACMDARG1_IRIS3    (CPU_CS_BASE_OFFS_IRIS3 + 0x50)
+
+/* HFI_QTBL_ADDR */
+#define CPU_CS_SCIACMDARG2_IRIS3    (CPU_CS_BASE_OFFS_IRIS3 + 0x54)
+
+/* SFR_ADDR */
+#define CPU_CS_SCIBCMD_IRIS3        (CPU_CS_BASE_OFFS_IRIS3 + 0x5C)
+
+#define UC_REGION_ADDR_IRIS3        CPU_CS_SCIBARG1_IRIS3
+#define UC_REGION_SIZE_IRIS3	    CPU_CS_SCIBARG2_IRIS3
+
+#define QTBL_INFO_IRIS3             CPU_CS_SCIACMDARG1_IRIS3
+#define QTBL_ADDR_IRIS3             CPU_CS_SCIACMDARG2_IRIS3
+
+#define SFR_ADDR_IRIS3              CPU_CS_SCIBCMD_IRIS3
+
+#define CTRL_INIT_IRIS3             CPU_CS_SCIACMD_IRIS3
+
+#define CTRL_STATUS_IRIS3           CPU_CS_SCIACMDARG0_IRIS3
+#define CPU_CS_SCIACMDARG0_HFI_CTRL_ERROR_STATUS_BMSK_IRIS3	0xfe
+#define CTRL_ERROR_STATUS__M_IRIS3 \
+		CPU_CS_SCIACMDARG0_HFI_CTRL_ERROR_STATUS_BMSK_IRIS3
+
+#define CPU_IC_SOFTINT_IRIS3        (CPU_IC_BASE_OFFS_IRIS3 + 0x150)
+#define CPU_IC_SOFTINT_H2A_SHFT_IRIS3	0x0
+
+static int setup_ucregion_memory_map_iris3(struct iris_core *core)
+{
+	int ret;
+	u32 value;
+
+	value = (u32)core->iface_q_table.device_addr;
+	ret = write_register(core, UC_REGION_ADDR_IRIS3, value);
+	if (ret)
+		return ret;
+
+	value = SHARED_QSIZE;
+	ret = write_register(core, UC_REGION_SIZE_IRIS3, value);
+	if (ret)
+		return ret;
+
+	value = (u32)core->iface_q_table.device_addr;
+	ret = write_register(core, QTBL_ADDR_IRIS3, value);
+	if (ret)
+		return ret;
+
+	ret = write_register(core, QTBL_INFO_IRIS3, 0x01);
+	if (ret)
+		return ret;
+
+	value = (u32)((u64)core->iface_q_table.kernel_vaddr);
+	ret = write_register(core, CPU_CS_VCICMDARG0_IRIS3, value);
+	if (ret)
+		return ret;
+
+	value = (u32)((u64)core->iface_q_table.kernel_vaddr >> 32);
+	ret = write_register(core, CPU_CS_VCICMDARG1_IRIS3, value);
+	if (ret)
+		return ret;
+
+	if (core->sfr.device_addr) {
+		value = (u32)core->sfr.device_addr + VIDEO_ARCH_LX;
+		ret = write_register(core, SFR_ADDR_IRIS3, value);
+		if (ret)
+			return ret;
+	}
+
+	return ret;
+}
+
+static int boot_firmware_iris3(struct iris_core *core)
+{
+	u32 ctrl_init = 0, ctrl_status = 0, count = 0, max_tries = 1000;
+	int ret;
+
+	ret = setup_ucregion_memory_map_iris3(core);
+	if (ret)
+		return ret;
+
+	ctrl_init = BIT(0);
+
+	ret = write_register(core, CTRL_INIT_IRIS3, ctrl_init);
+	if (ret)
+		return ret;
+
+	while (!ctrl_status && count < max_tries) {
+		ret = read_register(core, CTRL_STATUS_IRIS3, &ctrl_status);
+		if (ret)
+			return ret;
+
+		if ((ctrl_status & CTRL_ERROR_STATUS__M_IRIS3) == 0x4) {
+			dev_err(core->dev, "invalid setting for UC_REGION\n");
+			break;
+		}
+
+		usleep_range(50, 100);
+		count++;
+	}
+
+	if (count >= max_tries) {
+		dev_err(core->dev, "Error booting up vidc firmware\n");
+		return -ETIME;
+	}
+
+	ret = write_register(core, CPU_CS_H2XSOFTINTEN_IRIS3, 0x1);
+	if (ret)
+		return ret;
+
+	ret = write_register(core, CPU_CS_X2RPMH_IRIS3, 0x0);
+
+	return ret;
+}
+
+static int raise_interrupt_iris3(struct iris_core *core)
+{
+	return write_register(core, CPU_IC_SOFTINT_IRIS3, 1 << CPU_IC_SOFTINT_H2A_SHFT_IRIS3);
+}
+
+static const struct vpu_ops iris3_ops = {
+	.boot_firmware = boot_firmware_iris3,
+	.raise_interrupt = raise_interrupt_iris3,
+};
+
+int init_iris3(struct iris_core *core)
+{
+	core->vpu_ops = &iris3_ops;
+
+	return 0;
+}
diff --git a/drivers/media/platform/qcom/vcodec/iris/vpu_iris3.h b/drivers/media/platform/qcom/vcodec/iris/vpu_iris3.h
new file mode 100644
index 0000000..1424a5f
--- /dev/null
+++ b/drivers/media/platform/qcom/vcodec/iris/vpu_iris3.h
@@ -0,0 +1,13 @@ 
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2021-2023 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#ifndef _VPU_IRIS3_H_
+#define _VPU_IRIS3_H_
+
+#include "iris_core.h"
+
+int init_iris3(struct iris_core *core);
+
+#endif