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/soc_sdw_utils.h>
14
15 static const struct snd_soc_dapm_widget generic_dmic_widgets[] = {
16 SND_SOC_DAPM_MIC("DMIC", NULL),
17 };
18
19 static const struct snd_soc_dapm_widget generic_jack_widgets[] = {
20 SND_SOC_DAPM_HP("Headphone", NULL),
21 SND_SOC_DAPM_MIC("Headset Mic", NULL),
22 };
23
24 static const struct snd_kcontrol_new generic_jack_controls[] = {
25 SOC_DAPM_PIN_SWITCH("Headphone"),
26 SOC_DAPM_PIN_SWITCH("Headset Mic"),
27 };
28
29 static const struct snd_soc_dapm_widget generic_spk_widgets[] = {
30 SND_SOC_DAPM_SPK("Speaker", NULL),
31 };
32
33 static const struct snd_kcontrol_new generic_spk_controls[] = {
34 SOC_DAPM_PIN_SWITCH("Speaker"),
35 };
36
37 static const struct snd_soc_dapm_widget maxim_widgets[] = {
38 SND_SOC_DAPM_SPK("Left Spk", NULL),
39 SND_SOC_DAPM_SPK("Right Spk", NULL),
40 };
41
42 static const struct snd_kcontrol_new maxim_controls[] = {
43 SOC_DAPM_PIN_SWITCH("Left Spk"),
44 SOC_DAPM_PIN_SWITCH("Right Spk"),
45 };
46
47 static const struct snd_soc_dapm_widget rt700_widgets[] = {
48 SND_SOC_DAPM_HP("Headphones", NULL),
49 SND_SOC_DAPM_MIC("AMIC", NULL),
50 SND_SOC_DAPM_SPK("Speaker", NULL),
51 };
52
53 static const struct snd_kcontrol_new rt700_controls[] = {
54 SOC_DAPM_PIN_SWITCH("Headphones"),
55 SOC_DAPM_PIN_SWITCH("AMIC"),
56 SOC_DAPM_PIN_SWITCH("Speaker"),
57 };
58
59 struct asoc_sdw_codec_info codec_info_list[] = {
60 {
61 .part_id = 0x700,
62 .dais = {
63 {
64 .direction = {true, true},
65 .dai_name = "rt700-aif1",
66 .dai_type = SOC_SDW_DAI_TYPE_JACK,
67 .dailink = {SOC_SDW_JACK_OUT_DAI_ID, SOC_SDW_JACK_IN_DAI_ID},
68 .rtd_init = asoc_sdw_rt700_rtd_init,
69 .controls = rt700_controls,
70 .num_controls = ARRAY_SIZE(rt700_controls),
71 .widgets = rt700_widgets,
72 .num_widgets = ARRAY_SIZE(rt700_widgets),
73 },
74 },
75 .dai_num = 1,
76 },
77 {
78 .part_id = 0x711,
79 .version_id = 3,
80 .dais = {
81 {
82 .direction = {true, true},
83 .dai_name = "rt711-sdca-aif1",
84 .dai_type = SOC_SDW_DAI_TYPE_JACK,
85 .dailink = {SOC_SDW_JACK_OUT_DAI_ID, SOC_SDW_JACK_IN_DAI_ID},
86 .init = asoc_sdw_rt_sdca_jack_init,
87 .exit = asoc_sdw_rt_sdca_jack_exit,
88 .rtd_init = asoc_sdw_rt_sdca_jack_rtd_init,
89 .controls = generic_jack_controls,
90 .num_controls = ARRAY_SIZE(generic_jack_controls),
91 .widgets = generic_jack_widgets,
92 .num_widgets = ARRAY_SIZE(generic_jack_widgets),
93 },
94 },
95 .dai_num = 1,
96 },
97 {
98 .part_id = 0x711,
99 .version_id = 2,
100 .dais = {
101 {
102 .direction = {true, true},
103 .dai_name = "rt711-aif1",
104 .dai_type = SOC_SDW_DAI_TYPE_JACK,
105 .dailink = {SOC_SDW_JACK_OUT_DAI_ID, SOC_SDW_JACK_IN_DAI_ID},
106 .init = asoc_sdw_rt711_init,
107 .exit = asoc_sdw_rt711_exit,
108 .rtd_init = asoc_sdw_rt711_rtd_init,
109 .controls = generic_jack_controls,
110 .num_controls = ARRAY_SIZE(generic_jack_controls),
111 .widgets = generic_jack_widgets,
112 .num_widgets = ARRAY_SIZE(generic_jack_widgets),
113 },
114 },
115 .dai_num = 1,
116 },
117 {
118 .part_id = 0x712,
119 .version_id = 3,
120 .dais = {
121 {
122 .direction = {true, true},
123 .dai_name = "rt712-sdca-aif1",
124 .dai_type = SOC_SDW_DAI_TYPE_JACK,
125 .dailink = {SOC_SDW_JACK_OUT_DAI_ID, SOC_SDW_JACK_IN_DAI_ID},
126 .init = asoc_sdw_rt_sdca_jack_init,
127 .exit = asoc_sdw_rt_sdca_jack_exit,
128 .rtd_init = asoc_sdw_rt_sdca_jack_rtd_init,
129 .controls = generic_jack_controls,
130 .num_controls = ARRAY_SIZE(generic_jack_controls),
131 .widgets = generic_jack_widgets,
132 .num_widgets = ARRAY_SIZE(generic_jack_widgets),
133 },
134 {
135 .direction = {true, false},
136 .dai_name = "rt712-sdca-aif2",
137 .dai_type = SOC_SDW_DAI_TYPE_AMP,
138 .dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_UNUSED_DAI_ID},
139 .init = asoc_sdw_rt_amp_init,
140 .exit = asoc_sdw_rt_amp_exit,
141 .rtd_init = asoc_sdw_rt712_spk_rtd_init,
142 .controls = generic_spk_controls,
143 .num_controls = ARRAY_SIZE(generic_spk_controls),
144 .widgets = generic_spk_widgets,
145 .num_widgets = ARRAY_SIZE(generic_spk_widgets),
146 },
147 },
148 .dai_num = 2,
149 },
150 {
151 .part_id = 0x1712,
152 .version_id = 3,
153 .dais = {
154 {
155 .direction = {false, true},
156 .dai_name = "rt712-sdca-dmic-aif1",
157 .dai_type = SOC_SDW_DAI_TYPE_MIC,
158 .dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_DMIC_DAI_ID},
159 .rtd_init = asoc_sdw_rt_dmic_rtd_init,
160 },
161 },
162 .dai_num = 1,
163 },
164 {
165 .part_id = 0x713,
166 .version_id = 3,
167 .dais = {
168 {
169 .direction = {true, true},
170 .dai_name = "rt712-sdca-aif1",
171 .dai_type = SOC_SDW_DAI_TYPE_JACK,
172 .dailink = {SOC_SDW_JACK_OUT_DAI_ID, SOC_SDW_JACK_IN_DAI_ID},
173 .init = asoc_sdw_rt_sdca_jack_init,
174 .exit = asoc_sdw_rt_sdca_jack_exit,
175 .rtd_init = asoc_sdw_rt_sdca_jack_rtd_init,
176 .controls = generic_jack_controls,
177 .num_controls = ARRAY_SIZE(generic_jack_controls),
178 .widgets = generic_jack_widgets,
179 .num_widgets = ARRAY_SIZE(generic_jack_widgets),
180 },
181 },
182 .dai_num = 1,
183 },
184 {
185 .part_id = 0x1713,
186 .version_id = 3,
187 .dais = {
188 {
189 .direction = {false, true},
190 .dai_name = "rt712-sdca-dmic-aif1",
191 .dai_type = SOC_SDW_DAI_TYPE_MIC,
192 .dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_DMIC_DAI_ID},
193 .rtd_init = asoc_sdw_rt_dmic_rtd_init,
194 },
195 },
196 .dai_num = 1,
197 },
198 {
199 .part_id = 0x1308,
200 .acpi_id = "10EC1308",
201 .dais = {
202 {
203 .direction = {true, false},
204 .dai_name = "rt1308-aif",
205 .dai_type = SOC_SDW_DAI_TYPE_AMP,
206 .dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_UNUSED_DAI_ID},
207 .init = asoc_sdw_rt_amp_init,
208 .exit = asoc_sdw_rt_amp_exit,
209 .rtd_init = asoc_sdw_rt_amp_spk_rtd_init,
210 .controls = generic_spk_controls,
211 .num_controls = ARRAY_SIZE(generic_spk_controls),
212 .widgets = generic_spk_widgets,
213 .num_widgets = ARRAY_SIZE(generic_spk_widgets),
214 },
215 },
216 .dai_num = 1,
217 .ops = &soc_sdw_rt1308_i2s_ops,
218 },
219 {
220 .part_id = 0x1316,
221 .dais = {
222 {
223 .direction = {true, true},
224 .dai_name = "rt1316-aif",
225 .dai_type = SOC_SDW_DAI_TYPE_AMP,
226 .dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_AMP_IN_DAI_ID},
227 .init = asoc_sdw_rt_amp_init,
228 .exit = asoc_sdw_rt_amp_exit,
229 .rtd_init = asoc_sdw_rt_amp_spk_rtd_init,
230 .controls = generic_spk_controls,
231 .num_controls = ARRAY_SIZE(generic_spk_controls),
232 .widgets = generic_spk_widgets,
233 .num_widgets = ARRAY_SIZE(generic_spk_widgets),
234 },
235 },
236 .dai_num = 1,
237 },
238 {
239 .part_id = 0x1318,
240 .dais = {
241 {
242 .direction = {true, true},
243 .dai_name = "rt1318-aif",
244 .dai_type = SOC_SDW_DAI_TYPE_AMP,
245 .dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_AMP_IN_DAI_ID},
246 .init = asoc_sdw_rt_amp_init,
247 .exit = asoc_sdw_rt_amp_exit,
248 .rtd_init = asoc_sdw_rt_amp_spk_rtd_init,
249 .controls = generic_spk_controls,
250 .num_controls = ARRAY_SIZE(generic_spk_controls),
251 .widgets = generic_spk_widgets,
252 .num_widgets = ARRAY_SIZE(generic_spk_widgets),
253 },
254 },
255 .dai_num = 1,
256 },
257 {
258 .part_id = 0x1320,
259 .dais = {
260 {
261 .direction = {true, false},
262 .dai_name = "rt1320-aif1",
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 },
276 {
277 .part_id = 0x714,
278 .version_id = 3,
279 .ignore_internal_dmic = true,
280 .dais = {
281 {
282 .direction = {false, true},
283 .dai_name = "rt715-sdca-aif2",
284 .dai_type = SOC_SDW_DAI_TYPE_MIC,
285 .dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_DMIC_DAI_ID},
286 .rtd_init = asoc_sdw_rt_dmic_rtd_init,
287 },
288 },
289 .dai_num = 1,
290 },
291 {
292 .part_id = 0x715,
293 .version_id = 3,
294 .ignore_internal_dmic = true,
295 .dais = {
296 {
297 .direction = {false, true},
298 .dai_name = "rt715-sdca-aif2",
299 .dai_type = SOC_SDW_DAI_TYPE_MIC,
300 .dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_DMIC_DAI_ID},
301 .rtd_init = asoc_sdw_rt_dmic_rtd_init,
302 },
303 },
304 .dai_num = 1,
305 },
306 {
307 .part_id = 0x714,
308 .version_id = 2,
309 .ignore_internal_dmic = true,
310 .dais = {
311 {
312 .direction = {false, true},
313 .dai_name = "rt715-aif2",
314 .dai_type = SOC_SDW_DAI_TYPE_MIC,
315 .dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_DMIC_DAI_ID},
316 .rtd_init = asoc_sdw_rt_dmic_rtd_init,
317 },
318 },
319 .dai_num = 1,
320 },
321 {
322 .part_id = 0x715,
323 .version_id = 2,
324 .ignore_internal_dmic = true,
325 .dais = {
326 {
327 .direction = {false, true},
328 .dai_name = "rt715-aif2",
329 .dai_type = SOC_SDW_DAI_TYPE_MIC,
330 .dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_DMIC_DAI_ID},
331 .rtd_init = asoc_sdw_rt_dmic_rtd_init,
332 },
333 },
334 .dai_num = 1,
335 },
336 {
337 .part_id = 0x722,
338 .version_id = 3,
339 .dais = {
340 {
341 .direction = {true, true},
342 .dai_name = "rt722-sdca-aif1",
343 .dai_type = SOC_SDW_DAI_TYPE_JACK,
344 .dailink = {SOC_SDW_JACK_OUT_DAI_ID, SOC_SDW_JACK_IN_DAI_ID},
345 .init = asoc_sdw_rt_sdca_jack_init,
346 .exit = asoc_sdw_rt_sdca_jack_exit,
347 .rtd_init = asoc_sdw_rt_sdca_jack_rtd_init,
348 .controls = generic_jack_controls,
349 .num_controls = ARRAY_SIZE(generic_jack_controls),
350 .widgets = generic_jack_widgets,
351 .num_widgets = ARRAY_SIZE(generic_jack_widgets),
352 },
353 {
354 .direction = {true, false},
355 .dai_name = "rt722-sdca-aif2",
356 .dai_type = SOC_SDW_DAI_TYPE_AMP,
357 /* No feedback capability is provided by rt722-sdca codec driver*/
358 .dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_UNUSED_DAI_ID},
359 .init = asoc_sdw_rt_amp_init,
360 .exit = asoc_sdw_rt_amp_exit,
361 .rtd_init = asoc_sdw_rt722_spk_rtd_init,
362 .controls = generic_spk_controls,
363 .num_controls = ARRAY_SIZE(generic_spk_controls),
364 .widgets = generic_spk_widgets,
365 .num_widgets = ARRAY_SIZE(generic_spk_widgets),
366 .quirk = SOC_SDW_CODEC_SPKR,
367 .quirk_exclude = true,
368 },
369 {
370 .direction = {false, true},
371 .dai_name = "rt722-sdca-aif3",
372 .dai_type = SOC_SDW_DAI_TYPE_MIC,
373 .dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_DMIC_DAI_ID},
374 .rtd_init = asoc_sdw_rt_dmic_rtd_init,
375 },
376 },
377 .dai_num = 3,
378 },
379 {
380 .part_id = 0x8373,
381 .dais = {
382 {
383 .direction = {true, true},
384 .dai_name = "max98373-aif1",
385 .dai_type = SOC_SDW_DAI_TYPE_AMP,
386 .dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_AMP_IN_DAI_ID},
387 .init = asoc_sdw_maxim_init,
388 .rtd_init = asoc_sdw_maxim_spk_rtd_init,
389 .controls = maxim_controls,
390 .num_controls = ARRAY_SIZE(maxim_controls),
391 .widgets = maxim_widgets,
392 .num_widgets = ARRAY_SIZE(maxim_widgets),
393 },
394 },
395 .dai_num = 1,
396 },
397 {
398 .part_id = 0x8363,
399 .dais = {
400 {
401 .direction = {true, false},
402 .dai_name = "max98363-aif1",
403 .dai_type = SOC_SDW_DAI_TYPE_AMP,
404 .dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_UNUSED_DAI_ID},
405 .init = asoc_sdw_maxim_init,
406 .rtd_init = asoc_sdw_maxim_spk_rtd_init,
407 .controls = maxim_controls,
408 .num_controls = ARRAY_SIZE(maxim_controls),
409 .widgets = maxim_widgets,
410 .num_widgets = ARRAY_SIZE(maxim_widgets),
411 },
412 },
413 .dai_num = 1,
414 },
415 {
416 .part_id = 0x5682,
417 .dais = {
418 {
419 .direction = {true, true},
420 .dai_name = "rt5682-sdw",
421 .dai_type = SOC_SDW_DAI_TYPE_JACK,
422 .dailink = {SOC_SDW_JACK_OUT_DAI_ID, SOC_SDW_JACK_IN_DAI_ID},
423 .rtd_init = asoc_sdw_rt5682_rtd_init,
424 .controls = generic_jack_controls,
425 .num_controls = ARRAY_SIZE(generic_jack_controls),
426 .widgets = generic_jack_widgets,
427 .num_widgets = ARRAY_SIZE(generic_jack_widgets),
428 },
429 },
430 .dai_num = 1,
431 },
432 {
433 .part_id = 0x3556,
434 .dais = {
435 {
436 .direction = {true, true},
437 .dai_name = "cs35l56-sdw1",
438 .dai_type = SOC_SDW_DAI_TYPE_AMP,
439 .dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_AMP_IN_DAI_ID},
440 .init = asoc_sdw_cs_amp_init,
441 .rtd_init = asoc_sdw_cs_spk_rtd_init,
442 .controls = generic_spk_controls,
443 .num_controls = ARRAY_SIZE(generic_spk_controls),
444 .widgets = generic_spk_widgets,
445 .num_widgets = ARRAY_SIZE(generic_spk_widgets),
446 },
447 },
448 .dai_num = 1,
449 },
450 {
451 .part_id = 0x4242,
452 .dais = {
453 {
454 .direction = {true, true},
455 .dai_name = "cs42l42-sdw",
456 .dai_type = SOC_SDW_DAI_TYPE_JACK,
457 .dailink = {SOC_SDW_JACK_OUT_DAI_ID, SOC_SDW_JACK_IN_DAI_ID},
458 .rtd_init = asoc_sdw_cs42l42_rtd_init,
459 .controls = generic_jack_controls,
460 .num_controls = ARRAY_SIZE(generic_jack_controls),
461 .widgets = generic_jack_widgets,
462 .num_widgets = ARRAY_SIZE(generic_jack_widgets),
463 },
464 },
465 .dai_num = 1,
466 },
467 {
468 .part_id = 0x4243,
469 .codec_name = "cs42l43-codec",
470 .count_sidecar = asoc_sdw_bridge_cs35l56_count_sidecar,
471 .add_sidecar = asoc_sdw_bridge_cs35l56_add_sidecar,
472 .dais = {
473 {
474 .direction = {true, false},
475 .dai_name = "cs42l43-dp5",
476 .dai_type = SOC_SDW_DAI_TYPE_JACK,
477 .dailink = {SOC_SDW_JACK_OUT_DAI_ID, SOC_SDW_UNUSED_DAI_ID},
478 .rtd_init = asoc_sdw_cs42l43_hs_rtd_init,
479 .controls = generic_jack_controls,
480 .num_controls = ARRAY_SIZE(generic_jack_controls),
481 .widgets = generic_jack_widgets,
482 .num_widgets = ARRAY_SIZE(generic_jack_widgets),
483 },
484 {
485 .direction = {false, true},
486 .dai_name = "cs42l43-dp1",
487 .dai_type = SOC_SDW_DAI_TYPE_MIC,
488 .dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_DMIC_DAI_ID},
489 .rtd_init = asoc_sdw_cs42l43_dmic_rtd_init,
490 .widgets = generic_dmic_widgets,
491 .num_widgets = ARRAY_SIZE(generic_dmic_widgets),
492 .quirk = SOC_SDW_CODEC_MIC,
493 .quirk_exclude = true,
494 },
495 {
496 .direction = {false, true},
497 .dai_name = "cs42l43-dp2",
498 .dai_type = SOC_SDW_DAI_TYPE_JACK,
499 .dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_JACK_IN_DAI_ID},
500 },
501 {
502 .direction = {true, false},
503 .dai_name = "cs42l43-dp6",
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_cs42l43_spk_init,
507 .rtd_init = asoc_sdw_cs42l43_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 .quirk = SOC_SDW_CODEC_SPKR | SOC_SDW_SIDECAR_AMPS,
513 },
514 },
515 .dai_num = 4,
516 },
517 {
518 .part_id = 0xaaaa, /* generic codec mockup */
519 .version_id = 0,
520 .dais = {
521 {
522 .direction = {true, true},
523 .dai_name = "sdw-mockup-aif1",
524 .dai_type = SOC_SDW_DAI_TYPE_JACK,
525 .dailink = {SOC_SDW_JACK_OUT_DAI_ID, SOC_SDW_JACK_IN_DAI_ID},
526 },
527 },
528 .dai_num = 1,
529 },
530 {
531 .part_id = 0xaa55, /* headset codec mockup */
532 .version_id = 0,
533 .dais = {
534 {
535 .direction = {true, true},
536 .dai_name = "sdw-mockup-aif1",
537 .dai_type = SOC_SDW_DAI_TYPE_JACK,
538 .dailink = {SOC_SDW_JACK_OUT_DAI_ID, SOC_SDW_JACK_IN_DAI_ID},
539 },
540 },
541 .dai_num = 1,
542 },
543 {
544 .part_id = 0x55aa, /* amplifier mockup */
545 .version_id = 0,
546 .dais = {
547 {
548 .direction = {true, true},
549 .dai_name = "sdw-mockup-aif1",
550 .dai_type = SOC_SDW_DAI_TYPE_AMP,
551 .dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_AMP_IN_DAI_ID},
552 },
553 },
554 .dai_num = 1,
555 },
556 {
557 .part_id = 0x5555,
558 .version_id = 0,
559 .dais = {
560 {
561 .dai_name = "sdw-mockup-aif1",
562 .direction = {false, true},
563 .dai_type = SOC_SDW_DAI_TYPE_MIC,
564 .dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_DMIC_DAI_ID},
565 },
566 },
567 .dai_num = 1,
568 },
569 };
570 EXPORT_SYMBOL_NS(codec_info_list, SND_SOC_SDW_UTILS);
571
asoc_sdw_get_codec_info_list_count(void)572 int asoc_sdw_get_codec_info_list_count(void)
573 {
574 return ARRAY_SIZE(codec_info_list);
575 };
576 EXPORT_SYMBOL_NS(asoc_sdw_get_codec_info_list_count, SND_SOC_SDW_UTILS);
577
asoc_sdw_find_codec_info_part(const u64 adr)578 struct asoc_sdw_codec_info *asoc_sdw_find_codec_info_part(const u64 adr)
579 {
580 unsigned int part_id, sdw_version;
581 int i;
582
583 part_id = SDW_PART_ID(adr);
584 sdw_version = SDW_VERSION(adr);
585 for (i = 0; i < ARRAY_SIZE(codec_info_list); i++)
586 /*
587 * A codec info is for all sdw version with the part id if
588 * version_id is not specified in the codec info.
589 */
590 if (part_id == codec_info_list[i].part_id &&
591 (!codec_info_list[i].version_id ||
592 sdw_version == codec_info_list[i].version_id))
593 return &codec_info_list[i];
594
595 return NULL;
596 }
597 EXPORT_SYMBOL_NS(asoc_sdw_find_codec_info_part, SND_SOC_SDW_UTILS);
598
asoc_sdw_find_codec_info_acpi(const u8 * acpi_id)599 struct asoc_sdw_codec_info *asoc_sdw_find_codec_info_acpi(const u8 *acpi_id)
600 {
601 int i;
602
603 if (!acpi_id[0])
604 return NULL;
605
606 for (i = 0; i < ARRAY_SIZE(codec_info_list); i++)
607 if (!memcmp(codec_info_list[i].acpi_id, acpi_id, ACPI_ID_LEN))
608 return &codec_info_list[i];
609
610 return NULL;
611 }
612 EXPORT_SYMBOL_NS(asoc_sdw_find_codec_info_acpi, SND_SOC_SDW_UTILS);
613
asoc_sdw_find_codec_info_dai(const char * dai_name,int * dai_index)614 struct asoc_sdw_codec_info *asoc_sdw_find_codec_info_dai(const char *dai_name, int *dai_index)
615 {
616 int i, j;
617
618 for (i = 0; i < ARRAY_SIZE(codec_info_list); i++) {
619 for (j = 0; j < codec_info_list[i].dai_num; j++) {
620 if (!strcmp(codec_info_list[i].dais[j].dai_name, dai_name)) {
621 *dai_index = j;
622 return &codec_info_list[i];
623 }
624 }
625 }
626
627 return NULL;
628 }
629 EXPORT_SYMBOL_NS(asoc_sdw_find_codec_info_dai, SND_SOC_SDW_UTILS);
630
asoc_sdw_rtd_init(struct snd_soc_pcm_runtime * rtd)631 int asoc_sdw_rtd_init(struct snd_soc_pcm_runtime *rtd)
632 {
633 struct snd_soc_card *card = rtd->card;
634 struct asoc_sdw_codec_info *codec_info;
635 struct snd_soc_dai *dai;
636 int dai_index;
637 int ret;
638 int i;
639
640 for_each_rtd_codec_dais(rtd, i, dai) {
641 codec_info = asoc_sdw_find_codec_info_dai(dai->name, &dai_index);
642 if (!codec_info)
643 return -EINVAL;
644
645 /*
646 * A codec dai can be connected to different dai links for capture and playback,
647 * but we only need to call the rtd_init function once.
648 * The rtd_init for each codec dai is independent. So, the order of rtd_init
649 * doesn't matter.
650 */
651 if (codec_info->dais[dai_index].rtd_init_done)
652 continue;
653
654 /*
655 * Add card controls and dapm widgets for the first codec dai.
656 * The controls and widgets will be used for all codec dais.
657 */
658
659 if (i > 0)
660 goto skip_add_controls_widgets;
661
662 if (codec_info->dais[dai_index].controls) {
663 ret = snd_soc_add_card_controls(card, codec_info->dais[dai_index].controls,
664 codec_info->dais[dai_index].num_controls);
665 if (ret) {
666 dev_err(card->dev, "%#x controls addition failed: %d\n",
667 codec_info->part_id, ret);
668 return ret;
669 }
670 }
671 if (codec_info->dais[dai_index].widgets) {
672 ret = snd_soc_dapm_new_controls(&card->dapm,
673 codec_info->dais[dai_index].widgets,
674 codec_info->dais[dai_index].num_widgets);
675 if (ret) {
676 dev_err(card->dev, "%#x widgets addition failed: %d\n",
677 codec_info->part_id, ret);
678 return ret;
679 }
680 }
681
682 skip_add_controls_widgets:
683 if (codec_info->dais[dai_index].rtd_init) {
684 ret = codec_info->dais[dai_index].rtd_init(rtd, dai);
685 if (ret)
686 return ret;
687 }
688 codec_info->dais[dai_index].rtd_init_done = true;
689 }
690
691 return 0;
692 }
693 EXPORT_SYMBOL_NS(asoc_sdw_rtd_init, SND_SOC_SDW_UTILS);
694
695 /* these wrappers are only needed to avoid typecast compilation errors */
asoc_sdw_startup(struct snd_pcm_substream * substream)696 int asoc_sdw_startup(struct snd_pcm_substream *substream)
697 {
698 return sdw_startup_stream(substream);
699 }
700 EXPORT_SYMBOL_NS(asoc_sdw_startup, SND_SOC_SDW_UTILS);
701
asoc_sdw_prepare(struct snd_pcm_substream * substream)702 int asoc_sdw_prepare(struct snd_pcm_substream *substream)
703 {
704 struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
705 struct sdw_stream_runtime *sdw_stream;
706 struct snd_soc_dai *dai;
707
708 /* Find stream from first CPU DAI */
709 dai = snd_soc_rtd_to_cpu(rtd, 0);
710
711 sdw_stream = snd_soc_dai_get_stream(dai, substream->stream);
712 if (IS_ERR(sdw_stream)) {
713 dev_err(rtd->dev, "no stream found for DAI %s\n", dai->name);
714 return PTR_ERR(sdw_stream);
715 }
716
717 return sdw_prepare_stream(sdw_stream);
718 }
719 EXPORT_SYMBOL_NS(asoc_sdw_prepare, SND_SOC_SDW_UTILS);
720
asoc_sdw_trigger(struct snd_pcm_substream * substream,int cmd)721 int asoc_sdw_trigger(struct snd_pcm_substream *substream, int cmd)
722 {
723 struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
724 struct sdw_stream_runtime *sdw_stream;
725 struct snd_soc_dai *dai;
726 int ret;
727
728 /* Find stream from first CPU DAI */
729 dai = snd_soc_rtd_to_cpu(rtd, 0);
730
731 sdw_stream = snd_soc_dai_get_stream(dai, substream->stream);
732 if (IS_ERR(sdw_stream)) {
733 dev_err(rtd->dev, "no stream found for DAI %s\n", dai->name);
734 return PTR_ERR(sdw_stream);
735 }
736
737 switch (cmd) {
738 case SNDRV_PCM_TRIGGER_START:
739 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
740 case SNDRV_PCM_TRIGGER_RESUME:
741 ret = sdw_enable_stream(sdw_stream);
742 break;
743
744 case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
745 case SNDRV_PCM_TRIGGER_SUSPEND:
746 case SNDRV_PCM_TRIGGER_STOP:
747 ret = sdw_disable_stream(sdw_stream);
748 break;
749 default:
750 ret = -EINVAL;
751 break;
752 }
753
754 if (ret)
755 dev_err(rtd->dev, "%s trigger %d failed: %d\n", __func__, cmd, ret);
756
757 return ret;
758 }
759 EXPORT_SYMBOL_NS(asoc_sdw_trigger, SND_SOC_SDW_UTILS);
760
asoc_sdw_hw_params(struct snd_pcm_substream * substream,struct snd_pcm_hw_params * params)761 int asoc_sdw_hw_params(struct snd_pcm_substream *substream,
762 struct snd_pcm_hw_params *params)
763 {
764 struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
765 struct snd_soc_dai_link_ch_map *ch_maps;
766 int ch = params_channels(params);
767 unsigned int ch_mask;
768 int num_codecs;
769 int step;
770 int i;
771
772 if (!rtd->dai_link->ch_maps)
773 return 0;
774
775 /* Identical data will be sent to all codecs in playback */
776 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
777 ch_mask = GENMASK(ch - 1, 0);
778 step = 0;
779 } else {
780 num_codecs = rtd->dai_link->num_codecs;
781
782 if (ch < num_codecs || ch % num_codecs != 0) {
783 dev_err(rtd->dev, "Channels number %d is invalid when codec number = %d\n",
784 ch, num_codecs);
785 return -EINVAL;
786 }
787
788 ch_mask = GENMASK(ch / num_codecs - 1, 0);
789 step = hweight_long(ch_mask);
790 }
791
792 /*
793 * The captured data will be combined from each cpu DAI if the dai
794 * link has more than one codec DAIs. Set codec channel mask and
795 * ASoC will set the corresponding channel numbers for each cpu dai.
796 */
797 for_each_link_ch_maps(rtd->dai_link, i, ch_maps)
798 ch_maps->ch_mask = ch_mask << (i * step);
799
800 return 0;
801 }
802 EXPORT_SYMBOL_NS(asoc_sdw_hw_params, SND_SOC_SDW_UTILS);
803
asoc_sdw_hw_free(struct snd_pcm_substream * substream)804 int asoc_sdw_hw_free(struct snd_pcm_substream *substream)
805 {
806 struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
807 struct sdw_stream_runtime *sdw_stream;
808 struct snd_soc_dai *dai;
809
810 /* Find stream from first CPU DAI */
811 dai = snd_soc_rtd_to_cpu(rtd, 0);
812
813 sdw_stream = snd_soc_dai_get_stream(dai, substream->stream);
814 if (IS_ERR(sdw_stream)) {
815 dev_err(rtd->dev, "no stream found for DAI %s\n", dai->name);
816 return PTR_ERR(sdw_stream);
817 }
818
819 return sdw_deprepare_stream(sdw_stream);
820 }
821 EXPORT_SYMBOL_NS(asoc_sdw_hw_free, SND_SOC_SDW_UTILS);
822
asoc_sdw_shutdown(struct snd_pcm_substream * substream)823 void asoc_sdw_shutdown(struct snd_pcm_substream *substream)
824 {
825 sdw_shutdown_stream(substream);
826 }
827 EXPORT_SYMBOL_NS(asoc_sdw_shutdown, SND_SOC_SDW_UTILS);
828
asoc_sdw_is_unique_device(const struct snd_soc_acpi_link_adr * adr_link,unsigned int sdw_version,unsigned int mfg_id,unsigned int part_id,unsigned int class_id,int index_in_link)829 static bool asoc_sdw_is_unique_device(const struct snd_soc_acpi_link_adr *adr_link,
830 unsigned int sdw_version,
831 unsigned int mfg_id,
832 unsigned int part_id,
833 unsigned int class_id,
834 int index_in_link)
835 {
836 int i;
837
838 for (i = 0; i < adr_link->num_adr; i++) {
839 unsigned int sdw1_version, mfg1_id, part1_id, class1_id;
840 u64 adr;
841
842 /* skip itself */
843 if (i == index_in_link)
844 continue;
845
846 adr = adr_link->adr_d[i].adr;
847
848 sdw1_version = SDW_VERSION(adr);
849 mfg1_id = SDW_MFG_ID(adr);
850 part1_id = SDW_PART_ID(adr);
851 class1_id = SDW_CLASS_ID(adr);
852
853 if (sdw_version == sdw1_version &&
854 mfg_id == mfg1_id &&
855 part_id == part1_id &&
856 class_id == class1_id)
857 return false;
858 }
859
860 return true;
861 }
862
asoc_sdw_get_codec_name(struct device * dev,const struct asoc_sdw_codec_info * codec_info,const struct snd_soc_acpi_link_adr * adr_link,int adr_index)863 const char *asoc_sdw_get_codec_name(struct device *dev,
864 const struct asoc_sdw_codec_info *codec_info,
865 const struct snd_soc_acpi_link_adr *adr_link,
866 int adr_index)
867 {
868 u64 adr = adr_link->adr_d[adr_index].adr;
869 unsigned int sdw_version = SDW_VERSION(adr);
870 unsigned int link_id = SDW_DISCO_LINK_ID(adr);
871 unsigned int unique_id = SDW_UNIQUE_ID(adr);
872 unsigned int mfg_id = SDW_MFG_ID(adr);
873 unsigned int part_id = SDW_PART_ID(adr);
874 unsigned int class_id = SDW_CLASS_ID(adr);
875
876 if (codec_info->codec_name)
877 return devm_kstrdup(dev, codec_info->codec_name, GFP_KERNEL);
878 else if (asoc_sdw_is_unique_device(adr_link, sdw_version, mfg_id, part_id,
879 class_id, adr_index))
880 return devm_kasprintf(dev, GFP_KERNEL, "sdw:0:%01x:%04x:%04x:%02x",
881 link_id, mfg_id, part_id, class_id);
882 else
883 return devm_kasprintf(dev, GFP_KERNEL, "sdw:0:%01x:%04x:%04x:%02x:%01x",
884 link_id, mfg_id, part_id, class_id, unique_id);
885
886 return NULL;
887 }
888 EXPORT_SYMBOL_NS(asoc_sdw_get_codec_name, SND_SOC_SDW_UTILS);
889
890 /* helper to get the link that the codec DAI is used */
asoc_sdw_mc_find_codec_dai_used(struct snd_soc_card * card,const char * dai_name)891 struct snd_soc_dai_link *asoc_sdw_mc_find_codec_dai_used(struct snd_soc_card *card,
892 const char *dai_name)
893 {
894 struct snd_soc_dai_link *dai_link;
895 int i;
896 int j;
897
898 for_each_card_prelinks(card, i, dai_link) {
899 for (j = 0; j < dai_link->num_codecs; j++) {
900 /* Check each codec in a link */
901 if (!strcmp(dai_link->codecs[j].dai_name, dai_name))
902 return dai_link;
903 }
904 }
905 return NULL;
906 }
907 EXPORT_SYMBOL_NS(asoc_sdw_mc_find_codec_dai_used, SND_SOC_SDW_UTILS);
908
asoc_sdw_mc_dailink_exit_loop(struct snd_soc_card * card)909 void asoc_sdw_mc_dailink_exit_loop(struct snd_soc_card *card)
910 {
911 struct snd_soc_dai_link *dai_link;
912 struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card);
913 int ret;
914 int i, j;
915
916 for (i = 0; i < ctx->codec_info_list_count; i++) {
917 for (j = 0; j < codec_info_list[i].dai_num; j++) {
918 codec_info_list[i].dais[j].rtd_init_done = false;
919 /* Check each dai in codec_info_lis to see if it is used in the link */
920 if (!codec_info_list[i].dais[j].exit)
921 continue;
922 /*
923 * We don't need to call .exit function if there is no matched
924 * dai link found.
925 */
926 dai_link = asoc_sdw_mc_find_codec_dai_used(card,
927 codec_info_list[i].dais[j].dai_name);
928 if (dai_link) {
929 /* Do the .exit function if the codec dai is used in the link */
930 ret = codec_info_list[i].dais[j].exit(card, dai_link);
931 if (ret)
932 dev_warn(card->dev,
933 "codec exit failed %d\n",
934 ret);
935 break;
936 }
937 }
938 }
939 }
940 EXPORT_SYMBOL_NS(asoc_sdw_mc_dailink_exit_loop, SND_SOC_SDW_UTILS);
941
asoc_sdw_card_late_probe(struct snd_soc_card * card)942 int asoc_sdw_card_late_probe(struct snd_soc_card *card)
943 {
944 int ret = 0;
945 int i;
946
947 for (i = 0; i < ARRAY_SIZE(codec_info_list); i++) {
948 if (codec_info_list[i].codec_card_late_probe) {
949 ret = codec_info_list[i].codec_card_late_probe(card);
950 if (ret < 0)
951 return ret;
952 }
953 }
954 return ret;
955 }
956 EXPORT_SYMBOL_NS(asoc_sdw_card_late_probe, SND_SOC_SDW_UTILS);
957
asoc_sdw_init_dai_link(struct device * dev,struct snd_soc_dai_link * dai_links,int * be_id,char * name,int playback,int capture,struct snd_soc_dai_link_component * cpus,int cpus_num,struct snd_soc_dai_link_component * platform_component,int num_platforms,struct snd_soc_dai_link_component * codecs,int codecs_num,int (* init)(struct snd_soc_pcm_runtime * rtd),const struct snd_soc_ops * ops)958 void asoc_sdw_init_dai_link(struct device *dev, struct snd_soc_dai_link *dai_links,
959 int *be_id, char *name, int playback, int capture,
960 struct snd_soc_dai_link_component *cpus, int cpus_num,
961 struct snd_soc_dai_link_component *platform_component,
962 int num_platforms, struct snd_soc_dai_link_component *codecs,
963 int codecs_num, int (*init)(struct snd_soc_pcm_runtime *rtd),
964 const struct snd_soc_ops *ops)
965 {
966 dev_dbg(dev, "create dai link %s, id %d\n", name, *be_id);
967 dai_links->id = (*be_id)++;
968 dai_links->name = name;
969 dai_links->platforms = platform_component;
970 dai_links->num_platforms = num_platforms;
971 dai_links->no_pcm = 1;
972 dai_links->cpus = cpus;
973 dai_links->num_cpus = cpus_num;
974 dai_links->codecs = codecs;
975 dai_links->num_codecs = codecs_num;
976 dai_links->playback_only = playback && !capture;
977 dai_links->capture_only = !playback && capture;
978 dai_links->init = init;
979 dai_links->ops = ops;
980 }
981 EXPORT_SYMBOL_NS(asoc_sdw_init_dai_link, SND_SOC_SDW_UTILS);
982
asoc_sdw_init_simple_dai_link(struct device * dev,struct snd_soc_dai_link * dai_links,int * be_id,char * name,int playback,int capture,const char * cpu_dai_name,const char * platform_comp_name,int num_platforms,const char * codec_name,const char * codec_dai_name,int (* init)(struct snd_soc_pcm_runtime * rtd),const struct snd_soc_ops * ops)983 int asoc_sdw_init_simple_dai_link(struct device *dev, struct snd_soc_dai_link *dai_links,
984 int *be_id, char *name, int playback, int capture,
985 const char *cpu_dai_name, const char *platform_comp_name,
986 int num_platforms, const char *codec_name,
987 const char *codec_dai_name,
988 int (*init)(struct snd_soc_pcm_runtime *rtd),
989 const struct snd_soc_ops *ops)
990 {
991 struct snd_soc_dai_link_component *dlc;
992
993 /* Allocate three DLCs one for the CPU, one for platform and one for the CODEC */
994 dlc = devm_kcalloc(dev, 3, sizeof(*dlc), GFP_KERNEL);
995 if (!dlc || !name || !cpu_dai_name || !platform_comp_name || !codec_name || !codec_dai_name)
996 return -ENOMEM;
997
998 dlc[0].dai_name = cpu_dai_name;
999 dlc[1].name = platform_comp_name;
1000
1001 dlc[2].name = codec_name;
1002 dlc[2].dai_name = codec_dai_name;
1003
1004 asoc_sdw_init_dai_link(dev, dai_links, be_id, name, playback, capture,
1005 &dlc[0], 1, &dlc[1], num_platforms,
1006 &dlc[2], 1, init, ops);
1007
1008 return 0;
1009 }
1010 EXPORT_SYMBOL_NS(asoc_sdw_init_simple_dai_link, SND_SOC_SDW_UTILS);
1011
asoc_sdw_count_sdw_endpoints(struct snd_soc_card * card,int * num_devs,int * num_ends)1012 int asoc_sdw_count_sdw_endpoints(struct snd_soc_card *card, int *num_devs, int *num_ends)
1013 {
1014 struct device *dev = card->dev;
1015 struct snd_soc_acpi_mach *mach = dev_get_platdata(dev);
1016 struct snd_soc_acpi_mach_params *mach_params = &mach->mach_params;
1017 const struct snd_soc_acpi_link_adr *adr_link;
1018 int i;
1019
1020 for (adr_link = mach_params->links; adr_link->num_adr; adr_link++) {
1021 *num_devs += adr_link->num_adr;
1022
1023 for (i = 0; i < adr_link->num_adr; i++)
1024 *num_ends += adr_link->adr_d[i].num_endpoints;
1025 }
1026
1027 dev_dbg(dev, "Found %d devices with %d endpoints\n", *num_devs, *num_ends);
1028
1029 return 0;
1030 }
1031 EXPORT_SYMBOL_NS(asoc_sdw_count_sdw_endpoints, SND_SOC_SDW_UTILS);
1032
asoc_sdw_find_dailink(struct asoc_sdw_dailink * dailinks,const struct snd_soc_acpi_endpoint * new)1033 struct asoc_sdw_dailink *asoc_sdw_find_dailink(struct asoc_sdw_dailink *dailinks,
1034 const struct snd_soc_acpi_endpoint *new)
1035 {
1036 while (dailinks->initialised) {
1037 if (new->aggregated && dailinks->group_id == new->group_id)
1038 return dailinks;
1039
1040 dailinks++;
1041 }
1042
1043 INIT_LIST_HEAD(&dailinks->endpoints);
1044 dailinks->group_id = new->group_id;
1045 dailinks->initialised = true;
1046
1047 return dailinks;
1048 }
1049 EXPORT_SYMBOL_NS(asoc_sdw_find_dailink, SND_SOC_SDW_UTILS);
1050
asoc_sdw_parse_sdw_endpoints(struct snd_soc_card * card,struct asoc_sdw_dailink * soc_dais,struct asoc_sdw_endpoint * soc_ends,int * num_devs)1051 int asoc_sdw_parse_sdw_endpoints(struct snd_soc_card *card,
1052 struct asoc_sdw_dailink *soc_dais,
1053 struct asoc_sdw_endpoint *soc_ends,
1054 int *num_devs)
1055 {
1056 struct device *dev = card->dev;
1057 struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card);
1058 struct snd_soc_acpi_mach *mach = dev_get_platdata(dev);
1059 struct snd_soc_acpi_mach_params *mach_params = &mach->mach_params;
1060 const struct snd_soc_acpi_link_adr *adr_link;
1061 struct asoc_sdw_endpoint *soc_end = soc_ends;
1062 int num_dais = 0;
1063 int i, j;
1064 int ret;
1065
1066 for (adr_link = mach_params->links; adr_link->num_adr; adr_link++) {
1067 int num_link_dailinks = 0;
1068
1069 if (!is_power_of_2(adr_link->mask)) {
1070 dev_err(dev, "link with multiple mask bits: 0x%x\n",
1071 adr_link->mask);
1072 return -EINVAL;
1073 }
1074
1075 for (i = 0; i < adr_link->num_adr; i++) {
1076 const struct snd_soc_acpi_adr_device *adr_dev = &adr_link->adr_d[i];
1077 struct asoc_sdw_codec_info *codec_info;
1078 const char *codec_name;
1079
1080 if (!adr_dev->name_prefix) {
1081 dev_err(dev, "codec 0x%llx does not have a name prefix\n",
1082 adr_dev->adr);
1083 return -EINVAL;
1084 }
1085
1086 codec_info = asoc_sdw_find_codec_info_part(adr_dev->adr);
1087 if (!codec_info)
1088 return -EINVAL;
1089
1090 ctx->ignore_internal_dmic |= codec_info->ignore_internal_dmic;
1091
1092 codec_name = asoc_sdw_get_codec_name(dev, codec_info, adr_link, i);
1093 if (!codec_name)
1094 return -ENOMEM;
1095
1096 dev_dbg(dev, "Adding prefix %s for %s\n",
1097 adr_dev->name_prefix, codec_name);
1098
1099 soc_end->name_prefix = adr_dev->name_prefix;
1100
1101 if (codec_info->count_sidecar && codec_info->add_sidecar) {
1102 ret = codec_info->count_sidecar(card, &num_dais, num_devs);
1103 if (ret)
1104 return ret;
1105
1106 soc_end->include_sidecar = true;
1107 }
1108
1109 for (j = 0; j < adr_dev->num_endpoints; j++) {
1110 const struct snd_soc_acpi_endpoint *adr_end;
1111 const struct asoc_sdw_dai_info *dai_info;
1112 struct asoc_sdw_dailink *soc_dai;
1113 int stream;
1114
1115 adr_end = &adr_dev->endpoints[j];
1116 dai_info = &codec_info->dais[adr_end->num];
1117 soc_dai = asoc_sdw_find_dailink(soc_dais, adr_end);
1118
1119 if (dai_info->quirk &&
1120 !(dai_info->quirk_exclude ^ !!(dai_info->quirk & ctx->mc_quirk)))
1121 continue;
1122
1123 dev_dbg(dev,
1124 "Add dev: %d, 0x%llx end: %d, dai: %d, %c/%c to %s: %d\n",
1125 ffs(adr_link->mask) - 1, adr_dev->adr,
1126 adr_end->num, dai_info->dai_type,
1127 dai_info->direction[SNDRV_PCM_STREAM_PLAYBACK] ? 'P' : '-',
1128 dai_info->direction[SNDRV_PCM_STREAM_CAPTURE] ? 'C' : '-',
1129 adr_end->aggregated ? "group" : "solo",
1130 adr_end->group_id);
1131
1132 if (adr_end->num >= codec_info->dai_num) {
1133 dev_err(dev,
1134 "%d is too many endpoints for codec: 0x%x\n",
1135 adr_end->num, codec_info->part_id);
1136 return -EINVAL;
1137 }
1138
1139 for_each_pcm_streams(stream) {
1140 if (dai_info->direction[stream] &&
1141 dai_info->dailink[stream] < 0) {
1142 dev_err(dev,
1143 "Invalid dailink id %d for codec: 0x%x\n",
1144 dai_info->dailink[stream],
1145 codec_info->part_id);
1146 return -EINVAL;
1147 }
1148
1149 if (dai_info->direction[stream]) {
1150 num_dais += !soc_dai->num_devs[stream];
1151 soc_dai->num_devs[stream]++;
1152 soc_dai->link_mask[stream] |= adr_link->mask;
1153 }
1154 }
1155
1156 num_link_dailinks += !!list_empty(&soc_dai->endpoints);
1157 list_add_tail(&soc_end->list, &soc_dai->endpoints);
1158
1159 soc_end->link_mask = adr_link->mask;
1160 soc_end->codec_name = codec_name;
1161 soc_end->codec_info = codec_info;
1162 soc_end->dai_info = dai_info;
1163 soc_end++;
1164 }
1165 }
1166
1167 ctx->append_dai_type |= (num_link_dailinks > 1);
1168 }
1169
1170 return num_dais;
1171 }
1172 EXPORT_SYMBOL_NS(asoc_sdw_parse_sdw_endpoints, SND_SOC_SDW_UTILS);
1173
1174 MODULE_LICENSE("GPL");
1175 MODULE_DESCRIPTION("SoundWire ASoC helpers");
1176