1226633Sdim// SPDX-License-Identifier: GPL-2.0-only
2218887Sdim// Copyright (c) 2020 Intel Corporation
3218887Sdim
4218887Sdim/*
5218887Sdim *  sof_sdw - ASOC Machine driver for Intel SoundWire platforms
6218887Sdim */
7218887Sdim
8218887Sdim#include <linux/device.h>
9218887Sdim#include <linux/dmi.h>
10218887Sdim#include <linux/module.h>
11218887Sdim#include <linux/soundwire/sdw.h>
12218887Sdim#include <linux/soundwire/sdw_type.h>
13218887Sdim#include <sound/soc.h>
14218887Sdim#include <sound/soc-acpi.h>
15218887Sdim#include "sof_sdw_common.h"
16234353Sdim#include "../../codecs/rt711.h"
17249423Sdim
18249423Sdimunsigned long sof_sdw_quirk = RT711_JD1;
19221345Sdimstatic int quirk_override = -1;
20218887Sdimmodule_param_named(quirk, quirk_override, int, 0444);
21219077SdimMODULE_PARM_DESC(quirk, "Board-specific quirk override");
22226633Sdim
23249423Sdimstatic void log_quirks(struct device *dev)
24234353Sdim{
25218887Sdim	if (SOF_JACK_JDSRC(sof_sdw_quirk))
26249423Sdim		dev_dbg(dev, "quirk realtek,jack-detect-source %ld\n",
27218887Sdim			SOF_JACK_JDSRC(sof_sdw_quirk));
28218887Sdim	if (sof_sdw_quirk & SOF_SDW_FOUR_SPK)
29218887Sdim		dev_dbg(dev, "quirk SOF_SDW_FOUR_SPK enabled\n");
30218887Sdim	if (sof_sdw_quirk & SOF_SDW_TGL_HDMI)
31218887Sdim		dev_dbg(dev, "quirk SOF_SDW_TGL_HDMI enabled\n");
32221345Sdim	if (sof_sdw_quirk & SOF_SDW_PCH_DMIC)
33219077Sdim		dev_dbg(dev, "quirk SOF_SDW_PCH_DMIC enabled\n");
34219077Sdim	if (SOF_SSP_GET_PORT(sof_sdw_quirk))
35219077Sdim		dev_dbg(dev, "SSP port %ld\n",
36219077Sdim			SOF_SSP_GET_PORT(sof_sdw_quirk));
37219077Sdim	if (sof_sdw_quirk & SOF_SDW_NO_AGGREGATION)
38234353Sdim		dev_dbg(dev, "quirk SOF_SDW_NO_AGGREGATION enabled\n");
39234353Sdim}
40234353Sdim
41234353Sdimstatic int sof_sdw_quirk_cb(const struct dmi_system_id *id)
42234353Sdim{
43234353Sdim	sof_sdw_quirk = (unsigned long)id->driver_data;
44224145Sdim	return 1;
45224145Sdim}
46218887Sdim
47234353Sdimstatic const struct dmi_system_id sof_sdw_quirk_table[] = {
48234353Sdim	/* CometLake devices */
49234353Sdim	{
50234353Sdim		.callback = sof_sdw_quirk_cb,
51234353Sdim		.matches = {
52234353Sdim			DMI_MATCH(DMI_SYS_VENDOR, "Intel Corporation"),
53234353Sdim			DMI_MATCH(DMI_PRODUCT_NAME, "CometLake Client"),
54234353Sdim		},
55234353Sdim		.driver_data = (void *)SOF_SDW_PCH_DMIC,
56234353Sdim	},
57234353Sdim	{
58218887Sdim		.callback = sof_sdw_quirk_cb,
59218887Sdim		.matches = {
60219077Sdim			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
61219077Sdim			DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "09C6")
62234353Sdim		},
63219077Sdim		.driver_data = (void *)RT711_JD2,
64234353Sdim	},
65218887Sdim	{
66234353Sdim		/* early version of SKU 09C6 */
67234353Sdim		.callback = sof_sdw_quirk_cb,
68249423Sdim		.matches = {
69226633Sdim			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
70234353Sdim			DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0983")
71239462Sdim		},
72218887Sdim		.driver_data = (void *)RT711_JD2,
73219077Sdim	},
74219077Sdim	{
75218887Sdim		.callback = sof_sdw_quirk_cb,
76219077Sdim		.matches = {
77221345Sdim			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
78219077Sdim			DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "098F"),
79219077Sdim		},
80221345Sdim		.driver_data = (void *)(RT711_JD2 |
81234353Sdim					SOF_SDW_FOUR_SPK),
82226633Sdim	},
83226633Sdim	{
84226633Sdim		.callback = sof_sdw_quirk_cb,
85221345Sdim		.matches = {
86221345Sdim			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
87218887Sdim			DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0990"),
88219077Sdim		},
89218887Sdim		.driver_data = (void *)(RT711_JD2 |
90219077Sdim					SOF_SDW_FOUR_SPK),
91219077Sdim	},
92226633Sdim	/* IceLake devices */
93226633Sdim	{
94219077Sdim		.callback = sof_sdw_quirk_cb,
95218887Sdim		.matches = {
96219077Sdim			DMI_MATCH(DMI_SYS_VENDOR, "Intel Corporation"),
97219077Sdim			DMI_MATCH(DMI_PRODUCT_NAME, "Ice Lake Client"),
98219077Sdim		},
99226633Sdim		.driver_data = (void *)SOF_SDW_PCH_DMIC,
100226633Sdim	},
101226633Sdim	/* TigerLake devices */
102226633Sdim	{
103226633Sdim		.callback = sof_sdw_quirk_cb,
104218887Sdim		.matches = {
105221345Sdim			DMI_MATCH(DMI_SYS_VENDOR, "Intel Corporation"),
106221345Sdim			DMI_MATCH(DMI_PRODUCT_NAME,
107221345Sdim				  "Tiger Lake Client Platform"),
108221345Sdim		},
109221345Sdim		.driver_data = (void *)(SOF_SDW_TGL_HDMI |
110221345Sdim					RT711_JD1 |
111223017Sdim					SOF_SDW_PCH_DMIC |
112226633Sdim					SOF_SSP_PORT(SOF_I2S_SSP2)),
113226633Sdim	},
114226633Sdim	{
115226633Sdim		.callback = sof_sdw_quirk_cb,
116221345Sdim		.matches = {
117251662Sdim			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
118251662Sdim			DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0A3E")
119218887Sdim		},
120234353Sdim		.driver_data = (void *)(SOF_SDW_TGL_HDMI |
121219077Sdim					RT711_JD2),
122234353Sdim	},
123218887Sdim	{
124234353Sdim		/* another SKU of Dell Latitude 9520 */
125226633Sdim		.callback = sof_sdw_quirk_cb,
126226633Sdim		.matches = {
127219077Sdim			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
128234353Sdim			DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0A3F")
129226633Sdim		},
130226633Sdim		.driver_data = (void *)(SOF_SDW_TGL_HDMI |
131224145Sdim					RT711_JD2),
132226633Sdim	},
133234353Sdim	{
134226633Sdim		/* Dell XPS 9710 */
135226633Sdim		.callback = sof_sdw_quirk_cb,
136224145Sdim		.matches = {
137218887Sdim			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
138221345Sdim			DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0A5D")
139234353Sdim		},
140221345Sdim		.driver_data = (void *)(SOF_SDW_TGL_HDMI |
141221345Sdim					RT711_JD2 |
142221345Sdim					SOF_SDW_FOUR_SPK),
143234353Sdim	},
144263508Sdim	{
145263508Sdim		.callback = sof_sdw_quirk_cb,
146263508Sdim		.matches = {
147218887Sdim			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
148226633Sdim			DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0A5E")
149219077Sdim		},
150218887Sdim		.driver_data = (void *)(SOF_SDW_TGL_HDMI |
151218887Sdim					RT711_JD2 |
152234353Sdim					SOF_SDW_FOUR_SPK),
153234353Sdim	},
154226633Sdim	{
155226633Sdim		.callback = sof_sdw_quirk_cb,
156234353Sdim		.matches = {
157234353Sdim			DMI_MATCH(DMI_SYS_VENDOR, "Google"),
158226633Sdim			DMI_MATCH(DMI_PRODUCT_NAME, "Volteer"),
159226633Sdim		},
160226633Sdim		.driver_data = (void *)(SOF_SDW_TGL_HDMI |
161234353Sdim					SOF_SDW_PCH_DMIC |
162234353Sdim					SOF_SDW_FOUR_SPK |
163226633Sdim					SOF_BT_OFFLOAD_SSP(2) |
164226633Sdim					SOF_SSP_BT_OFFLOAD_PRESENT),
165226633Sdim	},
166226633Sdim	{
167226633Sdim		.callback = sof_sdw_quirk_cb,
168226633Sdim		.matches = {
169226633Sdim			DMI_MATCH(DMI_SYS_VENDOR, "Google"),
170234353Sdim			DMI_MATCH(DMI_PRODUCT_NAME, "Ripto"),
171234353Sdim		},
172226633Sdim		.driver_data = (void *)(SOF_SDW_TGL_HDMI |
173226633Sdim					SOF_SDW_PCH_DMIC |
174226633Sdim					SOF_SDW_FOUR_SPK),
175226633Sdim	},
176224145Sdim	{
177224145Sdim		/*
178224145Sdim		 * this entry covers multiple HP SKUs. The family name
179224145Sdim		 * does not seem robust enough, so we use a partial
180234353Sdim		 * match that ignores the product name suffix
181234353Sdim		 * (e.g. 15-eb1xxx, 14t-ea000 or 13-aw2xxx)
182226633Sdim		 */
183226633Sdim		.callback = sof_sdw_quirk_cb,
184226633Sdim		.matches = {
185226633Sdim			DMI_MATCH(DMI_SYS_VENDOR, "HP"),
186234353Sdim			DMI_MATCH(DMI_PRODUCT_NAME, "HP Spectre x360 Conv"),
187226633Sdim		},
188226633Sdim		.driver_data = (void *)(SOF_SDW_TGL_HDMI |
189226633Sdim					SOF_SDW_PCH_DMIC |
190234353Sdim					RT711_JD1),
191234353Sdim	},
192226633Sdim	{
193226633Sdim		/*
194218887Sdim		 * this entry covers HP Spectre x360 where the DMI information
195218887Sdim		 * changed somehow
196218887Sdim		 */
197218887Sdim		.callback = sof_sdw_quirk_cb,
198243830Sdim		.matches = {
199218887Sdim			DMI_MATCH(DMI_SYS_VENDOR, "HP"),
200218887Sdim			DMI_MATCH(DMI_BOARD_NAME, "8709"),
201218887Sdim		},
202218887Sdim		.driver_data = (void *)(SOF_SDW_TGL_HDMI |
203218887Sdim					SOF_SDW_PCH_DMIC |
204234353Sdim					RT711_JD1),
205234353Sdim	},
206218887Sdim	{
207249423Sdim		/* NUC15 'Bishop County' LAPBC510 and LAPBC710 skews */
208218887Sdim		.callback = sof_sdw_quirk_cb,
209234353Sdim		.matches = {
210218887Sdim			DMI_MATCH(DMI_SYS_VENDOR, "Intel(R) Client Systems"),
211218887Sdim			DMI_MATCH(DMI_PRODUCT_NAME, "LAPBC"),
212218887Sdim		},
213218887Sdim		.driver_data = (void *)(SOF_SDW_TGL_HDMI |
214218887Sdim					SOF_SDW_PCH_DMIC |
215218887Sdim					RT711_JD1),
216234353Sdim	},
217234353Sdim	{
218219077Sdim		/* NUC15 LAPBC710 skews */
219218887Sdim		.callback = sof_sdw_quirk_cb,
220218887Sdim		.matches = {
221218887Sdim			DMI_MATCH(DMI_BOARD_VENDOR, "Intel Corporation"),
222218887Sdim			DMI_MATCH(DMI_BOARD_NAME, "LAPBC710"),
223234353Sdim		},
224218887Sdim		.driver_data = (void *)(SOF_SDW_TGL_HDMI |
225218887Sdim					SOF_SDW_PCH_DMIC |
226218887Sdim					RT711_JD1),
227234353Sdim	},
228234353Sdim	{
229234353Sdim		/* NUC15 'Rooks County' LAPRC510 and LAPRC710 skews */
230218887Sdim		.callback = sof_sdw_quirk_cb,
231218887Sdim		.matches = {
232218887Sdim			DMI_MATCH(DMI_SYS_VENDOR, "Intel(R) Client Systems"),
233218887Sdim			DMI_MATCH(DMI_PRODUCT_NAME, "LAPRC"),
234218887Sdim		},
235263508Sdim		.driver_data = (void *)(SOF_SDW_TGL_HDMI |
236219077Sdim					SOF_SDW_PCH_DMIC |
237218887Sdim					RT711_JD2_100K),
238234353Sdim	},
239224145Sdim	{
240224145Sdim		/* NUC15 LAPRC710 skews */
241224145Sdim		.callback = sof_sdw_quirk_cb,
242224145Sdim		.matches = {
243218887Sdim			DMI_MATCH(DMI_BOARD_VENDOR, "Intel Corporation"),
244219077Sdim			DMI_MATCH(DMI_BOARD_NAME, "LAPRC710"),
245226633Sdim		},
246218887Sdim		.driver_data = (void *)(SOF_SDW_TGL_HDMI |
247218887Sdim					SOF_SDW_PCH_DMIC |
248243830Sdim					RT711_JD2_100K),
249243830Sdim	},
250218887Sdim	/* TigerLake-SDCA devices */
251218887Sdim	{
252218887Sdim		.callback = sof_sdw_quirk_cb,
253218887Sdim		.matches = {
254218887Sdim			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
255218887Sdim			DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0A32")
256218887Sdim		},
257218887Sdim		.driver_data = (void *)(SOF_SDW_TGL_HDMI |
258218887Sdim					RT711_JD2 |
259234353Sdim					SOF_SDW_FOUR_SPK),
260234353Sdim	},
261218887Sdim	{
262224145Sdim		.callback = sof_sdw_quirk_cb,
263218887Sdim		.matches = {
264218887Sdim			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
265218887Sdim			DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0A45")
266218887Sdim		},
267218887Sdim		.driver_data = (void *)(SOF_SDW_TGL_HDMI |
268218887Sdim					RT711_JD2),
269218887Sdim	},
270218887Sdim	/* AlderLake devices */
271218887Sdim	{
272218887Sdim		.callback = sof_sdw_quirk_cb,
273218887Sdim		.matches = {
274218887Sdim			DMI_MATCH(DMI_SYS_VENDOR, "Intel Corporation"),
275218887Sdim			DMI_MATCH(DMI_PRODUCT_NAME, "Alder Lake Client Platform"),
276218887Sdim		},
277218887Sdim		.driver_data = (void *)(RT711_JD2_100K |
278218887Sdim					SOF_SDW_TGL_HDMI |
279218887Sdim					SOF_BT_OFFLOAD_SSP(2) |
280218887Sdim					SOF_SSP_BT_OFFLOAD_PRESENT),
281218887Sdim	},
282224145Sdim	{
283224145Sdim		.callback = sof_sdw_quirk_cb,
284249423Sdim		.matches = {
285218887Sdim			DMI_MATCH(DMI_SYS_VENDOR, "Google"),
286218887Sdim			DMI_MATCH(DMI_PRODUCT_NAME, "Brya"),
287249423Sdim		},
288218887Sdim		.driver_data = (void *)(SOF_SDW_TGL_HDMI |
289234353Sdim					SOF_SDW_PCH_DMIC |
290234353Sdim					SOF_SDW_FOUR_SPK |
291218887Sdim					SOF_BT_OFFLOAD_SSP(2) |
292218887Sdim					SOF_SSP_BT_OFFLOAD_PRESENT),
293218887Sdim	},
294218887Sdim	{
295218887Sdim		.callback = sof_sdw_quirk_cb,
296224145Sdim		.matches = {
297224145Sdim			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
298224145Sdim			DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0AF0")
299224145Sdim		},
300224145Sdim		.driver_data = (void *)(SOF_SDW_TGL_HDMI |
301224145Sdim					RT711_JD2 |
302224145Sdim					SOF_SDW_FOUR_SPK),
303226633Sdim	},
304224145Sdim	{
305226633Sdim		.callback = sof_sdw_quirk_cb,
306218887Sdim		.matches = {
307224145Sdim			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
308224145Sdim			DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0AF3"),
309224145Sdim		},
310234353Sdim		/* No Jack */
311224145Sdim		.driver_data = (void *)(SOF_SDW_TGL_HDMI |
312249423Sdim					SOF_SDW_FOUR_SPK),
313224145Sdim	},
314224145Sdim	{
315226633Sdim		.callback = sof_sdw_quirk_cb,
316218887Sdim		.matches = {
317218887Sdim			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
318218887Sdim			DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0AFE")
319218887Sdim		},
320218887Sdim		.driver_data = (void *)(SOF_SDW_TGL_HDMI |
321218887Sdim					RT711_JD2 |
322218887Sdim					SOF_SDW_FOUR_SPK),
323243830Sdim	},
324218887Sdim	{
325218887Sdim		.callback = sof_sdw_quirk_cb,
326218887Sdim		.matches = {
327218887Sdim			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
328218887Sdim			DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0AFF")
329218887Sdim		},
330218887Sdim		.driver_data = (void *)(SOF_SDW_TGL_HDMI |
331218887Sdim					RT711_JD2 |
332234353Sdim					SOF_SDW_FOUR_SPK),
333234353Sdim	},
334218887Sdim	{
335218887Sdim		.callback = sof_sdw_quirk_cb,
336218887Sdim		.matches = {
337224145Sdim			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
338224145Sdim			DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0B00")
339224145Sdim		},
340218887Sdim		.driver_data = (void *)(SOF_SDW_TGL_HDMI |
341218887Sdim					RT711_JD2 |
342218887Sdim					SOF_SDW_FOUR_SPK),
343218887Sdim	},
344218887Sdim	{
345224145Sdim		.callback = sof_sdw_quirk_cb,
346234353Sdim		.matches = {
347218887Sdim			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
348218887Sdim			DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0B01")
349218887Sdim		},
350218887Sdim		.driver_data = (void *)(SOF_SDW_TGL_HDMI |
351218887Sdim					RT711_JD2 |
352234353Sdim					SOF_SDW_FOUR_SPK),
353218887Sdim	},
354218887Sdim	{
355218887Sdim		.callback = sof_sdw_quirk_cb,
356218887Sdim		.matches = {
357234353Sdim			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
358234353Sdim			DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0B11")
359234353Sdim		},
360234353Sdim		.driver_data = (void *)(SOF_SDW_TGL_HDMI |
361218887Sdim					RT711_JD2 |
362224145Sdim					SOF_SDW_FOUR_SPK),
363224145Sdim	},
364234353Sdim	{
365249423Sdim		.callback = sof_sdw_quirk_cb,
366218887Sdim		.matches = {
367218887Sdim			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
368218887Sdim			DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0B12")
369218887Sdim		},
370249423Sdim		.driver_data = (void *)(SOF_SDW_TGL_HDMI |
371249423Sdim					RT711_JD2 |
372249423Sdim					SOF_SDW_FOUR_SPK),
373218887Sdim	},
374221345Sdim	{
375218887Sdim		.callback = sof_sdw_quirk_cb,
376249423Sdim		.matches = {
377224145Sdim			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
378224145Sdim			DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0B13"),
379218887Sdim		},
380218887Sdim		/* No Jack */
381224145Sdim		.driver_data = (void *)SOF_SDW_TGL_HDMI,
382218887Sdim	},
383218887Sdim	{
384218887Sdim		.callback = sof_sdw_quirk_cb,
385218887Sdim		.matches = {
386218887Sdim			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
387218887Sdim			DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0B14"),
388218887Sdim		},
389218887Sdim		/* No Jack */
390234353Sdim		.driver_data = (void *)SOF_SDW_TGL_HDMI,
391218887Sdim	},
392218887Sdim
393218887Sdim	{
394218887Sdim		.callback = sof_sdw_quirk_cb,
395218887Sdim		.matches = {
396249423Sdim			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
397224145Sdim			DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0B29"),
398224145Sdim		},
399218887Sdim		.driver_data = (void *)(SOF_SDW_TGL_HDMI |
400218887Sdim					RT711_JD2 |
401224145Sdim					SOF_SDW_FOUR_SPK),
402218887Sdim	},
403218887Sdim	{
404218887Sdim		.callback = sof_sdw_quirk_cb,
405218887Sdim		.matches = {
406218887Sdim			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
407218887Sdim			DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0B34"),
408218887Sdim		},
409234353Sdim		/* No Jack */
410234353Sdim		.driver_data = (void *)SOF_SDW_TGL_HDMI,
411218887Sdim	},
412218887Sdim	{
413219077Sdim		.callback = sof_sdw_quirk_cb,
414234353Sdim		.matches = {
415234353Sdim			DMI_MATCH(DMI_SYS_VENDOR, "HP"),
416234353Sdim			DMI_MATCH(DMI_PRODUCT_NAME, "OMEN by HP Gaming Laptop 16"),
417218887Sdim		},
418218887Sdim		.driver_data = (void *)(SOF_SDW_TGL_HDMI |
419218887Sdim					RT711_JD2),
420218887Sdim	},
421218887Sdim	/* RaptorLake devices */
422218887Sdim	{
423218887Sdim		.callback = sof_sdw_quirk_cb,
424218887Sdim		.matches = {
425234353Sdim			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
426218887Sdim			DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0BDA")
427218887Sdim		},
428234353Sdim		.driver_data = (void *)(SOF_SDW_TGL_HDMI |
429234353Sdim					RT711_JD2 |
430234353Sdim					SOF_SDW_FOUR_SPK),
431218887Sdim	},
432249423Sdim	{
433218887Sdim		.callback = sof_sdw_quirk_cb,
434218887Sdim		.matches = {
435218887Sdim			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
436249423Sdim			DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0C10"),
437218887Sdim		},
438218887Sdim		/* No Jack */
439218887Sdim		.driver_data = (void *)(SOF_SDW_TGL_HDMI |
440218887Sdim					SOF_SDW_FOUR_SPK),
441218887Sdim	},
442218887Sdim	{
443218887Sdim		.callback = sof_sdw_quirk_cb,
444218887Sdim		.matches = {
445218887Sdim			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
446218887Sdim			DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0C11")
447218887Sdim		},
448218887Sdim		.driver_data = (void *)(SOF_SDW_TGL_HDMI |
449218887Sdim					RT711_JD2 |
450218887Sdim					SOF_SDW_FOUR_SPK),
451218887Sdim	},
452218887Sdim	{
453218887Sdim		.callback = sof_sdw_quirk_cb,
454218887Sdim		.matches = {
455218887Sdim			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
456224145Sdim			DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0C40")
457218887Sdim		},
458218887Sdim		.driver_data = (void *)(SOF_SDW_TGL_HDMI |
459249423Sdim					RT711_JD2 |
460249423Sdim					SOF_SDW_FOUR_SPK),
461218887Sdim	},
462218887Sdim	{
463218887Sdim		.callback = sof_sdw_quirk_cb,
464218887Sdim		.matches = {
465218887Sdim			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
466218887Sdim			DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0C4F")
467218887Sdim		},
468218887Sdim		.driver_data = (void *)(SOF_SDW_TGL_HDMI |
469218887Sdim					RT711_JD2 |
470218887Sdim					SOF_SDW_FOUR_SPK),
471249423Sdim	},
472218887Sdim	/* MeteorLake devices */
473218887Sdim	{
474249423Sdim		.callback = sof_sdw_quirk_cb,
475218887Sdim		.matches = {
476218887Sdim			DMI_MATCH(DMI_PRODUCT_FAMILY, "Intel_mtlrvp"),
477218887Sdim		},
478218887Sdim		.driver_data = (void *)(RT711_JD1),
479234353Sdim	},
480249423Sdim	{
481218887Sdim		.callback = sof_sdw_quirk_cb,
482218887Sdim		.matches = {
483218887Sdim			DMI_MATCH(DMI_SYS_VENDOR, "Intel Corporation"),
484218887Sdim			DMI_MATCH(DMI_PRODUCT_NAME, "Meteor Lake Client Platform"),
485218887Sdim		},
486224145Sdim		.driver_data = (void *)(RT711_JD2_100K),
487218887Sdim	},
488224145Sdim	{
489224145Sdim		.callback = sof_sdw_quirk_cb,
490249423Sdim		.matches = {
491218887Sdim			DMI_MATCH(DMI_SYS_VENDOR, "Google"),
492218887Sdim			DMI_MATCH(DMI_PRODUCT_NAME, "Rex"),
493218887Sdim		},
494218887Sdim		.driver_data = (void *)(SOF_SDW_PCH_DMIC |
495218887Sdim					SOF_BT_OFFLOAD_SSP(1) |
496218887Sdim					SOF_SSP_BT_OFFLOAD_PRESENT),
497249423Sdim	},
498218887Sdim	/* LunarLake devices */
499218887Sdim	{
500218887Sdim		.callback = sof_sdw_quirk_cb,
501218887Sdim		.matches = {
502218887Sdim			DMI_MATCH(DMI_SYS_VENDOR, "Intel Corporation"),
503218887Sdim			DMI_MATCH(DMI_PRODUCT_NAME, "Lunar Lake Client Platform"),
504249423Sdim		},
505249423Sdim		.driver_data = (void *)(RT711_JD2),
506218887Sdim	},
507218887Sdim	{}
508218887Sdim};
509218887Sdim
510218887Sdimstatic struct snd_soc_dai_link_component platform_component[] = {
511218887Sdim	{
512218887Sdim		/* name might be overridden during probe */
513218887Sdim		.name = "0000:00:1f.3"
514218887Sdim	}
515218887Sdim};
516218887Sdim
517218887Sdim/* these wrappers are only needed to avoid typecast compilation errors */
518218887Sdimint sdw_startup(struct snd_pcm_substream *substream)
519218887Sdim{
520218887Sdim	return sdw_startup_stream(substream);
521218887Sdim}
522234353Sdim
523219077Sdimint sdw_prepare(struct snd_pcm_substream *substream)
524218887Sdim{
525218887Sdim	struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
526218887Sdim	struct sdw_stream_runtime *sdw_stream;
527218887Sdim	struct snd_soc_dai *dai;
528218887Sdim
529263508Sdim	/* Find stream from first CPU DAI */
530218887Sdim	dai = snd_soc_rtd_to_cpu(rtd, 0);
531218887Sdim
532226633Sdim	sdw_stream = snd_soc_dai_get_stream(dai, substream->stream);
533226633Sdim	if (IS_ERR(sdw_stream)) {
534218887Sdim		dev_err(rtd->dev, "no stream found for DAI %s\n", dai->name);
535218887Sdim		return PTR_ERR(sdw_stream);
536218887Sdim	}
537218887Sdim
538243830Sdim	return sdw_prepare_stream(sdw_stream);
539218887Sdim}
540218887Sdim
541234353Sdimint sdw_trigger(struct snd_pcm_substream *substream, int cmd)
542234353Sdim{
543224145Sdim	struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
544224145Sdim	struct sdw_stream_runtime *sdw_stream;
545234353Sdim	struct snd_soc_dai *dai;
546234353Sdim	int ret;
547234353Sdim
548234353Sdim	/* Find stream from first CPU DAI */
549224145Sdim	dai = snd_soc_rtd_to_cpu(rtd, 0);
550224145Sdim
551224145Sdim	sdw_stream = snd_soc_dai_get_stream(dai, substream->stream);
552224145Sdim	if (IS_ERR(sdw_stream)) {
553224145Sdim		dev_err(rtd->dev, "no stream found for DAI %s\n", dai->name);
554224145Sdim		return PTR_ERR(sdw_stream);
555224145Sdim	}
556224145Sdim
557224145Sdim	switch (cmd) {
558224145Sdim	case SNDRV_PCM_TRIGGER_START:
559224145Sdim	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
560234353Sdim	case SNDRV_PCM_TRIGGER_RESUME:
561249423Sdim		ret = sdw_enable_stream(sdw_stream);
562234353Sdim		break;
563234353Sdim
564234353Sdim	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
565224145Sdim	case SNDRV_PCM_TRIGGER_SUSPEND:
566224145Sdim	case SNDRV_PCM_TRIGGER_STOP:
567224145Sdim		ret = sdw_disable_stream(sdw_stream);
568224145Sdim		break;
569224145Sdim	default:
570224145Sdim		ret = -EINVAL;
571224145Sdim		break;
572249423Sdim	}
573224145Sdim
574224145Sdim	if (ret)
575224145Sdim		dev_err(rtd->dev, "%s trigger %d failed: %d\n", __func__, cmd, ret);
576224145Sdim
577224145Sdim	return ret;
578234353Sdim}
579224145Sdim
580249423Sdimint sdw_hw_params(struct snd_pcm_substream *substream,
581224145Sdim		  struct snd_pcm_hw_params *params)
582224145Sdim{
583224145Sdim	struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
584224145Sdim	struct snd_soc_dai_link_ch_map *ch_maps;
585224145Sdim	int ch = params_channels(params);
586224145Sdim	unsigned int ch_mask;
587224145Sdim	int num_codecs;
588224145Sdim	int step;
589224145Sdim	int i;
590224145Sdim
591224145Sdim	if (!rtd->dai_link->ch_maps)
592224145Sdim		return 0;
593224145Sdim
594224145Sdim	/* Identical data will be sent to all codecs in playback */
595224145Sdim	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
596224145Sdim		ch_mask = GENMASK(ch - 1, 0);
597224145Sdim		step = 0;
598224145Sdim	} else {
599224145Sdim		num_codecs = rtd->dai_link->num_codecs;
600224145Sdim
601243830Sdim		if (ch < num_codecs || ch % num_codecs != 0) {
602224145Sdim			dev_err(rtd->dev, "Channels number %d is invalid when codec number = %d\n",
603224145Sdim				ch, num_codecs);
604224145Sdim			return -EINVAL;
605224145Sdim		}
606224145Sdim
607224145Sdim		ch_mask = GENMASK(ch / num_codecs - 1, 0);
608224145Sdim		step = hweight_long(ch_mask);
609224145Sdim
610224145Sdim	}
611224145Sdim
612224145Sdim	/*
613224145Sdim	 * The captured data will be combined from each cpu DAI if the dai
614234353Sdim	 * link has more than one codec DAIs. Set codec channel mask and
615218887Sdim	 * ASoC will set the corresponding channel numbers for each cpu dai.
616218887Sdim	 */
617218887Sdim	for_each_link_ch_maps(rtd->dai_link, i, ch_maps)
618218887Sdim		ch_maps->ch_mask = ch_mask << (i * step);
619218887Sdim
620218887Sdim	return 0;
621218887Sdim}
622218887Sdim
623218887Sdimint sdw_hw_free(struct snd_pcm_substream *substream)
624218887Sdim{
625218887Sdim	struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
626218887Sdim	struct sdw_stream_runtime *sdw_stream;
627218887Sdim	struct snd_soc_dai *dai;
628218887Sdim
629218887Sdim	/* Find stream from first CPU DAI */
630218887Sdim	dai = snd_soc_rtd_to_cpu(rtd, 0);
631218887Sdim
632224145Sdim	sdw_stream = snd_soc_dai_get_stream(dai, substream->stream);
633224145Sdim	if (IS_ERR(sdw_stream)) {
634218887Sdim		dev_err(rtd->dev, "no stream found for DAI %s\n", dai->name);
635218887Sdim		return PTR_ERR(sdw_stream);
636218887Sdim	}
637218887Sdim
638218887Sdim	return sdw_deprepare_stream(sdw_stream);
639218887Sdim}
640218887Sdim
641218887Sdimvoid sdw_shutdown(struct snd_pcm_substream *substream)
642218887Sdim{
643218887Sdim	sdw_shutdown_stream(substream);
644218887Sdim}
645218887Sdim
646218887Sdimstatic const struct snd_soc_ops sdw_ops = {
647224145Sdim	.startup = sdw_startup,
648224145Sdim	.prepare = sdw_prepare,
649224145Sdim	.trigger = sdw_trigger,
650224145Sdim	.hw_params = sdw_hw_params,
651224145Sdim	.hw_free = sdw_hw_free,
652218887Sdim	.shutdown = sdw_shutdown,
653218887Sdim};
654218887Sdim
655234353Sdimstatic struct sof_sdw_codec_info codec_info_list[] = {
656218887Sdim	{
657224145Sdim		.part_id = 0x700,
658224145Sdim		.dais = {
659224145Sdim			{
660224145Sdim				.direction = {true, true},
661224145Sdim				.dai_name = "rt700-aif1",
662224145Sdim				.dai_type = SOF_SDW_DAI_TYPE_JACK,
663224145Sdim				.dailink = {SDW_JACK_OUT_DAI_ID, SDW_JACK_IN_DAI_ID},
664224145Sdim				.rtd_init = rt700_rtd_init,
665263508Sdim			},
666218887Sdim		},
667218887Sdim		.dai_num = 1,
668218887Sdim	},
669219077Sdim	{
670243830Sdim		.part_id = 0x711,
671243830Sdim		.version_id = 3,
672224145Sdim		.dais = {
673263508Sdim			{
674263508Sdim				.direction = {true, true},
675263508Sdim				.dai_name = "rt711-sdca-aif1",
676263508Sdim				.dai_type = SOF_SDW_DAI_TYPE_JACK,
677263508Sdim				.dailink = {SDW_JACK_OUT_DAI_ID, SDW_JACK_IN_DAI_ID},
678263508Sdim				.init = sof_sdw_rt_sdca_jack_init,
679263508Sdim				.exit = sof_sdw_rt_sdca_jack_exit,
680263508Sdim				.rtd_init = rt_sdca_jack_rtd_init,
681263508Sdim			},
682263508Sdim		},
683263508Sdim		.dai_num = 1,
684263508Sdim	},
685263508Sdim	{
686224145Sdim		.part_id = 0x711,
687263508Sdim		.version_id = 2,
688224145Sdim		.dais = {
689218887Sdim			{
690218887Sdim				.direction = {true, true},
691218887Sdim				.dai_name = "rt711-aif1",
692234353Sdim				.dai_type = SOF_SDW_DAI_TYPE_JACK,
693224145Sdim				.dailink = {SDW_JACK_OUT_DAI_ID, SDW_JACK_IN_DAI_ID},
694224145Sdim				.init = sof_sdw_rt711_init,
695218887Sdim				.exit = sof_sdw_rt711_exit,
696218887Sdim				.rtd_init = rt711_rtd_init,
697218887Sdim			},
698218887Sdim		},
699218887Sdim		.dai_num = 1,
700249423Sdim	},
701234353Sdim	{
702234353Sdim		.part_id = 0x712,
703234353Sdim		.version_id = 3,
704234353Sdim		.dais =	{
705218887Sdim			{
706263508Sdim				.direction = {true, true},
707219077Sdim				.dai_name = "rt712-sdca-aif1",
708218887Sdim				.dai_type = SOF_SDW_DAI_TYPE_JACK,
709234353Sdim				.dailink = {SDW_JACK_OUT_DAI_ID, SDW_JACK_IN_DAI_ID},
710218887Sdim				.init = sof_sdw_rt_sdca_jack_init,
711224145Sdim				.exit = sof_sdw_rt_sdca_jack_exit,
712224145Sdim				.rtd_init = rt_sdca_jack_rtd_init,
713224145Sdim			},
714218887Sdim			{
715218887Sdim				.direction = {true, false},
716218887Sdim				.dai_name = "rt712-sdca-aif2",
717226633Sdim				.dai_type = SOF_SDW_DAI_TYPE_AMP,
718218887Sdim				.dailink = {SDW_AMP_OUT_DAI_ID, SDW_UNUSED_DAI_ID},
719218887Sdim				.rtd_init = rt712_spk_rtd_init,
720218887Sdim			},
721243830Sdim		},
722218887Sdim		.dai_num = 2,
723234353Sdim	},
724218887Sdim	{
725218887Sdim		.part_id = 0x1712,
726218887Sdim		.version_id = 3,
727218887Sdim		.dais =	{
728218887Sdim			{
729218887Sdim				.direction = {false, true},
730218887Sdim				.dai_name = "rt712-sdca-dmic-aif1",
731218887Sdim				.dai_type = SOF_SDW_DAI_TYPE_MIC,
732218887Sdim				.dailink = {SDW_UNUSED_DAI_ID, SDW_DMIC_DAI_ID},
733218887Sdim				.rtd_init = rt712_sdca_dmic_rtd_init,
734218887Sdim			},
735218887Sdim		},
736218887Sdim		.dai_num = 1,
737218887Sdim	},
738218887Sdim	{
739218887Sdim		.part_id = 0x713,
740218887Sdim		.version_id = 3,
741218887Sdim		.dais =	{
742218887Sdim			{
743218887Sdim				.direction = {true, true},
744218887Sdim				.dai_name = "rt712-sdca-aif1",
745218887Sdim				.dai_type = SOF_SDW_DAI_TYPE_JACK,
746218887Sdim				.dailink = {SDW_JACK_OUT_DAI_ID, SDW_JACK_IN_DAI_ID},
747218887Sdim				.init = sof_sdw_rt_sdca_jack_init,
748218887Sdim				.exit = sof_sdw_rt_sdca_jack_exit,
749224145Sdim				.rtd_init = rt_sdca_jack_rtd_init,
750218887Sdim			},
751218887Sdim		},
752218887Sdim		.dai_num = 1,
753218887Sdim	},
754218887Sdim	{
755218887Sdim		.part_id = 0x1713,
756218887Sdim		.version_id = 3,
757218887Sdim		.dais =	{
758218887Sdim			{
759218887Sdim				.direction = {false, true},
760218887Sdim				.dai_name = "rt712-sdca-dmic-aif1",
761234353Sdim				.dai_type = SOF_SDW_DAI_TYPE_MIC,
762234353Sdim				.dailink = {SDW_UNUSED_DAI_ID, SDW_DMIC_DAI_ID},
763234353Sdim				.rtd_init = rt712_sdca_dmic_rtd_init,
764234353Sdim			},
765218887Sdim		},
766263508Sdim		.dai_num = 1,
767219077Sdim	},
768218887Sdim	{
769234353Sdim		.part_id = 0x1308,
770218887Sdim		.acpi_id = "10EC1308",
771218887Sdim		.dais = {
772224145Sdim			{
773224145Sdim				.direction = {true, false},
774218887Sdim				.dai_name = "rt1308-aif",
775218887Sdim				.dai_type = SOF_SDW_DAI_TYPE_AMP,
776218887Sdim				.dailink = {SDW_AMP_OUT_DAI_ID, SDW_UNUSED_DAI_ID},
777218887Sdim				.init = sof_sdw_rt_amp_init,
778218887Sdim				.exit = sof_sdw_rt_amp_exit,
779218887Sdim				.rtd_init = rt_amp_spk_rtd_init,
780218887Sdim			},
781226633Sdim		},
782218887Sdim		.dai_num = 1,
783218887Sdim		.ops = &sof_sdw_rt1308_i2s_ops,
784218887Sdim	},
785243830Sdim	{
786218887Sdim		.part_id = 0x1316,
787218887Sdim		.dais = {
788218887Sdim			{
789218887Sdim				.direction = {true, true},
790218887Sdim				.dai_name = "rt1316-aif",
791218887Sdim				.dai_type = SOF_SDW_DAI_TYPE_AMP,
792221345Sdim				.dailink = {SDW_AMP_OUT_DAI_ID, SDW_AMP_IN_DAI_ID},
793234353Sdim				.init = sof_sdw_rt_amp_init,
794221345Sdim				.exit = sof_sdw_rt_amp_exit,
795221345Sdim				.rtd_init = rt_amp_spk_rtd_init,
796221345Sdim			},
797221345Sdim		},
798221345Sdim		.dai_num = 1,
799221345Sdim	},
800221345Sdim	{
801221345Sdim		.part_id = 0x1318,
802221345Sdim		.dais = {
803221345Sdim			{
804221345Sdim				.direction = {true, true},
805221345Sdim				.dai_name = "rt1318-aif",
806221345Sdim				.dai_type = SOF_SDW_DAI_TYPE_AMP,
807221345Sdim				.dailink = {SDW_AMP_OUT_DAI_ID, SDW_AMP_IN_DAI_ID},
808221345Sdim				.init = sof_sdw_rt_amp_init,
809221345Sdim				.exit = sof_sdw_rt_amp_exit,
810221345Sdim				.rtd_init = rt_amp_spk_rtd_init,
811221345Sdim			},
812234353Sdim		},
813263508Sdim		.dai_num = 1,
814263508Sdim	},
815263508Sdim	{
816249423Sdim		.part_id = 0x714,
817218887Sdim		.version_id = 3,
818218887Sdim		.ignore_pch_dmic = true,
819218887Sdim		.dais = {
820218887Sdim			{
821218887Sdim				.direction = {false, true},
822218887Sdim				.dai_name = "rt715-aif2",
823249423Sdim				.dai_type = SOF_SDW_DAI_TYPE_MIC,
824218887Sdim				.dailink = {SDW_UNUSED_DAI_ID, SDW_DMIC_DAI_ID},
825218887Sdim				.rtd_init = rt715_sdca_rtd_init,
826218887Sdim			},
827218887Sdim		},
828218887Sdim		.dai_num = 1,
829218887Sdim	},
830218887Sdim	{
831218887Sdim		.part_id = 0x715,
832218887Sdim		.version_id = 3,
833218887Sdim		.ignore_pch_dmic = true,
834234353Sdim		.dais = {
835263508Sdim			{
836263508Sdim				.direction = {false, true},
837263508Sdim				.dai_name = "rt715-aif2",
838263508Sdim				.dai_type = SOF_SDW_DAI_TYPE_MIC,
839263508Sdim				.dailink = {SDW_UNUSED_DAI_ID, SDW_DMIC_DAI_ID},
840263508Sdim				.rtd_init = rt715_sdca_rtd_init,
841263508Sdim			},
842263508Sdim		},
843263508Sdim		.dai_num = 1,
844263508Sdim	},
845263508Sdim	{
846263508Sdim		.part_id = 0x714,
847263508Sdim		.version_id = 2,
848263508Sdim		.ignore_pch_dmic = true,
849218887Sdim		.dais = {
850218887Sdim			{
851218887Sdim				.direction = {false, true},
852218887Sdim				.dai_name = "rt715-aif2",
853218887Sdim				.dai_type = SOF_SDW_DAI_TYPE_MIC,
854243830Sdim				.dailink = {SDW_UNUSED_DAI_ID, SDW_DMIC_DAI_ID},
855218887Sdim				.rtd_init = rt715_rtd_init,
856218887Sdim			},
857226633Sdim		},
858218887Sdim		.dai_num = 1,
859226633Sdim	},
860218887Sdim	{
861226633Sdim		.part_id = 0x715,
862218887Sdim		.version_id = 2,
863243830Sdim		.ignore_pch_dmic = true,
864218887Sdim		.dais = {
865226633Sdim			{
866218887Sdim				.direction = {false, true},
867218887Sdim				.dai_name = "rt715-aif2",
868218887Sdim				.dai_type = SOF_SDW_DAI_TYPE_MIC,
869218887Sdim				.dailink = {SDW_UNUSED_DAI_ID, SDW_DMIC_DAI_ID},
870218887Sdim				.rtd_init = rt715_rtd_init,
871218887Sdim			},
872218887Sdim		},
873218887Sdim		.dai_num = 1,
874218887Sdim	},
875218887Sdim	{
876218887Sdim		.part_id = 0x722,
877218887Sdim		.version_id = 3,
878226633Sdim		.dais = {
879218887Sdim			{
880218887Sdim				.direction = {true, true},
881226633Sdim				.dai_name = "rt722-sdca-aif1",
882218887Sdim				.dai_type = SOF_SDW_DAI_TYPE_JACK,
883218887Sdim				.dailink = {SDW_JACK_OUT_DAI_ID, SDW_JACK_IN_DAI_ID},
884226633Sdim				.init = sof_sdw_rt_sdca_jack_init,
885218887Sdim				.exit = sof_sdw_rt_sdca_jack_exit,
886218887Sdim			},
887226633Sdim			{
888218887Sdim				.direction = {true, false},
889218887Sdim				.dai_name = "rt722-sdca-aif2",
890218887Sdim				.dai_type = SOF_SDW_DAI_TYPE_AMP,
891218887Sdim				/* No feedback capability is provided by rt722-sdca codec driver*/
892218887Sdim				.dailink = {SDW_AMP_OUT_DAI_ID, SDW_UNUSED_DAI_ID},
893218887Sdim				.init = sof_sdw_rt722_spk_init,
894218887Sdim			},
895218887Sdim			{
896218887Sdim				.direction = {false, true},
897218887Sdim				.dai_name = "rt722-sdca-aif3",
898221345Sdim				.dai_type = SOF_SDW_DAI_TYPE_MIC,
899221345Sdim				.dailink = {SDW_UNUSED_DAI_ID, SDW_DMIC_DAI_ID},
900234353Sdim				.init = sof_sdw_rt722_sdca_dmic_init,
901218887Sdim			},
902221345Sdim		},
903221345Sdim		.dai_num = 3,
904224145Sdim	},
905224145Sdim	{
906218887Sdim		.part_id = 0x8373,
907234353Sdim		.dais = {
908234353Sdim			{
909218887Sdim				.direction = {true, true},
910218887Sdim				.dai_name = "max98373-aif1",
911234353Sdim				.dai_type = SOF_SDW_DAI_TYPE_AMP,
912224145Sdim				.dailink = {SDW_AMP_OUT_DAI_ID, SDW_AMP_IN_DAI_ID},
913224145Sdim				.init = sof_sdw_maxim_init,
914218887Sdim				.rtd_init = maxim_spk_rtd_init,
915221345Sdim			},
916234353Sdim		},
917221345Sdim		.dai_num = 1,
918221345Sdim	},
919221345Sdim	{
920239462Sdim		.part_id = 0x8363,
921234353Sdim		.dais = {
922218887Sdim			{
923239462Sdim				.direction = {true, false},
924221345Sdim				.dai_name = "max98363-aif1",
925218887Sdim				.dai_type = SOF_SDW_DAI_TYPE_AMP,
926218887Sdim				.dailink = {SDW_AMP_OUT_DAI_ID, SDW_UNUSED_DAI_ID},
927218887Sdim				.init = sof_sdw_maxim_init,
928223017Sdim				.rtd_init = maxim_spk_rtd_init,
929221345Sdim			},
930221345Sdim		},
931221345Sdim		.dai_num = 1,
932221345Sdim	},
933221345Sdim	{
934221345Sdim		.part_id = 0x5682,
935221345Sdim		.dais = {
936221345Sdim			{
937234353Sdim				.direction = {true, true},
938221345Sdim				.dai_name = "rt5682-sdw",
939221345Sdim				.dai_type = SOF_SDW_DAI_TYPE_JACK,
940221345Sdim				.dailink = {SDW_JACK_OUT_DAI_ID, SDW_JACK_IN_DAI_ID},
941221345Sdim				.rtd_init = rt5682_rtd_init,
942221345Sdim			},
943221345Sdim		},
944221345Sdim		.dai_num = 1,
945223017Sdim	},
946224145Sdim	{
947224145Sdim		.part_id = 0x3556,
948218887Sdim		.dais = {
949224145Sdim			{
950218887Sdim				.direction = {true, true},
951218887Sdim				.dai_name = "cs35l56-sdw1",
952218887Sdim				.dai_type = SOF_SDW_DAI_TYPE_AMP,
953223017Sdim				.dailink = {SDW_AMP_OUT_DAI_ID, SDW_AMP_IN_DAI_ID},
954223017Sdim				.init = sof_sdw_cs_amp_init,
955221345Sdim				.rtd_init = cs_spk_rtd_init,
956223017Sdim			},
957223017Sdim		},
958223017Sdim		.dai_num = 1,
959249423Sdim	},
960223017Sdim	{
961223017Sdim		.part_id = 0x4242,
962249423Sdim		.dais = {
963221345Sdim			{
964221345Sdim				.direction = {true, true},
965249423Sdim				.dai_name = "cs42l42-sdw",
966221345Sdim				.dai_type = SOF_SDW_DAI_TYPE_JACK,
967221345Sdim				.dailink = {SDW_JACK_OUT_DAI_ID, SDW_JACK_IN_DAI_ID},
968223017Sdim				.rtd_init = cs42l42_rtd_init,
969221345Sdim			},
970234353Sdim		},
971223017Sdim		.dai_num = 1,
972223017Sdim	},
973223017Sdim	{
974243830Sdim		.part_id = 0x4243,
975243830Sdim		.codec_name = "cs42l43-codec",
976234353Sdim		.dais = {
977221345Sdim			{
978221345Sdim				.direction = {true, false},
979223017Sdim				.dai_name = "cs42l43-dp5",
980223017Sdim				.dai_type = SOF_SDW_DAI_TYPE_JACK,
981223017Sdim				.dailink = {SDW_JACK_OUT_DAI_ID, SDW_UNUSED_DAI_ID},
982234353Sdim				.rtd_init = cs42l43_hs_rtd_init,
983218887Sdim			},
984223017Sdim			{
985263508Sdim				.direction = {false, true},
986263508Sdim				.dai_name = "cs42l43-dp1",
987223017Sdim				.dai_type = SOF_SDW_DAI_TYPE_MIC,
988223017Sdim				.dailink = {SDW_UNUSED_DAI_ID, SDW_DMIC_DAI_ID},
989223017Sdim				.rtd_init = cs42l43_dmic_rtd_init,
990223017Sdim			},
991263508Sdim			{
992263508Sdim				.direction = {false, true},
993263508Sdim				.dai_name = "cs42l43-dp2",
994263508Sdim				.dai_type = SOF_SDW_DAI_TYPE_JACK,
995263508Sdim				.dailink = {SDW_UNUSED_DAI_ID, SDW_JACK_IN_DAI_ID},
996263508Sdim			},
997263508Sdim		},
998263508Sdim		.dai_num = 3,
999223017Sdim	},
1000218887Sdim	{
1001218887Sdim		.part_id = 0xaaaa, /* generic codec mockup */
1002218887Sdim		.version_id = 0,
1003218887Sdim		.dais = {
1004219077Sdim			{
1005234353Sdim				.direction = {true, true},
1006234353Sdim				.dai_name = "sdw-mockup-aif1",
1007234353Sdim				.dai_type = SOF_SDW_DAI_TYPE_JACK,
1008218887Sdim				.dailink = {SDW_JACK_OUT_DAI_ID, SDW_JACK_IN_DAI_ID},
1009218887Sdim				.init = NULL,
1010218887Sdim			},
1011234353Sdim		},
1012223017Sdim		.dai_num = 1,
1013221345Sdim	},
1014218887Sdim	{
1015218887Sdim		.part_id = 0xaa55, /* headset codec mockup */
1016221345Sdim		.version_id = 0,
1017234353Sdim		.dais = {
1018234353Sdim			{
1019234353Sdim				.direction = {true, true},
1020221345Sdim				.dai_name = "sdw-mockup-aif1",
1021221345Sdim				.dai_type = SOF_SDW_DAI_TYPE_JACK,
1022221345Sdim				.dailink = {SDW_JACK_OUT_DAI_ID, SDW_JACK_IN_DAI_ID},
1023234353Sdim				.init = NULL,
1024221345Sdim			},
1025221345Sdim		},
1026221345Sdim		.dai_num = 1,
1027221345Sdim	},
1028219077Sdim	{
1029234353Sdim		.part_id = 0x55aa, /* amplifier mockup */
1030234353Sdim		.version_id = 0,
1031234353Sdim		.dais = {
1032218887Sdim			{
1033218887Sdim				.direction = {true, true},
1034218887Sdim				.dai_name = "sdw-mockup-aif1",
1035234353Sdim				.dai_type = SOF_SDW_DAI_TYPE_AMP,
1036223017Sdim				.dailink = {SDW_AMP_OUT_DAI_ID, SDW_AMP_IN_DAI_ID},
1037221345Sdim				.init = NULL,
1038218887Sdim			},
1039218887Sdim		},
1040219077Sdim		.dai_num = 1,
1041234353Sdim	},
1042234353Sdim	{
1043234353Sdim		.part_id = 0x5555,
1044218887Sdim		.version_id = 0,
1045221345Sdim		.dais = {
1046221345Sdim			{
1047218887Sdim				.dai_name = "sdw-mockup-aif1",
1048218887Sdim				.direction = {false, true},
1049219077Sdim				.dai_type = SOF_SDW_DAI_TYPE_MIC,
1050234353Sdim				.dailink = {SDW_UNUSED_DAI_ID, SDW_DMIC_DAI_ID},
1051234353Sdim				.init = NULL,
1052234353Sdim			},
1053218887Sdim		},
1054224145Sdim		.dai_num = 1,
1055224145Sdim	},
1056218887Sdim};
1057218887Sdim
1058218887Sdimstatic inline int find_codec_info_part(const u64 adr)
1059218887Sdim{
1060234353Sdim	unsigned int part_id, sdw_version;
1061218887Sdim	int i;
1062218887Sdim
1063218887Sdim	part_id = SDW_PART_ID(adr);
1064234353Sdim	sdw_version = SDW_VERSION(adr);
1065234353Sdim	for (i = 0; i < ARRAY_SIZE(codec_info_list); i++)
1066218887Sdim		/*
1067218887Sdim		 * A codec info is for all sdw version with the part id if
1068234353Sdim		 * version_id is not specified in the codec info.
1069218887Sdim		 */
1070218887Sdim		if (part_id == codec_info_list[i].part_id &&
1071218887Sdim		    (!codec_info_list[i].version_id ||
1072218887Sdim		     sdw_version == codec_info_list[i].version_id))
1073218887Sdim			return i;
1074218887Sdim
1075218887Sdim	return -EINVAL;
1076234353Sdim
1077234353Sdim}
1078218887Sdim
1079218887Sdimstatic inline int find_codec_info_acpi(const u8 *acpi_id)
1080218887Sdim{
1081218887Sdim	int i;
1082218887Sdim
1083218887Sdim	if (!acpi_id[0])
1084218887Sdim		return -EINVAL;
1085218887Sdim
1086218887Sdim	for (i = 0; i < ARRAY_SIZE(codec_info_list); i++)
1087234353Sdim		if (!memcmp(codec_info_list[i].acpi_id, acpi_id, ACPI_ID_LEN))
1088249423Sdim			return i;
1089234353Sdim
1090249423Sdim	return -EINVAL;
1091218887Sdim}
1092218887Sdim
1093218887Sdim/*
1094234353Sdim * get BE dailink number and CPU DAI number based on sdw link adr.
1095218887Sdim * Since some sdw slaves may be aggregated, the CPU DAI number
1096218887Sdim * may be larger than the number of BE dailinks.
1097224145Sdim */
1098218887Sdimstatic int get_dailink_info(struct device *dev,
1099218887Sdim			    const struct snd_soc_acpi_link_adr *adr_link,
1100218887Sdim			    int *sdw_be_num, int *codecs_num)
1101218887Sdim{
1102218887Sdim	bool group_visited[SDW_MAX_GROUPS];
1103234353Sdim	bool no_aggregation;
1104234353Sdim	int i;
1105234353Sdim	int j;
1106218887Sdim
1107218887Sdim	no_aggregation = sof_sdw_quirk & SOF_SDW_NO_AGGREGATION;
1108218887Sdim	*sdw_be_num  = 0;
1109218887Sdim
1110218887Sdim	if (!adr_link)
1111218887Sdim		return -EINVAL;
1112218887Sdim
1113218887Sdim	for (i = 0; i < SDW_MAX_GROUPS; i++)
1114218887Sdim		group_visited[i] = false;
1115218887Sdim
1116243830Sdim	for (; adr_link->num_adr; adr_link++) {
1117234353Sdim		const struct snd_soc_acpi_endpoint *endpoint;
1118218887Sdim		struct sof_sdw_codec_info *codec_info;
1119218887Sdim		int codec_index;
1120218887Sdim		int stream;
1121218887Sdim		u64 adr;
1122218887Sdim
1123218887Sdim		/* make sure the link mask has a single bit set */
1124219077Sdim		if (!is_power_of_2(adr_link->mask))
1125219077Sdim			return -EINVAL;
1126234353Sdim
1127234353Sdim		for (i = 0; i < adr_link->num_adr; i++) {
1128234353Sdim			adr = adr_link->adr_d[i].adr;
1129218887Sdim			codec_index = find_codec_info_part(adr);
1130219077Sdim			if (codec_index < 0)
1131219077Sdim				return codec_index;
1132219077Sdim
1133219077Sdim			codec_info = &codec_info_list[codec_index];
1134219077Sdim
1135234353Sdim			*codecs_num += codec_info->dai_num;
1136234353Sdim
1137234353Sdim			if (!adr_link->adr_d[i].name_prefix) {
1138219077Sdim				dev_err(dev, "codec 0x%llx does not have a name prefix\n",
1139219077Sdim					adr_link->adr_d[i].adr);
1140219077Sdim				return -EINVAL;
1141219077Sdim			}
1142219077Sdim
1143219077Sdim			endpoint = adr_link->adr_d[i].endpoints;
1144224145Sdim			if (endpoint->aggregated && !endpoint->group_id) {
1145234353Sdim				dev_err(dev, "invalid group id on link %x\n",
1146234353Sdim					adr_link->mask);
1147224145Sdim				return -EINVAL;
1148224145Sdim			}
1149224145Sdim
1150234353Sdim			for (j = 0; j < codec_info->dai_num; j++) {
1151224145Sdim				/* count DAI number for playback and capture */
1152234353Sdim				for_each_pcm_streams(stream) {
1153224145Sdim					if (!codec_info->dais[j].direction[stream])
1154224145Sdim						continue;
1155224145Sdim
1156224145Sdim					/* count BE for each non-aggregated slave or group */
1157224145Sdim					if (!endpoint->aggregated || no_aggregation ||
1158224145Sdim					    !group_visited[endpoint->group_id])
1159224145Sdim						(*sdw_be_num)++;
1160234353Sdim				}
1161224145Sdim			}
1162224145Sdim
1163224145Sdim			if (endpoint->aggregated)
1164224145Sdim				group_visited[endpoint->group_id] = true;
1165224145Sdim		}
1166224145Sdim	}
1167224145Sdim
1168224145Sdim	return 0;
1169224145Sdim}
1170224145Sdim
1171224145Sdimstatic void init_dai_link(struct device *dev, struct snd_soc_dai_link *dai_links,
1172224145Sdim			  int *be_id, char *name, int playback, int capture,
1173218887Sdim			  struct snd_soc_dai_link_component *cpus, int cpus_num,
1174234353Sdim			  struct snd_soc_dai_link_component *codecs, int codecs_num,
1175218887Sdim			  int (*init)(struct snd_soc_pcm_runtime *rtd),
1176218887Sdim			  const struct snd_soc_ops *ops)
1177218887Sdim{
1178224145Sdim	dev_dbg(dev, "create dai link %s, id %d\n", name, *be_id);
1179224145Sdim	dai_links->id = (*be_id)++;
1180218887Sdim	dai_links->name = name;
1181224145Sdim	dai_links->platforms = platform_component;
1182218887Sdim	dai_links->num_platforms = ARRAY_SIZE(platform_component);
1183224145Sdim	dai_links->no_pcm = 1;
1184224145Sdim	dai_links->cpus = cpus;
1185224145Sdim	dai_links->num_cpus = cpus_num;
1186224145Sdim	dai_links->codecs = codecs;
1187219077Sdim	dai_links->num_codecs = codecs_num;
1188224145Sdim	dai_links->dpcm_playback = playback;
1189224145Sdim	dai_links->dpcm_capture = capture;
1190224145Sdim	dai_links->init = init;
1191224145Sdim	dai_links->ops = ops;
1192224145Sdim}
1193224145Sdim
1194224145Sdimstatic int init_simple_dai_link(struct device *dev, struct snd_soc_dai_link *dai_links,
1195224145Sdim				int *be_id, char *name, int playback, int capture,
1196224145Sdim				const char *cpu_dai_name,
1197224145Sdim				const char *codec_name, const char *codec_dai_name,
1198234353Sdim				int (*init)(struct snd_soc_pcm_runtime *rtd),
1199224145Sdim				const struct snd_soc_ops *ops)
1200249423Sdim{
1201249423Sdim	struct snd_soc_dai_link_component *dlc;
1202224145Sdim
1203224145Sdim	/* Allocate two DLCs one for the CPU, one for the CODEC */
1204234353Sdim	dlc = devm_kcalloc(dev, 2, sizeof(*dlc), GFP_KERNEL);
1205224145Sdim	if (!dlc || !name || !cpu_dai_name || !codec_name || !codec_dai_name)
1206224145Sdim		return -ENOMEM;
1207224145Sdim
1208249423Sdim	dlc[0].dai_name = cpu_dai_name;
1209249423Sdim
1210249423Sdim	dlc[1].name = codec_name;
1211219077Sdim	dlc[1].dai_name = codec_dai_name;
1212224145Sdim
1213224145Sdim	init_dai_link(dev, dai_links, be_id, name, playback, capture,
1214224145Sdim		      &dlc[0], 1, &dlc[1], 1, init, ops);
1215224145Sdim
1216224145Sdim	return 0;
1217224145Sdim}
1218219077Sdim
1219219077Sdimstatic bool is_unique_device(const struct snd_soc_acpi_link_adr *adr_link,
1220219077Sdim			     unsigned int sdw_version,
1221224145Sdim			     unsigned int mfg_id,
1222224145Sdim			     unsigned int part_id,
1223224145Sdim			     unsigned int class_id,
1224224145Sdim			     int index_in_link)
1225224145Sdim{
1226243830Sdim	int i;
1227249423Sdim
1228224145Sdim	for (i = 0; i < adr_link->num_adr; i++) {
1229224145Sdim		unsigned int sdw1_version, mfg1_id, part1_id, class1_id;
1230249423Sdim		u64 adr;
1231249423Sdim
1232249423Sdim		/* skip itself */
1233224145Sdim		if (i == index_in_link)
1234224145Sdim			continue;
1235224145Sdim
1236249423Sdim		adr = adr_link->adr_d[i].adr;
1237249423Sdim
1238249423Sdim		sdw1_version = SDW_VERSION(adr);
1239224145Sdim		mfg1_id = SDW_MFG_ID(adr);
1240224145Sdim		part1_id = SDW_PART_ID(adr);
1241224145Sdim		class1_id = SDW_CLASS_ID(adr);
1242224145Sdim
1243224145Sdim		if (sdw_version == sdw1_version &&
1244249423Sdim		    mfg_id == mfg1_id &&
1245224145Sdim		    part_id == part1_id &&
1246224145Sdim		    class_id == class1_id)
1247218887Sdim			return false;
1248224145Sdim	}
1249243830Sdim
1250218887Sdim	return true;
1251224145Sdim}
1252218887Sdim
1253224145Sdimstatic int fill_sdw_codec_dlc(struct device *dev,
1254224145Sdim			      const struct snd_soc_acpi_link_adr *adr_link,
1255234353Sdim			      struct snd_soc_dai_link_component *codec,
1256224145Sdim			      int adr_index, int dai_index)
1257218887Sdim{
1258218887Sdim	unsigned int sdw_version, unique_id, mfg_id, link_id, part_id, class_id;
1259219077Sdim	u64 adr = adr_link->adr_d[adr_index].adr;
1260234353Sdim	int codec_index;
1261234353Sdim
1262234353Sdim	codec_index = find_codec_info_part(adr);
1263218887Sdim	if (codec_index < 0)
1264221345Sdim		return codec_index;
1265221345Sdim
1266221345Sdim	sdw_version = SDW_VERSION(adr);
1267221345Sdim	link_id = SDW_DISCO_LINK_ID(adr);
1268218887Sdim	unique_id = SDW_UNIQUE_ID(adr);
1269218887Sdim	mfg_id = SDW_MFG_ID(adr);
1270219077Sdim	part_id = SDW_PART_ID(adr);
1271234353Sdim	class_id = SDW_CLASS_ID(adr);
1272234353Sdim
1273234353Sdim	if (codec_info_list[codec_index].codec_name)
1274223017Sdim		codec->name = devm_kstrdup(dev,
1275221345Sdim					   codec_info_list[codec_index].codec_name,
1276221345Sdim					   GFP_KERNEL);
1277221345Sdim	else if (is_unique_device(adr_link, sdw_version, mfg_id, part_id,
1278221345Sdim				  class_id, adr_index))
1279219077Sdim		codec->name = devm_kasprintf(dev, GFP_KERNEL,
1280219077Sdim					     "sdw:0:%01x:%04x:%04x:%02x", link_id,
1281219077Sdim					     mfg_id, part_id, class_id);
1282234353Sdim	else
1283234353Sdim		codec->name = devm_kasprintf(dev, GFP_KERNEL,
1284234353Sdim					     "sdw:0:%01x:%04x:%04x:%02x:%01x", link_id,
1285218887Sdim					     mfg_id, part_id, class_id, unique_id);
1286221345Sdim
1287221345Sdim	if (!codec->name)
1288221345Sdim		return -ENOMEM;
1289221345Sdim
1290218887Sdim	codec->dai_name = codec_info_list[codec_index].dais[dai_index].dai_name;
1291218887Sdim
1292221345Sdim	return 0;
1293234353Sdim}
1294234353Sdim
1295234353Sdimstatic int set_codec_init_func(struct snd_soc_card *card,
1296221345Sdim			       const struct snd_soc_acpi_link_adr *adr_link,
1297221345Sdim			       struct snd_soc_dai_link *dai_links,
1298221345Sdim			       bool playback, int group_id, int adr_index, int dai_index)
1299221345Sdim{
1300221345Sdim	int i = adr_index;
1301221345Sdim
1302221345Sdim	do {
1303221345Sdim		/*
1304234353Sdim		 * Initialize the codec. If codec is part of an aggregated
1305234353Sdim		 * group (group_id>0), initialize all codecs belonging to
1306234353Sdim		 * same group.
1307221345Sdim		 * The first link should start with adr_link->adr_d[adr_index]
1308221345Sdim		 * because that is the device that we want to initialize and
1309221345Sdim		 * we should end immediately if it is not aggregated (group_id=0)
1310221345Sdim		 */
1311221345Sdim		for ( ; i < adr_link->num_adr; i++) {
1312221345Sdim			int codec_index;
1313221345Sdim
1314218887Sdim			codec_index = find_codec_info_part(adr_link->adr_d[i].adr);
1315221345Sdim			if (codec_index < 0)
1316221345Sdim				return codec_index;
1317224145Sdim
1318234353Sdim			/* The group_id is > 0 iff the codec is aggregated */
1319234353Sdim			if (adr_link->adr_d[i].endpoints->group_id != group_id)
1320218887Sdim				continue;
1321221345Sdim
1322218887Sdim			if (codec_info_list[codec_index].dais[dai_index].init)
1323234353Sdim				codec_info_list[codec_index].dais[dai_index].init(card,
1324218887Sdim						adr_link,
1325218887Sdim						dai_links,
1326218887Sdim						&codec_info_list[codec_index],
1327218887Sdim						playback);
1328218887Sdim			if (!group_id)
1329218887Sdim				return 0;
1330218887Sdim		}
1331234353Sdim
1332218887Sdim		i = 0;
1333218887Sdim		adr_link++;
1334218887Sdim	} while (adr_link->mask);
1335218887Sdim
1336218887Sdim	return 0;
1337218887Sdim}
1338218887Sdim
1339218887Sdim/*
1340218887Sdim * check endpoint status in slaves and gather link ID for all slaves in
1341218887Sdim * the same group to generate different CPU DAI. Now only support
1342218887Sdim * one sdw link with all slaves set with only single group id.
1343224145Sdim *
1344224145Sdim * one slave on one sdw link with aggregated = 0
1345224145Sdim * one sdw BE DAI <---> one-cpu DAI <---> one-codec DAI
1346224145Sdim *
1347224145Sdim * two or more slaves on one sdw link with aggregated = 0
1348224145Sdim * one sdw BE DAI  <---> one-cpu DAI <---> multi-codec DAIs
1349224145Sdim *
1350224145Sdim * multiple links with multiple slaves with aggregated = 1
1351224145Sdim * one sdw BE DAI  <---> 1 .. N CPU DAIs <----> 1 .. N codec DAIs
1352224145Sdim */
1353224145Sdimstatic int get_slave_info(const struct snd_soc_acpi_link_adr *adr_link,
1354221345Sdim			  struct device *dev, int *cpu_dai_id, int *cpu_dai_num,
1355221345Sdim			  int *codec_num, unsigned int *group_id,
1356221345Sdim			  int adr_index)
1357219077Sdim{
1358234353Sdim	bool no_aggregation = sof_sdw_quirk & SOF_SDW_NO_AGGREGATION;
1359219077Sdim	int i;
1360224145Sdim
1361224145Sdim	if (!adr_link->adr_d[adr_index].endpoints->aggregated || no_aggregation) {
1362224145Sdim		cpu_dai_id[0] = ffs(adr_link->mask) - 1;
1363249423Sdim		*cpu_dai_num = 1;
1364249423Sdim		*codec_num = 1;
1365219077Sdim		*group_id = 0;
1366224145Sdim		return 0;
1367224145Sdim	}
1368224145Sdim
1369234353Sdim	*codec_num = 0;
1370219077Sdim	*cpu_dai_num = 0;
1371224145Sdim	*group_id = adr_link->adr_d[adr_index].endpoints->group_id;
1372224145Sdim
1373224145Sdim	/* Count endpoints with the same group_id in the adr_link */
1374249423Sdim	for (; adr_link && adr_link->num_adr; adr_link++) {
1375249423Sdim		unsigned int link_codecs = 0;
1376249423Sdim
1377224145Sdim		for (i = 0; i < adr_link->num_adr; i++) {
1378224145Sdim			if (adr_link->adr_d[i].endpoints->aggregated &&
1379224145Sdim			    adr_link->adr_d[i].endpoints->group_id == *group_id)
1380224145Sdim				link_codecs++;
1381224145Sdim		}
1382224145Sdim
1383224145Sdim		if (link_codecs) {
1384224145Sdim			*codec_num += link_codecs;
1385224145Sdim
1386224145Sdim			if (*cpu_dai_num >= SDW_MAX_CPU_DAIS) {
1387224145Sdim				dev_err(dev, "cpu_dai_id array overflowed\n");
1388224145Sdim				return -EINVAL;
1389224145Sdim			}
1390224145Sdim
1391224145Sdim			cpu_dai_id[(*cpu_dai_num)++] = ffs(adr_link->mask) - 1;
1392224145Sdim		}
1393224145Sdim	}
1394224145Sdim
1395224145Sdim	return 0;
1396224145Sdim}
1397224145Sdim
1398224145Sdimstatic void set_dailink_map(struct snd_soc_dai_link_ch_map *sdw_codec_ch_maps,
1399224145Sdim			    int codec_num, int cpu_num)
1400224145Sdim{
1401224145Sdim	int step;
1402224145Sdim	int i;
1403249423Sdim
1404224145Sdim	step = codec_num / cpu_num;
1405224145Sdim	for (i = 0; i < codec_num; i++) {
1406224145Sdim		sdw_codec_ch_maps[i].cpu	= i / step;
1407224145Sdim		sdw_codec_ch_maps[i].codec	= i;
1408224145Sdim	}
1409224145Sdim}
1410224145Sdim
1411224145Sdimstatic inline int find_codec_info_dai(const char *dai_name, int *dai_index)
1412224145Sdim{
1413224145Sdim	int i, j;
1414224145Sdim
1415224145Sdim	for (i = 0; i < ARRAY_SIZE(codec_info_list); i++) {
1416239462Sdim		for (j = 0; j < codec_info_list[i].dai_num; j++) {
1417239462Sdim			if (!strcmp(codec_info_list[i].dais[j].dai_name, dai_name)) {
1418239462Sdim				*dai_index = j;
1419239462Sdim				return i;
1420239462Sdim			}
1421239462Sdim		}
1422239462Sdim	}
1423239462Sdim
1424239462Sdim	return -EINVAL;
1425239462Sdim}
1426239462Sdim
1427239462Sdimstatic int sof_sdw_rtd_init(struct snd_soc_pcm_runtime *rtd)
1428239462Sdim{
1429239462Sdim	struct sof_sdw_codec_info *codec_info;
1430239462Sdim	struct snd_soc_dai *dai;
1431239462Sdim	int codec_index;
1432239462Sdim	int dai_index;
1433239462Sdim	int ret;
1434249423Sdim	int i;
1435224145Sdim
1436224145Sdim	for_each_rtd_codec_dais(rtd, i, dai) {
1437224145Sdim		codec_index = find_codec_info_dai(dai->name, &dai_index);
1438224145Sdim		if (codec_index < 0)
1439224145Sdim			return -EINVAL;
1440224145Sdim
1441224145Sdim		codec_info = &codec_info_list[codec_index];
1442224145Sdim		/*
1443224145Sdim		 * A codec dai can be connected to different dai links for capture and playback,
1444224145Sdim		 * but we only need to call the rtd_init function once.
1445224145Sdim		 * The rtd_init for each codec dai is independent. So, the order of rtd_init
1446224145Sdim		 * doesn't matter.
1447224145Sdim		 */
1448224145Sdim		if (codec_info->dais[dai_index].rtd_init_done)
1449224145Sdim			continue;
1450224145Sdim		if (codec_info->dais[dai_index].rtd_init) {
1451224145Sdim			ret = codec_info->dais[dai_index].rtd_init(rtd);
1452249423Sdim			if (ret)
1453224145Sdim				return ret;
1454224145Sdim		}
1455224145Sdim		codec_info->dais[dai_index].rtd_init_done = true;
1456224145Sdim	}
1457224145Sdim
1458224145Sdim	return 0;
1459249423Sdim}
1460249423Sdim
1461224145Sdimstatic const char * const type_strings[] = {"SimpleJack", "SmartAmp", "SmartMic"};
1462224145Sdim
1463224145Sdimstatic int create_sdw_dailink(struct snd_soc_card *card, int *link_index,
1464224145Sdim			      struct snd_soc_dai_link *dai_links, int sdw_be_num,
1465224145Sdim			      const struct snd_soc_acpi_link_adr *adr_link,
1466224145Sdim			      struct snd_soc_codec_conf *codec_conf,
1467224145Sdim			      int codec_count, int *be_id,
1468224145Sdim			      int *codec_conf_index,
1469224145Sdim			      bool *ignore_pch_dmic,
1470224145Sdim			      bool append_dai_type,
1471249423Sdim			      int adr_index,
1472249423Sdim			      int dai_index)
1473224145Sdim{
1474224145Sdim	struct mc_private *ctx = snd_soc_card_get_drvdata(card);
1475224145Sdim	struct device *dev = card->dev;
1476224145Sdim	const struct snd_soc_acpi_link_adr *adr_link_next;
1477224145Sdim	struct snd_soc_dai_link_component *codecs;
1478224145Sdim	struct snd_soc_dai_link_component *cpus;
1479224145Sdim	struct sof_sdw_codec_info *codec_info;
1480224145Sdim	int cpu_dai_id[SDW_MAX_CPU_DAIS];
1481224145Sdim	int cpu_dai_num;
1482224145Sdim	unsigned int group_id;
1483219077Sdim	int codec_dlc_index = 0;
1484219077Sdim	int codec_index;
1485224145Sdim	int codec_num;
1486224145Sdim	int stream;
1487224145Sdim	int i = 0;
1488224145Sdim	int j, k;
1489224145Sdim	int ret;
1490224145Sdim
1491224145Sdim	ret = get_slave_info(adr_link, dev, cpu_dai_id, &cpu_dai_num, &codec_num,
1492221345Sdim			     &group_id, adr_index);
1493221345Sdim	if (ret)
1494221345Sdim		return ret;
1495221345Sdim
1496224145Sdim	codecs = devm_kcalloc(dev, codec_num, sizeof(*codecs), GFP_KERNEL);
1497224145Sdim	if (!codecs)
1498221345Sdim		return -ENOMEM;
1499221345Sdim
1500221345Sdim	/* generate codec name on different links in the same group */
1501221345Sdim	j = adr_index;
1502249423Sdim	for (adr_link_next = adr_link; adr_link_next && adr_link_next->num_adr &&
1503249423Sdim	     i < cpu_dai_num; adr_link_next++) {
1504221345Sdim		/* skip the link excluded by this processed group */
1505224145Sdim		if (cpu_dai_id[i] != ffs(adr_link_next->mask) - 1)
1506224145Sdim			continue;
1507224145Sdim
1508224145Sdim		/* j reset after loop, adr_index only applies to first link */
1509224145Sdim		for (; j < adr_link_next->num_adr && codec_dlc_index < codec_num; j++) {
1510224145Sdim			const struct snd_soc_acpi_endpoint *endpoints;
1511221345Sdim
1512224145Sdim			endpoints = adr_link_next->adr_d[j].endpoints;
1513224145Sdim
1514224145Sdim			if (group_id && (!endpoints->aggregated ||
1515221345Sdim					 endpoints->group_id != group_id))
1516224145Sdim				continue;
1517224145Sdim
1518224145Sdim			/* sanity check */
1519224145Sdim			if (*codec_conf_index >= codec_count) {
1520224145Sdim				dev_err(dev, "codec_conf array overflowed\n");
1521224145Sdim				return -EINVAL;
1522224145Sdim			}
1523224145Sdim
1524249423Sdim			ret = fill_sdw_codec_dlc(dev, adr_link_next,
1525224145Sdim						 &codecs[codec_dlc_index],
1526224145Sdim						 j, dai_index);
1527224145Sdim			if (ret)
1528224145Sdim				return ret;
1529224145Sdim
1530224145Sdim			codec_conf[*codec_conf_index].dlc = codecs[codec_dlc_index];
1531249423Sdim			codec_conf[*codec_conf_index].name_prefix =
1532224145Sdim					adr_link_next->adr_d[j].name_prefix;
1533224145Sdim
1534224145Sdim			codec_dlc_index++;
1535224145Sdim			(*codec_conf_index)++;
1536224145Sdim		}
1537224145Sdim		j = 0;
1538224145Sdim
1539224145Sdim		/* check next link to create codec dai in the processed group */
1540224145Sdim		i++;
1541224145Sdim	}
1542224145Sdim
1543249423Sdim	/* find codec info to create BE DAI */
1544249423Sdim	codec_index = find_codec_info_part(adr_link->adr_d[adr_index].adr);
1545224145Sdim	if (codec_index < 0)
1546224145Sdim		return codec_index;
1547224145Sdim	codec_info = &codec_info_list[codec_index];
1548224145Sdim
1549224145Sdim	if (codec_info->ignore_pch_dmic)
1550224145Sdim		*ignore_pch_dmic = true;
1551224145Sdim
1552224145Sdim	for_each_pcm_streams(stream) {
1553224145Sdim		struct snd_soc_dai_link_ch_map *sdw_codec_ch_maps;
1554224145Sdim		char *name, *cpu_name;
1555221345Sdim		int playback, capture;
1556221345Sdim		static const char * const sdw_stream_name[] = {
1557224145Sdim			"SDW%d-Playback",
1558224145Sdim			"SDW%d-Capture",
1559218887Sdim			"SDW%d-Playback-%s",
1560218887Sdim			"SDW%d-Capture-%s",
1561224145Sdim		};
1562224145Sdim
1563218887Sdim		if (!codec_info->dais[dai_index].direction[stream])
1564218887Sdim			continue;
1565249423Sdim
1566249423Sdim		*be_id = codec_info->dais[dai_index].dailink[stream];
1567224145Sdim		if (*be_id < 0) {
1568218887Sdim			dev_err(dev, "Invalid dailink id %d\n", *be_id);
1569224145Sdim			return -EINVAL;
1570224145Sdim		}
1571224145Sdim
1572249423Sdim		sdw_codec_ch_maps = devm_kcalloc(dev, codec_num,
1573224145Sdim						 sizeof(*sdw_codec_ch_maps), GFP_KERNEL);
1574224145Sdim		if (!sdw_codec_ch_maps)
1575224145Sdim			return -ENOMEM;
1576224145Sdim
1577224145Sdim		/* create stream name according to first link id */
1578224145Sdim		if (append_dai_type) {
1579224145Sdim			name = devm_kasprintf(dev, GFP_KERNEL,
1580224145Sdim					      sdw_stream_name[stream + 2], cpu_dai_id[0],
1581218887Sdim					      type_strings[codec_info->dais[dai_index].dai_type]);
1582224145Sdim		} else {
1583249423Sdim			name = devm_kasprintf(dev, GFP_KERNEL,
1584224145Sdim					      sdw_stream_name[stream], cpu_dai_id[0]);
1585224145Sdim		}
1586224145Sdim		if (!name)
1587224145Sdim			return -ENOMEM;
1588224145Sdim
1589224145Sdim		cpus = devm_kcalloc(dev, cpu_dai_num, sizeof(*cpus), GFP_KERNEL);
1590224145Sdim		if (!cpus)
1591224145Sdim			return -ENOMEM;
1592224145Sdim
1593224145Sdim		/*
1594224145Sdim		 * generate CPU DAI name base on the sdw link ID and
1595224145Sdim		 * PIN ID with offset of 2 according to sdw dai driver.
1596218887Sdim		 */
1597218887Sdim		for (k = 0; k < cpu_dai_num; k++) {
1598218887Sdim			cpu_name = devm_kasprintf(dev, GFP_KERNEL,
1599218887Sdim						  "SDW%d Pin%d", cpu_dai_id[k],
1600218887Sdim						  ctx->sdw_pin_index[cpu_dai_id[k]]++);
1601263508Sdim			if (!cpu_name)
1602263508Sdim				return -ENOMEM;
1603263508Sdim
1604218887Sdim			cpus[k].dai_name = cpu_name;
1605218887Sdim		}
1606218887Sdim
1607218887Sdim		/*
1608263508Sdim		 * We create sdw dai links at first stage, so link index should
1609263508Sdim		 * not be larger than sdw_be_num
1610218887Sdim		 */
1611263508Sdim		if (*link_index >= sdw_be_num) {
1612263508Sdim			dev_err(dev, "invalid dai link index %d\n", *link_index);
1613263508Sdim			return -EINVAL;
1614263508Sdim		}
1615224145Sdim
1616224145Sdim		playback = (stream == SNDRV_PCM_STREAM_PLAYBACK);
1617224145Sdim		capture = (stream == SNDRV_PCM_STREAM_CAPTURE);
1618224145Sdim
1619224145Sdim		init_dai_link(dev, dai_links + *link_index, be_id, name,
1620224145Sdim			      playback, capture, cpus, cpu_dai_num, codecs, codec_num,
1621224145Sdim			      sof_sdw_rtd_init, &sdw_ops);
1622224145Sdim
1623224145Sdim		/*
1624224145Sdim		 * SoundWire DAILINKs use 'stream' functions and Bank Switch operations
1625218887Sdim		 * based on wait_for_completion(), tag them as 'nonatomic'.
1626218887Sdim		 */
1627224145Sdim		dai_links[*link_index].nonatomic = true;
1628224145Sdim
1629218887Sdim		set_dailink_map(sdw_codec_ch_maps, codec_num, cpu_dai_num);
1630218887Sdim		dai_links[*link_index].ch_maps = sdw_codec_ch_maps;
1631218887Sdim		ret = set_codec_init_func(card, adr_link, dai_links + (*link_index)++,
1632243830Sdim					  playback, group_id, adr_index, dai_index);
1633218887Sdim		if (ret < 0) {
1634218887Sdim			dev_err(dev, "failed to init codec %d\n", codec_index);
1635218887Sdim			return ret;
1636234353Sdim		}
1637218887Sdim	}
1638218887Sdim
1639218887Sdim	return 0;
1640221345Sdim}
1641234353Sdim
1642234353Sdimstatic int sof_card_dai_links_create(struct snd_soc_card *card)
1643234353Sdim{
1644224145Sdim	struct device *dev = card->dev;
1645221345Sdim	struct snd_soc_acpi_mach *mach = dev_get_platdata(card->dev);
1646221345Sdim	int sdw_be_num = 0, ssp_num = 0, dmic_num = 0, bt_num = 0;
1647221345Sdim	struct mc_private *ctx = snd_soc_card_get_drvdata(card);
1648221345Sdim	struct snd_soc_acpi_mach_params *mach_params = &mach->mach_params;
1649234353Sdim	const struct snd_soc_acpi_link_adr *adr_link = mach_params->links;
1650234353Sdim	bool aggregation = !(sof_sdw_quirk & SOF_SDW_NO_AGGREGATION);
1651234353Sdim	struct snd_soc_codec_conf *codec_conf;
1652224145Sdim	bool append_dai_type = false;
1653221345Sdim	bool ignore_pch_dmic = false;
1654221345Sdim	int codec_conf_num = 0;
1655221345Sdim	int codec_conf_index = 0;
1656221345Sdim	bool group_generated[SDW_MAX_GROUPS] = { };
1657221345Sdim	int ssp_codec_index, ssp_mask;
1658234353Sdim	struct snd_soc_dai_link *dai_links;
1659234353Sdim	int num_links, link_index = 0;
1660234353Sdim	char *name, *cpu_dai_name;
1661224145Sdim	char *codec_name, *codec_dai_name;
1662221345Sdim	int i, j, be_id = 0;
1663221345Sdim	int codec_index;
1664221345Sdim	int hdmi_num;
1665223017Sdim	int ret;
1666223017Sdim
1667234353Sdim	ret = get_dailink_info(dev, adr_link, &sdw_be_num, &codec_conf_num);
1668234353Sdim	if (ret < 0) {
1669234353Sdim		dev_err(dev, "failed to get sdw link info %d\n", ret);
1670224145Sdim		return ret;
1671223017Sdim	}
1672223017Sdim
1673223017Sdim	/*
1674221345Sdim	 * on generic tgl platform, I2S or sdw mode is supported
1675221345Sdim	 * based on board rework. A ACPI device is registered in
1676224145Sdim	 * system only when I2S mode is supported, not sdw mode.
1677234353Sdim	 * Here check ACPI ID to confirm I2S is supported.
1678234353Sdim	 */
1679221345Sdim	ssp_codec_index = find_codec_info_acpi(mach->id);
1680221345Sdim	if (ssp_codec_index >= 0) {
1681221345Sdim		ssp_mask = SOF_SSP_GET_PORT(sof_sdw_quirk);
1682234353Sdim		ssp_num = hweight_long(ssp_mask);
1683221345Sdim	}
1684221345Sdim
1685221345Sdim	if (mach_params->codec_mask & IDISP_CODEC_MASK)
1686221345Sdim		ctx->hdmi.idisp_codec = true;
1687221345Sdim
1688221345Sdim	if (sof_sdw_quirk & SOF_SDW_TGL_HDMI)
1689234353Sdim		hdmi_num = SOF_TGL_HDMI_COUNT;
1690221345Sdim	else
1691221345Sdim		hdmi_num = SOF_PRE_TGL_HDMI_COUNT;
1692221345Sdim
1693221345Sdim	/* enable dmic01 & dmic16k */
1694221345Sdim	if (sof_sdw_quirk & SOF_SDW_PCH_DMIC || mach_params->dmic_num)
1695221345Sdim		dmic_num = 2;
1696221345Sdim
1697221345Sdim	if (sof_sdw_quirk & SOF_SSP_BT_OFFLOAD_PRESENT)
1698221345Sdim		bt_num = 1;
1699221345Sdim
1700221345Sdim	dev_dbg(dev, "sdw %d, ssp %d, dmic %d, hdmi %d, bt: %d\n",
1701221345Sdim		sdw_be_num, ssp_num, dmic_num,
1702221345Sdim		ctx->hdmi.idisp_codec ? hdmi_num : 0, bt_num);
1703221345Sdim
1704224145Sdim	/* allocate BE dailinks */
1705224145Sdim	num_links = sdw_be_num + ssp_num + dmic_num + hdmi_num + bt_num;
1706224145Sdim	dai_links = devm_kcalloc(dev, num_links, sizeof(*dai_links), GFP_KERNEL);
1707249423Sdim	if (!dai_links)
1708249423Sdim		return -ENOMEM;
1709221345Sdim
1710224145Sdim	/* allocate codec conf, will be populated when dailinks are created */
1711224145Sdim	codec_conf = devm_kcalloc(dev, codec_conf_num, sizeof(*codec_conf),
1712224145Sdim				  GFP_KERNEL);
1713234353Sdim	if (!codec_conf)
1714224145Sdim		return -ENOMEM;
1715221345Sdim
1716224145Sdim	/* SDW */
1717224145Sdim	if (!sdw_be_num)
1718224145Sdim		goto SSP;
1719234353Sdim
1720234353Sdim	for (i = 0; i < SDW_MAX_LINKS; i++)
1721224145Sdim		ctx->sdw_pin_index[i] = SDW_INTEL_BIDIR_PDI_BASE;
1722221345Sdim
1723224145Sdim	for (; adr_link->num_adr; adr_link++) {
1724224145Sdim		/*
1725221345Sdim		 * If there are two or more different devices on the same sdw link, we have to
1726224145Sdim		 * append the codec type to the dai link name to prevent duplicated dai link name.
1727221345Sdim		 * The same type devices on the same sdw link will be in the same
1728224145Sdim		 * snd_soc_acpi_adr_device array. They won't be described in different adr_links.
1729224145Sdim		 */
1730224145Sdim		for (i = 0; i < adr_link->num_adr; i++) {
1731224145Sdim			/* find codec info to get dai_num */
1732224145Sdim			codec_index = find_codec_info_part(adr_link->adr_d[i].adr);
1733224145Sdim			if (codec_index < 0)
1734224145Sdim				return codec_index;
1735224145Sdim			if (codec_info_list[codec_index].dai_num > 1) {
1736224145Sdim				append_dai_type = true;
1737224145Sdim				goto out;
1738224145Sdim			}
1739224145Sdim			for (j = 0; j < i; j++) {
1740226633Sdim				if ((SDW_PART_ID(adr_link->adr_d[i].adr) !=
1741226633Sdim				    SDW_PART_ID(adr_link->adr_d[j].adr)) ||
1742224145Sdim				    (SDW_MFG_ID(adr_link->adr_d[i].adr) !=
1743224145Sdim				    SDW_MFG_ID(adr_link->adr_d[j].adr))) {
1744224145Sdim					append_dai_type = true;
1745224145Sdim					goto out;
1746234353Sdim				}
1747224145Sdim			}
1748224145Sdim		}
1749224145Sdim	}
1750224145Sdimout:
1751224145Sdim
1752224145Sdim	/* generate DAI links by each sdw link */
1753224145Sdim	for (adr_link = mach_params->links ; adr_link->num_adr; adr_link++) {
1754224145Sdim		for (i = 0; i < adr_link->num_adr; i++) {
1755224145Sdim			const struct snd_soc_acpi_endpoint *endpoint;
1756224145Sdim
1757224145Sdim			endpoint = adr_link->adr_d[i].endpoints;
1758224145Sdim
1759224145Sdim			/* this group has been generated */
1760224145Sdim			if (endpoint->aggregated &&
1761224145Sdim			    group_generated[endpoint->group_id])
1762224145Sdim				continue;
1763226633Sdim
1764224145Sdim			/* find codec info to get dai_num */
1765224145Sdim			codec_index = find_codec_info_part(adr_link->adr_d[i].adr);
1766224145Sdim			if (codec_index < 0)
1767226633Sdim				return codec_index;
1768224145Sdim
1769224145Sdim			for (j = 0; j < codec_info_list[codec_index].dai_num ; j++) {
1770224145Sdim				int current_be_id;
1771224145Sdim
1772224145Sdim				ret = create_sdw_dailink(card, &link_index, dai_links,
1773224145Sdim							 sdw_be_num, adr_link,
1774224145Sdim							 codec_conf, codec_conf_num,
1775224145Sdim							 &current_be_id, &codec_conf_index,
1776224145Sdim							 &ignore_pch_dmic, append_dai_type, i, j);
1777224145Sdim				if (ret < 0) {
1778224145Sdim					dev_err(dev, "failed to create dai link %d\n", link_index);
1779224145Sdim					return ret;
1780224145Sdim				}
1781224145Sdim
1782224145Sdim				/* Update the be_id to match the highest ID used for SDW link */
1783234353Sdim				if (be_id < current_be_id)
1784224145Sdim					be_id = current_be_id;
1785223017Sdim			}
1786221345Sdim
1787224145Sdim			if (aggregation && endpoint->aggregated)
1788224145Sdim				group_generated[endpoint->group_id] = true;
1789243830Sdim		}
1790234353Sdim	}
1791221345Sdim
1792221345SdimSSP:
1793224145Sdim	/* SSP */
1794221345Sdim	if (!ssp_num)
1795221345Sdim		goto DMIC;
1796221345Sdim
1797251662Sdim	for (i = 0, j = 0; ssp_mask; i++, ssp_mask >>= 1) {
1798251662Sdim		struct sof_sdw_codec_info *info;
1799251662Sdim		int playback, capture;
1800251662Sdim
1801251662Sdim		if (!(ssp_mask & 0x1))
1802251662Sdim			continue;
1803251662Sdim
1804251662Sdim		info = &codec_info_list[ssp_codec_index];
1805251662Sdim
1806251662Sdim		name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d-Codec", i);
1807251662Sdim		cpu_dai_name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d Pin", i);
1808251662Sdim		codec_name = devm_kasprintf(dev, GFP_KERNEL, "i2c-%s:0%d",
1809251662Sdim					    info->acpi_id, j++);
1810251662Sdim
1811251662Sdim		playback = info->dais[0].direction[SNDRV_PCM_STREAM_PLAYBACK];
1812251662Sdim		capture = info->dais[0].direction[SNDRV_PCM_STREAM_CAPTURE];
1813251662Sdim
1814251662Sdim		ret = init_simple_dai_link(dev, dai_links + link_index, &be_id, name,
1815251662Sdim					   playback, capture, cpu_dai_name,
1816251662Sdim					   codec_name, info->dais[0].dai_name,
1817251662Sdim					   NULL, info->ops);
1818251662Sdim		if (ret)
1819251662Sdim			return ret;
1820251662Sdim
1821251662Sdim		ret = info->dais[0].init(card, NULL, dai_links + link_index, info, 0);
1822251662Sdim		if (ret < 0)
1823251662Sdim			return ret;
1824251662Sdim
1825251662Sdim		link_index++;
1826251662Sdim	}
1827251662Sdim
1828251662SdimDMIC:
1829251662Sdim	/* dmic */
1830251662Sdim	if (dmic_num > 0) {
1831251662Sdim		if (ignore_pch_dmic) {
1832251662Sdim			dev_warn(dev, "Ignoring PCH DMIC\n");
1833251662Sdim			goto HDMI;
1834251662Sdim		}
1835263508Sdim
1836263508Sdim		ret = init_simple_dai_link(dev, dai_links + link_index, &be_id, "dmic01",
1837251662Sdim					   0, 1, // DMIC only supports capture
1838251662Sdim					   "DMIC01 Pin", "dmic-codec", "dmic-hifi",
1839251662Sdim					   sof_sdw_dmic_init, NULL);
1840251662Sdim		if (ret)
1841251662Sdim			return ret;
1842251662Sdim
1843251662Sdim		link_index++;
1844251662Sdim
1845251662Sdim		ret = init_simple_dai_link(dev, dai_links + link_index, &be_id, "dmic16k",
1846251662Sdim					   0, 1, // DMIC only supports capture
1847251662Sdim					   "DMIC16k Pin", "dmic-codec", "dmic-hifi",
1848251662Sdim					   /* don't call sof_sdw_dmic_init() twice */
1849251662Sdim					   NULL, NULL);
1850251662Sdim		if (ret)
1851251662Sdim			return ret;
1852251662Sdim
1853251662Sdim		link_index++;
1854251662Sdim	}
1855218887Sdim
1856218887SdimHDMI:
1857218887Sdim	/* HDMI */
1858218887Sdim	for (i = 0; i < hdmi_num; i++) {
1859219077Sdim		name = devm_kasprintf(dev, GFP_KERNEL, "iDisp%d", i + 1);
1860234353Sdim		cpu_dai_name = devm_kasprintf(dev, GFP_KERNEL, "iDisp%d Pin", i + 1);
1861218887Sdim
1862234353Sdim		if (ctx->hdmi.idisp_codec) {
1863218887Sdim			codec_name = "ehdaudio0D2";
1864218887Sdim			codec_dai_name = devm_kasprintf(dev, GFP_KERNEL,
1865251662Sdim							"intel-hdmi-hifi%d", i + 1);
1866234353Sdim		} else {
1867234353Sdim			codec_name = "snd-soc-dummy";
1868234353Sdim			codec_dai_name = "snd-soc-dummy-dai";
1869234353Sdim		}
1870234353Sdim
1871234353Sdim		ret = init_simple_dai_link(dev, dai_links + link_index, &be_id, name,
1872234353Sdim					   1, 0, // HDMI only supports playback
1873234353Sdim					   cpu_dai_name, codec_name, codec_dai_name,
1874234353Sdim					   i == 0 ? sof_sdw_hdmi_init : NULL, NULL);
1875234353Sdim		if (ret)
1876234353Sdim			return ret;
1877234353Sdim
1878234353Sdim		link_index++;
1879234353Sdim	}
1880234353Sdim
1881234353Sdim	if (sof_sdw_quirk & SOF_SSP_BT_OFFLOAD_PRESENT) {
1882234353Sdim		int port = (sof_sdw_quirk & SOF_BT_OFFLOAD_SSP_MASK) >>
1883234353Sdim				SOF_BT_OFFLOAD_SSP_SHIFT;
1884234353Sdim
1885234353Sdim		name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d-BT", port);
1886234353Sdim		cpu_dai_name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d Pin", port);
1887234353Sdim
1888234353Sdim		ret = init_simple_dai_link(dev, dai_links + link_index, &be_id, name,
1889234353Sdim					   1, 1, cpu_dai_name, snd_soc_dummy_dlc.name,
1890234353Sdim					   snd_soc_dummy_dlc.dai_name, NULL, NULL);
1891234353Sdim		if (ret)
1892234353Sdim			return ret;
1893234353Sdim	}
1894234353Sdim
1895234353Sdim	card->dai_link = dai_links;
1896234353Sdim	card->num_links = num_links;
1897251662Sdim
1898251662Sdim	card->codec_conf = codec_conf;
1899234353Sdim	card->num_configs = codec_conf_num;
1900234353Sdim
1901234353Sdim	return 0;
1902234353Sdim}
1903234353Sdim
1904218887Sdimstatic int sof_sdw_card_late_probe(struct snd_soc_card *card)
1905218887Sdim{
1906218887Sdim	struct mc_private *ctx = snd_soc_card_get_drvdata(card);
1907218887Sdim	int ret = 0;
1908224145Sdim	int i;
1909224145Sdim
1910224145Sdim	for (i = 0; i < ARRAY_SIZE(codec_info_list); i++) {
1911224145Sdim		if (codec_info_list[i].codec_card_late_probe) {
1912218887Sdim			ret = codec_info_list[i].codec_card_late_probe(card);
1913218887Sdim
1914234353Sdim			if (ret < 0)
1915234353Sdim				return ret;
1916234353Sdim		}
1917234353Sdim	}
1918234353Sdim
1919234353Sdim	if (ctx->hdmi.idisp_codec)
1920234353Sdim		ret = sof_sdw_hdmi_card_late_probe(card);
1921234353Sdim
1922234353Sdim	return ret;
1923234353Sdim}
1924218887Sdim
1925218887Sdim/* SoC card */
1926218887Sdimstatic const char sdw_card_long_name[] = "Intel Soundwire SOF";
1927219077Sdim
1928218887Sdimstatic struct snd_soc_card card_sof_sdw = {
1929234353Sdim	.name = "soundwire",
1930218887Sdim	.owner = THIS_MODULE,
1931218887Sdim	.late_probe = sof_sdw_card_late_probe,
1932218887Sdim};
1933218887Sdim
1934218887Sdim/* helper to get the link that the codec DAI is used */
1935218887Sdimstatic struct snd_soc_dai_link *mc_find_codec_dai_used(struct snd_soc_card *card,
1936218887Sdim						       const char *dai_name)
1937218887Sdim{
1938218887Sdim	struct snd_soc_dai_link *dai_link;
1939218887Sdim	int i;
1940218887Sdim	int j;
1941218887Sdim
1942218887Sdim	for_each_card_prelinks(card, i, dai_link) {
1943218887Sdim		for (j = 0; j < dai_link->num_codecs; j++) {
1944218887Sdim			/* Check each codec in a link */
1945218887Sdim			if (!strcmp(dai_link->codecs[j].dai_name, dai_name))
1946218887Sdim				return dai_link;
1947234353Sdim		}
1948218887Sdim	}
1949218887Sdim	return NULL;
1950218887Sdim}
1951218887Sdim
1952234353Sdimstatic void mc_dailink_exit_loop(struct snd_soc_card *card)
1953218887Sdim{
1954249423Sdim	struct snd_soc_dai_link *dai_link;
1955249423Sdim	int ret;
1956218887Sdim	int i, j;
1957218887Sdim
1958218887Sdim	for (i = 0; i < ARRAY_SIZE(codec_info_list); i++) {
1959218887Sdim		for (j = 0; j < codec_info_list[i].dai_num; j++) {
1960218887Sdim			codec_info_list[i].dais[j].rtd_init_done = false;
1961218887Sdim			/* Check each dai in codec_info_lis to see if it is used in the link */
1962218887Sdim			if (!codec_info_list[i].dais[j].exit)
1963234353Sdim				continue;
1964243830Sdim			/*
1965218887Sdim			 * We don't need to call .exit function if there is no matched
1966218887Sdim			 * dai link found.
1967218887Sdim			 */
1968234353Sdim			dai_link = mc_find_codec_dai_used(card,
1969234353Sdim							  codec_info_list[i].dais[j].dai_name);
1970249423Sdim			if (dai_link) {
1971226633Sdim				/* Do the .exit function if the codec dai is used in the link */
1972234353Sdim				ret = codec_info_list[i].dais[j].exit(card, dai_link);
1973239462Sdim				if (ret)
1974243830Sdim					dev_warn(card->dev,
1975218887Sdim						 "codec exit failed %d\n",
1976218887Sdim						 ret);
1977218887Sdim				break;
1978218887Sdim			}
1979218887Sdim		}
1980218887Sdim	}
1981218887Sdim}
1982226633Sdim
1983226633Sdimstatic int mc_probe(struct platform_device *pdev)
1984226633Sdim{
1985218887Sdim	struct snd_soc_card *card = &card_sof_sdw;
1986218887Sdim	struct snd_soc_acpi_mach *mach = dev_get_platdata(&pdev->dev);
1987218887Sdim	struct mc_private *ctx;
1988218887Sdim	int amp_num = 0, i;
1989218887Sdim	int ret;
1990218887Sdim
1991218887Sdim	card->dev = &pdev->dev;
1992218887Sdim
1993218887Sdim	dev_dbg(card->dev, "Entry\n");
1994243830Sdim
1995218887Sdim	ctx = devm_kzalloc(card->dev, sizeof(*ctx), GFP_KERNEL);
1996218887Sdim	if (!ctx)
1997243830Sdim		return -ENOMEM;
1998218887Sdim
1999218887Sdim	snd_soc_card_set_drvdata(card, ctx);
2000218887Sdim
2001218887Sdim	dmi_check_system(sof_sdw_quirk_table);
2002218887Sdim
2003218887Sdim	if (quirk_override != -1) {
2004218887Sdim		dev_info(card->dev, "Overriding quirk 0x%lx => 0x%x\n",
2005218887Sdim			 sof_sdw_quirk, quirk_override);
2006218887Sdim		sof_sdw_quirk = quirk_override;
2007218887Sdim	}
2008218887Sdim
2009218887Sdim	log_quirks(card->dev);
2010218887Sdim
2011218887Sdim	/* reset amp_num to ensure amp_num++ starts from 0 in each probe */
2012218887Sdim	for (i = 0; i < ARRAY_SIZE(codec_info_list); i++)
2013218887Sdim		codec_info_list[i].amp_num = 0;
2014218887Sdim
2015218887Sdim	if (mach->mach_params.subsystem_id_set) {
2016218887Sdim		snd_soc_card_set_pci_ssid(card,
2017218887Sdim					  mach->mach_params.subsystem_vendor,
2018218887Sdim					  mach->mach_params.subsystem_device);
2019218887Sdim	}
2020218887Sdim
2021234353Sdim	ret = sof_card_dai_links_create(card);
2022219077Sdim	if (ret < 0)
2023218887Sdim		return ret;
2024243830Sdim
2025218887Sdim	/*
2026243830Sdim	 * the default amp_num is zero for each codec and
2027218887Sdim	 * amp_num will only be increased for active amp
2028218887Sdim	 * codecs on used platform
2029224145Sdim	 */
2030234353Sdim	for (i = 0; i < ARRAY_SIZE(codec_info_list); i++)
2031234353Sdim		amp_num += codec_info_list[i].amp_num;
2032224145Sdim
2033218887Sdim	card->components = devm_kasprintf(card->dev, GFP_KERNEL,
2034218887Sdim					  "cfg-spk:%d cfg-amp:%d",
2035218887Sdim					  (sof_sdw_quirk & SOF_SDW_FOUR_SPK)
2036219077Sdim					  ? 4 : 2, amp_num);
2037219077Sdim	if (!card->components)
2038218887Sdim		return -ENOMEM;
2039218887Sdim
2040218887Sdim	if (mach->mach_params.dmic_num) {
2041234353Sdim		card->components = devm_kasprintf(card->dev, GFP_KERNEL,
2042243830Sdim						  "%s mic:dmic cfg-mics:%d",
2043218887Sdim						  card->components,
2044218887Sdim						  mach->mach_params.dmic_num);
2045218887Sdim		if (!card->components)
2046243830Sdim			return -ENOMEM;
2047243830Sdim	}
2048218887Sdim
2049218887Sdim	card->long_name = sdw_card_long_name;
2050218887Sdim
2051218887Sdim	/* Register the card */
2052218887Sdim	ret = devm_snd_soc_register_card(card->dev, card);
2053218887Sdim	if (ret) {
2054218887Sdim		dev_err_probe(card->dev, ret, "snd_soc_register_card failed %d\n", ret);
2055218887Sdim		mc_dailink_exit_loop(card);
2056218887Sdim		return ret;
2057234353Sdim	}
2058218887Sdim
2059219077Sdim	platform_set_drvdata(pdev, card);
2060234353Sdim
2061234353Sdim	return ret;
2062263508Sdim}
2063219077Sdim
2064234353Sdimstatic void mc_remove(struct platform_device *pdev)
2065234353Sdim{
2066234353Sdim	struct snd_soc_card *card = platform_get_drvdata(pdev);
2067234353Sdim
2068234353Sdim	mc_dailink_exit_loop(card);
2069234353Sdim}
2070234353Sdim
2071234353Sdimstatic const struct platform_device_id mc_id_table[] = {
2072234353Sdim	{ "sof_sdw", },
2073	{}
2074};
2075MODULE_DEVICE_TABLE(platform, mc_id_table);
2076
2077static struct platform_driver sof_sdw_driver = {
2078	.driver = {
2079		.name = "sof_sdw",
2080		.pm = &snd_soc_pm_ops,
2081	},
2082	.probe = mc_probe,
2083	.remove_new = mc_remove,
2084	.id_table = mc_id_table,
2085};
2086
2087module_platform_driver(sof_sdw_driver);
2088
2089MODULE_DESCRIPTION("ASoC SoundWire Generic Machine driver");
2090MODULE_AUTHOR("Bard Liao <yung-chuan.liao@linux.intel.com>");
2091MODULE_AUTHOR("Rander Wang <rander.wang@linux.intel.com>");
2092MODULE_AUTHOR("Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>");
2093MODULE_LICENSE("GPL v2");
2094MODULE_IMPORT_NS(SND_SOC_INTEL_HDA_DSP_COMMON);
2095MODULE_IMPORT_NS(SND_SOC_INTEL_SOF_MAXIM_COMMON);
2096