* [PATCH v6 00/10] ASoC: mediatek: Add support for MT8196 SoC
@ 2025-07-08 11:15 Darren.Ye
2025-07-08 11:15 ` [PATCH v6 01/10] ASoC: mediatek: common: modify mtk afe platform driver for mt8196 Darren.Ye
` (9 more replies)
0 siblings, 10 replies; 24+ messages in thread
From: Darren.Ye @ 2025-07-08 11:15 UTC (permalink / raw)
To: Liam Girdwood, Mark Brown, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Matthias Brugger, AngeloGioacchino Del Regno,
Jaroslav Kysela, Takashi Iwai, Linus Walleij, Bartosz Golaszewski
Cc: linux-sound, devicetree, linux-kernel, linux-arm-kernel,
linux-mediatek, linux-gpio, Darren Ye
From: Darren Ye <darren.ye@mediatek.com>
This series of patches adds support for Mediatek AFE of MT8196 SoC.
Patches are based on broonie tree "for-next" branch.
Changes since v5:
- restore the commit message for mediatek,mt8196-afe.yaml and only remove the string document.
- add reviewed owner for mediatek,mt8196-nau8825.yaml.
- use SND_JACK_AVOUT as jack status.
- use GENMASK_ULL to support 64-bit address masks.
- modify the afe platform and i2s dai driver code based on reviewer's suggestions.
Changes since v4:
- modify the mediatek,mt8196-afe.yaml commit message and add reviewed owner.
- modify the mediatek,mt8196-nau8825.yaml commit message.
- modify the audio common code based on reviewer's suggestions.
- add reviewed and tested owners in the audio common code submission message.
- fix cm update cnt calculation issue.
Changes since v3:
- the AFE TOP CG index is added to the common header.
- remove the audsys clk register and directly read and write to the regmap of afe cg clk.
- modify the clk logic according to the suggestions.
- remove the macro definition of MTKAIF4
- remove the tdm cg event and directly read and write the tdm cg reg form the widget.
- remove the i2s and cm cg event and directly read and write cg reg.
- afe hopping and f26m clk cg are placed in remap_register_patch and enable.
- the yaml file is modified according to the suggestions.
- replace SND_SOC_DAIFMT_CBS_CFS with SND_SOC_DAIFMT_CBC_CFC.
Changes since v2:
- remove the mtk_memif_set_channel interface modify.
- remove duplicate definitions from the header file.
- move the afe gate clk to the audio driver for management and registration
and manage the afe clk gate in each dai driver.
- delete the useless clk source.
- the i2s driver adds i2s clk gate management, removes the additional dts
configuration of i2s4.
- the afe and i2s dai driver,memif and irq data structs are encapsulated using
macros to reduce the amount of code.
- the volatile reg is modified as suggested.
- mt6681 codec is not supported, the mt6681 keyword is removed.
- the name of the machine driver is changed from mt8196-mt6681.c to mt8196-nau8825.c
- remove the i2s4 configuration from mt8196-afe.yaml and make the modifications as suggested.
- change the mt8196-mt6681.yaml to mt8196-nau8825.yaml and make the modifications as suggested.
Changes since v1:
- modify mtk_memif_set_channel and mtk_afe_pcm_pointer interfaces
are improved to support mt8196.
- remove duplicate definitions in the mt8196 common header file.
- cm logic is merge into the afe platform driver.
- modify afe clk to return judgment logic and remove useless clk sources.
- refactor the mt8196 adda dai driver.
- remove the gpio module and use SND_SOC_DAPM_PINCTRL to manage it.
- removes CONNSYS_I2S related functions that are not supported in i2s dai driver.
- fixed mt8196-afe.yaml and mt8196-mt6681.yaml syntax issues.
- modify log printing in all modules.
- optimize the header file included for machine driver.
Darren Ye (10):
ASoC: mediatek: common: modify mtk afe platform driver for mt8196
ASoC: mediatek: mt8196: add common header
ASoC: mediatek: mt8196: support audio clock control
ASoC: mediatek: mt8196: support ADDA in platform driver
ASoC: mediatek: mt8196: support I2S in platform driver
ASoC: mediatek: mt8196: support TDM in platform driver
ASoC: mediatek: mt8196: add platform driver
ASoC: dt-bindings: mediatek,mt8196-afe: add audio AFE
ASoC: mediatek: mt8196: add machine driver with nau8825
ASoC: dt-bindings: mediatek,mt8196-nau8825: Add audio sound card
.../bindings/sound/mediatek,mt8196-afe.yaml | 157 +
.../sound/mediatek,mt8196-nau8825.yaml | 102 +
sound/soc/mediatek/Kconfig | 30 +
sound/soc/mediatek/Makefile | 1 +
.../mediatek/common/mtk-afe-platform-driver.c | 47 +-
.../mediatek/common/mtk-afe-platform-driver.h | 2 +
sound/soc/mediatek/mt8196/Makefile | 17 +
sound/soc/mediatek/mt8196/mt8196-afe-clk.c | 728 +
sound/soc/mediatek/mt8196/mt8196-afe-clk.h | 80 +
sound/soc/mediatek/mt8196/mt8196-afe-common.h | 213 +
sound/soc/mediatek/mt8196/mt8196-afe-pcm.c | 2632 ++++
sound/soc/mediatek/mt8196/mt8196-dai-adda.c | 888 ++
sound/soc/mediatek/mt8196/mt8196-dai-i2s.c | 3944 +++++
sound/soc/mediatek/mt8196/mt8196-dai-tdm.c | 836 ++
.../mediatek/mt8196/mt8196-interconnection.h | 121 +
sound/soc/mediatek/mt8196/mt8196-nau8825.c | 869 ++
sound/soc/mediatek/mt8196/mt8196-reg.h | 12068 ++++++++++++++++
17 files changed, 22719 insertions(+), 16 deletions(-)
create mode 100644 Documentation/devicetree/bindings/sound/mediatek,mt8196-afe.yaml
create mode 100644 Documentation/devicetree/bindings/sound/mediatek,mt8196-nau8825.yaml
create mode 100644 sound/soc/mediatek/mt8196/Makefile
create mode 100644 sound/soc/mediatek/mt8196/mt8196-afe-clk.c
create mode 100644 sound/soc/mediatek/mt8196/mt8196-afe-clk.h
create mode 100644 sound/soc/mediatek/mt8196/mt8196-afe-common.h
create mode 100644 sound/soc/mediatek/mt8196/mt8196-afe-pcm.c
create mode 100644 sound/soc/mediatek/mt8196/mt8196-dai-adda.c
create mode 100644 sound/soc/mediatek/mt8196/mt8196-dai-i2s.c
create mode 100644 sound/soc/mediatek/mt8196/mt8196-dai-tdm.c
create mode 100644 sound/soc/mediatek/mt8196/mt8196-interconnection.h
create mode 100644 sound/soc/mediatek/mt8196/mt8196-nau8825.c
create mode 100644 sound/soc/mediatek/mt8196/mt8196-reg.h
--
2.45.2
^ permalink raw reply [flat|nested] 24+ messages in thread
* [PATCH v6 01/10] ASoC: mediatek: common: modify mtk afe platform driver for mt8196
2025-07-08 11:15 [PATCH v6 00/10] ASoC: mediatek: Add support for MT8196 SoC Darren.Ye
@ 2025-07-08 11:15 ` Darren.Ye
2025-07-22 7:38 ` Chen-Yu Tsai
2025-07-08 11:15 ` [PATCH v6 03/10] ASoC: mediatek: mt8196: support audio clock control Darren.Ye
` (8 subsequent siblings)
9 siblings, 1 reply; 24+ messages in thread
From: Darren.Ye @ 2025-07-08 11:15 UTC (permalink / raw)
To: Liam Girdwood, Mark Brown, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Matthias Brugger, AngeloGioacchino Del Regno,
Jaroslav Kysela, Takashi Iwai, Linus Walleij, Bartosz Golaszewski
Cc: linux-sound, devicetree, linux-kernel, linux-arm-kernel,
linux-mediatek, linux-gpio, Darren Ye, Louis-Alexis Eyraud
From: Darren Ye <darren.ye@mediatek.com>
Mofify the pcm pointer interface to support 64-bit address access.
Signed-off-by: Darren Ye <darren.ye@mediatek.com>
Reviewed-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
Tested-by: Louis-Alexis Eyraud <louisalexis.eyraud@collabora.com>
---
.../mediatek/common/mtk-afe-platform-driver.c | 47 ++++++++++++-------
.../mediatek/common/mtk-afe-platform-driver.h | 2 +
2 files changed, 33 insertions(+), 16 deletions(-)
diff --git a/sound/soc/mediatek/common/mtk-afe-platform-driver.c b/sound/soc/mediatek/common/mtk-afe-platform-driver.c
index 70fd05d5ff48..cab4ef035199 100644
--- a/sound/soc/mediatek/common/mtk-afe-platform-driver.c
+++ b/sound/soc/mediatek/common/mtk-afe-platform-driver.c
@@ -86,29 +86,44 @@ snd_pcm_uframes_t mtk_afe_pcm_pointer(struct snd_soc_component *component,
const struct mtk_base_memif_data *memif_data = memif->data;
struct regmap *regmap = afe->regmap;
struct device *dev = afe->dev;
- int reg_ofs_base = memif_data->reg_ofs_base;
- int reg_ofs_cur = memif_data->reg_ofs_cur;
- unsigned int hw_ptr = 0, hw_base = 0;
- int ret, pcm_ptr_bytes;
-
- ret = regmap_read(regmap, reg_ofs_cur, &hw_ptr);
- if (ret || hw_ptr == 0) {
- dev_err(dev, "%s hw_ptr err\n", __func__);
- pcm_ptr_bytes = 0;
+ unsigned int hw_ptr_lower32 = 0, hw_ptr_upper32 = 0;
+ unsigned int hw_base_lower32 = 0, hw_base_upper32 = 0;
+ unsigned long long hw_ptr = 0, hw_base = 0;
+ int ret;
+ unsigned long long pcm_ptr_bytes = 0;
+
+ ret = regmap_read(regmap, memif_data->reg_ofs_cur, &hw_ptr_lower32);
+ if (ret || hw_ptr_lower32 == 0) {
+ dev_err(dev, "%s hw_ptr_lower32 err\n", __func__);
goto POINTER_RETURN_FRAMES;
}
- ret = regmap_read(regmap, reg_ofs_base, &hw_base);
- if (ret || hw_base == 0) {
- dev_err(dev, "%s hw_ptr err\n", __func__);
- pcm_ptr_bytes = 0;
- goto POINTER_RETURN_FRAMES;
+ if (memif_data->reg_ofs_cur_msb) {
+ ret = regmap_read(regmap, memif_data->reg_ofs_cur_msb, &hw_ptr_upper32);
+ if (ret) {
+ dev_err(dev, "%s hw_ptr_upper32 err\n", __func__);
+ goto POINTER_RETURN_FRAMES;
+ }
}
- pcm_ptr_bytes = hw_ptr - hw_base;
+ ret = regmap_read(regmap, memif_data->reg_ofs_base, &hw_base_lower32);
+ if (ret || hw_base_lower32 == 0) {
+ dev_err(dev, "%s hw_base_lower32 err\n", __func__);
+ goto POINTER_RETURN_FRAMES;
+ }
+ if (memif_data->reg_ofs_base_msb) {
+ ret = regmap_read(regmap, memif_data->reg_ofs_base_msb, &hw_base_upper32);
+ if (ret) {
+ dev_err(dev, "%s hw_base_upper32 err\n", __func__);
+ goto POINTER_RETURN_FRAMES;
+ }
+ }
+ hw_ptr = ((unsigned long long)hw_ptr_upper32 << 32) + hw_ptr_lower32;
+ hw_base = ((unsigned long long)hw_base_upper32 << 32) + hw_base_lower32;
POINTER_RETURN_FRAMES:
- return bytes_to_frames(substream->runtime, pcm_ptr_bytes);
+ pcm_ptr_bytes = MTK_ALIGN_16BYTES(hw_ptr - hw_base);
+ return bytes_to_frames(substream->runtime, (ssize_t)pcm_ptr_bytes);
}
EXPORT_SYMBOL_GPL(mtk_afe_pcm_pointer);
diff --git a/sound/soc/mediatek/common/mtk-afe-platform-driver.h b/sound/soc/mediatek/common/mtk-afe-platform-driver.h
index fcc923b88f12..71070b26f8f8 100644
--- a/sound/soc/mediatek/common/mtk-afe-platform-driver.h
+++ b/sound/soc/mediatek/common/mtk-afe-platform-driver.h
@@ -12,6 +12,8 @@
#define AFE_PCM_NAME "mtk-afe-pcm"
extern const struct snd_soc_component_driver mtk_afe_pcm_platform;
+#define MTK_ALIGN_16BYTES(x) ((x) & GENMASK_ULL(39, 4))
+
struct mtk_base_afe;
struct snd_pcm;
struct snd_soc_component;
--
2.45.2
^ permalink raw reply related [flat|nested] 24+ messages in thread
* [PATCH v6 03/10] ASoC: mediatek: mt8196: support audio clock control
2025-07-08 11:15 [PATCH v6 00/10] ASoC: mediatek: Add support for MT8196 SoC Darren.Ye
2025-07-08 11:15 ` [PATCH v6 01/10] ASoC: mediatek: common: modify mtk afe platform driver for mt8196 Darren.Ye
@ 2025-07-08 11:15 ` Darren.Ye
2025-07-30 8:42 ` Chen-Yu Tsai
2025-07-08 11:15 ` [PATCH v6 04/10] ASoC: mediatek: mt8196: support ADDA in platform driver Darren.Ye
` (7 subsequent siblings)
9 siblings, 1 reply; 24+ messages in thread
From: Darren.Ye @ 2025-07-08 11:15 UTC (permalink / raw)
To: Liam Girdwood, Mark Brown, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Matthias Brugger, AngeloGioacchino Del Regno,
Jaroslav Kysela, Takashi Iwai, Linus Walleij, Bartosz Golaszewski
Cc: linux-sound, devicetree, linux-kernel, linux-arm-kernel,
linux-mediatek, linux-gpio, Darren Ye
From: Darren Ye <darren.ye@mediatek.com>
Add audio clock wrapper and audio tuner control.
Signed-off-by: Darren Ye <darren.ye@mediatek.com>
---
sound/soc/mediatek/mt8196/mt8196-afe-clk.c | 728 +++++++++++++++++++++
sound/soc/mediatek/mt8196/mt8196-afe-clk.h | 80 +++
2 files changed, 808 insertions(+)
create mode 100644 sound/soc/mediatek/mt8196/mt8196-afe-clk.c
create mode 100644 sound/soc/mediatek/mt8196/mt8196-afe-clk.h
diff --git a/sound/soc/mediatek/mt8196/mt8196-afe-clk.c b/sound/soc/mediatek/mt8196/mt8196-afe-clk.c
new file mode 100644
index 000000000000..00f47b485812
--- /dev/null
+++ b/sound/soc/mediatek/mt8196/mt8196-afe-clk.c
@@ -0,0 +1,728 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * mt8196-afe-clk.c -- Mediatek 8196 afe clock ctrl
+ *
+ * Copyright (c) 2024 MediaTek Inc.
+ * Author: Darren Ye <darren.ye@mediatek.com>
+ */
+
+#include <linux/clk.h>
+#include <linux/regmap.h>
+#include <linux/mfd/syscon.h>
+#include "mt8196-afe-common.h"
+#include "mt8196-afe-clk.h"
+
+static const char *aud_clks[MT8196_CLK_NUM] = {
+ /* vlp clk */
+ [MT8196_CLK_VLP_MUX_AUDIOINTBUS] = "top_aud_intbus",
+ [MT8196_CLK_VLP_MUX_AUD_ENG1] = "top_aud_eng1",
+ [MT8196_CLK_VLP_MUX_AUD_ENG2] = "top_aud_eng2",
+ [MT8196_CLK_VLP_MUX_AUDIO_H] = "top_aud_h",
+ [MT8196_CLK_VLP_CLK26M] = "vlp_clk26m",
+ /* pll */
+ [MT8196_CLK_TOP_APLL1_CK] = "apll1",
+ [MT8196_CLK_TOP_APLL2_CK] = "apll2",
+ /* divider */
+ [MT8196_CLK_TOP_APLL1_D4] = "apll1_d4",
+ [MT8196_CLK_TOP_APLL2_D4] = "apll2_d4",
+ [MT8196_CLK_TOP_APLL12_DIV_I2SIN0] = "apll12_div_i2sin0",
+ [MT8196_CLK_TOP_APLL12_DIV_I2SIN1] = "apll12_div_i2sin1",
+ [MT8196_CLK_TOP_APLL12_DIV_FMI2S] = "apll12_div_fmi2s",
+ [MT8196_CLK_TOP_APLL12_DIV_TDMOUT_M] = "apll12_div_tdmout_m",
+ [MT8196_CLK_TOP_APLL12_DIV_TDMOUT_B] = "apll12_div_tdmout_b",
+ /* mux */
+ [MT8196_CLK_TOP_MUX_AUD_1] = "top_apll1",
+ [MT8196_CLK_TOP_MUX_AUD_2] = "top_apll2",
+ [MT8196_CLK_TOP_I2SIN0_M_SEL] = "top_i2sin0",
+ [MT8196_CLK_TOP_I2SIN1_M_SEL] = "top_i2sin1",
+ [MT8196_CLK_TOP_FMI2S_M_SEL] = "top_fmi2s",
+ [MT8196_CLK_TOP_TDMOUT_M_SEL] = "top_dptx",
+ [MT8196_CLK_TOP_ADSP_SEL] = "top_adsp",
+ /* top 26m*/
+ [MT8196_CLK_TOP_CLK26M] = "clk26m",
+};
+
+int mt8196_afe_enable_clk(struct mtk_base_afe *afe, struct clk *clk)
+{
+ int ret;
+
+ if (clk) {
+ ret = clk_prepare_enable(clk);
+ if (ret) {
+ dev_dbg(afe->dev, "failed to enable clk\n");
+ return ret;
+ }
+ } else {
+ dev_dbg(afe->dev, "NULL clk\n");
+ }
+ return 0;
+}
+EXPORT_SYMBOL_GPL(mt8196_afe_enable_clk);
+
+void mt8196_afe_disable_clk(struct mtk_base_afe *afe, struct clk *clk)
+{
+ if (clk)
+ clk_disable_unprepare(clk);
+ else
+ dev_dbg(afe->dev, "NULL clk\n");
+}
+EXPORT_SYMBOL_GPL(mt8196_afe_disable_clk);
+
+static int mt8196_afe_set_clk_rate(struct mtk_base_afe *afe, struct clk *clk,
+ unsigned int rate)
+{
+ int ret;
+
+ if (clk) {
+ ret = clk_set_rate(clk, rate);
+ if (ret) {
+ dev_dbg(afe->dev, "failed to set clk rate\n");
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static int mt8196_afe_set_clk_parent(struct mtk_base_afe *afe, struct clk *clk,
+ struct clk *parent)
+{
+ int ret;
+
+ if (clk && parent) {
+ ret = clk_set_parent(clk, parent);
+ if (ret) {
+ dev_dbg(afe->dev, "failed to set clk parent %d\n", ret);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static unsigned int get_top_cg_reg(unsigned int cg_type)
+{
+ switch (cg_type) {
+ case MT8196_AUDIO_26M_EN_ON:
+ case MT8196_AUDIO_F3P25M_EN_ON:
+ case MT8196_AUDIO_APLL1_EN_ON:
+ case MT8196_AUDIO_APLL2_EN_ON:
+ return AUDIO_ENGEN_CON0;
+ case MT8196_CG_AUDIO_HOPPING_CK:
+ case MT8196_CG_AUDIO_F26M_CK:
+ case MT8196_CG_APLL1_CK:
+ case MT8196_CG_APLL2_CK:
+ case MT8196_PDN_APLL_TUNER2:
+ case MT8196_PDN_APLL_TUNER1:
+ return AUDIO_TOP_CON4;
+ default:
+ return 0;
+ }
+}
+
+static unsigned int get_top_cg_mask(unsigned int cg_type)
+{
+ switch (cg_type) {
+ case MT8196_AUDIO_26M_EN_ON:
+ return AUDIO_26M_EN_ON_MASK_SFT;
+ case MT8196_AUDIO_F3P25M_EN_ON:
+ return AUDIO_F3P25M_EN_ON_MASK_SFT;
+ case MT8196_AUDIO_APLL1_EN_ON:
+ return AUDIO_APLL1_EN_ON_MASK_SFT;
+ case MT8196_AUDIO_APLL2_EN_ON:
+ return AUDIO_APLL2_EN_ON_MASK_SFT;
+ case MT8196_CG_AUDIO_HOPPING_CK:
+ return CG_AUDIO_HOPPING_CK_MASK_SFT;
+ case MT8196_CG_AUDIO_F26M_CK:
+ return CG_AUDIO_F26M_CK_MASK_SFT;
+ case MT8196_CG_APLL1_CK:
+ return CG_APLL1_CK_MASK_SFT;
+ case MT8196_CG_APLL2_CK:
+ return CG_APLL2_CK_MASK_SFT;
+ case MT8196_PDN_APLL_TUNER2:
+ return PDN_APLL_TUNER2_MASK_SFT;
+ case MT8196_PDN_APLL_TUNER1:
+ return PDN_APLL_TUNER1_MASK_SFT;
+ default:
+ return 0;
+ }
+}
+
+static unsigned int get_top_cg_on_val(unsigned int cg_type)
+{
+ switch (cg_type) {
+ case MT8196_AUDIO_26M_EN_ON:
+ case MT8196_AUDIO_F3P25M_EN_ON:
+ case MT8196_AUDIO_APLL1_EN_ON:
+ case MT8196_AUDIO_APLL2_EN_ON:
+ return get_top_cg_mask(cg_type);
+ case MT8196_CG_AUDIO_HOPPING_CK:
+ case MT8196_CG_AUDIO_F26M_CK:
+ case MT8196_CG_APLL1_CK:
+ case MT8196_CG_APLL2_CK:
+ case MT8196_PDN_APLL_TUNER2:
+ case MT8196_PDN_APLL_TUNER1:
+ return 0;
+ default:
+ return 0;
+ }
+}
+
+static unsigned int get_top_cg_off_val(unsigned int cg_type)
+{
+ switch (cg_type) {
+ case MT8196_AUDIO_26M_EN_ON:
+ case MT8196_AUDIO_F3P25M_EN_ON:
+ case MT8196_AUDIO_APLL1_EN_ON:
+ case MT8196_AUDIO_APLL2_EN_ON:
+ return 0;
+ case MT8196_CG_AUDIO_HOPPING_CK:
+ case MT8196_CG_AUDIO_F26M_CK:
+ case MT8196_CG_APLL1_CK:
+ case MT8196_CG_APLL2_CK:
+ case MT8196_PDN_APLL_TUNER2:
+ case MT8196_PDN_APLL_TUNER1:
+ return get_top_cg_mask(cg_type);
+ default:
+ return get_top_cg_mask(cg_type);
+ }
+}
+
+static int mt8196_afe_enable_top_cg(struct mtk_base_afe *afe, unsigned int cg_type)
+{
+ unsigned int reg = get_top_cg_reg(cg_type);
+ unsigned int mask = get_top_cg_mask(cg_type);
+ unsigned int val = get_top_cg_on_val(cg_type);
+
+ if (!afe->regmap) {
+ dev_warn(afe->dev, "skip regmap\n");
+ return 0;
+ }
+
+ dev_dbg(afe->dev, "reg: 0x%x, mask: 0x%x, val: 0x%x\n", reg, mask, val);
+ regmap_update_bits(afe->regmap, reg, mask, val);
+ return 0;
+}
+
+static int mt8196_afe_disable_top_cg(struct mtk_base_afe *afe, unsigned int cg_type)
+{
+ unsigned int reg = get_top_cg_reg(cg_type);
+ unsigned int mask = get_top_cg_mask(cg_type);
+ unsigned int val = get_top_cg_off_val(cg_type);
+
+ if (!afe->regmap) {
+ dev_warn(afe->dev, "skip regmap\n");
+ return 0;
+ }
+
+ dev_dbg(afe->dev, "reg: 0x%x, mask: 0x%x, val: 0x%x\n", reg, mask, val);
+ regmap_update_bits(afe->regmap, reg, mask, val);
+
+ return 0;
+}
+
+static int apll1_mux_setting(struct mtk_base_afe *afe, bool enable)
+{
+ struct mt8196_afe_private *afe_priv = afe->platform_priv;
+ int ret = 0;
+
+ dev_dbg(afe->dev, "enable: %d\n", enable);
+
+ if (enable) {
+ ret = mt8196_afe_enable_clk(afe, afe_priv->clk[MT8196_CLK_TOP_MUX_AUD_1]);
+ if (ret)
+ return ret;
+
+ ret = mt8196_afe_set_clk_parent(afe, afe_priv->clk[MT8196_CLK_TOP_MUX_AUD_1],
+ afe_priv->clk[MT8196_CLK_TOP_APLL1_CK]);
+ if (ret)
+ return ret;
+
+ /* 180.6336 / 4 = 45.1584MHz */
+ ret = mt8196_afe_enable_clk(afe, afe_priv->clk[MT8196_CLK_VLP_MUX_AUD_ENG1]);
+ if (ret)
+ return ret;
+
+ ret = mt8196_afe_set_clk_parent(afe, afe_priv->clk[MT8196_CLK_VLP_MUX_AUD_ENG1],
+ afe_priv->clk[MT8196_CLK_TOP_APLL1_D4]);
+ if (ret)
+ return ret;
+
+ ret = mt8196_afe_enable_clk(afe, afe_priv->clk[MT8196_CLK_VLP_MUX_AUDIO_H]);
+ if (ret)
+ return ret;
+
+ ret = mt8196_afe_set_clk_parent(afe, afe_priv->clk[MT8196_CLK_VLP_MUX_AUDIO_H],
+ afe_priv->clk[MT8196_CLK_TOP_APLL1_CK]);
+ if (ret)
+ return ret;
+ } else {
+ ret = mt8196_afe_set_clk_parent(afe, afe_priv->clk[MT8196_CLK_VLP_MUX_AUD_ENG1],
+ afe_priv->clk[MT8196_CLK_VLP_CLK26M]);
+ if (ret)
+ return ret;
+
+ mt8196_afe_disable_clk(afe, afe_priv->clk[MT8196_CLK_VLP_MUX_AUD_ENG1]);
+
+ ret = mt8196_afe_set_clk_parent(afe, afe_priv->clk[MT8196_CLK_TOP_MUX_AUD_1],
+ afe_priv->clk[MT8196_CLK_TOP_CLK26M]);
+ if (ret)
+ return ret;
+
+ mt8196_afe_disable_clk(afe, afe_priv->clk[MT8196_CLK_TOP_MUX_AUD_1]);
+ mt8196_afe_set_clk_parent(afe, afe_priv->clk[MT8196_CLK_VLP_MUX_AUDIO_H],
+ afe_priv->clk[MT8196_CLK_VLP_CLK26M]);
+ mt8196_afe_disable_clk(afe, afe_priv->clk[MT8196_CLK_VLP_MUX_AUDIO_H]);
+ }
+
+ return 0;
+}
+
+static int apll2_mux_setting(struct mtk_base_afe *afe, bool enable)
+{
+ struct mt8196_afe_private *afe_priv = afe->platform_priv;
+ int ret = 0;
+
+ dev_dbg(afe->dev, "enable: %d\n", enable);
+
+ if (enable) {
+ ret = mt8196_afe_enable_clk(afe, afe_priv->clk[MT8196_CLK_TOP_MUX_AUD_2]);
+ if (ret)
+ return ret;
+
+ ret = mt8196_afe_set_clk_parent(afe, afe_priv->clk[MT8196_CLK_TOP_MUX_AUD_2],
+ afe_priv->clk[MT8196_CLK_TOP_APLL2_CK]);
+ if (ret)
+ return ret;
+
+ /* 196.608 / 4 = 49.152MHz */
+ ret = mt8196_afe_enable_clk(afe, afe_priv->clk[MT8196_CLK_VLP_MUX_AUD_ENG2]);
+ if (ret)
+ return ret;
+
+ ret = mt8196_afe_set_clk_parent(afe, afe_priv->clk[MT8196_CLK_VLP_MUX_AUD_ENG2],
+ afe_priv->clk[MT8196_CLK_TOP_APLL2_D4]);
+ if (ret)
+ return ret;
+
+ ret = mt8196_afe_enable_clk(afe, afe_priv->clk[MT8196_CLK_VLP_MUX_AUDIO_H]);
+ if (ret)
+ return ret;
+
+ ret = mt8196_afe_set_clk_parent(afe, afe_priv->clk[MT8196_CLK_VLP_MUX_AUDIO_H],
+ afe_priv->clk[MT8196_CLK_TOP_APLL2_CK]);
+ if (ret)
+ return ret;
+ } else {
+ ret = mt8196_afe_set_clk_parent(afe, afe_priv->clk[MT8196_CLK_VLP_MUX_AUD_ENG2],
+ afe_priv->clk[MT8196_CLK_VLP_CLK26M]);
+ if (ret)
+ return ret;
+
+ mt8196_afe_disable_clk(afe, afe_priv->clk[MT8196_CLK_VLP_MUX_AUD_ENG2]);
+
+ ret = mt8196_afe_set_clk_parent(afe, afe_priv->clk[MT8196_CLK_TOP_MUX_AUD_2],
+ afe_priv->clk[MT8196_CLK_TOP_CLK26M]);
+ if (ret)
+ return ret;
+
+ mt8196_afe_disable_clk(afe, afe_priv->clk[MT8196_CLK_TOP_MUX_AUD_2]);
+ mt8196_afe_set_clk_parent(afe, afe_priv->clk[MT8196_CLK_VLP_MUX_AUDIO_H],
+ afe_priv->clk[MT8196_CLK_VLP_CLK26M]);
+ mt8196_afe_disable_clk(afe, afe_priv->clk[MT8196_CLK_VLP_MUX_AUDIO_H]);
+ }
+
+ return 0;
+}
+
+static int mt8196_afe_disable_apll(struct mtk_base_afe *afe)
+{
+ struct mt8196_afe_private *afe_priv = afe->platform_priv;
+ int ret = 0;
+
+ ret = mt8196_afe_enable_clk(afe, afe_priv->clk[MT8196_CLK_VLP_MUX_AUDIO_H]);
+ if (ret)
+ return ret;
+
+ ret = mt8196_afe_enable_clk(afe, afe_priv->clk[MT8196_CLK_TOP_MUX_AUD_1]);
+ if (ret)
+ goto clk_ck_mux_aud1_err;
+
+ ret = mt8196_afe_set_clk_parent(afe, afe_priv->clk[MT8196_CLK_TOP_MUX_AUD_1],
+ afe_priv->clk[MT8196_CLK_TOP_CLK26M]);
+ if (ret)
+ goto clk_ck_mux_aud1_parent_err;
+
+ ret = mt8196_afe_enable_clk(afe, afe_priv->clk[MT8196_CLK_TOP_MUX_AUD_2]);
+ if (ret)
+ goto clk_ck_mux_aud2_err;
+
+ ret = mt8196_afe_set_clk_parent(afe, afe_priv->clk[MT8196_CLK_TOP_MUX_AUD_2],
+ afe_priv->clk[MT8196_CLK_TOP_CLK26M]);
+ if (ret)
+ goto clk_ck_mux_aud2_parent_err;
+
+ mt8196_afe_disable_clk(afe, afe_priv->clk[MT8196_CLK_TOP_MUX_AUD_1]);
+ mt8196_afe_disable_clk(afe, afe_priv->clk[MT8196_CLK_TOP_MUX_AUD_2]);
+ mt8196_afe_set_clk_parent(afe, afe_priv->clk[MT8196_CLK_VLP_MUX_AUDIO_H],
+ afe_priv->clk[MT8196_CLK_VLP_CLK26M]);
+ mt8196_afe_disable_clk(afe, afe_priv->clk[MT8196_CLK_VLP_MUX_AUDIO_H]);
+ return 0;
+
+clk_ck_mux_aud2_parent_err:
+ mt8196_afe_disable_clk(afe, afe_priv->clk[MT8196_CLK_TOP_MUX_AUD_2]);
+clk_ck_mux_aud2_err:
+ mt8196_afe_set_clk_parent(afe, afe_priv->clk[MT8196_CLK_TOP_MUX_AUD_1],
+ afe_priv->clk[MT8196_CLK_TOP_APLL1_CK]);
+clk_ck_mux_aud1_parent_err:
+ mt8196_afe_disable_clk(afe, afe_priv->clk[MT8196_CLK_TOP_MUX_AUD_1]);
+clk_ck_mux_aud1_err:
+ mt8196_afe_disable_clk(afe, afe_priv->clk[MT8196_CLK_VLP_MUX_AUDIO_H]);
+
+ return ret;
+}
+
+static void mt8196_afe_apll_init(struct mtk_base_afe *afe)
+{
+ struct mt8196_afe_private *afe_priv = afe->platform_priv;
+
+ if (!afe_priv->vlp_ck) {
+ dev_warn(afe->dev, "vlp_ck regmap is null ptr\n");
+ return;
+ }
+
+ regmap_write(afe_priv->vlp_ck, VLP_APLL1_TUNER_CON0, VLP_APLL1_TUNER_CON0_VALUE);
+ regmap_write(afe_priv->vlp_ck, VLP_APLL2_TUNER_CON0, VLP_APLL2_TUNER_CON0_VALUE);
+}
+
+int mt8196_apll1_enable(struct mtk_base_afe *afe)
+{
+ int ret;
+
+ /* setting for APLL */
+ apll1_mux_setting(afe, true);
+
+ ret = mt8196_afe_enable_top_cg(afe, MT8196_CG_APLL1_CK);
+ if (ret)
+ goto err_clk_apll1;
+
+ ret = mt8196_afe_enable_top_cg(afe, MT8196_PDN_APLL_TUNER1);
+ if (ret)
+ goto err_clk_apll1_tuner;
+
+ /* sel 44.1kHz:1, apll_div:7, upper bound:3 */
+ regmap_update_bits(afe->regmap, AFE_APLL1_TUNER_CFG,
+ XTAL_EN_128FS_SEL_MASK_SFT | APLL_DIV_MASK_SFT | UPPER_BOUND_MASK_SFT,
+ (0x1 << XTAL_EN_128FS_SEL_SFT) | (7 << APLL_DIV_SFT) |
+ (3 << UPPER_BOUND_SFT));
+
+ /* apll1 freq tuner enable */
+ regmap_update_bits(afe->regmap, AFE_APLL1_TUNER_CFG,
+ FREQ_TUNER_EN_MASK_SFT,
+ 0x1 << FREQ_TUNER_EN_SFT);
+
+ /* audio apll1 on */
+ mt8196_afe_enable_top_cg(afe, MT8196_AUDIO_APLL1_EN_ON);
+
+ return 0;
+
+err_clk_apll1_tuner:
+ mt8196_afe_disable_top_cg(afe, MT8196_PDN_APLL_TUNER1);
+err_clk_apll1:
+ mt8196_afe_disable_top_cg(afe, MT8196_CG_APLL1_CK);
+ return ret;
+}
+
+void mt8196_apll1_disable(struct mtk_base_afe *afe)
+{
+ /* audio apll1 off */
+ mt8196_afe_disable_top_cg(afe, MT8196_AUDIO_APLL1_EN_ON);
+
+ /* apll1 freq tuner disable */
+ regmap_update_bits(afe->regmap, AFE_APLL1_TUNER_CFG,
+ FREQ_TUNER_EN_MASK_SFT,
+ 0x0);
+
+ mt8196_afe_disable_top_cg(afe, MT8196_PDN_APLL_TUNER1);
+ mt8196_afe_disable_top_cg(afe, MT8196_CG_APLL1_CK);
+ apll1_mux_setting(afe, false);
+}
+
+int mt8196_apll2_enable(struct mtk_base_afe *afe)
+{
+ int ret;
+
+ /* setting for APLL */
+ apll2_mux_setting(afe, true);
+
+ ret = mt8196_afe_enable_top_cg(afe, MT8196_CG_APLL2_CK);
+ if (ret)
+ goto err_clk_apll2;
+
+ ret = mt8196_afe_enable_top_cg(afe, MT8196_PDN_APLL_TUNER2);
+ if (ret)
+ goto err_clk_apll2_tuner;
+
+ /* sel 48kHz: 2, apll_div: 7, upper bound: 3*/
+ regmap_update_bits(afe->regmap, AFE_APLL2_TUNER_CFG,
+ XTAL_EN_128FS_SEL_MASK_SFT | APLL_DIV_MASK_SFT | UPPER_BOUND_MASK_SFT,
+ (0x2 << XTAL_EN_128FS_SEL_SFT) | (7 << APLL_DIV_SFT) |
+ (3 << UPPER_BOUND_SFT));
+
+ /* apll2 freq tuner enable */
+ regmap_update_bits(afe->regmap, AFE_APLL2_TUNER_CFG,
+ FREQ_TUNER_EN_MASK_SFT,
+ 0x1 << FREQ_TUNER_EN_SFT);
+
+ /* audio apll2 on */
+ mt8196_afe_enable_top_cg(afe, MT8196_AUDIO_APLL2_EN_ON);
+ return 0;
+
+err_clk_apll2_tuner:
+ mt8196_afe_disable_top_cg(afe, MT8196_PDN_APLL_TUNER2);
+err_clk_apll2:
+ mt8196_afe_disable_top_cg(afe, MT8196_CG_APLL2_CK);
+ return 0;
+}
+
+void mt8196_apll2_disable(struct mtk_base_afe *afe)
+{
+ /* audio apll2 off */
+ mt8196_afe_disable_top_cg(afe, MT8196_AUDIO_APLL2_EN_ON);
+
+ /* apll2 freq tuner disable */
+ regmap_update_bits(afe->regmap, AFE_APLL2_TUNER_CFG,
+ FREQ_TUNER_EN_MASK_SFT,
+ 0x0);
+
+ mt8196_afe_disable_top_cg(afe, MT8196_PDN_APLL_TUNER2);
+ mt8196_afe_disable_top_cg(afe, MT8196_CG_APLL2_CK);
+ apll2_mux_setting(afe, false);
+}
+
+int mt8196_get_apll_rate(struct mtk_base_afe *afe, int apll)
+{
+ struct mt8196_afe_private *afe_priv = afe->platform_priv;
+ int clk_id = 0;
+
+ if (apll < MT8196_APLL1 || apll > MT8196_APLL2) {
+ dev_warn(afe->dev, "invalid clk id %d\n", apll);
+ return 0;
+ }
+
+ if (apll == MT8196_APLL1)
+ clk_id = MT8196_CLK_TOP_APLL1_CK;
+ else
+ clk_id = MT8196_CLK_TOP_APLL2_CK;
+
+ return clk_get_rate(afe_priv->clk[clk_id]);
+}
+
+int mt8196_get_apll_by_rate(struct mtk_base_afe *afe, int rate)
+{
+ return (rate % 8000) ? MT8196_APLL1 : MT8196_APLL2;
+}
+
+int mt8196_get_apll_by_name(struct mtk_base_afe *afe, const char *name)
+{
+ if (strcmp(name, APLL1_W_NAME) == 0)
+ return MT8196_APLL1;
+
+ return MT8196_APLL2;
+}
+
+/* mck */
+struct mt8196_mck_div {
+ int m_sel_id;
+ int div_clk_id;
+};
+
+static const struct mt8196_mck_div mck_div[MT8196_MCK_NUM] = {
+ [MT8196_I2SIN0_MCK] = {
+ .m_sel_id = MT8196_CLK_TOP_I2SIN0_M_SEL,
+ .div_clk_id = MT8196_CLK_TOP_APLL12_DIV_I2SIN0,
+ },
+ [MT8196_I2SIN1_MCK] = {
+ .m_sel_id = MT8196_CLK_TOP_I2SIN1_M_SEL,
+ .div_clk_id = MT8196_CLK_TOP_APLL12_DIV_I2SIN1,
+ },
+ [MT8196_FMI2S_MCK] = {
+ .m_sel_id = MT8196_CLK_TOP_FMI2S_M_SEL,
+ .div_clk_id = MT8196_CLK_TOP_APLL12_DIV_FMI2S,
+ },
+ [MT8196_TDMOUT_MCK] = {
+ .m_sel_id = MT8196_CLK_TOP_TDMOUT_M_SEL,
+ .div_clk_id = MT8196_CLK_TOP_APLL12_DIV_TDMOUT_M,
+ },
+ [MT8196_TDMOUT_BCK] = {
+ .m_sel_id = -1,
+ .div_clk_id = MT8196_CLK_TOP_APLL12_DIV_TDMOUT_B,
+ },
+};
+
+int mt8196_mck_enable(struct mtk_base_afe *afe, int mck_id, int rate)
+{
+ struct mt8196_afe_private *afe_priv = afe->platform_priv;
+ int apll = mt8196_get_apll_by_rate(afe, rate);
+ int apll_clk_id = apll == MT8196_APLL1 ?
+ MT8196_CLK_TOP_MUX_AUD_1 : MT8196_CLK_TOP_MUX_AUD_2;
+ int m_sel_id;
+ int div_clk_id;
+ int ret;
+
+ dev_dbg(afe->dev, "mck_id: %d, rate: %d\n", mck_id, rate);
+
+ if (mck_id >= MT8196_MCK_NUM || mck_id < 0)
+ return -EINVAL;
+
+ m_sel_id = mck_div[mck_id].m_sel_id;
+ div_clk_id = mck_div[mck_id].div_clk_id;
+
+ /* select apll */
+ if (m_sel_id >= 0) {
+ ret = mt8196_afe_enable_clk(afe, afe_priv->clk[m_sel_id]);
+ if (ret)
+ return ret;
+
+ ret = mt8196_afe_set_clk_parent(afe, afe_priv->clk[m_sel_id],
+ afe_priv->clk[apll_clk_id]);
+ if (ret)
+ return ret;
+ }
+
+ /* enable div, set rate */
+ if (div_clk_id < 0) {
+ dev_err(afe->dev, "invalid div_clk_id %d\n", div_clk_id);
+ return -EINVAL;
+ }
+ if (div_clk_id == MT8196_CLK_TOP_APLL12_DIV_TDMOUT_B)
+ rate = rate * 16;
+
+ ret = mt8196_afe_enable_clk(afe, afe_priv->clk[div_clk_id]);
+ if (ret)
+ return ret;
+
+ ret = mt8196_afe_set_clk_rate(afe, afe_priv->clk[div_clk_id], rate);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+int mt8196_mck_disable(struct mtk_base_afe *afe, int mck_id)
+{
+ struct mt8196_afe_private *afe_priv = afe->platform_priv;
+ int m_sel_id;
+ int div_clk_id;
+
+ dev_dbg(afe->dev, "mck_id: %d.\n", mck_id);
+
+ if (mck_id < 0) {
+ dev_err(afe->dev, "mck_id = %d < 0\n", mck_id);
+ return -EINVAL;
+ }
+
+ m_sel_id = mck_div[mck_id].m_sel_id;
+ div_clk_id = mck_div[mck_id].div_clk_id;
+
+ if (div_clk_id < 0) {
+ dev_err(afe->dev, "div_clk_id = %d < 0\n",
+ div_clk_id);
+ return -EINVAL;
+ }
+
+ mt8196_afe_disable_clk(afe, afe_priv->clk[div_clk_id]);
+
+ if (m_sel_id >= 0)
+ mt8196_afe_disable_clk(afe, afe_priv->clk[m_sel_id]);
+
+ return 0;
+}
+
+int mt8196_afe_enable_reg_rw_clk(struct mtk_base_afe *afe)
+{
+ struct mt8196_afe_private *afe_priv = afe->platform_priv;
+
+ /* bus clock for AFE external access, like DRAM */
+ mt8196_afe_enable_clk(afe, afe_priv->clk[MT8196_CLK_TOP_ADSP_SEL]);
+
+ /* bus clock for AFE internal access, like AFE SRAM */
+ mt8196_afe_enable_clk(afe, afe_priv->clk[MT8196_CLK_VLP_MUX_AUDIOINTBUS]);
+ mt8196_afe_set_clk_parent(afe, afe_priv->clk[MT8196_CLK_VLP_MUX_AUDIOINTBUS],
+ afe_priv->clk[MT8196_CLK_VLP_CLK26M]);
+ /* enable audio vlp clock source */
+ mt8196_afe_enable_clk(afe, afe_priv->clk[MT8196_CLK_VLP_MUX_AUDIO_H]);
+ mt8196_afe_set_clk_parent(afe, afe_priv->clk[MT8196_CLK_VLP_MUX_AUDIO_H],
+ afe_priv->clk[MT8196_CLK_VLP_CLK26M]);
+
+ /* AFE hw clock */
+ /* IPM2.0: USE HOPPING & 26M */
+ /* set in the regmap_register_patch */
+ return 0;
+}
+
+int mt8196_afe_disable_reg_rw_clk(struct mtk_base_afe *afe)
+{
+ struct mt8196_afe_private *afe_priv = afe->platform_priv;
+
+ /* IPM2.0: Use HOPPING & 26M */
+ /* set in the regmap_register_patch */
+
+ mt8196_afe_set_clk_parent(afe, afe_priv->clk[MT8196_CLK_VLP_MUX_AUDIO_H],
+ afe_priv->clk[MT8196_CLK_VLP_CLK26M]);
+
+ mt8196_afe_disable_clk(afe, afe_priv->clk[MT8196_CLK_VLP_MUX_AUDIO_H]);
+ mt8196_afe_set_clk_parent(afe, afe_priv->clk[MT8196_CLK_VLP_MUX_AUDIOINTBUS],
+ afe_priv->clk[MT8196_CLK_VLP_CLK26M]);
+ mt8196_afe_disable_clk(afe, afe_priv->clk[MT8196_CLK_VLP_MUX_AUDIOINTBUS]);
+ mt8196_afe_disable_clk(afe, afe_priv->clk[MT8196_CLK_TOP_ADSP_SEL]);
+ return 0;
+}
+
+int mt8196_afe_enable_main_clock(struct mtk_base_afe *afe)
+{
+ mt8196_afe_enable_top_cg(afe, MT8196_AUDIO_26M_EN_ON);
+ return 0;
+}
+
+int mt8196_afe_disable_main_clock(struct mtk_base_afe *afe)
+{
+ mt8196_afe_disable_top_cg(afe, MT8196_AUDIO_26M_EN_ON);
+ return 0;
+}
+
+int mt8196_init_clock(struct mtk_base_afe *afe)
+{
+ struct mt8196_afe_private *afe_priv = afe->platform_priv;
+ int ret = 0;
+ int i = 0;
+
+ afe_priv->clk = devm_kcalloc(afe->dev, MT8196_CLK_NUM, sizeof(*afe_priv->clk),
+ GFP_KERNEL);
+ if (!afe_priv->clk)
+ return -ENOMEM;
+
+ for (i = 0; i < MT8196_CLK_NUM; i++) {
+ afe_priv->clk[i] = devm_clk_get(afe->dev, aud_clks[i]);
+ if (IS_ERR(afe_priv->clk[i])) {
+ dev_err(afe->dev, "devm_clk_get %s fail\n", aud_clks[i]);
+ return PTR_ERR(afe_priv->clk[i]);
+ }
+ }
+
+ afe_priv->vlp_ck = syscon_regmap_lookup_by_phandle(afe->dev->of_node,
+ "vlpcksys");
+ if (IS_ERR(afe_priv->vlp_ck)) {
+ dev_err(afe->dev, "Cannot find vlpcksys\n");
+ return PTR_ERR(afe_priv->vlp_ck);
+ }
+
+ mt8196_afe_apll_init(afe);
+
+ ret = mt8196_afe_disable_apll(afe);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
diff --git a/sound/soc/mediatek/mt8196/mt8196-afe-clk.h b/sound/soc/mediatek/mt8196/mt8196-afe-clk.h
new file mode 100644
index 000000000000..854da3844104
--- /dev/null
+++ b/sound/soc/mediatek/mt8196/mt8196-afe-clk.h
@@ -0,0 +1,80 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * mt8196-afe-clk.h -- Mediatek MT8196 AFE Clock Control definitions
+ *
+ * Copyright (c) 2024 MediaTek Inc.
+ * Author: Darren Ye <darren.ye@mediatek.com>
+ */
+
+#ifndef _MT8196_AFE_CLOCK_CTRL_H_
+#define _MT8196_AFE_CLOCK_CTRL_H_
+
+/* vlp_cksys_clk: 0x1c016000 */
+#define VLP_APLL1_TUNER_CON0 0x02a4
+#define VLP_APLL2_TUNER_CON0 0x02a8
+
+/* vlp apll1 tuner default value*/
+#define VLP_APLL1_TUNER_CON0_VALUE 0x6f28bd4d
+/* vlp apll2 tuner default value + 1*/
+#define VLP_APLL2_TUNER_CON0_VALUE 0x78fd5265
+
+/* APLL */
+#define APLL1_W_NAME "APLL1"
+#define APLL2_W_NAME "APLL2"
+
+enum {
+ MT8196_APLL1 = 0,
+ MT8196_APLL2,
+};
+
+enum {
+ /* vlp clk */
+ MT8196_CLK_VLP_MUX_AUDIOINTBUS,
+ MT8196_CLK_VLP_MUX_AUD_ENG1,
+ MT8196_CLK_VLP_MUX_AUD_ENG2,
+ MT8196_CLK_VLP_MUX_AUDIO_H,
+ MT8196_CLK_VLP_CLK26M,
+ /* pll */
+ MT8196_CLK_TOP_APLL1_CK,
+ MT8196_CLK_TOP_APLL2_CK,
+ /* divider */
+ MT8196_CLK_TOP_APLL1_D4,
+ MT8196_CLK_TOP_APLL2_D4,
+ MT8196_CLK_TOP_APLL12_DIV_I2SIN0,
+ MT8196_CLK_TOP_APLL12_DIV_I2SIN1,
+ MT8196_CLK_TOP_APLL12_DIV_FMI2S,
+ MT8196_CLK_TOP_APLL12_DIV_TDMOUT_M,
+ MT8196_CLK_TOP_APLL12_DIV_TDMOUT_B,
+ /* mux */
+ MT8196_CLK_TOP_MUX_AUD_1,
+ MT8196_CLK_TOP_MUX_AUD_2,
+ MT8196_CLK_TOP_I2SIN0_M_SEL,
+ MT8196_CLK_TOP_I2SIN1_M_SEL,
+ MT8196_CLK_TOP_FMI2S_M_SEL,
+ MT8196_CLK_TOP_TDMOUT_M_SEL,
+ MT8196_CLK_TOP_ADSP_SEL,
+ /* top 26m */
+ MT8196_CLK_TOP_CLK26M,
+ MT8196_CLK_NUM,
+};
+
+struct mtk_base_afe;
+
+int mt8196_mck_enable(struct mtk_base_afe *afe, int mck_id, int rate);
+int mt8196_mck_disable(struct mtk_base_afe *afe, int mck_id);
+int mt8196_get_apll_rate(struct mtk_base_afe *afe, int apll);
+int mt8196_get_apll_by_rate(struct mtk_base_afe *afe, int rate);
+int mt8196_get_apll_by_name(struct mtk_base_afe *afe, const char *name);
+int mt8196_init_clock(struct mtk_base_afe *afe);
+int mt8196_afe_enable_clk(struct mtk_base_afe *afe, struct clk *clk);
+void mt8196_afe_disable_clk(struct mtk_base_afe *afe, struct clk *clk);
+int mt8196_apll1_enable(struct mtk_base_afe *afe);
+void mt8196_apll1_disable(struct mtk_base_afe *afe);
+int mt8196_apll2_enable(struct mtk_base_afe *afe);
+void mt8196_apll2_disable(struct mtk_base_afe *afe);
+int mt8196_afe_enable_main_clock(struct mtk_base_afe *afe);
+int mt8196_afe_disable_main_clock(struct mtk_base_afe *afe);
+int mt8196_afe_enable_reg_rw_clk(struct mtk_base_afe *afe);
+int mt8196_afe_disable_reg_rw_clk(struct mtk_base_afe *afe);
+
+#endif
--
2.45.2
^ permalink raw reply related [flat|nested] 24+ messages in thread
* [PATCH v6 04/10] ASoC: mediatek: mt8196: support ADDA in platform driver
2025-07-08 11:15 [PATCH v6 00/10] ASoC: mediatek: Add support for MT8196 SoC Darren.Ye
2025-07-08 11:15 ` [PATCH v6 01/10] ASoC: mediatek: common: modify mtk afe platform driver for mt8196 Darren.Ye
2025-07-08 11:15 ` [PATCH v6 03/10] ASoC: mediatek: mt8196: support audio clock control Darren.Ye
@ 2025-07-08 11:15 ` Darren.Ye
2025-07-29 11:45 ` Chen-Yu Tsai
2025-07-08 11:15 ` [PATCH v6 06/10] ASoC: mediatek: mt8196: support TDM " Darren.Ye
` (6 subsequent siblings)
9 siblings, 1 reply; 24+ messages in thread
From: Darren.Ye @ 2025-07-08 11:15 UTC (permalink / raw)
To: Liam Girdwood, Mark Brown, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Matthias Brugger, AngeloGioacchino Del Regno,
Jaroslav Kysela, Takashi Iwai, Linus Walleij, Bartosz Golaszewski
Cc: linux-sound, devicetree, linux-kernel, linux-arm-kernel,
linux-mediatek, linux-gpio, Darren Ye
From: Darren Ye <darren.ye@mediatek.com>
Add mt8196 ADDA DAI driver support.
Signed-off-by: Darren Ye <darren.ye@mediatek.com>
---
sound/soc/mediatek/mt8196/mt8196-dai-adda.c | 888 ++++++++++++++++++++
1 file changed, 888 insertions(+)
create mode 100644 sound/soc/mediatek/mt8196/mt8196-dai-adda.c
diff --git a/sound/soc/mediatek/mt8196/mt8196-dai-adda.c b/sound/soc/mediatek/mt8196/mt8196-dai-adda.c
new file mode 100644
index 000000000000..e44332ffd0c4
--- /dev/null
+++ b/sound/soc/mediatek/mt8196/mt8196-dai-adda.c
@@ -0,0 +1,888 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * MediaTek ALSA SoC Audio DAI ADDA Control
+ *
+ * Copyright (c) 2024 MediaTek Inc.
+ * Author: Darren Ye <darren.ye@mediatek.com>
+ */
+
+#include <linux/regmap.h>
+#include <linux/delay.h>
+#include "mt8196-afe-clk.h"
+#include "mt8196-afe-common.h"
+#include "mt8196-interconnection.h"
+
+enum {
+ UL_IIR_SW = 0,
+ UL_IIR_5HZ,
+ UL_IIR_10HZ,
+ UL_IIR_25HZ,
+ UL_IIR_50HZ,
+ UL_IIR_75HZ,
+};
+
+enum {
+ MTK_AFE_ADDA_UL_RATE_8K = 0,
+ MTK_AFE_ADDA_UL_RATE_16K = 1,
+ MTK_AFE_ADDA_UL_RATE_32K = 2,
+ MTK_AFE_ADDA_UL_RATE_48K = 3,
+ MTK_AFE_ADDA_UL_RATE_96K = 4,
+ MTK_AFE_ADDA_UL_RATE_192K = 5,
+ MTK_AFE_ADDA_UL_RATE_48K_HD = 6,
+};
+
+enum {
+ MTK_AFE_MTKAIF_RATE_8K = 0x0,
+ MTK_AFE_MTKAIF_RATE_12K = 0x1,
+ MTK_AFE_MTKAIF_RATE_16K = 0x2,
+ MTK_AFE_MTKAIF_RATE_24K = 0x3,
+ MTK_AFE_MTKAIF_RATE_32K = 0x4,
+ MTK_AFE_MTKAIF_RATE_48K = 0x5,
+ MTK_AFE_MTKAIF_RATE_64K = 0x6,
+ MTK_AFE_MTKAIF_RATE_96K = 0x7,
+ MTK_AFE_MTKAIF_RATE_128K = 0x8,
+ MTK_AFE_MTKAIF_RATE_192K = 0x9,
+ MTK_AFE_MTKAIF_RATE_256K = 0xa,
+ MTK_AFE_MTKAIF_RATE_384K = 0xb,
+ MTK_AFE_MTKAIF_RATE_11K = 0x10,
+ MTK_AFE_MTKAIF_RATE_22K = 0x11,
+ MTK_AFE_MTKAIF_RATE_44K = 0x12,
+ MTK_AFE_MTKAIF_RATE_88K = 0x13,
+ MTK_AFE_MTKAIF_RATE_176K = 0x14,
+ MTK_AFE_MTKAIF_RATE_352K = 0x15,
+};
+
+struct mtk_afe_adda_priv {
+ int dl_rate;
+ int ul_rate;
+};
+
+static unsigned int adda_ul_rate_transform(struct mtk_base_afe *afe,
+ unsigned int rate)
+{
+ switch (rate) {
+ case 8000:
+ return MTK_AFE_ADDA_UL_RATE_8K;
+ case 16000:
+ return MTK_AFE_ADDA_UL_RATE_16K;
+ case 32000:
+ return MTK_AFE_ADDA_UL_RATE_32K;
+ case 48000:
+ return MTK_AFE_ADDA_UL_RATE_48K;
+ case 96000:
+ return MTK_AFE_ADDA_UL_RATE_96K;
+ case 192000:
+ return MTK_AFE_ADDA_UL_RATE_192K;
+ default:
+ dev_info(afe->dev, "rate %d invalid, use 48kHz!!!\n", rate);
+ return MTK_AFE_ADDA_UL_RATE_48K;
+ }
+}
+
+static unsigned int mtkaif_rate_transform(struct mtk_base_afe *afe,
+ unsigned int rate)
+{
+ switch (rate) {
+ case 8000:
+ return MTK_AFE_MTKAIF_RATE_8K;
+ case 11025:
+ return MTK_AFE_MTKAIF_RATE_11K;
+ case 12000:
+ return MTK_AFE_MTKAIF_RATE_12K;
+ case 16000:
+ return MTK_AFE_MTKAIF_RATE_16K;
+ case 22050:
+ return MTK_AFE_MTKAIF_RATE_22K;
+ case 24000:
+ return MTK_AFE_MTKAIF_RATE_24K;
+ case 32000:
+ return MTK_AFE_MTKAIF_RATE_32K;
+ case 44100:
+ return MTK_AFE_MTKAIF_RATE_44K;
+ case 48000:
+ return MTK_AFE_MTKAIF_RATE_48K;
+ case 96000:
+ return MTK_AFE_MTKAIF_RATE_96K;
+ case 192000:
+ return MTK_AFE_MTKAIF_RATE_192K;
+ default:
+ dev_info(afe->dev, "rate %d invalid, use 48kHz!!!\n", rate);
+ return MTK_AFE_MTKAIF_RATE_48K;
+ }
+}
+
+enum {
+ SUPPLY_SEQ_ADDA_AFE_ON,
+ SUPPLY_SEQ_ADDA_FIFO,
+ SUPPLY_SEQ_ADDA_AP_DMIC,
+ SUPPLY_SEQ_ADDA_UL_ON,
+};
+
+static int mtk_adda_ul_src_dmic_phase_sync(struct mtk_base_afe *afe)
+{
+ dev_dbg(afe->dev, "set dmic phase sync\n");
+ // ul0~1
+ regmap_update_bits(afe->regmap, AFE_ADDA_ULSRC_PHASE_CON1,
+ UL0_PHASE_SYNC_HCLK_SET_MASK_SFT,
+ 0x1 << UL0_PHASE_SYNC_HCLK_SET_SFT);
+ regmap_update_bits(afe->regmap, AFE_ADDA_ULSRC_PHASE_CON1,
+ UL0_PHASE_SYNC_FCLK_SET_MASK_SFT,
+ 0x1 << UL0_PHASE_SYNC_FCLK_SET_SFT);
+ regmap_update_bits(afe->regmap, AFE_ADDA_ULSRC_PHASE_CON1,
+ UL1_PHASE_SYNC_HCLK_SET_MASK_SFT,
+ 0x1 << UL1_PHASE_SYNC_HCLK_SET_SFT);
+ regmap_update_bits(afe->regmap, AFE_ADDA_ULSRC_PHASE_CON1,
+ UL1_PHASE_SYNC_FCLK_SET_MASK_SFT,
+ 0x1 << UL1_PHASE_SYNC_FCLK_SET_SFT);
+ // dmic 0
+ regmap_update_bits(afe->regmap, AFE_ADDA_ULSRC_PHASE_CON1,
+ DMIC0_PHASE_SYNC_FCLK_SET_MASK_SFT,
+ 0x1 << DMIC0_PHASE_SYNC_FCLK_SET_SFT);
+ regmap_update_bits(afe->regmap, AFE_ADDA_ULSRC_PHASE_CON1,
+ DMIC0_PHASE_SYNC_HCLK_SET_MASK_SFT,
+ 0x1 << DMIC0_PHASE_SYNC_HCLK_SET_SFT);
+ // dmic 1
+ regmap_update_bits(afe->regmap, AFE_ADDA_ULSRC_PHASE_CON1,
+ DMIC1_PHASE_SYNC_FCLK_SET_MASK_SFT,
+ 0x1 << DMIC1_PHASE_SYNC_FCLK_SET_SFT);
+ regmap_update_bits(afe->regmap, AFE_ADDA_ULSRC_PHASE_CON1,
+ DMIC1_PHASE_SYNC_HCLK_SET_MASK_SFT,
+ 0x1 << DMIC1_PHASE_SYNC_HCLK_SET_SFT);
+ // ul0~1 phase sync clock
+ regmap_update_bits(afe->regmap, AFE_ADDA_ULSRC_PHASE_CON0,
+ DMIC1_PHASE_HCLK_SEL_MASK_SFT,
+ 0x1 << DMIC1_PHASE_HCLK_SEL_SFT);
+ regmap_update_bits(afe->regmap, AFE_ADDA_ULSRC_PHASE_CON0,
+ DMIC1_PHASE_FCLK_SEL_MASK_SFT,
+ 0x1 << DMIC1_PHASE_FCLK_SEL_SFT);
+ regmap_update_bits(afe->regmap, AFE_ADDA_ULSRC_PHASE_CON0,
+ DMIC0_PHASE_HCLK_SEL_MASK_SFT,
+ 0x1 << DMIC0_PHASE_HCLK_SEL_SFT);
+ regmap_update_bits(afe->regmap, AFE_ADDA_ULSRC_PHASE_CON0,
+ DMIC0_PHASE_FCLK_SEL_MASK_SFT,
+ 0x1 << DMIC0_PHASE_FCLK_SEL_SFT);
+ // dmic 0
+ regmap_update_bits(afe->regmap, AFE_ADDA_ULSRC_PHASE_CON0,
+ UL1_PHASE_HCLK_SEL_MASK_SFT,
+ 0x2 << UL1_PHASE_HCLK_SEL_SFT);
+ regmap_update_bits(afe->regmap, AFE_ADDA_ULSRC_PHASE_CON0,
+ UL1_PHASE_FCLK_SEL_MASK_SFT,
+ 0x2 << UL1_PHASE_FCLK_SEL_SFT);
+ // dmic 1
+ regmap_update_bits(afe->regmap, AFE_ADDA_ULSRC_PHASE_CON0,
+ UL0_PHASE_HCLK_SEL_MASK_SFT,
+ 0x2 << UL0_PHASE_HCLK_SEL_SFT);
+ regmap_update_bits(afe->regmap, AFE_ADDA_ULSRC_PHASE_CON0,
+ UL0_PHASE_FCLK_SEL_MASK_SFT,
+ 0x2 << UL0_PHASE_FCLK_SEL_SFT);
+
+ return 0;
+}
+
+static int mtk_adda_ul_src_dmic_phase_sync_clock(struct mtk_base_afe *afe)
+{
+ dev_dbg(afe->dev, "dmic turn on phase sync clk\n");
+ regmap_update_bits(afe->regmap, AFE_ADDA_ULSRC_PHASE_CON0,
+ UL_PHASE_SYNC_HCLK_1_ON_MASK_SFT,
+ 0x1 << UL_PHASE_SYNC_HCLK_1_ON_SFT);
+ regmap_update_bits(afe->regmap, AFE_ADDA_ULSRC_PHASE_CON0,
+ UL_PHASE_SYNC_HCLK_0_ON_MASK_SFT,
+ 0x1 << UL_PHASE_SYNC_HCLK_0_ON_SFT);
+
+ regmap_update_bits(afe->regmap, AFE_ADDA_ULSRC_PHASE_CON0,
+ UL_PHASE_SYNC_FCLK_1_ON_MASK_SFT,
+ 0x1 << UL_PHASE_SYNC_FCLK_1_ON_SFT);
+ regmap_update_bits(afe->regmap, AFE_ADDA_ULSRC_PHASE_CON0,
+ UL_PHASE_SYNC_FCLK_0_ON_MASK_SFT,
+ 0x1 << UL_PHASE_SYNC_FCLK_0_ON_SFT);
+
+ return 0;
+}
+
+static int mtk_adda_ul_src_dmic(struct mtk_base_afe *afe, int id)
+{
+ unsigned int reg_con0 = 0, reg_con1 = 0;
+
+ dev_dbg(afe->dev, "id: %d\n", id);
+
+ switch (id) {
+ case MT8196_DAI_ADDA:
+ case MT8196_DAI_AP_DMIC:
+ reg_con0 = AFE_ADDA_UL0_SRC_CON0;
+ reg_con1 = AFE_ADDA_UL0_SRC_CON1;
+ break;
+ case MT8196_DAI_ADDA_CH34:
+ case MT8196_DAI_AP_DMIC_CH34:
+ reg_con0 = AFE_ADDA_UL1_SRC_CON0;
+ reg_con1 = AFE_ADDA_UL1_SRC_CON1;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (id) {
+ case MT8196_DAI_AP_DMIC:
+ dev_dbg(afe->dev, "clear mtkaifv4 ul ch1ch2 mux\n");
+ regmap_update_bits(afe->regmap, AFE_ADDA_MTKAIFV4_RX_CFG0,
+ MTKAIFV4_UL_CH1CH2_IN_EN_SEL_MASK_SFT,
+ 0x0 << MTKAIFV4_UL_CH1CH2_IN_EN_SEL_SFT);
+ break;
+ case MT8196_DAI_AP_DMIC_CH34:
+ dev_dbg(afe->dev, "clear mtkaifv4 ul ch3ch4 mux\n");
+ regmap_update_bits(afe->regmap, AFE_ADDA_MTKAIFV4_RX_CFG0,
+ MTKAIFV4_UL_CH3CH4_IN_EN_SEL_MASK_SFT,
+ 0x0 << MTKAIFV4_UL_CH3CH4_IN_EN_SEL_SFT);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* choose Phase */
+ regmap_update_bits(afe->regmap, reg_con0,
+ UL_DMIC_PHASE_SEL_CH1_MASK_SFT,
+ 0x0 << UL_DMIC_PHASE_SEL_CH1_SFT);
+ regmap_update_bits(afe->regmap, reg_con0,
+ UL_DMIC_PHASE_SEL_CH2_MASK_SFT,
+ 0x4 << UL_DMIC_PHASE_SEL_CH2_SFT);
+
+ /* dmic mode, 3.25M*/
+ regmap_update_bits(afe->regmap, reg_con0,
+ DIGMIC_3P25M_1P625M_SEL_CTL_MASK_SFT,
+ 0x0);
+ regmap_update_bits(afe->regmap, reg_con0,
+ DMIC_LOW_POWER_MODE_CTL_MASK_SFT,
+ 0x0);
+
+ /* turn on dmic, ch1, ch2 */
+ regmap_update_bits(afe->regmap, reg_con0,
+ UL_SDM_3_LEVEL_CTL_MASK_SFT,
+ 0x1 << UL_SDM_3_LEVEL_CTL_SFT);
+ regmap_update_bits(afe->regmap, reg_con0,
+ UL_MODE_3P25M_CH1_CTL_MASK_SFT,
+ 0x1 << UL_MODE_3P25M_CH1_CTL_SFT);
+ regmap_update_bits(afe->regmap, reg_con0,
+ UL_MODE_3P25M_CH2_CTL_MASK_SFT,
+ 0x1 << UL_MODE_3P25M_CH2_CTL_SFT);
+
+ /* ul gain: gain = 0x7fff/positive_gain = 0x0/gain_mode = 0x10 */
+ regmap_update_bits(afe->regmap, reg_con1,
+ ADDA_UL_GAIN_VALUE_MASK_SFT,
+ 0x7fff << ADDA_UL_GAIN_VALUE_SFT);
+ regmap_update_bits(afe->regmap, reg_con1,
+ ADDA_UL_POSTIVEGAIN_MASK_SFT,
+ 0x0 << ADDA_UL_POSTIVEGAIN_SFT);
+ /* gain_mode = 0x10: Add 0.5 gain at CIC output */
+ regmap_update_bits(afe->regmap, reg_con1,
+ GAIN_MODE_MASK_SFT,
+ 0x02 << GAIN_MODE_SFT);
+ return 0;
+}
+
+static int mtk_adda_ul_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
+
+ dev_dbg(afe->dev, "name %s, event 0x%x\n", w->name, event);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ /* should delayed 1/fs(smallest is 8k) = 125us before afe off */
+ usleep_range(120, 130);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int mtk_adda_ch34_ul_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
+
+ dev_dbg(afe->dev, "name %s, event 0x%x\n", w->name, event);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ /* should delayed 1/fs(smallest is 8k) = 125us before afe off */
+ usleep_range(120, 130);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int mtk_adda_ul_ap_dmic_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
+
+ dev_info(afe->dev, "name %s, event 0x%x\n", w->name, event);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ /* should delayed 1/fs(smallest is 8k) = 125us before afe off */
+ usleep_range(120, 130);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int mtk_adda_ch34_ul_ap_dmic_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
+
+ dev_dbg(afe->dev, "name %s, event 0x%x\n", w->name, event);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ /* should delayed 1/fs(smallest is 8k) = 125us before afe off */
+ usleep_range(120, 130);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static const struct snd_kcontrol_new mtk_adda_controls[] = {
+};
+
+/* ADDA UL MUX */
+#define ADDA_UL_MUX_MASK 0x3
+enum {
+ ADDA_UL_MUX_MTKAIF = 0,
+ ADDA_UL_MUX_AP_DMIC,
+ ADDA_UL_MUX_AP_DMIC_MULTICH,
+};
+
+static const char *const adda_ul_mux_map[] = {
+ "MTKAIF", "AP_DMIC", "AP_DMIC_MULTI_CH",
+};
+
+static int adda_ul_map_value[] = {
+ ADDA_UL_MUX_MTKAIF,
+ ADDA_UL_MUX_AP_DMIC,
+ ADDA_UL_MUX_AP_DMIC_MULTICH,
+};
+
+static SOC_VALUE_ENUM_SINGLE_DECL(adda_ul_mux_map_enum,
+ SND_SOC_NOPM,
+ 0,
+ ADDA_UL_MUX_MASK,
+ adda_ul_mux_map,
+ adda_ul_map_value);
+
+static const struct snd_kcontrol_new adda_ul_mux_control =
+ SOC_DAPM_ENUM("ADDA_UL_MUX Select", adda_ul_mux_map_enum);
+
+static const struct snd_kcontrol_new adda_ch34_ul_mux_control =
+ SOC_DAPM_ENUM("ADDA_CH34_UL_MUX Select", adda_ul_mux_map_enum);
+
+static const struct snd_soc_dapm_widget mtk_dai_adda_widgets[] = {
+ /* inter-connections */
+ SND_SOC_DAPM_SUPPLY_S("ADDA Enable", SUPPLY_SEQ_ADDA_AFE_ON,
+ AUDIO_ENGEN_CON0, AUDIO_F3P25M_EN_ON_SFT, 0,
+ NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("ADDA Capture Enable", SUPPLY_SEQ_ADDA_UL_ON,
+ AFE_ADDA_UL0_SRC_CON0,
+ UL_SRC_ON_TMP_CTL_SFT, 0,
+ mtk_adda_ul_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY_S("ADDA CH34 Capture Enable", SUPPLY_SEQ_ADDA_UL_ON,
+ AFE_ADDA_UL1_SRC_CON0,
+ UL_SRC_ON_TMP_CTL_SFT, 0,
+ mtk_adda_ch34_ul_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY_S("AP_DMIC_EN", SUPPLY_SEQ_ADDA_AP_DMIC,
+ AFE_ADDA_UL0_SRC_CON0,
+ UL_AP_DMIC_ON_SFT, 0,
+ mtk_adda_ul_ap_dmic_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY_S("AP_DMIC_CH34_EN", SUPPLY_SEQ_ADDA_AP_DMIC,
+ AFE_ADDA_UL1_SRC_CON0,
+ UL_AP_DMIC_ON_SFT, 0,
+ mtk_adda_ch34_ul_ap_dmic_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY_S("ADDA_FIFO", SUPPLY_SEQ_ADDA_FIFO,
+ AFE_ADDA_UL0_SRC_CON1,
+ FIFO_SOFT_RST_SFT, 1,
+ NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("ADDA_CH34_FIFO", SUPPLY_SEQ_ADDA_FIFO,
+ AFE_ADDA_UL1_SRC_CON1,
+ FIFO_SOFT_RST_SFT, 1,
+ NULL, 0),
+ SND_SOC_DAPM_MUX("ADDA_UL_Mux", SND_SOC_NOPM, 0, 0,
+ &adda_ul_mux_control),
+ SND_SOC_DAPM_MUX("ADDA_CH34_UL_Mux", SND_SOC_NOPM, 0, 0,
+ &adda_ch34_ul_mux_control),
+ SND_SOC_DAPM_INPUT("AP_DMIC_INPUT"),
+ SND_SOC_DAPM_INPUT("AP_DMIC_CH34_INPUT"),
+};
+
+static const struct snd_soc_dapm_route mtk_dai_adda_routes[] = {
+ /* capture */
+ {"ADDA_UL_Mux", "MTKAIF", "ADDA Capture"},
+ {"ADDA_UL_Mux", "AP_DMIC", "AP DMIC Capture"},
+ {"ADDA_UL_Mux", "AP_DMIC_MULTI_CH", "AP DMIC MULTICH Capture"},
+
+ {"ADDA_CH34_UL_Mux", "MTKAIF", "ADDA CH34 Capture"},
+ {"ADDA_CH34_UL_Mux", "AP_DMIC", "AP DMIC CH34 Capture"},
+ {"ADDA_CH34_UL_Mux", "AP_DMIC_MULTI_CH", "AP DMIC MULTICH Capture"},
+
+ {"AP DMIC Capture", NULL, "ADDA Enable"},
+ {"AP DMIC Capture", NULL, "ADDA Capture Enable"},
+ {"AP DMIC Capture", NULL, "ADDA_FIFO"},
+ {"AP DMIC Capture", NULL, "AP_DMIC_EN"},
+
+ {"AP DMIC CH34 Capture", NULL, "ADDA Enable"},
+ {"AP DMIC CH34 Capture", NULL, "ADDA CH34 Capture Enable"},
+ {"AP DMIC CH34 Capture", NULL, "ADDA_CH34_FIFO"},
+ {"AP DMIC CH34 Capture", NULL, "AP_DMIC_CH34_EN"},
+
+ {"AP DMIC MULTICH Capture", NULL, "ADDA Enable"},
+ {"AP DMIC MULTICH Capture", NULL, "ADDA Capture Enable"},
+ {"AP DMIC MULTICH Capture", NULL, "ADDA CH34 Capture Enable"},
+ {"AP DMIC MULTICH Capture", NULL, "ADDA_FIFO"},
+ {"AP DMIC MULTICH Capture", NULL, "ADDA_CH34_FIFO"},
+ {"AP DMIC MULTICH Capture", NULL, "AP_DMIC_EN"},
+ {"AP DMIC MULTICH Capture", NULL, "AP_DMIC_CH34_EN"},
+ {"AP DMIC Capture", NULL, "AP_DMIC_INPUT"},
+ {"AP DMIC CH34 Capture", NULL, "AP_DMIC_CH34_INPUT"},
+ {"AP DMIC MULTICH Capture", NULL, "AP_DMIC_INPUT"},
+};
+
+/* dai ops */
+static int mtk_dai_adda_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+ struct mt8196_afe_private *afe_priv = afe->platform_priv;
+ unsigned int rate = params_rate(params);
+ unsigned int mtkaif_rate = 0;
+ int id = dai->id;
+ struct mtk_afe_adda_priv *adda_priv;
+
+ if (id >= MT8196_DAI_NUM || id < 0)
+ return -EINVAL;
+
+ adda_priv = afe_priv->dai_priv[id];
+
+ dev_info(afe->dev, "id %d, stream %d, rate %d\n",
+ id,
+ substream->stream,
+ rate);
+
+ if (!adda_priv)
+ return -EINVAL;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ adda_priv->dl_rate = rate;
+
+ /* get mtkaif dl rate */
+ mtkaif_rate =
+ mtkaif_rate_transform(afe, adda_priv->dl_rate);
+ if (id == MT8196_DAI_ADDA) {
+ /* MTKAIF sample rate config */
+ regmap_update_bits(afe->regmap, AFE_ADDA_MTKAIFV4_TX_CFG0,
+ MTKAIFV4_TXIF_INPUT_MODE_MASK_SFT,
+ mtkaif_rate << MTKAIFV4_TXIF_INPUT_MODE_SFT);
+ /* AFE_ADDA_MTKAIFV4_TX_CFG0 */
+ regmap_update_bits(afe->regmap, AFE_ADDA_MTKAIFV4_TX_CFG0,
+ MTKAIFV4_TXIF_FOUR_CHANNEL_MASK_SFT,
+ 0x0 << MTKAIFV4_TXIF_FOUR_CHANNEL_SFT);
+ regmap_update_bits(afe->regmap, AFE_ADDA_MTKAIFV4_TX_CFG0,
+ MTKAIFV4_ADDA_OUT_EN_SEL_MASK_SFT,
+ 0x1 << MTKAIFV4_ADDA_OUT_EN_SEL_SFT);
+ regmap_update_bits(afe->regmap, AFE_ADDA_MTKAIFV4_TX_CFG0,
+ MTKAIFV4_ADDA6_OUT_EN_SEL_MASK_SFT,
+ 0x1 << MTKAIFV4_ADDA6_OUT_EN_SEL_SFT);
+ regmap_update_bits(afe->regmap, AFE_ADDA_MTKAIFV4_TX_CFG0,
+ MTKAIFV4_TXIF_V4_MASK_SFT,
+ 0x1 << MTKAIFV4_TXIF_V4_SFT);
+ regmap_update_bits(afe->regmap, AFE_ADDA_MTKAIFV4_TX_CFG0,
+ MTKAIFV4_TXIF_EN_SEL_MASK_SFT,
+ 0x0 << MTKAIFV4_TXIF_EN_SEL_SFT);
+ /* clean predistortion */
+ } else {
+ /* MTKAIF sample rate config */
+ regmap_update_bits(afe->regmap, AFE_ADDA6_MTKAIFV4_TX_CFG0,
+ ADDA6_MTKAIFV4_TXIF_INPUT_MODE_MASK_SFT,
+ mtkaif_rate << ADDA6_MTKAIFV4_TXIF_INPUT_MODE_SFT);
+ /* AFE_ADDA6_MTKAIFV4_TX_CFG0 */
+ regmap_update_bits(afe->regmap, AFE_ADDA6_MTKAIFV4_TX_CFG0,
+ ADDA6_MTKAIFV4_TXIF_FOUR_CHANNEL_MASK_SFT,
+ 0x0 << ADDA6_MTKAIFV4_TXIF_FOUR_CHANNEL_SFT);
+ regmap_update_bits(afe->regmap, AFE_ADDA6_MTKAIFV4_TX_CFG0,
+ ADDA6_MTKAIFV4_TXIF_EN_SEL_MASK_SFT,
+ 0x1 << ADDA6_MTKAIFV4_TXIF_EN_SEL_SFT);
+ }
+ } else {
+ unsigned int voice_mode = 0;
+ unsigned int ul_src_con0 = 0; /* default value */
+
+ adda_priv->ul_rate = rate;
+
+ /* get mtkaif dl rate */
+ mtkaif_rate =
+ mtkaif_rate_transform(afe, adda_priv->ul_rate);
+
+ voice_mode = adda_ul_rate_transform(afe, rate);
+
+ ul_src_con0 |= (voice_mode << 17) & (0x7 << 17);
+
+ /* enable iir */
+ ul_src_con0 |= (1 << UL_IIR_ON_TMP_CTL_SFT) &
+ UL_IIR_ON_TMP_CTL_MASK_SFT;
+ ul_src_con0 |= (UL_IIR_SW << UL_IIRMODE_CTL_SFT) &
+ UL_IIRMODE_CTL_MASK_SFT;
+
+ regmap_update_bits(afe->regmap, AFE_ADDA_MTKAIFV4_RX_CFG0,
+ MTKAIFV4_RXIF_INPUT_MODE_MASK_SFT,
+ mtkaif_rate << MTKAIFV4_RXIF_INPUT_MODE_SFT);
+
+ regmap_update_bits(afe->regmap, AFE_ADDA6_MTKAIFV4_RX_CFG0,
+ ADDA6_MTKAIFV4_RXIF_INPUT_MODE_MASK_SFT,
+ mtkaif_rate << ADDA6_MTKAIFV4_RXIF_INPUT_MODE_SFT);
+
+ switch (id) {
+ case MT8196_DAI_ADDA:
+ case MT8196_DAI_AP_DMIC:
+ case MT8196_DAI_AP_DMIC_MULTICH:
+ regmap_update_bits(afe->regmap, AFE_ADDA_MTKAIFV4_RX_CFG0,
+ MTKAIFV4_RXIF_INPUT_MODE_MASK_SFT,
+ mtkaif_rate << MTKAIFV4_RXIF_INPUT_MODE_SFT);
+ /* AFE_ADDA_MTKAIFV4_RX_CFG0 */
+ regmap_update_bits(afe->regmap, AFE_ADDA_MTKAIFV4_RX_CFG0,
+ MTKAIFV4_RXIF_FOUR_CHANNEL_MASK_SFT,
+ 0x1 << MTKAIFV4_RXIF_FOUR_CHANNEL_SFT);
+ regmap_update_bits(afe->regmap, AFE_ADDA_MTKAIFV4_RX_CFG0,
+ MTKAIFV4_RXIF_EN_SEL_MASK_SFT,
+ 0x0 << MTKAIFV4_RXIF_EN_SEL_SFT);
+ /* [28] loopback mode
+ * 0: loopback adda tx to adda rx
+ * 1: loopback adda6 tx to adda rx
+ */
+ regmap_update_bits(afe->regmap, AFE_ADDA_MTKAIFV4_RX_CFG0,
+ MTKAIFV4_TXIF_EN_SEL_MASK_SFT,
+ 0x0 << MTKAIFV4_TXIF_EN_SEL_SFT);
+
+ regmap_update_bits(afe->regmap, AFE_ADDA_MTKAIFV4_RX_CFG0,
+ MTKAIFV4_UL_CH1CH2_IN_EN_SEL_MASK_SFT,
+ 0x1 << MTKAIFV4_UL_CH1CH2_IN_EN_SEL_SFT);
+ regmap_update_bits(afe->regmap, AFE_ADDA_MTKAIFV4_RX_CFG0,
+ MTKAIFV4_UL_CH3CH4_IN_EN_SEL_MASK_SFT,
+ 0x1 << MTKAIFV4_UL_CH3CH4_IN_EN_SEL_SFT);
+
+ /* 35Hz @ 48k */
+ regmap_write(afe->regmap,
+ AFE_ADDA_UL1_IIR_COEF_02_01, 0x00000000);
+ regmap_write(afe->regmap,
+ AFE_ADDA_UL1_IIR_COEF_04_03, 0x00003FB8);
+ regmap_write(afe->regmap,
+ AFE_ADDA_UL1_IIR_COEF_06_05, 0x3FB80000);
+ regmap_write(afe->regmap,
+ AFE_ADDA_UL1_IIR_COEF_08_07, 0x3FB80000);
+ regmap_write(afe->regmap,
+ AFE_ADDA_UL1_IIR_COEF_10_09, 0x0000C048);
+
+ regmap_write(afe->regmap,
+ AFE_ADDA_UL1_SRC_CON0, ul_src_con0);
+
+ /* mtkaif_rxif_data_mode = 0, amic */
+ regmap_update_bits(afe->regmap,
+ AFE_MTKAIF1_RX_CFG0,
+ 0x1 << 0,
+ 0x0 << 0);
+
+ /* 35Hz @ 48k */
+ regmap_write(afe->regmap,
+ AFE_ADDA_UL0_IIR_COEF_02_01, 0x00000000);
+ regmap_write(afe->regmap,
+ AFE_ADDA_UL0_IIR_COEF_04_03, 0x00003FB8);
+ regmap_write(afe->regmap,
+ AFE_ADDA_UL0_IIR_COEF_06_05, 0x3FB80000);
+ regmap_write(afe->regmap,
+ AFE_ADDA_UL0_IIR_COEF_08_07, 0x3FB80000);
+ regmap_write(afe->regmap,
+ AFE_ADDA_UL0_IIR_COEF_10_09, 0x0000C048);
+
+ regmap_write(afe->regmap,
+ AFE_ADDA_UL0_SRC_CON0, ul_src_con0);
+
+ /* mtkaif_rxif_data_mode = 0, amic */
+ regmap_update_bits(afe->regmap,
+ AFE_MTKAIF0_RX_CFG0,
+ 0x1 << 0,
+ 0x0 << 0);
+ break;
+ case MT8196_DAI_ADDA_CH34:
+ case MT8196_DAI_AP_DMIC_CH34:
+ /* AFE_ADDA_MTKAIFV4_RX_CFG0 */
+ regmap_update_bits(afe->regmap, AFE_ADDA_MTKAIFV4_RX_CFG0,
+ MTKAIFV4_RXIF_FOUR_CHANNEL_MASK_SFT,
+ 0x1 << MTKAIFV4_RXIF_FOUR_CHANNEL_SFT);
+ regmap_update_bits(afe->regmap, AFE_ADDA_MTKAIFV4_RX_CFG0,
+ MTKAIFV4_RXIF_EN_SEL_MASK_SFT,
+ 0x0 << MTKAIFV4_RXIF_EN_SEL_SFT);
+
+ regmap_update_bits(afe->regmap, AFE_ADDA_MTKAIFV4_RX_CFG0,
+ MTKAIFV4_UL_CH1CH2_IN_EN_SEL_MASK_SFT,
+ 0x1 << MTKAIFV4_UL_CH1CH2_IN_EN_SEL_SFT);
+ regmap_update_bits(afe->regmap, AFE_ADDA_MTKAIFV4_RX_CFG0,
+ MTKAIFV4_UL_CH3CH4_IN_EN_SEL_MASK_SFT,
+ 0x1 << MTKAIFV4_UL_CH3CH4_IN_EN_SEL_SFT);
+
+ /* 35Hz @ 48k */
+ regmap_write(afe->regmap,
+ AFE_ADDA_UL1_IIR_COEF_02_01, 0x00000000);
+ regmap_write(afe->regmap,
+ AFE_ADDA_UL1_IIR_COEF_04_03, 0x00003FB8);
+ regmap_write(afe->regmap,
+ AFE_ADDA_UL1_IIR_COEF_06_05, 0x3FB80000);
+ regmap_write(afe->regmap,
+ AFE_ADDA_UL1_IIR_COEF_08_07, 0x3FB80000);
+ regmap_write(afe->regmap,
+ AFE_ADDA_UL1_IIR_COEF_10_09, 0x0000C048);
+
+ regmap_write(afe->regmap,
+ AFE_ADDA_UL1_SRC_CON0, ul_src_con0);
+
+ /* mtkaif_rxif_data_mode = 0, amic */
+ regmap_update_bits(afe->regmap,
+ AFE_MTKAIF1_RX_CFG0,
+ 0x1 << 0,
+ 0x0 << 0);
+
+ break;
+ case MT8196_DAI_ADDA_CH56:
+ regmap_update_bits(afe->regmap, AFE_ADDA6_MTKAIFV4_RX_CFG0,
+ ADDA6_MTKAIFV4_RXIF_INPUT_MODE_MASK_SFT,
+ mtkaif_rate << ADDA6_MTKAIFV4_RXIF_INPUT_MODE_SFT);
+ /* AFE_ADDA6_MTKAIFV4_RX_CFG0 */
+ regmap_update_bits(afe->regmap, AFE_ADDA6_MTKAIFV4_RX_CFG0,
+ ADDA6_MTKAIFV4_RXIF_FOUR_CHANNEL_MASK_SFT,
+ 0x1 << ADDA6_MTKAIFV4_RXIF_FOUR_CHANNEL_SFT);
+ regmap_update_bits(afe->regmap, AFE_ADDA_MTKAIFV4_RX_CFG0,
+ MTKAIFV4_UL_CH5CH6_IN_EN_SEL_MASK_SFT,
+ 0x1 << MTKAIFV4_UL_CH5CH6_IN_EN_SEL_SFT);
+ regmap_update_bits(afe->regmap, AFE_ADDA6_MTKAIFV4_RX_CFG0,
+ ADDA6_MTKAIFV4_RXIF_EN_SEL_MASK_SFT,
+ 0x1 << ADDA6_MTKAIFV4_RXIF_EN_SEL_SFT);
+ break;
+ default:
+ break;
+ }
+
+ /* ap dmic */
+ switch (id) {
+ case MT8196_DAI_AP_DMIC:
+ case MT8196_DAI_AP_DMIC_CH34:
+ mtk_adda_ul_src_dmic(afe, id);
+ break;
+ case MT8196_DAI_AP_DMIC_MULTICH:
+ regmap_update_bits(afe->regmap, AFE_ADDA_ULSRC_PHASE_CON1,
+ DMIC_CLK_PHASE_SYNC_SET_MASK_SFT,
+ 0x1 << DMIC_CLK_PHASE_SYNC_SET_SFT);
+ mtk_adda_ul_src_dmic_phase_sync(afe);
+ mtk_adda_ul_src_dmic(afe, MT8196_DAI_AP_DMIC);
+ mtk_adda_ul_src_dmic(afe, MT8196_DAI_AP_DMIC_CH34);
+ mtk_adda_ul_src_dmic_phase_sync_clock(afe);
+ break;
+ default:
+ break;
+ }
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops mtk_dai_adda_ops = {
+ .hw_params = mtk_dai_adda_hw_params,
+};
+
+/* dai driver */
+#define MTK_ADDA_PLAYBACK_RATES (SNDRV_PCM_RATE_8000_48000 |\
+ SNDRV_PCM_RATE_96000 |\
+ SNDRV_PCM_RATE_192000)
+
+#define MTK_ADDA_CAPTURE_RATES (SNDRV_PCM_RATE_8000 |\
+ SNDRV_PCM_RATE_16000 |\
+ SNDRV_PCM_RATE_32000 |\
+ SNDRV_PCM_RATE_48000 |\
+ SNDRV_PCM_RATE_96000 |\
+ SNDRV_PCM_RATE_192000)
+
+#define MTK_ADDA_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\
+ SNDRV_PCM_FMTBIT_S24_LE |\
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+static struct snd_soc_dai_driver mtk_dai_adda_driver[] = {
+ {
+ .name = "ADDA",
+ .id = MT8196_DAI_ADDA,
+ .playback = {
+ .stream_name = "ADDA Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MTK_ADDA_PLAYBACK_RATES,
+ .formats = MTK_ADDA_FORMATS,
+ },
+ .capture = {
+ .stream_name = "ADDA Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MTK_ADDA_CAPTURE_RATES,
+ .formats = MTK_ADDA_FORMATS,
+ },
+ .ops = &mtk_dai_adda_ops,
+ },
+ {
+ .name = "ADDA_CH34",
+ .id = MT8196_DAI_ADDA_CH34,
+ .playback = {
+ .stream_name = "ADDA CH34 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MTK_ADDA_PLAYBACK_RATES,
+ .formats = MTK_ADDA_FORMATS,
+ },
+ .capture = {
+ .stream_name = "ADDA CH34 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MTK_ADDA_CAPTURE_RATES,
+ .formats = MTK_ADDA_FORMATS,
+ },
+ .ops = &mtk_dai_adda_ops,
+ },
+ {
+ .name = "ADDA_CH56",
+ .id = MT8196_DAI_ADDA_CH56,
+ .capture = {
+ .stream_name = "ADDA CH56 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MTK_ADDA_CAPTURE_RATES,
+ .formats = MTK_ADDA_FORMATS,
+ },
+ .ops = &mtk_dai_adda_ops,
+ },
+ {
+ .name = "AP_DMIC",
+ .id = MT8196_DAI_AP_DMIC,
+ .capture = {
+ .stream_name = "AP DMIC Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MTK_ADDA_CAPTURE_RATES,
+ .formats = MTK_ADDA_FORMATS,
+ },
+ .ops = &mtk_dai_adda_ops,
+ },
+ {
+ .name = "AP_DMIC_CH34",
+ .id = MT8196_DAI_AP_DMIC_CH34,
+ .capture = {
+ .stream_name = "AP DMIC CH34 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MTK_ADDA_CAPTURE_RATES,
+ .formats = MTK_ADDA_FORMATS,
+ },
+ .ops = &mtk_dai_adda_ops,
+ },
+ {
+ .name = "AP_DMIC_MULTICH",
+ .id = MT8196_DAI_AP_DMIC_MULTICH,
+ .capture = {
+ .stream_name = "AP DMIC MULTICH Capture",
+ .channels_min = 1,
+ .channels_max = 4,
+ .rates = MTK_ADDA_CAPTURE_RATES,
+ .formats = MTK_ADDA_FORMATS,
+ },
+ .ops = &mtk_dai_adda_ops,
+ },
+};
+
+static int init_adda_priv_data(struct mtk_base_afe *afe)
+{
+ struct mt8196_afe_private *afe_priv = afe->platform_priv;
+ struct mtk_afe_adda_priv *adda_priv;
+ static const int adda_dai_list[] = {
+ MT8196_DAI_ADDA,
+ MT8196_DAI_ADDA_CH34,
+ MT8196_DAI_ADDA_CH56,
+ MT8196_DAI_AP_DMIC_MULTICH
+ };
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(adda_dai_list); i++) {
+ adda_priv = devm_kzalloc(afe->dev,
+ sizeof(struct mtk_afe_adda_priv),
+ GFP_KERNEL);
+ if (!adda_priv)
+ return -ENOMEM;
+
+ afe_priv->dai_priv[adda_dai_list[i]] = adda_priv;
+ }
+
+ /* ap dmic priv share with adda */
+ afe_priv->dai_priv[MT8196_DAI_AP_DMIC] =
+ afe_priv->dai_priv[MT8196_DAI_ADDA];
+ afe_priv->dai_priv[MT8196_DAI_AP_DMIC_CH34] =
+ afe_priv->dai_priv[MT8196_DAI_ADDA_CH34];
+
+ return 0;
+}
+
+int mt8196_dai_adda_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 = mtk_dai_adda_driver;
+ dai->num_dai_drivers = ARRAY_SIZE(mtk_dai_adda_driver);
+
+ dai->controls = mtk_adda_controls;
+ dai->num_controls = ARRAY_SIZE(mtk_adda_controls);
+ dai->dapm_widgets = mtk_dai_adda_widgets;
+ dai->num_dapm_widgets = ARRAY_SIZE(mtk_dai_adda_widgets);
+ dai->dapm_routes = mtk_dai_adda_routes;
+ dai->num_dapm_routes = ARRAY_SIZE(mtk_dai_adda_routes);
+
+ return init_adda_priv_data(afe);
+}
+
--
2.45.2
^ permalink raw reply related [flat|nested] 24+ messages in thread
* [PATCH v6 06/10] ASoC: mediatek: mt8196: support TDM in platform driver
2025-07-08 11:15 [PATCH v6 00/10] ASoC: mediatek: Add support for MT8196 SoC Darren.Ye
` (2 preceding siblings ...)
2025-07-08 11:15 ` [PATCH v6 04/10] ASoC: mediatek: mt8196: support ADDA in platform driver Darren.Ye
@ 2025-07-08 11:15 ` Darren.Ye
2025-07-28 10:53 ` Chen-Yu Tsai
2025-07-08 11:15 ` [PATCH v6 07/10] ASoC: mediatek: mt8196: add " Darren.Ye
` (5 subsequent siblings)
9 siblings, 1 reply; 24+ messages in thread
From: Darren.Ye @ 2025-07-08 11:15 UTC (permalink / raw)
To: Liam Girdwood, Mark Brown, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Matthias Brugger, AngeloGioacchino Del Regno,
Jaroslav Kysela, Takashi Iwai, Linus Walleij, Bartosz Golaszewski
Cc: linux-sound, devicetree, linux-kernel, linux-arm-kernel,
linux-mediatek, linux-gpio, Darren Ye
From: Darren Ye <darren.ye@mediatek.com>
Add mt8196 TDM DAI driver support.
Signed-off-by: Darren Ye <darren.ye@mediatek.com>
---
sound/soc/mediatek/mt8196/mt8196-dai-tdm.c | 836 +++++++++++++++++++++
1 file changed, 836 insertions(+)
create mode 100644 sound/soc/mediatek/mt8196/mt8196-dai-tdm.c
diff --git a/sound/soc/mediatek/mt8196/mt8196-dai-tdm.c b/sound/soc/mediatek/mt8196/mt8196-dai-tdm.c
new file mode 100644
index 000000000000..dcbde41fb61c
--- /dev/null
+++ b/sound/soc/mediatek/mt8196/mt8196-dai-tdm.c
@@ -0,0 +1,836 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * MediaTek ALSA SoC Audio DAI TDM Control
+ *
+ * Copyright (c) 2024 MediaTek Inc.
+ * Author: Darren Ye <darren.ye@mediatek.com>
+ */
+
+#include <linux/regmap.h>
+#include <sound/pcm_params.h>
+#include "mt8196-afe-clk.h"
+#include "mt8196-afe-common.h"
+#include "mt8196-interconnection.h"
+
+struct mtk_afe_tdm_priv {
+ int bck_id;
+ int bck_rate;
+
+ int mclk_id;
+ int mclk_multiple; /* according to sample rate */
+ int mclk_rate;
+ int mclk_apll;
+};
+
+enum {
+ TDM_WLEN_16_BIT = 1,
+ TDM_WLEN_32_BIT = 2,
+};
+
+enum {
+ TDM_CHANNEL_BCK_16 = 0,
+ TDM_CHANNEL_BCK_24 = 1,
+ TDM_CHANNEL_BCK_32 = 2,
+};
+
+enum {
+ TDM_CHANNEL_NUM_2 = 0,
+ TDM_CHANNEL_NUM_4 = 1,
+ TDM_CHANNEL_NUM_8 = 2,
+};
+
+enum {
+ TDM_CH_START_O30_O31 = 0,
+ TDM_CH_START_O32_O33,
+ TDM_CH_START_O34_O35,
+ TDM_CH_START_O36_O37,
+ TDM_CH_ZERO,
+};
+
+enum {
+ DPTX_CHANNEL_2,
+ DPTX_CHANNEL_8,
+};
+
+enum {
+ DPTX_WLEN_24_BIT,
+ DPTX_WLEN_16_BIT,
+};
+
+enum {
+ DPTX_CH_EN_MASK_2CH = 0x3,
+ DPTX_CH_EN_MASK_4CH = 0xf,
+ DPTX_CH_EN_MASK_6CH = 0x3f,
+ DPTX_CH_EN_MASK_8CH = 0xff,
+};
+
+static unsigned int get_tdm_wlen(snd_pcm_format_t format)
+{
+ return snd_pcm_format_physical_width(format) <= 16 ?
+ TDM_WLEN_16_BIT : TDM_WLEN_32_BIT;
+}
+
+static unsigned int get_tdm_channel_bck(snd_pcm_format_t format)
+{
+ return snd_pcm_format_physical_width(format) <= 16 ?
+ TDM_CHANNEL_BCK_16 : TDM_CHANNEL_BCK_32;
+}
+
+static unsigned int get_tdm_lrck_width(snd_pcm_format_t format)
+{
+ return snd_pcm_format_physical_width(format) - 1;
+}
+
+static unsigned int get_tdm_ch(unsigned int ch)
+{
+ switch (ch) {
+ case 1:
+ case 2:
+ return TDM_CHANNEL_NUM_2;
+ case 3:
+ case 4:
+ return TDM_CHANNEL_NUM_4;
+ case 5:
+ case 6:
+ case 7:
+ case 8:
+ default:
+ return TDM_CHANNEL_NUM_8;
+ }
+}
+
+static unsigned int get_dptx_ch_enable_mask(unsigned int ch)
+{
+ switch (ch) {
+ case 1:
+ case 2:
+ return DPTX_CH_EN_MASK_2CH;
+ case 3:
+ case 4:
+ return DPTX_CH_EN_MASK_4CH;
+ case 5:
+ case 6:
+ return DPTX_CH_EN_MASK_6CH;
+ case 7:
+ case 8:
+ return DPTX_CH_EN_MASK_8CH;
+ default:
+ pr_info("invalid channel num, default use 2ch\n");
+ return DPTX_CH_EN_MASK_2CH;
+ }
+}
+
+static unsigned int get_dptx_ch(unsigned int ch)
+{
+ if (ch == 2)
+ return DPTX_CHANNEL_2;
+ else
+ return DPTX_CHANNEL_8;
+}
+
+static unsigned int get_dptx_wlen(snd_pcm_format_t format)
+{
+ return snd_pcm_format_physical_width(format) <= 16 ?
+ DPTX_WLEN_16_BIT : DPTX_WLEN_24_BIT;
+}
+
+/* interconnection */
+enum {
+ HDMI_CONN_CH0 = 0,
+ HDMI_CONN_CH1,
+ HDMI_CONN_CH2,
+ HDMI_CONN_CH3,
+ HDMI_CONN_CH4,
+ HDMI_CONN_CH5,
+ HDMI_CONN_CH6,
+ HDMI_CONN_CH7,
+};
+
+static const char *const hdmi_conn_mux_map[] = {
+ "CH0", "CH1", "CH2", "CH3",
+ "CH4", "CH5", "CH6", "CH7",
+};
+
+static int hdmi_conn_mux_map_value[] = {
+ HDMI_CONN_CH0,
+ HDMI_CONN_CH1,
+ HDMI_CONN_CH2,
+ HDMI_CONN_CH3,
+ HDMI_CONN_CH4,
+ HDMI_CONN_CH5,
+ HDMI_CONN_CH6,
+ HDMI_CONN_CH7,
+};
+
+static SOC_VALUE_ENUM_SINGLE_DECL(hdmi_ch0_mux_map_enum,
+ AFE_HDMI_CONN0,
+ HDMI_O_0_SFT,
+ HDMI_O_0_MASK,
+ hdmi_conn_mux_map,
+ hdmi_conn_mux_map_value);
+
+static const struct snd_kcontrol_new hdmi_ch0_mux_control =
+ SOC_DAPM_ENUM("HDMI_CH0_MUX", hdmi_ch0_mux_map_enum);
+
+static SOC_VALUE_ENUM_SINGLE_DECL(hdmi_ch1_mux_map_enum,
+ AFE_HDMI_CONN0,
+ HDMI_O_1_SFT,
+ HDMI_O_1_MASK,
+ hdmi_conn_mux_map,
+ hdmi_conn_mux_map_value);
+
+static const struct snd_kcontrol_new hdmi_ch1_mux_control =
+ SOC_DAPM_ENUM("HDMI_CH1_MUX", hdmi_ch1_mux_map_enum);
+
+static SOC_VALUE_ENUM_SINGLE_DECL(hdmi_ch2_mux_map_enum,
+ AFE_HDMI_CONN0,
+ HDMI_O_2_SFT,
+ HDMI_O_2_MASK,
+ hdmi_conn_mux_map,
+ hdmi_conn_mux_map_value);
+
+static const struct snd_kcontrol_new hdmi_ch2_mux_control =
+ SOC_DAPM_ENUM("HDMI_CH2_MUX", hdmi_ch2_mux_map_enum);
+
+static SOC_VALUE_ENUM_SINGLE_DECL(hdmi_ch3_mux_map_enum,
+ AFE_HDMI_CONN0,
+ HDMI_O_3_SFT,
+ HDMI_O_3_MASK,
+ hdmi_conn_mux_map,
+ hdmi_conn_mux_map_value);
+
+static const struct snd_kcontrol_new hdmi_ch3_mux_control =
+ SOC_DAPM_ENUM("HDMI_CH3_MUX", hdmi_ch3_mux_map_enum);
+
+static SOC_VALUE_ENUM_SINGLE_DECL(hdmi_ch4_mux_map_enum,
+ AFE_HDMI_CONN0,
+ HDMI_O_4_SFT,
+ HDMI_O_4_MASK,
+ hdmi_conn_mux_map,
+ hdmi_conn_mux_map_value);
+
+static const struct snd_kcontrol_new hdmi_ch4_mux_control =
+ SOC_DAPM_ENUM("HDMI_CH4_MUX", hdmi_ch4_mux_map_enum);
+
+static SOC_VALUE_ENUM_SINGLE_DECL(hdmi_ch5_mux_map_enum,
+ AFE_HDMI_CONN0,
+ HDMI_O_5_SFT,
+ HDMI_O_5_MASK,
+ hdmi_conn_mux_map,
+ hdmi_conn_mux_map_value);
+
+static const struct snd_kcontrol_new hdmi_ch5_mux_control =
+ SOC_DAPM_ENUM("HDMI_CH5_MUX", hdmi_ch5_mux_map_enum);
+
+static SOC_VALUE_ENUM_SINGLE_DECL(hdmi_ch6_mux_map_enum,
+ AFE_HDMI_CONN0,
+ HDMI_O_6_SFT,
+ HDMI_O_6_MASK,
+ hdmi_conn_mux_map,
+ hdmi_conn_mux_map_value);
+
+static const struct snd_kcontrol_new hdmi_ch6_mux_control =
+ SOC_DAPM_ENUM("HDMI_CH6_MUX", hdmi_ch6_mux_map_enum);
+
+static SOC_VALUE_ENUM_SINGLE_DECL(hdmi_ch7_mux_map_enum,
+ AFE_HDMI_CONN0,
+ HDMI_O_7_SFT,
+ HDMI_O_7_MASK,
+ hdmi_conn_mux_map,
+ hdmi_conn_mux_map_value);
+
+static const struct snd_kcontrol_new hdmi_ch7_mux_control =
+ SOC_DAPM_ENUM("HDMI_CH7_MUX", hdmi_ch7_mux_map_enum);
+
+static const char *const tdm_out_mux_map[] = {
+ "Disconnect", "Connect",
+};
+
+static int tdm_out_mux_map_value[] = {
+ 0, 1,
+};
+
+static SOC_VALUE_ENUM_SINGLE_AUTODISABLE_DECL(hdmi_out_mux_map_enum,
+ SND_SOC_NOPM,
+ 0,
+ 1,
+ tdm_out_mux_map,
+ tdm_out_mux_map_value);
+static const struct snd_kcontrol_new hdmi_out_mux_control =
+ SOC_DAPM_ENUM("HDMI_OUT_MUX", hdmi_out_mux_map_enum);
+
+static SOC_VALUE_ENUM_SINGLE_AUTODISABLE_DECL(dptx_out_mux_map_enum,
+ SND_SOC_NOPM,
+ 0,
+ 1,
+ tdm_out_mux_map,
+ tdm_out_mux_map_value);
+static const struct snd_kcontrol_new dptx_out_mux_control =
+ SOC_DAPM_ENUM("DPTX_OUT_MUX", dptx_out_mux_map_enum);
+
+static SOC_VALUE_ENUM_SINGLE_AUTODISABLE_DECL(dptx_virtual_out_mux_map_enum,
+ SND_SOC_NOPM,
+ 0,
+ 1,
+ tdm_out_mux_map,
+ tdm_out_mux_map_value);
+
+static const struct snd_kcontrol_new dptx_virtual_out_mux_control =
+ SOC_DAPM_ENUM("DPTX_VIRTUAL_OUT_MUX", dptx_virtual_out_mux_map_enum);
+
+enum {
+ SUPPLY_SEQ_APLL,
+ SUPPLY_SEQ_TDM_MCK_EN,
+ SUPPLY_SEQ_TDM_BCK_EN,
+ SUPPLY_SEQ_TDM_DPTX_MCK_EN,
+ SUPPLY_SEQ_TDM_DPTX_BCK_EN,
+ SUPPLY_SEQ_TDM_CG_EN,
+};
+
+static int get_tdm_id_by_name(const char *name)
+{
+ if (strstr(name, "DPTX"))
+ return MT8196_DAI_TDM_DPTX;
+ else
+ return MT8196_DAI_TDM;
+}
+
+static int mtk_tdm_bck_en_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
+ struct mt8196_afe_private *afe_priv = afe->platform_priv;
+ int dai_id = get_tdm_id_by_name(w->name);
+ struct mtk_afe_tdm_priv *tdm_priv = afe_priv->dai_priv[dai_id];
+
+ dev_dbg(cmpnt->dev, "name %s, event 0x%x, dai_id %d\n",
+ w->name, event, dai_id);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ mt8196_mck_enable(afe, tdm_priv->bck_id, tdm_priv->bck_rate);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ mt8196_mck_disable(afe, tdm_priv->bck_id);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int mtk_tdm_mck_en_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
+ struct mt8196_afe_private *afe_priv = afe->platform_priv;
+ int dai_id = get_tdm_id_by_name(w->name);
+ struct mtk_afe_tdm_priv *tdm_priv = afe_priv->dai_priv[dai_id];
+
+ dev_dbg(cmpnt->dev, "name %s, event 0x%x, dai_id %d\n",
+ w->name, event, dai_id);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ mt8196_mck_enable(afe, tdm_priv->mclk_id, tdm_priv->mclk_rate);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ tdm_priv->mclk_rate = 0;
+ mt8196_mck_disable(afe, tdm_priv->mclk_id);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget mtk_dai_tdm_widgets[] = {
+ SND_SOC_DAPM_MUX("HDMI_CH0_MUX", SND_SOC_NOPM, 0, 0,
+ &hdmi_ch0_mux_control),
+ SND_SOC_DAPM_MUX("HDMI_CH1_MUX", SND_SOC_NOPM, 0, 0,
+ &hdmi_ch1_mux_control),
+ SND_SOC_DAPM_MUX("HDMI_CH2_MUX", SND_SOC_NOPM, 0, 0,
+ &hdmi_ch2_mux_control),
+ SND_SOC_DAPM_MUX("HDMI_CH3_MUX", SND_SOC_NOPM, 0, 0,
+ &hdmi_ch3_mux_control),
+ SND_SOC_DAPM_MUX("HDMI_CH4_MUX", SND_SOC_NOPM, 0, 0,
+ &hdmi_ch4_mux_control),
+ SND_SOC_DAPM_MUX("HDMI_CH5_MUX", SND_SOC_NOPM, 0, 0,
+ &hdmi_ch5_mux_control),
+ SND_SOC_DAPM_MUX("HDMI_CH6_MUX", SND_SOC_NOPM, 0, 0,
+ &hdmi_ch6_mux_control),
+ SND_SOC_DAPM_MUX("HDMI_CH7_MUX", SND_SOC_NOPM, 0, 0,
+ &hdmi_ch7_mux_control),
+ SND_SOC_DAPM_MUX("HDMI_OUT_MUX", SND_SOC_NOPM, 0, 0,
+ &hdmi_out_mux_control),
+ SND_SOC_DAPM_MUX("DPTX_OUT_MUX", SND_SOC_NOPM, 0, 0,
+ &dptx_out_mux_control),
+
+ SND_SOC_DAPM_SUPPLY_S("TDM_BCK", SUPPLY_SEQ_TDM_BCK_EN,
+ SND_SOC_NOPM, 0, 0,
+ mtk_tdm_bck_en_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_SUPPLY_S("TDM_MCK", SUPPLY_SEQ_TDM_MCK_EN,
+ SND_SOC_NOPM, 0, 0,
+ mtk_tdm_mck_en_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_SUPPLY_S("TDM_DPTX_BCK", SUPPLY_SEQ_TDM_DPTX_BCK_EN,
+ SND_SOC_NOPM, 0, 0,
+ mtk_tdm_bck_en_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_SUPPLY_S("TDM_DPTX_MCK", SUPPLY_SEQ_TDM_DPTX_MCK_EN,
+ SND_SOC_NOPM, 0, 0,
+ mtk_tdm_mck_en_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ /* cg */
+ SND_SOC_DAPM_SUPPLY_S("TDM_CG", SUPPLY_SEQ_TDM_CG_EN,
+ AUDIO_TOP_CON2, PDN_TDM_OUT_SFT, 1,
+ NULL, 0),
+
+ SND_SOC_DAPM_MUX("DPTX_VIRTUAL_OUT_MUX",
+ SND_SOC_NOPM, 0, 0, &dptx_virtual_out_mux_control),
+ SND_SOC_DAPM_OUTPUT("DPTX_VIRTUAL_OUT"),
+};
+
+static int mtk_afe_tdm_apll_connect(struct snd_soc_dapm_widget *source,
+ struct snd_soc_dapm_widget *sink)
+{
+ struct snd_soc_dapm_widget *w = sink;
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
+ struct mt8196_afe_private *afe_priv = afe->platform_priv;
+ int dai_id = get_tdm_id_by_name(w->name);
+ struct mtk_afe_tdm_priv *tdm_priv = afe_priv->dai_priv[dai_id];
+ int cur_apll;
+
+ /* which apll */
+ cur_apll = mt8196_get_apll_by_name(afe, source->name);
+
+ return (tdm_priv->mclk_apll == cur_apll) ? 1 : 0;
+}
+
+static const struct snd_soc_dapm_route mtk_dai_tdm_routes[] = {
+ {"HDMI_CH0_MUX", "CH0", "HDMI"},
+ {"HDMI_CH0_MUX", "CH1", "HDMI"},
+ {"HDMI_CH0_MUX", "CH2", "HDMI"},
+ {"HDMI_CH0_MUX", "CH3", "HDMI"},
+ {"HDMI_CH0_MUX", "CH4", "HDMI"},
+ {"HDMI_CH0_MUX", "CH5", "HDMI"},
+ {"HDMI_CH0_MUX", "CH6", "HDMI"},
+ {"HDMI_CH0_MUX", "CH7", "HDMI"},
+
+ {"HDMI_CH1_MUX", "CH0", "HDMI"},
+ {"HDMI_CH1_MUX", "CH1", "HDMI"},
+ {"HDMI_CH1_MUX", "CH2", "HDMI"},
+ {"HDMI_CH1_MUX", "CH3", "HDMI"},
+ {"HDMI_CH1_MUX", "CH4", "HDMI"},
+ {"HDMI_CH1_MUX", "CH5", "HDMI"},
+ {"HDMI_CH1_MUX", "CH6", "HDMI"},
+ {"HDMI_CH1_MUX", "CH7", "HDMI"},
+
+ {"HDMI_CH2_MUX", "CH0", "HDMI"},
+ {"HDMI_CH2_MUX", "CH1", "HDMI"},
+ {"HDMI_CH2_MUX", "CH2", "HDMI"},
+ {"HDMI_CH2_MUX", "CH3", "HDMI"},
+ {"HDMI_CH2_MUX", "CH4", "HDMI"},
+ {"HDMI_CH2_MUX", "CH5", "HDMI"},
+ {"HDMI_CH2_MUX", "CH6", "HDMI"},
+ {"HDMI_CH2_MUX", "CH7", "HDMI"},
+
+ {"HDMI_CH3_MUX", "CH0", "HDMI"},
+ {"HDMI_CH3_MUX", "CH1", "HDMI"},
+ {"HDMI_CH3_MUX", "CH2", "HDMI"},
+ {"HDMI_CH3_MUX", "CH3", "HDMI"},
+ {"HDMI_CH3_MUX", "CH4", "HDMI"},
+ {"HDMI_CH3_MUX", "CH5", "HDMI"},
+ {"HDMI_CH3_MUX", "CH6", "HDMI"},
+ {"HDMI_CH3_MUX", "CH7", "HDMI"},
+
+ {"HDMI_CH4_MUX", "CH0", "HDMI"},
+ {"HDMI_CH4_MUX", "CH1", "HDMI"},
+ {"HDMI_CH4_MUX", "CH2", "HDMI"},
+ {"HDMI_CH4_MUX", "CH3", "HDMI"},
+ {"HDMI_CH4_MUX", "CH4", "HDMI"},
+ {"HDMI_CH4_MUX", "CH5", "HDMI"},
+ {"HDMI_CH4_MUX", "CH6", "HDMI"},
+ {"HDMI_CH4_MUX", "CH7", "HDMI"},
+
+ {"HDMI_CH5_MUX", "CH0", "HDMI"},
+ {"HDMI_CH5_MUX", "CH1", "HDMI"},
+ {"HDMI_CH5_MUX", "CH2", "HDMI"},
+ {"HDMI_CH5_MUX", "CH3", "HDMI"},
+ {"HDMI_CH5_MUX", "CH4", "HDMI"},
+ {"HDMI_CH5_MUX", "CH5", "HDMI"},
+ {"HDMI_CH5_MUX", "CH6", "HDMI"},
+ {"HDMI_CH5_MUX", "CH7", "HDMI"},
+
+ {"HDMI_CH6_MUX", "CH0", "HDMI"},
+ {"HDMI_CH6_MUX", "CH1", "HDMI"},
+ {"HDMI_CH6_MUX", "CH2", "HDMI"},
+ {"HDMI_CH6_MUX", "CH3", "HDMI"},
+ {"HDMI_CH6_MUX", "CH4", "HDMI"},
+ {"HDMI_CH6_MUX", "CH5", "HDMI"},
+ {"HDMI_CH6_MUX", "CH6", "HDMI"},
+ {"HDMI_CH6_MUX", "CH7", "HDMI"},
+
+ {"HDMI_CH7_MUX", "CH0", "HDMI"},
+ {"HDMI_CH7_MUX", "CH1", "HDMI"},
+ {"HDMI_CH7_MUX", "CH2", "HDMI"},
+ {"HDMI_CH7_MUX", "CH3", "HDMI"},
+ {"HDMI_CH7_MUX", "CH4", "HDMI"},
+ {"HDMI_CH7_MUX", "CH5", "HDMI"},
+ {"HDMI_CH7_MUX", "CH6", "HDMI"},
+ {"HDMI_CH7_MUX", "CH7", "HDMI"},
+
+ {"HDMI_OUT_MUX", "Connect", "HDMI_CH0_MUX"},
+ {"HDMI_OUT_MUX", "Connect", "HDMI_CH1_MUX"},
+ {"HDMI_OUT_MUX", "Connect", "HDMI_CH2_MUX"},
+ {"HDMI_OUT_MUX", "Connect", "HDMI_CH3_MUX"},
+ {"HDMI_OUT_MUX", "Connect", "HDMI_CH4_MUX"},
+ {"HDMI_OUT_MUX", "Connect", "HDMI_CH5_MUX"},
+ {"HDMI_OUT_MUX", "Connect", "HDMI_CH6_MUX"},
+ {"HDMI_OUT_MUX", "Connect", "HDMI_CH7_MUX"},
+
+ {"DPTX_OUT_MUX", "Connect", "HDMI_CH0_MUX"},
+ {"DPTX_OUT_MUX", "Connect", "HDMI_CH1_MUX"},
+ {"DPTX_OUT_MUX", "Connect", "HDMI_CH2_MUX"},
+ {"DPTX_OUT_MUX", "Connect", "HDMI_CH3_MUX"},
+ {"DPTX_OUT_MUX", "Connect", "HDMI_CH4_MUX"},
+ {"DPTX_OUT_MUX", "Connect", "HDMI_CH5_MUX"},
+ {"DPTX_OUT_MUX", "Connect", "HDMI_CH6_MUX"},
+ {"DPTX_OUT_MUX", "Connect", "HDMI_CH7_MUX"},
+
+ {"TDM", NULL, "HDMI_OUT_MUX"},
+ {"TDM", NULL, "TDM_BCK"},
+ {"TDM", NULL, "TDM_CG"},
+
+ {"TDM_DPTX", NULL, "DPTX_OUT_MUX"},
+ {"TDM_DPTX", NULL, "TDM_DPTX_BCK"},
+ {"TDM_DPTX", NULL, "TDM_CG"},
+
+ {"TDM_BCK", NULL, "TDM_MCK"},
+ {"TDM_DPTX_BCK", NULL, "TDM_DPTX_MCK"},
+ {"TDM_MCK", NULL, APLL1_W_NAME, mtk_afe_tdm_apll_connect},
+ {"TDM_MCK", NULL, APLL2_W_NAME, mtk_afe_tdm_apll_connect},
+ {"TDM_DPTX_MCK", NULL, APLL1_W_NAME, mtk_afe_tdm_apll_connect},
+ {"TDM_DPTX_MCK", NULL, APLL2_W_NAME, mtk_afe_tdm_apll_connect},
+
+ {"DPTX_VIRTUAL_OUT_MUX", "Connect", "TDM_DPTX"},
+ {"DPTX_VIRTUAL_OUT", NULL, "DPTX_VIRTUAL_OUT_MUX"},
+};
+
+/* dai ops */
+static int mtk_dai_tdm_cal_mclk(struct mtk_base_afe *afe,
+ struct mtk_afe_tdm_priv *tdm_priv,
+ int freq)
+{
+ int apll;
+ int apll_rate;
+
+ apll = mt8196_get_apll_by_rate(afe, freq);
+ apll_rate = mt8196_get_apll_rate(afe, apll);
+
+ if (freq > apll_rate)
+ return -EINVAL;
+
+ if (apll_rate % freq != 0)
+ return -EINVAL;
+
+ tdm_priv->mclk_rate = freq;
+ tdm_priv->mclk_apll = apll;
+
+ return 0;
+}
+
+static int mtk_dai_tdm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+ struct mt8196_afe_private *afe_priv = afe->platform_priv;
+ int tdm_id = dai->id;
+ struct mtk_afe_tdm_priv *tdm_priv;
+ unsigned int rate = params_rate(params);
+ unsigned int channels = params_channels(params);
+ snd_pcm_format_t format = params_format(params);
+ unsigned int tdm_con = 0;
+
+ if (tdm_id >= MT8196_DAI_NUM || tdm_id < 0)
+ return -EINVAL;
+
+ tdm_priv = afe_priv->dai_priv[tdm_id];
+
+ if (!tdm_priv)
+ return -EINVAL;
+
+ /* calculate mclk_rate, if not set explicitly */
+ if (!tdm_priv->mclk_rate) {
+ tdm_priv->mclk_rate = rate * tdm_priv->mclk_multiple;
+ mtk_dai_tdm_cal_mclk(afe,
+ tdm_priv,
+ tdm_priv->mclk_rate);
+ }
+
+ /* calculate bck */
+ tdm_priv->bck_rate = rate *
+ channels *
+ snd_pcm_format_physical_width(format);
+
+ if (tdm_priv->bck_rate > tdm_priv->mclk_rate)
+ return -EINVAL;
+
+ if (tdm_priv->mclk_rate % tdm_priv->bck_rate != 0)
+ return -EINVAL;
+
+ dev_info(afe->dev, "id %d, rate %d, channels %d, format %d, mclk_rate %d, bck_rate %d\n",
+ tdm_id, rate, channels, format,
+ tdm_priv->mclk_rate, tdm_priv->bck_rate);
+
+ /* set tdm */
+ tdm_con = 0 << BCK_INVERSE_SFT;
+ tdm_con |= 0 << LRCK_INVERSE_SFT;
+ tdm_con |= 0 << DELAY_DATA_SFT;
+ tdm_con |= 1 << LEFT_ALIGN_SFT;
+ tdm_con |= get_tdm_wlen(format) << WLEN_SFT;
+ tdm_con |= get_tdm_ch(channels) << CHANNEL_NUM_SFT;
+ tdm_con |= get_tdm_channel_bck(format) << CHANNEL_BCK_CYCLES_SFT;
+ tdm_con |= get_tdm_lrck_width(format) << LRCK_TDM_WIDTH_SFT;
+ regmap_write(afe->regmap, AFE_TDM_CON1, tdm_con);
+
+ /* set dptx */
+ if (tdm_id == MT8196_DAI_TDM_DPTX) {
+ regmap_update_bits(afe->regmap, AFE_DPTX_CON,
+ DPTX_CHANNEL_ENABLE_MASK_SFT,
+ get_dptx_ch_enable_mask(channels) <<
+ DPTX_CHANNEL_ENABLE_SFT);
+ regmap_update_bits(afe->regmap, AFE_DPTX_CON,
+ DPTX_CHANNEL_NUMBER_MASK_SFT,
+ get_dptx_ch(channels) <<
+ DPTX_CHANNEL_NUMBER_SFT);
+ regmap_update_bits(afe->regmap, AFE_DPTX_CON,
+ DPTX_16BIT_MASK_SFT,
+ get_dptx_wlen(format) << DPTX_16BIT_SFT);
+ }
+ switch (channels) {
+ case 1:
+ case 2:
+ tdm_con = TDM_CH_START_O30_O31 << ST_CH_PAIR_SOUT0_SFT;
+ tdm_con |= TDM_CH_ZERO << ST_CH_PAIR_SOUT1_SFT;
+ tdm_con |= TDM_CH_ZERO << ST_CH_PAIR_SOUT2_SFT;
+ tdm_con |= TDM_CH_ZERO << ST_CH_PAIR_SOUT3_SFT;
+ break;
+ case 3:
+ case 4:
+ tdm_con = TDM_CH_START_O30_O31 << ST_CH_PAIR_SOUT0_SFT;
+ tdm_con |= TDM_CH_START_O32_O33 << ST_CH_PAIR_SOUT1_SFT;
+ tdm_con |= TDM_CH_ZERO << ST_CH_PAIR_SOUT2_SFT;
+ tdm_con |= TDM_CH_ZERO << ST_CH_PAIR_SOUT3_SFT;
+ break;
+ case 5:
+ case 6:
+ tdm_con = TDM_CH_START_O30_O31 << ST_CH_PAIR_SOUT0_SFT;
+ tdm_con |= TDM_CH_START_O32_O33 << ST_CH_PAIR_SOUT1_SFT;
+ tdm_con |= TDM_CH_START_O34_O35 << ST_CH_PAIR_SOUT2_SFT;
+ tdm_con |= TDM_CH_ZERO << ST_CH_PAIR_SOUT3_SFT;
+ break;
+ case 7:
+ case 8:
+ tdm_con = TDM_CH_START_O30_O31 << ST_CH_PAIR_SOUT0_SFT;
+ tdm_con |= TDM_CH_START_O32_O33 << ST_CH_PAIR_SOUT1_SFT;
+ tdm_con |= TDM_CH_START_O34_O35 << ST_CH_PAIR_SOUT2_SFT;
+ tdm_con |= TDM_CH_START_O36_O37 << ST_CH_PAIR_SOUT3_SFT;
+ break;
+ default:
+ tdm_con = 0;
+ }
+ regmap_write(afe->regmap, AFE_TDM_CON2, tdm_con);
+ regmap_update_bits(afe->regmap, AFE_HDMI_OUT_CON0,
+ HDMI_CH_NUM_MASK_SFT,
+ channels << HDMI_CH_NUM_SFT);
+
+ return 0;
+}
+
+static int mtk_dai_tdm_trigger(struct snd_pcm_substream *substream,
+ int cmd,
+ struct snd_soc_dai *dai)
+{
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+ int tdm_id = dai->id;
+
+ dev_dbg(afe->dev, "cmd %d, tdm_id %d\n", cmd, tdm_id);
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ /* enable Out control */
+ regmap_update_bits(afe->regmap, AFE_HDMI_OUT_CON0,
+ HDMI_OUT_ON_MASK_SFT,
+ 0x1 << HDMI_OUT_ON_SFT);
+
+ /* enable dptx */
+ if (tdm_id == MT8196_DAI_TDM_DPTX) {
+ regmap_update_bits(afe->regmap, AFE_DPTX_CON,
+ DPTX_ON_MASK_SFT, 0x1 <<
+ DPTX_ON_SFT);
+ }
+
+ /* enable tdm */
+ regmap_update_bits(afe->regmap, AFE_TDM_CON1,
+ TDM_EN_MASK_SFT, 0x1 << TDM_EN_SFT);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ /* disable tdm */
+ regmap_update_bits(afe->regmap, AFE_TDM_CON1,
+ TDM_EN_MASK_SFT, 0);
+
+ /* disable dptx */
+ if (tdm_id == MT8196_DAI_TDM_DPTX) {
+ regmap_update_bits(afe->regmap, AFE_DPTX_CON,
+ DPTX_ON_MASK_SFT, 0);
+ }
+
+ /* disable Out control */
+ regmap_update_bits(afe->regmap, AFE_HDMI_OUT_CON0,
+ HDMI_OUT_ON_MASK_SFT, 0);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int mtk_dai_tdm_set_sysclk(struct snd_soc_dai *dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct mtk_base_afe *afe = dev_get_drvdata(dai->dev);
+ struct mt8196_afe_private *afe_priv = afe->platform_priv;
+ struct mtk_afe_tdm_priv *tdm_priv;
+
+ if (dai->id >= MT8196_DAI_NUM || dai->id < 0)
+ return -EINVAL;
+
+ tdm_priv = afe_priv->dai_priv[dai->id];
+
+ if (!tdm_priv)
+ return -EINVAL;
+
+ if (dir != SND_SOC_CLOCK_OUT)
+ return -EINVAL;
+
+ dev_dbg(afe->dev, "freq %d\n", freq);
+
+ return mtk_dai_tdm_cal_mclk(afe, tdm_priv, freq);
+}
+
+static const struct snd_soc_dai_ops mtk_dai_tdm_ops = {
+ .hw_params = mtk_dai_tdm_hw_params,
+ .trigger = mtk_dai_tdm_trigger,
+ .set_sysclk = mtk_dai_tdm_set_sysclk,
+};
+
+/* dai driver */
+#define MTK_TDM_RATES (SNDRV_PCM_RATE_8000_48000 |\
+ SNDRV_PCM_RATE_88200 |\
+ SNDRV_PCM_RATE_96000 |\
+ SNDRV_PCM_RATE_176400 |\
+ SNDRV_PCM_RATE_192000)
+
+#define MTK_TDM_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\
+ SNDRV_PCM_FMTBIT_S24_LE |\
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+static struct snd_soc_dai_driver mtk_dai_tdm_driver[] = {
+ {
+ .name = "TDM",
+ .id = MT8196_DAI_TDM,
+ .playback = {
+ .stream_name = "TDM",
+ .channels_min = 2,
+ .channels_max = 8,
+ .rates = MTK_TDM_RATES,
+ .formats = MTK_TDM_FORMATS,
+ },
+ .ops = &mtk_dai_tdm_ops,
+ },
+ {
+ .name = "TDM_DPTX",
+ .id = MT8196_DAI_TDM_DPTX,
+ .playback = {
+ .stream_name = "TDM_DPTX",
+ .channels_min = 2,
+ .channels_max = 8,
+ .rates = MTK_TDM_RATES,
+ .formats = MTK_TDM_FORMATS,
+ },
+ .ops = &mtk_dai_tdm_ops,
+ },
+};
+
+static struct mtk_afe_tdm_priv *init_tdm_priv_data(struct mtk_base_afe *afe,
+ int id)
+{
+ struct mtk_afe_tdm_priv *tdm_priv;
+
+ tdm_priv = devm_kzalloc(afe->dev, sizeof(struct mtk_afe_tdm_priv),
+ GFP_KERNEL);
+ if (!tdm_priv)
+ return NULL;
+
+ if (id == MT8196_DAI_TDM_DPTX)
+ tdm_priv->mclk_multiple = 256;
+ else
+ tdm_priv->mclk_multiple = 128;
+
+ tdm_priv->bck_id = MT8196_TDMOUT_BCK;
+ tdm_priv->mclk_id = MT8196_TDMOUT_MCK;
+
+ return tdm_priv;
+}
+
+int mt8196_dai_tdm_register(struct mtk_base_afe *afe)
+{
+ struct mt8196_afe_private *afe_priv = afe->platform_priv;
+ struct mtk_afe_tdm_priv *tdm_priv, *tdm_dptx_priv;
+ 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 = mtk_dai_tdm_driver;
+ dai->num_dai_drivers = ARRAY_SIZE(mtk_dai_tdm_driver);
+
+ dai->dapm_widgets = mtk_dai_tdm_widgets;
+ dai->num_dapm_widgets = ARRAY_SIZE(mtk_dai_tdm_widgets);
+ dai->dapm_routes = mtk_dai_tdm_routes;
+ dai->num_dapm_routes = ARRAY_SIZE(mtk_dai_tdm_routes);
+
+ tdm_priv = init_tdm_priv_data(afe, MT8196_DAI_TDM);
+ if (!tdm_priv)
+ return -ENOMEM;
+
+ tdm_dptx_priv = init_tdm_priv_data(afe, MT8196_DAI_TDM_DPTX);
+ if (!tdm_dptx_priv)
+ return -ENOMEM;
+
+ afe_priv->dai_priv[MT8196_DAI_TDM] = tdm_priv;
+ afe_priv->dai_priv[MT8196_DAI_TDM_DPTX] = tdm_dptx_priv;
+
+ return 0;
+}
+
--
2.45.2
^ permalink raw reply related [flat|nested] 24+ messages in thread
* [PATCH v6 07/10] ASoC: mediatek: mt8196: add platform driver
2025-07-08 11:15 [PATCH v6 00/10] ASoC: mediatek: Add support for MT8196 SoC Darren.Ye
` (3 preceding siblings ...)
2025-07-08 11:15 ` [PATCH v6 06/10] ASoC: mediatek: mt8196: support TDM " Darren.Ye
@ 2025-07-08 11:15 ` Darren.Ye
2025-08-05 10:40 ` Chen-Yu Tsai
2025-07-08 11:16 ` [PATCH v6 08/10] ASoC: dt-bindings: mediatek,mt8196-afe: add audio AFE Darren.Ye
` (4 subsequent siblings)
9 siblings, 1 reply; 24+ messages in thread
From: Darren.Ye @ 2025-07-08 11:15 UTC (permalink / raw)
To: Liam Girdwood, Mark Brown, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Matthias Brugger, AngeloGioacchino Del Regno,
Jaroslav Kysela, Takashi Iwai, Linus Walleij, Bartosz Golaszewski
Cc: linux-sound, devicetree, linux-kernel, linux-arm-kernel,
linux-mediatek, linux-gpio, Darren Ye
From: Darren Ye <darren.ye@mediatek.com>
Add mt8196 platform driver.
Signed-off-by: Darren Ye <darren.ye@mediatek.com>
---
sound/soc/mediatek/Kconfig | 10 +
sound/soc/mediatek/Makefile | 1 +
sound/soc/mediatek/mt8196/Makefile | 15 +
sound/soc/mediatek/mt8196/mt8196-afe-pcm.c | 2632 ++++++++++++++++++++
4 files changed, 2658 insertions(+)
create mode 100644 sound/soc/mediatek/mt8196/Makefile
create mode 100644 sound/soc/mediatek/mt8196/mt8196-afe-pcm.c
diff --git a/sound/soc/mediatek/Kconfig b/sound/soc/mediatek/Kconfig
index 10ca8bccabdd..7003d71b847c 100644
--- a/sound/soc/mediatek/Kconfig
+++ b/sound/soc/mediatek/Kconfig
@@ -322,4 +322,14 @@ config SND_SOC_MT8365_MT6357
Select Y if you have such device.
If unsure select "N".
+config SND_SOC_MT8196
+ tristate "ASoC support for Mediatek MT8196 chip"
+ depends on ARCH_MEDIATEK
+ select SND_SOC_MEDIATEK
+ help
+ This adds ASoC driver for Mediatek MT8196 boards
+ that can be used with other codecs.
+ 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 4b55434f2168..11d7c484a5d3 100644
--- a/sound/soc/mediatek/Makefile
+++ b/sound/soc/mediatek/Makefile
@@ -10,3 +10,4 @@ obj-$(CONFIG_SND_SOC_MT8188) += mt8188/
obj-$(CONFIG_SND_SOC_MT8192) += mt8192/
obj-$(CONFIG_SND_SOC_MT8195) += mt8195/
obj-$(CONFIG_SND_SOC_MT8365) += mt8365/
+obj-$(CONFIG_SND_SOC_MT8196) += mt8196/
diff --git a/sound/soc/mediatek/mt8196/Makefile b/sound/soc/mediatek/mt8196/Makefile
new file mode 100644
index 000000000000..af41ece87672
--- /dev/null
+++ b/sound/soc/mediatek/mt8196/Makefile
@@ -0,0 +1,15 @@
+# SPDX-License-Identifier: GPL-2.0
+
+# common include path
+subdir-ccflags-y += -I$(srctree)/sound/soc/mediatek/common
+
+# platform driver
+snd-soc-mt8196-afe-objs += \
+ mt8196-afe-pcm.o \
+ mt8196-afe-clk.o \
+ mt8196-dai-adda.o \
+ mt8196-dai-i2s.o \
+ mt8196-dai-tdm.o
+
+obj-$(CONFIG_SND_SOC_MT8196) += snd-soc-mt8196-afe.o
+
diff --git a/sound/soc/mediatek/mt8196/mt8196-afe-pcm.c b/sound/soc/mediatek/mt8196/mt8196-afe-pcm.c
new file mode 100644
index 000000000000..41a48ba89b95
--- /dev/null
+++ b/sound/soc/mediatek/mt8196/mt8196-afe-pcm.c
@@ -0,0 +1,2632 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Mediatek ALSA SoC AFE platform driver for 8196
+ *
+ * Copyright (c) 2024 MediaTek Inc.
+ * Author: Darren Ye <darren.ye@mediatek.com>
+ */
+
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/of_device.h>
+#include <sound/soc.h>
+#include <linux/of_reserved_mem.h>
+
+#include "mt8196-afe-clk.h"
+#include "mt8196-afe-common.h"
+#include "mtk-afe-fe-dai.h"
+#include "mtk-afe-platform-driver.h"
+#include "mt8196-interconnection.h"
+
+static const struct snd_pcm_hardware mt8196_afe_hardware = {
+ .info = (SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_NO_PERIOD_WAKEUP |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_MMAP_VALID),
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE),
+ .period_bytes_min = 96,
+ .period_bytes_max = 4 * 48 * 1024,
+ .periods_min = 2,
+ .periods_max = 256,
+ .buffer_bytes_max = 256 * 1024,
+ .fifo_size = 0,
+};
+
+static unsigned int mt8196_rate_transform(struct device *dev,
+ unsigned int rate)
+{
+ switch (rate) {
+ case 8000:
+ return MTK_AFE_IPM2P0_RATE_8K;
+ case 11025:
+ return MTK_AFE_IPM2P0_RATE_11K;
+ case 12000:
+ return MTK_AFE_IPM2P0_RATE_12K;
+ case 16000:
+ return MTK_AFE_IPM2P0_RATE_16K;
+ case 22050:
+ return MTK_AFE_IPM2P0_RATE_22K;
+ case 24000:
+ return MTK_AFE_IPM2P0_RATE_24K;
+ case 32000:
+ return MTK_AFE_IPM2P0_RATE_32K;
+ case 44100:
+ return MTK_AFE_IPM2P0_RATE_44K;
+ case 48000:
+ return MTK_AFE_IPM2P0_RATE_48K;
+ case 88200:
+ return MTK_AFE_IPM2P0_RATE_88K;
+ case 96000:
+ return MTK_AFE_IPM2P0_RATE_96K;
+ case 176400:
+ return MTK_AFE_IPM2P0_RATE_176K;
+ case 192000:
+ return MTK_AFE_IPM2P0_RATE_192K;
+ /* not support 260K */
+ case 352800:
+ return MTK_AFE_IPM2P0_RATE_352K;
+ case 384000:
+ return MTK_AFE_IPM2P0_RATE_384K;
+ default:
+ dev_err(dev, "rate %u invalid, use %d!!!\n",
+ rate, MTK_AFE_IPM2P0_RATE_48K);
+ return MTK_AFE_IPM2P0_RATE_48K;
+ }
+}
+
+static void mt8196_set_cm_rate(struct mtk_base_afe *afe, int id, unsigned int rate)
+{
+ struct mt8196_afe_private *afe_priv = afe->platform_priv;
+
+ afe_priv->cm_rate[id] = rate;
+}
+
+static inline int mt8196_convert_cm_ch(unsigned int ch)
+{
+ return ch - 1;
+}
+
+static unsigned int calculate_cm_update(unsigned int rate, unsigned int ch)
+{
+ return (((26000000 / rate) - 10) / (ch / 2)) - 1;
+}
+
+static int mt8196_set_cm(struct mtk_base_afe *afe, int id,
+ bool update, bool swap, unsigned int ch)
+{
+ struct mt8196_afe_private *afe_priv = afe->platform_priv;
+ unsigned int rate = afe_priv->cm_rate[id];
+ unsigned int rate_val = mt8196_rate_transform(afe->dev, rate);
+ unsigned int update_val = update ? calculate_cm_update(rate, ch) : 0x64;
+ int reg = AFE_CM0_CON0 + 0x10 * id;
+
+ dev_dbg(afe->dev, "CM%d, rate %d, update %d, swap %d, ch %d\n",
+ id, rate, update, swap, ch);
+
+ /* update cnt */
+ regmap_update_bits(afe->regmap,
+ reg,
+ AFE_CM_UPDATE_CNT_MASK << AFE_CM_UPDATE_CNT_SFT,
+ update_val << AFE_CM_UPDATE_CNT_SFT);
+
+ /* rate */
+ regmap_update_bits(afe->regmap,
+ reg,
+ AFE_CM_1X_EN_SEL_FS_MASK << AFE_CM_1X_EN_SEL_FS_SFT,
+ rate_val << AFE_CM_1X_EN_SEL_FS_SFT);
+
+ /* ch num */
+ ch = mt8196_convert_cm_ch(ch);
+ regmap_update_bits(afe->regmap,
+ reg,
+ AFE_CM_CH_NUM_MASK << AFE_CM_CH_NUM_SFT,
+ ch << AFE_CM_CH_NUM_SFT);
+
+ /* swap */
+ regmap_update_bits(afe->regmap,
+ reg,
+ AFE_CM_BYTE_SWAP_MASK << AFE_CM_BYTE_SWAP_SFT,
+ swap << AFE_CM_BYTE_SWAP_SFT);
+
+ return 0;
+}
+
+static int mt8196_enable_cm_bypass(struct mtk_base_afe *afe, int id, bool en)
+{
+ return regmap_update_bits(afe->regmap,
+ AFE_CM0_CON0 + 0x10 * id,
+ AFE_CM_BYPASS_MODE_MASK << AFE_CM_BYPASS_MODE_SFT,
+ en << AFE_CM_BYPASS_MODE_SFT);
+}
+
+static int mt8196_fe_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
+ int memif_num = cpu_dai->id;
+ struct mtk_base_afe_memif *memif = &afe->memif[memif_num];
+ const struct snd_pcm_hardware *mtk_afe_hardware = afe->mtk_afe_hardware;
+ int ret;
+
+ dev_dbg(afe->dev, "memif_num: %d.\n", memif_num);
+
+ memif->substream = substream;
+
+ snd_pcm_hw_constraint_step(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 16);
+
+ snd_soc_set_runtime_hwparams(substream, mtk_afe_hardware);
+
+ ret = snd_pcm_hw_constraint_integer(runtime,
+ SNDRV_PCM_HW_PARAM_PERIODS);
+ if (ret < 0)
+ dev_info(afe->dev, "snd_pcm_hw_constraint_integer failed\n");
+
+ /* dynamic allocate irq to memif */
+ if (memif->irq_usage < 0) {
+ int irq_id = mtk_dynamic_irq_acquire(afe);
+
+ if (irq_id != afe->irqs_size) {
+ /* link */
+ memif->irq_usage = irq_id;
+ } else {
+ dev_err(afe->dev, "no more asys irq\n");
+ ret = -EBUSY;
+ }
+ }
+ return ret;
+}
+
+static void mt8196_fe_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+ struct mt8196_afe_private *afe_priv = afe->platform_priv;
+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
+ int memif_num = cpu_dai->id;
+ struct mtk_base_afe_memif *memif = &afe->memif[memif_num];
+ int irq_id = memif->irq_usage;
+
+ dev_dbg(afe->dev, "memif_num: %d.\n", memif_num);
+
+ memif->substream = NULL;
+ afe_priv->irq_cnt[memif_num] = 0;
+ afe_priv->xrun_assert[memif_num] = 0;
+
+ if (!memif->const_irq) {
+ mtk_dynamic_irq_release(afe, irq_id);
+ memif->irq_usage = -1;
+ memif->substream = NULL;
+ }
+}
+
+static int mt8196_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);
+ unsigned int channels = params_channels(params);
+ struct mt8196_afe_private *afe_priv = afe->platform_priv;
+ int id = snd_soc_rtd_to_cpu(rtd, 0)->id;
+ struct mtk_base_afe_memif *memif = &afe->memif[id];
+ const struct mtk_base_memif_data *data = memif->data;
+
+ afe_priv->cm_channels = channels;
+
+ /* set channels */
+ if (data->ch_num_shift >= 0) {
+ regmap_update_bits(afe->regmap, data->ch_num_reg,
+ data->ch_num_maskbit << data->ch_num_shift,
+ channels << data->ch_num_shift);
+ }
+
+ return mtk_afe_fe_hw_params(substream, params, dai);
+}
+
+static int mt8196_fe_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_pcm_runtime *const runtime = substream->runtime;
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+ struct mt8196_afe_private *afe_priv = afe->platform_priv;
+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
+ int id = cpu_dai->id;
+ struct mtk_base_afe_memif *memif = &afe->memif[id];
+ int irq_id = memif->irq_usage;
+ struct mtk_base_afe_irq *irqs = &afe->irqs[irq_id];
+ const struct mtk_base_irq_data *irq_data = irqs->irq_data;
+ unsigned int counter = runtime->period_size;
+ unsigned int rate = runtime->rate;
+ unsigned int tmp_reg;
+ int fs;
+ int ret;
+
+ dev_info(afe->dev, "%s cmd %d, irq_id %d\n",
+ memif->data->name, cmd, irq_id);
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ dev_dbg(afe->dev, "%s cmd %d, id %d\n",
+ memif->data->name, cmd, id);
+ ret = mtk_memif_set_enable(afe, id);
+ if (ret) {
+ dev_err(afe->dev, "id %d, memif enable fail.\n", id);
+ return ret;
+ }
+
+ /*
+ * for small latency record
+ * ul memif need read some data before irq enable
+ */
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+ if ((runtime->period_size * 1000) / rate <= 10)
+ udelay(300);
+ }
+
+ /* set irq counter */
+ if (afe_priv->irq_cnt[id] > 0)
+ counter = afe_priv->irq_cnt[id];
+
+ regmap_update_bits(afe->regmap,
+ irq_data->irq_cnt_reg,
+ irq_data->irq_cnt_maskbit << irq_data->irq_cnt_shift,
+ counter << irq_data->irq_cnt_shift);
+
+ /* set irq fs */
+ fs = afe->irq_fs(substream, runtime->rate);
+ if (fs < 0)
+ return -EINVAL;
+
+ if (irq_data->irq_fs_reg >= 0)
+ regmap_update_bits(afe->regmap,
+ irq_data->irq_fs_reg,
+ irq_data->irq_fs_maskbit << irq_data->irq_fs_shift,
+ fs << irq_data->irq_fs_shift);
+
+ /* enable interrupt */
+ regmap_update_bits(afe->regmap,
+ irq_data->irq_en_reg,
+ 1 << irq_data->irq_en_shift,
+ 1 << irq_data->irq_en_shift);
+
+ return 0;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ ret = mtk_memif_set_disable(afe, id);
+ if (ret) {
+ dev_warn(afe->dev,
+ "id %d, memif disable fail\n", id);
+ }
+
+ /* disable interrupt */
+ regmap_update_bits(afe->regmap,
+ irq_data->irq_en_reg,
+ 1 << irq_data->irq_en_shift,
+ 0 << irq_data->irq_en_shift);
+
+ /* clear pending IRQ */
+ regmap_read(afe->regmap, irq_data->irq_clr_reg, &tmp_reg);
+ regmap_update_bits(afe->regmap, irq_data->irq_clr_reg,
+ AFE_IRQ_CLR_CFG_MASK_SFT | AFE_IRQ_MISS_FLAG_CLR_CFG_MASK_SFT,
+ tmp_reg ^ (AFE_IRQ_CLR_CFG_MASK_SFT |
+ AFE_IRQ_MISS_FLAG_CLR_CFG_MASK_SFT));
+
+ return ret;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int mt8196_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 =
+ snd_soc_rtdcom_lookup(rtd, AFE_PCM_NAME);
+ struct mtk_base_afe *afe = NULL;
+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
+ int id = cpu_dai->id;
+ unsigned int rate_reg;
+ int cm;
+
+ if (!component)
+ return -EINVAL;
+
+ afe = snd_soc_component_get_drvdata(component);
+
+ if (!afe)
+ return -EINVAL;
+
+ rate_reg = mt8196_rate_transform(afe->dev, rate);
+
+ switch (id) {
+ case MT8196_MEMIF_VUL8:
+ case MT8196_MEMIF_VUL_CM0:
+ cm = CM0;
+ break;
+ case MT8196_MEMIF_VUL9:
+ case MT8196_MEMIF_VUL_CM1:
+ cm = CM1;
+ break;
+ case MT8196_MEMIF_VUL10:
+ case MT8196_MEMIF_VUL_CM2:
+ cm = CM2;
+ break;
+ default:
+ cm = CM0;
+ break;
+ }
+
+ mt8196_set_cm_rate(afe, cm, rate);
+
+ return rate_reg;
+}
+
+static int mt8196_get_dai_fs(struct mtk_base_afe *afe,
+ int dai_id, unsigned int rate)
+{
+ return mt8196_rate_transform(afe->dev, rate);
+}
+
+static int mt8196_irq_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 =
+ snd_soc_rtdcom_lookup(rtd, AFE_PCM_NAME);
+ struct mtk_base_afe *afe = NULL;
+
+ if (!component)
+ return -EINVAL;
+ afe = snd_soc_component_get_drvdata(component);
+ return mt8196_rate_transform(afe->dev, rate);
+}
+
+static int mt8196_get_memif_pbuf_size(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+
+ if ((runtime->period_size * 1000) / runtime->rate > 10)
+ return MT8196_MEMIF_PBUF_SIZE_256_BYTES;
+ else
+ return MT8196_MEMIF_PBUF_SIZE_32_BYTES;
+}
+
+/* FE DAIs */
+static const struct snd_soc_dai_ops mt8196_memif_dai_ops = {
+ .startup = mt8196_fe_startup,
+ .shutdown = mt8196_fe_shutdown,
+ .hw_params = mt8196_fe_hw_params,
+ .hw_free = mtk_afe_fe_hw_free,
+ .prepare = mtk_afe_fe_prepare,
+ .trigger = mt8196_fe_trigger,
+};
+
+#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)
+
+#define MTK_PCM_DAI_RATES (SNDRV_PCM_RATE_8000 |\
+ SNDRV_PCM_RATE_16000 |\
+ SNDRV_PCM_RATE_32000 |\
+ SNDRV_PCM_RATE_48000)
+
+#define MTK_PCM_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\
+ SNDRV_PCM_FMTBIT_S24_LE |\
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+#define MT8196_FE_DAI(_name, _id, max_ch, dir) \
+{ \
+ .name = #_name, \
+ .id = _id, \
+ .dir = { \
+ .stream_name = #_name, \
+ .channels_min = 1, \
+ .channels_max = max_ch, \
+ .rates = MTK_PCM_RATES, \
+ .formats = MTK_PCM_FORMATS, \
+ }, \
+ .ops = &mt8196_memif_dai_ops, \
+}
+
+static struct snd_soc_dai_driver mt8196_memif_dai_driver[] = {
+ /* FE DAIs: memory intefaces to CPU */
+ /* Playback */
+ MT8196_FE_DAI(DL0, MT8196_MEMIF_DL0, 2, playback),
+ MT8196_FE_DAI(DL1, MT8196_MEMIF_DL1, 2, playback),
+ MT8196_FE_DAI(DL2, MT8196_MEMIF_DL2, 2, playback),
+ MT8196_FE_DAI(DL3, MT8196_MEMIF_DL3, 2, playback),
+ MT8196_FE_DAI(DL4, MT8196_MEMIF_DL4, 2, playback),
+ MT8196_FE_DAI(DL5, MT8196_MEMIF_DL5, 2, playback),
+ MT8196_FE_DAI(DL6, MT8196_MEMIF_DL6, 2, playback),
+ MT8196_FE_DAI(DL7, MT8196_MEMIF_DL7, 2, playback),
+ MT8196_FE_DAI(DL8, MT8196_MEMIF_DL8, 2, playback),
+ MT8196_FE_DAI(DL23, MT8196_MEMIF_DL23, 2, playback),
+ MT8196_FE_DAI(DL24, MT8196_MEMIF_DL24, 2, playback),
+ MT8196_FE_DAI(DL25, MT8196_MEMIF_DL25, 2, playback),
+ MT8196_FE_DAI(DL26, MT8196_MEMIF_DL26, 2, playback),
+ MT8196_FE_DAI(DL_4CH, MT8196_MEMIF_DL_4CH, 4, playback),
+ MT8196_FE_DAI(DL_24CH, MT8196_MEMIF_DL_24CH, 8, playback),
+ MT8196_FE_DAI(HDMI, MT8196_MEMIF_HDMI, 8, playback),
+ /* Capture */
+ MT8196_FE_DAI(UL0, MT8196_MEMIF_VUL0, 2, capture),
+ MT8196_FE_DAI(UL1, MT8196_MEMIF_VUL1, 2, capture),
+ MT8196_FE_DAI(UL2, MT8196_MEMIF_VUL2, 2, capture),
+ MT8196_FE_DAI(UL3, MT8196_MEMIF_VUL3, 2, capture),
+ MT8196_FE_DAI(UL4, MT8196_MEMIF_VUL4, 2, capture),
+ MT8196_FE_DAI(UL5, MT8196_MEMIF_VUL5, 2, capture),
+ MT8196_FE_DAI(UL6, MT8196_MEMIF_VUL6, 2, capture),
+ MT8196_FE_DAI(UL7, MT8196_MEMIF_VUL7, 2, capture),
+ MT8196_FE_DAI(UL8, MT8196_MEMIF_VUL8, 2, capture),
+ MT8196_FE_DAI(UL9, MT8196_MEMIF_VUL9, 16, capture),
+ MT8196_FE_DAI(UL10, MT8196_MEMIF_VUL10, 2, capture),
+ MT8196_FE_DAI(UL24, MT8196_MEMIF_VUL24, 2, capture),
+ MT8196_FE_DAI(UL25, MT8196_MEMIF_VUL25, 2, capture),
+ MT8196_FE_DAI(UL26, MT8196_MEMIF_VUL26, 2, capture),
+ MT8196_FE_DAI(UL_CM0, MT8196_MEMIF_VUL_CM0, 8, capture),
+ MT8196_FE_DAI(UL_CM1, MT8196_MEMIF_VUL_CM1, 16, capture),
+ MT8196_FE_DAI(UL_CM2, MT8196_MEMIF_VUL_CM2, 32, capture),
+ MT8196_FE_DAI(UL_ETDM_IN0, MT8196_MEMIF_ETDM_IN0, 2, capture),
+ MT8196_FE_DAI(UL_ETDM_IN1, MT8196_MEMIF_ETDM_IN1, 2, capture),
+ MT8196_FE_DAI(UL_ETDM_IN2, MT8196_MEMIF_ETDM_IN2, 2, capture),
+ MT8196_FE_DAI(UL_ETDM_IN3, MT8196_MEMIF_ETDM_IN3, 2, capture),
+ MT8196_FE_DAI(UL_ETDM_IN4, MT8196_MEMIF_ETDM_IN4, 2, capture),
+ MT8196_FE_DAI(UL_ETDM_IN6, MT8196_MEMIF_ETDM_IN6, 2, capture),
+};
+
+static const struct snd_kcontrol_new mt8196_pcm_kcontrols[] = {
+};
+
+enum {
+ CM0_MUX_VUL8_2CH,
+ CM0_MUX_VUL8_8CH,
+ CM0_MUX_MASK,
+};
+
+enum {
+ CM1_MUX_VUL9_2CH,
+ CM1_MUX_VUL9_16CH,
+ CM1_MUX_MASK,
+};
+
+enum {
+ CM2_MUX_VUL10_2CH,
+ CM2_MUX_VUL10_32CH,
+ CM2_MUX_MASK,
+};
+
+static int ul_cm0_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
+ struct mt8196_afe_private *afe_priv = afe->platform_priv;
+ unsigned int channels = afe_priv->cm_channels;
+
+ dev_dbg(afe->dev, "event 0x%x, name %s, channels %u\n",
+ event, w->name, channels);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ mt8196_enable_cm_bypass(afe, CM0, 0x0);
+ mt8196_set_cm(afe, CM0, true, false, channels);
+ regmap_update_bits(afe->regmap, AUDIO_TOP_CON0,
+ PDN_CM0_MASK_SFT, 0 << PDN_CM0_SFT);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ mt8196_enable_cm_bypass(afe, CM0, 0x1);
+ regmap_update_bits(afe->regmap, AUDIO_TOP_CON0,
+ PDN_CM0_MASK_SFT, 1 << PDN_CM0_SFT);
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+static int ul_cm1_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
+ struct mt8196_afe_private *afe_priv = afe->platform_priv;
+ unsigned int channels = afe_priv->cm_channels;
+
+ dev_dbg(afe->dev, "event 0x%x, name %s, channels %u\n",
+ event, w->name, channels);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ mt8196_enable_cm_bypass(afe, CM1, 0x0);
+ mt8196_set_cm(afe, CM1, true, false, channels);
+ regmap_update_bits(afe->regmap, AUDIO_TOP_CON0,
+ PDN_CM1_MASK_SFT, 0 << PDN_CM1_SFT);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ mt8196_enable_cm_bypass(afe, CM1, 0x1);
+ regmap_update_bits(afe->regmap, AUDIO_TOP_CON0,
+ PDN_CM1_MASK_SFT, 1 << PDN_CM1_SFT);
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+static int ul_cm2_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
+ struct mt8196_afe_private *afe_priv = afe->platform_priv;
+ unsigned int channels = afe_priv->cm_channels;
+
+ dev_dbg(afe->dev, "event 0x%x, name %s, channels %u\n",
+ event, w->name, channels);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ mt8196_enable_cm_bypass(afe, CM2, 0x0);
+ mt8196_set_cm(afe, CM2, true, false, channels);
+ regmap_update_bits(afe->regmap, AUDIO_TOP_CON0,
+ PDN_CM2_MASK_SFT, 0 << PDN_CM2_SFT);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ mt8196_enable_cm_bypass(afe, CM2, 0x1);
+ regmap_update_bits(afe->regmap, AUDIO_TOP_CON0,
+ PDN_CM2_MASK_SFT, 1 << PDN_CM2_SFT);
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+/* dma widget & routes*/
+static const struct snd_kcontrol_new memif_ul0_ch1_mix[] = {
+ /* Normal record */
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN018_0,
+ I_ADDA_UL_CH1, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul0_ch2_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2", AFE_CONN019_0,
+ I_ADDA_UL_CH2, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul1_ch1_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I2SIN4_CH1", AFE_CONN020_4,
+ I_I2SIN4_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I2SIN6_CH1", AFE_CONN020_5,
+ I_I2SIN6_CH1, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul1_ch2_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I2SIN4_CH2", AFE_CONN021_4,
+ I_I2SIN4_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I2SIN6_CH2", AFE_CONN021_5,
+ I_I2SIN6_CH2, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul2_ch1_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH3", AFE_CONN022_0,
+ I_ADDA_UL_CH3, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul2_ch2_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH4", AFE_CONN023_0,
+ I_ADDA_UL_CH4, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul3_ch1_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I2SIN0_CH1", AFE_CONN024_4,
+ I_I2SIN0_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I2SIN1_CH1", AFE_CONN024_4,
+ I_I2SIN1_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I2SIN3_CH1", AFE_CONN024_4,
+ I_I2SIN3_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I2SIN4_CH1", AFE_CONN024_4,
+ I_I2SIN4_CH1, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul3_ch2_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I2SIN0_CH2", AFE_CONN025_4,
+ I_I2SIN0_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I2SIN1_CH2", AFE_CONN025_4,
+ I_I2SIN1_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I2SIN3_CH2", AFE_CONN025_4,
+ I_I2SIN3_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I2SIN4_CH2", AFE_CONN025_4,
+ I_I2SIN4_CH2, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul4_ch1_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN026_0,
+ I_ADDA_UL_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL0_CH1", AFE_CONN026_1,
+ I_DL0_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL1_CH1", AFE_CONN026_1,
+ I_DL1_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL6_CH1", AFE_CONN026_1,
+ I_DL6_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL2_CH1", AFE_CONN026_1,
+ I_DL2_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL3_CH1", AFE_CONN026_1,
+ I_DL3_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL_24CH_CH1", AFE_CONN026_1,
+ I_DL_24CH_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I2SIN0_CH1", AFE_CONN026_4,
+ I_I2SIN0_CH1, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul4_ch2_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2", AFE_CONN027_0,
+ I_ADDA_UL_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL0_CH2", AFE_CONN027_1,
+ I_DL0_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL1_CH2", AFE_CONN027_1,
+ I_DL1_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL6_CH2", AFE_CONN027_1,
+ I_DL6_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL2_CH2", AFE_CONN027_1,
+ I_DL2_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL3_CH2", AFE_CONN027_1,
+ I_DL3_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("DL_24CH_CH2", AFE_CONN027_1,
+ I_DL_24CH_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I2SIN0_CH2", AFE_CONN027_4,
+ I_I2SIN0_CH2, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul5_ch1_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I2SIN3_CH1", AFE_CONN028_4,
+ I_I2SIN3_CH1, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul5_ch2_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I2SIN3_CH2", AFE_CONN029_4,
+ I_I2SIN3_CH2, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul6_ch1_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN030_0,
+ I_ADDA_UL_CH1, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul6_ch2_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2", AFE_CONN031_0,
+ I_ADDA_UL_CH2, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul7_ch1_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN032_0,
+ I_ADDA_UL_CH1, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul7_ch2_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2", AFE_CONN033_0,
+ I_ADDA_UL_CH2, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul8_ch1_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN034_0,
+ I_ADDA_UL_CH1, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul8_ch2_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN035_0,
+ I_ADDA_UL_CH1, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul9_ch1_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN036_0,
+ I_ADDA_UL_CH1, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul9_ch2_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2", AFE_CONN037_0,
+ I_ADDA_UL_CH2, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul10_ch1_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN038_0,
+ I_ADDA_UL_CH1, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul10_ch2_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2", AFE_CONN039_0,
+ I_ADDA_UL_CH2, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul24_ch1_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I2SIN0_CH1", AFE_CONN066_4,
+ I_I2SIN0_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I2SIN6_CH1", AFE_CONN066_5,
+ I_I2SIN6_CH1, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul24_ch2_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I2SIN0_CH2", AFE_CONN067_4,
+ I_I2SIN0_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I2SIN6_CH2", AFE_CONN067_5,
+ I_I2SIN6_CH2, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul25_ch1_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I2SIN0_CH1", AFE_CONN068_4,
+ I_I2SIN0_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I2SIN6_CH1", AFE_CONN068_5,
+ I_I2SIN6_CH1, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul25_ch2_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I2SIN0_CH2", AFE_CONN069_4,
+ I_I2SIN0_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I2SIN6_CH2", AFE_CONN069_5,
+ I_I2SIN6_CH2, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul26_ch1_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I2SIN0_CH1", AFE_CONN070_4,
+ I_I2SIN0_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I2SIN6_CH1", AFE_CONN070_5,
+ I_I2SIN6_CH1, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul26_ch2_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I2SIN0_CH2", AFE_CONN071_4,
+ I_I2SIN0_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I2SIN6_CH2", AFE_CONN071_5,
+ I_I2SIN6_CH2, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul_cm0_ch1_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN040_0,
+ I_ADDA_UL_CH1, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul_cm0_ch2_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2", AFE_CONN041_0,
+ I_ADDA_UL_CH2, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul_cm0_ch3_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH3", AFE_CONN042_0,
+ I_ADDA_UL_CH3, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul_cm0_ch4_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH4", AFE_CONN043_0,
+ I_ADDA_UL_CH4, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul_cm0_ch5_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN044_0,
+ I_ADDA_UL_CH1, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul_cm0_ch6_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN045_0,
+ I_ADDA_UL_CH1, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul_cm0_ch7_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN046_0,
+ I_ADDA_UL_CH1, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul_cm0_ch8_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN047_0,
+ I_ADDA_UL_CH1, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul_cm1_ch1_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN048_0,
+ I_ADDA_UL_CH1, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul_cm1_ch2_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2", AFE_CONN049_0,
+ I_ADDA_UL_CH2, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul_cm1_ch3_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH3", AFE_CONN050_0,
+ I_ADDA_UL_CH3, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul_cm1_ch4_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH4", AFE_CONN051_0,
+ I_ADDA_UL_CH4, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul_cm1_ch5_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN052_0,
+ I_ADDA_UL_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2", AFE_CONN052_0,
+ I_ADDA_UL_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH3", AFE_CONN052_0,
+ I_ADDA_UL_CH3, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH4", AFE_CONN052_0,
+ I_ADDA_UL_CH4, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul_cm1_ch6_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN053_0,
+ I_ADDA_UL_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2", AFE_CONN053_0,
+ I_ADDA_UL_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH3", AFE_CONN053_0,
+ I_ADDA_UL_CH3, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH4", AFE_CONN053_0,
+ I_ADDA_UL_CH4, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul_cm1_ch7_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN054_0,
+ I_ADDA_UL_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2", AFE_CONN054_0,
+ I_ADDA_UL_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH3", AFE_CONN054_0,
+ I_ADDA_UL_CH3, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH4", AFE_CONN054_0,
+ I_ADDA_UL_CH4, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul_cm1_ch8_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN055_0,
+ I_ADDA_UL_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2", AFE_CONN055_0,
+ I_ADDA_UL_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH3", AFE_CONN055_0,
+ I_ADDA_UL_CH3, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH4", AFE_CONN055_0,
+ I_ADDA_UL_CH4, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul_cm1_ch9_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN056_0,
+ I_ADDA_UL_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2", AFE_CONN056_0,
+ I_ADDA_UL_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH3", AFE_CONN056_0,
+ I_ADDA_UL_CH3, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH4", AFE_CONN056_0,
+ I_ADDA_UL_CH4, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul_cm1_ch10_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN057_0,
+ I_ADDA_UL_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2", AFE_CONN057_0,
+ I_ADDA_UL_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH3", AFE_CONN057_0,
+ I_ADDA_UL_CH3, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH4", AFE_CONN057_0,
+ I_ADDA_UL_CH4, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul_cm1_ch11_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN058_0,
+ I_ADDA_UL_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2", AFE_CONN058_0,
+ I_ADDA_UL_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH3", AFE_CONN058_0,
+ I_ADDA_UL_CH3, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH4", AFE_CONN058_0,
+ I_ADDA_UL_CH4, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul_cm1_ch12_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN059_0,
+ I_ADDA_UL_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2", AFE_CONN059_0,
+ I_ADDA_UL_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH3", AFE_CONN059_0,
+ I_ADDA_UL_CH3, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH4", AFE_CONN059_0,
+ I_ADDA_UL_CH4, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul_cm1_ch13_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN060_0,
+ I_ADDA_UL_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2", AFE_CONN060_0,
+ I_ADDA_UL_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH3", AFE_CONN060_0,
+ I_ADDA_UL_CH3, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH4", AFE_CONN060_0,
+ I_ADDA_UL_CH4, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul_cm1_ch14_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN061_0,
+ I_ADDA_UL_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2", AFE_CONN061_0,
+ I_ADDA_UL_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH3", AFE_CONN061_0,
+ I_ADDA_UL_CH3, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH4", AFE_CONN061_0,
+ I_ADDA_UL_CH4, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul_cm1_ch15_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN062_0,
+ I_ADDA_UL_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2", AFE_CONN062_0,
+ I_ADDA_UL_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH3", AFE_CONN062_0,
+ I_ADDA_UL_CH3, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH4", AFE_CONN062_0,
+ I_ADDA_UL_CH4, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul_cm1_ch16_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN063_0,
+ I_ADDA_UL_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2", AFE_CONN063_0,
+ I_ADDA_UL_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH3", AFE_CONN063_0,
+ I_ADDA_UL_CH3, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH4", AFE_CONN063_0,
+ I_ADDA_UL_CH4, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul_cm2_ch1_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN064_0, I_ADDA_UL_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2", AFE_CONN064_0, I_ADDA_UL_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH3", AFE_CONN064_0, I_ADDA_UL_CH3, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH4", AFE_CONN064_0, I_ADDA_UL_CH4, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH5", AFE_CONN064_0, I_ADDA_UL_CH5, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH6", AFE_CONN064_0, I_ADDA_UL_CH6, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul_cm2_ch2_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN065_0, I_ADDA_UL_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2", AFE_CONN065_0, I_ADDA_UL_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH3", AFE_CONN065_0, I_ADDA_UL_CH3, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH4", AFE_CONN065_0, I_ADDA_UL_CH4, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH5", AFE_CONN065_0, I_ADDA_UL_CH5, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH6", AFE_CONN065_0, I_ADDA_UL_CH6, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul_cm2_ch3_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN066_0, I_ADDA_UL_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2", AFE_CONN066_0, I_ADDA_UL_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH3", AFE_CONN066_0, I_ADDA_UL_CH3, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH4", AFE_CONN066_0, I_ADDA_UL_CH4, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH5", AFE_CONN066_0, I_ADDA_UL_CH5, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH6", AFE_CONN066_0, I_ADDA_UL_CH6, 1, 0)
+};
+
+static const struct snd_kcontrol_new memif_ul_cm2_ch4_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN067_0, I_ADDA_UL_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2", AFE_CONN067_0, I_ADDA_UL_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH3", AFE_CONN067_0, I_ADDA_UL_CH3, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH4", AFE_CONN067_0, I_ADDA_UL_CH4, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH5", AFE_CONN067_0, I_ADDA_UL_CH5, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH6", AFE_CONN067_0, I_ADDA_UL_CH6, 1, 0)
+};
+
+static const struct snd_kcontrol_new memif_ul_cm2_ch5_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN068_0, I_ADDA_UL_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2", AFE_CONN068_0, I_ADDA_UL_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH3", AFE_CONN068_0, I_ADDA_UL_CH3, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH4", AFE_CONN068_0, I_ADDA_UL_CH4, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH5", AFE_CONN068_0, I_ADDA_UL_CH5, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH6", AFE_CONN068_0, I_ADDA_UL_CH6, 1, 0)
+};
+
+static const struct snd_kcontrol_new memif_ul_cm2_ch6_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN069_0, I_ADDA_UL_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2", AFE_CONN069_0, I_ADDA_UL_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH3", AFE_CONN069_0, I_ADDA_UL_CH3, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH4", AFE_CONN069_0, I_ADDA_UL_CH4, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH5", AFE_CONN069_0, I_ADDA_UL_CH5, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH6", AFE_CONN069_0, I_ADDA_UL_CH6, 1, 0)
+};
+
+static const struct snd_kcontrol_new memif_ul_cm2_ch7_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN070_0, I_ADDA_UL_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2", AFE_CONN070_0, I_ADDA_UL_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH3", AFE_CONN070_0, I_ADDA_UL_CH3, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH4", AFE_CONN070_0, I_ADDA_UL_CH4, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul_cm2_ch8_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN071_0, I_ADDA_UL_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2", AFE_CONN071_0, I_ADDA_UL_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH3", AFE_CONN071_0, I_ADDA_UL_CH3, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH4", AFE_CONN071_0, I_ADDA_UL_CH4, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul_cm2_ch9_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN072_0, I_ADDA_UL_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2", AFE_CONN072_0, I_ADDA_UL_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH3", AFE_CONN072_0, I_ADDA_UL_CH3, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH4", AFE_CONN072_0, I_ADDA_UL_CH4, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul_cm2_ch10_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN073_0, I_ADDA_UL_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2", AFE_CONN073_0, I_ADDA_UL_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH3", AFE_CONN073_0, I_ADDA_UL_CH3, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH4", AFE_CONN073_0, I_ADDA_UL_CH4, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul_cm2_ch11_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN074_0, I_ADDA_UL_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2", AFE_CONN074_0, I_ADDA_UL_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH3", AFE_CONN074_0, I_ADDA_UL_CH3, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH4", AFE_CONN074_0, I_ADDA_UL_CH4, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul_cm2_ch12_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN075_0, I_ADDA_UL_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2", AFE_CONN075_0, I_ADDA_UL_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH3", AFE_CONN075_0, I_ADDA_UL_CH3, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH4", AFE_CONN075_0, I_ADDA_UL_CH4, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul_cm2_ch13_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN076_0, I_ADDA_UL_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2", AFE_CONN076_0, I_ADDA_UL_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH3", AFE_CONN076_0, I_ADDA_UL_CH3, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH4", AFE_CONN076_0, I_ADDA_UL_CH4, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul_cm2_ch14_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN077_0, I_ADDA_UL_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2", AFE_CONN077_0, I_ADDA_UL_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH3", AFE_CONN077_0, I_ADDA_UL_CH3, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH4", AFE_CONN077_0, I_ADDA_UL_CH4, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul_cm2_ch15_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN078_0, I_ADDA_UL_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2", AFE_CONN078_0, I_ADDA_UL_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH3", AFE_CONN078_0, I_ADDA_UL_CH3, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH4", AFE_CONN078_0, I_ADDA_UL_CH4, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul_cm2_ch16_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN079_0, I_ADDA_UL_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2", AFE_CONN079_0, I_ADDA_UL_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH3", AFE_CONN079_0, I_ADDA_UL_CH3, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH4", AFE_CONN079_0, I_ADDA_UL_CH4, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul_cm2_ch17_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN080_0, I_ADDA_UL_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2", AFE_CONN080_0, I_ADDA_UL_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH3", AFE_CONN080_0, I_ADDA_UL_CH3, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH4", AFE_CONN080_0, I_ADDA_UL_CH4, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul_cm2_ch18_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN081_0, I_ADDA_UL_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2", AFE_CONN081_0, I_ADDA_UL_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH3", AFE_CONN081_0, I_ADDA_UL_CH3, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH4", AFE_CONN081_0, I_ADDA_UL_CH4, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul_cm2_ch19_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN082_0, I_ADDA_UL_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2", AFE_CONN082_0, I_ADDA_UL_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH3", AFE_CONN082_0, I_ADDA_UL_CH3, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH4", AFE_CONN082_0, I_ADDA_UL_CH4, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul_cm2_ch20_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN083_0, I_ADDA_UL_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2", AFE_CONN083_0, I_ADDA_UL_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH3", AFE_CONN083_0, I_ADDA_UL_CH3, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH4", AFE_CONN083_0, I_ADDA_UL_CH4, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul_cm2_ch21_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN084_0, I_ADDA_UL_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2", AFE_CONN084_0, I_ADDA_UL_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH3", AFE_CONN084_0, I_ADDA_UL_CH3, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH4", AFE_CONN084_0, I_ADDA_UL_CH4, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul_cm2_ch22_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN085_0, I_ADDA_UL_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2", AFE_CONN085_0, I_ADDA_UL_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH3", AFE_CONN085_0, I_ADDA_UL_CH3, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH4", AFE_CONN085_0, I_ADDA_UL_CH4, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul_cm2_ch23_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN086_0, I_ADDA_UL_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2", AFE_CONN086_0, I_ADDA_UL_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH3", AFE_CONN086_0, I_ADDA_UL_CH3, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH4", AFE_CONN086_0, I_ADDA_UL_CH4, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul_cm2_ch24_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN087_0, I_ADDA_UL_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2", AFE_CONN087_0, I_ADDA_UL_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH3", AFE_CONN087_0, I_ADDA_UL_CH3, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH4", AFE_CONN087_0, I_ADDA_UL_CH4, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul_cm2_ch25_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN088_0, I_ADDA_UL_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2", AFE_CONN088_0, I_ADDA_UL_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH3", AFE_CONN088_0, I_ADDA_UL_CH3, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH4", AFE_CONN088_0, I_ADDA_UL_CH4, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul_cm2_ch26_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN089_0, I_ADDA_UL_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2", AFE_CONN089_0, I_ADDA_UL_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH3", AFE_CONN089_0, I_ADDA_UL_CH3, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH4", AFE_CONN089_0, I_ADDA_UL_CH4, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul_cm2_ch27_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN090_0, I_ADDA_UL_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2", AFE_CONN090_0, I_ADDA_UL_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH3", AFE_CONN090_0, I_ADDA_UL_CH3, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH4", AFE_CONN090_0, I_ADDA_UL_CH4, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul_cm2_ch28_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN091_0, I_ADDA_UL_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2", AFE_CONN091_0, I_ADDA_UL_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH3", AFE_CONN091_0, I_ADDA_UL_CH3, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH4", AFE_CONN091_0, I_ADDA_UL_CH4, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul_cm2_ch29_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN092_0, I_ADDA_UL_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2", AFE_CONN092_0, I_ADDA_UL_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH3", AFE_CONN092_0, I_ADDA_UL_CH3, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH4", AFE_CONN092_0, I_ADDA_UL_CH4, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul_cm2_ch30_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN093_0, I_ADDA_UL_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2", AFE_CONN093_0, I_ADDA_UL_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH3", AFE_CONN093_0, I_ADDA_UL_CH3, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH4", AFE_CONN093_0, I_ADDA_UL_CH4, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul_cm2_ch31_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN094_0, I_ADDA_UL_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2", AFE_CONN094_0, I_ADDA_UL_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH3", AFE_CONN094_0, I_ADDA_UL_CH3, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH4", AFE_CONN094_0, I_ADDA_UL_CH4, 1, 0),
+};
+
+static const struct snd_kcontrol_new memif_ul_cm2_ch32_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN095_0, I_ADDA_UL_CH1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2", AFE_CONN095_0, I_ADDA_UL_CH2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH3", AFE_CONN095_0, I_ADDA_UL_CH3, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH4", AFE_CONN095_0, I_ADDA_UL_CH4, 1, 0),
+};
+
+static const char * const cm0_mux_map[] = {
+ "CM0_8CH_PATH",
+ "UL8_2CH_PATH",
+};
+
+static const char * const cm1_mux_map[] = {
+ "CM1_16CH_PATH",
+ "UL9_2CH_PATH",
+};
+
+static const char * const cm2_mux_map[] = {
+ "CM2_32CH_PATH",
+ "UL10_2CH_PATH",
+};
+
+static int cm0_mux_map_value[] = {
+ CM0_MUX_VUL8_8CH,
+ CM0_MUX_VUL8_2CH,
+};
+
+static int cm1_mux_map_value[] = {
+ CM1_MUX_VUL9_16CH,
+ CM1_MUX_VUL9_2CH,
+};
+
+static int cm2_mux_map_value[] = {
+ CM2_MUX_VUL10_32CH,
+ CM2_MUX_VUL10_2CH,
+};
+
+static SOC_VALUE_ENUM_SINGLE_DECL(ul_cm0_mux_map_enum,
+ AFE_CM0_CON0,
+ AFE_CM0_OUTPUT_MUX_SFT,
+ AFE_CM0_OUTPUT_MUX_MASK,
+ cm0_mux_map,
+ cm0_mux_map_value);
+static SOC_VALUE_ENUM_SINGLE_DECL(ul_cm1_mux_map_enum,
+ AFE_CM1_CON0,
+ AFE_CM1_OUTPUT_MUX_SFT,
+ AFE_CM1_OUTPUT_MUX_MASK,
+ cm1_mux_map,
+ cm1_mux_map_value);
+static SOC_VALUE_ENUM_SINGLE_DECL(ul_cm2_mux_map_enum,
+ AFE_CM2_CON0,
+ AFE_CM2_OUTPUT_MUX_SFT,
+ AFE_CM2_OUTPUT_MUX_MASK,
+ cm2_mux_map,
+ cm2_mux_map_value);
+
+static const struct snd_kcontrol_new ul_cm0_mux_control =
+ SOC_DAPM_ENUM("CM0_UL_MUX Route", ul_cm0_mux_map_enum);
+
+static const struct snd_kcontrol_new ul_cm1_mux_control =
+ SOC_DAPM_ENUM("CM1_UL_MUX Route", ul_cm1_mux_map_enum);
+
+static const struct snd_kcontrol_new ul_cm2_mux_control =
+ SOC_DAPM_ENUM("CM2_UL_MUX Route", ul_cm2_mux_map_enum);
+
+static const struct snd_soc_dapm_widget mt8196_memif_widgets[] = {
+ /* inter-connections */
+ SND_SOC_DAPM_MIXER("UL0_CH1", SND_SOC_NOPM, 0, 0,
+ memif_ul0_ch1_mix, ARRAY_SIZE(memif_ul0_ch1_mix)),
+ SND_SOC_DAPM_MIXER("UL0_CH2", SND_SOC_NOPM, 0, 0,
+ memif_ul0_ch2_mix, ARRAY_SIZE(memif_ul0_ch2_mix)),
+
+ SND_SOC_DAPM_MIXER("UL1_CH1", SND_SOC_NOPM, 0, 0,
+ memif_ul1_ch1_mix, ARRAY_SIZE(memif_ul1_ch1_mix)),
+ SND_SOC_DAPM_MIXER("UL1_CH2", SND_SOC_NOPM, 0, 0,
+ memif_ul1_ch2_mix, ARRAY_SIZE(memif_ul1_ch2_mix)),
+
+ SND_SOC_DAPM_MIXER("UL2_CH1", SND_SOC_NOPM, 0, 0,
+ memif_ul2_ch1_mix, ARRAY_SIZE(memif_ul2_ch1_mix)),
+ SND_SOC_DAPM_MIXER("UL2_CH2", SND_SOC_NOPM, 0, 0,
+ memif_ul2_ch2_mix, ARRAY_SIZE(memif_ul2_ch2_mix)),
+
+ SND_SOC_DAPM_MIXER("UL3_CH1", SND_SOC_NOPM, 0, 0,
+ memif_ul3_ch1_mix, ARRAY_SIZE(memif_ul3_ch1_mix)),
+ SND_SOC_DAPM_MIXER("UL3_CH2", SND_SOC_NOPM, 0, 0,
+ memif_ul3_ch2_mix, ARRAY_SIZE(memif_ul3_ch2_mix)),
+
+ SND_SOC_DAPM_MIXER("UL4_CH1", SND_SOC_NOPM, 0, 0,
+ memif_ul4_ch1_mix, ARRAY_SIZE(memif_ul4_ch1_mix)),
+ SND_SOC_DAPM_MIXER("UL4_CH2", SND_SOC_NOPM, 0, 0,
+ memif_ul4_ch2_mix, ARRAY_SIZE(memif_ul4_ch2_mix)),
+
+ SND_SOC_DAPM_MIXER("UL5_CH1", SND_SOC_NOPM, 0, 0,
+ memif_ul5_ch1_mix, ARRAY_SIZE(memif_ul5_ch1_mix)),
+ SND_SOC_DAPM_MIXER("UL5_CH2", SND_SOC_NOPM, 0, 0,
+ memif_ul5_ch2_mix, ARRAY_SIZE(memif_ul5_ch2_mix)),
+
+ SND_SOC_DAPM_MIXER("UL6_CH1", SND_SOC_NOPM, 0, 0,
+ memif_ul6_ch1_mix, ARRAY_SIZE(memif_ul6_ch1_mix)),
+ SND_SOC_DAPM_MIXER("UL6_CH2", SND_SOC_NOPM, 0, 0,
+ memif_ul6_ch2_mix, ARRAY_SIZE(memif_ul6_ch2_mix)),
+
+ SND_SOC_DAPM_MIXER("UL7_CH1", SND_SOC_NOPM, 0, 0,
+ memif_ul7_ch1_mix, ARRAY_SIZE(memif_ul7_ch1_mix)),
+ SND_SOC_DAPM_MIXER("UL7_CH2", SND_SOC_NOPM, 0, 0,
+ memif_ul7_ch2_mix, ARRAY_SIZE(memif_ul7_ch2_mix)),
+
+ SND_SOC_DAPM_MIXER("UL8_CH1", SND_SOC_NOPM, 0, 0,
+ memif_ul8_ch1_mix, ARRAY_SIZE(memif_ul8_ch1_mix)),
+ SND_SOC_DAPM_MIXER("UL8_CH2", SND_SOC_NOPM, 0, 0,
+ memif_ul8_ch2_mix, ARRAY_SIZE(memif_ul8_ch2_mix)),
+
+ SND_SOC_DAPM_MIXER("UL9_CH1", SND_SOC_NOPM, 0, 0,
+ memif_ul9_ch1_mix, ARRAY_SIZE(memif_ul9_ch1_mix)),
+ SND_SOC_DAPM_MIXER("UL9_CH2", SND_SOC_NOPM, 0, 0,
+ memif_ul9_ch2_mix, ARRAY_SIZE(memif_ul9_ch2_mix)),
+
+ SND_SOC_DAPM_MIXER("UL10_CH1", SND_SOC_NOPM, 0, 0,
+ memif_ul10_ch1_mix, ARRAY_SIZE(memif_ul10_ch1_mix)),
+ SND_SOC_DAPM_MIXER("UL10_CH2", SND_SOC_NOPM, 0, 0,
+ memif_ul10_ch2_mix, ARRAY_SIZE(memif_ul10_ch2_mix)),
+
+ SND_SOC_DAPM_MIXER("UL24_CH1", SND_SOC_NOPM, 0, 0,
+ memif_ul24_ch1_mix, ARRAY_SIZE(memif_ul24_ch1_mix)),
+ SND_SOC_DAPM_MIXER("UL24_CH2", SND_SOC_NOPM, 0, 0,
+ memif_ul24_ch2_mix, ARRAY_SIZE(memif_ul24_ch2_mix)),
+
+ SND_SOC_DAPM_MIXER("UL25_CH1", SND_SOC_NOPM, 0, 0,
+ memif_ul25_ch1_mix, ARRAY_SIZE(memif_ul25_ch1_mix)),
+ SND_SOC_DAPM_MIXER("UL25_CH2", SND_SOC_NOPM, 0, 0,
+ memif_ul25_ch2_mix, ARRAY_SIZE(memif_ul25_ch2_mix)),
+
+ SND_SOC_DAPM_MIXER("UL26_CH1", SND_SOC_NOPM, 0, 0,
+ memif_ul26_ch1_mix, ARRAY_SIZE(memif_ul26_ch1_mix)),
+ SND_SOC_DAPM_MIXER("UL26_CH2", SND_SOC_NOPM, 0, 0,
+ memif_ul26_ch2_mix, ARRAY_SIZE(memif_ul26_ch2_mix)),
+
+ SND_SOC_DAPM_MIXER("UL_CM0_CH1", SND_SOC_NOPM, 0, 0,
+ memif_ul_cm0_ch1_mix, ARRAY_SIZE(memif_ul_cm0_ch1_mix)),
+ SND_SOC_DAPM_MIXER("UL_CM0_CH2", SND_SOC_NOPM, 0, 0,
+ memif_ul_cm0_ch2_mix, ARRAY_SIZE(memif_ul_cm0_ch2_mix)),
+ SND_SOC_DAPM_MIXER("UL_CM0_CH3", SND_SOC_NOPM, 0, 0,
+ memif_ul_cm0_ch3_mix, ARRAY_SIZE(memif_ul_cm0_ch3_mix)),
+ SND_SOC_DAPM_MIXER("UL_CM0_CH4", SND_SOC_NOPM, 0, 0,
+ memif_ul_cm0_ch4_mix, ARRAY_SIZE(memif_ul_cm0_ch4_mix)),
+ SND_SOC_DAPM_MIXER("UL_CM0_CH5", SND_SOC_NOPM, 0, 0,
+ memif_ul_cm0_ch5_mix, ARRAY_SIZE(memif_ul_cm0_ch5_mix)),
+ SND_SOC_DAPM_MIXER("UL_CM0_CH6", SND_SOC_NOPM, 0, 0,
+ memif_ul_cm0_ch6_mix, ARRAY_SIZE(memif_ul_cm0_ch6_mix)),
+ SND_SOC_DAPM_MIXER("UL_CM0_CH7", SND_SOC_NOPM, 0, 0,
+ memif_ul_cm0_ch7_mix, ARRAY_SIZE(memif_ul_cm0_ch7_mix)),
+ SND_SOC_DAPM_MIXER("UL_CM0_CH8", SND_SOC_NOPM, 0, 0,
+ memif_ul_cm0_ch8_mix, ARRAY_SIZE(memif_ul_cm0_ch8_mix)),
+ SND_SOC_DAPM_MUX("CM0_UL_MUX", SND_SOC_NOPM, 0, 0,
+ &ul_cm0_mux_control),
+ SND_SOC_DAPM_MIXER("UL_CM1_CH1", SND_SOC_NOPM, 0, 0,
+ memif_ul_cm1_ch1_mix, ARRAY_SIZE(memif_ul_cm1_ch1_mix)),
+ SND_SOC_DAPM_MIXER("UL_CM1_CH2", SND_SOC_NOPM, 0, 0,
+ memif_ul_cm1_ch2_mix, ARRAY_SIZE(memif_ul_cm1_ch2_mix)),
+ SND_SOC_DAPM_MIXER("UL_CM1_CH3", SND_SOC_NOPM, 0, 0,
+ memif_ul_cm1_ch3_mix, ARRAY_SIZE(memif_ul_cm1_ch3_mix)),
+ SND_SOC_DAPM_MIXER("UL_CM1_CH4", SND_SOC_NOPM, 0, 0,
+ memif_ul_cm1_ch4_mix, ARRAY_SIZE(memif_ul_cm1_ch4_mix)),
+ SND_SOC_DAPM_MIXER("UL_CM1_CH5", SND_SOC_NOPM, 0, 0,
+ memif_ul_cm1_ch5_mix, ARRAY_SIZE(memif_ul_cm1_ch5_mix)),
+ SND_SOC_DAPM_MIXER("UL_CM1_CH6", SND_SOC_NOPM, 0, 0,
+ memif_ul_cm1_ch6_mix, ARRAY_SIZE(memif_ul_cm1_ch6_mix)),
+ SND_SOC_DAPM_MIXER("UL_CM1_CH7", SND_SOC_NOPM, 0, 0,
+ memif_ul_cm1_ch7_mix, ARRAY_SIZE(memif_ul_cm1_ch7_mix)),
+ SND_SOC_DAPM_MIXER("UL_CM1_CH8", SND_SOC_NOPM, 0, 0,
+ memif_ul_cm1_ch8_mix, ARRAY_SIZE(memif_ul_cm1_ch8_mix)),
+ SND_SOC_DAPM_MIXER("UL_CM1_CH9", SND_SOC_NOPM, 0, 0,
+ memif_ul_cm1_ch9_mix, ARRAY_SIZE(memif_ul_cm1_ch9_mix)),
+ SND_SOC_DAPM_MIXER("UL_CM1_CH10", SND_SOC_NOPM, 0, 0,
+ memif_ul_cm1_ch10_mix, ARRAY_SIZE(memif_ul_cm1_ch10_mix)),
+ SND_SOC_DAPM_MIXER("UL_CM1_CH11", SND_SOC_NOPM, 0, 0,
+ memif_ul_cm1_ch11_mix, ARRAY_SIZE(memif_ul_cm1_ch11_mix)),
+ SND_SOC_DAPM_MIXER("UL_CM1_CH12", SND_SOC_NOPM, 0, 0,
+ memif_ul_cm1_ch12_mix, ARRAY_SIZE(memif_ul_cm1_ch12_mix)),
+ SND_SOC_DAPM_MIXER("UL_CM1_CH13", SND_SOC_NOPM, 0, 0,
+ memif_ul_cm1_ch13_mix, ARRAY_SIZE(memif_ul_cm1_ch13_mix)),
+ SND_SOC_DAPM_MIXER("UL_CM1_CH14", SND_SOC_NOPM, 0, 0,
+ memif_ul_cm1_ch14_mix, ARRAY_SIZE(memif_ul_cm1_ch14_mix)),
+ SND_SOC_DAPM_MIXER("UL_CM1_CH15", SND_SOC_NOPM, 0, 0,
+ memif_ul_cm1_ch15_mix, ARRAY_SIZE(memif_ul_cm1_ch15_mix)),
+ SND_SOC_DAPM_MIXER("UL_CM1_CH16", SND_SOC_NOPM, 0, 0,
+ memif_ul_cm1_ch16_mix, ARRAY_SIZE(memif_ul_cm1_ch16_mix)),
+ SND_SOC_DAPM_MUX("CM1_UL_MUX", SND_SOC_NOPM, 0, 0,
+ &ul_cm1_mux_control),
+ SND_SOC_DAPM_MIXER("UL_CM2_CH1", SND_SOC_NOPM, 0, 0,
+ memif_ul_cm2_ch1_mix, ARRAY_SIZE(memif_ul_cm2_ch1_mix)),
+ SND_SOC_DAPM_MIXER("UL_CM2_CH2", SND_SOC_NOPM, 0, 0,
+ memif_ul_cm2_ch2_mix, ARRAY_SIZE(memif_ul_cm2_ch2_mix)),
+ SND_SOC_DAPM_MIXER("UL_CM2_CH3", SND_SOC_NOPM, 0, 0,
+ memif_ul_cm2_ch3_mix, ARRAY_SIZE(memif_ul_cm2_ch3_mix)),
+ SND_SOC_DAPM_MIXER("UL_CM2_CH4", SND_SOC_NOPM, 0, 0,
+ memif_ul_cm2_ch4_mix, ARRAY_SIZE(memif_ul_cm2_ch4_mix)),
+ SND_SOC_DAPM_MIXER("UL_CM2_CH5", SND_SOC_NOPM, 0, 0,
+ memif_ul_cm2_ch5_mix, ARRAY_SIZE(memif_ul_cm2_ch5_mix)),
+ SND_SOC_DAPM_MIXER("UL_CM2_CH6", SND_SOC_NOPM, 0, 0,
+ memif_ul_cm2_ch6_mix, ARRAY_SIZE(memif_ul_cm2_ch6_mix)),
+ SND_SOC_DAPM_MIXER("UL_CM2_CH7", SND_SOC_NOPM, 0, 0,
+ memif_ul_cm2_ch7_mix, ARRAY_SIZE(memif_ul_cm2_ch7_mix)),
+ SND_SOC_DAPM_MIXER("UL_CM2_CH8", SND_SOC_NOPM, 0, 0,
+ memif_ul_cm2_ch8_mix, ARRAY_SIZE(memif_ul_cm2_ch8_mix)),
+ SND_SOC_DAPM_MIXER("UL_CM2_CH9", SND_SOC_NOPM, 0, 0,
+ memif_ul_cm2_ch9_mix, ARRAY_SIZE(memif_ul_cm2_ch9_mix)),
+ SND_SOC_DAPM_MIXER("UL_CM2_CH10", SND_SOC_NOPM, 0, 0,
+ memif_ul_cm2_ch10_mix, ARRAY_SIZE(memif_ul_cm2_ch10_mix)),
+ SND_SOC_DAPM_MIXER("UL_CM2_CH11", SND_SOC_NOPM, 0, 0,
+ memif_ul_cm2_ch11_mix, ARRAY_SIZE(memif_ul_cm2_ch11_mix)),
+ SND_SOC_DAPM_MIXER("UL_CM2_CH12", SND_SOC_NOPM, 0, 0,
+ memif_ul_cm2_ch12_mix, ARRAY_SIZE(memif_ul_cm2_ch12_mix)),
+ SND_SOC_DAPM_MIXER("UL_CM2_CH13", SND_SOC_NOPM, 0, 0,
+ memif_ul_cm2_ch13_mix, ARRAY_SIZE(memif_ul_cm2_ch13_mix)),
+ SND_SOC_DAPM_MIXER("UL_CM2_CH14", SND_SOC_NOPM, 0, 0,
+ memif_ul_cm2_ch14_mix, ARRAY_SIZE(memif_ul_cm2_ch14_mix)),
+ SND_SOC_DAPM_MIXER("UL_CM2_CH15", SND_SOC_NOPM, 0, 0,
+ memif_ul_cm2_ch15_mix, ARRAY_SIZE(memif_ul_cm2_ch15_mix)),
+ SND_SOC_DAPM_MIXER("UL_CM2_CH16", SND_SOC_NOPM, 0, 0,
+ memif_ul_cm2_ch16_mix, ARRAY_SIZE(memif_ul_cm2_ch16_mix)),
+ SND_SOC_DAPM_MIXER("UL_CM2_CH17", SND_SOC_NOPM, 0, 0,
+ memif_ul_cm2_ch17_mix, ARRAY_SIZE(memif_ul_cm2_ch17_mix)),
+ SND_SOC_DAPM_MIXER("UL_CM2_CH18", SND_SOC_NOPM, 0, 0,
+ memif_ul_cm2_ch18_mix, ARRAY_SIZE(memif_ul_cm2_ch18_mix)),
+ SND_SOC_DAPM_MIXER("UL_CM2_CH19", SND_SOC_NOPM, 0, 0,
+ memif_ul_cm2_ch19_mix, ARRAY_SIZE(memif_ul_cm2_ch19_mix)),
+ SND_SOC_DAPM_MIXER("UL_CM2_CH20", SND_SOC_NOPM, 0, 0,
+ memif_ul_cm2_ch20_mix, ARRAY_SIZE(memif_ul_cm2_ch20_mix)),
+ SND_SOC_DAPM_MIXER("UL_CM2_CH21", SND_SOC_NOPM, 0, 0,
+ memif_ul_cm2_ch21_mix, ARRAY_SIZE(memif_ul_cm2_ch21_mix)),
+ SND_SOC_DAPM_MIXER("UL_CM2_CH22", SND_SOC_NOPM, 0, 0,
+ memif_ul_cm2_ch22_mix, ARRAY_SIZE(memif_ul_cm2_ch22_mix)),
+ SND_SOC_DAPM_MIXER("UL_CM2_CH23", SND_SOC_NOPM, 0, 0,
+ memif_ul_cm2_ch23_mix, ARRAY_SIZE(memif_ul_cm2_ch23_mix)),
+ SND_SOC_DAPM_MIXER("UL_CM2_CH24", SND_SOC_NOPM, 0, 0,
+ memif_ul_cm2_ch24_mix, ARRAY_SIZE(memif_ul_cm2_ch24_mix)),
+ SND_SOC_DAPM_MIXER("UL_CM2_CH25", SND_SOC_NOPM, 0, 0,
+ memif_ul_cm2_ch25_mix, ARRAY_SIZE(memif_ul_cm2_ch25_mix)),
+ SND_SOC_DAPM_MIXER("UL_CM2_CH26", SND_SOC_NOPM, 0, 0,
+ memif_ul_cm2_ch26_mix, ARRAY_SIZE(memif_ul_cm2_ch26_mix)),
+ SND_SOC_DAPM_MIXER("UL_CM2_CH27", SND_SOC_NOPM, 0, 0,
+ memif_ul_cm2_ch27_mix, ARRAY_SIZE(memif_ul_cm2_ch27_mix)),
+ SND_SOC_DAPM_MIXER("UL_CM2_CH28", SND_SOC_NOPM, 0, 0,
+ memif_ul_cm2_ch28_mix, ARRAY_SIZE(memif_ul_cm2_ch28_mix)),
+ SND_SOC_DAPM_MIXER("UL_CM2_CH29", SND_SOC_NOPM, 0, 0,
+ memif_ul_cm2_ch29_mix, ARRAY_SIZE(memif_ul_cm2_ch29_mix)),
+ SND_SOC_DAPM_MIXER("UL_CM2_CH30", SND_SOC_NOPM, 0, 0,
+ memif_ul_cm2_ch30_mix, ARRAY_SIZE(memif_ul_cm2_ch30_mix)),
+ SND_SOC_DAPM_MIXER("UL_CM2_CH31", SND_SOC_NOPM, 0, 0,
+ memif_ul_cm2_ch31_mix, ARRAY_SIZE(memif_ul_cm2_ch31_mix)),
+ SND_SOC_DAPM_MIXER("UL_CM2_CH32", SND_SOC_NOPM, 0, 0,
+ memif_ul_cm2_ch32_mix, ARRAY_SIZE(memif_ul_cm2_ch32_mix)),
+ SND_SOC_DAPM_MUX("CM2_UL_MUX", SND_SOC_NOPM, 0, 0,
+ &ul_cm2_mux_control),
+
+ SND_SOC_DAPM_SUPPLY("CM0_Enable",
+ AFE_CM0_CON0, AFE_CM0_ON_SFT, 0,
+ ul_cm0_event,
+ SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_PRE_PMD),
+
+ SND_SOC_DAPM_SUPPLY("CM1_Enable",
+ AFE_CM1_CON0, AFE_CM1_ON_SFT, 0,
+ ul_cm1_event,
+ SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_PRE_PMD),
+
+ SND_SOC_DAPM_SUPPLY("CM2_Enable",
+ AFE_CM2_CON0, AFE_CM2_ON_SFT, 0,
+ ul_cm2_event,
+ SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_PRE_PMD),
+
+ SND_SOC_DAPM_MIXER("SOF_DMA_UL0", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("SOF_DMA_UL1", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("SOF_DMA_UL2", SND_SOC_NOPM, 0, 0, NULL, 0),
+};
+
+static const struct snd_soc_dapm_route mt8196_memif_routes[] = {
+ {"UL0", NULL, "UL0_CH1"},
+ {"UL0", NULL, "UL0_CH2"},
+ /* Normal record */
+ {"UL0_CH1", "ADDA_UL_CH1", "ADDA_UL_Mux"},
+ {"UL0_CH2", "ADDA_UL_CH2", "ADDA_UL_Mux"},
+ /* SOF Uplink */
+ {"SOF_DMA_UL0", NULL, "UL0_CH1"},
+ {"SOF_DMA_UL0", NULL, "UL0_CH2"},
+
+ {"UL1", NULL, "UL1_CH1"},
+ {"UL1", NULL, "UL1_CH2"},
+ {"UL1_CH1", "I2SIN4_CH1", "I2SIN4"},
+ {"UL1_CH2", "I2SIN4_CH2", "I2SIN4"},
+ {"UL1_CH1", "I2SIN6_CH1", "I2SIN6"},
+ {"UL1_CH2", "I2SIN6_CH2", "I2SIN6"},
+ /* SOF Uplink */
+ {"SOF_DMA_UL1", NULL, "UL1_CH1"},
+ {"SOF_DMA_UL1", NULL, "UL1_CH2"},
+
+ {"UL2", NULL, "UL2_CH1"},
+ {"UL2", NULL, "UL2_CH2"},
+ {"UL2_CH1", "ADDA_UL_CH3", "ADDA_CH34_UL_Mux"},
+ {"UL2_CH2", "ADDA_UL_CH4", "ADDA_CH34_UL_Mux"},
+ /* SOF Uplink */
+ {"SOF_DMA_UL2", NULL, "UL2_CH1"},
+ {"SOF_DMA_UL2", NULL, "UL2_CH2"},
+
+ {"UL3", NULL, "UL3_CH1"},
+ {"UL3", NULL, "UL3_CH2"},
+ {"UL3_CH1", "I2SIN0_CH1", "I2SIN0"},
+ {"UL3_CH2", "I2SIN0_CH2", "I2SIN0"},
+ {"UL3_CH1", "I2SIN1_CH1", "I2SIN1"},
+ {"UL3_CH2", "I2SIN1_CH2", "I2SIN1"},
+ {"UL3_CH1", "I2SIN3_CH1", "I2SIN3"},
+ {"UL3_CH2", "I2SIN3_CH2", "I2SIN3"},
+ {"UL3_CH1", "I2SIN4_CH1", "I2SIN4"},
+ {"UL3_CH2", "I2SIN4_CH2", "I2SIN4"},
+
+ {"UL4", NULL, "UL4_CH1"},
+ {"UL4", NULL, "UL4_CH2"},
+ {"UL4_CH1", "ADDA_UL_CH1", "ADDA_UL_Mux"},
+ {"UL4_CH2", "ADDA_UL_CH2", "ADDA_UL_Mux"},
+ {"UL4_CH1", "I2SIN0_CH1", "I2SIN0"},
+ {"UL4_CH2", "I2SIN0_CH2", "I2SIN0"},
+
+ {"UL5", NULL, "UL5_CH1"},
+ {"UL5", NULL, "UL5_CH2"},
+ {"UL5_CH1", "I2SIN3_CH1", "I2SIN3"},
+ {"UL5_CH2", "I2SIN3_CH2", "I2SIN3"},
+
+ {"UL6", NULL, "UL6_CH1"},
+ {"UL6", NULL, "UL6_CH2"},
+ {"UL6_CH1", "ADDA_UL_CH1", "ADDA_UL_Mux"},
+ {"UL6_CH2", "ADDA_UL_CH2", "ADDA_UL_Mux"},
+
+ {"UL7", NULL, "UL7_CH1"},
+ {"UL7", NULL, "UL7_CH2"},
+ {"UL7_CH1", "ADDA_UL_CH1", "ADDA_UL_Mux"},
+ {"UL7_CH2", "ADDA_UL_CH2", "ADDA_UL_Mux"},
+
+ {"UL8", NULL, "CM0_UL_MUX"},
+ {"CM0_UL_MUX", "UL8_2CH_PATH", "UL8_CH1"},
+ {"CM0_UL_MUX", "UL8_2CH_PATH", "UL8_CH2"},
+ {"CM0_UL_MUX", "CM0_8CH_PATH", "UL_CM0_CH1"},
+ {"CM0_UL_MUX", "CM0_8CH_PATH", "UL_CM0_CH2"},
+ {"CM0_UL_MUX", "CM0_8CH_PATH", "UL_CM0_CH3"},
+ {"CM0_UL_MUX", "CM0_8CH_PATH", "UL_CM0_CH4"},
+ {"CM0_UL_MUX", "CM0_8CH_PATH", "UL_CM0_CH5"},
+ {"CM0_UL_MUX", "CM0_8CH_PATH", "UL_CM0_CH6"},
+ {"CM0_UL_MUX", "CM0_8CH_PATH", "UL_CM0_CH7"},
+ {"CM0_UL_MUX", "CM0_8CH_PATH", "UL_CM0_CH8"},
+ {"UL_CM0_CH1", NULL, "CM0_Enable"},
+ {"UL_CM0_CH2", NULL, "CM0_Enable"},
+ {"UL_CM0_CH3", NULL, "CM0_Enable"},
+ {"UL_CM0_CH4", NULL, "CM0_Enable"},
+ {"UL_CM0_CH5", NULL, "CM0_Enable"},
+ {"UL_CM0_CH6", NULL, "CM0_Enable"},
+ {"UL_CM0_CH7", NULL, "CM0_Enable"},
+ {"UL_CM0_CH8", NULL, "CM0_Enable"},
+
+ /* UL9 */
+ {"UL9", NULL, "CM1_UL_MUX"},
+ {"CM1_UL_MUX", "UL9_2CH_PATH", "UL9_CH1"},
+ {"CM1_UL_MUX", "UL9_2CH_PATH", "UL9_CH2"},
+ {"CM1_UL_MUX", "CM1_16CH_PATH", "UL_CM1_CH1"},
+ {"CM1_UL_MUX", "CM1_16CH_PATH", "UL_CM1_CH2"},
+ {"CM1_UL_MUX", "CM1_16CH_PATH", "UL_CM1_CH3"},
+ {"CM1_UL_MUX", "CM1_16CH_PATH", "UL_CM1_CH4"},
+ {"CM1_UL_MUX", "CM1_16CH_PATH", "UL_CM1_CH5"},
+ {"CM1_UL_MUX", "CM1_16CH_PATH", "UL_CM1_CH6"},
+ {"CM1_UL_MUX", "CM1_16CH_PATH", "UL_CM1_CH7"},
+ {"CM1_UL_MUX", "CM1_16CH_PATH", "UL_CM1_CH8"},
+ {"CM1_UL_MUX", "CM1_16CH_PATH", "UL_CM1_CH9"},
+ {"CM1_UL_MUX", "CM1_16CH_PATH", "UL_CM1_CH10"},
+ {"CM1_UL_MUX", "CM1_16CH_PATH", "UL_CM1_CH11"},
+ {"CM1_UL_MUX", "CM1_16CH_PATH", "UL_CM1_CH12"},
+ {"CM1_UL_MUX", "CM1_16CH_PATH", "UL_CM1_CH13"},
+ {"CM1_UL_MUX", "CM1_16CH_PATH", "UL_CM1_CH14"},
+ {"CM1_UL_MUX", "CM1_16CH_PATH", "UL_CM1_CH15"},
+ {"CM1_UL_MUX", "CM1_16CH_PATH", "UL_CM1_CH16"},
+ {"UL_CM1_CH1", NULL, "CM1_Enable"},
+ {"UL_CM1_CH2", NULL, "CM1_Enable"},
+ {"UL_CM1_CH3", NULL, "CM1_Enable"},
+ {"UL_CM1_CH4", NULL, "CM1_Enable"},
+ {"UL_CM1_CH5", NULL, "CM1_Enable"},
+ {"UL_CM1_CH6", NULL, "CM1_Enable"},
+ {"UL_CM1_CH7", NULL, "CM1_Enable"},
+ {"UL_CM1_CH8", NULL, "CM1_Enable"},
+ {"UL_CM1_CH9", NULL, "CM1_Enable"},
+ {"UL_CM1_CH10", NULL, "CM1_Enable"},
+ {"UL_CM1_CH11", NULL, "CM1_Enable"},
+ {"UL_CM1_CH12", NULL, "CM1_Enable"},
+ {"UL_CM1_CH13", NULL, "CM1_Enable"},
+ {"UL_CM1_CH14", NULL, "CM1_Enable"},
+ {"UL_CM1_CH15", NULL, "CM1_Enable"},
+ {"UL_CM1_CH16", NULL, "CM1_Enable"},
+ {"UL9_CH1", "ADDA_UL_CH1", "ADDA_UL_Mux"},
+ {"UL9_CH2", "ADDA_UL_CH2", "ADDA_UL_Mux"},
+
+ {"UL10", NULL, "CM2_UL_MUX"},
+ {"CM2_UL_MUX", "UL10_2CH_PATH", "UL10_CH1"},
+ {"CM2_UL_MUX", "UL10_2CH_PATH", "UL10_CH2"},
+ {"CM2_UL_MUX", "CM2_32CH_PATH", "UL_CM2_CH1"},
+ {"CM2_UL_MUX", "CM2_32CH_PATH", "UL_CM2_CH2"},
+ {"CM2_UL_MUX", "CM2_32CH_PATH", "UL_CM2_CH3"},
+ {"CM2_UL_MUX", "CM2_32CH_PATH", "UL_CM2_CH4"},
+ {"CM2_UL_MUX", "CM2_32CH_PATH", "UL_CM2_CH5"},
+ {"CM2_UL_MUX", "CM2_32CH_PATH", "UL_CM2_CH6"},
+ {"CM2_UL_MUX", "CM2_32CH_PATH", "UL_CM2_CH7"},
+ {"CM2_UL_MUX", "CM2_32CH_PATH", "UL_CM2_CH8"},
+ {"CM2_UL_MUX", "CM2_32CH_PATH", "UL_CM2_CH9"},
+ {"CM2_UL_MUX", "CM2_32CH_PATH", "UL_CM2_CH10"},
+ {"CM2_UL_MUX", "CM2_32CH_PATH", "UL_CM2_CH11"},
+ {"CM2_UL_MUX", "CM2_32CH_PATH", "UL_CM2_CH12"},
+ {"CM2_UL_MUX", "CM2_32CH_PATH", "UL_CM2_CH13"},
+ {"CM2_UL_MUX", "CM2_32CH_PATH", "UL_CM2_CH14"},
+ {"CM2_UL_MUX", "CM2_32CH_PATH", "UL_CM2_CH15"},
+ {"CM2_UL_MUX", "CM2_32CH_PATH", "UL_CM2_CH16"},
+ {"CM2_UL_MUX", "CM2_32CH_PATH", "UL_CM2_CH17"},
+ {"CM2_UL_MUX", "CM2_32CH_PATH", "UL_CM2_CH18"},
+ {"CM2_UL_MUX", "CM2_32CH_PATH", "UL_CM2_CH19"},
+ {"CM2_UL_MUX", "CM2_32CH_PATH", "UL_CM2_CH20"},
+ {"CM2_UL_MUX", "CM2_32CH_PATH", "UL_CM2_CH21"},
+ {"CM2_UL_MUX", "CM2_32CH_PATH", "UL_CM2_CH22"},
+ {"CM2_UL_MUX", "CM2_32CH_PATH", "UL_CM2_CH23"},
+ {"CM2_UL_MUX", "CM2_32CH_PATH", "UL_CM2_CH24"},
+ {"CM2_UL_MUX", "CM2_32CH_PATH", "UL_CM2_CH25"},
+ {"CM2_UL_MUX", "CM2_32CH_PATH", "UL_CM2_CH26"},
+ {"CM2_UL_MUX", "CM2_32CH_PATH", "UL_CM2_CH27"},
+ {"CM2_UL_MUX", "CM2_32CH_PATH", "UL_CM2_CH28"},
+ {"CM2_UL_MUX", "CM2_32CH_PATH", "UL_CM2_CH29"},
+ {"CM2_UL_MUX", "CM2_32CH_PATH", "UL_CM2_CH30"},
+ {"CM2_UL_MUX", "CM2_32CH_PATH", "UL_CM2_CH31"},
+ {"CM2_UL_MUX", "CM2_32CH_PATH", "UL_CM2_CH32"},
+ {"UL_CM2_CH1", NULL, "CM2_Enable"},
+ {"UL_CM2_CH2", NULL, "CM2_Enable"},
+ {"UL_CM2_CH3", NULL, "CM2_Enable"},
+ {"UL_CM2_CH4", NULL, "CM2_Enable"},
+ {"UL_CM2_CH5", NULL, "CM2_Enable"},
+ {"UL_CM2_CH6", NULL, "CM2_Enable"},
+ {"UL_CM2_CH7", NULL, "CM2_Enable"},
+ {"UL_CM2_CH8", NULL, "CM2_Enable"},
+ {"UL_CM2_CH9", NULL, "CM2_Enable"},
+ {"UL_CM2_CH10", NULL, "CM2_Enable"},
+ {"UL_CM2_CH11", NULL, "CM2_Enable"},
+ {"UL_CM2_CH12", NULL, "CM2_Enable"},
+ {"UL_CM2_CH13", NULL, "CM2_Enable"},
+ {"UL_CM2_CH14", NULL, "CM2_Enable"},
+ {"UL_CM2_CH15", NULL, "CM2_Enable"},
+ {"UL_CM2_CH16", NULL, "CM2_Enable"},
+ {"UL_CM2_CH17", NULL, "CM2_Enable"},
+ {"UL_CM2_CH18", NULL, "CM2_Enable"},
+ {"UL_CM2_CH19", NULL, "CM2_Enable"},
+ {"UL_CM2_CH20", NULL, "CM2_Enable"},
+ {"UL_CM2_CH21", NULL, "CM2_Enable"},
+ {"UL_CM2_CH22", NULL, "CM2_Enable"},
+ {"UL_CM2_CH23", NULL, "CM2_Enable"},
+ {"UL_CM2_CH24", NULL, "CM2_Enable"},
+ {"UL_CM2_CH25", NULL, "CM2_Enable"},
+ {"UL_CM2_CH26", NULL, "CM2_Enable"},
+ {"UL_CM2_CH27", NULL, "CM2_Enable"},
+ {"UL_CM2_CH28", NULL, "CM2_Enable"},
+ {"UL_CM2_CH29", NULL, "CM2_Enable"},
+ {"UL_CM2_CH30", NULL, "CM2_Enable"},
+ {"UL_CM2_CH31", NULL, "CM2_Enable"},
+ {"UL_CM2_CH32", NULL, "CM2_Enable"},
+ {"UL10_CH1", "ADDA_UL_CH1", "ADDA_UL_Mux"},
+ {"UL10_CH2", "ADDA_UL_CH2", "ADDA_UL_Mux"},
+
+ {"UL24", NULL, "UL24_CH1"},
+ {"UL24", NULL, "UL24_CH2"},
+ {"UL24_CH1", "I2SIN6_CH1", "I2SIN6"},
+ {"UL24_CH2", "I2SIN6_CH2", "I2SIN6"},
+ {"UL24_CH1", "I2SIN0_CH1", "I2SIN0"},
+ {"UL24_CH2", "I2SIN0_CH2", "I2SIN0"},
+
+ {"UL25", NULL, "UL25_CH1"},
+ {"UL25", NULL, "UL25_CH2"},
+ {"UL25_CH1", "I2SIN6_CH1", "I2SIN6"},
+ {"UL25_CH2", "I2SIN6_CH2", "I2SIN6"},
+ {"UL25_CH1", "I2SIN0_CH1", "I2SIN0"},
+ {"UL25_CH2", "I2SIN0_CH2", "I2SIN0"},
+
+ {"UL26", NULL, "UL26_CH1"},
+ {"UL26", NULL, "UL26_CH2"},
+ {"UL26_CH1", "I2SIN6_CH1", "I2SIN6"},
+ {"UL26_CH2", "I2SIN6_CH2", "I2SIN6"},
+ {"UL26_CH1", "I2SIN0_CH1", "I2SIN0"},
+ {"UL26_CH2", "I2SIN0_CH2", "I2SIN0"},
+
+ {"UL_CM0", NULL, "UL_CM0_CH1"},
+ {"UL_CM0", NULL, "UL_CM0_CH2"},
+ {"UL_CM0", NULL, "UL_CM0_CH3"},
+ {"UL_CM0", NULL, "UL_CM0_CH4"},
+ {"UL_CM0", NULL, "UL_CM0_CH5"},
+ {"UL_CM0", NULL, "UL_CM0_CH6"},
+ {"UL_CM0", NULL, "UL_CM0_CH7"},
+ {"UL_CM0", NULL, "UL_CM0_CH8"},
+ {"UL_CM0_CH1", "ADDA_UL_CH1", "ADDA_UL_Mux"},
+ {"UL_CM0_CH2", "ADDA_UL_CH2", "ADDA_UL_Mux"},
+ {"UL_CM0_CH3", "ADDA_UL_CH3", "ADDA_CH34_UL_Mux"},
+ {"UL_CM0_CH4", "ADDA_UL_CH4", "ADDA_CH34_UL_Mux"},
+
+ {"UL_CM1", NULL, "UL_CM1_CH1"},
+ {"UL_CM1", NULL, "UL_CM1_CH2"},
+ {"UL_CM1", NULL, "UL_CM1_CH3"},
+ {"UL_CM1", NULL, "UL_CM1_CH4"},
+ {"UL_CM1", NULL, "UL_CM1_CH5"},
+ {"UL_CM1", NULL, "UL_CM1_CH6"},
+ {"UL_CM1", NULL, "UL_CM1_CH7"},
+ {"UL_CM1", NULL, "UL_CM1_CH8"},
+ {"UL_CM1", NULL, "UL_CM1_CH9"},
+ {"UL_CM1", NULL, "UL_CM1_CH10"},
+ {"UL_CM1", NULL, "UL_CM1_CH11"},
+ {"UL_CM1", NULL, "UL_CM1_CH12"},
+ {"UL_CM1", NULL, "UL_CM1_CH13"},
+ {"UL_CM1", NULL, "UL_CM1_CH14"},
+ {"UL_CM1", NULL, "UL_CM1_CH15"},
+ {"UL_CM1", NULL, "UL_CM1_CH16"},
+ {"UL_CM1_CH1", "ADDA_UL_CH1", "ADDA_UL_Mux"},
+ {"UL_CM1_CH2", "ADDA_UL_CH2", "ADDA_UL_Mux"},
+ {"UL_CM1_CH3", "ADDA_UL_CH3", "ADDA_CH34_UL_Mux"},
+ {"UL_CM1_CH4", "ADDA_UL_CH4", "ADDA_CH34_UL_Mux"},
+
+ {"UL_CM2", NULL, "UL_CM2_CH1"},
+ {"UL_CM2", NULL, "UL_CM2_CH2"},
+ {"UL_CM2", NULL, "UL_CM2_CH3"},
+ {"UL_CM2", NULL, "UL_CM2_CH4"},
+ {"UL_CM2", NULL, "UL_CM2_CH5"},
+ {"UL_CM2", NULL, "UL_CM2_CH6"},
+ {"UL_CM2", NULL, "UL_CM2_CH7"},
+ {"UL_CM2", NULL, "UL_CM2_CH8"},
+ {"UL_CM2", NULL, "UL_CM2_CH9"},
+ {"UL_CM2", NULL, "UL_CM2_CH10"},
+ {"UL_CM2", NULL, "UL_CM2_CH11"},
+ {"UL_CM2", NULL, "UL_CM2_CH12"},
+ {"UL_CM2", NULL, "UL_CM2_CH13"},
+ {"UL_CM2", NULL, "UL_CM2_CH14"},
+ {"UL_CM2", NULL, "UL_CM2_CH15"},
+ {"UL_CM2", NULL, "UL_CM2_CH16"},
+ {"UL_CM2", NULL, "UL_CM2_CH17"},
+ {"UL_CM2", NULL, "UL_CM2_CH18"},
+ {"UL_CM2", NULL, "UL_CM2_CH19"},
+ {"UL_CM2", NULL, "UL_CM2_CH20"},
+ {"UL_CM2", NULL, "UL_CM2_CH21"},
+ {"UL_CM2", NULL, "UL_CM2_CH22"},
+ {"UL_CM2", NULL, "UL_CM2_CH23"},
+ {"UL_CM2", NULL, "UL_CM2_CH24"},
+ {"UL_CM2", NULL, "UL_CM2_CH25"},
+ {"UL_CM2", NULL, "UL_CM2_CH26"},
+ {"UL_CM2", NULL, "UL_CM2_CH27"},
+ {"UL_CM2", NULL, "UL_CM2_CH28"},
+ {"UL_CM2", NULL, "UL_CM2_CH29"},
+ {"UL_CM2", NULL, "UL_CM2_CH30"},
+ {"UL_CM2", NULL, "UL_CM2_CH31"},
+ {"UL_CM2", NULL, "UL_CM2_CH32"},
+ {"UL_CM2_CH1", "ADDA_UL_CH1", "ADDA_UL_Mux"},
+ {"UL_CM2_CH2", "ADDA_UL_CH2", "ADDA_UL_Mux"},
+ {"UL_CM2_CH3", "ADDA_UL_CH3", "ADDA_CH34_UL_Mux"},
+ {"UL_CM2_CH4", "ADDA_UL_CH4", "ADDA_CH34_UL_Mux"},
+};
+
+#define MT8196_DL_MEMIF(_id) \
+ [MT8196_MEMIF_##_id] = { \
+ .name = #_id, \
+ .id = MT8196_MEMIF_##_id, \
+ .reg_ofs_base = AFE_##_id##_BASE, \
+ .reg_ofs_cur = AFE_##_id##_CUR, \
+ .reg_ofs_end = AFE_##_id##_END, \
+ .reg_ofs_base_msb = AFE_##_id##_BASE_MSB, \
+ .reg_ofs_cur_msb = AFE_##_id##_CUR_MSB, \
+ .reg_ofs_end_msb = AFE_##_id##_END_MSB, \
+ .fs_reg = AFE_##_id##_CON0, \
+ .fs_shift = _id##_SEL_FS_SFT, \
+ .fs_maskbit = _id##_SEL_FS_MASK, \
+ .mono_reg = AFE_##_id##_CON0, \
+ .mono_shift = _id##_MONO_SFT, \
+ .enable_reg = AFE_##_id##_CON0, \
+ .enable_shift = _id##_ON_SFT, \
+ .hd_reg = AFE_##_id##_CON0, \
+ .hd_shift = _id##_HD_MODE_SFT, \
+ .hd_align_reg = AFE_##_id##_CON0, \
+ .hd_align_mshift = _id##_HALIGN_SFT, \
+ .agent_disable_reg = -1, \
+ .agent_disable_shift = -1, \
+ .msb_reg = -1, \
+ .msb_shift = -1, \
+ .pbuf_reg = AFE_##_id##_CON0, \
+ .pbuf_mask = _id##_PBUF_SIZE_MASK, \
+ .pbuf_shift = _id##_PBUF_SIZE_SFT, \
+ .minlen_reg = AFE_##_id##_CON0, \
+ .minlen_mask = _id##_MINLEN_MASK, \
+ .minlen_shift = _id##_MINLEN_SFT, \
+}
+
+#define MT8196_MULTI_DL_MEMIF(_id) \
+ [MT8196_MEMIF_##_id] = { \
+ .name = #_id, \
+ .id = MT8196_MEMIF_##_id, \
+ .reg_ofs_base = AFE_##_id##_BASE, \
+ .reg_ofs_cur = AFE_##_id##_CUR, \
+ .reg_ofs_end = AFE_##_id##_END, \
+ .reg_ofs_base_msb = AFE_##_id##_BASE_MSB, \
+ .reg_ofs_cur_msb = AFE_##_id##_CUR_MSB, \
+ .reg_ofs_end_msb = AFE_##_id##_END_MSB, \
+ .fs_reg = AFE_##_id##_CON0, \
+ .fs_shift = _id##_SEL_FS_SFT, \
+ .fs_maskbit = _id##_SEL_FS_MASK, \
+ .mono_reg = -1, \
+ .mono_shift = -1, \
+ .enable_reg = AFE_##_id##_CON0, \
+ .enable_shift = _id##_ON_SFT, \
+ .hd_reg = AFE_##_id##_CON0, \
+ .hd_shift = _id##_HD_MODE_SFT, \
+ .hd_align_reg = AFE_##_id##_CON0, \
+ .hd_align_mshift = _id##_HALIGN_SFT, \
+ .agent_disable_reg = -1, \
+ .agent_disable_shift = -1, \
+ .msb_reg = -1, \
+ .msb_shift = -1, \
+ .pbuf_reg = AFE_##_id##_CON0, \
+ .pbuf_mask = _id##_PBUF_SIZE_MASK, \
+ .pbuf_shift = _id##_PBUF_SIZE_SFT, \
+ .minlen_reg = AFE_##_id##_CON0, \
+ .minlen_mask = _id##_MINLEN_MASK, \
+ .minlen_shift = _id##_MINLEN_SFT, \
+ .ch_num_reg = AFE_##_id##_CON0, \
+ .ch_num_maskbit = _id##_NUM_MASK, \
+ .ch_num_shift = _id##_NUM_SFT, \
+}
+
+#define MT8196_UL_MEMIF(_id, _fs_shift, _fs_maskbit, _mono_shift) \
+ [MT8196_MEMIF_##_id] = { \
+ .name = #_id, \
+ .id = MT8196_MEMIF_##_id, \
+ .reg_ofs_base = AFE_##_id##_BASE, \
+ .reg_ofs_cur = AFE_##_id##_CUR, \
+ .reg_ofs_end = AFE_##_id##_END, \
+ .reg_ofs_base_msb = AFE_##_id##_BASE_MSB, \
+ .reg_ofs_cur_msb = AFE_##_id##_CUR_MSB, \
+ .reg_ofs_end_msb = AFE_##_id##_END_MSB, \
+ .fs_reg = AFE_##_id##_CON0, \
+ .fs_shift = _fs_shift, \
+ .fs_maskbit = _fs_maskbit, \
+ .mono_reg = AFE_##_id##_CON0, \
+ .mono_shift = _mono_shift, \
+ .enable_reg = AFE_##_id##_CON0, \
+ .enable_shift = _id##_ON_SFT, \
+ .hd_reg = AFE_##_id##_CON0, \
+ .hd_shift = _id##_HD_MODE_SFT, \
+ .hd_align_reg = AFE_##_id##_CON0, \
+ .hd_align_mshift = _id##_HALIGN_SFT, \
+ .agent_disable_reg = -1, \
+ .agent_disable_shift = -1, \
+ .msb_reg = -1, \
+ .msb_shift = -1, \
+ }
+
+/* For convenience with macros: missing register fields */
+#define HDMI_SEL_FS_SFT -1
+#define HDMI_SEL_FS_MASK -1
+
+/* For convenience with macros: register name differences */
+#define AFE_HDMI_BASE AFE_HDMI_OUT_BASE
+#define AFE_HDMI_CUR AFE_HDMI_OUT_CUR
+#define AFE_HDMI_END AFE_HDMI_OUT_END
+#define AFE_HDMI_BASE_MSB AFE_HDMI_OUT_BASE_MSB
+#define AFE_HDMI_CUR_MSB AFE_HDMI_OUT_CUR_MSB
+#define AFE_HDMI_END_MSB AFE_HDMI_OUT_END_MSB
+#define AFE_HDMI_CON0 AFE_HDMI_OUT_CON0
+#define HDMI_ON_SFT HDMI_OUT_ON_SFT
+#define HDMI_HD_MODE_SFT HDMI_OUT_HD_MODE_SFT
+#define HDMI_HALIGN_SFT HDMI_OUT_HALIGN_SFT
+#define HDMI_PBUF_SIZE_MASK HDMI_OUT_PBUF_SIZE_MASK
+#define HDMI_PBUF_SIZE_SFT HDMI_OUT_PBUF_SIZE_SFT
+#define HDMI_MINLEN_MASK HDMI_OUT_MINLEN_MASK
+#define HDMI_MINLEN_SFT HDMI_OUT_MINLEN_SFT
+#define HDMI_NUM_MASK HDMI_CH_NUM_MASK
+#define HDMI_NUM_SFT HDMI_CH_NUM_SFT
+
+static const struct mtk_base_memif_data memif_data[MT8196_MEMIF_NUM] = {
+ MT8196_DL_MEMIF(DL0),
+ MT8196_DL_MEMIF(DL1),
+ MT8196_DL_MEMIF(DL2),
+ MT8196_DL_MEMIF(DL3),
+ MT8196_DL_MEMIF(DL4),
+ MT8196_DL_MEMIF(DL5),
+ MT8196_DL_MEMIF(DL6),
+ MT8196_DL_MEMIF(DL7),
+ MT8196_DL_MEMIF(DL8),
+ MT8196_DL_MEMIF(DL23),
+ MT8196_DL_MEMIF(DL24),
+ MT8196_DL_MEMIF(DL25),
+ MT8196_DL_MEMIF(DL26),
+ MT8196_MULTI_DL_MEMIF(DL_4CH),
+ MT8196_MULTI_DL_MEMIF(DL_24CH),
+ MT8196_MULTI_DL_MEMIF(HDMI),
+ MT8196_UL_MEMIF(VUL0, VUL0_SEL_FS_SFT, VUL0_SEL_FS_MASK, VUL0_MONO_SFT),
+ MT8196_UL_MEMIF(VUL1, VUL1_SEL_FS_SFT, VUL1_SEL_FS_MASK, VUL1_MONO_SFT),
+ MT8196_UL_MEMIF(VUL2, VUL2_SEL_FS_SFT, VUL2_SEL_FS_MASK, VUL2_MONO_SFT),
+ MT8196_UL_MEMIF(VUL3, VUL3_SEL_FS_SFT, VUL3_SEL_FS_MASK, VUL3_MONO_SFT),
+ MT8196_UL_MEMIF(VUL4, VUL4_SEL_FS_SFT, VUL4_SEL_FS_MASK, VUL4_MONO_SFT),
+ MT8196_UL_MEMIF(VUL5, VUL5_SEL_FS_SFT, VUL5_SEL_FS_MASK, VUL5_MONO_SFT),
+ MT8196_UL_MEMIF(VUL6, VUL6_SEL_FS_SFT, VUL6_SEL_FS_MASK, VUL6_MONO_SFT),
+ MT8196_UL_MEMIF(VUL7, VUL7_SEL_FS_SFT, VUL7_SEL_FS_MASK, VUL7_MONO_SFT),
+ MT8196_UL_MEMIF(VUL8, VUL8_SEL_FS_SFT, VUL8_SEL_FS_MASK, VUL8_MONO_SFT),
+ MT8196_UL_MEMIF(VUL9, VUL9_SEL_FS_SFT, VUL9_SEL_FS_MASK, VUL9_MONO_SFT),
+ MT8196_UL_MEMIF(VUL10, VUL10_SEL_FS_SFT, VUL10_SEL_FS_MASK, VUL10_MONO_SFT),
+ MT8196_UL_MEMIF(VUL24, VUL24_SEL_FS_SFT, VUL24_SEL_FS_MASK, VUL24_MONO_SFT),
+ MT8196_UL_MEMIF(VUL25, VUL25_SEL_FS_SFT, VUL25_SEL_FS_MASK, VUL25_MONO_SFT),
+ MT8196_UL_MEMIF(VUL26, VUL26_SEL_FS_SFT, VUL26_SEL_FS_MASK, VUL26_MONO_SFT),
+ MT8196_UL_MEMIF(VUL_CM0, -1, -1, -1),
+ MT8196_UL_MEMIF(VUL_CM1, -1, -1, -1),
+ MT8196_UL_MEMIF(VUL_CM2, -1, -1, -1),
+ MT8196_UL_MEMIF(ETDM_IN0, REG_FS_TIMING_SEL_SFT, REG_FS_TIMING_SEL_MASK, -1),
+ MT8196_UL_MEMIF(ETDM_IN1, REG_FS_TIMING_SEL_SFT, REG_FS_TIMING_SEL_MASK, -1),
+ MT8196_UL_MEMIF(ETDM_IN2, REG_FS_TIMING_SEL_SFT, REG_FS_TIMING_SEL_MASK, -1),
+ MT8196_UL_MEMIF(ETDM_IN3, REG_FS_TIMING_SEL_SFT, REG_FS_TIMING_SEL_MASK, -1),
+ MT8196_UL_MEMIF(ETDM_IN4, REG_FS_TIMING_SEL_SFT, REG_FS_TIMING_SEL_MASK, -1),
+ MT8196_UL_MEMIF(ETDM_IN6, REG_FS_TIMING_SEL_SFT, REG_FS_TIMING_SEL_MASK, -1),
+};
+
+#define MT8183_AFE_IRQ_BASE(_id, _fs_reg, _fs_shift, _fs_maskbit) \
+ [MT8183_IRQ_##_id] = { \
+ .id = MT8183_IRQ_##_id, \
+ .irq_cnt_reg = AFE_IRQ_MCU_CNT##_id, \
+ .irq_cnt_shift = 0, \
+ .irq_cnt_maskbit = 0x3ffff, \
+ .irq_fs_reg = _fs_reg, \
+ .irq_fs_shift = _fs_shift, \
+ .irq_fs_maskbit = _fs_maskbit, \
+ .irq_en_reg = AFE_IRQ_MCU_CON0, \
+ .irq_en_shift = IRQ##_id##_MCU_ON_SFT, \
+ .irq_clr_reg = AFE_IRQ_MCU_CLR, \
+ .irq_clr_shift = IRQ##_id##_MCU_CLR_SFT, \
+ }
+
+#define MT8196_AFE_IRQ(_id) \
+ [MT8196_IRQ_##_id] = { \
+ .id = MT8196_IRQ_##_id, \
+ .irq_cnt_reg = AFE_IRQ##_id##_MCU_CFG1, \
+ .irq_cnt_shift = AFE_IRQ_CNT_SHIFT, \
+ .irq_cnt_maskbit = AFE_IRQ_CNT_MASK, \
+ .irq_fs_reg = AFE_IRQ##_id##_MCU_CFG0, \
+ .irq_fs_shift = AFE_IRQ##_id##_MCU_FS_SFT, \
+ .irq_fs_maskbit = AFE_IRQ##_id##_MCU_FS_MASK, \
+ .irq_en_reg = AFE_IRQ##_id##_MCU_CFG0, \
+ .irq_en_shift = AFE_IRQ##_id##_MCU_ON_SFT, \
+ .irq_clr_reg = AFE_IRQ##_id##_MCU_CFG1, \
+ .irq_clr_shift = AFE_IRQ##_id##_CLR_CFG_SFT, \
+ }
+
+#define MT8196_AFE_TDM_IRQ(_id) \
+ [MT8196_IRQ_##_id] = { \
+ .id = MT8196_CUS_IRQ_TDM, \
+ .irq_cnt_reg = AFE_CUSTOM_IRQ0_MCU_CFG1, \
+ .irq_cnt_shift = AFE_CUSTOM_IRQ0_MCU_CNT_SFT, \
+ .irq_cnt_maskbit = AFE_CUSTOM_IRQ0_MCU_CNT_MASK, \
+ .irq_fs_reg = -1, \
+ .irq_fs_shift = -1, \
+ .irq_fs_maskbit = -1, \
+ .irq_en_reg = AFE_CUSTOM_IRQ0_MCU_CFG0, \
+ .irq_en_shift = AFE_CUSTOM_IRQ0_MCU_ON_SFT, \
+ .irq_clr_reg = AFE_CUSTOM_IRQ0_MCU_CFG1, \
+ .irq_clr_shift = AFE_CUSTOM_IRQ0_CLR_CFG_SFT, \
+ }
+
+static const struct mtk_base_irq_data irq_data[MT8196_IRQ_NUM] = {
+ MT8196_AFE_IRQ(0),
+ MT8196_AFE_IRQ(1),
+ MT8196_AFE_IRQ(2),
+ MT8196_AFE_IRQ(3),
+ MT8196_AFE_IRQ(4),
+ MT8196_AFE_IRQ(5),
+ MT8196_AFE_IRQ(6),
+ MT8196_AFE_IRQ(7),
+ MT8196_AFE_IRQ(8),
+ MT8196_AFE_IRQ(9),
+ MT8196_AFE_IRQ(10),
+ MT8196_AFE_IRQ(11),
+ MT8196_AFE_IRQ(12),
+ MT8196_AFE_IRQ(13),
+ MT8196_AFE_IRQ(14),
+ MT8196_AFE_IRQ(15),
+ MT8196_AFE_IRQ(16),
+ MT8196_AFE_IRQ(17),
+ MT8196_AFE_IRQ(18),
+ MT8196_AFE_IRQ(19),
+ MT8196_AFE_IRQ(20),
+ MT8196_AFE_IRQ(21),
+ MT8196_AFE_IRQ(22),
+ MT8196_AFE_IRQ(23),
+ MT8196_AFE_IRQ(24),
+ MT8196_AFE_IRQ(25),
+ MT8196_AFE_IRQ(26),
+ MT8196_AFE_TDM_IRQ(31),
+};
+
+static const int memif_irq_usage[MT8196_MEMIF_NUM] = {
+ /* TODO: verify each memif & irq */
+ [MT8196_MEMIF_DL0] = MT8196_IRQ_0,
+ [MT8196_MEMIF_DL1] = MT8196_IRQ_1,
+ [MT8196_MEMIF_DL2] = MT8196_IRQ_2,
+ [MT8196_MEMIF_DL3] = MT8196_IRQ_3,
+ [MT8196_MEMIF_DL4] = MT8196_IRQ_4,
+ [MT8196_MEMIF_DL5] = MT8196_IRQ_5,
+ [MT8196_MEMIF_DL6] = MT8196_IRQ_6,
+ [MT8196_MEMIF_DL7] = MT8196_IRQ_7,
+ [MT8196_MEMIF_DL8] = MT8196_IRQ_8,
+ [MT8196_MEMIF_DL23] = MT8196_IRQ_9,
+ [MT8196_MEMIF_DL24] = MT8196_IRQ_10,
+ [MT8196_MEMIF_DL25] = MT8196_IRQ_11,
+ [MT8196_MEMIF_DL26] = MT8196_IRQ_0,
+ [MT8196_MEMIF_DL_4CH] = MT8196_IRQ_0,
+ [MT8196_MEMIF_DL_24CH] = MT8196_IRQ_12,
+ [MT8196_MEMIF_VUL0] = MT8196_IRQ_13,
+ [MT8196_MEMIF_VUL1] = MT8196_IRQ_14,
+ [MT8196_MEMIF_VUL2] = MT8196_IRQ_15,
+ [MT8196_MEMIF_VUL3] = MT8196_IRQ_16,
+ [MT8196_MEMIF_VUL4] = MT8196_IRQ_17,
+ [MT8196_MEMIF_VUL5] = MT8196_IRQ_18,
+ [MT8196_MEMIF_VUL6] = MT8196_IRQ_19,
+ [MT8196_MEMIF_VUL7] = MT8196_IRQ_20,
+ [MT8196_MEMIF_VUL8] = MT8196_IRQ_21,
+ [MT8196_MEMIF_VUL9] = MT8196_IRQ_22,
+ [MT8196_MEMIF_VUL10] = MT8196_IRQ_23,
+ [MT8196_MEMIF_VUL24] = MT8196_IRQ_24,
+ [MT8196_MEMIF_VUL25] = MT8196_IRQ_25,
+ [MT8196_MEMIF_VUL26] = MT8196_IRQ_0,
+ [MT8196_MEMIF_VUL_CM0] = MT8196_IRQ_26,
+ [MT8196_MEMIF_VUL_CM1] = MT8196_IRQ_0,
+ [MT8196_MEMIF_VUL_CM2] = MT8196_IRQ_0,
+ [MT8196_MEMIF_ETDM_IN0] = MT8196_IRQ_0,
+ [MT8196_MEMIF_ETDM_IN1] = MT8196_IRQ_0,
+ [MT8196_MEMIF_ETDM_IN2] = MT8196_IRQ_0,
+ [MT8196_MEMIF_ETDM_IN3] = MT8196_IRQ_0,
+ [MT8196_MEMIF_ETDM_IN4] = MT8196_IRQ_0,
+ [MT8196_MEMIF_ETDM_IN6] = MT8196_IRQ_0,
+ [MT8196_MEMIF_HDMI] = MT8196_IRQ_31
+};
+
+static bool mt8196_is_volatile_reg(struct device *dev, unsigned int reg)
+{
+ /* these auto-gen reg has read-only bit, so put it as volatile */
+ /* volatile reg cannot be cached, so cannot be set when power off */
+ switch (reg) {
+ case AUDIO_TOP_CON0 ... AUDIO_TOP_CON4:
+ case AFE_APLL1_TUNER_MON0:
+ case AFE_APLL2_TUNER_MON0:
+ case AFE_SPM_CONTROL_ACK:
+ case AUDIO_TOP_IP_VERSION:
+ case AUDIO_ENGEN_CON0_MON:
+ case AUD_TOP_MON_RG:
+ case AFE_CONNSYS_I2S_IPM_VER_MON:
+ case AFE_CONNSYS_I2S_MON:
+ case AFE_PCM_INTF_MON:
+ case AFE_PCM_TOP_IP_VERSION:
+ case AFE_IRQ_MCU_STATUS:
+ case AFE_CUSTOM_IRQ_MCU_STATUS:
+ case AFE_IRQ_MCU_MON0 ... AFE_IRQ26_CNT_MON:
+ case AFE_CUSTOM_IRQ0_CNT_MON:
+ case AFE_STF_MON:
+ case AFE_STF_IP_VERSION:
+ case AFE_CM0_MON:
+ case AFE_CM0_IP_VERSION:
+ case AFE_CM1_MON:
+ case AFE_CM1_IP_VERSION:
+ case AFE_ADDA_UL0_SRC_DEBUG_MON0 ... AFE_ADDA_UL0_SRC_MON1:
+ case AFE_ADDA_UL0_IP_VERSION:
+ case AFE_ADDA_UL1_SRC_DEBUG_MON0 ... AFE_ADDA_UL1_SRC_MON1:
+ case AFE_ADDA_UL1_IP_VERSION:
+ case AFE_MTKAIF_IPM_VER_MON:
+ case AFE_MTKAIF_MON:
+ case AFE_AUD_PAD_TOP_MON:
+ case AFE_ADDA_MTKAIFV4_MON0 ... AFE_ADDA6_MTKAIFV4_MON0:
+ case ETDM_IN0_MON:
+ case ETDM_IN1_MON:
+ case ETDM_IN2_MON:
+ case ETDM_IN4_MON:
+ case ETDM_IN6_MON:
+ case ETDM_OUT0_MON:
+ case ETDM_OUT1_MON:
+ case ETDM_OUT2_MON:
+ case ETDM_OUT4_MON:
+ case ETDM_OUT6_MON:
+ case AFE_DPTX_MON:
+ case AFE_TDM_TOP_IP_VERSION:
+ case AFE_CONN_MON0 ... AFE_CONN_MON5:
+ case AFE_CBIP_SLV_DECODER_MON0 ... AFE_CBIP_SLV_MUX_MON1:
+ case AFE_DL0_CUR_MSB ... AFE_DL0_CUR:
+ case AFE_DL0_RCH_MON ... AFE_DL0_LCH_MON:
+ case AFE_DL1_CUR_MSB ... AFE_DL1_CUR:
+ case AFE_DL1_RCH_MON ... AFE_DL1_LCH_MON:
+ case AFE_DL2_CUR_MSB ... AFE_DL2_CUR:
+ case AFE_DL2_RCH_MON ... AFE_DL2_LCH_MON:
+ case AFE_DL3_CUR_MSB ... AFE_DL3_CUR:
+ case AFE_DL3_RCH_MON ... AFE_DL3_LCH_MON:
+ case AFE_DL4_CUR_MSB ... AFE_DL4_CUR:
+ case AFE_DL4_RCH_MON ... AFE_DL4_LCH_MON:
+ case AFE_DL5_CUR_MSB ... AFE_DL5_CUR:
+ case AFE_DL5_RCH_MON ... AFE_DL5_LCH_MON:
+ case AFE_DL6_CUR_MSB ... AFE_DL6_CUR:
+ case AFE_DL6_RCH_MON ... AFE_DL6_LCH_MON:
+ case AFE_DL7_CUR_MSB ... AFE_DL7_CUR:
+ case AFE_DL7_RCH_MON ... AFE_DL7_LCH_MON:
+ case AFE_DL8_CUR_MSB ... AFE_DL8_CUR:
+ case AFE_DL8_RCH_MON ... AFE_DL8_LCH_MON:
+ case AFE_DL_24CH_CUR_MSB ... AFE_DL_24CH_CUR:
+ case AFE_DL_4CH_CUR_MSB ... AFE_DL_4CH_CUR:
+ case AFE_DL23_CUR_MSB ... AFE_DL23_CUR:
+ case AFE_DL23_RCH_MON ... AFE_DL23_LCH_MON:
+ case AFE_DL24_CUR_MSB ... AFE_DL24_CUR:
+ case AFE_DL24_RCH_MON ... AFE_DL24_LCH_MON:
+ case AFE_DL25_CUR_MSB ... AFE_DL25_CUR:
+ case AFE_DL25_RCH_MON ... AFE_DL25_LCH_MON:
+ case AFE_DL26_CUR_MSB ... AFE_DL26_CUR:
+ case AFE_DL26_RCH_MON ... AFE_DL26_LCH_MON:
+ case AFE_VUL0_CUR_MSB ... AFE_VUL0_CUR:
+ case AFE_VUL1_CUR_MSB ... AFE_VUL1_CUR:
+ case AFE_VUL2_CUR_MSB ... AFE_VUL2_CUR:
+ case AFE_VUL3_CUR_MSB ... AFE_VUL3_CUR:
+ case AFE_VUL4_CUR_MSB ... AFE_VUL4_CUR:
+ case AFE_VUL5_CUR_MSB ... AFE_VUL5_CUR:
+ case AFE_VUL6_CUR_MSB ... AFE_VUL6_CUR:
+ case AFE_VUL7_CUR_MSB ... AFE_VUL7_CUR:
+ case AFE_VUL8_CUR_MSB ... AFE_VUL8_CUR:
+ case AFE_VUL9_CUR_MSB ... AFE_VUL9_CUR:
+ case AFE_VUL10_CUR_MSB ... AFE_VUL10_CUR:
+ case AFE_VUL24_CUR_MSB ... AFE_VUL24_CUR:
+ case AFE_VUL25_CUR_MSB ... AFE_VUL25_CUR:
+ case AFE_VUL25_RCH_MON ... AFE_VUL25_LCH_MON:
+ case AFE_VUL26_CUR_MSB ... AFE_VUL26_CUR:
+ case AFE_VUL_CM0_BASE_MSB ... AFE_VUL_CM0_CON0:
+ case AFE_VUL_CM1_CUR_MSB ... AFE_VUL_CM1_CUR:
+ case AFE_VUL_CM2_CUR_MSB ... AFE_VUL_CM2_CUR:
+ case AFE_ETDM_IN0_CUR_MSB ... AFE_ETDM_IN0_CUR:
+ case AFE_ETDM_IN1_CUR_MSB ... AFE_ETDM_IN1_CUR:
+ case AFE_ETDM_IN2_CUR_MSB ... AFE_ETDM_IN2_CUR:
+ case AFE_ETDM_IN3_CUR_MSB ... AFE_ETDM_IN3_CUR:
+ case AFE_ETDM_IN4_CUR_MSB ... AFE_ETDM_IN4_CUR:
+ case AFE_ETDM_IN6_CUR_MSB ... AFE_ETDM_IN6_CUR:
+ case AFE_HDMI_OUT_CUR_MSB ... AFE_HDMI_OUT_CUR:
+ case AFE_HDMI_OUT_END:
+ case AFE_PROT_SIDEBAND0_MON ... AFE_DOMAIN_SIDEBAND9_MON:
+ case AFE_PCM0_INTF_CON1_MASK_MON ... AFE_ADDA_UL1_SRC_CON0_MASK_MON:
+ case AFE_IRQ_MCU_EN ... AFE_IRQ_MCU_DSP2_EN:
+ case AFE_CUSTOM_IRQ_MCU_EN:
+ case AFE_DL5_CON0:
+ case AFE_DL6_CON0:
+ case AFE_DL23_CON0:
+ case AFE_DL_24CH_CON0:
+ case AFE_VUL1_CON0:
+ case AFE_VUL3_CON0:
+ case AFE_VUL4_CON0:
+ case AFE_VUL5_CON0:
+ case AFE_VUL9_CON0:
+ case AFE_VUL25_CON0:
+ case AFE_IRQ0_MCU_CFG0 ... AFE_IRQ26_MCU_CFG1:
+ return true;
+ default:
+ return false;
+ };
+}
+
+static const struct regmap_config mt8196_afe_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+
+ .volatile_reg = mt8196_is_volatile_reg,
+
+ .max_register = AFE_MAX_REGISTER,
+ .num_reg_defaults_raw = AFE_MAX_REGISTER,
+
+ .cache_type = REGCACHE_FLAT,
+};
+
+static irqreturn_t mt8196_afe_irq_handler(int irq_id, void *dev)
+{
+ struct mtk_base_afe *afe = dev;
+ struct mtk_base_afe_irq *irq;
+ unsigned int status;
+ unsigned int status_mcu;
+ unsigned int mcu_en;
+ unsigned int cus_status;
+ unsigned int cus_status_mcu;
+ unsigned int cus_mcu_en;
+ unsigned int tmp_reg;
+ int ret, cus_ret;
+ int i;
+ struct timespec64 ts64;
+ unsigned long long t1, t2;
+ /* one interrupt period = 5ms */
+ const unsigned long long timeout_limit = 5000000;
+
+ /* get irq that is sent to MCU */
+ regmap_read(afe->regmap, AFE_IRQ_MCU_EN, &mcu_en);
+ regmap_read(afe->regmap, AFE_CUSTOM_IRQ_MCU_EN, &cus_mcu_en);
+
+ ret = regmap_read(afe->regmap, AFE_IRQ_MCU_STATUS, &status);
+ cus_ret = regmap_read(afe->regmap, AFE_CUSTOM_IRQ_MCU_STATUS, &cus_status);
+ /* only care IRQ which is sent to MCU */
+ status_mcu = status & mcu_en & AFE_IRQ_STATUS_BITS;
+ cus_status_mcu = cus_status & cus_mcu_en & AFE_IRQ_STATUS_BITS;
+ if ((ret || !status_mcu) &&
+ (cus_ret || !cus_status_mcu)) {
+ dev_err(afe->dev, "ret %d, sat 0x%x, en 0x%x,csat 0x%x, cen 0x%x\n",
+ ret, status, mcu_en, cus_status_mcu, cus_mcu_en);
+ goto err_irq;
+ }
+
+ ktime_get_ts64(&ts64);
+ t1 = timespec64_to_ns(&ts64);
+
+ for (i = 0; i < MT8196_MEMIF_NUM; i++) {
+ struct mtk_base_afe_memif *memif = &afe->memif[i];
+
+ if (!memif->substream)
+ continue;
+
+ if (memif->irq_usage < 0)
+ continue;
+ irq = &afe->irqs[memif->irq_usage];
+
+ if (i == MT8196_MEMIF_HDMI) {
+ if (cus_status_mcu & (0x1 << irq->irq_data->id))
+ snd_pcm_period_elapsed(memif->substream);
+ } else {
+ if (status_mcu & (0x1 << irq->irq_data->id))
+ snd_pcm_period_elapsed(memif->substream);
+ }
+ }
+
+ ktime_get_ts64(&ts64);
+ t2 = timespec64_to_ns(&ts64);
+ t2 = t2 - t1; /* in ns (10^9) */
+
+ if (t2 > timeout_limit) {
+ dev_warn(afe->dev, "mcu_en 0x%x, cus_mcu_en 0x%x, timeout %llu, limit %llu, ret %d\n",
+ mcu_en, cus_mcu_en,
+ t2, timeout_limit, ret);
+ }
+
+err_irq:
+ /* clear irq */
+ for (i = 0; i < MT8196_IRQ_NUM; ++i) {
+ /* cus_status_mcu only bit0 is used for TDM */
+ if ((status_mcu & (0x1 << i)) || (cus_status_mcu & 0x1)) {
+ regmap_read(afe->regmap, irq_data[i].irq_clr_reg, &tmp_reg);
+ regmap_update_bits(afe->regmap, irq_data[i].irq_clr_reg,
+ AFE_IRQ_CLR_CFG_MASK_SFT |
+ AFE_IRQ_MISS_FLAG_CLR_CFG_MASK_SFT,
+ tmp_reg ^ (AFE_IRQ_CLR_CFG_MASK_SFT |
+ AFE_IRQ_MISS_FLAG_CLR_CFG_MASK_SFT));
+ }
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int mt8196_afe_runtime_suspend(struct device *dev)
+{
+ struct mtk_base_afe *afe = dev_get_drvdata(dev);
+ unsigned int value;
+ unsigned int tmp_reg;
+ int ret, i;
+
+ if (!afe->regmap) {
+ dev_err(afe->dev, "skip regmap\n");
+ goto skip_regmap;
+ }
+
+ /* disable AFE */
+ mt8196_afe_disable_main_clock(afe);
+
+ ret = regmap_read_poll_timeout(afe->regmap,
+ AUDIO_ENGEN_CON0_MON,
+ value,
+ (value & AUDIO_ENGEN_MON_SFT) == 0,
+ 20,
+ 1 * 1000 * 1000);
+ dev_dbg(afe->dev, "read_poll ret %d\n", ret);
+ if (ret)
+ dev_info(afe->dev, "ret %d\n", ret);
+
+ /* make sure all irq status are cleared */
+ for (i = 0; i < MT8196_IRQ_NUM; ++i) {
+ regmap_read(afe->regmap, irq_data[i].irq_clr_reg, &tmp_reg);
+ regmap_update_bits(afe->regmap, irq_data[i].irq_clr_reg,
+ AFE_IRQ_CLR_CFG_MASK_SFT | AFE_IRQ_MISS_FLAG_CLR_CFG_MASK_SFT,
+ tmp_reg ^ (AFE_IRQ_CLR_CFG_MASK_SFT |
+ AFE_IRQ_MISS_FLAG_CLR_CFG_MASK_SFT));
+ }
+
+ /* reset audio 26M request */
+ regmap_update_bits(afe->regmap,
+ AFE_SPM_CONTROL_REQ, 0x1, 0x0);
+
+ /* cache only */
+ regcache_cache_only(afe->regmap, true);
+ regcache_mark_dirty(afe->regmap);
+
+skip_regmap:
+ mt8196_afe_disable_reg_rw_clk(afe);
+ return 0;
+}
+
+static int mt8196_afe_runtime_resume(struct device *dev)
+{
+ struct mtk_base_afe *afe = dev_get_drvdata(dev);
+ int ret = 0;
+
+ ret = mt8196_afe_enable_reg_rw_clk(afe);
+ if (ret)
+ return ret;
+
+ if (!afe->regmap) {
+ dev_warn(afe->dev, "skip regmap\n");
+ goto skip_regmap;
+ }
+ regcache_cache_only(afe->regmap, false);
+ regcache_sync(afe->regmap);
+
+ /* set audio 26M request */
+ regmap_update_bits(afe->regmap, AFE_SPM_CONTROL_REQ, 0x1, 0x1);
+ regmap_update_bits(afe->regmap, AFE_CBIP_CFG0, 0x1, 0x1);
+
+ /* force cpu use 8_24 format when writing 32bit data */
+ regmap_update_bits(afe->regmap, AFE_MEMIF_CON0,
+ CPU_HD_ALIGN_MASK_SFT, 0 << CPU_HD_ALIGN_SFT);
+
+ /* enable AFE */
+ mt8196_afe_enable_main_clock(afe);
+
+skip_regmap:
+ return 0;
+}
+
+static int mt8196_afe_component_probe(struct snd_soc_component *component)
+{
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(component);
+
+ if (component) {
+ /* enable clock for regcache get default value from hw */
+ pm_runtime_get_sync(afe->dev);
+ mtk_afe_add_sub_dai_control(component);
+ pm_runtime_put_sync(afe->dev);
+ }
+ return 0;
+}
+
+static int mt8196_afe_pcm_open(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ /* set the wait_for_avail to 2 sec*/
+ substream->wait_time = msecs_to_jiffies(2 * 1000);
+
+ return 0;
+}
+
+static void mt8196_afe_pcm_free(struct snd_soc_component *component, struct snd_pcm *pcm)
+{
+ snd_pcm_lib_preallocate_free_for_all(pcm);
+}
+
+static const struct snd_soc_component_driver mt8196_afe_component = {
+ .name = AFE_PCM_NAME,
+ .probe = mt8196_afe_component_probe,
+ .pcm_construct = mtk_afe_pcm_new,
+ .pcm_destruct = mt8196_afe_pcm_free,
+ .open = mt8196_afe_pcm_open,
+ .pointer = mtk_afe_pcm_pointer,
+};
+
+static int mt8196_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 = mt8196_memif_dai_driver;
+ dai->num_dai_drivers = ARRAY_SIZE(mt8196_memif_dai_driver);
+
+ dai->controls = mt8196_pcm_kcontrols;
+ dai->num_controls = ARRAY_SIZE(mt8196_pcm_kcontrols);
+ dai->dapm_widgets = mt8196_memif_widgets;
+ dai->num_dapm_widgets = ARRAY_SIZE(mt8196_memif_widgets);
+ dai->dapm_routes = mt8196_memif_routes;
+ dai->num_dapm_routes = ARRAY_SIZE(mt8196_memif_routes);
+ return 0;
+}
+
+typedef int (*dai_register_cb)(struct mtk_base_afe *);
+static const dai_register_cb dai_register_cbs[] = {
+ mt8196_dai_adda_register,
+ mt8196_dai_i2s_register,
+ mt8196_dai_tdm_register,
+ mt8196_dai_memif_register,
+};
+
+static const struct reg_sequence mt8196_cg_patch[] = {
+ { AUDIO_TOP_CON4, 0x361c },
+};
+
+static int mt8196_afe_pcm_dev_probe(struct platform_device *pdev)
+{
+ int ret, i;
+ unsigned int tmp_reg;
+ int irq_id;
+ struct mtk_base_afe *afe;
+ struct mt8196_afe_private *afe_priv;
+ struct device *dev = &pdev->dev;
+
+ ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(34));
+ if (ret)
+ return ret;
+
+ ret = of_reserved_mem_device_init(dev);
+ if (ret)
+ dev_err(dev, "failed to assign memory region: %d\n", ret);
+
+ afe = devm_kzalloc(dev, sizeof(*afe), GFP_KERNEL);
+ if (!afe)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, afe);
+
+ afe->platform_priv = devm_kzalloc(dev, sizeof(*afe_priv),
+ GFP_KERNEL);
+ if (!afe->platform_priv)
+ return -ENOMEM;
+
+ afe_priv = afe->platform_priv;
+ afe->dev = 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");
+
+ /* init audio related clock */
+ ret = mt8196_init_clock(afe);
+ if (ret)
+ return dev_err_probe(dev, ret, "init clock error.\n");
+
+ /* init memif */
+ /* IPM2.0 no need banding */
+ afe->memif_32bit_supported = 1;
+ afe->memif_size = MT8196_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 = memif_irq_usage[i];
+ afe->memif[i].const_irq = 1;
+ }
+
+ mutex_init(&afe->irq_alloc_lock);
+
+ /* init irq */
+ afe->irqs_size = MT8196_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];
+
+ /* request irq */
+ irq_id = platform_get_irq(pdev, 0);
+ if (irq_id < 0)
+ return dev_err_probe(dev, irq_id, "no irq found");
+
+ ret = devm_request_irq(dev, irq_id, mt8196_afe_irq_handler,
+ IRQF_TRIGGER_NONE,
+ "Afe_ISR_Handle", (void *)afe);
+ if (ret)
+ return dev_err_probe(dev, ret, "could not request_irq for Afe_ISR_Handle\n");
+
+ ret = enable_irq_wake(irq_id);
+ if (ret < 0)
+ dev_warn(dev, "enable_irq_wake %d ret: %d\n", irq_id, ret);
+
+ /* 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");
+
+ /* others */
+ afe->mtk_afe_hardware = &mt8196_afe_hardware;
+ afe->memif_fs = mt8196_memif_fs;
+ afe->irq_fs = mt8196_irq_fs;
+ afe->get_dai_fs = mt8196_get_dai_fs;
+ afe->get_memif_pbuf_size = mt8196_get_memif_pbuf_size;
+
+ afe->runtime_resume = mt8196_afe_runtime_resume;
+ afe->runtime_suspend = mt8196_afe_runtime_suspend;
+
+ ret = devm_pm_runtime_enable(dev);
+ if (ret)
+ return ret;
+
+ /* Audio device is part of genpd.
+ * Set audio as syscore device to prevent
+ * genpd automatically power off audio
+ * device when suspend
+ */
+ dev_pm_syscore_device(dev, true);
+
+ /* enable clock for regcache get default value from hw */
+ pm_runtime_get_sync(dev);
+
+ afe->regmap = devm_regmap_init_mmio(dev, afe->base_addr,
+ &mt8196_afe_regmap_config);
+ if (IS_ERR(afe->regmap))
+ return PTR_ERR(afe->regmap);
+
+ ret = regmap_register_patch(afe->regmap, mt8196_cg_patch,
+ ARRAY_SIZE(mt8196_cg_patch));
+ if (ret < 0) {
+ dev_info(dev, "Failed to apply cg patch\n");
+ goto err_pm_disable;
+ }
+
+ regmap_read(afe->regmap, AFE_IRQ_MCU_EN, &tmp_reg);
+ regmap_write(afe->regmap, AFE_IRQ_MCU_EN, 0xffffffff);
+ regmap_read(afe->regmap, AFE_IRQ_MCU_EN, &tmp_reg);
+
+ pm_runtime_put_sync(dev);
+
+ regcache_cache_only(afe->regmap, true);
+ regcache_mark_dirty(afe->regmap);
+
+ /* register component */
+ ret = devm_snd_soc_register_component(dev,
+ &mt8196_afe_component,
+ afe->dai_drivers,
+ afe->num_dai_drivers);
+ if (ret) {
+ dev_warn(dev, "afe component err\n");
+ goto err_pm_disable;
+ }
+
+ return 0;
+
+err_pm_disable:
+ pm_runtime_disable(dev);
+ return ret;
+}
+
+static void mt8196_afe_pcm_dev_remove(struct platform_device *pdev)
+{
+ struct mtk_base_afe *afe = platform_get_drvdata(pdev);
+ struct device *dev = &pdev->dev;
+
+ pm_runtime_disable(dev);
+ if (!pm_runtime_status_suspended(dev))
+ mt8196_afe_runtime_suspend(dev);
+
+ /* disable afe clock */
+ mt8196_afe_disable_reg_rw_clk(afe);
+ mt8196_afe_disable_main_clock(afe);
+}
+
+static const struct of_device_id mt8196_afe_pcm_dt_match[] = {
+ { .compatible = "mediatek,mt8196-afe", },
+ { /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, mt8196_afe_pcm_dt_match);
+
+static const struct dev_pm_ops mt8196_afe_pm_ops = {
+ SET_RUNTIME_PM_OPS(mt8196_afe_runtime_suspend,
+ mt8196_afe_runtime_resume, NULL)
+};
+
+static struct platform_driver mt8196_afe_pcm_driver = {
+ .driver = {
+ .name = "mt8196-afe",
+ .of_match_table = mt8196_afe_pcm_dt_match,
+#if IS_ENABLED(CONFIG_PM)
+ .pm = &mt8196_afe_pm_ops,
+#endif
+ },
+ .probe = mt8196_afe_pcm_dev_probe,
+ .remove = mt8196_afe_pcm_dev_remove,
+};
+
+module_platform_driver(mt8196_afe_pcm_driver);
+
+MODULE_DESCRIPTION("Mediatek ALSA SoC AFE platform driver for 8196");
+MODULE_AUTHOR("Darren Ye <darren.ye@mediatek.com>");
+MODULE_LICENSE("GPL");
--
2.45.2
^ permalink raw reply related [flat|nested] 24+ messages in thread
* [PATCH v6 08/10] ASoC: dt-bindings: mediatek,mt8196-afe: add audio AFE
2025-07-08 11:15 [PATCH v6 00/10] ASoC: mediatek: Add support for MT8196 SoC Darren.Ye
` (4 preceding siblings ...)
2025-07-08 11:15 ` [PATCH v6 07/10] ASoC: mediatek: mt8196: add " Darren.Ye
@ 2025-07-08 11:16 ` Darren.Ye
2025-07-15 5:09 ` Chen-Yu Tsai
2025-07-08 11:16 ` [PATCH v6 09/10] ASoC: mediatek: mt8196: add machine driver with nau8825 Darren.Ye
` (3 subsequent siblings)
9 siblings, 1 reply; 24+ messages in thread
From: Darren.Ye @ 2025-07-08 11:16 UTC (permalink / raw)
To: Liam Girdwood, Mark Brown, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Matthias Brugger, AngeloGioacchino Del Regno,
Jaroslav Kysela, Takashi Iwai, Linus Walleij, Bartosz Golaszewski
Cc: linux-sound, devicetree, linux-kernel, linux-arm-kernel,
linux-mediatek, linux-gpio, Darren Ye, Krzysztof Kozlowski
From: Darren Ye <darren.ye@mediatek.com>
Add mt8196 audio AFE.
Signed-off-by: Darren Ye <darren.ye@mediatek.com>
Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
---
.../bindings/sound/mediatek,mt8196-afe.yaml | 157 ++++++++++++++++++
1 file changed, 157 insertions(+)
create mode 100644 Documentation/devicetree/bindings/sound/mediatek,mt8196-afe.yaml
diff --git a/Documentation/devicetree/bindings/sound/mediatek,mt8196-afe.yaml b/Documentation/devicetree/bindings/sound/mediatek,mt8196-afe.yaml
new file mode 100644
index 000000000000..fe147eddf5e7
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/mediatek,mt8196-afe.yaml
@@ -0,0 +1,157 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/sound/mediatek,mt8196-afe.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: MediaTek Audio Front End PCM controller for MT8196
+
+maintainers:
+ - Darren Ye <darren.ye@mediatek.com>
+
+properties:
+ compatible:
+ const: mediatek,mt8196-afe
+
+ reg:
+ maxItems: 1
+
+ interrupts:
+ maxItems: 1
+
+ memory-region:
+ maxItems: 1
+
+ mediatek,vlpcksys:
+ $ref: /schemas/types.yaml#/definitions/phandle
+ description: To set up the apll12 tuner
+
+ power-domains:
+ maxItems: 1
+
+ clocks:
+ items:
+ - description: mux for audio intbus
+ - description: mux for audio engen1
+ - description: mux for audio engen2
+ - description: mux for audio h
+ - description: vlp 26m clock
+ - description: audio apll1 clock
+ - description: audio apll2 clock
+ - description: audio apll1 divide4
+ - description: audio apll2 divide4
+ - description: audio apll12 divide for i2sin0
+ - description: audio apll12 divide for i2sin1
+ - description: audio apll12 divide for fmi2s
+ - description: audio apll12 divide for tdmout mck
+ - description: audio apll12 divide for tdmout bck
+ - description: mux for audio apll1
+ - description: mux for audio apll2
+ - description: mux for i2sin0 mck
+ - description: mux for i2sin1 mck
+ - description: mux for fmi2s mck
+ - description: mux for tdmout mck
+ - description: mux for adsp clock
+ - description: 26m clock
+
+ clock-names:
+ items:
+ - const: top_aud_intbus
+ - const: top_aud_eng1
+ - const: top_aud_eng2
+ - const: top_aud_h
+ - const: vlp_clk26m
+ - const: apll1
+ - const: apll2
+ - const: apll1_d4
+ - const: apll2_d4
+ - const: apll12_div_i2sin0
+ - const: apll12_div_i2sin1
+ - const: apll12_div_fmi2s
+ - const: apll12_div_tdmout_m
+ - const: apll12_div_tdmout_b
+ - const: top_apll1
+ - const: top_apll2
+ - const: top_i2sin0
+ - const: top_i2sin1
+ - const: top_fmi2s
+ - const: top_tdmout
+ - const: top_adsp
+ - const: clk26m
+
+required:
+ - compatible
+ - reg
+ - interrupts
+ - memory-region
+ - mediatek,vlpcksys
+ - power-domains
+ - clocks
+ - clock-names
+
+additionalProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/interrupt-controller/arm-gic.h>
+ #include <dt-bindings/interrupt-controller/irq.h>
+
+ soc {
+ #address-cells = <2>;
+ #size-cells = <2>;
+
+ afe@1a110000 {
+ compatible = "mediatek,mt8196-afe";
+ reg = <0 0x1a110000 0 0x9000>;
+ interrupts = <GIC_SPI 351 IRQ_TYPE_LEVEL_HIGH 0>;
+ memory-region = <&afe_dma_mem_reserved>;
+ mediatek,vlpcksys = <&vlp_cksys_clk>;
+ power-domains = <&scpsys 14>; //MT8196_POWER_DOMAIN_AUDIO
+ clocks = <&vlp_cksys_clk 40>, //CLK_VLP_CK_AUD_INTBUS_SEL
+ <&vlp_cksys_clk 38>, //CLK_VLP_CK_AUD_ENGEN1_SEL
+ <&vlp_cksys_clk 39>, //CLK_VLP_CK_AUD_ENGEN2_SEL
+ <&vlp_cksys_clk 37>, //CLK_VLP_CK_AUDIO_H_SEL
+ <&vlp_cksys_clk 45>, //CLK_VLP_CK_CLKSQ
+ <&cksys_clk 129>, //CLK_CK_APLL1
+ <&cksys_clk 132>, //CLK_CK_APLL2
+ <&cksys_clk 130>, //CLK_CK_APLL1_D4
+ <&cksys_clk 133>, //CLK_CK_APLL2_D4
+ <&cksys_clk 80>, //CLK_CK_APLL12_CK_DIV_I2SIN0
+ <&cksys_clk 81>, //CLK_CK_APLL12_CK_DIV_I2SIN1
+ <&cksys_clk 92>, //CLK_CK_APLL12_CK_DIV_FMI2S
+ <&cksys_clk 93>, //CLK_CK_APLL12_CK_DIV_TDMOUT_M
+ <&cksys_clk 94>, //CLK_CK_APLL12_CK_DIV_TDMOUT_B
+ <&cksys_clk 43>, //CLK_CK_AUD_1_SEL
+ <&cksys_clk 44>, //CLK_CK_AUD_2_SEL
+ <&cksys_clk 66>, //CLK_CK_APLL_I2SIN0_MCK_SEL
+ <&cksys_clk 67>, //CLK_CK_APLL_I2SIN1_MCK_SEL
+ <&cksys_clk 78>, //CLK_CK_APLL_FMI2S_MCK_SEL
+ <&cksys_clk 79>, //CLK_CK_APLL_TDMOUT_MCK_SEL
+ <&cksys_clk 45>, //CLK_CK_ADSP_SEL
+ <&cksys_clk 140>; //CLK_CK_TCK_26M_MX9
+ clock-names = "top_aud_intbus",
+ "top_aud_eng1",
+ "top_aud_eng2",
+ "top_aud_h",
+ "vlp_clk26m",
+ "apll1",
+ "apll2",
+ "apll1_d4",
+ "apll2_d4",
+ "apll12_div_i2sin0",
+ "apll12_div_i2sin1",
+ "apll12_div_fmi2s",
+ "apll12_div_tdmout_m",
+ "apll12_div_tdmout_b",
+ "top_apll1",
+ "top_apll2",
+ "top_i2sin0",
+ "top_i2sin1",
+ "top_fmi2s",
+ "top_tdmout",
+ "top_adsp",
+ "clk26m";
+ };
+ };
+
+...
--
2.45.2
^ permalink raw reply related [flat|nested] 24+ messages in thread
* [PATCH v6 09/10] ASoC: mediatek: mt8196: add machine driver with nau8825
2025-07-08 11:15 [PATCH v6 00/10] ASoC: mediatek: Add support for MT8196 SoC Darren.Ye
` (5 preceding siblings ...)
2025-07-08 11:16 ` [PATCH v6 08/10] ASoC: dt-bindings: mediatek,mt8196-afe: add audio AFE Darren.Ye
@ 2025-07-08 11:16 ` Darren.Ye
2025-07-21 9:17 ` Chen-Yu Tsai
2025-07-08 11:16 ` [PATCH v6 10/10] ASoC: dt-bindings: mediatek,mt8196-nau8825: Add audio sound card Darren.Ye
` (2 subsequent siblings)
9 siblings, 1 reply; 24+ messages in thread
From: Darren.Ye @ 2025-07-08 11:16 UTC (permalink / raw)
To: Liam Girdwood, Mark Brown, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Matthias Brugger, AngeloGioacchino Del Regno,
Jaroslav Kysela, Takashi Iwai, Linus Walleij, Bartosz Golaszewski
Cc: linux-sound, devicetree, linux-kernel, linux-arm-kernel,
linux-mediatek, linux-gpio, Darren Ye
From: Darren Ye <darren.ye@mediatek.com>
Add support for mt8196 board with nau8825.
Signed-off-by: Darren Ye <darren.ye@mediatek.com>
---
sound/soc/mediatek/Kconfig | 20 +
sound/soc/mediatek/mt8196/Makefile | 2 +
sound/soc/mediatek/mt8196/mt8196-nau8825.c | 869 +++++++++++++++++++++
3 files changed, 891 insertions(+)
create mode 100644 sound/soc/mediatek/mt8196/mt8196-nau8825.c
diff --git a/sound/soc/mediatek/Kconfig b/sound/soc/mediatek/Kconfig
index 7003d71b847c..2889600652a0 100644
--- a/sound/soc/mediatek/Kconfig
+++ b/sound/soc/mediatek/Kconfig
@@ -332,4 +332,24 @@ config SND_SOC_MT8196
Select Y if you have such device.
If unsure select "N".
+config SND_SOC_MT8196_NAU8825
+ tristate "ASoc Audio driver for MT8196 with NAU8825 and I2S codec"
+ depends on SND_SOC_MT8196
+ depends on I2C
+ select SND_SOC_HDMI_CODEC
+ select SND_SOC_DMIC
+ select SND_SOC_NAU8315
+ select SND_SOC_NAU8825
+ select SND_SOC_RT5645
+ select SND_SOC_RT5682_I2C
+ select SND_SOC_RT5682S
+ select SND_SOC_TAS2781_COMLIB
+ select SND_SOC_TAS2781_FMWLIB
+ select SND_SOC_TAS2781_I2C
+ help
+ This adds support for ASoC machine driver for MediaTek MT8196
+ boards with the NAU8825 and other I2S audio codecs.
+ Select Y if you have such device.
+ If unsure select "N".
+
endmenu
diff --git a/sound/soc/mediatek/mt8196/Makefile b/sound/soc/mediatek/mt8196/Makefile
index af41ece87672..5c100040d1fe 100644
--- a/sound/soc/mediatek/mt8196/Makefile
+++ b/sound/soc/mediatek/mt8196/Makefile
@@ -13,3 +13,5 @@ snd-soc-mt8196-afe-objs += \
obj-$(CONFIG_SND_SOC_MT8196) += snd-soc-mt8196-afe.o
+# machine driver
+obj-$(CONFIG_SND_SOC_MT8196_NAU8825) += mt8196-nau8825.o
diff --git a/sound/soc/mediatek/mt8196/mt8196-nau8825.c b/sound/soc/mediatek/mt8196/mt8196-nau8825.c
new file mode 100644
index 000000000000..ac978be7fcc6
--- /dev/null
+++ b/sound/soc/mediatek/mt8196/mt8196-nau8825.c
@@ -0,0 +1,869 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * mt8196-nau8825.c -- mt8196 nau8825 ALSA SoC machine driver
+ *
+ * Copyright (c) 2024 MediaTek Inc.
+ * Author: Darren Ye <darren.ye@mediatek.com>
+ */
+
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/jack.h>
+#include <linux/input.h>
+#include <linux/of_device.h>
+
+#include "mtk-afe-platform-driver.h"
+#include "mt8196-afe-common.h"
+#include "mtk-afe-platform-driver.h"
+#include "mtk-soundcard-driver.h"
+#include "mtk-dsp-sof-common.h"
+#include "mtk-soc-card.h"
+
+#include "../../codecs/nau8825.h"
+#include "../../codecs/rt5682s.h"
+
+#define NAU8825_HS_PRESENT BIT(0)
+#define RT5682S_HS_PRESENT BIT(1)
+#define RT5650_HS_PRESENT BIT(2)
+
+/*
+ * Nau88l25
+ */
+#define NAU8825_CODEC_DAI "nau8825-hifi"
+
+/*
+ * Rt5682s
+ */
+#define RT5682S_CODEC_DAI "rt5682s-aif1"
+
+/*
+ * Rt5650
+ */
+#define RT5650_CODEC_DAI "rt5645-aif1"
+
+#define SOF_DMA_DL1 "SOF_DMA_DL1"
+#define SOF_DMA_DL_24CH "SOF_DMA_DL_24CH"
+#define SOF_DMA_UL0 "SOF_DMA_UL0"
+#define SOF_DMA_UL1 "SOF_DMA_UL1"
+#define SOF_DMA_UL2 "SOF_DMA_UL2"
+
+enum mt8196_jacks {
+ MT8196_JACK_HEADSET,
+ MT8196_JACK_DP,
+ MT8196_JACK_HDMI,
+ MT8196_JACK_MAX,
+};
+
+static struct snd_soc_jack_pin mt8196_dp_jack_pins[] = {
+ {
+ .pin = "DP",
+ .mask = SND_JACK_AVOUT,
+ },
+};
+
+static struct snd_soc_jack_pin mt8196_hdmi_jack_pins[] = {
+ {
+ .pin = "HDMI",
+ .mask = SND_JACK_AVOUT,
+ },
+};
+
+static struct snd_soc_jack_pin nau8825_jack_pins[] = {
+ {
+ .pin = "Headphone Jack",
+ .mask = SND_JACK_HEADPHONE,
+ },
+ {
+ .pin = "Headset Mic",
+ .mask = SND_JACK_MICROPHONE,
+ },
+};
+
+static const struct snd_kcontrol_new mt8196_dumb_spk_controls[] = {
+ SOC_DAPM_PIN_SWITCH("Ext Spk"),
+};
+
+static const struct snd_soc_dapm_widget mt8196_dumb_spk_widgets[] = {
+ SND_SOC_DAPM_SPK("Ext Spk", NULL),
+};
+
+static const struct snd_soc_dapm_widget mt8196_nau8825_widgets[] = {
+ SND_SOC_DAPM_HP("Headphone Jack", NULL),
+ SND_SOC_DAPM_MIC("Headset Mic", NULL),
+ SND_SOC_DAPM_SINK("DP"),
+};
+
+static const struct snd_kcontrol_new mt8196_nau8825_controls[] = {
+ SOC_DAPM_PIN_SWITCH("Headphone Jack"),
+ SOC_DAPM_PIN_SWITCH("Headset Mic"),
+};
+
+#define EXT_SPK_AMP_W_NAME "Ext_Speaker_Amp"
+
+static struct snd_soc_card mt8196_nau8825_soc_card;
+
+static const struct snd_soc_dapm_widget mt8196_nau8825_card_widgets[] = {
+ /* dynamic pinctrl */
+ SND_SOC_DAPM_PINCTRL("ETDMIN_SPK_PIN", "aud-gpio-i2sin4-on", "aud-gpio-i2sin4-off"),
+ SND_SOC_DAPM_PINCTRL("ETDMOUT_SPK_PIN", "aud-gpio-i2sout4-on", "aud-gpio-i2sout4-off"),
+ SND_SOC_DAPM_PINCTRL("ETDMIN_HP_PIN", "aud-gpio-i2sin6-on", "aud-gpio-i2sin6-off"),
+ SND_SOC_DAPM_PINCTRL("ETDMOUT_HP_PIN", "aud-gpio-i2sout6-on", "aud-gpio-i2sout6-off"),
+ SND_SOC_DAPM_PINCTRL("ETDMIN_HDMI_PIN", "aud-gpio-i2sin3-on", "aud-gpio-i2sin3-off"),
+ SND_SOC_DAPM_PINCTRL("ETDMOUT_HDMI_PIN", "aud-gpio-i2sout3-on", "aud-gpio-i2sout3-off"),
+ SND_SOC_DAPM_PINCTRL("AP_DMIC0_PIN", "aud-gpio-ap-dmic-on", "aud-gpio-ap-dmic-off"),
+ SND_SOC_DAPM_PINCTRL("AP_DMIC1_PIN", "aud-gpio-ap-dmic1-on", "aud-gpio-ap-dmic1-off"),
+};
+
+static const struct snd_soc_dapm_route mt8196_nau8825_card_routes[] = {
+};
+
+static const struct snd_kcontrol_new mt8196_nau8825_card_controls[] = {
+ SOC_DAPM_PIN_SWITCH(EXT_SPK_AMP_W_NAME),
+};
+
+/*
+ * define mtk_spk_i2s_mck node in dts when need mclk,
+ * BE i2s need assign snd_soc_ops = mt8196_nau8825_i2s_ops
+ */
+static int mt8196_nau8825_i2s_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ unsigned int rate = params_rate(params);
+ unsigned int mclk_fs_ratio = 128;
+ unsigned int mclk_fs = rate * mclk_fs_ratio;
+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
+
+ return snd_soc_dai_set_sysclk(cpu_dai,
+ 0, mclk_fs, SND_SOC_CLOCK_OUT);
+}
+
+static const struct snd_soc_ops mt8196_nau8825_i2s_ops = {
+ .hw_params = mt8196_nau8825_i2s_hw_params,
+};
+
+static int mt8196_dptx_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ unsigned int rate = params_rate(params);
+ unsigned int mclk_fs_ratio = 256;
+ unsigned int mclk_fs = rate * mclk_fs_ratio;
+ struct snd_soc_dai *dai = snd_soc_rtd_to_cpu(rtd, 0);
+
+ return snd_soc_dai_set_sysclk(dai, 0, mclk_fs, SND_SOC_CLOCK_OUT);
+}
+
+static const struct snd_soc_ops mt8196_dptx_ops = {
+ .hw_params = mt8196_dptx_hw_params,
+};
+
+static int mt8196_dptx_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
+ struct snd_pcm_hw_params *params)
+{
+ dev_info(rtd->dev, "fix format to 32bit\n");
+
+ /* fix BE i2s format to 32bit, clean param mask first */
+ snd_mask_reset_range(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT),
+ 0, (__force unsigned int)SNDRV_PCM_FORMAT_LAST);
+
+ params_set_format(params, SNDRV_PCM_FORMAT_S32_LE);
+
+ return 0;
+}
+
+static int mt8196_i2s_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
+ struct snd_pcm_hw_params *params)
+{
+ dev_info(rtd->dev, "fix format to 32bit\n");
+
+ /* fix BE i2s format to 32bit, clean param mask first */
+ snd_mask_reset_range(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT),
+ 0, SNDRV_PCM_FORMAT_LAST);
+
+ params_set_format(params, SNDRV_PCM_FORMAT_S32_LE);
+ return 0;
+}
+
+static int mt8196_sof_be_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_component *cmpnt_afe = NULL;
+ struct snd_soc_pcm_runtime *runtime;
+
+ /* find afe component */
+ for_each_card_rtds(rtd->card, runtime) {
+ cmpnt_afe = snd_soc_rtdcom_lookup(runtime, AFE_PCM_NAME);
+ if (cmpnt_afe) {
+ dev_info(rtd->dev, "component->name: %s\n", cmpnt_afe->name);
+ break;
+ }
+ }
+
+ if (cmpnt_afe && !pm_runtime_active(cmpnt_afe->dev)) {
+ dev_err(rtd->dev, "afe pm runtime is not active!!\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_ops mt8196_sof_be_ops = {
+ .hw_params = mt8196_sof_be_hw_params,
+};
+
+static const struct sof_conn_stream g_sof_conn_streams[] = {
+ {
+ .sof_link = "AFE_SOF_DL1",
+ .sof_dma = SOF_DMA_DL1,
+ .stream_dir = SNDRV_PCM_STREAM_PLAYBACK
+ },
+ {
+ .sof_link = "AFE_SOF_DL_24CH",
+ .sof_dma = SOF_DMA_DL_24CH,
+ .stream_dir = SNDRV_PCM_STREAM_PLAYBACK
+ },
+ {
+ .sof_link = "AFE_SOF_UL0",
+ .sof_dma = SOF_DMA_UL0,
+ .stream_dir = SNDRV_PCM_STREAM_CAPTURE
+ },
+ {
+ .sof_link = "AFE_SOF_UL1",
+ .sof_dma = SOF_DMA_UL1,
+ .stream_dir = SNDRV_PCM_STREAM_CAPTURE
+ },
+ {
+ .sof_link = "AFE_SOF_UL2",
+ .sof_dma = SOF_DMA_UL2,
+ .stream_dir = SNDRV_PCM_STREAM_CAPTURE
+ },
+};
+
+/* FE */
+SND_SOC_DAILINK_DEFS(playback1,
+ DAILINK_COMP_ARRAY(COMP_CPU("DL1")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+SND_SOC_DAILINK_DEFS(playback_24ch,
+ DAILINK_COMP_ARRAY(COMP_CPU("DL_24CH")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+SND_SOC_DAILINK_DEFS(capture0,
+ DAILINK_COMP_ARRAY(COMP_CPU("UL0")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+SND_SOC_DAILINK_DEFS(capture1,
+ DAILINK_COMP_ARRAY(COMP_CPU("UL1")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+SND_SOC_DAILINK_DEFS(capture2,
+ DAILINK_COMP_ARRAY(COMP_CPU("UL2")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+SND_SOC_DAILINK_DEFS(playback_hdmi,
+ DAILINK_COMP_ARRAY(COMP_CPU("HDMI")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+SND_SOC_DAILINK_DEFS(playback2,
+ DAILINK_COMP_ARRAY(COMP_CPU("DL2")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+SND_SOC_DAILINK_DEFS(capture_cm0,
+ DAILINK_COMP_ARRAY(COMP_CPU("UL_CM0")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+/* BE */
+SND_SOC_DAILINK_DEFS(ap_dmic,
+ DAILINK_COMP_ARRAY(COMP_CPU("AP_DMIC")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+SND_SOC_DAILINK_DEFS(ap_dmic_ch34,
+ DAILINK_COMP_ARRAY(COMP_CPU("AP_DMIC_CH34")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+SND_SOC_DAILINK_DEFS(ap_dmic_multich,
+ DAILINK_COMP_ARRAY(COMP_CPU("AP_DMIC_MULTICH")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+SND_SOC_DAILINK_DEFS(i2sin6,
+ DAILINK_COMP_ARRAY(COMP_CPU("I2SIN6")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+SND_SOC_DAILINK_DEFS(i2sout3,
+ DAILINK_COMP_ARRAY(COMP_CPU("I2SOUT3")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+SND_SOC_DAILINK_DEFS(i2sout4,
+ DAILINK_COMP_ARRAY(COMP_CPU("I2SOUT4")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+SND_SOC_DAILINK_DEFS(i2sout6,
+ DAILINK_COMP_ARRAY(COMP_CPU("I2SOUT6")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+SND_SOC_DAILINK_DEFS(tdm_dptx,
+ DAILINK_COMP_ARRAY(COMP_CPU("TDM_DPTX")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+SND_SOC_DAILINK_DEFS(AFE_SOF_DL_24CH,
+ DAILINK_COMP_ARRAY(COMP_CPU("SOF_DL_24CH")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+SND_SOC_DAILINK_DEFS(AFE_SOF_DL1,
+ DAILINK_COMP_ARRAY(COMP_CPU("SOF_DL1")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+SND_SOC_DAILINK_DEFS(AFE_SOF_UL0,
+ DAILINK_COMP_ARRAY(COMP_CPU("SOF_UL0")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+SND_SOC_DAILINK_DEFS(AFE_SOF_UL1,
+ DAILINK_COMP_ARRAY(COMP_CPU("SOF_UL1")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+SND_SOC_DAILINK_DEFS(AFE_SOF_UL2,
+ DAILINK_COMP_ARRAY(COMP_CPU("SOF_UL2")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+static struct snd_soc_dai_link mt8196_nau8825_dai_links[] = {
+ /*
+ * The SOF topology expects PCM streams 0~4 to be available
+ * for the SOF PCM streams. Put the SOF BE definitions here
+ * so that the PCM device numbers are skipped over.
+ * (BE dailinks do not have PCM devices created.)
+ */
+ {
+ .name = "AFE_SOF_DL_24CH",
+ .no_pcm = 1,
+ .playback_only = 1,
+ .ops = &mt8196_sof_be_ops,
+ SND_SOC_DAILINK_REG(AFE_SOF_DL_24CH),
+ },
+ {
+ .name = "AFE_SOF_DL1",
+ .no_pcm = 1,
+ .playback_only = 1,
+ .ops = &mt8196_sof_be_ops,
+ SND_SOC_DAILINK_REG(AFE_SOF_DL1),
+ },
+ {
+ .name = "AFE_SOF_UL0",
+ .no_pcm = 1,
+ .capture_only = 1,
+ .ops = &mt8196_sof_be_ops,
+ SND_SOC_DAILINK_REG(AFE_SOF_UL0),
+ },
+ {
+ .name = "AFE_SOF_UL1",
+ .no_pcm = 1,
+ .capture_only = 1,
+ .ops = &mt8196_sof_be_ops,
+ SND_SOC_DAILINK_REG(AFE_SOF_UL1),
+ },
+ {
+ .name = "AFE_SOF_UL2",
+ .no_pcm = 1,
+ .capture_only = 1,
+ .ops = &mt8196_sof_be_ops,
+ SND_SOC_DAILINK_REG(AFE_SOF_UL2),
+ },
+ /* Front End DAI links */
+ {
+ .name = "HDMI_FE",
+ .stream_name = "HDMI Playback",
+ .trigger = {SND_SOC_DPCM_TRIGGER_PRE,
+ SND_SOC_DPCM_TRIGGER_PRE},
+ .dynamic = 1,
+ .playback_only = 1,
+ SND_SOC_DAILINK_REG(playback_hdmi),
+ },
+ {
+ .name = "DL2_FE",
+ .stream_name = "DL2 Playback",
+ .trigger = {SND_SOC_DPCM_TRIGGER_PRE,
+ SND_SOC_DPCM_TRIGGER_PRE},
+ .dynamic = 1,
+ .playback_only = 1,
+ SND_SOC_DAILINK_REG(playback2),
+ },
+ {
+ .name = "UL_CM0_FE",
+ .stream_name = "UL_CM0 Capture",
+ .trigger = {SND_SOC_DPCM_TRIGGER_PRE,
+ SND_SOC_DPCM_TRIGGER_PRE},
+ .dynamic = 1,
+ .capture_only = 1,
+ SND_SOC_DAILINK_REG(capture_cm0),
+ },
+ {
+ .name = "DL_24CH_FE",
+ .stream_name = "DL_24CH Playback",
+ .trigger = {SND_SOC_DPCM_TRIGGER_PRE,
+ SND_SOC_DPCM_TRIGGER_PRE},
+ .dynamic = 1,
+ .playback_only = 1,
+ SND_SOC_DAILINK_REG(playback_24ch),
+ },
+ {
+ .name = "DL1_FE",
+ .stream_name = "DL1 Playback",
+ .trigger = {SND_SOC_DPCM_TRIGGER_PRE,
+ SND_SOC_DPCM_TRIGGER_PRE},
+ .dynamic = 1,
+ .playback_only = 1,
+ SND_SOC_DAILINK_REG(playback1),
+ },
+ {
+ .name = "UL0_FE",
+ .stream_name = "UL0 Capture",
+ .trigger = {SND_SOC_DPCM_TRIGGER_PRE,
+ SND_SOC_DPCM_TRIGGER_PRE},
+ .dynamic = 1,
+ .capture_only = 1,
+ SND_SOC_DAILINK_REG(capture0),
+ },
+ {
+ .name = "UL1_FE",
+ .stream_name = "UL1 Capture",
+ .trigger = {SND_SOC_DPCM_TRIGGER_PRE,
+ SND_SOC_DPCM_TRIGGER_PRE},
+ .dynamic = 1,
+ .capture_only = 1,
+ SND_SOC_DAILINK_REG(capture1),
+ },
+ {
+ .name = "UL2_FE",
+ .stream_name = "UL2 Capture",
+ .trigger = {SND_SOC_DPCM_TRIGGER_PRE,
+ SND_SOC_DPCM_TRIGGER_PRE},
+ .dynamic = 1,
+ .capture_only = 1,
+ SND_SOC_DAILINK_REG(capture2),
+ },
+ /* Back End DAI links */
+ {
+ .name = "I2SIN6_BE",
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBC_CFC
+ | SND_SOC_DAIFMT_GATED,
+ .ops = &mt8196_nau8825_i2s_ops,
+ .no_pcm = 1,
+ .capture_only = 1,
+ .ignore_suspend = 1,
+ .be_hw_params_fixup = mt8196_i2s_hw_params_fixup,
+ SND_SOC_DAILINK_REG(i2sin6),
+ },
+ {
+ .name = "I2SOUT4_BE",
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBC_CFC
+ | SND_SOC_DAIFMT_GATED,
+ .ops = &mt8196_nau8825_i2s_ops,
+ .no_pcm = 1,
+ .playback_only = 1,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .be_hw_params_fixup = mt8196_i2s_hw_params_fixup,
+ SND_SOC_DAILINK_REG(i2sout4),
+ },
+ {
+ .name = "I2SOUT6_BE",
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBC_CFC
+ | SND_SOC_DAIFMT_GATED,
+ .ops = &mt8196_nau8825_i2s_ops,
+ .no_pcm = 1,
+ .playback_only = 1,
+ .ignore_suspend = 1,
+ .be_hw_params_fixup = mt8196_i2s_hw_params_fixup,
+ SND_SOC_DAILINK_REG(i2sout6),
+ },
+ {
+ .name = "AP_DMIC_BE",
+ .no_pcm = 1,
+ .capture_only = 1,
+ .ignore_suspend = 1,
+ SND_SOC_DAILINK_REG(ap_dmic),
+ },
+ {
+ .name = "AP_DMIC_CH34_BE",
+ .no_pcm = 1,
+ .capture_only = 1,
+ .ignore_suspend = 1,
+ SND_SOC_DAILINK_REG(ap_dmic_ch34),
+ },
+ {
+ .name = "AP_DMIC_MULTICH_BE",
+ .no_pcm = 1,
+ .capture_only = 1,
+ .ignore_suspend = 1,
+ SND_SOC_DAILINK_REG(ap_dmic_multich),
+ },
+ {
+ .name = "TDM_DPTX_BE",
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBC_CFC
+ | SND_SOC_DAIFMT_GATED,
+ .ops = &mt8196_dptx_ops,
+ .be_hw_params_fixup = mt8196_dptx_hw_params_fixup,
+ .no_pcm = 1,
+ .playback_only = 1,
+ .ignore_suspend = 1,
+ SND_SOC_DAILINK_REG(tdm_dptx),
+ },
+ {
+ .name = "I2SOUT3_BE",
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBC_CFC
+ | SND_SOC_DAIFMT_GATED,
+ .ops = &mt8196_nau8825_i2s_ops,
+ .no_pcm = 1,
+ .playback_only = 1,
+ .ignore_suspend = 1,
+ SND_SOC_DAILINK_REG(i2sout3),
+ },
+};
+
+static int mt8196_dumb_amp_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_card *card = rtd->card;
+ int ret = 0;
+
+ ret = snd_soc_dapm_new_controls(&card->dapm, mt8196_dumb_spk_widgets,
+ ARRAY_SIZE(mt8196_dumb_spk_widgets));
+ if (ret) {
+ dev_err(rtd->dev, "unable to add Dumb Speaker dapm, ret %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_add_card_controls(card, mt8196_dumb_spk_controls,
+ ARRAY_SIZE(mt8196_dumb_spk_controls));
+ if (ret) {
+ dev_err(rtd->dev, "unable to add Dumb card controls, ret %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int mt8196_dptx_codec_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct mtk_soc_card_data *soc_card_data = snd_soc_card_get_drvdata(rtd->card);
+ struct snd_soc_jack *jack = &soc_card_data->card_data->jacks[MT8196_JACK_DP];
+ struct snd_soc_component *component = snd_soc_rtd_to_codec(rtd, 0)->component;
+ int ret = 0;
+
+ ret = snd_soc_card_jack_new_pins(rtd->card, "DP Jack", SND_JACK_AVOUT,
+ jack, mt8196_dp_jack_pins,
+ ARRAY_SIZE(mt8196_dp_jack_pins));
+ if (ret) {
+ dev_err(rtd->dev, "new jack failed: %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_component_set_jack(component, jack, NULL);
+ if (ret) {
+ dev_err(rtd->dev, "set jack failed on %s (ret=%d)\n",
+ component->name, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int mt8196_hdmi_codec_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct mtk_soc_card_data *soc_card_data = snd_soc_card_get_drvdata(rtd->card);
+ struct snd_soc_jack *jack = &soc_card_data->card_data->jacks[MT8196_JACK_HDMI];
+ struct snd_soc_component *component = snd_soc_rtd_to_codec(rtd, 0)->component;
+ int ret = 0;
+
+ ret = snd_soc_card_jack_new_pins(rtd->card, "HDMI Jack", SND_JACK_AVOUT,
+ jack, mt8196_hdmi_jack_pins,
+ ARRAY_SIZE(mt8196_hdmi_jack_pins));
+ if (ret) {
+ dev_err(rtd->dev, "new jack failed: %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_component_set_jack(component, jack, NULL);
+ if (ret) {
+ dev_err(rtd->dev, "set jack failed on %s (ret=%d)\n",
+ component->name, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int mt8196_headset_codec_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_card *card = rtd->card;
+ struct mtk_soc_card_data *soc_card_data = snd_soc_card_get_drvdata(card);
+ struct snd_soc_jack *jack = &soc_card_data->card_data->jacks[MT8196_JACK_HEADSET];
+ struct snd_soc_component *component = snd_soc_rtd_to_codec(rtd, 0)->component;
+ int ret;
+ int type;
+
+ ret = snd_soc_dapm_new_controls(&card->dapm, mt8196_nau8825_widgets,
+ ARRAY_SIZE(mt8196_nau8825_widgets));
+ if (ret) {
+ dev_err(rtd->dev, "unable to add nau8825 card widget, ret %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_add_card_controls(card, mt8196_nau8825_controls,
+ ARRAY_SIZE(mt8196_nau8825_controls));
+ if (ret) {
+ dev_err(rtd->dev, "unable to add nau8825 card controls, ret %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_card_jack_new_pins(rtd->card, "Headset Jack",
+ SND_JACK_HEADSET | SND_JACK_BTN_0 |
+ SND_JACK_BTN_1 | SND_JACK_BTN_2 |
+ SND_JACK_BTN_3,
+ jack,
+ nau8825_jack_pins,
+ ARRAY_SIZE(nau8825_jack_pins));
+ if (ret) {
+ dev_err(rtd->dev, "Headset Jack creation failed: %d\n", ret);
+ return ret;
+ }
+
+ snd_jack_set_key(jack->jack, SND_JACK_BTN_0, KEY_PLAYPAUSE);
+ snd_jack_set_key(jack->jack, SND_JACK_BTN_1, KEY_VOICECOMMAND);
+ snd_jack_set_key(jack->jack, SND_JACK_BTN_2, KEY_VOLUMEUP);
+ snd_jack_set_key(jack->jack, SND_JACK_BTN_3, KEY_VOLUMEDOWN);
+
+ type = SND_JACK_HEADSET | SND_JACK_BTN_0 | SND_JACK_BTN_1 | SND_JACK_BTN_2 | SND_JACK_BTN_3;
+ ret = snd_soc_component_set_jack(component, jack, (void *)&type);
+
+ if (ret) {
+ dev_err(rtd->dev, "Headset Jack call-back failed: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+};
+
+static void mt8196_headset_codec_exit(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_component *component = snd_soc_rtd_to_codec(rtd, 0)->component;
+
+ snd_soc_component_set_jack(component, NULL, NULL);
+}
+
+static int mt8196_nau8825_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
+ unsigned int rate = params_rate(params);
+ unsigned int bit_width = params_width(params);
+ int clk_freq, ret;
+
+ clk_freq = rate * 2 * bit_width;
+
+ /* Configure clock for codec */
+ ret = snd_soc_dai_set_sysclk(codec_dai, NAU8825_CLK_FLL_BLK, 0,
+ SND_SOC_CLOCK_IN);
+ if (ret < 0) {
+ dev_err(codec_dai->dev, "can't set BCLK clock %d\n", ret);
+ return ret;
+ }
+
+ /* Configure pll for codec */
+ ret = snd_soc_dai_set_pll(codec_dai, 0, 0, clk_freq,
+ params_rate(params) * 256);
+ if (ret < 0) {
+ dev_err(codec_dai->dev, "can't set BCLK: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_ops mt8196_nau8825_ops = {
+ .hw_params = mt8196_nau8825_hw_params,
+};
+
+static int mt8196_rt5682s_i2s_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_card *card = rtd->card;
+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
+ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
+ unsigned int rate = params_rate(params);
+ int bitwidth;
+ int ret;
+
+ bitwidth = snd_pcm_format_width(params_format(params));
+ if (bitwidth < 0) {
+ dev_err(card->dev, "invalid bit width: %d\n", bitwidth);
+ return bitwidth;
+ }
+
+ ret = snd_soc_dai_set_tdm_slot(codec_dai, 0x00, 0x0, 0x2, bitwidth);
+ if (ret) {
+ dev_err(card->dev, "failed to set tdm slot\n");
+ return ret;
+ }
+
+ ret = snd_soc_dai_set_pll(codec_dai, RT5682S_PLL1, RT5682S_PLL_S_BCLK1,
+ rate * 32, rate * 512);
+ if (ret) {
+ dev_err(card->dev, "failed to set pll\n");
+ return ret;
+ }
+
+ dev_info(card->dev, "%s set mclk rate: %d\n", __func__, rate * 512);
+
+ ret = snd_soc_dai_set_sysclk(codec_dai, RT5682S_SCLK_S_MCLK,
+ rate * 512, SND_SOC_CLOCK_IN);
+ if (ret) {
+ dev_err(card->dev, "failed to set sysclk\n");
+ return ret;
+ }
+
+ return snd_soc_dai_set_sysclk(cpu_dai, 0, rate * 512,
+ SND_SOC_CLOCK_OUT);
+}
+
+static const struct snd_soc_ops mt8196_rt5682s_i2s_ops = {
+ .hw_params = mt8196_rt5682s_i2s_hw_params,
+};
+
+static int mt8196_nau8825_soc_card_probe(struct mtk_soc_card_data *soc_card_data, bool legacy)
+{
+ struct snd_soc_card *card = soc_card_data->card_data->card;
+ struct snd_soc_dai_link *dai_link;
+ bool init_nau8825 = false;
+ bool init_rt5682s = false;
+ bool init_rt5650 = false;
+ bool init_dumb = false;
+ int i;
+
+ dev_info(card->dev, "legacy: %d\n", legacy);
+
+ for_each_card_prelinks(card, i, dai_link) {
+ if (strcmp(dai_link->name, "TDM_DPTX_BE") == 0) {
+ if (dai_link->num_codecs &&
+ strcmp(dai_link->codecs->dai_name, "snd-soc-dummy-dai"))
+ dai_link->init = mt8196_dptx_codec_init;
+ } else if (strcmp(dai_link->name, "I2SOUT3_BE") == 0) {
+ if (dai_link->num_codecs &&
+ strcmp(dai_link->codecs->dai_name, "snd-soc-dummy-dai"))
+ dai_link->init = mt8196_hdmi_codec_init;
+ } else if (strcmp(dai_link->name, "I2SOUT6_BE") == 0 ||
+ strcmp(dai_link->name, "I2SIN6_BE") == 0) {
+ if (!strcmp(dai_link->codecs->dai_name, NAU8825_CODEC_DAI)) {
+ dai_link->ops = &mt8196_nau8825_ops;
+ if (!init_nau8825) {
+ dai_link->init = mt8196_headset_codec_init;
+ dai_link->exit = mt8196_headset_codec_exit;
+ init_nau8825 = true;
+ }
+ } else if (!strcmp(dai_link->codecs->dai_name, RT5682S_CODEC_DAI)) {
+ dai_link->ops = &mt8196_rt5682s_i2s_ops;
+ if (!init_rt5682s) {
+ dai_link->init = mt8196_headset_codec_init;
+ dai_link->exit = mt8196_headset_codec_exit;
+ init_rt5682s = true;
+ }
+ } else if (!strcmp(dai_link->codecs->dai_name, RT5650_CODEC_DAI)) {
+ dai_link->ops = &mt8196_rt5682s_i2s_ops;
+ if (!init_rt5650) {
+ dai_link->init = mt8196_headset_codec_init;
+ dai_link->exit = mt8196_headset_codec_exit;
+ init_rt5650 = true;
+ }
+ } else {
+ if (strcmp(dai_link->codecs->dai_name, "snd-soc-dummy-dai")) {
+ if (!init_dumb) {
+ dai_link->init = mt8196_dumb_amp_init;
+ init_dumb = true;
+ }
+ }
+ }
+ }
+ }
+
+ return 0;
+}
+
+static const struct mtk_sof_priv mt8196_sof_priv = {
+ .conn_streams = g_sof_conn_streams,
+ .num_streams = ARRAY_SIZE(g_sof_conn_streams),
+};
+
+static struct snd_soc_card mt8196_nau8825_soc_card = {
+ .owner = THIS_MODULE,
+ .dai_link = mt8196_nau8825_dai_links,
+ .num_links = ARRAY_SIZE(mt8196_nau8825_dai_links),
+ .dapm_widgets = mt8196_nau8825_card_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(mt8196_nau8825_card_widgets),
+ .dapm_routes = mt8196_nau8825_card_routes,
+ .num_dapm_routes = ARRAY_SIZE(mt8196_nau8825_card_routes),
+ .controls = mt8196_nau8825_card_controls,
+ .num_controls = ARRAY_SIZE(mt8196_nau8825_card_controls),
+};
+
+static const struct mtk_soundcard_pdata mt8196_nau8825_card = {
+ .card_name = "mt8196_nau8825",
+ .card_data = &(struct mtk_platform_card_data) {
+ .card = &mt8196_nau8825_soc_card,
+ .num_jacks = MT8196_JACK_MAX,
+ .flags = NAU8825_HS_PRESENT
+ },
+ .sof_priv = &mt8196_sof_priv,
+ .soc_probe = mt8196_nau8825_soc_card_probe,
+};
+
+static const struct mtk_soundcard_pdata mt8196_rt5682s_card = {
+ .card_name = "mt8196_rt5682s",
+ .card_data = &(struct mtk_platform_card_data) {
+ .card = &mt8196_nau8825_soc_card,
+ .num_jacks = MT8196_JACK_MAX,
+ .flags = RT5682S_HS_PRESENT
+ },
+ .sof_priv = &mt8196_sof_priv,
+ .soc_probe = mt8196_nau8825_soc_card_probe,
+};
+
+static const struct mtk_soundcard_pdata mt8196_rt5650_card = {
+ .card_name = "mt8196_rt5650",
+ .card_data = &(struct mtk_platform_card_data) {
+ .card = &mt8196_nau8825_soc_card,
+ .num_jacks = MT8196_JACK_MAX,
+ .flags = RT5650_HS_PRESENT
+ },
+ .sof_priv = &mt8196_sof_priv,
+ .soc_probe = mt8196_nau8825_soc_card_probe,
+};
+
+static const struct of_device_id mt8196_nau8825_dt_match[] = {
+ {.compatible = "mediatek,mt8196-nau8825-sound", .data = &mt8196_nau8825_card,},
+ {.compatible = "mediatek,mt8196-rt5682s-sound", .data = &mt8196_rt5682s_card,},
+ {.compatible = "mediatek,mt8196-rt5650-sound", .data = &mt8196_rt5650_card,},
+ {}
+};
+MODULE_DEVICE_TABLE(of, mt8196_nau8825_dt_match);
+
+static struct platform_driver mt8196_nau8825_driver = {
+ .driver = {
+ .name = "mt8196-nau8825",
+ .of_match_table = mt8196_nau8825_dt_match,
+ .pm = &snd_soc_pm_ops,
+ },
+ .probe = mtk_soundcard_common_probe,
+};
+module_platform_driver(mt8196_nau8825_driver);
+
+/* Module information */
+MODULE_DESCRIPTION("MT8196 nau8825 ALSA SoC machine driver");
+MODULE_AUTHOR("Darren Ye <darren.ye@mediatek.com>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("mt8196 nau8825 soc card");
+
--
2.45.2
^ permalink raw reply related [flat|nested] 24+ messages in thread
* [PATCH v6 10/10] ASoC: dt-bindings: mediatek,mt8196-nau8825: Add audio sound card
2025-07-08 11:15 [PATCH v6 00/10] ASoC: mediatek: Add support for MT8196 SoC Darren.Ye
` (6 preceding siblings ...)
2025-07-08 11:16 ` [PATCH v6 09/10] ASoC: mediatek: mt8196: add machine driver with nau8825 Darren.Ye
@ 2025-07-08 11:16 ` Darren.Ye
[not found] ` <20250708111806.3992-3-darren.ye@mediatek.com>
[not found] ` <20250708111806.3992-6-darren.ye@mediatek.com>
9 siblings, 0 replies; 24+ messages in thread
From: Darren.Ye @ 2025-07-08 11:16 UTC (permalink / raw)
To: Liam Girdwood, Mark Brown, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Matthias Brugger, AngeloGioacchino Del Regno,
Jaroslav Kysela, Takashi Iwai, Linus Walleij, Bartosz Golaszewski
Cc: linux-sound, devicetree, linux-kernel, linux-arm-kernel,
linux-mediatek, linux-gpio, Darren Ye, Krzysztof Kozlowski
From: Darren Ye <darren.ye@mediatek.com>
Add soundcard bindings for the MT8196 SoC with the NAU8825 audio codec.
Signed-off-by: Darren Ye <darren.ye@mediatek.com>
Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
---
.../sound/mediatek,mt8196-nau8825.yaml | 102 ++++++++++++++++++
1 file changed, 102 insertions(+)
create mode 100644 Documentation/devicetree/bindings/sound/mediatek,mt8196-nau8825.yaml
diff --git a/Documentation/devicetree/bindings/sound/mediatek,mt8196-nau8825.yaml b/Documentation/devicetree/bindings/sound/mediatek,mt8196-nau8825.yaml
new file mode 100644
index 000000000000..5c4162e64004
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/mediatek,mt8196-nau8825.yaml
@@ -0,0 +1,102 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/sound/mediatek,mt8196-nau8825.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: MediaTek MT8196 ASoC sound card
+
+maintainers:
+ - Darren Ye <darren.ye@mediatek.com>
+
+allOf:
+ - $ref: sound-card-common.yaml#
+
+properties:
+ compatible:
+ enum:
+ - mediatek,mt8196-nau8825-sound
+ - mediatek,mt8196-rt5682s-sound
+ - mediatek,mt8196-rt5650-sound
+
+ mediatek,platform:
+ $ref: /schemas/types.yaml#/definitions/phandle
+ description: The phandle of MT8188 ASoC platform.
+
+patternProperties:
+ "^dai-link-[0-9]+$":
+ type: object
+ description:
+ Container for dai-link level properties and CODEC sub-nodes.
+
+ properties:
+ link-name:
+ description:
+ This property corresponds to the name of the BE dai-link to which
+ we are going to update parameters in this node.
+ items:
+ enum:
+ - TDM_DPTX_BE
+ - I2SOUT6_BE
+ - I2SIN6_BE
+ - I2SOUT4_BE
+ - I2SOUT3_BE
+
+ codec:
+ description: Holds subnode which indicates codec dai.
+ type: object
+ additionalProperties: false
+ properties:
+ sound-dai:
+ minItems: 1
+ maxItems: 2
+ required:
+ - sound-dai
+
+ dai-format:
+ description: audio format.
+ items:
+ enum:
+ - i2s
+ - right_j
+ - left_j
+ - dsp_a
+ - dsp_b
+
+ mediatek,clk-provider:
+ $ref: /schemas/types.yaml#/definitions/string
+ description: Indicates dai-link clock master.
+ enum:
+ - cpu
+ - codec
+
+ additionalProperties: false
+
+ required:
+ - link-name
+
+required:
+ - compatible
+ - mediatek,platform
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ sound {
+ compatible = "mediatek,mt8196-nau8825-sound";
+ model = "mt8196-nau8825";
+ mediatek,platform = <&afe>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&aud_pins_default>;
+ dai-link-0 {
+ link-name = "I2SOUT6_BE";
+ dai-format = "i2s";
+ mediatek,clk-provider = "cpu";
+ codec {
+ sound-dai = <&nau8825>;
+ };
+ };
+ };
+
+...
--
2.45.2
^ permalink raw reply related [flat|nested] 24+ messages in thread
* Re: [PATCH v6 08/10] ASoC: dt-bindings: mediatek,mt8196-afe: add audio AFE
2025-07-08 11:16 ` [PATCH v6 08/10] ASoC: dt-bindings: mediatek,mt8196-afe: add audio AFE Darren.Ye
@ 2025-07-15 5:09 ` Chen-Yu Tsai
2025-07-15 7:34 ` Chen-Yu Tsai
2025-07-16 12:41 ` Darren Ye (叶飞)
0 siblings, 2 replies; 24+ messages in thread
From: Chen-Yu Tsai @ 2025-07-15 5:09 UTC (permalink / raw)
To: Darren.Ye
Cc: Liam Girdwood, Mark Brown, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Matthias Brugger, AngeloGioacchino Del Regno,
Jaroslav Kysela, Takashi Iwai, Linus Walleij, Bartosz Golaszewski,
linux-sound, devicetree, linux-kernel, linux-arm-kernel,
linux-mediatek, linux-gpio, Krzysztof Kozlowski
Hi,
On Tue, Jul 8, 2025 at 7:35 PM Darren.Ye <darren.ye@mediatek.com> wrote:
>
> From: Darren Ye <darren.ye@mediatek.com>
>
> Add mt8196 audio AFE.
>
> Signed-off-by: Darren Ye <darren.ye@mediatek.com>
> Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
> ---
> .../bindings/sound/mediatek,mt8196-afe.yaml | 157 ++++++++++++++++++
> 1 file changed, 157 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/sound/mediatek,mt8196-afe.yaml
>
> diff --git a/Documentation/devicetree/bindings/sound/mediatek,mt8196-afe.yaml b/Documentation/devicetree/bindings/sound/mediatek,mt8196-afe.yaml
> new file mode 100644
> index 000000000000..fe147eddf5e7
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/sound/mediatek,mt8196-afe.yaml
> @@ -0,0 +1,157 @@
> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/sound/mediatek,mt8196-afe.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: MediaTek Audio Front End PCM controller for MT8196
> +
> +maintainers:
> + - Darren Ye <darren.ye@mediatek.com>
> +
> +properties:
> + compatible:
> + const: mediatek,mt8196-afe
> +
> + reg:
> + maxItems: 1
> +
> + interrupts:
> + maxItems: 1
> +
> + memory-region:
> + maxItems: 1
> +
> + mediatek,vlpcksys:
> + $ref: /schemas/types.yaml#/definitions/phandle
> + description: To set up the apll12 tuner
Looking at the implementation, the configuration is just a fixed value.
Can this be moved to the VLP clock driver instead?
> +
> + power-domains:
> + maxItems: 1
> +
> + clocks:
> + items:
> + - description: mux for audio intbus
> + - description: mux for audio engen1
> + - description: mux for audio engen2
> + - description: mux for audio h
> + - description: vlp 26m clock
> + - description: audio apll1 clock
> + - description: audio apll2 clock
> + - description: audio apll1 divide4
> + - description: audio apll2 divide4
> + - description: audio apll12 divide for i2sin0
> + - description: audio apll12 divide for i2sin1
> + - description: audio apll12 divide for fmi2s
> + - description: audio apll12 divide for tdmout mck
> + - description: audio apll12 divide for tdmout bck
> + - description: mux for audio apll1
> + - description: mux for audio apll2
> + - description: mux for i2sin0 mck
> + - description: mux for i2sin1 mck
> + - description: mux for fmi2s mck
> + - description: mux for tdmout mck
> + - description: mux for adsp clock
> + - description: 26m clock
> +
> + clock-names:
> + items:
> + - const: top_aud_intbus
> + - const: top_aud_eng1
> + - const: top_aud_eng2
> + - const: top_aud_h
> + - const: vlp_clk26m
> + - const: apll1
> + - const: apll2
> + - const: apll1_d4
> + - const: apll2_d4
These are parents of the top_apll[12]. They do not feed into the
hardware directly, so you should not be including them here.
> + - const: apll12_div_i2sin0
> + - const: apll12_div_i2sin1
> + - const: apll12_div_fmi2s
> + - const: apll12_div_tdmout_m
> + - const: apll12_div_tdmout_b
In the clock bindings sent by Collabora, these dividers are no longer
separately modeled; they have been combined with their respective
top_* clocks.
> + - const: top_apll1
> + - const: top_apll2
These two are parents to apll12_div_*, do not feed into the hardware
directly, so you should not be including them here.
The clock tree for each audio interface clock looks like the following:
apll1 -> apll1_d4 -> top_apll1 --
/ \
clk26m --> top_fmi2s -> apll12_div_fmi2s
\ /
apll2 -> apll2_d4 -> top_apll2 --
Only the final "apll12_div_fmi2s" should be referenced.
On the implementation side, it should simply be a matter of setting the
required rate (24.576 MHz or 22.5792 MHz, or some multiple) on this leaf
clock, and let the clock framework figure out the PLL and dividers to
use. Same thing for enabling the clock.
> + - const: top_i2sin0
> + - const: top_i2sin1
> + - const: top_fmi2s
> + - const: top_tdmout
> + - const: top_adsp
> + - const: clk26m
Is this one directly needed? It is similar to vlp_clk26m, and I suspect
only that one is needed.
ChenYu
> +
> +required:
> + - compatible
> + - reg
> + - interrupts
> + - memory-region
> + - mediatek,vlpcksys
> + - power-domains
> + - clocks
> + - clock-names
> +
> +additionalProperties: false
> +
> +examples:
> + - |
> + #include <dt-bindings/interrupt-controller/arm-gic.h>
> + #include <dt-bindings/interrupt-controller/irq.h>
> +
> + soc {
> + #address-cells = <2>;
> + #size-cells = <2>;
> +
> + afe@1a110000 {
> + compatible = "mediatek,mt8196-afe";
> + reg = <0 0x1a110000 0 0x9000>;
> + interrupts = <GIC_SPI 351 IRQ_TYPE_LEVEL_HIGH 0>;
> + memory-region = <&afe_dma_mem_reserved>;
> + mediatek,vlpcksys = <&vlp_cksys_clk>;
> + power-domains = <&scpsys 14>; //MT8196_POWER_DOMAIN_AUDIO
> + clocks = <&vlp_cksys_clk 40>, //CLK_VLP_CK_AUD_INTBUS_SEL
> + <&vlp_cksys_clk 38>, //CLK_VLP_CK_AUD_ENGEN1_SEL
> + <&vlp_cksys_clk 39>, //CLK_VLP_CK_AUD_ENGEN2_SEL
> + <&vlp_cksys_clk 37>, //CLK_VLP_CK_AUDIO_H_SEL
> + <&vlp_cksys_clk 45>, //CLK_VLP_CK_CLKSQ
> + <&cksys_clk 129>, //CLK_CK_APLL1
> + <&cksys_clk 132>, //CLK_CK_APLL2
> + <&cksys_clk 130>, //CLK_CK_APLL1_D4
> + <&cksys_clk 133>, //CLK_CK_APLL2_D4
> + <&cksys_clk 80>, //CLK_CK_APLL12_CK_DIV_I2SIN0
> + <&cksys_clk 81>, //CLK_CK_APLL12_CK_DIV_I2SIN1
> + <&cksys_clk 92>, //CLK_CK_APLL12_CK_DIV_FMI2S
> + <&cksys_clk 93>, //CLK_CK_APLL12_CK_DIV_TDMOUT_M
> + <&cksys_clk 94>, //CLK_CK_APLL12_CK_DIV_TDMOUT_B
> + <&cksys_clk 43>, //CLK_CK_AUD_1_SEL
> + <&cksys_clk 44>, //CLK_CK_AUD_2_SEL
> + <&cksys_clk 66>, //CLK_CK_APLL_I2SIN0_MCK_SEL
> + <&cksys_clk 67>, //CLK_CK_APLL_I2SIN1_MCK_SEL
> + <&cksys_clk 78>, //CLK_CK_APLL_FMI2S_MCK_SEL
> + <&cksys_clk 79>, //CLK_CK_APLL_TDMOUT_MCK_SEL
> + <&cksys_clk 45>, //CLK_CK_ADSP_SEL
> + <&cksys_clk 140>; //CLK_CK_TCK_26M_MX9
> + clock-names = "top_aud_intbus",
> + "top_aud_eng1",
> + "top_aud_eng2",
> + "top_aud_h",
> + "vlp_clk26m",
> + "apll1",
> + "apll2",
> + "apll1_d4",
> + "apll2_d4",
> + "apll12_div_i2sin0",
> + "apll12_div_i2sin1",
> + "apll12_div_fmi2s",
> + "apll12_div_tdmout_m",
> + "apll12_div_tdmout_b",
> + "top_apll1",
> + "top_apll2",
> + "top_i2sin0",
> + "top_i2sin1",
> + "top_fmi2s",
> + "top_tdmout",
> + "top_adsp",
> + "clk26m";
> + };
> + };
> +
> +...
> --
> 2.45.2
>
>
^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [PATCH v6 08/10] ASoC: dt-bindings: mediatek,mt8196-afe: add audio AFE
2025-07-15 5:09 ` Chen-Yu Tsai
@ 2025-07-15 7:34 ` Chen-Yu Tsai
2025-07-16 12:41 ` Darren Ye (叶飞)
1 sibling, 0 replies; 24+ messages in thread
From: Chen-Yu Tsai @ 2025-07-15 7:34 UTC (permalink / raw)
To: Darren.Ye
Cc: Liam Girdwood, Mark Brown, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Matthias Brugger, AngeloGioacchino Del Regno,
Jaroslav Kysela, Takashi Iwai, Linus Walleij, Bartosz Golaszewski,
linux-sound, devicetree, linux-kernel, linux-arm-kernel,
linux-mediatek, linux-gpio, Krzysztof Kozlowski
On Tue, Jul 15, 2025 at 1:09 PM Chen-Yu Tsai <wenst@chromium.org> wrote:
>
> Hi,
>
> On Tue, Jul 8, 2025 at 7:35 PM Darren.Ye <darren.ye@mediatek.com> wrote:
> >
> > From: Darren Ye <darren.ye@mediatek.com>
> >
> > Add mt8196 audio AFE.
> >
> > Signed-off-by: Darren Ye <darren.ye@mediatek.com>
> > Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
> > ---
> > .../bindings/sound/mediatek,mt8196-afe.yaml | 157 ++++++++++++++++++
> > 1 file changed, 157 insertions(+)
> > create mode 100644 Documentation/devicetree/bindings/sound/mediatek,mt8196-afe.yaml
> >
> > diff --git a/Documentation/devicetree/bindings/sound/mediatek,mt8196-afe.yaml b/Documentation/devicetree/bindings/sound/mediatek,mt8196-afe.yaml
> > new file mode 100644
> > index 000000000000..fe147eddf5e7
> > --- /dev/null
> > +++ b/Documentation/devicetree/bindings/sound/mediatek,mt8196-afe.yaml
> > @@ -0,0 +1,157 @@
> > +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> > +%YAML 1.2
> > +---
> > +$id: http://devicetree.org/schemas/sound/mediatek,mt8196-afe.yaml#
> > +$schema: http://devicetree.org/meta-schemas/core.yaml#
> > +
> > +title: MediaTek Audio Front End PCM controller for MT8196
> > +
> > +maintainers:
> > + - Darren Ye <darren.ye@mediatek.com>
> > +
> > +properties:
> > + compatible:
> > + const: mediatek,mt8196-afe
> > +
> > + reg:
> > + maxItems: 1
> > +
> > + interrupts:
> > + maxItems: 1
> > +
> > + memory-region:
> > + maxItems: 1
> > +
> > + mediatek,vlpcksys:
> > + $ref: /schemas/types.yaml#/definitions/phandle
> > + description: To set up the apll12 tuner
>
> Looking at the implementation, the configuration is just a fixed value.
> Can this be moved to the VLP clock driver instead?
>
> > +
> > + power-domains:
> > + maxItems: 1
> > +
> > + clocks:
> > + items:
> > + - description: mux for audio intbus
> > + - description: mux for audio engen1
> > + - description: mux for audio engen2
> > + - description: mux for audio h
> > + - description: vlp 26m clock
> > + - description: audio apll1 clock
> > + - description: audio apll2 clock
> > + - description: audio apll1 divide4
> > + - description: audio apll2 divide4
> > + - description: audio apll12 divide for i2sin0
> > + - description: audio apll12 divide for i2sin1
> > + - description: audio apll12 divide for fmi2s
> > + - description: audio apll12 divide for tdmout mck
> > + - description: audio apll12 divide for tdmout bck
> > + - description: mux for audio apll1
> > + - description: mux for audio apll2
> > + - description: mux for i2sin0 mck
> > + - description: mux for i2sin1 mck
> > + - description: mux for fmi2s mck
> > + - description: mux for tdmout mck
> > + - description: mux for adsp clock
> > + - description: 26m clock
> > +
> > + clock-names:
> > + items:
> > + - const: top_aud_intbus
> > + - const: top_aud_eng1
> > + - const: top_aud_eng2
> > + - const: top_aud_h
> > + - const: vlp_clk26m
>
> > + - const: apll1
> > + - const: apll2
> > + - const: apll1_d4
> > + - const: apll2_d4
>
> These are parents of the top_apll[12]. They do not feed into the
> hardware directly, so you should not be including them here.
>
> > + - const: apll12_div_i2sin0
> > + - const: apll12_div_i2sin1
> > + - const: apll12_div_fmi2s
> > + - const: apll12_div_tdmout_m
> > + - const: apll12_div_tdmout_b
>
> In the clock bindings sent by Collabora, these dividers are no longer
> separately modeled; they have been combined with their respective
> top_* clocks.
>
> > + - const: top_apll1
> > + - const: top_apll2
>
> These two are parents to apll12_div_*, do not feed into the hardware
> directly, so you should not be including them here.
Slight correction: looking at the submitted AFE clk driver, these two
feed into some clock gate in the AFE block, so these two are probably
needed.
ChenYu
> The clock tree for each audio interface clock looks like the following:
>
> apll1 -> apll1_d4 -> top_apll1 --
> / \
> clk26m --> top_fmi2s -> apll12_div_fmi2s
> \ /
> apll2 -> apll2_d4 -> top_apll2 --
>
> Only the final "apll12_div_fmi2s" should be referenced.
>
> On the implementation side, it should simply be a matter of setting the
> required rate (24.576 MHz or 22.5792 MHz, or some multiple) on this leaf
> clock, and let the clock framework figure out the PLL and dividers to
> use. Same thing for enabling the clock.
>
> > + - const: top_i2sin0
> > + - const: top_i2sin1
> > + - const: top_fmi2s
> > + - const: top_tdmout
> > + - const: top_adsp
>
> > + - const: clk26m
>
> Is this one directly needed? It is similar to vlp_clk26m, and I suspect
> only that one is needed.
>
>
> ChenYu
>
> > +
> > +required:
> > + - compatible
> > + - reg
> > + - interrupts
> > + - memory-region
> > + - mediatek,vlpcksys
> > + - power-domains
> > + - clocks
> > + - clock-names
> > +
> > +additionalProperties: false
> > +
> > +examples:
> > + - |
> > + #include <dt-bindings/interrupt-controller/arm-gic.h>
> > + #include <dt-bindings/interrupt-controller/irq.h>
> > +
> > + soc {
> > + #address-cells = <2>;
> > + #size-cells = <2>;
> > +
> > + afe@1a110000 {
> > + compatible = "mediatek,mt8196-afe";
> > + reg = <0 0x1a110000 0 0x9000>;
> > + interrupts = <GIC_SPI 351 IRQ_TYPE_LEVEL_HIGH 0>;
> > + memory-region = <&afe_dma_mem_reserved>;
> > + mediatek,vlpcksys = <&vlp_cksys_clk>;
> > + power-domains = <&scpsys 14>; //MT8196_POWER_DOMAIN_AUDIO
> > + clocks = <&vlp_cksys_clk 40>, //CLK_VLP_CK_AUD_INTBUS_SEL
> > + <&vlp_cksys_clk 38>, //CLK_VLP_CK_AUD_ENGEN1_SEL
> > + <&vlp_cksys_clk 39>, //CLK_VLP_CK_AUD_ENGEN2_SEL
> > + <&vlp_cksys_clk 37>, //CLK_VLP_CK_AUDIO_H_SEL
> > + <&vlp_cksys_clk 45>, //CLK_VLP_CK_CLKSQ
> > + <&cksys_clk 129>, //CLK_CK_APLL1
> > + <&cksys_clk 132>, //CLK_CK_APLL2
> > + <&cksys_clk 130>, //CLK_CK_APLL1_D4
> > + <&cksys_clk 133>, //CLK_CK_APLL2_D4
> > + <&cksys_clk 80>, //CLK_CK_APLL12_CK_DIV_I2SIN0
> > + <&cksys_clk 81>, //CLK_CK_APLL12_CK_DIV_I2SIN1
> > + <&cksys_clk 92>, //CLK_CK_APLL12_CK_DIV_FMI2S
> > + <&cksys_clk 93>, //CLK_CK_APLL12_CK_DIV_TDMOUT_M
> > + <&cksys_clk 94>, //CLK_CK_APLL12_CK_DIV_TDMOUT_B
> > + <&cksys_clk 43>, //CLK_CK_AUD_1_SEL
> > + <&cksys_clk 44>, //CLK_CK_AUD_2_SEL
> > + <&cksys_clk 66>, //CLK_CK_APLL_I2SIN0_MCK_SEL
> > + <&cksys_clk 67>, //CLK_CK_APLL_I2SIN1_MCK_SEL
> > + <&cksys_clk 78>, //CLK_CK_APLL_FMI2S_MCK_SEL
> > + <&cksys_clk 79>, //CLK_CK_APLL_TDMOUT_MCK_SEL
> > + <&cksys_clk 45>, //CLK_CK_ADSP_SEL
> > + <&cksys_clk 140>; //CLK_CK_TCK_26M_MX9
> > + clock-names = "top_aud_intbus",
> > + "top_aud_eng1",
> > + "top_aud_eng2",
> > + "top_aud_h",
> > + "vlp_clk26m",
> > + "apll1",
> > + "apll2",
> > + "apll1_d4",
> > + "apll2_d4",
> > + "apll12_div_i2sin0",
> > + "apll12_div_i2sin1",
> > + "apll12_div_fmi2s",
> > + "apll12_div_tdmout_m",
> > + "apll12_div_tdmout_b",
> > + "top_apll1",
> > + "top_apll2",
> > + "top_i2sin0",
> > + "top_i2sin1",
> > + "top_fmi2s",
> > + "top_tdmout",
> > + "top_adsp",
> > + "clk26m";
> > + };
> > + };
> > +
> > +...
> > --
> > 2.45.2
> >
> >
^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [PATCH v6 08/10] ASoC: dt-bindings: mediatek,mt8196-afe: add audio AFE
2025-07-15 5:09 ` Chen-Yu Tsai
2025-07-15 7:34 ` Chen-Yu Tsai
@ 2025-07-16 12:41 ` Darren Ye (叶飞)
2025-07-17 2:16 ` Chen-Yu Tsai
1 sibling, 1 reply; 24+ messages in thread
From: Darren Ye (叶飞) @ 2025-07-16 12:41 UTC (permalink / raw)
To: wenst@chromium.org
Cc: linux-kernel@vger.kernel.org, linux-mediatek@lists.infradead.org,
devicetree@vger.kernel.org, linus.walleij@linaro.org,
linux-sound@vger.kernel.org, linux-gpio@vger.kernel.org,
krzysztof.kozlowski@linaro.org, broonie@kernel.org, brgl@bgdev.pl,
conor+dt@kernel.org, tiwai@suse.com, robh@kernel.org,
lgirdwood@gmail.com, linux-arm-kernel@lists.infradead.org,
matthias.bgg@gmail.com, krzk+dt@kernel.org, perex@perex.cz,
AngeloGioacchino Del Regno
On Tue, 2025-07-15 at 13:09 +0800, Chen-Yu Tsai wrote:
> External email : Please do not click links or open attachments until
> you have verified the sender or the content.
>
>
> Hi,
>
> On Tue, Jul 8, 2025 at 7:35 PM Darren.Ye <darren.ye@mediatek.com>
> wrote:
> >
> > From: Darren Ye <darren.ye@mediatek.com>
> >
> > Add mt8196 audio AFE.
> >
> > Signed-off-by: Darren Ye <darren.ye@mediatek.com>
> > Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
> > ---
> > .../bindings/sound/mediatek,mt8196-afe.yaml | 157
> > ++++++++++++++++++
> > 1 file changed, 157 insertions(+)
> > create mode 100644
> > Documentation/devicetree/bindings/sound/mediatek,mt8196-afe.yaml
> >
> > diff --git
> > a/Documentation/devicetree/bindings/sound/mediatek,mt8196-afe.yaml
> > b/Documentation/devicetree/bindings/sound/mediatek,mt8196-afe.yaml
> > new file mode 100644
> > index 000000000000..fe147eddf5e7
> > --- /dev/null
> > +++ b/Documentation/devicetree/bindings/sound/mediatek,mt8196-
> > afe.yaml
> > @@ -0,0 +1,157 @@
> > +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> > +%YAML 1.2
> > +---
> > +$id:
> > https://urldefense.com/v3/__http://devicetree.org/schemas/sound/mediatek,mt8196-afe.yaml*__;Iw!!CTRNKA9wMg0ARbw!iSiBwCYEjWdWSv25XRbl3ky3Niiw3nDpVY-fW1dxyp3eU5YDs0bbZXEgPUQ1_NInbxUIgyz3HJvf-xTH$
> > +$schema:
> > https://urldefense.com/v3/__http://devicetree.org/meta-schemas/core.yaml*__;Iw!!CTRNKA9wMg0ARbw!iSiBwCYEjWdWSv25XRbl3ky3Niiw3nDpVY-fW1dxyp3eU5YDs0bbZXEgPUQ1_NInbxUIgyz3HHUjHsuW$
> > +
> > +title: MediaTek Audio Front End PCM controller for MT8196
> > +
> > +maintainers:
> > + - Darren Ye <darren.ye@mediatek.com>
> > +
> > +properties:
> > + compatible:
> > + const: mediatek,mt8196-afe
> > +
> > + reg:
> > + maxItems: 1
> > +
> > + interrupts:
> > + maxItems: 1
> > +
> > + memory-region:
> > + maxItems: 1
> > +
> > + mediatek,vlpcksys:
> > + $ref: /schemas/types.yaml#/definitions/phandle
> > + description: To set up the apll12 tuner
>
> Looking at the implementation, the configuration is just a fixed
> value.
> Can this be moved to the VLP clock driver instead?
>
I thinks it's not good to put it in the VLP clock kernel driver,
because this value needs to be adjusted. Usually, it is set in
coreboot, but it is hard to change later. Audio needs to adjust
this value, so it's better to put it in the audio driver. What do
you think?
> > +
> > + power-domains:
> > + maxItems: 1
> > +
> > + clocks:
> > + items:
> > + - description: mux for audio intbus
> > + - description: mux for audio engen1
> > + - description: mux for audio engen2
> > + - description: mux for audio h
> > + - description: vlp 26m clock
> > + - description: audio apll1 clock
> > + - description: audio apll2 clock
> > + - description: audio apll1 divide4
> > + - description: audio apll2 divide4
> > + - description: audio apll12 divide for i2sin0
> > + - description: audio apll12 divide for i2sin1
> > + - description: audio apll12 divide for fmi2s
> > + - description: audio apll12 divide for tdmout mck
> > + - description: audio apll12 divide for tdmout bck
> > + - description: mux for audio apll1
> > + - description: mux for audio apll2
> > + - description: mux for i2sin0 mck
> > + - description: mux for i2sin1 mck
> > + - description: mux for fmi2s mck
> > + - description: mux for tdmout mck
> > + - description: mux for adsp clock
> > + - description: 26m clock
> > +
> > + clock-names:
> > + items:
> > + - const: top_aud_intbus
> > + - const: top_aud_eng1
> > + - const: top_aud_eng2
> > + - const: top_aud_h
> > + - const: vlp_clk26m
> > + - const: apll1
> > + - const: apll2
> > + - const: apll1_d4
> > + - const: apll2_d4
>
> These are parents of the top_apll[12]. They do not feed into the
> hardware directly, so you should not be including them here.
>
> > + - const: apll12_div_i2sin0
> > + - const: apll12_div_i2sin1
> > + - const: apll12_div_fmi2s
> > + - const: apll12_div_tdmout_m
> > + - const: apll12_div_tdmout_b
>
> In the clock bindings sent by Collabora, these dividers are no longer
> separately modeled; they have been combined with their respective
> top_* clocks.
>
> > + - const: top_apll1
> > + - const: top_apll2
>
> These two are parents to apll12_div_*, do not feed into the hardware
> directly, so you should not be including them here.
>
> The clock tree for each audio interface clock looks like the
> following:
>
> apll1 -> apll1_d4 -> top_apll1 --
> / \
> clk26m --> top_fmi2s ->
> apll12_div_fmi2s
> \ /
> apll2 -> apll2_d4 -> top_apll2 --
>
> Only the final "apll12_div_fmi2s" should be referenced.
>
> On the implementation side, it should simply be a matter of setting
> the
> required rate (24.576 MHz or 22.5792 MHz, or some multiple) on this
> leaf
> clock, and let the clock framework figure out the PLL and dividers to
> use. Same thing for enabling the clock.
I think we have some misunderstandings. I will first draw mtk audio
clock topology diagram, and then we can disscuss which parts can be
optimized together.
top_aud_intbus: as read/write reg clock source;
top_aud_eng1/top_aud_eng2: as i2s bck clock source;
apll12_div_xxx: as mclk clock source;
top_audio_h: as afe other ip clock source;
vlp_clk26m
\
apll1 --> top_audio_h
/
apll2
vlp_clk26m
\
apll1 --> apll1_d4 --> top_aud_eng1
\
clk26m --> top_apll1
\
--> top_fmi2s --> apll12_div_fmi2s
/
clk26m --> top_apll2
/
apll2 --> apll2_d4 --> top_aud_eng2
/
vlp_clk26m
vlp_clk26m -> top_aud_intbus
>
> > + - const: top_i2sin0
> > + - const: top_i2sin1
> > + - const: top_fmi2s
> > + - const: top_tdmout
> > + - const: top_adsp
> > + - const: clk26m
>
> Is this one directly needed? It is similar to vlp_clk26m, and I
> suspect
> only that one is needed.
>
vlp_clk26m belongs to the VLP clock domain, and clk26 belongs to the
system clock domain;
top_apll1/top_apll2 can only select system apll1/apll2 or clk26m, they
cannot use vlp_clk26m;
BR
Darren Ye
>
> ChenYu
>
> > +
> > +required:
> > + - compatible
> > + - reg
> > + - interrupts
> > + - memory-region
> > + - mediatek,vlpcksys
> > + - power-domains
> > + - clocks
> > + - clock-names
> > +
> > +additionalProperties: false
> > +
> > +examples:
> > + - |
> > + #include <dt-bindings/interrupt-controller/arm-gic.h>
> > + #include <dt-bindings/interrupt-controller/irq.h>
> > +
> > + soc {
> > + #address-cells = <2>;
> > + #size-cells = <2>;
> > +
> > + afe@1a110000 {
> > + compatible = "mediatek,mt8196-afe";
> > + reg = <0 0x1a110000 0 0x9000>;
> > + interrupts = <GIC_SPI 351 IRQ_TYPE_LEVEL_HIGH 0>;
> > + memory-region = <&afe_dma_mem_reserved>;
> > + mediatek,vlpcksys = <&vlp_cksys_clk>;
> > + power-domains = <&scpsys 14>;
> > //MT8196_POWER_DOMAIN_AUDIO
> > + clocks = <&vlp_cksys_clk 40>,
> > //CLK_VLP_CK_AUD_INTBUS_SEL
> > + <&vlp_cksys_clk 38>,
> > //CLK_VLP_CK_AUD_ENGEN1_SEL
> > + <&vlp_cksys_clk 39>,
> > //CLK_VLP_CK_AUD_ENGEN2_SEL
> > + <&vlp_cksys_clk 37>, //CLK_VLP_CK_AUDIO_H_SEL
> > + <&vlp_cksys_clk 45>, //CLK_VLP_CK_CLKSQ
> > + <&cksys_clk 129>, //CLK_CK_APLL1
> > + <&cksys_clk 132>, //CLK_CK_APLL2
> > + <&cksys_clk 130>, //CLK_CK_APLL1_D4
> > + <&cksys_clk 133>, //CLK_CK_APLL2_D4
> > + <&cksys_clk 80>,
> > //CLK_CK_APLL12_CK_DIV_I2SIN0
> > + <&cksys_clk 81>,
> > //CLK_CK_APLL12_CK_DIV_I2SIN1
> > + <&cksys_clk 92>, //CLK_CK_APLL12_CK_DIV_FMI2S
> > + <&cksys_clk 93>,
> > //CLK_CK_APLL12_CK_DIV_TDMOUT_M
> > + <&cksys_clk 94>,
> > //CLK_CK_APLL12_CK_DIV_TDMOUT_B
> > + <&cksys_clk 43>, //CLK_CK_AUD_1_SEL
> > + <&cksys_clk 44>, //CLK_CK_AUD_2_SEL
> > + <&cksys_clk 66>, //CLK_CK_APLL_I2SIN0_MCK_SEL
> > + <&cksys_clk 67>, //CLK_CK_APLL_I2SIN1_MCK_SEL
> > + <&cksys_clk 78>, //CLK_CK_APLL_FMI2S_MCK_SEL
> > + <&cksys_clk 79>, //CLK_CK_APLL_TDMOUT_MCK_SEL
> > + <&cksys_clk 45>, //CLK_CK_ADSP_SEL
> > + <&cksys_clk 140>; //CLK_CK_TCK_26M_MX9
> > + clock-names = "top_aud_intbus",
> > + "top_aud_eng1",
> > + "top_aud_eng2",
> > + "top_aud_h",
> > + "vlp_clk26m",
> > + "apll1",
> > + "apll2",
> > + "apll1_d4",
> > + "apll2_d4",
> > + "apll12_div_i2sin0",
> > + "apll12_div_i2sin1",
> > + "apll12_div_fmi2s",
> > + "apll12_div_tdmout_m",
> > + "apll12_div_tdmout_b",
> > + "top_apll1",
> > + "top_apll2",
> > + "top_i2sin0",
> > + "top_i2sin1",
> > + "top_fmi2s",
> > + "top_tdmout",
> > + "top_adsp",
> > + "clk26m";
> > + };
> > + };
> > +
> > +...
> > --
> > 2.45.2
> >
> >
^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [PATCH v6 08/10] ASoC: dt-bindings: mediatek,mt8196-afe: add audio AFE
2025-07-16 12:41 ` Darren Ye (叶飞)
@ 2025-07-17 2:16 ` Chen-Yu Tsai
0 siblings, 0 replies; 24+ messages in thread
From: Chen-Yu Tsai @ 2025-07-17 2:16 UTC (permalink / raw)
To: Darren Ye (叶飞)
Cc: linux-kernel@vger.kernel.org, linux-mediatek@lists.infradead.org,
devicetree@vger.kernel.org, linus.walleij@linaro.org,
linux-sound@vger.kernel.org, linux-gpio@vger.kernel.org,
krzysztof.kozlowski@linaro.org, broonie@kernel.org, brgl@bgdev.pl,
conor+dt@kernel.org, tiwai@suse.com, robh@kernel.org,
lgirdwood@gmail.com, linux-arm-kernel@lists.infradead.org,
matthias.bgg@gmail.com, krzk+dt@kernel.org, perex@perex.cz,
AngeloGioacchino Del Regno
On Wed, Jul 16, 2025 at 9:41 PM Darren Ye (叶飞) <Darren.Ye@mediatek.com> wrote:
>
> On Tue, 2025-07-15 at 13:09 +0800, Chen-Yu Tsai wrote:
> > External email : Please do not click links or open attachments until
> > you have verified the sender or the content.
> >
> >
> > Hi,
> >
> > On Tue, Jul 8, 2025 at 7:35 PM Darren.Ye <darren.ye@mediatek.com>
> > wrote:
> > >
> > > From: Darren Ye <darren.ye@mediatek.com>
> > >
> > > Add mt8196 audio AFE.
> > >
> > > Signed-off-by: Darren Ye <darren.ye@mediatek.com>
> > > Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
> > > ---
> > > .../bindings/sound/mediatek,mt8196-afe.yaml | 157
> > > ++++++++++++++++++
> > > 1 file changed, 157 insertions(+)
> > > create mode 100644
> > > Documentation/devicetree/bindings/sound/mediatek,mt8196-afe.yaml
> > >
> > > diff --git
> > > a/Documentation/devicetree/bindings/sound/mediatek,mt8196-afe.yaml
> > > b/Documentation/devicetree/bindings/sound/mediatek,mt8196-afe.yaml
> > > new file mode 100644
> > > index 000000000000..fe147eddf5e7
> > > --- /dev/null
> > > +++ b/Documentation/devicetree/bindings/sound/mediatek,mt8196-
> > > afe.yaml
> > > @@ -0,0 +1,157 @@
> > > +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> > > +%YAML 1.2
> > > +---
> > > +$id:
> > > https://urldefense.com/v3/__http://devicetree.org/schemas/sound/mediatek,mt8196-afe.yaml*__;Iw!!CTRNKA9wMg0ARbw!iSiBwCYEjWdWSv25XRbl3ky3Niiw3nDpVY-fW1dxyp3eU5YDs0bbZXEgPUQ1_NInbxUIgyz3HJvf-xTH$
> > > +$schema:
> > > https://urldefense.com/v3/__http://devicetree.org/meta-schemas/core.yaml*__;Iw!!CTRNKA9wMg0ARbw!iSiBwCYEjWdWSv25XRbl3ky3Niiw3nDpVY-fW1dxyp3eU5YDs0bbZXEgPUQ1_NInbxUIgyz3HHUjHsuW$
> > > +
> > > +title: MediaTek Audio Front End PCM controller for MT8196
> > > +
> > > +maintainers:
> > > + - Darren Ye <darren.ye@mediatek.com>
> > > +
> > > +properties:
> > > + compatible:
> > > + const: mediatek,mt8196-afe
> > > +
> > > + reg:
> > > + maxItems: 1
> > > +
> > > + interrupts:
> > > + maxItems: 1
> > > +
> > > + memory-region:
> > > + maxItems: 1
> > > +
> > > + mediatek,vlpcksys:
> > > + $ref: /schemas/types.yaml#/definitions/phandle
> > > + description: To set up the apll12 tuner
> >
> > Looking at the implementation, the configuration is just a fixed
> > value.
> > Can this be moved to the VLP clock driver instead?
> >
> I thinks it's not good to put it in the VLP clock kernel driver,
> because this value needs to be adjusted. Usually, it is set in
> coreboot, but it is hard to change later. Audio needs to adjust
> this value, so it's better to put it in the audio driver. What do
> you think?
It looks like just one fixed value, instead of needing to tweak the value
during operation? If that's the case I think it could be moved to the
clock driver.
> > > +
> > > + power-domains:
> > > + maxItems: 1
> > > +
> > > + clocks:
> > > + items:
> > > + - description: mux for audio intbus
> > > + - description: mux for audio engen1
> > > + - description: mux for audio engen2
> > > + - description: mux for audio h
> > > + - description: vlp 26m clock
> > > + - description: audio apll1 clock
> > > + - description: audio apll2 clock
> > > + - description: audio apll1 divide4
> > > + - description: audio apll2 divide4
> > > + - description: audio apll12 divide for i2sin0
> > > + - description: audio apll12 divide for i2sin1
> > > + - description: audio apll12 divide for fmi2s
> > > + - description: audio apll12 divide for tdmout mck
> > > + - description: audio apll12 divide for tdmout bck
> > > + - description: mux for audio apll1
> > > + - description: mux for audio apll2
> > > + - description: mux for i2sin0 mck
> > > + - description: mux for i2sin1 mck
> > > + - description: mux for fmi2s mck
> > > + - description: mux for tdmout mck
> > > + - description: mux for adsp clock
> > > + - description: 26m clock
> > > +
> > > + clock-names:
> > > + items:
> > > + - const: top_aud_intbus
> > > + - const: top_aud_eng1
> > > + - const: top_aud_eng2
> > > + - const: top_aud_h
> > > + - const: vlp_clk26m
> > > + - const: apll1
> > > + - const: apll2
> > > + - const: apll1_d4
> > > + - const: apll2_d4
> >
> > These are parents of the top_apll[12]. They do not feed into the
> > hardware directly, so you should not be including them here.
> >
> > > + - const: apll12_div_i2sin0
> > > + - const: apll12_div_i2sin1
> > > + - const: apll12_div_fmi2s
> > > + - const: apll12_div_tdmout_m
> > > + - const: apll12_div_tdmout_b
> >
> > In the clock bindings sent by Collabora, these dividers are no longer
> > separately modeled; they have been combined with their respective
> > top_* clocks.
> >
> > > + - const: top_apll1
> > > + - const: top_apll2
> >
> > These two are parents to apll12_div_*, do not feed into the hardware
> > directly, so you should not be including them here.
> >
> > The clock tree for each audio interface clock looks like the
> > following:
> >
> > apll1 -> apll1_d4 -> top_apll1 --
> > / \
> > clk26m --> top_fmi2s ->
> > apll12_div_fmi2s
> > \ /
> > apll2 -> apll2_d4 -> top_apll2 --
> >
> > Only the final "apll12_div_fmi2s" should be referenced.
> >
> > On the implementation side, it should simply be a matter of setting
> > the
> > required rate (24.576 MHz or 22.5792 MHz, or some multiple) on this
> > leaf
> > clock, and let the clock framework figure out the PLL and dividers to
> > use. Same thing for enabling the clock.
>
> I think we have some misunderstandings. I will first draw mtk audio
> clock topology diagram, and then we can disscuss which parts can be
> optimized together.
>
> top_aud_intbus: as read/write reg clock source;
> top_aud_eng1/top_aud_eng2: as i2s bck clock source;
> apll12_div_xxx: as mclk clock source;
> top_audio_h: as afe other ip clock source;
And what about top_i2sin0, top_i2sin1, top_fmi2s, top_tdmout?
>
> vlp_clk26m
> \
> apll1 --> top_audio_h
> /
> apll2
This shows that vlp_clk26m, apll1, apll2 are parents to top_audio_h.
> vlp_clk26m
> \
> apll1 --> apll1_d4 --> top_aud_eng1
> \
> clk26m --> top_apll1
> \
> --> top_fmi2s --> apll12_div_fmi2s
> /
> clk26m --> top_apll2
> /
> apll2 --> apll2_d4 --> top_aud_eng2
> /
> vlp_clk26m
This shows the parents of top_aud_eng1, top_aud_eng2, top_apll1, top_apll2,
and apll12_div_fmi2s.
My point is that all these parents should not be referenced unless they
also directly feed some input to the AFE block.
> vlp_clk26m -> top_aud_intbus
>
>
> >
> > > + - const: top_i2sin0
> > > + - const: top_i2sin1
> > > + - const: top_fmi2s
> > > + - const: top_tdmout
> > > + - const: top_adsp
> > > + - const: clk26m
> >
> > Is this one directly needed? It is similar to vlp_clk26m, and I
> > suspect
> > only that one is needed.
> >
> vlp_clk26m belongs to the VLP clock domain, and clk26 belongs to the
> system clock domain;
>
> top_apll1/top_apll2 can only select system apll1/apll2 or clk26m, they
> cannot use vlp_clk26m;
Yes. What I mean is that clk26m does not directly feed the AFE block.
If it does not directly feed the AFE block, it shouldn't be listed.
So please think about the modules in the AFE, and the clock gates in
the AFE block; think about what "external" clocks feed into them. Those
are the ones you should list.
ChenYu
> BR
> Darren Ye
>
> >
> > ChenYu
> >
> > > +
> > > +required:
> > > + - compatible
> > > + - reg
> > > + - interrupts
> > > + - memory-region
> > > + - mediatek,vlpcksys
> > > + - power-domains
> > > + - clocks
> > > + - clock-names
> > > +
> > > +additionalProperties: false
> > > +
> > > +examples:
> > > + - |
> > > + #include <dt-bindings/interrupt-controller/arm-gic.h>
> > > + #include <dt-bindings/interrupt-controller/irq.h>
> > > +
> > > + soc {
> > > + #address-cells = <2>;
> > > + #size-cells = <2>;
> > > +
> > > + afe@1a110000 {
> > > + compatible = "mediatek,mt8196-afe";
> > > + reg = <0 0x1a110000 0 0x9000>;
> > > + interrupts = <GIC_SPI 351 IRQ_TYPE_LEVEL_HIGH 0>;
> > > + memory-region = <&afe_dma_mem_reserved>;
> > > + mediatek,vlpcksys = <&vlp_cksys_clk>;
> > > + power-domains = <&scpsys 14>;
> > > //MT8196_POWER_DOMAIN_AUDIO
> > > + clocks = <&vlp_cksys_clk 40>,
> > > //CLK_VLP_CK_AUD_INTBUS_SEL
> > > + <&vlp_cksys_clk 38>,
> > > //CLK_VLP_CK_AUD_ENGEN1_SEL
> > > + <&vlp_cksys_clk 39>,
> > > //CLK_VLP_CK_AUD_ENGEN2_SEL
> > > + <&vlp_cksys_clk 37>, //CLK_VLP_CK_AUDIO_H_SEL
> > > + <&vlp_cksys_clk 45>, //CLK_VLP_CK_CLKSQ
> > > + <&cksys_clk 129>, //CLK_CK_APLL1
> > > + <&cksys_clk 132>, //CLK_CK_APLL2
> > > + <&cksys_clk 130>, //CLK_CK_APLL1_D4
> > > + <&cksys_clk 133>, //CLK_CK_APLL2_D4
> > > + <&cksys_clk 80>,
> > > //CLK_CK_APLL12_CK_DIV_I2SIN0
> > > + <&cksys_clk 81>,
> > > //CLK_CK_APLL12_CK_DIV_I2SIN1
> > > + <&cksys_clk 92>, //CLK_CK_APLL12_CK_DIV_FMI2S
> > > + <&cksys_clk 93>,
> > > //CLK_CK_APLL12_CK_DIV_TDMOUT_M
> > > + <&cksys_clk 94>,
> > > //CLK_CK_APLL12_CK_DIV_TDMOUT_B
> > > + <&cksys_clk 43>, //CLK_CK_AUD_1_SEL
> > > + <&cksys_clk 44>, //CLK_CK_AUD_2_SEL
> > > + <&cksys_clk 66>, //CLK_CK_APLL_I2SIN0_MCK_SEL
> > > + <&cksys_clk 67>, //CLK_CK_APLL_I2SIN1_MCK_SEL
> > > + <&cksys_clk 78>, //CLK_CK_APLL_FMI2S_MCK_SEL
> > > + <&cksys_clk 79>, //CLK_CK_APLL_TDMOUT_MCK_SEL
> > > + <&cksys_clk 45>, //CLK_CK_ADSP_SEL
> > > + <&cksys_clk 140>; //CLK_CK_TCK_26M_MX9
> > > + clock-names = "top_aud_intbus",
> > > + "top_aud_eng1",
> > > + "top_aud_eng2",
> > > + "top_aud_h",
> > > + "vlp_clk26m",
> > > + "apll1",
> > > + "apll2",
> > > + "apll1_d4",
> > > + "apll2_d4",
> > > + "apll12_div_i2sin0",
> > > + "apll12_div_i2sin1",
> > > + "apll12_div_fmi2s",
> > > + "apll12_div_tdmout_m",
> > > + "apll12_div_tdmout_b",
> > > + "top_apll1",
> > > + "top_apll2",
> > > + "top_i2sin0",
> > > + "top_i2sin1",
> > > + "top_fmi2s",
> > > + "top_tdmout",
> > > + "top_adsp",
> > > + "clk26m";
> > > + };
> > > + };
> > > +
> > > +...
> > > --
> > > 2.45.2
> > >
> > >
>
> ************* MEDIATEK Confidentiality Notice
> ********************
> The information contained in this e-mail message (including any
> attachments) may be confidential, proprietary, privileged, or otherwise
> exempt from disclosure under applicable laws. It is intended to be
> conveyed only to the designated recipient(s). Any use, dissemination,
> distribution, printing, retaining or copying of this e-mail (including its
> attachments) by unintended recipient(s) is strictly prohibited and may
> be unlawful. If you are not an intended recipient of this e-mail, or believe
>
> that you have received this e-mail in error, please notify the sender
> immediately (by replying to this e-mail), delete any and all copies of
> this e-mail (including any attachments) from your system, and do not
> disclose the content of this e-mail to any other person. Thank you!
^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [PATCH v6 09/10] ASoC: mediatek: mt8196: add machine driver with nau8825
2025-07-08 11:16 ` [PATCH v6 09/10] ASoC: mediatek: mt8196: add machine driver with nau8825 Darren.Ye
@ 2025-07-21 9:17 ` Chen-Yu Tsai
0 siblings, 0 replies; 24+ messages in thread
From: Chen-Yu Tsai @ 2025-07-21 9:17 UTC (permalink / raw)
To: Darren.Ye
Cc: Liam Girdwood, Mark Brown, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Matthias Brugger, AngeloGioacchino Del Regno,
Jaroslav Kysela, Takashi Iwai, Linus Walleij, Bartosz Golaszewski,
linux-sound, devicetree, linux-kernel, linux-arm-kernel,
linux-mediatek, linux-gpio
Hi,
On Tue, Jul 8, 2025 at 7:35 PM Darren.Ye <darren.ye@mediatek.com> wrote:
>
> From: Darren Ye <darren.ye@mediatek.com>
>
> Add support for mt8196 board with nau8825.
>
> Signed-off-by: Darren Ye <darren.ye@mediatek.com>
> ---
> sound/soc/mediatek/Kconfig | 20 +
> sound/soc/mediatek/mt8196/Makefile | 2 +
> sound/soc/mediatek/mt8196/mt8196-nau8825.c | 869 +++++++++++++++++++++
[...]
> +static const struct snd_soc_dapm_widget mt8196_nau8825_card_widgets[] = {
> + /* dynamic pinctrl */
> + SND_SOC_DAPM_PINCTRL("ETDMIN_SPK_PIN", "aud-gpio-i2sin4-on", "aud-gpio-i2sin4-off"),
> + SND_SOC_DAPM_PINCTRL("ETDMOUT_SPK_PIN", "aud-gpio-i2sout4-on", "aud-gpio-i2sout4-off"),
> + SND_SOC_DAPM_PINCTRL("ETDMIN_HP_PIN", "aud-gpio-i2sin6-on", "aud-gpio-i2sin6-off"),
> + SND_SOC_DAPM_PINCTRL("ETDMOUT_HP_PIN", "aud-gpio-i2sout6-on", "aud-gpio-i2sout6-off"),
> + SND_SOC_DAPM_PINCTRL("ETDMIN_HDMI_PIN", "aud-gpio-i2sin3-on", "aud-gpio-i2sin3-off"),
> + SND_SOC_DAPM_PINCTRL("ETDMOUT_HDMI_PIN", "aud-gpio-i2sout3-on", "aud-gpio-i2sout3-off"),
> + SND_SOC_DAPM_PINCTRL("AP_DMIC0_PIN", "aud-gpio-ap-dmic-on", "aud-gpio-ap-dmic-off"),
> + SND_SOC_DAPM_PINCTRL("AP_DMIC1_PIN", "aud-gpio-ap-dmic1-on", "aud-gpio-ap-dmic1-off"),
These are pins for the AFE block, not specific to any machine, so:
1. They should be moved to the AFE driver.
2. Their names should match the underlying interface (I2SOUT4), instead
of the intended use case (ETDMOUT_SPK).
3. Their related routes should be set statically in the AFE driver.
Also, for the I2S interfaces, since there are input and output pins, but
also common clock pins, you should either split out the common clock pins
as a separate pinctrl entry, or just combine them so that you have one
entry for each interface.
Last, you need to document the pinctrl names as required in the binding.
ChenYu
^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [PATCH v6 01/10] ASoC: mediatek: common: modify mtk afe platform driver for mt8196
2025-07-08 11:15 ` [PATCH v6 01/10] ASoC: mediatek: common: modify mtk afe platform driver for mt8196 Darren.Ye
@ 2025-07-22 7:38 ` Chen-Yu Tsai
0 siblings, 0 replies; 24+ messages in thread
From: Chen-Yu Tsai @ 2025-07-22 7:38 UTC (permalink / raw)
To: Darren.Ye
Cc: Liam Girdwood, Mark Brown, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Matthias Brugger, AngeloGioacchino Del Regno,
Jaroslav Kysela, Takashi Iwai, Linus Walleij, Bartosz Golaszewski,
linux-sound, devicetree, linux-kernel, linux-arm-kernel,
linux-mediatek, linux-gpio, Louis-Alexis Eyraud
Hi,
On Tue, Jul 8, 2025 at 7:33 PM Darren.Ye <darren.ye@mediatek.com> wrote:
>
> From: Darren Ye <darren.ye@mediatek.com>
>
> Mofify the pcm pointer interface to support 64-bit address access.
^ Modify
And the subject should say the same, or shorter:
Support 64-bit addresses in PCM pointer callback
> Signed-off-by: Darren Ye <darren.ye@mediatek.com>
> Reviewed-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
> Tested-by: Louis-Alexis Eyraud <louisalexis.eyraud@collabora.com>
> ---
> .../mediatek/common/mtk-afe-platform-driver.c | 47 ++++++++++++-------
> .../mediatek/common/mtk-afe-platform-driver.h | 2 +
> 2 files changed, 33 insertions(+), 16 deletions(-)
>
> diff --git a/sound/soc/mediatek/common/mtk-afe-platform-driver.c b/sound/soc/mediatek/common/mtk-afe-platform-driver.c
> index 70fd05d5ff48..cab4ef035199 100644
> --- a/sound/soc/mediatek/common/mtk-afe-platform-driver.c
> +++ b/sound/soc/mediatek/common/mtk-afe-platform-driver.c
> @@ -86,29 +86,44 @@ snd_pcm_uframes_t mtk_afe_pcm_pointer(struct snd_soc_component *component,
> const struct mtk_base_memif_data *memif_data = memif->data;
> struct regmap *regmap = afe->regmap;
> struct device *dev = afe->dev;
> - int reg_ofs_base = memif_data->reg_ofs_base;
> - int reg_ofs_cur = memif_data->reg_ofs_cur;
> - unsigned int hw_ptr = 0, hw_base = 0;
> - int ret, pcm_ptr_bytes;
> -
> - ret = regmap_read(regmap, reg_ofs_cur, &hw_ptr);
> - if (ret || hw_ptr == 0) {
> - dev_err(dev, "%s hw_ptr err\n", __func__);
> - pcm_ptr_bytes = 0;
> + unsigned int hw_ptr_lower32 = 0, hw_ptr_upper32 = 0;
> + unsigned int hw_base_lower32 = 0, hw_base_upper32 = 0;
> + unsigned long long hw_ptr = 0, hw_base = 0;
> + int ret;
> + unsigned long long pcm_ptr_bytes = 0;
> +
> + ret = regmap_read(regmap, memif_data->reg_ofs_cur, &hw_ptr_lower32);
> + if (ret || hw_ptr_lower32 == 0) {
I'm not sure how the hardware is, but I think hw_ptr_lower32 == 0
should no longer be a failure, since it could be 0x100000000 or
some other address with the lower 32-bits all zero. Instead the
check should be done once the addresses are put together.
> + dev_err(dev, "%s hw_ptr_lower32 err\n", __func__);
> goto POINTER_RETURN_FRAMES;
This is out of scope, but maybe this should just return zero directly
to simplify reading.
> }
>
> - ret = regmap_read(regmap, reg_ofs_base, &hw_base);
> - if (ret || hw_base == 0) {
> - dev_err(dev, "%s hw_ptr err\n", __func__);
> - pcm_ptr_bytes = 0;
> - goto POINTER_RETURN_FRAMES;
> + if (memif_data->reg_ofs_cur_msb) {
> + ret = regmap_read(regmap, memif_data->reg_ofs_cur_msb, &hw_ptr_upper32);
> + if (ret) {
> + dev_err(dev, "%s hw_ptr_upper32 err\n", __func__);
> + goto POINTER_RETURN_FRAMES;
> + }
> }
>
> - pcm_ptr_bytes = hw_ptr - hw_base;
> + ret = regmap_read(regmap, memif_data->reg_ofs_base, &hw_base_lower32);
> + if (ret || hw_base_lower32 == 0) {
Same here.
> + dev_err(dev, "%s hw_base_lower32 err\n", __func__);
> + goto POINTER_RETURN_FRAMES;
> + }
> + if (memif_data->reg_ofs_base_msb) {
> + ret = regmap_read(regmap, memif_data->reg_ofs_base_msb, &hw_base_upper32);
> + if (ret) {
> + dev_err(dev, "%s hw_base_upper32 err\n", __func__);
> + goto POINTER_RETURN_FRAMES;
> + }
> + }
> + hw_ptr = ((unsigned long long)hw_ptr_upper32 << 32) + hw_ptr_lower32;
> + hw_base = ((unsigned long long)hw_base_upper32 << 32) + hw_base_lower32;
Instead the check should be here. And to follow the original logic,
if either pointer value is zero, the function should return zero here
directly.
ChenYu
> POINTER_RETURN_FRAMES:
> - return bytes_to_frames(substream->runtime, pcm_ptr_bytes);
> + pcm_ptr_bytes = MTK_ALIGN_16BYTES(hw_ptr - hw_base);
> + return bytes_to_frames(substream->runtime, (ssize_t)pcm_ptr_bytes);
> }
> EXPORT_SYMBOL_GPL(mtk_afe_pcm_pointer);
>
> diff --git a/sound/soc/mediatek/common/mtk-afe-platform-driver.h b/sound/soc/mediatek/common/mtk-afe-platform-driver.h
> index fcc923b88f12..71070b26f8f8 100644
> --- a/sound/soc/mediatek/common/mtk-afe-platform-driver.h
> +++ b/sound/soc/mediatek/common/mtk-afe-platform-driver.h
> @@ -12,6 +12,8 @@
> #define AFE_PCM_NAME "mtk-afe-pcm"
> extern const struct snd_soc_component_driver mtk_afe_pcm_platform;
>
> +#define MTK_ALIGN_16BYTES(x) ((x) & GENMASK_ULL(39, 4))
> +
> struct mtk_base_afe;
> struct snd_pcm;
> struct snd_soc_component;
> --
> 2.45.2
>
>
^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [PATCH v6 06/10] ASoC: mediatek: mt8196: support TDM in platform driver
2025-07-08 11:15 ` [PATCH v6 06/10] ASoC: mediatek: mt8196: support TDM " Darren.Ye
@ 2025-07-28 10:53 ` Chen-Yu Tsai
2025-08-21 8:58 ` Darren Ye (叶飞)
0 siblings, 1 reply; 24+ messages in thread
From: Chen-Yu Tsai @ 2025-07-28 10:53 UTC (permalink / raw)
To: Darren.Ye
Cc: Liam Girdwood, Mark Brown, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Matthias Brugger, AngeloGioacchino Del Regno,
Jaroslav Kysela, Takashi Iwai, Linus Walleij, Bartosz Golaszewski,
linux-sound, devicetree, linux-kernel, linux-arm-kernel,
linux-mediatek, linux-gpio
On Tue, Jul 8, 2025 at 7:34 PM Darren.Ye <darren.ye@mediatek.com> wrote:
>
> From: Darren Ye <darren.ye@mediatek.com>
>
> Add mt8196 TDM DAI driver support.
>
> Signed-off-by: Darren Ye <darren.ye@mediatek.com>
> ---
> sound/soc/mediatek/mt8196/mt8196-dai-tdm.c | 836 +++++++++++++++++++++
> 1 file changed, 836 insertions(+)
> create mode 100644 sound/soc/mediatek/mt8196/mt8196-dai-tdm.c
>
> diff --git a/sound/soc/mediatek/mt8196/mt8196-dai-tdm.c b/sound/soc/mediatek/mt8196/mt8196-dai-tdm.c
> new file mode 100644
> index 000000000000..dcbde41fb61c
> --- /dev/null
> +++ b/sound/soc/mediatek/mt8196/mt8196-dai-tdm.c
> @@ -0,0 +1,836 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * MediaTek ALSA SoC Audio DAI TDM Control
> + *
> + * Copyright (c) 2024 MediaTek Inc.
> + * Author: Darren Ye <darren.ye@mediatek.com>
> + */
> +
> +#include <linux/regmap.h>
> +#include <sound/pcm_params.h>
> +#include "mt8196-afe-clk.h"
> +#include "mt8196-afe-common.h"
> +#include "mt8196-interconnection.h"
> +
> +struct mtk_afe_tdm_priv {
> + int bck_id;
> + int bck_rate;
> +
> + int mclk_id;
> + int mclk_multiple; /* according to sample rate */
> + int mclk_rate;
> + int mclk_apll;
> +};
> +
> +enum {
> + TDM_WLEN_16_BIT = 1,
> + TDM_WLEN_32_BIT = 2,
I believe this was mentioned in another patch, but consecutive values
do not need to be assigned. Nor does a 0 starting value. I won't mention
this below, but please check all the enums.
> +};
> +
> +enum {
> + TDM_CHANNEL_BCK_16 = 0,
> + TDM_CHANNEL_BCK_24 = 1,
> + TDM_CHANNEL_BCK_32 = 2,
> +};
> +
> +enum {
> + TDM_CHANNEL_NUM_2 = 0,
> + TDM_CHANNEL_NUM_4 = 1,
> + TDM_CHANNEL_NUM_8 = 2,
> +};
> +
> +enum {
> + TDM_CH_START_O30_O31 = 0,
> + TDM_CH_START_O32_O33,
> + TDM_CH_START_O34_O35,
> + TDM_CH_START_O36_O37,
> + TDM_CH_ZERO,
> +};
> +
> +enum {
> + DPTX_CHANNEL_2,
> + DPTX_CHANNEL_8,
> +};
> +
> +enum {
> + DPTX_WLEN_24_BIT,
> + DPTX_WLEN_16_BIT,
> +};
> +
> +enum {
> + DPTX_CH_EN_MASK_2CH = 0x3,
> + DPTX_CH_EN_MASK_4CH = 0xf,
> + DPTX_CH_EN_MASK_6CH = 0x3f,
> + DPTX_CH_EN_MASK_8CH = 0xff,
> +};
I'm not entirely confident, but I think normally we use macros for register
values, and enums for internal / software state values.
> +
> +static unsigned int get_tdm_wlen(snd_pcm_format_t format)
> +{
> + return snd_pcm_format_physical_width(format) <= 16 ?
> + TDM_WLEN_16_BIT : TDM_WLEN_32_BIT;
Looking at the datasheet, this also supports 8 bit and 24 bit word lengths?
This could just be written as:
return snd_pcm_format_physical_width(format) / 8;
> +}
> +
> +static unsigned int get_tdm_channel_bck(snd_pcm_format_t format)
> +{
> + return snd_pcm_format_physical_width(format) <= 16 ?
> + TDM_CHANNEL_BCK_16 : TDM_CHANNEL_BCK_32;
I don't think this is correct. I believe this refers to the TDM slot
width, which is separate from how wide or how many bits are valid in
a given sample. The TDM slot width is something set by the machine
driver by calling snd_soc_dai_set_tdm_slot(), much like calling
snd_soc_dai_set_sysclk().
The TDM driver here needs to implement the .set_tdm_slot callback,
and store the slot width and slots.
This function here should be something like:
static unsigned int get_tdm_channel_bck(...)
{
return tdm_slot_width;
}
> +}
> +
> +static unsigned int get_tdm_lrck_width(snd_pcm_format_t format)
> +{
> + return snd_pcm_format_physical_width(format) - 1;
This needs to be multiplied by the number of channels / 2, since
the LRCK spans the entirety of the odd or even number channels.
Also, it should be based on the TDM slot width, not the sample width.
> +}
> +
> +static unsigned int get_tdm_ch(unsigned int ch)
> +{
> + switch (ch) {
> + case 1:
> + case 2:
> + return TDM_CHANNEL_NUM_2;
> + case 3:
> + case 4:
> + return TDM_CHANNEL_NUM_4;
> + case 5:
> + case 6:
> + case 7:
> + case 8:
> + default:
> + return TDM_CHANNEL_NUM_8;
> + }
> +}
> +
> +static unsigned int get_dptx_ch_enable_mask(unsigned int ch)
> +{
> + switch (ch) {
> + case 1:
> + case 2:
> + return DPTX_CH_EN_MASK_2CH;
> + case 3:
> + case 4:
> + return DPTX_CH_EN_MASK_4CH;
> + case 5:
> + case 6:
> + return DPTX_CH_EN_MASK_6CH;
> + case 7:
> + case 8:
> + return DPTX_CH_EN_MASK_8CH;
> + default:
> + pr_info("invalid channel num, default use 2ch\n");
Please pass in |struct device *| and use the dev_printk variants.
And maybe consider making this a warning?
Also there is some inconsistency here,
> + return DPTX_CH_EN_MASK_2CH;
> + }
> +}
> +
> +static unsigned int get_dptx_ch(unsigned int ch)
> +{
> + if (ch == 2)
> + return DPTX_CHANNEL_2;
> + else
> + return DPTX_CHANNEL_8;
> +}
> +
> +static unsigned int get_dptx_wlen(snd_pcm_format_t format)
> +{
> + return snd_pcm_format_physical_width(format) <= 16 ?
> + DPTX_WLEN_16_BIT : DPTX_WLEN_24_BIT;
> +}
> +
> +/* interconnection */
> +enum {
> + HDMI_CONN_CH0 = 0,
> + HDMI_CONN_CH1,
> + HDMI_CONN_CH2,
> + HDMI_CONN_CH3,
> + HDMI_CONN_CH4,
> + HDMI_CONN_CH5,
> + HDMI_CONN_CH6,
> + HDMI_CONN_CH7,
> +};
> +
> +static const char *const hdmi_conn_mux_map[] = {
> + "CH0", "CH1", "CH2", "CH3",
> + "CH4", "CH5", "CH6", "CH7",
Nit: This could fit in one line.
> +};
> +
> +static int hdmi_conn_mux_map_value[] = {
> + HDMI_CONN_CH0,
> + HDMI_CONN_CH1,
> + HDMI_CONN_CH2,
> + HDMI_CONN_CH3,
> + HDMI_CONN_CH4,
> + HDMI_CONN_CH5,
> + HDMI_CONN_CH6,
> + HDMI_CONN_CH7,
Nit: You could fit four values on one line.
> +};
> +
> +static SOC_VALUE_ENUM_SINGLE_DECL(hdmi_ch0_mux_map_enum,
> + AFE_HDMI_CONN0,
> + HDMI_O_0_SFT,
> + HDMI_O_0_MASK,
> + hdmi_conn_mux_map,
> + hdmi_conn_mux_map_value);
> +
> +static const struct snd_kcontrol_new hdmi_ch0_mux_control =
> + SOC_DAPM_ENUM("HDMI_CH0_MUX", hdmi_ch0_mux_map_enum);
Please name the controls based on the standard ALSA control name scheme:
https://docs.kernel.org/sound/designs/control-names.html
This should be something like "HDMI CH0 Source Playback Route". Same
applies to the other ones.
> +
> +static SOC_VALUE_ENUM_SINGLE_DECL(hdmi_ch1_mux_map_enum,
> + AFE_HDMI_CONN0,
> + HDMI_O_1_SFT,
> + HDMI_O_1_MASK,
> + hdmi_conn_mux_map,
> + hdmi_conn_mux_map_value);
> +
> +static const struct snd_kcontrol_new hdmi_ch1_mux_control =
> + SOC_DAPM_ENUM("HDMI_CH1_MUX", hdmi_ch1_mux_map_enum);
> +
> +static SOC_VALUE_ENUM_SINGLE_DECL(hdmi_ch2_mux_map_enum,
> + AFE_HDMI_CONN0,
> + HDMI_O_2_SFT,
> + HDMI_O_2_MASK,
> + hdmi_conn_mux_map,
> + hdmi_conn_mux_map_value);
> +
> +static const struct snd_kcontrol_new hdmi_ch2_mux_control =
> + SOC_DAPM_ENUM("HDMI_CH2_MUX", hdmi_ch2_mux_map_enum);
> +
> +static SOC_VALUE_ENUM_SINGLE_DECL(hdmi_ch3_mux_map_enum,
> + AFE_HDMI_CONN0,
> + HDMI_O_3_SFT,
> + HDMI_O_3_MASK,
> + hdmi_conn_mux_map,
> + hdmi_conn_mux_map_value);
> +
> +static const struct snd_kcontrol_new hdmi_ch3_mux_control =
> + SOC_DAPM_ENUM("HDMI_CH3_MUX", hdmi_ch3_mux_map_enum);
> +
> +static SOC_VALUE_ENUM_SINGLE_DECL(hdmi_ch4_mux_map_enum,
> + AFE_HDMI_CONN0,
> + HDMI_O_4_SFT,
> + HDMI_O_4_MASK,
> + hdmi_conn_mux_map,
> + hdmi_conn_mux_map_value);
> +
> +static const struct snd_kcontrol_new hdmi_ch4_mux_control =
> + SOC_DAPM_ENUM("HDMI_CH4_MUX", hdmi_ch4_mux_map_enum);
> +
> +static SOC_VALUE_ENUM_SINGLE_DECL(hdmi_ch5_mux_map_enum,
> + AFE_HDMI_CONN0,
> + HDMI_O_5_SFT,
> + HDMI_O_5_MASK,
> + hdmi_conn_mux_map,
> + hdmi_conn_mux_map_value);
> +
> +static const struct snd_kcontrol_new hdmi_ch5_mux_control =
> + SOC_DAPM_ENUM("HDMI_CH5_MUX", hdmi_ch5_mux_map_enum);
> +
> +static SOC_VALUE_ENUM_SINGLE_DECL(hdmi_ch6_mux_map_enum,
> + AFE_HDMI_CONN0,
> + HDMI_O_6_SFT,
> + HDMI_O_6_MASK,
> + hdmi_conn_mux_map,
> + hdmi_conn_mux_map_value);
> +
> +static const struct snd_kcontrol_new hdmi_ch6_mux_control =
> + SOC_DAPM_ENUM("HDMI_CH6_MUX", hdmi_ch6_mux_map_enum);
> +
> +static SOC_VALUE_ENUM_SINGLE_DECL(hdmi_ch7_mux_map_enum,
> + AFE_HDMI_CONN0,
> + HDMI_O_7_SFT,
> + HDMI_O_7_MASK,
> + hdmi_conn_mux_map,
> + hdmi_conn_mux_map_value);
> +
> +static const struct snd_kcontrol_new hdmi_ch7_mux_control =
> + SOC_DAPM_ENUM("HDMI_CH7_MUX", hdmi_ch7_mux_map_enum);
> +
> +static const char *const tdm_out_mux_map[] = {
> + "Disconnect", "Connect",
> +};
> +
> +static int tdm_out_mux_map_value[] = {
> + 0, 1,
> +};
> +
> +static SOC_VALUE_ENUM_SINGLE_AUTODISABLE_DECL(hdmi_out_mux_map_enum,
> + SND_SOC_NOPM,
> + 0,
> + 1,
> + tdm_out_mux_map,
> + tdm_out_mux_map_value);
Please align with the left parentheses.
> +static const struct snd_kcontrol_new hdmi_out_mux_control =
> + SOC_DAPM_ENUM("HDMI_OUT_MUX", hdmi_out_mux_map_enum);
> +
> +static SOC_VALUE_ENUM_SINGLE_AUTODISABLE_DECL(dptx_out_mux_map_enum,
> + SND_SOC_NOPM,
> + 0,
> + 1,
> + tdm_out_mux_map,
> + tdm_out_mux_map_value);
Same here.
> +static const struct snd_kcontrol_new dptx_out_mux_control =
> + SOC_DAPM_ENUM("DPTX_OUT_MUX", dptx_out_mux_map_enum);
> +
> +static SOC_VALUE_ENUM_SINGLE_AUTODISABLE_DECL(dptx_virtual_out_mux_map_enum,
> + SND_SOC_NOPM,
> + 0,
> + 1,
> + tdm_out_mux_map,
> + tdm_out_mux_map_value);
Same here.
> +
> +static const struct snd_kcontrol_new dptx_virtual_out_mux_control =
> + SOC_DAPM_ENUM("DPTX_VIRTUAL_OUT_MUX", dptx_virtual_out_mux_map_enum);
> +
> +enum {
> + SUPPLY_SEQ_APLL,
> + SUPPLY_SEQ_TDM_MCK_EN,
> + SUPPLY_SEQ_TDM_BCK_EN,
> + SUPPLY_SEQ_TDM_DPTX_MCK_EN,
> + SUPPLY_SEQ_TDM_DPTX_BCK_EN,
> + SUPPLY_SEQ_TDM_CG_EN,
> +};
> +
> +static int get_tdm_id_by_name(const char *name)
> +{
> + if (strstr(name, "DPTX"))
> + return MT8196_DAI_TDM_DPTX;
> + else
> + return MT8196_DAI_TDM;
> +}
> +
> +static int mtk_tdm_bck_en_event(struct snd_soc_dapm_widget *w,
> + struct snd_kcontrol *kcontrol,
> + int event)
> +{
> + struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
> + struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
> + struct mt8196_afe_private *afe_priv = afe->platform_priv;
> + int dai_id = get_tdm_id_by_name(w->name);
> + struct mtk_afe_tdm_priv *tdm_priv = afe_priv->dai_priv[dai_id];
> +
> + dev_dbg(cmpnt->dev, "name %s, event 0x%x, dai_id %d\n",
> + w->name, event, dai_id);
> +
> + switch (event) {
> + case SND_SOC_DAPM_PRE_PMU:
> + mt8196_mck_enable(afe, tdm_priv->bck_id, tdm_priv->bck_rate);
> + break;
> + case SND_SOC_DAPM_POST_PMD:
> + mt8196_mck_disable(afe, tdm_priv->bck_id);
> + break;
> + default:
> + break;
> + }
> +
> + return 0;
> +}
> +
> +static int mtk_tdm_mck_en_event(struct snd_soc_dapm_widget *w,
> + struct snd_kcontrol *kcontrol,
> + int event)
> +{
> + struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
> + struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
> + struct mt8196_afe_private *afe_priv = afe->platform_priv;
> + int dai_id = get_tdm_id_by_name(w->name);
> + struct mtk_afe_tdm_priv *tdm_priv = afe_priv->dai_priv[dai_id];
> +
> + dev_dbg(cmpnt->dev, "name %s, event 0x%x, dai_id %d\n",
> + w->name, event, dai_id);
> +
> + switch (event) {
> + case SND_SOC_DAPM_PRE_PMU:
> + mt8196_mck_enable(afe, tdm_priv->mclk_id, tdm_priv->mclk_rate);
> + break;
> + case SND_SOC_DAPM_POST_PMD:
> + tdm_priv->mclk_rate = 0;
> + mt8196_mck_disable(afe, tdm_priv->mclk_id);
> + break;
> + default:
> + break;
> + }
> +
> + return 0;
> +}
> +
> +static const struct snd_soc_dapm_widget mtk_dai_tdm_widgets[] = {
> + SND_SOC_DAPM_MUX("HDMI_CH0_MUX", SND_SOC_NOPM, 0, 0,
> + &hdmi_ch0_mux_control),
> + SND_SOC_DAPM_MUX("HDMI_CH1_MUX", SND_SOC_NOPM, 0, 0,
> + &hdmi_ch1_mux_control),
> + SND_SOC_DAPM_MUX("HDMI_CH2_MUX", SND_SOC_NOPM, 0, 0,
> + &hdmi_ch2_mux_control),
> + SND_SOC_DAPM_MUX("HDMI_CH3_MUX", SND_SOC_NOPM, 0, 0,
> + &hdmi_ch3_mux_control),
> + SND_SOC_DAPM_MUX("HDMI_CH4_MUX", SND_SOC_NOPM, 0, 0,
> + &hdmi_ch4_mux_control),
> + SND_SOC_DAPM_MUX("HDMI_CH5_MUX", SND_SOC_NOPM, 0, 0,
> + &hdmi_ch5_mux_control),
> + SND_SOC_DAPM_MUX("HDMI_CH6_MUX", SND_SOC_NOPM, 0, 0,
> + &hdmi_ch6_mux_control),
> + SND_SOC_DAPM_MUX("HDMI_CH7_MUX", SND_SOC_NOPM, 0, 0,
> + &hdmi_ch7_mux_control),
I don't think we really need these widgets. These select which channel
from the input map to which channel on the output. However AFAIK DAPM
doesn't really do multichannel tracking. Also since at least one channel
is getting passed through, the route is always connected and there
really isn't anything for DAPM to manage. Having simple kcontrols
should be enough.
> + SND_SOC_DAPM_MUX("HDMI_OUT_MUX", SND_SOC_NOPM, 0, 0,
This should be named "HDMI_OUT Playback Route". This changes the kcontrol
name seen in userspace. AFAICT, for muxes and demuxes the userspace kcontrol
name comes from the widget, not the underlying kcontrol definition.
> + &hdmi_out_mux_control),
> + SND_SOC_DAPM_MUX("DPTX_OUT_MUX", SND_SOC_NOPM, 0, 0,
This one "DPTX_OUT Playback Route".
> + &dptx_out_mux_control),
> +
> + SND_SOC_DAPM_SUPPLY_S("TDM_BCK", SUPPLY_SEQ_TDM_BCK_EN,
> + SND_SOC_NOPM, 0, 0,
> + mtk_tdm_bck_en_event,
> + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
> +
> + SND_SOC_DAPM_SUPPLY_S("TDM_MCK", SUPPLY_SEQ_TDM_MCK_EN,
> + SND_SOC_NOPM, 0, 0,
> + mtk_tdm_mck_en_event,
> + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
> +
> + SND_SOC_DAPM_SUPPLY_S("TDM_DPTX_BCK", SUPPLY_SEQ_TDM_DPTX_BCK_EN,
> + SND_SOC_NOPM, 0, 0,
> + mtk_tdm_bck_en_event,
> + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
> +
> + SND_SOC_DAPM_SUPPLY_S("TDM_DPTX_MCK", SUPPLY_SEQ_TDM_DPTX_MCK_EN,
> + SND_SOC_NOPM, 0, 0,
> + mtk_tdm_mck_en_event,
> + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
> +
> + /* cg */
> + SND_SOC_DAPM_SUPPLY_S("TDM_CG", SUPPLY_SEQ_TDM_CG_EN,
> + AUDIO_TOP_CON2, PDN_TDM_OUT_SFT, 1,
> + NULL, 0),
> +
> + SND_SOC_DAPM_MUX("DPTX_VIRTUAL_OUT_MUX",
> + SND_SOC_NOPM, 0, 0, &dptx_virtual_out_mux_control),
"DPTX_VIRTUAL_OUT Playback Route"
> + SND_SOC_DAPM_OUTPUT("DPTX_VIRTUAL_OUT"),
> +};
> +
> +static int mtk_afe_tdm_apll_connect(struct snd_soc_dapm_widget *source,
> + struct snd_soc_dapm_widget *sink)
> +{
> + struct snd_soc_dapm_widget *w = sink;
> + struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
Just combine the two? Having the placeholder `w` doesn't help readability.
Instead you could just slightly go over the 80 character limit here.
> + struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
> + struct mt8196_afe_private *afe_priv = afe->platform_priv;
> + int dai_id = get_tdm_id_by_name(w->name);
> + struct mtk_afe_tdm_priv *tdm_priv = afe_priv->dai_priv[dai_id];
> + int cur_apll;
> +
> + /* which apll */
> + cur_apll = mt8196_get_apll_by_name(afe, source->name);
> +
> + return (tdm_priv->mclk_apll == cur_apll) ? 1 : 0;
> +}
> +
> +static const struct snd_soc_dapm_route mtk_dai_tdm_routes[] = {
> + {"HDMI_CH0_MUX", "CH0", "HDMI"},
> + {"HDMI_CH0_MUX", "CH1", "HDMI"},
> + {"HDMI_CH0_MUX", "CH2", "HDMI"},
> + {"HDMI_CH0_MUX", "CH3", "HDMI"},
> + {"HDMI_CH0_MUX", "CH4", "HDMI"},
> + {"HDMI_CH0_MUX", "CH5", "HDMI"},
> + {"HDMI_CH0_MUX", "CH6", "HDMI"},
> + {"HDMI_CH0_MUX", "CH7", "HDMI"},
> +
> + {"HDMI_CH1_MUX", "CH0", "HDMI"},
> + {"HDMI_CH1_MUX", "CH1", "HDMI"},
> + {"HDMI_CH1_MUX", "CH2", "HDMI"},
> + {"HDMI_CH1_MUX", "CH3", "HDMI"},
> + {"HDMI_CH1_MUX", "CH4", "HDMI"},
> + {"HDMI_CH1_MUX", "CH5", "HDMI"},
> + {"HDMI_CH1_MUX", "CH6", "HDMI"},
> + {"HDMI_CH1_MUX", "CH7", "HDMI"},
> +
> + {"HDMI_CH2_MUX", "CH0", "HDMI"},
> + {"HDMI_CH2_MUX", "CH1", "HDMI"},
> + {"HDMI_CH2_MUX", "CH2", "HDMI"},
> + {"HDMI_CH2_MUX", "CH3", "HDMI"},
> + {"HDMI_CH2_MUX", "CH4", "HDMI"},
> + {"HDMI_CH2_MUX", "CH5", "HDMI"},
> + {"HDMI_CH2_MUX", "CH6", "HDMI"},
> + {"HDMI_CH2_MUX", "CH7", "HDMI"},
> +
> + {"HDMI_CH3_MUX", "CH0", "HDMI"},
> + {"HDMI_CH3_MUX", "CH1", "HDMI"},
> + {"HDMI_CH3_MUX", "CH2", "HDMI"},
> + {"HDMI_CH3_MUX", "CH3", "HDMI"},
> + {"HDMI_CH3_MUX", "CH4", "HDMI"},
> + {"HDMI_CH3_MUX", "CH5", "HDMI"},
> + {"HDMI_CH3_MUX", "CH6", "HDMI"},
> + {"HDMI_CH3_MUX", "CH7", "HDMI"},
> +
> + {"HDMI_CH4_MUX", "CH0", "HDMI"},
> + {"HDMI_CH4_MUX", "CH1", "HDMI"},
> + {"HDMI_CH4_MUX", "CH2", "HDMI"},
> + {"HDMI_CH4_MUX", "CH3", "HDMI"},
> + {"HDMI_CH4_MUX", "CH4", "HDMI"},
> + {"HDMI_CH4_MUX", "CH5", "HDMI"},
> + {"HDMI_CH4_MUX", "CH6", "HDMI"},
> + {"HDMI_CH4_MUX", "CH7", "HDMI"},
> +
> + {"HDMI_CH5_MUX", "CH0", "HDMI"},
> + {"HDMI_CH5_MUX", "CH1", "HDMI"},
> + {"HDMI_CH5_MUX", "CH2", "HDMI"},
> + {"HDMI_CH5_MUX", "CH3", "HDMI"},
> + {"HDMI_CH5_MUX", "CH4", "HDMI"},
> + {"HDMI_CH5_MUX", "CH5", "HDMI"},
> + {"HDMI_CH5_MUX", "CH6", "HDMI"},
> + {"HDMI_CH5_MUX", "CH7", "HDMI"},
> +
> + {"HDMI_CH6_MUX", "CH0", "HDMI"},
> + {"HDMI_CH6_MUX", "CH1", "HDMI"},
> + {"HDMI_CH6_MUX", "CH2", "HDMI"},
> + {"HDMI_CH6_MUX", "CH3", "HDMI"},
> + {"HDMI_CH6_MUX", "CH4", "HDMI"},
> + {"HDMI_CH6_MUX", "CH5", "HDMI"},
> + {"HDMI_CH6_MUX", "CH6", "HDMI"},
> + {"HDMI_CH6_MUX", "CH7", "HDMI"},
> +
> + {"HDMI_CH7_MUX", "CH0", "HDMI"},
> + {"HDMI_CH7_MUX", "CH1", "HDMI"},
> + {"HDMI_CH7_MUX", "CH2", "HDMI"},
> + {"HDMI_CH7_MUX", "CH3", "HDMI"},
> + {"HDMI_CH7_MUX", "CH4", "HDMI"},
> + {"HDMI_CH7_MUX", "CH5", "HDMI"},
> + {"HDMI_CH7_MUX", "CH6", "HDMI"},
> + {"HDMI_CH7_MUX", "CH7", "HDMI"},
> +
> + {"HDMI_OUT_MUX", "Connect", "HDMI_CH0_MUX"},
> + {"HDMI_OUT_MUX", "Connect", "HDMI_CH1_MUX"},
> + {"HDMI_OUT_MUX", "Connect", "HDMI_CH2_MUX"},
> + {"HDMI_OUT_MUX", "Connect", "HDMI_CH3_MUX"},
> + {"HDMI_OUT_MUX", "Connect", "HDMI_CH4_MUX"},
> + {"HDMI_OUT_MUX", "Connect", "HDMI_CH5_MUX"},
> + {"HDMI_OUT_MUX", "Connect", "HDMI_CH6_MUX"},
> + {"HDMI_OUT_MUX", "Connect", "HDMI_CH7_MUX"},
> +
> + {"DPTX_OUT_MUX", "Connect", "HDMI_CH0_MUX"},
> + {"DPTX_OUT_MUX", "Connect", "HDMI_CH1_MUX"},
> + {"DPTX_OUT_MUX", "Connect", "HDMI_CH2_MUX"},
> + {"DPTX_OUT_MUX", "Connect", "HDMI_CH3_MUX"},
> + {"DPTX_OUT_MUX", "Connect", "HDMI_CH4_MUX"},
> + {"DPTX_OUT_MUX", "Connect", "HDMI_CH5_MUX"},
> + {"DPTX_OUT_MUX", "Connect", "HDMI_CH6_MUX"},
> + {"DPTX_OUT_MUX", "Connect", "HDMI_CH7_MUX"},
As mentioned above the channel mux widgets don't really do anything.
Just use normal kcontrols and the aboue routes can be simplified to just
{ "HDMI_OUT_MUX", "Connect", "HDMI" }
{ "DPTX_OUT_MUX", "Connect", "HDMI" },
Also note that there should be one space between the braces ("{}") and
the elements.
> + {"TDM", NULL, "HDMI_OUT_MUX"},
> + {"TDM", NULL, "TDM_BCK"},
> + {"TDM", NULL, "TDM_CG"},
> +
> + {"TDM_DPTX", NULL, "DPTX_OUT_MUX"},
> + {"TDM_DPTX", NULL, "TDM_DPTX_BCK"},
> + {"TDM_DPTX", NULL, "TDM_CG"},
> +
> + {"TDM_BCK", NULL, "TDM_MCK"},
> + {"TDM_DPTX_BCK", NULL, "TDM_DPTX_MCK"},
> + {"TDM_MCK", NULL, APLL1_W_NAME, mtk_afe_tdm_apll_connect},
> + {"TDM_MCK", NULL, APLL2_W_NAME, mtk_afe_tdm_apll_connect},
> + {"TDM_DPTX_MCK", NULL, APLL1_W_NAME, mtk_afe_tdm_apll_connect},
> + {"TDM_DPTX_MCK", NULL, APLL2_W_NAME, mtk_afe_tdm_apll_connect},
> +
> + {"DPTX_VIRTUAL_OUT_MUX", "Connect", "TDM_DPTX"},
> + {"DPTX_VIRTUAL_OUT", NULL, "DPTX_VIRTUAL_OUT_MUX"},
> +};
> +
> +/* dai ops */
> +static int mtk_dai_tdm_cal_mclk(struct mtk_base_afe *afe,
> + struct mtk_afe_tdm_priv *tdm_priv,
> + int freq)
> +{
> + int apll;
> + int apll_rate;
> +
> + apll = mt8196_get_apll_by_rate(afe, freq);
> + apll_rate = mt8196_get_apll_rate(afe, apll);
> +
> + if (freq > apll_rate)
> + return -EINVAL;
> +
> + if (apll_rate % freq != 0)
> + return -EINVAL;
> +
> + tdm_priv->mclk_rate = freq;
> + tdm_priv->mclk_apll = apll;
> +
> + return 0;
> +}
> +
> +static int mtk_dai_tdm_hw_params(struct snd_pcm_substream *substream,
> + struct snd_pcm_hw_params *params,
> + struct snd_soc_dai *dai)
> +{
> + struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
> + struct mt8196_afe_private *afe_priv = afe->platform_priv;
> + int tdm_id = dai->id;
> + struct mtk_afe_tdm_priv *tdm_priv;
> + unsigned int rate = params_rate(params);
> + unsigned int channels = params_channels(params);
> + snd_pcm_format_t format = params_format(params);
> + unsigned int tdm_con = 0;
> +
> + if (tdm_id >= MT8196_DAI_NUM || tdm_id < 0)
> + return -EINVAL;
> +
> + tdm_priv = afe_priv->dai_priv[tdm_id];
> +
> + if (!tdm_priv)
> + return -EINVAL;
> +
> + /* calculate mclk_rate, if not set explicitly */
> + if (!tdm_priv->mclk_rate) {
> + tdm_priv->mclk_rate = rate * tdm_priv->mclk_multiple;
> + mtk_dai_tdm_cal_mclk(afe,
> + tdm_priv,
> + tdm_priv->mclk_rate);
> + }
> +
> + /* calculate bck */
> + tdm_priv->bck_rate = rate *
> + channels *
> + snd_pcm_format_physical_width(format);
> +
> + if (tdm_priv->bck_rate > tdm_priv->mclk_rate)
> + return -EINVAL;
> +
> + if (tdm_priv->mclk_rate % tdm_priv->bck_rate != 0)
> + return -EINVAL;
> +
> + dev_info(afe->dev, "id %d, rate %d, channels %d, format %d, mclk_rate %d, bck_rate %d\n",
> + tdm_id, rate, channels, format,
> + tdm_priv->mclk_rate, tdm_priv->bck_rate);
Please make this debug level.
> +
> + /* set tdm */
> + tdm_con = 0 << BCK_INVERSE_SFT;
> + tdm_con |= 0 << LRCK_INVERSE_SFT;
> + tdm_con |= 0 << DELAY_DATA_SFT;
> + tdm_con |= 1 << LEFT_ALIGN_SFT;
> + tdm_con |= get_tdm_wlen(format) << WLEN_SFT;
> + tdm_con |= get_tdm_ch(channels) << CHANNEL_NUM_SFT;
> + tdm_con |= get_tdm_channel_bck(format) << CHANNEL_BCK_CYCLES_SFT;
> + tdm_con |= get_tdm_lrck_width(format) << LRCK_TDM_WIDTH_SFT;
> + regmap_write(afe->regmap, AFE_TDM_CON1, tdm_con);
> +
> + /* set dptx */
> + if (tdm_id == MT8196_DAI_TDM_DPTX) {
> + regmap_update_bits(afe->regmap, AFE_DPTX_CON,
> + DPTX_CHANNEL_ENABLE_MASK_SFT,
> + get_dptx_ch_enable_mask(channels) <<
> + DPTX_CHANNEL_ENABLE_SFT);
> + regmap_update_bits(afe->regmap, AFE_DPTX_CON,
> + DPTX_CHANNEL_NUMBER_MASK_SFT,
> + get_dptx_ch(channels) <<
> + DPTX_CHANNEL_NUMBER_SFT);
> + regmap_update_bits(afe->regmap, AFE_DPTX_CON,
> + DPTX_16BIT_MASK_SFT,
> + get_dptx_wlen(format) << DPTX_16BIT_SFT);
> + }
> + switch (channels) {
> + case 1:
> + case 2:
> + tdm_con = TDM_CH_START_O30_O31 << ST_CH_PAIR_SOUT0_SFT;
> + tdm_con |= TDM_CH_ZERO << ST_CH_PAIR_SOUT1_SFT;
> + tdm_con |= TDM_CH_ZERO << ST_CH_PAIR_SOUT2_SFT;
> + tdm_con |= TDM_CH_ZERO << ST_CH_PAIR_SOUT3_SFT;
> + break;
> + case 3:
> + case 4:
> + tdm_con = TDM_CH_START_O30_O31 << ST_CH_PAIR_SOUT0_SFT;
> + tdm_con |= TDM_CH_START_O32_O33 << ST_CH_PAIR_SOUT1_SFT;
> + tdm_con |= TDM_CH_ZERO << ST_CH_PAIR_SOUT2_SFT;
> + tdm_con |= TDM_CH_ZERO << ST_CH_PAIR_SOUT3_SFT;
> + break;
> + case 5:
> + case 6:
> + tdm_con = TDM_CH_START_O30_O31 << ST_CH_PAIR_SOUT0_SFT;
> + tdm_con |= TDM_CH_START_O32_O33 << ST_CH_PAIR_SOUT1_SFT;
> + tdm_con |= TDM_CH_START_O34_O35 << ST_CH_PAIR_SOUT2_SFT;
> + tdm_con |= TDM_CH_ZERO << ST_CH_PAIR_SOUT3_SFT;
> + break;
> + case 7:
> + case 8:
> + tdm_con = TDM_CH_START_O30_O31 << ST_CH_PAIR_SOUT0_SFT;
> + tdm_con |= TDM_CH_START_O32_O33 << ST_CH_PAIR_SOUT1_SFT;
> + tdm_con |= TDM_CH_START_O34_O35 << ST_CH_PAIR_SOUT2_SFT;
> + tdm_con |= TDM_CH_START_O36_O37 << ST_CH_PAIR_SOUT3_SFT;
> + break;
> + default:
> + tdm_con = 0;
> + }
The indentation for this block is incorrect.
> + regmap_write(afe->regmap, AFE_TDM_CON2, tdm_con);
> + regmap_update_bits(afe->regmap, AFE_HDMI_OUT_CON0,
> + HDMI_CH_NUM_MASK_SFT,
> + channels << HDMI_CH_NUM_SFT);
> +
> + return 0;
> +}
> +
> +static int mtk_dai_tdm_trigger(struct snd_pcm_substream *substream,
> + int cmd,
> + struct snd_soc_dai *dai)
> +{
> + struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
> + int tdm_id = dai->id;
> +
> + dev_dbg(afe->dev, "cmd %d, tdm_id %d\n", cmd, tdm_id);
> +
> + switch (cmd) {
> + case SNDRV_PCM_TRIGGER_START:
> + case SNDRV_PCM_TRIGGER_RESUME:
> + /* enable Out control */
> + regmap_update_bits(afe->regmap, AFE_HDMI_OUT_CON0,
> + HDMI_OUT_ON_MASK_SFT,
> + 0x1 << HDMI_OUT_ON_SFT);
This is already controlled from the "HDMI" PCM component driver.
The DAI driver should not touch it.
> +
> + /* enable dptx */
> + if (tdm_id == MT8196_DAI_TDM_DPTX) {
> + regmap_update_bits(afe->regmap, AFE_DPTX_CON,
> + DPTX_ON_MASK_SFT, 0x1 <<
> + DPTX_ON_SFT);
> + }
> +
> + /* enable tdm */
> + regmap_update_bits(afe->regmap, AFE_TDM_CON1,
> + TDM_EN_MASK_SFT, 0x1 << TDM_EN_SFT);
> + break;
> + case SNDRV_PCM_TRIGGER_STOP:
> + case SNDRV_PCM_TRIGGER_SUSPEND:
> + /* disable tdm */
> + regmap_update_bits(afe->regmap, AFE_TDM_CON1,
> + TDM_EN_MASK_SFT, 0);
> +
> + /* disable dptx */
> + if (tdm_id == MT8196_DAI_TDM_DPTX) {
> + regmap_update_bits(afe->regmap, AFE_DPTX_CON,
> + DPTX_ON_MASK_SFT, 0);
> + }
> +
> + /* disable Out control */
> + regmap_update_bits(afe->regmap, AFE_HDMI_OUT_CON0,
> + HDMI_OUT_ON_MASK_SFT, 0);
> + break;
> + default:
> + return -EINVAL;
> + }
> +
> + return 0;
> +}
> +
> +static int mtk_dai_tdm_set_sysclk(struct snd_soc_dai *dai,
> + int clk_id, unsigned int freq, int dir)
> +{
> + struct mtk_base_afe *afe = dev_get_drvdata(dai->dev);
> + struct mt8196_afe_private *afe_priv = afe->platform_priv;
> + struct mtk_afe_tdm_priv *tdm_priv;
> +
> + if (dai->id >= MT8196_DAI_NUM || dai->id < 0)
> + return -EINVAL;
> +
> + tdm_priv = afe_priv->dai_priv[dai->id];
> +
> + if (!tdm_priv)
> + return -EINVAL;
> +
> + if (dir != SND_SOC_CLOCK_OUT)
> + return -EINVAL;
> +
> + dev_dbg(afe->dev, "freq %d\n", freq);
> +
> + return mtk_dai_tdm_cal_mclk(afe, tdm_priv, freq);
> +}
> +
> +static const struct snd_soc_dai_ops mtk_dai_tdm_ops = {
> + .hw_params = mtk_dai_tdm_hw_params,
> + .trigger = mtk_dai_tdm_trigger,
> + .set_sysclk = mtk_dai_tdm_set_sysclk,
> +};
> +
> +/* dai driver */
> +#define MTK_TDM_RATES (SNDRV_PCM_RATE_8000_48000 |\
> + SNDRV_PCM_RATE_88200 |\
> + SNDRV_PCM_RATE_96000 |\
> + SNDRV_PCM_RATE_176400 |\
> + SNDRV_PCM_RATE_192000)
> +
> +#define MTK_TDM_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\
> + SNDRV_PCM_FMTBIT_S24_LE |\
> + SNDRV_PCM_FMTBIT_S32_LE)
> +
> +static struct snd_soc_dai_driver mtk_dai_tdm_driver[] = {
> + {
> + .name = "TDM",
> + .id = MT8196_DAI_TDM,
> + .playback = {
> + .stream_name = "TDM",
> + .channels_min = 2,
> + .channels_max = 8,
> + .rates = MTK_TDM_RATES,
> + .formats = MTK_TDM_FORMATS,
> + },
> + .ops = &mtk_dai_tdm_ops,
> + },
> + {
> + .name = "TDM_DPTX",
> + .id = MT8196_DAI_TDM_DPTX,
> + .playback = {
> + .stream_name = "TDM_DPTX",
> + .channels_min = 2,
> + .channels_max = 8,
> + .rates = MTK_TDM_RATES,
> + .formats = MTK_TDM_FORMATS,
> + },
> + .ops = &mtk_dai_tdm_ops,
> + },
> +};
> +
> +static struct mtk_afe_tdm_priv *init_tdm_priv_data(struct mtk_base_afe *afe,
> + int id)
> +{
> + struct mtk_afe_tdm_priv *tdm_priv;
> +
> + tdm_priv = devm_kzalloc(afe->dev, sizeof(struct mtk_afe_tdm_priv),
> + GFP_KERNEL);
> + if (!tdm_priv)
> + return NULL;
> +
> + if (id == MT8196_DAI_TDM_DPTX)
> + tdm_priv->mclk_multiple = 256;
> + else
> + tdm_priv->mclk_multiple = 128;
> +
> + tdm_priv->bck_id = MT8196_TDMOUT_BCK;
> + tdm_priv->mclk_id = MT8196_TDMOUT_MCK;
> +
> + return tdm_priv;
> +}
> +
> +int mt8196_dai_tdm_register(struct mtk_base_afe *afe)
> +{
> + struct mt8196_afe_private *afe_priv = afe->platform_priv;
> + struct mtk_afe_tdm_priv *tdm_priv, *tdm_dptx_priv;
> + 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);
This should be the final step in this function, after everything has
been initialized correctly.
> +
> + dai->dai_drivers = mtk_dai_tdm_driver;
> + dai->num_dai_drivers = ARRAY_SIZE(mtk_dai_tdm_driver);
> +
> + dai->dapm_widgets = mtk_dai_tdm_widgets;
> + dai->num_dapm_widgets = ARRAY_SIZE(mtk_dai_tdm_widgets);
> + dai->dapm_routes = mtk_dai_tdm_routes;
> + dai->num_dapm_routes = ARRAY_SIZE(mtk_dai_tdm_routes);
> +
> + tdm_priv = init_tdm_priv_data(afe, MT8196_DAI_TDM);
> + if (!tdm_priv)
> + return -ENOMEM;
Or you end up with a bad entry in the list here.
> +
> + tdm_dptx_priv = init_tdm_priv_data(afe, MT8196_DAI_TDM_DPTX);
> + if (!tdm_dptx_priv)
> + return -ENOMEM;
Or here.
ChenYu
> +
> + afe_priv->dai_priv[MT8196_DAI_TDM] = tdm_priv;
> + afe_priv->dai_priv[MT8196_DAI_TDM_DPTX] = tdm_dptx_priv;
> +
> + return 0;
> +}
> +
> --
> 2.45.2
>
>
^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [PATCH v6 04/10] ASoC: mediatek: mt8196: support ADDA in platform driver
2025-07-08 11:15 ` [PATCH v6 04/10] ASoC: mediatek: mt8196: support ADDA in platform driver Darren.Ye
@ 2025-07-29 11:45 ` Chen-Yu Tsai
0 siblings, 0 replies; 24+ messages in thread
From: Chen-Yu Tsai @ 2025-07-29 11:45 UTC (permalink / raw)
To: Darren.Ye
Cc: Liam Girdwood, Mark Brown, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Matthias Brugger, AngeloGioacchino Del Regno,
Jaroslav Kysela, Takashi Iwai, Linus Walleij, Bartosz Golaszewski,
linux-sound, devicetree, linux-kernel, linux-arm-kernel,
linux-mediatek, linux-gpio
On Tue, Jul 8, 2025 at 7:34 PM Darren.Ye <darren.ye@mediatek.com> wrote:
>
> From: Darren Ye <darren.ye@mediatek.com>
>
> Add mt8196 ADDA DAI driver support.
>
> Signed-off-by: Darren Ye <darren.ye@mediatek.com>
> ---
> sound/soc/mediatek/mt8196/mt8196-dai-adda.c | 888 ++++++++++++++++++++
> 1 file changed, 888 insertions(+)
> create mode 100644 sound/soc/mediatek/mt8196/mt8196-dai-adda.c
>
> diff --git a/sound/soc/mediatek/mt8196/mt8196-dai-adda.c b/sound/soc/mediatek/mt8196/mt8196-dai-adda.c
> new file mode 100644
> index 000000000000..e44332ffd0c4
> --- /dev/null
> +++ b/sound/soc/mediatek/mt8196/mt8196-dai-adda.c
> @@ -0,0 +1,888 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * MediaTek ALSA SoC Audio DAI ADDA Control
> + *
> + * Copyright (c) 2024 MediaTek Inc.
> + * Author: Darren Ye <darren.ye@mediatek.com>
> + */
> +
> +#include <linux/regmap.h>
> +#include <linux/delay.h>
Please sort by name. And add an empty line here to separate the groups.
> +#include "mt8196-afe-clk.h"
> +#include "mt8196-afe-common.h"
> +#include "mt8196-interconnection.h"
> +
> +enum {
> + UL_IIR_SW = 0,
> + UL_IIR_5HZ,
> + UL_IIR_10HZ,
> + UL_IIR_25HZ,
> + UL_IIR_50HZ,
> + UL_IIR_75HZ,
> +};
> +
> +enum {
> + MTK_AFE_ADDA_UL_RATE_8K = 0,
> + MTK_AFE_ADDA_UL_RATE_16K = 1,
> + MTK_AFE_ADDA_UL_RATE_32K = 2,
> + MTK_AFE_ADDA_UL_RATE_48K = 3,
> + MTK_AFE_ADDA_UL_RATE_96K = 4,
> + MTK_AFE_ADDA_UL_RATE_192K = 5,
> + MTK_AFE_ADDA_UL_RATE_48K_HD = 6,
> +};
> +
> +enum {
> + MTK_AFE_MTKAIF_RATE_8K = 0x0,
> + MTK_AFE_MTKAIF_RATE_12K = 0x1,
> + MTK_AFE_MTKAIF_RATE_16K = 0x2,
> + MTK_AFE_MTKAIF_RATE_24K = 0x3,
> + MTK_AFE_MTKAIF_RATE_32K = 0x4,
> + MTK_AFE_MTKAIF_RATE_48K = 0x5,
> + MTK_AFE_MTKAIF_RATE_64K = 0x6,
> + MTK_AFE_MTKAIF_RATE_96K = 0x7,
> + MTK_AFE_MTKAIF_RATE_128K = 0x8,
> + MTK_AFE_MTKAIF_RATE_192K = 0x9,
> + MTK_AFE_MTKAIF_RATE_256K = 0xa,
> + MTK_AFE_MTKAIF_RATE_384K = 0xb,
> + MTK_AFE_MTKAIF_RATE_11K = 0x10,
> + MTK_AFE_MTKAIF_RATE_22K = 0x11,
> + MTK_AFE_MTKAIF_RATE_44K = 0x12,
> + MTK_AFE_MTKAIF_RATE_88K = 0x13,
> + MTK_AFE_MTKAIF_RATE_176K = 0x14,
> + MTK_AFE_MTKAIF_RATE_352K = 0x15,
Same comments from other patches about enum value assignments and macros
apply here as well.
> +};
> +
> +struct mtk_afe_adda_priv {
> + int dl_rate;
> + int ul_rate;
> +};
> +
> +static unsigned int adda_ul_rate_transform(struct mtk_base_afe *afe,
> + unsigned int rate)
> +{
> + switch (rate) {
> + case 8000:
> + return MTK_AFE_ADDA_UL_RATE_8K;
> + case 16000:
> + return MTK_AFE_ADDA_UL_RATE_16K;
> + case 32000:
> + return MTK_AFE_ADDA_UL_RATE_32K;
> + case 48000:
> + return MTK_AFE_ADDA_UL_RATE_48K;
> + case 96000:
> + return MTK_AFE_ADDA_UL_RATE_96K;
> + case 192000:
> + return MTK_AFE_ADDA_UL_RATE_192K;
> + default:
> + dev_info(afe->dev, "rate %d invalid, use 48kHz!!!\n", rate);
Make it a warning.
> + return MTK_AFE_ADDA_UL_RATE_48K;
> + }
> +}
> +
> +static unsigned int mtkaif_rate_transform(struct mtk_base_afe *afe,
> + unsigned int rate)
> +{
> + switch (rate) {
> + case 8000:
> + return MTK_AFE_MTKAIF_RATE_8K;
> + case 11025:
> + return MTK_AFE_MTKAIF_RATE_11K;
> + case 12000:
> + return MTK_AFE_MTKAIF_RATE_12K;
> + case 16000:
> + return MTK_AFE_MTKAIF_RATE_16K;
> + case 22050:
> + return MTK_AFE_MTKAIF_RATE_22K;
> + case 24000:
> + return MTK_AFE_MTKAIF_RATE_24K;
> + case 32000:
> + return MTK_AFE_MTKAIF_RATE_32K;
> + case 44100:
> + return MTK_AFE_MTKAIF_RATE_44K;
> + case 48000:
> + return MTK_AFE_MTKAIF_RATE_48K;
> + case 96000:
> + return MTK_AFE_MTKAIF_RATE_96K;
> + case 192000:
> + return MTK_AFE_MTKAIF_RATE_192K;
> + default:
> + dev_info(afe->dev, "rate %d invalid, use 48kHz!!!\n", rate);
Make it a warning.
> + return MTK_AFE_MTKAIF_RATE_48K;
> + }
> +}
> +
> +enum {
> + SUPPLY_SEQ_ADDA_AFE_ON,
> + SUPPLY_SEQ_ADDA_FIFO,
> + SUPPLY_SEQ_ADDA_AP_DMIC,
> + SUPPLY_SEQ_ADDA_UL_ON,
> +};
> +
> +static int mtk_adda_ul_src_dmic_phase_sync(struct mtk_base_afe *afe)
mtk_adda_ul_set_src_dmic_phase_sync() ?
Without a verb in the name, it's unclear what this function does.
[...]
> +
> +static int mtk_adda_ul_src_dmic_phase_sync_clock(struct mtk_base_afe *afe)
mtk_adda_ul_set_src_dmic_phase_sync_clock() ?
[...]
> +
> +static int mtk_adda_ul_src_dmic(struct mtk_base_afe *afe, int id)
mtk_adda_ul_enable_src_dmic() ?
> +{
> + unsigned int reg_con0 = 0, reg_con1 = 0;
> +
> + dev_dbg(afe->dev, "id: %d\n", id);
> +
> + switch (id) {
> + case MT8196_DAI_ADDA:
> + case MT8196_DAI_AP_DMIC:
> + reg_con0 = AFE_ADDA_UL0_SRC_CON0;
> + reg_con1 = AFE_ADDA_UL0_SRC_CON1;
> + break;
> + case MT8196_DAI_ADDA_CH34:
> + case MT8196_DAI_AP_DMIC_CH34:
> + reg_con0 = AFE_ADDA_UL1_SRC_CON0;
> + reg_con1 = AFE_ADDA_UL1_SRC_CON1;
> + break;
> + default:
> + return -EINVAL;
> + }
> +
> + switch (id) {
> + case MT8196_DAI_AP_DMIC:
> + dev_dbg(afe->dev, "clear mtkaifv4 ul ch1ch2 mux\n");
> + regmap_update_bits(afe->regmap, AFE_ADDA_MTKAIFV4_RX_CFG0,
> + MTKAIFV4_UL_CH1CH2_IN_EN_SEL_MASK_SFT,
> + 0x0 << MTKAIFV4_UL_CH1CH2_IN_EN_SEL_SFT);
> + break;
> + case MT8196_DAI_AP_DMIC_CH34:
> + dev_dbg(afe->dev, "clear mtkaifv4 ul ch3ch4 mux\n");
> + regmap_update_bits(afe->regmap, AFE_ADDA_MTKAIFV4_RX_CFG0,
> + MTKAIFV4_UL_CH3CH4_IN_EN_SEL_MASK_SFT,
> + 0x0 << MTKAIFV4_UL_CH3CH4_IN_EN_SEL_SFT);
> + break;
> + default:
> + return -EINVAL;
> + }
> +
> + /* choose Phase */
> + regmap_update_bits(afe->regmap, reg_con0,
> + UL_DMIC_PHASE_SEL_CH1_MASK_SFT,
> + 0x0 << UL_DMIC_PHASE_SEL_CH1_SFT);
> + regmap_update_bits(afe->regmap, reg_con0,
> + UL_DMIC_PHASE_SEL_CH2_MASK_SFT,
> + 0x4 << UL_DMIC_PHASE_SEL_CH2_SFT);
> +
> + /* dmic mode, 3.25M*/
> + regmap_update_bits(afe->regmap, reg_con0,
> + DIGMIC_3P25M_1P625M_SEL_CTL_MASK_SFT,
> + 0x0);
> + regmap_update_bits(afe->regmap, reg_con0,
> + DMIC_LOW_POWER_MODE_CTL_MASK_SFT,
> + 0x0);
> +
> + /* turn on dmic, ch1, ch2 */
> + regmap_update_bits(afe->regmap, reg_con0,
> + UL_SDM_3_LEVEL_CTL_MASK_SFT,
> + 0x1 << UL_SDM_3_LEVEL_CTL_SFT);
> + regmap_update_bits(afe->regmap, reg_con0,
> + UL_MODE_3P25M_CH1_CTL_MASK_SFT,
> + 0x1 << UL_MODE_3P25M_CH1_CTL_SFT);
> + regmap_update_bits(afe->regmap, reg_con0,
> + UL_MODE_3P25M_CH2_CTL_MASK_SFT,
> + 0x1 << UL_MODE_3P25M_CH2_CTL_SFT);
> +
> + /* ul gain: gain = 0x7fff/positive_gain = 0x0/gain_mode = 0x10 */
> + regmap_update_bits(afe->regmap, reg_con1,
> + ADDA_UL_GAIN_VALUE_MASK_SFT,
> + 0x7fff << ADDA_UL_GAIN_VALUE_SFT);
> + regmap_update_bits(afe->regmap, reg_con1,
> + ADDA_UL_POSTIVEGAIN_MASK_SFT,
> + 0x0 << ADDA_UL_POSTIVEGAIN_SFT);
> + /* gain_mode = 0x10: Add 0.5 gain at CIC output */
> + regmap_update_bits(afe->regmap, reg_con1,
> + GAIN_MODE_MASK_SFT,
> + 0x02 << GAIN_MODE_SFT);
> + return 0;
> +}
> +
> +static int mtk_adda_ul_event(struct snd_soc_dapm_widget *w,
> + struct snd_kcontrol *kcontrol,
> + int event)
> +{
> + struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
> + struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
> +
> + dev_dbg(afe->dev, "name %s, event 0x%x\n", w->name, event);
> +
> + switch (event) {
> + case SND_SOC_DAPM_PRE_PMU:
> + break;
> + case SND_SOC_DAPM_POST_PMD:
> + /* should delayed 1/fs(smallest is 8k) = 125us before afe off */
> + usleep_range(120, 130);
> + break;
> + default:
> + break;
> + }
> +
> + return 0;
> +}
> +
> +static int mtk_adda_ch34_ul_event(struct snd_soc_dapm_widget *w,
> + struct snd_kcontrol *kcontrol,
> + int event)
> +{
> + struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
> + struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
> +
> + dev_dbg(afe->dev, "name %s, event 0x%x\n", w->name, event);
> +
> + switch (event) {
> + case SND_SOC_DAPM_PRE_PMU:
> + break;
> + case SND_SOC_DAPM_POST_PMD:
> + /* should delayed 1/fs(smallest is 8k) = 125us before afe off */
> + usleep_range(120, 130);
> + break;
> + default:
> + break;
> + }
> +
> + return 0;
> +}
> +
> +static int mtk_adda_ul_ap_dmic_event(struct snd_soc_dapm_widget *w,
> + struct snd_kcontrol *kcontrol,
> + int event)
> +{
> + struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
> + struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
> +
> + dev_info(afe->dev, "name %s, event 0x%x\n", w->name, event);
> +
> + switch (event) {
> + case SND_SOC_DAPM_PRE_PMU:
> + break;
> + case SND_SOC_DAPM_POST_PMD:
> + /* should delayed 1/fs(smallest is 8k) = 125us before afe off */
> + usleep_range(120, 130);
> + break;
> + default:
> + break;
> + }
> +
> + return 0;
> +}
> +
> +static int mtk_adda_ch34_ul_ap_dmic_event(struct snd_soc_dapm_widget *w,
> + struct snd_kcontrol *kcontrol,
> + int event)
> +{
> + struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
> + struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
> +
> + dev_dbg(afe->dev, "name %s, event 0x%x\n", w->name, event);
> +
> + switch (event) {
> + case SND_SOC_DAPM_PRE_PMU:
> + break;
> + case SND_SOC_DAPM_POST_PMD:
> + /* should delayed 1/fs(smallest is 8k) = 125us before afe off */
> + usleep_range(120, 130);
> + break;
> + default:
> + break;
> + }
> +
> + return 0;
> +}
This function is duplicated four times. Please just have one copy.
And name it something like "mtk_adda_sleep_on_pmd_event(...)".
> +
> +static const struct snd_kcontrol_new mtk_adda_controls[] = {
> +};
Please remove this if it is empty.
> +
> +/* ADDA UL MUX */
> +#define ADDA_UL_MUX_MASK 0x3
> +enum {
> + ADDA_UL_MUX_MTKAIF = 0,
> + ADDA_UL_MUX_AP_DMIC,
> + ADDA_UL_MUX_AP_DMIC_MULTICH,
> +};
> +
> +static const char *const adda_ul_mux_map[] = {
> + "MTKAIF", "AP_DMIC", "AP_DMIC_MULTI_CH",
> +};
> +
> +static int adda_ul_map_value[] = {
> + ADDA_UL_MUX_MTKAIF,
> + ADDA_UL_MUX_AP_DMIC,
> + ADDA_UL_MUX_AP_DMIC_MULTICH,
> +};
> +
> +static SOC_VALUE_ENUM_SINGLE_DECL(adda_ul_mux_map_enum,
> + SND_SOC_NOPM,
> + 0,
> + ADDA_UL_MUX_MASK,
> + adda_ul_mux_map,
> + adda_ul_map_value);
> +
> +static const struct snd_kcontrol_new adda_ul_mux_control =
> + SOC_DAPM_ENUM("ADDA_UL_MUX Select", adda_ul_mux_map_enum);
> +
> +static const struct snd_kcontrol_new adda_ch34_ul_mux_control =
> + SOC_DAPM_ENUM("ADDA_CH34_UL_MUX Select", adda_ul_mux_map_enum);
> +
> +static const struct snd_soc_dapm_widget mtk_dai_adda_widgets[] = {
> + /* inter-connections */
> + SND_SOC_DAPM_SUPPLY_S("ADDA Enable", SUPPLY_SEQ_ADDA_AFE_ON,
> + AUDIO_ENGEN_CON0, AUDIO_F3P25M_EN_ON_SFT, 0,
> + NULL, 0),
> + SND_SOC_DAPM_SUPPLY_S("ADDA Capture Enable", SUPPLY_SEQ_ADDA_UL_ON,
> + AFE_ADDA_UL0_SRC_CON0,
> + UL_SRC_ON_TMP_CTL_SFT, 0,
> + mtk_adda_ul_event,
> + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
The event callback does nothing for SND_SOC_DAPM_PRE_PMU. Please drop the flag.
> + SND_SOC_DAPM_SUPPLY_S("ADDA CH34 Capture Enable", SUPPLY_SEQ_ADDA_UL_ON,
> + AFE_ADDA_UL1_SRC_CON0,
> + UL_SRC_ON_TMP_CTL_SFT, 0,
> + mtk_adda_ch34_ul_event,
> + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
Same here.
> + SND_SOC_DAPM_SUPPLY_S("AP_DMIC_EN", SUPPLY_SEQ_ADDA_AP_DMIC,
> + AFE_ADDA_UL0_SRC_CON0,
> + UL_AP_DMIC_ON_SFT, 0,
> + mtk_adda_ul_ap_dmic_event,
> + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
Same here.
> + SND_SOC_DAPM_SUPPLY_S("AP_DMIC_CH34_EN", SUPPLY_SEQ_ADDA_AP_DMIC,
> + AFE_ADDA_UL1_SRC_CON0,
> + UL_AP_DMIC_ON_SFT, 0,
> + mtk_adda_ch34_ul_ap_dmic_event,
> + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
Same here.
> + SND_SOC_DAPM_SUPPLY_S("ADDA_FIFO", SUPPLY_SEQ_ADDA_FIFO,
> + AFE_ADDA_UL0_SRC_CON1,
> + FIFO_SOFT_RST_SFT, 1,
> + NULL, 0),
> + SND_SOC_DAPM_SUPPLY_S("ADDA_CH34_FIFO", SUPPLY_SEQ_ADDA_FIFO,
> + AFE_ADDA_UL1_SRC_CON1,
> + FIFO_SOFT_RST_SFT, 1,
> + NULL, 0),
> + SND_SOC_DAPM_MUX("ADDA_UL_Mux", SND_SOC_NOPM, 0, 0,
> + &adda_ul_mux_control),
> + SND_SOC_DAPM_MUX("ADDA_CH34_UL_Mux", SND_SOC_NOPM, 0, 0,
> + &adda_ch34_ul_mux_control),
> + SND_SOC_DAPM_INPUT("AP_DMIC_INPUT"),
> + SND_SOC_DAPM_INPUT("AP_DMIC_CH34_INPUT"),
> +};
> +
> +static const struct snd_soc_dapm_route mtk_dai_adda_routes[] = {
> + /* capture */
> + {"ADDA_UL_Mux", "MTKAIF", "ADDA Capture"},
> + {"ADDA_UL_Mux", "AP_DMIC", "AP DMIC Capture"},
> + {"ADDA_UL_Mux", "AP_DMIC_MULTI_CH", "AP DMIC MULTICH Capture"},
> + {"ADDA_CH34_UL_Mux", "MTKAIF", "ADDA CH34 Capture"},
> + {"ADDA_CH34_UL_Mux", "AP_DMIC", "AP DMIC CH34 Capture"},
> + {"ADDA_CH34_UL_Mux", "AP_DMIC_MULTI_CH", "AP DMIC MULTICH Capture"},
> +
> + {"AP DMIC Capture", NULL, "ADDA Enable"},
> + {"AP DMIC Capture", NULL, "ADDA Capture Enable"},
Since "ADDA Enable" is the overall enable bit for the whole block,
you could just have the route point from "ADDA Enable" to "ADDA Capture
Enable". This also solves the sequencing order.
> + {"AP DMIC Capture", NULL, "ADDA_FIFO"},
> + {"AP DMIC Capture", NULL, "AP_DMIC_EN"},
> +
> + {"AP DMIC CH34 Capture", NULL, "ADDA Enable"},
> + {"AP DMIC CH34 Capture", NULL, "ADDA CH34 Capture Enable"},
Same here.
> + {"AP DMIC CH34 Capture", NULL, "ADDA_CH34_FIFO"},
> + {"AP DMIC CH34 Capture", NULL, "AP_DMIC_CH34_EN"},
> +
> + {"AP DMIC MULTICH Capture", NULL, "ADDA Enable"},
> + {"AP DMIC MULTICH Capture", NULL, "ADDA Capture Enable"},
> + {"AP DMIC MULTICH Capture", NULL, "ADDA CH34 Capture Enable"},
> + {"AP DMIC MULTICH Capture", NULL, "ADDA_FIFO"},
> + {"AP DMIC MULTICH Capture", NULL, "ADDA_CH34_FIFO"},
> + {"AP DMIC MULTICH Capture", NULL, "AP_DMIC_EN"},
> + {"AP DMIC MULTICH Capture", NULL, "AP_DMIC_CH34_EN"},
Nit: I'd add an empty line here for separation.
> + {"AP DMIC Capture", NULL, "AP_DMIC_INPUT"},
> + {"AP DMIC CH34 Capture", NULL, "AP_DMIC_CH34_INPUT"},
> + {"AP DMIC MULTICH Capture", NULL, "AP_DMIC_INPUT"},
> +};
> +
> +/* dai ops */
> +static int mtk_dai_adda_hw_params(struct snd_pcm_substream *substream,
> + struct snd_pcm_hw_params *params,
> + struct snd_soc_dai *dai)
> +{
> + struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
> + struct mt8196_afe_private *afe_priv = afe->platform_priv;
> + unsigned int rate = params_rate(params);
> + unsigned int mtkaif_rate = 0;
> + int id = dai->id;
> + struct mtk_afe_adda_priv *adda_priv;
> +
> + if (id >= MT8196_DAI_NUM || id < 0)
> + return -EINVAL;
> +
> + adda_priv = afe_priv->dai_priv[id];
> +
> + dev_info(afe->dev, "id %d, stream %d, rate %d\n",
> + id,
> + substream->stream,
> + rate);
dev_dbg() please.
> +
> + if (!adda_priv)
> + return -EINVAL;
> +
> + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
I suggest moving each block (playback and capture) into smaller functions.
That way each function is easier to read, and the indentation is decreased.
> + adda_priv->dl_rate = rate;
> +
> + /* get mtkaif dl rate */
> + mtkaif_rate =
> + mtkaif_rate_transform(afe, adda_priv->dl_rate);
This should fit on one line.
> + if (id == MT8196_DAI_ADDA) {
> + /* MTKAIF sample rate config */
> + regmap_update_bits(afe->regmap, AFE_ADDA_MTKAIFV4_TX_CFG0,
> + MTKAIFV4_TXIF_INPUT_MODE_MASK_SFT,
> + mtkaif_rate << MTKAIFV4_TXIF_INPUT_MODE_SFT);
> + /* AFE_ADDA_MTKAIFV4_TX_CFG0 */
> + regmap_update_bits(afe->regmap, AFE_ADDA_MTKAIFV4_TX_CFG0,
> + MTKAIFV4_TXIF_FOUR_CHANNEL_MASK_SFT,
> + 0x0 << MTKAIFV4_TXIF_FOUR_CHANNEL_SFT);
> + regmap_update_bits(afe->regmap, AFE_ADDA_MTKAIFV4_TX_CFG0,
> + MTKAIFV4_ADDA_OUT_EN_SEL_MASK_SFT,
> + 0x1 << MTKAIFV4_ADDA_OUT_EN_SEL_SFT);
> + regmap_update_bits(afe->regmap, AFE_ADDA_MTKAIFV4_TX_CFG0,
> + MTKAIFV4_ADDA6_OUT_EN_SEL_MASK_SFT,
> + 0x1 << MTKAIFV4_ADDA6_OUT_EN_SEL_SFT);
> + regmap_update_bits(afe->regmap, AFE_ADDA_MTKAIFV4_TX_CFG0,
> + MTKAIFV4_TXIF_V4_MASK_SFT,
> + 0x1 << MTKAIFV4_TXIF_V4_SFT);
> + regmap_update_bits(afe->regmap, AFE_ADDA_MTKAIFV4_TX_CFG0,
> + MTKAIFV4_TXIF_EN_SEL_MASK_SFT,
> + 0x0 << MTKAIFV4_TXIF_EN_SEL_SFT);
> + /* clean predistortion */
> + } else {
> + /* MTKAIF sample rate config */
> + regmap_update_bits(afe->regmap, AFE_ADDA6_MTKAIFV4_TX_CFG0,
> + ADDA6_MTKAIFV4_TXIF_INPUT_MODE_MASK_SFT,
> + mtkaif_rate << ADDA6_MTKAIFV4_TXIF_INPUT_MODE_SFT);
> + /* AFE_ADDA6_MTKAIFV4_TX_CFG0 */
> + regmap_update_bits(afe->regmap, AFE_ADDA6_MTKAIFV4_TX_CFG0,
> + ADDA6_MTKAIFV4_TXIF_FOUR_CHANNEL_MASK_SFT,
> + 0x0 << ADDA6_MTKAIFV4_TXIF_FOUR_CHANNEL_SFT);
> + regmap_update_bits(afe->regmap, AFE_ADDA6_MTKAIFV4_TX_CFG0,
> + ADDA6_MTKAIFV4_TXIF_EN_SEL_MASK_SFT,
> + 0x1 << ADDA6_MTKAIFV4_TXIF_EN_SEL_SFT);
> + }
> + } else {
> + unsigned int voice_mode = 0;
> + unsigned int ul_src_con0 = 0; /* default value */
> +
> + adda_priv->ul_rate = rate;
> +
> + /* get mtkaif dl rate */
> + mtkaif_rate =
> + mtkaif_rate_transform(afe, adda_priv->ul_rate);
This fits in one line. No need to wrap the line.
> +
> + voice_mode = adda_ul_rate_transform(afe, rate);
> +
> + ul_src_con0 |= (voice_mode << 17) & (0x7 << 17);
There should be macros for the shift and mask?
> +
> + /* enable iir */
> + ul_src_con0 |= (1 << UL_IIR_ON_TMP_CTL_SFT) &
> + UL_IIR_ON_TMP_CTL_MASK_SFT;
> + ul_src_con0 |= (UL_IIR_SW << UL_IIRMODE_CTL_SFT) &
> + UL_IIRMODE_CTL_MASK_SFT;
> +
> + regmap_update_bits(afe->regmap, AFE_ADDA_MTKAIFV4_RX_CFG0,
> + MTKAIFV4_RXIF_INPUT_MODE_MASK_SFT,
> + mtkaif_rate << MTKAIFV4_RXIF_INPUT_MODE_SFT);
> +
> + regmap_update_bits(afe->regmap, AFE_ADDA6_MTKAIFV4_RX_CFG0,
> + ADDA6_MTKAIFV4_RXIF_INPUT_MODE_MASK_SFT,
> + mtkaif_rate << ADDA6_MTKAIFV4_RXIF_INPUT_MODE_SFT);
> +
> + switch (id) {
> + case MT8196_DAI_ADDA:
> + case MT8196_DAI_AP_DMIC:
> + case MT8196_DAI_AP_DMIC_MULTICH:
> + regmap_update_bits(afe->regmap, AFE_ADDA_MTKAIFV4_RX_CFG0,
> + MTKAIFV4_RXIF_INPUT_MODE_MASK_SFT,
> + mtkaif_rate << MTKAIFV4_RXIF_INPUT_MODE_SFT);
> + /* AFE_ADDA_MTKAIFV4_RX_CFG0 */
> + regmap_update_bits(afe->regmap, AFE_ADDA_MTKAIFV4_RX_CFG0,
> + MTKAIFV4_RXIF_FOUR_CHANNEL_MASK_SFT,
> + 0x1 << MTKAIFV4_RXIF_FOUR_CHANNEL_SFT);
> + regmap_update_bits(afe->regmap, AFE_ADDA_MTKAIFV4_RX_CFG0,
> + MTKAIFV4_RXIF_EN_SEL_MASK_SFT,
> + 0x0 << MTKAIFV4_RXIF_EN_SEL_SFT);
> + /* [28] loopback mode
> + * 0: loopback adda tx to adda rx
> + * 1: loopback adda6 tx to adda rx
> + */
> + regmap_update_bits(afe->regmap, AFE_ADDA_MTKAIFV4_RX_CFG0,
> + MTKAIFV4_TXIF_EN_SEL_MASK_SFT,
> + 0x0 << MTKAIFV4_TXIF_EN_SEL_SFT);
> +
> + regmap_update_bits(afe->regmap, AFE_ADDA_MTKAIFV4_RX_CFG0,
> + MTKAIFV4_UL_CH1CH2_IN_EN_SEL_MASK_SFT,
> + 0x1 << MTKAIFV4_UL_CH1CH2_IN_EN_SEL_SFT);
> + regmap_update_bits(afe->regmap, AFE_ADDA_MTKAIFV4_RX_CFG0,
> + MTKAIFV4_UL_CH3CH4_IN_EN_SEL_MASK_SFT,
> + 0x1 << MTKAIFV4_UL_CH3CH4_IN_EN_SEL_SFT);
> +
> + /* 35Hz @ 48k */
> + regmap_write(afe->regmap,
> + AFE_ADDA_UL1_IIR_COEF_02_01, 0x00000000);
> + regmap_write(afe->regmap,
> + AFE_ADDA_UL1_IIR_COEF_04_03, 0x00003FB8);
> + regmap_write(afe->regmap,
> + AFE_ADDA_UL1_IIR_COEF_06_05, 0x3FB80000);
> + regmap_write(afe->regmap,
> + AFE_ADDA_UL1_IIR_COEF_08_07, 0x3FB80000);
> + regmap_write(afe->regmap,
> + AFE_ADDA_UL1_IIR_COEF_10_09, 0x0000C048);
> +
> + regmap_write(afe->regmap,
> + AFE_ADDA_UL1_SRC_CON0, ul_src_con0);
> +
> + /* mtkaif_rxif_data_mode = 0, amic */
> + regmap_update_bits(afe->regmap,
> + AFE_MTKAIF1_RX_CFG0,
> + 0x1 << 0,
> + 0x0 << 0);
> +
> + /* 35Hz @ 48k */
> + regmap_write(afe->regmap,
> + AFE_ADDA_UL0_IIR_COEF_02_01, 0x00000000);
> + regmap_write(afe->regmap,
> + AFE_ADDA_UL0_IIR_COEF_04_03, 0x00003FB8);
> + regmap_write(afe->regmap,
> + AFE_ADDA_UL0_IIR_COEF_06_05, 0x3FB80000);
> + regmap_write(afe->regmap,
> + AFE_ADDA_UL0_IIR_COEF_08_07, 0x3FB80000);
> + regmap_write(afe->regmap,
> + AFE_ADDA_UL0_IIR_COEF_10_09, 0x0000C048);
> +
> + regmap_write(afe->regmap,
> + AFE_ADDA_UL0_SRC_CON0, ul_src_con0);
> +
> + /* mtkaif_rxif_data_mode = 0, amic */
> + regmap_update_bits(afe->regmap,
> + AFE_MTKAIF0_RX_CFG0,
> + 0x1 << 0,
> + 0x0 << 0);
> + break;
> + case MT8196_DAI_ADDA_CH34:
> + case MT8196_DAI_AP_DMIC_CH34:
> + /* AFE_ADDA_MTKAIFV4_RX_CFG0 */
> + regmap_update_bits(afe->regmap, AFE_ADDA_MTKAIFV4_RX_CFG0,
> + MTKAIFV4_RXIF_FOUR_CHANNEL_MASK_SFT,
> + 0x1 << MTKAIFV4_RXIF_FOUR_CHANNEL_SFT);
> + regmap_update_bits(afe->regmap, AFE_ADDA_MTKAIFV4_RX_CFG0,
> + MTKAIFV4_RXIF_EN_SEL_MASK_SFT,
> + 0x0 << MTKAIFV4_RXIF_EN_SEL_SFT);
> +
> + regmap_update_bits(afe->regmap, AFE_ADDA_MTKAIFV4_RX_CFG0,
> + MTKAIFV4_UL_CH1CH2_IN_EN_SEL_MASK_SFT,
> + 0x1 << MTKAIFV4_UL_CH1CH2_IN_EN_SEL_SFT);
> + regmap_update_bits(afe->regmap, AFE_ADDA_MTKAIFV4_RX_CFG0,
> + MTKAIFV4_UL_CH3CH4_IN_EN_SEL_MASK_SFT,
> + 0x1 << MTKAIFV4_UL_CH3CH4_IN_EN_SEL_SFT);
> +
> + /* 35Hz @ 48k */
> + regmap_write(afe->regmap,
> + AFE_ADDA_UL1_IIR_COEF_02_01, 0x00000000);
> + regmap_write(afe->regmap,
> + AFE_ADDA_UL1_IIR_COEF_04_03, 0x00003FB8);
> + regmap_write(afe->regmap,
> + AFE_ADDA_UL1_IIR_COEF_06_05, 0x3FB80000);
> + regmap_write(afe->regmap,
> + AFE_ADDA_UL1_IIR_COEF_08_07, 0x3FB80000);
> + regmap_write(afe->regmap,
> + AFE_ADDA_UL1_IIR_COEF_10_09, 0x0000C048);
> +
> + regmap_write(afe->regmap,
> + AFE_ADDA_UL1_SRC_CON0, ul_src_con0);
> +
> + /* mtkaif_rxif_data_mode = 0, amic */
> + regmap_update_bits(afe->regmap,
> + AFE_MTKAIF1_RX_CFG0,
> + 0x1 << 0,
> + 0x0 << 0);
> +
> + break;
> + case MT8196_DAI_ADDA_CH56:
> + regmap_update_bits(afe->regmap, AFE_ADDA6_MTKAIFV4_RX_CFG0,
> + ADDA6_MTKAIFV4_RXIF_INPUT_MODE_MASK_SFT,
> + mtkaif_rate << ADDA6_MTKAIFV4_RXIF_INPUT_MODE_SFT);
> + /* AFE_ADDA6_MTKAIFV4_RX_CFG0 */
> + regmap_update_bits(afe->regmap, AFE_ADDA6_MTKAIFV4_RX_CFG0,
> + ADDA6_MTKAIFV4_RXIF_FOUR_CHANNEL_MASK_SFT,
> + 0x1 << ADDA6_MTKAIFV4_RXIF_FOUR_CHANNEL_SFT);
> + regmap_update_bits(afe->regmap, AFE_ADDA_MTKAIFV4_RX_CFG0,
> + MTKAIFV4_UL_CH5CH6_IN_EN_SEL_MASK_SFT,
> + 0x1 << MTKAIFV4_UL_CH5CH6_IN_EN_SEL_SFT);
> + regmap_update_bits(afe->regmap, AFE_ADDA6_MTKAIFV4_RX_CFG0,
> + ADDA6_MTKAIFV4_RXIF_EN_SEL_MASK_SFT,
> + 0x1 << ADDA6_MTKAIFV4_RXIF_EN_SEL_SFT);
> + break;
> + default:
> + break;
> + }
> +
> + /* ap dmic */
> + switch (id) {
> + case MT8196_DAI_AP_DMIC:
> + case MT8196_DAI_AP_DMIC_CH34:
> + mtk_adda_ul_src_dmic(afe, id);
> + break;
> + case MT8196_DAI_AP_DMIC_MULTICH:
> + regmap_update_bits(afe->regmap, AFE_ADDA_ULSRC_PHASE_CON1,
> + DMIC_CLK_PHASE_SYNC_SET_MASK_SFT,
> + 0x1 << DMIC_CLK_PHASE_SYNC_SET_SFT);
> + mtk_adda_ul_src_dmic_phase_sync(afe);
> + mtk_adda_ul_src_dmic(afe, MT8196_DAI_AP_DMIC);
> + mtk_adda_ul_src_dmic(afe, MT8196_DAI_AP_DMIC_CH34);
> + mtk_adda_ul_src_dmic_phase_sync_clock(afe);
> + break;
> + default:
> + break;
> + }
> + }
> +
> + return 0;
> +}
> +
> +static const struct snd_soc_dai_ops mtk_dai_adda_ops = {
> + .hw_params = mtk_dai_adda_hw_params,
> +};
> +
> +/* dai driver */
> +#define MTK_ADDA_PLAYBACK_RATES (SNDRV_PCM_RATE_8000_48000 |\
> + SNDRV_PCM_RATE_96000 |\
> + SNDRV_PCM_RATE_192000)
> +
> +#define MTK_ADDA_CAPTURE_RATES (SNDRV_PCM_RATE_8000 |\
> + SNDRV_PCM_RATE_16000 |\
> + SNDRV_PCM_RATE_32000 |\
> + SNDRV_PCM_RATE_48000 |\
> + SNDRV_PCM_RATE_96000 |\
> + SNDRV_PCM_RATE_192000)
> +
> +#define MTK_ADDA_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\
> + SNDRV_PCM_FMTBIT_S24_LE |\
> + SNDRV_PCM_FMTBIT_S32_LE)
> +
> +static struct snd_soc_dai_driver mtk_dai_adda_driver[] = {
> + {
> + .name = "ADDA",
> + .id = MT8196_DAI_ADDA,
> + .playback = {
> + .stream_name = "ADDA Playback",
> + .channels_min = 1,
> + .channels_max = 2,
> + .rates = MTK_ADDA_PLAYBACK_RATES,
> + .formats = MTK_ADDA_FORMATS,
> + },
> + .capture = {
> + .stream_name = "ADDA Capture",
> + .channels_min = 1,
> + .channels_max = 2,
> + .rates = MTK_ADDA_CAPTURE_RATES,
> + .formats = MTK_ADDA_FORMATS,
> + },
> + .ops = &mtk_dai_adda_ops,
> + },
> + {
> + .name = "ADDA_CH34",
> + .id = MT8196_DAI_ADDA_CH34,
> + .playback = {
> + .stream_name = "ADDA CH34 Playback",
> + .channels_min = 1,
> + .channels_max = 2,
> + .rates = MTK_ADDA_PLAYBACK_RATES,
> + .formats = MTK_ADDA_FORMATS,
> + },
> + .capture = {
> + .stream_name = "ADDA CH34 Capture",
> + .channels_min = 1,
> + .channels_max = 2,
> + .rates = MTK_ADDA_CAPTURE_RATES,
> + .formats = MTK_ADDA_FORMATS,
> + },
> + .ops = &mtk_dai_adda_ops,
> + },
> + {
> + .name = "ADDA_CH56",
> + .id = MT8196_DAI_ADDA_CH56,
> + .capture = {
> + .stream_name = "ADDA CH56 Capture",
> + .channels_min = 1,
> + .channels_max = 2,
> + .rates = MTK_ADDA_CAPTURE_RATES,
> + .formats = MTK_ADDA_FORMATS,
> + },
> + .ops = &mtk_dai_adda_ops,
> + },
> + {
> + .name = "AP_DMIC",
> + .id = MT8196_DAI_AP_DMIC,
> + .capture = {
> + .stream_name = "AP DMIC Capture",
> + .channels_min = 1,
> + .channels_max = 2,
> + .rates = MTK_ADDA_CAPTURE_RATES,
> + .formats = MTK_ADDA_FORMATS,
> + },
> + .ops = &mtk_dai_adda_ops,
> + },
> + {
> + .name = "AP_DMIC_CH34",
> + .id = MT8196_DAI_AP_DMIC_CH34,
> + .capture = {
> + .stream_name = "AP DMIC CH34 Capture",
> + .channels_min = 1,
> + .channels_max = 2,
> + .rates = MTK_ADDA_CAPTURE_RATES,
> + .formats = MTK_ADDA_FORMATS,
> + },
> + .ops = &mtk_dai_adda_ops,
> + },
> + {
> + .name = "AP_DMIC_MULTICH",
> + .id = MT8196_DAI_AP_DMIC_MULTICH,
> + .capture = {
> + .stream_name = "AP DMIC MULTICH Capture",
> + .channels_min = 1,
> + .channels_max = 4,
> + .rates = MTK_ADDA_CAPTURE_RATES,
> + .formats = MTK_ADDA_FORMATS,
> + },
> + .ops = &mtk_dai_adda_ops,
> + },
I think this is a weird setup. AFAIK there is only one ADDA, but here
it is expanded into 6 backend DAIs for different setups. However only
one is actually usable: enabling a second one would clobber the hardware
configuration setup by the first one. This seems very fragile.
> +};
> +
> +static int init_adda_priv_data(struct mtk_base_afe *afe)
> +{
> + struct mt8196_afe_private *afe_priv = afe->platform_priv;
> + struct mtk_afe_adda_priv *adda_priv;
> + static const int adda_dai_list[] = {
> + MT8196_DAI_ADDA,
> + MT8196_DAI_ADDA_CH34,
> + MT8196_DAI_ADDA_CH56,
> + MT8196_DAI_AP_DMIC_MULTICH
> + };
> + int i;
> +
> + for (i = 0; i < ARRAY_SIZE(adda_dai_list); i++) {
> + adda_priv = devm_kzalloc(afe->dev,
> + sizeof(struct mtk_afe_adda_priv),
> + GFP_KERNEL);
> + if (!adda_priv)
> + return -ENOMEM;
> +
> + afe_priv->dai_priv[adda_dai_list[i]] = adda_priv;
> + }
> +
> + /* ap dmic priv share with adda */
> + afe_priv->dai_priv[MT8196_DAI_AP_DMIC] =
> + afe_priv->dai_priv[MT8196_DAI_ADDA];
> + afe_priv->dai_priv[MT8196_DAI_AP_DMIC_CH34] =
> + afe_priv->dai_priv[MT8196_DAI_ADDA_CH34];
> +
> + return 0;
> +}
> +
> +int mt8196_dai_adda_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);
The DAI should be added to the list only if everything succeeds.
ChenYu
> + dai->dai_drivers = mtk_dai_adda_driver;
> + dai->num_dai_drivers = ARRAY_SIZE(mtk_dai_adda_driver);
> +
> + dai->controls = mtk_adda_controls;
> + dai->num_controls = ARRAY_SIZE(mtk_adda_controls);
> + dai->dapm_widgets = mtk_dai_adda_widgets;
> + dai->num_dapm_widgets = ARRAY_SIZE(mtk_dai_adda_widgets);
> + dai->dapm_routes = mtk_dai_adda_routes;
> + dai->num_dapm_routes = ARRAY_SIZE(mtk_dai_adda_routes);
> +
> + return init_adda_priv_data(afe);
> +}
> +
> --
> 2.45.2
>
>
^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [PATCH v6 02/10] ASoC: mediatek: mt8196: add common header
[not found] ` <20250708111806.3992-3-darren.ye@mediatek.com>
@ 2025-07-30 8:23 ` Chen-Yu Tsai
0 siblings, 0 replies; 24+ messages in thread
From: Chen-Yu Tsai @ 2025-07-30 8:23 UTC (permalink / raw)
To: Darren.Ye
Cc: Liam Girdwood, Mark Brown, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Matthias Brugger, AngeloGioacchino Del Regno,
Jaroslav Kysela, Takashi Iwai, Linus Walleij, Bartosz Golaszewski,
linux-sound, devicetree, linux-kernel, linux-arm-kernel,
linux-mediatek, linux-gpio
On Tue, Jul 8, 2025 at 7:34 PM Darren.Ye <darren.ye@mediatek.com> wrote:
>
> From: Darren Ye <darren.ye@mediatek.com>
>
> Add header files for register definitions and structures.
>
> Signed-off-by: Darren Ye <darren.ye@mediatek.com>
> ---
> sound/soc/mediatek/mt8196/mt8196-afe-common.h | 213 +
> .../mediatek/mt8196/mt8196-interconnection.h | 121 +
> sound/soc/mediatek/mt8196/mt8196-reg.h | 12068 ++++++++++++++++
> 3 files changed, 12402 insertions(+)
> create mode 100644 sound/soc/mediatek/mt8196/mt8196-afe-common.h
> create mode 100644 sound/soc/mediatek/mt8196/mt8196-interconnection.h
> create mode 100644 sound/soc/mediatek/mt8196/mt8196-reg.h
>
> diff --git a/sound/soc/mediatek/mt8196/mt8196-afe-common.h b/sound/soc/mediatek/mt8196/mt8196-afe-common.h
> new file mode 100644
> index 000000000000..574003a8a2e4
> --- /dev/null
> +++ b/sound/soc/mediatek/mt8196/mt8196-afe-common.h
> @@ -0,0 +1,213 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * mt8196-afe-common.h -- Mediatek 8196 audio driver definitions
> + *
> + * Copyright (c) 2024 MediaTek Inc.
> + * Author: Darren Ye <darren.ye@mediatek.com>
> + */
> +
> +#ifndef _MT_8196_AFE_COMMON_H_
> +#define _MT_8196_AFE_COMMON_H_
Add an empty line here for separation.
> +#include <sound/soc.h>
> +#include <sound/pcm.h>
> +#include <linux/clk.h>
> +#include <linux/list.h>
> +#include <linux/regmap.h>
> +#include "mt8196-reg.h"
> +#include "mtk-base-afe.h"
Please add empty lines between each group of headers. Headers are grouped
by path prefix.
Moreover, the contents of these header files are not required in this
header file. Please push these include statements to the files that
actually need them.
For the pointer to struct types, you can just forward declare the struct
types instead of including the whole header file:
struct clk;
struct mtk_base_afe;
[...]
> +struct mt8196_afe_private {
> + struct clk **clk;
> + struct clk_lookup **lookup;
This doesn't seem to be used.
> + struct regmap *vlp_ck;
IIRC this will get removed since the tuner values will be moved to the
clk driver.
> + int irq_cnt[MT8196_MEMIF_NUM];
> + int dram_resource_counter;
This seems unused. Please remove.
> +
> + /* xrun assert */
> + int xrun_assert[MT8196_MEMIF_NUM];
This doesn't seem to do anything. It is initialized to zero and then
never used.
> +
> + /* dai */
> + void *dai_priv[MT8196_DAI_NUM];
> +
> + /* mck */
> + int mck_rate[MT8196_MCK_NUM];
> +
> + /* channel merge */
> + unsigned int cm_rate[CM_NUM];
> + unsigned int cm_channels;
> +};
> +
> +int mt8196_dai_adda_register(struct mtk_base_afe *afe);
> +int mt8196_dai_i2s_register(struct mtk_base_afe *afe);
> +int mt8196_dai_tdm_register(struct mtk_base_afe *afe);
> +int mt8196_dai_set_priv(struct mtk_base_afe *afe, int id,
> + int priv_size, const void *priv_data);
Please add an empty line here.
> +#endif
[...]
ChenYu
^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [PATCH v6 03/10] ASoC: mediatek: mt8196: support audio clock control
2025-07-08 11:15 ` [PATCH v6 03/10] ASoC: mediatek: mt8196: support audio clock control Darren.Ye
@ 2025-07-30 8:42 ` Chen-Yu Tsai
0 siblings, 0 replies; 24+ messages in thread
From: Chen-Yu Tsai @ 2025-07-30 8:42 UTC (permalink / raw)
To: Darren.Ye
Cc: Liam Girdwood, Mark Brown, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Matthias Brugger, AngeloGioacchino Del Regno,
Jaroslav Kysela, Takashi Iwai, Linus Walleij, Bartosz Golaszewski,
linux-sound, devicetree, linux-kernel, linux-arm-kernel,
linux-mediatek, linux-gpio
On Tue, Jul 8, 2025 at 7:34 PM Darren.Ye <darren.ye@mediatek.com> wrote:
>
> From: Darren Ye <darren.ye@mediatek.com>
>
> Add audio clock wrapper and audio tuner control.
>
> Signed-off-by: Darren Ye <darren.ye@mediatek.com>
> ---
> sound/soc/mediatek/mt8196/mt8196-afe-clk.c | 728 +++++++++++++++++++++
> sound/soc/mediatek/mt8196/mt8196-afe-clk.h | 80 +++
> 2 files changed, 808 insertions(+)
> create mode 100644 sound/soc/mediatek/mt8196/mt8196-afe-clk.c
> create mode 100644 sound/soc/mediatek/mt8196/mt8196-afe-clk.h
>
> diff --git a/sound/soc/mediatek/mt8196/mt8196-afe-clk.c b/sound/soc/mediatek/mt8196/mt8196-afe-clk.c
> new file mode 100644
> index 000000000000..00f47b485812
> --- /dev/null
> +++ b/sound/soc/mediatek/mt8196/mt8196-afe-clk.c
> @@ -0,0 +1,728 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * mt8196-afe-clk.c -- Mediatek 8196 afe clock ctrl
> + *
> + * Copyright (c) 2024 MediaTek Inc.
> + * Author: Darren Ye <darren.ye@mediatek.com>
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/regmap.h>
> +#include <linux/mfd/syscon.h>
Please add an empty line here for separation.
> +#include "mt8196-afe-common.h"
> +#include "mt8196-afe-clk.h"
> +
> +static const char *aud_clks[MT8196_CLK_NUM] = {
> + /* vlp clk */
> + [MT8196_CLK_VLP_MUX_AUDIOINTBUS] = "top_aud_intbus",
> + [MT8196_CLK_VLP_MUX_AUD_ENG1] = "top_aud_eng1",
> + [MT8196_CLK_VLP_MUX_AUD_ENG2] = "top_aud_eng2",
> + [MT8196_CLK_VLP_MUX_AUDIO_H] = "top_aud_h",
> + [MT8196_CLK_VLP_CLK26M] = "vlp_clk26m",
> + /* pll */
> + [MT8196_CLK_TOP_APLL1_CK] = "apll1",
> + [MT8196_CLK_TOP_APLL2_CK] = "apll2",
> + /* divider */
> + [MT8196_CLK_TOP_APLL1_D4] = "apll1_d4",
> + [MT8196_CLK_TOP_APLL2_D4] = "apll2_d4",
> + [MT8196_CLK_TOP_APLL12_DIV_I2SIN0] = "apll12_div_i2sin0",
> + [MT8196_CLK_TOP_APLL12_DIV_I2SIN1] = "apll12_div_i2sin1",
> + [MT8196_CLK_TOP_APLL12_DIV_FMI2S] = "apll12_div_fmi2s",
> + [MT8196_CLK_TOP_APLL12_DIV_TDMOUT_M] = "apll12_div_tdmout_m",
> + [MT8196_CLK_TOP_APLL12_DIV_TDMOUT_B] = "apll12_div_tdmout_b",
> + /* mux */
> + [MT8196_CLK_TOP_MUX_AUD_1] = "top_apll1",
> + [MT8196_CLK_TOP_MUX_AUD_2] = "top_apll2",
> + [MT8196_CLK_TOP_I2SIN0_M_SEL] = "top_i2sin0",
> + [MT8196_CLK_TOP_I2SIN1_M_SEL] = "top_i2sin1",
> + [MT8196_CLK_TOP_FMI2S_M_SEL] = "top_fmi2s",
> + [MT8196_CLK_TOP_TDMOUT_M_SEL] = "top_dptx",
> + [MT8196_CLK_TOP_ADSP_SEL] = "top_adsp",
> + /* top 26m*/
> + [MT8196_CLK_TOP_CLK26M] = "clk26m",
> +};
> +
> +int mt8196_afe_enable_clk(struct mtk_base_afe *afe, struct clk *clk)
> +{
> + int ret;
> +
> + if (clk) {
There's no need to check the validity of the pointer. The clk prepare
and enable APIs can take NULL pointers and become no-ops.
> + ret = clk_prepare_enable(clk);
> + if (ret) {
> + dev_dbg(afe->dev, "failed to enable clk\n");
This should be a visible error.
> + return ret;
> + }
> + } else {
> + dev_dbg(afe->dev, "NULL clk\n");
> + }
> + return 0;
> +}
> +EXPORT_SYMBOL_GPL(mt8196_afe_enable_clk);
> +
> +void mt8196_afe_disable_clk(struct mtk_base_afe *afe, struct clk *clk)
> +{
> + if (clk)
> + clk_disable_unprepare(clk);
> + else
> + dev_dbg(afe->dev, "NULL clk\n");
> +}
> +EXPORT_SYMBOL_GPL(mt8196_afe_disable_clk);
> +
> +static int mt8196_afe_set_clk_rate(struct mtk_base_afe *afe, struct clk *clk,
> + unsigned int rate)
> +{
> + int ret;
> +
> + if (clk) {
> + ret = clk_set_rate(clk, rate);
> + if (ret) {
> + dev_dbg(afe->dev, "failed to set clk rate\n");
This should be a visible error.
> + return ret;
> + }
> + }
> +
> + return 0;
> +}
> +
> +static int mt8196_afe_set_clk_parent(struct mtk_base_afe *afe, struct clk *clk,
> + struct clk *parent)
> +{
> + int ret;
> +
> + if (clk && parent) {
> + ret = clk_set_parent(clk, parent);
> + if (ret) {
> + dev_dbg(afe->dev, "failed to set clk parent %d\n", ret);
> + return ret;
> + }
> + }
> +
> + return 0;
> +}
Per our offline discussions, explicitly setting clock parents are not
needed.
> +
> +static unsigned int get_top_cg_reg(unsigned int cg_type)
> +{
> + switch (cg_type) {
> + case MT8196_AUDIO_26M_EN_ON:
> + case MT8196_AUDIO_F3P25M_EN_ON:
> + case MT8196_AUDIO_APLL1_EN_ON:
> + case MT8196_AUDIO_APLL2_EN_ON:
> + return AUDIO_ENGEN_CON0;
> + case MT8196_CG_AUDIO_HOPPING_CK:
> + case MT8196_CG_AUDIO_F26M_CK:
> + case MT8196_CG_APLL1_CK:
> + case MT8196_CG_APLL2_CK:
> + case MT8196_PDN_APLL_TUNER2:
> + case MT8196_PDN_APLL_TUNER1:
> + return AUDIO_TOP_CON4;
> + default:
> + return 0;
> + }
> +}
> +
> +static unsigned int get_top_cg_mask(unsigned int cg_type)
> +{
> + switch (cg_type) {
> + case MT8196_AUDIO_26M_EN_ON:
> + return AUDIO_26M_EN_ON_MASK_SFT;
> + case MT8196_AUDIO_F3P25M_EN_ON:
> + return AUDIO_F3P25M_EN_ON_MASK_SFT;
> + case MT8196_AUDIO_APLL1_EN_ON:
> + return AUDIO_APLL1_EN_ON_MASK_SFT;
> + case MT8196_AUDIO_APLL2_EN_ON:
> + return AUDIO_APLL2_EN_ON_MASK_SFT;
> + case MT8196_CG_AUDIO_HOPPING_CK:
> + return CG_AUDIO_HOPPING_CK_MASK_SFT;
> + case MT8196_CG_AUDIO_F26M_CK:
> + return CG_AUDIO_F26M_CK_MASK_SFT;
> + case MT8196_CG_APLL1_CK:
> + return CG_APLL1_CK_MASK_SFT;
> + case MT8196_CG_APLL2_CK:
> + return CG_APLL2_CK_MASK_SFT;
> + case MT8196_PDN_APLL_TUNER2:
> + return PDN_APLL_TUNER2_MASK_SFT;
> + case MT8196_PDN_APLL_TUNER1:
> + return PDN_APLL_TUNER1_MASK_SFT;
> + default:
> + return 0;
> + }
> +}
> +
> +static unsigned int get_top_cg_on_val(unsigned int cg_type)
> +{
> + switch (cg_type) {
> + case MT8196_AUDIO_26M_EN_ON:
> + case MT8196_AUDIO_F3P25M_EN_ON:
> + case MT8196_AUDIO_APLL1_EN_ON:
> + case MT8196_AUDIO_APLL2_EN_ON:
> + return get_top_cg_mask(cg_type);
> + case MT8196_CG_AUDIO_HOPPING_CK:
> + case MT8196_CG_AUDIO_F26M_CK:
> + case MT8196_CG_APLL1_CK:
> + case MT8196_CG_APLL2_CK:
> + case MT8196_PDN_APLL_TUNER2:
> + case MT8196_PDN_APLL_TUNER1:
> + return 0;
> + default:
> + return 0;
> + }
> +}
> +
> +static unsigned int get_top_cg_off_val(unsigned int cg_type)
> +{
> + switch (cg_type) {
> + case MT8196_AUDIO_26M_EN_ON:
> + case MT8196_AUDIO_F3P25M_EN_ON:
> + case MT8196_AUDIO_APLL1_EN_ON:
> + case MT8196_AUDIO_APLL2_EN_ON:
> + return 0;
> + case MT8196_CG_AUDIO_HOPPING_CK:
> + case MT8196_CG_AUDIO_F26M_CK:
> + case MT8196_CG_APLL1_CK:
> + case MT8196_CG_APLL2_CK:
> + case MT8196_PDN_APLL_TUNER2:
> + case MT8196_PDN_APLL_TUNER1:
> + return get_top_cg_mask(cg_type);
> + default:
> + return get_top_cg_mask(cg_type);
> + }
> +}
> +
> +static int mt8196_afe_enable_top_cg(struct mtk_base_afe *afe, unsigned int cg_type)
> +{
> + unsigned int reg = get_top_cg_reg(cg_type);
> + unsigned int mask = get_top_cg_mask(cg_type);
> + unsigned int val = get_top_cg_on_val(cg_type);
> +
> + if (!afe->regmap) {
> + dev_warn(afe->dev, "skip regmap\n");
> + return 0;
This should be a fatal error.
> + }
> +
> + dev_dbg(afe->dev, "reg: 0x%x, mask: 0x%x, val: 0x%x\n", reg, mask, val);
> + regmap_update_bits(afe->regmap, reg, mask, val);
Should check the return value, since it could fail because these are set as
volatile registers and cannot be updated in cache-only state.
> + return 0;
> +}
> +
> +static int mt8196_afe_disable_top_cg(struct mtk_base_afe *afe, unsigned int cg_type)
> +{
> + unsigned int reg = get_top_cg_reg(cg_type);
> + unsigned int mask = get_top_cg_mask(cg_type);
> + unsigned int val = get_top_cg_off_val(cg_type);
> +
> + if (!afe->regmap) {
> + dev_warn(afe->dev, "skip regmap\n");
> + return 0;
> + }
> +
> + dev_dbg(afe->dev, "reg: 0x%x, mask: 0x%x, val: 0x%x\n", reg, mask, val);
> + regmap_update_bits(afe->regmap, reg, mask, val);
Same here.
> +
> + return 0;
> +}
> +
> +static int apll1_mux_setting(struct mtk_base_afe *afe, bool enable)
> +{
> + struct mt8196_afe_private *afe_priv = afe->platform_priv;
> + int ret = 0;
> +
> + dev_dbg(afe->dev, "enable: %d\n", enable);
> +
> + if (enable) {
> + ret = mt8196_afe_enable_clk(afe, afe_priv->clk[MT8196_CLK_TOP_MUX_AUD_1]);
> + if (ret)
> + return ret;
> +
> + ret = mt8196_afe_set_clk_parent(afe, afe_priv->clk[MT8196_CLK_TOP_MUX_AUD_1],
> + afe_priv->clk[MT8196_CLK_TOP_APLL1_CK]);
> + if (ret)
> + return ret;
> +
> + /* 180.6336 / 4 = 45.1584MHz */
> + ret = mt8196_afe_enable_clk(afe, afe_priv->clk[MT8196_CLK_VLP_MUX_AUD_ENG1]);
> + if (ret)
> + return ret;
> +
> + ret = mt8196_afe_set_clk_parent(afe, afe_priv->clk[MT8196_CLK_VLP_MUX_AUD_ENG1],
> + afe_priv->clk[MT8196_CLK_TOP_APLL1_D4]);
> + if (ret)
> + return ret;
> +
> + ret = mt8196_afe_enable_clk(afe, afe_priv->clk[MT8196_CLK_VLP_MUX_AUDIO_H]);
> + if (ret)
> + return ret;
> +
> + ret = mt8196_afe_set_clk_parent(afe, afe_priv->clk[MT8196_CLK_VLP_MUX_AUDIO_H],
> + afe_priv->clk[MT8196_CLK_TOP_APLL1_CK]);
> + if (ret)
> + return ret;
> + } else {
> + ret = mt8196_afe_set_clk_parent(afe, afe_priv->clk[MT8196_CLK_VLP_MUX_AUD_ENG1],
> + afe_priv->clk[MT8196_CLK_VLP_CLK26M]);
> + if (ret)
> + return ret;
> +
> + mt8196_afe_disable_clk(afe, afe_priv->clk[MT8196_CLK_VLP_MUX_AUD_ENG1]);
> +
> + ret = mt8196_afe_set_clk_parent(afe, afe_priv->clk[MT8196_CLK_TOP_MUX_AUD_1],
> + afe_priv->clk[MT8196_CLK_TOP_CLK26M]);
> + if (ret)
> + return ret;
> +
> + mt8196_afe_disable_clk(afe, afe_priv->clk[MT8196_CLK_TOP_MUX_AUD_1]);
> + mt8196_afe_set_clk_parent(afe, afe_priv->clk[MT8196_CLK_VLP_MUX_AUDIO_H],
> + afe_priv->clk[MT8196_CLK_VLP_CLK26M]);
> + mt8196_afe_disable_clk(afe, afe_priv->clk[MT8196_CLK_VLP_MUX_AUDIO_H]);
> + }
We've talked about this offline. FTR there's no need to enable intermediate
clocks. When the leaf clock is enabled, the CCF also enables all connected
parents. There's also no need to set parents explicitly. When the clock
rate of the leaf clock gets set, the CCF will take care to reparent it
or the sub-tree to the most appropriate clock parent.
> + return 0;
> +}
> +
> +static int apll2_mux_setting(struct mtk_base_afe *afe, bool enable)
> +{
> + struct mt8196_afe_private *afe_priv = afe->platform_priv;
> + int ret = 0;
> +
> + dev_dbg(afe->dev, "enable: %d\n", enable);
> +
> + if (enable) {
> + ret = mt8196_afe_enable_clk(afe, afe_priv->clk[MT8196_CLK_TOP_MUX_AUD_2]);
> + if (ret)
> + return ret;
> +
> + ret = mt8196_afe_set_clk_parent(afe, afe_priv->clk[MT8196_CLK_TOP_MUX_AUD_2],
> + afe_priv->clk[MT8196_CLK_TOP_APLL2_CK]);
> + if (ret)
> + return ret;
> +
> + /* 196.608 / 4 = 49.152MHz */
> + ret = mt8196_afe_enable_clk(afe, afe_priv->clk[MT8196_CLK_VLP_MUX_AUD_ENG2]);
> + if (ret)
> + return ret;
> +
> + ret = mt8196_afe_set_clk_parent(afe, afe_priv->clk[MT8196_CLK_VLP_MUX_AUD_ENG2],
> + afe_priv->clk[MT8196_CLK_TOP_APLL2_D4]);
> + if (ret)
> + return ret;
> +
> + ret = mt8196_afe_enable_clk(afe, afe_priv->clk[MT8196_CLK_VLP_MUX_AUDIO_H]);
> + if (ret)
> + return ret;
> +
> + ret = mt8196_afe_set_clk_parent(afe, afe_priv->clk[MT8196_CLK_VLP_MUX_AUDIO_H],
> + afe_priv->clk[MT8196_CLK_TOP_APLL2_CK]);
> + if (ret)
> + return ret;
> + } else {
> + ret = mt8196_afe_set_clk_parent(afe, afe_priv->clk[MT8196_CLK_VLP_MUX_AUD_ENG2],
> + afe_priv->clk[MT8196_CLK_VLP_CLK26M]);
> + if (ret)
> + return ret;
> +
> + mt8196_afe_disable_clk(afe, afe_priv->clk[MT8196_CLK_VLP_MUX_AUD_ENG2]);
> +
> + ret = mt8196_afe_set_clk_parent(afe, afe_priv->clk[MT8196_CLK_TOP_MUX_AUD_2],
> + afe_priv->clk[MT8196_CLK_TOP_CLK26M]);
> + if (ret)
> + return ret;
> +
> + mt8196_afe_disable_clk(afe, afe_priv->clk[MT8196_CLK_TOP_MUX_AUD_2]);
> + mt8196_afe_set_clk_parent(afe, afe_priv->clk[MT8196_CLK_VLP_MUX_AUDIO_H],
> + afe_priv->clk[MT8196_CLK_VLP_CLK26M]);
> + mt8196_afe_disable_clk(afe, afe_priv->clk[MT8196_CLK_VLP_MUX_AUDIO_H]);
> + }
Same for this function.
> + return 0;
> +}
> +
> +static int mt8196_afe_disable_apll(struct mtk_base_afe *afe)
> +{
> + struct mt8196_afe_private *afe_priv = afe->platform_priv;
> + int ret = 0;
> +
> + ret = mt8196_afe_enable_clk(afe, afe_priv->clk[MT8196_CLK_VLP_MUX_AUDIO_H]);
> + if (ret)
> + return ret;
> +
> + ret = mt8196_afe_enable_clk(afe, afe_priv->clk[MT8196_CLK_TOP_MUX_AUD_1]);
> + if (ret)
> + goto clk_ck_mux_aud1_err;
> +
> + ret = mt8196_afe_set_clk_parent(afe, afe_priv->clk[MT8196_CLK_TOP_MUX_AUD_1],
> + afe_priv->clk[MT8196_CLK_TOP_CLK26M]);
> + if (ret)
> + goto clk_ck_mux_aud1_parent_err;
> +
> + ret = mt8196_afe_enable_clk(afe, afe_priv->clk[MT8196_CLK_TOP_MUX_AUD_2]);
> + if (ret)
> + goto clk_ck_mux_aud2_err;
> +
> + ret = mt8196_afe_set_clk_parent(afe, afe_priv->clk[MT8196_CLK_TOP_MUX_AUD_2],
> + afe_priv->clk[MT8196_CLK_TOP_CLK26M]);
> + if (ret)
> + goto clk_ck_mux_aud2_parent_err;
> +
> + mt8196_afe_disable_clk(afe, afe_priv->clk[MT8196_CLK_TOP_MUX_AUD_1]);
> + mt8196_afe_disable_clk(afe, afe_priv->clk[MT8196_CLK_TOP_MUX_AUD_2]);
> + mt8196_afe_set_clk_parent(afe, afe_priv->clk[MT8196_CLK_VLP_MUX_AUDIO_H],
> + afe_priv->clk[MT8196_CLK_VLP_CLK26M]);
> + mt8196_afe_disable_clk(afe, afe_priv->clk[MT8196_CLK_VLP_MUX_AUDIO_H]);
Same here. There's no need to toggle all the intermediate clocks. And since
everything is getting disabled, what parent is selected shouldn't matter.
> + return 0;
> +
> +clk_ck_mux_aud2_parent_err:
> + mt8196_afe_disable_clk(afe, afe_priv->clk[MT8196_CLK_TOP_MUX_AUD_2]);
> +clk_ck_mux_aud2_err:
> + mt8196_afe_set_clk_parent(afe, afe_priv->clk[MT8196_CLK_TOP_MUX_AUD_1],
> + afe_priv->clk[MT8196_CLK_TOP_APLL1_CK]);
> +clk_ck_mux_aud1_parent_err:
> + mt8196_afe_disable_clk(afe, afe_priv->clk[MT8196_CLK_TOP_MUX_AUD_1]);
> +clk_ck_mux_aud1_err:
> + mt8196_afe_disable_clk(afe, afe_priv->clk[MT8196_CLK_VLP_MUX_AUDIO_H]);
> +
> + return ret;
> +}
> +
> +static void mt8196_afe_apll_init(struct mtk_base_afe *afe)
> +{
> + struct mt8196_afe_private *afe_priv = afe->platform_priv;
> +
> + if (!afe_priv->vlp_ck) {
> + dev_warn(afe->dev, "vlp_ck regmap is null ptr\n");
> + return;
> + }
> +
> + regmap_write(afe_priv->vlp_ck, VLP_APLL1_TUNER_CON0, VLP_APLL1_TUNER_CON0_VALUE);
> + regmap_write(afe_priv->vlp_ck, VLP_APLL2_TUNER_CON0, VLP_APLL2_TUNER_CON0_VALUE);
Per offline discussion, this should be moved to the vlp clk driver.
This was already mentioned to the clk patch owners in a recent review.
> +}
[...]
> +/* mck */
> +struct mt8196_mck_div {
> + int m_sel_id;
> + int div_clk_id;
> +};
> +
> +static const struct mt8196_mck_div mck_div[MT8196_MCK_NUM] = {
> + [MT8196_I2SIN0_MCK] = {
> + .m_sel_id = MT8196_CLK_TOP_I2SIN0_M_SEL,
> + .div_clk_id = MT8196_CLK_TOP_APLL12_DIV_I2SIN0,
> + },
> + [MT8196_I2SIN1_MCK] = {
> + .m_sel_id = MT8196_CLK_TOP_I2SIN1_M_SEL,
> + .div_clk_id = MT8196_CLK_TOP_APLL12_DIV_I2SIN1,
> + },
> + [MT8196_FMI2S_MCK] = {
> + .m_sel_id = MT8196_CLK_TOP_FMI2S_M_SEL,
> + .div_clk_id = MT8196_CLK_TOP_APLL12_DIV_FMI2S,
> + },
> + [MT8196_TDMOUT_MCK] = {
> + .m_sel_id = MT8196_CLK_TOP_TDMOUT_M_SEL,
> + .div_clk_id = MT8196_CLK_TOP_APLL12_DIV_TDMOUT_M,
> + },
> + [MT8196_TDMOUT_BCK] = {
> + .m_sel_id = -1,
> + .div_clk_id = MT8196_CLK_TOP_APLL12_DIV_TDMOUT_B,
> + },
> +};
In the upstream clk patch submission, the mux and divider have been
combined. So this part could be simplified a bit. Also...
> +int mt8196_mck_enable(struct mtk_base_afe *afe, int mck_id, int rate)
> +{
> + struct mt8196_afe_private *afe_priv = afe->platform_priv;
> + int apll = mt8196_get_apll_by_rate(afe, rate);
> + int apll_clk_id = apll == MT8196_APLL1 ?
> + MT8196_CLK_TOP_MUX_AUD_1 : MT8196_CLK_TOP_MUX_AUD_2;
> + int m_sel_id;
> + int div_clk_id;
> + int ret;
> +
> + dev_dbg(afe->dev, "mck_id: %d, rate: %d\n", mck_id, rate);
> +
> + if (mck_id >= MT8196_MCK_NUM || mck_id < 0)
> + return -EINVAL;
> +
> + m_sel_id = mck_div[mck_id].m_sel_id;
> + div_clk_id = mck_div[mck_id].div_clk_id;
> +
> + /* select apll */
> + if (m_sel_id >= 0) {
> + ret = mt8196_afe_enable_clk(afe, afe_priv->clk[m_sel_id]);
> + if (ret)
> + return ret;
> +
> + ret = mt8196_afe_set_clk_parent(afe, afe_priv->clk[m_sel_id],
> + afe_priv->clk[apll_clk_id]);
This part would be taken care of by the framework as well. There's no
need to do it explicitly.
> + if (ret)
> + return ret;
> + }
> +
> + /* enable div, set rate */
> + if (div_clk_id < 0) {
> + dev_err(afe->dev, "invalid div_clk_id %d\n", div_clk_id);
> + return -EINVAL;
> + }
> + if (div_clk_id == MT8196_CLK_TOP_APLL12_DIV_TDMOUT_B)
> + rate = rate * 16;
rate *= 16;
> +
> + ret = mt8196_afe_enable_clk(afe, afe_priv->clk[div_clk_id]);
> + if (ret)
> + return ret;
> +
> + ret = mt8196_afe_set_clk_rate(afe, afe_priv->clk[div_clk_id], rate);
> + if (ret)
> + return ret;
> +
> + return 0;
> +}
> +
> +int mt8196_mck_disable(struct mtk_base_afe *afe, int mck_id)
> +{
> + struct mt8196_afe_private *afe_priv = afe->platform_priv;
> + int m_sel_id;
> + int div_clk_id;
> +
> + dev_dbg(afe->dev, "mck_id: %d.\n", mck_id);
> +
> + if (mck_id < 0) {
> + dev_err(afe->dev, "mck_id = %d < 0\n", mck_id);
> + return -EINVAL;
> + }
> +
> + m_sel_id = mck_div[mck_id].m_sel_id;
> + div_clk_id = mck_div[mck_id].div_clk_id;
> +
> + if (div_clk_id < 0) {
> + dev_err(afe->dev, "div_clk_id = %d < 0\n",
> + div_clk_id);
> + return -EINVAL;
> + }
> +
> + mt8196_afe_disable_clk(afe, afe_priv->clk[div_clk_id]);
> +
> + if (m_sel_id >= 0)
> + mt8196_afe_disable_clk(afe, afe_priv->clk[m_sel_id]);
> +
> + return 0;
> +}
> +
> +int mt8196_afe_enable_reg_rw_clk(struct mtk_base_afe *afe)
> +{
> + struct mt8196_afe_private *afe_priv = afe->platform_priv;
> +
> + /* bus clock for AFE external access, like DRAM */
> + mt8196_afe_enable_clk(afe, afe_priv->clk[MT8196_CLK_TOP_ADSP_SEL]);
> +
> + /* bus clock for AFE internal access, like AFE SRAM */
> + mt8196_afe_enable_clk(afe, afe_priv->clk[MT8196_CLK_VLP_MUX_AUDIOINTBUS]);
> + mt8196_afe_set_clk_parent(afe, afe_priv->clk[MT8196_CLK_VLP_MUX_AUDIOINTBUS],
> + afe_priv->clk[MT8196_CLK_VLP_CLK26M]);
If you are setting it to 26M, then it probably doesn't matter what parent
it uses? I would just drop this.
> + /* enable audio vlp clock source */
> + mt8196_afe_enable_clk(afe, afe_priv->clk[MT8196_CLK_VLP_MUX_AUDIO_H]);
> + mt8196_afe_set_clk_parent(afe, afe_priv->clk[MT8196_CLK_VLP_MUX_AUDIO_H],
> + afe_priv->clk[MT8196_CLK_VLP_CLK26M]);
Same here.
> +
> + /* AFE hw clock */
> + /* IPM2.0: USE HOPPING & 26M */
> + /* set in the regmap_register_patch */
> + return 0;
> +}
> +
> +int mt8196_afe_disable_reg_rw_clk(struct mtk_base_afe *afe)
> +{
> + struct mt8196_afe_private *afe_priv = afe->platform_priv;
> +
> + /* IPM2.0: Use HOPPING & 26M */
> + /* set in the regmap_register_patch */
> +
> + mt8196_afe_set_clk_parent(afe, afe_priv->clk[MT8196_CLK_VLP_MUX_AUDIO_H],
> + afe_priv->clk[MT8196_CLK_VLP_CLK26M]);
There's no point in selecting a parent on a clock that is going to be disabled.
> +
> + mt8196_afe_disable_clk(afe, afe_priv->clk[MT8196_CLK_VLP_MUX_AUDIO_H]);
> + mt8196_afe_set_clk_parent(afe, afe_priv->clk[MT8196_CLK_VLP_MUX_AUDIOINTBUS],
> + afe_priv->clk[MT8196_CLK_VLP_CLK26M]);
> + mt8196_afe_disable_clk(afe, afe_priv->clk[MT8196_CLK_VLP_MUX_AUDIOINTBUS]);
> + mt8196_afe_disable_clk(afe, afe_priv->clk[MT8196_CLK_TOP_ADSP_SEL]);
> + return 0;
> +}
> +
> +int mt8196_afe_enable_main_clock(struct mtk_base_afe *afe)
> +{
> + mt8196_afe_enable_top_cg(afe, MT8196_AUDIO_26M_EN_ON);
> + return 0;
> +}
> +
> +int mt8196_afe_disable_main_clock(struct mtk_base_afe *afe)
> +{
> + mt8196_afe_disable_top_cg(afe, MT8196_AUDIO_26M_EN_ON);
> + return 0;
> +}
> +
> +int mt8196_init_clock(struct mtk_base_afe *afe)
> +{
> + struct mt8196_afe_private *afe_priv = afe->platform_priv;
> + int ret = 0;
> + int i = 0;
> +
> + afe_priv->clk = devm_kcalloc(afe->dev, MT8196_CLK_NUM, sizeof(*afe_priv->clk),
> + GFP_KERNEL);
> + if (!afe_priv->clk)
> + return -ENOMEM;
> +
> + for (i = 0; i < MT8196_CLK_NUM; i++) {
> + afe_priv->clk[i] = devm_clk_get(afe->dev, aud_clks[i]);
> + if (IS_ERR(afe_priv->clk[i])) {
> + dev_err(afe->dev, "devm_clk_get %s fail\n", aud_clks[i]);
> + return PTR_ERR(afe_priv->clk[i]);
> + }
> + }
> +
> + afe_priv->vlp_ck = syscon_regmap_lookup_by_phandle(afe->dev->of_node,
> + "vlpcksys");
> + if (IS_ERR(afe_priv->vlp_ck)) {
> + dev_err(afe->dev, "Cannot find vlpcksys\n");
> + return PTR_ERR(afe_priv->vlp_ck);
> + }
As mentioned, the tuner bits will be moved to the clk driver, so this
bit is no longer needed.
> +
> + mt8196_afe_apll_init(afe);
> +
> + ret = mt8196_afe_disable_apll(afe);
> + if (ret)
> + return ret;
> +
> + return 0;
> +}
> +
> diff --git a/sound/soc/mediatek/mt8196/mt8196-afe-clk.h b/sound/soc/mediatek/mt8196/mt8196-afe-clk.h
> new file mode 100644
> index 000000000000..854da3844104
> --- /dev/null
> +++ b/sound/soc/mediatek/mt8196/mt8196-afe-clk.h
> @@ -0,0 +1,80 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * mt8196-afe-clk.h -- Mediatek MT8196 AFE Clock Control definitions
> + *
> + * Copyright (c) 2024 MediaTek Inc.
> + * Author: Darren Ye <darren.ye@mediatek.com>
> + */
> +
> +#ifndef _MT8196_AFE_CLOCK_CTRL_H_
> +#define _MT8196_AFE_CLOCK_CTRL_H_
> +
> +/* vlp_cksys_clk: 0x1c016000 */
> +#define VLP_APLL1_TUNER_CON0 0x02a4
> +#define VLP_APLL2_TUNER_CON0 0x02a8
> +
> +/* vlp apll1 tuner default value*/
> +#define VLP_APLL1_TUNER_CON0_VALUE 0x6f28bd4d
> +/* vlp apll2 tuner default value + 1*/
> +#define VLP_APLL2_TUNER_CON0_VALUE 0x78fd5265
> +
> +/* APLL */
> +#define APLL1_W_NAME "APLL1"
> +#define APLL2_W_NAME "APLL2"
> +
> +enum {
> + MT8196_APLL1 = 0,
> + MT8196_APLL2,
> +};
> +
> +enum {
> + /* vlp clk */
> + MT8196_CLK_VLP_MUX_AUDIOINTBUS,
> + MT8196_CLK_VLP_MUX_AUD_ENG1,
> + MT8196_CLK_VLP_MUX_AUD_ENG2,
> + MT8196_CLK_VLP_MUX_AUDIO_H,
> + MT8196_CLK_VLP_CLK26M,
> + /* pll */
> + MT8196_CLK_TOP_APLL1_CK,
> + MT8196_CLK_TOP_APLL2_CK,
> + /* divider */
> + MT8196_CLK_TOP_APLL1_D4,
> + MT8196_CLK_TOP_APLL2_D4,
> + MT8196_CLK_TOP_APLL12_DIV_I2SIN0,
> + MT8196_CLK_TOP_APLL12_DIV_I2SIN1,
> + MT8196_CLK_TOP_APLL12_DIV_FMI2S,
> + MT8196_CLK_TOP_APLL12_DIV_TDMOUT_M,
> + MT8196_CLK_TOP_APLL12_DIV_TDMOUT_B,
> + /* mux */
> + MT8196_CLK_TOP_MUX_AUD_1,
> + MT8196_CLK_TOP_MUX_AUD_2,
> + MT8196_CLK_TOP_I2SIN0_M_SEL,
> + MT8196_CLK_TOP_I2SIN1_M_SEL,
> + MT8196_CLK_TOP_FMI2S_M_SEL,
> + MT8196_CLK_TOP_TDMOUT_M_SEL,
> + MT8196_CLK_TOP_ADSP_SEL,
> + /* top 26m */
> + MT8196_CLK_TOP_CLK26M,
> + MT8196_CLK_NUM,
The list should be reworked based on review comments to the DT bindings
and our offline discussions. Basically only clocks that directly feed
into the hardware, or otherwise have a reason to be referenced should
be listed.
ChenYu
> +};
> +
> +struct mtk_base_afe;
> +
> +int mt8196_mck_enable(struct mtk_base_afe *afe, int mck_id, int rate);
> +int mt8196_mck_disable(struct mtk_base_afe *afe, int mck_id);
> +int mt8196_get_apll_rate(struct mtk_base_afe *afe, int apll);
> +int mt8196_get_apll_by_rate(struct mtk_base_afe *afe, int rate);
> +int mt8196_get_apll_by_name(struct mtk_base_afe *afe, const char *name);
> +int mt8196_init_clock(struct mtk_base_afe *afe);
> +int mt8196_afe_enable_clk(struct mtk_base_afe *afe, struct clk *clk);
> +void mt8196_afe_disable_clk(struct mtk_base_afe *afe, struct clk *clk);
> +int mt8196_apll1_enable(struct mtk_base_afe *afe);
> +void mt8196_apll1_disable(struct mtk_base_afe *afe);
> +int mt8196_apll2_enable(struct mtk_base_afe *afe);
> +void mt8196_apll2_disable(struct mtk_base_afe *afe);
> +int mt8196_afe_enable_main_clock(struct mtk_base_afe *afe);
> +int mt8196_afe_disable_main_clock(struct mtk_base_afe *afe);
> +int mt8196_afe_enable_reg_rw_clk(struct mtk_base_afe *afe);
> +int mt8196_afe_disable_reg_rw_clk(struct mtk_base_afe *afe);
> +
> +#endif
> --
> 2.45.2
>
>
^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [PATCH v6 07/10] ASoC: mediatek: mt8196: add platform driver
2025-07-08 11:15 ` [PATCH v6 07/10] ASoC: mediatek: mt8196: add " Darren.Ye
@ 2025-08-05 10:40 ` Chen-Yu Tsai
0 siblings, 0 replies; 24+ messages in thread
From: Chen-Yu Tsai @ 2025-08-05 10:40 UTC (permalink / raw)
To: Darren.Ye
Cc: Liam Girdwood, Mark Brown, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Matthias Brugger, AngeloGioacchino Del Regno,
Jaroslav Kysela, Takashi Iwai, Linus Walleij, Bartosz Golaszewski,
linux-sound, devicetree, linux-kernel, linux-arm-kernel,
linux-mediatek, linux-gpio
On Tue, Jul 8, 2025 at 7:34 PM Darren.Ye <darren.ye@mediatek.com> wrote:
>
> From: Darren Ye <darren.ye@mediatek.com>
>
> Add mt8196 platform driver.
>
> Signed-off-by: Darren Ye <darren.ye@mediatek.com>
> ---
> sound/soc/mediatek/Kconfig | 10 +
> sound/soc/mediatek/Makefile | 1 +
> sound/soc/mediatek/mt8196/Makefile | 15 +
> sound/soc/mediatek/mt8196/mt8196-afe-pcm.c | 2632 ++++++++++++++++++++
> 4 files changed, 2658 insertions(+)
> create mode 100644 sound/soc/mediatek/mt8196/Makefile
> create mode 100644 sound/soc/mediatek/mt8196/mt8196-afe-pcm.c
>
> diff --git a/sound/soc/mediatek/Kconfig b/sound/soc/mediatek/Kconfig
> index 10ca8bccabdd..7003d71b847c 100644
> --- a/sound/soc/mediatek/Kconfig
> +++ b/sound/soc/mediatek/Kconfig
> @@ -322,4 +322,14 @@ config SND_SOC_MT8365_MT6357
> Select Y if you have such device.
> If unsure select "N".
>
> +config SND_SOC_MT8196
> + tristate "ASoC support for Mediatek MT8196 chip"
> + depends on ARCH_MEDIATEK
> + select SND_SOC_MEDIATEK
> + help
> + This adds ASoC driver for Mediatek MT8196 boards
> + that can be used with other codecs.
> + 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 4b55434f2168..11d7c484a5d3 100644
> --- a/sound/soc/mediatek/Makefile
> +++ b/sound/soc/mediatek/Makefile
> @@ -10,3 +10,4 @@ obj-$(CONFIG_SND_SOC_MT8188) += mt8188/
> obj-$(CONFIG_SND_SOC_MT8192) += mt8192/
> obj-$(CONFIG_SND_SOC_MT8195) += mt8195/
> obj-$(CONFIG_SND_SOC_MT8365) += mt8365/
> +obj-$(CONFIG_SND_SOC_MT8196) += mt8196/
> diff --git a/sound/soc/mediatek/mt8196/Makefile b/sound/soc/mediatek/mt8196/Makefile
> new file mode 100644
> index 000000000000..af41ece87672
> --- /dev/null
> +++ b/sound/soc/mediatek/mt8196/Makefile
> @@ -0,0 +1,15 @@
> +# SPDX-License-Identifier: GPL-2.0
> +
> +# common include path
> +subdir-ccflags-y += -I$(srctree)/sound/soc/mediatek/common
Please avoid using include paths. Simply use the relative path in
each include statement.
> +
> +# platform driver
> +snd-soc-mt8196-afe-objs += \
> + mt8196-afe-pcm.o \
> + mt8196-afe-clk.o \
> + mt8196-dai-adda.o \
> + mt8196-dai-i2s.o \
> + mt8196-dai-tdm.o
There's no need to have just one entry per line.
> +
> +obj-$(CONFIG_SND_SOC_MT8196) += snd-soc-mt8196-afe.o
> +
> diff --git a/sound/soc/mediatek/mt8196/mt8196-afe-pcm.c b/sound/soc/mediatek/mt8196/mt8196-afe-pcm.c
> new file mode 100644
> index 000000000000..41a48ba89b95
> --- /dev/null
> +++ b/sound/soc/mediatek/mt8196/mt8196-afe-pcm.c
> @@ -0,0 +1,2632 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Mediatek ALSA SoC AFE platform driver for 8196
> + *
> + * Copyright (c) 2024 MediaTek Inc.
Please update to 2025.
> + * Author: Darren Ye <darren.ye@mediatek.com>
> + */
> +
> +#include <linux/delay.h>
> +#include <linux/dma-mapping.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/of_address.h>
> +#include <linux/pm_runtime.h>
> +#include <linux/regmap.h>
> +#include <linux/of_device.h>
> +#include <sound/soc.h>
> +#include <linux/of_reserved_mem.h>
Please sort, and also split out the sound header to a separate group.
> +
> +#include "mt8196-afe-clk.h"
> +#include "mt8196-afe-common.h"
> +#include "mtk-afe-fe-dai.h"
> +#include "mtk-afe-platform-driver.h"
> +#include "mt8196-interconnection.h"
> +
> +static const struct snd_pcm_hardware mt8196_afe_hardware = {
You should directly include <sound/pcm.h> for the definition of this
structure.
> + .info = (SNDRV_PCM_INFO_MMAP |
> + SNDRV_PCM_INFO_NO_PERIOD_WAKEUP |
> + SNDRV_PCM_INFO_INTERLEAVED |
> + SNDRV_PCM_INFO_MMAP_VALID),
> + .formats = (SNDRV_PCM_FMTBIT_S16_LE |
> + SNDRV_PCM_FMTBIT_S24_LE |
> + SNDRV_PCM_FMTBIT_S32_LE),
> + .period_bytes_min = 96,
> + .period_bytes_max = 4 * 48 * 1024,
> + .periods_min = 2,
> + .periods_max = 256,
> + .buffer_bytes_max = 256 * 1024,
> + .fifo_size = 0,
> +};
> +
> +static unsigned int mt8196_rate_transform(struct device *dev,
> + unsigned int rate)
> +{
> + switch (rate) {
> + case 8000:
> + return MTK_AFE_IPM2P0_RATE_8K;
> + case 11025:
> + return MTK_AFE_IPM2P0_RATE_11K;
> + case 12000:
> + return MTK_AFE_IPM2P0_RATE_12K;
> + case 16000:
> + return MTK_AFE_IPM2P0_RATE_16K;
> + case 22050:
> + return MTK_AFE_IPM2P0_RATE_22K;
> + case 24000:
> + return MTK_AFE_IPM2P0_RATE_24K;
> + case 32000:
> + return MTK_AFE_IPM2P0_RATE_32K;
> + case 44100:
> + return MTK_AFE_IPM2P0_RATE_44K;
> + case 48000:
> + return MTK_AFE_IPM2P0_RATE_48K;
> + case 88200:
> + return MTK_AFE_IPM2P0_RATE_88K;
> + case 96000:
> + return MTK_AFE_IPM2P0_RATE_96K;
> + case 176400:
> + return MTK_AFE_IPM2P0_RATE_176K;
> + case 192000:
> + return MTK_AFE_IPM2P0_RATE_192K;
> + /* not support 260K */
> + case 352800:
> + return MTK_AFE_IPM2P0_RATE_352K;
> + case 384000:
> + return MTK_AFE_IPM2P0_RATE_384K;
> + default:
> + dev_err(dev, "rate %u invalid, use %d!!!\n",
> + rate, MTK_AFE_IPM2P0_RATE_48K);
> + return MTK_AFE_IPM2P0_RATE_48K;
> + }
> +}
> +
> +static void mt8196_set_cm_rate(struct mtk_base_afe *afe, int id, unsigned int rate)
> +{
> + struct mt8196_afe_private *afe_priv = afe->platform_priv;
> +
> + afe_priv->cm_rate[id] = rate;
> +}
This is only one line, and only used once. I would just inline it directly.
> +
> +static inline int mt8196_convert_cm_ch(unsigned int ch)
> +{
> + return ch - 1;
This is only one line and only used once. Just inline it.
> +}
> +
> +static unsigned int calculate_cm_update(unsigned int rate, unsigned int ch)
> +{
> + return (((26000000 / rate) - 10) / (ch / 2)) - 1;
This is only one line and only used once. Just inline it.
> +}
> +
> +static int mt8196_set_cm(struct mtk_base_afe *afe, int id,
> + bool update, bool swap, unsigned int ch)
> +{
> + struct mt8196_afe_private *afe_priv = afe->platform_priv;
> + unsigned int rate = afe_priv->cm_rate[id];
> + unsigned int rate_val = mt8196_rate_transform(afe->dev, rate);
> + unsigned int update_val = update ? calculate_cm_update(rate, ch) : 0x64;
> + int reg = AFE_CM0_CON0 + 0x10 * id;
> +
> + dev_dbg(afe->dev, "CM%d, rate %d, update %d, swap %d, ch %d\n",
> + id, rate, update, swap, ch);
> +
> + /* update cnt */
> + regmap_update_bits(afe->regmap,
> + reg,
This can be one the first line. Same for the other calls in this function.
> + AFE_CM_UPDATE_CNT_MASK << AFE_CM_UPDATE_CNT_SFT,
> + update_val << AFE_CM_UPDATE_CNT_SFT);
> +
> + /* rate */
> + regmap_update_bits(afe->regmap,
> + reg,
> + AFE_CM_1X_EN_SEL_FS_MASK << AFE_CM_1X_EN_SEL_FS_SFT,
> + rate_val << AFE_CM_1X_EN_SEL_FS_SFT);
> +
> + /* ch num */
> + ch = mt8196_convert_cm_ch(ch);
> + regmap_update_bits(afe->regmap,
> + reg,
> + AFE_CM_CH_NUM_MASK << AFE_CM_CH_NUM_SFT,
> + ch << AFE_CM_CH_NUM_SFT);
> +
> + /* swap */
> + regmap_update_bits(afe->regmap,
> + reg,
> + AFE_CM_BYTE_SWAP_MASK << AFE_CM_BYTE_SWAP_SFT,
> + swap << AFE_CM_BYTE_SWAP_SFT);
> +
> + return 0;
> +}
> +
> +static int mt8196_enable_cm_bypass(struct mtk_base_afe *afe, int id, bool en)
> +{
> + return regmap_update_bits(afe->regmap,
> + AFE_CM0_CON0 + 0x10 * id,
> + AFE_CM_BYPASS_MODE_MASK << AFE_CM_BYPASS_MODE_SFT,
> + en << AFE_CM_BYPASS_MODE_SFT);
Please align the start of the line with the left parenthesis.
> +}
> +
> +static int mt8196_fe_startup(struct snd_pcm_substream *substream,
> + struct snd_soc_dai *dai)
> +{
> + struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
> + struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
> + struct snd_pcm_runtime *runtime = substream->runtime;
> + struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
> + int memif_num = cpu_dai->id;
> + struct mtk_base_afe_memif *memif = &afe->memif[memif_num];
> + const struct snd_pcm_hardware *mtk_afe_hardware = afe->mtk_afe_hardware;
> + int ret;
> +
> + dev_dbg(afe->dev, "memif_num: %d.\n", memif_num);
> +
> + memif->substream = substream;
> +
> + snd_pcm_hw_constraint_step(substream->runtime, 0,
> + SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 16);
> +
> + snd_soc_set_runtime_hwparams(substream, mtk_afe_hardware);
> +
> + ret = snd_pcm_hw_constraint_integer(runtime,
> + SNDRV_PCM_HW_PARAM_PERIODS);
> + if (ret < 0)
> + dev_info(afe->dev, "snd_pcm_hw_constraint_integer failed\n");
This should be a warning at least. Otherwise just ignore any error.
> +
> + /* dynamic allocate irq to memif */
> + if (memif->irq_usage < 0) {
> + int irq_id = mtk_dynamic_irq_acquire(afe);
> +
> + if (irq_id != afe->irqs_size) {
> + /* link */
> + memif->irq_usage = irq_id;
> + } else {
> + dev_err(afe->dev, "no more asys irq\n");
> + ret = -EBUSY;
> + }
> + }
> + return ret;
> +}
> +
> +static void mt8196_fe_shutdown(struct snd_pcm_substream *substream,
> + struct snd_soc_dai *dai)
> +{
> + struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
> + struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
> + struct mt8196_afe_private *afe_priv = afe->platform_priv;
> + struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
> + int memif_num = cpu_dai->id;
> + struct mtk_base_afe_memif *memif = &afe->memif[memif_num];
> + int irq_id = memif->irq_usage;
> +
> + dev_dbg(afe->dev, "memif_num: %d.\n", memif_num);
> +
> + memif->substream = NULL;
> + afe_priv->irq_cnt[memif_num] = 0;
> + afe_priv->xrun_assert[memif_num] = 0;
> +
> + if (!memif->const_irq) {
> + mtk_dynamic_irq_release(afe, irq_id);
> + memif->irq_usage = -1;
> + memif->substream = NULL;
> + }
> +}
> +
> +static int mt8196_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);
> + unsigned int channels = params_channels(params);
> + struct mt8196_afe_private *afe_priv = afe->platform_priv;
> + int id = snd_soc_rtd_to_cpu(rtd, 0)->id;
> + struct mtk_base_afe_memif *memif = &afe->memif[id];
> + const struct mtk_base_memif_data *data = memif->data;
> +
> + afe_priv->cm_channels = channels;
> +
> + /* set channels */
> + if (data->ch_num_shift >= 0) {
> + regmap_update_bits(afe->regmap, data->ch_num_reg,
> + data->ch_num_maskbit << data->ch_num_shift,
> + channels << data->ch_num_shift);
> + }
> +
> + return mtk_afe_fe_hw_params(substream, params, dai);
> +}
> +
> +static int mt8196_fe_trigger(struct snd_pcm_substream *substream, int cmd,
> + struct snd_soc_dai *dai)
> +{
> + struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
> + struct snd_pcm_runtime *const runtime = substream->runtime;
> + struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
> + struct mt8196_afe_private *afe_priv = afe->platform_priv;
> + struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
> + int id = cpu_dai->id;
> + struct mtk_base_afe_memif *memif = &afe->memif[id];
> + int irq_id = memif->irq_usage;
> + struct mtk_base_afe_irq *irqs = &afe->irqs[irq_id];
> + const struct mtk_base_irq_data *irq_data = irqs->irq_data;
> + unsigned int counter = runtime->period_size;
> + unsigned int rate = runtime->rate;
> + unsigned int tmp_reg;
> + int fs;
> + int ret;
> +
> + dev_info(afe->dev, "%s cmd %d, irq_id %d\n",
> + memif->data->name, cmd, irq_id);
Make it debug level.
> +
> + switch (cmd) {
> + case SNDRV_PCM_TRIGGER_START:
> + case SNDRV_PCM_TRIGGER_RESUME:
> + dev_dbg(afe->dev, "%s cmd %d, id %d\n",
> + memif->data->name, cmd, id);
> + ret = mtk_memif_set_enable(afe, id);
> + if (ret) {
> + dev_err(afe->dev, "id %d, memif enable fail.\n", id);
> + return ret;
> + }
> +
> + /*
> + * for small latency record
> + * ul memif need read some data before irq enable
> + */
> + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
> + if ((runtime->period_size * 1000) / rate <= 10)
> + udelay(300);
> + }
Curly braces are not needed here.
Also, please consider generalizing this part. This is the only difference
between this version and the common FE trigger function. The MT8189 driver
also contains this, so this should somehow be shared.
Maybe add .capture_prefill_* fields to |struct mtk_base_afe| to model it.
> +
> + /* set irq counter */
> + if (afe_priv->irq_cnt[id] > 0)
> + counter = afe_priv->irq_cnt[id];
> +
> + regmap_update_bits(afe->regmap,
> + irq_data->irq_cnt_reg,
> + irq_data->irq_cnt_maskbit << irq_data->irq_cnt_shift,
> + counter << irq_data->irq_cnt_shift);
> +
> + /* set irq fs */
> + fs = afe->irq_fs(substream, runtime->rate);
> + if (fs < 0)
> + return -EINVAL;
Return `fs` directly, since it should be a valid error number?
Also, please add an error message.
> +
> + if (irq_data->irq_fs_reg >= 0)
> + regmap_update_bits(afe->regmap,
> + irq_data->irq_fs_reg,
> + irq_data->irq_fs_maskbit << irq_data->irq_fs_shift,
> + fs << irq_data->irq_fs_shift);
> +
> + /* enable interrupt */
> + regmap_update_bits(afe->regmap,
> + irq_data->irq_en_reg,
> + 1 << irq_data->irq_en_shift,
> + 1 << irq_data->irq_en_shift);
> +
> + return 0;
> + case SNDRV_PCM_TRIGGER_STOP:
> + case SNDRV_PCM_TRIGGER_SUSPEND:
> + ret = mtk_memif_set_disable(afe, id);
> + if (ret) {
> + dev_warn(afe->dev,
> + "id %d, memif disable fail\n", id);
> + }
Curly braces not needed.
> +
> + /* disable interrupt */
> + regmap_update_bits(afe->regmap,
> + irq_data->irq_en_reg,
> + 1 << irq_data->irq_en_shift,
> + 0 << irq_data->irq_en_shift);
> +
> + /* clear pending IRQ */
> + regmap_read(afe->regmap, irq_data->irq_clr_reg, &tmp_reg);
> + regmap_update_bits(afe->regmap, irq_data->irq_clr_reg,
> + AFE_IRQ_CLR_CFG_MASK_SFT | AFE_IRQ_MISS_FLAG_CLR_CFG_MASK_SFT,
> + tmp_reg ^ (AFE_IRQ_CLR_CFG_MASK_SFT |
> + AFE_IRQ_MISS_FLAG_CLR_CFG_MASK_SFT));
Why is this an XOR?
> +
> + return ret;
> + default:
> + return -EINVAL;
> + }
> +}
> +
> +static int mt8196_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 =
> + snd_soc_rtdcom_lookup(rtd, AFE_PCM_NAME);
> + struct mtk_base_afe *afe = NULL;
> + struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
> + int id = cpu_dai->id;
> + unsigned int rate_reg;
> + int cm;
> +
> + if (!component)
> + return -EINVAL;
> +
> + afe = snd_soc_component_get_drvdata(component);
> +
> + if (!afe)
> + return -EINVAL;
> +
> + rate_reg = mt8196_rate_transform(afe->dev, rate);
> +
> + switch (id) {
> + case MT8196_MEMIF_VUL8:
> + case MT8196_MEMIF_VUL_CM0:
> + cm = CM0;
> + break;
> + case MT8196_MEMIF_VUL9:
> + case MT8196_MEMIF_VUL_CM1:
> + cm = CM1;
> + break;
> + case MT8196_MEMIF_VUL10:
> + case MT8196_MEMIF_VUL_CM2:
> + cm = CM2;
> + break;
> + default:
> + cm = CM0;
> + break;
> + }
> +
> + mt8196_set_cm_rate(afe, cm, rate);
Please move the CM parts to mt8196_fe_hw_params(). You already have
a custom version of .hw_params. The .memif_fs callback is supposed
to be a pure (no side effect) function that converts one value to
another.
> +
> + return rate_reg;
> +}
> +
> +static int mt8196_get_dai_fs(struct mtk_base_afe *afe,
> + int dai_id, unsigned int rate)
> +{
> + return mt8196_rate_transform(afe->dev, rate);
> +}
> +
> +static int mt8196_irq_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 =
> + snd_soc_rtdcom_lookup(rtd, AFE_PCM_NAME);
> + struct mtk_base_afe *afe = NULL;
> +
> + if (!component)
> + return -EINVAL;
> + afe = snd_soc_component_get_drvdata(component);
> + return mt8196_rate_transform(afe->dev, rate);
> +}
> +
> +static int mt8196_get_memif_pbuf_size(struct snd_pcm_substream *substream)
> +{
> + struct snd_pcm_runtime *runtime = substream->runtime;
> +
> + if ((runtime->period_size * 1000) / runtime->rate > 10)
> + return MT8196_MEMIF_PBUF_SIZE_256_BYTES;
> + else
> + return MT8196_MEMIF_PBUF_SIZE_32_BYTES;
> +}
> +
> +/* FE DAIs */
> +static const struct snd_soc_dai_ops mt8196_memif_dai_ops = {
> + .startup = mt8196_fe_startup,
> + .shutdown = mt8196_fe_shutdown,
> + .hw_params = mt8196_fe_hw_params,
> + .hw_free = mtk_afe_fe_hw_free,
> + .prepare = mtk_afe_fe_prepare,
> + .trigger = mt8196_fe_trigger,
> +};
> +
> +#define MTK_PCM_RATES (SNDRV_PCM_RATE_8000_48000 |\
Please add a space betwee '|' and '\'. Same for the next few blocks.
> + SNDRV_PCM_RATE_88200 |\
> + SNDRV_PCM_RATE_96000 |\
> + SNDRV_PCM_RATE_176400 |\
> + SNDRV_PCM_RATE_192000)
> +
> +#define MTK_PCM_DAI_RATES (SNDRV_PCM_RATE_8000 |\
> + SNDRV_PCM_RATE_16000 |\
> + SNDRV_PCM_RATE_32000 |\
> + SNDRV_PCM_RATE_48000)
> +
> +#define MTK_PCM_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\
> + SNDRV_PCM_FMTBIT_S24_LE |\
> + SNDRV_PCM_FMTBIT_S32_LE)
> +
[...]
> +static const struct snd_kcontrol_new mt8196_pcm_kcontrols[] = {
> +};
Drop the empty variable.
> +enum {
> + CM0_MUX_VUL8_2CH,
> + CM0_MUX_VUL8_8CH,
> + CM0_MUX_MASK,
> +};
> +
> +enum {
> + CM1_MUX_VUL9_2CH,
> + CM1_MUX_VUL9_16CH,
> + CM1_MUX_MASK,
> +};
> +
> +enum {
> + CM2_MUX_VUL10_2CH,
> + CM2_MUX_VUL10_32CH,
> + CM2_MUX_MASK,
> +};
As mentioned below, these aren't needed.
> +static int ul_cm0_event(struct snd_soc_dapm_widget *w,
> + struct snd_kcontrol *kcontrol,
> + int event)
> +{
> + struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
> + struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
> + struct mt8196_afe_private *afe_priv = afe->platform_priv;
> + unsigned int channels = afe_priv->cm_channels;
> +
> + dev_dbg(afe->dev, "event 0x%x, name %s, channels %u\n",
> + event, w->name, channels);
> +
> + switch (event) {
> + case SND_SOC_DAPM_PRE_PMU:
> + mt8196_enable_cm_bypass(afe, CM0, 0x0);
The last parameter is of type bool. Please use "true" or "false".
> + mt8196_set_cm(afe, CM0, true, false, channels);
> + regmap_update_bits(afe->regmap, AUDIO_TOP_CON0,
> + PDN_CM0_MASK_SFT, 0 << PDN_CM0_SFT);
> + break;
> + case SND_SOC_DAPM_PRE_PMD:
Why is it PRE_PMD and not POST_PMD? Seems out of sequence.
> + mt8196_enable_cm_bypass(afe, CM0, 0x1);
> + regmap_update_bits(afe->regmap, AUDIO_TOP_CON0,
> + PDN_CM0_MASK_SFT, 1 << PDN_CM0_SFT);
> + break;
> + default:
> + break;
> + }
Add an empty line here.
Above comments apply to the other CM event callbacks below.
> + return 0;
> +}
[...]
> +
> +/* dma widget & routes*/
You should mention in a comment here and in the commit message that the
mixer controls and routes are by no means fully implemented; only the
ones that are intended to be used are, as other wise a fully interconnected
switch bar mixer would introduce way too many unused controls.
[...]
> +static const char * const cm0_mux_map[] = {
> + "CM0_8CH_PATH",
> + "UL8_2CH_PATH",
> +};
> +
> +static const char * const cm1_mux_map[] = {
> + "CM1_16CH_PATH",
> + "UL9_2CH_PATH",
> +};
> +
> +static const char * const cm2_mux_map[] = {
> + "CM2_32CH_PATH",
> + "UL10_2CH_PATH",
> +};
> +
> +static int cm0_mux_map_value[] = {
> + CM0_MUX_VUL8_8CH,
> + CM0_MUX_VUL8_2CH,
> +};
> +
> +static int cm1_mux_map_value[] = {
> + CM1_MUX_VUL9_16CH,
> + CM1_MUX_VUL9_2CH,
> +};
> +
> +static int cm2_mux_map_value[] = {
> + CM2_MUX_VUL10_32CH,
> + CM2_MUX_VUL10_2CH,
> +};
> +
> +static SOC_VALUE_ENUM_SINGLE_DECL(ul_cm0_mux_map_enum,
> + AFE_CM0_CON0,
> + AFE_CM0_OUTPUT_MUX_SFT,
> + AFE_CM0_OUTPUT_MUX_MASK,
> + cm0_mux_map,
> + cm0_mux_map_value);
Since the values are just 0 and 1, the simplified version can be used here:
static SOC_ENUM_SINGLE_DECL(ul_cm0_mux_map_enum, AFE_CM0_CON0,
AFE_CM0_OUTPUT_MUX_SFT, cm0_mux_map);
The enum for the values and mask, and the map_value array can then be dropped.
Same thing applies to CM1 and CM2.
> +static SOC_VALUE_ENUM_SINGLE_DECL(ul_cm1_mux_map_enum,
> + AFE_CM1_CON0,
> + AFE_CM1_OUTPUT_MUX_SFT,
> + AFE_CM1_OUTPUT_MUX_MASK,
> + cm1_mux_map,
> + cm1_mux_map_value);
> +static SOC_VALUE_ENUM_SINGLE_DECL(ul_cm2_mux_map_enum,
> + AFE_CM2_CON0,
> + AFE_CM2_OUTPUT_MUX_SFT,
> + AFE_CM2_OUTPUT_MUX_MASK,
> + cm2_mux_map,
> + cm2_mux_map_value);
> +
> +static const struct snd_kcontrol_new ul_cm0_mux_control =
> + SOC_DAPM_ENUM("CM0_UL_MUX Route", ul_cm0_mux_map_enum);
> +
> +static const struct snd_kcontrol_new ul_cm1_mux_control =
> + SOC_DAPM_ENUM("CM1_UL_MUX Route", ul_cm1_mux_map_enum);
> +
> +static const struct snd_kcontrol_new ul_cm2_mux_control =
> + SOC_DAPM_ENUM("CM2_UL_MUX Route", ul_cm2_mux_map_enum);
> +
> +static const struct snd_soc_dapm_widget mt8196_memif_widgets[] = {
[...]
> + SND_SOC_DAPM_MIXER("UL_CM0_CH1", SND_SOC_NOPM, 0, 0,
> + memif_ul_cm0_ch1_mix, ARRAY_SIZE(memif_ul_cm0_ch1_mix)),
> + SND_SOC_DAPM_MIXER("UL_CM0_CH2", SND_SOC_NOPM, 0, 0,
> + memif_ul_cm0_ch2_mix, ARRAY_SIZE(memif_ul_cm0_ch2_mix)),
> + SND_SOC_DAPM_MIXER("UL_CM0_CH3", SND_SOC_NOPM, 0, 0,
> + memif_ul_cm0_ch3_mix, ARRAY_SIZE(memif_ul_cm0_ch3_mix)),
> + SND_SOC_DAPM_MIXER("UL_CM0_CH4", SND_SOC_NOPM, 0, 0,
> + memif_ul_cm0_ch4_mix, ARRAY_SIZE(memif_ul_cm0_ch4_mix)),
> + SND_SOC_DAPM_MIXER("UL_CM0_CH5", SND_SOC_NOPM, 0, 0,
> + memif_ul_cm0_ch5_mix, ARRAY_SIZE(memif_ul_cm0_ch5_mix)),
> + SND_SOC_DAPM_MIXER("UL_CM0_CH6", SND_SOC_NOPM, 0, 0,
> + memif_ul_cm0_ch6_mix, ARRAY_SIZE(memif_ul_cm0_ch6_mix)),
> + SND_SOC_DAPM_MIXER("UL_CM0_CH7", SND_SOC_NOPM, 0, 0,
> + memif_ul_cm0_ch7_mix, ARRAY_SIZE(memif_ul_cm0_ch7_mix)),
> + SND_SOC_DAPM_MIXER("UL_CM0_CH8", SND_SOC_NOPM, 0, 0,
> + memif_ul_cm0_ch8_mix, ARRAY_SIZE(memif_ul_cm0_ch8_mix)),
> + SND_SOC_DAPM_MUX("CM0_UL_MUX", SND_SOC_NOPM, 0, 0,
> + &ul_cm0_mux_control),
Please add an empty line here for separation.
> + SND_SOC_DAPM_MIXER("UL_CM1_CH1", SND_SOC_NOPM, 0, 0,
> + memif_ul_cm1_ch1_mix, ARRAY_SIZE(memif_ul_cm1_ch1_mix)),
> + SND_SOC_DAPM_MIXER("UL_CM1_CH2", SND_SOC_NOPM, 0, 0,
> + memif_ul_cm1_ch2_mix, ARRAY_SIZE(memif_ul_cm1_ch2_mix)),
> + SND_SOC_DAPM_MIXER("UL_CM1_CH3", SND_SOC_NOPM, 0, 0,
> + memif_ul_cm1_ch3_mix, ARRAY_SIZE(memif_ul_cm1_ch3_mix)),
> + SND_SOC_DAPM_MIXER("UL_CM1_CH4", SND_SOC_NOPM, 0, 0,
> + memif_ul_cm1_ch4_mix, ARRAY_SIZE(memif_ul_cm1_ch4_mix)),
> + SND_SOC_DAPM_MIXER("UL_CM1_CH5", SND_SOC_NOPM, 0, 0,
> + memif_ul_cm1_ch5_mix, ARRAY_SIZE(memif_ul_cm1_ch5_mix)),
> + SND_SOC_DAPM_MIXER("UL_CM1_CH6", SND_SOC_NOPM, 0, 0,
> + memif_ul_cm1_ch6_mix, ARRAY_SIZE(memif_ul_cm1_ch6_mix)),
> + SND_SOC_DAPM_MIXER("UL_CM1_CH7", SND_SOC_NOPM, 0, 0,
> + memif_ul_cm1_ch7_mix, ARRAY_SIZE(memif_ul_cm1_ch7_mix)),
> + SND_SOC_DAPM_MIXER("UL_CM1_CH8", SND_SOC_NOPM, 0, 0,
> + memif_ul_cm1_ch8_mix, ARRAY_SIZE(memif_ul_cm1_ch8_mix)),
> + SND_SOC_DAPM_MIXER("UL_CM1_CH9", SND_SOC_NOPM, 0, 0,
> + memif_ul_cm1_ch9_mix, ARRAY_SIZE(memif_ul_cm1_ch9_mix)),
> + SND_SOC_DAPM_MIXER("UL_CM1_CH10", SND_SOC_NOPM, 0, 0,
> + memif_ul_cm1_ch10_mix, ARRAY_SIZE(memif_ul_cm1_ch10_mix)),
> + SND_SOC_DAPM_MIXER("UL_CM1_CH11", SND_SOC_NOPM, 0, 0,
> + memif_ul_cm1_ch11_mix, ARRAY_SIZE(memif_ul_cm1_ch11_mix)),
> + SND_SOC_DAPM_MIXER("UL_CM1_CH12", SND_SOC_NOPM, 0, 0,
> + memif_ul_cm1_ch12_mix, ARRAY_SIZE(memif_ul_cm1_ch12_mix)),
> + SND_SOC_DAPM_MIXER("UL_CM1_CH13", SND_SOC_NOPM, 0, 0,
> + memif_ul_cm1_ch13_mix, ARRAY_SIZE(memif_ul_cm1_ch13_mix)),
> + SND_SOC_DAPM_MIXER("UL_CM1_CH14", SND_SOC_NOPM, 0, 0,
> + memif_ul_cm1_ch14_mix, ARRAY_SIZE(memif_ul_cm1_ch14_mix)),
> + SND_SOC_DAPM_MIXER("UL_CM1_CH15", SND_SOC_NOPM, 0, 0,
> + memif_ul_cm1_ch15_mix, ARRAY_SIZE(memif_ul_cm1_ch15_mix)),
> + SND_SOC_DAPM_MIXER("UL_CM1_CH16", SND_SOC_NOPM, 0, 0,
> + memif_ul_cm1_ch16_mix, ARRAY_SIZE(memif_ul_cm1_ch16_mix)),
> + SND_SOC_DAPM_MUX("CM1_UL_MUX", SND_SOC_NOPM, 0, 0,
> + &ul_cm1_mux_control),
And here as well.
[...]
> + SND_SOC_DAPM_MIXER("SOF_DMA_UL0", SND_SOC_NOPM, 0, 0, NULL, 0),
> + SND_SOC_DAPM_MIXER("SOF_DMA_UL1", SND_SOC_NOPM, 0, 0, NULL, 0),
> + SND_SOC_DAPM_MIXER("SOF_DMA_UL2", SND_SOC_NOPM, 0, 0, NULL, 0),
> +};
> +
> +static const struct snd_soc_dapm_route mt8196_memif_routes[] = {
> + {"UL0", NULL, "UL0_CH1"},
> + {"UL0", NULL, "UL0_CH2"},
> + /* Normal record */
> + {"UL0_CH1", "ADDA_UL_CH1", "ADDA_UL_Mux"},
> + {"UL0_CH2", "ADDA_UL_CH2", "ADDA_UL_Mux"},
> + /* SOF Uplink */
> + {"SOF_DMA_UL0", NULL, "UL0_CH1"},
> + {"SOF_DMA_UL0", NULL, "UL0_CH2"},
Somehow this is weird. SOF_DMA_UL0 is a dummy widget created above,
while SOF somehow creates a widget named AFE2.IN which goes to the
same SOF buffer widget. The AFE2.IN widget remains unused.
> +
> + {"UL1", NULL, "UL1_CH1"},
> + {"UL1", NULL, "UL1_CH2"},
> + {"UL1_CH1", "I2SIN4_CH1", "I2SIN4"},
> + {"UL1_CH2", "I2SIN4_CH2", "I2SIN4"},
> + {"UL1_CH1", "I2SIN6_CH1", "I2SIN6"},
> + {"UL1_CH2", "I2SIN6_CH2", "I2SIN6"},
> + /* SOF Uplink */
> + {"SOF_DMA_UL1", NULL, "UL1_CH1"},
> + {"SOF_DMA_UL1", NULL, "UL1_CH2"},
> +
> + {"UL2", NULL, "UL2_CH1"},
> + {"UL2", NULL, "UL2_CH2"},
> + {"UL2_CH1", "ADDA_UL_CH3", "ADDA_CH34_UL_Mux"},
> + {"UL2_CH2", "ADDA_UL_CH4", "ADDA_CH34_UL_Mux"},
> + /* SOF Uplink */
> + {"SOF_DMA_UL2", NULL, "UL2_CH1"},
> + {"SOF_DMA_UL2", NULL, "UL2_CH2"},
[...]
> + {"UL8", NULL, "CM0_UL_MUX"},
> + {"CM0_UL_MUX", "UL8_2CH_PATH", "UL8_CH1"},
> + {"CM0_UL_MUX", "UL8_2CH_PATH", "UL8_CH2"},
> + {"CM0_UL_MUX", "CM0_8CH_PATH", "UL_CM0_CH1"},
> + {"CM0_UL_MUX", "CM0_8CH_PATH", "UL_CM0_CH2"},
> + {"CM0_UL_MUX", "CM0_8CH_PATH", "UL_CM0_CH3"},
> + {"CM0_UL_MUX", "CM0_8CH_PATH", "UL_CM0_CH4"},
> + {"CM0_UL_MUX", "CM0_8CH_PATH", "UL_CM0_CH5"},
> + {"CM0_UL_MUX", "CM0_8CH_PATH", "UL_CM0_CH6"},
> + {"CM0_UL_MUX", "CM0_8CH_PATH", "UL_CM0_CH7"},
> + {"CM0_UL_MUX", "CM0_8CH_PATH", "UL_CM0_CH8"},
> + {"UL_CM0_CH1", NULL, "CM0_Enable"},
> + {"UL_CM0_CH2", NULL, "CM0_Enable"},
> + {"UL_CM0_CH3", NULL, "CM0_Enable"},
> + {"UL_CM0_CH4", NULL, "CM0_Enable"},
> + {"UL_CM0_CH5", NULL, "CM0_Enable"},
> + {"UL_CM0_CH6", NULL, "CM0_Enable"},
> + {"UL_CM0_CH7", NULL, "CM0_Enable"},
> + {"UL_CM0_CH8", NULL, "CM0_Enable"},
As mentioned in the review of another patch, you can tie "CM0_Enable"
to "UL_CM0", the widget for the DAI, which actually maps to a hardware
engine. This also reduces the number of routes needed.
Same goes for all the other MEMIFs.
[...]
> +#define MT8183_AFE_IRQ_BASE(_id, _fs_reg, _fs_shift, _fs_maskbit) \
> + [MT8183_IRQ_##_id] = { \
> + .id = MT8183_IRQ_##_id, \
> + .irq_cnt_reg = AFE_IRQ_MCU_CNT##_id, \
> + .irq_cnt_shift = 0, \
> + .irq_cnt_maskbit = 0x3ffff, \
> + .irq_fs_reg = _fs_reg, \
> + .irq_fs_shift = _fs_shift, \
> + .irq_fs_maskbit = _fs_maskbit, \
> + .irq_en_reg = AFE_IRQ_MCU_CON0, \
> + .irq_en_shift = IRQ##_id##_MCU_ON_SFT, \
> + .irq_clr_reg = AFE_IRQ_MCU_CLR, \
> + .irq_clr_shift = IRQ##_id##_MCU_CLR_SFT, \
> + }
Please remove. This seems like it was copied over as a reference?
[...]
> +static irqreturn_t mt8196_afe_irq_handler(int irq_id, void *dev)
> +{
> + struct mtk_base_afe *afe = dev;
> + struct mtk_base_afe_irq *irq;
> + unsigned int status;
> + unsigned int status_mcu;
> + unsigned int mcu_en;
> + unsigned int cus_status;
> + unsigned int cus_status_mcu;
> + unsigned int cus_mcu_en;
> + unsigned int tmp_reg;
> + int ret, cus_ret;
> + int i;
> + struct timespec64 ts64;
> + unsigned long long t1, t2;
> + /* one interrupt period = 5ms */
> + const unsigned long long timeout_limit = 5000000;
> +
> + /* get irq that is sent to MCU */
> + regmap_read(afe->regmap, AFE_IRQ_MCU_EN, &mcu_en);
> + regmap_read(afe->regmap, AFE_CUSTOM_IRQ_MCU_EN, &cus_mcu_en);
> +
> + ret = regmap_read(afe->regmap, AFE_IRQ_MCU_STATUS, &status);
> + cus_ret = regmap_read(afe->regmap, AFE_CUSTOM_IRQ_MCU_STATUS, &cus_status);
> + /* only care IRQ which is sent to MCU */
> + status_mcu = status & mcu_en & AFE_IRQ_STATUS_BITS;
> + cus_status_mcu = cus_status & cus_mcu_en & AFE_IRQ_STATUS_BITS;
> + if ((ret || !status_mcu) &&
> + (cus_ret || !cus_status_mcu)) {
This can be on the same line.
> + dev_err(afe->dev, "ret %d, sat 0x%x, en 0x%x,csat 0x%x, cen 0x%x\n",
> + ret, status, mcu_en, cus_status_mcu, cus_mcu_en);
> + goto err_irq;
Just return IRQ_NONE here, because nothing was handled.
> + }
> +
> + ktime_get_ts64(&ts64);
> + t1 = timespec64_to_ns(&ts64);
u64 t1 = ktime_get_ns();
Maybe t1 should be at the start of the function, if the time limit
is for the whole interrupt handler, which I think it is?
> + for (i = 0; i < MT8196_MEMIF_NUM; i++) {
> + struct mtk_base_afe_memif *memif = &afe->memif[i];
> +
> + if (!memif->substream)
> + continue;
> +
> + if (memif->irq_usage < 0)
> + continue;
> + irq = &afe->irqs[memif->irq_usage];
> +
> + if (i == MT8196_MEMIF_HDMI) {
> + if (cus_status_mcu & (0x1 << irq->irq_data->id))
Please use BIT(x).
> + snd_pcm_period_elapsed(memif->substream);
> + } else {
> + if (status_mcu & (0x1 << irq->irq_data->id))
BIT(irq->irq_data->id)
> + snd_pcm_period_elapsed(memif->substream);
> + }
> + }
> +
> + ktime_get_ts64(&ts64);
> + t2 = timespec64_to_ns(&ts64);
u64 t2 = ktime_get_ns();
> + t2 = t2 - t1; /* in ns (10^9) */
> +
> + if (t2 > timeout_limit) {
> + dev_warn(afe->dev, "mcu_en 0x%x, cus_mcu_en 0x%x, timeout %llu, limit %llu, ret %d\n",
Please make the message more meaningful, such as
dev_warn(afe->dev, "IRQ handler exceeded time limit by %llu ns",
t2 - timeout_limit);
Also, ret's value probably doesn't factor in here, so I don't think it needs
to be included in the message.
> + mcu_en, cus_mcu_en,
> + t2, timeout_limit, ret);
> + }
Curly braces are not required here.
> +
> +err_irq:
> + /* clear irq */
> + for (i = 0; i < MT8196_IRQ_NUM; ++i) {
> + /* cus_status_mcu only bit0 is used for TDM */
> + if ((status_mcu & (0x1 << i)) || (cus_status_mcu & 0x1)) {
BIT(i)
> + regmap_read(afe->regmap, irq_data[i].irq_clr_reg, &tmp_reg);
> + regmap_update_bits(afe->regmap, irq_data[i].irq_clr_reg,
> + AFE_IRQ_CLR_CFG_MASK_SFT |
> + AFE_IRQ_MISS_FLAG_CLR_CFG_MASK_SFT,
> + tmp_reg ^ (AFE_IRQ_CLR_CFG_MASK_SFT |
> + AFE_IRQ_MISS_FLAG_CLR_CFG_MASK_SFT));
Same question as the trigger callback. Why is it XOR?
> + }
> + }
> +
> + return IRQ_HANDLED;
> +}
> +
> +static int mt8196_afe_runtime_suspend(struct device *dev)
> +{
> + struct mtk_base_afe *afe = dev_get_drvdata(dev);
> + unsigned int value;
> + unsigned int tmp_reg;
> + int ret, i;
> +
> + if (!afe->regmap) {
> + dev_err(afe->dev, "skip regmap\n");
> + goto skip_regmap;
> + }
> +
> + /* disable AFE */
> + mt8196_afe_disable_main_clock(afe);
> +
> + ret = regmap_read_poll_timeout(afe->regmap,
> + AUDIO_ENGEN_CON0_MON,
> + value,
> + (value & AUDIO_ENGEN_MON_SFT) == 0,
> + 20,
> + 1 * 1000 * 1000);
This timeout is a bit long.
> + dev_dbg(afe->dev, "read_poll ret %d\n", ret);
> + if (ret)
> + dev_info(afe->dev, "ret %d\n", ret);
> +
> + /* make sure all irq status are cleared */
> + for (i = 0; i < MT8196_IRQ_NUM; ++i) {
> + regmap_read(afe->regmap, irq_data[i].irq_clr_reg, &tmp_reg);
> + regmap_update_bits(afe->regmap, irq_data[i].irq_clr_reg,
> + AFE_IRQ_CLR_CFG_MASK_SFT | AFE_IRQ_MISS_FLAG_CLR_CFG_MASK_SFT,
> + tmp_reg ^ (AFE_IRQ_CLR_CFG_MASK_SFT |
Same thing here about the XOR.
> + AFE_IRQ_MISS_FLAG_CLR_CFG_MASK_SFT));
> + }
> +
> + /* reset audio 26M request */
> + regmap_update_bits(afe->regmap,
> + AFE_SPM_CONTROL_REQ, 0x1, 0x0);
> +
> + /* cache only */
> + regcache_cache_only(afe->regmap, true);
> + regcache_mark_dirty(afe->regmap);
> +
> +skip_regmap:
> + mt8196_afe_disable_reg_rw_clk(afe);
> + return 0;
> +}
> +
> +static int mt8196_afe_runtime_resume(struct device *dev)
> +{
> + struct mtk_base_afe *afe = dev_get_drvdata(dev);
> + int ret = 0;
> +
> + ret = mt8196_afe_enable_reg_rw_clk(afe);
> + if (ret)
> + return ret;
> +
> + if (!afe->regmap) {
> + dev_warn(afe->dev, "skip regmap\n");
> + goto skip_regmap;
> + }
> + regcache_cache_only(afe->regmap, false);
> + regcache_sync(afe->regmap);
> +
> + /* set audio 26M request */
> + regmap_update_bits(afe->regmap, AFE_SPM_CONTROL_REQ, 0x1, 0x1);
> + regmap_update_bits(afe->regmap, AFE_CBIP_CFG0, 0x1, 0x1);
> +
> + /* force cpu use 8_24 format when writing 32bit data */
> + regmap_update_bits(afe->regmap, AFE_MEMIF_CON0,
> + CPU_HD_ALIGN_MASK_SFT, 0 << CPU_HD_ALIGN_SFT);
> +
> + /* enable AFE */
> + mt8196_afe_enable_main_clock(afe);
> +
> +skip_regmap:
> + return 0;
> +}
> +
> +static int mt8196_afe_component_probe(struct snd_soc_component *component)
> +{
> + struct mtk_base_afe *afe = snd_soc_component_get_drvdata(component);
> +
> + if (component) {
> + /* enable clock for regcache get default value from hw */
> + pm_runtime_get_sync(afe->dev);
> + mtk_afe_add_sub_dai_control(component);
> + pm_runtime_put_sync(afe->dev);
> + }
> + return 0;
> +}
> +
> +static int mt8196_afe_pcm_open(struct snd_soc_component *component,
> + struct snd_pcm_substream *substream)
> +{
> + /* set the wait_for_avail to 2 sec*/
> + substream->wait_time = msecs_to_jiffies(2 * 1000);
> +
> + return 0;
> +}
> +
> +static void mt8196_afe_pcm_free(struct snd_soc_component *component, struct snd_pcm *pcm)
> +{
> + snd_pcm_lib_preallocate_free_for_all(pcm);
> +}
> +
> +static const struct snd_soc_component_driver mt8196_afe_component = {
> + .name = AFE_PCM_NAME,
> + .probe = mt8196_afe_component_probe,
> + .pcm_construct = mtk_afe_pcm_new,
> + .pcm_destruct = mt8196_afe_pcm_free,
> + .open = mt8196_afe_pcm_open,
> + .pointer = mtk_afe_pcm_pointer,
> +};
> +
> +static int mt8196_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 = mt8196_memif_dai_driver;
> + dai->num_dai_drivers = ARRAY_SIZE(mt8196_memif_dai_driver);
> +
> + dai->controls = mt8196_pcm_kcontrols;
> + dai->num_controls = ARRAY_SIZE(mt8196_pcm_kcontrols);
> + dai->dapm_widgets = mt8196_memif_widgets;
> + dai->num_dapm_widgets = ARRAY_SIZE(mt8196_memif_widgets);
> + dai->dapm_routes = mt8196_memif_routes;
> + dai->num_dapm_routes = ARRAY_SIZE(mt8196_memif_routes);
> + return 0;
> +}
> +
> +typedef int (*dai_register_cb)(struct mtk_base_afe *);
> +static const dai_register_cb dai_register_cbs[] = {
> + mt8196_dai_adda_register,
> + mt8196_dai_i2s_register,
> + mt8196_dai_tdm_register,
> + mt8196_dai_memif_register,
> +};
> +
> +static const struct reg_sequence mt8196_cg_patch[] = {
> + { AUDIO_TOP_CON4, 0x361c },
> +};
> +
> +static int mt8196_afe_pcm_dev_probe(struct platform_device *pdev)
> +{
> + int ret, i;
> + unsigned int tmp_reg;
> + int irq_id;
> + struct mtk_base_afe *afe;
> + struct mt8196_afe_private *afe_priv;
> + struct device *dev = &pdev->dev;
> +
> + ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(34));
> + if (ret)
> + return ret;
> +
> + ret = of_reserved_mem_device_init(dev);
> + if (ret)
> + dev_err(dev, "failed to assign memory region: %d\n", ret);
> +
> + afe = devm_kzalloc(dev, sizeof(*afe), GFP_KERNEL);
> + if (!afe)
> + return -ENOMEM;
> +
> + platform_set_drvdata(pdev, afe);
> +
> + afe->platform_priv = devm_kzalloc(dev, sizeof(*afe_priv),
> + GFP_KERNEL);
> + if (!afe->platform_priv)
> + return -ENOMEM;
> +
> + afe_priv = afe->platform_priv;
> + afe->dev = 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");
> +
> + /* init audio related clock */
> + ret = mt8196_init_clock(afe);
> + if (ret)
> + return dev_err_probe(dev, ret, "init clock error.\n");
> +
> + /* init memif */
> + /* IPM2.0 no need banding */
> + afe->memif_32bit_supported = 1;
> + afe->memif_size = MT8196_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 = memif_irq_usage[i];
> + afe->memif[i].const_irq = 1;
> + }
> +
> + mutex_init(&afe->irq_alloc_lock);
> +
> + /* init irq */
> + afe->irqs_size = MT8196_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];
> +
> + /* request irq */
> + irq_id = platform_get_irq(pdev, 0);
> + if (irq_id < 0)
> + return dev_err_probe(dev, irq_id, "no irq found");
> +
> + ret = devm_request_irq(dev, irq_id, mt8196_afe_irq_handler,
> + IRQF_TRIGGER_NONE,
Just pass zero.
> + "Afe_ISR_Handle", (void *)afe);
Cast to void * is not necessary.
> + if (ret)
> + return dev_err_probe(dev, ret, "could not request_irq for Afe_ISR_Handle\n");
> +
> + ret = enable_irq_wake(irq_id);
This needs a matching disable_irq_wake() in the error path and remove path.
Also, is this even valid? What in the audio block would trigger a wakeup?
> + if (ret < 0)
> + dev_warn(dev, "enable_irq_wake %d ret: %d\n", irq_id, ret);
> +
> + /* 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");
> +
> + /* others */
> + afe->mtk_afe_hardware = &mt8196_afe_hardware;
> + afe->memif_fs = mt8196_memif_fs;
> + afe->irq_fs = mt8196_irq_fs;
> + afe->get_dai_fs = mt8196_get_dai_fs;
> + afe->get_memif_pbuf_size = mt8196_get_memif_pbuf_size;
> +
> + afe->runtime_resume = mt8196_afe_runtime_resume;
> + afe->runtime_suspend = mt8196_afe_runtime_suspend;
> +
> + ret = devm_pm_runtime_enable(dev);
> + if (ret)
> + return ret;
> +
> + /* Audio device is part of genpd.
> + * Set audio as syscore device to prevent
> + * genpd automatically power off audio
> + * device when suspend
Multi-line comment blocks should start with /* empty line. Comment text
starts on the second line, i.e. the opening and closing /* */ should
not have text on the same line.
This needs more context. Why can't it be powered off?
Please reformat the comment and make it wider.
> + */
> + dev_pm_syscore_device(dev, true);
> +
> + /* enable clock for regcache get default value from hw */
> + pm_runtime_get_sync(dev);
> +
> + afe->regmap = devm_regmap_init_mmio(dev, afe->base_addr,
> + &mt8196_afe_regmap_config);
> + if (IS_ERR(afe->regmap))
> + return PTR_ERR(afe->regmap);
> +
> + ret = regmap_register_patch(afe->regmap, mt8196_cg_patch,
> + ARRAY_SIZE(mt8196_cg_patch));
> + if (ret < 0) {
> + dev_info(dev, "Failed to apply cg patch\n");
This should be an error message.
> + goto err_pm_disable;
> + }
> +
> + regmap_read(afe->regmap, AFE_IRQ_MCU_EN, &tmp_reg);
> + regmap_write(afe->regmap, AFE_IRQ_MCU_EN, 0xffffffff);
> + regmap_read(afe->regmap, AFE_IRQ_MCU_EN, &tmp_reg);
> +
> + pm_runtime_put_sync(dev);
> +
> + regcache_cache_only(afe->regmap, true);
> + regcache_mark_dirty(afe->regmap);
> +
> + /* register component */
> + ret = devm_snd_soc_register_component(dev,
> + &mt8196_afe_component,
> + afe->dai_drivers,
> + afe->num_dai_drivers);
> + if (ret) {
> + dev_warn(dev, "afe component err\n");
This should be an error message.
> + goto err_pm_disable;
> + }
> +
> + return 0;
> +
> +err_pm_disable:
> + pm_runtime_disable(dev);
This is not needed since you use the devm version above. What you do need
to do is
pm_runtime_put_sync(dev);
when jumping from devm_regmap_init_mmio() or regmap_register_patch()
failures.
> + return ret;
> +}
> +
> +static void mt8196_afe_pcm_dev_remove(struct platform_device *pdev)
> +{
> + struct mtk_base_afe *afe = platform_get_drvdata(pdev);
> + struct device *dev = &pdev->dev;
> +
> + pm_runtime_disable(dev);
Not needed since it is enabled with devm version.
> + if (!pm_runtime_status_suspended(dev))
> + mt8196_afe_runtime_suspend(dev);
> +
> + /* disable afe clock */
> + mt8196_afe_disable_reg_rw_clk(afe);
> + mt8196_afe_disable_main_clock(afe);
It is generally not a good idea to mix devm and non-devm stuff. The
release functions of devm get called _after_ the remove function returns,
so it is very easy to do things out of order and cause errors or lockups.
Also I wonder if this is necessary, but I'm not that familiar with runtime
PM. I would think that since ASoC is the only entry point into this driver
and ASoC does power sequencing, it shouldn't leave components enabled on
removal.
> +}
> +
> +static const struct of_device_id mt8196_afe_pcm_dt_match[] = {
> + { .compatible = "mediatek,mt8196-afe", },
> + { /* sentinel */ },
> +};
> +MODULE_DEVICE_TABLE(of, mt8196_afe_pcm_dt_match);
> +
> +static const struct dev_pm_ops mt8196_afe_pm_ops = {
> + SET_RUNTIME_PM_OPS(mt8196_afe_runtime_suspend,
> + mt8196_afe_runtime_resume, NULL)
> +};
> +
> +static struct platform_driver mt8196_afe_pcm_driver = {
> + .driver = {
> + .name = "mt8196-afe",
> + .of_match_table = mt8196_afe_pcm_dt_match,
> +#if IS_ENABLED(CONFIG_PM)
Drop the #if around this. The field is always available.
> + .pm = &mt8196_afe_pm_ops,
> +#endif
> + },
> + .probe = mt8196_afe_pcm_dev_probe,
> + .remove = mt8196_afe_pcm_dev_remove,
> +};
> +
Don't need this empty line.
> +module_platform_driver(mt8196_afe_pcm_driver);
> +
> +MODULE_DESCRIPTION("Mediatek ALSA SoC AFE platform driver for 8196");
> +MODULE_AUTHOR("Darren Ye <darren.ye@mediatek.com>");
> +MODULE_LICENSE("GPL");
Thanks
ChenYu
> --
> 2.45.2
>
>
^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [PATCH v6 05/10] ASoC: mediatek: mt8196: support I2S in platform driver
[not found] ` <20250708111806.3992-6-darren.ye@mediatek.com>
@ 2025-08-11 11:03 ` Chen-Yu Tsai
2025-08-21 9:10 ` Darren Ye (叶飞)
2025-08-11 11:24 ` Chen-Yu Tsai
1 sibling, 1 reply; 24+ messages in thread
From: Chen-Yu Tsai @ 2025-08-11 11:03 UTC (permalink / raw)
To: Darren.Ye
Cc: Liam Girdwood, Mark Brown, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Matthias Brugger, AngeloGioacchino Del Regno,
Jaroslav Kysela, Takashi Iwai, Linus Walleij, Bartosz Golaszewski,
linux-sound, devicetree, linux-kernel, linux-arm-kernel,
linux-mediatek, linux-gpio
On Tue, Jul 8, 2025 at 7:34 PM Darren.Ye <darren.ye@mediatek.com> wrote:
>
> From: Darren Ye <darren.ye@mediatek.com>
>
> Add mt8196 I2S DAI driver support.
>
> Signed-off-by: Darren Ye <darren.ye@mediatek.com>
> ---
> sound/soc/mediatek/mt8196/mt8196-dai-i2s.c | 3944 ++++++++++++++++++++
> 1 file changed, 3944 insertions(+)
> create mode 100644 sound/soc/mediatek/mt8196/mt8196-dai-i2s.c
>
> diff --git a/sound/soc/mediatek/mt8196/mt8196-dai-i2s.c b/sound/soc/mediatek/mt8196/mt8196-dai-i2s.c
> new file mode 100644
> index 000000000000..59f66ab8fa9f
> --- /dev/null
> +++ b/sound/soc/mediatek/mt8196/mt8196-dai-i2s.c
> @@ -0,0 +1,3944 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * MediaTek ALSA SoC Audio DAI I2S Control
> + *
> + * Copyright (c) 2024 MediaTek Inc.
^ update to 2025 (for all new files)
> + * Author: Darren Ye <darren.ye@mediatek.com>
> + */
> +
> +#include <linux/bitops.h>
> +#include <linux/regmap.h>
> +#include <sound/pcm_params.h>
> +#include "mt8196-afe-clk.h"
> +#include "mt8196-afe-common.h"
> +#include "mt8196-interconnection.h"
> +#include "mtk-afe-fe-dai.h"
Please add one empty line between each group of headers (linux/*, sound/*,
local). And please use relative paths for the local headers.
> +
> +#define ETDM_22M_CLOCK_THRES 11289600
> +
> +enum {
> + ETDM_CLK_SOURCE_H26M,
> + ETDM_CLK_SOURCE_APLL,
> + ETDM_CLK_SOURCE_SPDIF,
> + ETDM_CLK_SOURCE_HDMI,
> + ETDM_CLK_SOURCE_EARC,
> + ETDM_CLK_SOURCE_LINEIN,
> +};
> +
> +enum {
> + ETDM_RELATCH_SEL_H26M,
> + ETDM_RELATCH_SEL_APLL,
> +};
> +
> +enum {
> + ETDM_RATE_8K,
> + ETDM_RATE_12K,
> + ETDM_RATE_16K,
> + ETDM_RATE_24K,
> + ETDM_RATE_32K,
> + ETDM_RATE_48K,
> + ETDM_RATE_64K,
> + ETDM_RATE_96K,
> + ETDM_RATE_128K,
> + ETDM_RATE_192K,
> + ETDM_RATE_256K,
> + ETDM_RATE_384K,
> + ETDM_RATE_11025 = 16,
> + ETDM_RATE_22050,
> + ETDM_RATE_44100,
> + ETDM_RATE_88200,
> + ETDM_RATE_176400,
> + ETDM_RATE_352800,
> +};
> +
> +enum {
> + ETDM_CONN_8K,
> + ETDM_CONN_11K,
> + ETDM_CONN_12K,
> + ETDM_CONN_16K = 4,
> + ETDM_CONN_22K,
> + ETDM_CONN_24K,
> + ETDM_CONN_32K = 8,
> + ETDM_CONN_44K,
> + ETDM_CONN_48K,
> + ETDM_CONN_88K = 13,
> + ETDM_CONN_96K,
> + ETDM_CONN_176K = 17,
> + ETDM_CONN_192K,
> + ETDM_CONN_352K = 21,
> + ETDM_CONN_384K,
> +};
> +
> +enum {
> + ETDM_WLEN_8_BIT = 0x7,
> + ETDM_WLEN_16_BIT = 0xf,
> + ETDM_WLEN_32_BIT = 0x1f,
> +};
> +
> +enum {
> + ETDM_SLAVE_SEL_ETDMIN0_MASTER,
> + ETDM_SLAVE_SEL_ETDMIN0_SLAVE,
> + ETDM_SLAVE_SEL_ETDMIN1_MASTER,
> + ETDM_SLAVE_SEL_ETDMIN1_SLAVE,
> + ETDM_SLAVE_SEL_ETDMIN2_MASTER,
> + ETDM_SLAVE_SEL_ETDMIN2_SLAVE,
> + ETDM_SLAVE_SEL_ETDMIN3_MASTER,
> + ETDM_SLAVE_SEL_ETDMIN3_SLAVE,
> + ETDM_SLAVE_SEL_ETDMOUT0_MASTER,
> + ETDM_SLAVE_SEL_ETDMOUT0_SLAVE,
> + ETDM_SLAVE_SEL_ETDMOUT1_MASTER,
> + ETDM_SLAVE_SEL_ETDMOUT1_SLAVE,
> + ETDM_SLAVE_SEL_ETDMOUT2_MASTER,
> + ETDM_SLAVE_SEL_ETDMOUT2_SLAVE,
> + ETDM_SLAVE_SEL_ETDMOUT3_MASTER,
> + ETDM_SLAVE_SEL_ETDMOUT3_SLAVE,
> +};
> +
> +enum {
> + ETDM_SLAVE_SEL_ETDMIN4_MASTER,
> + ETDM_SLAVE_SEL_ETDMIN4_SLAVE,
> + ETDM_SLAVE_SEL_ETDMIN5_MASTER,
> + ETDM_SLAVE_SEL_ETDMIN5_SLAVE,
> + ETDM_SLAVE_SEL_ETDMIN6_MASTER,
> + ETDM_SLAVE_SEL_ETDMIN6_SLAVE,
> + ETDM_SLAVE_SEL_ETDMIN7_MASTER,
> + ETDM_SLAVE_SEL_ETDMIN7_SLAVE,
> + ETDM_SLAVE_SEL_ETDMOUT4_MASTER,
> + ETDM_SLAVE_SEL_ETDMOUT4_SLAVE,
> + ETDM_SLAVE_SEL_ETDMOUT5_MASTER,
> + ETDM_SLAVE_SEL_ETDMOUT5_SLAVE,
> + ETDM_SLAVE_SEL_ETDMOUT6_MASTER,
> + ETDM_SLAVE_SEL_ETDMOUT6_SLAVE,
> + ETDM_SLAVE_SEL_ETDMOUT7_MASTER,
> + ETDM_SLAVE_SEL_ETDMOUT7_SLAVE,
> +};
> +
> +enum {
> + MTK_DAI_ETDM_FORMAT_I2S,
> + MTK_DAI_ETDM_FORMAT_LJ,
> + MTK_DAI_ETDM_FORMAT_RJ,
> + MTK_DAI_ETDM_FORMAT_EIAJ,
> + MTK_DAI_ETDM_FORMAT_DSPA,
> + MTK_DAI_ETDM_FORMAT_DSPB,
> +};
> +
> +static unsigned int get_etdm_wlen(snd_pcm_format_t format)
> +{
> + return snd_pcm_format_physical_width(format) < 16 ?
> + ETDM_WLEN_16_BIT : ETDM_WLEN_32_BIT;
> +}
> +
> +static unsigned int get_etdm_lrck_width(snd_pcm_format_t format)
> +{
> + /* The valid data bit number should be large than 7 due to hardware limitation. */
^ larger?
> + return snd_pcm_format_physical_width(format) - 1;
> +}
> +
> +static unsigned int get_etdm_rate(unsigned int rate)
> +{
> + switch (rate) {
> + case 8000:
> + return ETDM_RATE_8K;
> + case 12000:
> + return ETDM_RATE_12K;
> + case 16000:
> + return ETDM_RATE_16K;
> + case 24000:
> + return ETDM_RATE_24K;
> + case 32000:
> + return ETDM_RATE_32K;
> + case 48000:
> + return ETDM_RATE_48K;
> + case 64000:
> + return ETDM_RATE_64K;
> + case 96000:
> + return ETDM_RATE_96K;
> + case 128000:
> + return ETDM_RATE_128K;
> + case 192000:
> + return ETDM_RATE_192K;
> + case 256000:
> + return ETDM_RATE_256K;
> + case 384000:
> + return ETDM_RATE_384K;
> + case 11025:
> + return ETDM_RATE_11025;
> + case 22050:
> + return ETDM_RATE_22050;
> + case 44100:
> + return ETDM_RATE_44100;
> + case 88200:
> + return ETDM_RATE_88200;
> + case 176400:
> + return ETDM_RATE_176400;
> + case 352800:
> + return ETDM_RATE_352800;
> + default:
> + return 0;
> + }
> +}
> +
> +static unsigned int get_etdm_inconn_rate(unsigned int rate)
> +{
> + switch (rate) {
> + case 8000:
> + return ETDM_CONN_8K;
> + case 12000:
> + return ETDM_CONN_12K;
> + case 16000:
> + return ETDM_CONN_16K;
> + case 24000:
> + return ETDM_CONN_24K;
> + case 32000:
> + return ETDM_CONN_32K;
> + case 48000:
> + return ETDM_CONN_48K;
> + case 96000:
> + return ETDM_CONN_96K;
> + case 192000:
> + return ETDM_CONN_192K;
> + case 384000:
> + return ETDM_CONN_384K;
> + case 11025:
> + return ETDM_CONN_11K;
> + case 22050:
> + return ETDM_CONN_22K;
> + case 44100:
> + return ETDM_CONN_44K;
> + case 88200:
> + return ETDM_CONN_88K;
> + case 176400:
> + return ETDM_CONN_176K;
> + case 352800:
> + return ETDM_CONN_352K;
> + default:
> + return 0;
> + }
> +}
> +
> +struct mtk_afe_i2s_priv {
> + u8 id;
> + u32 rate; /* for determine which apll to use */
> + int low_jitter_en;
> + const char *share_property_name;
> + int share_i2s_id;
> + u32 mclk_rate;
> + u8 mclk_id;
> + u8 mclk_apll;
> + u8 ch_num;
> + u8 sync;
> + u8 ip_mode;
> + u8 format;
> +};
> +
> +/* this enum is merely for mtk_afe_i2s_priv & mtk_base_etdm_data declare */
> +enum {
> + DAI_I2SIN0,
> + DAI_I2SIN1,
> + DAI_I2SIN2,
> + DAI_I2SIN3,
> + DAI_I2SIN4,
> + DAI_I2SIN6,
> + DAI_I2SOUT0,
> + DAI_I2SOUT1,
> + DAI_I2SOUT2,
> + DAI_I2SOUT3,
> + DAI_I2SOUT4,
> + DAI_I2SOUT6,
> + DAI_FMI2S_MASTER,
> + DAI_I2S_NUM,
> +};
> +
> +static bool is_etdm_in_pad_top(unsigned int dai_num)
> +{
> + switch (dai_num) {
> + case DAI_I2SOUT4:
> + case DAI_I2SIN4:
> + return true;
> + default:
> + return false;
> + }
> +}
> +
> +struct mtk_base_etdm_data {
> + u16 enable_reg;
> + u16 enable_mask;
> + u8 enable_shift;
> + u16 sync_reg;
> + u16 sync_mask;
> + u8 sync_shift;
> + u16 ch_reg;
> + u16 ch_mask;
> + u8 ch_shift;
> + u16 ip_mode_reg;
> + u16 ip_mode_mask;
> + u8 ip_mode_shift;
> + u16 init_count_reg;
> + u16 init_count_mask;
> + u8 init_count_shift;
> + u16 init_point_reg;
> + u16 init_point_mask;
> + u8 init_point_shift;
> + u16 lrck_reset_reg;
> + u16 lrck_reset_mask;
> + u8 lrck_reset_shift;
> + u16 clk_source_reg;
> + u16 clk_source_mask;
> + u8 clk_source_shift;
> + u16 ck_en_sel_reg;
> + u16 ck_en_sel_mask;
> + u8 ck_en_sel_shift;
> + u16 fs_timing_reg;
> + u16 fs_timing_mask;
> + u8 fs_timing_shift;
> + u16 relatch_en_sel_reg;
> + u16 relatch_en_sel_mask;
> + u8 relatch_en_sel_shift;
> + u16 use_afifo_reg;
> + u16 use_afifo_mask;
> + u8 use_afifo_shift;
> + u16 afifo_mode_reg;
> + u16 afifo_mode_mask;
> + u8 afifo_mode_shift;
> + u16 almost_end_ch_reg;
> + u16 almost_end_ch_mask;
> + u8 almost_end_ch_shift;
> + u16 almost_end_bit_reg;
> + u16 almost_end_bit_mask;
> + u8 almost_end_bit_shift;
> + u16 out2latch_time_reg;
> + u16 out2latch_time_mask;
> + u8 out2latch_time_shift;
> + u16 tdm_mode_reg;
> + u16 tdm_mode_mask;
> + u8 tdm_mode_shift;
> + u16 relatch_domain_sel_reg;
> + u16 relatch_domain_sel_mask;
> + u8 relatch_domain_sel_shift;
> + u16 bit_length_reg;
> + u16 bit_length_mask;
> + u8 bit_length_shift;
> + u16 word_length_reg;
> + u16 word_length_mask;
> + u8 word_length_shift;
> + u16 cowork_reg;
> + u16 cowork_mask;
> + u8 cowork_shift;
> + u32 cowork_val;
> + u16 in2latch_time_reg;
> + u16 in2latch_time_mask;
> + u8 in2latch_time_shift;
> + u16 pad_top_ck_en_reg;
> + u16 pad_top_ck_en_mask;
> + u8 pad_top_ck_en_shift;
> + u16 master_latch_reg;
> + u16 master_latch_mask;
> + u8 master_latch_shift;
> +};
> +
> +static const struct mtk_base_etdm_data mtk_etdm_data[DAI_I2S_NUM] = {
> + [DAI_I2SIN0] = {
> + .enable_reg = ETDM_IN0_CON0,
> + .enable_mask = REG_ETDM_IN_EN_MASK,
> + .enable_shift = REG_ETDM_IN_EN_SFT,
> + .sync_reg = ETDM_IN0_CON0,
> + .sync_mask = REG_SYNC_MODE_MASK,
> + .sync_shift = REG_SYNC_MODE_SFT,
> + .ch_reg = ETDM_IN0_CON0,
> + .ch_mask = REG_CH_NUM_MASK,
> + .ch_shift = REG_CH_NUM_SFT,
> + .ip_mode_reg = ETDM_IN0_CON2,
> + .ip_mode_mask = REG_MULTI_IP_MODE_MASK,
> + .ip_mode_shift = REG_MULTI_IP_MODE_SFT,
> + .init_count_reg = ETDM_IN0_CON1,
> + .init_count_mask = REG_INITIAL_COUNT_MASK,
> + .init_count_shift = REG_INITIAL_COUNT_SFT,
> + .init_point_reg = ETDM_IN0_CON1,
> + .init_point_mask = REG_INITIAL_POINT_MASK,
> + .init_point_shift = REG_INITIAL_POINT_SFT,
> + .lrck_reset_reg = ETDM_IN0_CON1,
> + .lrck_reset_mask = REG_LRCK_RESET_MASK,
> + .lrck_reset_shift = REG_LRCK_RESET_SFT,
> + .clk_source_reg = ETDM_IN0_CON2,
> + .clk_source_mask = REG_CLOCK_SOURCE_SEL_MASK,
> + .clk_source_shift = REG_CLOCK_SOURCE_SEL_SFT,
> + .ck_en_sel_reg = ETDM_IN0_CON2,
> + .ck_en_sel_mask = REG_CK_EN_SEL_AUTO_MASK,
> + .ck_en_sel_shift = REG_CK_EN_SEL_AUTO_SFT,
> + .fs_timing_reg = ETDM_IN0_CON3,
> + .fs_timing_mask = REG_FS_TIMING_SEL_MASK,
> + .fs_timing_shift = REG_FS_TIMING_SEL_SFT,
> + .relatch_en_sel_reg = ETDM_IN0_CON4,
> + .relatch_en_sel_mask = REG_RELATCH_1X_EN_SEL_MASK,
> + .relatch_en_sel_shift = REG_RELATCH_1X_EN_SEL_SFT,
> + .use_afifo_reg = ETDM_IN0_CON8,
> + .use_afifo_mask = REG_ETDM_USE_AFIFO_MASK,
> + .use_afifo_shift = REG_ETDM_USE_AFIFO_SFT,
> + .afifo_mode_reg = ETDM_IN0_CON8,
> + .afifo_mode_mask = REG_AFIFO_MODE_MASK,
> + .afifo_mode_shift = REG_AFIFO_MODE_SFT,
> + .almost_end_ch_reg = ETDM_IN0_CON9,
> + .almost_end_ch_mask = REG_ALMOST_END_CH_COUNT_MASK,
> + .almost_end_ch_shift = REG_ALMOST_END_CH_COUNT_SFT,
> + .almost_end_bit_reg = ETDM_IN0_CON9,
> + .almost_end_bit_mask = REG_ALMOST_END_BIT_COUNT_MASK,
> + .almost_end_bit_shift = REG_ALMOST_END_BIT_COUNT_SFT,
> + .out2latch_time_reg = ETDM_IN0_CON9,
> + .out2latch_time_mask = REG_OUT2LATCH_TIME_MASK,
> + .out2latch_time_shift = REG_OUT2LATCH_TIME_SFT,
> + .tdm_mode_reg = ETDM_IN0_CON0,
> + .tdm_mode_mask = REG_FMT_MASK,
> + .tdm_mode_shift = REG_FMT_SFT,
> + .relatch_domain_sel_reg = ETDM_IN0_CON0,
> + .relatch_domain_sel_mask = REG_RELATCH_1X_EN_DOMAIN_SEL_MASK,
> + .relatch_domain_sel_shift = REG_RELATCH_1X_EN_DOMAIN_SEL_SFT,
> + .bit_length_reg = ETDM_IN0_CON0,
> + .bit_length_mask = REG_BIT_LENGTH_MASK,
> + .bit_length_shift = REG_BIT_LENGTH_SFT,
> + .word_length_reg = ETDM_IN0_CON0,
> + .word_length_mask = REG_WORD_LENGTH_MASK,
> + .word_length_shift = REG_WORD_LENGTH_SFT,
> + .cowork_reg = ETDM_0_3_COWORK_CON0,
> + .cowork_mask = ETDM_IN0_SLAVE_SEL_MASK,
> + .cowork_shift = ETDM_IN0_SLAVE_SEL_SFT,
> + .cowork_val = ETDM_SLAVE_SEL_ETDMOUT0_MASTER,
> + .pad_top_ck_en_reg = 0,
> + .master_latch_reg = 0,
> + },
Could this be compressed somehow with some macro magic? This whole
block is really long. Something like:
#define MTK_ETDM_DATA(_dir, _id) \
[DAI_I2S##_dir##_id] = {
.enable_reg = ETDM_##_dir##_id##_CON0, \
...
[...]
Add more parameters as needed.
> +
Drop the extra empty line at the end of the list.
> +};
> +
> +/* lpbk */
> +static const u8 etdm_lpbk_idx_0[] = {
> + 0x0, 0x8,
> +};
> +
> +static const u8 etdm_lpbk_idx_1[] = {
> + 0x2, 0xa,
> +};
> +
> +static const u8 etdm_lpbk_idx_2[] = {
> + 0x4, 0xc,
> +};
> +
> +static const u8 etdm_lpbk_idx_3[] = {
> + 0x6, 0xe,
> +};
> +
> +static int etdm_lpbk_get(struct snd_kcontrol *kcontrol,
> + struct snd_ctl_elem_value *ucontrol)
> +{
> + struct snd_soc_component *component =
> + snd_soc_kcontrol_component(kcontrol);
> + struct mtk_base_afe *afe = snd_soc_component_get_drvdata(component);
> + u32 value;
> + u32 value_ipmode;
> + u32 reg;
> + u32 mask;
> + u8 shift;
> +
> + if (!strcmp(kcontrol->id.name, "I2SIN0_LPBK")) {
> + reg = ETDM_0_3_COWORK_CON1;
> + mask = ETDM_IN0_SDATA0_SEL_MASK_SFT;
> + shift = ETDM_IN0_SDATA0_SEL_SFT;
> + } else if (!strcmp(kcontrol->id.name, "I2SIN1_LPBK")) {
> + reg = ETDM_0_3_COWORK_CON1;
> + mask = ETDM_IN1_SDATA0_SEL_MASK_SFT;
> + shift = ETDM_IN1_SDATA0_SEL_SFT;
> + } else if (!strcmp(kcontrol->id.name, "I2SIN2_LPBK")) {
> + reg = ETDM_0_3_COWORK_CON3;
> + mask = ETDM_IN2_SDATA0_SEL_MASK_SFT;
> + shift = ETDM_IN2_SDATA0_SEL_SFT;
> + } else if (!strcmp(kcontrol->id.name, "I2SIN3_LPBK")) {
> + reg = ETDM_0_3_COWORK_CON3;
> + mask = ETDM_IN3_SDATA0_SEL_MASK_SFT;
> + shift = ETDM_IN3_SDATA0_SEL_SFT;
> + } else if (!strcmp(kcontrol->id.name, "I2SIN4_LPBK")) {
> + reg = ETDM_4_7_COWORK_CON1;
> +
> + // Get I2SIN4 multi-ip mode
> + regmap_read(afe->regmap, ETDM_IN4_CON2, &value_ipmode);
> + value_ipmode = FIELD_GET(BIT(31), value_ipmode);
> +
> + if (value_ipmode) {
> + mask = ETDM_IN4_SDATA1_15_SEL_MASK_SFT;
> + shift = ETDM_IN4_SDATA1_15_SEL_SFT;
> + } else {
> + mask = ETDM_IN4_SDATA0_SEL_MASK_SFT;
> + shift = ETDM_IN4_SDATA0_SEL_SFT;
> + }
It seems to me that only I2S4IN_LPBK needs special handling, while
the others can just use enum map values with the standard ENUM control.
That would simplify the code by a lot, and also get rid of all the
string comparisons.
> + } else if (!strcmp(kcontrol->id.name, "I2SIN6_LPBK")) {
> + reg = ETDM_4_7_COWORK_CON3;
> + mask = ETDM_IN6_SDATA0_SEL_MASK_SFT;
> + shift = ETDM_IN6_SDATA0_SEL_SFT;
> + } else {
> + dev_warn(afe->dev, "i2s lpbk no match\n");
> + return 0;
> + }
> +
> + if (reg)
> + regmap_read(afe->regmap, reg, &value);
> + else
> + value = 0;
> +
> + value &= mask;
> + value >>= shift;
> + ucontrol->value.enumerated.item[0] = value;
> +
> + switch (value) {
> + case 0x8:
> + case 0xa:
> + case 0xc:
> + case 0xe:
> + ucontrol->value.enumerated.item[0] = 1;
> + break;
> + default:
> + ucontrol->value.enumerated.item[0] = 0;
> + break;
> + }
> +
> + return 0;
> +}
> +
> +static int etdm_lpbk_put(struct snd_kcontrol *kcontrol,
> + struct snd_ctl_elem_value *ucontrol)
> +{
> + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
Please use snd_soc_kcontrol_component() as seen in the previous function.
They are effectively the same, but this one is part of the ASoC API.
> + struct mtk_base_afe *afe = snd_soc_component_get_drvdata(component);
> + u32 value = ucontrol->value.integer.value[0];
> + u32 value_ipmode;
> + u32 reg;
> + u32 val;
> + u32 mask;
> +
> + if (value >= ARRAY_SIZE(etdm_lpbk_idx_0))
> + return -EINVAL;
> +
> + if (!strcmp(kcontrol->id.name, "I2SIN0_LPBK")) {
> + reg = ETDM_0_3_COWORK_CON1;
> + mask = ETDM_IN0_SDATA0_SEL_MASK_SFT;
> + val = etdm_lpbk_idx_0[value] << ETDM_IN0_SDATA0_SEL_SFT;
> + } else if (!strcmp(kcontrol->id.name, "I2SIN1_LPBK")) {
> + reg = ETDM_0_3_COWORK_CON1;
> + mask = ETDM_IN1_SDATA0_SEL_MASK_SFT;
> + val = etdm_lpbk_idx_1[value] << ETDM_IN1_SDATA0_SEL_SFT;
> + } else if (!strcmp(kcontrol->id.name, "I2SIN2_LPBK")) {
> + reg = ETDM_0_3_COWORK_CON3;
> + mask = ETDM_IN2_SDATA0_SEL_MASK_SFT;
> + val = etdm_lpbk_idx_2[value] << ETDM_IN2_SDATA0_SEL_SFT;
> + } else if (!strcmp(kcontrol->id.name, "I2SIN3_LPBK")) {
> + reg = ETDM_0_3_COWORK_CON3;
> + mask = ETDM_IN3_SDATA0_SEL_MASK_SFT;
> + val = etdm_lpbk_idx_3[value] << ETDM_IN3_SDATA0_SEL_SFT;
> + } else if (!strcmp(kcontrol->id.name, "I2SIN4_LPBK")) {
> + reg = ETDM_4_7_COWORK_CON1;
> +
> + // Get I2SIN4 multi-ip mode
> + regmap_read(afe->regmap, ETDM_IN4_CON2, &value_ipmode);
> + value_ipmode = FIELD_GET(BIT(31), value_ipmode);
> +
> + if (!value) {
> + mask = ETDM_IN4_SDATA1_15_SEL_MASK_SFT |
> + ETDM_IN4_SDATA0_SEL_MASK_SFT;
> + val = (etdm_lpbk_idx_0[value] << ETDM_IN4_SDATA1_15_SEL_SFT) |
> + (etdm_lpbk_idx_0[value] << ETDM_IN4_SDATA0_SEL_SFT);
> + } else if (value_ipmode) {
> + mask = ETDM_IN4_SDATA1_15_SEL_MASK_SFT;
> + val = etdm_lpbk_idx_0[value] << ETDM_IN4_SDATA1_15_SEL_SFT;
> + } else {
> + mask = ETDM_IN4_SDATA0_SEL_MASK_SFT;
> + val = etdm_lpbk_idx_0[value] << ETDM_IN4_SDATA0_SEL_SFT;
> + }
> + } else {
> + reg = ETDM_4_7_COWORK_CON3;
> + mask = ETDM_IN6_SDATA0_SEL_MASK_SFT;
> + val = etdm_lpbk_idx_2[value] << ETDM_IN6_SDATA0_SEL_SFT;
> + }
> +
> + if (reg)
> + regmap_update_bits(afe->regmap, reg, mask, val);
> +
> + return 0;
> +}
> +
> +static const char *const etdm_lpbk_map[] = {
> + "Off", "On",
> +};
> +
> +static SOC_ENUM_SINGLE_EXT_DECL(etdm_lpbk_map_enum,
> + etdm_lpbk_map);
> +/* lpbk */
> +
> +/* multi-ip mode */
> +static const int etdm_ip_mode_idx[] = {
> + 0x0, 0x1,
> +};
> +
> +static int etdm_ip_mode_get(struct snd_kcontrol *kcontrol,
> + struct snd_ctl_elem_value *ucontrol)
> +{
> + struct snd_soc_component *component =
> + snd_soc_kcontrol_component(kcontrol);
> + struct mtk_base_afe *afe = snd_soc_component_get_drvdata(component);
> + struct mt8196_afe_private *afe_priv = afe->platform_priv;
> + struct mtk_afe_i2s_priv *i2sin4_priv = afe_priv->dai_priv[MT8196_DAI_I2S_IN4];
> +
> + ucontrol->value.enumerated.item[0] = i2sin4_priv->ip_mode;
> +
> + return 0;
> +}
> +
> +static int etdm_ip_mode_put(struct snd_kcontrol *kcontrol,
> + struct snd_ctl_elem_value *ucontrol)
> +{
> + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
Please use snd_soc_kcontrol_component() as seen in the previous function.
They are effectively the same, but this one is part of the ASoC API.
Please replace any other snd_kcontrol_chip() invocations as well.
> + struct mtk_base_afe *afe = snd_soc_component_get_drvdata(component);
> + struct mt8196_afe_private *afe_priv = afe->platform_priv;
> + struct mtk_afe_i2s_priv *i2sin4_priv = afe_priv->dai_priv[MT8196_DAI_I2S_IN4];
> + unsigned int value = ucontrol->value.integer.value[0];
> +
> + if (value >= ARRAY_SIZE(etdm_ip_mode_idx))
> + return -EINVAL;
> +
> + /* 0: One IP multi-channel 1: Multi-IP 2-channel */
> + i2sin4_priv->ip_mode = etdm_ip_mode_idx[value];
> +
> + return 0;
> +}
> +
> +static const char *const etdm_ip_mode_map[] = {
> + "Off", "On",
> +};
> +
> +static SOC_ENUM_SINGLE_EXT_DECL(etdm_ip_mode_map_enum,
> + etdm_ip_mode_map);
Put it on the same line.
> +/* multi-ip mode */
> +
> +/* ch num */
> +static const int etdm_ch_num_idx[] = {
> + 0x2, 0x4, 0x6, 0x8,
> +};
> +
> +static int etdm_ch_num_get(struct snd_kcontrol *kcontrol,
> + struct snd_ctl_elem_value *ucontrol)
> +{
> + struct snd_soc_component *component =
> + snd_soc_kcontrol_component(kcontrol);
> + struct mtk_base_afe *afe = snd_soc_component_get_drvdata(component);
> + struct mt8196_afe_private *afe_priv = afe->platform_priv;
> + struct mtk_afe_i2s_priv *i2sin4_priv = afe_priv->dai_priv[MT8196_DAI_I2S_IN4];
> + struct mtk_afe_i2s_priv *i2sout4_priv = afe_priv->dai_priv[MT8196_DAI_I2S_OUT4];
> + unsigned int value = 0;
> +
> + if (!strcmp(kcontrol->id.name, "I2SIN4_CH_NUM"))
> + value = i2sin4_priv->ch_num;
> + else if (!strcmp(kcontrol->id.name, "I2SOUT4_CH_NUM"))
> + value = i2sout4_priv->ch_num;
> +
> + if (value == 0x2)
> + ucontrol->value.enumerated.item[0] = 0;
> + else if (value == 0x4)
> + ucontrol->value.enumerated.item[0] = 1;
> + else if (value == 0x6)
> + ucontrol->value.enumerated.item[0] = 2;
> + else
> + ucontrol->value.enumerated.item[0] = 3;
Replace the if-else if-else blocks with:
ucontrol->value.enumerated.item[0] = (value >> 1) - 1;
The internal value is controlled by the driver and never goes out of range.
> +
> + return 0;
> +}
> +
> +static int etdm_ch_num_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 mt8196_afe_private *afe_priv = afe->platform_priv;
> + struct mtk_afe_i2s_priv *i2sin4_priv = afe_priv->dai_priv[MT8196_DAI_I2S_IN4];
> + struct mtk_afe_i2s_priv *i2sout4_priv = afe_priv->dai_priv[MT8196_DAI_I2S_OUT4];
> + unsigned int value = ucontrol->value.integer.value[0];
> +
> + if (value >= ARRAY_SIZE(etdm_ch_num_idx))
> + return -EINVAL;
> +
> + if (!strcmp(kcontrol->id.name, "I2SIN4_CH_NUM"))
> + i2sin4_priv->ch_num = etdm_ch_num_idx[value];
i2sin4_priv->ch_num = (value + 1) << 1;
And then etdm_ch_num_idx can be dropped.
> + else if (!strcmp(kcontrol->id.name, "I2SOUT4_CH_NUM"))
> + i2sout4_priv->ch_num = etdm_ch_num_idx[value];
> +
> + return 0;
> +}
> +
> +static const char *const etdm_ch_num_map[] = {
> + "2CH", "4CH", "6CH", "8CH",
> +};
> +
> +static SOC_ENUM_SINGLE_EXT_DECL(etdm_ch_num_map_enum,
> + etdm_ch_num_map);
> +/* ch num */
> +
> +enum {
> + I2S_FMT_EIAJ = 0,
> + I2S_FMT_I2S = 1,
> +};
> +
> +enum {
> + I2S_WLEN_16_BIT = 0,
> + I2S_WLEN_32_BIT = 1,
> +};
> +
> +enum {
> + I2S_HD_NORMAL = 0,
> + I2S_HD_LOW_JITTER = 1,
> +};
> +
> +enum {
> + I2S1_SEL_O28_O29 = 0,
> + I2S1_SEL_O03_O04 = 1,
> +};
> +
> +enum {
> + I2S_IN_PAD_CONNSYS = 0,
> + I2S_IN_PAD_IO_MUX = 1,
> +};
> +
> +static unsigned int get_i2s_wlen(snd_pcm_format_t format)
> +{
> + return snd_pcm_format_physical_width(format) <= 16 ?
> + I2S_WLEN_16_BIT : I2S_WLEN_32_BIT;
> +}
> +
> +#define MTK_AFE_I2SIN0_KCONTROL_NAME "I2SIN0_HD_Mux"
> +#define MTK_AFE_I2SIN1_KCONTROL_NAME "I2SIN1_HD_Mux"
> +#define MTK_AFE_I2SIN2_KCONTROL_NAME "I2SIN2_HD_Mux"
> +#define MTK_AFE_I2SIN4_KCONTROL_NAME "I2SIN4_HD_Mux"
> +#define MTK_AFE_I2SIN6_KCONTROL_NAME "I2SIN6_HD_Mux"
> +#define MTK_AFE_I2SOUT0_KCONTROL_NAME "I2SOUT0_HD_Mux"
> +#define MTK_AFE_I2SOUT1_KCONTROL_NAME "I2SOUT1_HD_Mux"
> +#define MTK_AFE_I2SOUT2_KCONTROL_NAME "I2SOUT2_HD_Mux"
> +#define MTK_AFE_I2SOUT4_KCONTROL_NAME "I2SOUT4_HD_Mux"
> +#define MTK_AFE_I2SOUT6_KCONTROL_NAME "I2SOUT6_HD_Mux"
> +#define MTK_AFE_FMI2S_MASTER_KCONTROL_NAME "FMI2S_MASTER_HD_Mux"
> +
> +#define I2SIN0_HD_EN_W_NAME "I2SIN0_HD_EN"
> +#define I2SIN1_HD_EN_W_NAME "I2SIN1_HD_EN"
> +#define I2SIN2_HD_EN_W_NAME "I2SIN2_HD_EN"
> +#define I2SIN3_HD_EN_W_NAME "I2SIN3_HD_EN"
> +#define I2SIN4_HD_EN_W_NAME "I2SIN4_HD_EN"
> +#define I2SIN6_HD_EN_W_NAME "I2SIN6_HD_EN"
> +#define I2SOUT0_HD_EN_W_NAME "I2SOUT0_HD_EN"
> +#define I2SOUT1_HD_EN_W_NAME "I2SOUT1_HD_EN"
> +#define I2SOUT2_HD_EN_W_NAME "I2SOUT2_HD_EN"
> +#define I2SOUT3_HD_EN_W_NAME "I2SOUT3_HD_EN"
> +#define I2SOUT4_HD_EN_W_NAME "I2SOUT4_HD_EN"
> +#define I2SOUT6_HD_EN_W_NAME "I2SOUT6_HD_EN"
> +#define FMI2S_MASTER_HD_EN_W_NAME "FMI2S_MASTER_HD_EN"
> +
> +#define I2SIN0_MCLK_EN_W_NAME "I2SIN0_MCLK_EN"
> +#define I2SIN1_MCLK_EN_W_NAME "I2SIN1_MCLK_EN"
> +#define I2SIN2_MCLK_EN_W_NAME "I2SIN2_MCLK_EN"
> +#define I2SIN3_MCLK_EN_W_NAME "I2SIN3_MCLK_EN"
> +#define I2SIN4_MCLK_EN_W_NAME "I2SIN4_MCLK_EN"
> +#define I2SIN6_MCLK_EN_W_NAME "I2SIN6_MCLK_EN"
> +#define I2SOUT0_MCLK_EN_W_NAME "I2SOUT0_MCLK_EN"
> +#define I2SOUT1_MCLK_EN_W_NAME "I2SOUT1_MCLK_EN"
> +#define I2SOUT2_MCLK_EN_W_NAME "I2SOUT2_MCLK_EN"
> +#define I2SOUT3_MCLK_EN_W_NAME "I2SOUT3_MCLK_EN"
> +#define I2SOUT4_MCLK_EN_W_NAME "I2SOUT4_MCLK_EN"
> +#define I2SOUT6_MCLK_EN_W_NAME "I2SOUT6_MCLK_EN"
> +#define FMI2S_MASTER_MCLK_EN_W_NAME "FMI2S_MASTER_MCLK_EN"
> +
> +static int get_i2s_id_by_name(struct mtk_base_afe *afe,
> + const char *name)
> +{
> + if (strncmp(name, "I2SIN0", 6) == 0)
> + return MT8196_DAI_I2S_IN0;
> + else if (strncmp(name, "I2SIN1", 6) == 0)
> + return MT8196_DAI_I2S_IN1;
> + else if (strncmp(name, "I2SIN2", 6) == 0)
> + return MT8196_DAI_I2S_IN2;
> + else if (strncmp(name, "I2SIN3", 6) == 0)
> + return MT8196_DAI_I2S_IN3;
> + else if (strncmp(name, "I2SIN4", 6) == 0)
> + return MT8196_DAI_I2S_IN4;
> + else if (strncmp(name, "I2SIN6", 6) == 0)
> + return MT8196_DAI_I2S_IN6;
> + else if (strncmp(name, "I2SOUT0", 7) == 0)
> + return MT8196_DAI_I2S_OUT0;
> + else if (strncmp(name, "I2SOUT1", 7) == 0)
> + return MT8196_DAI_I2S_OUT1;
> + else if (strncmp(name, "I2SOUT2", 7) == 0)
> + return MT8196_DAI_I2S_OUT2;
> + else if (strncmp(name, "I2SOUT3", 7) == 0)
> + return MT8196_DAI_I2S_OUT3;
> + else if (strncmp(name, "I2SOUT4", 7) == 0)
> + return MT8196_DAI_I2S_OUT4;
> + else if (strncmp(name, "I2SOUT6", 7) == 0)
> + return MT8196_DAI_I2S_OUT6;
> + else if (strncmp(name, "FMI2S_MASTER", 12) == 0)
> + return MT8196_DAI_FM_I2S_MASTER;
> + else
> + return -EINVAL;
> +}
> +
> +static struct mtk_afe_i2s_priv *get_i2s_priv_by_name(struct mtk_base_afe *afe,
> + const char *name)
> +{
> + struct mt8196_afe_private *afe_priv = afe->platform_priv;
> + int dai_id = get_i2s_id_by_name(afe, name);
> +
> + if (dai_id < 0)
> + return NULL;
> +
> + return afe_priv->dai_priv[dai_id];
> +}
> +
> +/*
> + * bit mask for i2s low power control
> + * such as bit0 for i2s0, bit1 for i2s1...
> + * if set 1, means i2s low power mode
> + * if set 0, means i2s low jitter mode
> + * 0 for all i2s bit in default
> + */
> +static unsigned int i2s_low_power_mask;
> +static int mtk_i2s_low_power_mask_get(struct snd_kcontrol *kcontrol,
> + struct snd_ctl_elem_value *ucontrol)
> +{
> + pr_debug("%s(), mask: %x\n", __func__, i2s_low_power_mask);
> + ucontrol->value.integer.value[0] = i2s_low_power_mask;
> + return 0;
> +}
> +
> +static int mtk_i2s_low_power_mask_set(struct snd_kcontrol *kcontrol,
> + struct snd_ctl_elem_value *ucontrol)
> +{
> + i2s_low_power_mask = ucontrol->value.integer.value[0];
> + return 0;
> +}
> +
> +static int mtk_is_i2s_low_power(int i2s_num)
> +{
> + int i2s_bit_shift;
> +
> + i2s_bit_shift = i2s_num - MT8196_DAI_I2S_IN0;
> + if (i2s_bit_shift < 0 || i2s_bit_shift > MT8196_DAI_I2S_MAX_NUM)
> + return 0;
> +
> + return (i2s_low_power_mask >> i2s_bit_shift) & 0x1;
> +}
This doesn't really do anything besides block the HD EN route connection.
Is there supposed to be a matching hardware configuration? I don't
understand the purpose of this control. If it serves no purpose, it
might be better to just remove it.
And I think controls shouldn't be just a bitmask. It's hard for users to
understand what bit map to which interface.
> +/* low jitter control */
> +static const char *const mt8196_i2s_hd_str[] = {
> + "Normal", "Low_Jitter"
> +};
> +
> +static const struct soc_enum mt8196_i2s_enum[] = {
> + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(mt8196_i2s_hd_str),
> + mt8196_i2s_hd_str),
> +};
> +
> +static int mt8196_i2s_hd_get(struct snd_kcontrol *kcontrol,
> + struct snd_ctl_elem_value *ucontrol)
> +{
> + struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
> + struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
> + struct mtk_afe_i2s_priv *i2s_priv;
> +
> + i2s_priv = get_i2s_priv_by_name(afe, kcontrol->id.name);
> +
> + if (!i2s_priv)
> + return -EINVAL;
> +
> + ucontrol->value.integer.value[0] = i2s_priv->low_jitter_en;
> +
> + return 0;
> +}
> +
> +static int mt8196_i2s_hd_set(struct snd_kcontrol *kcontrol,
> + struct snd_ctl_elem_value *ucontrol)
> +{
> + struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
> + struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
> + struct mtk_afe_i2s_priv *i2s_priv;
> + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
> + int hd_en;
> +
> + if (ucontrol->value.enumerated.item[0] >= e->items)
> + return -EINVAL;
> +
> + hd_en = ucontrol->value.integer.value[0];
> +
> + dev_dbg(afe->dev, "kcontrol name %s, hd_en %d\n", kcontrol->id.name, hd_en);
> +
> + i2s_priv = get_i2s_priv_by_name(afe, kcontrol->id.name);
> +
> + if (!i2s_priv)
> + return -EINVAL;
> +
> + i2s_priv->low_jitter_en = hd_en;
> +
> + return 0;
> +}
> +
> +static const struct snd_kcontrol_new mtk_dai_i2s_controls[] = {
> + SOC_ENUM_EXT(MTK_AFE_I2SIN0_KCONTROL_NAME, mt8196_i2s_enum[0],
> + mt8196_i2s_hd_get, mt8196_i2s_hd_set),
> + SOC_ENUM_EXT(MTK_AFE_I2SIN1_KCONTROL_NAME, mt8196_i2s_enum[0],
> + mt8196_i2s_hd_get, mt8196_i2s_hd_set),
> + SOC_ENUM_EXT(MTK_AFE_I2SIN2_KCONTROL_NAME, mt8196_i2s_enum[0],
> + mt8196_i2s_hd_get, mt8196_i2s_hd_set),
> + SOC_ENUM_EXT(MTK_AFE_I2SIN4_KCONTROL_NAME, mt8196_i2s_enum[0],
> + mt8196_i2s_hd_get, mt8196_i2s_hd_set),
> + SOC_ENUM_EXT(MTK_AFE_I2SIN6_KCONTROL_NAME, mt8196_i2s_enum[0],
> + mt8196_i2s_hd_get, mt8196_i2s_hd_set),
> + SOC_ENUM_EXT(MTK_AFE_I2SOUT0_KCONTROL_NAME, mt8196_i2s_enum[0],
> + mt8196_i2s_hd_get, mt8196_i2s_hd_set),
> + SOC_ENUM_EXT(MTK_AFE_I2SOUT1_KCONTROL_NAME, mt8196_i2s_enum[0],
> + mt8196_i2s_hd_get, mt8196_i2s_hd_set),
> + SOC_ENUM_EXT(MTK_AFE_I2SOUT2_KCONTROL_NAME, mt8196_i2s_enum[0],
> + mt8196_i2s_hd_get, mt8196_i2s_hd_set),
> + SOC_ENUM_EXT(MTK_AFE_I2SOUT4_KCONTROL_NAME, mt8196_i2s_enum[0],
> + mt8196_i2s_hd_get, mt8196_i2s_hd_set),
> + SOC_ENUM_EXT(MTK_AFE_I2SOUT6_KCONTROL_NAME, mt8196_i2s_enum[0],
> + mt8196_i2s_hd_get, mt8196_i2s_hd_set),
> + SOC_ENUM_EXT(MTK_AFE_FMI2S_MASTER_KCONTROL_NAME, mt8196_i2s_enum[0],
> + mt8196_i2s_hd_get, mt8196_i2s_hd_set),
> + SOC_SINGLE_EXT("i2s_low_power_mask", SND_SOC_NOPM, 0, 0xffff, 0,
> + mtk_i2s_low_power_mask_get,
> + mtk_i2s_low_power_mask_set),
> +
> + SOC_ENUM_EXT("I2SIN0_LPBK", etdm_lpbk_map_enum,
> + etdm_lpbk_get, etdm_lpbk_put),
> + SOC_ENUM_EXT("I2SIN1_LPBK", etdm_lpbk_map_enum,
> + etdm_lpbk_get, etdm_lpbk_put),
> + SOC_ENUM_EXT("I2SIN2_LPBK", etdm_lpbk_map_enum,
> + etdm_lpbk_get, etdm_lpbk_put),
> + SOC_ENUM_EXT("I2SIN3_LPBK", etdm_lpbk_map_enum,
> + etdm_lpbk_get, etdm_lpbk_put),
> + SOC_ENUM_EXT("I2SIN4_LPBK", etdm_lpbk_map_enum,
> + etdm_lpbk_get, etdm_lpbk_put),
> + SOC_ENUM_EXT("I2SIN6_LPBK", etdm_lpbk_map_enum,
> + etdm_lpbk_get, etdm_lpbk_put),
Please use a more sane name for the control. Just call it "I2SINx Loopback".
> + SOC_ENUM_EXT("I2SIN4_IP_MODE", etdm_ip_mode_map_enum,
I2SIN4 IP mode
> + etdm_ip_mode_get, etdm_ip_mode_put),
> + SOC_ENUM_EXT("I2SIN4_CH_NUM", etdm_ch_num_map_enum,
I2SIN4 # of channels
> + etdm_ch_num_get, etdm_ch_num_put),
> + SOC_ENUM_EXT("I2SOUT4_CH_NUM", etdm_ch_num_map_enum,
> + etdm_ch_num_get, etdm_ch_num_put),
> +};
> +
> +/* dai component */
> +/* i2s virtual mux to output widget */
> +static const char *const i2s_mux_map[] = {
> + "Normal", "Dummy_Widget",
> +};
I think this needs some explanation about why a dummy widget actually
exists, or why it is needed.
> +
> +static int i2s_mux_map_value[] = {
> + 0, 1,
> +};
> +
> +static SOC_VALUE_ENUM_SINGLE_AUTODISABLE_DECL(i2s_mux_map_enum,
> + SND_SOC_NOPM,
> + 0,
> + 1,
> + i2s_mux_map,
> + i2s_mux_map_value);
> +
> +static const struct snd_kcontrol_new i2s_in0_mux_control =
> + SOC_DAPM_ENUM("I2S IN0 Select", i2s_mux_map_enum);
> +static const struct snd_kcontrol_new i2s_in1_mux_control =
> + SOC_DAPM_ENUM("I2S IN1 Select", i2s_mux_map_enum);
> +static const struct snd_kcontrol_new i2s_in2_mux_control =
> + SOC_DAPM_ENUM("I2S IN2 Select", i2s_mux_map_enum);
> +static const struct snd_kcontrol_new i2s_in3_mux_control =
> + SOC_DAPM_ENUM("I2S IN3 Select", i2s_mux_map_enum);
> +static const struct snd_kcontrol_new i2s_in4_mux_control =
> + SOC_DAPM_ENUM("I2S IN4 Select", i2s_mux_map_enum);
> +static const struct snd_kcontrol_new i2s_in6_mux_control =
> + SOC_DAPM_ENUM("I2S IN6 Select", i2s_mux_map_enum);
> +static const struct snd_kcontrol_new i2s_out0_mux_control =
> + SOC_DAPM_ENUM("I2S OUT0 Select", i2s_mux_map_enum);
> +static const struct snd_kcontrol_new i2s_out1_mux_control =
> + SOC_DAPM_ENUM("I2S OUT1 Select", i2s_mux_map_enum);
> +static const struct snd_kcontrol_new i2s_out2_mux_control =
> + SOC_DAPM_ENUM("I2S OUT2 Select", i2s_mux_map_enum);
> +static const struct snd_kcontrol_new i2s_out3_mux_control =
> + SOC_DAPM_ENUM("I2S OUT3 Select", i2s_mux_map_enum);
> +static const struct snd_kcontrol_new i2s_out4_mux_control =
> + SOC_DAPM_ENUM("I2S OUT4 Select", i2s_mux_map_enum);
> +static const struct snd_kcontrol_new i2s_out6_mux_control =
> + SOC_DAPM_ENUM("I2S OUT6 Select", i2s_mux_map_enum);
> +
> +/* interconnection */
> +static const struct snd_kcontrol_new mtk_i2sout0_ch1_mix[] = {
> + SOC_DAPM_SINGLE_AUTODISABLE("DL0_CH1", AFE_CONN108_1, I_DL0_CH1, 1, 0),
I think there should be some explanation on why these are "AUTODISABLE".
[...]
> +enum {
> + SUPPLY_SEQ_APLL,
> + SUPPLY_SEQ_I2S_MCLK_EN,
> + SUPPLY_SEQ_I2S_CG_EN,
> + SUPPLY_SEQ_I2S_HD_EN,
> + SUPPLY_SEQ_I2S_EN,
I'm not sure why explicit sequencing is needed. The DAPM widgets are
connected in a directed graph, and widgets feeding other widgets are
enabled before the ones that are fed.
If the APLL widget feeds the MCLK widget, and the MCLK widget feeds
the I2S widget, then the APLL is enabled first, then the MCLK, then
the I2S.
> +};
> +
> +static int mtk_i2s_hd_en_event(struct snd_soc_dapm_widget *w,
> + struct snd_kcontrol *kcontrol,
> + int event)
> +{
> + struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
> +
> + dev_dbg(cmpnt->dev, "name %s, event 0x%x\n", w->name, event);
> +
> + return 0;
> +}
This doesn't do anything. Please drop it.
> +
> +static int mtk_apll_event(struct snd_soc_dapm_widget *w,
> + struct snd_kcontrol *kcontrol,
> + int event)
> +{
> + struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
> + struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
> +
> + dev_dbg(cmpnt->dev, "name %s, event 0x%x\n", w->name, event);
> +
> + switch (event) {
> + case SND_SOC_DAPM_PRE_PMU:
> + if (strcmp(w->name, APLL1_W_NAME) == 0)
> + mt8196_apll1_enable(afe);
> + else
> + mt8196_apll2_enable(afe);
> + break;
> + case SND_SOC_DAPM_POST_PMD:
> + if (strcmp(w->name, APLL1_W_NAME) == 0)
> + mt8196_apll1_disable(afe);
> + else
> + mt8196_apll2_disable(afe);
> + break;
> + default:
> + break;
> + }
> +
> + return 0;
> +}
> +
> +static int mtk_mclk_en_event(struct snd_soc_dapm_widget *w,
> + struct snd_kcontrol *kcontrol,
> + int event)
> +{
> + struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
> + struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
> + struct mtk_afe_i2s_priv *i2s_priv;
> +
> + dev_dbg(cmpnt->dev, "name %s, event 0x%x\n", w->name, event);
> +
> + i2s_priv = get_i2s_priv_by_name(afe, w->name);
> +
> + if (!i2s_priv)
> + return -EINVAL;
> +
> + switch (event) {
> + case SND_SOC_DAPM_PRE_PMU:
> + mt8196_mck_enable(afe, i2s_priv->mclk_id, i2s_priv->mclk_rate);
> + break;
> + case SND_SOC_DAPM_POST_PMD:
> + i2s_priv->mclk_rate = 0;
I think this shouldn't be reset here. It should be done only in .hwparams
and maybe .close or .shutdown callbacks. I believe it's actually possible
to toggle the interface (and thus the mclk) on and off while the stream
is running.
> + mt8196_mck_disable(afe, i2s_priv->mclk_id);
> + break;
> + default:
> + break;
> + }
> +
> + return 0;
> +}
> +
> +static const struct snd_soc_dapm_widget mtk_dai_i2s_widgets[] = {
[...]
> +
> + /* i2s hd en */
> + SND_SOC_DAPM_SUPPLY_S(I2SIN0_HD_EN_W_NAME, SUPPLY_SEQ_I2S_HD_EN,
> + SND_SOC_NOPM, 0, 0,
> + mtk_i2s_hd_en_event,
> + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
As mentioned above, please drop the no-op event. Same for the other *_HD_EN
widgets below.
[...]
> + /* allow i2s on without codec on */
> + SND_SOC_DAPM_OUTPUT("I2S_DUMMY_OUT"),
> + SND_SOC_DAPM_MUX("I2S_OUT0_Mux",
> + SND_SOC_NOPM, 0, 0, &i2s_out0_mux_control),
> + SND_SOC_DAPM_MUX("I2S_OUT1_Mux",
> + SND_SOC_NOPM, 0, 0, &i2s_out1_mux_control),
> + SND_SOC_DAPM_MUX("I2S_OUT2_Mux",
> + SND_SOC_NOPM, 0, 0, &i2s_out2_mux_control),
> + SND_SOC_DAPM_MUX("I2S_OUT3_Mux",
> + SND_SOC_NOPM, 0, 0, &i2s_out3_mux_control),
> + SND_SOC_DAPM_MUX("I2S_OUT4_Mux",
> + SND_SOC_NOPM, 0, 0, &i2s_out4_mux_control),
> + SND_SOC_DAPM_MUX("I2S_OUT6_Mux",
> + SND_SOC_NOPM, 0, 0, &i2s_out6_mux_control),
> +
> + SND_SOC_DAPM_INPUT("I2S_DUMMY_IN"),
> + SND_SOC_DAPM_MUX("I2S_IN0_Mux",
> + SND_SOC_NOPM, 0, 0, &i2s_in0_mux_control),
> + SND_SOC_DAPM_MUX("I2S_IN1_Mux",
> + SND_SOC_NOPM, 0, 0, &i2s_in1_mux_control),
> + SND_SOC_DAPM_MUX("I2S_IN2_Mux",
> + SND_SOC_NOPM, 0, 0, &i2s_in2_mux_control),
> + SND_SOC_DAPM_MUX("I2S_IN3_Mux",
> + SND_SOC_NOPM, 0, 0, &i2s_in3_mux_control),
> + SND_SOC_DAPM_MUX("I2S_IN4_Mux",
> + SND_SOC_NOPM, 0, 0, &i2s_in4_mux_control),
> + SND_SOC_DAPM_MUX("I2S_IN6_Mux",
> + SND_SOC_NOPM, 0, 0, &i2s_in6_mux_control),
Same question about dummy widgets.
> + SND_SOC_DAPM_MIXER("SOF_DMA_DL_24CH", SND_SOC_NOPM, 0, 0, NULL, 0),
> + SND_SOC_DAPM_MIXER("SOF_DMA_DL1", SND_SOC_NOPM, 0, 0, NULL, 0),
SOF widgets belong in the card/machine driver.
> +};
[...]
> +static const struct snd_soc_dapm_route mtk_dai_i2s_routes[] = {
[...]
> + /* SOF Downlink */
> + {"I2SOUT4_CH1", "DL_24CH_CH1", "SOF_DMA_DL_24CH"},
> + {"I2SOUT4_CH2", "DL_24CH_CH2", "SOF_DMA_DL_24CH"},
> + {"I2SOUT4_CH3", "DL_24CH_CH3", "SOF_DMA_DL_24CH"},
> + {"I2SOUT4_CH4", "DL_24CH_CH4", "SOF_DMA_DL_24CH"},
SOF widgets and routes belong in the card driver.
[...]
> + /* SOF Downlink */
> + {"I2SOUT6_CH1", "DL1_CH1", "SOF_DMA_DL1"},
> + {"I2SOUT6_CH2", "DL1_CH2", "SOF_DMA_DL1"},
> + {"I2SOUT6_CH1", "DL_24CH_CH1", "SOF_DMA_DL_24CH"},
> + {"I2SOUT6_CH2", "DL_24CH_CH2", "SOF_DMA_DL_24CH"},
Same comment about SOF.
[...]
> + /* allow i2s on without codec on */
> + {"I2SIN0", NULL, "I2S_IN0_Mux"},
> + {"I2S_IN0_Mux", "Dummy_Widget", "I2S_DUMMY_IN"},
> +
> + {"I2SIN1", NULL, "I2S_IN1_Mux"},
> + {"I2S_IN1_Mux", "Dummy_Widget", "I2S_DUMMY_IN"},
> +
> + {"I2SIN2", NULL, "I2S_IN2_Mux"},
> + {"I2S_IN2_Mux", "Dummy_Widget", "I2S_DUMMY_IN"},
> +
> + {"I2SIN3", NULL, "I2S_IN3_Mux"},
> + {"I2S_IN3_Mux", "Dummy_Widget", "I2S_DUMMY_IN"},
> +
> + {"I2SIN4", NULL, "I2S_IN4_Mux"},
> + {"I2S_IN4_Mux", "Dummy_Widget", "I2S_DUMMY_IN"},
> +
> + {"I2SIN6", NULL, "I2S_IN6_Mux"},
> + {"I2S_IN6_Mux", "Dummy_Widget", "I2S_DUMMY_IN"},
> +
> + {"I2S_OUT0_Mux", "Dummy_Widget", "I2SOUT0"},
> + {"I2S_DUMMY_OUT", NULL, "I2S_OUT0_Mux"},
> +
> + {"I2S_OUT1_Mux", "Dummy_Widget", "I2SOUT1"},
> + {"I2S_DUMMY_OUT", NULL, "I2S_OUT1_Mux"},
> +
> + {"I2S_OUT2_Mux", "Dummy_Widget", "I2SOUT2"},
> + {"I2S_DUMMY_OUT", NULL, "I2S_OUT2_Mux"},
> +
> + {"I2S_OUT3_Mux", "Dummy_Widget", "I2SOUT3"},
> + {"I2S_DUMMY_OUT", NULL, "I2S_OUT3_Mux"},
> + {"I2S_OUT4_Mux", "Dummy_Widget", "I2SOUT4"},
> + {"I2S_DUMMY_OUT", NULL, "I2S_OUT4_Mux"},
> +
> + {"I2S_OUT6_Mux", "Dummy_Widget", "I2SOUT6"},
> + {"I2S_DUMMY_OUT", NULL, "I2S_OUT6_Mux"},
Same question about dummy widgets.
> +};
> +
> +/* i2s dai ops*/
> +static int mtk_dai_i2s_config(struct mtk_base_afe *afe,
> + struct snd_pcm_hw_params *params,
> + int i2s_id)
> +{
> + struct mt8196_afe_private *afe_priv = afe->platform_priv;
> + struct mtk_afe_i2s_priv *i2s_priv;
> + struct mtk_afe_i2s_priv *i2sin_priv = NULL;
> + int id = i2s_id - MT8196_DAI_I2S_IN0;
> + struct mtk_base_etdm_data etdm_data;
> + unsigned int rate = params_rate(params);
> + unsigned int rate_reg = get_etdm_inconn_rate(rate);
> + snd_pcm_format_t format = params_format(params);
> + unsigned int channels = params_channels(params);
> + int ret;
> + unsigned int i2s_con;
> + int pad_top;
> +
> + if (i2s_id >= MT8196_DAI_NUM || i2s_id < 0 || id < 0 || id >= DAI_I2S_NUM)
> + return -EINVAL;
> +
> + i2s_priv = afe_priv->dai_priv[i2s_id];
> +
Drop the empty line here so that the check immediately follows the assignment.
> + if (!i2s_priv)
> + return -EINVAL;
> +
> + dev_info(afe->dev, "id: %d, rate: %d, pcm_fmt: %d, fmt: %d, ch: %d\n",
> + i2s_id, rate, format, i2s_priv->format, channels);
Make this debug level.
> + i2s_priv->rate = rate;
> + etdm_data = mtk_etdm_data[id];
> +
> + if (is_etdm_in_pad_top(id))
> + pad_top = 0x3;
> + else
> + pad_top = 0x5;
> +
> + switch (id) {
> + case DAI_FMI2S_MASTER:
> + i2s_con = I2S_IN_PAD_IO_MUX << I2SIN_PAD_SEL_SFT;
> + i2s_con |= rate_reg << I2S_MODE_SFT;
> + i2s_con |= I2S_FMT_I2S << I2S_FMT_SFT;
> + i2s_con |= get_i2s_wlen(format) << I2S_WLEN_SFT;
> + regmap_update_bits(afe->regmap, AFE_CONNSYS_I2S_CON,
> + 0xffffeffe, i2s_con);
> + break;
Please add an empty line after the "break" statement to clearly separate
each block. Same for the following blocks.
> + case DAI_I2SIN0:
> + case DAI_I2SIN1:
> + case DAI_I2SIN2:
> + case DAI_I2SIN3:
> + case DAI_I2SIN4:
> + case DAI_I2SIN6:
> + /* ---etdm in --- */
> + regmap_update_bits(afe->regmap,
> + etdm_data.init_count_reg,
> + etdm_data.init_count_mask << etdm_data.init_count_shift,
> + 0x5 << etdm_data.init_count_shift);
> +
> + /* 3: pad top 5: no pad top */
> + regmap_update_bits(afe->regmap,
> + etdm_data.init_point_reg,
> + etdm_data.init_point_mask << etdm_data.init_point_shift,
> + pad_top << etdm_data.init_point_shift);
> +
> + regmap_update_bits(afe->regmap,
> + etdm_data.lrck_reset_reg,
> + etdm_data.lrck_reset_mask << etdm_data.lrck_reset_shift,
> + 0x1 << etdm_data.lrck_reset_shift);
> +
> + regmap_update_bits(afe->regmap,
> + etdm_data.clk_source_reg,
> + etdm_data.clk_source_mask << etdm_data.clk_source_shift,
> + ETDM_CLK_SOURCE_APLL << etdm_data.clk_source_shift);
> +
> + /* 0: manual 1: auto */
> + regmap_update_bits(afe->regmap,
> + etdm_data.ck_en_sel_reg,
> + etdm_data.ck_en_sel_mask << etdm_data.ck_en_sel_shift,
> + 0x1 << etdm_data.ck_en_sel_shift);
> +
> + regmap_update_bits(afe->regmap,
> + etdm_data.fs_timing_reg,
> + etdm_data.fs_timing_mask << etdm_data.fs_timing_shift,
> + get_etdm_rate(rate) << etdm_data.fs_timing_shift);
> +
> + regmap_update_bits(afe->regmap,
> + etdm_data.relatch_en_sel_reg,
> + etdm_data.relatch_en_sel_mask << etdm_data.relatch_en_sel_shift,
> + get_etdm_inconn_rate(rate) << etdm_data.relatch_en_sel_shift);
> +
> + regmap_update_bits(afe->regmap,
> + etdm_data.use_afifo_reg,
> + etdm_data.use_afifo_mask << etdm_data.use_afifo_shift,
> + 0x0);
> +
> + regmap_update_bits(afe->regmap,
> + etdm_data.afifo_mode_reg,
> + etdm_data.afifo_mode_mask << etdm_data.afifo_mode_shift,
> + 0x0);
> +
> + regmap_update_bits(afe->regmap,
> + etdm_data.almost_end_ch_reg,
> + etdm_data.almost_end_ch_mask << etdm_data.almost_end_ch_shift,
> + 0x0);
> +
> + regmap_update_bits(afe->regmap,
> + etdm_data.almost_end_bit_reg,
> + etdm_data.almost_end_bit_mask << etdm_data.almost_end_bit_shift,
> + 0x0);
> +
> + if (is_etdm_in_pad_top(id)) {
> + regmap_update_bits(afe->regmap,
> + etdm_data.out2latch_time_reg,
> + etdm_data.out2latch_time_mask <<
> + etdm_data.out2latch_time_shift,
> + 0x6 << etdm_data.out2latch_time_shift);
> + } else {
> + regmap_update_bits(afe->regmap,
> + etdm_data.out2latch_time_reg,
> + etdm_data.out2latch_time_mask <<
> + etdm_data.out2latch_time_shift,
> + 0x4 << etdm_data.out2latch_time_shift);
> + }
> +
> + if (id == DAI_I2SIN4) {
> + dev_dbg(afe->dev, "i2sin4, id: %d, fmt: %d, ch: %d, ip_mode: %d, sync: %d\n",
> + id,
> + i2s_priv->format,
> + channels,
> + i2s_priv->ip_mode,
> + i2s_priv->sync);
Put more parameters on one line.
[...]
> +static int mtk_dai_i2s_set_sysclk(struct snd_soc_dai *dai,
> + int clk_id, unsigned int freq, int dir)
> +{
> + struct mtk_base_afe *afe = dev_get_drvdata(dai->dev);
> + struct mt8196_afe_private *afe_priv = afe->platform_priv;
> + struct mtk_afe_i2s_priv *i2s_priv;
> + int apll;
> + int apll_rate;
> +
> + if (dai->id >= MT8196_DAI_NUM || dai->id < 0 || dir != SND_SOC_CLOCK_OUT)
> + return -EINVAL;
> +
> + i2s_priv = afe_priv->dai_priv[dai->id];
> +
> + dev_dbg(afe->dev, "freq: %u\n", freq);
> +
Drop empty line here and make the check below immediately follow
the i2s_priv assignment.
You might want to move the debug trace before the assignment and
make it include more information?
> + if (!i2s_priv)
> + return -EINVAL;
> +
> + apll = mt8196_get_apll_by_rate(afe, freq);
> + apll_rate = mt8196_get_apll_rate(afe, apll);
> +
> + if (freq > apll_rate || apll_rate % freq)
> + return -EINVAL;
> +
> + i2s_priv->mclk_rate = freq;
> + i2s_priv->mclk_apll = apll;
> +
> + if (i2s_priv->share_i2s_id > 0) {
> + struct mtk_afe_i2s_priv *share_i2s_priv;
> +
> + share_i2s_priv = afe_priv->dai_priv[i2s_priv->share_i2s_id];
> + if (!share_i2s_priv)
> + return -EINVAL;
> +
> + share_i2s_priv->mclk_rate = i2s_priv->mclk_rate;
> + share_i2s_priv->mclk_apll = i2s_priv->mclk_apll;
> + }
Add empty line here before final return statement.
> + return 0;
> +}
> +
> +static int mtk_dai_i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
> +{
> + struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
> + struct mt8196_afe_private *afe_priv = afe->platform_priv;
> + struct mtk_afe_i2s_priv *i2s_priv;
> +
> + if (dai->id >= MT8196_DAI_NUM || dai->id < 0)
> + return -EINVAL;
> +
> + i2s_priv = afe_priv->dai_priv[dai->id];
> +
Drop empty line here.
> + if (!i2s_priv)
> + return -EINVAL;
> +
> + dev_dbg(afe->dev, "dai->id: %d, fmt: 0x%x\n", dai->id, fmt);
> +
> + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
> + case SND_SOC_DAIFMT_I2S:
> + i2s_priv->format = MTK_DAI_ETDM_FORMAT_I2S;
> + break;
> + case SND_SOC_DAIFMT_LEFT_J:
> + i2s_priv->format = MTK_DAI_ETDM_FORMAT_LJ;
> + break;
> + case SND_SOC_DAIFMT_RIGHT_J:
> + i2s_priv->format = MTK_DAI_ETDM_FORMAT_RJ;
> + break;
> + case SND_SOC_DAIFMT_DSP_A:
> + i2s_priv->format = MTK_DAI_ETDM_FORMAT_DSPA;
> + break;
> + case SND_SOC_DAIFMT_DSP_B:
> + i2s_priv->format = MTK_DAI_ETDM_FORMAT_DSPB;
> + break;
> + default:
> + return -EINVAL;
> + }
> +
> + dev_dbg(afe->dev, "dai->id: %d, i2s_priv->format: 0x%x\n",
> + dai->id, i2s_priv->format);
Drop this one. One debug trace is enough.
> +
> + return 0;
> +}
> +
> +static const struct snd_soc_dai_ops mtk_dai_i2s_ops = {
> + .hw_params = mtk_dai_i2s_hw_params,
> + .set_sysclk = mtk_dai_i2s_set_sysclk,
> + .set_fmt = mtk_dai_i2s_set_fmt,
> +};
> +
> +/* dai driver */
> +#define MTK_ETDM_RATES (SNDRV_PCM_RATE_8000_384000)
> +#define MTK_ETDM_FORMATS (SNDRV_PCM_FMTBIT_S8 |\
> + SNDRV_PCM_FMTBIT_S16_LE |\
> + SNDRV_PCM_FMTBIT_S24_LE |\
> + SNDRV_PCM_FMTBIT_S32_LE)
> +
> +#define MT8196_I2S_DAI(_name, _id, max_ch, dir) \
> +{ \
> + .name = #_name, \
> + .id = _id, \
> + .dir = { \
> + .stream_name = #_name, \
> + .channels_min = 1, \
> + .channels_max = max_ch, \
> + .rates = MTK_ETDM_RATES, \
> + .formats = MTK_ETDM_FORMATS, \
> + }, \
> + .ops = &mtk_dai_i2s_ops, \
> +}
> +
> +static struct snd_soc_dai_driver mtk_dai_i2s_driver[] = {
> + /* capture */
> + MT8196_I2S_DAI(I2SIN0, MT8196_DAI_I2S_IN0, 2, capture),
> + MT8196_I2S_DAI(I2SIN1, MT8196_DAI_I2S_IN1, 2, capture),
> + MT8196_I2S_DAI(I2SIN2, MT8196_DAI_I2S_IN2, 2, capture),
> + MT8196_I2S_DAI(I2SIN3, MT8196_DAI_I2S_IN3, 2, capture),
> + MT8196_I2S_DAI(I2SIN4, MT8196_DAI_I2S_IN4, 8, capture),
> + MT8196_I2S_DAI(I2SIN6, MT8196_DAI_I2S_IN6, 2, capture),
> + MT8196_I2S_DAI(FMI2S_MASTER, MT8196_DAI_FM_I2S_MASTER, 2, capture),
> + /* playback */
> + MT8196_I2S_DAI(I2SOUT0, MT8196_DAI_I2S_OUT0, 2, playback),
> + MT8196_I2S_DAI(I2SOUT1, MT8196_DAI_I2S_OUT1, 2, playback),
> + MT8196_I2S_DAI(I2SOUT2, MT8196_DAI_I2S_OUT2, 2, playback),
> + MT8196_I2S_DAI(I2SOUT3, MT8196_DAI_I2S_OUT3, 2, playback),
> + MT8196_I2S_DAI(I2SOUT4, MT8196_DAI_I2S_OUT4, 8, playback),
> + MT8196_I2S_DAI(I2SOUT6, MT8196_DAI_I2S_OUT6, 2, playback),
> +};
> +
> +static const struct mtk_afe_i2s_priv mt8196_i2s_priv[DAI_I2S_NUM] = {
> + [DAI_I2SIN0] = {
> + .id = MT8196_DAI_I2S_IN0,
> + .mclk_id = MT8196_I2SIN0_MCK,
> + .share_property_name = "i2sin0-share",
> + .share_i2s_id = -1,
> + },
> + [DAI_I2SIN1] = {
> + .id = MT8196_DAI_I2S_IN1,
> + .mclk_id = MT8196_I2SIN1_MCK,
> + .share_property_name = "i2sin1-share",
> + .share_i2s_id = -1,
> + },
> + [DAI_I2SIN2] = {
> + .id = MT8196_DAI_I2S_IN2,
> + .mclk_id = MT8196_I2SIN0_MCK,
> + .share_property_name = "i2sin2-share",
> + .share_i2s_id = -1,
> + },
> + [DAI_I2SIN3] = {
> + .id = MT8196_DAI_I2S_IN3,
> + .mclk_id = MT8196_I2SIN0_MCK,
> + .share_property_name = "i2sin3-share",
> + .share_i2s_id = -1,
> + },
> + [DAI_I2SIN4] = {
> + .id = MT8196_DAI_I2S_IN4,
> + .mclk_id = MT8196_I2SIN0_MCK,
> + .share_property_name = "i2sin4-share",
> + .share_i2s_id = -1,
> + .sync = 0,
> + .ip_mode = 0,
> + },
> + [DAI_I2SIN6] = {
> + .id = MT8196_DAI_I2S_IN6,
> + .mclk_id = MT8196_I2SIN0_MCK,
> + .share_property_name = "i2sout6-share",
> + .share_i2s_id = -1,
> + },
> + [DAI_I2SOUT0] = {
> + .id = MT8196_DAI_I2S_OUT0,
> + .mclk_id = MT8196_I2SIN0_MCK,
> + .share_property_name = "i2sout0-share",
> + .share_i2s_id = MT8196_DAI_I2S_IN0,
> + },
> + [DAI_I2SOUT1] = {
> + .id = MT8196_DAI_I2S_OUT1,
> + .mclk_id = MT8196_I2SIN1_MCK,
> + .share_property_name = "i2sout1-share",
> + .share_i2s_id = MT8196_DAI_I2S_IN1,
> + },
> + [DAI_I2SOUT2] = {
> + .id = MT8196_DAI_I2S_OUT2,
> + .mclk_id = MT8196_I2SIN0_MCK,
> + .share_property_name = "i2sout2-share",
> + .share_i2s_id = MT8196_DAI_I2S_IN2,
> + },
> + [DAI_I2SOUT3] = {
> + .id = MT8196_DAI_I2S_OUT3,
> + .mclk_id = MT8196_I2SIN0_MCK,
> + .share_property_name = "i2sout3-share",
> + .share_i2s_id = MT8196_DAI_I2S_IN3,
> + },
> + [DAI_I2SOUT4] = {
> + .id = MT8196_DAI_I2S_OUT4,
> + .mclk_id = MT8196_I2SIN0_MCK,
> + .share_property_name = "i2sout4-share",
> + .share_i2s_id = MT8196_DAI_I2S_IN4,
> + .sync = 0,
> + },
> + [DAI_I2SOUT6] = {
> + .id = MT8196_DAI_I2S_OUT6,
> + .mclk_id = MT8196_I2SIN0_MCK,
> + .share_property_name = "i2sout6-share",
> + .share_i2s_id = MT8196_DAI_I2S_IN6,
> + },
> + [DAI_FMI2S_MASTER] = {
> + .id = MT8196_DAI_FM_I2S_MASTER,
> + .mclk_id = MT8196_FMI2S_MCK,
> + .share_property_name = "fmi2s-share",
> + .share_i2s_id = -1,
> + },
> +};
> +
> +static int mt8196_dai_i2s_get_share(struct mtk_base_afe *afe)
> +{
> + struct mt8196_afe_private *afe_priv = afe->platform_priv;
> + const struct device_node *of_node = afe->dev->of_node;
> + const char *of_str;
> + const char *property_name;
> + struct mtk_afe_i2s_priv *i2s_priv;
of_str, property_name, and i2s_priv declarations can be moved inside the
loop.
> + int i;
> +
> + for (i = 0; i < DAI_I2S_NUM; i++) {
Here you can write C99 style, and also use unsigned type for index:
for (unsigned int i = 0; i < DAI_I2S_NUM; i++) {
> + i2s_priv = afe_priv->dai_priv[mt8196_i2s_priv[i].id];
> + property_name = mt8196_i2s_priv[i].share_property_name;
> + if (of_property_read_string(of_node, property_name, &of_str))
> + continue;
> + i2s_priv->share_i2s_id = get_i2s_id_by_name(afe, of_str);
> + }
Nit: add empty line here.
> + return 0;
> +}
> +
> +static int init_i2s_priv_data(struct mtk_base_afe *afe)
> +{
> + struct mt8196_afe_private *afe_priv = afe->platform_priv;
> + struct mtk_afe_i2s_priv *i2s_priv;
> + int size;
Please use size_t for size variable.
> + int id;
You can move i2s_priv, size, and id declarations inside the loop.
> + int i;
> +
> + for (i = 0; i < DAI_I2S_NUM; i++) {
Here you can write C99 style, and also use unsigned type for index:
for (unsigned int i = 0; i < DAI_I2S_NUM; i++) {
> + id = mt8196_i2s_priv[i].id;
> + size = sizeof(struct mtk_afe_i2s_priv);
> +
> + if (id >= MT8196_DAI_NUM || id < 0)
> + return -EINVAL;
> +
> + i2s_priv = devm_kzalloc(afe->dev, size, GFP_KERNEL);
> + if (!i2s_priv)
> + return -ENOMEM;
> +
> + memcpy(i2s_priv, &mt8196_i2s_priv[i], size);
> +
> + afe_priv->dai_priv[id] = i2s_priv;
> + }
Nit: add an empty line here.
> + return 0;
> +}
> +
> +int mt8196_dai_i2s_register(struct mtk_base_afe *afe)
> +{
> + struct mtk_base_afe_dai *dai;
> + int ret;
> +
> + dai = devm_kzalloc(afe->dev, sizeof(*dai), GFP_KERNEL);
> + if (!dai)
> + return -ENOMEM;
> +
> + list_add(&dai->list, &afe->sub_dais);
> +
> + dai->dai_drivers = mtk_dai_i2s_driver;
> + dai->num_dai_drivers = ARRAY_SIZE(mtk_dai_i2s_driver);
> +
> + dai->controls = mtk_dai_i2s_controls;
> + dai->num_controls = ARRAY_SIZE(mtk_dai_i2s_controls);
> + dai->dapm_widgets = mtk_dai_i2s_widgets;
> + dai->num_dapm_widgets = ARRAY_SIZE(mtk_dai_i2s_widgets);
> + dai->dapm_routes = mtk_dai_i2s_routes;
> + dai->num_dapm_routes = ARRAY_SIZE(mtk_dai_i2s_routes);
> +
> + /* set all dai i2s private data */
> + ret = init_i2s_priv_data(afe);
> + if (ret)
> + return ret;
> +
> + /* parse share i2s */
> + ret = mt8196_dai_i2s_get_share(afe);
> + if (ret)
> + return ret;
> +
> + return 0;
> +}
> --
> 2.45.2
>
>
^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [PATCH v6 05/10] ASoC: mediatek: mt8196: support I2S in platform driver
[not found] ` <20250708111806.3992-6-darren.ye@mediatek.com>
2025-08-11 11:03 ` [PATCH v6 05/10] ASoC: mediatek: mt8196: support I2S in platform driver Chen-Yu Tsai
@ 2025-08-11 11:24 ` Chen-Yu Tsai
1 sibling, 0 replies; 24+ messages in thread
From: Chen-Yu Tsai @ 2025-08-11 11:24 UTC (permalink / raw)
To: Darren.Ye
Cc: Liam Girdwood, Mark Brown, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Matthias Brugger, AngeloGioacchino Del Regno,
Jaroslav Kysela, Takashi Iwai, Linus Walleij, Bartosz Golaszewski,
linux-sound, devicetree, linux-kernel, linux-arm-kernel,
linux-mediatek, linux-gpio
Sorry for another reply, but I lost some of the context I wanted to reply to.
On Tue, Jul 8, 2025 at 7:34 PM Darren.Ye <darren.ye@mediatek.com> wrote:
>
> From: Darren Ye <darren.ye@mediatek.com>
>
> Add mt8196 I2S DAI driver support.
>
> Signed-off-by: Darren Ye <darren.ye@mediatek.com>
> ---
> sound/soc/mediatek/mt8196/mt8196-dai-i2s.c | 3944 ++++++++++++++++++++
> 1 file changed, 3944 insertions(+)
> create mode 100644 sound/soc/mediatek/mt8196/mt8196-dai-i2s.c
>
> diff --git a/sound/soc/mediatek/mt8196/mt8196-dai-i2s.c b/sound/soc/mediatek/mt8196/mt8196-dai-i2s.c
> new file mode 100644
> index 000000000000..59f66ab8fa9f
> --- /dev/null
> +++ b/sound/soc/mediatek/mt8196/mt8196-dai-i2s.c
> @@ -0,0 +1,3944 @@
[...]
> +static int mtk_afe_i2s_share_connect(struct snd_soc_dapm_widget *source,
> + struct snd_soc_dapm_widget *sink)
> +{
> + struct snd_soc_dapm_widget *w = sink;
> + struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
> + struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
> + struct mtk_afe_i2s_priv *i2s_priv;
> + int ret = 0;
> +
> + i2s_priv = get_i2s_priv_by_name(afe, sink->name);
> +
Drop empty line between assignment and check.
> + if (!i2s_priv)
> + return 0;
> +
> + if (i2s_priv->share_i2s_id < 0)
> + return 0;
> +
> + ret = (i2s_priv->share_i2s_id == get_i2s_id_by_name(afe, source->name)) ? 1 : 0;
> +
> + return ret;
return i2s_priv->share_i2s_id == get_i2s_id_by_name(afe,
source->name);
bool casts to int implicitly in the same way you wrote explicitly.
> +}
> +
> +static int mtk_afe_i2s_hd_connect(struct snd_soc_dapm_widget *source,
> + struct snd_soc_dapm_widget *sink)
> +{
> + struct snd_soc_dapm_widget *w = sink;
> + struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
> + struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
> + struct mtk_afe_i2s_priv *i2s_priv;
> + int i2s_num;
Rename to "src_i2s_num" to be explicit.
> +
> + i2s_priv = get_i2s_priv_by_name(afe, sink->name);
> +
Drop empty line between assignment and check.
> + if (!i2s_priv)
> + return 0;
> +
> + i2s_num = get_i2s_id_by_name(afe, source->name);
> + if (get_i2s_id_by_name(afe, sink->name) == i2s_num)
Use i2s_priv->id?
> + return !mtk_is_i2s_low_power(i2s_num) ||
> + i2s_priv->low_jitter_en;
> +
> + /* check if share i2s need hd en */
> + if (i2s_priv->share_i2s_id < 0)
> + return 0;
> +
> + if (i2s_priv->share_i2s_id == i2s_num)
> + return !mtk_is_i2s_low_power(i2s_num) ||
> + i2s_priv->low_jitter_en;
> +
> + return 0;
> +}
> +
> +static int mtk_afe_i2s_apll_connect(struct snd_soc_dapm_widget *source,
> + struct snd_soc_dapm_widget *sink)
> +{
> + struct snd_soc_dapm_widget *w = sink;
> + struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
> + struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
> + struct mtk_afe_i2s_priv *i2s_priv;
> + int cur_apll;
> + int i2s_need_apll;
Rename to needed_apll or desired_apll.
> +
> + i2s_priv = get_i2s_priv_by_name(afe, w->name);
> +
Drop empty line between assignment and check.
> + if (!i2s_priv)
> + return 0;
> +
> + /* which apll */
> + cur_apll = mt8196_get_apll_by_name(afe, source->name);
> +
> + /* choose APLL from i2s rate */
> + i2s_need_apll = mt8196_get_apll_by_rate(afe, i2s_priv->rate);
> +
> + return (i2s_need_apll == cur_apll) ? 1 : 0;
return i2s_need_apll == cur_apll;
> +}
> +
> +static int mtk_afe_i2s_mclk_connect(struct snd_soc_dapm_widget *source,
> + struct snd_soc_dapm_widget *sink)
> +{
> + struct snd_soc_dapm_widget *w = sink;
> + struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
> + struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
> + struct mtk_afe_i2s_priv *i2s_priv;
> +
> + i2s_priv = get_i2s_priv_by_name(afe, sink->name);
> +
Drop empty line between assignment and check.
> + if (!i2s_priv)
> + return 0;
> +
> + if (get_i2s_id_by_name(afe, sink->name) ==
i2s_priv->id == ...
> + get_i2s_id_by_name(afe, source->name))
Keep a copy of the result ...
> + return (i2s_priv->mclk_rate > 0) ? 1 : 0;
return i2s_priv->mclk_rate > 0;
> +
> + /* check if share i2s need mclk */
> + if (i2s_priv->share_i2s_id < 0)
> + return 0;
> +
> + if (i2s_priv->share_i2s_id == get_i2s_id_by_name(afe, source->name))
and use it here, like in mtk_afe_i2s_hd_connect().
> + return (i2s_priv->mclk_rate > 0) ? 1 : 0;
return i2s_priv->mclk_rate > 0;
> + return 0;
> +}
> +
> +static int mtk_afe_mclk_apll_connect(struct snd_soc_dapm_widget *source,
> + struct snd_soc_dapm_widget *sink)
> +{
> + struct snd_soc_dapm_widget *w = sink;
> + struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
> + struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
> + struct mtk_afe_i2s_priv *i2s_priv;
> + int cur_apll;
> +
> + i2s_priv = get_i2s_priv_by_name(afe, w->name);
> +
Drop empty line between assignment and check.
> + if (!i2s_priv)
> + return 0;
> +
> + /* which apll */
> + cur_apll = mt8196_get_apll_by_name(afe, source->name);
> +
> + return (i2s_priv->mclk_apll == cur_apll) ? 1 : 0;
return i2s_priv->mclk_apll == cur_apll;
> +}
[...]
ChenYu
^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [PATCH v6 06/10] ASoC: mediatek: mt8196: support TDM in platform driver
2025-07-28 10:53 ` Chen-Yu Tsai
@ 2025-08-21 8:58 ` Darren Ye (叶飞)
0 siblings, 0 replies; 24+ messages in thread
From: Darren Ye (叶飞) @ 2025-08-21 8:58 UTC (permalink / raw)
To: wenst@chromium.org
Cc: linux-kernel@vger.kernel.org, linux-mediatek@lists.infradead.org,
devicetree@vger.kernel.org, linus.walleij@linaro.org,
linux-sound@vger.kernel.org, linux-gpio@vger.kernel.org,
broonie@kernel.org, brgl@bgdev.pl, conor+dt@kernel.org,
tiwai@suse.com, robh@kernel.org, lgirdwood@gmail.com,
linux-arm-kernel@lists.infradead.org, matthias.bgg@gmail.com,
krzk+dt@kernel.org, perex@perex.cz, AngeloGioacchino Del Regno
On Mon, 2025-07-28 at 18:53 +0800, Chen-Yu Tsai wrote:
> External email : Please do not click links or open attachments until
> you have verified the sender or the content.
>
>
> On Tue, Jul 8, 2025 at 7:34 PM Darren.Ye <darren.ye@mediatek.com>
> wrote:
> >
> > From: Darren Ye <darren.ye@mediatek.com>
> >
> > Add mt8196 TDM DAI driver support.
> >
> > Signed-off-by: Darren Ye <darren.ye@mediatek.com>
> > ---
> > sound/soc/mediatek/mt8196/mt8196-dai-tdm.c | 836
> > +++++++++++++++++++++
> > 1 file changed, 836 insertions(+)
> > create mode 100644 sound/soc/mediatek/mt8196/mt8196-dai-tdm.c
> >
> > diff --git a/sound/soc/mediatek/mt8196/mt8196-dai-tdm.c
> > b/sound/soc/mediatek/mt8196/mt8196-dai-tdm.c
> > new file mode 100644
> > index 000000000000..dcbde41fb61c
> > --- /dev/null
> > +++ b/sound/soc/mediatek/mt8196/mt8196-dai-tdm.c
> > @@ -0,0 +1,836 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/*
> > + * MediaTek ALSA SoC Audio DAI TDM Control
> > + *
> > + * Copyright (c) 2024 MediaTek Inc.
> > + * Author: Darren Ye <darren.ye@mediatek.com>
> > + */
> > +
> > +#include <linux/regmap.h>
> > +#include <sound/pcm_params.h>
> > +#include "mt8196-afe-clk.h"
> > +#include "mt8196-afe-common.h"
> > +#include "mt8196-interconnection.h"
> > +
> > +struct mtk_afe_tdm_priv {
> > + int bck_id;
> > + int bck_rate;
> > +
> > + int mclk_id;
> > + int mclk_multiple; /* according to sample rate */
> > + int mclk_rate;
> > + int mclk_apll;
> > +};
> > +
> > +enum {
> > + TDM_WLEN_16_BIT = 1,
> > + TDM_WLEN_32_BIT = 2,
>
> I believe this was mentioned in another patch, but consecutive values
> do not need to be assigned. Nor does a 0 starting value. I won't
> mention
> this below, but please check all the enums.
>
> > +};
> > +
> > +enum {
> > + TDM_CHANNEL_BCK_16 = 0,
> > + TDM_CHANNEL_BCK_24 = 1,
> > + TDM_CHANNEL_BCK_32 = 2,
> > +};
> > +
> > +enum {
> > + TDM_CHANNEL_NUM_2 = 0,
> > + TDM_CHANNEL_NUM_4 = 1,
> > + TDM_CHANNEL_NUM_8 = 2,
> > +};
> > +
> > +enum {
> > + TDM_CH_START_O30_O31 = 0,
> > + TDM_CH_START_O32_O33,
> > + TDM_CH_START_O34_O35,
> > + TDM_CH_START_O36_O37,
> > + TDM_CH_ZERO,
> > +};
> > +
> > +enum {
> > + DPTX_CHANNEL_2,
> > + DPTX_CHANNEL_8,
> > +};
> > +
> > +enum {
> > + DPTX_WLEN_24_BIT,
> > + DPTX_WLEN_16_BIT,
> > +};
> > +
> > +enum {
> > + DPTX_CH_EN_MASK_2CH = 0x3,
> > + DPTX_CH_EN_MASK_4CH = 0xf,
> > + DPTX_CH_EN_MASK_6CH = 0x3f,
> > + DPTX_CH_EN_MASK_8CH = 0xff,
> > +};
>
> I'm not entirely confident, but I think normally we use macros for
> register
> values, and enums for internal / software state values.
>
> > +
> > +static unsigned int get_tdm_wlen(snd_pcm_format_t format)
> > +{
> > + return snd_pcm_format_physical_width(format) <= 16 ?
> > + TDM_WLEN_16_BIT : TDM_WLEN_32_BIT;
>
> Looking at the datasheet, this also supports 8 bit and 24 bit word
> lengths?
>
> This could just be written as:
>
> return snd_pcm_format_physical_width(format) / 8;
>
> > +}
> > +
> > +static unsigned int get_tdm_channel_bck(snd_pcm_format_t format)
> > +{
> > + return snd_pcm_format_physical_width(format) <= 16 ?
> > + TDM_CHANNEL_BCK_16 : TDM_CHANNEL_BCK_32;
>
> I don't think this is correct. I believe this refers to the TDM slot
> width, which is separate from how wide or how many bits are valid in
> a given sample. The TDM slot width is something set by the machine
> driver by calling snd_soc_dai_set_tdm_slot(), much like calling
> snd_soc_dai_set_sysclk().
>
> The TDM driver here needs to implement the .set_tdm_slot callback,
> and store the slot width and slots.
>
> This function here should be something like:
>
> static unsigned int get_tdm_channel_bck(...)
> {
> return tdm_slot_width;
> }
>
> > +}
> > +
> > +static unsigned int get_tdm_lrck_width(snd_pcm_format_t format)
> > +{
> > + return snd_pcm_format_physical_width(format) - 1;
>
> This needs to be multiplied by the number of channels / 2, since
> the LRCK spans the entirety of the odd or even number channels.
> Also, it should be based on the TDM slot width, not the sample width.
>
> > +}
Based on offline discussions, the TDM controller only works with the
internal codec, and they will automatically negotiate which data format
to use.
> > +
> > +static unsigned int get_tdm_ch(unsigned int ch)
> > +{
> > + switch (ch) {
> > + case 1:
> > + case 2:
> > + return TDM_CHANNEL_NUM_2;
> > + case 3:
> > + case 4:
> > + return TDM_CHANNEL_NUM_4;
> > + case 5:
> > + case 6:
> > + case 7:
> > + case 8:
> > + default:
> > + return TDM_CHANNEL_NUM_8;
> > + }
> > +}
> > +
> > +static unsigned int get_dptx_ch_enable_mask(unsigned int ch)
> > +{
> > + switch (ch) {
> > + case 1:
> > + case 2:
> > + return DPTX_CH_EN_MASK_2CH;
> > + case 3:
> > + case 4:
> > + return DPTX_CH_EN_MASK_4CH;
> > + case 5:
> > + case 6:
> > + return DPTX_CH_EN_MASK_6CH;
> > + case 7:
> > + case 8:
> > + return DPTX_CH_EN_MASK_8CH;
> > + default:
> > + pr_info("invalid channel num, default use 2ch\n");
>
> Please pass in |struct device *| and use the dev_printk variants.
> And maybe consider making this a warning?
>
> Also there is some inconsistency here,
>
> > + return DPTX_CH_EN_MASK_2CH;
> > + }
> > +}
> > +
> > +static unsigned int get_dptx_ch(unsigned int ch)
> > +{
> > + if (ch == 2)
> > + return DPTX_CHANNEL_2;
> > + else
> > + return DPTX_CHANNEL_8;
> > +}
> > +
> > +static unsigned int get_dptx_wlen(snd_pcm_format_t format)
> > +{
> > + return snd_pcm_format_physical_width(format) <= 16 ?
> > + DPTX_WLEN_16_BIT : DPTX_WLEN_24_BIT;
> > +}
> > +
> > +/* interconnection */
> > +enum {
> > + HDMI_CONN_CH0 = 0,
> > + HDMI_CONN_CH1,
> > + HDMI_CONN_CH2,
> > + HDMI_CONN_CH3,
> > + HDMI_CONN_CH4,
> > + HDMI_CONN_CH5,
> > + HDMI_CONN_CH6,
> > + HDMI_CONN_CH7,
> > +};
> > +
> > +static const char *const hdmi_conn_mux_map[] = {
> > + "CH0", "CH1", "CH2", "CH3",
> > + "CH4", "CH5", "CH6", "CH7",
>
> Nit: This could fit in one line.
>
> > +};
> > +
> > +static int hdmi_conn_mux_map_value[] = {
> > + HDMI_CONN_CH0,
> > + HDMI_CONN_CH1,
> > + HDMI_CONN_CH2,
> > + HDMI_CONN_CH3,
> > + HDMI_CONN_CH4,
> > + HDMI_CONN_CH5,
> > + HDMI_CONN_CH6,
> > + HDMI_CONN_CH7,
>
> Nit: You could fit four values on one line.
>
> > +};
> > +
> > +static SOC_VALUE_ENUM_SINGLE_DECL(hdmi_ch0_mux_map_enum,
> > + AFE_HDMI_CONN0,
> > + HDMI_O_0_SFT,
> > + HDMI_O_0_MASK,
> > + hdmi_conn_mux_map,
> > + hdmi_conn_mux_map_value);
> > +
> > +static const struct snd_kcontrol_new hdmi_ch0_mux_control =
> > + SOC_DAPM_ENUM("HDMI_CH0_MUX", hdmi_ch0_mux_map_enum);
>
> Please name the controls based on the standard ALSA control name
> scheme:
>
>
> https://urldefense.com/v3/__https://docs.kernel.org/sound/designs/control-names.html__;!!CTRNKA9wMg0ARbw!i_tPw3qUBa_oSWs_TbOSkvyukDl7Yb53mxn2lLoNd5dePggKvY-FUwdx0dV0z_Nzul6i2ve5-XKEViQr$
>
> This should be something like "HDMI CH0 Source Playback Route". Same
> applies to the other ones.
yes, it has been modified.
>
> > +
> > +static SOC_VALUE_ENUM_SINGLE_DECL(hdmi_ch1_mux_map_enum,
> > + AFE_HDMI_CONN0,
> > + HDMI_O_1_SFT,
> > + HDMI_O_1_MASK,
> > + hdmi_conn_mux_map,
> > + hdmi_conn_mux_map_value);
> > +
> > +static const struct snd_kcontrol_new hdmi_ch1_mux_control =
> > + SOC_DAPM_ENUM("HDMI_CH1_MUX", hdmi_ch1_mux_map_enum);
> > +
> > +static SOC_VALUE_ENUM_SINGLE_DECL(hdmi_ch2_mux_map_enum,
> > + AFE_HDMI_CONN0,
> > + HDMI_O_2_SFT,
> > + HDMI_O_2_MASK,
> > + hdmi_conn_mux_map,
> > + hdmi_conn_mux_map_value);
> > +
> > +static const struct snd_kcontrol_new hdmi_ch2_mux_control =
> > + SOC_DAPM_ENUM("HDMI_CH2_MUX", hdmi_ch2_mux_map_enum);
> > +
> > +static SOC_VALUE_ENUM_SINGLE_DECL(hdmi_ch3_mux_map_enum,
> > + AFE_HDMI_CONN0,
> > + HDMI_O_3_SFT,
> > + HDMI_O_3_MASK,
> > + hdmi_conn_mux_map,
> > + hdmi_conn_mux_map_value);
> > +
> > +static const struct snd_kcontrol_new hdmi_ch3_mux_control =
> > + SOC_DAPM_ENUM("HDMI_CH3_MUX", hdmi_ch3_mux_map_enum);
> > +
> > +static SOC_VALUE_ENUM_SINGLE_DECL(hdmi_ch4_mux_map_enum,
> > + AFE_HDMI_CONN0,
> > + HDMI_O_4_SFT,
> > + HDMI_O_4_MASK,
> > + hdmi_conn_mux_map,
> > + hdmi_conn_mux_map_value);
> > +
> > +static const struct snd_kcontrol_new hdmi_ch4_mux_control =
> > + SOC_DAPM_ENUM("HDMI_CH4_MUX", hdmi_ch4_mux_map_enum);
> > +
> > +static SOC_VALUE_ENUM_SINGLE_DECL(hdmi_ch5_mux_map_enum,
> > + AFE_HDMI_CONN0,
> > + HDMI_O_5_SFT,
> > + HDMI_O_5_MASK,
> > + hdmi_conn_mux_map,
> > + hdmi_conn_mux_map_value);
> > +
> > +static const struct snd_kcontrol_new hdmi_ch5_mux_control =
> > + SOC_DAPM_ENUM("HDMI_CH5_MUX", hdmi_ch5_mux_map_enum);
> > +
> > +static SOC_VALUE_ENUM_SINGLE_DECL(hdmi_ch6_mux_map_enum,
> > + AFE_HDMI_CONN0,
> > + HDMI_O_6_SFT,
> > + HDMI_O_6_MASK,
> > + hdmi_conn_mux_map,
> > + hdmi_conn_mux_map_value);
> > +
> > +static const struct snd_kcontrol_new hdmi_ch6_mux_control =
> > + SOC_DAPM_ENUM("HDMI_CH6_MUX", hdmi_ch6_mux_map_enum);
> > +
> > +static SOC_VALUE_ENUM_SINGLE_DECL(hdmi_ch7_mux_map_enum,
> > + AFE_HDMI_CONN0,
> > + HDMI_O_7_SFT,
> > + HDMI_O_7_MASK,
> > + hdmi_conn_mux_map,
> > + hdmi_conn_mux_map_value);
> > +
> > +static const struct snd_kcontrol_new hdmi_ch7_mux_control =
> > + SOC_DAPM_ENUM("HDMI_CH7_MUX", hdmi_ch7_mux_map_enum);
> > +
> > +static const char *const tdm_out_mux_map[] = {
> > + "Disconnect", "Connect",
> > +};
> > +
> > +static int tdm_out_mux_map_value[] = {
> > + 0, 1,
> > +};
> > +
> > +static
> > SOC_VALUE_ENUM_SINGLE_AUTODISABLE_DECL(hdmi_out_mux_map_enum,
> > + SND_SOC_NOPM,
> > + 0,
> > + 1,
> > + tdm_out_mux_map,
> > + tdm_out_mux_map_value);
>
> Please align with the left parentheses.
>
> > +static const struct snd_kcontrol_new hdmi_out_mux_control =
> > + SOC_DAPM_ENUM("HDMI_OUT_MUX", hdmi_out_mux_map_enum);
> > +
> > +static
> > SOC_VALUE_ENUM_SINGLE_AUTODISABLE_DECL(dptx_out_mux_map_enum,
> > + SND_SOC_NOPM,
> > + 0,
> > + 1,
> > + tdm_out_mux_map,
> > + tdm_out_mux_map_value);
>
> Same here.
>
> > +static const struct snd_kcontrol_new dptx_out_mux_control =
> > + SOC_DAPM_ENUM("DPTX_OUT_MUX", dptx_out_mux_map_enum);
> > +
> > +static
> > SOC_VALUE_ENUM_SINGLE_AUTODISABLE_DECL(dptx_virtual_out_mux_map_enu
> > m,
> > + SND_SOC_NOPM,
> > + 0,
> > + 1,
> > + tdm_out_mux_map,
> > + tdm_out_mux_map_value);
>
> Same here.
>
> > +
> > +static const struct snd_kcontrol_new dptx_virtual_out_mux_control
> > =
> > + SOC_DAPM_ENUM("DPTX_VIRTUAL_OUT_MUX",
> > dptx_virtual_out_mux_map_enum);
> > +
> > +enum {
> > + SUPPLY_SEQ_APLL,
> > + SUPPLY_SEQ_TDM_MCK_EN,
> > + SUPPLY_SEQ_TDM_BCK_EN,
> > + SUPPLY_SEQ_TDM_DPTX_MCK_EN,
> > + SUPPLY_SEQ_TDM_DPTX_BCK_EN,
> > + SUPPLY_SEQ_TDM_CG_EN,
> > +};
> > +
> > +static int get_tdm_id_by_name(const char *name)
> > +{
> > + if (strstr(name, "DPTX"))
> > + return MT8196_DAI_TDM_DPTX;
> > + else
> > + return MT8196_DAI_TDM;
> > +}
> > +
> > +static int mtk_tdm_bck_en_event(struct snd_soc_dapm_widget *w,
> > + struct snd_kcontrol *kcontrol,
> > + int event)
> > +{
> > + struct snd_soc_component *cmpnt =
> > snd_soc_dapm_to_component(w->dapm);
> > + struct mtk_base_afe *afe =
> > snd_soc_component_get_drvdata(cmpnt);
> > + struct mt8196_afe_private *afe_priv = afe->platform_priv;
> > + int dai_id = get_tdm_id_by_name(w->name);
> > + struct mtk_afe_tdm_priv *tdm_priv = afe_priv-
> > >dai_priv[dai_id];
> > +
> > + dev_dbg(cmpnt->dev, "name %s, event 0x%x, dai_id %d\n",
> > + w->name, event, dai_id);
> > +
> > + switch (event) {
> > + case SND_SOC_DAPM_PRE_PMU:
> > + mt8196_mck_enable(afe, tdm_priv->bck_id, tdm_priv-
> > >bck_rate);
> > + break;
> > + case SND_SOC_DAPM_POST_PMD:
> > + mt8196_mck_disable(afe, tdm_priv->bck_id);
> > + break;
> > + default:
> > + break;
> > + }
> > +
> > + return 0;
> > +}
> > +
> > +static int mtk_tdm_mck_en_event(struct snd_soc_dapm_widget *w,
> > + struct snd_kcontrol *kcontrol,
> > + int event)
> > +{
> > + struct snd_soc_component *cmpnt =
> > snd_soc_dapm_to_component(w->dapm);
> > + struct mtk_base_afe *afe =
> > snd_soc_component_get_drvdata(cmpnt);
> > + struct mt8196_afe_private *afe_priv = afe->platform_priv;
> > + int dai_id = get_tdm_id_by_name(w->name);
> > + struct mtk_afe_tdm_priv *tdm_priv = afe_priv-
> > >dai_priv[dai_id];
> > +
> > + dev_dbg(cmpnt->dev, "name %s, event 0x%x, dai_id %d\n",
> > + w->name, event, dai_id);
> > +
> > + switch (event) {
> > + case SND_SOC_DAPM_PRE_PMU:
> > + mt8196_mck_enable(afe, tdm_priv->mclk_id, tdm_priv-
> > >mclk_rate);
> > + break;
> > + case SND_SOC_DAPM_POST_PMD:
> > + tdm_priv->mclk_rate = 0;
> > + mt8196_mck_disable(afe, tdm_priv->mclk_id);
> > + break;
> > + default:
> > + break;
> > + }
> > +
> > + return 0;
> > +}
> > +
> > +static const struct snd_soc_dapm_widget mtk_dai_tdm_widgets[] = {
> > + SND_SOC_DAPM_MUX("HDMI_CH0_MUX", SND_SOC_NOPM, 0, 0,
> > + &hdmi_ch0_mux_control),
> > + SND_SOC_DAPM_MUX("HDMI_CH1_MUX", SND_SOC_NOPM, 0, 0,
> > + &hdmi_ch1_mux_control),
> > + SND_SOC_DAPM_MUX("HDMI_CH2_MUX", SND_SOC_NOPM, 0, 0,
> > + &hdmi_ch2_mux_control),
> > + SND_SOC_DAPM_MUX("HDMI_CH3_MUX", SND_SOC_NOPM, 0, 0,
> > + &hdmi_ch3_mux_control),
> > + SND_SOC_DAPM_MUX("HDMI_CH4_MUX", SND_SOC_NOPM, 0, 0,
> > + &hdmi_ch4_mux_control),
> > + SND_SOC_DAPM_MUX("HDMI_CH5_MUX", SND_SOC_NOPM, 0, 0,
> > + &hdmi_ch5_mux_control),
> > + SND_SOC_DAPM_MUX("HDMI_CH6_MUX", SND_SOC_NOPM, 0, 0,
> > + &hdmi_ch6_mux_control),
> > + SND_SOC_DAPM_MUX("HDMI_CH7_MUX", SND_SOC_NOPM, 0, 0,
> > + &hdmi_ch7_mux_control),
>
> I don't think we really need these widgets. These select which
> channel
> from the input map to which channel on the output. However AFAIK DAPM
> doesn't really do multichannel tracking. Also since at least one
> channel
> is getting passed through, the route is always connected and there
> really isn't anything for DAPM to manage. Having simple kcontrols
> should be enough.
>
> > + SND_SOC_DAPM_MUX("HDMI_OUT_MUX", SND_SOC_NOPM, 0, 0,
>
> This should be named "HDMI_OUT Playback Route". This changes the
> kcontrol
> name seen in userspace. AFAICT, for muxes and demuxes the userspace
> kcontrol
> name comes from the widget, not the underlying kcontrol definition.
yes, it has been modified.
>
> > + &hdmi_out_mux_control),
> > + SND_SOC_DAPM_MUX("DPTX_OUT_MUX", SND_SOC_NOPM, 0, 0,
>
> This one "DPTX_OUT Playback Route".
>
> > + &dptx_out_mux_control),
> > +
> > + SND_SOC_DAPM_SUPPLY_S("TDM_BCK", SUPPLY_SEQ_TDM_BCK_EN,
> > + SND_SOC_NOPM, 0, 0,
> > + mtk_tdm_bck_en_event,
> > + SND_SOC_DAPM_PRE_PMU |
> > SND_SOC_DAPM_POST_PMD),
> > +
> > + SND_SOC_DAPM_SUPPLY_S("TDM_MCK", SUPPLY_SEQ_TDM_MCK_EN,
> > + SND_SOC_NOPM, 0, 0,
> > + mtk_tdm_mck_en_event,
> > + SND_SOC_DAPM_PRE_PMU |
> > SND_SOC_DAPM_POST_PMD),
> > +
> > + SND_SOC_DAPM_SUPPLY_S("TDM_DPTX_BCK",
> > SUPPLY_SEQ_TDM_DPTX_BCK_EN,
> > + SND_SOC_NOPM, 0, 0,
> > + mtk_tdm_bck_en_event,
> > + SND_SOC_DAPM_PRE_PMU |
> > SND_SOC_DAPM_POST_PMD),
> > +
> > + SND_SOC_DAPM_SUPPLY_S("TDM_DPTX_MCK",
> > SUPPLY_SEQ_TDM_DPTX_MCK_EN,
> > + SND_SOC_NOPM, 0, 0,
> > + mtk_tdm_mck_en_event,
> > + SND_SOC_DAPM_PRE_PMU |
> > SND_SOC_DAPM_POST_PMD),
> > +
> > + /* cg */
> > + SND_SOC_DAPM_SUPPLY_S("TDM_CG", SUPPLY_SEQ_TDM_CG_EN,
> > + AUDIO_TOP_CON2, PDN_TDM_OUT_SFT, 1,
> > + NULL, 0),
> > +
> > + SND_SOC_DAPM_MUX("DPTX_VIRTUAL_OUT_MUX",
> > + SND_SOC_NOPM, 0, 0,
> > &dptx_virtual_out_mux_control),
>
> "DPTX_VIRTUAL_OUT Playback Route"
>
> > + SND_SOC_DAPM_OUTPUT("DPTX_VIRTUAL_OUT"),
> > +};
> > +
> > +static int mtk_afe_tdm_apll_connect(struct snd_soc_dapm_widget
> > *source,
> > + struct snd_soc_dapm_widget
> > *sink)
> > +{
> > + struct snd_soc_dapm_widget *w = sink;
> > + struct snd_soc_component *cmpnt =
> > snd_soc_dapm_to_component(w->dapm);
>
> Just combine the two? Having the placeholder `w` doesn't help
> readability.
> Instead you could just slightly go over the 80 character limit here.
>
> > + struct mtk_base_afe *afe =
> > snd_soc_component_get_drvdata(cmpnt);
> > + struct mt8196_afe_private *afe_priv = afe->platform_priv;
> > + int dai_id = get_tdm_id_by_name(w->name);
> > + struct mtk_afe_tdm_priv *tdm_priv = afe_priv-
> > >dai_priv[dai_id];
> > + int cur_apll;
> > +
> > + /* which apll */
> > + cur_apll = mt8196_get_apll_by_name(afe, source->name);
> > +
> > + return (tdm_priv->mclk_apll == cur_apll) ? 1 : 0;
> > +}
> > +
> > +static const struct snd_soc_dapm_route mtk_dai_tdm_routes[] = {
> > + {"HDMI_CH0_MUX", "CH0", "HDMI"},
> > + {"HDMI_CH0_MUX", "CH1", "HDMI"},
> > + {"HDMI_CH0_MUX", "CH2", "HDMI"},
> > + {"HDMI_CH0_MUX", "CH3", "HDMI"},
> > + {"HDMI_CH0_MUX", "CH4", "HDMI"},
> > + {"HDMI_CH0_MUX", "CH5", "HDMI"},
> > + {"HDMI_CH0_MUX", "CH6", "HDMI"},
> > + {"HDMI_CH0_MUX", "CH7", "HDMI"},
> > +
> > + {"HDMI_CH1_MUX", "CH0", "HDMI"},
> > + {"HDMI_CH1_MUX", "CH1", "HDMI"},
> > + {"HDMI_CH1_MUX", "CH2", "HDMI"},
> > + {"HDMI_CH1_MUX", "CH3", "HDMI"},
> > + {"HDMI_CH1_MUX", "CH4", "HDMI"},
> > + {"HDMI_CH1_MUX", "CH5", "HDMI"},
> > + {"HDMI_CH1_MUX", "CH6", "HDMI"},
> > + {"HDMI_CH1_MUX", "CH7", "HDMI"},
> > +
> > + {"HDMI_CH2_MUX", "CH0", "HDMI"},
> > + {"HDMI_CH2_MUX", "CH1", "HDMI"},
> > + {"HDMI_CH2_MUX", "CH2", "HDMI"},
> > + {"HDMI_CH2_MUX", "CH3", "HDMI"},
> > + {"HDMI_CH2_MUX", "CH4", "HDMI"},
> > + {"HDMI_CH2_MUX", "CH5", "HDMI"},
> > + {"HDMI_CH2_MUX", "CH6", "HDMI"},
> > + {"HDMI_CH2_MUX", "CH7", "HDMI"},
> > +
> > + {"HDMI_CH3_MUX", "CH0", "HDMI"},
> > + {"HDMI_CH3_MUX", "CH1", "HDMI"},
> > + {"HDMI_CH3_MUX", "CH2", "HDMI"},
> > + {"HDMI_CH3_MUX", "CH3", "HDMI"},
> > + {"HDMI_CH3_MUX", "CH4", "HDMI"},
> > + {"HDMI_CH3_MUX", "CH5", "HDMI"},
> > + {"HDMI_CH3_MUX", "CH6", "HDMI"},
> > + {"HDMI_CH3_MUX", "CH7", "HDMI"},
> > +
> > + {"HDMI_CH4_MUX", "CH0", "HDMI"},
> > + {"HDMI_CH4_MUX", "CH1", "HDMI"},
> > + {"HDMI_CH4_MUX", "CH2", "HDMI"},
> > + {"HDMI_CH4_MUX", "CH3", "HDMI"},
> > + {"HDMI_CH4_MUX", "CH4", "HDMI"},
> > + {"HDMI_CH4_MUX", "CH5", "HDMI"},
> > + {"HDMI_CH4_MUX", "CH6", "HDMI"},
> > + {"HDMI_CH4_MUX", "CH7", "HDMI"},
> > +
> > + {"HDMI_CH5_MUX", "CH0", "HDMI"},
> > + {"HDMI_CH5_MUX", "CH1", "HDMI"},
> > + {"HDMI_CH5_MUX", "CH2", "HDMI"},
> > + {"HDMI_CH5_MUX", "CH3", "HDMI"},
> > + {"HDMI_CH5_MUX", "CH4", "HDMI"},
> > + {"HDMI_CH5_MUX", "CH5", "HDMI"},
> > + {"HDMI_CH5_MUX", "CH6", "HDMI"},
> > + {"HDMI_CH5_MUX", "CH7", "HDMI"},
> > +
> > + {"HDMI_CH6_MUX", "CH0", "HDMI"},
> > + {"HDMI_CH6_MUX", "CH1", "HDMI"},
> > + {"HDMI_CH6_MUX", "CH2", "HDMI"},
> > + {"HDMI_CH6_MUX", "CH3", "HDMI"},
> > + {"HDMI_CH6_MUX", "CH4", "HDMI"},
> > + {"HDMI_CH6_MUX", "CH5", "HDMI"},
> > + {"HDMI_CH6_MUX", "CH6", "HDMI"},
> > + {"HDMI_CH6_MUX", "CH7", "HDMI"},
> > +
> > + {"HDMI_CH7_MUX", "CH0", "HDMI"},
> > + {"HDMI_CH7_MUX", "CH1", "HDMI"},
> > + {"HDMI_CH7_MUX", "CH2", "HDMI"},
> > + {"HDMI_CH7_MUX", "CH3", "HDMI"},
> > + {"HDMI_CH7_MUX", "CH4", "HDMI"},
> > + {"HDMI_CH7_MUX", "CH5", "HDMI"},
> > + {"HDMI_CH7_MUX", "CH6", "HDMI"},
> > + {"HDMI_CH7_MUX", "CH7", "HDMI"},
> > +
> > + {"HDMI_OUT_MUX", "Connect", "HDMI_CH0_MUX"},
> > + {"HDMI_OUT_MUX", "Connect", "HDMI_CH1_MUX"},
> > + {"HDMI_OUT_MUX", "Connect", "HDMI_CH2_MUX"},
> > + {"HDMI_OUT_MUX", "Connect", "HDMI_CH3_MUX"},
> > + {"HDMI_OUT_MUX", "Connect", "HDMI_CH4_MUX"},
> > + {"HDMI_OUT_MUX", "Connect", "HDMI_CH5_MUX"},
> > + {"HDMI_OUT_MUX", "Connect", "HDMI_CH6_MUX"},
> > + {"HDMI_OUT_MUX", "Connect", "HDMI_CH7_MUX"},
> > +
> > + {"DPTX_OUT_MUX", "Connect", "HDMI_CH0_MUX"},
> > + {"DPTX_OUT_MUX", "Connect", "HDMI_CH1_MUX"},
> > + {"DPTX_OUT_MUX", "Connect", "HDMI_CH2_MUX"},
> > + {"DPTX_OUT_MUX", "Connect", "HDMI_CH3_MUX"},
> > + {"DPTX_OUT_MUX", "Connect", "HDMI_CH4_MUX"},
> > + {"DPTX_OUT_MUX", "Connect", "HDMI_CH5_MUX"},
> > + {"DPTX_OUT_MUX", "Connect", "HDMI_CH6_MUX"},
> > + {"DPTX_OUT_MUX", "Connect", "HDMI_CH7_MUX"},
>
> As mentioned above the channel mux widgets don't really do anything.
> Just use normal kcontrols and the aboue routes can be simplified to
> just
>
> { "HDMI_OUT_MUX", "Connect", "HDMI" }
> { "DPTX_OUT_MUX", "Connect", "HDMI" },
>
> Also note that there should be one space between the braces ("{}")
> and
> the elements.
>
> > + {"TDM", NULL, "HDMI_OUT_MUX"},
> > + {"TDM", NULL, "TDM_BCK"},
> > + {"TDM", NULL, "TDM_CG"},
> > +
> > + {"TDM_DPTX", NULL, "DPTX_OUT_MUX"},
> > + {"TDM_DPTX", NULL, "TDM_DPTX_BCK"},
> > + {"TDM_DPTX", NULL, "TDM_CG"},
> > +
> > + {"TDM_BCK", NULL, "TDM_MCK"},
> > + {"TDM_DPTX_BCK", NULL, "TDM_DPTX_MCK"},
> > + {"TDM_MCK", NULL, APLL1_W_NAME, mtk_afe_tdm_apll_connect},
> > + {"TDM_MCK", NULL, APLL2_W_NAME, mtk_afe_tdm_apll_connect},
> > + {"TDM_DPTX_MCK", NULL, APLL1_W_NAME,
> > mtk_afe_tdm_apll_connect},
> > + {"TDM_DPTX_MCK", NULL, APLL2_W_NAME,
> > mtk_afe_tdm_apll_connect},
> > +
> > + {"DPTX_VIRTUAL_OUT_MUX", "Connect", "TDM_DPTX"},
> > + {"DPTX_VIRTUAL_OUT", NULL, "DPTX_VIRTUAL_OUT_MUX"},
> > +};
> > +
> > +/* dai ops */
> > +static int mtk_dai_tdm_cal_mclk(struct mtk_base_afe *afe,
> > + struct mtk_afe_tdm_priv *tdm_priv,
> > + int freq)
> > +{
> > + int apll;
> > + int apll_rate;
> > +
> > + apll = mt8196_get_apll_by_rate(afe, freq);
> > + apll_rate = mt8196_get_apll_rate(afe, apll);
> > +
> > + if (freq > apll_rate)
> > + return -EINVAL;
> > +
> > + if (apll_rate % freq != 0)
> > + return -EINVAL;
> > +
> > + tdm_priv->mclk_rate = freq;
> > + tdm_priv->mclk_apll = apll;
> > +
> > + return 0;
> > +}
> > +
> > +static int mtk_dai_tdm_hw_params(struct snd_pcm_substream
> > *substream,
> > + struct snd_pcm_hw_params *params,
> > + struct snd_soc_dai *dai)
> > +{
> > + struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
> > + struct mt8196_afe_private *afe_priv = afe->platform_priv;
> > + int tdm_id = dai->id;
> > + struct mtk_afe_tdm_priv *tdm_priv;
> > + unsigned int rate = params_rate(params);
> > + unsigned int channels = params_channels(params);
> > + snd_pcm_format_t format = params_format(params);
> > + unsigned int tdm_con = 0;
> > +
> > + if (tdm_id >= MT8196_DAI_NUM || tdm_id < 0)
> > + return -EINVAL;
> > +
> > + tdm_priv = afe_priv->dai_priv[tdm_id];
> > +
> > + if (!tdm_priv)
> > + return -EINVAL;
> > +
> > + /* calculate mclk_rate, if not set explicitly */
> > + if (!tdm_priv->mclk_rate) {
> > + tdm_priv->mclk_rate = rate * tdm_priv-
> > >mclk_multiple;
> > + mtk_dai_tdm_cal_mclk(afe,
> > + tdm_priv,
> > + tdm_priv->mclk_rate);
> > + }
> > +
> > + /* calculate bck */
> > + tdm_priv->bck_rate = rate *
> > + channels *
> > + snd_pcm_format_physical_width(format);
> > +
> > + if (tdm_priv->bck_rate > tdm_priv->mclk_rate)
> > + return -EINVAL;
> > +
> > + if (tdm_priv->mclk_rate % tdm_priv->bck_rate != 0)
> > + return -EINVAL;
> > +
> > + dev_info(afe->dev, "id %d, rate %d, channels %d, format %d,
> > mclk_rate %d, bck_rate %d\n",
> > + tdm_id, rate, channels, format,
> > + tdm_priv->mclk_rate, tdm_priv->bck_rate);
>
> Please make this debug level.
>
> > +
> > + /* set tdm */
> > + tdm_con = 0 << BCK_INVERSE_SFT;
> > + tdm_con |= 0 << LRCK_INVERSE_SFT;
> > + tdm_con |= 0 << DELAY_DATA_SFT;
> > + tdm_con |= 1 << LEFT_ALIGN_SFT;
> > + tdm_con |= get_tdm_wlen(format) << WLEN_SFT;
> > + tdm_con |= get_tdm_ch(channels) << CHANNEL_NUM_SFT;
> > + tdm_con |= get_tdm_channel_bck(format) <<
> > CHANNEL_BCK_CYCLES_SFT;
> > + tdm_con |= get_tdm_lrck_width(format) <<
> > LRCK_TDM_WIDTH_SFT;
> > + regmap_write(afe->regmap, AFE_TDM_CON1, tdm_con);
> > +
> > + /* set dptx */
> > + if (tdm_id == MT8196_DAI_TDM_DPTX) {
> > + regmap_update_bits(afe->regmap, AFE_DPTX_CON,
> > + DPTX_CHANNEL_ENABLE_MASK_SFT,
> > + get_dptx_ch_enable_mask(channels
> > ) <<
> > + DPTX_CHANNEL_ENABLE_SFT);
> > + regmap_update_bits(afe->regmap, AFE_DPTX_CON,
> > + DPTX_CHANNEL_NUMBER_MASK_SFT,
> > + get_dptx_ch(channels) <<
> > + DPTX_CHANNEL_NUMBER_SFT);
> > + regmap_update_bits(afe->regmap, AFE_DPTX_CON,
> > + DPTX_16BIT_MASK_SFT,
> > + get_dptx_wlen(format) <<
> > DPTX_16BIT_SFT);
> > + }
>
>
> > + switch (channels) {
> > + case 1:
> > + case 2:
> > + tdm_con = TDM_CH_START_O30_O31 <<
> > ST_CH_PAIR_SOUT0_SFT;
> > + tdm_con |= TDM_CH_ZERO <<
> > ST_CH_PAIR_SOUT1_SFT;
> > + tdm_con |= TDM_CH_ZERO <<
> > ST_CH_PAIR_SOUT2_SFT;
> > + tdm_con |= TDM_CH_ZERO <<
> > ST_CH_PAIR_SOUT3_SFT;
> > + break;
> > + case 3:
> > + case 4:
> > + tdm_con = TDM_CH_START_O30_O31 <<
> > ST_CH_PAIR_SOUT0_SFT;
> > + tdm_con |= TDM_CH_START_O32_O33 <<
> > ST_CH_PAIR_SOUT1_SFT;
> > + tdm_con |= TDM_CH_ZERO <<
> > ST_CH_PAIR_SOUT2_SFT;
> > + tdm_con |= TDM_CH_ZERO <<
> > ST_CH_PAIR_SOUT3_SFT;
> > + break;
> > + case 5:
> > + case 6:
> > + tdm_con = TDM_CH_START_O30_O31 <<
> > ST_CH_PAIR_SOUT0_SFT;
> > + tdm_con |= TDM_CH_START_O32_O33 <<
> > ST_CH_PAIR_SOUT1_SFT;
> > + tdm_con |= TDM_CH_START_O34_O35 <<
> > ST_CH_PAIR_SOUT2_SFT;
> > + tdm_con |= TDM_CH_ZERO <<
> > ST_CH_PAIR_SOUT3_SFT;
> > + break;
> > + case 7:
> > + case 8:
> > + tdm_con = TDM_CH_START_O30_O31 <<
> > ST_CH_PAIR_SOUT0_SFT;
> > + tdm_con |= TDM_CH_START_O32_O33 <<
> > ST_CH_PAIR_SOUT1_SFT;
> > + tdm_con |= TDM_CH_START_O34_O35 <<
> > ST_CH_PAIR_SOUT2_SFT;
> > + tdm_con |= TDM_CH_START_O36_O37 <<
> > ST_CH_PAIR_SOUT3_SFT;
> > + break;
> > + default:
> > + tdm_con = 0;
> > + }
>
> The indentation for this block is incorrect.
>
> > + regmap_write(afe->regmap, AFE_TDM_CON2, tdm_con);
> > + regmap_update_bits(afe->regmap, AFE_HDMI_OUT_CON0,
> > + HDMI_CH_NUM_MASK_SFT,
> > + channels << HDMI_CH_NUM_SFT);
> > +
> > + return 0;
> > +}
> > +
> > +static int mtk_dai_tdm_trigger(struct snd_pcm_substream
> > *substream,
> > + int cmd,
> > + struct snd_soc_dai *dai)
> > +{
> > + struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
> > + int tdm_id = dai->id;
> > +
> > + dev_dbg(afe->dev, "cmd %d, tdm_id %d\n", cmd, tdm_id);
> > +
> > + switch (cmd) {
> > + case SNDRV_PCM_TRIGGER_START:
> > + case SNDRV_PCM_TRIGGER_RESUME:
> > + /* enable Out control */
> > + regmap_update_bits(afe->regmap, AFE_HDMI_OUT_CON0,
> > + HDMI_OUT_ON_MASK_SFT,
> > + 0x1 << HDMI_OUT_ON_SFT);
>
> This is already controlled from the "HDMI" PCM component driver.
> The DAI driver should not touch it.
>
> > +
> > + /* enable dptx */
> > + if (tdm_id == MT8196_DAI_TDM_DPTX) {
> > + regmap_update_bits(afe->regmap,
> > AFE_DPTX_CON,
> > + DPTX_ON_MASK_SFT, 0x1 <<
> > + DPTX_ON_SFT);
> > + }
> > +
> > + /* enable tdm */
> > + regmap_update_bits(afe->regmap, AFE_TDM_CON1,
> > + TDM_EN_MASK_SFT, 0x1 <<
> > TDM_EN_SFT);
> > + break;
> > + case SNDRV_PCM_TRIGGER_STOP:
> > + case SNDRV_PCM_TRIGGER_SUSPEND:
> > + /* disable tdm */
> > + regmap_update_bits(afe->regmap, AFE_TDM_CON1,
> > + TDM_EN_MASK_SFT, 0);
> > +
> > + /* disable dptx */
> > + if (tdm_id == MT8196_DAI_TDM_DPTX) {
> > + regmap_update_bits(afe->regmap,
> > AFE_DPTX_CON,
> > + DPTX_ON_MASK_SFT, 0);
> > + }
> > +
> > + /* disable Out control */
> > + regmap_update_bits(afe->regmap, AFE_HDMI_OUT_CON0,
> > + HDMI_OUT_ON_MASK_SFT, 0);
> > + break;
> > + default:
> > + return -EINVAL;
> > + }
> > +
> > + return 0;
> > +}
> > +
> > +static int mtk_dai_tdm_set_sysclk(struct snd_soc_dai *dai,
> > + int clk_id, unsigned int freq,
> > int dir)
> > +{
> > + struct mtk_base_afe *afe = dev_get_drvdata(dai->dev);
> > + struct mt8196_afe_private *afe_priv = afe->platform_priv;
> > + struct mtk_afe_tdm_priv *tdm_priv;
> > +
> > + if (dai->id >= MT8196_DAI_NUM || dai->id < 0)
> > + return -EINVAL;
> > +
> > + tdm_priv = afe_priv->dai_priv[dai->id];
> > +
> > + if (!tdm_priv)
> > + return -EINVAL;
> > +
> > + if (dir != SND_SOC_CLOCK_OUT)
> > + return -EINVAL;
> > +
> > + dev_dbg(afe->dev, "freq %d\n", freq);
> > +
> > + return mtk_dai_tdm_cal_mclk(afe, tdm_priv, freq);
> > +}
> > +
> > +static const struct snd_soc_dai_ops mtk_dai_tdm_ops = {
> > + .hw_params = mtk_dai_tdm_hw_params,
> > + .trigger = mtk_dai_tdm_trigger,
> > + .set_sysclk = mtk_dai_tdm_set_sysclk,
> > +};
> > +
> > +/* dai driver */
> > +#define MTK_TDM_RATES (SNDRV_PCM_RATE_8000_48000 |\
> > + SNDRV_PCM_RATE_88200 |\
> > + SNDRV_PCM_RATE_96000 |\
> > + SNDRV_PCM_RATE_176400 |\
> > + SNDRV_PCM_RATE_192000)
> > +
> > +#define MTK_TDM_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\
> > + SNDRV_PCM_FMTBIT_S24_LE |\
> > + SNDRV_PCM_FMTBIT_S32_LE)
> > +
> > +static struct snd_soc_dai_driver mtk_dai_tdm_driver[] = {
> > + {
> > + .name = "TDM",
> > + .id = MT8196_DAI_TDM,
> > + .playback = {
> > + .stream_name = "TDM",
> > + .channels_min = 2,
> > + .channels_max = 8,
> > + .rates = MTK_TDM_RATES,
> > + .formats = MTK_TDM_FORMATS,
> > + },
> > + .ops = &mtk_dai_tdm_ops,
> > + },
> > + {
> > + .name = "TDM_DPTX",
> > + .id = MT8196_DAI_TDM_DPTX,
> > + .playback = {
> > + .stream_name = "TDM_DPTX",
> > + .channels_min = 2,
> > + .channels_max = 8,
> > + .rates = MTK_TDM_RATES,
> > + .formats = MTK_TDM_FORMATS,
> > + },
> > + .ops = &mtk_dai_tdm_ops,
> > + },
> > +};
> > +
> > +static struct mtk_afe_tdm_priv *init_tdm_priv_data(struct
> > mtk_base_afe *afe,
> > + int id)
> > +{
> > + struct mtk_afe_tdm_priv *tdm_priv;
> > +
> > + tdm_priv = devm_kzalloc(afe->dev, sizeof(struct
> > mtk_afe_tdm_priv),
> > + GFP_KERNEL);
> > + if (!tdm_priv)
> > + return NULL;
> > +
> > + if (id == MT8196_DAI_TDM_DPTX)
> > + tdm_priv->mclk_multiple = 256;
> > + else
> > + tdm_priv->mclk_multiple = 128;
> > +
> > + tdm_priv->bck_id = MT8196_TDMOUT_BCK;
> > + tdm_priv->mclk_id = MT8196_TDMOUT_MCK;
> > +
> > + return tdm_priv;
> > +}
> > +
> > +int mt8196_dai_tdm_register(struct mtk_base_afe *afe)
> > +{
> > + struct mt8196_afe_private *afe_priv = afe->platform_priv;
> > + struct mtk_afe_tdm_priv *tdm_priv, *tdm_dptx_priv;
> > + 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);
>
> This should be the final step in this function, after everything has
> been initialized correctly.
>
> > +
> > + dai->dai_drivers = mtk_dai_tdm_driver;
> > + dai->num_dai_drivers = ARRAY_SIZE(mtk_dai_tdm_driver);
> > +
> > + dai->dapm_widgets = mtk_dai_tdm_widgets;
> > + dai->num_dapm_widgets = ARRAY_SIZE(mtk_dai_tdm_widgets);
> > + dai->dapm_routes = mtk_dai_tdm_routes;
> > + dai->num_dapm_routes = ARRAY_SIZE(mtk_dai_tdm_routes);
> > +
> > + tdm_priv = init_tdm_priv_data(afe, MT8196_DAI_TDM);
> > + if (!tdm_priv)
> > + return -ENOMEM;
>
> Or you end up with a bad entry in the list here.
>
> > +
> > + tdm_dptx_priv = init_tdm_priv_data(afe,
> > MT8196_DAI_TDM_DPTX);
> > + if (!tdm_dptx_priv)
> > + return -ENOMEM;
>
> Or here.
>
>
> ChenYu
>
> > +
> > + afe_priv->dai_priv[MT8196_DAI_TDM] = tdm_priv;
> > + afe_priv->dai_priv[MT8196_DAI_TDM_DPTX] = tdm_dptx_priv;
> > +
> > + return 0;
> > +}
> > +
> > --
> > 2.45.2
> >
> >
^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [PATCH v6 05/10] ASoC: mediatek: mt8196: support I2S in platform driver
2025-08-11 11:03 ` [PATCH v6 05/10] ASoC: mediatek: mt8196: support I2S in platform driver Chen-Yu Tsai
@ 2025-08-21 9:10 ` Darren Ye (叶飞)
0 siblings, 0 replies; 24+ messages in thread
From: Darren Ye (叶飞) @ 2025-08-21 9:10 UTC (permalink / raw)
To: wenst@chromium.org
Cc: linux-kernel@vger.kernel.org, linux-mediatek@lists.infradead.org,
devicetree@vger.kernel.org, linus.walleij@linaro.org,
linux-sound@vger.kernel.org, linux-gpio@vger.kernel.org,
broonie@kernel.org, brgl@bgdev.pl, conor+dt@kernel.org,
tiwai@suse.com, robh@kernel.org, lgirdwood@gmail.com,
linux-arm-kernel@lists.infradead.org, matthias.bgg@gmail.com,
krzk+dt@kernel.org, perex@perex.cz, AngeloGioacchino Del Regno
On Mon, 2025-08-11 at 19:03 +0800, Chen-Yu Tsai wrote:
> External email : Please do not click links or open attachments until
> you have verified the sender or the content.
>
>
> On Tue, Jul 8, 2025 at 7:34 PM Darren.Ye <darren.ye@mediatek.com>
> wrote:
> >
> > From: Darren Ye <darren.ye@mediatek.com>
> >
> > Add mt8196 I2S DAI driver support.
> >
> > Signed-off-by: Darren Ye <darren.ye@mediatek.com>
> > ---
> > sound/soc/mediatek/mt8196/mt8196-dai-i2s.c | 3944
> > ++++++++++++++++++++
> > 1 file changed, 3944 insertions(+)
> > create mode 100644 sound/soc/mediatek/mt8196/mt8196-dai-i2s.c
> >
> > diff --git a/sound/soc/mediatek/mt8196/mt8196-dai-i2s.c
> > b/sound/soc/mediatek/mt8196/mt8196-dai-i2s.c
> > new file mode 100644
> > index 000000000000..59f66ab8fa9f
> > --- /dev/null
> > +++ b/sound/soc/mediatek/mt8196/mt8196-dai-i2s.c
> > @@ -0,0 +1,3944 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/*
> > + * MediaTek ALSA SoC Audio DAI I2S Control
> > + *
> > + * Copyright (c) 2024 MediaTek Inc.
>
> ^ update to 2025 (for all new files)
>
> > + * Author: Darren Ye <darren.ye@mediatek.com>
> > + */
> > +
> > +#include <linux/bitops.h>
> > +#include <linux/regmap.h>
> > +#include <sound/pcm_params.h>
> > +#include "mt8196-afe-clk.h"
> > +#include "mt8196-afe-common.h"
> > +#include "mt8196-interconnection.h"
> > +#include "mtk-afe-fe-dai.h"
>
> Please add one empty line between each group of headers (linux/*,
> sound/*,
> local). And please use relative paths for the local headers.
>
> > +
> > +#define ETDM_22M_CLOCK_THRES 11289600
> > +
> > +enum {
> > + ETDM_CLK_SOURCE_H26M,
> > + ETDM_CLK_SOURCE_APLL,
> > + ETDM_CLK_SOURCE_SPDIF,
> > + ETDM_CLK_SOURCE_HDMI,
> > + ETDM_CLK_SOURCE_EARC,
> > + ETDM_CLK_SOURCE_LINEIN,
> > +};
> > +
> > +enum {
> > + ETDM_RELATCH_SEL_H26M,
> > + ETDM_RELATCH_SEL_APLL,
> > +};
> > +
> > +enum {
> > + ETDM_RATE_8K,
> > + ETDM_RATE_12K,
> > + ETDM_RATE_16K,
> > + ETDM_RATE_24K,
> > + ETDM_RATE_32K,
> > + ETDM_RATE_48K,
> > + ETDM_RATE_64K,
> > + ETDM_RATE_96K,
> > + ETDM_RATE_128K,
> > + ETDM_RATE_192K,
> > + ETDM_RATE_256K,
> > + ETDM_RATE_384K,
> > + ETDM_RATE_11025 = 16,
> > + ETDM_RATE_22050,
> > + ETDM_RATE_44100,
> > + ETDM_RATE_88200,
> > + ETDM_RATE_176400,
> > + ETDM_RATE_352800,
> > +};
> > +
> > +enum {
> > + ETDM_CONN_8K,
> > + ETDM_CONN_11K,
> > + ETDM_CONN_12K,
> > + ETDM_CONN_16K = 4,
> > + ETDM_CONN_22K,
> > + ETDM_CONN_24K,
> > + ETDM_CONN_32K = 8,
> > + ETDM_CONN_44K,
> > + ETDM_CONN_48K,
> > + ETDM_CONN_88K = 13,
> > + ETDM_CONN_96K,
> > + ETDM_CONN_176K = 17,
> > + ETDM_CONN_192K,
> > + ETDM_CONN_352K = 21,
> > + ETDM_CONN_384K,
> > +};
> > +
> > +enum {
> > + ETDM_WLEN_8_BIT = 0x7,
> > + ETDM_WLEN_16_BIT = 0xf,
> > + ETDM_WLEN_32_BIT = 0x1f,
> > +};
> > +
> > +enum {
> > + ETDM_SLAVE_SEL_ETDMIN0_MASTER,
> > + ETDM_SLAVE_SEL_ETDMIN0_SLAVE,
> > + ETDM_SLAVE_SEL_ETDMIN1_MASTER,
> > + ETDM_SLAVE_SEL_ETDMIN1_SLAVE,
> > + ETDM_SLAVE_SEL_ETDMIN2_MASTER,
> > + ETDM_SLAVE_SEL_ETDMIN2_SLAVE,
> > + ETDM_SLAVE_SEL_ETDMIN3_MASTER,
> > + ETDM_SLAVE_SEL_ETDMIN3_SLAVE,
> > + ETDM_SLAVE_SEL_ETDMOUT0_MASTER,
> > + ETDM_SLAVE_SEL_ETDMOUT0_SLAVE,
> > + ETDM_SLAVE_SEL_ETDMOUT1_MASTER,
> > + ETDM_SLAVE_SEL_ETDMOUT1_SLAVE,
> > + ETDM_SLAVE_SEL_ETDMOUT2_MASTER,
> > + ETDM_SLAVE_SEL_ETDMOUT2_SLAVE,
> > + ETDM_SLAVE_SEL_ETDMOUT3_MASTER,
> > + ETDM_SLAVE_SEL_ETDMOUT3_SLAVE,
> > +};
> > +
> > +enum {
> > + ETDM_SLAVE_SEL_ETDMIN4_MASTER,
> > + ETDM_SLAVE_SEL_ETDMIN4_SLAVE,
> > + ETDM_SLAVE_SEL_ETDMIN5_MASTER,
> > + ETDM_SLAVE_SEL_ETDMIN5_SLAVE,
> > + ETDM_SLAVE_SEL_ETDMIN6_MASTER,
> > + ETDM_SLAVE_SEL_ETDMIN6_SLAVE,
> > + ETDM_SLAVE_SEL_ETDMIN7_MASTER,
> > + ETDM_SLAVE_SEL_ETDMIN7_SLAVE,
> > + ETDM_SLAVE_SEL_ETDMOUT4_MASTER,
> > + ETDM_SLAVE_SEL_ETDMOUT4_SLAVE,
> > + ETDM_SLAVE_SEL_ETDMOUT5_MASTER,
> > + ETDM_SLAVE_SEL_ETDMOUT5_SLAVE,
> > + ETDM_SLAVE_SEL_ETDMOUT6_MASTER,
> > + ETDM_SLAVE_SEL_ETDMOUT6_SLAVE,
> > + ETDM_SLAVE_SEL_ETDMOUT7_MASTER,
> > + ETDM_SLAVE_SEL_ETDMOUT7_SLAVE,
> > +};
> > +
> > +enum {
> > + MTK_DAI_ETDM_FORMAT_I2S,
> > + MTK_DAI_ETDM_FORMAT_LJ,
> > + MTK_DAI_ETDM_FORMAT_RJ,
> > + MTK_DAI_ETDM_FORMAT_EIAJ,
> > + MTK_DAI_ETDM_FORMAT_DSPA,
> > + MTK_DAI_ETDM_FORMAT_DSPB,
> > +};
> > +
> > +static unsigned int get_etdm_wlen(snd_pcm_format_t format)
> > +{
> > + return snd_pcm_format_physical_width(format) < 16 ?
> > + ETDM_WLEN_16_BIT : ETDM_WLEN_32_BIT;
> > +}
> > +
> > +static unsigned int get_etdm_lrck_width(snd_pcm_format_t format)
> > +{
> > + /* The valid data bit number should be large than 7 due to
> > hardware limitation. */
>
> ^ larger?
>
> > + return snd_pcm_format_physical_width(format) - 1;
> > +}
> > +
> > +static unsigned int get_etdm_rate(unsigned int rate)
> > +{
> > + switch (rate) {
> > + case 8000:
> > + return ETDM_RATE_8K;
> > + case 12000:
> > + return ETDM_RATE_12K;
> > + case 16000:
> > + return ETDM_RATE_16K;
> > + case 24000:
> > + return ETDM_RATE_24K;
> > + case 32000:
> > + return ETDM_RATE_32K;
> > + case 48000:
> > + return ETDM_RATE_48K;
> > + case 64000:
> > + return ETDM_RATE_64K;
> > + case 96000:
> > + return ETDM_RATE_96K;
> > + case 128000:
> > + return ETDM_RATE_128K;
> > + case 192000:
> > + return ETDM_RATE_192K;
> > + case 256000:
> > + return ETDM_RATE_256K;
> > + case 384000:
> > + return ETDM_RATE_384K;
> > + case 11025:
> > + return ETDM_RATE_11025;
> > + case 22050:
> > + return ETDM_RATE_22050;
> > + case 44100:
> > + return ETDM_RATE_44100;
> > + case 88200:
> > + return ETDM_RATE_88200;
> > + case 176400:
> > + return ETDM_RATE_176400;
> > + case 352800:
> > + return ETDM_RATE_352800;
> > + default:
> > + return 0;
> > + }
> > +}
> > +
> > +static unsigned int get_etdm_inconn_rate(unsigned int rate)
> > +{
> > + switch (rate) {
> > + case 8000:
> > + return ETDM_CONN_8K;
> > + case 12000:
> > + return ETDM_CONN_12K;
> > + case 16000:
> > + return ETDM_CONN_16K;
> > + case 24000:
> > + return ETDM_CONN_24K;
> > + case 32000:
> > + return ETDM_CONN_32K;
> > + case 48000:
> > + return ETDM_CONN_48K;
> > + case 96000:
> > + return ETDM_CONN_96K;
> > + case 192000:
> > + return ETDM_CONN_192K;
> > + case 384000:
> > + return ETDM_CONN_384K;
> > + case 11025:
> > + return ETDM_CONN_11K;
> > + case 22050:
> > + return ETDM_CONN_22K;
> > + case 44100:
> > + return ETDM_CONN_44K;
> > + case 88200:
> > + return ETDM_CONN_88K;
> > + case 176400:
> > + return ETDM_CONN_176K;
> > + case 352800:
> > + return ETDM_CONN_352K;
> > + default:
> > + return 0;
> > + }
> > +}
> > +
> > +struct mtk_afe_i2s_priv {
> > + u8 id;
> > + u32 rate; /* for determine which apll to use */
> > + int low_jitter_en;
> > + const char *share_property_name;
> > + int share_i2s_id;
> > + u32 mclk_rate;
> > + u8 mclk_id;
> > + u8 mclk_apll;
> > + u8 ch_num;
> > + u8 sync;
> > + u8 ip_mode;
> > + u8 format;
> > +};
> > +
> > +/* this enum is merely for mtk_afe_i2s_priv & mtk_base_etdm_data
> > declare */
> > +enum {
> > + DAI_I2SIN0,
> > + DAI_I2SIN1,
> > + DAI_I2SIN2,
> > + DAI_I2SIN3,
> > + DAI_I2SIN4,
> > + DAI_I2SIN6,
> > + DAI_I2SOUT0,
> > + DAI_I2SOUT1,
> > + DAI_I2SOUT2,
> > + DAI_I2SOUT3,
> > + DAI_I2SOUT4,
> > + DAI_I2SOUT6,
> > + DAI_FMI2S_MASTER,
> > + DAI_I2S_NUM,
> > +};
> > +
> > +static bool is_etdm_in_pad_top(unsigned int dai_num)
> > +{
> > + switch (dai_num) {
> > + case DAI_I2SOUT4:
> > + case DAI_I2SIN4:
> > + return true;
> > + default:
> > + return false;
> > + }
> > +}
> > +
> > +struct mtk_base_etdm_data {
> > + u16 enable_reg;
> > + u16 enable_mask;
> > + u8 enable_shift;
> > + u16 sync_reg;
> > + u16 sync_mask;
> > + u8 sync_shift;
> > + u16 ch_reg;
> > + u16 ch_mask;
> > + u8 ch_shift;
> > + u16 ip_mode_reg;
> > + u16 ip_mode_mask;
> > + u8 ip_mode_shift;
> > + u16 init_count_reg;
> > + u16 init_count_mask;
> > + u8 init_count_shift;
> > + u16 init_point_reg;
> > + u16 init_point_mask;
> > + u8 init_point_shift;
> > + u16 lrck_reset_reg;
> > + u16 lrck_reset_mask;
> > + u8 lrck_reset_shift;
> > + u16 clk_source_reg;
> > + u16 clk_source_mask;
> > + u8 clk_source_shift;
> > + u16 ck_en_sel_reg;
> > + u16 ck_en_sel_mask;
> > + u8 ck_en_sel_shift;
> > + u16 fs_timing_reg;
> > + u16 fs_timing_mask;
> > + u8 fs_timing_shift;
> > + u16 relatch_en_sel_reg;
> > + u16 relatch_en_sel_mask;
> > + u8 relatch_en_sel_shift;
> > + u16 use_afifo_reg;
> > + u16 use_afifo_mask;
> > + u8 use_afifo_shift;
> > + u16 afifo_mode_reg;
> > + u16 afifo_mode_mask;
> > + u8 afifo_mode_shift;
> > + u16 almost_end_ch_reg;
> > + u16 almost_end_ch_mask;
> > + u8 almost_end_ch_shift;
> > + u16 almost_end_bit_reg;
> > + u16 almost_end_bit_mask;
> > + u8 almost_end_bit_shift;
> > + u16 out2latch_time_reg;
> > + u16 out2latch_time_mask;
> > + u8 out2latch_time_shift;
> > + u16 tdm_mode_reg;
> > + u16 tdm_mode_mask;
> > + u8 tdm_mode_shift;
> > + u16 relatch_domain_sel_reg;
> > + u16 relatch_domain_sel_mask;
> > + u8 relatch_domain_sel_shift;
> > + u16 bit_length_reg;
> > + u16 bit_length_mask;
> > + u8 bit_length_shift;
> > + u16 word_length_reg;
> > + u16 word_length_mask;
> > + u8 word_length_shift;
> > + u16 cowork_reg;
> > + u16 cowork_mask;
> > + u8 cowork_shift;
> > + u32 cowork_val;
> > + u16 in2latch_time_reg;
> > + u16 in2latch_time_mask;
> > + u8 in2latch_time_shift;
> > + u16 pad_top_ck_en_reg;
> > + u16 pad_top_ck_en_mask;
> > + u8 pad_top_ck_en_shift;
> > + u16 master_latch_reg;
> > + u16 master_latch_mask;
> > + u8 master_latch_shift;
> > +};
> > +
> > +static const struct mtk_base_etdm_data mtk_etdm_data[DAI_I2S_NUM]
> > = {
> > + [DAI_I2SIN0] = {
> > + .enable_reg = ETDM_IN0_CON0,
> > + .enable_mask = REG_ETDM_IN_EN_MASK,
> > + .enable_shift = REG_ETDM_IN_EN_SFT,
> > + .sync_reg = ETDM_IN0_CON0,
> > + .sync_mask = REG_SYNC_MODE_MASK,
> > + .sync_shift = REG_SYNC_MODE_SFT,
> > + .ch_reg = ETDM_IN0_CON0,
> > + .ch_mask = REG_CH_NUM_MASK,
> > + .ch_shift = REG_CH_NUM_SFT,
> > + .ip_mode_reg = ETDM_IN0_CON2,
> > + .ip_mode_mask = REG_MULTI_IP_MODE_MASK,
> > + .ip_mode_shift = REG_MULTI_IP_MODE_SFT,
> > + .init_count_reg = ETDM_IN0_CON1,
> > + .init_count_mask = REG_INITIAL_COUNT_MASK,
> > + .init_count_shift = REG_INITIAL_COUNT_SFT,
> > + .init_point_reg = ETDM_IN0_CON1,
> > + .init_point_mask = REG_INITIAL_POINT_MASK,
> > + .init_point_shift = REG_INITIAL_POINT_SFT,
> > + .lrck_reset_reg = ETDM_IN0_CON1,
> > + .lrck_reset_mask = REG_LRCK_RESET_MASK,
> > + .lrck_reset_shift = REG_LRCK_RESET_SFT,
> > + .clk_source_reg = ETDM_IN0_CON2,
> > + .clk_source_mask = REG_CLOCK_SOURCE_SEL_MASK,
> > + .clk_source_shift = REG_CLOCK_SOURCE_SEL_SFT,
> > + .ck_en_sel_reg = ETDM_IN0_CON2,
> > + .ck_en_sel_mask = REG_CK_EN_SEL_AUTO_MASK,
> > + .ck_en_sel_shift = REG_CK_EN_SEL_AUTO_SFT,
> > + .fs_timing_reg = ETDM_IN0_CON3,
> > + .fs_timing_mask = REG_FS_TIMING_SEL_MASK,
> > + .fs_timing_shift = REG_FS_TIMING_SEL_SFT,
> > + .relatch_en_sel_reg = ETDM_IN0_CON4,
> > + .relatch_en_sel_mask = REG_RELATCH_1X_EN_SEL_MASK,
> > + .relatch_en_sel_shift = REG_RELATCH_1X_EN_SEL_SFT,
> > + .use_afifo_reg = ETDM_IN0_CON8,
> > + .use_afifo_mask = REG_ETDM_USE_AFIFO_MASK,
> > + .use_afifo_shift = REG_ETDM_USE_AFIFO_SFT,
> > + .afifo_mode_reg = ETDM_IN0_CON8,
> > + .afifo_mode_mask = REG_AFIFO_MODE_MASK,
> > + .afifo_mode_shift = REG_AFIFO_MODE_SFT,
> > + .almost_end_ch_reg = ETDM_IN0_CON9,
> > + .almost_end_ch_mask = REG_ALMOST_END_CH_COUNT_MASK,
> > + .almost_end_ch_shift = REG_ALMOST_END_CH_COUNT_SFT,
> > + .almost_end_bit_reg = ETDM_IN0_CON9,
> > + .almost_end_bit_mask =
> > REG_ALMOST_END_BIT_COUNT_MASK,
> > + .almost_end_bit_shift =
> > REG_ALMOST_END_BIT_COUNT_SFT,
> > + .out2latch_time_reg = ETDM_IN0_CON9,
> > + .out2latch_time_mask = REG_OUT2LATCH_TIME_MASK,
> > + .out2latch_time_shift = REG_OUT2LATCH_TIME_SFT,
> > + .tdm_mode_reg = ETDM_IN0_CON0,
> > + .tdm_mode_mask = REG_FMT_MASK,
> > + .tdm_mode_shift = REG_FMT_SFT,
> > + .relatch_domain_sel_reg = ETDM_IN0_CON0,
> > + .relatch_domain_sel_mask =
> > REG_RELATCH_1X_EN_DOMAIN_SEL_MASK,
> > + .relatch_domain_sel_shift =
> > REG_RELATCH_1X_EN_DOMAIN_SEL_SFT,
> > + .bit_length_reg = ETDM_IN0_CON0,
> > + .bit_length_mask = REG_BIT_LENGTH_MASK,
> > + .bit_length_shift = REG_BIT_LENGTH_SFT,
> > + .word_length_reg = ETDM_IN0_CON0,
> > + .word_length_mask = REG_WORD_LENGTH_MASK,
> > + .word_length_shift = REG_WORD_LENGTH_SFT,
> > + .cowork_reg = ETDM_0_3_COWORK_CON0,
> > + .cowork_mask = ETDM_IN0_SLAVE_SEL_MASK,
> > + .cowork_shift = ETDM_IN0_SLAVE_SEL_SFT,
> > + .cowork_val = ETDM_SLAVE_SEL_ETDMOUT0_MASTER,
> > + .pad_top_ck_en_reg = 0,
> > + .master_latch_reg = 0,
> > + },
>
> Could this be compressed somehow with some macro magic? This whole
> block is really long. Something like:
>
> #define MTK_ETDM_DATA(_dir, _id) \
> [DAI_I2S##_dir##_id] = {
> .enable_reg = ETDM_##_dir##_id##_CON0, \
> ...
>
> [...]
>
> Add more parameters as needed.
>
> > +
>
> Drop the extra empty line at the end of the list.
>
> > +};
> > +
> > +/* lpbk */
> > +static const u8 etdm_lpbk_idx_0[] = {
> > + 0x0, 0x8,
> > +};
> > +
> > +static const u8 etdm_lpbk_idx_1[] = {
> > + 0x2, 0xa,
> > +};
> > +
> > +static const u8 etdm_lpbk_idx_2[] = {
> > + 0x4, 0xc,
> > +};
> > +
> > +static const u8 etdm_lpbk_idx_3[] = {
> > + 0x6, 0xe,
> > +};
> > +
> > +static int etdm_lpbk_get(struct snd_kcontrol *kcontrol,
> > + struct snd_ctl_elem_value *ucontrol)
> > +{
> > + struct snd_soc_component *component =
> > + snd_soc_kcontrol_component(kcontrol);
> > + struct mtk_base_afe *afe =
> > snd_soc_component_get_drvdata(component);
> > + u32 value;
> > + u32 value_ipmode;
> > + u32 reg;
> > + u32 mask;
> > + u8 shift;
> > +
> > + if (!strcmp(kcontrol->id.name, "I2SIN0_LPBK")) {
> > + reg = ETDM_0_3_COWORK_CON1;
> > + mask = ETDM_IN0_SDATA0_SEL_MASK_SFT;
> > + shift = ETDM_IN0_SDATA0_SEL_SFT;
> > + } else if (!strcmp(kcontrol->id.name, "I2SIN1_LPBK")) {
> > + reg = ETDM_0_3_COWORK_CON1;
> > + mask = ETDM_IN1_SDATA0_SEL_MASK_SFT;
> > + shift = ETDM_IN1_SDATA0_SEL_SFT;
> > + } else if (!strcmp(kcontrol->id.name, "I2SIN2_LPBK")) {
> > + reg = ETDM_0_3_COWORK_CON3;
> > + mask = ETDM_IN2_SDATA0_SEL_MASK_SFT;
> > + shift = ETDM_IN2_SDATA0_SEL_SFT;
> > + } else if (!strcmp(kcontrol->id.name, "I2SIN3_LPBK")) {
> > + reg = ETDM_0_3_COWORK_CON3;
> > + mask = ETDM_IN3_SDATA0_SEL_MASK_SFT;
> > + shift = ETDM_IN3_SDATA0_SEL_SFT;
> > + } else if (!strcmp(kcontrol->id.name, "I2SIN4_LPBK")) {
> > + reg = ETDM_4_7_COWORK_CON1;
> > +
> > + // Get I2SIN4 multi-ip mode
> > + regmap_read(afe->regmap, ETDM_IN4_CON2,
> > &value_ipmode);
> > + value_ipmode = FIELD_GET(BIT(31), value_ipmode);
> > +
> > + if (value_ipmode) {
> > + mask = ETDM_IN4_SDATA1_15_SEL_MASK_SFT;
> > + shift = ETDM_IN4_SDATA1_15_SEL_SFT;
> > + } else {
> > + mask = ETDM_IN4_SDATA0_SEL_MASK_SFT;
> > + shift = ETDM_IN4_SDATA0_SEL_SFT;
> > + }
>
> It seems to me that only I2S4IN_LPBK needs special handling, while
> the others can just use enum map values with the standard ENUM
> control.
> That would simplify the code by a lot, and also get rid of all the
> string comparisons.
>
> > + } else if (!strcmp(kcontrol->id.name, "I2SIN6_LPBK")) {
> > + reg = ETDM_4_7_COWORK_CON3;
> > + mask = ETDM_IN6_SDATA0_SEL_MASK_SFT;
> > + shift = ETDM_IN6_SDATA0_SEL_SFT;
> > + } else {
> > + dev_warn(afe->dev, "i2s lpbk no match\n");
> > + return 0;
> > + }
> > +
> > + if (reg)
> > + regmap_read(afe->regmap, reg, &value);
> > + else
> > + value = 0;
> > +
> > + value &= mask;
> > + value >>= shift;
> > + ucontrol->value.enumerated.item[0] = value;
> > +
> > + switch (value) {
> > + case 0x8:
> > + case 0xa:
> > + case 0xc:
> > + case 0xe:
> > + ucontrol->value.enumerated.item[0] = 1;
> > + break;
> > + default:
> > + ucontrol->value.enumerated.item[0] = 0;
> > + break;
> > + }
> > +
> > + return 0;
> > +}
> > +
> > +static int etdm_lpbk_put(struct snd_kcontrol *kcontrol,
> > + struct snd_ctl_elem_value *ucontrol)
> > +{
> > + struct snd_soc_component *component =
> > snd_kcontrol_chip(kcontrol);
>
> Please use snd_soc_kcontrol_component() as seen in the previous
> function.
> They are effectively the same, but this one is part of the ASoC API.
>
> > + struct mtk_base_afe *afe =
> > snd_soc_component_get_drvdata(component);
> > + u32 value = ucontrol->value.integer.value[0];
> > + u32 value_ipmode;
> > + u32 reg;
> > + u32 val;
> > + u32 mask;
> > +
> > + if (value >= ARRAY_SIZE(etdm_lpbk_idx_0))
> > + return -EINVAL;
> > +
> > + if (!strcmp(kcontrol->id.name, "I2SIN0_LPBK")) {
> > + reg = ETDM_0_3_COWORK_CON1;
> > + mask = ETDM_IN0_SDATA0_SEL_MASK_SFT;
> > + val = etdm_lpbk_idx_0[value] <<
> > ETDM_IN0_SDATA0_SEL_SFT;
> > + } else if (!strcmp(kcontrol->id.name, "I2SIN1_LPBK")) {
> > + reg = ETDM_0_3_COWORK_CON1;
> > + mask = ETDM_IN1_SDATA0_SEL_MASK_SFT;
> > + val = etdm_lpbk_idx_1[value] <<
> > ETDM_IN1_SDATA0_SEL_SFT;
> > + } else if (!strcmp(kcontrol->id.name, "I2SIN2_LPBK")) {
> > + reg = ETDM_0_3_COWORK_CON3;
> > + mask = ETDM_IN2_SDATA0_SEL_MASK_SFT;
> > + val = etdm_lpbk_idx_2[value] <<
> > ETDM_IN2_SDATA0_SEL_SFT;
> > + } else if (!strcmp(kcontrol->id.name, "I2SIN3_LPBK")) {
> > + reg = ETDM_0_3_COWORK_CON3;
> > + mask = ETDM_IN3_SDATA0_SEL_MASK_SFT;
> > + val = etdm_lpbk_idx_3[value] <<
> > ETDM_IN3_SDATA0_SEL_SFT;
> > + } else if (!strcmp(kcontrol->id.name, "I2SIN4_LPBK")) {
> > + reg = ETDM_4_7_COWORK_CON1;
> > +
> > + // Get I2SIN4 multi-ip mode
> > + regmap_read(afe->regmap, ETDM_IN4_CON2,
> > &value_ipmode);
> > + value_ipmode = FIELD_GET(BIT(31), value_ipmode);
> > +
> > + if (!value) {
> > + mask = ETDM_IN4_SDATA1_15_SEL_MASK_SFT |
> > + ETDM_IN4_SDATA0_SEL_MASK_SFT;
> > + val = (etdm_lpbk_idx_0[value] <<
> > ETDM_IN4_SDATA1_15_SEL_SFT) |
> > + (etdm_lpbk_idx_0[value] <<
> > ETDM_IN4_SDATA0_SEL_SFT);
> > + } else if (value_ipmode) {
> > + mask = ETDM_IN4_SDATA1_15_SEL_MASK_SFT;
> > + val = etdm_lpbk_idx_0[value] <<
> > ETDM_IN4_SDATA1_15_SEL_SFT;
> > + } else {
> > + mask = ETDM_IN4_SDATA0_SEL_MASK_SFT;
> > + val = etdm_lpbk_idx_0[value] <<
> > ETDM_IN4_SDATA0_SEL_SFT;
> > + }
> > + } else {
> > + reg = ETDM_4_7_COWORK_CON3;
> > + mask = ETDM_IN6_SDATA0_SEL_MASK_SFT;
> > + val = etdm_lpbk_idx_2[value] <<
> > ETDM_IN6_SDATA0_SEL_SFT;
> > + }
> > +
> > + if (reg)
> > + regmap_update_bits(afe->regmap, reg, mask, val);
> > +
> > + return 0;
> > +}
> > +
> > +static const char *const etdm_lpbk_map[] = {
> > + "Off", "On",
> > +};
> > +
> > +static SOC_ENUM_SINGLE_EXT_DECL(etdm_lpbk_map_enum,
> > + etdm_lpbk_map);
> > +/* lpbk */
> > +
> > +/* multi-ip mode */
> > +static const int etdm_ip_mode_idx[] = {
> > + 0x0, 0x1,
> > +};
> > +
> > +static int etdm_ip_mode_get(struct snd_kcontrol *kcontrol,
> > + struct snd_ctl_elem_value *ucontrol)
> > +{
> > + struct snd_soc_component *component =
> > + snd_soc_kcontrol_component(kcontrol);
> > + struct mtk_base_afe *afe =
> > snd_soc_component_get_drvdata(component);
> > + struct mt8196_afe_private *afe_priv = afe->platform_priv;
> > + struct mtk_afe_i2s_priv *i2sin4_priv = afe_priv-
> > >dai_priv[MT8196_DAI_I2S_IN4];
> > +
> > + ucontrol->value.enumerated.item[0] = i2sin4_priv->ip_mode;
> > +
> > + return 0;
> > +}
> > +
> > +static int etdm_ip_mode_put(struct snd_kcontrol *kcontrol,
> > + struct snd_ctl_elem_value *ucontrol)
> > +{
> > + struct snd_soc_component *component =
> > snd_kcontrol_chip(kcontrol);
>
> Please use snd_soc_kcontrol_component() as seen in the previous
> function.
> They are effectively the same, but this one is part of the ASoC API.
>
> Please replace any other snd_kcontrol_chip() invocations as well.
>
> > + struct mtk_base_afe *afe =
> > snd_soc_component_get_drvdata(component);
> > + struct mt8196_afe_private *afe_priv = afe->platform_priv;
> > + struct mtk_afe_i2s_priv *i2sin4_priv = afe_priv-
> > >dai_priv[MT8196_DAI_I2S_IN4];
> > + unsigned int value = ucontrol->value.integer.value[0];
> > +
> > + if (value >= ARRAY_SIZE(etdm_ip_mode_idx))
> > + return -EINVAL;
> > +
> > + /* 0: One IP multi-channel 1: Multi-IP 2-channel */
> > + i2sin4_priv->ip_mode = etdm_ip_mode_idx[value];
> > +
> > + return 0;
> > +}
> > +
> > +static const char *const etdm_ip_mode_map[] = {
> > + "Off", "On",
> > +};
> > +
> > +static SOC_ENUM_SINGLE_EXT_DECL(etdm_ip_mode_map_enum,
> > + etdm_ip_mode_map);
>
> Put it on the same line.
>
> > +/* multi-ip mode */
> > +
> > +/* ch num */
> > +static const int etdm_ch_num_idx[] = {
> > + 0x2, 0x4, 0x6, 0x8,
> > +};
> > +
> > +static int etdm_ch_num_get(struct snd_kcontrol *kcontrol,
> > + struct snd_ctl_elem_value *ucontrol)
> > +{
> > + struct snd_soc_component *component =
> > + snd_soc_kcontrol_component(kcontrol);
> > + struct mtk_base_afe *afe =
> > snd_soc_component_get_drvdata(component);
> > + struct mt8196_afe_private *afe_priv = afe->platform_priv;
> > + struct mtk_afe_i2s_priv *i2sin4_priv = afe_priv-
> > >dai_priv[MT8196_DAI_I2S_IN4];
> > + struct mtk_afe_i2s_priv *i2sout4_priv = afe_priv-
> > >dai_priv[MT8196_DAI_I2S_OUT4];
> > + unsigned int value = 0;
> > +
> > + if (!strcmp(kcontrol->id.name, "I2SIN4_CH_NUM"))
> > + value = i2sin4_priv->ch_num;
> > + else if (!strcmp(kcontrol->id.name, "I2SOUT4_CH_NUM"))
> > + value = i2sout4_priv->ch_num;
> > +
> > + if (value == 0x2)
> > + ucontrol->value.enumerated.item[0] = 0;
> > + else if (value == 0x4)
> > + ucontrol->value.enumerated.item[0] = 1;
> > + else if (value == 0x6)
> > + ucontrol->value.enumerated.item[0] = 2;
> > + else
> > + ucontrol->value.enumerated.item[0] = 3;
>
> Replace the if-else if-else blocks with:
>
> ucontrol->value.enumerated.item[0] = (value >> 1) - 1;
>
> The internal value is controlled by the driver and never goes out of
> range.
>
> > +
> > + return 0;
> > +}
> > +
> > +static int etdm_ch_num_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 mt8196_afe_private *afe_priv = afe->platform_priv;
> > + struct mtk_afe_i2s_priv *i2sin4_priv = afe_priv-
> > >dai_priv[MT8196_DAI_I2S_IN4];
> > + struct mtk_afe_i2s_priv *i2sout4_priv = afe_priv-
> > >dai_priv[MT8196_DAI_I2S_OUT4];
> > + unsigned int value = ucontrol->value.integer.value[0];
> > +
> > + if (value >= ARRAY_SIZE(etdm_ch_num_idx))
> > + return -EINVAL;
> > +
> > + if (!strcmp(kcontrol->id.name, "I2SIN4_CH_NUM"))
> > + i2sin4_priv->ch_num = etdm_ch_num_idx[value];
>
> i2sin4_priv->ch_num = (value + 1) << 1;
>
> And then etdm_ch_num_idx can be dropped.
>
> > + else if (!strcmp(kcontrol->id.name, "I2SOUT4_CH_NUM"))
> > + i2sout4_priv->ch_num = etdm_ch_num_idx[value];
> > +
> > + return 0;
> > +}
> > +
> > +static const char *const etdm_ch_num_map[] = {
> > + "2CH", "4CH", "6CH", "8CH",
> > +};
> > +
> > +static SOC_ENUM_SINGLE_EXT_DECL(etdm_ch_num_map_enum,
> > + etdm_ch_num_map);
> > +/* ch num */
> > +
> > +enum {
> > + I2S_FMT_EIAJ = 0,
> > + I2S_FMT_I2S = 1,
> > +};
> > +
> > +enum {
> > + I2S_WLEN_16_BIT = 0,
> > + I2S_WLEN_32_BIT = 1,
> > +};
> > +
> > +enum {
> > + I2S_HD_NORMAL = 0,
> > + I2S_HD_LOW_JITTER = 1,
> > +};
> > +
> > +enum {
> > + I2S1_SEL_O28_O29 = 0,
> > + I2S1_SEL_O03_O04 = 1,
> > +};
> > +
> > +enum {
> > + I2S_IN_PAD_CONNSYS = 0,
> > + I2S_IN_PAD_IO_MUX = 1,
> > +};
> > +
> > +static unsigned int get_i2s_wlen(snd_pcm_format_t format)
> > +{
> > + return snd_pcm_format_physical_width(format) <= 16 ?
> > + I2S_WLEN_16_BIT : I2S_WLEN_32_BIT;
> > +}
> > +
> > +#define MTK_AFE_I2SIN0_KCONTROL_NAME "I2SIN0_HD_Mux"
> > +#define MTK_AFE_I2SIN1_KCONTROL_NAME "I2SIN1_HD_Mux"
> > +#define MTK_AFE_I2SIN2_KCONTROL_NAME "I2SIN2_HD_Mux"
> > +#define MTK_AFE_I2SIN4_KCONTROL_NAME "I2SIN4_HD_Mux"
> > +#define MTK_AFE_I2SIN6_KCONTROL_NAME "I2SIN6_HD_Mux"
> > +#define MTK_AFE_I2SOUT0_KCONTROL_NAME "I2SOUT0_HD_Mux"
> > +#define MTK_AFE_I2SOUT1_KCONTROL_NAME "I2SOUT1_HD_Mux"
> > +#define MTK_AFE_I2SOUT2_KCONTROL_NAME "I2SOUT2_HD_Mux"
> > +#define MTK_AFE_I2SOUT4_KCONTROL_NAME "I2SOUT4_HD_Mux"
> > +#define MTK_AFE_I2SOUT6_KCONTROL_NAME "I2SOUT6_HD_Mux"
> > +#define MTK_AFE_FMI2S_MASTER_KCONTROL_NAME "FMI2S_MASTER_HD_Mux"
> > +
> > +#define I2SIN0_HD_EN_W_NAME "I2SIN0_HD_EN"
> > +#define I2SIN1_HD_EN_W_NAME "I2SIN1_HD_EN"
> > +#define I2SIN2_HD_EN_W_NAME "I2SIN2_HD_EN"
> > +#define I2SIN3_HD_EN_W_NAME "I2SIN3_HD_EN"
> > +#define I2SIN4_HD_EN_W_NAME "I2SIN4_HD_EN"
> > +#define I2SIN6_HD_EN_W_NAME "I2SIN6_HD_EN"
> > +#define I2SOUT0_HD_EN_W_NAME "I2SOUT0_HD_EN"
> > +#define I2SOUT1_HD_EN_W_NAME "I2SOUT1_HD_EN"
> > +#define I2SOUT2_HD_EN_W_NAME "I2SOUT2_HD_EN"
> > +#define I2SOUT3_HD_EN_W_NAME "I2SOUT3_HD_EN"
> > +#define I2SOUT4_HD_EN_W_NAME "I2SOUT4_HD_EN"
> > +#define I2SOUT6_HD_EN_W_NAME "I2SOUT6_HD_EN"
> > +#define FMI2S_MASTER_HD_EN_W_NAME "FMI2S_MASTER_HD_EN"
> > +
> > +#define I2SIN0_MCLK_EN_W_NAME "I2SIN0_MCLK_EN"
> > +#define I2SIN1_MCLK_EN_W_NAME "I2SIN1_MCLK_EN"
> > +#define I2SIN2_MCLK_EN_W_NAME "I2SIN2_MCLK_EN"
> > +#define I2SIN3_MCLK_EN_W_NAME "I2SIN3_MCLK_EN"
> > +#define I2SIN4_MCLK_EN_W_NAME "I2SIN4_MCLK_EN"
> > +#define I2SIN6_MCLK_EN_W_NAME "I2SIN6_MCLK_EN"
> > +#define I2SOUT0_MCLK_EN_W_NAME "I2SOUT0_MCLK_EN"
> > +#define I2SOUT1_MCLK_EN_W_NAME "I2SOUT1_MCLK_EN"
> > +#define I2SOUT2_MCLK_EN_W_NAME "I2SOUT2_MCLK_EN"
> > +#define I2SOUT3_MCLK_EN_W_NAME "I2SOUT3_MCLK_EN"
> > +#define I2SOUT4_MCLK_EN_W_NAME "I2SOUT4_MCLK_EN"
> > +#define I2SOUT6_MCLK_EN_W_NAME "I2SOUT6_MCLK_EN"
> > +#define FMI2S_MASTER_MCLK_EN_W_NAME "FMI2S_MASTER_MCLK_EN"
> > +
> > +static int get_i2s_id_by_name(struct mtk_base_afe *afe,
> > + const char *name)
> > +{
> > + if (strncmp(name, "I2SIN0", 6) == 0)
> > + return MT8196_DAI_I2S_IN0;
> > + else if (strncmp(name, "I2SIN1", 6) == 0)
> > + return MT8196_DAI_I2S_IN1;
> > + else if (strncmp(name, "I2SIN2", 6) == 0)
> > + return MT8196_DAI_I2S_IN2;
> > + else if (strncmp(name, "I2SIN3", 6) == 0)
> > + return MT8196_DAI_I2S_IN3;
> > + else if (strncmp(name, "I2SIN4", 6) == 0)
> > + return MT8196_DAI_I2S_IN4;
> > + else if (strncmp(name, "I2SIN6", 6) == 0)
> > + return MT8196_DAI_I2S_IN6;
> > + else if (strncmp(name, "I2SOUT0", 7) == 0)
> > + return MT8196_DAI_I2S_OUT0;
> > + else if (strncmp(name, "I2SOUT1", 7) == 0)
> > + return MT8196_DAI_I2S_OUT1;
> > + else if (strncmp(name, "I2SOUT2", 7) == 0)
> > + return MT8196_DAI_I2S_OUT2;
> > + else if (strncmp(name, "I2SOUT3", 7) == 0)
> > + return MT8196_DAI_I2S_OUT3;
> > + else if (strncmp(name, "I2SOUT4", 7) == 0)
> > + return MT8196_DAI_I2S_OUT4;
> > + else if (strncmp(name, "I2SOUT6", 7) == 0)
> > + return MT8196_DAI_I2S_OUT6;
> > + else if (strncmp(name, "FMI2S_MASTER", 12) == 0)
> > + return MT8196_DAI_FM_I2S_MASTER;
> > + else
> > + return -EINVAL;
> > +}
> > +
> > +static struct mtk_afe_i2s_priv *get_i2s_priv_by_name(struct
> > mtk_base_afe *afe,
> > + const char
> > *name)
> > +{
> > + struct mt8196_afe_private *afe_priv = afe->platform_priv;
> > + int dai_id = get_i2s_id_by_name(afe, name);
> > +
> > + if (dai_id < 0)
> > + return NULL;
> > +
> > + return afe_priv->dai_priv[dai_id];
> > +}
> > +
> > +/*
> > + * bit mask for i2s low power control
> > + * such as bit0 for i2s0, bit1 for i2s1...
> > + * if set 1, means i2s low power mode
> > + * if set 0, means i2s low jitter mode
> > + * 0 for all i2s bit in default
> > + */
> > +static unsigned int i2s_low_power_mask;
> > +static int mtk_i2s_low_power_mask_get(struct snd_kcontrol
> > *kcontrol,
> > + struct snd_ctl_elem_value
> > *ucontrol)
> > +{
> > + pr_debug("%s(), mask: %x\n", __func__, i2s_low_power_mask);
> > + ucontrol->value.integer.value[0] = i2s_low_power_mask;
> > + return 0;
> > +}
> > +
> > +static int mtk_i2s_low_power_mask_set(struct snd_kcontrol
> > *kcontrol,
> > + struct snd_ctl_elem_value
> > *ucontrol)
> > +{
> > + i2s_low_power_mask = ucontrol->value.integer.value[0];
> > + return 0;
> > +}
> > +
> > +static int mtk_is_i2s_low_power(int i2s_num)
> > +{
> > + int i2s_bit_shift;
> > +
> > + i2s_bit_shift = i2s_num - MT8196_DAI_I2S_IN0;
> > + if (i2s_bit_shift < 0 || i2s_bit_shift >
> > MT8196_DAI_I2S_MAX_NUM)
> > + return 0;
> > +
> > + return (i2s_low_power_mask >> i2s_bit_shift) & 0x1;
> > +}
>
> This doesn't really do anything besides block the HD EN route
> connection.
> Is there supposed to be a matching hardware configuration? I don't
> understand the purpose of this control. If it serves no purpose, it
> might be better to just remove it.
>
> And I think controls shouldn't be just a bitmask. It's hard for users
> to
> understand what bit map to which interface.
>
> > +/* low jitter control */
> > +static const char *const mt8196_i2s_hd_str[] = {
> > + "Normal", "Low_Jitter"
> > +};
> > +
> > +static const struct soc_enum mt8196_i2s_enum[] = {
> > + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(mt8196_i2s_hd_str),
> > + mt8196_i2s_hd_str),
> > +};
> > +
> > +static int mt8196_i2s_hd_get(struct snd_kcontrol *kcontrol,
> > + struct snd_ctl_elem_value *ucontrol)
> > +{
> > + struct snd_soc_component *cmpnt =
> > snd_soc_kcontrol_component(kcontrol);
> > + struct mtk_base_afe *afe =
> > snd_soc_component_get_drvdata(cmpnt);
> > + struct mtk_afe_i2s_priv *i2s_priv;
> > +
> > + i2s_priv = get_i2s_priv_by_name(afe, kcontrol->id.name);
> > +
> > + if (!i2s_priv)
> > + return -EINVAL;
> > +
> > + ucontrol->value.integer.value[0] = i2s_priv->low_jitter_en;
> > +
> > + return 0;
> > +}
> > +
> > +static int mt8196_i2s_hd_set(struct snd_kcontrol *kcontrol,
> > + struct snd_ctl_elem_value *ucontrol)
> > +{
> > + struct snd_soc_component *cmpnt =
> > snd_soc_kcontrol_component(kcontrol);
> > + struct mtk_base_afe *afe =
> > snd_soc_component_get_drvdata(cmpnt);
> > + struct mtk_afe_i2s_priv *i2s_priv;
> > + struct soc_enum *e = (struct soc_enum *)kcontrol-
> > >private_value;
> > + int hd_en;
> > +
> > + if (ucontrol->value.enumerated.item[0] >= e->items)
> > + return -EINVAL;
> > +
> > + hd_en = ucontrol->value.integer.value[0];
> > +
> > + dev_dbg(afe->dev, "kcontrol name %s, hd_en %d\n", kcontrol-
> > >id.name, hd_en);
> > +
> > + i2s_priv = get_i2s_priv_by_name(afe, kcontrol->id.name);
> > +
> > + if (!i2s_priv)
> > + return -EINVAL;
> > +
> > + i2s_priv->low_jitter_en = hd_en;
> > +
> > + return 0;
> > +}
> > +
> > +static const struct snd_kcontrol_new mtk_dai_i2s_controls[] = {
> > + SOC_ENUM_EXT(MTK_AFE_I2SIN0_KCONTROL_NAME,
> > mt8196_i2s_enum[0],
> > + mt8196_i2s_hd_get, mt8196_i2s_hd_set),
> > + SOC_ENUM_EXT(MTK_AFE_I2SIN1_KCONTROL_NAME,
> > mt8196_i2s_enum[0],
> > + mt8196_i2s_hd_get, mt8196_i2s_hd_set),
> > + SOC_ENUM_EXT(MTK_AFE_I2SIN2_KCONTROL_NAME,
> > mt8196_i2s_enum[0],
> > + mt8196_i2s_hd_get, mt8196_i2s_hd_set),
> > + SOC_ENUM_EXT(MTK_AFE_I2SIN4_KCONTROL_NAME,
> > mt8196_i2s_enum[0],
> > + mt8196_i2s_hd_get, mt8196_i2s_hd_set),
> > + SOC_ENUM_EXT(MTK_AFE_I2SIN6_KCONTROL_NAME,
> > mt8196_i2s_enum[0],
> > + mt8196_i2s_hd_get, mt8196_i2s_hd_set),
> > + SOC_ENUM_EXT(MTK_AFE_I2SOUT0_KCONTROL_NAME,
> > mt8196_i2s_enum[0],
> > + mt8196_i2s_hd_get, mt8196_i2s_hd_set),
> > + SOC_ENUM_EXT(MTK_AFE_I2SOUT1_KCONTROL_NAME,
> > mt8196_i2s_enum[0],
> > + mt8196_i2s_hd_get, mt8196_i2s_hd_set),
> > + SOC_ENUM_EXT(MTK_AFE_I2SOUT2_KCONTROL_NAME,
> > mt8196_i2s_enum[0],
> > + mt8196_i2s_hd_get, mt8196_i2s_hd_set),
> > + SOC_ENUM_EXT(MTK_AFE_I2SOUT4_KCONTROL_NAME,
> > mt8196_i2s_enum[0],
> > + mt8196_i2s_hd_get, mt8196_i2s_hd_set),
> > + SOC_ENUM_EXT(MTK_AFE_I2SOUT6_KCONTROL_NAME,
> > mt8196_i2s_enum[0],
> > + mt8196_i2s_hd_get, mt8196_i2s_hd_set),
> > + SOC_ENUM_EXT(MTK_AFE_FMI2S_MASTER_KCONTROL_NAME,
> > mt8196_i2s_enum[0],
> > + mt8196_i2s_hd_get, mt8196_i2s_hd_set),
> > + SOC_SINGLE_EXT("i2s_low_power_mask", SND_SOC_NOPM, 0,
> > 0xffff, 0,
> > + mtk_i2s_low_power_mask_get,
> > + mtk_i2s_low_power_mask_set),
> > +
> > + SOC_ENUM_EXT("I2SIN0_LPBK", etdm_lpbk_map_enum,
> > + etdm_lpbk_get, etdm_lpbk_put),
> > + SOC_ENUM_EXT("I2SIN1_LPBK", etdm_lpbk_map_enum,
> > + etdm_lpbk_get, etdm_lpbk_put),
> > + SOC_ENUM_EXT("I2SIN2_LPBK", etdm_lpbk_map_enum,
> > + etdm_lpbk_get, etdm_lpbk_put),
> > + SOC_ENUM_EXT("I2SIN3_LPBK", etdm_lpbk_map_enum,
> > + etdm_lpbk_get, etdm_lpbk_put),
> > + SOC_ENUM_EXT("I2SIN4_LPBK", etdm_lpbk_map_enum,
> > + etdm_lpbk_get, etdm_lpbk_put),
> > + SOC_ENUM_EXT("I2SIN6_LPBK", etdm_lpbk_map_enum,
> > + etdm_lpbk_get, etdm_lpbk_put),
>
> Please use a more sane name for the control. Just call it "I2SINx
> Loopback".
>
> > + SOC_ENUM_EXT("I2SIN4_IP_MODE", etdm_ip_mode_map_enum,
>
> I2SIN4 IP mode
>
> > + etdm_ip_mode_get, etdm_ip_mode_put),
> > + SOC_ENUM_EXT("I2SIN4_CH_NUM", etdm_ch_num_map_enum,
>
> I2SIN4 # of channels
>
> > + etdm_ch_num_get, etdm_ch_num_put),
> > + SOC_ENUM_EXT("I2SOUT4_CH_NUM", etdm_ch_num_map_enum,
> > + etdm_ch_num_get, etdm_ch_num_put),
> > +};
> > +
> > +/* dai component */
> > +/* i2s virtual mux to output widget */
> > +static const char *const i2s_mux_map[] = {
> > + "Normal", "Dummy_Widget",
> > +};
>
> I think this needs some explanation about why a dummy widget actually
> exists, or why it is needed.
>
> > +
> > +static int i2s_mux_map_value[] = {
> > + 0, 1,
> > +};
> > +
> > +static SOC_VALUE_ENUM_SINGLE_AUTODISABLE_DECL(i2s_mux_map_enum,
> > + SND_SOC_NOPM,
> > + 0,
> > + 1,
> > + i2s_mux_map,
> > + i2s_mux_map_value);
> > +
> > +static const struct snd_kcontrol_new i2s_in0_mux_control =
> > + SOC_DAPM_ENUM("I2S IN0 Select", i2s_mux_map_enum);
> > +static const struct snd_kcontrol_new i2s_in1_mux_control =
> > + SOC_DAPM_ENUM("I2S IN1 Select", i2s_mux_map_enum);
> > +static const struct snd_kcontrol_new i2s_in2_mux_control =
> > + SOC_DAPM_ENUM("I2S IN2 Select", i2s_mux_map_enum);
> > +static const struct snd_kcontrol_new i2s_in3_mux_control =
> > + SOC_DAPM_ENUM("I2S IN3 Select", i2s_mux_map_enum);
> > +static const struct snd_kcontrol_new i2s_in4_mux_control =
> > + SOC_DAPM_ENUM("I2S IN4 Select", i2s_mux_map_enum);
> > +static const struct snd_kcontrol_new i2s_in6_mux_control =
> > + SOC_DAPM_ENUM("I2S IN6 Select", i2s_mux_map_enum);
> > +static const struct snd_kcontrol_new i2s_out0_mux_control =
> > + SOC_DAPM_ENUM("I2S OUT0 Select", i2s_mux_map_enum);
> > +static const struct snd_kcontrol_new i2s_out1_mux_control =
> > + SOC_DAPM_ENUM("I2S OUT1 Select", i2s_mux_map_enum);
> > +static const struct snd_kcontrol_new i2s_out2_mux_control =
> > + SOC_DAPM_ENUM("I2S OUT2 Select", i2s_mux_map_enum);
> > +static const struct snd_kcontrol_new i2s_out3_mux_control =
> > + SOC_DAPM_ENUM("I2S OUT3 Select", i2s_mux_map_enum);
> > +static const struct snd_kcontrol_new i2s_out4_mux_control =
> > + SOC_DAPM_ENUM("I2S OUT4 Select", i2s_mux_map_enum);
> > +static const struct snd_kcontrol_new i2s_out6_mux_control =
> > + SOC_DAPM_ENUM("I2S OUT6 Select", i2s_mux_map_enum);
> > +
> > +/* interconnection */
> > +static const struct snd_kcontrol_new mtk_i2sout0_ch1_mix[] = {
> > + SOC_DAPM_SINGLE_AUTODISABLE("DL0_CH1", AFE_CONN108_1,
> > I_DL0_CH1, 1, 0),
>
> I think there should be some explanation on why these are
> "AUTODISABLE".
>
> [...]
>
> > +enum {
> > + SUPPLY_SEQ_APLL,
> > + SUPPLY_SEQ_I2S_MCLK_EN,
> > + SUPPLY_SEQ_I2S_CG_EN,
> > + SUPPLY_SEQ_I2S_HD_EN,
> > + SUPPLY_SEQ_I2S_EN,
>
> I'm not sure why explicit sequencing is needed. The DAPM widgets are
> connected in a directed graph, and widgets feeding other widgets are
> enabled before the ones that are fed.
>
> If the APLL widget feeds the MCLK widget, and the MCLK widget feeds
> the I2S widget, then the APLL is enabled first, then the MCLK, then
> the I2S.
>
> > +};
> > +
> > +static int mtk_i2s_hd_en_event(struct snd_soc_dapm_widget *w,
> > + struct snd_kcontrol *kcontrol,
> > + int event)
> > +{
> > + struct snd_soc_component *cmpnt =
> > snd_soc_dapm_to_component(w->dapm);
> > +
> > + dev_dbg(cmpnt->dev, "name %s, event 0x%x\n", w->name,
> > event);
> > +
> > + return 0;
> > +}
>
> This doesn't do anything. Please drop it.
>
> > +
> > +static int mtk_apll_event(struct snd_soc_dapm_widget *w,
> > + struct snd_kcontrol *kcontrol,
> > + int event)
> > +{
> > + struct snd_soc_component *cmpnt =
> > snd_soc_dapm_to_component(w->dapm);
> > + struct mtk_base_afe *afe =
> > snd_soc_component_get_drvdata(cmpnt);
> > +
> > + dev_dbg(cmpnt->dev, "name %s, event 0x%x\n", w->name,
> > event);
> > +
> > + switch (event) {
> > + case SND_SOC_DAPM_PRE_PMU:
> > + if (strcmp(w->name, APLL1_W_NAME) == 0)
> > + mt8196_apll1_enable(afe);
> > + else
> > + mt8196_apll2_enable(afe);
> > + break;
> > + case SND_SOC_DAPM_POST_PMD:
> > + if (strcmp(w->name, APLL1_W_NAME) == 0)
> > + mt8196_apll1_disable(afe);
> > + else
> > + mt8196_apll2_disable(afe);
> > + break;
> > + default:
> > + break;
> > + }
> > +
> > + return 0;
> > +}
> > +
> > +static int mtk_mclk_en_event(struct snd_soc_dapm_widget *w,
> > + struct snd_kcontrol *kcontrol,
> > + int event)
> > +{
> > + struct snd_soc_component *cmpnt =
> > snd_soc_dapm_to_component(w->dapm);
> > + struct mtk_base_afe *afe =
> > snd_soc_component_get_drvdata(cmpnt);
> > + struct mtk_afe_i2s_priv *i2s_priv;
> > +
> > + dev_dbg(cmpnt->dev, "name %s, event 0x%x\n", w->name,
> > event);
> > +
> > + i2s_priv = get_i2s_priv_by_name(afe, w->name);
> > +
> > + if (!i2s_priv)
> > + return -EINVAL;
> > +
> > + switch (event) {
> > + case SND_SOC_DAPM_PRE_PMU:
> > + mt8196_mck_enable(afe, i2s_priv->mclk_id, i2s_priv-
> > >mclk_rate);
> > + break;
> > + case SND_SOC_DAPM_POST_PMD:
> > + i2s_priv->mclk_rate = 0;
>
> I think this shouldn't be reset here. It should be done only in
> .hwparams
> and maybe .close or .shutdown callbacks. I believe it's actually
> possible
> to toggle the interface (and thus the mclk) on and off while the
> stream
> is running.
>
> > + mt8196_mck_disable(afe, i2s_priv->mclk_id);
> > + break;
> > + default:
> > + break;
> > + }
> > +
> > + return 0;
> > +}
> > +
> > +static const struct snd_soc_dapm_widget mtk_dai_i2s_widgets[] = {
>
> [...]
>
> > +
> > + /* i2s hd en */
> > + SND_SOC_DAPM_SUPPLY_S(I2SIN0_HD_EN_W_NAME,
> > SUPPLY_SEQ_I2S_HD_EN,
> > + SND_SOC_NOPM, 0, 0,
> > + mtk_i2s_hd_en_event,
> > + SND_SOC_DAPM_PRE_PMU |
> > SND_SOC_DAPM_POST_PMD),
>
> As mentioned above, please drop the no-op event. Same for the other
> *_HD_EN
> widgets below.
>
> [...]
>
> > + /* allow i2s on without codec on */
> > + SND_SOC_DAPM_OUTPUT("I2S_DUMMY_OUT"),
> > + SND_SOC_DAPM_MUX("I2S_OUT0_Mux",
> > + SND_SOC_NOPM, 0, 0,
> > &i2s_out0_mux_control),
> > + SND_SOC_DAPM_MUX("I2S_OUT1_Mux",
> > + SND_SOC_NOPM, 0, 0,
> > &i2s_out1_mux_control),
> > + SND_SOC_DAPM_MUX("I2S_OUT2_Mux",
> > + SND_SOC_NOPM, 0, 0,
> > &i2s_out2_mux_control),
> > + SND_SOC_DAPM_MUX("I2S_OUT3_Mux",
> > + SND_SOC_NOPM, 0, 0,
> > &i2s_out3_mux_control),
> > + SND_SOC_DAPM_MUX("I2S_OUT4_Mux",
> > + SND_SOC_NOPM, 0, 0,
> > &i2s_out4_mux_control),
> > + SND_SOC_DAPM_MUX("I2S_OUT6_Mux",
> > + SND_SOC_NOPM, 0, 0,
> > &i2s_out6_mux_control),
> > +
> > + SND_SOC_DAPM_INPUT("I2S_DUMMY_IN"),
> > + SND_SOC_DAPM_MUX("I2S_IN0_Mux",
> > + SND_SOC_NOPM, 0, 0, &i2s_in0_mux_control),
> > + SND_SOC_DAPM_MUX("I2S_IN1_Mux",
> > + SND_SOC_NOPM, 0, 0, &i2s_in1_mux_control),
> > + SND_SOC_DAPM_MUX("I2S_IN2_Mux",
> > + SND_SOC_NOPM, 0, 0, &i2s_in2_mux_control),
> > + SND_SOC_DAPM_MUX("I2S_IN3_Mux",
> > + SND_SOC_NOPM, 0, 0, &i2s_in3_mux_control),
> > + SND_SOC_DAPM_MUX("I2S_IN4_Mux",
> > + SND_SOC_NOPM, 0, 0, &i2s_in4_mux_control),
> > + SND_SOC_DAPM_MUX("I2S_IN6_Mux",
> > + SND_SOC_NOPM, 0, 0, &i2s_in6_mux_control),
>
> Same question about dummy widgets.
>
> > + SND_SOC_DAPM_MIXER("SOF_DMA_DL_24CH", SND_SOC_NOPM, 0, 0,
> > NULL, 0),
> > + SND_SOC_DAPM_MIXER("SOF_DMA_DL1", SND_SOC_NOPM, 0, 0, NULL,
> > 0),
>
> SOF widgets belong in the card/machine driver.
>
> > +};
>
> [...]
>
> > +static const struct snd_soc_dapm_route mtk_dai_i2s_routes[] = {
>
> [...]
>
> > + /* SOF Downlink */
> > + {"I2SOUT4_CH1", "DL_24CH_CH1", "SOF_DMA_DL_24CH"},
> > + {"I2SOUT4_CH2", "DL_24CH_CH2", "SOF_DMA_DL_24CH"},
> > + {"I2SOUT4_CH3", "DL_24CH_CH3", "SOF_DMA_DL_24CH"},
> > + {"I2SOUT4_CH4", "DL_24CH_CH4", "SOF_DMA_DL_24CH"},
>
> SOF widgets and routes belong in the card driver.
Debugging found that the widget on the machine driver cannot use the
parameter with kcontrol because the widget domain is its platform
driver and thus is not applicable in the machine driver.
>
> [...]
>
> > + /* SOF Downlink */
> > + {"I2SOUT6_CH1", "DL1_CH1", "SOF_DMA_DL1"},
> > + {"I2SOUT6_CH2", "DL1_CH2", "SOF_DMA_DL1"},
> > + {"I2SOUT6_CH1", "DL_24CH_CH1", "SOF_DMA_DL_24CH"},
> > + {"I2SOUT6_CH2", "DL_24CH_CH2", "SOF_DMA_DL_24CH"},
>
> Same comment about SOF.
>
> [...]
>
> > + /* allow i2s on without codec on */
> > + {"I2SIN0", NULL, "I2S_IN0_Mux"},
> > + {"I2S_IN0_Mux", "Dummy_Widget", "I2S_DUMMY_IN"},
> > +
> > + {"I2SIN1", NULL, "I2S_IN1_Mux"},
> > + {"I2S_IN1_Mux", "Dummy_Widget", "I2S_DUMMY_IN"},
> > +
> > + {"I2SIN2", NULL, "I2S_IN2_Mux"},
> > + {"I2S_IN2_Mux", "Dummy_Widget", "I2S_DUMMY_IN"},
> > +
> > + {"I2SIN3", NULL, "I2S_IN3_Mux"},
> > + {"I2S_IN3_Mux", "Dummy_Widget", "I2S_DUMMY_IN"},
> > +
> > + {"I2SIN4", NULL, "I2S_IN4_Mux"},
> > + {"I2S_IN4_Mux", "Dummy_Widget", "I2S_DUMMY_IN"},
> > +
> > + {"I2SIN6", NULL, "I2S_IN6_Mux"},
> > + {"I2S_IN6_Mux", "Dummy_Widget", "I2S_DUMMY_IN"},
> > +
> > + {"I2S_OUT0_Mux", "Dummy_Widget", "I2SOUT0"},
> > + {"I2S_DUMMY_OUT", NULL, "I2S_OUT0_Mux"},
> > +
> > + {"I2S_OUT1_Mux", "Dummy_Widget", "I2SOUT1"},
> > + {"I2S_DUMMY_OUT", NULL, "I2S_OUT1_Mux"},
> > +
> > + {"I2S_OUT2_Mux", "Dummy_Widget", "I2SOUT2"},
> > + {"I2S_DUMMY_OUT", NULL, "I2S_OUT2_Mux"},
> > +
> > + {"I2S_OUT3_Mux", "Dummy_Widget", "I2SOUT3"},
> > + {"I2S_DUMMY_OUT", NULL, "I2S_OUT3_Mux"},
> > + {"I2S_OUT4_Mux", "Dummy_Widget", "I2SOUT4"},
> > + {"I2S_DUMMY_OUT", NULL, "I2S_OUT4_Mux"},
> > +
> > + {"I2S_OUT6_Mux", "Dummy_Widget", "I2SOUT6"},
> > + {"I2S_DUMMY_OUT", NULL, "I2S_OUT6_Mux"},
>
> Same question about dummy widgets.
>
> > +};
> > +
> > +/* i2s dai ops*/
> > +static int mtk_dai_i2s_config(struct mtk_base_afe *afe,
> > + struct snd_pcm_hw_params *params,
> > + int i2s_id)
> > +{
> > + struct mt8196_afe_private *afe_priv = afe->platform_priv;
> > + struct mtk_afe_i2s_priv *i2s_priv;
> > + struct mtk_afe_i2s_priv *i2sin_priv = NULL;
> > + int id = i2s_id - MT8196_DAI_I2S_IN0;
> > + struct mtk_base_etdm_data etdm_data;
> > + unsigned int rate = params_rate(params);
> > + unsigned int rate_reg = get_etdm_inconn_rate(rate);
> > + snd_pcm_format_t format = params_format(params);
> > + unsigned int channels = params_channels(params);
> > + int ret;
> > + unsigned int i2s_con;
> > + int pad_top;
> > +
> > + if (i2s_id >= MT8196_DAI_NUM || i2s_id < 0 || id < 0 || id
> > >= DAI_I2S_NUM)
> > + return -EINVAL;
> > +
> > + i2s_priv = afe_priv->dai_priv[i2s_id];
> > +
>
> Drop the empty line here so that the check immediately follows the
> assignment.
>
> > + if (!i2s_priv)
> > + return -EINVAL;
> > +
> > + dev_info(afe->dev, "id: %d, rate: %d, pcm_fmt: %d, fmt: %d,
> > ch: %d\n",
> > + i2s_id, rate, format, i2s_priv->format, channels);
>
> Make this debug level.
>
> > + i2s_priv->rate = rate;
> > + etdm_data = mtk_etdm_data[id];
> > +
> > + if (is_etdm_in_pad_top(id))
> > + pad_top = 0x3;
> > + else
> > + pad_top = 0x5;
> > +
> > + switch (id) {
> > + case DAI_FMI2S_MASTER:
> > + i2s_con = I2S_IN_PAD_IO_MUX << I2SIN_PAD_SEL_SFT;
> > + i2s_con |= rate_reg << I2S_MODE_SFT;
> > + i2s_con |= I2S_FMT_I2S << I2S_FMT_SFT;
> > + i2s_con |= get_i2s_wlen(format) << I2S_WLEN_SFT;
> > + regmap_update_bits(afe->regmap,
> > AFE_CONNSYS_I2S_CON,
> > + 0xffffeffe, i2s_con);
> > + break;
>
> Please add an empty line after the "break" statement to clearly
> separate
> each block. Same for the following blocks.
>
> > + case DAI_I2SIN0:
> > + case DAI_I2SIN1:
> > + case DAI_I2SIN2:
> > + case DAI_I2SIN3:
> > + case DAI_I2SIN4:
> > + case DAI_I2SIN6:
> > + /* ---etdm in --- */
> > + regmap_update_bits(afe->regmap,
> > + etdm_data.init_count_reg,
> > + etdm_data.init_count_mask <<
> > etdm_data.init_count_shift,
> > + 0x5 <<
> > etdm_data.init_count_shift);
> > +
> > + /* 3: pad top 5: no pad top */
> > + regmap_update_bits(afe->regmap,
> > + etdm_data.init_point_reg,
> > + etdm_data.init_point_mask <<
> > etdm_data.init_point_shift,
> > + pad_top <<
> > etdm_data.init_point_shift);
> > +
> > + regmap_update_bits(afe->regmap,
> > + etdm_data.lrck_reset_reg,
> > + etdm_data.lrck_reset_mask <<
> > etdm_data.lrck_reset_shift,
> > + 0x1 <<
> > etdm_data.lrck_reset_shift);
> > +
> > + regmap_update_bits(afe->regmap,
> > + etdm_data.clk_source_reg,
> > + etdm_data.clk_source_mask <<
> > etdm_data.clk_source_shift,
> > + ETDM_CLK_SOURCE_APLL <<
> > etdm_data.clk_source_shift);
> > +
> > + /* 0: manual 1: auto */
> > + regmap_update_bits(afe->regmap,
> > + etdm_data.ck_en_sel_reg,
> > + etdm_data.ck_en_sel_mask <<
> > etdm_data.ck_en_sel_shift,
> > + 0x1 <<
> > etdm_data.ck_en_sel_shift);
> > +
> > + regmap_update_bits(afe->regmap,
> > + etdm_data.fs_timing_reg,
> > + etdm_data.fs_timing_mask <<
> > etdm_data.fs_timing_shift,
> > + get_etdm_rate(rate) <<
> > etdm_data.fs_timing_shift);
> > +
> > + regmap_update_bits(afe->regmap,
> > + etdm_data.relatch_en_sel_reg,
> > + etdm_data.relatch_en_sel_mask <<
> > etdm_data.relatch_en_sel_shift,
> > + get_etdm_inconn_rate(rate) <<
> > etdm_data.relatch_en_sel_shift);
> > +
> > + regmap_update_bits(afe->regmap,
> > + etdm_data.use_afifo_reg,
> > + etdm_data.use_afifo_mask <<
> > etdm_data.use_afifo_shift,
> > + 0x0);
> > +
> > + regmap_update_bits(afe->regmap,
> > + etdm_data.afifo_mode_reg,
> > + etdm_data.afifo_mode_mask <<
> > etdm_data.afifo_mode_shift,
> > + 0x0);
> > +
> > + regmap_update_bits(afe->regmap,
> > + etdm_data.almost_end_ch_reg,
> > + etdm_data.almost_end_ch_mask <<
> > etdm_data.almost_end_ch_shift,
> > + 0x0);
> > +
> > + regmap_update_bits(afe->regmap,
> > + etdm_data.almost_end_bit_reg,
> > + etdm_data.almost_end_bit_mask <<
> > etdm_data.almost_end_bit_shift,
> > + 0x0);
> > +
> > + if (is_etdm_in_pad_top(id)) {
> > + regmap_update_bits(afe->regmap,
> > + etdm_data.out2latch_time
> > _reg,
> > + etdm_data.out2latch_time
> > _mask <<
> > + etdm_data.out2latch_time
> > _shift,
> > + 0x6 <<
> > etdm_data.out2latch_time_shift);
> > + } else {
> > + regmap_update_bits(afe->regmap,
> > + etdm_data.out2latch_time
> > _reg,
> > + etdm_data.out2latch_time
> > _mask <<
> > + etdm_data.out2latch_time
> > _shift,
> > + 0x4 <<
> > etdm_data.out2latch_time_shift);
> > + }
> > +
> > + if (id == DAI_I2SIN4) {
> > + dev_dbg(afe->dev, "i2sin4, id: %d, fmt: %d,
> > ch: %d, ip_mode: %d, sync: %d\n",
> > + id,
> > + i2s_priv->format,
> > + channels,
> > + i2s_priv->ip_mode,
> > + i2s_priv->sync);
>
> Put more parameters on one line.
>
> [...]
>
> > +static int mtk_dai_i2s_set_sysclk(struct snd_soc_dai *dai,
> > + int clk_id, unsigned int freq,
> > int dir)
> > +{
> > + struct mtk_base_afe *afe = dev_get_drvdata(dai->dev);
> > + struct mt8196_afe_private *afe_priv = afe->platform_priv;
> > + struct mtk_afe_i2s_priv *i2s_priv;
> > + int apll;
> > + int apll_rate;
> > +
> > + if (dai->id >= MT8196_DAI_NUM || dai->id < 0 || dir !=
> > SND_SOC_CLOCK_OUT)
> > + return -EINVAL;
> > +
> > + i2s_priv = afe_priv->dai_priv[dai->id];
> > +
> > + dev_dbg(afe->dev, "freq: %u\n", freq);
> > +
>
> Drop empty line here and make the check below immediately follow
> the i2s_priv assignment.
>
> You might want to move the debug trace before the assignment and
> make it include more information?
>
> > + if (!i2s_priv)
> > + return -EINVAL;
> > +
> > + apll = mt8196_get_apll_by_rate(afe, freq);
> > + apll_rate = mt8196_get_apll_rate(afe, apll);
> > +
> > + if (freq > apll_rate || apll_rate % freq)
> > + return -EINVAL;
> > +
> > + i2s_priv->mclk_rate = freq;
> > + i2s_priv->mclk_apll = apll;
> > +
> > + if (i2s_priv->share_i2s_id > 0) {
> > + struct mtk_afe_i2s_priv *share_i2s_priv;
> > +
> > + share_i2s_priv = afe_priv->dai_priv[i2s_priv-
> > >share_i2s_id];
> > + if (!share_i2s_priv)
> > + return -EINVAL;
> > +
> > + share_i2s_priv->mclk_rate = i2s_priv->mclk_rate;
> > + share_i2s_priv->mclk_apll = i2s_priv->mclk_apll;
> > + }
>
> Add empty line here before final return statement.
>
> > + return 0;
> > +}
> > +
> > +static int mtk_dai_i2s_set_fmt(struct snd_soc_dai *dai, unsigned
> > int fmt)
> > +{
> > + struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
> > + struct mt8196_afe_private *afe_priv = afe->platform_priv;
> > + struct mtk_afe_i2s_priv *i2s_priv;
> > +
> > + if (dai->id >= MT8196_DAI_NUM || dai->id < 0)
> > + return -EINVAL;
> > +
> > + i2s_priv = afe_priv->dai_priv[dai->id];
> > +
>
> Drop empty line here.
>
> > + if (!i2s_priv)
> > + return -EINVAL;
> > +
> > + dev_dbg(afe->dev, "dai->id: %d, fmt: 0x%x\n", dai->id,
> > fmt);
> > +
> > + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
> > + case SND_SOC_DAIFMT_I2S:
> > + i2s_priv->format = MTK_DAI_ETDM_FORMAT_I2S;
> > + break;
> > + case SND_SOC_DAIFMT_LEFT_J:
> > + i2s_priv->format = MTK_DAI_ETDM_FORMAT_LJ;
> > + break;
> > + case SND_SOC_DAIFMT_RIGHT_J:
> > + i2s_priv->format = MTK_DAI_ETDM_FORMAT_RJ;
> > + break;
> > + case SND_SOC_DAIFMT_DSP_A:
> > + i2s_priv->format = MTK_DAI_ETDM_FORMAT_DSPA;
> > + break;
> > + case SND_SOC_DAIFMT_DSP_B:
> > + i2s_priv->format = MTK_DAI_ETDM_FORMAT_DSPB;
> > + break;
> > + default:
> > + return -EINVAL;
> > + }
> > +
> > + dev_dbg(afe->dev, "dai->id: %d, i2s_priv->format: 0x%x\n",
> > + dai->id, i2s_priv->format);
>
> Drop this one. One debug trace is enough.
>
> > +
> > + return 0;
> > +}
> > +
> > +static const struct snd_soc_dai_ops mtk_dai_i2s_ops = {
> > + .hw_params = mtk_dai_i2s_hw_params,
> > + .set_sysclk = mtk_dai_i2s_set_sysclk,
> > + .set_fmt = mtk_dai_i2s_set_fmt,
> > +};
> > +
> > +/* dai driver */
> > +#define MTK_ETDM_RATES (SNDRV_PCM_RATE_8000_384000)
> > +#define MTK_ETDM_FORMATS (SNDRV_PCM_FMTBIT_S8 |\
> > + SNDRV_PCM_FMTBIT_S16_LE |\
> > + SNDRV_PCM_FMTBIT_S24_LE |\
> > + SNDRV_PCM_FMTBIT_S32_LE)
> > +
> > +#define MT8196_I2S_DAI(_name, _id, max_ch, dir) \
> > +{ \
> > + .name = #_name, \
> > + .id = _id, \
> > + .dir = { \
> > + .stream_name = #_name, \
> > + .channels_min = 1, \
> > + .channels_max = max_ch, \
> > + .rates = MTK_ETDM_RATES, \
> > + .formats = MTK_ETDM_FORMATS, \
> > + }, \
> > + .ops = &mtk_dai_i2s_ops, \
> > +}
> > +
> > +static struct snd_soc_dai_driver mtk_dai_i2s_driver[] = {
> > + /* capture */
> > + MT8196_I2S_DAI(I2SIN0, MT8196_DAI_I2S_IN0, 2, capture),
> > + MT8196_I2S_DAI(I2SIN1, MT8196_DAI_I2S_IN1, 2, capture),
> > + MT8196_I2S_DAI(I2SIN2, MT8196_DAI_I2S_IN2, 2, capture),
> > + MT8196_I2S_DAI(I2SIN3, MT8196_DAI_I2S_IN3, 2, capture),
> > + MT8196_I2S_DAI(I2SIN4, MT8196_DAI_I2S_IN4, 8, capture),
> > + MT8196_I2S_DAI(I2SIN6, MT8196_DAI_I2S_IN6, 2, capture),
> > + MT8196_I2S_DAI(FMI2S_MASTER, MT8196_DAI_FM_I2S_MASTER, 2,
> > capture),
> > + /* playback */
> > + MT8196_I2S_DAI(I2SOUT0, MT8196_DAI_I2S_OUT0, 2, playback),
> > + MT8196_I2S_DAI(I2SOUT1, MT8196_DAI_I2S_OUT1, 2, playback),
> > + MT8196_I2S_DAI(I2SOUT2, MT8196_DAI_I2S_OUT2, 2, playback),
> > + MT8196_I2S_DAI(I2SOUT3, MT8196_DAI_I2S_OUT3, 2, playback),
> > + MT8196_I2S_DAI(I2SOUT4, MT8196_DAI_I2S_OUT4, 8, playback),
> > + MT8196_I2S_DAI(I2SOUT6, MT8196_DAI_I2S_OUT6, 2, playback),
> > +};
> > +
> > +static const struct mtk_afe_i2s_priv mt8196_i2s_priv[DAI_I2S_NUM]
> > = {
> > + [DAI_I2SIN0] = {
> > + .id = MT8196_DAI_I2S_IN0,
> > + .mclk_id = MT8196_I2SIN0_MCK,
> > + .share_property_name = "i2sin0-share",
> > + .share_i2s_id = -1,
> > + },
> > + [DAI_I2SIN1] = {
> > + .id = MT8196_DAI_I2S_IN1,
> > + .mclk_id = MT8196_I2SIN1_MCK,
> > + .share_property_name = "i2sin1-share",
> > + .share_i2s_id = -1,
> > + },
> > + [DAI_I2SIN2] = {
> > + .id = MT8196_DAI_I2S_IN2,
> > + .mclk_id = MT8196_I2SIN0_MCK,
> > + .share_property_name = "i2sin2-share",
> > + .share_i2s_id = -1,
> > + },
> > + [DAI_I2SIN3] = {
> > + .id = MT8196_DAI_I2S_IN3,
> > + .mclk_id = MT8196_I2SIN0_MCK,
> > + .share_property_name = "i2sin3-share",
> > + .share_i2s_id = -1,
> > + },
> > + [DAI_I2SIN4] = {
> > + .id = MT8196_DAI_I2S_IN4,
> > + .mclk_id = MT8196_I2SIN0_MCK,
> > + .share_property_name = "i2sin4-share",
> > + .share_i2s_id = -1,
> > + .sync = 0,
> > + .ip_mode = 0,
> > + },
> > + [DAI_I2SIN6] = {
> > + .id = MT8196_DAI_I2S_IN6,
> > + .mclk_id = MT8196_I2SIN0_MCK,
> > + .share_property_name = "i2sout6-share",
> > + .share_i2s_id = -1,
> > + },
> > + [DAI_I2SOUT0] = {
> > + .id = MT8196_DAI_I2S_OUT0,
> > + .mclk_id = MT8196_I2SIN0_MCK,
> > + .share_property_name = "i2sout0-share",
> > + .share_i2s_id = MT8196_DAI_I2S_IN0,
> > + },
> > + [DAI_I2SOUT1] = {
> > + .id = MT8196_DAI_I2S_OUT1,
> > + .mclk_id = MT8196_I2SIN1_MCK,
> > + .share_property_name = "i2sout1-share",
> > + .share_i2s_id = MT8196_DAI_I2S_IN1,
> > + },
> > + [DAI_I2SOUT2] = {
> > + .id = MT8196_DAI_I2S_OUT2,
> > + .mclk_id = MT8196_I2SIN0_MCK,
> > + .share_property_name = "i2sout2-share",
> > + .share_i2s_id = MT8196_DAI_I2S_IN2,
> > + },
> > + [DAI_I2SOUT3] = {
> > + .id = MT8196_DAI_I2S_OUT3,
> > + .mclk_id = MT8196_I2SIN0_MCK,
> > + .share_property_name = "i2sout3-share",
> > + .share_i2s_id = MT8196_DAI_I2S_IN3,
> > + },
> > + [DAI_I2SOUT4] = {
> > + .id = MT8196_DAI_I2S_OUT4,
> > + .mclk_id = MT8196_I2SIN0_MCK,
> > + .share_property_name = "i2sout4-share",
> > + .share_i2s_id = MT8196_DAI_I2S_IN4,
> > + .sync = 0,
> > + },
> > + [DAI_I2SOUT6] = {
> > + .id = MT8196_DAI_I2S_OUT6,
> > + .mclk_id = MT8196_I2SIN0_MCK,
> > + .share_property_name = "i2sout6-share",
> > + .share_i2s_id = MT8196_DAI_I2S_IN6,
> > + },
> > + [DAI_FMI2S_MASTER] = {
> > + .id = MT8196_DAI_FM_I2S_MASTER,
> > + .mclk_id = MT8196_FMI2S_MCK,
> > + .share_property_name = "fmi2s-share",
> > + .share_i2s_id = -1,
> > + },
> > +};
> > +
> > +static int mt8196_dai_i2s_get_share(struct mtk_base_afe *afe)
> > +{
> > + struct mt8196_afe_private *afe_priv = afe->platform_priv;
> > + const struct device_node *of_node = afe->dev->of_node;
> > + const char *of_str;
> > + const char *property_name;
> > + struct mtk_afe_i2s_priv *i2s_priv;
>
> of_str, property_name, and i2s_priv declarations can be moved inside
> the
> loop.
>
> > + int i;
> > +
> > + for (i = 0; i < DAI_I2S_NUM; i++) {
>
> Here you can write C99 style, and also use unsigned type for index:
>
> for (unsigned int i = 0; i < DAI_I2S_NUM; i++) {
>
> > + i2s_priv = afe_priv-
> > >dai_priv[mt8196_i2s_priv[i].id];
> > + property_name =
> > mt8196_i2s_priv[i].share_property_name;
> > + if (of_property_read_string(of_node, property_name,
> > &of_str))
> > + continue;
> > + i2s_priv->share_i2s_id = get_i2s_id_by_name(afe,
> > of_str);
> > + }
>
> Nit: add empty line here.
>
> > + return 0;
> > +}
> > +
> > +static int init_i2s_priv_data(struct mtk_base_afe *afe)
> > +{
> > + struct mt8196_afe_private *afe_priv = afe->platform_priv;
> > + struct mtk_afe_i2s_priv *i2s_priv;
> > + int size;
>
> Please use size_t for size variable.
>
> > + int id;
>
> You can move i2s_priv, size, and id declarations inside the loop.
>
> > + int i;
> > +
> > + for (i = 0; i < DAI_I2S_NUM; i++) {
>
> Here you can write C99 style, and also use unsigned type for index:
>
> for (unsigned int i = 0; i < DAI_I2S_NUM; i++) {
>
> > + id = mt8196_i2s_priv[i].id;
> > + size = sizeof(struct mtk_afe_i2s_priv);
> > +
> > + if (id >= MT8196_DAI_NUM || id < 0)
> > + return -EINVAL;
> > +
> > + i2s_priv = devm_kzalloc(afe->dev, size,
> > GFP_KERNEL);
> > + if (!i2s_priv)
> > + return -ENOMEM;
> > +
> > + memcpy(i2s_priv, &mt8196_i2s_priv[i], size);
> > +
> > + afe_priv->dai_priv[id] = i2s_priv;
> > + }
>
> Nit: add an empty line here.
>
> > + return 0;
> > +}
> > +
> > +int mt8196_dai_i2s_register(struct mtk_base_afe *afe)
> > +{
> > + struct mtk_base_afe_dai *dai;
> > + int ret;
> > +
> > + dai = devm_kzalloc(afe->dev, sizeof(*dai), GFP_KERNEL);
> > + if (!dai)
> > + return -ENOMEM;
> > +
> > + list_add(&dai->list, &afe->sub_dais);
> > +
> > + dai->dai_drivers = mtk_dai_i2s_driver;
> > + dai->num_dai_drivers = ARRAY_SIZE(mtk_dai_i2s_driver);
> > +
> > + dai->controls = mtk_dai_i2s_controls;
> > + dai->num_controls = ARRAY_SIZE(mtk_dai_i2s_controls);
> > + dai->dapm_widgets = mtk_dai_i2s_widgets;
> > + dai->num_dapm_widgets = ARRAY_SIZE(mtk_dai_i2s_widgets);
> > + dai->dapm_routes = mtk_dai_i2s_routes;
> > + dai->num_dapm_routes = ARRAY_SIZE(mtk_dai_i2s_routes);
> > +
> > + /* set all dai i2s private data */
> > + ret = init_i2s_priv_data(afe);
> > + if (ret)
> > + return ret;
> > +
> > + /* parse share i2s */
> > + ret = mt8196_dai_i2s_get_share(afe);
> > + if (ret)
> > + return ret;
> > +
> > + return 0;
> > +}
> > --
> > 2.45.2
> >
> >
^ permalink raw reply [flat|nested] 24+ messages in thread
end of thread, other threads:[~2025-08-21 12:50 UTC | newest]
Thread overview: 24+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-07-08 11:15 [PATCH v6 00/10] ASoC: mediatek: Add support for MT8196 SoC Darren.Ye
2025-07-08 11:15 ` [PATCH v6 01/10] ASoC: mediatek: common: modify mtk afe platform driver for mt8196 Darren.Ye
2025-07-22 7:38 ` Chen-Yu Tsai
2025-07-08 11:15 ` [PATCH v6 03/10] ASoC: mediatek: mt8196: support audio clock control Darren.Ye
2025-07-30 8:42 ` Chen-Yu Tsai
2025-07-08 11:15 ` [PATCH v6 04/10] ASoC: mediatek: mt8196: support ADDA in platform driver Darren.Ye
2025-07-29 11:45 ` Chen-Yu Tsai
2025-07-08 11:15 ` [PATCH v6 06/10] ASoC: mediatek: mt8196: support TDM " Darren.Ye
2025-07-28 10:53 ` Chen-Yu Tsai
2025-08-21 8:58 ` Darren Ye (叶飞)
2025-07-08 11:15 ` [PATCH v6 07/10] ASoC: mediatek: mt8196: add " Darren.Ye
2025-08-05 10:40 ` Chen-Yu Tsai
2025-07-08 11:16 ` [PATCH v6 08/10] ASoC: dt-bindings: mediatek,mt8196-afe: add audio AFE Darren.Ye
2025-07-15 5:09 ` Chen-Yu Tsai
2025-07-15 7:34 ` Chen-Yu Tsai
2025-07-16 12:41 ` Darren Ye (叶飞)
2025-07-17 2:16 ` Chen-Yu Tsai
2025-07-08 11:16 ` [PATCH v6 09/10] ASoC: mediatek: mt8196: add machine driver with nau8825 Darren.Ye
2025-07-21 9:17 ` Chen-Yu Tsai
2025-07-08 11:16 ` [PATCH v6 10/10] ASoC: dt-bindings: mediatek,mt8196-nau8825: Add audio sound card Darren.Ye
[not found] ` <20250708111806.3992-3-darren.ye@mediatek.com>
2025-07-30 8:23 ` [PATCH v6 02/10] ASoC: mediatek: mt8196: add common header Chen-Yu Tsai
[not found] ` <20250708111806.3992-6-darren.ye@mediatek.com>
2025-08-11 11:03 ` [PATCH v6 05/10] ASoC: mediatek: mt8196: support I2S in platform driver Chen-Yu Tsai
2025-08-21 9:10 ` Darren Ye (叶飞)
2025-08-11 11:24 ` Chen-Yu Tsai
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).