xref: /linux/sound/soc/sdw_utils/soc_sdw_utils.c (revision e3966940559d52aa1800a008dcfeec218dd31f88)
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 				.component_name = "cs42l43",
642 				.dai_type = SOC_SDW_DAI_TYPE_AMP,
643 				.dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_UNUSED_DAI_ID},
644 				.init = asoc_sdw_cs42l43_spk_init,
645 				.rtd_init = asoc_sdw_cs42l43_spk_rtd_init,
646 				.controls = generic_spk_controls,
647 				.num_controls = ARRAY_SIZE(generic_spk_controls),
648 				.widgets = generic_spk_widgets,
649 				.num_widgets = ARRAY_SIZE(generic_spk_widgets),
650 				.quirk = SOC_SDW_CODEC_SPKR | SOC_SDW_SIDECAR_AMPS,
651 			},
652 		},
653 		.dai_num = 4,
654 	},
655 	{
656 		.part_id = 0xaaaa, /* generic codec mockup */
657 		.version_id = 0,
658 		.dais = {
659 			{
660 				.direction = {true, true},
661 				.dai_name = "sdw-mockup-aif1",
662 				.dai_type = SOC_SDW_DAI_TYPE_JACK,
663 				.dailink = {SOC_SDW_JACK_OUT_DAI_ID, SOC_SDW_JACK_IN_DAI_ID},
664 			},
665 			{
666 				.direction = {true, false},
667 				.dai_name = "sdw-mockup-aif1",
668 				.dai_type = SOC_SDW_DAI_TYPE_AMP,
669 				.dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_UNUSED_DAI_ID},
670 			},
671 			{
672 				.direction = {false, true},
673 				.dai_name = "sdw-mockup-aif1",
674 				.dai_type = SOC_SDW_DAI_TYPE_MIC,
675 				.dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_DMIC_DAI_ID},
676 			},
677 		},
678 		.dai_num = 3,
679 	},
680 	{
681 		.part_id = 0xaa55, /* headset codec mockup */
682 		.version_id = 0,
683 		.dais = {
684 			{
685 				.direction = {true, true},
686 				.dai_name = "sdw-mockup-aif1",
687 				.dai_type = SOC_SDW_DAI_TYPE_JACK,
688 				.dailink = {SOC_SDW_JACK_OUT_DAI_ID, SOC_SDW_JACK_IN_DAI_ID},
689 			},
690 		},
691 		.dai_num = 1,
692 	},
693 	{
694 		.part_id = 0x55aa, /* amplifier mockup */
695 		.version_id = 0,
696 		.dais = {
697 			{
698 				.direction = {true, true},
699 				.dai_name = "sdw-mockup-aif1",
700 				.dai_type = SOC_SDW_DAI_TYPE_AMP,
701 				.dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_AMP_IN_DAI_ID},
702 			},
703 		},
704 		.dai_num = 1,
705 	},
706 	{
707 		.part_id = 0x5555,
708 		.version_id = 0,
709 		.dais = {
710 			{
711 				.dai_name = "sdw-mockup-aif1",
712 				.direction = {false, true},
713 				.dai_type = SOC_SDW_DAI_TYPE_MIC,
714 				.dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_DMIC_DAI_ID},
715 			},
716 		},
717 		.dai_num = 1,
718 	},
719 };
720 EXPORT_SYMBOL_NS(codec_info_list, "SND_SOC_SDW_UTILS");
721 
722 int asoc_sdw_get_codec_info_list_count(void)
723 {
724 	return ARRAY_SIZE(codec_info_list);
725 };
726 EXPORT_SYMBOL_NS(asoc_sdw_get_codec_info_list_count, "SND_SOC_SDW_UTILS");
727 
728 struct asoc_sdw_codec_info *asoc_sdw_find_codec_info_part(const u64 adr)
729 {
730 	unsigned int part_id, sdw_version;
731 	int i;
732 
733 	part_id = SDW_PART_ID(adr);
734 	sdw_version = SDW_VERSION(adr);
735 	for (i = 0; i < ARRAY_SIZE(codec_info_list); i++)
736 		/*
737 		 * A codec info is for all sdw version with the part id if
738 		 * version_id is not specified in the codec info.
739 		 */
740 		if (part_id == codec_info_list[i].part_id &&
741 		    (!codec_info_list[i].version_id ||
742 		     sdw_version == codec_info_list[i].version_id))
743 			return &codec_info_list[i];
744 
745 	return NULL;
746 }
747 EXPORT_SYMBOL_NS(asoc_sdw_find_codec_info_part, "SND_SOC_SDW_UTILS");
748 
749 struct asoc_sdw_codec_info *asoc_sdw_find_codec_info_acpi(const u8 *acpi_id)
750 {
751 	int i;
752 
753 	if (!acpi_id[0])
754 		return NULL;
755 
756 	for (i = 0; i < ARRAY_SIZE(codec_info_list); i++)
757 		if (!memcmp(codec_info_list[i].acpi_id, acpi_id, ACPI_ID_LEN))
758 			return &codec_info_list[i];
759 
760 	return NULL;
761 }
762 EXPORT_SYMBOL_NS(asoc_sdw_find_codec_info_acpi, "SND_SOC_SDW_UTILS");
763 
764 struct asoc_sdw_codec_info *asoc_sdw_find_codec_info_dai(const char *dai_name, int *dai_index)
765 {
766 	int i, j;
767 
768 	for (i = 0; i < ARRAY_SIZE(codec_info_list); i++) {
769 		for (j = 0; j < codec_info_list[i].dai_num; j++) {
770 			if (!strcmp(codec_info_list[i].dais[j].dai_name, dai_name)) {
771 				*dai_index = j;
772 				return &codec_info_list[i];
773 			}
774 		}
775 	}
776 
777 	return NULL;
778 }
779 EXPORT_SYMBOL_NS(asoc_sdw_find_codec_info_dai, "SND_SOC_SDW_UTILS");
780 
781 int asoc_sdw_rtd_init(struct snd_soc_pcm_runtime *rtd)
782 {
783 	struct snd_soc_card *card = rtd->card;
784 	struct asoc_sdw_codec_info *codec_info;
785 	struct snd_soc_dai *dai;
786 	const char *spk_components="";
787 	int dai_index;
788 	int ret;
789 	int i;
790 
791 	for_each_rtd_codec_dais(rtd, i, dai) {
792 		codec_info = asoc_sdw_find_codec_info_dai(dai->name, &dai_index);
793 		if (!codec_info)
794 			return -EINVAL;
795 
796 		/*
797 		 * A codec dai can be connected to different dai links for capture and playback,
798 		 * but we only need to call the rtd_init function once.
799 		 * The rtd_init for each codec dai is independent. So, the order of rtd_init
800 		 * doesn't matter.
801 		 */
802 		if (codec_info->dais[dai_index].rtd_init_done)
803 			continue;
804 
805 		/*
806 		 * Add card controls and dapm widgets for the first codec dai.
807 		 * The controls and widgets will be used for all codec dais.
808 		 */
809 
810 		if (i > 0)
811 			goto skip_add_controls_widgets;
812 
813 		if (codec_info->dais[dai_index].controls) {
814 			ret = snd_soc_add_card_controls(card, codec_info->dais[dai_index].controls,
815 							codec_info->dais[dai_index].num_controls);
816 			if (ret) {
817 				dev_err(card->dev, "%#x controls addition failed: %d\n",
818 					codec_info->part_id, ret);
819 				return ret;
820 			}
821 		}
822 		if (codec_info->dais[dai_index].widgets) {
823 			ret = snd_soc_dapm_new_controls(&card->dapm,
824 							codec_info->dais[dai_index].widgets,
825 							codec_info->dais[dai_index].num_widgets);
826 			if (ret) {
827 				dev_err(card->dev, "%#x widgets addition failed: %d\n",
828 					codec_info->part_id, ret);
829 				return ret;
830 			}
831 		}
832 
833 skip_add_controls_widgets:
834 		if (codec_info->dais[dai_index].rtd_init) {
835 			ret = codec_info->dais[dai_index].rtd_init(rtd, dai);
836 			if (ret)
837 				return ret;
838 		}
839 
840 		/* Generate the spk component string for card->components string */
841 		if (codec_info->dais[dai_index].dai_type == SOC_SDW_DAI_TYPE_AMP &&
842 		    codec_info->dais[dai_index].component_name) {
843 			if (strlen (spk_components) == 0)
844 				spk_components =
845 					devm_kasprintf(card->dev, GFP_KERNEL, "%s",
846 						       codec_info->dais[dai_index].component_name);
847 			else
848 				/* Append component name to spk_components */
849 				spk_components =
850 					devm_kasprintf(card->dev, GFP_KERNEL,
851 						       "%s+%s", spk_components,
852 						       codec_info->dais[dai_index].component_name);
853 		}
854 
855 		codec_info->dais[dai_index].rtd_init_done = true;
856 
857 	}
858 
859 	if (strlen (spk_components) > 0) {
860 		/* Update card components for speaker components */
861 		card->components = devm_kasprintf(card->dev, GFP_KERNEL, "%s spk:%s",
862 						  card->components, spk_components);
863 		if (!card->components)
864 			return -ENOMEM;
865 	}
866 
867 	return 0;
868 }
869 EXPORT_SYMBOL_NS(asoc_sdw_rtd_init, "SND_SOC_SDW_UTILS");
870 
871 /* these wrappers are only needed to avoid typecast compilation errors */
872 int asoc_sdw_startup(struct snd_pcm_substream *substream)
873 {
874 	return sdw_startup_stream(substream);
875 }
876 EXPORT_SYMBOL_NS(asoc_sdw_startup, "SND_SOC_SDW_UTILS");
877 
878 int asoc_sdw_prepare(struct snd_pcm_substream *substream)
879 {
880 	struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
881 	struct sdw_stream_runtime *sdw_stream;
882 	struct snd_soc_dai *dai;
883 
884 	/* Find stream from first CPU DAI */
885 	dai = snd_soc_rtd_to_cpu(rtd, 0);
886 
887 	sdw_stream = snd_soc_dai_get_stream(dai, substream->stream);
888 	if (IS_ERR(sdw_stream)) {
889 		dev_err(rtd->dev, "no stream found for DAI %s\n", dai->name);
890 		return PTR_ERR(sdw_stream);
891 	}
892 
893 	return sdw_prepare_stream(sdw_stream);
894 }
895 EXPORT_SYMBOL_NS(asoc_sdw_prepare, "SND_SOC_SDW_UTILS");
896 
897 int asoc_sdw_trigger(struct snd_pcm_substream *substream, int cmd)
898 {
899 	struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
900 	struct sdw_stream_runtime *sdw_stream;
901 	struct snd_soc_dai *dai;
902 	int ret;
903 
904 	/* Find stream from first CPU DAI */
905 	dai = snd_soc_rtd_to_cpu(rtd, 0);
906 
907 	sdw_stream = snd_soc_dai_get_stream(dai, substream->stream);
908 	if (IS_ERR(sdw_stream)) {
909 		dev_err(rtd->dev, "no stream found for DAI %s\n", dai->name);
910 		return PTR_ERR(sdw_stream);
911 	}
912 
913 	switch (cmd) {
914 	case SNDRV_PCM_TRIGGER_START:
915 	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
916 	case SNDRV_PCM_TRIGGER_RESUME:
917 		ret = sdw_enable_stream(sdw_stream);
918 		break;
919 
920 	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
921 	case SNDRV_PCM_TRIGGER_SUSPEND:
922 	case SNDRV_PCM_TRIGGER_STOP:
923 		ret = sdw_disable_stream(sdw_stream);
924 		break;
925 	default:
926 		ret = -EINVAL;
927 		break;
928 	}
929 
930 	if (ret)
931 		dev_err(rtd->dev, "%s trigger %d failed: %d\n", __func__, cmd, ret);
932 
933 	return ret;
934 }
935 EXPORT_SYMBOL_NS(asoc_sdw_trigger, "SND_SOC_SDW_UTILS");
936 
937 int asoc_sdw_hw_params(struct snd_pcm_substream *substream,
938 		       struct snd_pcm_hw_params *params)
939 {
940 	struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
941 	struct snd_soc_dai_link_ch_map *ch_maps;
942 	int ch = params_channels(params);
943 	unsigned int ch_mask;
944 	int num_codecs;
945 	int step;
946 	int i;
947 
948 	if (!rtd->dai_link->ch_maps)
949 		return 0;
950 
951 	/* Identical data will be sent to all codecs in playback */
952 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
953 		ch_mask = GENMASK(ch - 1, 0);
954 		step = 0;
955 	} else {
956 		num_codecs = rtd->dai_link->num_codecs;
957 
958 		if (ch < num_codecs || ch % num_codecs != 0) {
959 			dev_err(rtd->dev, "Channels number %d is invalid when codec number = %d\n",
960 				ch, num_codecs);
961 			return -EINVAL;
962 		}
963 
964 		ch_mask = GENMASK(ch / num_codecs - 1, 0);
965 		step = hweight_long(ch_mask);
966 	}
967 
968 	/*
969 	 * The captured data will be combined from each cpu DAI if the dai
970 	 * link has more than one codec DAIs. Set codec channel mask and
971 	 * ASoC will set the corresponding channel numbers for each cpu dai.
972 	 */
973 	for_each_link_ch_maps(rtd->dai_link, i, ch_maps)
974 		ch_maps->ch_mask = ch_mask << (i * step);
975 
976 	return 0;
977 }
978 EXPORT_SYMBOL_NS(asoc_sdw_hw_params, "SND_SOC_SDW_UTILS");
979 
980 int asoc_sdw_hw_free(struct snd_pcm_substream *substream)
981 {
982 	struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
983 	struct sdw_stream_runtime *sdw_stream;
984 	struct snd_soc_dai *dai;
985 
986 	/* Find stream from first CPU DAI */
987 	dai = snd_soc_rtd_to_cpu(rtd, 0);
988 
989 	sdw_stream = snd_soc_dai_get_stream(dai, substream->stream);
990 	if (IS_ERR(sdw_stream)) {
991 		dev_err(rtd->dev, "no stream found for DAI %s\n", dai->name);
992 		return PTR_ERR(sdw_stream);
993 	}
994 
995 	return sdw_deprepare_stream(sdw_stream);
996 }
997 EXPORT_SYMBOL_NS(asoc_sdw_hw_free, "SND_SOC_SDW_UTILS");
998 
999 void asoc_sdw_shutdown(struct snd_pcm_substream *substream)
1000 {
1001 	sdw_shutdown_stream(substream);
1002 }
1003 EXPORT_SYMBOL_NS(asoc_sdw_shutdown, "SND_SOC_SDW_UTILS");
1004 
1005 static bool asoc_sdw_is_unique_device(const struct snd_soc_acpi_link_adr *adr_link,
1006 				      unsigned int sdw_version,
1007 				      unsigned int mfg_id,
1008 				      unsigned int part_id,
1009 				      unsigned int class_id,
1010 				      int index_in_link)
1011 {
1012 	int i;
1013 
1014 	for (i = 0; i < adr_link->num_adr; i++) {
1015 		unsigned int sdw1_version, mfg1_id, part1_id, class1_id;
1016 		u64 adr;
1017 
1018 		/* skip itself */
1019 		if (i == index_in_link)
1020 			continue;
1021 
1022 		adr = adr_link->adr_d[i].adr;
1023 
1024 		sdw1_version = SDW_VERSION(adr);
1025 		mfg1_id = SDW_MFG_ID(adr);
1026 		part1_id = SDW_PART_ID(adr);
1027 		class1_id = SDW_CLASS_ID(adr);
1028 
1029 		if (sdw_version == sdw1_version &&
1030 		    mfg_id == mfg1_id &&
1031 		    part_id == part1_id &&
1032 		    class_id == class1_id)
1033 			return false;
1034 	}
1035 
1036 	return true;
1037 }
1038 
1039 static const char *_asoc_sdw_get_codec_name(struct device *dev,
1040 					    const struct asoc_sdw_codec_info *codec_info,
1041 					    const struct snd_soc_acpi_link_adr *adr_link,
1042 					    int adr_index)
1043 {
1044 	u64 adr = adr_link->adr_d[adr_index].adr;
1045 	unsigned int sdw_version = SDW_VERSION(adr);
1046 	unsigned int link_id = SDW_DISCO_LINK_ID(adr);
1047 	unsigned int unique_id = SDW_UNIQUE_ID(adr);
1048 	unsigned int mfg_id = SDW_MFG_ID(adr);
1049 	unsigned int part_id = SDW_PART_ID(adr);
1050 	unsigned int class_id = SDW_CLASS_ID(adr);
1051 
1052 	if (asoc_sdw_is_unique_device(adr_link, sdw_version, mfg_id, part_id,
1053 				      class_id, adr_index))
1054 		return devm_kasprintf(dev, GFP_KERNEL, "sdw:0:%01x:%04x:%04x:%02x",
1055 				      link_id, mfg_id, part_id, class_id);
1056 
1057 	return devm_kasprintf(dev, GFP_KERNEL, "sdw:0:%01x:%04x:%04x:%02x:%01x",
1058 			      link_id, mfg_id, part_id, class_id, unique_id);
1059 }
1060 
1061 const char *asoc_sdw_get_codec_name(struct device *dev,
1062 				    const struct asoc_sdw_codec_info *codec_info,
1063 				    const struct snd_soc_acpi_link_adr *adr_link,
1064 				    int adr_index)
1065 {
1066 	if (codec_info->codec_name)
1067 		return devm_kstrdup(dev, codec_info->codec_name, GFP_KERNEL);
1068 
1069 	return _asoc_sdw_get_codec_name(dev, codec_info, adr_link, adr_index);
1070 }
1071 EXPORT_SYMBOL_NS(asoc_sdw_get_codec_name, "SND_SOC_SDW_UTILS");
1072 
1073 /* helper to get the link that the codec DAI is used */
1074 struct snd_soc_dai_link *asoc_sdw_mc_find_codec_dai_used(struct snd_soc_card *card,
1075 							 const char *dai_name)
1076 {
1077 	struct snd_soc_dai_link *dai_link;
1078 	int i;
1079 	int j;
1080 
1081 	for_each_card_prelinks(card, i, dai_link) {
1082 		for (j = 0; j < dai_link->num_codecs; j++) {
1083 			/* Check each codec in a link */
1084 			if (!strcmp(dai_link->codecs[j].dai_name, dai_name))
1085 				return dai_link;
1086 		}
1087 	}
1088 	return NULL;
1089 }
1090 EXPORT_SYMBOL_NS(asoc_sdw_mc_find_codec_dai_used, "SND_SOC_SDW_UTILS");
1091 
1092 void asoc_sdw_mc_dailink_exit_loop(struct snd_soc_card *card)
1093 {
1094 	struct snd_soc_dai_link *dai_link;
1095 	struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card);
1096 	int ret;
1097 	int i, j;
1098 
1099 	for (i = 0; i < ctx->codec_info_list_count; i++) {
1100 		for (j = 0; j < codec_info_list[i].dai_num; j++) {
1101 			codec_info_list[i].dais[j].rtd_init_done = false;
1102 			/* Check each dai in codec_info_lis to see if it is used in the link */
1103 			if (!codec_info_list[i].dais[j].exit)
1104 				continue;
1105 			/*
1106 			 * We don't need to call .exit function if there is no matched
1107 			 * dai link found.
1108 			 */
1109 			dai_link = asoc_sdw_mc_find_codec_dai_used(card,
1110 							  codec_info_list[i].dais[j].dai_name);
1111 			if (dai_link) {
1112 				/* Do the .exit function if the codec dai is used in the link */
1113 				ret = codec_info_list[i].dais[j].exit(card, dai_link);
1114 				if (ret)
1115 					dev_warn(card->dev,
1116 						 "codec exit failed %d\n",
1117 						 ret);
1118 				break;
1119 			}
1120 		}
1121 	}
1122 }
1123 EXPORT_SYMBOL_NS(asoc_sdw_mc_dailink_exit_loop, "SND_SOC_SDW_UTILS");
1124 
1125 int asoc_sdw_card_late_probe(struct snd_soc_card *card)
1126 {
1127 	int ret = 0;
1128 	int i;
1129 
1130 	for (i = 0; i < ARRAY_SIZE(codec_info_list); i++) {
1131 		if (codec_info_list[i].codec_card_late_probe) {
1132 			ret = codec_info_list[i].codec_card_late_probe(card);
1133 			if (ret < 0)
1134 				return ret;
1135 		}
1136 	}
1137 	return ret;
1138 }
1139 EXPORT_SYMBOL_NS(asoc_sdw_card_late_probe, "SND_SOC_SDW_UTILS");
1140 
1141 void asoc_sdw_init_dai_link(struct device *dev, struct snd_soc_dai_link *dai_links,
1142 			    int *be_id, char *name, int playback, int capture,
1143 			    struct snd_soc_dai_link_component *cpus, int cpus_num,
1144 			    struct snd_soc_dai_link_component *platform_component,
1145 			    int num_platforms, struct snd_soc_dai_link_component *codecs,
1146 			    int codecs_num, int no_pcm,
1147 			    int (*init)(struct snd_soc_pcm_runtime *rtd),
1148 			    const struct snd_soc_ops *ops)
1149 {
1150 	dev_dbg(dev, "create dai link %s, id %d\n", name, *be_id);
1151 	dai_links->id = (*be_id)++;
1152 	dai_links->name = name;
1153 	dai_links->stream_name = name;
1154 	dai_links->platforms = platform_component;
1155 	dai_links->num_platforms = num_platforms;
1156 	dai_links->no_pcm = no_pcm;
1157 	dai_links->cpus = cpus;
1158 	dai_links->num_cpus = cpus_num;
1159 	dai_links->codecs = codecs;
1160 	dai_links->num_codecs = codecs_num;
1161 	dai_links->playback_only =  playback && !capture;
1162 	dai_links->capture_only  = !playback &&  capture;
1163 	dai_links->init = init;
1164 	dai_links->ops = ops;
1165 }
1166 EXPORT_SYMBOL_NS(asoc_sdw_init_dai_link, "SND_SOC_SDW_UTILS");
1167 
1168 int asoc_sdw_init_simple_dai_link(struct device *dev, struct snd_soc_dai_link *dai_links,
1169 				  int *be_id, char *name, int playback, int capture,
1170 				  const char *cpu_dai_name, const char *platform_comp_name,
1171 				  const char *codec_name, const char *codec_dai_name,
1172 				  int no_pcm, int (*init)(struct snd_soc_pcm_runtime *rtd),
1173 				  const struct snd_soc_ops *ops)
1174 {
1175 	struct snd_soc_dai_link_component *dlc;
1176 
1177 	/* Allocate three DLCs one for the CPU, one for platform and one for the CODEC */
1178 	dlc = devm_kcalloc(dev, 3, sizeof(*dlc), GFP_KERNEL);
1179 	if (!dlc || !name || !cpu_dai_name || !platform_comp_name || !codec_name || !codec_dai_name)
1180 		return -ENOMEM;
1181 
1182 	dlc[0].dai_name = cpu_dai_name;
1183 	dlc[1].name = platform_comp_name;
1184 
1185 	dlc[2].name = codec_name;
1186 	dlc[2].dai_name = codec_dai_name;
1187 
1188 	asoc_sdw_init_dai_link(dev, dai_links, be_id, name, playback, capture,
1189 			       &dlc[0], 1, &dlc[1], 1, &dlc[2], 1,
1190 			       no_pcm, init, ops);
1191 
1192 	return 0;
1193 }
1194 EXPORT_SYMBOL_NS(asoc_sdw_init_simple_dai_link, "SND_SOC_SDW_UTILS");
1195 
1196 int asoc_sdw_count_sdw_endpoints(struct snd_soc_card *card, int *num_devs, int *num_ends)
1197 {
1198 	struct device *dev = card->dev;
1199 	struct snd_soc_acpi_mach *mach = dev_get_platdata(dev);
1200 	struct snd_soc_acpi_mach_params *mach_params = &mach->mach_params;
1201 	const struct snd_soc_acpi_link_adr *adr_link;
1202 	int i;
1203 
1204 	for (adr_link = mach_params->links; adr_link->num_adr; adr_link++) {
1205 		*num_devs += adr_link->num_adr;
1206 
1207 		for (i = 0; i < adr_link->num_adr; i++)
1208 			*num_ends += adr_link->adr_d[i].num_endpoints;
1209 	}
1210 
1211 	dev_dbg(dev, "Found %d devices with %d endpoints\n", *num_devs, *num_ends);
1212 
1213 	return 0;
1214 }
1215 EXPORT_SYMBOL_NS(asoc_sdw_count_sdw_endpoints, "SND_SOC_SDW_UTILS");
1216 
1217 struct asoc_sdw_dailink *asoc_sdw_find_dailink(struct asoc_sdw_dailink *dailinks,
1218 					       const struct snd_soc_acpi_endpoint *new)
1219 {
1220 	while (dailinks->initialised) {
1221 		if (new->aggregated && dailinks->group_id == new->group_id)
1222 			return dailinks;
1223 
1224 		dailinks++;
1225 	}
1226 
1227 	INIT_LIST_HEAD(&dailinks->endpoints);
1228 	dailinks->group_id = new->group_id;
1229 	dailinks->initialised = true;
1230 
1231 	return dailinks;
1232 }
1233 EXPORT_SYMBOL_NS(asoc_sdw_find_dailink, "SND_SOC_SDW_UTILS");
1234 
1235 static int asoc_sdw_get_dai_type(u32 type)
1236 {
1237 	switch (type) {
1238 	case SDCA_FUNCTION_TYPE_SMART_AMP:
1239 	case SDCA_FUNCTION_TYPE_SIMPLE_AMP:
1240 		return SOC_SDW_DAI_TYPE_AMP;
1241 	case SDCA_FUNCTION_TYPE_SMART_MIC:
1242 	case SDCA_FUNCTION_TYPE_SIMPLE_MIC:
1243 	case SDCA_FUNCTION_TYPE_SPEAKER_MIC:
1244 		return SOC_SDW_DAI_TYPE_MIC;
1245 	case SDCA_FUNCTION_TYPE_UAJ:
1246 	case SDCA_FUNCTION_TYPE_RJ:
1247 	case SDCA_FUNCTION_TYPE_SIMPLE_JACK:
1248 		return SOC_SDW_DAI_TYPE_JACK;
1249 	default:
1250 		return -EINVAL;
1251 	}
1252 }
1253 
1254 /*
1255  * Check if the SDCA endpoint is present by the SDW peripheral
1256  *
1257  * @dev: Device pointer
1258  * @codec_info: Codec info pointer
1259  * @adr_link: ACPI link address
1260  * @adr_index: Index of the ACPI link address
1261  * @end_index: Index of the endpoint
1262  *
1263  * Return: 1 if the endpoint is present,
1264  *	   0 if the endpoint is not present,
1265  *	   negative error code.
1266  */
1267 
1268 static int is_sdca_endpoint_present(struct device *dev,
1269 				    struct asoc_sdw_codec_info *codec_info,
1270 				    const struct snd_soc_acpi_link_adr *adr_link,
1271 				    int adr_index, int end_index)
1272 {
1273 	const struct snd_soc_acpi_adr_device *adr_dev = &adr_link->adr_d[adr_index];
1274 	const struct snd_soc_acpi_endpoint *adr_end;
1275 	const struct asoc_sdw_dai_info *dai_info;
1276 	struct snd_soc_dai_link_component *dlc;
1277 	struct snd_soc_dai *codec_dai;
1278 	struct sdw_slave *slave;
1279 	struct device *sdw_dev;
1280 	const char *sdw_codec_name;
1281 	int i;
1282 
1283 	dlc = kzalloc(sizeof(*dlc), GFP_KERNEL);
1284 	if (!dlc)
1285 		return -ENOMEM;
1286 
1287 	adr_end = &adr_dev->endpoints[end_index];
1288 	dai_info = &codec_info->dais[adr_end->num];
1289 
1290 	dlc->dai_name = dai_info->dai_name;
1291 	codec_dai = snd_soc_find_dai_with_mutex(dlc);
1292 	if (!codec_dai) {
1293 		dev_warn(dev, "codec dai %s not registered yet\n", dlc->dai_name);
1294 		kfree(dlc);
1295 		return -EPROBE_DEFER;
1296 	}
1297 	kfree(dlc);
1298 
1299 	sdw_codec_name = _asoc_sdw_get_codec_name(dev, codec_info,
1300 						  adr_link, adr_index);
1301 	if (!sdw_codec_name)
1302 		return -ENOMEM;
1303 
1304 	sdw_dev = bus_find_device_by_name(&sdw_bus_type, NULL, sdw_codec_name);
1305 	if (!sdw_dev) {
1306 		dev_err(dev, "codec %s not found\n", sdw_codec_name);
1307 		return -EINVAL;
1308 	}
1309 
1310 	slave = dev_to_sdw_dev(sdw_dev);
1311 	if (!slave)
1312 		return -EINVAL;
1313 
1314 	/* Make sure BIOS provides SDCA properties */
1315 	if (!slave->sdca_data.interface_revision) {
1316 		dev_warn(&slave->dev, "SDCA properties not found in the BIOS\n");
1317 		return 1;
1318 	}
1319 
1320 	for (i = 0; i < slave->sdca_data.num_functions; i++) {
1321 		int dai_type = asoc_sdw_get_dai_type(slave->sdca_data.function[i].type);
1322 
1323 		if (dai_type == dai_info->dai_type) {
1324 			dev_dbg(&slave->dev, "DAI type %d sdca function %s found\n",
1325 				dai_type, slave->sdca_data.function[i].name);
1326 			return 1;
1327 		}
1328 	}
1329 
1330 	dev_dbg(&slave->dev,
1331 		"SDCA device function for DAI type %d not supported, skip endpoint\n",
1332 		dai_info->dai_type);
1333 
1334 	return 0;
1335 }
1336 
1337 int asoc_sdw_parse_sdw_endpoints(struct snd_soc_card *card,
1338 				 struct asoc_sdw_dailink *soc_dais,
1339 				 struct asoc_sdw_endpoint *soc_ends,
1340 				 int *num_devs)
1341 {
1342 	struct device *dev = card->dev;
1343 	struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card);
1344 	struct snd_soc_acpi_mach *mach = dev_get_platdata(dev);
1345 	struct snd_soc_acpi_mach_params *mach_params = &mach->mach_params;
1346 	const struct snd_soc_acpi_link_adr *adr_link;
1347 	struct asoc_sdw_endpoint *soc_end = soc_ends;
1348 	int num_dais = 0;
1349 	int i, j;
1350 	int ret;
1351 
1352 	for (adr_link = mach_params->links; adr_link->num_adr; adr_link++) {
1353 		int num_link_dailinks = 0;
1354 
1355 		if (!is_power_of_2(adr_link->mask)) {
1356 			dev_err(dev, "link with multiple mask bits: 0x%x\n",
1357 				adr_link->mask);
1358 			return -EINVAL;
1359 		}
1360 
1361 		for (i = 0; i < adr_link->num_adr; i++) {
1362 			const struct snd_soc_acpi_adr_device *adr_dev = &adr_link->adr_d[i];
1363 			struct asoc_sdw_codec_info *codec_info;
1364 			const char *codec_name;
1365 			bool check_sdca = false;
1366 
1367 			if (!adr_dev->name_prefix) {
1368 				dev_err(dev, "codec 0x%llx does not have a name prefix\n",
1369 					adr_dev->adr);
1370 				return -EINVAL;
1371 			}
1372 
1373 			codec_info = asoc_sdw_find_codec_info_part(adr_dev->adr);
1374 			if (!codec_info)
1375 				return -EINVAL;
1376 
1377 			ctx->ignore_internal_dmic |= codec_info->ignore_internal_dmic;
1378 
1379 			codec_name = asoc_sdw_get_codec_name(dev, codec_info, adr_link, i);
1380 			if (!codec_name)
1381 				return -ENOMEM;
1382 
1383 			dev_dbg(dev, "Adding prefix %s for %s\n",
1384 				adr_dev->name_prefix, codec_name);
1385 
1386 			soc_end->name_prefix = adr_dev->name_prefix;
1387 
1388 			if (codec_info->count_sidecar && codec_info->add_sidecar) {
1389 				ret = codec_info->count_sidecar(card, &num_dais, num_devs);
1390 				if (ret)
1391 					return ret;
1392 
1393 				soc_end->include_sidecar = true;
1394 			}
1395 
1396 			if (SDW_CLASS_ID(adr_dev->adr) && adr_dev->num_endpoints > 1)
1397 				check_sdca = true;
1398 
1399 			for (j = 0; j < adr_dev->num_endpoints; j++) {
1400 				const struct snd_soc_acpi_endpoint *adr_end;
1401 				const struct asoc_sdw_dai_info *dai_info;
1402 				struct asoc_sdw_dailink *soc_dai;
1403 				int stream;
1404 
1405 				adr_end = &adr_dev->endpoints[j];
1406 				dai_info = &codec_info->dais[adr_end->num];
1407 				soc_dai = asoc_sdw_find_dailink(soc_dais, adr_end);
1408 
1409 				/*
1410 				 * quirk should have higher priority than the sdca properties
1411 				 * in the BIOS. We can't always check the DAI quirk because we
1412 				 * will set the mc_quirk when the BIOS doesn't provide the right
1413 				 * information. The endpoint will be skipped if the dai_info->
1414 				 * quirk_exclude and mc_quirk are both not set if we always skip
1415 				 * the endpoint according to the quirk information. We need to
1416 				 * keep the endpoint if it is present in the BIOS. So, only
1417 				 * check the DAI quirk when the mc_quirk is set or SDCA endpoint
1418 				 * present check is not needed.
1419 				 */
1420 				if (dai_info->quirk & ctx->mc_quirk || !check_sdca) {
1421 					/*
1422 					 * Check the endpoint if a matching quirk is set or SDCA
1423 					 * endpoint check is not necessary
1424 					 */
1425 					if (dai_info->quirk &&
1426 					    !(dai_info->quirk_exclude ^ !!(dai_info->quirk & ctx->mc_quirk)))
1427 						continue;
1428 				} else {
1429 					/* Check SDCA codec endpoint if there is no matching quirk */
1430 					ret = is_sdca_endpoint_present(dev, codec_info, adr_link, i, j);
1431 					if (ret < 0)
1432 						return ret;
1433 
1434 					/* The endpoint is not present, skip */
1435 					if (!ret)
1436 						continue;
1437 				}
1438 
1439 				dev_dbg(dev,
1440 					"Add dev: %d, 0x%llx end: %d, dai: %d, %c/%c to %s: %d\n",
1441 					ffs(adr_link->mask) - 1, adr_dev->adr,
1442 					adr_end->num, dai_info->dai_type,
1443 					dai_info->direction[SNDRV_PCM_STREAM_PLAYBACK] ? 'P' : '-',
1444 					dai_info->direction[SNDRV_PCM_STREAM_CAPTURE] ? 'C' : '-',
1445 					adr_end->aggregated ? "group" : "solo",
1446 					adr_end->group_id);
1447 
1448 				if (adr_end->num >= codec_info->dai_num) {
1449 					dev_err(dev,
1450 						"%d is too many endpoints for codec: 0x%x\n",
1451 						adr_end->num, codec_info->part_id);
1452 					return -EINVAL;
1453 				}
1454 
1455 				for_each_pcm_streams(stream) {
1456 					if (dai_info->direction[stream] &&
1457 					    dai_info->dailink[stream] < 0) {
1458 						dev_err(dev,
1459 							"Invalid dailink id %d for codec: 0x%x\n",
1460 							dai_info->dailink[stream],
1461 							codec_info->part_id);
1462 						return -EINVAL;
1463 					}
1464 
1465 					if (dai_info->direction[stream]) {
1466 						num_dais += !soc_dai->num_devs[stream];
1467 						soc_dai->num_devs[stream]++;
1468 						soc_dai->link_mask[stream] |= adr_link->mask;
1469 					}
1470 				}
1471 
1472 				num_link_dailinks += !!list_empty(&soc_dai->endpoints);
1473 				list_add_tail(&soc_end->list, &soc_dai->endpoints);
1474 
1475 				soc_end->link_mask = adr_link->mask;
1476 				soc_end->codec_name = codec_name;
1477 				soc_end->codec_info = codec_info;
1478 				soc_end->dai_info = dai_info;
1479 				soc_end++;
1480 			}
1481 		}
1482 
1483 		ctx->append_dai_type |= (num_link_dailinks > 1);
1484 	}
1485 
1486 	return num_dais;
1487 }
1488 EXPORT_SYMBOL_NS(asoc_sdw_parse_sdw_endpoints, "SND_SOC_SDW_UTILS");
1489 
1490 MODULE_LICENSE("GPL");
1491 MODULE_DESCRIPTION("SoundWire ASoC helpers");
1492