diff mbox series

[4/5] ASoC: mediatek: mt8516: Add ADDA DAI driver

Message ID 20190502121041.8045-5-fparent@baylibre.com (mailing list archive)
State New, archived
Headers show
Series ASoC: mediatek: Add basic PCM driver for MT8516 | expand

Commit Message

Fabien Parent May 2, 2019, 12:10 p.m. UTC
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
diff mbox series

Patch

diff --git a/sound/soc/mediatek/mt8516/Makefile b/sound/soc/mediatek/mt8516/Makefile
index 6e49b01d02c2..dc05ab45560d 100644
--- a/sound/soc/mediatek/mt8516/Makefile
+++ b/sound/soc/mediatek/mt8516/Makefile
@@ -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
diff --git a/sound/soc/mediatek/mt8516/mt8516-afe-common.h b/sound/soc/mediatek/mt8516/mt8516-afe-common.h
new file mode 100644
index 000000000000..e70877c044a4
--- /dev/null
+++ b/sound/soc/mediatek/mt8516/mt8516-afe-common.h
@@ -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
diff --git a/sound/soc/mediatek/mt8516/mt8516-dai-adda.c b/sound/soc/mediatek/mt8516/mt8516-dai-adda.c
new file mode 100644
index 000000000000..76e53772d784
--- /dev/null
+++ b/sound/soc/mediatek/mt8516/mt8516-dai-adda.c
@@ -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;
+}