[PATCH v1 05/10] media: platform: mediatek: add isp_7x camsys unit

Shu-hsiang Yang posted 10 patches 1 month, 2 weeks ago
[PATCH v1 05/10] media: platform: mediatek: add isp_7x camsys unit
Posted by Shu-hsiang Yang 1 month, 2 weeks ago
Introduces the top media device driver for the MediaTek ISP7X CAMSYS.
The driver maintains the camera system, including sub-device management,
DMA operations, and integration with the V4L2 framework. It handles
request stream data, buffer management, and MediaTek-specific features,
and pipeline management, streaming control, error handling mechanism.
Additionally, it aggregates sub-drivers for the camera interface, raw
and yuv pipelines.

Signed-off-by: Shu-hsiang Yang <Shu-hsiang.Yang@mediatek.com>
---
 .../isp/isp_7x/camsys/mtk_cam-timesync.c      |  125 +
 .../isp/isp_7x/camsys/mtk_cam-timesync.h      |   12 +
 .../isp/isp_7x/camsys/mtk_cam-ufbc-def.h      |   59 +
 .../mediatek/isp/isp_7x/camsys/mtk_cam.c      | 4168 +++++++++++++++++
 .../mediatek/isp/isp_7x/camsys/mtk_cam.h      |  733 +++
 5 files changed, 5097 insertions(+)
 create mode 100644 drivers/media/platform/mediatek/isp/isp_7x/camsys/mtk_cam-timesync.c
 create mode 100644 drivers/media/platform/mediatek/isp/isp_7x/camsys/mtk_cam-timesync.h
 create mode 100644 drivers/media/platform/mediatek/isp/isp_7x/camsys/mtk_cam-ufbc-def.h
 create mode 100644 drivers/media/platform/mediatek/isp/isp_7x/camsys/mtk_cam.c
 create mode 100644 drivers/media/platform/mediatek/isp/isp_7x/camsys/mtk_cam.h

