mirror of
https://github.com/torvalds/linux.git
synced 2024-11-22 12:11:40 +00:00
ASoC: mediatek: mt8365: Add DMIC DAI support
Add Digital Micro Device Audio Interface support for MT8365 SoC. Reviewed-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com> Signed-off-by: Alexandre Mergnat <amergnat@baylibre.com> Link: https://patch.msgid.link/20240226-audio-i350-v7-8-6518d953a141@baylibre.com Signed-off-by: Mark Brown <broonie@kernel.org>
This commit is contained in:
parent
7c58c88e52
commit
1c50ec75ce
340
sound/soc/mediatek/mt8365/mt8365-dai-dmic.c
Normal file
340
sound/soc/mediatek/mt8365/mt8365-dai-dmic.c
Normal file
@ -0,0 +1,340 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* MediaTek 8365 ALSA SoC Audio DAI DMIC Control
|
||||
*
|
||||
* Copyright (c) 2024 MediaTek Inc.
|
||||
* Authors: Jia Zeng <jia.zeng@mediatek.com>
|
||||
* Alexandre Mergnat <amergnat@baylibre.com>
|
||||
*/
|
||||
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <sound/pcm_params.h>
|
||||
#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);
|
||||
}
|
Loading…
Reference in New Issue
Block a user