diff --git a/arch/arm64/configs/defconfig b/arch/arm64/configs/defconfig index b99d72bd959a8..508fb4e31d24b 100644 --- a/arch/arm64/configs/defconfig +++ b/arch/arm64/configs/defconfig @@ -1112,6 +1112,7 @@ CONFIG_SND_SOC_SOF_OF=m CONFIG_SND_SOC_SOF_MTK_TOPLEVEL=y CONFIG_SND_SOC_SOF_MT8186=m CONFIG_SND_SOC_SOF_MT8195=m +CONFIG_SND_SOC_SOF_MT8901=m CONFIG_SND_SUN8I_CODEC=m CONFIG_SND_SUN8I_CODEC_ANALOG=m CONFIG_SND_SUN50I_CODEC_ANALOG=m diff --git a/debian.nvidia-bos/config/annotations b/debian.nvidia-bos/config/annotations index cf6d9d8c55694..a3954df2c9522 100644 --- a/debian.nvidia-bos/config/annotations +++ b/debian.nvidia-bos/config/annotations @@ -279,6 +279,27 @@ CONFIG_SENSORS_SPD5118 note<'Enable SPD5118 temperature CONFIG_SFC_CXL policy<{'amd64': 'n', 'arm64': 'n'}> CONFIG_SFC_CXL note<'Solarflare SFC9100-family CXL Type-2 device support; not needed for NVIDIA platforms'> +CONFIG_SND_SOC_MT8901 policy<{'arm64': 'm'}> +CONFIG_SND_SOC_MT8901 note<'MediaTek MT8901 AFE platform driver (N1x audio).'> + +CONFIG_SND_SOC_MT8901_LEGACY_SDW_MACH policy<{'arm64': 'm'}> +CONFIG_SND_SOC_MT8901_LEGACY_SDW_MACH note<'MT8901 SoundWire machine driver (N1x audio).'> + +CONFIG_SND_SOC_MTK_SDCA policy<{'arm64': 'm'}> +CONFIG_SND_SOC_MTK_SDCA note<'SDCA support for codecs on the MediaTek SoundWire master (N1x audio).'> + +CONFIG_SND_SOC_SDCA policy<{'amd64': 'm', 'arm64': 'm'}> +CONFIG_SND_SOC_SDCA note<'SDCA framework, selected by SND_SOC_MTK_SDCA; the SDCA_* and SDW_UTILS lines below are its derived symbols.'> + +CONFIG_SND_SOC_SOF_MT8901 policy<{'arm64': 'm'}> +CONFIG_SND_SOC_SOF_MT8901 note<'MediaTek MT8901 ACPI ADSP power-management driver (N1x audio).'> + +CONFIG_SOUNDWIRE_MTK policy<{'arm64': 'm'}> +CONFIG_SOUNDWIRE_MTK note<'MediaTek SoundWire manager (N1x audio).'> + +CONFIG_SOUNDWIRE_MTK_MT8901_CLK policy<{'arm64': 'y'}> +CONFIG_SOUNDWIRE_MTK_MT8901_CLK note<'MT8901 SoundWire clock/power control; selected by the machine driver.'> + CONFIG_SPI_TEGRA210_QUAD policy<{'arm64': 'y'}> CONFIG_SPI_TEGRA210_QUAD note<'Ensures the TPM is available before the IMA driver initializes'> @@ -306,7 +327,6 @@ CONFIG_VFIO_CXL_CORE note<'Enable VFIO CXL core for C CONFIG_VFIO_IOMMU_TYPE1 policy<{'amd64': 'm', 'arm64': '-'}> CONFIG_VFIO_IOMMU_TYPE1 note<'LP: #2095028'> - # ---- Annotations without notes ---- CONFIG_ARCH_HAS_CPU_RESCTRL policy<{'amd64': 'y', 'arm64': 'y'}> @@ -317,3 +337,9 @@ CONFIG_MTD_NAND_CORE policy<{'amd64': 'm', 'arm64': ' CONFIG_PROC_CPU_RESCTRL policy<{'amd64': 'y', 'arm64': 'y'}> CONFIG_RESCTRL_IOMMU policy<{'arm64': 'y'}> CONFIG_RESCTRL_RMID_DEPENDS_ON_CLOSID policy<{'arm64': 'y'}> +CONFIG_SND_SOC_SDCA_CLASS policy<{'amd64': 'm', 'arm64': 'n'}> +CONFIG_SND_SOC_SDCA_FDL policy<{'amd64': 'y', 'arm64': 'y'}> +CONFIG_SND_SOC_SDCA_HID policy<{'amd64': 'y', 'arm64': 'y'}> +CONFIG_SND_SOC_SDCA_IRQ policy<{'amd64': 'y', 'arm64': 'y'}> +CONFIG_SND_SOC_SDW_UTILS policy<{'amd64': 'm', 'arm64': 'm'}> +CONFIG_SND_SOC_TAS2783_SDW policy<{'amd64': 'm', 'arm64': 'n'}> diff --git a/drivers/soundwire/Kconfig b/drivers/soundwire/Kconfig index 196a7daaabdb8..88223c0e0d03c 100644 --- a/drivers/soundwire/Kconfig +++ b/drivers/soundwire/Kconfig @@ -57,6 +57,28 @@ config SOUNDWIRE_QCOM enable this config option to get the SoundWire support for that device +config SOUNDWIRE_MTK + tristate "MediaTek SoundWire Master driver" + select SOUNDWIRE_GENERIC_ALLOCATION + depends on SND_SOC && ACPI + depends on PINCTRL_MTK_PARIS + select SND_SOC_ACPI + help + SoundWire MediaTek Master driver. + If you have an MediaTek platform which has a SoundWire Master then + enable this config option to get the SoundWire support for that + device. + +config SOUNDWIRE_MTK_MT8901_CLK + bool "MediaTek MT8901 clock control for SoundWire" + depends on SOUNDWIRE_MTK + depends on MTK_POWER_WRAP + help + This enables the MT8901 clock and power control for the MediaTek + SoundWire Master driver. + This symbol is normally selected automatically by the MT8901 + SoundWire machine driver and does not need to be chosen directly. + config SOUNDWIRE_GENERIC_ALLOCATION tristate diff --git a/drivers/soundwire/Makefile b/drivers/soundwire/Makefile index e80a2c2cf3e71..9663e80501c54 100644 --- a/drivers/soundwire/Makefile +++ b/drivers/soundwire/Makefile @@ -36,3 +36,6 @@ obj-$(CONFIG_SOUNDWIRE_INTEL) += soundwire-intel.o #Qualcomm driver soundwire-qcom-y := qcom.o obj-$(CONFIG_SOUNDWIRE_QCOM) += soundwire-qcom.o + +#MediaTek driver +obj-$(CONFIG_SOUNDWIRE_MTK) += mediatek/ diff --git a/drivers/soundwire/mediatek/Makefile b/drivers/soundwire/mediatek/Makefile new file mode 100644 index 0000000000000..d82e26435b666 --- /dev/null +++ b/drivers/soundwire/mediatek/Makefile @@ -0,0 +1,11 @@ +# SPDX-License-Identifier: GPL-2.0-only +# +# Makefile for MediaTek SoundWire driver +# + +# for the soundwire core's internal bus.h (sdw_clear_slave_status) +ccflags-y := -I$(srctree)/drivers/soundwire + +soundwire-mtk-y := mtk-sdw.o mtk-sdw-top.o mtk-sdw-core.o mtk-sdw-mach.o mtk-sdw-chipglue.o +soundwire-mtk-$(CONFIG_SOUNDWIRE_MTK_MT8901_CLK) += mt8901-afe-clk.o +obj-$(CONFIG_SOUNDWIRE_MTK) += soundwire-mtk.o diff --git a/drivers/soundwire/mediatek/mt8901-afe-clk.c b/drivers/soundwire/mediatek/mt8901-afe-clk.c new file mode 100644 index 0000000000000..2e390e195e99a --- /dev/null +++ b/drivers/soundwire/mediatek/mt8901-afe-clk.c @@ -0,0 +1,985 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * mt8901-afe-clk.c -- MediaTek 8901 afe clock ctrl + * + * Copyright (c) 2026 MediaTek Inc. + * Author: Trevor Wu + * Weiyi Hsieh + */ + +#include +#include +#include "mtk-sdw-common.h" +#include "mt8901-afe-clk.h" +#include + +#define AUDIO_ENGEN_CON0_USER1 (0x0018) +#define AUDIO_ENGEN_CON0_APLL2_MASK BIT(3) +#define AUDIO_ENGEN_CON0_APLL1_MASK BIT(2) +#define AUDIO_ENGEN_CON0_26M_MASK BIT(0) + +static const unsigned int mt8901_sdw_common_clk_muxes[] = { + AFE_CLK_VLP_CKSYS_TOP_AUDIO_H_SEL, + AFE_CLK_VLP_CKSYS_TOP_AUD_ENGEN2_SEL, + AFE_CLK_VLP_CKSYS_TOP_AUD_INTBUS_SEL, + AFE_CLK_VLP_CKSYS_TOP_VADSP_SEL, +}; + +static const unsigned int mt8901_sdw0_main_clk_muxes[] = { + AFE_CLK_VLP_CKSYS_TOP_AUD_SOUNDWIRE0_SEL, + AFE_CLK_VLP_CKSYS_TOP_AUD_SOUNDWIRE0_PHY_SEL, + AFE_CLK_VLP_CKSYS_TOP_VADSP_SEL, +}; + +static const unsigned int mt8901_sdw1_main_clk_muxes[] = { + AFE_CLK_VLP_CKSYS_TOP_AUD_SOUNDWIRE1_SEL, + AFE_CLK_VLP_CKSYS_TOP_AUD_SOUNDWIRE1_PHY_SEL, + AFE_CLK_VLP_CKSYS_TOP_VADSP_SEL, +}; + +static const unsigned int mt8901_apll_top_con_cgs[] = { + AUD_CG_APLL1_CK, + AUD_CG_APLL2_CK, + AUD_CG_APLL_TUNER1, + AUD_CG_APLL_TUNER2, +}; + +static const struct { + unsigned int mux; + const char *parent_name; + unsigned int level; +} mt8901_clk_parent_map[] = { + { AFE_CLK_VLP_CKSYS_TOP_AUDIO_H_SEL, + CLK_PARENT_TCX_26M_MX9_CK_NAME, 0 }, + { AFE_CLK_VLP_CKSYS_TOP_AUDIO_H_SEL, + CLK_PARENT_OSC_D20_NAME, 1 }, + { AFE_CLK_VLP_CKSYS_TOP_AUDIO_H_SEL, + CLK_PARENT_APLL1_D2_NAME, 2 }, + { AFE_CLK_VLP_CKSYS_TOP_AUDIO_H_SEL, + CLK_PARENT_APLL2_D2_NAME, 3 }, + + { AFE_CLK_VLP_CKSYS_TOP_AUD_ENGEN1_SEL, + CLK_PARENT_TCX_26M_MX9_CK_NAME, 0 }, + { AFE_CLK_VLP_CKSYS_TOP_AUD_ENGEN1_SEL, + CLK_PARENT_OSC_D20_NAME, 1 }, + { AFE_CLK_VLP_CKSYS_TOP_AUD_ENGEN1_SEL, + CLK_PARENT_APLL1_D8_NAME, 2 }, + { AFE_CLK_VLP_CKSYS_TOP_AUD_ENGEN1_SEL, + CLK_PARENT_APLL1_D4_NAME, 3 }, + + { AFE_CLK_VLP_CKSYS_TOP_AUD_ENGEN2_SEL, + CLK_PARENT_TCX_26M_MX9_CK_NAME, 0 }, + { AFE_CLK_VLP_CKSYS_TOP_AUD_ENGEN2_SEL, + CLK_PARENT_OSC_D20_NAME, 1 }, + { AFE_CLK_VLP_CKSYS_TOP_AUD_ENGEN2_SEL, + CLK_PARENT_APLL2_D8_NAME, 2 }, + { AFE_CLK_VLP_CKSYS_TOP_AUD_ENGEN2_SEL, + CLK_PARENT_APLL2_D4_NAME, 3 }, + + { AFE_CLK_VLP_CKSYS_TOP_AUD_INTBUS_SEL, + CLK_PARENT_TCX_26M_MX9_CK_NAME, 0 }, + { AFE_CLK_VLP_CKSYS_TOP_AUD_INTBUS_SEL, + CLK_PARENT_OSC_D20_NAME, 1 }, + { AFE_CLK_VLP_CKSYS_TOP_AUD_INTBUS_SEL, + CLK_PARENT_MAINPLL2_D7_D4_NAME, 2 }, + { AFE_CLK_VLP_CKSYS_TOP_AUD_INTBUS_SEL, + CLK_PARENT_MAINPLL2_D4_D4_NAME, 3 }, + + { AFE_CLK_VLP_CKSYS_TOP_AUD_1_SEL, + CLK_PARENT_TCX_26M_MX9_CK_NAME, 0 }, + { AFE_CLK_VLP_CKSYS_TOP_AUD_1_SEL, + CLK_PARENT_APLL1_NAME, 1 }, + + { AFE_CLK_VLP_CKSYS_TOP_AUD_2_SEL, + CLK_PARENT_TCX_26M_MX9_CK_NAME, 0 }, + { AFE_CLK_VLP_CKSYS_TOP_AUD_2_SEL, + CLK_PARENT_APLL2_NAME, 1 }, +}; + +static const struct { + unsigned int mux; + const char *idle_parent_name; + const char *active_parent_name; +} idle_clk_muxes[] = { + { AFE_CLK_VLP_CKSYS_TOP_AUDIO_H_SEL, + CLK_PARENT_OSC_D20_NAME, CLK_PARENT_APLL2_D2_NAME }, + { AFE_CLK_VLP_CKSYS_TOP_AUD_INTBUS_SEL, + CLK_PARENT_OSC_D20_NAME, CLK_PARENT_MAINPLL2_D4_D4_NAME }, + { AFE_CLK_VLP_CKSYS_TOP_AUD_1_SEL, + CLK_PARENT_TCX_26M_MX9_CK_NAME, CLK_PARENT_APLL1_NAME }, + { AFE_CLK_VLP_CKSYS_TOP_AUD_2_SEL, + CLK_PARENT_TCX_26M_MX9_CK_NAME, CLK_PARENT_APLL2_NAME }, + { AFE_CLK_VLP_CKSYS_TOP_AUD_ENGEN1_SEL, + CLK_PARENT_OSC_D20_NAME, CLK_PARENT_APLL1_D4_NAME }, + { AFE_CLK_VLP_CKSYS_TOP_AUD_ENGEN2_SEL, + CLK_PARENT_OSC_D20_NAME, CLK_PARENT_APLL2_D4_NAME }, +}; + +struct sdw_clk_parent_info { + unsigned int mux; + const char *apll_parent_name; + const char *lp_parent_name; +}; + +struct sdw_clk_parent_info sdw0_clk_parent_info[] = { + { AFE_CLK_VLP_CKSYS_TOP_AUD_SOUNDWIRE0_SEL, + CLK_PARENT_APLL2_D8_NAME, + CLK_PARENT_OSC_D20_NAME}, + { AFE_CLK_VLP_CKSYS_TOP_AUD_SOUNDWIRE0_PHY_SEL, + CLK_PARENT_APLL2_NAME, + CLK_PARENT_OSC_D20_NAME}, +}; + +struct sdw_clk_parent_info sdw1_clk_parent_info[] = { + { AFE_CLK_VLP_CKSYS_TOP_AUD_SOUNDWIRE1_SEL, + CLK_PARENT_APLL2_D8_NAME, + CLK_PARENT_OSC_D20_NAME}, + { AFE_CLK_VLP_CKSYS_TOP_AUD_SOUNDWIRE1_PHY_SEL, + CLK_PARENT_APLL2_NAME, + CLK_PARENT_OSC_D20_NAME}, +}; + +static const char * const mt8901_cg_names[] = { + [AUD_CG_HW_GAIN01] = "HW_GAIN01", + [AUD_CG_HW_GAIN23] = "HW_GAIN23", + [AUD_CG_CM0] = "CM0", + [AUD_CG_CM1] = "CM1", + [AUD_CG_CM2] = "CM2", + [AUD_CG_HW_GAIN_DL0_DL1] = "HW_GAIN_DL0_DL1", + [AUD_CG_HW_GAIN_DL2_DL3] = "HW_GAIN_DL2_DL3", + [AUD_CG_HW_GAIN_DL4_DL5] = "HW_GAIN_DL4_DL5", + [AUD_CG_HW_GAIN_DL24CH] = "HW_GAIN_DL24CH", + [AUD_CG_UL0_ADC] = "UL0_ADC", + [AUD_CG_UL0_TML] = "UL0_TML", + [AUD_CG_UL0_ADC_HIRES] = "UL0_ADC_HIRES", + [AUD_CG_UL0_ADC_HIRES_TML] = "UL0_ADC_HIRES_TML", + [AUD_CG_UL1_ADC] = "UL1_ADC", + [AUD_CG_UL1_TML] = "UL1_TML", + [AUD_CG_UL1_ADC_HIRES] = "UL1_ADC_HIRES", + [AUD_CG_UL1_ADC_HIRES_TML] = "UL1_ADC_HIRES_TML", + [AUD_CG_ETDM_OUT0] = "ETDM_OUT0", + [AUD_CG_ETDM_OUT5] = "ETDM_OUT5", + [AUD_CG_ETDM_IN0] = "ETDM_IN0", + [AUD_CG_ETDM_IN5] = "ETDM_IN5", + [AUD_CG_GENERAL0_ASRC] = "GASRC0", + [AUD_CG_GENERAL1_ASRC] = "GASRC1", + [AUD_CG_GENERAL2_ASRC] = "GASRC2", + [AUD_CG_GENERAL3_ASRC] = "GASRC3", + [AUD_CG_GENERAL4_ASRC] = "GASRC4", + [AUD_CG_GENERAL5_ASRC] = "GASRC5", + [AUD_CG_GENERAL6_ASRC] = "GASRC6", + [AUD_CG_GENERAL7_ASRC] = "GASRC7", + [AUD_CG_APLL_TUNER1] = "APLL_TUNER1", + [AUD_CG_APLL_TUNER2] = "APLL_TUNER2", + [AUD_CG_H208M_CK] = "H208M", + [AUD_CG_APLL2_CK] = "APLL2", + [AUD_CG_APLL1_CK] = "APLL1", + [AUD_CG_AUDIO_F26M_CK] = "F26M", + [AUD_CG_AUDIO_HOPPING_CK] = "HOPPING", + [AUD_CG_SOUNDWIRE_DMIC0_ADC] = "SDW_DMIC0_ADC", + [AUD_CG_SOUNDWIRE_DMIC0_TML] = "SDW_DMIC0_TML", + [AUD_CG_SOUNDWIRE_DMIC1_ADC] = "SDW_DMIC1_ADC", + [AUD_CG_SOUNDWIRE_DMIC1_TML] = "SDW_DMIC1_TML", + [AUD_CG_SOUNDWIRE_DMIC2_ADC] = "SDW_DMIC2_ADC", + [AUD_CG_SOUNDWIRE_DMIC2_TML] = "SDW_DMIC2_TML", + [AUD_CG_SOUNDWIRE_DMIC3_ADC] = "SDW_DMIC3_ADC", + [AUD_CG_SOUNDWIRE_DMIC3_TML] = "SDW_DMIC3_TML", + [AUD_CG_SOUNDWIRE_DMIC4_ADC] = "SDW_DMIC4_ADC", + [AUD_CG_SOUNDWIRE_DMIC4_TML] = "SDW_DMIC4_TML", + [AUD_CG_SOUNDWIRE_DMIC5_ADC] = "SDW_DMIC5_ADC", + [AUD_CG_SOUNDWIRE_DMIC5_TML] = "SDW_DMIC5_TML", + [AUD_CG_SOUNDWIRE_DMIC6_ADC] = "SDW_DMIC6_ADC", + [AUD_CG_SOUNDWIRE_DMIC6_TML] = "SDW_DMIC6_TML", + [AUD_CG_SOUNDWIRE_DMIC7_ADC] = "SDW_DMIC7_ADC", + [AUD_CG_SOUNDWIRE_DMIC7_TML] = "SDW_DMIC7_TML", + [AUD_CG_SOUNDWIRE1] = "SOUNDWIRE1", + [AUD_CG_SOUNDWIRE0] = "SOUNDWIRE0", +}; + +static int mt8901_afe_send_power_req(struct device *dev, u8 cmd, + u32 d0, u32 d1, u32 d2) +{ + struct power_cntl_scmi_data scmidata = { + .requestId = cmd, + .customdata = { d0, d1, d2 }, + }; + struct _power_cntrl_request req = { + .dev = dev, + .InBuffer = &scmidata, + .InBufferSize = sizeof(scmidata), + }; + + return mtk_send_power_control_req(&req); +} + +static int mt8901_afe_pwr_ctrl_scene_req(struct device *dev, unsigned int scene) +{ + return mt8901_afe_send_power_req(dev, SSPM_AUD_CMD_PWR_CTRL_SCENE, + scene, 0, 0); +} + +static int mt8901_afe_clk_cg_deinit_req(struct device *dev) +{ + return mt8901_afe_send_power_req(dev, SSPM_AUD_CMD_CLK_CG_DEINIT, + 0, 0, 0); +} + +static int mt8901_afe_cg_req(struct device *dev, unsigned int cg, bool enable) +{ + return mt8901_afe_send_power_req(dev, SSPM_AUD_CMD_CG, cg, enable, 0); +} + +static int mt8901_afe_mtcmos_req(struct device *dev, unsigned int mtcmos, + bool enable) +{ + return mt8901_afe_send_power_req(dev, SSPM_AUD_CMD_MTCMOS, mtcmos, + enable, 0); +} + +static int mt8901_afe_afe_spm_ctrl_req(struct device *dev, unsigned int req, + bool enable) +{ + return mt8901_afe_send_power_req(dev, SSPM_AUD_CMD_AFE_SPM_CTRL, + req, enable, 0); +} + +static int mt8901_afe_clk_mux_req(struct device *dev, unsigned int mux, + bool enable) +{ + return mt8901_afe_send_power_req(dev, SSPM_AUD_CMD_VLP_SYS_CLK_MUX, + mux, enable, 0); +} + +static int mt8901_afe_clk_set_parent_req(struct device *dev, unsigned int mux, + unsigned int parent_level) +{ + return mt8901_afe_send_power_req(dev, + SSPM_AUD_CMD_VLP_SYS_CLK_SET_PARENT, + mux, parent_level, 0); +} + +static int mt8901_afe_enable_afe_on(struct mtk_sdw *mst) +{ + void *reg; + unsigned int mask; + unsigned int val; + + reg = mst->base + AUDIO_ENGEN_CON0_USER1; + mask = AUDIO_ENGEN_CON0_APLL2_MASK | + AUDIO_ENGEN_CON0_APLL1_MASK | + AUDIO_ENGEN_CON0_26M_MASK; + + guard(mutex)(&mst->afe_lock); + + mst->afe_on_refcnt++; + if (mst->afe_on_refcnt == 1) { + val = readl(reg); + val &= ~mask; + val |= mask; + writel(val, reg); + } + + return 0; +} + +static int mt8901_afe_disable_afe_on(struct mtk_sdw *mst) +{ + void *reg; + unsigned int mask; + unsigned int val; + + reg = mst->base + AUDIO_ENGEN_CON0_USER1; + mask = AUDIO_ENGEN_CON0_APLL2_MASK | + AUDIO_ENGEN_CON0_APLL1_MASK | + AUDIO_ENGEN_CON0_26M_MASK; + + guard(mutex)(&mst->afe_lock); + + if (mst->afe_on_refcnt > 0) + mst->afe_on_refcnt--; + + if (mst->afe_on_refcnt == 0) { + val = readl(reg); + val &= ~mask; + writel(val, reg); + } + + return 0; +} + +static int mt8901_afe_check_sspm_probe(struct mtk_sdw *mst) +{ + struct device *dev = mst->dev; + int ret; + + ret = mt8901_afe_pwr_ctrl_scene_req(dev, AUDIO_PWR_SCEN_NORMAL); + if (ret) { + dev_err(dev, "SSPM probe failed: %d\n", ret); + return ret; + } + + dev_dbg(dev, "SSPM available\n"); + return 0; +} + +static int mt8901_afe_disable_default_on_clk_and_top_cg(struct mtk_sdw *mst) +{ + struct device *dev = mst->dev; + int ret; + + ret = mt8901_afe_clk_cg_deinit_req(dev); + dev_dbg(dev, "disable_default_on_clk_and_top_cg: %d\n", ret); + + return ret; +} + +static int mt8901_sdw_init_clock(struct mtk_sdw *mst) +{ + struct device *dev = mst->dev; + int ret; + + ret = mt8901_afe_check_sspm_probe(mst); + if (ret) + return ret; + + ret = mt8901_afe_disable_default_on_clk_and_top_cg(mst); + if (ret) { + dev_err(dev, "disable_default_on_clk_and_top_cg failed: %d\n", + ret); + return ret; + } + + return ret; +} + +static int mt8901_afe_enable_top_cg(struct mtk_sdw *mst, unsigned int cg) +{ + struct device *dev = mst->dev; + const char *name; + int ret; + + if (cg >= AUD_CG_NUMS) + return -EINVAL; + + name = mt8901_cg_names[cg]; + + ret = mt8901_afe_cg_req(dev, cg, true); + + dev_dbg(dev, "enable_top_cg %s: %d\n", name, ret); + return ret; +} + +static int mt8901_afe_disable_top_cg(struct mtk_sdw *mst, unsigned int cg) +{ + struct device *dev = mst->dev; + const char *name; + int ret; + + if (cg >= AUD_CG_NUMS) + return -EINVAL; + + name = mt8901_cg_names[cg]; + + ret = mt8901_afe_cg_req(dev, cg, false); + + dev_dbg(dev, "disable_top_cg %s: %d\n", name, ret); + return ret; +} + +static int mt8901_afe_enable_mtcmos(struct mtk_sdw *mst, unsigned int mtcmos) +{ + struct device *dev = mst->dev; + int ret; + + if (mtcmos >= AUDIO_PD_NUM) + return -EINVAL; + + ret = mt8901_afe_mtcmos_req(dev, mtcmos, true); + dev_dbg(dev, "enable_mtcmos %u: %d\n", mtcmos, ret); + return ret; +} + +static int mt8901_afe_disable_mtcmos(struct mtk_sdw *mst, unsigned int mtcmos) +{ + struct device *dev = mst->dev; + int ret; + + if (mtcmos >= AUDIO_PD_NUM) + return -EINVAL; + + ret = mt8901_afe_mtcmos_req(dev, mtcmos, false); + dev_dbg(dev, "disable_mtcmos %u: %d\n", mtcmos, ret); + return ret; +} + +static int mt8901_afe_send_spm_req(struct mtk_sdw *mst, unsigned int req, + bool enable) +{ + struct device *dev = mst->dev; + int ret; + + if (req >= AFE_SPM_CTRL_REQ_NUMS) + return -EINVAL; + + ret = mt8901_afe_afe_spm_ctrl_req(dev, req, enable); + dev_dbg(dev, "send_spm_req %u en=%d: %d\n", req, enable, ret); + return ret; +} + +static int mt8901_afe_enable_main_clk_muxes(struct mtk_sdw *mst) +{ + struct device *dev = mst->dev; + int i, ret; + + for (i = 0; i < ARRAY_SIZE(mt8901_sdw_common_clk_muxes); i++) { + ret = mt8901_afe_clk_mux_req(dev, + mt8901_sdw_common_clk_muxes[i], + true); + dev_dbg(dev, "enable main_clk mux[%d]: %d\n", i, ret); + if (ret) + goto err_ret; + } + + return 0; +err_ret: + while (i-- > 0) + mt8901_afe_clk_mux_req(dev, mt8901_sdw_common_clk_muxes[i], + false); + + return ret; +} + +static int mt8901_afe_disable_main_clk_muxes(struct mtk_sdw *mst) +{ + struct device *dev = mst->dev; + int i, ret; + int last_ret = 0; + + for (i = 0; i < ARRAY_SIZE(mt8901_sdw_common_clk_muxes); i++) { + ret = mt8901_afe_clk_mux_req(dev, + mt8901_sdw_common_clk_muxes[i], + false); + dev_dbg(dev, "disable main_clk mux[%d]: %d\n", i, ret); + if (ret) { + dev_warn(dev, "disable main_clk mux[%d] failed: %d\n", + i, ret); + last_ret = ret; + } + } + + return last_ret; +} + +static int mt8901_afe_enable_main_clock(struct mtk_sdw *mst) +{ + struct device *dev = mst->dev; + int ret; + + ret = mt8901_afe_enable_main_clk_muxes(mst); + if (ret) { + dev_err(dev, "enable_main_clk_muxes failed: %d\n", ret); + return ret; + } + + ret = mt8901_afe_enable_top_cg(mst, AUD_CG_AUDIO_HOPPING_CK); + if (ret) { + dev_err(dev, "enable_top_cg HOPPING failed: %d\n", ret); + goto err_muxes; + } + + ret = mt8901_afe_enable_top_cg(mst, AUD_CG_AUDIO_F26M_CK); + if (ret) { + dev_err(dev, "enable_top_cg F26M failed: %d\n", ret); + goto err_hopping; + } + + ret = mt8901_afe_enable_afe_on(mst); + if (ret) { + dev_err(dev, "enable_afe_on failed: %d\n", ret); + goto err_f26m; + } + + return 0; + +err_f26m: + mt8901_afe_disable_top_cg(mst, AUD_CG_AUDIO_F26M_CK); +err_hopping: + mt8901_afe_disable_top_cg(mst, AUD_CG_AUDIO_HOPPING_CK); +err_muxes: + mt8901_afe_disable_main_clk_muxes(mst); + + return ret; +} + +static int mt8901_afe_disable_main_clock(struct mtk_sdw *mst) +{ + struct device *dev = mst->dev; + int ret; + int last_ret = 0; + + ret = mt8901_afe_disable_afe_on(mst); + if (ret) { + dev_err(dev, "disable_afe_on failed: %d\n", ret); + last_ret = ret; + } + + ret = mt8901_afe_disable_top_cg(mst, AUD_CG_AUDIO_F26M_CK); + if (ret) { + dev_err(dev, "disable_top_cg F26M failed: %d\n", ret); + last_ret = ret; + } + + ret = mt8901_afe_disable_top_cg(mst, AUD_CG_AUDIO_HOPPING_CK); + if (ret) { + dev_err(dev, "disable_top_cg HOPPING failed: %d\n", ret); + last_ret = ret; + } + + ret = mt8901_afe_disable_main_clk_muxes(mst); + if (ret) { + dev_err(dev, "disable_main_clk_muxes failed: %d\n", ret); + last_ret = ret; + } + + return last_ret; +} + +static int mt8901_afe_enable_reg_rw_clk(struct mtk_sdw *mst) +{ + struct device *dev = mst->dev; + int ret; + + ret = mt8901_afe_clk_mux_req(dev, AFE_CLK_VLP_CKSYS_TOP_AUD_INTBUS_SEL, + true); + if (ret) { + dev_err(dev, "enable reg_rw_clk INTBUS failed: %d\n", ret); + return ret; + } + + ret = mt8901_afe_clk_mux_req(dev, AFE_CLK_VLP_CKSYS_TOP_VADSP_SEL, + true); + if (ret) { + dev_err(dev, "enable reg_rw_clk VADSP failed: %d\n", ret); + goto err_intbus; + } + + ret = mt8901_afe_enable_top_cg(mst, AUD_CG_AUDIO_F26M_CK); + if (ret) { + dev_err(dev, "enable_top_cg F26M failed: %d\n", ret); + goto err_vadsp; + } + + return 0; + +err_vadsp: + mt8901_afe_clk_mux_req(dev, AFE_CLK_VLP_CKSYS_TOP_VADSP_SEL, false); +err_intbus: + mt8901_afe_clk_mux_req(dev, AFE_CLK_VLP_CKSYS_TOP_AUD_INTBUS_SEL, + false); + + return ret; +} + +static int mt8901_afe_disable_reg_rw_clk(struct mtk_sdw *mst) +{ + struct device *dev = mst->dev; + int ret; + + ret = mt8901_afe_disable_top_cg(mst, AUD_CG_AUDIO_F26M_CK); + if (ret) { + dev_err(dev, "disable_top_cg F26M failed: %d\n", ret); + return ret; + } + + ret = mt8901_afe_clk_mux_req(dev, AFE_CLK_VLP_CKSYS_TOP_VADSP_SEL, + false); + if (ret) { + dev_err(dev, "disable reg_rw_clk VADSP failed: %d\n", ret); + goto err_f26m; + } + + ret = mt8901_afe_clk_mux_req(dev, AFE_CLK_VLP_CKSYS_TOP_AUD_INTBUS_SEL, + false); + if (ret) { + dev_err(dev, "disable reg_rw_clk INTBUS failed: %d\n", ret); + goto err_vadsp; + } + + return 0; + +err_vadsp: + mt8901_afe_clk_mux_req(dev, AFE_CLK_VLP_CKSYS_TOP_VADSP_SEL, true); +err_f26m: + mt8901_afe_enable_top_cg(mst, AUD_CG_AUDIO_F26M_CK); + + return ret; +} + +static int mt8901_afe_map_clk_parent(unsigned int mux, const char *parent_name, + unsigned int *level) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(mt8901_clk_parent_map); i++) { + if (mt8901_clk_parent_map[i].mux == mux && + !strcmp(mt8901_clk_parent_map[i].parent_name, + parent_name)) { + *level = mt8901_clk_parent_map[i].level; + return 0; + } + } + + return -EINVAL; +} + +static int mt8901_afe_set_clock_parent(struct mtk_sdw *mst, unsigned int mux, + const char *parent_name) +{ + struct device *dev = mst->dev; + int ret; + unsigned int parent_level; + + ret = mt8901_afe_map_clk_parent(mux, parent_name, &parent_level); + if (ret) { + dev_err(dev, "map_clock_parent mux=%u parent=%s failed %d\n", + mux, parent_name, ret); + return ret; + } + + ret = mt8901_afe_clk_set_parent_req(dev, mux, parent_level); + dev_dbg(dev, "set_clock_parent mux=%u parent=%u: %d\n", + mux, parent_level, ret); + + return ret; +} + +static int mt8901_afe_enable_sdw_top_clock(struct mtk_sdw *mst, int link) +{ + struct device *dev = mst->dev; + const unsigned int *clk_array; + unsigned int clk_array_size; + int i, ret; + + switch (link) { + case MTK_SDW_CONTROLLER_0: + clk_array = mt8901_sdw0_main_clk_muxes; + clk_array_size = ARRAY_SIZE(mt8901_sdw0_main_clk_muxes); + break; + case MTK_SDW_CONTROLLER_1: + clk_array = mt8901_sdw1_main_clk_muxes; + clk_array_size = ARRAY_SIZE(mt8901_sdw1_main_clk_muxes); + break; + default: + return -EINVAL; + } + + for (i = 0; i < clk_array_size; i++) { + ret = mt8901_afe_clk_mux_req(dev, clk_array[i], true); + if (ret) + goto err_ret; + dev_dbg(dev, "enable sdw_top_clk mux[%d]\n", i); + } + + return 0; +err_ret: + while (i-- > 0) + mt8901_afe_clk_mux_req(dev, clk_array[i], false); + return ret; +} + +static int mt8901_afe_disable_sdw_top_clock(struct mtk_sdw *mst, int link) +{ + struct device *dev = mst->dev; + const unsigned int *clk_array; + unsigned int clk_array_size; + int i, ret; + int last_ret = 0; + + switch (link) { + case MTK_SDW_CONTROLLER_0: + clk_array = mt8901_sdw0_main_clk_muxes; + clk_array_size = ARRAY_SIZE(mt8901_sdw0_main_clk_muxes); + break; + case MTK_SDW_CONTROLLER_1: + clk_array = mt8901_sdw1_main_clk_muxes; + clk_array_size = ARRAY_SIZE(mt8901_sdw1_main_clk_muxes); + break; + default: + return -EINVAL; + } + + for (i = 0; i < clk_array_size; i++) { + ret = mt8901_afe_clk_mux_req(dev, clk_array[i], false); + if (ret) + last_ret = ret; + dev_dbg(dev, "disable sdw_top_clk mux[%u]\n", i); + } + + return last_ret; +} + +static int mt8901_afe_enable_sdw_active_clock(struct mtk_sdw *mst) +{ + int ret; + + ret = mt8901_afe_clk_mux_req(mst->dev, + AFE_CLK_VLP_CKSYS_TOP_AUD_ENGEN1_SEL, + true); + if (ret) + return ret; + + dev_dbg(mst->dev, "enable active_clk mux\n"); + + return 0; +} + +static int mt8901_afe_disable_sdw_active_clock(struct mtk_sdw *mst) +{ + int ret; + + ret = mt8901_afe_clk_mux_req(mst->dev, + AFE_CLK_VLP_CKSYS_TOP_AUD_ENGEN1_SEL, + false); + if (ret) + return ret; + + dev_dbg(mst->dev, "disable active_clk mux\n"); + return 0; +} + +static int mt8901_sdw_enable_spm_request(struct mtk_sdw *mst, int link) +{ + int ret; + + ret = mt8901_afe_send_spm_req(mst, AFE_SPM_CTRL_SRCCLKENA_REQ, true); + if (ret) { + dev_err(mst->dev, "[%d]request SRCCLKENA failed\n", link); + return ret; + } + + if (link == MTK_SDW_CONTROLLER_1) { + /* Link1 requires VCORE because the PINs are not AO */ + ret = mt8901_afe_send_spm_req(mst, AFE_SPM_CTRL_VCORE_REQ, + true); + if (ret) { + dev_err(mst->dev, "[%d]request VCORE failed\n", link); + mt8901_afe_send_spm_req(mst, AFE_SPM_CTRL_SRCCLKENA_REQ, + false); + return ret; + } + } + + return 0; +} + +static int mt8901_sdw_disable_spm_request(struct mtk_sdw *mst, int link) +{ + int ret; + + ret = mt8901_afe_send_spm_req(mst, AFE_SPM_CTRL_SRCCLKENA_REQ, false); + if (ret) { + dev_err(mst->dev, "[%d]release SRCCLKENA failed\n", link); + return ret; + } + + if (link == MTK_SDW_CONTROLLER_1) { + ret = mt8901_afe_send_spm_req(mst, AFE_SPM_CTRL_VCORE_REQ, + false); + if (ret) { + dev_err(mst->dev, "[%d]release VCORE failed\n", link); + mt8901_afe_send_spm_req(mst, AFE_SPM_CTRL_SRCCLKENA_REQ, + true); + return ret; + } + } + + return 0; +} + +static int mt8901_sdw_enable_link_clock(struct mtk_sdw *mst, int link) +{ + struct mtk_sdw_link *sdw_link = &mst->links[link]; + int ret; + + ret = mt8901_afe_enable_sdw_top_clock(mst, link); + if (ret) { + dev_err(mst->dev, "failed to enable sdw_top clock, ret %d\n", + ret); + return ret; + } + + ret = mt8901_afe_enable_main_clock(mst); + if (ret) { + dev_err(mst->dev, "failed to enable main clock, ret %d\n", ret); + mt8901_afe_disable_sdw_top_clock(mst, link); + return ret; + } + + if (!sdw_link->active_clk_enabled) { + ret = mt8901_afe_enable_sdw_active_clock(mst); + if (ret) { + dev_err(mst->dev, + "failed to enable main clock, ret %d\n", ret); + goto err_ret; + } + sdw_link->active_clk_enabled = true; + } + + if (link == MTK_SDW_CONTROLLER_0) { + ret = mt8901_afe_enable_top_cg(mst, AUD_CG_SOUNDWIRE0); + if (ret) { + dev_err(mst->dev, "failed to enable top cg %d, ret %d\n", + AUD_CG_SOUNDWIRE0, ret); + goto err_ret; + } + } else { + ret = mt8901_afe_enable_top_cg(mst, AUD_CG_SOUNDWIRE0); + if (ret) { + dev_err(mst->dev, "failed to enable top cg %d, ret %d\n", + AUD_CG_SOUNDWIRE0, ret); + goto err_ret; + } + ret = mt8901_afe_enable_top_cg(mst, AUD_CG_SOUNDWIRE1); + if (ret) { + dev_err(mst->dev, "failed to enable top cg %d, ret %d\n", + AUD_CG_SOUNDWIRE1, ret); + mt8901_afe_disable_top_cg(mst, AUD_CG_SOUNDWIRE0); + goto err_ret; + } + } + + ret = mt8901_afe_enable_top_cg(mst, AUD_CG_APLL2_CK); + if (ret) { + dev_err(mst->dev, "failed to enable top cg %d, ret %d\n", + AUD_CG_APLL2_CK, ret); + goto err_ret_cg; + } + + return 0; +err_ret_cg: + if (link == MTK_SDW_CONTROLLER_0) { + mt8901_afe_disable_top_cg(mst, AUD_CG_SOUNDWIRE0); + } else { + mt8901_afe_disable_top_cg(mst, AUD_CG_SOUNDWIRE0); + mt8901_afe_disable_top_cg(mst, AUD_CG_SOUNDWIRE1); + } +err_ret: + if (sdw_link->active_clk_enabled) { + mt8901_afe_disable_sdw_active_clock(mst); + sdw_link->active_clk_enabled = false; + } + + mt8901_afe_disable_main_clock(mst); + mt8901_afe_disable_sdw_top_clock(mst, link); + + return ret; +} + +static int mt8901_sdw_disable_link_clock(struct mtk_sdw *mst, int link) +{ + struct mtk_sdw_link *sdw_link = &mst->links[link]; + int ret; + + /* + * Transactional: stop at the first failure and re-enable what was + * already released, so the caller always sees either a fully clocked + * or a fully unclocked link. + */ + ret = mt8901_afe_disable_top_cg(mst, AUD_CG_APLL2_CK); + if (ret) { + dev_err(mst->dev, "failed to disable top cg %d, ret %d\n", + AUD_CG_APLL2_CK, ret); + return ret; + } + + ret = mt8901_afe_disable_top_cg(mst, AUD_CG_SOUNDWIRE0); + if (ret) { + dev_err(mst->dev, "failed to disable top cg %d, ret %d\n", + AUD_CG_SOUNDWIRE0, ret); + goto err_apll2; + } + + if (link != MTK_SDW_CONTROLLER_0) { + ret = mt8901_afe_disable_top_cg(mst, AUD_CG_SOUNDWIRE1); + if (ret) { + dev_err(mst->dev, + "failed to disable top cg %d, ret %d\n", + AUD_CG_SOUNDWIRE1, ret); + goto err_sdw0; + } + } + + ret = mt8901_afe_disable_main_clock(mst); + if (ret) { + dev_err(mst->dev, "failed to disable main clock, ret %d\n", + ret); + goto err_sdw1; + } + + if (sdw_link->active_clk_enabled) { + ret = mt8901_afe_disable_sdw_active_clock(mst); + if (ret) { + dev_err(mst->dev, + "failed to disable active clock, ret %d\n", ret); + goto err_main; + } + sdw_link->active_clk_enabled = false; + } + + ret = mt8901_afe_disable_sdw_top_clock(mst, link); + if (ret) { + dev_err(mst->dev, "failed to disable sdw_top clock, ret %d\n", + ret); + goto err_active; + } + + return 0; + +err_active: + if (!sdw_link->active_clk_enabled && + !mt8901_afe_enable_sdw_active_clock(mst)) + sdw_link->active_clk_enabled = true; +err_main: + mt8901_afe_enable_main_clock(mst); +err_sdw1: + if (link != MTK_SDW_CONTROLLER_0) + mt8901_afe_enable_top_cg(mst, AUD_CG_SOUNDWIRE1); +err_sdw0: + mt8901_afe_enable_top_cg(mst, AUD_CG_SOUNDWIRE0); +err_apll2: + mt8901_afe_enable_top_cg(mst, AUD_CG_APLL2_CK); + + return ret; +} + +static int mt8901_sdw_enable_reg_rw_clk(struct mtk_sdw *mst) +{ + return mt8901_afe_enable_reg_rw_clk(mst); +} + +static int mt8901_sdw_disable_reg_rw_clk(struct mtk_sdw *mst) +{ + return mt8901_afe_disable_reg_rw_clk(mst); +} + +static int mt8901_sdw_enable_power_domain(struct mtk_sdw *mst) +{ + return mt8901_afe_enable_mtcmos(mst, AUDIO_PD); +} + +static int mt8901_sdw_disable_power_domain(struct mtk_sdw *mst) +{ + return mt8901_afe_disable_mtcmos(mst, AUDIO_PD); +} + +const struct mtk_sdw_clk_ops mt8901_clk_ops = { + .init_clock = mt8901_sdw_init_clock, + .enable_reg_rw_clk = mt8901_sdw_enable_reg_rw_clk, + .disable_reg_rw_clk = mt8901_sdw_disable_reg_rw_clk, + .enable_link_clock = mt8901_sdw_enable_link_clock, + .disable_link_clock = mt8901_sdw_disable_link_clock, + .enable_power_domain = mt8901_sdw_enable_power_domain, + .disable_power_domain = mt8901_sdw_disable_power_domain, + .enable_spm_request = mt8901_sdw_enable_spm_request, + .disable_spm_request = mt8901_sdw_disable_spm_request, +}; diff --git a/drivers/soundwire/mediatek/mt8901-afe-clk.h b/drivers/soundwire/mediatek/mt8901-afe-clk.h new file mode 100644 index 0000000000000..9980f496e672f --- /dev/null +++ b/drivers/soundwire/mediatek/mt8901-afe-clk.h @@ -0,0 +1,162 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * mt8901-afe-clk.h -- MediaTek 8901 afe clock ctrl definition + * + * Copyright (c) 2026 MediaTek Inc. + * Author: Trevor Wu + * Weiyi Hsieh + */ + +#ifndef _MT8901_AFE_CLK_H_ +#define _MT8901_AFE_CLK_H_ + +#include "mtk-sdw-common.h" + +enum sspm_aud_cmd { + SSPM_AUD_CMD_MTCMOS = 0, + SSPM_AUD_CMD_CG, + SSPM_AUD_CMD_VLP_SYS_CLK_MUX, + SSPM_AUD_CMD_VLP_SYS_CLK_SET_RATE, + SSPM_AUD_CMD_VLP_SYS_CLK_SET_PARENT, + SSPM_AUD_CMD_AFE_SPM_CTRL, + SSPM_AUD_CMD_PWR_CTRL_SCENE, + SSPM_AUD_CMD_CLK_CG_DEINIT, + SSPM_AUD_CMD_NUM, +}; + +enum audio_mtcmos_pd_id { + AUDIO_VADSYS_PD_AO = 0, + AUDIO_VADSYS_PD_INFRA, + AUDIO_PD, + AUDIO_PD_NUM, +}; + +enum afe_pwr_scene { + AUDIO_PWR_SCEN_NORMAL = 0, + AUDIO_PWR_SCEN_BY_DSTATE, + AUDIO_PWR_SCEN_AON, + AUDIO_PWR_SCEN_NUM, +}; + +enum afe_spm_ctrl_req_type { + AFE_SPM_CTRL_VCORE_REQ = 0, + AFE_SPM_CTRL_PMIC_REQ, + AFE_SPM_CTRL_SRCCLKENA_REQ, + AFE_SPM_CTRL_APSRC_REQ, + AFE_SPM_CTRL_VRF18_REQ, + AFE_SPM_CTRL_INFRA_REQ, + AFE_SPM_CTRL_DDREN_REQ, + AFE_SPM_CTRL_REQ_NUMS, +}; + +enum audio_top_con_cg_type { + AUD_CG_ASD_BEGIN, + /* AUDIO_TOP_CON0 (0x0000) */ + AUD_CG_HW_GAIN01 = AUD_CG_ASD_BEGIN, + AUD_CG_HW_GAIN23, + AUD_CG_CM0, + AUD_CG_CM1, + AUD_CG_CM2, + /* AUDIO_TOP_CON1 (0x0004) */ + AUD_CG_HW_GAIN_DL0_DL1, + AUD_CG_HW_GAIN_DL2_DL3, + AUD_CG_HW_GAIN_DL4_DL5, + AUD_CG_HW_GAIN_DL24CH, + AUD_CG_UL0_ADC, + AUD_CG_UL0_TML, + AUD_CG_UL0_ADC_HIRES, + AUD_CG_UL0_ADC_HIRES_TML, + AUD_CG_UL1_ADC, + AUD_CG_UL1_TML, + AUD_CG_UL1_ADC_HIRES, + AUD_CG_UL1_ADC_HIRES_TML, + /* AUDIO_TOP_CON2 (0x0008) */ + AUD_CG_ETDM_OUT0, + AUD_CG_ETDM_OUT5, + AUD_CG_ETDM_IN0, + AUD_CG_ETDM_IN5, + /* AUDIO_TOP_CON3 (0x0008) */ + AUD_CG_GENERAL0_ASRC, + AUD_CG_GENERAL1_ASRC, + AUD_CG_GENERAL2_ASRC, + AUD_CG_GENERAL3_ASRC, + AUD_CG_GENERAL4_ASRC, + AUD_CG_GENERAL5_ASRC, + AUD_CG_GENERAL6_ASRC, + AUD_CG_GENERAL7_ASRC, + /* AUDIO_TOP_CON4 (0x0010) */ + AUD_CG_APLL_TUNER1, + AUD_CG_APLL_TUNER2, + AUD_CG_H208M_CK, + AUD_CG_APLL2_CK, + AUD_CG_APLL1_CK, + AUD_CG_SDW_BEGIN, + AUD_CG_AUDIO_F26M_CK = AUD_CG_SDW_BEGIN, + AUD_CG_AUDIO_HOPPING_CK, + AUD_CG_ASD_END = AUD_CG_AUDIO_HOPPING_CK, + /* AUDIO_TOP_CON5 (0x0070) - SoundWire (owned by dai-soundwire.c) */ + AUD_CG_SOUNDWIRE_DMIC0_ADC, + AUD_CG_SOUNDWIRE_DMIC0_TML, + AUD_CG_SOUNDWIRE_DMIC1_ADC, + AUD_CG_SOUNDWIRE_DMIC1_TML, + AUD_CG_SOUNDWIRE_DMIC2_ADC, + AUD_CG_SOUNDWIRE_DMIC2_TML, + AUD_CG_SOUNDWIRE_DMIC3_ADC, + AUD_CG_SOUNDWIRE_DMIC3_TML, + AUD_CG_SOUNDWIRE_DMIC4_ADC, + AUD_CG_SOUNDWIRE_DMIC4_TML, + AUD_CG_SOUNDWIRE_DMIC5_ADC, + AUD_CG_SOUNDWIRE_DMIC5_TML, + AUD_CG_SOUNDWIRE_DMIC6_ADC, + AUD_CG_SOUNDWIRE_DMIC6_TML, + AUD_CG_SOUNDWIRE_DMIC7_ADC, + AUD_CG_SOUNDWIRE_DMIC7_TML, + AUD_CG_SOUNDWIRE1, + AUD_CG_SOUNDWIRE0, + AUD_CG_SDW_END = AUD_CG_SOUNDWIRE0, + AUD_CG_NUMS, +}; + +enum afe_clk_mux_type { + AFE_CLK_VLP_CKSYS_TOP_AUDIO_H_SEL = 0, + AFE_CLK_VLP_CKSYS_TOP_AUD_ENGEN1_SEL, + AFE_CLK_VLP_CKSYS_TOP_AUD_ENGEN2_SEL, + AFE_CLK_VLP_CKSYS_TOP_AUD_SOUNDWIRE0_SEL, + AFE_CLK_VLP_CKSYS_TOP_AUD_SOUNDWIRE1_SEL, + AFE_CLK_VLP_CKSYS_TOP_AUD_SOUNDWIRE0_PHY_SEL, + AFE_CLK_VLP_CKSYS_TOP_AUD_SOUNDWIRE1_PHY_SEL, + AFE_CLK_VLP_CKSYS_TOP_AUD_INTBUS_SEL, + AFE_CLK_VLP_CKSYS_TOP_AUD_1_SEL, + AFE_CLK_VLP_CKSYS_TOP_AUD_2_SEL, + AFE_CLK_VLP_CKSYS_TOP_APLL_I2S1_MCK_SEL, + AFE_CLK_VLP_CKSYS_TOP_VADSP_SEL, + AFE_CLK_NUMS, +}; + +/* Clock parent names, per audio_clock_interface.h (SSPM firmware header) */ +#define CLK_PARENT_TCX_26M_MX9_CK_NAME "tcx_26m_mx9_ck" +#define CLK_PARENT_OSC_D20_NAME "osc_d20" +#define CLK_PARENT_OSC_D12_NAME "osc_d12" +#define CLK_PARENT_OSC_D10_NAME "osc_d10" +#define CLK_PARENT_OSC_D6_NAME "osc_d6" +#define CLK_PARENT_OSC_D5_NAME "osc_d5" +#define CLK_PARENT_OSC_D2_NAME "osc_d2" +#define CLK_PARENT_AD_OSC_NAME "ad_osc_ck" +#define CLK_PARENT_APLL1_D2_NAME "apll1_d2" +#define CLK_PARENT_APLL2_D2_NAME "apll2_d2" +#define CLK_PARENT_APLL1_D8_NAME "apll1_d8" +#define CLK_PARENT_APLL1_D4_NAME "apll1_d4" +#define CLK_PARENT_APLL2_D8_NAME "apll2_d8" +#define CLK_PARENT_APLL2_D4_NAME "apll2_d4" +#define CLK_PARENT_APLL1_NAME "apll1" +#define CLK_PARENT_APLL2_NAME "apll2" +#define CLK_PARENT_MAINPLL2_D7_D4_NAME "mainpll2_d7_d4" +#define CLK_PARENT_MAINPLL2_D4_D4_NAME "mainpll2_d4_d4" +#define CLK_PARENT_UNIVPLL2_D4_NAME "univpll2_d4" +#define CLK_PARENT_UNIVPLL2_D4_D2_NAME "univpll2_d4_d2" +#define CLK_PARENT_ADSPPLL_NAME "adsppll_ck" +#define CLK_PARENT_ADSPPLL_D2_NAME "adsppll_d2" + +extern const struct mtk_sdw_clk_ops mt8901_clk_ops; + +#endif diff --git a/drivers/soundwire/mediatek/mtk-sdw-chipglue.c b/drivers/soundwire/mediatek/mtk-sdw-chipglue.c new file mode 100644 index 0000000000000..1ed676ed50547 --- /dev/null +++ b/drivers/soundwire/mediatek/mtk-sdw-chipglue.c @@ -0,0 +1,34 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * mtk-sdw-chipglue.c -- chip-specific selection for the MediaTek + * SoundWire controller driver. + * + * Copyright (c) 2026 MediaTek Inc. + * Author: Trevor Wu + */ + +#include +#include "mtk-sdw-common.h" + +#if IS_ENABLED(CONFIG_SOUNDWIRE_MTK_MT8901_CLK) +#include "mt8901-afe-clk.h" +#endif + +int mtk_sdw_clk_ops_select(struct mtk_sdw *mst, int hw_ver) +{ + switch (hw_ver) { +#if IS_ENABLED(CONFIG_SOUNDWIRE_MTK_MT8901_CLK) + case MTK_SDW_HW_VER_MT8901: + case MTK_SDW_HW_VER_MT8971: + mst->clk_ops = &mt8901_clk_ops; + break; +#endif + default: + dev_err(mst->dev, + "no clock control ops for hw-ver %u (unsupported or not compiled in)\n", + hw_ver); + return -EINVAL; + } + + return 0; +} diff --git a/drivers/soundwire/mediatek/mtk-sdw-common.h b/drivers/soundwire/mediatek/mtk-sdw-common.h new file mode 100644 index 0000000000000..11d1d4c8b1eb6 --- /dev/null +++ b/drivers/soundwire/mediatek/mtk-sdw-common.h @@ -0,0 +1,108 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * MediaTek SoundWire master — shared driver structure. + * + * Copyright (c) 2026 MediaTek Inc. + * Author: Trevor Wu + */ +#ifndef __MTK_SDW_COMMON_H +#define __MTK_SDW_COMMON_H + +#include "mtk-sdw-core.h" + +struct mtk_sdw; +struct mtk_sdw_top_cfg; +struct mtk_sdw_top_pdi_params; + +enum mtk_sdw_hw_ver { + MTK_SDW_HW_VER_MT8195 = 0, /* first chip */ + MTK_SDW_HW_VER_MT8901, /* second chip */ + MTK_SDW_HW_VER_MT8971, + MTK_SDW_HW_VER_MAX, +}; + +enum mtk_sdw_controller_id { + MTK_SDW_CONTROLLER_0 = 0, + MTK_SDW_CONTROLLER_1, + MTK_SDW_CONTROLLE_NUM +}; + +#define SDW_PDI_CONFIG_NUM (8) +#define MTK_SDW_MAX_GROUP_SYNC 4 +#define MTK_SDW_MAX_DP 16 + +struct mtk_sdw_link { + struct mtk_sdw_core core; + struct mtk_sdw *master; + int ip_idx; + int irq; + u32 src_base; + u32 src_num; + u32 sink_base; + u32 sink_num; + bool active_clk_enabled; +}; + +struct mtk_sdw_dai { + struct mtk_sdw_link *link; + unsigned int port_num; + struct sdw_stream_runtime *sruntime; + unsigned int pdi_count; + struct mtk_sdw_pdi_params pdi_params[SDW_PDI_CONFIG_NUM]; + struct mtk_sdw_top_pdi_params *top_pdi_params; + bool use_group_sync; + unsigned int group_sync_id; + bool need_enable_delay; + bool suspended; +}; + +/* + * Per-chip-generation AFE clock/MTCMOS control. + */ +struct mtk_sdw_clk_ops { + int (*init_clock)(struct mtk_sdw *mst); + int (*enable_reg_rw_clk)(struct mtk_sdw *mst); + int (*disable_reg_rw_clk)(struct mtk_sdw *mst); + int (*enable_link_clock)(struct mtk_sdw *mst, int link); + int (*disable_link_clock)(struct mtk_sdw *mst, int link); + int (*enable_power_domain)(struct mtk_sdw *mst); + int (*disable_power_domain)(struct mtk_sdw *mst); + int (*enable_spm_request)(struct mtk_sdw *mst, int link); + int (*disable_spm_request)(struct mtk_sdw *mst, int link); +}; + +int mtk_sdw_clk_ops_select(struct mtk_sdw *mst, int hw_ver); + +struct mtk_sdw { + struct device *dev; + struct dentry *debugfs; + void __iomem *base; /* full AFE MMIO */ + struct mtk_sdw_link links[MTK_SDW_CONTROLLE_NUM]; + int num_links; + u32 controller_en_list; + const struct mtk_sdw_top_cfg *top_cfg; + void __iomem *top_pdi; /* base + top_cfg->pdi_base */ + void __iomem *top_con; /* base + top_cfg->con_base */ + unsigned int *top_pdi_reg_backup; + unsigned int top_pdi_reg_backup_num; + unsigned int *top_con_reg_backup; + unsigned int top_con_reg_backup_num; + const struct mtk_sdw_clk_ops *clk_ops; + u32 hw_ver; + /* group_lock: serializes group_refcnt[] group-sync allocation */ + struct mutex group_lock; + int group_refcnt[MTK_SDW_MAX_GROUP_SYNC]; + /* afe_lock: serializes afe_on_refcnt for afe on/off */ + struct mutex afe_lock; + int afe_on_refcnt; + u32 tzd_delay; + bool tzd_inverse; + u32 phy_delay; + u32 phy_double_delay; + struct mtk_sdw_dai *dais; + int num_dais; + u16 dp_pdi_mask[MTK_SDW_MAX_DP]; + struct platform_device *mach_dev; +}; + +#endif /* __MTK_SDW_COMMON_H */ diff --git a/drivers/soundwire/mediatek/mtk-sdw-core.c b/drivers/soundwire/mediatek/mtk-sdw-core.c new file mode 100644 index 0000000000000..df9964690e6b9 --- /dev/null +++ b/drivers/soundwire/mediatek/mtk-sdw-core.c @@ -0,0 +1,1338 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * MediaTek SoundWire master — STABLE controller core. + * + * Copyright (c) 2026 MediaTek Inc. + * Author: Trevor Wu + */ + +#include +#include +#include +#include +#include +#include + +#include "../bus.h" +#include "mtk-sdw-core.h" +#include "mtk-sdw.h" + +/* ---- register helpers ---- */ +static u32 core_readl(struct mtk_sdw_core *core, u32 off) +{ + return readl(core->regs + off); +} + +static void core_writel(struct mtk_sdw_core *core, u32 off, u32 val) +{ + writel(val, core->regs + off); +} + +static void core_updatel(struct mtk_sdw_core *core, u32 off, u32 mask, u32 val) +{ + u32 tmp = core_readl(core, off); + + core_writel(core, off, (tmp & ~mask) | (val & mask)); +} + +static u32 core_get_slave_status(struct mtk_sdw_core *core, u32 dev_addr) +{ + u32 slave_stat = 0; + + if (dev_addr > SDW_MAX_DEVICES) { + dev_err(core->dev, "Unexpected device id %u\n", dev_addr); + return slave_stat; + } + slave_stat = core_readl(core, MCP_SLAVESTAT); + slave_stat = MCP_SLAVESTAT_STATUS(slave_stat, dev_addr); + + return slave_stat; +} + +#define CORE_CMD_TIMEOUT_US 50000 +#define CORE_CMD_POLL_US 10 + +static int core_send_cmd(struct mtk_sdw_core *core, u32 cmd, u8 *rdata) +{ + u32 resp; + int ret; + + core_writel(core, MCP_COMMAND(0), cmd); + + ret = readl_poll_timeout(core->regs + MCP_STAT, resp, + resp & MCP_STAT_RX_NE, + CORE_CMD_POLL_US, CORE_CMD_TIMEOUT_US); + if (ret) { + dev_err(core->dev, "cmd 0x%08x timeout\n", cmd); + return SDW_CMD_TIMEOUT; + } + + resp = core_readl(core, MCP_COMMAND(0)); + if (FIELD_GET(MCP_RESP_NAK, resp)) + return SDW_CMD_FAIL; + if (!FIELD_GET(MCP_RESP_ACK, resp)) + return SDW_CMD_IGNORED; + if (rdata) + *rdata = FIELD_GET(MCP_RESP_RDATA, resp); + + return SDW_CMD_OK; +} + +static enum sdw_command_response +mtk_program_scp_addr(struct mtk_sdw_core *core, struct sdw_msg *msg) +{ + int ret; + u32 p1 = FIELD_PREP(MCP_CMD_FIELD, MCP_CMD_WRITE) | + FIELD_PREP(MCP_CMD_DEV_ADDR, msg->dev_num) | + FIELD_PREP(MCP_CMD_REG_ADDR, SDW_SCP_ADDRPAGE1) | + FIELD_PREP(MCP_CMD_REG_DATA, msg->addr_page1); + u32 p2 = FIELD_PREP(MCP_CMD_FIELD, MCP_CMD_WRITE) | + FIELD_PREP(MCP_CMD_DEV_ADDR, msg->dev_num) | + FIELD_PREP(MCP_CMD_REG_ADDR, SDW_SCP_ADDRPAGE2) | + FIELD_PREP(MCP_CMD_REG_DATA, msg->addr_page2); + + ret = core_send_cmd(core, p1, NULL); + if (ret == SDW_CMD_OK) + ret = core_send_cmd(core, p2, NULL); + + return ret; +} + +static int mtk_core_prep_msg(struct mtk_sdw_core *core, struct sdw_msg *msg) +{ + int ret; + + if (msg->page) { + ret = mtk_program_scp_addr(core, msg); + if (ret) { + msg->len = 0; + return ret; + } + } + + switch (msg->flags) { + case SDW_MSG_FLAG_READ: + case SDW_MSG_FLAG_WRITE: + break; + default: + dev_err(core->dev, "Invalid msg cmd: %d\n", msg->flags); + return -EINVAL; + } + + return 0; +} + +/* ======================================================================= + * sdw_master_ops + * ======================================================================= + */ +static enum sdw_command_response +mtk_sdw_do_xfer_msg(struct mtk_sdw_core *core, struct sdw_msg *msg, u8 *buf) +{ + int i, ret = 0; + + for (i = 0; i < msg->len; i++) { + u32 cmd = FIELD_PREP(MCP_CMD_DEV_ADDR, msg->dev_num) | + FIELD_PREP(MCP_CMD_REG_ADDR, msg->addr + i) | + FIELD_PREP(MCP_CMD_SSP_TAG, msg->ssp_sync); + + cmd |= FIELD_PREP(MCP_CMD_FIELD, MCP_CMD_READ); + ret = core_send_cmd(core, cmd, &buf[i]); + if (ret) + break; + } + + return ret; +} + +/* + * A peripheral that is still re-synchronizing at Dev0 (or several + * peripherals colliding there) can return corrupted DevId bytes while + * still ACKing the read. The bus core trusts a single read, so a + * corrupted ID creates a phantom peripheral that consumes a device + * number while the real device stays orphaned. Run the whole DevId + * read twice as two complete sequential 0x50..0x55 passes (per-byte + * repeat reads would break the peripheral's enumeration commit + * tracking) and report the command ignored on mismatch; the core + * retries on the next attach pass, by which point the peripheral is + * stable. + */ +static bool mtk_sdw_is_dev0_devid_msg(struct sdw_msg *msg) +{ + return msg->dev_num == 0 && msg->flags == SDW_MSG_FLAG_READ && + msg->addr == SDW_SCP_DEVID_0 && + msg->addr + msg->len - 1 == SDW_SCP_DEVID_5; +} + +static enum sdw_command_response +mtk_sdw_xfer_msg(struct sdw_bus *bus, struct sdw_msg *msg) +{ + struct mtk_sdw_core *core = bus_to_core(bus); + int i, ret = 0; + + ret = mtk_core_prep_msg(core, msg); + if (ret) + return SDW_CMD_FAIL_OTHER; + + if (mtk_sdw_is_dev0_devid_msg(msg)) { + u8 verify[SDW_SCP_DEVID_5 - SDW_SCP_DEVID_0 + 1] = {0}; + + ret = mtk_sdw_do_xfer_msg(core, msg, msg->buf); + if (ret) + return ret; + + ret = mtk_sdw_do_xfer_msg(core, msg, verify); + if (ret) + return ret; + + if (memcmp(msg->buf, verify, msg->len)) { + dev_dbg(core->dev, + "Dev0 DevId unstable (%*ph / %*ph), deferring\n", + msg->len, msg->buf, msg->len, verify); + return SDW_CMD_IGNORED; + } + + /* + * All-zeros / all-ones reads back stably (bus keepers hold + * the lines) but is not a DevId; nothing real was driving. + */ + if (memchr_inv(msg->buf, 0x00, msg->len) == NULL || + memchr_inv(msg->buf, 0xff, msg->len) == NULL) { + dev_dbg(core->dev, + "Dev0 DevId reads as keeper-held %02x, deferring\n", + msg->buf[0]); + return SDW_CMD_IGNORED; + } + + return SDW_CMD_OK; + } + + for (i = 0; i < msg->len; i++) { + u32 cmd = FIELD_PREP(MCP_CMD_DEV_ADDR, msg->dev_num) | + FIELD_PREP(MCP_CMD_REG_ADDR, msg->addr + i) | + FIELD_PREP(MCP_CMD_SSP_TAG, msg->ssp_sync); + + if (msg->flags == SDW_MSG_FLAG_READ) { + cmd |= FIELD_PREP(MCP_CMD_FIELD, MCP_CMD_READ); + ret = core_send_cmd(core, cmd, &msg->buf[i]); + } else { + cmd |= FIELD_PREP(MCP_CMD_FIELD, MCP_CMD_WRITE) | + FIELD_PREP(MCP_CMD_REG_DATA, msg->buf[i]); + ret = core_send_cmd(core, cmd, NULL); + } + if (ret) + break; + } + + return ret; +} + +static u32 mtk_sdw_read_ping_status(struct sdw_bus *bus) +{ + struct mtk_sdw_core *core = bus_to_core(bus); + + return core_readl(core, MCP_SLAVESTAT); +} + +static bool mipi_fwnode_property_read_bool(const struct fwnode_handle *fwnode, + const char *propname) +{ + int ret; + u8 val; + + if (!fwnode_property_present(fwnode, propname)) + return false; + ret = fwnode_property_read_u8_array(fwnode, propname, &val, 1); + if (ret < 0) + return false; + return !!val; +} + +static int mtk_sdw_read_prop(struct sdw_bus *bus) +{ + struct sdw_master_prop *prop = &bus->prop; + struct fwnode_handle *link; + char name[40]; + int nval; + int ret; + int i; + int id = 0; + + snprintf(name, sizeof(name), "mipi-sdw-%d-sw-interface-revision", + bus->link_id); + + device_property_read_u32(bus->dev, name, &prop->revision); + + /* Find master handle */ + snprintf(name, sizeof(name), + "mipi-sdw-%d-link-%d-subproperties", bus->link_id, id); + + link = device_get_named_child_node(bus->dev, name); + if (!link) { + dev_err(bus->dev, "Master node %s not found\n", name); + return -EIO; + } + + for (i = SDW_CLK_STOP_MODE0; i <= SDW_CLK_STOP_MODE1; i++) { + snprintf(name, sizeof(name), + "mipi-sdw-clock-stop-mode%d-supported", i); + if (mipi_fwnode_property_read_bool(link, name)) + prop->clk_stop_modes |= BIT(i); + } + + fwnode_property_read_u32(link, + "mipi-sdw-max-clock-frequency", + &prop->max_clk_freq); + + nval = fwnode_property_count_u32(link, + "mipi-sdw-clock-frequencies-supported"); + if (nval > 0) { + prop->num_clk_freq = nval; + prop->clk_freq = devm_kcalloc(bus->dev, prop->num_clk_freq, + sizeof(*prop->clk_freq), + GFP_KERNEL); + if (!prop->clk_freq) { + fwnode_handle_put(link); + return -ENOMEM; + } + + ret = fwnode_property_read_u32_array(link, + "mipi-sdw-clock-frequencies-supported", + prop->clk_freq, + prop->num_clk_freq); + if (ret < 0) { + fwnode_handle_put(link); + return ret; + } + } + + /* + * Check the frequencies supported. If FW doesn't provide max + * freq, then populate here by checking values. + */ + if (!prop->max_clk_freq && prop->clk_freq) { + prop->max_clk_freq = prop->clk_freq[0]; + for (i = 1; i < prop->num_clk_freq; i++) { + if (prop->clk_freq[i] > prop->max_clk_freq) + prop->max_clk_freq = prop->clk_freq[i]; + } + } + + fwnode_property_read_u32(link, "mipi-sdw-default-frame-rate", + &prop->default_frame_rate); + + fwnode_property_read_u32(link, "mipi-sdw-default-frame-row-size", + &prop->default_row); + + fwnode_property_read_u32(link, "mipi-sdw-default-frame-col-size", + &prop->default_col); + + fwnode_handle_put(link); + + /* + * sdw_bus_master_add() derives the bus data-rate limits from these + * right after read_prop returns, so the fallbacks must be in place + * here rather than after the master is added. + */ + if (!prop->max_clk_freq) + prop->max_clk_freq = SDW_DEFAULT_MAX_BUS_CLK_FREQ; + if (!prop->default_frame_rate) + prop->default_frame_rate = SDW_DEFAULT_FRAME_RATE; + if (!prop->default_row) + prop->default_row = SDW_DEFAULT_FRAME_SHAPE_ROW; + if (!prop->default_col) + prop->default_col = SDW_DEFAULT_FRAME_SHAPE_COL; + + return 0; +} + +static int mtk_sdw_set_bus_conf(struct sdw_bus *bus, + struct sdw_bus_params *params) +{ + struct mtk_sdw_core *core = bus_to_core(bus); + struct sdw_master_prop *prop = &bus->prop; + u8 next = params->next_bank ? 1 : 0; + u32 mclkd; + + /* curr_dr_freq is the *double* rate; halve it to get the bus clock. */ + if (params->curr_dr_freq && prop->mclk_freq) { + mclkd = (prop->mclk_freq / params->curr_dr_freq); + if (mclkd > 0) + mclkd--; + core_updatel(core, MCP_CLOCKCTRL(next), MCP_CLOCKCTRL_MCLKD, + FIELD_PREP(MCP_CLOCKCTRL_MCLKD, mclkd)); + core->curr_clk_freq = params->curr_dr_freq / 2; + } + return 0; +} + +/* ======================================================================= + * Port ops + * ======================================================================= + */ + +/* Source: word length, flow mode, data mode */ +static int mtk_sdw_dpn_set_port_params(struct sdw_bus *bus, + struct sdw_port_params *params, + unsigned int bank) +{ + struct mtk_sdw_core *core = bus_to_core(bus); + unsigned int p = params->num; + u32 cfg; + + cfg = core_readl(core, MCP_DPN_CONFIG(p, bank)); + + /* Bits per sample: encoded as (bps - 1). */ + u32p_replace_bits(&cfg, params->bps - 1, MCP_DPN_CONFIG_WL); + /* Port flow mode (isochronous=0 / async=1).*/ + u32p_replace_bits(&cfg, params->flow_mode, MCP_DPN_CONFIG_PFM); + /* Test/normal data mode. */ + u32p_replace_bits(&cfg, params->data_mode, MCP_DPN_CONFIG_PDM); + + core_writel(core, MCP_DPN_CONFIG(p, bank), cfg); + return 0; +} + +/* sample interval, block offsets, h-ctrl, BGC/BPM */ +static int +mtk_sdw_dpn_set_port_transport_params(struct sdw_bus *bus, + struct sdw_transport_params *params, + enum sdw_reg_bank bank) +{ + struct mtk_sdw_core *core = bus_to_core(bus); + unsigned int p = params->port_num; + u8 bk = (bank == SDW_BANK1) ? 1 : 0; + u32 cfg, offsetctrl, hctrl; + + /* BGC and BPM live in DPN_CONFIG alongside WL/PFM/PDM. */ + cfg = core_readl(core, MCP_DPN_CONFIG(p, bk)); + u32p_replace_bits(&cfg, params->blk_grp_ctrl, MCP_DPN_CONFIG_BGC); + u32p_replace_bits(&cfg, params->blk_pkg_mode, MCP_DPN_CONFIG_BPM); + core_writel(core, MCP_DPN_CONFIG(p, bk), cfg); + + /* Sample interval: encoded as (interval - 1). */ + core_writel(core, MCP_DPN_SAMPLECTRL(p, bk), + FIELD_PREP(MCP_DPN_SAMPLECTRL_INTERVAL, + params->sample_interval - 1)); + + /* Block offsets 1 and 2. */ + offsetctrl = FIELD_PREP(MCP_DPN_OFFSETCTRL_OFFSET1, params->offset1) | + FIELD_PREP(MCP_DPN_OFFSETCTRL_OFFSET2, params->offset2); + core_writel(core, MCP_DPN_OFFSETCTRL(p, bk), offsetctrl); + + /* Horizontal start/stop and lane control. */ + hctrl = FIELD_PREP(MCP_DPN_HCTRL_HSTART, params->hstart) | + FIELD_PREP(MCP_DPN_HCTRL_HSTOP, params->hstop) | + FIELD_PREP(MCP_DPN_HCTRL_LANECTRL, params->lane_ctrl); + core_writel(core, MCP_DPN_HCTRL(p, bk), hctrl); + + return 0; +} + +static int mtk_sdw_dpn_port_enable_ch(struct sdw_bus *bus, + struct sdw_enable_ch *enable_ch, + unsigned int bank) +{ + struct mtk_sdw_core *core = bus_to_core(bus); + unsigned int p = enable_ch->port_num; + u32 val = 0; + + if (enable_ch->enable) + val = FIELD_PREP(MCP_DPN_CHANNELEN_EN, enable_ch->ch_mask); + + core_writel(core, MCP_DPN_CHANNELEN(p, bank), val); + return 0; +} + +void mtk_sdw_core_dump_dpn_regs(struct mtk_sdw_core *core, unsigned int port, + const char *tag) +{ + struct sdw_bus *bus = &core->bus; + int bk; + + dev_info(core->dev, + "%s: port %u curr_bank=%d next_bank=%d\n", + tag, port, bus->params.curr_bank, bus->params.next_bank); + + for (bk = 0; bk < 2; bk++) { + u32 config = core_readl(core, MCP_DPN_CONFIG(port, bk)); + u32 channelen = core_readl(core, MCP_DPN_CHANNELEN(port, bk)); + u32 samplectrl = core_readl(core, MCP_DPN_SAMPLECTRL(port, bk)); + u32 offsetctrl = core_readl(core, MCP_DPN_OFFSETCTRL(port, bk)); + u32 hctrl = core_readl(core, MCP_DPN_HCTRL(port, bk)); + + dev_info(core->dev, + "%s: port %u bank%d:\n" + "CONFIG=0x%08x (wl=%lu pfm=%lu pdm=%lu bgc=%lu bpm=%lu)\n" + "CHANNELEN=0x%08x\n" + "SAMPLECTRL=0x%08x (interval=%lu)\n" + "OFFSETCTRL=0x%08x (off1=%lu off2=%lu)\n" + "HCTRL=0x%08x (hstart=%lu hstop=%lu lane=%lu)\n", + tag, port, bk, config, + FIELD_GET(MCP_DPN_CONFIG_WL, config) + 1, + FIELD_GET(MCP_DPN_CONFIG_PFM, config), + FIELD_GET(MCP_DPN_CONFIG_PDM, config), + FIELD_GET(MCP_DPN_CONFIG_BGC, config), + FIELD_GET(MCP_DPN_CONFIG_BPM, config), + channelen, samplectrl, + FIELD_GET(MCP_DPN_SAMPLECTRL_INTERVAL, samplectrl) + 1, + offsetctrl, + FIELD_GET(MCP_DPN_OFFSETCTRL_OFFSET1, offsetctrl), + FIELD_GET(MCP_DPN_OFFSETCTRL_OFFSET2, offsetctrl), + hctrl, + FIELD_GET(MCP_DPN_HCTRL_HSTART, hctrl), + FIELD_GET(MCP_DPN_HCTRL_HSTOP, hctrl), + FIELD_GET(MCP_DPN_HCTRL_LANECTRL, hctrl)); + } +} +EXPORT_SYMBOL_GPL(mtk_sdw_core_dump_dpn_regs); + +const struct sdw_master_port_ops mtk_sdw_core_port_ops = { + .dpn_set_port_params = mtk_sdw_dpn_set_port_params, + .dpn_set_port_transport_params = mtk_sdw_dpn_set_port_transport_params, + .dpn_port_enable_ch = mtk_sdw_dpn_port_enable_ch, +}; + +const struct sdw_master_ops mtk_sdw_core_ops = { + .read_prop = mtk_sdw_read_prop, + .read_ping_status = mtk_sdw_read_ping_status, + .xfer_msg = mtk_sdw_xfer_msg, + .set_bus_conf = mtk_sdw_set_bus_conf, +}; + +static int mtk_do_config_update(struct mtk_sdw_core *core) +{ + u32 val; + int ret; + + core_updatel(core, MCP_CONFIGUPDATE, MCP_CONFIGUPDATE_CU, + MCP_CONFIGUPDATE_CU); + ret = readl_poll_timeout(core->regs + MCP_CONFIGUPDATE, val, + !(val & MCP_CONFIGUPDATE_CU), 100, 2000); + if (ret) { + dev_err(core->dev, "[%u]config update timeout\n", + core->bus.link_id); + return ret; + } + return 0; +} + +static int mtk_init_clock_ctrl(struct mtk_sdw_core *core) +{ + struct sdw_bus *bus = &core->bus; + struct sdw_master_prop *prop = &bus->prop; + int c; + int r; + u32 val; + + dev_dbg(core->dev, "[%u]mclk %d max %d row %d col %d\n", + bus->link_id, prop->mclk_freq, prop->max_clk_freq, + prop->default_row, prop->default_col); + + /* Set clock divider */ + if (bus->params.curr_dr_freq && prop->mclk_freq) { + u32 mclkd = (prop->mclk_freq / bus->params.curr_dr_freq); + + if (mclkd > 0) + mclkd--; + core_updatel(core, MCP_CLOCKCTRL_B0, MCP_CLOCKCTRL_MCLKD, + FIELD_PREP(MCP_CLOCKCTRL_MCLKD, mclkd)); + core_updatel(core, MCP_CLOCKCTRL_B1, MCP_CLOCKCTRL_MCLKD, + FIELD_PREP(MCP_CLOCKCTRL_MCLKD, mclkd)); + } + + r = sdw_find_row_index(prop->default_row); + c = sdw_find_col_index(prop->default_col); + val = FIELD_PREP(MCP_FRAMESHAPE_NC, c) | + FIELD_PREP(MCP_FRAMESHAPE_NR, r); + + core_updatel(core, MCP_FRAMESHAPEINIT, + (MCP_FRAMESHAPE_NC | MCP_FRAMESHAPE_NR), val); + + if (prop->default_frame_rate % SDW_GSYNC_HZ) { + dev_warn(core->dev, + "[%u]FrameRate:%u not aligned with GSYNC:%d", + bus->link_id, prop->default_frame_rate, SDW_GSYNC_HZ); + } + + val = prop->default_frame_rate / SDW_GSYNC_HZ; + val = FIELD_PREP(MCP_SSP_CTRL_SSP_INTERVAL, val); + + core_updatel(core, MCP_SSP_CTRL_B0, MCP_SSP_CTRL_SSP_INTERVAL, val); + core_updatel(core, MCP_SSP_CTRL_B1, MCP_SSP_CTRL_SSP_INTERVAL, val); + + return 0; +} + +static int mtk_init_control_setting(struct mtk_sdw_core *core) +{ + u32 val; + u32 mask; + + mask = MCP_CONTROL_RST_DELAY | MCP_CONTROL_CMD_RST | + MCP_CONTROL_CMD_ACCEPT_MODE; + + val = FIELD_PREP(MCP_CONTROL_RST_DELAY, 1) | MCP_CONTROL_CMD_RST; + + core_updatel(core, MCP_CONTROL, mask, val); + + return mtk_do_config_update(core); +} + +/* + * Re-issue the command reset that hw_init performs once at cold boot: + * every peripheral drops to Dev0 and re-synchronizes together, which is + * the one enumeration scenario known to work reliably. Used as recovery + * when the link is wedged (peripherals stuck contending at Dev0, or + * carrying stale state across a warm reboot). + */ +/* + * Reference-stack restart choreography (ResetAndReInitMaster): soft-reset + * the IP and drop to the quiesce operation mode (clock held LOW, data + * released, bus keepers holding) in one config update, dwell so the + * peripherals see a defined quiet bus, then run the full hw re-init which + * ends by switching back to normal mode - the clock restarts cleanly into + * the new frame configuration and peripherals re-synchronize from a known + * state. Re-initializing while the bus keeps running in normal mode (the + * previous behaviour) leaves peripherals unable to regain frame sync. + */ +int mtk_sdw_core_bus_reset(struct mtk_sdw_core *core) +{ + int ret; + + dev_info(core->dev, "[%u] bus restart (soft reset + quiesce)\n", + core->bus.link_id); + + /* + * Hold the bus message lock so the reset cannot overlap an + * sdw_transfer() issued by a peripheral driver on another CPU. + */ + mutex_lock(&core->bus.msg_lock); + + core->dev0_repoll_count = 0; + + core_updatel(core, MCP_CONTROL, MCP_CONTROL_SOFT_RST, + MCP_CONTROL_SOFT_RST); + core_updatel(core, MCP_CONFIG, MCP_CONFIG_OP_MODE, + FIELD_PREP(MCP_CONFIG_OP_MODE, + SDWC_OM_CLK_LOW_DATA_OFF_KEEPER_ON)); + ret = mtk_do_config_update(core); + if (ret) + goto out; + + msleep(20); + + ret = mtk_sdw_core_hw_init(core); + if (ret) + goto out; + + /* + * SOFT_RST clears the per-device slave interrupt masks. Callers that + * quiesced interrupts around the reset get them back from their + * enable_irq(true); the status work cannot use that helper (it would + * cancel itself), so restore the masks here when interrupts are live. + */ + if (core->interrupt_enabled) { + core_writel(core, MCP_SLAVEINTMASK0, MCP_SLAVE_INTMASK0_MASK); + core_writel(core, MCP_SLAVEINTMASK1, MCP_SLAVE_INTMASK1_MASK); + } + +out: + mutex_unlock(&core->bus.msg_lock); + return ret; +} + +static int mtk_init_config_setting(struct mtk_sdw_core *core) +{ + u32 val; + u32 mask; + + mask = MCP_CONFIG_MCR | MCP_CONFIG_OP_MODE; + + val = FIELD_PREP(MCP_CONFIG_MCR, SDW_DEFAULT_MAX_CMD_RETRY) | + FIELD_PREP(MCP_CONFIG_OP_MODE, SDWC_OM_NORMAL); + + core_updatel(core, MCP_CONFIG, mask, val); + + return mtk_do_config_update(core); +} + +/* + * All MCP_INTMASK updates are read-modify-write and can run concurrently + * from the ISR, the status work, and the attach-check work: serialize + * them so no path can drop another path's mask bits. + */ +static void mtk_sdw_update_intmask(struct mtk_sdw_core *core, u32 mask, u32 val) +{ + unsigned long flags; + + spin_lock_irqsave(&core->intmask_lock, flags); + core_updatel(core, MCP_INTMASK, mask, val); + spin_unlock_irqrestore(&core->intmask_lock, flags); +} + +static void mtk_sdw_enable_master_irq(struct mtk_sdw_core *core, bool enable) +{ + mtk_sdw_update_intmask(core, (MCP_INT_MST_ERR_MASK | MCP_INT_WAKEUP), + enable ? (MCP_INT_MST_ERR_MASK | MCP_INT_WAKEUP) : 0); +} + +void mtk_sdw_enable_irq(struct mtk_sdw_core *core, bool enable) +{ + if (enable) { + core_writel(core, MCP_SLAVEINTMASK0, MCP_SLAVE_INTMASK0_MASK); + core_writel(core, MCP_SLAVEINTMASK1, MCP_SLAVE_INTMASK1_MASK); + } else { + core_writel(core, MCP_SLAVEINTMASK0, 0); + core_writel(core, MCP_SLAVEINTMASK1, 0); + } + + mtk_sdw_enable_slave_irq(core, enable); + + mtk_sdw_enable_master_irq(core, enable); + + core->interrupt_enabled = enable; + + /* + * attach_check_work is NOT cancelled here: its handler calls this + * function, so a sync cancel would self-deadlock. It is cancelled + * explicitly on the teardown paths (link deinit, suspend). + * + * A handler that already observed interrupt_enabled == true may still + * be running and about to queue status_work: wait for it before the + * cancel, or the work is re-queued behind the caller's back. The line + * is shared between links, so synchronize rather than disable it. + */ + if (!enable) { + struct mtk_sdw_link *link = + container_of(core, struct mtk_sdw_link, core); + + synchronize_irq(link->irq); + cancel_delayed_work_sync(&core->status_work); + } + + mtk_sdw_update_intmask(core, MCP_INT_IRQ, enable ? MCP_INT_IRQ : 0); +} + +void mtk_sdw_enable_slave_irq(struct mtk_sdw_core *core, bool enable) +{ + mtk_sdw_update_intmask(core, MCP_INT_SLV_MASK, + enable ? MCP_INT_SLV_MASK : 0); +} + +/* + * slave IRQ handling + */ +/* + * Experiment/quarantine knob: suppress ALERT handling for one CS35L56 + * peripheral identified by unique id. An electrically faulty peripheral + * that asserts Alert permanently drags the bus core into an endless + * alert-read retry storm (every read of its interrupt registers fails + * parity), and the extra traffic from a misbehaving responder disturbs + * enumeration of the healthy peripherals on the link. Reporting it as + * plainly attached quiets the storm so the rest of the link can be + * validated. Not for production; diagnostic aid for a bad unit. + */ +static int quarantine_uid = -1; +module_param(quarantine_uid, int, 0644); +MODULE_PARM_DESC(quarantine_uid, + "Suppress ALERT for the 0x3556 peripheral with this unique id (-1 = off)"); + +static bool mtk_sdw_quarantined(struct mtk_sdw_core *core, int dev_num) +{ + struct sdw_slave *slave; + + if (quarantine_uid < 0) + return false; + + list_for_each_entry(slave, &core->bus.slaves, node) { + if (slave->dev_num == dev_num && + slave->id.mfg_id == 0x01fa && + slave->id.part_id == 0x3556 && + slave->id.unique_id == quarantine_uid) + return true; + } + + return false; +} + +static int mtk_sdw_update_slave_status(struct mtk_sdw_core *core, + u64 slave_intstat) +{ + enum sdw_slave_status status[SDW_MAX_DEVICES + 1]; + bool need_handle_slave = false; + u32 intstat; + u32 val; + int i, set_status; + + memset(status, 0, sizeof(status)); + + for (i = 0; i <= SDW_MAX_DEVICES; i++) { + intstat = MCP_SLAVEINTSTAT_STATUS(slave_intstat, i); + + set_status = 0; + + if (intstat) { + need_handle_slave = true; + + if (intstat & MCP_SLAVEINTSTAT_RESERVED) { + status[i] = SDW_SLAVE_RESERVED; + set_status++; + } + + if (intstat & MCP_SLAVEINTSTAT_ALERT) { + status[i] = mtk_sdw_quarantined(core, i) ? + SDW_SLAVE_ATTACHED : SDW_SLAVE_ALERT; + set_status++; + } + + if (intstat & MCP_SLAVEINTSTAT_ATTACHED) { + status[i] = SDW_SLAVE_ATTACHED; + set_status++; + } + + if (intstat & MCP_SLAVEINTSTAT_NOT_PRESENT) { + status[i] = SDW_SLAVE_UNATTACHED; + set_status++; + } + } + + if (set_status != 1) { + val = core_get_slave_status(core, i); + + switch (val) { + case 0: + status[i] = SDW_SLAVE_UNATTACHED; + break; + case 1: + status[i] = SDW_SLAVE_ATTACHED; + break; + case 2: + status[i] = mtk_sdw_quarantined(core, i) ? + SDW_SLAVE_ATTACHED : SDW_SLAVE_ALERT; + break; + case 3: + default: + status[i] = SDW_SLAVE_RESERVED; + break; + } + } + } + + if (need_handle_slave) + return sdw_handle_slave_status(&core->bus, status); + + return 0; +} + +/* + * A peripheral that soft-resets after enumeration (the cs42l43 does this + * during driver probe) drops to Dev0 and takes tens of ms to re-sync + * before it can ACK a new device number. The poll loop below exhausts its + * retries well inside that window, and the IP raises no further attach + * transition for a device already sitting at Dev0, so without a re-poll + * the attach is lost and the peripheral stays UNATTACHED forever. Windows + * never hits this (its codec stack does not reset after enumeration). + */ +#define MTK_SDW_DEV0_REPOLL_MAX 25 +#define MTK_SDW_DEV0_REPOLL_DELAY_MS 20 +#define MTK_SDW_DEV0_SETTLE_MS 30 +#define MTK_SDW_DEV0_SETTLE_ROUNDS 4 +#define MTK_SDW_BUS_RESET_MAX 3 +#define MTK_SDW_BUS_RESET_SETTLE_MS 100 + +static void mtk_sdw_slave_status_work(struct work_struct *work) +{ + struct mtk_sdw_core *core = + container_of(work, struct mtk_sdw_core, status_work.work); + u32 stat0; + u32 stat1; + u32 dev0_stat; + int retries = 0; + unsigned int n; + u64 slv_intstat; + + core_writel(core, MCP_INTSTAT, MCP_INT_SLV_MASK); + + stat0 = core_readl(core, MCP_SLAVEINTSTAT0); + stat1 = core_readl(core, MCP_SLAVEINTSTAT1); + + core_writel(core, MCP_SLAVEINTSTAT0, stat0); + core_writel(core, MCP_SLAVEINTSTAT1, stat1); + dev_dbg_ratelimited(core->dev, + "[%u]Slave intstat0: 0x%x, intstat1 0x%x\n", + core->bus.link_id, stat0, stat1); + + slv_intstat = ((u64)stat1 << 32) | stat0; + + do { + /* + * Pace the enumeration rounds. A peripheral that just + * dropped to Dev0 (soft reset, late power-up) needs time to + * regain frame sync; reading DevId while it drives misaligned + * data corrupts the response of any synced peripheral + * answering in the same round, and the corruption is + * deterministic (wire-mixed), so re-reads cannot filter it. + * The wait must precede every round including the first, or + * the straggler is enumerated from its mixed response. A + * peripheral arriving at Dev0 during the wait latches a new + * attach transition for Dev0; give it a full interval too, + * bounded so a flapping device cannot stall the work. + */ + n = 0; + while (core_get_slave_status(core, 0) == MCP_SLAVESTAT_ATTACHED && + n++ < MTK_SDW_DEV0_SETTLE_ROUNDS) { + core_writel(core, MCP_SLAVEINTSTAT0, + MCP_SLAVEINTSTAT_ATTACHED); + msleep(MTK_SDW_DEV0_SETTLE_MS); + if (!(core_readl(core, MCP_SLAVEINTSTAT0) & + MCP_SLAVEINTSTAT_ATTACHED)) + break; + } + + mtk_sdw_update_slave_status(core, slv_intstat); + + dev0_stat = core_get_slave_status(core, 0); + + if (dev0_stat == MCP_SLAVESTAT_ATTACHED) { + slv_intstat = MCP_SLAVEINTSTAT_ATTACHED; + dev_info(core->dev, + "[%u]slave attached Dev0Stat:0x%x Retries:%d", + core->bus.link_id, dev0_stat, retries); + } else { + break; + } + + } while (retries++ < SDW_MAX_DEVICES); + + if (dev0_stat == MCP_SLAVESTAT_ATTACHED && + core->interrupt_enabled) { + if (core->dev0_repoll_count++ < MTK_SDW_DEV0_REPOLL_MAX) { + dev_dbg(core->dev, + "[%u] Dev0 still attached, re-polling (%u)\n", + core->bus.link_id, core->dev0_repoll_count); + schedule_delayed_work(&core->status_work, + msecs_to_jiffies(MTK_SDW_DEV0_REPOLL_DELAY_MS)); + } else if (core->bus_reset_count < MTK_SDW_BUS_RESET_MAX) { + core->bus_reset_count++; + dev_warn(core->dev, + "[%u] Dev0 attach unresolved after %u re-polls, bus reset %u/%u\n", + core->bus.link_id, core->dev0_repoll_count, + core->bus_reset_count, MTK_SDW_BUS_RESET_MAX); + core->dev0_repoll_count = 0; + if (mtk_sdw_core_bus_reset(core)) + dev_err(core->dev, + "[%u] bus reset failed, not re-polling\n", + core->bus.link_id); + else + schedule_delayed_work(&core->status_work, + msecs_to_jiffies(MTK_SDW_BUS_RESET_SETTLE_MS)); + } else { + dev_warn(core->dev, + "[%u] Dev0 attach unresolved after %u re-polls and %u bus resets, giving up\n", + core->bus.link_id, core->dev0_repoll_count, + core->bus_reset_count); + core->dev0_repoll_count = 0; + } + } else { + core->dev0_repoll_count = 0; + core->bus_reset_count = 0; + } + + mtk_sdw_enable_slave_irq(core, true); +} + +static void mtk_sdw_handle_master_dp_interrupt(struct mtk_sdw_core *core) +{ + u32 portintstatus = core_readl(core, MCP_PORTINTSTAT); + u32 pdistatus = core_readl(core, MCP_PDISTAT); + struct sdw_bus *bus = &core->bus; + + dev_info(core->dev, "[%u]PortIntStatus:0x%x PdiStatus:0x%x\n", + bus->link_id, portintstatus, pdistatus); + + if (portintstatus) + core_writel(core, MCP_PORTINTSTAT, portintstatus); + + if (pdistatus) + core_writel(core, MCP_PDISTAT, pdistatus); +} + +static void mtk_sdw_recalc_block_offset(struct sdw_port_runtime *p_rt) +{ + struct sdw_transport_params *tp = &p_rt->transport_params; + int offset; + + offset = (tp->offset2 << 8) + tp->offset1; + offset -= 1; + + tp->offset1 = offset & 0xFF; + tp->offset2 = offset >> 8; +} + +static int mtk_sdw_compute_params(struct sdw_bus *bus, + struct sdw_stream_runtime *stream) +{ + struct sdw_master_runtime *m_rt; + struct sdw_slave_runtime *s_rt; + struct sdw_port_runtime *p_rt; + int ret; + + ret = sdw_compute_params(bus, stream); + if (ret) + return ret; + + /* block offset should begin from 0 */ + list_for_each_entry(m_rt, &bus->m_rt_list, bus_node) { + list_for_each_entry(p_rt, &m_rt->port_list, port_node) + mtk_sdw_recalc_block_offset(p_rt); + + list_for_each_entry(s_rt, &m_rt->slave_rt_list, m_rt_node) + list_for_each_entry(p_rt, &s_rt->port_list, port_node) + mtk_sdw_recalc_block_offset(p_rt); + } + + return 0; +} + +/* + * One-shot check scheduled after link init. A re-initialized master (module + * reload, and eventually resume) comes up in normal operation mode while the + * peripherals still hold their previous device numbers, so they raise no new + * attach transition and the interrupt-driven recovery machinery is never + * armed: the link sits dead with zero attach events. The reference stack + * avoids this by always running its restart choreography on re-init. Detect + * the state instead (peripherals declared for the link but every one of them + * still UNATTACHED well past the normal attach window) and run the restart, + * which drops them to Dev0 and re-enumerates them through the normal + * interrupt path. On a first boot out of POST the peripherals attach within + * tens of ms, every slave leaves UNATTACHED, and this work does nothing. + */ +static void mtk_sdw_attach_check_work(struct work_struct *work) +{ + struct mtk_sdw_core *core = + container_of(work, struct mtk_sdw_core, attach_check_work.work); + struct sdw_slave *slave; + bool any_declared = false; + + if (!core->interrupt_enabled) + return; + + /* + * Same choreography as the debugfs knob: quiesce interrupt handling + * (which also cancels the status work) before looking at the slave + * states, so an attach completing concurrently cannot be judged on a + * stale snapshot, then re-enable it afterwards, which also restores + * the slave interrupt masks that SOFT_RST clears - without that the + * attach transitions after the restart never interrupt and + * enumeration stalls after Dev0. + */ + mtk_sdw_enable_irq(core, false); + + mutex_lock(&core->bus.bus_lock); + list_for_each_entry(slave, &core->bus.slaves, node) { + any_declared = true; + if (slave->status != SDW_SLAVE_UNATTACHED) { + mutex_unlock(&core->bus.bus_lock); + mtk_sdw_enable_irq(core, true); + return; + } + } + mutex_unlock(&core->bus.bus_lock); + + if (!any_declared) { + mtk_sdw_enable_irq(core, true); + return; + } + + dev_warn(core->dev, + "[%u] no peripheral attached %u ms after init, restarting the bus\n", + core->bus.link_id, MTK_SDW_ATTACH_CHECK_DELAY_MS); + + if (mtk_sdw_core_bus_reset(core)) { + dev_err(core->dev, "[%u] post-init bus restart failed\n", + core->bus.link_id); + mtk_sdw_enable_irq(core, true); + return; + } + mtk_sdw_enable_irq(core, true); + schedule_delayed_work(&core->status_work, + msecs_to_jiffies(MTK_SDW_BUS_RESET_SETTLE_MS)); +} + +int mtk_sdw_core_init(struct mtk_sdw_core *core) +{ + core->bus.ops = &mtk_sdw_core_ops; + core->bus.port_ops = &mtk_sdw_core_port_ops; + core->bus.compute_params = mtk_sdw_compute_params; + + spin_lock_init(&core->intmask_lock); + INIT_DELAYED_WORK(&core->status_work, mtk_sdw_slave_status_work); + INIT_DELAYED_WORK(&core->attach_check_work, mtk_sdw_attach_check_work); + return 0; +} + +int mtk_sdw_core_hw_init(struct mtk_sdw_core *core) +{ + int ret; + + ret = mtk_init_clock_ctrl(core); + if (ret) + return ret; + + ret = mtk_init_control_setting(core); + if (ret) + return ret; + + ret = mtk_init_config_setting(core); + if (ret) + return ret; + + return 0; +} + +irqreturn_t mtk_sdw_core_irq(int irq, void *data) +{ + struct mtk_sdw_core *core = data; + struct sdw_bus *bus = &core->bus; + u32 intstat = core_readl(core, MCP_INTSTAT); + u32 clearstat; + + if (!(intstat & MCP_INT_IRQ)) { + dev_warn(core->dev, + "[%u]interrupt request not enabled IntStatus:0x%x", + bus->link_id, intstat); + return IRQ_NONE; + } + + clearstat = intstat; + + if (intstat & MCP_INT_WAKEUP) + dev_err(core->dev, "[%u]wakeup IntStatus:0x%x\n", + bus->link_id, intstat); + + if (intstat & MCP_INT_SLV_MASK) { + if (intstat & MCP_INT_SLV_RESERVED) { + dev_info(core->dev, "[%u]slave reserved int IntStatus:0x%x\n", + bus->link_id, intstat); + } + + if (intstat & MCP_INT_SLV_ALERT) { + dev_info(core->dev, + "[%u]slave alert int IntStatus:0x%x\n", + bus->link_id, intstat); + } + + if (intstat & MCP_INT_SLV_ATTACHED) { + dev_info(core->dev, + "[%u]slave attached int IntStatus:0x%x\n", + bus->link_id, intstat); + } + + if (intstat & MCP_INT_SLV_NOT_ATTACH) { + dev_info(core->dev, + "[%u]slave not attached int IntStatus:0x%x\n", + bus->link_id, intstat); + } + if (core->interrupt_enabled) { + /* + * Mask before scheduling: the work re-enables the + * slave interrupt when it finishes, so masking after + * schedule_delayed_work() races a work that completes + * on another CPU first and leaves the interrupt + * masked forever (all subsequent attach/detach + * transitions are then lost). + */ + mtk_sdw_enable_slave_irq(core, false); + schedule_delayed_work(&core->status_work, 0); + clearstat &= ~MCP_INT_SLV_MASK; + } + } + if (intstat & MCP_INT_DP_INT) { + dev_info(core->dev, "[%u]dp int IntStatus:0x%x\n", bus->link_id, + intstat); + + mtk_sdw_handle_master_dp_interrupt(core); + } + + if (intstat & (MCP_INT_CTL_BUS_CLASH | MCP_INT_DATA_BUS_CLASH)) { + /* + * Isolated clashes at bus power transitions are harmless. + * A persistent clash condition (e.g. floating pads on a + * link with no peripherals) refires the interrupt as fast + * as it is cleared and storms the CPU: mask the clash + * interrupts on this link when a rapid burst is detected. + */ + if (time_after(jiffies, core->clash_window + HZ / 10)) { + core->clash_window = jiffies; + core->clash_burst = 0; + } + if (++core->clash_burst >= 32) { + mtk_sdw_update_intmask(core, + MCP_INT_CTL_BUS_CLASH | + MCP_INT_DATA_BUS_CLASH, 0); + dev_err(core->dev, + "[%u]bus clash interrupt storm: masking clash interrupts IntStatus:0x%x\n", + bus->link_id, intstat); + core->clash_burst = 0; + } + } + + if (intstat & (MCP_INT_CTL_BUS_CLASH)) + dev_err_ratelimited(core->dev, "[%u]control bus clash 0x%x\n", + bus->link_id, intstat); + + if (intstat & (MCP_INT_DATA_BUS_CLASH)) + dev_err_ratelimited(core->dev, "[%u]data bus clash 0x%x\n", + bus->link_id, intstat); + + if (intstat & MCP_INT_PARITY_ERR) + dev_err(core->dev, "[%u]parity error 0x%x\n", bus->link_id, + intstat); + + if (intstat & MCP_INT_CMD_ERR) + dev_err(core->dev, "[%u]command error 0x%x\n", bus->link_id, + intstat); + + if (intstat & MCP_INT_RXWL) + dev_info(core->dev, "[%u]rw wl 0x%x\n", bus->link_id, intstat); + + dev_info(core->dev, "[%u]ClearStatus 0x%x, IntStatus 0x%x\n", + bus->link_id, clearstat, intstat); + + core_writel(core, MCP_INTSTAT, clearstat); + + return IRQ_HANDLED; +} + +void mtk_sdw_configure_pdi_params(struct mtk_sdw_core *core, + struct mtk_sdw_pdi_params *params) +{ + u32 offset; + u32 val; + u32 mask; + + offset = MCP_PDIN_CONFIG(params->pdi_num); + + mask = MCP_PDIN_CONFIG_SOFTRST | MCP_PDIN_CONFIG_CHANNELMASK | + MCP_PDIN_CONFIG_PORTNUM; + + val = MCP_PDIN_CONFIG_SOFTRST | + FIELD_PREP(MCP_PDIN_CONFIG_CHANNELMASK, params->ch_mask) | + FIELD_PREP(MCP_PDIN_CONFIG_PORTNUM, params->port_num); + + core_updatel(core, offset, mask, val); +} + +void mtk_sdw_clear_pdi_settings(struct mtk_sdw_core *core, + struct mtk_sdw_pdi_params *pdi) +{ + u32 offset; + u32 val; + u32 mask; + + offset = MCP_PDIN_CONFIG(pdi->pdi_num); + + /* clear pdi fifo */ + mask = MCP_PDIN_CONFIG_SOFTRST; + val = MCP_PDIN_CONFIG_SOFTRST; + core_updatel(core, offset, mask, val); + + /* clear pdi channel mask */ + mask = MCP_PDIN_CONFIG_CHANNELMASK; + val = FIELD_PREP(MCP_PDIN_CONFIG_CHANNELMASK, 0); + core_updatel(core, offset, mask, val); +} + +void mtk_sdw_configure_port_params(struct mtk_sdw_core *core, + struct mtk_sdw_port_params *params, u32 dir) +{ + u32 offset; + u32 val = 0; + u32 mask; + + offset = MCP_DPN_PORTCTRL(params->port_num); + mask = MCP_DPN_PORTCTRL_DIR | MCP_DPN_PORTCTRL_BPT_EN | + MCP_DPN_PORTCTRL_BPT_TYPE; + if (dir == SDW_DATA_DIR_RX) + val |= MCP_DPN_PORTCTRL_DIR; + + if (params->port_num == 0) /* bpt rx? */ + val |= FIELD_PREP(MCP_DPN_PORTCTRL_BPT_EN, params->bpt_en) | + FIELD_PREP(MCP_DPN_PORTCTRL_BPT_TYPE, + params->bpt_payload_type); + + core_updatel(core, offset, mask, val); +} + +/* passing sync=false without a follow up config update would leave the reset + * pending. + */ +static int mtk_sdw_reset_master(struct mtk_sdw_core *core, bool sync) +{ + int ret = 0; + + core_updatel(core, MCP_CONTROL, MCP_CONTROL_SOFT_RST, + MCP_CONTROL_SOFT_RST); + + if (sync) { + ret = mtk_do_config_update(core); + if (ret) { + dev_err(core->dev, + "do_config_update failed ret = %d\n", ret); + } + } + + return ret; +} + +static int mtk_sdw_change_op_mode(struct mtk_sdw_core *core, int mode) +{ + u32 val; + u32 mask; + + mask = MCP_CONFIG_OP_MODE; + + val = FIELD_PREP(MCP_CONFIG_OP_MODE, mode); + + core_updatel(core, MCP_CONFIG, mask, val); + + return mtk_do_config_update(core); +} + +int mtk_sdw_shutdown_master(struct mtk_sdw_core *core) +{ + int ret; + + ret = mtk_sdw_reset_master(core, false); + if (ret) + return ret; + + ret = mtk_sdw_change_op_mode(core, SDWC_OM_ALL_OFF); + if (ret) { + dev_err(core->dev, "change to SDWC_OM_ALL_OFF fail, ret = %d\n", + ret); + return ret; + } + + return 0; +} + +int mtk_sdw_reinit_master(struct mtk_sdw_core *core) +{ + int ret; + + /* IRQ should be enabled first */ + mtk_sdw_enable_irq(core, true); + + ret = mtk_sdw_core_hw_init(core); + if (ret) { + dev_err(core->dev, + "[%u]mtk_sdw_core_hw_init failed, ret = %d\n", + core->bus.link_id, ret); + mtk_sdw_enable_irq(core, false); + return ret; + } + + return 0; +} diff --git a/drivers/soundwire/mediatek/mtk-sdw-core.h b/drivers/soundwire/mediatek/mtk-sdw-core.h new file mode 100644 index 0000000000000..df1d169775656 --- /dev/null +++ b/drivers/soundwire/mediatek/mtk-sdw-core.h @@ -0,0 +1,262 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * MediaTek SoundWire master — STABLE controller core. + * + * Copyright (c) 2026 MediaTek Inc. + * Author: Trevor Wu + */ +#ifndef __MTK_SDW_CORE_H +#define __MTK_SDW_CORE_H + +#include + +enum { + SDWC_OM_NORMAL = 0, + SDWC_OM_DATA_OFF, + SDWC_OM_CLK_DATA_LOW, + SDWC_OM_CLK_DATA_OFF, + SDWC_OM_KEEPER_OFF, + SDWC_OM_CLK_LOW_DATA_OFF_KEEPER_ON, + SDWC_OM_CLK_LOW_DATA_HIGH, + SDWC_OM_ALL_OFF, +}; + +#define SDW_GSYNC_HZ (8000) +#define SDW_DEFAULT_MAX_CMD_RETRY (5) +/* ======================================================================= + * MCP (Master Control Port) registers + * ======================================================================= + */ +#define MCP_DP_SIZE 128 +#define MCP_PDI_SIZE 16 + +#define MCP_CONFIG 0x0000 +#define MCP_CONTROL 0x0004 +#define MCP_CMDCTRL 0x0008 +#define MCP_SSPSTAT 0x000c +#define MCP_FRAMESHAPE 0x0010 +#define MCP_FRAMESHAPEINIT 0x0014 +#define MCP_CONFIGUPDATE 0x0018 +#define MCP_PHYCTRL 0x001c +#define MCP_SSP_CTRL_B0 0x0020 +#define MCP_SSP_CTRL_B1 0x0028 +#define MCP_CLOCKCTRL_B0 0x0030 +#define MCP_CLOCKCTRL_B1 0x0038 +#define MCP_STAT 0x0040 +#define MCP_INTSTAT 0x0044 +#define MCP_INTMASK 0x0048 +#define MCP_INTSET 0x004c +#define MCP_SLAVESTAT 0x0050 +#define MCP_SLAVEINTSTAT0 0x0054 +#define MCP_SLAVEINTSTAT1 0x0058 +#define MCP_SLAVEINTMASK0 0x005c +#define MCP_SLAVEINTMASK1 0x0060 +#define MCP_PORTINTSTAT 0x0064 +#define MCP_PDISTAT 0x006c +#define MCP_FIFOLEVEL 0x0078 +#define MCP_FIFOSTAT 0x007c +#define MCP_COMMAND(n) (0x0080 + (n) * 4) + +#define MCP_DPN_CONFIG_B0(n) (0x0100 + (n) * MCP_DP_SIZE) +#define MCP_DPN_CHANNELEN_B0(n) (0x0104 + (n) * MCP_DP_SIZE) +#define MCP_DPN_SAMPLECTRL_B0(n) (0x0108 + (n) * MCP_DP_SIZE) +#define MCP_DPN_OFFSETCTRL_B0(n) (0x010c + (n) * MCP_DP_SIZE) +#define MCP_DPN_HCTRL_B0(n) (0x0110 + (n) * MCP_DP_SIZE) +#define MCP_DPN_CONFIG_B1(n) (0x0118 + (n) * MCP_DP_SIZE) +#define MCP_DPN_CHANNELEN_B1(n) (0x011c + (n) * MCP_DP_SIZE) +#define MCP_DPN_SAMPLECTRL_B1(n) (0x0120 + (n) * MCP_DP_SIZE) +#define MCP_DPN_OFFSETCTRL_B1(n) (0x0124 + (n) * MCP_DP_SIZE) +#define MCP_DPN_HCTRL_B1(n) (0x0128 + (n) * MCP_DP_SIZE) +#define MCP_DPN_PORTCTRL(n) (0x0130 + ((n) & 0xf) * MCP_DP_SIZE) +#define MCP_PDIN_CONFIG(n) (0x1100 + ((n) & 0xf) * MCP_PDI_SIZE) + +/* Bank selectors: each takes (port, bank) / (bank) */ +#define MCP_DPN_CONFIG(n, b) \ + ((b) ? MCP_DPN_CONFIG_B1(n) : MCP_DPN_CONFIG_B0(n)) +#define MCP_DPN_CHANNELEN(n, b) \ + ((b) ? MCP_DPN_CHANNELEN_B1(n) : MCP_DPN_CHANNELEN_B0(n)) +#define MCP_DPN_SAMPLECTRL(n, b) \ + ((b) ? MCP_DPN_SAMPLECTRL_B1(n) : MCP_DPN_SAMPLECTRL_B0(n)) +#define MCP_DPN_OFFSETCTRL(n, b) \ + ((b) ? MCP_DPN_OFFSETCTRL_B1(n) : MCP_DPN_OFFSETCTRL_B0(n)) +#define MCP_DPN_HCTRL(n, b) \ + ((b) ? MCP_DPN_HCTRL_B1(n) : MCP_DPN_HCTRL_B0(n)) +#define MCP_CLOCKCTRL(b) ((b) ? MCP_CLOCKCTRL_B1 : MCP_CLOCKCTRL_B0) + +/* MCP_CONFIG */ +#define MCP_CONFIG_OP_MODE GENMASK(2, 0) +#define MCP_CONFIG_MCR GENMASK(27, 24) + +/* MCP_CONTROL */ +#define MCP_CONTROL_CMD_ACCEPT_MODE BIT(1) +#define MCP_CONTROL_CLOCK_STOP_CLR BIT(2) +#define MCP_CONTROL_HW_BUS_RST BIT(4) +#define MCP_CONTROL_SOFT_RST BIT(6) +#define MCP_CONTROL_CMD_RST BIT(7) +#define MCP_CONTROL_RST_DELAY GENMASK(10, 8) + +/* MCP_FRAMESHAPE(INIT) */ +#define MCP_FRAMESHAPE_NC GENMASK(2, 0) /* columns index */ +#define MCP_FRAMESHAPE_NR GENMASK(7, 3) /* rows, encoded (rows/2-1) */ + +/* MCP_CONFIGUPDATE */ +#define MCP_CONFIGUPDATE_CU BIT(0) + +/* MCP_SSP_CTRL */ +#define MCP_SSP_CTRL_SSP_INTERVAL GENMASK(7, 0) + +/* MCP_CLOCKCTRL */ +#define MCP_CLOCKCTRL_MCLKD GENMASK(7, 0) + +/* MCP_STAT */ +#define MCP_STAT_RX_NE BIT(3) +#define MCP_STAT_CLK_STOP BIT(16) + +/* MCP_INTMASK / MCP_INTSTAT */ +#define MCP_INT_RXWL BIT(2) +#define MCP_INT_RXNE BIT(3) +#define MCP_INT_CMD_ERR BIT(7) +#define MCP_INT_PARITY_ERR BIT(8) +#define MCP_INT_DATA_BUS_CLASH BIT(9) +#define MCP_INT_CTL_BUS_CLASH BIT(10) +#define MCP_INT_DP_INT BIT(11) +#define MCP_INT_SLV_NOT_ATTACH BIT(12) +#define MCP_INT_SLV_ATTACHED BIT(13) +#define MCP_INT_SLV_ALERT BIT(14) +#define MCP_INT_SLV_RESERVED BIT(15) +#define MCP_INT_WAKEUP BIT(16) +#define MCP_INT_IRQ BIT(31) +#define MCP_INT_SLV_MASK (MCP_INT_SLV_RESERVED | \ + MCP_INT_SLV_ALERT | \ + MCP_INT_SLV_ATTACHED | \ + MCP_INT_SLV_NOT_ATTACH) +#define MCP_INT_MST_ERR_MASK (MCP_INT_RXWL | \ + MCP_INT_PARITY_ERR | \ + MCP_INT_DATA_BUS_CLASH | \ + MCP_INT_CTL_BUS_CLASH) + +/* MCP_SLAVESTAT: 2 bits/slave; MCP_SLAVEINTSTAT: 4 bits/slave */ +#define MCP_SLAVESTAT_STATUS(x, n) (((x) >> ((n) * 2)) & 0x3) +#define MCP_SLAVESTAT_UNATTACHED 0x0 +#define MCP_SLAVESTAT_ATTACHED 0x1 +#define MCP_SLAVESTAT_ALERT 0x2 +#define MCP_SLAVEINTSTAT_STATUS(x, n) (((x) >> ((n) * 4)) & 0xf) +#define MCP_SLAVEINTSTAT_NOT_PRESENT BIT(0) +#define MCP_SLAVEINTSTAT_ATTACHED BIT(1) +#define MCP_SLAVEINTSTAT_ALERT BIT(2) +#define MCP_SLAVEINTSTAT_RESERVED BIT(3) + +/* MCP_SLAVEINTMASK */ +#define MCP_SLAVE_INTMASK0_MASK GENMASK(31, 0) +#define MCP_SLAVE_INTMASK1_MASK GENMASK(15, 0) + +/* MCP command word. CONFIRMED: dev_addr is 4-bit [27:24]. */ +#define MCP_CMD_READ 0x2 +#define MCP_CMD_WRITE 0x3 +#define MCP_CMD_SSP_TAG BIT(31) +#define MCP_CMD_FIELD GENMASK(30, 28) +#define MCP_CMD_DEV_ADDR GENMASK(27, 24) +#define MCP_CMD_REG_ADDR GENMASK(23, 8) +#define MCP_CMD_REG_DATA GENMASK(7, 0) + +/* MCP command response. CONFIRMED. */ +#define MCP_RESP_ACK BIT(0) +#define MCP_RESP_NAK BIT(1) +#define MCP_RESP_RDATA GENMASK(15, 8) + +/* Per-port transport fields (used by streaming) */ +#define MCP_DPN_CONFIG_PFM GENMASK(1, 0) /* port flow mode */ +#define MCP_DPN_CONFIG_PDM GENMASK(3, 2) /* port data mode */ +/* word length, encoded (wl-1) */ +#define MCP_DPN_CONFIG_WL GENMASK(12, 8) +#define MCP_DPN_CONFIG_BGC GENMASK(17, 16) /* block group control */ +#define MCP_DPN_CONFIG_BPM BIT(18) /* block packing mode */ +#define MCP_DPN_CHANNELEN_EN GENMASK(7, 0) +#define MCP_DPN_SAMPLECTRL_INTERVAL GENMASK(15, 0) /* encoded (interval-1) */ +#define MCP_DPN_OFFSETCTRL_OFFSET1 GENMASK(7, 0) +#define MCP_DPN_OFFSETCTRL_OFFSET2 GENMASK(15, 8) +#define MCP_DPN_HCTRL_HSTOP GENMASK(3, 0) +#define MCP_DPN_HCTRL_HSTART GENMASK(7, 4) +#define MCP_DPN_HCTRL_LANECTRL GENMASK(10, 8) /* data lane select */ + +#define MCP_DPN_PORTCTRL_BPT_TYPE GENMASK(23, 22) +#define MCP_DPN_PORTCTRL_BPT_EN BIT(16) +#define MCP_DPN_PORTCTRL_DIR BIT(7) + +#define MCP_PDIN_CONFIG_SOFTRST BIT(24) +#define MCP_PDIN_CONFIG_CHANNELMASK GENMASK(15, 8) +#define MCP_PDIN_CONFIG_PORTNUM GENMASK(4, 0) + +/* ======================================================================= + * Core object + * ======================================================================= + */ +struct mtk_sdw_core { + struct sdw_bus bus; + struct device *dev; + void __iomem *regs; + unsigned int curr_clk_freq; + bool interrupt_enabled; + u16 attached_slaves; + void *priv; + struct delayed_work status_work; + struct delayed_work attach_check_work; + unsigned int dev0_repoll_count; + unsigned int bus_reset_count; + unsigned int clash_burst; + unsigned long clash_window; + /* serializes all MCP_INTMASK read-modify-write updates */ + spinlock_t intmask_lock; +}; + +struct mtk_sdw_pdi_params { + u32 pdi_num; + u32 port_num; + u32 ch_mask; +}; + +struct mtk_sdw_port_params { + u32 port_num; + u32 bpt_payload_type; + bool bpt_en; +}; + +static inline struct mtk_sdw_core *bus_to_core(struct sdw_bus *bus) +{ + return container_of(bus, struct mtk_sdw_core, bus); +} + +/* ======================================================================= + * Core API — called by the glue + * ======================================================================= + */ +extern const struct sdw_master_ops mtk_sdw_core_ops; +extern const struct sdw_master_port_ops mtk_sdw_core_port_ops; + +/* + * Delay before the one-shot post-init attach check. Peripherals that are + * going to attach do so within tens of ms of the link coming up; 2 s is + * far past that but still inside the 5 s the codec drivers wait for + * re-attach at probe, so a restart triggered here lets them recover + * without a rebind. + */ +#define MTK_SDW_ATTACH_CHECK_DELAY_MS 2000 + +int mtk_sdw_core_init(struct mtk_sdw_core *core); +int mtk_sdw_core_hw_init(struct mtk_sdw_core *core); +int mtk_sdw_core_bus_reset(struct mtk_sdw_core *core); +void mtk_sdw_enable_irq(struct mtk_sdw_core *core, bool enable); +void mtk_sdw_enable_slave_irq(struct mtk_sdw_core *core, bool enable); +irqreturn_t mtk_sdw_core_irq(int irq, void *data); +void mtk_sdw_configure_port_params(struct mtk_sdw_core *core, + struct mtk_sdw_port_params *params, u32 dir); +void mtk_sdw_configure_pdi_params(struct mtk_sdw_core *core, + struct mtk_sdw_pdi_params *params); +void mtk_sdw_clear_pdi_settings(struct mtk_sdw_core *core, + struct mtk_sdw_pdi_params *pdi); +void mtk_sdw_core_dump_dpn_regs(struct mtk_sdw_core *core, unsigned int port, + const char *tag); +int mtk_sdw_shutdown_master(struct mtk_sdw_core *core); +int mtk_sdw_reinit_master(struct mtk_sdw_core *core); + +#endif /* __MTK_SDW_CORE_H */ diff --git a/drivers/soundwire/mediatek/mtk-sdw-mach.c b/drivers/soundwire/mediatek/mtk-sdw-mach.c new file mode 100644 index 0000000000000..967afbaea18d7 --- /dev/null +++ b/drivers/soundwire/mediatek/mtk-sdw-mach.c @@ -0,0 +1,498 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * MediaTek SoundWire audio machine selection (codec-combination tables). + * + * Copyright (c) 2026 MediaTek Inc. + * Author: Trevor Wu + */ +#include +#include +#include +#include "mtk-sdw-mach.h" + +static const struct snd_soc_acpi_endpoint rt1320_endpoints[] = { + { + .num = 0, + .aggregated = 1, + .group_position = 0, + .group_id = 1, + }, + { + .num = 0, + .aggregated = 1, + .group_position = 1, + .group_id = 1, + }, +}; + +static const struct snd_soc_acpi_endpoint rt712_endpoints[] = { + { + .num = 0, + .aggregated = 0, + .group_position = 0, + .group_id = 0, + }, + { + .num = 2, + .aggregated = 0, + .group_position = 0, + .group_id = 0, + }, +}; + +/* + * Config 0: SDW0 = RT1320 (amp), SDW1 = RT712 (jack codec). + */ +static const struct snd_soc_acpi_adr_device rt1320_l0_adr[] = { + { + .adr = 0x000030025D132001ull, + .num_endpoints = 1, + .endpoints = &rt1320_endpoints[0], + .name_prefix = "rt1320-1", + }, + { + .adr = 0x000032025D132001ull, + .num_endpoints = 1, + .endpoints = &rt1320_endpoints[1], + .name_prefix = "rt1320-2", + }, +}; + +static const struct snd_soc_acpi_adr_device rt712_l1_adr[] = { + { + .adr = 0x000130025D071201ull, + .num_endpoints = ARRAY_SIZE(rt712_endpoints), + .endpoints = rt712_endpoints, + .name_prefix = "rt712", + }, +}; + +/* + * Variant of the RT1320 pair with the second amp at unique ID 0x31 + * (the reference layout has it at 0x32). + */ +static const struct snd_soc_acpi_adr_device rt1320x2_l0_adr[] = { + { + .adr = 0x000030025D132001ull, + .num_endpoints = 1, + .endpoints = &rt1320_endpoints[0], + .name_prefix = "rt1320-1", + }, + { + .adr = 0x000031025D132001ull, + .num_endpoints = 1, + .endpoints = &rt1320_endpoints[1], + .name_prefix = "rt1320-2", + }, +}; + +static const struct snd_soc_acpi_link_adr mtk_sdw_rt1320x2_l0_rt712_l1[] = { + { + .mask = BIT(0), + .num_adr = ARRAY_SIZE(rt1320x2_l0_adr), + .adr_d = rt1320x2_l0_adr, + }, + { + .mask = BIT(1), + .num_adr = ARRAY_SIZE(rt712_l1_adr), + .adr_d = rt712_l1_adr, + }, + {} +}; + +static const struct snd_soc_acpi_link_adr mtk_sdw_rt1320_l0_rt712_l1[] = { + { + .mask = BIT(0), + .num_adr = ARRAY_SIZE(rt1320_l0_adr), + .adr_d = rt1320_l0_adr, + }, + { + .mask = BIT(1), + .num_adr = ARRAY_SIZE(rt712_l1_adr), + .adr_d = rt712_l1_adr, + }, + {} +}; + +/* + * Config 1: SDW0 = 4x CS35L56 (amps, aggregated), SDW1 = CS42L43 (jack codec). + * Topology: uids 0x30-0x33 on link0, 0x30 on + * link1. Speaker position vs uid mapping to be confirmed on hardware; the + * group positions below assume uid order. + */ +static const struct snd_soc_acpi_endpoint cs35l56_endpoints[] = { + { + .num = 0, + .aggregated = 1, + .group_position = 0, + .group_id = 1, + }, + { + .num = 0, + .aggregated = 1, + .group_position = 1, + .group_id = 1, + }, + { + .num = 0, + .aggregated = 1, + .group_position = 2, + .group_id = 1, + }, + { + .num = 0, + .aggregated = 1, + .group_position = 3, + .group_id = 1, + }, +}; + +static const struct snd_soc_acpi_endpoint cs42l43_endpoints[] = { + { /* Jack Playback Endpoint */ + .num = 0, + .aggregated = 0, + .group_position = 0, + .group_id = 0, + }, + { /* DMIC Capture Endpoint */ + .num = 1, + .aggregated = 0, + .group_position = 0, + .group_id = 0, + }, + { /* Jack Capture Endpoint */ + .num = 2, + .aggregated = 0, + .group_position = 0, + .group_id = 0, + }, +}; + +static const struct snd_soc_acpi_adr_device cs35l56x4_l0_adr[] = { + { + .adr = 0x00003001FA355601ull, + .num_endpoints = 1, + .endpoints = &cs35l56_endpoints[0], + .name_prefix = "AMP1", + }, + { + .adr = 0x00003101FA355601ull, + .num_endpoints = 1, + .endpoints = &cs35l56_endpoints[1], + .name_prefix = "AMP2", + }, + { + .adr = 0x00003201FA355601ull, + .num_endpoints = 1, + .endpoints = &cs35l56_endpoints[2], + .name_prefix = "AMP3", + }, + { + .adr = 0x00003301FA355601ull, + .num_endpoints = 1, + .endpoints = &cs35l56_endpoints[3], + .name_prefix = "AMP4", + }, +}; + +static const struct snd_soc_acpi_adr_device cs42l43_l1_adr[] = { + { + .adr = 0x00013001FA424301ull, + /* + * The cs42l43 Speaker Playback endpoint (num 3, the sidecar-amp + * path) is intentionally not in cs42l43_endpoints: on these + * boards the speaker amps are separate SoundWire peripherals. + */ + .num_endpoints = ARRAY_SIZE(cs42l43_endpoints), + .endpoints = cs42l43_endpoints, + .name_prefix = "cs42l43", + }, +}; + +static const struct snd_soc_acpi_link_adr mtk_sdw_cs35l56_l0_cs42l43_l1[] = { + { + .mask = BIT(0), + .num_adr = ARRAY_SIZE(cs35l56x4_l0_adr), + .adr_d = cs35l56x4_l0_adr, + }, + { + .mask = BIT(1), + .num_adr = ARRAY_SIZE(cs42l43_l1_adr), + .adr_d = cs42l43_l1_adr, + }, + {} +}; + +/* + * Config 2: SDW0 = 2x RT1321 (amps, aggregated), SDW1 = RT713 (jack codec). + * Topology: uids 0x30/0x31 on link0, 0x30 + * on link1. RT1321/RT713 are handled by the rt1320-sdw/rt712-sdca drivers; + * name prefixes follow the driver family so the control names match the + * RT1320/RT712 configuration. + */ +static const struct snd_soc_acpi_adr_device rt1321_l0_adr[] = { + { + .adr = 0x000030025D132101ull, + .num_endpoints = 1, + .endpoints = &rt1320_endpoints[0], + .name_prefix = "rt1320-1", + }, + { + .adr = 0x000031025D132101ull, + .num_endpoints = 1, + .endpoints = &rt1320_endpoints[1], + .name_prefix = "rt1320-2", + }, +}; + +/* + * RT713 exposes two DAIs (0 = jack, 1 = DMIC), unlike RT712 whose DMIC + * DAI sits at index 2 behind the amp DAI, so it needs its own endpoint + * list rather than reusing rt712_endpoints. + */ +static const struct snd_soc_acpi_endpoint rt713_endpoints[] = { + { + .num = 0, + .aggregated = 0, + .group_position = 0, + .group_id = 0, + }, + { + .num = 1, + .aggregated = 0, + .group_position = 0, + .group_id = 0, + }, +}; + +static const struct snd_soc_acpi_adr_device rt713_l1_adr[] = { + { + .adr = 0x000130025D071301ull, + .num_endpoints = ARRAY_SIZE(rt713_endpoints), + .endpoints = rt713_endpoints, + .name_prefix = "rt713", + }, +}; + +static const struct snd_soc_acpi_link_adr mtk_sdw_rt1321_l0_rt713_l1[] = { + { + .mask = BIT(0), + .num_adr = ARRAY_SIZE(rt1321_l0_adr), + .adr_d = rt1321_l0_adr, + }, + { + .mask = BIT(1), + .num_adr = ARRAY_SIZE(rt713_l1_adr), + .adr_d = rt713_l1_adr, + }, + {} +}; + +/* + * Config 3: SDW1 = CS42L43 (jack codec) + 2x CS35L56 (amps, aggregated), + * link0 unused. Topology: all three devices + * on link1, CS42L43 at uid 0x30, amps at uids 0x30/0x31 (peripherals of + * different part numbers may share unique IDs on one link). + */ +static const struct snd_soc_acpi_adr_device cs35l56x2_cs42l43_l1_adr[] = { + { + .adr = 0x00013001FA424301ull, + /* Speaker Playback endpoint (num 3) excluded as on the HP + * entry: the amps are SoundWire peripherals, not the cs42l43 + * sidecar path. + */ + .num_endpoints = ARRAY_SIZE(cs42l43_endpoints), + .endpoints = cs42l43_endpoints, + .name_prefix = "cs42l43", + }, + { + .adr = 0x00013001FA355601ull, + .num_endpoints = 1, + .endpoints = &cs35l56_endpoints[0], + .name_prefix = "AMP1", + }, + { + .adr = 0x00013101FA355601ull, + .num_endpoints = 1, + .endpoints = &cs35l56_endpoints[1], + .name_prefix = "AMP2", + }, +}; + +static const struct snd_soc_acpi_link_adr mtk_sdw_cs42l43_cs35l56x2_l1[] = { + { + .mask = BIT(1), + .num_adr = ARRAY_SIZE(cs35l56x2_cs42l43_l1_adr), + .adr_d = cs35l56x2_cs42l43_l1_adr, + }, + {} +}; + +/* + * Config 4: SDW1 = CS42L43 (jack codec) + 4x CS35L56 (amps, aggregated), + * link0 unused. All five devices on link1: CS42L43 at uid 0x30, amps at + * uids 0x30/0x31/0x34/0x35 (peripherals of different part numbers may + * share unique IDs on one link). Superset of the two-amp single-link + * entry, so it must precede that entry in the machine table. + */ +static const struct snd_soc_acpi_adr_device cs35l56x4_cs42l43_l1_adr[] = { + { + .adr = 0x00013001FA424301ull, + /* Speaker Playback endpoint (num 3) excluded as on the other + * Cirrus entries: the amps are SoundWire peripherals, not the + * cs42l43 sidecar path. + */ + .num_endpoints = ARRAY_SIZE(cs42l43_endpoints), + .endpoints = cs42l43_endpoints, + .name_prefix = "cs42l43", + }, + { + .adr = 0x00013001FA355601ull, + .num_endpoints = 1, + .endpoints = &cs35l56_endpoints[0], + .name_prefix = "AMP1", + }, + { + .adr = 0x00013101FA355601ull, + .num_endpoints = 1, + .endpoints = &cs35l56_endpoints[1], + .name_prefix = "AMP2", + }, + { + .adr = 0x00013401FA355601ull, + .num_endpoints = 1, + .endpoints = &cs35l56_endpoints[2], + .name_prefix = "AMP3", + }, + { + .adr = 0x00013501FA355601ull, + .num_endpoints = 1, + .endpoints = &cs35l56_endpoints[3], + .name_prefix = "AMP4", + }, +}; + +static const struct snd_soc_acpi_link_adr mtk_sdw_cs42l43_cs35l56x4_l1[] = { + { + .mask = BIT(1), + .num_adr = ARRAY_SIZE(cs35l56x4_cs42l43_l1_adr), + .adr_d = cs35l56x4_cs42l43_l1_adr, + }, + {} +}; + +static struct snd_soc_acpi_mach mtk_sdw_machines[] = { + { + .link_mask = BIT(0) | BIT(1), + .links = mtk_sdw_rt1320_l0_rt712_l1, + .drv_name = "mtk_sdw_mc", + }, + { + .link_mask = BIT(0) | BIT(1), + .links = mtk_sdw_rt1321_l0_rt713_l1, + .drv_name = "mtk_sdw_mc", + }, + { + .link_mask = BIT(0) | BIT(1), + .links = mtk_sdw_rt1320x2_l0_rt712_l1, + .drv_name = "mtk_sdw_mc", + }, + { + .link_mask = BIT(0) | BIT(1), + .links = mtk_sdw_cs35l56_l0_cs42l43_l1, + .drv_name = "mtk_sdw_mc", + }, + /* + * The four-amp single-link entry must precede the two-amp one: + * matching is first-fit and tolerates extra enumerated peripherals, + * so on a four-amp board the two-amp entry would otherwise win and + * register a card with only half the speakers. + */ + { + .link_mask = BIT(1), + .links = mtk_sdw_cs42l43_cs35l56x4_l1, + .drv_name = "mtk_sdw_mc", + }, + { + .link_mask = BIT(1), + .links = mtk_sdw_cs42l43_cs35l56x2_l1, + .drv_name = "mtk_sdw_mc", + }, + {} +}; + +static void mtk_sdw_log_peripheral(struct device *dev, + struct sdw_slave *slave) +{ + dev_info(dev, + "[%d] class=0x%02x mfg=0x%04x part=0x%04x uniq=0x%x ver=0x%x\n", + slave->bus->link_id, slave->id.class_id, slave->id.mfg_id, + slave->id.part_id, slave->id.unique_id, slave->id.sdw_version); +} + +static struct sdw_peripherals *mtk_sdw_get_peripherals(struct mtk_sdw *mst) +{ + struct sdw_peripherals *peripherals; + struct sdw_slave *slave; + struct sdw_bus *bus; + int max_peripherals_num; + int i, idx = 0; + + max_peripherals_num = mst->num_links * SDW_MAX_DEVICES; + + peripherals = kmalloc_flex(*peripherals, array, max_peripherals_num, + GFP_KERNEL); + if (!peripherals) + return NULL; + + for (i = 0; i < mst->num_links; i++) { + bus = &mst->links[i].core.bus; + mutex_lock(&bus->bus_lock); + list_for_each_entry(slave, &bus->slaves, node) { + if (idx >= max_peripherals_num) { + dev_warn(mst->dev, "slave count exceeds capacity, truncating"); + break; + } + peripherals->array[idx++] = slave; + mtk_sdw_log_peripheral(mst->dev, slave); + } + mutex_unlock(&bus->bus_lock); + } + peripherals->num_peripherals = idx; + + return peripherals; +} + +struct snd_soc_acpi_mach *mtk_sdw_machine_select(struct mtk_sdw *mst) +{ + struct sdw_peripherals *peripherals; + struct snd_soc_acpi_mach *mach; + const struct snd_soc_acpi_link_adr *link; + + peripherals = mtk_sdw_get_peripherals(mst); + if (!peripherals) + return NULL; + + if (!peripherals->num_peripherals) { + dev_dbg(mst->dev, "no SoundWire peripherals enumerated\n"); + kfree(peripherals); + return NULL; + } + + for (mach = mtk_sdw_machines; mach->link_mask; mach++) { + for (link = mach->links; link && link->num_adr; link++) { + if (!snd_soc_acpi_sdw_link_slaves_found(mst->dev, link, + peripherals)) + break; + } + if (link && !link->num_adr) { + mach->mach_params.links = mach->links; + mach->mach_params.link_mask = mach->link_mask; + kfree(peripherals); + return mach; + } + } + dev_dbg(mst->dev, "no SoundWire machine matched\n"); + kfree(peripherals); + return NULL; +} diff --git a/drivers/soundwire/mediatek/mtk-sdw-mach.h b/drivers/soundwire/mediatek/mtk-sdw-mach.h new file mode 100644 index 0000000000000..158aae2652b18 --- /dev/null +++ b/drivers/soundwire/mediatek/mtk-sdw-mach.h @@ -0,0 +1,20 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * MediaTek SoundWire audio machine selection (codec-combination tables). + * + * Copyright (c) 2026 MediaTek Inc. + * Author: Trevor Wu + */ +#ifndef __MTK_SDW_MACH_H +#define __MTK_SDW_MACH_H + +#include "mtk-sdw-common.h" + +struct snd_soc_acpi_mach; + +/* mtk-sdw-mach.c: match enumerated SoundWire peripherals against the codec- + * combination tables; returns the machine row to spawn, or NULL. + */ +struct snd_soc_acpi_mach *mtk_sdw_machine_select(struct mtk_sdw *mst); + +#endif /* __MTK_SDW_MACH_H */ diff --git a/drivers/soundwire/mediatek/mtk-sdw-top.c b/drivers/soundwire/mediatek/mtk-sdw-top.c new file mode 100644 index 0000000000000..2c38a93b96f38 --- /dev/null +++ b/drivers/soundwire/mediatek/mtk-sdw-top.c @@ -0,0 +1,381 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * MediaTek SoundWire master — AFE TOP control + * + * Copyright (c) 2026 MediaTek Inc. + * Author: Trevor Wu + */ + +#include +#include +#include +#include +#include +#include +#include "mtk-sdw-top.h" + +/* these two arrays cover everything written by top_init and configure_pdi */ +static const unsigned int mtk_sdw_top_con_backup_regs[] = { + SDW_TOP_CON0, + SDW_TOP_CON1, +}; + +static const unsigned int mtk_sdw_top_pdi_backup_regs[] = { + SDW_TOP_PDIN_CON0(0), + SDW_TOP_PDIN_CON0(1), + SDW_TOP_PDIN_CON0(2), + SDW_TOP_PDIN_CON0(3), + SDW_TOP_PDIN_CON0(4), + SDW_TOP_PDIN_CON0(5), + SDW_TOP_PDIN_CON0(6), + SDW_TOP_PDIN_CON0(7), + SDW_TOP_PDIN_CON0(8), + SDW_TOP_PDIN_CON0(9), + SDW_TOP_PDIN_CON0(10), + SDW_TOP_PDIN_CON0(11), + SDW_TOP_PDIN_CON0(12), + SDW_TOP_PDIN_CON0(13), + SDW_TOP_PDIN_CON0(14), + SDW_TOP_PDIN_CON0(15), + SDW_TOP_PDIN_CON0(16), + SDW_TOP_PDIN_CON0(17), +}; + +static const struct mtk_sdw_top_cfg mtk_sdw_top_cfgs[MTK_SDW_TOP_VER_MAX] = { + [MTK_SDW_TOP_VER_0] = { + .name = "hw_8901", + .reg = { + .tzd_delay_reg = SDW_TOP_CON0, + .tzd_delay_sel = GENMASK(5, 3), + .tzd_inverse = BIT(2), + .tzd_delay_en = BIT(1), + .phy_delay_reg = SDW_TOP_CON1, + .phy_delay_sel = GENMASK(3, 1), + .phy_double_delay_sel = GENMASK(6, 4), + }, + }, +}; + +static u32 topcon_readl(struct mtk_sdw *mst, u32 off) +{ + return readl(mst->top_con + off); +} + +static void topcon_writel(struct mtk_sdw *mst, u32 off, u32 val) +{ + writel(val, mst->top_con + off); +} + +static void topcon_updatel(struct mtk_sdw *mst, u32 off, u32 mask, u32 val) +{ + u32 tmp = topcon_readl(mst, off); + + topcon_writel(mst, off, (tmp & ~mask) | (val & mask)); +} + +static u32 toppdi_readl(struct mtk_sdw *mst, u32 off) +{ + return readl(mst->top_pdi + off); +} + +static void toppdi_writel(struct mtk_sdw *mst, u32 off, u32 val) +{ + writel(val, mst->top_pdi + off); +} + +static void toppdi_updatel(struct mtk_sdw *mst, u32 off, u32 mask, u32 val) +{ + u32 tmp = toppdi_readl(mst, off); + + toppdi_writel(mst, off, (tmp & ~mask) | (val & mask)); +} + +static inline u32 mtk_field_prep(u32 mask, u32 val) +{ + if (!mask) + return 0; + return (val << __bf_shf(mask)) & mask; +} + +int mtk_sdw_top_config_select(struct mtk_sdw *mst, int hw_ver) +{ + int ver_id; + + switch (hw_ver) { + case MTK_SDW_HW_VER_MT8901: + case MTK_SDW_HW_VER_MT8971: + ver_id = MTK_SDW_TOP_VER_0; + break; + default: + dev_err(mst->dev, "unsupported hw-ver %d\n", hw_ver); + return -EINVAL; + } + + mst->top_cfg = &mtk_sdw_top_cfgs[ver_id]; + dev_info(mst->dev, "AFE TOP variant: %s (hw-ver %u)\n", + mst->top_cfg->name, mst->hw_ver); + return 0; +} + +static void mtk_sdw_top_init_settings(struct mtk_sdw *mst) +{ + topcon_updatel(mst, SDW_TOP_CON0, SDW_TOP_CON0_GSYNC_DIR_CTRL, + SDW_TOP_CON0_GSYNC_DIR_CTRL); +} + +static void mtk_sdw_top_configure_delays(struct mtk_sdw *mst) +{ + u32 val = 0; + u32 mask; + const struct mtk_sdw_top_reg *top_reg = &mst->top_cfg->reg; + + mask = top_reg->tzd_delay_sel | top_reg->tzd_inverse | + top_reg->tzd_delay_en; + val |= mtk_field_prep(top_reg->tzd_delay_sel, mst->tzd_delay - 1); + if (mst->tzd_inverse) + val |= top_reg->tzd_inverse; + if (mst->tzd_delay) + val |= top_reg->tzd_delay_en; + topcon_updatel(mst, top_reg->tzd_delay_reg, mask, val); + + val = 0; + mask = top_reg->phy_delay_sel | top_reg->phy_double_delay_sel; + val |= mtk_field_prep(top_reg->phy_delay_sel, mst->phy_delay); + val |= mtk_field_prep(top_reg->phy_double_delay_sel, + mst->phy_double_delay); + + topcon_updatel(mst, top_reg->phy_delay_reg, mask, val); +} + +int mtk_sdw_top_init(struct mtk_sdw *mst) +{ + struct device *dev = mst->dev; + + mst->top_pdi_reg_backup_num = ARRAY_SIZE(mtk_sdw_top_pdi_backup_regs); + mst->top_con_reg_backup_num = ARRAY_SIZE(mtk_sdw_top_con_backup_regs); + + mst->top_pdi_reg_backup = + devm_kcalloc(dev, mst->top_pdi_reg_backup_num, + sizeof(*mst->top_pdi_reg_backup), + GFP_KERNEL); + if (!mst->top_pdi_reg_backup) + return -ENOMEM; + + mst->top_con_reg_backup = + devm_kcalloc(dev, mst->top_con_reg_backup_num, + sizeof(*mst->top_con_reg_backup), + GFP_KERNEL); + if (!mst->top_con_reg_backup) + return -ENOMEM; + + mtk_sdw_top_init_settings(mst); + mtk_sdw_top_configure_delays(mst); + + return 0; +} + +void mtk_sdw_top_configure_pdi(struct mtk_sdw *mst, u32 pdi, + struct mtk_sdw_top_pdi_params *param) +{ + u32 val = 0; + u32 mask = 0; + + mask |= SDW_TOP_PDI_GROUP_SYNC; + + val |= FIELD_PREP(SDW_TOP_PDI_SLAVE_MODE, param->slave_mode); + mask |= SDW_TOP_PDI_SLAVE_MODE; + + val |= FIELD_PREP(SDW_TOP_PDI_BRA_SLAVE, param->bra_slave); + mask |= SDW_TOP_PDI_BRA_SLAVE; + + val |= FIELD_PREP(SDW_TOP_PDI_BRA_EOP, param->bra_eop); + mask |= SDW_TOP_PDI_BRA_EOP; + + val |= FIELD_PREP(SDW_TOP_PDI_BRA_MODE, param->bra_mode); + mask |= SDW_TOP_PDI_BRA_MODE; + + val |= FIELD_PREP(SDW_TOP_PDI_DMIC_1XEN, param->dmic_1xen); + mask |= SDW_TOP_PDI_DMIC_1XEN; + + val |= FIELD_PREP(SDW_TOP_PDI_PDM_ONE_WIRE, param->pdm_one_wire); + mask |= SDW_TOP_PDI_PDM_ONE_WIRE; + + val |= FIELD_PREP(SDW_TOP_PDI_PDM_MODE, param->pdm_mode); + mask |= SDW_TOP_PDI_PDM_MODE; + + val |= FIELD_PREP(SDW_TOP_PDI_HD_SEL, param->hd_sel); + mask |= SDW_TOP_PDI_HD_SEL; + + val |= FIELD_PREP(SDW_TOP_PDI_CLOCK, param->clock); + mask |= SDW_TOP_PDI_CLOCK; + + val |= FIELD_PREP(SDW_TOP_PDI_PDM2PCM, param->pdm2pcm); + mask |= SDW_TOP_PDI_PDM2PCM; + + val |= FIELD_PREP(SDW_TOP_PDI_IP_SEL, param->sdw_ip_sel); + mask |= SDW_TOP_PDI_IP_SEL; + + val |= FIELD_PREP(SDW_TOP_PDI_DOMAIN, param->domain); + mask |= SDW_TOP_PDI_DOMAIN; + + val |= FIELD_PREP(SDW_TOP_PDI_FS, param->fs); + mask |= SDW_TOP_PDI_FS; + + toppdi_updatel(mst, SDW_TOP_PDIN_CON0(pdi), mask, val); +} + +static void mtk_sdw_top_enable_pdi_engen(struct mtk_sdw *mst, u32 pdi, + bool enable) +{ + toppdi_updatel(mst, SDW_TOP_PDIN_CON0(pdi), SDW_TOP_PDI_EN, + enable ? SDW_TOP_PDI_EN : 0); +} + +static void mtk_sdw_top_enable_group_sync(struct mtk_sdw *mst, u32 pdi, + bool enable) +{ + toppdi_updatel(mst, SDW_TOP_PDIN_CON0(pdi), SDW_TOP_PDI_GROUP_SYNC_ON, + enable ? SDW_TOP_PDI_GROUP_SYNC_ON : 0); +} + +static void mtk_sdw_top_config_group_sync(struct mtk_sdw *mst, u32 pdi, + u32 group) +{ + toppdi_updatel(mst, SDW_TOP_PDIN_CON0(pdi), SDW_TOP_PDI_GROUP_SYNC, + FIELD_PREP(SDW_TOP_PDI_GROUP_SYNC, group)); +} + +int mtk_sdw_top_group_sync_config_get(struct mtk_sdw *mst, u8 id, u32 *cfg) +{ + if (id > MTK_SDW_MAX_GROUP_SYNC) + return -EINVAL; + else if (id == GROUP_SYNC_NONE) + *cfg = 0; + else + *cfg = BIT(id - 1); + + return 0; +} + +int mtk_sdw_top_group_sync_acquire(struct mtk_sdw *mst, bool is_tx) +{ + u8 min_id = is_tx ? TOP_GROUP_SYNC_TX_MIN_ID : TOP_GROUP_SYNC_RX_MIN_ID; + u8 max_id = is_tx ? TOP_GROUP_SYNC_TX_MAX_ID : TOP_GROUP_SYNC_RX_MAX_ID; + int best = -1; + int best_refcnt = INT_MAX; + int i, idx; + + guard(mutex)(&mst->group_lock); + + for (i = 0; i < MTK_SDW_MAX_GROUP_SYNC; i++) { + idx = is_tx ? i : (MTK_SDW_MAX_GROUP_SYNC - 1 - i); + + if (idx < min_id - 1 || idx > max_id - 1) + continue; + if (mst->group_refcnt[idx] < best_refcnt) { + best = idx; + best_refcnt = mst->group_refcnt[idx]; + } + } + if (best < 0) + return -EINVAL; + + mst->group_refcnt[best]++; + return best + 1; /* 1-based id */ +} + +void mtk_sdw_top_group_sync_release(struct mtk_sdw *mst, u8 id) +{ + if (id == 0 || id > MTK_SDW_MAX_GROUP_SYNC) + return; + + guard(mutex)(&mst->group_lock); + + if (mst->group_refcnt[id - 1] <= 0) { + dev_err(mst->dev, "unexpected refcnt %d\n", + mst->group_refcnt[id - 1]); + return; + } + mst->group_refcnt[id - 1]--; +} + +int mtk_sdw_top_enable_stream(struct mtk_sdw *mst, int dai_id) +{ + struct mtk_sdw_dai *mdai = &mst->dais[dai_id]; + int i; + u32 pdi_num, group_sync; + + for (i = 0; i < mdai->pdi_count; i++) { + pdi_num = mdai->pdi_params[i].pdi_num; + mtk_sdw_top_enable_pdi_engen(mst, pdi_num, true); + } + + if (mdai->need_enable_delay) + usleep_range(1000, 1100); + + if (mdai->use_group_sync) { + for (i = 0; i < mdai->pdi_count; i++) { + pdi_num = mdai->pdi_params[i].pdi_num; + group_sync = mdai->top_pdi_params[i].group_sync; + mtk_sdw_top_config_group_sync(mst, pdi_num, group_sync); + } + for (i = 0; i < mdai->pdi_count; i++) { + pdi_num = mdai->pdi_params[i].pdi_num; + mtk_sdw_top_enable_group_sync(mst, pdi_num, true); + } + } + + return 0; +} + +int mtk_sdw_top_disable_stream(struct mtk_sdw *mst, int dai_id) +{ + struct mtk_sdw_dai *mdai = &mst->dais[dai_id]; + int i; + u32 pdi_num; + + for (i = 0; i < mdai->pdi_count; i++) { + pdi_num = mdai->pdi_params[i].pdi_num; + mtk_sdw_top_enable_pdi_engen(mst, pdi_num, false); + } + + if (mdai->use_group_sync) { + for (i = 0; i < mdai->pdi_count; i++) { + pdi_num = mdai->pdi_params[i].pdi_num; + mtk_sdw_top_enable_group_sync(mst, pdi_num, false); + } + + for (i = 0; i < mdai->pdi_count; i++) { + pdi_num = mdai->pdi_params[i].pdi_num; + mtk_sdw_top_config_group_sync(mst, pdi_num, 0); + } + } + + return 0; +} + +void mtk_sdw_top_backup_regs(struct mtk_sdw *mst) +{ + int i; + + for (i = 0; i < mst->top_con_reg_backup_num; i++) + mst->top_con_reg_backup[i] = + topcon_readl(mst, mtk_sdw_top_con_backup_regs[i]); + + for (i = 0; i < mst->top_pdi_reg_backup_num; i++) + mst->top_pdi_reg_backup[i] = + toppdi_readl(mst, mtk_sdw_top_pdi_backup_regs[i]); +} + +void mtk_sdw_top_restore_regs(struct mtk_sdw *mst) +{ + int i; + + for (i = 0; i < mst->top_con_reg_backup_num; i++) + topcon_writel(mst, mtk_sdw_top_con_backup_regs[i], + mst->top_con_reg_backup[i]); + + for (i = 0; i < mst->top_pdi_reg_backup_num; i++) + toppdi_writel(mst, mtk_sdw_top_pdi_backup_regs[i], + mst->top_pdi_reg_backup[i]); +} diff --git a/drivers/soundwire/mediatek/mtk-sdw-top.h b/drivers/soundwire/mediatek/mtk-sdw-top.h new file mode 100644 index 0000000000000..b643b5cfc01d8 --- /dev/null +++ b/drivers/soundwire/mediatek/mtk-sdw-top.h @@ -0,0 +1,106 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * MediaTek SoundWire master — AFE TOP control. + * + * Copyright (c) 2026 MediaTek Inc. + * Author: Trevor Wu + */ +#ifndef __MTK_SDW_TOP_H +#define __MTK_SDW_TOP_H + +#include "mtk-sdw-common.h" + +enum mtk_sdw_group_sync_sel { + GROUP_SYNC_NONE = 0, + GROUP_1_SYNC, + GROUP_2_SYNC, + GROUP_3_SYNC, + GROUP_4_SYNC, +}; + +enum mtk_sdw_top_ver { + MTK_SDW_TOP_VER_0 = 0, + MTK_SDW_TOP_VER_MAX, +}; + +/* ---- AFE TOP registers (offsets that DON'T vary stay as #defines) ---- + */ +#define SDW_TOP_PDIN_CON0(n) ((n) * 4) /* rel. top_pdi */ +#define SDW_TOP_PDI_EN BIT(0) +#define SDW_TOP_PDI_FS GENMASK(5, 1) +#define SDW_TOP_PDI_DOMAIN GENMASK(8, 6) +#define SDW_TOP_PDI_IP_SEL BIT(9) +#define SDW_TOP_PDI_PDM2PCM BIT(10) +#define SDW_TOP_PDI_CLOCK BIT(11) +#define SDW_TOP_PDI_HD_SEL BIT(12) +#define SDW_TOP_PDI_PDM_MODE BIT(13) +#define SDW_TOP_PDI_PDM_ONE_WIRE BIT(14) +#define SDW_TOP_PDI_DMIC_1XEN BIT(16) +#define SDW_TOP_PDI_BRA_MODE BIT(17) +#define SDW_TOP_PDI_BRA_EOP BIT(18) +#define SDW_TOP_PDI_BRA_SLAVE BIT(19) +#define SDW_TOP_PDI_SLAVE_MODE GENMASK(21, 20) +#define SDW_TOP_PDI_GROUP_SYNC_ON BIT(22) +#define SDW_TOP_PDI_GROUP_SYNC GENMASK(26, 23) + +#define SDW_TOP_CON0 0x0000 /* rel. top_con */ +#define SDW_TOP_CON0_GSYNC_DIR_CTRL BIT(7) + +#define SDW_TOP_CON1 0x0004 + +#define TOP_GROUP_SYNC_TX_MIN_ID 1 +#define TOP_GROUP_SYNC_TX_MAX_ID 2 +#define TOP_GROUP_SYNC_RX_MIN_ID 1 +#define TOP_GROUP_SYNC_RX_MAX_ID 4 + +struct mtk_sdw_top_pdi_params { + u32 group_sync; + u32 slave_mode; + u32 bra_slave; + u32 bra_eop; + u32 bra_mode; + u32 dmic_1xen; + u32 pdm_one_wire; + u32 pdm_mode; + u32 hd_sel; + u32 clock; + u32 pdm2pcm; + u32 sdw_ip_sel; + u32 domain; + u32 fs; +}; + +struct mtk_sdw_top_reg { + u32 tzd_delay_reg; + u32 tzd_delay_sel; + u32 tzd_inverse; + u32 tzd_delay_en; + u32 phy_delay_reg; + u32 phy_delay_sel; + u32 phy_double_delay_sel; +}; + +struct mtk_sdw_top_cfg { + const char *name; + struct mtk_sdw_top_reg reg; + /* + * u32 features; + * can be added for a bitfield if new features are supported + * in the future. + * + */ +}; + +int mtk_sdw_top_config_select(struct mtk_sdw *mst, int hw_ver); +int mtk_sdw_top_init(struct mtk_sdw *mst); +void mtk_sdw_top_configure_pdi(struct mtk_sdw *mst, u32 pdi, + struct mtk_sdw_top_pdi_params *param); +int mtk_sdw_top_group_sync_config_get(struct mtk_sdw *mst, u8 id, u32 *cfg); +int mtk_sdw_top_group_sync_acquire(struct mtk_sdw *mst, bool is_tx); +void mtk_sdw_top_group_sync_release(struct mtk_sdw *mst, u8 id); +int mtk_sdw_top_enable_stream(struct mtk_sdw *mst, int dai_id); +int mtk_sdw_top_disable_stream(struct mtk_sdw *mst, int dai_id); +void mtk_sdw_top_backup_regs(struct mtk_sdw *mst); +void mtk_sdw_top_restore_regs(struct mtk_sdw *mst); + +#endif /* __MTK_SDW_TOP_H */ diff --git a/drivers/soundwire/mediatek/mtk-sdw.c b/drivers/soundwire/mediatek/mtk-sdw.c new file mode 100644 index 0000000000000..5ddf902adf548 --- /dev/null +++ b/drivers/soundwire/mediatek/mtk-sdw.c @@ -0,0 +1,1629 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * MediaTek SoundWire master driver + * + * Copyright (c) 2026 MediaTek Inc. + * Author: Trevor Wu + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "mtk-sdw.h" +#include "mtk-sdw-mach.h" +#include "mtk-sdw-top.h" +#include "bus.h" + +/* + * Restart (SOFT_RST + quiesce + re-init) every link at probe instead of + * adopting the POST-enumerated bus, matching the reference stack's + * ResetAndReInitMaster-on-every-init behavior. Clears defective + * persistent peripheral state that survives reboots on the always-on + * codec rail. Boot-time cost is one restart dwell per link. + */ +static bool restart_on_probe = true; +module_param(restart_on_probe, bool, 0444); +MODULE_PARM_DESC(restart_on_probe, "Restart each SoundWire link at probe instead of adopting the POST-enumerated bus (default: on)"); + +static acpi_status mtk_sdw_apply_pin_bias_cb(struct acpi_resource *res, + void *context) +{ + struct mtk_sdw_acpi_cb_context *ctx = context; + struct device *dev = ctx->dev; + struct acpi_resource_pin_function *pf; + u32 pullup; + int i, err; + + if (res->type != ACPI_RESOURCE_TYPE_PIN_FUNCTION) + return AE_OK; + + pf = &res->data.pin_function; + + switch (pf->pin_config) { + case MTK_SDW_PIN_CFG_BUS_HOLD: + pullup = MTK_PIN_BUS_HOLD; + break; + case MTK_SDW_PIN_CFG_PD: + pullup = MTK_PIN_PULLDOWN; + break; + default: + dev_dbg(dev, + "SDW _CRS PinFunction pin_cfg=0x%02x - leaving default bias\n", + pf->pin_config); + return AE_OK; + } + + for (i = 0; i < pf->pin_table_length; i++) { + unsigned int pin = pf->pin_table[i]; + + err = mtk_pinctrl_program_bias_by_gpio(pin, pullup, + MTK_PIN_ENABLE); + if (err == -EPROBE_DEFER) { + ctx->err = -EPROBE_DEFER; + return AE_CTRL_TERMINATE; + } else if (err == -ENODEV) { + dev_warn(dev, + "SDW pin %u: no pinctrl owns this GPIO - DSDT/SoC mismatch\n", + pin); + } else if (err) { + dev_warn(dev, + "SDW pin %u: bias program failed (pin_cfg=0x%02x, err=%d)\n", + pin, pf->pin_config, err); + } else { + dev_dbg(dev, + "SDW pin %u: pin_cfg=0x%02x -> pullup=%u applied\n", + pin, pf->pin_config, pullup); + } + } + + return AE_OK; +} + +static int mtk_sdw_apply_pin_config(struct device *dev) +{ + acpi_handle handle = ACPI_HANDLE(dev); + acpi_status status; + struct mtk_sdw_acpi_cb_context ctx = { + .dev = dev, + .err = 0, + }; + + if (!handle) { + dev_info(dev, "No ACPI handle\n"); + return 0; + } + + /* pin bias */ + status = acpi_walk_resources(handle, METHOD_NAME__CRS, + mtk_sdw_apply_pin_bias_cb, &ctx); + + if (ACPI_FAILURE(status)) { + dev_warn(dev, + "SDW _CRS walk failed (0x%x) - pin bias not programmed from DSDT\n", + status); + return -EIO; + } + + /* only handle a defer probe error*/ + if (ctx.err == -EPROBE_DEFER) + return -EPROBE_DEFER; + + return 0; +} + +static int mtk_sdw_parse_dp_allocation(struct mtk_sdw *mst, int idx) +{ + struct mtk_sdw_link *link = &mst->links[idx]; + struct fwnode_handle *node; + char key[40]; + u32 dp[4]; + int ret; + + snprintf(key, sizeof(key), "acpi-scd-%d-subproperties", idx); + node = device_get_named_child_node(mst->dev, key); + if (!node) + return dev_err_probe(mst->dev, -ENODEV, + "link %d: missing %s node\n", idx, key); + + ret = fwnode_property_read_u32_array(node, "acpi-scd-dp-allocation", dp, + ARRAY_SIZE(dp)); + fwnode_handle_put(node); + if (ret) + return dev_err_probe(mst->dev, ret, + "link %d: bad acpi-scd-dp-allocation\n", + idx); + + /* { source_base, source_num, sink_base, sink_num } */ + link->src_base = dp[0]; + link->src_num = dp[1]; + link->sink_base = dp[2]; + link->sink_num = dp[3]; + + dev_dbg(mst->dev, "link %d DP alloc: src %u..%u, sink %u..%u\n", idx, + link->src_base, link->src_base + link->src_num - 1, + link->sink_base, link->sink_base + link->sink_num - 1); + return 0; +} + +static int mtk_sdw_init_clock(struct mtk_sdw *mst) +{ + int ret; + + ret = mst->clk_ops->init_clock(mst); + if (ret) + dev_err(mst->dev, "failed to init clock, ret %d\n", ret); + + return ret; +} + +static int mtk_sdw_enable_top_clock(struct mtk_sdw *mst, int link) +{ + int ret; + + ret = mst->clk_ops->enable_link_clock(mst, link); + if (ret) + dev_err(mst->dev, "[%d]failed to enable top clock, ret %d\n", + link, ret); + + return ret; +} + +static int mtk_sdw_disable_top_clock(struct mtk_sdw *mst, int link) +{ + int ret; + + ret = mst->clk_ops->disable_link_clock(mst, link); + if (ret) + dev_err(mst->dev, "[%d]failed to disable top clock, ret %d\n", + link, ret); + + return ret; +} + +static int mtk_sdw_enable_reg_clock(struct mtk_sdw *mst) +{ + int ret; + + ret = mst->clk_ops->enable_reg_rw_clk(mst); + if (ret) + dev_err(mst->dev, "failed to enable reg clock, ret %d\n", ret); + + return ret; +} + +static int mtk_sdw_disable_reg_clock(struct mtk_sdw *mst) +{ + int ret; + + ret = mst->clk_ops->disable_reg_rw_clk(mst); + if (ret) + dev_err(mst->dev, "failed to disable reg clock, ret %d\n", ret); + + return ret; +} + +static int mtk_sdw_enable_power(struct mtk_sdw *mst) +{ + int ret; + + ret = mst->clk_ops->enable_power_domain(mst); + if (ret) + dev_err(mst->dev, "failed to enable power, ret %d\n", ret); + + return ret; +} + +static int mtk_sdw_disable_power(struct mtk_sdw *mst) +{ + int ret; + + ret = mst->clk_ops->disable_power_domain(mst); + if (ret) + dev_err(mst->dev, "failed to disable power, ret %d\n", ret); + + return ret; +} + +static int mtk_sdw_enable_spm_request(struct mtk_sdw *mst, int link) +{ + int ret; + + ret = mst->clk_ops->enable_spm_request(mst, link); + if (ret) + dev_err(mst->dev, "[%d]failed to enable spm reqest, ret %d\n", + link, ret); + + return ret; +} + +static int mtk_sdw_disable_spm_request(struct mtk_sdw *mst, int link) +{ + int ret; + + ret = mst->clk_ops->disable_spm_request(mst, link); + if (ret) + dev_err(mst->dev, "[%d]failed to disable spm request, ret %d\n", + link, ret); + + return ret; +} + +/* ======================================================================= + * ASoC DAI layer (ALSA <-> SoundWire stream binding) + * ======================================================================= + */ + +static int mtk_sdw_set_sdw_stream(struct snd_soc_dai *dai, void *stream, + int direction) +{ + struct mtk_sdw *mst = snd_soc_dai_get_drvdata(dai); + + mst->dais[dai->id].sruntime = stream; + return 0; +} + +static void *mtk_sdw_get_sdw_stream(struct snd_soc_dai *dai, int direction) +{ + struct mtk_sdw *mst = snd_soc_dai_get_drvdata(dai); + + return mst->dais[dai->id].sruntime; +} + +static unsigned int derive_afe_fs_timing(unsigned int rate) +{ + unsigned int fs = 0; + + switch (rate) { + case RATE_384000: + fs = MT8901_FS_384K; + break; + case RATE_352800: + fs = MT8901_FS_352D8K; + break; + case RATE_192000: + fs = MT8901_FS_192K; + break; + case RATE_176400: + fs = MT8901_FS_176D4K; + break; + case RATE_96000: + fs = MT8901_FS_96K; + break; + case RATE_88200: + fs = MT8901_FS_88D2K; + break; + case RATE_48000: + fs = MT8901_FS_48K; + break; + case RATE_44100: + fs = MT8901_FS_44D1K; + break; + case RATE_32000: + fs = MT8901_FS_32K; + break; + case RATE_24000: + fs = MT8901_FS_24K; + break; + case RATE_22050: + fs = MT8901_FS_22D05K; + break; + case RATE_16000: + fs = MT8901_FS_16K; + break; + case RATE_12000: + fs = MT8901_FS_12K; + break; + case RATE_8000: + fs = MT8901_FS_8K; + break; + default: + break; + } + + return fs; +} + +static int mtk_sdw_dai_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct mtk_sdw *mst = snd_soc_dai_get_drvdata(dai); + struct mtk_sdw_dai *mdai = &mst->dais[dai->id]; + struct sdw_stream_config sconfig = { 0 }; + struct sdw_port_config pconfig = { 0 }; + struct mtk_sdw_port_params mtk_port_param = { 0 }; + struct mtk_sdw_top_pdi_params *top_params; + int dir, ret, i; + unsigned long pdis; + unsigned int pdi; + unsigned int channels = params_channels(params); + unsigned int rate = params_rate(params); + unsigned int pdi_count; + unsigned int ch_per_pdi = SDW_DEFAULT_CH_PER_PDI; + unsigned int tmp = 0; + unsigned int group_sync_config = 0; + unsigned int pdi_begin; + unsigned int last_pdi; + + mdai->suspended = false; + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) + dir = SDW_DATA_DIR_RX; + else + dir = SDW_DATA_DIR_TX; + + pdis = (mdai->port_num < MTK_SDW_MAX_DP) ? + mst->dp_pdi_mask[mdai->port_num] : 0; + for_each_set_bit(pdi, &pdis, MTK_SDW_MAX_DP) { + dev_dbg(dai->dev, "DP%u <- PDI%u\n", mdai->port_num, pdi); + if (tmp++) { + if (pdi != last_pdi + 1) { + dev_dbg(dai->dev, "unexpected last PDI %u, PDI%u\n", + last_pdi, pdi); + return -EINVAL; + } + } else { + pdi_begin = pdi; + } + last_pdi = pdi; + } + + pdi_count = (channels + ch_per_pdi - 1) / ch_per_pdi; + if (tmp != pdi_count) { + dev_err(dai->dev, "pdi mask 0x%x\n doesn't match channel %u", + mst->dp_pdi_mask[mdai->port_num], channels); + return -EINVAL; + } + + mdai->use_group_sync = false; + + if (pdi_count > 1) { + mdai->use_group_sync = true; + + ret = mtk_sdw_top_group_sync_acquire(mst, + dir == SDW_DATA_DIR_TX); + if (ret < 0) + return ret; + mdai->group_sync_id = ret; + + ret = mtk_sdw_top_group_sync_config_get(mst, + mdai->group_sync_id, + &group_sync_config); + if (ret < 0) { + mtk_sdw_top_group_sync_release(mst, + mdai->group_sync_id); + mdai->group_sync_id = 0; + return ret; + } + } + + /* update pdi_params */ + mdai->top_pdi_params = devm_kcalloc(mst->dev, pdi_count, + sizeof(*mdai->top_pdi_params), + GFP_KERNEL); + if (!mdai->top_pdi_params) { + ret = -ENOMEM; + goto err_group_sync; + } + mdai->pdi_count = pdi_count; + mdai->need_enable_delay = TRUE; + for (i = 0; i < pdi_count; i++) { + mdai->pdi_params[i].pdi_num = pdi_begin++; + mdai->pdi_params[i].port_num = mdai->port_num; + mdai->pdi_params[i].ch_mask = (1 << (i * ch_per_pdi)) | + (1 << (i * ch_per_pdi + 1)); + + top_params = &mdai->top_pdi_params[i]; + top_params->group_sync = group_sync_config; + //top_params->slave_mode = 0; + top_params->bra_slave = 0; //ToDo : BRA + top_params->bra_mode = 0; //ToDo : BRA + top_params->bra_eop = (top_params->bra_mode != 0) ? 1 : 0; + top_params->dmic_1xen = 0; + top_params->pdm_one_wire = 0; + top_params->pdm_mode = 0; + //top_params->hd_sel = 0; + top_params->clock = IS_48K_RATE_DOMAIN(rate) ? 1 : 0; + top_params->pdm2pcm = 0; + top_params->sdw_ip_sel = mdai->link->ip_idx; + /* ToDo: SpecifyPdiMode for BRA */ + top_params->domain = MT8901_APLL_CLK_DOMAIN; + /* ToDo: SpecifyPdiFs for BRA */ + top_params->fs = derive_afe_fs_timing(rate); + + if (top_params->bra_mode != 0 && top_params->bra_slave != 0) + mdai->need_enable_delay = FALSE; + } + + pconfig.num = mdai->port_num; + pconfig.ch_mask = GENMASK(channels - 1, 0); + + sconfig.frame_rate = rate; + sconfig.ch_count = channels; + sconfig.bps = snd_pcm_format_width(params_format(params)); + sconfig.direction = dir; + sconfig.type = SDW_STREAM_PCM; + + mtk_port_param.port_num = pconfig.num; + mtk_port_param.bpt_payload_type = 0; + mtk_port_param.bpt_en = true; + mtk_sdw_configure_port_params(&mdai->link->core, &mtk_port_param, dir); + + for (i = 0; i < mdai->pdi_count; i++) { + top_params = &mdai->top_pdi_params[i]; + mtk_sdw_top_configure_pdi(mst, mdai->pdi_params[i].pdi_num, + top_params); + mtk_sdw_configure_pdi_params(&mdai->link->core, + &mdai->pdi_params[i]); + } + + ret = sdw_stream_add_master(&mdai->link->core.bus, &sconfig, &pconfig, + 1, mdai->sruntime); + if (ret) + goto err_pdi; + + return 0; + +err_pdi: + for (i = 0; i < mdai->pdi_count; i++) + mtk_sdw_clear_pdi_settings(&mdai->link->core, + &mdai->pdi_params[i]); + mdai->pdi_count = 0; + devm_kfree(mst->dev, mdai->top_pdi_params); + mdai->top_pdi_params = NULL; +err_group_sync: + if (mdai->group_sync_id > 0) { + mtk_sdw_top_group_sync_release(mst, mdai->group_sync_id); + mdai->group_sync_id = 0; + } + mdai->use_group_sync = false; + return ret; +} + +static int mtk_sdw_dai_hw_free(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct mtk_sdw *mst = snd_soc_dai_get_drvdata(dai); + struct mtk_sdw_dai *mdai = &mst->dais[dai->id]; + int i, ret; + + ret = sdw_stream_remove_master(&mdai->link->core.bus, mdai->sruntime); + if (ret < 0) + dev_err(dai->dev, "remove manager from stream %s failed: %d\n", + mdai->sruntime->name, ret); + + for (i = 0; i < mdai->pdi_count; i++) + mtk_sdw_clear_pdi_settings(&mdai->link->core, + &mdai->pdi_params[i]); + mdai->pdi_count = 0; + mdai->suspended = false; + + if (mdai->group_sync_id > 0) { + mtk_sdw_top_group_sync_release(mst, mdai->group_sync_id); + mdai->group_sync_id = 0; + } + mdai->use_group_sync = false; + + devm_kfree(mst->dev, mdai->top_pdi_params); + mdai->top_pdi_params = NULL; + return ret; +} + +/* + * If we need to recover an active stream, the settings for TOP_PDI and TOP_CON + * are managed in the restore_regs function, while the others are managed in + * the prepare callback. + */ +static int mtk_sdw_dai_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct mtk_sdw *mst = snd_soc_dai_get_drvdata(dai); + struct mtk_sdw_dai *mdai = &mst->dais[dai->id]; + struct mtk_sdw_port_params mtk_port_param = { 0 }; + int i, dir; + + if (!mdai->suspended) + return 0; + + mdai->suspended = false; + + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) + dir = SDW_DATA_DIR_RX; + else + dir = SDW_DATA_DIR_TX; + + mtk_port_param.port_num = mdai->port_num; + mtk_port_param.bpt_payload_type = 0; + mtk_port_param.bpt_en = true; + mtk_sdw_configure_port_params(&mdai->link->core, &mtk_port_param, dir); + + for (i = 0; i < mdai->pdi_count; i++) { + mtk_sdw_configure_pdi_params(&mdai->link->core, + &mdai->pdi_params[i]); + } + + return 0; +} + +/* configure top stream here, it can be called before dai_link ops */ +static int mtk_sdw_dai_trigger(struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *dai) +{ + struct mtk_sdw *mst = snd_soc_dai_get_drvdata(dai); + struct mtk_sdw_dai *mdai = &mst->dais[dai->id]; + + mtk_sdw_core_dump_dpn_regs(&mdai->link->core, mdai->port_num, + "trigger"); + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + return mtk_sdw_top_enable_stream(mst, dai->id); + case SNDRV_PCM_TRIGGER_SUSPEND: + mdai->suspended = true; + fallthrough; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + return mtk_sdw_top_disable_stream(mst, dai->id); + default: + return -EINVAL; + } +} + +static const struct snd_soc_dai_ops mtk_sdw_dai_ops = { + .hw_params = mtk_sdw_dai_hw_params, + .hw_free = mtk_sdw_dai_hw_free, + .prepare = mtk_sdw_dai_prepare, + .trigger = mtk_sdw_dai_trigger, + .set_stream = mtk_sdw_set_sdw_stream, + .get_stream = mtk_sdw_get_sdw_stream, +}; + +/* ======================================================================= + * DAPM: PDI <-> DP routing + * ======================================================================= + */ + +/* Playback DEMUX target DPs (item 0 == "None"). */ +static const char * const mtk_sdw_playback_dp_texts[] = { + "None", "DP2", "DP3", "DP4", "DP5", "DP6", +}; + +/* Capture MUX source DPs (item 0 == "None"). */ +static const char * const mtk_sdw_capture_dp_texts[] = { + "None", "DP7", "DP8", "DP9", "DP10", + "DP11", "DP12", "DP13", "DP14", "DP15", +}; + +static int mtk_sdw_pdi_route_put(struct snd_kcontrol *kc, + struct snd_ctl_elem_value *uc) +{ + struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_to_dapm(kc); + struct snd_soc_component *comp = snd_soc_dapm_to_component(dapm); + struct mtk_sdw *mst = snd_soc_component_get_drvdata(comp); + struct soc_enum *e = (struct soc_enum *)kc->private_value; + unsigned int item = uc->value.enumerated.item[0]; + unsigned int pdi, dp, d; + + if (sscanf(kc->id.name, "PDI%u", &pdi) != 1) + return -EINVAL; + if (pdi >= BITS_PER_TYPE(mst->dp_pdi_mask[0])) + return -EINVAL; + if (item >= e->items) + return -EINVAL; + + for (d = 0; d < MTK_SDW_MAX_DP; d++) + mst->dp_pdi_mask[d] &= ~BIT(pdi); + + if (item && sscanf(e->texts[item], "DP%u", &dp) == 1 && + dp < MTK_SDW_MAX_DP) + mst->dp_pdi_mask[dp] |= BIT(pdi); + + return snd_soc_dapm_put_enum_double(kc, uc); +} + +/* Playback: PGA(PDIn) -> DEMUX(PDIn) -> one of DP2..DP6. */ +#define MTK_SDW_PLAYBACK_PDI(pdi) \ +static SOC_ENUM_SINGLE_VIRT_DECL(mtk_sdw_pdi##pdi##_enum, \ + mtk_sdw_playback_dp_texts); \ +static const struct snd_kcontrol_new mtk_sdw_pdi##pdi##_demux = \ + SOC_DAPM_ENUM_EXT("PDI" #pdi " Demux Select", \ + mtk_sdw_pdi##pdi##_enum, \ + snd_soc_dapm_get_enum_double, \ + mtk_sdw_pdi_route_put) + +/* Capture: one of DP7..DP15 -> MUX(PDIn) -> PGA(PDIn). */ +#define MTK_SDW_CAPTURE_PDI(pdi) \ +static SOC_ENUM_SINGLE_VIRT_DECL(mtk_sdw_pdi##pdi##_enum, \ + mtk_sdw_capture_dp_texts); \ +static const struct snd_kcontrol_new mtk_sdw_pdi##pdi##_mux = \ + SOC_DAPM_ENUM_EXT("PDI" #pdi " Mux Select", \ + mtk_sdw_pdi##pdi##_enum, \ + snd_soc_dapm_get_enum_double, \ + mtk_sdw_pdi_route_put) + +MTK_SDW_PLAYBACK_PDI(2); +MTK_SDW_PLAYBACK_PDI(3); +MTK_SDW_PLAYBACK_PDI(4); +MTK_SDW_PLAYBACK_PDI(5); +MTK_SDW_PLAYBACK_PDI(6); + +MTK_SDW_CAPTURE_PDI(7); +MTK_SDW_CAPTURE_PDI(8); +MTK_SDW_CAPTURE_PDI(9); +MTK_SDW_CAPTURE_PDI(10); +MTK_SDW_CAPTURE_PDI(11); +MTK_SDW_CAPTURE_PDI(12); +MTK_SDW_CAPTURE_PDI(13); +MTK_SDW_CAPTURE_PDI(14); +MTK_SDW_CAPTURE_PDI(15); + +/* PDI node + its selector. */ +#define MTK_SDW_PDI_PGA(pdi) \ + SND_SOC_DAPM_PGA("PDI" #pdi, SND_SOC_NOPM, 0, 0, NULL, 0) +#define MTK_SDW_PLAYBACK_PDI_DEMUX(pdi) \ + SND_SOC_DAPM_DEMUX("PDI" #pdi " Demux", SND_SOC_NOPM, 0, 0, \ + &mtk_sdw_pdi##pdi##_demux) +#define MTK_SDW_CAPTURE_PDI_MUX(pdi) \ + SND_SOC_DAPM_MUX("PDI" #pdi " Mux", SND_SOC_NOPM, 0, 0, \ + &mtk_sdw_pdi##pdi##_mux) + +/* DP node. */ +#define MTK_SDW_DP_WIDGET(dp) \ + SND_SOC_DAPM_PGA("DP" #dp, SND_SOC_NOPM, 0, 0, NULL, 0) + +static const struct snd_soc_dapm_widget mtk_sdw_dapm_widgets[] = { + /* BRA PDIs, no DP connection */ + SND_SOC_DAPM_PGA("PDI0", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("PDI1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("PDI16", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("PDI17", SND_SOC_NOPM, 0, 0, NULL, 0), + + /* Playback PDIs + demuxes */ + MTK_SDW_PDI_PGA(2), MTK_SDW_PLAYBACK_PDI_DEMUX(2), + MTK_SDW_PDI_PGA(3), MTK_SDW_PLAYBACK_PDI_DEMUX(3), + MTK_SDW_PDI_PGA(4), MTK_SDW_PLAYBACK_PDI_DEMUX(4), + MTK_SDW_PDI_PGA(5), MTK_SDW_PLAYBACK_PDI_DEMUX(5), + MTK_SDW_PDI_PGA(6), MTK_SDW_PLAYBACK_PDI_DEMUX(6), + /* Capture PDIs + muxes */ + MTK_SDW_PDI_PGA(7), MTK_SDW_CAPTURE_PDI_MUX(7), + MTK_SDW_PDI_PGA(8), MTK_SDW_CAPTURE_PDI_MUX(8), + MTK_SDW_PDI_PGA(9), MTK_SDW_CAPTURE_PDI_MUX(9), + MTK_SDW_PDI_PGA(10), MTK_SDW_CAPTURE_PDI_MUX(10), + MTK_SDW_PDI_PGA(11), MTK_SDW_CAPTURE_PDI_MUX(11), + MTK_SDW_PDI_PGA(12), MTK_SDW_CAPTURE_PDI_MUX(12), + MTK_SDW_PDI_PGA(13), MTK_SDW_CAPTURE_PDI_MUX(13), + MTK_SDW_PDI_PGA(14), MTK_SDW_CAPTURE_PDI_MUX(14), + MTK_SDW_PDI_PGA(15), MTK_SDW_CAPTURE_PDI_MUX(15), + /* DP nodes */ + MTK_SDW_DP_WIDGET(2), MTK_SDW_DP_WIDGET(3), MTK_SDW_DP_WIDGET(4), + MTK_SDW_DP_WIDGET(5), MTK_SDW_DP_WIDGET(6), MTK_SDW_DP_WIDGET(7), + MTK_SDW_DP_WIDGET(8), MTK_SDW_DP_WIDGET(9), MTK_SDW_DP_WIDGET(10), + MTK_SDW_DP_WIDGET(11), MTK_SDW_DP_WIDGET(12), MTK_SDW_DP_WIDGET(13), + MTK_SDW_DP_WIDGET(14), MTK_SDW_DP_WIDGET(15), +}; + +/* + * Playback edge: PDIn -> PDIn Demux -> DPx (active when demux selects "DPx"). + */ +#define MTK_SDW_PLAYBACK_DEMUX_OUT(pdi, dp) \ + { "DP" #dp, "DP" #dp, "PDI" #pdi " Demux" } +#define MTK_SDW_PLAYBACK_PDI_LINK(pdi) \ + { "PDI" #pdi " Demux", NULL, "PDI" #pdi } + +/* Capture edge: DPx -> PDIn Mux -> PDIn (active when mux selects "DPx"). */ +#define MTK_SDW_CAPTURE_MUX_IN(pdi, dp) \ + { "PDI" #pdi " Mux", "DP" #dp, "DP" #dp } +#define MTK_SDW_CAPTURE_PDI_LINK(pdi) \ + { "PDI" #pdi, NULL, "PDI" #pdi " Mux" } + +static const struct snd_soc_dapm_route mtk_sdw_dapm_routes[] = { + /* Playback: PDI2..PDI6 -> DP2..DP6 */ + MTK_SDW_PLAYBACK_PDI_LINK(2), + MTK_SDW_PLAYBACK_DEMUX_OUT(2, 2), MTK_SDW_PLAYBACK_DEMUX_OUT(2, 3), + MTK_SDW_PLAYBACK_DEMUX_OUT(2, 4), MTK_SDW_PLAYBACK_DEMUX_OUT(2, 5), + MTK_SDW_PLAYBACK_DEMUX_OUT(2, 6), + MTK_SDW_PLAYBACK_PDI_LINK(3), + MTK_SDW_PLAYBACK_DEMUX_OUT(3, 2), MTK_SDW_PLAYBACK_DEMUX_OUT(3, 3), + MTK_SDW_PLAYBACK_DEMUX_OUT(3, 4), MTK_SDW_PLAYBACK_DEMUX_OUT(3, 5), + MTK_SDW_PLAYBACK_DEMUX_OUT(3, 6), + MTK_SDW_PLAYBACK_PDI_LINK(4), + MTK_SDW_PLAYBACK_DEMUX_OUT(4, 2), MTK_SDW_PLAYBACK_DEMUX_OUT(4, 3), + MTK_SDW_PLAYBACK_DEMUX_OUT(4, 4), MTK_SDW_PLAYBACK_DEMUX_OUT(4, 5), + MTK_SDW_PLAYBACK_DEMUX_OUT(4, 6), + MTK_SDW_PLAYBACK_PDI_LINK(5), + MTK_SDW_PLAYBACK_DEMUX_OUT(5, 2), MTK_SDW_PLAYBACK_DEMUX_OUT(5, 3), + MTK_SDW_PLAYBACK_DEMUX_OUT(5, 4), MTK_SDW_PLAYBACK_DEMUX_OUT(5, 5), + MTK_SDW_PLAYBACK_DEMUX_OUT(5, 6), + MTK_SDW_PLAYBACK_PDI_LINK(6), + MTK_SDW_PLAYBACK_DEMUX_OUT(6, 2), MTK_SDW_PLAYBACK_DEMUX_OUT(6, 3), + MTK_SDW_PLAYBACK_DEMUX_OUT(6, 4), MTK_SDW_PLAYBACK_DEMUX_OUT(6, 5), + MTK_SDW_PLAYBACK_DEMUX_OUT(6, 6), + /* Capture: DP7..DP15 -> PDI7..PDI15 */ + MTK_SDW_CAPTURE_MUX_IN(7, 7), MTK_SDW_CAPTURE_MUX_IN(7, 8), + MTK_SDW_CAPTURE_MUX_IN(7, 9), MTK_SDW_CAPTURE_MUX_IN(7, 10), + MTK_SDW_CAPTURE_MUX_IN(7, 11), MTK_SDW_CAPTURE_MUX_IN(7, 12), + MTK_SDW_CAPTURE_MUX_IN(7, 13), MTK_SDW_CAPTURE_MUX_IN(7, 14), + MTK_SDW_CAPTURE_MUX_IN(7, 15), + MTK_SDW_CAPTURE_PDI_LINK(7), + MTK_SDW_CAPTURE_MUX_IN(8, 7), MTK_SDW_CAPTURE_MUX_IN(8, 8), + MTK_SDW_CAPTURE_MUX_IN(8, 9), MTK_SDW_CAPTURE_MUX_IN(8, 10), + MTK_SDW_CAPTURE_MUX_IN(8, 11), MTK_SDW_CAPTURE_MUX_IN(8, 12), + MTK_SDW_CAPTURE_MUX_IN(8, 13), MTK_SDW_CAPTURE_MUX_IN(8, 14), + MTK_SDW_CAPTURE_MUX_IN(8, 15), + MTK_SDW_CAPTURE_PDI_LINK(8), + MTK_SDW_CAPTURE_MUX_IN(9, 7), MTK_SDW_CAPTURE_MUX_IN(9, 8), + MTK_SDW_CAPTURE_MUX_IN(9, 9), MTK_SDW_CAPTURE_MUX_IN(9, 10), + MTK_SDW_CAPTURE_MUX_IN(9, 11), MTK_SDW_CAPTURE_MUX_IN(9, 12), + MTK_SDW_CAPTURE_MUX_IN(9, 13), MTK_SDW_CAPTURE_MUX_IN(9, 14), + MTK_SDW_CAPTURE_MUX_IN(9, 15), + MTK_SDW_CAPTURE_PDI_LINK(9), + MTK_SDW_CAPTURE_MUX_IN(10, 7), MTK_SDW_CAPTURE_MUX_IN(10, 8), + MTK_SDW_CAPTURE_MUX_IN(10, 9), MTK_SDW_CAPTURE_MUX_IN(10, 10), + MTK_SDW_CAPTURE_MUX_IN(10, 11), MTK_SDW_CAPTURE_MUX_IN(10, 12), + MTK_SDW_CAPTURE_MUX_IN(10, 13), MTK_SDW_CAPTURE_MUX_IN(10, 14), + MTK_SDW_CAPTURE_MUX_IN(10, 15), + MTK_SDW_CAPTURE_PDI_LINK(10), + MTK_SDW_CAPTURE_MUX_IN(11, 7), MTK_SDW_CAPTURE_MUX_IN(11, 8), + MTK_SDW_CAPTURE_MUX_IN(11, 9), MTK_SDW_CAPTURE_MUX_IN(11, 10), + MTK_SDW_CAPTURE_MUX_IN(11, 11), MTK_SDW_CAPTURE_MUX_IN(11, 12), + MTK_SDW_CAPTURE_MUX_IN(11, 13), MTK_SDW_CAPTURE_MUX_IN(11, 14), + MTK_SDW_CAPTURE_MUX_IN(11, 15), + MTK_SDW_CAPTURE_PDI_LINK(11), + MTK_SDW_CAPTURE_MUX_IN(12, 7), MTK_SDW_CAPTURE_MUX_IN(12, 8), + MTK_SDW_CAPTURE_MUX_IN(12, 9), MTK_SDW_CAPTURE_MUX_IN(12, 10), + MTK_SDW_CAPTURE_MUX_IN(12, 11), MTK_SDW_CAPTURE_MUX_IN(12, 12), + MTK_SDW_CAPTURE_MUX_IN(12, 13), MTK_SDW_CAPTURE_MUX_IN(12, 14), + MTK_SDW_CAPTURE_MUX_IN(12, 15), + MTK_SDW_CAPTURE_PDI_LINK(12), + MTK_SDW_CAPTURE_MUX_IN(13, 7), MTK_SDW_CAPTURE_MUX_IN(13, 8), + MTK_SDW_CAPTURE_MUX_IN(13, 9), MTK_SDW_CAPTURE_MUX_IN(13, 10), + MTK_SDW_CAPTURE_MUX_IN(13, 11), MTK_SDW_CAPTURE_MUX_IN(13, 12), + MTK_SDW_CAPTURE_MUX_IN(13, 13), MTK_SDW_CAPTURE_MUX_IN(13, 14), + MTK_SDW_CAPTURE_MUX_IN(13, 15), + MTK_SDW_CAPTURE_PDI_LINK(13), + MTK_SDW_CAPTURE_MUX_IN(14, 7), MTK_SDW_CAPTURE_MUX_IN(14, 8), + MTK_SDW_CAPTURE_MUX_IN(14, 9), MTK_SDW_CAPTURE_MUX_IN(14, 10), + MTK_SDW_CAPTURE_MUX_IN(14, 11), MTK_SDW_CAPTURE_MUX_IN(14, 12), + MTK_SDW_CAPTURE_MUX_IN(14, 13), MTK_SDW_CAPTURE_MUX_IN(14, 14), + MTK_SDW_CAPTURE_MUX_IN(14, 15), + MTK_SDW_CAPTURE_PDI_LINK(14), + MTK_SDW_CAPTURE_MUX_IN(15, 7), MTK_SDW_CAPTURE_MUX_IN(15, 8), + MTK_SDW_CAPTURE_MUX_IN(15, 9), MTK_SDW_CAPTURE_MUX_IN(15, 10), + MTK_SDW_CAPTURE_MUX_IN(15, 11), MTK_SDW_CAPTURE_MUX_IN(15, 12), + MTK_SDW_CAPTURE_MUX_IN(15, 13), MTK_SDW_CAPTURE_MUX_IN(15, 14), + MTK_SDW_CAPTURE_MUX_IN(15, 15), + MTK_SDW_CAPTURE_PDI_LINK(15), +}; + +static int mtk_sdw_add_dai_routes(struct snd_soc_component *comp) +{ + struct mtk_sdw *mst = snd_soc_component_get_drvdata(comp); + struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(comp); + int i, p, ret; + + for (i = 0; i < mst->num_links; i++) { + struct mtk_sdw_link *link = &mst->links[i]; + int offset; + + offset = 0; + for (p = 0; p < link->src_num; p++, offset++) { + char dai[32], dp[8]; + struct snd_soc_dapm_route r = { dai, NULL, dp }; + + snprintf(dai, sizeof(dai), "SDW%d_Playback%u", + link->ip_idx, offset); + snprintf(dp, sizeof(dp), "DP%u", link->src_base + p); + ret = snd_soc_dapm_add_routes(dapm, &r, 1); + if (ret) + return ret; + } + + offset = 0; + for (p = 0; p < link->sink_num; p++, offset++) { + char dai[32], dp[8]; + struct snd_soc_dapm_route r = { dp, NULL, dai }; + + snprintf(dai, sizeof(dai), "SDW%d_Capture%u", + link->ip_idx, offset); + snprintf(dp, sizeof(dp), "DP%u", link->sink_base + p); + ret = snd_soc_dapm_add_routes(dapm, &r, 1); + if (ret) + return ret; + } + } + + return 0; +} + +static int mtk_sdw_component_probe(struct snd_soc_component *comp) +{ + return mtk_sdw_add_dai_routes(comp); +} + +static const struct snd_soc_component_driver mtk_sdw_component = { + .name = "mtk-soundwire", + .probe = mtk_sdw_component_probe, + .dapm_widgets = mtk_sdw_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(mtk_sdw_dapm_widgets), + .dapm_routes = mtk_sdw_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(mtk_sdw_dapm_routes), +}; + +static int mtk_sdw_init_dai(struct mtk_sdw *mst, struct snd_soc_dai_driver *drv, + int idx, struct mtk_sdw_link *link, + unsigned int port, int offset, bool playback) +{ + struct snd_soc_pcm_stream *stream; + + drv->id = idx; + drv->ops = &mtk_sdw_dai_ops; + + if (playback) { + drv->name = devm_kasprintf(mst->dev, GFP_KERNEL, + "SDW%d_Playback%u", + link->ip_idx, offset); + if (!drv->name) + return -ENOMEM; + + stream = &drv->playback; + } else { + drv->name = devm_kasprintf(mst->dev, GFP_KERNEL, + "SDW%d_Capture%u", + link->ip_idx, offset); + if (!drv->name) + return -ENOMEM; + + stream = &drv->capture; + } + + stream->channels_min = 2; + stream->channels_max = 8; + stream->rates = SNDRV_PCM_RATE_48000; + stream->rates = SNDRV_PCM_RATE_8000_384000; + /* S32 dropped: the AFE path is 24-bit internally, see MTK_PCM_FORMATS */ + stream->formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE; + + stream->stream_name = drv->name; + + mst->dais[idx].link = link; + mst->dais[idx].port_num = port; + + return 0; +} + +static int mtk_sdw_register_dais(struct mtk_sdw *mst) +{ + struct snd_soc_dai_driver *drvs; + struct device *dev = mst->dev; + int i, p, idx = 0, num = 0, ret; + + for (i = 0; i < mst->num_links; i++) + num += mst->links[i].src_num + mst->links[i].sink_num; + if (!num) + return dev_err_probe(dev, -EINVAL, "no data ports allocated\n"); + + mst->dais = devm_kcalloc(dev, num, sizeof(*mst->dais), GFP_KERNEL); + if (!mst->dais) + return -ENOMEM; + + drvs = devm_kcalloc(dev, num, sizeof(*drvs), GFP_KERNEL); + if (!drvs) + return -ENOMEM; + + for (i = 0; i < mst->num_links; i++) { + struct mtk_sdw_link *link = &mst->links[i]; + int offset; + + offset = 0; + /* Source ports -> playback DAIs */ + for (p = 0; p < link->src_num; p++, idx++, offset++) { + ret = mtk_sdw_init_dai(mst, &drvs[idx], idx, link, + link->src_base + p, offset, + true); + if (ret) + return ret; + } + + offset = 0; + /* Sink ports -> capture DAIs */ + for (p = 0; p < link->sink_num; p++, idx++, offset++) { + ret = mtk_sdw_init_dai(mst, &drvs[idx], idx, link, + link->sink_base + p, offset, + false); + if (ret) + return ret; + } + } + mst->num_dais = num; + + return devm_snd_soc_register_component(dev, &mtk_sdw_component, drvs, + num); +} + +/* ======================================================================= + * Per-IP bring-up + * ======================================================================= + */ + +static int mtk_sdw_link_init(struct mtk_sdw *mst, int idx) +{ + struct mtk_sdw_link *link = &mst->links[idx]; + struct mtk_sdw_core *core = &link->core; + int ret; + + link->master = mst; + link->ip_idx = idx; + + core->dev = mst->dev; + core->priv = link; + + core->bus.link_id = idx; + core->bus.prop.mclk_freq = SDW_DEFAULT_MCLK_CLK_FREQ; + + ret = mtk_sdw_enable_top_clock(mst, idx); + if (ret) + return ret; + + ret = mtk_sdw_enable_spm_request(mst, idx); + if (ret) + goto err_spm; + + core->bus.dev = mst->dev; + ret = sdw_bus_master_add(&core->bus, mst->dev, mst->dev->fwnode); + if (ret) { + dev_err(mst->dev, "IP%d: bus_master_add failed: %d\n", + idx, ret); + goto err_bus; + } + + ret = mtk_sdw_core_hw_init(core); + if (ret) { + sdw_bus_master_delete(&core->bus); + goto err_bus; + } + + /* + * Restart the bus unconditionally before enabling interrupts, the + * way the reference stack runs ResetAndReInitMaster on every init. + * POST leaves the peripherals enumerated and running; adopting that + * state preserves whatever internal state the peripherals hold, + * which has been observed to include defective persistent state + * that register-level reconfiguration cannot clear (a codec's rail + * never drops while the battery is connected, so such state + * survives reboots indefinitely). The restart drops every + * peripheral to Dev0 and re-enumerates it from a known-clean + * state. Interrupt enabling AFTER the restart arms the slave + * interrupt masks that SOFT_RST clears, so the attach transitions + * are processed normally; the post-init attach check remains as a + * backstop. + */ + if (restart_on_probe) { + ret = mtk_sdw_core_bus_reset(core); + if (ret) { + sdw_bus_master_delete(&core->bus); + goto err_bus; + } + } + + mtk_sdw_enable_irq(core, true); + + if (restart_on_probe) + schedule_delayed_work(&core->status_work, + msecs_to_jiffies(100)); + + dev_info(mst->dev, "IP%d: SoundWire bus ready\n", idx); + return 0; + +err_bus: + mtk_sdw_disable_spm_request(mst, idx); +err_spm: + mtk_sdw_disable_top_clock(mst, idx); + + return ret; +} + +static void mtk_sdw_link_deinit(struct mtk_sdw *mst, int idx) +{ + struct mtk_sdw_core *core = &mst->links[idx].core; + + cancel_delayed_work_sync(&core->attach_check_work); + mtk_sdw_enable_irq(core, false); + sdw_bus_master_delete(&core->bus); + mtk_sdw_disable_spm_request(mst, idx); + mtk_sdw_disable_top_clock(mst, idx); +} + +static void mtk_init_sdw_master(struct mtk_sdw *mst) +{ + int i; + + mst->tzd_delay = 3; + mst->tzd_inverse = 0; + mst->phy_delay = 2; + mst->phy_double_delay = 0; + mst->num_links = MTK_SDW_CONTROLLE_NUM; + + for (i = 0; i < MTK_SDW_CONTROLLE_NUM; i++) + mst->controller_en_list |= (1UL << i); +} + +static int mtk_sdw_register_machine(struct mtk_sdw *mst) +{ + struct snd_soc_acpi_mach *mach; + + mach = mtk_sdw_machine_select(mst); + if (!mach) { + dev_info(mst->dev, "no card from this controller\n"); + return 0; /* no card from this controller; not an error */ + } + + mst->mach_dev = platform_device_register_data(mst->dev, mach->drv_name, + PLATFORM_DEVID_NONE, + mach, sizeof(*mach)); + if (IS_ERR(mst->mach_dev)) { + dev_err(mst->dev, "failed to register machine %s\n", + mach->drv_name); + return PTR_ERR(mst->mach_dev); + } + + dev_info(mst->dev, "registered SoundWire machine %s\n", mach->drv_name); + return 0; +} + +/* + * debugfs: echo > .../bus_reset re-issues the cold-boot + * command reset on that link (all peripherals drop to Dev0 and + * re-enumerate together) and kicks the status work. Debug/recovery aid + * for links wedged by failed enumeration. + */ +static ssize_t mtk_sdw_bus_reset_write(struct file *file, + const char __user *ubuf, + size_t count, loff_t *ppos) +{ + struct mtk_sdw *mst = file->private_data; + struct mtk_sdw_core *core; + unsigned int link; + int ret; + + ret = kstrtouint_from_user(ubuf, count, 0, &link); + if (ret) + return ret; + + /* + * = hardware bus reset (MCP_CONTROL HW_BUS_RST) + * 10 + = deep reset: gate the link clock so peripherals + * lose sync entirely and reset their bus interface + * state machines, then re-init the core. Closest + * commanded equivalent to a power cycle. + */ + if (link >= 10) { + link -= 10; + if (link >= mst->num_links) + return -EINVAL; + + core = &mst->links[link].core; + + dev_info(mst->dev, "IP%u: deep reset (clock cycle)\n", link); + + /* + * Zero the recovery counters only after enable_irq(false) has + * cancelled the status work; a still-running work could write + * them back otherwise. + */ + mtk_sdw_enable_irq(core, false); + core->bus_reset_count = 0; + core->dev0_repoll_count = 0; + + /* + * Hold the bus message lock across the whole clock cycle so + * no sdw_transfer() or standard reset touches the unclocked + * controller, and restore interrupts only once the link clock + * is known to be back on (enable_irq writes controller + * registers). + */ + mutex_lock(&core->bus.msg_lock); + + ret = mtk_sdw_disable_top_clock(mst, link); + if (ret) + goto err_unlock_reenable; + + msleep(200); + + ret = mtk_sdw_enable_top_clock(mst, link); + if (ret) { + mutex_unlock(&core->bus.msg_lock); + dev_err(mst->dev, + "IP%u: link clock off after failed deep reset, interrupts left disabled\n", + link); + return ret; + } + + ret = mtk_sdw_core_hw_init(core); + mutex_unlock(&core->bus.msg_lock); + + mtk_sdw_enable_irq(core, true); + if (ret) + return ret; + + schedule_delayed_work(&core->status_work, msecs_to_jiffies(100)); + + return count; + +err_unlock_reenable: + mutex_unlock(&core->bus.msg_lock); + mtk_sdw_enable_irq(core, true); + return ret; + } + + if (link >= mst->num_links) + return -EINVAL; + + core = &mst->links[link].core; + + /* + * Quiesce interrupt handling and the status work (enable_irq(false) + * cancels it) so the reset does not race concurrent register access. + * Zero the recovery counters only after the cancellation, so a + * still-running work cannot write them back. + */ + mtk_sdw_enable_irq(core, false); + core->bus_reset_count = 0; + ret = mtk_sdw_core_bus_reset(core); + mtk_sdw_enable_irq(core, true); + if (ret) + return ret; + + schedule_delayed_work(&core->status_work, msecs_to_jiffies(100)); + + return count; +} + +static const struct file_operations mtk_sdw_bus_reset_fops = { + .open = simple_open, + .write = mtk_sdw_bus_reset_write, +}; + +static void mtk_sdw_debugfs_init(struct mtk_sdw *mst) +{ + mst->debugfs = debugfs_create_dir(dev_name(mst->dev), NULL); + debugfs_create_file("bus_reset", 0200, mst->debugfs, mst, + &mtk_sdw_bus_reset_fops); +} + +static int mtk_sdw_probe_controller(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct mtk_sdw *mst; + u32 hw_ver = MTK_SDW_HW_VER_MT8901; + u32 mmio[2]; + int i = 0; + int ret; + bool clk_on; + + mst = devm_kzalloc(dev, sizeof(*mst), GFP_KERNEL); + if (!mst) + return -ENOMEM; + + /* Ignore other errors except for EPROBE_DEFER */ + ret = mtk_sdw_apply_pin_config(dev); + if (ret == -EPROBE_DEFER) + return ret; + else if (ret) + dev_info(dev, "fail to apply pin config\n"); + + mtk_init_sdw_master(mst); + + mst->dev = dev; + mutex_init(&mst->group_lock); + mutex_init(&mst->afe_lock); + platform_set_drvdata(pdev, mst); + + ret = device_property_read_u32_array(dev, "afe-base", mmio, 2); + if (ret) + return dev_err_probe(dev, ret, + "missing/invalid afe-base property\n"); + dev_info(dev, "address %x, size %d\n", mmio[0], mmio[1]); + + mst->base = devm_ioremap(dev, mmio[0], mmio[1]); + if (!mst->base) + return -ENOMEM; + mst->top_pdi = mst->base + AFE_SDW_TOP_PDI_REG_BASE; + mst->top_con = mst->base + AFE_SDW_TOP_CON_REG_BASE; + + device_property_read_u32(dev, "acpi-scd-hw-ver", &hw_ver); + if (hw_ver >= MTK_SDW_HW_VER_MAX) + return dev_err_probe(dev, -EINVAL, + "unknown acpi-scd-hw-ver %u\n", hw_ver); + + mst->hw_ver = hw_ver; + + ret = mtk_sdw_top_config_select(mst, mst->hw_ver); + if (ret) + return ret; + + ret = mtk_sdw_clk_ops_select(mst, mst->hw_ver); + if (ret) + return ret; + + /* Timing delays from ACPI _DSD. */ + device_property_read_u32(dev, "acpi-scd-tzd-delay", &mst->tzd_delay); + device_property_read_u32(dev, "acpi-scd-phy-delay", &mst->phy_delay); + device_property_read_u32(dev, "acpi-scd-phy-double-delay", + &mst->phy_double_delay); + + if (mst->tzd_delay <= mst->phy_delay) { + dev_err(dev, "invalid delay, tzd %u - phy %u", mst->tzd_delay, + mst->phy_delay); + return -EINVAL; + } + + ret = mtk_sdw_init_clock(mst); + if (ret) + return ret; + + ret = mtk_sdw_enable_reg_clock(mst); + if (ret) + return ret; + + clk_on = true; + + ret = mtk_sdw_top_init(mst); + if (ret) { + dev_err(dev, "failed to init sdw top, ret %d\n", ret); + goto err; + } + + for (i = 0; i < mst->num_links; i++) { + int irq = platform_get_irq(pdev, i); + struct mtk_sdw_core *core = &mst->links[i].core; + + if (irq < 0) { + ret = irq; + goto err; + } + mst->links[i].irq = irq; + mst->links[i].ip_idx = i; + + ret = mtk_sdw_parse_dp_allocation(mst, i); + if (ret) + goto err; + + core->regs = devm_platform_ioremap_resource(pdev, i); + if (IS_ERR(core->regs)) { + ret = PTR_ERR(core->regs); + goto err; + } + + /* + * The line is shared and MCP_INTMASK holds whatever POST left, + * so the handler can run as soon as it is registered: the lock + * and work items it uses must exist first. + */ + ret = mtk_sdw_core_init(core); + if (ret) + goto err; + + ret = devm_request_irq(dev, irq, mtk_sdw_core_irq, IRQF_SHARED, + dev_name(dev), core); + if (ret) { + dev_err(dev, "IP%d: request_irq failed: %d\n", i, ret); + goto err; + } + + ret = mtk_sdw_link_init(mst, i); + if (ret) + goto err; + } + + ret = mtk_sdw_disable_reg_clock(mst); + if (ret) + goto err; + clk_on = false; + + ret = mtk_sdw_register_dais(mst); + if (ret) { + dev_err(dev, "failed to register DAIs: %d\n", ret); + goto err; + } + + dev_info(dev, "MediaTek SoundWire ready, %d link(s), %d DAI(s)\n", + mst->num_links, mst->num_dais); + + return 0; + +err: + while (--i >= 0) + mtk_sdw_link_deinit(mst, i); + + if (clk_on) + mtk_sdw_disable_reg_clock(mst); + + return ret; +} + +static int mtk_sdw_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct mtk_sdw *mst; + int i, ret; + + ret = mtk_sdw_probe_controller(pdev); + if (ret) { + dev_err(dev, "failed to probe_controller %d\n", ret); + return ret; + } + + mst = platform_get_drvdata(pdev); + + ret = mtk_sdw_register_machine(mst); + if (ret) { + for (i = mst->num_links - 1; i >= 0; i--) + mtk_sdw_link_deinit(mst, i); + + return ret; + } + + mtk_sdw_debugfs_init(mst); + + /* + * Arm the post-init attach check on every link: if the ACPI-declared + * peripherals raise no attach event (re-init with the bus already + * running, see mtk_sdw_attach_check_work()), restart the bus so they + * re-enumerate. + */ + for (i = 0; i < mst->num_links; i++) + schedule_delayed_work(&mst->links[i].core.attach_check_work, + msecs_to_jiffies(MTK_SDW_ATTACH_CHECK_DELAY_MS)); + + return 0; +} + +static void mtk_sdw_remove(struct platform_device *pdev) +{ + struct mtk_sdw *mst = platform_get_drvdata(pdev); + int i; + + debugfs_remove_recursive(mst->debugfs); + if (mst->mach_dev) + platform_device_unregister(mst->mach_dev); + for (i = mst->num_links - 1; i >= 0; i--) + mtk_sdw_link_deinit(mst, i); +} + +static int mtk_sdw_enter_low_power_mode(struct mtk_sdw *mst, int idx) +{ + struct mtk_sdw_link *link = &mst->links[idx]; + struct mtk_sdw_core *core = &link->core; + int ret; + + cancel_delayed_work_sync(&core->attach_check_work); + mtk_sdw_enable_irq(core, false); + + ret = mtk_sdw_shutdown_master(core); + if (ret) { + dev_err(mst->dev, "shutdown master failed, ret = %d\n", ret); + mtk_sdw_enable_irq(core, true); + return ret; + } + + ret = mtk_sdw_disable_spm_request(mst, idx); + if (ret) + goto err_spm; + + ret = mtk_sdw_disable_top_clock(mst, idx); + if (ret) + goto err_top; + + return 0; + +err_top: + if (mtk_sdw_enable_spm_request(mst, idx)) + dev_err(mst->dev, "[%d]failed to restore SPM on error path", + idx); +err_spm: + if (mtk_sdw_reinit_master(core)) + dev_err(mst->dev, "[%d]failed to reinit master on error path", + idx); + + return ret; +} + +static int mtk_sdw_exit_low_power_mode(struct mtk_sdw *mst, int idx) +{ + struct mtk_sdw_link *link = &mst->links[idx]; + struct mtk_sdw_core *core = &link->core; + int ret; + + ret = mtk_sdw_enable_top_clock(mst, idx); + if (ret) + return ret; + + ret = mtk_sdw_enable_spm_request(mst, idx); + if (ret) { + mtk_sdw_disable_top_clock(mst, idx); + return ret; + } + + sdw_clear_slave_status(&core->bus, SDW_UNATTACH_REQUEST_MASTER_RESET); + + ret = mtk_sdw_reinit_master(core); + if (ret) { + dev_err(mst->dev, "mtk_sdw_reinit_master failed: %d\n", ret); + mtk_sdw_disable_spm_request(mst, idx); + mtk_sdw_disable_top_clock(mst, idx); + return ret; + } + + return 0; +} + +static int mtk_sdw_backup_hw_settings(struct mtk_sdw *mst) +{ + int ret; + + ret = mtk_sdw_enable_reg_clock(mst); + if (ret) + return ret; + + mtk_sdw_top_backup_regs(mst); + + ret = mtk_sdw_disable_reg_clock(mst); + if (ret) + return ret; + + return 0; +} + +static int mtk_sdw_restore_hw_settings(struct mtk_sdw *mst) +{ + int ret; + + ret = mtk_sdw_enable_reg_clock(mst); + if (ret) + return ret; + + mtk_sdw_top_restore_regs(mst); + + ret = mtk_sdw_disable_reg_clock(mst); + if (ret) + return ret; + + return 0; +} + +static int mtk_sdw_suspend(struct device *dev) +{ + struct mtk_sdw *mst = dev_get_drvdata(dev); + int i, ret; + + /* 1. backup setting */ + ret = mtk_sdw_backup_hw_settings(mst); + if (ret) { + dev_err(dev, "backup hw settings failed: %d\n", ret); + return ret; + } + + /* + * The controller shutdown below wipes every configured port and PDI. + * Only running streams get TRIGGER_SUSPEND; a stream that is prepared + * but idle does not, so flag every configured DAI here and let the + * prepare callback rebuild its port state on the next start. + */ + for (i = 0; i < mst->num_dais; i++) + if (mst->dais[i].pdi_count) + mst->dais[i].suspended = true; + + /* 2. enter low power per link */ + for (i = 0; i < mst->num_links; i++) { + ret = mtk_sdw_enter_low_power_mode(mst, i); + if (ret) { + dev_err(dev, "[%d] enter low power mode failed: %d\n", + i, ret); + while (--i >= 0) + mtk_sdw_exit_low_power_mode(mst, i); + return ret; + } + } + + /* + * 3. disable mtcmos per link: the SSPM power refcount is one vote + * per link (the firmware initializes it to the link count, and the + * Windows driver relinquishes once per SoundWire IP), so vote per + * link, not per master, or the count never reaches zero and the + * SoC cannot enter PPS7/PPS8. + */ + for (i = 0; i < mst->num_links; i++) { + ret = mtk_sdw_disable_power(mst); + if (ret) { + while (--i >= 0) + mtk_sdw_enable_power(mst); + + for (i = 0; i < mst->num_links; i++) + mtk_sdw_exit_low_power_mode(mst, i); + return ret; + } + } + + return 0; +} + +static int mtk_sdw_resume(struct device *dev) +{ + struct mtk_sdw *mst = dev_get_drvdata(dev); + int i, ret; + + /* 1. enable mtcmos per link */ + for (i = 0; i < mst->num_links; i++) { + ret = mtk_sdw_enable_power(mst); + if (ret) { + while (--i >= 0) + mtk_sdw_disable_power(mst); + return ret; + } + } + + /* 2. restore setting */ + ret = mtk_sdw_restore_hw_settings(mst); + if (ret) { + dev_err(dev, "restore hw settings failed: %d\n", ret); + goto err_power; + } + + /* 3. exit low power per link */ + for (i = 0; i < mst->num_links; i++) { + ret = mtk_sdw_exit_low_power_mode(mst, i); + if (ret) { + dev_err(dev, "[%d] exit low power mode failed: %d\n", + i, ret); + while (--i >= 0) + mtk_sdw_enter_low_power_mode(mst, i); + goto err_power; + } + } + + return 0; +err_power: + for (i = 0; i < mst->num_links; i++) + mtk_sdw_disable_power(mst); + + return ret; +} + +static const struct acpi_device_id mtk_sdw_acpi_match[] = { + { "NVDA9100", 0 }, + { } +}; +MODULE_DEVICE_TABLE(acpi, mtk_sdw_acpi_match); + +static const struct dev_pm_ops mtk_sdw_pm_ops = { + SYSTEM_SLEEP_PM_OPS(mtk_sdw_suspend, mtk_sdw_resume) +}; + +static struct platform_driver mtk_sdw_driver = { + .probe = mtk_sdw_probe, + .remove = mtk_sdw_remove, + .driver = { + .name = "mtk-soundwire", + .acpi_match_table = mtk_sdw_acpi_match, + .pm = &mtk_sdw_pm_ops, + }, +}; +module_platform_driver(mtk_sdw_driver); + +MODULE_DESCRIPTION("MediaTek SoundWire master"); +MODULE_AUTHOR("Trevor Wu "); +MODULE_LICENSE("GPL"); diff --git a/drivers/soundwire/mediatek/mtk-sdw.h b/drivers/soundwire/mediatek/mtk-sdw.h new file mode 100644 index 0000000000000..90ddefc418cea --- /dev/null +++ b/drivers/soundwire/mediatek/mtk-sdw.h @@ -0,0 +1,90 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * MediaTek SoundWire master driver header. + * + * Copyright (c) 2026 MediaTek Inc. + * Author: Trevor Wu + */ +#ifndef __MTK_SDW_H +#define __MTK_SDW_H + +#include "mtk-sdw-common.h" + +enum mt8901_afe_clk_domain { + MT8901_HOPPING_CLK_DOMAIN = 0, + MT8901_APLL_CLK_DOMAIN = 1, +}; + +enum mtk_sdw_sample_rate { + RATE_384000 = 384000, + RATE_352800 = 352800, + RATE_192000 = 192000, + RATE_176400 = 176400, + RATE_96000 = 96000, + RATE_88200 = 88200, + RATE_48000 = 48000, + RATE_44100 = 44100, + RATE_32000 = 32000, + RATE_29400 = 29400, + RATE_24000 = 24000, + RATE_22050 = 22050, + RATE_16000 = 16000, + RATE_14700 = 14700, + RATE_12000 = 12000, + RATE_11025 = 11025, + RATE_8000 = 8000, + RATE_7350 = 7350, +}; + +enum mt8901_afe_fs_mode { + MT8901_FS_8K = 0, + MT8901_FS_11D025K = 1, + MT8901_FS_12K = 2, + MT8901_FS_16K = 4, + MT8901_FS_22D05K = 5, + MT8901_FS_24K = 6, + MT8901_FS_32K = 8, + MT8901_FS_44D1K = 9, + MT8901_FS_48K = 10, + MT8901_FS_88D2K = 13, + MT8901_FS_96K = 14, + MT8901_FS_176D4K = 17, + MT8901_FS_192K = 18, + MT8901_FS_352D8K = 21, + MT8901_FS_384K = 22, + MT8901_ETDM_OUT0_HOPPING_DOMAIN = 4, + MT8901_ETDM_OUT5_HOPPING_DOMAIN = 9, + MT8901_ETDM_IN0_HOPPING_DOMAIN = 12, + MT8901_ETDM_IN5_HOPPING_DOMAIN = 17, + MT8901_FS_TOP_PDI_IP0_PDI0 = 24, + MT8901_FS_TOP_PDI_IP1_PDI0 = 25, + MT8901_FS_TOP_PDI_IP0_PDI1 = 26, + MT8901_FS_TOP_PDI_IP1_PDI1 = 27, +}; + +#define IS_48K_RATE_DOMAIN(x) (((x) % 8000) == 0) + +#define SDW_DEFAULT_CH_PER_PDI 2 + +#define AFE_SDW_TOP_PDI_REG_BASE (0x9000) +#define AFE_SDW_TOP_CON_REG_BASE (0x9048) + +#define SDW_DEFAULT_MAX_BUS_CLK_FREQ (6144000) +#define SDW_DEFAULT_MCLK_CLK_FREQ (24576000) +#define SDW_DEFAULT_FRAME_RATE (24000) +#define SDW_DEFAULT_FRAME_SHAPE_ROW (64) +#define SDW_DEFAULT_FRAME_SHAPE_COL (8) + +#define MTK_SDW_DP_SOURCE_BASE 2 /* DP2.. (master source / playback) */ +#define MTK_SDW_DP_SOURCE_NUM 5 +#define MTK_SDW_DP_SINK_BASE 7 /* DP7.. (master sink / capture) */ +#define MTK_SDW_DP_SINK_NUM 9 +#define MTK_SDW_MAX_DAIS \ + ((MTK_SDW_DP_SOURCE_NUM) + (MTK_SDW_DP_SINK_NUM)) /* BRA not included */ + +struct mtk_sdw_acpi_cb_context { + struct device *dev; + int err; +}; + +#endif /* __MTK_SDW_H */ diff --git a/sound/soc/mediatek/Kconfig b/sound/soc/mediatek/Kconfig index 3a1e1fa3fe5cc..76c2334eea16f 100644 --- a/sound/soc/mediatek/Kconfig +++ b/sound/soc/mediatek/Kconfig @@ -215,6 +215,16 @@ config SND_SOC_MTK_BTCVSD Select Y if you have such device. If unsure select "N". +config SND_SOC_MTK_SDCA + tristate "SDCA support for Mediatek chip" + depends on ACPI + select SND_SOC_SDCA + help + This enables SDCA support for codecs connecting with the + MediaTek SoundWire master. + Select Y if you use SDCA device. + If unsure select "N". + config SND_SOC_MT8188 tristate "ASoC support for MediaTek MT8188 chip" depends on ARCH_MEDIATEK || COMPILE_TEST @@ -353,4 +363,34 @@ config SND_SOC_MT8365_MT6357 Select Y if you have such device. If unsure select "N". +config SND_SOC_MT8901 + tristate "ASoC support for MediaTek MT8901 chip" + depends on ARCH_MEDIATEK && ACPI + depends on MTK_POWER_WRAP + select SND_SOC_MEDIATEK + help + This adds ASoC platform driver support for MediaTek MT8901 chip + that can be used with other codecs. + Select Y if you have such device. + If unsure select "N". + +config SND_SOC_MT8901_LEGACY_SDW_MACH + tristate "ASoC Audio driver for MT8901 SoundWire machine" + depends on SND_SOC_MT8901 && ACPI + depends on SOUNDWIRE_MTK + select SOUNDWIRE_MTK_MT8901_CLK + select SND_SOC_SDW_UTILS + select SND_SOC_RT712_SDCA_SDW + select SND_SOC_RT712_SDCA_DMIC_SDW + select SND_SOC_RT1320_SDW + select SND_SOC_CS35L56_SDW + select MFD_CS42L43_SDW + select SND_SOC_CS42L43 + select SND_SOC_CS42L43_SDW + help + This adds support for Legacy(No DSP) sound card for MediaTek + MT8901 boards. + Select Y if you have such device. + If unsure select "N". + endmenu diff --git a/sound/soc/mediatek/Makefile b/sound/soc/mediatek/Makefile index 7cd67bce92e9a..aa51bee96f321 100644 --- a/sound/soc/mediatek/Makefile +++ b/sound/soc/mediatek/Makefile @@ -11,3 +11,4 @@ obj-$(CONFIG_SND_SOC_MT8192) += mt8192/ obj-$(CONFIG_SND_SOC_MT8195) += mt8195/ obj-$(CONFIG_SND_SOC_MT8365) += mt8365/ obj-$(CONFIG_SND_SOC_MT8189) += mt8189/ +obj-$(CONFIG_SND_SOC_MT8901) += mt8901/ diff --git a/sound/soc/mediatek/common/mtk-afe-fe-dai.c b/sound/soc/mediatek/common/mtk-afe-fe-dai.c index 3809068f5620b..343ff9d30ec3d 100644 --- a/sound/soc/mediatek/common/mtk-afe-fe-dai.c +++ b/sound/soc/mediatek/common/mtk-afe-fe-dai.c @@ -459,6 +459,12 @@ int mtk_memif_set_channel(struct mtk_base_afe *afe, struct mtk_base_afe_memif *memif = &afe->memif[id]; unsigned int mono; + if (memif->data->ch_num_reg > 0) { + mtk_regmap_update_bits(afe->regmap, memif->data->ch_num_reg, + memif->data->ch_num_maskbit, channel, + memif->data->ch_num_shift); + } + if (memif->data->mono_shift < 0) return 0; diff --git a/sound/soc/mediatek/common/mtk-afe-platform-driver.c b/sound/soc/mediatek/common/mtk-afe-platform-driver.c index 938dd3d46b003..4f79283a4327d 100644 --- a/sound/soc/mediatek/common/mtk-afe-platform-driver.c +++ b/sound/soc/mediatek/common/mtk-afe-platform-driver.c @@ -101,7 +101,7 @@ snd_pcm_uframes_t mtk_afe_pcm_pointer(struct snd_soc_component *component, ret = regmap_read(regmap, reg_ofs_base, &hw_base); if (ret || hw_base == 0) { - dev_err(dev, "%s hw_ptr err\n", __func__); + dev_err(dev, "%s hw_base err\n", __func__); pcm_ptr_bytes = 0; goto POINTER_RETURN_FRAMES; } diff --git a/sound/soc/mediatek/common/mtk-base-afe.h b/sound/soc/mediatek/common/mtk-base-afe.h index a406f2e3e7a87..0161e5164af83 100644 --- a/sound/soc/mediatek/common/mtk-base-afe.h +++ b/sound/soc/mediatek/common/mtk-base-afe.h @@ -72,6 +72,16 @@ struct mtk_base_memif_data { int minlen_reg; int minlen_mask; int minlen_shift; + int maxlen_reg; + int maxlen_mask; + int maxlen_shift; + int domain_reg; + int domain_shift; + int domain_mask; + /* one hart mode */ + int one_heart_mode_sel_reg; + int one_heart_mode_shift; + int one_heart_mode_mask_shift; }; struct mtk_base_irq_data { @@ -87,6 +97,11 @@ struct mtk_base_irq_data { int irq_clr_reg; int irq_clr_shift; int irq_status_shift; + int irq_domain_reg; + int irq_domain_shift; + int irq_domain_maskbit; + int irq_mask_reg; + int irq_mask_shift; }; struct device; diff --git a/sound/soc/mediatek/mt8901/Makefile b/sound/soc/mediatek/mt8901/Makefile new file mode 100644 index 0000000000000..b2c921f11c6c2 --- /dev/null +++ b/sound/soc/mediatek/mt8901/Makefile @@ -0,0 +1,9 @@ +# SPDX-License-Identifier: GPL-2.0 + +snd-soc-mt8901-afe-y := \ + mt8901-afe-clk.o \ + mt8901-afe-pcm.o \ + mt8901-dai-soundwire.o + +obj-$(CONFIG_SND_SOC_MT8901) += snd-soc-mt8901-afe.o +obj-$(CONFIG_SND_SOC_MT8901_LEGACY_SDW_MACH) += mt8901-acp-sdw-legacy-mach.o diff --git a/sound/soc/mediatek/mt8901/mt8901-acp-sdw-legacy-mach.c b/sound/soc/mediatek/mt8901/mt8901-acp-sdw-legacy-mach.c new file mode 100644 index 0000000000000..cf374b3eeedae --- /dev/null +++ b/sound/soc/mediatek/mt8901/mt8901-acp-sdw-legacy-mach.c @@ -0,0 +1,672 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * ALSA SoC machine driver for ACPI legacy path + * + * Copyright (c) 2026 MediaTek Inc. + * Author: Trevor Wu + * Weiyi Hsieh + */ + +#include +#include +#include +#include +#include +#include + +static unsigned long soc_sdw_quirk; +static int quirk_override = -1; +module_param_named(quirk, quirk_override, int, 0444); +MODULE_PARM_DESC(quirk, "Board-specific quirk override"); + +#define MT8901_SDW_MAX_LINKS 2 +#define PCM_STREAM_NUM (SNDRV_PCM_STREAM_LAST + 1) + +/* + * The AFE playback interconnect is fixed in hardware: PDI2/PDI3/PDI4 are + * only reachable from the speaker memifs (DL0/DL_24CH) and PDI5/PDI6 only + * from the headphone memif (DL1) - see the O150..O159 mixer inputs in + * mt8901-dai-soundwire.c. Data port DP is hard-wired to PDI, so the + * jack stream must sit on DP5 (or DP6) or DL1 has no path to it. + */ +#define MT8901_SDW_JACK_PLAYBACK_DP 5 + +static struct snd_soc_dai_link_component afe_platform_component[] = { + { .name = "NVDA9022:00" } +}; + +struct mtk_mc_ctx { + unsigned int sdw_pin_index[MT8901_SDW_MAX_LINKS][PCM_STREAM_NUM]; + /* firmware DP allocation per link (acpi-scd-dp-allocation) */ + unsigned int sdw_src_base[MT8901_SDW_MAX_LINKS]; + unsigned int sdw_src_num[MT8901_SDW_MAX_LINKS]; +}; + +/* + * Pin (DAI index) reserved for the jack playback stream on this link, or + * -1 when the firmware DP allocation is unknown or does not cover DP5. + */ +static int mt8901_sdw_jack_pin(struct mtk_mc_ctx *mtk_ctx, int link) +{ + unsigned int base = mtk_ctx->sdw_src_base[link]; + unsigned int num = mtk_ctx->sdw_src_num[link]; + + if (!num) + return -1; + if (base > MT8901_SDW_JACK_PLAYBACK_DP || + base + num <= MT8901_SDW_JACK_PLAYBACK_DP) + return -1; + return MT8901_SDW_JACK_PLAYBACK_DP - base; +} + +/* + * Historically pins were handed out sequentially in endpoint order. On + * two-link boards (amps on link0, jack codec on link1) that works out + * because the firmware allocates link1's DPs starting at DP5. On + * single-link boards (all codecs on link1, DPs from DP2) the sequential + * order gave the jack DP2, which DL1 cannot reach, so opening the jack + * PCM failed with EINVAL. Reserve the DP5 pin for the jack stream and + * let everything else allocate around it. + */ +static unsigned int mt8901_sdw_alloc_pin(struct mtk_mc_ctx *mtk_ctx, + int link, int stream, int dai_type) +{ + int jack_pin = -1; + unsigned int pin; + + if (stream == SNDRV_PCM_STREAM_PLAYBACK) + jack_pin = mt8901_sdw_jack_pin(mtk_ctx, link); + + if (jack_pin >= 0 && dai_type == SOC_SDW_DAI_TYPE_JACK) { + /* + * Burn the sequential slot the jack would have taken so + * every other stream keeps the pin (and therefore the DP) + * it had before this reservation existed: UCM profiles + * written for the sequential order stay valid for all + * non-jack paths. Skip the burn when the sequence has + * already stepped over the jack pin (jack endpoint + * processed late): the skip below consumed the slot + * already, and burning again would leave a pin gap and + * push later streams past the firmware DP window. + */ + if (mtk_ctx->sdw_pin_index[link][stream] <= (unsigned int)jack_pin) + mtk_ctx->sdw_pin_index[link][stream]++; + return jack_pin; + } + + pin = mtk_ctx->sdw_pin_index[link][stream]++; + if ((int)pin == jack_pin) + pin = mtk_ctx->sdw_pin_index[link][stream]++; + return pin; +} + +static void mt8901_sdw_parse_dp_allocation(struct device *dev, + struct mtk_mc_ctx *mtk_ctx) +{ + /* the machine device is a child of the SoundWire controller */ + struct device *sdw_dev = dev->parent; + int i; + + if (!sdw_dev) + return; + + for (i = 0; i < MT8901_SDW_MAX_LINKS; i++) { + struct fwnode_handle *node; + char key[32]; + u32 dp[4]; + + snprintf(key, sizeof(key), "acpi-scd-%d-subproperties", i); + node = device_get_named_child_node(sdw_dev, key); + if (!node) + continue; + + if (!fwnode_property_read_u32_array(node, + "acpi-scd-dp-allocation", + dp, ARRAY_SIZE(dp))) { + mtk_ctx->sdw_src_base[i] = dp[0]; + mtk_ctx->sdw_src_num[i] = dp[1]; + dev_dbg(dev, "link %d playback DPs %u..%u\n", i, + dp[0], dp[0] + dp[1] - 1); + } + fwnode_handle_put(node); + } +} + +static void log_quirks(struct device *dev) +{ + if (SOC_SDW_JACK_JDSRC(soc_sdw_quirk)) + dev_dbg(dev, "quirk realtek,jack-detect-source %ld\n", + SOC_SDW_JACK_JDSRC(soc_sdw_quirk)); + if (soc_sdw_quirk & SOC_SDW_CODEC_SPKR) + dev_dbg(dev, "quirk SOC_SDW_CODEC_SPKR enabled\n"); + if (soc_sdw_quirk & SOC_SDW_CODEC_MIC) + dev_dbg(dev, "quirk SOC_SDW_CODEC_MIC enabled\n"); +} + +static const struct snd_soc_dapm_route mt8901_card_routes[] = { + /* TX: AFE O-ports -> SDW PDIs (playback) */ + {"PDI0", NULL, "O148"}, {"PDI0", NULL, "O149"}, + {"PDI2", NULL, "O150"}, {"PDI2", NULL, "O151"}, + {"PDI3", NULL, "O152"}, {"PDI3", NULL, "O153"}, + {"PDI4", NULL, "O154"}, {"PDI4", NULL, "O155"}, + {"PDI5", NULL, "O156"}, {"PDI5", NULL, "O157"}, + {"PDI6", NULL, "O158"}, {"PDI6", NULL, "O159"}, + {"PDI16", NULL, "O160"}, {"PDI16", NULL, "O161"}, + + /* RX: SDW PDIs -> AFE I-ports (capture) */ + {"I166", NULL, "PDI1"}, {"I167", NULL, "PDI1"}, + {"I168", NULL, "PDI7"}, {"I169", NULL, "PDI7"}, + {"I170", NULL, "PDI8"}, {"I171", NULL, "PDI8"}, + {"I172", NULL, "PDI9"}, {"I173", NULL, "PDI9"}, + {"I174", NULL, "PDI10"}, {"I175", NULL, "PDI10"}, + {"I176", NULL, "PDI11"}, {"I177", NULL, "PDI11"}, + {"I178", NULL, "PDI12"}, {"I179", NULL, "PDI12"}, + {"I180", NULL, "PDI13"}, {"I181", NULL, "PDI13"}, + {"I182", NULL, "PDI14"}, {"I183", NULL, "PDI14"}, + {"I184", NULL, "PDI15"}, {"I185", NULL, "PDI15"}, + {"I186", NULL, "PDI17"}, {"I187", NULL, "PDI17"}, +}; + +static const struct snd_soc_ops sdw_ops = { + .startup = asoc_sdw_startup, + .prepare = asoc_sdw_prepare, + .trigger = asoc_sdw_trigger, + .hw_params = asoc_sdw_hw_params, + .hw_free = asoc_sdw_hw_free, + .shutdown = asoc_sdw_shutdown, +}; + +static const char * const type_strings[] = { + "SimpleJack", "SmartAmp", "SmartMic"}; + +struct mtk_fe_dai { + const char *dai_name; /* AFE CPU DAI name, see mt8901-afe-pcm.c */ + int playback; /* 1 = playback FE, 0 = capture FE */ +}; + +static const struct mtk_fe_dai mt8901_fe_dais[] = { + { "DL0", 1 }, + { "DL1", 1 }, + { "DL2", 1 }, + { "DL3", 1 }, + { "DL4", 1 }, + { "DL5", 1 }, + { "DL_24CH", 1 }, + { "UL0", 0 }, + { "UL1", 0 }, + { "UL2", 0 }, + { "UL3", 0 }, + { "UL4", 0 }, + { "UL5", 0 }, + { "VUL_CM0", 0 }, + { "VUL_CM1", 0 }, + { "VUL_CM2", 0 }, +}; + +static int create_FE_dailinks(struct snd_soc_card *card, + struct snd_soc_dai_link **dai_links) +{ + struct device *dev = card->dev; + int i; + + for (i = 0; i < ARRAY_SIZE(mt8901_fe_dais); i++) { + const struct mtk_fe_dai *fe = &mt8901_fe_dais[i]; + struct snd_soc_dai_link_component *cpus; + struct snd_soc_dai_link *link = *dai_links; + + cpus = devm_kzalloc(dev, sizeof(*cpus), GFP_KERNEL); + if (!cpus) + return -ENOMEM; + + /* CPU = AFE memif DAI; platform = AFE; codec = shared dummy. */ + cpus->dai_name = fe->dai_name; + + link->name = devm_kasprintf(dev, GFP_KERNEL, "%s_FE", + fe->dai_name); + if (!link->name) + return -ENOMEM; + link->stream_name = + devm_kasprintf(dev, GFP_KERNEL, "%s %s", + fe->dai_name, + fe->playback ? "Playback" : "Capture"); + if (!link->stream_name) + return -ENOMEM; + + link->cpus = cpus; + link->num_cpus = 1; + link->codecs = &snd_soc_dummy_dlc; + link->num_codecs = 1; + link->platforms = afe_platform_component; + link->num_platforms = 1; + + link->dynamic = 1; /* DPCM front end */ + link->nonatomic = 1; /* same as BE */ + link->trigger[0] = SND_SOC_DPCM_TRIGGER_POST; + link->trigger[1] = SND_SOC_DPCM_TRIGGER_POST; + if (fe->playback) + link->playback_only = 1; + else + link->capture_only = 1; + + (*dai_links)++; + } + + return 0; +} + +static int create_sdw_dailink(struct snd_soc_card *card, + struct asoc_sdw_dailink *soc_dai, + struct snd_soc_dai_link **dai_links, + int *be_id, + struct snd_soc_codec_conf **codec_conf) +{ + struct device *dev = card->dev; + struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card); + struct mtk_mc_ctx *mtk_ctx = (struct mtk_mc_ctx *)ctx->private; + struct asoc_sdw_endpoint *soc_end; + int stream; + int ret; + + list_for_each_entry(soc_end, &soc_dai->endpoints, list) { + if (soc_end->name_prefix) { + (*codec_conf)->dlc.name = soc_end->codec_name; + (*codec_conf)->name_prefix = soc_end->name_prefix; + (*codec_conf)++; + } + + if (soc_end->include_sidecar && + soc_end->codec_info && + soc_end->codec_info->add_sidecar) { + ret = soc_end->codec_info->add_sidecar(card, dai_links, + codec_conf); + if (ret) + return ret; + } + } + + for_each_pcm_streams(stream) { + static const char * const sdw_stream_name[] = { + "SDW%d-PLAYBACK", + "SDW%d-CAPTURE", + "SDW%d-PLAYBACK-%s", + "SDW%d-CAPTURE-%s", + }; + struct snd_soc_dai_link_ch_map *codec_maps; + struct snd_soc_dai_link_component *codecs; + struct snd_soc_dai_link_component *cpus; + int num_cpus = hweight32(soc_dai->link_mask[stream]); + int num_codecs = soc_dai->num_devs[stream]; + int playback, capture; + int cur_link = 0; + int i = 0, j = 0; + int dai_type; + char *name; + const int str_name_with_type_offset = 2; + int str_offset = stream; + + if (!soc_dai->num_devs[stream]) + continue; + + soc_end = list_first_entry(&soc_dai->endpoints, + struct asoc_sdw_endpoint, list); + + *be_id = soc_end->dai_info->dailink[stream]; + if (*be_id < 0) { + dev_err(dev, "Invalid dailink id %d\n", *be_id); + return -EINVAL; + } + + /* create stream name according to first link id */ + if (ctx->append_dai_type) { + str_offset += str_name_with_type_offset; + dai_type = soc_end->dai_info->dai_type; + if (dai_type >= ARRAY_SIZE(type_strings)) { + dev_err(dev, "Invalid dai_type %d\n", dai_type); + return -EINVAL; + } + name = devm_kasprintf(dev, GFP_KERNEL, + sdw_stream_name[str_offset], + ffs(soc_end->link_mask) - 1, + type_strings[dai_type]); + } else { + name = devm_kasprintf(dev, GFP_KERNEL, + sdw_stream_name[str_offset], + ffs(soc_end->link_mask) - 1); + } + if (!name) + return -ENOMEM; + + cpus = devm_kcalloc(dev, num_cpus, sizeof(*cpus), GFP_KERNEL); + if (!cpus) + return -ENOMEM; + + codecs = devm_kcalloc(dev, num_codecs, sizeof(*codecs), + GFP_KERNEL); + if (!codecs) + return -ENOMEM; + + codec_maps = devm_kcalloc(dev, num_codecs, sizeof(*codec_maps), + GFP_KERNEL); + if (!codec_maps) + return -ENOMEM; + + list_for_each_entry(soc_end, &soc_dai->endpoints, list) { + if (!soc_end->dai_info->direction[stream]) + continue; + + if (cur_link != soc_end->link_mask) { + int link = ffs(soc_end->link_mask) - 1; + int pin; + + if (link < 0 || link >= MT8901_SDW_MAX_LINKS) { + dev_err(dev, "Invalid link index %d", + link); + return -EINVAL; + } + + pin = mt8901_sdw_alloc_pin(mtk_ctx, link, stream, + soc_end->dai_info->dai_type); + + cur_link = soc_end->link_mask; + if (stream == SNDRV_PCM_STREAM_PLAYBACK) { + cpus[i].dai_name = + devm_kasprintf(dev, GFP_KERNEL, + "SDW%d_Playback%d", + link, pin); + } else { + cpus[i].dai_name = + devm_kasprintf(dev, GFP_KERNEL, + "SDW%d_Capture%d", + link, pin); + } + if (!cpus[i].dai_name) + return -ENOMEM; + i++; + } + + codec_maps[j].cpu = i - 1; + codec_maps[j].codec = j; + + codecs[j].name = soc_end->codec_name; + codecs[j].dai_name = soc_end->dai_info->dai_name; + j++; + } + + WARN_ON(i != num_cpus || j != num_codecs); + + playback = (stream == SNDRV_PCM_STREAM_PLAYBACK); + capture = (stream == SNDRV_PCM_STREAM_CAPTURE); + + asoc_sdw_init_dai_link(dev, *dai_links, be_id, name, + playback, capture, + cpus, num_cpus, &snd_soc_dummy_dlc, + 1, codecs, num_codecs, + 1, asoc_sdw_rtd_init, &sdw_ops); + + (*dai_links)->nonatomic = true; + (*dai_links)->ch_maps = codec_maps; + + list_for_each_entry(soc_end, &soc_dai->endpoints, list) { + if (soc_end->dai_info->init) + soc_end->dai_info->init(card, *dai_links, + soc_end->codec_info, + playback); + } + + (*dai_links)++; + } + + return 0; +} + +static int create_sdw_dailinks(struct snd_soc_card *card, + struct snd_soc_dai_link **dai_links, int *be_id, + struct asoc_sdw_dailink *soc_dais, + struct snd_soc_codec_conf **codec_conf) +{ + struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card); + struct mtk_mc_ctx *mtk_ctx = (struct mtk_mc_ctx *)ctx->private; + int ret, i, stream; + + for (i = 0; i < MT8901_SDW_MAX_LINKS; i++) + for_each_pcm_streams(stream) + mtk_ctx->sdw_pin_index[i][stream] = 0; + + /* generate DAI links by each sdw link */ + while (soc_dais->initialised) { + int current_be_id = 0; + + ret = create_sdw_dailink(card, soc_dais, dai_links, + ¤t_be_id, codec_conf); + if (ret) + return ret; + + /* Update the be_id to match the highest ID used for SDW link */ + if (*be_id < current_be_id) + *be_id = current_be_id; + + soc_dais++; + } + + return 0; +} + +static int soc_card_dai_links_create(struct snd_soc_card *card) +{ + struct device *dev = card->dev; + int sdw_be_num = 0; + struct asoc_sdw_endpoint *soc_ends __free(kfree) = NULL; + struct asoc_sdw_dailink *soc_dais __free(kfree) = NULL; + struct snd_soc_aux_dev *soc_aux; + struct snd_soc_codec_conf *codec_conf; + struct snd_soc_dai_link *dai_links; + int num_devs = 0; + int num_ends = 0; + int num_confs; + int num_aux = 0; + int num_links; + int be_id = 0; + int ret; + + ret = asoc_sdw_count_sdw_endpoints(card, &num_devs, &num_ends, + &num_aux); + if (ret < 0) { + dev_err(dev, "failed to count sdw devices/endpoints: %d\n", + ret); + return ret; + } + + /* + * One codec_conf entry is consumed per surviving endpoint (each + * endpoint of a multi-endpoint codec carries the name prefix), so the + * budget must start from the endpoint count, not the device count. + * asoc_sdw_parse_sdw_endpoints() decrements it for each endpoint it + * skips. Same accounting as upstream sof_sdw. + */ + num_confs = num_ends; + + /* One per DAI link, worst case is a DAI link for every endpoint */ + soc_dais = kcalloc(num_ends, sizeof(*soc_dais), GFP_KERNEL); + if (!soc_dais) + return -ENOMEM; + + /* One per endpoint, ie. each DAI on each codec/amp */ + soc_ends = kcalloc(num_ends, sizeof(*soc_ends), GFP_KERNEL); + if (!soc_ends) + return -ENOMEM; + + soc_aux = devm_kcalloc(dev, num_aux, sizeof(*soc_aux), GFP_KERNEL); + if (!soc_aux) + return -ENOMEM; + + ret = asoc_sdw_parse_sdw_endpoints(card, soc_aux, soc_dais, soc_ends, + &num_confs); + if (ret < 0) { + dev_err(dev, "failed to parse sdw devices/endpoints: %d\n", + ret); + return ret; + } + + sdw_be_num = ret; + dev_dbg(dev, "sdw be %d", sdw_be_num); + + codec_conf = devm_kcalloc(dev, num_confs, sizeof(*codec_conf), + GFP_KERNEL); + if (!codec_conf) + return -ENOMEM; + + /* allocate FE + BE dailinks */ + num_links = ARRAY_SIZE(mt8901_fe_dais) + sdw_be_num; + dai_links = devm_kcalloc(dev, num_links, sizeof(*dai_links), + GFP_KERNEL); + if (!dai_links) + return -ENOMEM; + + card->codec_conf = codec_conf; + card->num_configs = num_confs; + card->dai_link = dai_links; + card->num_links = num_links; + + /* FE (AFE front ends) first, then BE (SoundWire). */ + ret = create_FE_dailinks(card, &dai_links); + if (ret) + return ret; + + /* SDW */ + if (sdw_be_num) { + ret = create_sdw_dailinks(card, &dai_links, &be_id, + soc_dais, &codec_conf); + if (ret) + return ret; + } + + WARN_ON(codec_conf != card->codec_conf + card->num_configs); + WARN_ON(dai_links != card->dai_link + card->num_links); + + return ret; +} + +static int mt8901_card_probe(struct platform_device *pdev) +{ + struct snd_soc_acpi_mach *mach = dev_get_platdata(&pdev->dev); + struct snd_soc_card *card; + struct mtk_mc_ctx *mtk_ctx; + struct asoc_sdw_mc_private *ctx; + int amp_num = 0; + int i, ret; + + mtk_ctx = devm_kzalloc(&pdev->dev, sizeof(*mtk_ctx), GFP_KERNEL); + if (!mtk_ctx) + return -ENOMEM; + + ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL); + if (!ctx) + return -ENOMEM; + ctx->codec_info_list_count = asoc_sdw_get_codec_info_list_count(); + ctx->private = mtk_ctx; + card = &ctx->card; + card->dev = &pdev->dev; + card->name = "mt8901-soundwire"; + card->owner = THIS_MODULE; + card->late_probe = asoc_sdw_card_late_probe; + card->dapm_routes = mt8901_card_routes; + card->num_dapm_routes = ARRAY_SIZE(mt8901_card_routes); + + snd_soc_card_set_drvdata(card, ctx); + + if (quirk_override != -1) { + dev_info(card->dev, "Overriding quirk 0x%lx => 0x%x\n", + soc_sdw_quirk, quirk_override); + soc_sdw_quirk = (unsigned int)quirk_override; + } + + log_quirks(card->dev); + + ctx->mc_quirk = soc_sdw_quirk; + dev_dbg(card->dev, "legacy quirk 0x%lx\n", ctx->mc_quirk); + + mt8901_sdw_parse_dp_allocation(&pdev->dev, mtk_ctx); + + /* reset amp_num to ensure amp_num++ starts from 0 in each probe */ + for (i = 0; i < ctx->codec_info_list_count; i++) + codec_info_list[i].amp_num = 0; + + ret = soc_card_dai_links_create(card); + if (ret < 0) { + dev_err_probe(card->dev, ret, + "soc_card_dai_links_create failed %d\n", ret); + goto err_ret; + } + + /* + * the default amp_num is zero for each codec and + * amp_num will only be increased for active amp + * codecs on used platform + */ + for (i = 0; i < ctx->codec_info_list_count; i++) + amp_num += codec_info_list[i].amp_num; + + card->components = devm_kasprintf(card->dev, GFP_KERNEL, + "cfg-amp:%d", amp_num); + if (!card->components) { + ret = -ENOMEM; + goto err_ret; + } + + if (mach && mach->mach_params.dmic_num) { + card->components = devm_kasprintf(card->dev, GFP_KERNEL, + "%s mic:dmic cfg-mics:%d", + card->components, + mach->mach_params.dmic_num); + if (!card->components) { + ret = -ENOMEM; + goto err_ret; + } + } + + /* Register the card */ + ret = devm_snd_soc_register_card(card->dev, card); + if (ret) { + dev_err_probe(card->dev, ret, + "snd_soc_register_card failed %d\n", ret); + goto err_ret; + } + + platform_set_drvdata(pdev, card); + + return 0; + +err_ret: + asoc_sdw_mc_dailink_exit_loop(card); + return ret; +} + +static void mt8901_card_remove(struct platform_device *pdev) +{ + struct snd_soc_card *card = platform_get_drvdata(pdev); + + asoc_sdw_mc_dailink_exit_loop(card); +} + +static const struct platform_device_id mc_id_table[] = { + { "mtk_sdw_mc", }, + {} +}; +MODULE_DEVICE_TABLE(platform, mc_id_table); + +static struct platform_driver soc_sdw_driver = { + .driver = { + .name = "mtk_sdw_mc", + .pm = &snd_soc_pm_ops, + }, + .probe = mt8901_card_probe, + .remove = mt8901_card_remove, + .id_table = mc_id_table, +}; + +module_platform_driver(soc_sdw_driver); + +MODULE_DESCRIPTION("MTK SoundWire legacy MT8901 machine driver"); +MODULE_AUTHOR("Trevor Wu "); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("SND_SOC_SDW_UTILS"); diff --git a/sound/soc/mediatek/mt8901/mt8901-afe-clk.c b/sound/soc/mediatek/mt8901/mt8901-afe-clk.c new file mode 100644 index 0000000000000..9b4482c327ff2 --- /dev/null +++ b/sound/soc/mediatek/mt8901/mt8901-afe-clk.c @@ -0,0 +1,685 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * mt8901-afe-clk.c -- MediaTek 8901 afe clock ctrl + * + * Copyright (c) 2026 MediaTek Inc. + * Author: Trevor Wu + * Weiyi Hsieh + */ + +#include +#include +#include "mt8901-afe-common.h" +#include "mt8901-afe-clk.h" +#include "mt8901-reg.h" +#include + +static const unsigned int mt8901_main_clk_muxes[] = { + AFE_CLK_VLP_CKSYS_TOP_AUDIO_H_SEL, + AFE_CLK_VLP_CKSYS_TOP_AUD_ENGEN1_SEL, + AFE_CLK_VLP_CKSYS_TOP_AUD_ENGEN2_SEL, + AFE_CLK_VLP_CKSYS_TOP_AUD_INTBUS_SEL, + AFE_CLK_VLP_CKSYS_TOP_AUD_1_SEL, + AFE_CLK_VLP_CKSYS_TOP_AUD_2_SEL, + AFE_CLK_VLP_CKSYS_TOP_VADSP_SEL, +}; + +static const unsigned int mt8901_apll_top_con_cgs[] = { + AUD_CG_APLL1_CK, + AUD_CG_APLL2_CK, + AUD_CG_APLL_TUNER1, + AUD_CG_APLL_TUNER2, +}; + +static const struct { + unsigned int mux; + const char *parent_name; + unsigned int level; +} mt8901_clk_parent_map[] = { + { AFE_CLK_VLP_CKSYS_TOP_AUDIO_H_SEL, + CLK_PARENT_TCX_26M_MX9_CK_NAME, 0 }, + { AFE_CLK_VLP_CKSYS_TOP_AUDIO_H_SEL, + CLK_PARENT_OSC_D20_NAME, 1 }, + { AFE_CLK_VLP_CKSYS_TOP_AUDIO_H_SEL, + CLK_PARENT_APLL1_D2_NAME, 2 }, + { AFE_CLK_VLP_CKSYS_TOP_AUDIO_H_SEL, + CLK_PARENT_APLL2_D2_NAME, 3 }, + + { AFE_CLK_VLP_CKSYS_TOP_AUD_ENGEN1_SEL, + CLK_PARENT_TCX_26M_MX9_CK_NAME, 0 }, + { AFE_CLK_VLP_CKSYS_TOP_AUD_ENGEN1_SEL, + CLK_PARENT_OSC_D20_NAME, 1 }, + { AFE_CLK_VLP_CKSYS_TOP_AUD_ENGEN1_SEL, + CLK_PARENT_APLL1_D8_NAME, 2 }, + { AFE_CLK_VLP_CKSYS_TOP_AUD_ENGEN1_SEL, + CLK_PARENT_APLL1_D4_NAME, 3 }, + + { AFE_CLK_VLP_CKSYS_TOP_AUD_ENGEN2_SEL, + CLK_PARENT_TCX_26M_MX9_CK_NAME, 0 }, + { AFE_CLK_VLP_CKSYS_TOP_AUD_ENGEN2_SEL, + CLK_PARENT_OSC_D20_NAME, 1 }, + { AFE_CLK_VLP_CKSYS_TOP_AUD_ENGEN2_SEL, + CLK_PARENT_APLL2_D8_NAME, 2 }, + { AFE_CLK_VLP_CKSYS_TOP_AUD_ENGEN2_SEL, + CLK_PARENT_APLL2_D4_NAME, 3 }, + + { AFE_CLK_VLP_CKSYS_TOP_AUD_INTBUS_SEL, + CLK_PARENT_TCX_26M_MX9_CK_NAME, 0 }, + { AFE_CLK_VLP_CKSYS_TOP_AUD_INTBUS_SEL, + CLK_PARENT_OSC_D20_NAME, 1 }, + { AFE_CLK_VLP_CKSYS_TOP_AUD_INTBUS_SEL, + CLK_PARENT_MAINPLL2_D7_D4_NAME, 2 }, + { AFE_CLK_VLP_CKSYS_TOP_AUD_INTBUS_SEL, + CLK_PARENT_MAINPLL2_D4_D4_NAME, 3 }, + + { AFE_CLK_VLP_CKSYS_TOP_AUD_1_SEL, + CLK_PARENT_TCX_26M_MX9_CK_NAME, 0 }, + { AFE_CLK_VLP_CKSYS_TOP_AUD_1_SEL, + CLK_PARENT_APLL1_NAME, 1 }, + + { AFE_CLK_VLP_CKSYS_TOP_AUD_2_SEL, + CLK_PARENT_TCX_26M_MX9_CK_NAME, 0 }, + { AFE_CLK_VLP_CKSYS_TOP_AUD_2_SEL, + CLK_PARENT_APLL2_NAME, 1 }, +}; + +static const struct { + unsigned int mux; + const char *idle_parent_name; + const char *active_parent_name; +} idle_clk_muxes[] = { + { AFE_CLK_VLP_CKSYS_TOP_AUDIO_H_SEL, + CLK_PARENT_OSC_D20_NAME, CLK_PARENT_APLL2_D2_NAME }, + { AFE_CLK_VLP_CKSYS_TOP_AUD_INTBUS_SEL, + CLK_PARENT_OSC_D20_NAME, CLK_PARENT_MAINPLL2_D4_D4_NAME }, + { AFE_CLK_VLP_CKSYS_TOP_AUD_1_SEL, + CLK_PARENT_TCX_26M_MX9_CK_NAME, CLK_PARENT_APLL1_NAME }, + { AFE_CLK_VLP_CKSYS_TOP_AUD_2_SEL, + CLK_PARENT_TCX_26M_MX9_CK_NAME, CLK_PARENT_APLL2_NAME }, + { AFE_CLK_VLP_CKSYS_TOP_AUD_ENGEN1_SEL, + CLK_PARENT_OSC_D20_NAME, CLK_PARENT_APLL1_D4_NAME }, + { AFE_CLK_VLP_CKSYS_TOP_AUD_ENGEN2_SEL, + CLK_PARENT_OSC_D20_NAME, CLK_PARENT_APLL2_D4_NAME }, +}; + +static const char * const mt8901_cg_names[] = { + [AUD_CG_HW_GAIN01] = "HW_GAIN01", + [AUD_CG_HW_GAIN23] = "HW_GAIN23", + [AUD_CG_CM0] = "CM0", + [AUD_CG_CM1] = "CM1", + [AUD_CG_CM2] = "CM2", + [AUD_CG_HW_GAIN_DL0_DL1] = "HW_GAIN_DL0_DL1", + [AUD_CG_HW_GAIN_DL2_DL3] = "HW_GAIN_DL2_DL3", + [AUD_CG_HW_GAIN_DL4_DL5] = "HW_GAIN_DL4_DL5", + [AUD_CG_HW_GAIN_DL24CH] = "HW_GAIN_DL24CH", + [AUD_CG_UL0_ADC] = "UL0_ADC", + [AUD_CG_UL0_TML] = "UL0_TML", + [AUD_CG_UL0_ADC_HIRES] = "UL0_ADC_HIRES", + [AUD_CG_UL0_ADC_HIRES_TML] = "UL0_ADC_HIRES_TML", + [AUD_CG_UL1_ADC] = "UL1_ADC", + [AUD_CG_UL1_TML] = "UL1_TML", + [AUD_CG_UL1_ADC_HIRES] = "UL1_ADC_HIRES", + [AUD_CG_UL1_ADC_HIRES_TML] = "UL1_ADC_HIRES_TML", + [AUD_CG_ETDM_OUT0] = "ETDM_OUT0", + [AUD_CG_ETDM_OUT5] = "ETDM_OUT5", + [AUD_CG_ETDM_IN0] = "ETDM_IN0", + [AUD_CG_ETDM_IN5] = "ETDM_IN5", + [AUD_CG_GENERAL0_ASRC] = "GASRC0", + [AUD_CG_GENERAL1_ASRC] = "GASRC1", + [AUD_CG_GENERAL2_ASRC] = "GASRC2", + [AUD_CG_GENERAL3_ASRC] = "GASRC3", + [AUD_CG_GENERAL4_ASRC] = "GASRC4", + [AUD_CG_GENERAL5_ASRC] = "GASRC5", + [AUD_CG_GENERAL6_ASRC] = "GASRC6", + [AUD_CG_GENERAL7_ASRC] = "GASRC7", + [AUD_CG_APLL_TUNER1] = "APLL_TUNER1", + [AUD_CG_APLL_TUNER2] = "APLL_TUNER2", + [AUD_CG_H208M_CK] = "H208M", + [AUD_CG_APLL2_CK] = "APLL2", + [AUD_CG_APLL1_CK] = "APLL1", + [AUD_CG_AUDIO_F26M_CK] = "F26M", + [AUD_CG_AUDIO_HOPPING_CK] = "HOPPING", + [AUD_CG_SOUNDWIRE_DMIC0_ADC] = "SDW_DMIC0_ADC", + [AUD_CG_SOUNDWIRE_DMIC0_TML] = "SDW_DMIC0_TML", + [AUD_CG_SOUNDWIRE_DMIC1_ADC] = "SDW_DMIC1_ADC", + [AUD_CG_SOUNDWIRE_DMIC1_TML] = "SDW_DMIC1_TML", + [AUD_CG_SOUNDWIRE_DMIC2_ADC] = "SDW_DMIC2_ADC", + [AUD_CG_SOUNDWIRE_DMIC2_TML] = "SDW_DMIC2_TML", + [AUD_CG_SOUNDWIRE_DMIC3_ADC] = "SDW_DMIC3_ADC", + [AUD_CG_SOUNDWIRE_DMIC3_TML] = "SDW_DMIC3_TML", + [AUD_CG_SOUNDWIRE_DMIC4_ADC] = "SDW_DMIC4_ADC", + [AUD_CG_SOUNDWIRE_DMIC4_TML] = "SDW_DMIC4_TML", + [AUD_CG_SOUNDWIRE_DMIC5_ADC] = "SDW_DMIC5_ADC", + [AUD_CG_SOUNDWIRE_DMIC5_TML] = "SDW_DMIC5_TML", + [AUD_CG_SOUNDWIRE_DMIC6_ADC] = "SDW_DMIC6_ADC", + [AUD_CG_SOUNDWIRE_DMIC6_TML] = "SDW_DMIC6_TML", + [AUD_CG_SOUNDWIRE_DMIC7_ADC] = "SDW_DMIC7_ADC", + [AUD_CG_SOUNDWIRE_DMIC7_TML] = "SDW_DMIC7_TML", + [AUD_CG_SOUNDWIRE1] = "SOUNDWIRE1", + [AUD_CG_SOUNDWIRE0] = "SOUNDWIRE0", +}; + +static int mt8901_afe_send_power_req(struct device *dev, u8 cmd, + u32 d0, u32 d1, u32 d2) +{ + struct power_cntl_scmi_data scmidata = { + .requestId = cmd, + .customdata = { d0, d1, d2 }, + }; + struct _power_cntrl_request req = { + .dev = dev, + .InBuffer = &scmidata, + .InBufferSize = sizeof(scmidata), + }; + + return mtk_send_power_control_req(&req); +} + +static int mt8901_afe_pwr_ctrl_scene_req(struct device *dev, unsigned int scene) +{ + return mt8901_afe_send_power_req(dev, SSPM_AUD_CMD_PWR_CTRL_SCENE, + scene, 0, 0); +} + +static int mt8901_afe_clk_cg_deinit_req(struct device *dev) +{ + return mt8901_afe_send_power_req(dev, SSPM_AUD_CMD_CLK_CG_DEINIT, + 0, 0, 0); +} + +static int mt8901_afe_cg_req(struct device *dev, unsigned int cg, bool enable) +{ + return mt8901_afe_send_power_req(dev, SSPM_AUD_CMD_CG, cg, enable, 0); +} + +static int mt8901_afe_mtcmos_req(struct device *dev, unsigned int mtcmos, + bool enable) +{ + return mt8901_afe_send_power_req(dev, SSPM_AUD_CMD_MTCMOS, mtcmos, + enable, 0); +} + +static int mt8901_afe_afe_spm_ctrl_req(struct device *dev, unsigned int req, + bool enable) +{ + return mt8901_afe_send_power_req(dev, SSPM_AUD_CMD_AFE_SPM_CTRL, + req, enable, 0); +} + +static int mt8901_afe_clk_mux_req(struct device *dev, unsigned int mux, + bool enable) +{ + return mt8901_afe_send_power_req(dev, SSPM_AUD_CMD_VLP_SYS_CLK_MUX, + mux, enable, 0); +} + +static int mt8901_afe_clk_set_parent_req(struct device *dev, unsigned int mux, + unsigned int parent_level) +{ + return mt8901_afe_send_power_req(dev, + SSPM_AUD_CMD_VLP_SYS_CLK_SET_PARENT, + mux, parent_level, 0); +} + +static int mt8901_afe_enable_afe_on(struct mtk_base_afe *afe) +{ + unsigned int mask; + + mask = AUDIO_ENGEN_CON0_APLL2_MASK | + AUDIO_ENGEN_CON0_APLL1_MASK | + AUDIO_ENGEN_CON0_26M_MASK; + return regmap_update_bits(afe->regmap, AUDIO_ENGEN_CON0, mask, mask); +} + +static int mt8901_afe_disable_afe_on(struct mtk_base_afe *afe) +{ + unsigned int mask; + + mask = AUDIO_ENGEN_CON0_APLL2_MASK | + AUDIO_ENGEN_CON0_APLL1_MASK | + AUDIO_ENGEN_CON0_26M_MASK; + return regmap_update_bits(afe->regmap, AUDIO_ENGEN_CON0, mask, 0x0); +} + +static int mt8901_afe_check_sspm_probe(struct mtk_base_afe *afe) +{ + struct device *dev = afe->dev; + int ret; + + ret = mt8901_afe_pwr_ctrl_scene_req(dev, AUDIO_PWR_SCEN_NORMAL); + if (ret) { + dev_err(dev, "SSPM probe failed: %d\n", ret); + return ret; + } + + dev_dbg(dev, "SSPM available\n"); + return 0; +} + +int mt8901_afe_disable_default_on_clk_and_top_cg(struct mtk_base_afe *afe) +{ + struct device *dev = afe->dev; + int ret; + + ret = mt8901_afe_clk_cg_deinit_req(dev); + dev_dbg(dev, "disable_default_on_clk_and_top_cg: %d\n", ret); + + return ret; +} + +int mt8901_afe_init_clock(struct mtk_base_afe *afe) +{ + struct device *dev = afe->dev; + int ret; + + ret = mt8901_afe_check_sspm_probe(afe); + if (ret) + return ret; + + ret = mt8901_afe_disable_default_on_clk_and_top_cg(afe); + if (ret) { + dev_err(dev, "disable_default_on_clk_and_top_cg failed: %d\n", + ret); + return ret; + } + + return ret; +} + +int mt8901_afe_enable_top_cg(struct mtk_base_afe *afe, unsigned int cg) +{ + struct device *dev = afe->dev; + const char *name; + int ret; + + if (cg >= AUD_CG_NUMS) + return -EINVAL; + + name = mt8901_cg_names[cg]; + + ret = mt8901_afe_cg_req(dev, cg, true); + + dev_dbg(dev, "enable_top_cg %s: %d\n", name, ret); + return ret; +} + +int mt8901_afe_disable_top_cg(struct mtk_base_afe *afe, unsigned int cg) +{ + struct device *dev = afe->dev; + const char *name; + int ret; + + if (cg >= AUD_CG_NUMS) + return -EINVAL; + + name = mt8901_cg_names[cg]; + + ret = mt8901_afe_cg_req(dev, cg, false); + + dev_dbg(dev, "disable_top_cg %s: %d\n", name, ret); + return ret; +} + +int mt8901_afe_enable_apll_top_con_cg(struct mtk_base_afe *afe) +{ + struct device *dev = afe->dev; + int i, ret; + + for (i = 0; i < ARRAY_SIZE(mt8901_apll_top_con_cgs); i++) { + ret = mt8901_afe_enable_top_cg(afe, mt8901_apll_top_con_cgs[i]); + if (ret) { + dev_err(dev, "enable_top_cg %s failed: %d\n", + mt8901_cg_names[mt8901_apll_top_con_cgs[i]], + ret); + goto err; + } + } + + return 0; + +err: + while (i-- > 0) + mt8901_afe_disable_top_cg(afe, mt8901_apll_top_con_cgs[i]); + + return ret; +} + +/* + * The disable helpers are transactional: on failure they re-enable what they + * already released and return the error, so a caller's own unwind can assume + * the clocks are still in the state they were in before the call. + */ +int mt8901_afe_disable_apll_top_con_cg(struct mtk_base_afe *afe) +{ + struct device *dev = afe->dev; + int i, ret; + + for (i = ARRAY_SIZE(mt8901_apll_top_con_cgs) - 1; i >= 0; i--) { + ret = mt8901_afe_disable_top_cg(afe, + mt8901_apll_top_con_cgs[i]); + if (ret) { + dev_warn(dev, "disable_top_cg %s failed: %d\n", + mt8901_cg_names[mt8901_apll_top_con_cgs[i]], + ret); + goto err; + } + } + + return 0; + +err: + while (++i < ARRAY_SIZE(mt8901_apll_top_con_cgs)) + mt8901_afe_enable_top_cg(afe, mt8901_apll_top_con_cgs[i]); + + return ret; +} + +int mt8901_afe_enable_mtcmos(struct mtk_base_afe *afe, unsigned int mtcmos) +{ + struct device *dev = afe->dev; + int ret; + + if (mtcmos >= AUDIO_PD_NUM) + return -EINVAL; + + ret = mt8901_afe_mtcmos_req(dev, mtcmos, true); + dev_dbg(dev, "enable_mtcmos %u: %d\n", mtcmos, ret); + return ret; +} + +int mt8901_afe_disable_mtcmos(struct mtk_base_afe *afe, unsigned int mtcmos) +{ + struct device *dev = afe->dev; + int ret; + + if (mtcmos >= AUDIO_PD_NUM) + return -EINVAL; + + ret = mt8901_afe_mtcmos_req(dev, mtcmos, false); + dev_dbg(dev, "disable_mtcmos %u: %d\n", mtcmos, ret); + return ret; +} + +int mt8901_afe_send_spm_req(struct mtk_base_afe *afe, unsigned int req, + bool enable) +{ + struct device *dev = afe->dev; + int ret; + + if (req >= AFE_SPM_CTRL_REQ_NUMS) + return -EINVAL; + + ret = mt8901_afe_afe_spm_ctrl_req(dev, req, enable); + dev_dbg(dev, "send_spm_req %u en=%d: %d\n", req, enable, ret); + return ret; +} + +static int mt8901_afe_enable_main_clk_muxes(struct mtk_base_afe *afe) +{ + struct device *dev = afe->dev; + int i, ret; + + for (i = 0; i < ARRAY_SIZE(mt8901_main_clk_muxes); i++) { + ret = mt8901_afe_clk_mux_req(dev, mt8901_main_clk_muxes[i], + true); + dev_dbg(dev, "enable main_clk mux[%d]: %d\n", i, ret); + if (ret) + goto err_ret; + } + + return 0; +err_ret: + while (i-- > 0) + mt8901_afe_clk_mux_req(dev, mt8901_main_clk_muxes[i], false); + + return ret; +} + +static int mt8901_afe_disable_main_clk_muxes(struct mtk_base_afe *afe) +{ + struct device *dev = afe->dev; + int i, ret; + + for (i = 0; i < ARRAY_SIZE(mt8901_main_clk_muxes); i++) { + ret = mt8901_afe_clk_mux_req(dev, mt8901_main_clk_muxes[i], + false); + dev_dbg(dev, "disable main_clk mux[%d]: %d\n", i, ret); + if (ret) { + dev_warn(dev, "disable main_clk mux[%d] failed: %d\n", + i, ret); + goto err; + } + } + + return 0; + +err: + while (i-- > 0) + mt8901_afe_clk_mux_req(dev, mt8901_main_clk_muxes[i], true); + + return ret; +} + +int mt8901_afe_enable_main_clock(struct mtk_base_afe *afe) +{ + struct device *dev = afe->dev; + int ret; + + ret = mt8901_afe_enable_main_clk_muxes(afe); + if (ret) { + dev_err(dev, "enable_main_clk_muxes failed: %d\n", ret); + return ret; + } + + ret = mt8901_afe_enable_top_cg(afe, AUD_CG_AUDIO_HOPPING_CK); + if (ret) { + dev_err(dev, "enable_top_cg HOPPING failed: %d\n", ret); + goto err_muxes; + } + + ret = mt8901_afe_enable_top_cg(afe, AUD_CG_AUDIO_F26M_CK); + if (ret) { + dev_err(dev, "enable_top_cg F26M failed: %d\n", ret); + goto err_hopping; + } + + ret = mt8901_afe_enable_afe_on(afe); + if (ret) { + dev_err(dev, "enable_afe_on failed: %d\n", ret); + goto err_f26m; + } + + return 0; + +err_f26m: + mt8901_afe_disable_top_cg(afe, AUD_CG_AUDIO_F26M_CK); +err_hopping: + mt8901_afe_disable_top_cg(afe, AUD_CG_AUDIO_HOPPING_CK); +err_muxes: + mt8901_afe_disable_main_clk_muxes(afe); + + return ret; +} + +int mt8901_afe_disable_main_clock(struct mtk_base_afe *afe) +{ + struct device *dev = afe->dev; + int ret; + + ret = mt8901_afe_disable_afe_on(afe); + if (ret) { + dev_err(dev, "disable_afe_on failed: %d\n", ret); + return ret; + } + + ret = mt8901_afe_disable_top_cg(afe, AUD_CG_AUDIO_F26M_CK); + if (ret) { + dev_err(dev, "disable_top_cg F26M failed: %d\n", ret); + goto err_afe_on; + } + + ret = mt8901_afe_disable_top_cg(afe, AUD_CG_AUDIO_HOPPING_CK); + if (ret) { + dev_err(dev, "disable_top_cg HOPPING failed: %d\n", ret); + goto err_f26m; + } + + ret = mt8901_afe_disable_main_clk_muxes(afe); + if (ret) { + dev_err(dev, "disable_main_clk_muxes failed: %d\n", ret); + goto err_hopping; + } + + return 0; + +err_hopping: + mt8901_afe_enable_top_cg(afe, AUD_CG_AUDIO_HOPPING_CK); +err_f26m: + mt8901_afe_enable_top_cg(afe, AUD_CG_AUDIO_F26M_CK); +err_afe_on: + mt8901_afe_enable_afe_on(afe); + + return ret; +} + +int mt8901_afe_enable_reg_rw_clk(struct mtk_base_afe *afe) +{ + struct device *dev = afe->dev; + int ret; + + ret = mt8901_afe_clk_mux_req(dev, AFE_CLK_VLP_CKSYS_TOP_AUD_INTBUS_SEL, + true); + if (ret) { + dev_err(dev, "enable reg_rw_clk INTBUS failed: %d\n", ret); + return ret; + } + + ret = mt8901_afe_clk_mux_req(dev, AFE_CLK_VLP_CKSYS_TOP_VADSP_SEL, + true); + if (ret) { + dev_err(dev, "enable reg_rw_clk VADSP failed: %d\n", ret); + goto err_intbus; + } + + ret = mt8901_afe_enable_top_cg(afe, AUD_CG_AUDIO_F26M_CK); + if (ret) { + dev_err(dev, "enable_top_cg F26M failed: %d\n", ret); + goto err_vadsp; + } + + return 0; + +err_vadsp: + mt8901_afe_clk_mux_req(dev, AFE_CLK_VLP_CKSYS_TOP_VADSP_SEL, false); +err_intbus: + mt8901_afe_clk_mux_req(dev, AFE_CLK_VLP_CKSYS_TOP_AUD_INTBUS_SEL, + false); + + return ret; +} + +int mt8901_afe_disable_reg_rw_clk(struct mtk_base_afe *afe) +{ + struct device *dev = afe->dev; + int ret; + + ret = mt8901_afe_disable_top_cg(afe, AUD_CG_AUDIO_F26M_CK); + if (ret) { + dev_err(dev, "disable_top_cg F26M failed: %d\n", ret); + return ret; + } + + ret = mt8901_afe_clk_mux_req(dev, AFE_CLK_VLP_CKSYS_TOP_VADSP_SEL, + false); + if (ret) { + dev_err(dev, "disable reg_rw_clk VADSP failed: %d\n", ret); + goto err_f26m; + } + + ret = mt8901_afe_clk_mux_req(dev, AFE_CLK_VLP_CKSYS_TOP_AUD_INTBUS_SEL, + false); + if (ret) { + dev_err(dev, "disable reg_rw_clk INTBUS failed: %d\n", ret); + goto err_vadsp; + } + + return 0; + +err_vadsp: + mt8901_afe_clk_mux_req(dev, AFE_CLK_VLP_CKSYS_TOP_VADSP_SEL, true); +err_f26m: + mt8901_afe_enable_top_cg(afe, AUD_CG_AUDIO_F26M_CK); + + return ret; +} + +static int mt8901_afe_map_clk_parent(unsigned int mux, const char *parent_name, + unsigned int *level) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(mt8901_clk_parent_map); i++) { + if (mt8901_clk_parent_map[i].mux == mux && + !strcmp(mt8901_clk_parent_map[i].parent_name, + parent_name)) { + *level = mt8901_clk_parent_map[i].level; + return 0; + } + } + + return -EINVAL; +} + +int mt8901_afe_set_clock_parent(struct mtk_base_afe *afe, unsigned int mux, + const char *parent_name) +{ + struct device *dev = afe->dev; + int ret; + unsigned int parent_level; + + ret = mt8901_afe_map_clk_parent(mux, parent_name, &parent_level); + if (ret) { + dev_err(dev, "map_clock_parent mux=%u parent=%s failed %d\n", + mux, parent_name, ret); + return ret; + } + + ret = mt8901_afe_clk_set_parent_req(dev, mux, parent_level); + dev_dbg(dev, "set_clock_parent mux=%u parent=%u: %d\n", + mux, parent_level, ret); + + return ret; +} + +int mt8901_afe_set_idle_clock_parent(struct mtk_base_afe *afe, bool idle) +{ + const char *parent_name; + int i, j, ret; + + for (i = 0; i < ARRAY_SIZE(idle_clk_muxes); i++) { + parent_name = idle ? idle_clk_muxes[i].idle_parent_name : + idle_clk_muxes[i].active_parent_name; + + ret = mt8901_afe_set_clock_parent(afe, idle_clk_muxes[i].mux, + parent_name); + if (ret) + goto err_rollback; + } + + return 0; + +err_rollback: + /* + * Best effort: re-parent the muxes already switched back to the + * other set, so a failed transition does not leave a mixed + * idle/active clock topology. + */ + for (j = i - 1; j >= 0; j--) { + parent_name = idle ? idle_clk_muxes[j].active_parent_name : + idle_clk_muxes[j].idle_parent_name; + mt8901_afe_set_clock_parent(afe, idle_clk_muxes[j].mux, + parent_name); + } + + return ret; +} diff --git a/sound/soc/mediatek/mt8901/mt8901-afe-clk.h b/sound/soc/mediatek/mt8901/mt8901-afe-clk.h new file mode 100644 index 0000000000000..171d62aa02086 --- /dev/null +++ b/sound/soc/mediatek/mt8901/mt8901-afe-clk.h @@ -0,0 +1,180 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * mt8901-afe-clk.h -- MediaTek 8901 afe clock ctrl definition + * + * Copyright (c) 2026 MediaTek Inc. + * Author: Trevor Wu + * Weiyi Hsieh + */ + +#ifndef _MT8901_AFE_CLK_H_ +#define _MT8901_AFE_CLK_H_ + +struct mtk_base_afe; +struct device; + +enum sspm_aud_cmd { + SSPM_AUD_CMD_MTCMOS = 0, + SSPM_AUD_CMD_CG, + SSPM_AUD_CMD_VLP_SYS_CLK_MUX, + SSPM_AUD_CMD_VLP_SYS_CLK_SET_RATE, + SSPM_AUD_CMD_VLP_SYS_CLK_SET_PARENT, + SSPM_AUD_CMD_AFE_SPM_CTRL, + SSPM_AUD_CMD_PWR_CTRL_SCENE, + SSPM_AUD_CMD_CLK_CG_DEINIT, + SSPM_AUD_CMD_NUM, +}; + +enum audio_mtcmos_pd_id { + AUDIO_VADSYS_PD_AO = 0, + AUDIO_VADSYS_PD_INFRA, + AUDIO_PD, + AUDIO_PD_NUM, +}; + +enum afe_pwr_scene { + AUDIO_PWR_SCEN_NORMAL = 0, + AUDIO_PWR_SCEN_BY_DSTATE, + AUDIO_PWR_SCEN_AON, + AUDIO_PWR_SCEN_NUM, +}; + +enum afe_spm_ctrl_req_type { + AFE_SPM_CTRL_VCORE_REQ = 0, + AFE_SPM_CTRL_PMIC_REQ, + AFE_SPM_CTRL_SRCCLKENA_REQ, + AFE_SPM_CTRL_APSRC_REQ, + AFE_SPM_CTRL_VRF18_REQ, + AFE_SPM_CTRL_INFRA_REQ, + AFE_SPM_CTRL_DDREN_REQ, + AFE_SPM_CTRL_REQ_NUMS, +}; + +enum audio_top_con_cg_type { + AUD_CG_ASD_BEGIN, + /* AUDIO_TOP_CON0 (0x0000) */ + AUD_CG_HW_GAIN01 = AUD_CG_ASD_BEGIN, + AUD_CG_HW_GAIN23, + AUD_CG_CM0, + AUD_CG_CM1, + AUD_CG_CM2, + /* AUDIO_TOP_CON1 (0x0004) */ + AUD_CG_HW_GAIN_DL0_DL1, + AUD_CG_HW_GAIN_DL2_DL3, + AUD_CG_HW_GAIN_DL4_DL5, + AUD_CG_HW_GAIN_DL24CH, + AUD_CG_UL0_ADC, + AUD_CG_UL0_TML, + AUD_CG_UL0_ADC_HIRES, + AUD_CG_UL0_ADC_HIRES_TML, + AUD_CG_UL1_ADC, + AUD_CG_UL1_TML, + AUD_CG_UL1_ADC_HIRES, + AUD_CG_UL1_ADC_HIRES_TML, + /* AUDIO_TOP_CON2 (0x0008) */ + AUD_CG_ETDM_OUT0, + AUD_CG_ETDM_OUT5, + AUD_CG_ETDM_IN0, + AUD_CG_ETDM_IN5, + /* AUDIO_TOP_CON3 (0x0008) */ + AUD_CG_GENERAL0_ASRC, + AUD_CG_GENERAL1_ASRC, + AUD_CG_GENERAL2_ASRC, + AUD_CG_GENERAL3_ASRC, + AUD_CG_GENERAL4_ASRC, + AUD_CG_GENERAL5_ASRC, + AUD_CG_GENERAL6_ASRC, + AUD_CG_GENERAL7_ASRC, + /* AUDIO_TOP_CON4 (0x0010) */ + AUD_CG_APLL_TUNER1, + AUD_CG_APLL_TUNER2, + AUD_CG_H208M_CK, + AUD_CG_APLL2_CK, + AUD_CG_APLL1_CK, + AUD_CG_SDW_BEGIN, + AUD_CG_AUDIO_F26M_CK = AUD_CG_SDW_BEGIN, + AUD_CG_AUDIO_HOPPING_CK, + AUD_CG_ASD_END = AUD_CG_AUDIO_HOPPING_CK, + /* AUDIO_TOP_CON5 (0x0070) - SoundWire (owned by dai-soundwire.c) */ + AUD_CG_SOUNDWIRE_DMIC0_ADC, + AUD_CG_SOUNDWIRE_DMIC0_TML, + AUD_CG_SOUNDWIRE_DMIC1_ADC, + AUD_CG_SOUNDWIRE_DMIC1_TML, + AUD_CG_SOUNDWIRE_DMIC2_ADC, + AUD_CG_SOUNDWIRE_DMIC2_TML, + AUD_CG_SOUNDWIRE_DMIC3_ADC, + AUD_CG_SOUNDWIRE_DMIC3_TML, + AUD_CG_SOUNDWIRE_DMIC4_ADC, + AUD_CG_SOUNDWIRE_DMIC4_TML, + AUD_CG_SOUNDWIRE_DMIC5_ADC, + AUD_CG_SOUNDWIRE_DMIC5_TML, + AUD_CG_SOUNDWIRE_DMIC6_ADC, + AUD_CG_SOUNDWIRE_DMIC6_TML, + AUD_CG_SOUNDWIRE_DMIC7_ADC, + AUD_CG_SOUNDWIRE_DMIC7_TML, + AUD_CG_SOUNDWIRE1, + AUD_CG_SOUNDWIRE0, + AUD_CG_SDW_END = AUD_CG_SOUNDWIRE0, + AUD_CG_NUMS, +}; + +enum afe_clk_mux_type { + AFE_CLK_VLP_CKSYS_TOP_AUDIO_H_SEL = 0, + AFE_CLK_VLP_CKSYS_TOP_AUD_ENGEN1_SEL, + AFE_CLK_VLP_CKSYS_TOP_AUD_ENGEN2_SEL, + AFE_CLK_VLP_CKSYS_TOP_AUD_SOUNDWIRE0_SEL, + AFE_CLK_VLP_CKSYS_TOP_AUD_SOUNDWIRE1_SEL, + AFE_CLK_VLP_CKSYS_TOP_AUD_SOUNDWIRE0_PHY_SEL, + AFE_CLK_VLP_CKSYS_TOP_AUD_SOUNDWIRE1_PHY_SEL, + AFE_CLK_VLP_CKSYS_TOP_AUD_INTBUS_SEL, + AFE_CLK_VLP_CKSYS_TOP_AUD_1_SEL, + AFE_CLK_VLP_CKSYS_TOP_AUD_2_SEL, + AFE_CLK_VLP_CKSYS_TOP_APLL_I2S1_MCK_SEL, + AFE_CLK_VLP_CKSYS_TOP_VADSP_SEL, + AFE_CLK_NUMS, +}; + +/* Clock parent names, per audio_clock_interface.h (SSPM firmware header) */ +#define CLK_PARENT_TCX_26M_MX9_CK_NAME "tcx_26m_mx9_ck" +#define CLK_PARENT_OSC_D20_NAME "osc_d20" +#define CLK_PARENT_OSC_D12_NAME "osc_d12" +#define CLK_PARENT_OSC_D10_NAME "osc_d10" +#define CLK_PARENT_OSC_D6_NAME "osc_d6" +#define CLK_PARENT_OSC_D5_NAME "osc_d5" +#define CLK_PARENT_OSC_D2_NAME "osc_d2" +#define CLK_PARENT_AD_OSC_NAME "ad_osc_ck" +#define CLK_PARENT_APLL1_D2_NAME "apll1_d2" +#define CLK_PARENT_APLL2_D2_NAME "apll2_d2" +#define CLK_PARENT_APLL1_D8_NAME "apll1_d8" +#define CLK_PARENT_APLL1_D4_NAME "apll1_d4" +#define CLK_PARENT_APLL2_D8_NAME "apll2_d8" +#define CLK_PARENT_APLL2_D4_NAME "apll2_d4" +#define CLK_PARENT_APLL1_NAME "apll1" +#define CLK_PARENT_APLL2_NAME "apll2" +#define CLK_PARENT_MAINPLL2_D7_D4_NAME "mainpll2_d7_d4" +#define CLK_PARENT_MAINPLL2_D4_D4_NAME "mainpll2_d4_d4" +#define CLK_PARENT_UNIVPLL2_D4_NAME "univpll2_d4" +#define CLK_PARENT_UNIVPLL2_D4_D2_NAME "univpll2_d4_d2" +#define CLK_PARENT_ADSPPLL_NAME "adsppll_ck" +#define CLK_PARENT_ADSPPLL_D2_NAME "adsppll_d2" + +/* SSPM-managed clock/MTCMOS control (via power_wrap SCMI requests) */ +int mt8901_afe_init_clock(struct mtk_base_afe *afe); +int mt8901_afe_disable_default_on_clk_and_top_cg(struct mtk_base_afe *afe); +int mt8901_afe_enable_top_cg(struct mtk_base_afe *afe, unsigned int cg); +int mt8901_afe_disable_top_cg(struct mtk_base_afe *afe, unsigned int cg); +int mt8901_afe_enable_mtcmos(struct mtk_base_afe *afe, unsigned int mtcmos); +int mt8901_afe_disable_mtcmos(struct mtk_base_afe *afe, unsigned int mtcmos); +int mt8901_afe_send_spm_req(struct mtk_base_afe *afe, unsigned int req, + bool enable); +int mt8901_afe_enable_main_clock(struct mtk_base_afe *afe); +int mt8901_afe_disable_main_clock(struct mtk_base_afe *afe); +int mt8901_afe_enable_reg_rw_clk(struct mtk_base_afe *afe); +int mt8901_afe_disable_reg_rw_clk(struct mtk_base_afe *afe); +int mt8901_afe_set_clock_parent(struct mtk_base_afe *afe, + unsigned int mux, const char *parent_name); +int mt8901_afe_enable_apll_top_con_cg(struct mtk_base_afe *afe); +int mt8901_afe_disable_apll_top_con_cg(struct mtk_base_afe *afe); +int mt8901_afe_set_idle_clock_parent(struct mtk_base_afe *afe, bool idle); + +#endif diff --git a/sound/soc/mediatek/mt8901/mt8901-afe-common.h b/sound/soc/mediatek/mt8901/mt8901-afe-common.h new file mode 100644 index 0000000000000..6eb1c7024b599 --- /dev/null +++ b/sound/soc/mediatek/mt8901/mt8901-afe-common.h @@ -0,0 +1,101 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * mt8901-afe-common.h -- MediaTek 8901 audio driver definitions + * + * Copyright (c) 2026 MediaTek Inc. + * Author: Trevor Wu + * Weiyi Hsieh + */ +#ifndef _MT_8901_AFE_COMMON_H_ +#define _MT_8901_AFE_COMMON_H_ + +#include +#include +#include +#include "../common/mtk-base-afe.h" + +enum { + MT8901_DAI_START, + MT8901_AFE_MEMIF_START = MT8901_DAI_START, + MT8901_AFE_MEMIF_DL0 = MT8901_AFE_MEMIF_START, + MT8901_AFE_MEMIF_DL1, + MT8901_AFE_MEMIF_DL2, + MT8901_AFE_MEMIF_DL3, + MT8901_AFE_MEMIF_DL4, + MT8901_AFE_MEMIF_DL5, + MT8901_AFE_MEMIF_DL_24CH_0, + MT8901_AFE_MEMIF_UL_START, + MT8901_AFE_MEMIF_UL0 = MT8901_AFE_MEMIF_UL_START, + MT8901_AFE_MEMIF_UL1, + MT8901_AFE_MEMIF_UL2, + MT8901_AFE_MEMIF_UL3, + MT8901_AFE_MEMIF_UL4, + MT8901_AFE_MEMIF_UL5, + MT8901_AFE_MEMIF_VUL_CM0, + MT8901_AFE_MEMIF_VUL_CM1, + MT8901_AFE_MEMIF_VUL_CM2, + MT8901_AFE_MEMIF_END, + MT8901_AFE_MEMIF_NUM = (MT8901_AFE_MEMIF_END - MT8901_AFE_MEMIF_START), + MT8901_DAI_END = MT8901_AFE_MEMIF_END, + MT8901_DAI_NUM = (MT8901_DAI_END - MT8901_DAI_START), +}; + +enum { + MT8901_AFE_IRQ_0 = 0, + MT8901_AFE_IRQ_1, + MT8901_AFE_IRQ_2, + MT8901_AFE_IRQ_3, + MT8901_AFE_IRQ_4, + MT8901_AFE_IRQ_5, + MT8901_AFE_IRQ_6, + MT8901_AFE_IRQ_7, + MT8901_AFE_IRQ_8, + MT8901_AFE_IRQ_9, + MT8901_AFE_IRQ_10, + MT8901_AFE_IRQ_11, + MT8901_AFE_IRQ_12, + MT8901_AFE_IRQ_13, + MT8901_AFE_IRQ_14, + MT8901_AFE_IRQ_15, + MT8901_AFE_IRQ_16, + MT8901_AFE_IRQ_17, + MT8901_AFE_IRQ_18, + MT8901_AFE_IRQ_19, + MT8901_AFE_IRQ_20, + MT8901_AFE_IRQ_21, + MT8901_AFE_IRQ_NUM, +}; + +enum { + MT8901_DOMAIN_HOPPING = 0, + MT8901_DOMAIN_APLL, +}; + +struct mtk_dai_memif_irq_priv { + unsigned int asys_timing_sel; +}; + +struct mt8901_afe_private { + u32 hw_ver; /* acpi-asd-hw-ver from _DSD */ + struct mtk_dai_memif_irq_priv irq_priv[MT8901_AFE_IRQ_NUM]; + int pm_runtime_bypass_reg_ctl; + bool default_mtcmos_vote_dropped; + + /* dai */ + void *dai_priv[MT8901_DAI_NUM]; +}; + +int mt8901_afe_fs_timing(struct device *dev, unsigned int rate); +/* dai register */ +int mt8901_dai_soundwire_register(struct mtk_base_afe *afe); + +#define MT8901_SOC_ENUM_EXT(xname, xenum, xhandler_get, xhandler_put, id) \ +{ \ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ + .info = snd_soc_info_enum_double, \ + .get = xhandler_get, .put = xhandler_put, \ + .device = id, \ + .private_value = (unsigned long)&(xenum), \ +} + +#endif diff --git a/sound/soc/mediatek/mt8901/mt8901-afe-pcm.c b/sound/soc/mediatek/mt8901/mt8901-afe-pcm.c new file mode 100644 index 0000000000000..c1419bd3c167a --- /dev/null +++ b/sound/soc/mediatek/mt8901/mt8901-afe-pcm.c @@ -0,0 +1,2208 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * MediaTek ALSA SoC AFE platform driver for 8901 + * + * Copyright (c) 2026 MediaTek Inc. + * Author: Trevor Wu + * Weiyi Hsieh + */ + +#include +#include +#include +#include +#include +#include +#include "mt8901-afe-common.h" +#include "mt8901-afe-clk.h" +#include "mt8901-reg.h" +#include "../common/mtk-afe-platform-driver.h" +#include "../common/mtk-afe-fe-dai.h" + +#define MICROSECONDS_PER_SECOND (1000000UL) +#define STEREO_CH 2 +#define CLK_RATE_26M 26000000 +#define CM_UPDATE_CNT_OFFSET 10 + +/* hardware constants */ +#define MT8901_DMA_ADDR_BITS 33 //ToDo: check 40bits +#define MT8901_MEMIF_MINLEN 64 +#define MT8901_MEMIF_MAXLEN 64 +#define MT8901_CM0_MAX_CH 8 +#define MT8901_CM1_MAX_CH 16 +/* CM2 exposes 16 wired inputs (O064-O079 <- I170-I185), not the HW max 32 */ +#define MT8901_CM2_MAX_CH 16 +#define MT8901_MEMIF_BUFFER_BYTES_ALIGN (0x10) + +/* experience values */ +#define IO_INTF_DELAY_IN_SAMPLES 3 +#define SERIAL_TO_PARALLEL_DELAY_IN_SAMPLES 1 +#define CHANNEL_MERGE_DELAY_IN_SAMPLES 1 +#define INTERCONN_DELAY_IN_SAMPLES 1 + +#define MTK_PCM_RATES (SNDRV_PCM_RATE_8000_48000 |\ + SNDRV_PCM_RATE_88200 |\ + SNDRV_PCM_RATE_96000 |\ + SNDRV_PCM_RATE_176400 |\ + SNDRV_PCM_RATE_192000 |\ + SNDRV_PCM_RATE_352800 |\ + SNDRV_PCM_RATE_384000) + +/* + * The AFE data path is 24-bit internally: S16 and S24 (24 valid bits in + * a 32-bit container) play clean, but true 32-bit memif mode produces + * distorted output on hardware regardless of the HD/align settings, so + * S32 is deliberately not offered (matches the reference driver, which + * only uses the 16/24-bit memif modes). + */ +#define MTK_PCM_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\ + SNDRV_PCM_FMTBIT_S24_LE) + +struct mtk_dai_memif_priv { + unsigned int fs_timing; + unsigned int domain; + unsigned int minlen; + unsigned int maxlen; +}; + +static const struct snd_pcm_hardware mt8901_afe_hardware = { + .info = SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_NO_PERIOD_WAKEUP, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE, + .period_bytes_min = 64, + .period_bytes_max = 256 * 1024, + .periods_min = 2, + .periods_max = 256, + .buffer_bytes_max = 256 * 2 * 1024, +}; + +struct mt8901_afe_rate { + unsigned int rate; + unsigned int reg_value; +}; + +static const struct mt8901_afe_rate mt8901_afe_rates[] = { + { .rate = 8000, .reg_value = 0, }, + { .rate = 11025, .reg_value = 1, }, + { .rate = 12000, .reg_value = 2, }, + { .rate = 16000, .reg_value = 4, }, + { .rate = 22050, .reg_value = 5, }, + { .rate = 24000, .reg_value = 6, }, + { .rate = 32000, .reg_value = 8, }, + { .rate = 44100, .reg_value = 9, }, + { .rate = 48000, .reg_value = 10, }, + { .rate = 88200, .reg_value = 13, }, + { .rate = 96000, .reg_value = 14, }, + { .rate = 176400, .reg_value = 17, }, + { .rate = 192000, .reg_value = 18, }, + { .rate = 352800, .reg_value = 21, }, + { .rate = 384000, .reg_value = 22, }, +}; + +int mt8901_afe_fs_timing(struct device *dev, unsigned int rate) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(mt8901_afe_rates); i++) + if (mt8901_afe_rates[i].rate == rate) + return mt8901_afe_rates[i].reg_value; + + dev_err(dev, "rate %u invalid!!!\n", rate); + + return -EINVAL; +} + +static int mt8901_memif_fs(struct snd_pcm_substream *substream, + unsigned int rate) +{ + struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); + struct snd_soc_component *component = NULL; + struct mtk_base_afe *afe = NULL; + + component = snd_soc_rtdcom_lookup(rtd, AFE_PCM_NAME); + if (!component) + return -EINVAL; + + afe = snd_soc_component_get_drvdata(component); + if (!afe) + return -EINVAL; + + return mt8901_afe_fs_timing(afe->dev, rate); +} + +static int mt8901_irq_fs(struct snd_pcm_substream *substream, + unsigned int rate) +{ + return mt8901_memif_fs(substream, rate); +} + +enum { + MT8901_AFE_CM0, + MT8901_AFE_CM1, + MT8901_AFE_CM2, + MT8901_AFE_CM_NUM, +}; + +struct mt8901_afe_channel_merge { + int id; + int reg; + unsigned int sel_shift; + unsigned int sel_maskbit; + unsigned int sel_default; + unsigned int ch_num_shift; + unsigned int ch_num_maskbit; + unsigned int en_shift; + unsigned int en_maskbit; + unsigned int update_cnt_shift; + unsigned int update_cnt_maskbit; + unsigned int update_cnt_default; + unsigned int domain_shift; + unsigned int domain_maskbit; + unsigned int fs_shift; + unsigned int fs_maskbit; + unsigned int output_mux_shift; + unsigned int output_mux_maskbit; +}; + +static const struct mt8901_afe_channel_merge + mt8901_afe_cm[MT8901_AFE_CM_NUM] = { + [MT8901_AFE_CM0] = { + .id = MT8901_AFE_CM0, + .reg = AFE_CM0_CON0, + .sel_shift = 31, + .sel_maskbit = 0x1, + .sel_default = 0, + .ch_num_shift = 2, + .ch_num_maskbit = 0x1f, + .en_shift = 0, + .en_maskbit = 0x1, + .update_cnt_shift = 16, + .update_cnt_maskbit = 0x7fff, + .update_cnt_default = 0x6, + .domain_shift = 13, + .domain_maskbit = 0x7, + .fs_shift = 8, + .fs_maskbit = 0x1f, + .output_mux_shift = 7, + .output_mux_maskbit = 0x1, + }, + [MT8901_AFE_CM1] = { + .id = MT8901_AFE_CM1, + .reg = AFE_CM1_CON0, + .sel_shift = 31, + .sel_maskbit = 0x1, + .sel_default = 0, + .ch_num_shift = 2, + .ch_num_maskbit = 0x1f, + .en_shift = 0, + .en_maskbit = 0x1, + .update_cnt_shift = 16, + .update_cnt_maskbit = 0x7fff, + .update_cnt_default = 0x6, + .domain_shift = 13, + .domain_maskbit = 0x7, + .fs_shift = 8, + .fs_maskbit = 0x1f, + .output_mux_shift = 7, + .output_mux_maskbit = 0x1, + }, + [MT8901_AFE_CM2] = { + .id = MT8901_AFE_CM2, + .reg = AFE_CM2_CON0, + .sel_shift = 31, + .sel_maskbit = 0x1, + .sel_default = 0, + .ch_num_shift = 2, + .ch_num_maskbit = 0x1f, + .en_shift = 0, + .en_maskbit = 0x1, + .update_cnt_shift = 16, + .update_cnt_maskbit = 0x7fff, + .update_cnt_default = 0x6, + .domain_shift = 13, + .domain_maskbit = 0x7, + .fs_shift = 8, + .fs_maskbit = 0x1f, + .output_mux_shift = 7, + .output_mux_maskbit = 0x1, + }, +}; + +static int mt8901_afe_memif_is_ul(int id) +{ + if (id >= MT8901_AFE_MEMIF_UL_START && id < MT8901_AFE_MEMIF_END) + return 1; + else + return 0; +} + +static const struct mt8901_afe_channel_merge * + mt8901_afe_found_cm(struct snd_soc_dai *dai) +{ + struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai); + int id = -EINVAL; + + if (mt8901_afe_memif_is_ul(dai->id) == 0) + return NULL; + + switch (dai->id) { + case MT8901_AFE_MEMIF_VUL_CM0: + id = MT8901_AFE_CM0; + break; + case MT8901_AFE_MEMIF_VUL_CM1: + id = MT8901_AFE_CM1; + break; + case MT8901_AFE_MEMIF_VUL_CM2: + id = MT8901_AFE_CM2; + break; + default: + break; + } + + if (id < 0) { + dev_dbg(afe->dev, "%s, memif %d cannot find CM!\n", + __func__, dai->id); + return NULL; + } + + return &mt8901_afe_cm[id]; +} + +static unsigned int mt8901_afe_cm_derive_update_cnt(unsigned int rate, + unsigned int channels) +{ + unsigned int cnt; + + if (!rate || !channels) + return 0; + + /* ((26M/fs) - 10) / ceil(ch/2) - 1 */ + cnt = CLK_RATE_26M / rate; + if (cnt <= CM_UPDATE_CNT_OFFSET) + return 0; + + cnt = (cnt - CM_UPDATE_CNT_OFFSET) / ((channels + 1) / 2); + if (cnt <= 1) + return 0; + + return cnt - 1; +} + +static int mt8901_afe_config_cm(struct mtk_base_afe *afe, + const struct mt8901_afe_channel_merge *cm, + unsigned int channels, unsigned int rate, + unsigned int domain) +{ + unsigned int update_cnt; + int fs_index; + + if (!cm) + return -EINVAL; + + fs_index = mt8901_afe_fs_timing(afe->dev, rate); + if (fs_index < 0) + return fs_index; + + regmap_update_bits(afe->regmap, + cm->reg, + cm->sel_maskbit << cm->sel_shift, + cm->sel_default << cm->sel_shift); + + regmap_update_bits(afe->regmap, + cm->reg, + cm->ch_num_maskbit << cm->ch_num_shift, + (channels - 1) << cm->ch_num_shift); + + update_cnt = mt8901_afe_cm_derive_update_cnt(rate, channels); + regmap_update_bits(afe->regmap, + cm->reg, + cm->update_cnt_maskbit << cm->update_cnt_shift, + update_cnt << cm->update_cnt_shift); + + regmap_update_bits(afe->regmap, + cm->reg, + cm->domain_maskbit << cm->domain_shift, + domain << cm->domain_shift); + + regmap_update_bits(afe->regmap, + cm->reg, + cm->fs_maskbit << cm->fs_shift, + (unsigned int)fs_index << cm->fs_shift); + + return 0; +} + +static int mt8901_afe_enable_cm(struct mtk_base_afe *afe, + const struct mt8901_afe_channel_merge *cm, + bool enable) +{ + if (!cm) + return -EINVAL; + + regmap_update_bits(afe->regmap, + cm->reg, + cm->en_maskbit << cm->en_shift, + enable << cm->en_shift); + + return 0; +} + +struct mt8901_afe_memif_minlen { + unsigned int len; + unsigned int reg_value; +}; + +static const struct mt8901_afe_memif_minlen mt8901_memif_minlen[] = { + { .len = 0, .reg_value = 0, }, + { .len = 16, .reg_value = 1, }, + { .len = 32, .reg_value = 2, }, + { .len = 64, .reg_value = 3, }, +}; + +static int mt8901_memif_set_minlen(struct mtk_base_afe *afe, + int id, unsigned int len) +{ + int i, value = 0; + struct mtk_base_afe_memif *memif = &afe->memif[id]; + const struct mtk_base_memif_data *data = memif->data; + + if (data->minlen_reg < 0) + return 0; + + for (i = 0; i < ARRAY_SIZE(mt8901_memif_minlen); i++) + if (mt8901_memif_minlen[i].len == len) + value = mt8901_memif_minlen[i].reg_value; + + regmap_update_bits(afe->regmap, data->minlen_reg, + data->minlen_mask << data->minlen_shift, + value << data->minlen_shift); + return 0; +} + +struct mt8901_afe_memif_maxlen { + unsigned int len; + unsigned int reg_value; +}; + +static const struct mt8901_afe_memif_maxlen mt8901_memif_maxlen[] = { + { .len = 4, .reg_value = 0, }, + { .len = 16, .reg_value = 1, }, + { .len = 32, .reg_value = 2, }, + { .len = 64, .reg_value = 3, }, +}; + +static int mt8901_memif_set_maxlen(struct mtk_base_afe *afe, + int id, unsigned int len) +{ + int i, value = 0; + struct mtk_base_afe_memif *memif = &afe->memif[id]; + const struct mtk_base_memif_data *data = memif->data; + + if (data->maxlen_reg < 0) + return 0; + + for (i = 0; i < ARRAY_SIZE(mt8901_memif_maxlen); i++) + if (mt8901_memif_maxlen[i].len == len) + value = mt8901_memif_maxlen[i].reg_value; + + regmap_update_bits(afe->regmap, data->maxlen_reg, + data->maxlen_mask << data->maxlen_shift, + value << data->maxlen_shift); + + return 0; +} + +static int mt8901_memif_set_domain(struct mtk_base_afe *afe, + int id, unsigned int domain) +{ + struct mtk_base_afe_memif *memif = &afe->memif[id]; + const struct mtk_base_memif_data *data = memif->data; + + if (data->domain_reg >= 0) + regmap_update_bits(afe->regmap, data->domain_reg, + data->domain_mask << data->domain_shift, + domain << data->domain_shift); + + return 0; +} + +static void mt8901_afe_irq_clear(struct mtk_base_afe *afe, + const struct mtk_base_irq_data *irq_data) +{ + unsigned int val = 0; + + regmap_read(afe->regmap, irq_data->irq_clr_reg, &val); + regmap_update_bits(afe->regmap, irq_data->irq_clr_reg, + BIT(irq_data->irq_clr_shift), + ~val & BIT(irq_data->irq_clr_shift)); +} + +static void mt8901_afe_irq_enable(struct mtk_base_afe *afe, + const struct mtk_base_irq_data *irq_data) +{ + if (irq_data->irq_mask_reg >= 0) + regmap_set_bits(afe->regmap, irq_data->irq_mask_reg, + BIT(irq_data->irq_mask_shift)); + + if (irq_data->irq_en_reg >= 0) + regmap_set_bits(afe->regmap, irq_data->irq_en_reg, + BIT(irq_data->irq_en_shift)); +} + +static void mt8901_afe_irq_disable(struct mtk_base_afe *afe, + const struct mtk_base_irq_data *irq_data) +{ + if (irq_data->irq_en_reg >= 0) + regmap_clear_bits(afe->regmap, irq_data->irq_en_reg, + BIT(irq_data->irq_en_shift)); + + /* clear pending IRQ */ + mt8901_afe_irq_clear(afe, irq_data); + + if (irq_data->irq_mask_reg >= 0) + regmap_clear_bits(afe->regmap, irq_data->irq_mask_reg, + BIT(irq_data->irq_mask_shift)); +} + +static void mt8901_afe_irq_configure(struct mtk_base_afe *afe, int id, + int fs, unsigned int domain, + unsigned int counter) +{ + struct mtk_base_afe_memif *memif = &afe->memif[id]; + struct mtk_base_afe_irq *irqs = &afe->irqs[memif->irq_usage]; + const struct mtk_base_irq_data *irq_data = irqs->irq_data; + int msk, shift; + + /* set irq counter */ + msk = irq_data->irq_cnt_maskbit; + shift = irq_data->irq_cnt_shift; + regmap_update_bits(afe->regmap, irq_data->irq_cnt_reg, + msk << shift, counter << shift); + + if (irq_data->irq_fs_reg >= 0) { + msk = irq_data->irq_fs_maskbit; + shift = irq_data->irq_fs_shift; + regmap_update_bits(afe->regmap, irq_data->irq_fs_reg, + msk << shift, fs << shift); + } + + if (irq_data->irq_domain_reg >= 0) { + msk = irq_data->irq_domain_maskbit; + shift = irq_data->irq_domain_shift; + regmap_update_bits(afe->regmap, irq_data->irq_domain_reg, + msk << shift, domain << shift); + } +} + +static unsigned long mt8901_get_prefetch_delay(struct mtk_base_afe *afe, + int id, unsigned int minlen, + unsigned int rate, + unsigned int channel, + unsigned int bitwidth) +{ + unsigned long delay_us = 0; + unsigned long pbuf_delay; + + if (rate == 0 || channel == 0 || bitwidth == 0) { + dev_warn(afe->dev, + "%s(), unexpected parameter rate %u, ch %u, bit %u\n", + __func__, rate, channel, bitwidth); + return 0; + } + + delay_us = IO_INTF_DELAY_IN_SAMPLES + + SERIAL_TO_PARALLEL_DELAY_IN_SAMPLES + + CHANNEL_MERGE_DELAY_IN_SAMPLES + + INTERCONN_DELAY_IN_SAMPLES; + + pbuf_delay = minlen * BITS_PER_BYTE / (channel * bitwidth); + delay_us += pbuf_delay; + delay_us = delay_us * MICROSECONDS_PER_SECOND / rate; + + return delay_us; +} + +static int mt8901_afe_fe_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + int ret; + + ret = mtk_afe_fe_startup(substream, dai); + + snd_pcm_hw_constraint_step(runtime, 0, + SNDRV_PCM_HW_PARAM_BUFFER_BYTES, + MT8901_MEMIF_BUFFER_BYTES_ALIGN); + + return ret; +} + +static int mt8901_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 mt8901_afe_private *afe_priv = afe->platform_priv; + int id = snd_soc_rtd_to_cpu(rtd, 0)->id; + const struct mt8901_afe_channel_merge *cm = mt8901_afe_found_cm(dai); + struct mtk_dai_memif_priv *memif_priv = afe_priv->dai_priv[id]; + unsigned int channels = params_channels(params); + unsigned int rate = params_rate(params); + unsigned int domain = memif_priv->domain; + + mt8901_afe_config_cm(afe, cm, channels, rate, domain); + + mt8901_memif_set_domain(afe, id, domain); + + mt8901_memif_set_minlen(afe, id, memif_priv->minlen); + + mt8901_memif_set_maxlen(afe, id, memif_priv->maxlen); + + return mtk_afe_fe_hw_params(substream, params, dai); +} + +static int mt8901_afe_fe_trigger(struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *dai) +{ + struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai); + const struct mt8901_afe_channel_merge *cm = mt8901_afe_found_cm(dai); + struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); + struct snd_pcm_runtime * const runtime = substream->runtime; + int id = snd_soc_rtd_to_cpu(rtd, 0)->id; + struct mtk_base_afe_memif *memif = &afe->memif[id]; + struct mtk_base_afe_irq *irqs = &afe->irqs[memif->irq_usage]; + const struct mtk_base_irq_data *irq_data = irqs->irq_data; + unsigned int counter = runtime->period_size; + struct mt8901_afe_private *afe_priv = afe->platform_priv; + struct mtk_dai_memif_priv *memif_priv = afe_priv->dai_priv[id]; + int fs; + int ret; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + ret = mtk_memif_set_enable(afe, id); + if (ret) { + dev_err(afe->dev, + "%s(), error, id %d, memif enable, ret %d\n", + __func__, id, ret); + return ret; + } + + mt8901_afe_enable_cm(afe, cm, true); + + /* set irq fs */ + fs = afe->irq_fs(substream, runtime->rate); + if (fs < 0) + return -EINVAL; + + mt8901_afe_irq_configure(afe, id, fs, memif_priv->domain, + counter); + + /* delay for uplink */ + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { + unsigned long prefetch_delay = + mt8901_get_prefetch_delay(afe, id, + memif_priv->minlen, + runtime->rate, + runtime->channels, + runtime->sample_bits); + + udelay(prefetch_delay); + } + + /* enable interrupt */ + mt8901_afe_irq_enable(afe, irq_data); + return 0; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + mt8901_afe_enable_cm(afe, cm, false); + + ret = mtk_memif_set_disable(afe, id); + if (ret) + dev_err(afe->dev, "%s(), error, id %d, memif enable, ret %d\n", + __func__, id, ret); + + /* disable interrupt */ + mt8901_afe_irq_disable(afe, irq_data); + return ret; + default: + return -EINVAL; + } +} + +static const struct snd_soc_dai_ops mt8901_afe_fe_dai_ops = { + .startup = mt8901_afe_fe_startup, + .shutdown = mtk_afe_fe_shutdown, + .hw_params = mt8901_afe_fe_hw_params, + .hw_free = mtk_afe_fe_hw_free, + .prepare = mtk_afe_fe_prepare, + .trigger = mt8901_afe_fe_trigger, +}; + +static struct snd_soc_dai_driver mt8901_memif_dai_driver[] = { + /* FE DAIs: memory intefaces to CPU */ + { + .name = "DL0", + .id = MT8901_AFE_MEMIF_DL0, + .playback = { + .stream_name = "DL0", + .channels_min = 1, + .channels_max = STEREO_CH, + .rates = MTK_PCM_RATES, + .formats = MTK_PCM_FORMATS, + }, + .ops = &mt8901_afe_fe_dai_ops, + }, + { + .name = "DL1", + .id = MT8901_AFE_MEMIF_DL1, + .playback = { + .stream_name = "DL1", + .channels_min = 1, + .channels_max = STEREO_CH, + .rates = MTK_PCM_RATES, + .formats = MTK_PCM_FORMATS, + }, + .ops = &mt8901_afe_fe_dai_ops, + }, + { + .name = "DL2", + .id = MT8901_AFE_MEMIF_DL2, + .playback = { + .stream_name = "DL2", + .channels_min = 1, + .channels_max = STEREO_CH, + .rates = MTK_PCM_RATES, + .formats = MTK_PCM_FORMATS, + }, + .ops = &mt8901_afe_fe_dai_ops, + }, + { + .name = "DL3", + .id = MT8901_AFE_MEMIF_DL3, + .playback = { + .stream_name = "DL3", + .channels_min = 1, + .channels_max = STEREO_CH, + .rates = MTK_PCM_RATES, + .formats = MTK_PCM_FORMATS, + }, + .ops = &mt8901_afe_fe_dai_ops, + }, + { + .name = "DL4", + .id = MT8901_AFE_MEMIF_DL4, + .playback = { + .stream_name = "DL4", + .channels_min = 1, + .channels_max = STEREO_CH, + .rates = MTK_PCM_RATES, + .formats = MTK_PCM_FORMATS, + }, + .ops = &mt8901_afe_fe_dai_ops, + }, + { + .name = "DL5", + .id = MT8901_AFE_MEMIF_DL5, + .playback = { + .stream_name = "DL5", + .channels_min = 1, + .channels_max = STEREO_CH, + .rates = MTK_PCM_RATES, + .formats = MTK_PCM_FORMATS, + }, + .ops = &mt8901_afe_fe_dai_ops, + }, + { + .name = "DL_24CH", + .id = MT8901_AFE_MEMIF_DL_24CH_0, + .playback = { + .stream_name = "DL_24CH", + .channels_min = 1, + .channels_max = MT8901_CM0_MAX_CH, + .rates = MTK_PCM_RATES, + .formats = MTK_PCM_FORMATS, + }, + .ops = &mt8901_afe_fe_dai_ops, + }, + { + .name = "UL0", + .id = MT8901_AFE_MEMIF_UL0, + .capture = { + .stream_name = "UL0", + .channels_min = 1, + .channels_max = STEREO_CH, + .rates = MTK_PCM_RATES, + .formats = MTK_PCM_FORMATS, + }, + .ops = &mt8901_afe_fe_dai_ops, + }, + { + .name = "UL1", + .id = MT8901_AFE_MEMIF_UL1, + .capture = { + .stream_name = "UL1", + .channels_min = 1, + .channels_max = STEREO_CH, + .rates = MTK_PCM_RATES, + .formats = MTK_PCM_FORMATS, + }, + .ops = &mt8901_afe_fe_dai_ops, + }, + { + .name = "UL2", + .id = MT8901_AFE_MEMIF_UL2, + .capture = { + .stream_name = "UL2", + .channels_min = 1, + .channels_max = STEREO_CH, + .rates = MTK_PCM_RATES, + .formats = MTK_PCM_FORMATS, + }, + .ops = &mt8901_afe_fe_dai_ops, + }, + { + .name = "UL3", + .id = MT8901_AFE_MEMIF_UL3, + .capture = { + .stream_name = "UL3", + .channels_min = 1, + .channels_max = STEREO_CH, + .rates = MTK_PCM_RATES, + .formats = MTK_PCM_FORMATS, + }, + .ops = &mt8901_afe_fe_dai_ops, + }, + { + .name = "UL4", + .id = MT8901_AFE_MEMIF_UL4, + .capture = { + .stream_name = "UL4", + .channels_min = 1, + .channels_max = STEREO_CH, + .rates = MTK_PCM_RATES, + .formats = MTK_PCM_FORMATS, + }, + .ops = &mt8901_afe_fe_dai_ops, + }, + { + .name = "UL5", + .id = MT8901_AFE_MEMIF_UL5, + .capture = { + .stream_name = "UL5", + .channels_min = 1, + .channels_max = STEREO_CH, + .rates = MTK_PCM_RATES, + .formats = MTK_PCM_FORMATS, + }, + .ops = &mt8901_afe_fe_dai_ops, + }, + { + .name = "VUL_CM0", + .id = MT8901_AFE_MEMIF_VUL_CM0, + .capture = { + .stream_name = "VUL_CM0", + .channels_min = 1, + .channels_max = MT8901_CM0_MAX_CH, + .rates = MTK_PCM_RATES, + .formats = MTK_PCM_FORMATS, + }, + .ops = &mt8901_afe_fe_dai_ops, + }, + { + .name = "VUL_CM1", + .id = MT8901_AFE_MEMIF_VUL_CM1, + .capture = { + .stream_name = "VUL_CM1", + .channels_min = 1, + .channels_max = MT8901_CM1_MAX_CH, + .rates = MTK_PCM_RATES, + .formats = MTK_PCM_FORMATS, + }, + .ops = &mt8901_afe_fe_dai_ops, + }, + { + .name = "VUL_CM2", + .id = MT8901_AFE_MEMIF_VUL_CM2, + .capture = { + .stream_name = "VUL_CM2", + .channels_min = 1, + .channels_max = MT8901_CM2_MAX_CH, + .rates = MTK_PCM_RATES, + .formats = MTK_PCM_FORMATS, + }, + .ops = &mt8901_afe_fe_dai_ops, + }, +}; + +/* + * VUL_2CH_0: Mic 2CH + * first-fit: PDI8 (I170/I171); contention: PDI12 (I178/I179), + * PDI13 (I180/I181) + */ +static const struct snd_kcontrol_new o018_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I170 Switch", AFE_CONN018_5, 10, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I178 Switch", AFE_CONN018_5, 18, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I180 Switch", AFE_CONN018_5, 20, 1, 0), +}; + +static const struct snd_kcontrol_new o019_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I171 Switch", AFE_CONN019_5, 11, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I179 Switch", AFE_CONN019_5, 19, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I181 Switch", AFE_CONN019_5, 21, 1, 0), +}; + +/* + * VUL_2CH_1: HeadsetMic 2CH + * first-fit: PDI7 (I168/I169); contention: PDI12~14 (I178~I183) + */ +static const struct snd_kcontrol_new o020_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I168 Switch", AFE_CONN020_5, 8, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I178 Switch", AFE_CONN020_5, 18, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I180 Switch", AFE_CONN020_5, 20, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I182 Switch", AFE_CONN020_5, 22, 1, 0), +}; + +static const struct snd_kcontrol_new o021_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I169 Switch", AFE_CONN021_5, 9, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I179 Switch", AFE_CONN021_5, 19, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I181 Switch", AFE_CONN021_5, 21, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I183 Switch", AFE_CONN021_5, 23, 1, 0), +}; + +/* VUL_2CH_4: BRA RX SDW0 — I186/I187 exclusive non-SDCA path */ +static const struct snd_kcontrol_new o026_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I186 Switch", AFE_CONN026_5, 26, 1, 0), +}; + +static const struct snd_kcontrol_new o027_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I187 Switch", AFE_CONN027_5, 27, 1, 0), +}; + +/* VUL_2CH_5: BRA RX SDW1 — I166/I167 exclusive non-SDCA path */ +static const struct snd_kcontrol_new o028_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I166 Switch", AFE_CONN028_5, 6, 1, 0), +}; + +static const struct snd_kcontrol_new o029_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I167 Switch", AFE_CONN029_5, 7, 1, 0), +}; + +/* + * VUL_CM2: Mic >2CH, max 16CH (PDI8~PDI15, I170~I185). + * O064~O079 each connect to one I port (no contention fallback in CM path). + */ +static const struct snd_kcontrol_new o064_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I170 Switch", AFE_CONN064_5, 10, 1, 0), +}; + +static const struct snd_kcontrol_new o065_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I171 Switch", AFE_CONN065_5, 11, 1, 0), +}; + +static const struct snd_kcontrol_new o066_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I172 Switch", AFE_CONN066_5, 12, 1, 0), +}; + +static const struct snd_kcontrol_new o067_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I173 Switch", AFE_CONN067_5, 13, 1, 0), +}; + +static const struct snd_kcontrol_new o068_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I174 Switch", AFE_CONN068_5, 14, 1, 0), +}; + +static const struct snd_kcontrol_new o069_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I175 Switch", AFE_CONN069_5, 15, 1, 0), +}; + +static const struct snd_kcontrol_new o070_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I176 Switch", AFE_CONN070_5, 16, 1, 0), +}; + +static const struct snd_kcontrol_new o071_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I177 Switch", AFE_CONN071_5, 17, 1, 0), +}; + +static const struct snd_kcontrol_new o072_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I178 Switch", AFE_CONN072_5, 18, 1, 0), +}; + +static const struct snd_kcontrol_new o073_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I179 Switch", AFE_CONN073_5, 19, 1, 0), +}; + +static const struct snd_kcontrol_new o074_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I180 Switch", AFE_CONN074_5, 20, 1, 0), +}; + +static const struct snd_kcontrol_new o075_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I181 Switch", AFE_CONN075_5, 21, 1, 0), +}; + +static const struct snd_kcontrol_new o076_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I182 Switch", AFE_CONN076_5, 22, 1, 0), +}; + +static const struct snd_kcontrol_new o077_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I183 Switch", AFE_CONN077_5, 23, 1, 0), +}; + +static const struct snd_kcontrol_new o078_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I184 Switch", AFE_CONN078_5, 24, 1, 0), +}; + +static const struct snd_kcontrol_new o079_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I185 Switch", AFE_CONN079_5, 25, 1, 0), +}; + +static const char * const mt8901_afe_domain_sel_text[] = { + "hopping", "apll", +}; + +static const unsigned int mt8901_afe_domain_sel_values[] = { + MT8901_DOMAIN_HOPPING, MT8901_DOMAIN_APLL, +}; + +static int mt8901_memif_domain_sel_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct mtk_base_afe *afe = snd_soc_component_get_drvdata(component); + struct mt8901_afe_private *afe_priv = afe->platform_priv; + unsigned int dai_id = kcontrol->id.device; + struct mtk_dai_memif_priv *priv = afe_priv->dai_priv[dai_id]; + int i; + + for (i = 0; i < ARRAY_SIZE(mt8901_afe_domain_sel_values); i++) { + if (mt8901_afe_domain_sel_values[i] == priv->domain) { + ucontrol->value.enumerated.item[0] = i; + return 0; + } + } + return -EINVAL; +} + +static int mt8901_memif_domain_sel_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct mtk_base_afe *afe = snd_soc_component_get_drvdata(component); + struct mt8901_afe_private *afe_priv = afe->platform_priv; + unsigned int dai_id = kcontrol->id.device; + struct mtk_dai_memif_priv *priv = afe_priv->dai_priv[dai_id]; + unsigned long idx = ucontrol->value.enumerated.item[0]; + unsigned int reg_val; + + if (idx >= ARRAY_SIZE(mt8901_afe_domain_sel_values)) + return -EINVAL; + reg_val = mt8901_afe_domain_sel_values[idx]; + if (reg_val == priv->domain) + return 0; + priv->domain = reg_val; + return 1; +} + +static SOC_ENUM_SINGLE_EXT_DECL(mt8901_domain_sel_enum, + mt8901_afe_domain_sel_text); + +static const struct snd_kcontrol_new mt8901_memif_controls[] = { + MT8901_SOC_ENUM_EXT("DL0_Domain_Sel", mt8901_domain_sel_enum, + mt8901_memif_domain_sel_get, + mt8901_memif_domain_sel_put, + MT8901_AFE_MEMIF_DL0), + MT8901_SOC_ENUM_EXT("DL1_Domain_Sel", mt8901_domain_sel_enum, + mt8901_memif_domain_sel_get, + mt8901_memif_domain_sel_put, + MT8901_AFE_MEMIF_DL1), + MT8901_SOC_ENUM_EXT("DL2_Domain_Sel", mt8901_domain_sel_enum, + mt8901_memif_domain_sel_get, + mt8901_memif_domain_sel_put, + MT8901_AFE_MEMIF_DL2), + MT8901_SOC_ENUM_EXT("DL3_Domain_Sel", mt8901_domain_sel_enum, + mt8901_memif_domain_sel_get, + mt8901_memif_domain_sel_put, + MT8901_AFE_MEMIF_DL3), + MT8901_SOC_ENUM_EXT("DL4_Domain_Sel", mt8901_domain_sel_enum, + mt8901_memif_domain_sel_get, + mt8901_memif_domain_sel_put, + MT8901_AFE_MEMIF_DL4), + MT8901_SOC_ENUM_EXT("DL5_Domain_Sel", mt8901_domain_sel_enum, + mt8901_memif_domain_sel_get, + mt8901_memif_domain_sel_put, + MT8901_AFE_MEMIF_DL5), + MT8901_SOC_ENUM_EXT("DL_24CH_Domain_Sel", mt8901_domain_sel_enum, + mt8901_memif_domain_sel_get, + mt8901_memif_domain_sel_put, + MT8901_AFE_MEMIF_DL_24CH_0), + MT8901_SOC_ENUM_EXT("UL0_Domain_Sel", mt8901_domain_sel_enum, + mt8901_memif_domain_sel_get, + mt8901_memif_domain_sel_put, + MT8901_AFE_MEMIF_UL0), + MT8901_SOC_ENUM_EXT("UL1_Domain_Sel", mt8901_domain_sel_enum, + mt8901_memif_domain_sel_get, + mt8901_memif_domain_sel_put, + MT8901_AFE_MEMIF_UL1), + MT8901_SOC_ENUM_EXT("UL2_Domain_Sel", mt8901_domain_sel_enum, + mt8901_memif_domain_sel_get, + mt8901_memif_domain_sel_put, + MT8901_AFE_MEMIF_UL2), + MT8901_SOC_ENUM_EXT("UL3_Domain_Sel", mt8901_domain_sel_enum, + mt8901_memif_domain_sel_get, + mt8901_memif_domain_sel_put, + MT8901_AFE_MEMIF_UL3), + MT8901_SOC_ENUM_EXT("UL4_Domain_Sel", mt8901_domain_sel_enum, + mt8901_memif_domain_sel_get, + mt8901_memif_domain_sel_put, + MT8901_AFE_MEMIF_UL4), + MT8901_SOC_ENUM_EXT("UL5_Domain_Sel", mt8901_domain_sel_enum, + mt8901_memif_domain_sel_get, + mt8901_memif_domain_sel_put, + MT8901_AFE_MEMIF_UL5), + MT8901_SOC_ENUM_EXT("VUL_CM0_Domain_Sel", mt8901_domain_sel_enum, + mt8901_memif_domain_sel_get, + mt8901_memif_domain_sel_put, + MT8901_AFE_MEMIF_VUL_CM0), + MT8901_SOC_ENUM_EXT("VUL_CM1_Domain_Sel", mt8901_domain_sel_enum, + mt8901_memif_domain_sel_get, + mt8901_memif_domain_sel_put, + MT8901_AFE_MEMIF_VUL_CM1), + MT8901_SOC_ENUM_EXT("VUL_CM2_Domain_Sel", mt8901_domain_sel_enum, + mt8901_memif_domain_sel_get, + mt8901_memif_domain_sel_put, + MT8901_AFE_MEMIF_VUL_CM2), +}; + +static const struct snd_soc_dapm_widget mt8901_memif_widgets[] = { + /* DL0 */ + SND_SOC_DAPM_MIXER("I032", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("I033", SND_SOC_NOPM, 0, 0, NULL, 0), + + /* DL1 */ + SND_SOC_DAPM_MIXER("I034", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("I035", SND_SOC_NOPM, 0, 0, NULL, 0), + + /* DL4 */ + SND_SOC_DAPM_MIXER("I040", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("I041", SND_SOC_NOPM, 0, 0, NULL, 0), + + /* DL5 */ + SND_SOC_DAPM_MIXER("I042", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("I043", SND_SOC_NOPM, 0, 0, NULL, 0), + + /* DL_24CH */ + SND_SOC_DAPM_MIXER("I054", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("I055", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("I056", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("I057", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("I058", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("I059", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("I060", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("I061", SND_SOC_NOPM, 0, 0, NULL, 0), + + /* UL0 */ + SND_SOC_DAPM_MIXER("O018", SND_SOC_NOPM, 0, 0, + o018_mix, ARRAY_SIZE(o018_mix)), + SND_SOC_DAPM_MIXER("O019", SND_SOC_NOPM, 0, 0, + o019_mix, ARRAY_SIZE(o019_mix)), + + /* UL1 */ + SND_SOC_DAPM_MIXER("O020", SND_SOC_NOPM, 0, 0, + o020_mix, ARRAY_SIZE(o020_mix)), + SND_SOC_DAPM_MIXER("O021", SND_SOC_NOPM, 0, 0, + o021_mix, ARRAY_SIZE(o021_mix)), + + /* UL4 */ + SND_SOC_DAPM_MIXER("O026", SND_SOC_NOPM, 0, 0, + o026_mix, ARRAY_SIZE(o026_mix)), + SND_SOC_DAPM_MIXER("O027", SND_SOC_NOPM, 0, 0, + o027_mix, ARRAY_SIZE(o027_mix)), + + /* UL5 */ + SND_SOC_DAPM_MIXER("O028", SND_SOC_NOPM, 0, 0, + o028_mix, ARRAY_SIZE(o028_mix)), + SND_SOC_DAPM_MIXER("O029", SND_SOC_NOPM, 0, 0, + o029_mix, ARRAY_SIZE(o029_mix)), + + /* VUL_CM2: O64~O79 (SCD0 UL pool PDI8~PDI15, max 16ch) */ + SND_SOC_DAPM_MIXER("O064", SND_SOC_NOPM, 0, 0, + o064_mix, ARRAY_SIZE(o064_mix)), + SND_SOC_DAPM_MIXER("O065", SND_SOC_NOPM, 0, 0, + o065_mix, ARRAY_SIZE(o065_mix)), + SND_SOC_DAPM_MIXER("O066", SND_SOC_NOPM, 0, 0, + o066_mix, ARRAY_SIZE(o066_mix)), + SND_SOC_DAPM_MIXER("O067", SND_SOC_NOPM, 0, 0, + o067_mix, ARRAY_SIZE(o067_mix)), + SND_SOC_DAPM_MIXER("O068", SND_SOC_NOPM, 0, 0, + o068_mix, ARRAY_SIZE(o068_mix)), + SND_SOC_DAPM_MIXER("O069", SND_SOC_NOPM, 0, 0, + o069_mix, ARRAY_SIZE(o069_mix)), + SND_SOC_DAPM_MIXER("O070", SND_SOC_NOPM, 0, 0, + o070_mix, ARRAY_SIZE(o070_mix)), + SND_SOC_DAPM_MIXER("O071", SND_SOC_NOPM, 0, 0, + o071_mix, ARRAY_SIZE(o071_mix)), + SND_SOC_DAPM_MIXER("O072", SND_SOC_NOPM, 0, 0, + o072_mix, ARRAY_SIZE(o072_mix)), + SND_SOC_DAPM_MIXER("O073", SND_SOC_NOPM, 0, 0, + o073_mix, ARRAY_SIZE(o073_mix)), + SND_SOC_DAPM_MIXER("O074", SND_SOC_NOPM, 0, 0, + o074_mix, ARRAY_SIZE(o074_mix)), + SND_SOC_DAPM_MIXER("O075", SND_SOC_NOPM, 0, 0, + o075_mix, ARRAY_SIZE(o075_mix)), + SND_SOC_DAPM_MIXER("O076", SND_SOC_NOPM, 0, 0, + o076_mix, ARRAY_SIZE(o076_mix)), + SND_SOC_DAPM_MIXER("O077", SND_SOC_NOPM, 0, 0, + o077_mix, ARRAY_SIZE(o077_mix)), + SND_SOC_DAPM_MIXER("O078", SND_SOC_NOPM, 0, 0, + o078_mix, ARRAY_SIZE(o078_mix)), + SND_SOC_DAPM_MIXER("O079", SND_SOC_NOPM, 0, 0, + o079_mix, ARRAY_SIZE(o079_mix)), +}; + +static const struct snd_soc_dapm_route mt8901_memif_routes[] = { + {"I032", NULL, "DL0"}, + {"I033", NULL, "DL0"}, + + {"I034", NULL, "DL1"}, + {"I035", NULL, "DL1"}, + + {"I040", NULL, "DL4"}, + {"I041", NULL, "DL4"}, + + {"I042", NULL, "DL5"}, + {"I043", NULL, "DL5"}, + + {"I054", NULL, "DL_24CH"}, + {"I055", NULL, "DL_24CH"}, + {"I056", NULL, "DL_24CH"}, + {"I057", NULL, "DL_24CH"}, + {"I058", NULL, "DL_24CH"}, + {"I059", NULL, "DL_24CH"}, + {"I060", NULL, "DL_24CH"}, + {"I061", NULL, "DL_24CH"}, + + {"UL0", NULL, "O018"}, + {"UL0", NULL, "O019"}, + + {"UL1", NULL, "O020"}, + {"UL1", NULL, "O021"}, + + {"UL4", NULL, "O026"}, + {"UL4", NULL, "O027"}, + + {"UL5", NULL, "O028"}, + {"UL5", NULL, "O029"}, + + {"VUL_CM2", NULL, "O064"}, + {"VUL_CM2", NULL, "O065"}, + {"VUL_CM2", NULL, "O066"}, + {"VUL_CM2", NULL, "O067"}, + {"VUL_CM2", NULL, "O068"}, + {"VUL_CM2", NULL, "O069"}, + {"VUL_CM2", NULL, "O070"}, + {"VUL_CM2", NULL, "O071"}, + {"VUL_CM2", NULL, "O072"}, + {"VUL_CM2", NULL, "O073"}, + {"VUL_CM2", NULL, "O074"}, + {"VUL_CM2", NULL, "O075"}, + {"VUL_CM2", NULL, "O076"}, + {"VUL_CM2", NULL, "O077"}, + {"VUL_CM2", NULL, "O078"}, + {"VUL_CM2", NULL, "O079"}, + + /* UL5: BRA RX SDW1 — I166/I167 exclusive */ + {"O028", "I166 Switch", "I166"}, + {"O029", "I167 Switch", "I167"}, + + /* UL1: HeadsetMic 2CH — I168/I169 first-fit (PDI7), + * I178~I183 contention (PDI12~14) + */ + {"O020", "I168 Switch", "I168"}, + {"O020", "I178 Switch", "I178"}, + {"O020", "I180 Switch", "I180"}, + {"O020", "I182 Switch", "I182"}, + {"O021", "I169 Switch", "I169"}, + {"O021", "I179 Switch", "I179"}, + {"O021", "I181 Switch", "I181"}, + {"O021", "I183 Switch", "I183"}, + + /* UL0: Mic 2CH — I170/I171 first-fit (PDI8), + * I178~I181 contention (PDI12/13) + */ + {"O018", "I170 Switch", "I170"}, + {"O018", "I178 Switch", "I178"}, + {"O018", "I180 Switch", "I180"}, + {"O019", "I171 Switch", "I171"}, + {"O019", "I179 Switch", "I179"}, + {"O019", "I181 Switch", "I181"}, + + /* VUL_CM2: Mic >2CH — I170~I185 (PDI8~PDI15, max 16CH) */ + {"O064", "I170 Switch", "I170"}, + {"O065", "I171 Switch", "I171"}, + {"O066", "I172 Switch", "I172"}, + {"O067", "I173 Switch", "I173"}, + {"O068", "I174 Switch", "I174"}, + {"O069", "I175 Switch", "I175"}, + {"O070", "I176 Switch", "I176"}, + {"O071", "I177 Switch", "I177"}, + {"O072", "I178 Switch", "I178"}, + {"O073", "I179 Switch", "I179"}, + {"O074", "I180 Switch", "I180"}, + {"O075", "I181 Switch", "I181"}, + {"O076", "I182 Switch", "I182"}, + {"O077", "I183 Switch", "I183"}, + {"O078", "I184 Switch", "I184"}, + {"O079", "I185 Switch", "I185"}, + + /* UL4: BRA RX SDW0 — I186/I187 exclusive */ + {"O026", "I186 Switch", "I186"}, + {"O027", "I187 Switch", "I187"}, +}; + +#define MT8901_MEMIF_DL(n) \ +[MT8901_AFE_MEMIF_DL##n] = { \ + .name = "DL" #n, \ + .id = MT8901_AFE_MEMIF_DL##n, \ + .reg_ofs_base = AFE_DL##n##_BASE, \ + .reg_ofs_cur = AFE_DL##n##_CUR, \ + .reg_ofs_end = AFE_DL##n##_END, \ + .reg_ofs_base_msb = AFE_DL##n##_BASE_MSB, \ + .reg_ofs_cur_msb = AFE_DL##n##_CUR_MSB, \ + .reg_ofs_end_msb = AFE_DL##n##_END_MSB, \ + .fs_reg = AFE_DL##n##_CON0, \ + .fs_shift = DL_SEL_FS_SFT, \ + .fs_maskbit = DL_SEL_FS_MASK, \ + .mono_reg = AFE_DL##n##_CON0, \ + .mono_shift = DL_MONO_SFT, \ + .enable_reg = AFE_DL##n##_CON0, \ + .enable_shift = DL_ON_SFT, \ + .hd_reg = AFE_DL##n##_CON0, \ + .hd_shift = DL_HD_MODE_SFT, \ + .hd_align_reg = AFE_DL##n##_CON0, \ + .hd_align_mshift = 2, \ + .pbuf_reg = AFE_DL##n##_CON0, \ + .pbuf_shift = DL_PBUF_SIZE_SFT, \ + .pbuf_mask = GENMASK(1, 0), \ + .minlen_reg = AFE_DL##n##_CON0, \ + .minlen_shift = DL_MINLEN_SFT, \ + .minlen_mask = GENMASK(1, 0), \ + .ch_num_reg = -1, \ + .ch_num_shift = 0, \ + .ch_num_maskbit = 0, \ + .maxlen_reg = AFE_DL##n##_CON0, \ + .maxlen_shift = DL_MAXLEN_SFT, \ + .maxlen_mask = GENMASK(1, 0), \ + .domain_reg = AFE_DL##n##_CON0, \ + .domain_shift = DL_SEL_DOMAIN_SFT, \ + .domain_mask = DL_SEL_DOMAIN_MASK, \ + .one_heart_mode_sel_reg = AFE_DL##n##_CON0, \ + .one_heart_mode_shift = DL_ONE_HEART_SEL_SFT, \ + .one_heart_mode_mask_shift = DL_ONE_HEART_SEL_MASK_SFT, \ +} + +/* MT8901_MEMIF_UL(n) is the same idea for ULn (n = 0..5). */ +#define MT8901_MEMIF_UL(n) \ +[MT8901_AFE_MEMIF_UL##n] = { \ + .name = "UL" #n, \ + .id = MT8901_AFE_MEMIF_UL##n, \ + .reg_ofs_base = AFE_VUL##n##_BASE, \ + .reg_ofs_cur = AFE_VUL##n##_CUR, \ + .reg_ofs_end = AFE_VUL##n##_END, \ + .reg_ofs_base_msb = AFE_VUL##n##_BASE_MSB, \ + .reg_ofs_cur_msb = AFE_VUL##n##_CUR_MSB, \ + .reg_ofs_end_msb = AFE_VUL##n##_END_MSB, \ + .fs_reg = AFE_VUL##n##_CON0, \ + .fs_shift = VUL_SEL_FS_SFT, \ + .fs_maskbit = VUL_SEL_FS_MASK, \ + .mono_reg = AFE_VUL##n##_CON0, \ + .mono_shift = VUL_MONO_SFT, \ + .enable_reg = AFE_VUL##n##_CON0, \ + .enable_shift = VUL_ON_SFT, \ + .hd_reg = AFE_VUL##n##_CON0, \ + .hd_shift = VUL_HD_MODE_SFT, \ + .hd_align_reg = AFE_VUL##n##_CON0, \ + .hd_align_mshift = VUL_HALIGN_SFT, \ + .minlen_reg = AFE_VUL##n##_CON0, \ + .minlen_shift = UL_MINLEN_SFT, \ + .minlen_mask = GENMASK(1, 0), \ + .ch_num_reg = -1, \ + .ch_num_shift = 0, \ + .ch_num_maskbit = 0, \ + .maxlen_reg = AFE_VUL##n##_CON0, \ + .maxlen_shift = UL_MAXLEN_SFT, \ + .maxlen_mask = GENMASK(1, 0), \ + .domain_reg = AFE_VUL##n##_CON0, \ + .domain_shift = VUL_SEL_DOMAIN_SFT, \ + .domain_mask = VUL_SEL_DOMAIN_MASK, \ +} + +#define MT8901_MEMIF_VUL_CM(n) \ +[MT8901_AFE_MEMIF_VUL_CM##n] = { \ + .name = "VUL_CM" #n, \ + .id = MT8901_AFE_MEMIF_VUL_CM##n, \ + .reg_ofs_base = AFE_VUL_CM##n##_BASE, \ + .reg_ofs_cur = AFE_VUL_CM##n##_CUR, \ + .reg_ofs_end = AFE_VUL_CM##n##_END, \ + .reg_ofs_base_msb = AFE_VUL_CM##n##_BASE_MSB, \ + .reg_ofs_cur_msb = AFE_VUL_CM##n##_CUR_MSB, \ + .reg_ofs_end_msb = AFE_VUL_CM##n##_END_MSB, \ + .fs_reg = -1, \ + .fs_shift = 0, \ + .fs_maskbit = 0, \ + .mono_reg = -1, \ + .mono_shift = 0, \ + .int_odd_flag_reg = AFE_VUL_CM##n##_CON0, \ + .int_odd_flag_shift = 0, \ + .enable_reg = AFE_VUL_CM##n##_CON0, \ + .enable_shift = 28, \ + .hd_reg = AFE_VUL_CM##n##_CON0, \ + .hd_shift = 4, \ + .hd_align_reg = AFE_VUL_CM##n##_CON0, \ + .hd_align_mshift = 7, \ + .agent_disable_reg = -1, \ + .agent_disable_shift = 0, \ + .ch_num_reg = -1, \ + .ch_num_shift = 0, \ + .ch_num_maskbit = 0, \ + .msb_reg = -1, \ + .msb_shift = 0, \ + .msb_end_reg = -1, \ + .msb_end_shift = 0, \ + .minlen_reg = AFE_VUL_CM##n##_CON0, \ + .minlen_shift = UL_CM_MINLEN_SFT, \ + .minlen_mask = GENMASK(1, 0), \ + .maxlen_reg = AFE_VUL_CM##n##_CON0, \ + .maxlen_shift = UL_CM_MAXLEN_SFT, \ + .maxlen_mask = GENMASK(1, 0), \ +} + +static const struct mtk_base_memif_data memif_data[MT8901_AFE_MEMIF_NUM] = { + MT8901_MEMIF_DL(0), + MT8901_MEMIF_DL(1), + MT8901_MEMIF_DL(2), + MT8901_MEMIF_DL(3), + MT8901_MEMIF_DL(4), + MT8901_MEMIF_DL(5), + [MT8901_AFE_MEMIF_DL_24CH_0] = { + .name = "DL_24CH", + .id = MT8901_AFE_MEMIF_DL_24CH_0, + .reg_ofs_base = AFE_DL_24CH_BASE, + .reg_ofs_cur = AFE_DL_24CH_CUR, + .reg_ofs_end = AFE_DL_24CH_END, + .reg_ofs_base_msb = AFE_DL_24CH_BASE_MSB, + .reg_ofs_cur_msb = AFE_DL_24CH_CUR_MSB, + .reg_ofs_end_msb = AFE_DL_24CH_END_MSB, + .fs_reg = AFE_DL_24CH_CON0, + .fs_shift = DL_SEL_FS_SFT, + .fs_maskbit = DL_SEL_FS_MASK, + .mono_reg = -1, + .mono_shift = 0, + .enable_reg = AFE_DL_24CH_CON0, + .enable_shift = 31, + .hd_reg = AFE_DL_24CH_CON0, + .hd_shift = DL_HD_MODE_SFT, + .hd_align_reg = AFE_DL_24CH_CON0, + .hd_align_mshift = 2, + .ch_num_reg = AFE_DL_24CH_CON0, + .ch_num_shift = 24, + .ch_num_maskbit = GENMASK(5, 0), + .pbuf_reg = AFE_DL_24CH_CON0, + .pbuf_shift = DL_PBUF_SIZE_SFT, + .pbuf_mask = GENMASK(1, 0), + .minlen_reg = AFE_DL_24CH_CON0, + .minlen_shift = DL_MINLEN_SFT, + .minlen_mask = GENMASK(1, 0), + .maxlen_reg = AFE_DL_24CH_CON0, + .maxlen_shift = DL_MAXLEN_SFT, + .maxlen_mask = GENMASK(1, 0), + .domain_reg = AFE_DL_24CH_CON0, + .domain_shift = DL_SEL_DOMAIN_SFT, + .domain_mask = DL_SEL_DOMAIN_MASK, + .one_heart_mode_sel_reg = AFE_DL_24CH_CON0, + .one_heart_mode_shift = DL_ONE_HEART_SEL_SFT, + .one_heart_mode_mask_shift = DL_ONE_HEART_SEL_MASK_SFT, + }, + MT8901_MEMIF_UL(0), + MT8901_MEMIF_UL(1), + MT8901_MEMIF_UL(2), + MT8901_MEMIF_UL(3), + MT8901_MEMIF_UL(4), + MT8901_MEMIF_UL(5), + + MT8901_MEMIF_VUL_CM(0), + MT8901_MEMIF_VUL_CM(1), + MT8901_MEMIF_VUL_CM(2), +}; + +#define MT8901_AFE_IRQ(n) \ +[MT8901_AFE_IRQ_##n] = { \ + .id = MT8901_AFE_IRQ_##n, \ + .irq_cnt_reg = AFE_IRQ##n##_MCU_CFG1, \ + .irq_cnt_shift = 0, \ + .irq_cnt_maskbit = GENMASK(23, 0), \ + .irq_fs_reg = AFE_IRQ##n##_MCU_CFG0, \ + .irq_fs_shift = 4, \ + .irq_fs_maskbit = GENMASK(4, 0), \ + .irq_en_reg = AFE_IRQ##n##_MCU_CFG0, \ + .irq_en_shift = 0, \ + .irq_clr_reg = AFE_IRQ##n##_MCU_CFG1, \ + .irq_clr_shift = 31, \ + .irq_status_shift = n, \ + .irq_domain_reg = AFE_IRQ##n##_MCU_CFG0, \ + .irq_domain_shift = 9, \ + .irq_domain_maskbit = 0x7, \ + .irq_mask_reg = AFE_IRQ_MCU_EN, \ + .irq_mask_shift = n, \ +} + +static const struct mtk_base_irq_data irq_data[MT8901_AFE_IRQ_NUM] = { + MT8901_AFE_IRQ(0), + MT8901_AFE_IRQ(1), + MT8901_AFE_IRQ(2), + MT8901_AFE_IRQ(3), + MT8901_AFE_IRQ(4), + MT8901_AFE_IRQ(5), + MT8901_AFE_IRQ(6), + MT8901_AFE_IRQ(7), + MT8901_AFE_IRQ(8), + MT8901_AFE_IRQ(9), + MT8901_AFE_IRQ(10), + MT8901_AFE_IRQ(11), + MT8901_AFE_IRQ(12), + MT8901_AFE_IRQ(13), + MT8901_AFE_IRQ(14), + MT8901_AFE_IRQ(15), + MT8901_AFE_IRQ(16), + MT8901_AFE_IRQ(17), + MT8901_AFE_IRQ(18), + MT8901_AFE_IRQ(19), + MT8901_AFE_IRQ(20), + MT8901_AFE_IRQ(21), +}; + +static const int mt8901_afe_memif_const_irqs[MT8901_AFE_MEMIF_NUM] = { + [MT8901_AFE_MEMIF_DL0] = MT8901_AFE_IRQ_0, + [MT8901_AFE_MEMIF_DL1] = MT8901_AFE_IRQ_1, + [MT8901_AFE_MEMIF_DL2] = MT8901_AFE_IRQ_2, + [MT8901_AFE_MEMIF_DL3] = MT8901_AFE_IRQ_3, + [MT8901_AFE_MEMIF_DL4] = MT8901_AFE_IRQ_4, + [MT8901_AFE_MEMIF_DL5] = MT8901_AFE_IRQ_5, + [MT8901_AFE_MEMIF_DL_24CH_0] = MT8901_AFE_IRQ_6, + [MT8901_AFE_MEMIF_UL0] = MT8901_AFE_IRQ_7, + [MT8901_AFE_MEMIF_UL1] = MT8901_AFE_IRQ_8, + [MT8901_AFE_MEMIF_UL2] = MT8901_AFE_IRQ_9, + [MT8901_AFE_MEMIF_UL3] = MT8901_AFE_IRQ_10, + [MT8901_AFE_MEMIF_UL4] = MT8901_AFE_IRQ_11, + [MT8901_AFE_MEMIF_UL5] = MT8901_AFE_IRQ_12, + [MT8901_AFE_MEMIF_VUL_CM0] = MT8901_AFE_IRQ_13, + [MT8901_AFE_MEMIF_VUL_CM1] = MT8901_AFE_IRQ_14, + [MT8901_AFE_MEMIF_VUL_CM2] = MT8901_AFE_IRQ_15, +}; + +static bool mt8901_is_volatile_reg(struct device *dev, unsigned int reg) +{ + /* registers with read-only bits cannot be cached */ + switch (reg) { + /* AUDIO_TOP: contains RO status bits */ + case AUDIO_TOP_CON0: + case AUDIO_TOP_CON1: + case AUDIO_TOP_CON3: + case AUDIO_TOP_CON4: + case AUDIO_TOP_CON5: + /* CM monitor */ + case AFE_CM0_MON: + case AFE_CM1_MON: + case AFE_CM2_MON: + /* DL current pointer and monitor registers */ + case AFE_DL0_CUR_MSB: + case AFE_DL0_CUR: + case AFE_DL0_RCH_MON: + case AFE_DL0_LCH_MON: + case AFE_DL0_MON0: + case AFE_DL1_CUR_MSB: + case AFE_DL1_CUR: + case AFE_DL1_RCH_MON: + case AFE_DL1_LCH_MON: + case AFE_DL1_MON0: + case AFE_DL2_CUR_MSB: + case AFE_DL2_CUR: + case AFE_DL2_RCH_MON: + case AFE_DL2_LCH_MON: + case AFE_DL2_MON0: + case AFE_DL3_CUR_MSB: + case AFE_DL3_CUR: + case AFE_DL3_RCH_MON: + case AFE_DL3_LCH_MON: + case AFE_DL3_MON0: + case AFE_DL4_CUR_MSB: + case AFE_DL4_CUR: + case AFE_DL4_RCH_MON: + case AFE_DL4_LCH_MON: + case AFE_DL4_MON0: + case AFE_DL5_CUR_MSB: + case AFE_DL5_CUR: + case AFE_DL5_RCH_MON: + case AFE_DL5_LCH_MON: + case AFE_DL5_MON0: + case AFE_DL_24CH_CUR_MSB: + case AFE_DL_24CH_CUR: + case AFE_DL_24CH_MON0: + /* VUL current pointer and monitor registers */ + case AFE_VUL0_CUR_MSB: + case AFE_VUL0_CUR: + case AFE_VUL0_RCH_MON: + case AFE_VUL0_LCH_MON: + case AFE_VUL0_MON0: + case AFE_VUL1_CUR_MSB: + case AFE_VUL1_CUR: + case AFE_VUL1_RCH_MON: + case AFE_VUL1_LCH_MON: + case AFE_VUL1_MON0: + case AFE_VUL2_CUR_MSB: + case AFE_VUL2_CUR: + case AFE_VUL2_RCH_MON: + case AFE_VUL2_LCH_MON: + case AFE_VUL2_MON0: + case AFE_VUL3_CUR_MSB: + case AFE_VUL3_CUR: + case AFE_VUL3_RCH_MON: + case AFE_VUL3_LCH_MON: + case AFE_VUL3_MON0: + case AFE_VUL4_CUR_MSB: + case AFE_VUL4_CUR: + case AFE_VUL4_RCH_MON: + case AFE_VUL4_LCH_MON: + case AFE_VUL4_MON0: + case AFE_VUL5_CUR_MSB: + case AFE_VUL5_CUR: + case AFE_VUL5_RCH_MON: + case AFE_VUL5_LCH_MON: + case AFE_VUL5_MON0: + case AFE_VUL8_CUR_MSB: + case AFE_VUL8_CUR: + case AFE_VUL8_RCH_MON: + case AFE_VUL8_LCH_MON: + case AFE_VUL8_MON0: + case AFE_VUL9_CUR_MSB: + case AFE_VUL9_CUR: + case AFE_VUL9_RCH_MON: + case AFE_VUL9_LCH_MON: + case AFE_VUL9_MON0: + case AFE_VUL10_CUR_MSB: + case AFE_VUL10_CUR: + case AFE_VUL10_RCH_MON: + case AFE_VUL10_LCH_MON: + case AFE_VUL10_MON0: + /* VUL_CM current pointer (MON0 not defined in reg.h) */ + case AFE_VUL_CM0_CUR_MSB: + case AFE_VUL_CM0_CUR: + case AFE_VUL_CM1_CUR_MSB: + case AFE_VUL_CM1_CUR: + case AFE_VUL_CM2_CUR_MSB: + case AFE_VUL_CM2_CUR: + /* IRQ status and monitor registers */ + case AFE_IRQ_MCU_STATUS: + case AFE_CUSTOM_IRQ_MCU_STATUS: + case AFE_IRQ0_CNT_MON: + case AFE_IRQ1_CNT_MON: + case AFE_IRQ2_CNT_MON: + case AFE_IRQ3_CNT_MON: + case AFE_IRQ4_CNT_MON: + case AFE_IRQ5_CNT_MON: + case AFE_IRQ6_CNT_MON: + case AFE_IRQ7_CNT_MON: + case AFE_IRQ8_CNT_MON: + case AFE_IRQ9_CNT_MON: + case AFE_IRQ10_CNT_MON: + case AFE_IRQ11_CNT_MON: + case AFE_IRQ12_CNT_MON: + case AFE_IRQ13_CNT_MON: + case AFE_IRQ14_CNT_MON: + case AFE_IRQ15_CNT_MON: + case AFE_IRQ16_CNT_MON: + case AFE_IRQ17_CNT_MON: + case AFE_IRQ18_CNT_MON: + case AFE_IRQ19_CNT_MON: + case AFE_IRQ20_CNT_MON: + case AFE_IRQ21_CNT_MON: + case AFE_IRQ22_CNT_MON: + case AFE_IRQ23_CNT_MON: + case AFE_IRQ24_CNT_MON: + case AFE_IRQ25_CNT_MON: + case AFE_IRQ26_CNT_MON: + case AFE_IRQ_MCU_MON3: + case AFE_IRQ_MCU_MON0: + case AFE_IRQ_MCU_MON1: + case AFE_IRQ_MCU_MON2: + /* GASRC ratio/status monitor registers */ + case AFE_GASRC0_NEW_CON8: + case AFE_GASRC0_NEW_CON9: + case AFE_GASRC0_NEW_CON12: + case AFE_GASRC0_NEW_IP_VERSION: + case AFE_GASRC1_NEW_CON8: + case AFE_GASRC1_NEW_CON9: + case AFE_GASRC1_NEW_CON12: + case AFE_GASRC1_NEW_IP_VERSION: + case AFE_GASRC2_NEW_CON8: + case AFE_GASRC2_NEW_CON9: + case AFE_GASRC2_NEW_CON12: + case AFE_GASRC2_NEW_IP_VERSION: + case AFE_GASRC3_NEW_CON8: + case AFE_GASRC3_NEW_CON9: + case AFE_GASRC3_NEW_CON12: + case AFE_GASRC3_NEW_IP_VERSION: + case AFE_GASRC4_NEW_CON8: + case AFE_GASRC4_NEW_CON9: + case AFE_GASRC4_NEW_CON12: + case AFE_GASRC4_NEW_IP_VERSION: + case AFE_GASRC5_NEW_CON8: + case AFE_GASRC5_NEW_CON9: + case AFE_GASRC5_NEW_CON12: + case AFE_GASRC5_NEW_IP_VERSION: + case AFE_GASRC6_NEW_CON8: + case AFE_GASRC6_NEW_CON9: + case AFE_GASRC6_NEW_CON12: + case AFE_GASRC6_NEW_IP_VERSION: + case AFE_GASRC7_NEW_CON8: + case AFE_GASRC7_NEW_CON9: + case AFE_GASRC7_NEW_CON12: + case AFE_GASRC7_NEW_IP_VERSION: + case AFE_GASRC8_NEW_CON8: + case AFE_GASRC8_NEW_CON9: + case AFE_GASRC8_NEW_CON12: + case AFE_GASRC8_NEW_IP_VERSION: + case AFE_GASRC9_NEW_CON8: + case AFE_GASRC9_NEW_CON9: + case AFE_GASRC9_NEW_CON12: + case AFE_GASRC9_NEW_IP_VERSION: + case AFE_GASRC10_NEW_CON8: + case AFE_GASRC10_NEW_CON9: + case AFE_GASRC10_NEW_CON12: + case AFE_GASRC10_NEW_IP_VERSION: + case AFE_GASRC11_NEW_CON8: + case AFE_GASRC11_NEW_CON9: + case AFE_GASRC11_NEW_CON12: + case AFE_GASRC11_NEW_IP_VERSION: + return true; + default: + /* SoundWire registers are controlled by another driver */ + if (reg >= AFE_SOUNDWIRE_REG_BEGIN && + reg < AFE_SOUNDWIRE_REG_END) + return true; + else + return false; + }; +} + +static const struct regmap_config mt8901_afe_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .volatile_reg = mt8901_is_volatile_reg, + .max_register = AFE_MAX_REGISTER, + .num_reg_defaults_raw = ((AFE_MAX_REGISTER / 4) + 1), + .cache_type = REGCACHE_FLAT, +}; + +static irqreturn_t mt8901_afe_irq_handler(int irq_id, void *dev_id) +{ + struct mtk_base_afe *afe = dev_id; + unsigned int val = 0; + unsigned int mcu_irq_en = 0; + int i = 0; + int ret = 0; + + ret = regmap_read(afe->regmap, AFE_IRQ_MCU_STATUS, &val); + if (ret) { + dev_err(afe->dev, "%s irq status err\n", __func__); + goto err_irq; + } + + ret = regmap_read(afe->regmap, AFE_IRQ_MCU_EN, &mcu_irq_en); + if (ret) { + dev_err(afe->dev, "%s read irq en err\n", __func__); + goto err_irq; + } + + /* only handle MCU-enabled IRQs */ + val &= mcu_irq_en; + + for (i = 0; i < MT8901_AFE_MEMIF_NUM; i++) { + struct mtk_base_afe_memif *memif = &afe->memif[i]; + struct mtk_base_irq_data const *irq_data; + + if (memif->irq_usage < 0) + continue; + + irq_data = afe->irqs[memif->irq_usage].irq_data; + + if (!(val & BIT(irq_data->irq_status_shift))) + continue; + + /* clear IRQ by writing bit 31 of each IRQ's CFG1 register */ + mt8901_afe_irq_clear(afe, irq_data); + + snd_pcm_period_elapsed(memif->substream); + } + + return IRQ_HANDLED; + +err_irq: + /* clear all enabled IRQs on error */ + for (i = 0; i < MT8901_AFE_IRQ_NUM; i++) + mt8901_afe_irq_clear(afe, &irq_data[i]); + + return IRQ_HANDLED; +} + +static int init_memif_priv_data(struct mtk_base_afe *afe) +{ + struct mt8901_afe_private *afe_priv = afe->platform_priv; + struct mtk_dai_memif_priv *memif_priv; + int i; + + for (i = MT8901_AFE_MEMIF_START; i < MT8901_AFE_MEMIF_END; i++) { + memif_priv = devm_kzalloc(afe->dev, + sizeof(struct mtk_dai_memif_priv), + GFP_KERNEL); + if (!memif_priv) + return -ENOMEM; + + memif_priv->domain = MT8901_DOMAIN_APLL; + /* burst mode: minlen = 64-byte, maxlen = 64-byte burst */ + memif_priv->minlen = MT8901_MEMIF_MINLEN; + memif_priv->maxlen = MT8901_MEMIF_MAXLEN; + afe_priv->dai_priv[i] = memif_priv; + } + + return 0; +} + +static int mt8901_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 = mt8901_memif_dai_driver; + dai->num_dai_drivers = ARRAY_SIZE(mt8901_memif_dai_driver); + + dai->dapm_widgets = mt8901_memif_widgets; + dai->num_dapm_widgets = ARRAY_SIZE(mt8901_memif_widgets); + dai->dapm_routes = mt8901_memif_routes; + dai->num_dapm_routes = ARRAY_SIZE(mt8901_memif_routes); + dai->controls = mt8901_memif_controls; + dai->num_controls = ARRAY_SIZE(mt8901_memif_controls); + return init_memif_priv_data(afe); +} + +typedef int (*dai_register_cb)(struct mtk_base_afe *); +static const dai_register_cb dai_register_cbs[] = { + mt8901_dai_soundwire_register, + mt8901_dai_memif_register, +}; + +static int mt8901_afe_init_regs(struct mtk_base_afe *afe) +{ + /* + * Route only the sources a memif can own (IRQ 0 to 15) to the MCU. + * The handler acknowledges through the memif mapping, so an enabled + * source without a memif would retrigger forever. Per-stream gating + * is the CFG0 enable bit driven by the trigger callbacks. + */ + static const struct reg_sequence init_regs[] = { + { AFE_IRQ_MCU_EN, GENMASK(MT8901_AFE_MEMIF_NUM - 1, 0) }, + { AFE_APLL1_TUNER_CFG, 0x332 }, + { AFE_APLL2_TUNER_CFG, 0x374 }, + { AFE_MEMIF_CON0, 0x9 }, + }; + int i; + + /* Firmware may leave sources asserted; clear them all before routing. */ + for (i = 0; i < MT8901_AFE_IRQ_NUM; i++) + mt8901_afe_irq_clear(afe, &irq_data[i]); + + return regmap_multi_reg_write(afe->regmap, + init_regs, ARRAY_SIZE(init_regs)); +} + +static int mt8901_afe_runtime_suspend(struct device *dev) +{ + struct mtk_base_afe *afe = dev_get_drvdata(dev); + struct mt8901_afe_private *afe_priv = afe->platform_priv; + int ret; + bool skip = false; + + if (!afe->regmap || afe_priv->pm_runtime_bypass_reg_ctl) { + skip = true; + goto skip_regmap; + } + + ret = mt8901_afe_send_spm_req(afe, AFE_SPM_CTRL_DDREN_REQ, false); + if (ret) { + dev_warn(dev, + "mt8901_afe_send_spm_req disable failed: %d\n", ret); + return ret; + } + + ret = mt8901_afe_disable_apll_top_con_cg(afe); + if (ret) { + dev_warn(dev, "mt8901_afe_disable_apll_top_con_cg failed: %d\n", + ret); + goto err_apll; + } + + ret = mt8901_afe_disable_main_clock(afe); + if (ret) { + dev_warn(dev, "mt8901_afe_disable_main_clock failed: %d\n", + ret); + goto err_main; + } + + regcache_cache_only(afe->regmap, true); + regcache_mark_dirty(afe->regmap); + +skip_regmap: + ret = mt8901_afe_disable_reg_rw_clk(afe); + if (ret) { + dev_warn(dev, "mt8901_afe_disable_reg_rw_clk failed: %d\n", + ret); + goto err_reg; + } + + ret = mt8901_afe_disable_mtcmos(afe, AUDIO_PD); + if (ret) { + dev_warn(dev, "disable MTCMOS failed\n"); + goto err_mtcmos; + } + + return 0; +err_mtcmos: + mt8901_afe_enable_reg_rw_clk(afe); +err_reg: + if (skip) + return ret; + mt8901_afe_enable_main_clock(afe); + + regcache_cache_only(afe->regmap, false); +err_main: + mt8901_afe_enable_apll_top_con_cg(afe); +err_apll: + mt8901_afe_send_spm_req(afe, AFE_SPM_CTRL_DDREN_REQ, true); + + return ret; +} + +static int mt8901_afe_runtime_resume(struct device *dev) +{ + struct mtk_base_afe *afe = dev_get_drvdata(dev); + struct mt8901_afe_private *afe_priv = afe->platform_priv; + int ret; + + ret = mt8901_afe_enable_mtcmos(afe, AUDIO_PD); + if (ret) { + dev_err(dev, "enable MTCMOS failed\n"); + return ret; + } + + ret = mt8901_afe_enable_reg_rw_clk(afe); + if (ret) { + dev_err(dev, "mt8901_afe_enable_reg_rw_clk failed: %d\n", ret); + goto err_reg; + } + + if (!afe->regmap || afe_priv->pm_runtime_bypass_reg_ctl) + goto skip_regmap; + + regcache_cache_only(afe->regmap, false); + ret = regcache_sync(afe->regmap); + if (ret) { + dev_err(dev, "regcache_sync failed: %d", ret); + goto err_main; + } + + ret = mt8901_afe_enable_main_clock(afe); + if (ret) { + dev_err(dev, "mt8901_afe_enable_main_clock failed: %d\n", ret); + goto err_main; + } + + ret = mt8901_afe_enable_apll_top_con_cg(afe); + if (ret) { + dev_err(dev, "mt8901_afe_enable_apll_top_con_cg failed: %d\n", + ret); + goto err_apll; + } + + /* request DDR for memif */ + ret = mt8901_afe_send_spm_req(afe, AFE_SPM_CTRL_DDREN_REQ, true); + if (ret) { + dev_err(dev, "mt8901_afe_send_spm_req enable failed: %d\n", + ret); + goto err_spm; + } +skip_regmap: + return 0; + +err_spm: + mt8901_afe_disable_apll_top_con_cg(afe); +err_apll: + mt8901_afe_disable_main_clock(afe); + +err_main: + regcache_cache_only(afe->regmap, true); + regcache_mark_dirty(afe->regmap); + + mt8901_afe_disable_reg_rw_clk(afe); +err_reg: + mt8901_afe_disable_mtcmos(afe, AUDIO_PD); + + return ret; +} + +static int mt8901_afe_pcm_dev_probe(struct platform_device *pdev) +{ + struct mtk_base_afe *afe; + struct mt8901_afe_private *afe_priv; + struct device *dev = &pdev->dev; + int i, irq_id, ret; + + ret = dma_set_mask_and_coherent(dev, + DMA_BIT_MASK(MT8901_DMA_ADDR_BITS)); + if (ret) + return ret; + + afe = devm_kzalloc(dev, sizeof(*afe), GFP_KERNEL); + if (!afe) + return -ENOMEM; + + afe->platform_priv = devm_kzalloc(dev, sizeof(*afe_priv), + GFP_KERNEL); + if (!afe->platform_priv) + return -ENOMEM; + + afe_priv = afe->platform_priv; + afe->dev = &pdev->dev; + + afe->base_addr = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(afe->base_addr)) + return dev_err_probe(dev, PTR_ERR(afe->base_addr), + "AFE base_addr not found\n"); + + mutex_init(&afe->irq_alloc_lock); + + /* irq initialize */ + afe->irqs_size = MT8901_AFE_IRQ_NUM; + afe->irqs = devm_kcalloc(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]; + + /* init memif */ + afe->memif_size = MT8901_AFE_MEMIF_NUM; + afe->memif = devm_kcalloc(dev, afe->memif_size, sizeof(*afe->memif), + GFP_KERNEL); + if (!afe->memif) + return -ENOMEM; + + for (i = 0; i < afe->memif_size; i++) { + afe->memif[i].data = &memif_data[i]; + afe->memif[i].irq_usage = mt8901_afe_memif_const_irqs[i]; + afe->memif[i].const_irq = 1; + afe->irqs[afe->memif[i].irq_usage].irq_occupyed = true; + } + + irq_id = platform_get_irq(pdev, 0); + if (irq_id < 0) + return dev_err_probe(dev, irq_id, "no irq found"); + + /* 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) + return dev_err_probe(dev, ret, + "dai register i %d fail\n", i); + } + + /* init dai_driver and component_driver */ + ret = mtk_afe_combine_sub_dai(afe); + if (ret) + return dev_err_probe(dev, ret, + "mtk_afe_combine_sub_dai fail\n"); + + afe->mtk_afe_hardware = &mt8901_afe_hardware; + afe->memif_fs = mt8901_memif_fs; + afe->irq_fs = mt8901_irq_fs; + afe->memif_32bit_supported = 1; + + platform_set_drvdata(pdev, afe); + + device_property_read_u32(dev, "acpi-asd-hw-ver", &afe_priv->hw_ver); + + dev_info(dev, "acpi-asd-hw-ver = %u\n", afe_priv->hw_ver); + + ret = mt8901_afe_init_clock(afe); + if (ret) { + dev_err(dev, "afe init clock failed: %d\n", ret); + return ret; + } + + ret = devm_pm_runtime_enable(dev); + if (ret) + return ret; + + afe_priv->pm_runtime_bypass_reg_ctl = true; + ret = pm_runtime_resume_and_get(dev); + if (ret) + return dev_err_probe(dev, ret, "failed to resume device\n"); + + afe->regmap = devm_regmap_init_mmio(&pdev->dev, afe->base_addr, + &mt8901_afe_regmap_config); + if (IS_ERR(afe->regmap)) { + ret = PTR_ERR(afe->regmap); + goto err_pm_put; + } + + ret = mt8901_afe_init_regs(afe); + if (ret) { + dev_err(dev, "afe init regs failed: %d\n", ret); + goto err_pm_put; + } + + /* + * Request the IRQ only once the regmap exists and the IRQ enable + * state has been initialized: the handler dereferences afe->regmap + * unconditionally and firmware (UEFI POST audio) may leave interrupt + * state asserted, so an earlier request can fire into a half-set-up + * driver. + */ + ret = devm_request_threaded_irq(dev, irq_id, NULL, + mt8901_afe_irq_handler, + IRQF_ONESHOT, + "AFE_ISR_Handle", (void *)afe); + if (ret) { + dev_err_probe(dev, ret, + "could not request_irq for AFE_ISR_Handle\n"); + goto err_pm_put; + } + + ret = pm_runtime_put_sync(dev); + afe_priv->pm_runtime_bypass_reg_ctl = false; + if (ret < 0 && ret != -EAGAIN && ret != -EBUSY) { + /* + * The PM core latches a callback error in runtime_error and + * refuses every later runtime get, so the component would be + * unusable. Probe-failure devres disables runtime PM without + * running the suspend callback, so release what the bypass + * resume took (MTCMOS and the register-access clock) here. + */ + dev_err_probe(dev, ret, "initial runtime suspend failed\n"); + mt8901_afe_disable_reg_rw_clk(afe); + mt8901_afe_disable_mtcmos(afe, AUDIO_PD); + return ret; + } + if (ret < 0) { + /* + * Transient refusal, nothing latched: the device stays + * runtime-active with the regcache in write-through mode + * until the PM core retries. + */ + dev_warn(dev, "initial runtime suspend deferred: %d\n", ret); + } else { + regcache_cache_only(afe->regmap, true); + regcache_mark_dirty(afe->regmap); + } + + /* + * Drop the firmware's default-on MTCMOS vote. Safe on the transient + * path too: the runtime vote keeps the domain powered while the + * device is active. Track it so remove() restores only a vote that + * was actually dropped. + */ + ret = mt8901_afe_disable_mtcmos(afe, AUDIO_PD); + if (ret) + dev_warn(dev, "disable MTCMOS failed: %d\n", ret); + else + afe_priv->default_mtcmos_vote_dropped = true; + + /* Publish the component only once the PM baseline is established. */ + ret = devm_snd_soc_register_component(dev, &mtk_afe_pcm_platform, + afe->dai_drivers, + afe->num_dai_drivers); + if (ret) { + if (afe_priv->default_mtcmos_vote_dropped) + mt8901_afe_enable_mtcmos(afe, AUDIO_PD); + return dev_err_probe(dev, ret, "err_platform\n"); + } + + return 0; + +err_pm_put: + pm_runtime_put_sync(dev); + + return ret; +} + +static void mt8901_afe_pcm_dev_remove(struct platform_device *pdev) +{ + struct mtk_base_afe *afe = platform_get_drvdata(pdev); + struct mt8901_afe_private *afe_priv = afe->platform_priv; + struct device *dev = &pdev->dev; + int ret; + + /* + * Undo the default-vote drop at the end of probe, which accounts for + * MTCMOS defaulting to on before any driver has ever run, but only if + * probe actually managed to drop it. + */ + if (!afe_priv->default_mtcmos_vote_dropped) + return; + + ret = mt8901_afe_enable_mtcmos(afe, AUDIO_PD); + if (ret) + dev_warn(dev, "enable MTCMOS failed: %d\n", ret); +} + +/* ACPI match: ASD0 device in Audio.asl (_HID = "NVDA9022") */ +static const struct acpi_device_id mt8901_afe_acpi_match[] = { + { "NVDA9022", 0 }, + { } +}; +MODULE_DEVICE_TABLE(acpi, mt8901_afe_acpi_match); + +static const struct dev_pm_ops mt8901_afe_pm_ops = { + SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume) + RUNTIME_PM_OPS(mt8901_afe_runtime_suspend, + mt8901_afe_runtime_resume, NULL) +}; + +static struct platform_driver mt8901_afe_pcm_driver = { + .driver = { + .name = "mt8901-audio", + .acpi_match_table = ACPI_PTR(mt8901_afe_acpi_match), + .pm = &mt8901_afe_pm_ops, + }, + .probe = mt8901_afe_pcm_dev_probe, + .remove = mt8901_afe_pcm_dev_remove, +}; + +module_platform_driver(mt8901_afe_pcm_driver); + +MODULE_DESCRIPTION("MediaTek SoC AFE platform driver for ALSA 8901"); +MODULE_AUTHOR("Weiyi Hsieh "); +MODULE_AUTHOR("Trevor Wu "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/mediatek/mt8901/mt8901-dai-soundwire.c b/sound/soc/mediatek/mt8901/mt8901-dai-soundwire.c new file mode 100644 index 0000000000000..8c6914154727c --- /dev/null +++ b/sound/soc/mediatek/mt8901/mt8901-dai-soundwire.c @@ -0,0 +1,213 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * MediaTek MT8901 SoundWire DAI driver + * + * Copyright (c) 2026 MediaTek Inc. + * Author: Trevor Wu + * Weiyi Hsieh + */ + +#include "mt8901-afe-common.h" +#include "mt8901-reg.h" + +/* BRA TX SDW1: DL5 I042/I043 -> O148/O149 */ +static const struct snd_kcontrol_new mtk_dai_sndw_o148_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I042 Switch", AFE_CONN148_1, 10, 1, 0), +}; + +static const struct snd_kcontrol_new mtk_dai_sndw_o149_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I043 Switch", AFE_CONN149_1, 11, 1, 0), +}; + +/* SCD0 DL: O150/O151 <- DL0 I032/I033 (2CH), DL_24CH I054/I055 (>2CH) */ +static const struct snd_kcontrol_new mtk_dai_sndw_o150_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I032 Switch", AFE_CONN150_1, 0, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I054 Switch", AFE_CONN150_1, 22, 1, 0), +}; + +static const struct snd_kcontrol_new mtk_dai_sndw_o151_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I033 Switch", AFE_CONN151_1, 1, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I055 Switch", AFE_CONN151_1, 23, 1, 0), +}; + +/* PDI3 mix: O152/O153 <- DL0 I032/I033 (2CH aggr), DL_24CH I056/I057 (>2CH) */ +static const struct snd_kcontrol_new mtk_dai_sndw_o152_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I032 Switch", AFE_CONN152_1, 0, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I056 Switch", AFE_CONN152_1, 24, 1, 0), +}; + +static const struct snd_kcontrol_new mtk_dai_sndw_o153_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I033 Switch", AFE_CONN153_1, 1, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I057 Switch", AFE_CONN153_1, 25, 1, 0), +}; + +/* PDI4 mix: O154/O155 <- DL_24CH I058/I059 (>2CH ch5~6) */ +static const struct snd_kcontrol_new mtk_dai_sndw_o154_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I058 Switch", AFE_CONN154_1, 26, 1, 0), +}; + +static const struct snd_kcontrol_new mtk_dai_sndw_o155_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I059 Switch", AFE_CONN155_1, 27, 1, 0), +}; + +/* PDI5 mix: O156/O157 <- DL1 I034/I035 (HP/HSO first-fit), + * DL_24CH I060/I061 (>2CH ch7~8) + */ +static const struct snd_kcontrol_new mtk_dai_sndw_o156_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I034 Switch", AFE_CONN156_1, 2, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I060 Switch", AFE_CONN156_1, 28, 1, 0), +}; + +static const struct snd_kcontrol_new mtk_dai_sndw_o157_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I035 Switch", AFE_CONN157_1, 3, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I061 Switch", AFE_CONN157_1, 29, 1, 0), +}; + +/* PDI6 mix: O158/O159 <- DL1 I034/I035 (Headphone/HeadsetOutput contention) */ +static const struct snd_kcontrol_new mtk_dai_sndw_o158_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I034 Switch", AFE_CONN158_1, 2, 1, 0), +}; + +static const struct snd_kcontrol_new mtk_dai_sndw_o159_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I035 Switch", AFE_CONN159_1, 3, 1, 0), +}; + +/* BRA TX SDW0: O160/O161 <- DL4 I040/I041 */ +static const struct snd_kcontrol_new mtk_dai_sndw_o160_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I040 Switch", AFE_CONN160_1, 8, 1, 0), +}; + +static const struct snd_kcontrol_new mtk_dai_sndw_o161_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I041 Switch", AFE_CONN161_1, 9, 1, 0), +}; + +static const struct snd_soc_dapm_widget mtk_dai_soundwire_widgets[] = { + /* BRA TX SDW1: O148/O149 */ + SND_SOC_DAPM_MIXER("O148", SND_SOC_NOPM, 0, 0, + mtk_dai_sndw_o148_mix, + ARRAY_SIZE(mtk_dai_sndw_o148_mix)), + SND_SOC_DAPM_MIXER("O149", SND_SOC_NOPM, 0, 0, + mtk_dai_sndw_o149_mix, + ARRAY_SIZE(mtk_dai_sndw_o149_mix)), + + /* TX O port widgets: O150~O161 */ + SND_SOC_DAPM_MIXER("O150", SND_SOC_NOPM, 0, 0, + mtk_dai_sndw_o150_mix, + ARRAY_SIZE(mtk_dai_sndw_o150_mix)), + SND_SOC_DAPM_MIXER("O151", SND_SOC_NOPM, 0, 0, + mtk_dai_sndw_o151_mix, + ARRAY_SIZE(mtk_dai_sndw_o151_mix)), + SND_SOC_DAPM_MIXER("O152", SND_SOC_NOPM, 0, 0, + mtk_dai_sndw_o152_mix, + ARRAY_SIZE(mtk_dai_sndw_o152_mix)), + SND_SOC_DAPM_MIXER("O153", SND_SOC_NOPM, 0, 0, + mtk_dai_sndw_o153_mix, + ARRAY_SIZE(mtk_dai_sndw_o153_mix)), + SND_SOC_DAPM_MIXER("O154", SND_SOC_NOPM, 0, 0, + mtk_dai_sndw_o154_mix, + ARRAY_SIZE(mtk_dai_sndw_o154_mix)), + SND_SOC_DAPM_MIXER("O155", SND_SOC_NOPM, 0, 0, + mtk_dai_sndw_o155_mix, + ARRAY_SIZE(mtk_dai_sndw_o155_mix)), + SND_SOC_DAPM_MIXER("O156", SND_SOC_NOPM, 0, 0, + mtk_dai_sndw_o156_mix, + ARRAY_SIZE(mtk_dai_sndw_o156_mix)), + SND_SOC_DAPM_MIXER("O157", SND_SOC_NOPM, 0, 0, + mtk_dai_sndw_o157_mix, + ARRAY_SIZE(mtk_dai_sndw_o157_mix)), + SND_SOC_DAPM_MIXER("O158", SND_SOC_NOPM, 0, 0, + mtk_dai_sndw_o158_mix, + ARRAY_SIZE(mtk_dai_sndw_o158_mix)), + SND_SOC_DAPM_MIXER("O159", SND_SOC_NOPM, 0, 0, + mtk_dai_sndw_o159_mix, + ARRAY_SIZE(mtk_dai_sndw_o159_mix)), + SND_SOC_DAPM_MIXER("O160", SND_SOC_NOPM, 0, 0, + mtk_dai_sndw_o160_mix, + ARRAY_SIZE(mtk_dai_sndw_o160_mix)), + SND_SOC_DAPM_MIXER("O161", SND_SOC_NOPM, 0, 0, + mtk_dai_sndw_o161_mix, + ARRAY_SIZE(mtk_dai_sndw_o161_mix)), + + /* RX I port widgets */ + /* SNDW1 exclusive: I166/I167 */ + SND_SOC_DAPM_MIXER("I166", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("I167", SND_SOC_NOPM, 0, 0, NULL, 0), + + /* Shared PDI7~PDI15: I168~I185 */ + SND_SOC_DAPM_MIXER("I168", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("I169", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("I170", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("I171", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("I172", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("I173", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("I174", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("I175", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("I176", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("I177", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("I178", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("I179", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("I180", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("I181", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("I182", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("I183", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("I184", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("I185", SND_SOC_NOPM, 0, 0, NULL, 0), + + /* SNDW0 exclusive: I186/I187 */ + SND_SOC_DAPM_MIXER("I186", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("I187", SND_SOC_NOPM, 0, 0, NULL, 0), +}; + +static const struct snd_soc_dapm_route mtk_dai_sndw_routes[] = { + /* BRA TX SDW1: DL5 -> O148/O149 */ + {"O148", "I042 Switch", "I042"}, + {"O149", "I043 Switch", "I043"}, + + /* Speaker 2CH: DL0 -> PDI2 */ + {"O150", "I032 Switch", "I032"}, + {"O151", "I033 Switch", "I033"}, + + /* Speaker 2CH aggregation: DL0 -> PDI3 */ + {"O152", "I032 Switch", "I032"}, + {"O153", "I033 Switch", "I033"}, + + /* Speaker >2CH max 8CH: DL_24CH -> PDI2~PDI5 (O150~O157) */ + {"O150", "I054 Switch", "I054"}, + {"O151", "I055 Switch", "I055"}, + {"O152", "I056 Switch", "I056"}, + {"O153", "I057 Switch", "I057"}, + {"O154", "I058 Switch", "I058"}, + {"O155", "I059 Switch", "I059"}, + {"O156", "I060 Switch", "I060"}, + {"O157", "I061 Switch", "I061"}, + + /* BRA TX SDW0: DL4 -> O160/O161 */ + {"O160", "I040 Switch", "I040"}, + {"O161", "I041 Switch", "I041"}, + + /* Headphone/HeadsetOutput: DL1 -> PDI5 (first-fit) */ + {"O156", "I034 Switch", "I034"}, + {"O157", "I035 Switch", "I035"}, + + /* Headphone/HeadsetOutput: DL1 -> PDI6 (contention) */ + {"O158", "I034 Switch", "I034"}, + {"O159", "I035 Switch", "I035"}, +}; + +int mt8901_dai_soundwire_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->dapm_widgets = mtk_dai_soundwire_widgets; + dai->num_dapm_widgets = ARRAY_SIZE(mtk_dai_soundwire_widgets); + dai->dapm_routes = mtk_dai_sndw_routes; + dai->num_dapm_routes = ARRAY_SIZE(mtk_dai_sndw_routes); + + return 0; +} diff --git a/sound/soc/mediatek/mt8901/mt8901-reg.h b/sound/soc/mediatek/mt8901/mt8901-reg.h new file mode 100644 index 0000000000000..4dcec1bfff229 --- /dev/null +++ b/sound/soc/mediatek/mt8901/mt8901-reg.h @@ -0,0 +1,2190 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * mt8901-reg.h -- MediaTek 8901 audio driver reg definition + * + * Copyright (c) 2026 MediaTek Inc. + * Author: Trevor Wu + * Weiyi Hsieh + */ + +#ifndef _MT8901_REG_H_ +#define _MT8901_REG_H_ + +#define AUDIO_TOP_CON0 (0x0000) +#define AUDIO_TOP_CON1 (0x0004) +#define AUDIO_TOP_CON2 (0x0008) +#define AUDIO_TOP_CON3 (0x000c) +#define AUDIO_TOP_CON4 (0x0010) +#define AUDIO_ENGEN_CON0 (0x0014) +#define AUDIO_ENGEN_CON0_USER1 (0x0018) +#define AUDIO_ENGEN_CON0_USER2 (0x001c) +#define AFE_SINEGEN_CON0 (0x0020) +#define AFE_SINEGEN_CON1 (0x0024) +#define AFE_SINEGEN_CON2 (0x0028) +#define AFE_SINEGEN_CON3 (0x002c) +#define AFE_APLL1_TUNER_CFG (0x0030) +#define AFE_APLL1_TUNER_MON0 (0x0034) +#define AFE_APLL2_TUNER_CFG (0x0038) +#define AFE_APLL2_TUNER_MON0 (0x003c) +#define AFE_SPM_CONTROL_REQ (0x0060) +#define AFE_SPM_CONTROL_ACK (0x0064) +#define AUD_TOP_CFG_VCORE_RG (0x0068) +#define AUDIO_TOP_IP_VERSION (0x006c) +#define AUDIO_TOP_CON5 (0x0070) +#define AUDIO_ENGEN_CON0_MON (0x007c) +#define AUD_TOP_CFG_VLP_RG (0x0098) +#define AUD_TOP_MON_RG (0x009c) +#define AFE_CM0_CON0 (0x0ba0) +#define AFE_CM0_MON (0x0ba4) +#define AFE_CM0_IP_VERSION (0x0ba8) +#define AFE_CM1_CON0 (0x0bb0) +#define AFE_CM1_MON (0x0bb4) +#define AFE_CM1_IP_VERSION (0x0bb8) +#define AFE_CM2_CON0 (0x0bc0) +#define AFE_CM2_MON (0x0bc4) +#define AFE_CM2_IP_VERSION (0x0bc8) +#define AFE_ADDA_UL0_SRC_CON0 (0x0bd0) +#define AFE_ADDA_UL0_SRC_CON1 (0x0bd4) +#define AFE_ADDA_UL0_SRC_CON2 (0x0bd8) +#define AFE_ADDA_UL0_SRC_DEBUG (0x0bdc) +#define AFE_ADDA_UL0_SRC_DEBUG_MON0 (0x0be0) +#define AFE_ADDA_UL0_SRC_MON0 (0x0be4) +#define AFE_ADDA_UL0_SRC_MON1 (0x0be8) +#define AFE_ADDA_UL0_IIR_COEF_02_01 (0x0bec) +#define AFE_ADDA_UL0_IIR_COEF_04_03 (0x0bf0) +#define AFE_ADDA_UL0_IIR_COEF_06_05 (0x0bf4) +#define AFE_ADDA_UL0_IIR_COEF_08_07 (0x0bf8) +#define AFE_ADDA_UL0_IIR_COEF_10_09 (0x0bfc) +#define AFE_ADDA_UL0_ULCF_CFG_02_01 (0x0c00) +#define AFE_ADDA_UL0_ULCF_CFG_04_03 (0x0c04) +#define AFE_ADDA_UL0_ULCF_CFG_06_05 (0x0c08) +#define AFE_ADDA_UL0_ULCF_CFG_08_07 (0x0c0c) +#define AFE_ADDA_UL0_ULCF_CFG_10_09 (0x0c10) +#define AFE_ADDA_UL0_ULCF_CFG_12_11 (0x0c14) +#define AFE_ADDA_UL0_ULCF_CFG_14_13 (0x0c18) +#define AFE_ADDA_UL0_ULCF_CFG_16_15 (0x0c1c) +#define AFE_ADDA_UL0_ULCF_CFG_18_17 (0x0c20) +#define AFE_ADDA_UL0_ULCF_CFG_20_19 (0x0c24) +#define AFE_ADDA_UL0_ULCF_CFG_22_21 (0x0c28) +#define AFE_ADDA_UL0_ULCF_CFG_24_23 (0x0c2c) +#define AFE_ADDA_UL0_ULCF_CFG_26_25 (0x0c30) +#define AFE_ADDA_UL0_ULCF_CFG_28_27 (0x0c34) +#define AFE_ADDA_UL0_ULCF_CFG_30_29 (0x0c38) +#define AFE_ADDA_UL0_ULCF_CFG_32_31 (0x0c3c) +#define AFE_ADDA_UL0_IP_VERSION (0x0c4c) +#define AFE_ADDA_UL1_SRC_CON0 (0x0c50) +#define AFE_ADDA_UL1_SRC_CON1 (0x0c54) +#define AFE_ADDA_UL1_SRC_CON2 (0x0c58) +#define AFE_ADDA_UL1_SRC_DEBUG (0x0c5c) +#define AFE_ADDA_UL1_SRC_DEBUG_MON0 (0x0c60) +#define AFE_ADDA_UL1_SRC_MON0 (0x0c64) +#define AFE_ADDA_UL1_SRC_MON1 (0x0c68) +#define AFE_ADDA_UL1_IIR_COEF_02_01 (0x0c6c) +#define AFE_ADDA_UL1_IIR_COEF_04_03 (0x0c70) +#define AFE_ADDA_UL1_IIR_COEF_06_05 (0x0c74) +#define AFE_ADDA_UL1_IIR_COEF_08_07 (0x0c78) +#define AFE_ADDA_UL1_IIR_COEF_10_09 (0x0c7c) +#define AFE_ADDA_UL1_ULCF_CFG_02_01 (0x0c80) +#define AFE_ADDA_UL1_ULCF_CFG_04_03 (0x0c84) +#define AFE_ADDA_UL1_ULCF_CFG_06_05 (0x0c88) +#define AFE_ADDA_UL1_ULCF_CFG_08_07 (0x0c8c) +#define AFE_ADDA_UL1_ULCF_CFG_10_09 (0x0c90) +#define AFE_ADDA_UL1_ULCF_CFG_12_11 (0x0c94) +#define AFE_ADDA_UL1_ULCF_CFG_14_13 (0x0c98) +#define AFE_ADDA_UL1_ULCF_CFG_16_15 (0x0c9c) +#define AFE_ADDA_UL1_ULCF_CFG_18_17 (0x0ca0) +#define AFE_ADDA_UL1_ULCF_CFG_20_19 (0x0ca4) +#define AFE_ADDA_UL1_ULCF_CFG_22_21 (0x0ca8) +#define AFE_ADDA_UL1_ULCF_CFG_24_23 (0x0cac) +#define AFE_ADDA_UL1_ULCF_CFG_26_25 (0x0cb0) +#define AFE_ADDA_UL1_ULCF_CFG_28_27 (0x0cb4) +#define AFE_ADDA_UL1_ULCF_CFG_30_29 (0x0cb8) +#define AFE_ADDA_UL1_ULCF_CFG_32_31 (0x0cbc) +#define AFE_ADDA_UL1_IP_VERSION (0x0ccc) +#define AFE_ADDA_ULSRC_PHASE_CLK_CON0 (0x0f00) +#define AFE_ADDA_ULSRC_PHASE_CLK_CON1 (0x0f04) +#define AFE_ADDA_ULSRC_PHASE_CLK_CON2 (0x0f08) +#define AFE_ADDA_ULSRC_PHASE_CLK_CON3 (0x0f0c) +#define AFE_ADDA_ULSRC_PHASE_CLK_CON4 (0x0f10) +#define AFE_ADDA_ULSRC_PHASE_ENGEN_CON0 (0x0f14) +#define AFE_ADDA_ULSRC_PHASE_ENGEN_CON1 (0x0f18) +#define AFE_ADDA_ULSRC_PHASE_RST_CON0 (0x0f1c) +#define ETDM_IN0_CON0 (0x1300) +#define ETDM_IN0_CON1 (0x1304) +#define ETDM_IN0_CON2 (0x1308) +#define ETDM_IN0_CON3 (0x130c) +#define ETDM_IN0_CON4 (0x1310) +#define ETDM_IN0_CON5 (0x1314) +#define ETDM_IN0_CON6 (0x1318) +#define ETDM_IN0_CON7 (0x131c) +#define ETDM_IN0_CON8 (0x1320) +#define ETDM_IN0_CON9 (0x1324) +#define ETDM_IN0_MON (0x1328) +#define ETDM_IN5_CON0 (0x13f0) +#define ETDM_IN5_CON1 (0x13f4) +#define ETDM_IN5_CON2 (0x13f8) +#define ETDM_IN5_CON3 (0x13fc) +#define ETDM_IN5_CON4 (0x1400) +#define ETDM_IN5_CON5 (0x1404) +#define ETDM_IN5_CON6 (0x1408) +#define ETDM_IN5_CON7 (0x140c) +#define ETDM_IN5_CON8 (0x1410) +#define ETDM_IN5_CON9 (0x1414) +#define ETDM_IN5_MON (0x1418) +#define ETDM_OUT0_CON0 (0x1480) +#define ETDM_OUT0_CON1 (0x1484) +#define ETDM_OUT0_CON2 (0x1488) +#define ETDM_OUT0_CON3 (0x148c) +#define ETDM_OUT0_CON4 (0x1490) +#define ETDM_OUT0_CON5 (0x1494) +#define ETDM_OUT0_CON6 (0x1498) +#define ETDM_OUT0_CON7 (0x149c) +#define ETDM_OUT0_CON8 (0x14a0) +#define ETDM_OUT0_CON9 (0x14a4) +#define ETDM_OUT0_MON (0x14a8) +#define ETDM_OUT5_CON0 (0x15c0) +#define ETDM_OUT5_CON1 (0x15c4) +#define ETDM_OUT5_CON2 (0x15c8) +#define ETDM_OUT5_CON3 (0x15cc) +#define ETDM_OUT5_CON4 (0x15d0) +#define ETDM_OUT5_CON5 (0x15d4) +#define ETDM_OUT5_CON6 (0x15d8) +#define ETDM_OUT5_CON7 (0x15dc) +#define ETDM_OUT5_CON8 (0x15e0) +#define ETDM_OUT5_CON9 (0x15e4) +#define ETDM_OUT5_MON (0x15e8) +#define ETDM_0_3_COWORK_CON0 (0x1680) +#define ETDM_0_3_COWORK_CON1 (0x1684) +#define ETDM_0_3_COWORK_CON2 (0x1688) +#define ETDM_0_3_COWORK_CON3 (0x168c) +#define ETDM_4_7_COWORK_CON0 (0x1690) +#define ETDM_4_7_COWORK_CON1 (0x1694) +#define ETDM_4_7_COWORK_CON2 (0x1698) +#define ETDM_4_7_COWORK_CON3 (0x169c) +#define ETDM_IP_VERSION (0x1c4c) +#define AFE_CONN000_0 (0x2080) +#define AFE_CONN004_0 (0x2100) +#define AFE_CONN004_1 (0x2104) +#define AFE_CONN004_2 (0x2108) +#define AFE_CONN004_3 (0x210c) +#define AFE_CONN004_4 (0x2110) +#define AFE_CONN004_5 (0x2114) +#define AFE_CONN004_6 (0x2118) +#define AFE_CONN004_7 (0x211c) +#define AFE_CONN005_0 (0x2120) +#define AFE_CONN005_1 (0x2124) +#define AFE_CONN005_2 (0x2128) +#define AFE_CONN005_3 (0x212c) +#define AFE_CONN005_4 (0x2130) +#define AFE_CONN005_5 (0x2134) +#define AFE_CONN005_6 (0x2138) +#define AFE_CONN005_7 (0x213c) +#define AFE_CONN006_0 (0x2140) +#define AFE_CONN006_1 (0x2144) +#define AFE_CONN006_2 (0x2148) +#define AFE_CONN006_3 (0x214c) +#define AFE_CONN006_4 (0x2150) +#define AFE_CONN006_5 (0x2154) +#define AFE_CONN006_6 (0x2158) +#define AFE_CONN006_7 (0x215c) +#define AFE_CONN007_0 (0x2160) +#define AFE_CONN007_1 (0x2164) +#define AFE_CONN007_2 (0x2168) +#define AFE_CONN007_3 (0x216c) +#define AFE_CONN007_4 (0x2170) +#define AFE_CONN007_5 (0x2174) +#define AFE_CONN007_6 (0x2178) +#define AFE_CONN007_7 (0x217c) +#define AFE_CONN008_0 (0x2180) +#define AFE_CONN008_1 (0x2184) +#define AFE_CONN008_2 (0x2188) +#define AFE_CONN008_3 (0x218c) +#define AFE_CONN008_4 (0x2190) +#define AFE_CONN008_5 (0x2194) +#define AFE_CONN008_6 (0x2198) +#define AFE_CONN008_7 (0x219c) +#define AFE_CONN009_0 (0x21a0) +#define AFE_CONN009_1 (0x21a4) +#define AFE_CONN009_2 (0x21a8) +#define AFE_CONN009_3 (0x21ac) +#define AFE_CONN009_4 (0x21b0) +#define AFE_CONN009_5 (0x21b4) +#define AFE_CONN009_6 (0x21b8) +#define AFE_CONN009_7 (0x21bc) +#define AFE_CONN010_0 (0x21c0) +#define AFE_CONN010_1 (0x21c4) +#define AFE_CONN010_2 (0x21c8) +#define AFE_CONN010_3 (0x21cc) +#define AFE_CONN010_4 (0x21d0) +#define AFE_CONN010_5 (0x21d4) +#define AFE_CONN010_6 (0x21d8) +#define AFE_CONN010_7 (0x21dc) +#define AFE_CONN011_0 (0x21e0) +#define AFE_CONN011_1 (0x21e4) +#define AFE_CONN011_2 (0x21e8) +#define AFE_CONN011_3 (0x21ec) +#define AFE_CONN011_4 (0x21f0) +#define AFE_CONN011_5 (0x21f4) +#define AFE_CONN011_6 (0x21f8) +#define AFE_CONN011_7 (0x21fc) +#define AFE_CONN012_0 (0x2200) +#define AFE_CONN012_1 (0x2204) +#define AFE_CONN012_2 (0x2208) +#define AFE_CONN012_3 (0x220c) +#define AFE_CONN012_4 (0x2210) +#define AFE_CONN012_5 (0x2214) +#define AFE_CONN012_6 (0x2218) +#define AFE_CONN012_7 (0x221c) +#define AFE_CONN013_0 (0x2220) +#define AFE_CONN013_1 (0x2224) +#define AFE_CONN013_2 (0x2228) +#define AFE_CONN013_3 (0x222c) +#define AFE_CONN013_4 (0x2230) +#define AFE_CONN013_5 (0x2234) +#define AFE_CONN013_6 (0x2238) +#define AFE_CONN013_7 (0x223c) +#define AFE_CONN014_0 (0x2240) +#define AFE_CONN014_1 (0x2244) +#define AFE_CONN014_2 (0x2248) +#define AFE_CONN014_3 (0x224c) +#define AFE_CONN014_4 (0x2250) +#define AFE_CONN014_5 (0x2254) +#define AFE_CONN014_6 (0x2258) +#define AFE_CONN014_7 (0x225c) +#define AFE_CONN015_0 (0x2260) +#define AFE_CONN015_1 (0x2264) +#define AFE_CONN015_2 (0x2268) +#define AFE_CONN015_3 (0x226c) +#define AFE_CONN015_4 (0x2270) +#define AFE_CONN015_5 (0x2274) +#define AFE_CONN015_6 (0x2278) +#define AFE_CONN015_7 (0x227c) +#define AFE_CONN016_0 (0x2280) +#define AFE_CONN016_1 (0x2284) +#define AFE_CONN016_2 (0x2288) +#define AFE_CONN016_3 (0x228c) +#define AFE_CONN016_4 (0x2290) +#define AFE_CONN016_5 (0x2294) +#define AFE_CONN016_6 (0x2298) +#define AFE_CONN016_7 (0x229c) +#define AFE_CONN017_0 (0x22a0) +#define AFE_CONN017_1 (0x22a4) +#define AFE_CONN017_2 (0x22a8) +#define AFE_CONN017_3 (0x22ac) +#define AFE_CONN017_4 (0x22b0) +#define AFE_CONN017_5 (0x22b4) +#define AFE_CONN017_6 (0x22b8) +#define AFE_CONN017_7 (0x22bc) +#define AFE_CONN018_0 (0x22c0) +#define AFE_CONN018_1 (0x22c4) +#define AFE_CONN018_2 (0x22c8) +#define AFE_CONN018_3 (0x22cc) +#define AFE_CONN018_4 (0x22d0) +#define AFE_CONN018_5 (0x22d4) +#define AFE_CONN018_6 (0x22d8) +#define AFE_CONN018_7 (0x22dc) +#define AFE_CONN019_0 (0x22e0) +#define AFE_CONN019_1 (0x22e4) +#define AFE_CONN019_2 (0x22e8) +#define AFE_CONN019_3 (0x22ec) +#define AFE_CONN019_4 (0x22f0) +#define AFE_CONN019_5 (0x22f4) +#define AFE_CONN019_6 (0x22f8) +#define AFE_CONN019_7 (0x22fc) +#define AFE_CONN020_0 (0x2300) +#define AFE_CONN020_1 (0x2304) +#define AFE_CONN020_2 (0x2308) +#define AFE_CONN020_3 (0x230c) +#define AFE_CONN020_4 (0x2310) +#define AFE_CONN020_5 (0x2314) +#define AFE_CONN020_6 (0x2318) +#define AFE_CONN020_7 (0x231c) +#define AFE_CONN021_0 (0x2320) +#define AFE_CONN021_1 (0x2324) +#define AFE_CONN021_2 (0x2328) +#define AFE_CONN021_3 (0x232c) +#define AFE_CONN021_4 (0x2330) +#define AFE_CONN021_5 (0x2334) +#define AFE_CONN021_6 (0x2338) +#define AFE_CONN021_7 (0x233c) +#define AFE_CONN022_0 (0x2340) +#define AFE_CONN022_1 (0x2344) +#define AFE_CONN022_2 (0x2348) +#define AFE_CONN022_3 (0x234c) +#define AFE_CONN022_4 (0x2350) +#define AFE_CONN022_5 (0x2354) +#define AFE_CONN022_6 (0x2358) +#define AFE_CONN022_7 (0x235c) +#define AFE_CONN023_0 (0x2360) +#define AFE_CONN023_1 (0x2364) +#define AFE_CONN023_2 (0x2368) +#define AFE_CONN023_3 (0x236c) +#define AFE_CONN023_4 (0x2370) +#define AFE_CONN023_5 (0x2374) +#define AFE_CONN023_6 (0x2378) +#define AFE_CONN023_7 (0x237c) +#define AFE_CONN024_0 (0x2380) +#define AFE_CONN024_1 (0x2384) +#define AFE_CONN024_2 (0x2388) +#define AFE_CONN024_3 (0x238c) +#define AFE_CONN024_4 (0x2390) +#define AFE_CONN024_5 (0x2394) +#define AFE_CONN024_6 (0x2398) +#define AFE_CONN024_7 (0x239c) +#define AFE_CONN025_0 (0x23a0) +#define AFE_CONN025_1 (0x23a4) +#define AFE_CONN025_2 (0x23a8) +#define AFE_CONN025_3 (0x23ac) +#define AFE_CONN025_4 (0x23b0) +#define AFE_CONN025_5 (0x23b4) +#define AFE_CONN025_6 (0x23b8) +#define AFE_CONN025_7 (0x23bc) +#define AFE_CONN026_0 (0x23c0) +#define AFE_CONN026_1 (0x23c4) +#define AFE_CONN026_2 (0x23c8) +#define AFE_CONN026_3 (0x23cc) +#define AFE_CONN026_4 (0x23d0) +#define AFE_CONN026_5 (0x23d4) +#define AFE_CONN026_6 (0x23d8) +#define AFE_CONN026_7 (0x23dc) +#define AFE_CONN027_0 (0x23e0) +#define AFE_CONN027_1 (0x23e4) +#define AFE_CONN027_2 (0x23e8) +#define AFE_CONN027_3 (0x23ec) +#define AFE_CONN027_4 (0x23f0) +#define AFE_CONN027_5 (0x23f4) +#define AFE_CONN027_6 (0x23f8) +#define AFE_CONN027_7 (0x23fc) +#define AFE_CONN028_0 (0x2400) +#define AFE_CONN028_1 (0x2404) +#define AFE_CONN028_2 (0x2408) +#define AFE_CONN028_3 (0x240c) +#define AFE_CONN028_4 (0x2410) +#define AFE_CONN028_5 (0x2414) +#define AFE_CONN028_6 (0x2418) +#define AFE_CONN028_7 (0x241c) +#define AFE_CONN029_0 (0x2420) +#define AFE_CONN029_1 (0x2424) +#define AFE_CONN029_2 (0x2428) +#define AFE_CONN029_3 (0x242c) +#define AFE_CONN029_4 (0x2430) +#define AFE_CONN029_5 (0x2434) +#define AFE_CONN029_6 (0x2438) +#define AFE_CONN029_7 (0x243c) +#define AFE_CONN034_0 (0x24c0) +#define AFE_CONN034_1 (0x24c4) +#define AFE_CONN034_2 (0x24c8) +#define AFE_CONN034_3 (0x24cc) +#define AFE_CONN034_4 (0x24d0) +#define AFE_CONN034_5 (0x24d4) +#define AFE_CONN034_6 (0x24d8) +#define AFE_CONN034_7 (0x24dc) +#define AFE_CONN035_0 (0x24e0) +#define AFE_CONN035_1 (0x24e4) +#define AFE_CONN035_2 (0x24e8) +#define AFE_CONN035_3 (0x24ec) +#define AFE_CONN035_4 (0x24f0) +#define AFE_CONN035_5 (0x24f4) +#define AFE_CONN035_6 (0x24f8) +#define AFE_CONN035_7 (0x24fc) +#define AFE_CONN036_0 (0x2500) +#define AFE_CONN036_1 (0x2504) +#define AFE_CONN036_2 (0x2508) +#define AFE_CONN036_3 (0x250c) +#define AFE_CONN036_4 (0x2510) +#define AFE_CONN036_5 (0x2514) +#define AFE_CONN036_6 (0x2518) +#define AFE_CONN036_7 (0x251c) +#define AFE_CONN037_0 (0x2520) +#define AFE_CONN037_1 (0x2524) +#define AFE_CONN037_2 (0x2528) +#define AFE_CONN037_3 (0x252c) +#define AFE_CONN037_4 (0x2530) +#define AFE_CONN037_5 (0x2534) +#define AFE_CONN037_6 (0x2538) +#define AFE_CONN037_7 (0x253c) +#define AFE_CONN038_0 (0x2540) +#define AFE_CONN038_1 (0x2544) +#define AFE_CONN038_2 (0x2548) +#define AFE_CONN038_3 (0x254c) +#define AFE_CONN038_4 (0x2550) +#define AFE_CONN038_5 (0x2554) +#define AFE_CONN038_6 (0x2558) +#define AFE_CONN038_7 (0x255c) +#define AFE_CONN039_0 (0x2560) +#define AFE_CONN039_1 (0x2564) +#define AFE_CONN039_2 (0x2568) +#define AFE_CONN039_3 (0x256c) +#define AFE_CONN039_4 (0x2570) +#define AFE_CONN039_5 (0x2574) +#define AFE_CONN039_6 (0x2578) +#define AFE_CONN039_7 (0x257c) +#define AFE_CONN040_0 (0x2580) +#define AFE_CONN040_1 (0x2584) +#define AFE_CONN040_2 (0x2588) +#define AFE_CONN040_3 (0x258c) +#define AFE_CONN040_4 (0x2590) +#define AFE_CONN040_5 (0x2594) +#define AFE_CONN040_6 (0x2598) +#define AFE_CONN040_7 (0x259c) +#define AFE_CONN041_0 (0x25a0) +#define AFE_CONN041_1 (0x25a4) +#define AFE_CONN041_2 (0x25a8) +#define AFE_CONN041_3 (0x25ac) +#define AFE_CONN041_4 (0x25b0) +#define AFE_CONN041_5 (0x25b4) +#define AFE_CONN041_6 (0x25b8) +#define AFE_CONN041_7 (0x25bc) +#define AFE_CONN042_0 (0x25c0) +#define AFE_CONN042_1 (0x25c4) +#define AFE_CONN042_2 (0x25c8) +#define AFE_CONN042_3 (0x25cc) +#define AFE_CONN042_4 (0x25d0) +#define AFE_CONN042_5 (0x25d4) +#define AFE_CONN042_6 (0x25d8) +#define AFE_CONN042_7 (0x25dc) +#define AFE_CONN043_0 (0x25e0) +#define AFE_CONN043_1 (0x25e4) +#define AFE_CONN043_2 (0x25e8) +#define AFE_CONN043_3 (0x25ec) +#define AFE_CONN043_4 (0x25f0) +#define AFE_CONN043_5 (0x25f4) +#define AFE_CONN043_6 (0x25f8) +#define AFE_CONN043_7 (0x25fc) +#define AFE_CONN044_0 (0x2600) +#define AFE_CONN044_1 (0x2604) +#define AFE_CONN044_2 (0x2608) +#define AFE_CONN044_3 (0x260c) +#define AFE_CONN044_4 (0x2610) +#define AFE_CONN044_5 (0x2614) +#define AFE_CONN044_6 (0x2618) +#define AFE_CONN044_7 (0x261c) +#define AFE_CONN045_0 (0x2620) +#define AFE_CONN045_1 (0x2624) +#define AFE_CONN045_2 (0x2628) +#define AFE_CONN045_3 (0x262c) +#define AFE_CONN045_4 (0x2630) +#define AFE_CONN045_5 (0x2634) +#define AFE_CONN045_6 (0x2638) +#define AFE_CONN045_7 (0x263c) +#define AFE_CONN046_0 (0x2640) +#define AFE_CONN046_1 (0x2644) +#define AFE_CONN046_2 (0x2648) +#define AFE_CONN046_3 (0x264c) +#define AFE_CONN046_4 (0x2650) +#define AFE_CONN046_5 (0x2654) +#define AFE_CONN046_6 (0x2658) +#define AFE_CONN046_7 (0x265c) +#define AFE_CONN047_0 (0x2660) +#define AFE_CONN047_1 (0x2664) +#define AFE_CONN047_2 (0x2668) +#define AFE_CONN047_3 (0x266c) +#define AFE_CONN047_4 (0x2670) +#define AFE_CONN047_5 (0x2674) +#define AFE_CONN047_6 (0x2678) +#define AFE_CONN047_7 (0x267c) +#define AFE_CONN048_0 (0x2680) +#define AFE_CONN048_1 (0x2684) +#define AFE_CONN048_2 (0x2688) +#define AFE_CONN048_3 (0x268c) +#define AFE_CONN048_4 (0x2690) +#define AFE_CONN048_5 (0x2694) +#define AFE_CONN048_6 (0x2698) +#define AFE_CONN048_7 (0x269c) +#define AFE_CONN049_0 (0x26a0) +#define AFE_CONN049_1 (0x26a4) +#define AFE_CONN049_2 (0x26a8) +#define AFE_CONN049_3 (0x26ac) +#define AFE_CONN049_4 (0x26b0) +#define AFE_CONN049_5 (0x26b4) +#define AFE_CONN049_6 (0x26b8) +#define AFE_CONN049_7 (0x26bc) +#define AFE_CONN050_0 (0x26c0) +#define AFE_CONN050_1 (0x26c4) +#define AFE_CONN050_2 (0x26c8) +#define AFE_CONN050_3 (0x26cc) +#define AFE_CONN050_4 (0x26d0) +#define AFE_CONN050_5 (0x26d4) +#define AFE_CONN050_6 (0x26d8) +#define AFE_CONN050_7 (0x26dc) +#define AFE_CONN051_0 (0x26e0) +#define AFE_CONN051_1 (0x26e4) +#define AFE_CONN051_2 (0x26e8) +#define AFE_CONN051_3 (0x26ec) +#define AFE_CONN051_4 (0x26f0) +#define AFE_CONN051_5 (0x26f4) +#define AFE_CONN051_6 (0x26f8) +#define AFE_CONN051_7 (0x26fc) +#define AFE_CONN052_0 (0x2700) +#define AFE_CONN052_1 (0x2704) +#define AFE_CONN052_2 (0x2708) +#define AFE_CONN052_3 (0x270c) +#define AFE_CONN052_4 (0x2710) +#define AFE_CONN052_5 (0x2714) +#define AFE_CONN052_6 (0x2718) +#define AFE_CONN052_7 (0x271c) +#define AFE_CONN053_0 (0x2720) +#define AFE_CONN053_1 (0x2724) +#define AFE_CONN053_2 (0x2728) +#define AFE_CONN053_3 (0x272c) +#define AFE_CONN053_4 (0x2730) +#define AFE_CONN053_5 (0x2734) +#define AFE_CONN053_6 (0x2738) +#define AFE_CONN053_7 (0x273c) +#define AFE_CONN054_0 (0x2740) +#define AFE_CONN054_1 (0x2744) +#define AFE_CONN054_2 (0x2748) +#define AFE_CONN054_3 (0x274c) +#define AFE_CONN054_4 (0x2750) +#define AFE_CONN054_5 (0x2754) +#define AFE_CONN054_6 (0x2758) +#define AFE_CONN054_7 (0x275c) +#define AFE_CONN055_0 (0x2760) +#define AFE_CONN055_1 (0x2764) +#define AFE_CONN055_2 (0x2768) +#define AFE_CONN055_3 (0x276c) +#define AFE_CONN055_4 (0x2770) +#define AFE_CONN055_5 (0x2774) +#define AFE_CONN055_6 (0x2778) +#define AFE_CONN055_7 (0x277c) +#define AFE_CONN056_0 (0x2780) +#define AFE_CONN056_1 (0x2784) +#define AFE_CONN056_2 (0x2788) +#define AFE_CONN056_3 (0x278c) +#define AFE_CONN056_4 (0x2790) +#define AFE_CONN056_5 (0x2794) +#define AFE_CONN056_6 (0x2798) +#define AFE_CONN056_7 (0x279c) +#define AFE_CONN057_0 (0x27a0) +#define AFE_CONN057_1 (0x27a4) +#define AFE_CONN057_2 (0x27a8) +#define AFE_CONN057_3 (0x27ac) +#define AFE_CONN057_4 (0x27b0) +#define AFE_CONN057_5 (0x27b4) +#define AFE_CONN057_6 (0x27b8) +#define AFE_CONN057_7 (0x27bc) +#define AFE_CONN058_0 (0x27c0) +#define AFE_CONN058_1 (0x27c4) +#define AFE_CONN058_2 (0x27c8) +#define AFE_CONN058_3 (0x27cc) +#define AFE_CONN058_4 (0x27d0) +#define AFE_CONN058_5 (0x27d4) +#define AFE_CONN058_6 (0x27d8) +#define AFE_CONN058_7 (0x27dc) +#define AFE_CONN059_0 (0x27e0) +#define AFE_CONN059_1 (0x27e4) +#define AFE_CONN059_2 (0x27e8) +#define AFE_CONN059_3 (0x27ec) +#define AFE_CONN059_4 (0x27f0) +#define AFE_CONN059_5 (0x27f4) +#define AFE_CONN059_6 (0x27f8) +#define AFE_CONN059_7 (0x27fc) +#define AFE_CONN060_0 (0x2800) +#define AFE_CONN060_1 (0x2804) +#define AFE_CONN060_2 (0x2808) +#define AFE_CONN060_3 (0x280c) +#define AFE_CONN060_4 (0x2810) +#define AFE_CONN060_5 (0x2814) +#define AFE_CONN060_6 (0x2818) +#define AFE_CONN060_7 (0x281c) +#define AFE_CONN061_0 (0x2820) +#define AFE_CONN061_1 (0x2824) +#define AFE_CONN061_2 (0x2828) +#define AFE_CONN061_3 (0x282c) +#define AFE_CONN061_4 (0x2830) +#define AFE_CONN061_5 (0x2834) +#define AFE_CONN061_6 (0x2838) +#define AFE_CONN061_7 (0x283c) +#define AFE_CONN062_0 (0x2840) +#define AFE_CONN062_1 (0x2844) +#define AFE_CONN062_2 (0x2848) +#define AFE_CONN062_3 (0x284c) +#define AFE_CONN062_4 (0x2850) +#define AFE_CONN062_5 (0x2854) +#define AFE_CONN062_6 (0x2858) +#define AFE_CONN062_7 (0x285c) +#define AFE_CONN063_0 (0x2860) +#define AFE_CONN063_1 (0x2864) +#define AFE_CONN063_2 (0x2868) +#define AFE_CONN063_3 (0x286c) +#define AFE_CONN063_4 (0x2870) +#define AFE_CONN063_5 (0x2874) +#define AFE_CONN063_6 (0x2878) +#define AFE_CONN063_7 (0x287c) +#define AFE_CONN064_0 (0x2880) +#define AFE_CONN064_1 (0x2884) +#define AFE_CONN064_2 (0x2888) +#define AFE_CONN064_3 (0x288c) +#define AFE_CONN064_4 (0x2890) +#define AFE_CONN064_5 (0x2894) +#define AFE_CONN064_6 (0x2898) +#define AFE_CONN064_7 (0x289c) +#define AFE_CONN065_0 (0x28a0) +#define AFE_CONN065_1 (0x28a4) +#define AFE_CONN065_2 (0x28a8) +#define AFE_CONN065_3 (0x28ac) +#define AFE_CONN065_4 (0x28b0) +#define AFE_CONN065_5 (0x28b4) +#define AFE_CONN065_6 (0x28b8) +#define AFE_CONN065_7 (0x28bc) +#define AFE_CONN066_0 (0x28c0) +#define AFE_CONN066_1 (0x28c4) +#define AFE_CONN066_2 (0x28c8) +#define AFE_CONN066_3 (0x28cc) +#define AFE_CONN066_4 (0x28d0) +#define AFE_CONN066_5 (0x28d4) +#define AFE_CONN066_6 (0x28d8) +#define AFE_CONN066_7 (0x28dc) +#define AFE_CONN067_0 (0x28e0) +#define AFE_CONN067_1 (0x28e4) +#define AFE_CONN067_2 (0x28e8) +#define AFE_CONN067_3 (0x28ec) +#define AFE_CONN067_4 (0x28f0) +#define AFE_CONN067_5 (0x28f4) +#define AFE_CONN067_6 (0x28f8) +#define AFE_CONN067_7 (0x28fc) +#define AFE_CONN068_0 (0x2900) +#define AFE_CONN068_1 (0x2904) +#define AFE_CONN068_2 (0x2908) +#define AFE_CONN068_3 (0x290c) +#define AFE_CONN068_4 (0x2910) +#define AFE_CONN068_5 (0x2914) +#define AFE_CONN068_6 (0x2918) +#define AFE_CONN068_7 (0x291c) +#define AFE_CONN069_0 (0x2920) +#define AFE_CONN069_1 (0x2924) +#define AFE_CONN069_2 (0x2928) +#define AFE_CONN069_3 (0x292c) +#define AFE_CONN069_4 (0x2930) +#define AFE_CONN069_5 (0x2934) +#define AFE_CONN069_6 (0x2938) +#define AFE_CONN069_7 (0x293c) +#define AFE_CONN070_0 (0x2940) +#define AFE_CONN070_1 (0x2944) +#define AFE_CONN070_2 (0x2948) +#define AFE_CONN070_3 (0x294c) +#define AFE_CONN070_4 (0x2950) +#define AFE_CONN070_5 (0x2954) +#define AFE_CONN070_6 (0x2958) +#define AFE_CONN070_7 (0x295c) +#define AFE_CONN071_0 (0x2960) +#define AFE_CONN071_1 (0x2964) +#define AFE_CONN071_2 (0x2968) +#define AFE_CONN071_3 (0x296c) +#define AFE_CONN071_4 (0x2970) +#define AFE_CONN071_5 (0x2974) +#define AFE_CONN071_6 (0x2978) +#define AFE_CONN071_7 (0x297c) +#define AFE_CONN072_0 (0x2980) +#define AFE_CONN072_1 (0x2984) +#define AFE_CONN072_2 (0x2988) +#define AFE_CONN072_3 (0x298c) +#define AFE_CONN072_4 (0x2990) +#define AFE_CONN072_5 (0x2994) +#define AFE_CONN072_6 (0x2998) +#define AFE_CONN072_7 (0x299c) +#define AFE_CONN073_0 (0x29a0) +#define AFE_CONN073_1 (0x29a4) +#define AFE_CONN073_2 (0x29a8) +#define AFE_CONN073_3 (0x29ac) +#define AFE_CONN073_4 (0x29b0) +#define AFE_CONN073_5 (0x29b4) +#define AFE_CONN073_6 (0x29b8) +#define AFE_CONN073_7 (0x29bc) +#define AFE_CONN074_0 (0x29c0) +#define AFE_CONN074_1 (0x29c4) +#define AFE_CONN074_2 (0x29c8) +#define AFE_CONN074_3 (0x29cc) +#define AFE_CONN074_4 (0x29d0) +#define AFE_CONN074_5 (0x29d4) +#define AFE_CONN074_6 (0x29d8) +#define AFE_CONN074_7 (0x29dc) +#define AFE_CONN075_0 (0x29e0) +#define AFE_CONN075_1 (0x29e4) +#define AFE_CONN075_2 (0x29e8) +#define AFE_CONN075_3 (0x29ec) +#define AFE_CONN075_4 (0x29f0) +#define AFE_CONN075_5 (0x29f4) +#define AFE_CONN075_6 (0x29f8) +#define AFE_CONN075_7 (0x29fc) +#define AFE_CONN076_0 (0x2a00) +#define AFE_CONN076_1 (0x2a04) +#define AFE_CONN076_2 (0x2a08) +#define AFE_CONN076_3 (0x2a0c) +#define AFE_CONN076_4 (0x2a10) +#define AFE_CONN076_5 (0x2a14) +#define AFE_CONN076_6 (0x2a18) +#define AFE_CONN076_7 (0x2a1c) +#define AFE_CONN077_0 (0x2a20) +#define AFE_CONN077_1 (0x2a24) +#define AFE_CONN077_2 (0x2a28) +#define AFE_CONN077_3 (0x2a2c) +#define AFE_CONN077_4 (0x2a30) +#define AFE_CONN077_5 (0x2a34) +#define AFE_CONN077_6 (0x2a38) +#define AFE_CONN077_7 (0x2a3c) +#define AFE_CONN078_0 (0x2a40) +#define AFE_CONN078_1 (0x2a44) +#define AFE_CONN078_2 (0x2a48) +#define AFE_CONN078_3 (0x2a4c) +#define AFE_CONN078_4 (0x2a50) +#define AFE_CONN078_5 (0x2a54) +#define AFE_CONN078_6 (0x2a58) +#define AFE_CONN078_7 (0x2a5c) +#define AFE_CONN079_0 (0x2a60) +#define AFE_CONN079_1 (0x2a64) +#define AFE_CONN079_2 (0x2a68) +#define AFE_CONN079_3 (0x2a6c) +#define AFE_CONN079_4 (0x2a70) +#define AFE_CONN079_5 (0x2a74) +#define AFE_CONN079_6 (0x2a78) +#define AFE_CONN079_7 (0x2a7c) +#define AFE_CONN080_0 (0x2a80) +#define AFE_CONN080_1 (0x2a84) +#define AFE_CONN080_2 (0x2a88) +#define AFE_CONN080_3 (0x2a8c) +#define AFE_CONN080_4 (0x2a90) +#define AFE_CONN080_5 (0x2a94) +#define AFE_CONN080_6 (0x2a98) +#define AFE_CONN080_7 (0x2a9c) +#define AFE_CONN081_0 (0x2aa0) +#define AFE_CONN081_1 (0x2aa4) +#define AFE_CONN081_2 (0x2aa8) +#define AFE_CONN081_3 (0x2aac) +#define AFE_CONN081_4 (0x2ab0) +#define AFE_CONN081_5 (0x2ab4) +#define AFE_CONN081_6 (0x2ab8) +#define AFE_CONN081_7 (0x2abc) +#define AFE_CONN082_0 (0x2ac0) +#define AFE_CONN082_1 (0x2ac4) +#define AFE_CONN082_2 (0x2ac8) +#define AFE_CONN082_3 (0x2acc) +#define AFE_CONN082_4 (0x2ad0) +#define AFE_CONN082_5 (0x2ad4) +#define AFE_CONN082_6 (0x2ad8) +#define AFE_CONN082_7 (0x2adc) +#define AFE_CONN083_0 (0x2ae0) +#define AFE_CONN083_1 (0x2ae4) +#define AFE_CONN083_2 (0x2ae8) +#define AFE_CONN083_3 (0x2aec) +#define AFE_CONN083_4 (0x2af0) +#define AFE_CONN083_5 (0x2af4) +#define AFE_CONN083_6 (0x2af8) +#define AFE_CONN083_7 (0x2afc) +#define AFE_CONN084_0 (0x2b00) +#define AFE_CONN084_1 (0x2b04) +#define AFE_CONN084_2 (0x2b08) +#define AFE_CONN084_3 (0x2b0c) +#define AFE_CONN084_4 (0x2b10) +#define AFE_CONN084_5 (0x2b14) +#define AFE_CONN084_6 (0x2b18) +#define AFE_CONN084_7 (0x2b1c) +#define AFE_CONN085_0 (0x2b20) +#define AFE_CONN085_1 (0x2b24) +#define AFE_CONN085_2 (0x2b28) +#define AFE_CONN085_3 (0x2b2c) +#define AFE_CONN085_4 (0x2b30) +#define AFE_CONN085_5 (0x2b34) +#define AFE_CONN085_6 (0x2b38) +#define AFE_CONN085_7 (0x2b3c) +#define AFE_CONN086_0 (0x2b40) +#define AFE_CONN086_1 (0x2b44) +#define AFE_CONN086_2 (0x2b48) +#define AFE_CONN086_3 (0x2b4c) +#define AFE_CONN086_4 (0x2b50) +#define AFE_CONN086_5 (0x2b54) +#define AFE_CONN086_6 (0x2b58) +#define AFE_CONN086_7 (0x2b5c) +#define AFE_CONN087_0 (0x2b60) +#define AFE_CONN087_1 (0x2b64) +#define AFE_CONN087_2 (0x2b68) +#define AFE_CONN087_3 (0x2b6c) +#define AFE_CONN087_4 (0x2b70) +#define AFE_CONN087_5 (0x2b74) +#define AFE_CONN087_6 (0x2b78) +#define AFE_CONN087_7 (0x2b7c) +#define AFE_CONN088_0 (0x2b80) +#define AFE_CONN088_1 (0x2b84) +#define AFE_CONN088_2 (0x2b88) +#define AFE_CONN088_3 (0x2b8c) +#define AFE_CONN088_4 (0x2b90) +#define AFE_CONN088_5 (0x2b94) +#define AFE_CONN088_6 (0x2b98) +#define AFE_CONN088_7 (0x2b9c) +#define AFE_CONN089_0 (0x2ba0) +#define AFE_CONN089_1 (0x2ba4) +#define AFE_CONN089_2 (0x2ba8) +#define AFE_CONN089_3 (0x2bac) +#define AFE_CONN089_4 (0x2bb0) +#define AFE_CONN089_5 (0x2bb4) +#define AFE_CONN089_6 (0x2bb8) +#define AFE_CONN089_7 (0x2bbc) +#define AFE_CONN090_0 (0x2bc0) +#define AFE_CONN090_1 (0x2bc4) +#define AFE_CONN090_2 (0x2bc8) +#define AFE_CONN090_3 (0x2bcc) +#define AFE_CONN090_4 (0x2bd0) +#define AFE_CONN090_5 (0x2bd4) +#define AFE_CONN090_6 (0x2bd8) +#define AFE_CONN090_7 (0x2bdc) +#define AFE_CONN091_0 (0x2be0) +#define AFE_CONN091_1 (0x2be4) +#define AFE_CONN091_2 (0x2be8) +#define AFE_CONN091_3 (0x2bec) +#define AFE_CONN091_4 (0x2bf0) +#define AFE_CONN091_5 (0x2bf4) +#define AFE_CONN091_6 (0x2bf8) +#define AFE_CONN091_7 (0x2bfc) +#define AFE_CONN092_0 (0x2c00) +#define AFE_CONN092_1 (0x2c04) +#define AFE_CONN092_2 (0x2c08) +#define AFE_CONN092_3 (0x2c0c) +#define AFE_CONN092_4 (0x2c10) +#define AFE_CONN092_5 (0x2c14) +#define AFE_CONN092_6 (0x2c18) +#define AFE_CONN092_7 (0x2c1c) +#define AFE_CONN093_0 (0x2c20) +#define AFE_CONN093_1 (0x2c24) +#define AFE_CONN093_2 (0x2c28) +#define AFE_CONN093_3 (0x2c2c) +#define AFE_CONN093_4 (0x2c30) +#define AFE_CONN093_5 (0x2c34) +#define AFE_CONN093_6 (0x2c38) +#define AFE_CONN093_7 (0x2c3c) +#define AFE_CONN094_0 (0x2c40) +#define AFE_CONN094_1 (0x2c44) +#define AFE_CONN094_2 (0x2c48) +#define AFE_CONN094_3 (0x2c4c) +#define AFE_CONN094_4 (0x2c50) +#define AFE_CONN094_5 (0x2c54) +#define AFE_CONN094_6 (0x2c58) +#define AFE_CONN094_7 (0x2c5c) +#define AFE_CONN095_0 (0x2c60) +#define AFE_CONN095_1 (0x2c64) +#define AFE_CONN095_2 (0x2c68) +#define AFE_CONN095_3 (0x2c6c) +#define AFE_CONN095_4 (0x2c70) +#define AFE_CONN095_5 (0x2c74) +#define AFE_CONN095_6 (0x2c78) +#define AFE_CONN095_7 (0x2c7c) +#define AFE_CONN108_0 (0x2e00) +#define AFE_CONN108_1 (0x2e04) +#define AFE_CONN108_2 (0x2e08) +#define AFE_CONN108_3 (0x2e0c) +#define AFE_CONN108_4 (0x2e10) +#define AFE_CONN108_5 (0x2e14) +#define AFE_CONN108_6 (0x2e18) +#define AFE_CONN108_7 (0x2e1c) +#define AFE_CONN109_0 (0x2e20) +#define AFE_CONN109_1 (0x2e24) +#define AFE_CONN109_2 (0x2e28) +#define AFE_CONN109_3 (0x2e2c) +#define AFE_CONN109_4 (0x2e30) +#define AFE_CONN109_5 (0x2e34) +#define AFE_CONN109_6 (0x2e38) +#define AFE_CONN109_7 (0x2e3c) +#define AFE_CONN124_0 (0x3000) +#define AFE_CONN124_1 (0x3004) +#define AFE_CONN124_2 (0x3008) +#define AFE_CONN124_3 (0x300c) +#define AFE_CONN124_4 (0x3010) +#define AFE_CONN124_5 (0x3014) +#define AFE_CONN124_6 (0x3018) +#define AFE_CONN124_7 (0x301c) +#define AFE_CONN125_0 (0x3020) +#define AFE_CONN125_1 (0x3024) +#define AFE_CONN125_2 (0x3028) +#define AFE_CONN125_3 (0x302c) +#define AFE_CONN125_4 (0x3030) +#define AFE_CONN125_5 (0x3034) +#define AFE_CONN125_6 (0x3038) +#define AFE_CONN125_7 (0x303c) +#define AFE_CONN126_0 (0x3040) +#define AFE_CONN126_1 (0x3044) +#define AFE_CONN126_2 (0x3048) +#define AFE_CONN126_3 (0x304c) +#define AFE_CONN126_4 (0x3050) +#define AFE_CONN126_5 (0x3054) +#define AFE_CONN126_6 (0x3058) +#define AFE_CONN126_7 (0x305c) +#define AFE_CONN127_0 (0x3060) +#define AFE_CONN127_1 (0x3064) +#define AFE_CONN127_2 (0x3068) +#define AFE_CONN127_3 (0x306c) +#define AFE_CONN127_4 (0x3070) +#define AFE_CONN127_5 (0x3074) +#define AFE_CONN127_6 (0x3078) +#define AFE_CONN127_7 (0x307c) +#define AFE_CONN128_0 (0x3080) +#define AFE_CONN128_1 (0x3084) +#define AFE_CONN128_2 (0x3088) +#define AFE_CONN128_3 (0x308c) +#define AFE_CONN128_4 (0x3090) +#define AFE_CONN128_5 (0x3094) +#define AFE_CONN128_6 (0x3098) +#define AFE_CONN128_7 (0x309c) +#define AFE_CONN129_0 (0x30a0) +#define AFE_CONN129_1 (0x30a4) +#define AFE_CONN129_2 (0x30a8) +#define AFE_CONN129_3 (0x30ac) +#define AFE_CONN129_4 (0x30b0) +#define AFE_CONN129_5 (0x30b4) +#define AFE_CONN129_6 (0x30b8) +#define AFE_CONN129_7 (0x30bc) +#define AFE_CONN130_0 (0x30c0) +#define AFE_CONN130_1 (0x30c4) +#define AFE_CONN130_2 (0x30c8) +#define AFE_CONN130_3 (0x30cc) +#define AFE_CONN130_4 (0x30d0) +#define AFE_CONN130_5 (0x30d4) +#define AFE_CONN130_6 (0x30d8) +#define AFE_CONN130_7 (0x30dc) +#define AFE_CONN131_0 (0x30e0) +#define AFE_CONN131_1 (0x30e4) +#define AFE_CONN131_2 (0x30e8) +#define AFE_CONN131_3 (0x30ec) +#define AFE_CONN131_4 (0x30f0) +#define AFE_CONN131_5 (0x30f4) +#define AFE_CONN131_6 (0x30f8) +#define AFE_CONN131_7 (0x30fc) +#define AFE_CONN148_0 (0x3300) +#define AFE_CONN148_1 (0x3304) +#define AFE_CONN148_2 (0x3308) +#define AFE_CONN148_3 (0x330c) +#define AFE_CONN148_4 (0x3310) +#define AFE_CONN148_5 (0x3314) +#define AFE_CONN148_6 (0x3318) +#define AFE_CONN148_7 (0x331c) +#define AFE_CONN149_0 (0x3320) +#define AFE_CONN149_1 (0x3324) +#define AFE_CONN149_2 (0x3328) +#define AFE_CONN149_3 (0x332c) +#define AFE_CONN149_4 (0x3330) +#define AFE_CONN149_5 (0x3334) +#define AFE_CONN149_6 (0x3338) +#define AFE_CONN149_7 (0x333c) +#define AFE_CONN150_0 (0x3340) +#define AFE_CONN150_1 (0x3344) +#define AFE_CONN150_2 (0x3348) +#define AFE_CONN150_3 (0x334c) +#define AFE_CONN150_4 (0x3350) +#define AFE_CONN150_5 (0x3354) +#define AFE_CONN150_6 (0x3358) +#define AFE_CONN150_7 (0x335c) +#define AFE_CONN151_0 (0x3360) +#define AFE_CONN151_1 (0x3364) +#define AFE_CONN151_2 (0x3368) +#define AFE_CONN151_3 (0x336c) +#define AFE_CONN151_4 (0x3370) +#define AFE_CONN151_5 (0x3374) +#define AFE_CONN151_6 (0x3378) +#define AFE_CONN151_7 (0x337c) +#define AFE_CONN152_0 (0x3380) +#define AFE_CONN152_1 (0x3384) +#define AFE_CONN152_2 (0x3388) +#define AFE_CONN152_3 (0x338c) +#define AFE_CONN152_4 (0x3390) +#define AFE_CONN152_5 (0x3394) +#define AFE_CONN152_6 (0x3398) +#define AFE_CONN152_7 (0x339c) +#define AFE_CONN153_0 (0x33a0) +#define AFE_CONN153_1 (0x33a4) +#define AFE_CONN153_2 (0x33a8) +#define AFE_CONN153_3 (0x33ac) +#define AFE_CONN153_4 (0x33b0) +#define AFE_CONN153_5 (0x33b4) +#define AFE_CONN153_6 (0x33b8) +#define AFE_CONN153_7 (0x33bc) +#define AFE_CONN154_0 (0x33c0) +#define AFE_CONN154_1 (0x33c4) +#define AFE_CONN154_2 (0x33c8) +#define AFE_CONN154_3 (0x33cc) +#define AFE_CONN154_4 (0x33d0) +#define AFE_CONN154_5 (0x33d4) +#define AFE_CONN154_6 (0x33d8) +#define AFE_CONN154_7 (0x33dc) +#define AFE_CONN155_0 (0x33e0) +#define AFE_CONN155_1 (0x33e4) +#define AFE_CONN155_2 (0x33e8) +#define AFE_CONN155_3 (0x33ec) +#define AFE_CONN155_4 (0x33f0) +#define AFE_CONN155_5 (0x33f4) +#define AFE_CONN155_6 (0x33f8) +#define AFE_CONN155_7 (0x33fc) +#define AFE_CONN156_0 (0x3400) +#define AFE_CONN156_1 (0x3404) +#define AFE_CONN156_2 (0x3408) +#define AFE_CONN156_3 (0x340c) +#define AFE_CONN156_4 (0x3410) +#define AFE_CONN156_5 (0x3414) +#define AFE_CONN156_6 (0x3418) +#define AFE_CONN156_7 (0x341c) +#define AFE_CONN157_0 (0x3420) +#define AFE_CONN157_1 (0x3424) +#define AFE_CONN157_2 (0x3428) +#define AFE_CONN157_3 (0x342c) +#define AFE_CONN157_4 (0x3430) +#define AFE_CONN157_5 (0x3434) +#define AFE_CONN157_6 (0x3438) +#define AFE_CONN157_7 (0x343c) +#define AFE_CONN158_0 (0x3440) +#define AFE_CONN158_1 (0x3444) +#define AFE_CONN158_2 (0x3448) +#define AFE_CONN158_3 (0x344c) +#define AFE_CONN158_4 (0x3450) +#define AFE_CONN158_5 (0x3454) +#define AFE_CONN158_6 (0x3458) +#define AFE_CONN158_7 (0x345c) +#define AFE_CONN159_0 (0x3460) +#define AFE_CONN159_1 (0x3464) +#define AFE_CONN159_2 (0x3468) +#define AFE_CONN159_3 (0x346c) +#define AFE_CONN159_4 (0x3470) +#define AFE_CONN159_5 (0x3474) +#define AFE_CONN159_6 (0x3478) +#define AFE_CONN159_7 (0x347c) +#define AFE_CONN160_0 (0x3480) +#define AFE_CONN160_1 (0x3484) +#define AFE_CONN160_2 (0x3488) +#define AFE_CONN160_3 (0x348c) +#define AFE_CONN160_4 (0x3490) +#define AFE_CONN160_5 (0x3494) +#define AFE_CONN160_6 (0x3498) +#define AFE_CONN160_7 (0x349c) +#define AFE_CONN161_0 (0x34a0) +#define AFE_CONN161_1 (0x34a4) +#define AFE_CONN161_2 (0x34a8) +#define AFE_CONN161_3 (0x34ac) +#define AFE_CONN161_4 (0x34b0) +#define AFE_CONN161_5 (0x34b4) +#define AFE_CONN161_6 (0x34b8) +#define AFE_CONN161_7 (0x34bc) +#define AFE_CONN180_0 (0x3700) +#define AFE_CONN180_1 (0x3704) +#define AFE_CONN180_2 (0x3708) +#define AFE_CONN180_3 (0x370c) +#define AFE_CONN180_4 (0x3710) +#define AFE_CONN180_5 (0x3714) +#define AFE_CONN180_6 (0x3718) +#define AFE_CONN180_7 (0x371c) +#define AFE_CONN181_0 (0x3720) +#define AFE_CONN181_1 (0x3724) +#define AFE_CONN181_2 (0x3728) +#define AFE_CONN181_3 (0x372c) +#define AFE_CONN181_4 (0x3730) +#define AFE_CONN181_5 (0x3734) +#define AFE_CONN181_6 (0x3738) +#define AFE_CONN181_7 (0x373c) +#define AFE_CONN182_0 (0x3740) +#define AFE_CONN182_1 (0x3744) +#define AFE_CONN182_2 (0x3748) +#define AFE_CONN182_3 (0x374c) +#define AFE_CONN182_4 (0x3750) +#define AFE_CONN182_5 (0x3754) +#define AFE_CONN182_6 (0x3758) +#define AFE_CONN182_7 (0x375c) +#define AFE_CONN183_0 (0x3760) +#define AFE_CONN183_1 (0x3764) +#define AFE_CONN183_2 (0x3768) +#define AFE_CONN183_3 (0x376c) +#define AFE_CONN183_4 (0x3770) +#define AFE_CONN183_5 (0x3774) +#define AFE_CONN183_6 (0x3778) +#define AFE_CONN183_7 (0x377c) +#define AFE_CONN184_0 (0x3780) +#define AFE_CONN184_1 (0x3784) +#define AFE_CONN184_2 (0x3788) +#define AFE_CONN184_3 (0x378c) +#define AFE_CONN184_4 (0x3790) +#define AFE_CONN184_5 (0x3794) +#define AFE_CONN184_6 (0x3798) +#define AFE_CONN184_7 (0x379c) +#define AFE_CONN185_0 (0x37a0) +#define AFE_CONN185_1 (0x37a4) +#define AFE_CONN185_2 (0x37a8) +#define AFE_CONN185_3 (0x37ac) +#define AFE_CONN185_4 (0x37b0) +#define AFE_CONN185_5 (0x37b4) +#define AFE_CONN185_6 (0x37b8) +#define AFE_CONN185_7 (0x37bc) +#define AFE_CONN186_0 (0x37c0) +#define AFE_CONN186_1 (0x37c4) +#define AFE_CONN186_2 (0x37c8) +#define AFE_CONN186_3 (0x37cc) +#define AFE_CONN186_4 (0x37d0) +#define AFE_CONN186_5 (0x37d4) +#define AFE_CONN186_6 (0x37d8) +#define AFE_CONN186_7 (0x37dc) +#define AFE_CONN187_0 (0x37e0) +#define AFE_CONN187_1 (0x37e4) +#define AFE_CONN187_2 (0x37e8) +#define AFE_CONN187_3 (0x37ec) +#define AFE_CONN187_4 (0x37f0) +#define AFE_CONN187_5 (0x37f4) +#define AFE_CONN187_6 (0x37f8) +#define AFE_CONN187_7 (0x37fc) +#define AFE_CONN188_0 (0x3800) +#define AFE_CONN188_1 (0x3804) +#define AFE_CONN188_2 (0x3808) +#define AFE_CONN188_3 (0x380c) +#define AFE_CONN188_4 (0x3810) +#define AFE_CONN188_5 (0x3814) +#define AFE_CONN188_6 (0x3818) +#define AFE_CONN188_7 (0x381c) +#define AFE_CONN189_0 (0x3820) +#define AFE_CONN189_1 (0x3824) +#define AFE_CONN189_2 (0x3828) +#define AFE_CONN189_3 (0x382c) +#define AFE_CONN189_4 (0x3830) +#define AFE_CONN189_5 (0x3834) +#define AFE_CONN189_6 (0x3838) +#define AFE_CONN189_7 (0x383c) +#define AFE_CONN190_0 (0x3840) +#define AFE_CONN190_1 (0x3844) +#define AFE_CONN190_2 (0x3848) +#define AFE_CONN190_3 (0x384c) +#define AFE_CONN190_4 (0x3850) +#define AFE_CONN190_5 (0x3854) +#define AFE_CONN190_6 (0x3858) +#define AFE_CONN190_7 (0x385c) +#define AFE_CONN191_0 (0x3860) +#define AFE_CONN191_1 (0x3864) +#define AFE_CONN191_2 (0x3868) +#define AFE_CONN191_3 (0x386c) +#define AFE_CONN191_4 (0x3870) +#define AFE_CONN191_5 (0x3874) +#define AFE_CONN191_6 (0x3878) +#define AFE_CONN191_7 (0x387c) +#define AFE_CONN192_0 (0x3880) +#define AFE_CONN192_1 (0x3884) +#define AFE_CONN192_2 (0x3888) +#define AFE_CONN192_3 (0x388c) +#define AFE_CONN192_4 (0x3890) +#define AFE_CONN192_5 (0x3894) +#define AFE_CONN192_6 (0x3898) +#define AFE_CONN192_7 (0x389c) +#define AFE_CONN193_0 (0x38a0) +#define AFE_CONN193_1 (0x38a4) +#define AFE_CONN193_2 (0x38a8) +#define AFE_CONN193_3 (0x38ac) +#define AFE_CONN193_4 (0x38b0) +#define AFE_CONN193_5 (0x38b4) +#define AFE_CONN193_6 (0x38b8) +#define AFE_CONN193_7 (0x38bc) +#define AFE_CONN194_0 (0x38c0) +#define AFE_CONN194_1 (0x38c4) +#define AFE_CONN194_2 (0x38c8) +#define AFE_CONN194_3 (0x38cc) +#define AFE_CONN194_4 (0x38d0) +#define AFE_CONN194_5 (0x38d4) +#define AFE_CONN194_6 (0x38d8) +#define AFE_CONN194_7 (0x38dc) +#define AFE_CONN195_0 (0x38e0) +#define AFE_CONN195_1 (0x38e4) +#define AFE_CONN195_2 (0x38e8) +#define AFE_CONN195_3 (0x38ec) +#define AFE_CONN195_4 (0x38f0) +#define AFE_CONN195_5 (0x38f4) +#define AFE_CONN195_6 (0x38f8) +#define AFE_CONN195_7 (0x38fc) +#define AFE_MEMIF_CON0 (0x4400) +#define AFE_MEMIF_ONE_HEART (0x4420) +#define AFE_DL0_BASE_MSB (0x4440) +#define AFE_DL0_BASE (0x4444) +#define AFE_DL0_CUR_MSB (0x4448) +#define AFE_DL0_CUR (0x444c) +#define AFE_DL0_END_MSB (0x4450) +#define AFE_DL0_END (0x4454) +#define AFE_DL0_RCH_MON (0x4458) +#define AFE_DL0_LCH_MON (0x445c) +#define AFE_DL0_CON0 (0x4460) +#define AFE_DL0_MON0 (0x4464) +#define AFE_DL0_MEM_UP_MSB (0x4468) +#define AFE_DL0_MEM_UP (0x446c) +#define AFE_DL1_BASE_MSB (0x4470) +#define AFE_DL1_BASE (0x4474) +#define AFE_DL1_CUR_MSB (0x4478) +#define AFE_DL1_CUR (0x447c) +#define AFE_DL1_END_MSB (0x4480) +#define AFE_DL1_END (0x4484) +#define AFE_DL1_RCH_MON (0x4488) +#define AFE_DL1_LCH_MON (0x448c) +#define AFE_DL1_CON0 (0x4490) +#define AFE_DL1_MON0 (0x4494) +#define AFE_DL1_MEM_UP_MSB (0x4498) +#define AFE_DL1_MEM_UP (0x449c) +#define AFE_DL2_BASE_MSB (0x44a0) +#define AFE_DL2_BASE (0x44a4) +#define AFE_DL2_CUR_MSB (0x44a8) +#define AFE_DL2_CUR (0x44ac) +#define AFE_DL2_END_MSB (0x44b0) +#define AFE_DL2_END (0x44b4) +#define AFE_DL2_RCH_MON (0x44b8) +#define AFE_DL2_LCH_MON (0x44bc) +#define AFE_DL2_CON0 (0x44c0) +#define AFE_DL2_MON0 (0x44c4) +#define AFE_DL2_MEM_UP_MSB (0x44c8) +#define AFE_DL2_MEM_UP (0x44cc) +#define AFE_DL3_BASE_MSB (0x44d0) +#define AFE_DL3_BASE (0x44d4) +#define AFE_DL3_CUR_MSB (0x44d8) +#define AFE_DL3_CUR (0x44dc) +#define AFE_DL3_END_MSB (0x44e0) +#define AFE_DL3_END (0x44e4) +#define AFE_DL3_RCH_MON (0x44e8) +#define AFE_DL3_LCH_MON (0x44ec) +#define AFE_DL3_CON0 (0x44f0) +#define AFE_DL3_MON0 (0x44f4) +#define AFE_DL3_MEM_UP_MSB (0x44f8) +#define AFE_DL3_MEM_UP (0x44fc) +#define AFE_DL4_BASE_MSB (0x4500) +#define AFE_DL4_BASE (0x4504) +#define AFE_DL4_CUR_MSB (0x4508) +#define AFE_DL4_CUR (0x450c) +#define AFE_DL4_END_MSB (0x4510) +#define AFE_DL4_END (0x4514) +#define AFE_DL4_RCH_MON (0x4518) +#define AFE_DL4_LCH_MON (0x451c) +#define AFE_DL4_CON0 (0x4520) +#define AFE_DL4_MON0 (0x4524) +#define AFE_DL4_MEM_UP_MSB (0x4528) +#define AFE_DL4_MEM_UP (0x452c) +#define AFE_DL5_BASE_MSB (0x4530) +#define AFE_DL5_BASE (0x4534) +#define AFE_DL5_CUR_MSB (0x4538) +#define AFE_DL5_CUR (0x453c) +#define AFE_DL5_END_MSB (0x4540) +#define AFE_DL5_END (0x4544) +#define AFE_DL5_RCH_MON (0x4548) +#define AFE_DL5_LCH_MON (0x454c) +#define AFE_DL5_CON0 (0x4550) +#define AFE_DL5_MON0 (0x4554) +#define AFE_DL5_MEM_UP_MSB (0x4558) +#define AFE_DL5_MEM_UP (0x455c) +#define AFE_DL_24CH_BASE_MSB (0x4620) +#define AFE_DL_24CH_BASE (0x4624) +#define AFE_DL_24CH_CUR_MSB (0x4628) +#define AFE_DL_24CH_CUR (0x462c) +#define AFE_DL_24CH_END_MSB (0x4630) +#define AFE_DL_24CH_END (0x4634) +#define AFE_DL_24CH_CON0 (0x4640) +#define AFE_DL_24CH_MON0 (0x4644) +#define AFE_DL_24CH_MEM_UP_MSB (0x4648) +#define AFE_DL_24CH_MEM_UP (0x464c) +#define AFE_VUL0_BASE_MSB (0x4d60) +#define AFE_VUL0_BASE (0x4d64) +#define AFE_VUL0_CUR_MSB (0x4d68) +#define AFE_VUL0_CUR (0x4d6c) +#define AFE_VUL0_END_MSB (0x4d70) +#define AFE_VUL0_END (0x4d74) +#define AFE_VUL0_RCH_MON (0x4d78) +#define AFE_VUL0_LCH_MON (0x4d7c) +#define AFE_VUL0_CON0 (0x4d80) +#define AFE_VUL0_MON0 (0x4d84) +#define AFE_VUL1_BASE_MSB (0x4d90) +#define AFE_VUL1_BASE (0x4d94) +#define AFE_VUL1_CUR_MSB (0x4d98) +#define AFE_VUL1_CUR (0x4d9c) +#define AFE_VUL1_END_MSB (0x4da0) +#define AFE_VUL1_END (0x4da4) +#define AFE_VUL1_RCH_MON (0x4da8) +#define AFE_VUL1_LCH_MON (0x4dac) +#define AFE_VUL1_CON0 (0x4db0) +#define AFE_VUL1_MON0 (0x4db4) +#define AFE_VUL2_BASE_MSB (0x4dc0) +#define AFE_VUL2_BASE (0x4dc4) +#define AFE_VUL2_CUR_MSB (0x4dc8) +#define AFE_VUL2_CUR (0x4dcc) +#define AFE_VUL2_END_MSB (0x4dd0) +#define AFE_VUL2_END (0x4dd4) +#define AFE_VUL2_RCH_MON (0x4dd8) +#define AFE_VUL2_LCH_MON (0x4ddc) +#define AFE_VUL2_CON0 (0x4de0) +#define AFE_VUL2_MON0 (0x4de4) +#define AFE_VUL3_BASE_MSB (0x4df0) +#define AFE_VUL3_BASE (0x4df4) +#define AFE_VUL3_CUR_MSB (0x4df8) +#define AFE_VUL3_CUR (0x4dfc) +#define AFE_VUL3_END_MSB (0x4e00) +#define AFE_VUL3_END (0x4e04) +#define AFE_VUL3_RCH_MON (0x4e08) +#define AFE_VUL3_LCH_MON (0x4e0c) +#define AFE_VUL3_CON0 (0x4e10) +#define AFE_VUL3_MON0 (0x4e14) +#define AFE_VUL4_BASE_MSB (0x4e20) +#define AFE_VUL4_BASE (0x4e24) +#define AFE_VUL4_CUR_MSB (0x4e28) +#define AFE_VUL4_CUR (0x4e2c) +#define AFE_VUL4_END_MSB (0x4e30) +#define AFE_VUL4_END (0x4e34) +#define AFE_VUL4_RCH_MON (0x4e38) +#define AFE_VUL4_LCH_MON (0x4e3c) +#define AFE_VUL4_CON0 (0x4e40) +#define AFE_VUL4_MON0 (0x4e44) +#define AFE_VUL5_BASE_MSB (0x4e50) +#define AFE_VUL5_BASE (0x4e54) +#define AFE_VUL5_CUR_MSB (0x4e58) +#define AFE_VUL5_CUR (0x4e5c) +#define AFE_VUL5_END_MSB (0x4e60) +#define AFE_VUL5_END (0x4e64) +#define AFE_VUL5_RCH_MON (0x4e68) +#define AFE_VUL5_LCH_MON (0x4e6c) +#define AFE_VUL5_CON0 (0x4e70) +#define AFE_VUL5_MON0 (0x4e74) +#define AFE_VUL8_BASE_MSB (0x4ee0) +#define AFE_VUL8_BASE (0x4ee4) +#define AFE_VUL8_CUR_MSB (0x4ee8) +#define AFE_VUL8_CUR (0x4eec) +#define AFE_VUL8_END_MSB (0x4ef0) +#define AFE_VUL8_END (0x4ef4) +#define AFE_VUL8_RCH_MON (0x4ef8) +#define AFE_VUL8_LCH_MON (0x4efc) +#define AFE_VUL8_CON0 (0x4f00) +#define AFE_VUL8_MON0 (0x4f04) +#define AFE_VUL9_BASE_MSB (0x4f10) +#define AFE_VUL9_BASE (0x4f14) +#define AFE_VUL9_CUR_MSB (0x4f18) +#define AFE_VUL9_CUR (0x4f1c) +#define AFE_VUL9_END_MSB (0x4f20) +#define AFE_VUL9_END (0x4f24) +#define AFE_VUL9_RCH_MON (0x4f28) +#define AFE_VUL9_LCH_MON (0x4f2c) +#define AFE_VUL9_CON0 (0x4f30) +#define AFE_VUL9_MON0 (0x4f34) +#define AFE_VUL10_BASE_MSB (0x4f40) +#define AFE_VUL10_BASE (0x4f44) +#define AFE_VUL10_CUR_MSB (0x4f48) +#define AFE_VUL10_CUR (0x4f4c) +#define AFE_VUL10_END_MSB (0x4f50) +#define AFE_VUL10_END (0x4f54) +#define AFE_VUL10_RCH_MON (0x4f58) +#define AFE_VUL10_LCH_MON (0x4f5c) +#define AFE_VUL10_CON0 (0x4f60) +#define AFE_VUL10_MON0 (0x4f64) +#define AFE_VUL_CM0_BASE_MSB (0x51c0) +#define AFE_VUL_CM0_BASE (0x51c4) +#define AFE_VUL_CM0_CUR_MSB (0x51c8) +#define AFE_VUL_CM0_CUR (0x51cc) +#define AFE_VUL_CM0_END_MSB (0x51d0) +#define AFE_VUL_CM0_END (0x51d4) +#define AFE_VUL_CM0_CON0 (0x51d8) +#define AFE_VUL_CM1_BASE_MSB (0x51e0) +#define AFE_VUL_CM1_BASE (0x51e4) +#define AFE_VUL_CM1_CUR_MSB (0x51e8) +#define AFE_VUL_CM1_CUR (0x51ec) +#define AFE_VUL_CM1_END_MSB (0x51f0) +#define AFE_VUL_CM1_END (0x51f4) +#define AFE_VUL_CM1_CON0 (0x51f8) +#define AFE_VUL_CM2_BASE_MSB (0x5200) +#define AFE_VUL_CM2_BASE (0x5204) +#define AFE_VUL_CM2_CUR_MSB (0x5208) +#define AFE_VUL_CM2_CUR (0x520c) +#define AFE_VUL_CM2_END_MSB (0x5210) +#define AFE_VUL_CM2_END (0x5214) +#define AFE_VUL_CM2_CON0 (0x5218) +#define AFE_GASRC0_NEW_CON0 (0x7840) +#define AFE_GASRC0_NEW_CON1 (0x7844) +#define AFE_GASRC0_NEW_CON2 (0x7848) +#define AFE_GASRC0_NEW_CON3 (0x784c) +#define AFE_GASRC0_NEW_CON4 (0x7850) +#define AFE_GASRC0_NEW_CON5 (0x7854) +#define AFE_GASRC0_NEW_CON6 (0x7858) +#define AFE_GASRC0_NEW_CON7 (0x785c) +#define AFE_GASRC0_NEW_CON8 (0x7860) +#define AFE_GASRC0_NEW_CON9 (0x7864) +#define AFE_GASRC0_NEW_CON10 (0x7868) +#define AFE_GASRC0_NEW_CON11 (0x786c) +#define AFE_GASRC0_NEW_CON12 (0x7870) +#define AFE_GASRC0_NEW_CON13 (0x7874) +#define AFE_GASRC0_NEW_CON14 (0x7878) +#define AFE_GASRC0_NEW_IP_VERSION (0x787c) +#define AFE_GASRC1_NEW_CON0 (0x7880) +#define AFE_GASRC1_NEW_CON1 (0x7884) +#define AFE_GASRC1_NEW_CON2 (0x7888) +#define AFE_GASRC1_NEW_CON3 (0x788c) +#define AFE_GASRC1_NEW_CON4 (0x7890) +#define AFE_GASRC1_NEW_CON5 (0x7894) +#define AFE_GASRC1_NEW_CON6 (0x7898) +#define AFE_GASRC1_NEW_CON7 (0x789c) +#define AFE_GASRC1_NEW_CON8 (0x78a0) +#define AFE_GASRC1_NEW_CON9 (0x78a4) +#define AFE_GASRC1_NEW_CON10 (0x78a8) +#define AFE_GASRC1_NEW_CON11 (0x78ac) +#define AFE_GASRC1_NEW_CON12 (0x78b0) +#define AFE_GASRC1_NEW_CON13 (0x78b4) +#define AFE_GASRC1_NEW_CON14 (0x78b8) +#define AFE_GASRC1_NEW_IP_VERSION (0x78bc) +#define AFE_GASRC2_NEW_CON0 (0x78c0) +#define AFE_GASRC2_NEW_CON1 (0x78c4) +#define AFE_GASRC2_NEW_CON2 (0x78c8) +#define AFE_GASRC2_NEW_CON3 (0x78cc) +#define AFE_GASRC2_NEW_CON4 (0x78d0) +#define AFE_GASRC2_NEW_CON5 (0x78d4) +#define AFE_GASRC2_NEW_CON6 (0x78d8) +#define AFE_GASRC2_NEW_CON7 (0x78dc) +#define AFE_GASRC2_NEW_CON8 (0x78e0) +#define AFE_GASRC2_NEW_CON9 (0x78e4) +#define AFE_GASRC2_NEW_CON10 (0x78e8) +#define AFE_GASRC2_NEW_CON11 (0x78ec) +#define AFE_GASRC2_NEW_CON12 (0x78f0) +#define AFE_GASRC2_NEW_CON13 (0x78f4) +#define AFE_GASRC2_NEW_CON14 (0x78f8) +#define AFE_GASRC2_NEW_IP_VERSION (0x78fc) +#define AFE_GASRC3_NEW_CON0 (0x7900) +#define AFE_GASRC3_NEW_CON1 (0x7904) +#define AFE_GASRC3_NEW_CON2 (0x7908) +#define AFE_GASRC3_NEW_CON3 (0x790c) +#define AFE_GASRC3_NEW_CON4 (0x7910) +#define AFE_GASRC3_NEW_CON5 (0x7914) +#define AFE_GASRC3_NEW_CON6 (0x7918) +#define AFE_GASRC3_NEW_CON7 (0x791c) +#define AFE_GASRC3_NEW_CON8 (0x7920) +#define AFE_GASRC3_NEW_CON9 (0x7924) +#define AFE_GASRC3_NEW_CON10 (0x7928) +#define AFE_GASRC3_NEW_CON11 (0x792c) +#define AFE_GASRC3_NEW_CON12 (0x7930) +#define AFE_GASRC3_NEW_CON13 (0x7934) +#define AFE_GASRC3_NEW_CON14 (0x7938) +#define AFE_GASRC3_NEW_IP_VERSION (0x793c) +#define AFE_GASRC4_NEW_CON0 (0x7940) +#define AFE_GASRC4_NEW_CON1 (0x7944) +#define AFE_GASRC4_NEW_CON2 (0x7948) +#define AFE_GASRC4_NEW_CON3 (0x794c) +#define AFE_GASRC4_NEW_CON4 (0x7950) +#define AFE_GASRC4_NEW_CON5 (0x7954) +#define AFE_GASRC4_NEW_CON6 (0x7958) +#define AFE_GASRC4_NEW_CON7 (0x795c) +#define AFE_GASRC4_NEW_CON8 (0x7960) +#define AFE_GASRC4_NEW_CON9 (0x7964) +#define AFE_GASRC4_NEW_CON10 (0x7968) +#define AFE_GASRC4_NEW_CON11 (0x796c) +#define AFE_GASRC4_NEW_CON12 (0x7970) +#define AFE_GASRC4_NEW_CON13 (0x7974) +#define AFE_GASRC4_NEW_CON14 (0x7978) +#define AFE_GASRC4_NEW_IP_VERSION (0x797c) +#define AFE_GASRC5_NEW_CON0 (0x7980) +#define AFE_GASRC5_NEW_CON1 (0x7984) +#define AFE_GASRC5_NEW_CON2 (0x7988) +#define AFE_GASRC5_NEW_CON3 (0x798c) +#define AFE_GASRC5_NEW_CON4 (0x7990) +#define AFE_GASRC5_NEW_CON5 (0x7994) +#define AFE_GASRC5_NEW_CON6 (0x7998) +#define AFE_GASRC5_NEW_CON7 (0x799c) +#define AFE_GASRC5_NEW_CON8 (0x79a0) +#define AFE_GASRC5_NEW_CON9 (0x79a4) +#define AFE_GASRC5_NEW_CON10 (0x79a8) +#define AFE_GASRC5_NEW_CON11 (0x79ac) +#define AFE_GASRC5_NEW_CON12 (0x79b0) +#define AFE_GASRC5_NEW_CON13 (0x79b4) +#define AFE_GASRC5_NEW_CON14 (0x79b8) +#define AFE_GASRC5_NEW_IP_VERSION (0x79bc) +#define AFE_GASRC6_NEW_CON0 (0x79c0) +#define AFE_GASRC6_NEW_CON1 (0x79c4) +#define AFE_GASRC6_NEW_CON2 (0x79c8) +#define AFE_GASRC6_NEW_CON3 (0x79cc) +#define AFE_GASRC6_NEW_CON4 (0x79d0) +#define AFE_GASRC6_NEW_CON5 (0x79d4) +#define AFE_GASRC6_NEW_CON6 (0x79d8) +#define AFE_GASRC6_NEW_CON7 (0x79dc) +#define AFE_GASRC6_NEW_CON8 (0x79e0) +#define AFE_GASRC6_NEW_CON9 (0x79e4) +#define AFE_GASRC6_NEW_CON10 (0x79e8) +#define AFE_GASRC6_NEW_CON11 (0x79ec) +#define AFE_GASRC6_NEW_CON12 (0x79f0) +#define AFE_GASRC6_NEW_CON13 (0x79f4) +#define AFE_GASRC6_NEW_CON14 (0x79f8) +#define AFE_GASRC6_NEW_IP_VERSION (0x79fc) +#define AFE_GASRC7_NEW_CON0 (0x7a00) +#define AFE_GASRC7_NEW_CON1 (0x7a04) +#define AFE_GASRC7_NEW_CON2 (0x7a08) +#define AFE_GASRC7_NEW_CON3 (0x7a0c) +#define AFE_GASRC7_NEW_CON4 (0x7a10) +#define AFE_GASRC7_NEW_CON5 (0x7a14) +#define AFE_GASRC7_NEW_CON6 (0x7a18) +#define AFE_GASRC7_NEW_CON7 (0x7a1c) +#define AFE_GASRC7_NEW_CON8 (0x7a20) +#define AFE_GASRC7_NEW_CON9 (0x7a24) +#define AFE_GASRC7_NEW_CON10 (0x7a28) +#define AFE_GASRC7_NEW_CON11 (0x7a2c) +#define AFE_GASRC7_NEW_CON12 (0x7a30) +#define AFE_GASRC7_NEW_CON13 (0x7a34) +#define AFE_GASRC7_NEW_CON14 (0x7a38) +#define AFE_GASRC7_NEW_IP_VERSION (0x7a3c) +#define AFE_GASRC8_NEW_CON0 (0x7a40) +#define AFE_GASRC8_NEW_CON1 (0x7a44) +#define AFE_GASRC8_NEW_CON2 (0x7a48) +#define AFE_GASRC8_NEW_CON3 (0x7a4c) +#define AFE_GASRC8_NEW_CON4 (0x7a50) +#define AFE_GASRC8_NEW_CON5 (0x7a54) +#define AFE_GASRC8_NEW_CON6 (0x7a58) +#define AFE_GASRC8_NEW_CON7 (0x7a5c) +#define AFE_GASRC8_NEW_CON8 (0x7a60) +#define AFE_GASRC8_NEW_CON9 (0x7a64) +#define AFE_GASRC8_NEW_CON10 (0x7a68) +#define AFE_GASRC8_NEW_CON11 (0x7a6c) +#define AFE_GASRC8_NEW_CON12 (0x7a70) +#define AFE_GASRC8_NEW_CON13 (0x7a74) +#define AFE_GASRC8_NEW_CON14 (0x7a78) +#define AFE_GASRC8_NEW_IP_VERSION (0x7a7c) +#define AFE_GASRC9_NEW_CON0 (0x7a80) +#define AFE_GASRC9_NEW_CON1 (0x7a84) +#define AFE_GASRC9_NEW_CON2 (0x7a88) +#define AFE_GASRC9_NEW_CON3 (0x7a8c) +#define AFE_GASRC9_NEW_CON4 (0x7a90) +#define AFE_GASRC9_NEW_CON5 (0x7a94) +#define AFE_GASRC9_NEW_CON6 (0x7a98) +#define AFE_GASRC9_NEW_CON7 (0x7a9c) +#define AFE_GASRC9_NEW_CON8 (0x7aa0) +#define AFE_GASRC9_NEW_CON9 (0x7aa4) +#define AFE_GASRC9_NEW_CON10 (0x7aa8) +#define AFE_GASRC9_NEW_CON11 (0x7aac) +#define AFE_GASRC9_NEW_CON12 (0x7ab0) +#define AFE_GASRC9_NEW_CON13 (0x7ab4) +#define AFE_GASRC9_NEW_CON14 (0x7ab8) +#define AFE_GASRC9_NEW_IP_VERSION (0x7abc) +#define AFE_GASRC10_NEW_CON0 (0x7ac0) +#define AFE_GASRC10_NEW_CON1 (0x7ac4) +#define AFE_GASRC10_NEW_CON2 (0x7ac8) +#define AFE_GASRC10_NEW_CON3 (0x7acc) +#define AFE_GASRC10_NEW_CON4 (0x7ad0) +#define AFE_GASRC10_NEW_CON5 (0x7ad4) +#define AFE_GASRC10_NEW_CON6 (0x7ad8) +#define AFE_GASRC10_NEW_CON7 (0x7adc) +#define AFE_GASRC10_NEW_CON8 (0x7ae0) +#define AFE_GASRC10_NEW_CON9 (0x7ae4) +#define AFE_GASRC10_NEW_CON10 (0x7ae8) +#define AFE_GASRC10_NEW_CON11 (0x7aec) +#define AFE_GASRC10_NEW_CON12 (0x7af0) +#define AFE_GASRC10_NEW_CON13 (0x7af4) +#define AFE_GASRC10_NEW_CON14 (0x7af8) +#define AFE_GASRC10_NEW_IP_VERSION (0x7afc) +#define AFE_GASRC11_NEW_CON0 (0x7b00) +#define AFE_GASRC11_NEW_CON1 (0x7b04) +#define AFE_GASRC11_NEW_CON2 (0x7b08) +#define AFE_GASRC11_NEW_CON3 (0x7b0c) +#define AFE_GASRC11_NEW_CON4 (0x7b10) +#define AFE_GASRC11_NEW_CON5 (0x7b14) +#define AFE_GASRC11_NEW_CON6 (0x7b18) +#define AFE_GASRC11_NEW_CON7 (0x7b1c) +#define AFE_GASRC11_NEW_CON8 (0x7b20) +#define AFE_GASRC11_NEW_CON9 (0x7b24) +#define AFE_GASRC11_NEW_CON10 (0x7b28) +#define AFE_GASRC11_NEW_CON11 (0x7b2c) +#define AFE_GASRC11_NEW_CON12 (0x7b30) +#define AFE_GASRC11_NEW_CON13 (0x7b34) +#define AFE_GASRC11_NEW_CON14 (0x7b38) +#define AFE_GASRC11_NEW_IP_VERSION (0x7b3c) +#define AFE_SOUNDWIRE_REG_BEGIN (0x9000) +#define AFE_SOUNDWIRE_REG_END (0x9600) +#define AFE_GAIN_DL0_CON0 (0x9800) +#define AFE_GAIN_DL0_CON1_R (0x9804) +#define AFE_GAIN_DL0_CON1_L (0x9808) +#define AFE_GAIN_DL0_CON2 (0x980c) +#define AFE_GAIN_DL0_CON3 (0x9810) +#define AFE_GAIN_DL0_CUR_R (0x9814) +#define AFE_GAIN_DL0_CUR_L (0x9818) +#define AFE_GAIN_DL1_CON0 (0x981c) +#define AFE_GAIN_DL1_CON1_R (0x9820) +#define AFE_GAIN_DL1_CON1_L (0x9824) +#define AFE_GAIN_DL1_CON2 (0x9828) +#define AFE_GAIN_DL1_CON3 (0x982c) +#define AFE_GAIN_DL1_CUR_R (0x9830) +#define AFE_GAIN_DL1_CUR_L (0x9834) +#define AFE_GAIN_DL2_CON0 (0x9838) +#define AFE_GAIN_DL2_CON1_R (0x983c) +#define AFE_GAIN_DL2_CON1_L (0x9840) +#define AFE_GAIN_DL2_CON2 (0x9844) +#define AFE_GAIN_DL2_CON3 (0x9848) +#define AFE_GAIN_DL2_CUR_R (0x984c) +#define AFE_GAIN_DL2_CUR_L (0x9850) +#define AFE_GAIN_DL3_CON0 (0x9854) +#define AFE_GAIN_DL3_CON1_R (0x9858) +#define AFE_GAIN_DL3_CON1_L (0x985c) +#define AFE_GAIN_DL3_CON2 (0x9860) +#define AFE_GAIN_DL3_CON3 (0x9864) +#define AFE_GAIN_DL3_CUR_R (0x9868) +#define AFE_GAIN_DL3_CUR_L (0x986c) +#define AFE_GAIN_DL4_CON0 (0x9870) +#define AFE_GAIN_DL4_CON1_R (0x9874) +#define AFE_GAIN_DL4_CON1_L (0x9878) +#define AFE_GAIN_DL4_CON2 (0x987c) +#define AFE_GAIN_DL4_CON3 (0x9880) +#define AFE_GAIN_DL4_CUR_R (0x9884) +#define AFE_GAIN_DL4_CUR_L (0x9888) +#define AFE_GAIN_DL5_CON0 (0x988c) +#define AFE_GAIN_DL5_CON1_R (0x9890) +#define AFE_GAIN_DL5_CON1_L (0x9894) +#define AFE_GAIN_DL5_CON2 (0x9898) +#define AFE_GAIN_DL5_CON3 (0x989c) +#define AFE_GAIN_DL5_CUR_R (0x98a0) +#define AFE_GAIN_DL5_CUR_L (0x98a4) +#define AFE_GAIN_DL_24CH_CON0 (0x9910) +#define AFE_GAIN_DL_24CH_CON1 (0x9914) +#define AFE_GAIN_DL_24CH_CON2 (0x9918) +#define AFE_GAIN_DL_24CH_CON3 (0x991c) +#define AFE_GAIN_DL_24CH_CUR (0x9920) +#define AFE_GAIN_DL_SYNC_CON0 (0x9924) +#define AFE_GAIN_DL_PAUSE_CON0 (0x9928) +#define AFE_GAIN_DL_PAUSE_CON1 (0x992c) +#define AFE_GAIN_VUL_PAUSE_CON0 (0x9930) +#define AFE_GAIN_DL_BYPASS_CON0 (0x9934) +#define AFE_GAIN_DL_VUL_WRAP_IP_VERSION (0x9938) +#define AFE_IRQ_MCU_EN (0x9d00) +#define AFE_IRQ_MCU_DSP_EN (0x9d04) +#define AFE_IRQ_MCU_DSP2_EN (0x9d08) +#define AFE_IRQ_MCU_SCP_EN (0x9d0c) +#define AFE_CUSTOM_IRQ_MCU_EN (0x9d10) +#define AFE_CUSTOM_IRQ_MCU_DSP_EN (0x9d14) +#define AFE_CUSTOM_IRQ_MCU_DSP2_EN (0x9d18) +#define AFE_CUSTOM_IRQ_MCU_SCP_EN (0x9d1c) +#define AFE_IRQ_MCU_STATUS (0x9d20) +#define AFE_CUSTOM_IRQ_MCU_STATUS (0x9d24) +#define AFE_IRQ0_MCU_CFG0 (0x9d40) +#define AFE_IRQ0_MCU_CFG1 (0x9d44) +#define AFE_IRQ1_MCU_CFG0 (0x9d48) +#define AFE_IRQ1_MCU_CFG1 (0x9d4c) +#define AFE_IRQ2_MCU_CFG0 (0x9d50) +#define AFE_IRQ2_MCU_CFG1 (0x9d54) +#define AFE_IRQ3_MCU_CFG0 (0x9d58) +#define AFE_IRQ3_MCU_CFG1 (0x9d5c) +#define AFE_IRQ4_MCU_CFG0 (0x9d60) +#define AFE_IRQ4_MCU_CFG1 (0x9d64) +#define AFE_IRQ5_MCU_CFG0 (0x9d68) +#define AFE_IRQ5_MCU_CFG1 (0x9d6c) +#define AFE_IRQ6_MCU_CFG0 (0x9d70) +#define AFE_IRQ6_MCU_CFG1 (0x9d74) +#define AFE_IRQ7_MCU_CFG0 (0x9d78) +#define AFE_IRQ7_MCU_CFG1 (0x9d7c) +#define AFE_IRQ8_MCU_CFG0 (0x9d80) +#define AFE_IRQ8_MCU_CFG1 (0x9d84) +#define AFE_IRQ9_MCU_CFG0 (0x9d88) +#define AFE_IRQ9_MCU_CFG1 (0x9d8c) +#define AFE_IRQ10_MCU_CFG0 (0x9d90) +#define AFE_IRQ10_MCU_CFG1 (0x9d94) +#define AFE_IRQ11_MCU_CFG0 (0x9d98) +#define AFE_IRQ11_MCU_CFG1 (0x9d9c) +#define AFE_IRQ12_MCU_CFG0 (0x9da0) +#define AFE_IRQ12_MCU_CFG1 (0x9da4) +#define AFE_IRQ13_MCU_CFG0 (0x9da8) +#define AFE_IRQ13_MCU_CFG1 (0x9dac) +#define AFE_IRQ14_MCU_CFG0 (0x9db0) +#define AFE_IRQ14_MCU_CFG1 (0x9db4) +#define AFE_IRQ15_MCU_CFG0 (0x9db8) +#define AFE_IRQ15_MCU_CFG1 (0x9dbc) +#define AFE_IRQ16_MCU_CFG0 (0x9dc0) +#define AFE_IRQ16_MCU_CFG1 (0x9dc4) +#define AFE_IRQ17_MCU_CFG0 (0x9dc8) +#define AFE_IRQ17_MCU_CFG1 (0x9dcc) +#define AFE_IRQ18_MCU_CFG0 (0x9dd0) +#define AFE_IRQ18_MCU_CFG1 (0x9dd4) +#define AFE_IRQ19_MCU_CFG0 (0x9dd8) +#define AFE_IRQ19_MCU_CFG1 (0x9ddc) +#define AFE_IRQ20_MCU_CFG0 (0x9de0) +#define AFE_IRQ20_MCU_CFG1 (0x9de4) +#define AFE_IRQ21_MCU_CFG0 (0x9de8) +#define AFE_IRQ21_MCU_CFG1 (0x9dec) +#define AFE_IRQ22_MCU_CFG0 (0x9df0) +#define AFE_IRQ22_MCU_CFG1 (0x9df4) +#define AFE_IRQ23_MCU_CFG0 (0x9df8) +#define AFE_IRQ23_MCU_CFG1 (0x9dfc) +#define AFE_IRQ24_MCU_CFG0 (0x9e00) +#define AFE_IRQ24_MCU_CFG1 (0x9e04) +#define AFE_IRQ25_MCU_CFG0 (0x9e08) +#define AFE_IRQ25_MCU_CFG1 (0x9e0c) +#define AFE_IRQ26_MCU_CFG0 (0x9e10) +#define AFE_IRQ26_MCU_CFG1 (0x9e14) +#define AFE_CUSTOM_IRQ22_MCU_CFG0 (0x9ec8) +#define AFE_CUSTOM_IRQ22_MCU_CFG1 (0x9ecc) +#define AFE_CUSTOM_IRQ23_MCU_CFG0 (0x9ed0) +#define AFE_CUSTOM_IRQ23_MCU_CFG1 (0x9ed4) +#define AFE_IRQ0_CNT_MON (0x9f10) +#define AFE_IRQ1_CNT_MON (0x9f14) +#define AFE_IRQ2_CNT_MON (0x9f18) +#define AFE_IRQ3_CNT_MON (0x9f1c) +#define AFE_IRQ4_CNT_MON (0x9f20) +#define AFE_IRQ5_CNT_MON (0x9f24) +#define AFE_IRQ6_CNT_MON (0x9f28) +#define AFE_IRQ7_CNT_MON (0x9f2c) +#define AFE_IRQ8_CNT_MON (0x9f30) +#define AFE_IRQ9_CNT_MON (0x9f34) +#define AFE_IRQ10_CNT_MON (0x9f38) +#define AFE_IRQ11_CNT_MON (0x9f3c) +#define AFE_IRQ12_CNT_MON (0x9f40) +#define AFE_IRQ13_CNT_MON (0x9f44) +#define AFE_IRQ14_CNT_MON (0x9f48) +#define AFE_IRQ15_CNT_MON (0x9f4c) +#define AFE_IRQ16_CNT_MON (0x9f50) +#define AFE_IRQ17_CNT_MON (0x9f54) +#define AFE_IRQ18_CNT_MON (0x9f58) +#define AFE_IRQ19_CNT_MON (0x9f5c) +#define AFE_IRQ20_CNT_MON (0x9f60) +#define AFE_IRQ21_CNT_MON (0x9f64) +#define AFE_IRQ22_CNT_MON (0x9f68) +#define AFE_IRQ23_CNT_MON (0x9f6c) +#define AFE_IRQ24_CNT_MON (0x9f70) +#define AFE_IRQ25_CNT_MON (0x9f74) +#define AFE_IRQ26_CNT_MON (0x9f78) +#define AFE_IRQ_MCU_DSP3_EN (0xa000) +#define AFE_CUSTOM_IRQ_MCU_DSP3_EN (0xa004) +#define AFE_CUSTOM2_IRQ_MCU_EN (0xa008) +#define AFE_CUSTOM2_IRQ_MCU_DSP_EN (0xa00c) +#define AFE_CUSTOM2_IRQ_MCU_DSP2_EN (0xa010) +#define AFE_CUSTOM2_IRQ_MCU_DSP3_EN (0xa014) +#define AFE_CUSTOM2_IRQ_MCU_SCP_EN (0xa018) +#define AFE_IRQ_MCU_MON3 (0xa01c) +#define AFE_IRQ_MCU_MON0 (0xa024) +#define AFE_IRQ_MCU_MON1 (0xa028) +#define AFE_IRQ_MCU_MON2 (0xa02c) + +#define AFE_MAX_REGISTER (AFE_IRQ_MCU_MON2) + +#define AUDIO_TOP_FPGA_RG1 (0x76a8) + +#define AFE_SOUNDWIRE_PDI_MON_TX_LR (0x955c) +#define AFE_SOUNDWIRE_PDI_MON_TX_OUT (0x9560) +#define AFE_SOUNDWIRE_PDI_MON_INC (0x956c) +#define AFE_SOUNDWIRE_PDI_MON (0x9570) + +/* AUDIO_TOP_CON0 */ +#define AUDIO_TOP_CON0_PDN_HW_GAIN01 BIT(21) +#define AUDIO_TOP_CON0_PDN_HW_GAIN23 BIT(20) +#define AUDIO_TOP_CON0_PDN_CM0 BIT(18) +#define AUDIO_TOP_CON0_PDN_CM1 BIT(17) +#define AUDIO_TOP_CON0_PDN_CM2 BIT(16) + +/* AUDIO_TOP_CON1 */ +#define AUDIO_TOP_CON1_PDN_HW_GAIN_DL0_DL1 BIT(30) +#define AUDIO_TOP_CON1_PDN_HW_GAIN_DL2_DL3 BIT(29) +#define AUDIO_TOP_CON1_PDN_HW_GAIN_DL4_DL5 BIT(28) +#define AUDIO_TOP_CON1_PDN_HW_GAIN_DL_24CH BIT(24) +#define AUDIO_TOP_CON1_PDN_UL0_ADC BIT(23) +#define AUDIO_TOP_CON1_PDN_UL0_TML BIT(22) +#define AUDIO_TOP_CON1_PDN_UL0_ADC_HIRES BIT(21) +#define AUDIO_TOP_CON1_PDN_UL0_TML_HIRES BIT(20) +#define AUDIO_TOP_CON1_PDN_UL1_ADC BIT(19) +#define AUDIO_TOP_CON1_PDN_UL1_TML BIT(18) +#define AUDIO_TOP_CON1_PDN_UL1_ADC_HIRES BIT(17) +#define AUDIO_TOP_CON1_PDN_UL1_TML_HIRES BIT(16) + +/* AUDIO_TOP_CON2 */ +#define AUDIO_TOP_CON2_PDN_ETDM_OUT0 BIT(21) +#define AUDIO_TOP_CON2_PDN_ETDM_OUT5 BIT(16) +#define AUDIO_TOP_CON2_PDN_ETDM_IN0 BIT(13) +#define AUDIO_TOP_CON2_PDN_ETDM_IN5 BIT(8) + +/* AUDIO_TOP_CON3 */ +#define AUDIO_TOP_CON3_PDN_GASRC0 BIT(24) +#define AUDIO_TOP_CON3_PDN_GASRC1 BIT(23) +#define AUDIO_TOP_CON3_PDN_GASRC2 BIT(22) +#define AUDIO_TOP_CON3_PDN_GASRC3 BIT(21) +#define AUDIO_TOP_CON3_PDN_GASRC4 BIT(20) +#define AUDIO_TOP_CON3_PDN_GASRC5 BIT(19) +#define AUDIO_TOP_CON3_PDN_GASRC6 BIT(18) +#define AUDIO_TOP_CON3_PDN_GASRC7 BIT(17) + +/* AUDIO_TOP_CON4 */ +#define AUDIO_TOP_CON4_PDN_APLL_TUNER1 BIT(13) +#define AUDIO_TOP_CON4_PDN_APLL_TUNER2 BIT(12) +#define AUDIO_TOP_CON4_PDN_H208M BIT(4) +#define AUDIO_TOP_CON4_PDN_APLL2 BIT(3) +#define AUDIO_TOP_CON4_PDN_APLL1 BIT(2) +#define AUDIO_TOP_CON4_PDN_F26M BIT(1) +#define AUDIO_TOP_CON4_PDN_HOPPING BIT(0) + +/* AUDIO_TOP_CON5 */ +#define AUDIO_TOP_CON5_SOUNDWIRE1 BIT(1) +#define AUDIO_TOP_CON5_SOUNDWIRE0 BIT(0) + +/* AUDIO_ENGEN_CON0 */ +#define AUDIO_ENGEN_CON0_ON_APLL2 BIT(3) +#define AUDIO_ENGEN_CON0_APLL2_MASK BIT(3) +#define AUDIO_ENGEN_CON0_ON_APLL1 BIT(2) +#define AUDIO_ENGEN_CON0_APLL1_MASK BIT(2) +#define AUDIO_ENGEN_CON0_ON_F3P25M BIT(1) +#define AUDIO_ENGEN_CON0_F3P25M_MASK BIT(1) +#define AUDIO_ENGEN_CON0_ON_26M BIT(0) +#define AUDIO_ENGEN_CON0_26M_MASK BIT(0) + +/* AFE_SINEGEN_CON0 */ +#define SINEGEN_DAC_EN_SET(x) (((x) & 0x1) << 26) +#define SINEGEN_DAC_EN_MASK BIT(26) +#define SINEGEN_TIE_SW_CH2_SET(x) (((x) & 0x1) << 25) +#define SINEGEN_TIE_SW_CH2_MASK BIT(25) +#define SINEGEN_TIE_SW_CH1_SET(x) (((x) & 0x1) << 24) +#define SINEGEN_TIE_SW_CH1_MASK BIT(24) +#define SINEGEN_AMP_DIV_CH2_SET(x) (((x) & 0xf) << 20) +#define SINEGEN_AMP_DIV_CH2_MASK (0xfUL << 20) +#define SINEGEN_FREQ_DIV_CH2_SET(x) (((x) & 0x1f) << 12) +#define SINEGEN_FREQ_DIV_CH2_MASK (0x1fUL << 12) +#define SINEGEN_AMP_DIV_CH1_SET(x) (((x) & 0xf) << 8) +#define SINEGEN_AMP_DIV_CH1_MASK (0xfUL << 8) +#define SINEGEN_FREQ_DIV_CH1_SET(x) (((x) & 0x1f) << 0) +#define SINEGEN_FREQ_DIV_CH1_MASK (0x1fUL << 0) + +/* AFE_SINEGEN_CON1 */ +#define SINEGEN_SINE_MODE_SET(x) (((x) & 0x1f) << 12) +#define SINEGEN_SINE_MODE_MASK (0x1fUL << 12) +#define SINEGEN_INNER_LPBK_SEL_SET(x) (((x) & 0x1UL) << 8) +#define SINEGEN_INNER_LPBK_SEL_MASK (0x1UL << 8) +#define SINEGEN_INNER_LPBK_MODE_SET(x) (((x) & 0xff) << 0) +#define SINEGEN_INNER_LPBK_MODE_MASK (0xffUL << 0) + +/* AFE_APLLx_TUNER_CFG */ +#define AUDIO_APLL_TUNER_CFG_ON BIT(0) +#define AUDIO_APLL_TUNER_CFG_XTAL_SEL_SET(x) (((x) & 0x3) << 1) +#define AUDIO_APLL_TUNER_CFG_XTAL_SEL_MASK (0x3UL << 1) +#define AUDIO_APLL_TUNER_CFG_APLL_DIV_SET(x) (((x) & 0xf) << 4) +#define AUDIO_APLL_TUNER_CFG_APLL_DIV_MASK (0xfUL << 4) +#define AUDIO_APLL_TUNER_CFG_UPPER_BOUND_SET(x) (((x) & 0xf) << 8) +#define AUDIO_APLL_TUNER_CFG_UPPER_BOUND_MASK (0xfUL << 8) + +/* AFE_ADDA_ULSRC_PHASE_CLK_CON0 */ +#define DMIC_CLK_PHASE_SYNC_SET_MODE(x) (((x) & 0x1UL) << 31) +#define DMIC_CLK_PHASE_SYNC_SET_MASK BIT(31) + +/* AFE_ADDA_ULSRC_PHASE_CLK_CON1 */ +#define UL1_PHASE_SYNC_HCLK_SET_MODE(x) (((x) & 0x1) << 19) +#define UL1_PHASE_SYNC_HCLK_SET_MASK BIT(19) +#define UL1_PHASE_SYNC_FCLK_SET_MODE(x) (((x) & 0x1) << 18) +#define UL1_PHASE_SYNC_FCLK_SET_MASK BIT(18) +#define UL0_PHASE_SYNC_HCLK_SET_MODE(x) (((x) & 0x1) << 17) +#define UL0_PHASE_SYNC_HCLK_SET_MASK BIT(17) +#define UL0_PHASE_SYNC_FCLK_SET_MODE(x) (((x) & 0x1) << 16) +#define UL0_PHASE_SYNC_FCLK_SET_MASK BIT(16) + +/* AFE_ADDA_ULSRC_PHASE_ENGEN_CON0 */ +#define UL_PHASE_SYNC_HCLK_1X_EN_3_ON_SET(x) (((x) & 0x1) << 11) +#define UL_PHASE_SYNC_HCLK_1X_EN_3_ON_MASK BIT(11) +#define UL_PHASE_SYNC_HCLK_1X_EN_2_ON_SET(x) (((x) & 0x1) << 10) +#define UL_PHASE_SYNC_HCLK_1X_EN_2_ON_MASK BIT(10) +#define UL_PHASE_SYNC_HCLK_1X_EN_1_ON_SET(x) (((x) & 0x1) << 9) +#define UL_PHASE_SYNC_HCLK_1X_EN_1_ON_MASK BIT(9) +#define UL_PHASE_SYNC_HCLK_1X_EN_0_ON_SET(x) (((x) & 0x1) << 8) +#define UL_PHASE_SYNC_HCLK_1X_EN_0_ON_MASK BIT(8) +#define UL_PHASE_SYNC_FCLK_1X_EN_3_ON_SET(x) (((x) & 0x1) << 7) +#define UL_PHASE_SYNC_FCLK_1X_EN_3_ON_MASK BIT(7) +#define UL_PHASE_SYNC_FCLK_1X_EN_2_ON_SET(x) (((x) & 0x1) << 6) +#define UL_PHASE_SYNC_FCLK_1X_EN_2_ON_MASK BIT(6) +#define UL_PHASE_SYNC_FCLK_1X_EN_1_ON_SET(x) (((x) & 0x1) << 5) +#define UL_PHASE_SYNC_FCLK_1X_EN_1_ON_MASK BIT(5) +#define UL_PHASE_SYNC_FCLK_1X_EN_0_ON_SET(x) (((x) & 0x1) << 4) +#define UL_PHASE_SYNC_FCLK_1X_EN_0_ON_MASK BIT(4) + +/* AFE_ADDA_ULSRC_PHASE_ENGEN_CON1 */ +#define UL1_PHASE_SYNC_1X_EN_SEL(x) (((x) & 0x7) << 11) +#define UL1_PHASE_SYNC_1X_EN_SEL_MASK (0x7UL << 11) +#define UL0_PHASE_SYNC_1X_EN_SEL(x) (((x) & 0x7) << 8) +#define UL0_PHASE_SYNC_1X_EN_SEL_MASK (0x7UL << 8) + +/* ETDM_INx_CON8 */ +#define ETDM_IN_USE_AFIFO BIT(8) +#define ETDM_IN_AFIFO_CLOCK(x) ((x) << 5) +#define ETDM_IN_AFIFO_CLOCK_MASK (0x7UL << 5) +#define ETDM_IN_AFIFO_MODE(x) (x) +#define ETDM_IN_AFIFO_MODE_MASK (0x1f) + +/* ETDM_0_3_COWORK_CON0 */ +#define ETDM_IN0_SYNC_SEL(x) ((x) << 28) +#define ETDM_IN0_SYNC_SEL_MASK (0xfUL << 28) +#define ETDM_IN0_SYNC_SEL_SHIFT 28 +#define ETDM_IN0_SLAVE_SEL(x) ((x) << 24) +#define ETDM_IN0_SLAVE_SEL_MASK (0xfUL << 24) +#define ETDM_IN0_SLAVE_SEL_SHIFT 24 +#define ETDM_OUT0_SLAVE_SEL(x) ((x) << 8) +#define ETDM_OUT0_SLAVE_SEL_MASK (0xfUL << 8) +#define ETDM_OUT0_SLAVE_SEL_SHIFT 8 +#define ETDM_OUT0_SYNC_SEL(x) ((x) << 4) +#define ETDM_OUT0_SYNC_SEL_MASK (0xfUL << 4) +#define ETDM_OUT0_SYNC_SEL_SHIFT 4 + +/* ETDM_0_3_COWORK_CON1 */ +#define ETDM_IN0_SDATA1_15_SEL(x) ((x) << 4) +#define ETDM_IN0_SDATA1_15_SEL_MASK (0xfUL << 4) +#define ETDM_IN0_SDATA1_15_SEL_SHIFT 4 +#define ETDM_IN0_SDATA0_SEL(x) ((x) << 0) +#define ETDM_IN0_SDATA0_SEL_MASK (0xfUL << 0) +#define ETDM_IN0_SDATA0_SEL_SHIFT 0 + +/* ETDM_4_7_COWORK_CON0 */ +#define ETDM_OUT5_SLAVE_SEL(x) ((x) << 20) +#define ETDM_OUT5_SLAVE_SEL_MASK (0xfUL << 20) +#define ETDM_OUT5_SLAVE_SEL_SHIFT 20 +#define ETDM_OUT5_SYNC_SEL(x) ((x) << 16) +#define ETDM_OUT5_SYNC_SEL_MASK (0xfUL << 16) +#define ETDM_OUT5_SYNC_SEL_SHIFT 16 + +/* ETDM_4_7_COWORK_CON1 */ +#define ETDM_IN5_SYNC_SEL(x) ((x) << 12) +#define ETDM_IN5_SYNC_SEL_MASK (0xfUL << 12) +#define ETDM_IN5_SYNC_SEL_SHIFT 12 +#define ETDM_IN5_SLAVE_SEL(x) ((x) << 8) +#define ETDM_IN5_SLAVE_SEL_MASK (0xfUL << 8) +#define ETDM_IN5_SLAVE_SEL_SHIFT 8 + +/* ETDM_x_CONx */ +#define ETDM_CON0_RELATCH_DOMAIN(x) ((x) << 28) +#define ETDM_CON0_RELATCH_DOMAIN_MASK (0x7UL << 28) +#define ETDM_CON0_CH_NUM(x) \ + ({ typeof(x) _x = (x); (_x > 0) ? ((_x - 1) << 23) : 0; }) +#define ETDM_CON0_CH_NUM_MASK (0x1fUL << 23) +#define ETDM_CON0_WORD_LEN(x) \ + ({ typeof(x) _x = (x); (_x > 0) ? ((_x - 1) << 16) : 0; }) +#define ETDM_CON0_WORD_LEN_MASK (0x1fUL << 16) +#define ETDM_CON0_BIT_LEN(x) \ + ({ typeof(x) _x = (x); (_x > 0) ? ((_x - 1) << 11) : 0; }) +#define ETDM_CON0_BIT_LEN_MASK (0x1fUL << 11) +#define ETDM_CON0_FORMAT(x) ((x) << 6) +#define ETDM_CON0_FORMAT_MASK (0x7UL << 6) +#define ETDM_CON0_SLAVE_MODE BIT(5) +#define ETDM_CON0_SYNC_MODE BIT(1) +#define ETDM_CON0_EN BIT(0) + +#define ETDM_CON1_MCLK_OUTPUT BIT(16) +#define ETDM_CON1_INITIAL_POINT(x) ((x) << 5) +#define ETDM_CON1_INITIAL_POINT_MASK (0x1f << 5) + +#define ETDM_IN_CON1_LRCK_AUTO_MODE BIT(31) +#define ETDM_IN_CON1_LRCK_WIDTH(x) \ + ({ typeof(x) _x = (x); (_x > 0) ? ((_x - 1) << 20) : 0; }) +#define ETDM_IN_CON1_LRCK_WIDTH_MASK (0x3ffUL << 20) +#define ETDM_OUT_CON1_LRCK_AUTO_MODE BIT(29) +#define ETDM_OUT_CON1_LRCK_WIDTH(x) \ + ({ typeof(x) _x = (x); (_x > 0) ? ((_x - 1) << 19) : 0; }) +#define ETDM_OUT_CON1_LRCK_WIDTH_MASK (0x3ffUL << 19) + +#define ETDM_CON2_LRCK_DELAY_BCK_INV BIT(30) +#define ETDM_CON2_LRCK_DELAY_0P5T_EN BIT(29) +#define ETDM_CON2_SDATA_DELAY_0P5T_EN BIT(27) + +#define ETDM_OUT_CON2_IN2LATCH_TIME_MINUS3(x) (((x) - 3) & 0x1f) +#define ETDM_OUT_CON2_IN2LATCH_TIME(x) ((x) & 0x1f) +#define ETDM_OUT_CON2_IN2LATCH_TIME_MASK 0x1f + +#define ETDM_IN_CON1_REG_OUT_EN(x) (((x) & 0x1) << 18) +#define ETDM_IN_CON1_REG_OUT_EN_MASK BIT(18) + +#define ETDM_IN_CON2_MULTI_IP_2CH_MODE BIT(31) +#define ETDM_IN_CON2_MULTI_IP_TOTAL_CH(x) \ + ({ typeof(x) _x = (x); (_x > 0) ? ((_x - 1) << 15) : 0; }) +#define ETDM_IN_CON2_MULTI_IP_TOTAL_CH_MASK (0x1fUL << 15) +#define ETDM_IN_CON2_CLOCK(x) ((x) << 10) +#define ETDM_IN_CON2_CLOCK_MASK (0x7UL << 10) +#define ETDM_IN_CON2_CLOCK_SHIFT 10 +#define ETDM_IN_CON2_UPDATE_GAP(x) ((x) << 5) +#define ETDM_IN_CON2_UPDATE_GAP_MASK (0x1fUL << 5) + +#define ETDM_IN_CON3_FS(x) ((x) << 26) +#define ETDM_IN_CON3_FS_MASK (0x1fUL << 26) +#define ETDM_IN_CON3_FS_SHIFT 26 +#define ETDM_IN_CON3_DISABLE_OUT(x) (1UL << ((x) & 0xf)) +#define ETDM_IN_CON3_DISABLE_OUT_MASK (0xffff) + +#define ETDM_IN_CON4_MASTER_LRCK_INV BIT(19) +#define ETDM_IN_CON4_MASTER_BCK_INV BIT(18) +#define ETDM_IN_CON4_SLAVE_LRCK_INV BIT(17) +#define ETDM_IN_CON4_SLAVE_BCK_INV BIT(16) +#define ETDM_IN_CON4_RELATCH_EN(x) ((x) << 20) +#define ETDM_IN_CON4_RELATCH_EN_MASK (0x1fUL << 20) + +#define ETDM_OUT_CON4_RELATCH_EN(x) ((x) << 24) +#define ETDM_OUT_CON4_RELATCH_EN_MASK (0x1fUL << 24) +#define ETDM_OUT_CON4_CLOCK(x) ((x) << 6) +#define ETDM_OUT_CON4_CLOCK_MASK (0x7UL << 6) +#define ETDM_OUT_CON4_CLOCK_SHIFT 6 +#define ETDM_OUT_CON4_FS(x) (x) +#define ETDM_OUT_CON4_FS_MASK (0x1f) + +#define ETDM_IN_CON5_LR_SWAP(x) BIT(((x) & 0xf) + 16) +#define ETDM_IN_CON5_LR_SWAP_MASK (0xffffUL << 16) +#define ETDM_IN_CON5_ENABLE_ODD(x) BIT((x) & 0xf) +#define ETDM_IN_CON5_ENABLE_ODD_MASK (0xffff) + +#define ETDM_OUT_CON5_MASTER_LRCK_INV BIT(10) +#define ETDM_OUT_CON5_MASTER_BCK_INV BIT(9) +#define ETDM_OUT_CON5_SLAVE_LRCK_INV BIT(8) +#define ETDM_OUT_CON5_SLAVE_BCK_INV BIT(7) + +#define ETDM_IN_CON9_OUT2LATCH_TIME(x) ((x) << 10) +#define ETDM_IN_CON9_OUT2LATCH_TIME_MASK (0x1fUL << 10) + +/* AFE_ADDA_ULx_SRC_CON0 */ +#define AFE_ADDA_UL_SRC_CON0_ULCF_CFG_DEFAULT (0x0) +#define AFE_ADDA_UL_SRC_CON0_ULCF_CFG_EN_CTL_MASK (0x80000000) +#define AFE_ADDA_UL_SRC_CON0_UL_26M_SEL (0x0) +#define AFE_ADDA_UL_SRC_CON0_UL_26M_APLL_SEL_MASK (0x40000000) +#define AFE_ADDA_UL_SRC_CON0_UL_PHASE_SEL_CH1(x) (((x) & 0x7) << 27) +#define AFE_ADDA_UL_SRC_CON0_UL_PHASE_SEL_CH2(x) (((x) & 0x7) << 24) +#define AFE_ADDA_UL_SRC_CON0_UL_PHASE_SEL_MASK (0x3f000000) +#define AFE_ADDA_UL_SRC_CON0_UL_TWO_WIRE_MODE_CTL (0x800000) +#define AFE_ADDA_UL_SRC_CON0_UL_TWO_WIRE_MODE_CTL_MASK (0x800000) +#define AFE_ADDA_UL_SRC_CON0_UL_MODE_3P25M_CH2_CTL (0x400000) +#define AFE_ADDA_UL_SRC_CON0_UL_MODE_3P25M_CH1_CTL (0x200000) +#define AFE_ADDA_UL_VOICE_MODE(x) (((x) & 0x7) << 17) +#define AFE_ADDA_UL_VOICE_MODE_MASK (0xe0000) +#define AFE_ADDA_UL_CON0_VOCIE_MODE_8K AFE_ADDA_UL_VOICE_MODE(0) +#define AFE_ADDA_UL_CON0_VOCIE_MODE_16K AFE_ADDA_UL_VOICE_MODE(1) +#define AFE_ADDA_UL_CON0_VOCIE_MODE_32K AFE_ADDA_UL_VOICE_MODE(2) +#define AFE_ADDA_UL_CON0_VOCIE_MODE_48K AFE_ADDA_UL_VOICE_MODE(3) +#define AFE_ADDA_UL_CON0_VOCIE_MODE_96K AFE_ADDA_UL_VOICE_MODE(4) +#define AFE_ADDA_UL_CON0_VOCIE_MODE_192K AFE_ADDA_UL_VOICE_MODE(5) +#define AFE_ADDA_UL_SRC_CON0_UL_AP_DMIC_ON (0x10000) +#define AFE_ADDA_UL_SRC_CON0_UL_AP_DMIC_ON_MASK (0x10000) +#define AFE_ADDA_UL_DMIC_LP_MODE(x) (((x) & 0x3) << 14) +#define AFE_ADDA_UL_DMIC_LP_MODE_MASK (0xc000) +#define AFE_ADDA_UL_DMIC_LP_MODE_3D25M AFE_ADDA_UL_DMIC_LP_MODE(0) +#define AFE_ADDA_UL_DMIC_LP_MODE_1D625M AFE_ADDA_UL_DMIC_LP_MODE(1) +#define AFE_ADDA_UL_DMIC_LP_MODE_8D125K AFE_ADDA_UL_DMIC_LP_MODE(2) +#define AFE_ADDA_UL_DMIC_LP_MODE_4D0625K AFE_ADDA_UL_DMIC_LP_MODE(3) +#define AFE_ADDA_UL_SRC_CON0_UL_IIR_MODE_CTL(x) (((x) & 0x7) << 7) +#define AFE_ADDA_UL_SRC_CON0_UL_IIR_MODE_CTL_MASK (0x380) +#define AFE_ADDA_UL_SRC_CON0_UL_IIR_ON_TMP_CTL (0x400) +#define AFE_ADDA_UL_SRC_CON0_UL_IIR_ON_TMP_CTL_MASK (0x400) +#define AFE_ADDA_UL_SRC_CON0_UL_DMIC_4P33M_SEL (0x40) +#define AFE_ADDA_UL_SRC_CON0_UL_DMIC_4P33M_SEL_MASK (0x40) +#define AFE_ADDA_UL_SRC_CON0_UL_DMIC_1P625M_SEL (0x20) +#define AFE_ADDA_UL_SRC_CON0_UL_DMIC_1P625M_SEL_MASK (0x20) +#define AFE_ADDA_UL_SRC_CON0_UL_SDM_3_LEVEL_CTL (0x2) +#define AFE_ADDA_UL_SRC_CON0_UL_SRC_ON_TMP_CTL (0x1) + +/* AFE_ADDA_ULx_SRC_CON2 */ +#define AFE_ADDA_UL_SRC_SINEGEN_DAC_EN_SET(x) (((x) & 0x1) << 27) +#define AFE_ADDA_UL_SRC_SINEGEN_DAC_EN_MASK BIT(27) +#define AFE_ADDA_UL_SRC_SINEGEN_AMP_DIV_CH2_SET(x) (((x) & 0x7) << 21) +#define AFE_ADDA_UL_SRC_SINEGEN_AMP_DIV_CH2_MASK (0x7UL << 21) +#define AFE_ADDA_UL_SRC_SINEGEN_FREQ_DIV_CH2_SET(x) (((x) & 0x1f) << 16) +#define AFE_ADDA_UL_SRC_SINEGEN_FREQ_DIV_CH2_MASK (0x1fUL << 16) +#define AFE_ADDA_UL_SRC_SINEGEN_SINE_MODE_CH2_SET(x) (((x) & 0xf) << 12) +#define AFE_ADDA_UL_SRC_SINEGEN_SINE_MODE_CH2_MASK (0xfUL << 12) +#define AFE_ADDA_UL_SRC_SINEGEN_AMP_DIV_CH1_SET(x) (((x) & 0x7) << 9) +#define AFE_ADDA_UL_SRC_SINEGEN_AMP_DIV_CH1_MASK (0x7UL << 9) +#define AFE_ADDA_UL_SRC_SINEGEN_FREQ_DIV_CH1_SET(x) (((x) & 0x1f) << 4) +#define AFE_ADDA_UL_SRC_SINEGEN_FREQ_DIV_CH1_MASK (0x1fUL << 4) +#define AFE_ADDA_UL_SRC_SINEGEN_SINE_MODE_CH1_SET(x) (((x) & 0xf) << 0) +#define AFE_ADDA_UL_SRC_SINEGEN_SINE_MODE_CH1_MASK (0xfUL << 0) + +/* AUDIO_TOP_FPGA_RG1 */ +#define AUDIO_TOP_FPGA_RG1_PINMUX_TO_DBG_BOARD (0x0 << 4) +#define AUDIO_TOP_FPGA_RG1_PINMUX_TO_AUDIO_DTB BIT(4) +#define AUDIO_TOP_FPGA_RG1_PINMUX_MASK BIT(4) + +/* AFE_GASRCx_NEW_CON0 */ +#define GASRC_CON0_ONE_HEART_MASK BIT(31) +#define GASRC_CON0_ONE_HEART(x) ((x) << 31) +#define GASRC_CON0_CHSET0_OFS_ONE_HEART_DISABLE_MASK BIT(30) +#define GASRC_CON0_CHSET0_OFS_ONE_HEART_DISABLE(x) ((x) << 30) +#define GASRC_CON0_USE_SHORT_DELAY_COEFF_MASK BIT(29) +#define GASRC_CON0_USE_SHORT_DELAY_COEFF(x) ((x) << 29) +#define GASRC_CON0_CHSET0_O16BIT_MASK BIT(19) +#define GASRC_CON0_CHSET0_O16BIT(x) ((x) << 19) +#define GASRC_CON0_CHSET0_CLR_IIR_HISTORY BIT(17) +#define GASRC_CON0_CHSET0_IS_MONO_MASK BIT(16) +#define GASRC_CON0_CHSET0_IS_MONO(x) ((x) << 16) +#define GASRC_CON0_CHSET0_OFS_SEL_MASK GENMASK(15, 14) +#define GASRC_CON0_CHSET0_OFS_SEL_TX (0 << 14) +#define GASRC_CON0_CHSET0_OFS_SEL_RX BIT(14) +#define GASRC_CON0_CHSET0_IFS_SEL_MASK GENMASK(13, 12) +#define GASRC_CON0_CHSET0_IFS_SEL_TX (3 << 12) +#define GASRC_CON0_CHSET0_IFS_SEL_RX (2 << 12) +#define GASRC_CON0_CHSET0_IIR_EN BIT(11) +#define GASRC_CON0_CHSET0_IIR_STAGE_MASK GENMASK(10, 8) +#define GASRC_CON0_CHSET0_IIR_STAGE(x) (((x) - 1) << 8) +#define GASRC_CON0_CHSET_STR_CLR BIT(4) +#define GASRC_CON0_COEFF_SRAM_CTRL BIT(1) +#define GASRC_CON0_ASM_ON BIT(0) + +/* AFE_GASRCx_NEW_CON1 ~ CON4 */ +#define GASRC_CON1_ASM_FREQ_0_MASK GENMASK(23, 0) +#define GASRC_CON2_ASM_FREQ_1_MASK GENMASK(23, 0) +#define GASRC_CON3_ASM_FREQ_2_MASK GENMASK(23, 0) +#define GASRC_CON4_ASM_FREQ_3_MASK GENMASK(23, 0) + +/* AFE_GASRCx_NEW_CON5 */ +#define GASRC_CON5_OUT_EN_SEL_DOMAIN_MASK GENMASK(31, 29) +#define GASRC_CON5_OUT_EN_SEL_DOMAIN(x) ((x) << 29) +#define GASRC_CON5_OUT_EN_SEL_FS_MASK GENMASK(28, 24) +#define GASRC_CON5_OUT_EN_SEL_FS(x) ((x) << 24) +#define GASRC_CON5_IN_EN_SEL_DOMAIN_MASK GENMASK(23, 21) +#define GASRC_CON5_IN_EN_SEL_DOMAIN(x) ((x) << 21) +#define GASRC_CON5_IN_EN_SEL_FS_MASK GENMASK(20, 16) +#define GASRC_CON5_IN_EN_SEL_FS(x) ((x) << 16) +#define GASRC_CON5_CALI_CK_SEL_MASK GENMASK(6, 4) +#define GASRC_CON5_CALI_CK_SEL_26M (0 << 4) +#define GASRC_CON5_CALI_CK_SEL_49M BIT(4) +#define GASRC_CON5_CALI_CK_SEL_45M (2 << 4) +#define GASRC_CON5_CALI_LRCK_SEL_MASK GENMASK(3, 1) +#define GASRC_CON5_CALI_LRCK_SEL(x) ((x) << 1) +#define GASRC_CON5_SOFT_RESET BIT(0) + +/* AFE_GASRCx_NEW_CON6 */ +#define GASRC_CON6_FREQ_CALI_CYCLE_MASK GENMASK(31, 16) +#define GASRC_CON6_FREQ_CALI_CYCLE(x) ((((x) - 1) & GENMASK(15, 0)) << 16) +#define GASRC_CON6_FREQ_CALI_AUTORST_EN BIT(15) +#define GASRC_CON6_AUTO_TUNE_FREQ3 BIT(12) +#define GASRC_CON6_AUTO_TUNE_FREQ2 BIT(3) +#define GASRC_CON6_AUTO_TUNE_FREQ_MASK (GASRC_CON6_AUTO_TUNE_FREQ3 | \ + GASRC_CON6_AUTO_TUNE_FREQ2) +#define GASRC_CON6_COMP_FREQ_RES_EN BIT(11) +#define GASRC_CON6_FREQ_CALI_SEL_MASK GENMASK(9, 8) +#define GASRC_CON6_FREQ_CALI_SEL(x) ((x) << 8) +#define GASRC_CON6_FREQ_CALI_BP_DGL BIT(7) +#define GASRC_CON6_FREQ_CALI_AUTO_RESTART BIT(2) +#define GASRC_CON6_CALI_USE_FREQ_OUT BIT(1) +#define GASRC_CON6_CALI_EN BIT(0) + +/* AFE_GASRCx_NEW_CON7 */ +#define GASRC_CON7_FREQ_CALC_DENOMINATOR_MASK GENMASK(23, 0) +#define GASRC_CON7_FREQ_CALC_DENOMINATOR_49M (0x3c00) +#define GASRC_CON7_FREQ_CALC_DENOMINATOR_45M (0x3720) +#define GASRC_CON7_FREQ_CALC_DENOMINATOR_26M (0x1FBD) + +/* AFE_GASRCx_NEW_CON13 */ +#define GASRC_CON13_FREQ_CALI_AUTORST_TH_HIGH_MASK GENMASK(23, 0) +#define GASRC_CON13_FREQ_CALI_AUTORST_TH_HIGH(x) (((x) * 0x11) >> 4) + +/* AFE_GASRCx_NEW_CON14 */ +#define GASRC_CON14_FREQ_CALI_AUTORST_TH_LOW_MASK GENMASK(23, 0) +#define GASRC_CON14_FREQ_CALI_AUTORST_TH_LOW(x) (((x) * 0xFF) >> 4) + +/* AFE DL*_CON0 */ +#define DL_ON_SFT 28 +#define DL_ONE_HEART_SEL_SFT 22 +#define DL_ONE_HEART_SEL_MASK_SFT (0x3 << 22) +#define DL_MINLEN_SFT 20 +#define DL_MINLEN_MASK 0x3 +#define DL_MAXLEN_SFT 16 +#define DL_MAXLEN_MASK 0x3 +#define DL_SEL_DOMAIN_SFT 13 +#define DL_SEL_DOMAIN_MASK 0x7 +#define DL_SEL_FS_SFT 8 +#define DL_SEL_FS_MASK 0x1f +#define DL_PBUF_SIZE_SFT 5 +#define DL_PBUF_SIZE_MASK 0x3 +#define DL_MONO_SFT 4 +#define DL_NORMAL_MODE_SFT 3 +#define DL_HD_MODE_SFT 0 +#define DL_HD_MODE_MASK 0x3 + +/* AFE VULx_CON0 */ +#define VUL_ON_SFT 28 +#define VUL_SEL_DOMAIN_SFT 13 +#define VUL_SEL_DOMAIN_MASK 0x7 +#define VUL_SEL_FS_SFT 8 +#define VUL_SEL_FS_MASK 0x1f +#define VUL_MONO_SFT 4 +#define VUL_MONO_MASK 0x1 +#define VUL_NORMAL_MODE_SFT 3 +#define VUL_HALIGN_SFT 2 +#define VUL_HD_MODE_SFT 0 +#define VUL_HD_MODE_MASK 0x3 +#define UL_MINLEN_SFT 20 +#define UL_MINLEN_MASK 0x3 +#define UL_MAXLEN_SFT 16 +#define UL_MAXLEN_MASK 0x3 +#define UL_ENABLE_INT_ODD_RFT 0 + +/* AFE VUL_CMx_CON0 */ +#define VUL_CM_NORMAL_MODE_SFT 17 +#define UL_CM_MINLEN_SFT 8 +#define UL_CM_MAXLEN_SFT 12 + +#endif /* _MT8901_REG_H_ */ diff --git a/sound/soc/sof/mediatek/Kconfig b/sound/soc/sof/mediatek/Kconfig index 4a2eddf6009a5..66caa6d13bae1 100644 --- a/sound/soc/sof/mediatek/Kconfig +++ b/sound/soc/sof/mediatek/Kconfig @@ -3,7 +3,7 @@ config SND_SOC_SOF_MTK_TOPLEVEL bool "SOF support for MTK audio DSPs" depends on ARM64 || COMPILE_TEST - depends on SND_SOC_SOF_OF + depends on SND_SOC_SOF_OF || SND_SOC_SOF_ACPI help This adds support for Sound Open Firmware for Mediatek platforms. It is top level for all mediatek platforms. @@ -42,4 +42,14 @@ config SND_SOC_SOF_MT8195 Say Y if you have such a device. If unsure select "N". +config SND_SOC_SOF_MT8901 + tristate "SOF support for MT8901 audio DSP" + depends on ARCH_MEDIATEK && ACPI + depends on MTK_POWER_WRAP + help + This adds support for Sound Open Firmware for Mediatek platforms + using the mt8901 processors. + Say Y if you have such a device. + If unsure select "N". + endif ## SND_SOC_SOF_MTK_TOPLEVEL diff --git a/sound/soc/sof/mediatek/Makefile b/sound/soc/sof/mediatek/Makefile index 29c5afb2f3d6f..8a1068fcca38c 100644 --- a/sound/soc/sof/mediatek/Makefile +++ b/sound/soc/sof/mediatek/Makefile @@ -2,3 +2,4 @@ obj-$(CONFIG_SND_SOC_SOF_MTK_COMMON) += mtk-adsp-common.o obj-$(CONFIG_SND_SOC_SOF_MT8195) += mt8195/ obj-$(CONFIG_SND_SOC_SOF_MT8186) += mt8186/ +obj-$(CONFIG_SND_SOC_SOF_MT8901) += mt8901/ diff --git a/sound/soc/sof/mediatek/mt8901/Makefile b/sound/soc/sof/mediatek/mt8901/Makefile new file mode 100644 index 0000000000000..52449748612b1 --- /dev/null +++ b/sound/soc/sof/mediatek/mt8901/Makefile @@ -0,0 +1,4 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) + +snd-sof-mt8901-y := mt8901.o +obj-$(CONFIG_SND_SOC_SOF_MT8901) += snd-sof-mt8901.o diff --git a/sound/soc/sof/mediatek/mt8901/mt8901.c b/sound/soc/sof/mediatek/mt8901/mt8901.c new file mode 100644 index 0000000000000..b574bedfa16c9 --- /dev/null +++ b/sound/soc/sof/mediatek/mt8901/mt8901.c @@ -0,0 +1,179 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) +/* + * Copyright (c) 2026 MediaTek Inc. + */ + +/* + * ACPI platform driver for MT8901 audio DSP devices + */ + +#include +#include +#include +#include +#include + +#include + +#define MT8901_AUTOSUSPEND_DELAY_MS 100 + +struct sof_mt8901_priv { + void *pwrap_ctrl; +}; + +#ifdef CONFIG_ACPI +static const struct acpi_device_id sof_mt8901_match[] = { + { "NVDA8301" }, + { "NVDA8302" }, + { "NVDA8303" }, + { } +}; +MODULE_DEVICE_TABLE(acpi, sof_mt8901_match); +#endif + +static int sof_mt8901_probe(struct platform_device *pdev) +{ + const struct acpi_device_id *id; + struct acpi_device *adev; + struct acpi_buffer acpi_path = { ACPI_ALLOCATE_BUFFER, NULL }; + struct device *dev = &pdev->dev; + struct sof_mt8901_priv *priv; + acpi_status status; + int ret; + + id = acpi_match_device(dev->driver->acpi_match_table, dev); + if (!id) { + dev_err(dev, "failed to match ACPI device\n"); + return -ENODEV; + } + + dev_dbg(dev, "matched ACPI device %s\n", id->id); + + adev = ACPI_COMPANION(dev); + if (!adev) + return -ENODEV; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + status = acpi_get_name(adev->handle, ACPI_FULL_PATHNAME, &acpi_path); + if (ACPI_FAILURE(status)) { + dev_err(dev, "failed to get ACPI device path\n"); + return -EINVAL; + } + + priv->pwrap_ctrl = mtk_pwrap_dev_probe(acpi_path.pointer); + if (!priv->pwrap_ctrl) { + ret = dev_err_probe(dev, -ENODEV, + "pwrap probe failed for %s\n", + (char *)acpi_path.pointer); + kfree(acpi_path.pointer); + return ret; + } + dev_dbg(dev, "pwrap probe succeeded for %s\n", + (char *)acpi_path.pointer); + + kfree(acpi_path.pointer); + platform_set_drvdata(pdev, priv); + + pm_runtime_set_autosuspend_delay(dev, MT8901_AUTOSUSPEND_DELAY_MS); + pm_runtime_use_autosuspend(dev); + + ret = pm_runtime_set_active(dev); + if (ret) { + dev_err(dev, "failed to set runtime active (%d)\n", ret); + pm_runtime_dont_use_autosuspend(dev); + goto err_pwrap_remove; + } + + pm_runtime_enable(dev); + pm_runtime_mark_last_busy(dev); + pm_runtime_idle(dev); + + return 0; + +err_pwrap_remove: + if (priv->pwrap_ctrl) { + mtk_pwrap_dev_remove(priv->pwrap_ctrl); + priv->pwrap_ctrl = NULL; + } + + return ret; +} + +static void sof_mt8901_remove(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct sof_mt8901_priv *priv = platform_get_drvdata(pdev); + int ret; + + /* SSPM transitions the DSP to D3 as part of abandoning the device. */ + ret = pm_runtime_resume_and_get(dev); + if (ret < 0) + dev_warn(dev, "failed to resume before remove (%d)\n", ret); + + pm_runtime_dont_use_autosuspend(dev); + pm_runtime_disable(dev); + + if (priv && priv->pwrap_ctrl) { + mtk_pwrap_dev_remove(priv->pwrap_ctrl); + priv->pwrap_ctrl = NULL; + } + + if (ret >= 0) + pm_runtime_put_noidle(dev); +} + +static int sof_mt8901_runtime_suspend(struct device *dev) +{ + struct sof_mt8901_priv *priv = dev_get_drvdata(dev); + int ret; + + if (priv && priv->pwrap_ctrl) { + ret = mtk_pwrap_dev_suspend(priv->pwrap_ctrl, 0); + if (ret) { + dev_err(dev, "pwrap suspend failed (%d)\n", ret); + return ret; + } + } + + return 0; +} + +static int sof_mt8901_runtime_resume(struct device *dev) +{ + struct sof_mt8901_priv *priv = dev_get_drvdata(dev); + int ret; + + if (priv && priv->pwrap_ctrl) { + ret = mtk_pwrap_dev_resume(priv->pwrap_ctrl, 0); + if (ret) { + dev_err(dev, "pwrap resume failed (%d)\n", ret); + return ret; + } + } + + return 0; +} + +static const struct dev_pm_ops sof_mt8901_pm = { + SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, + pm_runtime_force_resume) + RUNTIME_PM_OPS(sof_mt8901_runtime_suspend, + sof_mt8901_runtime_resume, NULL) +}; + +static struct platform_driver snd_sof_acpi_mt8901_driver = { + .probe = sof_mt8901_probe, + .remove = sof_mt8901_remove, + .driver = { + .name = "sof-audio-acpi-mt8901", + .pm = pm_ptr(&sof_mt8901_pm), + .acpi_match_table = ACPI_PTR(sof_mt8901_match), + }, +}; +module_platform_driver(snd_sof_acpi_mt8901_driver); + +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_DESCRIPTION("MT8901 ACPI audio DSP driver");