diff --git a/drivers/media/platform/mediatek/isp/isp_7x/camsys/mtk_cam-timesync.c b/drivers/media/platform/mediatek/isp/isp_7x/camsys/mtk_cam-timesync.c
new file mode 100644
index 000000000000..b333434d86c0
--- /dev/null
+++ b/drivers/media/platform/mediatek/isp/isp_7x/camsys/mtk_cam-timesync.c
@@ -0,0 +1,125 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2017 MediaTek Inc.
+ */
+
+#include <linux/module.h>
+#include <asm/arch_timer.h>
+#include <linux/init.h>
+#include <linux/timer.h>
+#include <linux/workqueue.h>
+#include <linux/spinlock.h>
+#include <linux/math64.h>
+
+#include "mtk_cam-timesync.h"
+
+#define FILTER_DATAPOINTS	16
+#define FILTER_FREQ		10000000ULL /* 10 ms */
+
+struct moving_average {
+	u64 last_time;
+	s64 input[FILTER_DATAPOINTS];
+	s64 output;
+	u8 cnt;
+	u8 tail;
+};
+
+static struct moving_average moving_average_algo_mono;
+static struct moving_average moving_average_algo_boot;
+static DEFINE_SPINLOCK(moving_average_lock);
+
+static u64 arch_counter_to_ns(u64 cyc)
+{
+	/* arch counter is 13M */
+	u64 num, max = ULLONG_MAX;
+	u32 mult = 161319385;
+	u32 shift = 21;
+	s64 nsec = 0;
+
+	do_div(max, mult);
+	if (cyc > max) {
+		num = div64_u64(cyc, max);
+		nsec = (((u64)max * mult) >> shift) * num;
+		cyc -= num * max;
+	}
+	nsec += ((u64)cyc * mult) >> shift;
+	return nsec;
+}
+
+static void moving_average_filter(struct moving_average *filter,
+				  u64 base_time, u64 archcounter_time)
+{
+	int i = 0;
+	s64 avg = 0;
+	s64 ret_avg = 0;
+
+	if (base_time < filter->last_time + FILTER_FREQ)
+		return;
+
+	filter->last_time = base_time;
+
+	filter->input[filter->tail++] = base_time - archcounter_time;
+	filter->tail &= (FILTER_DATAPOINTS - 1);
+	if (filter->cnt < FILTER_DATAPOINTS)
+		filter->cnt++;
+
+	for (i = 1, avg = 0; i < filter->cnt; i++)
+		avg += (filter->input[i] - filter->input[0]);
+	ret_avg = div_s64(avg, filter->cnt) + filter->input[0];
+	WRITE_ONCE(filter->output, ret_avg);
+}
+
+static u64 get_filter_output(struct moving_average *filter)
+{
+	return READ_ONCE(filter->output);
+}
+
+u64 mtk_cam_timesync_to_monotonic(u64 hwclock)
+{
+	unsigned long flags = 0;
+	u64 base_time = 0;
+	u64 archcounter_time = 0;
+	u64 reslut_time = 0;
+
+	spin_lock(&moving_average_lock);
+
+	local_irq_save(flags);
+	base_time = ktime_to_ns(ktime_get());
+	archcounter_time =
+		arch_counter_to_ns(__arch_counter_get_cntvct_stable());
+	local_irq_restore(flags);
+
+	moving_average_filter(&moving_average_algo_mono,
+			      base_time, archcounter_time);
+
+	reslut_time = arch_counter_to_ns(hwclock) +
+		get_filter_output(&moving_average_algo_mono);
+
+	spin_unlock(&moving_average_lock);
+	return reslut_time;
+}
+
+u64 mtk_cam_timesync_to_boot(u64 hwclock)
+{
+	unsigned long flags = 0;
+	u64 base_time = 0;
+	u64 archcounter_time = 0;
+	u64 reslut_time = 0;
+
+	spin_lock(&moving_average_lock);
+
+	local_irq_save(flags);
+	base_time = ktime_get_boottime_ns();
+	archcounter_time =
+		arch_counter_to_ns(__arch_counter_get_cntvct_stable());
+	local_irq_restore(flags);
+
+	moving_average_filter(&moving_average_algo_boot,
+			      base_time, archcounter_time);
+
+	reslut_time = arch_counter_to_ns(hwclock) +
+		get_filter_output(&moving_average_algo_boot);
+
+	spin_unlock(&moving_average_lock);
+	return reslut_time;
+}
diff --git a/drivers/media/platform/mediatek/isp/isp_7x/camsys/mtk_cam-timesync.h b/drivers/media/platform/mediatek/isp/isp_7x/camsys/mtk_cam-timesync.h
new file mode 100644
index 000000000000..7a226bd1c0a9
--- /dev/null
+++ b/drivers/media/platform/mediatek/isp/isp_7x/camsys/mtk_cam-timesync.h
@@ -0,0 +1,12 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2017 MediaTek Inc.
+ */
+
+#ifndef __ARCHCOUNTER_TIMESYNC__
+#define __ARCHCOUNTER_TIMESYNC__
+
+u64 mtk_cam_timesync_to_monotonic(u64 hwclock);
+u64 mtk_cam_timesync_to_boot(u64 hwclock);
+
+#endif
diff --git a/drivers/media/platform/mediatek/isp/isp_7x/camsys/mtk_cam-ufbc-def.h b/drivers/media/platform/mediatek/isp/isp_7x/camsys/mtk_cam-ufbc-def.h
new file mode 100644
index 000000000000..12fc415380fe
--- /dev/null
+++ b/drivers/media/platform/mediatek/isp/isp_7x/camsys/mtk_cam-ufbc-def.h
@@ -0,0 +1,59 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2022 MediaTek Inc.
+ * Author: Hung-Wen Hsieh <hung-wen.hsieh@mediatek.com>
+ */
+
+#ifndef __UFBC_DEF_H__
+#define __UFBC_DEF_H__
+
+/**
+ * MediaTek Universal Frame Buffer Compression (UFBC) buffer header
+ * Caller must follow the bit stream buffer size and length table buffer size.
+ *
+ * Header Size
+ * Fixed size of 4096 bytes. Caller SHOULD NOT edit it.
+ *
+ * Bit Stream Size
+ * Bit stream width must be aligned to 64 pixel.
+ */
+
+struct ufbc_buf_header {
+	/** Describe image resolution, unit in pixel. */
+	u32 width;
+	/** Describe image resolution, unit in pixel. */
+	u32 height;
+	/** Describe UFBC data plane count, UFBC supports maximum 2 planes. */
+	u32 plane_count;
+	/** Describe the original image data bits per pixel of the given plane. */
+	u32 bits_per_pixel[3];
+
+	/**
+	 * Describe the offset of the given plane bit stream data in bytes,
+	 * including header size.
+	 */
+	u32 bitstream_offset[3];
+	/** Describe the bit stream data size in bytes of the given plane. */
+	u32 bitstream_size[3];
+	/** Describe the encoded data size in bytes of the given plane. */
+	u32 bitstream_data_size[3];
+
+	/**
+	 * Describe the offset of length table of the given plane, including
+	 * header size.
+	 */
+	u32 table_offset[3];
+	/** Describe the length table size of the given plane */
+	u32 table_size[3];
+	/** Describe the total buffer size, including buffer header. */
+	u32 buffer_size;
+};
+
+struct ufbc_buffer_header {
+	union {
+		struct ufbc_buf_header header;
+		u8 bits[4096];
+	};
+};
+
+#endif /* __UFBC_DEF_H__ */
diff --git a/drivers/media/platform/mediatek/isp/isp_7x/camsys/mtk_cam.c b/drivers/media/platform/mediatek/isp/isp_7x/camsys/mtk_cam.c
new file mode 100644
index 000000000000..5773dc7352da
--- /dev/null
+++ b/drivers/media/platform/mediatek/isp/isp_7x/camsys/mtk_cam.c
@@ -0,0 +1,4168 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Copyright (c) 2022 MediaTek Inc.
+
+#include <linux/iopoll.h>
+#include <linux/module.h>
+#include <linux/of_platform.h>
+#include <linux/of_reserved_mem.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/remoteproc.h>
+#include <linux/remoteproc/mtk_scp.h>
+#include <linux/types.h>
+#include <linux/vmalloc.h>
+#include <linux/videodev2.h>
+#include <media/videobuf2-v4l2.h>
+#include <media/v4l2-fwnode.h>
+#include <media/v4l2-mc.h>
+#include <media/v4l2-subdev.h>
+
+#include "mtk_cam.h"
+#include "mtk_cam-ctrl.h"
+#include "mtk_cam-feature.h"
+#include "mtk_cam-pool.h"
+#include "mtk_cam-regs-mt8188.h"
+#include "mtk_camera-v4l2-controls.h"
+#include "mtk_cam-ufbc-def.h"
+#include "mtk_cam-timesync.h"
+
+static unsigned int debug_ae;
+module_param(debug_ae, uint, 0644);
+MODULE_PARM_DESC(debug_ae, "activates debug ae info");
+
+#define MTK_CAM_CIO_PAD_SRC		PAD_SRC_RAW0
+#define MTK_CAM_CIO_PAD_SINK		MTK_RAW_SINK
+#define MTK_CAM_IPI_SEND_TIMEOUT	1000
+/* consider running_job_list depth 3*3 */
+#define RUNNING_JOB_DEPTH		9
+
+static const struct of_device_id mtk_cam_of_ids[] = {
+	{.compatible = "mediatek,camisp",},
+	{}
+};
+MODULE_DEVICE_TABLE(of, mtk_cam_of_ids);
+
+/*
+ * All member of mtk_cam_request_stream_data which may be used
+ * after media_request_ioctl_reinit and before the next
+ * media_request_ioctl_queue must be clean here. For
+ * example, the pending set fmt, set selection, and sensor
+ * switch extension of camsys driver.
+ */
+static void
+mtk_cam_req_s_data_clean(struct mtk_cam_request_stream_data *s_data)
+{
+	s_data->sensor = NULL;
+	s_data->flags = 0;
+}
+
+static void
+mtk_cam_req_pipe_s_data_clean(struct mtk_cam_request *req, int pipe_id,
+			      int index)
+{
+	struct mtk_cam_request_stream_data *req_stream_data;
+
+	req_stream_data = mtk_cam_req_get_s_data(req, pipe_id, index);
+	if (req_stream_data) {
+		mtk_cam_req_s_data_clean(req_stream_data);
+		/**
+		 * Notice that the we clean req_stream_data->bufs here so
+		 * we should not use it after this function called on it.
+		 * mtk_cam_vb2_return_all_buffers() uses another list
+		 * of mtk_cam_video_device to keep the vb2 buffers to be clean.
+		 */
+		memset(req_stream_data->bufs, 0, sizeof(req_stream_data->bufs));
+	}
+}
+
+void mtk_cam_s_data_update_timestamp(struct mtk_cam_buffer *buf,
+				     struct mtk_cam_request_stream_data *s_data)
+{
+	struct mtk_cam_ctx *ctx;
+	struct vb2_buffer *vb;
+	struct mtk_cam_video_device *node;
+
+	ctx = mtk_cam_s_data_get_ctx(s_data);
+	if (!ctx) {
+		pr_info("%s: get ctx from s_data failed", __func__);
+		return;
+	}
+
+	vb = &buf->vbb.vb2_buf;
+	node = mtk_cam_vbq_to_vdev(vb->vb2_queue);
+
+	buf->vbb.sequence = s_data->frame_seq_no;
+	if (vb->vb2_queue->timestamp_flags & V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC)
+		vb->timestamp = s_data->timestamp_mono;
+	else
+		vb->timestamp = s_data->timestamp;
+
+	/* check buffer's timestamp */
+	if (node->desc.dma_port == MTKCAM_IPI_RAW_META_STATS_CFG)
+		dev_dbg(ctx->cam->dev,
+			"%s:%s:vb sequence:%d, queue type:%d, timestamp_flags:0x%x, timestamp:%lld\n",
+			__func__, node->desc.name, buf->vbb.sequence,
+			vb->vb2_queue->type, vb->vb2_queue->timestamp_flags,
+			vb->timestamp);
+}
+
+static void mtk_cam_req_return_pipe_buffers(struct mtk_cam_request *req,
+					    int pipe_id, int index)
+{
+	struct mtk_cam_device *cam =
+		container_of(req->req.mdev, struct mtk_cam_device, media_dev);
+	struct mtk_cam_request_stream_data *s_data_pipe;
+	struct mtk_cam_buffer *buf_ret[MTK_RAW_TOTAL_NODES];
+	struct mtk_cam_buffer *buf;
+	struct mtk_cam_video_device *node;
+	struct vb2_buffer *vb;
+	int buf_state;
+	u32 i, buf_ret_cnt = 0, buf_start = 0, buf_end = 0;
+
+	s_data_pipe = mtk_cam_req_get_s_data(req, pipe_id, index);
+	if (!s_data_pipe) {
+		pr_info("%s: get s_data pipe failed", __func__);
+		return;
+	}
+
+	if (is_raw_subdev(pipe_id)) {
+		buf_start = MTK_RAW_SINK_NUM;
+		buf_end = MTK_RAW_PIPELINE_PADS_NUM;
+	}
+
+	for (i = buf_start; i < buf_end; i++) {
+		/* make sure do not touch req/s_data after vb2_buffe_done */
+		buf = mtk_cam_s_data_get_vbuf(s_data_pipe, i);
+		if (!buf)
+			continue;
+		buf_ret[buf_ret_cnt++] = buf;
+		/* clean the stream data for req reinit case */
+		mtk_cam_s_data_reset_vbuf(s_data_pipe, i);
+	}
+
+	/* clean the req_stream_data being used right after request reinit */
+	mtk_cam_req_pipe_s_data_clean(req, pipe_id, index);
+
+	buf_state = atomic_read(&s_data_pipe->buf_state);
+	if (buf_state == -1)
+		buf_state = VB2_BUF_STATE_ERROR;
+
+	dev_dbg(cam->dev,
+		"%s:%s: pipe_id(%d) buf_state(%d) buf_ret_cnt(%d)\n", __func__,
+		req->req.debug_str, pipe_id, buf_state, buf_ret_cnt);
+
+	for (i = 0; i < buf_ret_cnt; i++) {
+		buf = buf_ret[i];
+		vb = &buf->vbb.vb2_buf;
+		node = mtk_cam_vbq_to_vdev(vb->vb2_queue);
+		if (node->uid.pipe_id != pipe_id) {
+			dev_info(cam->dev,
+				 "%s:%s:node(%s): invalid pipe id (%d), should be (%d)\n",
+				 __func__, req->req.debug_str,
+				 node->desc.name, node->uid.pipe_id, pipe_id);
+			continue;
+		}
+
+		if (atomic_read(&req->state) > MTK_CAM_REQ_STATE_PENDING)
+			mtk_cam_s_data_update_timestamp(buf, s_data_pipe);
+
+		vb2_buffer_done(&buf->vbb.vb2_buf, buf_state);
+	}
+}
+
+struct mtk_cam_request_stream_data *
+mtk_cam_get_req_s_data(struct mtk_cam_ctx *ctx, unsigned int pipe_id,
+		       unsigned int frame_seq_no)
+
+{
+	struct mtk_cam_device *cam = ctx->cam;
+	struct mtk_cam_request *req, *req_prev;
+	struct mtk_cam_request_stream_data *req_stream_data;
+	int i;
+
+	spin_lock(&cam->running_job_lock);
+	list_for_each_entry_safe(req, req_prev, &cam->running_job_list, list) {
+		if (req->pipe_used & (1 << pipe_id)) {
+			for (i = 0; i < req->p_data[pipe_id].s_data_num; i++) {
+				req_stream_data = &req->p_data[pipe_id].s_data[i];
+				if (req_stream_data->frame_seq_no == frame_seq_no) {
+					spin_unlock(&cam->running_job_lock);
+					return req_stream_data;
+				}
+			}
+		}
+	}
+	spin_unlock(&cam->running_job_lock);
+
+	return NULL;
+}
+
+struct mtk_cam_request *mtk_cam_get_req(struct mtk_cam_ctx *ctx,
+					unsigned int frame_seq_no)
+{
+	struct mtk_cam_request_stream_data *req_stream_data;
+
+	req_stream_data = mtk_cam_get_req_s_data(ctx, ctx->stream_id, frame_seq_no);
+	if (!req_stream_data)
+		return NULL;
+
+	return req_stream_data->req;
+}
+
+bool watchdog_scenario(struct mtk_cam_ctx *ctx)
+{
+	if (ctx->sensor && !mtk_cam_is_m2m(ctx) && ctx->used_raw_num)
+		return true;
+	else
+		return false;
+}
+
+static bool finish_cq_buf(struct mtk_cam_request_stream_data *req_stream_data)
+{
+	struct mtk_cam_ctx *ctx = req_stream_data->ctx;
+	struct mtk_cam_working_buf_entry *cq_buf_entry;
+
+	if (!ctx->used_raw_num)
+		return false;
+
+	spin_lock(&ctx->processing_buffer_list.lock);
+
+	cq_buf_entry = req_stream_data->working_buf;
+	/* Check if the cq buffer is already finished */
+	if (!cq_buf_entry || !cq_buf_entry->s_data) {
+		dev_info(ctx->cam->dev,
+			 "%s:%s:ctx(%d):req(%d):working_buf is already release\n",
+			 __func__, req_stream_data->req->req.debug_str,
+			 ctx->stream_id, req_stream_data->frame_seq_no);
+		spin_unlock(&ctx->processing_buffer_list.lock);
+		return false;
+	}
+
+	list_del(&cq_buf_entry->list_entry);
+	mtk_cam_s_data_reset_wbuf(req_stream_data);
+	ctx->processing_buffer_list.cnt--;
+	spin_unlock(&ctx->processing_buffer_list.lock);
+
+	mtk_cam_working_buf_put(cq_buf_entry);
+
+	dev_dbg(ctx->cam->dev, "put cq buf:%pad, %s\n",
+		&cq_buf_entry->buffer.iova,
+		req_stream_data->req->req.debug_str);
+
+	return true;
+}
+
+static void update_hw_mapping(struct mtk_cam_ctx *ctx,
+			      struct mtkcam_ipi_config_param *config_param)
+{
+	/* raw config */
+	config_param->maps[0].pipe_id = ctx->pipe->id;
+	config_param->maps[0].dev_mask = MTKCAM_SUBDEV_RAW_MASK & ctx->used_raw_dev;
+	config_param->n_maps = 1;
+}
+
+static void mtk_cam_del_req_from_running(struct mtk_cam_ctx *ctx,
+					 struct mtk_cam_request *req, int pipe_id)
+{
+	struct mtk_cam_request_stream_data *s_data;
+
+	s_data = mtk_cam_req_get_s_data(req, ctx->stream_id, 0);
+	dev_dbg(ctx->cam->dev,
+		"%s: %s: removed, req:%d, ctx:(%d/0x%x/0x%x), pipe:(%d/0x%x/0x%x) done_status:0x%x)\n",
+		__func__, req->req.debug_str, s_data->frame_seq_no,
+		ctx->stream_id, req->ctx_used, ctx->cam->streaming_ctx,
+		pipe_id, req->pipe_used, ctx->cam->streaming_pipe,
+		req->done_status);
+
+	atomic_set(&req->state, MTK_CAM_REQ_STATE_COMPLETE);
+	spin_lock(&ctx->cam->running_job_lock);
+	list_del(&req->list);
+	ctx->cam->running_job_count--;
+	spin_unlock(&ctx->cam->running_job_lock);
+}
+
+static void mtk_cam_req_works_clean(struct mtk_cam_request_stream_data *s_data)
+{
+	struct mtk_cam_ctx *ctx = mtk_cam_s_data_get_ctx(s_data);
+	char *dbg_str = mtk_cam_s_data_get_dbg_str(s_data);
+
+	/* flush the sensor work */
+	if (atomic_read(&s_data->sensor_work.is_queued)) {
+		kthread_flush_work(&s_data->sensor_work.work);
+		dev_dbg(ctx->cam->dev,
+			"%s:ctx(%d):%s:seq(%d): flushed sensor_work\n",
+			__func__, ctx->stream_id, dbg_str, s_data->frame_seq_no);
+	}
+}
+
+static void mtk_cam_get_timestamp(struct mtk_cam_ctx *ctx,
+				  struct mtk_cam_request_stream_data *s_data)
+{
+	struct mtk_cam_buffer *buf;
+	struct vb2_buffer *vb;
+	void *vaddr;
+	u64 *timestamp_addr;
+	u64 *fho_va;
+
+	buf = mtk_cam_s_data_get_vbuf(s_data, MTK_RAW_META_OUT_0);
+	if (!buf) {
+		dev_info(ctx->cam->dev,
+			 "ctx(%d): can't get MTK_RAW_META_OUT_0 buf from req(%d)\n",
+			 ctx->stream_id, s_data->frame_seq_no);
+		return;
+	}
+
+	vb = &buf->vbb.vb2_buf;
+	if (!vb) {
+		dev_info(ctx->cam->dev,
+			 "%s:ctx(%d): can't get vb2 buf\n",
+			 __func__, ctx->stream_id);
+		return;
+	}
+
+	vaddr = vb2_plane_vaddr(&buf->vbb.vb2_buf, 0);
+	if (!vaddr) {
+		dev_info(ctx->cam->dev,
+			 "%s:ctx(%d): can't get plane_vadd\n",
+			 __func__, ctx->stream_id);
+		return;
+	}
+
+	if (!s_data->working_buf->buffer.va ||
+	    s_data->working_buf->buffer.size == 0) {
+		dev_info(ctx->cam->dev,
+			 "%s:ctx(%d): can't get working_buf\n",
+			 __func__, ctx->stream_id);
+		return;
+	}
+
+	fho_va = (u64 *)(s_data->working_buf->buffer.va +
+		s_data->working_buf->buffer.size - 64);
+
+	timestamp_addr = camsys_get_timestamp_addr(vaddr);
+	*timestamp_addr = div_u64(mtk_cam_timesync_to_monotonic(*fho_va), 1000);
+	*(timestamp_addr + 1) =	div_u64(mtk_cam_timesync_to_boot(*fho_va), 1000);
+	dev_dbg(ctx->cam->dev,
+		"timestamp TS:momo %llu us boot %llu us, TS_cnt:%llu\n",
+		*timestamp_addr, *(timestamp_addr + 1),	*fho_va);
+}
+
+int mtk_cam_dequeue_req_frame(struct mtk_cam_ctx *ctx,
+			      unsigned int dequeued_frame_seq_no,
+			      int pipe_id)
+{
+	struct mtk_cam_request *req, *req_prev;
+	struct mtk_cam_request_stream_data *s_data, *s_data_pipe;
+	struct mtk_cam_request_stream_data *deq_s_data[RUNNING_JOB_DEPTH];
+	struct mtk_raw_pipeline *pipe = ctx->pipe;
+	struct mtk_camsys_sensor_ctrl *sensor_ctrl = &ctx->sensor_ctrl;
+	struct mtk_ae_debug_data ae_data;
+	int buf_state;
+	u32 dequeue_cnt, s_data_cnt, handled_cnt;
+	bool del_job, del_req;
+	bool unreliable = false;
+	unsigned int done_status_latch;
+
+	memset(&ae_data, 0, sizeof(struct mtk_ae_debug_data));
+	dequeue_cnt = 0;
+	s_data_cnt = 0;
+	spin_lock(&ctx->cam->running_job_lock);
+	list_for_each_entry_safe(req, req_prev, &ctx->cam->running_job_list, list) {
+		if (!(req->pipe_used & (1 << pipe_id)))
+			continue;
+
+		s_data = mtk_cam_req_get_s_data(req, ctx->stream_id, 0);
+		if (!s_data) {
+			dev_info(ctx->cam->dev,
+				 "frame_seq:%d[ctx=%d,pipe=%d], de-queue request not found\n",
+				 dequeued_frame_seq_no, ctx->stream_id, pipe_id);
+			continue;
+		}
+
+		if (s_data->frame_seq_no > dequeued_frame_seq_no)
+			goto STOP_SCAN;
+
+		deq_s_data[s_data_cnt++] = s_data;
+		if (s_data_cnt >= RUNNING_JOB_DEPTH) {
+			dev_info(ctx->cam->dev,
+				 "%s:%s:ctx(%d):pipe(%d):seq(%d/%d) dequeue s_data over local buffer cnt(%d)\n",
+				 __func__, req->req.debug_str, ctx->stream_id, pipe_id,
+				 s_data->frame_seq_no, dequeued_frame_seq_no,
+				 s_data_cnt);
+			goto STOP_SCAN;
+		}
+	}
+
+STOP_SCAN:
+	spin_unlock(&ctx->cam->running_job_lock);
+
+	for (handled_cnt = 0; handled_cnt < s_data_cnt; handled_cnt++) {
+		s_data = deq_s_data[handled_cnt];
+		del_req = false;
+		del_job = false;
+		req = mtk_cam_s_data_get_req(s_data);
+		if (!req) {
+			dev_info(ctx->cam->dev,
+				 "%s:ctx(%d):pipe(%d):seq(%d) req not found\n",
+				 __func__, ctx->stream_id, pipe_id,
+				 s_data->frame_seq_no);
+			continue;
+		}
+
+		spin_lock(&req->done_status_lock);
+
+		if (req->done_status & 1 << pipe_id) {
+			/* already handled by another job done work */
+			spin_unlock(&req->done_status_lock);
+			continue;
+		}
+
+		/* Check whether all pipelines of single ctx are done */
+		req->done_status |= 1 << pipe_id;
+		if ((req->done_status & ctx->streaming_pipe) ==
+		    (req->pipe_used & ctx->streaming_pipe))
+			del_job = true;
+
+		if ((req->done_status & ctx->cam->streaming_pipe) ==
+		    (req->pipe_used & ctx->cam->streaming_pipe)) {
+			if (MTK_CAM_REQ_STATE_RUNNING ==
+			    atomic_cmpxchg(&req->state,
+					   MTK_CAM_REQ_STATE_RUNNING,
+					   MTK_CAM_REQ_STATE_DELETING))
+				del_req = true;
+		}
+		done_status_latch = req->done_status;
+		spin_unlock(&req->done_status_lock);
+
+		if (is_raw_subdev(pipe_id) && debug_ae) {
+			mtk_cam_raw_dump_aa_info(ctx, &ae_data);
+			dev_dbg(ctx->cam->dev,
+				"%s:%s:ctx(%d):pipe(%d):de-queue seq(%d):handle seq(%d),done(0x%x),pipes(req:0x%x,ctx:0x%x,all:0x%x),del_job(%d),del_req(%d),metaout,size(%u,%u),AA(0x%llx,0x%llx,0x%llx,0x%llx)(0x%llx,0x%llx,0x%llx,0x%llx)(0x%llx,0x%llx,0x%llx,0x%llx)(0x%llx,0x%llx,0x%llx,0x%llx)(0x%llx,0x%llx,0x%llx,0x%llx)\n",
+				__func__, req->req.debug_str, ctx->stream_id, pipe_id,
+				dequeued_frame_seq_no, s_data->frame_seq_no,
+				done_status_latch, req->pipe_used,
+				ctx->streaming_pipe, ctx->cam->streaming_pipe,
+				del_job, del_req,
+				pipe->res_config.sink_fmt.width,
+				pipe->res_config.sink_fmt.height,
+				ae_data.obc_r1_sum[0], ae_data.obc_r1_sum[1],
+				ae_data.obc_r1_sum[2], ae_data.obc_r1_sum[3],
+				ae_data.obc_r2_sum[0], ae_data.obc_r2_sum[1],
+				ae_data.obc_r2_sum[2], ae_data.obc_r2_sum[3],
+				ae_data.obc_r3_sum[0], ae_data.obc_r3_sum[1],
+				ae_data.obc_r3_sum[2], ae_data.obc_r3_sum[3],
+				ae_data.aa_sum[0], ae_data.aa_sum[1],
+				ae_data.aa_sum[2], ae_data.aa_sum[3],
+				ae_data.ltm_sum[0], ae_data.ltm_sum[1],
+				ae_data.ltm_sum[2], ae_data.ltm_sum[3]);
+		} else {
+			dev_dbg(ctx->cam->dev,
+				"%s:%s:ctx(%d):pipe(%d):de-queue seq(%d):handle seq(%d),done(0x%x),pipes(req:0x%x,ctx:0x%x,all:0x%x),del_job(%d),del_req(%d)\n",
+				__func__, req->req.debug_str, ctx->stream_id, pipe_id,
+				dequeued_frame_seq_no, s_data->frame_seq_no,
+				done_status_latch, req->pipe_used,
+				ctx->streaming_pipe, ctx->cam->streaming_pipe,
+				del_job, del_req);
+		}
+
+		if (is_raw_subdev(pipe_id)) {
+			mtk_cam_get_timestamp(ctx, s_data);
+			mtk_cam_req_dbg_works_clean(s_data);
+			mtk_cam_req_works_clean(s_data);
+		}
+
+		if (del_job) {
+			atomic_dec(&ctx->running_s_data_cnt);
+			mtk_camsys_state_delete(ctx, sensor_ctrl, req);
+
+			/* release internal buffers */
+			finish_cq_buf(s_data);
+		}
+
+		if (del_req) {
+			mtk_cam_del_req_from_running(ctx, req, pipe_id);
+			dequeue_cnt++;
+		}
+
+		/* release vb2 buffers of the pipe */
+		s_data_pipe = mtk_cam_req_get_s_data(req, pipe_id, 0);
+		if (!s_data_pipe) {
+			dev_info(ctx->cam->dev,
+				 "%s:%s:ctx(%d):pipe(%d):seq(%d) s_data_pipe not found\n",
+				 __func__, req->req.debug_str, ctx->stream_id, pipe_id,
+				 s_data->frame_seq_no);
+			continue;
+		}
+
+		if (s_data->frame_seq_no < dequeued_frame_seq_no) {
+			buf_state = VB2_BUF_STATE_ERROR;
+			dev_dbg(ctx->cam->dev,
+				"%s:%s:pipe(%d) seq:%d, time:%lld drop, ctx:%d\n",
+				__func__, req->req.debug_str, pipe_id,
+				s_data->frame_seq_no, s_data->timestamp,
+				ctx->stream_id);
+		} else if (s_data->state.estate == E_STATE_DONE_MISMATCH) {
+			buf_state = VB2_BUF_STATE_ERROR;
+			dev_dbg(ctx->cam->dev,
+				"%s:%s:pipe(%d) seq:%d, state done mismatch",
+				__func__, req->req.debug_str, pipe_id,
+				s_data->frame_seq_no);
+		} else if (unreliable) {
+			buf_state = VB2_BUF_STATE_ERROR;
+			dev_dbg(ctx->cam->dev,
+				"%s:%s:pipe(%d) seq:%d, done (unreliable)",
+				__func__, req->req.debug_str, pipe_id,
+				s_data->frame_seq_no);
+		} else {
+			buf_state = VB2_BUF_STATE_DONE;
+			dev_dbg(ctx->cam->dev,
+				"%s:%s:pipe(%d) seq:%d, done success",
+				__func__, req->req.debug_str, pipe_id,
+				s_data->frame_seq_no);
+		}
+
+		if (mtk_cam_s_data_set_buf_state(s_data_pipe, buf_state)) {
+			/* handle vb2_buffer_done */
+			if (mtk_cam_req_put(req, pipe_id))
+				dev_dbg(ctx->cam->dev,
+					"%s:%s:pipe(%d) return request",
+					__func__, req->req.debug_str, pipe_id);
+		}
+	}
+
+	return dequeue_cnt;
+}
+
+void mtk_cam_dev_req_clean_pending(struct mtk_cam_device *cam, int pipe_id,
+				   int buf_state)
+{
+	struct mtk_cam_request *req, *req_prev;
+	struct mtk_cam_request_stream_data *s_data_pipe;
+	struct list_head *pending = &cam->pending_job_list;
+	struct list_head req_clean_list;
+
+	/* Consider pipe bufs and pipe_used only */
+
+	INIT_LIST_HEAD(&req_clean_list);
+
+	spin_lock(&cam->pending_job_lock);
+	list_for_each_entry_safe(req, req_prev, pending, list) {
+		/* update pipe_used */
+		req->pipe_used &= ~(1 << pipe_id);
+		list_add_tail(&req->cleanup_list, &req_clean_list);
+		if (!(req->pipe_used & cam->streaming_pipe)) {
+			/* the last pipe */
+			list_del(&req->list);
+			dev_info(cam->dev,
+				 "%s:%s:pipe(%d) remove req from pending list\n",
+				 __func__, req->req.debug_str, pipe_id);
+		}
+	}
+	spin_unlock(&cam->pending_job_lock);
+
+	list_for_each_entry_safe(req, req_prev, &req_clean_list, cleanup_list) {
+		list_del(&req->cleanup_list);
+		s_data_pipe = mtk_cam_req_get_s_data(req, pipe_id, 0);
+		if (!s_data_pipe) {
+			dev_dbg(cam->dev,
+				"%s:%s:pipe_used(0x%x):pipe(%d) s_data_pipe not found\n",
+				__func__, req->req.debug_str, req->pipe_used,
+				pipe_id);
+			continue;
+		}
+		if (mtk_cam_s_data_set_buf_state(s_data_pipe, buf_state)) {
+			/* handle vb2_buffer_done */
+			if (mtk_cam_req_put(req, pipe_id))
+				dev_dbg(cam->dev,
+					"%s:%s:pipe_used(0x%x):pipe(%d) return request",
+					__func__, req->req.debug_str,
+					req->pipe_used, pipe_id);
+			/* DO NOT touch req after here */
+		}
+	}
+}
+
+void mtk_cam_dev_req_cleanup(struct mtk_cam_ctx *ctx, int pipe_id, int buf_state)
+{
+	struct mtk_cam_device *cam = ctx->cam;
+	struct mtk_cam_request *req, *req_prev;
+	struct mtk_cam_request_stream_data *s_data, *s_data_pipe;
+	struct mtk_cam_request_stream_data *clean_s_data[RUNNING_JOB_DEPTH];
+	struct list_head *running = &cam->running_job_list;
+	unsigned int other_pipes, done_status;
+	int i;
+	u32 num_s_data, s_data_cnt, handled_cnt;
+	bool need_clean_req;
+
+	mtk_cam_dev_req_clean_pending(cam, pipe_id, buf_state);
+
+	s_data_cnt = 0;
+	spin_lock(&cam->running_job_lock);
+	list_for_each_entry_safe(req, req_prev, running, list) {
+		/* only handle requests belong to current ctx */
+		if (!(req->pipe_used & ctx->streaming_pipe))
+			continue;
+
+		num_s_data = mtk_cam_req_get_num_s_data(req, pipe_id);
+		/* reverse the order for release req with s_data_0 */
+		for (i = num_s_data - 1; i >= 0; i--) {
+			s_data = mtk_cam_req_get_s_data(req, pipe_id, i);
+			if (s_data) {
+				clean_s_data[s_data_cnt++] = s_data;
+				if (s_data_cnt >= RUNNING_JOB_DEPTH) {
+					dev_info(cam->dev,
+						 "%s: over local buffer cnt(%d)\n",
+						 __func__,  s_data_cnt);
+					goto STOP_SCAN;
+				}
+			} else {
+				dev_info(cam->dev,
+					 "%s:%s:pipe(%d): get s_data failed\n",
+					 __func__, req->req.debug_str, pipe_id);
+			}
+		}
+	}
+STOP_SCAN:
+	spin_unlock(&cam->running_job_lock);
+
+	for (handled_cnt = 0; handled_cnt < s_data_cnt; handled_cnt++) {
+		s_data = clean_s_data[handled_cnt];
+		req = mtk_cam_s_data_get_req(s_data);
+		if (!req) {
+			pr_info("ERR can't be recovered: invalid req found in s_data_clean_list\n");
+			continue;
+		}
+
+		if (ctx->used_raw_num != 0) {
+			if (s_data->index > 0)
+				dev_info(cam->dev,
+					 "%s:%s:pipe(%d):seq(%d): clean s_data_%d, raw_feature(%lld)\n",
+					 __func__, req->req.debug_str, pipe_id,
+					 s_data->frame_seq_no, s_data->index,
+					 ctx->pipe->feature_pending);
+			else
+				dev_dbg(cam->dev,
+					"%s:%s:pipe(%d):seq(%d): clean s_data_%d, raw_feature(%lld)\n",
+					__func__, req->req.debug_str, pipe_id,
+					s_data->frame_seq_no, s_data->index,
+					ctx->pipe->feature_pending);
+		}
+
+		/* Cancel s_data's works before we clean up the data */
+		if (atomic_read(&s_data->sensor_work.is_queued)) {
+			kthread_cancel_work_sync(&s_data->sensor_work.work);
+			dev_info(cam->dev,
+				 "%s:%s:pipe(%d):seq(%d): cancel sensor_work\n",
+				 __func__, req->req.debug_str, pipe_id,
+				 s_data->frame_seq_no);
+		}
+		atomic_set(&s_data->sensor_work.is_queued, 1);
+
+		if (atomic_read(&s_data->meta1_done_work.is_queued)) {
+			cancel_work_sync(&s_data->meta1_done_work.work);
+			dev_info(cam->dev,
+				 "%s:%s:pipe(%d):seq(%d): cancel AFO done_work\n",
+				 __func__, req->req.debug_str, pipe_id,
+				 s_data->frame_seq_no);
+		}
+		atomic_set(&s_data->meta1_done_work.is_queued, 1);
+
+		if (atomic_read(&s_data->frame_done_work.is_queued)) {
+			cancel_work_sync(&s_data->frame_done_work.work);
+			dev_info(cam->dev,
+				 "%s:%s:pipe(%d):seq(%d): cancel frame_done_work\n",
+				 __func__, req->req.debug_str, pipe_id,
+				 s_data->frame_seq_no);
+		}
+		atomic_set(&s_data->frame_done_work.is_queued, 1);
+
+		if (atomic_read(&s_data->dbg_exception_work.state) ==
+			MTK_CAM_REQ_DBGWORK_S_PREPARED) {
+			atomic_set(&s_data->dbg_exception_work.state,
+				   MTK_CAM_REQ_DBGWORK_S_CANCEL);
+			mtk_cam_debug_wakeup(&ctx->cam->debug_exception_waitq);
+			cancel_work_sync(&s_data->dbg_exception_work.work);
+			dev_info(cam->dev,
+				 "%s:%s:pipe(%d):seq(%d): cancel dbg_exception_work\n",
+				 __func__, req->req.debug_str, pipe_id,
+				 s_data->frame_seq_no);
+		}
+		atomic_set(&s_data->dbg_exception_work.state,
+			   MTK_CAM_REQ_DBGWORK_S_FINISHED);
+
+		if (atomic_read(&s_data->dbg_work.state) ==
+			MTK_CAM_REQ_DBGWORK_S_PREPARED) {
+			cancel_work_sync(&s_data->dbg_work.work);
+			dev_info(cam->dev,
+				 "%s:%s:pipe(%d):seq(%d): cancel dbg_work\n",
+				 __func__, req->req.debug_str, pipe_id,
+				 s_data->frame_seq_no);
+		}
+		atomic_set(&s_data->dbg_work.state,
+			   MTK_CAM_REQ_DBGWORK_S_FINISHED);
+
+		spin_lock(&req->done_status_lock);
+		dev_dbg(cam->dev,
+			"%s:%s:pipe(%d):seq(%d):req staus before clean:done(0x%x),pipe_used(0x%x)\n",
+			__func__, req->req.debug_str, pipe_id,
+			s_data->frame_seq_no, req->done_status, req->pipe_used);
+
+		need_clean_req = false;
+		if (atomic_read(&req->state) == MTK_CAM_REQ_STATE_RUNNING) {
+			/* mark request status to done for release */
+			req->done_status |= req->pipe_used & (1 << pipe_id);
+			if (req->done_status == req->pipe_used &&
+			    MTK_CAM_REQ_STATE_RUNNING ==
+			    atomic_cmpxchg(&req->state,
+					   MTK_CAM_REQ_STATE_RUNNING,
+					   MTK_CAM_REQ_STATE_DELETING))
+				need_clean_req = true;
+		}
+
+		/* if being the last one, check other pipes in the ctx */
+		other_pipes = 0;
+		done_status = req->done_status;
+		if (need_clean_req)
+			other_pipes = ctx->streaming_pipe & ~(1 << pipe_id);
+		spin_unlock(&req->done_status_lock);
+
+		/**
+		 * Before remove the request, flush other pipe's done work
+		 * in the same ctx to make sure mtk_cam_dev_job_done finished
+		 */
+		if (other_pipes) {
+			for (i = 0; i < MTKCAM_SUBDEV_MAX; i++) {
+				if (!(1 << i & other_pipes & done_status))
+					continue;
+
+				s_data_pipe = mtk_cam_req_get_s_data(req, i, 0);
+				if (!s_data_pipe)
+					continue;
+
+				/**
+				 * if done_status is marked, it means the work
+				 * is running or complete
+				 */
+				if (flush_work(&s_data->frame_done_work.work))
+					dev_info(cam->dev,
+						 "%s:%s:pipe(%d):seq(%d): flush pipe(%d) frame_done_work\n",
+						 __func__, req->req.debug_str,
+						 pipe_id, s_data_pipe->frame_seq_no,
+						 i);
+			}
+		}
+
+		mtk_cam_complete_sensor_hdl(s_data);
+		mtk_cam_complete_raw_hdl(s_data);
+		/*
+		 * reset fs state, if one sensor off and another one alive,
+		 * Let the req be the single sensor case.
+		 */
+		mutex_lock(&req->fs.op_lock);
+		mtk_cam_fs_reset(&req->fs);
+		mutex_unlock(&req->fs.op_lock);
+		if (need_clean_req) {
+			dev_info(cam->dev,
+				 "%s:%s:pipe(%d):seq(%d): remove req from running list\n",
+				 __func__, req->req.debug_str, pipe_id,
+				 s_data->frame_seq_no);
+			atomic_set(&req->state, MTK_CAM_REQ_STATE_CLEANUP);
+			spin_lock(&cam->running_job_lock);
+			list_del(&req->list);
+			cam->running_job_count--;
+			spin_unlock(&cam->running_job_lock);
+		} else {
+			dev_info(cam->dev,
+				 "%s:%s:pipe(%d):seq(%d): skip remove req from running list\n",
+				 __func__, req->req.debug_str, pipe_id,
+				 s_data->frame_seq_no);
+		}
+
+		if (mtk_cam_s_data_set_buf_state(s_data, buf_state)) {
+			if (s_data->index > 0) {
+				mtk_cam_req_return_pipe_buffers(req, pipe_id,
+								s_data->index);
+			} else {
+				/* handle vb2_buffer_done */
+				if (mtk_cam_req_put(req, pipe_id))
+					dev_dbg(cam->dev,
+						"%s:%s:pipe(%d) return request",
+						__func__, req->req.debug_str,
+						pipe_id);
+			}
+		}
+	}
+
+	/* all bufs in this node should be returned by req */
+
+	dev_dbg(cam->dev,
+		"%s: cleanup all stream off req, streaming ctx:0x%x, streaming pipe:0x%x)\n",
+		__func__, cam->streaming_ctx, cam->streaming_pipe);
+}
+
+void mtk_cam_req_get(struct mtk_cam_request *req, int pipe_id)
+{
+	atomic_inc(&req->ref_cnt);
+}
+
+bool mtk_cam_req_put(struct mtk_cam_request *req, int pipe_id)
+{
+	bool ret = false;
+
+	if (!atomic_dec_return(&req->ref_cnt))
+		ret = true;
+
+	/* release the pipe buf with s_data_pipe buf state */
+	mtk_cam_req_return_pipe_buffers(req, pipe_id, 0);
+
+	return ret;
+}
+
+static int config_img_in_fmt(struct mtk_cam_device *cam,
+			     struct mtk_cam_video_device *node,
+			     const struct v4l2_format *cfg_fmt,
+			     struct mtkcam_ipi_img_input *in_fmt)
+{
+	/* Check output & input image size dimension */
+	if (node->desc.dma_port != MTKCAM_IPI_RAW_RAWI_2) {
+		dev_info(cam->dev,
+			 "pipe(%d):dam_port(%d) only support MTKCAM_IPI_RAW_RAWI_2 now\n",
+			 node->uid.pipe_id, node->desc.dma_port);
+		return -EINVAL;
+	}
+
+	in_fmt->fmt.format =
+		mtk_cam_get_img_fmt(cfg_fmt->fmt.pix_mp.pixelformat);
+	if (in_fmt->fmt.format == MTKCAM_IPI_IMG_FMT_UNKNOWN) {
+		dev_info(cam->dev, "pipe: %d, node:%d unknown pixel fmt:%d\n",
+			 node->uid.pipe_id, node->desc.dma_port,
+			 cfg_fmt->fmt.pix_mp.pixelformat);
+		return -EINVAL;
+	}
+
+	in_fmt->fmt.s.w = cfg_fmt->fmt.pix_mp.width;
+	in_fmt->fmt.s.h = cfg_fmt->fmt.pix_mp.height;
+	in_fmt->fmt.stride[0] = cfg_fmt->fmt.pix_mp.plane_fmt[0].bytesperline;
+
+	dev_dbg(cam->dev,
+		"pipe: %d dma_port:%d size=%0dx%0d, stride:%d\n",
+		node->uid.pipe_id, node->desc.dma_port, in_fmt->fmt.s.w,
+		in_fmt->fmt.s.h, in_fmt->fmt.stride[0]);
+
+	return 0;
+}
+
+static int config_img_fmt(struct mtk_cam_device *cam,
+			  struct mtk_cam_video_device *node,
+			  const struct v4l2_format *cfg_fmt,
+			  struct mtkcam_ipi_img_output *out_fmt, int sd_width,
+			  int sd_height)
+{
+	/* Check output & input image size dimension */
+	if (node->desc.dma_port == MTKCAM_IPI_RAW_IMGO &&
+	    (cfg_fmt->fmt.pix_mp.width > sd_width ||
+			cfg_fmt->fmt.pix_mp.height > sd_height)) {
+		dev_err(cam->dev, "pipe: %d cfg(%d,%d) size is larger than sensor(%d,%d)\n",
+			node->uid.pipe_id,
+			cfg_fmt->fmt.pix_mp.width, cfg_fmt->fmt.pix_mp.height,
+			sd_width, sd_height);
+		return -EINVAL;
+	}
+
+	out_fmt->fmt.format =
+		mtk_cam_get_img_fmt(cfg_fmt->fmt.pix_mp.pixelformat);
+	if (out_fmt->fmt.format == MTKCAM_IPI_IMG_FMT_UNKNOWN) {
+		dev_err(cam->dev, "pipe: %d, node:%d unknown pixel fmt:%d\n",
+			node->uid.pipe_id, node->desc.dma_port,
+			cfg_fmt->fmt.pix_mp.pixelformat);
+		return -EINVAL;
+	}
+	out_fmt->fmt.s.w = cfg_fmt->fmt.pix_mp.width;
+	out_fmt->fmt.s.h = cfg_fmt->fmt.pix_mp.height;
+
+	/* not support multi-plane stride */
+	out_fmt->fmt.stride[0] = cfg_fmt->fmt.pix_mp.plane_fmt[0].bytesperline;
+
+	if (out_fmt->crop.p.x == 0 && out_fmt->crop.s.w == 0) {
+		out_fmt->crop.p.x = 0;
+		out_fmt->crop.p.y = 0;
+		out_fmt->crop.s.w = sd_width;
+		out_fmt->crop.s.h = sd_height;
+	}
+
+	dev_dbg(cam->dev,
+		"pipe: %d dma_port:%d size=%0dx%0d, stride:%d, crop=%0dx%0d\n",
+		node->uid.pipe_id, node->desc.dma_port, out_fmt->fmt.s.w,
+		out_fmt->fmt.s.h, out_fmt->fmt.stride[0], out_fmt->crop.s.w,
+		out_fmt->crop.s.h);
+
+	return 0;
+}
+
+static void mtk_cam_req_set_vfmt(struct mtk_cam_device *cam,
+				 struct mtk_raw_pipeline *raw_pipeline,
+				 struct mtk_cam_request_stream_data *s_data)
+{
+	struct mtk_cam_video_device *node;
+	struct mtk_cam_request *req;
+	struct v4l2_format *f;
+	struct v4l2_selection *s;
+	int i;
+
+	req = mtk_cam_s_data_get_req(s_data);
+
+	/* force update format to every video device before re-streamon */
+	for (i = MTK_RAW_SINK_NUM + 1; i < MTK_RAW_META_OUT_BEGIN; i++) {
+		node = &raw_pipeline->vdev_nodes[i - MTK_RAW_SINK_NUM];
+		f = mtk_cam_s_data_get_vfmt(s_data, node->desc.id);
+		if (!f) {
+			dev_info(cam->dev,
+				 "%s:%s:pipe(%d):%s: can't find the vfmt field to save\n",
+				 __func__, req->req.debug_str,
+				 node->uid.pipe_id, node->desc.name);
+		} else {
+			*f = node->active_fmt;
+			dev_dbg(cam->dev,
+				"%s:%s:pipe(%d):%s:save v4l2 fmt: pixelfmt(0x%x), w(%d), h(%d)\n",
+				__func__, req->req.debug_str,
+				node->uid.pipe_id, node->desc.name,
+				f->fmt.pix_mp.pixelformat,
+				f->fmt.pix_mp.width, f->fmt.pix_mp.height);
+		}
+
+		s = mtk_cam_s_data_get_vsel(s_data, node->desc.id);
+		if (!s) {
+			dev_info(cam->dev,
+				 "%s:%s:pipe(%d):%s: can't find the vsel field to save\n",
+				 __func__, req->req.debug_str,
+				 node->uid.pipe_id,
+				 node->desc.name);
+		} else {
+			*s = node->active_crop;
+		}
+	}
+}
+
+static int mtk_cam_req_set_fmt(struct mtk_cam_device *cam,
+			       struct mtk_cam_request *req)
+{
+	int pipe_id;
+	int pad;
+	struct mtk_cam_request_stream_data *stream_data;
+	struct mtk_raw_pipeline *raw_pipeline;
+
+	dev_dbg(cam->dev, "%s:%s\n", __func__, req->req.debug_str);
+	for (pipe_id = 0; pipe_id < cam->max_stream_num; pipe_id++) {
+		if (req->pipe_used & (1 << pipe_id)) {
+			if (is_raw_subdev(pipe_id)) {
+				stream_data = mtk_cam_req_get_s_data(req, pipe_id, 0);
+				raw_pipeline = &cam->raw.pipelines[pipe_id];
+				mtk_cam_req_set_vfmt(cam, raw_pipeline,
+						     stream_data);
+				pad = MTK_RAW_SINK;
+
+				/* Set MEDIA_PAD_FL_SINK pad's fmt */
+				for (pad = MTK_RAW_SINK_BEGIN;
+				     pad < MTK_RAW_SOURCE_BEGIN; pad++) {
+					stream_data->pad_fmt[pad].format =
+						raw_pipeline->cfg[pad].mbus_fmt;
+				}
+				/* Set MEDIA_PAD_FL_SOURCE pad's fmt */
+				for (pad = MTK_RAW_SOURCE_BEGIN;
+				     pad < MTK_RAW_PIPELINE_PADS_NUM; pad++) {
+					stream_data->pad_fmt[pad].format =
+						raw_pipeline->cfg[pad].mbus_fmt;
+				}
+			}
+		}
+	}
+	return 0;
+}
+
+static int mtk_cam_req_update_ctrl(struct mtk_raw_pipeline *raw_pipe,
+				   struct mtk_cam_request_stream_data *s_data)
+{
+	char *debug_str = mtk_cam_s_data_get_dbg_str(s_data);
+	struct mtk_cam_request *req;
+	struct mtk_cam_req_raw_pipe_data *raw_pipe_data;
+
+	raw_pipe_data = mtk_cam_s_data_get_raw_pipe_data(s_data);
+	if (!raw_pipe_data) {
+		dev_err(raw_pipe->subdev.v4l2_dev->dev,
+			"%s: cannot find raw_pipe_data, pipe_id:%d, frm_seq:%d\n",
+			__func__, s_data->pipe_id, s_data->frame_seq_no);
+		return -EINVAL;
+	}
+	req = mtk_cam_s_data_get_req(s_data);
+	if (!req) {
+		dev_err(raw_pipe->subdev.v4l2_dev->dev,
+			"%s: cannot find req, pipe_id:%d, frm_seq:%d\n",
+			__func__, s_data->pipe_id, s_data->frame_seq_no);
+		return -EINVAL;
+	}
+
+	/* clear seamless switch mode */
+	raw_pipe->sensor_mode_update = 0;
+	mtk_cam_req_ctrl_setup(raw_pipe, req);
+
+	s_data->feature.raw_feature = raw_pipe->feature_pending;
+	atomic_set(&s_data->first_setting_check, 0);
+
+	dev_dbg(raw_pipe->subdev.v4l2_dev->dev,
+		"%s:%s:%s:raw_feature(0x%0x), sensor_mode_update(0x%0x)\n",
+		__func__, raw_pipe->subdev.name, debug_str,
+		s_data->feature.raw_feature,
+		raw_pipe->sensor_mode_update);
+
+	if (raw_pipe->sensor_mode_update)
+		s_data->flags |= MTK_CAM_REQ_S_DATA_FLAG_SENSOR_MODE_UPDATE_T1;
+
+	raw_pipe_data->res = raw_pipe->user_res;
+
+	return 0;
+}
+
+static int mtk_cam_fill_img_buf(struct mtkcam_ipi_img_output *img_out,
+				const struct v4l2_format *f, dma_addr_t daddr)
+{
+	u32 pixelformat = f->fmt.pix_mp.pixelformat;
+	u32 width = f->fmt.pix_mp.width;
+	u32 height = f->fmt.pix_mp.height;
+	const struct v4l2_plane_pix_format *plane = &f->fmt.pix_mp.plane_fmt[0];
+	u32 stride = plane->bytesperline;
+	u32 aligned_width;
+	unsigned int addr_offset = 0;
+	int i;
+
+	if (is_mtk_format(pixelformat)) {
+		const struct mtk_format_info *info;
+
+		info = mtk_format_info(pixelformat);
+		if (!info)
+			return -EINVAL;
+
+		aligned_width = stride / info->bpp[0];
+		if (info->mem_planes == 1) {
+			if (is_yuv_ufo(pixelformat)) {
+				aligned_width = ALIGN(width, 64);
+				img_out->buf[0][0].iova = daddr;
+				img_out->fmt.stride[0] = aligned_width * info->bit_r_num
+							 / info->bit_r_den;
+				img_out->buf[0][0].size = img_out->fmt.stride[0] * height;
+				img_out->buf[0][0].size += img_out->fmt.stride[0] * height / 2;
+				img_out->buf[0][0].size += ALIGN((aligned_width / 64), 8) * height;
+				img_out->buf[0][0].size += ALIGN((aligned_width / 64), 8) * height
+							   / 2;
+				img_out->buf[0][0].size += sizeof(struct ufbc_buffer_header);
+
+				pr_debug("plane:%d stride:%d plane_size:%d addr:0x%lx\n",
+					 0, img_out->fmt.stride[0], img_out->buf[0][0].size,
+					 (unsigned long)img_out->buf[0][0].iova);
+			} else if (is_raw_ufo(pixelformat)) {
+				aligned_width = ALIGN(width, 64);
+				img_out->buf[0][0].iova = daddr;
+				img_out->fmt.stride[0] = aligned_width * info->bit_r_num /
+							 info->bit_r_den;
+				img_out->buf[0][0].size = img_out->fmt.stride[0] * height;
+				img_out->buf[0][0].size += ALIGN((aligned_width / 64), 8) * height;
+				img_out->buf[0][0].size += sizeof(struct ufbc_buffer_header);
+
+				pr_debug("plane:%d stride:%d plane_size:%d addr:0x%lx\n",
+					 0, img_out->fmt.stride[0],
+					 img_out->buf[0][0].size,
+					 (unsigned long)img_out->buf[0][0].iova);
+			} else {
+				for (i = 0; i < info->comp_planes; i++) {
+					unsigned int hdiv = (i == 0) ? 1 : info->hdiv;
+					unsigned int vdiv = (i == 0) ? 1 : info->vdiv;
+
+					img_out->buf[0][i].iova = daddr + addr_offset;
+					img_out->fmt.stride[i] = info->bpp[i] *
+						DIV_ROUND_UP(aligned_width, hdiv);
+					img_out->buf[0][i].size = img_out->fmt.stride[i]
+						* DIV_ROUND_UP(height, vdiv);
+					addr_offset += img_out->buf[0][i].size;
+
+					pr_debug("plane:%d stride:%d plane_size:%d addr:0x%lx\n",
+						 i, img_out->fmt.stride[i],
+						 img_out->buf[0][i].size,
+						 (unsigned long)img_out->buf[0][i].iova);
+				}
+			}
+		} else {
+			pr_debug("do not support non contiguous mplane\n");
+		}
+	} else {
+		const struct v4l2_format_info *info;
+
+		info = v4l2_format_info(pixelformat);
+		if (!info)
+			return -EINVAL;
+
+		aligned_width = stride / info->bpp[0];
+		if (info->mem_planes == 1) {
+			for (i = 0; i < info->comp_planes; i++) {
+				unsigned int hdiv = (i == 0) ? 1 : info->hdiv;
+				unsigned int vdiv = (i == 0) ? 1 : info->vdiv;
+
+				img_out->buf[0][i].iova = daddr + addr_offset;
+				img_out->fmt.stride[i] = info->bpp[i] *
+					DIV_ROUND_UP(aligned_width, hdiv);
+				img_out->buf[0][i].size = img_out->fmt.stride[i]
+					* DIV_ROUND_UP(height, vdiv);
+				addr_offset += img_out->buf[0][i].size;
+
+				pr_debug("stride:%d plane_size:%d addr:0x%lx\n",
+					 img_out->fmt.stride[i],
+					 img_out->buf[0][i].size,
+					 (unsigned long)img_out->buf[0][i].iova);
+			}
+		} else {
+			pr_debug("do not support non contiguous mplane\n");
+		}
+	}
+
+	return 0;
+}
+
+static void mtk_cam_config_raw_path(struct mtk_cam_device *cam,
+				    struct mtkcam_ipi_frame_param *frame_param,
+				    struct mtk_cam_buffer *buf)
+{
+	struct mtk_cam_video_device *node;
+	struct mtk_raw_pipeline *raw_pipeline;
+
+	node = mtk_cam_vbq_to_vdev(buf->vbb.vb2_buf.vb2_queue);
+	raw_pipeline = mtk_cam_dev_get_raw_pipeline(cam, node->uid.pipe_id);
+	if (!raw_pipeline) {
+		dev_err(cam->dev, "%s: cannot find raw_pipeline, pipe_id:%d\n",
+			__func__, node->uid.pipe_id);
+		return;
+	}
+
+	if (raw_pipeline->res_config.raw_path == V4L2_MTK_CAM_RAW_PATH_SELECT_BPC)
+		frame_param->raw_param.imgo_path_sel = MTKCAM_IPI_IMGO_AFTER_BPC;
+	else if (raw_pipeline->res_config.raw_path == V4L2_MTK_CAM_RAW_PATH_SELECT_FUS)
+		frame_param->raw_param.imgo_path_sel = MTKCAM_IPI_IMGO_AFTER_FUS;
+	else if (raw_pipeline->res_config.raw_path == V4L2_MTK_CAM_RAW_PATH_SELECT_DGN)
+		frame_param->raw_param.imgo_path_sel = MTKCAM_IPI_IMGO_AFTER_DGN;
+	else if (raw_pipeline->res_config.raw_path == V4L2_MTK_CAM_RAW_PATH_SELECT_LSC)
+		frame_param->raw_param.imgo_path_sel = MTKCAM_IPI_IMGO_AFTER_LSC;
+	else if (raw_pipeline->res_config.raw_path == V4L2_MTK_CAM_RAW_PATH_SELECT_LTM)
+		frame_param->raw_param.imgo_path_sel = MTKCAM_IPI_IMGO_AFTER_LTM;
+	else
+		/* un-processed raw frame */
+		frame_param->raw_param.imgo_path_sel = MTKCAM_IPI_IMGO_UNPROCESSED;
+
+	dev_dbg(cam->dev, "%s: node:%d fd:%d idx:%d raw_path(%d) ipi imgo_path_sel(%d))\n",
+		__func__, node->desc.id, buf->vbb.request_fd, buf->vbb.vb2_buf.index,
+		raw_pipeline->res_config.raw_path, frame_param->raw_param.imgo_path_sel);
+}
+
+static void mtk_cam_config_raw_img_out_imgo(struct mtk_cam_device *cam,
+					    struct mtkcam_ipi_frame_param *frame_param,
+					    struct mtk_cam_buffer *buf)
+{
+	struct mtk_cam_video_device *node;
+	struct mtkcam_ipi_img_output *img_out;
+	unsigned int pixelformat;
+
+	node = mtk_cam_vbq_to_vdev(buf->vbb.vb2_buf.vb2_queue);
+
+	/* not support sub-sampling multi-plane buffer */
+	img_out = &frame_param->img_outs[node->desc.id - MTK_RAW_SOURCE_BEGIN];
+	pixelformat = node->active_fmt.fmt.pix_mp.pixelformat;
+	img_out->buf[0][0].iova = buf->daddr;
+	if (is_raw_ufo(pixelformat))
+		mtk_cam_fill_img_buf(img_out, &node->active_fmt, buf->daddr);
+}
+
+/* Update dmo's buffer information except imgo (address and size) */
+static void mtk_cam_config_raw_img_out(struct mtk_cam_device *cam,
+				       struct mtkcam_ipi_frame_param *frame_param,
+				       struct mtk_cam_buffer *buf)
+{
+	struct mtk_cam_video_device *node;
+	struct mtkcam_ipi_img_output *img_out;
+
+	node = mtk_cam_vbq_to_vdev(buf->vbb.vb2_buf.vb2_queue);
+
+	/* not support sub-sampling multi-plane buffer */
+	img_out = &frame_param->img_outs[node->desc.id - MTK_RAW_SOURCE_BEGIN];
+	mtk_cam_fill_img_buf(img_out, &node->active_fmt, buf->daddr);
+}
+
+static int
+mtk_cam_config_raw_img_fmt(struct mtk_cam_device *cam,
+			   struct mtkcam_ipi_frame_param *frame_param,
+			   struct mtk_cam_buffer *buf)
+{
+	struct mtk_cam_video_device *node;
+	struct mtkcam_ipi_img_output *img_out;
+	struct v4l2_mbus_framefmt *pfmt;
+	int sd_width, sd_height, ret;
+	struct mtk_raw_pipeline *raw_pipline;
+
+	node = mtk_cam_vbq_to_vdev(buf->vbb.vb2_buf.vb2_queue);
+	raw_pipline = mtk_cam_dev_get_raw_pipeline(cam, node->uid.pipe_id);
+
+	/* not support sub-sampling multi-plane buffer */
+	img_out = &frame_param->img_outs[node->desc.id - MTK_RAW_SOURCE_BEGIN];
+
+	pfmt = &raw_pipline->cfg[MTK_RAW_SINK].mbus_fmt;
+	sd_width = pfmt->width;
+	sd_height = pfmt->height;
+
+	img_out->uid.pipe_id = node->uid.pipe_id;
+	img_out->uid.id =  node->desc.dma_port;
+
+	img_out->crop.p.x = node->active_crop.r.left;
+	img_out->crop.p.y = node->active_crop.r.top;
+	img_out->crop.s.w = node->active_crop.r.width;
+	img_out->crop.s.h = node->active_crop.r.height;
+
+	ret = config_img_fmt(cam, node, &node->active_fmt, img_out,
+			     sd_width, sd_height);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+/* Update raw_param.imgo_path_sel */
+static void mtk_cam_req_config_raw_path(struct mtk_cam_request_stream_data *s_data,
+					struct mtk_cam_buffer *buf)
+{
+	struct mtk_cam_ctx *ctx;
+	struct mtk_cam_device *cam;
+	struct mtkcam_ipi_frame_param *frame_param;
+
+	ctx = mtk_cam_s_data_get_ctx(s_data);
+	cam = ctx->cam;
+	frame_param = &s_data->frame_params;
+
+	mtk_cam_config_raw_path(cam, frame_param, buf);
+}
+
+/*
+ * Update:
+ * 1. imgo's buffer information (address and size)
+ * 2. rawi's buffer information (address and size)
+ */
+static int mtk_cam_req_config_raw_img_out_imgo(struct mtk_cam_request_stream_data *s_data,
+					       struct mtk_cam_buffer *buf)
+{
+	struct mtk_cam_ctx *ctx;
+	struct mtk_cam_device *cam;
+	struct mtk_cam_request *req;
+	struct mtk_cam_video_device *node;
+	struct mtkcam_ipi_frame_param *frame_param;
+	struct mtkcam_ipi_img_output *img_out;
+	const struct v4l2_format *cfg_fmt;
+	unsigned int pixelformat;
+
+	ctx = mtk_cam_s_data_get_ctx(s_data);
+	cam = ctx->cam;
+	req = mtk_cam_s_data_get_req(s_data);
+	frame_param = &s_data->frame_params;
+	node = mtk_cam_vbq_to_vdev(buf->vbb.vb2_buf.vb2_queue);
+
+	/* not support sub-sampling multi-plane buffer */
+	img_out = &frame_param->img_outs[node->desc.id - MTK_RAW_SOURCE_BEGIN];
+	cfg_fmt = mtk_cam_s_data_get_vfmt(s_data, node->desc.id);
+	if (!cfg_fmt) {
+		dev_info(cam->dev,
+			 "%s:%s:pipe(%d):%s: can't find the vfmt field to save\n",
+			 __func__, req->req.debug_str, node->uid.pipe_id, node->desc.name);
+		return -EINVAL;
+	}
+	pixelformat = cfg_fmt->fmt.pix_mp.pixelformat;
+	img_out->buf[0][0].iova = buf->daddr;
+	if (is_raw_ufo(pixelformat))
+		mtk_cam_fill_img_buf(img_out, cfg_fmt, buf->daddr);
+
+	return 0;
+}
+
+/* Update dmo's buffer information except imgo (address and size) */
+static int mtk_cam_req_config_raw_img_out(struct mtk_cam_request_stream_data *s_data,
+					  struct mtk_cam_buffer *buf)
+{
+	struct mtk_cam_ctx *ctx;
+	struct mtk_cam_device *cam;
+	struct mtk_cam_request *req;
+	struct mtk_cam_video_device *node;
+	struct mtkcam_ipi_frame_param *frame_param;
+	struct mtkcam_ipi_img_output *img_out;
+	const struct v4l2_format *cfg_fmt;
+
+	ctx = mtk_cam_s_data_get_ctx(s_data);
+	cam = ctx->cam;
+	req = mtk_cam_s_data_get_req(s_data);
+	frame_param = &s_data->frame_params;
+	node = mtk_cam_vbq_to_vdev(buf->vbb.vb2_buf.vb2_queue);
+
+	/* not support sub-sampling multi-plane buffer */
+	img_out = &frame_param->img_outs[node->desc.id - MTK_RAW_SOURCE_BEGIN];
+	cfg_fmt = mtk_cam_s_data_get_vfmt(s_data, node->desc.id);
+	if (!cfg_fmt) {
+		dev_info(cam->dev,
+			 "%s:%s:pipe(%d):%s: can't find the vfmt field to save\n",
+			 __func__, req->req.debug_str, node->uid.pipe_id, node->desc.name);
+		return -EINVAL;
+	}
+
+	mtk_cam_fill_img_buf(img_out, cfg_fmt, buf->daddr);
+
+	return 0;
+}
+
+static int
+mtk_cam_req_config_raw_img_fmt(struct mtk_cam_request_stream_data *s_data,
+			       struct mtk_cam_buffer *buf)
+{
+	struct mtk_cam_ctx *ctx;
+	struct mtk_cam_device *cam;
+	struct mtk_cam_request *req;
+	struct mtk_cam_video_device *node;
+	struct mtkcam_ipi_frame_param *frame_param;
+	struct mtkcam_ipi_img_output *img_out;
+	struct v4l2_mbus_framefmt *pfmt;
+	int sd_width, sd_height, ret;
+	const struct v4l2_format *cfg_fmt;
+	struct v4l2_selection *cfg_selection;
+
+	ctx = mtk_cam_s_data_get_ctx(s_data);
+	cam = ctx->cam;
+	req = mtk_cam_s_data_get_req(s_data);
+	frame_param = &s_data->frame_params;
+	node = mtk_cam_vbq_to_vdev(buf->vbb.vb2_buf.vb2_queue);
+
+	/* not support sub-sampling multi-plane buffer */
+	img_out = &frame_param->img_outs[node->desc.id - MTK_RAW_SOURCE_BEGIN];
+	cfg_fmt = mtk_cam_s_data_get_vfmt(s_data, node->desc.id);
+	if (!cfg_fmt) {
+		dev_err(cam->dev,
+			"%s:%s:pipe(%d):%s: can't find the vfmt field to save\n",
+			__func__, req->req.debug_str, node->uid.pipe_id, node->desc.name);
+		return -EINVAL;
+	}
+
+	cfg_selection = mtk_cam_s_data_get_vsel(s_data, node->desc.id);
+	if (!cfg_selection) {
+		dev_err(cam->dev,
+			"%s:%s:pipe(%d):%s: can't find the vsel field to save\n",
+			__func__, req->req.debug_str, node->uid.pipe_id, node->desc.name);
+		return -EINVAL;
+	}
+
+	pfmt = mtk_cam_s_data_get_pfmt(s_data, MTK_RAW_SINK);
+	sd_width = pfmt->width;
+	sd_height = pfmt->height;
+
+	img_out->uid.pipe_id = node->uid.pipe_id;
+	img_out->uid.id =  node->desc.dma_port;
+
+	img_out->crop.p.x = cfg_selection->r.left;
+	img_out->crop.p.y = cfg_selection->r.top;
+	img_out->crop.s.w = cfg_selection->r.width;
+	img_out->crop.s.h = cfg_selection->r.height;
+
+	ret = config_img_fmt(cam, node, cfg_fmt, img_out, sd_width, sd_height);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+static int
+mtk_cam_req_config_raw_img_in_rawi2(struct mtk_cam_request_stream_data *s_data,
+				    struct mtk_cam_buffer *buf)
+{
+	struct mtk_cam_ctx *ctx;
+	struct mtk_cam_device *cam;
+	struct mtk_cam_request *req;
+	struct mtk_cam_video_device *node;
+	struct mtkcam_ipi_frame_param *frame_param;
+	struct mtkcam_ipi_img_input *in_fmt;
+	const struct v4l2_format *cfg_fmt;
+	int ret;
+
+	ctx = mtk_cam_s_data_get_ctx(s_data);
+	cam = ctx->cam;
+	req = mtk_cam_s_data_get_req(s_data);
+	frame_param = &s_data->frame_params;
+	node = mtk_cam_vbq_to_vdev(buf->vbb.vb2_buf.vb2_queue);
+	in_fmt = &frame_param->img_ins[node->desc.id - MTK_RAW_RAWI_2_IN];
+
+	cfg_fmt = mtk_cam_s_data_get_vfmt(s_data, node->desc.id);
+	if (!cfg_fmt) {
+		dev_info(cam->dev,
+			 "%s:%s:pipe(%d):%s: can't find the vfmt field to save\n",
+			 __func__, req->req.debug_str, node->uid.pipe_id, node->desc.name);
+		return -EINVAL;
+	}
+
+	in_fmt->buf[0].iova = buf->daddr;
+	frame_param->raw_param.hardware_scenario =
+		MTKCAM_IPI_HW_PATH_OFFLINE_M2M;
+
+	in_fmt->uid.pipe_id = node->uid.pipe_id;
+	in_fmt->uid.id = node->desc.dma_port;
+	ret = config_img_in_fmt(cam, node, cfg_fmt, in_fmt);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+static int mtk_cam_req_update(struct mtk_cam_device *cam,
+			      struct mtk_cam_request *req)
+{
+	struct media_request_object *obj, *obj_prev;
+	struct vb2_buffer *vb;
+	struct mtk_cam_buffer *buf;
+	struct mtk_cam_video_device *node;
+	struct mtk_cam_ctx *ctx;
+	struct mtk_cam_request_stream_data *req_stream_data;
+	int i, ctx_cnt;
+	int ret;
+
+	dev_dbg(cam->dev, "update request:%s\n", req->req.debug_str);
+
+	mtk_cam_req_set_fmt(cam, req);
+
+	list_for_each_entry_safe(obj, obj_prev, &req->req.objects, list) {
+		if (!vb2_request_object_is_buffer(obj))
+			continue;
+		vb = container_of(obj, struct vb2_buffer, req_obj);
+		buf = mtk_cam_vb2_buf_to_dev_buf(vb);
+		node = mtk_cam_vbq_to_vdev(vb->vb2_queue);
+
+		ctx = mtk_cam_find_ctx(cam, &node->vdev.entity);
+		if (!ctx)
+			return -EINVAL;
+		req->ctx_used |= 1 << ctx->stream_id;
+
+		req_stream_data = mtk_cam_req_get_s_data(req, node->uid.pipe_id, 0);
+		req_stream_data->ctx = ctx;
+		req_stream_data->no_frame_done_cnt = 0;
+		atomic_set(&req_stream_data->sensor_work.is_queued, 0);
+		atomic_set(&req_stream_data->dbg_work.state, MTK_CAM_REQ_DBGWORK_S_INIT);
+		req_stream_data->dbg_work.dump_flags = 0;
+		atomic_set(&req_stream_data->dbg_exception_work.state, MTK_CAM_REQ_DBGWORK_S_INIT);
+		req_stream_data->dbg_exception_work.dump_flags = 0;
+		atomic_set(&req_stream_data->frame_done_work.is_queued, 0);
+		req->sync_id = (ctx->used_raw_num) ? ctx->pipe->sync_id : 0;
+
+		/* not support TWIN independent AFO  */
+		if (ctx->used_raw_num && ctx->pipe->res_config.raw_num_used == 1)
+			req_stream_data->flags |= MTK_CAM_REQ_S_DATA_FLAG_META1_INDEPENDENT;
+
+		/* update buffer format */
+		switch (node->desc.dma_port) {
+		case MTKCAM_IPI_RAW_RAWI_2:
+			ret = mtk_cam_req_config_raw_img_in_rawi2(req_stream_data, buf);
+			if (ret)
+				return ret;
+			break;
+		case MTKCAM_IPI_RAW_IMGO:
+			mtk_cam_req_config_raw_path(req_stream_data, buf);
+			ret = mtk_cam_req_config_raw_img_out_imgo(req_stream_data, buf);
+			if (ret)
+				return ret;
+
+			ret = mtk_cam_req_config_raw_img_fmt(req_stream_data, buf);
+			if (ret)
+				return ret;
+			break;
+		case MTKCAM_IPI_RAW_YUVO_1:
+		case MTKCAM_IPI_RAW_YUVO_2:
+		case MTKCAM_IPI_RAW_YUVO_3:
+		case MTKCAM_IPI_RAW_YUVO_4:
+		case MTKCAM_IPI_RAW_YUVO_5:
+		case MTKCAM_IPI_RAW_RZH1N2TO_1:
+		case MTKCAM_IPI_RAW_RZH1N2TO_2:
+		case MTKCAM_IPI_RAW_RZH1N2TO_3:
+		case MTKCAM_IPI_RAW_DRZS4NO_1:
+		case MTKCAM_IPI_RAW_DRZS4NO_2:
+		case MTKCAM_IPI_RAW_DRZS4NO_3:
+			ret = mtk_cam_req_config_raw_img_out(req_stream_data, buf);
+			if (ret)
+				return ret;
+
+			ret = mtk_cam_req_config_raw_img_fmt(req_stream_data, buf);
+				if (ret)
+					return ret;
+			break;
+		case MTKCAM_IPI_RAW_META_STATS_CFG:
+		case MTKCAM_IPI_RAW_META_STATS_0:
+		case MTKCAM_IPI_RAW_META_STATS_1:
+		case MTKCAM_IPI_RAW_META_STATS_2:
+			break;
+		default:
+			/* Do nothing for the ports not related to crop settings */
+			break;
+		}
+	}
+
+	/* frame sync */
+	/* prepare img working buf */
+	ctx_cnt = 0;
+	for (i = 0; i < cam->max_stream_num; i++) {
+		if (!(1 << i & req->ctx_used))
+			continue;
+
+		/* internal fs */
+		ctx_cnt++;
+
+		ctx = &cam->ctxs[i];
+		req_stream_data = mtk_cam_req_get_s_data(req, ctx->stream_id, 0);
+		if (!req_stream_data)
+			continue;
+	}
+	req->fs.target = ctx_cnt > 1 ? ctx_cnt : 0;
+
+	return 0;
+}
+
+/* Check all pipeline involved in the request are streamed on */
+static int mtk_cam_dev_req_is_stream_on(struct mtk_cam_device *cam,
+					struct mtk_cam_request *req)
+{
+	dev_dbg(cam->dev,
+		"%s: pipe_used(0x%x), streaming_pipe(0x%x), req(%s)\n",
+		__func__, req->pipe_used, cam->streaming_pipe,
+		req->req.debug_str);
+	return (req->pipe_used & cam->streaming_pipe) == req->pipe_used;
+}
+
+static void mtk_cam_req_work_init(struct mtk_cam_req_work *work,
+				  struct mtk_cam_request_stream_data *s_data)
+{
+	work->s_data = s_data;
+}
+
+static void mtk_cam_req_s_data_init(struct mtk_cam_request *req,
+				    u32 pipe_id,
+				    u32 s_data_index)
+{
+	struct mtk_cam_request_stream_data *req_stream_data;
+
+	req_stream_data = &req->p_data[pipe_id].s_data[s_data_index];
+	req_stream_data->req = req;
+	req_stream_data->pipe_id = pipe_id;
+	req_stream_data->state.estate = E_STATE_READY;
+	req_stream_data->index = s_data_index;
+	atomic_set(&req_stream_data->buf_state, -1);
+
+	/**
+	 * req_stream_data->flags is cleaned by
+	 * mtk_cam_req_s_data_clean () at previous job done
+	 * and may by updated by qbuf before request enqueue
+	 * so we don't reset it here.
+	 */
+	mtk_cam_req_work_init(&req_stream_data->seninf_s_fmt_work, req_stream_data);
+	mtk_cam_req_work_init(&req_stream_data->frame_work, req_stream_data);
+	mtk_cam_req_work_init(&req_stream_data->frame_done_work, req_stream_data);
+	mtk_cam_req_work_init(&req_stream_data->meta1_done_work, req_stream_data);
+	/**
+	 * clean the param structs since we support req reinit.
+	 * the mtk_cam_request may not be "zero" when it is
+	 * enqueued.
+	 */
+	memset(&req_stream_data->frame_params, 0,
+	       sizeof(req_stream_data->frame_params));
+
+	/* generally is single exposure */
+	req_stream_data->frame_params.raw_param.exposure_num = 1;
+}
+
+void mtk_cam_dev_req_try_queue(struct mtk_cam_device *cam)
+{
+	struct mtk_cam_ctx *ctx;
+	struct mtk_cam_request *req, *req_prev;
+	struct mtk_cam_request_stream_data *s_data;
+	int i, s_data_flags;
+	int enqueue_req_cnt, job_count, s_data_cnt;
+	struct list_head equeue_list;
+	struct v4l2_ctrl_handler *hdl;
+	struct media_request_object *sensor_hdl_obj, *raw_hdl_obj, *obj;
+	unsigned long flags;
+
+	if (!cam->streaming_ctx) {
+		dev_info(cam->dev, "streams are off\n");
+		return;
+	}
+
+	INIT_LIST_HEAD(&equeue_list);
+
+	spin_lock(&cam->running_job_lock);
+	job_count = cam->running_job_count;
+	spin_unlock(&cam->running_job_lock);
+
+	/* Pick up requests which are runnable */
+	enqueue_req_cnt = 0;
+	spin_lock(&cam->pending_job_lock);
+	list_for_each_entry_safe(req, req_prev, &cam->pending_job_list, list) {
+		if (likely(mtk_cam_dev_req_is_stream_on(cam, req))) {
+			if (job_count + enqueue_req_cnt >=
+			    RAW_PIPELINE_NUM * MTK_CAM_MAX_RUNNING_JOBS) {
+				dev_dbg(cam->dev, "jobs are full, job cnt(%d)\n",
+					job_count);
+				break;
+			}
+			dev_dbg(cam->dev, "%s job cnt(%d), allow req_enqueue(%s)\n",
+				__func__, job_count + enqueue_req_cnt, req->req.debug_str);
+
+			enqueue_req_cnt++;
+			list_del(&req->list);
+			list_add_tail(&req->list, &equeue_list);
+		}
+	}
+	spin_unlock(&cam->pending_job_lock);
+
+	if (!enqueue_req_cnt)
+		return;
+
+	list_for_each_entry_safe(req, req_prev, &equeue_list, list) {
+		for (i = 0; i < cam->max_stream_num; i++) {
+			if (!(req->pipe_used & 1 << i))
+				continue;
+
+			/* Initialize ctx related s_data fields */
+			ctx = &cam->ctxs[i];
+			sensor_hdl_obj = NULL;
+			raw_hdl_obj = NULL;
+			s_data_flags = 0;
+
+			/* Update frame_seq_no */
+			s_data = mtk_cam_req_get_s_data(req, i, 0);
+			s_data->frame_seq_no =
+				atomic_inc_return(&ctx->enqueued_frame_seq_no);
+			if (is_raw_subdev(i) && ctx->sensor) {
+				s_data_cnt =
+					atomic_inc_return(&ctx->running_s_data_cnt);
+				s_data->sensor = ctx->sensor;
+
+				spin_lock_irqsave(&req->req.lock, flags);
+				list_for_each_entry(obj, &req->req.objects, list) {
+					if (vb2_request_object_is_buffer(obj))
+						continue;
+
+					hdl = (struct v4l2_ctrl_handler *)obj->priv;
+					if (hdl == &ctx->pipe->ctrl_handler)
+						raw_hdl_obj = obj;
+					else if (hdl == ctx->sensor->ctrl_handler)
+						sensor_hdl_obj = obj;
+				}
+				spin_unlock_irqrestore(&req->req.lock, flags);
+
+				if (raw_hdl_obj) {
+					s_data->flags |=
+						MTK_CAM_REQ_S_DATA_FLAG_RAW_HDL_EN;
+					s_data->raw_hdl_obj = raw_hdl_obj;
+					dev_dbg(cam->dev,
+						"%s:%s:ctx(%d): find pipe hdl\n",
+						__func__, req->req.debug_str, i);
+				}
+
+				/* Apply raw subdev's ctrl */
+				mtk_cam_req_update_ctrl(ctx->pipe, s_data);
+
+				if (s_data->sensor && s_data->sensor->ctrl_handler &&
+				    sensor_hdl_obj) {
+					s_data->sensor_hdl_obj = sensor_hdl_obj;
+					dev_dbg(cam->dev,
+						"%s:%s:ctx(%d): find sensor(%s) hdl\n",
+						__func__, req->req.debug_str, i,
+						s_data->sensor->name);
+					s_data->flags |=
+						MTK_CAM_REQ_S_DATA_FLAG_SENSOR_HDL_EN;
+				}
+
+				s_data_flags = s_data->flags;
+
+				/* reload s_data */
+				s_data->flags = s_data_flags;
+				s_data->raw_hdl_obj = raw_hdl_obj;
+				s_data->sensor_hdl_obj = sensor_hdl_obj;
+
+				/* copy s_data content */
+				if (req->p_data[i].s_data_num == 2)
+					dev_info(cam->dev,
+						 "%s:req(%s): undefined s_data_1, raw_feature(%lld), s_data_cnt(%d)\n",
+						 __func__, req->req.debug_str,
+						 ctx->pipe->feature_pending,
+						 s_data_cnt);
+			} else if (is_raw_subdev(i) && !ctx->sensor) {
+				/* pure m2m raw ctrl handle */
+				s_data_cnt =
+					atomic_inc_return(&ctx->running_s_data_cnt);
+
+				spin_lock_irqsave(&req->req.lock, flags);
+				list_for_each_entry(obj, &req->req.objects, list) {
+					if (vb2_request_object_is_buffer(obj))
+						continue;
+
+					hdl = (struct v4l2_ctrl_handler *)obj->priv;
+					if (hdl == &ctx->pipe->ctrl_handler)
+						raw_hdl_obj = obj;
+				}
+				spin_unlock_irqrestore(&req->req.lock, flags);
+
+				if (raw_hdl_obj) {
+					s_data->flags |=
+						MTK_CAM_REQ_S_DATA_FLAG_RAW_HDL_EN;
+					s_data->raw_hdl_obj = raw_hdl_obj;
+					dev_dbg(cam->dev,
+						"%s:%s:ctx(%d): find pipe hdl, s_data_cnt(%d)\n",
+						__func__, req->req.debug_str, i,
+						s_data_cnt);
+				}
+
+				/* Apply raw subdev's ctrl */
+				mtk_cam_req_update_ctrl(ctx->pipe, s_data);
+			}
+		}
+
+		if (mtk_cam_req_update(cam, req)) {
+			dev_info(cam->dev,
+				 "%s:req(%s): invalid req settings which can't be recovered\n",
+				 __func__, req->req.debug_str);
+			WARN_ON(1);
+			return;
+		}
+
+		atomic_set(&req->state, MTK_CAM_REQ_STATE_RUNNING);
+		spin_lock(&cam->running_job_lock);
+		cam->running_job_count++;
+		list_del(&req->list);
+		list_add_tail(&req->list, &cam->running_job_list);
+		spin_unlock(&cam->running_job_lock);
+		mtk_cam_dev_req_enqueue(cam, req);
+	}
+}
+
+static struct media_request *mtk_cam_req_alloc(struct media_device *mdev)
+{
+	struct mtk_cam_request *cam_req;
+
+	cam_req = vzalloc(sizeof(*cam_req));
+	spin_lock_init(&cam_req->done_status_lock);
+	mutex_init(&cam_req->fs.op_lock);
+
+	return &cam_req->req;
+}
+
+static void mtk_cam_req_free(struct media_request *req)
+{
+	struct mtk_cam_request *cam_req = to_mtk_cam_req(req);
+
+	vfree(cam_req);
+}
+
+static int mtk_cam_req_chk_job_list(struct mtk_cam_device *cam,
+				    struct mtk_cam_request *new_req,
+				    struct list_head *job_list,
+				    char *job_list_name)
+{
+	if (!job_list || !job_list->prev || !job_list->prev->next || !new_req) {
+		dev_dbg(cam->dev,
+			"%s:%s: job_list, job_list->prev, job_list->prev->next, new_req can't be NULL\n",
+			__func__, job_list_name);
+		return -EINVAL;
+	}
+
+	if (job_list->prev->next != job_list) {
+		dev_dbg(cam->dev,
+			"%s broken: job_list->prev->next(%p), job_list(%p), req(%s)\n",
+			job_list_name, job_list->prev->next, job_list,
+			new_req->req.debug_str);
+		return -EINVAL;
+	}
+
+	if (&new_req->list == job_list->prev || &new_req->list == job_list) {
+		dev_dbg(cam->dev, "%s job double add: req(%s)\n",
+			job_list_name, new_req->req.debug_str);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static void mtk_cam_req_p_data_init(struct mtk_cam_request *req,
+				    u32 pipe_id,
+				    u32 s_data_num)
+{
+	u32 i = 0;
+
+	if (pipe_id >= MTKCAM_SUBDEV_MAX)
+		return;
+
+	req->p_data[pipe_id].s_data_num = s_data_num;
+	for (i = 0; i < s_data_num; i++)
+		mtk_cam_req_s_data_init(req, pipe_id, i);
+}
+
+static inline void mtk_cam_req_cnt_init(struct mtk_cam_request *req)
+{
+	atomic_set(&req->ref_cnt, 0);
+}
+
+static unsigned int mtk_cam_req_get_pipe_used(struct media_request *req)
+{
+	/**
+	 * V4L2 framework doesn't trigger q->ops->buf_queue(q, buf) when it is
+	 * stream off. We have to check the used context through the request
+	 * directly before streaming on.
+	 */
+	struct media_request_object *obj;
+	unsigned int pipe_used = 0;
+	struct mtk_cam_request *cam_req = to_mtk_cam_req(req);
+	unsigned int i;
+	struct mtk_cam_device *cam =
+		container_of(req->mdev, struct mtk_cam_device, media_dev);
+
+	list_for_each_entry(obj, &req->objects, list) {
+		struct vb2_buffer *vb;
+		struct mtk_cam_video_device *node;
+
+		if (!vb2_request_object_is_buffer(obj))
+			continue;
+		vb = container_of(obj, struct vb2_buffer, req_obj);
+		node = mtk_cam_vbq_to_vdev(vb->vb2_queue);
+		pipe_used |= 1 << node->uid.pipe_id;
+	}
+
+	mtk_cam_req_cnt_init(cam_req);
+
+	/* Initialize per pipe's stream data (without ctx)*/
+	for (i = 0; i < cam->max_stream_num; i++) {
+		if (pipe_used & (1 << i)) {
+			mtk_cam_req_p_data_init(cam_req, i, 1);
+			mtk_cam_req_get(cam_req, i); /* pipe id */
+		}
+	}
+
+	return pipe_used;
+}
+
+static void mtk_cam_req_queue(struct media_request *req)
+{
+	struct mtk_cam_request *cam_req = to_mtk_cam_req(req);
+	struct mtk_cam_device *cam =
+		container_of(req->mdev, struct mtk_cam_device, media_dev);
+
+	/* reset done status */
+	cam_req->done_status = 0;
+	cam_req->pipe_used = mtk_cam_req_get_pipe_used(req);
+	cam_req->ctx_used = 0; /* will update after stream on */
+	mtk_cam_fs_reset(&cam_req->fs);
+
+	/* update frame_params's dma_bufs in mtk_cam_vb2_buf_queue */
+	vb2_request_queue(req);
+
+	/* add to pending job list */
+	spin_lock(&cam->pending_job_lock);
+	if (mtk_cam_req_chk_job_list(cam, cam_req, &cam->pending_job_list,
+				     "pending_job_list")) {
+		spin_unlock(&cam->pending_job_lock);
+		return;
+	}
+
+	/**
+	 * Add req's ref cnt since it is used by pending_job_list and running
+	 * pending_job_list.
+	 */
+	atomic_set(&cam_req->state, MTK_CAM_REQ_STATE_PENDING);
+	list_add_tail(&cam_req->list, &cam->pending_job_list);
+	spin_unlock(&cam->pending_job_lock);
+	mutex_lock(&cam->queue_lock);
+	mtk_cam_dev_req_try_queue(cam);
+	mutex_unlock(&cam->queue_lock);
+}
+
+static int mtk_cam_link_notify(struct media_link *link, u32 flags,
+			       unsigned int notification)
+{
+	struct media_entity *source = link->source->entity;
+	struct media_entity *sink = link->sink->entity;
+	struct v4l2_subdev *subdev;
+	struct mtk_cam_ctx *ctx;
+	struct mtk_cam_device *cam;
+
+	if (source->function != MEDIA_ENT_F_VID_IF_BRIDGE ||
+	    notification != MEDIA_DEV_NOTIFY_POST_LINK_CH)
+		return v4l2_pipeline_link_notify(link, flags, notification);
+
+	subdev = media_entity_to_v4l2_subdev(sink);
+	if (!subdev)
+		return -EINVAL;
+	cam = container_of(subdev->v4l2_dev->mdev, struct mtk_cam_device, media_dev);
+	ctx = mtk_cam_find_ctx(cam, sink);
+
+	if (!ctx || !ctx->streaming || !(flags & MEDIA_LNK_FL_ENABLED))
+		return v4l2_pipeline_link_notify(link, flags, notification);
+
+	dev_info(cam->dev, "stream_id(%d) is streaming, can't change link\n",
+		 ctx->stream_id);
+
+	return -EBUSY;
+}
+
+static const struct media_device_ops mtk_cam_dev_ops = {
+	.link_notify = mtk_cam_link_notify,
+	.req_alloc = mtk_cam_req_alloc,
+	.req_free = mtk_cam_req_free,
+	.req_validate = vb2_request_validate,
+	.req_queue = mtk_cam_req_queue,
+};
+
+static int mtk_cam_of_rproc(struct mtk_cam_device *cam,
+			    struct platform_device *pdev)
+{
+	struct device *dev = cam->dev;
+
+	cam->scp = scp_get(pdev);
+	if (!cam->scp) {
+		dev_err(dev, "failed to get scp device\n");
+		return -ENODEV;
+	}
+
+	cam->rproc_handle = scp_get_rproc(cam->scp);
+	dev_dbg(dev, "camsys rproc_phandle: 0x%pK\n", cam->rproc_handle);
+	cam->smem_dev = scp_get_device(cam->scp);
+
+	return 0;
+}
+
+struct mtk_raw_device *get_main_raw_dev(struct mtk_cam_device *cam,
+					struct mtk_raw_pipeline *pipe)
+{
+	struct device *dev_main = NULL;
+	unsigned int i;
+
+	for (i = 0; i < cam->num_raw_devices; i++) {
+		if (pipe->enabled_raw & (1 << i)) {
+			dev_main = cam->raw.devs[i];
+			break;
+		}
+	}
+
+	if (!dev_main) {
+		dev_err(cam->dev, "Not found main raw device\n");
+		return NULL;
+	}
+
+	return dev_get_drvdata(dev_main);
+}
+
+struct mtk_raw_device *get_sub_raw_dev(struct mtk_cam_device *cam,
+				       struct mtk_raw_pipeline *pipe)
+{
+	struct device *dev_sub = NULL;
+	unsigned int i;
+
+	for (i = 0; i < cam->num_raw_devices - 1; i++) {
+		if (pipe->enabled_raw & (1 << i)) {
+			dev_sub = cam->raw.devs[i + 1];
+			break;
+		}
+	}
+
+	if (!dev_sub) {
+		dev_err(cam->dev, "Not found sub raw device\n");
+		return NULL;
+	}
+
+	return dev_get_drvdata(dev_sub);
+}
+
+struct mtk_raw_device *get_sub2_raw_dev(struct mtk_cam_device *cam,
+					struct mtk_raw_pipeline *pipe)
+{
+	struct device *dev_sub = NULL;
+	unsigned int i;
+
+	for (i = 0; i < cam->num_raw_devices; i++) {
+		if (pipe->enabled_raw & (1 << i)) {
+			dev_sub = cam->raw.devs[i + 2];
+			break;
+		}
+	}
+
+	if (!dev_sub) {
+		dev_err(cam->dev, "Not found second sub raw device\n");
+		return NULL;
+	}
+
+	return dev_get_drvdata(dev_sub);
+}
+
+static int isp_composer_handle_ack(struct mtk_cam_device *cam,
+				   struct mtkcam_ipi_event *ipi_msg)
+{
+	struct device *dev = cam->dev;
+	struct mtk_cam_ctx *ctx;
+	struct mtk_cam_working_buf_entry *buf_entry;
+	struct mtk_cam_request_stream_data *s_data;
+	struct mtk_raw_device *raw_dev;
+	bool is_m2m_apply_cq = false;
+
+	ctx = &cam->ctxs[ipi_msg->cookie.session_id];
+
+	if (mtk_cam_is_m2m(ctx)) {
+		if (ipi_msg->cookie.frame_no > 1) {
+			dev_dbg(dev, "[M2M] wait_for_completion +\n");
+			wait_for_completion(&ctx->m2m_complete);
+			dev_dbg(dev, "[M2M] wait_for_completion -\n");
+		}
+	}
+
+	spin_lock(&ctx->using_buffer_list.lock);
+
+	ctx->composed_frame_seq_no = ipi_msg->cookie.frame_no;
+	dev_dbg(dev, "ctx:%d ack frame_num:%d\n",
+		ctx->stream_id, ctx->composed_frame_seq_no);
+
+	/* get from using list */
+	if (list_empty(&ctx->using_buffer_list.list)) {
+		spin_unlock(&ctx->using_buffer_list.lock);
+		return -EINVAL;
+	}
+	/* assign raw using buf */
+	buf_entry = list_first_entry(&ctx->using_buffer_list.list,
+				     struct mtk_cam_working_buf_entry,
+				     list_entry);
+	list_del(&buf_entry->list_entry);
+	ctx->using_buffer_list.cnt--;
+
+	spin_lock(&cam->dma_processing_lock);
+	if (!list_empty(&cam->dma_processing)) {
+		spin_unlock(&cam->dma_processing_lock);
+		buf_entry->cq_desc_offset =
+			ipi_msg->ack_data.frame_result.cq_desc_offset;
+		buf_entry->cq_desc_size =
+			ipi_msg->ack_data.frame_result.cq_desc_size;
+		buf_entry->sub_cq_desc_offset =
+			ipi_msg->ack_data.frame_result.sub_cq_desc_offset;
+		buf_entry->sub_cq_desc_size =
+			ipi_msg->ack_data.frame_result.sub_cq_desc_size;
+
+		if (mtk_cam_is_m2m(ctx)) {
+			dev_info(dev, "Unsupport M2M with non-request buffer\n");
+			spin_unlock(&ctx->using_buffer_list.lock);
+			return -EINVAL;
+		}
+		goto skip_req;
+	}
+	spin_unlock(&cam->dma_processing_lock);
+
+	s_data = buf_entry->s_data;
+	if (!s_data) {
+		dev_dbg(dev, "ctx:%d no req for ack frame_num:%d\n",
+			ctx->stream_id, ctx->composed_frame_seq_no);
+		spin_unlock(&ctx->using_buffer_list.lock);
+		return -EINVAL;
+	}
+
+	buf_entry->cq_desc_offset =
+		ipi_msg->ack_data.frame_result.cq_desc_offset;
+	buf_entry->cq_desc_size =
+		ipi_msg->ack_data.frame_result.cq_desc_size;
+	buf_entry->sub_cq_desc_offset =
+		ipi_msg->ack_data.frame_result.sub_cq_desc_offset;
+	buf_entry->sub_cq_desc_size =
+		ipi_msg->ack_data.frame_result.sub_cq_desc_size;
+
+	/* Do nothing if the user doesn't enable force dump */
+	mtk_cam_req_dump(s_data, MTK_CAM_REQ_DUMP_FORCE,
+			 "Camsys Force Dump", false);
+
+	if (ipi_msg->ack_data.ret)
+		mtk_cam_req_dump(s_data, MTK_CAM_REQ_DUMP_DEQUEUE_FAILED,
+				 "Camsys compose error", false);
+
+	if (mtk_cam_is_m2m(ctx)) {
+		spin_lock(&ctx->composed_buffer_list.lock);
+		dev_dbg(dev, "%s ctx->composed_buffer_list.cnt %d\n",
+			__func__, ctx->composed_buffer_list.cnt);
+
+		if (ctx->composed_buffer_list.cnt == 0)
+			is_m2m_apply_cq = true;
+
+		spin_unlock(&ctx->composed_buffer_list.lock);
+
+		spin_lock(&ctx->processing_buffer_list.lock);
+		dev_dbg(dev, "%s ctx->processing_buffer_list.cnt %d\n",
+			__func__, ctx->processing_buffer_list.cnt);
+		spin_unlock(&ctx->processing_buffer_list.lock);
+	}
+
+skip_req:
+	if (ctx->composed_frame_seq_no == 1 || is_m2m_apply_cq) {
+		struct device *dev;
+		/* apply raw CQ */
+		spin_lock(&ctx->processing_buffer_list.lock);
+		list_add_tail(&buf_entry->list_entry,
+			      &ctx->processing_buffer_list.list);
+		ctx->processing_buffer_list.cnt++;
+		spin_unlock(&ctx->processing_buffer_list.lock);
+		spin_unlock(&ctx->using_buffer_list.lock);
+
+		dev = mtk_cam_find_raw_dev(cam, ctx->pipe->enabled_raw);
+		if (!dev) {
+			dev_dbg(dev, "frm#1 raw device not found\n");
+			return -EINVAL;
+		}
+
+		raw_dev = dev_get_drvdata(dev);
+
+		if (mtk_cam_is_m2m(ctx)) {
+			dev_dbg(dev,
+				"%s M2M apply_cq, composed_buffer_list.cnt %d frame_seq_no %d\n",
+				__func__, ctx->composed_buffer_list.cnt,
+				s_data->frame_seq_no);
+			mtk_cam_m2m_enter_cq_state(&s_data->state);
+		}
+
+		mtk_cam_raw_apply_cq(raw_dev, buf_entry->buffer.iova,
+				     buf_entry->cq_desc_size,
+				     buf_entry->cq_desc_offset,
+				     buf_entry->sub_cq_desc_size,
+				     buf_entry->sub_cq_desc_offset);
+		if (s_data) {
+			s_data->timestamp = ktime_get_boottime_ns();
+			s_data->timestamp_mono = ktime_get_ns();
+		}
+
+		return 0;
+	}
+
+	/* add composed_buffer_list */
+	spin_lock(&ctx->composed_buffer_list.lock);
+	list_add_tail(&buf_entry->list_entry, &ctx->composed_buffer_list.list);
+	ctx->composed_buffer_list.cnt++;
+	if (mtk_cam_is_m2m(ctx)) {
+		dev_dbg(dev, "%s M2M composed_buffer_list.cnt %d\n",
+			__func__, ctx->composed_buffer_list.cnt);
+	}
+	spin_unlock(&ctx->composed_buffer_list.lock);
+
+	spin_unlock(&ctx->using_buffer_list.lock);
+
+	return 0;
+}
+
+static void isp_composer_handler(void *data, unsigned int len, void *priv)
+{
+	struct mtk_cam_device *cam = (struct mtk_cam_device *)priv;
+	struct device *dev = cam->dev;
+	struct mtkcam_ipi_event *ipi_msg;
+	struct mtk_cam_ctx *ctx;
+
+	ipi_msg = (struct mtkcam_ipi_event *)data;
+
+	if (len < offsetofend(struct mtkcam_ipi_event, ack_data)) {
+		dev_dbg(dev, "wrong IPI len:%d\n", len);
+		return;
+	}
+
+	if (ipi_msg->cmd_id != CAM_CMD_ACK ||
+	    (ipi_msg->ack_data.ack_cmd_id != CAM_CMD_FRAME &&
+	     ipi_msg->ack_data.ack_cmd_id != CAM_CMD_DESTROY_SESSION))
+		return;
+
+	if (ipi_msg->ack_data.ack_cmd_id == CAM_CMD_FRAME) {
+		isp_composer_handle_ack(cam, ipi_msg);
+		return;
+
+	} else if (ipi_msg->ack_data.ack_cmd_id == CAM_CMD_DESTROY_SESSION) {
+		ctx = &cam->ctxs[ipi_msg->cookie.session_id];
+		complete(&ctx->session_complete);
+		dev_info(dev, "%s:ctx(%d): session destroyed",
+			 __func__, ctx->stream_id);
+	}
+}
+
+static int isp_composer_init(struct mtk_cam_device *cam)
+{
+	struct device *dev = cam->dev;
+	int ret;
+
+	ret = rproc_boot(cam->rproc_handle);
+	if (ret) {
+		dev_err(dev, "failed to rproc_boot\n");
+		return ret;
+	}
+
+	ret = scp_ipi_register(cam->scp, SCP_IPI_ISP_CMD,
+			       isp_composer_handler, cam);
+	if (ret) {
+		dev_err(dev, "failed to register IPI cmd\n");
+		return ret;
+	}
+
+	ret = scp_ipi_register(cam->scp, SCP_IPI_ISP_FRAME,
+			       isp_composer_handler, cam);
+	if (ret) {
+		dev_err(dev, "failed to register IPI frame\n");
+		goto unreg_ipi_cmd;
+	}
+
+	return 0;
+
+unreg_ipi_cmd:
+	scp_ipi_unregister(cam->scp, SCP_IPI_ISP_CMD);
+
+	return ret;
+}
+
+static void isp_composer_uninit(struct mtk_cam_device *cam)
+{
+	scp_ipi_unregister(cam->scp, SCP_IPI_ISP_CMD);
+	scp_ipi_unregister(cam->scp, SCP_IPI_ISP_FRAME);
+}
+
+int isp_composer_create_session(struct mtk_cam_ctx *ctx)
+{
+	struct mtk_cam_device *cam = ctx->cam;
+	struct mtkcam_ipi_event event;
+	struct mtkcam_ipi_session_cookie *session = &event.cookie;
+	struct mtkcam_ipi_session_param	*session_data = &event.session_data;
+
+	memset(&event, 0, sizeof(event));
+	event.cmd_id = CAM_CMD_CREATE_SESSION;
+	session->session_id = ctx->stream_id;
+	session_data->workbuf.iova = ctx->buf_pool.working_buf_iova;
+	session_data->workbuf.scp_addr = ctx->buf_pool.working_buf_scp_addr;
+	session_data->workbuf.size = ctx->buf_pool.working_buf_size;
+	session_data->msg_buf.scp_addr = ctx->buf_pool.msg_buf_scp_addr;
+	session_data->msg_buf.size = ctx->buf_pool.msg_buf_size;
+	session_data->raw_workbuf.scp_addr = ctx->buf_pool.raw_workbuf_scp_addr;
+	session_data->raw_workbuf.size = ctx->buf_pool.raw_workbuf_size;
+	session_data->priv_workbuf.scp_addr = ctx->buf_pool.priv_workbuf_scp_addr;
+	session_data->priv_workbuf.size = ctx->buf_pool.priv_workbuf_size;
+	session_data->session_buf.scp_addr = ctx->buf_pool.session_buf_scp_addr;
+	session_data->session_buf.size = ctx->buf_pool.session_buf_size;
+
+	scp_ipi_send(cam->scp, SCP_IPI_ISP_CMD, &event,
+		     sizeof(event), MTK_CAM_IPI_SEND_TIMEOUT);
+	dev_dbg(cam->dev,
+		"%s: IPI send id: %d, cq_buf(scp addr:%x,sz:%d, msg_buf(scp addr:%x,sz%d)\n",
+		__func__, event.cmd_id, session_data->workbuf.scp_addr,
+		session_data->workbuf.size, session_data->msg_buf.scp_addr,
+		session_data->msg_buf.size);
+	return 0;
+}
+
+void isp_composer_destroy_session(struct mtk_cam_ctx *ctx)
+{
+	struct mtk_cam_device *cam = ctx->cam;
+	struct mtkcam_ipi_event event;
+	struct mtkcam_ipi_session_cookie *session = &event.cookie;
+
+	memset(&event, 0, sizeof(event));
+	event.cmd_id = CAM_CMD_DESTROY_SESSION;
+	session->session_id = ctx->stream_id;
+	scp_ipi_send(cam->scp, SCP_IPI_ISP_CMD, &event,
+		     sizeof(event), MTK_CAM_IPI_SEND_TIMEOUT);
+	dev_info(cam->dev, "IPI send id: %d\n", event.cmd_id);
+}
+
+static void
+isp_composer_hw_config(struct mtk_cam_device *cam,
+		       struct mtk_cam_ctx *ctx,
+		       struct mtkcam_ipi_config_param *config_param)
+{
+	struct mtkcam_ipi_event event;
+	struct mtkcam_ipi_session_cookie *session = &event.cookie;
+	struct mtkcam_ipi_config_param *config = &event.config_data;
+
+	memset(&event, 0, sizeof(event));
+	event.cmd_id = CAM_CMD_CONFIG;
+	session->session_id = ctx->stream_id;
+	memcpy(config, config_param, sizeof(*config_param));
+	dev_dbg(cam->dev, "%s sw_feature:%d\n", __func__, config->sw_feature);
+	scp_ipi_send(cam->scp, SCP_IPI_ISP_CMD, &event,
+		     sizeof(event), MTK_CAM_IPI_SEND_TIMEOUT);
+	dev_dbg(cam->dev, "IPI send id: %d\n", event.cmd_id);
+
+	/* For debug dump file */
+	memcpy(&ctx->config_params, config_param, sizeof(*config_param));
+	dev_dbg(cam->dev, "%s:ctx(%d): save config_param to ctx, sz:%zu\n",
+		__func__, ctx->stream_id, sizeof(*config_param));
+}
+
+static int mtk_cam_s_data_dev_config(struct mtk_cam_request_stream_data *s_data)
+{
+	struct mtk_cam_req_raw_pipe_data *s_raw_pipe_data;
+	struct mtk_cam_ctx *ctx;
+	struct mtk_cam_device *cam;
+	struct device *dev;
+	struct mtkcam_ipi_config_param config_param;
+	struct mtkcam_ipi_input_param *cfg_in_param;
+	struct mtk_raw_pipeline *pipe;
+	struct mtk_raw *raw;
+	struct v4l2_mbus_framefmt *mf;
+	struct device *dev_raw;
+	struct mtk_raw_device *raw_dev;
+	struct v4l2_format *img_fmt;
+	unsigned int i;
+	u32 mf_code;
+
+	ctx = mtk_cam_s_data_get_ctx(s_data);
+	cam = ctx->cam;
+	dev = cam->dev;
+	pipe = ctx->pipe;
+	raw = pipe->raw;
+	mf = &pipe->cfg[MTK_RAW_SINK].mbus_fmt;
+	s_raw_pipe_data = mtk_cam_s_data_get_raw_pipe_data(s_data);
+	if (!s_raw_pipe_data)
+		return -EINVAL;
+
+	memset(&config_param, 0, sizeof(config_param));
+
+	/* Update cfg_in_param */
+	cfg_in_param = &config_param.input;
+	cfg_in_param->pixel_mode = ctx->pipe->res_config.tgo_pxl_mode;
+
+	cfg_in_param->data_pattern = MTKCAM_IPI_SENSOR_PATTERN_NORMAL;
+	img_fmt = &pipe->vdev_nodes[MTK_RAW_SINK].pending_fmt;
+	cfg_in_param->in_crop.s.w = img_fmt->fmt.pix_mp.width;
+	cfg_in_param->in_crop.s.h = img_fmt->fmt.pix_mp.height;
+	dev_dbg(dev, "sink pad code:0x%x, tg size:%d %d\n", mf->code,
+		cfg_in_param->in_crop.s.w, cfg_in_param->in_crop.s.h);
+
+	mf_code = mf->code & 0xffff; /* sensor mode */
+	cfg_in_param->raw_pixel_id = mtk_cam_get_sensor_pixel_id(mf_code);
+	cfg_in_param->fmt = mtk_cam_get_sensor_fmt(mf_code);
+	if (cfg_in_param->fmt == MTKCAM_IPI_IMG_FMT_UNKNOWN ||
+	    cfg_in_param->raw_pixel_id == MTKCAM_IPI_BAYER_PXL_ID_UNKNOWN) {
+		dev_err(dev, "unknown sd code:%d\n", mf_code);
+		return -EINVAL;
+	}
+
+	s_raw_pipe_data->enabled_raw = ctx->pipe->enabled_raw &
+		MTKCAM_SUBDEV_RAW_MASK;
+	config_param.flags = MTK_CAM_IPI_CONFIG_TYPE_INPUT_CHANGE;
+
+	dev_dbg(dev, "%s: config_param flag:0x%x enabled_raw:0x%x\n", __func__,
+		config_param.flags, s_raw_pipe_data->enabled_raw);
+
+	update_hw_mapping(ctx, &config_param);
+
+	config_param.sw_feature = MTKCAM_IPI_SW_FEATURE_NORMAL;
+
+	dev_raw = mtk_cam_find_raw_dev(cam, s_raw_pipe_data->enabled_raw);
+	if (!dev_raw) {
+		dev_err(dev, "config raw device not found\n");
+		return -EINVAL;
+	}
+
+	raw_dev = dev_get_drvdata(dev_raw);
+	for (i = 0; i < RAW_PIPELINE_NUM; i++)
+		if (raw->pipelines[i].enabled_raw & 1 << raw_dev->id) {
+			raw_dev->pipeline = &raw->pipelines[i];
+			/* TWIN case */
+			if (raw->pipelines[i].res_config.raw_num_used != 1) {
+				struct mtk_raw_device *raw_dev_sub =
+						get_sub_raw_dev(cam, ctx->pipe);
+				raw_dev_sub->pipeline = &raw->pipelines[i];
+				dev_dbg(dev, "twin main/sub raw_id:%d/%d\n",
+					raw_dev->id, raw_dev_sub->id);
+				if (raw->pipelines[i].res_config.raw_num_used == 3) {
+					struct mtk_raw_device *raw_dev_sub2 =
+						get_sub2_raw_dev(cam, ctx->pipe);
+					raw_dev_sub2->pipeline = &raw->pipelines[i];
+					dev_dbg(dev,
+						"triplet m/s/s2 raw_id:%d/%d/%d\n",
+						raw_dev->id, raw_dev_sub->id,
+						raw_dev_sub2->id);
+				}
+			}
+			break;
+		}
+
+	isp_composer_hw_config(cam, ctx, &config_param);
+	dev_dbg(dev, "raw %d %s done\n", raw_dev->id, __func__);
+
+	return 0;
+}
+
+static void mtk_cam_res_init(struct mtk_cam_resource_config *res_cfg)
+{
+	res_cfg->raw_num_used = 1;
+	res_cfg->bin_enable = 0;
+	res_cfg->raw_path = 0;
+	res_cfg->hwn_limit_min = 1;
+	res_cfg->raw_feature = 0;
+}
+
+static int mtk_cam_buf_config(struct mtk_cam_device *cam,
+			      struct mtkcam_ipi_frame_param *frame_param,
+			      struct mtk_cam_buffer *buf)
+{
+	struct vb2_buffer *vb;
+	struct mtk_cam_video_device *node;
+	int ret;
+
+	vb = &buf->vbb.vb2_buf;
+	node = mtk_cam_vbq_to_vdev(vb->vb2_queue);
+
+	/* update buffer format */
+	switch (node->desc.dma_port) {
+	case MTKCAM_IPI_RAW_IMGO:
+		mtk_cam_config_raw_path(cam, frame_param, buf);
+		mtk_cam_config_raw_img_out_imgo(cam, frame_param, buf);
+		ret = mtk_cam_config_raw_img_fmt(cam, frame_param, buf);
+		if (ret)
+			return ret;
+		break;
+	case MTKCAM_IPI_RAW_YUVO_1:
+	case MTKCAM_IPI_RAW_YUVO_2:
+	case MTKCAM_IPI_RAW_YUVO_3:
+	case MTKCAM_IPI_RAW_YUVO_4:
+	case MTKCAM_IPI_RAW_YUVO_5:
+	case MTKCAM_IPI_RAW_RZH1N2TO_1:
+	case MTKCAM_IPI_RAW_RZH1N2TO_2:
+	case MTKCAM_IPI_RAW_RZH1N2TO_3:
+	case MTKCAM_IPI_RAW_DRZS4NO_1:
+	case MTKCAM_IPI_RAW_DRZS4NO_2:
+	case MTKCAM_IPI_RAW_DRZS4NO_3:
+		mtk_cam_config_raw_img_out(cam, frame_param, buf);
+		break;
+	default:
+		/* Do nothing for the ports not related to crop settings */
+		break;
+	}
+
+	return 0;
+}
+
+void mtk_cam_buf_try_queue(struct mtk_cam_ctx *ctx)
+{
+	struct mtk_cam_device *cam;
+	struct mtk_cam_buffer *buf, *buf_prev;
+	struct mtkcam_ipi_event event;
+	struct mtkcam_ipi_session_cookie *session = &event.cookie;
+	struct mtkcam_ipi_frame_info *frame_info = &event.frame_data;
+	struct mtkcam_ipi_frame_param *frame_param;
+	struct mtkcam_ipi_frame_param *frame_data;
+	struct mtk_cam_working_buf_entry *buf_entry;
+	struct list_head equeue_list;
+	unsigned int processing_cnt, enque_cnt;
+
+	cam = ctx->cam;
+	if (!cam->streaming_ctx) {
+		dev_info(cam->dev, "streams are off\n");
+		return;
+	}
+
+	INIT_LIST_HEAD(&equeue_list);
+
+	spin_lock(&cam->dma_processing_lock);
+	processing_cnt = cam->dma_processing_count;
+	spin_unlock(&cam->dma_processing_lock);
+
+	enque_cnt = 0;
+	spin_lock(&cam->dma_pending_lock);
+	list_for_each_entry_safe(buf, buf_prev, &cam->dma_pending, list) {
+		if (processing_cnt + enque_cnt >= MTK_CAM_MAX_PROCESSING_BUFS) {
+			dev_dbg(cam->dev,
+				"processing bufs are full, buf cnt(%d)\n",
+				processing_cnt);
+			break;
+		}
+		dev_dbg(cam->dev, "%s buf cnt(%d)\n",
+			__func__, processing_cnt + enque_cnt);
+
+		enque_cnt++;
+		list_del(&buf->list);
+		list_add_tail(&buf->list, &equeue_list);
+	}
+	spin_unlock(&cam->dma_pending_lock);
+
+	if (!enque_cnt)
+		return;
+
+	frame_param = kzalloc(sizeof(*frame_param), GFP_KERNEL);
+	if (!frame_param)
+		return;
+
+	list_for_each_entry_safe(buf, buf_prev, &equeue_list, list) {
+		if (buf->state.estate == E_BUF_STATE_COMPOSED)
+			continue;
+
+		memset(&event, 0, sizeof(event));
+		event.cmd_id = CAM_CMD_FRAME;
+		session->session_id = ctx->stream_id;
+		/* prepare working buffer */
+		buf_entry = mtk_cam_working_buf_get(ctx);
+		if (!buf_entry) {
+			dev_info(cam->dev,
+				 "%s: No CQ buf availablle: enqueued_frame_seq_no:%d\n",
+				 __func__, atomic_read(&ctx->enqueued_frame_seq_no));
+			WARN_ON(1);
+			goto EXIT;
+		}
+
+		spin_lock(&ctx->using_buffer_list.lock);
+		list_add_tail(&buf_entry->list_entry, &ctx->using_buffer_list.list);
+		ctx->using_buffer_list.cnt++;
+		spin_unlock(&ctx->using_buffer_list.lock);
+
+		spin_lock(&cam->dma_processing_lock);
+		list_del(&buf->list);
+		list_add_tail(&buf->list, &cam->dma_processing);
+		cam->dma_processing_count++;
+		spin_unlock(&cam->dma_processing_lock);
+
+		/* Prepare rp message */
+		frame_info->cur_msgbuf_offset =
+			buf_entry->msg_buffer.va -
+			cam->ctxs[session->session_id].buf_pool.msg_buf_va;
+		frame_info->cur_msgbuf_size = buf_entry->msg_buffer.size;
+		frame_data = (struct mtkcam_ipi_frame_param *)buf_entry->msg_buffer.va;
+		session->frame_no = atomic_inc_return(&ctx->enqueued_frame_seq_no);
+
+		if (mtk_cam_buf_config(cam, frame_param, buf)) {
+			dev_err(cam->dev, "%s: Buffer config failed\n",	__func__);
+			continue;
+		}
+		memcpy(frame_data, frame_param, sizeof(*frame_param));
+		frame_data->cur_workbuf_offset =
+			buf_entry->buffer.iova -
+			cam->ctxs[session->session_id].buf_pool.working_buf_iova;
+		frame_data->cur_workbuf_size = buf_entry->buffer.size;
+
+		if (ctx->pipe->res_config.bin_limit == BIN_AUTO)
+			frame_data->raw_param.bin_flag = ctx->pipe->res_config.bin_enable;
+		else
+			frame_data->raw_param.bin_flag = ctx->pipe->res_config.bin_limit;
+
+		scp_ipi_send(cam->scp, SCP_IPI_ISP_FRAME, &event,
+			     sizeof(event), MTK_CAM_IPI_SEND_TIMEOUT);
+		buf->state.estate = E_BUF_STATE_COMPOSED;
+	}
+EXIT:
+	kfree(frame_param);
+}
+
+static void isp_tx_frame_worker(struct work_struct *work)
+{
+	struct mtk_cam_req_work *req_work = (struct mtk_cam_req_work *)work;
+	struct mtkcam_ipi_event event;
+	struct mtkcam_ipi_session_cookie *session = &event.cookie;
+	struct mtkcam_ipi_frame_info *frame_info = &event.frame_data;
+	struct mtkcam_ipi_frame_param *frame_data;
+	struct mtk_cam_request *req;
+	struct mtk_cam_request_stream_data *req_stream_data;
+	struct mtk_cam_ctx *ctx;
+	struct mtk_cam_device *cam;
+	struct mtk_cam_working_buf_entry *buf_entry;
+	struct mtk_cam_resource *res_user;
+	struct mtk_cam_req_raw_pipe_data *s_raw_pipe_data;
+
+	req_stream_data = mtk_cam_req_work_get_s_data(req_work);
+	if (!req_stream_data) {
+		pr_info("%s mtk_cam_req_work(%p), req_stream_data(%p), dropped\n",
+			__func__, req_work, req_stream_data);
+		return;
+	}
+	req = mtk_cam_s_data_get_req(req_stream_data);
+	if (!req) {
+		pr_info("%s req_stream_data(%p), req(%p), dropped\n",
+			__func__, req_stream_data, req);
+		return;
+	}
+	ctx = mtk_cam_s_data_get_ctx(req_stream_data);
+	if (!ctx) {
+		pr_info("%s req_stream_data(%p), ctx(%p), dropped\n",
+			__func__, req_stream_data, ctx);
+		return;
+	}
+
+	cam = ctx->cam;
+	if (ctx->used_raw_num == 0) {
+		dev_dbg(cam->dev, "raw is un-used, skip frame work");
+		return;
+	}
+	/* check if the ctx is streaming */
+	spin_lock(&ctx->streaming_lock);
+	if (!ctx->streaming) {
+		dev_info(cam->dev,
+			 "%s: skip frame work, for stream off ctx:%d, req:%d\n",
+			 __func__, ctx->stream_id, req_stream_data->frame_seq_no);
+		spin_unlock(&ctx->streaming_lock);
+		return;
+	}
+	spin_unlock(&ctx->streaming_lock);
+
+	s_raw_pipe_data = mtk_cam_s_data_get_raw_pipe_data(req_stream_data);
+
+	/* Send CAM_CMD_CONFIG if the sink pad fmt is changed */
+	if (req_stream_data->flags & MTK_CAM_REQ_S_DATA_FLAG_SINK_FMT_UPDATE)
+		mtk_cam_s_data_dev_config(req_stream_data);
+
+	if (!s_raw_pipe_data) {
+		dev_info(cam->dev, "error: %s: s_raw_pipe_data = NULL\n", __func__);
+		return;
+	}
+
+	memset(&event, 0, sizeof(event));
+	event.cmd_id = CAM_CMD_FRAME;
+	session->session_id = ctx->stream_id;
+	/* prepare working buffer */
+	buf_entry = mtk_cam_working_buf_get(ctx);
+	if (!buf_entry) {
+		dev_info(cam->dev, "%s: No CQ buf availablle: req:%d(%s)\n",
+			 __func__, req_stream_data->frame_seq_no,
+			 req->req.debug_str);
+		WARN_ON(1);
+		return;
+	}
+	mtk_cam_s_data_set_wbuf(req_stream_data, buf_entry);
+	/* put to using list */
+	spin_lock(&ctx->using_buffer_list.lock);
+	list_add_tail(&buf_entry->list_entry, &ctx->using_buffer_list.list);
+	ctx->using_buffer_list.cnt++;
+	spin_unlock(&ctx->using_buffer_list.lock);
+
+	/* Prepare rp message */
+	frame_info->cur_msgbuf_offset =
+		buf_entry->msg_buffer.va -
+		cam->ctxs[session->session_id].buf_pool.msg_buf_va;
+	frame_info->cur_msgbuf_size = buf_entry->msg_buffer.size;
+	frame_data = (struct mtkcam_ipi_frame_param *)buf_entry->msg_buffer.va;
+	session->frame_no = req_stream_data->frame_seq_no;
+
+	memcpy(frame_data, &req_stream_data->frame_params,
+	       sizeof(req_stream_data->frame_params));
+	frame_data->cur_workbuf_offset =
+		buf_entry->buffer.iova -
+		cam->ctxs[session->session_id].buf_pool.working_buf_iova;
+	frame_data->cur_workbuf_size = buf_entry->buffer.size;
+
+	res_user = mtk_cam_s_data_get_res(req_stream_data);
+	if (res_user && res_user->raw_res.bin) {
+		frame_data->raw_param.bin_flag = res_user->raw_res.bin;
+	} else {
+		if (ctx->pipe->res_config.bin_limit == BIN_AUTO)
+			frame_data->raw_param.bin_flag = ctx->pipe->res_config.bin_enable;
+		else
+			frame_data->raw_param.bin_flag = ctx->pipe->res_config.bin_limit;
+	}
+
+	scp_ipi_send(cam->scp, SCP_IPI_ISP_FRAME, &event,
+		     sizeof(event), MTK_CAM_IPI_SEND_TIMEOUT);
+	dev_dbg(cam->dev,
+		"%s: IPI send id: %d, ctx:%d, seq:%d, bin:(0x%x)\n",
+		req->req.debug_str, event.cmd_id, session->session_id,
+		req_stream_data->frame_seq_no,
+		frame_data->raw_param.bin_flag);
+}
+
+static void mtk_cam_dev_summit_sensor_work(struct mtk_cam_ctx *ctx,
+					   struct mtk_camsys_sensor_ctrl *sensor_ctrl)
+{
+	unsigned int drained_seq_no = 0;
+
+	if (ctx->pipe->feature_active == 0 && ctx->dequeued_frame_seq_no > 3) {
+		drained_seq_no = atomic_read(&sensor_ctrl->last_drained_seq_no);
+		if (atomic_read(&sensor_ctrl->sensor_enq_seq_no) == drained_seq_no)
+			mtk_cam_submit_kwork_in_sensorctrl(sensor_ctrl->sensorsetting_wq,
+							   sensor_ctrl);
+	}
+}
+
+void mtk_cam_dev_req_enqueue(struct mtk_cam_device *cam,
+			     struct mtk_cam_request *req)
+{
+	unsigned int i, j;
+
+	for (i = 0; i < cam->max_stream_num; i++) {
+		if (req->pipe_used & (1 << i))  {
+			unsigned int stream_id = i;
+			struct mtk_cam_req_work *frame_work, *done_work;
+			struct mtk_cam_request_stream_data *req_stream_data;
+			struct mtk_cam_request_stream_data *pipe_stream_data;
+			struct mtk_cam_ctx *ctx = &cam->ctxs[stream_id];
+			struct mtk_camsys_sensor_ctrl *sensor_ctrl = &ctx->sensor_ctrl;
+			unsigned int initial_frame = 0;
+
+			if (!ctx->streaming)
+				continue;
+
+			atomic_set(&ctx->sensor_ctrl.sensor_enq_seq_no,
+				   atomic_read(&ctx->enqueued_frame_seq_no));
+			/* sensor setting after request drained check */
+			if (ctx->used_raw_num)
+				mtk_cam_dev_summit_sensor_work(ctx, sensor_ctrl);
+			req_stream_data = mtk_cam_req_get_s_data(req, stream_id, 0);
+
+			if (req_stream_data->frame_seq_no == 1)
+				initial_frame = 1;
+			frame_work = &req_stream_data->frame_work;
+			mtk_cam_req_work_init(frame_work, req_stream_data);
+
+			for (j = 0 ; j < MTKCAM_SUBDEV_MAX ; j++) {
+				if ((1 << j & ctx->streaming_pipe) &&
+				    (1 << j & req->pipe_used)) {
+					pipe_stream_data = mtk_cam_req_get_s_data(req, j, 0);
+					done_work = &pipe_stream_data->frame_done_work;
+					INIT_WORK(&done_work->work, mtk_cam_frame_done_work);
+
+					done_work = &pipe_stream_data->meta1_done_work;
+					atomic_set(&done_work->is_queued, 0);
+					INIT_WORK(&done_work->work, mtk_cam_meta1_done_work);
+				}
+			}
+
+			if (ctx->sensor && (initial_frame || mtk_cam_is_m2m(ctx)))
+				mtk_cam_initial_sensor_setup(req, ctx);
+
+			if (ctx->used_raw_num != 0) {
+				if (ctx->sensor &&
+				    ctx->pipe->feature_active == 0 &&
+				    req_stream_data->frame_seq_no == 2)
+					mtk_cam_initial_sensor_setup(req, ctx);
+			} else {
+				if (ctx->sensor &&
+				    req_stream_data->frame_seq_no == 2)
+					mtk_cam_initial_sensor_setup(req, ctx);
+			}
+
+			/* Prepare CQ compose work */
+			mtk_cam_req_dump_work_init(req_stream_data);
+			INIT_WORK(&frame_work->work, isp_tx_frame_worker);
+			queue_work(ctx->composer_wq, &frame_work->work);
+
+			dev_dbg(cam->dev,
+				"%s:ctx:%d:req:%d(%s) enqueue ctx_used:0x%x,streaming_ctx:0x%x,job cnt:%d, running(%d)\n",
+				__func__, stream_id, req_stream_data->frame_seq_no,
+				req->req.debug_str, req->ctx_used,
+				cam->streaming_ctx, cam->running_job_count,
+				atomic_read(&ctx->running_s_data_cnt));
+		}
+	}
+}
+
+struct mtk_raw_pipeline *
+mtk_cam_dev_get_raw_pipeline(struct mtk_cam_device *cam, unsigned int pipe_id)
+{
+	if (pipe_id < MTKCAM_SUBDEV_RAW_START ||
+	    pipe_id >= MTKCAM_SUBDEV_RAW_START + cam->num_raw_devices)
+		return NULL;
+	else
+		return &cam->raw.pipelines[pipe_id - MTKCAM_SUBDEV_RAW_0];
+}
+
+static int
+mtk_cam_raw_pipeline_config(struct mtk_cam_ctx *ctx,
+			    struct mtkcam_ipi_input_param *cfg_in_param)
+{
+	struct mtk_raw_pipeline *pipe = ctx->pipe;
+	struct mtk_raw *raw = pipe->raw;
+	int ret, i;
+
+	/* reset pm_runtime during streaming dynamic change */
+	if (ctx->streaming) {
+		for (i = 0; i < ARRAY_SIZE(raw->devs); i++) {
+			if (pipe->enabled_raw & 1 << i) {
+				dev_info(raw->cam_dev,
+					 "%s: power off raw (%d) for reset\n",
+					 __func__, i);
+				pm_runtime_put_sync(raw->devs[i]);
+			}
+		}
+	}
+
+	ret = mtk_cam_raw_select(ctx, cfg_in_param);
+	if (ret) {
+		dev_info(raw->cam_dev, "failed select raw: %d\n",
+			 ctx->stream_id);
+		return ret;
+	}
+
+	for (i = 0; i < ARRAY_SIZE(raw->devs); i++) {
+		if (pipe->enabled_raw & 1 << i) {
+			dev_info(raw->cam_dev, "%s: power on raw (%d)\n",
+				 __func__, i);
+			ret = pm_runtime_get_sync(raw->devs[i]);
+
+			if (ret < 0) {
+				dev_info(raw->cam_dev,
+					 "failed at pm_runtime_get_sync: %s\n",
+					 dev_driver_string(raw->devs[i]));
+
+				/* put devices already get */
+				for (; i >= 0; i--) {
+					pm_runtime_put_sync(raw->devs[i]);
+					dev_info(raw->cam_dev,
+						 "%s: power off raw (%d)\n",
+						 __func__, i);
+				}
+
+				return ret;
+			}
+		}
+	}
+
+	ctx->used_raw_dev = pipe->enabled_raw;
+	dev_info(raw->cam_dev, "ctx_id %d used_raw_dev 0x%x pipe_id %d\n",
+		 ctx->stream_id, ctx->used_raw_dev, pipe->id);
+	return 0;
+}
+
+void mtk_cam_apply_pending_dev_config(struct mtk_cam_request_stream_data *s_data)
+{
+	struct mtk_cam_req_raw_pipe_data *s_raw_pipe_data;
+	struct mtk_cam_ctx *ctx;
+	char *debug_str = mtk_cam_s_data_get_dbg_str(s_data);
+
+	s_raw_pipe_data = mtk_cam_s_data_get_raw_pipe_data(s_data);
+	if (!s_raw_pipe_data)
+		return;
+
+	ctx = mtk_cam_s_data_get_ctx(s_data);
+	if (!ctx)
+		return;
+	ctx->pipe->feature_active = ctx->pipe->user_res.raw_res.feature;
+	ctx->pipe->enabled_raw = s_raw_pipe_data->enabled_raw;
+	ctx->used_raw_dev = s_raw_pipe_data->enabled_raw;
+
+	dev_info(ctx->cam->dev,
+		 "%s:%s:pipe(%d):seq(%d):feature_active(0x%llx), ctx->pipe->user_res.raw_res.feature(%lld), enabled_raw(0x%x)\n",
+		 __func__, debug_str, ctx->stream_id, s_data->frame_seq_no,
+		 ctx->pipe->feature_active,
+		 ctx->pipe->user_res.raw_res.feature,
+		 ctx->pipe->enabled_raw);
+}
+
+static int mtk_cam_dev_config(struct mtk_cam_ctx *ctx)
+{
+	struct mtk_cam_device *cam = ctx->cam;
+	struct device *dev = cam->dev;
+	struct mtkcam_ipi_config_param config_param;
+	struct mtkcam_ipi_input_param *cfg_in_param;
+	struct mtk_raw_pipeline *pipe = ctx->pipe;
+	struct mtk_raw *raw = pipe->raw;
+	struct v4l2_mbus_framefmt *mf = &pipe->cfg[MTK_RAW_SINK].mbus_fmt;
+	struct device *dev_raw;
+	struct mtk_raw_device *raw_dev;
+	unsigned int i;
+	int ret;
+	u32 mf_code;
+
+	/**
+	 * If don't want to get the first req's raw_feature (not the max exp. num),
+	 * you can use read ctx->pipe->feature_pending here.
+	 */
+
+	memset(&config_param, 0, sizeof(config_param));
+
+	/* Update cfg_in_param */
+	cfg_in_param = &config_param.input;
+	cfg_in_param->pixel_mode = ctx->pipe->res_config.tgo_pxl_mode;
+
+	cfg_in_param->data_pattern = MTKCAM_IPI_SENSOR_PATTERN_NORMAL;
+	cfg_in_param->in_crop.s.w = mf->width;
+	cfg_in_param->in_crop.s.h = mf->height;
+
+	if (mtk_cam_is_pure_m2m(ctx)) {
+		mf = &pipe->cfg[MTK_RAW_RAWI_2_IN].mbus_fmt;
+		dev_dbg(dev, "[pure m2m] rawi2 pad code:0x%x, sink tg size:%d %d\n",
+			mf->code, cfg_in_param->in_crop.s.w, cfg_in_param->in_crop.s.h);
+	} else {
+		dev_dbg(dev, "sink pad code:0x%x, tg size:%d %d\n", mf->code,
+			cfg_in_param->in_crop.s.w, cfg_in_param->in_crop.s.h);
+	}
+
+	mf_code = mf->code & 0xffff; /* sensor mode */
+	cfg_in_param->raw_pixel_id = mtk_cam_get_sensor_pixel_id(mf_code);
+	cfg_in_param->fmt = mtk_cam_get_sensor_fmt(mf_code);
+	if (cfg_in_param->fmt == MTKCAM_IPI_IMG_FMT_UNKNOWN ||
+	    cfg_in_param->raw_pixel_id == MTKCAM_IPI_BAYER_PXL_ID_UNKNOWN) {
+		dev_err(dev, "unknown sd code:%d\n", mf_code);
+		return -EINVAL;
+	}
+
+	config_param.flags = MTK_CAM_IPI_CONFIG_TYPE_INIT;
+	ret = mtk_cam_raw_pipeline_config(ctx, cfg_in_param);
+	if (ret)
+		return ret;
+
+	dev_dbg(dev, "%s: config_param flag:0x%x enabled_raw:0x%x\n",
+		__func__, config_param.flags, ctx->pipe->enabled_raw);
+
+	update_hw_mapping(ctx, &config_param);
+	config_param.sw_feature = MTKCAM_IPI_SW_FEATURE_NORMAL;
+
+	dev_raw = mtk_cam_find_raw_dev(cam, ctx->used_raw_dev);
+	if (!dev_raw) {
+		dev_err(dev, "config raw device not found\n");
+		return -EINVAL;
+	}
+	raw_dev = dev_get_drvdata(dev_raw);
+	for (i = 0; i < RAW_PIPELINE_NUM; i++)
+		if (raw->pipelines[i].enabled_raw & 1 << raw_dev->id) {
+			raw_dev->pipeline = &raw->pipelines[i];
+			if (raw->pipelines[i].res_config.raw_num_used == 0)
+				mtk_cam_res_init(&raw->pipelines[i].res_config);
+			break;
+		}
+
+	mtk_cam_raw_reset(raw_dev);
+	isp_composer_hw_config(cam, ctx, &config_param);
+	dev_dbg(dev, "raw %d %s done\n", raw_dev->id, __func__);
+
+	return 0;
+}
+
+static int __maybe_unused mtk_cam_runtime_suspend(struct device *dev)
+{
+	dev_dbg(dev, "- %s\n", __func__);
+	return 0;
+}
+
+static int __maybe_unused mtk_cam_runtime_resume(struct device *dev)
+{
+	dev_dbg(dev, "- %s\n", __func__);
+	return 0;
+}
+
+static void mtk_cam_ctx_watchdog_worker(struct work_struct *work)
+{
+	struct mtk_cam_ctx *ctx;
+	struct mtk_raw_device *raw;
+	struct v4l2_subdev *seninf;
+	static u64 last_vsync_count;
+
+	ctx = container_of(work, struct mtk_cam_ctx, watchdog_work);
+
+	/* dump cam-raw */
+	raw = get_main_raw_dev(ctx->cam, ctx->pipe);
+	if (!raw)
+		goto EXIT;
+	dev_info(ctx->cam->dev,
+		 "%s:ctx(%d):[timeout] VF(%d) vsync count(%lld) sof count(%lld) timeout_tg(%d)(>%dms)\n",
+		 __func__, ctx->stream_id, atomic_read(&raw->vf_en),
+		 raw->vsync_count, raw->sof_count, ctx->watchdog_timeout_tg,
+		 ctx->watchdog_timeout_tg * MTK_CAM_CTX_WATCHDOG_INTERVAL);
+
+	if (last_vsync_count == raw->vsync_count)
+		dev_err(ctx->cam->dev, "%s:cam-raw abnormal vsync\n", __func__);
+	last_vsync_count = raw->vsync_count;
+
+	/* dump seninf */
+	seninf = ctx->seninf;
+	if (seninf)
+		mtk_cam_seninf_dump(seninf);
+
+EXIT:
+	atomic_inc(&ctx->watchdog_dump);
+}
+
+#define WDT_DUMP_CNT 4
+
+static void mtk_ctx_watchdog_callback(struct timer_list *t)
+{
+	struct mtk_cam_ctx *ctx = from_timer(ctx, t, watchdog_timer);
+
+	/* disable if not streaming */
+	if (!ctx->streaming)
+		return;
+
+	if (atomic_read(&ctx->watchdog_dump) < WDT_DUMP_CNT)
+		schedule_work(&ctx->watchdog_work);
+
+	dev_info_ratelimited(ctx->cam->dev,
+			     "%s:ctx(%d):[TIMEOUT] no_sync_tg:%d(>%dms)\n",
+			     __func__, ctx->stream_id, ctx->watchdog_timeout_tg,
+			     jiffies_to_msecs(jiffies -
+					atomic_long_read(&ctx->watchdog_prev)));
+
+	/* update timer */
+	mod_timer(&ctx->watchdog_timer,
+		  jiffies + msecs_to_jiffies(MTK_CAM_CTX_WATCHDOG_INTERVAL));
+}
+
+void mtk_ctx_watchdog_kick(struct mtk_cam_ctx *ctx)
+{
+	dev_dbg(ctx->cam->dev, "%s:ctx(%d)\n", __func__, ctx->stream_id);
+	atomic_set(&ctx->watchdog_dump, 0);
+
+	/* delay timer */
+	mod_timer(&ctx->watchdog_timer,
+		  jiffies + msecs_to_jiffies(MTK_CAM_CTX_WATCHDOG_INTERVAL *
+					     ctx->watchdog_timeout_tg));
+	atomic_long_set(&ctx->watchdog_prev, jiffies);
+}
+
+static void mtk_ctx_watchdog_init(struct mtk_cam_ctx *ctx)
+{
+	INIT_WORK(&ctx->watchdog_work, mtk_cam_ctx_watchdog_worker);
+	timer_setup(&ctx->watchdog_timer, mtk_ctx_watchdog_callback, 0);
+}
+
+static void mtk_ctx_watchdog_start(struct mtk_cam_ctx *ctx, int timeout_tg)
+{
+	dev_info(ctx->cam->dev,
+		 "%s:ctx(%d):start the watchdog, timeout setting(%dms)\n",
+		 __func__, ctx->stream_id,
+		 MTK_CAM_CTX_WATCHDOG_INTERVAL * timeout_tg);
+
+	ctx->watchdog_timeout_tg = timeout_tg;
+	atomic_set(&ctx->watchdog_dump, 0);
+
+	mod_timer(&ctx->watchdog_timer,
+		  jiffies + msecs_to_jiffies(MTK_CAM_CTX_WATCHDOG_INTERVAL *
+					     ctx->watchdog_timeout_tg));
+	atomic_long_set(&ctx->watchdog_prev, jiffies);
+}
+
+static void mtk_ctx_watchdog_stop(struct mtk_cam_ctx *ctx)
+{
+	dev_info(ctx->cam->dev, "%s:ctx(%d):stop the watchdog\n",
+		 __func__, ctx->stream_id);
+	del_timer_sync(&ctx->watchdog_timer);
+}
+
+struct mtk_cam_ctx *mtk_cam_find_ctx(struct mtk_cam_device *cam,
+				     struct media_entity *entity)
+{
+	unsigned int i;
+
+	for (i = 0;  i < cam->max_stream_num; i++) {
+		if (media_entity_pipeline(entity) == &cam->ctxs[i].pipeline)
+			return &cam->ctxs[i];
+	}
+
+	return NULL;
+}
+
+struct mtk_cam_ctx *mtk_cam_start_ctx(struct mtk_cam_device *cam,
+				      struct mtk_cam_video_device *node)
+{
+	struct mtk_cam_ctx *ctx = node->ctx;
+	struct device *dev;
+	struct v4l2_subdev **target_sd;
+	int ret, i, is_first_ctx;
+	struct media_entity *entity = &node->vdev.entity;
+	struct media_graph graph;
+
+	dev_info(cam->dev, "%s:ctx(%d): triggered by %s\n",
+		 __func__, ctx->stream_id, entity->name);
+
+	atomic_set(&ctx->enqueued_frame_seq_no, 0);
+	ctx->composed_frame_seq_no = 0;
+	ctx->dequeued_frame_seq_no = 0;
+	atomic_set(&ctx->running_s_data_cnt, 0);
+	init_completion(&ctx->session_complete);
+	init_completion(&ctx->m2m_complete);
+
+	is_first_ctx = !cam->composer_cnt;
+	if (is_first_ctx) {
+		spin_lock(&cam->dma_processing_lock);
+		cam->dma_processing_count = 0;
+		spin_unlock(&cam->dma_processing_lock);
+
+		spin_lock(&cam->running_job_lock);
+		cam->running_job_count = 0;
+		spin_unlock(&cam->running_job_lock);
+
+		dev_info(cam->dev, "%s: power on camsys\n", __func__);
+		ret = pm_runtime_resume_and_get(cam->dev);
+		if (ret < 0) {
+			dev_info(cam->dev, "%s: power on camsys failed\n",
+				 __func__);
+			return NULL;
+		}
+
+		ret = isp_composer_init(cam);
+		if (ret)
+			goto fail_shutdown;
+
+		/* To catch camsys exception and trigger dump */
+		if (cam->debug_fs)
+			cam->debug_fs->ops->exp_reinit(cam->debug_fs);
+	}
+	cam->composer_cnt++;
+	if (is_yuv_node(node->desc.id))
+		dev = cam->raw.yuvs[0];
+	else
+		dev = cam->raw.devs[0];
+
+	ret = mtk_cam_working_buf_pool_init(ctx, dev);
+	if (ret) {
+		dev_info(cam->dev, "failed to reserve DMA memory:%d\n", ret);
+		goto fail_uninit_composer;
+	}
+
+	kthread_init_worker(&ctx->sensor_worker);
+	ctx->sensor_worker_task = kthread_run(kthread_worker_fn,
+					      &ctx->sensor_worker,
+					      "sensor_worker-%d",
+					      ctx->stream_id);
+	if (IS_ERR(ctx->sensor_worker_task)) {
+		dev_info(cam->dev,
+			 "%s:ctx(%d): could not create sensor_worker_task\n",
+			 __func__, ctx->stream_id);
+		goto fail_release_buffer_pool;
+	}
+
+	sched_set_fifo(ctx->sensor_worker_task);
+
+	ctx->composer_wq = alloc_ordered_workqueue(dev_name(cam->dev),
+						   WQ_HIGHPRI | WQ_FREEZABLE);
+	if (!ctx->composer_wq) {
+		dev_info(cam->dev, "failed to alloc composer workqueue\n");
+		goto fail_uninit_sensor_worker_task;
+	}
+
+	ctx->frame_done_wq = alloc_ordered_workqueue(dev_name(cam->dev),
+						     WQ_HIGHPRI | WQ_FREEZABLE);
+	if (!ctx->frame_done_wq) {
+		dev_info(cam->dev, "failed to alloc frame_done workqueue\n");
+		goto fail_uninit_composer_wq;
+	}
+
+	ret = media_pipeline_start(&entity->pads[0], &ctx->pipeline);
+	if (ret) {
+		dev_warn(cam->dev,
+			 "%s:pipe(%d):failed in media_pipeline_start:%d\n",
+			 __func__, node->uid.pipe_id, ret);
+		goto fail_uninit_frame_done_wq;
+	}
+
+	/* traverse to update used subdevs & number of nodes */
+	i = 0;
+	ret = media_graph_walk_init(&graph, entity->graph_obj.mdev);
+	if (ret)
+		goto fail_stop_pipeline;
+
+	media_graph_walk_start(&graph, entity);
+	while ((entity = media_graph_walk_next(&graph))) {
+		dev_dbg(cam->dev, "linked entity %s\n", entity->name);
+
+		target_sd = NULL;
+
+		switch (entity->function) {
+		case MEDIA_ENT_F_IO_V4L:
+			ctx->enabled_node_cnt++;
+			break;
+		case MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER: /* pipeline */
+			if (i >= MAX_PIPES_PER_STREAM)
+				goto fail_stop_pipeline;
+			target_sd = ctx->pipe_subdevs + i;
+			i++;
+			break;
+		case MEDIA_ENT_F_VID_IF_BRIDGE: /* seninf */
+			target_sd = &ctx->seninf;
+			break;
+		case MEDIA_ENT_F_CAM_SENSOR:
+			target_sd = &ctx->sensor;
+			break;
+		default:
+			break;
+		}
+
+		if (!target_sd)
+			continue;
+
+		if (*target_sd) {
+			dev_info(cam->dev, "duplicated subdevs!!!\n");
+			goto fail_traverse_subdev;
+		}
+
+		if (is_media_entity_v4l2_subdev(entity))
+			*target_sd = media_entity_to_v4l2_subdev(entity);
+	}
+	media_graph_walk_cleanup(&graph);
+
+	return ctx;
+
+fail_traverse_subdev:
+	media_graph_walk_cleanup(&graph);
+fail_stop_pipeline:
+	media_pipeline_stop(&entity->pads[0]);
+fail_uninit_frame_done_wq:
+	destroy_workqueue(ctx->frame_done_wq);
+fail_uninit_composer_wq:
+	destroy_workqueue(ctx->composer_wq);
+fail_uninit_sensor_worker_task:
+	kthread_stop(ctx->sensor_worker_task);
+	ctx->sensor_worker_task = NULL;
+fail_release_buffer_pool:
+	mtk_cam_working_buf_pool_release(ctx, dev);
+fail_uninit_composer:
+	isp_composer_uninit(cam);
+	cam->composer_cnt--;
+fail_shutdown:
+	if (is_first_ctx)
+		rproc_shutdown(cam->rproc_handle);
+
+	return NULL;
+}
+
+void mtk_cam_stop_ctx(struct mtk_cam_ctx *ctx, struct mtk_cam_video_device *node)
+{
+	struct mtk_cam_device *cam = ctx->cam;
+	struct media_entity *entity = &node->vdev.entity;
+	struct device *dev;
+	unsigned int i;
+
+	if (is_yuv_node(node->desc.id))
+		dev = cam->raw.yuvs[0];
+	else
+		dev = cam->raw.devs[0];
+
+	dev_info(cam->dev, "%s:ctx(%d): triggered by %s\n",
+		 __func__, ctx->stream_id, entity->name);
+
+	media_pipeline_stop(&entity->pads[0]);
+
+	if (ctx->session_created) {
+		dev_dbg(cam->dev,
+			"%s:ctx(%d): session_created, wait for composer session destroy\n",
+			__func__, ctx->stream_id);
+		if (wait_for_completion_timeout(&ctx->session_complete,
+						msecs_to_jiffies(300)) == 0)
+			dev_info(cam->dev, "%s:ctx(%d): complete timeout\n",
+				 __func__, ctx->stream_id);
+	}
+
+	/* For M2M feature, signal all waiters */
+	if (mtk_cam_is_m2m(ctx))
+		complete_all(&ctx->m2m_complete);
+
+	if (!cam->streaming_ctx) {
+		struct v4l2_subdev *sd;
+
+		v4l2_device_for_each_subdev(sd, &cam->v4l2_dev) {
+			if (sd->entity.function == MEDIA_ENT_F_VID_IF_BRIDGE) {
+				int ret;
+
+				ret = v4l2_subdev_call(sd, video, s_stream, 0);
+				if (ret)
+					dev_err(cam->dev,
+						"failed to streamoff %s:%d\n",
+						sd->name, ret);
+			}
+		}
+	}
+
+	drain_workqueue(ctx->composer_wq);
+	destroy_workqueue(ctx->composer_wq);
+	ctx->composer_wq = NULL;
+	drain_workqueue(ctx->frame_done_wq);
+	destroy_workqueue(ctx->frame_done_wq);
+	ctx->frame_done_wq = NULL;
+	kthread_flush_worker(&ctx->sensor_worker);
+	kthread_stop(ctx->sensor_worker_task);
+	ctx->sensor_worker_task = NULL;
+	ctx->session_created = 0;
+	ctx->enabled_node_cnt = 0;
+	ctx->streaming_node_cnt = 0;
+	ctx->streaming_pipe = 0;
+	ctx->sensor = NULL;
+	ctx->seninf = NULL;
+	atomic_set(&ctx->enqueued_frame_seq_no, 0);
+	ctx->composed_frame_seq_no = 0;
+	ctx->is_first_cq_done = 0;
+	ctx->cq_done_status = 0;
+	ctx->used_raw_num = 0;
+
+	INIT_LIST_HEAD(&ctx->using_buffer_list.list);
+	INIT_LIST_HEAD(&ctx->composed_buffer_list.list);
+	INIT_LIST_HEAD(&ctx->processing_buffer_list.list);
+
+	INIT_LIST_HEAD(&ctx->processing_img_buffer_list.list);
+	for (i = 0; i < MAX_PIPES_PER_STREAM; i++)
+		ctx->pipe_subdevs[i] = NULL;
+
+	isp_composer_uninit(cam);
+		cam->composer_cnt--;
+
+	dev_info(cam->dev, "%s: ctx-%d:  composer_cnt:%d\n",
+		 __func__, ctx->stream_id, cam->composer_cnt);
+
+	mtk_cam_working_buf_pool_release(ctx, dev);
+
+	if (ctx->cam->rproc_handle && !ctx->cam->composer_cnt) {
+		dev_info(cam->dev, "%s power off camsys\n", __func__);
+		pm_runtime_put_sync(cam->dev);
+		rproc_shutdown(cam->rproc_handle);
+	}
+}
+
+static int pipeid_to_tgidx(int pipe_id)
+{
+	switch (pipe_id) {
+	case MTKCAM_SUBDEV_RAW_0:
+		return 0;
+	case MTKCAM_SUBDEV_RAW_1:
+		return 1;
+	case MTKCAM_SUBDEV_RAW_2:
+		return 2;
+	default:
+		break;
+	}
+	return -1;
+}
+
+int mtk_cam_call_seninf_set_pixelmode(struct mtk_cam_ctx *ctx,
+				      struct v4l2_subdev *sd,
+				      int pad_id, int pixel_mode)
+{
+	int ret;
+
+	ret = mtk_cam_seninf_set_pixelmode(sd, pad_id, pixel_mode);
+	dev_dbg(ctx->cam->dev,
+		"%s:ctx(%d): seninf(%s): pad(%d), pixel_mode(%d)\n, ret(%d)",
+		__func__, ctx->stream_id, sd->name, pad_id, pixel_mode,
+		ret);
+
+	return ret;
+}
+
+int mtk_cam_ctx_stream_on(struct mtk_cam_ctx *ctx, struct mtk_cam_video_device *node)
+{
+	struct mtk_cam_device *cam = ctx->cam;
+	struct device *dev;
+	struct mtk_raw_device *raw_dev;
+	int i, ret;
+	int tgo_pxl_mode;
+	bool need_dump_mem = false;
+	unsigned int streaming_ctx_latch;
+
+	dev_dbg(cam->dev, "ctx %d stream on, streaming_pipe:0x%x\n",
+		ctx->stream_id, ctx->streaming_pipe);
+
+	if (ctx->streaming) {
+		dev_dbg(cam->dev, "ctx-%d is already streaming on\n", ctx->stream_id);
+		return 0;
+	}
+
+	for (i = 0; i < MAX_PIPES_PER_STREAM && ctx->pipe_subdevs[i]; i++) {
+		ret = v4l2_subdev_call(ctx->pipe_subdevs[i], video,
+				       s_stream, 1);
+		if (ret) {
+			dev_info(cam->dev, "failed to stream on %s: %d\n",
+				 ctx->pipe_subdevs[i]->name, ret);
+			goto fail_pipe_off;
+		}
+	}
+
+	if (ctx->used_raw_num) {
+		tgo_pxl_mode = ctx->pipe->res_config.tgo_pxl_mode;
+
+		ret = mtk_cam_dev_config(ctx);
+		if (ret)
+			goto fail_pipe_off;
+		dev = mtk_cam_find_raw_dev(cam, ctx->used_raw_dev);
+		if (!dev) {
+			dev_info(cam->dev, "streamon raw device not found\n");
+			goto fail_pipe_off;
+		}
+		raw_dev = dev_get_drvdata(dev);
+
+		if (!mtk_cam_is_m2m(ctx)) {
+			mtk_cam_call_seninf_set_pixelmode(ctx, ctx->seninf,
+							  PAD_SRC_RAW0,
+							  tgo_pxl_mode);
+			mtk_cam_seninf_set_camtg(ctx->seninf, PAD_SRC_RAW0,
+						 pipeid_to_tgidx(raw_dev->id));
+		}
+	}
+
+	if (!mtk_cam_is_m2m(ctx)) {
+		ret = v4l2_subdev_call(ctx->seninf, video, s_stream, 1);
+		if (ret) {
+			dev_info(cam->dev, "failed to stream on seninf %s:%d\n",
+				 ctx->seninf->name, ret);
+			goto fail_pipe_off;
+		}
+	} else {
+		ctx->processing_buffer_list.cnt = 0;
+		ctx->composed_buffer_list.cnt = 0;
+		dev_dbg(cam->dev,
+			"[M2M] reset processing_buffer_list.cnt & composed_buffer_list.cnt\n");
+	}
+
+	if (ctx->used_raw_num) {
+		mtk_cam_raw_initialize(raw_dev, 0);
+		/* Twin */
+		if (ctx->pipe->res_config.raw_num_used != 1) {
+			struct mtk_raw_device *raw_dev_sub =
+				get_sub_raw_dev(cam, ctx->pipe);
+			mtk_cam_raw_initialize(raw_dev_sub, 1);
+
+			if (ctx->pipe->res_config.raw_num_used == 3) {
+				struct mtk_raw_device *raw_dev_sub2 =
+					get_sub2_raw_dev(cam, ctx->pipe);
+				mtk_cam_raw_initialize(raw_dev_sub2, 1);
+			}
+		}
+	}
+
+	spin_lock(&ctx->streaming_lock);
+	if (!cam->streaming_ctx && cam->debug_fs)
+		need_dump_mem = true;
+
+	streaming_ctx_latch = cam->streaming_ctx;
+	ctx->streaming = true;
+	cam->streaming_ctx |= 1 << ctx->stream_id;
+	spin_unlock(&ctx->streaming_lock);
+
+	if (need_dump_mem)
+		cam->debug_fs->ops->reinit(cam->debug_fs, ctx->stream_id);
+	else
+		dev_dbg(cam->dev,
+			"No need to alloc mem for ctx: streaming_ctx(0x%x), debug_fs(%p)\n",
+			streaming_ctx_latch, cam->debug_fs);
+	ret = mtk_camsys_ctrl_start(ctx);
+	if (ret)
+		goto fail_streaming_off;
+
+	mutex_lock(&cam->queue_lock);
+	mtk_cam_dev_req_try_queue(cam);  /* request moved into working list */
+	mutex_unlock(&cam->queue_lock);
+
+	if (watchdog_scenario(ctx))
+		mtk_ctx_watchdog_start(ctx, 4);
+
+	dev_dbg(cam->dev, "streamed on camsys ctx:%d\n", ctx->stream_id);
+
+	return 0;
+
+fail_streaming_off:
+	spin_lock(&ctx->streaming_lock);
+	ctx->streaming = false;
+	cam->streaming_ctx &= ~(1 << ctx->stream_id);
+	spin_unlock(&ctx->streaming_lock);
+
+	if (!mtk_cam_is_m2m(ctx))
+		v4l2_subdev_call(ctx->seninf, video, s_stream, 0);
+fail_pipe_off:
+	for (i = 0; i < MAX_PIPES_PER_STREAM && ctx->pipe_subdevs[i]; i++)
+		v4l2_subdev_call(ctx->pipe_subdevs[i], video, s_stream, 0);
+
+	return ret;
+}
+
+int mtk_cam_ctx_stream_off(struct mtk_cam_ctx *ctx, struct mtk_cam_video_device *node)
+{
+	struct mtk_cam_device *cam = ctx->cam;
+	struct device *dev;
+	struct mtk_raw_device *raw_dev;
+	unsigned int i;
+	int ret;
+
+	if (!ctx->streaming) {
+		dev_dbg(cam->dev, "ctx-%d is already streaming off\n",
+			ctx->stream_id);
+		return 0;
+	}
+
+	if (watchdog_scenario(ctx))
+		mtk_ctx_watchdog_stop(ctx);
+
+	dev_info(cam->dev, "%s: ctx-%d:  composer_cnt:%d, streaming_pipe:0x%x\n",
+		 __func__, ctx->stream_id, cam->composer_cnt, ctx->streaming_pipe);
+
+	spin_lock(&ctx->streaming_lock);
+	ctx->streaming = false;
+	cam->streaming_ctx &= ~(1 << ctx->stream_id);
+	spin_unlock(&ctx->streaming_lock);
+
+	if (ctx->synced)
+		ctx->synced = 0;
+
+	if (!mtk_cam_is_m2m(ctx)) {
+		ret = v4l2_subdev_call(ctx->seninf, video, s_stream, 0);
+		if (ret) {
+			dev_err(cam->dev, "failed to stream off %s:%d\n",
+				ctx->seninf->name, ret);
+			return -EPERM;
+		}
+	}
+
+	if (ctx->used_raw_num) {
+		dev = mtk_cam_find_raw_dev(cam, ctx->used_raw_dev);
+		if (!dev) {
+			dev_info(cam->dev, "streamoff raw device not found\n");
+			goto fail_stream_off;
+		}
+		raw_dev = dev_get_drvdata(dev);
+		mtk_cam_raw_stream_on(raw_dev, 0);
+		/* Twin */
+		if (ctx->pipe->res_config.raw_num_used != 1) {
+			struct mtk_raw_device *raw_dev_sub =
+						get_sub_raw_dev(cam, ctx->pipe);
+			mtk_cam_raw_stream_on(raw_dev_sub, 0);
+
+			if (ctx->pipe->res_config.raw_num_used == 3) {
+				struct mtk_raw_device *raw_dev_sub2 =
+					get_sub2_raw_dev(cam, ctx->pipe);
+				mtk_cam_raw_stream_on(raw_dev_sub2, 0);
+			}
+		}
+	}
+
+	for (i = 0; i < MAX_PIPES_PER_STREAM && ctx->pipe_subdevs[i]; i++) {
+		ret = v4l2_subdev_call(ctx->pipe_subdevs[i], video, s_stream, 0);
+		if (ret) {
+			dev_err(cam->dev, "failed to stream off %s: %d\n",
+				ctx->pipe_subdevs[i]->name, ret);
+			return -EPERM;
+		}
+	}
+
+	if (ctx->img_buf_pool.working_img_buf_size > 0) {
+		if (is_yuv_node(node->desc.id))
+			dev = cam->raw.yuvs[0];
+		else
+			dev = cam->raw.devs[0];
+
+		mtk_cam_img_working_buf_pool_release(ctx, dev);
+	}
+
+	mtk_camsys_ctrl_stop(ctx);
+
+fail_stream_off:
+	if (ctx->used_raw_num)
+		isp_composer_destroy_session(ctx);
+
+	dev_dbg(cam->dev, "streamed off camsys ctx:%d\n", ctx->stream_id);
+
+	return 0;
+}
+
+static int config_bridge_pad_links(struct mtk_cam_device *cam,
+				   struct v4l2_subdev *seninf)
+{
+	struct media_entity *pipe_entity;
+	unsigned int i;
+	int ret;
+
+	for (i = MTKCAM_SUBDEV_RAW_START;
+	     i < MTKCAM_SUBDEV_RAW_START + cam->num_raw_devices; i++) {
+		pipe_entity = &cam->raw.pipelines[i].subdev.entity;
+
+		dev_info(cam->dev, "create pad link %s %s\n",
+			 seninf->entity.name, pipe_entity->name);
+
+		ret = media_create_pad_link(&seninf->entity,
+					    MTK_CAM_CIO_PAD_SRC,
+					    pipe_entity,
+					    MTK_CAM_CIO_PAD_SINK,
+					    MEDIA_LNK_FL_DYNAMIC);
+
+		if (ret) {
+			dev_warn(cam->dev,
+				 "failed to create pad link %s %s err:%d\n",
+				 seninf->entity.name, pipe_entity->name,
+				 ret);
+			return ret;
+		}
+	}
+
+	return 0;
+}
+
+static int mtk_cam_create_links(struct mtk_cam_device *cam)
+{
+	struct v4l2_subdev *sd;
+	int ret = 0;
+
+	v4l2_device_for_each_subdev(sd, &cam->v4l2_dev) {
+		if (sd->entity.function == MEDIA_ENT_F_VID_IF_BRIDGE)
+			ret = config_bridge_pad_links(cam, sd);
+	}
+
+	return ret;
+}
+
+static int mtk_cam_master_register(struct device *dev)
+{
+	struct mtk_cam_device *cam_dev = dev_get_drvdata(dev);
+	struct media_device *media_dev = &cam_dev->media_dev;
+	int ret;
+
+	dev_info(dev, "camsys | start %s\n", __func__);
+
+	media_dev->dev = cam_dev->dev;
+	strscpy(media_dev->model, dev_driver_string(dev),
+		sizeof(media_dev->model));
+	snprintf(media_dev->bus_info, sizeof(media_dev->bus_info),
+		 "platform:%s", dev_name(dev));
+	media_dev->hw_revision = 0;
+	media_dev->ops = &mtk_cam_dev_ops;
+	media_device_init(media_dev);
+
+	cam_dev->v4l2_dev.mdev = media_dev;
+	ret = v4l2_device_register(cam_dev->dev, &cam_dev->v4l2_dev);
+	if (ret) {
+		dev_dbg(dev, "Failed to register V4L2 device: %d\n", ret);
+		goto fail_media_device_cleanup;
+	}
+
+	ret = media_device_register(media_dev);
+	if (ret) {
+		dev_dbg(dev, "Failed to register media device: %d\n",
+			ret);
+		goto fail_v4l2_device_unreg;
+	}
+
+	dev_info(dev, "%s success\n", __func__);
+	return 0;
+
+fail_v4l2_device_unreg:
+	v4l2_device_unregister(&cam_dev->v4l2_dev);
+
+fail_media_device_cleanup:
+	media_device_cleanup(&cam_dev->media_dev);
+
+	return ret;
+}
+
+static void mtk_cam_master_unregister(struct device *dev)
+{
+	struct mtk_cam_device *cam_dev = dev_get_drvdata(dev);
+
+	dev_info(dev, "camsys | start %s\n", __func__);
+
+	media_device_unregister(&cam_dev->media_dev);
+	v4l2_device_unregister(&cam_dev->v4l2_dev);
+	media_device_cleanup(&cam_dev->media_dev);
+}
+
+static int mtk_cam_async_add_by_driver(struct device *dev,
+				       struct platform_driver *drv,
+				       struct v4l2_async_notifier *notifier)
+{
+	struct fwnode_handle *fwnode;
+	struct device *p;
+	struct v4l2_async_connection *asc;
+	int dev_num = 0;
+
+	p = platform_find_device_by_driver(NULL, &drv->driver);
+	while (p) {
+		dev_info(dev, "camsys | %s add %s\n", __func__, p->kobj.name);
+
+		fwnode = dev_fwnode(p);
+		asc = v4l2_async_nf_add_fwnode(notifier, fwnode,
+					       struct v4l2_async_connection);
+		put_device(p);
+
+		if (IS_ERR(asc)) {
+			dev_info(dev, "%s add fwnode fail %ld\n", __func__,
+				 PTR_ERR(asc));
+
+			return PTR_ERR(asc);
+		}
+		++dev_num;
+
+		p = platform_find_device_by_driver(p, &drv->driver);
+	}
+
+	return dev_num;
+}
+
+static int mtk_cam_async_subdev_add(struct device *dev)
+{
+	struct mtk_cam_device *cam_dev = dev_get_drvdata(dev);
+	struct v4l2_async_notifier *notifier = &cam_dev->notifier;
+	int raw_num, yuv_num, seninf_num;
+
+	raw_num = mtk_cam_async_add_by_driver(dev, &mtk_cam_raw_driver,
+					      notifier);
+	yuv_num = mtk_cam_async_add_by_driver(dev, &mtk_cam_yuv_driver,
+					      notifier);
+	seninf_num = mtk_cam_async_add_by_driver(dev, &seninf_pdrv, notifier);
+
+	if (raw_num < 0 || yuv_num < 0 || seninf_num < 0) {
+		dev_err(dev, "%s failed\n", __func__);
+		return -ENODEV;
+	}
+
+	cam_dev->num_raw_devices = raw_num;
+	cam_dev->num_seninf_devices = seninf_num;
+	dev_info(dev, "dependent module #: raw %d, yuv %d, seninf %d\n",
+		 cam_dev->num_raw_devices, yuv_num,
+		 cam_dev->num_seninf_devices);
+
+	return 0;
+}
+
+static void mtk_cam_ctx_init(struct mtk_cam_ctx *ctx,
+			     struct mtk_cam_device *cam,
+			     unsigned int stream_id)
+{
+	ctx->cam = cam;
+	ctx->stream_id = stream_id;
+	ctx->sensor = NULL;
+
+	ctx->streaming_pipe = 0;
+	ctx->streaming_node_cnt = 0;
+
+	ctx->used_raw_num = 0;
+	ctx->used_raw_dev = 0;
+	ctx->processing_buffer_list.cnt = 0;
+	ctx->composed_buffer_list.cnt = 0;
+	ctx->is_first_cq_done = 0;
+	ctx->cq_done_status = 0;
+	ctx->session_created = 0;
+
+	INIT_LIST_HEAD(&ctx->using_buffer_list.list);
+	INIT_LIST_HEAD(&ctx->composed_buffer_list.list);
+	INIT_LIST_HEAD(&ctx->processing_buffer_list.list);
+	INIT_LIST_HEAD(&ctx->processing_img_buffer_list.list);
+	spin_lock_init(&ctx->using_buffer_list.lock);
+	spin_lock_init(&ctx->composed_buffer_list.lock);
+	spin_lock_init(&ctx->processing_buffer_list.lock);
+	spin_lock_init(&ctx->streaming_lock);
+	spin_lock_init(&ctx->first_cq_lock);
+	spin_lock_init(&ctx->processing_img_buffer_list.lock);
+
+	mtk_ctx_watchdog_init(ctx);
+}
+
+int mtk_cam_link_validate(struct v4l2_subdev *sd,
+			  struct media_link *link,
+			  struct v4l2_subdev_format *source_fmt,
+			  struct v4l2_subdev_format *sink_fmt)
+{
+	int ret = 0;
+
+	/* The width, height and code must match. */
+	if (source_fmt->format.width != sink_fmt->format.width) {
+		dev_err(sd->entity.graph_obj.mdev->dev,
+			"%s: width does not match (source %u, sink %u)\n",
+			__func__,
+			source_fmt->format.width, sink_fmt->format.width);
+		ret = -EPIPE;
+	}
+
+	if (source_fmt->format.height != sink_fmt->format.height) {
+		dev_err(sd->entity.graph_obj.mdev->dev,
+			"%s: height does not match (source %u, sink %u)\n",
+			__func__,
+			source_fmt->format.height, sink_fmt->format.height);
+		ret = -EPIPE;
+	}
+
+	if (source_fmt->format.code != sink_fmt->format.code) {
+		dev_err(sd->entity.graph_obj.mdev->dev,
+			"%s: warn: media bus code does not match (source 0x%8.8x, sink 0x%8.8x)\n",
+			__func__,
+			source_fmt->format.code, sink_fmt->format.code);
+		ret = -EPIPE;
+	}
+
+	dev_dbg(sd->entity.graph_obj.mdev->dev,
+		"%s: link was \"%s\":%u -> \"%s\":%u\n", __func__,
+		link->source->entity->name, link->source->index,
+		link->sink->entity->name, link->sink->index);
+
+	if (ret)
+		dev_info(sd->v4l2_dev->dev,
+			 "%s: link validate failed pad/code/w/h: SRC(%d/0x%x/%d/%d), SINK(%d:0x%x/%d/%d)\n",
+			 sd->name, source_fmt->pad, source_fmt->format.code,
+			 source_fmt->format.width, source_fmt->format.height,
+			 sink_fmt->pad, sink_fmt->format.code,
+			 sink_fmt->format.width, sink_fmt->format.height);
+
+	return ret;
+}
+
+static int mtk_cam_debug_fs_init(struct mtk_cam_device *cam)
+{
+	/**
+	 * The dump buffer size depdends on the meta buffer size
+	 * which is variable among devices using different type of sensors
+	 * , e.g. PD's statistic buffers.
+	 */
+	u32 dump_mem_size = MTK_CAM_DEBUG_DUMP_HEADER_MAX_SIZE +
+			    CQ_BUF_SIZE +
+			    camsys_get_meta_size(MTKCAM_IPI_RAW_META_STATS_CFG) +
+			    RAW_STATS_CFG_VARIOUS_SIZE +
+			    sizeof(struct mtkcam_ipi_frame_param) +
+			    sizeof(struct mtkcam_ipi_config_param) *
+			    RAW_PIPELINE_NUM;
+
+	cam->debug_fs = mtk_cam_get_debugfs();
+	if (!cam->debug_fs)
+		return 0;
+
+	cam->debug_fs->ops->init(cam->debug_fs, cam, dump_mem_size);
+	cam->debug_wq = alloc_ordered_workqueue(dev_name(cam->dev),
+						__WQ_LEGACY | WQ_MEM_RECLAIM |
+						WQ_FREEZABLE);
+	if (!cam->debug_wq)
+		return -EINVAL;
+
+	cam->debug_exception_wq = alloc_ordered_workqueue(dev_name(cam->dev),
+							  __WQ_LEGACY |
+							  WQ_MEM_RECLAIM |
+							  WQ_FREEZABLE);
+	if (!cam->debug_exception_wq) {
+		destroy_workqueue(cam->debug_wq);
+		return -EINVAL;
+	}
+
+	init_waitqueue_head(&cam->debug_exception_waitq);
+
+	return 0;
+}
+
+static void mtk_cam_debug_fs_deinit(struct mtk_cam_device *cam)
+{
+	if (!cam->debug_fs)
+		return;
+
+	drain_workqueue(cam->debug_wq);
+	destroy_workqueue(cam->debug_wq);
+	drain_workqueue(cam->debug_exception_wq);
+	destroy_workqueue(cam->debug_exception_wq);
+	cam->debug_fs->ops->deinit(cam->debug_fs);
+}
+
+static int register_sub_drivers(struct device *dev)
+{
+	int ret;
+
+	ret = platform_driver_register(&seninf_pdrv);
+	if (ret) {
+		dev_info(dev, "%s seninf_pdrv fail\n", __func__);
+		goto REGISTER_SENINF_FAIL;
+	}
+
+	ret = platform_driver_register(&seninf_core_pdrv);
+	if (ret) {
+		dev_info(dev, "%s seninf_core_pdrv fail\n", __func__);
+		goto REGISTER_SENINF_CORE_FAIL;
+	}
+
+	ret = platform_driver_register(&mtk_cam_raw_driver);
+	if (ret) {
+		dev_info(dev, "%s mtk_cam_raw_driver fail\n", __func__);
+		goto REGISTER_RAW_FAIL;
+	}
+
+	ret = platform_driver_register(&mtk_cam_yuv_driver);
+	if (ret) {
+		dev_info(dev, "%s mtk_cam_raw_driver fail\n", __func__);
+		goto REGISTER_YUV_FAIL;
+	}
+
+	ret = mtk_cam_master_register(dev);
+	if (ret) {
+		dev_err(dev, "%s mtk_cam_master_register fail\n", __func__);
+		goto ADD_CAM_MASTER_FAIL;
+	}
+
+	return 0;
+
+ADD_CAM_MASTER_FAIL:
+	platform_driver_unregister(&mtk_cam_yuv_driver);
+
+REGISTER_YUV_FAIL:
+	platform_driver_unregister(&mtk_cam_raw_driver);
+
+REGISTER_RAW_FAIL:
+	platform_driver_unregister(&seninf_core_pdrv);
+
+REGISTER_SENINF_CORE_FAIL:
+	platform_driver_unregister(&seninf_pdrv);
+
+REGISTER_SENINF_FAIL:
+	return ret;
+}
+
+static void unregister_sub_drivers(struct device *dev)
+{
+	mtk_cam_master_unregister(dev);
+
+	platform_driver_unregister(&mtk_cam_yuv_driver);
+	platform_driver_unregister(&mtk_cam_raw_driver);
+	platform_driver_unregister(&seninf_core_pdrv);
+	platform_driver_unregister(&seninf_pdrv);
+}
+
+static int mtk_cam_master_complete(struct v4l2_async_notifier *notifier)
+{
+	struct mtk_cam_device *cam_dev =
+		container_of(notifier, struct mtk_cam_device, notifier);
+	struct device *dev = cam_dev->dev;
+	int ret;
+
+	dev_info(dev, "cmasys | trigger %s\n", __func__);
+
+	/* set raw and yuv internal */
+	ret = mtk_cam_raw_setup_dependencies(&cam_dev->raw);
+	if (ret) {
+		dev_err(dev, "Failed to mtk_cam_raw_setup_dependencies: %d\n", ret);
+		goto fail_unbind_all;
+	}
+
+	/* register raw subdev */
+	ret = mtk_cam_raw_register_entities(&cam_dev->raw, &cam_dev->v4l2_dev);
+	if (ret) {
+		dev_err(dev, "Failed to init raw subdevs: %d\n", ret);
+		goto fail_remove_dependencies;
+	}
+
+	mtk_cam_create_links(cam_dev);
+
+	/* Expose all subdev's nodes */
+	ret = v4l2_device_register_subdev_nodes(&cam_dev->v4l2_dev);
+	if (ret) {
+		dev_err(dev, "Failed to register subdev nodes\n");
+		goto fail_unreg_raw_entities;
+	}
+
+	dev_info(dev, "%s success\n", __func__);
+
+	return 0;
+
+fail_unreg_raw_entities:
+	mtk_cam_raw_unregister_entities(&cam_dev->raw);
+
+fail_remove_dependencies:
+	/* nothing to do for now */
+
+fail_unbind_all:
+	return ret;
+}
+
+static int mtk_cam_master_bound(struct v4l2_async_notifier *notifier,
+				struct v4l2_subdev *subdev,
+				struct v4l2_async_connection *asc)
+{
+	struct mtk_cam_device *cam_dev =
+		container_of(notifier, struct mtk_cam_device, notifier);
+	struct mtk_raw *raw = &cam_dev->raw;
+	struct device *cam = cam_dev->dev;
+	struct device *dev = subdev->dev;
+
+	dev_info(cam, "cmasys | %s trigger %s\n", subdev->name, __func__);
+
+	if (strcmp(dev_driver_string(dev), "seninf") == 0) {
+		dev_dbg(cam, "%s@(seninf) done\n", __func__);
+	} else if (strcmp(dev_driver_string(dev), "mtk-cam raw") == 0) {
+		struct mtk_raw_device *raw_dev = dev_get_drvdata(dev);
+
+		raw_dev->cam = cam_dev;
+		raw->devs[raw_dev->id] = dev;
+		raw->cam_dev = cam_dev->dev;
+		dev_dbg(cam, "%s@(mtk-cam raw) done\n", __func__);
+	} else if (strcmp(dev_driver_string(dev), "mtk-cam yuv") == 0) {
+		struct mtk_yuv_device *yuv_dev = dev_get_drvdata(dev);
+
+		raw->yuvs[yuv_dev->id] = dev;
+		dev_dbg(cam, "%s@(mtk-cam yuv) done\n", __func__);
+	} else {
+		dev_warn(cam, "%s got unrecongized device\n", __func__);
+	}
+
+	return 0;
+}
+
+static void mtk_cam_master_unbound(struct v4l2_async_notifier *notifier,
+				   struct v4l2_subdev *subdev,
+				   struct v4l2_async_connection *asc)
+{
+	struct mtk_cam_device *cam_dev =
+		container_of(notifier, struct mtk_cam_device, notifier);
+	struct mtk_raw *raw = &cam_dev->raw;
+	struct device *cam = cam_dev->dev;
+	struct device *dev = subdev->dev;
+
+	dev_info(cam, "cmasys | %s trigger %s\n", subdev->name, __func__);
+
+	if (strcmp(dev_driver_string(dev), "seninf") == 0) {
+		dev_dbg(cam, "%s@(seninf) done\n", __func__);
+	} else if (strcmp(dev_driver_string(dev), "mtk-cam raw") == 0) {
+		struct mtk_raw_device *raw_dev = dev_get_drvdata(dev);
+
+		mtk_cam_raw_unregister_entities(&cam_dev->raw);
+
+		raw_dev->cam = NULL;
+		raw->devs[raw_dev->id] = NULL;
+		raw->cam_dev = NULL;
+		dev_dbg(cam, "%s@(mtk-cam raw) done\n", __func__);
+	} else if (strcmp(dev_driver_string(dev), "mtk-cam yuv") == 0) {
+		struct mtk_yuv_device *yuv_dev = dev_get_drvdata(dev);
+
+		raw->yuvs[yuv_dev->id] = NULL;
+		dev_dbg(cam, "%s@(mtk-cam yuv) done\n", __func__);
+	} else {
+		dev_warn(cam, "%s got unrecongized device\n", __func__);
+	}
+}
+
+static const struct v4l2_async_notifier_operations mtk_cam_async_nf_ops = {
+	.complete = mtk_cam_master_complete,
+	.bound = mtk_cam_master_bound,
+	.unbind = mtk_cam_master_unbound,
+};
+
+static int mtk_cam_probe(struct platform_device *pdev)
+{
+	struct mtk_cam_device *cam_dev;
+	struct device *dev = &pdev->dev;
+	struct resource *res;
+	int ret;
+	unsigned int i;
+
+	dev_dbg(dev, "camsys | start %s\n", __func__);
+
+	/* initialize structure */
+	cam_dev = devm_kzalloc(dev, sizeof(*cam_dev), GFP_KERNEL);
+	if (!cam_dev)
+		return -ENOMEM;
+
+	if (dma_set_mask_and_coherent(dev, DMA_BIT_MASK(34))) {
+		dev_err(dev, "%s: No suitable DMA available\n", __func__);
+		return -EIO;
+	}
+
+	if (!dev->dma_parms) {
+		dev->dma_parms =
+			devm_kzalloc(dev, sizeof(*dev->dma_parms), GFP_KERNEL);
+		if (!dev->dma_parms)
+			return -ENOMEM;
+	}
+
+	dma_set_max_seg_size(dev, UINT_MAX);
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		dev_err(dev, "failed to get mem\n");
+		return -ENODEV;
+	}
+
+	cam_dev->base = devm_ioremap_resource(dev, res);
+	if (IS_ERR(cam_dev->base)) {
+		dev_err(dev, "failed to map register base\n");
+		return PTR_ERR(cam_dev->base);
+	}
+
+	cam_dev->dev = dev;
+	dev_set_drvdata(dev, cam_dev);
+
+	cam_dev->composer_cnt = 0;
+	cam_dev->num_seninf_devices = 0;
+
+	cam_dev->max_stream_num = MTKCAM_SUBDEV_MAX;
+	cam_dev->ctxs = devm_kcalloc(dev, cam_dev->max_stream_num,
+				     sizeof(*cam_dev->ctxs), GFP_KERNEL);
+	if (!cam_dev->ctxs)
+		return -ENOMEM;
+
+	cam_dev->streaming_ctx = 0;
+	for (i = 0; i < cam_dev->max_stream_num; i++)
+		mtk_cam_ctx_init(cam_dev->ctxs + i, cam_dev, i);
+
+	cam_dev->running_job_count = 0;
+	spin_lock_init(&cam_dev->pending_job_lock);
+	spin_lock_init(&cam_dev->running_job_lock);
+	INIT_LIST_HEAD(&cam_dev->pending_job_list);
+	INIT_LIST_HEAD(&cam_dev->running_job_list);
+
+	cam_dev->dma_processing_count = 0;
+	spin_lock_init(&cam_dev->dma_pending_lock);
+	spin_lock_init(&cam_dev->dma_processing_lock);
+	INIT_LIST_HEAD(&cam_dev->dma_pending);
+	INIT_LIST_HEAD(&cam_dev->dma_processing);
+
+	mutex_init(&cam_dev->queue_lock);
+
+	pm_runtime_enable(dev);
+
+	ret = mtk_cam_of_rproc(cam_dev, pdev);
+	if (ret)
+		goto fail_destroy_mutex;
+
+	ret = register_sub_drivers(dev);
+	if (ret) {
+		dev_err(dev, "fail to register_sub_drivers\n");
+		goto fail_destroy_mutex;
+	}
+
+	/* register mtk_cam as all isp subdev async parent */
+	cam_dev->notifier.ops = &mtk_cam_async_nf_ops;
+	v4l2_async_nf_init(&cam_dev->notifier, &cam_dev->v4l2_dev);
+	ret = mtk_cam_async_subdev_add(dev); /* wait all isp sub drivers */
+	if (ret) {
+		dev_err(dev, "%s failed mtk_cam_async_subdev_add\n", __func__);
+		goto fail_unregister_sub_drivers;
+	}
+
+	ret = v4l2_async_nf_register(&cam_dev->notifier);
+	if (ret) {
+		dev_err(dev, "%s async_nf_register ret:%d\n", __func__, ret);
+		v4l2_async_nf_cleanup(&cam_dev->notifier);
+		goto fail_unregister_sub_drivers;
+	}
+
+	ret = mtk_cam_debug_fs_init(cam_dev);
+	if (ret < 0)
+		goto fail_unregister_async_nf;
+
+	dev_info(dev, "camsys | [%s] success\n", __func__);
+
+	return 0;
+
+fail_unregister_async_nf:
+	v4l2_async_nf_unregister(&cam_dev->notifier);
+
+fail_unregister_sub_drivers:
+	unregister_sub_drivers(dev);
+
+fail_destroy_mutex:
+	mutex_destroy(&cam_dev->queue_lock);
+
+	return ret;
+}
+
+static void mtk_cam_remove(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct mtk_cam_device *cam_dev = dev_get_drvdata(dev);
+
+	pm_runtime_disable(dev);
+
+	mtk_cam_debug_fs_deinit(cam_dev);
+
+	v4l2_async_nf_unregister(&cam_dev->notifier);
+
+	unregister_sub_drivers(dev);
+
+	mutex_destroy(&cam_dev->queue_lock);
+}
+
+static const struct dev_pm_ops mtk_cam_pm_ops = {
+	SET_RUNTIME_PM_OPS(mtk_cam_runtime_suspend, mtk_cam_runtime_resume,
+			   NULL)
+};
+
+static struct platform_driver mtk_cam_driver = {
+	.probe   = mtk_cam_probe,
+	.remove  = mtk_cam_remove,
+	.driver  = {
+		.name  = "mtk-cam",
+		.of_match_table = of_match_ptr(mtk_cam_of_ids),
+		.pm     = &mtk_cam_pm_ops,
+	}
+};
+
+module_platform_driver(mtk_cam_driver);
+
+MODULE_DESCRIPTION("MediaTek camera ISP driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/platform/mediatek/isp/isp_7x/camsys/mtk_cam.h b/drivers/media/platform/mediatek/isp/isp_7x/camsys/mtk_cam.h
new file mode 100644
index 000000000000..46342624c198
--- /dev/null
+++ b/drivers/media/platform/mediatek/isp/isp_7x/camsys/mtk_cam.h
@@ -0,0 +1,733 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2022 MediaTek Inc.
+ */
+
+#ifndef __MTK_CAM_H
+#define __MTK_CAM_H
+
+#include <linux/list.h>
+#include <linux/of.h>
+#include <linux/rpmsg.h>
+#include <media/media-device.h>
+#include <media/media-request.h>
+#include <media/v4l2-async.h>
+#include <media/v4l2-device.h>
+
+#include "mtk_cam-raw.h"
+#include "mtk_cam-ipi.h"
+#include "kd_imgsensor_define_v4l2.h"
+#include "mtk_cam-seninf-def.h"
+#include "mtk_cam-seninf-drv.h"
+#include "mtk_cam-seninf-if.h"
+#include "mtk_cam-ctrl.h"
+#include "mtk_cam-debug.h"
+#include "mtk_cam-plat-util.h"
+
+/* for SCP internal working buffers, need to align it with SCP */
+#define SIZE_OF_RAW_PRIV		20788
+#define SIZE_OF_RAW_WORKBUF		18600
+#define SIZE_OF_SESSION			22596
+
+#define IPI_FRAME_BUF_SIZE ALIGN(sizeof(struct mtkcam_ipi_frame_param), SZ_1K)
+
+/* for cq working buffers */
+#define CQ_BUF_SIZE			0x8000 /* ISP7_1 */
+#define CAM_CQ_BUF_NUM			16
+#define CAM_IMG_BUF_NUM			6
+#define MAX_PIPES_PER_STREAM		5
+#define MTK_CAM_CTX_WATCHDOG_INTERVAL	100
+#define MTK_CAM_REQ_MAX_S_DATA		2
+
+#define SENSOR_FMT_MASK					0xFFFF
+
+/* flags of mtk_cam_request */
+#define MTK_CAM_REQ_FLAG_SENINF_CHANGED			BIT(0)
+#define MTK_CAM_REQ_FLAG_SENINF_IMMEDIATE_UPDATE	BIT(1)
+
+/* flags of mtk_cam_request_stream_data */
+#define MTK_CAM_REQ_S_DATA_FLAG_TG_FLASH		BIT(0)
+
+#define MTK_CAM_REQ_S_DATA_FLAG_META1_INDEPENDENT	BIT(1)
+
+#define MTK_CAM_REQ_S_DATA_FLAG_SINK_FMT_UPDATE		BIT(2)
+/* Apply sensor mode and the timing is 1 vsync before */
+#define MTK_CAM_REQ_S_DATA_FLAG_SENSOR_MODE_UPDATE_T1	BIT(3)
+
+#define MTK_CAM_REQ_S_DATA_FLAG_SENSOR_HDL_EN		BIT(4)
+
+#define MTK_CAM_REQ_S_DATA_FLAG_RAW_HDL_EN		BIT(5)
+
+#define MTK_CAM_REQ_S_DATA_FLAG_SENSOR_HDL_COMPLETE	BIT(6)
+
+#define MTK_CAM_REQ_S_DATA_FLAG_RAW_HDL_COMPLETE	BIT(7)
+
+#define MTK_CAM_REQ_S_DATA_FLAG_SENSOR_HDL_DELAYED	BIT(8)
+
+struct mtk_cam_request;
+struct mtk_cam_debug_fs;
+struct mtk_cam_device;
+struct mtk_raw_pipeline;
+
+struct mtk_cam_working_buf {
+	void *va;
+	dma_addr_t iova;
+	dma_addr_t scp_addr;
+	unsigned int size;
+};
+
+struct mtk_cam_msg_buf {
+	void *va;
+	dma_addr_t scp_addr;
+	unsigned int size;
+};
+
+struct mtk_cam_working_buf_entry {
+	struct mtk_cam_ctx *ctx;
+	struct mtk_cam_request_stream_data *s_data;
+	struct mtk_cam_working_buf buffer;
+	struct mtk_cam_msg_buf msg_buffer;
+	struct list_head list_entry;
+	int cq_desc_offset;
+	unsigned int cq_desc_size;
+	int sub_cq_desc_offset;
+	unsigned int sub_cq_desc_size;
+};
+
+struct mtk_cam_img_working_buf_entry {
+	struct mtk_cam_ctx *ctx;
+	struct mtk_cam_request_stream_data *s_data;
+	struct mtk_cam_working_buf img_buffer;
+	struct list_head list_entry;
+};
+
+struct mtk_cam_working_buf_list {
+	struct list_head list;
+	u32 cnt;
+	spinlock_t lock; /* protect this list and cnt */
+};
+
+struct mtk_cam_req_work {
+	struct work_struct work;
+	struct mtk_cam_request_stream_data *s_data;
+	struct list_head list;
+	atomic_t is_queued;
+};
+
+struct mtk_cam_req_feature {
+	int raw_feature;
+	bool switch_prev_frame_done;
+	bool switch_curr_setting_done;
+	bool switch_done;
+};
+
+struct mtk_cam_sensor_work {
+	struct kthread_work work;
+	atomic_t is_queued;
+};
+
+/*
+ * struct mtk_cam_request_stream_data - per stream members of a request
+ *
+ * @pad_fmt: pad format configurtion for sensor switch.
+ * @frame_params: The frame info. & address info. of enabled DMA nodes.
+ * @frame_work: work queue entry for frame transmission to SCP.
+ * @working_buf: command queue buffer associated to this request
+ * @deque_list_node: the entry node of s_data for deque
+ * @cleanup_list_node: the entry node of s_data for cleanup
+ */
+struct mtk_cam_request_stream_data {
+	u32 index;
+	struct mtk_cam_request *req;
+	struct mtk_cam_ctx *ctx;
+	u32 pipe_id;
+	u32 frame_seq_no;
+	u32 flags;
+	unsigned long raw_dmas;
+	u64 timestamp;
+	u64 timestamp_mono;
+	atomic_t buf_state; /* default: -1 */
+	struct mtk_cam_buffer *bufs[MTK_RAW_TOTAL_NODES];
+	struct v4l2_subdev *sensor;
+	struct media_request_object *sensor_hdl_obj;
+	struct media_request_object *raw_hdl_obj;
+	struct v4l2_subdev_format seninf_fmt;
+	struct v4l2_subdev_format pad_fmt[MTK_RAW_PIPELINE_PADS_NUM];
+	struct v4l2_rect pad_selection[MTK_RAW_PIPELINE_PADS_NUM];
+	struct v4l2_format vdev_fmt[MTK_RAW_TOTAL_NODES];
+	struct v4l2_selection vdev_selection[MTK_RAW_TOTAL_NODES];
+	struct mtkcam_ipi_frame_param frame_params;
+	struct mtk_cam_sensor_work sensor_work;
+	struct mtk_cam_req_work seninf_s_fmt_work;
+	struct mtk_cam_req_work frame_work;
+	struct mtk_cam_req_work meta1_done_work;
+	struct mtk_cam_req_work frame_done_work;
+	struct mtk_camsys_ctrl_state state;
+	struct mtk_cam_working_buf_entry *working_buf;
+	unsigned int no_frame_done_cnt;
+	atomic_t seninf_dump_state;
+	struct mtk_cam_req_feature feature;
+	struct mtk_cam_req_dbg_work dbg_work;
+	struct mtk_cam_req_dbg_work dbg_exception_work;
+	struct list_head deque_list_node;
+	struct list_head cleanup_list_node;
+	atomic_t first_setting_check;
+};
+
+struct mtk_cam_req_pipe {
+	int s_data_num;
+	int req_seq;
+	struct mtk_cam_request_stream_data s_data[MTK_CAM_REQ_MAX_S_DATA];
+};
+
+enum mtk_cam_request_state {
+	MTK_CAM_REQ_STATE_PENDING,
+	MTK_CAM_REQ_STATE_RUNNING,
+	MTK_CAM_REQ_STATE_DELETING,
+	MTK_CAM_REQ_STATE_COMPLETE,
+	MTK_CAM_REQ_STATE_CLEANUP,
+	NR_OF_MTK_CAM_REQ_STATE,
+};
+
+enum mtk_cam_pixel_mode {
+	PXL_MOD_1 = 0,
+	PXL_MOD_2,
+	PXL_MOD_4,
+	PXL_MOD_8,
+};
+
+/*
+ * mtk_cam_frame_sync: the frame sync state of one request
+ *
+ * @target: the num of ctx(sensor) which should be synced
+ * @on_cnt: the count of frame sync on called by ctx
+ * @off_cnt: the count of frame sync off called by ctx
+ * @op_lock: protect frame sync state variables
+ */
+struct mtk_cam_frame_sync {
+	unsigned int target;
+	unsigned int on_cnt;
+	unsigned int off_cnt;
+	struct mutex op_lock; /* sync operation lock */
+};
+
+struct mtk_cam_req_raw_pipe_data {
+	struct mtk_cam_resource res;
+	int enabled_raw;
+};
+
+/*
+ * struct mtk_cam_request - MTK camera request.
+ *
+ * @req: Embedded struct media request.
+ * @pipe_used: pipe used in this request. Two or more pipes may share
+ *     the same context.
+ * @ctx_used: context used in this request.
+ * @done_status: Record context done status.
+ * @done_status_lock: Spinlock for context done status.
+ * @fs: the frame sync state.
+ * @list: List entry of the object for pending_job_list or running_job_list.
+ * @cleanup_list: List entry of the request to cleanup.
+ * @p_data: restore stream request data in a pipe.
+ * @p_data: restore raw pipe resource data.
+ * @sync_id: frame sync index.
+ */
+struct mtk_cam_request {
+	struct media_request req;
+	unsigned int pipe_used;
+	unsigned int ctx_used;
+	unsigned int done_status;
+	spinlock_t done_status_lock; /* protect done_status */
+	atomic_t state;
+	struct mtk_cam_frame_sync fs;
+	struct list_head list;
+	struct list_head cleanup_list;
+	struct mtk_cam_req_pipe p_data[MTKCAM_SUBDEV_MAX];
+	struct mtk_cam_req_raw_pipe_data raw_pipe_data[MTKCAM_SUBDEV_RAW_END -
+						       MTKCAM_SUBDEV_RAW_START];
+	s64 sync_id;
+	atomic_t ref_cnt;
+};
+
+struct mtk_cam_working_buf_pool {
+	struct mtk_cam_ctx *ctx;
+
+	struct dma_buf *working_buf_dmabuf;
+	void *working_buf_va;
+	dma_addr_t working_buf_iova;
+	dma_addr_t working_buf_scp_addr;
+	unsigned int working_buf_size;
+
+	void *msg_buf_va;
+	dma_addr_t msg_buf_scp_addr;
+	unsigned int msg_buf_size;
+
+	void *raw_workbuf_va;
+	dma_addr_t raw_workbuf_scp_addr;
+	unsigned int raw_workbuf_size;
+
+	void *priv_workbuf_va;
+	dma_addr_t priv_workbuf_scp_addr;
+	unsigned int priv_workbuf_size;
+
+	void *session_buf_va;
+	dma_addr_t session_buf_scp_addr;
+	unsigned int session_buf_size;
+
+	struct mtk_cam_working_buf_entry working_buf[CAM_CQ_BUF_NUM];
+	struct mtk_cam_working_buf_list cam_freelist;
+};
+
+struct mtk_cam_img_working_buf_pool {
+	struct mtk_cam_ctx *ctx;
+	struct dma_buf *working_img_buf_dmabuf;
+	void *working_img_buf_va;
+	dma_addr_t working_img_buf_iova;
+	dma_addr_t working_img_buf_scp_addr;
+	unsigned int working_img_buf_size;
+	struct mtk_cam_img_working_buf_entry img_working_buf[CAM_IMG_BUF_NUM];
+	struct mtk_cam_working_buf_list cam_freeimglist;
+};
+
+struct mtk_cam_ctx {
+	struct mtk_cam_device *cam;
+	unsigned int stream_id;
+	unsigned int streaming;
+	unsigned int synced;
+	struct media_pipeline pipeline;
+	struct mtk_raw_pipeline *pipe;
+	unsigned int enabled_node_cnt;
+	unsigned int streaming_pipe;
+	unsigned int streaming_node_cnt;
+	unsigned int is_first_cq_done;
+	unsigned int cq_done_status;
+	atomic_t running_s_data_cnt;
+	struct v4l2_subdev *sensor;
+	struct v4l2_subdev *seninf;
+	struct v4l2_subdev *pipe_subdevs[MAX_PIPES_PER_STREAM];
+	struct mtk_camsys_sensor_ctrl sensor_ctrl;
+
+	unsigned int used_raw_num;
+	unsigned int used_raw_dev;
+
+	struct task_struct *sensor_worker_task;
+	struct kthread_worker sensor_worker;
+	struct workqueue_struct *composer_wq;
+	struct workqueue_struct *frame_done_wq;
+
+	struct completion session_complete;
+	struct completion m2m_complete;
+	int session_created;
+
+	struct mtk_cam_working_buf_pool buf_pool;
+	struct mtk_cam_working_buf_list using_buffer_list;
+	struct mtk_cam_working_buf_list composed_buffer_list;
+	struct mtk_cam_working_buf_list processing_buffer_list;
+
+	/* sensor image buffer pool handling from kernel */
+	struct mtk_cam_img_working_buf_pool img_buf_pool;
+	struct mtk_cam_working_buf_list processing_img_buffer_list;
+
+	atomic_t enqueued_frame_seq_no;
+	unsigned int composed_frame_seq_no;
+	unsigned int dequeued_frame_seq_no;
+
+	spinlock_t streaming_lock; /* protect streaming */
+	spinlock_t first_cq_lock; /* protect is_first_cq_done */
+
+	/* watchdog */
+	int watchdog_timeout_tg;
+	atomic_t watchdog_dump;
+	atomic_long_t watchdog_prev;
+	struct timer_list watchdog_timer;
+	struct work_struct watchdog_work;
+
+	/* support debug dump */
+	struct mtkcam_ipi_config_param config_params;
+};
+
+struct mtk_cam_device {
+	struct device *dev;
+
+	struct v4l2_device v4l2_dev;
+	struct v4l2_async_notifier notifier;
+	struct media_device media_dev;
+	void __iomem *base;
+
+	struct mtk_scp *scp;
+	struct device *smem_dev;
+	phandle rproc_phandle;
+	struct rproc *rproc_handle;
+
+	unsigned int composer_cnt;
+
+	unsigned int num_seninf_devices;
+	unsigned int num_raw_devices;
+	unsigned int num_larb_devices;
+
+	/* raw_pipe controller subdev */
+	struct mtk_raw raw;
+	struct mutex queue_lock; /* protect queue request */
+
+	unsigned int max_stream_num;
+	unsigned int streaming_ctx;
+	unsigned int streaming_pipe;
+	struct mtk_cam_ctx *ctxs;
+
+	/* request related */
+	struct list_head pending_job_list;
+	spinlock_t pending_job_lock; /* protect pending_job_list */
+	struct list_head running_job_list;
+	unsigned int running_job_count;
+	spinlock_t running_job_lock; /* protect running_job_list */
+
+	/* standard v4l2 buffer control */
+	struct list_head dma_pending;
+	spinlock_t dma_pending_lock; /* protect dma_pending_list */
+	struct list_head dma_processing;
+	spinlock_t dma_processing_lock; /* protect dma_processing_list and dma_processing_count */
+	unsigned int dma_processing_count;
+
+	struct mtk_cam_debug_fs *debug_fs;
+	struct workqueue_struct *debug_wq;
+	struct workqueue_struct *debug_exception_wq;
+	wait_queue_head_t debug_exception_waitq;
+};
+
+static inline struct mtk_cam_request_stream_data *
+mtk_cam_req_work_get_s_data(struct mtk_cam_req_work *work)
+{
+	return work->s_data;
+}
+
+static inline struct mtk_cam_request_stream_data *
+mtk_cam_ctrl_state_to_req_s_data(struct mtk_camsys_ctrl_state *state)
+{
+	return container_of(state, struct mtk_cam_request_stream_data, state);
+}
+
+static inline struct mtk_cam_request *
+mtk_cam_ctrl_state_get_req(struct mtk_camsys_ctrl_state *state)
+{
+	struct mtk_cam_request_stream_data *request_stream_data;
+
+	request_stream_data = mtk_cam_ctrl_state_to_req_s_data(state);
+	return request_stream_data->req;
+}
+
+static inline u32
+mtk_cam_req_get_num_s_data(struct mtk_cam_request *req, u32 pipe_id)
+{
+	if (pipe_id >= MTKCAM_SUBDEV_MAX)
+		return 0;
+
+	return req->p_data[pipe_id].s_data_num;
+}
+
+/**
+ * Be used operation between request reinit and enqueue.
+ * For example, request-based set fmt and selection.
+ */
+static inline struct mtk_cam_request_stream_data *
+mtk_cam_req_get_s_data_no_chk(struct mtk_cam_request *req, u32 pipe_id, u32 idx)
+{
+	return &req->p_data[pipe_id].s_data[idx];
+}
+
+static inline struct mtk_cam_request_stream_data *
+mtk_cam_req_get_s_data(struct mtk_cam_request *req, u32 pipe_id, u32 idx)
+{
+	if (!req || pipe_id >= MTKCAM_SUBDEV_MAX)
+		return NULL;
+
+	if (idx >= req->p_data[pipe_id].s_data_num)
+		return NULL;
+
+	return mtk_cam_req_get_s_data_no_chk(req, pipe_id, idx);
+}
+
+static inline struct mtk_cam_ctx *
+mtk_cam_s_data_get_ctx(struct mtk_cam_request_stream_data *s_data)
+{
+	if (!s_data)
+		return NULL;
+
+	return s_data->ctx;
+}
+
+static inline char *
+mtk_cam_s_data_get_dbg_str(struct mtk_cam_request_stream_data *s_data)
+{
+	return s_data->req->req.debug_str;
+}
+
+static inline struct mtk_cam_request *
+mtk_cam_s_data_get_req(struct mtk_cam_request_stream_data *s_data)
+{
+	if (!s_data)
+		return NULL;
+
+	return s_data->req;
+}
+
+static inline struct mtk_cam_req_raw_pipe_data *
+mtk_cam_s_data_get_raw_pipe_data(struct mtk_cam_request_stream_data *s_data)
+{
+	if (!is_raw_subdev(s_data->pipe_id))
+		return NULL;
+
+	return &s_data->req->raw_pipe_data[s_data->pipe_id];
+}
+
+static inline struct mtk_cam_resource *
+mtk_cam_s_data_get_res(struct mtk_cam_request_stream_data *s_data)
+{
+	if (!s_data)
+		return NULL;
+
+	if (!is_raw_subdev(s_data->pipe_id))
+		return NULL;
+
+	return &s_data->req->raw_pipe_data[s_data->pipe_id].res;
+}
+
+static inline int
+mtk_cam_s_data_get_res_feature(struct mtk_cam_request_stream_data *s_data)
+{
+	return (!s_data || !is_raw_subdev(s_data->pipe_id)) ?
+		0 : s_data->req->raw_pipe_data[s_data->pipe_id].res.raw_res.feature;
+}
+
+static inline int
+mtk_cam_s_data_get_vbuf_idx(struct mtk_cam_request_stream_data *s_data, int node_id)
+{
+	if (s_data->pipe_id >= MTKCAM_SUBDEV_RAW_START &&
+	    s_data->pipe_id < MTKCAM_SUBDEV_RAW_END)
+		return node_id - MTK_RAW_SINK_NUM;
+
+	return -1;
+}
+
+static inline void
+mtk_cam_s_data_set_vbuf(struct mtk_cam_request_stream_data *s_data,
+			struct mtk_cam_buffer *buf,
+			int node_id)
+{
+	int idx = mtk_cam_s_data_get_vbuf_idx(s_data, node_id);
+
+	if (idx >= 0)
+		s_data->bufs[idx] = buf;
+}
+
+static inline struct mtk_cam_buffer *
+mtk_cam_s_data_get_vbuf(struct mtk_cam_request_stream_data *s_data, int node_id)
+{
+	int idx = mtk_cam_s_data_get_vbuf_idx(s_data, node_id);
+
+	if (idx >= 0)
+		return s_data->bufs[idx];
+	return NULL;
+}
+
+static inline struct v4l2_format *
+mtk_cam_s_data_get_vfmt(struct mtk_cam_request_stream_data *s_data, int node_id)
+{
+	int idx = mtk_cam_s_data_get_vbuf_idx(s_data, node_id);
+
+	if (idx >= 0)
+		return &s_data->vdev_fmt[idx];
+
+	return NULL;
+}
+
+static inline struct v4l2_mbus_framefmt *
+mtk_cam_s_data_get_pfmt(struct mtk_cam_request_stream_data *s_data, int pad)
+{
+	if (pad >= 0)
+		return &s_data->pad_fmt[pad].format;
+
+	return NULL;
+}
+
+static inline struct v4l2_selection *
+mtk_cam_s_data_get_vsel(struct mtk_cam_request_stream_data *s_data, int node_id)
+{
+	int idx = mtk_cam_s_data_get_vbuf_idx(s_data, node_id);
+
+	if (idx >= 0)
+		return &s_data->vdev_selection[idx];
+
+	return NULL;
+}
+
+static inline void
+mtk_cam_s_data_reset_vbuf(struct mtk_cam_request_stream_data *s_data, int node_id)
+{
+	int idx = mtk_cam_s_data_get_vbuf_idx(s_data, node_id);
+
+	if (idx >= 0)
+		s_data->bufs[idx] = NULL;
+}
+
+static inline void
+mtk_cam_s_data_set_wbuf(struct mtk_cam_request_stream_data *s_data,
+			struct mtk_cam_working_buf_entry *buf_entry)
+{
+	buf_entry->s_data = s_data;
+	s_data->working_buf = buf_entry;
+}
+
+static inline void
+mtk_cam_s_data_reset_wbuf(struct mtk_cam_request_stream_data *s_data)
+{
+	if (!s_data->working_buf)
+		return;
+
+	s_data->working_buf->s_data = NULL;
+	s_data->working_buf = NULL;
+}
+
+static inline bool
+mtk_cam_s_data_set_buf_state(struct mtk_cam_request_stream_data *s_data,
+			     enum vb2_buffer_state state)
+{
+	if (!s_data)
+		return false;
+
+	if (-1 == atomic_cmpxchg(&s_data->buf_state, -1, state))
+		return true;
+
+	return false;
+}
+
+int mtk_cam_s_data_raw_select(struct mtk_cam_request_stream_data *s_data,
+			      struct mtkcam_ipi_input_param *cfg_in_param);
+
+static inline struct mtk_cam_request_stream_data *
+mtk_cam_sensor_work_to_s_data(struct kthread_work *work)
+{
+	return container_of(work, struct mtk_cam_request_stream_data,
+			    sensor_work.work);
+}
+
+static inline struct mtk_cam_seninf_dump_work *
+to_mtk_cam_seninf_dump_work(struct work_struct *work)
+{
+	return container_of(work, struct mtk_cam_seninf_dump_work, work);
+}
+
+static inline struct mtk_cam_request *
+to_mtk_cam_req(struct media_request *__req)
+{
+	return container_of(__req, struct mtk_cam_request, req);
+}
+
+static inline void
+mtk_cam_pad_fmt_enable(struct v4l2_mbus_framefmt *framefmt)
+{
+	framefmt->flags |= V4L2_MBUS_FRAMEFMT_PAD_ENABLE;
+}
+
+static inline void
+mtk_cam_pad_fmt_disable(struct v4l2_mbus_framefmt *framefmt)
+{
+	framefmt->flags &= ~V4L2_MBUS_FRAMEFMT_PAD_ENABLE;
+}
+
+static inline bool
+mtk_cam_is_pad_fmt_enable(struct v4l2_mbus_framefmt *framefmt)
+{
+	return framefmt->flags & V4L2_MBUS_FRAMEFMT_PAD_ENABLE;
+}
+
+static inline void mtk_cam_fs_reset(struct mtk_cam_frame_sync *fs)
+{
+	fs->target = 0;
+	fs->on_cnt = 0;
+	fs->off_cnt = 0;
+}
+
+static inline struct device *
+mtk_cam_find_raw_dev(struct mtk_cam_device *cam,  unsigned int raw_mask)
+{
+	unsigned int i;
+
+	for (i = 0; i < cam->num_raw_devices; i++)
+		if (raw_mask & (1 << i))
+			return cam->raw.devs[i];
+
+	return NULL;
+}
+
+void mtk_cam_buf_try_queue(struct mtk_cam_ctx *ctx);
+
+struct mtk_cam_ctx *mtk_cam_find_ctx(struct mtk_cam_device *cam,
+				     struct media_entity *entity);
+struct mtk_cam_ctx *mtk_cam_start_ctx(struct mtk_cam_device *cam,
+				      struct mtk_cam_video_device *node);
+void mtk_cam_stop_ctx(struct mtk_cam_ctx *ctx,
+		      struct mtk_cam_video_device *node);
+int mtk_cam_ctx_stream_on(struct mtk_cam_ctx *ctx,
+			  struct mtk_cam_video_device *node);
+int mtk_cam_ctx_stream_off(struct mtk_cam_ctx *ctx,
+			   struct mtk_cam_video_device *node);
+void mtk_cam_complete_raw_hdl(struct mtk_cam_request_stream_data *s_data);
+void mtk_cam_complete_sensor_hdl(struct mtk_cam_request_stream_data *s_data);
+
+bool watchdog_scenario(struct mtk_cam_ctx *ctx);
+void mtk_ctx_watchdog_kick(struct mtk_cam_ctx *ctx);
+
+int mtk_cam_call_seninf_set_pixelmode(struct mtk_cam_ctx *ctx,
+				      struct v4l2_subdev *sd,
+				      int pad_id, int pixel_mode);
+void mtk_cam_dev_req_enqueue(struct mtk_cam_device *cam,
+			     struct mtk_cam_request *req);
+void mtk_cam_dev_req_cleanup(struct mtk_cam_ctx *ctx, int pipe_id,
+			     int buf_state);
+void mtk_cam_dev_req_clean_pending(struct mtk_cam_device *cam, int pipe_id,
+				   int buf_state);
+
+void mtk_cam_req_get(struct mtk_cam_request *req, int pipe_id);
+bool mtk_cam_req_put(struct mtk_cam_request *req, int pipe_id);
+
+void mtk_cam_dev_req_try_queue(struct mtk_cam_device *cam);
+
+void mtk_cam_s_data_update_timestamp(struct mtk_cam_buffer *buf,
+				     struct mtk_cam_request_stream_data *s_data);
+
+int mtk_cam_dequeue_req_frame(struct mtk_cam_ctx *ctx,
+			      unsigned int dequeued_frame_seq_no,
+			      int pipe_id);
+
+void mtk_cam_dev_job_done(struct mtk_cam_ctx *ctx,
+			  struct mtk_cam_request *req,
+			  int pipe_id,
+			  enum vb2_buffer_state state);
+
+void mtk_cam_apply_pending_dev_config(struct mtk_cam_request_stream_data *s_data);
+
+int mtk_cam_link_validate(struct v4l2_subdev *sd,
+			  struct media_link *link,
+			  struct v4l2_subdev_format *source_fmt,
+			  struct v4l2_subdev_format *sink_fmt);
+
+struct mtk_cam_request *mtk_cam_get_req(struct mtk_cam_ctx *ctx,
+					unsigned int frame_seq_no);
+struct mtk_cam_request_stream_data *
+mtk_cam_get_req_s_data(struct mtk_cam_ctx *ctx, unsigned int pipe_id,
+		       unsigned int frame_seq_no);
+
+struct mtk_raw_pipeline *mtk_cam_dev_get_raw_pipeline(struct mtk_cam_device *cam,
+						      unsigned int id);
+
+struct mtk_raw_device *get_main_raw_dev(struct mtk_cam_device *cam,
+					struct mtk_raw_pipeline *pipe);
+struct mtk_raw_device *get_sub_raw_dev(struct mtk_cam_device *cam,
+				       struct mtk_raw_pipeline *pipe);
+struct mtk_raw_device *get_sub2_raw_dev(struct mtk_cam_device *cam,
+					struct mtk_raw_pipeline *pipe);
+
+int isp_composer_create_session(struct mtk_cam_ctx *ctx);
+void isp_composer_destroy_session(struct mtk_cam_ctx *ctx);
+
+#endif /*__MTK_CAM_H */
-- 
2.18.0
Re: [PATCH v1 05/10] media: platform: mediatek: add isp_7x camsys unit
Posted by CK Hu (胡俊光) 1 week ago
Hi, Shu-hsiang:

On Wed, 2024-10-09 at 19:15 +0800, Shu-hsiang Yang wrote:
> Introduces the top media device driver for the MediaTek ISP7X CAMSYS.
> The driver maintains the camera system, including sub-device management,
> DMA operations, and integration with the V4L2 framework. It handles
> request stream data, buffer management, and MediaTek-specific features,
> and pipeline management, streaming control, error handling mechanism.
> Additionally, it aggregates sub-drivers for the camera interface, raw
> and yuv pipelines.
> 
> Signed-off-by: Shu-hsiang Yang <Shu-hsiang.Yang@mediatek.com>
> ---

[snip]

> +static int isp_composer_init(struct mtk_cam_device *cam)
> +{
> +	struct device *dev = cam->dev;
> +	int ret;
> +
> +	ret = rproc_boot(cam->rproc_handle);
> +	if (ret) {
> +		dev_err(dev, "failed to rproc_boot\n");
> +		return ret;
> +	}
> +
> +	ret = scp_ipi_register(cam->scp, SCP_IPI_ISP_CMD,
> +			       isp_composer_handler, cam);
> +	if (ret) {
> +		dev_err(dev, "failed to register IPI cmd\n");
> +		return ret;
> +	}
> +
> +	ret = scp_ipi_register(cam->scp, SCP_IPI_ISP_FRAME,
> +			       isp_composer_handler, cam);

SCP_IPI_ISP_CMD and SCP_IPI_ISP_FRAME use different way to handler ack.
So do not use the common function isp_composer_handler to handler different command.
Use different handler function.

Regards,
CK

> +	if (ret) {
> +		dev_err(dev, "failed to register IPI frame\n");
> +		goto unreg_ipi_cmd;
> +	}
> +
> +	return 0;
> +
> +unreg_ipi_cmd:
> +	scp_ipi_unregister(cam->scp, SCP_IPI_ISP_CMD);
> +
> +	return ret;
> +}
> +

Re: [PATCH v1 05/10] media: platform: mediatek: add isp_7x camsys unit
Posted by CK Hu (胡俊光) 2 weeks ago
Hi, Shu-hsiang:

On Wed, 2024-10-09 at 19:15 +0800, Shu-hsiang Yang wrote:
> Introduces the top media device driver for the MediaTek ISP7X CAMSYS.
> The driver maintains the camera system, including sub-device management,
> DMA operations, and integration with the V4L2 framework. It handles
> request stream data, buffer management, and MediaTek-specific features,
> and pipeline management, streaming control, error handling mechanism.
> Additionally, it aggregates sub-drivers for the camera interface, raw
> and yuv pipelines.
> 
> Signed-off-by: Shu-hsiang Yang <Shu-hsiang.Yang@mediatek.com>
> ---

[snip]

> +struct mtk_cam_ctx *mtk_cam_start_ctx(struct mtk_cam_device *cam,
> +				      struct mtk_cam_video_device *node)
> +{
> +	struct mtk_cam_ctx *ctx = node->ctx;
> +	struct device *dev;
> +	struct v4l2_subdev **target_sd;
> +	int ret, i, is_first_ctx;
> +	struct media_entity *entity = &node->vdev.entity;
> +	struct media_graph graph;
> +
> +	dev_info(cam->dev, "%s:ctx(%d): triggered by %s\n",
> +		 __func__, ctx->stream_id, entity->name);
> +
> +	atomic_set(&ctx->enqueued_frame_seq_no, 0);
> +	ctx->composed_frame_seq_no = 0;
> +	ctx->dequeued_frame_seq_no = 0;
> +	atomic_set(&ctx->running_s_data_cnt, 0);
> +	init_completion(&ctx->session_complete);
> +	init_completion(&ctx->m2m_complete);
> +
> +	is_first_ctx = !cam->composer_cnt;
> +	if (is_first_ctx) {
> +		spin_lock(&cam->dma_processing_lock);
> +		cam->dma_processing_count = 0;
> +		spin_unlock(&cam->dma_processing_lock);
> +
> +		spin_lock(&cam->running_job_lock);
> +		cam->running_job_count = 0;
> +		spin_unlock(&cam->running_job_lock);
> +
> +		dev_info(cam->dev, "%s: power on camsys\n", __func__);
> +		ret = pm_runtime_resume_and_get(cam->dev);
> +		if (ret < 0) {
> +			dev_info(cam->dev, "%s: power on camsys failed\n",
> +				 __func__);
> +			return NULL;
> +		}
> +
> +		ret = isp_composer_init(cam);
> +		if (ret)
> +			goto fail_shutdown;
> +
> +		/* To catch camsys exception and trigger dump */
> +		if (cam->debug_fs)
> +			cam->debug_fs->ops->exp_reinit(cam->debug_fs);
> +	}
> +	cam->composer_cnt++;
> +	if (is_yuv_node(node->desc.id))
> +		dev = cam->raw.yuvs[0];
> +	else
> +		dev = cam->raw.devs[0];
> +
> +	ret = mtk_cam_working_buf_pool_init(ctx, dev);
> +	if (ret) {
> +		dev_info(cam->dev, "failed to reserve DMA memory:%d\n", ret);
> +		goto fail_uninit_composer;
> +	}
> +
> +	kthread_init_worker(&ctx->sensor_worker);
> +	ctx->sensor_worker_task = kthread_run(kthread_worker_fn,
> +					      &ctx->sensor_worker,
> +					      "sensor_worker-%d",
> +					      ctx->stream_id);
> +	if (IS_ERR(ctx->sensor_worker_task)) {
> +		dev_info(cam->dev,
> +			 "%s:ctx(%d): could not create sensor_worker_task\n",
> +			 __func__, ctx->stream_id);
> +		goto fail_release_buffer_pool;
> +	}
> +
> +	sched_set_fifo(ctx->sensor_worker_task);
> +
> +	ctx->composer_wq = alloc_ordered_workqueue(dev_name(cam->dev),
> +						   WQ_HIGHPRI | WQ_FREEZABLE);
> +	if (!ctx->composer_wq) {
> +		dev_info(cam->dev, "failed to alloc composer workqueue\n");
> +		goto fail_uninit_sensor_worker_task;
> +	}
> +
> +	ctx->frame_done_wq = alloc_ordered_workqueue(dev_name(cam->dev),
> +						     WQ_HIGHPRI | WQ_FREEZABLE);
> +	if (!ctx->frame_done_wq) {
> +		dev_info(cam->dev, "failed to alloc frame_done workqueue\n");
> +		goto fail_uninit_composer_wq;
> +	}
> +
> +	ret = media_pipeline_start(&entity->pads[0], &ctx->pipeline);
> +	if (ret) {
> +		dev_warn(cam->dev,
> +			 "%s:pipe(%d):failed in media_pipeline_start:%d\n",
> +			 __func__, node->uid.pipe_id, ret);
> +		goto fail_uninit_frame_done_wq;
> +	}
> +
> +	/* traverse to update used subdevs & number of nodes */
> +	i = 0;
> +	ret = media_graph_walk_init(&graph, entity->graph_obj.mdev);
> +	if (ret)
> +		goto fail_stop_pipeline;
> +
> +	media_graph_walk_start(&graph, entity);
> +	while ((entity = media_graph_walk_next(&graph))) {
> +		dev_dbg(cam->dev, "linked entity %s\n", entity->name);
> +
> +		target_sd = NULL;
> +
> +		switch (entity->function) {
> +		case MEDIA_ENT_F_IO_V4L:
> +			ctx->enabled_node_cnt++;
> +			break;
> +		case MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER: /* pipeline */
> +			if (i >= MAX_PIPES_PER_STREAM)
> +				goto fail_stop_pipeline;
> +			target_sd = ctx->pipe_subdevs + i;
> +			i++;
> +			break;
> +		case MEDIA_ENT_F_VID_IF_BRIDGE: /* seninf */
> +			target_sd = &ctx->seninf;
> +			break;
> +		case MEDIA_ENT_F_CAM_SENSOR:

This does not exist, so drop ctx->sensor.

Regards,
CK

> +			target_sd = &ctx->sensor;
> +			break;
> +		default:
> +			break;
> +		}
> +
> +		if (!target_sd)
> +			continue;
> +
> +		if (*target_sd) {
> +			dev_info(cam->dev, "duplicated subdevs!!!\n");
> +			goto fail_traverse_subdev;
> +		}
> +
> +		if (is_media_entity_v4l2_subdev(entity))
> +			*target_sd = media_entity_to_v4l2_subdev(entity);
> +	}
> +	media_graph_walk_cleanup(&graph);
> +
> +	return ctx;
> +
> +fail_traverse_subdev:
> +	media_graph_walk_cleanup(&graph);
> +fail_stop_pipeline:
> +	media_pipeline_stop(&entity->pads[0]);
> +fail_uninit_frame_done_wq:
> +	destroy_workqueue(ctx->frame_done_wq);
> +fail_uninit_composer_wq:
> +	destroy_workqueue(ctx->composer_wq);
> +fail_uninit_sensor_worker_task:
> +	kthread_stop(ctx->sensor_worker_task);
> +	ctx->sensor_worker_task = NULL;
> +fail_release_buffer_pool:
> +	mtk_cam_working_buf_pool_release(ctx, dev);
> +fail_uninit_composer:
> +	isp_composer_uninit(cam);
> +	cam->composer_cnt--;
> +fail_shutdown:
> +	if (is_first_ctx)
> +		rproc_shutdown(cam->rproc_handle);
> +
> +	return NULL;
> +}
> +

Re: [PATCH v1 05/10] media: platform: mediatek: add isp_7x camsys unit
Posted by CK Hu (胡俊光) 3 weeks ago
Hi, Shu-hsiang:

On Wed, 2024-10-09 at 19:15 +0800, Shu-hsiang Yang wrote:
> Introduces the top media device driver for the MediaTek ISP7X CAMSYS.
> The driver maintains the camera system, including sub-device management,
> DMA operations, and integration with the V4L2 framework. It handles
> request stream data, buffer management, and MediaTek-specific features,
> and pipeline management, streaming control, error handling mechanism.
> Additionally, it aggregates sub-drivers for the camera interface, raw
> and yuv pipelines.
> 
> Signed-off-by: Shu-hsiang Yang <Shu-hsiang.Yang@mediatek.com>
> ---

[snip]

> +/* flags of mtk_cam_request */
> +#define MTK_CAM_REQ_FLAG_SENINF_CHANGED			BIT(0)

MTK_CAM_REQ_FLAG_SENINF_CHANGED is never used, so drop it.

> +#define MTK_CAM_REQ_FLAG_SENINF_IMMEDIATE_UPDATE	BIT(1)
> +
> +/* flags of mtk_cam_request_stream_data */
> +#define MTK_CAM_REQ_S_DATA_FLAG_TG_FLASH		BIT(0)
> +
> +#define MTK_CAM_REQ_S_DATA_FLAG_META1_INDEPENDENT	BIT(1)
> +
> +#define MTK_CAM_REQ_S_DATA_FLAG_SINK_FMT_UPDATE		BIT(2)
> +/* Apply sensor mode and the timing is 1 vsync before */
> +#define MTK_CAM_REQ_S_DATA_FLAG_SENSOR_MODE_UPDATE_T1	BIT(3)

MTK_CAM_REQ_S_DATA_FLAG_SENSOR_MODE_UPDATE_T1 is useless, so drop it.

Regards,
CK

> +
> +#define MTK_CAM_REQ_S_DATA_FLAG_SENSOR_HDL_EN		BIT(4)
> +
> +#define MTK_CAM_REQ_S_DATA_FLAG_RAW_HDL_EN		BIT(5)
> +
> +#define MTK_CAM_REQ_S_DATA_FLAG_SENSOR_HDL_COMPLETE	BIT(6)
> +
> +#define MTK_CAM_REQ_S_DATA_FLAG_RAW_HDL_COMPLETE	BIT(7)
> +
> +#define MTK_CAM_REQ_S_DATA_FLAG_SENSOR_HDL_DELAYED	BIT(8)
> +

Re: [PATCH 05/10] media: platform: mediatek: add isp_7x camsys unit
Posted by Markus Elfring 3 weeks, 2 days ago
…
> +++ b/drivers/media/platform/mediatek/isp/isp_7x/camsys/mtk_cam.c
> @@ -0,0 +1,4168 @@
…
> +void mtk_cam_dev_req_try_queue(struct mtk_cam_device *cam)
> +{
…
> +	spin_lock(&cam->running_job_lock);
> +	job_count = cam->running_job_count;
> +	spin_unlock(&cam->running_job_lock);
…

Under which circumstances would you become interested to apply a statement
like “guard(spinlock)(&cam->running_job_lock);”?
https://elixir.bootlin.com/linux/v6.12-rc6/source/include/linux/spinlock.h#L559

Regards,
Markus
Re: [PATCH v1 05/10] media: platform: mediatek: add isp_7x camsys unit
Posted by CK Hu (胡俊光) 3 weeks, 2 days ago
Hi, Shu-hsiang:

On Wed, 2024-10-09 at 19:15 +0800, Shu-hsiang Yang wrote:
> Introduces the top media device driver for the MediaTek ISP7X CAMSYS.
> The driver maintains the camera system, including sub-device management,
> DMA operations, and integration with the V4L2 framework. It handles
> request stream data, buffer management, and MediaTek-specific features,
> and pipeline management, streaming control, error handling mechanism.
> Additionally, it aggregates sub-drivers for the camera interface, raw
> and yuv pipelines.
> 
> Signed-off-by: Shu-hsiang Yang <Shu-hsiang.Yang@mediatek.com>
> ---

[snip]

> +struct mtk_cam_request_stream_data *
> +mtk_cam_get_req_s_data(struct mtk_cam_ctx *ctx, unsigned int pipe_id,
> +		       unsigned int frame_seq_no)
> +
> +{
> +	struct mtk_cam_device *cam = ctx->cam;
> +	struct mtk_cam_request *req, *req_prev;
> +	struct mtk_cam_request_stream_data *req_stream_data;
> +	int i;
> +
> +	spin_lock(&cam->running_job_lock);
> +	list_for_each_entry_safe(req, req_prev, &cam->running_job_list, list) {
> +		if (req->pipe_used & (1 << pipe_id)) {
> +			for (i = 0; i < req->p_data[pipe_id].s_data_num; i++) {
> +				req_stream_data = &req->p_data[pipe_id].s_data[i];
> +				if (req_stream_data->frame_seq_no == frame_seq_no) {
> +					spin_unlock(&cam->running_job_lock);
> +					return req_stream_data;
> +				}
> +			}
> +		}
> +	}
> +	spin_unlock(&cam->running_job_lock);
> +
> +	return NULL;
> +}
> +
> +struct mtk_cam_request *mtk_cam_get_req(struct mtk_cam_ctx *ctx,
> +					unsigned int frame_seq_no)
> +{
> +	struct mtk_cam_request_stream_data *req_stream_data;
> +
> +	req_stream_data = mtk_cam_get_req_s_data(ctx, ctx->stream_id, frame_seq_no);

In some place, it is called 'stream_id'. In some place, it is called 'pipe_id'.
It's easy to confuse us that stream_id and pipe_id are different and the code readability is bad.
Use the unique name so that we would not get confused.

Regards,
CK

> +	if (!req_stream_data)
> +		return NULL;
> +
> +	return req_stream_data->req;
> +}
> +

Re: [PATCH v1 05/10] media: platform: mediatek: add isp_7x camsys unit
Posted by CK Hu (胡俊光) 4 weeks ago
Hi, Shu-hsiang:

On Wed, 2024-10-09 at 19:15 +0800, Shu-hsiang Yang wrote:
> Introduces the top media device driver for the MediaTek ISP7X CAMSYS.
> The driver maintains the camera system, including sub-device management,
> DMA operations, and integration with the V4L2 framework. It handles
> request stream data, buffer management, and MediaTek-specific features,
> and pipeline management, streaming control, error handling mechanism.
> Additionally, it aggregates sub-drivers for the camera interface, raw
> and yuv pipelines.
> 
> Signed-off-by: Shu-hsiang Yang <Shu-hsiang.Yang@mediatek.com>
> ---

[snip]

> +struct mtk_cam_device {
> +	struct device *dev;
> +
> +	struct v4l2_device v4l2_dev;
> +	struct v4l2_async_notifier notifier;
> +	struct media_device media_dev;
> +	void __iomem *base;
> +
> +	struct mtk_scp *scp;
> +	struct device *smem_dev;
> +	phandle rproc_phandle;

rproc_phandle is useless, so drop it.

> +	struct rproc *rproc_handle;
> +
> +	unsigned int composer_cnt;
> +
> +	unsigned int num_seninf_devices;
> +	unsigned int num_raw_devices;
> +	unsigned int num_larb_devices;

num_larb_devices is useless, so drop it.

Regards,
CK

> +
> +	/* raw_pipe controller subdev */
> +	struct mtk_raw raw;
> +	struct mutex queue_lock; /* protect queue request */
> +
> +	unsigned int max_stream_num;
> +	unsigned int streaming_ctx;
> +	unsigned int streaming_pipe;
> +	struct mtk_cam_ctx *ctxs;
> +
> +	/* request related */
> +	struct list_head pending_job_list;
> +	spinlock_t pending_job_lock; /* protect pending_job_list */
> +	struct list_head running_job_list;
> +	unsigned int running_job_count;
> +	spinlock_t running_job_lock; /* protect running_job_list */
> +
> +	/* standard v4l2 buffer control */
> +	struct list_head dma_pending;
> +	spinlock_t dma_pending_lock; /* protect dma_pending_list */
> +	struct list_head dma_processing;
> +	spinlock_t dma_processing_lock; /* protect dma_processing_list and dma_processing_count */
> +	unsigned int dma_processing_count;
> +
> +	struct mtk_cam_debug_fs *debug_fs;
> +	struct workqueue_struct *debug_wq;
> +	struct workqueue_struct *debug_exception_wq;
> +	wait_queue_head_t debug_exception_waitq;
> +};
> +

Re: [PATCH v1 05/10] media: platform: mediatek: add isp_7x camsys unit
Posted by CK Hu (胡俊光) 4 weeks, 1 day ago
Hi, Shu-hsiang:

On Wed, 2024-10-09 at 19:15 +0800, Shu-hsiang Yang wrote:
> Introduces the top media device driver for the MediaTek ISP7X CAMSYS.
> The driver maintains the camera system, including sub-device management,
> DMA operations, and integration with the V4L2 framework. It handles
> request stream data, buffer management, and MediaTek-specific features,
> and pipeline management, streaming control, error handling mechanism.
> Additionally, it aggregates sub-drivers for the camera interface, raw
> and yuv pipelines.
> 
> Signed-off-by: Shu-hsiang Yang <Shu-hsiang.Yang@mediatek.com>
> ---

[snip]

> +struct mtk_cam_device {
> +	struct device *dev;
> +
> +	struct v4l2_device v4l2_dev;
> +	struct v4l2_async_notifier notifier;
> +	struct media_device media_dev;
> +	void __iomem *base;
> +
> +	struct mtk_scp *scp;
> +	struct device *smem_dev;
> +	phandle rproc_phandle;
> +	struct rproc *rproc_handle;
> +
> +	unsigned int composer_cnt;
> +
> +	unsigned int num_seninf_devices;
> +	unsigned int num_raw_devices;
> +	unsigned int num_larb_devices;
> +
> +	/* raw_pipe controller subdev */
> +	struct mtk_raw raw;
> +	struct mutex queue_lock; /* protect queue request */
> +
> +	unsigned int max_stream_num;
> +	unsigned int streaming_ctx;
> +	unsigned int streaming_pipe;
> +	struct mtk_cam_ctx *ctxs;
> +
> +	/* request related */
> +	struct list_head pending_job_list;
> +	spinlock_t pending_job_lock; /* protect pending_job_list */
> +	struct list_head running_job_list;
> +	unsigned int running_job_count;
> +	spinlock_t running_job_lock; /* protect running_job_list */
> +
> +	/* standard v4l2 buffer control */
> +	struct list_head dma_pending;
> +	spinlock_t dma_pending_lock; /* protect dma_pending_list */
> +	struct list_head dma_processing;
> +	spinlock_t dma_processing_lock; /* protect dma_processing_list and dma_processing_count */
> +	unsigned int dma_processing_count;

I would like scp-related variable has the scp prefix.

struct list_head scp_dma_processing;
spinlock_t scp_dma_processing_lock;
unsigned int scp_dma_processing_count;

So it's easy to understand that scp_dma_processing_count's max value is 2.
One buffer is currently doing dma, and another one is waiting for dma. Both buffer are queued in scp.

Regards,
CK

> +
> +	struct mtk_cam_debug_fs *debug_fs;
> +	struct workqueue_struct *debug_wq;
> +	struct workqueue_struct *debug_exception_wq;
> +	wait_queue_head_t debug_exception_waitq;
> +};
> +

Re: [PATCH v1 05/10] media: platform: mediatek: add isp_7x camsys unit
Posted by CK Hu (胡俊光) 4 weeks ago
Hi, Shu-hsiang:

On Tue, 2024-10-29 at 15:03 +0800, CK Hu wrote:
> Hi, Shu-hsiang:
> 
> On Wed, 2024-10-09 at 19:15 +0800, Shu-hsiang Yang wrote:
> > Introduces the top media device driver for the MediaTek ISP7X CAMSYS.
> > The driver maintains the camera system, including sub-device management,
> > DMA operations, and integration with the V4L2 framework. It handles
> > request stream data, buffer management, and MediaTek-specific features,
> > and pipeline management, streaming control, error handling mechanism.
> > Additionally, it aggregates sub-drivers for the camera interface, raw
> > and yuv pipelines.
> > 
> > Signed-off-by: Shu-hsiang Yang <Shu-hsiang.Yang@mediatek.com>
> > ---
> 
> [snip]
> 
> > +struct mtk_cam_device {
> > +	struct device *dev;
> > +
> > +	struct v4l2_device v4l2_dev;
> > +	struct v4l2_async_notifier notifier;
> > +	struct media_device media_dev;
> > +	void __iomem *base;
> > +
> > +	struct mtk_scp *scp;
> > +	struct device *smem_dev;
> > +	phandle rproc_phandle;
> > +	struct rproc *rproc_handle;
> > +
> > +	unsigned int composer_cnt;
> > +
> > +	unsigned int num_seninf_devices;
> > +	unsigned int num_raw_devices;
> > +	unsigned int num_larb_devices;
> > +
> > +	/* raw_pipe controller subdev */
> > +	struct mtk_raw raw;
> > +	struct mutex queue_lock; /* protect queue request */
> > +
> > +	unsigned int max_stream_num;
> > +	unsigned int streaming_ctx;
> > +	unsigned int streaming_pipe;
> > +	struct mtk_cam_ctx *ctxs;
> > +
> > +	/* request related */
> > +	struct list_head pending_job_list;
> > +	spinlock_t pending_job_lock; /* protect pending_job_list */
> > +	struct list_head running_job_list;
> > +	unsigned int running_job_count;
> > +	spinlock_t running_job_lock; /* protect running_job_list */
> > +
> > +	/* standard v4l2 buffer control */
> > +	struct list_head dma_pending;
> > +	spinlock_t dma_pending_lock; /* protect dma_pending_list */
> > +	struct list_head dma_processing;
> > +	spinlock_t dma_processing_lock; /* protect dma_processing_list and dma_processing_count */
> > +	unsigned int dma_processing_count;
> 
> I would like scp-related variable has the scp prefix.
> 
> struct list_head scp_dma_processing;
> spinlock_t scp_dma_processing_lock;
> unsigned int scp_dma_processing_count;
> 
> So it's easy to understand that scp_dma_processing_count's max value is 2.
> One buffer is currently doing dma, and another one is waiting for dma. Both buffer are queued in scp.

Forget previous comment. After review the buffer control, I think the buffer list should be simplified.
dma_pending, dma_processing, using_buffer_list, composed_buffer_list, processing_buffer_list could be merge into one buf_list.
The buffer in buf_list has different status.
In init, the buffer is queued into driver and status is waiting.

buf_list-> buf0(waiting)-> buf1(waiting)-> buf2(waiting)-> buf3(waiting)-> buf4(waiting)

In mtk_cam_buf_try_queue(), use scp to generate cq buffer content of buf0 and buf1, so the status is scp_generate_cq.

buf_list-> buf0(scp_generate_cq)-> buf1(scp_generate_cq)-> buf2(waiting)-> buf3(waiting)-> buf4(waiting)

So the buf_entry is bound to buf0 and buf1, it's not necessary have using_buffer_list, composed_buffer_list, processing_buffer_list to manage buf_entry.

In isp_composer_handler_ack(), scp has finish generating cq buffer content, so the status is cq_ready.
In the meantime, use scp to generate cq buffer content of buf2.

buf_list-> buf0(cq_ready)-> buf1(scp_generate_cq)-> buf2(scp_generate_cq)-> buf3(waiting)-> buf4(waiting)

In mtk_camsys_raw_frame_start(), apply cq buffer to hardware, so the status is cq_apply

buf_list-> buf0(cq_apply)-> buf1(scp_generate_cq)-> buf2(scp_generate_cq)-> buf3(waiting)-> buf4(waiting)

In mtk_camsys_frame_done(), hardware has finished writing video into buffer, so the status is done

buf_list-> buf0(done)-> buf1(scp_generate_cq)-> buf2(scp_generate_cq)-> buf3(waiting)-> buf4(waiting)

In this design, just need one buf_list with status.
The code would be more simple.
Simple code would has less bug.
Maybe you could drop so many debug utility.

Regards,
CK


> 
> Regards,
> CK
> 
> > +
> > +	struct mtk_cam_debug_fs *debug_fs;
> > +	struct workqueue_struct *debug_wq;
> > +	struct workqueue_struct *debug_exception_wq;
> > +	wait_queue_head_t debug_exception_waitq;
> > +};
> > +
> 
> 
Re: [PATCH v1 05/10] media: platform: mediatek: add isp_7x camsys unit
Posted by CK Hu (胡俊光) 4 weeks, 1 day ago
Hi, Shu-hsiang:

On Wed, 2024-10-09 at 19:15 +0800, Shu-hsiang Yang wrote:
> Introduces the top media device driver for the MediaTek ISP7X CAMSYS.
> The driver maintains the camera system, including sub-device management,
> DMA operations, and integration with the V4L2 framework. It handles
> request stream data, buffer management, and MediaTek-specific features,
> and pipeline management, streaming control, error handling mechanism.
> Additionally, it aggregates sub-drivers for the camera interface, raw
> and yuv pipelines.
> 
> Signed-off-by: Shu-hsiang Yang <Shu-hsiang.Yang@mediatek.com>
> ---

[snip]

> +void mtk_cam_buf_try_queue(struct mtk_cam_ctx *ctx)
> +{
> +	struct mtk_cam_device *cam;
> +	struct mtk_cam_buffer *buf, *buf_prev;
> +	struct mtkcam_ipi_event event;
> +	struct mtkcam_ipi_session_cookie *session = &event.cookie;
> +	struct mtkcam_ipi_frame_info *frame_info = &event.frame_data;
> +	struct mtkcam_ipi_frame_param *frame_param;
> +	struct mtkcam_ipi_frame_param *frame_data;
> +	struct mtk_cam_working_buf_entry *buf_entry;
> +	struct list_head equeue_list;
> +	unsigned int processing_cnt, enque_cnt;
> +
> +	cam = ctx->cam;
> +	if (!cam->streaming_ctx) {
> +		dev_info(cam->dev, "streams are off\n");
> +		return;
> +	}
> +
> +	INIT_LIST_HEAD(&equeue_list);
> +
> +	spin_lock(&cam->dma_processing_lock);
> +	processing_cnt = cam->dma_processing_count;
> +	spin_unlock(&cam->dma_processing_lock);
> +
> +	enque_cnt = 0;
> +	spin_lock(&cam->dma_pending_lock);
> +	list_for_each_entry_safe(buf, buf_prev, &cam->dma_pending, list) {
> +		if (processing_cnt + enque_cnt >= MTK_CAM_MAX_PROCESSING_BUFS) {
> +			dev_dbg(cam->dev,
> +				"processing bufs are full, buf cnt(%d)\n",
> +				processing_cnt);
> +			break;
> +		}
> +		dev_dbg(cam->dev, "%s buf cnt(%d)\n",
> +			__func__, processing_cnt + enque_cnt);
> +
> +		enque_cnt++;
> +		list_del(&buf->list);
> +		list_add_tail(&buf->list, &equeue_list);
> +	}
> +	spin_unlock(&cam->dma_pending_lock);
> +
> +	if (!enque_cnt)
> +		return;
> +
> +	frame_param = kzalloc(sizeof(*frame_param), GFP_KERNEL);
> +	if (!frame_param)
> +		return;
> +
> +	list_for_each_entry_safe(buf, buf_prev, &equeue_list, list) {
> +		if (buf->state.estate == E_BUF_STATE_COMPOSED)

This would never happened.
buf in equeue_list is moved from cam->dma_pending and buf's state is E_BUF_STATE_QUEUED.
So drop this checking.

Regards,
CK

> +			continue;
> +
> +		memset(&event, 0, sizeof(event));
> +		event.cmd_id = CAM_CMD_FRAME;
> +		session->session_id = ctx->stream_id;
> +		/* prepare working buffer */
> +		buf_entry = mtk_cam_working_buf_get(ctx);
> +		if (!buf_entry) {
> +			dev_info(cam->dev,
> +				 "%s: No CQ buf availablle: enqueued_frame_seq_no:%d\n",
> +				 __func__, atomic_read(&ctx->enqueued_frame_seq_no));
> +			WARN_ON(1);
> +			goto EXIT;
> +		}
> +
> +		spin_lock(&ctx->using_buffer_list.lock);
> +		list_add_tail(&buf_entry->list_entry, &ctx->using_buffer_list.list);
> +		ctx->using_buffer_list.cnt++;
> +		spin_unlock(&ctx->using_buffer_list.lock);
> +
> +		spin_lock(&cam->dma_processing_lock);
> +		list_del(&buf->list);
> +		list_add_tail(&buf->list, &cam->dma_processing);
> +		cam->dma_processing_count++;
> +		spin_unlock(&cam->dma_processing_lock);
> +
> +		/* Prepare rp message */
> +		frame_info->cur_msgbuf_offset =
> +			buf_entry->msg_buffer.va -
> +			cam->ctxs[session->session_id].buf_pool.msg_buf_va;
> +		frame_info->cur_msgbuf_size = buf_entry->msg_buffer.size;
> +		frame_data = (struct mtkcam_ipi_frame_param *)buf_entry->msg_buffer.va;
> +		session->frame_no = atomic_inc_return(&ctx->enqueued_frame_seq_no);
> +
> +		if (mtk_cam_buf_config(cam, frame_param, buf)) {
> +			dev_err(cam->dev, "%s: Buffer config failed\n",	__func__);
> +			continue;
> +		}
> +		memcpy(frame_data, frame_param, sizeof(*frame_param));
> +		frame_data->cur_workbuf_offset =
> +			buf_entry->buffer.iova -
> +			cam->ctxs[session->session_id].buf_pool.working_buf_iova;
> +		frame_data->cur_workbuf_size = buf_entry->buffer.size;
> +
> +		if (ctx->pipe->res_config.bin_limit == BIN_AUTO)
> +			frame_data->raw_param.bin_flag = ctx->pipe->res_config.bin_enable;
> +		else
> +			frame_data->raw_param.bin_flag = ctx->pipe->res_config.bin_limit;
> +
> +		scp_ipi_send(cam->scp, SCP_IPI_ISP_FRAME, &event,
> +			     sizeof(event), MTK_CAM_IPI_SEND_TIMEOUT);
> +		buf->state.estate = E_BUF_STATE_COMPOSED;
> +	}
> +EXIT:
> +	kfree(frame_param);
> +}
> +

Re: [PATCH v1 05/10] media: platform: mediatek: add isp_7x camsys unit
Posted by CK Hu (胡俊光) 4 weeks, 1 day ago
Hi, Shu-hsiang:

On Wed, 2024-10-09 at 19:15 +0800, Shu-hsiang Yang wrote:
> Introduces the top media device driver for the MediaTek ISP7X CAMSYS.
> The driver maintains the camera system, including sub-device management,
> DMA operations, and integration with the V4L2 framework. It handles
> request stream data, buffer management, and MediaTek-specific features,
> and pipeline management, streaming control, error handling mechanism.
> Additionally, it aggregates sub-drivers for the camera interface, raw
> and yuv pipelines.
> 
> Signed-off-by: Shu-hsiang Yang <Shu-hsiang.Yang@mediatek.com>
> ---

[snip]

> +
> +/**
> + * MediaTek Universal Frame Buffer Compression (UFBC) buffer header
> + * Caller must follow the bit stream buffer size and length table buffer size.
> + *
> + * Header Size
> + * Fixed size of 4096 bytes. Caller SHOULD NOT edit it.
> + *
> + * Bit Stream Size
> + * Bit stream width must be aligned to 64 pixel.
> + */
> +
> +struct ufbc_buf_header {
> +	/** Describe image resolution, unit in pixel. */
> +	u32 width;
> +	/** Describe image resolution, unit in pixel. */
> +	u32 height;
> +	/** Describe UFBC data plane count, UFBC supports maximum 2 planes. */
> +	u32 plane_count;
> +	/** Describe the original image data bits per pixel of the given plane. */
> +	u32 bits_per_pixel[3];
> +
> +	/**
> +	 * Describe the offset of the given plane bit stream data in bytes,
> +	 * including header size.
> +	 */
> +	u32 bitstream_offset[3];
> +	/** Describe the bit stream data size in bytes of the given plane. */
> +	u32 bitstream_size[3];
> +	/** Describe the encoded data size in bytes of the given plane. */
> +	u32 bitstream_data_size[3];
> +
> +	/**
> +	 * Describe the offset of length table of the given plane, including
> +	 * header size.
> +	 */
> +	u32 table_offset[3];
> +	/** Describe the length table size of the given plane */
> +	u32 table_size[3];
> +	/** Describe the total buffer size, including buffer header. */
> +	u32 buffer_size;
> +};
> +
> +struct ufbc_buffer_header {
> +	union {
> +		struct ufbc_buf_header header;

'struct ufbc_buf_header' is never used, so drop it.

Regards,
CK

> +		u8 bits[4096];
> +	};
> +};
> +

Re: [PATCH v1 05/10] media: platform: mediatek: add isp_7x camsys unit
Posted by CK Hu (胡俊光) 1 month ago
Hi, Shu-hsiang:

On Wed, 2024-10-09 at 19:15 +0800, Shu-hsiang Yang wrote:
> Introduces the top media device driver for the MediaTek ISP7X CAMSYS.
> The driver maintains the camera system, including sub-device management,
> DMA operations, and integration with the V4L2 framework. It handles
> request stream data, buffer management, and MediaTek-specific features,
> and pipeline management, streaming control, error handling mechanism.
> Additionally, it aggregates sub-drivers for the camera interface, raw
> and yuv pipelines.
> 
> Signed-off-by: Shu-hsiang Yang <Shu-hsiang.Yang@mediatek.com>
> ---

[snip]

> +static void mtk_cam_config_raw_path(struct mtk_cam_device *cam,
> +				    struct mtkcam_ipi_frame_param *frame_param,
> +				    struct mtk_cam_buffer *buf)
> +{
> +	struct mtk_cam_video_device *node;
> +	struct mtk_raw_pipeline *raw_pipeline;
> +
> +	node = mtk_cam_vbq_to_vdev(buf->vbb.vb2_buf.vb2_queue);
> +	raw_pipeline = mtk_cam_dev_get_raw_pipeline(cam, node->uid.pipe_id);
> +	if (!raw_pipeline) {
> +		dev_err(cam->dev, "%s: cannot find raw_pipeline, pipe_id:%d\n",
> +			__func__, node->uid.pipe_id);
> +		return;
> +	}
> +
> +	if (raw_pipeline->res_config.raw_path == V4L2_MTK_CAM_RAW_PATH_SELECT_BPC)
> +		frame_param->raw_param.imgo_path_sel = MTKCAM_IPI_IMGO_AFTER_BPC;
> +	else if (raw_pipeline->res_config.raw_path == V4L2_MTK_CAM_RAW_PATH_SELECT_FUS)
> +		frame_param->raw_param.imgo_path_sel = MTKCAM_IPI_IMGO_AFTER_FUS;
> +	else if (raw_pipeline->res_config.raw_path == V4L2_MTK_CAM_RAW_PATH_SELECT_DGN)
> +		frame_param->raw_param.imgo_path_sel = MTKCAM_IPI_IMGO_AFTER_DGN;
> +	else if (raw_pipeline->res_config.raw_path == V4L2_MTK_CAM_RAW_PATH_SELECT_LSC)
> +		frame_param->raw_param.imgo_path_sel = MTKCAM_IPI_IMGO_AFTER_LSC;
> +	else if (raw_pipeline->res_config.raw_path == V4L2_MTK_CAM_RAW_PATH_SELECT_LTM)
> +		frame_param->raw_param.imgo_path_sel = MTKCAM_IPI_IMGO_AFTER_LTM;
> +	else
> +		/* un-processed raw frame */
> +		frame_param->raw_param.imgo_path_sel = MTKCAM_IPI_IMGO_UNPROCESSED;

This patch is too big, so let this patch support basic function first.
Let this patch support only MTKCAM_IPI_IMGO_UNPROCESSED,
and add other path by other patch. Maybe one patch for one path.

Regards,
CK


> +
> +	dev_dbg(cam->dev, "%s: node:%d fd:%d idx:%d raw_path(%d) ipi imgo_path_sel(%d))\n",
> +		__func__, node->desc.id, buf->vbb.request_fd, buf->vbb.vb2_buf.index,
> +		raw_pipeline->res_config.raw_path, frame_param->raw_param.imgo_path_sel);
> +}
> +
Re: [PATCH v1 05/10] media: platform: mediatek: add isp_7x camsys unit
Posted by Krzysztof Kozlowski 1 month ago
On 09/10/2024 13:15, Shu-hsiang Yang wrote:
> Introduces the top media device driver for the MediaTek ISP7X CAMSYS.
> The driver maintains the camera system, including sub-device management,
> DMA operations, and integration with the V4L2 framework. It handles
> request stream data, buffer management, and MediaTek-specific features,
> and pipeline management, streaming control, error handling mechanism.
> Additionally, it aggregates sub-drivers for the camera interface, raw
> and yuv pipelines.
> 
> Signed-off-by: Shu-hsiang Yang <Shu-hsiang.Yang@mediatek.com>

...

> +
> +static int mtk_cam_probe(struct platform_device *pdev)
> +{
> +	struct mtk_cam_device *cam_dev;
> +	struct device *dev = &pdev->dev;
> +	struct resource *res;
> +	int ret;
> +	unsigned int i;
> +
> +	dev_dbg(dev, "camsys | start %s\n", __func__);

NAK. Same issues.

> +
> +	/* initialize structure */
> +	cam_dev = devm_kzalloc(dev, sizeof(*cam_dev), GFP_KERNEL);
> +	if (!cam_dev)
> +		return -ENOMEM;
> +
> +	if (dma_set_mask_and_coherent(dev, DMA_BIT_MASK(34))) {
> +		dev_err(dev, "%s: No suitable DMA available\n", __func__);
> +		return -EIO;
> +	}
> +
> +	if (!dev->dma_parms) {
> +		dev->dma_parms =
> +			devm_kzalloc(dev, sizeof(*dev->dma_parms), GFP_KERNEL);
> +		if (!dev->dma_parms)
> +			return -ENOMEM;
> +	}
> +
> +	dma_set_max_seg_size(dev, UINT_MAX);
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	if (!res) {
> +		dev_err(dev, "failed to get mem\n");
> +		return -ENODEV;
> +	}
> +
> +	cam_dev->base = devm_ioremap_resource(dev, res);
> +	if (IS_ERR(cam_dev->base)) {
> +		dev_err(dev, "failed to map register base\n");
> +		return PTR_ERR(cam_dev->base);
> +	}
> +
> +	cam_dev->dev = dev;
> +	dev_set_drvdata(dev, cam_dev);
> +
> +	cam_dev->composer_cnt = 0;
> +	cam_dev->num_seninf_devices = 0;
> +
> +	cam_dev->max_stream_num = MTKCAM_SUBDEV_MAX;
> +	cam_dev->ctxs = devm_kcalloc(dev, cam_dev->max_stream_num,
> +				     sizeof(*cam_dev->ctxs), GFP_KERNEL);
> +	if (!cam_dev->ctxs)
> +		return -ENOMEM;
> +
> +	cam_dev->streaming_ctx = 0;
> +	for (i = 0; i < cam_dev->max_stream_num; i++)
> +		mtk_cam_ctx_init(cam_dev->ctxs + i, cam_dev, i);
> +
> +	cam_dev->running_job_count = 0;
> +	spin_lock_init(&cam_dev->pending_job_lock);
> +	spin_lock_init(&cam_dev->running_job_lock);
> +	INIT_LIST_HEAD(&cam_dev->pending_job_list);
> +	INIT_LIST_HEAD(&cam_dev->running_job_list);
> +
> +	cam_dev->dma_processing_count = 0;
> +	spin_lock_init(&cam_dev->dma_pending_lock);
> +	spin_lock_init(&cam_dev->dma_processing_lock);
> +	INIT_LIST_HEAD(&cam_dev->dma_pending);
> +	INIT_LIST_HEAD(&cam_dev->dma_processing);
> +
> +	mutex_init(&cam_dev->queue_lock);
> +
> +	pm_runtime_enable(dev);
> +
> +	ret = mtk_cam_of_rproc(cam_dev, pdev);
> +	if (ret)
> +		goto fail_destroy_mutex;
> +
> +	ret = register_sub_drivers(dev);
> +	if (ret) {
> +		dev_err(dev, "fail to register_sub_drivers\n");
> +		goto fail_destroy_mutex;
> +	}
> +
> +	/* register mtk_cam as all isp subdev async parent */
> +	cam_dev->notifier.ops = &mtk_cam_async_nf_ops;
> +	v4l2_async_nf_init(&cam_dev->notifier, &cam_dev->v4l2_dev);
> +	ret = mtk_cam_async_subdev_add(dev); /* wait all isp sub drivers */
> +	if (ret) {
> +		dev_err(dev, "%s failed mtk_cam_async_subdev_add\n", __func__);
> +		goto fail_unregister_sub_drivers;
> +	}
> +
> +	ret = v4l2_async_nf_register(&cam_dev->notifier);
> +	if (ret) {
> +		dev_err(dev, "%s async_nf_register ret:%d\n", __func__, ret);
> +		v4l2_async_nf_cleanup(&cam_dev->notifier);
> +		goto fail_unregister_sub_drivers;
> +	}
> +
> +	ret = mtk_cam_debug_fs_init(cam_dev);
> +	if (ret < 0)
> +		goto fail_unregister_async_nf;
> +
> +	dev_info(dev, "camsys | [%s] success\n", __func__);

NAK. Same issues.

> +
> +	return 0;
> +
> +fail_unregister_async_nf:
> +	v4l2_async_nf_unregister(&cam_dev->notifier);
> +
> +fail_unregister_sub_drivers:
> +	unregister_sub_drivers(dev);
> +
> +fail_destroy_mutex:
> +	mutex_destroy(&cam_dev->queue_lock);
> +
> +	return ret;
> +}
> +
> +static void mtk_cam_remove(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct mtk_cam_device *cam_dev = dev_get_drvdata(dev);
> +
> +	pm_runtime_disable(dev);
> +
> +	mtk_cam_debug_fs_deinit(cam_dev);
> +
> +	v4l2_async_nf_unregister(&cam_dev->notifier);
> +
> +	unregister_sub_drivers(dev);
> +
> +	mutex_destroy(&cam_dev->queue_lock);
> +}
> +
> +static const struct dev_pm_ops mtk_cam_pm_ops = {
> +	SET_RUNTIME_PM_OPS(mtk_cam_runtime_suspend, mtk_cam_runtime_resume,
> +			   NULL)
> +};
> +
> +static struct platform_driver mtk_cam_driver = {
> +	.probe   = mtk_cam_probe,
> +	.remove  = mtk_cam_remove,
> +	.driver  = {
> +		.name  = "mtk-cam",
> +		.of_match_table = of_match_ptr(mtk_cam_of_ids),

Same issues as in previous patch.

All my comments apply to all your patches in this thread.

> +		.pm     = &mtk_cam_pm_ops,
> +	}
> +};



Best regards,
Krzysztof
Re: [PATCH v1 05/10] media: platform: mediatek: add isp_7x camsys unit
Posted by CK Hu (胡俊光) 1 month, 1 week ago
Hi, Shu-hsiang:

On Wed, 2024-10-09 at 19:15 +0800, Shu-hsiang Yang wrote:
> Introduces the top media device driver for the MediaTek ISP7X CAMSYS.
> The driver maintains the camera system, including sub-device management,
> DMA operations, and integration with the V4L2 framework. It handles
> request stream data, buffer management, and MediaTek-specific features,
> and pipeline management, streaming control, error handling mechanism.
> Additionally, it aggregates sub-drivers for the camera interface, raw
> and yuv pipelines.
> 
> Signed-off-by: Shu-hsiang Yang <Shu-hsiang.Yang@mediatek.com>
> ---

[snip]

> +static int mtk_cam_probe(struct platform_device *pdev)
> +{
> +	struct mtk_cam_device *cam_dev;
> +	struct device *dev = &pdev->dev;
> +	struct resource *res;
> +	int ret;
> +	unsigned int i;
> +
> +	dev_dbg(dev, "camsys | start %s\n", __func__);
> +
> +	/* initialize structure */
> +	cam_dev = devm_kzalloc(dev, sizeof(*cam_dev), GFP_KERNEL);
> +	if (!cam_dev)
> +		return -ENOMEM;
> +
> +	if (dma_set_mask_and_coherent(dev, DMA_BIT_MASK(34))) {
> +		dev_err(dev, "%s: No suitable DMA available\n", __func__);
> +		return -EIO;
> +	}
> +
> +	if (!dev->dma_parms) {
> +		dev->dma_parms =
> +			devm_kzalloc(dev, sizeof(*dev->dma_parms), GFP_KERNEL);
> +		if (!dev->dma_parms)
> +			return -ENOMEM;
> +	}
> +
> +	dma_set_max_seg_size(dev, UINT_MAX);
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	if (!res) {
> +		dev_err(dev, "failed to get mem\n");
> +		return -ENODEV;
> +	}
> +
> +	cam_dev->base = devm_ioremap_resource(dev, res);

I can not find any where to write register of this device.
If so, I think we need not to probe this driver.
The rest software control can be setup by other driver.

Regards,
CK

> +	if (IS_ERR(cam_dev->base)) {
> +		dev_err(dev, "failed to map register base\n");
> +		return PTR_ERR(cam_dev->base);
> +	}
> +
> +	cam_dev->dev = dev;
> +	dev_set_drvdata(dev, cam_dev);
> +
> +	cam_dev->composer_cnt = 0;
> +	cam_dev->num_seninf_devices = 0;
> +
> +	cam_dev->max_stream_num = MTKCAM_SUBDEV_MAX;
> +	cam_dev->ctxs = devm_kcalloc(dev, cam_dev->max_stream_num,
> +				     sizeof(*cam_dev->ctxs), GFP_KERNEL);
> +	if (!cam_dev->ctxs)
> +		return -ENOMEM;
> +
> +	cam_dev->streaming_ctx = 0;
> +	for (i = 0; i < cam_dev->max_stream_num; i++)
> +		mtk_cam_ctx_init(cam_dev->ctxs + i, cam_dev, i);
> +
> +	cam_dev->running_job_count = 0;
> +	spin_lock_init(&cam_dev->pending_job_lock);
> +	spin_lock_init(&cam_dev->running_job_lock);
> +	INIT_LIST_HEAD(&cam_dev->pending_job_list);
> +	INIT_LIST_HEAD(&cam_dev->running_job_list);
> +
> +	cam_dev->dma_processing_count = 0;
> +	spin_lock_init(&cam_dev->dma_pending_lock);
> +	spin_lock_init(&cam_dev->dma_processing_lock);
> +	INIT_LIST_HEAD(&cam_dev->dma_pending);
> +	INIT_LIST_HEAD(&cam_dev->dma_processing);
> +
> +	mutex_init(&cam_dev->queue_lock);
> +
> +	pm_runtime_enable(dev);
> +
> +	ret = mtk_cam_of_rproc(cam_dev, pdev);
> +	if (ret)
> +		goto fail_destroy_mutex;
> +
> +	ret = register_sub_drivers(dev);
> +	if (ret) {
> +		dev_err(dev, "fail to register_sub_drivers\n");
> +		goto fail_destroy_mutex;
> +	}
> +
> +	/* register mtk_cam as all isp subdev async parent */
> +	cam_dev->notifier.ops = &mtk_cam_async_nf_ops;
> +	v4l2_async_nf_init(&cam_dev->notifier, &cam_dev->v4l2_dev);
> +	ret = mtk_cam_async_subdev_add(dev); /* wait all isp sub drivers */
> +	if (ret) {
> +		dev_err(dev, "%s failed mtk_cam_async_subdev_add\n", __func__);
> +		goto fail_unregister_sub_drivers;
> +	}
> +
> +	ret = v4l2_async_nf_register(&cam_dev->notifier);
> +	if (ret) {
> +		dev_err(dev, "%s async_nf_register ret:%d\n", __func__, ret);
> +		v4l2_async_nf_cleanup(&cam_dev->notifier);
> +		goto fail_unregister_sub_drivers;
> +	}
> +
> +	ret = mtk_cam_debug_fs_init(cam_dev);
> +	if (ret < 0)
> +		goto fail_unregister_async_nf;
> +
> +	dev_info(dev, "camsys | [%s] success\n", __func__);
> +
> +	return 0;
> +
> +fail_unregister_async_nf:
> +	v4l2_async_nf_unregister(&cam_dev->notifier);
> +
> +fail_unregister_sub_drivers:
> +	unregister_sub_drivers(dev);
> +
> +fail_destroy_mutex:
> +	mutex_destroy(&cam_dev->queue_lock);
> +
> +	return ret;
> +}
> +
Re: [PATCH v1 05/10] media: platform: mediatek: add isp_7x camsys unit
Posted by CK Hu (胡俊光) 1 week, 1 day ago
Hi, Shu-hsiang:

On Wed, 2024-10-16 at 11:43 +0800, CK Hu wrote:
> Hi, Shu-hsiang:
> 
> On Wed, 2024-10-09 at 19:15 +0800, Shu-hsiang Yang wrote:
> > Introduces the top media device driver for the MediaTek ISP7X CAMSYS.
> > The driver maintains the camera system, including sub-device management,
> > DMA operations, and integration with the V4L2 framework. It handles
> > request stream data, buffer management, and MediaTek-specific features,
> > and pipeline management, streaming control, error handling mechanism.
> > Additionally, it aggregates sub-drivers for the camera interface, raw
> > and yuv pipelines.
> > 
> > Signed-off-by: Shu-hsiang Yang <Shu-hsiang.Yang@mediatek.com>
> > ---
> 
> [snip]
> 
> > +static int mtk_cam_probe(struct platform_device *pdev)
> > +{
> > +	struct mtk_cam_device *cam_dev;
> > +	struct device *dev = &pdev->dev;
> > +	struct resource *res;
> > +	int ret;
> > +	unsigned int i;
> > +
> > +	dev_dbg(dev, "camsys | start %s\n", __func__);
> > +
> > +	/* initialize structure */
> > +	cam_dev = devm_kzalloc(dev, sizeof(*cam_dev), GFP_KERNEL);
> > +	if (!cam_dev)
> > +		return -ENOMEM;
> > +
> > +	if (dma_set_mask_and_coherent(dev, DMA_BIT_MASK(34))) {
> > +		dev_err(dev, "%s: No suitable DMA available\n", __func__);
> > +		return -EIO;
> > +	}
> > +
> > +	if (!dev->dma_parms) {
> > +		dev->dma_parms =
> > +			devm_kzalloc(dev, sizeof(*dev->dma_parms), GFP_KERNEL);
> > +		if (!dev->dma_parms)
> > +			return -ENOMEM;
> > +	}
> > +
> > +	dma_set_max_seg_size(dev, UINT_MAX);
> > +
> > +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> > +	if (!res) {
> > +		dev_err(dev, "failed to get mem\n");
> > +		return -ENODEV;
> > +	}
> > +
> > +	cam_dev->base = devm_ioremap_resource(dev, res);
> 
> I can not find any where to write register of this device.
> If so, I think we need not to probe this driver.
> The rest software control can be setup by other driver.

Ignore my previous comment. I find you need to power on/off this device.
So this probe is necessary.

Regards,
CK

> 
> Regards,
> CK
> 
> > +	if (IS_ERR(cam_dev->base)) {
> > +		dev_err(dev, "failed to map register base\n");
> > +		return PTR_ERR(cam_dev->base);
> > +	}
> > +
> > +	cam_dev->dev = dev;
> > +	dev_set_drvdata(dev, cam_dev);
> > +
> > +	cam_dev->composer_cnt = 0;
> > +	cam_dev->num_seninf_devices = 0;
> > +
> > +	cam_dev->max_stream_num = MTKCAM_SUBDEV_MAX;
> > +	cam_dev->ctxs = devm_kcalloc(dev, cam_dev->max_stream_num,
> > +				     sizeof(*cam_dev->ctxs), GFP_KERNEL);
> > +	if (!cam_dev->ctxs)
> > +		return -ENOMEM;
> > +
> > +	cam_dev->streaming_ctx = 0;
> > +	for (i = 0; i < cam_dev->max_stream_num; i++)
> > +		mtk_cam_ctx_init(cam_dev->ctxs + i, cam_dev, i);
> > +
> > +	cam_dev->running_job_count = 0;
> > +	spin_lock_init(&cam_dev->pending_job_lock);
> > +	spin_lock_init(&cam_dev->running_job_lock);
> > +	INIT_LIST_HEAD(&cam_dev->pending_job_list);
> > +	INIT_LIST_HEAD(&cam_dev->running_job_list);
> > +
> > +	cam_dev->dma_processing_count = 0;
> > +	spin_lock_init(&cam_dev->dma_pending_lock);
> > +	spin_lock_init(&cam_dev->dma_processing_lock);
> > +	INIT_LIST_HEAD(&cam_dev->dma_pending);
> > +	INIT_LIST_HEAD(&cam_dev->dma_processing);
> > +
> > +	mutex_init(&cam_dev->queue_lock);
> > +
> > +	pm_runtime_enable(dev);
> > +
> > +	ret = mtk_cam_of_rproc(cam_dev, pdev);
> > +	if (ret)
> > +		goto fail_destroy_mutex;
> > +
> > +	ret = register_sub_drivers(dev);
> > +	if (ret) {
> > +		dev_err(dev, "fail to register_sub_drivers\n");
> > +		goto fail_destroy_mutex;
> > +	}
> > +
> > +	/* register mtk_cam as all isp subdev async parent */
> > +	cam_dev->notifier.ops = &mtk_cam_async_nf_ops;
> > +	v4l2_async_nf_init(&cam_dev->notifier, &cam_dev->v4l2_dev);
> > +	ret = mtk_cam_async_subdev_add(dev); /* wait all isp sub drivers */
> > +	if (ret) {
> > +		dev_err(dev, "%s failed mtk_cam_async_subdev_add\n", __func__);
> > +		goto fail_unregister_sub_drivers;
> > +	}
> > +
> > +	ret = v4l2_async_nf_register(&cam_dev->notifier);
> > +	if (ret) {
> > +		dev_err(dev, "%s async_nf_register ret:%d\n", __func__, ret);
> > +		v4l2_async_nf_cleanup(&cam_dev->notifier);
> > +		goto fail_unregister_sub_drivers;
> > +	}
> > +
> > +	ret = mtk_cam_debug_fs_init(cam_dev);
> > +	if (ret < 0)
> > +		goto fail_unregister_async_nf;
> > +
> > +	dev_info(dev, "camsys | [%s] success\n", __func__);
> > +
> > +	return 0;
> > +
> > +fail_unregister_async_nf:
> > +	v4l2_async_nf_unregister(&cam_dev->notifier);
> > +
> > +fail_unregister_sub_drivers:
> > +	unregister_sub_drivers(dev);
> > +
> > +fail_destroy_mutex:
> > +	mutex_destroy(&cam_dev->queue_lock);
> > +
> > +	return ret;
> > +}
> > +
Re: [PATCH v1 05/10] media: platform: mediatek: add isp_7x camsys unit
Posted by CK Hu (胡俊光) 1 month, 2 weeks ago
Hi, Shu-hsiang:

On Wed, 2024-10-09 at 19:15 +0800, Shu-hsiang Yang wrote:
> Introduces the top media device driver for the MediaTek ISP7X CAMSYS.
> The driver maintains the camera system, including sub-device management,
> DMA operations, and integration with the V4L2 framework. It handles
> request stream data, buffer management, and MediaTek-specific features,
> and pipeline management, streaming control, error handling mechanism.
> Additionally, it aggregates sub-drivers for the camera interface, raw
> and yuv pipelines.
> 
> Signed-off-by: Shu-hsiang Yang <Shu-hsiang.Yang@mediatek.com>
> ---

[snip]

> +
> +static int mtk_cam_fill_img_buf(struct mtkcam_ipi_img_output *img_out,
> +				const struct v4l2_format *f, dma_addr_t daddr)
> +{
> +	u32 pixelformat = f->fmt.pix_mp.pixelformat;
> +	u32 width = f->fmt.pix_mp.width;
> +	u32 height = f->fmt.pix_mp.height;
> +	const struct v4l2_plane_pix_format *plane = &f->fmt.pix_mp.plane_fmt[0];
> +	u32 stride = plane->bytesperline;
> +	u32 aligned_width;
> +	unsigned int addr_offset = 0;
> +	int i;
> +
> +	if (is_mtk_format(pixelformat)) {
> +		const struct mtk_format_info *info;
> +
> +		info = mtk_format_info(pixelformat);
> +		if (!info)
> +			return -EINVAL;
> +
> +		aligned_width = stride / info->bpp[0];
> +		if (info->mem_planes == 1) {
> +			if (is_yuv_ufo(pixelformat)) {
> +				aligned_width = ALIGN(width, 64);
> +				img_out->buf[0][0].iova = daddr;
> +				img_out->fmt.stride[0] = aligned_width * info->bit_r_num
> +							 / info->bit_r_den;
> +				img_out->buf[0][0].size = img_out->fmt.stride[0] * height;
> +				img_out->buf[0][0].size += img_out->fmt.stride[0] * height / 2;
> +				img_out->buf[0][0].size += ALIGN((aligned_width / 64), 8) * height;
> +				img_out->buf[0][0].size += ALIGN((aligned_width / 64), 8) * height
> +							   / 2;
> +				img_out->buf[0][0].size += sizeof(struct ufbc_buffer_header);
> +
> +				pr_debug("plane:%d stride:%d plane_size:%d addr:0x%lx\n",
> +					 0, img_out->fmt.stride[0], img_out->buf[0][0].size,
> +					 (unsigned long)img_out->buf[0][0].iova);
> +			} else if (is_raw_ufo(pixelformat)) {
> +				aligned_width = ALIGN(width, 64);
> +				img_out->buf[0][0].iova = daddr;
> +				img_out->fmt.stride[0] = aligned_width * info->bit_r_num /
> +							 info->bit_r_den;
> +				img_out->buf[0][0].size = img_out->fmt.stride[0] * height;
> +				img_out->buf[0][0].size += ALIGN((aligned_width / 64), 8) * height;
> +				img_out->buf[0][0].size += sizeof(struct ufbc_buffer_header);
> +
> +				pr_debug("plane:%d stride:%d plane_size:%d addr:0x%lx\n",
> +					 0, img_out->fmt.stride[0],
> +					 img_out->buf[0][0].size,
> +					 (unsigned long)img_out->buf[0][0].iova);
> +			} else {
> +				for (i = 0; i < info->comp_planes; i++) {
> +					unsigned int hdiv = (i == 0) ? 1 : info->hdiv;
> +					unsigned int vdiv = (i == 0) ? 1 : info->vdiv;
> +
> +					img_out->buf[0][i].iova = daddr + addr_offset;
> +					img_out->fmt.stride[i] = info->bpp[i] *
> +						DIV_ROUND_UP(aligned_width, hdiv);
> +					img_out->buf[0][i].size = img_out->fmt.stride[i]
> +						* DIV_ROUND_UP(height, vdiv);
> +					addr_offset += img_out->buf[0][i].size;
> +
> +					pr_debug("plane:%d stride:%d plane_size:%d addr:0x%lx\n",
> +						 i, img_out->fmt.stride[i],
> +						 img_out->buf[0][i].size,
> +						 (unsigned long)img_out->buf[0][i].iova);
> +				}
> +			}
> +		} else {
> +			pr_debug("do not support non contiguous mplane\n");
> +		}
> +	} else {
> +		const struct v4l2_format_info *info;
> +
> +		info = v4l2_format_info(pixelformat);
> +		if (!info)
> +			return -EINVAL;
> +
> +		aligned_width = stride / info->bpp[0];
> +		if (info->mem_planes == 1) {
> +			for (i = 0; i < info->comp_planes; i++) {

This for-loop is identical with the for-loop in is_mtk_format-is-true block.
So the checking of is_mtk_format is redundant.

Regards,
CK

> +				unsigned int hdiv = (i == 0) ? 1 : info->hdiv;
> +				unsigned int vdiv = (i == 0) ? 1 : info->vdiv;
> +
> +				img_out->buf[0][i].iova = daddr + addr_offset;
> +				img_out->fmt.stride[i] = info->bpp[i] *
> +					DIV_ROUND_UP(aligned_width, hdiv);
> +				img_out->buf[0][i].size = img_out->fmt.stride[i]
> +					* DIV_ROUND_UP(height, vdiv);
> +				addr_offset += img_out->buf[0][i].size;
> +
> +				pr_debug("stride:%d plane_size:%d addr:0x%lx\n",
> +					 img_out->fmt.stride[i],
> +					 img_out->buf[0][i].size,
> +					 (unsigned long)img_out->buf[0][i].iova);
> +			}
> +		} else {
> +			pr_debug("do not support non contiguous mplane\n");
> +		}
> +	}
> +
> +	return 0;
> +}
> +