Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/sound/soc/mediatek/mt8365/mt8365-dai-adda.c
26488 views
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 <[email protected]>
7
* Alexandre Mergnat <[email protected]>
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
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
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
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
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
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
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
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
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
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
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
313