]>
Commit | Line | Data |
---|---|---|
e149ca29 | 1 | // SPDX-License-Identifier: GPL-2.0-only |
52db12d1 PLB |
2 | // Copyright (c) 2020 Intel Corporation |
3 | ||
4 | /* | |
5 | * sof_sdw - ASOC Machine driver for Intel SoundWire platforms | |
6 | */ | |
7 | ||
8 | #include <linux/device.h> | |
9 | #include <linux/dmi.h> | |
10 | #include <linux/module.h> | |
11 | #include <linux/soundwire/sdw.h> | |
12 | #include <linux/soundwire/sdw_type.h> | |
13 | #include <sound/soc.h> | |
14 | #include <sound/soc-acpi.h> | |
15 | #include "sof_sdw_common.h" | |
8e6c00f1 | 16 | #include "../../codecs/rt711.h" |
52db12d1 | 17 | |
8e6c00f1 | 18 | unsigned long sof_sdw_quirk = RT711_JD1; |
2555ebe9 PLB |
19 | static int quirk_override = -1; |
20 | module_param_named(quirk, quirk_override, int, 0444); | |
21 | MODULE_PARM_DESC(quirk, "Board-specific quirk override"); | |
52db12d1 PLB |
22 | |
23 | #define INC_ID(BE, CPU, LINK) do { (BE)++; (CPU)++; (LINK)++; } while (0) | |
24 | ||
35d28ccd BL |
25 | #define SDW_MAX_LINKS 4 |
26 | ||
27 | /* To store SDW Pin index for each SoundWire link */ | |
28 | static unsigned int sdw_pin_index[SDW_MAX_LINKS]; | |
29 | ||
2555ebe9 PLB |
30 | static void log_quirks(struct device *dev) |
31 | { | |
752d4de4 | 32 | if (SOF_JACK_JDSRC(sof_sdw_quirk)) |
2555ebe9 | 33 | dev_dbg(dev, "quirk realtek,jack-detect-source %ld\n", |
752d4de4 | 34 | SOF_JACK_JDSRC(sof_sdw_quirk)); |
2555ebe9 PLB |
35 | if (sof_sdw_quirk & SOF_SDW_FOUR_SPK) |
36 | dev_dbg(dev, "quirk SOF_SDW_FOUR_SPK enabled\n"); | |
37 | if (sof_sdw_quirk & SOF_SDW_TGL_HDMI) | |
38 | dev_dbg(dev, "quirk SOF_SDW_TGL_HDMI enabled\n"); | |
39 | if (sof_sdw_quirk & SOF_SDW_PCH_DMIC) | |
40 | dev_dbg(dev, "quirk SOF_SDW_PCH_DMIC enabled\n"); | |
41 | if (SOF_SSP_GET_PORT(sof_sdw_quirk)) | |
42 | dev_dbg(dev, "SSP port %ld\n", | |
43 | SOF_SSP_GET_PORT(sof_sdw_quirk)); | |
2555ebe9 PLB |
44 | if (sof_sdw_quirk & SOF_SDW_NO_AGGREGATION) |
45 | dev_dbg(dev, "quirk SOF_SDW_NO_AGGREGATION enabled\n"); | |
46 | } | |
47 | ||
52db12d1 PLB |
48 | static int sof_sdw_quirk_cb(const struct dmi_system_id *id) |
49 | { | |
50 | sof_sdw_quirk = (unsigned long)id->driver_data; | |
51 | return 1; | |
52 | } | |
53 | ||
54 | static const struct dmi_system_id sof_sdw_quirk_table[] = { | |
3d09cf8d | 55 | /* CometLake devices */ |
488cdbd8 PLB |
56 | { |
57 | .callback = sof_sdw_quirk_cb, | |
58 | .matches = { | |
3d09cf8d PLB |
59 | DMI_MATCH(DMI_SYS_VENDOR, "Intel Corporation"), |
60 | DMI_MATCH(DMI_PRODUCT_NAME, "CometLake Client"), | |
9ad9bc59 | 61 | }, |
3d09cf8d | 62 | .driver_data = (void *)SOF_SDW_PCH_DMIC, |
9ad9bc59 | 63 | }, |
52db12d1 PLB |
64 | { |
65 | .callback = sof_sdw_quirk_cb, | |
66 | .matches = { | |
67 | DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"), | |
68 | DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "09C6") | |
69 | }, | |
f8f83122 | 70 | .driver_data = (void *)RT711_JD2, |
52db12d1 PLB |
71 | }, |
72 | { | |
73 | /* early version of SKU 09C6 */ | |
74 | .callback = sof_sdw_quirk_cb, | |
75 | .matches = { | |
76 | DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"), | |
77 | DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0983") | |
78 | }, | |
f8f83122 | 79 | .driver_data = (void *)RT711_JD2, |
52db12d1 PLB |
80 | }, |
81 | { | |
82 | .callback = sof_sdw_quirk_cb, | |
83 | .matches = { | |
84 | DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"), | |
85 | DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "098F"), | |
86 | }, | |
8e6c00f1 | 87 | .driver_data = (void *)(RT711_JD2 | |
52db12d1 PLB |
88 | SOF_SDW_FOUR_SPK), |
89 | }, | |
3d09cf8d | 90 | { |
52db12d1 PLB |
91 | .callback = sof_sdw_quirk_cb, |
92 | .matches = { | |
93 | DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"), | |
94 | DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0990"), | |
95 | }, | |
8e6c00f1 | 96 | .driver_data = (void *)(RT711_JD2 | |
52db12d1 PLB |
97 | SOF_SDW_FOUR_SPK), |
98 | }, | |
3d09cf8d PLB |
99 | /* IceLake devices */ |
100 | { | |
101 | .callback = sof_sdw_quirk_cb, | |
102 | .matches = { | |
103 | DMI_MATCH(DMI_SYS_VENDOR, "Intel Corporation"), | |
104 | DMI_MATCH(DMI_PRODUCT_NAME, "Ice Lake Client"), | |
105 | }, | |
106 | .driver_data = (void *)SOF_SDW_PCH_DMIC, | |
107 | }, | |
108 | /* TigerLake devices */ | |
52db12d1 PLB |
109 | { |
110 | .callback = sof_sdw_quirk_cb, | |
111 | .matches = { | |
112 | DMI_MATCH(DMI_SYS_VENDOR, "Intel Corporation"), | |
113 | DMI_MATCH(DMI_PRODUCT_NAME, | |
114 | "Tiger Lake Client Platform"), | |
115 | }, | |
8caf37e2 | 116 | .driver_data = (void *)(SOF_SDW_TGL_HDMI | |
8e6c00f1 | 117 | RT711_JD1 | |
8caf37e2 PLB |
118 | SOF_SDW_PCH_DMIC | |
119 | SOF_SSP_PORT(SOF_I2S_SSP2)), | |
52db12d1 PLB |
120 | }, |
121 | { | |
122 | .callback = sof_sdw_quirk_cb, | |
123 | .matches = { | |
3d09cf8d PLB |
124 | DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"), |
125 | DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0A3E") | |
52db12d1 | 126 | }, |
3d09cf8d | 127 | .driver_data = (void *)(SOF_SDW_TGL_HDMI | |
f8f83122 | 128 | RT711_JD2), |
52db12d1 | 129 | }, |
1071f241 PLB |
130 | { |
131 | /* another SKU of Dell Latitude 9520 */ | |
132 | .callback = sof_sdw_quirk_cb, | |
133 | .matches = { | |
134 | DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"), | |
135 | DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0A3F") | |
136 | }, | |
137 | .driver_data = (void *)(SOF_SDW_TGL_HDMI | | |
f8f83122 | 138 | RT711_JD2), |
1071f241 | 139 | }, |
b8cab69b PLB |
140 | { |
141 | /* Dell XPS 9710 */ | |
142 | .callback = sof_sdw_quirk_cb, | |
143 | .matches = { | |
144 | DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"), | |
145 | DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0A5D") | |
146 | }, | |
147 | .driver_data = (void *)(SOF_SDW_TGL_HDMI | | |
148 | RT711_JD2 | | |
b8cab69b PLB |
149 | SOF_SDW_FOUR_SPK), |
150 | }, | |
52db12d1 PLB |
151 | { |
152 | .callback = sof_sdw_quirk_cb, | |
153 | .matches = { | |
3d09cf8d PLB |
154 | DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"), |
155 | DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0A5E") | |
52db12d1 | 156 | }, |
3d09cf8d | 157 | .driver_data = (void *)(SOF_SDW_TGL_HDMI | |
8e6c00f1 | 158 | RT711_JD2 | |
3d09cf8d | 159 | SOF_SDW_FOUR_SPK), |
52db12d1 | 160 | }, |
798313f2 NM |
161 | { |
162 | .callback = sof_sdw_quirk_cb, | |
163 | .matches = { | |
164 | DMI_MATCH(DMI_SYS_VENDOR, "Google"), | |
165 | DMI_MATCH(DMI_PRODUCT_NAME, "Volteer"), | |
166 | }, | |
8caf37e2 PLB |
167 | .driver_data = (void *)(SOF_SDW_TGL_HDMI | |
168 | SOF_SDW_PCH_DMIC | | |
19f1eace YZ |
169 | SOF_SDW_FOUR_SPK | |
170 | SOF_BT_OFFLOAD_SSP(2) | | |
171 | SOF_SSP_BT_OFFLOAD_PRESENT), | |
798313f2 | 172 | }, |
626200df RW |
173 | { |
174 | .callback = sof_sdw_quirk_cb, | |
175 | .matches = { | |
176 | DMI_MATCH(DMI_SYS_VENDOR, "Google"), | |
177 | DMI_MATCH(DMI_PRODUCT_NAME, "Ripto"), | |
178 | }, | |
8caf37e2 PLB |
179 | .driver_data = (void *)(SOF_SDW_TGL_HDMI | |
180 | SOF_SDW_PCH_DMIC | | |
626200df RW |
181 | SOF_SDW_FOUR_SPK), |
182 | }, | |
d92e279d PLB |
183 | { |
184 | /* | |
185 | * this entry covers multiple HP SKUs. The family name | |
186 | * does not seem robust enough, so we use a partial | |
187 | * match that ignores the product name suffix | |
188 | * (e.g. 15-eb1xxx, 14t-ea000 or 13-aw2xxx) | |
189 | */ | |
190 | .callback = sof_sdw_quirk_cb, | |
191 | .matches = { | |
192 | DMI_MATCH(DMI_SYS_VENDOR, "HP"), | |
ce73ef6e | 193 | DMI_MATCH(DMI_PRODUCT_NAME, "HP Spectre x360 Conv"), |
d92e279d PLB |
194 | }, |
195 | .driver_data = (void *)(SOF_SDW_TGL_HDMI | | |
196 | SOF_SDW_PCH_DMIC | | |
0527b19f | 197 | RT711_JD1), |
49d1f3cc PLB |
198 | }, |
199 | { | |
200 | /* | |
201 | * this entry covers HP Spectre x360 where the DMI information | |
202 | * changed somehow | |
203 | */ | |
204 | .callback = sof_sdw_quirk_cb, | |
205 | .matches = { | |
206 | DMI_MATCH(DMI_SYS_VENDOR, "HP"), | |
207 | DMI_MATCH(DMI_BOARD_NAME, "8709"), | |
208 | }, | |
209 | .driver_data = (void *)(SOF_SDW_TGL_HDMI | | |
210 | SOF_SDW_PCH_DMIC | | |
211 | RT711_JD1), | |
d92e279d | 212 | }, |
1bd80ff2 PLB |
213 | { |
214 | /* NUC15 'Bishop County' LAPBC510 and LAPBC710 skews */ | |
215 | .callback = sof_sdw_quirk_cb, | |
216 | .matches = { | |
217 | DMI_MATCH(DMI_SYS_VENDOR, "Intel(R) Client Systems"), | |
218 | DMI_MATCH(DMI_PRODUCT_NAME, "LAPBC"), | |
219 | }, | |
220 | .driver_data = (void *)(SOF_SDW_TGL_HDMI | | |
221 | SOF_SDW_PCH_DMIC | | |
222 | RT711_JD1), | |
223 | }, | |
41deb2db PLB |
224 | { |
225 | /* NUC15 LAPBC710 skews */ | |
226 | .callback = sof_sdw_quirk_cb, | |
227 | .matches = { | |
228 | DMI_MATCH(DMI_BOARD_VENDOR, "Intel Corporation"), | |
229 | DMI_MATCH(DMI_BOARD_NAME, "LAPBC710"), | |
230 | }, | |
231 | .driver_data = (void *)(SOF_SDW_TGL_HDMI | | |
232 | SOF_SDW_PCH_DMIC | | |
233 | RT711_JD1), | |
234 | }, | |
3c728b1b EH |
235 | { |
236 | /* NUC15 'Rooks County' LAPRC510 and LAPRC710 skews */ | |
237 | .callback = sof_sdw_quirk_cb, | |
238 | .matches = { | |
239 | DMI_MATCH(DMI_SYS_VENDOR, "Intel(R) Client Systems"), | |
240 | DMI_MATCH(DMI_PRODUCT_NAME, "LAPRC"), | |
241 | }, | |
242 | .driver_data = (void *)(SOF_SDW_TGL_HDMI | | |
243 | SOF_SDW_PCH_DMIC | | |
244 | RT711_JD2_100K), | |
245 | }, | |
3d09cf8d PLB |
246 | /* TigerLake-SDCA devices */ |
247 | { | |
248 | .callback = sof_sdw_quirk_cb, | |
249 | .matches = { | |
250 | DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"), | |
251 | DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0A32") | |
252 | }, | |
253 | .driver_data = (void *)(SOF_SDW_TGL_HDMI | | |
8e6c00f1 | 254 | RT711_JD2 | |
3d09cf8d PLB |
255 | SOF_SDW_FOUR_SPK), |
256 | }, | |
64ba6d2c PLB |
257 | { |
258 | .callback = sof_sdw_quirk_cb, | |
259 | .matches = { | |
260 | DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"), | |
261 | DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0A45") | |
262 | }, | |
263 | .driver_data = (void *)(SOF_SDW_TGL_HDMI | | |
f8f83122 | 264 | RT711_JD2), |
64ba6d2c | 265 | }, |
d25bbe80 VKG |
266 | /* AlderLake devices */ |
267 | { | |
268 | .callback = sof_sdw_quirk_cb, | |
269 | .matches = { | |
270 | DMI_MATCH(DMI_SYS_VENDOR, "Intel Corporation"), | |
271 | DMI_MATCH(DMI_PRODUCT_NAME, "Alder Lake Client Platform"), | |
272 | }, | |
f28fbe57 | 273 | .driver_data = (void *)(RT711_JD2_100K | |
d25bbe80 | 274 | SOF_SDW_TGL_HDMI | |
03effde3 VKG |
275 | SOF_BT_OFFLOAD_SSP(2) | |
276 | SOF_SSP_BT_OFFLOAD_PRESENT), | |
277 | }, | |
278 | { | |
279 | .callback = sof_sdw_quirk_cb, | |
280 | .matches = { | |
281 | DMI_MATCH(DMI_SYS_VENDOR, "Google"), | |
282 | DMI_MATCH(DMI_PRODUCT_NAME, "Brya"), | |
283 | }, | |
284 | .driver_data = (void *)(SOF_SDW_TGL_HDMI | | |
285 | SOF_SDW_PCH_DMIC | | |
286 | SOF_SDW_FOUR_SPK | | |
287 | SOF_BT_OFFLOAD_SSP(2) | | |
288 | SOF_SSP_BT_OFFLOAD_PRESENT), | |
d25bbe80 | 289 | }, |
4e68eef4 PLB |
290 | { |
291 | .callback = sof_sdw_quirk_cb, | |
292 | .matches = { | |
293 | DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"), | |
294 | DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0AF0") | |
295 | }, | |
296 | .driver_data = (void *)(SOF_SDW_TGL_HDMI | | |
297 | RT711_JD2 | | |
298 | SOF_SDW_FOUR_SPK), | |
299 | }, | |
8f4fa459 GS |
300 | { |
301 | .callback = sof_sdw_quirk_cb, | |
302 | .matches = { | |
303 | DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"), | |
304 | DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0AF3"), | |
305 | }, | |
306 | /* No Jack */ | |
307 | .driver_data = (void *)(SOF_SDW_TGL_HDMI | | |
308 | SOF_SDW_FOUR_SPK), | |
309 | }, | |
0cc85f2b PLB |
310 | { |
311 | .callback = sof_sdw_quirk_cb, | |
312 | .matches = { | |
313 | DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"), | |
314 | DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0AFE") | |
315 | }, | |
316 | .driver_data = (void *)(SOF_SDW_TGL_HDMI | | |
317 | RT711_JD2 | | |
318 | SOF_SDW_FOUR_SPK), | |
319 | }, | |
4a13c949 PLB |
320 | { |
321 | .callback = sof_sdw_quirk_cb, | |
322 | .matches = { | |
323 | DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"), | |
324 | DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0AFF") | |
325 | }, | |
326 | .driver_data = (void *)(SOF_SDW_TGL_HDMI | | |
327 | RT711_JD2 | | |
328 | SOF_SDW_FOUR_SPK), | |
329 | }, | |
cf304329 GS |
330 | { |
331 | .callback = sof_sdw_quirk_cb, | |
332 | .matches = { | |
333 | DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"), | |
334 | DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0B00") | |
335 | }, | |
336 | .driver_data = (void *)(SOF_SDW_TGL_HDMI | | |
337 | RT711_JD2 | | |
338 | SOF_SDW_FOUR_SPK), | |
339 | }, | |
340 | { | |
341 | .callback = sof_sdw_quirk_cb, | |
342 | .matches = { | |
343 | DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"), | |
344 | DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0B01") | |
345 | }, | |
346 | .driver_data = (void *)(SOF_SDW_TGL_HDMI | | |
347 | RT711_JD2 | | |
348 | SOF_SDW_FOUR_SPK), | |
349 | }, | |
6fef4c2f GS |
350 | { |
351 | .callback = sof_sdw_quirk_cb, | |
352 | .matches = { | |
353 | DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"), | |
354 | DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0B11") | |
355 | }, | |
f55af705 GS |
356 | .driver_data = (void *)(SOF_SDW_TGL_HDMI | |
357 | RT711_JD2 | | |
358 | SOF_SDW_FOUR_SPK), | |
359 | }, | |
360 | { | |
361 | .callback = sof_sdw_quirk_cb, | |
362 | .matches = { | |
363 | DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"), | |
364 | DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0B12") | |
365 | }, | |
6fef4c2f GS |
366 | .driver_data = (void *)(SOF_SDW_TGL_HDMI | |
367 | RT711_JD2 | | |
368 | SOF_SDW_FOUR_SPK), | |
369 | }, | |
6448d059 GS |
370 | { |
371 | .callback = sof_sdw_quirk_cb, | |
372 | .matches = { | |
373 | DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"), | |
374 | DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0B13"), | |
375 | }, | |
376 | /* No Jack */ | |
377 | .driver_data = (void *)SOF_SDW_TGL_HDMI, | |
378 | }, | |
fb0b8d29 PLB |
379 | { |
380 | .callback = sof_sdw_quirk_cb, | |
381 | .matches = { | |
382 | DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"), | |
383 | DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0B14"), | |
384 | }, | |
385 | /* No Jack */ | |
386 | .driver_data = (void *)SOF_SDW_TGL_HDMI, | |
387 | }, | |
388 | ||
0c2ed4f0 GS |
389 | { |
390 | .callback = sof_sdw_quirk_cb, | |
391 | .matches = { | |
392 | DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"), | |
393 | DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0B29"), | |
394 | }, | |
395 | .driver_data = (void *)(SOF_SDW_TGL_HDMI | | |
396 | RT711_JD2 | | |
397 | SOF_SDW_FOUR_SPK), | |
398 | }, | |
332f6187 PLB |
399 | { |
400 | .callback = sof_sdw_quirk_cb, | |
401 | .matches = { | |
402 | DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"), | |
403 | DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0B34"), | |
404 | }, | |
405 | /* No Jack */ | |
406 | .driver_data = (void *)SOF_SDW_TGL_HDMI, | |
407 | }, | |
f7bbdf5b PLB |
408 | { |
409 | .callback = sof_sdw_quirk_cb, | |
410 | .matches = { | |
411 | DMI_MATCH(DMI_SYS_VENDOR, "HP"), | |
412 | DMI_MATCH(DMI_PRODUCT_NAME, "OMEN by HP Gaming Laptop 16-k0xxx"), | |
413 | }, | |
414 | .driver_data = (void *)(SOF_SDW_TGL_HDMI | | |
415 | RT711_JD2), | |
416 | }, | |
d608bc44 | 417 | /* RaptorLake devices */ |
3daf0281 PLB |
418 | { |
419 | .callback = sof_sdw_quirk_cb, | |
420 | .matches = { | |
421 | DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"), | |
422 | DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0BDA") | |
423 | }, | |
424 | .driver_data = (void *)(SOF_SDW_TGL_HDMI | | |
425 | RT711_JD2 | | |
426 | SOF_SDW_FOUR_SPK), | |
427 | }, | |
d608bc44 GS |
428 | { |
429 | .callback = sof_sdw_quirk_cb, | |
430 | .matches = { | |
431 | DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"), | |
432 | DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0C10"), | |
433 | }, | |
434 | /* No Jack */ | |
435 | .driver_data = (void *)(SOF_SDW_TGL_HDMI | | |
436 | SOF_SDW_FOUR_SPK), | |
437 | }, | |
d84e10da GS |
438 | { |
439 | .callback = sof_sdw_quirk_cb, | |
440 | .matches = { | |
441 | DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"), | |
442 | DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0C11") | |
443 | }, | |
444 | .driver_data = (void *)(SOF_SDW_TGL_HDMI | | |
445 | RT711_JD2 | | |
446 | SOF_SDW_FOUR_SPK), | |
447 | }, | |
880bf4b4 GS |
448 | { |
449 | .callback = sof_sdw_quirk_cb, | |
450 | .matches = { | |
451 | DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"), | |
452 | DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0C40") | |
453 | }, | |
454 | .driver_data = (void *)(SOF_SDW_TGL_HDMI | | |
455 | RT711_JD2 | | |
456 | SOF_SDW_FOUR_SPK), | |
457 | }, | |
a9248c86 GS |
458 | { |
459 | .callback = sof_sdw_quirk_cb, | |
460 | .matches = { | |
461 | DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"), | |
462 | DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0C4F") | |
463 | }, | |
464 | .driver_data = (void *)(SOF_SDW_TGL_HDMI | | |
465 | RT711_JD2 | | |
466 | SOF_SDW_FOUR_SPK), | |
467 | }, | |
18489174 YZ |
468 | /* MeteorLake devices */ |
469 | { | |
470 | .callback = sof_sdw_quirk_cb, | |
471 | .matches = { | |
472 | DMI_MATCH(DMI_PRODUCT_FAMILY, "Intel_mtlrvp"), | |
473 | }, | |
0db94947 | 474 | .driver_data = (void *)(RT711_JD1), |
18489174 | 475 | }, |
289e1df0 BL |
476 | { |
477 | .callback = sof_sdw_quirk_cb, | |
478 | .matches = { | |
479 | DMI_MATCH(DMI_SYS_VENDOR, "Intel Corporation"), | |
480 | DMI_MATCH(DMI_PRODUCT_NAME, "Meteor Lake Client Platform"), | |
481 | }, | |
482 | .driver_data = (void *)(RT711_JD2_100K), | |
483 | }, | |
164e5dc1 UB |
484 | { |
485 | .callback = sof_sdw_quirk_cb, | |
486 | .matches = { | |
487 | DMI_MATCH(DMI_SYS_VENDOR, "Google"), | |
488 | DMI_MATCH(DMI_PRODUCT_NAME, "Rex"), | |
489 | }, | |
a14aded9 UB |
490 | .driver_data = (void *)(SOF_SDW_PCH_DMIC | |
491 | SOF_BT_OFFLOAD_SSP(1) | | |
492 | SOF_SSP_BT_OFFLOAD_PRESENT), | |
164e5dc1 | 493 | }, |
dfe25fea PU |
494 | /* LunarLake devices */ |
495 | { | |
496 | .callback = sof_sdw_quirk_cb, | |
497 | .matches = { | |
498 | DMI_MATCH(DMI_SYS_VENDOR, "Intel Corporation"), | |
499 | DMI_MATCH(DMI_PRODUCT_NAME, "Lunar Lake Client Platform"), | |
500 | }, | |
bd76caa2 | 501 | .driver_data = (void *)(RT711_JD2), |
dfe25fea | 502 | }, |
52db12d1 PLB |
503 | {} |
504 | }; | |
505 | ||
52db12d1 PLB |
506 | static struct snd_soc_dai_link_component dmic_component[] = { |
507 | { | |
508 | .name = "dmic-codec", | |
509 | .dai_name = "dmic-hifi", | |
510 | } | |
511 | }; | |
512 | ||
513 | static struct snd_soc_dai_link_component platform_component[] = { | |
514 | { | |
515 | /* name might be overridden during probe */ | |
516 | .name = "0000:00:1f.3" | |
517 | } | |
518 | }; | |
519 | ||
520 | /* these wrappers are only needed to avoid typecast compilation errors */ | |
be82e888 | 521 | int sdw_startup(struct snd_pcm_substream *substream) |
52db12d1 PLB |
522 | { |
523 | return sdw_startup_stream(substream); | |
524 | } | |
525 | ||
7cc3b56f | 526 | int sdw_prepare(struct snd_pcm_substream *substream) |
06998d49 PLB |
527 | { |
528 | struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); | |
529 | struct sdw_stream_runtime *sdw_stream; | |
530 | struct snd_soc_dai *dai; | |
531 | ||
532 | /* Find stream from first CPU DAI */ | |
533 | dai = asoc_rtd_to_cpu(rtd, 0); | |
534 | ||
e8444560 | 535 | sdw_stream = snd_soc_dai_get_stream(dai, substream->stream); |
06998d49 | 536 | if (IS_ERR(sdw_stream)) { |
c307ca16 | 537 | dev_err(rtd->dev, "no stream found for DAI %s\n", dai->name); |
06998d49 PLB |
538 | return PTR_ERR(sdw_stream); |
539 | } | |
540 | ||
541 | return sdw_prepare_stream(sdw_stream); | |
542 | } | |
543 | ||
7cc3b56f | 544 | int sdw_trigger(struct snd_pcm_substream *substream, int cmd) |
ae3a3918 PLB |
545 | { |
546 | struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); | |
547 | struct sdw_stream_runtime *sdw_stream; | |
548 | struct snd_soc_dai *dai; | |
549 | int ret; | |
550 | ||
551 | /* Find stream from first CPU DAI */ | |
552 | dai = asoc_rtd_to_cpu(rtd, 0); | |
553 | ||
e8444560 | 554 | sdw_stream = snd_soc_dai_get_stream(dai, substream->stream); |
ae3a3918 | 555 | if (IS_ERR(sdw_stream)) { |
c307ca16 | 556 | dev_err(rtd->dev, "no stream found for DAI %s\n", dai->name); |
ae3a3918 PLB |
557 | return PTR_ERR(sdw_stream); |
558 | } | |
559 | ||
560 | switch (cmd) { | |
561 | case SNDRV_PCM_TRIGGER_START: | |
562 | case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: | |
563 | case SNDRV_PCM_TRIGGER_RESUME: | |
564 | ret = sdw_enable_stream(sdw_stream); | |
565 | break; | |
566 | ||
567 | case SNDRV_PCM_TRIGGER_PAUSE_PUSH: | |
568 | case SNDRV_PCM_TRIGGER_SUSPEND: | |
569 | case SNDRV_PCM_TRIGGER_STOP: | |
570 | ret = sdw_disable_stream(sdw_stream); | |
571 | break; | |
572 | default: | |
573 | ret = -EINVAL; | |
574 | break; | |
575 | } | |
576 | ||
577 | if (ret) | |
c307ca16 | 578 | dev_err(rtd->dev, "%s trigger %d failed: %d\n", __func__, cmd, ret); |
ae3a3918 PLB |
579 | |
580 | return ret; | |
581 | } | |
582 | ||
0281b02e BL |
583 | int sdw_hw_params(struct snd_pcm_substream *substream, |
584 | struct snd_pcm_hw_params *params) | |
585 | { | |
586 | struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); | |
587 | int ch = params_channels(params); | |
588 | struct snd_soc_dai *codec_dai; | |
589 | struct snd_soc_dai *cpu_dai; | |
590 | unsigned int ch_mask; | |
591 | int num_codecs; | |
592 | int step; | |
593 | int i; | |
594 | int j; | |
595 | ||
596 | if (!rtd->dai_link->codec_ch_maps) | |
597 | return 0; | |
598 | ||
599 | /* Identical data will be sent to all codecs in playback */ | |
600 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { | |
601 | ch_mask = GENMASK(ch - 1, 0); | |
602 | step = 0; | |
603 | } else { | |
604 | num_codecs = rtd->dai_link->num_codecs; | |
605 | ||
606 | if (ch < num_codecs || ch % num_codecs != 0) { | |
607 | dev_err(rtd->dev, "Channels number %d is invalid when codec number = %d\n", | |
608 | ch, num_codecs); | |
609 | return -EINVAL; | |
610 | } | |
611 | ||
612 | ch_mask = GENMASK(ch / num_codecs - 1, 0); | |
613 | step = hweight_long(ch_mask); | |
614 | ||
615 | } | |
616 | ||
617 | /* | |
618 | * The captured data will be combined from each cpu DAI if the dai | |
619 | * link has more than one codec DAIs. Set codec channel mask and | |
620 | * ASoC will set the corresponding channel numbers for each cpu dai. | |
621 | */ | |
622 | for_each_rtd_cpu_dais(rtd, i, cpu_dai) { | |
623 | for_each_rtd_codec_dais(rtd, j, codec_dai) { | |
624 | if (rtd->dai_link->codec_ch_maps[j].connected_cpu_id != i) | |
625 | continue; | |
626 | rtd->dai_link->codec_ch_maps[j].ch_mask = ch_mask << (j * step); | |
627 | } | |
628 | } | |
629 | return 0; | |
630 | } | |
631 | ||
7cc3b56f | 632 | int sdw_hw_free(struct snd_pcm_substream *substream) |
06998d49 PLB |
633 | { |
634 | struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); | |
635 | struct sdw_stream_runtime *sdw_stream; | |
636 | struct snd_soc_dai *dai; | |
637 | ||
638 | /* Find stream from first CPU DAI */ | |
639 | dai = asoc_rtd_to_cpu(rtd, 0); | |
640 | ||
e8444560 | 641 | sdw_stream = snd_soc_dai_get_stream(dai, substream->stream); |
06998d49 | 642 | if (IS_ERR(sdw_stream)) { |
c307ca16 | 643 | dev_err(rtd->dev, "no stream found for DAI %s\n", dai->name); |
06998d49 PLB |
644 | return PTR_ERR(sdw_stream); |
645 | } | |
646 | ||
647 | return sdw_deprepare_stream(sdw_stream); | |
648 | } | |
649 | ||
be82e888 | 650 | void sdw_shutdown(struct snd_pcm_substream *substream) |
52db12d1 PLB |
651 | { |
652 | sdw_shutdown_stream(substream); | |
653 | } | |
654 | ||
655 | static const struct snd_soc_ops sdw_ops = { | |
656 | .startup = sdw_startup, | |
06998d49 | 657 | .prepare = sdw_prepare, |
ae3a3918 | 658 | .trigger = sdw_trigger, |
0281b02e | 659 | .hw_params = sdw_hw_params, |
06998d49 | 660 | .hw_free = sdw_hw_free, |
52db12d1 PLB |
661 | .shutdown = sdw_shutdown, |
662 | }; | |
663 | ||
664 | static struct sof_sdw_codec_info codec_info_list[] = { | |
665 | { | |
535df653 | 666 | .part_id = 0x700, |
07140abb BL |
667 | .dais = { |
668 | { | |
669 | .direction = {true, true}, | |
670 | .dai_name = "rt700-aif1", | |
671 | .dai_type = SOF_SDW_DAI_TYPE_JACK, | |
b2745865 | 672 | .dailink = {SDW_JACK_OUT_DAI_ID, SDW_JACK_IN_DAI_ID}, |
07140abb BL |
673 | .init = sof_sdw_rt700_init, |
674 | }, | |
675 | }, | |
676 | .dai_num = 1, | |
52db12d1 PLB |
677 | }, |
678 | { | |
535df653 | 679 | .part_id = 0x711, |
b75bea4b | 680 | .version_id = 3, |
07140abb BL |
681 | .dais = { |
682 | { | |
683 | .direction = {true, true}, | |
684 | .dai_name = "rt711-sdca-aif1", | |
685 | .dai_type = SOF_SDW_DAI_TYPE_JACK, | |
b2745865 | 686 | .dailink = {SDW_JACK_OUT_DAI_ID, SDW_JACK_IN_DAI_ID}, |
43f8012c BL |
687 | .init = sof_sdw_rt_sdca_jack_init, |
688 | .exit = sof_sdw_rt_sdca_jack_exit, | |
07140abb BL |
689 | }, |
690 | }, | |
691 | .dai_num = 1, | |
b75bea4b BL |
692 | }, |
693 | { | |
694 | .part_id = 0x711, | |
695 | .version_id = 2, | |
07140abb BL |
696 | .dais = { |
697 | { | |
698 | .direction = {true, true}, | |
699 | .dai_name = "rt711-aif1", | |
700 | .dai_type = SOF_SDW_DAI_TYPE_JACK, | |
b2745865 | 701 | .dailink = {SDW_JACK_OUT_DAI_ID, SDW_JACK_IN_DAI_ID}, |
07140abb BL |
702 | .init = sof_sdw_rt711_init, |
703 | .exit = sof_sdw_rt711_exit, | |
704 | }, | |
705 | }, | |
706 | .dai_num = 1, | |
52db12d1 | 707 | }, |
5360c670 BL |
708 | { |
709 | .part_id = 0x712, | |
710 | .version_id = 3, | |
711 | .dais = { | |
712 | { | |
713 | .direction = {true, true}, | |
714 | .dai_name = "rt712-sdca-aif1", | |
715 | .dai_type = SOF_SDW_DAI_TYPE_JACK, | |
716 | .dailink = {SDW_JACK_OUT_DAI_ID, SDW_JACK_IN_DAI_ID}, | |
717 | .init = sof_sdw_rt_sdca_jack_init, | |
718 | .exit = sof_sdw_rt_sdca_jack_exit, | |
719 | }, | |
720 | { | |
721 | .direction = {true, false}, | |
722 | .dai_name = "rt712-sdca-aif2", | |
723 | .dai_type = SOF_SDW_DAI_TYPE_AMP, | |
724 | .dailink = {SDW_AMP_OUT_DAI_ID, SDW_UNUSED_DAI_ID}, | |
725 | .init = sof_sdw_rt712_spk_init, | |
726 | }, | |
727 | }, | |
728 | .dai_num = 2, | |
729 | }, | |
730 | { | |
731 | .part_id = 0x1712, | |
732 | .version_id = 3, | |
733 | .dais = { | |
734 | { | |
735 | .direction = {false, true}, | |
736 | .dai_name = "rt712-sdca-dmic-aif1", | |
737 | .dai_type = SOF_SDW_DAI_TYPE_MIC, | |
fbaaf80d BL |
738 | .dailink = {SDW_UNUSED_DAI_ID, SDW_DMIC_DAI_ID}, |
739 | .init = sof_sdw_rt712_sdca_dmic_init, | |
740 | }, | |
741 | }, | |
742 | .dai_num = 1, | |
743 | }, | |
744 | { | |
745 | .part_id = 0x713, | |
746 | .version_id = 3, | |
747 | .dais = { | |
748 | { | |
749 | .direction = {true, true}, | |
750 | .dai_name = "rt712-sdca-aif1", | |
751 | .dai_type = SOF_SDW_DAI_TYPE_JACK, | |
752 | .dailink = {SDW_JACK_OUT_DAI_ID, SDW_JACK_IN_DAI_ID}, | |
753 | .init = sof_sdw_rt_sdca_jack_init, | |
754 | .exit = sof_sdw_rt_sdca_jack_exit, | |
755 | }, | |
756 | }, | |
757 | .dai_num = 1, | |
758 | }, | |
759 | { | |
760 | .part_id = 0x1713, | |
761 | .version_id = 3, | |
762 | .dais = { | |
763 | { | |
764 | .direction = {false, true}, | |
765 | .dai_name = "rt712-sdca-dmic-aif1", | |
766 | .dai_type = SOF_SDW_DAI_TYPE_MIC, | |
5360c670 BL |
767 | .dailink = {SDW_UNUSED_DAI_ID, SDW_DMIC_DAI_ID}, |
768 | .init = sof_sdw_rt712_sdca_dmic_init, | |
769 | }, | |
770 | }, | |
771 | .dai_num = 1, | |
772 | }, | |
52db12d1 | 773 | { |
535df653 | 774 | .part_id = 0x1308, |
52db12d1 | 775 | .acpi_id = "10EC1308", |
07140abb BL |
776 | .dais = { |
777 | { | |
778 | .direction = {true, false}, | |
779 | .dai_name = "rt1308-aif", | |
780 | .dai_type = SOF_SDW_DAI_TYPE_AMP, | |
b2745865 | 781 | .dailink = {SDW_AMP_OUT_DAI_ID, SDW_UNUSED_DAI_ID}, |
07140abb BL |
782 | .init = sof_sdw_rt_amp_init, |
783 | .exit = sof_sdw_rt_amp_exit, | |
784 | }, | |
785 | }, | |
786 | .dai_num = 1, | |
52db12d1 | 787 | .ops = &sof_sdw_rt1308_i2s_ops, |
52db12d1 | 788 | }, |
b75bea4b BL |
789 | { |
790 | .part_id = 0x1316, | |
07140abb BL |
791 | .dais = { |
792 | { | |
793 | .direction = {true, true}, | |
794 | .dai_name = "rt1316-aif", | |
795 | .dai_type = SOF_SDW_DAI_TYPE_AMP, | |
b2745865 | 796 | .dailink = {SDW_AMP_OUT_DAI_ID, SDW_AMP_IN_DAI_ID}, |
07140abb BL |
797 | .init = sof_sdw_rt_amp_init, |
798 | .exit = sof_sdw_rt_amp_exit, | |
799 | }, | |
800 | }, | |
801 | .dai_num = 1, | |
b75bea4b | 802 | }, |
8c4b3a8e GS |
803 | { |
804 | .part_id = 0x1318, | |
07140abb BL |
805 | .dais = { |
806 | { | |
807 | .direction = {true, true}, | |
808 | .dai_name = "rt1318-aif", | |
809 | .dai_type = SOF_SDW_DAI_TYPE_AMP, | |
b2745865 | 810 | .dailink = {SDW_AMP_OUT_DAI_ID, SDW_AMP_IN_DAI_ID}, |
07140abb BL |
811 | .init = sof_sdw_rt_amp_init, |
812 | .exit = sof_sdw_rt_amp_exit, | |
813 | }, | |
814 | }, | |
815 | .dai_num = 1, | |
8c4b3a8e | 816 | }, |
b75bea4b BL |
817 | { |
818 | .part_id = 0x714, | |
df64b988 | 819 | .version_id = 3, |
35564e2b | 820 | .ignore_pch_dmic = true, |
07140abb BL |
821 | .dais = { |
822 | { | |
823 | .direction = {false, true}, | |
824 | .dai_name = "rt715-aif2", | |
825 | .dai_type = SOF_SDW_DAI_TYPE_MIC, | |
b2745865 | 826 | .dailink = {SDW_UNUSED_DAI_ID, SDW_DMIC_DAI_ID}, |
07140abb BL |
827 | .init = sof_sdw_rt715_sdca_init, |
828 | }, | |
829 | }, | |
830 | .dai_num = 1, | |
b75bea4b | 831 | }, |
52db12d1 | 832 | { |
535df653 | 833 | .part_id = 0x715, |
df64b988 | 834 | .version_id = 3, |
35564e2b | 835 | .ignore_pch_dmic = true, |
07140abb BL |
836 | .dais = { |
837 | { | |
838 | .direction = {false, true}, | |
839 | .dai_name = "rt715-aif2", | |
840 | .dai_type = SOF_SDW_DAI_TYPE_MIC, | |
b2745865 | 841 | .dailink = {SDW_UNUSED_DAI_ID, SDW_DMIC_DAI_ID}, |
07140abb BL |
842 | .init = sof_sdw_rt715_sdca_init, |
843 | }, | |
844 | }, | |
845 | .dai_num = 1, | |
df64b988 PLB |
846 | }, |
847 | { | |
848 | .part_id = 0x714, | |
849 | .version_id = 2, | |
35564e2b | 850 | .ignore_pch_dmic = true, |
07140abb BL |
851 | .dais = { |
852 | { | |
853 | .direction = {false, true}, | |
854 | .dai_name = "rt715-aif2", | |
855 | .dai_type = SOF_SDW_DAI_TYPE_MIC, | |
b2745865 | 856 | .dailink = {SDW_UNUSED_DAI_ID, SDW_DMIC_DAI_ID}, |
07140abb BL |
857 | .init = sof_sdw_rt715_init, |
858 | }, | |
859 | }, | |
860 | .dai_num = 1, | |
df64b988 PLB |
861 | }, |
862 | { | |
863 | .part_id = 0x715, | |
864 | .version_id = 2, | |
35564e2b | 865 | .ignore_pch_dmic = true, |
07140abb BL |
866 | .dais = { |
867 | { | |
868 | .direction = {false, true}, | |
869 | .dai_name = "rt715-aif2", | |
870 | .dai_type = SOF_SDW_DAI_TYPE_MIC, | |
b2745865 | 871 | .dailink = {SDW_UNUSED_DAI_ID, SDW_DMIC_DAI_ID}, |
07140abb BL |
872 | .init = sof_sdw_rt715_init, |
873 | }, | |
874 | }, | |
875 | .dai_num = 1, | |
52db12d1 | 876 | }, |
be82e888 | 877 | { |
535df653 | 878 | .part_id = 0x8373, |
07140abb BL |
879 | .dais = { |
880 | { | |
881 | .direction = {true, true}, | |
882 | .dai_name = "max98373-aif1", | |
883 | .dai_type = SOF_SDW_DAI_TYPE_AMP, | |
b2745865 | 884 | .dailink = {SDW_AMP_OUT_DAI_ID, SDW_AMP_IN_DAI_ID}, |
fcb3f0fb | 885 | .init = sof_sdw_maxim_init, |
07140abb BL |
886 | }, |
887 | }, | |
888 | .dai_num = 1, | |
be82e888 | 889 | }, |
dea4138d UB |
890 | { |
891 | .part_id = 0x8363, | |
892 | .dais = { | |
893 | { | |
894 | .direction = {true, false}, | |
895 | .dai_name = "max98363-aif1", | |
896 | .dai_type = SOF_SDW_DAI_TYPE_AMP, | |
897 | .dailink = {SDW_AMP_OUT_DAI_ID, SDW_UNUSED_DAI_ID}, | |
898 | .init = sof_sdw_maxim_init, | |
899 | }, | |
900 | }, | |
901 | .dai_num = 1, | |
902 | }, | |
798313f2 | 903 | { |
535df653 | 904 | .part_id = 0x5682, |
07140abb BL |
905 | .dais = { |
906 | { | |
907 | .direction = {true, true}, | |
908 | .dai_name = "rt5682-sdw", | |
909 | .dai_type = SOF_SDW_DAI_TYPE_JACK, | |
b2745865 | 910 | .dailink = {SDW_JACK_OUT_DAI_ID, SDW_JACK_IN_DAI_ID}, |
07140abb BL |
911 | .init = sof_sdw_rt5682_init, |
912 | }, | |
913 | }, | |
914 | .dai_num = 1, | |
798313f2 | 915 | }, |
4754e29c CS |
916 | { |
917 | .part_id = 0x3556, | |
918 | .dais = { | |
919 | { | |
920 | .direction = {true, true}, | |
921 | .dai_name = "cs35l56-sdw1", | |
922 | .dai_type = SOF_SDW_DAI_TYPE_AMP, | |
923 | .dailink = {SDW_AMP_OUT_DAI_ID, SDW_AMP_IN_DAI_ID}, | |
924 | .init = sof_sdw_cs_amp_init, | |
925 | }, | |
926 | }, | |
927 | .dai_num = 1, | |
928 | }, | |
43cdea08 UB |
929 | { |
930 | .part_id = 0x4242, | |
931 | .dais = { | |
932 | { | |
933 | .direction = {true, true}, | |
934 | .dai_name = "cs42l42-sdw", | |
935 | .dai_type = SOF_SDW_DAI_TYPE_JACK, | |
936 | .dailink = {SDW_JACK_OUT_DAI_ID, SDW_JACK_IN_DAI_ID}, | |
937 | .init = sof_sdw_cs42l42_init, | |
938 | }, | |
939 | }, | |
940 | .dai_num = 1, | |
941 | }, | |
0ccac3bc PLB |
942 | { |
943 | .part_id = 0xaaaa, /* generic codec mockup */ | |
944 | .version_id = 0, | |
07140abb BL |
945 | .dais = { |
946 | { | |
947 | .direction = {true, true}, | |
948 | .dai_name = "sdw-mockup-aif1", | |
949 | .dai_type = SOF_SDW_DAI_TYPE_JACK, | |
b2745865 | 950 | .dailink = {SDW_JACK_OUT_DAI_ID, SDW_JACK_IN_DAI_ID}, |
07140abb BL |
951 | .init = NULL, |
952 | }, | |
953 | }, | |
954 | .dai_num = 1, | |
0ccac3bc PLB |
955 | }, |
956 | { | |
957 | .part_id = 0xaa55, /* headset codec mockup */ | |
958 | .version_id = 0, | |
07140abb BL |
959 | .dais = { |
960 | { | |
961 | .direction = {true, true}, | |
962 | .dai_name = "sdw-mockup-aif1", | |
963 | .dai_type = SOF_SDW_DAI_TYPE_JACK, | |
b2745865 | 964 | .dailink = {SDW_JACK_OUT_DAI_ID, SDW_JACK_IN_DAI_ID}, |
07140abb BL |
965 | .init = NULL, |
966 | }, | |
967 | }, | |
968 | .dai_num = 1, | |
0ccac3bc PLB |
969 | }, |
970 | { | |
971 | .part_id = 0x55aa, /* amplifier mockup */ | |
972 | .version_id = 0, | |
07140abb BL |
973 | .dais = { |
974 | { | |
3390d4ed | 975 | .direction = {true, true}, |
07140abb BL |
976 | .dai_name = "sdw-mockup-aif1", |
977 | .dai_type = SOF_SDW_DAI_TYPE_AMP, | |
3390d4ed | 978 | .dailink = {SDW_AMP_OUT_DAI_ID, SDW_AMP_IN_DAI_ID}, |
07140abb BL |
979 | .init = NULL, |
980 | }, | |
981 | }, | |
982 | .dai_num = 1, | |
0ccac3bc PLB |
983 | }, |
984 | { | |
985 | .part_id = 0x5555, | |
986 | .version_id = 0, | |
07140abb BL |
987 | .dais = { |
988 | { | |
989 | .dai_name = "sdw-mockup-aif1", | |
990 | .direction = {false, true}, | |
991 | .dai_type = SOF_SDW_DAI_TYPE_MIC, | |
b2745865 | 992 | .dailink = {SDW_UNUSED_DAI_ID, SDW_DMIC_DAI_ID}, |
07140abb BL |
993 | .init = NULL, |
994 | }, | |
995 | }, | |
996 | .dai_num = 1, | |
0ccac3bc | 997 | }, |
52db12d1 PLB |
998 | }; |
999 | ||
fad1a9ef | 1000 | static inline int find_codec_info_part(const u64 adr) |
52db12d1 | 1001 | { |
2e2d287b | 1002 | unsigned int part_id, sdw_version; |
52db12d1 PLB |
1003 | int i; |
1004 | ||
2e2d287b BL |
1005 | part_id = SDW_PART_ID(adr); |
1006 | sdw_version = SDW_VERSION(adr); | |
52db12d1 | 1007 | for (i = 0; i < ARRAY_SIZE(codec_info_list); i++) |
2e2d287b BL |
1008 | /* |
1009 | * A codec info is for all sdw version with the part id if | |
1010 | * version_id is not specified in the codec info. | |
1011 | */ | |
535df653 | 1012 | if (part_id == codec_info_list[i].part_id && |
2e2d287b BL |
1013 | (!codec_info_list[i].version_id || |
1014 | sdw_version == codec_info_list[i].version_id)) | |
1015 | return i; | |
52db12d1 | 1016 | |
2e2d287b | 1017 | return -EINVAL; |
52db12d1 | 1018 | |
52db12d1 PLB |
1019 | } |
1020 | ||
1021 | static inline int find_codec_info_acpi(const u8 *acpi_id) | |
1022 | { | |
1023 | int i; | |
1024 | ||
1025 | if (!acpi_id[0]) | |
1026 | return -EINVAL; | |
1027 | ||
1028 | for (i = 0; i < ARRAY_SIZE(codec_info_list); i++) | |
31a54f78 CK |
1029 | if (!memcmp(codec_info_list[i].acpi_id, acpi_id, ACPI_ID_LEN)) |
1030 | return i; | |
52db12d1 | 1031 | |
31a54f78 | 1032 | return -EINVAL; |
52db12d1 PLB |
1033 | } |
1034 | ||
1035 | /* | |
1036 | * get BE dailink number and CPU DAI number based on sdw link adr. | |
1037 | * Since some sdw slaves may be aggregated, the CPU DAI number | |
1038 | * may be larger than the number of BE dailinks. | |
1039 | */ | |
a386162e CK |
1040 | static int get_dailink_info(struct device *dev, |
1041 | const struct snd_soc_acpi_link_adr *adr_link, | |
1042 | int *sdw_be_num, int *sdw_cpu_dai_num, int *codecs_num) | |
52db12d1 | 1043 | { |
52db12d1 PLB |
1044 | bool group_visited[SDW_MAX_GROUPS]; |
1045 | bool no_aggregation; | |
1046 | int i; | |
d3fc5c4d | 1047 | int j; |
52db12d1 PLB |
1048 | |
1049 | no_aggregation = sof_sdw_quirk & SOF_SDW_NO_AGGREGATION; | |
1050 | *sdw_cpu_dai_num = 0; | |
1051 | *sdw_be_num = 0; | |
1052 | ||
1d106238 | 1053 | if (!adr_link) |
52db12d1 PLB |
1054 | return -EINVAL; |
1055 | ||
1056 | for (i = 0; i < SDW_MAX_GROUPS; i++) | |
1057 | group_visited[i] = false; | |
1058 | ||
1d106238 | 1059 | for (; adr_link->num_adr; adr_link++) { |
52db12d1 | 1060 | const struct snd_soc_acpi_endpoint *endpoint; |
cededa5a | 1061 | struct sof_sdw_codec_info *codec_info; |
2e2d287b | 1062 | int codec_index; |
52db12d1 PLB |
1063 | int stream; |
1064 | u64 adr; | |
1065 | ||
e1cfd5fe CK |
1066 | /* make sure the link mask has a single bit set */ |
1067 | if (!is_power_of_2(adr_link->mask)) | |
1068 | return -EINVAL; | |
1069 | ||
1d106238 CK |
1070 | for (i = 0; i < adr_link->num_adr; i++) { |
1071 | adr = adr_link->adr_d[i].adr; | |
c8db7b50 BL |
1072 | codec_index = find_codec_info_part(adr); |
1073 | if (codec_index < 0) | |
1074 | return codec_index; | |
a386162e | 1075 | |
cededa5a | 1076 | codec_info = &codec_info_list[codec_index]; |
52db12d1 | 1077 | |
a386162e CK |
1078 | *codecs_num += codec_info->dai_num; |
1079 | ||
1080 | if (!adr_link->adr_d[i].name_prefix) { | |
1081 | dev_err(dev, "codec 0x%llx does not have a name prefix\n", | |
1082 | adr_link->adr_d[i].adr); | |
1083 | return -EINVAL; | |
1084 | } | |
1085 | ||
1d106238 | 1086 | endpoint = adr_link->adr_d[i].endpoints; |
87608d3e CK |
1087 | if (endpoint->aggregated && !endpoint->group_id) { |
1088 | dev_err(dev, "invalid group id on link %x\n", | |
1089 | adr_link->mask); | |
1090 | return -EINVAL; | |
1091 | } | |
52db12d1 | 1092 | |
d3fc5c4d BL |
1093 | for (j = 0; j < codec_info->dai_num; j++) { |
1094 | /* count DAI number for playback and capture */ | |
1095 | for_each_pcm_streams(stream) { | |
1096 | if (!codec_info->dais[j].direction[stream]) | |
1097 | continue; | |
52db12d1 | 1098 | |
d3fc5c4d | 1099 | (*sdw_cpu_dai_num)++; |
52db12d1 | 1100 | |
d3fc5c4d BL |
1101 | /* count BE for each non-aggregated slave or group */ |
1102 | if (!endpoint->aggregated || no_aggregation || | |
1103 | !group_visited[endpoint->group_id]) | |
1104 | (*sdw_be_num)++; | |
1105 | } | |
c8db7b50 | 1106 | } |
52db12d1 | 1107 | |
c8db7b50 BL |
1108 | if (endpoint->aggregated) |
1109 | group_visited[endpoint->group_id] = true; | |
1110 | } | |
52db12d1 PLB |
1111 | } |
1112 | ||
1113 | return 0; | |
1114 | } | |
1115 | ||
3827b7ca BL |
1116 | static void init_dai_link(struct device *dev, struct snd_soc_dai_link *dai_links, |
1117 | int be_id, char *name, int playback, int capture, | |
1118 | struct snd_soc_dai_link_component *cpus, int cpus_num, | |
1119 | struct snd_soc_dai_link_component *codecs, int codecs_num, | |
52db12d1 PLB |
1120 | int (*init)(struct snd_soc_pcm_runtime *rtd), |
1121 | const struct snd_soc_ops *ops) | |
1122 | { | |
3827b7ca | 1123 | dev_dbg(dev, "create dai link %s, id %d\n", name, be_id); |
52db12d1 PLB |
1124 | dai_links->id = be_id; |
1125 | dai_links->name = name; | |
1126 | dai_links->platforms = platform_component; | |
1127 | dai_links->num_platforms = ARRAY_SIZE(platform_component); | |
52db12d1 PLB |
1128 | dai_links->no_pcm = 1; |
1129 | dai_links->cpus = cpus; | |
1130 | dai_links->num_cpus = cpus_num; | |
1131 | dai_links->codecs = codecs; | |
1132 | dai_links->num_codecs = codecs_num; | |
1133 | dai_links->dpcm_playback = playback; | |
1134 | dai_links->dpcm_capture = capture; | |
1135 | dai_links->init = init; | |
1136 | dai_links->ops = ops; | |
1137 | } | |
1138 | ||
1d106238 | 1139 | static bool is_unique_device(const struct snd_soc_acpi_link_adr *adr_link, |
52db12d1 PLB |
1140 | unsigned int sdw_version, |
1141 | unsigned int mfg_id, | |
1142 | unsigned int part_id, | |
1143 | unsigned int class_id, | |
08f62f62 | 1144 | int index_in_link) |
52db12d1 PLB |
1145 | { |
1146 | int i; | |
1147 | ||
1d106238 | 1148 | for (i = 0; i < adr_link->num_adr; i++) { |
52db12d1 PLB |
1149 | unsigned int sdw1_version, mfg1_id, part1_id, class1_id; |
1150 | u64 adr; | |
1151 | ||
1152 | /* skip itself */ | |
1153 | if (i == index_in_link) | |
1154 | continue; | |
1155 | ||
1d106238 | 1156 | adr = adr_link->adr_d[i].adr; |
52db12d1 PLB |
1157 | |
1158 | sdw1_version = SDW_VERSION(adr); | |
1159 | mfg1_id = SDW_MFG_ID(adr); | |
1160 | part1_id = SDW_PART_ID(adr); | |
1161 | class1_id = SDW_CLASS_ID(adr); | |
1162 | ||
1163 | if (sdw_version == sdw1_version && | |
1164 | mfg_id == mfg1_id && | |
1165 | part_id == part1_id && | |
1166 | class_id == class1_id) | |
1167 | return false; | |
1168 | } | |
1169 | ||
1170 | return true; | |
1171 | } | |
1172 | ||
92e9f10a CK |
1173 | static int fill_sdw_codec_dlc(struct device *dev, |
1174 | const struct snd_soc_acpi_link_adr *adr_link, | |
1175 | struct snd_soc_dai_link_component *codec, | |
317dcdec | 1176 | int adr_index, int dai_index) |
92e9f10a CK |
1177 | { |
1178 | unsigned int sdw_version, unique_id, mfg_id, link_id, part_id, class_id; | |
1179 | u64 adr = adr_link->adr_d[adr_index].adr; | |
317dcdec CK |
1180 | int codec_index; |
1181 | ||
1182 | codec_index = find_codec_info_part(adr); | |
1183 | if (codec_index < 0) | |
1184 | return codec_index; | |
92e9f10a CK |
1185 | |
1186 | sdw_version = SDW_VERSION(adr); | |
1187 | link_id = SDW_DISCO_LINK_ID(adr); | |
1188 | unique_id = SDW_UNIQUE_ID(adr); | |
1189 | mfg_id = SDW_MFG_ID(adr); | |
1190 | part_id = SDW_PART_ID(adr); | |
1191 | class_id = SDW_CLASS_ID(adr); | |
1192 | ||
1193 | if (codec_info_list[codec_index].codec_name) | |
1194 | codec->name = devm_kstrdup(dev, | |
1195 | codec_info_list[codec_index].codec_name, | |
1196 | GFP_KERNEL); | |
1197 | else if (is_unique_device(adr_link, sdw_version, mfg_id, part_id, | |
1198 | class_id, adr_index)) | |
1199 | codec->name = devm_kasprintf(dev, GFP_KERNEL, | |
1200 | "sdw:%01x:%04x:%04x:%02x", link_id, | |
1201 | mfg_id, part_id, class_id); | |
1202 | else | |
1203 | codec->name = devm_kasprintf(dev, GFP_KERNEL, | |
1204 | "sdw:%01x:%04x:%04x:%02x:%01x", link_id, | |
1205 | mfg_id, part_id, class_id, unique_id); | |
1206 | ||
1207 | if (!codec->name) | |
1208 | return -ENOMEM; | |
1209 | ||
1210 | codec->dai_name = codec_info_list[codec_index].dais[dai_index].dai_name; | |
1211 | ||
1212 | return 0; | |
1213 | } | |
1214 | ||
cdf99c9a | 1215 | static int set_codec_init_func(struct snd_soc_card *card, |
1d106238 | 1216 | const struct snd_soc_acpi_link_adr *adr_link, |
52db12d1 | 1217 | struct snd_soc_dai_link *dai_links, |
d3fc5c4d | 1218 | bool playback, int group_id, int adr_index, int dai_index) |
52db12d1 | 1219 | { |
f0c8d83a | 1220 | int i = adr_index; |
52db12d1 | 1221 | |
5930d02c BL |
1222 | do { |
1223 | /* | |
1224 | * Initialize the codec. If codec is part of an aggregated | |
1225 | * group (group_id>0), initialize all codecs belonging to | |
1226 | * same group. | |
1d106238 | 1227 | * The first link should start with adr_link->adr_d[adr_index] |
f0c8d83a BL |
1228 | * because that is the device that we want to initialize and |
1229 | * we should end immediately if it is not aggregated (group_id=0) | |
5930d02c | 1230 | */ |
1d106238 | 1231 | for ( ; i < adr_link->num_adr; i++) { |
5930d02c | 1232 | int codec_index; |
52db12d1 | 1233 | |
1d106238 | 1234 | codec_index = find_codec_info_part(adr_link->adr_d[i].adr); |
5930d02c BL |
1235 | if (codec_index < 0) |
1236 | return codec_index; | |
d3fc5c4d | 1237 | |
5930d02c | 1238 | /* The group_id is > 0 iff the codec is aggregated */ |
1d106238 | 1239 | if (adr_link->adr_d[i].endpoints->group_id != group_id) |
5930d02c | 1240 | continue; |
d3fc5c4d BL |
1241 | |
1242 | if (codec_info_list[codec_index].dais[dai_index].init) | |
1243 | codec_info_list[codec_index].dais[dai_index].init(card, | |
1d106238 | 1244 | adr_link, |
5930d02c BL |
1245 | dai_links, |
1246 | &codec_info_list[codec_index], | |
1247 | playback); | |
f0c8d83a BL |
1248 | if (!group_id) |
1249 | return 0; | |
5930d02c | 1250 | } |
1d106238 | 1251 | |
f0c8d83a | 1252 | i = 0; |
1d106238 CK |
1253 | adr_link++; |
1254 | } while (adr_link->mask); | |
52db12d1 PLB |
1255 | |
1256 | return 0; | |
1257 | } | |
1258 | ||
1259 | /* | |
1260 | * check endpoint status in slaves and gather link ID for all slaves in | |
1261 | * the same group to generate different CPU DAI. Now only support | |
1262 | * one sdw link with all slaves set with only single group id. | |
1263 | * | |
1264 | * one slave on one sdw link with aggregated = 0 | |
1265 | * one sdw BE DAI <---> one-cpu DAI <---> one-codec DAI | |
1266 | * | |
1267 | * two or more slaves on one sdw link with aggregated = 0 | |
1268 | * one sdw BE DAI <---> one-cpu DAI <---> multi-codec DAIs | |
1269 | * | |
1270 | * multiple links with multiple slaves with aggregated = 1 | |
1271 | * one sdw BE DAI <---> 1 .. N CPU DAIs <----> 1 .. N codec DAIs | |
1272 | */ | |
1273 | static int get_slave_info(const struct snd_soc_acpi_link_adr *adr_link, | |
1274 | struct device *dev, int *cpu_dai_id, int *cpu_dai_num, | |
0a1f3958 | 1275 | int *codec_num, unsigned int *group_id, |
a60ed3b7 | 1276 | int adr_index) |
52db12d1 | 1277 | { |
7f5cf197 | 1278 | bool no_aggregation = sof_sdw_quirk & SOF_SDW_NO_AGGREGATION; |
c8db7b50 | 1279 | int i; |
52db12d1 | 1280 | |
7f5cf197 CK |
1281 | if (!adr_link->adr_d[adr_index].endpoints->aggregated || no_aggregation) { |
1282 | cpu_dai_id[0] = ffs(adr_link->mask) - 1; | |
52db12d1 | 1283 | *cpu_dai_num = 1; |
16373f30 | 1284 | *codec_num = 1; |
52db12d1 PLB |
1285 | *group_id = 0; |
1286 | return 0; | |
1287 | } | |
1288 | ||
c8db7b50 | 1289 | *codec_num = 0; |
7f5cf197 CK |
1290 | *cpu_dai_num = 0; |
1291 | *group_id = adr_link->adr_d[adr_index].endpoints->group_id; | |
c8db7b50 | 1292 | |
7f5cf197 CK |
1293 | /* Count endpoints with the same group_id in the adr_link */ |
1294 | for (; adr_link && adr_link->num_adr; adr_link++) { | |
f82742dd | 1295 | unsigned int link_codecs = 0; |
52db12d1 | 1296 | |
7f5cf197 CK |
1297 | for (i = 0; i < adr_link->num_adr; i++) { |
1298 | if (adr_link->adr_d[i].endpoints->aggregated && | |
1299 | adr_link->adr_d[i].endpoints->group_id == *group_id) | |
f82742dd CK |
1300 | link_codecs++; |
1301 | } | |
1302 | ||
1303 | if (link_codecs) { | |
1304 | *codec_num += link_codecs; | |
1305 | ||
7f5cf197 | 1306 | if (*cpu_dai_num >= SDW_MAX_CPU_DAIS) { |
f82742dd CK |
1307 | dev_err(dev, "cpu_dai_id array overflowed\n"); |
1308 | return -EINVAL; | |
1309 | } | |
1310 | ||
7f5cf197 | 1311 | cpu_dai_id[(*cpu_dai_num)++] = ffs(adr_link->mask) - 1; |
c8db7b50 | 1312 | } |
52db12d1 PLB |
1313 | } |
1314 | ||
52db12d1 PLB |
1315 | return 0; |
1316 | } | |
1317 | ||
0281b02e BL |
1318 | static void set_dailink_map(struct snd_soc_dai_link_codec_ch_map *sdw_codec_ch_maps, |
1319 | int codec_num, int cpu_num) | |
1320 | { | |
1321 | int step; | |
1322 | int i; | |
1323 | ||
1324 | step = codec_num / cpu_num; | |
1325 | for (i = 0; i < codec_num; i++) | |
1326 | sdw_codec_ch_maps[i].connected_cpu_id = i / step; | |
1327 | } | |
1328 | ||
dc5a3e60 BL |
1329 | static const char * const type_strings[] = {"SimpleJack", "SmartAmp", "SmartMic"}; |
1330 | ||
febac07b | 1331 | static int create_sdw_dailink(struct snd_soc_card *card, int *link_index, |
52db12d1 PLB |
1332 | struct snd_soc_dai_link *dai_links, |
1333 | int sdw_be_num, int sdw_cpu_dai_num, | |
1334 | struct snd_soc_dai_link_component *cpus, | |
1d106238 | 1335 | const struct snd_soc_acpi_link_adr *adr_link, |
a60ed3b7 | 1336 | int *cpu_id, struct snd_soc_codec_conf *codec_conf, |
616bee2c | 1337 | int codec_count, int *be_id, |
35564e2b | 1338 | int *codec_conf_index, |
c8db7b50 | 1339 | bool *ignore_pch_dmic, |
5714aabd | 1340 | bool append_dai_type, |
d3fc5c4d BL |
1341 | int adr_index, |
1342 | int dai_index) | |
52db12d1 | 1343 | { |
febac07b | 1344 | struct device *dev = card->dev; |
1d106238 | 1345 | const struct snd_soc_acpi_link_adr *adr_link_next; |
52db12d1 | 1346 | struct snd_soc_dai_link_component *codecs; |
cededa5a | 1347 | struct sof_sdw_codec_info *codec_info; |
52db12d1 PLB |
1348 | int cpu_dai_id[SDW_MAX_CPU_DAIS]; |
1349 | int cpu_dai_num, cpu_dai_index; | |
2e2d287b | 1350 | unsigned int group_id; |
4fc16d21 | 1351 | int codec_dlc_index = 0; |
52db12d1 PLB |
1352 | int codec_index; |
1353 | int codec_num; | |
1354 | int stream; | |
35d28ccd | 1355 | int i = 0; |
c3d7e29a | 1356 | int j, k; |
52db12d1 | 1357 | int ret; |
52db12d1 | 1358 | |
1d106238 | 1359 | ret = get_slave_info(adr_link, dev, cpu_dai_id, &cpu_dai_num, &codec_num, |
a60ed3b7 | 1360 | &group_id, adr_index); |
52db12d1 PLB |
1361 | if (ret) |
1362 | return ret; | |
1363 | ||
1364 | codecs = devm_kcalloc(dev, codec_num, sizeof(*codecs), GFP_KERNEL); | |
1365 | if (!codecs) | |
1366 | return -ENOMEM; | |
1367 | ||
1368 | /* generate codec name on different links in the same group */ | |
f3eb3d45 | 1369 | j = adr_index; |
1d106238 CK |
1370 | for (adr_link_next = adr_link; adr_link_next && adr_link_next->num_adr && |
1371 | i < cpu_dai_num; adr_link_next++) { | |
52db12d1 | 1372 | /* skip the link excluded by this processed group */ |
1d106238 | 1373 | if (cpu_dai_id[i] != ffs(adr_link_next->mask) - 1) |
52db12d1 PLB |
1374 | continue; |
1375 | ||
f3eb3d45 CK |
1376 | /* j reset after loop, adr_index only applies to first link */ |
1377 | for (; j < adr_link_next->num_adr; j++) { | |
f82742dd | 1378 | const struct snd_soc_acpi_endpoint *endpoints; |
c3d7e29a | 1379 | |
f82742dd CK |
1380 | endpoints = adr_link_next->adr_d[j].endpoints; |
1381 | ||
1382 | if (group_id && (!endpoints->aggregated || | |
1383 | endpoints->group_id != group_id)) | |
1384 | continue; | |
1385 | ||
59736ca6 CK |
1386 | /* sanity check */ |
1387 | if (*codec_conf_index >= codec_count) { | |
1388 | dev_err(dev, "codec_conf array overflowed\n"); | |
1389 | return -EINVAL; | |
1390 | } | |
1391 | ||
c3d7e29a | 1392 | ret = fill_sdw_codec_dlc(dev, adr_link_next, |
0e82229f | 1393 | &codecs[codec_dlc_index], |
317dcdec | 1394 | j, dai_index); |
c3d7e29a CK |
1395 | if (ret) |
1396 | return ret; | |
1397 | ||
0e82229f | 1398 | codec_conf[*codec_conf_index].dlc = codecs[codec_dlc_index]; |
c3d7e29a CK |
1399 | codec_conf[*codec_conf_index].name_prefix = |
1400 | adr_link_next->adr_d[j].name_prefix; | |
1401 | ||
0e82229f | 1402 | codec_dlc_index++; |
c3d7e29a CK |
1403 | (*codec_conf_index)++; |
1404 | } | |
f3eb3d45 | 1405 | j = 0; |
52db12d1 PLB |
1406 | |
1407 | /* check next link to create codec dai in the processed group */ | |
1408 | i++; | |
52db12d1 PLB |
1409 | } |
1410 | ||
1411 | /* find codec info to create BE DAI */ | |
1d106238 | 1412 | codec_index = find_codec_info_part(adr_link->adr_d[adr_index].adr); |
52db12d1 PLB |
1413 | if (codec_index < 0) |
1414 | return codec_index; | |
cededa5a | 1415 | codec_info = &codec_info_list[codec_index]; |
52db12d1 | 1416 | |
cededa5a | 1417 | if (codec_info->ignore_pch_dmic) |
35564e2b PLB |
1418 | *ignore_pch_dmic = true; |
1419 | ||
52db12d1 PLB |
1420 | cpu_dai_index = *cpu_id; |
1421 | for_each_pcm_streams(stream) { | |
0281b02e | 1422 | struct snd_soc_dai_link_codec_ch_map *sdw_codec_ch_maps; |
52db12d1 PLB |
1423 | char *name, *cpu_name; |
1424 | int playback, capture; | |
1425 | static const char * const sdw_stream_name[] = { | |
1426 | "SDW%d-Playback", | |
1427 | "SDW%d-Capture", | |
dc5a3e60 BL |
1428 | "SDW%d-Playback-%s", |
1429 | "SDW%d-Capture-%s", | |
52db12d1 PLB |
1430 | }; |
1431 | ||
d3fc5c4d | 1432 | if (!codec_info->dais[dai_index].direction[stream]) |
52db12d1 PLB |
1433 | continue; |
1434 | ||
616bee2c BL |
1435 | *be_id = codec_info->dais[dai_index].dailink[stream]; |
1436 | if (*be_id < 0) { | |
1437 | dev_err(dev, "Invalid dailink id %d\n", *be_id); | |
b2745865 BL |
1438 | return -EINVAL; |
1439 | } | |
1440 | ||
0281b02e BL |
1441 | sdw_codec_ch_maps = devm_kcalloc(dev, codec_num, |
1442 | sizeof(*sdw_codec_ch_maps), GFP_KERNEL); | |
1443 | if (!sdw_codec_ch_maps) | |
1444 | return -ENOMEM; | |
1445 | ||
52db12d1 | 1446 | /* create stream name according to first link id */ |
5714aabd | 1447 | if (append_dai_type) { |
dc5a3e60 BL |
1448 | name = devm_kasprintf(dev, GFP_KERNEL, |
1449 | sdw_stream_name[stream + 2], cpu_dai_id[0], | |
d3fc5c4d | 1450 | type_strings[codec_info->dais[dai_index].dai_type]); |
dc5a3e60 BL |
1451 | } else { |
1452 | name = devm_kasprintf(dev, GFP_KERNEL, | |
1453 | sdw_stream_name[stream], cpu_dai_id[0]); | |
1454 | } | |
52db12d1 PLB |
1455 | if (!name) |
1456 | return -ENOMEM; | |
1457 | ||
1458 | /* | |
1459 | * generate CPU DAI name base on the sdw link ID and | |
1460 | * PIN ID with offset of 2 according to sdw dai driver. | |
1461 | */ | |
1462 | for (k = 0; k < cpu_dai_num; k++) { | |
1463 | cpu_name = devm_kasprintf(dev, GFP_KERNEL, | |
1464 | "SDW%d Pin%d", cpu_dai_id[k], | |
35d28ccd | 1465 | sdw_pin_index[cpu_dai_id[k]]++); |
52db12d1 PLB |
1466 | if (!cpu_name) |
1467 | return -ENOMEM; | |
1468 | ||
1469 | if (cpu_dai_index >= sdw_cpu_dai_num) { | |
c307ca16 | 1470 | dev_err(dev, "invalid cpu dai index %d\n", |
52db12d1 PLB |
1471 | cpu_dai_index); |
1472 | return -EINVAL; | |
1473 | } | |
1474 | ||
1475 | cpus[cpu_dai_index++].dai_name = cpu_name; | |
1476 | } | |
1477 | ||
b63137cf BL |
1478 | /* |
1479 | * We create sdw dai links at first stage, so link index should | |
1480 | * not be larger than sdw_be_num | |
1481 | */ | |
1482 | if (*link_index >= sdw_be_num) { | |
c307ca16 | 1483 | dev_err(dev, "invalid dai link index %d\n", *link_index); |
52db12d1 PLB |
1484 | return -EINVAL; |
1485 | } | |
1486 | ||
1487 | if (*cpu_id >= sdw_cpu_dai_num) { | |
c307ca16 | 1488 | dev_err(dev, "invalid cpu dai index %d\n", *cpu_id); |
52db12d1 PLB |
1489 | return -EINVAL; |
1490 | } | |
1491 | ||
1492 | playback = (stream == SNDRV_PCM_STREAM_PLAYBACK); | |
1493 | capture = (stream == SNDRV_PCM_STREAM_CAPTURE); | |
616bee2c | 1494 | init_dai_link(dev, dai_links + *link_index, (*be_id)++, name, |
52db12d1 PLB |
1495 | playback, capture, |
1496 | cpus + *cpu_id, cpu_dai_num, | |
1497 | codecs, codec_num, | |
1498 | NULL, &sdw_ops); | |
b63137cf | 1499 | |
58eafe1f PLB |
1500 | /* |
1501 | * SoundWire DAILINKs use 'stream' functions and Bank Switch operations | |
1502 | * based on wait_for_completion(), tag them as 'nonatomic'. | |
1503 | */ | |
b63137cf | 1504 | dai_links[*link_index].nonatomic = true; |
52db12d1 | 1505 | |
0281b02e BL |
1506 | set_dailink_map(sdw_codec_ch_maps, codec_num, cpu_dai_num); |
1507 | dai_links[*link_index].codec_ch_maps = sdw_codec_ch_maps; | |
1d106238 | 1508 | ret = set_codec_init_func(card, adr_link, dai_links + (*link_index)++, |
d3fc5c4d | 1509 | playback, group_id, adr_index, dai_index); |
52db12d1 | 1510 | if (ret < 0) { |
c307ca16 | 1511 | dev_err(dev, "failed to init codec %d\n", codec_index); |
52db12d1 PLB |
1512 | return ret; |
1513 | } | |
1514 | ||
1515 | *cpu_id += cpu_dai_num; | |
52db12d1 PLB |
1516 | } |
1517 | ||
1518 | return 0; | |
1519 | } | |
1520 | ||
15ef2ea0 KV |
1521 | #define IDISP_CODEC_MASK 0x4 |
1522 | ||
febac07b | 1523 | static int sof_card_dai_links_create(struct snd_soc_card *card) |
52db12d1 | 1524 | { |
febac07b CK |
1525 | struct device *dev = card->dev; |
1526 | struct snd_soc_acpi_mach *mach = dev_get_platdata(card->dev); | |
98a7a114 | 1527 | int sdw_be_num = 0, ssp_num = 0, dmic_num = 0, hdmi_num = 0, bt_num = 0; |
15ef2ea0 | 1528 | struct mc_private *ctx = snd_soc_card_get_drvdata(card); |
52db12d1 | 1529 | struct snd_soc_dai_link_component *idisp_components; |
52db12d1 | 1530 | struct snd_soc_dai_link_component *ssp_components; |
98a7a114 CK |
1531 | struct snd_soc_acpi_mach_params *mach_params = &mach->mach_params; |
1532 | const struct snd_soc_acpi_link_adr *adr_link = mach_params->links; | |
a60ed3b7 | 1533 | bool aggregation = !(sof_sdw_quirk & SOF_SDW_NO_AGGREGATION); |
52db12d1 | 1534 | struct snd_soc_dai_link_component *cpus; |
23c8aa3e | 1535 | struct snd_soc_codec_conf *codec_conf; |
5714aabd | 1536 | bool append_dai_type = false; |
35564e2b | 1537 | bool ignore_pch_dmic = false; |
a386162e | 1538 | int codec_conf_num = 0; |
23c8aa3e | 1539 | int codec_conf_index = 0; |
a60ed3b7 | 1540 | bool group_generated[SDW_MAX_GROUPS] = { }; |
52db12d1 | 1541 | int ssp_codec_index, ssp_mask; |
1d106238 | 1542 | struct snd_soc_dai_link *dai_links; |
b63137cf | 1543 | int num_links, link_index = 0; |
52db12d1 PLB |
1544 | char *name, *cpu_name; |
1545 | int total_cpu_dai_num; | |
1546 | int sdw_cpu_dai_num; | |
1547 | int i, j, be_id = 0; | |
d3fc5c4d | 1548 | int codec_index; |
52db12d1 | 1549 | int cpu_id = 0; |
52db12d1 PLB |
1550 | int ret; |
1551 | ||
a386162e CK |
1552 | ret = get_dailink_info(dev, adr_link, &sdw_be_num, &sdw_cpu_dai_num, |
1553 | &codec_conf_num); | |
98a7a114 CK |
1554 | if (ret < 0) { |
1555 | dev_err(dev, "failed to get sdw link info %d\n", ret); | |
1556 | return ret; | |
8208dd75 | 1557 | } |
52db12d1 | 1558 | |
52db12d1 PLB |
1559 | /* |
1560 | * on generic tgl platform, I2S or sdw mode is supported | |
1561 | * based on board rework. A ACPI device is registered in | |
1562 | * system only when I2S mode is supported, not sdw mode. | |
1563 | * Here check ACPI ID to confirm I2S is supported. | |
1564 | */ | |
1565 | ssp_codec_index = find_codec_info_acpi(mach->id); | |
98a7a114 CK |
1566 | if (ssp_codec_index >= 0) { |
1567 | ssp_mask = SOF_SSP_GET_PORT(sof_sdw_quirk); | |
1568 | ssp_num = hweight_long(ssp_mask); | |
1569 | } | |
52db12d1 | 1570 | |
98a7a114 CK |
1571 | if (mach_params->codec_mask & IDISP_CODEC_MASK) { |
1572 | ctx->idisp_codec = true; | |
1573 | ||
1574 | if (sof_sdw_quirk & SOF_SDW_TGL_HDMI) | |
1575 | hdmi_num = SOF_TGL_HDMI_COUNT; | |
1576 | else | |
1577 | hdmi_num = SOF_PRE_TGL_HDMI_COUNT; | |
52db12d1 PLB |
1578 | } |
1579 | ||
1580 | /* enable dmic01 & dmic16k */ | |
98a7a114 CK |
1581 | if (sof_sdw_quirk & SOF_SDW_PCH_DMIC || mach_params->dmic_num) |
1582 | dmic_num = 2; | |
52db12d1 | 1583 | |
19f1eace | 1584 | if (sof_sdw_quirk & SOF_SSP_BT_OFFLOAD_PRESENT) |
98a7a114 | 1585 | bt_num = 1; |
19f1eace | 1586 | |
98a7a114 CK |
1587 | dev_dbg(dev, "sdw %d, ssp %d, dmic %d, hdmi %d, bt: %d\n", |
1588 | sdw_be_num, ssp_num, dmic_num, hdmi_num, bt_num); | |
52db12d1 PLB |
1589 | |
1590 | /* allocate BE dailinks */ | |
98a7a114 | 1591 | num_links = sdw_be_num + ssp_num + dmic_num + hdmi_num + bt_num; |
1d106238 | 1592 | dai_links = devm_kcalloc(dev, num_links, sizeof(*dai_links), GFP_KERNEL); |
98a7a114 CK |
1593 | if (!dai_links) |
1594 | return -ENOMEM; | |
52db12d1 PLB |
1595 | |
1596 | /* allocated CPU DAIs */ | |
98a7a114 CK |
1597 | total_cpu_dai_num = sdw_cpu_dai_num + ssp_num + dmic_num + hdmi_num + bt_num; |
1598 | cpus = devm_kcalloc(dev, total_cpu_dai_num, sizeof(*cpus), GFP_KERNEL); | |
1599 | if (!cpus) | |
52db12d1 PLB |
1600 | return -ENOMEM; |
1601 | ||
a386162e CK |
1602 | /* allocate codec conf, will be populated when dailinks are created */ |
1603 | codec_conf = devm_kcalloc(dev, codec_conf_num, sizeof(*codec_conf), | |
1604 | GFP_KERNEL); | |
1605 | if (!codec_conf) | |
1606 | return -ENOMEM; | |
1607 | ||
52db12d1 PLB |
1608 | /* SDW */ |
1609 | if (!sdw_be_num) | |
1610 | goto SSP; | |
1611 | ||
35d28ccd BL |
1612 | for (i = 0; i < SDW_MAX_LINKS; i++) |
1613 | sdw_pin_index[i] = SDW_INTEL_BIDIR_PDI_BASE; | |
1614 | ||
52db12d1 | 1615 | for (; adr_link->num_adr; adr_link++) { |
dc5a3e60 BL |
1616 | /* |
1617 | * If there are two or more different devices on the same sdw link, we have to | |
1618 | * append the codec type to the dai link name to prevent duplicated dai link name. | |
1619 | * The same type devices on the same sdw link will be in the same | |
1620 | * snd_soc_acpi_adr_device array. They won't be described in different adr_links. | |
1621 | */ | |
1622 | for (i = 0; i < adr_link->num_adr; i++) { | |
d3fc5c4d BL |
1623 | /* find codec info to get dai_num */ |
1624 | codec_index = find_codec_info_part(adr_link->adr_d[i].adr); | |
1625 | if (codec_index < 0) | |
1626 | return codec_index; | |
1627 | if (codec_info_list[codec_index].dai_num > 1) { | |
1628 | append_dai_type = true; | |
1629 | goto out; | |
1630 | } | |
dc5a3e60 BL |
1631 | for (j = 0; j < i; j++) { |
1632 | if ((SDW_PART_ID(adr_link->adr_d[i].adr) != | |
1633 | SDW_PART_ID(adr_link->adr_d[j].adr)) || | |
1634 | (SDW_MFG_ID(adr_link->adr_d[i].adr) != | |
1f1ef7e5 | 1635 | SDW_MFG_ID(adr_link->adr_d[j].adr))) { |
5714aabd | 1636 | append_dai_type = true; |
dc5a3e60 BL |
1637 | goto out; |
1638 | } | |
1639 | } | |
1640 | } | |
1641 | } | |
1642 | out: | |
1643 | ||
1644 | /* generate DAI links by each sdw link */ | |
1645 | for (adr_link = mach_params->links ; adr_link->num_adr; adr_link++) { | |
c8db7b50 BL |
1646 | for (i = 0; i < adr_link->num_adr; i++) { |
1647 | const struct snd_soc_acpi_endpoint *endpoint; | |
52db12d1 | 1648 | |
c8db7b50 | 1649 | endpoint = adr_link->adr_d[i].endpoints; |
52db12d1 | 1650 | |
c8db7b50 BL |
1651 | /* this group has been generated */ |
1652 | if (endpoint->aggregated && | |
1653 | group_generated[endpoint->group_id]) | |
1654 | continue; | |
52db12d1 | 1655 | |
d3fc5c4d BL |
1656 | /* find codec info to get dai_num */ |
1657 | codec_index = find_codec_info_part(adr_link->adr_d[i].adr); | |
1658 | if (codec_index < 0) | |
1659 | return codec_index; | |
1660 | ||
1661 | for (j = 0; j < codec_info_list[codec_index].dai_num ; j++) { | |
febac07b | 1662 | ret = create_sdw_dailink(card, &link_index, dai_links, |
1d106238 | 1663 | sdw_be_num, sdw_cpu_dai_num, cpus, |
a60ed3b7 | 1664 | adr_link, &cpu_id, |
a386162e | 1665 | codec_conf, codec_conf_num, |
d3fc5c4d BL |
1666 | &be_id, &codec_conf_index, |
1667 | &ignore_pch_dmic, append_dai_type, i, j); | |
1668 | if (ret < 0) { | |
c307ca16 | 1669 | dev_err(dev, "failed to create dai link %d\n", link_index); |
d3fc5c4d BL |
1670 | return ret; |
1671 | } | |
c8db7b50 | 1672 | } |
a60ed3b7 CK |
1673 | |
1674 | if (aggregation && endpoint->aggregated) | |
1675 | group_generated[endpoint->group_id] = true; | |
52db12d1 PLB |
1676 | } |
1677 | } | |
1678 | ||
52db12d1 PLB |
1679 | SSP: |
1680 | /* SSP */ | |
1681 | if (!ssp_num) | |
1682 | goto DMIC; | |
1683 | ||
1684 | for (i = 0, j = 0; ssp_mask; i++, ssp_mask >>= 1) { | |
1685 | struct sof_sdw_codec_info *info; | |
1686 | int playback, capture; | |
1687 | char *codec_name; | |
1688 | ||
1689 | if (!(ssp_mask & 0x1)) | |
1690 | continue; | |
1691 | ||
1692 | name = devm_kasprintf(dev, GFP_KERNEL, | |
1693 | "SSP%d-Codec", i); | |
1694 | if (!name) | |
1695 | return -ENOMEM; | |
1696 | ||
1697 | cpu_name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d Pin", i); | |
1698 | if (!cpu_name) | |
1699 | return -ENOMEM; | |
1700 | ||
1701 | ssp_components = devm_kzalloc(dev, sizeof(*ssp_components), | |
1702 | GFP_KERNEL); | |
1703 | if (!ssp_components) | |
1704 | return -ENOMEM; | |
1705 | ||
1706 | info = &codec_info_list[ssp_codec_index]; | |
1707 | codec_name = devm_kasprintf(dev, GFP_KERNEL, "i2c-%s:0%d", | |
1708 | info->acpi_id, j++); | |
1709 | if (!codec_name) | |
1710 | return -ENOMEM; | |
1711 | ||
1712 | ssp_components->name = codec_name; | |
d3fc5c4d | 1713 | /* TODO: support multi codec dai on SSP when it is needed */ |
07140abb | 1714 | ssp_components->dai_name = info->dais[0].dai_name; |
52db12d1 PLB |
1715 | cpus[cpu_id].dai_name = cpu_name; |
1716 | ||
07140abb BL |
1717 | playback = info->dais[0].direction[SNDRV_PCM_STREAM_PLAYBACK]; |
1718 | capture = info->dais[0].direction[SNDRV_PCM_STREAM_CAPTURE]; | |
1d106238 | 1719 | init_dai_link(dev, dai_links + link_index, be_id, name, |
52db12d1 PLB |
1720 | playback, capture, |
1721 | cpus + cpu_id, 1, | |
1722 | ssp_components, 1, | |
1723 | NULL, info->ops); | |
1724 | ||
1d106238 | 1725 | ret = info->dais[0].init(card, NULL, dai_links + link_index, info, 0); |
52db12d1 PLB |
1726 | if (ret < 0) |
1727 | return ret; | |
1728 | ||
b63137cf | 1729 | INC_ID(be_id, cpu_id, link_index); |
52db12d1 PLB |
1730 | } |
1731 | ||
1732 | DMIC: | |
1733 | /* dmic */ | |
1734 | if (dmic_num > 0) { | |
35564e2b PLB |
1735 | if (ignore_pch_dmic) { |
1736 | dev_warn(dev, "Ignoring PCH DMIC\n"); | |
1737 | goto HDMI; | |
1738 | } | |
52db12d1 | 1739 | cpus[cpu_id].dai_name = "DMIC01 Pin"; |
1d106238 | 1740 | init_dai_link(dev, dai_links + link_index, be_id, "dmic01", |
52db12d1 PLB |
1741 | 0, 1, // DMIC only supports capture |
1742 | cpus + cpu_id, 1, | |
1743 | dmic_component, 1, | |
1744 | sof_sdw_dmic_init, NULL); | |
b63137cf | 1745 | INC_ID(be_id, cpu_id, link_index); |
52db12d1 PLB |
1746 | |
1747 | cpus[cpu_id].dai_name = "DMIC16k Pin"; | |
1d106238 | 1748 | init_dai_link(dev, dai_links + link_index, be_id, "dmic16k", |
52db12d1 PLB |
1749 | 0, 1, // DMIC only supports capture |
1750 | cpus + cpu_id, 1, | |
1751 | dmic_component, 1, | |
1752 | /* don't call sof_sdw_dmic_init() twice */ | |
1753 | NULL, NULL); | |
b63137cf | 1754 | INC_ID(be_id, cpu_id, link_index); |
52db12d1 PLB |
1755 | } |
1756 | ||
35564e2b | 1757 | HDMI: |
52db12d1 PLB |
1758 | /* HDMI */ |
1759 | if (hdmi_num > 0) { | |
1760 | idisp_components = devm_kcalloc(dev, hdmi_num, | |
1761 | sizeof(*idisp_components), | |
1762 | GFP_KERNEL); | |
1763 | if (!idisp_components) | |
1764 | return -ENOMEM; | |
1765 | } | |
1766 | ||
1767 | for (i = 0; i < hdmi_num; i++) { | |
1768 | name = devm_kasprintf(dev, GFP_KERNEL, | |
1769 | "iDisp%d", i + 1); | |
1770 | if (!name) | |
1771 | return -ENOMEM; | |
1772 | ||
15ef2ea0 KV |
1773 | if (ctx->idisp_codec) { |
1774 | idisp_components[i].name = "ehdaudio0D2"; | |
1775 | idisp_components[i].dai_name = devm_kasprintf(dev, | |
1776 | GFP_KERNEL, | |
1777 | "intel-hdmi-hifi%d", | |
1778 | i + 1); | |
1779 | if (!idisp_components[i].dai_name) | |
1780 | return -ENOMEM; | |
1781 | } else { | |
1785af9f | 1782 | idisp_components[i] = asoc_dummy_dlc; |
15ef2ea0 | 1783 | } |
52db12d1 PLB |
1784 | |
1785 | cpu_name = devm_kasprintf(dev, GFP_KERNEL, | |
1786 | "iDisp%d Pin", i + 1); | |
1787 | if (!cpu_name) | |
1788 | return -ENOMEM; | |
1789 | ||
1790 | cpus[cpu_id].dai_name = cpu_name; | |
1d106238 | 1791 | init_dai_link(dev, dai_links + link_index, be_id, name, |
52db12d1 PLB |
1792 | 1, 0, // HDMI only supports playback |
1793 | cpus + cpu_id, 1, | |
1794 | idisp_components + i, 1, | |
1795 | sof_sdw_hdmi_init, NULL); | |
b63137cf | 1796 | INC_ID(be_id, cpu_id, link_index); |
52db12d1 | 1797 | } |
52db12d1 | 1798 | |
19f1eace YZ |
1799 | if (sof_sdw_quirk & SOF_SSP_BT_OFFLOAD_PRESENT) { |
1800 | int port = (sof_sdw_quirk & SOF_BT_OFFLOAD_SSP_MASK) >> | |
1801 | SOF_BT_OFFLOAD_SSP_SHIFT; | |
1802 | ||
1803 | name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d-BT", port); | |
1804 | if (!name) | |
1805 | return -ENOMEM; | |
1806 | ||
19f1eace YZ |
1807 | cpu_name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d Pin", port); |
1808 | if (!cpu_name) | |
1809 | return -ENOMEM; | |
1810 | ||
1811 | cpus[cpu_id].dai_name = cpu_name; | |
1d106238 CK |
1812 | init_dai_link(dev, dai_links + link_index, be_id, name, 1, 1, |
1813 | cpus + cpu_id, 1, &asoc_dummy_dlc, 1, NULL, NULL); | |
19f1eace YZ |
1814 | } |
1815 | ||
1d106238 | 1816 | card->dai_link = dai_links; |
52db12d1 PLB |
1817 | card->num_links = num_links; |
1818 | ||
23c8aa3e | 1819 | card->codec_conf = codec_conf; |
a386162e | 1820 | card->num_configs = codec_conf_num; |
23c8aa3e | 1821 | |
52db12d1 PLB |
1822 | return 0; |
1823 | } | |
1824 | ||
be3afa12 | 1825 | static int sof_sdw_card_late_probe(struct snd_soc_card *card) |
1826 | { | |
8208dd75 PLB |
1827 | struct mc_private *ctx = snd_soc_card_get_drvdata(card); |
1828 | int ret = 0; | |
1829 | int i; | |
be3afa12 | 1830 | |
1831 | for (i = 0; i < ARRAY_SIZE(codec_info_list); i++) { | |
06b830bd YZ |
1832 | if (codec_info_list[i].codec_card_late_probe) { |
1833 | ret = codec_info_list[i].codec_card_late_probe(card); | |
be3afa12 | 1834 | |
06b830bd YZ |
1835 | if (ret < 0) |
1836 | return ret; | |
1837 | } | |
be3afa12 | 1838 | } |
1839 | ||
8208dd75 PLB |
1840 | if (ctx->idisp_codec) |
1841 | ret = sof_sdw_hdmi_card_late_probe(card); | |
1842 | ||
1843 | return ret; | |
be3afa12 | 1844 | } |
1845 | ||
52db12d1 PLB |
1846 | /* SoC card */ |
1847 | static const char sdw_card_long_name[] = "Intel Soundwire SOF"; | |
1848 | ||
1849 | static struct snd_soc_card card_sof_sdw = { | |
1850 | .name = "soundwire", | |
fb4b42f6 | 1851 | .owner = THIS_MODULE, |
be3afa12 | 1852 | .late_probe = sof_sdw_card_late_probe, |
52db12d1 PLB |
1853 | }; |
1854 | ||
d3fc5c4d BL |
1855 | /* helper to get the link that the codec DAI is used */ |
1856 | static struct snd_soc_dai_link *mc_find_codec_dai_used(struct snd_soc_card *card, | |
1857 | const char *dai_name) | |
1858 | { | |
1d106238 | 1859 | struct snd_soc_dai_link *dai_link; |
d3fc5c4d BL |
1860 | int i; |
1861 | int j; | |
1862 | ||
1d106238 CK |
1863 | for_each_card_prelinks(card, i, dai_link) { |
1864 | for (j = 0; j < dai_link->num_codecs; j++) { | |
d3fc5c4d | 1865 | /* Check each codec in a link */ |
1d106238 CK |
1866 | if (!strcmp(dai_link->codecs[j].dai_name, dai_name)) |
1867 | return dai_link; | |
d3fc5c4d BL |
1868 | } |
1869 | } | |
1870 | return NULL; | |
1871 | } | |
1872 | ||
768ad6d8 PLB |
1873 | static void mc_dailink_exit_loop(struct snd_soc_card *card) |
1874 | { | |
1d106238 | 1875 | struct snd_soc_dai_link *dai_link; |
768ad6d8 PLB |
1876 | int ret; |
1877 | int i, j; | |
1878 | ||
1879 | for (i = 0; i < ARRAY_SIZE(codec_info_list); i++) { | |
d3fc5c4d BL |
1880 | for (j = 0; j < codec_info_list[i].dai_num; j++) { |
1881 | /* Check each dai in codec_info_lis to see if it is used in the link */ | |
1882 | if (!codec_info_list[i].dais[j].exit) | |
1883 | continue; | |
1884 | /* | |
1885 | * We don't need to call .exit function if there is no matched | |
1886 | * dai link found. | |
1887 | */ | |
1d106238 CK |
1888 | dai_link = mc_find_codec_dai_used(card, |
1889 | codec_info_list[i].dais[j].dai_name); | |
1890 | if (dai_link) { | |
d3fc5c4d | 1891 | /* Do the .exit function if the codec dai is used in the link */ |
1d106238 | 1892 | ret = codec_info_list[i].dais[j].exit(card, dai_link); |
768ad6d8 PLB |
1893 | if (ret) |
1894 | dev_warn(card->dev, | |
1895 | "codec exit failed %d\n", | |
1896 | ret); | |
1897 | break; | |
1898 | } | |
1899 | } | |
1900 | } | |
1901 | } | |
1902 | ||
52db12d1 PLB |
1903 | static int mc_probe(struct platform_device *pdev) |
1904 | { | |
1905 | struct snd_soc_card *card = &card_sof_sdw; | |
855e69f4 | 1906 | struct snd_soc_acpi_mach *mach = dev_get_platdata(&pdev->dev); |
52db12d1 | 1907 | struct mc_private *ctx; |
b1ca2f63 | 1908 | int amp_num = 0, i; |
52db12d1 PLB |
1909 | int ret; |
1910 | ||
855e69f4 CK |
1911 | card->dev = &pdev->dev; |
1912 | ||
1913 | dev_dbg(card->dev, "Entry\n"); | |
52db12d1 | 1914 | |
855e69f4 | 1915 | ctx = devm_kzalloc(card->dev, sizeof(*ctx), GFP_KERNEL); |
52db12d1 PLB |
1916 | if (!ctx) |
1917 | return -ENOMEM; | |
1918 | ||
855e69f4 CK |
1919 | INIT_LIST_HEAD(&ctx->hdmi_pcm_list); |
1920 | ||
1921 | snd_soc_card_set_drvdata(card, ctx); | |
1922 | ||
52db12d1 PLB |
1923 | dmi_check_system(sof_sdw_quirk_table); |
1924 | ||
2555ebe9 | 1925 | if (quirk_override != -1) { |
855e69f4 | 1926 | dev_info(card->dev, "Overriding quirk 0x%lx => 0x%x\n", |
2555ebe9 PLB |
1927 | sof_sdw_quirk, quirk_override); |
1928 | sof_sdw_quirk = quirk_override; | |
1929 | } | |
52db12d1 | 1930 | |
855e69f4 | 1931 | log_quirks(card->dev); |
52db12d1 | 1932 | |
8673e68b CK |
1933 | /* reset amp_num to ensure amp_num++ starts from 0 in each probe */ |
1934 | for (i = 0; i < ARRAY_SIZE(codec_info_list); i++) | |
1935 | codec_info_list[i].amp_num = 0; | |
1936 | ||
febac07b | 1937 | ret = sof_card_dai_links_create(card); |
52db12d1 PLB |
1938 | if (ret < 0) |
1939 | return ret; | |
1940 | ||
b1ca2f63 | 1941 | /* |
1942 | * the default amp_num is zero for each codec and | |
1943 | * amp_num will only be increased for active amp | |
1944 | * codecs on used platform | |
1945 | */ | |
1946 | for (i = 0; i < ARRAY_SIZE(codec_info_list); i++) | |
1947 | amp_num += codec_info_list[i].amp_num; | |
1948 | ||
52db12d1 | 1949 | card->components = devm_kasprintf(card->dev, GFP_KERNEL, |
9d05a1e5 | 1950 | "cfg-spk:%d cfg-amp:%d", |
b1ca2f63 | 1951 | (sof_sdw_quirk & SOF_SDW_FOUR_SPK) |
1952 | ? 4 : 2, amp_num); | |
52db12d1 PLB |
1953 | if (!card->components) |
1954 | return -ENOMEM; | |
1955 | ||
209b0b0d PLB |
1956 | if (mach->mach_params.dmic_num) { |
1957 | card->components = devm_kasprintf(card->dev, GFP_KERNEL, | |
1958 | "%s mic:dmic cfg-mics:%d", | |
1959 | card->components, | |
1960 | mach->mach_params.dmic_num); | |
1961 | if (!card->components) | |
1962 | return -ENOMEM; | |
1963 | } | |
1964 | ||
52db12d1 PLB |
1965 | card->long_name = sdw_card_long_name; |
1966 | ||
1967 | /* Register the card */ | |
855e69f4 | 1968 | ret = devm_snd_soc_register_card(card->dev, card); |
52db12d1 PLB |
1969 | if (ret) { |
1970 | dev_err(card->dev, "snd_soc_register_card failed %d\n", ret); | |
768ad6d8 | 1971 | mc_dailink_exit_loop(card); |
52db12d1 PLB |
1972 | return ret; |
1973 | } | |
1974 | ||
1975 | platform_set_drvdata(pdev, card); | |
1976 | ||
1977 | return ret; | |
1978 | } | |
1979 | ||
51a4a7ec | 1980 | static void mc_remove(struct platform_device *pdev) |
75136503 BL |
1981 | { |
1982 | struct snd_soc_card *card = platform_get_drvdata(pdev); | |
75136503 | 1983 | |
768ad6d8 | 1984 | mc_dailink_exit_loop(card); |
75136503 BL |
1985 | } |
1986 | ||
18c45cb3 CK |
1987 | static const struct platform_device_id mc_id_table[] = { |
1988 | { "sof_sdw", }, | |
1989 | {} | |
1990 | }; | |
1991 | MODULE_DEVICE_TABLE(platform, mc_id_table); | |
1992 | ||
52db12d1 PLB |
1993 | static struct platform_driver sof_sdw_driver = { |
1994 | .driver = { | |
1995 | .name = "sof_sdw", | |
1996 | .pm = &snd_soc_pm_ops, | |
1997 | }, | |
1998 | .probe = mc_probe, | |
51a4a7ec | 1999 | .remove_new = mc_remove, |
18c45cb3 | 2000 | .id_table = mc_id_table, |
52db12d1 PLB |
2001 | }; |
2002 | ||
2003 | module_platform_driver(sof_sdw_driver); | |
2004 | ||
2005 | MODULE_DESCRIPTION("ASoC SoundWire Generic Machine driver"); | |
2006 | MODULE_AUTHOR("Bard Liao <yung-chuan.liao@linux.intel.com>"); | |
2007 | MODULE_AUTHOR("Rander Wang <rander.wang@linux.intel.com>"); | |
2008 | MODULE_AUTHOR("Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>"); | |
2009 | MODULE_LICENSE("GPL v2"); | |
f6081af6 | 2010 | MODULE_IMPORT_NS(SND_SOC_INTEL_HDA_DSP_COMMON); |
9c5046e4 | 2011 | MODULE_IMPORT_NS(SND_SOC_INTEL_SOF_MAXIM_COMMON); |