Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/sound/soc/sdw_utils/soc_sdw_utils.c
51680 views
1
// SPDX-License-Identifier: GPL-2.0-only
2
// This file incorporates work covered by the following copyright notice:
3
// Copyright (c) 2020 Intel Corporation
4
// Copyright(c) 2024 Advanced Micro Devices, Inc.
5
/*
6
* soc-sdw-utils.c - common SoundWire machine driver helper functions
7
*/
8
9
#include <linux/device.h>
10
#include <linux/module.h>
11
#include <linux/soundwire/sdw.h>
12
#include <linux/soundwire/sdw_type.h>
13
#include <sound/sdca_function.h>
14
#include <sound/soc_sdw_utils.h>
15
16
static const struct snd_soc_dapm_widget generic_dmic_widgets[] = {
17
SND_SOC_DAPM_MIC("DMIC", NULL),
18
};
19
20
static const struct snd_soc_dapm_widget generic_jack_widgets[] = {
21
SND_SOC_DAPM_HP("Headphone", NULL),
22
SND_SOC_DAPM_MIC("Headset Mic", NULL),
23
};
24
25
static const struct snd_kcontrol_new generic_jack_controls[] = {
26
SOC_DAPM_PIN_SWITCH("Headphone"),
27
SOC_DAPM_PIN_SWITCH("Headset Mic"),
28
};
29
30
static const struct snd_soc_dapm_widget generic_spk_widgets[] = {
31
SND_SOC_DAPM_SPK("Speaker", NULL),
32
};
33
34
static const struct snd_kcontrol_new generic_spk_controls[] = {
35
SOC_DAPM_PIN_SWITCH("Speaker"),
36
};
37
38
static const struct snd_soc_dapm_widget lr_spk_widgets[] = {
39
SND_SOC_DAPM_SPK("Left Spk", NULL),
40
SND_SOC_DAPM_SPK("Right Spk", NULL),
41
};
42
43
static const struct snd_soc_dapm_widget lr_4spk_widgets[] = {
44
SND_SOC_DAPM_SPK("Left Spk", NULL),
45
SND_SOC_DAPM_SPK("Right Spk", NULL),
46
SND_SOC_DAPM_SPK("Left Spk2", NULL),
47
SND_SOC_DAPM_SPK("Right Spk2", NULL),
48
};
49
50
static const struct snd_kcontrol_new lr_spk_controls[] = {
51
SOC_DAPM_PIN_SWITCH("Left Spk"),
52
SOC_DAPM_PIN_SWITCH("Right Spk"),
53
};
54
55
static const struct snd_kcontrol_new lr_4spk_controls[] = {
56
SOC_DAPM_PIN_SWITCH("Left Spk"),
57
SOC_DAPM_PIN_SWITCH("Right Spk"),
58
SOC_DAPM_PIN_SWITCH("Left Spk2"),
59
SOC_DAPM_PIN_SWITCH("Right Spk2"),
60
};
61
62
static const struct snd_soc_dapm_widget rt700_widgets[] = {
63
SND_SOC_DAPM_HP("Headphones", NULL),
64
SND_SOC_DAPM_MIC("AMIC", NULL),
65
SND_SOC_DAPM_SPK("Speaker", NULL),
66
};
67
68
static const struct snd_kcontrol_new rt700_controls[] = {
69
SOC_DAPM_PIN_SWITCH("Headphones"),
70
SOC_DAPM_PIN_SWITCH("AMIC"),
71
SOC_DAPM_PIN_SWITCH("Speaker"),
72
};
73
74
struct asoc_sdw_codec_info codec_info_list[] = {
75
{
76
.part_id = 0x0000, /* TAS2783A */
77
.name_prefix = "tas2783",
78
.dais = {
79
{
80
.direction = {true, true},
81
.dai_name = "tas2783-codec",
82
.dai_type = SOC_SDW_DAI_TYPE_AMP,
83
.dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_AMP_IN_DAI_ID},
84
.init = asoc_sdw_ti_amp_init,
85
.rtd_init = asoc_sdw_ti_spk_rtd_init,
86
.controls = lr_4spk_controls,
87
.num_controls = ARRAY_SIZE(lr_4spk_controls),
88
.widgets = lr_4spk_widgets,
89
.num_widgets = ARRAY_SIZE(lr_4spk_widgets),
90
},
91
},
92
.dai_num = 1,
93
},
94
{
95
.part_id = 0x700,
96
.name_prefix = "rt700",
97
.dais = {
98
{
99
.direction = {true, true},
100
.dai_name = "rt700-aif1",
101
.dai_type = SOC_SDW_DAI_TYPE_JACK,
102
.dailink = {SOC_SDW_JACK_OUT_DAI_ID, SOC_SDW_JACK_IN_DAI_ID},
103
.rtd_init = asoc_sdw_rt700_rtd_init,
104
.controls = rt700_controls,
105
.num_controls = ARRAY_SIZE(rt700_controls),
106
.widgets = rt700_widgets,
107
.num_widgets = ARRAY_SIZE(rt700_widgets),
108
},
109
},
110
.dai_num = 1,
111
},
112
{
113
.part_id = 0x711,
114
.name_prefix = "rt711",
115
.version_id = 3,
116
.dais = {
117
{
118
.direction = {true, true},
119
.dai_name = "rt711-sdca-aif1",
120
.dai_type = SOC_SDW_DAI_TYPE_JACK,
121
.dailink = {SOC_SDW_JACK_OUT_DAI_ID, SOC_SDW_JACK_IN_DAI_ID},
122
.init = asoc_sdw_rt_sdca_jack_init,
123
.exit = asoc_sdw_rt_sdca_jack_exit,
124
.rtd_init = asoc_sdw_rt_sdca_jack_rtd_init,
125
.controls = generic_jack_controls,
126
.num_controls = ARRAY_SIZE(generic_jack_controls),
127
.widgets = generic_jack_widgets,
128
.num_widgets = ARRAY_SIZE(generic_jack_widgets),
129
},
130
},
131
.dai_num = 1,
132
},
133
{
134
.part_id = 0x711,
135
.name_prefix = "rt711",
136
.version_id = 2,
137
.dais = {
138
{
139
.direction = {true, true},
140
.dai_name = "rt711-aif1",
141
.dai_type = SOC_SDW_DAI_TYPE_JACK,
142
.dailink = {SOC_SDW_JACK_OUT_DAI_ID, SOC_SDW_JACK_IN_DAI_ID},
143
.init = asoc_sdw_rt711_init,
144
.exit = asoc_sdw_rt711_exit,
145
.rtd_init = asoc_sdw_rt711_rtd_init,
146
.controls = generic_jack_controls,
147
.num_controls = ARRAY_SIZE(generic_jack_controls),
148
.widgets = generic_jack_widgets,
149
.num_widgets = ARRAY_SIZE(generic_jack_widgets),
150
},
151
},
152
.dai_num = 1,
153
},
154
{
155
.part_id = 0x712,
156
.name_prefix = "rt712",
157
.version_id = 3,
158
.dais = {
159
{
160
.direction = {true, true},
161
.dai_name = "rt712-sdca-aif1",
162
.dai_type = SOC_SDW_DAI_TYPE_JACK,
163
.dailink = {SOC_SDW_JACK_OUT_DAI_ID, SOC_SDW_JACK_IN_DAI_ID},
164
.init = asoc_sdw_rt_sdca_jack_init,
165
.exit = asoc_sdw_rt_sdca_jack_exit,
166
.rtd_init = asoc_sdw_rt_sdca_jack_rtd_init,
167
.controls = generic_jack_controls,
168
.num_controls = ARRAY_SIZE(generic_jack_controls),
169
.widgets = generic_jack_widgets,
170
.num_widgets = ARRAY_SIZE(generic_jack_widgets),
171
},
172
{
173
.direction = {true, false},
174
.dai_name = "rt712-sdca-aif2",
175
.component_name = "rt712",
176
.dai_type = SOC_SDW_DAI_TYPE_AMP,
177
.dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_UNUSED_DAI_ID},
178
.init = asoc_sdw_rt_amp_init,
179
.exit = asoc_sdw_rt_amp_exit,
180
.rtd_init = asoc_sdw_rt_mf_sdca_spk_rtd_init,
181
.controls = generic_spk_controls,
182
.num_controls = ARRAY_SIZE(generic_spk_controls),
183
.widgets = generic_spk_widgets,
184
.num_widgets = ARRAY_SIZE(generic_spk_widgets),
185
},
186
{
187
.direction = {false, true},
188
.dai_name = "rt712-sdca-aif3",
189
.dai_type = SOC_SDW_DAI_TYPE_MIC,
190
.dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_DMIC_DAI_ID},
191
.rtd_init = asoc_sdw_rt_dmic_rtd_init,
192
},
193
},
194
.dai_num = 3,
195
},
196
{
197
.part_id = 0x1712,
198
.name_prefix = "rt712-dmic",
199
.version_id = 3,
200
.dais = {
201
{
202
.direction = {false, true},
203
.dai_name = "rt712-sdca-dmic-aif1",
204
.dai_type = SOC_SDW_DAI_TYPE_MIC,
205
.dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_DMIC_DAI_ID},
206
.rtd_init = asoc_sdw_rt_dmic_rtd_init,
207
},
208
},
209
.dai_num = 1,
210
},
211
{
212
.part_id = 0x713,
213
.name_prefix = "rt713",
214
.version_id = 3,
215
.dais = {
216
{
217
.direction = {true, true},
218
.dai_name = "rt712-sdca-aif1",
219
.dai_type = SOC_SDW_DAI_TYPE_JACK,
220
.dailink = {SOC_SDW_JACK_OUT_DAI_ID, SOC_SDW_JACK_IN_DAI_ID},
221
.init = asoc_sdw_rt_sdca_jack_init,
222
.exit = asoc_sdw_rt_sdca_jack_exit,
223
.rtd_init = asoc_sdw_rt_sdca_jack_rtd_init,
224
.controls = generic_jack_controls,
225
.num_controls = ARRAY_SIZE(generic_jack_controls),
226
.widgets = generic_jack_widgets,
227
.num_widgets = ARRAY_SIZE(generic_jack_widgets),
228
},
229
{
230
.direction = {false, true},
231
.dai_name = "rt712-sdca-aif3",
232
.dai_type = SOC_SDW_DAI_TYPE_MIC,
233
.dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_DMIC_DAI_ID},
234
.rtd_init = asoc_sdw_rt_dmic_rtd_init,
235
},
236
},
237
.dai_num = 2,
238
},
239
{
240
.part_id = 0x1713,
241
.name_prefix = "rt713-dmic",
242
.version_id = 3,
243
.dais = {
244
{
245
.direction = {false, true},
246
.dai_name = "rt712-sdca-dmic-aif1",
247
.dai_type = SOC_SDW_DAI_TYPE_MIC,
248
.dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_DMIC_DAI_ID},
249
.rtd_init = asoc_sdw_rt_dmic_rtd_init,
250
},
251
},
252
.dai_num = 1,
253
},
254
{
255
.part_id = 0x1308,
256
.name_prefix = "rt1308",
257
.acpi_id = "10EC1308",
258
.dais = {
259
{
260
.direction = {true, false},
261
.dai_name = "rt1308-aif",
262
.component_name = "rt1308",
263
.dai_type = SOC_SDW_DAI_TYPE_AMP,
264
.dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_UNUSED_DAI_ID},
265
.init = asoc_sdw_rt_amp_init,
266
.exit = asoc_sdw_rt_amp_exit,
267
.rtd_init = asoc_sdw_rt_amp_spk_rtd_init,
268
.controls = generic_spk_controls,
269
.num_controls = ARRAY_SIZE(generic_spk_controls),
270
.widgets = generic_spk_widgets,
271
.num_widgets = ARRAY_SIZE(generic_spk_widgets),
272
},
273
},
274
.dai_num = 1,
275
.ops = &soc_sdw_rt1308_i2s_ops,
276
},
277
{
278
.part_id = 0x1316,
279
.name_prefix = "rt1316",
280
.dais = {
281
{
282
.direction = {true, true},
283
.dai_name = "rt1316-aif",
284
.component_name = "rt1316",
285
.dai_type = SOC_SDW_DAI_TYPE_AMP,
286
.dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_AMP_IN_DAI_ID},
287
.init = asoc_sdw_rt_amp_init,
288
.exit = asoc_sdw_rt_amp_exit,
289
.rtd_init = asoc_sdw_rt_amp_spk_rtd_init,
290
.controls = generic_spk_controls,
291
.num_controls = ARRAY_SIZE(generic_spk_controls),
292
.widgets = generic_spk_widgets,
293
.num_widgets = ARRAY_SIZE(generic_spk_widgets),
294
},
295
},
296
.dai_num = 1,
297
},
298
{
299
.part_id = 0x1318,
300
.name_prefix = "rt1318",
301
.dais = {
302
{
303
.direction = {true, true},
304
.dai_name = "rt1318-aif",
305
.component_name = "rt1318",
306
.dai_type = SOC_SDW_DAI_TYPE_AMP,
307
.dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_AMP_IN_DAI_ID},
308
.init = asoc_sdw_rt_amp_init,
309
.exit = asoc_sdw_rt_amp_exit,
310
.rtd_init = asoc_sdw_rt_amp_spk_rtd_init,
311
.controls = generic_spk_controls,
312
.num_controls = ARRAY_SIZE(generic_spk_controls),
313
.widgets = generic_spk_widgets,
314
.num_widgets = ARRAY_SIZE(generic_spk_widgets),
315
},
316
},
317
.dai_num = 1,
318
},
319
{
320
.part_id = 0x1320,
321
.name_prefix = "rt1320",
322
.dais = {
323
{
324
.direction = {true, false},
325
.dai_name = "rt1320-aif1",
326
.component_name = "rt1320",
327
.dai_type = SOC_SDW_DAI_TYPE_AMP,
328
.dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_UNUSED_DAI_ID},
329
.init = asoc_sdw_rt_amp_init,
330
.exit = asoc_sdw_rt_amp_exit,
331
.rtd_init = asoc_sdw_rt_amp_spk_rtd_init,
332
.controls = generic_spk_controls,
333
.num_controls = ARRAY_SIZE(generic_spk_controls),
334
.widgets = generic_spk_widgets,
335
.num_widgets = ARRAY_SIZE(generic_spk_widgets),
336
},
337
},
338
.dai_num = 1,
339
},
340
{
341
.part_id = 0x1321,
342
.name_prefix = "rt1320",
343
.dais = {
344
{
345
.direction = {true, false},
346
.dai_name = "rt1320-aif1",
347
.component_name = "rt1320",
348
.dai_type = SOC_SDW_DAI_TYPE_AMP,
349
.dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_UNUSED_DAI_ID},
350
.init = asoc_sdw_rt_amp_init,
351
.exit = asoc_sdw_rt_amp_exit,
352
.rtd_init = asoc_sdw_rt_amp_spk_rtd_init,
353
.controls = generic_spk_controls,
354
.num_controls = ARRAY_SIZE(generic_spk_controls),
355
.widgets = generic_spk_widgets,
356
.num_widgets = ARRAY_SIZE(generic_spk_widgets),
357
},
358
},
359
.dai_num = 1,
360
},
361
{
362
.part_id = 0x714,
363
.name_prefix = "rt714",
364
.version_id = 3,
365
.ignore_internal_dmic = true,
366
.dais = {
367
{
368
.direction = {false, true},
369
.dai_name = "rt715-sdca-aif2",
370
.dai_type = SOC_SDW_DAI_TYPE_MIC,
371
.dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_DMIC_DAI_ID},
372
.rtd_init = asoc_sdw_rt_dmic_rtd_init,
373
},
374
},
375
.dai_num = 1,
376
},
377
{
378
.part_id = 0x715,
379
.name_prefix = "rt715",
380
.version_id = 3,
381
.ignore_internal_dmic = true,
382
.dais = {
383
{
384
.direction = {false, true},
385
.dai_name = "rt715-sdca-aif2",
386
.dai_type = SOC_SDW_DAI_TYPE_MIC,
387
.dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_DMIC_DAI_ID},
388
.rtd_init = asoc_sdw_rt_dmic_rtd_init,
389
},
390
},
391
.dai_num = 1,
392
},
393
{
394
.part_id = 0x714,
395
.name_prefix = "rt714",
396
.version_id = 2,
397
.ignore_internal_dmic = true,
398
.dais = {
399
{
400
.direction = {false, true},
401
.dai_name = "rt715-aif2",
402
.dai_type = SOC_SDW_DAI_TYPE_MIC,
403
.dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_DMIC_DAI_ID},
404
.rtd_init = asoc_sdw_rt_dmic_rtd_init,
405
},
406
},
407
.dai_num = 1,
408
},
409
{
410
.part_id = 0x715,
411
.name_prefix = "rt715",
412
.version_id = 2,
413
.ignore_internal_dmic = true,
414
.dais = {
415
{
416
.direction = {false, true},
417
.dai_name = "rt715-aif2",
418
.dai_type = SOC_SDW_DAI_TYPE_MIC,
419
.dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_DMIC_DAI_ID},
420
.rtd_init = asoc_sdw_rt_dmic_rtd_init,
421
},
422
},
423
.dai_num = 1,
424
},
425
{
426
.part_id = 0x721,
427
.name_prefix = "rt721",
428
.version_id = 3,
429
.dais = {
430
{
431
.direction = {true, true},
432
.dai_name = "rt721-sdca-aif1",
433
.dai_type = SOC_SDW_DAI_TYPE_JACK,
434
.dailink = {SOC_SDW_JACK_OUT_DAI_ID, SOC_SDW_JACK_IN_DAI_ID},
435
.init = asoc_sdw_rt_sdca_jack_init,
436
.exit = asoc_sdw_rt_sdca_jack_exit,
437
.rtd_init = asoc_sdw_rt_sdca_jack_rtd_init,
438
.controls = generic_jack_controls,
439
.num_controls = ARRAY_SIZE(generic_jack_controls),
440
.widgets = generic_jack_widgets,
441
.num_widgets = ARRAY_SIZE(generic_jack_widgets),
442
},
443
{
444
.direction = {true, false},
445
.dai_name = "rt721-sdca-aif2",
446
.component_name = "rt721",
447
.dai_type = SOC_SDW_DAI_TYPE_AMP,
448
/* No feedback capability is provided by rt721-sdca codec driver*/
449
.dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_UNUSED_DAI_ID},
450
.init = asoc_sdw_rt_amp_init,
451
.exit = asoc_sdw_rt_amp_exit,
452
.rtd_init = asoc_sdw_rt_mf_sdca_spk_rtd_init,
453
.controls = generic_spk_controls,
454
.num_controls = ARRAY_SIZE(generic_spk_controls),
455
.widgets = generic_spk_widgets,
456
.num_widgets = ARRAY_SIZE(generic_spk_widgets),
457
},
458
{
459
.direction = {false, true},
460
.dai_name = "rt721-sdca-aif3",
461
.dai_type = SOC_SDW_DAI_TYPE_MIC,
462
.dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_DMIC_DAI_ID},
463
.rtd_init = asoc_sdw_rt_dmic_rtd_init,
464
},
465
},
466
.dai_num = 3,
467
},
468
{
469
.part_id = 0x722,
470
.name_prefix = "rt722",
471
.version_id = 3,
472
.dais = {
473
{
474
.direction = {true, true},
475
.dai_name = "rt722-sdca-aif1",
476
.dai_type = SOC_SDW_DAI_TYPE_JACK,
477
.dailink = {SOC_SDW_JACK_OUT_DAI_ID, SOC_SDW_JACK_IN_DAI_ID},
478
.init = asoc_sdw_rt_sdca_jack_init,
479
.exit = asoc_sdw_rt_sdca_jack_exit,
480
.rtd_init = asoc_sdw_rt_sdca_jack_rtd_init,
481
.controls = generic_jack_controls,
482
.num_controls = ARRAY_SIZE(generic_jack_controls),
483
.widgets = generic_jack_widgets,
484
.num_widgets = ARRAY_SIZE(generic_jack_widgets),
485
},
486
{
487
.direction = {true, false},
488
.dai_name = "rt722-sdca-aif2",
489
.component_name = "rt722",
490
.dai_type = SOC_SDW_DAI_TYPE_AMP,
491
/* No feedback capability is provided by rt722-sdca codec driver*/
492
.dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_UNUSED_DAI_ID},
493
.init = asoc_sdw_rt_amp_init,
494
.exit = asoc_sdw_rt_amp_exit,
495
.rtd_init = asoc_sdw_rt_mf_sdca_spk_rtd_init,
496
.controls = generic_spk_controls,
497
.num_controls = ARRAY_SIZE(generic_spk_controls),
498
.widgets = generic_spk_widgets,
499
.num_widgets = ARRAY_SIZE(generic_spk_widgets),
500
.quirk = SOC_SDW_CODEC_SPKR,
501
.quirk_exclude = true,
502
},
503
{
504
.direction = {false, true},
505
.dai_name = "rt722-sdca-aif3",
506
.dai_type = SOC_SDW_DAI_TYPE_MIC,
507
.dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_DMIC_DAI_ID},
508
.rtd_init = asoc_sdw_rt_dmic_rtd_init,
509
.quirk = SOC_SDW_CODEC_MIC,
510
.quirk_exclude = true,
511
},
512
},
513
.dai_num = 3,
514
},
515
{
516
.part_id = 0x8373,
517
.name_prefix = "Left",
518
.dais = {
519
{
520
.direction = {true, true},
521
.dai_name = "max98373-aif1",
522
.component_name = "mx8373",
523
.dai_type = SOC_SDW_DAI_TYPE_AMP,
524
.dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_AMP_IN_DAI_ID},
525
.init = asoc_sdw_maxim_init,
526
.rtd_init = asoc_sdw_maxim_spk_rtd_init,
527
.controls = lr_spk_controls,
528
.num_controls = ARRAY_SIZE(lr_spk_controls),
529
.widgets = lr_spk_widgets,
530
.num_widgets = ARRAY_SIZE(lr_spk_widgets),
531
},
532
},
533
.dai_num = 1,
534
},
535
{
536
.part_id = 0x8363,
537
.name_prefix = "Left",
538
.dais = {
539
{
540
.direction = {true, false},
541
.dai_name = "max98363-aif1",
542
.component_name = "mx8363",
543
.dai_type = SOC_SDW_DAI_TYPE_AMP,
544
.dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_UNUSED_DAI_ID},
545
.init = asoc_sdw_maxim_init,
546
.rtd_init = asoc_sdw_maxim_spk_rtd_init,
547
.controls = lr_spk_controls,
548
.num_controls = ARRAY_SIZE(lr_spk_controls),
549
.widgets = lr_spk_widgets,
550
.num_widgets = ARRAY_SIZE(lr_spk_widgets),
551
},
552
},
553
.dai_num = 1,
554
},
555
{
556
.part_id = 0x5682,
557
.name_prefix = "rt5682",
558
.dais = {
559
{
560
.direction = {true, true},
561
.dai_name = "rt5682-sdw",
562
.dai_type = SOC_SDW_DAI_TYPE_JACK,
563
.dailink = {SOC_SDW_JACK_OUT_DAI_ID, SOC_SDW_JACK_IN_DAI_ID},
564
.rtd_init = asoc_sdw_rt5682_rtd_init,
565
.controls = generic_jack_controls,
566
.num_controls = ARRAY_SIZE(generic_jack_controls),
567
.widgets = generic_jack_widgets,
568
.num_widgets = ARRAY_SIZE(generic_jack_widgets),
569
},
570
},
571
.dai_num = 1,
572
},
573
{
574
.part_id = 0x3556,
575
.name_prefix = "AMP",
576
.dais = {
577
{
578
.direction = {true, false},
579
.dai_name = "cs35l56-sdw1",
580
.component_name = "cs35l56",
581
.dai_type = SOC_SDW_DAI_TYPE_AMP,
582
.dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_UNUSED_DAI_ID},
583
.init = asoc_sdw_cs_amp_init,
584
.rtd_init = asoc_sdw_cs_spk_rtd_init,
585
.controls = generic_spk_controls,
586
.num_controls = ARRAY_SIZE(generic_spk_controls),
587
.widgets = generic_spk_widgets,
588
.num_widgets = ARRAY_SIZE(generic_spk_widgets),
589
},
590
{
591
.direction = {false, true},
592
.dai_name = "cs35l56-sdw1c",
593
.dai_type = SOC_SDW_DAI_TYPE_AMP,
594
.dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_AMP_IN_DAI_ID},
595
.rtd_init = asoc_sdw_cs_spk_feedback_rtd_init,
596
},
597
},
598
.dai_num = 2,
599
},
600
{
601
.part_id = 0x3557,
602
.name_prefix = "AMP",
603
.dais = {
604
{
605
.direction = {true, false},
606
.dai_name = "cs35l56-sdw1",
607
.component_name = "cs35l56",
608
.dai_type = SOC_SDW_DAI_TYPE_AMP,
609
.dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_UNUSED_DAI_ID},
610
.init = asoc_sdw_cs_amp_init,
611
.rtd_init = asoc_sdw_cs_spk_rtd_init,
612
.controls = generic_spk_controls,
613
.num_controls = ARRAY_SIZE(generic_spk_controls),
614
.widgets = generic_spk_widgets,
615
.num_widgets = ARRAY_SIZE(generic_spk_widgets),
616
},
617
{
618
.direction = {false, true},
619
.dai_name = "cs35l56-sdw1c",
620
.dai_type = SOC_SDW_DAI_TYPE_AMP,
621
.dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_AMP_IN_DAI_ID},
622
.rtd_init = asoc_sdw_cs_spk_feedback_rtd_init,
623
},
624
},
625
.dai_num = 2,
626
},
627
{
628
.part_id = 0x3563,
629
.name_prefix = "AMP",
630
.dais = {
631
{
632
.direction = {true, false},
633
.dai_name = "cs35l56-sdw1",
634
.component_name = "cs35l56",
635
.dai_type = SOC_SDW_DAI_TYPE_AMP,
636
.dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_UNUSED_DAI_ID},
637
.init = asoc_sdw_cs_amp_init,
638
.rtd_init = asoc_sdw_cs_spk_rtd_init,
639
.controls = generic_spk_controls,
640
.num_controls = ARRAY_SIZE(generic_spk_controls),
641
.widgets = generic_spk_widgets,
642
.num_widgets = ARRAY_SIZE(generic_spk_widgets),
643
},
644
{
645
.direction = {false, true},
646
.dai_name = "cs35l56-sdw1c",
647
.dai_type = SOC_SDW_DAI_TYPE_AMP,
648
.dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_AMP_IN_DAI_ID},
649
.rtd_init = asoc_sdw_cs_spk_feedback_rtd_init,
650
},
651
},
652
.dai_num = 2,
653
},
654
{
655
.part_id = 0x4242,
656
.name_prefix = "cs42l42",
657
.dais = {
658
{
659
.direction = {true, true},
660
.dai_name = "cs42l42-sdw",
661
.dai_type = SOC_SDW_DAI_TYPE_JACK,
662
.dailink = {SOC_SDW_JACK_OUT_DAI_ID, SOC_SDW_JACK_IN_DAI_ID},
663
.rtd_init = asoc_sdw_cs42l42_rtd_init,
664
.controls = generic_jack_controls,
665
.num_controls = ARRAY_SIZE(generic_jack_controls),
666
.widgets = generic_jack_widgets,
667
.num_widgets = ARRAY_SIZE(generic_jack_widgets),
668
},
669
},
670
.dai_num = 1,
671
},
672
{
673
.part_id = 0x4243,
674
.name_prefix = "cs42l43",
675
.count_sidecar = asoc_sdw_bridge_cs35l56_count_sidecar,
676
.add_sidecar = asoc_sdw_bridge_cs35l56_add_sidecar,
677
.dais = {
678
{
679
.direction = {true, false},
680
.codec_name = "cs42l43-codec",
681
.dai_name = "cs42l43-dp5",
682
.dai_type = SOC_SDW_DAI_TYPE_JACK,
683
.dailink = {SOC_SDW_JACK_OUT_DAI_ID, SOC_SDW_UNUSED_DAI_ID},
684
.rtd_init = asoc_sdw_cs42l43_hs_rtd_init,
685
.controls = generic_jack_controls,
686
.num_controls = ARRAY_SIZE(generic_jack_controls),
687
.widgets = generic_jack_widgets,
688
.num_widgets = ARRAY_SIZE(generic_jack_widgets),
689
},
690
{
691
.direction = {false, true},
692
.codec_name = "cs42l43-codec",
693
.dai_name = "cs42l43-dp1",
694
.dai_type = SOC_SDW_DAI_TYPE_MIC,
695
.dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_DMIC_DAI_ID},
696
.rtd_init = asoc_sdw_cs42l43_dmic_rtd_init,
697
.widgets = generic_dmic_widgets,
698
.num_widgets = ARRAY_SIZE(generic_dmic_widgets),
699
.quirk = SOC_SDW_CODEC_MIC,
700
.quirk_exclude = true,
701
},
702
{
703
.direction = {false, true},
704
.codec_name = "cs42l43-codec",
705
.dai_name = "cs42l43-dp2",
706
.dai_type = SOC_SDW_DAI_TYPE_JACK,
707
.dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_JACK_IN_DAI_ID},
708
},
709
{
710
.direction = {true, false},
711
.codec_name = "cs42l43-codec",
712
.dai_name = "cs42l43-dp6",
713
.dai_type = SOC_SDW_DAI_TYPE_AMP,
714
.dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_UNUSED_DAI_ID},
715
.init = asoc_sdw_cs42l43_spk_init,
716
.rtd_init = asoc_sdw_cs42l43_spk_rtd_init,
717
.controls = generic_spk_controls,
718
.num_controls = ARRAY_SIZE(generic_spk_controls),
719
.widgets = generic_spk_widgets,
720
.num_widgets = ARRAY_SIZE(generic_spk_widgets),
721
.quirk = SOC_SDW_CODEC_SPKR | SOC_SDW_SIDECAR_AMPS,
722
},
723
},
724
.dai_num = 4,
725
},
726
{
727
.part_id = 0x4245,
728
.name_prefix = "cs42l45",
729
.dais = {
730
{
731
.direction = {true, false},
732
.codec_name = "snd_soc_sdca.UAJ.1",
733
.dai_name = "IT 41",
734
.dai_type = SOC_SDW_DAI_TYPE_JACK,
735
.dailink = {SOC_SDW_JACK_OUT_DAI_ID, SOC_SDW_UNUSED_DAI_ID},
736
.rtd_init = asoc_sdw_cs42l45_hs_rtd_init,
737
},
738
{
739
.direction = {false, true},
740
.codec_name = "snd_soc_sdca.SmartMic.0",
741
.dai_name = "OT 113",
742
.dai_type = SOC_SDW_DAI_TYPE_MIC,
743
.dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_DMIC_DAI_ID},
744
.rtd_init = asoc_sdw_cs42l45_dmic_rtd_init,
745
},
746
{
747
.direction = {false, true},
748
.codec_name = "snd_soc_sdca.UAJ.1",
749
.dai_name = "OT 36",
750
.dai_type = SOC_SDW_DAI_TYPE_JACK,
751
.dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_JACK_IN_DAI_ID},
752
},
753
},
754
.dai_num = 3,
755
.auxs = {
756
{
757
.codec_name = "snd_soc_sdca.HID.2",
758
},
759
},
760
.aux_num = 1,
761
},
762
{
763
.part_id = 0xaaaa, /* generic codec mockup */
764
.name_prefix = "sdw_mockup_mmulti-function",
765
.version_id = 0,
766
.dais = {
767
{
768
.direction = {true, true},
769
.dai_name = "sdw-mockup-aif1",
770
.dai_type = SOC_SDW_DAI_TYPE_JACK,
771
.dailink = {SOC_SDW_JACK_OUT_DAI_ID, SOC_SDW_JACK_IN_DAI_ID},
772
},
773
{
774
.direction = {true, false},
775
.dai_name = "sdw-mockup-aif1",
776
.dai_type = SOC_SDW_DAI_TYPE_AMP,
777
.dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_UNUSED_DAI_ID},
778
},
779
{
780
.direction = {false, true},
781
.dai_name = "sdw-mockup-aif1",
782
.dai_type = SOC_SDW_DAI_TYPE_MIC,
783
.dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_DMIC_DAI_ID},
784
},
785
},
786
.dai_num = 3,
787
},
788
{
789
.part_id = 0xaa55, /* headset codec mockup */
790
.name_prefix = "sdw_mockup_headset0",
791
.version_id = 0,
792
.dais = {
793
{
794
.direction = {true, true},
795
.dai_name = "sdw-mockup-aif1",
796
.dai_type = SOC_SDW_DAI_TYPE_JACK,
797
.dailink = {SOC_SDW_JACK_OUT_DAI_ID, SOC_SDW_JACK_IN_DAI_ID},
798
},
799
},
800
.dai_num = 1,
801
},
802
{
803
.part_id = 0x55aa, /* amplifier mockup */
804
.name_prefix = "sdw_mockup_amp1",
805
.version_id = 0,
806
.dais = {
807
{
808
.direction = {true, true},
809
.dai_name = "sdw-mockup-aif1",
810
.dai_type = SOC_SDW_DAI_TYPE_AMP,
811
.dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_AMP_IN_DAI_ID},
812
},
813
},
814
.dai_num = 1,
815
},
816
{
817
.part_id = 0x5555,
818
.name_prefix = "sdw_mockup_mic0",
819
.version_id = 0,
820
.dais = {
821
{
822
.dai_name = "sdw-mockup-aif1",
823
.direction = {false, true},
824
.dai_type = SOC_SDW_DAI_TYPE_MIC,
825
.dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_DMIC_DAI_ID},
826
},
827
},
828
.dai_num = 1,
829
},
830
};
831
EXPORT_SYMBOL_NS(codec_info_list, "SND_SOC_SDW_UTILS");
832
833
int asoc_sdw_get_codec_info_list_count(void)
834
{
835
return ARRAY_SIZE(codec_info_list);
836
};
837
EXPORT_SYMBOL_NS(asoc_sdw_get_codec_info_list_count, "SND_SOC_SDW_UTILS");
838
839
struct asoc_sdw_codec_info *asoc_sdw_find_codec_info_part(const u64 adr)
840
{
841
unsigned int part_id, sdw_version;
842
int i;
843
844
part_id = SDW_PART_ID(adr);
845
sdw_version = SDW_VERSION(adr);
846
for (i = 0; i < ARRAY_SIZE(codec_info_list); i++)
847
/*
848
* A codec info is for all sdw version with the part id if
849
* version_id is not specified in the codec info.
850
*/
851
if (part_id == codec_info_list[i].part_id &&
852
(!codec_info_list[i].version_id ||
853
sdw_version == codec_info_list[i].version_id))
854
return &codec_info_list[i];
855
856
return NULL;
857
}
858
EXPORT_SYMBOL_NS(asoc_sdw_find_codec_info_part, "SND_SOC_SDW_UTILS");
859
860
static struct asoc_sdw_codec_info *asoc_sdw_find_codec_info_sdw_id(const struct sdw_slave_id *id)
861
{
862
int i;
863
864
for (i = 0; i < ARRAY_SIZE(codec_info_list); i++)
865
if (id->part_id == codec_info_list[i].part_id &&
866
(!codec_info_list[i].version_id ||
867
id->sdw_version == codec_info_list[i].version_id))
868
return &codec_info_list[i];
869
870
return NULL;
871
}
872
873
struct asoc_sdw_codec_info *asoc_sdw_find_codec_info_acpi(const u8 *acpi_id)
874
{
875
int i;
876
877
if (!acpi_id[0])
878
return NULL;
879
880
for (i = 0; i < ARRAY_SIZE(codec_info_list); i++)
881
if (!memcmp(codec_info_list[i].acpi_id, acpi_id, ACPI_ID_LEN))
882
return &codec_info_list[i];
883
884
return NULL;
885
}
886
EXPORT_SYMBOL_NS(asoc_sdw_find_codec_info_acpi, "SND_SOC_SDW_UTILS");
887
888
struct asoc_sdw_codec_info *asoc_sdw_find_codec_info_dai(const char *dai_name, int *dai_index)
889
{
890
int i, j;
891
892
for (i = 0; i < ARRAY_SIZE(codec_info_list); i++) {
893
for (j = 0; j < codec_info_list[i].dai_num; j++) {
894
if (!strcmp(codec_info_list[i].dais[j].dai_name, dai_name)) {
895
*dai_index = j;
896
return &codec_info_list[i];
897
}
898
}
899
}
900
901
return NULL;
902
}
903
EXPORT_SYMBOL_NS(asoc_sdw_find_codec_info_dai, "SND_SOC_SDW_UTILS");
904
905
static int asoc_sdw_find_codec_info_dai_index(const struct asoc_sdw_codec_info *codec_info,
906
const char *dai_name)
907
{
908
int i;
909
910
for (i = 0; i < codec_info->dai_num; i++) {
911
if (!strcmp(codec_info->dais[i].dai_name, dai_name))
912
return i;
913
}
914
915
return -ENOENT;
916
}
917
918
int asoc_sdw_rtd_init(struct snd_soc_pcm_runtime *rtd)
919
{
920
struct snd_soc_card *card = rtd->card;
921
struct snd_soc_dapm_context *dapm = snd_soc_card_to_dapm(card);
922
struct asoc_sdw_codec_info *codec_info;
923
struct snd_soc_dai *dai;
924
struct sdw_slave *sdw_peripheral;
925
const char *spk_components="";
926
int dai_index;
927
int ret;
928
int i;
929
930
for_each_rtd_codec_dais(rtd, i, dai) {
931
if (is_sdw_slave(dai->component->dev))
932
sdw_peripheral = dev_to_sdw_dev(dai->component->dev);
933
else if (dai->component->dev->parent && is_sdw_slave(dai->component->dev->parent))
934
sdw_peripheral = dev_to_sdw_dev(dai->component->dev->parent);
935
else
936
continue;
937
938
codec_info = asoc_sdw_find_codec_info_sdw_id(&sdw_peripheral->id);
939
if (!codec_info)
940
return -EINVAL;
941
942
dai_index = asoc_sdw_find_codec_info_dai_index(codec_info, dai->name);
943
WARN_ON(dai_index < 0);
944
945
/*
946
* A codec dai can be connected to different dai links for capture and playback,
947
* but we only need to call the rtd_init function once.
948
* The rtd_init for each codec dai is independent. So, the order of rtd_init
949
* doesn't matter.
950
*/
951
if (codec_info->dais[dai_index].rtd_init_done)
952
continue;
953
954
dev_dbg(card->dev, "%#x/%s initializing for %s/%s\n",
955
codec_info->part_id, codec_info->dais[dai_index].dai_name,
956
dai->component->name, dai->name);
957
958
/*
959
* Add card controls and dapm widgets for the first codec dai.
960
* The controls and widgets will be used for all codec dais.
961
*/
962
963
if (i > 0)
964
goto skip_add_controls_widgets;
965
966
if (codec_info->dais[dai_index].controls) {
967
ret = snd_soc_add_card_controls(card, codec_info->dais[dai_index].controls,
968
codec_info->dais[dai_index].num_controls);
969
if (ret) {
970
dev_err(card->dev, "%#x controls addition failed: %d\n",
971
codec_info->part_id, ret);
972
return ret;
973
}
974
}
975
if (codec_info->dais[dai_index].widgets) {
976
ret = snd_soc_dapm_new_controls(dapm,
977
codec_info->dais[dai_index].widgets,
978
codec_info->dais[dai_index].num_widgets);
979
if (ret) {
980
dev_err(card->dev, "%#x widgets addition failed: %d\n",
981
codec_info->part_id, ret);
982
return ret;
983
}
984
}
985
986
skip_add_controls_widgets:
987
if (codec_info->dais[dai_index].rtd_init) {
988
ret = codec_info->dais[dai_index].rtd_init(rtd, dai);
989
if (ret)
990
return ret;
991
}
992
993
/* Generate the spk component string for card->components string */
994
if (codec_info->dais[dai_index].dai_type == SOC_SDW_DAI_TYPE_AMP &&
995
codec_info->dais[dai_index].component_name) {
996
if (strlen (spk_components) == 0)
997
spk_components =
998
devm_kasprintf(card->dev, GFP_KERNEL, "%s",
999
codec_info->dais[dai_index].component_name);
1000
else
1001
/* Append component name to spk_components */
1002
spk_components =
1003
devm_kasprintf(card->dev, GFP_KERNEL,
1004
"%s+%s", spk_components,
1005
codec_info->dais[dai_index].component_name);
1006
}
1007
1008
codec_info->dais[dai_index].rtd_init_done = true;
1009
1010
}
1011
1012
if (strlen (spk_components) > 0) {
1013
/* Update card components for speaker components */
1014
card->components = devm_kasprintf(card->dev, GFP_KERNEL, "%s spk:%s",
1015
card->components, spk_components);
1016
if (!card->components)
1017
return -ENOMEM;
1018
}
1019
1020
return 0;
1021
}
1022
EXPORT_SYMBOL_NS(asoc_sdw_rtd_init, "SND_SOC_SDW_UTILS");
1023
1024
/* these wrappers are only needed to avoid typecast compilation errors */
1025
int asoc_sdw_startup(struct snd_pcm_substream *substream)
1026
{
1027
return sdw_startup_stream(substream);
1028
}
1029
EXPORT_SYMBOL_NS(asoc_sdw_startup, "SND_SOC_SDW_UTILS");
1030
1031
int asoc_sdw_prepare(struct snd_pcm_substream *substream)
1032
{
1033
struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
1034
struct sdw_stream_runtime *sdw_stream;
1035
struct snd_soc_dai *dai;
1036
1037
/* Find stream from first CPU DAI */
1038
dai = snd_soc_rtd_to_cpu(rtd, 0);
1039
1040
sdw_stream = snd_soc_dai_get_stream(dai, substream->stream);
1041
if (IS_ERR(sdw_stream)) {
1042
dev_err(rtd->dev, "no stream found for DAI %s\n", dai->name);
1043
return PTR_ERR(sdw_stream);
1044
}
1045
1046
return sdw_prepare_stream(sdw_stream);
1047
}
1048
EXPORT_SYMBOL_NS(asoc_sdw_prepare, "SND_SOC_SDW_UTILS");
1049
1050
int asoc_sdw_trigger(struct snd_pcm_substream *substream, int cmd)
1051
{
1052
struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
1053
struct sdw_stream_runtime *sdw_stream;
1054
struct snd_soc_dai *dai;
1055
int ret;
1056
1057
/* Find stream from first CPU DAI */
1058
dai = snd_soc_rtd_to_cpu(rtd, 0);
1059
1060
sdw_stream = snd_soc_dai_get_stream(dai, substream->stream);
1061
if (IS_ERR(sdw_stream)) {
1062
dev_err(rtd->dev, "no stream found for DAI %s\n", dai->name);
1063
return PTR_ERR(sdw_stream);
1064
}
1065
1066
switch (cmd) {
1067
case SNDRV_PCM_TRIGGER_START:
1068
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
1069
case SNDRV_PCM_TRIGGER_RESUME:
1070
ret = sdw_enable_stream(sdw_stream);
1071
break;
1072
1073
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
1074
case SNDRV_PCM_TRIGGER_SUSPEND:
1075
case SNDRV_PCM_TRIGGER_STOP:
1076
ret = sdw_disable_stream(sdw_stream);
1077
break;
1078
default:
1079
ret = -EINVAL;
1080
break;
1081
}
1082
1083
if (ret)
1084
dev_err(rtd->dev, "%s trigger %d failed: %d\n", __func__, cmd, ret);
1085
1086
return ret;
1087
}
1088
EXPORT_SYMBOL_NS(asoc_sdw_trigger, "SND_SOC_SDW_UTILS");
1089
1090
int asoc_sdw_hw_params(struct snd_pcm_substream *substream,
1091
struct snd_pcm_hw_params *params)
1092
{
1093
struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
1094
struct snd_soc_dai_link_ch_map *ch_maps;
1095
int ch = params_channels(params);
1096
unsigned int ch_mask;
1097
int num_codecs;
1098
int step;
1099
int i;
1100
1101
if (!rtd->dai_link->ch_maps)
1102
return 0;
1103
1104
/* Identical data will be sent to all codecs in playback */
1105
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
1106
ch_mask = GENMASK(ch - 1, 0);
1107
step = 0;
1108
} else {
1109
num_codecs = rtd->dai_link->num_codecs;
1110
1111
if (ch < num_codecs || ch % num_codecs != 0) {
1112
dev_err(rtd->dev, "Channels number %d is invalid when codec number = %d\n",
1113
ch, num_codecs);
1114
return -EINVAL;
1115
}
1116
1117
ch_mask = GENMASK(ch / num_codecs - 1, 0);
1118
step = hweight_long(ch_mask);
1119
}
1120
1121
/*
1122
* The captured data will be combined from each cpu DAI if the dai
1123
* link has more than one codec DAIs. Set codec channel mask and
1124
* ASoC will set the corresponding channel numbers for each cpu dai.
1125
*/
1126
for_each_link_ch_maps(rtd->dai_link, i, ch_maps)
1127
ch_maps->ch_mask = ch_mask << (i * step);
1128
1129
return 0;
1130
}
1131
EXPORT_SYMBOL_NS(asoc_sdw_hw_params, "SND_SOC_SDW_UTILS");
1132
1133
int asoc_sdw_hw_free(struct snd_pcm_substream *substream)
1134
{
1135
struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
1136
struct sdw_stream_runtime *sdw_stream;
1137
struct snd_soc_dai *dai;
1138
1139
/* Find stream from first CPU DAI */
1140
dai = snd_soc_rtd_to_cpu(rtd, 0);
1141
1142
sdw_stream = snd_soc_dai_get_stream(dai, substream->stream);
1143
if (IS_ERR(sdw_stream)) {
1144
dev_err(rtd->dev, "no stream found for DAI %s\n", dai->name);
1145
return PTR_ERR(sdw_stream);
1146
}
1147
1148
return sdw_deprepare_stream(sdw_stream);
1149
}
1150
EXPORT_SYMBOL_NS(asoc_sdw_hw_free, "SND_SOC_SDW_UTILS");
1151
1152
void asoc_sdw_shutdown(struct snd_pcm_substream *substream)
1153
{
1154
sdw_shutdown_stream(substream);
1155
}
1156
EXPORT_SYMBOL_NS(asoc_sdw_shutdown, "SND_SOC_SDW_UTILS");
1157
1158
static bool asoc_sdw_is_unique_device(const struct snd_soc_acpi_link_adr *adr_link,
1159
unsigned int sdw_version,
1160
unsigned int mfg_id,
1161
unsigned int part_id,
1162
unsigned int class_id,
1163
int index_in_link)
1164
{
1165
int i;
1166
1167
for (i = 0; i < adr_link->num_adr; i++) {
1168
unsigned int sdw1_version, mfg1_id, part1_id, class1_id;
1169
u64 adr;
1170
1171
/* skip itself */
1172
if (i == index_in_link)
1173
continue;
1174
1175
adr = adr_link->adr_d[i].adr;
1176
1177
sdw1_version = SDW_VERSION(adr);
1178
mfg1_id = SDW_MFG_ID(adr);
1179
part1_id = SDW_PART_ID(adr);
1180
class1_id = SDW_CLASS_ID(adr);
1181
1182
if (sdw_version == sdw1_version &&
1183
mfg_id == mfg1_id &&
1184
part_id == part1_id &&
1185
class_id == class1_id)
1186
return false;
1187
}
1188
1189
return true;
1190
}
1191
1192
static const char *_asoc_sdw_get_codec_name(struct device *dev,
1193
const struct snd_soc_acpi_link_adr *adr_link,
1194
int adr_index)
1195
{
1196
u64 adr = adr_link->adr_d[adr_index].adr;
1197
unsigned int sdw_version = SDW_VERSION(adr);
1198
unsigned int link_id = SDW_DISCO_LINK_ID(adr);
1199
unsigned int unique_id = SDW_UNIQUE_ID(adr);
1200
unsigned int mfg_id = SDW_MFG_ID(adr);
1201
unsigned int part_id = SDW_PART_ID(adr);
1202
unsigned int class_id = SDW_CLASS_ID(adr);
1203
1204
if (asoc_sdw_is_unique_device(adr_link, sdw_version, mfg_id, part_id,
1205
class_id, adr_index))
1206
return devm_kasprintf(dev, GFP_KERNEL, "sdw:0:%01x:%04x:%04x:%02x",
1207
link_id, mfg_id, part_id, class_id);
1208
1209
return devm_kasprintf(dev, GFP_KERNEL, "sdw:0:%01x:%04x:%04x:%02x:%01x",
1210
link_id, mfg_id, part_id, class_id, unique_id);
1211
}
1212
1213
const char *asoc_sdw_get_codec_name(struct device *dev,
1214
const struct asoc_sdw_dai_info *dai_info,
1215
const struct snd_soc_acpi_link_adr *adr_link,
1216
int adr_index)
1217
{
1218
if (dai_info->codec_name)
1219
return devm_kstrdup(dev, dai_info->codec_name, GFP_KERNEL);
1220
1221
return _asoc_sdw_get_codec_name(dev, adr_link, adr_index);
1222
}
1223
EXPORT_SYMBOL_NS(asoc_sdw_get_codec_name, "SND_SOC_SDW_UTILS");
1224
1225
/* helper to get the link that the codec DAI is used */
1226
struct snd_soc_dai_link *asoc_sdw_mc_find_codec_dai_used(struct snd_soc_card *card,
1227
const char *dai_name)
1228
{
1229
struct snd_soc_dai_link *dai_link;
1230
int i;
1231
int j;
1232
1233
for_each_card_prelinks(card, i, dai_link) {
1234
for (j = 0; j < dai_link->num_codecs; j++) {
1235
/* Check each codec in a link */
1236
if (!strcmp(dai_link->codecs[j].dai_name, dai_name))
1237
return dai_link;
1238
}
1239
}
1240
return NULL;
1241
}
1242
EXPORT_SYMBOL_NS(asoc_sdw_mc_find_codec_dai_used, "SND_SOC_SDW_UTILS");
1243
1244
void asoc_sdw_mc_dailink_exit_loop(struct snd_soc_card *card)
1245
{
1246
struct snd_soc_dai_link *dai_link;
1247
struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card);
1248
int ret;
1249
int i, j;
1250
1251
for (i = 0; i < ctx->codec_info_list_count; i++) {
1252
for (j = 0; j < codec_info_list[i].dai_num; j++) {
1253
codec_info_list[i].dais[j].rtd_init_done = false;
1254
/* Check each dai in codec_info_lis to see if it is used in the link */
1255
if (!codec_info_list[i].dais[j].exit)
1256
continue;
1257
/*
1258
* We don't need to call .exit function if there is no matched
1259
* dai link found.
1260
*/
1261
dai_link = asoc_sdw_mc_find_codec_dai_used(card,
1262
codec_info_list[i].dais[j].dai_name);
1263
if (dai_link) {
1264
/* Do the .exit function if the codec dai is used in the link */
1265
ret = codec_info_list[i].dais[j].exit(card, dai_link);
1266
if (ret)
1267
dev_warn(card->dev,
1268
"codec exit failed %d\n",
1269
ret);
1270
break;
1271
}
1272
}
1273
}
1274
}
1275
EXPORT_SYMBOL_NS(asoc_sdw_mc_dailink_exit_loop, "SND_SOC_SDW_UTILS");
1276
1277
int asoc_sdw_card_late_probe(struct snd_soc_card *card)
1278
{
1279
int ret = 0;
1280
int i;
1281
1282
for (i = 0; i < ARRAY_SIZE(codec_info_list); i++) {
1283
if (codec_info_list[i].codec_card_late_probe) {
1284
ret = codec_info_list[i].codec_card_late_probe(card);
1285
if (ret < 0)
1286
return ret;
1287
}
1288
}
1289
return ret;
1290
}
1291
EXPORT_SYMBOL_NS(asoc_sdw_card_late_probe, "SND_SOC_SDW_UTILS");
1292
1293
void asoc_sdw_init_dai_link(struct device *dev, struct snd_soc_dai_link *dai_links,
1294
int *be_id, char *name, int playback, int capture,
1295
struct snd_soc_dai_link_component *cpus, int cpus_num,
1296
struct snd_soc_dai_link_component *platform_component,
1297
int num_platforms, struct snd_soc_dai_link_component *codecs,
1298
int codecs_num, int no_pcm,
1299
int (*init)(struct snd_soc_pcm_runtime *rtd),
1300
const struct snd_soc_ops *ops)
1301
{
1302
dev_dbg(dev, "create dai link %s, id %d\n", name, *be_id);
1303
dai_links->id = (*be_id)++;
1304
dai_links->name = name;
1305
dai_links->stream_name = name;
1306
dai_links->platforms = platform_component;
1307
dai_links->num_platforms = num_platforms;
1308
dai_links->no_pcm = no_pcm;
1309
dai_links->cpus = cpus;
1310
dai_links->num_cpus = cpus_num;
1311
dai_links->codecs = codecs;
1312
dai_links->num_codecs = codecs_num;
1313
dai_links->playback_only = playback && !capture;
1314
dai_links->capture_only = !playback && capture;
1315
dai_links->init = init;
1316
dai_links->ops = ops;
1317
}
1318
EXPORT_SYMBOL_NS(asoc_sdw_init_dai_link, "SND_SOC_SDW_UTILS");
1319
1320
int asoc_sdw_init_simple_dai_link(struct device *dev, struct snd_soc_dai_link *dai_links,
1321
int *be_id, char *name, int playback, int capture,
1322
const char *cpu_dai_name, const char *platform_comp_name,
1323
const char *codec_name, const char *codec_dai_name,
1324
int no_pcm, int (*init)(struct snd_soc_pcm_runtime *rtd),
1325
const struct snd_soc_ops *ops)
1326
{
1327
struct snd_soc_dai_link_component *dlc;
1328
1329
/* Allocate three DLCs one for the CPU, one for platform and one for the CODEC */
1330
dlc = devm_kcalloc(dev, 3, sizeof(*dlc), GFP_KERNEL);
1331
if (!dlc || !name || !cpu_dai_name || !platform_comp_name || !codec_name || !codec_dai_name)
1332
return -ENOMEM;
1333
1334
dlc[0].dai_name = cpu_dai_name;
1335
dlc[1].name = platform_comp_name;
1336
1337
dlc[2].name = codec_name;
1338
dlc[2].dai_name = codec_dai_name;
1339
1340
asoc_sdw_init_dai_link(dev, dai_links, be_id, name, playback, capture,
1341
&dlc[0], 1, &dlc[1], 1, &dlc[2], 1,
1342
no_pcm, init, ops);
1343
1344
return 0;
1345
}
1346
EXPORT_SYMBOL_NS(asoc_sdw_init_simple_dai_link, "SND_SOC_SDW_UTILS");
1347
1348
int asoc_sdw_count_sdw_endpoints(struct snd_soc_card *card,
1349
int *num_devs, int *num_ends, int *num_aux)
1350
{
1351
struct device *dev = card->dev;
1352
struct snd_soc_acpi_mach *mach = dev_get_platdata(dev);
1353
struct snd_soc_acpi_mach_params *mach_params = &mach->mach_params;
1354
const struct snd_soc_acpi_link_adr *adr_link;
1355
int i;
1356
1357
for (adr_link = mach_params->links; adr_link->num_adr; adr_link++) {
1358
*num_devs += adr_link->num_adr;
1359
1360
for (i = 0; i < adr_link->num_adr; i++) {
1361
const struct snd_soc_acpi_adr_device *adr_dev = &adr_link->adr_d[i];
1362
struct asoc_sdw_codec_info *codec_info;
1363
1364
*num_ends += adr_dev->num_endpoints;
1365
1366
codec_info = asoc_sdw_find_codec_info_part(adr_dev->adr);
1367
if (!codec_info)
1368
return -EINVAL;
1369
1370
*num_aux += codec_info->aux_num;
1371
}
1372
}
1373
1374
dev_dbg(dev, "Found %d devices with %d endpoints\n", *num_devs, *num_ends);
1375
1376
return 0;
1377
}
1378
EXPORT_SYMBOL_NS(asoc_sdw_count_sdw_endpoints, "SND_SOC_SDW_UTILS");
1379
1380
struct asoc_sdw_dailink *asoc_sdw_find_dailink(struct asoc_sdw_dailink *dailinks,
1381
const struct snd_soc_acpi_endpoint *new)
1382
{
1383
while (dailinks->initialised) {
1384
if (new->aggregated && dailinks->group_id == new->group_id)
1385
return dailinks;
1386
1387
dailinks++;
1388
}
1389
1390
INIT_LIST_HEAD(&dailinks->endpoints);
1391
dailinks->group_id = new->group_id;
1392
dailinks->initialised = true;
1393
1394
return dailinks;
1395
}
1396
EXPORT_SYMBOL_NS(asoc_sdw_find_dailink, "SND_SOC_SDW_UTILS");
1397
1398
int asoc_sdw_get_dai_type(u32 type)
1399
{
1400
switch (type) {
1401
case SDCA_FUNCTION_TYPE_SMART_AMP:
1402
case SDCA_FUNCTION_TYPE_SIMPLE_AMP:
1403
return SOC_SDW_DAI_TYPE_AMP;
1404
case SDCA_FUNCTION_TYPE_SMART_MIC:
1405
case SDCA_FUNCTION_TYPE_SIMPLE_MIC:
1406
case SDCA_FUNCTION_TYPE_SPEAKER_MIC:
1407
return SOC_SDW_DAI_TYPE_MIC;
1408
case SDCA_FUNCTION_TYPE_UAJ:
1409
case SDCA_FUNCTION_TYPE_RJ:
1410
case SDCA_FUNCTION_TYPE_SIMPLE_JACK:
1411
return SOC_SDW_DAI_TYPE_JACK;
1412
default:
1413
return -EINVAL;
1414
}
1415
}
1416
EXPORT_SYMBOL_NS(asoc_sdw_get_dai_type, "SND_SOC_SDW_UTILS");
1417
1418
/*
1419
* Check if the SDCA endpoint is present by the SDW peripheral
1420
*
1421
* @dev: Device pointer
1422
* @codec_info: Codec info pointer
1423
* @adr_link: ACPI link address
1424
* @adr_index: Index of the ACPI link address
1425
* @end_index: Index of the endpoint
1426
*
1427
* Return: 1 if the endpoint is present,
1428
* 0 if the endpoint is not present,
1429
* negative error code.
1430
*/
1431
1432
static int is_sdca_endpoint_present(struct device *dev,
1433
struct asoc_sdw_codec_info *codec_info,
1434
const struct snd_soc_acpi_link_adr *adr_link,
1435
int adr_index, int end_index)
1436
{
1437
const struct snd_soc_acpi_adr_device *adr_dev = &adr_link->adr_d[adr_index];
1438
const struct snd_soc_acpi_endpoint *adr_end;
1439
const struct asoc_sdw_dai_info *dai_info;
1440
struct sdw_slave *slave;
1441
struct device *sdw_dev;
1442
const char *sdw_codec_name;
1443
int ret, i;
1444
1445
adr_end = &adr_dev->endpoints[end_index];
1446
dai_info = &codec_info->dais[adr_end->num];
1447
1448
sdw_codec_name = _asoc_sdw_get_codec_name(dev, adr_link, adr_index);
1449
if (!sdw_codec_name)
1450
return -ENOMEM;
1451
1452
sdw_dev = bus_find_device_by_name(&sdw_bus_type, NULL, sdw_codec_name);
1453
if (!sdw_dev) {
1454
dev_err(dev, "codec %s not found\n", sdw_codec_name);
1455
return -EINVAL;
1456
}
1457
1458
slave = dev_to_sdw_dev(sdw_dev);
1459
1460
/* Make sure BIOS provides SDCA properties */
1461
if (!slave->sdca_data.interface_revision) {
1462
dev_warn(&slave->dev, "SDCA properties not found in the BIOS\n");
1463
ret = 1;
1464
goto put_device;
1465
}
1466
1467
for (i = 0; i < slave->sdca_data.num_functions; i++) {
1468
int dai_type = asoc_sdw_get_dai_type(slave->sdca_data.function[i].type);
1469
1470
if (dai_type == dai_info->dai_type) {
1471
dev_dbg(&slave->dev, "DAI type %d sdca function %s found\n",
1472
dai_type, slave->sdca_data.function[i].name);
1473
ret = 1;
1474
goto put_device;
1475
}
1476
}
1477
1478
dev_dbg(&slave->dev,
1479
"SDCA device function for DAI type %d not supported, skip endpoint\n",
1480
dai_info->dai_type);
1481
1482
ret = 0;
1483
1484
put_device:
1485
put_device(sdw_dev);
1486
return ret;
1487
}
1488
1489
int asoc_sdw_parse_sdw_endpoints(struct snd_soc_card *card,
1490
struct snd_soc_aux_dev *soc_aux,
1491
struct asoc_sdw_dailink *soc_dais,
1492
struct asoc_sdw_endpoint *soc_ends,
1493
int *num_devs)
1494
{
1495
struct device *dev = card->dev;
1496
struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card);
1497
struct snd_soc_acpi_mach *mach = dev_get_platdata(dev);
1498
struct snd_soc_acpi_mach_params *mach_params = &mach->mach_params;
1499
const struct snd_soc_acpi_link_adr *adr_link;
1500
struct asoc_sdw_endpoint *soc_end = soc_ends;
1501
int num_dais = 0;
1502
int i, j;
1503
int ret;
1504
1505
for (adr_link = mach_params->links; adr_link->num_adr; adr_link++) {
1506
int num_link_dailinks = 0;
1507
1508
if (!is_power_of_2(adr_link->mask)) {
1509
dev_err(dev, "link with multiple mask bits: 0x%x\n",
1510
adr_link->mask);
1511
return -EINVAL;
1512
}
1513
1514
for (i = 0; i < adr_link->num_adr; i++) {
1515
const struct snd_soc_acpi_adr_device *adr_dev = &adr_link->adr_d[i];
1516
struct asoc_sdw_codec_info *codec_info;
1517
const char *codec_name;
1518
bool check_sdca = false;
1519
1520
if (!adr_dev->name_prefix) {
1521
dev_err(dev, "codec 0x%llx does not have a name prefix\n",
1522
adr_dev->adr);
1523
return -EINVAL;
1524
}
1525
1526
codec_info = asoc_sdw_find_codec_info_part(adr_dev->adr);
1527
if (!codec_info)
1528
return -EINVAL;
1529
1530
for (j = 0; j < codec_info->aux_num; j++) {
1531
soc_aux->dlc.name = codec_info->auxs[j].codec_name;
1532
soc_aux++;
1533
}
1534
1535
ctx->ignore_internal_dmic |= codec_info->ignore_internal_dmic;
1536
1537
if (codec_info->count_sidecar && codec_info->add_sidecar) {
1538
ret = codec_info->count_sidecar(card, &num_dais, num_devs);
1539
if (ret)
1540
return ret;
1541
1542
soc_end->include_sidecar = true;
1543
}
1544
1545
if (SDW_CLASS_ID(adr_dev->adr) && adr_dev->num_endpoints > 1)
1546
check_sdca = true;
1547
1548
for (j = 0; j < adr_dev->num_endpoints; j++) {
1549
const struct snd_soc_acpi_endpoint *adr_end;
1550
const struct asoc_sdw_dai_info *dai_info;
1551
struct asoc_sdw_dailink *soc_dai;
1552
int stream;
1553
1554
adr_end = &adr_dev->endpoints[j];
1555
dai_info = &codec_info->dais[adr_end->num];
1556
soc_dai = asoc_sdw_find_dailink(soc_dais, adr_end);
1557
1558
/*
1559
* quirk should have higher priority than the sdca properties
1560
* in the BIOS. We can't always check the DAI quirk because we
1561
* will set the mc_quirk when the BIOS doesn't provide the right
1562
* information. The endpoint will be skipped if the dai_info->
1563
* quirk_exclude and mc_quirk are both not set if we always skip
1564
* the endpoint according to the quirk information. We need to
1565
* keep the endpoint if it is present in the BIOS. So, only
1566
* check the DAI quirk when the mc_quirk is set or SDCA endpoint
1567
* present check is not needed.
1568
*/
1569
if (dai_info->quirk & ctx->mc_quirk || !check_sdca) {
1570
/*
1571
* Check the endpoint if a matching quirk is set or SDCA
1572
* endpoint check is not necessary
1573
*/
1574
if (dai_info->quirk &&
1575
!(dai_info->quirk_exclude ^ !!(dai_info->quirk & ctx->mc_quirk))) {
1576
(*num_devs)--;
1577
continue;
1578
}
1579
} else {
1580
/* Check SDCA codec endpoint if there is no matching quirk */
1581
ret = is_sdca_endpoint_present(dev, codec_info, adr_link, i, j);
1582
if (ret < 0)
1583
return ret;
1584
1585
/* The endpoint is not present, skip */
1586
if (!ret) {
1587
(*num_devs)--;
1588
continue;
1589
}
1590
}
1591
1592
dev_dbg(dev,
1593
"Add dev: %d, 0x%llx end: %d, dai: %d, %c/%c to %s: %d\n",
1594
ffs(adr_link->mask) - 1, adr_dev->adr,
1595
adr_end->num, dai_info->dai_type,
1596
dai_info->direction[SNDRV_PCM_STREAM_PLAYBACK] ? 'P' : '-',
1597
dai_info->direction[SNDRV_PCM_STREAM_CAPTURE] ? 'C' : '-',
1598
adr_end->aggregated ? "group" : "solo",
1599
adr_end->group_id);
1600
1601
if (adr_end->num >= codec_info->dai_num) {
1602
dev_err(dev,
1603
"%d is too many endpoints for codec: 0x%x\n",
1604
adr_end->num, codec_info->part_id);
1605
return -EINVAL;
1606
}
1607
1608
for_each_pcm_streams(stream) {
1609
if (dai_info->direction[stream] &&
1610
dai_info->dailink[stream] < 0) {
1611
dev_err(dev,
1612
"Invalid dailink id %d for codec: 0x%x\n",
1613
dai_info->dailink[stream],
1614
codec_info->part_id);
1615
return -EINVAL;
1616
}
1617
1618
if (dai_info->direction[stream]) {
1619
num_dais += !soc_dai->num_devs[stream];
1620
soc_dai->num_devs[stream]++;
1621
soc_dai->link_mask[stream] |= adr_link->mask;
1622
}
1623
}
1624
1625
num_link_dailinks += !!list_empty(&soc_dai->endpoints);
1626
list_add_tail(&soc_end->list, &soc_dai->endpoints);
1627
1628
codec_name = asoc_sdw_get_codec_name(dev, dai_info,
1629
adr_link, i);
1630
if (!codec_name)
1631
return -ENOMEM;
1632
1633
dev_dbg(dev, "Adding prefix %s for %s\n",
1634
adr_dev->name_prefix, codec_name);
1635
1636
soc_end->name_prefix = adr_dev->name_prefix;
1637
1638
soc_end->link_mask = adr_link->mask;
1639
soc_end->codec_name = codec_name;
1640
soc_end->codec_info = codec_info;
1641
soc_end->dai_info = dai_info;
1642
soc_end++;
1643
}
1644
}
1645
1646
ctx->append_dai_type |= (num_link_dailinks > 1);
1647
}
1648
1649
return num_dais;
1650
}
1651
EXPORT_SYMBOL_NS(asoc_sdw_parse_sdw_endpoints, "SND_SOC_SDW_UTILS");
1652
1653
MODULE_LICENSE("GPL");
1654
MODULE_DESCRIPTION("SoundWire ASoC helpers");
1655
1656