Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/sound/soc/mediatek/common/mtk-soundcard-driver.c
29268 views
1
// SPDX-License-Identifier: GPL-2.0
2
/*
3
* mtk-soundcard-driver.c -- MediaTek soundcard driver common
4
*
5
* Copyright (c) 2022 MediaTek Inc.
6
* Author: Trevor Wu <[email protected]>
7
*/
8
9
#include <linux/module.h>
10
#include <linux/of.h>
11
#include <linux/of_platform.h>
12
#include <sound/soc.h>
13
14
#include "mtk-dsp-sof-common.h"
15
#include "mtk-soc-card.h"
16
#include "mtk-soundcard-driver.h"
17
18
static int set_card_codec_info(struct snd_soc_card *card,
19
struct device_node *sub_node,
20
struct snd_soc_dai_link *dai_link)
21
{
22
struct device *dev = card->dev;
23
struct device_node *codec_node;
24
int ret;
25
26
codec_node = of_get_child_by_name(sub_node, "codec");
27
if (!codec_node) {
28
dev_dbg(dev, "%s no specified codec: setting dummy.\n", dai_link->name);
29
30
dai_link->codecs = &snd_soc_dummy_dlc;
31
dai_link->num_codecs = 1;
32
dai_link->dynamic = 1;
33
return 0;
34
}
35
36
/* set card codec info */
37
ret = snd_soc_of_get_dai_link_codecs(dev, codec_node, dai_link);
38
39
of_node_put(codec_node);
40
41
if (ret < 0)
42
return dev_err_probe(dev, ret, "%s: codec dai not found\n",
43
dai_link->name);
44
45
return 0;
46
}
47
48
static int set_dailink_daifmt(struct snd_soc_card *card,
49
struct device_node *sub_node,
50
struct snd_soc_dai_link *dai_link)
51
{
52
unsigned int daifmt;
53
const char *str;
54
int ret;
55
struct {
56
char *name;
57
unsigned int val;
58
} of_clk_table[] = {
59
{ "cpu", SND_SOC_DAIFMT_CBC_CFC },
60
{ "codec", SND_SOC_DAIFMT_CBP_CFP },
61
};
62
63
daifmt = snd_soc_daifmt_parse_format(sub_node, NULL);
64
if (daifmt) {
65
dai_link->dai_fmt &= SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK;
66
dai_link->dai_fmt |= daifmt;
67
}
68
69
/*
70
* check "mediatek,clk-provider = xxx"
71
* SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK area
72
*/
73
ret = of_property_read_string(sub_node, "mediatek,clk-provider", &str);
74
if (ret == 0) {
75
int i;
76
77
for (i = 0; i < ARRAY_SIZE(of_clk_table); i++) {
78
if (strcmp(str, of_clk_table[i].name) == 0) {
79
dai_link->dai_fmt &= ~SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK;
80
dai_link->dai_fmt |= of_clk_table[i].val;
81
break;
82
}
83
}
84
}
85
86
return 0;
87
}
88
89
int parse_dai_link_info(struct snd_soc_card *card)
90
{
91
struct device *dev = card->dev;
92
struct snd_soc_dai_link *dai_link;
93
const char *dai_link_name;
94
int ret, i;
95
96
/* Loop over all the dai link sub nodes */
97
for_each_available_child_of_node_scoped(dev->of_node, sub_node) {
98
if (of_property_read_string(sub_node, "link-name",
99
&dai_link_name))
100
return -EINVAL;
101
102
for_each_card_prelinks(card, i, dai_link) {
103
if (!strcmp(dai_link_name, dai_link->name))
104
break;
105
}
106
107
if (i >= card->num_links)
108
return -EINVAL;
109
110
ret = set_card_codec_info(card, sub_node, dai_link);
111
if (ret < 0)
112
return ret;
113
114
ret = set_dailink_daifmt(card, sub_node, dai_link);
115
if (ret < 0)
116
return ret;
117
}
118
119
return 0;
120
}
121
EXPORT_SYMBOL_GPL(parse_dai_link_info);
122
123
void clean_card_reference(struct snd_soc_card *card)
124
{
125
struct snd_soc_dai_link *dai_link;
126
int i;
127
128
/* release codec reference gotten by set_card_codec_info */
129
for_each_card_prelinks(card, i, dai_link)
130
snd_soc_of_put_dai_link_codecs(dai_link);
131
}
132
EXPORT_SYMBOL_GPL(clean_card_reference);
133
134
int mtk_soundcard_startup(struct snd_pcm_substream *substream,
135
enum mtk_pcm_constraint_type ctype)
136
{
137
struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
138
struct mtk_soc_card_data *soc_card = snd_soc_card_get_drvdata(rtd->card);
139
const struct mtk_pcm_constraints_data *mpc = &soc_card->card_data->pcm_constraints[ctype];
140
int ret;
141
142
if (unlikely(!mpc))
143
return -EINVAL;
144
145
ret = snd_pcm_hw_constraint_list(substream->runtime, 0,
146
SNDRV_PCM_HW_PARAM_RATE,
147
mpc->rates);
148
if (ret < 0) {
149
dev_err(rtd->dev, "hw_constraint_list rate failed\n");
150
return ret;
151
}
152
153
ret = snd_pcm_hw_constraint_list(substream->runtime, 0,
154
SNDRV_PCM_HW_PARAM_CHANNELS,
155
mpc->channels);
156
if (ret < 0) {
157
dev_err(rtd->dev, "hw_constraint_list channel failed\n");
158
return ret;
159
}
160
161
return 0;
162
}
163
EXPORT_SYMBOL_GPL(mtk_soundcard_startup);
164
165
static int mtk_soundcard_playback_startup(struct snd_pcm_substream *substream)
166
{
167
return mtk_soundcard_startup(substream, MTK_CONSTRAINT_PLAYBACK);
168
}
169
170
const struct snd_soc_ops mtk_soundcard_common_playback_ops = {
171
.startup = mtk_soundcard_playback_startup,
172
};
173
EXPORT_SYMBOL_GPL(mtk_soundcard_common_playback_ops);
174
175
static int mtk_soundcard_capture_startup(struct snd_pcm_substream *substream)
176
{
177
return mtk_soundcard_startup(substream, MTK_CONSTRAINT_CAPTURE);
178
}
179
180
const struct snd_soc_ops mtk_soundcard_common_capture_ops = {
181
.startup = mtk_soundcard_capture_startup,
182
};
183
EXPORT_SYMBOL_GPL(mtk_soundcard_common_capture_ops);
184
185
int mtk_soundcard_common_probe(struct platform_device *pdev)
186
{
187
struct device_node *platform_node, *adsp_node, *accdet_node;
188
struct snd_soc_component *accdet_comp;
189
struct platform_device *accdet_pdev;
190
const struct mtk_soundcard_pdata *pdata;
191
struct mtk_soc_card_data *soc_card_data;
192
struct snd_soc_dai_link *orig_dai_link, *dai_link;
193
struct snd_soc_jack *jacks;
194
struct snd_soc_card *card;
195
int i, orig_num_links, ret;
196
bool needs_legacy_probe;
197
198
pdata = device_get_match_data(&pdev->dev);
199
if (!pdata)
200
return -EINVAL;
201
202
card = pdata->card_data->card;
203
card->dev = &pdev->dev;
204
orig_dai_link = card->dai_link;
205
orig_num_links = card->num_links;
206
207
ret = snd_soc_of_parse_card_name(card, "model");
208
if (ret)
209
return ret;
210
211
if (!card->name) {
212
if (!pdata->card_name)
213
return -EINVAL;
214
215
card->name = pdata->card_name;
216
}
217
218
needs_legacy_probe = !of_property_present(pdev->dev.of_node, "audio-routing");
219
if (needs_legacy_probe) {
220
/*
221
* If we have no .soc_probe() callback there's no way of using
222
* any legacy probe mechanism, as that cannot not be generic.
223
*/
224
if (!pdata->soc_probe)
225
return -EINVAL;
226
227
dev_info_once(&pdev->dev, "audio-routing not found: using legacy probe\n");
228
} else {
229
ret = snd_soc_of_parse_audio_routing(card, "audio-routing");
230
if (ret)
231
return ret;
232
}
233
234
soc_card_data = devm_kzalloc(&pdev->dev, sizeof(*soc_card_data), GFP_KERNEL);
235
if (!soc_card_data)
236
return -ENOMEM;
237
238
soc_card_data->card_data = pdata->card_data;
239
240
jacks = devm_kcalloc(card->dev, soc_card_data->card_data->num_jacks,
241
sizeof(*jacks), GFP_KERNEL);
242
if (!jacks)
243
return -ENOMEM;
244
245
soc_card_data->card_data->jacks = jacks;
246
247
accdet_node = of_parse_phandle(pdev->dev.of_node, "mediatek,accdet", 0);
248
if (accdet_node) {
249
accdet_pdev = of_find_device_by_node(accdet_node);
250
if (accdet_pdev) {
251
accdet_comp = snd_soc_lookup_component(&accdet_pdev->dev, NULL);
252
if (accdet_comp)
253
soc_card_data->accdet = accdet_comp;
254
else
255
dev_err(&pdev->dev, "No sound component found from mediatek,accdet property\n");
256
257
put_device(&accdet_pdev->dev);
258
} else {
259
dev_err(&pdev->dev, "No device found from mediatek,accdet property\n");
260
}
261
262
of_node_put(accdet_node);
263
}
264
265
platform_node = of_parse_phandle(pdev->dev.of_node, "mediatek,platform", 0);
266
if (!platform_node)
267
return dev_err_probe(&pdev->dev, -EINVAL,
268
"Property mediatek,platform missing or invalid\n");
269
270
/* Check if this SoC has an Audio DSP */
271
if (pdata->sof_priv)
272
adsp_node = of_parse_phandle(pdev->dev.of_node, "mediatek,adsp", 0);
273
else
274
adsp_node = NULL;
275
276
if (adsp_node) {
277
if (of_property_present(pdev->dev.of_node, "mediatek,dai-link")) {
278
ret = mtk_sof_dailink_parse_of(card, pdev->dev.of_node,
279
"mediatek,dai-link",
280
card->dai_link, card->num_links);
281
if (ret) {
282
of_node_put(adsp_node);
283
of_node_put(platform_node);
284
return dev_err_probe(&pdev->dev, ret,
285
"Cannot parse mediatek,dai-link\n");
286
}
287
}
288
289
soc_card_data->sof_priv = pdata->sof_priv;
290
card->probe = mtk_sof_card_probe;
291
card->late_probe = mtk_sof_card_late_probe;
292
if (!card->topology_shortname_created) {
293
snprintf(card->topology_shortname, 32, "sof-%s", card->name);
294
card->topology_shortname_created = true;
295
}
296
card->name = card->topology_shortname;
297
}
298
299
/*
300
* Regardless of whether the ADSP is wanted and/or present in a machine
301
* specific device tree or not and regardless of whether any AFE_SOF
302
* link is present, we have to make sure that the platforms->of_node
303
* is not NULL, and set to either ADSP (adsp_node) or AFE (platform_node).
304
*/
305
for_each_card_prelinks(card, i, dai_link) {
306
if (adsp_node && !strncmp(dai_link->name, "AFE_SOF", strlen("AFE_SOF")))
307
dai_link->platforms->of_node = adsp_node;
308
else if (!dai_link->platforms->name && !dai_link->platforms->of_node)
309
dai_link->platforms->of_node = platform_node;
310
}
311
312
if (!needs_legacy_probe) {
313
ret = parse_dai_link_info(card);
314
if (ret)
315
goto err_restore_dais;
316
} else {
317
if (adsp_node)
318
of_node_put(adsp_node);
319
of_node_put(platform_node);
320
}
321
322
if (pdata->soc_probe) {
323
ret = pdata->soc_probe(soc_card_data, needs_legacy_probe);
324
if (ret) {
325
if (!needs_legacy_probe)
326
clean_card_reference(card);
327
goto err_restore_dais;
328
}
329
}
330
snd_soc_card_set_drvdata(card, soc_card_data);
331
332
ret = devm_snd_soc_register_card(&pdev->dev, card);
333
334
if (!needs_legacy_probe)
335
clean_card_reference(card);
336
337
if (ret) {
338
dev_err_probe(&pdev->dev, ret, "Cannot register card\n");
339
goto err_restore_dais;
340
}
341
342
return 0;
343
344
err_restore_dais:
345
card->dai_link = orig_dai_link;
346
card->num_links = orig_num_links;
347
return ret;
348
}
349
EXPORT_SYMBOL_GPL(mtk_soundcard_common_probe);
350
351