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