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.
6 * soc-sdw-utils.c - common SoundWire machine driver helper functions
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>
15 static const struct snd_soc_dapm_widget generic_dmic_widgets
[] = {
16 SND_SOC_DAPM_MIC("DMIC", NULL
),
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
),
24 static const struct snd_kcontrol_new generic_jack_controls
[] = {
25 SOC_DAPM_PIN_SWITCH("Headphone"),
26 SOC_DAPM_PIN_SWITCH("Headset Mic"),
29 static const struct snd_soc_dapm_widget generic_spk_widgets
[] = {
30 SND_SOC_DAPM_SPK("Speaker", NULL
),
33 static const struct snd_kcontrol_new generic_spk_controls
[] = {
34 SOC_DAPM_PIN_SWITCH("Speaker"),
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
),
42 static const struct snd_kcontrol_new maxim_controls
[] = {
43 SOC_DAPM_PIN_SWITCH("Left Spk"),
44 SOC_DAPM_PIN_SWITCH("Right Spk"),
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
),
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"),
59 struct asoc_sdw_codec_info codec_info_list
[] = {
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
),
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
),
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
),
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
),
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
),
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
,
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
,
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
),
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
,
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
,
214 .acpi_id
= "10EC1308",
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
),
231 .ops
= &soc_sdw_rt1308_i2s_ops
,
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
),
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
),
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
),
293 .ignore_internal_dmic
= true,
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
,
308 .ignore_internal_dmic
= true,
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
,
323 .ignore_internal_dmic
= true,
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
,
338 .ignore_internal_dmic
= true,
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
,
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
),
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
),
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
,
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
),
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,
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
,
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
),
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
),
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
),
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
),
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
),
524 .codec_name
= "cs42l43-codec",
525 .count_sidecar
= asoc_sdw_bridge_cs35l56_count_sidecar
,
526 .add_sidecar
= asoc_sdw_bridge_cs35l56_add_sidecar
,
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
),
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,
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
},
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
,
573 .part_id
= 0xaaaa, /* generic codec mockup */
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
},
586 .part_id
= 0xaa55, /* headset codec mockup */
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
},
599 .part_id
= 0x55aa, /* amplifier mockup */
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
},
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
},
625 EXPORT_SYMBOL_NS(codec_info_list
, "SND_SOC_SDW_UTILS");
627 int asoc_sdw_get_codec_info_list_count(void)
629 return ARRAY_SIZE(codec_info_list
);
631 EXPORT_SYMBOL_NS(asoc_sdw_get_codec_info_list_count
, "SND_SOC_SDW_UTILS");
633 struct asoc_sdw_codec_info
*asoc_sdw_find_codec_info_part(const u64 adr
)
635 unsigned int part_id
, sdw_version
;
638 part_id
= SDW_PART_ID(adr
);
639 sdw_version
= SDW_VERSION(adr
);
640 for (i
= 0; i
< ARRAY_SIZE(codec_info_list
); i
++)
642 * A codec info is for all sdw version with the part id if
643 * version_id is not specified in the codec info.
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
];
652 EXPORT_SYMBOL_NS(asoc_sdw_find_codec_info_part
, "SND_SOC_SDW_UTILS");
654 struct asoc_sdw_codec_info
*asoc_sdw_find_codec_info_acpi(const u8
*acpi_id
)
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
];
667 EXPORT_SYMBOL_NS(asoc_sdw_find_codec_info_acpi
, "SND_SOC_SDW_UTILS");
669 struct asoc_sdw_codec_info
*asoc_sdw_find_codec_info_dai(const char *dai_name
, int *dai_index
)
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
)) {
677 return &codec_info_list
[i
];
684 EXPORT_SYMBOL_NS(asoc_sdw_find_codec_info_dai
, "SND_SOC_SDW_UTILS");
686 int asoc_sdw_rtd_init(struct snd_soc_pcm_runtime
*rtd
)
688 struct snd_soc_card
*card
= rtd
->card
;
689 struct asoc_sdw_codec_info
*codec_info
;
690 struct snd_soc_dai
*dai
;
695 for_each_rtd_codec_dais(rtd
, i
, dai
) {
696 codec_info
= asoc_sdw_find_codec_info_dai(dai
->name
, &dai_index
);
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
706 if (codec_info
->dais
[dai_index
].rtd_init_done
)
710 * Add card controls and dapm widgets for the first codec dai.
711 * The controls and widgets will be used for all codec dais.
715 goto skip_add_controls_widgets
;
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
);
721 dev_err(card
->dev
, "%#x controls addition failed: %d\n",
722 codec_info
->part_id
, ret
);
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
);
731 dev_err(card
->dev
, "%#x widgets addition failed: %d\n",
732 codec_info
->part_id
, ret
);
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
);
743 codec_info
->dais
[dai_index
].rtd_init_done
= true;
748 EXPORT_SYMBOL_NS(asoc_sdw_rtd_init
, "SND_SOC_SDW_UTILS");
750 /* these wrappers are only needed to avoid typecast compilation errors */
751 int asoc_sdw_startup(struct snd_pcm_substream
*substream
)
753 return sdw_startup_stream(substream
);
755 EXPORT_SYMBOL_NS(asoc_sdw_startup
, "SND_SOC_SDW_UTILS");
757 int asoc_sdw_prepare(struct snd_pcm_substream
*substream
)
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
;
763 /* Find stream from first CPU DAI */
764 dai
= snd_soc_rtd_to_cpu(rtd
, 0);
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
);
772 return sdw_prepare_stream(sdw_stream
);
774 EXPORT_SYMBOL_NS(asoc_sdw_prepare
, "SND_SOC_SDW_UTILS");
776 int asoc_sdw_trigger(struct snd_pcm_substream
*substream
, int cmd
)
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
;
783 /* Find stream from first CPU DAI */
784 dai
= snd_soc_rtd_to_cpu(rtd
, 0);
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
);
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
);
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
);
810 dev_err(rtd
->dev
, "%s trigger %d failed: %d\n", __func__
, cmd
, ret
);
814 EXPORT_SYMBOL_NS(asoc_sdw_trigger
, "SND_SOC_SDW_UTILS");
816 int asoc_sdw_hw_params(struct snd_pcm_substream
*substream
,
817 struct snd_pcm_hw_params
*params
)
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
;
827 if (!rtd
->dai_link
->ch_maps
)
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);
835 num_codecs
= rtd
->dai_link
->num_codecs
;
837 if (ch
< num_codecs
|| ch
% num_codecs
!= 0) {
838 dev_err(rtd
->dev
, "Channels number %d is invalid when codec number = %d\n",
843 ch_mask
= GENMASK(ch
/ num_codecs
- 1, 0);
844 step
= hweight_long(ch_mask
);
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.
852 for_each_link_ch_maps(rtd
->dai_link
, i
, ch_maps
)
853 ch_maps
->ch_mask
= ch_mask
<< (i
* step
);
857 EXPORT_SYMBOL_NS(asoc_sdw_hw_params
, "SND_SOC_SDW_UTILS");
859 int asoc_sdw_hw_free(struct snd_pcm_substream
*substream
)
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
;
865 /* Find stream from first CPU DAI */
866 dai
= snd_soc_rtd_to_cpu(rtd
, 0);
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
);
874 return sdw_deprepare_stream(sdw_stream
);
876 EXPORT_SYMBOL_NS(asoc_sdw_hw_free
, "SND_SOC_SDW_UTILS");
878 void asoc_sdw_shutdown(struct snd_pcm_substream
*substream
)
880 sdw_shutdown_stream(substream
);
882 EXPORT_SYMBOL_NS(asoc_sdw_shutdown
, "SND_SOC_SDW_UTILS");
884 static bool asoc_sdw_is_unique_device(const struct snd_soc_acpi_link_adr
*adr_link
,
885 unsigned int sdw_version
,
887 unsigned int part_id
,
888 unsigned int class_id
,
893 for (i
= 0; i
< adr_link
->num_adr
; i
++) {
894 unsigned int sdw1_version
, mfg1_id
, part1_id
, class1_id
;
898 if (i
== index_in_link
)
901 adr
= adr_link
->adr_d
[i
].adr
;
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
);
908 if (sdw_version
== sdw1_version
&&
910 part_id
== part1_id
&&
911 class_id
== class1_id
)
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
,
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
);
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
);
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
);
943 EXPORT_SYMBOL_NS(asoc_sdw_get_codec_name
, "SND_SOC_SDW_UTILS");
945 /* helper to get the link that the codec DAI is used */
946 struct snd_soc_dai_link
*asoc_sdw_mc_find_codec_dai_used(struct snd_soc_card
*card
,
947 const char *dai_name
)
949 struct snd_soc_dai_link
*dai_link
;
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
))
962 EXPORT_SYMBOL_NS(asoc_sdw_mc_find_codec_dai_used
, "SND_SOC_SDW_UTILS");
964 void asoc_sdw_mc_dailink_exit_loop(struct snd_soc_card
*card
)
966 struct snd_soc_dai_link
*dai_link
;
967 struct asoc_sdw_mc_private
*ctx
= snd_soc_card_get_drvdata(card
);
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
)
978 * We don't need to call .exit function if there is no matched
981 dai_link
= asoc_sdw_mc_find_codec_dai_used(card
,
982 codec_info_list
[i
].dais
[j
].dai_name
);
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
);
988 "codec exit failed %d\n",
995 EXPORT_SYMBOL_NS(asoc_sdw_mc_dailink_exit_loop
, "SND_SOC_SDW_UTILS");
997 int asoc_sdw_card_late_probe(struct snd_soc_card
*card
)
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
);
1011 EXPORT_SYMBOL_NS(asoc_sdw_card_late_probe
, "SND_SOC_SDW_UTILS");
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
)
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
;
1038 EXPORT_SYMBOL_NS(asoc_sdw_init_dai_link
, "SND_SOC_SDW_UTILS");
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
)
1048 struct snd_soc_dai_link_component
*dlc
;
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
)
1055 dlc
[0].dai_name
= cpu_dai_name
;
1056 dlc
[1].name
= platform_comp_name
;
1058 dlc
[2].name
= codec_name
;
1059 dlc
[2].dai_name
= codec_dai_name
;
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
);
1067 EXPORT_SYMBOL_NS(asoc_sdw_init_simple_dai_link
, "SND_SOC_SDW_UTILS");
1069 int asoc_sdw_count_sdw_endpoints(struct snd_soc_card
*card
, int *num_devs
, int *num_ends
)
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
;
1077 for (adr_link
= mach_params
->links
; adr_link
->num_adr
; adr_link
++) {
1078 *num_devs
+= adr_link
->num_adr
;
1080 for (i
= 0; i
< adr_link
->num_adr
; i
++)
1081 *num_ends
+= adr_link
->adr_d
[i
].num_endpoints
;
1084 dev_dbg(dev
, "Found %d devices with %d endpoints\n", *num_devs
, *num_ends
);
1088 EXPORT_SYMBOL_NS(asoc_sdw_count_sdw_endpoints
, "SND_SOC_SDW_UTILS");
1090 struct asoc_sdw_dailink
*asoc_sdw_find_dailink(struct asoc_sdw_dailink
*dailinks
,
1091 const struct snd_soc_acpi_endpoint
*new)
1093 while (dailinks
->initialised
) {
1094 if (new->aggregated
&& dailinks
->group_id
== new->group_id
)
1100 INIT_LIST_HEAD(&dailinks
->endpoints
);
1101 dailinks
->group_id
= new->group_id
;
1102 dailinks
->initialised
= true;
1106 EXPORT_SYMBOL_NS(asoc_sdw_find_dailink
, "SND_SOC_SDW_UTILS");
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
,
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
;
1123 for (adr_link
= mach_params
->links
; adr_link
->num_adr
; adr_link
++) {
1124 int num_link_dailinks
= 0;
1126 if (!is_power_of_2(adr_link
->mask
)) {
1127 dev_err(dev
, "link with multiple mask bits: 0x%x\n",
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
;
1137 if (!adr_dev
->name_prefix
) {
1138 dev_err(dev
, "codec 0x%llx does not have a name prefix\n",
1143 codec_info
= asoc_sdw_find_codec_info_part(adr_dev
->adr
);
1147 ctx
->ignore_internal_dmic
|= codec_info
->ignore_internal_dmic
;
1149 codec_name
= asoc_sdw_get_codec_name(dev
, codec_info
, adr_link
, i
);
1153 dev_dbg(dev
, "Adding prefix %s for %s\n",
1154 adr_dev
->name_prefix
, codec_name
);
1156 soc_end
->name_prefix
= adr_dev
->name_prefix
;
1158 if (codec_info
->count_sidecar
&& codec_info
->add_sidecar
) {
1159 ret
= codec_info
->count_sidecar(card
, &num_dais
, num_devs
);
1163 soc_end
->include_sidecar
= true;
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
;
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
);
1176 if (dai_info
->quirk
&&
1177 !(dai_info
->quirk_exclude
^ !!(dai_info
->quirk
& ctx
->mc_quirk
)))
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",
1189 if (adr_end
->num
>= codec_info
->dai_num
) {
1191 "%d is too many endpoints for codec: 0x%x\n",
1192 adr_end
->num
, codec_info
->part_id
);
1196 for_each_pcm_streams(stream
) {
1197 if (dai_info
->direction
[stream
] &&
1198 dai_info
->dailink
[stream
] < 0) {
1200 "Invalid dailink id %d for codec: 0x%x\n",
1201 dai_info
->dailink
[stream
],
1202 codec_info
->part_id
);
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
;
1213 num_link_dailinks
+= !!list_empty(&soc_dai
->endpoints
);
1214 list_add_tail(&soc_end
->list
, &soc_dai
->endpoints
);
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
;
1224 ctx
->append_dai_type
|= (num_link_dailinks
> 1);
1229 EXPORT_SYMBOL_NS(asoc_sdw_parse_sdw_endpoints
, "SND_SOC_SDW_UTILS");
1231 MODULE_LICENSE("GPL");
1232 MODULE_DESCRIPTION("SoundWire ASoC helpers");