[PATCH v2 14/34] media: iris: implement iris v4l2 file ops

From: Dikshita Agarwal
Date: Mon Dec 18 2023 - 06:48:11 EST


From: Vikash Garodia <quic_vgarodia@xxxxxxxxxxx>

Implements iris v4l2 file ops - open, close and poll.

Open:
Configure the vb2 queue and v4l2 file handler. Initialize
a video instance and add the instance to core instance list.
Open a session in video firmware via HFI_CMD_OPEN.

Close:
De-initialize the instance and remove it from core
instance list. Close the session in firmware via HFI_CMD_CLOSE.

Signed-off-by: Vikash Garodia <quic_vgarodia@xxxxxxxxxxx>
Signed-off-by: Dikshita Agarwal <quic_dikshita@xxxxxxxxxxx>
---
drivers/media/platform/qcom/vcodec/iris/Makefile | 2 +
.../media/platform/qcom/vcodec/iris/hfi_defines.h | 2 +
.../media/platform/qcom/vcodec/iris/iris_common.h | 26 ++
.../media/platform/qcom/vcodec/iris/iris_core.c | 42 ++-
.../media/platform/qcom/vcodec/iris/iris_core.h | 8 +
.../media/platform/qcom/vcodec/iris/iris_helpers.c | 62 +++++
.../media/platform/qcom/vcodec/iris/iris_helpers.h | 2 +
drivers/media/platform/qcom/vcodec/iris/iris_hfi.c | 115 +++++++-
drivers/media/platform/qcom/vcodec/iris/iris_hfi.h | 3 +
.../platform/qcom/vcodec/iris/iris_hfi_packet.c | 28 ++
.../platform/qcom/vcodec/iris/iris_hfi_packet.h | 7 +
.../platform/qcom/vcodec/iris/iris_instance.h | 48 ++++
.../media/platform/qcom/vcodec/iris/iris_probe.c | 10 +
.../media/platform/qcom/vcodec/iris/iris_vdec.c | 47 ++++
.../media/platform/qcom/vcodec/iris/iris_vdec.h | 14 +
.../media/platform/qcom/vcodec/iris/iris_vidc.c | 298 +++++++++++++++++++++
.../media/platform/qcom/vcodec/iris/iris_vidc.h | 15 ++
17 files changed, 719 insertions(+), 10 deletions(-)
create mode 100644 drivers/media/platform/qcom/vcodec/iris/iris_common.h
create mode 100644 drivers/media/platform/qcom/vcodec/iris/iris_instance.h
create mode 100644 drivers/media/platform/qcom/vcodec/iris/iris_vdec.c
create mode 100644 drivers/media/platform/qcom/vcodec/iris/iris_vdec.h
create mode 100644 drivers/media/platform/qcom/vcodec/iris/iris_vidc.c
create mode 100644 drivers/media/platform/qcom/vcodec/iris/iris_vidc.h

diff --git a/drivers/media/platform/qcom/vcodec/iris/Makefile b/drivers/media/platform/qcom/vcodec/iris/Makefile
index c50e3241..3c076d0 100644
--- a/drivers/media/platform/qcom/vcodec/iris/Makefile
+++ b/drivers/media/platform/qcom/vcodec/iris/Makefile
@@ -3,6 +3,8 @@ iris-objs += ../hfi_queue.o ../firmware.o
iris-objs += iris_probe.o \
iris_state.o \
iris_core.o \
+ iris_vidc.o \
+ iris_vdec.o \
iris_state.o \
iris_helpers.o \
iris_hfi.o \
diff --git a/drivers/media/platform/qcom/vcodec/iris/hfi_defines.h b/drivers/media/platform/qcom/vcodec/iris/hfi_defines.h
index fb383b2..423ba1a 100644
--- a/drivers/media/platform/qcom/vcodec/iris/hfi_defines.h
+++ b/drivers/media/platform/qcom/vcodec/iris/hfi_defines.h
@@ -9,6 +9,8 @@
#define HFI_VIDEO_ARCH_LX 0x1

#define HFI_CMD_INIT 0x01000001
+#define HFI_CMD_OPEN 0x01000003
+#define HFI_CMD_CLOSE 0x01000004

#define HFI_PROP_IMAGE_VERSION 0x03000001

