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