@@ -1,6 +1,7 @@
# SPDX-License-Identifier: GPL-2.0
snd-soc-mt8516-afe-objs := \
- mt8516-afe-pcm.o
+ mt8516-afe-pcm.o \
+ mt8516-dai-adda.o
obj-$(CONFIG_SND_SOC_MT8516) += snd-soc-mt8516-afe.o
new file mode 100644
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2019 BayLibre, SAS
+ * Author: Fabien Parent <fparent@baylibre.com>
+ */
+
+#ifndef _MT8516_AFE_COMMON_H_
+#define _MT8516_AFE_COMMON_H_
+
+#include "../common/mtk-base-afe.h"
+
+enum {
+ MT8516_AFE_BE_ADDA,
+};
+
+int mt8516_dai_adda_register(struct mtk_base_afe *afe);
+
+#endif
new file mode 100644
@@ -0,0 +1,316 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2019 BayLibre, SAS
+ * Copyright (c) 2019 MediaTek, Inc
+ * Author: Fabien Parent <fparent@baylibre.com>
+ */
+
+#include <sound/soc.h>
+#include <sound/pcm_params.h>
+
+#include "mt8516-afe-common.h"
+#include "mt8516-afe-regs.h"
+
+enum {
+ MTK_AFE_ADDA_DL_RATE_8K = 0,
+ MTK_AFE_ADDA_DL_RATE_11K = 1,
+ MTK_AFE_ADDA_DL_RATE_12K = 2,
+ MTK_AFE_ADDA_DL_RATE_16K = 3,
+ MTK_AFE_ADDA_DL_RATE_22K = 4,
+ MTK_AFE_ADDA_DL_RATE_24K = 5,
+ MTK_AFE_ADDA_DL_RATE_32K = 6,
+ MTK_AFE_ADDA_DL_RATE_44K = 7,
+ MTK_AFE_ADDA_DL_RATE_48K = 8,
+};
+
+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,
+};
+
+static int mt8516_afe_setup_i2s(struct mtk_base_afe *afe,
+ struct snd_pcm_substream *substream,
+ unsigned int rate, int bit_width)
+{
+ int fs = afe->memif_fs(substream, rate);
+ unsigned int val;
+
+ if (bit_width > 16)
+ val |= AFE_I2S_CON1_WLEN_32BIT;
+
+ if (fs < 0)
+ return -EINVAL;
+
+ val = AFE_I2S_CON1_I2S2_TO_PAD |
+ AFE_I2S_CON1_LOW_JITTER_CLK |
+ AFE_I2S_CON1_RATE(fs) |
+ AFE_I2S_CON1_FORMAT_I2S |
+ AFE_I2S_CON1_EN;
+
+ regmap_write(afe->regmap, AFE_I2S_CON1, val);
+
+ return 0;
+}
+
+static int mt8516_afe_setup_adda_dl(struct mtk_base_afe *afe, unsigned int rate)
+{
+ unsigned int val = AFE_ADDA_DL_8X_UPSAMPLE |
+ AFE_ADDA_DL_MUTE_OFF |
+ AFE_ADDA_DL_DEGRADE_GAIN;
+
+ if (rate == 8000 || rate == 16000)
+ val |= AFE_ADDA_DL_VOICE_DATA;
+
+ switch (rate) {
+ case 8000:
+ val |= MTK_AFE_ADDA_DL_RATE_8K << AFE_ADDA_DL_RATE_SHIFT;
+ break;
+ case 11025:
+ val |= MTK_AFE_ADDA_DL_RATE_11K << AFE_ADDA_DL_RATE_SHIFT;
+ break;
+ case 12000:
+ val |= MTK_AFE_ADDA_DL_RATE_12K << AFE_ADDA_DL_RATE_SHIFT;
+ break;
+ case 16000:
+ val |= MTK_AFE_ADDA_DL_RATE_16K << AFE_ADDA_DL_RATE_SHIFT;
+ break;
+ case 22050:
+ val |= MTK_AFE_ADDA_DL_RATE_22K << AFE_ADDA_DL_RATE_SHIFT;
+ break;
+ case 24000:
+ val |= MTK_AFE_ADDA_DL_RATE_24K << AFE_ADDA_DL_RATE_SHIFT;
+ break;
+ case 32000:
+ val |= MTK_AFE_ADDA_DL_RATE_32K << AFE_ADDA_DL_RATE_SHIFT;
+ break;
+ case 44100:
+ val |= MTK_AFE_ADDA_DL_RATE_44K << AFE_ADDA_DL_RATE_SHIFT;
+ break;
+ case 48000:
+ val |= MTK_AFE_ADDA_DL_RATE_48K << AFE_ADDA_DL_RATE_SHIFT;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ regmap_write(afe->regmap, AFE_ADDA_PREDIS_CON0, 0);
+ regmap_write(afe->regmap, AFE_ADDA_PREDIS_CON1, 0);
+ regmap_write(afe->regmap, AFE_ADDA_DL_SRC2_CON0, val);
+ regmap_write(afe->regmap, AFE_ADDA_DL_SRC2_CON1, 0xf74f0000);
+
+ return 0;
+}
+
+static int mt8516_afe_setup_adda_ul(struct mtk_base_afe *afe, unsigned int rate)
+{
+ unsigned int val = 0;
+ unsigned int val2 = 0;
+
+ switch (rate) {
+ case 8000:
+ val |= MTK_AFE_ADDA_UL_RATE_8K << AFE_ADDA_UL_RATE_CH1_SHIFT;
+ val |= MTK_AFE_ADDA_UL_RATE_8K << AFE_ADDA_UL_RATE_CH2_SHIFT;
+ val2 |= 1 << AFE_ADDA_NEWIF_ADC_VOICE_MODE_SHIFT;
+ break;
+ case 16000:
+ val |= MTK_AFE_ADDA_UL_RATE_16K << AFE_ADDA_UL_RATE_CH1_SHIFT;
+ val |= MTK_AFE_ADDA_UL_RATE_16K << AFE_ADDA_UL_RATE_CH2_SHIFT;
+ val2 |= 1 << AFE_ADDA_NEWIF_ADC_VOICE_MODE_SHIFT;
+ break;
+ case 32000:
+ val |= MTK_AFE_ADDA_UL_RATE_32K << AFE_ADDA_UL_RATE_CH1_SHIFT;
+ val |= MTK_AFE_ADDA_UL_RATE_32K << AFE_ADDA_UL_RATE_CH2_SHIFT;
+ val2 |= 1 << AFE_ADDA_NEWIF_ADC_VOICE_MODE_SHIFT;
+ break;
+ case 48000:
+ val |= MTK_AFE_ADDA_UL_RATE_48K << AFE_ADDA_UL_RATE_CH1_SHIFT;
+ val |= MTK_AFE_ADDA_UL_RATE_48K << AFE_ADDA_UL_RATE_CH2_SHIFT;
+ val2 |= 3 << AFE_ADDA_NEWIF_ADC_VOICE_MODE_SHIFT;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ regmap_update_bits(afe->regmap, AFE_ADDA_UL_SRC_CON0,
+ (AFE_ADDA_UL_RATE_CH1_MASK << AFE_ADDA_UL_RATE_CH1_SHIFT) ||
+ (AFE_ADDA_UL_RATE_CH2_MASK << AFE_ADDA_UL_RATE_CH2_MASK), val);
+ regmap_update_bits(afe->regmap, AFE_ADDA_NEWIF_CFG1,
+ AFE_ADDA_NEWIF_ADC_VOICE_MODE_CLR, val2);
+ regmap_update_bits(afe->regmap, AFE_ADDA_TOP_CON0, 1, 0);
+
+ return 0;
+}
+
+static void mt8516_afe_adda_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+ unsigned int stream = substream->stream;
+
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ regmap_update_bits(afe->regmap, AFE_ADDA_DL_SRC2_CON0, 1, 0);
+ regmap_update_bits(afe->regmap, AFE_I2S_CON1, 1, 0);
+ } else {
+ regmap_update_bits(afe->regmap, AFE_ADDA_UL_SRC_CON0, 1, 0);
+ }
+
+ regmap_update_bits(afe->regmap, AFE_ADDA_UL_DL_CON0, 1, 0);
+}
+
+static int mt8516_afe_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);
+ unsigned int width_val = params_width(params) > 16 ?
+ (AFE_CONN_24BIT_O03 | AFE_CONN_24BIT_O04) : 0;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ regmap_update_bits(afe->regmap, AFE_CONN_24BIT,
+ AFE_CONN_24BIT_O03 | AFE_CONN_24BIT_O04, width_val);
+
+ return 0;
+}
+
+static int mt8516_afe_adda_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+ const unsigned int rate = substream->runtime->rate;
+ unsigned int stream = substream->stream;
+ int bit_width = snd_pcm_format_width(substream->runtime->format);
+ int ret;
+
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ ret = mt8516_afe_setup_adda_dl(afe, rate);
+ if (ret)
+ return ret;
+
+ ret = mt8516_afe_setup_i2s(afe, substream, rate, bit_width);
+ if (ret)
+ return ret;
+
+ regmap_update_bits(afe->regmap, AFE_ADDA_DL_SRC2_CON0, 1, 1);
+ } else {
+ ret = mt8516_afe_setup_adda_ul(afe, rate);
+ if (ret)
+ return ret;
+
+ regmap_update_bits(afe->regmap, AFE_ADDA_UL_SRC_CON0, 1, 1);
+ }
+
+ regmap_update_bits(afe->regmap, AFE_ADDA_UL_DL_CON0, 1, 1);
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops mt8516_afe_adda_ops = {
+ .shutdown = mt8516_afe_adda_shutdown,
+ .hw_params = mt8516_afe_adda_hw_params,
+ .prepare = mt8516_afe_adda_prepare,
+};
+
+static const struct snd_kcontrol_new adda_o03_o04_enable_ctl =
+ SOC_DAPM_SINGLE_VIRT("Switch", 1);
+
+static const char * const ain_text[] = {
+ "INT ADC", "EXT ADC"
+};
+
+static SOC_ENUM_SINGLE_DECL(ain_enum, AFE_ADDA_TOP_CON0, 0, ain_text);
+
+static const struct snd_kcontrol_new ain_mux =
+ SOC_DAPM_ENUM("AIN Source", ain_enum);
+
+enum {
+ SUPPLY_SEQ_ADDA_AFE_ON,
+};
+
+static const struct snd_soc_dapm_widget mtk_dai_adda_widgets[] = {
+ SND_SOC_DAPM_MUX("AIN Mux", SND_SOC_NOPM, 0, 0, &ain_mux),
+
+ SND_SOC_DAPM_SWITCH("ADDA O03_O04", SND_SOC_NOPM, 0, 0,
+ &adda_o03_o04_enable_ctl),
+
+
+ SND_SOC_DAPM_SUPPLY_S("ADDA Enable", SUPPLY_SEQ_ADDA_AFE_ON,
+ AFE_DAC_CON0, 0, 0,
+ NULL, 0),
+
+ /* Clocks */
+ SND_SOC_DAPM_CLOCK_SUPPLY("top_pdn_audio"),
+ SND_SOC_DAPM_CLOCK_SUPPLY("aud_dac_clk"),
+ SND_SOC_DAPM_CLOCK_SUPPLY("aud_dac_predis_clk"),
+ SND_SOC_DAPM_CLOCK_SUPPLY("aud_adc_clk"),
+};
+
+static const struct snd_soc_dapm_route mtk_dai_adda_routes[] = {
+ /* playback */
+ {"ADDA O03_O04", "Switch", "O03"},
+ {"ADDA O03_O04", "Switch", "O04"},
+ {"ADDA Playback", NULL, "ADDA O03_O04"},
+
+ /* capture */
+ {"AIN Mux", "INT ADC", "ADDA Capture"},
+
+ /* enable */
+ {"ADDA Playback", NULL, "ADDA Enable"},
+ {"ADDA Capture", NULL, "ADDA Enable"},
+
+ /* clock */
+ {"ADDA Playback", NULL, "aud_dac_clk"},
+ {"ADDA Playback", NULL, "aud_dac_predis_clk"},
+ {"ADDA Playback", NULL, "top_pdn_audio"},
+
+ {"ADDA Capture", NULL, "top_pdn_audio"},
+ {"ADDA Capture", NULL, "aud_adc_clk"},
+};
+
+static struct snd_soc_dai_driver mtk_dai_adda_driver[] = {
+ {
+ .name = "ADDA",
+ .id = MT8516_AFE_BE_ADDA,
+ .playback = {
+ .stream_name = "ADDA Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE,
+ },
+ .capture = {
+ .stream_name = "ADDA Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000 |
+ SNDRV_PCM_RATE_16000 |
+ SNDRV_PCM_RATE_32000 |
+ SNDRV_PCM_RATE_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .ops = &mt8516_afe_adda_ops,
+ },
+};
+
+int mt8516_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->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 0;
+}
Add the ADDA DAI driver for the MediaTek MT8516 SoC. Signed-off-by: Fabien Parent <fparent@baylibre.com> --- sound/soc/mediatek/mt8516/Makefile | 3 +- sound/soc/mediatek/mt8516/mt8516-afe-common.h | 18 + sound/soc/mediatek/mt8516/mt8516-dai-adda.c | 316 ++++++++++++++++++ 3 files changed, 336 insertions(+), 1 deletion(-) create mode 100644 sound/soc/mediatek/mt8516/mt8516-afe-common.h create mode 100644 sound/soc/mediatek/mt8516/mt8516-dai-adda.c