File Index
Files and directories
Sign in
FILE

mt8365-dai-adda.c

File preview with syntax highlighting.

linux-6.19.14/sound/soc/mediatek/mt8365/mt8365-dai-adda.c C
  1// SPDX-License-Identifier: GPL-2.0
  2/*
  3 * MediaTek 8365 ALSA SoC Audio DAI ADDA Control
  4 *
  5 * Copyright (c) 2024 MediaTek Inc.
  6 * Authors: Jia Zeng <jia.zeng@mediatek.com>
  7 *          Alexandre Mergnat <amergnat@baylibre.com>
  8 */
  9
 10#include <linux/bitops.h>
 11#include <linux/regmap.h>
 12#include <sound/pcm_params.h>
 13#include "mt8365-afe-clk.h"
 14#include "mt8365-afe-common.h"
 15#include "../common/mtk-dai-adda-common.h"
 16
 17static int adda_afe_on_ref_cnt;
 18
 19/* DAI Drivers */
 20
 21static int mt8365_dai_set_adda_out(struct mtk_base_afe *afe, unsigned int rate)
 22{
 23	unsigned int val;
 24
 25	if (rate == 8000 || rate == 16000)
 26		val = AFE_ADDA_DL_VOICE_DATA;
 27	else
 28		val = 0;
 29
 30	val |= FIELD_PREP(AFE_ADDA_DL_SAMPLING_RATE,
 31		mtk_adda_dl_rate_transform(afe, rate));
 32	val |= AFE_ADDA_DL_8X_UPSAMPLE |
 33	       AFE_ADDA_DL_MUTE_OFF_CH1 |
 34	       AFE_ADDA_DL_MUTE_OFF_CH2 |
 35	       AFE_ADDA_DL_DEGRADE_GAIN;
 36
 37	regmap_update_bits(afe->regmap, AFE_ADDA_PREDIS_CON0, 0xffffffff, 0);
 38	regmap_update_bits(afe->regmap, AFE_ADDA_PREDIS_CON1, 0xffffffff, 0);
 39	regmap_update_bits(afe->regmap, AFE_ADDA_DL_SRC2_CON0, 0xffffffff, val);
 40	/* SA suggest apply -0.3db to audio/speech path */
 41	regmap_update_bits(afe->regmap, AFE_ADDA_DL_SRC2_CON1,
 42			   0xffffffff, 0xf74f0000);
 43	/* SA suggest use default value for sdm */
 44	regmap_update_bits(afe->regmap, AFE_ADDA_DL_SDM_DCCOMP_CON,
 45			   0xffffffff, 0x0700701e);
 46
 47	return 0;
 48}
 49
 50static int mt8365_dai_set_adda_in(struct mtk_base_afe *afe, unsigned int rate)
 51{
 52	unsigned int val;
 53
 54	val = FIELD_PREP(AFE_ADDA_UL_SAMPLING_RATE,
 55			 mtk_adda_ul_rate_transform(afe, rate));
 56	regmap_update_bits(afe->regmap, AFE_ADDA_UL_SRC_CON0,
 57			   AFE_ADDA_UL_SAMPLING_RATE, val);
 58	/* Using Internal ADC */
 59	regmap_update_bits(afe->regmap, AFE_ADDA_TOP_CON0, 0x1, 0x0);
 60
 61	return 0;
 62}
 63
 64int mt8365_dai_enable_adda_on(struct mtk_base_afe *afe)
 65{
 66	unsigned long flags;
 67	struct mt8365_afe_private *afe_priv = afe->platform_priv;
 68
 69	spin_lock_irqsave(&afe_priv->afe_ctrl_lock, flags);
 70
 71	adda_afe_on_ref_cnt++;
 72	if (adda_afe_on_ref_cnt == 1)
 73		regmap_update_bits(afe->regmap, AFE_ADDA_UL_DL_CON0,
 74				   AFE_ADDA_UL_DL_ADDA_AFE_ON,
 75				   AFE_ADDA_UL_DL_ADDA_AFE_ON);
 76
 77	spin_unlock_irqrestore(&afe_priv->afe_ctrl_lock, flags);
 78
 79	return 0;
 80}
 81
 82int mt8365_dai_disable_adda_on(struct mtk_base_afe *afe)
 83{
 84	unsigned long flags;
 85	struct mt8365_afe_private *afe_priv = afe->platform_priv;
 86
 87	spin_lock_irqsave(&afe_priv->afe_ctrl_lock, flags);
 88
 89	adda_afe_on_ref_cnt--;
 90	if (adda_afe_on_ref_cnt == 0)
 91		regmap_update_bits(afe->regmap, AFE_ADDA_UL_DL_CON0,
 92				   AFE_ADDA_UL_DL_ADDA_AFE_ON,
 93				   ~AFE_ADDA_UL_DL_ADDA_AFE_ON);
 94	else if (adda_afe_on_ref_cnt < 0) {
 95		adda_afe_on_ref_cnt = 0;
 96		dev_warn(afe->dev, "Abnormal adda_on ref count. Force it to 0\n");
 97	}
 98
 99	spin_unlock_irqrestore(&afe_priv->afe_ctrl_lock, flags);
100
101	return 0;
102}
103
104static void mt8365_dai_set_adda_out_enable(struct mtk_base_afe *afe,
105					   bool enable)
106{
107	regmap_update_bits(afe->regmap, AFE_ADDA_DL_SRC2_CON0, 0x1, enable);
108
109	if (enable)
110		mt8365_dai_enable_adda_on(afe);
111	else
112		mt8365_dai_disable_adda_on(afe);
113}
114
115static void mt8365_dai_set_adda_in_enable(struct mtk_base_afe *afe, bool enable)
116{
117	if (enable) {
118		regmap_update_bits(afe->regmap, AFE_ADDA_UL_SRC_CON0, 0x1, 0x1);
119		mt8365_dai_enable_adda_on(afe);
120		/* enable aud_pad_top fifo */
121		regmap_update_bits(afe->regmap, AFE_AUD_PAD_TOP,
122				   0xffffffff, 0x31);
123	} else {
124		/* disable aud_pad_top fifo */
125		regmap_update_bits(afe->regmap, AFE_AUD_PAD_TOP,
126				   0xffffffff, 0x30);
127		regmap_update_bits(afe->regmap, AFE_ADDA_UL_SRC_CON0, 0x1, 0x0);
128		/* de suggest disable ADDA_UL_SRC at least wait 125us */
129		usleep_range(150, 300);
130		mt8365_dai_disable_adda_on(afe);
131	}
132}
133
134static int mt8365_dai_int_adda_startup(struct snd_pcm_substream *substream,
135				       struct snd_soc_dai *dai)
136{
137	struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
138	unsigned int stream = substream->stream;
139
140	mt8365_afe_enable_main_clk(afe);
141
142	if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
143		mt8365_afe_enable_top_cg(afe, MT8365_TOP_CG_DAC);
144		mt8365_afe_enable_top_cg(afe, MT8365_TOP_CG_DAC_PREDIS);
145	} else if (stream == SNDRV_PCM_STREAM_CAPTURE) {
146		mt8365_afe_enable_top_cg(afe, MT8365_TOP_CG_ADC);
147	}
148
149	return 0;
150}
151
152static void mt8365_dai_int_adda_shutdown(struct snd_pcm_substream *substream,
153					 struct snd_soc_dai *dai)
154{
155	struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
156	struct mt8365_afe_private *afe_priv = afe->platform_priv;
157	struct mt8365_be_dai_data *be =
158		&afe_priv->be_data[dai->id - MT8365_AFE_BACKEND_BASE];
159	unsigned int stream = substream->stream;
160
161	if (be->prepared[stream]) {
162		if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
163			mt8365_dai_set_adda_out_enable(afe, false);
164			mt8365_afe_set_i2s_out_enable(afe, false);
165		} else {
166			mt8365_dai_set_adda_in_enable(afe, false);
167		}
168		be->prepared[stream] = false;
169	}
170
171	if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
172		mt8365_afe_disable_top_cg(afe, MT8365_TOP_CG_DAC_PREDIS);
173		mt8365_afe_disable_top_cg(afe, MT8365_TOP_CG_DAC);
174	} else if (stream == SNDRV_PCM_STREAM_CAPTURE) {
175		mt8365_afe_disable_top_cg(afe, MT8365_TOP_CG_ADC);
176	}
177
178	mt8365_afe_disable_main_clk(afe);
179}
180
181static int mt8365_dai_int_adda_prepare(struct snd_pcm_substream *substream,
182				       struct snd_soc_dai *dai)
183{
184	struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
185	struct mt8365_afe_private *afe_priv = afe->platform_priv;
186	struct mt8365_be_dai_data *be =
187		&afe_priv->be_data[dai->id - MT8365_AFE_BACKEND_BASE];
188	unsigned int rate = substream->runtime->rate;
189	int bit_width = snd_pcm_format_width(substream->runtime->format);
190	int ret;
191
192	dev_info(afe->dev, "%s '%s' rate = %u\n", __func__,
193		 snd_pcm_stream_str(substream), rate);
194
195	if (be->prepared[substream->stream]) {
196		dev_info(afe->dev, "%s '%s' prepared already\n",
197			 __func__, snd_pcm_stream_str(substream));
198		return 0;
199	}
200
201	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
202		ret = mt8365_dai_set_adda_out(afe, rate);
203		if (ret)
204			return ret;
205
206		ret = mt8365_afe_set_i2s_out(afe, rate, bit_width);
207		if (ret)
208			return ret;
209
210		mt8365_dai_set_adda_out_enable(afe, true);
211		mt8365_afe_set_i2s_out_enable(afe, true);
212	} else {
213		ret = mt8365_dai_set_adda_in(afe, rate);
214		if (ret)
215			return ret;
216
217		mt8365_dai_set_adda_in_enable(afe, true);
218	}
219	be->prepared[substream->stream] = true;
220	return 0;
221}
222
223static const struct snd_soc_dai_ops mt8365_afe_int_adda_ops = {
224	.startup	= mt8365_dai_int_adda_startup,
225	.shutdown	= mt8365_dai_int_adda_shutdown,
226	.prepare	= mt8365_dai_int_adda_prepare,
227};
228
229static struct snd_soc_dai_driver mtk_dai_adda_driver[] = {
230	{
231		.name = "INT ADDA",
232		.id = MT8365_AFE_IO_INT_ADDA,
233		.playback = {
234			.stream_name = "INT ADDA Playback",
235			.channels_min = 1,
236			.channels_max = 2,
237			.rates = SNDRV_PCM_RATE_8000_48000,
238			.formats = SNDRV_PCM_FMTBIT_S16_LE,
239		},
240		.capture = {
241			.stream_name = "INT ADDA Capture",
242			.channels_min = 1,
243			.channels_max = 2,
244			.rates = SNDRV_PCM_RATE_16000 |
245				 SNDRV_PCM_RATE_32000 |
246				 SNDRV_PCM_RATE_48000,
247			.formats = SNDRV_PCM_FMTBIT_S16_LE |
248				   SNDRV_PCM_FMTBIT_S32_LE,
249		},
250		.ops = &mt8365_afe_int_adda_ops,
251	}
252};
253
254/* DAI Controls */
255
256static const struct snd_kcontrol_new mtk_adda_dl_ch1_mix[] = {
257	SOC_DAPM_SINGLE_AUTODISABLE("GAIN1_OUT_CH1 Switch", AFE_CONN3,
258				    10, 1, 0),
259};
260
261static const struct snd_kcontrol_new mtk_adda_dl_ch2_mix[] = {
262	SOC_DAPM_SINGLE_AUTODISABLE("GAIN1_OUT_CH2 Switch", AFE_CONN4,
263				    11, 1, 0),
264};
265
266static const struct snd_kcontrol_new int_adda_o03_o04_enable_ctl =
267	SOC_DAPM_SINGLE_VIRT("Switch", 1);
268
269/* DAI widget */
270
271static const struct snd_soc_dapm_widget mtk_dai_adda_widgets[] = {
272	SND_SOC_DAPM_SWITCH("INT ADDA O03_O04", SND_SOC_NOPM, 0, 0,
273			    &int_adda_o03_o04_enable_ctl),
274	/* inter-connections */
275	SND_SOC_DAPM_MIXER("ADDA_DL_CH1", SND_SOC_NOPM, 0, 0,
276			   mtk_adda_dl_ch1_mix,
277			   ARRAY_SIZE(mtk_adda_dl_ch1_mix)),
278	SND_SOC_DAPM_MIXER("ADDA_DL_CH2", SND_SOC_NOPM, 0, 0,
279			   mtk_adda_dl_ch2_mix,
280			   ARRAY_SIZE(mtk_adda_dl_ch2_mix)),
281};
282
283/* DAI route */
284
285static const struct snd_soc_dapm_route mtk_dai_adda_routes[] = {
286	{"INT ADDA O03_O04", "Switch", "O03"},
287	{"INT ADDA O03_O04", "Switch", "O04"},
288	{"INT ADDA Playback", NULL, "INT ADDA O03_O04"},
289	{"INT ADDA Playback", NULL, "ADDA_DL_CH1"},
290	{"INT ADDA Playback", NULL, "ADDA_DL_CH2"},
291	{"AIN Mux", "INT ADC", "INT ADDA Capture"},
292	{"ADDA_DL_CH1", "GAIN1_OUT_CH1", "Hostless FM DL"},
293	{"ADDA_DL_CH2", "GAIN1_OUT_CH2", "Hostless FM DL"},
294};
295
296int mt8365_dai_adda_register(struct mtk_base_afe *afe)
297{
298	struct mtk_base_afe_dai *dai;
299
300	dai = devm_kzalloc(afe->dev, sizeof(*dai), GFP_KERNEL);
301	if (!dai)
302		return -ENOMEM;
303	list_add(&dai->list, &afe->sub_dais);
304	dai->dai_drivers = mtk_dai_adda_driver;
305	dai->num_dai_drivers = ARRAY_SIZE(mtk_dai_adda_driver);
306	dai->dapm_widgets = mtk_dai_adda_widgets;
307	dai->num_dapm_widgets = ARRAY_SIZE(mtk_dai_adda_widgets);
308	dai->dapm_routes = mtk_dai_adda_routes;
309	dai->num_dapm_routes = ARRAY_SIZE(mtk_dai_adda_routes);
310	return 0;
311}
~/ Read-only file preview.