diff --git a/drivers/media/platform/qcom/vcodec/iris/iris_common.h b/drivers/media/platform/qcom/vcodec/iris/iris_common.h
new file mode 100644
index 0000000..3e4dd71
--- /dev/null
+++ b/drivers/media/platform/qcom/vcodec/iris/iris_common.h
@@ -0,0 +1,26 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+#ifndef _IRIS_COMMON_H_
+#define _IRIS_COMMON_H_
+
+#include <media/v4l2-device.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-ioctl.h>
+#include <media/videobuf2-v4l2.h>
+
+#define INPUT_MPLANE V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE
+#define OUTPUT_MPLANE V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE
+#define DEFAULT_WIDTH 320
+#define DEFAULT_HEIGHT 240
+#define DEFAULT_BUF_SIZE 115200
+
+enum signal_session_response {
+ SIGNAL_CMD_STOP_INPUT = 0,
+ SIGNAL_CMD_STOP_OUTPUT,
+ SIGNAL_CMD_CLOSE,
+ MAX_SIGNAL,
+};
+
+#endif
diff --git a/drivers/media/platform/qcom/vcodec/iris/iris_core.c b/drivers/media/platform/qcom/vcodec/iris/iris_core.c
index ba8960d..174fad9 100644
--- a/drivers/media/platform/qcom/vcodec/iris/iris_core.c
+++ b/drivers/media/platform/qcom/vcodec/iris/iris_core.c
@@ -3,12 +3,14 @@
* Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved.
*/

+#include <linux/delay.h>
+
#include "iris_core.h"
#include "iris_helpers.h"
#include "iris_hfi.h"
#include "iris_state.h"

-static int iris_core_deinit_locked(struct iris_core *core)
+int iris_core_deinit_locked(struct iris_core *core)
{
int ret;

@@ -68,3 +70,41 @@ int iris_core_init(struct iris_core *core)

return ret;
}
+
+int iris_core_init_wait(struct iris_core *core)
+{
+ const int interval = 10;
+ int max_tries, count = 0, ret = 0;
+
+ mutex_lock(&core->lock);
+ if (!core_in_valid_state(core)) {
+ ret = -EINVAL;
+ goto unlock;
+ }
+
+ if (core->state == IRIS_CORE_INIT)
+ goto unlock;
+
+ max_tries = core->cap[HW_RESPONSE_TIMEOUT].value / interval;
+ while (count < max_tries) {
+ if (core->state != IRIS_CORE_INIT_WAIT)
+ break;
+ msleep(interval);
+ count++;
+ }
+
+ if (core->state == IRIS_CORE_INIT) {
+ ret = 0;
+ goto unlock;
+ } else {
+ iris_change_core_state(core, IRIS_CORE_ERROR);
+ iris_core_deinit_locked(core);
+ ret = -EINVAL;
+ goto unlock;
+ }
+
+unlock:
+ mutex_unlock(&core->lock);
+
+ return ret;
+}
diff --git a/drivers/media/platform/qcom/vcodec/iris/iris_core.h b/drivers/media/platform/qcom/vcodec/iris/iris_core.h
index 3c8497a..b47520a 100644
--- a/drivers/media/platform/qcom/vcodec/iris/iris_core.h
+++ b/drivers/media/platform/qcom/vcodec/iris/iris_core.h
@@ -33,6 +33,8 @@
* @clk_count: count of iris clocks
* @reset_tbl: table of iris reset clocks
* @reset_count: count of iris reset clocks
+ * @vb2_ops: iris vb2 ops
+ * @vb2_mem_ops: iris vb2 memory ops
* @state: current state of core
* @iface_q_table: Interface queue table memory
* @command_queue: shared interface queue to send commands to firmware
@@ -49,6 +51,7 @@
* @vpu_ops: a pointer to vpu ops
* @platform_data: a structure for platform data
* @cap: an array for supported core capabilities
+ * @instances: a list_head of all instances
*/

