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