// SPDX-License-Identifier: GPL-2.0 /* * MediaTek 8365 ALSA SoC Audio DAI DMIC Control * * Copyright (c) 2024 MediaTek Inc. * Authors: Jia Zeng * Alexandre Mergnat */ #include #include #include #include "mt8365-afe-clk.h" #include "mt8365-afe-common.h" struct mt8365_dmic_data { bool two_wire_mode; unsigned int clk_phase_sel_ch1; unsigned int clk_phase_sel_ch2; bool iir_on; unsigned int irr_mode; unsigned int dmic_mode; unsigned int dmic_channel; }; static int get_chan_reg(unsigned int channel) { switch (channel) { case 8: fallthrough; case 7: return AFE_DMIC3_UL_SRC_CON0; case 6: fallthrough; case 5: return AFE_DMIC2_UL_SRC_CON0; case 4: fallthrough; case 3: return AFE_DMIC1_UL_SRC_CON0; case 2: fallthrough; case 1: return AFE_DMIC0_UL_SRC_CON0; default: return -EINVAL; } } /* DAI Drivers */ static void audio_dmic_adda_enable(struct mtk_base_afe *afe) { mt8365_dai_enable_adda_on(afe); regmap_update_bits(afe->regmap, AFE_ADDA_UL_DL_CON0, AFE_ADDA_UL_DL_DMIC_CLKDIV_ON, AFE_ADDA_UL_DL_DMIC_CLKDIV_ON); } static void audio_dmic_adda_disable(struct mtk_base_afe *afe) { regmap_update_bits(afe->regmap, AFE_ADDA_UL_DL_CON0, AFE_ADDA_UL_DL_DMIC_CLKDIV_ON, ~AFE_ADDA_UL_DL_DMIC_CLKDIV_ON); mt8365_dai_disable_adda_on(afe); } static void mt8365_dai_enable_dmic(struct mtk_base_afe *afe, struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct mt8365_afe_private *afe_priv = afe->platform_priv; struct mt8365_dmic_data *dmic_data = afe_priv->dai_priv[MT8365_AFE_IO_DMIC]; unsigned int val_mask; int reg = get_chan_reg(dmic_data->dmic_channel); if (reg < 0) return; /* val and mask will be always same to enable */ val_mask = DMIC_TOP_CON_CH1_ON | DMIC_TOP_CON_CH2_ON | DMIC_TOP_CON_SRC_ON; regmap_update_bits(afe->regmap, reg, val_mask, val_mask); } static void mt8365_dai_disable_dmic(struct mtk_base_afe *afe, struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct mt8365_afe_private *afe_priv = afe->platform_priv; struct mt8365_dmic_data *dmic_data = afe_priv->dai_priv[MT8365_AFE_IO_DMIC]; unsigned int mask; int reg = get_chan_reg(dmic_data->dmic_channel); if (reg < 0) return; dev_dbg(afe->dev, "%s dmic_channel %d\n", __func__, dmic_data->dmic_channel); mask = DMIC_TOP_CON_CH1_ON | DMIC_TOP_CON_CH2_ON | DMIC_TOP_CON_SRC_ON | DMIC_TOP_CON_SDM3_LEVEL_MODE; /* Set all masked values to 0 */ regmap_update_bits(afe->regmap, reg, mask, 0); } static const struct reg_sequence mt8365_dmic_iir_coeff[] = { { AFE_DMIC0_IIR_COEF_02_01, 0x00000000 }, { AFE_DMIC0_IIR_COEF_04_03, 0x00003FB8 }, { AFE_DMIC0_IIR_COEF_06_05, 0x3FB80000 }, { AFE_DMIC0_IIR_COEF_08_07, 0x3FB80000 }, { AFE_DMIC0_IIR_COEF_10_09, 0x0000C048 }, { AFE_DMIC1_IIR_COEF_02_01, 0x00000000 }, { AFE_DMIC1_IIR_COEF_04_03, 0x00003FB8 }, { AFE_DMIC1_IIR_COEF_06_05, 0x3FB80000 }, { AFE_DMIC1_IIR_COEF_08_07, 0x3FB80000 }, { AFE_DMIC1_IIR_COEF_10_09, 0x0000C048 }, { AFE_DMIC2_IIR_COEF_02_01, 0x00000000 }, { AFE_DMIC2_IIR_COEF_04_03, 0x00003FB8 }, { AFE_DMIC2_IIR_COEF_06_05, 0x3FB80000 }, { AFE_DMIC2_IIR_COEF_08_07, 0x3FB80000 }, { AFE_DMIC2_IIR_COEF_10_09, 0x0000C048 }, { AFE_DMIC3_IIR_COEF_02_01, 0x00000000 }, { AFE_DMIC3_IIR_COEF_04_03, 0x00003FB8 }, { AFE_DMIC3_IIR_COEF_06_05, 0x3FB80000 }, { AFE_DMIC3_IIR_COEF_08_07, 0x3FB80000 }, { AFE_DMIC3_IIR_COEF_10_09, 0x0000C048 }, }; static int mt8365_dai_load_dmic_iir_coeff_table(struct mtk_base_afe *afe) { return regmap_multi_reg_write(afe->regmap, mt8365_dmic_iir_coeff, ARRAY_SIZE(mt8365_dmic_iir_coeff)); } static int mt8365_dai_configure_dmic(struct mtk_base_afe *afe, struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct mt8365_afe_private *afe_priv = afe->platform_priv; struct mt8365_dmic_data *dmic_data = afe_priv->dai_priv[MT8365_AFE_IO_DMIC]; bool two_wire_mode = dmic_data->two_wire_mode; unsigned int clk_phase_sel_ch1 = dmic_data->clk_phase_sel_ch1; unsigned int clk_phase_sel_ch2 = dmic_data->clk_phase_sel_ch2; unsigned int val = 0; unsigned int rate = dai->rate; int reg = get_chan_reg(dai->channels); if (reg < 0) return -EINVAL; dmic_data->dmic_channel = dai->channels; val |= DMIC_TOP_CON_SDM3_LEVEL_MODE; if (two_wire_mode) { val |= DMIC_TOP_CON_TWO_WIRE_MODE; } else { val |= FIELD_PREP(DMIC_TOP_CON_CK_PHASE_SEL_CH1, clk_phase_sel_ch1); val |= FIELD_PREP(DMIC_TOP_CON_CK_PHASE_SEL_CH2, clk_phase_sel_ch2); } switch (rate) { case 48000: val |= DMIC_TOP_CON_VOICE_MODE_48K; break; case 32000: val |= DMIC_TOP_CON_VOICE_MODE_32K; break; case 16000: val |= DMIC_TOP_CON_VOICE_MODE_16K; break; case 8000: val |= DMIC_TOP_CON_VOICE_MODE_8K; break; default: return -EINVAL; } regmap_update_bits(afe->regmap, reg, DMIC_TOP_CON_CONFIG_MASK, val); return 0; } static int mt8365_dai_dmic_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai); mt8365_afe_enable_main_clk(afe); mt8365_afe_enable_top_cg(afe, MT8365_TOP_CG_DMIC0_ADC); mt8365_afe_enable_top_cg(afe, MT8365_TOP_CG_DMIC1_ADC); mt8365_afe_enable_top_cg(afe, MT8365_TOP_CG_DMIC2_ADC); mt8365_afe_enable_top_cg(afe, MT8365_TOP_CG_DMIC3_ADC); audio_dmic_adda_enable(afe); return 0; } static void mt8365_dai_dmic_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai); mt8365_dai_disable_dmic(afe, substream, dai); audio_dmic_adda_disable(afe); /* HW Request delay 125us before CG off */ usleep_range(125, 300); mt8365_afe_disable_top_cg(afe, MT8365_TOP_CG_DMIC3_ADC); mt8365_afe_disable_top_cg(afe, MT8365_TOP_CG_DMIC2_ADC); mt8365_afe_disable_top_cg(afe, MT8365_TOP_CG_DMIC1_ADC); mt8365_afe_disable_top_cg(afe, MT8365_TOP_CG_DMIC0_ADC); mt8365_afe_disable_main_clk(afe); } static int mt8365_dai_dmic_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai); mt8365_dai_configure_dmic(afe, substream, dai); mt8365_dai_enable_dmic(afe, substream, dai); return 0; } static const struct snd_soc_dai_ops mt8365_afe_dmic_ops = { .startup = mt8365_dai_dmic_startup, .shutdown = mt8365_dai_dmic_shutdown, .prepare = mt8365_dai_dmic_prepare, }; static struct snd_soc_dai_driver mtk_dai_dmic_driver[] = { { .name = "DMIC", .id = MT8365_AFE_IO_DMIC, .capture = { .stream_name = "DMIC Capture", .channels_min = 1, .channels_max = 8, .rates = SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000, .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE, }, .ops = &mt8365_afe_dmic_ops, } }; /* DAI Controls */ /* Values for 48kHz mode */ static const char * const iir_mode_src[] = { "SW custom", "5Hz", "10Hz", "25Hz", "50Hz", "65Hz" }; static SOC_ENUM_SINGLE_DECL(iir_mode, AFE_DMIC0_UL_SRC_CON0, 7, iir_mode_src); static const struct snd_kcontrol_new mtk_dai_dmic_controls[] = { SOC_SINGLE("DMIC IIR Switch", AFE_DMIC0_UL_SRC_CON0, DMIC_TOP_CON_IIR_ON, 1, 0), SOC_ENUM("DMIC IIR Mode", iir_mode), }; /* DAI widget */ static const struct snd_soc_dapm_widget mtk_dai_dmic_widgets[] = { SND_SOC_DAPM_INPUT("DMIC In"), }; /* DAI route */ static const struct snd_soc_dapm_route mtk_dai_dmic_routes[] = { {"I14", NULL, "DMIC Capture"}, {"I15", NULL, "DMIC Capture"}, {"I16", NULL, "DMIC Capture"}, {"I17", NULL, "DMIC Capture"}, {"I18", NULL, "DMIC Capture"}, {"I19", NULL, "DMIC Capture"}, {"I20", NULL, "DMIC Capture"}, {"I21", NULL, "DMIC Capture"}, {"DMIC Capture", NULL, "DMIC In"}, }; static int init_dmic_priv_data(struct mtk_base_afe *afe) { struct mt8365_afe_private *afe_priv = afe->platform_priv; struct mt8365_dmic_data *dmic_priv; struct device_node *np = afe->dev->of_node; unsigned int temps[4]; int ret; dmic_priv = devm_kzalloc(afe->dev, sizeof(*dmic_priv), GFP_KERNEL); if (!dmic_priv) return -ENOMEM; ret = of_property_read_u32_array(np, "mediatek,dmic-mode", &temps[0], 1); if (ret == 0) dmic_priv->two_wire_mode = !!temps[0]; if (!dmic_priv->two_wire_mode) { dmic_priv->clk_phase_sel_ch1 = 0; dmic_priv->clk_phase_sel_ch2 = 4; } afe_priv->dai_priv[MT8365_AFE_IO_DMIC] = dmic_priv; return 0; } int mt8365_dai_dmic_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_dmic_driver; dai->num_dai_drivers = ARRAY_SIZE(mtk_dai_dmic_driver); dai->controls = mtk_dai_dmic_controls; dai->num_controls = ARRAY_SIZE(mtk_dai_dmic_controls); dai->dapm_widgets = mtk_dai_dmic_widgets; dai->num_dapm_widgets = ARRAY_SIZE(mtk_dai_dmic_widgets); dai->dapm_routes = mtk_dai_dmic_routes; dai->num_dapm_routes = ARRAY_SIZE(mtk_dai_dmic_routes); return init_dmic_priv_data(afe); }