struct iris_core {
@@ -67,6 +70,8 @@ struct iris_core {
u32 clk_count;
struct reset_info *reset_tbl;
u32 reset_count;
+ const struct vb2_ops *vb2_ops;
+ struct vb2_mem_ops *vb2_mem_ops;
enum iris_core_state state;
struct mem_desc iface_q_table;
struct iface_q_info command_queue;
@@ -83,9 +88,12 @@ struct iris_core {
const struct vpu_ops *vpu_ops;
struct platform_data *platform_data;
struct plat_core_cap cap[CORE_CAP_MAX + 1];
+ struct list_head instances;
};

int iris_core_init(struct iris_core *core);
+int iris_core_init_wait(struct iris_core *core);
int iris_core_deinit(struct iris_core *core);
+int iris_core_deinit_locked(struct iris_core *core);

#endif
diff --git a/drivers/media/platform/qcom/vcodec/iris/iris_helpers.c b/drivers/media/platform/qcom/vcodec/iris/iris_helpers.c
index c31dfd5..872268d 100644
--- a/drivers/media/platform/qcom/vcodec/iris/iris_helpers.c
+++ b/drivers/media/platform/qcom/vcodec/iris/iris_helpers.c
@@ -4,6 +4,8 @@
*/

#include "iris_helpers.h"
+#include "iris_hfi.h"
+#include "iris_instance.h"

int check_core_lock(struct iris_core *core)
{
@@ -32,3 +34,63 @@ int iris_init_core_caps(struct iris_core *core)

return 0;
}
+
+static int process_inst_timeout(struct iris_inst *inst)
+{
+ struct iris_inst *instance;
+ struct iris_core *core;
+ bool found = false;
+ int ret = 0;
+
+ core = inst->core;
+
+ mutex_lock(&core->lock);
+ list_for_each_entry(instance, &core->instances, list) {
+ if (instance == inst) {
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ ret = -EINVAL;
+ goto unlock;
+ }
+
+ iris_change_core_state(core, IRIS_CORE_ERROR);
+
+ iris_core_deinit_locked(core);
+
+unlock:
+ mutex_unlock(&core->lock);
+
+ return ret;
+}
+
+int close_session(struct iris_inst *inst)
+{
+ u32 hw_response_timeout_val;
+ bool wait_for_response;
+ struct iris_core *core;
+ int ret;
+
+ core = inst->core;
+ hw_response_timeout_val = core->cap[HW_RESPONSE_TIMEOUT].value;
+ wait_for_response = true;
+ ret = iris_hfi_session_close(inst);
+ if (ret)
+ wait_for_response = false;
+
+ kfree(inst->packet);
+ inst->packet = NULL;
+
+ if (wait_for_response) {
+ ret = wait_for_completion_timeout(&inst->completions[SIGNAL_CMD_CLOSE],
+ msecs_to_jiffies(hw_response_timeout_val));
+ if (!ret) {
+ ret = -ETIMEDOUT;
+ process_inst_timeout(inst);
+ }
+ }
+
+ return ret;
+}
diff --git a/drivers/media/platform/qcom/vcodec/iris/iris_helpers.h b/drivers/media/platform/qcom/vcodec/iris/iris_helpers.h
index 4c7ddbf..b9a6485 100644
--- a/drivers/media/platform/qcom/vcodec/iris/iris_helpers.h
+++ b/drivers/media/platform/qcom/vcodec/iris/iris_helpers.h
@@ -7,8 +7,10 @@
#define _IRIS_HELPERS_H_

#include "iris_core.h"
+#include "iris_instance.h"

int check_core_lock(struct iris_core *core);
int iris_init_core_caps(struct iris_core *core);
+int close_session(struct iris_inst *inst);

#endif
diff --git a/drivers/media/platform/qcom/vcodec/iris/iris_hfi.c b/drivers/media/platform/qcom/vcodec/iris/iris_hfi.c
index b0390b542..d144ae5 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 "hfi_defines.h"
#include "vpu_common.h"

static int iris_hfi_queue_cmd_write(struct iris_core *core, void *pkt)
@@ -43,6 +44,27 @@ static int iris_hfi_queue_cmd_write(struct iris_core *core, void *pkt)
return ret;
}

+static bool validate_session(struct iris_core *core,
+ struct iris_inst *inst)
+{
+ struct iris_inst *temp;
+ bool valid = false;
+ int ret;
+
+ ret = check_core_lock(core);
+ if (ret)
+ return false;
+
+ list_for_each_entry(temp, &core->instances, list) {
+ if (temp == inst) {
+ valid = true;
+ break;
+ }
+ }
+
+ return valid;
+}
+
static int sys_init(struct iris_core *core)
{
int ret;
@@ -69,14 +91,6 @@ static int sys_image_version(struct iris_core *core)
return ret;
}

-#define CP_START 0
-#define CP_SIZE 0x25800000
-#define CP_NONPIXEL_START 0x01000000
-#define CP_NONPIXEL_SIZE 0x24800000
-
-#define FW_NAME "vpu30_4v.mbn"
-#define IRIS_PAS_ID 9
-
int iris_hfi_core_init(struct iris_core *core)
{
u32 cp_nonpixel_start, cp_nonpixel_size;
@@ -152,7 +166,90 @@ int iris_hfi_core_deinit(struct iris_core *core)
if (core->state == IRIS_CORE_DEINIT)
return 0;

- unload_fw(IRIS_PAS_ID);
+ unload_fw(core->platform_data->pas_id);
+
+ return ret;
+}
+
+int iris_hfi_session_open(struct iris_inst *inst)
+{
+ struct iris_core *core;
+ int ret;
+
+ inst->packet_size = 4096;
+ inst->packet = kzalloc(inst->packet_size, GFP_KERNEL);
+ if (!inst->packet)
+ return -ENOMEM;
+
+ core = inst->core;
+ mutex_lock(&core->lock);
+
+ if (!validate_session(core, inst)) {
+ ret = -EINVAL;
+ goto fail_free_packet;
+ }
+
+ ret = hfi_packet_session_command(inst,
+ HFI_CMD_OPEN,
+ HFI_HOST_FLAGS_RESPONSE_REQUIRED |
+ HFI_HOST_FLAGS_INTR_REQUIRED,
+ HFI_PORT_NONE,
+ 0,
+ HFI_PAYLOAD_U32,
+ &inst->session_id,
+ sizeof(u32));
+ if (ret)
+ goto fail_free_packet;
+
+ ret = iris_hfi_queue_cmd_write(core, inst->packet);
+ if (ret)
+ goto fail_free_packet;
+
+ mutex_unlock(&core->lock);
+
+ return ret;
+
+fail_free_packet:
+ kfree(inst->packet);
+ inst->packet = NULL;
+ mutex_unlock(&core->lock);
+
+ return ret;
+}
+
+int iris_hfi_session_close(struct iris_inst *inst)
+{
+ struct iris_core *core;
+ int ret;
+
+ if (!inst->packet)
+ return -EINVAL;
+
+ core = inst->core;
+ mutex_lock(&core->lock);
+
+ if (!validate_session(core, inst)) {
+ ret = -EINVAL;
+ goto unlock;
+ }
+
+ ret = hfi_packet_session_command(inst,
+ HFI_CMD_CLOSE,
+ (HFI_HOST_FLAGS_RESPONSE_REQUIRED |
+ HFI_HOST_FLAGS_INTR_REQUIRED |
+ HFI_HOST_FLAGS_NON_DISCARDABLE),
+ HFI_PORT_NONE,
+ inst->session_id,
+ HFI_PAYLOAD_NONE,
+ NULL,
+ 0);
+ if (ret)
+ goto unlock;
+
+ ret = iris_hfi_queue_cmd_write(inst->core, inst->packet);
+
+unlock:
+ mutex_unlock(&core->lock);

return ret;
}
diff --git a/drivers/media/platform/qcom/vcodec/iris/iris_hfi.h b/drivers/media/platform/qcom/vcodec/iris/iris_hfi.h
index fcf9f28..8a057cc 100644
--- a/drivers/media/platform/qcom/vcodec/iris/iris_hfi.h
+++ b/drivers/media/platform/qcom/vcodec/iris/iris_hfi.h
@@ -6,9 +6,12 @@
#ifndef _IRIS_HFI_H_
#define _IRIS_HFI_H_

+#include "iris_instance.h"
#include "iris_core.h"

int iris_hfi_core_init(struct iris_core *core);
int iris_hfi_core_deinit(struct iris_core *core);
+int iris_hfi_session_open(struct iris_inst *inst);
+int iris_hfi_session_close(struct iris_inst *inst);

#endif
diff --git a/drivers/media/platform/qcom/vcodec/iris/iris_hfi_packet.c b/drivers/media/platform/qcom/vcodec/iris/iris_hfi_packet.c
index f31dd84..1ed572e 100644
--- a/drivers/media/platform/qcom/vcodec/iris/iris_hfi_packet.c
+++ b/drivers/media/platform/qcom/vcodec/iris/iris_hfi_packet.c
@@ -211,3 +211,31 @@ int hfi_packet_image_version(struct iris_core *core,

return ret;
}
+
+int hfi_packet_session_command(struct iris_inst *inst, u32 pkt_type,
+ u32 flags, u32 port, u32 session_id,
+ u32 payload_type, void *payload,
+ u32 payload_size)
+{
+ struct iris_core *core;
+ int ret;
+
+ core = inst->core;
+
+ ret = hfi_create_header(inst->packet, inst->packet_size,
+ session_id, core->header_id++);
+ if (ret)
+ return ret;
+
+ ret = hfi_create_packet(inst->packet,
+ inst->packet_size,
+ pkt_type,
+ flags,
+ payload_type,
+ port,
+ core->packet_id++,
+ payload,
+ payload_size);
+
+ return ret;
+}
diff --git a/drivers/media/platform/qcom/vcodec/iris/iris_hfi_packet.h b/drivers/media/platform/qcom/vcodec/iris/iris_hfi_packet.h
index e36612c..9e476e9 100644
--- a/drivers/media/platform/qcom/vcodec/iris/iris_hfi_packet.h
+++ b/drivers/media/platform/qcom/vcodec/iris/iris_hfi_packet.h
@@ -6,6 +6,9 @@
#ifndef _IRIS_HFI_PACKET_H_
#define _IRIS_HFI_PACKET_H_

+#include "iris_core.h"
+#include "iris_instance.h"
+
struct hfi_header {
u32 size;
u32 session_id;
@@ -67,5 +70,9 @@ int hfi_packet_sys_init(struct iris_core *core,
u8 *pkt, u32 pkt_size);
int hfi_packet_image_version(struct iris_core *core,
u8 *pkt, u32 pkt_size);
+int hfi_packet_session_command(struct iris_inst *inst, u32 pkt_type,
+ u32 flags, u32 port, u32 session_id,
+ u32 payload_type, void *payload,
+ u32 payload_size);

#endif
diff --git a/drivers/media/platform/qcom/vcodec/iris/iris_instance.h b/drivers/media/platform/qcom/vcodec/iris/iris_instance.h
new file mode 100644
index 0000000..1bbb533
--- /dev/null
+++ b/drivers/media/platform/qcom/vcodec/iris/iris_instance.h
@@ -0,0 +1,48 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#ifndef _IRIS_INSTANCE_H_
+#define _IRIS_INSTANCE_H_
+
+#include <media/v4l2-ctrls.h>
+
+#include "iris_core.h"
+#include "iris_common.h"
+
+/**
+ * struct iris_inst - holds per video instance parameters
+ *
+ * @list: used for attach an instance to the core
+ * @core: pointer to core structure
+ * @session_id: id of current video session
+ * @vb2q_src: source vb2 queue
+ * @vb2q_dst: destination vb2 queue
+ * @ctx_q_lock: lock to serialize queues related ioctls
+ * @fh: reference of v4l2 file handler
+ * @fmt_src: structure of v4l2_format for source
+ * @fmt_dst: structure of v4l2_format for destination
+ * @ctrl_handler: reference of v4l2 ctrl handler
+ * @packet: HFI packet
+ * @packet_size: HFI packet size
+ * @completions: structure of signal completions
+ */
+
+struct iris_inst {
+ struct list_head list;
+ struct iris_core *core;
+ u32 session_id;
+ struct vb2_queue *vb2q_src;
+ struct vb2_queue *vb2q_dst;
+ struct mutex ctx_q_lock;/* lock to serialize queues related ioctls */
+ struct v4l2_fh fh;
+ struct v4l2_format *fmt_src;
+ struct v4l2_format *fmt_dst;
+ struct v4l2_ctrl_handler ctrl_handler;
+ void *packet;
+ u32 packet_size;
+ struct completion completions[MAX_SIGNAL];
+};
+
+#endif
diff --git a/drivers/media/platform/qcom/vcodec/iris/iris_probe.c b/drivers/media/platform/qcom/vcodec/iris/iris_probe.c
index 9d6a6c5..4f20da8 100644
--- a/drivers/media/platform/qcom/vcodec/iris/iris_probe.c
+++ b/drivers/media/platform/qcom/vcodec/iris/iris_probe.c
@@ -11,6 +11,7 @@
#include "iris_core.h"
#include "iris_helpers.h"
#include "resources.h"
+#include "iris_vidc.h"

static int iris_register_video_device(struct iris_core *core)
{
@@ -85,6 +86,8 @@ static int iris_probe(struct platform_device *pdev)
if (!core->packet)
return -ENOMEM;

+ INIT_LIST_HEAD(&core->instances);
+
core->reg_base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(core->reg_base))
return PTR_ERR(core->reg_base);
@@ -107,6 +110,13 @@ static int iris_probe(struct platform_device *pdev)
return ret;
}

+ ret = init_ops(core);
+ if (ret) {
+ dev_err_probe(core->dev, ret,
+ "%s: init ops 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/iris_vdec.c b/drivers/media/platform/qcom/vcodec/iris/iris_vdec.c
new file mode 100644
index 0000000..984be34
--- /dev/null
+++ b/drivers/media/platform/qcom/vcodec/iris/iris_vdec.c
@@ -0,0 +1,47 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#include "iris_common.h"
+#include "iris_vdec.h"
+
+void vdec_inst_init(struct iris_inst *inst)
+{
+ struct v4l2_format *f;
+
+ inst->fmt_src = kzalloc(sizeof(*inst->fmt_src), GFP_KERNEL);
+ inst->fmt_dst = kzalloc(sizeof(*inst->fmt_dst), GFP_KERNEL);
+ inst->vb2q_src = kzalloc(sizeof(*inst->vb2q_src), GFP_KERNEL);
+ inst->vb2q_dst = kzalloc(sizeof(*inst->vb2q_dst), GFP_KERNEL);
+
+ f = inst->fmt_src;
+ f->type = INPUT_MPLANE;
+ f->fmt.pix_mp.width = DEFAULT_WIDTH;
+ f->fmt.pix_mp.height = DEFAULT_HEIGHT;
+ f->fmt.pix_mp.pixelformat = V4L2_PIX_FMT_H264;
+ f->fmt.pix_mp.num_planes = 1;
+ f->fmt.pix_mp.plane_fmt[0].bytesperline = 0;
+ f->fmt.pix_mp.plane_fmt[0].sizeimage = DEFAULT_BUF_SIZE;
+ f->fmt.pix_mp.field = V4L2_FIELD_NONE;
+
+ f = inst->fmt_dst;
+ f->type = OUTPUT_MPLANE;
+ f->fmt.pix_mp.pixelformat = V4L2_PIX_FMT_QC08C;
+ f->fmt.pix_mp.width = ALIGN(DEFAULT_WIDTH, 128);
+ f->fmt.pix_mp.height = ALIGN(DEFAULT_HEIGHT, 32);
+ f->fmt.pix_mp.num_planes = 1;
+ f->fmt.pix_mp.plane_fmt[0].bytesperline = ALIGN(DEFAULT_WIDTH, 128);
+ f->fmt.pix_mp.plane_fmt[0].sizeimage = DEFAULT_BUF_SIZE;
+ f->fmt.pix_mp.field = V4L2_FIELD_NONE;
+ f->fmt.pix_mp.colorspace = V4L2_COLORSPACE_DEFAULT;
+ f->fmt.pix_mp.xfer_func = V4L2_XFER_FUNC_DEFAULT;
+ f->fmt.pix_mp.ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
+ f->fmt.pix_mp.quantization = V4L2_QUANTIZATION_DEFAULT;
+}
+
+void vdec_inst_deinit(struct iris_inst *inst)
+{
+ kfree(inst->fmt_dst);
+ kfree(inst->fmt_src);
+}
diff --git a/drivers/media/platform/qcom/vcodec/iris/iris_vdec.h b/drivers/media/platform/qcom/vcodec/iris/iris_vdec.h
new file mode 100644
index 0000000..dc8f43f
--- /dev/null
+++ b/drivers/media/platform/qcom/vcodec/iris/iris_vdec.h
@@ -0,0 +1,14 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#ifndef _IRIS_VDEC_H_
+#define _IRIS_VDEC_H_
+
+#include "iris_instance.h"
+
+void vdec_inst_init(struct iris_inst *inst);
+void vdec_inst_deinit(struct iris_inst *inst);
+
+#endif
diff --git a/drivers/media/platform/qcom/vcodec/iris/iris_vidc.c b/drivers/media/platform/qcom/vcodec/iris/iris_vidc.c
new file mode 100644
index 0000000..d02da8b
--- /dev/null
+++ b/drivers/media/platform/qcom/vcodec/iris/iris_vidc.c
@@ -0,0 +1,298 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#include "iris_common.h"
+#include "iris_helpers.h"
+#include "iris_hfi.h"
+#include "iris_instance.h"
+#include "iris_vdec.h"
+#include "iris_vidc.h"
+
+static int vidc_v4l2_fh_init(struct iris_inst *inst)
+{
+ struct iris_core *core;
+
+ core = inst->core;
+
+ if (inst->fh.vdev)
+ return -EINVAL;
+
+ v4l2_fh_init(&inst->fh, core->vdev_dec);
+ inst->fh.ctrl_handler = &inst->ctrl_handler;
+ v4l2_fh_add(&inst->fh);
+
+ return 0;
+}
+
+static int vidc_v4l2_fh_deinit(struct iris_inst *inst)
+{
+ if (!inst->fh.vdev)
+ return 0;
+
+ v4l2_fh_del(&inst->fh);
+ inst->fh.ctrl_handler = NULL;
+ v4l2_fh_exit(&inst->fh);
+
+ return 0;
+}
+
+static int vb2q_init(struct iris_inst *inst,
+ struct vb2_queue *q, enum v4l2_buf_type type)
+{
+ struct iris_core *core;
+
+ core = inst->core;
+
+ q->lock = &inst->ctx_q_lock;
+ q->type = type;
+ q->io_modes = VB2_MMAP | VB2_DMABUF;
+ q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
+ q->ops = core->vb2_ops;
+ q->mem_ops = core->vb2_mem_ops;
+ q->drv_priv = inst;
+ q->copy_timestamp = 1;
+ q->min_buffers_needed = 0;
+ return vb2_queue_init(q);
+}
+
+static int vidc_vb2_queue_init(struct iris_inst *inst)
+{
+ int ret;
+
+ ret = vb2q_init(inst, inst->vb2q_src, INPUT_MPLANE);
+ if (ret)
+ return ret;
+
+ ret = vb2q_init(inst, inst->vb2q_dst, OUTPUT_MPLANE);
+ if (ret)
+ goto fail_vb2q_src_deinit;
+
+ return ret;
+
+fail_vb2q_src_deinit:
+ vb2_queue_release(inst->vb2q_src);
+
+ return ret;
+}
+
+static int vidc_vb2_queue_deinit(struct iris_inst *inst)
+{
+ vb2_queue_release(inst->vb2q_src);
+ kfree(inst->vb2q_src);
+ inst->vb2q_src = NULL;
+
+ vb2_queue_release(inst->vb2q_dst);
+ kfree(inst->vb2q_dst);
+ inst->vb2q_dst = NULL;
+
+ return 0;
+}
+
+static int vidc_add_session(struct iris_inst *inst)
+{
+ struct iris_core *core;
+ struct iris_inst *i;
+ u32 count = 0;
+ int ret = 0;
+
+ core = inst->core;
+
+ mutex_lock(&core->lock);
+ if (core->state != IRIS_CORE_INIT) {
+ ret = -EINVAL;
+ goto unlock;
+ }
+ list_for_each_entry(i, &core->instances, list)
+ count++;
+
+ if (count < core->cap[MAX_SESSION_COUNT].value)
+ list_add_tail(&inst->list, &core->instances);
+ else
+ ret = -EAGAIN;
+unlock:
+ mutex_unlock(&core->lock);
+
+ return ret;
+}
+
+static int vidc_remove_session(struct iris_inst *inst)
+{
+ struct iris_inst *i, *temp;
+ struct iris_core *core;
+
+ core = inst->core;
+
+ mutex_lock(&core->lock);
+ list_for_each_entry_safe(i, temp, &core->instances, list) {
+ if (i->session_id == inst->session_id) {
+ list_del_init(&i->list);
+ break;
+ }
+ }
+ mutex_unlock(&core->lock);
+
+ return 0;
+}
+
+static struct iris_inst *get_vidc_inst(struct file *filp, void *fh)
+{
+ if (!filp || !filp->private_data)
+ return NULL;
+
+ return container_of(filp->private_data,
+ struct iris_inst, fh);
+}
+
+int vidc_open(struct file *filp)
+{
+ struct iris_core *core = video_drvdata(filp);
+ struct iris_inst *inst = NULL;
+ int i = 0;
+ int ret;
+
+ ret = iris_core_init(core);
+ if (ret)
+ return ret;
+
+ ret = iris_core_init_wait(core);
+ if (ret)
+ return ret;
+
+ inst = kzalloc(sizeof(*inst), GFP_KERNEL);
+ if (!inst)
+ return -ENOMEM;
+
+ inst->core = core;
+ inst->session_id = hash32_ptr(inst);
+ mutex_init(&inst->ctx_q_lock);
+ for (i = 0; i < MAX_SIGNAL; i++)
+ init_completion(&inst->completions[i]);
+
+ ret = vidc_add_session(inst);
+ if (ret)
+ goto fail_free_inst;
+
+ ret = vidc_v4l2_fh_init(inst);
+ if (ret)
+ goto fail_remove_session;
+
+ vdec_inst_init(inst);
+
+ ret = vidc_vb2_queue_init(inst);
+ if (ret)
+ goto fail_inst_deinit;
+
+ ret = iris_hfi_session_open(inst);
+ if (ret) {
+ dev_err(core->dev, "%s: session open failed\n", __func__);
+ goto fail_core_deinit;
+ }
+ filp->private_data = &inst->fh;
+
+ return 0;
+
+fail_core_deinit:
+ iris_core_deinit(core);
+ vidc_vb2_queue_deinit(inst);
+fail_inst_deinit:
+ vdec_inst_deinit(inst);
+ vidc_v4l2_fh_deinit(inst);
+fail_remove_session:
+ vidc_remove_session(inst);
+fail_free_inst:
+ mutex_destroy(&inst->ctx_q_lock);
+ kfree(inst);
+
+ return ret;
+}
+
+int vidc_close(struct file *filp)
+{
+ struct iris_inst *inst;
+
+ inst = get_vidc_inst(filp, NULL);
+ if (!inst)
+ return -EINVAL;
+
+ vdec_inst_deinit(inst);
+ close_session(inst);
+ vidc_vb2_queue_deinit(inst);
+ vidc_v4l2_fh_deinit(inst);
+ vidc_remove_session(inst);
+ mutex_destroy(&inst->ctx_q_lock);
+ kfree(inst);
+
+ filp->private_data = NULL;
+
+ return 0;
+}
+
+static __poll_t get_poll_flags(struct iris_inst *inst, u32 plane)
+{
+ struct vb2_buffer *vb = NULL;
+ struct vb2_queue *q = NULL;
+ unsigned long flags = 0;
+ __poll_t poll = 0;
+
+ if (plane == INPUT_MPLANE)
+ q = inst->vb2q_src;
+ else if (plane == OUTPUT_MPLANE)
+ q = inst->vb2q_dst;
+
+ if (!q)
+ return EPOLLERR;
+
+ spin_lock_irqsave(&q->done_lock, flags);
+ if (!list_empty(&q->done_list))
+ vb = list_first_entry(&q->done_list, struct vb2_buffer,
+ done_entry);
+ if (vb && (vb->state == VB2_BUF_STATE_DONE ||
+ vb->state == VB2_BUF_STATE_ERROR)) {
+ if (plane == OUTPUT_MPLANE)
+ poll |= EPOLLIN | EPOLLRDNORM;
+ else if (plane == INPUT_MPLANE)
+ poll |= EPOLLOUT | EPOLLWRNORM;
+ }
+ spin_unlock_irqrestore(&q->done_lock, flags);
+
+ return poll;
+}
+
+static __poll_t vidc_poll(struct file *filp, struct poll_table_struct *pt)
+{
+ struct iris_inst *inst;
+ __poll_t poll = 0;
+
+ inst = get_vidc_inst(filp, NULL);
+ if (!inst)
+ return EPOLLERR;
+
+ poll_wait(filp, &inst->fh.wait, pt);
+ poll_wait(filp, &inst->vb2q_src->done_wq, pt);
+ poll_wait(filp, &inst->vb2q_dst->done_wq, pt);
+
+ if (v4l2_event_pending(&inst->fh))
+ poll |= EPOLLPRI;
+
+ poll |= get_poll_flags(inst, INPUT_MPLANE);
+ poll |= get_poll_flags(inst, OUTPUT_MPLANE);
+
+ return poll;
+}
+
+static const struct v4l2_file_operations v4l2_file_ops = {
+ .owner = THIS_MODULE,
+ .open = vidc_open,
+ .release = vidc_close,
+ .unlocked_ioctl = video_ioctl2,
+ .poll = vidc_poll,
+};
+
+int init_ops(struct iris_core *core)
+{
+ core->v4l2_file_ops = &v4l2_file_ops;
+
+ return 0;
+}
diff --git a/drivers/media/platform/qcom/vcodec/iris/iris_vidc.h b/drivers/media/platform/qcom/vcodec/iris/iris_vidc.h
new file mode 100644
index 0000000..aebff44
--- /dev/null
+++ b/drivers/media/platform/qcom/vcodec/iris/iris_vidc.h
@@ -0,0 +1,15 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#ifndef _IRIS_VIDC_H_
+#define _IRIS_VIDC_H_
+
+#include "iris_core.h"
+
+int init_ops(struct iris_core *core);
+int vidc_open(struct file *filp);
+int vidc_close(struct file *filp);
+
+#endif
--
2.7.4