Linux-mediatek Archive on lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 3/4] wifi: mt76: mt7925: fix potential tx_retries underflow
From: Ryder Lee @ 2026-06-05 11:33 UTC (permalink / raw)
  To: Felix Fietkau; +Cc: linux-mediatek, linux-wireless, Shayne Chen, Ryder Lee
In-Reply-To: <20260605113306.3485554-1-ryder.lee@mediatek.com>

When FIELD_GET returns 0 for the retry count, subtracting 1 causes
an unsigned integer underflow, resulting in tx_retries becoming a
very large value (0xFFFFFFFF for u32).

Fix by checking if count is non-zero before subtracting 1.

Fixes: c948b5da6bbe ("wifi: mt76: mt7925: add Mediatek Wi-Fi7 driver for mt7925 chips")
Signed-off-by: Ryder Lee <ryder.lee@mediatek.com>
---
 drivers/net/wireless/mediatek/mt76/mt7925/mac.c | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mac.c b/drivers/net/wireless/mediatek/mt76/mt7925/mac.c
index c47bd812b..c56a9e530 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/mac.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/mac.c
@@ -1141,8 +1141,9 @@ mt7925_mac_tx_free(struct mt792x_dev *dev, void *data, int len)
 
 		if (info & MT_TXFREE_INFO_HEADER) {
 			if (wcid) {
-				wcid->stats.tx_retries +=
-					FIELD_GET(MT_TXFREE_INFO_COUNT, info) - 1;
+				u32 count = FIELD_GET(MT_TXFREE_INFO_COUNT, info);
+
+				wcid->stats.tx_retries += count ? count - 1 : 0;
 				wcid->stats.tx_failed +=
 					!!FIELD_GET(MT_TXFREE_INFO_STAT, info);
 			}
-- 
2.45.2



^ permalink raw reply related

* [PATCH 2/4] wifi: mt76: mt7921: fix potential tx_retries underflow
From: Ryder Lee @ 2026-06-05 11:33 UTC (permalink / raw)
  To: Felix Fietkau; +Cc: linux-mediatek, linux-wireless, Shayne Chen, Ryder Lee
In-Reply-To: <20260605113306.3485554-1-ryder.lee@mediatek.com>

When FIELD_GET returns 0 for the retry count, subtracting 1 causes
an unsigned integer underflow, resulting in tx_retries becoming a
very large value (0xFFFFFFFF for u32).

Fix by checking if count is non-zero before subtracting 1.

Fixes: 9aecfa754c7f ("wifi: mt76: mt7921e: report tx retries/failed counts in tx free event")
Signed-off-by: Ryder Lee <ryder.lee@mediatek.com>
---
 drivers/net/wireless/mediatek/mt76/mt7921/mac.c | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/mac.c b/drivers/net/wireless/mediatek/mt76/mt7921/mac.c
index 03b4960db..668bfa195 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7921/mac.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7921/mac.c
@@ -530,8 +530,9 @@ static void mt7921_mac_tx_free(struct mt792x_dev *dev, void *data, int len)
 		stat = FIELD_GET(MT_TX_FREE_STATUS, info);
 
 		if (wcid) {
-			wcid->stats.tx_retries +=
-				FIELD_GET(MT_TX_FREE_COUNT, info) - 1;
+			u32 count = FIELD_GET(MT_TX_FREE_COUNT, info);
+
+			wcid->stats.tx_retries += count ? count - 1 : 0;
 			wcid->stats.tx_failed += !!stat;
 		}
 
-- 
2.45.2



^ permalink raw reply related

* [PATCH 1/4] wifi: mt76: mt7915: fix potential tx_retries underflow
From: Ryder Lee @ 2026-06-05 11:33 UTC (permalink / raw)
  To: Felix Fietkau; +Cc: linux-mediatek, linux-wireless, Shayne Chen, Ryder Lee

When FIELD_GET returns 0 for the retry count, subtracting 1 causes
an unsigned integer underflow, resulting in tx_retries becoming a
very large value (0xFFFFFFFF for u32).

Fix by checking if count is non-zero before subtracting 1.

Fixes: 943e4fb96e6f ("wifi: mt76: mt7915: report tx retries/failed counts for non-WED path")
Signed-off-by: Ryder Lee <ryder.lee@mediatek.com>
---
 drivers/net/wireless/mediatek/mt76/mt7915/mac.c | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mac.c b/drivers/net/wireless/mediatek/mt76/mt7915/mac.c
index cec2c4208..334c19ab2 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7915/mac.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7915/mac.c
@@ -912,16 +912,16 @@ mt7915_mac_tx_free(struct mt7915_dev *dev, void *data, int len)
 		}
 
 		if (!mtk_wed_device_active(&mdev->mmio.wed) && wcid) {
-			u32 tx_retries = 0, tx_failed = 0;
+			u32 tx_retries = 0, tx_failed = 0, count;
 
 			if (v3 && (info & MT_TX_FREE_MPDU_HEADER_V3)) {
-				tx_retries =
-					FIELD_GET(MT_TX_FREE_COUNT_V3, info) - 1;
+				count = FIELD_GET(MT_TX_FREE_COUNT_V3, info);
+				tx_retries = count ? count - 1 : 0;
 				tx_failed = tx_retries +
 					!!FIELD_GET(MT_TX_FREE_STAT_V3, info);
 			} else if (!v3 && (info & MT_TX_FREE_MPDU_HEADER)) {
-				tx_retries =
-					FIELD_GET(MT_TX_FREE_COUNT, info) - 1;
+				count = FIELD_GET(MT_TX_FREE_COUNT, info);
+				tx_retries = count ? count - 1 : 0;
 				tx_failed = tx_retries +
 					!!FIELD_GET(MT_TX_FREE_STAT, info);
 			}
-- 
2.45.2



^ permalink raw reply related

* Re: [PATCH v6 1/4] media: dt-bindings: mediatek: Add AIE face detection support for MT8188
From: Krzysztof Kozlowski @ 2026-06-05 10:31 UTC (permalink / raw)
  To: Sarang Chaudhari, Rob Herring, AngeloGioacchino Del Regno,
	Mauro Carvalho Chehab, linux-kernel, linux-arm-kernel,
	linux-mediatek
  Cc: zhaoyuan.chen, Teddy.Chen, Project_Global_Chrome_Upstream_Group
In-Reply-To: <20260605082816.2589809-1-sarang.chaudhari@mediatek.com>

On 05/06/2026 10:28, Sarang Chaudhari wrote:
> Add YAML device tree bindings for the MediaTek AI Engine (AIE) hardware
> accelerator found in MT8188 SoCs. The AIE provides hardware-accelerated
> face detection, facial landmark detection, and face attribute analysis
> capabilities.

v6 and still not following Linux kernel process...


Please use scripts/get_maintainers.pl to get a list of necessary people
and lists to CC. It might happen, that command when run on an older
kernel, gives you outdated entries. Therefore please be sure you base
your patches on recent Linux kernel.

Tools like b4 or scripts/get_maintainer.pl provide you proper list of
people, so fix your workflow. Tools might also fail if you work on some
ancient tree (don't, instead use mainline) or work on fork of kernel
(don't, instead use mainline). Just use b4 and everything should be
fine, although remember about `b4 prep --auto-to-cc` if you added new
patches to the patchset.

You missed at least devicetree list (maybe more), so this won't be
tested by automated tooling. Performing review on untested code might be
a waste of time.

Please kindly resend and include all necessary To/Cc entries.

> +F:	Documentation/devicetree/bindings/media/mediatek,mt8188-aie.yaml
> +F:	drivers/media/platform/mediatek/aie/

There is no such directory. Apply THIS patch and test.

> +F:	include/uapi/linux/mtk_aie_v4l2_controls.h
> +
>  MEDIATEK MDP DRIVER
>  M:	Minghsiu Tsai <minghsiu.tsai@mediatek.com>
>  S:	Supported


Best regards,
Krzysztof


^ permalink raw reply

* Re: [PATCH] wifi: mt76: fix potential tx_retries underflow
From: Lorenzo Bianconi @ 2026-06-05 10:17 UTC (permalink / raw)
  To: Ryder Lee; +Cc: Felix Fietkau, linux-mediatek, linux-wireless, Shayne Chen
In-Reply-To: <20260605024222.3388222-1-ryder.lee@mediatek.com>

[-- Attachment #1: Type: text/plain, Size: 4406 bytes --]

> When FIELD_GET returns 0 for the retry count, subtracting 1 causes
> an unsigned integer underflow, resulting in tx_retries becoming a
> very large value (0xFFFFFFFF for u32 or 255 for u8).
> 
> Fix by checking if count is non-zero before subtracting 1.
> 
> Signed-off-by: Ryder Lee <ryder.lee@mediatek.com>

I guess we need proper Fixes tags here. Moreover, if you split this patch
in three separated patches you will make life easier for guys that do
backports :)

> ---
>  drivers/net/wireless/mediatek/mt76/mt7915/mac.c | 10 +++++-----
>  drivers/net/wireless/mediatek/mt76/mt7921/mac.c |  5 +++--
>  drivers/net/wireless/mediatek/mt76/mt7925/mac.c |  5 +++--
>  drivers/net/wireless/mediatek/mt76/mt7996/mac.c |  6 +++---
>  4 files changed, 14 insertions(+), 12 deletions(-)
> 
> diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mac.c b/drivers/net/wireless/mediatek/mt76/mt7915/mac.c
> index cec2c4208..334c19ab2 100644
> --- a/drivers/net/wireless/mediatek/mt76/mt7915/mac.c
> +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mac.c
> @@ -912,16 +912,16 @@ mt7915_mac_tx_free(struct mt7915_dev *dev, void *data, int len)
>  		}
>  
>  		if (!mtk_wed_device_active(&mdev->mmio.wed) && wcid) {
> -			u32 tx_retries = 0, tx_failed = 0;
> +			u32 tx_retries = 0, tx_failed = 0, count;
>  
>  			if (v3 && (info & MT_TX_FREE_MPDU_HEADER_V3)) {
> -				tx_retries =
> -					FIELD_GET(MT_TX_FREE_COUNT_V3, info) - 1;
> +				count = FIELD_GET(MT_TX_FREE_COUNT_V3, info);
> +				tx_retries = count ? count - 1 : 0;

nit: I think it is more readable if you use a int for tx_retries and do
something like:
				tx_retries = max_t(int, tx_retries, 0);

This is valid even for below chunks.

Regards,
Lorenzo

>  				tx_failed = tx_retries +
>  					!!FIELD_GET(MT_TX_FREE_STAT_V3, info);
>  			} else if (!v3 && (info & MT_TX_FREE_MPDU_HEADER)) {
> -				tx_retries =
> -					FIELD_GET(MT_TX_FREE_COUNT, info) - 1;
> +				count = FIELD_GET(MT_TX_FREE_COUNT, info);
> +				tx_retries = count ? count - 1 : 0;
>  				tx_failed = tx_retries +
>  					!!FIELD_GET(MT_TX_FREE_STAT, info);
>  			}
> diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/mac.c b/drivers/net/wireless/mediatek/mt76/mt7921/mac.c
> index 03b4960db..668bfa195 100644
> --- a/drivers/net/wireless/mediatek/mt76/mt7921/mac.c
> +++ b/drivers/net/wireless/mediatek/mt76/mt7921/mac.c
> @@ -530,8 +530,9 @@ static void mt7921_mac_tx_free(struct mt792x_dev *dev, void *data, int len)
>  		stat = FIELD_GET(MT_TX_FREE_STATUS, info);
>  
>  		if (wcid) {
> -			wcid->stats.tx_retries +=
> -				FIELD_GET(MT_TX_FREE_COUNT, info) - 1;
> +			u32 count = FIELD_GET(MT_TX_FREE_COUNT, info);
> +
> +			wcid->stats.tx_retries += count ? count - 1 : 0;
>  			wcid->stats.tx_failed += !!stat;
>  		}
>  
> diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mac.c b/drivers/net/wireless/mediatek/mt76/mt7925/mac.c
> index c47bd812b..c56a9e530 100644
> --- a/drivers/net/wireless/mediatek/mt76/mt7925/mac.c
> +++ b/drivers/net/wireless/mediatek/mt76/mt7925/mac.c
> @@ -1141,8 +1141,9 @@ mt7925_mac_tx_free(struct mt792x_dev *dev, void *data, int len)
>  
>  		if (info & MT_TXFREE_INFO_HEADER) {
>  			if (wcid) {
> -				wcid->stats.tx_retries +=
> -					FIELD_GET(MT_TXFREE_INFO_COUNT, info) - 1;
> +				u32 count = FIELD_GET(MT_TXFREE_INFO_COUNT, info);
> +
> +				wcid->stats.tx_retries += count ? count - 1 : 0;
>  				wcid->stats.tx_failed +=
>  					!!FIELD_GET(MT_TXFREE_INFO_STAT, info);
>  			}
> diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mac.c b/drivers/net/wireless/mediatek/mt76/mt7996/mac.c
> index a59c14c8f..3fad977ba 100644
> --- a/drivers/net/wireless/mediatek/mt76/mt7996/mac.c
> +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mac.c
> @@ -1361,13 +1361,13 @@ mt7996_mac_tx_free(struct mt7996_dev *dev, void *data, int len)
>  				cur_info++;
>  			continue;
>  		} else if (info & MT_TXFREE_INFO_HEADER) {
> -			u32 tx_retries = 0, tx_failed = 0;
> +			u32 tx_retries = 0, tx_failed = 0, count;
>  
>  			if (!wcid)
>  				continue;
>  
> -			tx_retries =
> -				FIELD_GET(MT_TXFREE_INFO_COUNT, info) - 1;
> +			count = FIELD_GET(MT_TXFREE_INFO_COUNT, info);
> +			tx_retries = count ? count - 1 : 0;
>  			tx_failed = tx_retries +
>  				!!FIELD_GET(MT_TXFREE_INFO_STAT, info);
>  
> -- 
> 2.45.2
> 
> 

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]

^ permalink raw reply

* Re: [PATCH] pinctrl: Move Airoha driver to dedicated directory
From: Lorenzo Bianconi @ 2026-06-05  9:49 UTC (permalink / raw)
  To: Christian Marangi
  Cc: Linus Walleij, Sean Wang, Matthias Brugger,
	AngeloGioacchino Del Regno, linux-kernel, linux-gpio,
	linux-mediatek, linux-arm-kernel
In-Reply-To: <20260605071233.28873-1-ansuelsmth@gmail.com>

[-- Attachment #1: Type: text/plain, Size: 5747 bytes --]

> In preparation for additional SoC support, move the Airoha pinctrl driver
> for AN7581 SoC to a dedicated directory.
> 
> This is to tidy things up and keep code organized without polluting the
> Mediatek driver directory.
> 
> The driver doesn't depend on any generic or common code from the Mediatek
> codebase so it can be safely moved without any modification.
> 
> Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>

Acked-by: Lorenzo Bianconi <lorenzo@kernel.org>

> ---
>  MAINTAINERS                                   |  2 +-
>  drivers/pinctrl/Kconfig                       |  1 +
>  drivers/pinctrl/Makefile                      |  1 +
>  drivers/pinctrl/airoha/Kconfig                | 20 +++++++++++++++++++
>  drivers/pinctrl/airoha/Makefile               |  3 +++
>  .../{mediatek => airoha}/pinctrl-airoha.c     |  0
>  drivers/pinctrl/mediatek/Kconfig              | 17 +---------------
>  drivers/pinctrl/mediatek/Makefile             |  1 -
>  8 files changed, 27 insertions(+), 18 deletions(-)
>  create mode 100644 drivers/pinctrl/airoha/Kconfig
>  create mode 100644 drivers/pinctrl/airoha/Makefile
>  rename drivers/pinctrl/{mediatek => airoha}/pinctrl-airoha.c (100%)
> 
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 21c0ef0b9ce5..38bf92149a15 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -21024,7 +21024,7 @@ M:	Lorenzo Bianconi <lorenzo@kernel.org>
>  L:	linux-mediatek@lists.infradead.org (moderated for non-subscribers)
>  S:	Maintained
>  F:	Documentation/devicetree/bindings/pinctrl/airoha,en7581-pinctrl.yaml
> -F:	drivers/pinctrl/mediatek/pinctrl-airoha.c
> +F:	drivers/pinctrl/airoha/pinctrl-airoha.c
>  
>  PIN CONTROLLER - AMD
>  M:	Basavaraj Natikar <Basavaraj.Natikar@amd.com>
> diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig
> index 03f2e3ee065f..e0babad31445 100644
> --- a/drivers/pinctrl/Kconfig
> +++ b/drivers/pinctrl/Kconfig
> @@ -679,6 +679,7 @@ config PINCTRL_RP1
>  	  multi function device.
>  
>  source "drivers/pinctrl/actions/Kconfig"
> +source "drivers/pinctrl/airoha/Kconfig"
>  source "drivers/pinctrl/aspeed/Kconfig"
>  source "drivers/pinctrl/bcm/Kconfig"
>  source "drivers/pinctrl/berlin/Kconfig"
> diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile
> index f7d5d5f76d0c..36c55858801f 100644
> --- a/drivers/pinctrl/Makefile
> +++ b/drivers/pinctrl/Makefile
> @@ -66,6 +66,7 @@ obj-$(CONFIG_PINCTRL_ZYNQMP)	+= pinctrl-zynqmp.o
>  obj-$(CONFIG_PINCTRL_ZYNQ)	+= pinctrl-zynq.o
>  
>  obj-y				+= actions/
> +obj-y				+= airoha/
>  obj-$(CONFIG_ARCH_ASPEED)	+= aspeed/
>  obj-y				+= bcm/
>  obj-$(CONFIG_PINCTRL_BERLIN)	+= berlin/
> diff --git a/drivers/pinctrl/airoha/Kconfig b/drivers/pinctrl/airoha/Kconfig
> new file mode 100644
> index 000000000000..03adaeae8fc3
> --- /dev/null
> +++ b/drivers/pinctrl/airoha/Kconfig
> @@ -0,0 +1,20 @@
> +# SPDX-License-Identifier: GPL-2.0-only
> +menu "Airoha pinctrl drivers"
> +	depends on ARCH_AIROHA || COMPILE_TEST
> +
> +config PINCTRL_AIROHA
> +	tristate "Airoha EN7581 pin control"
> +	depends on OF
> +	depends on ARM64 || COMPILE_TEST
> +	select PINMUX
> +	select GENERIC_PINCONF
> +	select GENERIC_PINCTRL_GROUPS
> +	select GENERIC_PINMUX_FUNCTIONS
> +	select GPIOLIB
> +	select GPIOLIB_IRQCHIP
> +	select REGMAP_MMIO
> +	help
> +	  Say yes here to support pin controller and gpio driver
> +	  on Airoha EN7581 SoC.
> +
> +endmenu
> diff --git a/drivers/pinctrl/airoha/Makefile b/drivers/pinctrl/airoha/Makefile
> new file mode 100644
> index 000000000000..a25b744dd7a8
> --- /dev/null
> +++ b/drivers/pinctrl/airoha/Makefile
> @@ -0,0 +1,3 @@
> +# SPDX-License-Identifier: GPL-2.0
> +
> +obj-$(CONFIG_PINCTRL_AIROHA)		+= pinctrl-airoha.o
> diff --git a/drivers/pinctrl/mediatek/pinctrl-airoha.c b/drivers/pinctrl/airoha/pinctrl-airoha.c
> similarity index 100%
> rename from drivers/pinctrl/mediatek/pinctrl-airoha.c
> rename to drivers/pinctrl/airoha/pinctrl-airoha.c
> diff --git a/drivers/pinctrl/mediatek/Kconfig b/drivers/pinctrl/mediatek/Kconfig
> index 4819617d9368..97980cc28b9c 100644
> --- a/drivers/pinctrl/mediatek/Kconfig
> +++ b/drivers/pinctrl/mediatek/Kconfig
> @@ -1,6 +1,6 @@
>  # SPDX-License-Identifier: GPL-2.0-only
>  menu "MediaTek pinctrl drivers"
> -	depends on ARCH_MEDIATEK || ARCH_AIROHA || RALINK || COMPILE_TEST
> +	depends on ARCH_MEDIATEK || RALINK || COMPILE_TEST
>  
>  config EINT_MTK
>  	tristate "MediaTek External Interrupt Support"
> @@ -126,21 +126,6 @@ config PINCTRL_MT8127
>  	select PINCTRL_MTK
>  
>  # For ARMv8 SoCs
> -config PINCTRL_AIROHA
> -	tristate "Airoha EN7581 pin control"
> -	depends on OF
> -	depends on ARM64 || COMPILE_TEST
> -	select PINMUX
> -	select GENERIC_PINCONF
> -	select GENERIC_PINCTRL_GROUPS
> -	select GENERIC_PINMUX_FUNCTIONS
> -	select GPIOLIB
> -	select GPIOLIB_IRQCHIP
> -	select REGMAP_MMIO
> -	help
> -	  Say yes here to support pin controller and gpio driver
> -	  on Airoha EN7581 SoC.
> -
>  config PINCTRL_MT2712
>  	bool "MediaTek MT2712 pin control"
>  	depends on OF
> diff --git a/drivers/pinctrl/mediatek/Makefile b/drivers/pinctrl/mediatek/Makefile
> index ae765bd99965..6dc17b0c23f9 100644
> --- a/drivers/pinctrl/mediatek/Makefile
> +++ b/drivers/pinctrl/mediatek/Makefile
> @@ -8,7 +8,6 @@ obj-$(CONFIG_PINCTRL_MTK_MOORE)		+= pinctrl-moore.o
>  obj-$(CONFIG_PINCTRL_MTK_PARIS)		+= pinctrl-paris.o
>  
>  # SoC Drivers
> -obj-$(CONFIG_PINCTRL_AIROHA)		+= pinctrl-airoha.o
>  obj-$(CONFIG_PINCTRL_MT7620)		+= pinctrl-mt7620.o
>  obj-$(CONFIG_PINCTRL_MT7621)		+= pinctrl-mt7621.o
>  obj-$(CONFIG_PINCTRL_MT76X8)		+= pinctrl-mt76x8.o
> -- 
> 2.53.0
> 

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]

^ permalink raw reply

* [PATCH v7 5/6] media: mediatek: encoder: Add support for VCP encode process
From: Irui Wang @ 2026-06-05  9:35 UTC (permalink / raw)
  To: Hans Verkuil, Mauro Carvalho Chehab, Rob Herring,
	Matthias Brugger, Krzysztof Kozlowski, angelogioacchino.delregno,
	nicolas.dufresne, Tiffany Lin, kyrie wu
  Cc: Yunfei Dong, Maoguang Meng, Longfei Wang, Irui Wang,
	Project_Global_Chrome_Upstream_Group, linux-media, devicetree,
	linux-kernel, linux-arm-kernel, linux-mediatek
In-Reply-To: <20260605093519.13695-1-irui.wang@mediatek.com>

Adapt the encoder driver to support VCP firmware interface.

Use platform data fw_type to identify VCP firmware and perform
VCP-specific operations:
- Allocate RC buffers using the VCP device
- Send shared memory address to VCP firmware
- Map the encoder VSI address to the CPU address space using the
VCP shared memory address.

Signed-off-by: Irui Wang <irui.wang@mediatek.com>
---
 .../mediatek/vcodec/common/mtk_vcodec_fw.c    |  6 +++++
 .../mediatek/vcodec/common/mtk_vcodec_fw.h    |  1 +
 .../vcodec/common/mtk_vcodec_fw_priv.h        |  1 +
 .../vcodec/common/mtk_vcodec_fw_vcp.c         |  6 +++++
 .../vcodec/encoder/venc/venc_common_if.c      | 22 ++++++++++++++-----
 .../mediatek/vcodec/encoder/venc_vpu_if.c     | 16 ++++++++++++--
 6 files changed, 44 insertions(+), 8 deletions(-)

diff --git a/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw.c b/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw.c
index 9df64200d933..7619ccd1f538 100644
--- a/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw.c
+++ b/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw.c
@@ -90,3 +90,9 @@ int mtk_vcodec_fw_get_type(struct mtk_vcodec_fw *fw)
 	return fw->type;
 }
 EXPORT_SYMBOL_GPL(mtk_vcodec_fw_get_type);
+
+struct device *mtk_vcodec_fw_get_dev(struct mtk_vcodec_fw *fw)
+{
+	return fw->ops->get_fw_dev(fw);
+}
+EXPORT_SYMBOL_GPL(mtk_vcodec_fw_get_dev);
diff --git a/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw.h b/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw.h
index 142e2e87905c..8ff6fcc114e3 100644
--- a/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw.h
+++ b/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw.h
@@ -42,5 +42,6 @@ int mtk_vcodec_fw_ipi_send(struct mtk_vcodec_fw *fw, int id,
 			   void *buf, unsigned int len, unsigned int wait);
 int mtk_vcodec_fw_get_type(struct mtk_vcodec_fw *fw);
 int mtk_vcodec_fw_get_ipi(enum mtk_vcodec_fw_type type, int hw_id);
+struct device *mtk_vcodec_fw_get_dev(struct mtk_vcodec_fw *fw);
 
 #endif /* _MTK_VCODEC_FW_H_ */
diff --git a/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw_priv.h b/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw_priv.h
index 0a2a9b010244..710c83c871f4 100644
--- a/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw_priv.h
+++ b/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw_priv.h
@@ -29,6 +29,7 @@ struct mtk_vcodec_fw_ops {
 	int (*ipi_send)(struct mtk_vcodec_fw *fw, int id, void *buf,
 			unsigned int len, unsigned int wait);
 	void (*release)(struct mtk_vcodec_fw *fw);
+	struct device *(*get_fw_dev)(struct mtk_vcodec_fw *fw);
 };
 
 #if IS_ENABLED(CONFIG_VIDEO_MEDIATEK_VCODEC_VPU)
diff --git a/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw_vcp.c b/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw_vcp.c
index 061a61bda33f..72627fef0ac5 100644
--- a/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw_vcp.c
+++ b/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw_vcp.c
@@ -494,6 +494,11 @@ static void mtk_vcodec_vcp_release(struct mtk_vcodec_fw *fw)
 	fw->vcp->is_register_done = false;
 }
 
+static struct device *mtk_vcodec_vcp_get_fw_dev(struct mtk_vcodec_fw *fw)
+{
+	return fw->vcp->vcp_device->dev;
+}
+
 static const struct mtk_vcodec_fw_ops mtk_vcodec_vcp_msg = {
 	.load_firmware = mtk_vcodec_vcp_load_firmware,
 	.get_vdec_capa = mtk_vcodec_vcp_get_vdec_capa,
@@ -501,6 +506,7 @@ static const struct mtk_vcodec_fw_ops mtk_vcodec_vcp_msg = {
 	.ipi_register = mtk_vcodec_vcp_set_ipi_register,
 	.ipi_send = mtk_vcodec_vcp_ipi_send,
 	.release = mtk_vcodec_vcp_release,
+	.get_fw_dev = mtk_vcodec_vcp_get_fw_dev,
 };
 
 struct mtk_vcodec_fw *mtk_vcodec_fw_vcp_init(void *priv, enum mtk_vcodec_fw_use fw_use)
diff --git a/drivers/media/platform/mediatek/vcodec/encoder/venc/venc_common_if.c b/drivers/media/platform/mediatek/vcodec/encoder/venc/venc_common_if.c
index 0efb13aef8d6..5ee138f0b2e7 100644
--- a/drivers/media/platform/mediatek/vcodec/encoder/venc/venc_common_if.c
+++ b/drivers/media/platform/mediatek/vcodec/encoder/venc/venc_common_if.c
@@ -481,7 +481,11 @@ static void venc_free_rc_buf(struct venc_inst *inst,
 	int i;
 	struct device *dev;
 
-	dev = &inst->ctx->dev->plat_dev->dev;
+	if (inst->ctx->dev->venc_pdata->fw_type == VCP)
+		dev = mtk_vcodec_fw_get_dev(inst->ctx->dev->fw_handler);
+	else
+		dev = &inst->ctx->dev->plat_dev->dev;
+
 	mtk_venc_mem_free(inst, dev, &bufs->rc_code);
 
 	for (i = 0; i < core_num; i++)
@@ -530,12 +534,18 @@ static int venc_alloc_rc_buf(struct venc_inst *inst,
 	struct device *dev;
 	void *tmp_va;
 
-	dev = &inst->ctx->dev->plat_dev->dev;
-	if (mtk_venc_mem_alloc(inst, dev, &bufs->rc_code))
-		return -ENOMEM;
+	if (inst->ctx->dev->venc_pdata->fw_type == VCP) {
+		dev = mtk_vcodec_fw_get_dev(fw);
+		if (mtk_venc_mem_alloc(inst, dev, &bufs->rc_code))
+			return -ENOMEM;
+	} else {
+		dev = &inst->ctx->dev->plat_dev->dev;
+		if (mtk_venc_mem_alloc(inst, dev, &bufs->rc_code))
+			return -ENOMEM;
 
-	tmp_va = mtk_vcodec_fw_map_dm_addr(fw, bufs->rc_code.pa);
-	memcpy(bufs->rc_code.va, tmp_va, bufs->rc_code.size);
+		tmp_va = mtk_vcodec_fw_map_dm_addr(fw, bufs->rc_code.pa);
+		memcpy(bufs->rc_code.va, tmp_va, bufs->rc_code.size);
+	}
 
 	for (i = 0; i < core_num; i++) {
 		if (mtk_venc_mem_alloc(inst, dev, &bufs->rc_info[i]))
diff --git a/drivers/media/platform/mediatek/vcodec/encoder/venc_vpu_if.c b/drivers/media/platform/mediatek/vcodec/encoder/venc_vpu_if.c
index 7772b8442ebc..1da9043fd4f6 100644
--- a/drivers/media/platform/mediatek/vcodec/encoder/venc_vpu_if.c
+++ b/drivers/media/platform/mediatek/vcodec/encoder/venc_vpu_if.c
@@ -8,16 +8,26 @@
 #include "venc_ipi_msg.h"
 #include "venc_vpu_if.h"
 
+#define VSI_OFFSET_MASK 0x0FFFFFFF
+
 static void handle_enc_init_msg(struct venc_vpu_inst *vpu, const void *data)
 {
 	const struct venc_vpu_ipi_msg_init_comm *msg = data;
 	struct mtk_vcodec_fw *fw = vpu->ctx->dev->fw_handler;
+	u64 pa_start, vsi_offset;
 
 	vpu->inst_addr = msg->init_ack.vpu_inst_addr;
-	vpu->vsi = mtk_vcodec_fw_map_dm_addr(fw, vpu->inst_addr);
+
+	if (vpu->ctx->dev->venc_pdata->fw_type == VCP) {
+		pa_start = (u64)fw->vcp->iova_addr;
+		vsi_offset = (msg->vpu_vsi_addr & VSI_OFFSET_MASK) - (pa_start & VSI_OFFSET_MASK);
+		vpu->vsi = mtk_vcodec_fw_map_dm_addr(fw, ENCODER_MEM) + vsi_offset;
+	} else {
+		vpu->vsi = mtk_vcodec_fw_map_dm_addr(fw, msg->vpu_vsi_addr);
+	}
 
 	/* Firmware version field value is unspecified on MT8173. */
-	if (mtk_vcodec_fw_get_type(fw) == VPU)
+	if (vpu->ctx->dev->venc_pdata->fw_type == VPU)
 		return;
 
 	/* Check firmware version. */
@@ -155,6 +165,8 @@ int vpu_enc_init(struct venc_vpu_inst *vpu)
 	out.base.venc_inst = (unsigned long)vpu;
 	if (MTK_ENC_DRV_IS_COMM(vpu->ctx)) {
 		out.codec_type = vpu->ctx->q_data[MTK_Q_DATA_DST].fmt->fourcc;
+		if (vpu->ctx->dev->venc_pdata->fw_type == VCP)
+			out.shared_iova = vpu->ctx->dev->fw_handler->vcp->iova_addr;
 		msg_size = sizeof(struct venc_ap_ipi_msg_init_comm);
 	} else {
 		msg_size = sizeof(struct venc_ap_ipi_msg_init);
-- 
2.45.2



^ permalink raw reply related

* [PATCH v7 6/6] media: mediatek: encoder: Add MT8196 encoder compatible data
From: Irui Wang @ 2026-06-05  9:35 UTC (permalink / raw)
  To: Hans Verkuil, Mauro Carvalho Chehab, Rob Herring,
	Matthias Brugger, Krzysztof Kozlowski, angelogioacchino.delregno,
	nicolas.dufresne, Tiffany Lin, kyrie wu
  Cc: Yunfei Dong, Maoguang Meng, Longfei Wang, Irui Wang,
	Project_Global_Chrome_Upstream_Group, linux-media, devicetree,
	linux-kernel, linux-arm-kernel, linux-mediatek
In-Reply-To: <20260605093519.13695-1-irui.wang@mediatek.com>

MT8196 encoder use common firmware interface, add compatible data to
support MT8196 encoding, and need set dma mask to support 34bit.

Signed-off-by: Irui Wang <irui.wang@mediatek.com>
---
 .../vcodec/encoder/mtk_vcodec_enc_drv.c       | 22 +++++++++++++++++++
 .../vcodec/encoder/mtk_vcodec_enc_drv.h       |  2 ++
 2 files changed, 24 insertions(+)

diff --git a/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc_drv.c b/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc_drv.c
index 5f1feb3b07a6..bc6dfb564026 100644
--- a/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc_drv.c
+++ b/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc_drv.c
@@ -20,6 +20,8 @@
 #include "mtk_vcodec_enc_pm.h"
 #include "../common/mtk_vcodec_intr.h"
 
+#define VENC_DMA_BIT_MASK 34
+
 static const struct mtk_video_fmt mtk_video_formats_output[] = {
 	{
 		.fourcc = V4L2_PIX_FMT_NV12M,
@@ -298,6 +300,9 @@ static int mtk_vcodec_probe(struct platform_device *pdev)
 		goto err_res;
 	}
 
+	if (dev->venc_pdata->set_dma_bit_mask)
+		dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(VENC_DMA_BIT_MASK));
+
 	mutex_init(&dev->enc_mutex);
 	mutex_init(&dev->dev_mutex);
 	spin_lock_init(&dev->dev_ctx_lock);
@@ -461,6 +466,22 @@ static const struct mtk_vcodec_enc_pdata mt8195_pdata = {
 	.fw_init = mtk_vcodec_fw_scp_init,
 };
 
+static const struct mtk_vcodec_enc_pdata mt8196_pdata = {
+	.venc_model_num = 8196,
+	.capture_formats = mtk_video_formats_capture_h264,
+	.num_capture_formats = ARRAY_SIZE(mtk_video_formats_capture_h264),
+	.output_formats = mtk_video_formats_output,
+	.num_output_formats = ARRAY_SIZE(mtk_video_formats_output),
+	.min_bitrate = 64,
+	.max_bitrate = 100000000,
+	.core_id = VENC_SYS,
+	.uses_common_fw_iface = true,
+	.set_dma_bit_mask = true,
+	.fw_type = VCP,
+	.fw_init = mtk_vcodec_fw_vcp_init,
+	.ipi_id = VCP_IPI_ENCODER,
+};
+
 static const struct of_device_id mtk_vcodec_enc_match[] = {
 	{.compatible = "mediatek,mt8173-vcodec-enc",
 			.data = &mt8173_avc_pdata},
@@ -470,6 +491,7 @@ static const struct of_device_id mtk_vcodec_enc_match[] = {
 	{.compatible = "mediatek,mt8188-vcodec-enc", .data = &mt8188_pdata},
 	{.compatible = "mediatek,mt8192-vcodec-enc", .data = &mt8192_pdata},
 	{.compatible = "mediatek,mt8195-vcodec-enc", .data = &mt8195_pdata},
+	{.compatible = "mediatek,mt8196-vcodec-enc", .data = &mt8196_pdata},
 	{},
 };
 MODULE_DEVICE_TABLE(of, mtk_vcodec_enc_match);
diff --git a/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc_drv.h b/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc_drv.h
index 8a69168c350e..1aad27008ce6 100644
--- a/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc_drv.h
+++ b/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc_drv.h
@@ -32,6 +32,7 @@
  * @core_id: stand for h264 or vp8 encode index
  * @uses_34bit: whether the encoder uses 34-bit iova
  * @uses_common_fw_iface: whether the encoder uses common driver interface
+ * @set_dma_bit_mask: whether the encoder need set extra DMA bit mask
  * @fw_type: firmware type (VPU, SCP, or VCP)
  * @fw_init: firmware-specific initialization callback
  * @ipi_id: IPI ID for encoder communication with firmware
@@ -48,6 +49,7 @@ struct mtk_vcodec_enc_pdata {
 	u8 core_id;
 	bool uses_34bit;
 	bool uses_common_fw_iface;
+	bool set_dma_bit_mask;
 	enum mtk_vcodec_fw_type fw_type;
 	struct mtk_vcodec_fw *(*fw_init)(void *priv, enum mtk_vcodec_fw_use fw_use);
 	int ipi_id;
-- 
2.45.2



^ permalink raw reply related

* [PATCH v7 4/6] media: mediatek: encoder: Add support for common firmware interface
From: Irui Wang @ 2026-06-05  9:35 UTC (permalink / raw)
  To: Hans Verkuil, Mauro Carvalho Chehab, Rob Herring,
	Matthias Brugger, Krzysztof Kozlowski, angelogioacchino.delregno,
	nicolas.dufresne, Tiffany Lin, kyrie wu
  Cc: Yunfei Dong, Maoguang Meng, Longfei Wang, Irui Wang,
	Project_Global_Chrome_Upstream_Group, linux-media, devicetree,
	linux-kernel, linux-arm-kernel, linux-mediatek
In-Reply-To: <20260605093519.13695-1-irui.wang@mediatek.com>

The existing encoder firmware interface implied just one type of codec:
H.264. Future encoders may support additional codecs; however adding
entire sets of interfaces for them is not scalable.

Instead, a new "common" firmware interface is defined for non codec
specific messages. The new messages encapsulate the old ones for
backward compatibility.

This patch adds support for these new messages.

Signed-off-by: Irui Wang <irui.wang@mediatek.com>
Reviewed-by: Nicolas Dufresne <nicolas.dufresne@collabora.com>
---
 .../vcodec/encoder/mtk_vcodec_enc_drv.h       |  3 ++
 .../mediatek/vcodec/encoder/venc_drv_if.c     |  3 +-
 .../mediatek/vcodec/encoder/venc_ipi_msg.h    | 26 +++++++++++++++
 .../mediatek/vcodec/encoder/venc_vpu_if.c     | 33 ++++++++++++-------
 4 files changed, 52 insertions(+), 13 deletions(-)

diff --git a/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc_drv.h b/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc_drv.h
index 029133e48073..8a69168c350e 100644
--- a/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc_drv.h
+++ b/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc_drv.h
@@ -16,6 +16,7 @@
 
 #define MTK_ENC_CTX_IS_EXT(ctx) ((ctx)->dev->venc_pdata->uses_ext)
 #define MTK_ENC_IOVA_IS_34BIT(ctx) ((ctx)->dev->venc_pdata->uses_34bit)
+#define MTK_ENC_DRV_IS_COMM(ctx) (((ctx)->dev->venc_pdata->uses_common_fw_iface))
 
 /**
  * struct mtk_vcodec_enc_pdata - compatible data for each IC
@@ -30,6 +31,7 @@
  * @num_output_formats: number of entries in output_formats
  * @core_id: stand for h264 or vp8 encode index
  * @uses_34bit: whether the encoder uses 34-bit iova
+ * @uses_common_fw_iface: whether the encoder uses common driver interface
  * @fw_type: firmware type (VPU, SCP, or VCP)
  * @fw_init: firmware-specific initialization callback
  * @ipi_id: IPI ID for encoder communication with firmware
@@ -45,6 +47,7 @@ struct mtk_vcodec_enc_pdata {
 	size_t num_output_formats;
 	u8 core_id;
 	bool uses_34bit;
+	bool uses_common_fw_iface;
 	enum mtk_vcodec_fw_type fw_type;
 	struct mtk_vcodec_fw *(*fw_init)(void *priv, enum mtk_vcodec_fw_use fw_use);
 	int ipi_id;
diff --git a/drivers/media/platform/mediatek/vcodec/encoder/venc_drv_if.c b/drivers/media/platform/mediatek/vcodec/encoder/venc_drv_if.c
index e83747b8d69a..f8c9349c18c0 100644
--- a/drivers/media/platform/mediatek/vcodec/encoder/venc_drv_if.c
+++ b/drivers/media/platform/mediatek/vcodec/encoder/venc_drv_if.c
@@ -19,13 +19,14 @@
 int venc_if_init(struct mtk_vcodec_enc_ctx *ctx, unsigned int fourcc)
 {
 	int ret = 0;
+	const bool uses_common_fw_iface = MTK_ENC_DRV_IS_COMM(ctx);
 
 	switch (fourcc) {
 	case V4L2_PIX_FMT_VP8:
 		ctx->enc_if = &venc_vp8_if;
 		break;
 	case V4L2_PIX_FMT_H264:
-		ctx->enc_if = &venc_h264_if;
+		ctx->enc_if = uses_common_fw_iface ? &venc_if : &venc_h264_if;
 		break;
 	default:
 		return -EINVAL;
diff --git a/drivers/media/platform/mediatek/vcodec/encoder/venc_ipi_msg.h b/drivers/media/platform/mediatek/vcodec/encoder/venc_ipi_msg.h
index bb16d96a7f57..ce3c2c8059fb 100644
--- a/drivers/media/platform/mediatek/vcodec/encoder/venc_ipi_msg.h
+++ b/drivers/media/platform/mediatek/vcodec/encoder/venc_ipi_msg.h
@@ -45,6 +45,20 @@ struct venc_ap_ipi_msg_init {
 	uint64_t venc_inst;
 };
 
+/**
+ * struct venc_ap_ipi_msg_init_comm - AP to VPU init cmd structure
+ * @base: AP to VPU init cmd structure
+ * @codec_type: encoder type
+ * @reserved: reserved field
+ * @shared_iova: shared iova
+ */
+struct venc_ap_ipi_msg_init_comm {
+	struct venc_ap_ipi_msg_init base;
+	u32 codec_type;
+	u32 reserved;
+	u64 shared_iova;
+};
+
 /**
  * struct venc_ap_ipi_msg_set_param - AP to VPU set_param cmd structure
  * @msg_id:	message id (AP_IPIMSG_XXX_ENC_SET_PARAM)
@@ -175,6 +189,18 @@ struct venc_vpu_ipi_msg_init {
 	uint32_t venc_abi_version;
 };
 
+/**
+ * struct venc_vpu_ipi_msg_init_comm - VPU ack AP init cmd structure
+ * @init_ack: AP init cmd structure
+ * @vpu_vsi_addr: VSI address from VPU
+ * @reserved: reserved field
+ */
+struct venc_vpu_ipi_msg_init_comm {
+	struct venc_vpu_ipi_msg_init init_ack;
+	u32 vpu_vsi_addr;
+	u32 reserved;
+};
+
 /**
  * struct venc_vpu_ipi_msg_set_param - VPU ack AP set_param cmd structure
  * @msg_id:	message id (VPU_IPIMSG_XXX_ENC_SET_PARAM_DONE)
diff --git a/drivers/media/platform/mediatek/vcodec/encoder/venc_vpu_if.c b/drivers/media/platform/mediatek/vcodec/encoder/venc_vpu_if.c
index 0c825aa7224d..7772b8442ebc 100644
--- a/drivers/media/platform/mediatek/vcodec/encoder/venc_vpu_if.c
+++ b/drivers/media/platform/mediatek/vcodec/encoder/venc_vpu_if.c
@@ -10,24 +10,25 @@
 
 static void handle_enc_init_msg(struct venc_vpu_inst *vpu, const void *data)
 {
-	const struct venc_vpu_ipi_msg_init *msg = data;
+	const struct venc_vpu_ipi_msg_init_comm *msg = data;
+	struct mtk_vcodec_fw *fw = vpu->ctx->dev->fw_handler;
 
-	vpu->inst_addr = msg->vpu_inst_addr;
-	vpu->vsi = mtk_vcodec_fw_map_dm_addr(vpu->ctx->dev->fw_handler,
-					     msg->vpu_inst_addr);
+	vpu->inst_addr = msg->init_ack.vpu_inst_addr;
+	vpu->vsi = mtk_vcodec_fw_map_dm_addr(fw, vpu->inst_addr);
 
 	/* Firmware version field value is unspecified on MT8173. */
-	if (mtk_vcodec_fw_get_type(vpu->ctx->dev->fw_handler) == VPU)
+	if (mtk_vcodec_fw_get_type(fw) == VPU)
 		return;
 
 	/* Check firmware version. */
-	mtk_venc_debug(vpu->ctx, "firmware version: 0x%x\n", msg->venc_abi_version);
-	switch (msg->venc_abi_version) {
+	mtk_venc_debug(vpu->ctx, "firmware version: 0x%x\n",
+		       msg->init_ack.venc_abi_version);
+	switch (msg->init_ack.venc_abi_version) {
 	case 1:
 		break;
 	default:
 		mtk_venc_err(vpu->ctx, "unhandled firmware version 0x%x\n",
-			     msg->venc_abi_version);
+			     msg->init_ack.venc_abi_version);
 		vpu->failure = 1;
 		break;
 	}
@@ -133,7 +134,8 @@ static int vpu_enc_send_msg(struct venc_vpu_inst *vpu, void *msg,
 int vpu_enc_init(struct venc_vpu_inst *vpu)
 {
 	int status;
-	struct venc_ap_ipi_msg_init out = { };
+	size_t msg_size;
+	struct venc_ap_ipi_msg_init_comm out = { };
 
 	init_waitqueue_head(&vpu->wq_hd);
 	vpu->signaled = 0;
@@ -149,9 +151,16 @@ int vpu_enc_init(struct venc_vpu_inst *vpu)
 		return -EINVAL;
 	}
 
-	out.msg_id = AP_IPIMSG_ENC_INIT;
-	out.venc_inst = (unsigned long)vpu;
-	if (vpu_enc_send_msg(vpu, &out, sizeof(out))) {
+	out.base.msg_id = AP_IPIMSG_ENC_INIT;
+	out.base.venc_inst = (unsigned long)vpu;
+	if (MTK_ENC_DRV_IS_COMM(vpu->ctx)) {
+		out.codec_type = vpu->ctx->q_data[MTK_Q_DATA_DST].fmt->fourcc;
+		msg_size = sizeof(struct venc_ap_ipi_msg_init_comm);
+	} else {
+		msg_size = sizeof(struct venc_ap_ipi_msg_init);
+	}
+
+	if (vpu_enc_send_msg(vpu, &out, msg_size)) {
 		mtk_venc_err(vpu->ctx, "AP_IPIMSG_ENC_INIT fail");
 		return -EINVAL;
 	}
-- 
2.45.2



^ permalink raw reply related

* [PATCH v7 3/6] media: mediatek: encoder: Add a new encoder driver interface
From: Irui Wang @ 2026-06-05  9:35 UTC (permalink / raw)
  To: Hans Verkuil, Mauro Carvalho Chehab, Rob Herring,
	Matthias Brugger, Krzysztof Kozlowski, angelogioacchino.delregno,
	nicolas.dufresne, Tiffany Lin, kyrie wu
  Cc: Yunfei Dong, Maoguang Meng, Longfei Wang, Irui Wang,
	Project_Global_Chrome_Upstream_Group, linux-media, devicetree,
	linux-kernel, linux-arm-kernel, linux-mediatek
In-Reply-To: <20260605093519.13695-1-irui.wang@mediatek.com>

Introduce a new encoder kernel driver interface to ensure compatibility
with the updated encoder software driver running in firmware.
The new driver interface is expected to support more encoder formats,
share more encode parameters between kernel and firmware.

Signed-off-by: Irui Wang <irui.wang@mediatek.com>
Reviewed-by: Nicolas Dufresne <nicolas.dufresne@collabora.com>
---
 .../platform/mediatek/vcodec/encoder/Makefile |   1 +
 .../mediatek/vcodec/encoder/mtk_vcodec_enc.c  |  14 +-
 .../vcodec/encoder/mtk_vcodec_enc_drv.h       |   8 +-
 .../vcodec/encoder/venc/venc_common_if.c      | 674 ++++++++++++++++++
 .../vcodec/encoder/venc/venc_h264_if.c        |   8 +-
 .../mediatek/vcodec/encoder/venc_drv_if.h     |  11 +-
 6 files changed, 698 insertions(+), 18 deletions(-)
 create mode 100644 drivers/media/platform/mediatek/vcodec/encoder/venc/venc_common_if.c

diff --git a/drivers/media/platform/mediatek/vcodec/encoder/Makefile b/drivers/media/platform/mediatek/vcodec/encoder/Makefile
index e621b5b7e5e6..9d3229d56e39 100644
--- a/drivers/media/platform/mediatek/vcodec/encoder/Makefile
+++ b/drivers/media/platform/mediatek/vcodec/encoder/Makefile
@@ -4,6 +4,7 @@ obj-$(CONFIG_VIDEO_MEDIATEK_VCODEC) += mtk-vcodec-enc.o
 
 mtk-vcodec-enc-y := venc/venc_vp8_if.o \
 		venc/venc_h264_if.o \
+		venc/venc_common_if.o \
 		mtk_vcodec_enc.o \
 		mtk_vcodec_enc_drv.o \
 		mtk_vcodec_enc_pm.o \
diff --git a/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc.c b/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc.c
index fcf0e4f90429..b2f911746c01 100644
--- a/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc.c
+++ b/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc.c
@@ -81,11 +81,11 @@ static int vidioc_venc_s_ctrl(struct v4l2_ctrl *ctrl)
 		break;
 	case V4L2_CID_MPEG_VIDEO_H264_PROFILE:
 		mtk_v4l2_venc_dbg(2, ctx, "V4L2_CID_MPEG_VIDEO_H264_PROFILE val = %d", ctrl->val);
-		p->h264_profile = ctrl->val;
+		p->profile = ctrl->val;
 		break;
 	case V4L2_CID_MPEG_VIDEO_H264_LEVEL:
 		mtk_v4l2_venc_dbg(2, ctx, "V4L2_CID_MPEG_VIDEO_H264_LEVEL val = %d", ctrl->val);
-		p->h264_level = ctrl->val;
+		p->level = ctrl->val;
 		break;
 	case V4L2_CID_MPEG_VIDEO_H264_I_PERIOD:
 		mtk_v4l2_venc_dbg(2, ctx, "V4L2_CID_MPEG_VIDEO_H264_I_PERIOD val = %d", ctrl->val);
@@ -367,8 +367,8 @@ static void mtk_venc_set_param(struct mtk_vcodec_enc_ctx *ctx,
 		mtk_v4l2_venc_err(ctx, "Unsupported fourcc =%d", q_data_src->fmt->fourcc);
 		break;
 	}
-	param->h264_profile = enc_params->h264_profile;
-	param->h264_level = enc_params->h264_level;
+	param->profile = enc_params->profile;
+	param->level = enc_params->level;
 
 	/* Config visible resolution */
 	param->width = q_data_src->visible_width;
@@ -384,8 +384,8 @@ static void mtk_venc_set_param(struct mtk_vcodec_enc_ctx *ctx,
 
 	mtk_v4l2_venc_dbg(0, ctx,
 			  "fmt 0x%x, P/L %d/%d w/h %d/%d buf %d/%d fps/bps %d/%d gop %d i_per %d",
-			  param->input_yuv_fmt, param->h264_profile,
-			  param->h264_level, param->width, param->height,
+			  param->input_yuv_fmt, param->profile,
+			  param->level, param->width, param->height,
 			  param->buf_width, param->buf_height,
 			  param->frm_rate, param->bitrate,
 			  param->gop_size, param->intra_period);
@@ -1139,6 +1139,8 @@ static void mtk_venc_worker(struct work_struct *work)
 		frm_buf.fb_addr[i].size =
 				(size_t)src_buf->vb2_buf.planes[i].length;
 	}
+	frm_buf.num_planes = src_buf->vb2_buf.num_planes;
+
 	bs_buf.va = vb2_plane_vaddr(&dst_buf->vb2_buf, 0);
 	bs_buf.dma_addr = vb2_dma_contig_plane_dma_addr(&dst_buf->vb2_buf, 0);
 	bs_buf.size = (size_t)dst_buf->vb2_buf.planes[0].length;
diff --git a/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc_drv.h b/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc_drv.h
index 6c7e8da6d8ee..029133e48073 100644
--- a/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc_drv.h
+++ b/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc_drv.h
@@ -77,8 +77,8 @@ enum mtk_encode_param {
  * @framerate_denom: frame rate denominator. ex: framerate_num=30 and
  *		     framerate_denom=1 means FPS is 30
  * @h264_max_qp: Max value for H.264 quantization parameter
- * @h264_profile: V4L2 defined H.264 profile
- * @h264_level: V4L2 defined H.264 level
+ * @profile: V4L2 defined profile
+ * @level: V4L2 defined level
  * @force_intra: force/insert intra frame
  */
 struct mtk_enc_params {
@@ -92,8 +92,8 @@ struct mtk_enc_params {
 	unsigned int	framerate_num;
 	unsigned int	framerate_denom;
 	unsigned int	h264_max_qp;
-	unsigned int	h264_profile;
-	unsigned int	h264_level;
+	unsigned int	profile;
+	unsigned int	level;
 	unsigned int	force_intra;
 };
 
diff --git a/drivers/media/platform/mediatek/vcodec/encoder/venc/venc_common_if.c b/drivers/media/platform/mediatek/vcodec/encoder/venc/venc_common_if.c
new file mode 100644
index 000000000000..0efb13aef8d6
--- /dev/null
+++ b/drivers/media/platform/mediatek/vcodec/encoder/venc/venc_common_if.c
@@ -0,0 +1,674 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2025 MediaTek Inc.
+ */
+
+#include "../mtk_vcodec_enc.h"
+#include "../mtk_vcodec_enc_drv.h"
+#include "../venc_drv_base.h"
+#include "../venc_drv_if.h"
+#include "../venc_vpu_if.h"
+#include "../../common/mtk_vcodec_intr.h"
+#include "../../common/mtk_vcodec_util.h"
+
+#define SEQ_HEADER_SIZE 1024
+#define PPS_SIZE 128
+#define MAX_DPB_SIZE 16
+#define MAX_VENC_CORE 3
+#define VENC_CONFIG_LENGTH 115
+#define VENC_CONFIG_DATA 128
+#define VENC_PIC_BITSTREAM_BYTE_CNT 0x0098
+
+/**
+ * enum venc_bs_mode - encode bitstream mode
+ * @VENC_BS_MODE_SPS: encode sps
+ * @VENC_BS_MODE_PPS: encode pps
+ * @VENC_BS_MODE_VPS: encode vps
+ * @VENC_BS_MODE_SEQ_HDR: encode sequence header
+ * @VENC_BS_MODE_FRAME: encode frame
+ * @VENC_BS_MODE_FRAME_FINAL: encode final frame
+ * @VENC_BS_MODE_MAX: max value
+ */
+enum venc_bs_mode {
+	VENC_BS_MODE_SPS = 0,
+	VENC_BS_MODE_PPS,
+	VENC_BS_MODE_VPS,
+	VENC_BS_MODE_SEQ_HDR,
+	VENC_BS_MODE_FRAME,
+	VENC_BS_MODE_FRAME_FINAL,
+	VENC_BS_MODE_MAX
+};
+
+/**
+ * struct venc_config - Structure for encoder configuration
+ *                      AP-W/R : AP is writer/reader on this item
+ *                      MCU-W/R: MCU is write/reader on this item
+ * @input_fourcc: input format fourcc
+ * @bitrate: target bitrate (in bps)
+ * @pic_w: visible width of resolution
+ * @pic_h: visible height of resolution
+ * @buf_w: buffer alignment width of resolution
+ * @buf_h: buffer alignment height of resolution
+ * @gop_size: group of picture size (IDR frame period)
+ * @intra_period: I frame period
+ * @framerate: frame rate in fps
+ * @profile: profile_idc in SPS
+ * @level: level_idc in SPS
+ * @core_num: encoder core num
+ * @dpb_size: encode dpb size
+ * @reserved: reserved fields config
+ */
+struct venc_config {
+	__u32 input_fourcc;
+	__u32 bitrate;
+	__u32 pic_w;
+	__u32 pic_h;
+	__u32 buf_w;
+	__u32 buf_h;
+	__u32 gop_size;
+	__u32 intra_period;
+	__u32 framerate;
+	__u32 profile;
+	__u32 level;
+	__u32 core_num;
+	__u32 dpb_size;
+	__u32 reserved[VENC_CONFIG_LENGTH];
+};
+
+/**
+ * struct venc_config_data - Structure for configuration data
+ * @config_data: extended configuration data besides the basic configuration
+ */
+struct venc_config_data {
+	unsigned int config_data[VENC_CONFIG_DATA];
+};
+
+/**
+ * struct venc_work_buf - Structure for working buffer information
+ *                               AP-W/R : AP is writer/reader on this item
+ *                               MCU-W/R: MCU is write/reader on this item
+ * @iova: IO virtual address
+ * @pa: physical address
+ * @pa_64: for 64bit pa padding
+ * @va: virtual address
+ * @va_padding: for 64bit va padding
+ * @size: buffer size
+ * @size_padding: for 64bit size padding
+ */
+struct venc_work_buf {
+	unsigned long long iova;
+	union {
+		unsigned int pa;
+		unsigned long long pa_64;
+	};
+	union {
+		void *va;
+		unsigned long long va_padding;
+	};
+	union {
+		unsigned int size;
+		unsigned long long size_padding;
+	};
+};
+
+/**
+ * struct venc_work_buf_list - Structure for encode working buffer list
+ * @rc_code: RC code buffer
+ * @rc_info: RC info buffer
+ * @luma: luma buffer
+ * @chroma: chroma buffer
+ * @sub_luma: sub luma buffer
+ * @sub_write: sub write buffer
+ * @col_mv: col_mv buffer
+ * @wpp: wpp buffer
+ * @wpp_nbm: wpp nbm buffer
+ * @skip_frame: skip frame buffer
+ */
+struct venc_work_buf_list {
+	struct venc_work_buf rc_code;
+	struct venc_work_buf rc_info[MAX_VENC_CORE];
+	struct venc_work_buf luma[MAX_DPB_SIZE];
+	struct venc_work_buf chroma[MAX_DPB_SIZE];
+	struct venc_work_buf sub_luma[MAX_DPB_SIZE];
+	struct venc_work_buf sub_write[MAX_DPB_SIZE];
+	struct venc_work_buf col_mv[MAX_DPB_SIZE];
+	struct venc_work_buf wpp[MAX_VENC_CORE];
+	struct venc_work_buf wpp_nbm[MAX_VENC_CORE];
+	struct venc_work_buf skip_frame;
+};
+
+/**
+ * struct venc_info -  Structure for encode frame and bs information
+ * @fb_addr: frame buffer address array
+ * @fb_size: frame buffer size array
+ * @bs_addr: bitstream buffer address
+ * @bs_size: bitstream buffer size
+ */
+struct venc_info {
+	unsigned long long fb_addr[VIDEO_MAX_PLANES];
+	unsigned int fb_size[VIDEO_MAX_PLANES];
+	unsigned long long bs_addr;
+	unsigned long long bs_size;
+};
+
+/**
+ * struct venc_vsi - Structure for VCP driver control and info share
+ *                   AP-W/R : AP is writer/reader on this item
+ *                   VCP-W/R: VCP is write/reader on this item
+ * @config: encoder configuration
+ * @data: encoder configuration data
+ * @bufs: encoder working buffers
+ * @venc: encoder information
+ */
+struct venc_vsi {
+	struct venc_config config;
+	struct venc_config_data data;
+	struct venc_work_buf_list bufs;
+	struct venc_info venc;
+};
+
+/**
+ * struct venc_inst - Structure for encoder instance
+ * @hw_base: hardware io address
+ * @pps_buf: PPS buffer
+ * @seq_buf: sequence header buffer
+ * @work_buf_allocated: work buffer allocated or not
+ * @frm_cnt: encoded frame count
+ * @skip_frm_cnt: encoded skip frame count
+ * @prepend_hdr: prepend header flag
+ * @vpu_inst: vpu instance
+ * @vsi: encode vsi
+ * @ctx: encoder context
+ */
+struct venc_inst {
+	void __iomem *hw_base;
+	struct mtk_vcodec_mem pps_buf;
+	struct mtk_vcodec_mem seq_buf;
+	bool work_buf_allocated;
+	unsigned int frm_cnt;
+	unsigned int skip_frm_cnt;
+	unsigned int prepend_hdr;
+	struct venc_vpu_inst vpu_inst;
+	struct venc_vsi *vsi;
+	struct mtk_vcodec_enc_ctx *ctx;
+};
+
+static int venc_init(struct mtk_vcodec_enc_ctx *ctx)
+{
+	int ret = 0;
+	struct venc_inst *inst;
+
+	inst = kzalloc_obj(*inst, GFP_KERNEL);
+	if (!inst)
+		return -ENOMEM;
+
+	inst->ctx = ctx;
+	inst->vpu_inst.ctx = ctx;
+	inst->vpu_inst.id = ctx->dev->venc_pdata->ipi_id;
+	inst->hw_base = mtk_vcodec_get_reg_addr(inst->ctx->dev->reg_base, VENC_SYS);
+
+	ret = vpu_enc_init(&inst->vpu_inst);
+	inst->vsi = (struct venc_vsi *)inst->vpu_inst.vsi;
+
+	if (ret) {
+		kfree(inst);
+		return ret;
+	}
+
+	ctx->drv_handle = inst;
+
+	return 0;
+}
+
+static inline u32 venc_read_reg(struct venc_inst *inst, u32 addr)
+{
+	return readl(inst->hw_base + addr);
+}
+
+static unsigned int venc_wait_encode_done(struct venc_inst *inst)
+{
+	unsigned int irq_status = 0;
+	struct mtk_vcodec_enc_ctx *ctx = (struct mtk_vcodec_enc_ctx *)inst->ctx;
+
+	if (!mtk_vcodec_wait_for_done_ctx(ctx, MTK_INST_IRQ_RECEIVED,
+					  WAIT_INTR_TIMEOUT_MS, 0)) {
+		irq_status = ctx->irq_status;
+		mtk_venc_debug(ctx, "irq_status %x <-", irq_status);
+	}
+	return irq_status;
+}
+
+static void venc_set_bufs(struct venc_inst *inst,
+			  struct venc_frm_buf *frm_buf,
+			  struct mtk_vcodec_mem *bs_buf)
+{
+	unsigned int i;
+
+	if (frm_buf) {
+		for (i = 0; i < frm_buf->num_planes; i++) {
+			inst->vsi->venc.fb_addr[i] = frm_buf->fb_addr[i].dma_addr;
+			inst->vsi->venc.fb_size[i] = frm_buf->fb_addr[i].size;
+			mtk_venc_debug(inst->ctx, "%s: fb_buf[%d]: %llx(%d)\n",
+				       __func__, i,
+				       inst->vsi->venc.fb_addr[i],
+				       inst->vsi->venc.fb_size[i]);
+		}
+	}
+
+	if (bs_buf) {
+		inst->vsi->venc.bs_addr = bs_buf->dma_addr;
+		inst->vsi->venc.bs_size = bs_buf->size;
+		mtk_venc_debug(inst->ctx, "%s: bs_buf: %llx(%d)\n",
+			       __func__,
+			       inst->vsi->venc.bs_addr,
+			       (unsigned int)inst->vsi->venc.bs_size);
+	}
+}
+
+static int venc_encode_sps(struct venc_inst *inst,
+			   struct mtk_vcodec_mem *bs_buf,
+			   unsigned int *bs_size)
+{
+	int ret = 0;
+	unsigned int irq_status;
+
+	venc_set_bufs(inst, NULL, bs_buf);
+	ret = vpu_enc_encode(&inst->vpu_inst, VENC_BS_MODE_SPS, NULL, bs_buf, NULL);
+	if (ret)
+		return ret;
+
+	irq_status = venc_wait_encode_done(inst);
+	if (irq_status != MTK_VENC_IRQ_STATUS_SPS) {
+		mtk_venc_err(inst->ctx, "expect irq status %d", MTK_VENC_IRQ_STATUS_SPS);
+		return -EINVAL;
+	}
+
+	*bs_size = venc_read_reg(inst, VENC_PIC_BITSTREAM_BYTE_CNT);
+	mtk_venc_debug(inst->ctx, "sps bs size %d <-", *bs_size);
+
+	return ret;
+}
+
+static int venc_encode_pps(struct venc_inst *inst,
+			   struct mtk_vcodec_mem *bs_buf,
+			   unsigned int *bs_size)
+{
+	int ret = 0;
+	unsigned int irq_status;
+
+	venc_set_bufs(inst, NULL, bs_buf);
+	ret = vpu_enc_encode(&inst->vpu_inst, VENC_BS_MODE_PPS, NULL, bs_buf, NULL);
+	if (ret)
+		return ret;
+
+	irq_status = venc_wait_encode_done(inst);
+	if (irq_status != MTK_VENC_IRQ_STATUS_PPS) {
+		mtk_venc_err(inst->ctx, "expect irq status %d", MTK_VENC_IRQ_STATUS_PPS);
+		return -EINVAL;
+	}
+
+	*bs_size = venc_read_reg(inst, VENC_PIC_BITSTREAM_BYTE_CNT);
+	mtk_venc_debug(inst->ctx, "pps bs size %d <-", *bs_size);
+
+	return ret;
+}
+
+static int venc_encode_header(struct venc_inst *inst,
+			      struct mtk_vcodec_mem *bs_buf,
+			      unsigned int *bs_size)
+{
+	int ret = 0;
+	unsigned int bs_size_sps;
+	unsigned int bs_size_pps;
+
+	ret = venc_encode_sps(inst, bs_buf, &bs_size_sps);
+	if (ret)
+		return ret;
+
+	ret = venc_encode_pps(inst, &inst->pps_buf, &bs_size_pps);
+	if (ret)
+		return ret;
+
+	memcpy(bs_buf->va + bs_size_sps, inst->pps_buf.va, bs_size_pps);
+	*bs_size = bs_size_sps + bs_size_pps;
+
+	return ret;
+}
+
+static int venc_encode_frame(struct venc_inst *inst,
+			     struct venc_frm_buf *frm_buf,
+			     struct mtk_vcodec_mem *bs_buf,
+			     unsigned int *bs_size)
+{
+	int ret = 0;
+	unsigned int irq_status;
+
+	venc_set_bufs(inst, frm_buf, bs_buf);
+	ret = vpu_enc_encode(&inst->vpu_inst, VENC_BS_MODE_FRAME, frm_buf, bs_buf, NULL);
+	if (ret)
+		return ret;
+
+	irq_status = venc_wait_encode_done(inst);
+	if (irq_status != MTK_VENC_IRQ_STATUS_FRM) {
+		mtk_venc_err(inst->ctx, "expect irq status %d", MTK_VENC_IRQ_STATUS_FRM);
+		return -EINVAL;
+	}
+
+	*bs_size = venc_read_reg(inst, VENC_PIC_BITSTREAM_BYTE_CNT);
+
+	++inst->frm_cnt;
+
+	return ret;
+}
+
+static int venc_encode(void *handle,
+		       enum venc_start_opt opt,
+		       struct venc_frm_buf *frm_buf,
+		       struct mtk_vcodec_mem *bs_buf,
+		       struct venc_done_result *result)
+{
+	int ret = 0;
+	struct venc_inst *inst = (struct venc_inst *)handle;
+	struct mtk_vcodec_enc_ctx *ctx;
+	unsigned int bs_size_hdr;
+
+	if (WARN_ON(!inst || !inst->vsi))
+		return -EINVAL;
+
+	ctx = inst->ctx;
+
+	mtk_venc_debug(ctx, "%s: opt: %d\n", __func__, opt);
+
+	enable_irq(ctx->dev->enc_irq);
+	switch (opt) {
+	case VENC_START_OPT_ENCODE_SEQUENCE_HEADER: {
+		ret = venc_encode_header(inst, bs_buf, &bs_size_hdr);
+		if (ret)
+			goto encode_err;
+
+		result->bs_size = bs_size_hdr;
+		result->is_key_frm = false;
+		break;
+	}
+
+	case VENC_START_OPT_ENCODE_FRAME: {
+		if (!inst->prepend_hdr) {
+			ret = venc_encode_frame(inst, frm_buf, bs_buf, &result->bs_size);
+			if (ret)
+				goto encode_err;
+
+			result->is_key_frm = inst->vpu_inst.is_key_frm;
+			break;
+		}
+
+		ret = venc_encode_header(inst, &inst->seq_buf, &bs_size_hdr);
+		if (ret)
+			goto encode_err;
+
+		ret = venc_encode_frame(inst, frm_buf, bs_buf, &result->bs_size);
+		if (ret)
+			goto encode_err;
+
+		memmove(bs_buf->va + bs_size_hdr, bs_buf->va, result->bs_size);
+		memcpy(bs_buf->va, inst->seq_buf.va, bs_size_hdr);
+		result->bs_size += bs_size_hdr;
+
+		inst->prepend_hdr = 0;
+		result->is_key_frm = inst->vpu_inst.is_key_frm;
+		break;
+	}
+
+	default:
+		mtk_venc_err(inst->ctx, "venc_opt %d not supported", opt);
+		ret = -EINVAL;
+		break;
+	}
+
+encode_err:
+	disable_irq(ctx->dev->enc_irq);
+	mtk_venc_debug(ctx, "opt %d, return %d", opt, ret);
+
+	return ret;
+}
+
+static int mtk_venc_mem_alloc(struct venc_inst *inst,
+			      struct device *dev,
+			      struct venc_work_buf *buf)
+{
+	dma_addr_t dma_addr;
+
+	if (WARN_ON(!dev || !buf))
+		return -EINVAL;
+
+	if (buf->size == 0)
+		return 0;
+
+	buf->va = dma_alloc_coherent(dev, buf->size, &dma_addr, GFP_KERNEL);
+	if (!buf->va)
+		return -ENOMEM;
+
+	buf->iova = (unsigned long long)dma_addr;
+
+	mtk_venc_debug(inst->ctx, "allocate buffer, size: %d, va: %p, iova: 0x%llx",
+		       buf->size, buf->va, buf->iova);
+
+	return 0;
+}
+
+static void mtk_venc_mem_free(struct venc_inst *inst,
+			      struct device *dev,
+			      struct venc_work_buf *buf)
+{
+	if (WARN_ON(!dev || !buf))
+		return;
+
+	if (!buf->va)
+		return;
+
+	mtk_venc_debug(inst->ctx, "free buffer, size: %d, va: %p, iova: 0x%llx",
+		       buf->size, buf->va, buf->iova);
+
+	dma_free_coherent(dev, buf->size, buf->va, buf->iova);
+	buf->va = NULL;
+	buf->iova = 0;
+	buf->size = 0;
+}
+
+static void venc_free_rc_buf(struct venc_inst *inst,
+			     struct venc_work_buf_list *bufs,
+			     unsigned int core_num)
+{
+	int i;
+	struct device *dev;
+
+	dev = &inst->ctx->dev->plat_dev->dev;
+	mtk_venc_mem_free(inst, dev, &bufs->rc_code);
+
+	for (i = 0; i < core_num; i++)
+		mtk_venc_mem_free(inst, dev, &bufs->rc_info[i]);
+}
+
+static void venc_free_work_buf(struct venc_inst *inst)
+{
+	int i;
+	struct venc_work_buf_list *bufs = &inst->vsi->bufs;
+	unsigned int core_num = inst->vsi->config.core_num;
+	unsigned int dpb_size = inst->vsi->config.dpb_size;
+	struct device *dev;
+
+	if (bufs->rc_code.va)
+		venc_free_rc_buf(inst, bufs, core_num);
+
+	dev = &inst->ctx->dev->plat_dev->dev;
+
+	for (i = 0; i < core_num; i++) {
+		mtk_venc_mem_free(inst, dev, &bufs->wpp[i]);
+		mtk_venc_mem_free(inst, dev, &bufs->wpp_nbm[i]);
+	}
+
+	for (i = 0; i < dpb_size; i++) {
+		mtk_venc_mem_free(inst, dev, &bufs->luma[i]);
+		mtk_venc_mem_free(inst, dev, &bufs->chroma[i]);
+		mtk_venc_mem_free(inst, dev, &bufs->sub_luma[i]);
+		mtk_venc_mem_free(inst, dev, &bufs->sub_write[i]);
+		mtk_venc_mem_free(inst, dev, &bufs->col_mv[i]);
+	}
+
+	if (inst->pps_buf.va)
+		mtk_vcodec_mem_free(inst->ctx, &inst->pps_buf);
+
+	if (inst->seq_buf.va)
+		mtk_vcodec_mem_free(inst->ctx, &inst->seq_buf);
+}
+
+static int venc_alloc_rc_buf(struct venc_inst *inst,
+			     struct venc_work_buf_list *bufs,
+			     unsigned int core_num)
+{
+	int i;
+	struct mtk_vcodec_fw *fw = inst->ctx->dev->fw_handler;
+	struct device *dev;
+	void *tmp_va;
+
+	dev = &inst->ctx->dev->plat_dev->dev;
+	if (mtk_venc_mem_alloc(inst, dev, &bufs->rc_code))
+		return -ENOMEM;
+
+	tmp_va = mtk_vcodec_fw_map_dm_addr(fw, bufs->rc_code.pa);
+	memcpy(bufs->rc_code.va, tmp_va, bufs->rc_code.size);
+
+	for (i = 0; i < core_num; i++) {
+		if (mtk_venc_mem_alloc(inst, dev, &bufs->rc_info[i]))
+			goto err_rc_buf;
+	}
+
+	return 0;
+
+err_rc_buf:
+	venc_free_rc_buf(inst, bufs, core_num);
+
+	return -ENOMEM;
+}
+
+static int venc_alloc_work_buf(struct venc_inst *inst)
+{
+	int i, ret;
+	struct venc_work_buf_list *bufs = &inst->vsi->bufs;
+	unsigned int core_num = inst->vsi->config.core_num;
+	unsigned int dpb_size = inst->vsi->config.dpb_size;
+	struct device *dev;
+
+	if (bufs->rc_code.size != 0) {
+		ret = venc_alloc_rc_buf(inst, bufs, core_num);
+		if (ret) {
+			mtk_venc_err(inst->ctx, "cannot allocate rc buf");
+			return -ENOMEM;
+		}
+	}
+
+	dev = &inst->ctx->dev->plat_dev->dev;
+
+	for (i = 0; i < core_num; i++) {
+		if (mtk_venc_mem_alloc(inst, dev, &bufs->wpp[i]) ||
+		    mtk_venc_mem_alloc(inst, dev, &bufs->wpp_nbm[i]))
+			goto err_alloc;
+	}
+
+	for (i = 0; i < dpb_size; i++) {
+		if (mtk_venc_mem_alloc(inst, dev, &bufs->luma[i]) ||
+		    mtk_venc_mem_alloc(inst, dev, &bufs->chroma[i]) ||
+		    mtk_venc_mem_alloc(inst, dev, &bufs->sub_luma[i]) ||
+		    mtk_venc_mem_alloc(inst, dev, &bufs->sub_write[i]) ||
+		    mtk_venc_mem_alloc(inst, dev, &bufs->col_mv[i]))
+			goto err_alloc;
+	}
+
+	/* the pps_buf and seq_buf are used by AP side only */
+	inst->pps_buf.size = PPS_SIZE;
+	ret = mtk_vcodec_mem_alloc(inst->ctx, &inst->pps_buf);
+	if (ret) {
+		mtk_venc_err(inst->ctx, "cannot allocate pps_buf");
+		goto err_alloc;
+	}
+
+	inst->seq_buf.size = SEQ_HEADER_SIZE;
+	ret = mtk_vcodec_mem_alloc(inst->ctx, &inst->seq_buf);
+	if (ret) {
+		mtk_venc_err(inst->ctx, "cannot allocate seq_buf");
+		goto err_alloc;
+	}
+	return 0;
+
+err_alloc:
+	venc_free_work_buf(inst);
+	return -ENOMEM;
+}
+
+static int venc_set_param(void *handle,
+			  enum venc_set_param_type type,
+			  struct venc_enc_param *enc_prm)
+{
+	int ret = 0;
+	struct venc_inst *inst = (struct venc_inst *)handle;
+
+	switch (type) {
+	case VENC_SET_PARAM_ENC:
+		if (WARN_ON(!inst->vsi))
+			return -EINVAL;
+		inst->vsi->config.input_fourcc = enc_prm->input_yuv_fmt;
+		inst->vsi->config.bitrate = enc_prm->bitrate;
+		inst->vsi->config.pic_w = enc_prm->width;
+		inst->vsi->config.pic_h = enc_prm->height;
+		inst->vsi->config.buf_w = enc_prm->buf_width;
+		inst->vsi->config.buf_h = enc_prm->buf_height;
+		inst->vsi->config.gop_size = enc_prm->gop_size;
+		inst->vsi->config.framerate = enc_prm->frm_rate;
+		inst->vsi->config.intra_period = enc_prm->intra_period;
+		inst->vsi->config.profile = enc_prm->profile;
+		inst->vsi->config.level = enc_prm->level;
+
+		ret = vpu_enc_set_param(&inst->vpu_inst, type, enc_prm);
+		if (ret)
+			break;
+
+		if (inst->work_buf_allocated) {
+			venc_free_work_buf(inst);
+			inst->work_buf_allocated = false;
+		}
+		ret = venc_alloc_work_buf(inst);
+		if (ret)
+			break;
+		inst->work_buf_allocated = true;
+		break;
+	case VENC_SET_PARAM_PREPEND_HEADER:
+		inst->prepend_hdr = 1;
+		break;
+	default:
+		ret = vpu_enc_set_param(&inst->vpu_inst, type, enc_prm);
+		break;
+	}
+
+	return ret;
+}
+
+static int venc_deinit(void *handle)
+{
+	int ret = 0;
+	struct venc_inst *inst = (struct venc_inst *)handle;
+
+	ret = vpu_enc_deinit(&inst->vpu_inst);
+
+	if (inst->work_buf_allocated)
+		venc_free_work_buf(inst);
+
+	kfree(inst);
+
+	return ret;
+}
+
+const struct venc_common_if venc_if = {
+	.init = venc_init,
+	.encode = venc_encode,
+	.set_param = venc_set_param,
+	.deinit = venc_deinit,
+};
diff --git a/drivers/media/platform/mediatek/vcodec/encoder/venc/venc_h264_if.c b/drivers/media/platform/mediatek/vcodec/encoder/venc/venc_h264_if.c
index d2f4d732d2f7..320c505cdb21 100644
--- a/drivers/media/platform/mediatek/vcodec/encoder/venc/venc_h264_if.c
+++ b/drivers/media/platform/mediatek/vcodec/encoder/venc/venc_h264_if.c
@@ -723,9 +723,9 @@ static void h264_enc_set_vsi_configs(struct venc_h264_inst *inst,
 	inst->vsi->config.framerate = enc_prm->frm_rate;
 	inst->vsi->config.intra_period = enc_prm->intra_period;
 	inst->vsi->config.profile =
-		h264_get_profile(inst, enc_prm->h264_profile);
+		h264_get_profile(inst, enc_prm->profile);
 	inst->vsi->config.level =
-		h264_get_level(inst, enc_prm->h264_level);
+		h264_get_level(inst, enc_prm->level);
 	inst->vsi->config.wfd = 0;
 }
 
@@ -742,9 +742,9 @@ static void h264_enc_set_vsi_34_configs(struct venc_h264_inst *inst,
 	inst->vsi_34->config.framerate = enc_prm->frm_rate;
 	inst->vsi_34->config.intra_period = enc_prm->intra_period;
 	inst->vsi_34->config.profile =
-		h264_get_profile(inst, enc_prm->h264_profile);
+		h264_get_profile(inst, enc_prm->profile);
 	inst->vsi_34->config.level =
-		h264_get_level(inst, enc_prm->h264_level);
+		h264_get_level(inst, enc_prm->level);
 	inst->vsi_34->config.wfd = 0;
 }
 
diff --git a/drivers/media/platform/mediatek/vcodec/encoder/venc_drv_if.h b/drivers/media/platform/mediatek/vcodec/encoder/venc_drv_if.h
index 889440a436b6..3c2a1b5e9312 100644
--- a/drivers/media/platform/mediatek/vcodec/encoder/venc_drv_if.h
+++ b/drivers/media/platform/mediatek/vcodec/encoder/venc_drv_if.h
@@ -66,8 +66,8 @@ enum venc_set_param_type {
  * struct venc_enc_prm - encoder settings for VENC_SET_PARAM_ENC used in
  *					  venc_if_set_param()
  * @input_fourcc: input yuv format
- * @h264_profile: V4L2 defined H.264 profile
- * @h264_level: V4L2 defined H.264 level
+ * @profile: V4L2 defined profile
+ * @level: V4L2 defined level
  * @width: image width
  * @height: image height
  * @buf_width: buffer width
@@ -79,8 +79,8 @@ enum venc_set_param_type {
  */
 struct venc_enc_param {
 	enum venc_yuv_fmt input_yuv_fmt;
-	unsigned int h264_profile;
-	unsigned int h264_level;
+	unsigned int profile;
+	unsigned int level;
 	unsigned int width;
 	unsigned int height;
 	unsigned int buf_width;
@@ -107,9 +107,11 @@ struct venc_frame_info {
 /*
  * struct venc_frm_buf - frame buffer information used in venc_if_encode()
  * @fb_addr: plane frame buffer addresses
+ * @num_planes: number of planes
  */
 struct venc_frm_buf {
 	struct mtk_vcodec_fb fb_addr[MTK_VCODEC_MAX_PLANES];
+	unsigned int num_planes;
 };
 
 /*
@@ -124,6 +126,7 @@ struct venc_done_result {
 
 extern const struct venc_common_if venc_h264_if;
 extern const struct venc_common_if venc_vp8_if;
+extern const struct venc_common_if venc_if;
 
 /*
  * venc_if_init - Create the driver handle
-- 
2.45.2



^ permalink raw reply related

* [PATCH v7 1/6] media: dt-bindings: mediatek,vcodec-encoder: Add MT8196
From: Irui Wang @ 2026-06-05  9:35 UTC (permalink / raw)
  To: Hans Verkuil, Mauro Carvalho Chehab, Rob Herring,
	Matthias Brugger, Krzysztof Kozlowski, angelogioacchino.delregno,
	nicolas.dufresne, Tiffany Lin, kyrie wu
  Cc: Yunfei Dong, Maoguang Meng, Longfei Wang, Irui Wang,
	Project_Global_Chrome_Upstream_Group, linux-media, devicetree,
	linux-kernel, linux-arm-kernel, linux-mediatek, Rob Herring (Arm)
In-Reply-To: <20260605093519.13695-1-irui.wang@mediatek.com>

Add support for MT8196 video encoder which uses VCP (Video Co-Processor)
for firmware management. Unlike previous platforms that use SCP/VPU, MT8196
requires VCP to load and execute the video encoding firmware, with the
encoder communicating through VCP to perform encoding operations.

Add the "mediatek,mt8196-vcodec-enc" compatible string and introduce
the "mediatek,vcp" property to reference the VCP device, which is
required for MT8196 encoder operation.

Signed-off-by: Irui Wang <irui.wang@mediatek.com>
Reviewed-by: Rob Herring (Arm) <robh@kernel.org>
Reviewed-by: Nicolas Dufresne <nicolas.dufresne@collabora.com>
---
 .../media/mediatek,vcodec-encoder.yaml        | 22 +++++++++++++++++++
 1 file changed, 22 insertions(+)

diff --git a/Documentation/devicetree/bindings/media/mediatek,vcodec-encoder.yaml b/Documentation/devicetree/bindings/media/mediatek,vcodec-encoder.yaml
index ebc615584f92..72698456374a 100644
--- a/Documentation/devicetree/bindings/media/mediatek,vcodec-encoder.yaml
+++ b/Documentation/devicetree/bindings/media/mediatek,vcodec-encoder.yaml
@@ -24,6 +24,7 @@ properties:
               - mediatek,mt8188-vcodec-enc
               - mediatek,mt8192-vcodec-enc
               - mediatek,mt8195-vcodec-enc
+              - mediatek,mt8196-vcodec-enc
       - items:
           - const: mediatek,mt8186-vcodec-enc
           - const: mediatek,mt8183-vcodec-enc
@@ -58,6 +59,13 @@ properties:
     description:
       Describes point to scp.
 
+  mediatek,vcp:
+    $ref: /schemas/types.yaml#/definitions/phandle
+    description:
+      Reference to the VCP (Video Co-Processor) device that loads and executes
+      the video encoding firmware. The encoder communicates with the firmware
+      through VCP to perform encoding operations.
+
   power-domains:
     maxItems: 1
 
@@ -76,6 +84,20 @@ required:
   - iommus
 
 allOf:
+  - if:
+      properties:
+        compatible:
+          contains:
+            enum:
+              - mediatek,mt8196-vcodec-enc
+
+    then:
+      required:
+        - mediatek,vcp
+    else:
+      properties:
+        mediatek,vcp: false
+
   - if:
       properties:
         compatible:
-- 
2.45.2



^ permalink raw reply related

* [PATCH v7 2/6] media: mediatek: encoder: Add new platform data members
From: Irui Wang @ 2026-06-05  9:35 UTC (permalink / raw)
  To: Hans Verkuil, Mauro Carvalho Chehab, Rob Herring,
	Matthias Brugger, Krzysztof Kozlowski, angelogioacchino.delregno,
	nicolas.dufresne, Tiffany Lin, kyrie wu
  Cc: Yunfei Dong, Maoguang Meng, Longfei Wang, Irui Wang,
	Project_Global_Chrome_Upstream_Group, linux-media, devicetree,
	linux-kernel, linux-arm-kernel, linux-mediatek
In-Reply-To: <20260605093519.13695-1-irui.wang@mediatek.com>

Add new platform data members to support different encoder ICs:
- venc_model_num: encoder model number
- fw_type: firmware type (VPU, SCP, or VCP)
- fw_init: firmware-specific initialization callback
- ipi_id: IPI ID for encoder communication

This centralizes all static platform configuration in the platform
data structure, eliminating the need for runtime device tree parsing
and the per-device fw_init callback pointer. Each platform's pdata
now directly specifies its firmware initialization function.

Changes:
1. Add venc_model_num to pdata and remove mtk_vcodec_enc_get_chip_name()
2. Add fw_type to pdata for each platform (VPU or SCP)
3. Add ipi_id field declaration to pdata
4. Remove device tree parsing for fw_type

Signed-off-by: Irui Wang <irui.wang@mediatek.com>
---
 .../mediatek/vcodec/common/mtk_vcodec_fw.c    |  3 +-
 .../mediatek/vcodec/encoder/mtk_vcodec_enc.c  | 22 +---------
 .../vcodec/encoder/mtk_vcodec_enc_drv.c       | 40 +++++++++++--------
 .../vcodec/encoder/mtk_vcodec_enc_drv.h       | 10 ++++-
 4 files changed, 36 insertions(+), 39 deletions(-)

diff --git a/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw.c b/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw.c
index a2e6a01272b2..9df64200d933 100644
--- a/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw.c
+++ b/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw.c
@@ -23,8 +23,9 @@ struct mtk_vcodec_fw *mtk_vcodec_fw_select(void *priv, enum mtk_vcodec_fw_type t
 {
 	if (fw_use == ENCODER) {
 		struct mtk_vcodec_enc_dev *enc_dev = priv;
+		const struct mtk_vcodec_enc_pdata *pdata = enc_dev->venc_pdata;
 
-		return enc_dev->fw_init(priv, fw_use);
+		return pdata->fw_init(priv, fw_use);
 	}
 
 	if (fw_use == DECODER) {
diff --git a/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc.c b/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc.c
index 48cb5dded70a..fcf0e4f90429 100644
--- a/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc.c
+++ b/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc.c
@@ -198,33 +198,15 @@ static int vidioc_enum_fmt_vid_out(struct file *file, void *priv,
 			       pdata->num_output_formats);
 }
 
-static int mtk_vcodec_enc_get_chip_name(struct mtk_vcodec_enc_ctx *ctx)
-{
-	struct device *dev = &ctx->dev->plat_dev->dev;
-
-	if (of_device_is_compatible(dev->of_node, "mediatek,mt8173-vcodec-enc"))
-		return 8173;
-	else if (of_device_is_compatible(dev->of_node, "mediatek,mt8183-vcodec-enc"))
-		return 8183;
-	else if (of_device_is_compatible(dev->of_node, "mediatek,mt8192-vcodec-enc"))
-		return 8192;
-	else if (of_device_is_compatible(dev->of_node, "mediatek,mt8195-vcodec-enc"))
-		return 8195;
-	else if (of_device_is_compatible(dev->of_node, "mediatek,mt8188-vcodec-enc"))
-		return 8188;
-	else
-		return 8173;
-}
-
 static int vidioc_venc_querycap(struct file *file, void *priv,
 				struct v4l2_capability *cap)
 {
 	struct mtk_vcodec_enc_ctx *ctx = file_to_enc_ctx(file);
+	const struct mtk_vcodec_enc_pdata *pdata = ctx->dev->venc_pdata;
 	struct device *dev = &ctx->dev->plat_dev->dev;
-	int platform_name = mtk_vcodec_enc_get_chip_name(ctx);
 
 	strscpy(cap->driver, dev->driver->name, sizeof(cap->driver));
-	snprintf(cap->card, sizeof(cap->card), "MT%d video encoder", platform_name);
+	snprintf(cap->card, sizeof(cap->card), "MT%d video encoder", pdata->venc_model_num);
 
 	return 0;
 }
diff --git a/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc_drv.c b/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc_drv.c
index dc54d445d98d..5f1feb3b07a6 100644
--- a/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc_drv.c
+++ b/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc_drv.c
@@ -245,8 +245,6 @@ static int mtk_vcodec_probe(struct platform_device *pdev)
 {
 	struct mtk_vcodec_enc_dev *dev;
 	struct video_device *vfd_enc;
-	phandle rproc_phandle;
-	enum mtk_vcodec_fw_type fw_type;
 	int ret;
 
 	dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);
@@ -256,25 +254,17 @@ static int mtk_vcodec_probe(struct platform_device *pdev)
 	INIT_LIST_HEAD(&dev->ctx_list);
 	dev->plat_dev = pdev;
 
-	if (!of_property_read_u32(pdev->dev.of_node, "mediatek,vpu",
-				  &rproc_phandle)) {
-		fw_type = VPU;
-		dev->fw_init = mtk_vcodec_fw_vpu_init;
-	} else if (!of_property_read_u32(pdev->dev.of_node, "mediatek,scp",
-					 &rproc_phandle)) {
-		fw_type = SCP;
-		dev->fw_init = mtk_vcodec_fw_scp_init;
-	} else {
-		dev_err(&pdev->dev, "[MTK VCODEC] Could not get venc IPI device");
+	dev->venc_pdata = of_device_get_match_data(&pdev->dev);
+	if (!dev->venc_pdata) {
+		dev_err(&pdev->dev, "Failed to get match data");
 		return -ENODEV;
 	}
-	dma_set_max_seg_size(&pdev->dev, UINT_MAX);
-
-	dev->fw_handler = mtk_vcodec_fw_select(dev, fw_type, ENCODER);
+	dev->fw_handler = mtk_vcodec_fw_select(dev, dev->venc_pdata->fw_type, ENCODER);
 	if (IS_ERR(dev->fw_handler))
 		return PTR_ERR(dev->fw_handler);
 
-	dev->venc_pdata = of_device_get_match_data(&pdev->dev);
+	dma_set_max_seg_size(&pdev->dev, UINT_MAX);
+
 	ret = mtk_vcodec_init_enc_clk(dev);
 	if (ret < 0) {
 		dev_err(&pdev->dev, "[MTK VCODEC] Failed to get mtk vcodec clock source!");
@@ -389,6 +379,7 @@ static int mtk_vcodec_probe(struct platform_device *pdev)
 }
 
 static const struct mtk_vcodec_enc_pdata mt8173_avc_pdata = {
+	.venc_model_num = 8173,
 	.capture_formats = mtk_video_formats_capture_h264,
 	.num_capture_formats = ARRAY_SIZE(mtk_video_formats_capture_h264),
 	.output_formats = mtk_video_formats_output,
@@ -396,9 +387,12 @@ static const struct mtk_vcodec_enc_pdata mt8173_avc_pdata = {
 	.min_bitrate = 64,
 	.max_bitrate = 60000000,
 	.core_id = VENC_SYS,
+	.fw_type = VPU,
+	.fw_init = mtk_vcodec_fw_vpu_init,
 };
 
 static const struct mtk_vcodec_enc_pdata mt8173_vp8_pdata = {
+	.venc_model_num = 8173,
 	.capture_formats = mtk_video_formats_capture_vp8,
 	.num_capture_formats = ARRAY_SIZE(mtk_video_formats_capture_vp8),
 	.output_formats = mtk_video_formats_output,
@@ -406,9 +400,12 @@ static const struct mtk_vcodec_enc_pdata mt8173_vp8_pdata = {
 	.min_bitrate = 64,
 	.max_bitrate = 9000000,
 	.core_id = VENC_LT_SYS,
+	.fw_type = VPU,
+	.fw_init = mtk_vcodec_fw_vpu_init,
 };
 
 static const struct mtk_vcodec_enc_pdata mt8183_pdata = {
+	.venc_model_num = 8183,
 	.uses_ext = true,
 	.capture_formats = mtk_video_formats_capture_h264,
 	.num_capture_formats = ARRAY_SIZE(mtk_video_formats_capture_h264),
@@ -417,9 +414,12 @@ static const struct mtk_vcodec_enc_pdata mt8183_pdata = {
 	.min_bitrate = 64,
 	.max_bitrate = 40000000,
 	.core_id = VENC_SYS,
+	.fw_type = SCP,
+	.fw_init = mtk_vcodec_fw_scp_init,
 };
 
 static const struct mtk_vcodec_enc_pdata mt8188_pdata = {
+	.venc_model_num = 8188,
 	.uses_ext = true,
 	.capture_formats = mtk_video_formats_capture_h264,
 	.num_capture_formats = ARRAY_SIZE(mtk_video_formats_capture_h264),
@@ -429,9 +429,12 @@ static const struct mtk_vcodec_enc_pdata mt8188_pdata = {
 	.max_bitrate = 50000000,
 	.core_id = VENC_SYS,
 	.uses_34bit = true,
+	.fw_type = SCP,
+	.fw_init = mtk_vcodec_fw_scp_init,
 };
 
 static const struct mtk_vcodec_enc_pdata mt8192_pdata = {
+	.venc_model_num = 8192,
 	.uses_ext = true,
 	.capture_formats = mtk_video_formats_capture_h264,
 	.num_capture_formats = ARRAY_SIZE(mtk_video_formats_capture_h264),
@@ -440,9 +443,12 @@ static const struct mtk_vcodec_enc_pdata mt8192_pdata = {
 	.min_bitrate = 64,
 	.max_bitrate = 100000000,
 	.core_id = VENC_SYS,
+	.fw_type = SCP,
+	.fw_init = mtk_vcodec_fw_scp_init,
 };
 
 static const struct mtk_vcodec_enc_pdata mt8195_pdata = {
+	.venc_model_num = 8195,
 	.uses_ext = true,
 	.capture_formats = mtk_video_formats_capture_h264,
 	.num_capture_formats = ARRAY_SIZE(mtk_video_formats_capture_h264),
@@ -451,6 +457,8 @@ static const struct mtk_vcodec_enc_pdata mt8195_pdata = {
 	.min_bitrate = 64,
 	.max_bitrate = 100000000,
 	.core_id = VENC_SYS,
+	.fw_type = SCP,
+	.fw_init = mtk_vcodec_fw_scp_init,
 };
 
 static const struct of_device_id mtk_vcodec_enc_match[] = {
diff --git a/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc_drv.h b/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc_drv.h
index 934ff648125d..6c7e8da6d8ee 100644
--- a/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc_drv.h
+++ b/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc_drv.h
@@ -20,6 +20,7 @@
 /**
  * struct mtk_vcodec_enc_pdata - compatible data for each IC
  *
+ * @venc_model_num: encoder model number
  * @uses_ext: whether the encoder uses the extended firmware messaging format
  * @min_bitrate: minimum supported encoding bitrate
  * @max_bitrate: maximum supported encoding bitrate
@@ -29,8 +30,12 @@
  * @num_output_formats: number of entries in output_formats
  * @core_id: stand for h264 or vp8 encode index
  * @uses_34bit: whether the encoder uses 34-bit iova
+ * @fw_type: firmware type (VPU, SCP, or VCP)
+ * @fw_init: firmware-specific initialization callback
+ * @ipi_id: IPI ID for encoder communication with firmware
  */
 struct mtk_vcodec_enc_pdata {
+	u16 venc_model_num;
 	bool uses_ext;
 	u64 min_bitrate;
 	u64 max_bitrate;
@@ -40,6 +45,9 @@ struct mtk_vcodec_enc_pdata {
 	size_t num_output_formats;
 	u8 core_id;
 	bool uses_34bit;
+	enum mtk_vcodec_fw_type fw_type;
+	struct mtk_vcodec_fw *(*fw_init)(void *priv, enum mtk_vcodec_fw_use fw_use);
+	int ipi_id;
 };
 
 /*
@@ -174,7 +182,6 @@ struct mtk_vcodec_enc_ctx {
  * @venc_pdata: encoder IC-specific data
  *
  * @fw_handler: used to communicate with the firmware.
- * @fw_init: firmware-specific init callback selected at probe time
  * @id_counter: used to identify current opened instance
  *
  * @enc_mutex: encoder hardware lock.
@@ -202,7 +209,6 @@ struct mtk_vcodec_enc_dev {
 	const struct mtk_vcodec_enc_pdata *venc_pdata;
 
 	struct mtk_vcodec_fw *fw_handler;
-	struct mtk_vcodec_fw *(*fw_init)(void *priv, enum mtk_vcodec_fw_use fw_use);
 	u64 id_counter;
 
 	/* encoder hardware mutex lock */
-- 
2.45.2



^ permalink raw reply related

* [PATCH v7 0/6] Add support for MT8196 video encoder
From: Irui Wang @ 2026-06-05  9:35 UTC (permalink / raw)
  To: Hans Verkuil, Mauro Carvalho Chehab, Rob Herring,
	Matthias Brugger, Krzysztof Kozlowski, angelogioacchino.delregno,
	nicolas.dufresne, Tiffany Lin, kyrie wu
  Cc: Yunfei Dong, Maoguang Meng, Longfei Wang, Irui Wang,
	Project_Global_Chrome_Upstream_Group, linux-media, devicetree,
	linux-kernel, linux-arm-kernel, linux-mediatek

This patch series add support for MT8196 video encoder.
patch 1: Add dt-bindings.
patch 2: Add new encoder driver platform data.
patch 3~5: Add a new encoder driver interface for new VCP firmware.
patch 6: Add compatible data.

About adding new driver support, the v4l2-compliance report shows:
"Total for mtk-vcodec-enc device /dev/video3: 47, Succeeded: 46, Failed: 1, Warnings: 0"
The 1 Failed case is not caused by current patch set:
                fail: v4l2-test-controls.cpp(1171): node->codec_mask & STATEFUL_ENCODER
        test VIDIOC_(UN)SUBSCRIBE_EVENT/DQEVENT: FAIL

This patch set depends on "media: mediatek: vcodec: support video decoder in mt8196"[1]

[1] https://patchwork.linuxtv.org/project/linux-media/list/?series=25981

Change in v7:
 - patch 2: New pdata members for remove if-else device tree parsing statement.
 - Rebase patch onto decoder's patch set.
 - Link to v6: https://patchwork.linuxtv.org/project/linux-media/cover/20260423073345.27402-1-irui.wang@mediatek.com/

Change in v6:
 - Move dt-bindings to patch 1.
 - Rebase patch onto decoder's patch set.
 - Link to v5: https://patchwork.linuxtv.org/project/linux-media/cover/20260302035244.8994-1-irui.wang@mediatek.com/

Change in v5:
 - Rewrite patch5 commit subject.
 - Add else statement in patch5.
 - Link to v4: https://patchwork.linuxtv.org/project/linux-media/list/?series=21757

Change in v4:
 - Rework patch3 commit message.
 - Rework patch5 commit with more details.
 - Rebase patch onto decoder's patch set.
 - Link to v3: https://patchwork.linuxtv.org/project/linux-media/cover/20250814085642.17343-1-kyrie.wu@mediatek.com/

Change in v3:
 - Add venc rc buffer alloc failure error handling.
 - Add mediatek,vcp property definition in dt-bindning.

Change in v2:
 - Add support for VCP encode process.
 - Add MT8196 encoder driver platform data.
 - Rebase encoder patch onto decoder's patch set.
 - Fix some review comments in v1.

Irui Wang (6):
  media: dt-bindings: mediatek,vcodec-encoder: Add MT8196
  media: mediatek: encoder: Add new platform data members
  media: mediatek: encoder: Add a new encoder driver interface
  media: mediatek: encoder: Add support for common firmware interface
  media: mediatek: encoder: Add support for VCP encode process
  media: mediatek: encoder: Add MT8196 encoder compatible data

 .../media/mediatek,vcodec-encoder.yaml        |  22 +
 .../mediatek/vcodec/common/mtk_vcodec_fw.c    |   9 +-
 .../mediatek/vcodec/common/mtk_vcodec_fw.h    |   1 +
 .../vcodec/common/mtk_vcodec_fw_priv.h        |   1 +
 .../vcodec/common/mtk_vcodec_fw_vcp.c         |   6 +
 .../platform/mediatek/vcodec/encoder/Makefile |   1 +
 .../mediatek/vcodec/encoder/mtk_vcodec_enc.c  |  36 +-
 .../vcodec/encoder/mtk_vcodec_enc_drv.c       |  62 +-
 .../vcodec/encoder/mtk_vcodec_enc_drv.h       |  23 +-
 .../vcodec/encoder/venc/venc_common_if.c      | 684 ++++++++++++++++++
 .../vcodec/encoder/venc/venc_h264_if.c        |   8 +-
 .../mediatek/vcodec/encoder/venc_drv_if.c     |   3 +-
 .../mediatek/vcodec/encoder/venc_drv_if.h     |  11 +-
 .../mediatek/vcodec/encoder/venc_ipi_msg.h    |  26 +
 .../mediatek/vcodec/encoder/venc_vpu_if.c     |  47 +-
 15 files changed, 869 insertions(+), 71 deletions(-)
 create mode 100644 drivers/media/platform/mediatek/vcodec/encoder/venc/venc_common_if.c

-- 
2.45.2



^ permalink raw reply

* Re: [PATCH] pinctrl: Move Airoha driver to dedicated directory
From: Benjamin Larsson @ 2026-06-05  9:14 UTC (permalink / raw)
  To: linux-mediatek
In-Reply-To: <20260605071233.28873-1-ansuelsmth@gmail.com>

Hi.

On 6/5/26 09:12, Christian Marangi wrote:
> In preparation for additional SoC support, move the Airoha pinctrl driver
> for AN7581 SoC to a dedicated directory.
>
> This is to tidy things up and keep code organized without polluting the
> Mediatek driver directory.
>
> The driver doesn't depend on any generic or common code from the Mediatek
> codebase so it can be safely moved without any modification.
>
> Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
> ---

For the record I plan on working on/assisting the following platforms: 
EN751221, EN7528, EN7580, EN7523 and AN7563/AN7552. There is a patch set 
under review in uboot that we hopefully can port over to linux. Any 
assistance/comments there would be much appreciated.

MvH

Benjamin Larsson



^ permalink raw reply

* [PATCH v6 4/4] media: platform: mediatek: Add MT8188 AIE driver
From: Sarang Chaudhari @ 2026-06-05  8:30 UTC (permalink / raw)
  To: Rob Herring, AngeloGioacchino Del Regno, Mauro Carvalho Chehab,
	linux-kernel, linux-arm-kernel, linux-mediatek
  Cc: zhaoyuan.chen, Teddy.Chen, Project_Global_Chrome_Upstream_Group,
	Sarang Chaudhari

Add the MediaTek AI Engine (AIE) V4L2 memory-to-memory driver for the
MT8188 SoC. The AIE hardware accelerator provides face detection, facial
landmark detection (FLD), and face attribute analysis (age, gender, race)
capabilities.

The driver implements:
- V4L2 mem2mem device with multi-planar video and metadata capture
- Three operation modes: face detection (FD), attribute analysis, and
  facial landmark detection (FLD) using Binary Tree Traversal
- Pyramid-based multi-scale face detection (640x480 base, 2x downscale
  per level)
- Hardware configuration via DMA descriptor tables
- Clock and power management integration

Signed-off-by: Sarang Chaudhari <sarang.chaudhari@mediatek.com>
---

diff --git a/drivers/media/platform/mediatek/Kconfig b/drivers/media/platform/mediatek/Kconfig
index 84104e2..987c754 100644
--- a/drivers/media/platform/mediatek/Kconfig
+++ b/drivers/media/platform/mediatek/Kconfig
@@ -7,3 +7,4 @@
 source "drivers/media/platform/mediatek/vcodec/Kconfig"
 source "drivers/media/platform/mediatek/vpu/Kconfig"
 source "drivers/media/platform/mediatek/mdp3/Kconfig"
+source "drivers/media/platform/mediatek/aie/Kconfig"
diff --git a/drivers/media/platform/mediatek/Makefile b/drivers/media/platform/mediatek/Makefile
index 38e6ba9..fc4fc50 100644
--- a/drivers/media/platform/mediatek/Makefile
+++ b/drivers/media/platform/mediatek/Makefile
@@ -4,3 +4,4 @@
 obj-y += vcodec/
 obj-y += vpu/
 obj-y += mdp3/
+obj-$(CONFIG_VIDEO_MTK_AIE) += aie/
diff --git a/drivers/media/platform/mediatek/aie/Kconfig b/drivers/media/platform/mediatek/aie/Kconfig
new file mode 100644
index 0000000..ffe7510
--- /dev/null
+++ b/drivers/media/platform/mediatek/aie/Kconfig
@@ -0,0 +1,20 @@
+config VIDEO_MTK_AIE
+	tristate "MediaTek AI Engine (AIE) driver"
+	depends on VIDEO_DEV
+	depends on OF
+	select V4L2_MEM2MEM_DEV
+	select VIDEOBUF2_DMA_CONTIG
+	select MEDIA_CONTROLLER
+	help
+	  Enable support for the MediaTek AI Engine (AIE) hardware
+	  accelerator for face detection and analysis.
+
+	  The AIE driver is a V4L2 memory-to-memory device driver that
+	  provides hardware-accelerated face detection, facial landmark
+	  detection, and attribute analysis (age, gender, ethnicity)
+	  functions. It can detect multiple faces of various sizes in
+	  raw image data.
+
+	  To compile this driver as a module, choose M here: the module
+	  will be called mtk-aie.
+
diff --git a/drivers/media/platform/mediatek/aie/Makefile b/drivers/media/platform/mediatek/aie/Makefile
new file mode 100644
index 0000000..75d3466
--- /dev/null
+++ b/drivers/media/platform/mediatek/aie/Makefile
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: GPL-2.0
+
+mtk-aie-y := mtk_aie_v4l2.o mtk_aie_drv.o
+
+obj-$(CONFIG_VIDEO_MTK_AIE) += mtk-aie.o
diff --git a/drivers/media/platform/mediatek/aie/mtk_aie.h b/drivers/media/platform/mediatek/aie/mtk_aie.h
new file mode 100644
index 0000000..d54f640
--- /dev/null
+++ b/drivers/media/platform/mediatek/aie/mtk_aie.h
@@ -0,0 +1,1045 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2018 MediaTek Inc.
+ */
+
+#ifndef __MTK_AIE_H__
+#define __MTK_AIE_H__
+
+#include <linux/completion.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <linux/pm_opp.h>
+#include <linux/regulator/consumer.h>
+#include <linux/types.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/videobuf2-v4l2.h>
+
+/* AIE 3.X specific features */
+
+#ifdef CONFIG_INTERCONNECT_MTK_EXTENSION
+#include "mtk-interconnect.h"
+#endif
+
+/* AIE 3.X DVFS and clock management */
+#define MTK_AIE_OPP_SET 1
+#define MTK_AIE_CLK_LEVEL_CNT 4
+
+#define FD_VERSION 1946050
+#define ATTR_VERSION 1929401
+
+#define Y2R_SRC_DST_FORMAT 0
+#define Y2R_IN_W_H 1
+#define Y2R_OUT_W_H 2
+#define Y2R_RA0_RA1_EN 3
+#define Y2R_IN_X_Y_SIZE0 4
+#define Y2R_IN_STRIDE0_BUS_SIZE0 5
+#define Y2R_IN_X_Y_SIZE1 6
+#define Y2R_IN_STRIDE1_BUS_SIZE1 7
+#define Y2R_OUT_X_Y_SIZE0 8
+#define Y2R_OUT_STRIDE0_BUS_SIZE0 9
+#define Y2R_OUT_X_Y_SIZE1 10
+#define Y2R_OUT_STRIDE1_BUS_SIZE1 11
+#define Y2R_OUT_X_Y_SIZE2 12
+#define Y2R_OUT_STRIDE2_BUS_SIZE2 13
+#define Y2R_IN_0 14
+#define Y2R_IN_1 15
+#define Y2R_OUT_0 16
+#define Y2R_OUT_1 17
+#define Y2R_OUT_2 18
+#define Y2R_RS_SEL_SRZ_EN 19
+#define Y2R_X_Y_MAG 20
+#define Y2R_SRZ_HORI_STEP 22
+#define Y2R_SRZ_VERT_STEP 23
+#define Y2R_PADDING_EN_UP_DOWN 26
+#define Y2R_PADDING_RIGHT_LEFT 27
+#define Y2R_CO2_FMT_MODE_EN 28 /* AIE3.0 new */
+#define Y2R_CO2_CROP_X 29 /* AIE3.0 new */
+#define Y2R_CO2_CROP_Y 30 /* AIE3.0 new */
+#define Y2R_CON_IN_BA_MSB 31
+#define Y2R_CON_OUT_BA_MSB 32
+
+#define RS_IN_0 22
+#define RS_IN_1 23
+#define RS_IN_2 24
+#define RS_OUT_0 25
+#define RS_OUT_1 26
+#define RS_OUT_2 27
+#define RS_X_Y_MAG 1
+#define RS_SRZ_HORI_STEP 3
+#define RS_SRZ_VERT_STEP 4
+#define RS_INPUT_W_H 7
+#define RS_OUTPUT_W_H 8
+#define RS_IN_X_Y_SIZE0 10
+#define RS_IN_STRIDE0 11
+#define RS_IN_X_Y_SIZE1 12
+#define RS_IN_STRIDE1 13
+#define RS_IN_X_Y_SIZE2 14
+#define RS_IN_STRIDE2 15
+#define RS_OUT_X_Y_SIZE0 16
+#define RS_OUT_STRIDE0 17
+#define RS_OUT_X_Y_SIZE1 18
+#define RS_OUT_STRIDE1 19
+#define RS_OUT_X_Y_SIZE2 20
+#define RS_OUT_STRIDE2 21
+/* AIE 3.X RS configuration for MSB addresses */
+#define RS_CON_IN_BA_MSB 28
+#define RS_CON_OUT_BA_MSB 29
+
+#define FD_INPUT_ROTATE 1
+#define FD_CONV_WIDTH_MOD6 2
+#define FD_CONV_IMG_W_H 4
+
+#define FD_IN_IMG_W_H 5
+#define FD_OUT_IMG_W_H 6
+
+#define FD_IN_X_Y_SIZE0 9
+#define FD_IN_X_Y_SIZE1 11
+#define FD_IN_X_Y_SIZE2 13
+#define FD_IN_X_Y_SIZE3 15
+
+#define FD_IN_STRIDE0_BUS_SIZE0 10
+#define FD_IN_STRIDE1_BUS_SIZE1 12
+#define FD_IN_STRIDE2_BUS_SIZE2 14
+#define FD_IN_STRIDE3_BUS_SIZE3 16
+
+#define FD_OUT_X_Y_SIZE0 17
+#define FD_OUT_X_Y_SIZE1 19
+#define FD_OUT_X_Y_SIZE2 21
+#define FD_OUT_X_Y_SIZE3 23
+
+#define FD_OUT_STRIDE0_BUS_SIZE0 18
+#define FD_OUT_STRIDE1_BUS_SIZE1 20
+#define FD_OUT_STRIDE2_BUS_SIZE2 22
+#define FD_OUT_STRIDE3_BUS_SIZE3 24
+
+#define FD_IN_0 27
+#define FD_IN_1 28
+#define FD_IN_2 29
+#define FD_IN_3 30
+
+#define FD_OUT_0 31
+#define FD_OUT_1 32
+#define FD_OUT_2 33
+#define FD_OUT_3 34
+
+#define FD_KERNEL_0 35
+#define FD_KERNEL_1 36
+
+#define FD_RPN_SET 37
+#define FD_IMAGE_COORD 38
+#define FD_IMAGE_COORD_XY_OFST 39 /* AIE3.0 new */
+#define FD_BIAS_ACCU 47 /* AIE3.0 new */
+#define FD_SRZ_FDRZ_RS 48 /* AIE3.0 new */
+#define FD_SRZ_HORI_STEP 49 /* AIE3.0 new */
+#define FD_SRZ_VERT_STEP 50 /* AIE3.0 new */
+#define FD_SRZ_HORI_SUB_INT_OFST 51 /* AIE3.0 new */
+#define FD_SRZ_VERT_SUB_INT_OFST 52 /* AIE3.0 new */
+/* AIE 3.X FD configuration for MSB addresses */
+#define FD_CON_IN_BA_MSB 53
+#define FD_CON_OUT_BA_MSB 54
+#define FD_CON_KERNEL_BA_MSB 55
+
+#define SRZ_BIT (BIT_MASK(16) | BIT_MASK(12) | BIT_MASK(8) | BIT_MASK(0))
+
+#define FD_LOOP_NUM 87
+#define RPN0_LOOP_NUM 86
+#define RPN1_LOOP_NUM 57
+#define RPN2_LOOP_NUM 28
+
+#define PYM0_START_LOOP 58
+#define PYM1_START_LOOP 29
+#define PYM2_START_LOOP 0
+
+#define ATTR_LOOP_NUM 26
+#define AGE_OUT_RGS 17
+#define GENDER_OUT_RGS 20
+#define INDIAN_OUT_RGS 22
+#define RACE_OUT_RGS 25
+
+#define INPUT_WDMA_WRA_NUM 4
+#define OUTPUT_WDMA_WRA_NUM 4
+#define KERNEL_RDMA_RA_NUM 2
+
+#define MAX_ENQUE_FRAME_NUM 10
+#define PYM_NUM 3
+#define COLOR_NUM 3
+
+#define ATTR_MODE_PYRAMID_WIDTH 128
+#define ATTR_OUT_SIZE 32
+
+/* AIE 2.0 3.X register offset */
+#define AIE_START_REG 0x000
+#define AIE_ENABLE_REG 0x004
+#define AIE_LOOP_REG 0x008
+#define AIE_YUV2RGB_CON_BASE_ADR_REG 0x00c
+#define AIE_RS_CON_BASE_ADR_REG 0x010
+#define AIE_FD_CON_BASE_ADR_REG 0x014
+#define AIE_INT_EN_REG 0x018
+#define AIE_INT_REG 0x01c
+#define AIE_RESULT_0_REG 0x08c
+#define AIE_RESULT_1_REG 0x090
+#define AIE_DMA_CTL_REG 0x094
+
+/* AIE 3.0 register offset */
+#define AIE_YUV2RGB_CON_BASE_ADR_MSB 0x14C
+#define AIE_RS_CON_BASE_ADR_MSB 0x150
+#define AIE_FD_CON_BASE_ADR_MSB 0x154
+
+/* AIE 3.0 FLD register offset */
+#define FLD_EN 0x400
+#define FLD_BASE_ADDR_FACE_0 0x404
+#define FLD_BASE_ADDR_FACE_1 0x408
+#define FLD_BASE_ADDR_FACE_2 0x40C
+#define FLD_BASE_ADDR_FACE_3 0x410
+#define FLD_BASE_ADDR_FACE_4 0x414
+#define FLD_BASE_ADDR_FACE_5 0x418
+#define FLD_BASE_ADDR_FACE_6 0x41C
+#define FLD_BASE_ADDR_FACE_7 0x420
+#define FLD_BASE_ADDR_FACE_8 0x424
+#define FLD_BASE_ADDR_FACE_9 0x428
+#define FLD_BASE_ADDR_FACE_10 0x42C
+#define FLD_BASE_ADDR_FACE_11 0x430
+#define FLD_BASE_ADDR_FACE_12 0x434
+#define FLD_BASE_ADDR_FACE_13 0x438
+#define FLD_BASE_ADDR_FACE_14 0x43C
+
+#define FLD_INFO_0_FACE_0 0x440
+#define FLD_INFO_1_FACE_0 0x444
+#define FLD_INFO_2_FACE_0 0x448
+#define FLD_INFO_0_FACE_1 0x44C
+#define FLD_INFO_1_FACE_1 0x450
+#define FLD_INFO_2_FACE_1 0x454
+#define FLD_INFO_0_FACE_2 0x458
+#define FLD_INFO_1_FACE_2 0x45C
+#define FLD_INFO_2_FACE_2 0x460
+#define FLD_INFO_0_FACE_3 0x464
+#define FLD_INFO_1_FACE_3 0x468
+#define FLD_INFO_2_FACE_3 0x46C
+#define FLD_INFO_0_FACE_4 0x470
+#define FLD_INFO_1_FACE_4 0x474
+#define FLD_INFO_2_FACE_4 0x478
+#define FLD_INFO_0_FACE_5 0x47C
+#define FLD_INFO_1_FACE_5 0x480
+#define FLD_INFO_2_FACE_5 0x484
+#define FLD_INFO_0_FACE_6 0x488
+#define FLD_INFO_1_FACE_6 0x48C
+#define FLD_INFO_2_FACE_6 0x490
+#define FLD_INFO_0_FACE_7 0x494
+#define FLD_INFO_1_FACE_7 0x498
+
+#define FLD_INFO_2_FACE_7 0x4A0
+#define FLD_INFO_0_FACE_8 0x4A4
+#define FLD_INFO_1_FACE_8 0x4A8
+#define FLD_INFO_2_FACE_8 0x4AC
+#define FLD_INFO_0_FACE_9 0x4B0
+#define FLD_INFO_1_FACE_9 0x4B4
+#define FLD_INFO_2_FACE_9 0x4B8
+#define FLD_INFO_0_FACE_10 0x4BC
+#define FLD_INFO_1_FACE_10 0x4C0
+#define FLD_INFO_2_FACE_10 0x4C4
+#define FLD_INFO_0_FACE_11 0x4C8
+#define FLD_INFO_1_FACE_11 0x4CC
+#define FLD_INFO_2_FACE_11 0x4D0
+#define FLD_INFO_0_FACE_12 0x4D4
+#define FLD_INFO_1_FACE_12 0x4D8
+#define FLD_INFO_2_FACE_12 0x4DC
+#define FLD_INFO_0_FACE_13 0x4E0
+#define FLD_INFO_1_FACE_13 0x4E4
+#define FLD_INFO_2_FACE_13 0x4E8
+#define FLD_INFO_0_FACE_14 0x4EC
+#define FLD_INFO_1_FACE_14 0x4F0
+#define FLD_INFO_2_FACE_14 0x4F4
+
+#define FLD_MODEL_PARA0 0x4F8
+#define FLD_MODEL_PARA1 0x4FC
+#define FLD_MODEL_PARA2 0x500
+#define FLD_MODEL_PARA3 0x504
+#define FLD_MODEL_PARA4 0x508
+#define FLD_MODEL_PARA5 0x50C
+#define FLD_MODEL_PARA6 0x510
+#define FLD_MODEL_PARA7 0x514
+#define FLD_MODEL_PARA8 0x518
+#define FLD_MODEL_PARA9 0x51C
+#define FLD_MODEL_PARA10 0x520
+#define FLD_MODEL_PARA11 0x524
+#define FLD_MODEL_PARA12 0x528
+#define FLD_MODEL_PARA13 0x52C
+#define FLD_MODEL_PARA14 0x530
+#define FLD_MODEL_PARA15 0x534
+#define FLD_MODEL_PARA16 0x538
+#define FLD_DEBUG_INFO0 0x53C
+#define FLD_DEBUG_INFO1 0x540
+
+#define FLD_BUSY 0x544
+#define FLD_DONE 0x548
+#define FLD_SRC_WD_HT 0x54C
+
+#define FLD_PL_IN_BASE_ADDR_0_0 0x550
+#define FLD_PL_IN_BASE_ADDR_0_1 0x554
+#define FLD_PL_IN_BASE_ADDR_0_2 0x558
+#define FLD_PL_IN_BASE_ADDR_0_3 0x55C
+#define FLD_PL_IN_BASE_ADDR_0_4 0x560
+#define FLD_PL_IN_BASE_ADDR_0_5 0x564
+#define FLD_PL_IN_BASE_ADDR_0_6 0x568
+#define FLD_PL_IN_BASE_ADDR_0_7 0x56C
+#define FLD_PL_IN_BASE_ADDR_0_8 0x570
+#define FLD_PL_IN_BASE_ADDR_0_9 0x574
+#define FLD_PL_IN_BASE_ADDR_0_10 0x578
+#define FLD_PL_IN_BASE_ADDR_0_11 0x57C
+#define FLD_PL_IN_BASE_ADDR_0_12 0x580
+#define FLD_PL_IN_BASE_ADDR_0_13 0x584
+#define FLD_PL_IN_BASE_ADDR_0_14 0x588
+#define FLD_PL_IN_BASE_ADDR_0_15 0x58C
+#define FLD_PL_IN_BASE_ADDR_0_16 0x590
+#define FLD_PL_IN_BASE_ADDR_0_17 0x594
+#define FLD_PL_IN_BASE_ADDR_0_18 0x598
+#define FLD_PL_IN_BASE_ADDR_0_19 0x59C
+#define FLD_PL_IN_BASE_ADDR_0_20 0x5A0
+#define FLD_PL_IN_BASE_ADDR_0_21 0x5A4
+#define FLD_PL_IN_BASE_ADDR_0_22 0x5A8
+#define FLD_PL_IN_BASE_ADDR_0_23 0x5AC
+#define FLD_PL_IN_BASE_ADDR_0_24 0x5B0
+#define FLD_PL_IN_BASE_ADDR_0_25 0x5B4
+#define FLD_PL_IN_BASE_ADDR_0_26 0x5B8
+#define FLD_PL_IN_BASE_ADDR_0_27 0x5BC
+#define FLD_PL_IN_BASE_ADDR_0_28 0x5C0
+#define FLD_PL_IN_BASE_ADDR_0_29 0x5C4
+
+#define FLD_PL_IN_BASE_ADDR_1_0 0x5C8
+#define FLD_PL_IN_BASE_ADDR_1_1 0x5CC
+#define FLD_PL_IN_BASE_ADDR_1_2 0x5D0
+#define FLD_PL_IN_BASE_ADDR_1_3 0x5D4
+#define FLD_PL_IN_BASE_ADDR_1_4 0x5D8
+#define FLD_PL_IN_BASE_ADDR_1_5 0x5DC
+#define FLD_PL_IN_BASE_ADDR_1_6 0x5E0
+#define FLD_PL_IN_BASE_ADDR_1_7 0x5E4
+#define FLD_PL_IN_BASE_ADDR_1_8 0x5E8
+#define FLD_PL_IN_BASE_ADDR_1_9 0x5EC
+#define FLD_PL_IN_BASE_ADDR_1_10 0x5F0
+#define FLD_PL_IN_BASE_ADDR_1_11 0x5F4
+#define FLD_PL_IN_BASE_ADDR_1_12 0x5F8
+#define FLD_PL_IN_BASE_ADDR_1_13 0x5FC
+#define FLD_PL_IN_BASE_ADDR_1_14 0x600
+#define FLD_PL_IN_BASE_ADDR_1_15 0x604
+#define FLD_PL_IN_BASE_ADDR_1_16 0x608
+#define FLD_PL_IN_BASE_ADDR_1_17 0x60C
+#define FLD_PL_IN_BASE_ADDR_1_18 0x610
+#define FLD_PL_IN_BASE_ADDR_1_19 0x614
+#define FLD_PL_IN_BASE_ADDR_1_20 0x618
+#define FLD_PL_IN_BASE_ADDR_1_21 0x61C
+#define FLD_PL_IN_BASE_ADDR_1_22 0x620
+#define FLD_PL_IN_BASE_ADDR_1_23 0x624
+#define FLD_PL_IN_BASE_ADDR_1_24 0x628
+#define FLD_PL_IN_BASE_ADDR_1_25 0x62C
+#define FLD_PL_IN_BASE_ADDR_1_26 0x630
+#define FLD_PL_IN_BASE_ADDR_1_27 0x634
+#define FLD_PL_IN_BASE_ADDR_1_28 0x638
+#define FLD_PL_IN_BASE_ADDR_1_29 0x63C
+
+#define FLD_PL_IN_BASE_ADDR_2_0 0x640
+#define FLD_PL_IN_BASE_ADDR_2_1 0x644
+#define FLD_PL_IN_BASE_ADDR_2_2 0x648
+#define FLD_PL_IN_BASE_ADDR_2_3 0x64C
+#define FLD_PL_IN_BASE_ADDR_2_4 0x650
+#define FLD_PL_IN_BASE_ADDR_2_5 0x654
+#define FLD_PL_IN_BASE_ADDR_2_6 0x658
+#define FLD_PL_IN_BASE_ADDR_2_7 0x65C
+#define FLD_PL_IN_BASE_ADDR_2_8 0x660
+#define FLD_PL_IN_BASE_ADDR_2_9 0x664
+#define FLD_PL_IN_BASE_ADDR_2_10 0x668
+#define FLD_PL_IN_BASE_ADDR_2_11 0x66C
+#define FLD_PL_IN_BASE_ADDR_2_12 0x670
+#define FLD_PL_IN_BASE_ADDR_2_13 0x674
+#define FLD_PL_IN_BASE_ADDR_2_14 0x678
+#define FLD_PL_IN_BASE_ADDR_2_15 0x67C
+#define FLD_PL_IN_BASE_ADDR_2_16 0x680
+#define FLD_PL_IN_BASE_ADDR_2_17 0x684
+#define FLD_PL_IN_BASE_ADDR_2_18 0x688
+#define FLD_PL_IN_BASE_ADDR_2_19 0x68C
+#define FLD_PL_IN_BASE_ADDR_2_20 0x690
+#define FLD_PL_IN_BASE_ADDR_2_21 0x694
+#define FLD_PL_IN_BASE_ADDR_2_22 0x698
+#define FLD_PL_IN_BASE_ADDR_2_23 0x69C
+#define FLD_PL_IN_BASE_ADDR_2_24 0x6A0
+#define FLD_PL_IN_BASE_ADDR_2_25 0x6A4
+#define FLD_PL_IN_BASE_ADDR_2_26 0x6A8
+#define FLD_PL_IN_BASE_ADDR_2_27 0x6AC
+#define FLD_PL_IN_BASE_ADDR_2_28 0x6B0
+#define FLD_PL_IN_BASE_ADDR_2_29 0x6B4
+
+#define FLD_PL_IN_BASE_ADDR_3_0 0x6B8
+#define FLD_PL_IN_BASE_ADDR_3_1 0x6BC
+#define FLD_PL_IN_BASE_ADDR_3_2 0x6C0
+#define FLD_PL_IN_BASE_ADDR_3_3 0x6C4
+#define FLD_PL_IN_BASE_ADDR_3_4 0x6C8
+#define FLD_PL_IN_BASE_ADDR_3_5 0x6CC
+#define FLD_PL_IN_BASE_ADDR_3_6 0x6D0
+#define FLD_PL_IN_BASE_ADDR_3_7 0x6D4
+#define FLD_PL_IN_BASE_ADDR_3_8 0x6D8
+#define FLD_PL_IN_BASE_ADDR_3_9 0x6DC
+#define FLD_PL_IN_BASE_ADDR_3_10 0x6E0
+#define FLD_PL_IN_BASE_ADDR_3_11 0x6E4
+#define FLD_PL_IN_BASE_ADDR_3_12 0x6E8
+#define FLD_PL_IN_BASE_ADDR_3_13 0x6EC
+#define FLD_PL_IN_BASE_ADDR_3_14 0x6F0
+#define FLD_PL_IN_BASE_ADDR_3_15 0x6F4
+#define FLD_PL_IN_BASE_ADDR_3_16 0x6F8
+#define FLD_PL_IN_BASE_ADDR_3_17 0x6FC
+#define FLD_PL_IN_BASE_ADDR_3_18 0x700
+#define FLD_PL_IN_BASE_ADDR_3_19 0x704
+#define FLD_PL_IN_BASE_ADDR_3_20 0x708
+#define FLD_PL_IN_BASE_ADDR_3_21 0x70C
+#define FLD_PL_IN_BASE_ADDR_3_22 0x710
+#define FLD_PL_IN_BASE_ADDR_3_23 0x714
+#define FLD_PL_IN_BASE_ADDR_3_24 0x718
+#define FLD_PL_IN_BASE_ADDR_3_25 0x71C
+#define FLD_PL_IN_BASE_ADDR_3_26 0x720
+#define FLD_PL_IN_BASE_ADDR_3_27 0x724
+#define FLD_PL_IN_BASE_ADDR_3_28 0x728
+#define FLD_PL_IN_BASE_ADDR_3_29 0x72C
+
+#define FLD_PL_IN_SIZE_0 0x730
+#define FLD_PL_IN_STRIDE_0 0x734
+#define FLD_PL_IN_SIZE_1 0x738
+#define FLD_PL_IN_STRIDE_1 0x73C
+#define FLD_PL_IN_SIZE_2_0 0x740
+#define FLD_PL_IN_STRIDE_2_0 0x744
+#define FLD_PL_IN_SIZE_2_1 0x748
+#define FLD_PL_IN_STRIDE_2_1 0x74C
+#define FLD_PL_IN_SIZE_2_2 0x750
+#define FLD_PL_IN_STRIDE_2_2 0x754
+#define FLD_PL_IN_SIZE_3 0x758
+#define FLD_PL_IN_STRIDE_3 0x75C
+
+#define FLD_SH_IN_BASE_ADDR_0 0x760
+#define FLD_SH_IN_BASE_ADDR_1 0x764
+#define FLD_SH_IN_BASE_ADDR_2 0x768
+#define FLD_SH_IN_BASE_ADDR_3 0x76C
+#define FLD_SH_IN_BASE_ADDR_4 0x770
+#define FLD_SH_IN_BASE_ADDR_5 0x774
+#define FLD_SH_IN_BASE_ADDR_6 0x778
+#define FLD_SH_IN_BASE_ADDR_7 0x77C
+#define FLD_SH_IN_BASE_ADDR_8 0x780
+#define FLD_SH_IN_BASE_ADDR_9 0x784
+#define FLD_SH_IN_BASE_ADDR_10 0x788
+#define FLD_SH_IN_BASE_ADDR_11 0x78C
+#define FLD_SH_IN_BASE_ADDR_12 0x790
+#define FLD_SH_IN_BASE_ADDR_13 0x794
+#define FLD_SH_IN_BASE_ADDR_14 0x798
+#define FLD_SH_IN_BASE_ADDR_15 0x79C
+#define FLD_SH_IN_BASE_ADDR_16 0x7A0
+#define FLD_SH_IN_BASE_ADDR_17 0x7A4
+#define FLD_SH_IN_BASE_ADDR_18 0x7A8
+#define FLD_SH_IN_BASE_ADDR_19 0x7AC
+#define FLD_SH_IN_BASE_ADDR_20 0x7B0
+#define FLD_SH_IN_BASE_ADDR_21 0x7B4
+#define FLD_SH_IN_BASE_ADDR_22 0x7B8
+#define FLD_SH_IN_BASE_ADDR_23 0x7BC
+#define FLD_SH_IN_BASE_ADDR_24 0x7C0
+#define FLD_SH_IN_BASE_ADDR_25 0x7C4
+#define FLD_SH_IN_BASE_ADDR_26 0x7C8
+#define FLD_SH_IN_BASE_ADDR_27 0x7CC
+#define FLD_SH_IN_BASE_ADDR_28 0x7D0
+#define FLD_SH_IN_BASE_ADDR_29 0x7D4
+
+#define FLD_SH_IN_SIZE_0 0x7D8
+#define FLD_SH_IN_STRIDE_0 0x7DC
+#define FLD_TR_OUT_BASE_ADDR_0 0x7E0
+#define FLD_TR_OUT_SIZE_0 0x7E4
+#define FLD_TR_OUT_STRIDE_0 0x7E8
+#define FLD_PP_OUT_BASE_ADDR_0 0x7EC
+#define FLD_PP_OUT_SIZE_0 0x7F0
+#define FLD_PP_OUT_STRIDE_0 0x7F4
+#define FLD_SPARE 0x7F8
+
+#define FLD_BASE_ADDR_FACE_0_7_MSB 0x7FC
+#define FLD_BASE_ADDR_FACE_8_14_MSB 0x800
+
+#define FLD_PL_IN_BASE_ADDR_0_0_7_MSB 0x804
+#define FLD_PL_IN_BASE_ADDR_0_8_15_MSB 0x808
+#define FLD_PL_IN_BASE_ADDR_0_16_23_MSB 0x80C
+#define FLD_PL_IN_BASE_ADDR_0_24_29_MSB 0x810
+
+#define FLD_PL_IN_BASE_ADDR_1_0_7_MSB 0x814
+#define FLD_PL_IN_BASE_ADDR_1_8_15_MSB 0x818
+#define FLD_PL_IN_BASE_ADDR_1_16_23_MSB 0x81C
+#define FLD_PL_IN_BASE_ADDR_1_24_29_MSB 0x820
+
+#define FLD_PL_IN_BASE_ADDR_2_0_7_MSB 0x824
+#define FLD_PL_IN_BASE_ADDR_2_8_15_MSB 0x828
+#define FLD_PL_IN_BASE_ADDR_2_16_23_MSB 0x82C
+#define FLD_PL_IN_BASE_ADDR_2_24_29_MSB 0x830
+
+#define FLD_PL_IN_BASE_ADDR_3_0_7_MSB 0x834
+#define FLD_PL_IN_BASE_ADDR_3_8_15_MSB 0x838
+#define FLD_PL_IN_BASE_ADDR_3_16_23_MSB 0x83C
+#define FLD_PL_IN_BASE_ADDR_3_24_29_MSB 0x840
+
+#define FLD_SH_IN_BASE_ADDR_0_7_MSB 0x844
+#define FLD_SH_IN_BASE_ADDR_8_15_MSB 0x848
+#define FLD_SH_IN_BASE_ADDR_16_23_MSB 0x84C
+#define FLD_SH_IN_BASE_ADDR_24_29_MSB 0x850
+
+#define FLD_BS_IN_BASE_ADDR_0_7_MSB 0x8d4
+#define FLD_BS_IN_BASE_ADDR_8_15_MSB 0x8d8
+
+#define FLD_TR_OUT_BASE_ADDR_0_MSB 0x854
+#define FLD_PP_OUT_BASE_ADDR_0_MSB 0x858
+
+#define FLD_BS_IN_BASE_ADDR_00 0x85C
+#define FLD_BS_IN_BASE_ADDR_01 0x860
+#define FLD_BS_IN_BASE_ADDR_02 0x864
+#define FLD_BS_IN_BASE_ADDR_03 0x868
+#define FLD_BS_IN_BASE_ADDR_04 0x86C
+#define FLD_BS_IN_BASE_ADDR_05 0x870
+#define FLD_BS_IN_BASE_ADDR_06 0x874
+#define FLD_BS_IN_BASE_ADDR_07 0x878
+#define FLD_BS_IN_BASE_ADDR_08 0x87C
+#define FLD_BS_IN_BASE_ADDR_09 0x880
+#define FLD_BS_IN_BASE_ADDR_10 0x884
+#define FLD_BS_IN_BASE_ADDR_11 0x888
+#define FLD_BS_IN_BASE_ADDR_12 0x88C
+#define FLD_BS_IN_BASE_ADDR_13 0x890
+#define FLD_BS_IN_BASE_ADDR_14 0x894
+#define FLD_BS_BIAS 0x8E4
+#define FLD_CV_FM_RANGE_0 0x8E8
+#define FLD_CV_FM_RANGE_1 0x8EC
+#define FLD_CV_PM_RANGE_0 0x8F0
+#define FLD_CV_PM_RANGE_1 0x8F4
+#define FLD_BS_RANGE_0 0x8F8
+#define FLD_BS_RANGE_1 0x8FC
+
+#define MTK_FD_OUTPUT_MIN_WIDTH 16U
+#define MTK_FD_OUTPUT_MIN_HEIGHT 16U
+#define MTK_FD_OUTPUT_MAX_WIDTH 4096U
+#define MTK_FD_OUTPUT_MAX_HEIGHT 4096U
+
+#define MTK_FD_HW_TIMEOUT_IN_MSEC 2000
+#define MAX_FACE_NUM 1024
+#define RLT_NUM 48
+#define GENDER_OUT 32
+
+#define RACE_RST_X_NUM 4
+#define RACE_RST_Y_NUM 64
+#define GENDER_RST_X_NUM 2
+#define GENDER_RST_Y_NUM 64
+#define MRACE_RST_NUM 4
+#define MGENDER_RST_NUM 2
+#define MAGE_RST_NUM 2
+#define MINDIAN_RST_NUM 2
+
+/* AIE 3.X FLD configuration */
+#define FLD_FOREST 14
+#define FLD_POINT 500
+
+#define FLD_STEP_NUM 6
+#define FLD_MAX_FRAME 15
+
+#define FLD_STEP_BLINK 0
+#define FLD_STEP_CV 1
+#define FLD_STEP_FP 2
+#define FLD_STEP_LEAF 3
+#define FLD_STEP_KM02 4
+#define FLD_STEP_KM13 5
+
+#define FLD_BLINK_WEIGHT_FOREST14_SIZE 6416
+#define FLD_CV_SIZE 19392
+#define FLD_FP_SIZE 80160
+#define FLD_LEAFNODE_SIZE 4608000
+#define FLD_TREE_KM02_SIZE 120000
+#define FLD_TREE_KM13_SIZE 120000
+#define FLD_OUTPUT_SIZE 112
+
+#define FLD_CUR_LANDMARK 11
+
+#define RESULT_SIZE (RLT_NUM * MAX_FACE_NUM)
+
+#include <linux/mtk_aie_v4l2_controls.h>
+
+struct aie_static_info_element {
+	unsigned int fd_wdma_size[OUTPUT_WDMA_WRA_NUM];
+	unsigned int out_xsize_plus_1;
+	unsigned int out_height;
+	unsigned int out_ysize_plus_1_stride2;
+	unsigned int out_stride;
+	unsigned int out_stride_stride2;
+	unsigned int out_width;
+	unsigned int img_width;
+	unsigned int img_height;
+	unsigned int stride2_out_width;
+	unsigned int stride2_out_height;
+	unsigned int out_xsize_plus_1_stride2;
+	unsigned int input_xsize_plus_1;
+};
+
+struct aie_static_info {
+	struct aie_static_info_element inf_elm[FD_LOOP_NUM];
+};
+
+enum aie_state { STATE_NA = 0x0, STATE_INIT = 0x1, STATE_OPEN = 0x2 };
+
+/* AIE 3.1 operation modes */
+enum aie_mode {
+	FDMODE = 0,		/* Face Detection */
+	ATTRIBUTEMODE = 1,	/* Attribute Detection (gender, race, etc.) */
+	FLDMODE = 2		/* Facial Landmark Detection */
+};
+
+enum aie_format {
+	FMT_NA = 0,
+	FMT_YUV_2P = 1,
+	FMT_YVU_2P = 2,
+	FMT_YUYV = 3,
+	FMT_YVYU = 4,
+	FMT_UYVY = 5,
+	FMT_VYUY = 6,
+	FMT_MONO = 7,
+	/* AIE 3.X additional formats */
+	FMT_YUV420_2P = 8,
+	FMT_YUV420_1P = 9
+};
+
+enum aie_input_degree {
+	DEGREE_0 = 0,
+	DEGREE_90 = 1,
+	DEGREE_270 = 2,
+	DEGREE_180 = 3
+};
+
+/* align v4l2 user space interface */
+struct FDRESULT {
+	u16 anchor_x0[MAX_FACE_NUM];
+	u16 anchor_x1[MAX_FACE_NUM];
+	u16 anchor_y0[MAX_FACE_NUM];
+	u16 anchor_y1[MAX_FACE_NUM];
+	signed short rop_landmark_score0[MAX_FACE_NUM];
+	signed short rop_landmark_score1[MAX_FACE_NUM];
+	signed short rop_landmark_score2[MAX_FACE_NUM];
+	signed short anchor_score[MAX_FACE_NUM];
+	signed short rip_landmark_score0[MAX_FACE_NUM];
+	signed short rip_landmark_score1[MAX_FACE_NUM];
+	signed short rip_landmark_score2[MAX_FACE_NUM];
+	signed short rip_landmark_score3[MAX_FACE_NUM];
+	signed short rip_landmark_score4[MAX_FACE_NUM];
+	signed short rip_landmark_score5[MAX_FACE_NUM];
+	signed short rip_landmark_score6[MAX_FACE_NUM];
+	u16 face_result_index[MAX_FACE_NUM];
+	u16 anchor_index[MAX_FACE_NUM];
+	u32 fd_partial_result;
+};
+
+struct fd_result {
+	u16 fd_pyramid0_num;
+	u16 fd_pyramid1_num;
+	u16 fd_pyramid2_num;
+	u16 fd_total_num;
+	struct FDRESULT PYRAMID0_RESULT;
+	struct FDRESULT PYRAMID1_RESULT;
+	struct FDRESULT PYRAMID2_RESULT;
+};
+
+struct RACERESULT {
+	signed short RESULT[4][64]; /* RESULT[Channel][Feature] */
+};
+
+struct GENDERRESULT {
+	signed short RESULT[2][64]; /* RESULT[Channel][Feature] */
+};
+
+struct MERGED_RACERESULT {
+	signed short RESULT[4]; /* RESULT[Feature] */
+};
+
+struct MERGED_GENDERRESULT {
+	signed short RESULT[2]; /* RESULT[Feature] */
+};
+
+struct MERGED_AGERESULT {
+	signed short RESULT[2]; /* RESULT[Feature] */
+};
+
+struct MERGED_IS_INDIANRESULT {
+	signed short RESULT[2]; /* RESULT[Feature] */
+};
+
+struct attr_result {
+	struct GENDERRESULT GENDER_RESULT;
+	struct RACERESULT RACE_RESULT;
+	struct MERGED_AGERESULT MERGED_AGE_RESULT;
+	struct MERGED_GENDERRESULT MERGED_GENDER_RESULT;
+	struct MERGED_IS_INDIANRESULT MERGED_IS_INDIAN_RESULT;
+	struct MERGED_RACERESULT MERGED_RACE_RESULT;
+};
+
+/* AIE 3.X Facial Landmark Detection structures */
+struct FLD_LANDMARK {
+	u16 x;
+	u16 y;
+};
+
+struct fld_result {
+	struct FLD_LANDMARK fld_landmark[FLD_CUR_LANDMARK];
+	u16 fld_out_rip;
+	u16 fld_out_rop;
+	u16 confidence;
+	signed short blinkscore;
+};
+
+struct aie_roi {
+	u32 x1;
+	u32 y1;
+	u32 x2;
+	u32 y2;
+};
+
+struct aie_padding {
+	u32 left;
+	u32 right;
+	u32 down;
+	u32 up;
+};
+
+/* AIE 3.X FLD input parameters */
+struct fld_crop_rip_rop {
+	unsigned int fld_in_crop_x1;
+	unsigned int fld_in_crop_y1;
+	unsigned int fld_in_crop_x2;
+	unsigned int fld_in_crop_y2;
+	unsigned int fld_in_rip;	/* Rotation-invariant pose */
+	unsigned int fld_in_rop;	/* Rotation out-of-plane */
+} __packed;
+
+/* align v4l2 user space interface */
+struct aie_enq_info {
+	unsigned int sel_mode;
+	unsigned int src_img_fmt;
+	unsigned int src_img_width;
+	unsigned int src_img_height;
+	unsigned int src_img_stride;
+	unsigned int pyramid_base_width;
+	unsigned int pyramid_base_height;
+	unsigned int number_of_pyramid;
+	unsigned int rotate_degree;
+	int en_roi;
+	struct aie_roi src_roi;
+	int en_padding;
+	struct aie_padding src_padding;
+	unsigned int freq_level;
+	/* AIE 3.X FLD parameters */
+	unsigned int fld_face_num;
+	struct fld_crop_rip_rop fld_input[FLD_MAX_FRAME];
+	u32 src_img_addr;
+	u32 src_img_addr_uv;
+	u32 fd_version;
+	u32 attr_version;
+	u32 pose_version;
+	struct fd_result fd_out;
+	struct attr_result attr_out;
+	/* AIE 3.X FLD results */
+	struct fld_result fld_out[FLD_MAX_FRAME];
+	u32 irq_status;
+};
+
+struct aie_reg_cfg {
+	u32 rs_adr;
+	u32 yuv2rgb_adr;
+	u32 fd_adr;
+	u32 fd_pose_adr;
+	u32 fd_mode;
+	u32 hw_result;
+	u32 hw_result1;
+	u32 reserved;
+};
+
+struct aie_para {
+	void *fd_fd_cfg_va;
+	void *fd_rs_cfg_va;
+	void *fd_yuv2rgb_cfg_va;
+
+	void *attr_fd_cfg_va[MAX_ENQUE_FRAME_NUM];
+	void *attr_yuv2rgb_cfg_va[MAX_ENQUE_FRAME_NUM];
+
+	void *rs_pym_rst_va[PYM_NUM][COLOR_NUM];
+
+	dma_addr_t fd_fd_cfg_pa;
+	dma_addr_t fd_rs_cfg_pa;
+	dma_addr_t fd_yuv2rgb_cfg_pa;
+
+	dma_addr_t attr_fd_cfg_pa[MAX_ENQUE_FRAME_NUM];
+	dma_addr_t attr_yuv2rgb_cfg_pa[MAX_ENQUE_FRAME_NUM];
+
+	dma_addr_t rs_pym_rst_pa[PYM_NUM][COLOR_NUM];
+
+	u32 sel_mode;
+	u16 max_img_width;
+	u16 max_img_height;
+	u16 img_width;
+	u16 img_height;
+	u16 crop_width;
+	u16 crop_height;
+	u32 src_img_fmt;
+	u32 rotate_degree;
+	s16 rpn_anchor_thrd;
+	u16 pyramid_width;
+	u16 pyramid_height;
+	u16 max_pyramid_width;
+	u16 max_pyramid_height;
+	u16 number_of_pyramid;
+	u32 src_img_addr;
+	u32 src_img_addr_uv;
+};
+
+struct aie_attr_para {
+	u32 w_idx;
+	u32 r_idx;
+	u32 sel_mode[MAX_ENQUE_FRAME_NUM];
+	u16 img_width[MAX_ENQUE_FRAME_NUM];
+	u16 img_height[MAX_ENQUE_FRAME_NUM];
+	u16 crop_width[MAX_ENQUE_FRAME_NUM];
+	u16 crop_height[MAX_ENQUE_FRAME_NUM];
+	u32 src_img_fmt[MAX_ENQUE_FRAME_NUM];
+	u32 rotate_degree[MAX_ENQUE_FRAME_NUM];
+	u32 src_img_addr[MAX_ENQUE_FRAME_NUM];
+	u32 src_img_addr_uv[MAX_ENQUE_FRAME_NUM];
+};
+
+struct aie_fd_dma_para {
+	void *fd_out_hw_va[FD_LOOP_NUM][OUTPUT_WDMA_WRA_NUM];
+	void *fd_kernel_va[FD_LOOP_NUM][KERNEL_RDMA_RA_NUM];
+	void *attr_out_hw_va[ATTR_LOOP_NUM][OUTPUT_WDMA_WRA_NUM];
+	void *attr_kernel_va[ATTR_LOOP_NUM][KERNEL_RDMA_RA_NUM];
+
+	void *age_out_hw_va[MAX_ENQUE_FRAME_NUM];
+	void *gender_out_hw_va[MAX_ENQUE_FRAME_NUM];
+	void *is_indian_out_hw_va[MAX_ENQUE_FRAME_NUM];
+	void *race_out_hw_va[MAX_ENQUE_FRAME_NUM];
+
+	dma_addr_t fd_out_hw_pa[FD_LOOP_NUM][OUTPUT_WDMA_WRA_NUM];
+	dma_addr_t fd_kernel_pa[FD_LOOP_NUM][KERNEL_RDMA_RA_NUM];
+	dma_addr_t attr_out_hw_pa[ATTR_LOOP_NUM][OUTPUT_WDMA_WRA_NUM];
+	dma_addr_t attr_kernel_pa[ATTR_LOOP_NUM][KERNEL_RDMA_RA_NUM];
+
+	dma_addr_t age_out_hw_pa[MAX_ENQUE_FRAME_NUM];
+	dma_addr_t gender_out_hw_pa[MAX_ENQUE_FRAME_NUM];
+	dma_addr_t is_indian_out_hw_pa[MAX_ENQUE_FRAME_NUM];
+	dma_addr_t race_out_hw_pa[MAX_ENQUE_FRAME_NUM];
+};
+
+/* AIE 3.X FLD DMA buffer parameters */
+struct aie_fd_fld_para {
+	void *fld_step_va[FLD_STEP_NUM][FLD_MAX_FRAME];
+	void *fld_output_va[FLD_MAX_FRAME];
+	dma_addr_t fld_step_pa[FLD_STEP_NUM][FLD_MAX_FRAME];
+	dma_addr_t fld_output_pa[FLD_MAX_FRAME];
+};
+
+struct imem_buf_info {
+	void *va;
+	dma_addr_t pa;
+	unsigned int size;
+	unsigned int reserved;
+};
+
+struct fd_buffer {
+	u32 dma_addr; /* used by DMA HW */
+} __packed;
+
+struct user_init {
+	unsigned int max_img_width;
+	unsigned int max_img_height;
+	unsigned int pyramid_width;
+	unsigned int pyramid_height;
+	signed int feature_threshold;
+} __packed;
+
+struct user_param {
+	unsigned int fd_mode;
+	unsigned int src_img_fmt;
+	unsigned int src_img_width;
+	unsigned int src_img_height;
+	unsigned int src_img_stride;
+	unsigned int pyramid_base_width;
+	unsigned int pyramid_base_height;
+	unsigned int number_of_pyramid;
+	unsigned int rotate_degree;
+	int en_roi;
+	struct aie_roi src_roi;
+	int en_padding;
+	struct aie_padding src_padding;
+	unsigned int freq_level;
+	/* AIE 3.X FLD configuration */
+	unsigned int fld_face_num;
+	struct fld_crop_rip_rop fld_input[FLD_MAX_FRAME];
+} __packed;
+
+struct fd_enq_param {
+	struct fd_buffer src_img[2];
+	struct fd_buffer user_result;
+	struct user_param user_param;
+} __packed;
+
+/* AIE 3.X DVFS and QoS support */
+#ifdef CONFIG_INTERCONNECT_MTK_EXTENSION
+struct mtk_aie_dvfs {
+	struct device *dev;
+	struct regulator *reg;
+	unsigned int clklv_num[MTK_AIE_OPP_SET];
+	unsigned int clklv[MTK_AIE_OPP_SET][MTK_AIE_CLK_LEVEL_CNT];
+	unsigned int voltlv[MTK_AIE_OPP_SET][MTK_AIE_CLK_LEVEL_CNT];
+	unsigned int clklv_idx[MTK_AIE_OPP_SET];
+	unsigned int clklv_target[MTK_AIE_OPP_SET];
+	unsigned int cur_volt;
+};
+
+struct mtk_aie_qos_path {
+	struct icc_path *path; /* cmdq event enum value */
+	char dts_name[256];
+	unsigned long long bw;
+};
+
+struct mtk_aie_qos {
+	struct device *dev;
+	struct mtk_aie_qos_path *qos_path;
+};
+#endif
+
+struct aie_clocks {
+	struct clk_bulk_data *clks;
+	unsigned int clk_num;
+};
+
+struct mtk_aie_req_work {
+	struct work_struct work;
+	struct mtk_aie_dev *fd_dev;
+};
+
+#ifdef CONFIG_DEBUG_FS
+struct aie_trace_info {
+	char *dump_buffer;
+	unsigned int dump_size;
+	unsigned int dump_offset;
+	char trigger_dump[3];
+	char *srcdata_buf;
+	unsigned int srcdata_size;
+	char *dstdata_buf;
+	unsigned int dstdata_size;
+};
+#endif
+
+struct mtk_aie_variant {
+	unsigned int hw_version;
+	unsigned int fld_enable;
+	unsigned int y2r_cfg_size;
+	unsigned int rs_cfg_size;
+	unsigned int fd_cfg_size;
+};
+
+struct mtk_aie_dev {
+	struct device *dev;
+	struct mtk_aie_ctx *ctx;
+	struct v4l2_m2m_dev *m2m_dev;
+	struct aie_para *base_para;
+	struct aie_attr_para *attr_para;
+	struct aie_fd_dma_para *dma_para;
+
+	/* AIE 3.X FLD parameters */
+	struct aie_fd_fld_para *fld_para;
+#ifdef CONFIG_INTERCONNECT_MTK_EXTENSION
+	struct mtk_aie_dvfs *dvfs_info;
+	struct mtk_aie_qos *qos_info;
+#endif
+
+	struct aie_enq_info *aie_cfg;
+	struct workqueue_struct *frame_done_wq;
+	void __iomem *fd_base;
+	const struct mtk_aie_variant *variant;
+
+	/* Input Buffer Pointer */
+	struct imem_buf_info rs_cfg_data;
+	struct imem_buf_info fd_cfg_data;
+	struct imem_buf_info yuv2rgb_cfg_data;
+	/* HW Output Buffer Pointer */
+	struct imem_buf_info rs_output_hw;
+	struct imem_buf_info fd_dma_hw;
+	struct imem_buf_info fd_dma_result_hw;
+	struct imem_buf_info fd_kernel_hw;
+	struct imem_buf_info fd_attr_dma_hw;
+	struct aie_static_info st_info;
+
+	struct aie_reg_cfg reg_cfg;
+
+	/* AIE 3.X FLD firmware buffer */
+	struct media_device mdev;
+	struct video_device vfd;
+	struct aie_clocks aie_clk;
+	struct v4l2_device v4l2_dev;
+
+	/* Lock for V4L2 operations */
+	struct mutex vfd_lock;
+	struct mutex dev_lock;
+	/* Lock for performance optimization */
+	struct mutex fd_lock;
+	struct imem_buf_info fd_fld_step_data;
+	struct imem_buf_info fd_fld_out_hw;
+
+	int irq;
+	struct completion fd_job_finished;
+	struct delayed_work job_timeout_work;
+
+	/* DRAM Buffer Size */
+	unsigned int fd_rs_cfg_size;
+	unsigned int fd_fd_cfg_size;
+	unsigned int fd_yuv2rgb_cfg_size;
+	unsigned int attr_fd_cfg_size;
+	unsigned int attr_yuv2rgb_cfg_size;
+
+	/* HW Output Buffer Size */
+	unsigned int rs_pym_out_size[PYM_NUM];
+	unsigned int fd_dma_max_size;
+	unsigned int fd_dma_rst_max_size;
+	unsigned int fd_fd_kernel_size;
+	unsigned int fd_attr_kernel_size;
+	unsigned int fd_attr_dma_max_size;
+	unsigned int fd_attr_dma_rst_max_size;
+	/* AIE 3.X FLD buffer sizes */
+	unsigned int fld_step_size;
+	unsigned int fld_out_size;
+
+	wait_queue_head_t flushing_waitq;
+	atomic_t num_composing;
+	struct mtk_aie_req_work req_work;
+	unsigned int fd_state;
+	unsigned int fd_mem_size;
+	u32 fd_stream_count;
+
+#ifdef CONFIG_DEBUG_FS
+	struct aie_trace_info tr_info;
+#endif
+};
+
+struct mtk_aie_ctx {
+	struct mtk_aie_dev *fd_dev;
+	struct device *dev;
+	struct v4l2_fh fh;
+	struct v4l2_ctrl_handler hdl;
+	struct v4l2_pix_format_mplane src_fmt;
+	struct v4l2_meta_format dst_fmt;
+};
+
+/**************************************************************************/
+/*                   C L A S S    D E C L A R A T I O N                   */
+/**************************************************************************/
+
+void aie_reset(struct mtk_aie_dev *fd);
+int aie_init(struct mtk_aie_dev *fd, struct user_init *user_init);
+void aie_uninit(struct mtk_aie_dev *fd);
+int aie_prepare(struct mtk_aie_dev *fd, struct aie_enq_info *aie_cfg);
+void aie_execute(struct mtk_aie_dev *fd, struct aie_enq_info *aie_cfg);
+void aie_irqhandle(struct mtk_aie_dev *fd);
+void aie_get_fd_result(struct mtk_aie_dev *fd, struct aie_enq_info *aie_cfg);
+void aie_get_attr_result(struct mtk_aie_dev *fd, struct aie_enq_info *aie_cfg);
+void aie_get_fld_result(struct mtk_aie_dev *fd, struct aie_enq_info *aie_cfg);
+#endif /*__MTK_AIE_H__*/
diff --git a/drivers/media/platform/mediatek/aie/mtk_aie_drv.c b/drivers/media/platform/mediatek/aie/mtk_aie_drv.c
new file mode 100644
index 0000000..55dcfe0
--- /dev/null
+++ b/drivers/media/platform/mediatek/aie/mtk_aie_drv.c
@@ -0,0 +1,3667 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2020 MediaTek Inc.
+ *
+ * Author: Fish Wu <fish.wu@mediatek.com>
+ *
+ */
+
+#include "mtk_aie.h"
+#include <linux/delay.h>
+#include <linux/firmware.h>
+
+static const unsigned int fd_wdma_en[FD_LOOP_NUM][OUTPUT_WDMA_WRA_NUM] = {
+	{ 1, 0, 0, 0 }, { 1, 0, 1, 0 }, { 1, 0, 1, 0 }, { 1, 0, 0, 0 },
+	{ 1, 1, 1, 1 }, { 1, 1, 1, 1 }, { 1, 0, 0, 0 }, { 1, 0, 1, 0 },
+	{ 1, 1, 0, 0 }, { 1, 0, 0, 0 }, { 1, 0, 1, 0 }, { 1, 1, 0, 0 },
+	{ 1, 0, 0, 0 }, { 1, 0, 0, 0 }, { 1, 0, 0, 0 }, { 1, 0, 0, 0 },
+	{ 1, 0, 0, 0 }, { 1, 0, 0, 0 }, { 1, 1, 0, 0 }, { 1, 1, 0, 0 },
+	{ 1, 1, 0, 0 }, { 1, 0, 0, 0 }, { 1, 1, 1, 1 }, { 1, 1, 1, 1 },
+	{ 1, 1, 0, 0 }, { 1, 1, 0, 0 }, { 1, 1, 0, 0 }, { 1, 0, 0, 0 },
+	{ 1, 0, 0, 0 }, { 1, 0, 0, 0 }, { 1, 0, 1, 0 }, { 1, 0, 1, 0 },
+	{ 1, 0, 0, 0 }, { 1, 1, 1, 1 }, { 1, 1, 1, 1 }, { 1, 0, 0, 0 },
+	{ 1, 0, 1, 0 }, { 1, 1, 0, 0 }, { 1, 0, 0, 0 }, { 1, 0, 1, 0 },
+	{ 1, 1, 0, 0 }, { 1, 0, 0, 0 }, { 1, 0, 0, 0 }, { 1, 0, 0, 0 },
+	{ 1, 0, 0, 0 }, { 1, 0, 0, 0 }, { 1, 0, 0, 0 }, { 1, 1, 0, 0 },
+	{ 1, 1, 0, 0 }, { 1, 1, 0, 0 }, { 1, 0, 0, 0 }, { 1, 1, 1, 1 },
+	{ 1, 1, 1, 1 }, { 1, 1, 0, 0 }, { 1, 1, 0, 0 }, { 1, 1, 0, 0 },
+	{ 1, 0, 0, 0 }, { 1, 0, 0, 0 }, { 1, 0, 0, 0 }, { 1, 0, 1, 0 },
+	{ 1, 0, 1, 0 }, { 1, 0, 0, 0 }, { 1, 1, 1, 1 }, { 1, 1, 1, 1 },
+	{ 1, 0, 0, 0 }, { 1, 0, 1, 0 }, { 1, 1, 0, 0 }, { 1, 0, 0, 0 },
+	{ 1, 0, 1, 0 }, { 1, 1, 0, 0 }, { 1, 0, 0, 0 }, { 1, 0, 0, 0 },
+	{ 1, 0, 0, 0 }, { 1, 0, 0, 0 }, { 1, 0, 0, 0 }, { 1, 0, 0, 0 },
+	{ 1, 1, 0, 0 }, { 1, 1, 0, 0 }, { 1, 1, 0, 0 }, { 1, 0, 0, 0 },
+	{ 1, 1, 1, 1 }, { 1, 1, 1, 1 }, { 1, 1, 0, 0 }, { 1, 1, 0, 0 },
+	{ 1, 1, 0, 0 }, { 1, 0, 0, 0 }, { 1, 0, 0, 0 }
+};
+
+static const unsigned int out_stride_size[FD_LOOP_NUM][OUTPUT_WDMA_WRA_NUM] = {
+	{ 1, 0, 0, 0 }, { 1, 0, 2, 0 }, { 1, 0, 2, 0 }, { 1, 0, 0, 0 },
+	{ 1, 1, 2, 2 }, { 1, 1, 2, 2 }, { 1, 0, 0, 0 }, { 1, 0, 2, 0 },
+	{ 1, 1, 0, 0 }, { 1, 0, 0, 0 }, { 1, 0, 2, 0 }, { 1, 1, 0, 0 },
+	{ 1, 0, 0, 0 }, { 1, 0, 0, 0 }, { 1, 0, 0, 0 }, { 1, 0, 0, 0 },
+	{ 1, 0, 0, 0 }, { 1, 0, 0, 0 }, { 1, 1, 0, 0 }, { 1, 1, 0, 0 },
+	{ 1, 1, 0, 0 }, { 1, 0, 0, 0 }, { 1, 1, 1, 1 }, { 1, 1, 1, 1 },
+	{ 1, 1, 0, 0 }, { 1, 1, 0, 0 }, { 1, 1, 0, 0 }, { 1, 0, 0, 0 },
+	{ 3, 0, 0, 0 }, { 1, 0, 0, 0 }, { 1, 0, 2, 0 }, { 1, 0, 2, 0 },
+	{ 1, 0, 0, 0 }, { 1, 1, 2, 2 }, { 1, 1, 2, 2 }, { 1, 0, 0, 0 },
+	{ 1, 0, 2, 0 }, { 1, 1, 0, 0 }, { 1, 0, 0, 0 }, { 1, 0, 2, 0 },
+	{ 1, 1, 0, 0 }, { 1, 0, 0, 0 }, { 1, 0, 0, 0 }, { 1, 0, 0, 0 },
+	{ 1, 0, 0, 0 }, { 1, 0, 0, 0 }, { 1, 0, 0, 0 }, { 1, 1, 0, 0 },
+	{ 1, 1, 0, 0 }, { 1, 1, 0, 0 }, { 1, 0, 0, 0 }, { 1, 1, 1, 1 },
+	{ 1, 1, 1, 1 }, { 1, 1, 0, 0 }, { 1, 1, 0, 0 }, { 1, 1, 0, 0 },
+	{ 1, 0, 0, 0 }, { 3, 0, 0, 0 }, { 1, 0, 0, 0 }, { 1, 0, 2, 0 },
+	{ 1, 0, 2, 0 }, { 1, 0, 0, 0 }, { 1, 1, 2, 2 }, { 1, 1, 2, 2 },
+	{ 1, 0, 0, 0 }, { 1, 0, 2, 0 }, { 1, 1, 0, 0 }, { 1, 0, 0, 0 },
+	{ 1, 0, 2, 0 }, { 1, 1, 0, 0 }, { 1, 0, 0, 0 }, { 1, 0, 0, 0 },
+	{ 1, 0, 0, 0 }, { 1, 0, 0, 0 }, { 1, 0, 0, 0 }, { 1, 0, 0, 0 },
+	{ 1, 1, 0, 0 }, { 1, 1, 0, 0 }, { 1, 1, 0, 0 }, { 1, 0, 0, 0 },
+	{ 1, 1, 1, 1 }, { 1, 1, 1, 1 }, { 1, 1, 0, 0 }, { 1, 1, 0, 0 },
+	{ 1, 1, 0, 0 }, { 1, 0, 0, 0 }, { 3, 0, 0, 0 }
+};
+
+static const unsigned int fd_ker_rdma_size[FD_LOOP_NUM][KERNEL_RDMA_RA_NUM] = {
+	{ 240, 240 },	{ 1168, 1168 }, { 1168, 1168 }, { 272, 272 },
+	{ 2320, 2320 }, { 2080, 2080 }, { 1040, 1040 }, { 4624, 4624 },
+	{ 3104, 3104 }, { 9232, 9232 }, { 4624, 4624 }, { 4128, 4128 },
+	{ 1040, 1040 }, { 4624, 4624 }, { 4624, 4624 }, { 1552, 1552 },
+	{ 4624, 4624 }, { 4624, 4624 }, { 4128, 4128 }, { 1040, 1040 },
+	{ 1040, 1040 }, { 528, 528 },	{ 4160, 4160 }, { 4160, 4160 },
+	{ 2080, 2080 }, { 2080, 2080 }, { 2080, 2080 }, { 1040, 1040 },
+	{ 0, 0 },	{ 240, 240 },	{ 1168, 1168 }, { 1168, 1168 },
+	{ 272, 272 },	{ 2320, 2320 }, { 2080, 2080 }, { 1040, 1040 },
+	{ 4624, 4624 }, { 3104, 3104 }, { 9232, 9232 }, { 4624, 4624 },
+	{ 4128, 4128 }, { 1040, 1040 }, { 4624, 4624 }, { 4624, 4624 },
+	{ 1552, 1552 }, { 4624, 4624 }, { 4624, 4624 }, { 4128, 4128 },
+	{ 1040, 1040 }, { 1040, 1040 }, { 528, 528 },	{ 4160, 4160 },
+	{ 4160, 4160 }, { 2080, 2080 }, { 2080, 2080 }, { 2080, 2080 },
+	{ 1040, 1040 }, { 0, 0 },	{ 240, 240 },	{ 1168, 1168 },
+	{ 1168, 1168 }, { 272, 272 },	{ 2320, 2320 }, { 2080, 2080 },
+	{ 1040, 1040 }, { 4624, 4624 }, { 3104, 3104 }, { 9232, 9232 },
+	{ 4624, 4624 }, { 4128, 4128 }, { 1040, 1040 }, { 4624, 4624 },
+	{ 4624, 4624 }, { 1552, 1552 }, { 4624, 4624 }, { 4624, 4624 },
+	{ 4128, 4128 }, { 1040, 1040 }, { 1040, 1040 }, { 528, 528 },
+	{ 4160, 4160 }, { 4160, 4160 }, { 2080, 2080 }, { 2080, 2080 },
+	{ 2080, 2080 }, { 1040, 1040 }, { 0, 0 }
+};
+
+static const unsigned int fd_out_stride2_in[FD_LOOP_NUM] = {
+	0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0,
+	0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+static const unsigned int fd_stride[FD_LOOP_NUM] = {
+	2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
+};
+
+static const unsigned int fd_maxpool[FD_LOOP_NUM] = {
+	0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+static const unsigned int out_2size[FD_LOOP_NUM] = {
+	0, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 1,
+	0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+static const unsigned int in_ch_pack[FD_LOOP_NUM] = {
+	1,  16, 16, 16, 16, 16, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
+	32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 0,  1,	16, 16, 16, 16, 16, 32,
+	32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
+	32, 32, 32, 0,	1,  16, 16, 16, 16, 16, 32, 32, 32, 32, 32, 32, 32, 32,
+	32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 0
+};
+
+static const unsigned int outlayer[FD_LOOP_NUM] = {
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0
+};
+
+static const unsigned int out_ch_pack[FD_LOOP_NUM] = {
+	16, 16, 16, 16, 16, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
+	32, 16, 16, 16, 32, 32, 32, 32, 32, 32, 0,  16, 16, 16, 16, 16, 32, 32,
+	32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 16, 16, 16, 32, 32, 32,
+	32, 32, 32, 0,	16, 16, 16, 16, 16, 32, 32, 32, 32, 32, 32, 32, 32, 32,
+	32, 32, 32, 32, 32, 16, 16, 16, 32, 32, 32, 32, 32, 32, 0
+};
+
+static const unsigned int anchor_en_num[FD_LOOP_NUM] = {
+	5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+	5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+	5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+	5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5
+};
+
+/* [loop][ch][output_index] */
+static const signed int fd_rdma_en[FD_LOOP_NUM][INPUT_WDMA_WRA_NUM][2] = {
+	{ { 99, 99 }, { 99, 99 }, { 99, 99 }, { -1, -1 } },
+	{ { 0, 0 }, { -1, -1 }, { -1, -1 }, { -1, -1 } },
+	{ { 1, 0 }, { -1, -1 }, { -1, -1 }, { -1, -1 } },
+	{ { 1, 0 }, { 2, 0 }, { -1, -1 }, { -1, -1 } },
+	{ { 3, 0 }, { -1, -1 }, { -1, -1 }, { -1, -1 } },
+	{ { 1, 2 }, { 2, 2 }, { 4, 2 }, { 4, 3 } },
+	{ { 5, 0 }, { 5, 1 }, { -1, -1 }, { -1, -1 } },
+	{ { 6, 0 }, { -1, -1 }, { -1, -1 }, { -1, -1 } },
+	{ { 5, 0 }, { 5, 1 }, { 7, 0 }, { -1, -1 } },
+	{ { 8, 0 }, { 8, 1 }, { -1, -1 }, { -1, -1 } },
+	{ { 9, 0 }, { -1, -1 }, { -1, -1 }, { -1, -1 } },
+	{ { 5, 2 }, { 5, 3 }, { 7, 2 }, { 10, 2 } },
+	{ { 11, 0 }, { 11, 1 }, { -1, -1 }, { -1, -1 } },
+	{ { 12, 0 }, { -1, -1 }, { -1, -1 }, { -1, -1 } },
+	{ { 13, 0 }, { -1, -1 }, { -1, -1 }, { -1, -1 } },
+	{ { 11, 0 }, { 11, 1 }, { 14, 0 }, { -1, -1 } },
+	{ { 15, 0 }, { -1, -1 }, { -1, -1 }, { -1, -1 } },
+	{ { 16, 0 }, { -1, -1 }, { -1, -1 }, { -1, -1 } },
+	{ { 11, 0 }, { 11, 1 }, { 14, 0 }, { 17, 0 } },
+	{ { 18, 0 }, { 18, 1 }, { -1, -1 }, { -1, -1 } },
+	{ { 18, 0 }, { 18, 1 }, { -1, -1 }, { -1, -1 } },
+	{ { 18, 0 }, { 18, 1 }, { -1, -1 }, { -1, -1 } },
+	{ { 18, 0 }, { 18, 1 }, { -1, -1 }, { -1, -1 } },
+	{ { 18, 0 }, { 18, 1 }, { -1, -1 }, { -1, -1 } },
+	{ { 18, 0 }, { 18, 1 }, { -1, -1 }, { -1, -1 } },
+	{ { 18, 0 }, { 18, 1 }, { -1, -1 }, { -1, -1 } },
+	{ { 18, 0 }, { 18, 1 }, { -1, -1 }, { -1, -1 } },
+	{ { 18, 0 }, { 18, 1 }, { -1, -1 }, { -1, -1 } },
+	{ { 19, 0 }, { 22, 0 }, { 22, 1 }, { 25, 0 } },
+	{ { 99, 99 }, { 99, 99 }, { 99, 99 }, { -1, -1 } },
+	{ { 29, 0 }, { -1, -1 }, { -1, -1 }, { -1, -1 } },
+	{ { 30, 0 }, { -1, -1 }, { -1, -1 }, { -1, -1 } },
+	{ { 30, 0 }, { 31, 0 }, { -1, -1 }, { -1, -1 } },
+	{ { 32, 0 }, { -1, -1 }, { -1, -1 }, { -1, -1 } },
+	{ { 30, 2 }, { 31, 2 }, { 33, 2 }, { 33, 3 } },
+	{ { 34, 0 }, { 34, 1 }, { -1, -1 }, { -1, -1 } },
+	{ { 35, 0 }, { -1, -1 }, { -1, -1 }, { -1, -1 } },
+	{ { 34, 0 }, { 34, 1 }, { 36, 0 }, { -1, -1 } },
+	{ { 37, 0 }, { 37, 1 }, { -1, -1 }, { -1, -1 } },
+	{ { 38, 0 }, { -1, -1 }, { -1, -1 }, { -1, -1 } },
+	{ { 34, 2 }, { 34, 3 }, { 36, 2 }, { 39, 2 } },
+	{ { 40, 0 }, { 40, 1 }, { -1, -1 }, { -1, -1 } },
+	{ { 41, 0 }, { -1, -1 }, { -1, -1 }, { -1, -1 } },
+	{ { 42, 0 }, { -1, -1 }, { -1, -1 }, { -1, -1 } },
+	{ { 40, 0 }, { 40, 1 }, { 43, 0 }, { -1, -1 } },
+	{ { 44, 0 }, { -1, -1 }, { -1, -1 }, { -1, -1 } },
+	{ { 45, 0 }, { -1, -1 }, { -1, -1 }, { -1, -1 } },
+	{ { 40, 0 }, { 40, 1 }, { 43, 0 }, { 46, 0 } },
+	{ { 47, 0 }, { 47, 1 }, { -1, -1 }, { -1, -1 } },
+	{ { 47, 0 }, { 47, 1 }, { -1, -1 }, { -1, -1 } },
+	{ { 47, 0 }, { 47, 1 }, { -1, -1 }, { -1, -1 } },
+	{ { 47, 0 }, { 47, 1 }, { -1, -1 }, { -1, -1 } },
+	{ { 47, 0 }, { 47, 1 }, { -1, -1 }, { -1, -1 } },
+	{ { 47, 0 }, { 47, 1 }, { -1, -1 }, { -1, -1 } },
+	{ { 47, 0 }, { 47, 1 }, { -1, -1 }, { -1, -1 } },
+	{ { 47, 0 }, { 47, 1 }, { -1, -1 }, { -1, -1 } },
+	{ { 47, 0 }, { 47, 1 }, { -1, -1 }, { -1, -1 } },
+	{ { 48, 0 }, { 51, 0 }, { 51, 1 }, { 54, 0 } },
+	{ { 99, 99 }, { 99, 99 }, { 99, 99 }, { -1, -1 } },
+	{ { 58, 0 }, { -1, -1 }, { -1, -1 }, { -1, -1 } },
+	{ { 59, 0 }, { -1, -1 }, { -1, -1 }, { -1, -1 } },
+	{ { 59, 0 }, { 60, 0 }, { -1, -1 }, { -1, -1 } },
+	{ { 61, 0 }, { -1, -1 }, { -1, -1 }, { -1, -1 } },
+	{ { 59, 2 }, { 60, 2 }, { 62, 2 }, { 62, 3 } },
+	{ { 63, 0 }, { 63, 1 }, { -1, -1 }, { -1, -1 } },
+	{ { 64, 0 }, { -1, -1 }, { -1, -1 }, { -1, -1 } },
+	{ { 63, 0 }, { 63, 1 }, { 65, 0 }, { -1, -1 } },
+	{ { 66, 0 }, { 66, 1 }, { -1, -1 }, { -1, -1 } },
+	{ { 67, 0 }, { -1, -1 }, { -1, -1 }, { -1, -1 } },
+	{ { 63, 2 }, { 63, 3 }, { 65, 2 }, { 68, 2 } },
+	{ { 69, 0 }, { 69, 1 }, { -1, -1 }, { -1, -1 } },
+	{ { 70, 0 }, { -1, -1 }, { -1, -1 }, { -1, -1 } },
+	{ { 71, 0 }, { -1, -1 }, { -1, -1 }, { -1, -1 } },
+	{ { 69, 0 }, { 69, 1 }, { 72, 0 }, { -1, -1 } },
+	{ { 73, 0 }, { -1, -1 }, { -1, -1 }, { -1, -1 } },
+	{ { 74, 0 }, { -1, -1 }, { -1, -1 }, { -1, -1 } },
+	{ { 69, 0 }, { 69, 1 }, { 72, 0 }, { 75, 0 } },
+	{ { 76, 0 }, { 76, 1 }, { -1, -1 }, { -1, -1 } },
+	{ { 76, 0 }, { 76, 1 }, { -1, -1 }, { -1, -1 } },
+	{ { 76, 0 }, { 76, 1 }, { -1, -1 }, { -1, -1 } },
+	{ { 76, 0 }, { 76, 1 }, { -1, -1 }, { -1, -1 } },
+	{ { 76, 0 }, { 76, 1 }, { -1, -1 }, { -1, -1 } },
+	{ { 76, 0 }, { 76, 1 }, { -1, -1 }, { -1, -1 } },
+	{ { 76, 0 }, { 76, 1 }, { -1, -1 }, { -1, -1 } },
+	{ { 76, 0 }, { 76, 1 }, { -1, -1 }, { -1, -1 } },
+	{ { 76, 0 }, { 76, 1 }, { -1, -1 }, { -1, -1 } },
+	{ { 77, 0 }, { 80, 0 }, { 80, 1 }, { 83, 0 } }
+};
+
+static const unsigned int attr_wdma_en[ATTR_LOOP_NUM][OUTPUT_WDMA_WRA_NUM] = {
+	{ 1, 0, 1, 0 }, { 1, 0, 1, 0 }, { 1, 0, 0, 0 }, { 1, 1, 1, 1 },
+	{ 1, 1, 1, 1 }, { 1, 0, 1, 0 }, { 1, 1, 0, 0 }, { 1, 0, 1, 0 },
+	{ 1, 1, 0, 0 }, { 1, 0, 0, 0 }, { 1, 0, 0, 0 }, { 1, 0, 0, 0 },
+	{ 1, 0, 0, 0 }, { 1, 0, 0, 0 }, { 1, 0, 0, 0 }, { 1, 1, 0, 0 },
+	{ 1, 0, 0, 0 }, { 1, 0, 0, 0 }, { 1, 0, 0, 0 }, { 1, 0, 0, 0 },
+	{ 1, 0, 0, 0 }, { 1, 0, 0, 0 }, { 1, 0, 0, 0 }, { 1, 0, 0, 0 },
+	{ 1, 0, 0, 0 }, { 1, 0, 0, 0 }
+};
+
+static const unsigned int
+	attr_ker_rdma_size[ATTR_LOOP_NUM][KERNEL_RDMA_RA_NUM] = {
+		{ 240, 240 },	{ 1168, 1168 }, { 272, 272 },	{ 2320, 2320 },
+		{ 2080, 2080 }, { 9232, 9232 }, { 3104, 3104 }, { 9232, 9232 },
+		{ 4128, 4128 }, { 1040, 1040 }, { 4624, 4624 }, { 4624, 4624 },
+		{ 1552, 1552 }, { 4624, 4624 }, { 4624, 4624 }, { 4128, 4128 },
+		{ 9232, 9232 }, { 272, 272 },	{ 9232, 9232 }, { 2320, 2320 },
+		{ 144, 144 },	{ 9232, 9232 }, { 272, 272 },	{ 9232, 9232 },
+		{ 2320, 2320 }, { 144, 144 }
+	};
+
+static const unsigned int attr_out_stride2_as_in[ATTR_LOOP_NUM] = {
+	0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+static const unsigned int attr_fd_stride[ATTR_LOOP_NUM] = { /* H */
+							    2, 1, 1, 1, 1, 1, 1,
+							    1, 1, 1, 1, 1, 1, 1,
+							    1, 1, 1, 1, 1, 1, 1,
+							    1, 1, 1, 1, 1
+};
+
+static const unsigned int attr_fd_maxpool[ATTR_LOOP_NUM] = { /* L */
+							     1, 0, 0, 0, 0, 0,
+							     0, 0, 0, 0, 0, 0,
+							     0, 0, 0, 0, 0, 0,
+							     0, 0, 0, 0, 0, 0,
+							     0, 0
+};
+
+static const unsigned int attr_out_2size[ATTR_LOOP_NUM] = { /* O */
+							    1, 1, 0, 1, 1, 1, 0,
+							    1, 0, 0, 0, 0, 0, 0,
+							    0, 0, 0, 0, 0, 0, 0,
+							    0, 0, 0, 0, 0
+};
+
+/* [loop][ch][output_index] */
+static const signed int attr_rdma_en[ATTR_LOOP_NUM][INPUT_WDMA_WRA_NUM][2] = {
+	{ { 99, 99 }, { 99, 99 }, { 99, 99 }, { -1, -1 } },
+	{ { 0, 0 }, { -1, -1 }, { -1, -1 }, { -1, -1 } },
+	{ { 0, 0 }, { 1, 0 }, { -1, -1 }, { -1, -1 } },
+	{ { 2, 0 }, { -1, -1 }, { -1, -1 }, { -1, -1 } },
+	{ { 0, 2 }, { 1, 2 }, { 3, 2 }, { 3, 3 } },
+	{ { 4, 0 }, { 4, 1 }, { -1, -1 }, { -1, -1 } },
+	{ { 4, 0 }, { 4, 1 }, { 5, 0 }, { -1, -1 } },
+	{ { 6, 0 }, { 6, 1 }, { -1, -1 }, { -1, -1 } },
+	{ { 4, 2 }, { 4, 3 }, { 5, 2 }, { 7, 2 } },
+	{ { 8, 0 }, { 8, 1 }, { -1, -1 }, { -1, -1 } },
+	{ { 9, 0 }, { -1, -1 }, { -1, -1 }, { -1, -1 } },
+	{ { 10, 0 }, { -1, -1 }, { -1, -1 }, { -1, -1 } },
+	{ { 8, 0 }, { 8, 1 }, { 11, 0 }, { -1, -1 } },
+	{ { 12, 0 }, { -1, -1 }, { -1, -1 }, { -1, -1 } },
+	{ { 13, 0 }, { -1, -1 }, { -1, -1 }, { -1, -1 } },
+	{ { 8, 0 }, { 8, 1 }, { 11, 0 }, { 14, 0 } },
+	{ { 15, 0 }, { 15, 1 }, { -1, -1 }, { -1, -1 } },
+	{ { 16, 0 }, { -1, -1 }, { -1, -1 }, { -1, -1 } },
+	{ { 15, 0 }, { 15, 1 }, { -1, -1 }, { -1, -1 } },
+	{ { 18, 0 }, { -1, -1 }, { -1, -1 }, { -1, -1 } },
+	{ { 19, 0 }, { -1, -1 }, { -1, -1 }, { -1, -1 } },
+	{ { 15, 0 }, { 15, 1 }, { -1, -1 }, { -1, -1 } },
+	{ { 21, 0 }, { -1, -1 }, { -1, -1 }, { -1, -1 } },
+	{ { 15, 0 }, { 15, 1 }, { -1, -1 }, { -1, -1 } },
+	{ { 23, 0 }, { -1, -1 }, { -1, -1 }, { -1, -1 } },
+	{ { 24, 0 }, { -1, -1 }, { -1, -1 }, { -1, -1 } }
+};
+
+static const unsigned int attr_wdma_size[ATTR_LOOP_NUM][OUTPUT_WDMA_WRA_NUM] = {
+	{ 16384, 0, 4096, 0 },
+	{ 16384, 0, 4096, 0 },
+	{ 16384, 0, 0, 0 },
+	{ 16384, 16384, 4096, 4096 },
+	{ 8192, 8192, 2048, 2048 },
+	{ 8192, 0, 2048, 0 },
+	{ 8192, 8192, 0, 0 },
+	{ 8192, 0, 2048, 0 },
+	{ 2048, 2048, 0, 0 },
+	{ 2048, 0, 0, 0 },
+	{ 2048, 0, 0, 0 },
+	{ 2048, 0, 0, 0 },
+	{ 2048, 0, 0, 0 },
+	{ 2048, 0, 0, 0 },
+	{ 2048, 0, 0, 0 },
+	{ 2048, 2048, 0, 0 },
+	{ 2048, 0, 0, 0 },
+	{ 0, 0, 0, 0 },
+	{ 2048, 0, 0, 0 },
+	{ 1024, 0, 0, 0 },
+	{ 0, 0, 0, 0 },
+	{ 2048, 0, 0, 0 },
+	{ 0, 0, 0, 0 },
+	{ 2048, 0, 0, 0 },
+	{ 1024, 0, 0, 0 },
+	{ 0, 0, 0, 0 }
+};
+
+static const unsigned int fld_step_align_size[FLD_STEP_NUM][FLD_MAX_FRAME] = {
+	{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6528 },
+	{ 1536, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280,
+	  1280, 1280, 1280, 1280 },
+	{ 5376, 5376, 5376, 5376, 5376, 5376, 5376, 5376, 5376, 5376, 5376,
+	  5376, 5376, 5376, 5376 },
+	{ 307200, 307200, 307200, 307200, 307200, 307200, 307200, 307200,
+	  307200, 307200, 307200, 307200, 307200, 307200, 307200 },
+	{ 8064, 8064, 8064, 8064, 8064, 8064, 8064, 8064, 8064, 8064, 8064,
+	  8064, 8064, 8064, 8064 },
+	{ 8064, 8064, 8064, 8064, 8064, 8064, 8064, 8064, 8064, 8064, 8064,
+	  8064, 8064, 8064, 8064 }
+};
+
+static const unsigned int fld_face_info_0[FLD_MAX_FRAME] = {
+	FLD_INFO_0_FACE_0,  FLD_INFO_0_FACE_1,	FLD_INFO_0_FACE_2,
+	FLD_INFO_0_FACE_3,  FLD_INFO_0_FACE_4,	FLD_INFO_0_FACE_5,
+	FLD_INFO_0_FACE_6,  FLD_INFO_0_FACE_7,	FLD_INFO_0_FACE_8,
+	FLD_INFO_0_FACE_9,  FLD_INFO_0_FACE_10, FLD_INFO_0_FACE_11,
+	FLD_INFO_0_FACE_12, FLD_INFO_0_FACE_13, FLD_INFO_0_FACE_14
+};
+
+static const unsigned int fld_face_info_1[FLD_MAX_FRAME] = {
+	FLD_INFO_1_FACE_0,  FLD_INFO_1_FACE_1,	FLD_INFO_1_FACE_2,
+	FLD_INFO_1_FACE_3,  FLD_INFO_1_FACE_4,	FLD_INFO_1_FACE_5,
+	FLD_INFO_1_FACE_6,  FLD_INFO_1_FACE_7,	FLD_INFO_1_FACE_8,
+	FLD_INFO_1_FACE_9,  FLD_INFO_1_FACE_10, FLD_INFO_1_FACE_11,
+	FLD_INFO_1_FACE_12, FLD_INFO_1_FACE_13, FLD_INFO_1_FACE_14
+};
+
+static const unsigned int fld_face_info_2[FLD_MAX_FRAME] = {
+	FLD_INFO_2_FACE_0,  FLD_INFO_2_FACE_1,	FLD_INFO_2_FACE_2,
+	FLD_INFO_2_FACE_3,  FLD_INFO_2_FACE_4,	FLD_INFO_2_FACE_5,
+	FLD_INFO_2_FACE_6,  FLD_INFO_2_FACE_7,	FLD_INFO_2_FACE_8,
+	FLD_INFO_2_FACE_9,  FLD_INFO_2_FACE_10, FLD_INFO_2_FACE_11,
+	FLD_INFO_2_FACE_12, FLD_INFO_2_FACE_13, FLD_INFO_2_FACE_14
+};
+
+static u32 aie_cmb_u16(u16 low, u16 high)
+{
+	return ((u32)high << 16) | low;
+}
+
+static u32 aie_cmb_stride(u16 low, u16 high)
+{
+	return ((u32)high << 16) | (low & 0x000F);
+}
+
+static inline u16 dif_x(const struct aie_enq_info *aie_cfg)
+{
+	return (u16)(aie_cfg->src_roi.x2 - aie_cfg->src_roi.x1);
+}
+
+static inline u16 dif_y(const struct aie_enq_info *aie_cfg)
+{
+	return (u16)(aie_cfg->src_roi.y2 - aie_cfg->src_roi.y1);
+}
+
+static inline void set_cmb_cfg(u32 *tbl, u16 index, u16 stride)
+{
+	if (tbl)
+		tbl[index] = aie_cmb_u16(tbl[index], stride);
+}
+
+static inline void set_cmbst_cfg(u32 *tbl, u16 index, u16 stride)
+{
+	if (tbl)
+		tbl[index] = aie_cmb_stride(tbl[index], stride);
+}
+
+static int aie_imem_alloc(struct mtk_aie_dev *fd, u32 size,
+			  struct imem_buf_info *bufinfo)
+{
+	struct device *dev = fd->dev;
+	void *va = NULL;
+	dma_addr_t dma_handle = 0;
+
+	if (size == 0) {
+		dev_dbg(fd->dev, "%s: size(%d)\n", __func__, size);
+		return -EINVAL;
+	}
+
+	fd->fd_mem_size += size;
+
+	va = dma_alloc_coherent(dev, size, &dma_handle, GFP_KERNEL);
+	if (!va || dma_handle == 0)
+		return -ENOMEM;
+
+	bufinfo->va = va;
+	bufinfo->pa = dma_handle;
+	bufinfo->size = size;
+
+	dev_dbg(fd->dev,
+		"%s: vAddr(0x%p) pAddr(0x%pad) size(%d)\n",
+		__func__,
+		va,
+		&dma_handle,
+		size
+	);
+
+	return 0;
+}
+
+static void aie_imem_free(struct mtk_aie_dev *fd, struct imem_buf_info *bufinfo)
+{
+	dev_dbg(fd->dev,
+		"%s: vAddr(0x%p) pAddr(0x%pad) size(%d)\n",
+		__func__,
+		bufinfo->va,
+		&bufinfo->pa,
+		bufinfo->size
+	);
+
+	if (bufinfo->va)
+		dma_free_coherent(fd->dev,
+				  bufinfo->size,
+				  bufinfo->va,
+				  bufinfo->pa
+		);
+}
+
+static void aie_init_table(struct mtk_aie_dev *fd, u16 pym_width,
+			   u16 pym_height)
+{
+	int i = 0;
+	struct aie_static_info *pstv = &fd->st_info;
+
+	pstv->inf_elm[PYM2_START_LOOP].img_width = pym_width / 4;
+	pstv->inf_elm[PYM2_START_LOOP].img_height = pym_height / 4;
+
+	pstv->inf_elm[PYM1_START_LOOP].img_width = pym_width / 2;
+	pstv->inf_elm[PYM1_START_LOOP].img_height = pym_height / 2;
+
+	pstv->inf_elm[PYM0_START_LOOP].img_width = pym_width;
+	pstv->inf_elm[PYM0_START_LOOP].img_height = pym_height;
+
+	for (i = 0; i < FD_LOOP_NUM; i++) {
+		if (i != PYM2_START_LOOP && i != PYM1_START_LOOP && i != PYM0_START_LOOP) {
+			if (fd_out_stride2_in[i] == 1) {
+				pstv->inf_elm[i].img_width =
+					pstv->inf_elm[i - 1].stride2_out_width;
+				pstv->inf_elm[i].img_height =
+					pstv->inf_elm[i - 1].stride2_out_height;
+			} else {
+				pstv->inf_elm[i].img_width =
+					pstv->inf_elm[i - 1].out_width;
+				pstv->inf_elm[i].img_height =
+					pstv->inf_elm[i - 1].out_height;
+			}
+		}
+
+		if (fd_maxpool[i] == 1 && fd_stride[i] == 1) {
+			pstv->inf_elm[i].out_width =
+				(pstv->inf_elm[i].img_width - 1) / (2 * fd_maxpool[i]) + 1;
+			pstv->inf_elm[i].out_height =
+				(pstv->inf_elm[i].img_height - 1) / (2 * fd_maxpool[i]) + 1;
+		} else {
+			pstv->inf_elm[i].out_width =
+				(pstv->inf_elm[i].img_width - 1) /
+					(fd_stride[i] + 2 * fd_maxpool[i]) + 1;
+			pstv->inf_elm[i].out_height =
+				(pstv->inf_elm[i].img_height - 1) /
+					(fd_stride[i] + 2 * fd_maxpool[i]) + 1;
+		}
+
+		pstv->inf_elm[i].stride2_out_width =
+			((pstv->inf_elm[i].out_width - 1) / 2 + 1) * out_2size[i];
+		pstv->inf_elm[i].stride2_out_height =
+			((pstv->inf_elm[i].out_height - 1) / 2 + 1) * out_2size[i];
+
+		if (outlayer[i] == 1) {
+			pstv->inf_elm[i].out_xsize_plus_1 =
+				pstv->inf_elm[i].out_width * out_ch_pack[i] * 2;
+			pstv->inf_elm[i].out_stride =
+				round_up(pstv->inf_elm[i].out_xsize_plus_1 * anchor_en_num[i], 16);
+			pstv->inf_elm[i].out_xsize_plus_1_stride2 =
+				((pstv->inf_elm[i].out_width - 1) / 2 + 1) *
+				out_ch_pack[i] * 2 * out_2size[i];
+		} else {
+			pstv->inf_elm[i].out_xsize_plus_1 =
+				pstv->inf_elm[i].out_width * out_ch_pack[i];
+			pstv->inf_elm[i].out_stride =
+				round_up(pstv->inf_elm[i].out_xsize_plus_1, 16);
+			pstv->inf_elm[i].out_xsize_plus_1_stride2 =
+				((pstv->inf_elm[i].out_width - 1) / 2 + 1) *
+				out_ch_pack[i] * out_2size[i];
+		}
+
+		pstv->inf_elm[i].out_stride_stride2 =
+				round_up(pstv->inf_elm[i].out_xsize_plus_1_stride2, 16);
+
+		if (out_2size[i] == 1)
+			pstv->inf_elm[i].out_ysize_plus_1_stride2 =
+				(pstv->inf_elm[i].out_height - 1) / 2 + 1;
+		else
+			pstv->inf_elm[i].out_ysize_plus_1_stride2 =
+				pstv->inf_elm[i].out_height;
+
+		if (fd_wdma_en[i][0]) {
+			if (i == RPN2_LOOP_NUM || i == RPN1_LOOP_NUM || i == RPN0_LOOP_NUM)
+				pstv->inf_elm[i].fd_wdma_size[0] =
+					RESULT_SIZE;
+			else
+				pstv->inf_elm[i].fd_wdma_size[0] =
+					pstv->inf_elm[i].out_height *
+					pstv->inf_elm[i].out_stride;
+		}
+
+		if (outlayer[i] == 1) {
+			if (fd_wdma_en[i][1])
+				pstv->inf_elm[i].fd_wdma_size[1] =
+					pstv->inf_elm[i].fd_wdma_size[0];
+			if (fd_wdma_en[i][2])
+				pstv->inf_elm[i].fd_wdma_size[2] =
+					pstv->inf_elm[i].fd_wdma_size[0];
+			if (fd_wdma_en[i][3])
+				pstv->inf_elm[i].fd_wdma_size[3] =
+					pstv->inf_elm[i].fd_wdma_size[0];
+		} else if (i == RPN2_LOOP_NUM || i == RPN1_LOOP_NUM || i == RPN0_LOOP_NUM) {
+			pstv->inf_elm[i].fd_wdma_size[0] = RESULT_SIZE;
+		} else {
+			if (fd_wdma_en[i][1])
+				pstv->inf_elm[i].fd_wdma_size[1] =
+					pstv->inf_elm[i].out_height *
+					pstv->inf_elm[i].out_stride;
+			if (fd_wdma_en[i][2])
+				pstv->inf_elm[i].fd_wdma_size[2] =
+					pstv->inf_elm[i].out_ysize_plus_1_stride2 *
+					pstv->inf_elm[i].out_stride_stride2;
+			if (fd_wdma_en[i][3])
+				pstv->inf_elm[i].fd_wdma_size[3] =
+					pstv->inf_elm[i].out_ysize_plus_1_stride2 *
+					pstv->inf_elm[i].out_stride_stride2;
+		}
+
+		if (in_ch_pack[i] == 1)
+			pstv->inf_elm[i].input_xsize_plus_1 =
+				round_up(pstv->inf_elm[i].img_width, 8);
+		else
+			pstv->inf_elm[i].input_xsize_plus_1 =
+				pstv->inf_elm[i].img_width * in_ch_pack[i];
+	}
+}
+
+static void aie_update_table(struct mtk_aie_dev *fd, u16 pym_width,
+			     u16 pym_height)
+{
+	int i = 0;
+	struct aie_static_info *pstv = &fd->st_info;
+
+	pstv->inf_elm[PYM2_START_LOOP].img_width = pym_width / 4;
+	pstv->inf_elm[PYM2_START_LOOP].img_height = pym_height / 4;
+
+	pstv->inf_elm[PYM1_START_LOOP].img_width = pym_width / 2;
+	pstv->inf_elm[PYM1_START_LOOP].img_height = pym_height / 2;
+
+	pstv->inf_elm[PYM0_START_LOOP].img_width = pym_width;
+	pstv->inf_elm[PYM0_START_LOOP].img_height = pym_height;
+
+	for (i = 0; i < FD_LOOP_NUM; i++) {
+		if (i != PYM2_START_LOOP && i != PYM1_START_LOOP &&
+		    i != PYM0_START_LOOP) {
+			if (fd_out_stride2_in[i] == 1) {
+				pstv->inf_elm[i].img_width =
+					pstv->inf_elm[i - 1].stride2_out_width;
+				pstv->inf_elm[i].img_height =
+					pstv->inf_elm[i - 1].stride2_out_height;
+			} else {
+				pstv->inf_elm[i].img_width =
+					pstv->inf_elm[i - 1].out_width;
+				pstv->inf_elm[i].img_height =
+					pstv->inf_elm[i - 1].out_height;
+			}
+		}
+
+		if (fd_maxpool[i] == 1 && fd_stride[i] == 1) {
+			pstv->inf_elm[i].out_width =
+				(pstv->inf_elm[i].img_width - 1) /
+					(2 * fd_maxpool[i]) +
+				1;
+			pstv->inf_elm[i].out_height =
+				(pstv->inf_elm[i].img_height - 1) /
+					(2 * fd_maxpool[i]) +
+				1;
+		} else {
+			pstv->inf_elm[i].out_width =
+				(pstv->inf_elm[i].img_width - 1) /
+					(fd_stride[i] + 2 * fd_maxpool[i]) +
+				1;
+			pstv->inf_elm[i].out_height =
+				(pstv->inf_elm[i].img_height - 1) /
+					(fd_stride[i] + 2 * fd_maxpool[i]) +
+				1;
+		}
+
+		pstv->inf_elm[i].stride2_out_width =
+			((pstv->inf_elm[i].out_width - 1) / 2 + 1) *
+			out_2size[i];
+		pstv->inf_elm[i].stride2_out_height =
+			((pstv->inf_elm[i].out_height - 1) / 2 + 1) *
+			out_2size[i];
+
+		if (outlayer[i] == 1) {
+			pstv->inf_elm[i].out_xsize_plus_1 =
+				pstv->inf_elm[i].out_width *
+				out_ch_pack[i] * 2;
+			pstv->inf_elm[i].out_stride =
+				round_up(pstv->inf_elm[i].out_xsize_plus_1 * anchor_en_num[i], 16);
+			pstv->inf_elm[i].out_xsize_plus_1_stride2 =
+				((pstv->inf_elm[i].out_width - 1) / 2 +
+				 1) *
+				out_ch_pack[i] * 2 * out_2size[i];
+		} else {
+			pstv->inf_elm[i].out_xsize_plus_1 =
+				pstv->inf_elm[i].out_width *
+				out_ch_pack[i];
+			pstv->inf_elm[i].out_stride =
+				round_up(pstv->inf_elm[i].out_xsize_plus_1, 16);
+			pstv->inf_elm[i].out_xsize_plus_1_stride2 =
+				((pstv->inf_elm[i].out_width - 1) / 2 +
+				 1) *
+				out_ch_pack[i] * out_2size[i];
+		}
+
+		pstv->inf_elm[i].out_stride_stride2 =
+			round_up(pstv->inf_elm[i].out_xsize_plus_1_stride2, 16);
+
+		if (out_2size[i] == 1)
+			pstv->inf_elm[i].out_ysize_plus_1_stride2 =
+				(pstv->inf_elm[i].out_height - 1) / 2 + 1;
+		else
+			pstv->inf_elm[i].out_ysize_plus_1_stride2 =
+				pstv->inf_elm[i].out_height;
+
+		if (in_ch_pack[i] == 1)
+			pstv->inf_elm[i].input_xsize_plus_1 =
+				round_up(pstv->inf_elm[i].img_width, 8);
+		else
+			pstv->inf_elm[i].input_xsize_plus_1 =
+				pstv->inf_elm[i].img_width * in_ch_pack[i];
+	}
+}
+
+static void aie_update_buf_params(struct mtk_aie_dev *fd, u16 max_img_width,
+			      u16 max_img_height)
+{
+	u8 i = 0, j = 0;
+	struct aie_static_info *pstv = &fd->st_info;
+
+	fd->base_para->max_img_width = max_img_width;
+	fd->base_para->max_img_height = max_img_height;
+	fd->fd_dma_max_size = 0;
+	fd->fd_dma_rst_max_size = 0;
+	fd->fd_fd_kernel_size = 0;
+	fd->fd_attr_kernel_size = 0;
+	fd->fd_attr_dma_max_size = 0;
+	fd->fd_attr_dma_rst_max_size = 0;
+
+	/* FDMODE Dram Buffer Size */
+	fd->fd_rs_cfg_size = 4 * fd->variant->rs_cfg_size * 2;
+	fd->fd_fd_cfg_size = 4 * fd->variant->fd_cfg_size * FD_LOOP_NUM;
+	fd->fd_yuv2rgb_cfg_size = 4 * fd->variant->y2r_cfg_size;
+
+	/* ATTRMODE Dram Buffer Size */
+	fd->attr_fd_cfg_size = 4 * fd->variant->fd_cfg_size * ATTR_LOOP_NUM;
+	fd->attr_yuv2rgb_cfg_size = 4 * fd->variant->y2r_cfg_size;
+
+	/* HW Output Buffer Size */
+	fd->rs_pym_out_size[0] = fd->base_para->max_pyramid_width *
+				 fd->base_para->max_pyramid_height;
+	fd->rs_pym_out_size[1] = fd->rs_pym_out_size[0] / 2;
+	fd->rs_pym_out_size[2] = fd->rs_pym_out_size[0] / 4;
+
+	/* FDMODE Dram Buffer Size */
+	for (i = 0; i < FD_LOOP_NUM; i++) {
+		for (j = 0; j < OUTPUT_WDMA_WRA_NUM; j++) {
+			if (fd_wdma_en[i][j]) {
+				if ((i == RPN2_LOOP_NUM || i == RPN1_LOOP_NUM ||
+				     i == RPN0_LOOP_NUM) && j == 0) {
+					fd->fd_dma_rst_max_size +=
+						pstv->inf_elm[i]
+							.fd_wdma_size[j];
+				} else {
+					fd->fd_dma_max_size +=
+						pstv->inf_elm[i]
+							.fd_wdma_size[j];
+				}
+			}
+		}
+	}
+
+	for (i = 0; i < FD_LOOP_NUM; i++) {
+		for (j = 0; j < KERNEL_RDMA_RA_NUM; j++) {
+			if (fd_ker_rdma_size[i][j])
+				fd->fd_fd_kernel_size += fd_ker_rdma_size[i][j];
+		}
+	}
+
+	/* ATTRMODE Dram Buffer Size */
+	for (i = 0; i < ATTR_LOOP_NUM; i++) {
+		for (j = 0; j < OUTPUT_WDMA_WRA_NUM; j++) {
+			if (attr_wdma_en[i][j]) {
+				if ((i == AGE_OUT_RGS || i == GENDER_OUT_RGS ||
+				     i == INDIAN_OUT_RGS || i == RACE_OUT_RGS) && j == 0) {
+					fd->fd_attr_dma_rst_max_size +=
+						ATTR_OUT_SIZE *
+						MAX_ENQUE_FRAME_NUM;
+				} else {
+					fd->fd_attr_dma_max_size +=
+						attr_wdma_size[i][j];
+				}
+			}
+		}
+	}
+
+	for (i = 0; i < ATTR_LOOP_NUM; i++) {
+		for (j = 0; j < KERNEL_RDMA_RA_NUM; j++)
+			fd->fd_attr_kernel_size += attr_ker_rdma_size[i][j];
+	}
+
+	/* FD Pose secure result output buffer: result size * 3 loops */
+	fd->fd_dma_rst_max_size += RESULT_SIZE * 3;
+
+	if (fd->variant->fld_enable) {
+		/* fld size */
+		fd->fld_step_size = 0;
+		for (i = 0; i < FLD_STEP_NUM; i++)
+			for (j = 0; j < FLD_MAX_FRAME; j++)
+				fd->fld_step_size += fld_step_align_size[i][j];
+
+		fd->fld_out_size = FLD_OUTPUT_SIZE * FLD_MAX_FRAME;
+	}
+}
+
+static int aie_alloc_dram_buf(struct mtk_aie_dev *fd)
+{
+	int ret = -EINVAL;
+	u8 i = 0;
+	u32 alloc_size = 0;
+
+	/* RS DRAM */
+	alloc_size = fd->fd_rs_cfg_size;
+	dev_dbg(fd->dev, "RS CFG:");
+	ret = aie_imem_alloc(fd, alloc_size, &fd->rs_cfg_data);
+	if (ret)
+		goto dma_alloc_fail;
+	/* FD MODE */
+	fd->base_para->fd_rs_cfg_pa = fd->rs_cfg_data.pa;
+	fd->base_para->fd_rs_cfg_va = fd->rs_cfg_data.va;
+
+	/* FD DRAM */
+	alloc_size =
+		fd->fd_fd_cfg_size + fd->attr_fd_cfg_size * MAX_ENQUE_FRAME_NUM;
+	dev_dbg(fd->dev, "FD CFG:");
+	ret = aie_imem_alloc(fd, alloc_size, &fd->fd_cfg_data);
+	if (ret)
+		goto dma_alloc_fail;
+	/* FD MODE */
+	fd->base_para->fd_fd_cfg_pa = fd->fd_cfg_data.pa;
+	fd->base_para->fd_fd_cfg_va = fd->fd_cfg_data.va;
+	/* ATTR MODE */
+	fd->base_para->attr_fd_cfg_pa[0] =
+		fd->base_para->fd_fd_cfg_pa + fd->fd_fd_cfg_size;
+	fd->base_para->attr_fd_cfg_va[0] =
+		fd->base_para->fd_fd_cfg_va + fd->fd_fd_cfg_size;
+
+	for (i = 1; i < MAX_ENQUE_FRAME_NUM; i++) {
+		fd->base_para->attr_fd_cfg_pa[i] =
+			fd->base_para->attr_fd_cfg_pa[i - 1] +
+			fd->attr_fd_cfg_size;
+		fd->base_para->attr_fd_cfg_va[i] =
+			fd->base_para->attr_fd_cfg_va[i - 1] +
+			fd->attr_fd_cfg_size;
+	}
+
+	/* YUV2RGB DRAM */
+	alloc_size = fd->fd_yuv2rgb_cfg_size +
+		     fd->attr_yuv2rgb_cfg_size * MAX_ENQUE_FRAME_NUM;
+	dev_dbg(fd->dev, "YUV2RGB CFG:");
+	ret = aie_imem_alloc(fd, alloc_size, &fd->yuv2rgb_cfg_data);
+	if (ret)
+		goto dma_alloc_fail;
+	/* FD MODE */
+	fd->base_para->fd_yuv2rgb_cfg_pa = fd->yuv2rgb_cfg_data.pa;
+	fd->base_para->fd_yuv2rgb_cfg_va = fd->yuv2rgb_cfg_data.va;
+
+	/* ATTR MODE */
+	fd->base_para->attr_yuv2rgb_cfg_pa[0] =
+		fd->base_para->fd_yuv2rgb_cfg_pa + fd->fd_yuv2rgb_cfg_size;
+	fd->base_para->attr_yuv2rgb_cfg_va[0] =
+		fd->base_para->fd_yuv2rgb_cfg_va + fd->fd_yuv2rgb_cfg_size;
+
+	for (i = 1; i < MAX_ENQUE_FRAME_NUM; i++) {
+		fd->base_para->attr_yuv2rgb_cfg_pa[i] =
+			fd->base_para->attr_yuv2rgb_cfg_pa[i - 1] +
+			fd->attr_yuv2rgb_cfg_size;
+		fd->base_para->attr_yuv2rgb_cfg_va[i] =
+			fd->base_para->attr_yuv2rgb_cfg_va[i - 1] +
+			fd->attr_yuv2rgb_cfg_size;
+	}
+
+	return ret;
+dma_alloc_fail:
+	aie_imem_free(fd, &fd->fd_cfg_data);
+	aie_imem_free(fd, &fd->rs_cfg_data);
+
+	return ret;
+}
+
+static int aie_alloc_output_buf(struct mtk_aie_dev *fd)
+{
+	int ret = -EINVAL;
+	u32 alloc_size = 0;
+	int i, j, pa_off = 0, va_off = 0;
+
+	for (i = 0; i < PYM_NUM; i++)
+		alloc_size += fd->rs_pym_out_size[i] * 3;
+	dev_dbg(fd->dev, "RS OUT:");
+	ret = aie_imem_alloc(fd, alloc_size, &fd->rs_output_hw);
+	if (ret)
+		return ret;
+
+	for (i = 0; i < PYM_NUM; i++) {
+		for (j = 0; j < COLOR_NUM; j++) {
+			fd->base_para->rs_pym_rst_pa[i][j] =
+				fd->rs_output_hw.pa + pa_off;
+			pa_off += fd->rs_pym_out_size[i];
+
+			fd->base_para->rs_pym_rst_va[i][j] =
+				fd->rs_output_hw.va + va_off;
+			va_off += fd->rs_pym_out_size[i];
+		}
+	}
+
+	return ret;
+}
+
+static void aie_alloc_normal(struct mtk_aie_dev *fd, int start, int end)
+{
+	int i = 0, j = 0;
+	int pi = 0, pj = 0;
+	struct aie_static_info *pstv = &fd->st_info;
+
+	if (start <= 0 || end <= start) {
+		dev_err(fd->dev, "%s: start = %d, end = %d\n", __func__, start, end);
+		return;
+	}
+
+	pi = start - 1;
+	pj = 0;
+	for (i = start; i < end + 1; i++) {
+		for (j = 0; j < OUTPUT_WDMA_WRA_NUM; j++) {
+			if (fd_wdma_en[i][j]) {
+				fd->dma_para->fd_out_hw_pa[i][j] =
+					fd->dma_para->fd_out_hw_pa[pi][pj] +
+					pstv->inf_elm[pi].fd_wdma_size[pj];
+				pi = i;
+				pj = j;
+			}
+		}
+	}
+}
+
+static int aie_alloc_fddma_buf(struct mtk_aie_dev *fd)
+{
+	int ret = -EINVAL;
+	u32 alloc_size = 0;
+
+	alloc_size = fd->fd_dma_max_size;
+	dev_dbg(fd->dev, "FD DMA:");
+	ret = aie_imem_alloc(fd, alloc_size, &fd->fd_dma_hw);
+	if (ret)
+		goto dma_alloc_fail;
+	alloc_size = fd->fd_fd_kernel_size + fd->fd_attr_kernel_size;
+	dev_dbg(fd->dev, "FD KERNEL:");
+	ret = aie_imem_alloc(fd, alloc_size, &fd->fd_kernel_hw);
+	if (ret)
+		goto dma_alloc_fail;
+
+	alloc_size = fd->fd_attr_dma_max_size;
+	dev_dbg(fd->dev, "ATTR DMA:");
+	ret = aie_imem_alloc(fd, alloc_size, &fd->fd_attr_dma_hw);
+	if (ret)
+		goto dma_alloc_fail;
+
+	alloc_size = fd->fd_dma_rst_max_size + fd->fd_attr_dma_rst_max_size;
+	dev_dbg(fd->dev, "RESULT DMA:");
+	ret = aie_imem_alloc(fd, alloc_size, &fd->fd_dma_result_hw);
+	if (ret)
+		goto dma_alloc_fail;
+
+	return 0;
+
+dma_alloc_fail:
+	aie_imem_free(fd, &fd->fd_attr_dma_hw);
+	aie_imem_free(fd, &fd->fd_kernel_hw);
+	aie_imem_free(fd, &fd->fd_dma_hw);
+
+	return ret;
+}
+
+static int aie_alloc_fld_buf(struct mtk_aie_dev *fd)
+{
+	int ret = -EINVAL;
+	u32 alloc_size = 0;
+
+	alloc_size = fd->fld_step_size;
+	dev_dbg(fd->dev, "FLD STEP:");
+	ret = aie_imem_alloc(fd, alloc_size, &fd->fd_fld_step_data);
+	if (ret)
+		return ret;
+
+	alloc_size = fd->fld_out_size;
+	dev_dbg(fd->dev, "FLD OUT:");
+	ret = aie_imem_alloc(fd, alloc_size, &fd->fd_fld_out_hw);
+	if (ret)
+		goto fld_step;
+
+	return 0;
+fld_step:
+	aie_imem_free(fd, &fd->fd_fld_step_data);
+
+	return ret;
+}
+
+static void aie_arrange_fddma_buf(struct mtk_aie_dev *fd)
+{
+	void *current_va = NULL;
+	dma_addr_t current_pa = 0;
+	struct aie_static_info *pstv = &fd->st_info;
+	u8 i = 0, j = 0;
+
+	/* 0~18 */
+	fd->dma_para->fd_out_hw_pa[0][0] = fd->fd_dma_hw.pa;
+	aie_alloc_normal(fd, 1, 18);
+
+	/* 19~27 */
+	fd->dma_para->fd_out_hw_pa[19][0] =
+		fd->dma_para->fd_out_hw_pa[18][1] +
+		pstv->inf_elm[18].fd_wdma_size[1];
+	fd->dma_para->fd_out_hw_pa[19][1] =
+		fd->dma_para->fd_out_hw_pa[19][0] +
+		pstv->inf_elm[19].out_xsize_plus_1;
+	fd->dma_para->fd_out_hw_pa[20][0] =
+		fd->dma_para->fd_out_hw_pa[19][0] +
+		2 * pstv->inf_elm[20].out_xsize_plus_1;
+	fd->dma_para->fd_out_hw_pa[20][1] =
+		fd->dma_para->fd_out_hw_pa[19][0] +
+		3 * pstv->inf_elm[20].out_xsize_plus_1;
+	fd->dma_para->fd_out_hw_pa[21][0] =
+		fd->dma_para->fd_out_hw_pa[19][0] +
+		4 * pstv->inf_elm[21].out_xsize_plus_1;
+	fd->dma_para->fd_out_hw_pa[22][0] =
+		fd->dma_para->fd_out_hw_pa[19][0] +
+		pstv->inf_elm[19].fd_wdma_size[0] +
+		pstv->inf_elm[19].fd_wdma_size[1] +
+		pstv->inf_elm[20].fd_wdma_size[0] +
+		pstv->inf_elm[20].fd_wdma_size[1] +
+		pstv->inf_elm[21].fd_wdma_size[0];
+	fd->dma_para->fd_out_hw_pa[22][1] =
+		fd->dma_para->fd_out_hw_pa[22][0] +
+		pstv->inf_elm[22].fd_wdma_size[0] +
+		pstv->inf_elm[22].fd_wdma_size[2] +
+		pstv->inf_elm[23].fd_wdma_size[0];
+	fd->dma_para->fd_out_hw_pa[22][2] =
+		fd->dma_para->fd_out_hw_pa[22][0] +
+		pstv->inf_elm[22].out_xsize_plus_1;
+	fd->dma_para->fd_out_hw_pa[22][3] =
+		fd->dma_para->fd_out_hw_pa[22][1] +
+		pstv->inf_elm[22].out_xsize_plus_1;
+	fd->dma_para->fd_out_hw_pa[23][0] =
+		fd->dma_para->fd_out_hw_pa[22][0] +
+		2 * pstv->inf_elm[23].out_xsize_plus_1;
+	fd->dma_para->fd_out_hw_pa[23][1] =
+		fd->dma_para->fd_out_hw_pa[22][1] +
+		2 * pstv->inf_elm[23].out_xsize_plus_1;
+	fd->dma_para->fd_out_hw_pa[23][2] =
+		fd->dma_para->fd_out_hw_pa[22][0] +
+		3 * pstv->inf_elm[23].out_xsize_plus_1;
+	fd->dma_para->fd_out_hw_pa[23][3] =
+		fd->dma_para->fd_out_hw_pa[22][1] +
+		3 * pstv->inf_elm[23].out_xsize_plus_1;
+	fd->dma_para->fd_out_hw_pa[24][0] =
+		fd->dma_para->fd_out_hw_pa[22][0] +
+		4 * pstv->inf_elm[24].out_xsize_plus_1;
+	fd->dma_para->fd_out_hw_pa[24][1] =
+		fd->dma_para->fd_out_hw_pa[22][1] +
+		4 * pstv->inf_elm[24].out_xsize_plus_1;
+	fd->dma_para->fd_out_hw_pa[25][0] =
+		fd->dma_para->fd_out_hw_pa[22][1] +
+		pstv->inf_elm[22].fd_wdma_size[1] +
+		pstv->inf_elm[22].fd_wdma_size[3] +
+		pstv->inf_elm[23].fd_wdma_size[1] +
+		pstv->inf_elm[23].fd_wdma_size[3] +
+		pstv->inf_elm[24].fd_wdma_size[1];
+	fd->dma_para->fd_out_hw_pa[25][1] =
+		fd->dma_para->fd_out_hw_pa[25][0] +
+		pstv->inf_elm[25].out_xsize_plus_1;
+	fd->dma_para->fd_out_hw_pa[26][0] =
+		fd->dma_para->fd_out_hw_pa[25][0] +
+		2 * pstv->inf_elm[26].out_xsize_plus_1;
+	fd->dma_para->fd_out_hw_pa[26][1] =
+		fd->dma_para->fd_out_hw_pa[25][0] +
+		3 * pstv->inf_elm[26].out_xsize_plus_1;
+	fd->dma_para->fd_out_hw_pa[27][0] =
+		fd->dma_para->fd_out_hw_pa[25][0] +
+		4 * pstv->inf_elm[27].out_xsize_plus_1;
+
+	/* 29~47 */
+	fd->dma_para->fd_out_hw_pa[29][0] =
+		fd->dma_para->fd_out_hw_pa[25][0] +
+		pstv->inf_elm[25].fd_wdma_size[0] +
+		pstv->inf_elm[25].fd_wdma_size[1] +
+		pstv->inf_elm[26].fd_wdma_size[0] +
+		pstv->inf_elm[26].fd_wdma_size[1] +
+		pstv->inf_elm[27].fd_wdma_size[0];
+	aie_alloc_normal(fd, 30, 47);
+
+	/* 48~56 */
+	fd->dma_para->fd_out_hw_pa[48][0] =
+		fd->dma_para->fd_out_hw_pa[47][1] +
+		pstv->inf_elm[47].fd_wdma_size[1];
+	fd->dma_para->fd_out_hw_pa[48][1] =
+		fd->dma_para->fd_out_hw_pa[48][0] +
+		pstv->inf_elm[48].out_xsize_plus_1;
+	fd->dma_para->fd_out_hw_pa[49][0] =
+		fd->dma_para->fd_out_hw_pa[48][0] +
+		2 * pstv->inf_elm[49].out_xsize_plus_1;
+	fd->dma_para->fd_out_hw_pa[49][1] =
+		fd->dma_para->fd_out_hw_pa[48][0] +
+		3 * pstv->inf_elm[49].out_xsize_plus_1;
+	fd->dma_para->fd_out_hw_pa[50][0] =
+		fd->dma_para->fd_out_hw_pa[48][0] +
+		4 * pstv->inf_elm[50].out_xsize_plus_1;
+	fd->dma_para->fd_out_hw_pa[51][0] =
+		fd->dma_para->fd_out_hw_pa[48][0] +
+		pstv->inf_elm[48].fd_wdma_size[0] +
+		pstv->inf_elm[48].fd_wdma_size[1] +
+		pstv->inf_elm[49].fd_wdma_size[0] +
+		pstv->inf_elm[49].fd_wdma_size[1] +
+		pstv->inf_elm[50].fd_wdma_size[0];
+	fd->dma_para->fd_out_hw_pa[51][1] =
+		fd->dma_para->fd_out_hw_pa[51][0] +
+		pstv->inf_elm[51].fd_wdma_size[0] +
+		pstv->inf_elm[51].fd_wdma_size[2] +
+		pstv->inf_elm[52].fd_wdma_size[0] +
+		pstv->inf_elm[52].fd_wdma_size[2] +
+		pstv->inf_elm[53].fd_wdma_size[0];
+	fd->dma_para->fd_out_hw_pa[51][2] =
+		fd->dma_para->fd_out_hw_pa[51][0] +
+		pstv->inf_elm[51].out_xsize_plus_1;
+	fd->dma_para->fd_out_hw_pa[51][3] =
+		fd->dma_para->fd_out_hw_pa[51][1] +
+		pstv->inf_elm[51].out_xsize_plus_1;
+	fd->dma_para->fd_out_hw_pa[52][0] =
+		fd->dma_para->fd_out_hw_pa[51][0] +
+		2 * pstv->inf_elm[52].out_xsize_plus_1;
+	fd->dma_para->fd_out_hw_pa[52][1] =
+		fd->dma_para->fd_out_hw_pa[51][1] +
+		2 * pstv->inf_elm[52].out_xsize_plus_1;
+	fd->dma_para->fd_out_hw_pa[52][2] =
+		fd->dma_para->fd_out_hw_pa[51][0] +
+		3 * pstv->inf_elm[52].out_xsize_plus_1;
+	fd->dma_para->fd_out_hw_pa[52][3] =
+		fd->dma_para->fd_out_hw_pa[51][1] +
+		3 * pstv->inf_elm[52].out_xsize_plus_1;
+	fd->dma_para->fd_out_hw_pa[53][0] =
+		fd->dma_para->fd_out_hw_pa[51][0] +
+		4 * pstv->inf_elm[53].out_xsize_plus_1;
+	fd->dma_para->fd_out_hw_pa[53][1] =
+		fd->dma_para->fd_out_hw_pa[51][1] +
+		4 * pstv->inf_elm[53].out_xsize_plus_1;
+	fd->dma_para->fd_out_hw_pa[54][0] =
+		fd->dma_para->fd_out_hw_pa[51][1] +
+		pstv->inf_elm[51].fd_wdma_size[1] +
+		pstv->inf_elm[51].fd_wdma_size[3] +
+		pstv->inf_elm[52].fd_wdma_size[1] +
+		pstv->inf_elm[52].fd_wdma_size[3] +
+		pstv->inf_elm[53].fd_wdma_size[1];
+	fd->dma_para->fd_out_hw_pa[54][1] =
+		fd->dma_para->fd_out_hw_pa[54][0] +
+		pstv->inf_elm[54].out_xsize_plus_1;
+	fd->dma_para->fd_out_hw_pa[55][0] =
+		fd->dma_para->fd_out_hw_pa[54][0] +
+		2 * pstv->inf_elm[55].out_xsize_plus_1;
+	fd->dma_para->fd_out_hw_pa[55][1] =
+		fd->dma_para->fd_out_hw_pa[54][0] +
+		3 * pstv->inf_elm[55].out_xsize_plus_1;
+	fd->dma_para->fd_out_hw_pa[56][0] =
+		fd->dma_para->fd_out_hw_pa[54][0] +
+		4 * pstv->inf_elm[56].out_xsize_plus_1;
+
+	/* 58~76 */
+	fd->dma_para->fd_out_hw_pa[58][0] =
+		fd->dma_para->fd_out_hw_pa[54][0] +
+		pstv->inf_elm[54].fd_wdma_size[0] +
+		pstv->inf_elm[54].fd_wdma_size[1] +
+		pstv->inf_elm[55].fd_wdma_size[0] +
+		pstv->inf_elm[55].fd_wdma_size[1] +
+		pstv->inf_elm[56].fd_wdma_size[0];
+	aie_alloc_normal(fd, 59, 76);
+
+	/* 77~85 */
+	fd->dma_para->fd_out_hw_pa[77][0] =
+		fd->dma_para->fd_out_hw_pa[76][1] +
+		pstv->inf_elm[76].fd_wdma_size[1];
+	fd->dma_para->fd_out_hw_pa[77][1] =
+		fd->dma_para->fd_out_hw_pa[77][0] +
+		pstv->inf_elm[77].out_xsize_plus_1;
+	fd->dma_para->fd_out_hw_pa[78][0] =
+		fd->dma_para->fd_out_hw_pa[77][0] +
+		2 * pstv->inf_elm[78].out_xsize_plus_1;
+	fd->dma_para->fd_out_hw_pa[78][1] =
+		fd->dma_para->fd_out_hw_pa[77][0] +
+		3 * pstv->inf_elm[78].out_xsize_plus_1;
+	fd->dma_para->fd_out_hw_pa[79][0] =
+		fd->dma_para->fd_out_hw_pa[77][0] +
+		4 * pstv->inf_elm[79].out_xsize_plus_1;
+	fd->dma_para->fd_out_hw_pa[80][0] =
+		fd->dma_para->fd_out_hw_pa[77][0] +
+		pstv->inf_elm[77].fd_wdma_size[0] +
+		pstv->inf_elm[77].fd_wdma_size[1] +
+		pstv->inf_elm[78].fd_wdma_size[0] +
+		pstv->inf_elm[78].fd_wdma_size[1] +
+		pstv->inf_elm[79].fd_wdma_size[0];
+	fd->dma_para->fd_out_hw_pa[80][1] =
+		fd->dma_para->fd_out_hw_pa[80][0] +
+		pstv->inf_elm[80].fd_wdma_size[0] +
+		pstv->inf_elm[80].fd_wdma_size[2] +
+		pstv->inf_elm[81].fd_wdma_size[0] +
+		pstv->inf_elm[81].fd_wdma_size[2] +
+		pstv->inf_elm[82].fd_wdma_size[0];
+	fd->dma_para->fd_out_hw_pa[80][2] =
+		fd->dma_para->fd_out_hw_pa[80][0] +
+		pstv->inf_elm[80].out_xsize_plus_1;
+	fd->dma_para->fd_out_hw_pa[80][3] =
+		fd->dma_para->fd_out_hw_pa[80][1] +
+		pstv->inf_elm[80].out_xsize_plus_1;
+	fd->dma_para->fd_out_hw_pa[81][0] =
+		fd->dma_para->fd_out_hw_pa[80][0] +
+		2 * pstv->inf_elm[81].out_xsize_plus_1;
+	fd->dma_para->fd_out_hw_pa[81][1] =
+		fd->dma_para->fd_out_hw_pa[80][1] +
+		2 * pstv->inf_elm[81].out_xsize_plus_1;
+	fd->dma_para->fd_out_hw_pa[81][2] =
+		fd->dma_para->fd_out_hw_pa[80][0] +
+		3 * pstv->inf_elm[81].out_xsize_plus_1;
+	fd->dma_para->fd_out_hw_pa[81][3] =
+		fd->dma_para->fd_out_hw_pa[80][1] +
+		3 * pstv->inf_elm[81].out_xsize_plus_1;
+	fd->dma_para->fd_out_hw_pa[82][0] =
+		fd->dma_para->fd_out_hw_pa[80][0] +
+		4 * pstv->inf_elm[82].out_xsize_plus_1;
+	fd->dma_para->fd_out_hw_pa[82][1] =
+		fd->dma_para->fd_out_hw_pa[80][1] +
+		4 * pstv->inf_elm[82].out_xsize_plus_1;
+	fd->dma_para->fd_out_hw_pa[83][0] =
+		fd->dma_para->fd_out_hw_pa[80][1] +
+		pstv->inf_elm[80].fd_wdma_size[1] +
+		pstv->inf_elm[80].fd_wdma_size[3] +
+		pstv->inf_elm[81].fd_wdma_size[1] +
+		pstv->inf_elm[81].fd_wdma_size[3] +
+		pstv->inf_elm[82].fd_wdma_size[1];
+	fd->dma_para->fd_out_hw_pa[83][1] =
+		fd->dma_para->fd_out_hw_pa[83][0] +
+		pstv->inf_elm[83].out_xsize_plus_1;
+	fd->dma_para->fd_out_hw_pa[84][0] =
+		fd->dma_para->fd_out_hw_pa[83][0] +
+		2 * pstv->inf_elm[84].out_xsize_plus_1;
+	fd->dma_para->fd_out_hw_pa[84][1] =
+		fd->dma_para->fd_out_hw_pa[83][0] +
+		3 * pstv->inf_elm[84].out_xsize_plus_1;
+	fd->dma_para->fd_out_hw_pa[85][0] =
+		fd->dma_para->fd_out_hw_pa[83][0] +
+		4 * pstv->inf_elm[85].out_xsize_plus_1;
+
+	/* VA : except 28, 57, 86 */
+	/* 0~86 */
+	fd->dma_para->fd_out_hw_va[0][0] = fd->fd_dma_hw.va;
+	for (i = 1; i < FD_LOOP_NUM; i++) {
+		if (i == RPN2_LOOP_NUM || i == RPN1_LOOP_NUM ||
+		    i == RPN0_LOOP_NUM)
+			continue;
+		for (j = 0; j < 4; j++) {
+			if (fd_wdma_en[i][j]) {
+				fd->dma_para->fd_out_hw_va[i][j] =
+					fd->fd_dma_hw.va +
+					fd->dma_para->fd_out_hw_pa[i][j] -
+					fd->fd_dma_hw.pa;
+			}
+		}
+	}
+
+	current_pa = fd->dma_para->fd_out_hw_pa[83][0] +
+		    pstv->inf_elm[83].fd_wdma_size[0] +
+		    pstv->inf_elm[83].fd_wdma_size[1] +
+		    pstv->inf_elm[84].fd_wdma_size[0] +
+		    pstv->inf_elm[84].fd_wdma_size[1] +
+		    pstv->inf_elm[85].fd_wdma_size[0];
+	current_va = fd->dma_para->fd_out_hw_va[83][0] +
+		    pstv->inf_elm[83].fd_wdma_size[0] +
+		    pstv->inf_elm[83].fd_wdma_size[1] +
+		    pstv->inf_elm[84].fd_wdma_size[0] +
+		    pstv->inf_elm[84].fd_wdma_size[1] +
+		    pstv->inf_elm[85].fd_wdma_size[0];
+
+	dev_dbg(fd->dev,
+		"%s: current VA = %p PA = 0x%pad\n",
+		__func__,
+		current_va,
+		&current_pa
+	);
+}
+
+static void aie_arrange_kernel_buf(struct mtk_aie_dev *fd)
+{
+	void *current_va = NULL;
+	dma_addr_t current_pa = 0;
+	u8 i = 0, j = 0;
+
+	current_pa = fd->fd_kernel_hw.pa;
+	current_va = fd->fd_kernel_hw.va;
+
+	for (i = 0; i < FD_LOOP_NUM; i++) {
+		for (j = 0; j < KERNEL_RDMA_RA_NUM; j++) {
+			if (fd_ker_rdma_size[i][j]) {
+				fd->dma_para->fd_kernel_pa[i][j] = current_pa;
+				fd->dma_para->fd_kernel_va[i][j] = current_va;
+				current_pa += fd_ker_rdma_size[i][j];
+				current_va += fd_ker_rdma_size[i][j];
+			}
+		}
+	}
+
+	for (i = 0; i < ATTR_LOOP_NUM; i++) {
+		for (j = 0; j < KERNEL_RDMA_RA_NUM; j++) {
+			fd->dma_para->attr_kernel_pa[i][j] = current_pa;
+			fd->dma_para->attr_kernel_va[i][j] = current_va;
+			current_pa += attr_ker_rdma_size[i][j];
+			current_va += attr_ker_rdma_size[i][j];
+		}
+	}
+
+	dev_dbg(fd->dev,
+		"%s: current VA = %p PA = 0x%pad\n",
+		__func__,
+		current_va,
+		&current_pa
+	);
+}
+
+static void aie_arrange_attrdma_buf(struct mtk_aie_dev *fd)
+{
+	void *current_va = NULL;
+	dma_addr_t current_pa = 0;
+	u8 i = 0, j = 0;
+
+	current_pa = fd->fd_attr_dma_hw.pa;
+	current_va = fd->fd_attr_dma_hw.va;
+
+	/* attribute mode */
+	for (i = 0; i < ATTR_LOOP_NUM; i++) {
+		for (j = 0; j < OUTPUT_WDMA_WRA_NUM; j++) {
+			if (attr_wdma_en[i][j]) {
+				fd->dma_para->attr_out_hw_pa[i][j] = current_pa;
+				fd->dma_para->attr_out_hw_va[i][j] = current_va;
+				current_pa += attr_wdma_size[i][j];
+				current_va += attr_wdma_size[i][j];
+			}
+		}
+	}
+
+	dev_dbg(fd->dev,
+		"%s: current VA = %p PA = 0x%pad\n",
+		__func__,
+		current_va,
+		&current_pa
+	);
+}
+
+static void aie_arrange_result_dma_buf(struct mtk_aie_dev *fd)
+{
+	void *currentresult_va = NULL;
+	dma_addr_t currentresult_pa = 0;
+	u8 i = 0;
+	struct aie_static_info *pstv = &fd->st_info;
+
+	currentresult_pa = fd->fd_dma_result_hw.pa;
+	currentresult_va = fd->fd_dma_result_hw.va;
+
+	fd->dma_para->fd_out_hw_pa[RPN2_LOOP_NUM][0] = currentresult_pa;
+	fd->dma_para->fd_out_hw_va[RPN2_LOOP_NUM][0] = currentresult_va;
+	currentresult_pa += pstv->inf_elm[RPN2_LOOP_NUM].fd_wdma_size[0];
+	currentresult_va += pstv->inf_elm[RPN2_LOOP_NUM].fd_wdma_size[0];
+	fd->dma_para->fd_out_hw_pa[RPN1_LOOP_NUM][0] = currentresult_pa;
+	fd->dma_para->fd_out_hw_va[RPN1_LOOP_NUM][0] = currentresult_va;
+	currentresult_pa += pstv->inf_elm[RPN1_LOOP_NUM].fd_wdma_size[0];
+	currentresult_va += pstv->inf_elm[RPN1_LOOP_NUM].fd_wdma_size[0];
+	fd->dma_para->fd_out_hw_pa[RPN0_LOOP_NUM][0] = currentresult_pa;
+	fd->dma_para->fd_out_hw_va[RPN0_LOOP_NUM][0] = currentresult_va;
+	currentresult_pa += pstv->inf_elm[RPN0_LOOP_NUM].fd_wdma_size[0];
+	currentresult_va += pstv->inf_elm[RPN0_LOOP_NUM].fd_wdma_size[0];
+
+	fd->dma_para->attr_out_hw_pa[AGE_OUT_RGS][0] = currentresult_pa;
+	fd->dma_para->attr_out_hw_va[AGE_OUT_RGS][0] = currentresult_va;
+	currentresult_pa += ATTR_OUT_SIZE * MAX_ENQUE_FRAME_NUM;
+	currentresult_va += ATTR_OUT_SIZE * MAX_ENQUE_FRAME_NUM;
+	fd->dma_para->attr_out_hw_pa[GENDER_OUT_RGS][0] = currentresult_pa;
+	fd->dma_para->attr_out_hw_va[GENDER_OUT_RGS][0] = currentresult_va;
+	currentresult_pa += ATTR_OUT_SIZE * MAX_ENQUE_FRAME_NUM;
+	currentresult_va += ATTR_OUT_SIZE * MAX_ENQUE_FRAME_NUM;
+	fd->dma_para->attr_out_hw_pa[INDIAN_OUT_RGS][0] = currentresult_pa;
+	fd->dma_para->attr_out_hw_va[INDIAN_OUT_RGS][0] = currentresult_va;
+	currentresult_pa += ATTR_OUT_SIZE * MAX_ENQUE_FRAME_NUM;
+	currentresult_va += ATTR_OUT_SIZE * MAX_ENQUE_FRAME_NUM;
+	fd->dma_para->attr_out_hw_pa[RACE_OUT_RGS][0] = currentresult_pa;
+	fd->dma_para->attr_out_hw_va[RACE_OUT_RGS][0] = currentresult_va;
+	currentresult_pa += ATTR_OUT_SIZE * MAX_ENQUE_FRAME_NUM;
+	currentresult_va += ATTR_OUT_SIZE * MAX_ENQUE_FRAME_NUM;
+
+	/* need to prepare 10 buffers to store 10 times result */
+	fd->dma_para->age_out_hw_pa[0] =
+		fd->dma_para->attr_out_hw_pa[AGE_OUT_RGS][0];
+	fd->dma_para->age_out_hw_va[0] =
+		fd->dma_para->attr_out_hw_va[AGE_OUT_RGS][0];
+	fd->dma_para->gender_out_hw_pa[0] =
+		fd->dma_para->attr_out_hw_pa[GENDER_OUT_RGS][0];
+	fd->dma_para->gender_out_hw_va[0] =
+		fd->dma_para->attr_out_hw_va[GENDER_OUT_RGS][0];
+	fd->dma_para->is_indian_out_hw_pa[0] =
+		fd->dma_para->attr_out_hw_pa[INDIAN_OUT_RGS][0];
+	fd->dma_para->is_indian_out_hw_va[0] =
+		fd->dma_para->attr_out_hw_va[INDIAN_OUT_RGS][0];
+	fd->dma_para->race_out_hw_pa[0] =
+		fd->dma_para->attr_out_hw_pa[RACE_OUT_RGS][0];
+	fd->dma_para->race_out_hw_va[0] =
+		fd->dma_para->attr_out_hw_va[RACE_OUT_RGS][0];
+
+	for (i = 1; i < MAX_ENQUE_FRAME_NUM; i++) {
+		fd->dma_para->age_out_hw_pa[i] =
+			fd->dma_para->age_out_hw_pa[i - 1] + ATTR_OUT_SIZE;
+		fd->dma_para->age_out_hw_va[i] =
+			fd->dma_para->age_out_hw_va[i - 1] + ATTR_OUT_SIZE;
+		fd->dma_para->gender_out_hw_pa[i] =
+			fd->dma_para->gender_out_hw_pa[i - 1] + ATTR_OUT_SIZE;
+		fd->dma_para->gender_out_hw_va[i] =
+			fd->dma_para->gender_out_hw_va[i - 1] + ATTR_OUT_SIZE;
+		fd->dma_para->is_indian_out_hw_pa[i] =
+			fd->dma_para->is_indian_out_hw_pa[i - 1] + ATTR_OUT_SIZE;
+		fd->dma_para->is_indian_out_hw_va[i] =
+			fd->dma_para->is_indian_out_hw_va[i - 1] + ATTR_OUT_SIZE;
+		fd->dma_para->race_out_hw_pa[i] =
+			fd->dma_para->race_out_hw_pa[i - 1] + ATTR_OUT_SIZE;
+		fd->dma_para->race_out_hw_va[i] =
+			fd->dma_para->race_out_hw_va[i - 1] + ATTR_OUT_SIZE;
+	}
+
+	memset(fd->fd_dma_result_hw.va, 0, fd->fd_dma_result_hw.size);
+
+	dev_dbg(fd->dev,
+		"%s: current VA = %p PA = 0x%pad\n",
+		__func__,
+		currentresult_va,
+		&currentresult_pa
+	);
+}
+
+static void aie_arrange_fld_buf(struct mtk_aie_dev *fd)
+{
+	u8 i = 0, j = 0;
+	unsigned int offset = 0;
+
+	for (i = 0; i < FLD_STEP_NUM; i++) {
+		for (j = 0; j < FLD_MAX_FRAME; j++) {
+			fd->fld_para->fld_step_va[i][j] =
+				fd->fd_fld_step_data.va + offset;
+			fd->fld_para->fld_step_pa[i][j] =
+				fd->fd_fld_step_data.pa + offset;
+			offset += fld_step_align_size[i][j];
+		}
+	}
+
+	for (i = 0, offset = 0; i < FLD_MAX_FRAME; i++) {
+		fd->fld_para->fld_output_va[i] = fd->fd_fld_out_hw.va + offset;
+		fd->fld_para->fld_output_pa[i] = fd->fd_fld_out_hw.pa + offset;
+		offset += FLD_OUTPUT_SIZE;
+	}
+}
+
+static void aie_update_fddma_buf(struct mtk_aie_dev *fd)
+{
+	struct aie_static_info *pstv = &fd->st_info;
+	u8 i = 0, j = 0;
+
+	/* 19~27 */
+	fd->dma_para->fd_out_hw_pa[19][0] =
+		fd->dma_para->fd_out_hw_pa[18][1] +
+		pstv->inf_elm[18].fd_wdma_size[1];
+	fd->dma_para->fd_out_hw_pa[19][1] =
+		fd->dma_para->fd_out_hw_pa[19][0] +
+		pstv->inf_elm[19].out_xsize_plus_1;
+	fd->dma_para->fd_out_hw_pa[20][0] =
+		fd->dma_para->fd_out_hw_pa[19][0] +
+		2 * pstv->inf_elm[20].out_xsize_plus_1;
+	fd->dma_para->fd_out_hw_pa[20][1] =
+		fd->dma_para->fd_out_hw_pa[19][0] +
+		3 * pstv->inf_elm[20].out_xsize_plus_1;
+	fd->dma_para->fd_out_hw_pa[21][0] =
+		fd->dma_para->fd_out_hw_pa[19][0] +
+		4 * pstv->inf_elm[21].out_xsize_plus_1;
+	fd->dma_para->fd_out_hw_pa[22][0] =
+		fd->dma_para->fd_out_hw_pa[19][0] +
+		pstv->inf_elm[19].fd_wdma_size[0] +
+		pstv->inf_elm[19].fd_wdma_size[1] +
+		pstv->inf_elm[20].fd_wdma_size[0] +
+		pstv->inf_elm[20].fd_wdma_size[1] +
+		pstv->inf_elm[21].fd_wdma_size[0];
+	fd->dma_para->fd_out_hw_pa[22][1] =
+		fd->dma_para->fd_out_hw_pa[22][0] +
+		pstv->inf_elm[22].fd_wdma_size[0] +
+		pstv->inf_elm[22].fd_wdma_size[2] +
+		pstv->inf_elm[23].fd_wdma_size[0] +
+		pstv->inf_elm[23].fd_wdma_size[2] +
+		pstv->inf_elm[24].fd_wdma_size[0];
+	fd->dma_para->fd_out_hw_pa[22][2] =
+		fd->dma_para->fd_out_hw_pa[22][0] +
+		pstv->inf_elm[22].out_xsize_plus_1;
+	fd->dma_para->fd_out_hw_pa[22][3] =
+		fd->dma_para->fd_out_hw_pa[22][1] +
+		pstv->inf_elm[22].out_xsize_plus_1;
+	fd->dma_para->fd_out_hw_pa[23][0] =
+		fd->dma_para->fd_out_hw_pa[22][0] +
+		2 * pstv->inf_elm[23].out_xsize_plus_1;
+	fd->dma_para->fd_out_hw_pa[23][1] =
+		fd->dma_para->fd_out_hw_pa[22][1] +
+		2 * pstv->inf_elm[23].out_xsize_plus_1;
+	fd->dma_para->fd_out_hw_pa[23][2] =
+		fd->dma_para->fd_out_hw_pa[22][0] +
+		3 * pstv->inf_elm[23].out_xsize_plus_1;
+	fd->dma_para->fd_out_hw_pa[23][3] =
+		fd->dma_para->fd_out_hw_pa[22][1] +
+		3 * pstv->inf_elm[23].out_xsize_plus_1;
+	fd->dma_para->fd_out_hw_pa[24][0] =
+		fd->dma_para->fd_out_hw_pa[22][0] +
+		4 * pstv->inf_elm[24].out_xsize_plus_1;
+	fd->dma_para->fd_out_hw_pa[24][1] =
+		fd->dma_para->fd_out_hw_pa[22][1] +
+		4 * pstv->inf_elm[24].out_xsize_plus_1;
+	fd->dma_para->fd_out_hw_pa[25][0] =
+		fd->dma_para->fd_out_hw_pa[22][1] +
+		pstv->inf_elm[22].fd_wdma_size[1] +
+		pstv->inf_elm[22].fd_wdma_size[3] +
+		pstv->inf_elm[23].fd_wdma_size[1] +
+		pstv->inf_elm[23].fd_wdma_size[3] +
+		pstv->inf_elm[24].fd_wdma_size[1];
+	fd->dma_para->fd_out_hw_pa[25][1] =
+		fd->dma_para->fd_out_hw_pa[25][0] +
+		pstv->inf_elm[25].out_xsize_plus_1;
+	fd->dma_para->fd_out_hw_pa[26][0] =
+		fd->dma_para->fd_out_hw_pa[25][0] +
+		2 * pstv->inf_elm[26].out_xsize_plus_1;
+	fd->dma_para->fd_out_hw_pa[26][1] =
+		fd->dma_para->fd_out_hw_pa[25][0] +
+		3 * pstv->inf_elm[26].out_xsize_plus_1;
+	fd->dma_para->fd_out_hw_pa[27][0] =
+		fd->dma_para->fd_out_hw_pa[25][0] +
+		4 * pstv->inf_elm[27].out_xsize_plus_1;
+
+	/* 48~56 */
+	fd->dma_para->fd_out_hw_pa[48][0] =
+		fd->dma_para->fd_out_hw_pa[47][1] +
+		pstv->inf_elm[47].fd_wdma_size[1];
+	fd->dma_para->fd_out_hw_pa[48][1] =
+		fd->dma_para->fd_out_hw_pa[48][0] +
+		pstv->inf_elm[48].out_xsize_plus_1;
+	fd->dma_para->fd_out_hw_pa[49][0] =
+		fd->dma_para->fd_out_hw_pa[48][0] +
+		2 * pstv->inf_elm[49].out_xsize_plus_1;
+	fd->dma_para->fd_out_hw_pa[49][1] =
+		fd->dma_para->fd_out_hw_pa[48][0] +
+		3 * pstv->inf_elm[49].out_xsize_plus_1;
+	fd->dma_para->fd_out_hw_pa[50][0] =
+		fd->dma_para->fd_out_hw_pa[48][0] +
+		4 * pstv->inf_elm[50].out_xsize_plus_1;
+	fd->dma_para->fd_out_hw_pa[51][0] =
+		fd->dma_para->fd_out_hw_pa[48][0] +
+		pstv->inf_elm[48].fd_wdma_size[0] +
+		pstv->inf_elm[48].fd_wdma_size[1] +
+		pstv->inf_elm[49].fd_wdma_size[0] +
+		pstv->inf_elm[49].fd_wdma_size[1] +
+		pstv->inf_elm[50].fd_wdma_size[0];
+	fd->dma_para->fd_out_hw_pa[51][1] =
+		fd->dma_para->fd_out_hw_pa[51][0] +
+		pstv->inf_elm[51].fd_wdma_size[0] +
+		pstv->inf_elm[51].fd_wdma_size[2] +
+		pstv->inf_elm[52].fd_wdma_size[0] +
+		pstv->inf_elm[52].fd_wdma_size[2] +
+		pstv->inf_elm[53].fd_wdma_size[0];
+	fd->dma_para->fd_out_hw_pa[51][2] =
+		fd->dma_para->fd_out_hw_pa[51][0] +
+		pstv->inf_elm[51].out_xsize_plus_1;
+	fd->dma_para->fd_out_hw_pa[51][3] =
+		fd->dma_para->fd_out_hw_pa[51][1] +
+		pstv->inf_elm[51].out_xsize_plus_1;
+	fd->dma_para->fd_out_hw_pa[52][0] =
+		fd->dma_para->fd_out_hw_pa[51][0] +
+		2 * pstv->inf_elm[52].out_xsize_plus_1;
+	fd->dma_para->fd_out_hw_pa[52][1] =
+		fd->dma_para->fd_out_hw_pa[51][1] +
+		2 * pstv->inf_elm[52].out_xsize_plus_1;
+	fd->dma_para->fd_out_hw_pa[52][2] =
+		fd->dma_para->fd_out_hw_pa[51][0] +
+		3 * pstv->inf_elm[52].out_xsize_plus_1;
+	fd->dma_para->fd_out_hw_pa[52][3] =
+		fd->dma_para->fd_out_hw_pa[51][1] +
+		3 * pstv->inf_elm[52].out_xsize_plus_1;
+	fd->dma_para->fd_out_hw_pa[53][0] =
+		fd->dma_para->fd_out_hw_pa[51][0] +
+		4 * pstv->inf_elm[53].out_xsize_plus_1;
+	fd->dma_para->fd_out_hw_pa[53][1] =
+		fd->dma_para->fd_out_hw_pa[51][1] +
+		4 * pstv->inf_elm[53].out_xsize_plus_1;
+	fd->dma_para->fd_out_hw_pa[54][0] =
+		fd->dma_para->fd_out_hw_pa[51][1] +
+		pstv->inf_elm[51].fd_wdma_size[1] +
+		pstv->inf_elm[51].fd_wdma_size[3] +
+		pstv->inf_elm[52].fd_wdma_size[1] +
+		pstv->inf_elm[52].fd_wdma_size[3] +
+		pstv->inf_elm[53].fd_wdma_size[1];
+	fd->dma_para->fd_out_hw_pa[54][1] =
+		fd->dma_para->fd_out_hw_pa[54][0] +
+		pstv->inf_elm[54].out_xsize_plus_1;
+	fd->dma_para->fd_out_hw_pa[55][0] =
+		fd->dma_para->fd_out_hw_pa[54][0] +
+		2 * pstv->inf_elm[55].out_xsize_plus_1;
+	fd->dma_para->fd_out_hw_pa[55][1] =
+		fd->dma_para->fd_out_hw_pa[54][0] +
+		3 * pstv->inf_elm[55].out_xsize_plus_1;
+	fd->dma_para->fd_out_hw_pa[56][0] =
+		fd->dma_para->fd_out_hw_pa[54][0] +
+		4 * pstv->inf_elm[56].out_xsize_plus_1;
+	/* 77~85 */
+	fd->dma_para->fd_out_hw_pa[77][0] =
+		fd->dma_para->fd_out_hw_pa[76][1] +
+		pstv->inf_elm[76].fd_wdma_size[1];
+	fd->dma_para->fd_out_hw_pa[77][1] =
+		fd->dma_para->fd_out_hw_pa[77][0] +
+		pstv->inf_elm[77].out_xsize_plus_1;
+	fd->dma_para->fd_out_hw_pa[78][0] =
+		fd->dma_para->fd_out_hw_pa[77][0] +
+		2 * pstv->inf_elm[78].out_xsize_plus_1;
+	fd->dma_para->fd_out_hw_pa[78][1] =
+		fd->dma_para->fd_out_hw_pa[77][0] +
+		3 * pstv->inf_elm[78].out_xsize_plus_1;
+	fd->dma_para->fd_out_hw_pa[79][0] =
+		fd->dma_para->fd_out_hw_pa[77][0] +
+		4 * pstv->inf_elm[79].out_xsize_plus_1;
+	fd->dma_para->fd_out_hw_pa[80][0] =
+		fd->dma_para->fd_out_hw_pa[77][0] +
+		pstv->inf_elm[77].fd_wdma_size[0] +
+		pstv->inf_elm[77].fd_wdma_size[1] +
+		pstv->inf_elm[78].fd_wdma_size[0] +
+		pstv->inf_elm[78].fd_wdma_size[1] +
+		pstv->inf_elm[79].fd_wdma_size[0];
+	fd->dma_para->fd_out_hw_pa[80][1] =
+		fd->dma_para->fd_out_hw_pa[80][0] +
+		pstv->inf_elm[80].fd_wdma_size[0] +
+		pstv->inf_elm[80].fd_wdma_size[2] +
+		pstv->inf_elm[81].fd_wdma_size[0] +
+		pstv->inf_elm[81].fd_wdma_size[2] +
+		pstv->inf_elm[82].fd_wdma_size[0];
+	fd->dma_para->fd_out_hw_pa[80][2] =
+		fd->dma_para->fd_out_hw_pa[80][0] +
+		pstv->inf_elm[80].out_xsize_plus_1;
+	fd->dma_para->fd_out_hw_pa[80][3] =
+		fd->dma_para->fd_out_hw_pa[80][1] +
+		pstv->inf_elm[80].out_xsize_plus_1;
+	fd->dma_para->fd_out_hw_pa[81][0] =
+		fd->dma_para->fd_out_hw_pa[80][0] +
+		2 * pstv->inf_elm[81].out_xsize_plus_1;
+	fd->dma_para->fd_out_hw_pa[81][1] =
+		fd->dma_para->fd_out_hw_pa[80][1] +
+		2 * pstv->inf_elm[81].out_xsize_plus_1;
+	fd->dma_para->fd_out_hw_pa[81][2] =
+		fd->dma_para->fd_out_hw_pa[80][0] +
+		3 * pstv->inf_elm[81].out_xsize_plus_1;
+	fd->dma_para->fd_out_hw_pa[81][3] =
+		fd->dma_para->fd_out_hw_pa[80][1] +
+		3 * pstv->inf_elm[81].out_xsize_plus_1;
+	fd->dma_para->fd_out_hw_pa[82][0] =
+		fd->dma_para->fd_out_hw_pa[80][0] +
+		4 * pstv->inf_elm[82].out_xsize_plus_1;
+	fd->dma_para->fd_out_hw_pa[82][1] =
+		fd->dma_para->fd_out_hw_pa[80][1] +
+		4 * pstv->inf_elm[82].out_xsize_plus_1;
+	fd->dma_para->fd_out_hw_pa[83][0] =
+		fd->dma_para->fd_out_hw_pa[80][1] +
+		pstv->inf_elm[80].fd_wdma_size[1] +
+		pstv->inf_elm[80].fd_wdma_size[3] +
+		pstv->inf_elm[81].fd_wdma_size[1] +
+		pstv->inf_elm[81].fd_wdma_size[3] +
+		pstv->inf_elm[82].fd_wdma_size[1];
+	fd->dma_para->fd_out_hw_pa[83][1] =
+		fd->dma_para->fd_out_hw_pa[83][0] +
+		pstv->inf_elm[83].out_xsize_plus_1;
+	fd->dma_para->fd_out_hw_pa[84][0] =
+		fd->dma_para->fd_out_hw_pa[83][0] +
+		2 * pstv->inf_elm[84].out_xsize_plus_1;
+	fd->dma_para->fd_out_hw_pa[84][1] =
+		fd->dma_para->fd_out_hw_pa[83][0] +
+		3 * pstv->inf_elm[84].out_xsize_plus_1;
+	fd->dma_para->fd_out_hw_pa[85][0] =
+		fd->dma_para->fd_out_hw_pa[83][0] +
+		4 * pstv->inf_elm[85].out_xsize_plus_1;
+
+	/* VA : except 28, 57, 86 */
+	/* 0~86 */
+	fd->dma_para->fd_out_hw_va[0][0] = fd->fd_dma_hw.va;
+	for (i = 1; i < FD_LOOP_NUM; i++) {
+		if (i == RPN2_LOOP_NUM || i == RPN1_LOOP_NUM ||
+		    i == RPN0_LOOP_NUM)
+			continue;
+		for (j = 0; j < 4; j++) {
+			if (fd_wdma_en[i][j]) {
+				fd->dma_para->fd_out_hw_va[i][j] =
+					fd->fd_dma_hw.va +
+					fd->dma_para->fd_out_hw_pa[i][j] -
+					fd->fd_dma_hw.pa;
+			}
+		}
+	}
+}
+
+static void aie_free_dram_buf(struct mtk_aie_dev *fd)
+{
+	aie_imem_free(fd, &fd->rs_cfg_data);
+	aie_imem_free(fd, &fd->fd_cfg_data);
+	aie_imem_free(fd, &fd->yuv2rgb_cfg_data);
+}
+
+static void aie_free_output_buf(struct mtk_aie_dev *fd)
+{
+	aie_imem_free(fd, &fd->rs_output_hw);
+}
+
+static void aie_free_fddma_buf(struct mtk_aie_dev *fd)
+{
+	aie_imem_free(fd, &fd->fd_dma_hw);
+	aie_imem_free(fd, &fd->fd_kernel_hw);
+	aie_imem_free(fd, &fd->fd_attr_dma_hw);
+	aie_imem_free(fd, &fd->fd_dma_result_hw);
+}
+
+static void aie_free_fld_buf(struct mtk_aie_dev *fd)
+{
+	aie_imem_free(fd, &fd->fd_fld_step_data);
+	aie_imem_free(fd, &fd->fd_fld_out_hw);
+}
+
+static int aie_copy_fw(struct mtk_aie_dev *fd, const char *name, void *buf,
+		       unsigned int size)
+{
+	int ret = -EINVAL;
+	const struct firmware *fw = NULL;
+
+	ret = request_firmware(&fw, name, fd->dev);
+	if (ret == 0) {
+		if (size >= fw->size)
+			memcpy(buf, fw->data, fw->size);
+		else
+			ret = -EINVAL;
+	}
+
+	release_firmware(fw);
+
+	return ret;
+}
+
+static int aie_load_fw(struct mtk_aie_dev *fd)
+{
+	u8 i = 0, j = 0;
+	int ret = -EINVAL;
+	char name[128] = {};
+	char *sel_folder = NULL;
+	char *mp_fw30_folder = "aie_mp_fw";
+	char *mp_fw31_folder = "aie_mp_fw31";
+
+	if (fd->variant->hw_version == 30)
+		sel_folder = mp_fw30_folder;
+	else if (fd->variant->hw_version == 31)
+		sel_folder = mp_fw31_folder;
+	else
+		return -EINVAL;
+
+	ret = sprintf(name, "%s/config/aie_fd_fd_config.bin", sel_folder);
+	if (ret < 0)
+		return ret;
+
+	ret = aie_copy_fw(fd,
+			  name,
+			  fd->base_para->fd_fd_cfg_va,
+			  fd->fd_fd_cfg_size
+		);
+	if (ret)
+		return ret;
+
+	ret = sprintf(name, "%s/config/aie_fd_rs_config.bin", sel_folder);
+	if (ret < 0)
+		return ret;
+
+	ret = aie_copy_fw(fd,
+			  name,
+			  fd->base_para->fd_rs_cfg_va,
+			  fd->fd_rs_cfg_size
+		);
+	if (ret)
+		return ret;
+
+	ret = sprintf(name, "%s/config/aie_fd_yuv2rgb_config.bin", sel_folder);
+	if (ret < 0)
+		return ret;
+
+	ret = aie_copy_fw(fd,
+			  name,
+			  fd->base_para->fd_yuv2rgb_cfg_va,
+			  fd->fd_yuv2rgb_cfg_size
+		);
+	if (ret)
+		return ret;
+
+	ret = sprintf(name, "%s/config/aie_attr_fd_config.bin", sel_folder);
+	if (ret < 0)
+		return ret;
+
+	ret = aie_copy_fw(fd,
+			  name,
+			  fd->base_para->attr_fd_cfg_va[0],
+			  fd->attr_fd_cfg_size
+		);
+	if (ret)
+		return ret;
+
+	ret = sprintf(name, "%s/config/aie_attr_yuv2rgb_config.bin", sel_folder);
+	if (ret < 0)
+		return ret;
+
+	ret = aie_copy_fw(fd,
+			  name,
+			  fd->base_para->attr_yuv2rgb_cfg_va[0],
+			  fd->attr_yuv2rgb_cfg_size
+		);
+	if (ret)
+		return ret;
+
+	for (i = 1; i < MAX_ENQUE_FRAME_NUM; i++) {
+		memcpy(fd->base_para->attr_fd_cfg_va[i],
+		       fd->base_para->attr_fd_cfg_va[0], fd->attr_fd_cfg_size);
+		memcpy(fd->base_para->attr_yuv2rgb_cfg_va[i],
+		       fd->base_para->attr_yuv2rgb_cfg_va[0],
+		       fd->attr_yuv2rgb_cfg_size);
+	}
+
+	for (i = 0; i < FD_LOOP_NUM; i++) {
+		for (j = 0; j < KERNEL_RDMA_RA_NUM; j++) {
+			if (fd_ker_rdma_size[i][j]) {
+				ret = sprintf(name,
+					      "%s/kernel/aie_fd_kernel_bias_loop%02d_%d.bin",
+					      sel_folder,
+					      i,
+					      j
+					);
+				if (ret < 0)
+					return ret;
+
+				ret = aie_copy_fw(fd,
+						  name,
+						  fd->dma_para->fd_kernel_va[i][j],
+						  fd_ker_rdma_size[i][j]
+					);
+				if (ret)
+					return ret;
+			}
+		}
+	}
+
+	for (i = 0; i < ATTR_LOOP_NUM; i++) {
+		for (j = 0; j < KERNEL_RDMA_RA_NUM; j++) {
+			ret = sprintf(name,
+				      "%s/kernel/aie_attr_kernel_bias_loop%02d_%d.bin",
+				      sel_folder,
+				      i,
+				      j
+				);
+			if (ret < 0)
+				return ret;
+
+			ret = aie_copy_fw(fd,
+					  name,
+					  fd->dma_para->attr_kernel_va[i][j],
+					  attr_ker_rdma_size[i][j]
+				);
+			if (ret)
+				return ret;
+		}
+	}
+
+	if (fd->variant->fld_enable) {
+		ret = sprintf(name, "%s/config/aie_fld_blink_weight_forest14.bin", sel_folder);
+		if (ret < 0)
+			return ret;
+
+		ret = aie_copy_fw(fd,
+				  name,
+				  fd->fld_para->fld_step_va[FLD_STEP_BLINK][14],
+				  fld_step_align_size[FLD_STEP_BLINK][14]
+			);
+		if (ret)
+			return ret;
+
+		for (j = 0; j < FLD_MAX_FRAME; j++) {
+			ret = sprintf(name,
+				      "%s/config/aie_fld_cv_forest%02d_iom3.bin",
+				      sel_folder,
+				      j
+				);
+			if (ret < 0)
+				return ret;
+
+			ret = aie_copy_fw(fd,
+					  name,
+					  fd->fld_para->fld_step_va[FLD_STEP_CV][j],
+					  fld_step_align_size[FLD_STEP_CV][j]
+				);
+			if (ret)
+				return ret;
+		}
+
+		for (j = 0; j < FLD_MAX_FRAME; j++) {
+			ret = sprintf(name,
+				      "%s/config/aie_fld_fp_forest%02d_om45.bin",
+				      sel_folder,
+				      j
+				);
+			if (ret < 0)
+				return ret;
+
+			ret = aie_copy_fw(fd,
+					  name,
+					  fd->fld_para->fld_step_va[FLD_STEP_FP][j],
+					  fld_step_align_size[FLD_STEP_FP][j]
+				);
+			if (ret)
+				return ret;
+		}
+
+		for (j = 0; j < FLD_MAX_FRAME; j++) {
+			ret = sprintf(name,
+				      "%s/config/aie_fld_leafnode_forest%02d.bin",
+				      sel_folder,
+				      j
+				);
+			if (ret < 0)
+				return ret;
+
+			ret = aie_copy_fw(fd,
+					  name,
+					  fd->fld_para->fld_step_va[FLD_STEP_LEAF][j],
+					  fld_step_align_size[FLD_STEP_LEAF][j]
+				);
+			if (ret)
+				return ret;
+		}
+
+		for (j = 0; j < FLD_MAX_FRAME; j++) {
+			ret = sprintf(name,
+				      "%s/config/aie_fld_tree_forest%02d_km02.bin",
+				      sel_folder,
+				      j
+				);
+			if (ret < 0)
+				return ret;
+			ret = aie_copy_fw(fd,
+					  name,
+					  fd->fld_para->fld_step_va[FLD_STEP_KM02][j],
+					  fld_step_align_size[FLD_STEP_KM02][j]
+				);
+			if (ret)
+				return ret;
+		}
+
+		for (j = 0; j < FLD_MAX_FRAME; j++) {
+			ret = sprintf(name,
+				      "%s/config/aie_fld_tree_forest%02d_km13.bin",
+				      sel_folder,
+				      j
+				);
+			if (ret < 0)
+				return ret;
+			ret = aie_copy_fw(fd,
+					  name,
+					  fd->fld_para->fld_step_va[FLD_STEP_KM13][j],
+					  fld_step_align_size[FLD_STEP_KM13][j]
+				);
+			if (ret)
+				return ret;
+		}
+	}
+
+	return ret;
+}
+
+static void aie_reset_output_buf(struct mtk_aie_dev *fd,
+				 struct aie_enq_info *aie_cfg)
+{
+	if (aie_cfg->sel_mode == FDMODE) {
+		memset(fd->rs_output_hw.va, 0, fd->rs_output_hw.size);
+		memset(fd->dma_para->fd_out_hw_va[RPN0_LOOP_NUM][0], 0,
+		       RESULT_SIZE);
+		memset(fd->dma_para->fd_out_hw_va[RPN1_LOOP_NUM][0], 0,
+		       RESULT_SIZE);
+		memset(fd->dma_para->fd_out_hw_va[RPN2_LOOP_NUM][0], 0,
+		       RESULT_SIZE);
+	} else if (aie_cfg->sel_mode == ATTRIBUTEMODE) {
+		memset(fd->base_para->rs_pym_rst_va[0][0], 0,
+		       fd->rs_pym_out_size[0]);
+		memset(fd->base_para->rs_pym_rst_va[0][1], 0,
+		       fd->rs_pym_out_size[0]);
+		memset(fd->base_para->rs_pym_rst_va[0][2], 0,
+		       fd->rs_pym_out_size[0]);
+	} else if (aie_cfg->sel_mode == FLDMODE) {
+		if (fd->variant->fld_enable)
+			memset(fd->fld_para->fld_output_va[0], 0,
+			       FLD_MAX_FRAME * FLD_OUTPUT_SIZE);
+	}
+}
+
+static int aie_update_cfg(struct mtk_aie_dev *fd, struct aie_enq_info *aie_cfg)
+{
+	int crop_width = 0;
+	int crop_height = 0;
+
+	crop_width = aie_cfg->src_img_width;
+	crop_height = aie_cfg->src_img_height;
+
+	if (aie_cfg->en_roi) {
+		crop_width = dif_x(aie_cfg) + 1;
+		crop_height = dif_y(aie_cfg) + 1;
+	}
+
+	if (crop_width == 0 || crop_height == 0) {
+		dev_err(fd->dev, "AIE error:crop size is wrong");
+		return -EINVAL;
+	}
+
+	if (aie_cfg->en_padding) {
+		crop_width = crop_width + aie_cfg->src_padding.right +
+			     aie_cfg->src_padding.left;
+		crop_height = crop_height + aie_cfg->src_padding.up +
+			      aie_cfg->src_padding.down;
+	}
+
+	if (aie_cfg->sel_mode == FDMODE) {
+		fd->base_para->sel_mode = aie_cfg->sel_mode;
+		fd->base_para->crop_width = crop_width;
+		fd->base_para->crop_height = crop_height;
+		fd->base_para->src_img_addr = aie_cfg->src_img_addr;
+		fd->base_para->src_img_addr_uv = aie_cfg->src_img_addr_uv;
+		fd->base_para->img_width = aie_cfg->src_img_width;
+		fd->base_para->img_height = aie_cfg->src_img_height;
+		fd->base_para->src_img_fmt = aie_cfg->src_img_fmt;
+		fd->base_para->rotate_degree = aie_cfg->rotate_degree;
+	} else if (aie_cfg->sel_mode == ATTRIBUTEMODE) {
+		fd->attr_para->sel_mode[fd->attr_para->w_idx] =
+			aie_cfg->sel_mode;
+		fd->attr_para->crop_width[fd->attr_para->w_idx] = crop_width;
+		fd->attr_para->crop_height[fd->attr_para->w_idx] = crop_height;
+		fd->attr_para->src_img_addr[fd->attr_para->w_idx] =
+			aie_cfg->src_img_addr;
+		fd->attr_para->src_img_addr_uv[fd->attr_para->w_idx] =
+			aie_cfg->src_img_addr_uv;
+		fd->attr_para->img_width[fd->attr_para->w_idx] =
+			aie_cfg->src_img_width;
+		fd->attr_para->img_height[fd->attr_para->w_idx] =
+			aie_cfg->src_img_height;
+		fd->attr_para->src_img_fmt[fd->attr_para->w_idx] =
+			aie_cfg->src_img_fmt;
+		fd->attr_para->rotate_degree[fd->attr_para->w_idx] =
+			aie_cfg->rotate_degree;
+	}
+
+	return 0;
+}
+
+static int aie_config_y2r(struct mtk_aie_dev *fd, struct aie_enq_info *aie_cfg,
+			  int mode)
+{
+	u32 img_addr = 0;
+	u32 img_addr_UV = 0;
+	u32 img_off = 0;
+	u32 img_off_uv = 0;
+	u32 *yuv2rgb_cfg = NULL;
+	u32 srcbuf, srcbuf_UV = 0;
+	u16 xmag_0 = 0, ymag_0 = 0;
+	u16 pym0_out_w = 0;
+	u16 pym0_out_h = 0;
+	u16 stride_pym0_out_w = 0;
+	u16 sr_crp_w = 0;
+	u16 sr_crp_h = 0;
+	u16 y1_stride = 0;
+
+	if (!aie_cfg->en_roi) {
+		img_off = 0;
+		img_off_uv = 0;
+	} else {
+		if (aie_cfg->src_img_fmt == FMT_MONO ||
+		    aie_cfg->src_img_fmt == FMT_YUV_2P ||
+		    aie_cfg->src_img_fmt == FMT_YVU_2P) {
+			y1_stride = aie_cfg->src_img_stride * aie_cfg->src_roi.y1;
+			img_off = y1_stride + aie_cfg->src_roi.x1;
+			img_off_uv = y1_stride + aie_cfg->src_roi.x1;
+		} else if (aie_cfg->src_img_fmt == FMT_YUV420_2P ||
+			   aie_cfg->src_img_fmt == FMT_YUV420_1P) {
+			y1_stride = aie_cfg->src_img_stride * aie_cfg->src_roi.y1;
+			img_off = y1_stride + aie_cfg->src_roi.x1;
+			img_off_uv = y1_stride / 2 + aie_cfg->src_roi.x1;
+		} else if (aie_cfg->src_img_fmt == FMT_YUYV ||
+			   aie_cfg->src_img_fmt == FMT_YVYU ||
+			   aie_cfg->src_img_fmt == FMT_UYVY ||
+			   aie_cfg->src_img_fmt == FMT_VYUY) {
+			y1_stride = aie_cfg->src_img_stride * aie_cfg->src_roi.y1;
+			img_off = y1_stride + aie_cfg->src_roi.x1 * 2;
+			img_off_uv = y1_stride + aie_cfg->src_roi.x1 * 2;
+		} else {
+			dev_err(fd->dev,
+				"AIE error: Unsupport input format %d",
+				aie_cfg->src_img_fmt
+				);
+			return -EINVAL;
+		}
+	}
+
+	img_addr = aie_cfg->src_img_addr + img_off;
+	img_addr_UV = aie_cfg->src_img_addr_uv + img_off_uv;
+
+	srcbuf = img_addr;
+	if (aie_cfg->src_img_fmt == FMT_YUV420_2P ||
+	    aie_cfg->src_img_fmt == FMT_YUV420_1P ||
+	    aie_cfg->src_img_fmt == FMT_YUV_2P ||
+	    aie_cfg->src_img_fmt == FMT_YVU_2P)
+		srcbuf_UV = img_addr_UV;
+	else
+		srcbuf_UV = 0;
+
+	if (mode == FDMODE) {
+		sr_crp_w = fd->base_para->crop_width;
+		sr_crp_h = fd->base_para->crop_height;
+		yuv2rgb_cfg = (u32 *)fd->base_para->fd_yuv2rgb_cfg_va;
+		pym0_out_w = fd->base_para->pyramid_width;
+	} else if (mode == ATTRIBUTEMODE) {
+		sr_crp_w = fd->attr_para->crop_width[fd->attr_para->w_idx];
+		sr_crp_h = fd->attr_para->crop_height[fd->attr_para->w_idx];
+		yuv2rgb_cfg =
+			(u32 *)fd->base_para
+				->attr_yuv2rgb_cfg_va[fd->attr_para->w_idx];
+		pym0_out_w = ATTR_MODE_PYRAMID_WIDTH;
+	}
+
+	pym0_out_h = pym0_out_w * sr_crp_h / sr_crp_w;
+
+	if (pym0_out_w != 0) {
+		xmag_0 = 512 * sr_crp_w / pym0_out_w;
+		ymag_0 = xmag_0;
+	} else {
+		xmag_0 = 0;
+		ymag_0 = 0;
+	}
+
+	yuv2rgb_cfg[Y2R_SRC_DST_FORMAT] =
+		(yuv2rgb_cfg[Y2R_SRC_DST_FORMAT] & 0xFFFFFFF8) |
+		((aie_cfg->src_img_fmt) & 0x7);
+	if (aie_cfg->src_img_fmt == FMT_YUV420_2P ||
+	    aie_cfg->src_img_fmt == FMT_YUV420_1P) { /* for match patten */
+		yuv2rgb_cfg[Y2R_SRC_DST_FORMAT] =
+			(yuv2rgb_cfg[Y2R_SRC_DST_FORMAT] & 0xFFFFFFF8) |
+			((0x3) & 0x7);
+	}
+	yuv2rgb_cfg[Y2R_IN_W_H] = (yuv2rgb_cfg[Y2R_IN_W_H] & 0xF800F800) |
+				  ((sr_crp_w << 16) & 0x7FF0000) |
+				  (sr_crp_h & 0x7FF);
+	yuv2rgb_cfg[Y2R_OUT_W_H] = (yuv2rgb_cfg[Y2R_OUT_W_H] & 0xF800F800) |
+				   ((pym0_out_w << 16) & 0x7FF0000) |
+				   (pym0_out_h & 0x7FF);
+
+	if (aie_cfg->src_img_fmt == FMT_YUV_2P ||
+	    aie_cfg->src_img_fmt == FMT_YVU_2P) { /* 2 plane */
+		yuv2rgb_cfg[Y2R_RA0_RA1_EN] =
+			(yuv2rgb_cfg[Y2R_RA0_RA1_EN] & 0xFFFFFFEE) | 0x11;
+		if (aie_cfg->en_roi) {
+			yuv2rgb_cfg[Y2R_IN_X_Y_SIZE0] = aie_cmb_u16(dif_x(aie_cfg), dif_y(aie_cfg));
+			yuv2rgb_cfg[Y2R_IN_X_Y_SIZE1] = aie_cmb_u16(dif_x(aie_cfg), dif_y(aie_cfg));
+		} else {
+			yuv2rgb_cfg[Y2R_IN_X_Y_SIZE0] =
+				aie_cmb_u16(sr_crp_w - 1, sr_crp_h - 1);
+			yuv2rgb_cfg[Y2R_IN_X_Y_SIZE1] =
+				aie_cmb_u16(sr_crp_w - 1, sr_crp_h - 1);
+		}
+		yuv2rgb_cfg[Y2R_IN_STRIDE0_BUS_SIZE0] =
+			(yuv2rgb_cfg[Y2R_IN_STRIDE0_BUS_SIZE0] & 0xFFF0) |
+			((aie_cfg->src_img_stride << 16) & 0xFFFF0000) | 0x1;
+		yuv2rgb_cfg[Y2R_IN_STRIDE1_BUS_SIZE1] =
+			(yuv2rgb_cfg[Y2R_IN_STRIDE1_BUS_SIZE1] & 0xFFF0) |
+			((aie_cfg->src_img_stride << 16) & 0xFFFF0000) | 0x1;
+	} else if (aie_cfg->src_img_fmt == FMT_MONO) {
+		yuv2rgb_cfg[Y2R_RA0_RA1_EN] =
+			(yuv2rgb_cfg[Y2R_RA0_RA1_EN] & 0xFFFFFFEE) | 0x01;
+		if (aie_cfg->en_roi) {
+			yuv2rgb_cfg[Y2R_IN_X_Y_SIZE0] = aie_cmb_u16(dif_x(aie_cfg), dif_y(aie_cfg));
+			yuv2rgb_cfg[Y2R_IN_X_Y_SIZE1] = aie_cmb_u16(dif_x(aie_cfg), dif_y(aie_cfg));
+		} else {
+			yuv2rgb_cfg[Y2R_IN_X_Y_SIZE0] =
+				aie_cmb_u16(sr_crp_w - 1, sr_crp_h - 1);
+			yuv2rgb_cfg[Y2R_IN_X_Y_SIZE1] =
+				aie_cmb_u16(sr_crp_w - 1, sr_crp_h - 1);
+		}
+		yuv2rgb_cfg[Y2R_IN_STRIDE0_BUS_SIZE0] =
+			(yuv2rgb_cfg[Y2R_IN_STRIDE0_BUS_SIZE0] & 0xFFF0) |
+			((aie_cfg->src_img_stride << 16) & 0xFFFF0000) | 0x0;
+		yuv2rgb_cfg[Y2R_IN_STRIDE1_BUS_SIZE1] =
+			(yuv2rgb_cfg[Y2R_IN_STRIDE1_BUS_SIZE1] & 0xFFF0) |
+			((aie_cfg->src_img_stride << 16) & 0xFFFF0000) | 0x0;
+	} else if (aie_cfg->src_img_fmt == FMT_YUYV ||
+		   aie_cfg->src_img_fmt == FMT_YVYU ||
+		   aie_cfg->src_img_fmt == FMT_UYVY ||
+		   aie_cfg->src_img_fmt == FMT_VYUY) { /* 1 plane */
+		yuv2rgb_cfg[Y2R_RA0_RA1_EN] =
+			(yuv2rgb_cfg[Y2R_RA0_RA1_EN] & 0xFFFFFFEE) | 0x1;
+		if (aie_cfg->en_roi) {
+			yuv2rgb_cfg[Y2R_IN_X_Y_SIZE0] = aie_cmb_u16(2 * (dif_x(aie_cfg) + 1) - 1,
+								    dif_y(aie_cfg)
+							);
+			yuv2rgb_cfg[Y2R_IN_X_Y_SIZE1] = aie_cmb_u16(2 * (dif_x(aie_cfg) + 1) - 1,
+								    dif_y(aie_cfg)
+							);
+		} else {
+			yuv2rgb_cfg[Y2R_IN_X_Y_SIZE0] = aie_cmb_u16(2 * sr_crp_w - 1, sr_crp_h - 1);
+			yuv2rgb_cfg[Y2R_IN_X_Y_SIZE1] = aie_cmb_u16(2 * sr_crp_w - 1, sr_crp_h - 1);
+		}
+		yuv2rgb_cfg[Y2R_IN_STRIDE0_BUS_SIZE0] =
+			(yuv2rgb_cfg[Y2R_IN_STRIDE0_BUS_SIZE0] & 0xFFF0) |
+			((aie_cfg->src_img_stride << 16) & 0xFFFF0000) | 0x3;
+		yuv2rgb_cfg[Y2R_IN_STRIDE1_BUS_SIZE1] =
+			(yuv2rgb_cfg[Y2R_IN_STRIDE1_BUS_SIZE1] & 0xFFF0) |
+			((aie_cfg->src_img_stride << 16) & 0xFFFF0000) | 0x3;
+	}
+
+	/* AIE3.0 */
+	if (aie_cfg->src_img_fmt == FMT_YUV420_2P ||
+	    aie_cfg->src_img_fmt == FMT_YUV420_1P) {
+		yuv2rgb_cfg[Y2R_RA0_RA1_EN] =
+			(yuv2rgb_cfg[Y2R_RA0_RA1_EN] & 0xFFFFFFEE) | 0x11;
+		if (aie_cfg->en_roi) {
+			yuv2rgb_cfg[Y2R_IN_X_Y_SIZE0] = aie_cmb_u16(dif_x(aie_cfg), dif_y(aie_cfg));
+			yuv2rgb_cfg[Y2R_IN_X_Y_SIZE1] = aie_cmb_u16(dif_x(aie_cfg),
+								    dif_y(aie_cfg) / 2
+							);
+		} else {
+			yuv2rgb_cfg[Y2R_IN_X_Y_SIZE0] =
+				aie_cmb_u16(sr_crp_w - 1, sr_crp_h - 1);
+			yuv2rgb_cfg[Y2R_IN_X_Y_SIZE1] = aie_cmb_u16(sr_crp_w - 1,
+								    sr_crp_h / 2 - 1
+							);
+		}
+		yuv2rgb_cfg[Y2R_IN_STRIDE0_BUS_SIZE0] =
+			(yuv2rgb_cfg[Y2R_IN_STRIDE0_BUS_SIZE0] & 0xFFF0) |
+			((aie_cfg->src_img_stride << 16) & 0xFFFF0000) | 0x0;
+		yuv2rgb_cfg[Y2R_IN_STRIDE1_BUS_SIZE1] =
+			(yuv2rgb_cfg[Y2R_IN_STRIDE1_BUS_SIZE1] & 0xFFF0) |
+			((aie_cfg->src_img_stride << 16) & 0xFFFF0000) | 0x0;
+
+		yuv2rgb_cfg[Y2R_CO2_FMT_MODE_EN] =
+			(yuv2rgb_cfg[Y2R_CO2_FMT_MODE_EN] & 0xFFFFFFFE) | 0x01;
+		if (aie_cfg->en_roi) {
+			yuv2rgb_cfg[Y2R_CO2_CROP_X] = aie_cmb_u16(0, dif_x(aie_cfg));
+			yuv2rgb_cfg[Y2R_CO2_CROP_Y] = aie_cmb_u16(0, dif_y(aie_cfg));
+		} else {
+			yuv2rgb_cfg[Y2R_CO2_CROP_X] =
+				aie_cmb_u16(0, sr_crp_w - 1);
+			yuv2rgb_cfg[Y2R_CO2_CROP_Y] =
+				aie_cmb_u16(0, sr_crp_h - 1);
+		}
+	} else {
+		yuv2rgb_cfg[Y2R_CO2_FMT_MODE_EN] =
+			(yuv2rgb_cfg[Y2R_CO2_FMT_MODE_EN] & 0xFFFFFFFE);
+
+		if (aie_cfg->en_roi) {
+			yuv2rgb_cfg[Y2R_CO2_CROP_X] = aie_cmb_u16(0, dif_x(aie_cfg));
+			yuv2rgb_cfg[Y2R_CO2_CROP_Y] = aie_cmb_u16(0, dif_y(aie_cfg));
+		} else {
+			yuv2rgb_cfg[Y2R_CO2_CROP_X] =
+				aie_cmb_u16(0, sr_crp_w - 1);
+			yuv2rgb_cfg[Y2R_CO2_CROP_Y] =
+				aie_cmb_u16(0, sr_crp_h - 1);
+		}
+	}
+
+	stride_pym0_out_w = round_up(pym0_out_w, 8);
+
+	yuv2rgb_cfg[Y2R_OUT_X_Y_SIZE0] =
+		aie_cmb_u16(pym0_out_w - 1, pym0_out_h - 1);
+	set_cmb_cfg(yuv2rgb_cfg, Y2R_OUT_STRIDE0_BUS_SIZE0, stride_pym0_out_w);
+	yuv2rgb_cfg[Y2R_OUT_X_Y_SIZE1] =
+		aie_cmb_u16(pym0_out_w - 1, pym0_out_h - 1);
+	set_cmb_cfg(yuv2rgb_cfg, Y2R_OUT_STRIDE1_BUS_SIZE1, stride_pym0_out_w);
+	yuv2rgb_cfg[Y2R_OUT_X_Y_SIZE2] =
+		aie_cmb_u16(pym0_out_w - 1, pym0_out_h - 1);
+	set_cmb_cfg(yuv2rgb_cfg, Y2R_OUT_STRIDE2_BUS_SIZE2, stride_pym0_out_w);
+
+	if (aie_cfg->en_padding) {
+		yuv2rgb_cfg[Y2R_PADDING_EN_UP_DOWN] =
+			1 | ((aie_cfg->src_padding.up << 4) & 0x1FF0) |
+			((aie_cfg->src_padding.down << 16) & 0x01FF0000);
+		yuv2rgb_cfg[Y2R_PADDING_RIGHT_LEFT] =
+			(aie_cfg->src_padding.right & 0x01FF) |
+			((aie_cfg->src_padding.left << 16) & 0x01FF0000);
+	} else {
+		yuv2rgb_cfg[Y2R_PADDING_EN_UP_DOWN] = 0;
+		yuv2rgb_cfg[Y2R_PADDING_RIGHT_LEFT] = 0;
+	}
+
+	yuv2rgb_cfg[Y2R_IN_0] = srcbuf;
+	yuv2rgb_cfg[Y2R_IN_1] = srcbuf_UV;
+
+	yuv2rgb_cfg[Y2R_OUT_0] = (u32)fd->base_para->rs_pym_rst_pa[0][0];
+	yuv2rgb_cfg[Y2R_OUT_1] = (u32)fd->base_para->rs_pym_rst_pa[0][1];
+	yuv2rgb_cfg[Y2R_OUT_2] = (u32)fd->base_para->rs_pym_rst_pa[0][2];
+
+	yuv2rgb_cfg[Y2R_X_Y_MAG] = (xmag_0 & 0x3FFF) |
+				   ((ymag_0 << 16) & 0x3FFF0000);
+
+	if (sr_crp_w >= pym0_out_w) { /* down scale AIE1.0 by FRZ */
+		yuv2rgb_cfg[Y2R_RS_SEL_SRZ_EN] =
+			(yuv2rgb_cfg[Y2R_RS_SEL_SRZ_EN] & 0x00100070);
+		yuv2rgb_cfg[Y2R_SRZ_HORI_STEP] = 0;
+		yuv2rgb_cfg[Y2R_SRZ_VERT_STEP] = 0;
+	} else { /* SRZ */
+		/* 0: FDRZ for down scaling */
+		/* 1: SRZ for up scaling */
+		yuv2rgb_cfg[Y2R_RS_SEL_SRZ_EN] =
+			(yuv2rgb_cfg[Y2R_RS_SEL_SRZ_EN] & 0x00100070) | SRZ_BIT;
+		yuv2rgb_cfg[Y2R_SRZ_HORI_STEP] =
+			((sr_crp_w - 1) << 15) / (pym0_out_w - 1);
+		yuv2rgb_cfg[Y2R_SRZ_VERT_STEP] =
+			((sr_crp_h - 1) << 15) / (pym0_out_h - 1);
+	}
+
+	if (fd->variant->hw_version == 31) {
+		yuv2rgb_cfg[Y2R_CON_IN_BA_MSB] = (u32)0x02020202;
+		yuv2rgb_cfg[Y2R_CON_OUT_BA_MSB] = (u32)0x02020202;
+	}
+
+	return 0;
+}
+
+static int aie_config_rs(struct mtk_aie_dev *fd, struct aie_enq_info *aie_cfg)
+{
+	u32 *rs_cfg = NULL;
+	u32 *rs_tbl[2] = { NULL, NULL };
+	u16 xmag_0 = 0, ymag_0 = 0;
+	u16 pym_out_w[3] = { 0, 0, 0 };
+	u16 pym_out_h[3] = { 0, 0, 0 };
+	u16 round_w = 0;
+	u16 sr_crp_w = 0;
+	u16 sr_crp_h = 0;
+	int i = 0;
+
+	if (aie_cfg->sel_mode == FDMODE) {
+		sr_crp_w = fd->base_para->crop_width;
+		sr_crp_h = fd->base_para->crop_height;
+	} else if (aie_cfg->sel_mode == ATTRIBUTEMODE) {
+		sr_crp_w = fd->attr_para->crop_width[fd->attr_para->w_idx];
+		sr_crp_h = fd->attr_para->crop_height[fd->attr_para->w_idx];
+	}
+
+	rs_cfg = (u32 *)fd->base_para->fd_rs_cfg_va;
+
+	pym_out_w[0] = fd->base_para->pyramid_width;
+	pym_out_w[1] = pym_out_w[0] >> 1;
+	pym_out_w[2] = pym_out_w[1] >> 1;
+
+	pym_out_h[0] = pym_out_w[0] * sr_crp_h / sr_crp_w;
+	pym_out_h[1] = pym_out_h[0] >> 1;
+	pym_out_h[2] = pym_out_h[1] >> 1;
+
+	for (i = 0; i < 2; i++) {
+		rs_tbl[i] = rs_cfg + fd->variant->rs_cfg_size * i;
+
+		rs_tbl[i][RS_IN_0] = (u32)fd->base_para->rs_pym_rst_pa[i][0];
+		rs_tbl[i][RS_IN_1] = (u32)fd->base_para->rs_pym_rst_pa[i][1];
+		rs_tbl[i][RS_IN_2] = (u32)fd->base_para->rs_pym_rst_pa[i][2];
+
+		rs_tbl[i][RS_OUT_0] =
+			(u32)fd->base_para->rs_pym_rst_pa[i + 1][0];
+		rs_tbl[i][RS_OUT_1] =
+			(u32)fd->base_para->rs_pym_rst_pa[i + 1][1];
+		rs_tbl[i][RS_OUT_2] =
+			(u32)fd->base_para->rs_pym_rst_pa[i + 1][2];
+
+		rs_tbl[i][RS_INPUT_W_H] =
+			(rs_tbl[i][RS_INPUT_W_H] & 0xF800F800) |
+			(pym_out_h[i] & 0x7FF) |
+			((pym_out_w[i] << 16) & 0x7FF0000);
+		rs_tbl[i][RS_OUTPUT_W_H] =
+			(rs_tbl[i][RS_OUTPUT_W_H] & 0xF800F800) |
+			(pym_out_h[i + 1] & 0x7FF) |
+			((pym_out_w[i + 1] << 16) & 0x7FF0000);
+		rs_tbl[i][RS_IN_X_Y_SIZE0] =
+			aie_cmb_u16(pym_out_w[i] - 1, pym_out_h[i] - 1);
+		rs_tbl[i][RS_IN_X_Y_SIZE1] =
+			aie_cmb_u16(pym_out_w[i] - 1, pym_out_h[i] - 1);
+		rs_tbl[i][RS_IN_X_Y_SIZE2] =
+			aie_cmb_u16(pym_out_w[i] - 1, pym_out_h[i] - 1);
+		set_cmb_cfg(rs_tbl[i], RS_IN_STRIDE0, pym_out_w[i]);
+		set_cmb_cfg(rs_tbl[i], RS_IN_STRIDE1, pym_out_w[i]);
+		set_cmb_cfg(rs_tbl[i], RS_IN_STRIDE2, pym_out_w[i]);
+		rs_tbl[i][RS_OUT_X_Y_SIZE0] = aie_cmb_u16(pym_out_w[i + 1] - 1,
+							  pym_out_h[i + 1] - 1
+						);
+		rs_tbl[i][RS_OUT_X_Y_SIZE1] = aie_cmb_u16(pym_out_w[i + 1] - 1,
+							  pym_out_h[i + 1] - 1
+						);
+		rs_tbl[i][RS_OUT_X_Y_SIZE2] = aie_cmb_u16(pym_out_w[i + 1] - 1,
+							  pym_out_h[i + 1] - 1
+						);
+
+		if (i == 0)
+			round_w = pym_out_w[i + 1];
+		else
+			round_w = round_up(pym_out_w[i + 1], 8);
+
+		set_cmb_cfg(rs_tbl[i], RS_OUT_STRIDE0, round_w);
+		set_cmb_cfg(rs_tbl[i], RS_OUT_STRIDE1, round_w);
+		set_cmb_cfg(rs_tbl[i], RS_OUT_STRIDE2, round_w);
+
+		xmag_0 = 512 * pym_out_w[i] / pym_out_w[i + 1];
+		ymag_0 = xmag_0;
+
+		rs_tbl[i][RS_X_Y_MAG] = (xmag_0 & 0x3FFF) |
+					((ymag_0 << 16) & 0x3FFF0000);
+
+		if (fd->variant->hw_version == 31) {
+			rs_tbl[i][RS_CON_IN_BA_MSB] = (u32)0x02020202;
+			rs_tbl[i][RS_CON_OUT_BA_MSB] = (u32)0x02020202;
+		}
+	}
+
+	return 0;
+}
+
+static int aie_config_network(struct mtk_aie_dev *fd,
+			      struct aie_enq_info *aie_cfg)
+{
+	u16 conv_width = 0;
+	u16 conv_height = 0;
+	u8 i = 0;
+	u8 j = 0;
+	u8 uch = 0;
+	u8 uloop = 0;
+	u16 fd_xsize[4] = { 0, 0, 0, 0 };
+	void *fd_cfg = NULL;
+	u32 *fd_cur_cfg = NULL;
+	u32 *fd_cur_set = NULL;
+	u16 pyramid0_out_w = 0;
+	u16 pyramid0_out_h = 0;
+	u16 pyramid1_out_h = 0;
+	u16 pyramid2_out_h = 0;
+	u16 input_height = 0;
+	u16 out_height = 0;
+	u16 out_ysize_plus_1 = 0;
+	u16 out_ysize_plus_1_stride2 = 0;
+	u32 sr_crp_w = 0;
+	u32 sr_crp_h = 0;
+	struct aie_static_info *pstv = &fd->st_info;
+	u32 cal_x = 0;
+	u32 cal_y = 0;
+
+	if (aie_cfg->sel_mode == FDMODE) {
+		sr_crp_w = fd->base_para->crop_width;
+		sr_crp_h = fd->base_para->crop_height;
+	} else if (aie_cfg->sel_mode == ATTRIBUTEMODE) {
+		sr_crp_w = fd->attr_para->crop_width[fd->attr_para->w_idx];
+		sr_crp_h = fd->attr_para->crop_height[fd->attr_para->w_idx];
+	}
+
+	pyramid0_out_w = fd->base_para->pyramid_width;
+	pyramid0_out_h = pyramid0_out_w * sr_crp_h / sr_crp_w;
+
+	pyramid1_out_h = pyramid0_out_h / 2;
+	pyramid2_out_h = pyramid1_out_h / 2;
+
+	fd_cfg = fd->base_para->fd_fd_cfg_va;
+
+	for (i = 0; i < FD_LOOP_NUM; i++) {
+		fd_cur_cfg = (u32 *)fd_cfg + fd->variant->fd_cfg_size * i;
+		fd_cur_cfg[FD_INPUT_ROTATE] =
+			(fd_cur_cfg[FD_INPUT_ROTATE] & 0xFFFF0FFF) |
+			((aie_cfg->rotate_degree << 12) & 0x3000);
+
+		if (i == 0)
+			input_height = pyramid2_out_h;
+		else if (i == (RPN2_LOOP_NUM + 1))
+			input_height = pyramid1_out_h;
+		else if (i == (RPN1_LOOP_NUM + 1))
+			input_height = pyramid0_out_h;
+		else
+			if (fd_out_stride2_in[i] == 0)
+				input_height = out_height;
+			else
+				input_height = (out_height + 1) / 2;
+
+		if (fd_maxpool[i] == 1 && fd_stride[i] == 1)
+			out_height =
+				DIV_ROUND_UP(input_height, 2 * fd_maxpool[i]);
+		else
+			out_height = DIV_ROUND_UP(input_height, fd_stride[i] + 2 * fd_maxpool[i]);
+
+		if (i == RPN0_LOOP_NUM || i == RPN1_LOOP_NUM ||
+		    i == RPN2_LOOP_NUM) {
+			conv_width = fd->base_para->img_width;
+			conv_height = fd->base_para->img_height;
+			fd_xsize[0] = pstv->inf_elm[i].img_width * 2 * 16 *
+					      anchor_en_num[i] -
+				      1;
+			fd_xsize[3] = pstv->inf_elm[i].img_width * 2 * 32 *
+					      anchor_en_num[i] - 1;
+			fd_xsize[2] = fd_xsize[3];
+			fd_xsize[1] = fd_xsize[2];
+		} else {
+			conv_width = DIV_ROUND_UP(pstv->inf_elm[i].img_width, fd_stride[i]);
+			conv_height = DIV_ROUND_UP(input_height, fd_stride[i]);
+
+			fd_xsize[3] = pstv->inf_elm[i].input_xsize_plus_1 - 1;
+			fd_xsize[2] = fd_xsize[3];
+			fd_xsize[1] = fd_xsize[2];
+			fd_xsize[0] = fd_xsize[1];
+		}
+
+		fd_cur_cfg[FD_CONV_WIDTH_MOD6] =
+			(fd_cur_cfg[FD_CONV_WIDTH_MOD6] & 0xFF8FFFFF) |
+			(((conv_width % 6) << 20) & 0x00700000);
+		fd_cur_cfg[FD_CONV_IMG_W_H] =
+			aie_cmb_u16(conv_height, conv_width);
+
+		fd_cur_cfg[FD_IN_IMG_W_H] = aie_cmb_u16(input_height, pstv->inf_elm[i].img_width);
+		fd_cur_cfg[FD_OUT_IMG_W_H] = aie_cmb_u16(out_height, pstv->inf_elm[i].out_width);
+
+		if (fd_rdma_en[i][0][0] != -1) {
+			for (j = 0; j < 4; j++) {
+				fd_cur_cfg[FD_IN_X_Y_SIZE0 + 2 * j] =
+					aie_cmb_u16(fd_xsize[j], input_height - 1);
+				set_cmbst_cfg(fd_cur_cfg,
+					      FD_IN_STRIDE0_BUS_SIZE0 + 2 * j,
+					      fd_xsize[j] + 1
+				);
+			}
+		}
+
+		out_ysize_plus_1 = out_height - 1;
+		out_ysize_plus_1_stride2 = (out_height + 1) / 2 - 1;
+
+		for (j = 0; j < OUTPUT_WDMA_WRA_NUM; j++) {
+			fd_cur_set = fd_cur_cfg + 2 * j;
+			if (!fd_wdma_en[i][j])
+				continue;
+
+			if (out_stride_size[i][j] == 1) {
+				fd_cur_set[FD_OUT_X_Y_SIZE0] =
+					aie_cmb_u16(pstv->inf_elm[i].out_xsize_plus_1 - 1,
+						    out_ysize_plus_1
+					);
+				set_cmbst_cfg(fd_cur_set,
+					      FD_OUT_STRIDE0_BUS_SIZE0,
+					      pstv->inf_elm[i].out_stride
+				);
+			} else if (out_stride_size[i][j] == 2) {
+				fd_cur_set[FD_OUT_X_Y_SIZE0] =
+					aie_cmb_u16(pstv->inf_elm[i].out_xsize_plus_1_stride2 - 1,
+						    out_ysize_plus_1_stride2
+					);
+				set_cmbst_cfg(fd_cur_set,
+					      FD_OUT_STRIDE0_BUS_SIZE0,
+					      pstv->inf_elm[i].out_stride_stride2
+				);
+			}
+		}
+
+		if (i == RPN0_LOOP_NUM || i == RPN1_LOOP_NUM || i == RPN2_LOOP_NUM)
+			set_cmb_cfg(fd_cur_cfg, FD_RPN_SET, fd->base_para->rpn_anchor_thrd);
+
+		if (i == RPN0_LOOP_NUM) {
+			cal_x = ((sr_crp_w << 10) * 100 /
+				 (int)fd->base_para->pyramid_width) >>
+				10;
+			cal_y = cal_x * 512 / 100;
+			fd_cur_cfg[FD_IMAGE_COORD] =
+				(fd_cur_cfg[FD_IMAGE_COORD] & 0xF) |
+				((cal_y << 4) & 0x7FFF0);
+			fd_cur_cfg[FD_IMAGE_COORD_XY_OFST] = 0;
+			if (aie_cfg->en_roi) {
+				fd_cur_cfg[FD_IMAGE_COORD_XY_OFST] =
+					(aie_cfg->src_roi.x1 -
+					 aie_cfg->src_padding.left) |
+					(aie_cfg->src_roi.y1 -
+					 aie_cfg->src_padding.up)
+						<< 16;
+			}
+		} else if (i == RPN1_LOOP_NUM) {
+			cal_x = ((sr_crp_w << 10) * 100 /
+				 (int)fd->base_para->pyramid_width) >>
+				10;
+			cal_y = cal_x * 2 * 512 / 100;
+			fd_cur_cfg[FD_IMAGE_COORD] =
+				(fd_cur_cfg[FD_IMAGE_COORD] & 0xF) |
+				((cal_y << 4) & 0x7FFF0);
+			fd_cur_cfg[FD_IMAGE_COORD_XY_OFST] = 0;
+			if (aie_cfg->en_roi) {
+				fd_cur_cfg[FD_IMAGE_COORD_XY_OFST] =
+					(aie_cfg->src_roi.x1 -
+					 aie_cfg->src_padding.left) |
+					(aie_cfg->src_roi.y1 -
+					 aie_cfg->src_padding.up)
+						<< 16;
+			}
+		} else if (i == RPN2_LOOP_NUM) {
+			cal_x = ((sr_crp_w << 10) * 100 /
+				 (int)fd->base_para->pyramid_width) >>
+				10;
+			cal_y = cal_x * 4 * 512 / 100;
+			fd_cur_cfg[FD_IMAGE_COORD] =
+				(fd_cur_cfg[FD_IMAGE_COORD] & 0xF) |
+				((cal_y << 4) & 0x7FFF0);
+			fd_cur_cfg[FD_IMAGE_COORD_XY_OFST] = 0;
+			if (aie_cfg->en_roi) {
+				fd_cur_cfg[FD_IMAGE_COORD_XY_OFST] =
+					(aie_cfg->src_roi.x1 -
+					 aie_cfg->src_padding.left) |
+					(aie_cfg->src_roi.y1 -
+					 aie_cfg->src_padding.up)
+						<< 16;
+			}
+		}
+
+		/* IN_FM_BASE_ADR */
+		if (i == 0) {
+			fd_cur_cfg[FD_IN_0] =
+				(u32)(fd->base_para->rs_pym_rst_pa[2][0]);
+			fd_cur_cfg[FD_IN_1] =
+				(u32)(fd->base_para->rs_pym_rst_pa[2][1]);
+			fd_cur_cfg[FD_IN_2] =
+				(u32)(fd->base_para->rs_pym_rst_pa[2][2]);
+		} else if (i == (RPN2_LOOP_NUM + 1)) {
+			fd_cur_cfg[FD_IN_0] =
+				(u32)(fd->base_para->rs_pym_rst_pa[1][0]);
+			fd_cur_cfg[FD_IN_1] =
+				(u32)(fd->base_para->rs_pym_rst_pa[1][1]);
+			fd_cur_cfg[FD_IN_2] =
+				(u32)(fd->base_para->rs_pym_rst_pa[1][2]);
+		} else if (i == (RPN1_LOOP_NUM + 1)) {
+			fd_cur_cfg[FD_IN_0] =
+				(u32)(fd->base_para->rs_pym_rst_pa[0][0]);
+			fd_cur_cfg[FD_IN_1] =
+				(u32)(fd->base_para->rs_pym_rst_pa[0][1]);
+			fd_cur_cfg[FD_IN_2] =
+				(u32)(fd->base_para->rs_pym_rst_pa[0][2]);
+		} else {
+			for (j = 0; j < INPUT_WDMA_WRA_NUM; j++) {
+				if (fd_rdma_en[i][j][0] != -1) {
+					uloop = fd_rdma_en[i][j][0];
+					uch = fd_rdma_en[i][j][1];
+					fd_cur_cfg[FD_IN_0 + j] =
+						(u32)(fd->dma_para->fd_out_hw_pa
+							      [uloop][uch]);
+				}
+			}
+		}
+
+		/* OUT_FM_BASE_ADR */
+		for (j = 0; j < OUTPUT_WDMA_WRA_NUM; j++) {
+			if (fd_wdma_en[i][j])
+				fd_cur_cfg[FD_OUT_0 + j] =
+					(u32)(fd->dma_para->fd_out_hw_pa[i][j]);
+		}
+
+		/* KERNEL_BASE_ADR */
+		for (j = 0; j < KERNEL_RDMA_RA_NUM; j++) {
+			if (fd_ker_rdma_size[i][j])
+				fd_cur_cfg[FD_KERNEL_0 + j] =
+					(u32)(fd->dma_para->fd_kernel_pa[i][j]);
+		}
+
+		if (fd->variant->hw_version == 31) {
+			fd_cur_cfg[FD_CON_IN_BA_MSB] = (u32)0x02020202;
+			fd_cur_cfg[FD_CON_OUT_BA_MSB] = (u32)0x02020202;
+			fd_cur_cfg[FD_CON_KERNEL_BA_MSB] = (u32)0x00000202;
+		}
+	}
+
+	return 0;
+}
+
+static int aie_config_attr_network(struct mtk_aie_dev *fd,
+				   struct aie_enq_info *aie_cfg)
+{
+	bool is_regression_loop = false;
+	void *fd_cfg = NULL;
+	u32 *fd_cur_cfg = NULL;
+	u16 fd_input_ht = 0, fd_output_ht = 0;
+	u16 fd_out_y[4] = { 0, 0, 0, 0 };
+	u8 i = 0, j = 0;
+	u8 uloop = 0, uch = 0, uidx = 0;
+	u16 pyramid0_out_w = 0, pyramid0_out_h = 0;
+	int fd_conv_ht = 0;
+	u16 sr_crp_w = 0;
+	u16 sr_crp_h = 0;
+
+	sr_crp_w = fd->attr_para->crop_width[fd->attr_para->w_idx];
+	sr_crp_h = fd->attr_para->crop_height[fd->attr_para->w_idx];
+
+	pyramid0_out_w = ATTR_MODE_PYRAMID_WIDTH;
+	pyramid0_out_h = pyramid0_out_w * sr_crp_h / sr_crp_w;
+
+	fd_cfg = fd->base_para->attr_fd_cfg_va[fd->attr_para->w_idx];
+
+	for (i = 0; i < ATTR_LOOP_NUM; i++) {
+		fd_cur_cfg = (u32 *)fd_cfg + fd->variant->fd_cfg_size * i;
+		fd_cur_cfg[FD_INPUT_ROTATE] =
+			(fd_cur_cfg[FD_INPUT_ROTATE] & 0xFFFF0FFF) |
+			((aie_cfg->rotate_degree << 12) & 0x3000);
+		if (i == 0)
+			fd_input_ht = pyramid0_out_h;
+		else
+			if (attr_out_stride2_as_in[i] == 0)
+				fd_input_ht = fd_output_ht;
+			else if (attr_out_stride2_as_in[i] == 1)
+				fd_input_ht = (fd_output_ht + 1) / 2;
+
+		fd_output_ht = DIV_ROUND_UP(fd_input_ht,
+					    attr_fd_stride[i] +
+					    2 * attr_fd_maxpool[i]
+				);
+		fd_conv_ht = DIV_ROUND_UP(fd_input_ht, attr_fd_stride[i]);
+
+		fd_cur_cfg[FD_CONV_IMG_W_H] =
+			(fd_cur_cfg[FD_CONV_IMG_W_H] & 0xFFFF0000) |
+			(fd_conv_ht & 0xFFFF);
+		fd_cur_cfg[FD_IN_IMG_W_H] =
+			(fd_cur_cfg[FD_IN_IMG_W_H] & 0xFFFF0000) |
+			(fd_input_ht & 0xFFFF);
+		fd_cur_cfg[FD_OUT_IMG_W_H] =
+			(fd_cur_cfg[FD_OUT_IMG_W_H] & 0xFFFF0000) |
+			(fd_output_ht & 0xFFFF);
+		set_cmb_cfg(fd_cur_cfg, FD_IN_X_Y_SIZE0, fd_input_ht - 1);
+		set_cmb_cfg(fd_cur_cfg, FD_IN_X_Y_SIZE1, fd_input_ht - 1);
+		set_cmb_cfg(fd_cur_cfg, FD_IN_X_Y_SIZE2, fd_input_ht - 1);
+		set_cmb_cfg(fd_cur_cfg, FD_IN_X_Y_SIZE3, fd_input_ht - 1);
+
+		is_regression_loop = (i == AGE_OUT_RGS || i == GENDER_OUT_RGS ||
+				    i == INDIAN_OUT_RGS || i == RACE_OUT_RGS);
+
+		if (is_regression_loop) {
+			fd_out_y[0] = 0;
+			fd_out_y[1] = 0;
+			fd_out_y[2] = 0;
+			fd_out_y[3] = 0;
+		} else {
+			fd_out_y[0] = fd_output_ht - 1;
+			fd_out_y[1] = fd_output_ht - 1;
+			if (attr_out_2size[i] == 0) {
+				fd_out_y[2] = fd_output_ht - 1;
+				fd_out_y[3] = fd_output_ht - 1;
+			} else {
+				fd_out_y[2] = (fd_output_ht + 1) / 2 - 1;
+				fd_out_y[3] = (fd_output_ht + 1) / 2 - 1;
+			}
+		}
+
+		for (j = 0; j < 4; j++)
+			set_cmb_cfg(fd_cur_cfg, FD_OUT_X_Y_SIZE0 + 2 * j, fd_out_y[j]);
+
+		/* IN_FM_BASE_ADR */
+		if (i == 0) {
+			fd_cur_cfg[FD_IN_0] =
+				(u32)(fd->base_para->rs_pym_rst_pa[0][0]);
+			fd_cur_cfg[FD_IN_1] =
+				(u32)(fd->base_para->rs_pym_rst_pa[0][1]);
+			fd_cur_cfg[FD_IN_2] =
+				(u32)(fd->base_para->rs_pym_rst_pa[0][2]);
+		} else {
+			for (j = 0; j < INPUT_WDMA_WRA_NUM; j++) {
+				if (attr_rdma_en[i][j][0] != -1) {
+					uloop = attr_rdma_en[i][j][0];
+					uch = attr_rdma_en[i][j][1];
+					fd_cur_cfg[FD_IN_0 + j] =
+						(u32)(fd->dma_para->attr_out_hw_pa
+							      [uloop][uch]);
+				}
+			}
+		}
+
+		/* OUT_FM_BASE_ADR */
+		for (j = 0; j < OUTPUT_WDMA_WRA_NUM; j++) {
+			if (attr_wdma_en[i][j]) {
+				uidx = fd->attr_para->w_idx;
+				if (i == AGE_OUT_RGS && j == 0)
+					fd_cur_cfg[FD_OUT_0 + j] =
+						(u32)(fd->dma_para->age_out_hw_pa
+							      [uidx]);
+				else if (i == GENDER_OUT_RGS && j == 0)
+					fd_cur_cfg[FD_OUT_0 + j] =
+						(u32)(fd->dma_para
+							      ->gender_out_hw_pa
+								      [uidx]);
+				else if (i == INDIAN_OUT_RGS && j == 0)
+					fd_cur_cfg[FD_OUT_0 + j] =
+						(u32)(fd->dma_para
+							      ->is_indian_out_hw_pa
+								      [uidx]);
+				else if (i == RACE_OUT_RGS && j == 0)
+					fd_cur_cfg[FD_OUT_0 + j] =
+						(u32)(fd->dma_para
+							      ->race_out_hw_pa
+								      [uidx]);
+				else
+					fd_cur_cfg[FD_OUT_0 + j] =
+						(u32)(fd->dma_para
+							      ->attr_out_hw_pa
+								      [i][j]);
+			}
+		}
+
+		/* KERNEL_BASE_ADR */
+		for (j = 0; j < KERNEL_RDMA_RA_NUM; j++) {
+			fd_cur_cfg[FD_KERNEL_0 + j] =
+				(u32)(fd->dma_para->attr_kernel_pa[i][j]);
+		}
+
+		if (fd->variant->hw_version == 31) {
+			fd_cur_cfg[FD_CON_IN_BA_MSB] = (u32)0x02020202;
+			fd_cur_cfg[FD_CON_OUT_BA_MSB] = (u32)0x02020202;
+			fd_cur_cfg[FD_CON_KERNEL_BA_MSB] = (u32)0x00000202;
+		}
+	}
+	return 0;
+}
+
+static int aie_config_dram(struct mtk_aie_dev *fd, struct aie_enq_info *aie_cfg)
+{
+	int ret = -EINVAL;
+
+	if (aie_cfg->sel_mode == FDMODE) {
+		ret = aie_config_y2r(fd, aie_cfg, aie_cfg->sel_mode);
+		if (ret)
+			return ret;
+
+		ret = aie_config_rs(fd, aie_cfg);
+		if (ret)
+			return ret;
+
+		ret = aie_config_network(fd, aie_cfg);
+		if (ret)
+			return ret;
+
+	} else if (aie_cfg->sel_mode == ATTRIBUTEMODE) {
+		ret = aie_config_y2r(fd, aie_cfg, aie_cfg->sel_mode);
+		if (ret)
+			return ret;
+
+		ret = aie_config_attr_network(fd, aie_cfg);
+		if (ret)
+			return ret;
+	}
+
+	return ret;
+}
+
+void aie_reset(struct mtk_aie_dev *fd)
+{
+	writel(0x30000, fd->fd_base + AIE_START_REG);
+	writel(0x0, fd->fd_base + AIE_START_REG);
+}
+
+int aie_init(struct mtk_aie_dev *fd, struct user_init *user_init)
+{
+	int ret = -ENOMEM;
+	int i = 0, j = 0;
+
+	if (fd->fd_state & STATE_INIT) {
+		dev_err(fd->dev, "%s fd state: %d\n", __func__, fd->fd_state);
+		return -EINVAL;
+	}
+
+	fd->fd_state &= ~STATE_INIT;
+	fd->fd_mem_size = 0;
+
+	fd->base_para = kmalloc(sizeof(*fd->base_para), GFP_KERNEL);
+	if (!fd->base_para)
+		goto kmalloc_fail;
+
+	fd->attr_para = kmalloc(sizeof(*fd->attr_para), GFP_KERNEL);
+	if (!fd->attr_para)
+		goto kmalloc_fail;
+
+	fd->dma_para = kmalloc(sizeof(*fd->dma_para), GFP_KERNEL);
+	if (!fd->dma_para)
+		goto kmalloc_fail;
+
+	if (fd->variant->fld_enable) {
+		fd->fld_para =
+			kmalloc(sizeof(*fd->fld_para), GFP_KERNEL);
+		if (!fd->fld_para)
+			goto kmalloc_fail;
+	}
+
+	fd->base_para->rpn_anchor_thrd =
+		(signed short)(user_init->feature_threshold & 0x0000FFFF);
+	fd->base_para->pyramid_width = user_init->pyramid_width;
+	fd->base_para->pyramid_height = user_init->pyramid_height;
+	fd->base_para->max_pyramid_width = user_init->pyramid_width;
+	fd->base_para->max_pyramid_height = user_init->pyramid_height;
+
+	fd->base_para->fd_fd_cfg_va = NULL;
+	fd->base_para->fd_rs_cfg_va = NULL;
+	fd->base_para->fd_yuv2rgb_cfg_va = NULL;
+	for (i = 0; i < MAX_ENQUE_FRAME_NUM; i++)
+		fd->base_para->attr_fd_cfg_va[i] = NULL;
+	for (i = 0; i < MAX_ENQUE_FRAME_NUM; i++)
+		fd->base_para->attr_yuv2rgb_cfg_va[i] = NULL;
+	for (i = 0; i < PYM_NUM; i++)
+		for (j = 0; j < COLOR_NUM; j++)
+			fd->base_para->rs_pym_rst_va[i][j] = NULL;
+
+	memset(&fd->st_info, 0, sizeof(struct aie_static_info));
+	aie_init_table(fd, fd->base_para->max_pyramid_width,
+		       fd->base_para->max_pyramid_height);
+	aie_update_buf_params(fd, user_init->max_img_width,
+			  user_init->max_img_height);
+	ret = aie_alloc_dram_buf(fd);
+	if (ret)
+		goto free_all;
+
+	ret = aie_alloc_output_buf(fd);
+	if (ret)
+		goto free_all;
+
+	ret = aie_alloc_fddma_buf(fd);
+	if (ret)
+		goto free_all;
+
+	if (fd->variant->fld_enable) {
+		ret = aie_alloc_fld_buf(fd);
+		if (ret)
+			goto free_all;
+	}
+
+	aie_arrange_fddma_buf(fd);
+	aie_arrange_kernel_buf(fd);
+	aie_arrange_attrdma_buf(fd);
+	aie_arrange_result_dma_buf(fd);
+
+	if (fd->variant->fld_enable)
+		aie_arrange_fld_buf(fd);
+
+	ret = aie_load_fw(fd);
+	if (ret) {
+		dev_err(fd->dev, "Failed to load aie fw\n");
+		goto free_all;
+	}
+
+	fd->attr_para->r_idx = 0;
+	fd->attr_para->w_idx = 0;
+
+	fd->fd_state |= STATE_INIT;
+
+	dev_info(fd->dev, "%s: fd_mem_size(%d)\n", __func__, fd->fd_mem_size);
+
+	return ret;
+
+free_all:
+	aie_free_dram_buf(fd);
+	aie_free_output_buf(fd);
+	aie_free_fddma_buf(fd);
+	if (fd->variant->fld_enable)
+		aie_free_fld_buf(fd);
+
+kmalloc_fail:
+	kfree(fd->base_para);
+	kfree(fd->attr_para);
+	kfree(fd->dma_para);
+	kfree(fd->fld_para);
+
+	dev_err(fd->dev, "Failed to init aie\n");
+
+	return ret;
+}
+
+void aie_uninit(struct mtk_aie_dev *fd)
+{
+	fd->fd_state &= ~STATE_INIT;
+
+	aie_free_dram_buf(fd);
+	aie_free_output_buf(fd);
+	aie_free_fddma_buf(fd);
+
+	if (fd->variant->fld_enable)
+		aie_free_fld_buf(fd);
+
+	kfree(fd->base_para);
+	kfree(fd->attr_para);
+	kfree(fd->dma_para);
+	kfree(fd->fld_para);
+}
+
+int aie_prepare(struct mtk_aie_dev *fd, struct aie_enq_info *aie_cfg)
+{
+	int ret = -EINVAL;
+
+	if (fd->variant->fld_enable) {
+		if (aie_cfg->sel_mode == FLDMODE) { /* FLD don't need to prepare buf */
+			dev_dbg(fd->dev, "FLD, Mode: %d", aie_cfg->sel_mode);
+			return 0;
+		}
+	}
+
+	memset(&fd->reg_cfg, 0, sizeof(struct aie_reg_cfg));
+
+	if (aie_cfg->pyramid_base_width == 0) {
+		fd->base_para->pyramid_width = fd->base_para->max_pyramid_width;
+		fd->base_para->pyramid_height =
+			fd->base_para->max_pyramid_height;
+		fd->base_para->number_of_pyramid = 3;
+	} else {
+		if (aie_cfg->pyramid_base_width >
+			    fd->base_para->max_pyramid_width ||
+		    aie_cfg->pyramid_base_height >
+			    fd->base_para->max_pyramid_height ||
+		    aie_cfg->number_of_pyramid > 3 ||
+		    aie_cfg->number_of_pyramid <= 0) {
+			dev_err(fd->dev,
+				"err: base w: %d h: %d num: %d\n",
+				aie_cfg->pyramid_base_width,
+				aie_cfg->pyramid_base_height,
+				aie_cfg->number_of_pyramid
+			);
+			dev_err(fd->dev,
+				"err: max w: %d h: %d\n",
+				fd->base_para->max_pyramid_width,
+				fd->base_para->max_pyramid_height
+			);
+
+			return ret;
+		}
+
+		fd->base_para->pyramid_height =
+			fd->base_para->max_pyramid_height;
+		fd->base_para->number_of_pyramid = aie_cfg->number_of_pyramid;
+		if (aie_cfg->pyramid_base_width !=
+		    fd->base_para->pyramid_width) {
+			dev_dbg(fd->dev,
+				"pre: %d cur: %d num: %d\n",
+				fd->base_para->pyramid_width,
+				aie_cfg->pyramid_base_width,
+				fd->base_para->number_of_pyramid
+			);
+			fd->base_para->pyramid_width =
+				aie_cfg->pyramid_base_width;
+			aie_update_table(fd, fd->base_para->pyramid_width,
+					 fd->base_para->pyramid_height);
+			aie_update_fddma_buf(fd);
+		}
+	}
+
+	if (aie_cfg->src_img_width > fd->base_para->max_img_width ||
+	    aie_cfg->src_img_height > fd->base_para->max_img_height) {
+		dev_err(fd->dev,
+			"AIE error: Enque Size error Src_WD: %d Src_HT: %d\n",
+			aie_cfg->src_img_width,
+			aie_cfg->src_img_height
+		);
+
+		dev_err(fd->dev,
+			"AIE error: MAX_Src_WD: %d MAX_Src_HT: %d\n",
+			fd->base_para->max_img_width,
+			fd->base_para->max_img_height
+		);
+		return ret;
+	}
+
+	aie_reset_output_buf(fd, aie_cfg);
+
+	fd->reg_cfg.fd_mode = aie_cfg->sel_mode;
+	if (aie_cfg->sel_mode == FDMODE) {
+		fd->reg_cfg.rs_adr = (u32)fd->base_para->fd_rs_cfg_pa;
+		fd->reg_cfg.yuv2rgb_adr = (u32)fd->base_para->fd_yuv2rgb_cfg_pa;
+		fd->reg_cfg.fd_adr = (u32)fd->base_para->fd_fd_cfg_pa +
+							 fd->variant->fd_cfg_size * 4 *
+							 FD_LOOP_NUM / 3 *
+							 (3 - aie_cfg->number_of_pyramid);
+
+	} else if (aie_cfg->sel_mode == ATTRIBUTEMODE) {
+		fd->reg_cfg.yuv2rgb_adr =
+			(u32)fd->base_para->attr_yuv2rgb_cfg_pa[fd->attr_para->w_idx];
+		fd->reg_cfg.fd_adr =
+			(u32)fd->base_para->attr_fd_cfg_pa[fd->attr_para->w_idx];
+	} else {
+		dev_err(fd->dev, "AIE error, Mode: %d", aie_cfg->sel_mode);
+		return ret;
+	}
+
+	ret = aie_update_cfg(fd, aie_cfg);
+	if (ret)
+		return ret;
+
+	ret = aie_config_dram(fd, aie_cfg);
+	if (ret)
+		return ret;
+
+	if (aie_cfg->sel_mode == ATTRIBUTEMODE)
+		fd->attr_para->w_idx =
+			(fd->attr_para->w_idx + 1) % MAX_ENQUE_FRAME_NUM;
+
+	return ret;
+}
+
+void aie_execute(struct mtk_aie_dev *fd, struct aie_enq_info *aie_cfg)
+{
+	unsigned int loop_num = 0;
+	unsigned int loop_reg_val = 0;
+	unsigned int i = 0;
+
+	if (aie_cfg->sel_mode == FDMODE) {
+		writel(0x0, fd->fd_base + AIE_START_REG);
+		writel(0x00000111, fd->fd_base + AIE_ENABLE_REG);
+		loop_num = FD_LOOP_NUM / 3 * (aie_cfg->number_of_pyramid);
+		loop_reg_val = (loop_num << 8) |
+			       (aie_cfg->number_of_pyramid - 1);
+		writel(loop_reg_val, fd->fd_base + AIE_LOOP_REG);
+		writel(0x1, fd->fd_base + AIE_INT_EN_REG);
+		writel(fd->reg_cfg.rs_adr,
+		       fd->fd_base + AIE_RS_CON_BASE_ADR_REG);
+		writel(fd->reg_cfg.fd_adr,
+		       fd->fd_base + AIE_FD_CON_BASE_ADR_REG);
+		writel(fd->reg_cfg.yuv2rgb_adr,
+		       fd->fd_base + AIE_YUV2RGB_CON_BASE_ADR_REG);
+
+		if (fd->variant->hw_version == 31) {
+			writel(0x00000002,
+			       fd->fd_base + AIE_YUV2RGB_CON_BASE_ADR_MSB);
+			writel(0x00000002,
+			       fd->fd_base + AIE_RS_CON_BASE_ADR_MSB);
+			writel(0x00000002,
+			       fd->fd_base + AIE_FD_CON_BASE_ADR_MSB);
+		}
+
+		writel(0x1, fd->fd_base + AIE_START_REG);
+	} else if (aie_cfg->sel_mode == ATTRIBUTEMODE) {
+		writel(0x0, fd->fd_base + AIE_START_REG);
+		writel(0x00000101, fd->fd_base + AIE_ENABLE_REG);
+		writel(0x00001A00, fd->fd_base + AIE_LOOP_REG);
+		writel(0x1, fd->fd_base + AIE_INT_EN_REG);
+		writel(fd->reg_cfg.rs_adr,
+		       fd->fd_base + AIE_RS_CON_BASE_ADR_REG);
+		writel(fd->reg_cfg.fd_adr,
+		       fd->fd_base + AIE_FD_CON_BASE_ADR_REG);
+		writel(fd->reg_cfg.yuv2rgb_adr,
+		       fd->fd_base + AIE_YUV2RGB_CON_BASE_ADR_REG);
+
+		if (fd->variant->hw_version == 31) {
+			writel(0x00000002,
+			       fd->fd_base + AIE_YUV2RGB_CON_BASE_ADR_MSB);
+			writel(0x00000002,
+			       fd->fd_base + AIE_RS_CON_BASE_ADR_MSB);
+			writel(0x00000002,
+			       fd->fd_base + AIE_FD_CON_BASE_ADR_MSB);
+		}
+
+		writel(0x1, fd->fd_base + AIE_START_REG);
+	} else if (aie_cfg->sel_mode == FLDMODE) {
+		if (fd->variant->fld_enable) {
+			writel(0x10, fd->fd_base + AIE_START_REG);
+			writel(0x00011111, fd->fd_base + AIE_DMA_CTL_REG);
+			writel(0x01111111, fd->fd_base + FLD_EN);
+			writel(0x1, fd->fd_base + AIE_INT_EN_REG);
+			for (i = 0; i < aie_cfg->fld_face_num; i++) {
+				writel(aie_cfg->src_img_addr,
+				       fd->fd_base + FLD_BASE_ADDR_FACE_0 +
+					       i * 0x4);
+				writel(aie_cfg->fld_input[i].fld_in_crop_x1
+						       << 16 |
+					       aie_cfg->fld_input[i]
+						       .fld_in_crop_y1,
+				       fd->fd_base + fld_face_info_0[i]);
+				writel(aie_cfg->fld_input[i].fld_in_crop_x2
+						       << 16 |
+					       aie_cfg->fld_input[i]
+						       .fld_in_crop_y2,
+				       fd->fd_base + fld_face_info_1[i]);
+				writel(aie_cfg->fld_input[i].fld_in_rip << 4 |
+					       aie_cfg->fld_input[i].fld_in_rop,
+				       fd->fd_base + fld_face_info_2[i]);
+			}
+
+			writel(aie_cfg->fld_face_num << 28 | FLD_FOREST << 16 |
+				       FLD_POINT,
+			       fd->fd_base + FLD_MODEL_PARA1);
+			writel(13 << 16 | 0xfe9,
+			       fd->fd_base + FLD_MODEL_PARA14);
+
+			writel(aie_cfg->src_img_width << 16 |
+				       aie_cfg->src_img_height,
+			       fd->fd_base + FLD_SRC_WD_HT);
+
+			/*input settings*/
+			writel(0x007c003f, fd->fd_base + FLD_PL_IN_SIZE_0);
+			writel(0x0040000f, fd->fd_base + FLD_PL_IN_STRIDE_0);
+			writel(0x007c003f, fd->fd_base + FLD_PL_IN_SIZE_1);
+			writel(0x0040000f, fd->fd_base + FLD_PL_IN_STRIDE_1);
+			writel(0x0016003f, fd->fd_base + FLD_PL_IN_SIZE_2_0);
+			writel(0x0040000f, fd->fd_base + FLD_PL_IN_STRIDE_2_0);
+			writel(0x0013003f, fd->fd_base + FLD_PL_IN_SIZE_2_1);
+			writel(0x0040000f, fd->fd_base + FLD_PL_IN_STRIDE_2_1);
+			writel(0x0013003f, fd->fd_base + FLD_PL_IN_SIZE_2_2);
+			writel(0x0040000f, fd->fd_base + FLD_PL_IN_STRIDE_2_2);
+			writel(0x00a6001f, fd->fd_base + FLD_PL_IN_SIZE_3);
+			writel(0x0020000f, fd->fd_base + FLD_PL_IN_STRIDE_3);
+
+			/*output setting*/
+			writel((2400 * aie_cfg->fld_face_num - 1) << 16 | 127,
+			       fd->fd_base + FLD_SH_IN_SIZE_0);
+			writel(0x0010000f, fd->fd_base + FLD_SH_IN_STRIDE_0);
+			writel(fd->fld_para->fld_output_pa[0],
+			       fd->fd_base + FLD_TR_OUT_BASE_ADDR_0);
+			writel((aie_cfg->fld_face_num - 1) << 16 | 0x6f,
+			       fd->fd_base + FLD_TR_OUT_SIZE_0);
+			writel(0x0070000f, fd->fd_base + FLD_TR_OUT_STRIDE_0);
+			writel(fd->fld_para->fld_output_pa[0],
+			       fd->fd_base + FLD_PP_OUT_BASE_ADDR_0);
+			writel((aie_cfg->fld_face_num - 1) << 16 | 0x6f,
+			       fd->fd_base + FLD_PP_OUT_SIZE_0);
+			writel(0x0070000f, fd->fd_base + FLD_PP_OUT_STRIDE_0);
+
+			/*cv score*/
+			writel(0x00000001, fd->fd_base + FLD_BS_BIAS);
+			writel(0x0000b835, fd->fd_base + FLD_CV_FM_RANGE_0);
+			writel(0xffff5cba, fd->fd_base + FLD_CV_FM_RANGE_1);
+			writel(0x00005ed5, fd->fd_base + FLD_CV_PM_RANGE_0);
+			writel(0xffff910d, fd->fd_base + FLD_CV_PM_RANGE_1);
+			writel(0x0000031e, fd->fd_base + FLD_BS_RANGE_0);
+			writel(0xfffffcae, fd->fd_base + FLD_BS_RANGE_1);
+
+			/* 6 steps */
+			writel(fd->fld_para->fld_step_pa[FLD_STEP_BLINK][14],
+			       fd->fd_base + FLD_BS_IN_BASE_ADDR_14);
+
+			writel(fd->fld_para->fld_step_pa[FLD_STEP_CV][0],
+			       fd->fd_base + FLD_PL_IN_BASE_ADDR_2_0);
+			writel(fd->fld_para->fld_step_pa[FLD_STEP_CV][1],
+			       fd->fd_base + FLD_PL_IN_BASE_ADDR_2_1);
+			writel(fd->fld_para->fld_step_pa[FLD_STEP_CV][2],
+			       fd->fd_base + FLD_PL_IN_BASE_ADDR_2_2);
+			writel(fd->fld_para->fld_step_pa[FLD_STEP_CV][3],
+			       fd->fd_base + FLD_PL_IN_BASE_ADDR_2_3);
+			writel(fd->fld_para->fld_step_pa[FLD_STEP_CV][4],
+			       fd->fd_base + FLD_PL_IN_BASE_ADDR_2_4);
+			writel(fd->fld_para->fld_step_pa[FLD_STEP_CV][5],
+			       fd->fd_base + FLD_PL_IN_BASE_ADDR_2_5);
+			writel(fd->fld_para->fld_step_pa[FLD_STEP_CV][6],
+			       fd->fd_base + FLD_PL_IN_BASE_ADDR_2_6);
+			writel(fd->fld_para->fld_step_pa[FLD_STEP_CV][7],
+			       fd->fd_base + FLD_PL_IN_BASE_ADDR_2_7);
+			writel(fd->fld_para->fld_step_pa[FLD_STEP_CV][8],
+			       fd->fd_base + FLD_PL_IN_BASE_ADDR_2_8);
+			writel(fd->fld_para->fld_step_pa[FLD_STEP_CV][9],
+			       fd->fd_base + FLD_PL_IN_BASE_ADDR_2_9);
+			writel(fd->fld_para->fld_step_pa[FLD_STEP_CV][10],
+			       fd->fd_base + FLD_PL_IN_BASE_ADDR_2_10);
+			writel(fd->fld_para->fld_step_pa[FLD_STEP_CV][11],
+			       fd->fd_base + FLD_PL_IN_BASE_ADDR_2_11);
+			writel(fd->fld_para->fld_step_pa[FLD_STEP_CV][12],
+			       fd->fd_base + FLD_PL_IN_BASE_ADDR_2_12);
+			writel(fd->fld_para->fld_step_pa[FLD_STEP_CV][13],
+			       fd->fd_base + FLD_PL_IN_BASE_ADDR_2_13);
+			writel(fd->fld_para->fld_step_pa[FLD_STEP_CV][14],
+			       fd->fd_base + FLD_PL_IN_BASE_ADDR_2_14);
+
+			writel(fd->fld_para->fld_step_pa[FLD_STEP_FP][0],
+			       fd->fd_base + FLD_PL_IN_BASE_ADDR_3_0);
+			writel(fd->fld_para->fld_step_pa[FLD_STEP_FP][1],
+			       fd->fd_base + FLD_PL_IN_BASE_ADDR_3_1);
+			writel(fd->fld_para->fld_step_pa[FLD_STEP_FP][2],
+			       fd->fd_base + FLD_PL_IN_BASE_ADDR_3_2);
+			writel(fd->fld_para->fld_step_pa[FLD_STEP_FP][3],
+			       fd->fd_base + FLD_PL_IN_BASE_ADDR_3_3);
+			writel(fd->fld_para->fld_step_pa[FLD_STEP_FP][4],
+			       fd->fd_base + FLD_PL_IN_BASE_ADDR_3_4);
+			writel(fd->fld_para->fld_step_pa[FLD_STEP_FP][5],
+			       fd->fd_base + FLD_PL_IN_BASE_ADDR_3_5);
+			writel(fd->fld_para->fld_step_pa[FLD_STEP_FP][6],
+			       fd->fd_base + FLD_PL_IN_BASE_ADDR_3_6);
+			writel(fd->fld_para->fld_step_pa[FLD_STEP_FP][7],
+			       fd->fd_base + FLD_PL_IN_BASE_ADDR_3_7);
+			writel(fd->fld_para->fld_step_pa[FLD_STEP_FP][8],
+			       fd->fd_base + FLD_PL_IN_BASE_ADDR_3_8);
+			writel(fd->fld_para->fld_step_pa[FLD_STEP_FP][9],
+			       fd->fd_base + FLD_PL_IN_BASE_ADDR_3_9);
+			writel(fd->fld_para->fld_step_pa[FLD_STEP_FP][10],
+			       fd->fd_base + FLD_PL_IN_BASE_ADDR_3_10);
+			writel(fd->fld_para->fld_step_pa[FLD_STEP_FP][11],
+			       fd->fd_base + FLD_PL_IN_BASE_ADDR_3_11);
+			writel(fd->fld_para->fld_step_pa[FLD_STEP_FP][12],
+			       fd->fd_base + FLD_PL_IN_BASE_ADDR_3_12);
+			writel(fd->fld_para->fld_step_pa[FLD_STEP_FP][13],
+			       fd->fd_base + FLD_PL_IN_BASE_ADDR_3_13);
+			writel(fd->fld_para->fld_step_pa[FLD_STEP_FP][14],
+			       fd->fd_base + FLD_PL_IN_BASE_ADDR_3_14);
+
+			writel(fd->fld_para->fld_step_pa[FLD_STEP_LEAF][0],
+			       fd->fd_base + FLD_SH_IN_BASE_ADDR_0);
+			writel(fd->fld_para->fld_step_pa[FLD_STEP_LEAF][1],
+			       fd->fd_base + FLD_SH_IN_BASE_ADDR_1);
+			writel(fd->fld_para->fld_step_pa[FLD_STEP_LEAF][2],
+			       fd->fd_base + FLD_SH_IN_BASE_ADDR_2);
+			writel(fd->fld_para->fld_step_pa[FLD_STEP_LEAF][3],
+			       fd->fd_base + FLD_SH_IN_BASE_ADDR_3);
+			writel(fd->fld_para->fld_step_pa[FLD_STEP_LEAF][4],
+			       fd->fd_base + FLD_SH_IN_BASE_ADDR_4);
+			writel(fd->fld_para->fld_step_pa[FLD_STEP_LEAF][5],
+			       fd->fd_base + FLD_SH_IN_BASE_ADDR_5);
+			writel(fd->fld_para->fld_step_pa[FLD_STEP_LEAF][6],
+			       fd->fd_base + FLD_SH_IN_BASE_ADDR_6);
+			writel(fd->fld_para->fld_step_pa[FLD_STEP_LEAF][7],
+			       fd->fd_base + FLD_SH_IN_BASE_ADDR_7);
+			writel(fd->fld_para->fld_step_pa[FLD_STEP_LEAF][8],
+			       fd->fd_base + FLD_SH_IN_BASE_ADDR_8);
+			writel(fd->fld_para->fld_step_pa[FLD_STEP_LEAF][9],
+			       fd->fd_base + FLD_SH_IN_BASE_ADDR_9);
+			writel(fd->fld_para->fld_step_pa[FLD_STEP_LEAF][10],
+			       fd->fd_base + FLD_SH_IN_BASE_ADDR_10);
+			writel(fd->fld_para->fld_step_pa[FLD_STEP_LEAF][11],
+			       fd->fd_base + FLD_SH_IN_BASE_ADDR_11);
+			writel(fd->fld_para->fld_step_pa[FLD_STEP_LEAF][12],
+			       fd->fd_base + FLD_SH_IN_BASE_ADDR_12);
+			writel(fd->fld_para->fld_step_pa[FLD_STEP_LEAF][13],
+			       fd->fd_base + FLD_SH_IN_BASE_ADDR_13);
+			writel(fd->fld_para->fld_step_pa[FLD_STEP_LEAF][14],
+			       fd->fd_base + FLD_SH_IN_BASE_ADDR_14);
+
+			writel(fd->fld_para->fld_step_pa[FLD_STEP_KM02][0],
+			       fd->fd_base + FLD_PL_IN_BASE_ADDR_0_0);
+			writel(fd->fld_para->fld_step_pa[FLD_STEP_KM02][1],
+			       fd->fd_base + FLD_PL_IN_BASE_ADDR_0_1);
+			writel(fd->fld_para->fld_step_pa[FLD_STEP_KM02][2],
+			       fd->fd_base + FLD_PL_IN_BASE_ADDR_0_2);
+			writel(fd->fld_para->fld_step_pa[FLD_STEP_KM02][3],
+			       fd->fd_base + FLD_PL_IN_BASE_ADDR_0_3);
+			writel(fd->fld_para->fld_step_pa[FLD_STEP_KM02][4],
+			       fd->fd_base + FLD_PL_IN_BASE_ADDR_0_4);
+			writel(fd->fld_para->fld_step_pa[FLD_STEP_KM02][5],
+			       fd->fd_base + FLD_PL_IN_BASE_ADDR_0_5);
+			writel(fd->fld_para->fld_step_pa[FLD_STEP_KM02][6],
+			       fd->fd_base + FLD_PL_IN_BASE_ADDR_0_6);
+			writel(fd->fld_para->fld_step_pa[FLD_STEP_KM02][7],
+			       fd->fd_base + FLD_PL_IN_BASE_ADDR_0_7);
+			writel(fd->fld_para->fld_step_pa[FLD_STEP_KM02][8],
+			       fd->fd_base + FLD_PL_IN_BASE_ADDR_0_8);
+			writel(fd->fld_para->fld_step_pa[FLD_STEP_KM02][9],
+			       fd->fd_base + FLD_PL_IN_BASE_ADDR_0_9);
+			writel(fd->fld_para->fld_step_pa[FLD_STEP_KM02][10],
+			       fd->fd_base + FLD_PL_IN_BASE_ADDR_0_10);
+			writel(fd->fld_para->fld_step_pa[FLD_STEP_KM02][11],
+			       fd->fd_base + FLD_PL_IN_BASE_ADDR_0_11);
+			writel(fd->fld_para->fld_step_pa[FLD_STEP_KM02][12],
+			       fd->fd_base + FLD_PL_IN_BASE_ADDR_0_12);
+			writel(fd->fld_para->fld_step_pa[FLD_STEP_KM02][13],
+			       fd->fd_base + FLD_PL_IN_BASE_ADDR_0_13);
+			writel(fd->fld_para->fld_step_pa[FLD_STEP_KM02][14],
+			       fd->fd_base + FLD_PL_IN_BASE_ADDR_0_14);
+
+			writel(fd->fld_para->fld_step_pa[FLD_STEP_KM13][0],
+			       fd->fd_base + FLD_PL_IN_BASE_ADDR_1_0);
+			writel(fd->fld_para->fld_step_pa[FLD_STEP_KM13][1],
+			       fd->fd_base + FLD_PL_IN_BASE_ADDR_1_1);
+			writel(fd->fld_para->fld_step_pa[FLD_STEP_KM13][2],
+			       fd->fd_base + FLD_PL_IN_BASE_ADDR_1_2);
+			writel(fd->fld_para->fld_step_pa[FLD_STEP_KM13][3],
+			       fd->fd_base + FLD_PL_IN_BASE_ADDR_1_3);
+			writel(fd->fld_para->fld_step_pa[FLD_STEP_KM13][4],
+			       fd->fd_base + FLD_PL_IN_BASE_ADDR_1_4);
+			writel(fd->fld_para->fld_step_pa[FLD_STEP_KM13][5],
+			       fd->fd_base + FLD_PL_IN_BASE_ADDR_1_5);
+			writel(fd->fld_para->fld_step_pa[FLD_STEP_KM13][6],
+			       fd->fd_base + FLD_PL_IN_BASE_ADDR_1_6);
+			writel(fd->fld_para->fld_step_pa[FLD_STEP_KM13][7],
+			       fd->fd_base + FLD_PL_IN_BASE_ADDR_1_7);
+			writel(fd->fld_para->fld_step_pa[FLD_STEP_KM13][8],
+			       fd->fd_base + FLD_PL_IN_BASE_ADDR_1_8);
+			writel(fd->fld_para->fld_step_pa[FLD_STEP_KM13][9],
+			       fd->fd_base + FLD_PL_IN_BASE_ADDR_1_9);
+			writel(fd->fld_para->fld_step_pa[FLD_STEP_KM13][10],
+			       fd->fd_base + FLD_PL_IN_BASE_ADDR_1_10);
+			writel(fd->fld_para->fld_step_pa[FLD_STEP_KM13][11],
+			       fd->fd_base + FLD_PL_IN_BASE_ADDR_1_11);
+			writel(fd->fld_para->fld_step_pa[FLD_STEP_KM13][12],
+			       fd->fd_base + FLD_PL_IN_BASE_ADDR_1_12);
+			writel(fd->fld_para->fld_step_pa[FLD_STEP_KM13][13],
+			       fd->fd_base + FLD_PL_IN_BASE_ADDR_1_13);
+			writel(fd->fld_para->fld_step_pa[FLD_STEP_KM13][14],
+			       fd->fd_base + FLD_PL_IN_BASE_ADDR_1_14);
+
+			/* */
+			writel(0x22222222,
+			       fd->fd_base + FLD_PL_IN_BASE_ADDR_0_0_7_MSB);
+			writel(0x02222222,
+			       fd->fd_base + FLD_PL_IN_BASE_ADDR_0_8_15_MSB);
+
+			writel(0x22222222,
+			       fd->fd_base + FLD_PL_IN_BASE_ADDR_1_0_7_MSB);
+			writel(0x02222222,
+			       fd->fd_base + FLD_PL_IN_BASE_ADDR_1_8_15_MSB);
+
+			writel(0x22222222,
+			       fd->fd_base + FLD_PL_IN_BASE_ADDR_2_0_7_MSB);
+			writel(0x02222222,
+			       fd->fd_base + FLD_PL_IN_BASE_ADDR_2_8_15_MSB);
+
+			writel(0x22222222,
+			       fd->fd_base + FLD_PL_IN_BASE_ADDR_3_0_7_MSB);
+			writel(0x02222222,
+			       fd->fd_base + FLD_PL_IN_BASE_ADDR_3_8_15_MSB);
+
+			writel(0x22222222,
+			       fd->fd_base + FLD_SH_IN_BASE_ADDR_0_7_MSB);
+			writel(0x02222222,
+			       fd->fd_base + FLD_SH_IN_BASE_ADDR_8_15_MSB);
+
+			writel(0x02000000,
+			       fd->fd_base + FLD_BS_IN_BASE_ADDR_8_15_MSB);
+
+			writel(0x22222222,
+			       fd->fd_base + FLD_BASE_ADDR_FACE_0_7_MSB);
+			writel(0x02222222,
+			       fd->fd_base + FLD_BASE_ADDR_FACE_8_14_MSB);
+			writel(0x00000002,
+			       fd->fd_base + FLD_TR_OUT_BASE_ADDR_0_MSB);
+			writel(0x00000002,
+			       fd->fd_base + FLD_PP_OUT_BASE_ADDR_0_MSB);
+
+			/*fld mode + trigger start*/
+			writel(0x11, fd->fd_base + AIE_START_REG);
+		}
+	}
+}
+
+void aie_irqhandle(struct mtk_aie_dev *fd)
+{
+	writel(0x0, fd->fd_base + AIE_START_REG);
+
+	/* interrupt read clear */
+	readl(fd->fd_base + AIE_INT_REG);
+}
+
+static u16 aie_get_hi16(unsigned int value)
+{
+	return (value & 0xFFFF0000) >> 16;
+}
+
+static u16 aie_get_lo16(unsigned int value)
+{
+	return value & 0xFFFF;
+}
+
+static signed short aie_refine_s16_value(signed short value)
+{
+	s16 result = 0;
+
+	if ((value & 0x200) >> 9)
+		result = (value | 0xFE00);
+	else
+		result = value;
+
+	return result;
+}
+
+/* return aie_cfg to user space */
+void aie_get_fd_result(struct mtk_aie_dev *fd, struct aie_enq_info *aie_cfg)
+{
+	void *fd_pym_result[PYM_NUM] = { NULL, NULL, NULL };
+	unsigned int *pto12 = NULL;
+	unsigned int i = 0, j = 0;
+	struct FDRESULT *prst = NULL;
+	signed short landmark = 0;
+	struct aie_enq_info *tmp_aie_cfg = NULL;
+	u32 fd_result_hw = 0, fd_result_1_hw = 0;
+	u32 fd_total_num = 0;
+	u32 fd_pyramid_num[PYM_NUM] = { 0, 0, 0 };
+
+	aie_cfg->sel_mode = fd->base_para->sel_mode;
+	aie_cfg->rotate_degree = fd->base_para->rotate_degree;
+	aie_cfg->src_img_addr = fd->base_para->src_img_addr;
+	aie_cfg->src_img_addr_uv = fd->base_para->src_img_addr_uv;
+	aie_cfg->src_img_width = fd->base_para->img_width;
+	aie_cfg->src_img_height = fd->base_para->img_height;
+	aie_cfg->src_img_fmt = fd->base_para->src_img_fmt;
+	aie_cfg->fd_version = FD_VERSION;
+	aie_cfg->attr_version = ATTR_VERSION;
+
+	aie_cfg->irq_status = readl(fd->fd_base + AIE_INT_EN_REG);
+
+	fd_result_hw = fd->reg_cfg.hw_result;
+	fd_result_1_hw = fd->reg_cfg.hw_result1;
+	fd_total_num = fd_result_hw & 0xFFF;
+	fd_pyramid_num[0] = (fd_result_hw & 0xFFF0000) >> 16;
+	fd_pyramid_num[1] = fd_result_1_hw & 0xFFF;
+	fd_pyramid_num[2] = (fd_result_1_hw & 0xFFF0000) >> 16;
+
+	if (fd_total_num == 0)
+		goto nothing_out;
+
+	tmp_aie_cfg =  aie_cfg;
+
+	tmp_aie_cfg->fd_out.fd_total_num = fd_total_num;
+	tmp_aie_cfg->fd_out.fd_pyramid0_num = fd_pyramid_num[0];
+	tmp_aie_cfg->fd_out.fd_pyramid1_num = fd_pyramid_num[1];
+	tmp_aie_cfg->fd_out.fd_pyramid2_num = fd_pyramid_num[2];
+
+	switch (tmp_aie_cfg->number_of_pyramid) {
+	case 1:
+		fd_pym_result[2] = fd->dma_para->fd_out_hw_va[RPN0_LOOP_NUM][0];
+		break;
+	case 2:
+		fd_pym_result[1] = fd->dma_para->fd_out_hw_va[RPN0_LOOP_NUM][0];
+		fd_pym_result[2] = fd->dma_para->fd_out_hw_va[RPN1_LOOP_NUM][0];
+		break;
+	case 3:
+		fd_pym_result[0] = fd->dma_para->fd_out_hw_va[RPN0_LOOP_NUM][0];
+		fd_pym_result[1] = fd->dma_para->fd_out_hw_va[RPN1_LOOP_NUM][0];
+		fd_pym_result[2] = fd->dma_para->fd_out_hw_va[RPN2_LOOP_NUM][0];
+		break;
+	default:
+		dev_err(fd->dev, "Wrong number_of_pyramid\n");
+		goto nothing_out;
+	}
+
+	for (i = 0; i < 3; i++) {
+		for (j = 0; j < fd_pyramid_num[i]; j++) {
+			if (i == 0)
+				prst = &tmp_aie_cfg->fd_out.PYRAMID0_RESULT;
+			else if (i == 1)
+				prst = &tmp_aie_cfg->fd_out.PYRAMID1_RESULT;
+			else if (i == 2)
+				prst = &tmp_aie_cfg->fd_out.PYRAMID2_RESULT;
+
+			pto12 = (unsigned int *)fd_pym_result[i] + 12 * j;
+
+			prst->anchor_x0[j] = aie_get_lo16(*(pto12 + 0));
+			prst->anchor_y0[j] = aie_get_hi16(*(pto12 + 0));
+			prst->anchor_x1[j] = aie_get_lo16(*(pto12 + 1));
+			prst->anchor_y1[j] = aie_get_hi16(*(pto12 + 1));
+
+			if (prst->anchor_x1[j] == 0 ||
+			    prst->anchor_y1[j] == 0) {
+				dev_err(fd->dev,
+					"wrong coordinate: i=%d j=%d M:%d %d %d %d\n",
+					i,
+					j,
+					prst->anchor_x0[j],
+					prst->anchor_x1[j],
+					prst->anchor_y0[j],
+					prst->anchor_y1[j]
+				);
+				goto nothing_out;
+			}
+
+			/* ROP result at 1st run */
+			landmark = (*(pto12 + 2) & 0x3FF);
+			prst->rop_landmark_score0[j] =
+				aie_refine_s16_value(landmark);
+			landmark = ((*(pto12 + 2) & 0xFFC00) >> 10);
+			prst->rop_landmark_score1[j] =
+				aie_refine_s16_value(landmark);
+			landmark = ((*(pto12 + 2) & 0x3FF00000) >> 20);
+			prst->rop_landmark_score2[j] =
+				aie_refine_s16_value(landmark);
+
+			prst->anchor_score[j] =
+				aie_refine_s16_value(*(pto12 + 9) & 0x3FF);
+
+			/* RIP result at 1st run */
+			landmark = ((*(pto12 + 9) & 0xFFC00) >> 10);
+			prst->rip_landmark_score0[j] =
+				aie_refine_s16_value(landmark);
+			landmark = ((*(pto12 + 9) & 0x3FF00000) >> 20);
+			prst->rip_landmark_score1[j] =
+				aie_refine_s16_value(landmark);
+			landmark = ((*(pto12 + 9) & 0xC0000000) >> 30) |
+				   ((*(pto12 + 10) & 0xFF) << 2);
+			prst->rip_landmark_score2[j] =
+				aie_refine_s16_value(landmark);
+			landmark = ((*(pto12 + 10) & 0x3FF00) >> 8);
+			prst->rip_landmark_score3[j] =
+				aie_refine_s16_value(landmark);
+			landmark = ((*(pto12 + 10) & 0xFFC0000) >> 18);
+			prst->rip_landmark_score4[j] =
+				aie_refine_s16_value(landmark);
+			landmark = ((*(pto12 + 10) & 0xF0000000) >> 28) |
+				   ((*(pto12 + 11) & 0x3F) << 4);
+			prst->rip_landmark_score5[j] =
+				aie_refine_s16_value(landmark);
+			landmark = ((*(pto12 + 11) & 0xFFC0) >> 6);
+			prst->rip_landmark_score6[j] =
+				aie_refine_s16_value(landmark);
+			prst->face_result_index[j] =
+				((*(pto12 + 11) & 0xFFF0000) >> 16);
+			prst->anchor_index[j] =
+				((*(pto12 + 11) & 0x70000000) >> 28);
+
+			prst->fd_partial_result = fd_pyramid_num[i];
+		}
+	}
+	return;
+nothing_out:
+	/* Ensure that user mode does not receive an inappropriate result structure */
+	memset(&aie_cfg->fd_out, 0, sizeof(struct fd_result));
+}
+
+void aie_get_attr_result(struct mtk_aie_dev *fd, struct aie_enq_info *aie_cfg)
+{
+	u32 *attr_race_result = NULL, *attr_gender_result = NULL;
+	u32 *attr_age_result = NULL, *attr_is_indian_result = NULL;
+
+	aie_cfg->sel_mode = fd->attr_para->sel_mode[fd->attr_para->r_idx];
+	aie_cfg->rotate_degree =
+		fd->attr_para->rotate_degree[fd->attr_para->r_idx];
+	aie_cfg->src_img_addr =
+		fd->attr_para->src_img_addr[fd->attr_para->r_idx];
+	aie_cfg->src_img_addr_uv =
+		fd->attr_para->src_img_addr_uv[fd->attr_para->r_idx];
+	aie_cfg->src_img_width = fd->attr_para->img_width[fd->attr_para->r_idx];
+	aie_cfg->src_img_height =
+		fd->attr_para->img_height[fd->attr_para->r_idx];
+	aie_cfg->src_img_fmt = fd->attr_para->src_img_fmt[fd->attr_para->r_idx];
+	aie_cfg->fd_version = FD_VERSION;
+	aie_cfg->attr_version = ATTR_VERSION;
+
+	aie_cfg->irq_status = readl(fd->fd_base + AIE_INT_EN_REG);
+
+	/* 64 feature * 32 bytes */
+	attr_age_result =
+		(u32 *)fd->dma_para->age_out_hw_va[fd->attr_para->r_idx];
+	attr_gender_result =
+		(u32 *)fd->dma_para->gender_out_hw_va[fd->attr_para->r_idx];
+	attr_is_indian_result =
+		(u32 *)fd->dma_para->is_indian_out_hw_va[fd->attr_para->r_idx];
+	attr_race_result =
+		(u32 *)fd->dma_para->race_out_hw_va[fd->attr_para->r_idx];
+
+	aie_cfg->attr_out.MERGED_AGE_RESULT.RESULT[0] =
+		aie_get_lo16(*attr_age_result);
+	aie_cfg->attr_out.MERGED_AGE_RESULT.RESULT[1] =
+		aie_get_hi16(*attr_age_result);
+
+	aie_cfg->attr_out.MERGED_GENDER_RESULT.RESULT[0] =
+		aie_get_lo16(*attr_gender_result);
+	aie_cfg->attr_out.MERGED_GENDER_RESULT.RESULT[1] =
+		aie_get_hi16(*attr_gender_result);
+
+	aie_cfg->attr_out.MERGED_IS_INDIAN_RESULT.RESULT[0] =
+		aie_get_lo16(*attr_is_indian_result);
+	aie_cfg->attr_out.MERGED_IS_INDIAN_RESULT.RESULT[1] =
+		aie_get_hi16(*attr_is_indian_result);
+
+	aie_cfg->attr_out.MERGED_RACE_RESULT.RESULT[0] =
+		aie_get_lo16(*attr_race_result);
+	aie_cfg->attr_out.MERGED_RACE_RESULT.RESULT[1] =
+		aie_get_hi16(*attr_race_result);
+	aie_cfg->attr_out.MERGED_RACE_RESULT.RESULT[2] =
+		aie_get_lo16(*(attr_race_result + 1));
+
+	fd->attr_para->r_idx = (fd->attr_para->r_idx + 1) % MAX_ENQUE_FRAME_NUM;
+}
+
+void aie_get_fld_result(struct mtk_aie_dev *fd, struct aie_enq_info *aie_cfg)
+{
+	int i = 0, j = 0;
+	u16 *out_parsing = NULL;
+	u8 fld_rlt[FLD_MAX_FRAME][FLD_OUTPUT_SIZE];
+
+	aie_cfg->irq_status = readl(fd->fd_base + AIE_INT_EN_REG);
+
+	memcpy(fld_rlt, fd->fld_para->fld_output_va[0], sizeof(fld_rlt));
+
+	for (j = 0; j < aie_cfg->fld_face_num; j++) {
+		out_parsing = (unsigned short *)&fld_rlt[j][0];
+		for (i = 0; i < FLD_CUR_LANDMARK; i++) {
+			aie_cfg->fld_out[j].fld_landmark[i].x = *out_parsing;
+			aie_cfg->fld_out[j].fld_landmark[i].y =
+				*(out_parsing + 1);
+
+			if (i % 2)
+				out_parsing = out_parsing + 6;
+			else
+				out_parsing = out_parsing + 2;
+		}
+		out_parsing = (unsigned short *)&fld_rlt[j][0];
+		if (FLD_CUR_LANDMARK % 2)
+			out_parsing =
+				out_parsing + ((FLD_CUR_LANDMARK + 1) / 2) * 8;
+		else
+			out_parsing = out_parsing + (FLD_CUR_LANDMARK / 2) * 8;
+
+		aie_cfg->fld_out[j].fld_out_rop = *out_parsing;
+		aie_cfg->fld_out[j].fld_out_rip = *(out_parsing + 1);
+		aie_cfg->fld_out[j].confidence = *(out_parsing + 2);
+		aie_cfg->fld_out[j].blinkscore = *(out_parsing + 3);
+	}
+}
diff --git a/drivers/media/platform/mediatek/aie/mtk_aie_v4l2.c b/drivers/media/platform/mediatek/aie/mtk_aie_v4l2.c
new file mode 100644
index 0000000..b95102a
--- /dev/null
+++ b/drivers/media/platform/mediatek/aie/mtk_aie_v4l2.c
@@ -0,0 +1,1907 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2020 MediaTek Inc.
+ *
+ * Author: Fish Wu <fish.wu@mediatek.com>
+ *
+ */
+
+#include <linux/clk.h>
+#include <linux/debugfs.h>
+#include <linux/dma-mapping.h>
+#include <linux/interrupt.h>
+#include <linux/iopoll.h>
+#include <linux/jiffies.h>
+#include <linux/module.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/types.h>
+
+#include <media/v4l2-event.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-mem2mem.h>
+#include <media/videobuf2-core.h>
+#include <media/videobuf2-dma-contig.h>
+
+#include "mtk_aie.h"
+
+static const struct mtk_aie_variant *mtk_aie_get_variant(struct device *dev);
+
+static struct clk_bulk_data aie_clks[] = {
+	{ .id = "img-ipe" },
+	{ .id = "ipe-fdvt" },
+	{ .id = "ipe-smi-larb12" },
+	{ .id = "ipe-top" },
+};
+
+#ifdef CONFIG_DEBUG_FS
+#define AIE_DUMP_BUFFER_SIZE (1 * 1024 * 1024)
+#define AIE_DEBUG_RAW_DATA_SIZE (1 * 640 * 480)
+static unsigned char srcrawdata[AIE_DEBUG_RAW_DATA_SIZE];
+static unsigned char dstrawdata[AIE_DEBUG_RAW_DATA_SIZE];
+static struct debugfs_blob_wrapper srcdata, dstdata;
+#endif
+
+#ifdef CONFIG_INTERCONNECT_MTK_EXTENSION
+#define AIE_QOS_MAX 4
+#define AIE_QOS_RA_IDX 0
+#define AIE_QOS_RB_IDX 1
+#define AIE_QOS_WA_IDX 2
+#define AIE_QOS_WB_IDX 3
+#define AIE_READ_AVG_BW 213
+#define AIE_WRITE_AVG_BW 145
+#endif
+
+#define V4L2_CID_MTK_AIE_MAX 2
+
+static const struct v4l2_pix_format_mplane mtk_aie_img_fmts[] = {
+	{
+		.pixelformat = V4L2_PIX_FMT_NV16M,
+		.num_planes = 2,
+	},
+	{
+		.pixelformat = V4L2_PIX_FMT_NV61M,
+		.num_planes = 2,
+	},
+	{
+		.pixelformat = V4L2_PIX_FMT_YUYV,
+		.num_planes = 1,
+	},
+	{
+		.pixelformat = V4L2_PIX_FMT_YVYU,
+		.num_planes = 1,
+	},
+	{
+		.pixelformat = V4L2_PIX_FMT_UYVY,
+		.num_planes = 1,
+	},
+	{
+		.pixelformat = V4L2_PIX_FMT_VYUY,
+		.num_planes = 1,
+	},
+	{
+		.pixelformat = V4L2_PIX_FMT_GREY,
+		.num_planes = 1,
+	},
+	{
+		.pixelformat = V4L2_PIX_FMT_NV12M,
+		.num_planes = 2,
+	},
+	{
+		.pixelformat = V4L2_PIX_FMT_NV12,
+		.num_planes = 1,
+	},
+};
+
+#ifdef CONFIG_INTERCONNECT_MTK_EXTENSION
+static const struct mtk_aie_qos_path aie_qos_path[AIE_QOS_MAX] = {
+	{ NULL, "l12_fdvt_rda", 0 },
+	{ NULL, "l12_fdvt_rdb", 0 },
+	{ NULL, "l12_fdvt_wra", 0 },
+	{ NULL, "l12_fdvt_wrb", 0 }
+};
+#endif
+
+#define NUM_FORMATS ARRAY_SIZE(mtk_aie_img_fmts)
+
+static inline struct mtk_aie_ctx *fh_to_ctx(struct v4l2_fh *fh)
+{
+	return container_of(fh, struct mtk_aie_ctx, fh);
+}
+
+#ifdef CONFIG_INTERCONNECT_MTK_EXTENSION
+static int mtk_aie_mmdvfs_init(struct mtk_aie_dev *fd)
+{
+	u64 freq = 0;
+	int ret = -ENOMEM;
+	int opp_num = 0, opp_idx = 0, idx = 0, volt = 0;
+	struct device_node *np = NULL, *child_np = NULL;
+	struct of_phandle_iterator it;
+
+	fd->dvfs_info = kzalloc(sizeof(*fd->dvfs_info), GFP_KERNEL);
+	if (!fd->dvfs_info)
+		return ret;
+
+	fd->dvfs_info->dev = fd->dev;
+	ret = dev_pm_opp_of_add_table(fd->dvfs_info->dev);
+	if (ret < 0) {
+		dev_err(fd->dvfs_info->dev, "fail to init opp table: %d\n", ret);
+		return ret;
+	}
+
+	fd->dvfs_info->reg =
+		devm_regulator_get(fd->dvfs_info->dev, "dvfsrc-vcore");
+	if (IS_ERR(fd->dvfs_info->reg)) {
+		dev_err(fd->dvfs_info->dev, "can't get dvfsrc-vcore\n");
+		ret = PTR_ERR(fd->dvfs_info->reg);
+		fd->dvfs_info->reg = NULL;
+		return ret;
+	}
+
+	opp_num = regulator_count_voltages(fd->dvfs_info->reg);
+	of_for_each_phandle(&it, ret, fd->dvfs_info->dev->of_node, "operating-points-v2", NULL, 0) {
+		if (!it.node) {
+			dev_err(fd->dvfs_info->dev, "of_node_get fail\n");
+			return ret;
+		}
+		np = of_node_get(it.node);
+
+		do {
+			child_np = of_get_next_available_child(np, child_np);
+			if (child_np) {
+				of_property_read_u64(child_np, "opp-hz", &freq);
+				of_property_read_u32(child_np, "opp-microvolt", &volt);
+				if (freq == 0 || volt == 0) {
+					dev_err(fd->dvfs_info->dev,
+						"%s: [ERROR] parsing zero freq/volt(%d/%d) at idx(%d)\n",
+						__func__,
+						freq,
+						volt,
+						idx
+					);
+					continue;
+				}
+				fd->dvfs_info->clklv[opp_idx][idx] = freq;
+				fd->dvfs_info->voltlv[opp_idx][idx] = volt;
+				dev_dbg(fd->dvfs_info->dev,
+					"[%s] opp=%d, idx=%d, clk=%d volt=%d\n",
+					__func__,
+					opp_idx,
+					idx,
+					fd->dvfs_info->clklv[opp_idx][idx],
+					fd->dvfs_info->voltlv[opp_idx][idx]
+				);
+				idx++;
+			}
+		} while (child_np);
+		fd->dvfs_info->clklv_num[opp_idx] = idx;
+		fd->dvfs_info->clklv_target[opp_idx] = fd->dvfs_info->clklv[opp_idx][0];
+		fd->dvfs_info->clklv_idx[opp_idx] = 0;
+		idx = 0;
+		opp_idx++;
+		of_node_put(np);
+	}
+	fd->dvfs_info->cur_volt = 0;
+
+	return 0;
+}
+
+static void mtk_aie_mmdvfs_uninit(struct mtk_aie_dev *fd)
+{
+	if (!fd->dvfs_info)
+		return;
+
+	fd->dvfs_info->cur_volt = 0;
+	if (fd->dvfs_info->reg)
+		regulator_set_voltage(fd->dvfs_info->reg, 0, INT_MAX);
+
+	dev_pm_opp_of_remove_table(fd->dvfs_info->dev);
+	kfree(fd->dvfs_info);
+	fd->dvfs_info = NULL;
+}
+
+static void mtk_aie_mmdvfs_set(struct mtk_aie_dev *fd, bool is_set,
+			       unsigned int level)
+{
+	int volt = 0, idx = 0, opp_idx = 0;
+
+	if (is_set) {
+		if (level < fd->dvfs_info->clklv_num[opp_idx])
+			idx = level;
+	}
+	volt = fd->dvfs_info->voltlv[opp_idx][idx];
+
+	if (fd->dvfs_info->cur_volt != volt) {
+		dev_dbg(fd->dvfs_info->dev,
+			"[%s] volt change opp=%d, idx=%d, clk=%d volt=%d\n",
+			__func__,
+			opp_idx,
+			idx,
+			fd->dvfs_info->clklv[opp_idx][idx],
+			fd->dvfs_info->voltlv[opp_idx][idx]
+		);
+		regulator_set_voltage(fd->dvfs_info->reg, volt, INT_MAX);
+		fd->dvfs_info->cur_volt = volt;
+	}
+}
+
+static int mtk_aie_mmqos_init(struct mtk_aie_dev *fd)
+{
+	int idx = 0;
+	int ret = -ENOMEM;
+
+	fd->qos_info = kzalloc(sizeof(*fd->qos_info), GFP_KERNEL);
+	if (!fd->qos_info)
+		return ret;
+
+	fd->qos_info->dev = fd->dev;
+	fd->qos_info->qos_path = (struct mtk_aie_qos_path *)aie_qos_path;
+
+	for (idx = 0; idx < AIE_QOS_MAX; idx++) {
+		fd->qos_info->qos_path[idx].path =
+			of_mtk_icc_get(fd->qos_info->dev, fd->qos_info->qos_path[idx].dts_name);
+		fd->qos_info->qos_path[idx].bw = 0;
+		dev_dbg(fd->qos_info->dev,
+			"[%s] idx=%d, path=%p, name=%s, bw=%llu\n",
+			__func__,
+			idx,
+			fd->qos_info->qos_path[idx].path,
+			fd->qos_info->qos_path[idx].dts_name,
+			fd->qos_info->qos_path[idx].bw
+		);
+	}
+	return 0;
+}
+
+static void mtk_aie_mmqos_uninit(struct mtk_aie_dev *fd)
+{
+	int idx;
+
+	if (!fd->qos_info)
+		return;
+
+	for (idx = 0; idx < AIE_QOS_MAX; idx++) {
+		if (!fd->qos_info->qos_path[idx].path)
+			continue;
+
+		fd->qos_info->qos_path[idx].bw = 0;
+		mtk_icc_set_bw(fd->qos_info->qos_path[idx].path, 0, 0);
+	}
+
+	kfree(fd->qos_info);
+	fd->qos_info = NULL;
+}
+
+static void mtk_aie_mmqos_set(struct mtk_aie_dev *fd, bool is_set)
+{
+	int r_bw = 0;
+	int w_bw = 0;
+	int idx = 0;
+
+	if (is_set) {
+		r_bw = AIE_READ_AVG_BW;
+		w_bw = AIE_WRITE_AVG_BW;
+	}
+
+	for (idx = 0; idx < AIE_QOS_MAX; idx++) {
+		if (!fd->qos_info->qos_path[idx].path) {
+			dev_dbg(fd->qos_info->dev,
+				"[%s] path of idx(%d) is NULL\n",
+				__func__,
+				idx
+			);
+			continue;
+		}
+
+		if (idx == AIE_QOS_RA_IDX &&
+		    fd->qos_info->qos_path[idx].bw != r_bw) {
+			dev_dbg(fd->qos_info->dev,
+				"[%s] idx=%d, path=%p, bw=%llu/%d,\n",
+				__func__,
+				idx,
+				fd->qos_info->qos_path[idx].path,
+				fd->qos_info->qos_path[idx].bw,
+				r_bw
+			);
+			fd->qos_info->qos_path[idx].bw = r_bw;
+			mtk_icc_set_bw(fd->qos_info->qos_path[idx].path,
+				       MBps_to_icc(fd->qos_info->qos_path[idx].bw),
+				       0
+			);
+		}
+
+		if (idx == AIE_QOS_WA_IDX &&
+		    fd->qos_info->qos_path[idx].bw != w_bw) {
+			dev_dbg(fd->qos_info->dev,
+				"[%s] idx=%d, path=%p, bw=%llu/%d,\n",
+				__func__,
+				idx,
+				fd->qos_info->qos_path[idx].path,
+				fd->qos_info->qos_path[idx].bw,
+				w_bw
+			);
+			fd->qos_info->qos_path[idx].bw = w_bw;
+			mtk_icc_set_bw(fd->qos_info->qos_path[idx].path,
+				       MBps_to_icc(fd->qos_info->qos_path[idx].bw),
+				       0
+			);
+		}
+	}
+}
+#endif
+
+static int mtk_aie_fill_init_param(struct mtk_aie_dev *fd,
+				   struct user_init *user_init,
+				   struct v4l2_ctrl_handler *hdl)
+{
+	struct v4l2_ctrl *ctrl = NULL;
+
+	ctrl = v4l2_ctrl_find(hdl, V4L2_CID_MTK_AIE_INIT);
+	if (ctrl) {
+		memcpy(user_init, ctrl->p_new.p_u32, sizeof(struct user_init));
+		dev_info(fd->dev,
+			 "init param : max w:%d, max h:%d",
+			 user_init->max_img_width,
+			 user_init->max_img_height
+		);
+		dev_info(fd->dev,
+			 "init param : p_w:%d, p_h:%d, f thread:%d",
+			 user_init->pyramid_width,
+			 user_init->pyramid_height,
+			 user_init->feature_threshold
+		);
+	} else {
+		dev_err(fd->dev, "NO V4L2_CID_MTK_AIE_INIT!\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int mtk_aie_hw_enable(struct mtk_aie_dev *fd)
+{
+	struct mtk_aie_ctx *ctx = fd->ctx;
+	struct user_init user_init;
+	int ret = -EINVAL;
+
+	memset(&user_init, 0, sizeof(struct user_init));
+
+	/* initial value */
+	ret = mtk_aie_fill_init_param(fd, &user_init, &ctx->hdl);
+	if (ret != 0)
+		goto init_fail;
+
+	ret = aie_init(fd, &user_init);
+
+init_fail:
+	return ret;
+}
+
+static void mtk_aie_hw_job_finish(struct mtk_aie_dev *fd,
+				  enum vb2_buffer_state vb_state)
+{
+	struct mtk_aie_ctx *ctx = NULL;
+	struct vb2_v4l2_buffer *src_vbuf = NULL, *dst_vbuf = NULL;
+
+	pm_runtime_put(fd->dev);
+	ctx = v4l2_m2m_get_curr_priv(fd->m2m_dev);
+	if (!ctx) {
+		dev_err(fd->dev, "Failed to do v4l2_m2m_get_curr_priv!\n");
+	} else {
+		src_vbuf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx);
+		dst_vbuf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx);
+		if (src_vbuf && dst_vbuf)
+			v4l2_m2m_buf_copy_metadata(src_vbuf, dst_vbuf);
+		if (src_vbuf)
+			v4l2_m2m_buf_done(src_vbuf, vb_state);
+		if (dst_vbuf)
+			v4l2_m2m_buf_done(dst_vbuf, vb_state);
+		if (src_vbuf && dst_vbuf)
+			v4l2_m2m_job_finish(fd->m2m_dev, ctx->fh.m2m_ctx);
+	}
+	complete_all(&fd->fd_job_finished);
+}
+
+static int mtk_aie_hw_connect(struct mtk_aie_dev *fd)
+{
+	if (mtk_aie_hw_enable(fd))
+		return -EINVAL;
+#ifdef CONFIG_INTERCONNECT_MTK_EXTENSION
+	mtk_aie_mmdvfs_set(fd, 1, 0);
+	mtk_aie_mmqos_set(fd, 1);
+#endif
+
+	return 0;
+}
+
+static void mtk_aie_hw_disconnect(struct mtk_aie_dev *fd)
+{
+#ifdef CONFIG_INTERCONNECT_MTK_EXTENSION
+	mtk_aie_mmqos_set(fd, 0);
+	mtk_aie_mmdvfs_set(fd, 0, 0);
+#endif
+	aie_uninit(fd);
+}
+
+static int mtk_aie_hw_job_exec(struct mtk_aie_dev *fd)
+{
+	int ret;
+
+	ret = pm_runtime_get_sync(fd->dev);
+	if (ret < 0) {
+		pm_runtime_put_noidle(fd->dev);
+		dev_err(fd->dev, "failed to enable power: %d\n", ret);
+		return ret;
+	}
+
+	reinit_completion(&fd->fd_job_finished);
+	schedule_delayed_work(&fd->job_timeout_work,
+			      msecs_to_jiffies(MTK_FD_HW_TIMEOUT_IN_MSEC)
+	);
+
+	return 0;
+}
+
+static int mtk_aie_vb2_buf_out_validate(struct vb2_buffer *vb)
+{
+	struct vb2_v4l2_buffer *v4l2_buf = to_vb2_v4l2_buffer(vb);
+
+	if (v4l2_buf->field == V4L2_FIELD_ANY)
+		v4l2_buf->field = V4L2_FIELD_NONE;
+	if (v4l2_buf->field != V4L2_FIELD_NONE)
+		return -EINVAL;
+
+	return 0;
+}
+
+static int mtk_aie_vb2_buf_prepare(struct vb2_buffer *vb)
+{
+	struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+	struct vb2_queue *vq = vb->vb2_queue;
+	struct mtk_aie_ctx *ctx = vb2_get_drv_priv(vq);
+	struct device *dev = ctx->dev;
+	struct v4l2_pix_format_mplane *pixfmt = NULL;
+	int ret = 0;
+
+	switch (vq->type) {
+	case V4L2_BUF_TYPE_META_CAPTURE:
+		if (vb2_plane_size(vb, 0) < ctx->dst_fmt.buffersize) {
+			dev_err(dev, "meta size %lu is too small\n", vb2_plane_size(vb, 0));
+			ret = -EINVAL;
+		} else {
+			vb2_set_plane_payload(vb, 0, ctx->dst_fmt.buffersize);
+		}
+		break;
+	case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
+		pixfmt = &ctx->src_fmt;
+
+		if (vbuf->field == V4L2_FIELD_ANY)
+			vbuf->field = V4L2_FIELD_NONE;
+
+		if (vb->num_planes > 2 || vbuf->field != V4L2_FIELD_NONE) {
+			dev_dbg(dev,
+				"plane %d or field %d not supported\n",
+				vb->num_planes,
+				vbuf->field
+			);
+			ret = -EINVAL;
+		}
+
+		if (vb2_plane_size(vb, 0) < pixfmt->plane_fmt[0].sizeimage) {
+			dev_dbg(dev,
+				"plane 0 %lu is too small than %x\n",
+				vb2_plane_size(vb, 0),
+				pixfmt->plane_fmt[0].sizeimage
+			);
+			ret = -EINVAL;
+		} else {
+			vb2_set_plane_payload(vb, 0, pixfmt->plane_fmt[0].sizeimage);
+		}
+
+		if (pixfmt->num_planes == 2 &&
+		    vb2_plane_size(vb, 1) < pixfmt->plane_fmt[1].sizeimage) {
+			dev_dbg(dev,
+				"plane 1 %lu is too small than %x\n",
+				vb2_plane_size(vb, 1),
+				pixfmt->plane_fmt[1].sizeimage
+			);
+			ret = -EINVAL;
+		} else {
+			vb2_set_plane_payload(vb, 1, pixfmt->plane_fmt[1].sizeimage);
+		}
+		break;
+	}
+
+	return ret;
+}
+
+static void mtk_aie_vb2_buf_queue(struct vb2_buffer *vb)
+{
+	struct mtk_aie_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
+	struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+
+	v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf);
+}
+
+static int mtk_aie_vb2_queue_setup(struct vb2_queue *vq,
+				   unsigned int *num_buffers,
+				   unsigned int *num_planes,
+				   unsigned int sizes[],
+				   struct device *alloc_devs[])
+{
+	struct mtk_aie_ctx *ctx = vb2_get_drv_priv(vq);
+	struct device *dev = ctx->dev;
+	unsigned int size[2] = { 0, 0 };
+	unsigned int plane = 0;
+
+	switch (vq->type) {
+	case V4L2_BUF_TYPE_META_CAPTURE:
+		size[0] = ctx->dst_fmt.buffersize;
+		break;
+	case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
+		size[0] = ctx->src_fmt.plane_fmt[0].sizeimage;
+		size[1] = ctx->src_fmt.plane_fmt[1].sizeimage;
+		break;
+	}
+
+	dev_info(dev, "vq type =%d, size[0]=%d, size[1]=%d\n", vq->type, size[0], size[1]);
+
+	if (*num_planes > 2)
+		return -EINVAL;
+
+	*num_buffers = clamp_val(*num_buffers, 1, VB2_MAX_FRAME);
+
+	if (*num_planes == 0) {
+		if (vq->type == V4L2_BUF_TYPE_META_CAPTURE) {
+			sizes[0] = ctx->dst_fmt.buffersize;
+			*num_planes = 1;
+			return 0;
+		}
+
+		*num_planes = ctx->src_fmt.num_planes;
+		if (*num_planes > 2)
+			return -EINVAL;
+		for (plane = 0; plane < *num_planes; plane++)
+			sizes[plane] = ctx->src_fmt.plane_fmt[plane].sizeimage;
+
+		return 0;
+	}
+
+	return 0;
+}
+
+static int mtk_aie_vb2_start_streaming(struct vb2_queue *vq, unsigned int count)
+{
+	struct mtk_aie_ctx *ctx = vb2_get_drv_priv(vq);
+	struct mtk_aie_dev *fd = NULL;
+
+	if (!ctx)
+		return -EINVAL;
+
+	fd = ctx->fd_dev;
+
+	if (vq->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+		fd->fd_stream_count++;
+		if (fd->fd_stream_count == 1)
+			return mtk_aie_hw_connect(ctx->fd_dev);
+	}
+
+	return 0;
+}
+
+#ifdef CONFIG_DEBUG_FS
+static void aie_dumpinfo(struct mtk_aie_dev *fd)
+{
+	unsigned int i = 0;
+	u32 *cfg_value = NULL;
+
+	for (i = 0; i < 0x158; i = i + 4) {
+		fd->tr_info.dump_offset +=
+			scnprintf(fd->tr_info.dump_buffer + fd->tr_info.dump_offset,
+				  fd->tr_info.dump_size - fd->tr_info.dump_offset,
+				  "Reg: %x %x\n",
+				  i,
+				  readl(fd->fd_base + i)
+			);
+	}
+	for (i = 0x200; i < 0x3F8; i = i + 4) {
+		fd->tr_info.dump_offset +=
+			scnprintf(fd->tr_info.dump_buffer + fd->tr_info.dump_offset,
+				  fd->tr_info.dump_size - fd->tr_info.dump_offset,
+				  "Dma Reg: %x %x\n",
+				  i,
+				  readl(fd->fd_base + i)
+			);
+	}
+	if (fd->aie_cfg->sel_mode == FDMODE) {
+		cfg_value = (u32 *)fd->base_para->fd_yuv2rgb_cfg_va;
+		for (i = 0; i < fd->fd_yuv2rgb_cfg_size / 4; i++) {
+			fd->tr_info.dump_offset +=
+				scnprintf(fd->tr_info.dump_buffer + fd->tr_info.dump_offset,
+					  fd->tr_info.dump_size - fd->tr_info.dump_offset,
+					  "YUV config: %x, %x\n",
+					  i,
+					  cfg_value[i]
+				);
+		}
+		cfg_value = (u32 *)fd->base_para->fd_rs_cfg_va;
+		for (i = 0; i < fd->fd_rs_cfg_size / 4; i++) {
+			fd->tr_info.dump_offset +=
+				scnprintf(fd->tr_info.dump_buffer + fd->tr_info.dump_offset,
+					  fd->tr_info.dump_size - fd->tr_info.dump_offset,
+					  "RS config: %x, %x\n",
+					  i,
+					  cfg_value[i]
+				);
+		}
+		cfg_value = (u32 *)fd->base_para->fd_fd_cfg_va;
+		for (i = 0; i < fd->fd_fd_cfg_size / 4; i++) {
+			fd->tr_info.dump_offset +=
+				scnprintf(fd->tr_info.dump_buffer + fd->tr_info.dump_offset,
+					  fd->tr_info.dump_size - fd->tr_info.dump_offset,
+					  "FD config: %x, %x\n",
+					  i,
+					  cfg_value[i]
+				);
+		}
+	}
+
+	if (fd->aie_cfg->sel_mode == ATTRIBUTEMODE) {
+		cfg_value = (u32 *)fd->base_para->attr_yuv2rgb_cfg_va;
+		for (i = 0; i < fd->fd_yuv2rgb_cfg_size / 4; i++) {
+			fd->tr_info.dump_offset +=
+				scnprintf(fd->tr_info.dump_buffer + fd->tr_info.dump_offset,
+					  fd->tr_info.dump_size - fd->tr_info.dump_offset,
+					  "YUV config: %x, %x\n",
+					  i,
+					  cfg_value[i]
+				);
+		}
+		cfg_value = (u32 *)fd->base_para->attr_fd_cfg_va;
+		for (i = 0; i < fd->fd_fd_cfg_size / 4; i++) {
+			fd->tr_info.dump_offset +=
+				scnprintf(fd->tr_info.dump_buffer + fd->tr_info.dump_offset,
+					  fd->tr_info.dump_size - fd->tr_info.dump_offset,
+					  "FD config: %x, %x\n",
+					  i,
+					  cfg_value[i]
+				);
+		}
+	}
+
+	if (fd->tr_info.srcdata_size <= AIE_DEBUG_RAW_DATA_SIZE)
+		memcpy(srcrawdata, fd->tr_info.srcdata_buf, fd->tr_info.srcdata_size);
+
+	if (fd->tr_info.dstdata_size <= AIE_DEBUG_RAW_DATA_SIZE)
+		memcpy(dstrawdata, fd->tr_info.dstdata_buf, fd->tr_info.dstdata_size);
+}
+#endif
+
+static void mtk_aie_job_timeout_work(struct work_struct *work)
+{
+	struct mtk_aie_dev *fd =
+		container_of(work, struct mtk_aie_dev, job_timeout_work.work);
+
+	dev_err(fd->dev, "FD Job timeout!");
+
+	dev_dbg(fd->dev,
+		"%s result result1: %x, %x, %x",
+		__func__,
+		readl(fd->fd_base + AIE_RESULT_0_REG),
+		readl(fd->fd_base + AIE_RESULT_1_REG),
+		readl(fd->fd_base + AIE_DMA_CTL_REG)
+	);
+
+	fd->aie_cfg->irq_status = readl(fd->fd_base + AIE_INT_EN_REG);
+
+#ifdef CONFIG_DEBUG_FS
+	if (fd->aie_cfg->irq_status & 0x00010000 ||
+	    fd->aie_cfg->irq_status & 0x00100000 ||
+	    fd->aie_cfg->irq_status & 0x01000000)
+		aie_dumpinfo(fd);
+#endif
+
+	if (fd->aie_cfg->sel_mode == ATTRIBUTEMODE)
+		dev_dbg(fd->dev,
+			"[ATTRMODE] w_idx = %d, r_idx = %d\n",
+			fd->attr_para->w_idx,
+			fd->attr_para->r_idx
+		);
+
+	aie_irqhandle(fd);
+	aie_reset(fd);
+	atomic_dec(&fd->num_composing);
+	mtk_aie_hw_job_finish(fd, VB2_BUF_STATE_ERROR);
+	wake_up(&fd->flushing_waitq);
+}
+
+static int mtk_aie_job_wait_finish(struct mtk_aie_dev *fd)
+{
+	return wait_for_completion_timeout(&fd->fd_job_finished, msecs_to_jiffies(1000));
+}
+
+static void mtk_aie_vb2_stop_streaming(struct vb2_queue *vq)
+{
+	struct mtk_aie_ctx *ctx = vb2_get_drv_priv(vq);
+	struct mtk_aie_dev *fd = ctx->fd_dev;
+	struct vb2_v4l2_buffer *vb = NULL;
+	struct v4l2_m2m_ctx *m2m_ctx = ctx->fh.m2m_ctx;
+	struct v4l2_m2m_queue_ctx *queue_ctx;
+
+	if (!mtk_aie_job_wait_finish(fd))
+		dev_info(fd->dev, "wait job finish timeout\n");
+
+	if (vq->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+		fd->fd_stream_count--;
+		if (fd->fd_stream_count > 0)
+			dev_info(fd->dev, "stop: fd_stream_count = %d\n", fd->fd_stream_count);
+		else
+			mtk_aie_hw_disconnect(fd);
+	}
+
+	queue_ctx = V4L2_TYPE_IS_OUTPUT(vq->type) ? &m2m_ctx->out_q_ctx :
+						    &m2m_ctx->cap_q_ctx;
+	while ((vb = v4l2_m2m_buf_remove(queue_ctx)))
+		v4l2_m2m_buf_done(vb, VB2_BUF_STATE_ERROR);
+}
+
+static void mtk_aie_vb2_request_complete(struct vb2_buffer *vb)
+{
+	struct mtk_aie_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
+
+	v4l2_ctrl_request_complete(vb->req_obj.req, &ctx->hdl);
+}
+
+static int mtk_aie_querycap(struct file *file, void *fh,
+			    struct v4l2_capability *cap)
+{
+	struct mtk_aie_dev *fd = video_drvdata(file);
+	struct device *dev = fd->dev;
+
+	strscpy(cap->driver, dev_driver_string(dev), sizeof(cap->driver));
+	strscpy(cap->card, dev_driver_string(dev), sizeof(cap->card));
+
+	cap->device_caps = V4L2_CAP_VIDEO_OUTPUT_MPLANE |
+			   V4L2_CAP_STREAMING | V4L2_CAP_META_CAPTURE;
+	cap->capabilities = V4L2_CAP_DEVICE_CAPS | cap->device_caps;
+
+	return 0;
+}
+
+static int mtk_aie_enum_fmt_out_mp(struct file *file, void *fh,
+				   struct v4l2_fmtdesc *f)
+{
+	if (f->index >= NUM_FORMATS)
+		return -EINVAL;
+
+	f->pixelformat = mtk_aie_img_fmts[f->index].pixelformat;
+	return 0;
+}
+
+static void mtk_aie_fill_pixfmt_mp(struct v4l2_pix_format_mplane *dfmt,
+				   const struct v4l2_pix_format_mplane *sfmt)
+{
+	dfmt->field = V4L2_FIELD_NONE;
+	dfmt->colorspace = V4L2_COLORSPACE_BT2020;
+	dfmt->num_planes = sfmt->num_planes;
+	dfmt->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
+	dfmt->quantization = V4L2_QUANTIZATION_DEFAULT;
+	dfmt->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(dfmt->colorspace);
+	dfmt->pixelformat = sfmt->pixelformat;
+
+	/* Keep user setting as possible */
+	dfmt->width = clamp(dfmt->width, MTK_FD_OUTPUT_MIN_WIDTH,
+			    MTK_FD_OUTPUT_MAX_WIDTH);
+	dfmt->height = clamp(dfmt->height, MTK_FD_OUTPUT_MIN_HEIGHT,
+			     MTK_FD_OUTPUT_MAX_HEIGHT);
+
+	dfmt->plane_fmt[0].bytesperline = ALIGN(dfmt->width, 16);
+	dfmt->plane_fmt[1].bytesperline = ALIGN(dfmt->width, 16);
+
+	if (sfmt->num_planes == 2) {
+		dfmt->plane_fmt[0].sizeimage =
+			dfmt->height * dfmt->plane_fmt[0].bytesperline;
+		if (sfmt->pixelformat == V4L2_PIX_FMT_NV12M)
+			dfmt->plane_fmt[1].sizeimage =
+				dfmt->height * dfmt->plane_fmt[1].bytesperline /
+				2;
+		else
+			dfmt->plane_fmt[1].sizeimage =
+				dfmt->height * dfmt->plane_fmt[1].bytesperline;
+	} else {
+		if (sfmt->pixelformat == V4L2_PIX_FMT_NV12)
+			dfmt->plane_fmt[0].sizeimage =
+				dfmt->height * dfmt->plane_fmt[0].bytesperline *
+				3 / 2;
+		else
+			dfmt->plane_fmt[0].sizeimage =
+				dfmt->height * dfmt->plane_fmt[0].bytesperline;
+	}
+}
+
+static const struct v4l2_pix_format_mplane *mtk_aie_find_fmt(u32 format)
+{
+	unsigned int i = 0;
+
+	for (i = 0; i < NUM_FORMATS; i++) {
+		if (mtk_aie_img_fmts[i].pixelformat == format)
+			return &mtk_aie_img_fmts[i];
+	}
+
+	return NULL;
+}
+
+static int mtk_aie_try_fmt_out_mp(struct file *file, void *fh,
+				  struct v4l2_format *f)
+{
+	struct v4l2_pix_format_mplane *pix_mp = &f->fmt.pix_mp;
+	const struct v4l2_pix_format_mplane *fmt = NULL;
+
+	fmt = mtk_aie_find_fmt(pix_mp->pixelformat);
+	if (!fmt)
+		fmt = &mtk_aie_img_fmts[0]; /* Get default img fmt */
+
+	mtk_aie_fill_pixfmt_mp(pix_mp, fmt);
+	return 0;
+}
+
+static int mtk_aie_g_fmt_out_mp(struct file *file, void *fh,
+				struct v4l2_format *f)
+{
+	struct mtk_aie_ctx *ctx = fh_to_ctx(fh);
+
+	f->fmt.pix_mp = ctx->src_fmt;
+
+	return 0;
+}
+
+static int mtk_aie_s_fmt_out_mp(struct file *file, void *fh,
+				struct v4l2_format *f)
+{
+	struct mtk_aie_ctx *ctx = fh_to_ctx(fh);
+	struct vb2_queue *vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type);
+	struct mtk_aie_dev *fd = NULL;
+	const struct v4l2_pix_format_mplane *fmt = NULL;
+
+	if (!ctx)
+		return -EINVAL;
+
+	fd = ctx->fd_dev;
+
+	/* Change not allowed if queue is streaming. */
+	if (vb2_is_streaming(vq))
+		return -EBUSY;
+
+	fmt = mtk_aie_find_fmt(f->fmt.pix_mp.pixelformat);
+	if (!fmt)
+		fmt = &mtk_aie_img_fmts[0]; /* Get default img fmt */
+	else if (&fd->ctx->fh != file->private_data)
+		return -EBUSY;
+	if (fd->ctx != ctx)
+		fd->ctx = ctx;
+
+	mtk_aie_fill_pixfmt_mp(&f->fmt.pix_mp, fmt);
+	ctx->src_fmt = f->fmt.pix_mp;
+
+	return 0;
+}
+
+static int mtk_aie_enum_fmt_meta_cap(struct file *file, void *fh,
+				     struct v4l2_fmtdesc *f)
+{
+	if (f->index)
+		return -EINVAL;
+
+	strscpy(f->description, "Face detection result",
+		sizeof(f->description));
+
+	f->pixelformat = V4L2_META_FMT_MTFD_RESULT;
+	f->flags = 0;
+
+	return 0;
+}
+
+static int mtk_aie_g_fmt_meta_cap(struct file *file, void *fh,
+				  struct v4l2_format *f)
+{
+	f->fmt.meta.dataformat = V4L2_META_FMT_MTFD_RESULT;
+	f->fmt.meta.buffersize = sizeof(struct aie_enq_info);
+
+	return 0;
+}
+
+static const struct vb2_ops mtk_aie_vb2_ops = {
+	.queue_setup = mtk_aie_vb2_queue_setup,
+	.buf_out_validate = mtk_aie_vb2_buf_out_validate,
+	.buf_prepare = mtk_aie_vb2_buf_prepare,
+	.buf_queue = mtk_aie_vb2_buf_queue,
+	.start_streaming = mtk_aie_vb2_start_streaming,
+	.stop_streaming = mtk_aie_vb2_stop_streaming,
+	.wait_prepare = vb2_ops_wait_prepare,
+	.wait_finish = vb2_ops_wait_finish,
+	.buf_request_complete = mtk_aie_vb2_request_complete,
+};
+
+static const struct v4l2_ioctl_ops mtk_aie_v4l2_video_out_ioctl_ops = {
+	.vidioc_querycap = mtk_aie_querycap,
+	.vidioc_enum_fmt_vid_out = mtk_aie_enum_fmt_out_mp,
+	.vidioc_g_fmt_vid_out_mplane = mtk_aie_g_fmt_out_mp,
+	.vidioc_s_fmt_vid_out_mplane = mtk_aie_s_fmt_out_mp,
+	.vidioc_try_fmt_vid_out_mplane = mtk_aie_try_fmt_out_mp,
+	.vidioc_enum_fmt_meta_cap = mtk_aie_enum_fmt_meta_cap,
+	.vidioc_g_fmt_meta_cap = mtk_aie_g_fmt_meta_cap,
+	.vidioc_s_fmt_meta_cap = mtk_aie_g_fmt_meta_cap,
+	.vidioc_try_fmt_meta_cap = mtk_aie_g_fmt_meta_cap,
+	.vidioc_reqbufs = v4l2_m2m_ioctl_reqbufs,
+	.vidioc_create_bufs = v4l2_m2m_ioctl_create_bufs,
+	.vidioc_expbuf = v4l2_m2m_ioctl_expbuf,
+	.vidioc_prepare_buf = v4l2_m2m_ioctl_prepare_buf,
+	.vidioc_querybuf = v4l2_m2m_ioctl_querybuf,
+	.vidioc_qbuf = v4l2_m2m_ioctl_qbuf,
+	.vidioc_dqbuf = v4l2_m2m_ioctl_dqbuf,
+	.vidioc_streamon = v4l2_m2m_ioctl_streamon,
+	.vidioc_streamoff = v4l2_m2m_ioctl_streamoff,
+	.vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
+	.vidioc_unsubscribe_event = v4l2_event_unsubscribe,
+};
+
+static int mtk_aie_queue_init(void *priv, struct vb2_queue *src_vq,
+			      struct vb2_queue *dst_vq)
+{
+	struct mtk_aie_ctx *ctx = priv;
+	int ret = -EINVAL;
+
+	src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
+	src_vq->io_modes = VB2_MMAP | VB2_DMABUF;
+	src_vq->supports_requests = true;
+	src_vq->drv_priv = ctx;
+	src_vq->ops = &mtk_aie_vb2_ops;
+	src_vq->mem_ops = &vb2_dma_contig_memops;
+	src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
+	src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
+	src_vq->lock = &ctx->fd_dev->vfd_lock;
+	src_vq->dev = ctx->fd_dev->v4l2_dev.dev;
+
+	ret = vb2_queue_init(src_vq);
+	if (ret)
+		return ret;
+
+	dst_vq->type = V4L2_BUF_TYPE_META_CAPTURE;
+	dst_vq->io_modes = VB2_MMAP | VB2_DMABUF;
+	dst_vq->drv_priv = ctx;
+	dst_vq->ops = &mtk_aie_vb2_ops;
+	dst_vq->mem_ops = &vb2_dma_contig_memops;
+	dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
+	dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
+	dst_vq->lock = &ctx->fd_dev->vfd_lock;
+	dst_vq->dev = ctx->fd_dev->v4l2_dev.dev;
+
+	return vb2_queue_init(dst_vq);
+}
+
+static struct v4l2_ctrl_config mtk_aie_controls[] = {
+	{
+		.id = V4L2_CID_MTK_AIE_INIT,
+		.name = "FD detection init",
+		.type = V4L2_CTRL_TYPE_U32,
+		.min = 0,
+		.max = 0xffffffff,
+		.step = 1,
+		.def = 0,
+		.dims = { sizeof(struct user_init) / 4 },
+	},
+	{
+		.id = V4L2_CID_MTK_AIE_PARAM,
+		.name = "FD detection param",
+		.type = V4L2_CTRL_TYPE_U32,
+		.min = 0,
+		.max = 0xffffffff,
+		.step = 1,
+		.def = 0,
+		.dims = { sizeof(struct user_param) / 4 },
+	},
+};
+
+static int mtk_aie_ctrls_setup(struct mtk_aie_ctx *ctx)
+{
+	struct v4l2_ctrl_handler *hdl = &ctx->hdl;
+	int i;
+
+	v4l2_ctrl_handler_init(hdl, V4L2_CID_MTK_AIE_MAX);
+	if (hdl->error)
+		return hdl->error;
+
+	for (i = 0; i < ARRAY_SIZE(mtk_aie_controls); i++) {
+		v4l2_ctrl_new_custom(hdl, &mtk_aie_controls[i], ctx);
+		if (hdl->error) {
+			v4l2_ctrl_handler_free(hdl);
+			dev_err(ctx->dev, "Failed to register controls:%d", i);
+			return hdl->error;
+		}
+	}
+
+	ctx->fh.ctrl_handler = &ctx->hdl;
+	v4l2_ctrl_handler_setup(hdl);
+
+	return 0;
+}
+
+static void init_ctx_fmt(struct mtk_aie_ctx *ctx)
+{
+	struct v4l2_pix_format_mplane *src_fmt = &ctx->src_fmt;
+	struct v4l2_meta_format *dst_fmt = &ctx->dst_fmt;
+
+	/* Initialize M2M source fmt */
+	src_fmt->width = MTK_FD_OUTPUT_MAX_WIDTH;
+	src_fmt->height = MTK_FD_OUTPUT_MAX_HEIGHT;
+	mtk_aie_fill_pixfmt_mp(src_fmt, &mtk_aie_img_fmts[0]);
+
+	/* Initialize M2M destination fmt */
+	dst_fmt->buffersize = sizeof(struct aie_enq_info);
+	dst_fmt->dataformat = V4L2_META_FMT_MTFD_RESULT;
+}
+
+/*
+ * V4L2 file operations.
+ */
+static int mtk_vfd_open(struct file *filp)
+{
+	struct mtk_aie_dev *fd = video_drvdata(filp);
+	struct video_device *vdev = video_devdata(filp);
+	struct mtk_aie_ctx *ctx = NULL;
+	int ret = -EINVAL;
+
+	mutex_lock(&fd->dev_lock);
+
+	if (fd->fd_state & STATE_OPEN) {
+		dev_info(fd->dev, "vfd_open again");
+		ret =  -EBUSY;
+		goto err_unlock;
+	}
+
+	ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
+	if (!ctx) {
+		ret =  -ENOMEM;
+		goto err_unlock;
+	}
+
+	ctx->fd_dev = fd;
+	ctx->dev = fd->dev;
+	fd->ctx = ctx;
+
+	v4l2_fh_init(&ctx->fh, vdev);
+	filp->private_data = &ctx->fh;
+
+	init_ctx_fmt(ctx);
+
+	ret = mtk_aie_ctrls_setup(ctx);
+	if (ret) {
+		dev_err(ctx->dev, "Failed to set up controls:%d\n", ret);
+		goto err_fh_exit;
+	}
+	ctx->fh.m2m_ctx =
+		v4l2_m2m_ctx_init(fd->m2m_dev, ctx, &mtk_aie_queue_init);
+	if (IS_ERR(ctx->fh.m2m_ctx)) {
+		ret = PTR_ERR(ctx->fh.m2m_ctx);
+		goto err_free_ctrl_handler;
+	}
+	v4l2_fh_add(&ctx->fh);
+	fd->fd_state |= STATE_OPEN;
+
+	mutex_unlock(&fd->dev_lock);
+
+	return 0;
+err_free_ctrl_handler:
+	v4l2_ctrl_handler_free(&ctx->hdl);
+err_fh_exit:
+	v4l2_fh_exit(&ctx->fh);
+	kfree(ctx);
+err_unlock:
+	mutex_unlock(&fd->dev_lock);
+
+	return ret;
+}
+
+static int mtk_vfd_release(struct file *filp)
+{
+	struct mtk_aie_ctx *ctx =
+		container_of(filp->private_data, struct mtk_aie_ctx, fh);
+	struct mtk_aie_dev *fd = video_drvdata(filp);
+
+	mutex_lock(&fd->dev_lock);
+
+	fd->fd_state &= ~STATE_OPEN;
+
+	v4l2_m2m_ctx_release(ctx->fh.m2m_ctx);
+	v4l2_ctrl_handler_free(&ctx->hdl);
+	v4l2_fh_del(&ctx->fh);
+	v4l2_fh_exit(&ctx->fh);
+
+	kfree(ctx);
+
+	mutex_unlock(&fd->dev_lock);
+
+	return 0;
+}
+
+static __poll_t mtk_vfd_fop_poll(struct file *file, poll_table *wait)
+{
+	struct mtk_aie_ctx *ctx =
+		container_of(file->private_data, struct mtk_aie_ctx, fh);
+
+	struct mtk_aie_dev *fd = ctx->fd_dev;
+
+	if (fd->fd_state & STATE_INIT) {
+		if (!mtk_aie_job_wait_finish(ctx->fd_dev)) {
+			dev_info(ctx->dev, "wait job finish timeout from poll\n");
+			return EPOLLERR;
+		}
+	}
+
+	return v4l2_m2m_fop_poll(file, wait);
+}
+
+static const struct v4l2_file_operations fd_video_fops = {
+	.owner = THIS_MODULE,
+	.open = mtk_vfd_open,
+	.release = mtk_vfd_release,
+	.poll = mtk_vfd_fop_poll,
+	.unlocked_ioctl = video_ioctl2,
+	.mmap = v4l2_m2m_fop_mmap,
+};
+
+static void mtk_aie_fill_user_param(struct mtk_aie_dev *fd,
+				    struct user_param *user_param,
+				    struct v4l2_ctrl_handler *hdl)
+{
+	struct v4l2_ctrl *ctrl;
+
+	ctrl = v4l2_ctrl_find(hdl, V4L2_CID_MTK_AIE_PARAM);
+	if (ctrl)
+		memcpy(user_param, ctrl->p_new.p_u32, sizeof(struct user_param));
+	else
+		dev_err(fd->dev, "NO V4L2_CID_MTK_AIE_PARAM!\n");
+}
+
+static int mtk_aie_job_ready(void *priv)
+{
+	struct mtk_aie_ctx *ctx = priv;
+	struct mtk_aie_dev *fd = ctx->fd_dev;
+	struct vb2_v4l2_buffer *src_buf = NULL, *dst_buf = NULL;
+	struct fd_enq_param fd_param = {};
+	void *plane_vaddr = NULL;
+	int ret = 1;
+
+	if (!ctx->fh.m2m_ctx) {
+		dev_info(fd->dev, "Memory-to-memory context is NULL\n");
+		ret = 0;
+		goto ctrl_ret;
+	}
+
+	if (!(fd->fd_state & (STATE_INIT | STATE_OPEN))) {
+		dev_err(fd->dev, "%s fd state fail: %d\n", __func__, fd->fd_state);
+		ret = 0;
+		goto ctrl_ret;
+	}
+
+	mutex_lock(&fd->fd_lock);
+
+	src_buf = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx);
+	dst_buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx);
+
+	mtk_aie_fill_user_param(fd, &fd_param.user_param, &ctx->hdl);
+
+	plane_vaddr = vb2_plane_vaddr(&dst_buf->vb2_buf, 0);
+	if (!plane_vaddr) {
+		dev_info(fd->dev, "Failed to get plane virtual address\n");
+		ret = 0;
+		goto err_unlock;
+	}
+
+#ifdef CONFIG_DEBUG_FS
+	fd->tr_info.srcdata_buf = vb2_plane_vaddr(&src_buf->vb2_buf, 0);
+	fd->tr_info.srcdata_size = vb2_get_plane_payload(&src_buf->vb2_buf, 0);
+	fd->tr_info.dstdata_buf = vb2_plane_vaddr(&dst_buf->vb2_buf, 0);
+	fd->tr_info.dstdata_size = vb2_get_plane_payload(&dst_buf->vb2_buf, 0);
+#endif
+
+	fd->aie_cfg = (struct aie_enq_info *)plane_vaddr;
+
+	memset(fd->aie_cfg, 0, sizeof(struct aie_enq_info));
+
+	memcpy(fd->aie_cfg, &fd_param.user_param, sizeof(struct user_param));
+
+	if (fd->variant->fld_enable) {
+		fd->aie_cfg->fld_face_num = fd_param.user_param.fld_face_num;
+		memcpy(fd->aie_cfg->fld_input,
+		       fd_param.user_param.fld_input,
+		       FLD_MAX_FRAME * sizeof(struct fld_crop_rip_rop)
+		);
+	}
+
+	fd_param.src_img[0].dma_addr =
+		vb2_dma_contig_plane_dma_addr(&src_buf->vb2_buf, 0);
+
+	if (ctx->src_fmt.num_planes == 2) {
+		fd_param.src_img[1].dma_addr =
+			vb2_dma_contig_plane_dma_addr(&src_buf->vb2_buf, 1);
+	}
+
+	if ((fd->aie_cfg->sel_mode == FDMODE || fd->aie_cfg->sel_mode == ATTRIBUTEMODE) &&
+	    fd->aie_cfg->src_img_fmt == FMT_YUV420_1P) {
+		fd_param.src_img[1].dma_addr =
+			fd_param.src_img[0].dma_addr +
+			fd_param.user_param.src_img_stride *
+			fd_param.user_param.src_img_height;
+	}
+
+	fd->aie_cfg->src_img_addr = fd_param.src_img[0].dma_addr;
+	fd->aie_cfg->src_img_addr_uv = fd_param.src_img[1].dma_addr;
+
+	if (aie_prepare(fd, fd->aie_cfg)) {
+		dev_err(fd->dev, "Failed to prepare aie setting\n");
+		ret = 0;
+		goto err_unlock;
+	}
+
+#ifdef CONFIG_INTERCONNECT_MTK_EXTENSION
+	mtk_aie_mmdvfs_set(fd, 1, fd->aie_cfg->freq_level);
+#endif
+
+err_unlock:
+	mutex_unlock(&fd->fd_lock);
+
+ctrl_ret:
+	if (src_buf)
+		v4l2_ctrl_request_complete(src_buf->vb2_buf.req_obj.req, &ctx->hdl);
+
+	return ret;
+}
+
+static void mtk_aie_device_run(void *priv)
+{
+	struct mtk_aie_ctx *ctx = priv;
+	struct mtk_aie_dev *fd = ctx->fd_dev;
+	struct vb2_v4l2_buffer *src_vbuf, *dst_vbuf;
+	int ret;
+
+	ret = mtk_aie_job_ready(priv);
+	if (ret != 1) {
+		dev_err(fd->dev, "Failed to run job ready\n");
+		goto err_job_finish;
+	}
+
+	atomic_inc(&fd->num_composing);
+	ret = mtk_aie_hw_job_exec(fd);
+	if (ret) {
+		atomic_dec(&fd->num_composing);
+		goto err_job_finish;
+	}
+	aie_execute(fd, fd->aie_cfg);
+	return;
+
+err_job_finish:
+	src_vbuf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx);
+	dst_vbuf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx);
+	if (src_vbuf)
+		v4l2_m2m_buf_done(src_vbuf, VB2_BUF_STATE_ERROR);
+	if (dst_vbuf)
+		v4l2_m2m_buf_done(dst_vbuf, VB2_BUF_STATE_ERROR);
+	v4l2_m2m_job_finish(fd->m2m_dev, ctx->fh.m2m_ctx);
+}
+
+static struct v4l2_m2m_ops fd_m2m_ops = {
+	.device_run = mtk_aie_device_run,
+};
+
+static const struct media_device_ops fd_m2m_media_ops = {
+	.req_validate = vb2_request_validate,
+	.req_queue = v4l2_m2m_request_queue,
+};
+
+static int mtk_aie_video_device_register(struct mtk_aie_dev *fd)
+{
+	struct video_device *vfd = &fd->vfd;
+	struct v4l2_m2m_dev *m2m_dev = fd->m2m_dev;
+	struct device *dev = fd->dev;
+	int ret = -EINVAL;
+
+	vfd->fops = &fd_video_fops;
+	vfd->release = video_device_release_empty;
+	vfd->lock = &fd->vfd_lock;
+	vfd->v4l2_dev = &fd->v4l2_dev;
+	vfd->vfl_dir = VFL_DIR_M2M;
+	vfd->device_caps = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_OUTPUT_MPLANE |
+			   V4L2_CAP_META_CAPTURE;
+	vfd->ioctl_ops = &mtk_aie_v4l2_video_out_ioctl_ops;
+
+	strscpy(vfd->name, dev_driver_string(dev), sizeof(vfd->name));
+
+	video_set_drvdata(vfd, fd);
+
+	ret = video_register_device(vfd, VFL_TYPE_VIDEO, 0);
+	if (ret) {
+		dev_err(dev, "Failed to register video device\n");
+		goto err_free_dev;
+	}
+#ifdef CONFIG_MEDIA_CONTROLLER
+	ret = v4l2_m2m_register_media_controller(m2m_dev, vfd, MEDIA_ENT_F_PROC_VIDEO_STATISTICS);
+	if (ret) {
+		dev_err(dev, "Failed to init mem2mem media controller\n");
+		goto err_unreg_video;
+	}
+#endif
+	return 0;
+#ifdef CONFIG_MEDIA_CONTROLLER
+err_unreg_video:
+	video_unregister_device(vfd);
+#endif
+err_free_dev:
+	return ret;
+}
+
+static int mtk_aie_dev_v4l2_init(struct mtk_aie_dev *fd)
+{
+#ifdef CONFIG_MEDIA_CONTROLLER
+	struct media_device *mdev = &fd->mdev;
+#endif
+	struct device *dev = fd->dev;
+	int ret = -EINVAL;
+
+	ret = v4l2_device_register(dev, &fd->v4l2_dev);
+	if (ret) {
+		dev_err(dev, "Failed to register v4l2 device\n");
+		return ret;
+	}
+
+	fd->m2m_dev = v4l2_m2m_init(&fd_m2m_ops);
+	if (IS_ERR(fd->m2m_dev)) {
+		dev_err(dev, "Failed to init mem2mem device\n");
+		ret = PTR_ERR(fd->m2m_dev);
+		goto err_unreg_v4l2_dev;
+	}
+#ifdef CONFIG_MEDIA_CONTROLLER
+	mdev->dev = dev;
+	strscpy(mdev->model, dev_driver_string(dev), sizeof(mdev->model));
+	media_device_init(mdev);
+	mdev->ops = &fd_m2m_media_ops;
+	fd->v4l2_dev.mdev = mdev;
+#endif
+
+	ret = mtk_aie_video_device_register(fd);
+	if (ret)
+		goto err_cleanup_mdev;
+
+#ifdef CONFIG_MEDIA_CONTROLLER
+	ret = media_device_register(mdev);
+	if (ret) {
+		dev_err(dev, "Failed to register mem2mem media device\n");
+		goto err_unreg_vdev;
+	}
+#endif
+	return 0;
+
+#ifdef CONFIG_MEDIA_CONTROLLER
+err_unreg_vdev:
+	v4l2_m2m_unregister_media_controller(fd->m2m_dev);
+	video_unregister_device(&fd->vfd);
+#endif
+err_cleanup_mdev:
+#ifdef CONFIG_MEDIA_CONTROLLER
+	media_device_cleanup(mdev);
+#endif
+	v4l2_m2m_release(fd->m2m_dev);
+err_unreg_v4l2_dev:
+	v4l2_device_unregister(&fd->v4l2_dev);
+	return ret;
+}
+
+static void mtk_aie_video_device_unregister(struct mtk_aie_dev *fd)
+{
+#ifdef CONFIG_MEDIA_CONTROLLER
+	v4l2_m2m_unregister_media_controller(fd->m2m_dev);
+#endif
+	video_unregister_device(&fd->vfd);
+#ifdef CONFIG_MEDIA_CONTROLLER
+	media_device_cleanup(&fd->mdev);
+#endif
+	v4l2_m2m_release(fd->m2m_dev);
+	v4l2_device_unregister(&fd->v4l2_dev);
+}
+
+static void mtk_aie_frame_done_worker(struct work_struct *work)
+{
+	struct mtk_aie_req_work *req_work = (struct mtk_aie_req_work *)work;
+	struct mtk_aie_dev *fd = (struct mtk_aie_dev *)req_work->fd_dev;
+
+	if (fd->reg_cfg.fd_mode == FDMODE) {
+		fd->reg_cfg.hw_result = readl(fd->fd_base + AIE_RESULT_0_REG);
+		fd->reg_cfg.hw_result1 = readl(fd->fd_base + AIE_RESULT_1_REG);
+	}
+
+	mutex_lock(&fd->fd_lock);
+
+	switch (fd->aie_cfg->sel_mode) {
+	case FDMODE:
+		aie_get_fd_result(fd, fd->aie_cfg);
+		break;
+	case ATTRIBUTEMODE:
+		aie_get_attr_result(fd, fd->aie_cfg);
+		break;
+	case FLDMODE:
+		if (fd->variant->fld_enable)
+			aie_get_fld_result(fd, fd->aie_cfg);
+		break;
+	default:
+		dev_dbg(fd->dev, "Wrong sel_mode\n");
+		break;
+	}
+
+	mutex_unlock(&fd->fd_lock);
+
+#ifdef CONFIG_DEBUG_FS
+	if (fd->tr_info.trigger_dump[0] != '0') {
+		cancel_delayed_work(&fd->job_timeout_work);
+		aie_irqhandle(fd);
+		aie_reset(fd);
+		atomic_dec(&fd->num_composing);
+		mtk_aie_hw_job_finish(fd, VB2_BUF_STATE_ERROR);
+		wake_up(&fd->flushing_waitq);
+		aie_dumpinfo(fd);
+		fd->tr_info.trigger_dump[0] = '0';
+		return;
+	}
+#endif
+	if (!cancel_delayed_work(&fd->job_timeout_work))
+		return;
+
+	atomic_dec(&fd->num_composing);
+	mtk_aie_hw_job_finish(fd, VB2_BUF_STATE_DONE);
+	wake_up(&fd->flushing_waitq);
+}
+
+#ifdef CONFIG_DEBUG_FS
+static int aie_dbg_dump_open(struct inode *inode, struct file *file)
+{
+	file->private_data = inode->i_private;
+	return 0;
+}
+
+static ssize_t aie_dbg_dump_read(struct file *file, char __user *buf,
+				 size_t len, loff_t *ppos)
+{
+	struct mtk_aie_dev *fd = file->private_data;
+	ssize_t ret_size = 0;
+
+	ret_size = simple_read_from_buffer(buf, len, ppos,
+					   fd->tr_info.dump_buffer,
+					   fd->tr_info.dump_offset);
+
+	return ret_size;
+}
+
+static const struct file_operations aie_debug_dump_fops = {
+	.owner = THIS_MODULE,
+	.open = aie_dbg_dump_open,
+	.read = aie_dbg_dump_read,
+};
+
+static int aie_dbg_trigger_open(struct inode *inode, struct file *file)
+{
+	file->private_data = inode->i_private;
+	return 0;
+}
+
+static ssize_t aie_dbg_trigger_write(struct file *file, const char __user *buf,
+				     size_t len, loff_t *ppos)
+{
+	struct mtk_aie_dev *fd = file->private_data;
+	size_t ret = -EINVAL;
+
+	if (*ppos > sizeof(fd->tr_info.trigger_dump))
+		return -EINVAL;
+
+	ret = simple_write_to_buffer(fd->tr_info.trigger_dump,
+				     sizeof(fd->tr_info.trigger_dump), ppos,
+				     buf, len);
+
+	return ret;
+}
+
+static ssize_t aie_dbg_trigger_read(struct file *file, char __user *buf,
+				    size_t len, loff_t *ppos)
+{
+	struct mtk_aie_dev *fd = file->private_data;
+
+	return simple_read_from_buffer(buf, len, ppos,
+				       fd->tr_info.trigger_dump,
+				       sizeof(fd->tr_info.trigger_dump));
+}
+
+static const struct file_operations aie_debug_trigger_fops = {
+	.owner = THIS_MODULE,
+	.open = aie_dbg_trigger_open,
+	.write = aie_dbg_trigger_write,
+	.read = aie_dbg_trigger_read,
+};
+
+static struct dentry *aie_debugfs_dump, *aie_debugfs_trigger,
+	*aie_debugfs_srcdata, *aie_debugfs_dstdata, *aie_debugfs_root;
+#endif
+
+#ifdef CONFIG_DEBUG_FS
+static int mtk_aie_debugfs_init(struct mtk_aie_dev *fd)
+{
+	aie_debugfs_root = debugfs_create_dir("mtk_aie", NULL);
+
+	/* No need to check return value - debugfs is designed to gracefully handle errors */
+	aie_debugfs_dump = debugfs_create_file("dumpinfo", 0444,
+					       aie_debugfs_root, fd,
+					       &aie_debug_dump_fops);
+
+	aie_debugfs_trigger = debugfs_create_file("trigger_dump", 0444,
+						  aie_debugfs_root, fd,
+						  &aie_debug_trigger_fops);
+
+	srcdata.data = (void *)srcrawdata;
+	srcdata.size = AIE_DEBUG_RAW_DATA_SIZE;
+
+	aie_debugfs_srcdata = debugfs_create_blob("srcdata.raw", 0444,
+						  aie_debugfs_root, &srcdata);
+
+	dstdata.data = (void *)dstrawdata;
+	dstdata.size = AIE_DEBUG_RAW_DATA_SIZE;
+
+	aie_debugfs_dstdata = debugfs_create_blob("dstdata.raw", 0444,
+						  aie_debugfs_root, &dstdata);
+
+	fd->tr_info.dump_size = AIE_DUMP_BUFFER_SIZE;
+	fd->tr_info.dump_buffer =
+		kmalloc(fd->tr_info.dump_size, GFP_KERNEL);
+	if (!fd->tr_info.dump_buffer)
+		return -ENOMEM;
+
+	fd->tr_info.trigger_dump[0] = '0';
+	fd->tr_info.trigger_dump[1] = '\n';
+	fd->tr_info.trigger_dump[2] = '\0';
+
+	return 0;
+}
+
+static void mtk_aie_debugfs_free(struct platform_device *pdev)
+{
+	struct mtk_aie_dev *fd = dev_get_drvdata(&pdev->dev);
+
+	kfree(fd->tr_info.dump_buffer);
+	fd->tr_info.dump_buffer = NULL;
+
+	debugfs_remove_recursive(aie_debugfs_root);
+	aie_debugfs_root = NULL;
+}
+#else
+static int mtk_aie_debugfs_init(struct mtk_aie_dev *fd)
+{
+	return 0;
+}
+
+static void mtk_aie_debugfs_free(struct platform_device *pdev)
+{
+}
+#endif
+
+static int mtk_aie_resource_init(struct mtk_aie_dev *fd)
+{
+	int ret = 0;
+
+	mutex_init(&fd->vfd_lock);
+	mutex_init(&fd->dev_lock);
+	mutex_init(&fd->fd_lock);
+
+	init_completion(&fd->fd_job_finished);
+	complete_all(&fd->fd_job_finished);
+	INIT_DELAYED_WORK(&fd->job_timeout_work, mtk_aie_job_timeout_work);
+	init_waitqueue_head(&fd->flushing_waitq);
+	atomic_set(&fd->num_composing, 0);
+	fd->fd_stream_count = 0;
+
+	fd->frame_done_wq = alloc_ordered_workqueue(dev_name(fd->dev),
+						    WQ_HIGHPRI | WQ_FREEZABLE
+				);
+	if (!fd->frame_done_wq) {
+		dev_err(fd->dev, "failed to alloc frame_done workqueue\n");
+		mutex_destroy(&fd->vfd_lock);
+		mutex_destroy(&fd->dev_lock);
+		mutex_destroy(&fd->fd_lock);
+		return -ENOMEM;
+	}
+
+	INIT_WORK(&fd->req_work.work, mtk_aie_frame_done_worker);
+	fd->req_work.fd_dev = fd;
+
+#ifdef CONFIG_INTERCONNECT_MTK_EXTENSION
+	ret = mtk_aie_mmdvfs_init(fd);
+	if (ret) {
+		dev_err(fd->dev, "failed to init mmdvfs: %d\n", ret);
+		goto err_destroy_wq;
+	}
+	ret = mtk_aie_mmqos_init(fd);
+	if (ret) {
+		dev_err(fd->dev, "failed to init mmqos: %d\n", ret);
+		goto err_uninit_mmdvfs;
+	}
+#endif
+	return 0;
+
+#ifdef CONFIG_INTERCONNECT_MTK_EXTENSION
+err_uninit_mmdvfs:
+	mtk_aie_mmdvfs_uninit(fd);
+err_destroy_wq:
+#endif
+	destroy_workqueue(fd->frame_done_wq);
+	fd->frame_done_wq = NULL;
+	mutex_destroy(&fd->vfd_lock);
+	mutex_destroy(&fd->dev_lock);
+	mutex_destroy(&fd->fd_lock);
+	return ret;
+}
+
+static void mtk_aie_resource_free(struct platform_device *pdev)
+{
+	struct mtk_aie_dev *fd = dev_get_drvdata(&pdev->dev);
+
+#ifdef CONFIG_INTERCONNECT_MTK_EXTENSION
+	mtk_aie_mmdvfs_uninit(fd);
+	mtk_aie_mmqos_uninit(fd);
+#endif
+	if (fd->frame_done_wq)
+		destroy_workqueue(fd->frame_done_wq);
+	fd->frame_done_wq = NULL;
+	mutex_destroy(&fd->vfd_lock);
+	mutex_destroy(&fd->dev_lock);
+	mutex_destroy(&fd->fd_lock);
+}
+
+static irqreturn_t mtk_aie_irq(int irq, void *data)
+{
+	struct mtk_aie_dev *fd = (struct mtk_aie_dev *)data;
+	u32 status;
+
+	status = readl(fd->fd_base + AIE_INT_REG);
+	if (!status)
+		return IRQ_NONE;
+
+	aie_irqhandle(fd);
+
+	queue_work(fd->frame_done_wq, &fd->req_work.work);
+
+	return IRQ_HANDLED;
+}
+
+static int mtk_aie_probe(struct platform_device *pdev)
+{
+	struct mtk_aie_dev *fd = NULL;
+	struct device *dev = &pdev->dev;
+
+	int irq = -1;
+	int ret = -EINVAL;
+
+	fd = devm_kzalloc(&pdev->dev, sizeof(struct mtk_aie_dev), GFP_KERNEL);
+	if (!fd)
+		return -ENOMEM;
+
+	fd->variant = mtk_aie_get_variant(dev);
+	if (!fd->variant)
+		return -ENODEV;
+
+	ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(34));
+	if (ret) {
+		dev_err(dev, "%s: No suitable DMA available\n", __func__);
+		return ret;
+	}
+
+	dev_set_drvdata(dev, fd);
+	fd->dev = dev;
+
+	irq = platform_get_irq(pdev, 0);
+	if (irq < 0) {
+		dev_err(dev, "Failed to get irq by platform: %d\n", irq);
+		return irq;
+	}
+
+	ret = devm_request_irq(dev,
+			       irq,
+			       mtk_aie_irq,
+			       IRQF_SHARED,
+			       dev_driver_string(dev),
+			       fd
+		);
+	if (ret) {
+		dev_err(dev, "Failed to request irq\n");
+		return ret;
+	}
+	fd->irq = irq;
+
+	fd->fd_base = devm_platform_ioremap_resource(pdev, 0);
+	if (IS_ERR(fd->fd_base)) {
+		dev_err(dev, "Failed to get fd reg base\n");
+		return PTR_ERR(fd->fd_base);
+	}
+
+	fd->aie_clk.clk_num = ARRAY_SIZE(aie_clks);
+	fd->aie_clk.clks = aie_clks;
+	ret = devm_clk_bulk_get(&pdev->dev, fd->aie_clk.clk_num, fd->aie_clk.clks);
+	if (ret) {
+		dev_err(dev, "failed to get raw clock:%d\n", ret);
+		return ret;
+	}
+
+	ret = mtk_aie_debugfs_init(fd);
+	if (ret)
+		return ret;
+
+	ret = mtk_aie_resource_init(fd);
+	if (ret)
+		goto err_debugfs_free;
+
+	pm_runtime_enable(dev);
+
+	ret = mtk_aie_dev_v4l2_init(fd);
+	if (ret)
+		goto err_pm_disable;
+
+	dev_info(dev, "AIE : Success to %s\n", __func__);
+
+	return 0;
+
+err_pm_disable:
+	pm_runtime_disable(&pdev->dev);
+	mtk_aie_resource_free(pdev);
+err_debugfs_free:
+	mtk_aie_debugfs_free(pdev);
+
+	return ret;
+}
+
+static void mtk_aie_remove(struct platform_device *pdev)
+{
+	struct mtk_aie_dev *fd = dev_get_drvdata(&pdev->dev);
+
+	disable_irq(fd->irq);
+	cancel_delayed_work_sync(&fd->job_timeout_work);
+	if (fd->frame_done_wq)
+		drain_workqueue(fd->frame_done_wq);
+	mtk_aie_video_device_unregister(fd);
+	pm_runtime_disable(&pdev->dev);
+	mtk_aie_debugfs_free(pdev);
+	mtk_aie_resource_free(pdev);
+}
+
+static int mtk_aie_suspend(struct device *dev)
+{
+	struct mtk_aie_dev *fd = dev_get_drvdata(dev);
+	int ret = -EINVAL, num = 0;
+
+	if (pm_runtime_suspended(dev))
+		return 0;
+
+	num = atomic_read(&fd->num_composing);
+	dev_info(dev, "%s: suspend aie job start, num(%d)\n", __func__, num);
+
+	ret = wait_event_timeout(fd->flushing_waitq,
+				 !(num = atomic_read(&fd->num_composing)),
+				 msecs_to_jiffies(MTK_FD_HW_TIMEOUT_IN_MSEC)
+		);
+	if (!ret && num) {
+		dev_dbg(dev,
+			"%s: flushing aie job timeout num %d\n",
+			__func__,
+			num
+		);
+
+		return -EBUSY;
+	}
+
+	dev_info(dev, "%s: suspend aie job end num(%d)\n", __func__, num);
+
+	ret = pm_runtime_force_suspend(dev);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+static int mtk_aie_resume(struct device *dev)
+{
+	int ret = -EINVAL;
+
+	dev_info(dev, "%s: resume aie job start)\n", __func__);
+
+	if (pm_runtime_suspended(dev)) {
+		dev_info(dev,
+			 "%s: pm_runtime_suspended is true, no action\n",
+			 __func__
+		);
+		return 0;
+	}
+
+	ret = pm_runtime_force_resume(dev);
+	if (ret)
+		return ret;
+
+	dev_info(dev, "%s: resume aie job end)\n", __func__);
+	return 0;
+}
+
+static int mtk_aie_runtime_suspend(struct device *dev)
+{
+	struct mtk_aie_dev *fd = dev_get_drvdata(dev);
+
+	clk_bulk_disable_unprepare(fd->aie_clk.clk_num, fd->aie_clk.clks);
+
+	return 0;
+}
+
+static int mtk_aie_runtime_resume(struct device *dev)
+{
+	struct mtk_aie_dev *fd = dev_get_drvdata(dev);
+	int ret = -EINVAL;
+
+	ret = clk_bulk_prepare_enable(fd->aie_clk.clk_num, fd->aie_clk.clks);
+	if (ret) {
+		dev_err(dev, "failed to enable clock:%d\n", ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static const struct dev_pm_ops mtk_aie_pm_ops = {
+	SYSTEM_SLEEP_PM_OPS(mtk_aie_suspend, mtk_aie_resume)
+	RUNTIME_PM_OPS(mtk_aie_runtime_suspend, mtk_aie_runtime_resume, NULL)
+};
+
+static const struct mtk_aie_variant aie_31_drvdata = {
+	.hw_version = 31,
+	.fld_enable = 1,
+	.y2r_cfg_size = 34,
+	.rs_cfg_size = 30,
+	.fd_cfg_size = 56,
+};
+
+static const struct of_device_id mtk_aie_of_ids[] = {
+	{
+		.compatible = "mediatek,mt8188-aie",
+		.data = &aie_31_drvdata,
+	},
+	{ /* end of list */ },
+};
+MODULE_DEVICE_TABLE(of, mtk_aie_of_ids);
+
+static const struct mtk_aie_variant *mtk_aie_get_variant(struct device *dev)
+{
+	const struct of_device_id *match;
+
+	match = of_match_node(mtk_aie_of_ids, dev->of_node);
+	if (match)
+		return (const struct mtk_aie_variant *)match->data;
+
+	return &aie_31_drvdata;
+}
+
+static struct platform_driver mtk_aie_driver = {
+	.probe = mtk_aie_probe,
+	.remove = mtk_aie_remove,
+	.driver = {
+			.name = "mtk-aie",
+			.of_match_table = mtk_aie_of_ids,
+			.pm = pm_ptr(&mtk_aie_pm_ops),
+	}
+};
+
+module_platform_driver(mtk_aie_driver);
+MODULE_AUTHOR("Fish Wu <fish.wu@mediatek.com>");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Mediatek AIE driver");



^ permalink raw reply related

* [PATCH v6 3/4] media: uapi: mediatek: Add MT8188 AIE control definitions
From: Sarang Chaudhari @ 2026-06-05  8:29 UTC (permalink / raw)
  To: Rob Herring, AngeloGioacchino Del Regno, Mauro Carvalho Chehab,
	linux-kernel, linux-arm-kernel, linux-mediatek
  Cc: zhaoyuan.chen, Teddy.Chen, Project_Global_Chrome_Upstream_Group,
	Sarang Chaudhari

Add AIE (AI Engine) UAPI control definitions and register the
V4L2_META_FMT_MTFD_RESULT metadata format for the MediaTek face
detection hardware accelerator.

This patch adds:
- include/uapi/linux/mtk_aie_v4l2_controls.h: Custom V4L2 control IDs
  for AIE initialization and per-frame parameters.
- V4L2_META_FMT_MTFD_RESULT format in videodev2.h for face detection
  result metadata output.
- Format description in v4l2-ioctl.c.

Signed-off-by: Sarang Chaudhari <sarang.chaudhari@mediatek.com>
---
Changes in v6:
- Simplify UAPI header to contain only control ID definitions. Full
  structures kept in kernel-internal header for now, pending UAPI
  structure redesign per CK Hu's feedback.
- Drop V4L2_CTRL_TYPE_AIE_INIT and V4L2_CTRL_TYPE_AIE_PARAM from
  v4l2_ctrl_type enum (use V4L2_CTRL_TYPE_U32 compound control instead).
- Address CK Hu's review feedback: remove freq_level, improve
  feature_threshold docs, clarify pyramid multi-scale detection, clarify
  FLD mode uses FD results via Binary Tree Traversal.

Changes in v5:
- Add an introduction for feature_threshold.
- Rename v4l2_aie_roi to aie_roi_coordinate.
- Rename v4l2_aie_padding to aie_padding_size.
- Explain en_padding and the three modes of fd_mode.
- Move structures from mtk_aie.h to the uapi directory.

Changes in v4:
- Document the detail of V4L2_META_FMT_MTFD_RESULT.
- Add the introduction of related variables.

Changes in v3: None

Changes in v2:
- Fix coding style.

 drivers/media/v4l2-core/v4l2-ioctl.c       |  1 +
 include/uapi/linux/mtk_aie_v4l2_controls.h | 23 ++++++++++++++++++++++
 include/uapi/linux/videodev2.h             |  1 +
 3 files changed, 25 insertions(+)
 create mode 100644 include/uapi/linux/mtk_aie_v4l2_controls.h

diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c
index e50e517..8754098 100644
--- a/drivers/media/v4l2-core/v4l2-ioctl.c
+++ b/drivers/media/v4l2-core/v4l2-ioctl.c
@@ -11,6 +11,7 @@ static void v4l_fill_fmtdesc(struct v4l2_fmtdesc *fmt)
 	case V4L2_META_FMT_GENERIC_CSI2_16:	descr = "8-bit Generic Meta, 16b CSI-2"; break;
 	case V4L2_META_FMT_GENERIC_CSI2_20:	descr = "8-bit Generic Meta, 20b CSI-2"; break;
 	case V4L2_META_FMT_GENERIC_CSI2_24:	descr = "8-bit Generic Meta, 24b CSI-2"; break;
+	case V4L2_META_FMT_MTFD_RESULT:	descr = "Mediatek Face Detect Result"; break;
 
 	default:
 		/* Compressed formats */
diff --git a/include/uapi/linux/mtk_aie_v4l2_controls.h b/include/uapi/linux/mtk_aie_v4l2_controls.h
new file mode 100644
index 0000000..a8b2927
--- /dev/null
+++ b/include/uapi/linux/mtk_aie_v4l2_controls.h
@@ -0,0 +1,23 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+/*
+ * MediaTek AI Engine (AIE) V4L2 control definitions
+ *
+ * Copyright (c) 2020 MediaTek Inc.
+ * Author: Fish Wu <fish.wu@mediatek.com>
+ */
+
+#ifndef __UAPI_MTK_AIE_V4L2_CONTROLS_H__
+#define __UAPI_MTK_AIE_V4L2_CONTROLS_H__
+
+#include <linux/videodev2.h>
+
+/*
+ * The base for the MediaTek AIE driver controls.
+ * We reserve 16 controls for this driver.
+ */
+#define V4L2_CID_USER_MTK_FD_BASE	(V4L2_CID_USER_BASE + 0x1fd0)
+
+#define V4L2_CID_MTK_AIE_INIT		(V4L2_CID_USER_MTK_FD_BASE + 1)
+#define V4L2_CID_MTK_AIE_PARAM		(V4L2_CID_USER_MTK_FD_BASE + 2)
+
+#endif /* __UAPI_MTK_AIE_V4L2_CONTROLS_H__ */
diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h
index 7668201..6d6866d 100644
--- a/include/uapi/linux/videodev2.h
+++ b/include/uapi/linux/videodev2.h
@@ -10,6 +10,7 @@ struct v4l2_pix_format {
 #define V4L2_META_FMT_GENERIC_CSI2_16	v4l2_fourcc('M', 'C', '1', 'G') /* 16-bit CSI-2 packed 8-bit metadata */
 #define V4L2_META_FMT_GENERIC_CSI2_20	v4l2_fourcc('M', 'C', '1', 'K') /* 20-bit CSI-2 packed 8-bit metadata */
 #define V4L2_META_FMT_GENERIC_CSI2_24	v4l2_fourcc('M', 'C', '1', 'O') /* 24-bit CSI-2 packed 8-bit metadata */
+#define V4L2_META_FMT_MTFD_RESULT	v4l2_fourcc('M', 'T', 'f', 'd') /* Mediatek face detection result */
 #endif
 
 /* priv field value to indicates that subsequent fields are valid. */
-- 
2.45.2



^ permalink raw reply related

* [PATCH v6 2/4] arm64: dts: mediatek: mt8188: Add AIE face detection node
From: Sarang Chaudhari @ 2026-06-05  8:29 UTC (permalink / raw)
  To: Rob Herring, AngeloGioacchino Del Regno, Mauro Carvalho Chehab,
	linux-kernel, linux-arm-kernel, linux-mediatek
  Cc: zhaoyuan.chen, Teddy.Chen, Project_Global_Chrome_Upstream_Group,
	Sarang Chaudhari

Add the AI Engine (AIE) device tree node for the MT8188 SoC. The AIE
hardware provides face detection, facial landmark detection, and face
attribute analysis capabilities.

Signed-off-by: Sarang Chaudhari <sarang.chaudhari@mediatek.com>
---
Changes in v6:
- Remove iommus and mediatek,larb properties (made optional).
- Remove larb12 node (not required when IOMMU is not used).
- Update IRQ number to correct hardware value.

Changes in v5:
- Modify the name of clock, change _ to -.

Changes in v4: None

Changes in v3:
- Remove dts non-MMIO nodes.

Changes in v2:
- Add AIE node and related node.

 arch/arm64/boot/dts/mediatek/mt8188.dtsi | 13 +++++++++++++
 1 file changed, 13 insertions(+)

diff --git a/arch/arm64/boot/dts/mediatek/mt8188.dtsi b/arch/arm64/boot/dts/mediatek/mt8188.dtsi
index ee833c3..29d11d8 100644
--- a/arch/arm64/boot/dts/mediatek/mt8188.dtsi
+++ b/arch/arm64/boot/dts/mediatek/mt8188.dtsi
@@ -12,6 +12,19 @@
 			#reset-cells = <1>;
 		};
 
+		aie@15310000 {
+			compatible = "mediatek,mt8188-aie";
+			reg = <0 0x15310000 0 0x1000>;
+			interrupts = <GIC_SPI 600 IRQ_TYPE_LEVEL_HIGH 0>;
+			clocks = <&imgsys CLK_IMGSYS_MAIN_IPE>,
+				 <&ipesys CLK_IPE_FDVT>,
+				 <&ipesys CLK_IPE_SMI_LARB12>,
+				 <&ipesys CLK_IPESYS_TOP>;
+			clock-names = "img-ipe", "ipe-fdvt",
+				      "ipe-smi-larb12", "ipe-top";
+			power-domains = <&spm MT8188_POWER_DOMAIN_IPE>;
+		};
+
 		ipesys: clock-controller@15330000 {
 			compatible = "mediatek,mt8188-ipesys";
 			reg = <0 0x15330000 0 0x1000>;
-- 
2.45.2



^ permalink raw reply related

* [PATCH v6 1/4] media: dt-bindings: mediatek: Add AIE face detection support for MT8188
From: Sarang Chaudhari @ 2026-06-05  8:28 UTC (permalink / raw)
  To: Rob Herring, AngeloGioacchino Del Regno, Mauro Carvalho Chehab,
	linux-kernel, linux-arm-kernel, linux-mediatek
  Cc: zhaoyuan.chen, Teddy.Chen, Project_Global_Chrome_Upstream_Group,
	Sarang Chaudhari

Add YAML device tree bindings for the MediaTek AI Engine (AIE) hardware
accelerator found in MT8188 SoCs. The AIE provides hardware-accelerated
face detection, facial landmark detection, and face attribute analysis
capabilities.

Add a MAINTAINERS entry covering the binding, the UAPI header and the
driver directory.

Signed-off-by: Sarang Chaudhari <sarang.chaudhari@mediatek.com>
---
Changes in v6:
- Add ipe-smi-larb12 clock to the binding (was missing in v5 binding
  but present in v5 dtsi).
- Remove iommus from required properties (made optional for platforms
  that can operate without IOMMU).
- Add mediatek,larb as an optional property.
- Improve description text.

Changes in v5:
- Modify the description to make it more concise.
- Delete the description of reg.
- Modify the description of iommus and delete the maxItems of iommus.
- Delete all mediatek,larb.
- Modify the name of clock, change _ to -.

Changes in v4:
- Remove address-cells and size-cells.
- Remove larb12 related content.
- Update id content.

Changes in v3: None

Changes in v2:
- Fix coding style.

 .../bindings/media/mediatek,mt8188-aie.yaml   | 85 +++++++++++++++++++
 MAINTAINERS                                   | 10 +++
 2 files changed, 95 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/media/mediatek,mt8188-aie.yaml

diff --git a/Documentation/devicetree/bindings/media/mediatek,mt8188-aie.yaml b/Documentation/devicetree/bindings/media/mediatek,mt8188-aie.yaml
new file mode 100644
index 0000000..ab888f0
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/mediatek,mt8188-aie.yaml
@@ -0,0 +1,85 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/media/mediatek,mt8188-aie.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: MediaTek AI Engine (AIE) for Face Detection
+
+maintainers:
+  - Fish Wu <fish.wu@mediatek.com>
+  - Bo Kong <bo.kong@mediatek.com>
+
+description: |
+  The MediaTek AI Engine (AIE) provides hardware-accelerated face detection,
+  facial landmark detection, and face attribute analysis. It is found in the
+  IPE (Image Processing Engine) subsystem of MediaTek SoCs.
+
+properties:
+  compatible:
+    enum:
+      - mediatek,mt8188-aie
+
+  reg:
+    maxItems: 1
+
+  interrupts:
+    maxItems: 1
+
+  clocks:
+    items:
+      - description: clock for imgsys main ipe
+      - description: clock for ipe fdvt
+      - description: clock for ipe smi larb12
+      - description: clock for ipe top
+
+  clock-names:
+    items:
+      - const: img-ipe
+      - const: ipe-fdvt
+      - const: ipe-smi-larb12
+      - const: ipe-top
+
+  power-domains:
+    maxItems: 1
+
+  iommus:
+    maxItems: 1
+
+  mediatek,larb:
+    $ref: /schemas/types.yaml#/definitions/phandle
+    description: phandle to the local arbiter (LARB) node
+
+required:
+  - compatible
+  - reg
+  - interrupts
+  - clocks
+  - clock-names
+  - power-domains
+
+additionalProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/interrupt-controller/arm-gic.h>
+    #include <dt-bindings/clock/mediatek,mt8188-clk.h>
+    #include <dt-bindings/power/mediatek,mt8188-power.h>
+
+    soc {
+        #address-cells = <2>;
+        #size-cells = <2>;
+
+        aie@15310000 {
+            compatible = "mediatek,mt8188-aie";
+            reg = <0 0x15310000 0 0x1000>;
+            interrupts = <GIC_SPI 600 IRQ_TYPE_LEVEL_HIGH 0>;
+            clocks = <&imgsys CLK_IMGSYS_MAIN_IPE>,
+                     <&ipesys CLK_IPE_FDVT>,
+                     <&ipesys CLK_IPE_SMI_LARB12>,
+                     <&ipesys CLK_IPESYS_TOP>;
+            clock-names = "img-ipe", "ipe-fdvt",
+                          "ipe-smi-larb12", "ipe-top";
+            power-domains = <&spm MT8188_POWER_DOMAIN_IPE>;
+        };
+    };
diff --git a/MAINTAINERS b/MAINTAINERS
index afb7487..fa631d6 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -3,6 +3,16 @@ M:	Felix Fietkau <nbd@nbd.name>
 S:	Maintained
 F:	drivers/net/ethernet/mediatek/
 
+MEDIATEK MT8188 AIE DRIVER
+M:	Fish Wu <fish.wu@mediatek.com>
+M:	Bo Kong <bo.kong@mediatek.com>
+L:	linux-media@vger.kernel.org
+L:	linux-mediatek@lists.infradead.org (moderated for non-subscribers)
+S:	Maintained
+F:	Documentation/devicetree/bindings/media/mediatek,mt8188-aie.yaml
+F:	drivers/media/platform/mediatek/aie/
+F:	include/uapi/linux/mtk_aie_v4l2_controls.h
+
 MEDIATEK MDP DRIVER
 M:	Minghsiu Tsai <minghsiu.tsai@mediatek.com>
 S:	Supported
-- 
2.45.2



^ permalink raw reply related

* [PATCH v6 0/4] Add MT8188 AIE driver
From: Sarang Chaudhari @ 2026-06-05  8:20 UTC (permalink / raw)
  To: Rob Herring, AngeloGioacchino Del Regno, Mauro Carvalho Chehab,
	linux-kernel, linux-arm-kernel, linux-mediatek
  Cc: zhaoyuan.chen, Teddy.Chen, Project_Global_Chrome_Upstream_Group,
	Sarang Chaudhari

AIE (AI Engine) is one of the units in MT8188 ISP which provides
hardware-accelerated face detection function. It can detect different
sizes of faces in a raw image using pyramid-based multi-scale detection.

The AIE supports three operation modes:
- Face Detection (FD): Multi-scale face detection using 3-level pyramid
  (640x480 base, 2x downscale per level).
- Attribute Analysis: Age, gender, and race classification.
- Facial Landmark Detection (FLD): 11-point landmark localization using
  Binary Tree Traversal on FD results.

Changes in v6:
- DT binding: Add ipe-smi-larb12 clock, remove iommus from required
  properties, add mediatek,larb as optional, improve description.
- DTS: Remove iommus and mediatek,larb properties, fix IRQ number.
- UAPI: Simplify header to control ID definitions only, drop custom
  V4L2_CTRL_TYPE (use V4L2_CTRL_TYPE_U32 compound controls instead).
- Driver: Fix NULL pointer check inversion, fix resource leaks on error
  path, remove debugfs return value checks, update clock names to hyphen
  convention, remove freq_level per CK Hu's feedback.

Changes in v5:
- DT binding: Use hyphens in clock names, remove mediatek,larb.
- UAPI: Add feature_threshold docs, rename structures, move structures
  from mtk_aie.h to uapi header.
- Driver: Update clock names, improve error handling in probe.

Changes in v4:
- DT binding: Remove address-cells/size-cells, remove larb12 content.
- UAPI: Add V4L2_META_FMT_MTFD_RESULT documentation.
- Driver: Remove larb12 related content.

Changes in v3:
- DTS: Remove non-MMIO nodes.

Changes in v2:
- Fix coding style issues throughout.

Sarang Chaudhari (4):
  media: dt-bindings: mediatek: Add AIE face detection support for
    MT8188
  arm64: dts: mediatek: mt8188: Add AIE face detection node
  media: uapi: mediatek: Add MT8188 AIE control definitions
  media: platform: mediatek: Add MT8188 AIE driver

 .../bindings/media/mediatek,mt8188-aie.yaml   |   85 +
 MAINTAINERS                                   |   10 +
 arch/arm64/boot/dts/mediatek/mt8188.dtsi      |   13 +
 drivers/media/platform/mediatek/Kconfig       |    1 +
 drivers/media/platform/mediatek/Makefile      |    1 +
 drivers/media/platform/mediatek/aie/Kconfig   |   20 +
 drivers/media/platform/mediatek/aie/Makefile  |    5 +
 drivers/media/platform/mediatek/aie/mtk_aie.h | 1045 +++++++++++
 .../platform/mediatek/aie/mtk_aie_drv.c       | 3667 +++++++++++++++++
 .../platform/mediatek/aie/mtk_aie_v4l2.c      | 1907 +++++++++
 drivers/media/v4l2-core/v4l2-ioctl.c          |    1 +
 include/uapi/linux/mtk_aie_v4l2_controls.h    |   23 +
 include/uapi/linux/videodev2.h                |    1 +
 13 files changed, 6779 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/media/mediatek,mt8188-aie.yaml
 create mode 100644 drivers/media/platform/mediatek/aie/Kconfig
 create mode 100644 drivers/media/platform/mediatek/aie/Makefile
 create mode 100644 drivers/media/platform/mediatek/aie/mtk_aie.h
 create mode 100644 drivers/media/platform/mediatek/aie/mtk_aie_drv.c
 create mode 100644 drivers/media/platform/mediatek/aie/mtk_aie_v4l2.c
 create mode 100644 include/uapi/linux/mtk_aie_v4l2_controls.h

-- 
2.45.2



^ permalink raw reply

* [PATCH] pinctrl: Move Airoha driver to dedicated directory
From: Christian Marangi @ 2026-06-05  7:12 UTC (permalink / raw)
  To: Linus Walleij, Lorenzo Bianconi, Sean Wang, Matthias Brugger,
	AngeloGioacchino Del Regno, Christian Marangi, linux-kernel,
	linux-gpio, linux-mediatek, linux-arm-kernel

In preparation for additional SoC support, move the Airoha pinctrl driver
for AN7581 SoC to a dedicated directory.

This is to tidy things up and keep code organized without polluting the
Mediatek driver directory.

The driver doesn't depend on any generic or common code from the Mediatek
codebase so it can be safely moved without any modification.

Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
---
 MAINTAINERS                                   |  2 +-
 drivers/pinctrl/Kconfig                       |  1 +
 drivers/pinctrl/Makefile                      |  1 +
 drivers/pinctrl/airoha/Kconfig                | 20 +++++++++++++++++++
 drivers/pinctrl/airoha/Makefile               |  3 +++
 .../{mediatek => airoha}/pinctrl-airoha.c     |  0
 drivers/pinctrl/mediatek/Kconfig              | 17 +---------------
 drivers/pinctrl/mediatek/Makefile             |  1 -
 8 files changed, 27 insertions(+), 18 deletions(-)
 create mode 100644 drivers/pinctrl/airoha/Kconfig
 create mode 100644 drivers/pinctrl/airoha/Makefile
 rename drivers/pinctrl/{mediatek => airoha}/pinctrl-airoha.c (100%)

diff --git a/MAINTAINERS b/MAINTAINERS
index 21c0ef0b9ce5..38bf92149a15 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -21024,7 +21024,7 @@ M:	Lorenzo Bianconi <lorenzo@kernel.org>
 L:	linux-mediatek@lists.infradead.org (moderated for non-subscribers)
 S:	Maintained
 F:	Documentation/devicetree/bindings/pinctrl/airoha,en7581-pinctrl.yaml
-F:	drivers/pinctrl/mediatek/pinctrl-airoha.c
+F:	drivers/pinctrl/airoha/pinctrl-airoha.c
 
 PIN CONTROLLER - AMD
 M:	Basavaraj Natikar <Basavaraj.Natikar@amd.com>
diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig
index 03f2e3ee065f..e0babad31445 100644
--- a/drivers/pinctrl/Kconfig
+++ b/drivers/pinctrl/Kconfig
@@ -679,6 +679,7 @@ config PINCTRL_RP1
 	  multi function device.
 
 source "drivers/pinctrl/actions/Kconfig"
+source "drivers/pinctrl/airoha/Kconfig"
 source "drivers/pinctrl/aspeed/Kconfig"
 source "drivers/pinctrl/bcm/Kconfig"
 source "drivers/pinctrl/berlin/Kconfig"
diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile
index f7d5d5f76d0c..36c55858801f 100644
--- a/drivers/pinctrl/Makefile
+++ b/drivers/pinctrl/Makefile
@@ -66,6 +66,7 @@ obj-$(CONFIG_PINCTRL_ZYNQMP)	+= pinctrl-zynqmp.o
 obj-$(CONFIG_PINCTRL_ZYNQ)	+= pinctrl-zynq.o
 
 obj-y				+= actions/
+obj-y				+= airoha/
 obj-$(CONFIG_ARCH_ASPEED)	+= aspeed/
 obj-y				+= bcm/
 obj-$(CONFIG_PINCTRL_BERLIN)	+= berlin/
diff --git a/drivers/pinctrl/airoha/Kconfig b/drivers/pinctrl/airoha/Kconfig
new file mode 100644
index 000000000000..03adaeae8fc3
--- /dev/null
+++ b/drivers/pinctrl/airoha/Kconfig
@@ -0,0 +1,20 @@
+# SPDX-License-Identifier: GPL-2.0-only
+menu "Airoha pinctrl drivers"
+	depends on ARCH_AIROHA || COMPILE_TEST
+
+config PINCTRL_AIROHA
+	tristate "Airoha EN7581 pin control"
+	depends on OF
+	depends on ARM64 || COMPILE_TEST
+	select PINMUX
+	select GENERIC_PINCONF
+	select GENERIC_PINCTRL_GROUPS
+	select GENERIC_PINMUX_FUNCTIONS
+	select GPIOLIB
+	select GPIOLIB_IRQCHIP
+	select REGMAP_MMIO
+	help
+	  Say yes here to support pin controller and gpio driver
+	  on Airoha EN7581 SoC.
+
+endmenu
diff --git a/drivers/pinctrl/airoha/Makefile b/drivers/pinctrl/airoha/Makefile
new file mode 100644
index 000000000000..a25b744dd7a8
--- /dev/null
+++ b/drivers/pinctrl/airoha/Makefile
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0
+
+obj-$(CONFIG_PINCTRL_AIROHA)		+= pinctrl-airoha.o
diff --git a/drivers/pinctrl/mediatek/pinctrl-airoha.c b/drivers/pinctrl/airoha/pinctrl-airoha.c
similarity index 100%
rename from drivers/pinctrl/mediatek/pinctrl-airoha.c
rename to drivers/pinctrl/airoha/pinctrl-airoha.c
diff --git a/drivers/pinctrl/mediatek/Kconfig b/drivers/pinctrl/mediatek/Kconfig
index 4819617d9368..97980cc28b9c 100644
--- a/drivers/pinctrl/mediatek/Kconfig
+++ b/drivers/pinctrl/mediatek/Kconfig
@@ -1,6 +1,6 @@
 # SPDX-License-Identifier: GPL-2.0-only
 menu "MediaTek pinctrl drivers"
-	depends on ARCH_MEDIATEK || ARCH_AIROHA || RALINK || COMPILE_TEST
+	depends on ARCH_MEDIATEK || RALINK || COMPILE_TEST
 
 config EINT_MTK
 	tristate "MediaTek External Interrupt Support"
@@ -126,21 +126,6 @@ config PINCTRL_MT8127
 	select PINCTRL_MTK
 
 # For ARMv8 SoCs
-config PINCTRL_AIROHA
-	tristate "Airoha EN7581 pin control"
-	depends on OF
-	depends on ARM64 || COMPILE_TEST
-	select PINMUX
-	select GENERIC_PINCONF
-	select GENERIC_PINCTRL_GROUPS
-	select GENERIC_PINMUX_FUNCTIONS
-	select GPIOLIB
-	select GPIOLIB_IRQCHIP
-	select REGMAP_MMIO
-	help
-	  Say yes here to support pin controller and gpio driver
-	  on Airoha EN7581 SoC.
-
 config PINCTRL_MT2712
 	bool "MediaTek MT2712 pin control"
 	depends on OF
diff --git a/drivers/pinctrl/mediatek/Makefile b/drivers/pinctrl/mediatek/Makefile
index ae765bd99965..6dc17b0c23f9 100644
--- a/drivers/pinctrl/mediatek/Makefile
+++ b/drivers/pinctrl/mediatek/Makefile
@@ -8,7 +8,6 @@ obj-$(CONFIG_PINCTRL_MTK_MOORE)		+= pinctrl-moore.o
 obj-$(CONFIG_PINCTRL_MTK_PARIS)		+= pinctrl-paris.o
 
 # SoC Drivers
-obj-$(CONFIG_PINCTRL_AIROHA)		+= pinctrl-airoha.o
 obj-$(CONFIG_PINCTRL_MT7620)		+= pinctrl-mt7620.o
 obj-$(CONFIG_PINCTRL_MT7621)		+= pinctrl-mt7621.o
 obj-$(CONFIG_PINCTRL_MT76X8)		+= pinctrl-mt76x8.o
-- 
2.53.0



^ permalink raw reply related

* Re: [PATCH 00/76] drm/bridge: Convert all reset users to create_state
From: Thomas Zimmermann @ 2026-06-05  6:26 UTC (permalink / raw)
  To: Maxime Ripard, Andrzej Hajda, Neil Armstrong, Robert Foss,
	Laurent Pinchart, Jonas Karlman, Jernej Skrabec, Luca Ceresoli,
	Maarten Lankhorst, David Airlie, Simona Vetter
  Cc: Dmitry Baryshkov, dri-devel, Jagan Teki, Liu Ying, Frank Li,
	Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam, imx,
	linux-arm-kernel, Andy Yan, Phong LE, Douglas Anderson, Inki Dae,
	Marek Szyprowski, Philipp Zabel, Paul Cercueil, linux-mips,
	Chun-Kuang Hu, Matthias Brugger, AngeloGioacchino Del Regno,
	linux-mediatek, linux-kernel, Kevin Hilman, Jerome Brunet,
	Martin Blumenstingl, linux-amlogic, Laurent Pinchart,
	Tomi Valkeinen, Geert Uytterhoeven, Magnus Damm, Kieran Bingham,
	linux-renesas-soc, Biju Das, Sandy Huang, Heiko Stübner,
	linux-rockchip, Yannick Fertre, Raphael Gallais-Pou,
	Philippe Cornu, Maxime Coquelin, Alexandre Torgue, linux-stm32,
	Jyri Sarha, Tomi Valkeinen, Dave Stevenson, Maíra Canal,
	Raspberry Pi Kernel Maintenance, Icenowy Zheng, Michal Simek
In-Reply-To: <20260530-drm-no-more-bridge-reset-v1-0-875d828d31bc@kernel.org>

Hi

Am 30.05.26 um 15:59 schrieb Maxime Ripard:
[...]
>        drm/bridge: adv7511: Switch to atomic_create_state
>        drm/bridge: analogix_dp: Switch to atomic_create_state
>        drm/bridge: anx7625: Switch to atomic_create_state
>        drm/bridge: chipone-icn6211: Switch to atomic_create_state
>        drm/bridge: display-connector: Switch to atomic_create_state
>        drm/bridge: fsl-ldb: Switch to atomic_create_state
>        drm/bridge: imx8mp-hdmi-pvi: Switch to atomic_create_state
>        drm/bridge: imx8qm-ldb: Switch to atomic_create_state
>        drm/bridge: imx8qxp-ldb: Switch to atomic_create_state
>        drm/bridge: imx8qxp-pixel-combiner: Switch to atomic_create_state
>        drm/bridge: imx8qxp-pixel-link: Switch to atomic_create_state
>        drm/bridge: imx8qxp-pxl2dpi: Switch to atomic_create_state
>        drm/bridge: inno-hdmi: Switch to atomic_create_state
>        drm/bridge: ite-it6263: Switch to atomic_create_state
>        drm/bridge: ite-it6505: Switch to atomic_create_state
>        drm/bridge: ite-it66121: Switch to atomic_create_state
>        drm/bridge: lontium-lt9211: Switch to atomic_create_state
>        drm/bridge: lontium-lt9611: Switch to atomic_create_state
>        drm/bridge: lvds-codec: Switch to atomic_create_state
>        drm/bridge: nwl-dsi: Switch to atomic_create_state
>        drm/bridge: panel: Switch to atomic_create_state
>        drm/bridge: parade-ps8640: Switch to atomic_create_state
>        drm/bridge: samsung-dsim: Switch to atomic_create_state
>        drm/bridge: sii902x: Switch to atomic_create_state
>        drm/bridge: ssd2825: Switch to atomic_create_state
>        drm/bridge: dw-dp: Switch to atomic_create_state
>        drm/bridge: dw-hdmi-qp: Switch to atomic_create_state
>        drm/bridge: dw-hdmi: Switch to atomic_create_state
>        drm/bridge: dw-mipi-dsi: Switch to atomic_create_state
>        drm/bridge: dw-mipi-dsi2: Switch to atomic_create_state
>        drm/bridge: tc358762: Switch to atomic_create_state
>        drm/bridge: tc358767: Switch to atomic_create_state
>        drm/bridge: tc358768: Switch to atomic_create_state
>        drm/bridge: tc358775: Switch to atomic_create_state
>        drm/bridge: ti-dlpc3433: Switch to atomic_create_state
>        drm/bridge: ti-sn65dsi83: Switch to atomic_create_state
>        drm/bridge: ti-sn65dsi86: Switch to atomic_create_state
>        drm/bridge: ti-tdp158: Switch to atomic_create_state
>        drm/bridge: ti-tfp410: Switch to atomic_create_state
>        drm/imx: parallel-display: Switch to atomic_create_state
>        drm/ingenic: Switch to atomic_create_state
>        drm/mediatek: dp: Switch to atomic_create_state
>        drm/mediatek: dpi: Switch to atomic_create_state
>        drm/mediatek: dsi: Switch to atomic_create_state
>        drm/mediatek: hdmi: Switch to atomic_create_state
>        drm/mediatek: hdmi_v2: Switch to atomic_create_state
>        drm/meson: encoder_cvbs: Switch to atomic_create_state
>        drm/meson: encoder_dsi: Switch to atomic_create_state
>        drm/meson: encoder_hdmi: Switch to atomic_create_state
>        drm/msm: dp: Switch to atomic_create_state
>        drm/msm: hdmi: Switch to atomic_create_state
>        drm/omap: hdmi4: Switch to atomic_create_state
>        drm/omap: hdmi5: Switch to atomic_create_state
>        drm/renesas: rcar-du: lvds: Switch to atomic_create_state
>        drm/renesas: rcar-du: mipi_dsi: Switch to atomic_create_state
>        drm/renesas: rz-du: mipi_dsi: Switch to atomic_create_state
>        drm/rockchip: cdn-dp: Switch to atomic_create_state
>        drm/rockchip: rk3066_hdmi: Switch to atomic_create_state
>        drm/rockchip: lvds: Switch to atomic_create_state
>        drm/stm: lvds: Switch to atomic_create_state
>        drm/tests: bridge: Switch to atomic_create_state
>        drm/tidss: encoder: Switch to atomic_create_state
>        drm/tidss: oldi: Switch to atomic_create_state
>        drm/vc4: dsi: Switch to atomic_create_state
>        drm/verisilicon: Switch to atomic_create_state
>        drm/xlnx: zynqmp_dp: Switch to atomic_create_state

You can also add my

Reviewed-by: Thomas Zimmermann <tzimmermann@suse.de>

to all these one-liners in the drivers.

Best regards
Thomas


>        drm/atomic-state-helper: Remove drm_atomic_helper_bridge_reset()
>        drm/bridge: cdns-dsi: Use __drm_atomic_helper_bridge_state_init()
>        drm/bridge: cdns-dsi: Switch to atomic_create_state
>        drm/bridge: cdns-mhdp8546: Switch to atomic_create_state
>        drm/bridge: Remove atomic_reset support
>
>   drivers/gpu/drm/bridge/adv7511/adv7511_drv.c       |  2 +-
>   drivers/gpu/drm/bridge/analogix/analogix_dp_core.c |  2 +-
>   drivers/gpu/drm/bridge/analogix/anx7625.c          |  2 +-
>   drivers/gpu/drm/bridge/cadence/cdns-dsi-core.c     |  9 +++---
>   .../gpu/drm/bridge/cadence/cdns-mhdp8546-core.c    |  8 +++---
>   drivers/gpu/drm/bridge/chipone-icn6211.c           |  2 +-
>   drivers/gpu/drm/bridge/display-connector.c         |  2 +-
>   drivers/gpu/drm/bridge/fsl-ldb.c                   |  2 +-
>   drivers/gpu/drm/bridge/imx/imx8mp-hdmi-pvi.c       |  2 +-
>   drivers/gpu/drm/bridge/imx/imx8qm-ldb.c            |  2 +-
>   drivers/gpu/drm/bridge/imx/imx8qxp-ldb.c           |  2 +-
>   .../gpu/drm/bridge/imx/imx8qxp-pixel-combiner.c    |  2 +-
>   drivers/gpu/drm/bridge/imx/imx8qxp-pixel-link.c    |  2 +-
>   drivers/gpu/drm/bridge/imx/imx8qxp-pxl2dpi.c       |  2 +-
>   drivers/gpu/drm/bridge/inno-hdmi.c                 |  2 +-
>   drivers/gpu/drm/bridge/ite-it6263.c                |  2 +-
>   drivers/gpu/drm/bridge/ite-it6505.c                |  2 +-
>   drivers/gpu/drm/bridge/ite-it66121.c               |  2 +-
>   drivers/gpu/drm/bridge/lontium-lt9211.c            |  2 +-
>   drivers/gpu/drm/bridge/lontium-lt9611.c            |  2 +-
>   drivers/gpu/drm/bridge/lvds-codec.c                |  2 +-
>   drivers/gpu/drm/bridge/nwl-dsi.c                   |  2 +-
>   drivers/gpu/drm/bridge/panel.c                     |  2 +-
>   drivers/gpu/drm/bridge/parade-ps8640.c             |  2 +-
>   drivers/gpu/drm/bridge/samsung-dsim.c              |  2 +-
>   drivers/gpu/drm/bridge/sii902x.c                   |  2 +-
>   drivers/gpu/drm/bridge/ssd2825.c                   |  2 +-
>   drivers/gpu/drm/bridge/synopsys/dw-dp.c            |  2 +-
>   drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.c       |  2 +-
>   drivers/gpu/drm/bridge/synopsys/dw-hdmi.c          |  2 +-
>   drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c      |  2 +-
>   drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi2.c     |  2 +-
>   drivers/gpu/drm/bridge/tc358762.c                  |  2 +-
>   drivers/gpu/drm/bridge/tc358767.c                  |  4 +--
>   drivers/gpu/drm/bridge/tc358768.c                  |  2 +-
>   drivers/gpu/drm/bridge/tc358775.c                  |  2 +-
>   drivers/gpu/drm/bridge/ti-dlpc3433.c               |  2 +-
>   drivers/gpu/drm/bridge/ti-sn65dsi83.c              |  2 +-
>   drivers/gpu/drm/bridge/ti-sn65dsi86.c              |  2 +-
>   drivers/gpu/drm/bridge/ti-tdp158.c                 |  2 +-
>   drivers/gpu/drm/bridge/ti-tfp410.c                 |  2 +-
>   drivers/gpu/drm/drm_atomic_state_helper.c          | 33 ++++++++++++----------
>   drivers/gpu/drm/drm_bridge.c                       |  4 +--
>   drivers/gpu/drm/imx/ipuv3/parallel-display.c       |  2 +-
>   drivers/gpu/drm/ingenic/ingenic-drm-drv.c          |  2 +-
>   drivers/gpu/drm/mediatek/mtk_dp.c                  |  2 +-
>   drivers/gpu/drm/mediatek/mtk_dpi.c                 |  2 +-
>   drivers/gpu/drm/mediatek/mtk_dsi.c                 |  2 +-
>   drivers/gpu/drm/mediatek/mtk_hdmi.c                |  2 +-
>   drivers/gpu/drm/mediatek/mtk_hdmi_v2.c             |  2 +-
>   drivers/gpu/drm/meson/meson_encoder_cvbs.c         |  2 +-
>   drivers/gpu/drm/meson/meson_encoder_dsi.c          |  2 +-
>   drivers/gpu/drm/meson/meson_encoder_hdmi.c         |  2 +-
>   drivers/gpu/drm/msm/dp/dp_drm.c                    |  4 +--
>   drivers/gpu/drm/msm/hdmi/hdmi_bridge.c             |  2 +-
>   drivers/gpu/drm/omapdrm/dss/hdmi4.c                |  2 +-
>   drivers/gpu/drm/omapdrm/dss/hdmi5.c                |  2 +-
>   drivers/gpu/drm/renesas/rcar-du/rcar_lvds.c        |  2 +-
>   drivers/gpu/drm/renesas/rcar-du/rcar_mipi_dsi.c    |  2 +-
>   drivers/gpu/drm/renesas/rz-du/rzg2l_mipi_dsi.c     |  2 +-
>   drivers/gpu/drm/rockchip/cdn-dp-core.c             |  2 +-
>   drivers/gpu/drm/rockchip/rk3066_hdmi.c             |  2 +-
>   drivers/gpu/drm/rockchip/rockchip_lvds.c           |  2 +-
>   drivers/gpu/drm/stm/lvds.c                         |  2 +-
>   drivers/gpu/drm/tests/drm_bridge_test.c            |  2 +-
>   drivers/gpu/drm/tidss/tidss_encoder.c              |  2 +-
>   drivers/gpu/drm/tidss/tidss_oldi.c                 |  2 +-
>   drivers/gpu/drm/vc4/vc4_dsi.c                      |  2 +-
>   drivers/gpu/drm/verisilicon/vs_bridge.c            |  4 +--
>   drivers/gpu/drm/xlnx/zynqmp_dp.c                   |  2 +-
>   include/drm/drm_atomic_state_helper.h              |  6 ++--
>   include/drm/drm_bridge.h                           | 33 ++++++++--------------
>   72 files changed, 111 insertions(+), 120 deletions(-)
> ---
> base-commit: 21fcb222f0d1e1c9f5b04c09e9fb3408e13a0264
> change-id: 20260530-drm-no-more-bridge-reset-ca20d5e22740
>
> Best regards,

-- 
--
Thomas Zimmermann
Graphics Driver Developer
SUSE Software Solutions Germany GmbH
Frankenstr. 146, 90461 Nürnberg, Germany, www.suse.com
GF: Jochen Jaser, Andrew McDonald, Werner Knoblich, (HRB 36809, AG Nürnberg)




^ permalink raw reply

* [PATCH] wifi: mt76: fix potential tx_retries underflow
From: Ryder Lee @ 2026-06-05  2:42 UTC (permalink / raw)
  To: Felix Fietkau; +Cc: linux-mediatek, linux-wireless, Shayne Chen, Ryder Lee

When FIELD_GET returns 0 for the retry count, subtracting 1 causes
an unsigned integer underflow, resulting in tx_retries becoming a
very large value (0xFFFFFFFF for u32 or 255 for u8).

Fix by checking if count is non-zero before subtracting 1.

Signed-off-by: Ryder Lee <ryder.lee@mediatek.com>
---
 drivers/net/wireless/mediatek/mt76/mt7915/mac.c | 10 +++++-----
 drivers/net/wireless/mediatek/mt76/mt7921/mac.c |  5 +++--
 drivers/net/wireless/mediatek/mt76/mt7925/mac.c |  5 +++--
 drivers/net/wireless/mediatek/mt76/mt7996/mac.c |  6 +++---
 4 files changed, 14 insertions(+), 12 deletions(-)

diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mac.c b/drivers/net/wireless/mediatek/mt76/mt7915/mac.c
index cec2c4208..334c19ab2 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7915/mac.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7915/mac.c
@@ -912,16 +912,16 @@ mt7915_mac_tx_free(struct mt7915_dev *dev, void *data, int len)
 		}
 
 		if (!mtk_wed_device_active(&mdev->mmio.wed) && wcid) {
-			u32 tx_retries = 0, tx_failed = 0;
+			u32 tx_retries = 0, tx_failed = 0, count;
 
 			if (v3 && (info & MT_TX_FREE_MPDU_HEADER_V3)) {
-				tx_retries =
-					FIELD_GET(MT_TX_FREE_COUNT_V3, info) - 1;
+				count = FIELD_GET(MT_TX_FREE_COUNT_V3, info);
+				tx_retries = count ? count - 1 : 0;
 				tx_failed = tx_retries +
 					!!FIELD_GET(MT_TX_FREE_STAT_V3, info);
 			} else if (!v3 && (info & MT_TX_FREE_MPDU_HEADER)) {
-				tx_retries =
-					FIELD_GET(MT_TX_FREE_COUNT, info) - 1;
+				count = FIELD_GET(MT_TX_FREE_COUNT, info);
+				tx_retries = count ? count - 1 : 0;
 				tx_failed = tx_retries +
 					!!FIELD_GET(MT_TX_FREE_STAT, info);
 			}
diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/mac.c b/drivers/net/wireless/mediatek/mt76/mt7921/mac.c
index 03b4960db..668bfa195 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7921/mac.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7921/mac.c
@@ -530,8 +530,9 @@ static void mt7921_mac_tx_free(struct mt792x_dev *dev, void *data, int len)
 		stat = FIELD_GET(MT_TX_FREE_STATUS, info);
 
 		if (wcid) {
-			wcid->stats.tx_retries +=
-				FIELD_GET(MT_TX_FREE_COUNT, info) - 1;
+			u32 count = FIELD_GET(MT_TX_FREE_COUNT, info);
+
+			wcid->stats.tx_retries += count ? count - 1 : 0;
 			wcid->stats.tx_failed += !!stat;
 		}
 
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mac.c b/drivers/net/wireless/mediatek/mt76/mt7925/mac.c
index c47bd812b..c56a9e530 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/mac.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/mac.c
@@ -1141,8 +1141,9 @@ mt7925_mac_tx_free(struct mt792x_dev *dev, void *data, int len)
 
 		if (info & MT_TXFREE_INFO_HEADER) {
 			if (wcid) {
-				wcid->stats.tx_retries +=
-					FIELD_GET(MT_TXFREE_INFO_COUNT, info) - 1;
+				u32 count = FIELD_GET(MT_TXFREE_INFO_COUNT, info);
+
+				wcid->stats.tx_retries += count ? count - 1 : 0;
 				wcid->stats.tx_failed +=
 					!!FIELD_GET(MT_TXFREE_INFO_STAT, info);
 			}
diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mac.c b/drivers/net/wireless/mediatek/mt76/mt7996/mac.c
index a59c14c8f..3fad977ba 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7996/mac.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7996/mac.c
@@ -1361,13 +1361,13 @@ mt7996_mac_tx_free(struct mt7996_dev *dev, void *data, int len)
 				cur_info++;
 			continue;
 		} else if (info & MT_TXFREE_INFO_HEADER) {
-			u32 tx_retries = 0, tx_failed = 0;
+			u32 tx_retries = 0, tx_failed = 0, count;
 
 			if (!wcid)
 				continue;
 
-			tx_retries =
-				FIELD_GET(MT_TXFREE_INFO_COUNT, info) - 1;
+			count = FIELD_GET(MT_TXFREE_INFO_COUNT, info);
+			tx_retries = count ? count - 1 : 0;
 			tx_failed = tx_retries +
 				!!FIELD_GET(MT_TXFREE_INFO_STAT, info);
 
-- 
2.45.2



^ permalink raw reply related

* Re: [PATCH net-next] net: airoha: Report extack error to the user if airoha_tc_htb_modify_queue() fails
From: patchwork-bot+netdevbpf @ 2026-06-05  1:30 UTC (permalink / raw)
  To: Lorenzo Bianconi
  Cc: andrew+netdev, davem, edumazet, kuba, pabeni, linux-arm-kernel,
	linux-mediatek, netdev
In-Reply-To: <20260603-airoha_tc_htb_modify_queue-err-message-v1-1-33ec3ab997d9@kernel.org>

Hello:

This patch was applied to netdev/net-next.git (main)
by Jakub Kicinski <kuba@kernel.org>:

On Wed, 03 Jun 2026 12:30:01 +0200 you wrote:
> Report an extack error message in airoha_tc_htb_modify_queue routine if
> airoha_qdma_set_tx_rate_limit() fails.
> 
> Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
> ---
>  drivers/net/ethernet/airoha/airoha_eth.c | 8 +++++++-
>  1 file changed, 7 insertions(+), 1 deletion(-)
> 
> [...]

Here is the summary with links:
  - [net-next] net: airoha: Report extack error to the user if airoha_tc_htb_modify_queue() fails
    https://git.kernel.org/netdev/net-next/c/144969cd80c5

You are awesome, thank you!
-- 
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html




^ permalink raw reply

* Re: [PATCH net 1/2] net: airoha: Fix use-after-free in metadata dst teardown
From: Jacob Keller @ 2026-06-04 21:53 UTC (permalink / raw)
  To: Lorenzo Bianconi
  Cc: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, Felix Fietkau, Matthias Brugger,
	AngeloGioacchino Del Regno, Florian Westphal, linux-arm-kernel,
	linux-mediatek, netdev
In-Reply-To: <aiHsxFrleJfpLeuA@lore-rh-laptop>

On 6/4/2026 2:23 PM, Lorenzo Bianconi wrote:
>> On 6/2/2026 2:21 AM, Lorenzo Bianconi wrote:
>>> airoha_metadata_dst_free() runs metadata_dst_free() which frees the
>>> metadata_dst with kfree() immediately, bypassing the RCU grace period.
>>> In the RX path, skb_dst_set_noref() sets a non-refcounted pointer from
>>> the skb to the metadata_dst. This function requires RCU read-side
>>> protection and the dst must remain valid until all RCU readers complete.
>>> Since metadata_dst_free() calls kfree() directly, an use-after-free can
>>> occur if any skb still holds a noref pointer to the dst when the driver
>>> tears it down.
>>> Replace metadata_dst_free() with dst_release() which properly goes
>>> through the refcount path: when the refcount drops to zero, it schedules
>>> the actual free via call_rcu_hurry(), ensuring all RCU readers have
>>> completed before the memory is freed.
>>>
>>> Fixes: af3cf757d5c9 ("net: airoha: Move DSA tag in DMA descriptor")
>>> Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
>>> ---
>>>  drivers/net/ethernet/airoha/airoha_eth.c | 2 +-
>>>  1 file changed, 1 insertion(+), 1 deletion(-)
>>>
>>> diff --git a/drivers/net/ethernet/airoha/airoha_eth.c b/drivers/net/ethernet/airoha/airoha_eth.c
>>> index cecd66251dba..eab6a98d62b9 100644
>>> --- a/drivers/net/ethernet/airoha/airoha_eth.c
>>> +++ b/drivers/net/ethernet/airoha/airoha_eth.c
>>> @@ -2936,7 +2936,7 @@ static void airoha_metadata_dst_free(struct airoha_gdm_port *port)
>>>  		if (!port->dsa_meta[i])
>>>  			continue;
>>>  
>>> -		metadata_dst_free(port->dsa_meta[i]);
>>> +		dst_release(&port->dsa_meta[i]->dst);
>>>  	}
>>>  }
>>>  
>>>
>>
>> the port->dsa_meta is allocated using metadata_dst_alloc().. how is it
>> safe to use dst_release here? Seems like we should be calling dst_alloc
>> instead of metadata_dst_alloc in order to use dst_release??
> 
> We need to allocate the metadata_dst using metadata_dst_alloc() since
> md_dst->u.port_info.port_id is consumed in dsa_switch_rcv() to get the
> switch conduit port.
> I guess it is fine to free metadata_dst running dst_release() since dst_init()
> sets DST_METADATA flag and so dst_destroy() runs metadata_dst_free() after the
> RCU grace period.
> 

Ok. So when would any flow need to call metadata_dst_free()? I guess
some path might happen to know that its already past RCU grace period
for all accesses or something...

I guess mostly my brain would be less confused if we had
metadata_dst_free() call dst_destroy or have a metadata_dst_release() or
something to make the two sides of the naming match... but I guess thats
really just noise on top of the API, since it doesn't really add any value.

Thanks for the explanation and helping un-confuse me at least a little!
>>
>> metadata_dst_alloc does call __metadata_dst_init which calls dst_init..
>>
>> I guess the start of the metadata_dst structure is also the same address
>> as the internal dst_entry struct...
>>
>> But dst_destroy does a whole lot more than metadata_dst_release so I
>> don't feel confident in this actually being a drop-in replacement... It
>> calls netdev_put, it calls the dst->ops->destroy, it releases child
>> refs.. Or for metadata dst entries is that all basically a no-op??
> 
> __metadata_dst_init() calls dst_init() with dev = NULL so netdev_put() is a
> no-op. Same for dst->ops is dst_blackhole_ops and and dst_blackhole_ops has no
> destroy callback.
> 

Ok. So basically its correct, it just looks odd because the code is
re-used by several other flows.

>>
>> I feel like I'm missing something here.. The driver also calls
>> metadata_dst_free in the remove path and that wasn't changed by this
>> patch either.
> 
> can you please explain what you mean here? we do not run metadata_dst_free()
> anymore.
> 

I might have been mistaken. I did a search for metadata_dst_free() in
the driver and I think I confused the airoha_metadata_dst_free() call
site while scanning the code.

All of that said, thanks for a quick response and now that I understand
it makes sense:

Reviewed-by: Jacob Keller <jacob.e.keller@intel.com>

Regards,
Jake


^ permalink raw reply

* Re: [PATCH net 1/2] net: airoha: Fix use-after-free in metadata dst teardown
From: Lorenzo Bianconi @ 2026-06-04 21:23 UTC (permalink / raw)
  To: Jacob Keller
  Cc: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, Felix Fietkau, Matthias Brugger,
	AngeloGioacchino Del Regno, Florian Westphal, linux-arm-kernel,
	linux-mediatek, netdev
In-Reply-To: <21810a20-abe6-4490-969c-cfd62c4c082a@intel.com>

[-- Attachment #1: Type: text/plain, Size: 3295 bytes --]

> On 6/2/2026 2:21 AM, Lorenzo Bianconi wrote:
> > airoha_metadata_dst_free() runs metadata_dst_free() which frees the
> > metadata_dst with kfree() immediately, bypassing the RCU grace period.
> > In the RX path, skb_dst_set_noref() sets a non-refcounted pointer from
> > the skb to the metadata_dst. This function requires RCU read-side
> > protection and the dst must remain valid until all RCU readers complete.
> > Since metadata_dst_free() calls kfree() directly, an use-after-free can
> > occur if any skb still holds a noref pointer to the dst when the driver
> > tears it down.
> > Replace metadata_dst_free() with dst_release() which properly goes
> > through the refcount path: when the refcount drops to zero, it schedules
> > the actual free via call_rcu_hurry(), ensuring all RCU readers have
> > completed before the memory is freed.
> > 
> > Fixes: af3cf757d5c9 ("net: airoha: Move DSA tag in DMA descriptor")
> > Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
> > ---
> >  drivers/net/ethernet/airoha/airoha_eth.c | 2 +-
> >  1 file changed, 1 insertion(+), 1 deletion(-)
> > 
> > diff --git a/drivers/net/ethernet/airoha/airoha_eth.c b/drivers/net/ethernet/airoha/airoha_eth.c
> > index cecd66251dba..eab6a98d62b9 100644
> > --- a/drivers/net/ethernet/airoha/airoha_eth.c
> > +++ b/drivers/net/ethernet/airoha/airoha_eth.c
> > @@ -2936,7 +2936,7 @@ static void airoha_metadata_dst_free(struct airoha_gdm_port *port)
> >  		if (!port->dsa_meta[i])
> >  			continue;
> >  
> > -		metadata_dst_free(port->dsa_meta[i]);
> > +		dst_release(&port->dsa_meta[i]->dst);
> >  	}
> >  }
> >  
> > 
> 
> the port->dsa_meta is allocated using metadata_dst_alloc().. how is it
> safe to use dst_release here? Seems like we should be calling dst_alloc
> instead of metadata_dst_alloc in order to use dst_release??

We need to allocate the metadata_dst using metadata_dst_alloc() since
md_dst->u.port_info.port_id is consumed in dsa_switch_rcv() to get the
switch conduit port.
I guess it is fine to free metadata_dst running dst_release() since dst_init()
sets DST_METADATA flag and so dst_destroy() runs metadata_dst_free() after the
RCU grace period.

> 
> metadata_dst_alloc does call __metadata_dst_init which calls dst_init..
> 
> I guess the start of the metadata_dst structure is also the same address
> as the internal dst_entry struct...
> 
> But dst_destroy does a whole lot more than metadata_dst_release so I
> don't feel confident in this actually being a drop-in replacement... It
> calls netdev_put, it calls the dst->ops->destroy, it releases child
> refs.. Or for metadata dst entries is that all basically a no-op??

__metadata_dst_init() calls dst_init() with dev = NULL so netdev_put() is a
no-op. Same for dst->ops is dst_blackhole_ops and and dst_blackhole_ops has no
destroy callback.

> 
> I feel like I'm missing something here.. The driver also calls
> metadata_dst_free in the remove path and that wasn't changed by this
> patch either.

can you please explain what you mean here? we do not run metadata_dst_free()
anymore.

Regards,
Lorenzo

> 
> Generally it seems like we should be using the same API to allocate as
> to release the object... This is confusing. What am I missing?

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]

^ permalink raw reply


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