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