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
17 static int adda_afe_on_ref_cnt;
18
19 /* DAI Drivers */
20
mt8365_dai_set_adda_out(struct mtk_base_afe * afe,unsigned int rate)21 static 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
mt8365_dai_set_adda_in(struct mtk_base_afe * afe,unsigned int rate)50 static 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
mt8365_dai_enable_adda_on(struct mtk_base_afe * afe)64 int 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
mt8365_dai_disable_adda_on(struct mtk_base_afe * afe)82 int 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
mt8365_dai_set_adda_out_enable(struct mtk_base_afe * afe,bool enable)104 static 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
mt8365_dai_set_adda_in_enable(struct mtk_base_afe * afe,bool enable)115 static 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
mt8365_dai_int_adda_startup(struct snd_pcm_substream * substream,struct snd_soc_dai * dai)134 static 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
mt8365_dai_int_adda_shutdown(struct snd_pcm_substream * substream,struct snd_soc_dai * dai)152 static 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
mt8365_dai_int_adda_prepare(struct snd_pcm_substream * substream,struct snd_soc_dai * dai)181 static 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
223 static 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
229 static 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
256 static 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
261 static 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
266 static const struct snd_kcontrol_new int_adda_o03_o04_enable_ctl =
267 SOC_DAPM_SINGLE_VIRT("Switch", 1);
268
269 /* DAI widget */
270
271 static 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
285 static 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
mt8365_dai_adda_register(struct mtk_base_afe * afe)296 int 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 }
312