[PATCH v6 11/16] ASoC: mediatek: mt8365: Add the AFE driver support

Alexandre Mergnat amergnat at baylibre.com
Wed Jun 19 07:46:47 PDT 2024


Add a driver for the Audio Front End (AFE) PCM to provide Audio
Uplink (UL) and Downlink (DL) paths.
Use the ALSA SoC Dynamic Audio Power Management to add widget and
kcontrol supports.

Reviewed-by: AngeloGioacchino Del Regno <angelogioacchino.delregno at collabora.com>
Signed-off-by: Alexandre Mergnat <amergnat at baylibre.com>
---
 sound/soc/mediatek/mt8365/mt8365-afe-pcm.c | 2275 ++++++++++++++++++++++++++++
 1 file changed, 2275 insertions(+)

diff --git a/sound/soc/mediatek/mt8365/mt8365-afe-pcm.c b/sound/soc/mediatek/mt8365/mt8365-afe-pcm.c
new file mode 100644
index 000000000000..df6dd8c5bbe5
--- /dev/null
+++ b/sound/soc/mediatek/mt8365/mt8365-afe-pcm.c
@@ -0,0 +1,2275 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * MediaTek 8365 ALSA SoC AFE platform driver
+ *
+ * Copyright (c) 2024 MediaTek Inc.
+ * Authors: Jia Zeng <jia.zeng at mediatek.com>
+ *          Alexandre Mergnat <amergnat at baylibre.com>
+ */
+
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/dma-mapping.h>
+#include <linux/pm_runtime.h>
+#include <sound/soc.h>
+#include <sound/pcm_params.h>
+#include "mt8365-afe-common.h"
+#include "mt8365-afe-clk.h"
+#include "mt8365-reg.h"
+#include "../common/mtk-base-afe.h"
+#include "../common/mtk-afe-platform-driver.h"
+#include "../common/mtk-afe-fe-dai.h"
+
+#define AFE_BASE_END_OFFSET 8
+
+static unsigned int mCM2Input;
+
+static const unsigned int mt8365_afe_backup_list[] = {
+	AUDIO_TOP_CON0,
+	AFE_CONN0,
+	AFE_CONN1,
+	AFE_CONN3,
+	AFE_CONN4,
+	AFE_CONN5,
+	AFE_CONN6,
+	AFE_CONN7,
+	AFE_CONN8,
+	AFE_CONN9,
+	AFE_CONN10,
+	AFE_CONN11,
+	AFE_CONN12,
+	AFE_CONN13,
+	AFE_CONN14,
+	AFE_CONN15,
+	AFE_CONN16,
+	AFE_CONN17,
+	AFE_CONN18,
+	AFE_CONN19,
+	AFE_CONN20,
+	AFE_CONN21,
+	AFE_CONN26,
+	AFE_CONN27,
+	AFE_CONN28,
+	AFE_CONN29,
+	AFE_CONN30,
+	AFE_CONN31,
+	AFE_CONN32,
+	AFE_CONN33,
+	AFE_CONN34,
+	AFE_CONN35,
+	AFE_CONN36,
+	AFE_CONN_24BIT,
+	AFE_CONN_24BIT_1,
+	AFE_DAC_CON0,
+	AFE_DAC_CON1,
+	AFE_DL1_BASE,
+	AFE_DL1_END,
+	AFE_DL2_BASE,
+	AFE_DL2_END,
+	AFE_VUL_BASE,
+	AFE_VUL_END,
+	AFE_AWB_BASE,
+	AFE_AWB_END,
+	AFE_VUL3_BASE,
+	AFE_VUL3_END,
+	AFE_HDMI_OUT_BASE,
+	AFE_HDMI_OUT_END,
+	AFE_HDMI_IN_2CH_BASE,
+	AFE_HDMI_IN_2CH_END,
+	AFE_ADDA_UL_DL_CON0,
+	AFE_ADDA_DL_SRC2_CON0,
+	AFE_ADDA_DL_SRC2_CON1,
+	AFE_I2S_CON,
+	AFE_I2S_CON1,
+	AFE_I2S_CON2,
+	AFE_I2S_CON3,
+	AFE_ADDA_UL_SRC_CON0,
+	AFE_AUD_PAD_TOP,
+	AFE_HD_ENGEN_ENABLE,
+};
+
+static const struct snd_pcm_hardware mt8365_afe_hardware = {
+	.info = (SNDRV_PCM_INFO_MMAP |
+		 SNDRV_PCM_INFO_INTERLEAVED |
+		 SNDRV_PCM_INFO_MMAP_VALID),
+	.buffer_bytes_max = 256 * 1024,
+	.period_bytes_min = 512,
+	.period_bytes_max = 128 * 1024,
+	.periods_min = 2,
+	.periods_max = 256,
+	.fifo_size = 0,
+};
+
+struct mt8365_afe_rate {
+	unsigned int rate;
+	unsigned int reg_val;
+};
+
+static const struct mt8365_afe_rate mt8365_afe_fs_rates[] = {
+	{ .rate = 8000, .reg_val = MT8365_FS_8K },
+	{ .rate = 11025, .reg_val = MT8365_FS_11D025K },
+	{ .rate = 12000, .reg_val = MT8365_FS_12K },
+	{ .rate = 16000, .reg_val = MT8365_FS_16K },
+	{ .rate = 22050, .reg_val = MT8365_FS_22D05K },
+	{ .rate = 24000, .reg_val = MT8365_FS_24K },
+	{ .rate = 32000, .reg_val = MT8365_FS_32K },
+	{ .rate = 44100, .reg_val = MT8365_FS_44D1K },
+	{ .rate = 48000, .reg_val = MT8365_FS_48K },
+	{ .rate = 88200, .reg_val = MT8365_FS_88D2K },
+	{ .rate = 96000, .reg_val = MT8365_FS_96K },
+	{ .rate = 176400, .reg_val = MT8365_FS_176D4K },
+	{ .rate = 192000, .reg_val = MT8365_FS_192K },
+};
+
+int mt8365_afe_fs_timing(unsigned int rate)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(mt8365_afe_fs_rates); i++)
+		if (mt8365_afe_fs_rates[i].rate == rate)
+			return mt8365_afe_fs_rates[i].reg_val;
+
+	return -EINVAL;
+}
+
+bool mt8365_afe_rate_supported(unsigned int rate, unsigned int id)
+{
+	switch (id) {
+	case MT8365_AFE_IO_TDM_IN:
+		if (rate >= 8000 && rate <= 192000)
+			return true;
+		break;
+	case MT8365_AFE_IO_DMIC:
+		if (rate >= 8000 && rate <= 48000)
+			return true;
+		break;
+	default:
+		break;
+	}
+
+	return false;
+}
+
+bool mt8365_afe_channel_supported(unsigned int channel, unsigned int id)
+{
+	switch (id) {
+	case MT8365_AFE_IO_TDM_IN:
+		if (channel >= 1 && channel <= 8)
+			return true;
+		break;
+	case MT8365_AFE_IO_DMIC:
+		if (channel >= 1 && channel <= 8)
+			return true;
+		break;
+	default:
+		break;
+	}
+
+	return false;
+}
+
+bool mt8365_afe_clk_group_44k(int sample_rate)
+{
+	if (sample_rate == 11025 ||
+	    sample_rate == 22050 ||
+	    sample_rate == 44100 ||
+	    sample_rate == 88200 ||
+	    sample_rate == 176400)
+		return true;
+	else
+		return false;
+}
+
+bool mt8365_afe_clk_group_48k(int sample_rate)
+{
+	return (!mt8365_afe_clk_group_44k(sample_rate));
+}
+
+int mt8365_dai_set_priv(struct mtk_base_afe *afe, int id,
+			int priv_size, const void *priv_data)
+{
+	struct mt8365_afe_private *afe_priv = afe->platform_priv;
+	void *temp_data;
+
+	temp_data = devm_kzalloc(afe->dev, priv_size, GFP_KERNEL);
+	if (!temp_data)
+		return -ENOMEM;
+
+	if (priv_data)
+		memcpy(temp_data, priv_data, priv_size);
+
+	afe_priv->dai_priv[id] = temp_data;
+
+	return 0;
+}
+
+static int mt8365_afe_irq_direction_enable(struct mtk_base_afe *afe,
+					   int irq_id, int direction)
+{
+	struct mtk_base_afe_irq *irq;
+
+	if (irq_id >= MT8365_AFE_IRQ_NUM)
+		return -1;
+
+	irq = &afe->irqs[irq_id];
+
+	if (direction == MT8365_AFE_IRQ_DIR_MCU) {
+		regmap_update_bits(afe->regmap, AFE_IRQ_MCU_DSP_EN,
+				   (1 << irq->irq_data->irq_clr_shift),
+				   0);
+		regmap_update_bits(afe->regmap, AFE_IRQ_MCU_EN,
+				   (1 << irq->irq_data->irq_clr_shift),
+				   (1 << irq->irq_data->irq_clr_shift));
+	} else if (direction == MT8365_AFE_IRQ_DIR_DSP) {
+		regmap_update_bits(afe->regmap, AFE_IRQ_MCU_DSP_EN,
+				   (1 << irq->irq_data->irq_clr_shift),
+				   (1 << irq->irq_data->irq_clr_shift));
+		regmap_update_bits(afe->regmap, AFE_IRQ_MCU_EN,
+				   (1 << irq->irq_data->irq_clr_shift),
+				   0);
+	} else {
+		regmap_update_bits(afe->regmap, AFE_IRQ_MCU_DSP_EN,
+				   (1 << irq->irq_data->irq_clr_shift),
+				   (1 << irq->irq_data->irq_clr_shift));
+		regmap_update_bits(afe->regmap, AFE_IRQ_MCU_EN,
+				   (1 << irq->irq_data->irq_clr_shift),
+				   (1 << irq->irq_data->irq_clr_shift));
+	}
+	return 0;
+}
+
+static int mt8365_memif_fs(struct snd_pcm_substream *substream,
+			   unsigned int rate)
+{
+	return mt8365_afe_fs_timing(rate);
+}
+
+static int mt8365_irq_fs(struct snd_pcm_substream *substream,
+			 unsigned int rate)
+{
+	return mt8365_memif_fs(substream, rate);
+}
+
+static const struct mt8365_cm_ctrl_reg cm_ctrl_reg[MT8365_CM_NUM] = {
+	[MT8365_CM1] = {
+		.con0 = AFE_CM1_CON0,
+		.con1 = AFE_CM1_CON1,
+		.con2 = AFE_CM1_CON2,
+		.con3 = AFE_CM1_CON3,
+		.con4 = AFE_CM1_CON4,
+	},
+	[MT8365_CM2] = {
+		.con0 = AFE_CM2_CON0,
+		.con1 = AFE_CM2_CON1,
+		.con2 = AFE_CM2_CON2,
+		.con3 = AFE_CM2_CON3,
+		.con4 = AFE_CM2_CON4,
+	}
+};
+
+static int mt8365_afe_cm2_mux_conn(struct mtk_base_afe *afe)
+{
+	struct mt8365_afe_private *afe_priv = afe->platform_priv;
+	unsigned int input = afe_priv->cm2_mux_input;
+
+	/* TDM_IN interconnect to CM2 */
+	regmap_update_bits(afe->regmap, AFE_CM2_CONN0,
+			   CM2_AFE_CM2_CONN_CFG1_MASK,
+			   CM2_AFE_CM2_CONN_CFG1(TDM_IN_CH0));
+	regmap_update_bits(afe->regmap, AFE_CM2_CONN0,
+			   CM2_AFE_CM2_CONN_CFG2_MASK,
+			   CM2_AFE_CM2_CONN_CFG2(TDM_IN_CH1));
+	regmap_update_bits(afe->regmap, AFE_CM2_CONN0,
+			   CM2_AFE_CM2_CONN_CFG3_MASK,
+			   CM2_AFE_CM2_CONN_CFG3(TDM_IN_CH2));
+	regmap_update_bits(afe->regmap, AFE_CM2_CONN0,
+			   CM2_AFE_CM2_CONN_CFG4_MASK,
+			   CM2_AFE_CM2_CONN_CFG4(TDM_IN_CH3));
+	regmap_update_bits(afe->regmap, AFE_CM2_CONN0,
+			   CM2_AFE_CM2_CONN_CFG5_MASK,
+			   CM2_AFE_CM2_CONN_CFG5(TDM_IN_CH4));
+	regmap_update_bits(afe->regmap, AFE_CM2_CONN0,
+			   CM2_AFE_CM2_CONN_CFG6_MASK,
+			   CM2_AFE_CM2_CONN_CFG6(TDM_IN_CH5));
+	regmap_update_bits(afe->regmap, AFE_CM2_CONN1,
+			   CM2_AFE_CM2_CONN_CFG7_MASK,
+			   CM2_AFE_CM2_CONN_CFG7(TDM_IN_CH6));
+	regmap_update_bits(afe->regmap, AFE_CM2_CONN1,
+			   CM2_AFE_CM2_CONN_CFG8_MASK,
+			   CM2_AFE_CM2_CONN_CFG8(TDM_IN_CH7));
+
+	/* ref data interconnect to CM2 */
+	if (input == MT8365_FROM_GASRC1) {
+		regmap_update_bits(afe->regmap, AFE_CM2_CONN1,
+				   CM2_AFE_CM2_CONN_CFG9_MASK,
+				   CM2_AFE_CM2_CONN_CFG9(GENERAL1_ASRC_OUT_LCH));
+		regmap_update_bits(afe->regmap, AFE_CM2_CONN1,
+				   CM2_AFE_CM2_CONN_CFG10_MASK,
+				   CM2_AFE_CM2_CONN_CFG10(GENERAL1_ASRC_OUT_RCH));
+	} else if (input == MT8365_FROM_GASRC2) {
+		regmap_update_bits(afe->regmap, AFE_CM2_CONN1,
+				   CM2_AFE_CM2_CONN_CFG9_MASK,
+				   CM2_AFE_CM2_CONN_CFG9(GENERAL2_ASRC_OUT_LCH));
+		regmap_update_bits(afe->regmap, AFE_CM2_CONN1,
+				   CM2_AFE_CM2_CONN_CFG10_MASK,
+				   CM2_AFE_CM2_CONN_CFG10(GENERAL2_ASRC_OUT_RCH));
+	} else if (input == MT8365_FROM_TDM_ASRC) {
+		regmap_update_bits(afe->regmap, AFE_CM2_CONN1,
+				   CM2_AFE_CM2_CONN_CFG9_MASK,
+				   CM2_AFE_CM2_CONN_CFG9(TDM_OUT_ASRC_CH0));
+		regmap_update_bits(afe->regmap, AFE_CM2_CONN1,
+				   CM2_AFE_CM2_CONN_CFG10_MASK,
+				   CM2_AFE_CM2_CONN_CFG10(TDM_OUT_ASRC_CH1));
+		regmap_update_bits(afe->regmap, AFE_CM2_CONN1,
+				   CM2_AFE_CM2_CONN_CFG11_MASK,
+				   CM2_AFE_CM2_CONN_CFG11(TDM_OUT_ASRC_CH2));
+		regmap_update_bits(afe->regmap, AFE_CM2_CONN1,
+				   CM2_AFE_CM2_CONN_CFG12_MASK,
+				   CM2_AFE_CM2_CONN_CFG12(TDM_OUT_ASRC_CH3));
+		regmap_update_bits(afe->regmap, AFE_CM2_CONN2,
+				   CM2_AFE_CM2_CONN_CFG13_MASK,
+				   CM2_AFE_CM2_CONN_CFG13(TDM_OUT_ASRC_CH4));
+		regmap_update_bits(afe->regmap, AFE_CM2_CONN2,
+				   CM2_AFE_CM2_CONN_CFG14_MASK,
+				   CM2_AFE_CM2_CONN_CFG14(TDM_OUT_ASRC_CH5));
+		regmap_update_bits(afe->regmap, AFE_CM2_CONN2,
+				   CM2_AFE_CM2_CONN_CFG15_MASK,
+				   CM2_AFE_CM2_CONN_CFG15(TDM_OUT_ASRC_CH6));
+		regmap_update_bits(afe->regmap, AFE_CM2_CONN2,
+				   CM2_AFE_CM2_CONN_CFG16_MASK,
+				   CM2_AFE_CM2_CONN_CFG16(TDM_OUT_ASRC_CH7));
+	} else {
+		dev_err(afe->dev, "%s wrong CM2 input %d\n", __func__, input);
+		return -1;
+	}
+
+	return 0;
+}
+
+static int mt8365_afe_get_cm_update_cnt(struct mtk_base_afe *afe,
+					enum mt8365_cm_num cmNum,
+					unsigned int rate, unsigned int channel)
+{
+	unsigned int total_cnt, div_cnt, ch_pair, best_cnt;
+	unsigned int ch_update_cnt[MT8365_CM_UPDATA_CNT_SET];
+	int i;
+
+	/* calculate cm update cnt
+	 * total_cnt = clk / fs, clk is 26m or 24m or 22m
+	 * div_cnt = total_cnt / ch_pair, max ch 16ch ,2ch is a set
+	 * best_cnt < div_cnt ,we set best_cnt = div_cnt -10
+	 * ch01 = best_cnt, ch23 = 2* ch01_up_cnt
+	 * ch45 = 3* ch01_up_cnt ...ch1415 = 8* ch01_up_cnt
+	 */
+
+	if (cmNum == MT8365_CM1) {
+		total_cnt = MT8365_CLK_26M / rate;
+	} else if (cmNum == MT8365_CM2) {
+		if (mt8365_afe_clk_group_48k(rate))
+			total_cnt = MT8365_CLK_24M / rate;
+		else
+			total_cnt = MT8365_CLK_22M / rate;
+	} else {
+		return -1;
+	}
+
+	if (channel % 2)
+		ch_pair = (channel / 2) + 1;
+	else
+		ch_pair = channel / 2;
+
+	div_cnt =  total_cnt / ch_pair;
+	best_cnt = div_cnt - 10;
+
+	if (best_cnt <= 0)
+		return -1;
+
+	for (i = 0; i < ch_pair; i++)
+		ch_update_cnt[i] = (i + 1) * best_cnt;
+
+	switch (channel) {
+	case 16:
+		fallthrough;
+	case 15:
+		regmap_update_bits(afe->regmap, cm_ctrl_reg[cmNum].con4,
+				   CM_AFE_CM_UPDATE_CNT2_MASK,
+				   CM_AFE_CM_UPDATE_CNT2(ch_update_cnt[7]));
+		fallthrough;
+	case 14:
+		fallthrough;
+	case 13:
+		regmap_update_bits(afe->regmap, cm_ctrl_reg[cmNum].con4,
+				   CM_AFE_CM_UPDATE_CNT1_MASK,
+				   CM_AFE_CM_UPDATE_CNT1(ch_update_cnt[6]));
+		fallthrough;
+	case 12:
+		fallthrough;
+	case 11:
+		regmap_update_bits(afe->regmap, cm_ctrl_reg[cmNum].con3,
+				   CM_AFE_CM_UPDATE_CNT2_MASK,
+				   CM_AFE_CM_UPDATE_CNT2(ch_update_cnt[5]));
+		fallthrough;
+	case 10:
+		fallthrough;
+	case 9:
+		regmap_update_bits(afe->regmap, cm_ctrl_reg[cmNum].con3,
+				   CM_AFE_CM_UPDATE_CNT1_MASK,
+				   CM_AFE_CM_UPDATE_CNT1(ch_update_cnt[4]));
+		fallthrough;
+	case 8:
+		fallthrough;
+	case 7:
+		regmap_update_bits(afe->regmap, cm_ctrl_reg[cmNum].con2,
+				   CM_AFE_CM_UPDATE_CNT2_MASK,
+				   CM_AFE_CM_UPDATE_CNT2(ch_update_cnt[3]));
+		fallthrough;
+	case 6:
+		fallthrough;
+	case 5:
+		regmap_update_bits(afe->regmap, cm_ctrl_reg[cmNum].con2,
+				   CM_AFE_CM_UPDATE_CNT1_MASK,
+				   CM_AFE_CM_UPDATE_CNT1(ch_update_cnt[2]));
+		fallthrough;
+	case 4:
+		fallthrough;
+	case 3:
+		regmap_update_bits(afe->regmap, cm_ctrl_reg[cmNum].con1,
+				   CM_AFE_CM_UPDATE_CNT2_MASK,
+				   CM_AFE_CM_UPDATE_CNT2(ch_update_cnt[1]));
+		fallthrough;
+	case 2:
+		fallthrough;
+	case 1:
+		regmap_update_bits(afe->regmap, cm_ctrl_reg[cmNum].con1,
+				   CM_AFE_CM_UPDATE_CNT1_MASK,
+				   CM_AFE_CM_UPDATE_CNT1(ch_update_cnt[0]));
+		break;
+	default:
+		return -1;
+	}
+
+	return 0;
+}
+
+static int mt8365_afe_configure_cm(struct mtk_base_afe *afe,
+				   enum mt8365_cm_num cmNum,
+				   unsigned int channels,
+				   unsigned int rate)
+{
+	unsigned int val, mask;
+	unsigned int fs = mt8365_afe_fs_timing(rate);
+
+	val = FIELD_PREP(CM_AFE_CM_CH_NUM_MASK, (channels - 1)) |
+	      FIELD_PREP(CM_AFE_CM_START_DATA_MASK, 0);
+
+	mask = CM_AFE_CM_CH_NUM_MASK |
+	       CM_AFE_CM_START_DATA_MASK;
+
+	if (cmNum == MT8365_CM1) {
+		val |= FIELD_PREP(CM_AFE_CM1_IN_MODE_MASK, fs);
+
+		mask |= CM_AFE_CM1_VUL_SEL |
+			CM_AFE_CM1_IN_MODE_MASK;
+	} else if (cmNum == MT8365_CM2) {
+		if (mt8365_afe_clk_group_48k(rate))
+			val |= FIELD_PREP(CM_AFE_CM2_CLK_SEL, 0);
+		else
+			val |= FIELD_PREP(CM_AFE_CM2_CLK_SEL, 1);
+
+		val |= FIELD_PREP(CM_AFE_CM2_TDM_SEL, 1);
+
+		mask |= CM_AFE_CM2_TDM_SEL |
+			CM_AFE_CM1_IN_MODE_MASK |
+			CM_AFE_CM2_CLK_SEL;
+
+		mt8365_afe_cm2_mux_conn(afe);
+	} else {
+		return -1;
+	}
+
+	regmap_update_bits(afe->regmap, cm_ctrl_reg[cmNum].con0, mask, val);
+
+	mt8365_afe_get_cm_update_cnt(afe, cmNum, rate, channels);
+
+	return 0;
+}
+
+int mt8365_afe_fe_startup(struct snd_pcm_substream *substream,
+			  struct snd_soc_dai *dai)
+{
+	struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+	struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	int memif_num = snd_soc_rtd_to_cpu(rtd, 0)->id;
+	struct mtk_base_afe_memif *memif = &afe->memif[memif_num];
+	int ret;
+
+	memif->substream = substream;
+
+	snd_pcm_hw_constraint_step(substream->runtime, 0,
+				   SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 16);
+
+	snd_soc_set_runtime_hwparams(substream, afe->mtk_afe_hardware);
+
+	ret = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
+	if (ret < 0)
+		dev_err(afe->dev, "snd_pcm_hw_constraint_integer failed\n");
+
+	mt8365_afe_enable_main_clk(afe);
+	return ret;
+}
+
+static void mt8365_afe_fe_shutdown(struct snd_pcm_substream *substream,
+				   struct snd_soc_dai *dai)
+{
+	struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+	struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+	int memif_num = snd_soc_rtd_to_cpu(rtd, 0)->id;
+	struct mtk_base_afe_memif *memif = &afe->memif[memif_num];
+
+	memif->substream = NULL;
+
+	mt8365_afe_disable_main_clk(afe);
+}
+
+static int mt8365_afe_fe_hw_params(struct snd_pcm_substream *substream,
+				   struct snd_pcm_hw_params *params,
+				   struct snd_soc_dai *dai)
+{
+	struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+	struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+	struct mt8365_afe_private *afe_priv = afe->platform_priv;
+	struct mt8365_control_data *ctrl_data = &afe_priv->ctrl_data;
+	int dai_id = snd_soc_rtd_to_cpu(rtd, 0)->id;
+	struct mtk_base_afe_memif *memif = &afe->memif[dai_id];
+	struct mt8365_fe_dai_data *fe_data = &afe_priv->fe_data[dai_id];
+	size_t request_size = params_buffer_bytes(params);
+	unsigned int channels = params_channels(params);
+	unsigned int rate = params_rate(params);
+	unsigned int base_end_offset = 8;
+	int ret, fs;
+
+	dev_info(afe->dev, "%s %s period = %d rate = %d channels = %d\n",
+		 __func__, memif->data->name, params_period_size(params),
+		 rate, channels);
+
+	if (dai_id == MT8365_AFE_MEMIF_VUL2) {
+		if (!ctrl_data->bypass_cm1)
+			/* configure cm1 */
+			mt8365_afe_configure_cm(afe, MT8365_CM1,
+						channels, rate);
+		else
+			regmap_update_bits(afe->regmap, AFE_CM1_CON0,
+					   CM_AFE_CM1_VUL_SEL,
+					   CM_AFE_CM1_VUL_SEL);
+	} else if (dai_id == MT8365_AFE_MEMIF_TDM_IN) {
+		if (!ctrl_data->bypass_cm2)
+			/* configure cm2 */
+			mt8365_afe_configure_cm(afe, MT8365_CM2,
+						channels, rate);
+		else
+			regmap_update_bits(afe->regmap, AFE_CM2_CON0,
+					   CM_AFE_CM2_TDM_SEL,
+					   ~CM_AFE_CM2_TDM_SEL);
+
+		base_end_offset = 4;
+	}
+
+	if (request_size > fe_data->sram_size) {
+		ret = snd_pcm_lib_malloc_pages(substream, request_size);
+		if (ret < 0) {
+			dev_err(afe->dev,
+				"%s %s malloc pages %zu bytes failed %d\n",
+				__func__, memif->data->name, request_size, ret);
+			return ret;
+		}
+
+		fe_data->use_sram = false;
+
+		mt8365_afe_emi_clk_on(afe);
+	} else {
+		struct snd_dma_buffer *dma_buf = &substream->dma_buffer;
+
+		dma_buf->dev.type = SNDRV_DMA_TYPE_DEV;
+		dma_buf->dev.dev = substream->pcm->card->dev;
+		dma_buf->area = (unsigned char *)fe_data->sram_vir_addr;
+		dma_buf->addr = fe_data->sram_phy_addr;
+		dma_buf->bytes = request_size;
+		snd_pcm_set_runtime_buffer(substream, dma_buf);
+
+		fe_data->use_sram = true;
+	}
+
+	memif->phys_buf_addr = lower_32_bits(substream->runtime->dma_addr);
+	memif->buffer_size = substream->runtime->dma_bytes;
+
+	/* start */
+	regmap_write(afe->regmap, memif->data->reg_ofs_base,
+		     memif->phys_buf_addr);
+	/* end */
+	regmap_write(afe->regmap,
+		     memif->data->reg_ofs_base + base_end_offset,
+		     memif->phys_buf_addr + memif->buffer_size - 1);
+
+	/* set channel */
+	if (memif->data->mono_shift >= 0) {
+		unsigned int mono = (params_channels(params) == 1) ? 1 : 0;
+
+		if (memif->data->mono_reg < 0)
+			dev_info(afe->dev, "%s mono_reg is NULL\n", __func__);
+		else
+			regmap_update_bits(afe->regmap, memif->data->mono_reg,
+					   1 << memif->data->mono_shift,
+					   mono << memif->data->mono_shift);
+	}
+
+	/* set rate */
+	if (memif->data->fs_shift < 0)
+		return 0;
+
+	fs = afe->memif_fs(substream, params_rate(params));
+
+	if (fs < 0)
+		return -EINVAL;
+
+	if (memif->data->fs_reg < 0)
+		dev_info(afe->dev, "%s fs_reg is NULL\n", __func__);
+	else
+		regmap_update_bits(afe->regmap, memif->data->fs_reg,
+				   memif->data->fs_maskbit << memif->data->fs_shift,
+				   fs << memif->data->fs_shift);
+
+	return 0;
+}
+
+static int mt8365_afe_fe_hw_free(struct snd_pcm_substream *substream,
+				 struct snd_soc_dai *dai)
+{
+	struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+	struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+	struct mt8365_afe_private *afe_priv = afe->platform_priv;
+	int dai_id = snd_soc_rtd_to_cpu(rtd, 0)->id;
+	struct mtk_base_afe_memif *memif = &afe->memif[dai_id];
+	struct mt8365_fe_dai_data *fe_data = &afe_priv->fe_data[dai_id];
+	int ret = 0;
+
+	if (fe_data->use_sram) {
+		snd_pcm_set_runtime_buffer(substream, NULL);
+	} else {
+		ret = snd_pcm_lib_free_pages(substream);
+
+		mt8365_afe_emi_clk_off(afe);
+	}
+
+	return ret;
+}
+
+static int mt8365_afe_fe_prepare(struct snd_pcm_substream *substream,
+				 struct snd_soc_dai *dai)
+{
+	struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+	struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+	int dai_id = snd_soc_rtd_to_cpu(rtd, 0)->id;
+	struct mtk_base_afe_memif *memif = &afe->memif[dai_id];
+
+	/* set format */
+	if (memif->data->hd_reg >= 0) {
+		switch (substream->runtime->format) {
+		case SNDRV_PCM_FORMAT_S16_LE:
+			regmap_update_bits(afe->regmap, memif->data->hd_reg,
+					   3 << memif->data->hd_shift,
+					   0 << memif->data->hd_shift);
+			break;
+		case SNDRV_PCM_FORMAT_S32_LE:
+			regmap_update_bits(afe->regmap, memif->data->hd_reg,
+					   3 << memif->data->hd_shift,
+					   3 << memif->data->hd_shift);
+
+			if (dai_id == MT8365_AFE_MEMIF_TDM_IN) {
+				regmap_update_bits(afe->regmap,
+						   memif->data->hd_reg,
+						   3 << memif->data->hd_shift,
+						   1 << memif->data->hd_shift);
+				regmap_update_bits(afe->regmap,
+						   memif->data->hd_reg,
+						   1 << memif->data->hd_align_mshift,
+						   1 << memif->data->hd_align_mshift);
+			}
+			break;
+		case SNDRV_PCM_FORMAT_S24_LE:
+			regmap_update_bits(afe->regmap, memif->data->hd_reg,
+					   3 << memif->data->hd_shift,
+					   1 << memif->data->hd_shift);
+			break;
+		default:
+			return -EINVAL;
+		}
+	}
+
+	mt8365_afe_irq_direction_enable(afe, memif->irq_usage,
+					MT8365_AFE_IRQ_DIR_MCU);
+
+	return 0;
+}
+
+int mt8365_afe_fe_trigger(struct snd_pcm_substream *substream, int cmd,
+			  struct snd_soc_dai *dai)
+{
+	struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+	struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+	struct mt8365_afe_private *afe_priv = afe->platform_priv;
+	int dai_id = snd_soc_rtd_to_cpu(rtd, 0)->id;
+	struct mt8365_control_data *ctrl_data = &afe_priv->ctrl_data;
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_RESUME:
+		/* enable channel merge */
+		if (dai_id == MT8365_AFE_MEMIF_VUL2 &&
+		    !ctrl_data->bypass_cm1) {
+			regmap_update_bits(afe->regmap, AFE_CM1_CON0,
+					   CM_AFE_CM_ON, CM_AFE_CM_ON);
+		} else if (dai_id == MT8365_AFE_MEMIF_TDM_IN &&
+			   !ctrl_data->bypass_cm2) {
+			regmap_update_bits(afe->regmap, AFE_CM2_CON0,
+					   CM_AFE_CM_ON, CM_AFE_CM_ON);
+		}
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+		/* disable channel merge */
+		if (dai_id == MT8365_AFE_MEMIF_VUL2 &&
+		    !ctrl_data->bypass_cm1) {
+			regmap_update_bits(afe->regmap, AFE_CM1_CON0,
+					   CM_AFE_CM_ON, ~CM_AFE_CM_ON);
+		} else if (dai_id == MT8365_AFE_MEMIF_TDM_IN &&
+			   !ctrl_data->bypass_cm2) {
+			regmap_update_bits(afe->regmap, AFE_CM2_CON0,
+					   CM_AFE_CM_ON, ~CM_AFE_CM_ON);
+		}
+		break;
+	default:
+		break;
+	}
+
+	return mtk_afe_fe_trigger(substream, cmd, dai);
+}
+
+static int mt8365_afe_hw_gain1_startup(struct snd_pcm_substream *substream,
+				       struct snd_soc_dai *dai)
+{
+	struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+
+	mt8365_afe_enable_main_clk(afe);
+	return 0;
+}
+
+static void mt8365_afe_hw_gain1_shutdown(struct snd_pcm_substream *substream,
+					 struct snd_soc_dai *dai)
+{
+	struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+	struct mt8365_afe_private *afe_priv = afe->platform_priv;
+	struct mt8365_be_dai_data *be =
+		&afe_priv->be_data[dai->id - MT8365_AFE_BACKEND_BASE];
+
+	if (be->prepared[substream->stream]) {
+		regmap_update_bits(afe->regmap, AFE_GAIN1_CON0,
+				   AFE_GAIN1_CON0_EN_MASK, 0);
+		be->prepared[substream->stream] = false;
+	}
+	mt8365_afe_disable_main_clk(afe);
+}
+
+static int mt8365_afe_hw_gain1_prepare(struct snd_pcm_substream *substream,
+				       struct snd_soc_dai *dai)
+{
+	struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+	struct mt8365_afe_private *afe_priv = afe->platform_priv;
+	struct mt8365_be_dai_data *be =
+		&afe_priv->be_data[dai->id - MT8365_AFE_BACKEND_BASE];
+
+	int fs;
+	unsigned int val1 = 0, val2 = 0;
+
+	if (be->prepared[substream->stream]) {
+		dev_info(afe->dev, "%s prepared already\n", __func__);
+		return 0;
+	}
+
+	fs = mt8365_afe_fs_timing(substream->runtime->rate);
+	regmap_update_bits(afe->regmap, AFE_GAIN1_CON0,
+			   AFE_GAIN1_CON0_MODE_MASK, (unsigned int)fs << 4);
+
+	regmap_read(afe->regmap, AFE_GAIN1_CON1, &val1);
+	regmap_read(afe->regmap, AFE_GAIN1_CUR, &val2);
+	if ((val1 & AFE_GAIN1_CON1_MASK) != (val2 & AFE_GAIN1_CUR_MASK))
+		regmap_update_bits(afe->regmap, AFE_GAIN1_CUR,
+				   AFE_GAIN1_CUR_MASK, val1);
+
+	regmap_update_bits(afe->regmap, AFE_GAIN1_CON0,
+			   AFE_GAIN1_CON0_EN_MASK, 1);
+	be->prepared[substream->stream] = true;
+
+	return 0;
+}
+
+static const struct snd_pcm_hardware mt8365_hostless_hardware = {
+	.info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+		 SNDRV_PCM_INFO_MMAP_VALID),
+	.period_bytes_min = 256,
+	.period_bytes_max = 4 * 48 * 1024,
+	.periods_min = 2,
+	.periods_max = 256,
+	.buffer_bytes_max = 8 * 48 * 1024,
+	.fifo_size = 0,
+};
+
+/* dai ops */
+static int mtk_dai_hostless_startup(struct snd_pcm_substream *substream,
+				    struct snd_soc_dai *dai)
+{
+	struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	int ret;
+
+	snd_soc_set_runtime_hwparams(substream, &mt8365_hostless_hardware);
+
+	ret = snd_pcm_hw_constraint_integer(runtime,
+					    SNDRV_PCM_HW_PARAM_PERIODS);
+	if (ret < 0)
+		dev_err(afe->dev, "snd_pcm_hw_constraint_integer failed\n");
+	return ret;
+}
+
+/* FE DAIs */
+static const struct snd_soc_dai_ops mt8365_afe_fe_dai_ops = {
+	.startup	= mt8365_afe_fe_startup,
+	.shutdown	= mt8365_afe_fe_shutdown,
+	.hw_params	= mt8365_afe_fe_hw_params,
+	.hw_free	= mt8365_afe_fe_hw_free,
+	.prepare	= mt8365_afe_fe_prepare,
+	.trigger	= mt8365_afe_fe_trigger,
+};
+
+static const struct snd_soc_dai_ops mt8365_dai_hostless_ops = {
+	.startup = mtk_dai_hostless_startup,
+};
+
+static const struct snd_soc_dai_ops mt8365_afe_hw_gain1_ops = {
+	.startup	= mt8365_afe_hw_gain1_startup,
+	.shutdown	= mt8365_afe_hw_gain1_shutdown,
+	.prepare	= mt8365_afe_hw_gain1_prepare,
+};
+
+static struct snd_soc_dai_driver mt8365_memif_dai_driver[] = {
+	/* FE DAIs: memory intefaces to CPU */
+	{
+		.name = "DL1",
+		.id = MT8365_AFE_MEMIF_DL1,
+		.playback = {
+			.stream_name = "DL1",
+			.channels_min = 1,
+			.channels_max = 2,
+			.rates = SNDRV_PCM_RATE_8000_192000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE |
+				   SNDRV_PCM_FMTBIT_S32_LE,
+		},
+		.ops = &mt8365_afe_fe_dai_ops,
+	}, {
+		.name = "DL2",
+		.id = MT8365_AFE_MEMIF_DL2,
+		.playback = {
+			.stream_name = "DL2",
+			.channels_min = 1,
+			.channels_max = 2,
+			.rates = SNDRV_PCM_RATE_8000_192000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE |
+				   SNDRV_PCM_FMTBIT_S32_LE,
+		},
+		.ops = &mt8365_afe_fe_dai_ops,
+	}, {
+		.name = "TDM_OUT",
+		.id = MT8365_AFE_MEMIF_TDM_OUT,
+		.playback = {
+			.stream_name = "TDM_OUT",
+			.channels_min = 1,
+			.channels_max = 8,
+			.rates = SNDRV_PCM_RATE_8000_192000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE |
+				   SNDRV_PCM_FMTBIT_S32_LE,
+		},
+		.ops = &mt8365_afe_fe_dai_ops,
+	}, {
+		.name = "AWB",
+		.id = MT8365_AFE_MEMIF_AWB,
+		.capture = {
+			.stream_name = "AWB",
+			.channels_min = 1,
+			.channels_max = 2,
+			.rates = SNDRV_PCM_RATE_8000_192000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE |
+				   SNDRV_PCM_FMTBIT_S32_LE,
+		},
+		.ops = &mt8365_afe_fe_dai_ops,
+	}, {
+		.name = "VUL",
+		.id = MT8365_AFE_MEMIF_VUL,
+		.capture = {
+			.stream_name = "VUL",
+			.channels_min = 1,
+			.channels_max = 2,
+			.rates = SNDRV_PCM_RATE_8000_192000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE |
+				   SNDRV_PCM_FMTBIT_S32_LE,
+		},
+		.ops = &mt8365_afe_fe_dai_ops,
+	}, {
+		.name = "VUL2",
+		.id = MT8365_AFE_MEMIF_VUL2,
+		.capture = {
+			.stream_name = "VUL2",
+			.channels_min = 1,
+			.channels_max = 16,
+			.rates = SNDRV_PCM_RATE_8000_192000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE |
+				   SNDRV_PCM_FMTBIT_S32_LE,
+		},
+		.ops = &mt8365_afe_fe_dai_ops,
+	}, {
+		.name = "VUL3",
+		.id = MT8365_AFE_MEMIF_VUL3,
+		.capture = {
+			.stream_name = "VUL3",
+			.channels_min = 1,
+			.channels_max = 2,
+			.rates = SNDRV_PCM_RATE_8000_192000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE |
+				   SNDRV_PCM_FMTBIT_S32_LE,
+		},
+		.ops = &mt8365_afe_fe_dai_ops,
+	}, {
+		.name = "TDM_IN",
+		.id = MT8365_AFE_MEMIF_TDM_IN,
+		.capture = {
+			.stream_name = "TDM_IN",
+			.channels_min = 1,
+			.channels_max = 16,
+			.rates = SNDRV_PCM_RATE_8000_192000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE |
+				   SNDRV_PCM_FMTBIT_S32_LE,
+		},
+		.ops = &mt8365_afe_fe_dai_ops,
+	}, {
+		.name = "Hostless FM DAI",
+		.id = MT8365_AFE_IO_VIRTUAL_FM,
+		.playback = {
+			.stream_name = "Hostless FM DL",
+			.channels_min = 1,
+			.channels_max = 2,
+			.rates = SNDRV_PCM_RATE_8000_192000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE |
+					  SNDRV_PCM_FMTBIT_S24_LE |
+					   SNDRV_PCM_FMTBIT_S32_LE,
+		},
+		.capture = {
+			.stream_name = "Hostless FM UL",
+			.channels_min = 1,
+			.channels_max = 2,
+			.rates = SNDRV_PCM_RATE_8000_192000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE |
+					  SNDRV_PCM_FMTBIT_S24_LE |
+					   SNDRV_PCM_FMTBIT_S32_LE,
+		},
+		.ops = &mt8365_dai_hostless_ops,
+	}, {
+		.name = "HW_GAIN1",
+		.id = MT8365_AFE_IO_HW_GAIN1,
+		.playback = {
+			.stream_name = "HW Gain 1 In",
+			.channels_min = 1,
+			.channels_max = 2,
+			.rates = SNDRV_PCM_RATE_8000_192000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE |
+				   SNDRV_PCM_FMTBIT_S24_LE |
+				   SNDRV_PCM_FMTBIT_S32_LE,
+		},
+		.capture = {
+			.stream_name = "HW Gain 1 Out",
+			.channels_min = 1,
+			.channels_max = 2,
+			.rates = SNDRV_PCM_RATE_8000_192000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE |
+				   SNDRV_PCM_FMTBIT_S24_LE |
+				   SNDRV_PCM_FMTBIT_S32_LE,
+		},
+		.ops = &mt8365_afe_hw_gain1_ops,
+		.symmetric_rate = 1,
+		.symmetric_channels = 1,
+		.symmetric_sample_bits = 1,
+	},
+};
+
+static const struct snd_kcontrol_new mt8365_afe_o00_mix[] = {
+	SOC_DAPM_SINGLE_AUTODISABLE("I05 Switch", AFE_CONN0, 5, 1, 0),
+	SOC_DAPM_SINGLE_AUTODISABLE("I07 Switch", AFE_CONN0, 7, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt8365_afe_o01_mix[] = {
+	SOC_DAPM_SINGLE_AUTODISABLE("I06 Switch", AFE_CONN1, 6, 1, 0),
+	SOC_DAPM_SINGLE_AUTODISABLE("I08 Switch", AFE_CONN1, 8, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt8365_afe_o03_mix[] = {
+	SOC_DAPM_SINGLE_AUTODISABLE("I05 Switch", AFE_CONN3, 5, 1, 0),
+	SOC_DAPM_SINGLE_AUTODISABLE("I07 Switch", AFE_CONN3, 7, 1, 0),
+	SOC_DAPM_SINGLE_AUTODISABLE("I00 Switch", AFE_CONN3, 0, 1, 0),
+	SOC_DAPM_SINGLE_AUTODISABLE("I10 Switch", AFE_CONN3, 10, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt8365_afe_o04_mix[] = {
+	SOC_DAPM_SINGLE_AUTODISABLE("I06 Switch", AFE_CONN4, 6, 1, 0),
+	SOC_DAPM_SINGLE_AUTODISABLE("I08 Switch", AFE_CONN4, 8, 1, 0),
+	SOC_DAPM_SINGLE_AUTODISABLE("I01 Switch", AFE_CONN4, 1, 1, 0),
+	SOC_DAPM_SINGLE_AUTODISABLE("I11 Switch", AFE_CONN4, 11, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt8365_afe_o05_mix[] = {
+	SOC_DAPM_SINGLE_AUTODISABLE("I00 Switch", AFE_CONN5, 0, 1, 0),
+	SOC_DAPM_SINGLE_AUTODISABLE("I03 Switch", AFE_CONN5, 3, 1, 0),
+	SOC_DAPM_SINGLE_AUTODISABLE("I05 Switch", AFE_CONN5, 5, 1, 0),
+	SOC_DAPM_SINGLE_AUTODISABLE("I07 Switch", AFE_CONN5, 7, 1, 0),
+	SOC_DAPM_SINGLE_AUTODISABLE("I09 Switch", AFE_CONN5, 9, 1, 0),
+	SOC_DAPM_SINGLE_AUTODISABLE("I14 Switch", AFE_CONN5, 14, 1, 0),
+	SOC_DAPM_SINGLE_AUTODISABLE("I16 Switch", AFE_CONN5, 16, 1, 0),
+	SOC_DAPM_SINGLE_AUTODISABLE("I18 Switch", AFE_CONN5, 18, 1, 0),
+	SOC_DAPM_SINGLE_AUTODISABLE("I20 Switch", AFE_CONN5, 20, 1, 0),
+	SOC_DAPM_SINGLE_AUTODISABLE("I23 Switch", AFE_CONN5, 23, 1, 0),
+	SOC_DAPM_SINGLE_AUTODISABLE("I10L Switch", AFE_CONN5, 10, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt8365_afe_o06_mix[] = {
+	SOC_DAPM_SINGLE_AUTODISABLE("I01 Switch", AFE_CONN6, 1, 1, 0),
+	SOC_DAPM_SINGLE_AUTODISABLE("I04 Switch", AFE_CONN6, 4, 1, 0),
+	SOC_DAPM_SINGLE_AUTODISABLE("I06 Switch", AFE_CONN6, 6, 1, 0),
+	SOC_DAPM_SINGLE_AUTODISABLE("I08 Switch", AFE_CONN6, 8, 1, 0),
+	SOC_DAPM_SINGLE_AUTODISABLE("I22 Switch", AFE_CONN6, 22, 1, 0),
+	SOC_DAPM_SINGLE_AUTODISABLE("I15 Switch", AFE_CONN6, 15, 1, 0),
+	SOC_DAPM_SINGLE_AUTODISABLE("I17 Switch", AFE_CONN6, 17, 1, 0),
+	SOC_DAPM_SINGLE_AUTODISABLE("I19 Switch", AFE_CONN6, 19, 1, 0),
+	SOC_DAPM_SINGLE_AUTODISABLE("I21 Switch", AFE_CONN6, 21, 1, 0),
+	SOC_DAPM_SINGLE_AUTODISABLE("I24 Switch", AFE_CONN6, 24, 1, 0),
+	SOC_DAPM_SINGLE_AUTODISABLE("I11L Switch", AFE_CONN6, 11, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt8365_afe_o07_mix[] = {
+	SOC_DAPM_SINGLE_AUTODISABLE("I05 Switch", AFE_CONN7, 5, 1, 0),
+	SOC_DAPM_SINGLE_AUTODISABLE("I07 Switch", AFE_CONN7, 7, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt8365_afe_o08_mix[] = {
+	SOC_DAPM_SINGLE_AUTODISABLE("I06 Switch", AFE_CONN8, 6, 1, 0),
+	SOC_DAPM_SINGLE_AUTODISABLE("I08 Switch", AFE_CONN8, 8, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt8365_afe_o09_mix[] = {
+	SOC_DAPM_SINGLE_AUTODISABLE("I00 Switch", AFE_CONN9, 0, 1, 0),
+	SOC_DAPM_SINGLE_AUTODISABLE("I03 Switch", AFE_CONN9, 3, 1, 0),
+	SOC_DAPM_SINGLE_AUTODISABLE("I09 Switch", AFE_CONN9, 9, 1, 0),
+	SOC_DAPM_SINGLE_AUTODISABLE("I14 Switch", AFE_CONN9, 14, 1, 0),
+	SOC_DAPM_SINGLE_AUTODISABLE("I16 Switch", AFE_CONN9, 16, 1, 0),
+	SOC_DAPM_SINGLE_AUTODISABLE("I18 Switch", AFE_CONN9, 18, 1, 0),
+	SOC_DAPM_SINGLE_AUTODISABLE("I20 Switch", AFE_CONN9, 20, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt8365_afe_o10_mix[] = {
+	SOC_DAPM_SINGLE_AUTODISABLE("I01 Switch", AFE_CONN10, 1, 1, 0),
+	SOC_DAPM_SINGLE_AUTODISABLE("I04 Switch", AFE_CONN10, 4, 1, 0),
+	SOC_DAPM_SINGLE_AUTODISABLE("I22 Switch", AFE_CONN10, 22, 1, 0),
+	SOC_DAPM_SINGLE_AUTODISABLE("I15 Switch", AFE_CONN10, 15, 1, 0),
+	SOC_DAPM_SINGLE_AUTODISABLE("I17 Switch", AFE_CONN10, 17, 1, 0),
+	SOC_DAPM_SINGLE_AUTODISABLE("I19 Switch", AFE_CONN10, 19, 1, 0),
+	SOC_DAPM_SINGLE_AUTODISABLE("I21 Switch", AFE_CONN10, 21, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt8365_afe_o11_mix[] = {
+	SOC_DAPM_SINGLE_AUTODISABLE("I00 Switch", AFE_CONN11, 0, 1, 0),
+	SOC_DAPM_SINGLE_AUTODISABLE("I03 Switch", AFE_CONN11, 3, 1, 0),
+	SOC_DAPM_SINGLE_AUTODISABLE("I09 Switch", AFE_CONN11, 9, 1, 0),
+	SOC_DAPM_SINGLE_AUTODISABLE("I14 Switch", AFE_CONN11, 14, 1, 0),
+	SOC_DAPM_SINGLE_AUTODISABLE("I16 Switch", AFE_CONN11, 16, 1, 0),
+	SOC_DAPM_SINGLE_AUTODISABLE("I18 Switch", AFE_CONN11, 18, 1, 0),
+	SOC_DAPM_SINGLE_AUTODISABLE("I20 Switch", AFE_CONN11, 20, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt8365_afe_o12_mix[] = {
+	SOC_DAPM_SINGLE_AUTODISABLE("I01 Switch", AFE_CONN12, 1, 1, 0),
+	SOC_DAPM_SINGLE_AUTODISABLE("I04 Switch", AFE_CONN12, 4, 1, 0),
+	SOC_DAPM_SINGLE_AUTODISABLE("I22 Switch", AFE_CONN12, 22, 1, 0),
+	SOC_DAPM_SINGLE_AUTODISABLE("I15 Switch", AFE_CONN12, 15, 1, 0),
+	SOC_DAPM_SINGLE_AUTODISABLE("I17 Switch", AFE_CONN12, 17, 1, 0),
+	SOC_DAPM_SINGLE_AUTODISABLE("I19 Switch", AFE_CONN12, 19, 1, 0),
+	SOC_DAPM_SINGLE_AUTODISABLE("I21 Switch", AFE_CONN12, 21, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt8365_afe_o13_mix[] = {
+	SOC_DAPM_SINGLE_AUTODISABLE("I00 Switch", AFE_CONN13, 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt8365_afe_o14_mix[] = {
+	SOC_DAPM_SINGLE_AUTODISABLE("I01 Switch", AFE_CONN14, 1, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt8365_afe_o15_mix[] = {
+};
+
+static const struct snd_kcontrol_new mt8365_afe_o16_mix[] = {
+};
+
+static const struct snd_kcontrol_new mt8365_afe_o17_mix[] = {
+	SOC_DAPM_SINGLE_AUTODISABLE("I03 Switch", AFE_CONN17, 3, 1, 0),
+	SOC_DAPM_SINGLE_AUTODISABLE("I14 Switch", AFE_CONN17, 14, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt8365_afe_o18_mix[] = {
+	SOC_DAPM_SINGLE_AUTODISABLE("I04 Switch", AFE_CONN18, 4, 1, 0),
+	SOC_DAPM_SINGLE_AUTODISABLE("I15 Switch", AFE_CONN18, 15, 1, 0),
+	SOC_DAPM_SINGLE_AUTODISABLE("I23 Switch", AFE_CONN18, 23, 1, 0),
+	SOC_DAPM_SINGLE_AUTODISABLE("I25 Switch", AFE_CONN18, 25, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt8365_afe_o19_mix[] = {
+	SOC_DAPM_SINGLE_AUTODISABLE("I04 Switch", AFE_CONN19, 4, 1, 0),
+	SOC_DAPM_SINGLE_AUTODISABLE("I16 Switch", AFE_CONN19, 16, 1, 0),
+	SOC_DAPM_SINGLE_AUTODISABLE("I23 Switch", AFE_CONN19, 23, 1, 0),
+	SOC_DAPM_SINGLE_AUTODISABLE("I24 Switch", AFE_CONN19, 24, 1, 0),
+	SOC_DAPM_SINGLE_AUTODISABLE("I25 Switch", AFE_CONN19, 25, 1, 0),
+	SOC_DAPM_SINGLE_AUTODISABLE("I26 Switch", AFE_CONN19, 26, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt8365_afe_o20_mix[] = {
+	SOC_DAPM_SINGLE_AUTODISABLE("I17 Switch", AFE_CONN20, 17, 1, 0),
+	SOC_DAPM_SINGLE_AUTODISABLE("I24 Switch", AFE_CONN20, 24, 1, 0),
+	SOC_DAPM_SINGLE_AUTODISABLE("I26 Switch", AFE_CONN20, 26, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt8365_afe_o21_mix[] = {
+	SOC_DAPM_SINGLE_AUTODISABLE("I18 Switch", AFE_CONN21, 18, 1, 0),
+	SOC_DAPM_SINGLE_AUTODISABLE("I23 Switch", AFE_CONN21, 23, 1, 0),
+	SOC_DAPM_SINGLE_AUTODISABLE("I25 Switch", AFE_CONN21, 25, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt8365_afe_o22_mix[] = {
+	SOC_DAPM_SINGLE_AUTODISABLE("I19 Switch", AFE_CONN22, 19, 1, 0),
+	SOC_DAPM_SINGLE_AUTODISABLE("I24 Switch", AFE_CONN22, 24, 1, 0),
+	SOC_DAPM_SINGLE_AUTODISABLE("I26 Switch", AFE_CONN22, 26, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt8365_afe_o23_mix[] = {
+	SOC_DAPM_SINGLE_AUTODISABLE("I20 Switch", AFE_CONN23, 20, 1, 0),
+	SOC_DAPM_SINGLE_AUTODISABLE("I23 Switch", AFE_CONN23, 23, 1, 0),
+	SOC_DAPM_SINGLE_AUTODISABLE("I25 Switch", AFE_CONN23, 25, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt8365_afe_o24_mix[] = {
+	SOC_DAPM_SINGLE_AUTODISABLE("I21 Switch", AFE_CONN24, 21, 1, 0),
+	SOC_DAPM_SINGLE_AUTODISABLE("I24 Switch", AFE_CONN24, 24, 1, 0),
+	SOC_DAPM_SINGLE_AUTODISABLE("I26 Switch", AFE_CONN24, 26, 1, 0),
+	SOC_DAPM_SINGLE_AUTODISABLE("I23 Switch", AFE_CONN24, 23, 1, 0),
+	SOC_DAPM_SINGLE_AUTODISABLE("I25 Switch", AFE_CONN24, 25, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt8365_afe_o25_mix[] = {
+	SOC_DAPM_SINGLE_AUTODISABLE("I27 Switch", AFE_CONN25, 27, 1, 0),
+	SOC_DAPM_SINGLE_AUTODISABLE("I23 Switch", AFE_CONN25, 23, 1, 0),
+	SOC_DAPM_SINGLE_AUTODISABLE("I25 Switch", AFE_CONN25, 25, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt8365_afe_o26_mix[] = {
+	SOC_DAPM_SINGLE_AUTODISABLE("I28 Switch", AFE_CONN26, 28, 1, 0),
+	SOC_DAPM_SINGLE_AUTODISABLE("I24 Switch", AFE_CONN26, 24, 1, 0),
+	SOC_DAPM_SINGLE_AUTODISABLE("I26 Switch", AFE_CONN26, 26, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt8365_afe_o27_mix[] = {
+	SOC_DAPM_SINGLE_AUTODISABLE("I05 Switch", AFE_CONN27, 5, 1, 0),
+	SOC_DAPM_SINGLE_AUTODISABLE("I07 Switch", AFE_CONN27, 7, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt8365_afe_o28_mix[] = {
+	SOC_DAPM_SINGLE_AUTODISABLE("I06 Switch", AFE_CONN28, 6, 1, 0),
+	SOC_DAPM_SINGLE_AUTODISABLE("I08 Switch", AFE_CONN28, 8, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt8365_afe_o29_mix[] = {
+	SOC_DAPM_SINGLE_AUTODISABLE("I05 Switch", AFE_CONN29, 5, 1, 0),
+	SOC_DAPM_SINGLE_AUTODISABLE("I07 Switch", AFE_CONN29, 7, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt8365_afe_o30_mix[] = {
+	SOC_DAPM_SINGLE_AUTODISABLE("I06 Switch", AFE_CONN30, 6, 1, 0),
+	SOC_DAPM_SINGLE_AUTODISABLE("I08 Switch", AFE_CONN30, 8, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt8365_afe_o31_mix[] = {
+	SOC_DAPM_SINGLE_AUTODISABLE("I29 Switch", AFE_CONN31, 29, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt8365_afe_o32_mix[] = {
+	SOC_DAPM_SINGLE_AUTODISABLE("I30 Switch", AFE_CONN32, 30, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt8365_afe_o33_mix[] = {
+	SOC_DAPM_SINGLE_AUTODISABLE("I31 Switch", AFE_CONN33, 31, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt8365_afe_o34_mix[] = {
+	SOC_DAPM_SINGLE_AUTODISABLE("I32 Switch", AFE_CONN34_1, 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt8365_afe_o35_mix[] = {
+	SOC_DAPM_SINGLE_AUTODISABLE("I33 Switch", AFE_CONN35_1, 1, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt8365_afe_o36_mix[] = {
+	SOC_DAPM_SINGLE_AUTODISABLE("I34 Switch", AFE_CONN36_1, 2, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_hw_gain1_in_ch1_mix[] = {
+	SOC_DAPM_SINGLE_AUTODISABLE("CONNSYS_I2S_CH1 Switch", AFE_CONN13,
+				    0, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_hw_gain1_in_ch2_mix[] = {
+	SOC_DAPM_SINGLE_AUTODISABLE("CONNSYS_I2S_CH2 Switch", AFE_CONN14,
+				    1, 1, 0),
+};
+
+static int mt8365_afe_cm2_io_input_mux_get(struct snd_kcontrol *kcontrol,
+					   struct snd_ctl_elem_value *ucontrol)
+{
+	ucontrol->value.integer.value[0] = mCM2Input;
+
+	return 0;
+}
+
+static int mt8365_afe_cm2_io_input_mux_put(struct snd_kcontrol *kcontrol,
+					   struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_dapm_context *dapm =
+		snd_soc_dapm_kcontrol_dapm(kcontrol);
+	struct snd_soc_component *comp = snd_soc_dapm_to_component(dapm);
+	struct mtk_base_afe *afe = snd_soc_component_get_drvdata(comp);
+	struct mt8365_afe_private *afe_priv = afe->platform_priv;
+	int ret;
+
+	mCM2Input = ucontrol->value.enumerated.item[0];
+
+	afe_priv->cm2_mux_input = mCM2Input;
+	ret = snd_soc_dapm_put_enum_double(kcontrol, ucontrol);
+
+	return ret;
+}
+
+static const char * const fmhwgain_text[] = {
+	"OPEN", "FM_HW_GAIN_IO"
+};
+
+static const char * const ain_text[] = {
+	"INT ADC", "EXT ADC",
+};
+
+static const char * const vul2_in_input_text[] = {
+	"VUL2_IN_FROM_O17O18", "VUL2_IN_FROM_CM1",
+};
+
+static const char * const mt8365_afe_cm2_mux_text[] = {
+	"OPEN", "FROM_GASRC1_OUT", "FROM_GASRC2_OUT", "FROM_TDM_ASRC_OUT",
+};
+
+static SOC_ENUM_SINGLE_VIRT_DECL(fmhwgain_enum, fmhwgain_text);
+static SOC_ENUM_SINGLE_DECL(ain_enum, AFE_ADDA_TOP_CON0, 0, ain_text);
+static SOC_ENUM_SINGLE_VIRT_DECL(vul2_in_input_enum, vul2_in_input_text);
+static SOC_ENUM_SINGLE_VIRT_DECL(mt8365_afe_cm2_mux_input_enum,
+	mt8365_afe_cm2_mux_text);
+
+static const struct snd_kcontrol_new fmhwgain_mux =
+	SOC_DAPM_ENUM("FM HW Gain Source", fmhwgain_enum);
+
+static const struct snd_kcontrol_new ain_mux =
+	SOC_DAPM_ENUM("AIN Source", ain_enum);
+
+static const struct snd_kcontrol_new vul2_in_input_mux =
+	SOC_DAPM_ENUM("VUL2 Input", vul2_in_input_enum);
+
+static const struct snd_kcontrol_new mt8365_afe_cm2_mux_input_mux =
+	SOC_DAPM_ENUM_EXT("CM2_MUX Source", mt8365_afe_cm2_mux_input_enum,
+			  mt8365_afe_cm2_io_input_mux_get,
+			  mt8365_afe_cm2_io_input_mux_put);
+
+static const struct snd_soc_dapm_widget mt8365_memif_widgets[] = {
+	/* inter-connections */
+	SND_SOC_DAPM_MIXER("I00", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("I01", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("I03", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("I04", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("I05", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("I06", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("I07", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("I08", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("I05L", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("I06L", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("I07L", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("I08L", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("I09", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("I10", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("I11", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("I10L", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("I11L", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("I12", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("I13", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("I14", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("I15", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("I16", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("I17", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("I18", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("I19", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("I20", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("I21", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("I22", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("I23", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("I24", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("I25", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("I26", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("I27", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("I28", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("I29", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("I30", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("I31", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("I32", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("I33", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("I34", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("O00", SND_SOC_NOPM, 0, 0,
+			   mt8365_afe_o00_mix, ARRAY_SIZE(mt8365_afe_o00_mix)),
+	SND_SOC_DAPM_MIXER("O01", SND_SOC_NOPM, 0, 0,
+			   mt8365_afe_o01_mix, ARRAY_SIZE(mt8365_afe_o01_mix)),
+	SND_SOC_DAPM_MIXER("O03", SND_SOC_NOPM, 0, 0,
+			   mt8365_afe_o03_mix, ARRAY_SIZE(mt8365_afe_o03_mix)),
+	SND_SOC_DAPM_MIXER("O04", SND_SOC_NOPM, 0, 0,
+			   mt8365_afe_o04_mix, ARRAY_SIZE(mt8365_afe_o04_mix)),
+	SND_SOC_DAPM_MIXER("O05", SND_SOC_NOPM, 0, 0,
+			   mt8365_afe_o05_mix, ARRAY_SIZE(mt8365_afe_o05_mix)),
+	SND_SOC_DAPM_MIXER("O06", SND_SOC_NOPM, 0, 0,
+			   mt8365_afe_o06_mix, ARRAY_SIZE(mt8365_afe_o06_mix)),
+	SND_SOC_DAPM_MIXER("O07", SND_SOC_NOPM, 0, 0,
+			   mt8365_afe_o07_mix, ARRAY_SIZE(mt8365_afe_o07_mix)),
+	SND_SOC_DAPM_MIXER("O08", SND_SOC_NOPM, 0, 0,
+			   mt8365_afe_o08_mix, ARRAY_SIZE(mt8365_afe_o08_mix)),
+	SND_SOC_DAPM_MIXER("O09", SND_SOC_NOPM, 0, 0,
+			   mt8365_afe_o09_mix, ARRAY_SIZE(mt8365_afe_o09_mix)),
+	SND_SOC_DAPM_MIXER("O10", SND_SOC_NOPM, 0, 0,
+			   mt8365_afe_o10_mix, ARRAY_SIZE(mt8365_afe_o10_mix)),
+	SND_SOC_DAPM_MIXER("O11", SND_SOC_NOPM, 0, 0,
+			   mt8365_afe_o11_mix, ARRAY_SIZE(mt8365_afe_o11_mix)),
+	SND_SOC_DAPM_MIXER("O12", SND_SOC_NOPM, 0, 0,
+			   mt8365_afe_o12_mix, ARRAY_SIZE(mt8365_afe_o12_mix)),
+	SND_SOC_DAPM_MIXER("O13", SND_SOC_NOPM, 0, 0,
+			   mt8365_afe_o13_mix, ARRAY_SIZE(mt8365_afe_o13_mix)),
+	SND_SOC_DAPM_MIXER("O14", SND_SOC_NOPM, 0, 0,
+			   mt8365_afe_o14_mix, ARRAY_SIZE(mt8365_afe_o14_mix)),
+	SND_SOC_DAPM_MIXER("O15", SND_SOC_NOPM, 0, 0,
+			   mt8365_afe_o15_mix, ARRAY_SIZE(mt8365_afe_o15_mix)),
+	SND_SOC_DAPM_MIXER("O16", SND_SOC_NOPM, 0, 0,
+			   mt8365_afe_o16_mix, ARRAY_SIZE(mt8365_afe_o16_mix)),
+	SND_SOC_DAPM_MIXER("O17", SND_SOC_NOPM, 0, 0,
+			   mt8365_afe_o17_mix, ARRAY_SIZE(mt8365_afe_o17_mix)),
+	SND_SOC_DAPM_MIXER("O18", SND_SOC_NOPM, 0, 0,
+			   mt8365_afe_o18_mix, ARRAY_SIZE(mt8365_afe_o18_mix)),
+	SND_SOC_DAPM_MIXER("O19", SND_SOC_NOPM, 0, 0,
+			   mt8365_afe_o19_mix, ARRAY_SIZE(mt8365_afe_o19_mix)),
+	SND_SOC_DAPM_MIXER("O20", SND_SOC_NOPM, 0, 0,
+			   mt8365_afe_o20_mix, ARRAY_SIZE(mt8365_afe_o20_mix)),
+	SND_SOC_DAPM_MIXER("O21", SND_SOC_NOPM, 0, 0,
+			   mt8365_afe_o21_mix, ARRAY_SIZE(mt8365_afe_o21_mix)),
+	SND_SOC_DAPM_MIXER("O22", SND_SOC_NOPM, 0, 0,
+			   mt8365_afe_o22_mix, ARRAY_SIZE(mt8365_afe_o22_mix)),
+	SND_SOC_DAPM_MIXER("O23", SND_SOC_NOPM, 0, 0,
+			   mt8365_afe_o23_mix, ARRAY_SIZE(mt8365_afe_o23_mix)),
+	SND_SOC_DAPM_MIXER("O24", SND_SOC_NOPM, 0, 0,
+			   mt8365_afe_o24_mix, ARRAY_SIZE(mt8365_afe_o24_mix)),
+	SND_SOC_DAPM_MIXER("O25", SND_SOC_NOPM, 0, 0,
+			   mt8365_afe_o25_mix, ARRAY_SIZE(mt8365_afe_o25_mix)),
+	SND_SOC_DAPM_MIXER("O26", SND_SOC_NOPM, 0, 0,
+			   mt8365_afe_o26_mix, ARRAY_SIZE(mt8365_afe_o26_mix)),
+	SND_SOC_DAPM_MIXER("O27", SND_SOC_NOPM, 0, 0,
+			   mt8365_afe_o27_mix, ARRAY_SIZE(mt8365_afe_o27_mix)),
+	SND_SOC_DAPM_MIXER("O28", SND_SOC_NOPM, 0, 0,
+			   mt8365_afe_o28_mix, ARRAY_SIZE(mt8365_afe_o28_mix)),
+	SND_SOC_DAPM_MIXER("O29", SND_SOC_NOPM, 0, 0,
+			   mt8365_afe_o29_mix, ARRAY_SIZE(mt8365_afe_o29_mix)),
+	SND_SOC_DAPM_MIXER("O30", SND_SOC_NOPM, 0, 0,
+			   mt8365_afe_o30_mix, ARRAY_SIZE(mt8365_afe_o30_mix)),
+	SND_SOC_DAPM_MIXER("O31", SND_SOC_NOPM, 0, 0,
+			   mt8365_afe_o31_mix, ARRAY_SIZE(mt8365_afe_o31_mix)),
+	SND_SOC_DAPM_MIXER("O32", SND_SOC_NOPM, 0, 0,
+			   mt8365_afe_o32_mix, ARRAY_SIZE(mt8365_afe_o32_mix)),
+	SND_SOC_DAPM_MIXER("O33", SND_SOC_NOPM, 0, 0,
+			   mt8365_afe_o33_mix, ARRAY_SIZE(mt8365_afe_o33_mix)),
+	SND_SOC_DAPM_MIXER("O34", SND_SOC_NOPM, 0, 0,
+			   mt8365_afe_o34_mix, ARRAY_SIZE(mt8365_afe_o34_mix)),
+	SND_SOC_DAPM_MIXER("O35", SND_SOC_NOPM, 0, 0,
+			   mt8365_afe_o35_mix, ARRAY_SIZE(mt8365_afe_o35_mix)),
+	SND_SOC_DAPM_MIXER("O36", SND_SOC_NOPM, 0, 0,
+			   mt8365_afe_o36_mix, ARRAY_SIZE(mt8365_afe_o36_mix)),
+	SND_SOC_DAPM_MIXER("CM2_Mux IO", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("CM1_IO", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("O17O18", SND_SOC_NOPM, 0, 0, NULL, 0),
+	/* inter-connections */
+	SND_SOC_DAPM_MIXER("HW_GAIN1_IN_CH1", SND_SOC_NOPM, 0, 0,
+			   mtk_hw_gain1_in_ch1_mix,
+			   ARRAY_SIZE(mtk_hw_gain1_in_ch1_mix)),
+	SND_SOC_DAPM_MIXER("HW_GAIN1_IN_CH2", SND_SOC_NOPM, 0, 0,
+			   mtk_hw_gain1_in_ch2_mix,
+			   ARRAY_SIZE(mtk_hw_gain1_in_ch2_mix)),
+
+	SND_SOC_DAPM_INPUT("DL Source"),
+
+	SND_SOC_DAPM_MUX("CM2_Mux_IO Input Mux", SND_SOC_NOPM, 0, 0,
+			 &mt8365_afe_cm2_mux_input_mux),
+
+	SND_SOC_DAPM_MUX("AIN Mux", SND_SOC_NOPM, 0, 0, &ain_mux),
+	SND_SOC_DAPM_MUX("VUL2 Input Mux", SND_SOC_NOPM, 0, 0,
+			 &vul2_in_input_mux),
+
+	SND_SOC_DAPM_MUX("FM HW Gain Mux", SND_SOC_NOPM, 0, 0, &fmhwgain_mux),
+
+	SND_SOC_DAPM_INPUT("HW Gain 1 Out Endpoint"),
+	SND_SOC_DAPM_OUTPUT("HW Gain 1 In Endpoint"),
+};
+
+static const struct snd_soc_dapm_route mt8365_memif_routes[] = {
+	/* downlink */
+	{"I00", NULL, "2ND I2S Capture"},
+	{"I01", NULL, "2ND I2S Capture"},
+	{"I05", NULL, "DL1"},
+	{"I06", NULL, "DL1"},
+	{"I07", NULL, "DL2"},
+	{"I08", NULL, "DL2"},
+
+	{"O03", "I05 Switch", "I05"},
+	{"O04", "I06 Switch", "I06"},
+	{"O00", "I05 Switch", "I05"},
+	{"O01", "I06 Switch", "I06"},
+	{"O07", "I05 Switch", "I05"},
+	{"O08", "I06 Switch", "I06"},
+	{"O27", "I05 Switch", "I05"},
+	{"O28", "I06 Switch", "I06"},
+	{"O29", "I05 Switch", "I05"},
+	{"O30", "I06 Switch", "I06"},
+
+	{"O03", "I07 Switch", "I07"},
+	{"O04", "I08 Switch", "I08"},
+	{"O00", "I07 Switch", "I07"},
+	{"O01", "I08 Switch", "I08"},
+	{"O07", "I07 Switch", "I07"},
+	{"O08", "I08 Switch", "I08"},
+
+	/* uplink */
+	{"AWB", NULL, "O05"},
+	{"AWB", NULL, "O06"},
+	{"VUL", NULL, "O09"},
+	{"VUL", NULL, "O10"},
+	{"VUL3", NULL, "O11"},
+	{"VUL3", NULL, "O12"},
+
+	{"AIN Mux", "EXT ADC", "I2S Capture"},
+	{"I03", NULL, "AIN Mux"},
+	{"I04", NULL, "AIN Mux"},
+
+	{"HW_GAIN1_IN_CH1", "CONNSYS_I2S_CH1", "Hostless FM DL"},
+	{"HW_GAIN1_IN_CH2", "CONNSYS_I2S_CH2", "Hostless FM DL"},
+
+	{"HW Gain 1 In Endpoint", NULL, "HW Gain 1 In"},
+	{"HW Gain 1 Out", NULL, "HW Gain 1 Out Endpoint"},
+	{"HW Gain 1 In", NULL, "HW_GAIN1_IN_CH1"},
+	{"HW Gain 1 In", NULL, "HW_GAIN1_IN_CH2"},
+
+	{"FM HW Gain Mux", "FM_HW_GAIN_IO", "HW Gain 1 Out"},
+	{"Hostless FM UL", NULL, "FM HW Gain Mux"},
+	{"Hostless FM UL", NULL, "FM 2ND I2S Mux"},
+
+	{"O05", "I05 Switch", "I05L"},
+	{"O06", "I06 Switch", "I06L"},
+	{"O05", "I07 Switch", "I07L"},
+	{"O06", "I08 Switch", "I08L"},
+
+	{"O05", "I03 Switch", "I03"},
+	{"O06", "I04 Switch", "I04"},
+	{"O05", "I00 Switch", "I00"},
+	{"O06", "I01 Switch", "I01"},
+	{"O05", "I09 Switch", "I09"},
+	{"O06", "I22 Switch", "I22"},
+	{"O05", "I14 Switch", "I14"},
+	{"O06", "I15 Switch", "I15"},
+	{"O05", "I16 Switch", "I16"},
+	{"O06", "I17 Switch", "I17"},
+	{"O05", "I18 Switch", "I18"},
+	{"O06", "I19 Switch", "I19"},
+	{"O05", "I20 Switch", "I20"},
+	{"O06", "I21 Switch", "I21"},
+	{"O05", "I23 Switch", "I23"},
+	{"O06", "I24 Switch", "I24"},
+
+	{"O09", "I03 Switch", "I03"},
+	{"O10", "I04 Switch", "I04"},
+	{"O09", "I00 Switch", "I00"},
+	{"O10", "I01 Switch", "I01"},
+	{"O09", "I09 Switch", "I09"},
+	{"O10", "I22 Switch", "I22"},
+	{"O09", "I14 Switch", "I14"},
+	{"O10", "I15 Switch", "I15"},
+	{"O09", "I16 Switch", "I16"},
+	{"O10", "I17 Switch", "I17"},
+	{"O09", "I18 Switch", "I18"},
+	{"O10", "I19 Switch", "I19"},
+	{"O09", "I20 Switch", "I20"},
+	{"O10", "I21 Switch", "I21"},
+
+	{"O11", "I03 Switch", "I03"},
+	{"O12", "I04 Switch", "I04"},
+	{"O11", "I00 Switch", "I00"},
+	{"O12", "I01 Switch", "I01"},
+	{"O11", "I09 Switch", "I09"},
+	{"O12", "I22 Switch", "I22"},
+	{"O11", "I14 Switch", "I14"},
+	{"O12", "I15 Switch", "I15"},
+	{"O11", "I16 Switch", "I16"},
+	{"O12", "I17 Switch", "I17"},
+	{"O11", "I18 Switch", "I18"},
+	{"O12", "I19 Switch", "I19"},
+	{"O11", "I20 Switch", "I20"},
+	{"O12", "I21 Switch", "I21"},
+
+	/* CM2_Mux*/
+	{"CM2_Mux IO", NULL, "CM2_Mux_IO Input Mux"},
+
+	/* VUL2 */
+	{"VUL2", NULL, "VUL2 Input Mux"},
+	{"VUL2 Input Mux", "VUL2_IN_FROM_O17O18", "O17O18"},
+	{"VUL2 Input Mux", "VUL2_IN_FROM_CM1", "CM1_IO"},
+
+	{"O17O18", NULL, "O17"},
+	{"O17O18", NULL, "O18"},
+	{"CM1_IO", NULL, "O17"},
+	{"CM1_IO", NULL, "O18"},
+	{"CM1_IO", NULL, "O19"},
+	{"CM1_IO", NULL, "O20"},
+	{"CM1_IO", NULL, "O21"},
+	{"CM1_IO", NULL, "O22"},
+	{"CM1_IO", NULL, "O23"},
+	{"CM1_IO", NULL, "O24"},
+	{"CM1_IO", NULL, "O25"},
+	{"CM1_IO", NULL, "O26"},
+	{"CM1_IO", NULL, "O31"},
+	{"CM1_IO", NULL, "O32"},
+	{"CM1_IO", NULL, "O33"},
+	{"CM1_IO", NULL, "O34"},
+	{"CM1_IO", NULL, "O35"},
+	{"CM1_IO", NULL, "O36"},
+
+	{"O17", "I14 Switch", "I14"},
+	{"O18", "I15 Switch", "I15"},
+	{"O19", "I16 Switch", "I16"},
+	{"O20", "I17 Switch", "I17"},
+	{"O21", "I18 Switch", "I18"},
+	{"O22", "I19 Switch", "I19"},
+	{"O23", "I20 Switch", "I20"},
+	{"O24", "I21 Switch", "I21"},
+	{"O25", "I23 Switch", "I23"},
+	{"O26", "I24 Switch", "I24"},
+	{"O25", "I25 Switch", "I25"},
+	{"O26", "I26 Switch", "I26"},
+
+	{"O17", "I03 Switch", "I03"},
+	{"O18", "I04 Switch", "I04"},
+	{"O18", "I23 Switch", "I23"},
+	{"O18", "I25 Switch", "I25"},
+	{"O19", "I04 Switch", "I04"},
+	{"O19", "I23 Switch", "I23"},
+	{"O19", "I24 Switch", "I24"},
+	{"O19", "I25 Switch", "I25"},
+	{"O19", "I26 Switch", "I26"},
+	{"O20", "I24 Switch", "I24"},
+	{"O20", "I26 Switch", "I26"},
+	{"O21", "I23 Switch", "I23"},
+	{"O21", "I25 Switch", "I25"},
+	{"O22", "I24 Switch", "I24"},
+	{"O22", "I26 Switch", "I26"},
+
+	{"O23", "I23 Switch", "I23"},
+	{"O23", "I25 Switch", "I25"},
+	{"O24", "I24 Switch", "I24"},
+	{"O24", "I26 Switch", "I26"},
+	{"O24", "I23 Switch", "I23"},
+	{"O24", "I25 Switch", "I25"},
+	{"O13", "I00 Switch", "I00"},
+	{"O14", "I01 Switch", "I01"},
+	{"O03", "I10 Switch", "I10"},
+	{"O04", "I11 Switch", "I11"},
+};
+
+static const struct mtk_base_memif_data memif_data[MT8365_AFE_MEMIF_NUM] = {
+	{
+		.name = "DL1",
+		.id = MT8365_AFE_MEMIF_DL1,
+		.reg_ofs_base = AFE_DL1_BASE,
+		.reg_ofs_cur = AFE_DL1_CUR,
+		.fs_reg = AFE_DAC_CON1,
+		.fs_shift = 0,
+		.fs_maskbit = 0xf,
+		.mono_reg = AFE_DAC_CON1,
+		.mono_shift = 21,
+		.hd_reg = AFE_MEMIF_PBUF_SIZE,
+		.hd_shift = 16,
+		.enable_reg = AFE_DAC_CON0,
+		.enable_shift = 1,
+		.msb_reg = -1,
+		.msb_shift = -1,
+		.agent_disable_reg = -1,
+		.agent_disable_shift = -1,
+	}, {
+		.name = "DL2",
+		.id = MT8365_AFE_MEMIF_DL2,
+		.reg_ofs_base = AFE_DL2_BASE,
+		.reg_ofs_cur = AFE_DL2_CUR,
+		.fs_reg = AFE_DAC_CON1,
+		.fs_shift = 4,
+		.fs_maskbit = 0xf,
+		.mono_reg = AFE_DAC_CON1,
+		.mono_shift = 22,
+		.hd_reg = AFE_MEMIF_PBUF_SIZE,
+		.hd_shift = 18,
+		.enable_reg = AFE_DAC_CON0,
+		.enable_shift = 2,
+		.msb_reg = -1,
+		.msb_shift = -1,
+		.agent_disable_reg = -1,
+		.agent_disable_shift = -1,
+	}, {
+		.name = "TDM OUT",
+		.id = MT8365_AFE_MEMIF_TDM_OUT,
+		.reg_ofs_base = AFE_HDMI_OUT_BASE,
+		.reg_ofs_cur = AFE_HDMI_OUT_CUR,
+		.fs_reg = -1,
+		.fs_shift = -1,
+		.fs_maskbit = -1,
+		.mono_reg = -1,
+		.mono_shift = -1,
+		.hd_reg = AFE_MEMIF_PBUF_SIZE,
+		.hd_shift = 28,
+		.enable_reg = AFE_HDMI_OUT_CON0,
+		.enable_shift = 0,
+		.msb_reg = -1,
+		.msb_shift = -1,
+		.agent_disable_reg = -1,
+		.agent_disable_shift = -1,
+	}, {
+		.name = "AWB",
+		.id = MT8365_AFE_MEMIF_AWB,
+		.reg_ofs_base = AFE_AWB_BASE,
+		.reg_ofs_cur = AFE_AWB_CUR,
+		.fs_reg = AFE_DAC_CON1,
+		.fs_shift = 12,
+		.fs_maskbit = 0xf,
+		.mono_reg = AFE_DAC_CON1,
+		.mono_shift = 24,
+		.hd_reg = AFE_MEMIF_PBUF_SIZE,
+		.hd_shift = 20,
+		.enable_reg = AFE_DAC_CON0,
+		.enable_shift = 6,
+		.msb_reg = AFE_MEMIF_MSB,
+		.msb_shift = 17,
+		.agent_disable_reg = -1,
+		.agent_disable_shift = -1,
+	}, {
+		.name = "VUL",
+		.id = MT8365_AFE_MEMIF_VUL,
+		.reg_ofs_base = AFE_VUL_BASE,
+		.reg_ofs_cur = AFE_VUL_CUR,
+		.fs_reg = AFE_DAC_CON1,
+		.fs_shift = 16,
+		.fs_maskbit = 0xf,
+		.mono_reg = AFE_DAC_CON1,
+		.mono_shift = 27,
+		.hd_reg = AFE_MEMIF_PBUF_SIZE,
+		.hd_shift = 22,
+		.enable_reg = AFE_DAC_CON0,
+		.enable_shift = 3,
+		.msb_reg = AFE_MEMIF_MSB,
+		.msb_shift = 20,
+		.agent_disable_reg = -1,
+		.agent_disable_shift = -1,
+	}, {
+		.name = "VUL2",
+		.id = MT8365_AFE_MEMIF_VUL2,
+		.reg_ofs_base = AFE_VUL_D2_BASE,
+		.reg_ofs_cur = AFE_VUL_D2_CUR,
+		.fs_reg = AFE_DAC_CON0,
+		.fs_shift = 20,
+		.fs_maskbit = 0xf,
+		.mono_reg = -1,
+		.mono_shift = -1,
+		.hd_reg = AFE_MEMIF_PBUF_SIZE,
+		.hd_shift = 14,
+		.enable_reg = AFE_DAC_CON0,
+		.enable_shift = 9,
+		.msb_reg = AFE_MEMIF_MSB,
+		.msb_shift = 21,
+		.agent_disable_reg = -1,
+		.agent_disable_shift = -1,
+	}, {
+		.name = "VUL3",
+		.id = MT8365_AFE_MEMIF_VUL3,
+		.reg_ofs_base = AFE_VUL3_BASE,
+		.reg_ofs_cur = AFE_VUL3_CUR,
+		.fs_reg = AFE_DAC_CON1,
+		.fs_shift = 8,
+		.fs_maskbit = 0xf,
+		.mono_reg = AFE_DAC_CON0,
+		.mono_shift = 13,
+		.hd_reg = AFE_MEMIF_PBUF2_SIZE,
+		.hd_shift = 10,
+		.enable_reg = AFE_DAC_CON0,
+		.enable_shift = 12,
+		.msb_reg = AFE_MEMIF_MSB,
+		.msb_shift = 27,
+		.agent_disable_reg = -1,
+		.agent_disable_shift = -1,
+	}, {
+		.name = "TDM IN",
+		.id = MT8365_AFE_MEMIF_TDM_IN,
+		.reg_ofs_base = AFE_HDMI_IN_2CH_BASE,
+		.reg_ofs_cur = AFE_HDMI_IN_2CH_CUR,
+		.fs_reg = -1,
+		.fs_shift = -1,
+		.fs_maskbit = -1,
+		.mono_reg = AFE_HDMI_IN_2CH_CON0,
+		.mono_shift = 1,
+		.hd_reg = AFE_MEMIF_PBUF2_SIZE,
+		.hd_shift = 8,
+		.hd_align_mshift = 5,
+		.enable_reg = AFE_HDMI_IN_2CH_CON0,
+		.enable_shift = 0,
+		.msb_reg = AFE_MEMIF_MSB,
+		.msb_shift = 28,
+		.agent_disable_reg = -1,
+		.agent_disable_shift = -1,
+	},
+};
+
+static const struct mtk_base_irq_data irq_data[MT8365_AFE_IRQ_NUM] = {
+	{
+		.id = MT8365_AFE_IRQ1,
+		.irq_cnt_reg = AFE_IRQ_MCU_CNT1,
+		.irq_cnt_shift = 0,
+		.irq_cnt_maskbit = 0x3ffff,
+		.irq_en_reg = AFE_IRQ_MCU_CON,
+		.irq_en_shift = 0,
+		.irq_fs_reg = AFE_IRQ_MCU_CON,
+		.irq_fs_shift = 4,
+		.irq_fs_maskbit = 0xf,
+		.irq_clr_reg = AFE_IRQ_MCU_CLR,
+		.irq_clr_shift = 0,
+	}, {
+		.id = MT8365_AFE_IRQ2,
+		.irq_cnt_reg = AFE_IRQ_MCU_CNT2,
+		.irq_cnt_shift = 0,
+		.irq_cnt_maskbit = 0x3ffff,
+		.irq_en_reg = AFE_IRQ_MCU_CON,
+		.irq_en_shift = 1,
+		.irq_fs_reg = AFE_IRQ_MCU_CON,
+		.irq_fs_shift = 8,
+		.irq_fs_maskbit = 0xf,
+		.irq_clr_reg = AFE_IRQ_MCU_CLR,
+		.irq_clr_shift = 1,
+	}, {
+		.id = MT8365_AFE_IRQ3,
+		.irq_cnt_reg = AFE_IRQ_MCU_CNT3,
+		.irq_cnt_shift = 0,
+		.irq_cnt_maskbit = 0x3ffff,
+		.irq_en_reg = AFE_IRQ_MCU_CON,
+		.irq_en_shift = 2,
+		.irq_fs_reg = AFE_IRQ_MCU_CON,
+		.irq_fs_shift = 16,
+		.irq_fs_maskbit = 0xf,
+		.irq_clr_reg = AFE_IRQ_MCU_CLR,
+		.irq_clr_shift = 2,
+	}, {
+		.id = MT8365_AFE_IRQ4,
+		.irq_cnt_reg = AFE_IRQ_MCU_CNT4,
+		.irq_cnt_shift = 0,
+		.irq_cnt_maskbit = 0x3ffff,
+		.irq_en_reg = AFE_IRQ_MCU_CON,
+		.irq_en_shift = 3,
+		.irq_fs_reg = AFE_IRQ_MCU_CON,
+		.irq_fs_shift = 20,
+		.irq_fs_maskbit = 0xf,
+		.irq_clr_reg = AFE_IRQ_MCU_CLR,
+		.irq_clr_shift = 3,
+	}, {
+		.id = MT8365_AFE_IRQ5,
+		.irq_cnt_reg = AFE_IRQ_MCU_CNT5,
+		.irq_cnt_shift = 0,
+		.irq_cnt_maskbit = 0x3ffff,
+		.irq_en_reg = AFE_IRQ_MCU_CON2,
+		.irq_en_shift = 3,
+		.irq_fs_reg = -1,
+		.irq_fs_shift = 0,
+		.irq_fs_maskbit = 0x0,
+		.irq_clr_reg = AFE_IRQ_MCU_CLR,
+		.irq_clr_shift = 4,
+	}, {
+		.id = MT8365_AFE_IRQ6,
+		.irq_cnt_reg = -1,
+		.irq_cnt_shift = 0,
+		.irq_cnt_maskbit = 0x0,
+		.irq_en_reg = AFE_IRQ_MCU_CON,
+		.irq_en_shift = 13,
+		.irq_fs_reg = -1,
+		.irq_fs_shift = 0,
+		.irq_fs_maskbit = 0x0,
+		.irq_clr_reg = AFE_IRQ_MCU_CLR,
+		.irq_clr_shift = 5,
+	}, {
+		.id = MT8365_AFE_IRQ7,
+		.irq_cnt_reg = AFE_IRQ_MCU_CNT7,
+		.irq_cnt_shift = 0,
+		.irq_cnt_maskbit = 0x3ffff,
+		.irq_en_reg = AFE_IRQ_MCU_CON,
+		.irq_en_shift = 14,
+		.irq_fs_reg = AFE_IRQ_MCU_CON,
+		.irq_fs_shift = 24,
+		.irq_fs_maskbit = 0xf,
+		.irq_clr_reg = AFE_IRQ_MCU_CLR,
+		.irq_clr_shift = 6,
+	}, {
+		.id = MT8365_AFE_IRQ8,
+		.irq_cnt_reg = AFE_IRQ_MCU_CNT8,
+		.irq_cnt_shift = 0,
+		.irq_cnt_maskbit = 0x3ffff,
+		.irq_en_reg = AFE_IRQ_MCU_CON,
+		.irq_en_shift = 15,
+		.irq_fs_reg = AFE_IRQ_MCU_CON,
+		.irq_fs_shift = 28,
+		.irq_fs_maskbit = 0xf,
+		.irq_clr_reg = AFE_IRQ_MCU_CLR,
+		.irq_clr_shift = 7,
+	}, {
+		.id = MT8365_AFE_IRQ9,
+		.irq_cnt_reg = -1,
+		.irq_cnt_shift = 0,
+		.irq_cnt_maskbit = 0x0,
+		.irq_en_reg = AFE_IRQ_MCU_CON2,
+		.irq_en_shift = 2,
+		.irq_fs_reg = -1,
+		.irq_fs_shift = 0,
+		.irq_fs_maskbit = 0x0,
+		.irq_clr_reg = AFE_IRQ_MCU_CLR,
+		.irq_clr_shift = 8,
+	}, {
+		.id = MT8365_AFE_IRQ10,
+		.irq_cnt_reg = AFE_IRQ_MCU_CNT10,
+		.irq_cnt_shift = 0,
+		.irq_cnt_maskbit = 0x3ffff,
+		.irq_en_reg = AFE_IRQ_MCU_CON2,
+		.irq_en_shift = 4,
+		.irq_fs_reg = -1,
+		.irq_fs_shift = 0,
+		.irq_fs_maskbit = 0x0,
+		.irq_clr_reg = AFE_IRQ_MCU_CLR,
+		.irq_clr_shift = 9,
+	},
+};
+
+static int memif_specified_irqs[MT8365_AFE_MEMIF_NUM] = {
+	[MT8365_AFE_MEMIF_DL1] = MT8365_AFE_IRQ1,
+	[MT8365_AFE_MEMIF_DL2] = MT8365_AFE_IRQ2,
+	[MT8365_AFE_MEMIF_TDM_OUT] = MT8365_AFE_IRQ5,
+	[MT8365_AFE_MEMIF_AWB] = MT8365_AFE_IRQ3,
+	[MT8365_AFE_MEMIF_VUL] = MT8365_AFE_IRQ4,
+	[MT8365_AFE_MEMIF_VUL2] = MT8365_AFE_IRQ7,
+	[MT8365_AFE_MEMIF_VUL3] = MT8365_AFE_IRQ8,
+	[MT8365_AFE_MEMIF_TDM_IN] = MT8365_AFE_IRQ10,
+};
+
+static const struct regmap_config mt8365_afe_regmap_config = {
+	.reg_bits = 32,
+	.reg_stride = 4,
+	.val_bits = 32,
+	.max_register = MAX_REGISTER,
+	.cache_type = REGCACHE_NONE,
+};
+
+static irqreturn_t mt8365_afe_irq_handler(int irq, void *dev_id)
+{
+	struct mtk_base_afe *afe = dev_id;
+	unsigned int reg_value;
+	unsigned int mcu_irq_mask;
+	int i, ret;
+
+	ret = regmap_read(afe->regmap, AFE_IRQ_MCU_STATUS, &reg_value);
+	if (ret) {
+		dev_err_ratelimited(afe->dev, "%s irq status err\n", __func__);
+		reg_value = AFE_IRQ_STATUS_BITS;
+		goto err_irq;
+	}
+
+	ret = regmap_read(afe->regmap, AFE_IRQ_MCU_EN, &mcu_irq_mask);
+	if (ret) {
+		dev_err_ratelimited(afe->dev, "%s irq mcu_en err\n", __func__);
+		reg_value = AFE_IRQ_STATUS_BITS;
+		goto err_irq;
+	}
+
+	/* only clr cpu irq */
+	reg_value &= mcu_irq_mask;
+
+	for (i = 0; i < MT8365_AFE_MEMIF_NUM; i++) {
+		struct mtk_base_afe_memif *memif = &afe->memif[i];
+		struct mtk_base_afe_irq *mcu_irq;
+
+		if (memif->irq_usage < 0)
+			continue;
+
+		mcu_irq = &afe->irqs[memif->irq_usage];
+
+		if (!(reg_value & (1 << mcu_irq->irq_data->irq_clr_shift)))
+			continue;
+
+		snd_pcm_period_elapsed(memif->substream);
+	}
+
+err_irq:
+	/* clear irq */
+	regmap_write(afe->regmap, AFE_IRQ_MCU_CLR,
+		     reg_value & AFE_IRQ_STATUS_BITS);
+
+	return IRQ_HANDLED;
+}
+
+static int __maybe_unused mt8365_afe_runtime_suspend(struct device *dev)
+{
+	return 0;
+}
+
+static int mt8365_afe_runtime_resume(struct device *dev)
+{
+	return 0;
+}
+
+static int __maybe_unused mt8365_afe_suspend(struct device *dev)
+{
+	struct mtk_base_afe *afe = dev_get_drvdata(dev);
+	struct regmap *regmap = afe->regmap;
+	int i;
+
+	mt8365_afe_enable_main_clk(afe);
+
+	if (!afe->reg_back_up)
+		afe->reg_back_up =
+			devm_kcalloc(dev, afe->reg_back_up_list_num,
+				     sizeof(unsigned int), GFP_KERNEL);
+
+	for (i = 0; i < afe->reg_back_up_list_num; i++)
+		regmap_read(regmap, afe->reg_back_up_list[i],
+			    &afe->reg_back_up[i]);
+
+	mt8365_afe_disable_main_clk(afe);
+
+	return 0;
+}
+
+static int __maybe_unused mt8365_afe_resume(struct device *dev)
+{
+	struct mtk_base_afe *afe = dev_get_drvdata(dev);
+	struct regmap *regmap = afe->regmap;
+	int i = 0;
+
+	if (!afe->reg_back_up)
+		return 0;
+
+	mt8365_afe_enable_main_clk(afe);
+
+	for (i = 0; i < afe->reg_back_up_list_num; i++)
+		regmap_write(regmap, afe->reg_back_up_list[i],
+			     afe->reg_back_up[i]);
+
+	mt8365_afe_disable_main_clk(afe);
+
+	return 0;
+}
+
+static int __maybe_unused mt8365_afe_dev_runtime_suspend(struct device *dev)
+{
+	struct mtk_base_afe *afe = dev_get_drvdata(dev);
+
+	if (pm_runtime_status_suspended(dev) || afe->suspended)
+		return 0;
+
+	mt8365_afe_suspend(dev);
+	afe->suspended = true;
+	return 0;
+}
+
+static int __maybe_unused mt8365_afe_dev_runtime_resume(struct device *dev)
+{
+	struct mtk_base_afe *afe = dev_get_drvdata(dev);
+
+	if (pm_runtime_status_suspended(dev) || !afe->suspended)
+		return 0;
+
+	mt8365_afe_resume(dev);
+	afe->suspended = false;
+	return 0;
+}
+
+static int mt8365_afe_init_registers(struct mtk_base_afe *afe)
+{
+	size_t i;
+
+	static struct {
+		unsigned int reg;
+		unsigned int mask;
+		unsigned int val;
+	} init_regs[] = {
+		{ AFE_CONN_24BIT, GENMASK(31, 0), GENMASK(31, 0) },
+		{ AFE_CONN_24BIT_1, GENMASK(21, 0), GENMASK(21, 0) },
+	};
+
+	mt8365_afe_enable_main_clk(afe);
+
+	for (i = 0; i < ARRAY_SIZE(init_regs); i++)
+		regmap_update_bits(afe->regmap, init_regs[i].reg,
+				   init_regs[i].mask, init_regs[i].val);
+
+	mt8365_afe_disable_main_clk(afe);
+
+	return 0;
+}
+
+static int mt8365_dai_memif_register(struct mtk_base_afe *afe)
+{
+	struct mtk_base_afe_dai *dai;
+
+	dai = devm_kzalloc(afe->dev, sizeof(*dai), GFP_KERNEL);
+	if (!dai)
+		return -ENOMEM;
+
+	list_add(&dai->list, &afe->sub_dais);
+
+	dai->dai_drivers = mt8365_memif_dai_driver;
+	dai->num_dai_drivers = ARRAY_SIZE(mt8365_memif_dai_driver);
+
+	dai->dapm_widgets = mt8365_memif_widgets;
+	dai->num_dapm_widgets = ARRAY_SIZE(mt8365_memif_widgets);
+	dai->dapm_routes = mt8365_memif_routes;
+	dai->num_dapm_routes = ARRAY_SIZE(mt8365_memif_routes);
+	return 0;
+}
+
+typedef int (*dai_register_cb)(struct mtk_base_afe *);
+static const dai_register_cb dai_register_cbs[] = {
+	mt8365_dai_pcm_register,
+	mt8365_dai_i2s_register,
+	mt8365_dai_adda_register,
+	mt8365_dai_dmic_register,
+	mt8365_dai_memif_register,
+};
+
+static int mt8365_afe_pcm_dev_probe(struct platform_device *pdev)
+{
+	struct mtk_base_afe *afe;
+	struct mt8365_afe_private *afe_priv;
+	struct device *dev;
+	int ret, i, sel_irq;
+	unsigned int irq_id;
+	struct resource *res;
+
+	afe = devm_kzalloc(&pdev->dev, sizeof(*afe), GFP_KERNEL);
+	if (!afe)
+		return -ENOMEM;
+	platform_set_drvdata(pdev, afe);
+
+	afe->platform_priv = devm_kzalloc(&pdev->dev, sizeof(*afe_priv),
+					  GFP_KERNEL);
+	if (!afe->platform_priv)
+		return -ENOMEM;
+
+	afe_priv = afe->platform_priv;
+	afe->dev = &pdev->dev;
+	dev = afe->dev;
+
+	spin_lock_init(&afe_priv->afe_ctrl_lock);
+	mutex_init(&afe_priv->afe_clk_mutex);
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	afe->base_addr = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(afe->base_addr))
+		return PTR_ERR(afe->base_addr);
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+	if (res) {
+		afe_priv->afe_sram_vir_addr =
+			devm_ioremap_resource(&pdev->dev, res);
+		if (!IS_ERR(afe_priv->afe_sram_vir_addr)) {
+			afe_priv->afe_sram_phy_addr = res->start;
+			afe_priv->afe_sram_size = resource_size(res);
+		}
+	}
+
+	/* initial audio related clock */
+	ret = mt8365_afe_init_audio_clk(afe);
+	if (ret)
+		return dev_err_probe(afe->dev, ret, "mt8365_afe_init_audio_clk fail\n");
+
+	afe->regmap = devm_regmap_init_mmio_clk(&pdev->dev, "top_audio_sel",
+						afe->base_addr,
+						&mt8365_afe_regmap_config);
+	if (IS_ERR(afe->regmap))
+		return PTR_ERR(afe->regmap);
+
+	/* memif % irq initialize*/
+	afe->memif_size = MT8365_AFE_MEMIF_NUM;
+	afe->memif = devm_kcalloc(afe->dev, afe->memif_size,
+				  sizeof(*afe->memif), GFP_KERNEL);
+	if (!afe->memif)
+		return -ENOMEM;
+
+	afe->irqs_size = MT8365_AFE_IRQ_NUM;
+	afe->irqs = devm_kcalloc(afe->dev, afe->irqs_size,
+				 sizeof(*afe->irqs), GFP_KERNEL);
+	if (!afe->irqs)
+		return -ENOMEM;
+
+	for (i = 0; i < afe->irqs_size; i++)
+		afe->irqs[i].irq_data = &irq_data[i];
+
+	irq_id = platform_get_irq(pdev, 0);
+	if (!irq_id) {
+		dev_err_probe(afe->dev, irq_id, "np %s no irq\n", afe->dev->of_node->name);
+		return -ENXIO;
+	}
+	ret = devm_request_irq(afe->dev, irq_id, mt8365_afe_irq_handler,
+			       0, "Afe_ISR_Handle", (void *)afe);
+	if (ret)
+		return dev_err_probe(afe->dev, ret, "could not request_irq\n");
+
+	/* init sub_dais */
+	INIT_LIST_HEAD(&afe->sub_dais);
+
+	for (i = 0; i < ARRAY_SIZE(dai_register_cbs); i++) {
+		ret = dai_register_cbs[i](afe);
+		if (ret) {
+			dev_warn(afe->dev, "dai register i %d fail, ret %d\n",
+				 i, ret);
+			return ret;
+		}
+	}
+
+	/* init dai_driver and component_driver */
+	ret = mtk_afe_combine_sub_dai(afe);
+	if (ret) {
+		dev_warn(afe->dev, "mtk_afe_combine_sub_dai fail, ret %d\n",
+			 ret);
+		return ret;
+	}
+
+	for (i = 0; i < afe->memif_size; i++) {
+		afe->memif[i].data = &memif_data[i];
+		sel_irq = memif_specified_irqs[i];
+		if (sel_irq >= 0) {
+			afe->memif[i].irq_usage = sel_irq;
+			afe->memif[i].const_irq = 1;
+			afe->irqs[sel_irq].irq_occupyed = true;
+		} else {
+			afe->memif[i].irq_usage = -1;
+		}
+	}
+
+	afe->mtk_afe_hardware = &mt8365_afe_hardware;
+	afe->memif_fs = mt8365_memif_fs;
+	afe->irq_fs = mt8365_irq_fs;
+
+	ret = devm_pm_runtime_enable(&pdev->dev);
+	if (ret)
+		return ret;
+
+	pm_runtime_get_sync(&pdev->dev);
+	afe->reg_back_up_list = mt8365_afe_backup_list;
+	afe->reg_back_up_list_num = ARRAY_SIZE(mt8365_afe_backup_list);
+	afe->runtime_resume = mt8365_afe_runtime_resume;
+	afe->runtime_suspend = mt8365_afe_runtime_suspend;
+
+	/* open afe pdn for dapm read/write audio register */
+	mt8365_afe_enable_top_cg(afe, MT8365_TOP_CG_AFE);
+
+	/* Set 26m parent clk */
+	mt8365_afe_set_clk_parent(afe,
+				  afe_priv->clocks[MT8365_CLK_TOP_AUD_SEL],
+				  afe_priv->clocks[MT8365_CLK_CLK26M]);
+
+	ret = devm_snd_soc_register_component(&pdev->dev,
+					      &mtk_afe_pcm_platform,
+					      afe->dai_drivers,
+					      afe->num_dai_drivers);
+	if (ret) {
+		dev_warn(dev, "err_platform\n");
+		return ret;
+	}
+
+	mt8365_afe_init_registers(afe);
+
+	return 0;
+}
+
+static void mt8365_afe_pcm_dev_remove(struct platform_device *pdev)
+{
+	struct mtk_base_afe *afe = platform_get_drvdata(pdev);
+
+	mt8365_afe_disable_top_cg(afe, MT8365_TOP_CG_AFE);
+
+	pm_runtime_disable(&pdev->dev);
+	if (!pm_runtime_status_suspended(&pdev->dev))
+		mt8365_afe_runtime_suspend(&pdev->dev);
+}
+
+static const struct of_device_id mt8365_afe_pcm_dt_match[] = {
+	{ .compatible = "mediatek,mt8365-afe-pcm", },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, mt8365_afe_pcm_dt_match);
+
+static const struct dev_pm_ops mt8365_afe_pm_ops = {
+	SET_RUNTIME_PM_OPS(mt8365_afe_dev_runtime_suspend,
+			   mt8365_afe_dev_runtime_resume, NULL)
+	SET_SYSTEM_SLEEP_PM_OPS(mt8365_afe_suspend,
+				mt8365_afe_resume)
+};
+
+static struct platform_driver mt8365_afe_pcm_driver = {
+	.driver = {
+		   .name = "mt8365-afe-pcm",
+		   .of_match_table = mt8365_afe_pcm_dt_match,
+		   .pm = &mt8365_afe_pm_ops,
+	},
+	.probe = mt8365_afe_pcm_dev_probe,
+	.remove_new = mt8365_afe_pcm_dev_remove,
+};
+
+module_platform_driver(mt8365_afe_pcm_driver);
+
+MODULE_DESCRIPTION("MediaTek ALSA SoC AFE platform driver");
+MODULE_AUTHOR("Jia Zeng <jia.zeng at mediatek.com>");
+MODULE_AUTHOR("Alexandre Mergnat <amergnat at baylibre.com>");
+MODULE_LICENSE("GPL");

-- 
2.25.1




More information about the linux-arm-kernel mailing list