diff mbox series

[v1,04/10] media: platform: mediatek: add isp_7x cam-raw unit

Message ID 20241009111551.27052-5-Shu-hsiang.Yang@mediatek.com (mailing list archive)
State New, archived
Headers show
Series Add MediaTek ISP7 camera system driver | expand

Commit Message

Shu-hsiang Yang Oct. 9, 2024, 11:15 a.m. UTC
Introduces the ISP pipeline driver for the MediaTek ISP raw and yuv
modules. Key functionalities include data processing, V4L2 integration,
resource management, debug support, and various control operations.
Additionally, IRQ handling, platform device management, and MediaTek
ISP DMA format support are also included.

Signed-off-by: Shu-hsiang Yang <Shu-hsiang.Yang@mediatek.com>
---
 .../mediatek/isp/isp_7x/camsys/mtk_cam-raw.c  | 5359 +++++++++++++++++
 .../mediatek/isp/isp_7x/camsys/mtk_cam-raw.h  |  325 +
 .../isp/isp_7x/camsys/mtk_cam-raw_debug.c     |  403 ++
 .../isp/isp_7x/camsys/mtk_cam-raw_debug.h     |   39 +
 .../isp_7x/camsys/mtk_camera-v4l2-controls.h  |   65 +
 5 files changed, 6191 insertions(+)
 create mode 100644 drivers/media/platform/mediatek/isp/isp_7x/camsys/mtk_cam-raw.c
 create mode 100644 drivers/media/platform/mediatek/isp/isp_7x/camsys/mtk_cam-raw.h
 create mode 100644 drivers/media/platform/mediatek/isp/isp_7x/camsys/mtk_cam-raw_debug.c
 create mode 100644 drivers/media/platform/mediatek/isp/isp_7x/camsys/mtk_cam-raw_debug.h
 create mode 100644 drivers/media/platform/mediatek/isp/isp_7x/camsys/mtk_camera-v4l2-controls.h

Comments

AngeloGioacchino Del Regno Oct. 10, 2024, 12:49 p.m. UTC | #1
Il 09/10/24 13:15, Shu-hsiang Yang ha scritto:
> Introduces the ISP pipeline driver for the MediaTek ISP raw and yuv
> modules. Key functionalities include data processing, V4L2 integration,
> resource management, debug support, and various control operations.
> Additionally, IRQ handling, platform device management, and MediaTek
> ISP DMA format support are also included.
> 
> Signed-off-by: Shu-hsiang Yang <Shu-hsiang.Yang@mediatek.com>
> ---
>   .../mediatek/isp/isp_7x/camsys/mtk_cam-raw.c  | 5359 +++++++++++++++++
>   .../mediatek/isp/isp_7x/camsys/mtk_cam-raw.h  |  325 +
>   .../isp/isp_7x/camsys/mtk_cam-raw_debug.c     |  403 ++
>   .../isp/isp_7x/camsys/mtk_cam-raw_debug.h     |   39 +
>   .../isp_7x/camsys/mtk_camera-v4l2-controls.h  |   65 +
>   5 files changed, 6191 insertions(+)
>   create mode 100644 drivers/media/platform/mediatek/isp/isp_7x/camsys/mtk_cam-raw.c
>   create mode 100644 drivers/media/platform/mediatek/isp/isp_7x/camsys/mtk_cam-raw.h
>   create mode 100644 drivers/media/platform/mediatek/isp/isp_7x/camsys/mtk_cam-raw_debug.c
>   create mode 100644 drivers/media/platform/mediatek/isp/isp_7x/camsys/mtk_cam-raw_debug.h
>   create mode 100644 drivers/media/platform/mediatek/isp/isp_7x/camsys/mtk_camera-v4l2-controls.h
> 
> diff --git a/drivers/media/platform/mediatek/isp/isp_7x/camsys/mtk_cam-raw.c b/drivers/media/platform/mediatek/isp/isp_7x/camsys/mtk_cam-raw.c
> new file mode 100644
> index 000000000000..c025f53c952d
> --- /dev/null
> +++ b/drivers/media/platform/mediatek/isp/isp_7x/camsys/mtk_cam-raw.c
> @@ -0,0 +1,5359 @@
> +// SPDX-License-Identifier: GPL-2.0
> +//
> +// Copyright (c) 2022 MediaTek Inc.
> +
> +#include <linux/clk.h>
> +#include <linux/interrupt.h>
> +#include <linux/iopoll.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/of_platform.h>
> +#include <linux/platform_device.h>
> +#include <linux/pm_runtime.h>
> +#include <linux/vmalloc.h>
> +#include <linux/videodev2.h>
> +#include <linux/suspend.h>
> +#include <linux/rtc.h>
> +
> +#include <media/v4l2-device.h>
> +#include <media/v4l2-event.h>
> +#include <media/v4l2-ioctl.h>
> +#include <media/v4l2-subdev.h>
> +
> +#include <soc/mediatek/smi.h>
> +
> +#include "mtk_cam.h"
> +#include "mtk_cam-feature.h"
> +#include "mtk_cam-raw.h"
> +
> +#include "mtk_cam-regs-mt8188.h"
> +
> +#include "mtk_cam-video.h"
> +#include "mtk_cam-seninf-if.h"
> +#include "mtk_camera-v4l2-controls.h"
> +
> +#include "mtk_cam-dmadbg.h"
> +#include "mtk_cam-raw_debug.h"
> +
> +static unsigned int debug_raw;
> +module_param(debug_raw, uint, 0644);
> +MODULE_PARM_DESC(debug_raw, "activates debug info");
> +
> +static int debug_raw_num = -1;
> +module_param(debug_raw_num, int, 0644);
> +MODULE_PARM_DESC(debug_raw_num, "debug: num of used raw devices");
> +
> +static int debug_pixel_mode = -1;
> +module_param(debug_pixel_mode, int, 0644);
> +MODULE_PARM_DESC(debug_pixel_mode, "debug: pixel mode");
> +
> +static int debug_clk_idx = -1;
> +module_param(debug_clk_idx, int, 0644);
> +MODULE_PARM_DESC(debug_clk_idx, "debug: clk idx");
> +
> +static int debug_dump_fbc;
> +module_param(debug_dump_fbc, int, 0644);
> +MODULE_PARM_DESC(debug_dump_fbc, "debug: dump fbc");
> +
In addition to the first review that I gave you on patch [02/10]: please drop
all those module parameters. If you want debug switches, use debugfs instead.

Regards,
Angelo
CK Hu (胡俊光) Oct. 11, 2024, 2:23 a.m. UTC | #2
Hi, Shu-hsiang:

On Wed, 2024-10-09 at 19:15 +0800, Shu-hsiang Yang wrote:
> Introduces the ISP pipeline driver for the MediaTek ISP raw and yuv
> modules. Key functionalities include data processing, V4L2 integration,
> resource management, debug support, and various control operations.
> Additionally, IRQ handling, platform device management, and MediaTek
> ISP DMA format support are also included.
> 
> Signed-off-by: Shu-hsiang Yang <Shu-hsiang.Yang@mediatek.com>
> ---
>  .../mediatek/isp/isp_7x/camsys/mtk_cam-raw.c  | 5359 +++++++++++++++++
>  .../mediatek/isp/isp_7x/camsys/mtk_cam-raw.h  |  325 +
>  .../isp/isp_7x/camsys/mtk_cam-raw_debug.c     |  403 ++
>  .../isp/isp_7x/camsys/mtk_cam-raw_debug.h     |   39 +
>  .../isp_7x/camsys/mtk_camera-v4l2-controls.h  |   65 +

Without debug, this driver could still work, so separate debug function to an independent patch.
It seems yuv is an independent function. If so, separate yuv function to an independent patch.

Regards,
CK
CK Hu (胡俊光) Oct. 11, 2024, 2:55 a.m. UTC | #3
Hi, Shu-hsiang:

On Wed, 2024-10-09 at 19:15 +0800, Shu-hsiang Yang wrote:
> Introduces the ISP pipeline driver for the MediaTek ISP raw and yuv
> modules. Key functionalities include data processing, V4L2 integration,
> resource management, debug support, and various control operations.
> Additionally, IRQ handling, platform device management, and MediaTek
> ISP DMA format support are also included.
> 
> Signed-off-by: Shu-hsiang Yang <Shu-hsiang.Yang@mediatek.com>
> ---

[snip]

> +
> +static void set_fifo_threshold(void __iomem *dma_base)
> +{
> +	u32 fifo_size = 0;
> +
> +	fifo_size = readl_relaxed(dma_base + DMA_OFFSET_CON0) & 0xFFF;
> +	writel_relaxed((0x1 << 28) |
> +		       ((fifo_size * 1 / 4) & 0xFFF) << 16 |
> +		       ((fifo_size * 1 / 8) & 0xFFF),
> +		       dma_base + DMA_OFFSET_CON1);

I can not find definition of DMA_OFFSET_CON1 in this patch, but I find it in [6/10] patch.
Move the definition to this patch.
And when you add this file, make sure you could build it successfully.

Regards,
CK

> +	writel_relaxed((0x1 << 28) |
> +		       ((fifo_size * 1 / 2) & 0xFFF) << 16 |
> +		       ((fifo_size * 3 / 8) & 0xFFF),
> +		       dma_base + DMA_OFFSET_CON2);
> +	writel_relaxed((0x1 << 31) |
> +		       ((fifo_size * 2 / 3) & 0xFFF) << 16 |
> +		       ((fifo_size * 13 / 24) & 0xFFF),
> +		       dma_base + DMA_OFFSET_CON3);
> +	writel_relaxed((0x1 << 31) |
> +		       ((fifo_size * 3 / 8) & 0xFFF) << 16 |
> +		       ((fifo_size * 1 / 4) & 0xFFF),
> +		       dma_base + DMA_OFFSET_CON4);
> +}
> +
CK Hu (胡俊光) Oct. 11, 2024, 5:40 a.m. UTC | #4
Hi, Shu-hsiang:

On Wed, 2024-10-09 at 19:15 +0800, Shu-hsiang Yang wrote:
> Introduces the ISP pipeline driver for the MediaTek ISP raw and yuv
> modules. Key functionalities include data processing, V4L2 integration,
> resource management, debug support, and various control operations.
> Additionally, IRQ handling, platform device management, and MediaTek
> ISP DMA format support are also included.
> 
> Signed-off-by: Shu-hsiang Yang <Shu-hsiang.Yang@mediatek.com>
> ---

[snip]

> +
> +static irqreturn_t mtk_thread_irq_raw(int irq, void *data)
> +{
> +	struct mtk_raw_device *raw_dev = (struct mtk_raw_device *)data;
> +	struct mtk_camsys_irq_info irq_info;
> +
> +	if (unlikely(atomic_cmpxchg(&raw_dev->is_fifo_overflow, 1, 0)))
> +		dev_info(raw_dev->dev, "msg fifo overflow\n");
> +
> +	while (kfifo_len(&raw_dev->msg_fifo) >= sizeof(irq_info)) {
> +		int len = kfifo_out(&raw_dev->msg_fifo, &irq_info, sizeof(irq_info));
> +
> +		WARN_ON(len != sizeof(irq_info));
> +
> +		dev_dbg(raw_dev->dev, "ts=%llu irq_type %d, req:%d/%d\n",
> +			irq_info.ts_ns, irq_info.irq_type,
> +			irq_info.frame_idx_inner, irq_info.frame_idx);
> +
> +		/* error case */
> +		if (unlikely(irq_info.irq_type == (1 << CAMSYS_IRQ_ERROR))) {
> +			raw_handle_error(raw_dev, &irq_info);
> +			continue;
> +		}
> +
> +		/* normal case */
> +		/* inform interrupt information to camsys controller */
> +		mtk_camsys_isr_event(raw_dev->cam, CAMSYS_ENGINE_RAW,
> +				     raw_dev->id, &irq_info);

mtk_camsys_isr_event() is not defined in this patch but defined in [8/10] patch.
This is weird to use a function defined in future.
Define this function in this patch or previous patch.

Regards,
CK

> +	}
> +
> +	return IRQ_HANDLED;
> +}
> +
CK Hu (胡俊光) Oct. 11, 2024, 6:03 a.m. UTC | #5
Hi, Shu-hsiang:

On Wed, 2024-10-09 at 19:15 +0800, Shu-hsiang Yang wrote:
> Introduces the ISP pipeline driver for the MediaTek ISP raw and yuv
> modules. Key functionalities include data processing, V4L2 integration,
> resource management, debug support, and various control operations.
> Additionally, IRQ handling, platform device management, and MediaTek
> ISP DMA format support are also included.
> 
> Signed-off-by: Shu-hsiang Yang <Shu-hsiang.Yang@mediatek.com>
> ---

[snip]

> +
> +/* feature mask to categorize all raw functions */
> +#define MTK_CAM_FEATURE_HDR_MASK		0x0000000F
> +#define MTK_CAM_FEATURE_SUBSAMPLE_MASK		0x000000F0
> +#define MTK_CAM_FEATURE_OFFLINE_M2M_MASK	0x00000100
> +#define MTK_CAM_FEATURE_PURE_OFFLINE_M2M_MASK	0x00000200

It seems that M2M is not basic function. It's an advanced function, so separate M2M related code to an independent patch.
Make the first patch as simple as possible.

Regards,
CK

> +
> +enum raw_function_id {
> +	/* bit [0~3] hdr */
> +	/* bit [4~7] fps */
> +	/* bit [8~9] m2m */
> +	OFFLINE_M2M			= (1 << 8),
> +	PURE_OFFLINE_M2M		= (1 << 9),
> +	RAW_FUNCTION_END		= 0xF0000000,
> +};
> +
Krzysztof Kozlowski Oct. 22, 2024, 5:43 a.m. UTC | #6
On 09/10/2024 13:15, Shu-hsiang Yang wrote:
> Introduces the ISP pipeline driver for the MediaTek ISP raw and yuv
> modules. Key functionalities include data processing, V4L2 integration,
> resource management, debug support, and various control operations.
> Additionally, IRQ handling, platform device management, and MediaTek
> ISP DMA format support are also included.
> 
> Signed-off-by: Shu-hsiang Yang <Shu-hsiang.Yang@mediatek.com>

...

> +
> +static int mtk_yuv_of_probe(struct platform_device *pdev,
> +			    struct mtk_yuv_device *drvdata)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct resource *res;
> +	int irq, ret;
> +	int n_clks;
> +
> +	ret = of_property_read_u32(dev->of_node, "mediatek,cam-id",
> +				   &drvdata->id);
> +	if (ret) {
> +		dev_dbg(dev, "missing camid property\n");

Debug? Or error?

> +		return ret;
> +	}
> +
> +	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);
> +
> +	/* base outer register */
> +	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "base");
> +	if (!res) {
> +		dev_err(dev, "failed to get mem\n");
> +		return -ENODEV;
> +	}
> +
> +	drvdata->base = devm_ioremap_resource(dev, res);
> +	if (IS_ERR(drvdata->base)) {
> +		dev_dbg(dev, "failed to map register base\n");

Dbg? What?

> +		return PTR_ERR(drvdata->base);
> +	}
> +
> +	irq = platform_get_irq(pdev, 0);
> +	if (irq < 0) {
> +		dev_err(dev, "failed to get irq\n");
> +		return -ENODEV;
> +	}
> +
> +	ret = devm_request_irq(dev, irq, mtk_irq_yuv, 0,
> +			       dev_name(dev), drvdata);
> +	if (ret) {
> +		dev_err(dev, "failed to request irq=%d\n", irq);
> +		return ret;
> +	}
> +	dev_dbg(dev, "registered irq=%d\n", irq);

Drop

> +
> +	n_clks = devm_clk_bulk_get_all(dev, &drvdata->clk_b);
> +	if (n_clks < 0) {
> +		dev_err(dev, "failed to devm_clk_bulk_get_all=%d\n", n_clks);

Syntax is: return dev_err_probe()

> +		return n_clks;
> +	}
> +
> +	drvdata->num_clks = n_clks;
> +	dev_info(dev, "clk_num:%d\n", drvdata->num_clks);

Drop

> +
> +#ifdef CONFIG_PM_SLEEP
> +	drvdata->pm_notifier.notifier_call = yuv_pm_notifier;
> +	ret = register_pm_notifier(&drvdata->pm_notifier);
> +	if (ret) {
> +		dev_err(dev, "failed to register notifier block.\n");
> +		return ret;
> +	}
> +#endif
> +
> +	return 0;
> +}
> +
> +static int mtk_yuv_probe(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct mtk_yuv_device *drvdata;
> +	struct v4l2_subdev *sd;
> +	int ret;
> +
> +	dev_dbg(dev, "camsys | start %s\n", __func__);

NAK

> +
> +	drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL);
> +	if (!drvdata)
> +		return -ENOMEM;
> +
> +	drvdata->dev = dev;
> +	dev_set_drvdata(dev, drvdata);
> +
> +	ret = mtk_yuv_of_probe(pdev, drvdata);
> +	if (ret) {
> +		dev_info(dev, "mtk_yuv_of_probe failed\n");

NAK. Neither info nor proper for memory allocation errors. Drop.

> +		return ret;
> +	}
> +
> +	/* register yuv as mtk_cam async child */
> +	sd = &drvdata->subdev;
> +	v4l2_subdev_init(sd, &mtk_raw_subdev_ops);
> +	sd->internal_ops = &mtk_raw_subdev_internal_ops;
> +	snprintf(sd->name, sizeof(sd->name), "%s",
> +		 of_node_full_name(dev->of_node));
> +	sd->dev = dev;
> +	sd->owner = THIS_MODULE;
> +
> +	ret = v4l2_async_register_subdev(sd);
> +	if (ret) {
> +		dev_err(dev, "%s failed on async_register_subdev\n", __func__);
> +		return ret;
> +	}
> +
> +	pm_runtime_enable(dev);
> +
> +	dev_info(dev, "camsys | [%s] success\n", __func__);

NAK

> +
> +	return 0;
> +}
> +
> +static void mtk_yuv_remove(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct mtk_yuv_device *yuv_dev = dev_get_drvdata(dev);
> +	struct v4l2_subdev *sd = &yuv_dev->subdev;
> +
> +	dev_dbg(dev, "camsys | start %s\n", __func__);

NAK

> +
> +	unregister_pm_notifier(&yuv_dev->pm_notifier);
> +
> +	pm_runtime_disable(dev);
> +
> +	v4l2_async_unregister_subdev(sd);
> +}
> +
> +/* driver for yuv part */
> +static int mtk_yuv_runtime_suspend(struct device *dev)
> +{
> +	struct mtk_yuv_device *drvdata = dev_get_drvdata(dev);
> +
> +	dev_info(dev, "%s:disable clock\n", __func__);

NAK

> +
> +	clk_bulk_disable_unprepare(drvdata->num_clks, drvdata->clk_b);
> +
> +	return 0;
> +}
> +
> +static int mtk_yuv_runtime_resume(struct device *dev)
> +{
> +	struct mtk_yuv_device *drvdata = dev_get_drvdata(dev);
> +	int ret;
> +
> +	dev_info(dev, "%s:enable clock\n", __func__);

NAK. Not even dev_dbg. Please don't ever post such code.


> +
> +	ret = clk_bulk_prepare_enable(drvdata->num_clks, drvdata->clk_b);
> +	if (ret) {
> +		dev_info(dev, "failed at clk_bulk_prepare_enable, ret = %d\n", ret);
> +		clk_bulk_disable_unprepare(drvdata->num_clks, drvdata->clk_b);
> +		return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +static const struct dev_pm_ops mtk_yuv_pm_ops = {
> +	SET_RUNTIME_PM_OPS(mtk_yuv_runtime_suspend, mtk_yuv_runtime_resume,
> +			   NULL)
> +};
> +
> +static const struct of_device_id mtk_yuv_of_ids[] = {
> +	{.compatible = "mediatek,cam-yuv",},
> +	{}
> +};
> +MODULE_DEVICE_TABLE(of, mtk_yuv_of_ids);
> +
> +struct platform_driver mtk_cam_yuv_driver = {
> +	.probe   = mtk_yuv_probe,
> +	.remove  = mtk_yuv_remove,
> +	.driver  = {
> +		.name  = "mtk-cam yuv",
> +		.of_match_table = of_match_ptr(mtk_yuv_of_ids),

Drop of_match_ptr(), you will have here warnings.

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

...



> diff --git a/drivers/media/platform/mediatek/isp/isp_7x/camsys/mtk_camera-v4l2-controls.h b/drivers/media/platform/mediatek/isp/isp_7x/camsys/mtk_camera-v4l2-controls.h
> new file mode 100644
> index 000000000000..b775e6c30aa1
> --- /dev/null
> +++ b/drivers/media/platform/mediatek/isp/isp_7x/camsys/mtk_camera-v4l2-controls.h
> @@ -0,0 +1,65 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (c) 2022 MediaTek Inc.
> + */
> +
> +#ifndef __MTK_CAMERA_V4l2_CONTROLS_H
> +#define __MTK_CAMERA_V4l2_CONTROLS_H
> +
> +#include <linux/videodev2.h>
> +#include <linux/v4l2-controls.h>
> +#include <linux/mtkisp_camsys.h>

How these headers are used here? I don't see. Don't include unrelated
stuff in your files.

> +
> +/* Allowed value of V4L2_CID_MTK_CAM_RAW_PATH_SELECT */
> +#define V4L2_MTK_CAM_RAW_PATH_SELECT_BPC	1
> +#define V4L2_MTK_CAM_RAW_PATH_SELECT_FUS	3
> +#define V4L2_MTK_CAM_RAW_PATH_SELECT_DGN	4
> +#define V4L2_MTK_CAM_RAW_PATH_SELECT_LSC	5
> +#define V4L2_MTK_CAM_RAW_PATH_SELECT_LTM	7
> +
> +#define V4L2_MBUS_FRAMEFMT_PAD_ENABLE  BIT(1)


Best regards,
Krzysztof
CK Hu (胡俊光) Oct. 22, 2024, 6:14 a.m. UTC | #7
Hi, Shu-hsiang:

On Wed, 2024-10-09 at 19:15 +0800, Shu-hsiang Yang wrote:
> Introduces the ISP pipeline driver for the MediaTek ISP raw and yuv
> modules. Key functionalities include data processing, V4L2 integration,
> resource management, debug support, and various control operations.
> Additionally, IRQ handling, platform device management, and MediaTek
> ISP DMA format support are also included.
> 
> Signed-off-by: Shu-hsiang Yang <Shu-hsiang.Yang@mediatek.com>
> ---

[snip]

> +static bool mtk_cam_raw_resource_calc(struct mtk_cam_device *cam,
> +				      struct mtk_cam_resource_config *res,
> +				      s64 pixel_rate, int res_plan,
> +				      int in_w, int in_h, int *out_w, int *out_h)
> +{
> +	int res_step_type = 0;
> +	int tgo_pxl_mode = 1;
> +	int pixel_mode[MTK_CAMSYS_RES_STEP_NUM] = {0};
> +	int bin_temp = 0, frz_temp = 0, hwn_temp = 0;
> +	int bin_en = 0, frz_en = 0, twin_en = 0, clk_cur = 0;
> +	int idx = 0, clk_res = 0, idx_res = 0;
> +	bool res_found = false;
> +	int lb_chk_res = -1;
> +	int frz_ratio = 100;
> +	int p;
> +
> +	res->res_plan = res_plan;
> +	res->pixel_rate = pixel_rate;
> +	/* test pattern */
> +	if (res->pixel_rate == 0)
> +		res->pixel_rate = 450 * MHz;
> +	dev_dbg(cam->dev,
> +		"[Res] PR = %lld, w/h=%d/%d HWN(%d)/BIN(%d)/FRZ(%d),Plan:%d\n",
> +		res->pixel_rate, in_w, in_h,
> +		res->hwn_limit_max, res->bin_limit, res->frz_limit, res->res_plan);
> +
> +	memcpy(res->res_strategy, raw_resource_strategy_plan + res->res_plan,
> +	       MTK_CAMSYS_RES_STEP_NUM * sizeof(int));
> +	res->bin_enable = 0;
> +	res->raw_num_used = 1;
> +	res->frz_enable = 0;
> +	res->frz_ratio = frz_ratio;
> +	for (idx = 0; idx < MTK_CAMSYS_RES_STEP_NUM ; idx++) {
> +		res_step_type = res->res_strategy[idx] & MTK_CAMSYS_RES_IDXMASK;
> +		switch (res_step_type) {
> +		case MTK_CAMSYS_RES_BIN_TAG:
> +			bin_temp = res->res_strategy[idx] - E_RES_BIN_S;
> +			if (bin_temp <= res->bin_limit)
> +				bin_en = bin_temp;
> +			if (bin_en && frz_en)
> +				frz_en = 0;
> +			break;
> +		case MTK_CAMSYS_RES_FRZ_TAG:
> +			frz_temp = res->res_strategy[idx] - E_RES_FRZ_S;
> +			if (res->frz_limit < 100)
> +				frz_en = frz_temp;
> +			break;
> +		case MTK_CAMSYS_RES_HWN_TAG:
> +			hwn_temp = res->res_strategy[idx] - E_RES_HWN_S;
> +			if (hwn_temp + 1 <= res->hwn_limit_max)
> +				twin_en = hwn_temp;
> +			break;
> +		case MTK_CAMSYS_RES_CLK_TAG:
> +			clk_cur = res->res_strategy[idx] - E_RES_CLK_S;
> +			break;
> +		default:
> +			break;
> +		}
> +
> +		/* 1 for force bin on */
> +		if (res->bin_limit >= 1)
> +			bin_en = 1;
> +
> +		if (res->hwn_limit_min > 1)
> +			twin_en = 1;
> +
> +		/* max line buffer check*/
> +		lb_chk_res = mtk_raw_linebuf_chk(twin_en, res->bin_limit & BIN_ON,
> +						 frz_en, res->bin_limit & QBND_ON,
> +						 is_cbn_en(res->bin_limit),
> +						 in_w, &frz_ratio);
> +		/* frz ratio*/
> +		if (res_step_type == MTK_CAMSYS_RES_FRZ_TAG) {
> +			res->frz_ratio = res->frz_limit < FRZ_PXLMODE_THRES ?
> +				res->frz_limit : FRZ_PXLMODE_THRES;
> +		}
> +		/*try 1-pixel mode first*/
> +		for (p = 1; p <= MTK_CAMSYS_PROC_DEFAULT_PIXELMODE; p++) {

MTK_CAMSYS_PROC_DEFAULT_PIXELMODE is 1, so this for-loop could be dropped.

> +			tgo_pxl_mode = mtk_raw_pixelmode_calc(p, twin_en,
> +							      bin_en, frz_en,
> +							      res->frz_ratio);

mtk_raw_pixelmode_calc() would always return 1, so mtk_raw_pixelmode_calc() could be dropped.

Regards,
CK

> +			/**
> +			 * isp throughput along resource strategy
> +			 * (compared with pixel rate)
> +			 */
> +			pixel_mode[idx] = tgo_pxl_mode;
> +
> +			/* only support 1-pixel mode */
> +			if (p == 1 && lb_chk_res == LB_CHECK_OK) {
> +				if (!res_found) {
> +					res->bin_enable = bin_en;
> +					res->frz_enable = frz_en;
> +					res->raw_num_used = twin_en + 1;
> +					clk_res = clk_cur;
> +					idx_res = idx;
> +					res_found = true;
> +					break;
> +				}
> +			}
> +		}
> +		dev_dbg(cam->dev,
> +			"Res-%d B/F/H/C=%d/%d/%d/%d -> %d/%d/%d/%d (%d)(%d)\n",
> +			idx, bin_temp, frz_temp, hwn_temp, clk_cur, bin_en,
> +			frz_en, twin_en, clk_cur, lb_chk_res, pixel_mode[idx]);
> +	}
> +
> +	tgo_pxl_mode = pixel_mode[idx_res];
> +	switch (tgo_pxl_mode) {
> +	case 1:
> +		res->tgo_pxl_mode = 0;
> +		break;
> +	case 2:
> +		res->tgo_pxl_mode = 1;
> +		break;
> +	case 4:
> +		res->tgo_pxl_mode = 2;
> +		break;
> +	case 8:
> +		res->tgo_pxl_mode = 3;
> +		break;
> +	default:
> +		break;
> +	}
> +
> +	mtk_raw_update_debug_param(cam, res, &clk_res);
> +
> +	if (res_found) {
> +		dev_info(cam->dev,
> +			 "Res-end:%d BIN/FRZ/HWN/CLK/pxl=%d/%d(%d)/%d/%d/%d, clk:%d\n",
> +			 idx_res, res->bin_enable, res->frz_enable, res->frz_ratio,
> +			 res->raw_num_used, clk_res, res->tgo_pxl_mode,
> +			 res->clk_target);
> +	} else {
> +		dev_dbg(cam->dev, "[%s] Error resource result\n", __func__);
> +	}
> +	if (res->bin_enable) {
> +		*out_w = in_w >> 1;
> +		*out_h = in_h >> 1;
> +	} else if (res->frz_enable) {
> +		*out_w = in_w * res->frz_ratio / 100;
> +		*out_h = in_h * res->frz_ratio / 100;
> +	} else {
> +		*out_w = in_w;
> +		*out_h = in_h;
> +	}
> +
> +	return res_found;
> +}
> +
CK Hu (胡俊光) Oct. 22, 2024, 6:30 a.m. UTC | #8
Hi, Shu-hsiang:

On Wed, 2024-10-09 at 19:15 +0800, Shu-hsiang Yang wrote:
> Introduces the ISP pipeline driver for the MediaTek ISP raw and yuv
> modules. Key functionalities include data processing, V4L2 integration,
> resource management, debug support, and various control operations.
> Additionally, IRQ handling, platform device management, and MediaTek
> ISP DMA format support are also included.
> 
> Signed-off-by: Shu-hsiang Yang <Shu-hsiang.Yang@mediatek.com>
> ---

[snip]

> 			sizeof(u32))
> +
> +enum MTK_CAMSYS_RES_STEP {
> +	E_RES_BASIC,
> +	E_RES_BIN_S = MTK_CAMSYS_RES_BIN_TAG,
> +	E_RES_BIN0 = E_RES_BIN_S,
> +	E_RES_BIN1,
> +	E_RES_BIN_E,
> +	E_RES_FRZ_S = MTK_CAMSYS_RES_FRZ_TAG,
> +	E_RES_FRZ0 = E_RES_FRZ_S,
> +	E_RES_FRZ1,
> +	E_RES_FRZ_E,
> +	E_RES_HWN_S = MTK_CAMSYS_RES_HWN_TAG,
> +	E_RES_HWN0 = E_RES_HWN_S,
> +	E_RES_HWN1,
> +	E_RES_HWN2,
> +	E_RES_HWN_E,
> +	E_RES_CLK_S = MTK_CAMSYS_RES_CLK_TAG,
> +	E_RES_CLK0 = E_RES_CLK_S,
> +	E_RES_CLK1,
> +	E_RES_CLK2,
> +	E_RES_CLK3,
> +	E_RES_CLK_E,

E_RES_CLK_E is useless, so drop it.

Regards,
CK
CK Hu (胡俊光) Oct. 23, 2024, 10:05 a.m. UTC | #9
Hi, Shu-hsiang:

On Wed, 2024-10-09 at 19:15 +0800, Shu-hsiang Yang wrote:
> Introduces the ISP pipeline driver for the MediaTek ISP raw and yuv
> modules. Key functionalities include data processing, V4L2 integration,
> resource management, debug support, and various control operations.
> Additionally, IRQ handling, platform device management, and MediaTek
> ISP DMA format support are also included.
> 
> Signed-off-by: Shu-hsiang Yang <Shu-hsiang.Yang@mediatek.com>
> ---

[snip]

> +static int mtk_cam_raw_try_res_ctrl(struct mtk_raw_pipeline *pipeline,
> +				    struct mtk_cam_resource *res_user,
> +				    struct mtk_cam_resource_config *res_cfg,
> +				    struct v4l2_mbus_framefmt *sink_fmt)
> +{
> +	s64 prate = 0;
> +	int width, height;
> +	struct device *dev = pipeline->raw->devs[pipeline->id];
> +
> +	res_cfg->bin_limit = res_user->raw_res.bin; /* 1: force bin on */
> +	res_cfg->frz_limit = 0;
> +	res_cfg->hwn_limit_max = res_user->raw_res.raw_max;
> +	res_cfg->hwn_limit_min = res_user->raw_res.raw_min;
> +	res_cfg->hblank = res_user->sensor_res.hblank;
> +	res_cfg->vblank = res_user->sensor_res.vblank;
> +	res_cfg->sensor_pixel_rate = res_user->sensor_res.pixel_rate;
> +	res_cfg->res_plan = res_user->raw_res.strategy;
> +	res_cfg->raw_feature = res_user->raw_res.feature;
> +	res_cfg->raw_path = res_user->raw_res.path_sel;
> +
> +	if (res_user->sensor_res.cust_pixel_rate)
> +		prate = res_user->sensor_res.cust_pixel_rate;
> +	else if (mtk_cam_feature_is_pure_m2m(res_cfg->raw_feature))
> +		prate = mtk_cam_calc_pure_m2m_pixelrate(sink_fmt->width,
> +							sink_fmt->height,
> +							&res_cfg->interval);
> +	else
> +		prate = mtk_cam_seninf_calc_pixelrate(pipeline->raw->cam_dev,
> +						      sink_fmt->width,
> +						      sink_fmt->height,
> +						      res_user->sensor_res.hblank,
> +						      res_user->sensor_res.vblank,
> +						      res_user->sensor_res.interval.denominator,
> +						      res_user->sensor_res.interval.numerator,
> +						      res_user->sensor_res.pixel_rate);
> +
> +	mtk_cam_raw_resource_calc(dev_get_drvdata(pipeline->raw->cam_dev),
> +				  res_cfg, prate, res_cfg->res_plan,
> +				  sink_fmt->width, sink_fmt->height,
> +				  &width, &height);

If the basic function support only imgo unprocessed, does the unprocessed path imply the resource could be fixed?
I mean it's not necessary to calculate resource when imgo unprocessed path.
If so, remove resource calculation related code in this basic patch and add it back in advance function patch.

Regards,
CK

> +
> +	if (res_user->raw_res.bin && !res_cfg->bin_enable) {
> +		dev_err(dev,
> +			"%s:pipe(%d): res calc failed on fource bin: user(%d)/bin_enable(%d)\n",
> +			__func__, pipeline->id,	res_user->raw_res.bin,
> +			res_cfg->bin_enable);
> +		return -EINVAL;
> +	}
> +
> +	if (res_cfg->raw_num_used > res_user->raw_res.raw_max ||
> +	    res_cfg->raw_num_used < res_user->raw_res.raw_min) {
> +		dev_err(dev,
> +			"%s:pipe(%d): res calc failed on raw used: user(%d/%d)/raw_num_used(%d)\n",
> +			__func__, pipeline->id, res_user->raw_res.raw_max,
> +			res_user->raw_res.raw_min, res_cfg->raw_num_used);
> +	}
> +
> +	res_user->raw_res.pixel_mode = res_cfg->tgo_pxl_mode;
> +	res_user->raw_res.raw_used = res_cfg->raw_num_used;
> +	if (res_cfg->bin_limit == BIN_AUTO)
> +		res_user->raw_res.bin = res_cfg->bin_enable;
> +	else
> +		res_user->raw_res.bin = res_cfg->bin_limit;
> +
> +	dev_info(dev,
> +		 "%s:pipe(%d): res calc result: raw_used(%d)/bin(%d)/pixelmode(%d)/strategy(%d)\n",
> +		 __func__, pipeline->id, res_user->raw_res.raw_used,
> +		 res_user->raw_res.bin, res_user->raw_res.pixel_mode,
> +		 res_user->raw_res.strategy);
> +
> +	/**
> +	 * Other output not reveal to user:
> +	 * res_cfg->res_strategy[MTK_CAMSYS_RES_STEP_NUM];
> +	 * res_cfg->clk_target;
> +	 * res_cfg->frz_enable;
> +	 * res_cfg->frz_ratio;
> +	 * res_cfg->tgo_pxl_mode;
> +	 */
> +	if (width != sink_fmt->width || height != sink_fmt->height) {
> +		dev_info(dev,
> +			 "%s:pipe(%d): size adjust info: raw: sink(%d,%d) res:(%d,%d)\n",
> +			__func__, pipeline->id, sink_fmt->width,
> +			sink_fmt->height, width, height);
> +	}
> +
> +	return 0;
> +}
> +
CK Hu (胡俊光) Oct. 25, 2024, 6:30 a.m. UTC | #10
Hi, Shu-hsiang:

On Wed, 2024-10-09 at 19:15 +0800, Shu-hsiang Yang wrote:
> Introduces the ISP pipeline driver for the MediaTek ISP raw and yuv
> modules. Key functionalities include data processing, V4L2 integration,
> resource management, debug support, and various control operations.
> Additionally, IRQ handling, platform device management, and MediaTek
> ISP DMA format support are also included.
> 
> Signed-off-by: Shu-hsiang Yang <Shu-hsiang.Yang@mediatek.com>
> ---

[snip]

> +static const
> +char *capture_queue_names[RAW_PIPELINE_NUM][MTK_RAW_TOTAL_CAPTURE_QUEUES] = {
> +	{"mtk-cam raw-0 main-stream",

Let the basic function support only one capture queue and add additional capture queue by other patch.
Maybe one patch for one queue.
The basic function support only "mtk-cam raw-0 main-stream" queue.

Regards,
CK

> +	 "mtk-cam raw-0 yuvo-1", "mtk-cam raw-0 yuvo-2",
> +	 "mtk-cam raw-0 yuvo-3", "mtk-cam raw-0 yuvo-4",
> +	 "mtk-cam raw-0 yuvo-5",
> +	 "mtk-cam raw-0 drzs4no-1", "mtk-cam raw-0 drzs4no-2", "mtk-cam raw-0 drzs4no-3",
> +	 "mtk-cam raw-0 rzh1n2to-1", "mtk-cam raw-0 rzh1n2to-2", "mtk-cam raw-0 rzh1n2to-3",
> +	 "mtk-cam raw-0 partial-meta-0", "mtk-cam raw-0 partial-meta-1",
> +	 "mtk-cam raw-0 partial-meta-2",
> +	 },
> +	{"mtk-cam raw-1 main-stream",
> +	 "mtk-cam raw-1 yuvo-1", "mtk-cam raw-1 yuvo-2",
> +	 "mtk-cam raw-1 yuvo-3", "mtk-cam raw-1 yuvo-4",
> +	 "mtk-cam raw-1 yuvo-5",
> +	 "mtk-cam raw-1 drzs4no-1", "mtk-cam raw-1 drzs4no-2", "mtk-cam raw-1 drzs4no-3",
> +	 "mtk-cam raw-1 rzh1n2to-1", "mtk-cam raw-1 rzh1n2to-2", "mtk-cam raw-1 rzh1n2to-3",
> +	 "mtk-cam raw-1 partial-meta-0", "mtk-cam raw-1 partial-meta-1",
> +	 "mtk-cam raw-1 partial-meta-2",
> +	 },
> +
> +	{"mtk-cam raw-2 main-stream",
> +	 "mtk-cam raw-2 yuvo-1", "mtk-cam raw-2 yuvo-2",
> +	 "mtk-cam raw-2 yuvo-3", "mtk-cam raw-2 yuvo-4",
> +	 "mtk-cam raw-2 yuvo-5",
> +	 "mtk-cam raw-2 drzs4no-1", "mtk-cam raw-2 drzs4no-2", "mtk-cam raw-2 drzs4no-3",
> +	 "mtk-cam raw-2 rzh1n2to-1", "mtk-cam raw-2 rzh1n2to-2", "mtk-cam raw-2 rzh1n2to-3",
> +	 "mtk-cam raw-2 partial-meta-0", "mtk-cam raw-2 partial-meta-1",
> +	 "mtk-cam raw-2 partial-meta-2",
> +	 },
> +};
> +
CK Hu (胡俊光) Oct. 28, 2024, 6:48 a.m. UTC | #11
Hi, Shu-hsiang:

On Wed, 2024-10-09 at 19:15 +0800, Shu-hsiang Yang wrote:
> Introduces the ISP pipeline driver for the MediaTek ISP raw and yuv
> modules. Key functionalities include data processing, V4L2 integration,
> resource management, debug support, and various control operations.
> Additionally, IRQ handling, platform device management, and MediaTek
> ISP DMA format support are also included.
> 
> Signed-off-by: Shu-hsiang Yang <Shu-hsiang.Yang@mediatek.com>
> ---

[snip]

> +
> +static const struct mtk_cam_format_desc yuv_out_group2_fmts[] = {
> +	{
> +		.vfmt.fmt.pix_mp = {
> +			.width = YUV_GROUP2_MAX_WIDTH,
> +			.height = YUV_GROUP2_MAX_HEIGHT,

I don't know why you define width and height here.
All width/height of yuv_out_group2_fmts are the same.
And the max width/height is define in capture_queues[].frmsizes.
So the width/height could be dropped here.

Regards,
CK

> +			.pixelformat = V4L2_PIX_FMT_NV12,
> +		},
> +	},
> +	{
> +		.vfmt.fmt.pix_mp = {
> +			.width = YUV_GROUP2_MAX_WIDTH,
> +			.height = YUV_GROUP2_MAX_HEIGHT,
> +			.pixelformat = V4L2_PIX_FMT_NV21,
> +		},
> +	},
> +	{
> +		.vfmt.fmt.pix_mp = {
> +			.width = YUV_GROUP2_MAX_WIDTH,
> +			.height = YUV_GROUP2_MAX_HEIGHT,
> +			.pixelformat = V4L2_PIX_FMT_NV12_10,
> +		},
> +	},
> +	{
> +		.vfmt.fmt.pix_mp = {
> +			.width = YUV_GROUP2_MAX_WIDTH,
> +			.height = YUV_GROUP2_MAX_HEIGHT,
> +			.pixelformat = V4L2_PIX_FMT_NV21_10,
> +		},
> +	},
> +	{
> +		.vfmt.fmt.pix_mp = {
> +			.width = YUV_GROUP2_MAX_WIDTH,
> +			.height = YUV_GROUP2_MAX_HEIGHT,
> +			.pixelformat = V4L2_PIX_FMT_MTISP_NV12_10P,
> +		},
> +	},
> +	{
> +		.vfmt.fmt.pix_mp = {
> +			.width = YUV_GROUP2_MAX_WIDTH,
> +			.height = YUV_GROUP2_MAX_HEIGHT,
> +			.pixelformat = V4L2_PIX_FMT_MTISP_NV21_10P,
> +		},
> +	},
> +	{
> +		.vfmt.fmt.pix_mp = {
> +			.width = YUV_GROUP2_MAX_WIDTH,
> +			.height = YUV_GROUP2_MAX_HEIGHT,
> +			.pixelformat = V4L2_PIX_FMT_NV12_12,
> +		},
> +	},
> +	{
> +		.vfmt.fmt.pix_mp = {
> +			.width = YUV_GROUP2_MAX_WIDTH,
> +			.height = YUV_GROUP2_MAX_HEIGHT,
> +			.pixelformat = V4L2_PIX_FMT_NV21_12,
> +		},
> +	},
> +	{
> +		.vfmt.fmt.pix_mp = {
> +			.width = YUV_GROUP2_MAX_WIDTH,
> +			.height = YUV_GROUP2_MAX_HEIGHT,
> +			.pixelformat = V4L2_PIX_FMT_MTISP_NV12_12P,
> +		},
> +	},
> +	{
> +		.vfmt.fmt.pix_mp = {
> +			.width = YUV_GROUP2_MAX_WIDTH,
> +			.height = YUV_GROUP2_MAX_HEIGHT,
> +			.pixelformat = V4L2_PIX_FMT_MTISP_NV21_12P,
> +		},
> +	}
> +};
> +

[snip]

> +#define MTK_RAW_TOTAL_CAPTURE_QUEUES 15
> +static const struct
> +mtk_cam_dev_node_desc capture_queues[] = {
> 

[snip]

> +	{
> +		.id = MTK_RAW_YUVO_2_OUT,
> +		.name = "yuvo 2",
> +		.cap = V4L2_CAP_VIDEO_CAPTURE_MPLANE,
> +		.buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE,
> +		.link_flags = MEDIA_LNK_FL_ENABLED |  MEDIA_LNK_FL_IMMUTABLE,
> +		.image = true,
> +		.smem_alloc = false,
> +		.dma_port = MTKCAM_IPI_RAW_YUVO_2,
> +		.fmts = yuv_out_group2_fmts,
> +		.num_fmts = ARRAY_SIZE(yuv_out_group2_fmts),
> +		.default_fmt_idx = 0,
> +		.pad_ops = &source_pad_ops_yuv,
> +		.ioctl_ops = &mtk_cam_v4l2_vcap_ioctl_ops,
> +		.frmsizes = &(struct v4l2_frmsizeenum) {
> +			.index = 0,
> +			.type = V4L2_FRMSIZE_TYPE_CONTINUOUS,
> +			.stepwise = {
> +				.max_width = YUV_GROUP2_MAX_WIDTH,
> +				.min_width = IMG_MIN_WIDTH,
> +				.max_height = YUV_GROUP2_MAX_HEIGHT,
> +				.min_height = IMG_MIN_HEIGHT,
> +				.step_height = 1,
> +				.step_width = 1,
> +			},
> +		},
> +	},
> +};
> +
CK Hu (胡俊光) Nov. 4, 2024, 2:35 a.m. UTC | #12
Hi, Shu-hsiang:

On Wed, 2024-10-09 at 19:15 +0800, Shu-hsiang Yang wrote:
> Introduces the ISP pipeline driver for the MediaTek ISP raw and yuv
> modules. Key functionalities include data processing, V4L2 integration,
> resource management, debug support, and various control operations.
> Additionally, IRQ handling, platform device management, and MediaTek
> ISP DMA format support are also included.
> 
> Signed-off-by: Shu-hsiang Yang <Shu-hsiang.Yang@mediatek.com>
> ---

[snip]

> +void mtk_cam_dump_req_rdy_status(struct device *dev,
> +				 void __iomem *base, void __iomem *yuvbase)
> +{

This function is useless, so drop it.

Regards,
CK

> +	dev_dbg_ratelimited(dev,
> +			    "REQ RAW/2/3 DMA/2:%08x/%08x/%08x/%08x/%08x\n",
> +			    readl_relaxed(base + REG_CTL_RAW_MOD_REQ_STAT),
> +			    readl_relaxed(base + REG_CTL_RAW_MOD2_REQ_STAT),
> +			    readl_relaxed(base + REG_CTL_RAW_MOD3_REQ_STAT),
> +			    readl_relaxed(base + REG_CTL_RAW_MOD5_REQ_STAT),
> +			    readl_relaxed(base + REG_CTL_RAW_MOD6_REQ_STAT));
> +	dev_dbg_ratelimited(dev,
> +			    "RDY RAW/2/3 DMA/2:%08x/%08x/%08x/%08x/%08x\n",
> +			    readl_relaxed(base + REG_CTL_RAW_MOD_RDY_STAT),
> +			    readl_relaxed(base + REG_CTL_RAW_MOD2_RDY_STAT),
> +			    readl_relaxed(base + REG_CTL_RAW_MOD3_RDY_STAT),
> +			    readl_relaxed(base + REG_CTL_RAW_MOD5_RDY_STAT),
> +			    readl_relaxed(base + REG_CTL_RAW_MOD6_RDY_STAT));
> +	dev_dbg_ratelimited(dev,
> +			    "REQ YUV/2/3/4 WDMA:%08x/%08x/%08x/%08x/%08x\n",
> +			    readl_relaxed(yuvbase + REG_CTL_RAW_MOD_REQ_STAT),
> +			    readl_relaxed(yuvbase + REG_CTL_RAW_MOD2_REQ_STAT),
> +			    readl_relaxed(yuvbase + REG_CTL_RAW_MOD3_REQ_STAT),
> +			    readl_relaxed(yuvbase + REG_CTL_RAW_MOD4_REQ_STAT),
> +			    readl_relaxed(yuvbase + REG_CTL_RAW_MOD5_REQ_STAT));
> +	dev_dbg_ratelimited(dev,
> +			    "RDY YUV/2/3/4 WDMA:%08x/%08x/%08x/%08x/%08x\n",
> +			    readl_relaxed(yuvbase + REG_CTL_RAW_MOD_RDY_STAT),
> +			    readl_relaxed(yuvbase + REG_CTL_RAW_MOD2_RDY_STAT),
> +			    readl_relaxed(yuvbase + REG_CTL_RAW_MOD3_RDY_STAT),
> +			    readl_relaxed(yuvbase + REG_CTL_RAW_MOD4_RDY_STAT),
> +			    readl_relaxed(yuvbase + REG_CTL_RAW_MOD5_RDY_STAT));
> +}
> +
CK Hu (胡俊光) Nov. 4, 2024, 8:04 a.m. UTC | #13
Hi, Shu-hsiang:

On Wed, 2024-10-09 at 19:15 +0800, Shu-hsiang Yang wrote:
> Introduces the ISP pipeline driver for the MediaTek ISP raw and yuv
> modules. Key functionalities include data processing, V4L2 integration,
> resource management, debug support, and various control operations.
> Additionally, IRQ handling, platform device management, and MediaTek
> ISP DMA format support are also included.
> 
> Signed-off-by: Shu-hsiang Yang <Shu-hsiang.Yang@mediatek.com>
> ---

[snip]

> +static bool mtk_raw_fmt_get_res(struct v4l2_subdev *sd,
> +				struct v4l2_subdev_format *fmt,
> +				struct mtk_cam_resource *res)
> +{
> +	void *user_ptr;
> +	u64 addr;
> +
> +	addr = ((u64)fmt->reserved[1] << 32) | fmt->reserved[2];

The callstack to this function is:

subdev_do_ioctl() -> mtk_raw_set_fmt() -> mtk_raw_try_pad_fmt() -> mtk_raw_set_src_pad_fmt() -> mtk_raw_fmt_get_res()

In subdev_do_ioctl() [1], fmt->reserved[] would be cleared to zero.
I don't know why you could get a non-zero addr value?

[1] https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/media/v4l2-core/v4l2-subdev.c?h=v6.12-rc6#n753

Regards,
CK

> +	user_ptr = (void *)addr;
> +	if (!user_ptr) {
> +		dev_info(sd->v4l2_dev->dev, "%s: mtk_cam_resource is null\n",
> +			 __func__);
> +		return false;
> +	}
> +
> +	if (copy_from_user(res, (void __user *)user_ptr, sizeof(*res))) {
> +		dev_info(sd->v4l2_dev->dev,
> +			 "%s: copy_from_user failedm user_ptr:%p\n",
> +			 __func__, user_ptr);
> +		return false;
> +	}
> +
> +	dev_dbg(sd->v4l2_dev->dev,
> +		"%s:sensor:%d/%d/%lld/%d/%d, raw:%lld/%d/%d/%d/%d/%d/%d/%d/%lld\n",
> +		__func__,
> +		res->sensor_res.hblank, res->sensor_res.vblank,
> +		res->sensor_res.pixel_rate, res->sensor_res.interval.denominator,
> +		res->sensor_res.interval.numerator,
> +		res->raw_res.feature, res->raw_res.bin, res->raw_res.path_sel,
> +		res->raw_res.raw_max, res->raw_res.raw_min, res->raw_res.raw_used,
> +		res->raw_res.strategy, res->raw_res.pixel_mode,
> +		res->raw_res.throughput);
> +
> +	return res;
> +}
> +
CK Hu (胡俊光) Nov. 5, 2024, 3:01 a.m. UTC | #14
Hi, Shu-hsiang:

On Wed, 2024-10-09 at 19:15 +0800, Shu-hsiang Yang wrote:
> Introduces the ISP pipeline driver for the MediaTek ISP raw and yuv
> modules. Key functionalities include data processing, V4L2 integration,
> resource management, debug support, and various control operations.
> Additionally, IRQ handling, platform device management, and MediaTek
> ISP DMA format support are also included.
> 
> Signed-off-by: Shu-hsiang Yang <Shu-hsiang.Yang@mediatek.com>
> ---

[snip]

> +static int mtk_raw_of_probe(struct platform_device *pdev,
> +			    struct mtk_raw_device *raw)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct resource *res;
> +	int ret;
> +	int n_clks;
> +
> +	ret = of_property_read_u32(dev->of_node, "mediatek,cam-id", &raw->id);

You don't need raw->id.
I think each raw could work independently.
Each raw has its own 'struct mtk_raw_device' context data.
It's not necessary to know which one it is.
When you use dev_dbg() or dev_err(), it would show register base so you could identify which raw print this message.

Regards,
CK

> +	if (ret) {
> +		dev_dbg(dev, "missing camid property\n");
> +		return ret;
> +	}
> +
> +	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);
> +
> +	/* base outer register */
> +	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "base");
> +	if (!res) {
> +		dev_err(dev, "failed to get mem\n");
> +		return -ENODEV;
> +	}
> +
> +	raw->base = devm_ioremap_resource(dev, res);
> +	if (IS_ERR(raw->base)) {
> +		dev_err(dev, "failed to map register base\n");
> +		return PTR_ERR(raw->base);
> +	}
> +
> +	/* base inner register */
> +	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "inner_base");
> +	if (!res) {
> +		dev_err(dev, "failed to get mem\n");
> +		return -ENODEV;
> +	}
> +
> +	raw->base_inner = devm_ioremap_resource(dev, res);
> +	if (IS_ERR(raw->base_inner)) {
> +		dev_err(dev, "failed to map register inner base\n");
> +		return PTR_ERR(raw->base_inner);
> +	}
> +
> +	/* will be assigned later */
> +	raw->yuv_base = NULL;
> +
> +	raw->irq = platform_get_irq(pdev, 0);
> +	if (raw->irq < 0) {
> +		dev_err(dev, "failed to get irq\n");
> +		return -ENODEV;
> +	}
> +
> +	ret = devm_request_threaded_irq(dev, raw->irq,
> +					mtk_irq_raw, mtk_thread_irq_raw,
> +					0, dev_name(dev), raw);
> +	if (ret) {
> +		dev_err(dev, "failed to request irq=%d\n", raw->irq);
> +		return ret;
> +	}
> +	dev_dbg(dev, "registered irq=%d\n", raw->irq);
> +
> +	disable_irq(raw->irq);
> +
> +	n_clks = devm_clk_bulk_get_all(dev, &raw->clk_b);
> +	if (n_clks < 0) {
> +		dev_err(dev, "failed to devm_clk_bulk_get_all=%d\n", n_clks);
> +		return n_clks;
> +	}
> +
> +	raw->num_clks = n_clks;
> +	dev_info(dev, "clk_num:%d\n", raw->num_clks);
> +
> +#ifdef CONFIG_PM_SLEEP
> +	raw->pm_notifier.notifier_call = raw_pm_notifier;
> +	ret = register_pm_notifier(&raw->pm_notifier);
> +	if (ret) {
> +		dev_info(dev, "failed to register notifier block.\n");
> +		return ret;
> +	}
> +#endif
> +	return 0;
> +}
> +
diff mbox series

Patch

diff --git a/drivers/media/platform/mediatek/isp/isp_7x/camsys/mtk_cam-raw.c b/drivers/media/platform/mediatek/isp/isp_7x/camsys/mtk_cam-raw.c
new file mode 100644
index 000000000000..c025f53c952d
--- /dev/null
+++ b/drivers/media/platform/mediatek/isp/isp_7x/camsys/mtk_cam-raw.c
@@ -0,0 +1,5359 @@ 
+// SPDX-License-Identifier: GPL-2.0
+//
+// Copyright (c) 2022 MediaTek Inc.
+
+#include <linux/clk.h>
+#include <linux/interrupt.h>
+#include <linux/iopoll.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/vmalloc.h>
+#include <linux/videodev2.h>
+#include <linux/suspend.h>
+#include <linux/rtc.h>
+
+#include <media/v4l2-device.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-subdev.h>
+
+#include <soc/mediatek/smi.h>
+
+#include "mtk_cam.h"
+#include "mtk_cam-feature.h"
+#include "mtk_cam-raw.h"
+
+#include "mtk_cam-regs-mt8188.h"
+
+#include "mtk_cam-video.h"
+#include "mtk_cam-seninf-if.h"
+#include "mtk_camera-v4l2-controls.h"
+
+#include "mtk_cam-dmadbg.h"
+#include "mtk_cam-raw_debug.h"
+
+static unsigned int debug_raw;
+module_param(debug_raw, uint, 0644);
+MODULE_PARM_DESC(debug_raw, "activates debug info");
+
+static int debug_raw_num = -1;
+module_param(debug_raw_num, int, 0644);
+MODULE_PARM_DESC(debug_raw_num, "debug: num of used raw devices");
+
+static int debug_pixel_mode = -1;
+module_param(debug_pixel_mode, int, 0644);
+MODULE_PARM_DESC(debug_pixel_mode, "debug: pixel mode");
+
+static int debug_clk_idx = -1;
+module_param(debug_clk_idx, int, 0644);
+MODULE_PARM_DESC(debug_clk_idx, "debug: clk idx");
+
+static int debug_dump_fbc;
+module_param(debug_dump_fbc, int, 0644);
+MODULE_PARM_DESC(debug_dump_fbc, "debug: dump fbc");
+
+#define MTK_RAW_STOP_HW_TIMEOUT		(33)
+
+#define MTK_CAMSYS_RES_IDXMASK		0xF0
+#define MTK_CAMSYS_RES_BIN_TAG		0x10
+#define MTK_CAMSYS_RES_FRZ_TAG		0x20
+#define MTK_CAMSYS_RES_HWN_TAG		0x30
+#define MTK_CAMSYS_RES_CLK_TAG		0x40
+
+#define MTK_CAMSYS_RES_PLAN_NUM		10
+#define FRZ_PXLMODE_THRES		71
+#define MHz				1000000
+
+/* ISP7_1 */
+#define TGO_MAX_PXLMODE				1
+#define MTK_CAMSYS_PROC_DEFAULT_PIXELMODE	1
+#define sizeof_u32(__struct__) ((sizeof(__struct__) + sizeof(u32) - 1) / \
+				sizeof(u32))
+
+enum MTK_CAMSYS_RES_STEP {
+	E_RES_BASIC,
+	E_RES_BIN_S = MTK_CAMSYS_RES_BIN_TAG,
+	E_RES_BIN0 = E_RES_BIN_S,
+	E_RES_BIN1,
+	E_RES_BIN_E,
+	E_RES_FRZ_S = MTK_CAMSYS_RES_FRZ_TAG,
+	E_RES_FRZ0 = E_RES_FRZ_S,
+	E_RES_FRZ1,
+	E_RES_FRZ_E,
+	E_RES_HWN_S = MTK_CAMSYS_RES_HWN_TAG,
+	E_RES_HWN0 = E_RES_HWN_S,
+	E_RES_HWN1,
+	E_RES_HWN2,
+	E_RES_HWN_E,
+	E_RES_CLK_S = MTK_CAMSYS_RES_CLK_TAG,
+	E_RES_CLK0 = E_RES_CLK_S,
+	E_RES_CLK1,
+	E_RES_CLK2,
+	E_RES_CLK3,
+	E_RES_CLK_E,
+};
+
+enum MTK_CAMSYS_MAXLB_CHECK_RESULT {
+	LB_CHECK_OK = 0,
+	LB_CHECK_CBN,
+	LB_CHECK_QBN,
+	LB_CHECK_BIN,
+	LB_CHECK_FRZ,
+	LB_CHECK_TWIN,
+	LB_CHECK_RAW,
+};
+
+#define CAM_RAW_PROCESS_MAX_LINE_BUFFER		(6632)
+#define CAM_RAW_FRZ_MAX_LINE_BUFFER		(6632)
+#define CAM_RAW_BIN_MAX_LINE_BUFFER		(12000)
+#define CAM_RAW_QBND_MAX_LINE_BUFFER		(16000)
+#define CAM_RAW_CBN_MAX_LINE_BUFFER		(18472)
+#define CAM_TWIN_PROCESS_MAX_LINE_BUFFER	(12400)
+
+struct cam_resource_plan {
+	int cam_resource[MTK_CAMSYS_RES_STEP_NUM];
+};
+
+enum resource_strategy_id {
+	RESOURCE_STRATEGY_QPR = 0,
+	RESOURCE_STRATEGY_PQR,
+	RESOURCE_STRATEGY_RPQ,
+	RESOURCE_STRATEGY_QRP,
+	RESOURCE_STRATEGY_NUMBER
+};
+
+static const struct cam_resource_plan raw_resource_strategy_plan[] = {
+	[RESOURCE_STRATEGY_QPR] = {
+		.cam_resource = {
+			E_RES_BASIC, E_RES_HWN1, E_RES_CLK1, E_RES_CLK2,
+			E_RES_CLK3, E_RES_FRZ1, E_RES_BIN1, E_RES_HWN2} },
+	[RESOURCE_STRATEGY_PQR] = {
+		.cam_resource = {
+			E_RES_BASIC, E_RES_HWN1, E_RES_HWN2, E_RES_FRZ1,
+			E_RES_BIN1, E_RES_CLK1, E_RES_CLK2, E_RES_CLK3} },
+	[RESOURCE_STRATEGY_RPQ] = {
+		.cam_resource = {
+			E_RES_BASIC, E_RES_FRZ1, E_RES_BIN1, E_RES_CLK1,
+			E_RES_CLK2, E_RES_CLK3, E_RES_HWN1, E_RES_HWN2} },
+	[RESOURCE_STRATEGY_QRP] = {
+		.cam_resource = {
+			E_RES_BASIC, E_RES_CLK1, E_RES_CLK2, E_RES_CLK3,
+			E_RES_HWN1, E_RES_HWN2, E_RES_FRZ1, E_RES_BIN1} },
+};
+
+static const struct v4l2_mbus_framefmt mfmt_default = {
+	.code = MEDIA_BUS_FMT_SBGGR10_1X10,
+	.width = DEFAULT_WIDTH,
+	.height = DEFAULT_HEIGHT,
+	.field = V4L2_FIELD_NONE,
+	.colorspace = V4L2_COLORSPACE_SRGB,
+	.xfer_func = V4L2_XFER_FUNC_DEFAULT,
+	.ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT,
+	.quantization = V4L2_QUANTIZATION_DEFAULT,
+};
+
+static bool mtk_cam_raw_resource_calc(struct mtk_cam_device *cam,
+				      struct mtk_cam_resource_config *res,
+				      s64 pixel_rate, int res_plan, int in_w,
+				      int in_h, int *out_w, int *out_h);
+
+static int mtk_cam_raw_res_copy_fmt_from_user(struct mtk_raw_pipeline *pipeline,
+					      struct mtk_cam_resource *res_user,
+					      struct v4l2_mbus_framefmt *dest)
+{
+	long bytes;
+	struct device *dev = pipeline->raw->devs[pipeline->id];
+
+	if (!res_user->sink_fmt) {
+		dev_err(dev,
+			"%s:pipe(%d): sink_fmt can't be NULL for res ctrl\n",
+			__func__, pipeline->id);
+
+		return -EINVAL;
+	}
+
+	bytes = copy_from_user(dest, (void __user *)res_user->sink_fmt,
+			       sizeof(*dest));
+	if (bytes) {
+		dev_err(dev,
+			"%s:pipe(%d): copy_from_user on sink_fmt failed (%ld)\n",
+			__func__, pipeline->id, bytes);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int mtk_cam_raw_res_copy_fmt_to_user(struct mtk_raw_pipeline *pipeline,
+					    struct mtk_cam_resource *res_user,
+					    struct v4l2_mbus_framefmt *src)
+{
+	long bytes;
+	struct device *dev = pipeline->raw->devs[pipeline->id];
+
+	/* return the fmt to the users */
+	bytes = copy_to_user((void __user *)res_user->sink_fmt, src, sizeof(*src));
+	if (bytes) {
+		dev_err(dev,
+			"%s:pipe(%d): copy_to_user on sink_fmt failed (%ld)\n",
+			__func__, pipeline->id, bytes);
+
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int mtk_cam_raw_get_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct mtk_cam_resource *user_res;
+	struct mtk_raw_pipeline *pipeline;
+	struct device *dev;
+	int ret = 0;
+
+	pipeline = mtk_cam_ctrl_handler_to_raw_pipeline(ctrl->handler);
+	dev = pipeline->raw->devs[pipeline->id];
+
+	if (ctrl->id == V4L2_CID_MTK_CAM_RAW_RESOURCE_CALC) {
+		user_res = (struct mtk_cam_resource *)ctrl->p_new.p;
+		user_res->sensor_res = pipeline->user_res.sensor_res;
+		user_res->raw_res = pipeline->user_res.raw_res;
+		if (user_res->sink_fmt)
+			ret = mtk_cam_raw_res_copy_fmt_to_user(pipeline,
+							       user_res,
+							       (struct v4l2_mbus_framefmt *)
+							       pipeline->user_res.sink_fmt);
+	} else if (ctrl->id == V4L2_CID_MTK_CAM_RAW_RESOURCE_UPDATE) {
+		ctrl->val = pipeline->sensor_mode_update;
+		dev_info(dev,
+			 "%s:pipe(%d): V4L2_CID_MTK_CAM_RAW_RESOURCE_UPDATE: %d\n",
+			 __func__, pipeline->id, pipeline->sensor_mode_update);
+	} else if (ctrl->id >= V4L2_CID_MTK_CAM_USED_ENGINE_TRY &&
+		   ctrl->id <= V4L2_CID_MTK_CAM_FRZ_TRY) {
+		/**
+		 * Read the determined resource for the "try" format
+		 * negotiation result
+		 */
+		mutex_lock(&pipeline->try_res_config.resource_lock);
+		switch (ctrl->id) {
+		case V4L2_CID_MTK_CAM_USED_ENGINE_TRY:
+			ctrl->val = pipeline->try_res_config.raw_num_used;
+			break;
+		case V4L2_CID_MTK_CAM_BIN_TRY:
+			ctrl->val = pipeline->try_res_config.bin_enable;
+			break;
+		case V4L2_CID_MTK_CAM_FRZ_TRY:
+			ctrl->val = pipeline->try_res_config.frz_enable ?
+				    pipeline->try_res_config.frz_ratio : 100;
+			break;
+		default:
+			dev_info(dev,
+				 "%s:pipe(%d): unknown resource CID: %d\n",
+				 __func__, pipeline->id, ctrl->id);
+			break;
+		}
+		mutex_unlock(&pipeline->try_res_config.resource_lock);
+	} else if (ctrl->id == V4L2_CID_MTK_CAM_SYNC_ID) {
+		mutex_lock(&pipeline->res_config.resource_lock);
+		*ctrl->p_new.p_s64 = pipeline->sync_id;
+		mutex_unlock(&pipeline->res_config.resource_lock);
+	} else {
+		/**
+		 * Read the determined resource for the "set" format
+		 * negotiations result
+		 */
+		mutex_lock(&pipeline->res_config.resource_lock);
+		switch (ctrl->id) {
+		case V4L2_CID_MTK_CAM_USED_ENGINE:
+			ctrl->val = pipeline->res_config.raw_num_used;
+			break;
+		case V4L2_CID_MTK_CAM_BIN_LIMIT:
+			ctrl->val = pipeline->res_config.bin_limit;
+			break;
+		case V4L2_CID_MTK_CAM_BIN:
+			ctrl->val = pipeline->res_config.bin_enable;
+			break;
+		case V4L2_CID_MTK_CAM_FRZ:
+			ctrl->val = pipeline->res_config.frz_enable ?
+				    pipeline->res_config.frz_ratio : 100;
+			break;
+		default:
+			dev_info(dev,
+				 "%s:pipe(%d): unknown try resource CID: %d\n",
+				 __func__, pipeline->id, ctrl->id);
+			break;
+		}
+		mutex_unlock(&pipeline->res_config.resource_lock);
+	}
+
+	dev_dbg(dev, "%s:pipe(%d):id(%s) val(%d)\n",
+		__func__, pipeline->id, ctrl->name, ctrl->val);
+	return ret;
+}
+
+static int mtk_cam_raw_res_store(struct mtk_raw_pipeline *pipeline,
+				 struct mtk_cam_resource *res_user)
+{
+	struct device *dev = pipeline->raw->devs[pipeline->id];
+
+	dev_info(dev,
+		 "%s:pipe(%d): from user: sensor:%d/%d/%lld/%lld/%d/%d, raw:%lld/%d/%d/%d/%d/%d/%d/%d/%lld\n",
+		 __func__, pipeline->id,
+		 res_user->sensor_res.hblank, res_user->sensor_res.vblank,
+		 res_user->sensor_res.pixel_rate,
+		 res_user->sensor_res.cust_pixel_rate,
+		 res_user->sensor_res.interval.denominator,
+		 res_user->sensor_res.interval.numerator,
+		 res_user->raw_res.feature, res_user->raw_res.bin,
+		 res_user->raw_res.path_sel, res_user->raw_res.raw_max,
+		 res_user->raw_res.raw_min, res_user->raw_res.raw_used,
+		 res_user->raw_res.strategy, res_user->raw_res.pixel_mode,
+		 res_user->raw_res.throughput);
+
+	/* check user value of sensor input parameters */
+	if (!mtk_cam_feature_is_pure_m2m(res_user->raw_res.feature) &&
+	    (!res_user->sensor_res.pixel_rate || !res_user->sensor_res.hblank ||
+	     !res_user->sensor_res.vblank ||
+	     !res_user->sensor_res.interval.denominator ||
+	     !res_user->sensor_res.interval.numerator)) {
+		dev_info(dev,
+			 "%s:pipe(%d): sensor info MUST be provided (TEST_PATTERN case)\n",
+			 __func__, pipeline->id);
+		/*test pattern case resource default copy*/
+		pipeline->res_config.raw_num_used = 1;
+		pipeline->res_config.bin_enable = 0;
+		pipeline->res_config.tgo_pxl_mode = 1;
+		pipeline->res_config.raw_path = 0;
+		pipeline->res_config.hwn_limit_min = 1;
+		pipeline->res_config.raw_feature = res_user->raw_res.feature;
+		pipeline->feature_pending = res_user->raw_res.feature;
+		pipeline->feature_active = res_user->raw_res.feature;
+	}
+
+	/* check user value of raw input parameters */
+	if (res_user->raw_res.feature == 0xFFFFFFFF)
+		res_user->raw_res.feature = 0;
+
+	if (res_user->raw_res.bin == 0xFF)
+		res_user->raw_res.bin = 0;
+
+	if (res_user->raw_res.strategy == 0xFFFF)
+		res_user->raw_res.strategy = RESOURCE_STRATEGY_QRP;
+
+	if (res_user->raw_res.raw_max == 0xFF)
+		res_user->raw_res.raw_max = 2;
+
+	if (res_user->raw_res.raw_min == 0xFF)
+		res_user->raw_res.raw_min = 1;
+
+	if (res_user->raw_res.raw_min > 1)
+		res_user->raw_res.strategy = RESOURCE_STRATEGY_QPR;
+
+	if (res_user->raw_res.path_sel == 0xFF)
+		res_user->raw_res.path_sel = 0;
+
+	dev_info(dev,
+		 "%s:pipe(%d): driver adjusted: raw:%lld/%d/%d/%d/%d/%d/%d/%d/%lld\n",
+		 __func__, pipeline->id,
+		 res_user->raw_res.feature, res_user->raw_res.bin,
+		 res_user->raw_res.path_sel, res_user->raw_res.raw_max,
+		 res_user->raw_res.raw_min, res_user->raw_res.raw_used,
+		 res_user->raw_res.strategy, res_user->raw_res.pixel_mode,
+		 res_user->raw_res.throughput);
+
+	return 0;
+}
+
+static s64 mtk_cam_calc_pure_m2m_pixelrate(s64 width, s64 height,
+					   struct v4l2_fract *interval)
+{
+	/* process + r/wdma margin = (1 + 5%) x (1 + 10%) */
+	#define PURE_M2M_PROCESS_MARGIN_N 11550
+	#define PURE_M2M_PROCESS_MARGIN_D 10000
+	u64 prate = 0;
+	int fps_n = interval->numerator;
+	int fps_d = interval->denominator;
+
+	prate = width * height * fps_d * PURE_M2M_PROCESS_MARGIN_N;
+	do_div(prate, fps_n * PURE_M2M_PROCESS_MARGIN_D);
+
+	pr_info("%s:width:%lld height:%lld interval:%d/%d prate:%llu\n",
+		__func__, width, height, fps_n, fps_d, prate);
+	return prate;
+}
+
+static int mtk_cam_raw_try_res_ctrl(struct mtk_raw_pipeline *pipeline,
+				    struct mtk_cam_resource *res_user,
+				    struct mtk_cam_resource_config *res_cfg,
+				    struct v4l2_mbus_framefmt *sink_fmt)
+{
+	s64 prate = 0;
+	int width, height;
+	struct device *dev = pipeline->raw->devs[pipeline->id];
+
+	res_cfg->bin_limit = res_user->raw_res.bin; /* 1: force bin on */
+	res_cfg->frz_limit = 0;
+	res_cfg->hwn_limit_max = res_user->raw_res.raw_max;
+	res_cfg->hwn_limit_min = res_user->raw_res.raw_min;
+	res_cfg->hblank = res_user->sensor_res.hblank;
+	res_cfg->vblank = res_user->sensor_res.vblank;
+	res_cfg->sensor_pixel_rate = res_user->sensor_res.pixel_rate;
+	res_cfg->res_plan = res_user->raw_res.strategy;
+	res_cfg->raw_feature = res_user->raw_res.feature;
+	res_cfg->raw_path = res_user->raw_res.path_sel;
+
+	if (res_user->sensor_res.cust_pixel_rate)
+		prate = res_user->sensor_res.cust_pixel_rate;
+	else if (mtk_cam_feature_is_pure_m2m(res_cfg->raw_feature))
+		prate = mtk_cam_calc_pure_m2m_pixelrate(sink_fmt->width,
+							sink_fmt->height,
+							&res_cfg->interval);
+	else
+		prate = mtk_cam_seninf_calc_pixelrate(pipeline->raw->cam_dev,
+						      sink_fmt->width,
+						      sink_fmt->height,
+						      res_user->sensor_res.hblank,
+						      res_user->sensor_res.vblank,
+						      res_user->sensor_res.interval.denominator,
+						      res_user->sensor_res.interval.numerator,
+						      res_user->sensor_res.pixel_rate);
+
+	mtk_cam_raw_resource_calc(dev_get_drvdata(pipeline->raw->cam_dev),
+				  res_cfg, prate, res_cfg->res_plan,
+				  sink_fmt->width, sink_fmt->height,
+				  &width, &height);
+
+	if (res_user->raw_res.bin && !res_cfg->bin_enable) {
+		dev_err(dev,
+			"%s:pipe(%d): res calc failed on fource bin: user(%d)/bin_enable(%d)\n",
+			__func__, pipeline->id,	res_user->raw_res.bin,
+			res_cfg->bin_enable);
+		return -EINVAL;
+	}
+
+	if (res_cfg->raw_num_used > res_user->raw_res.raw_max ||
+	    res_cfg->raw_num_used < res_user->raw_res.raw_min) {
+		dev_err(dev,
+			"%s:pipe(%d): res calc failed on raw used: user(%d/%d)/raw_num_used(%d)\n",
+			__func__, pipeline->id, res_user->raw_res.raw_max,
+			res_user->raw_res.raw_min, res_cfg->raw_num_used);
+	}
+
+	res_user->raw_res.pixel_mode = res_cfg->tgo_pxl_mode;
+	res_user->raw_res.raw_used = res_cfg->raw_num_used;
+	if (res_cfg->bin_limit == BIN_AUTO)
+		res_user->raw_res.bin = res_cfg->bin_enable;
+	else
+		res_user->raw_res.bin = res_cfg->bin_limit;
+
+	dev_info(dev,
+		 "%s:pipe(%d): res calc result: raw_used(%d)/bin(%d)/pixelmode(%d)/strategy(%d)\n",
+		 __func__, pipeline->id, res_user->raw_res.raw_used,
+		 res_user->raw_res.bin, res_user->raw_res.pixel_mode,
+		 res_user->raw_res.strategy);
+
+	/**
+	 * Other output not reveal to user:
+	 * res_cfg->res_strategy[MTK_CAMSYS_RES_STEP_NUM];
+	 * res_cfg->clk_target;
+	 * res_cfg->frz_enable;
+	 * res_cfg->frz_ratio;
+	 * res_cfg->tgo_pxl_mode;
+	 */
+	if (width != sink_fmt->width || height != sink_fmt->height) {
+		dev_info(dev,
+			 "%s:pipe(%d): size adjust info: raw: sink(%d,%d) res:(%d,%d)\n",
+			__func__, pipeline->id, sink_fmt->width,
+			sink_fmt->height, width, height);
+	}
+
+	return 0;
+}
+
+static int mtk_cam_raw_set_res_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct mtk_raw_pipeline *pipeline;
+	struct mtk_cam_resource *res_user;
+	struct v4l2_mbus_framefmt sink_fmt;
+	struct device *dev;
+	int ret = 0;
+
+	pipeline = mtk_cam_ctrl_handler_to_raw_pipeline(ctrl->handler);
+	dev = pipeline->raw->devs[pipeline->id];
+	res_user = (struct mtk_cam_resource *)ctrl->p_new.p;
+
+	ret = mtk_cam_raw_res_store(pipeline, res_user);
+	pipeline->user_res = *res_user;
+	if (media_entity_is_streaming(&pipeline->subdev.entity)) {
+		/* If the pipeline is streaming, pending the change */
+		dev_dbg(dev,
+			"%s:pipe(%d): pending res calc has not been supported except bin\n",
+			__func__, pipeline->id);
+		return ret;
+	}
+
+	dev_dbg(dev,
+		"%s:pipe(%d):streaming(%d), feature_pending(0x%llx), raw_res.feature(0x%llx), feature_active(0x%llx)\n",
+		__func__, pipeline->id,
+		media_entity_is_streaming(&pipeline->subdev.entity),
+		pipeline->feature_pending, pipeline->user_res.raw_res.feature,
+		pipeline->feature_active);
+
+	ret = mtk_cam_raw_res_copy_fmt_from_user(pipeline, res_user, &sink_fmt);
+	if (ret)
+		return ret;
+
+	pipeline->res_config.sink_fmt = sink_fmt;
+	ret = mtk_cam_raw_try_res_ctrl(pipeline, res_user,
+				       &pipeline->res_config, &sink_fmt);
+
+	if (ret)
+		return -EINVAL;
+
+	return ret;
+}
+
+static int mtk_raw_set_res_ctrl(struct device *dev, struct v4l2_ctrl *ctrl,
+				struct mtk_cam_resource_config *res_cfg,
+				int pipe_id)
+{
+	int ret = 0;
+	struct mtk_raw_pipeline *pipeline;
+
+	pipeline = mtk_cam_ctrl_handler_to_raw_pipeline(ctrl->handler);
+
+	if (ctrl->type == V4L2_CTRL_TYPE_INTEGER64)
+		dev_dbg(dev, "%s:pipe(%d):(name:%s, val:%lld)\n", __func__,
+			pipe_id, ctrl->name, *ctrl->p_new.p_s64);
+	else
+		dev_dbg(dev, "%s:pipe(%d):(name:%s, val:%d)\n", __func__,
+			pipe_id, ctrl->name, ctrl->val);
+
+	mutex_lock(&res_cfg->resource_lock);
+	switch (ctrl->id) {
+	case V4L2_CID_MTK_CAM_USED_ENGINE_LIMIT:
+		res_cfg->hwn_limit_max = ctrl->val;
+		break;
+	case V4L2_CID_MTK_CAM_BIN_LIMIT:
+		res_cfg->bin_limit = ctrl->val;
+		break;
+	case V4L2_CID_MTK_CAM_FRZ_LIMIT:
+		res_cfg->frz_limit = ctrl->val;
+		break;
+	case V4L2_CID_MTK_CAM_RESOURCE_PLAN_POLICY:
+		res_cfg->res_plan = ctrl->val;
+		break;
+	case V4L2_CID_MTK_CAM_RAW_PATH_SELECT:
+		res_cfg->raw_path = ctrl->val;
+		break;
+	case V4L2_CID_HBLANK:
+		res_cfg->hblank = ctrl->val;
+		break;
+	case V4L2_CID_VBLANK:
+		res_cfg->vblank = ctrl->val;
+		break;
+	case V4L2_CID_MTK_CAM_PIXEL_RATE:
+		res_cfg->sensor_pixel_rate = *ctrl->p_new.p_s64;
+		break;
+	case V4L2_CID_MTK_CAM_SYNC_ID:
+		pipeline->sync_id = *ctrl->p_new.p_s64;
+		break;
+	default:
+		dev_info(dev,
+			 "%s:pipe(%d):ctrl(id:0x%x,val:%d) not handled\n",
+			 __func__, pipe_id, ctrl->id, ctrl->val);
+		ret = -EINVAL;
+		break;
+	}
+	mutex_unlock(&res_cfg->resource_lock);
+
+	return ret;
+}
+
+static int mtk_cam_raw_try_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct device *dev;
+	struct mtk_raw_pipeline *pipeline;
+	struct mtk_cam_resource *res_user;
+	struct mtk_cam_resource_config res_cfg = {
+		.interval = {
+			.numerator = 1,
+			.denominator = 30
+		},
+	};
+	int ret = 0;
+
+	pipeline = mtk_cam_ctrl_handler_to_raw_pipeline(ctrl->handler);
+	dev = pipeline->raw->devs[pipeline->id];
+
+	switch (ctrl->id) {
+	case V4L2_CID_MTK_CAM_RAW_RESOURCE_CALC:
+		res_user = (struct mtk_cam_resource *)ctrl->p_new.p;
+		ret = mtk_cam_raw_res_store(pipeline, res_user);
+		if (ret)
+			break;
+
+		ret = mtk_cam_raw_res_copy_fmt_from_user(pipeline, res_user,
+							 &res_cfg.sink_fmt);
+		if (ret) {
+			ret = 0;
+			/* only corret the user's input, it may be request-based try and set fmt */
+			dev_dbg(dev,
+				"%s:pipe(%d): no sink_fmt from user, not calc res\n",
+				__func__, pipeline->id);
+			break;
+		}
+
+		dev_dbg(dev, "%s:pipe(%d): res ctrl start\n",
+			__func__, pipeline->id);
+		ret = mtk_cam_raw_try_res_ctrl(pipeline, res_user, &res_cfg,
+					       &res_cfg.sink_fmt);
+		if (ret)
+			break;
+
+		dev_dbg(dev,
+			"%s:pipe(%d):streaming(%d), feature_pending(0x%llx), feature_active(0x%llx)\n",
+			__func__, pipeline->id,
+			media_entity_is_streaming(&pipeline->subdev.entity),
+			pipeline->feature_pending, pipeline->feature_active);
+
+		dev_dbg(dev, "%s:pipe(%d): res ctrl end\n",
+			__func__, pipeline->id);
+			break;
+	case V4L2_CID_MTK_CAM_RAW_RESOURCE_UPDATE:
+		dev_info(dev,
+			 "%s:pipe(%d): skip V4L2_CID_MTK_CAM_RAW_RESOURCE_UPDATE: %d\n",
+			 __func__, pipeline->id, ctrl->val);
+		ret = 0; /* no support */
+		break;
+	/* skip control value checks */
+	case V4L2_CID_MTK_CAM_FEATURE:
+	case V4L2_CID_MTK_CAM_CAMSYS_HW_MODE:
+		ret = 0;
+		break;
+	default:
+		ret = mtk_raw_set_res_ctrl(pipeline->raw->devs[pipeline->id],
+					   ctrl, &pipeline->try_res_config,
+					   pipeline->id);
+		break;
+	}
+
+	return ret;
+}
+
+static int mtk_cam_raw_set_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct device *dev;
+	struct mtk_raw_pipeline *pipeline;
+	int ret = 0;
+
+	pipeline = mtk_cam_ctrl_handler_to_raw_pipeline(ctrl->handler);
+	dev = pipeline->raw->devs[pipeline->id];
+
+	switch (ctrl->id) {
+	case V4L2_CID_MTK_CAM_RAW_RESOURCE_CALC:
+		/**
+		 * It also updates V4L2_CID_MTK_CAM_FEATURE and
+		 * V4L2_CID_MTK_CAM_RAW_PATH_SELECT to device
+		 */
+		ret = mtk_cam_raw_set_res_ctrl(ctrl);
+		break;
+	case V4L2_CID_MTK_CAM_RAW_RESOURCE_UPDATE:
+		/**
+		 * sensor_mode_update should be reset by driver after the completion
+		 * of resource updating (seamless switch)
+		 */
+		pipeline->sensor_mode_update = ctrl->val;
+		dev_info(dev,
+			 "%s:pipe(%d):streaming(%d), sensor_mode_update(%d)\n",
+			 __func__, pipeline->id,
+			 media_entity_is_streaming(&pipeline->subdev.entity),
+			 pipeline->sensor_mode_update);
+		break;
+	case V4L2_CID_MTK_CAM_FEATURE:
+		pipeline->feature_pending = *ctrl->p_new.p_s64;
+		dev_dbg(dev,
+			"%s:pipe(%d):streaming(%d), feature_pending(0x%llx), feature_active(0x%llx)\n",
+			__func__, pipeline->id,
+			media_entity_is_streaming(&pipeline->subdev.entity),
+			pipeline->feature_pending, pipeline->feature_active);
+		ret = 0;
+		break;
+	case V4L2_CID_MTK_CAM_CAMSYS_HW_MODE:
+		pipeline->hw_mode = *ctrl->p_new.p_s64;
+		dev_dbg(dev,
+			"%s:pipe(%d):streaming(%d), hw_mode(0x%llx)\n",
+			__func__, pipeline->id,
+			media_entity_is_streaming(&pipeline->subdev.entity),
+			pipeline->hw_mode);
+
+		ret = 0;
+		break;
+	default:
+		ret = mtk_raw_set_res_ctrl(pipeline->raw->devs[pipeline->id],
+					   ctrl, &pipeline->res_config,
+					   pipeline->id);
+		break;
+	}
+
+	return ret;
+}
+
+static const struct v4l2_ctrl_ops cam_ctrl_ops = {
+	.g_volatile_ctrl = mtk_cam_raw_get_ctrl,
+	.s_ctrl = mtk_cam_raw_set_ctrl,
+	.try_ctrl = mtk_cam_raw_try_ctrl,
+};
+
+static int mtk_raw_pde_get_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct mtk_raw_pipeline *pipeline;
+	struct mtk_raw_pde_config *pde_cfg;
+	struct mtk_cam_pde_info *pde_info_p;
+	struct device *dev;
+	int ret = 0;
+
+	pipeline = mtk_cam_ctrl_handler_to_raw_pipeline(ctrl->handler);
+	pde_cfg = &pipeline->pde_config;
+	pde_info_p = ctrl->p_new.p;
+	dev = pipeline->raw->devs[pipeline->id];
+
+	switch (ctrl->id) {
+	case V4L2_CID_MTK_CAM_PDE_INFO:
+		pde_info_p->pdo_max_size = pde_cfg->pde_info.pdo_max_size;
+		pde_info_p->pdi_max_size = pde_cfg->pde_info.pdi_max_size;
+		pde_info_p->pd_table_offset = pde_cfg->pde_info.pd_table_offset;
+		break;
+	default:
+		dev_info(dev, "%s(id:0x%x,val:%d) is not handled\n",
+			 __func__, ctrl->id, ctrl->val);
+		ret = -EINVAL;
+	}
+
+	return ret;
+}
+
+static int mtk_raw_pde_set_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct mtk_raw_pipeline *pipeline;
+	struct mtk_raw_pde_config *pde_cfg;
+	struct mtk_cam_pde_info *pde_info_p;
+	struct device *dev;
+	int ret = 0;
+	struct mtk_cam_video_device *node;
+	struct mtk_cam_dev_node_desc *desc;
+	const struct v4l2_format *default_fmt;
+
+	pipeline = mtk_cam_ctrl_handler_to_raw_pipeline(ctrl->handler);
+	pde_cfg = &pipeline->pde_config;
+	pde_info_p = ctrl->p_new.p;
+	dev = pipeline->raw->devs[pipeline->id];
+
+	node = &pipeline->vdev_nodes[MTK_RAW_META_IN - MTK_RAW_SINK_NUM];
+	desc = &node->desc;
+	default_fmt = &desc->fmts[desc->default_fmt_idx].vfmt;
+
+	switch (ctrl->id) {
+	case V4L2_CID_MTK_CAM_PDE_INFO:
+		if (!pde_info_p->pdo_max_size || !pde_info_p->pdi_max_size) {
+			dev_info(dev,
+				 "%s:pdo_max_sz(%d)/pdi_max_sz(%d) cannot be 0\n",
+				 __func__, pde_info_p->pdo_max_size,
+				 pde_info_p->pdi_max_size);
+			ret = -EINVAL;
+			break;
+		}
+
+		pde_cfg->pde_info.pdo_max_size = pde_info_p->pdo_max_size;
+		pde_cfg->pde_info.pdi_max_size = pde_info_p->pdi_max_size;
+		pde_cfg->pde_info.pd_table_offset =
+			default_fmt->fmt.meta.buffersize;
+		break;
+	default:
+		dev_info(dev, "%s(id:0x%x,val:%d) is not handled\n",
+			 __func__, ctrl->id, ctrl->val);
+		ret = -EINVAL;
+	}
+
+	return ret;
+}
+
+static const struct v4l2_ctrl_ops cam_pde_ctrl_ops = {
+	.g_volatile_ctrl = mtk_raw_pde_get_ctrl,
+	.s_ctrl = mtk_raw_pde_set_ctrl,
+	.try_ctrl = mtk_raw_pde_set_ctrl,
+};
+
+static const struct v4l2_ctrl_config hwn_limit = {
+	.ops = &cam_ctrl_ops,
+	.id = V4L2_CID_MTK_CAM_USED_ENGINE_LIMIT,
+	.name = "Engine resource limitation",
+	.type = V4L2_CTRL_TYPE_INTEGER,
+	.min = 1,
+	.max = 2,
+	.step = 1,
+	.def = 2,
+};
+
+static const struct v4l2_ctrl_config bin_limit = {
+	.ops = &cam_ctrl_ops,
+	.id = V4L2_CID_MTK_CAM_BIN_LIMIT,
+	.name = "Binning limitation",
+	.type = V4L2_CTRL_TYPE_INTEGER,
+	.min = 0,
+	.max = 0xfff,
+	.step = 1,
+	.def = 0,
+};
+
+static const struct v4l2_ctrl_config frz_limit = {
+	.ops = &cam_ctrl_ops,
+	.id = V4L2_CID_MTK_CAM_FRZ_LIMIT,
+	.name = "Resizer limitation",
+	.type = V4L2_CTRL_TYPE_INTEGER,
+	.min = 70,
+	.max = 100,
+	.step = 1,
+	.def = 100,
+};
+
+static const struct v4l2_ctrl_config hwn = {
+	.ops = &cam_ctrl_ops,
+	.id = V4L2_CID_MTK_CAM_USED_ENGINE,
+	.name = "Engine resource",
+	.type = V4L2_CTRL_TYPE_INTEGER,
+	.min = 1,
+	.max = 2,
+	.step = 1,
+	.def = 2,
+};
+
+static const struct v4l2_ctrl_config bin = {
+	.ops = &cam_ctrl_ops,
+	.id = V4L2_CID_MTK_CAM_BIN,
+	.name = "Binning",
+	.type = V4L2_CTRL_TYPE_BOOLEAN,
+	.min = 0,
+	.max = 1,
+	.step = 1,
+	.def = 1,
+};
+
+static const struct v4l2_ctrl_config frz = {
+	.ops = &cam_ctrl_ops,
+	.id = V4L2_CID_MTK_CAM_FRZ,
+	.name = "Resizer",
+	.type = V4L2_CTRL_TYPE_INTEGER,
+	.min = 70,
+	.max = 100,
+	.step = 1,
+	.def = 100,
+};
+
+static const struct v4l2_ctrl_config raw_path = {
+	.ops = &cam_ctrl_ops,
+	.id = V4L2_CID_MTK_CAM_RAW_PATH_SELECT,
+	.name = "Raw Path",
+	.type = V4L2_CTRL_TYPE_INTEGER,
+	.min = 0,
+	.max = 1,
+	.step = 1,
+	.def = 1,
+};
+
+static const struct v4l2_ctrl_config hwn_try = {
+	.ops = &cam_ctrl_ops,
+	.id = V4L2_CID_MTK_CAM_USED_ENGINE_TRY,
+	.name = "Engine resource",
+	.type = V4L2_CTRL_TYPE_INTEGER,
+	.min = 1,
+	.max = 2,
+	.step = 1,
+	.def = 2,
+};
+
+static const struct v4l2_ctrl_config bin_try = {
+	.ops = &cam_ctrl_ops,
+	.id = V4L2_CID_MTK_CAM_BIN_TRY,
+	.name = "Binning",
+	.type = V4L2_CTRL_TYPE_BOOLEAN,
+	.min = 0,
+	.max = 1,
+	.step = 1,
+	.def = 1,
+};
+
+static const struct v4l2_ctrl_config frz_try = {
+	.ops = &cam_ctrl_ops,
+	.id = V4L2_CID_MTK_CAM_FRZ_TRY,
+	.name = "Resizer",
+	.type = V4L2_CTRL_TYPE_INTEGER,
+	.min = 70,
+	.max = 100,
+	.step = 1,
+	.def = 100,
+};
+
+static const struct v4l2_ctrl_config res_plan_policy = {
+	.ops = &cam_ctrl_ops,
+	.id = V4L2_CID_MTK_CAM_RESOURCE_PLAN_POLICY,
+	.name = "Resource planning policy",
+	.type = V4L2_CTRL_TYPE_INTEGER,
+	.min = 0,
+	.max = 10,
+	.step = 1,
+	.def = 1,
+};
+
+static const struct v4l2_ctrl_config res_pixel_rate = {
+	.ops = &cam_ctrl_ops,
+	.id = V4L2_CID_MTK_CAM_PIXEL_RATE,
+	.name = "Resource pixel rate",
+	.type = V4L2_CTRL_TYPE_INTEGER64,
+	.min = 0,
+	.max = 0xFFFFFFFF,
+	.step = 1,
+	.def = 0,
+};
+
+static const struct v4l2_ctrl_config frame_sync_id = {
+	.ops = &cam_ctrl_ops,
+	.id = V4L2_CID_MTK_CAM_SYNC_ID,
+	.name = "Frame sync id",
+	.type = V4L2_CTRL_TYPE_INTEGER64,
+	.min = -1,
+	.max = 0x7FFFFFFF,
+	.step = 1,
+	.def = -1,
+};
+
+static const struct v4l2_ctrl_config mtk_feature = {
+	.ops = &cam_ctrl_ops,
+	.id = V4L2_CID_MTK_CAM_FEATURE,
+	.name = "mediatek camsys feature",
+	.type = V4L2_CTRL_TYPE_INTEGER64,
+	.min = 0,
+	.max = RAW_FUNCTION_END,
+	.step = 1,
+	.def = 0,
+};
+
+static struct v4l2_ctrl_config cfg_res_ctrl = {
+	.ops = &cam_ctrl_ops,
+	.id = V4L2_CID_MTK_CAM_RAW_RESOURCE_CALC,
+	.name = "resource ctrl",
+	.type = V4L2_CTRL_COMPOUND_TYPES, /* V4L2_CTRL_TYPE_U32,*/
+	.flags = V4L2_CTRL_FLAG_EXECUTE_ON_WRITE,
+	.max = 0xffffffff,
+	.step = 1,
+	.dims = {sizeof(struct mtk_cam_resource)},
+};
+
+static struct v4l2_ctrl_config cfg_res_update = {
+	.ops = &cam_ctrl_ops,
+	.id = V4L2_CID_MTK_CAM_RAW_RESOURCE_UPDATE,
+	.name = "resource update",
+	.type = V4L2_CTRL_TYPE_INTEGER,
+	.min = 0,
+	.max = 0xf,
+	.step = 1,
+	.def = 0,
+};
+
+static const struct v4l2_ctrl_config cfg_pde_info = {
+	.ops = &cam_pde_ctrl_ops,
+	.id = V4L2_CID_MTK_CAM_PDE_INFO,
+	.name = "pde information",
+	.type = V4L2_CTRL_TYPE_INTEGER,
+	.flags = V4L2_CTRL_FLAG_VOLATILE,
+	.min = 0,
+	.max = 0x1fffffff,
+	.step = 1,
+	.def = 0,
+	.dims = {sizeof_u32(struct mtk_cam_pde_info)},
+};
+
+static const struct v4l2_ctrl_config mtk_camsys_hw_mode = {
+	.ops = &cam_ctrl_ops,
+	.id = V4L2_CID_MTK_CAM_CAMSYS_HW_MODE,
+	.name = "mediatek camsys hardware mode",
+	.type = V4L2_CTRL_TYPE_INTEGER64,
+	.min = 0,
+	.max = 0x7FFFFFFF,
+	.step = 1,
+	.def = DEFAULT,
+};
+
+static void mtk_cam_raw_toggle_db(struct mtk_raw_device *dev)
+{
+	int value;
+	u32 val_cfg, val_dcif_ctl, val_sen;
+
+	value = readl_relaxed(dev->base + REG_CTL_MISC);
+	val_cfg = readl_relaxed(dev->base_inner + REG_TG_PATH_CFG);
+	val_dcif_ctl = readl_relaxed(dev->base_inner + REG_TG_DCIF_CTL);
+	val_sen = readl_relaxed(dev->base_inner + REG_TG_SEN_MODE);
+	writel_relaxed(value & ~CTL_DB_EN, dev->base + REG_CTL_MISC);
+	wmb(); /* make sure committed */
+	writel_relaxed(value | CTL_DB_EN, dev->base + REG_CTL_MISC);
+	wmb(); /* make sure committed */
+	dev_info(dev->dev,
+		 "%s,  read inner AsIs->ToBe TG_PATH_CFG:0x%x->0x%x, TG_DCIF:0x%x->0x%x, TG_SEN:0x%x->0x%x\n",
+		 __func__, val_cfg,
+		 readl_relaxed(dev->base_inner + REG_TG_PATH_CFG), val_dcif_ctl,
+		 readl_relaxed(dev->base_inner + REG_TG_DCIF_CTL), val_sen,
+		 readl_relaxed(dev->base_inner + REG_TG_SEN_MODE));
+}
+
+void mtk_cam_raw_trigger_rawi(struct mtk_raw_device *dev,
+			      struct mtk_cam_ctx *ctx, signed int hw_scene)
+{
+	#define TRIGGER_RAWI_R6 0x10
+	#define TRIGGER_RAWI_R2 0x01
+	u32 cmd = TRIGGER_RAWI_R2;
+
+	writel_relaxed(cmd, dev->base + REG_CTL_RAWI_TRIG);
+	wmb(); /* make sure committed */
+}
+
+void mtk_cam_raw_apply_cq(struct mtk_raw_device *dev, dma_addr_t cq_addr,
+			  unsigned int cq_size, unsigned int cq_offset,
+			  unsigned int sub_cq_size, unsigned int sub_cq_offset)
+{
+	dma_addr_t main, sub;
+
+	dev_dbg(dev->dev,
+		"apply raw%d cq - addr:0x%pad,size:%d/%d,offset:%d\n",
+		dev->id, &cq_addr, cq_size, sub_cq_size, sub_cq_offset);
+
+	main = cq_addr + cq_offset;
+	sub = cq_addr + sub_cq_offset;
+
+	writel_relaxed(dmaaddr_lsb(main),
+		       dev->base + REG_CQ_THR0_BASEADDR);
+	writel_relaxed(dmaaddr_msb(main),
+		       dev->base + REG_CQ_THR0_BASEADDR_MSB);
+	writel_relaxed(cq_size,
+		       dev->base + REG_CQ_THR0_DESC_SIZE);
+	writel_relaxed(dmaaddr_lsb(sub),
+		       dev->base + REG_CQ_SUB_THR0_BASEADDR_2);
+	writel_relaxed(dmaaddr_msb(sub),
+		       dev->base + REG_CQ_SUB_THR0_BASEADDR_MSB_2);
+	writel_relaxed(sub_cq_size,
+		       dev->base + REG_CQ_SUB_THR0_DESC_SIZE_2);
+
+	/* USINGSCQ and always trigger now */
+	writel_relaxed(CAMCTL_CQ_THR0_DONE_ST,
+		       dev->base + REG_CTL_RAW_INT6_EN);
+	writel_relaxed(CAMCTL_CQ_THRSUB_DONE_EN,
+		       dev->base + REG_CTL_RAW_INT7_EN);
+	writel(CTL_CQ_THR0_START, dev->base + REG_CTL_START);
+
+	wmb(); /* make sure committed */
+}
+
+/* sw check again for rawi dcif case */
+static bool mtk_cam_raw_is_dma_idle(struct mtk_raw_device *dev)
+{
+	bool ret = false;
+	int chasing_stat;
+	int raw_rst_stat = readl(dev->base + REG_DMA_SOFT_RST_STAT);
+	int raw_rst_stat2 = readl(dev->base + REG_DMA_SOFT_RST_STAT2);
+	int yuv_rst_stat = readl(dev->yuv_base + REG_DMA_SOFT_RST_STAT);
+
+	if (raw_rst_stat2 != 0x7 || yuv_rst_stat != 0xfffffff)
+		return false;
+
+	/* check beside rawi_r2/r3/r5 */
+	if (~raw_rst_stat & 0x7fffffda)
+		return false;
+
+	if (~raw_rst_stat & RST_STAT_RAWI_R2) { /* RAWI_R2 */
+		chasing_stat = readl(dev->base + REG_DMA_DBG_CHASING_STATUS);
+		ret = ((chasing_stat & RAWI_R2_SMI_REQ_ST) == 0 &&
+		       (readl(dev->base + REG_RAWI_R2_BASE + DMA_OFFSET_SPECIAL_DCIF)
+		       & DC_CAMSV_STAGER_EN) &&
+		       (readl(dev->base + REG_CTL_MOD6_EN) & 0x1))
+		      ? true : false;
+		dev_info(dev->dev, "%s: chasing_stat: 0x%x ret=%d\n",
+			 __func__, chasing_stat, ret);
+	}
+
+	if (~raw_rst_stat & RST_STAT_RAWI_R3) {
+		chasing_stat = readl(dev->base + REG_DMA_DBG_CHASING_STATUS);
+		ret = ((chasing_stat & RAWI_R3_SMI_REQ_ST) == 0 &&
+		       (readl(dev->base + REG_RAWI_R3_BASE + DMA_OFFSET_SPECIAL_DCIF)
+		       & DC_CAMSV_STAGER_EN) &&
+		       (readl(dev->base + REG_CTL_MOD6_EN) & 0x80))
+		      ? true : false;
+		dev_info(dev->dev, "%s: chasing_stat: 0x%x, ret=%d\n",
+			 __func__, chasing_stat, ret);
+	}
+
+	if (~raw_rst_stat & RST_STAT_RAWI_R5) {
+		chasing_stat = readl(dev->base + REG_DMA_DBG_CHASING_STATUS2);
+		ret = ((chasing_stat & RAWI_R5_SMI_REQ_ST) == 0 &&
+		       (readl(dev->base + REG_RAWI_R5_BASE + DMA_OFFSET_SPECIAL_DCIF)
+		       & DC_CAMSV_STAGER_EN) &&
+		       (readl(dev->base + REG_CTL_MOD6_EN) & 0x1000))
+		      ? true : false;
+		dev_info(dev->dev, "%s: chasing_stat: 0x%x, ret=%d\n",
+			 __func__, chasing_stat, ret);
+	}
+
+	return ret;
+}
+
+void mtk_cam_raw_reset(struct mtk_raw_device *dev)
+{
+	int sw_ctl;
+	int ret;
+
+	dev_dbg(dev->dev, "camsys | trigger %s\n", __func__);
+
+	/* Disable all DMA DCM before reset */
+	writel(0x00000fff, dev->base + REG_CTL_RAW_MOD5_DCM_DIS);
+	writel(0x0007ffff, dev->base + REG_CTL_RAW_MOD6_DCM_DIS);
+	writel(0xffffffff, dev->yuv_base + REG_CTL_RAW_MOD5_DCM_DIS);
+
+	/* Enable CQI_R1 ~ R4 before reset and make sure loaded */
+	writel(readl(dev->base + REG_CTL_MOD6_EN) | 0x78000,
+	       dev->base + REG_CTL_MOD6_EN);
+	mtk_cam_raw_toggle_db(dev);
+
+	writel(0, dev->base + REG_CTL_SW_CTL);
+	writel(1, dev->base + REG_CTL_SW_CTL);
+	wmb(); /* make sure committed */
+
+	ret = readx_poll_timeout(readl, dev->base + REG_CTL_SW_CTL, sw_ctl,
+				 sw_ctl & 0x2,
+				 1 /* delay, us */, 5000 /* timeout, us */);
+
+	if (ret < 0 && !mtk_cam_raw_is_dma_idle(dev)) {
+		dev_info(dev->dev,
+			 "%s: timeout: tg_sen_mode: 0x%x, ctl_en: 0x%x, mod6_en: 0x%x, ctl_sw_ctl:0x%x, frame_no:0x%x,rst_stat:0x%x,rst_stat2:0x%x,yuv_rst_stat:0x%x\n",
+			 __func__,
+			 readl(dev->base + REG_TG_SEN_MODE),
+			 readl(dev->base + REG_CTL_EN),
+			 readl(dev->base + REG_CTL_MOD6_EN),
+			 readl(dev->base + REG_CTL_SW_CTL),
+			 readl(dev->base + REG_FRAME_SEQ_NUM),
+			 readl(dev->base + REG_DMA_SOFT_RST_STAT),
+			 readl(dev->base + REG_DMA_SOFT_RST_STAT2),
+			 readl(dev->yuv_base + REG_DMA_SOFT_RST_STAT));
+
+		goto RESET_FAILURE;
+	}
+
+	/* do hw rst */
+	writel(4, dev->base + REG_CTL_SW_CTL);
+	writel(0, dev->base + REG_CTL_SW_CTL);
+
+RESET_FAILURE:
+	/* Enable all DMA DCM back */
+	writel(0x0, dev->base + REG_CTL_RAW_MOD5_DCM_DIS);
+	writel(0x0, dev->base + REG_CTL_RAW_MOD6_DCM_DIS);
+	writel(0x0, dev->yuv_base + REG_CTL_RAW_MOD5_DCM_DIS);
+
+	wmb(); /* make sure committed */
+}
+
+static void mtk_cam_raw_reset_reg(struct mtk_raw_device *dev)
+{
+	int cq_en, sw_done;
+
+	dev_dbg(dev->dev,
+		"[%s++] CQ_EN/SW_SUB_CTL/SW_DONE [in] 0x%x/0x%x/0x%x [out] 0x%x/0x%x/0x%x\n",
+		__func__,
+		readl_relaxed(dev->base_inner + REG_CQ_EN),
+		readl_relaxed(dev->base_inner + REG_CTL_SW_SUB_CTL),
+		readl_relaxed(dev->base_inner + REG_CTL_SW_PASS1_DONE),
+		readl_relaxed(dev->base + REG_CQ_EN),
+		readl_relaxed(dev->base + REG_CTL_SW_SUB_CTL),
+		readl_relaxed(dev->base + REG_CTL_SW_PASS1_DONE));
+
+	cq_en = readl_relaxed(dev->base_inner + REG_CQ_EN);
+	sw_done = readl_relaxed(dev->base_inner + REG_CTL_SW_PASS1_DONE);
+
+	cq_en = cq_en & (~SCQ_SUBSAMPLE_EN) & (~SCQ_STAGGER_MODE);
+	writel(cq_en, dev->base_inner + REG_CQ_EN);
+	writel(cq_en, dev->base + REG_CQ_EN);
+
+	dev_dbg(dev->dev, "[--] try to disable SCQ_STAGGER_MODE: CQ_EN(0x%x)\n",
+		cq_en);
+
+	writel(sw_done & (~SW_DONE_SAMPLE_EN), dev->base_inner + REG_CTL_SW_PASS1_DONE);
+	writel(sw_done & (~SW_DONE_SAMPLE_EN), dev->base + REG_CTL_SW_PASS1_DONE);
+
+	writel(0, dev->base_inner + REG_CTL_SW_SUB_CTL);
+	writel(0, dev->base + REG_CTL_SW_SUB_CTL);
+
+	wmb(); /* make sure committed */
+
+	dev_dbg(dev->dev,
+		"[%s--] CQ_EN/SW_SUB_CTL/SW_DONE [in] 0x%x/0x%x/0x%x [out] 0x%x/0x%x/0x%x\n",
+		__func__, readl_relaxed(dev->base_inner + REG_CQ_EN),
+		readl_relaxed(dev->base_inner + REG_CTL_SW_SUB_CTL),
+		readl_relaxed(dev->base_inner + REG_CTL_SW_PASS1_DONE),
+		readl_relaxed(dev->base + REG_CQ_EN),
+		readl_relaxed(dev->base + REG_CTL_SW_SUB_CTL),
+		readl_relaxed(dev->base + REG_CTL_SW_PASS1_DONE));
+}
+
+void mtk_cam_raw_dump_aa_info(struct mtk_cam_ctx *ctx,
+			      struct mtk_ae_debug_data *ae_info)
+{
+	struct mtk_raw_device *raw_dev = NULL;
+	struct mtk_raw_pipeline *pipe = ctx->pipe;
+	int i;
+
+	for (i = 0; i < ctx->cam->num_raw_devices; i++) {
+		if (pipe->enabled_raw & (1 << i)) {
+			struct device *dev = ctx->cam->raw.devs[i];
+
+			raw_dev = dev_get_drvdata(dev);
+			ae_info->obc_r1_sum[0] +=
+				((u64)readl(raw_dev->base + OFFSET_OBC_R1_R_SUM_H) << 32) |
+				readl(raw_dev->base + OFFSET_OBC_R1_R_SUM_L);
+			ae_info->obc_r2_sum[0] +=
+				((u64)readl(raw_dev->base + OFFSET_OBC_R2_R_SUM_H) << 32) |
+				readl(raw_dev->base + OFFSET_OBC_R2_R_SUM_L);
+			ae_info->obc_r3_sum[0] +=
+				((u64)readl(raw_dev->base + OFFSET_OBC_R3_R_SUM_H) << 32) |
+				readl(raw_dev->base + OFFSET_OBC_R3_R_SUM_L);
+			ae_info->ltm_sum[0] +=
+				((u64)readl(raw_dev->base + REG_LTM_AE_DEBUG_R_MSB) << 32) |
+				readl(raw_dev->base + REG_LTM_AE_DEBUG_R_LSB);
+			ae_info->aa_sum[0] +=
+				((u64)readl(raw_dev->base + REG_AA_R_SUM_H) << 32) |
+				readl(raw_dev->base + REG_AA_R_SUM_L);
+
+			ae_info->obc_r1_sum[1] +=
+				((u64)readl(raw_dev->base + OFFSET_OBC_R1_B_SUM_H) << 32) |
+				readl(raw_dev->base + OFFSET_OBC_R1_B_SUM_L);
+			ae_info->obc_r2_sum[1] +=
+				((u64)readl(raw_dev->base + OFFSET_OBC_R2_B_SUM_H) << 32) |
+				readl(raw_dev->base + OFFSET_OBC_R2_B_SUM_L);
+			ae_info->obc_r3_sum[1] +=
+				((u64)readl(raw_dev->base + OFFSET_OBC_R3_B_SUM_H) << 32) |
+				readl(raw_dev->base + OFFSET_OBC_R3_B_SUM_L);
+			ae_info->ltm_sum[1] +=
+				((u64)readl(raw_dev->base + REG_LTM_AE_DEBUG_B_MSB) << 32) |
+				readl(raw_dev->base + REG_LTM_AE_DEBUG_B_LSB);
+			ae_info->aa_sum[1] +=
+				((u64)readl(raw_dev->base + REG_AA_B_SUM_H) << 32) |
+				readl(raw_dev->base + REG_AA_B_SUM_L);
+
+			ae_info->obc_r1_sum[2] +=
+				((u64)readl(raw_dev->base + OFFSET_OBC_R1_GR_SUM_H) << 32) |
+				readl(raw_dev->base + OFFSET_OBC_R1_GR_SUM_L);
+			ae_info->obc_r2_sum[2] +=
+				((u64)readl(raw_dev->base + OFFSET_OBC_R2_GR_SUM_H) << 32) |
+				readl(raw_dev->base + OFFSET_OBC_R2_GR_SUM_L);
+			ae_info->obc_r3_sum[2] +=
+				((u64)readl(raw_dev->base + OFFSET_OBC_R3_GR_SUM_H) << 32) |
+				readl(raw_dev->base + OFFSET_OBC_R3_GR_SUM_L);
+			ae_info->ltm_sum[2] +=
+				((u64)readl(raw_dev->base + REG_LTM_AE_DEBUG_GR_MSB) << 32) |
+				readl(raw_dev->base + REG_LTM_AE_DEBUG_GR_LSB);
+			ae_info->aa_sum[2] +=
+				((u64)readl(raw_dev->base + REG_AA_GR_SUM_H) << 32) |
+				readl(raw_dev->base + REG_AA_GR_SUM_L);
+
+			ae_info->obc_r1_sum[3] +=
+				((u64)readl(raw_dev->base + OFFSET_OBC_R1_GB_SUM_H) << 32) |
+				readl(raw_dev->base + OFFSET_OBC_R1_GB_SUM_L);
+			ae_info->obc_r2_sum[3] +=
+				((u64)readl(raw_dev->base + OFFSET_OBC_R2_GB_SUM_H) << 32) |
+				readl(raw_dev->base + OFFSET_OBC_R2_GB_SUM_L);
+			ae_info->obc_r3_sum[3] +=
+				((u64)readl(raw_dev->base + OFFSET_OBC_R3_GB_SUM_H) << 32) |
+				readl(raw_dev->base + OFFSET_OBC_R3_GB_SUM_L);
+			ae_info->ltm_sum[3] +=
+				((u64)readl(raw_dev->base + REG_LTM_AE_DEBUG_GB_MSB) << 32) |
+				readl(raw_dev->base + REG_LTM_AE_DEBUG_GB_LSB);
+			ae_info->aa_sum[3] +=
+				((u64)readl(raw_dev->base + REG_AA_GB_SUM_H) << 32) |
+				readl(raw_dev->base + REG_AA_GB_SUM_L);
+		}
+	}
+}
+
+static int reset_msgfifo(struct mtk_raw_device *dev)
+{
+	atomic_set(&dev->is_fifo_overflow, 0);
+	return kfifo_init(&dev->msg_fifo, dev->msg_buffer, dev->fifo_size);
+}
+
+static int push_msgfifo(struct mtk_raw_device *dev,
+			struct mtk_camsys_irq_info *info)
+{
+	int len;
+
+	if (unlikely(kfifo_avail(&dev->msg_fifo) < sizeof(*info))) {
+		atomic_set(&dev->is_fifo_overflow, 1);
+		return -1;
+	}
+
+	len = kfifo_in(&dev->msg_fifo, info, sizeof(*info));
+	WARN_ON(len != sizeof(*info));
+
+	return 0;
+}
+
+static void set_fifo_size(void __iomem *dma_base, int fifo_size)
+{
+	writel_relaxed((0x1 << 28) | (fifo_size & 0xFFF),
+		       dma_base + DMA_OFFSET_CON0);
+}
+
+static void set_fifo_threshold(void __iomem *dma_base)
+{
+	u32 fifo_size = 0;
+
+	fifo_size = readl_relaxed(dma_base + DMA_OFFSET_CON0) & 0xFFF;
+	writel_relaxed((0x1 << 28) |
+		       ((fifo_size * 1 / 4) & 0xFFF) << 16 |
+		       ((fifo_size * 1 / 8) & 0xFFF),
+		       dma_base + DMA_OFFSET_CON1);
+	writel_relaxed((0x1 << 28) |
+		       ((fifo_size * 1 / 2) & 0xFFF) << 16 |
+		       ((fifo_size * 3 / 8) & 0xFFF),
+		       dma_base + DMA_OFFSET_CON2);
+	writel_relaxed((0x1 << 31) |
+		       ((fifo_size * 2 / 3) & 0xFFF) << 16 |
+		       ((fifo_size * 13 / 24) & 0xFFF),
+		       dma_base + DMA_OFFSET_CON3);
+	writel_relaxed((0x1 << 31) |
+		       ((fifo_size * 3 / 8) & 0xFFF) << 16 |
+		       ((fifo_size * 1 / 4) & 0xFFF),
+		       dma_base + DMA_OFFSET_CON4);
+}
+
+static void init_dma_threshold(struct mtk_raw_device *dev)
+{
+	struct mtk_cam_device *cam_dev;
+
+	cam_dev = dev->cam;
+
+	/* Set mt8188 default fifo size */
+	set_fifo_size(dev->base + REG_IMGO_R1_BASE, 0x3C0);
+	set_fifo_size(dev->yuv_base + REG_YUVCO_R1_BASE, 0x200);
+	set_fifo_size(dev->yuv_base + REG_YUVDO_R1_BASE, 0x80);
+	set_fifo_size(dev->yuv_base + REG_YUVO_R3_BASE, 0xc0);
+	set_fifo_size(dev->yuv_base + REG_YUVBO_R3_BASE, 0x60);
+	set_fifo_size(dev->yuv_base + REG_YUVCO_R3_BASE, 0x40);
+	set_fifo_size(dev->yuv_base + REG_YUVO_R2_BASE, 0x80);
+	set_fifo_size(dev->yuv_base + REG_YUVBO_R2_BASE, 0x40);
+	set_fifo_size(dev->yuv_base + REG_YUVO_R4_BASE, 0x40);
+	set_fifo_size(dev->yuv_base + REG_YUVBO_R4_BASE, 0x40);
+	set_fifo_size(dev->yuv_base + REG_YUVO_R5_BASE, 0x40);
+	set_fifo_threshold(dev->base + REG_IMGO_R1_BASE);
+	set_fifo_threshold(dev->base + REG_FHO_R1_BASE);
+	set_fifo_threshold(dev->base + REG_AAHO_R1_BASE);
+	set_fifo_threshold(dev->base + REG_PDO_R1_BASE);
+	set_fifo_threshold(dev->base + REG_AAO_R1_BASE);
+	set_fifo_threshold(dev->base + REG_AFO_R1_BASE);
+	set_fifo_threshold(dev->base + REG_PDO_R1_BASE);
+
+	set_fifo_threshold(dev->yuv_base + REG_YUVO_R1_BASE);
+	set_fifo_threshold(dev->yuv_base + REG_YUVBO_R1_BASE);
+	set_fifo_threshold(dev->yuv_base + REG_YUVCO_R1_BASE);
+	set_fifo_threshold(dev->yuv_base + REG_YUVDO_R1_BASE);
+	set_fifo_threshold(dev->yuv_base + REG_YUVO_R3_BASE);
+	set_fifo_threshold(dev->yuv_base + REG_YUVBO_R3_BASE);
+	set_fifo_threshold(dev->yuv_base + REG_YUVCO_R3_BASE);
+	set_fifo_threshold(dev->yuv_base + REG_LTMSO_R1_BASE);
+	set_fifo_threshold(dev->yuv_base + REG_TSFSO_R1_BASE);
+	set_fifo_threshold(dev->yuv_base + REG_TSFSO_R2_BASE);
+	set_fifo_threshold(dev->yuv_base + REG_FLKO_R1_BASE);
+	set_fifo_threshold(dev->yuv_base + REG_UFEO_R1_BASE);
+	set_fifo_threshold(dev->yuv_base + REG_YUVO_R2_BASE);
+	set_fifo_threshold(dev->yuv_base + REG_YUVBO_R2_BASE);
+	set_fifo_threshold(dev->yuv_base + REG_YUVO_R4_BASE);
+	set_fifo_threshold(dev->yuv_base + REG_YUVBO_R4_BASE);
+	set_fifo_threshold(dev->yuv_base + REG_YUVO_R5_BASE);
+	set_fifo_threshold(dev->yuv_base + REG_YUVBO_R5_BASE);
+	set_fifo_threshold(dev->yuv_base + REG_RZH1N2TO_R1_BASE);
+	set_fifo_threshold(dev->yuv_base + REG_RZH1N2TBO_R1_BASE);
+	set_fifo_threshold(dev->yuv_base + REG_RZH1N2TO_R2_BASE);
+	set_fifo_threshold(dev->yuv_base + REG_RZH1N2TO_R3_BASE);
+	set_fifo_threshold(dev->yuv_base + REG_RZH1N2TBO_R3_BASE);
+	set_fifo_threshold(dev->yuv_base + REG_DRZS4NO_R1_BASE);
+	set_fifo_threshold(dev->yuv_base + REG_DRZS4NO_R2_BASE);
+	set_fifo_threshold(dev->yuv_base + REG_DRZS4NO_R3_BASE);
+	set_fifo_threshold(dev->yuv_base + REG_ACTSO_R1_BASE);
+	set_fifo_threshold(dev->yuv_base + REG_TNCSO_R1_BASE);
+	set_fifo_threshold(dev->yuv_base + REG_TNCSBO_R1_BASE);
+	set_fifo_threshold(dev->yuv_base + REG_TNCSHO_R1_BASE);
+	set_fifo_threshold(dev->yuv_base + REG_TNCSYO_R1_BASE);
+
+	set_fifo_threshold(dev->base + REG_RAWI_R2_BASE);
+	set_fifo_threshold(dev->base + REG_UFDI_R2_BASE);
+	set_fifo_threshold(dev->base + REG_RAWI_R3_BASE);
+	set_fifo_threshold(dev->base + REG_UFDI_R3_BASE);
+	set_fifo_threshold(dev->base + REG_CQI_R1_BASE);
+	set_fifo_threshold(dev->base + REG_CQI_R2_BASE);
+	set_fifo_threshold(dev->base + REG_CQI_R3_BASE);
+	set_fifo_threshold(dev->base + REG_CQI_R4_BASE);
+	set_fifo_threshold(dev->base + REG_LSCI_R1_BASE);
+	set_fifo_threshold(dev->base + REG_BPCI_R1_BASE);
+	set_fifo_threshold(dev->base + REG_BPCI_R2_BASE);
+	set_fifo_threshold(dev->base + REG_BPCI_R3_BASE);
+	set_fifo_threshold(dev->base + REG_PDI_R1_BASE);
+	set_fifo_threshold(dev->base + REG_AAI_R1_BASE);
+	set_fifo_threshold(dev->base + REG_CACI_R1_BASE);
+	set_fifo_threshold(dev->base + REG_RAWI_R6_BASE);
+
+	writel_relaxed(0x1, cam_dev->base + REG_HALT1_EN);
+	writel_relaxed(0x1, cam_dev->base + REG_HALT2_EN);
+	writel_relaxed(0x1, cam_dev->base + REG_HALT3_EN);
+	writel_relaxed(0x1, cam_dev->base + REG_HALT4_EN);
+	writel_relaxed(0x1, cam_dev->base + REG_HALT5_EN);
+	writel_relaxed(0x1, cam_dev->base + REG_HALT6_EN);
+	writel_relaxed(0x1, cam_dev->base + REG_ULTRA_HALT1_EN);
+	writel_relaxed(0x1, cam_dev->base + REG_ULTRA_HALT2_EN);
+	writel_relaxed(0x1, cam_dev->base + REG_ULTRA_HALT3_EN);
+	writel_relaxed(0x1, cam_dev->base + REG_ULTRA_HALT4_EN);
+	writel_relaxed(0x1, cam_dev->base + REG_ULTRA_HALT5_EN);
+	writel_relaxed(0x1, cam_dev->base + REG_ULTRA_HALT6_EN);
+	writel_relaxed(0x1, cam_dev->base + REG_PREULTRA_HALT1_EN);
+	writel_relaxed(0x1, cam_dev->base + REG_PREULTRA_HALT2_EN);
+	writel_relaxed(0x1, cam_dev->base + REG_PREULTRA_HALT3_EN);
+	writel_relaxed(0x1, cam_dev->base + REG_PREULTRA_HALT4_EN);
+	writel_relaxed(0x1, cam_dev->base + REG_PREULTRA_HALT5_EN);
+	writel_relaxed(0x1, cam_dev->base + REG_PREULTRA_HALT6_EN);
+}
+
+static int get_fps_ratio(struct mtk_raw_device *dev)
+{
+	int fps = (dev->pipeline->res_config.interval.numerator > 0) ?
+			(dev->pipeline->res_config.interval.denominator /
+			dev->pipeline->res_config.interval.numerator) : 0;
+
+	if (fps <= 30)
+		return 1;
+	else if (fps <= 60)
+		return 2;
+	else
+		return 1;
+}
+
+void mtk_cam_raw_initialize(struct mtk_raw_device *dev, int is_sub)
+{
+	u32 val;
+
+	/* USINGSCQ */
+	val = readl_relaxed(dev->base + REG_CQ_EN);
+	writel_relaxed(val | SCQ_EN, dev->base + REG_CQ_EN);
+	writel_relaxed(0xffffffff, dev->base + REG_SCQ_START_PERIOD);
+
+	writel_relaxed(CQ_THR0_MODE_IMMEDIATE | CQ_THR0_EN,
+		       dev->base + REG_CQ_THR0_CTL);
+	writel_relaxed(CQ_THR0_MODE_IMMEDIATE | CQ_THR0_EN,
+		       dev->base + REG_CQ_SUB_THR0_CTL);
+	writel_relaxed(CAMCTL_CQ_THR0_DONE_ST,
+		       dev->base + REG_CTL_RAW_INT6_EN);
+	writel_relaxed(CAMCTL_CQ_THRSUB_DONE_EN,
+		       dev->base + REG_CTL_RAW_INT7_EN);
+
+	dev->is_sub = is_sub;
+	dev->sof_count = 0;
+	dev->vsync_count = 0;
+	atomic_set(&dev->vf_en, 0);
+	reset_msgfifo(dev);
+
+	init_dma_threshold(dev);
+
+	writel_relaxed(0xFFFE0000,
+		       dev->base + REG_FLKO_R1_BASE + DMA_OFFSET_ERR_STAT);
+
+	dev_dbg(dev->dev, "%s - REG_CQ_EN:0x%x ,REG_CQ_THR0_CTL:0x%8x\n",
+		__func__,
+		readl_relaxed(dev->base + REG_CQ_EN),
+		readl_relaxed(dev->base + REG_CQ_THR0_CTL));
+}
+
+void mtk_cam_raw_stream_on(struct mtk_raw_device *dev, int on)
+{
+	u32 val;
+	u32 chk_val;
+	u32 cfg_val;
+	u32 fps_ratio = 1;
+	struct mtk_cam_ctx *ctx;
+
+	dev_info(dev->dev, "raw %d %s %d\n", dev->id, __func__, on);
+	ctx = mtk_cam_find_ctx(dev->cam, &dev->pipeline->subdev.entity);
+	if (!ctx) {
+		dev_info(dev->dev, "%s: cannot find ctx\n", __func__);
+		return;
+	}
+
+	if (on) {
+		/* USINGSCQ */
+		val = readl_relaxed(dev->base + REG_TG_TIME_STAMP_CNT);
+		val = (val == 0) ? 1 : val;
+		fps_ratio = get_fps_ratio(dev);
+		dev_info(dev->dev,
+			 "VF on - REG_TG_TIME_STAMP_CNT val:%d fps(30x):%d\n",
+			 val, fps_ratio);
+		writel_relaxed(SCQ_DEADLINE_MS * 1000 * SCQ_DEFAULT_CLK_RATE /
+			       (val * 2) / fps_ratio,
+			       dev->base + REG_SCQ_START_PERIOD);
+
+		mtk_cam_set_topdebug_rdyreq(dev->dev, dev->base, dev->yuv_base,
+					    TG_OVERRUN);
+		dev->overrun_debug_dump_cnt = 0;
+		if (mtk_cam_is_m2m(ctx)) {
+			dev_info(dev->dev, "[%s] M2M view finder disable\n", __func__);
+		} else {
+			val = readl_relaxed(dev->base + REG_TG_VF_CON);
+			val |= TG_VFDATA_EN;
+			writel_relaxed(val, dev->base + REG_TG_VF_CON);
+			wmb(); /* make sure committed */
+		}
+		atomic_set(&dev->vf_en, 1);
+		dev_info(dev->dev,
+			 "%s - CQ_EN:0x%x, CQ_THR0_CTL:0x%8x, TG_VF_CON:0x%8x, SCQ_START_PERIOD:%d\n",
+			 __func__,
+			 readl_relaxed(dev->base + REG_CQ_EN),
+			 readl_relaxed(dev->base + REG_CQ_THR0_CTL),
+			 readl_relaxed(dev->base + REG_TG_VF_CON),
+			 readl_relaxed(dev->base + REG_SCQ_START_PERIOD));
+	} else {
+		dev_info(dev->dev, "VF off\n");
+		atomic_set(&dev->vf_en, 0);
+
+		writel_relaxed((u32)~CQ_THR0_EN, dev->base + REG_CQ_THR0_CTL);
+		wmb(); /* make sure committed */
+
+		cfg_val = readl_relaxed(dev->base + REG_TG_PATH_CFG);
+		cfg_val |= 0x100;
+		writel(cfg_val, dev->base + REG_TG_PATH_CFG);
+
+		val = readl_relaxed(dev->base + REG_TG_VF_CON);
+		val &= ~TG_VFDATA_EN;
+		writel(val, dev->base + REG_TG_VF_CON);
+
+		cfg_val = readl_relaxed(dev->base + REG_TG_PATH_CFG);
+		cfg_val &= ~0x100;
+		writel(cfg_val, dev->base + REG_TG_PATH_CFG);
+
+		wmb(); /* make sure committed */
+		/* reset hw after vf off */
+		if (readx_poll_timeout(readl, dev->base_inner + REG_TG_VF_CON,
+				       chk_val, chk_val == val, 1 /* sleep us */,
+				       33000 /* timeout us */) < 0) {
+			dev_info(dev->dev,
+				 "%s: wait vf off timeout: TG_VF_CON 0x%x\n",
+				 __func__, chk_val);
+		}
+
+		mtk_cam_raw_reset_reg(dev);
+	}
+}
+
+static int mtk_raw_linebuf_chk(bool b_twin, bool b_bin, bool b_frz, bool b_qbn,
+			       bool b_cbn, int tg_x, int *frz_ratio)
+{
+	int input_x = tg_x;
+	/* max line buffer check for frontal binning and resizer */
+	if (b_twin) {
+		if (input_x > CAM_TWIN_PROCESS_MAX_LINE_BUFFER)
+			return LB_CHECK_TWIN;
+		input_x = input_x >> 1;
+	}
+	if (b_cbn) {
+		if (input_x > CAM_RAW_CBN_MAX_LINE_BUFFER)
+			return LB_CHECK_CBN;
+		input_x = input_x >> 1;
+	}
+	if (b_qbn) {
+		if (input_x > CAM_RAW_QBND_MAX_LINE_BUFFER)
+			return LB_CHECK_QBN;
+		input_x = input_x >> 1;
+	}
+	if (b_bin) {
+		if (input_x > CAM_RAW_BIN_MAX_LINE_BUFFER)
+			return LB_CHECK_BIN;
+		input_x = input_x >> 1;
+	}
+	if (input_x <= CAM_RAW_PROCESS_MAX_LINE_BUFFER) {
+		return LB_CHECK_OK;
+	} else if (b_frz) {
+		if (input_x > CAM_RAW_FRZ_MAX_LINE_BUFFER)
+			return LB_CHECK_FRZ;
+
+		*frz_ratio = input_x * 100 / CAM_RAW_PROCESS_MAX_LINE_BUFFER;
+		return LB_CHECK_OK;
+	} else {
+		return LB_CHECK_RAW;
+	}
+}
+
+static int mtk_raw_pixelmode_calc(int rawpxl, int b_twin, bool b_bin,
+				  bool b_frz, int min_ratio)
+{
+	int pixelmode = rawpxl;
+
+	pixelmode = (b_twin == 2) ? pixelmode << 2 : pixelmode;
+	pixelmode = (b_twin == 1) ? pixelmode << 1 : pixelmode;
+	pixelmode = b_bin ? pixelmode << 1 : pixelmode;
+	pixelmode = (b_frz && (min_ratio < FRZ_PXLMODE_THRES)) ?
+		pixelmode << 1 : pixelmode;
+	pixelmode = (pixelmode > TGO_MAX_PXLMODE) ?
+		TGO_MAX_PXLMODE : pixelmode;
+
+	return pixelmode;
+}
+
+static void mtk_raw_update_debug_param(struct mtk_cam_device *cam,
+				       struct mtk_cam_resource_config *res,
+				       int *clk_idx)
+{
+	/* skip if debug is not enabled */
+	if (!debug_raw)
+		return;
+
+	dev_dbg(cam->dev,
+		"%s:before:BIN/FRZ/HWN/CLK/pxl=%d/%d(%d)/%d/%d/%d, clk:%d\n",
+		__func__, res->bin_enable, res->frz_enable, res->frz_ratio,
+		res->raw_num_used, *clk_idx, res->tgo_pxl_mode,
+		res->clk_target);
+
+	if (debug_raw_num > 0) {
+		dev_info(cam->dev, "DEBUG: force raw_num_used: %d\n",
+			 debug_raw_num);
+		res->raw_num_used = debug_raw_num;
+	}
+
+	if (debug_pixel_mode >= 0) {
+		dev_info(cam->dev, "DEBUG: force debug_pixel_mode (log2): %d\n",
+			 debug_pixel_mode);
+		res->tgo_pxl_mode = debug_pixel_mode;
+	}
+
+	if (debug_clk_idx >= 0) {
+		dev_info(cam->dev, "DEBUG: force debug_clk_idx: %d\n",
+			 debug_clk_idx);
+		*clk_idx = debug_clk_idx;
+	}
+
+	dev_dbg(cam->dev,
+		"%s:after:BIN/FRZ/HWN/CLK/pxl=%d/%d(%d)/%d/%d/%d, clk:%d\n",
+		__func__, res->bin_enable, res->frz_enable, res->frz_ratio,
+		res->raw_num_used, *clk_idx, res->tgo_pxl_mode,
+		res->clk_target);
+}
+
+static bool is_cbn_en(int bin_flag)
+{
+	switch (bin_flag) {
+	case CBN_2X2_ON:
+	case CBN_3X3_ON:
+	case CBN_4X4_ON:
+		return true;
+	default:
+		return false;
+	}
+}
+
+static bool mtk_cam_raw_resource_calc(struct mtk_cam_device *cam,
+				      struct mtk_cam_resource_config *res,
+				      s64 pixel_rate, int res_plan,
+				      int in_w, int in_h, int *out_w, int *out_h)
+{
+	int res_step_type = 0;
+	int tgo_pxl_mode = 1;
+	int pixel_mode[MTK_CAMSYS_RES_STEP_NUM] = {0};
+	int bin_temp = 0, frz_temp = 0, hwn_temp = 0;
+	int bin_en = 0, frz_en = 0, twin_en = 0, clk_cur = 0;
+	int idx = 0, clk_res = 0, idx_res = 0;
+	bool res_found = false;
+	int lb_chk_res = -1;
+	int frz_ratio = 100;
+	int p;
+
+	res->res_plan = res_plan;
+	res->pixel_rate = pixel_rate;
+	/* test pattern */
+	if (res->pixel_rate == 0)
+		res->pixel_rate = 450 * MHz;
+	dev_dbg(cam->dev,
+		"[Res] PR = %lld, w/h=%d/%d HWN(%d)/BIN(%d)/FRZ(%d),Plan:%d\n",
+		res->pixel_rate, in_w, in_h,
+		res->hwn_limit_max, res->bin_limit, res->frz_limit, res->res_plan);
+
+	memcpy(res->res_strategy, raw_resource_strategy_plan + res->res_plan,
+	       MTK_CAMSYS_RES_STEP_NUM * sizeof(int));
+	res->bin_enable = 0;
+	res->raw_num_used = 1;
+	res->frz_enable = 0;
+	res->frz_ratio = frz_ratio;
+	for (idx = 0; idx < MTK_CAMSYS_RES_STEP_NUM ; idx++) {
+		res_step_type = res->res_strategy[idx] & MTK_CAMSYS_RES_IDXMASK;
+		switch (res_step_type) {
+		case MTK_CAMSYS_RES_BIN_TAG:
+			bin_temp = res->res_strategy[idx] - E_RES_BIN_S;
+			if (bin_temp <= res->bin_limit)
+				bin_en = bin_temp;
+			if (bin_en && frz_en)
+				frz_en = 0;
+			break;
+		case MTK_CAMSYS_RES_FRZ_TAG:
+			frz_temp = res->res_strategy[idx] - E_RES_FRZ_S;
+			if (res->frz_limit < 100)
+				frz_en = frz_temp;
+			break;
+		case MTK_CAMSYS_RES_HWN_TAG:
+			hwn_temp = res->res_strategy[idx] - E_RES_HWN_S;
+			if (hwn_temp + 1 <= res->hwn_limit_max)
+				twin_en = hwn_temp;
+			break;
+		case MTK_CAMSYS_RES_CLK_TAG:
+			clk_cur = res->res_strategy[idx] - E_RES_CLK_S;
+			break;
+		default:
+			break;
+		}
+
+		/* 1 for force bin on */
+		if (res->bin_limit >= 1)
+			bin_en = 1;
+
+		if (res->hwn_limit_min > 1)
+			twin_en = 1;
+
+		/* max line buffer check*/
+		lb_chk_res = mtk_raw_linebuf_chk(twin_en, res->bin_limit & BIN_ON,
+						 frz_en, res->bin_limit & QBND_ON,
+						 is_cbn_en(res->bin_limit),
+						 in_w, &frz_ratio);
+		/* frz ratio*/
+		if (res_step_type == MTK_CAMSYS_RES_FRZ_TAG) {
+			res->frz_ratio = res->frz_limit < FRZ_PXLMODE_THRES ?
+				res->frz_limit : FRZ_PXLMODE_THRES;
+		}
+		/*try 1-pixel mode first*/
+		for (p = 1; p <= MTK_CAMSYS_PROC_DEFAULT_PIXELMODE; p++) {
+			tgo_pxl_mode = mtk_raw_pixelmode_calc(p, twin_en,
+							      bin_en, frz_en,
+							      res->frz_ratio);
+			/**
+			 * isp throughput along resource strategy
+			 * (compared with pixel rate)
+			 */
+			pixel_mode[idx] = tgo_pxl_mode;
+
+			/* only support 1-pixel mode */
+			if (p == 1 && lb_chk_res == LB_CHECK_OK) {
+				if (!res_found) {
+					res->bin_enable = bin_en;
+					res->frz_enable = frz_en;
+					res->raw_num_used = twin_en + 1;
+					clk_res = clk_cur;
+					idx_res = idx;
+					res_found = true;
+					break;
+				}
+			}
+		}
+		dev_dbg(cam->dev,
+			"Res-%d B/F/H/C=%d/%d/%d/%d -> %d/%d/%d/%d (%d)(%d)\n",
+			idx, bin_temp, frz_temp, hwn_temp, clk_cur, bin_en,
+			frz_en, twin_en, clk_cur, lb_chk_res, pixel_mode[idx]);
+	}
+
+	tgo_pxl_mode = pixel_mode[idx_res];
+	switch (tgo_pxl_mode) {
+	case 1:
+		res->tgo_pxl_mode = 0;
+		break;
+	case 2:
+		res->tgo_pxl_mode = 1;
+		break;
+	case 4:
+		res->tgo_pxl_mode = 2;
+		break;
+	case 8:
+		res->tgo_pxl_mode = 3;
+		break;
+	default:
+		break;
+	}
+
+	mtk_raw_update_debug_param(cam, res, &clk_res);
+
+	if (res_found) {
+		dev_info(cam->dev,
+			 "Res-end:%d BIN/FRZ/HWN/CLK/pxl=%d/%d(%d)/%d/%d/%d, clk:%d\n",
+			 idx_res, res->bin_enable, res->frz_enable, res->frz_ratio,
+			 res->raw_num_used, clk_res, res->tgo_pxl_mode,
+			 res->clk_target);
+	} else {
+		dev_dbg(cam->dev, "[%s] Error resource result\n", __func__);
+	}
+	if (res->bin_enable) {
+		*out_w = in_w >> 1;
+		*out_h = in_h >> 1;
+	} else if (res->frz_enable) {
+		*out_w = in_w * res->frz_ratio / 100;
+		*out_h = in_h * res->frz_ratio / 100;
+	} else {
+		*out_w = in_w;
+		*out_h = in_h;
+	}
+
+	return res_found;
+}
+
+static void raw_irq_handle_tg_grab_err(struct mtk_raw_device *raw_dev,
+				       int dequeued_frame_seq_no);
+static void raw_irq_handle_dma_err(struct mtk_raw_device *raw_dev);
+static void raw_irq_handle_tg_overrun_err(struct mtk_raw_device *raw_dev,
+					  int dequeued_frame_seq_no);
+
+static void raw_handle_error(struct mtk_raw_device *raw_dev,
+			     struct mtk_camsys_irq_info *data)
+{
+	int err_status = data->e.err_status;
+	int frame_idx_inner = data->frame_idx_inner;
+
+	/* Show DMA errors in detail */
+	if (err_status & DMA_ERR_ST)
+		raw_irq_handle_dma_err(raw_dev);
+
+	/* Show TG register for more error detail*/
+	if (err_status & TG_GBERR_ST)
+		raw_irq_handle_tg_grab_err(raw_dev, frame_idx_inner);
+
+	if (err_status & TG_OVRUN_ST) {
+		if (raw_dev->overrun_debug_dump_cnt < 4) {
+			mtk_cam_dump_topdebug_rdyreq(raw_dev->dev,
+						     raw_dev->base,
+						     raw_dev->yuv_base);
+			raw_dev->overrun_debug_dump_cnt++;
+		} else {
+			dev_dbg(raw_dev->dev,
+				"%s: TG_OVRUN_ST repeated skip dump raw_id:%d\n",
+				__func__, raw_dev->id);
+		}
+		raw_irq_handle_tg_overrun_err(raw_dev, frame_idx_inner);
+	}
+}
+
+static inline unsigned int mtk_raw_get_err_status(unsigned int irq_status)
+{
+	return irq_status & INT_ST_MASK_CAM_ERR;
+}
+
+static irqreturn_t mtk_irq_raw(int irq, void *data)
+{
+	struct mtk_raw_device *raw_dev = (struct mtk_raw_device *)data;
+	struct device *dev = raw_dev->dev;
+	struct mtk_camsys_irq_info irq_info;
+	unsigned int frame_idx, frame_idx_inner, fbc_fho_ctl2;
+	unsigned int irq_status, err_status, dmao_done_status, dmai_done_status;
+	unsigned int drop_status, dma_ofl_status, cq_done_status, cq2_done_status;
+	bool wake_thread = 0;
+
+	irq_status       = readl_relaxed(raw_dev->base + REG_CTL_RAW_INT_STAT);
+	dmao_done_status = readl_relaxed(raw_dev->base + REG_CTL_RAW_INT2_STAT);
+	dmai_done_status = readl_relaxed(raw_dev->base + REG_CTL_RAW_INT3_STAT);
+	drop_status      = readl_relaxed(raw_dev->base + REG_CTL_RAW_INT4_STAT);
+	dma_ofl_status   = readl_relaxed(raw_dev->base + REG_CTL_RAW_INT5_STAT);
+	cq_done_status   = readl_relaxed(raw_dev->base + REG_CTL_RAW_INT6_STAT);
+	cq2_done_status  = readl_relaxed(raw_dev->base + REG_CTL_RAW_INT7_STAT);
+
+	frame_idx       = readl_relaxed(raw_dev->base + REG_FRAME_SEQ_NUM);
+	frame_idx_inner = readl_relaxed(raw_dev->base_inner + REG_FRAME_SEQ_NUM);
+
+	fbc_fho_ctl2 =
+		readl_relaxed(REG_FBC_CTL2(raw_dev->base + FBC_R1A_BASE, 1));
+
+	err_status = mtk_raw_get_err_status(irq_status);
+	if (unlikely(debug_raw))
+		dev_dbg(dev,
+			"INT:0x%x(err:0x%x) 2~7 0x%x/0x%x/0x%x/0x%x/0x%x/0x%x (in:%d)\n",
+			irq_status, err_status,
+			dmao_done_status, dmai_done_status, drop_status,
+			dma_ofl_status, cq_done_status, cq2_done_status,
+			frame_idx_inner);
+
+	if (unlikely(!raw_dev->pipeline || !raw_dev->pipeline->enabled_raw)) {
+		dev_dbg(dev, "%s: %i: raw pipeline is disabled\n",
+			__func__, raw_dev->id);
+		goto ctx_not_found;
+	}
+
+	irq_info.irq_type = 0;
+	irq_info.ts_ns = ktime_get_boottime_ns();
+	irq_info.frame_idx = frame_idx;
+	irq_info.frame_idx_inner = frame_idx_inner;
+
+	/* CQ done */
+	if (cq_done_status & CAMCTL_CQ_THR0_DONE_ST)
+		irq_info.irq_type |= 1 << CAMSYS_IRQ_SETTING_DONE;
+	/* DMAO done, only for AFO */
+	if (dmao_done_status & AFO_DONE_ST)
+		irq_info.irq_type |= 1 << CAMSYS_IRQ_AFO_DONE;
+		/* enable AFO_DONE_EN at backend manually */
+
+	/* Frame done */
+	if (irq_status & SW_PASS1_DON_ST) {
+		irq_info.irq_type |= 1 << CAMSYS_IRQ_FRAME_DONE;
+		raw_dev->overrun_debug_dump_cnt = 0;
+	}
+	/* Frame start */
+	if (irq_status & SOF_INT_ST) {
+		irq_info.irq_type |= 1 << CAMSYS_IRQ_FRAME_START;
+
+		raw_dev->sof_count++;
+		irq_info.write_cnt = ((fbc_fho_ctl2 & WCNT_BIT_MASK) >> 8) - 1;
+		irq_info.fbc_cnt = (fbc_fho_ctl2 & CNT_BIT_MASK) >> 16;
+	}
+	/* Vsync interrupt */
+	if (irq_status & VS_INT_ST)
+		raw_dev->vsync_count++;
+
+	if (irq_info.irq_type && !raw_dev->is_sub) {
+		if (push_msgfifo(raw_dev, &irq_info) == 0)
+			wake_thread = 1;
+	}
+
+	/* Check ISP error status */
+	if (unlikely(err_status)) {
+		struct mtk_camsys_irq_info err_info;
+
+		err_info.irq_type = 1 << CAMSYS_IRQ_ERROR;
+		err_info.ts_ns = irq_info.ts_ns;
+		err_info.frame_idx = irq_info.frame_idx;
+		err_info.frame_idx_inner = irq_info.frame_idx_inner;
+		err_info.e.err_status = err_status;
+
+		if (push_msgfifo(raw_dev, &err_info) == 0)
+			wake_thread = 1;
+	}
+
+	/* enable to debug fbc related */
+	if (debug_raw && debug_dump_fbc && (irq_status & SOF_INT_ST))
+		mtk_cam_raw_dump_fbc(raw_dev->dev, raw_dev->base, raw_dev->yuv_base);
+
+ctx_not_found:
+	return wake_thread ? IRQ_WAKE_THREAD : IRQ_HANDLED;
+}
+
+static irqreturn_t mtk_thread_irq_raw(int irq, void *data)
+{
+	struct mtk_raw_device *raw_dev = (struct mtk_raw_device *)data;
+	struct mtk_camsys_irq_info irq_info;
+
+	if (unlikely(atomic_cmpxchg(&raw_dev->is_fifo_overflow, 1, 0)))
+		dev_info(raw_dev->dev, "msg fifo overflow\n");
+
+	while (kfifo_len(&raw_dev->msg_fifo) >= sizeof(irq_info)) {
+		int len = kfifo_out(&raw_dev->msg_fifo, &irq_info, sizeof(irq_info));
+
+		WARN_ON(len != sizeof(irq_info));
+
+		dev_dbg(raw_dev->dev, "ts=%llu irq_type %d, req:%d/%d\n",
+			irq_info.ts_ns, irq_info.irq_type,
+			irq_info.frame_idx_inner, irq_info.frame_idx);
+
+		/* error case */
+		if (unlikely(irq_info.irq_type == (1 << CAMSYS_IRQ_ERROR))) {
+			raw_handle_error(raw_dev, &irq_info);
+			continue;
+		}
+
+		/* normal case */
+		/* inform interrupt information to camsys controller */
+		mtk_camsys_isr_event(raw_dev->cam, CAMSYS_ENGINE_RAW,
+				     raw_dev->id, &irq_info);
+	}
+
+	return IRQ_HANDLED;
+}
+
+void raw_irq_handle_tg_grab_err(struct mtk_raw_device *raw_dev,
+				int dequeued_frame_seq_no)
+{
+	struct mtk_cam_ctx *ctx;
+	struct mtk_cam_request_stream_data *s_data;
+	int val, val2;
+	unsigned int inner_val, tg_full_sel;
+
+	val = readl_relaxed(raw_dev->base + REG_TG_PATH_CFG);
+	inner_val = readl_relaxed(raw_dev->base_inner + REG_TG_PATH_CFG);
+	val = val | TG_TG_FULL_SEL;
+	writel_relaxed(val, raw_dev->base + REG_TG_PATH_CFG);
+	writel_relaxed(val, raw_dev->base_inner + REG_TG_PATH_CFG);
+	wmb(); /* make sure committed */
+	val2 = readl_relaxed(raw_dev->base + REG_TG_SEN_MODE);
+	val2 = val2 | TG_CMOS_RDY_SEL;
+	writel_relaxed(val2, raw_dev->base + REG_TG_SEN_MODE);
+	wmb(); /* make sure committed */
+
+	dev_info_ratelimited(raw_dev->dev,
+			     "%d Grab_Err [Outter] TG PATHCFG/SENMODE FRMSIZE/R GRABPXL/LIN:%x/%x %x/%x %x/%x\n",
+			     dequeued_frame_seq_no,
+			     readl_relaxed(raw_dev->base + REG_TG_PATH_CFG),
+			     readl_relaxed(raw_dev->base + REG_TG_SEN_MODE),
+			     readl_relaxed(raw_dev->base + REG_TG_FRMSIZE_ST),
+			     readl_relaxed(raw_dev->base + REG_TG_FRMSIZE_ST_R),
+			     readl_relaxed(raw_dev->base + REG_TG_SEN_GRAB_PXL),
+			     readl_relaxed(raw_dev->base + REG_TG_SEN_GRAB_LIN));
+	dev_info_ratelimited(raw_dev->dev,
+			     "%d Grab_Err [Inner] TG PATHCFG/SENMODE FRMSIZE/R GRABPXL/LIN:%x/%x %x/%x %x/%x\n",
+			     dequeued_frame_seq_no,
+			     readl_relaxed(raw_dev->base_inner + REG_TG_PATH_CFG),
+			     readl_relaxed(raw_dev->base_inner + REG_TG_SEN_MODE),
+			     readl_relaxed(raw_dev->base_inner + REG_TG_FRMSIZE_ST),
+			     readl_relaxed(raw_dev->base_inner + REG_TG_FRMSIZE_ST_R),
+			     readl_relaxed(raw_dev->base_inner + REG_TG_SEN_GRAB_PXL),
+			     readl_relaxed(raw_dev->base_inner + REG_TG_SEN_GRAB_LIN));
+
+	ctx = mtk_cam_find_ctx(raw_dev->cam, &raw_dev->pipeline->subdev.entity);
+	if (!ctx) {
+		dev_info(raw_dev->dev, "%s: cannot find ctx\n", __func__);
+		return;
+	}
+
+	s_data = mtk_cam_get_req_s_data(ctx, ctx->stream_id, dequeued_frame_seq_no);
+	if (s_data) {
+		mtk_cam_debug_seninf_dump(s_data);
+		tg_full_sel = ((inner_val & TG_FULLSEL_BIT_MASK) >> 15);
+
+		if (tg_full_sel == 1)
+			mtk_cam_req_dump(s_data, MTK_CAM_REQ_DUMP_CHK_DEQUEUE_FAILED,
+					 "TG Grab Err", false);
+		else
+			dev_info(raw_dev->dev, "%s: tg_full_sel 0x%x\n",
+				 __func__, tg_full_sel);
+	} else {
+		dev_info(raw_dev->dev,
+			 "%s: req(%d) can't be found for seninf dump\n",
+			 __func__, dequeued_frame_seq_no);
+	}
+}
+
+void raw_irq_handle_dma_err(struct mtk_raw_device *raw_dev)
+{
+	mtk_cam_raw_dump_dma_err_st(raw_dev->dev, raw_dev->base);
+	mtk_cam_yuv_dump_dma_err_st(raw_dev->dev, raw_dev->yuv_base);
+
+	if (raw_dev->pipeline->feature_active)
+		mtk_cam_dump_dma_debug(raw_dev->dev, raw_dev->base + CAMDMATOP_BASE,
+				       "RAWI_R2", dbg_RAWI_R2, ARRAY_SIZE(dbg_RAWI_R2));
+
+	if (raw_dev->pipeline->pde_config.pde_info.pd_table_offset) {
+		dev_dbg_ratelimited(raw_dev->dev,
+				    "DMA_ERR:%x,PDI_R1:%x,PDO_R1:%x\n",
+				     readl_relaxed(raw_dev->base + 0x4060),
+				     readl_relaxed(raw_dev->base +
+						   REG_PDI_R1_BASE +
+						   DMA_OFFSET_ERR_STAT),
+				     readl_relaxed(raw_dev->base +
+						   REG_PDO_R1_BASE +
+						   DMA_OFFSET_ERR_STAT));
+		dev_dbg_ratelimited(raw_dev->dev,
+				    "TG_FRMSIZE_ST:%x,TG_FRMSIZE_ST_R:%x\n",
+				    readl_relaxed(raw_dev->base + 0x0738),
+				    readl_relaxed(raw_dev->base + 0x076c));
+		mtk_cam_dump_dma_debug(raw_dev->dev, raw_dev->base + CAMDMATOP_BASE,
+				       "PDO_R1", dbg_PDO_R1, ARRAY_SIZE(dbg_PDO_R1));
+		mtk_cam_dump_dma_debug(raw_dev->dev, raw_dev->base + CAMDMATOP_BASE,
+				       "PDI_R1", dbg_PDI_R1, ARRAY_SIZE(dbg_PDI_R1));
+	}
+}
+
+static void raw_irq_handle_tg_overrun_err(struct mtk_raw_device *raw_dev,
+					  int dequeued_frame_seq_no)
+{
+	struct mtk_cam_ctx *ctx;
+	struct mtk_cam_request_stream_data *s_data;
+	int val, val2;
+	unsigned int inner_val, tg_full_sel;
+
+	val = readl_relaxed(raw_dev->base + REG_TG_PATH_CFG);
+	inner_val = readl_relaxed(raw_dev->base_inner + REG_TG_PATH_CFG);
+	val = val | TG_TG_FULL_SEL;
+	writel_relaxed(val, raw_dev->base + REG_TG_PATH_CFG);
+	writel_relaxed(val, raw_dev->base_inner + REG_TG_PATH_CFG);
+	wmb(); /* for dbg dump register */
+
+	val2 = readl_relaxed(raw_dev->base + REG_TG_SEN_MODE);
+	val2 = val2 | TG_CMOS_RDY_SEL;
+	writel_relaxed(val2, raw_dev->base + REG_TG_SEN_MODE);
+	wmb(); /* for dbg dump register */
+
+	dev_info(raw_dev->dev,
+		 "%d Overrun_Err [Outter] TG PATHCFG/SENMODE FRMSIZE/R GRABPXL/LIN:%x/%x %x/%x %x/%x\n",
+		 dequeued_frame_seq_no,
+		 readl_relaxed(raw_dev->base + REG_TG_PATH_CFG),
+		 readl_relaxed(raw_dev->base + REG_TG_SEN_MODE),
+		 readl_relaxed(raw_dev->base + REG_TG_FRMSIZE_ST),
+		 readl_relaxed(raw_dev->base + REG_TG_FRMSIZE_ST_R),
+		 readl_relaxed(raw_dev->base + REG_TG_SEN_GRAB_PXL),
+		 readl_relaxed(raw_dev->base + REG_TG_SEN_GRAB_LIN));
+	dev_info(raw_dev->dev,
+		 "%d Overrun_Err [Inner] TG PATHCFG/SENMODE FRMSIZE/R GRABPXL/LIN:%x/%x %x/%x %x/%x\n",
+		 dequeued_frame_seq_no,
+		 readl_relaxed(raw_dev->base_inner + REG_TG_PATH_CFG),
+		 readl_relaxed(raw_dev->base_inner + REG_TG_SEN_MODE),
+		 readl_relaxed(raw_dev->base_inner + REG_TG_FRMSIZE_ST),
+		 readl_relaxed(raw_dev->base_inner + REG_TG_FRMSIZE_ST_R),
+		 readl_relaxed(raw_dev->base_inner + REG_TG_SEN_GRAB_PXL),
+		 readl_relaxed(raw_dev->base_inner + REG_TG_SEN_GRAB_LIN));
+	dev_info(raw_dev->dev,
+		 "REQ RAW/2/3 DMA/2:%08x/%08x/%08x/%08x/%08x\n",
+		 readl_relaxed(raw_dev->base + REG_CTL_RAW_MOD_REQ_STAT),
+		 readl_relaxed(raw_dev->base + REG_CTL_RAW_MOD2_REQ_STAT),
+		 readl_relaxed(raw_dev->base + REG_CTL_RAW_MOD3_REQ_STAT),
+		 readl_relaxed(raw_dev->base + REG_CTL_RAW_MOD5_REQ_STAT),
+		 readl_relaxed(raw_dev->base + REG_CTL_RAW_MOD6_REQ_STAT));
+	dev_info(raw_dev->dev,
+		 "RDY RAW/2/3 DMA/2:%08x/%08x/%08x/%08x/%08x\n",
+		 readl_relaxed(raw_dev->base + REG_CTL_RAW_MOD_RDY_STAT),
+		 readl_relaxed(raw_dev->base + REG_CTL_RAW_MOD2_RDY_STAT),
+		 readl_relaxed(raw_dev->base + REG_CTL_RAW_MOD3_RDY_STAT),
+		 readl_relaxed(raw_dev->base + REG_CTL_RAW_MOD5_RDY_STAT),
+		 readl_relaxed(raw_dev->base + REG_CTL_RAW_MOD6_RDY_STAT));
+	dev_info(raw_dev->dev,
+		 "REQ YUV/2/3/4 WDMA:%08x/%08x/%08x/%08x/%08x\n",
+		 readl_relaxed(raw_dev->yuv_base + REG_CTL_RAW_MOD_REQ_STAT),
+		 readl_relaxed(raw_dev->yuv_base + REG_CTL_RAW_MOD2_REQ_STAT),
+		 readl_relaxed(raw_dev->yuv_base + REG_CTL_RAW_MOD3_REQ_STAT),
+		 readl_relaxed(raw_dev->yuv_base + REG_CTL_RAW_MOD4_REQ_STAT),
+		 readl_relaxed(raw_dev->yuv_base + REG_CTL_RAW_MOD5_REQ_STAT));
+	dev_info(raw_dev->dev,
+		 "RDY YUV/2/3/4 WDMA:%08x/%08x/%08x/%08x/%08x\n",
+		 readl_relaxed(raw_dev->yuv_base + REG_CTL_RAW_MOD_RDY_STAT),
+		 readl_relaxed(raw_dev->yuv_base + REG_CTL_RAW_MOD2_RDY_STAT),
+		 readl_relaxed(raw_dev->yuv_base + REG_CTL_RAW_MOD3_RDY_STAT),
+		 readl_relaxed(raw_dev->yuv_base + REG_CTL_RAW_MOD4_RDY_STAT),
+		 readl_relaxed(raw_dev->yuv_base + REG_CTL_RAW_MOD5_RDY_STAT));
+
+	ctx = mtk_cam_find_ctx(raw_dev->cam, &raw_dev->pipeline->subdev.entity);
+	if (!ctx) {
+		dev_info(raw_dev->dev, "%s: cannot find ctx\n", __func__);
+		return;
+	}
+
+	s_data = mtk_cam_get_req_s_data(ctx, ctx->stream_id, dequeued_frame_seq_no);
+	if (s_data) {
+		/**
+		 * Enable the dump manually if needed.
+		 * mtk_cam_req_dump() already call mtk_cam_seninf_dump()
+		 * in a delayed work if no P1 done comes.
+		 */
+		if (0 && raw_dev->sof_count > 3)
+			mtk_cam_debug_seninf_dump(s_data);
+
+		tg_full_sel = ((inner_val & TG_FULLSEL_BIT_MASK) >> 15);
+		if (tg_full_sel == 1)
+			mtk_cam_req_dump(s_data, MTK_CAM_REQ_DUMP_CHK_DEQUEUE_FAILED,
+					 "TG Overrun Err", true);
+		else
+			dev_info(raw_dev->dev, "%s: tg_full_sel 0x%x:\n",
+				 __func__, tg_full_sel);
+	} else {
+		dev_info(raw_dev->dev, "%s: req(%d) can't be found for dump\n",
+			 __func__, dequeued_frame_seq_no);
+
+		if (0 && raw_dev->sof_count > 3 && ctx->seninf)
+			mtk_cam_seninf_dump(ctx->seninf);
+	}
+}
+
+static int mtk_raw_pm_suspend_prepare(struct mtk_raw_device *dev)
+{
+	u32 val;
+	int ret;
+
+	dev_dbg(dev->dev, "- %s\n", __func__);
+
+	if (pm_runtime_suspended(dev->dev))
+		return 0;
+
+	/* Disable ISP's view finder and wait for TG idle */
+	dev_dbg(dev->dev, "cam suspend, disable VF\n");
+	val = readl(dev->base + REG_TG_VF_CON);
+	writel(val & (~TG_VFDATA_EN), dev->base + REG_TG_VF_CON);
+	ret = readl_poll_timeout_atomic(dev->base + REG_TG_INTER_ST, val,
+					(val & TG_CAM_CS_MASK) == TG_IDLE_ST,
+					USEC_PER_MSEC, MTK_RAW_STOP_HW_TIMEOUT);
+	if (ret)
+		dev_dbg(dev->dev, "can't stop HW:%d:0x%x\n", ret, val);
+
+	/* Disable CMOS */
+	val = readl(dev->base + REG_TG_SEN_MODE);
+	writel(val & (~TG_SEN_MODE_CMOS_EN), dev->base + REG_TG_SEN_MODE);
+
+	/* Force ISP HW to idle */
+	ret = pm_runtime_force_suspend(dev->dev);
+	return ret;
+}
+
+static int mtk_raw_pm_post_suspend(struct mtk_raw_device *dev)
+{
+	u32 val;
+	int ret;
+
+	dev_dbg(dev->dev, "- %s\n", __func__);
+
+	if (pm_runtime_suspended(dev->dev))
+		return 0;
+
+	/* Force ISP HW to resume */
+	ret = pm_runtime_force_resume(dev->dev);
+	if (ret)
+		return ret;
+
+	/* Enable CMOS */
+	dev_dbg(dev->dev, "cam resume, enable CMOS/VF\n");
+	val = readl(dev->base + REG_TG_SEN_MODE);
+	writel(val | TG_SEN_MODE_CMOS_EN, dev->base + REG_TG_SEN_MODE);
+
+	/* Enable VF */
+	val = readl(dev->base + REG_TG_VF_CON);
+	writel(val | TG_VFDATA_EN, dev->base + REG_TG_VF_CON);
+
+	return 0;
+}
+
+static int raw_pm_notifier(struct notifier_block *nb,
+			   unsigned long action, void *data)
+{
+	struct mtk_raw_device *raw_dev =
+			container_of(nb, struct mtk_raw_device, pm_notifier);
+
+	switch (action) {
+	case PM_SUSPEND_PREPARE:
+		mtk_raw_pm_suspend_prepare(raw_dev);
+		break;
+	case PM_POST_SUSPEND:
+		mtk_raw_pm_post_suspend(raw_dev);
+		break;
+	default:
+		break;
+	}
+
+	return NOTIFY_OK;
+}
+
+static int mtk_raw_of_probe(struct platform_device *pdev,
+			    struct mtk_raw_device *raw)
+{
+	struct device *dev = &pdev->dev;
+	struct resource *res;
+	int ret;
+	int n_clks;
+
+	ret = of_property_read_u32(dev->of_node, "mediatek,cam-id", &raw->id);
+	if (ret) {
+		dev_dbg(dev, "missing camid property\n");
+		return ret;
+	}
+
+	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);
+
+	/* base outer register */
+	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "base");
+	if (!res) {
+		dev_err(dev, "failed to get mem\n");
+		return -ENODEV;
+	}
+
+	raw->base = devm_ioremap_resource(dev, res);
+	if (IS_ERR(raw->base)) {
+		dev_err(dev, "failed to map register base\n");
+		return PTR_ERR(raw->base);
+	}
+
+	/* base inner register */
+	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "inner_base");
+	if (!res) {
+		dev_err(dev, "failed to get mem\n");
+		return -ENODEV;
+	}
+
+	raw->base_inner = devm_ioremap_resource(dev, res);
+	if (IS_ERR(raw->base_inner)) {
+		dev_err(dev, "failed to map register inner base\n");
+		return PTR_ERR(raw->base_inner);
+	}
+
+	/* will be assigned later */
+	raw->yuv_base = NULL;
+
+	raw->irq = platform_get_irq(pdev, 0);
+	if (raw->irq < 0) {
+		dev_err(dev, "failed to get irq\n");
+		return -ENODEV;
+	}
+
+	ret = devm_request_threaded_irq(dev, raw->irq,
+					mtk_irq_raw, mtk_thread_irq_raw,
+					0, dev_name(dev), raw);
+	if (ret) {
+		dev_err(dev, "failed to request irq=%d\n", raw->irq);
+		return ret;
+	}
+	dev_dbg(dev, "registered irq=%d\n", raw->irq);
+
+	disable_irq(raw->irq);
+
+	n_clks = devm_clk_bulk_get_all(dev, &raw->clk_b);
+	if (n_clks < 0) {
+		dev_err(dev, "failed to devm_clk_bulk_get_all=%d\n", n_clks);
+		return n_clks;
+	}
+
+	raw->num_clks = n_clks;
+	dev_info(dev, "clk_num:%d\n", raw->num_clks);
+
+#ifdef CONFIG_PM_SLEEP
+	raw->pm_notifier.notifier_call = raw_pm_notifier;
+	ret = register_pm_notifier(&raw->pm_notifier);
+	if (ret) {
+		dev_info(dev, "failed to register notifier block.\n");
+		return ret;
+	}
+#endif
+	return 0;
+}
+
+static int mtk_raw_sd_subscribe_event(struct v4l2_subdev *subdev,
+				      struct v4l2_fh *fh,
+				      struct v4l2_event_subscription *sub)
+{
+	switch (sub->type) {
+	case V4L2_EVENT_FRAME_SYNC:
+		return v4l2_event_subscribe(fh, sub, 0, NULL);
+	case V4L2_EVENT_REQUEST_DRAINED:
+		return v4l2_event_subscribe(fh, sub, 0, NULL);
+	case V4L2_EVENT_EOS:
+		return v4l2_event_subscribe(fh, sub, 0, NULL);
+	case V4L2_EVENT_REQUEST_DUMPED:
+		return v4l2_event_subscribe(fh, sub, 0, NULL);
+	default:
+		return -EINVAL;
+	}
+}
+
+static int mtk_raw_available_resource(struct mtk_raw *raw)
+{
+	struct device *dev = raw->cam_dev;
+	struct mtk_cam_device *cam_dev = dev_get_drvdata(dev);
+	int res_status = 0;
+	int i, j;
+
+	for (i = 0; i < cam_dev->num_raw_devices; i++) {
+		struct mtk_raw_pipeline *pipe = raw->pipelines + i;
+
+		for (j = 0; j < ARRAY_SIZE(raw->devs); j++) {
+			if (pipe->enabled_raw & 1 << j)
+				res_status |= 1 << j;
+		}
+	}
+	dev_dbg(raw->cam_dev,
+		"%s raw_status:0x%x Available Engine:A/B/C:%d/%d/%d\n",
+		__func__, res_status,
+		!(res_status & (1 << MTKCAM_SUBDEV_RAW_0)),
+		!(res_status & (1 << MTKCAM_SUBDEV_RAW_1)),
+		!(res_status & (1 << MTKCAM_SUBDEV_RAW_2)));
+
+	return res_status;
+}
+
+int mtk_cam_s_data_raw_select(struct mtk_cam_request_stream_data *s_data,
+			      struct mtkcam_ipi_input_param *cfg_in_param)
+{
+	struct mtk_cam_ctx *ctx;
+	struct mtk_raw_pipeline *pipe;
+	int raw_status = 0;
+	bool selected = false;
+
+	ctx = mtk_cam_s_data_get_ctx(s_data);
+	pipe = ctx->pipe;
+	raw_status = mtk_raw_available_resource(pipe->raw);
+	raw_status &= ~pipe->enabled_raw;
+
+	mtk_raw_available_resource(pipe->raw);
+
+	if (!selected)
+		return -EINVAL;
+
+	return 0;
+}
+
+int mtk_cam_raw_select(struct mtk_cam_ctx *ctx,
+		       struct mtkcam_ipi_input_param *cfg_in_param)
+{
+	struct mtk_raw_pipeline *pipe = ctx->pipe;
+	int raw_status = 0;
+	int mask = 0x0;
+	bool selected = false;
+	int m;
+
+	pipe->enabled_raw = 0;
+	raw_status = mtk_raw_available_resource(pipe->raw);
+	if (pipe->res_config.raw_num_used == 3) {
+		mask = 1 << MTKCAM_SUBDEV_RAW_0
+			| 1 << MTKCAM_SUBDEV_RAW_1 | 1 << MTKCAM_SUBDEV_RAW_2;
+		if (!(raw_status & mask)) {
+			pipe->enabled_raw |= mask;
+			selected = true;
+		}
+	} else if (pipe->res_config.raw_num_used == 2) {
+		for (m = MTKCAM_SUBDEV_RAW_0; m >= MTKCAM_SUBDEV_RAW_0; m--) {
+			mask = (1 << m) | (1 << (m + 1));
+			if (!(raw_status & mask)) {
+				pipe->enabled_raw |= mask;
+				selected = true;
+				break;
+			}
+		}
+	} else {
+		for (m = MTKCAM_SUBDEV_RAW_0; m < ARRAY_SIZE(pipe->raw->devs); m++) {
+			mask = 1 << m;
+			if (!(raw_status & mask)) {
+				pipe->enabled_raw |= mask;
+				selected = true;
+				break;
+			}
+		}
+	}
+	mtk_raw_available_resource(pipe->raw);
+	if (!selected)
+		return -EINVAL;
+
+	return 0;
+}
+
+static int mtk_raw_sd_s_stream(struct v4l2_subdev *sd, int enable)
+{
+	struct mtk_raw_pipeline *pipe;
+	struct mtk_raw *raw;
+	struct mtk_cam_device *cam;
+	struct mtk_cam_ctx *ctx;
+	unsigned int i;
+
+	pipe = container_of(sd, struct mtk_raw_pipeline, subdev);
+	raw = pipe->raw;
+	if (!raw)
+		return -1;
+	cam = dev_get_drvdata(raw->cam_dev);
+	ctx = mtk_cam_find_ctx(cam, &sd->entity);
+
+	if (WARN_ON(!ctx))
+		return -EINVAL;
+
+	if (enable) {
+		pipe->feature_active = pipe->user_res.raw_res.feature;
+		pipe->enabled_dmas = 0;
+		ctx->pipe = pipe;
+		ctx->used_raw_num++;
+		pipe->feature_active = pipe->user_res.raw_res.feature;
+		for (i = 0; i < ARRAY_SIZE(pipe->vdev_nodes); i++) {
+			if (!pipe->vdev_nodes[i].enabled)
+				continue;
+			pipe->enabled_dmas |=
+				(1ULL << pipe->vdev_nodes[i].desc.dma_port);
+		}
+	} else {
+		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)\n",
+					 __func__, i);
+				pm_runtime_put_sync(raw->devs[i]);
+			}
+		}
+	}
+
+	dev_info(raw->cam_dev, "%s:raw-%d: en %d, dev 0x%x dmas 0x%lx\n",
+		 __func__, pipe->id, enable, pipe->enabled_raw,
+		 pipe->enabled_dmas);
+
+	return 0;
+}
+
+static int mtk_raw_init_state(struct v4l2_subdev *sd,
+			      struct v4l2_subdev_state *sd_state)
+{
+	struct v4l2_mbus_framefmt *mf;
+	unsigned int i;
+	struct mtk_raw_pipeline *pipe =
+		container_of(sd, struct mtk_raw_pipeline, subdev);
+	struct mtk_raw *raw = pipe->raw;
+
+	for (i = 0; i < sd->entity.num_pads; i++) {
+		mf = v4l2_subdev_state_get_format(sd_state, i);
+		*mf = mfmt_default;
+
+		dev_dbg(raw->cam_dev, "%s init pad:%d format:0x%x\n",
+			sd->name, i, mf->code);
+	}
+
+	return 0;
+}
+
+static bool mtk_raw_try_fmt(struct v4l2_subdev *sd,
+			    struct v4l2_subdev_format *fmt)
+{
+	struct mtk_raw_pipeline *pipe =
+		container_of(sd, struct mtk_raw_pipeline, subdev);
+	struct mtk_raw *raw = pipe->raw;
+	unsigned int user_fmt = mtk_cam_get_sensor_fmt(fmt->format.code);
+
+	dev_dbg(raw->cam_dev, "%s:s_fmt: check format 0x%x, w:%d, h:%d field:%d\n",
+		sd->name, fmt->format.code, fmt->format.width,
+		fmt->format.height, fmt->format.field);
+
+	/* check sensor format */
+	if (fmt->pad == MTK_RAW_SINK) {
+		user_fmt = mtk_cam_get_sensor_fmt(fmt->format.code);
+		if (user_fmt == MTKCAM_IPI_IMG_FMT_UNKNOWN)
+			return false;
+	}
+
+	return true;
+}
+
+static struct v4l2_mbus_framefmt *
+mtk_raw_pipeline_get_fmt(struct mtk_raw_pipeline *pipe,
+			 struct v4l2_subdev_state *sd_state, u32 padid,
+			 int which)
+{
+	struct mtk_raw *raw = pipe->raw;
+
+	if (padid >= MTK_RAW_PIPELINE_PADS_NUM) {
+		dev_err(raw->cam_dev, "Wrong pad id:%d\n", padid);
+		return NULL;
+	}
+	/* format invalid and return default format */
+	if (which == V4L2_SUBDEV_FORMAT_TRY)
+		return v4l2_subdev_state_get_format(sd_state, padid);
+
+	if (WARN_ON(padid >= pipe->subdev.entity.num_pads))
+		return &pipe->cfg[0].mbus_fmt;
+
+	return &pipe->cfg[padid].mbus_fmt;
+}
+
+static struct v4l2_rect *
+mtk_raw_pipeline_get_selection(struct mtk_raw_pipeline *pipe,
+			       struct v4l2_subdev_state *sd_state, u32 pad,
+			       int which)
+{
+	struct mtk_raw *raw = pipe->raw;
+
+	if (pad >= MTK_RAW_PIPELINE_PADS_NUM) {
+		dev_err(raw->cam_dev, "Wrong pad id:%d\n", pad);
+		return NULL;
+	}
+	/* format invalid and return default format */
+	if (which == V4L2_SUBDEV_FORMAT_TRY)
+		return v4l2_subdev_state_get_crop(sd_state, pad);
+
+	if (WARN_ON(pad >= pipe->subdev.entity.num_pads))
+		return &pipe->cfg[0].crop;
+
+	return &pipe->cfg[pad].crop;
+}
+
+static void propagate_fmt(struct v4l2_mbus_framefmt *sink_mf,
+			  struct v4l2_mbus_framefmt *source_mf, int w, int h)
+{
+	source_mf->code = sink_mf->code;
+	source_mf->colorspace = sink_mf->colorspace;
+	source_mf->field = sink_mf->field;
+	source_mf->width = w;
+	source_mf->height = h;
+}
+
+static bool mtk_raw_fmt_get_res(struct v4l2_subdev *sd,
+				struct v4l2_subdev_format *fmt,
+				struct mtk_cam_resource *res)
+{
+	void *user_ptr;
+	u64 addr;
+
+	addr = ((u64)fmt->reserved[1] << 32) | fmt->reserved[2];
+	user_ptr = (void *)addr;
+	if (!user_ptr) {
+		dev_info(sd->v4l2_dev->dev, "%s: mtk_cam_resource is null\n",
+			 __func__);
+		return false;
+	}
+
+	if (copy_from_user(res, (void __user *)user_ptr, sizeof(*res))) {
+		dev_info(sd->v4l2_dev->dev,
+			 "%s: copy_from_user failedm user_ptr:%p\n",
+			 __func__, user_ptr);
+		return false;
+	}
+
+	dev_dbg(sd->v4l2_dev->dev,
+		"%s:sensor:%d/%d/%lld/%d/%d, raw:%lld/%d/%d/%d/%d/%d/%d/%d/%lld\n",
+		__func__,
+		res->sensor_res.hblank, res->sensor_res.vblank,
+		res->sensor_res.pixel_rate, res->sensor_res.interval.denominator,
+		res->sensor_res.interval.numerator,
+		res->raw_res.feature, res->raw_res.bin, res->raw_res.path_sel,
+		res->raw_res.raw_max, res->raw_res.raw_min, res->raw_res.raw_used,
+		res->raw_res.strategy, res->raw_res.pixel_mode,
+		res->raw_res.throughput);
+
+	return res;
+}
+
+static int mtk_raw_set_src_pad_selection_default(struct v4l2_subdev *sd,
+						 struct v4l2_subdev_state *sd_state,
+						 struct v4l2_mbus_framefmt *sink_fmt,
+						 struct mtk_cam_resource *res,
+						 int pad, int which)
+{
+	struct v4l2_rect *source_sel;
+	struct mtk_raw_pipeline *pipe;
+
+	pipe = container_of(sd, struct mtk_raw_pipeline, subdev);
+	source_sel = mtk_raw_pipeline_get_selection(pipe, sd_state, pad, which);
+	if (source_sel->width > sink_fmt->width)
+		source_sel->width = sink_fmt->width;
+
+	if (source_sel->height > sink_fmt->height)
+		source_sel->height = sink_fmt->height;
+
+	return 0;
+}
+
+static int mtk_raw_set_src_pad_selection_yuv(struct v4l2_subdev *sd,
+					     struct v4l2_subdev_state *sd_state,
+					     struct v4l2_mbus_framefmt *sink_fmt,
+					     struct mtk_cam_resource *res,
+					     int pad, int which)
+{
+	int i;
+	struct v4l2_rect *prev_yuv = NULL, *source_sel, *tmp_sel;
+	struct v4l2_mbus_framefmt *framefmt;
+	struct mtk_raw_pipeline *pipe;
+
+	pipe = container_of(sd, struct mtk_raw_pipeline, subdev);
+	mtk_raw_set_src_pad_selection_default(sd, sd_state, sink_fmt, res, pad, which);
+	source_sel = mtk_raw_pipeline_get_selection(pipe, sd_state, pad, which);
+
+	for (i = MTK_RAW_YUVO_1_OUT; i < pad; i++) {
+		framefmt = mtk_raw_pipeline_get_fmt(pipe, sd_state, pad, which);
+		tmp_sel = mtk_raw_pipeline_get_selection(pipe, sd_state, pad, which);
+
+		/* Skip disabled YUV pad */
+		if (!mtk_cam_is_pad_fmt_enable(framefmt))
+			continue;
+
+		prev_yuv = tmp_sel;
+	}
+
+	if (prev_yuv) {
+		if (source_sel->width != prev_yuv->width) {
+			source_sel->width = prev_yuv->width;
+			dev_dbg(sd->v4l2_dev->dev, "%s: prev width:%d",
+				__func__, source_sel->width);
+		}
+
+		if (source_sel->height != prev_yuv->height) {
+			source_sel->height = prev_yuv->height;
+			dev_dbg(sd->v4l2_dev->dev, "%s: prev height:%d",
+				__func__, source_sel->height);
+		}
+	}
+
+	return 0;
+}
+
+static struct v4l2_mbus_framefmt *
+mtk_raw_get_sink_pad_framefmt(struct v4l2_subdev *sd,
+			      struct v4l2_subdev_state *sd_state, int which)
+{
+	struct v4l2_mbus_framefmt *sink_fmt = NULL, *tmp_fmt;
+	struct mtk_raw_pipeline *pipe;
+	int i;
+
+	pipe = container_of(sd, struct mtk_raw_pipeline, subdev);
+	for (i = MTK_RAW_SINK; i < MTK_RAW_SOURCE_BEGIN; i++) {
+		tmp_fmt = mtk_raw_pipeline_get_fmt(pipe, sd_state, i, which);
+		if (i != MTK_RAW_META_IN && mtk_cam_is_pad_fmt_enable(tmp_fmt)) {
+			sink_fmt = tmp_fmt;
+			break;
+		}
+	}
+
+	return sink_fmt;
+}
+
+static int mtk_raw_set_pad_selection(struct v4l2_subdev *sd,
+				     struct v4l2_subdev_state *sd_state,
+				     struct v4l2_subdev_selection *sel)
+{
+	struct v4l2_mbus_framefmt *sink_fmt;
+	struct mtk_raw_pipeline *pipe;
+	struct mtk_cam_video_device *node;
+	struct mtk_cam_resource *res = NULL;
+	struct v4l2_rect *crop;
+	int ret;
+
+	if (sel->pad < MTK_RAW_MAIN_STREAM_OUT ||
+	    sel->pad >= MTK_RAW_META_OUT_BEGIN)
+		return -EINVAL;
+
+	pipe = container_of(sd, struct mtk_raw_pipeline, subdev);
+
+	/* The crop rectangle can't be changed while streaming. */
+	if (sel->which == V4L2_SUBDEV_FORMAT_ACTIVE &&
+	    !media_entity_is_streaming(&sd->entity))
+		return -EBUSY;
+
+	/*
+	 * Find the sink pad fmt, there must be one eanbled sink pad at least
+	 */
+	sink_fmt = mtk_raw_get_sink_pad_framefmt(sd, sd_state, sel->which);
+	if (!sink_fmt)
+		return -EINVAL;
+
+	node = &pipe->vdev_nodes[sel->pad - MTK_RAW_SINK_NUM];
+	crop = mtk_raw_pipeline_get_selection(pipe, sd_state, sel->pad, sel->which);
+	if (!crop)
+		return -EINVAL;
+
+	*crop = sel->r;
+	ret = node->desc.pad_ops->set_pad_selection(sd, sd_state, sink_fmt,
+						    res, sel->pad, sel->which);
+	if (ret)
+		return -EINVAL;
+
+	sel->r = *crop;
+
+	return 0;
+}
+
+static int mtk_raw_get_pad_selection(struct v4l2_subdev *sd,
+				     struct v4l2_subdev_state *sd_state,
+				     struct v4l2_subdev_selection *sel)
+{
+	struct mtk_raw_pipeline *pipe;
+	struct v4l2_rect *crop;
+
+	if (sel->pad < MTK_RAW_MAIN_STREAM_OUT ||
+	    sel->pad >= MTK_RAW_META_OUT_BEGIN)
+		return -EINVAL;
+
+	pipe = container_of(sd, struct mtk_raw_pipeline, subdev);
+	crop = mtk_raw_pipeline_get_selection(pipe, sd_state, sel->pad, sel->which);
+	if (!crop)
+		return -EINVAL;
+
+	sel->r = *crop;
+
+	return 0;
+}
+
+static int mtk_raw_set_sink_pad_fmt(struct v4l2_subdev *sd,
+				    struct v4l2_subdev_state *sd_state,
+				    struct v4l2_subdev_format *fmt)
+{
+	struct device *dev;
+	struct mtk_cam_video_device *node;
+	const char *node_str;
+	const struct mtk_cam_format_desc *fmt_desc;
+	struct mtk_raw_pipeline *pipe;
+	int i;
+	int ipi_fmt;
+	struct v4l2_mbus_framefmt *framefmt, *source_fmt = NULL, *tmp_fmt;
+
+	/* Do nothing for pad to meta video device */
+	if (fmt->pad == MTK_RAW_META_IN)
+		return 0;
+
+	dev = sd->v4l2_dev->dev;
+	pipe = container_of(sd, struct mtk_raw_pipeline, subdev);
+	framefmt = mtk_raw_pipeline_get_fmt(pipe, sd_state, fmt->pad, fmt->which);
+
+	/* If data from sensor, we check the size with max imgo size*/
+	if (fmt->pad < MTK_RAW_SINK_NUM) {
+		/* from sensor */
+		node = &pipe->vdev_nodes[MTK_RAW_MAIN_STREAM_OUT - MTK_RAW_SINK_NUM];
+		node_str = "sink";
+	} else {
+		/* from memory */
+		node = &pipe->vdev_nodes[fmt->pad - MTK_RAW_SINK_NUM];
+		node_str = node->desc.name;
+	}
+
+	ipi_fmt = mtk_cam_get_sensor_fmt(framefmt->code);
+	if (ipi_fmt == MTKCAM_IPI_IMG_FMT_UNKNOWN) {
+		/**
+		 * Set imgo's default fmt, the user must check
+		 * if the pad sink format is the same as the
+		 * source format of the link before stream on.
+		 */
+		fmt_desc = &node->desc.fmts[node->desc.default_fmt_idx];
+		framefmt->code = fmt_desc->pfmt.code;
+		dev_info(dev,
+			 "%s(%d): Adjust unaccept fmt code on sink pad:%d, 0x%x->0x%x\n",
+			 __func__, fmt->which, fmt->pad, fmt->format.code, framefmt->code);
+	}
+
+	/* Reset pads' enable state*/
+	for (i = MTK_RAW_SINK; i < MTK_RAW_META_OUT_BEGIN; i++) {
+		if (i == MTK_RAW_META_IN)
+			continue;
+		tmp_fmt = mtk_raw_pipeline_get_fmt(pipe, sd_state, i, fmt->which);
+		mtk_cam_pad_fmt_disable(tmp_fmt);
+	}
+
+	*framefmt = fmt->format;
+	if (framefmt->width > node->desc.frmsizes->stepwise.max_width)
+		framefmt->width = node->desc.frmsizes->stepwise.max_width;
+
+	if (framefmt->height > node->desc.frmsizes->stepwise.max_height)
+		framefmt->height = node->desc.frmsizes->stepwise.max_height;
+
+	mtk_cam_pad_fmt_enable(framefmt);
+
+	dev_info(dev,
+		 "%s(%d): Set fmt pad:%d(%s), code/w/h = 0x%x/%d/%d\n",
+		 __func__, fmt->which, fmt->pad, node_str,
+		 framefmt->code, framefmt->width, framefmt->height);
+
+	/* Propagation inside subdev */
+	for (i = MTK_RAW_SOURCE_BEGIN; i < MTK_RAW_META_OUT_BEGIN; i++) {
+		source_fmt =
+			mtk_raw_pipeline_get_fmt(pipe, sd_state, i, fmt->which);
+
+		/* Get default format's desc for the pad */
+		node = &pipe->vdev_nodes[i - MTK_RAW_SINK_NUM];
+
+		/**
+		 * Propagate the size from sink pad to source pades, adjusted
+		 * based on each pad's default format.
+		 */
+		if (source_fmt->width > node->desc.frmsizes->stepwise.max_width)
+			source_fmt->width = node->desc.frmsizes->stepwise.max_width;
+		else
+			source_fmt->width = framefmt->width;
+
+		if (source_fmt->height > node->desc.frmsizes->stepwise.max_height)
+			source_fmt->height = node->desc.frmsizes->stepwise.max_height;
+		else
+			source_fmt->height = framefmt->height;
+
+		dev_dbg(dev,
+			"%s(%d): Propagate to pad:%d(%s), (0x%x/%d/%d)\n",
+			__func__, fmt->which, fmt->pad, node->desc.name,
+			source_fmt->code, source_fmt->width, source_fmt->height);
+	}
+
+	return 0;
+}
+
+static int mtk_raw_set_src_pad_fmt_default(struct v4l2_subdev *sd,
+					   struct v4l2_subdev_state *sd_state,
+					   struct v4l2_mbus_framefmt *sink_fmt,
+					   struct mtk_cam_resource *res,
+					   int pad, int which)
+{
+	struct device *dev;
+	struct v4l2_mbus_framefmt *source_fmt;
+	struct mtk_raw_pipeline *pipe =
+		container_of(sd, struct mtk_raw_pipeline, subdev);
+	struct mtk_cam_video_device *node;
+
+	dev = sd->v4l2_dev->dev;
+	node = &pipe->vdev_nodes[pad - MTK_RAW_SINK_NUM];
+	source_fmt = mtk_raw_pipeline_get_fmt(pipe, sd_state, pad, which);
+	if (source_fmt->width > sink_fmt->width) {
+		dev_info(dev,
+			 "%s(%d): adjusted: width(%d) over sink (%d)\n",
+			 __func__, which, source_fmt->width, sink_fmt->width);
+		source_fmt->width = sink_fmt->width;
+	}
+
+	if (source_fmt->height > sink_fmt->height) {
+		dev_info(dev,
+			 "%s(%d): adjusted: width(%d) over sink (%d)\n",
+			 __func__, which, source_fmt->height, sink_fmt->height);
+		source_fmt->height = sink_fmt->height;
+	}
+
+	if (source_fmt->width > node->desc.frmsizes->stepwise.max_width) {
+		dev_info(dev,
+			 "%s(%d): adjusted: width(%d) over max (%d)\n",
+			 __func__, which, source_fmt->width,
+			 node->desc.frmsizes->stepwise.max_width);
+		source_fmt->width = node->desc.frmsizes->stepwise.max_width;
+	}
+
+	if (source_fmt->height > node->desc.frmsizes->stepwise.max_height) {
+		dev_info(dev,
+			 "%s(%d): adjusted: height(%d) over max (%d)\n",
+			 __func__, which, source_fmt->height,
+			 node->desc.frmsizes->stepwise.max_height);
+	}
+
+	return 0;
+}
+
+static int mtk_raw_set_src_pad_fmt_rzh1n2(struct v4l2_subdev *sd,
+					  struct v4l2_subdev_state *sd_state,
+					  struct v4l2_mbus_framefmt *sink_fmt,
+					  struct mtk_cam_resource *res, int pad,
+					  int which)
+{
+	struct device *dev;
+	struct v4l2_mbus_framefmt *source_fmt;
+	struct v4l2_mbus_framefmt *tmp_fmt;
+	struct mtk_raw_pipeline *pipe =
+		container_of(sd, struct mtk_raw_pipeline, subdev);
+
+	dev = sd->v4l2_dev->dev;
+	mtk_raw_set_src_pad_fmt_default(sd, sd_state, sink_fmt, res, pad, which);
+	source_fmt = mtk_raw_pipeline_get_fmt(pipe, sd_state, pad, which);
+
+	/* rzh1n2to_r1 and rzh1n2to_r3 size must be the same */
+	if (pad == MTK_RAW_RZH1N2TO_3_OUT) {
+		tmp_fmt = mtk_raw_pipeline_get_fmt(pipe, sd_state,
+						   MTK_RAW_RZH1N2TO_1_OUT, which);
+		if (mtk_cam_is_pad_fmt_enable(tmp_fmt) &&
+		    source_fmt->height != tmp_fmt->height &&
+		    source_fmt->width != tmp_fmt->width) {
+			dev_info(dev,
+				 "%s(%d): adjusted: rzh1n2to_r3(%d,%d) and rzh1n2to_r1(%d,%d) must have the same sz\n",
+				 __func__, which,
+				 source_fmt->width, source_fmt->height,
+				 tmp_fmt->width, tmp_fmt->height);
+
+			source_fmt->width = tmp_fmt->width;
+			source_fmt->height = tmp_fmt->height;
+		}
+	}
+
+	return 0;
+}
+
+static int mtk_raw_set_src_pad_fmt(struct v4l2_subdev *sd,
+				   struct v4l2_subdev_state *sd_state,
+				   struct v4l2_subdev_format *fmt)
+{
+	struct device *dev;
+	struct mtk_cam_resource res;
+	struct mtk_cam_video_device *node;
+	struct mtk_raw_pipeline *pipe;
+	int ret = 0;
+	struct v4l2_mbus_framefmt *source_fmt, *sink_fmt;
+
+	/* Do nothing for pad to meta video device */
+	if (fmt->pad >= MTK_RAW_META_OUT_BEGIN)
+		return 0;
+
+	pipe = container_of(sd, struct mtk_raw_pipeline, subdev);
+	dev = sd->v4l2_dev->dev;
+	node = &pipe->vdev_nodes[fmt->pad - MTK_RAW_SINK_NUM];
+
+	/*
+	 * Find the sink pad fmt, there must be one eanbled sink pad at least
+	 */
+	sink_fmt = mtk_raw_get_sink_pad_framefmt(sd, sd_state, fmt->which);
+	if (!sink_fmt) {
+		dev_info(dev,
+			 "%s(%d): Set fmt pad:%d(%s), no s_fmt on sink pad\n",
+			 __func__, fmt->which, fmt->pad, node->desc.name);
+		return -EINVAL;
+	}
+
+	if (!mtk_raw_fmt_get_res(sd, fmt, &res)) {
+		dev_info(dev,
+			 "%s(%d): Set fmt pad:%d(%s), no mtk_cam_resource found\n",
+			 __func__, fmt->which, fmt->pad, node->desc.name);
+		return -EINVAL;
+	}
+
+	if (node->desc.pad_ops->set_pad_fmt) {
+		/* call source pad's set_pad_fmt op to adjust fmt by pad */
+		source_fmt = mtk_raw_pipeline_get_fmt(pipe, sd_state, fmt->pad,
+						      fmt->which);
+
+		*source_fmt = fmt->format;
+		ret = node->desc.pad_ops->set_pad_fmt(sd, sd_state, sink_fmt,
+						      &res, fmt->pad, fmt->which);
+	}
+
+	if (ret)
+		return ret;
+
+	if (!source_fmt) {
+		dev_info(dev,
+			 "%s(%d): Set fmt pad:%d(%s), no s_fmt on source pad\n",
+			 __func__, fmt->which, fmt->pad, node->desc.name);
+		return -EINVAL;
+	}
+
+	dev_dbg(dev,
+		"%s(%d): s_fmt to pad:%d(%s), user(0x%x/%d/%d) driver(0x%x/%d/%d)\n",
+		__func__, fmt->which, fmt->pad, node->desc.name,
+		fmt->format.code, fmt->format.width, fmt->format.height,
+		source_fmt->code, source_fmt->width, source_fmt->height);
+	mtk_cam_pad_fmt_disable(source_fmt);
+	fmt->format = *source_fmt;
+
+	return 0;
+}
+
+static int mtk_raw_try_pad_fmt(struct v4l2_subdev *sd,
+			       struct v4l2_subdev_state *sd_state,
+			       struct v4l2_subdev_format *fmt)
+{
+	if (fmt->pad >= MTK_RAW_SINK && fmt->pad < MTK_RAW_SOURCE_BEGIN)
+		mtk_raw_set_sink_pad_fmt(sd, sd_state, fmt);
+	else if (fmt->pad < MTK_RAW_PIPELINE_PADS_NUM)
+		mtk_raw_set_src_pad_fmt(sd, sd_state, fmt);
+	else
+		return -EINVAL;
+
+	return 0;
+}
+
+static int mtk_raw_call_set_fmt(struct v4l2_subdev *sd,
+				struct v4l2_subdev_state *sd_state,
+				struct v4l2_subdev_format *fmt,
+				bool streaming)
+{
+	struct mtk_raw_pipeline *pipe =
+		container_of(sd, struct mtk_raw_pipeline, subdev);
+	struct mtk_raw *raw = pipe->raw;
+	struct v4l2_mbus_framefmt *mf;
+
+	if (!sd || !fmt) {
+		dev_dbg(raw->cam_dev, "%s: Required sd(%p), fmt(%p)\n",
+			__func__, sd, fmt);
+		return -EINVAL;
+	}
+
+	if (fmt->which == V4L2_SUBDEV_FORMAT_TRY && !sd_state) {
+		dev_dbg(raw->cam_dev,
+			"%s: Required sd(%p), cfg(%p) for FORMAT_TRY\n",
+			__func__, sd, sd_state);
+		return -EINVAL;
+	}
+
+	if (!mtk_raw_try_fmt(sd, fmt) &&
+	    !mtk_cam_feature_is_pure_m2m(pipe->feature_pending)) {
+		mf = mtk_raw_pipeline_get_fmt(pipe, sd_state, fmt->pad, fmt->which);
+		fmt->format = *mf;
+		dev_info(raw->cam_dev,
+			 "sd:%s pad:%d didn't apply and keep format w/h/code %d/%d/0x%x\n",
+			 sd->name, fmt->pad, mf->width, mf->height, mf->code);
+	} else {
+		mf = mtk_raw_pipeline_get_fmt(pipe, sd_state, fmt->pad, fmt->which);
+		*mf = fmt->format;
+		dev_dbg(raw->cam_dev,
+			"sd:%s pad:%d set format w/h/code %d/%d/0x%x\n",
+			sd->name, fmt->pad, mf->width, mf->height, mf->code);
+	}
+
+	/*sink pad format propagate to source pad*/
+	if (fmt->pad == MTK_RAW_SINK) {
+		struct v4l2_mbus_framefmt *source_mf;
+
+		source_mf = mtk_raw_pipeline_get_fmt(pipe, sd_state,
+						     MTK_RAW_MAIN_STREAM_OUT,
+						     fmt->which);
+
+		if (streaming) {
+			propagate_fmt(mf, source_mf, mf->width, mf->height);
+			return 0;
+		}
+
+		/**
+		 * User will trigger resource calc
+		 * with V4L2_CID_MTK_CAM_RAW_RESOURCE_CALC
+		 * so we don't need to trigger it here anymore.
+		 */
+		propagate_fmt(mf, source_mf, mf->width, mf->height);
+	}
+
+	return 0;
+}
+
+static int mtk_raw_set_fmt(struct v4l2_subdev *sd,
+			   struct v4l2_subdev_state *sd_state,
+			   struct v4l2_subdev_format *fmt)
+{
+	if (fmt->which == V4L2_SUBDEV_FORMAT_TRY)
+		return mtk_raw_try_pad_fmt(sd, sd_state, fmt);
+
+	/* The format can't be changed while streaming. */
+	if (!media_entity_is_streaming(&sd->entity))
+		return mtk_raw_call_set_fmt(sd, sd_state, fmt, false);
+
+	dev_info(sd->v4l2_dev->dev,
+		 "Pipeline is streaming, cannot change format\n");
+	return -EBUSY;
+}
+
+static int mtk_raw_get_fmt(struct v4l2_subdev *sd,
+			   struct v4l2_subdev_state *sd_state,
+			  struct v4l2_subdev_format *fmt)
+{
+	struct mtk_raw_pipeline *pipe =
+		container_of(sd, struct mtk_raw_pipeline, subdev);
+	struct mtk_raw *raw = pipe->raw;
+	struct v4l2_mbus_framefmt *mf;
+
+	mf = mtk_raw_pipeline_get_fmt(pipe, sd_state, fmt->pad, fmt->which);
+	fmt->format = *mf;
+	dev_dbg(raw->cam_dev, "sd:%s pad:%d get format w/h/code %d/%d/0x%x\n",
+		sd->name, fmt->pad, fmt->format.width, fmt->format.height,
+		fmt->format.code);
+
+	return 0;
+}
+
+static int mtk_cam_media_link_setup(struct media_entity *entity,
+				    const struct media_pad *local,
+				    const struct media_pad *remote, u32 flags)
+{
+	struct mtk_raw_pipeline *pipe =
+		container_of(entity, struct mtk_raw_pipeline, subdev.entity);
+	struct mtk_raw *raw = pipe->raw;
+	u32 pad = local->index;
+
+	dev_dbg(raw->cam_dev,
+		"%s: raw %d: remote:%s:%d->local:%s:%d flags:0x%x\n",
+		__func__, pipe->id, remote->entity->name, remote->index,
+		local->entity->name, local->index, flags);
+
+	if (pad < MTK_RAW_PIPELINE_PADS_NUM && pad != MTK_RAW_SINK)
+		pipe->vdev_nodes[pad - MTK_RAW_SINK_NUM].enabled =
+			!!(flags & MEDIA_LNK_FL_ENABLED);
+
+	if (!media_entity_is_streaming(entity) && !(flags & MEDIA_LNK_FL_ENABLED))
+		memset(pipe->cfg, 0, sizeof(pipe->cfg));
+
+	if (pad == MTK_RAW_SINK && flags & MEDIA_LNK_FL_ENABLED)
+		pipe->res_config.seninf =
+			media_entity_to_v4l2_subdev(remote->entity);
+
+	return 0;
+}
+
+static int mtk_raw_set_frame_interval(struct v4l2_subdev *sd,
+				      struct v4l2_subdev_state *state,
+				      struct v4l2_subdev_frame_interval *fi)
+{
+	struct mtk_raw_pipeline *pipe =
+		container_of(sd, struct mtk_raw_pipeline, subdev);
+	struct mtk_raw *raw = pipe->raw;
+
+	dev_dbg(raw->cam_dev, "%s:pipe(%d):current res: fps = %d/%d",
+		__func__, pipe->id,
+		fi->interval.numerator, fi->interval.denominator);
+	pipe->res_config.interval = fi->interval;
+
+	return 0;
+}
+
+static int mtk_raw_get_frame_interval(struct v4l2_subdev *sd,
+				      struct v4l2_subdev_state *state,
+				      struct v4l2_subdev_frame_interval *fi)
+{
+	struct mtk_raw_pipeline *pipe =
+		container_of(sd, struct mtk_raw_pipeline, subdev);
+	struct mtk_raw *raw = pipe->raw;
+
+	dev_dbg(raw->cam_dev, "%s:pipe(%d):current res: fps = %d/%d",
+		__func__, pipe->id,
+		fi->interval.numerator, fi->interval.denominator);
+	fi->interval = pipe->res_config.interval;
+
+	return 0;
+}
+
+static const struct v4l2_subdev_core_ops mtk_raw_subdev_core_ops = {
+	.subscribe_event = mtk_raw_sd_subscribe_event,
+	.unsubscribe_event = v4l2_event_subdev_unsubscribe,
+};
+
+static const struct v4l2_subdev_video_ops mtk_raw_subdev_video_ops = {
+	.s_stream =  mtk_raw_sd_s_stream,
+};
+
+static const struct v4l2_subdev_pad_ops mtk_raw_subdev_pad_ops = {
+	.link_validate = mtk_cam_link_validate,
+	.set_fmt = mtk_raw_set_fmt,
+	.get_fmt = mtk_raw_get_fmt,
+	.set_selection = mtk_raw_set_pad_selection,
+	.get_selection = mtk_raw_get_pad_selection,
+	.set_frame_interval = mtk_raw_set_frame_interval,
+	.get_frame_interval = mtk_raw_get_frame_interval,
+};
+
+static const struct v4l2_subdev_internal_ops mtk_raw_subdev_internal_ops = {
+	.init_state = mtk_raw_init_state,
+};
+
+static const struct v4l2_subdev_ops mtk_raw_subdev_ops = {
+	.core = &mtk_raw_subdev_core_ops,
+	.video = &mtk_raw_subdev_video_ops,
+	.pad = &mtk_raw_subdev_pad_ops,
+};
+
+static const struct media_entity_operations mtk_cam_media_entity_ops = {
+	.link_setup = mtk_cam_media_link_setup,
+	.link_validate = v4l2_subdev_link_validate,
+};
+
+static const struct v4l2_ioctl_ops mtk_cam_v4l2_vout_ioctl_ops = {
+	.vidioc_querycap = mtk_cam_vidioc_querycap,
+	.vidioc_enum_framesizes = mtk_cam_vidioc_enum_framesizes,
+	.vidioc_enum_fmt_vid_out = mtk_cam_vidioc_enum_fmt,
+	.vidioc_g_fmt_vid_out_mplane = mtk_cam_vidioc_g_fmt,
+	.vidioc_s_fmt_vid_out_mplane = mtk_cam_vidioc_s_fmt,
+	.vidioc_try_fmt_vid_out_mplane = mtk_cam_vidioc_try_fmt,
+	.vidioc_reqbufs = vb2_ioctl_reqbufs,
+	.vidioc_create_bufs = vb2_ioctl_create_bufs,
+	.vidioc_prepare_buf = vb2_ioctl_prepare_buf,
+	.vidioc_querybuf = vb2_ioctl_querybuf,
+	.vidioc_qbuf = vb2_ioctl_qbuf,
+	.vidioc_dqbuf = vb2_ioctl_dqbuf,
+	.vidioc_streamon = vb2_ioctl_streamon,
+	.vidioc_streamoff = vb2_ioctl_streamoff,
+	.vidioc_expbuf = vb2_ioctl_expbuf,
+	.vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
+	.vidioc_unsubscribe_event = v4l2_event_unsubscribe,
+};
+
+static const struct v4l2_ioctl_ops mtk_cam_v4l2_vcap_ioctl_ops = {
+	.vidioc_querycap = mtk_cam_vidioc_querycap,
+	.vidioc_enum_framesizes = mtk_cam_vidioc_enum_framesizes,
+	.vidioc_enum_fmt_vid_cap = mtk_cam_vidioc_enum_fmt,
+	.vidioc_g_fmt_vid_cap_mplane = mtk_cam_vidioc_g_fmt,
+	.vidioc_s_fmt_vid_cap_mplane = mtk_cam_vidioc_s_fmt,
+	.vidioc_try_fmt_vid_cap_mplane = mtk_cam_vidioc_try_fmt,
+	.vidioc_s_selection = mtk_cam_vidioc_s_selection,
+	.vidioc_reqbufs = vb2_ioctl_reqbufs,
+	.vidioc_create_bufs = vb2_ioctl_create_bufs,
+	.vidioc_prepare_buf = vb2_ioctl_prepare_buf,
+	.vidioc_querybuf = vb2_ioctl_querybuf,
+	.vidioc_qbuf = vb2_ioctl_qbuf,
+	.vidioc_dqbuf = vb2_ioctl_dqbuf,
+	.vidioc_streamon = vb2_ioctl_streamon,
+	.vidioc_streamoff = vb2_ioctl_streamoff,
+	.vidioc_expbuf = vb2_ioctl_expbuf,
+	.vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
+	.vidioc_unsubscribe_event = v4l2_event_unsubscribe,
+};
+
+static const struct v4l2_ioctl_ops mtk_cam_v4l2_meta_cap_ioctl_ops = {
+	.vidioc_querycap = mtk_cam_vidioc_querycap,
+	.vidioc_enum_fmt_meta_cap = mtk_cam_vidioc_meta_enum_fmt,
+	.vidioc_g_fmt_meta_cap = mtk_cam_vidioc_g_meta_fmt,
+	.vidioc_s_fmt_meta_cap = mtk_cam_vidioc_g_meta_fmt,
+	.vidioc_try_fmt_meta_cap = mtk_cam_vidioc_g_meta_fmt,
+	.vidioc_reqbufs = vb2_ioctl_reqbufs,
+	.vidioc_create_bufs = vb2_ioctl_create_bufs,
+	.vidioc_prepare_buf = vb2_ioctl_prepare_buf,
+	.vidioc_querybuf = vb2_ioctl_querybuf,
+	.vidioc_qbuf = vb2_ioctl_qbuf,
+	.vidioc_dqbuf = vb2_ioctl_dqbuf,
+	.vidioc_streamon = vb2_ioctl_streamon,
+	.vidioc_streamoff = vb2_ioctl_streamoff,
+	.vidioc_expbuf = vb2_ioctl_expbuf,
+};
+
+static const struct v4l2_ioctl_ops mtk_cam_v4l2_meta_out_ioctl_ops = {
+	.vidioc_querycap = mtk_cam_vidioc_querycap,
+	.vidioc_enum_fmt_meta_out = mtk_cam_vidioc_meta_enum_fmt,
+	.vidioc_g_fmt_meta_out = mtk_cam_vidioc_g_meta_fmt,
+	.vidioc_s_fmt_meta_out = mtk_cam_vidioc_g_meta_fmt,
+	.vidioc_try_fmt_meta_out = mtk_cam_vidioc_g_meta_fmt,
+	.vidioc_reqbufs = vb2_ioctl_reqbufs,
+	.vidioc_create_bufs = vb2_ioctl_create_bufs,
+	.vidioc_prepare_buf = vb2_ioctl_prepare_buf,
+	.vidioc_querybuf = vb2_ioctl_querybuf,
+	.vidioc_qbuf = vb2_ioctl_qbuf,
+	.vidioc_dqbuf = vb2_ioctl_dqbuf,
+	.vidioc_streamon = vb2_ioctl_streamon,
+	.vidioc_streamoff = vb2_ioctl_streamoff,
+	.vidioc_expbuf = vb2_ioctl_expbuf,
+};
+
+static const struct mtk_cam_format_desc  stream_out_fmts[] = {
+	/* This is a default image format */
+	{
+		.vfmt.fmt.pix_mp = {
+			.width = IMG_MAX_WIDTH,
+			.height = IMG_MAX_HEIGHT,
+			.pixelformat = V4L2_PIX_FMT_SBGGR8,
+			.num_planes = 1,
+		},
+		.pfmt = {
+			.code = MEDIA_BUS_FMT_SBGGR8_1X8,
+		}
+	},
+	{
+		.vfmt.fmt.pix_mp = {
+			.width = IMG_MAX_WIDTH,
+			.height = IMG_MAX_HEIGHT,
+			.pixelformat = V4L2_PIX_FMT_SBGGR10,
+			.num_planes = 1,
+		},
+		.pfmt = {
+			.code = MEDIA_BUS_FMT_SBGGR10_1X10,
+		}
+	},
+	{
+		.vfmt.fmt.pix_mp = {
+			.width = IMG_MAX_WIDTH,
+			.height = IMG_MAX_HEIGHT,
+			.pixelformat = V4L2_PIX_FMT_MTISP_SBGGR10,
+			.num_planes = 1,
+		},
+		.pfmt = {
+			.code = MEDIA_BUS_FMT_MTISP_SBGGR10_1X10,
+		}
+	},
+	{
+		.vfmt.fmt.pix_mp = {
+			.width = IMG_MAX_WIDTH,
+			.height = IMG_MAX_HEIGHT,
+			.pixelformat = V4L2_PIX_FMT_SBGGR10P,
+		},
+	},
+	{
+		.vfmt.fmt.pix_mp = {
+			.width = IMG_MAX_WIDTH,
+			.height = IMG_MAX_HEIGHT,
+			.pixelformat = V4L2_PIX_FMT_SBGGR12,
+			.num_planes = 1,
+		},
+		.pfmt = {
+			.code = MEDIA_BUS_FMT_SBGGR12_1X12,
+		}
+	},
+	{
+		.vfmt.fmt.pix_mp = {
+			.width = IMG_MAX_WIDTH,
+			.height = IMG_MAX_HEIGHT,
+			.pixelformat = V4L2_PIX_FMT_MTISP_SBGGR12,
+			.num_planes = 1,
+		},
+		.pfmt = {
+			.code = MEDIA_BUS_FMT_MTISP_SBGGR12_1X12,
+		}
+	},
+	{
+		.vfmt.fmt.pix_mp = {
+			.width = IMG_MAX_WIDTH,
+			.height = IMG_MAX_HEIGHT,
+			.pixelformat = V4L2_PIX_FMT_SBGGR14,
+			.num_planes = 1,
+		},
+		.pfmt = {
+			.code = MEDIA_BUS_FMT_SBGGR14_1X14,
+		}
+	},
+	{
+		.vfmt.fmt.pix_mp = {
+			.width = IMG_MAX_WIDTH,
+			.height = IMG_MAX_HEIGHT,
+			.pixelformat = V4L2_PIX_FMT_MTISP_SBGGR14,
+			.num_planes = 1,
+		},
+		.pfmt = {
+			.code = MEDIA_BUS_FMT_MTISP_SBGGR14_1X14,
+		}
+	},
+	{
+		.vfmt.fmt.pix_mp = {
+			.width = IMG_MAX_WIDTH,
+			.height = IMG_MAX_HEIGHT,
+			.pixelformat = V4L2_PIX_FMT_SGBRG8,
+			.num_planes = 1,
+		},
+		.pfmt = {
+			.code = MEDIA_BUS_FMT_SGBRG8_1X8,
+		}
+	},
+	{
+		.vfmt.fmt.pix_mp = {
+			.width = IMG_MAX_WIDTH,
+			.height = IMG_MAX_HEIGHT,
+			.pixelformat = V4L2_PIX_FMT_SGBRG10,
+			.num_planes = 1,
+		},
+		.pfmt = {
+			.code = MEDIA_BUS_FMT_SGBRG10_1X10,
+		}
+	},
+	{
+		.vfmt.fmt.pix_mp = {
+			.width = IMG_MAX_WIDTH,
+			.height = IMG_MAX_HEIGHT,
+			.pixelformat = V4L2_PIX_FMT_MTISP_SGBRG10,
+			.num_planes = 1,
+		},
+		.pfmt = {
+			.code = MEDIA_BUS_FMT_MTISP_SGBRG10_1X10,
+		}
+	},
+	{
+		.vfmt.fmt.pix_mp = {
+			.width = IMG_MAX_WIDTH,
+			.height = IMG_MAX_HEIGHT,
+			.pixelformat = V4L2_PIX_FMT_SGBRG10P,
+		},
+	},
+	{
+		.vfmt.fmt.pix_mp = {
+			.width = IMG_MAX_WIDTH,
+			.height = IMG_MAX_HEIGHT,
+			.pixelformat = V4L2_PIX_FMT_SGBRG12,
+			.num_planes = 1,
+		},
+		.pfmt = {
+			.code = MEDIA_BUS_FMT_SGBRG12_1X12,
+		}
+	},
+	{
+		.vfmt.fmt.pix_mp = {
+			.width = IMG_MAX_WIDTH,
+			.height = IMG_MAX_HEIGHT,
+			.pixelformat = V4L2_PIX_FMT_MTISP_SGBRG12,
+			.num_planes = 1,
+		},
+		.pfmt = {
+			.code = MEDIA_BUS_FMT_MTISP_SGBRG12_1X12,
+		}
+	},
+	{
+		.vfmt.fmt.pix_mp = {
+			.width = IMG_MAX_WIDTH,
+			.height = IMG_MAX_HEIGHT,
+			.pixelformat = V4L2_PIX_FMT_SGBRG14,
+			.num_planes = 1,
+		},
+		.pfmt = {
+			.code = MEDIA_BUS_FMT_SGBRG14_1X14,
+		}
+	},
+	{
+		.vfmt.fmt.pix_mp = {
+			.width = IMG_MAX_WIDTH,
+			.height = IMG_MAX_HEIGHT,
+			.pixelformat = V4L2_PIX_FMT_MTISP_SGBRG14,
+			.num_planes = 1,
+		},
+		.pfmt = {
+			.code = MEDIA_BUS_FMT_MTISP_SGBRG14_1X14,
+		}
+	},
+	{
+		.vfmt.fmt.pix_mp = {
+			.width = IMG_MAX_WIDTH,
+			.height = IMG_MAX_HEIGHT,
+			.pixelformat = V4L2_PIX_FMT_SGRBG8,
+			.num_planes = 1,
+		},
+		.pfmt = {
+			.code = MEDIA_BUS_FMT_SGRBG8_1X8,
+		}
+	},
+	{
+		.vfmt.fmt.pix_mp = {
+			.width = IMG_MAX_WIDTH,
+			.height = IMG_MAX_HEIGHT,
+			.pixelformat = V4L2_PIX_FMT_SGRBG10,
+			.num_planes = 1,
+		},
+		.pfmt = {
+			.code = MEDIA_BUS_FMT_SGRBG10_1X10,
+		}
+	},
+	{
+		.vfmt.fmt.pix_mp = {
+			.width = IMG_MAX_WIDTH,
+			.height = IMG_MAX_HEIGHT,
+			.pixelformat = V4L2_PIX_FMT_MTISP_SGRBG10,
+			.num_planes = 1,
+		},
+		.pfmt = {
+			.code = MEDIA_BUS_FMT_MTISP_SGRBG10_1X10,
+		}
+	},
+	{
+		.vfmt.fmt.pix_mp = {
+			.width = IMG_MAX_WIDTH,
+			.height = IMG_MAX_HEIGHT,
+			.pixelformat = V4L2_PIX_FMT_SGRBG10P,
+		},
+	},
+	{
+		.vfmt.fmt.pix_mp = {
+			.width = IMG_MAX_WIDTH,
+			.height = IMG_MAX_HEIGHT,
+			.pixelformat = V4L2_PIX_FMT_SGRBG12,
+			.num_planes = 1,
+		},
+		.pfmt = {
+			.code = MEDIA_BUS_FMT_SGRBG12_1X12,
+		}
+	},
+	{
+		.vfmt.fmt.pix_mp = {
+			.width = IMG_MAX_WIDTH,
+			.height = IMG_MAX_HEIGHT,
+			.pixelformat = V4L2_PIX_FMT_MTISP_SGRBG12,
+			.num_planes = 1,
+		},
+		.pfmt = {
+			.code = MEDIA_BUS_FMT_MTISP_SGRBG12_1X12,
+		}
+	},
+	{
+		.vfmt.fmt.pix_mp = {
+			.width = IMG_MAX_WIDTH,
+			.height = IMG_MAX_HEIGHT,
+			.pixelformat = V4L2_PIX_FMT_SGRBG14,
+			.num_planes = 1,
+		},
+		.pfmt = {
+			.code = MEDIA_BUS_FMT_SGRBG14_1X14,
+		}
+	},
+	{
+		.vfmt.fmt.pix_mp = {
+			.width = IMG_MAX_WIDTH,
+			.height = IMG_MAX_HEIGHT,
+			.pixelformat = V4L2_PIX_FMT_MTISP_SGRBG14,
+			.num_planes = 1,
+		},
+		.pfmt = {
+			.code = MEDIA_BUS_FMT_MTISP_SGRBG14_1X14,
+		}
+	},
+	{
+		.vfmt.fmt.pix_mp = {
+			.width = IMG_MAX_WIDTH,
+			.height = IMG_MAX_HEIGHT,
+			.pixelformat = V4L2_PIX_FMT_SRGGB8,
+			.num_planes = 1,
+		},
+		.pfmt = {
+			.code = MEDIA_BUS_FMT_SRGGB8_1X8,
+		}
+	},
+	{
+		.vfmt.fmt.pix_mp = {
+			.width = IMG_MAX_WIDTH,
+			.height = IMG_MAX_HEIGHT,
+			.pixelformat = V4L2_PIX_FMT_SRGGB10,
+			.num_planes = 1,
+		},
+		.pfmt = {
+			.code = MEDIA_BUS_FMT_SRGGB10_1X10,
+		}
+	},
+	{
+		.vfmt.fmt.pix_mp = {
+			.width = IMG_MAX_WIDTH,
+			.height = IMG_MAX_HEIGHT,
+			.pixelformat = V4L2_PIX_FMT_MTISP_SRGGB10,
+			.num_planes = 1,
+		},
+		.pfmt = {
+			.code = MEDIA_BUS_FMT_MTISP_SRGGB10_1X10,
+		}
+	},
+	{
+		.vfmt.fmt.pix_mp = {
+			.width = IMG_MAX_WIDTH,
+			.height = IMG_MAX_HEIGHT,
+			.pixelformat = V4L2_PIX_FMT_SRGGB10P,
+		},
+	},
+
+	{
+		.vfmt.fmt.pix_mp = {
+			.width = IMG_MAX_WIDTH,
+			.height = IMG_MAX_HEIGHT,
+			.pixelformat = V4L2_PIX_FMT_SRGGB12,
+			.num_planes = 1,
+		},
+		.pfmt = {
+			.code = MEDIA_BUS_FMT_SRGGB12_1X12,
+		}
+	},
+	{
+		.vfmt.fmt.pix_mp = {
+			.width = IMG_MAX_WIDTH,
+			.height = IMG_MAX_HEIGHT,
+			.pixelformat = V4L2_PIX_FMT_MTISP_SRGGB12,
+			.num_planes = 1,
+		},
+		.pfmt = {
+			.code = MEDIA_BUS_FMT_MTISP_SRGGB12_1X12,
+		}
+	},
+	{
+		.vfmt.fmt.pix_mp = {
+			.width = IMG_MAX_WIDTH,
+			.height = IMG_MAX_HEIGHT,
+			.pixelformat = V4L2_PIX_FMT_SRGGB14,
+			.num_planes = 1,
+		},
+		.pfmt = {
+			.code = MEDIA_BUS_FMT_SRGGB14_1X14,
+		}
+	},
+	{
+		.vfmt.fmt.pix_mp = {
+			.width = IMG_MAX_WIDTH,
+			.height = IMG_MAX_HEIGHT,
+			.pixelformat = V4L2_PIX_FMT_MTISP_SRGGB14,
+			.num_planes = 1,
+		},
+		.pfmt = {
+			.code = MEDIA_BUS_FMT_MTISP_SRGGB14_1X14,
+		}
+	},
+	{
+		.vfmt.fmt.pix_mp = {
+			.width = IMG_MAX_WIDTH,
+			.height = IMG_MAX_HEIGHT,
+			.pixelformat = V4L2_PIX_FMT_SBGGR16,
+			.num_planes = 1,
+		},
+		.pfmt = {
+			.code = MEDIA_BUS_FMT_SBGGR16_1X16,
+		}
+	},
+	{
+		.vfmt.fmt.pix_mp = {
+			.width = IMG_MAX_WIDTH,
+			.height = IMG_MAX_HEIGHT,
+			.pixelformat = V4L2_PIX_FMT_SGBRG16,
+			.num_planes = 1,
+		},
+		.pfmt = {
+			.code = MEDIA_BUS_FMT_SGBRG16_1X16,
+		}
+	},
+	{
+		.vfmt.fmt.pix_mp = {
+			.width = IMG_MAX_WIDTH,
+			.height = IMG_MAX_HEIGHT,
+			.pixelformat = V4L2_PIX_FMT_SGRBG16,
+			.num_planes = 1,
+		},
+		.pfmt = {
+			.code = MEDIA_BUS_FMT_SGRBG16_1X16,
+		}
+	},
+	{
+		.vfmt.fmt.pix_mp = {
+			.width = IMG_MAX_WIDTH,
+			.height = IMG_MAX_HEIGHT,
+			.pixelformat = V4L2_PIX_FMT_SRGGB16,
+			.num_planes = 1,
+		},
+		.pfmt = {
+			.code = MEDIA_BUS_FMT_SRGGB16_1X16,
+		}
+	},
+	{
+		.vfmt.fmt.pix_mp = {
+			.width = IMG_MAX_WIDTH,
+			.height = IMG_MAX_HEIGHT,
+			.pixelformat = V4L2_PIX_FMT_MTISP_BAYER8_UFBC,
+		},
+	},
+	{
+		.vfmt.fmt.pix_mp = {
+			.width = IMG_MAX_WIDTH,
+			.height = IMG_MAX_HEIGHT,
+			.pixelformat = V4L2_PIX_FMT_MTISP_BAYER10_UFBC,
+		},
+	},
+	{
+		.vfmt.fmt.pix_mp = {
+			.width = IMG_MAX_WIDTH,
+			.height = IMG_MAX_HEIGHT,
+			.pixelformat = V4L2_PIX_FMT_MTISP_BAYER12_UFBC,
+		},
+	},
+	{
+		.vfmt.fmt.pix_mp = {
+			.width = IMG_MAX_WIDTH,
+			.height = IMG_MAX_HEIGHT,
+			.pixelformat = V4L2_PIX_FMT_MTISP_BAYER14_UFBC,
+		},
+	},
+};
+
+static const struct mtk_cam_format_desc yuv_out_group1_fmts[] = {
+	{
+		.vfmt.fmt.pix_mp = {
+			.width = YUV_GROUP1_MAX_WIDTH,
+			.height = YUV_GROUP1_MAX_HEIGHT,
+			.pixelformat = V4L2_PIX_FMT_NV12,
+		},
+	},
+	{
+		.vfmt.fmt.pix_mp = {
+			.width = YUV_GROUP1_MAX_WIDTH,
+			.height = YUV_GROUP1_MAX_HEIGHT,
+			.pixelformat = V4L2_PIX_FMT_NV21,
+		},
+	},
+	{
+		.vfmt.fmt.pix_mp = {
+			.width = YUV_GROUP1_MAX_WIDTH,
+			.height = YUV_GROUP1_MAX_HEIGHT,
+			.pixelformat = V4L2_PIX_FMT_NV12_10,
+		},
+	},
+	{
+		.vfmt.fmt.pix_mp = {
+			.width = YUV_GROUP1_MAX_WIDTH,
+			.height = YUV_GROUP1_MAX_HEIGHT,
+			.pixelformat = V4L2_PIX_FMT_NV21_10,
+		},
+	},
+	{
+		.vfmt.fmt.pix_mp = {
+			.width = YUV_GROUP1_MAX_WIDTH,
+			.height = YUV_GROUP1_MAX_HEIGHT,
+			.pixelformat = V4L2_PIX_FMT_MTISP_NV12_10P,
+		},
+	},
+	{
+		.vfmt.fmt.pix_mp = {
+			.width = YUV_GROUP1_MAX_WIDTH,
+			.height = YUV_GROUP1_MAX_HEIGHT,
+			.pixelformat = V4L2_PIX_FMT_MTISP_NV21_10P,
+		},
+	},
+	{
+		.vfmt.fmt.pix_mp = {
+			.width = YUV_GROUP1_MAX_WIDTH,
+			.height = YUV_GROUP1_MAX_HEIGHT,
+			.pixelformat = V4L2_PIX_FMT_NV12_12,
+		},
+	},
+	{
+		.vfmt.fmt.pix_mp = {
+			.width = YUV_GROUP1_MAX_WIDTH,
+			.height = YUV_GROUP1_MAX_HEIGHT,
+			.pixelformat = V4L2_PIX_FMT_NV21_12,
+		},
+	},
+	{
+		.vfmt.fmt.pix_mp = {
+			.width = YUV_GROUP1_MAX_WIDTH,
+			.height = YUV_GROUP1_MAX_HEIGHT,
+			.pixelformat = V4L2_PIX_FMT_MTISP_NV12_12P,
+		},
+	},
+	{
+		.vfmt.fmt.pix_mp = {
+			.width = YUV_GROUP1_MAX_WIDTH,
+			.height = YUV_GROUP1_MAX_HEIGHT,
+			.pixelformat = V4L2_PIX_FMT_MTISP_NV21_12P,
+		},
+	},
+	{
+		.vfmt.fmt.pix_mp = {
+			.width = YUV_GROUP1_MAX_WIDTH,
+			.height = YUV_GROUP1_MAX_HEIGHT,
+			.pixelformat = V4L2_PIX_FMT_YUV420,
+		},
+	},
+	{
+		.vfmt.fmt.pix_mp = {
+			.width = YUV_GROUP1_MAX_WIDTH,
+			.height = YUV_GROUP1_MAX_HEIGHT,
+			.pixelformat = V4L2_PIX_FMT_MTISP_NV12_UFBC,
+		},
+	},
+	{
+		.vfmt.fmt.pix_mp = {
+			.width = YUV_GROUP1_MAX_WIDTH,
+			.height = YUV_GROUP1_MAX_HEIGHT,
+			.pixelformat = V4L2_PIX_FMT_MTISP_NV21_UFBC,
+		},
+	},
+	{
+		.vfmt.fmt.pix_mp = {
+			.width = YUV_GROUP1_MAX_WIDTH,
+			.height = YUV_GROUP1_MAX_HEIGHT,
+			.pixelformat = V4L2_PIX_FMT_MTISP_NV12_10_UFBC,
+		},
+	},
+	{
+		.vfmt.fmt.pix_mp = {
+			.width = YUV_GROUP1_MAX_WIDTH,
+			.height = YUV_GROUP1_MAX_HEIGHT,
+			.pixelformat = V4L2_PIX_FMT_MTISP_NV21_10_UFBC,
+		},
+	},
+	{
+		.vfmt.fmt.pix_mp = {
+			.width = YUV_GROUP1_MAX_WIDTH,
+			.height = YUV_GROUP1_MAX_HEIGHT,
+			.pixelformat = V4L2_PIX_FMT_MTISP_NV12_12_UFBC,
+		},
+	},
+	{
+		.vfmt.fmt.pix_mp = {
+			.width = YUV_GROUP1_MAX_WIDTH,
+			.height = YUV_GROUP1_MAX_HEIGHT,
+			.pixelformat = V4L2_PIX_FMT_MTISP_NV21_12_UFBC,
+		},
+	},
+	{
+		.vfmt.fmt.pix_mp = {
+			.width = YUV_GROUP1_MAX_WIDTH,
+			.height = YUV_GROUP1_MAX_HEIGHT,
+			.pixelformat = V4L2_PIX_FMT_MTISP_SGRB8F,
+		},
+	},
+	{
+		.vfmt.fmt.pix_mp = {
+			.width = YUV_GROUP1_MAX_WIDTH,
+			.height = YUV_GROUP1_MAX_HEIGHT,
+			.pixelformat = V4L2_PIX_FMT_MTISP_SGRB10F,
+		},
+	},
+	{
+		.vfmt.fmt.pix_mp = {
+			.width = YUV_GROUP1_MAX_WIDTH,
+			.height = YUV_GROUP1_MAX_HEIGHT,
+			.pixelformat = V4L2_PIX_FMT_MTISP_SGRB12F,
+		},
+	}
+};
+
+static const struct mtk_cam_format_desc yuv_out_group2_fmts[] = {
+	{
+		.vfmt.fmt.pix_mp = {
+			.width = YUV_GROUP2_MAX_WIDTH,
+			.height = YUV_GROUP2_MAX_HEIGHT,
+			.pixelformat = V4L2_PIX_FMT_NV12,
+		},
+	},
+	{
+		.vfmt.fmt.pix_mp = {
+			.width = YUV_GROUP2_MAX_WIDTH,
+			.height = YUV_GROUP2_MAX_HEIGHT,
+			.pixelformat = V4L2_PIX_FMT_NV21,
+		},
+	},
+	{
+		.vfmt.fmt.pix_mp = {
+			.width = YUV_GROUP2_MAX_WIDTH,
+			.height = YUV_GROUP2_MAX_HEIGHT,
+			.pixelformat = V4L2_PIX_FMT_NV12_10,
+		},
+	},
+	{
+		.vfmt.fmt.pix_mp = {
+			.width = YUV_GROUP2_MAX_WIDTH,
+			.height = YUV_GROUP2_MAX_HEIGHT,
+			.pixelformat = V4L2_PIX_FMT_NV21_10,
+		},
+	},
+	{
+		.vfmt.fmt.pix_mp = {
+			.width = YUV_GROUP2_MAX_WIDTH,
+			.height = YUV_GROUP2_MAX_HEIGHT,
+			.pixelformat = V4L2_PIX_FMT_MTISP_NV12_10P,
+		},
+	},
+	{
+		.vfmt.fmt.pix_mp = {
+			.width = YUV_GROUP2_MAX_WIDTH,
+			.height = YUV_GROUP2_MAX_HEIGHT,
+			.pixelformat = V4L2_PIX_FMT_MTISP_NV21_10P,
+		},
+	},
+	{
+		.vfmt.fmt.pix_mp = {
+			.width = YUV_GROUP2_MAX_WIDTH,
+			.height = YUV_GROUP2_MAX_HEIGHT,
+			.pixelformat = V4L2_PIX_FMT_NV12_12,
+		},
+	},
+	{
+		.vfmt.fmt.pix_mp = {
+			.width = YUV_GROUP2_MAX_WIDTH,
+			.height = YUV_GROUP2_MAX_HEIGHT,
+			.pixelformat = V4L2_PIX_FMT_NV21_12,
+		},
+	},
+	{
+		.vfmt.fmt.pix_mp = {
+			.width = YUV_GROUP2_MAX_WIDTH,
+			.height = YUV_GROUP2_MAX_HEIGHT,
+			.pixelformat = V4L2_PIX_FMT_MTISP_NV12_12P,
+		},
+	},
+	{
+		.vfmt.fmt.pix_mp = {
+			.width = YUV_GROUP2_MAX_WIDTH,
+			.height = YUV_GROUP2_MAX_HEIGHT,
+			.pixelformat = V4L2_PIX_FMT_MTISP_NV21_12P,
+		},
+	}
+};
+
+static const struct mtk_cam_format_desc rzh1n2to1_out_fmts[] = {
+	{
+		.vfmt.fmt.pix_mp = {
+			.width = RZH1N2TO1_MAX_WIDTH,
+			.height = RZH1N2TO1_MAX_HEIGHT,
+			.pixelformat = V4L2_PIX_FMT_NV12,
+		},
+	},
+	{
+		.vfmt.fmt.pix_mp = {
+			.width = RZH1N2TO1_MAX_WIDTH,
+			.height = RZH1N2TO1_MAX_HEIGHT,
+			.pixelformat = V4L2_PIX_FMT_NV21,
+		},
+	},
+	{
+		.vfmt.fmt.pix_mp = {
+			.width = RZH1N2TO1_MAX_WIDTH,
+			.height = RZH1N2TO1_MAX_HEIGHT,
+			.pixelformat = V4L2_PIX_FMT_NV16,
+		},
+	},
+	{
+		.vfmt.fmt.pix_mp = {
+			.width = RZH1N2TO1_MAX_WIDTH,
+			.height = RZH1N2TO1_MAX_HEIGHT,
+			.pixelformat = V4L2_PIX_FMT_NV61,
+		},
+	}
+};
+
+static const struct mtk_cam_format_desc rzh1n2to2_out_fmts[] = {
+	{
+		.vfmt.fmt.pix_mp = {
+			.width = RZH1N2TO2_MAX_WIDTH,
+			.height = RZH1N2TO2_MAX_HEIGHT,
+			.pixelformat = V4L2_PIX_FMT_YUYV,
+		},
+	},
+	{
+		.vfmt.fmt.pix_mp = {
+			.width = RZH1N2TO2_MAX_WIDTH,
+			.height = RZH1N2TO2_MAX_HEIGHT,
+			.pixelformat = V4L2_PIX_FMT_YVYU,
+		},
+	}
+};
+
+static const struct mtk_cam_format_desc rzh1n2to3_out_fmts[] = {
+	{
+		.vfmt.fmt.pix_mp = {
+			.width = RZH1N2TO3_MAX_WIDTH,
+			.height = RZH1N2TO3_MAX_HEIGHT,
+			.pixelformat = V4L2_PIX_FMT_NV12,
+		},
+	},
+	{
+		.vfmt.fmt.pix_mp = {
+			.width = RZH1N2TO3_MAX_WIDTH,
+			.height = RZH1N2TO3_MAX_HEIGHT,
+			.pixelformat = V4L2_PIX_FMT_NV21,
+		},
+	},
+	{
+		.vfmt.fmt.pix_mp = {
+			.width = RZH1N2TO3_MAX_WIDTH,
+			.height = RZH1N2TO3_MAX_HEIGHT,
+			.pixelformat = V4L2_PIX_FMT_NV16,
+		},
+	},
+	{
+		.vfmt.fmt.pix_mp = {
+			.width = RZH1N2TO3_MAX_WIDTH,
+			.height = RZH1N2TO3_MAX_HEIGHT,
+			.pixelformat = V4L2_PIX_FMT_NV61,
+		},
+	}
+};
+
+static const struct mtk_cam_format_desc drzs4no1_out_fmts[] = {
+	{
+		.vfmt.fmt.pix_mp = {
+			.width = DRZS4NO1_MAX_WIDTH,
+			.height = DRZS4NO1_MAX_HEIGHT,
+			.pixelformat = V4L2_PIX_FMT_GREY,
+		},
+	},
+	{
+		.vfmt.fmt.pix_mp = {
+			.width = DRZS4NO1_MAX_WIDTH,
+			.height = DRZS4NO1_MAX_HEIGHT,
+			.pixelformat = V4L2_PIX_FMT_NV16,
+		},
+	},
+	{
+		.vfmt.fmt.pix_mp = {
+			.width = DRZS4NO1_MAX_WIDTH,
+			.height = DRZS4NO1_MAX_HEIGHT,
+			.pixelformat = V4L2_PIX_FMT_NV61,
+		},
+	},
+	{
+		.vfmt.fmt.pix_mp = {
+			.width = DRZS4NO1_MAX_WIDTH,
+			.height = DRZS4NO1_MAX_HEIGHT,
+			.pixelformat = V4L2_PIX_FMT_NV16_10,
+		},
+	},
+	{
+		.vfmt.fmt.pix_mp = {
+			.width = DRZS4NO1_MAX_WIDTH,
+			.height = DRZS4NO1_MAX_HEIGHT,
+			.pixelformat = V4L2_PIX_FMT_NV61_10,
+		},
+	},
+	{
+		.vfmt.fmt.pix_mp = {
+			.width = DRZS4NO1_MAX_WIDTH,
+			.height = DRZS4NO1_MAX_HEIGHT,
+			.pixelformat = V4L2_PIX_FMT_MTISP_NV16_10P,
+		},
+	},
+	{
+		.vfmt.fmt.pix_mp = {
+			.width = DRZS4NO1_MAX_WIDTH,
+			.height = DRZS4NO1_MAX_HEIGHT,
+			.pixelformat = V4L2_PIX_FMT_MTISP_NV61_10P,
+		},
+	},
+	{
+		.vfmt.fmt.pix_mp = {
+			.width = DRZS4NO1_MAX_WIDTH,
+			.height = DRZS4NO1_MAX_HEIGHT,
+			.pixelformat = V4L2_PIX_FMT_NV12,
+		},
+	},
+	{
+		.vfmt.fmt.pix_mp = {
+			.width = DRZS4NO1_MAX_WIDTH,
+			.height = DRZS4NO1_MAX_HEIGHT,
+			.pixelformat = V4L2_PIX_FMT_NV21,
+		},
+	},
+	{
+		.vfmt.fmt.pix_mp = {
+			.width = DRZS4NO1_MAX_WIDTH,
+			.height = DRZS4NO1_MAX_HEIGHT,
+			.pixelformat = V4L2_PIX_FMT_NV12_10,
+		},
+	},
+	{
+		.vfmt.fmt.pix_mp = {
+			.width = DRZS4NO1_MAX_WIDTH,
+			.height = DRZS4NO1_MAX_HEIGHT,
+			.pixelformat = V4L2_PIX_FMT_NV21_10,
+		},
+	},
+	{
+		.vfmt.fmt.pix_mp = {
+			.width = DRZS4NO1_MAX_WIDTH,
+			.height = DRZS4NO1_MAX_HEIGHT,
+			.pixelformat = V4L2_PIX_FMT_MTISP_NV12_10P,
+		},
+	},
+	{
+		.vfmt.fmt.pix_mp = {
+			.width = DRZS4NO1_MAX_WIDTH,
+			.height = DRZS4NO1_MAX_HEIGHT,
+			.pixelformat = V4L2_PIX_FMT_MTISP_NV21_10P,
+		},
+	},
+};
+
+static const struct mtk_cam_format_desc drzs4no2_out_fmts[] = {
+	{
+		.vfmt.fmt.pix_mp = {
+			.width = DRZS4NO2_MAX_WIDTH,
+			.height = DRZS4NO2_MAX_HEIGHT,
+			.pixelformat = V4L2_PIX_FMT_GREY,
+		},
+	}
+};
+
+static const struct mtk_cam_format_desc drzs4no3_out_fmts[] = {
+	{
+		.vfmt.fmt.pix_mp = {
+			.width = DRZS4NO3_MAX_WIDTH,
+			.height = DRZS4NO3_MAX_HEIGHT,
+			.pixelformat = V4L2_PIX_FMT_GREY,
+		},
+	}
+};
+
+#define MTK_RAW_TOTAL_OUTPUT_QUEUES 4
+
+static const struct
+mtk_cam_dev_node_desc output_queues[] = {
+	{
+		.id = MTK_RAW_META_IN,
+		.name = "meta input",
+		.cap = V4L2_CAP_META_OUTPUT,
+		.buf_type = V4L2_BUF_TYPE_META_OUTPUT,
+		.link_flags = MEDIA_LNK_FL_ENABLED |  MEDIA_LNK_FL_IMMUTABLE,
+		.image = false,
+		.smem_alloc = true,
+		/* .need_cache_sync_on_prepare = true, */
+		.dma_port = MTKCAM_IPI_RAW_META_STATS_CFG,
+		.default_fmt_idx = 0,
+		.max_buf_count = 16,
+		.ioctl_ops = &mtk_cam_v4l2_meta_out_ioctl_ops,
+	},
+	{
+		.id = MTK_RAW_RAWI_2_IN,
+		.name = "rawi 2",
+		.cap = V4L2_CAP_VIDEO_OUTPUT_MPLANE,
+		.buf_type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE,
+		.link_flags = MEDIA_LNK_FL_ENABLED |  MEDIA_LNK_FL_IMMUTABLE,
+		.image = true,
+		.smem_alloc = false,
+		.dma_port = MTKCAM_IPI_RAW_RAWI_2,
+		.fmts = stream_out_fmts,
+		.num_fmts = ARRAY_SIZE(stream_out_fmts),
+		.default_fmt_idx = 0,
+		.ioctl_ops = &mtk_cam_v4l2_vout_ioctl_ops,
+		.frmsizes = &(struct v4l2_frmsizeenum) {
+			.index = 0,
+			.type = V4L2_FRMSIZE_TYPE_CONTINUOUS,
+			.stepwise = {
+				.max_width = IMG_MAX_WIDTH,
+				.min_width = IMG_MIN_WIDTH,
+				.max_height = IMG_MAX_HEIGHT,
+				.min_height = IMG_MIN_HEIGHT,
+				.step_height = 1,
+				.step_width = 1,
+			},
+		},
+	},
+	{
+		.id = MTK_RAW_RAWI_3_IN,
+		.name = "rawi 3",
+		.cap = V4L2_CAP_VIDEO_OUTPUT_MPLANE,
+		.buf_type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE,
+		.link_flags = 0,
+		.image = true,
+		.smem_alloc = false,
+		.dma_port = MTKCAM_IPI_RAW_RAWI_3,
+		.fmts = stream_out_fmts,
+		.num_fmts = ARRAY_SIZE(stream_out_fmts),
+		.default_fmt_idx = 0,
+		.ioctl_ops = &mtk_cam_v4l2_vout_ioctl_ops,
+		.frmsizes = &(struct v4l2_frmsizeenum) {
+			.index = 0,
+			.type = V4L2_FRMSIZE_TYPE_CONTINUOUS,
+			.stepwise = {
+				.max_width = IMG_MAX_WIDTH,
+				.min_width = IMG_MIN_WIDTH,
+				.max_height = IMG_MAX_HEIGHT,
+				.min_height = IMG_MIN_HEIGHT,
+				.step_height = 1,
+				.step_width = 1,
+			},
+		},
+	},
+	{
+		.id = MTK_RAW_RAWI_4_IN,
+		.name = "rawi 4",
+		.cap = V4L2_CAP_VIDEO_OUTPUT_MPLANE,
+		.buf_type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE,
+		.link_flags = 0,
+		.image = true,
+		.smem_alloc = false,
+		.dma_port = MTKCAM_IPI_RAW_RAWI_3, /* align backend RAWI_3 */
+		.fmts = stream_out_fmts,
+		.num_fmts = ARRAY_SIZE(stream_out_fmts),
+		.default_fmt_idx = 0,
+		.ioctl_ops = &mtk_cam_v4l2_vout_ioctl_ops,
+		.frmsizes = &(struct v4l2_frmsizeenum) {
+			.index = 0,
+			.type = V4L2_FRMSIZE_TYPE_CONTINUOUS,
+			.stepwise = {
+				.max_width = IMG_MAX_WIDTH,
+				.min_width = IMG_MIN_WIDTH,
+				.max_height = IMG_MAX_HEIGHT,
+				.min_height = IMG_MIN_HEIGHT,
+				.step_height = 1,
+				.step_width = 1,
+			},
+		},
+	}
+};
+
+static const
+char *output_queue_names[RAW_PIPELINE_NUM][MTK_RAW_TOTAL_OUTPUT_QUEUES] = {
+	{"mtk-cam raw-0 meta-input", "mtk-cam raw-0 rawi-2",
+	 "mtk-cam raw-0 rawi-3", "mtk-cam raw-0 rawi-4"},
+
+	{"mtk-cam raw-1 meta-input", "mtk-cam raw-1 rawi-2",
+	 "mtk-cam raw-1 rawi-3", "mtk-cam raw-1 rawi-4"},
+
+	{"mtk-cam raw-2 meta-input", "mtk-cam raw-2 rawi-2",
+	 "mtk-cam raw-2 rawi-3", "mtk-cam raw-2 rawi-4"},
+};
+
+static struct mtk_cam_pad_ops source_pad_ops_default = {
+	.set_pad_fmt = mtk_raw_set_src_pad_fmt_default,
+	.set_pad_selection = mtk_raw_set_src_pad_selection_default,
+};
+
+static struct mtk_cam_pad_ops source_pad_ops_yuv = {
+	.set_pad_fmt = mtk_raw_set_src_pad_fmt_default,
+	.set_pad_selection = mtk_raw_set_src_pad_selection_yuv,
+};
+
+static struct mtk_cam_pad_ops source_pad_ops_drzs4no = {
+	.set_pad_fmt = mtk_raw_set_src_pad_fmt_default,
+	.set_pad_selection = mtk_raw_set_src_pad_selection_default,
+};
+
+static struct mtk_cam_pad_ops source_pad_ops_rzh1n2 = {
+	.set_pad_fmt = mtk_raw_set_src_pad_fmt_rzh1n2,
+	.set_pad_selection = mtk_raw_set_src_pad_selection_default,
+};
+
+#define MTK_RAW_TOTAL_CAPTURE_QUEUES 15
+static const struct
+mtk_cam_dev_node_desc capture_queues[] = {
+	{
+		.id = MTK_RAW_MAIN_STREAM_OUT,
+		.name = "imgo",
+		.cap = V4L2_CAP_VIDEO_CAPTURE_MPLANE,
+		.buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE,
+		.link_flags = MEDIA_LNK_FL_ENABLED |  MEDIA_LNK_FL_IMMUTABLE,
+		.image = true,
+		.smem_alloc = false,
+		.dma_port = MTKCAM_IPI_RAW_IMGO,
+		.fmts = stream_out_fmts,
+		.num_fmts = ARRAY_SIZE(stream_out_fmts),
+		.default_fmt_idx = 0,
+		.pad_ops = &source_pad_ops_default,
+		.ioctl_ops = &mtk_cam_v4l2_vcap_ioctl_ops,
+		.frmsizes = &(struct v4l2_frmsizeenum) {
+			.index = 0,
+			.type = V4L2_FRMSIZE_TYPE_CONTINUOUS,
+			.stepwise = {
+				.max_width = IMG_MAX_WIDTH,
+				.min_width = IMG_MIN_WIDTH,
+				.max_height = IMG_MAX_HEIGHT,
+				.min_height = IMG_MIN_HEIGHT,
+				.step_height = 1,
+				.step_width = 1,
+			},
+		},
+	},
+	{
+		.id = MTK_RAW_YUVO_1_OUT,
+		.name = "yuvo 1",
+		.cap = V4L2_CAP_VIDEO_CAPTURE_MPLANE,
+		.buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE,
+		.link_flags = MEDIA_LNK_FL_ENABLED |  MEDIA_LNK_FL_IMMUTABLE,
+		.image = true,
+		.smem_alloc = false,
+		.dma_port = MTKCAM_IPI_RAW_YUVO_1,
+		.fmts = yuv_out_group1_fmts,
+		.num_fmts = ARRAY_SIZE(yuv_out_group1_fmts),
+		.default_fmt_idx = 0,
+		.pad_ops = &source_pad_ops_yuv,
+		.ioctl_ops = &mtk_cam_v4l2_vcap_ioctl_ops,
+		.frmsizes = &(struct v4l2_frmsizeenum) {
+			.index = 0,
+			.type = V4L2_FRMSIZE_TYPE_CONTINUOUS,
+			.stepwise = {
+				.max_width = YUV_GROUP1_MAX_WIDTH,
+				.min_width = IMG_MIN_WIDTH,
+				.max_height = YUV_GROUP1_MAX_HEIGHT,
+				.min_height = IMG_MIN_HEIGHT,
+				.step_height = 1,
+				.step_width = 1,
+			},
+		},
+	},
+	{
+		.id = MTK_RAW_YUVO_2_OUT,
+		.name = "yuvo 2",
+		.cap = V4L2_CAP_VIDEO_CAPTURE_MPLANE,
+		.buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE,
+		.link_flags = MEDIA_LNK_FL_ENABLED |  MEDIA_LNK_FL_IMMUTABLE,
+		.image = true,
+		.smem_alloc = false,
+		.dma_port = MTKCAM_IPI_RAW_YUVO_2,
+		.fmts = yuv_out_group2_fmts,
+		.num_fmts = ARRAY_SIZE(yuv_out_group2_fmts),
+		.default_fmt_idx = 0,
+		.pad_ops = &source_pad_ops_yuv,
+		.ioctl_ops = &mtk_cam_v4l2_vcap_ioctl_ops,
+		.frmsizes = &(struct v4l2_frmsizeenum) {
+			.index = 0,
+			.type = V4L2_FRMSIZE_TYPE_CONTINUOUS,
+			.stepwise = {
+				.max_width = YUV_GROUP2_MAX_WIDTH,
+				.min_width = IMG_MIN_WIDTH,
+				.max_height = YUV_GROUP2_MAX_HEIGHT,
+				.min_height = IMG_MIN_HEIGHT,
+				.step_height = 1,
+				.step_width = 1,
+			},
+		},
+	},
+	{
+		.id = MTK_RAW_YUVO_3_OUT,
+		.name = "yuvo 3",
+		.cap = V4L2_CAP_VIDEO_CAPTURE_MPLANE,
+		.buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE,
+		.link_flags = MEDIA_LNK_FL_ENABLED |  MEDIA_LNK_FL_IMMUTABLE,
+		.image = true,
+		.smem_alloc = false,
+		.dma_port = MTKCAM_IPI_RAW_YUVO_3,
+		.fmts = yuv_out_group1_fmts,
+		.num_fmts = ARRAY_SIZE(yuv_out_group1_fmts),
+		.default_fmt_idx = 0,
+		.pad_ops = &source_pad_ops_yuv,
+		.ioctl_ops = &mtk_cam_v4l2_vcap_ioctl_ops,
+		.frmsizes = &(struct v4l2_frmsizeenum) {
+			.index = 0,
+			.type = V4L2_FRMSIZE_TYPE_CONTINUOUS,
+			.stepwise = {
+				.max_width = YUV_GROUP1_MAX_WIDTH,
+				.min_width = IMG_MIN_WIDTH,
+				.max_height = YUV_GROUP1_MAX_HEIGHT,
+				.min_height = IMG_MIN_HEIGHT,
+				.step_height = 1,
+				.step_width = 1,
+			},
+		},
+	},
+	{
+		.id = MTK_RAW_YUVO_4_OUT,
+		.name = "yuvo 4",
+		.cap = V4L2_CAP_VIDEO_CAPTURE_MPLANE,
+		.buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE,
+		.link_flags = MEDIA_LNK_FL_ENABLED |  MEDIA_LNK_FL_IMMUTABLE,
+		.image = true,
+		.smem_alloc = false,
+		.dma_port = MTKCAM_IPI_RAW_YUVO_4,
+		.fmts = yuv_out_group2_fmts,
+		.num_fmts = ARRAY_SIZE(yuv_out_group2_fmts),
+		.default_fmt_idx = 0,
+		.pad_ops = &source_pad_ops_yuv,
+		.ioctl_ops = &mtk_cam_v4l2_vcap_ioctl_ops,
+		.frmsizes = &(struct v4l2_frmsizeenum) {
+			.index = 0,
+			.type = V4L2_FRMSIZE_TYPE_CONTINUOUS,
+			.stepwise = {
+				.max_width = YUV_GROUP2_MAX_WIDTH,
+				.min_width = IMG_MIN_WIDTH,
+				.max_height = YUV_GROUP2_MAX_HEIGHT,
+				.min_height = IMG_MIN_HEIGHT,
+				.step_height = 1,
+				.step_width = 1,
+			},
+		},
+	},
+	{
+		.id = MTK_RAW_YUVO_5_OUT,
+		.name = "yuvo 5",
+		.cap = V4L2_CAP_VIDEO_CAPTURE_MPLANE,
+		.buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE,
+		.link_flags = MEDIA_LNK_FL_ENABLED |  MEDIA_LNK_FL_IMMUTABLE,
+		.image = true,
+		.smem_alloc = false,
+		.dma_port = MTKCAM_IPI_RAW_YUVO_5,
+		.fmts = yuv_out_group2_fmts,
+		.num_fmts = ARRAY_SIZE(yuv_out_group2_fmts),
+		.default_fmt_idx = 0,
+		.pad_ops = &source_pad_ops_yuv,
+		.ioctl_ops = &mtk_cam_v4l2_vcap_ioctl_ops,
+		.frmsizes = &(struct v4l2_frmsizeenum) {
+			.index = 0,
+			.type = V4L2_FRMSIZE_TYPE_CONTINUOUS,
+			.stepwise = {
+				.max_width = YUV_GROUP2_MAX_WIDTH,
+				.min_width = IMG_MIN_WIDTH,
+				.max_height = YUV_GROUP2_MAX_HEIGHT,
+				.min_height = IMG_MIN_HEIGHT,
+				.step_height = 1,
+				.step_width = 1,
+			},
+		},
+	},
+	{
+		.id = MTK_RAW_DRZS4NO_1_OUT,
+		.name = "drzs4no 1",
+		.cap = V4L2_CAP_VIDEO_CAPTURE_MPLANE,
+		.buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE,
+		.link_flags = MEDIA_LNK_FL_ENABLED |  MEDIA_LNK_FL_IMMUTABLE,
+		.image = true,
+		.smem_alloc = false,
+		.dma_port = MTKCAM_IPI_RAW_DRZS4NO_1,
+		.fmts = drzs4no1_out_fmts,
+		.num_fmts = ARRAY_SIZE(drzs4no1_out_fmts),
+		.default_fmt_idx = 0,
+		.pad_ops = &source_pad_ops_drzs4no,
+		.ioctl_ops = &mtk_cam_v4l2_vcap_ioctl_ops,
+		.frmsizes = &(struct v4l2_frmsizeenum) {
+			.index = 0,
+			.type = V4L2_FRMSIZE_TYPE_CONTINUOUS,
+			.stepwise = {
+				.max_width = DRZS4NO1_MAX_WIDTH,
+				.min_width = IMG_MIN_WIDTH,
+				.max_height = DRZS4NO1_MAX_HEIGHT,
+				.min_height = IMG_MIN_HEIGHT,
+				.step_height = 1,
+				.step_width = 1,
+			},
+		},
+	},
+	{
+		.id = MTK_RAW_DRZS4NO_2_OUT,
+		.name = "drzs4no 2",
+		.cap = V4L2_CAP_VIDEO_CAPTURE_MPLANE,
+		.buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE,
+		.link_flags = MEDIA_LNK_FL_ENABLED |  MEDIA_LNK_FL_IMMUTABLE,
+		.image = true,
+		.smem_alloc = false,
+		.dma_port = MTKCAM_IPI_RAW_DRZS4NO_2,
+		.fmts = drzs4no2_out_fmts,
+		.num_fmts = ARRAY_SIZE(drzs4no2_out_fmts),
+		.default_fmt_idx = 0,
+		.pad_ops = &source_pad_ops_drzs4no,
+		.ioctl_ops = &mtk_cam_v4l2_vcap_ioctl_ops,
+		.frmsizes = &(struct v4l2_frmsizeenum) {
+			.index = 0,
+			.type = V4L2_FRMSIZE_TYPE_CONTINUOUS,
+			.stepwise = {
+				.max_width = DRZS4NO2_MAX_WIDTH,
+				.min_width = IMG_MIN_WIDTH,
+				.max_height = DRZS4NO2_MAX_HEIGHT,
+				.min_height = IMG_MIN_HEIGHT,
+				.step_height = 1,
+				.step_width = 1,
+			},
+		},
+	},
+	{
+		.id = MTK_RAW_DRZS4NO_3_OUT,
+		.name = "drzs4no 3",
+		.cap = V4L2_CAP_VIDEO_CAPTURE_MPLANE,
+		.buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE,
+		.link_flags = MEDIA_LNK_FL_ENABLED |  MEDIA_LNK_FL_IMMUTABLE,
+		.image = true,
+		.smem_alloc = false,
+		.dma_port = MTKCAM_IPI_RAW_DRZS4NO_3,
+		.fmts = drzs4no3_out_fmts,
+		.num_fmts = ARRAY_SIZE(drzs4no3_out_fmts),
+		.default_fmt_idx = 0,
+		.pad_ops = &source_pad_ops_drzs4no,
+		.ioctl_ops = &mtk_cam_v4l2_vcap_ioctl_ops,
+		.frmsizes = &(struct v4l2_frmsizeenum) {
+			.index = 0,
+			.type = V4L2_FRMSIZE_TYPE_CONTINUOUS,
+			.stepwise = {
+				.max_width = DRZS4NO3_MAX_WIDTH,
+				.min_width = IMG_MIN_WIDTH,
+				.max_height = DRZS4NO3_MAX_HEIGHT,
+				.min_height = IMG_MIN_HEIGHT,
+				.step_height = 1,
+				.step_width = 1,
+			},
+		},
+	},
+	{
+		.id = MTK_RAW_RZH1N2TO_1_OUT,
+		.name = "rzh1n2to 1",
+		.cap = V4L2_CAP_VIDEO_CAPTURE_MPLANE,
+		.buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE,
+		.link_flags = MEDIA_LNK_FL_ENABLED |  MEDIA_LNK_FL_IMMUTABLE,
+		.image = true,
+		.smem_alloc = false,
+		.dma_port = MTKCAM_IPI_RAW_RZH1N2TO_1,
+		.fmts = rzh1n2to1_out_fmts,
+		.num_fmts = ARRAY_SIZE(rzh1n2to1_out_fmts),
+		.default_fmt_idx = 0,
+		.pad_ops = &source_pad_ops_rzh1n2,
+		.ioctl_ops = &mtk_cam_v4l2_vcap_ioctl_ops,
+		.frmsizes = &(struct v4l2_frmsizeenum) {
+			.index = 0,
+			.type = V4L2_FRMSIZE_TYPE_CONTINUOUS,
+			.stepwise = {
+				.max_width = RZH1N2TO1_MAX_WIDTH,
+				.min_width = IMG_MIN_WIDTH,
+				.max_height = RZH1N2TO1_MAX_HEIGHT,
+				.min_height = IMG_MIN_HEIGHT,
+				.step_height = 1,
+				.step_width = 1,
+			},
+		},
+	},
+	{
+		.id = MTK_RAW_RZH1N2TO_2_OUT,
+		.name = "rzh1n2to 2",
+		.cap = V4L2_CAP_VIDEO_CAPTURE_MPLANE,
+		.buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE,
+		.link_flags = MEDIA_LNK_FL_ENABLED |  MEDIA_LNK_FL_IMMUTABLE,
+		.image = true,
+		.smem_alloc = false,
+		.dma_port = MTKCAM_IPI_RAW_RZH1N2TO_2,
+		.fmts = rzh1n2to2_out_fmts,
+		.num_fmts = ARRAY_SIZE(rzh1n2to2_out_fmts),
+		.default_fmt_idx = 0,
+		.pad_ops = &source_pad_ops_rzh1n2,
+		.ioctl_ops = &mtk_cam_v4l2_vcap_ioctl_ops,
+		.frmsizes = &(struct v4l2_frmsizeenum) {
+			.index = 0,
+			.type = V4L2_FRMSIZE_TYPE_CONTINUOUS,
+			.stepwise = {
+				.max_width = RZH1N2TO2_MAX_WIDTH,
+				.min_width = IMG_MIN_WIDTH,
+				.max_height = RZH1N2TO2_MAX_HEIGHT,
+				.min_height = IMG_MIN_HEIGHT,
+				.step_height = 1,
+				.step_width = 1,
+			},
+		},
+	},
+	{
+		.id = MTK_RAW_RZH1N2TO_3_OUT,
+		.name = "rzh1n2to 3",
+		.cap = V4L2_CAP_VIDEO_CAPTURE_MPLANE,
+		.buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE,
+		.link_flags = MEDIA_LNK_FL_ENABLED |  MEDIA_LNK_FL_IMMUTABLE,
+		.image = true,
+		.smem_alloc = false,
+		.dma_port = MTKCAM_IPI_RAW_RZH1N2TO_3,
+		.fmts = rzh1n2to3_out_fmts,
+		.num_fmts = ARRAY_SIZE(rzh1n2to3_out_fmts),
+		.default_fmt_idx = 0,
+		.pad_ops = &source_pad_ops_rzh1n2,
+		.ioctl_ops = &mtk_cam_v4l2_vcap_ioctl_ops,
+		.frmsizes = &(struct v4l2_frmsizeenum) {
+			.index = 0,
+			.type = V4L2_FRMSIZE_TYPE_CONTINUOUS,
+			.stepwise = {
+				.max_width = RZH1N2TO3_MAX_WIDTH,
+				.min_width = IMG_MIN_WIDTH,
+				.max_height = RZH1N2TO3_MAX_HEIGHT,
+				.min_height = IMG_MIN_HEIGHT,
+				.step_height = 1,
+				.step_width = 1,
+			},
+		},
+	},
+	{
+		.id = MTK_RAW_META_OUT_0,
+		.name = "partial meta 0",
+		.cap = V4L2_CAP_META_CAPTURE,
+		.buf_type = V4L2_BUF_TYPE_META_CAPTURE,
+		.link_flags = MEDIA_LNK_FL_ENABLED |  MEDIA_LNK_FL_IMMUTABLE,
+		.image = false,
+		.smem_alloc = true,
+		.dma_port = MTKCAM_IPI_RAW_META_STATS_0,
+		.default_fmt_idx = 1,
+		.max_buf_count = 16,
+		.ioctl_ops = &mtk_cam_v4l2_meta_cap_ioctl_ops,
+	},
+	{
+		.id = MTK_RAW_META_OUT_1,
+		.name = "partial meta 1",
+		.cap = V4L2_CAP_META_CAPTURE,
+		.buf_type = V4L2_BUF_TYPE_META_CAPTURE,
+		.link_flags = MEDIA_LNK_FL_ENABLED |  MEDIA_LNK_FL_IMMUTABLE,
+		.image = false,
+		.smem_alloc = true,
+		/* .need_cache_sync_on_finish = true, */
+		.dma_port = MTKCAM_IPI_RAW_META_STATS_1,
+		.default_fmt_idx = 2,
+		.max_buf_count = 16,
+		.ioctl_ops = &mtk_cam_v4l2_meta_cap_ioctl_ops,
+	},
+	{
+		.id = MTK_RAW_META_OUT_2,
+		.name = "partial meta 2",
+		.cap = V4L2_CAP_META_CAPTURE,
+		.buf_type = V4L2_BUF_TYPE_META_CAPTURE,
+		.link_flags = MEDIA_LNK_FL_ENABLED |  MEDIA_LNK_FL_IMMUTABLE,
+		.image = false,
+		.smem_alloc = false,
+		/* .need_cache_sync_on_finish = true, */
+		.dma_port = MTKCAM_IPI_RAW_META_STATS_2,
+		.default_fmt_idx = 3,
+		.max_buf_count = 16,
+		.ioctl_ops = &mtk_cam_v4l2_meta_cap_ioctl_ops,
+	},
+};
+
+static const
+char *capture_queue_names[RAW_PIPELINE_NUM][MTK_RAW_TOTAL_CAPTURE_QUEUES] = {
+	{"mtk-cam raw-0 main-stream",
+	 "mtk-cam raw-0 yuvo-1", "mtk-cam raw-0 yuvo-2",
+	 "mtk-cam raw-0 yuvo-3", "mtk-cam raw-0 yuvo-4",
+	 "mtk-cam raw-0 yuvo-5",
+	 "mtk-cam raw-0 drzs4no-1", "mtk-cam raw-0 drzs4no-2", "mtk-cam raw-0 drzs4no-3",
+	 "mtk-cam raw-0 rzh1n2to-1", "mtk-cam raw-0 rzh1n2to-2", "mtk-cam raw-0 rzh1n2to-3",
+	 "mtk-cam raw-0 partial-meta-0", "mtk-cam raw-0 partial-meta-1",
+	 "mtk-cam raw-0 partial-meta-2",
+	 },
+	{"mtk-cam raw-1 main-stream",
+	 "mtk-cam raw-1 yuvo-1", "mtk-cam raw-1 yuvo-2",
+	 "mtk-cam raw-1 yuvo-3", "mtk-cam raw-1 yuvo-4",
+	 "mtk-cam raw-1 yuvo-5",
+	 "mtk-cam raw-1 drzs4no-1", "mtk-cam raw-1 drzs4no-2", "mtk-cam raw-1 drzs4no-3",
+	 "mtk-cam raw-1 rzh1n2to-1", "mtk-cam raw-1 rzh1n2to-2", "mtk-cam raw-1 rzh1n2to-3",
+	 "mtk-cam raw-1 partial-meta-0", "mtk-cam raw-1 partial-meta-1",
+	 "mtk-cam raw-1 partial-meta-2",
+	 },
+
+	{"mtk-cam raw-2 main-stream",
+	 "mtk-cam raw-2 yuvo-1", "mtk-cam raw-2 yuvo-2",
+	 "mtk-cam raw-2 yuvo-3", "mtk-cam raw-2 yuvo-4",
+	 "mtk-cam raw-2 yuvo-5",
+	 "mtk-cam raw-2 drzs4no-1", "mtk-cam raw-2 drzs4no-2", "mtk-cam raw-2 drzs4no-3",
+	 "mtk-cam raw-2 rzh1n2to-1", "mtk-cam raw-2 rzh1n2to-2", "mtk-cam raw-2 rzh1n2to-3",
+	 "mtk-cam raw-2 partial-meta-0", "mtk-cam raw-2 partial-meta-1",
+	 "mtk-cam raw-2 partial-meta-2",
+	 },
+};
+
+/* The helper to configure the device context */
+static void mtk_raw_pipeline_queue_setup(struct mtk_raw_pipeline *pipe)
+{
+	unsigned int node_idx, i;
+
+	if (WARN_ON(MTK_RAW_TOTAL_OUTPUT_QUEUES + MTK_RAW_TOTAL_CAPTURE_QUEUES
+	    != MTK_RAW_TOTAL_NODES))
+		return;
+
+	node_idx = 0;
+	/* Setup the output queue */
+	for (i = 0; i < MTK_RAW_TOTAL_OUTPUT_QUEUES; i++) {
+		pipe->vdev_nodes[node_idx].desc = output_queues[i];
+
+		/* set input meta format */
+		if (pipe->vdev_nodes[node_idx].desc.id == MTK_RAW_META_IN)
+			pipe->vdev_nodes[node_idx].desc.fmts =
+				camsys_get_meta_fmts();
+
+		pipe->vdev_nodes[node_idx++].desc.name =
+			output_queue_names[pipe->id][i];
+	}
+
+	/* Setup the capture queue */
+	for (i = 0; i < MTK_RAW_TOTAL_CAPTURE_QUEUES; i++) {
+		pipe->vdev_nodes[node_idx].desc = capture_queues[i];
+
+		/* set partial meta and external meta format */
+		if (pipe->vdev_nodes[node_idx].desc.id >= MTK_RAW_META_OUT_BEGIN &&
+		    pipe->vdev_nodes[node_idx].desc.id <= MTK_RAW_META_OUT_2)
+			pipe->vdev_nodes[node_idx].desc.fmts =
+				camsys_get_meta_fmts();
+
+		pipe->vdev_nodes[node_idx++].desc.name =
+			capture_queue_names[pipe->id][i];
+	}
+}
+
+static void mtk_raw_pipeline_ctrl_setup(struct mtk_raw_pipeline *pipe)
+{
+	struct v4l2_ctrl_handler *ctrl_hdlr;
+	struct v4l2_ctrl *ctrl;
+	struct device *dev = pipe->raw->devs[pipe->id];
+	int ret = 0;
+
+	ctrl_hdlr = &pipe->ctrl_handler;
+	ret = v4l2_ctrl_handler_init(ctrl_hdlr, 5);
+	if (ret) {
+		dev_info(dev, "v4l2_ctrl_handler init failed\n");
+		return;
+	}
+	mutex_init(&pipe->res_config.resource_lock);
+	mutex_init(&pipe->try_res_config.resource_lock);
+	ctrl = v4l2_ctrl_new_custom(ctrl_hdlr, &hwn_limit, NULL);
+	if (ctrl)
+		ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE |
+			       V4L2_CTRL_FLAG_EXECUTE_ON_WRITE;
+	ctrl = v4l2_ctrl_new_custom(ctrl_hdlr, &frz_limit, NULL);
+	if (ctrl)
+		ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE |
+			       V4L2_CTRL_FLAG_EXECUTE_ON_WRITE;
+	ctrl = v4l2_ctrl_new_custom(ctrl_hdlr, &bin_limit, NULL);
+	if (ctrl)
+		ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE |
+			       V4L2_CTRL_FLAG_EXECUTE_ON_WRITE;
+	ctrl = v4l2_ctrl_new_custom(ctrl_hdlr, &hwn, NULL);
+	if (ctrl)
+		ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE;
+	ctrl = v4l2_ctrl_new_custom(ctrl_hdlr, &frz, NULL);
+	if (ctrl)
+		ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE;
+	ctrl = v4l2_ctrl_new_custom(ctrl_hdlr, &bin, NULL);
+	if (ctrl)
+		ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE;
+	ctrl = v4l2_ctrl_new_custom(ctrl_hdlr, &hwn_try, NULL);
+	if (ctrl)
+		ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE;
+	ctrl = v4l2_ctrl_new_custom(ctrl_hdlr, &frz_try, NULL);
+	if (ctrl)
+		ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE;
+	ctrl = v4l2_ctrl_new_custom(ctrl_hdlr, &bin_try, NULL);
+	if (ctrl)
+		ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE;
+	v4l2_ctrl_new_custom(ctrl_hdlr, &res_plan_policy, NULL);
+	ctrl = v4l2_ctrl_new_custom(ctrl_hdlr, &res_pixel_rate, NULL);
+	if (ctrl)
+		ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE;
+	ctrl = v4l2_ctrl_new_custom(ctrl_hdlr, &frame_sync_id, NULL);
+	if (ctrl)
+		ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE |
+			       V4L2_CTRL_FLAG_EXECUTE_ON_WRITE;
+	ctrl = v4l2_ctrl_new_custom(ctrl_hdlr, &raw_path, NULL);
+	if (ctrl)
+		ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE |
+			       V4L2_CTRL_FLAG_EXECUTE_ON_WRITE;
+
+	v4l2_ctrl_new_std(ctrl_hdlr, &cam_ctrl_ops,
+			  V4L2_CID_HBLANK, 0, 65535, 1, 0);
+	v4l2_ctrl_new_std(ctrl_hdlr, &cam_ctrl_ops,
+			  V4L2_CID_VBLANK, 0, 65535, 1, 0);
+
+	/* PDE */
+	ctrl = v4l2_ctrl_new_custom(ctrl_hdlr, &cfg_pde_info, NULL);
+
+	ctrl = v4l2_ctrl_new_custom(ctrl_hdlr, &mtk_feature, NULL);
+	if (ctrl)
+		ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE |
+			V4L2_CTRL_FLAG_EXECUTE_ON_WRITE;
+
+	ctrl = v4l2_ctrl_new_custom(ctrl_hdlr, &cfg_res_update, NULL);
+	if (ctrl)
+		ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE |
+			V4L2_CTRL_FLAG_EXECUTE_ON_WRITE;
+
+	ctrl = v4l2_ctrl_new_custom(ctrl_hdlr, &cfg_res_ctrl, NULL);
+	if (ctrl)
+		ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE |
+			V4L2_CTRL_FLAG_EXECUTE_ON_WRITE;
+
+	ctrl = v4l2_ctrl_new_custom(ctrl_hdlr,
+				    &mtk_camsys_hw_mode, NULL);
+	if (ctrl)
+		ctrl->flags |= V4L2_CTRL_FLAG_EXECUTE_ON_WRITE;
+
+	pipe->res_config.hwn_limit_max = hwn_limit.def;
+	pipe->res_config.frz_limit = frz_limit.def;
+	pipe->res_config.bin_limit = bin_limit.def;
+	pipe->res_config.res_plan = res_plan_policy.def;
+	pipe->feature_pending = mtk_feature.def;
+	pipe->sync_id = frame_sync_id.def;
+	pipe->sensor_mode_update = cfg_res_update.def;
+	pipe->pde_config.pde_info.pdo_max_size = cfg_pde_info.def;
+	pipe->pde_config.pde_info.pdi_max_size = cfg_pde_info.def;
+	pipe->pde_config.pde_info.pd_table_offset = cfg_pde_info.def;
+	pipe->subdev.ctrl_handler = ctrl_hdlr;
+	pipe->hw_mode = mtk_camsys_hw_mode.def;
+}
+
+static int mtk_raw_pipeline_register(unsigned int id, struct device *dev,
+				     struct mtk_raw_pipeline *pipe,
+				     struct v4l2_device *v4l2_dev)
+{
+	struct mtk_cam_device *cam = dev_get_drvdata(pipe->raw->cam_dev);
+	struct v4l2_subdev *sd = &pipe->subdev;
+	struct mtk_cam_video_device *video;
+	unsigned int i;
+	int ret;
+
+	pipe->id = id;
+
+	/* Initialize raw_pipe subdev */
+	v4l2_subdev_init(sd, &mtk_raw_subdev_ops);
+	sd->internal_ops = &mtk_raw_subdev_internal_ops;
+	sd->entity.function = MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER;
+	sd->entity.ops = &mtk_cam_media_entity_ops;
+	sd->flags = V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS;
+	snprintf(sd->name, sizeof(sd->name),
+		 "%s-%d", dev_driver_string(dev), pipe->id);
+	v4l2_set_subdevdata(sd, pipe);
+	mtk_raw_pipeline_ctrl_setup(pipe);
+	dev_info(dev, "%s: %s\n", __func__, sd->name);
+
+	ret = v4l2_device_register_subdev(v4l2_dev, sd);
+	if (ret < 0) {
+		dev_info(dev, "Failed to register subdev: %d\n", ret);
+		return ret;
+	}
+
+	mtk_raw_pipeline_queue_setup(pipe);
+	/* setup pads of raw pipeline */
+	for (i = 0; i < ARRAY_SIZE(pipe->pads); i++) {
+		pipe->pads[i].flags = i < MTK_RAW_SOURCE_BEGIN ?
+			MEDIA_PAD_FL_SINK : MEDIA_PAD_FL_SOURCE;
+	}
+
+	media_entity_pads_init(&sd->entity, ARRAY_SIZE(pipe->pads), pipe->pads);
+
+	/* setup video node */
+	for (i = 0; i < ARRAY_SIZE(pipe->vdev_nodes); i++) {
+		video = pipe->vdev_nodes + i;
+
+		switch (pipe->id) {
+		case MTKCAM_SUBDEV_RAW_0:
+		case MTKCAM_SUBDEV_RAW_1:
+		case MTKCAM_SUBDEV_RAW_2:
+			video->uid.pipe_id = pipe->id;
+			break;
+		default:
+			dev_err(dev, "invalid pipe id\n");
+			return -EINVAL;
+		}
+
+		video->uid.id = video->desc.dma_port;
+		video->ctx = &cam->ctxs[id];
+		ret = mtk_cam_video_register(video, v4l2_dev);
+		if (ret)
+			goto fail_unregister_video;
+
+		if (V4L2_TYPE_IS_OUTPUT(video->desc.buf_type))
+			ret = media_create_pad_link(&video->vdev.entity, 0,
+						    &sd->entity,
+						    video->desc.id,
+						    video->desc.link_flags);
+		else
+			ret = media_create_pad_link(&sd->entity,
+						    video->desc.id,
+						    &video->vdev.entity, 0,
+						    video->desc.link_flags);
+
+		if (ret)
+			goto fail_unregister_video;
+	}
+
+	for (i = 0; i < sd->entity.num_pads; i++)
+		pipe->cfg[i].mbus_fmt = mfmt_default;
+
+	return 0;
+
+fail_unregister_video:
+	if (!i)
+		return ret;
+
+	for (i = i - 1; i >= 0; i--)
+		mtk_cam_video_unregister(pipe->vdev_nodes + i);
+
+	return ret;
+}
+
+static void mtk_raw_pipeline_unregister(struct mtk_raw_pipeline *pipe)
+{
+	unsigned int i;
+
+	for (i = 0; i < ARRAY_SIZE(pipe->vdev_nodes); i++)
+		mtk_cam_video_unregister(pipe->vdev_nodes + i);
+	v4l2_ctrl_handler_free(&pipe->ctrl_handler);
+	mutex_destroy(&pipe->res_config.resource_lock);
+	v4l2_device_unregister_subdev(&pipe->subdev);
+	media_entity_cleanup(&pipe->subdev.entity);
+}
+
+int mtk_cam_raw_setup_dependencies(struct mtk_raw *raw)
+{
+	struct device *dev = raw->cam_dev;
+	struct mtk_cam_device *cam_dev = dev_get_drvdata(dev);
+	struct device *consumer, *supplier;
+	struct device_link *link;
+	struct mtk_raw_device *raw_dev;
+	struct mtk_yuv_device *yuv_dev;
+	int i;
+
+	for (i = 0; i < cam_dev->num_raw_devices; i++) {
+		consumer = raw->devs[i];
+		supplier = raw->yuvs[i];
+		if (!consumer || !supplier) {
+			dev_info(dev, "failed to get raw/yuv dev for id %d\n", i);
+			continue;
+		}
+
+		raw_dev = dev_get_drvdata(consumer);
+		yuv_dev = dev_get_drvdata(supplier);
+		raw_dev->yuv_base = yuv_dev->base;
+
+		link = device_link_add(consumer, supplier,
+				       DL_FLAG_AUTOREMOVE_CONSUMER |
+				       DL_FLAG_PM_RUNTIME);
+		if (!link) {
+			dev_err(dev, "Unable to create link between %s and %s\n",
+				dev_name(consumer), dev_name(supplier));
+			return -ENODEV;
+		}
+	}
+
+	return 0;
+}
+
+int mtk_cam_raw_register_entities(struct mtk_raw *raw, struct v4l2_device *v4l2_dev)
+{
+	struct device *dev = raw->cam_dev;
+	struct mtk_cam_device *cam_dev = dev_get_drvdata(dev);
+	unsigned int i;
+	int ret;
+
+	for (i = 0; i < cam_dev->num_raw_devices; i++) {
+		struct mtk_raw_pipeline *pipe = raw->pipelines + i;
+
+		pipe->raw = raw;
+		memset(pipe->cfg, 0, sizeof(*pipe->cfg));
+		ret = mtk_raw_pipeline_register(MTKCAM_SUBDEV_RAW_0 + i,
+						raw->devs[i],
+						raw->pipelines + i, v4l2_dev);
+		if (ret)
+			return ret;
+	}
+	return 0;
+}
+
+void mtk_cam_raw_unregister_entities(struct mtk_raw *raw)
+{
+	struct device *dev = raw->cam_dev;
+	struct mtk_cam_device *cam_dev = dev_get_drvdata(dev);
+	unsigned int i;
+
+	for (i = 0; i < cam_dev->num_raw_devices; i++)
+		mtk_raw_pipeline_unregister(raw->pipelines + i);
+}
+
+static int mtk_raw_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct mtk_raw_device *raw_dev;
+	struct v4l2_subdev *sd;
+	int ret;
+
+	dev_dbg(dev, "camsys | start %s\n", __func__);
+
+	raw_dev = devm_kzalloc(dev, sizeof(*raw_dev), GFP_KERNEL);
+	if (!raw_dev)
+		return -ENOMEM;
+
+	raw_dev->dev = dev;
+	dev_set_drvdata(dev, raw_dev);
+
+	ret = mtk_raw_of_probe(pdev, raw_dev);
+	if (ret)
+		return ret;
+
+	raw_dev->fifo_size =
+		roundup_pow_of_two(8 * sizeof(struct mtk_camsys_irq_info));
+	raw_dev->msg_buffer = devm_kzalloc(dev, raw_dev->fifo_size, GFP_KERNEL);
+	if (!raw_dev->msg_buffer)
+		return -ENOMEM;
+
+	/* register raw as mtk_cam async child */
+	sd = &raw_dev->subdev;
+	v4l2_subdev_init(sd, &mtk_raw_subdev_ops);
+	sd->internal_ops = &mtk_raw_subdev_internal_ops;
+	snprintf(sd->name, sizeof(sd->name), "%s",
+		 of_node_full_name(dev->of_node));
+	sd->dev = dev;
+	sd->owner = THIS_MODULE;
+
+	ret = v4l2_async_register_subdev(sd);
+	if (ret) {
+		dev_err(dev, "%s failed on async_register_subdev\n", __func__);
+		return ret;
+	}
+	dev_dbg(dev, "%s id:%d register subdev\n", __func__, raw_dev->id);
+
+	pm_runtime_enable(dev);
+
+	dev_info(dev, "camsys | [%s] success\n", __func__);
+
+	return 0;
+}
+
+static void mtk_raw_remove(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct mtk_raw_device *raw_dev = dev_get_drvdata(dev);
+	struct v4l2_subdev *sd = &raw_dev->subdev;
+
+	dev_dbg(dev, "camsys | start %s\n", __func__);
+
+	unregister_pm_notifier(&raw_dev->pm_notifier);
+
+	pm_runtime_disable(dev);
+
+	v4l2_async_unregister_subdev(sd);
+}
+
+static int mtk_raw_runtime_suspend(struct device *dev)
+{
+	struct mtk_raw_device *drvdata = dev_get_drvdata(dev);
+
+	dev_dbg(dev, "%s:disable clock\n", __func__);
+
+	disable_irq(drvdata->irq);
+
+	mtk_cam_raw_reset(drvdata);
+
+	clk_bulk_disable_unprepare(drvdata->num_clks, drvdata->clk_b);
+
+	return 0;
+}
+
+static int mtk_raw_runtime_resume(struct device *dev)
+{
+	struct mtk_raw_device *drvdata = dev_get_drvdata(dev);
+	int ret;
+
+	/* reset_msgfifo before enable_irq */
+	ret = reset_msgfifo(drvdata);
+	if (ret)
+		return ret;
+
+	enable_irq(drvdata->irq);
+
+	dev_dbg(dev, "%s:enable clock\n", __func__);
+
+	ret = clk_bulk_prepare_enable(drvdata->num_clks, drvdata->clk_b);
+	if (ret) {
+		dev_info(dev, "failed at clk_bulk_prepare_enable, ret = %d\n", ret);
+		clk_bulk_disable_unprepare(drvdata->num_clks, drvdata->clk_b);
+		return ret;
+	}
+
+	mtk_cam_raw_reset(drvdata);
+
+	return 0;
+}
+
+static const struct dev_pm_ops mtk_raw_pm_ops = {
+	SET_RUNTIME_PM_OPS(mtk_raw_runtime_suspend, mtk_raw_runtime_resume,
+			   NULL)
+};
+
+static const struct of_device_id mtk_raw_of_ids[] = {
+	{.compatible = "mediatek,cam-raw",},
+	{}
+};
+MODULE_DEVICE_TABLE(of, mtk_raw_of_ids);
+
+struct platform_driver mtk_cam_raw_driver = {
+	.probe   = mtk_raw_probe,
+	.remove  = mtk_raw_remove,
+	.driver  = {
+		.name  = "mtk-cam raw",
+		.of_match_table = of_match_ptr(mtk_raw_of_ids),
+		.pm     = &mtk_raw_pm_ops,
+	}
+};
+
+static inline unsigned int mtk_yuv_get_err_status(unsigned int irq_status)
+{
+	return irq_status & YUV_DMA_ERR_ST;
+}
+
+static irqreturn_t mtk_irq_yuv(int irq, void *data)
+{
+	struct mtk_yuv_device *drvdata = (struct mtk_yuv_device *)data;
+	struct device *dev = drvdata->dev;
+
+	unsigned int irq_status, err_status, dma_done_status;
+	unsigned int drop_status, dma_ofl_status;
+
+	irq_status =
+		readl_relaxed(drvdata->base + REG_CTL_RAW_INT_STAT);
+	dma_done_status =
+		readl_relaxed(drvdata->base + REG_CTL_RAW_INT2_STAT);
+	drop_status =
+		readl_relaxed(drvdata->base + REG_CTL_RAW_INT4_STAT);
+	dma_ofl_status =
+		readl_relaxed(drvdata->base + REG_CTL_RAW_INT5_STAT);
+
+	err_status = mtk_yuv_get_err_status(irq_status);
+
+	if (unlikely(debug_raw))
+		dev_dbg(dev, "YUV-INT:0x%x(err:0x%x) INT2/4/5 0x%x/0x%x/0x%x\n",
+			irq_status, err_status,
+			dma_done_status, drop_status, dma_ofl_status);
+
+	return IRQ_HANDLED;
+}
+
+static int mtk_yuv_pm_suspend_prepare(struct mtk_yuv_device *dev)
+{
+	int ret;
+
+	dev_dbg(dev->dev, "- %s\n", __func__);
+
+	if (pm_runtime_suspended(dev->dev))
+		return 0;
+
+	/* Force ISP HW to idle */
+	ret = pm_runtime_force_suspend(dev->dev);
+	return ret;
+}
+
+static int mtk_yuv_pm_post_suspend(struct mtk_yuv_device *dev)
+{
+	int ret;
+
+	dev_dbg(dev->dev, "- %s\n", __func__);
+
+	if (pm_runtime_suspended(dev->dev))
+		return 0;
+
+	/* Force ISP HW to resume */
+	ret = pm_runtime_force_resume(dev->dev);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+static int yuv_pm_notifier(struct notifier_block *nb,
+			   unsigned long action, void *data)
+{
+	struct mtk_yuv_device *yuv_dev =
+			container_of(nb, struct mtk_yuv_device, pm_notifier);
+
+	switch (action) {
+	case PM_SUSPEND_PREPARE:
+		mtk_yuv_pm_suspend_prepare(yuv_dev);
+		break;
+	case PM_POST_SUSPEND:
+		mtk_yuv_pm_post_suspend(yuv_dev);
+		break;
+	default:
+		break;
+	}
+
+	return NOTIFY_OK;
+}
+
+static int mtk_yuv_of_probe(struct platform_device *pdev,
+			    struct mtk_yuv_device *drvdata)
+{
+	struct device *dev = &pdev->dev;
+	struct resource *res;
+	int irq, ret;
+	int n_clks;
+
+	ret = of_property_read_u32(dev->of_node, "mediatek,cam-id",
+				   &drvdata->id);
+	if (ret) {
+		dev_dbg(dev, "missing camid property\n");
+		return ret;
+	}
+
+	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);
+
+	/* base outer register */
+	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "base");
+	if (!res) {
+		dev_err(dev, "failed to get mem\n");
+		return -ENODEV;
+	}
+
+	drvdata->base = devm_ioremap_resource(dev, res);
+	if (IS_ERR(drvdata->base)) {
+		dev_dbg(dev, "failed to map register base\n");
+		return PTR_ERR(drvdata->base);
+	}
+
+	irq = platform_get_irq(pdev, 0);
+	if (irq < 0) {
+		dev_err(dev, "failed to get irq\n");
+		return -ENODEV;
+	}
+
+	ret = devm_request_irq(dev, irq, mtk_irq_yuv, 0,
+			       dev_name(dev), drvdata);
+	if (ret) {
+		dev_err(dev, "failed to request irq=%d\n", irq);
+		return ret;
+	}
+	dev_dbg(dev, "registered irq=%d\n", irq);
+
+	n_clks = devm_clk_bulk_get_all(dev, &drvdata->clk_b);
+	if (n_clks < 0) {
+		dev_err(dev, "failed to devm_clk_bulk_get_all=%d\n", n_clks);
+		return n_clks;
+	}
+
+	drvdata->num_clks = n_clks;
+	dev_info(dev, "clk_num:%d\n", drvdata->num_clks);
+
+#ifdef CONFIG_PM_SLEEP
+	drvdata->pm_notifier.notifier_call = yuv_pm_notifier;
+	ret = register_pm_notifier(&drvdata->pm_notifier);
+	if (ret) {
+		dev_err(dev, "failed to register notifier block.\n");
+		return ret;
+	}
+#endif
+
+	return 0;
+}
+
+static int mtk_yuv_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct mtk_yuv_device *drvdata;
+	struct v4l2_subdev *sd;
+	int ret;
+
+	dev_dbg(dev, "camsys | start %s\n", __func__);
+
+	drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL);
+	if (!drvdata)
+		return -ENOMEM;
+
+	drvdata->dev = dev;
+	dev_set_drvdata(dev, drvdata);
+
+	ret = mtk_yuv_of_probe(pdev, drvdata);
+	if (ret) {
+		dev_info(dev, "mtk_yuv_of_probe failed\n");
+		return ret;
+	}
+
+	/* register yuv as mtk_cam async child */
+	sd = &drvdata->subdev;
+	v4l2_subdev_init(sd, &mtk_raw_subdev_ops);
+	sd->internal_ops = &mtk_raw_subdev_internal_ops;
+	snprintf(sd->name, sizeof(sd->name), "%s",
+		 of_node_full_name(dev->of_node));
+	sd->dev = dev;
+	sd->owner = THIS_MODULE;
+
+	ret = v4l2_async_register_subdev(sd);
+	if (ret) {
+		dev_err(dev, "%s failed on async_register_subdev\n", __func__);
+		return ret;
+	}
+
+	pm_runtime_enable(dev);
+
+	dev_info(dev, "camsys | [%s] success\n", __func__);
+
+	return 0;
+}
+
+static void mtk_yuv_remove(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct mtk_yuv_device *yuv_dev = dev_get_drvdata(dev);
+	struct v4l2_subdev *sd = &yuv_dev->subdev;
+
+	dev_dbg(dev, "camsys | start %s\n", __func__);
+
+	unregister_pm_notifier(&yuv_dev->pm_notifier);
+
+	pm_runtime_disable(dev);
+
+	v4l2_async_unregister_subdev(sd);
+}
+
+/* driver for yuv part */
+static int mtk_yuv_runtime_suspend(struct device *dev)
+{
+	struct mtk_yuv_device *drvdata = dev_get_drvdata(dev);
+
+	dev_info(dev, "%s:disable clock\n", __func__);
+
+	clk_bulk_disable_unprepare(drvdata->num_clks, drvdata->clk_b);
+
+	return 0;
+}
+
+static int mtk_yuv_runtime_resume(struct device *dev)
+{
+	struct mtk_yuv_device *drvdata = dev_get_drvdata(dev);
+	int ret;
+
+	dev_info(dev, "%s:enable clock\n", __func__);
+
+	ret = clk_bulk_prepare_enable(drvdata->num_clks, drvdata->clk_b);
+	if (ret) {
+		dev_info(dev, "failed at clk_bulk_prepare_enable, ret = %d\n", ret);
+		clk_bulk_disable_unprepare(drvdata->num_clks, drvdata->clk_b);
+		return ret;
+	}
+
+	return 0;
+}
+
+static const struct dev_pm_ops mtk_yuv_pm_ops = {
+	SET_RUNTIME_PM_OPS(mtk_yuv_runtime_suspend, mtk_yuv_runtime_resume,
+			   NULL)
+};
+
+static const struct of_device_id mtk_yuv_of_ids[] = {
+	{.compatible = "mediatek,cam-yuv",},
+	{}
+};
+MODULE_DEVICE_TABLE(of, mtk_yuv_of_ids);
+
+struct platform_driver mtk_cam_yuv_driver = {
+	.probe   = mtk_yuv_probe,
+	.remove  = mtk_yuv_remove,
+	.driver  = {
+		.name  = "mtk-cam yuv",
+		.of_match_table = of_match_ptr(mtk_yuv_of_ids),
+		.pm     = &mtk_yuv_pm_ops,
+	}
+};
diff --git a/drivers/media/platform/mediatek/isp/isp_7x/camsys/mtk_cam-raw.h b/drivers/media/platform/mediatek/isp/isp_7x/camsys/mtk_cam-raw.h
new file mode 100644
index 000000000000..09a92df5d20a
--- /dev/null
+++ b/drivers/media/platform/mediatek/isp/isp_7x/camsys/mtk_cam-raw.h
@@ -0,0 +1,325 @@ 
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2022 MediaTek Inc.
+ */
+
+#ifndef __MTK_CAM_RAW_H
+#define __MTK_CAM_RAW_H
+
+#include <linux/kfifo.h>
+#include <media/v4l2-subdev.h>
+#include "mtk_cam-video.h"
+#include "mtk_camera-v4l2-controls.h"
+
+struct mtk_cam_request_stream_data;
+
+/* ISP7_1 control */
+#define RAW_PIPELINE_NUM 3
+
+#define SCQ_DEADLINE_MS  15 /* ~1/2 frame length */
+#define SCQ_DEFAULT_CLK_RATE 208 /* default 208MHz */
+
+#define MTK_CAMSYS_RES_STEP_NUM	8
+
+/* ISP7_1 image */
+#define IMG_MAX_WIDTH		12000
+#define IMG_MAX_HEIGHT		9000
+
+#define IMG_MIN_WIDTH		80
+#define IMG_MIN_HEIGHT		60
+#define YUV_GROUP1_MAX_WIDTH	8160
+#define YUV_GROUP1_MAX_HEIGHT	3896
+#define YUV_GROUP2_MAX_WIDTH	3060
+#define YUV_GROUP2_MAX_HEIGHT	1145
+#define YUV1_MAX_WIDTH		8160
+#define YUV1_MAX_HEIGHT		2290
+#define YUV2_MAX_WIDTH		3060
+#define YUV2_MAX_HEIGHT		1145
+#define YUV3_MAX_WIDTH		7794
+#define YUV3_MAX_HEIGHT		3896
+#define YUV4_MAX_WIDTH		1530
+#define YUV4_MAX_HEIGHT		572
+#define YUV5_MAX_WIDTH		1530
+#define YUV5_MAX_HEIGHT		572
+#define DRZS4NO1_MAX_WIDTH	2400
+#define DRZS4NO1_MAX_HEIGHT	1080
+#define DRZS4NO2_MAX_WIDTH	2400
+#define DRZS4NO2_MAX_HEIGHT	1080
+#define DRZS4NO3_MAX_WIDTH	576
+#define DRZS4NO3_MAX_HEIGHT	432
+#define RZH1N2TO1_MAX_WIDTH	1280
+#define RZH1N2TO1_MAX_HEIGHT	600
+#define RZH1N2TO2_MAX_WIDTH	512
+#define RZH1N2TO2_MAX_HEIGHT	512
+#define RZH1N2TO3_MAX_WIDTH	1280
+#define RZH1N2TO3_MAX_HEIGHT	600
+
+#define IMG_PIX_ALIGN		2
+
+enum raw_module_id {
+	RAW_A = 0,
+	RAW_B = 1,
+	RAW_C = 2,
+	RAW_NUM,
+};
+
+/* feature mask to categorize all raw functions */
+#define MTK_CAM_FEATURE_HDR_MASK		0x0000000F
+#define MTK_CAM_FEATURE_SUBSAMPLE_MASK		0x000000F0
+#define MTK_CAM_FEATURE_OFFLINE_M2M_MASK	0x00000100
+#define MTK_CAM_FEATURE_PURE_OFFLINE_M2M_MASK	0x00000200
+
+enum raw_function_id {
+	/* bit [0~3] hdr */
+	/* bit [4~7] fps */
+	/* bit [8~9] m2m */
+	OFFLINE_M2M			= (1 << 8),
+	PURE_OFFLINE_M2M		= (1 << 9),
+	RAW_FUNCTION_END		= 0xF0000000,
+};
+
+enum hardware_mode_id {
+	DEFAULT			= 0,
+	ON_THE_FLY		= 1,
+	DCIF			= 2,
+};
+
+/* enum for pads of raw pipeline */
+enum {
+	MTK_RAW_SINK_BEGIN = 0,
+	MTK_RAW_SINK = MTK_RAW_SINK_BEGIN,
+	MTK_RAW_SINK_NUM,
+	MTK_RAW_META_IN = MTK_RAW_SINK_NUM,
+	MTK_RAW_RAWI_2_IN,
+	MTK_RAW_RAWI_3_IN,
+	MTK_RAW_RAWI_4_IN,
+	MTK_RAW_SOURCE_BEGIN,
+	MTK_RAW_MAIN_STREAM_OUT = MTK_RAW_SOURCE_BEGIN,
+	MTK_RAW_YUVO_1_OUT,
+	MTK_RAW_YUVO_2_OUT,
+	MTK_RAW_YUVO_3_OUT,
+	MTK_RAW_YUVO_4_OUT,
+	MTK_RAW_YUVO_5_OUT,
+	MTK_RAW_DRZS4NO_1_OUT,
+	MTK_RAW_DRZS4NO_2_OUT,
+	MTK_RAW_DRZS4NO_3_OUT,
+	MTK_RAW_RZH1N2TO_1_OUT,
+	MTK_RAW_RZH1N2TO_2_OUT,
+	MTK_RAW_RZH1N2TO_3_OUT,
+	MTK_RAW_META_OUT_BEGIN,
+	MTK_RAW_META_OUT_0 = MTK_RAW_META_OUT_BEGIN,
+	MTK_RAW_META_OUT_1,
+	MTK_RAW_META_OUT_2,
+	MTK_RAW_PIPELINE_PADS_NUM,
+};
+
+static inline bool is_yuv_node(u32 desc_id)
+{
+	switch (desc_id) {
+	case MTK_RAW_YUVO_1_OUT:
+	case MTK_RAW_YUVO_2_OUT:
+	case MTK_RAW_YUVO_3_OUT:
+	case MTK_RAW_YUVO_4_OUT:
+	case MTK_RAW_YUVO_5_OUT:
+	case MTK_RAW_DRZS4NO_1_OUT:
+	case MTK_RAW_DRZS4NO_2_OUT:
+	case MTK_RAW_DRZS4NO_3_OUT:
+	case MTK_RAW_RZH1N2TO_1_OUT:
+	case MTK_RAW_RZH1N2TO_2_OUT:
+	case MTK_RAW_RZH1N2TO_3_OUT:
+		return true;
+	default:
+		return false;
+	}
+}
+
+/* max(pdi_table1, pdi_table2, ...) */
+#define RAW_STATS_CFG_VARIOUS_SIZE ALIGN(0x7500, SZ_1K)
+
+#define MTK_RAW_TOTAL_NODES (MTK_RAW_PIPELINE_PADS_NUM - MTK_RAW_SINK_NUM)
+
+struct mtk_cam_dev;
+struct mtk_cam_ctx;
+
+struct mtk_raw_pde_config {
+	struct mtk_cam_pde_info pde_info;
+};
+
+struct mtk_cam_resource_config {
+	struct v4l2_subdev *seninf;
+	struct mutex resource_lock; /* protect resource calculation */
+	struct v4l2_fract interval;
+	s64 pixel_rate;
+	u32 bin_limit;
+	u32 frz_limit;
+	u32 hwn_limit_max;
+	u32 hwn_limit_min;
+	s64 hblank;
+	s64 vblank;
+	s64 sensor_pixel_rate;
+	u32 res_plan;
+	u32 raw_feature;
+	u32 res_strategy[MTK_CAMSYS_RES_STEP_NUM];
+	u32 clk_target;
+	u32 raw_num_used;
+	u32 bin_enable;
+	u32 frz_enable;
+	u32 frz_ratio;
+	u32 tgo_pxl_mode;
+	u32 raw_path;
+	/* sink fmt adjusted according resource used*/
+	struct v4l2_mbus_framefmt sink_fmt;
+};
+
+struct mtk_raw_pad_config {
+	struct v4l2_mbus_framefmt mbus_fmt;
+	struct v4l2_rect crop;
+};
+
+/*
+ * struct mtk_raw_pipeline - sub dev to use raws.
+ *
+ * @feature_pending: keep the user value of S_CTRL V4L2_CID_MTK_CAM_FEATURE.
+ *		     It it safe save to be used in mtk_cam_vidioc_s_fmt,
+ *		     mtk_cam_vb2_queue_setup and mtk_cam_vb2_buf_queue
+ *		     But considering that we can't when the user calls S_CTRL,
+ *		     please use mtk_cam_request_stream_data's
+ *		     feature.raw_feature field
+ *		     to avoid the CTRL value change tming issue.
+ * @feature_active: The active feature during streaming. It can't be changed
+ *		    during streaming and can only be used after streaming on.
+ *
+ */
+struct mtk_raw_pipeline {
+	unsigned int id;
+	struct v4l2_subdev subdev;
+	struct media_pad pads[MTK_RAW_PIPELINE_PADS_NUM];
+	struct mtk_cam_video_device vdev_nodes[MTK_RAW_TOTAL_NODES];
+	struct mtk_raw *raw;
+	struct mtk_raw_pad_config cfg[MTK_RAW_PIPELINE_PADS_NUM];
+
+	/* cached settings */
+	unsigned int enabled_raw;
+	unsigned long enabled_dmas;
+	/* resource controls */
+	struct v4l2_ctrl_handler ctrl_handler;
+	s64 feature_pending;
+	s64 feature_active;
+
+	struct mtk_cam_resource user_res;
+	struct mtk_cam_resource_config res_config;
+	struct mtk_cam_resource_config try_res_config;
+	int sensor_mode_update;
+	s64 sync_id;
+	/* pde module */
+	struct mtk_raw_pde_config pde_config;
+	s64 hw_mode;
+};
+
+struct mtk_raw_device {
+	struct v4l2_subdev subdev;
+	struct device *dev;
+	struct mtk_cam_device *cam;
+	unsigned int id;
+	int irq;
+	void __iomem *base;
+	void __iomem *base_inner;
+	void __iomem *yuv_base;
+	unsigned int num_clks;
+	struct clk_bulk_data *clk_b;
+#ifdef CONFIG_PM_SLEEP
+	struct notifier_block pm_notifier;
+#endif
+
+	unsigned int	fifo_size;
+	void		*msg_buffer;
+	struct kfifo	msg_fifo;
+	atomic_t	is_fifo_overflow;
+
+	struct mtk_raw_pipeline *pipeline;
+	bool is_sub;
+
+	u64 sof_count;
+	u64 vsync_count;
+
+	atomic_t vf_en;
+	int overrun_debug_dump_cnt;
+};
+
+struct mtk_yuv_device {
+	struct v4l2_subdev subdev;
+	struct device *dev;
+	unsigned int id;
+	void __iomem *base;
+	unsigned int num_clks;
+	struct clk_bulk_data *clk_b;
+#ifdef CONFIG_PM_SLEEP
+	struct notifier_block pm_notifier;
+#endif
+};
+
+/* AE information */
+struct mtk_ae_debug_data {
+	u64 obc_r1_sum[4];
+	u64 obc_r2_sum[4];
+	u64 obc_r3_sum[4];
+	u64 aa_sum[4];
+	u64 ltm_sum[4];
+};
+
+/*
+ * struct mtk_raw - the raw device information
+ */
+struct mtk_raw {
+	struct device *cam_dev;
+	struct device *devs[RAW_PIPELINE_NUM];
+	struct device *yuvs[RAW_PIPELINE_NUM];
+	struct mtk_raw_pipeline pipelines[RAW_PIPELINE_NUM];
+};
+
+static inline struct mtk_raw_pipeline *
+mtk_cam_ctrl_handler_to_raw_pipeline(struct v4l2_ctrl_handler *handler)
+{
+	return container_of(handler, struct mtk_raw_pipeline, ctrl_handler);
+};
+
+int mtk_cam_raw_setup_dependencies(struct mtk_raw *raw);
+
+int mtk_cam_raw_register_entities(struct mtk_raw *raw,
+				  struct v4l2_device *v4l2_dev);
+void mtk_cam_raw_unregister_entities(struct mtk_raw *raw);
+
+int mtk_cam_raw_select(struct mtk_cam_ctx *ctx,
+		       struct mtkcam_ipi_input_param *cfg_in_param);
+
+void mtk_cam_raw_initialize(struct mtk_raw_device *dev, int is_sub);
+
+void mtk_cam_raw_stream_on(struct mtk_raw_device *dev, int on);
+
+void mtk_cam_raw_apply_cq(struct mtk_raw_device *dev, dma_addr_t cq_addr,
+			  unsigned int cq_size, unsigned int cq_offset,
+			  unsigned int sub_cq_size, unsigned int sub_cq_offset);
+
+void mtk_cam_raw_trigger_rawi(struct mtk_raw_device *dev,
+			      struct mtk_cam_ctx *ctx,
+			      signed int hw_scene);
+
+void mtk_cam_raw_reset(struct mtk_raw_device *dev);
+
+void mtk_cam_raw_dump_aa_info(struct mtk_cam_ctx *ctx,
+			      struct mtk_ae_debug_data *ae_info);
+
+extern struct platform_driver mtk_cam_raw_driver;
+extern struct platform_driver mtk_cam_yuv_driver;
+
+static inline u32 dmaaddr_lsb(dma_addr_t addr)
+{
+	return addr & (u32)U32_MAX;
+}
+
+static inline u32 dmaaddr_msb(dma_addr_t addr)
+{
+	return (u64)addr >> 32;
+}
+
+#endif /*__MTK_CAM_RAW_H */
diff --git a/drivers/media/platform/mediatek/isp/isp_7x/camsys/mtk_cam-raw_debug.c b/drivers/media/platform/mediatek/isp/isp_7x/camsys/mtk_cam-raw_debug.c
new file mode 100644
index 000000000000..bd81ce53b251
--- /dev/null
+++ b/drivers/media/platform/mediatek/isp/isp_7x/camsys/mtk_cam-raw_debug.c
@@ -0,0 +1,403 @@ 
+// SPDX-License-Identifier: GPL-2.0
+//
+// Copyright (c) 2022 MediaTek Inc.
+
+#include <linux/device.h>
+#include <linux/io.h>
+
+#include "mtk_cam-regs-mt8188.h"
+
+#include "mtk_cam-raw_debug.h"
+
+#define ADD_FBC_DMA(name)	 #name
+static const char * const fbc_r1_list[] = {
+	ADD_FBC_DMA(IMGO_R1),
+	ADD_FBC_DMA(FHO_R1),
+	ADD_FBC_DMA(AAHO_R1),
+	ADD_FBC_DMA(PDO_R1),
+	ADD_FBC_DMA(AAO_R1),
+	ADD_FBC_DMA(TSFSO_R1),
+	ADD_FBC_DMA(LTMSO_R1),
+	ADD_FBC_DMA(AFO_R1),
+	ADD_FBC_DMA(FLKO_R1),
+	ADD_FBC_DMA(UFEO_R1),
+	ADD_FBC_DMA(TSFSO_R2),
+};
+
+static const char * const fbc_r2_list[] = {
+	ADD_FBC_DMA(YUVO_R1),
+	ADD_FBC_DMA(YUVBO_R1),
+	ADD_FBC_DMA(YUVCO_R1),
+	ADD_FBC_DMA(YUVDO_R1),
+	ADD_FBC_DMA(YUVO_R3),
+	ADD_FBC_DMA(YUVBO_R3),
+	ADD_FBC_DMA(YUVCO_R3),
+	ADD_FBC_DMA(YUVDO_R3),
+	ADD_FBC_DMA(YUVO_R2),
+	ADD_FBC_DMA(YUVBO_R2),
+	ADD_FBC_DMA(YUVO_R4),
+	ADD_FBC_DMA(YUVBO_R4),
+	ADD_FBC_DMA(RZH1N2TO_R1),
+	ADD_FBC_DMA(RZH1N2TBO_R1),
+	ADD_FBC_DMA(RZH1N2TO_R2),
+	ADD_FBC_DMA(RZH1N2TO_R3),
+	ADD_FBC_DMA(RZH1N2TBO_R3),
+	ADD_FBC_DMA(DRZS4NO_R1),
+	ADD_FBC_DMA(DRZS4NO_R2),
+	ADD_FBC_DMA(DRZS4NO_R3),
+	ADD_FBC_DMA(TNCSO_R1),
+	ADD_FBC_DMA(TNCSYO_R1),
+	ADD_FBC_DMA(TNCSBO_R1),
+	ADD_FBC_DMA(TNCSHO_R1),
+	ADD_FBC_DMA(ACTSO_R1),
+	ADD_FBC_DMA(YUVO_R5),
+	ADD_FBC_DMA(YUVBO_R5),
+};
+
+#define LOGGER_PREFIX_SIZE 16
+#define LOGGER_BUFSIZE 128
+struct buffered_logger {
+	struct device *dev;
+	void (*log_handler)(struct buffered_logger *log);
+
+	char prefix[LOGGER_PREFIX_SIZE];
+	char buf[LOGGER_BUFSIZE + 1];
+	unsigned int size;
+};
+
+static void mtk_cam_init_logger(struct buffered_logger *logger,
+				struct device *dev,
+				void (*_hdl)(struct buffered_logger *))
+{
+	logger->dev = dev;
+	logger->log_handler = _hdl;
+	logger->prefix[0] = '\0';
+	logger->buf[LOGGER_BUFSIZE] = '\0';
+	logger->size = 0;
+}
+
+static __printf(2, 3)
+void mtk_cam_log_set_prefix(struct buffered_logger *log, const char *fmt, ...)
+{
+	va_list args;
+
+	va_start(args, fmt);
+	vscnprintf(log->prefix, LOGGER_PREFIX_SIZE, fmt, args);
+	va_end(args);
+}
+
+static void mtk_cam_log_handle_limited(struct buffered_logger *log)
+{
+	dev_info_ratelimited(log->dev, "%s: %.*s\n",
+			     log->prefix, log->size, log->buf);
+	log->size = 0;
+}
+
+static void mtk_cam_log_handle_info(struct buffered_logger *log)
+{
+	dev_info(log->dev, "%s: %.*s\n", log->prefix, log->size, log->buf);
+	log->size = 0;
+}
+
+static void mtk_cam_log_flush(struct buffered_logger *log)
+{
+	log->log_handler(log);
+}
+
+static __printf(2, 3)
+void mtk_cam_log_push(struct buffered_logger *log, const char *fmt, ...)
+{
+	va_list args;
+	int len;
+
+	va_start(args, fmt);
+	len = vscnprintf(log->buf + log->size, LOGGER_BUFSIZE - log->size + 1,
+			 fmt, args);
+	va_end(args);
+
+	if (len + log->size < LOGGER_BUFSIZE) {
+		log->size += len;
+		return;
+	}
+
+	mtk_cam_log_flush(log);
+
+	va_start(args, fmt);
+	len = vscnprintf(log->buf + log->size, LOGGER_BUFSIZE - log->size + 1,
+			 fmt, args);
+	va_end(args);
+
+	log->size += len;
+
+	if (len == LOGGER_BUFSIZE)
+		dev_info(log->dev, "log buffer size not enough: %d\n",
+			 LOGGER_BUFSIZE);
+}
+
+void mtk_cam_raw_dump_fbc(struct device *dev,
+			  void __iomem *base, void __iomem *yuvbase)
+{
+	struct buffered_logger log;
+	int fbc_r1_ctl2[ARRAY_SIZE(fbc_r1_list)];
+	int fbc_r2_ctl2[ARRAY_SIZE(fbc_r2_list)];
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(fbc_r1_list); i++)
+		fbc_r1_ctl2[i] = readl(base + REG_FBC_CTL2(FBC_R1A_BASE, i));
+
+	for (i = 0; i < ARRAY_SIZE(fbc_r2_list); i++)
+		fbc_r2_ctl2[i] = readl(yuvbase + REG_FBC_CTL2(FBC_R2A_BASE, i));
+
+	mtk_cam_init_logger(&log, dev, mtk_cam_log_handle_info);
+
+	mtk_cam_log_set_prefix(&log, "%s", "RAW FBC");
+	for (i = 0; i < ARRAY_SIZE(fbc_r1_list); i++)
+		if (fbc_r1_ctl2[i] & 0xffffff) /* if has been used */
+			mtk_cam_log_push(&log, " %s: 0x%08x",
+					 fbc_r1_list[i], fbc_r1_ctl2[i]);
+	mtk_cam_log_flush(&log);
+
+	mtk_cam_log_set_prefix(&log, "%s", "YUV FBC");
+	for (i = 0; i < ARRAY_SIZE(fbc_r2_list); i++)
+		if (fbc_r2_ctl2[i] & 0xffffff) /* if has been used */
+			mtk_cam_log_push(&log, " %s: 0x%08x",
+					 fbc_r2_list[i], fbc_r2_ctl2[i]);
+	mtk_cam_log_flush(&log);
+}
+
+struct reg_to_dump {
+	const char *name;
+	unsigned int reg;
+};
+
+#define ADD_DMA(name)	{ #name, REG_ ## name ## _BASE + DMA_OFFSET_ERR_STAT }
+static const struct reg_to_dump raw_dma_list[] = {
+	ADD_DMA(IMGO_R1),
+	ADD_DMA(IMGO_R1),
+	ADD_DMA(UFEO_R1),
+	ADD_DMA(PDO_R1),
+	ADD_DMA(FLKO_R1),
+	ADD_DMA(TSFSO_R1),
+	ADD_DMA(TSFSO_R2),
+	ADD_DMA(AAO_R1),
+	ADD_DMA(AAHO_R1),
+	ADD_DMA(AFO_R1),
+	ADD_DMA(LTMSO_R1),
+	ADD_DMA(FHO_R1),
+	/* ADD_DMA(BPCO_R1), */
+
+	ADD_DMA(RAWI_R2),
+	ADD_DMA(UFDI_R2),
+	ADD_DMA(BPCI_R1),
+	ADD_DMA(LSCI_R1),
+	ADD_DMA(AAI_R1),
+	ADD_DMA(PDI_R1),
+	ADD_DMA(BPCI_R2),
+	ADD_DMA(RAWI_R3),
+	ADD_DMA(UFDI_R3),
+	ADD_DMA(BPCI_R3),
+	/* ADD_DMA(RAWI_R4), */
+	/* ADD_DMA(BPCI_R4), */
+	/* ADD_DMA(RAWI_R5), */
+	ADD_DMA(RAWI_R6),
+	ADD_DMA(CACI_R1),
+};
+
+static const struct reg_to_dump yuv_dma_list[] = {
+	ADD_DMA(ACTSO_R1),
+	ADD_DMA(TNCSO_R1),
+	ADD_DMA(TNCSBO_R1),
+	ADD_DMA(TNCSHO_R1),
+	ADD_DMA(TNCSYO_R1),
+	ADD_DMA(DRZS4NO_R1),
+	ADD_DMA(DRZS4NO_R2),
+	ADD_DMA(DRZS4NO_R3),
+	ADD_DMA(RZH1N2TO_R1),
+	ADD_DMA(RZH1N2TBO_R1),
+	ADD_DMA(RZH1N2TO_R2),
+	ADD_DMA(RZH1N2TO_R3),
+	ADD_DMA(RZH1N2TBO_R3),
+	ADD_DMA(YUVO_R1),
+	ADD_DMA(YUVBO_R1),
+	ADD_DMA(YUVCO_R1),
+	ADD_DMA(YUVDO_R1),
+	ADD_DMA(YUVO_R2),
+	ADD_DMA(YUVBO_R2),
+	ADD_DMA(YUVO_R3),
+	ADD_DMA(YUVBO_R3),
+	ADD_DMA(YUVCO_R3),
+	ADD_DMA(YUVDO_R3),
+	ADD_DMA(YUVO_R4),
+	ADD_DMA(YUVBO_R4),
+	ADD_DMA(YUVO_R5),
+	ADD_DMA(YUVBO_R5),
+};
+
+static void mtk_cam_dump_dma_err_st(struct device *dev, void __iomem *base,
+				    const char *prefix,
+				    const struct reg_to_dump *from,
+				    const struct reg_to_dump *to)
+{
+	struct buffered_logger log;
+	int err_found = 0;
+	int err_st;
+
+	mtk_cam_init_logger(&log, dev, mtk_cam_log_handle_limited);
+	mtk_cam_log_set_prefix(&log, "%s", prefix);
+
+	while (from < to) {
+		err_st = readl_relaxed(base + from->reg);
+
+		/* [15:0] ERR_STAT */
+		if (err_st & 0xffff) {
+			mtk_cam_log_push(&log, " %s: 0x%08x",
+					 from->name, err_st);
+			err_found = 1;
+		}
+		from++;
+	}
+
+	if (err_found)
+		mtk_cam_log_flush(&log);
+}
+
+void mtk_cam_raw_dump_dma_err_st(struct device *dev, void __iomem *base)
+{
+	mtk_cam_dump_dma_err_st(dev, base, "RAW DMA_ERR",
+				raw_dma_list,
+				raw_dma_list + ARRAY_SIZE(raw_dma_list));
+}
+
+void mtk_cam_yuv_dump_dma_err_st(struct device *dev, void __iomem *base)
+{
+	mtk_cam_dump_dma_err_st(dev, base, "YUV DMA_ERR",
+				yuv_dma_list,
+				yuv_dma_list + ARRAY_SIZE(yuv_dma_list));
+}
+
+void mtk_cam_dump_req_rdy_status(struct device *dev,
+				 void __iomem *base, void __iomem *yuvbase)
+{
+	dev_dbg_ratelimited(dev,
+			    "REQ RAW/2/3 DMA/2:%08x/%08x/%08x/%08x/%08x\n",
+			    readl_relaxed(base + REG_CTL_RAW_MOD_REQ_STAT),
+			    readl_relaxed(base + REG_CTL_RAW_MOD2_REQ_STAT),
+			    readl_relaxed(base + REG_CTL_RAW_MOD3_REQ_STAT),
+			    readl_relaxed(base + REG_CTL_RAW_MOD5_REQ_STAT),
+			    readl_relaxed(base + REG_CTL_RAW_MOD6_REQ_STAT));
+	dev_dbg_ratelimited(dev,
+			    "RDY RAW/2/3 DMA/2:%08x/%08x/%08x/%08x/%08x\n",
+			    readl_relaxed(base + REG_CTL_RAW_MOD_RDY_STAT),
+			    readl_relaxed(base + REG_CTL_RAW_MOD2_RDY_STAT),
+			    readl_relaxed(base + REG_CTL_RAW_MOD3_RDY_STAT),
+			    readl_relaxed(base + REG_CTL_RAW_MOD5_RDY_STAT),
+			    readl_relaxed(base + REG_CTL_RAW_MOD6_RDY_STAT));
+	dev_dbg_ratelimited(dev,
+			    "REQ YUV/2/3/4 WDMA:%08x/%08x/%08x/%08x/%08x\n",
+			    readl_relaxed(yuvbase + REG_CTL_RAW_MOD_REQ_STAT),
+			    readl_relaxed(yuvbase + REG_CTL_RAW_MOD2_REQ_STAT),
+			    readl_relaxed(yuvbase + REG_CTL_RAW_MOD3_REQ_STAT),
+			    readl_relaxed(yuvbase + REG_CTL_RAW_MOD4_REQ_STAT),
+			    readl_relaxed(yuvbase + REG_CTL_RAW_MOD5_REQ_STAT));
+	dev_dbg_ratelimited(dev,
+			    "RDY YUV/2/3/4 WDMA:%08x/%08x/%08x/%08x/%08x\n",
+			    readl_relaxed(yuvbase + REG_CTL_RAW_MOD_RDY_STAT),
+			    readl_relaxed(yuvbase + REG_CTL_RAW_MOD2_RDY_STAT),
+			    readl_relaxed(yuvbase + REG_CTL_RAW_MOD3_RDY_STAT),
+			    readl_relaxed(yuvbase + REG_CTL_RAW_MOD4_RDY_STAT),
+			    readl_relaxed(yuvbase + REG_CTL_RAW_MOD5_RDY_STAT));
+}
+
+void mtk_cam_dump_dma_debug(struct device *dev,
+			    void __iomem *dmatop_base,
+			    const char *dma_name,
+			    struct dma_debug_item *items, int n)
+{
+	void __iomem *dbg_sel = dmatop_base + 0x70;
+	void __iomem *dbg_port = dmatop_base + 0x74;
+	int i = 0;
+	unsigned int vals[16];
+
+	if (n >= 16) {
+		dev_dbg(dev, "%s: should enlarge array size for n(%d)\n",
+			__func__, n);
+		return;
+	}
+
+	for (i = 0; i < n; i++) {
+		writel(items[i].debug_sel, dbg_sel);
+		if (readl(dbg_sel) != items[i].debug_sel)
+			dev_dbg(dev, "failed to write dbg_sel %08x\n",
+				items[i].debug_sel);
+
+		vals[i] = readl(dbg_port);
+	};
+
+	dev_dbg(dev, "%s: %s, n = %d", __func__, dma_name, n);
+	for (i = 0; i < n; i++)
+		dev_dbg(dev, "%08x: %08x [%s]\n",
+			items[i].debug_sel, vals[i], items[i].msg);
+}
+
+void mtk_cam_set_topdebug_rdyreq(struct device *dev,
+				 void __iomem *base, void __iomem *yuvbase,
+				 u32 event)
+{
+	u32 val = event << 16 | 0xa << 12;
+
+	writel(val, base + REG_CTL_DBG_SET);
+	writel(event, base + REG_CTL_DBG_SET2);
+	writel(val, yuvbase + REG_CTL_DBG_SET);
+	dev_info(dev,
+		 "set CAMCTL_DBG_SET2/CAMCTL_DBG_SET (RAW/YUV) 0x%08x/0x%08x\n",
+		 event, val);
+}
+
+void mtk_cam_dump_topdebug_rdyreq(struct device *dev,
+				  void __iomem *base, void __iomem *yuvbase)
+{
+	static const u32 debug_sel[] = {
+		/* req group 1~7 */
+		0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6,
+		/* rdy group 1~7 */
+		0x8, 0x9, 0xA, 0xB, 0xC, 0xD, 0xE,
+		/* latched_events */
+		0xF,
+	};
+	void __iomem *dbg_set;
+	void __iomem *dbg_port;
+	u32 set;
+	int i;
+
+	/* CAMCTL_DBG_SET
+	 *   CAMCTL_SNAPSHOT_SEL  [23:16]
+	 *   CAMCTL_DEBUG_TOP_SEL [15:12]
+	 *   CAMCTL_DEBUG_SEL     [11: 8]
+	 *   CAMCTL_DEBUG_MOD_SEL [ 7: 0]
+	 */
+
+	/* CAMCTL_DEBUG_MOD_SEL
+	 *   0: tg_scq_cnt_sub_pass1_done
+	 *   1: rdyreq_dbg_data
+	 */
+
+	dbg_set = base + REG_CTL_DBG_SET;
+	dbg_port = base + REG_CTL_DBG_PORT;
+
+	set = (readl(dbg_set) & 0xfff000) | 0x1;
+	for (i = 0; i < ARRAY_SIZE(debug_sel); i++) {
+		writel(set | debug_sel[i] << 8, dbg_set);
+		dev_info(dev, "RAW debug_set 0x%08x port 0x%08x\n",
+			 readl(dbg_set), readl(dbg_port));
+	}
+
+	dbg_set = yuvbase + REG_CTL_DBG_SET;
+	dbg_port = yuvbase + REG_CTL_DBG_PORT;
+
+	set = (readl(dbg_set) & 0xfff000) | 0x1;
+	for (i = 0; i < ARRAY_SIZE(debug_sel); i++) {
+		writel(set | debug_sel[i] << 8, dbg_set);
+		dev_info(dev, "YUV debug_set 0x%08x port 0x%08x\n",
+			 readl(dbg_set), readl(dbg_port));
+	}
+}
diff --git a/drivers/media/platform/mediatek/isp/isp_7x/camsys/mtk_cam-raw_debug.h b/drivers/media/platform/mediatek/isp/isp_7x/camsys/mtk_cam-raw_debug.h
new file mode 100644
index 000000000000..8d457403a5b0
--- /dev/null
+++ b/drivers/media/platform/mediatek/isp/isp_7x/camsys/mtk_cam-raw_debug.h
@@ -0,0 +1,39 @@ 
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2022 MediaTek Inc.
+ */
+
+#ifndef _MTK_CAM_RAW_DEBUG_H
+#define _MTK_CAM_RAW_DEBUG_H
+
+enum topdebug_event {
+	ALL_THE_TIME	= BIT(0),
+	TG_OVERRUN	= BIT(1),
+	CQ_MAIN_VS_ERR	= BIT(2),
+	CQ_SUB_VS_ERR	= BIT(3),
+	RAW_DMA_ERR	= BIT(4),
+	YUV_DMA_ERR	= BIT(5),
+};
+
+struct dma_debug_item {
+	unsigned int	debug_sel;
+	const char	*msg;
+};
+
+void mtk_cam_raw_dump_fbc(struct device *dev,
+			  void __iomem *base, void __iomem *yuvbase);
+void mtk_cam_raw_dump_dma_err_st(struct device *dev, void __iomem *base);
+void mtk_cam_yuv_dump_dma_err_st(struct device *dev, void __iomem *yuvbase);
+void mtk_cam_dump_req_rdy_status(struct device *dev,
+				 void __iomem *base, void __iomem *yuvbase);
+void mtk_cam_dump_dma_debug(struct device *dev,
+			    void __iomem *dmatop_base,
+			    const char *dma_name,
+			    struct dma_debug_item *items, int n);
+void mtk_cam_set_topdebug_rdyreq(struct device *dev,
+				 void __iomem *base, void __iomem *yuvbase,
+				 u32 event);
+void mtk_cam_dump_topdebug_rdyreq(struct device *dev,
+				  void __iomem *base, void __iomem *yuvbase);
+
+#endif	/* _MTK_CAM_RAW_DEBUG_H */
diff --git a/drivers/media/platform/mediatek/isp/isp_7x/camsys/mtk_camera-v4l2-controls.h b/drivers/media/platform/mediatek/isp/isp_7x/camsys/mtk_camera-v4l2-controls.h
new file mode 100644
index 000000000000..b775e6c30aa1
--- /dev/null
+++ b/drivers/media/platform/mediatek/isp/isp_7x/camsys/mtk_camera-v4l2-controls.h
@@ -0,0 +1,65 @@ 
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2022 MediaTek Inc.
+ */
+
+#ifndef __MTK_CAMERA_V4l2_CONTROLS_H
+#define __MTK_CAMERA_V4l2_CONTROLS_H
+
+#include <linux/videodev2.h>
+#include <linux/v4l2-controls.h>
+#include <linux/mtkisp_camsys.h>
+
+/* Allowed value of V4L2_CID_MTK_CAM_RAW_PATH_SELECT */
+#define V4L2_MTK_CAM_RAW_PATH_SELECT_BPC	1
+#define V4L2_MTK_CAM_RAW_PATH_SELECT_FUS	3
+#define V4L2_MTK_CAM_RAW_PATH_SELECT_DGN	4
+#define V4L2_MTK_CAM_RAW_PATH_SELECT_LSC	5
+#define V4L2_MTK_CAM_RAW_PATH_SELECT_LTM	7
+
+#define V4L2_MBUS_FRAMEFMT_PAD_ENABLE  BIT(1)
+
+#define MEDIA_BUS_FMT_MTISP_SBGGR10_1X10		0x8001
+#define MEDIA_BUS_FMT_MTISP_SBGGR12_1X12		0x8002
+#define MEDIA_BUS_FMT_MTISP_SBGGR14_1X14		0x8003
+#define MEDIA_BUS_FMT_MTISP_SGBRG10_1X10		0x8004
+#define MEDIA_BUS_FMT_MTISP_SGBRG12_1X12		0x8005
+#define MEDIA_BUS_FMT_MTISP_SGBRG14_1X14		0x8006
+#define MEDIA_BUS_FMT_MTISP_SGRBG10_1X10		0x8007
+#define MEDIA_BUS_FMT_MTISP_SGRBG12_1X12		0x8008
+#define MEDIA_BUS_FMT_MTISP_SGRBG14_1X14		0x8009
+#define MEDIA_BUS_FMT_MTISP_SRGGB10_1X10		0x800a
+#define MEDIA_BUS_FMT_MTISP_SRGGB12_1X12		0x800b
+#define MEDIA_BUS_FMT_MTISP_SRGGB14_1X14		0x800c
+#define MEDIA_BUS_FMT_MTISP_BAYER8_UFBC			0x800d
+#define MEDIA_BUS_FMT_MTISP_BAYER10_UFBC		0x800e
+#define MEDIA_BUS_FMT_MTISP_BAYER12_UFBC		0x8010
+#define MEDIA_BUS_FMT_MTISP_BAYER14_UFBC		0x8011
+#define MEDIA_BUS_FMT_MTISP_BAYER16_UFBC		0x8012
+#define MEDIA_BUS_FMT_MTISP_NV12			0x8013
+#define MEDIA_BUS_FMT_MTISP_NV21			0x8014
+#define MEDIA_BUS_FMT_MTISP_NV12_10			0x8015
+#define MEDIA_BUS_FMT_MTISP_NV21_10			0x8016
+#define MEDIA_BUS_FMT_MTISP_NV12_10P			0x8017
+#define MEDIA_BUS_FMT_MTISP_NV21_10P			0x8018
+#define MEDIA_BUS_FMT_MTISP_NV12_12			0x8019
+#define MEDIA_BUS_FMT_MTISP_NV21_12			0x801a
+#define MEDIA_BUS_FMT_MTISP_NV12_12P			0x801b
+#define MEDIA_BUS_FMT_MTISP_NV21_12P			0x801c
+#define MEDIA_BUS_FMT_MTISP_YUV420			0x801d
+#define MEDIA_BUS_FMT_MTISP_NV12_UFBC			0x801e
+#define MEDIA_BUS_FMT_MTISP_NV21_UFBC			0x8020
+#define MEDIA_BUS_FMT_MTISP_NV12_10_UFBC		0x8021
+#define MEDIA_BUS_FMT_MTISP_NV21_10_UFBC		0x8022
+#define MEDIA_BUS_FMT_MTISP_NV12_12_UFBC		0x8023
+#define MEDIA_BUS_FMT_MTISP_NV21_12_UFBC		0x8024
+#define MEDIA_BUS_FMT_MTISP_NV16			0x8025
+#define MEDIA_BUS_FMT_MTISP_NV61			0x8026
+#define MEDIA_BUS_FMT_MTISP_NV16_10			0x8027
+#define MEDIA_BUS_FMT_MTISP_NV61_10			0x8028
+#define MEDIA_BUS_FMT_MTISP_NV16_10P			0x8029
+#define MEDIA_BUS_FMT_MTISP_NV61_10P			0x802a
+
+#define MTK_CAM_RESOURCE_DEFAULT	0xFFFF
+
+#endif /* __MTK_CAMERA_V4l2_CONTROLS_H */