Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/sound/soc/amd/acp/acp-sdw-sof-mach.c
26481 views
1
// SPDX-License-Identifier: GPL-2.0-only
2
// Copyright(c) 2024 Advanced Micro Devices, Inc.
3
4
/*
5
* acp-sdw-sof-mach - ASoC Machine driver for AMD SoundWire platforms
6
*/
7
8
#include <linux/bitmap.h>
9
#include <linux/device.h>
10
#include <linux/dmi.h>
11
#include <linux/module.h>
12
#include <linux/soundwire/sdw.h>
13
#include <linux/soundwire/sdw_type.h>
14
#include <sound/soc.h>
15
#include <sound/soc-acpi.h>
16
#include "soc_amd_sdw_common.h"
17
#include "../../codecs/rt711.h"
18
19
static unsigned long sof_sdw_quirk = RT711_JD1;
20
static int quirk_override = -1;
21
module_param_named(quirk, quirk_override, int, 0444);
22
MODULE_PARM_DESC(quirk, "Board-specific quirk override");
23
24
static void log_quirks(struct device *dev)
25
{
26
if (SOC_JACK_JDSRC(sof_sdw_quirk))
27
dev_dbg(dev, "quirk realtek,jack-detect-source %ld\n",
28
SOC_JACK_JDSRC(sof_sdw_quirk));
29
if (sof_sdw_quirk & ASOC_SDW_ACP_DMIC)
30
dev_dbg(dev, "quirk SOC_SDW_ACP_DMIC enabled\n");
31
}
32
33
static int sof_sdw_quirk_cb(const struct dmi_system_id *id)
34
{
35
sof_sdw_quirk = (unsigned long)id->driver_data;
36
return 1;
37
}
38
39
static const struct dmi_system_id sof_sdw_quirk_table[] = {
40
{
41
.callback = sof_sdw_quirk_cb,
42
.matches = {
43
DMI_MATCH(DMI_SYS_VENDOR, "AMD"),
44
DMI_MATCH(DMI_PRODUCT_NAME, "Birman-PHX"),
45
},
46
.driver_data = (void *)RT711_JD2,
47
},
48
{}
49
};
50
51
static struct snd_soc_dai_link_component platform_component[] = {
52
{
53
/* name might be overridden during probe */
54
.name = "0000:04:00.5",
55
}
56
};
57
58
static const struct snd_soc_ops sdw_ops = {
59
.startup = asoc_sdw_startup,
60
.prepare = asoc_sdw_prepare,
61
.trigger = asoc_sdw_trigger,
62
.hw_params = asoc_sdw_hw_params,
63
.hw_free = asoc_sdw_hw_free,
64
.shutdown = asoc_sdw_shutdown,
65
};
66
67
static const char * const type_strings[] = {"SimpleJack", "SmartAmp", "SmartMic"};
68
69
static int create_sdw_dailink(struct snd_soc_card *card,
70
struct asoc_sdw_dailink *sof_dai,
71
struct snd_soc_dai_link **dai_links,
72
int *be_id, struct snd_soc_codec_conf **codec_conf)
73
{
74
struct device *dev = card->dev;
75
struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card);
76
struct amd_mc_ctx *amd_ctx = (struct amd_mc_ctx *)ctx->private;
77
struct asoc_sdw_endpoint *sof_end;
78
int cpu_pin_id;
79
int stream;
80
int ret;
81
82
list_for_each_entry(sof_end, &sof_dai->endpoints, list) {
83
if (sof_end->name_prefix) {
84
(*codec_conf)->dlc.name = sof_end->codec_name;
85
(*codec_conf)->name_prefix = sof_end->name_prefix;
86
(*codec_conf)++;
87
}
88
89
if (sof_end->include_sidecar) {
90
ret = sof_end->codec_info->add_sidecar(card, dai_links, codec_conf);
91
if (ret)
92
return ret;
93
}
94
}
95
96
for_each_pcm_streams(stream) {
97
static const char * const sdw_stream_name[] = {
98
"SDW%d-PIN%d-PLAYBACK",
99
"SDW%d-PIN%d-CAPTURE",
100
"SDW%d-PIN%d-PLAYBACK-%s",
101
"SDW%d-PIN%d-CAPTURE-%s",
102
};
103
struct snd_soc_dai_link_ch_map *codec_maps;
104
struct snd_soc_dai_link_component *codecs;
105
struct snd_soc_dai_link_component *cpus;
106
int num_cpus = hweight32(sof_dai->link_mask[stream]);
107
int num_codecs = sof_dai->num_devs[stream];
108
int playback, capture;
109
int j = 0;
110
char *name;
111
112
if (!sof_dai->num_devs[stream])
113
continue;
114
115
sof_end = list_first_entry(&sof_dai->endpoints,
116
struct asoc_sdw_endpoint, list);
117
118
*be_id = sof_end->dai_info->dailink[stream];
119
if (*be_id < 0) {
120
dev_err(dev, "Invalid dailink id %d\n", *be_id);
121
return -EINVAL;
122
}
123
124
switch (amd_ctx->acp_rev) {
125
case ACP63_PCI_REV:
126
ret = get_acp63_cpu_pin_id(ffs(sof_end->link_mask - 1),
127
*be_id, &cpu_pin_id, dev);
128
if (ret)
129
return ret;
130
break;
131
case ACP70_PCI_REV:
132
case ACP71_PCI_REV:
133
case ACP72_PCI_REV:
134
ret = get_acp70_cpu_pin_id(ffs(sof_end->link_mask - 1),
135
*be_id, &cpu_pin_id, dev);
136
if (ret)
137
return ret;
138
break;
139
default:
140
return -EINVAL;
141
}
142
/* create stream name according to first link id */
143
if (ctx->append_dai_type) {
144
name = devm_kasprintf(dev, GFP_KERNEL,
145
sdw_stream_name[stream + 2],
146
ffs(sof_end->link_mask) - 1,
147
cpu_pin_id,
148
type_strings[sof_end->dai_info->dai_type]);
149
} else {
150
name = devm_kasprintf(dev, GFP_KERNEL,
151
sdw_stream_name[stream],
152
ffs(sof_end->link_mask) - 1,
153
cpu_pin_id);
154
}
155
if (!name)
156
return -ENOMEM;
157
158
cpus = devm_kcalloc(dev, num_cpus, sizeof(*cpus), GFP_KERNEL);
159
if (!cpus)
160
return -ENOMEM;
161
162
codecs = devm_kcalloc(dev, num_codecs, sizeof(*codecs), GFP_KERNEL);
163
if (!codecs)
164
return -ENOMEM;
165
166
codec_maps = devm_kcalloc(dev, num_codecs, sizeof(*codec_maps), GFP_KERNEL);
167
if (!codec_maps)
168
return -ENOMEM;
169
170
list_for_each_entry(sof_end, &sof_dai->endpoints, list) {
171
if (!sof_end->dai_info->direction[stream])
172
continue;
173
174
int link_num = ffs(sof_end->link_mask) - 1;
175
176
cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL,
177
"SDW%d Pin%d",
178
link_num, cpu_pin_id);
179
dev_dbg(dev, "cpu->dai_name:%s\n", cpus->dai_name);
180
if (!cpus->dai_name)
181
return -ENOMEM;
182
183
codec_maps[j].cpu = 0;
184
codec_maps[j].codec = j;
185
186
codecs[j].name = sof_end->codec_name;
187
codecs[j].dai_name = sof_end->dai_info->dai_name;
188
j++;
189
}
190
191
WARN_ON(j != num_codecs);
192
193
playback = (stream == SNDRV_PCM_STREAM_PLAYBACK);
194
capture = (stream == SNDRV_PCM_STREAM_CAPTURE);
195
196
asoc_sdw_init_dai_link(dev, *dai_links, be_id, name, playback, capture,
197
cpus, num_cpus, platform_component,
198
ARRAY_SIZE(platform_component), codecs, num_codecs,
199
1, asoc_sdw_rtd_init, &sdw_ops);
200
201
/*
202
* SoundWire DAILINKs use 'stream' functions and Bank Switch operations
203
* based on wait_for_completion(), tag them as 'nonatomic'.
204
*/
205
(*dai_links)->nonatomic = true;
206
(*dai_links)->ch_maps = codec_maps;
207
208
list_for_each_entry(sof_end, &sof_dai->endpoints, list) {
209
if (sof_end->dai_info->init)
210
sof_end->dai_info->init(card, *dai_links,
211
sof_end->codec_info,
212
playback);
213
}
214
215
(*dai_links)++;
216
}
217
218
return 0;
219
}
220
221
static int create_sdw_dailinks(struct snd_soc_card *card,
222
struct snd_soc_dai_link **dai_links, int *be_id,
223
struct asoc_sdw_dailink *sof_dais,
224
struct snd_soc_codec_conf **codec_conf)
225
{
226
int ret;
227
228
/* generate DAI links by each sdw link */
229
while (sof_dais->initialised) {
230
int current_be_id = 0;
231
232
ret = create_sdw_dailink(card, sof_dais, dai_links,
233
&current_be_id, codec_conf);
234
if (ret)
235
return ret;
236
237
/* Update the be_id to match the highest ID used for SDW link */
238
if (*be_id < current_be_id)
239
*be_id = current_be_id;
240
241
sof_dais++;
242
}
243
244
return 0;
245
}
246
247
static int create_dmic_dailinks(struct snd_soc_card *card,
248
struct snd_soc_dai_link **dai_links, int *be_id, int no_pcm)
249
{
250
struct device *dev = card->dev;
251
int ret;
252
253
ret = asoc_sdw_init_simple_dai_link(dev, *dai_links, be_id, "acp-dmic-codec",
254
0, 1, // DMIC only supports capture
255
"acp-sof-dmic", platform_component->name,
256
"dmic-codec", "dmic-hifi", no_pcm,
257
asoc_sdw_dmic_init, NULL);
258
if (ret)
259
return ret;
260
261
(*dai_links)++;
262
263
return 0;
264
}
265
266
static int sof_card_dai_links_create(struct snd_soc_card *card)
267
{
268
struct device *dev = card->dev;
269
struct snd_soc_acpi_mach *mach = dev_get_platdata(card->dev);
270
int sdw_be_num = 0, dmic_num = 0;
271
struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card);
272
struct snd_soc_acpi_mach_params *mach_params = &mach->mach_params;
273
struct asoc_sdw_endpoint *sof_ends __free(kfree) = NULL;
274
struct asoc_sdw_dailink *sof_dais __free(kfree) = NULL;
275
struct snd_soc_codec_conf *codec_conf;
276
struct snd_soc_dai_link *dai_links;
277
int num_devs = 0;
278
int num_ends = 0;
279
int num_links;
280
int be_id = 0;
281
int ret;
282
283
ret = asoc_sdw_count_sdw_endpoints(card, &num_devs, &num_ends);
284
if (ret < 0) {
285
dev_err(dev, "failed to count devices/endpoints: %d\n", ret);
286
return ret;
287
}
288
289
/* One per DAI link, worst case is a DAI link for every endpoint */
290
sof_dais = kcalloc(num_ends, sizeof(*sof_dais), GFP_KERNEL);
291
if (!sof_dais)
292
return -ENOMEM;
293
294
/* One per endpoint, ie. each DAI on each codec/amp */
295
sof_ends = kcalloc(num_ends, sizeof(*sof_ends), GFP_KERNEL);
296
if (!sof_ends)
297
return -ENOMEM;
298
299
ret = asoc_sdw_parse_sdw_endpoints(card, sof_dais, sof_ends, &num_devs);
300
if (ret < 0)
301
return ret;
302
303
sdw_be_num = ret;
304
305
/* enable dmic */
306
if (sof_sdw_quirk & ASOC_SDW_ACP_DMIC || mach_params->dmic_num)
307
dmic_num = 1;
308
309
dev_dbg(dev, "sdw %d, dmic %d", sdw_be_num, dmic_num);
310
311
codec_conf = devm_kcalloc(dev, num_devs, sizeof(*codec_conf), GFP_KERNEL);
312
if (!codec_conf)
313
return -ENOMEM;
314
315
/* allocate BE dailinks */
316
num_links = sdw_be_num + dmic_num;
317
dai_links = devm_kcalloc(dev, num_links, sizeof(*dai_links), GFP_KERNEL);
318
if (!dai_links)
319
return -ENOMEM;
320
321
card->codec_conf = codec_conf;
322
card->num_configs = num_devs;
323
card->dai_link = dai_links;
324
card->num_links = num_links;
325
326
/* SDW */
327
if (sdw_be_num) {
328
ret = create_sdw_dailinks(card, &dai_links, &be_id,
329
sof_dais, &codec_conf);
330
if (ret)
331
return ret;
332
}
333
334
/* dmic */
335
if (dmic_num > 0) {
336
if (ctx->ignore_internal_dmic) {
337
dev_warn(dev, "Ignoring ACP DMIC\n");
338
} else {
339
ret = create_dmic_dailinks(card, &dai_links, &be_id, 1);
340
if (ret)
341
return ret;
342
}
343
}
344
345
WARN_ON(codec_conf != card->codec_conf + card->num_configs);
346
WARN_ON(dai_links != card->dai_link + card->num_links);
347
348
return ret;
349
}
350
351
static int mc_probe(struct platform_device *pdev)
352
{
353
struct snd_soc_acpi_mach *mach = dev_get_platdata(&pdev->dev);
354
struct snd_soc_card *card;
355
struct amd_mc_ctx *amd_ctx;
356
struct asoc_sdw_mc_private *ctx;
357
int amp_num = 0, i;
358
int ret;
359
360
amd_ctx = devm_kzalloc(&pdev->dev, sizeof(*amd_ctx), GFP_KERNEL);
361
if (!amd_ctx)
362
return -ENOMEM;
363
364
amd_ctx->acp_rev = mach->mach_params.subsystem_rev;
365
amd_ctx->max_sdw_links = ACP63_SDW_MAX_LINKS;
366
ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL);
367
if (!ctx)
368
return -ENOMEM;
369
ctx->codec_info_list_count = asoc_sdw_get_codec_info_list_count();
370
ctx->private = amd_ctx;
371
card = &ctx->card;
372
card->dev = &pdev->dev;
373
card->name = "amd-soundwire";
374
card->owner = THIS_MODULE;
375
card->late_probe = asoc_sdw_card_late_probe;
376
377
snd_soc_card_set_drvdata(card, ctx);
378
379
dmi_check_system(sof_sdw_quirk_table);
380
381
if (quirk_override != -1) {
382
dev_info(card->dev, "Overriding quirk 0x%lx => 0x%x\n",
383
sof_sdw_quirk, quirk_override);
384
sof_sdw_quirk = quirk_override;
385
}
386
387
log_quirks(card->dev);
388
389
ctx->mc_quirk = sof_sdw_quirk;
390
/* reset amp_num to ensure amp_num++ starts from 0 in each probe */
391
for (i = 0; i < ctx->codec_info_list_count; i++)
392
codec_info_list[i].amp_num = 0;
393
394
ret = sof_card_dai_links_create(card);
395
if (ret < 0)
396
return ret;
397
398
/*
399
* the default amp_num is zero for each codec and
400
* amp_num will only be increased for active amp
401
* codecs on used platform
402
*/
403
for (i = 0; i < ctx->codec_info_list_count; i++)
404
amp_num += codec_info_list[i].amp_num;
405
406
card->components = devm_kasprintf(card->dev, GFP_KERNEL,
407
" cfg-amp:%d", amp_num);
408
if (!card->components)
409
return -ENOMEM;
410
411
/* Register the card */
412
ret = devm_snd_soc_register_card(card->dev, card);
413
if (ret) {
414
dev_err_probe(card->dev, ret, "snd_soc_register_card failed %d\n", ret);
415
asoc_sdw_mc_dailink_exit_loop(card);
416
return ret;
417
}
418
419
platform_set_drvdata(pdev, card);
420
421
return ret;
422
}
423
424
static void mc_remove(struct platform_device *pdev)
425
{
426
struct snd_soc_card *card = platform_get_drvdata(pdev);
427
428
asoc_sdw_mc_dailink_exit_loop(card);
429
}
430
431
static const struct platform_device_id mc_id_table[] = {
432
{ "amd_sof_sdw", },
433
{}
434
};
435
MODULE_DEVICE_TABLE(platform, mc_id_table);
436
437
static struct platform_driver sof_sdw_driver = {
438
.driver = {
439
.name = "amd_sof_sdw",
440
.pm = &snd_soc_pm_ops,
441
},
442
.probe = mc_probe,
443
.remove = mc_remove,
444
.id_table = mc_id_table,
445
};
446
447
module_platform_driver(sof_sdw_driver);
448
449
MODULE_DESCRIPTION("ASoC AMD SoundWire Generic Machine driver");
450
MODULE_AUTHOR("Vijendar Mukunda <[email protected]");
451
MODULE_LICENSE("GPL");
452
MODULE_IMPORT_NS("SND_SOC_SDW_UTILS");
453
MODULE_IMPORT_NS("SND_SOC_AMD_SDW_MACH");
454
455