xref: /OK3568_Linux_fs/kernel/sound/soc/intel/boards/sof_sdw.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-only
2*4882a593Smuzhiyun // Copyright (c) 2020 Intel Corporation
3*4882a593Smuzhiyun 
4*4882a593Smuzhiyun /*
5*4882a593Smuzhiyun  *  sof_sdw - ASOC Machine driver for Intel SoundWire platforms
6*4882a593Smuzhiyun  */
7*4882a593Smuzhiyun 
8*4882a593Smuzhiyun #include <linux/device.h>
9*4882a593Smuzhiyun #include <linux/dmi.h>
10*4882a593Smuzhiyun #include <linux/module.h>
11*4882a593Smuzhiyun #include <linux/soundwire/sdw.h>
12*4882a593Smuzhiyun #include <linux/soundwire/sdw_type.h>
13*4882a593Smuzhiyun #include <sound/soc.h>
14*4882a593Smuzhiyun #include <sound/soc-acpi.h>
15*4882a593Smuzhiyun #include "sof_sdw_common.h"
16*4882a593Smuzhiyun 
17*4882a593Smuzhiyun unsigned long sof_sdw_quirk = SOF_RT711_JD_SRC_JD1;
18*4882a593Smuzhiyun static int quirk_override = -1;
19*4882a593Smuzhiyun module_param_named(quirk, quirk_override, int, 0444);
20*4882a593Smuzhiyun MODULE_PARM_DESC(quirk, "Board-specific quirk override");
21*4882a593Smuzhiyun 
22*4882a593Smuzhiyun #define INC_ID(BE, CPU, LINK)	do { (BE)++; (CPU)++; (LINK)++; } while (0)
23*4882a593Smuzhiyun 
log_quirks(struct device * dev)24*4882a593Smuzhiyun static void log_quirks(struct device *dev)
25*4882a593Smuzhiyun {
26*4882a593Smuzhiyun 	if (SOF_RT711_JDSRC(sof_sdw_quirk))
27*4882a593Smuzhiyun 		dev_dbg(dev, "quirk realtek,jack-detect-source %ld\n",
28*4882a593Smuzhiyun 			SOF_RT711_JDSRC(sof_sdw_quirk));
29*4882a593Smuzhiyun 	if (sof_sdw_quirk & SOF_SDW_FOUR_SPK)
30*4882a593Smuzhiyun 		dev_dbg(dev, "quirk SOF_SDW_FOUR_SPK enabled\n");
31*4882a593Smuzhiyun 	if (sof_sdw_quirk & SOF_SDW_TGL_HDMI)
32*4882a593Smuzhiyun 		dev_dbg(dev, "quirk SOF_SDW_TGL_HDMI enabled\n");
33*4882a593Smuzhiyun 	if (sof_sdw_quirk & SOF_SDW_PCH_DMIC)
34*4882a593Smuzhiyun 		dev_dbg(dev, "quirk SOF_SDW_PCH_DMIC enabled\n");
35*4882a593Smuzhiyun 	if (SOF_SSP_GET_PORT(sof_sdw_quirk))
36*4882a593Smuzhiyun 		dev_dbg(dev, "SSP port %ld\n",
37*4882a593Smuzhiyun 			SOF_SSP_GET_PORT(sof_sdw_quirk));
38*4882a593Smuzhiyun 	if (sof_sdw_quirk & SOF_RT715_DAI_ID_FIX)
39*4882a593Smuzhiyun 		dev_dbg(dev, "quirk SOF_RT715_DAI_ID_FIX enabled\n");
40*4882a593Smuzhiyun 	if (sof_sdw_quirk & SOF_SDW_NO_AGGREGATION)
41*4882a593Smuzhiyun 		dev_dbg(dev, "quirk SOF_SDW_NO_AGGREGATION enabled\n");
42*4882a593Smuzhiyun }
43*4882a593Smuzhiyun 
sof_sdw_quirk_cb(const struct dmi_system_id * id)44*4882a593Smuzhiyun static int sof_sdw_quirk_cb(const struct dmi_system_id *id)
45*4882a593Smuzhiyun {
46*4882a593Smuzhiyun 	sof_sdw_quirk = (unsigned long)id->driver_data;
47*4882a593Smuzhiyun 	return 1;
48*4882a593Smuzhiyun }
49*4882a593Smuzhiyun 
50*4882a593Smuzhiyun static const struct dmi_system_id sof_sdw_quirk_table[] = {
51*4882a593Smuzhiyun 	/* CometLake devices */
52*4882a593Smuzhiyun 	{
53*4882a593Smuzhiyun 		.callback = sof_sdw_quirk_cb,
54*4882a593Smuzhiyun 		.matches = {
55*4882a593Smuzhiyun 			DMI_MATCH(DMI_SYS_VENDOR, "Intel Corporation"),
56*4882a593Smuzhiyun 			DMI_MATCH(DMI_PRODUCT_NAME, "CometLake Client"),
57*4882a593Smuzhiyun 		},
58*4882a593Smuzhiyun 		.driver_data = (void *)SOF_SDW_PCH_DMIC,
59*4882a593Smuzhiyun 	},
60*4882a593Smuzhiyun 	{
61*4882a593Smuzhiyun 		.callback = sof_sdw_quirk_cb,
62*4882a593Smuzhiyun 		.matches = {
63*4882a593Smuzhiyun 			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
64*4882a593Smuzhiyun 			DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "09C6")
65*4882a593Smuzhiyun 		},
66*4882a593Smuzhiyun 		.driver_data = (void *)(SOF_RT711_JD_SRC_JD2 |
67*4882a593Smuzhiyun 					SOF_RT715_DAI_ID_FIX),
68*4882a593Smuzhiyun 	},
69*4882a593Smuzhiyun 	{
70*4882a593Smuzhiyun 		/* early version of SKU 09C6 */
71*4882a593Smuzhiyun 		.callback = sof_sdw_quirk_cb,
72*4882a593Smuzhiyun 		.matches = {
73*4882a593Smuzhiyun 			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
74*4882a593Smuzhiyun 			DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0983")
75*4882a593Smuzhiyun 		},
76*4882a593Smuzhiyun 		.driver_data = (void *)(SOF_RT711_JD_SRC_JD2 |
77*4882a593Smuzhiyun 					SOF_RT715_DAI_ID_FIX),
78*4882a593Smuzhiyun 	},
79*4882a593Smuzhiyun 	{
80*4882a593Smuzhiyun 		.callback = sof_sdw_quirk_cb,
81*4882a593Smuzhiyun 		.matches = {
82*4882a593Smuzhiyun 			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
83*4882a593Smuzhiyun 			DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "098F"),
84*4882a593Smuzhiyun 		},
85*4882a593Smuzhiyun 		.driver_data = (void *)(SOF_RT711_JD_SRC_JD2 |
86*4882a593Smuzhiyun 					SOF_RT715_DAI_ID_FIX |
87*4882a593Smuzhiyun 					SOF_SDW_FOUR_SPK),
88*4882a593Smuzhiyun 	},
89*4882a593Smuzhiyun 	{
90*4882a593Smuzhiyun 		.callback = sof_sdw_quirk_cb,
91*4882a593Smuzhiyun 		.matches = {
92*4882a593Smuzhiyun 			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
93*4882a593Smuzhiyun 			DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0990"),
94*4882a593Smuzhiyun 		},
95*4882a593Smuzhiyun 		.driver_data = (void *)(SOF_RT711_JD_SRC_JD2 |
96*4882a593Smuzhiyun 					SOF_RT715_DAI_ID_FIX |
97*4882a593Smuzhiyun 					SOF_SDW_FOUR_SPK),
98*4882a593Smuzhiyun 	},
99*4882a593Smuzhiyun 	/* IceLake devices */
100*4882a593Smuzhiyun 	{
101*4882a593Smuzhiyun 		.callback = sof_sdw_quirk_cb,
102*4882a593Smuzhiyun 		.matches = {
103*4882a593Smuzhiyun 			DMI_MATCH(DMI_SYS_VENDOR, "Intel Corporation"),
104*4882a593Smuzhiyun 			DMI_MATCH(DMI_PRODUCT_NAME, "Ice Lake Client"),
105*4882a593Smuzhiyun 		},
106*4882a593Smuzhiyun 		.driver_data = (void *)SOF_SDW_PCH_DMIC,
107*4882a593Smuzhiyun 	},
108*4882a593Smuzhiyun 	/* TigerLake devices */
109*4882a593Smuzhiyun 	{
110*4882a593Smuzhiyun 		.callback = sof_sdw_quirk_cb,
111*4882a593Smuzhiyun 		.matches = {
112*4882a593Smuzhiyun 			DMI_MATCH(DMI_SYS_VENDOR, "Intel Corporation"),
113*4882a593Smuzhiyun 			DMI_MATCH(DMI_PRODUCT_NAME,
114*4882a593Smuzhiyun 				  "Tiger Lake Client Platform"),
115*4882a593Smuzhiyun 		},
116*4882a593Smuzhiyun 		.driver_data = (void *)(SOF_SDW_TGL_HDMI |
117*4882a593Smuzhiyun 					SOF_RT711_JD_SRC_JD1 |
118*4882a593Smuzhiyun 					SOF_SDW_PCH_DMIC |
119*4882a593Smuzhiyun 					SOF_SSP_PORT(SOF_I2S_SSP2)),
120*4882a593Smuzhiyun 	},
121*4882a593Smuzhiyun 	{
122*4882a593Smuzhiyun 		.callback = sof_sdw_quirk_cb,
123*4882a593Smuzhiyun 		.matches = {
124*4882a593Smuzhiyun 			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
125*4882a593Smuzhiyun 			DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0A3E")
126*4882a593Smuzhiyun 		},
127*4882a593Smuzhiyun 		.driver_data = (void *)(SOF_SDW_TGL_HDMI |
128*4882a593Smuzhiyun 					SOF_RT711_JD_SRC_JD2 |
129*4882a593Smuzhiyun 					SOF_RT715_DAI_ID_FIX),
130*4882a593Smuzhiyun 	},
131*4882a593Smuzhiyun 	{
132*4882a593Smuzhiyun 		.callback = sof_sdw_quirk_cb,
133*4882a593Smuzhiyun 		.matches = {
134*4882a593Smuzhiyun 			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
135*4882a593Smuzhiyun 			DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0A5E")
136*4882a593Smuzhiyun 		},
137*4882a593Smuzhiyun 		.driver_data = (void *)(SOF_SDW_TGL_HDMI |
138*4882a593Smuzhiyun 					SOF_RT711_JD_SRC_JD2 |
139*4882a593Smuzhiyun 					SOF_RT715_DAI_ID_FIX |
140*4882a593Smuzhiyun 					SOF_SDW_FOUR_SPK),
141*4882a593Smuzhiyun 	},
142*4882a593Smuzhiyun 	{
143*4882a593Smuzhiyun 		.callback = sof_sdw_quirk_cb,
144*4882a593Smuzhiyun 		.matches = {
145*4882a593Smuzhiyun 			DMI_MATCH(DMI_SYS_VENDOR, "Google"),
146*4882a593Smuzhiyun 			DMI_MATCH(DMI_PRODUCT_NAME, "Volteer"),
147*4882a593Smuzhiyun 		},
148*4882a593Smuzhiyun 		.driver_data = (void *)(SOF_SDW_TGL_HDMI |
149*4882a593Smuzhiyun 					SOF_SDW_PCH_DMIC |
150*4882a593Smuzhiyun 					SOF_SDW_FOUR_SPK),
151*4882a593Smuzhiyun 	},
152*4882a593Smuzhiyun 	{
153*4882a593Smuzhiyun 		.callback = sof_sdw_quirk_cb,
154*4882a593Smuzhiyun 		.matches = {
155*4882a593Smuzhiyun 			DMI_MATCH(DMI_SYS_VENDOR, "Google"),
156*4882a593Smuzhiyun 			DMI_MATCH(DMI_PRODUCT_NAME, "Ripto"),
157*4882a593Smuzhiyun 		},
158*4882a593Smuzhiyun 		.driver_data = (void *)(SOF_SDW_TGL_HDMI |
159*4882a593Smuzhiyun 					SOF_SDW_PCH_DMIC |
160*4882a593Smuzhiyun 					SOF_SDW_FOUR_SPK),
161*4882a593Smuzhiyun 	},
162*4882a593Smuzhiyun 	{
163*4882a593Smuzhiyun 		/*
164*4882a593Smuzhiyun 		 * this entry covers multiple HP SKUs. The family name
165*4882a593Smuzhiyun 		 * does not seem robust enough, so we use a partial
166*4882a593Smuzhiyun 		 * match that ignores the product name suffix
167*4882a593Smuzhiyun 		 * (e.g. 15-eb1xxx, 14t-ea000 or 13-aw2xxx)
168*4882a593Smuzhiyun 		 */
169*4882a593Smuzhiyun 		.callback = sof_sdw_quirk_cb,
170*4882a593Smuzhiyun 		.matches = {
171*4882a593Smuzhiyun 			DMI_MATCH(DMI_SYS_VENDOR, "HP"),
172*4882a593Smuzhiyun 			DMI_MATCH(DMI_PRODUCT_NAME, "HP Spectre x360 Convertible"),
173*4882a593Smuzhiyun 		},
174*4882a593Smuzhiyun 		.driver_data = (void *)(SOF_SDW_TGL_HDMI |
175*4882a593Smuzhiyun 					SOF_SDW_PCH_DMIC |
176*4882a593Smuzhiyun 					SOF_RT711_JD_SRC_JD2),
177*4882a593Smuzhiyun 	},
178*4882a593Smuzhiyun 	/* TigerLake-SDCA devices */
179*4882a593Smuzhiyun 	{
180*4882a593Smuzhiyun 		.callback = sof_sdw_quirk_cb,
181*4882a593Smuzhiyun 		.matches = {
182*4882a593Smuzhiyun 			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
183*4882a593Smuzhiyun 			DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0A32")
184*4882a593Smuzhiyun 		},
185*4882a593Smuzhiyun 		.driver_data = (void *)(SOF_SDW_TGL_HDMI |
186*4882a593Smuzhiyun 					SOF_RT711_JD_SRC_JD2 |
187*4882a593Smuzhiyun 					SOF_RT715_DAI_ID_FIX |
188*4882a593Smuzhiyun 					SOF_SDW_FOUR_SPK),
189*4882a593Smuzhiyun 	},
190*4882a593Smuzhiyun 	/* AlderLake devices */
191*4882a593Smuzhiyun 	{
192*4882a593Smuzhiyun 		.callback = sof_sdw_quirk_cb,
193*4882a593Smuzhiyun 		.matches = {
194*4882a593Smuzhiyun 			DMI_MATCH(DMI_SYS_VENDOR, "Intel Corporation"),
195*4882a593Smuzhiyun 			DMI_MATCH(DMI_PRODUCT_NAME, "Alder Lake Client Platform"),
196*4882a593Smuzhiyun 		},
197*4882a593Smuzhiyun 		.driver_data = (void *)(SOF_RT711_JD_SRC_JD1 |
198*4882a593Smuzhiyun 					SOF_SDW_TGL_HDMI |
199*4882a593Smuzhiyun 					SOF_RT715_DAI_ID_FIX |
200*4882a593Smuzhiyun 					SOF_SDW_PCH_DMIC),
201*4882a593Smuzhiyun 	},
202*4882a593Smuzhiyun 	{}
203*4882a593Smuzhiyun };
204*4882a593Smuzhiyun 
205*4882a593Smuzhiyun static struct snd_soc_dai_link_component dmic_component[] = {
206*4882a593Smuzhiyun 	{
207*4882a593Smuzhiyun 		.name = "dmic-codec",
208*4882a593Smuzhiyun 		.dai_name = "dmic-hifi",
209*4882a593Smuzhiyun 	}
210*4882a593Smuzhiyun };
211*4882a593Smuzhiyun 
212*4882a593Smuzhiyun static struct snd_soc_dai_link_component platform_component[] = {
213*4882a593Smuzhiyun 	{
214*4882a593Smuzhiyun 		/* name might be overridden during probe */
215*4882a593Smuzhiyun 		.name = "0000:00:1f.3"
216*4882a593Smuzhiyun 	}
217*4882a593Smuzhiyun };
218*4882a593Smuzhiyun 
219*4882a593Smuzhiyun /* these wrappers are only needed to avoid typecast compilation errors */
sdw_startup(struct snd_pcm_substream * substream)220*4882a593Smuzhiyun int sdw_startup(struct snd_pcm_substream *substream)
221*4882a593Smuzhiyun {
222*4882a593Smuzhiyun 	return sdw_startup_stream(substream);
223*4882a593Smuzhiyun }
224*4882a593Smuzhiyun 
sdw_prepare(struct snd_pcm_substream * substream)225*4882a593Smuzhiyun int sdw_prepare(struct snd_pcm_substream *substream)
226*4882a593Smuzhiyun {
227*4882a593Smuzhiyun 	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
228*4882a593Smuzhiyun 	struct sdw_stream_runtime *sdw_stream;
229*4882a593Smuzhiyun 	struct snd_soc_dai *dai;
230*4882a593Smuzhiyun 
231*4882a593Smuzhiyun 	/* Find stream from first CPU DAI */
232*4882a593Smuzhiyun 	dai = asoc_rtd_to_cpu(rtd, 0);
233*4882a593Smuzhiyun 
234*4882a593Smuzhiyun 	sdw_stream = snd_soc_dai_get_sdw_stream(dai, substream->stream);
235*4882a593Smuzhiyun 
236*4882a593Smuzhiyun 	if (IS_ERR(sdw_stream)) {
237*4882a593Smuzhiyun 		dev_err(rtd->dev, "no stream found for DAI %s", dai->name);
238*4882a593Smuzhiyun 		return PTR_ERR(sdw_stream);
239*4882a593Smuzhiyun 	}
240*4882a593Smuzhiyun 
241*4882a593Smuzhiyun 	return sdw_prepare_stream(sdw_stream);
242*4882a593Smuzhiyun }
243*4882a593Smuzhiyun 
sdw_trigger(struct snd_pcm_substream * substream,int cmd)244*4882a593Smuzhiyun int sdw_trigger(struct snd_pcm_substream *substream, int cmd)
245*4882a593Smuzhiyun {
246*4882a593Smuzhiyun 	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
247*4882a593Smuzhiyun 	struct sdw_stream_runtime *sdw_stream;
248*4882a593Smuzhiyun 	struct snd_soc_dai *dai;
249*4882a593Smuzhiyun 	int ret;
250*4882a593Smuzhiyun 
251*4882a593Smuzhiyun 	/* Find stream from first CPU DAI */
252*4882a593Smuzhiyun 	dai = asoc_rtd_to_cpu(rtd, 0);
253*4882a593Smuzhiyun 
254*4882a593Smuzhiyun 	sdw_stream = snd_soc_dai_get_sdw_stream(dai, substream->stream);
255*4882a593Smuzhiyun 
256*4882a593Smuzhiyun 	if (IS_ERR(sdw_stream)) {
257*4882a593Smuzhiyun 		dev_err(rtd->dev, "no stream found for DAI %s", dai->name);
258*4882a593Smuzhiyun 		return PTR_ERR(sdw_stream);
259*4882a593Smuzhiyun 	}
260*4882a593Smuzhiyun 
261*4882a593Smuzhiyun 	switch (cmd) {
262*4882a593Smuzhiyun 	case SNDRV_PCM_TRIGGER_START:
263*4882a593Smuzhiyun 	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
264*4882a593Smuzhiyun 	case SNDRV_PCM_TRIGGER_RESUME:
265*4882a593Smuzhiyun 		ret = sdw_enable_stream(sdw_stream);
266*4882a593Smuzhiyun 		break;
267*4882a593Smuzhiyun 
268*4882a593Smuzhiyun 	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
269*4882a593Smuzhiyun 	case SNDRV_PCM_TRIGGER_SUSPEND:
270*4882a593Smuzhiyun 	case SNDRV_PCM_TRIGGER_STOP:
271*4882a593Smuzhiyun 		ret = sdw_disable_stream(sdw_stream);
272*4882a593Smuzhiyun 		break;
273*4882a593Smuzhiyun 	default:
274*4882a593Smuzhiyun 		ret = -EINVAL;
275*4882a593Smuzhiyun 		break;
276*4882a593Smuzhiyun 	}
277*4882a593Smuzhiyun 
278*4882a593Smuzhiyun 	if (ret)
279*4882a593Smuzhiyun 		dev_err(rtd->dev, "%s trigger %d failed: %d", __func__, cmd, ret);
280*4882a593Smuzhiyun 
281*4882a593Smuzhiyun 	return ret;
282*4882a593Smuzhiyun }
283*4882a593Smuzhiyun 
sdw_hw_free(struct snd_pcm_substream * substream)284*4882a593Smuzhiyun int sdw_hw_free(struct snd_pcm_substream *substream)
285*4882a593Smuzhiyun {
286*4882a593Smuzhiyun 	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
287*4882a593Smuzhiyun 	struct sdw_stream_runtime *sdw_stream;
288*4882a593Smuzhiyun 	struct snd_soc_dai *dai;
289*4882a593Smuzhiyun 
290*4882a593Smuzhiyun 	/* Find stream from first CPU DAI */
291*4882a593Smuzhiyun 	dai = asoc_rtd_to_cpu(rtd, 0);
292*4882a593Smuzhiyun 
293*4882a593Smuzhiyun 	sdw_stream = snd_soc_dai_get_sdw_stream(dai, substream->stream);
294*4882a593Smuzhiyun 
295*4882a593Smuzhiyun 	if (IS_ERR(sdw_stream)) {
296*4882a593Smuzhiyun 		dev_err(rtd->dev, "no stream found for DAI %s", dai->name);
297*4882a593Smuzhiyun 		return PTR_ERR(sdw_stream);
298*4882a593Smuzhiyun 	}
299*4882a593Smuzhiyun 
300*4882a593Smuzhiyun 	return sdw_deprepare_stream(sdw_stream);
301*4882a593Smuzhiyun }
302*4882a593Smuzhiyun 
sdw_shutdown(struct snd_pcm_substream * substream)303*4882a593Smuzhiyun void sdw_shutdown(struct snd_pcm_substream *substream)
304*4882a593Smuzhiyun {
305*4882a593Smuzhiyun 	sdw_shutdown_stream(substream);
306*4882a593Smuzhiyun }
307*4882a593Smuzhiyun 
308*4882a593Smuzhiyun static const struct snd_soc_ops sdw_ops = {
309*4882a593Smuzhiyun 	.startup = sdw_startup,
310*4882a593Smuzhiyun 	.prepare = sdw_prepare,
311*4882a593Smuzhiyun 	.trigger = sdw_trigger,
312*4882a593Smuzhiyun 	.hw_free = sdw_hw_free,
313*4882a593Smuzhiyun 	.shutdown = sdw_shutdown,
314*4882a593Smuzhiyun };
315*4882a593Smuzhiyun 
316*4882a593Smuzhiyun static struct sof_sdw_codec_info codec_info_list[] = {
317*4882a593Smuzhiyun 	{
318*4882a593Smuzhiyun 		.part_id = 0x700,
319*4882a593Smuzhiyun 		.direction = {true, true},
320*4882a593Smuzhiyun 		.dai_name = "rt700-aif1",
321*4882a593Smuzhiyun 		.init = sof_sdw_rt700_init,
322*4882a593Smuzhiyun 	},
323*4882a593Smuzhiyun 	{
324*4882a593Smuzhiyun 		.part_id = 0x711,
325*4882a593Smuzhiyun 		.version_id = 3,
326*4882a593Smuzhiyun 		.direction = {true, true},
327*4882a593Smuzhiyun 		.dai_name = "rt711-sdca-aif1",
328*4882a593Smuzhiyun 		.init = sof_sdw_rt711_sdca_init,
329*4882a593Smuzhiyun 		.exit = sof_sdw_rt711_sdca_exit,
330*4882a593Smuzhiyun 	},
331*4882a593Smuzhiyun 	{
332*4882a593Smuzhiyun 		.part_id = 0x711,
333*4882a593Smuzhiyun 		.version_id = 2,
334*4882a593Smuzhiyun 		.direction = {true, true},
335*4882a593Smuzhiyun 		.dai_name = "rt711-aif1",
336*4882a593Smuzhiyun 		.init = sof_sdw_rt711_init,
337*4882a593Smuzhiyun 		.exit = sof_sdw_rt711_exit,
338*4882a593Smuzhiyun 	},
339*4882a593Smuzhiyun 	{
340*4882a593Smuzhiyun 		.part_id = 0x1308,
341*4882a593Smuzhiyun 		.acpi_id = "10EC1308",
342*4882a593Smuzhiyun 		.direction = {true, false},
343*4882a593Smuzhiyun 		.dai_name = "rt1308-aif",
344*4882a593Smuzhiyun 		.ops = &sof_sdw_rt1308_i2s_ops,
345*4882a593Smuzhiyun 		.init = sof_sdw_rt1308_init,
346*4882a593Smuzhiyun 	},
347*4882a593Smuzhiyun 	{
348*4882a593Smuzhiyun 		.part_id = 0x1316,
349*4882a593Smuzhiyun 		.direction = {true, true},
350*4882a593Smuzhiyun 		.dai_name = "rt1316-aif",
351*4882a593Smuzhiyun 		.init = sof_sdw_rt1316_init,
352*4882a593Smuzhiyun 	},
353*4882a593Smuzhiyun 	{
354*4882a593Smuzhiyun 		.part_id = 0x714,
355*4882a593Smuzhiyun 		.version_id = 3,
356*4882a593Smuzhiyun 		.direction = {false, true},
357*4882a593Smuzhiyun 		.ignore_pch_dmic = true,
358*4882a593Smuzhiyun 		.dai_name = "rt715-aif2",
359*4882a593Smuzhiyun 		.init = sof_sdw_rt715_sdca_init,
360*4882a593Smuzhiyun 	},
361*4882a593Smuzhiyun 	{
362*4882a593Smuzhiyun 		.part_id = 0x715,
363*4882a593Smuzhiyun 		.version_id = 3,
364*4882a593Smuzhiyun 		.direction = {false, true},
365*4882a593Smuzhiyun 		.ignore_pch_dmic = true,
366*4882a593Smuzhiyun 		.dai_name = "rt715-aif2",
367*4882a593Smuzhiyun 		.init = sof_sdw_rt715_sdca_init,
368*4882a593Smuzhiyun 	},
369*4882a593Smuzhiyun 	{
370*4882a593Smuzhiyun 		.part_id = 0x714,
371*4882a593Smuzhiyun 		.version_id = 2,
372*4882a593Smuzhiyun 		.direction = {false, true},
373*4882a593Smuzhiyun 		.ignore_pch_dmic = true,
374*4882a593Smuzhiyun 		.dai_name = "rt715-aif2",
375*4882a593Smuzhiyun 		.init = sof_sdw_rt715_init,
376*4882a593Smuzhiyun 	},
377*4882a593Smuzhiyun 	{
378*4882a593Smuzhiyun 		.part_id = 0x715,
379*4882a593Smuzhiyun 		.version_id = 2,
380*4882a593Smuzhiyun 		.direction = {false, true},
381*4882a593Smuzhiyun 		.ignore_pch_dmic = true,
382*4882a593Smuzhiyun 		.dai_name = "rt715-aif2",
383*4882a593Smuzhiyun 		.init = sof_sdw_rt715_init,
384*4882a593Smuzhiyun 	},
385*4882a593Smuzhiyun 	{
386*4882a593Smuzhiyun 		.part_id = 0x8373,
387*4882a593Smuzhiyun 		.direction = {true, true},
388*4882a593Smuzhiyun 		.dai_name = "max98373-aif1",
389*4882a593Smuzhiyun 		.init = sof_sdw_mx8373_init,
390*4882a593Smuzhiyun 		.codec_card_late_probe = sof_sdw_mx8373_late_probe,
391*4882a593Smuzhiyun 	},
392*4882a593Smuzhiyun 	{
393*4882a593Smuzhiyun 		.part_id = 0x5682,
394*4882a593Smuzhiyun 		.direction = {true, true},
395*4882a593Smuzhiyun 		.dai_name = "rt5682-sdw",
396*4882a593Smuzhiyun 		.init = sof_sdw_rt5682_init,
397*4882a593Smuzhiyun 	},
398*4882a593Smuzhiyun };
399*4882a593Smuzhiyun 
find_codec_info_part(u64 adr)400*4882a593Smuzhiyun static inline int find_codec_info_part(u64 adr)
401*4882a593Smuzhiyun {
402*4882a593Smuzhiyun 	unsigned int part_id, sdw_version;
403*4882a593Smuzhiyun 	int i;
404*4882a593Smuzhiyun 
405*4882a593Smuzhiyun 	part_id = SDW_PART_ID(adr);
406*4882a593Smuzhiyun 	sdw_version = SDW_VERSION(adr);
407*4882a593Smuzhiyun 	for (i = 0; i < ARRAY_SIZE(codec_info_list); i++)
408*4882a593Smuzhiyun 		/*
409*4882a593Smuzhiyun 		 * A codec info is for all sdw version with the part id if
410*4882a593Smuzhiyun 		 * version_id is not specified in the codec info.
411*4882a593Smuzhiyun 		 */
412*4882a593Smuzhiyun 		if (part_id == codec_info_list[i].part_id &&
413*4882a593Smuzhiyun 		    (!codec_info_list[i].version_id ||
414*4882a593Smuzhiyun 		     sdw_version == codec_info_list[i].version_id))
415*4882a593Smuzhiyun 			return i;
416*4882a593Smuzhiyun 
417*4882a593Smuzhiyun 	return -EINVAL;
418*4882a593Smuzhiyun 
419*4882a593Smuzhiyun }
420*4882a593Smuzhiyun 
find_codec_info_acpi(const u8 * acpi_id)421*4882a593Smuzhiyun static inline int find_codec_info_acpi(const u8 *acpi_id)
422*4882a593Smuzhiyun {
423*4882a593Smuzhiyun 	int i;
424*4882a593Smuzhiyun 
425*4882a593Smuzhiyun 	if (!acpi_id[0])
426*4882a593Smuzhiyun 		return -EINVAL;
427*4882a593Smuzhiyun 
428*4882a593Smuzhiyun 	for (i = 0; i < ARRAY_SIZE(codec_info_list); i++)
429*4882a593Smuzhiyun 		if (!memcmp(codec_info_list[i].acpi_id, acpi_id,
430*4882a593Smuzhiyun 			    ACPI_ID_LEN))
431*4882a593Smuzhiyun 			break;
432*4882a593Smuzhiyun 
433*4882a593Smuzhiyun 	if (i == ARRAY_SIZE(codec_info_list))
434*4882a593Smuzhiyun 		return -EINVAL;
435*4882a593Smuzhiyun 
436*4882a593Smuzhiyun 	return i;
437*4882a593Smuzhiyun }
438*4882a593Smuzhiyun 
439*4882a593Smuzhiyun /*
440*4882a593Smuzhiyun  * get BE dailink number and CPU DAI number based on sdw link adr.
441*4882a593Smuzhiyun  * Since some sdw slaves may be aggregated, the CPU DAI number
442*4882a593Smuzhiyun  * may be larger than the number of BE dailinks.
443*4882a593Smuzhiyun  */
get_sdw_dailink_info(const struct snd_soc_acpi_link_adr * links,int * sdw_be_num,int * sdw_cpu_dai_num)444*4882a593Smuzhiyun static int get_sdw_dailink_info(const struct snd_soc_acpi_link_adr *links,
445*4882a593Smuzhiyun 				int *sdw_be_num, int *sdw_cpu_dai_num)
446*4882a593Smuzhiyun {
447*4882a593Smuzhiyun 	const struct snd_soc_acpi_link_adr *link;
448*4882a593Smuzhiyun 	bool group_visited[SDW_MAX_GROUPS];
449*4882a593Smuzhiyun 	bool no_aggregation;
450*4882a593Smuzhiyun 	int i;
451*4882a593Smuzhiyun 
452*4882a593Smuzhiyun 	no_aggregation = sof_sdw_quirk & SOF_SDW_NO_AGGREGATION;
453*4882a593Smuzhiyun 	*sdw_cpu_dai_num = 0;
454*4882a593Smuzhiyun 	*sdw_be_num  = 0;
455*4882a593Smuzhiyun 
456*4882a593Smuzhiyun 	if (!links)
457*4882a593Smuzhiyun 		return -EINVAL;
458*4882a593Smuzhiyun 
459*4882a593Smuzhiyun 	for (i = 0; i < SDW_MAX_GROUPS; i++)
460*4882a593Smuzhiyun 		group_visited[i] = false;
461*4882a593Smuzhiyun 
462*4882a593Smuzhiyun 	for (link = links; link->num_adr; link++) {
463*4882a593Smuzhiyun 		const struct snd_soc_acpi_endpoint *endpoint;
464*4882a593Smuzhiyun 		int codec_index;
465*4882a593Smuzhiyun 		int stream;
466*4882a593Smuzhiyun 		u64 adr;
467*4882a593Smuzhiyun 
468*4882a593Smuzhiyun 		adr = link->adr_d->adr;
469*4882a593Smuzhiyun 		codec_index = find_codec_info_part(adr);
470*4882a593Smuzhiyun 		if (codec_index < 0)
471*4882a593Smuzhiyun 			return codec_index;
472*4882a593Smuzhiyun 
473*4882a593Smuzhiyun 		endpoint = link->adr_d->endpoints;
474*4882a593Smuzhiyun 
475*4882a593Smuzhiyun 		/* count DAI number for playback and capture */
476*4882a593Smuzhiyun 		for_each_pcm_streams(stream) {
477*4882a593Smuzhiyun 			if (!codec_info_list[codec_index].direction[stream])
478*4882a593Smuzhiyun 				continue;
479*4882a593Smuzhiyun 
480*4882a593Smuzhiyun 			(*sdw_cpu_dai_num)++;
481*4882a593Smuzhiyun 
482*4882a593Smuzhiyun 			/* count BE for each non-aggregated slave or group */
483*4882a593Smuzhiyun 			if (!endpoint->aggregated || no_aggregation ||
484*4882a593Smuzhiyun 			    !group_visited[endpoint->group_id])
485*4882a593Smuzhiyun 				(*sdw_be_num)++;
486*4882a593Smuzhiyun 		}
487*4882a593Smuzhiyun 
488*4882a593Smuzhiyun 		if (endpoint->aggregated)
489*4882a593Smuzhiyun 			group_visited[endpoint->group_id] = true;
490*4882a593Smuzhiyun 	}
491*4882a593Smuzhiyun 
492*4882a593Smuzhiyun 	return 0;
493*4882a593Smuzhiyun }
494*4882a593Smuzhiyun 
init_dai_link(struct snd_soc_dai_link * dai_links,int be_id,char * name,int playback,int capture,struct snd_soc_dai_link_component * cpus,int cpus_num,struct snd_soc_dai_link_component * codecs,int codecs_num,int (* init)(struct snd_soc_pcm_runtime * rtd),const struct snd_soc_ops * ops)495*4882a593Smuzhiyun static void init_dai_link(struct snd_soc_dai_link *dai_links, int be_id,
496*4882a593Smuzhiyun 			  char *name, int playback, int capture,
497*4882a593Smuzhiyun 			  struct snd_soc_dai_link_component *cpus,
498*4882a593Smuzhiyun 			  int cpus_num,
499*4882a593Smuzhiyun 			  struct snd_soc_dai_link_component *codecs,
500*4882a593Smuzhiyun 			  int codecs_num,
501*4882a593Smuzhiyun 			  int (*init)(struct snd_soc_pcm_runtime *rtd),
502*4882a593Smuzhiyun 			  const struct snd_soc_ops *ops)
503*4882a593Smuzhiyun {
504*4882a593Smuzhiyun 	dai_links->id = be_id;
505*4882a593Smuzhiyun 	dai_links->name = name;
506*4882a593Smuzhiyun 	dai_links->platforms = platform_component;
507*4882a593Smuzhiyun 	dai_links->num_platforms = ARRAY_SIZE(platform_component);
508*4882a593Smuzhiyun 	dai_links->nonatomic = true;
509*4882a593Smuzhiyun 	dai_links->no_pcm = 1;
510*4882a593Smuzhiyun 	dai_links->cpus = cpus;
511*4882a593Smuzhiyun 	dai_links->num_cpus = cpus_num;
512*4882a593Smuzhiyun 	dai_links->codecs = codecs;
513*4882a593Smuzhiyun 	dai_links->num_codecs = codecs_num;
514*4882a593Smuzhiyun 	dai_links->dpcm_playback = playback;
515*4882a593Smuzhiyun 	dai_links->dpcm_capture = capture;
516*4882a593Smuzhiyun 	dai_links->init = init;
517*4882a593Smuzhiyun 	dai_links->ops = ops;
518*4882a593Smuzhiyun }
519*4882a593Smuzhiyun 
is_unique_device(const struct snd_soc_acpi_link_adr * link,unsigned int sdw_version,unsigned int mfg_id,unsigned int part_id,unsigned int class_id,int index_in_link)520*4882a593Smuzhiyun static bool is_unique_device(const struct snd_soc_acpi_link_adr *link,
521*4882a593Smuzhiyun 			     unsigned int sdw_version,
522*4882a593Smuzhiyun 			     unsigned int mfg_id,
523*4882a593Smuzhiyun 			     unsigned int part_id,
524*4882a593Smuzhiyun 			     unsigned int class_id,
525*4882a593Smuzhiyun 			     int index_in_link
526*4882a593Smuzhiyun 			    )
527*4882a593Smuzhiyun {
528*4882a593Smuzhiyun 	int i;
529*4882a593Smuzhiyun 
530*4882a593Smuzhiyun 	for (i = 0; i < link->num_adr; i++) {
531*4882a593Smuzhiyun 		unsigned int sdw1_version, mfg1_id, part1_id, class1_id;
532*4882a593Smuzhiyun 		u64 adr;
533*4882a593Smuzhiyun 
534*4882a593Smuzhiyun 		/* skip itself */
535*4882a593Smuzhiyun 		if (i == index_in_link)
536*4882a593Smuzhiyun 			continue;
537*4882a593Smuzhiyun 
538*4882a593Smuzhiyun 		adr = link->adr_d[i].adr;
539*4882a593Smuzhiyun 
540*4882a593Smuzhiyun 		sdw1_version = SDW_VERSION(adr);
541*4882a593Smuzhiyun 		mfg1_id = SDW_MFG_ID(adr);
542*4882a593Smuzhiyun 		part1_id = SDW_PART_ID(adr);
543*4882a593Smuzhiyun 		class1_id = SDW_CLASS_ID(adr);
544*4882a593Smuzhiyun 
545*4882a593Smuzhiyun 		if (sdw_version == sdw1_version &&
546*4882a593Smuzhiyun 		    mfg_id == mfg1_id &&
547*4882a593Smuzhiyun 		    part_id == part1_id &&
548*4882a593Smuzhiyun 		    class_id == class1_id)
549*4882a593Smuzhiyun 			return false;
550*4882a593Smuzhiyun 	}
551*4882a593Smuzhiyun 
552*4882a593Smuzhiyun 	return true;
553*4882a593Smuzhiyun }
554*4882a593Smuzhiyun 
create_codec_dai_name(struct device * dev,const struct snd_soc_acpi_link_adr * link,struct snd_soc_dai_link_component * codec,int offset,struct snd_soc_codec_conf * codec_conf,int codec_count,int * codec_conf_index)555*4882a593Smuzhiyun static int create_codec_dai_name(struct device *dev,
556*4882a593Smuzhiyun 				 const struct snd_soc_acpi_link_adr *link,
557*4882a593Smuzhiyun 				 struct snd_soc_dai_link_component *codec,
558*4882a593Smuzhiyun 				 int offset,
559*4882a593Smuzhiyun 				 struct snd_soc_codec_conf *codec_conf,
560*4882a593Smuzhiyun 				 int codec_count,
561*4882a593Smuzhiyun 				 int *codec_conf_index)
562*4882a593Smuzhiyun {
563*4882a593Smuzhiyun 	int i;
564*4882a593Smuzhiyun 
565*4882a593Smuzhiyun 	/* sanity check */
566*4882a593Smuzhiyun 	if (*codec_conf_index + link->num_adr > codec_count) {
567*4882a593Smuzhiyun 		dev_err(dev, "codec_conf: out-of-bounds access requested\n");
568*4882a593Smuzhiyun 		return -EINVAL;
569*4882a593Smuzhiyun 	}
570*4882a593Smuzhiyun 
571*4882a593Smuzhiyun 	for (i = 0; i < link->num_adr; i++) {
572*4882a593Smuzhiyun 		unsigned int sdw_version, unique_id, mfg_id;
573*4882a593Smuzhiyun 		unsigned int link_id, part_id, class_id;
574*4882a593Smuzhiyun 		int codec_index, comp_index;
575*4882a593Smuzhiyun 		char *codec_str;
576*4882a593Smuzhiyun 		u64 adr;
577*4882a593Smuzhiyun 
578*4882a593Smuzhiyun 		adr = link->adr_d[i].adr;
579*4882a593Smuzhiyun 
580*4882a593Smuzhiyun 		sdw_version = SDW_VERSION(adr);
581*4882a593Smuzhiyun 		link_id = SDW_DISCO_LINK_ID(adr);
582*4882a593Smuzhiyun 		unique_id = SDW_UNIQUE_ID(adr);
583*4882a593Smuzhiyun 		mfg_id = SDW_MFG_ID(adr);
584*4882a593Smuzhiyun 		part_id = SDW_PART_ID(adr);
585*4882a593Smuzhiyun 		class_id = SDW_CLASS_ID(adr);
586*4882a593Smuzhiyun 
587*4882a593Smuzhiyun 		comp_index = i + offset;
588*4882a593Smuzhiyun 		if (is_unique_device(link, sdw_version, mfg_id, part_id,
589*4882a593Smuzhiyun 				     class_id, i)) {
590*4882a593Smuzhiyun 			codec_str = "sdw:%x:%x:%x:%x";
591*4882a593Smuzhiyun 			codec[comp_index].name =
592*4882a593Smuzhiyun 				devm_kasprintf(dev, GFP_KERNEL, codec_str,
593*4882a593Smuzhiyun 					       link_id, mfg_id, part_id,
594*4882a593Smuzhiyun 					       class_id);
595*4882a593Smuzhiyun 		} else {
596*4882a593Smuzhiyun 			codec_str = "sdw:%x:%x:%x:%x:%x";
597*4882a593Smuzhiyun 			codec[comp_index].name =
598*4882a593Smuzhiyun 				devm_kasprintf(dev, GFP_KERNEL, codec_str,
599*4882a593Smuzhiyun 					       link_id, mfg_id, part_id,
600*4882a593Smuzhiyun 					       class_id, unique_id);
601*4882a593Smuzhiyun 		}
602*4882a593Smuzhiyun 
603*4882a593Smuzhiyun 		if (!codec[comp_index].name)
604*4882a593Smuzhiyun 			return -ENOMEM;
605*4882a593Smuzhiyun 
606*4882a593Smuzhiyun 		codec_index = find_codec_info_part(adr);
607*4882a593Smuzhiyun 		if (codec_index < 0)
608*4882a593Smuzhiyun 			return codec_index;
609*4882a593Smuzhiyun 
610*4882a593Smuzhiyun 		codec[comp_index].dai_name =
611*4882a593Smuzhiyun 			codec_info_list[codec_index].dai_name;
612*4882a593Smuzhiyun 
613*4882a593Smuzhiyun 		codec_conf[*codec_conf_index].dlc = codec[comp_index];
614*4882a593Smuzhiyun 		codec_conf[*codec_conf_index].name_prefix = link->adr_d[i].name_prefix;
615*4882a593Smuzhiyun 
616*4882a593Smuzhiyun 		++*codec_conf_index;
617*4882a593Smuzhiyun 	}
618*4882a593Smuzhiyun 
619*4882a593Smuzhiyun 	return 0;
620*4882a593Smuzhiyun }
621*4882a593Smuzhiyun 
set_codec_init_func(const struct snd_soc_acpi_link_adr * link,struct snd_soc_dai_link * dai_links,bool playback,int group_id)622*4882a593Smuzhiyun static int set_codec_init_func(const struct snd_soc_acpi_link_adr *link,
623*4882a593Smuzhiyun 			       struct snd_soc_dai_link *dai_links,
624*4882a593Smuzhiyun 			       bool playback, int group_id)
625*4882a593Smuzhiyun {
626*4882a593Smuzhiyun 	int i;
627*4882a593Smuzhiyun 
628*4882a593Smuzhiyun 	do {
629*4882a593Smuzhiyun 		/*
630*4882a593Smuzhiyun 		 * Initialize the codec. If codec is part of an aggregated
631*4882a593Smuzhiyun 		 * group (group_id>0), initialize all codecs belonging to
632*4882a593Smuzhiyun 		 * same group.
633*4882a593Smuzhiyun 		 */
634*4882a593Smuzhiyun 		for (i = 0; i < link->num_adr; i++) {
635*4882a593Smuzhiyun 			int codec_index;
636*4882a593Smuzhiyun 
637*4882a593Smuzhiyun 			codec_index = find_codec_info_part(link->adr_d[i].adr);
638*4882a593Smuzhiyun 
639*4882a593Smuzhiyun 			if (codec_index < 0)
640*4882a593Smuzhiyun 				return codec_index;
641*4882a593Smuzhiyun 			/* The group_id is > 0 iff the codec is aggregated */
642*4882a593Smuzhiyun 			if (link->adr_d[i].endpoints->group_id != group_id)
643*4882a593Smuzhiyun 				continue;
644*4882a593Smuzhiyun 			if (codec_info_list[codec_index].init)
645*4882a593Smuzhiyun 				codec_info_list[codec_index].init(link,
646*4882a593Smuzhiyun 						dai_links,
647*4882a593Smuzhiyun 						&codec_info_list[codec_index],
648*4882a593Smuzhiyun 						playback);
649*4882a593Smuzhiyun 		}
650*4882a593Smuzhiyun 		link++;
651*4882a593Smuzhiyun 	} while (link->mask && group_id);
652*4882a593Smuzhiyun 
653*4882a593Smuzhiyun 	return 0;
654*4882a593Smuzhiyun }
655*4882a593Smuzhiyun 
656*4882a593Smuzhiyun /*
657*4882a593Smuzhiyun  * check endpoint status in slaves and gather link ID for all slaves in
658*4882a593Smuzhiyun  * the same group to generate different CPU DAI. Now only support
659*4882a593Smuzhiyun  * one sdw link with all slaves set with only single group id.
660*4882a593Smuzhiyun  *
661*4882a593Smuzhiyun  * one slave on one sdw link with aggregated = 0
662*4882a593Smuzhiyun  * one sdw BE DAI <---> one-cpu DAI <---> one-codec DAI
663*4882a593Smuzhiyun  *
664*4882a593Smuzhiyun  * two or more slaves on one sdw link with aggregated = 0
665*4882a593Smuzhiyun  * one sdw BE DAI  <---> one-cpu DAI <---> multi-codec DAIs
666*4882a593Smuzhiyun  *
667*4882a593Smuzhiyun  * multiple links with multiple slaves with aggregated = 1
668*4882a593Smuzhiyun  * one sdw BE DAI  <---> 1 .. N CPU DAIs <----> 1 .. N codec DAIs
669*4882a593Smuzhiyun  */
get_slave_info(const struct snd_soc_acpi_link_adr * adr_link,struct device * dev,int * cpu_dai_id,int * cpu_dai_num,int * codec_num,int * group_id,bool * group_generated)670*4882a593Smuzhiyun static int get_slave_info(const struct snd_soc_acpi_link_adr *adr_link,
671*4882a593Smuzhiyun 			  struct device *dev, int *cpu_dai_id, int *cpu_dai_num,
672*4882a593Smuzhiyun 			  int *codec_num, int *group_id,
673*4882a593Smuzhiyun 			  bool *group_generated)
674*4882a593Smuzhiyun {
675*4882a593Smuzhiyun 	const struct snd_soc_acpi_adr_device *adr_d;
676*4882a593Smuzhiyun 	const struct snd_soc_acpi_link_adr *adr_next;
677*4882a593Smuzhiyun 	bool no_aggregation;
678*4882a593Smuzhiyun 	int index = 0;
679*4882a593Smuzhiyun 
680*4882a593Smuzhiyun 	no_aggregation = sof_sdw_quirk & SOF_SDW_NO_AGGREGATION;
681*4882a593Smuzhiyun 	*codec_num = adr_link->num_adr;
682*4882a593Smuzhiyun 	adr_d = adr_link->adr_d;
683*4882a593Smuzhiyun 
684*4882a593Smuzhiyun 	/* make sure the link mask has a single bit set */
685*4882a593Smuzhiyun 	if (!is_power_of_2(adr_link->mask))
686*4882a593Smuzhiyun 		return -EINVAL;
687*4882a593Smuzhiyun 
688*4882a593Smuzhiyun 	cpu_dai_id[index++] = ffs(adr_link->mask) - 1;
689*4882a593Smuzhiyun 	if (!adr_d->endpoints->aggregated || no_aggregation) {
690*4882a593Smuzhiyun 		*cpu_dai_num = 1;
691*4882a593Smuzhiyun 		*group_id = 0;
692*4882a593Smuzhiyun 		return 0;
693*4882a593Smuzhiyun 	}
694*4882a593Smuzhiyun 
695*4882a593Smuzhiyun 	*group_id = adr_d->endpoints->group_id;
696*4882a593Smuzhiyun 
697*4882a593Smuzhiyun 	/* gather other link ID of slaves in the same group */
698*4882a593Smuzhiyun 	for (adr_next = adr_link + 1; adr_next && adr_next->num_adr;
699*4882a593Smuzhiyun 		adr_next++) {
700*4882a593Smuzhiyun 		const struct snd_soc_acpi_endpoint *endpoint;
701*4882a593Smuzhiyun 
702*4882a593Smuzhiyun 		endpoint = adr_next->adr_d->endpoints;
703*4882a593Smuzhiyun 		if (!endpoint->aggregated ||
704*4882a593Smuzhiyun 		    endpoint->group_id != *group_id)
705*4882a593Smuzhiyun 			continue;
706*4882a593Smuzhiyun 
707*4882a593Smuzhiyun 		/* make sure the link mask has a single bit set */
708*4882a593Smuzhiyun 		if (!is_power_of_2(adr_next->mask))
709*4882a593Smuzhiyun 			return -EINVAL;
710*4882a593Smuzhiyun 
711*4882a593Smuzhiyun 		if (index >= SDW_MAX_CPU_DAIS) {
712*4882a593Smuzhiyun 			dev_err(dev, " cpu_dai_id array overflows");
713*4882a593Smuzhiyun 			return -EINVAL;
714*4882a593Smuzhiyun 		}
715*4882a593Smuzhiyun 
716*4882a593Smuzhiyun 		cpu_dai_id[index++] = ffs(adr_next->mask) - 1;
717*4882a593Smuzhiyun 		*codec_num += adr_next->num_adr;
718*4882a593Smuzhiyun 	}
719*4882a593Smuzhiyun 
720*4882a593Smuzhiyun 	/*
721*4882a593Smuzhiyun 	 * indicate CPU DAIs for this group have been generated
722*4882a593Smuzhiyun 	 * to avoid generating CPU DAIs for this group again.
723*4882a593Smuzhiyun 	 */
724*4882a593Smuzhiyun 	group_generated[*group_id] = true;
725*4882a593Smuzhiyun 	*cpu_dai_num = index;
726*4882a593Smuzhiyun 
727*4882a593Smuzhiyun 	return 0;
728*4882a593Smuzhiyun }
729*4882a593Smuzhiyun 
create_sdw_dailink(struct device * dev,int * be_index,struct snd_soc_dai_link * dai_links,int sdw_be_num,int sdw_cpu_dai_num,struct snd_soc_dai_link_component * cpus,const struct snd_soc_acpi_link_adr * link,int * cpu_id,bool * group_generated,struct snd_soc_codec_conf * codec_conf,int codec_count,int * codec_conf_index,bool * ignore_pch_dmic)730*4882a593Smuzhiyun static int create_sdw_dailink(struct device *dev, int *be_index,
731*4882a593Smuzhiyun 			      struct snd_soc_dai_link *dai_links,
732*4882a593Smuzhiyun 			      int sdw_be_num, int sdw_cpu_dai_num,
733*4882a593Smuzhiyun 			      struct snd_soc_dai_link_component *cpus,
734*4882a593Smuzhiyun 			      const struct snd_soc_acpi_link_adr *link,
735*4882a593Smuzhiyun 			      int *cpu_id, bool *group_generated,
736*4882a593Smuzhiyun 			      struct snd_soc_codec_conf *codec_conf,
737*4882a593Smuzhiyun 			      int codec_count,
738*4882a593Smuzhiyun 			      int *codec_conf_index,
739*4882a593Smuzhiyun 			      bool *ignore_pch_dmic)
740*4882a593Smuzhiyun {
741*4882a593Smuzhiyun 	const struct snd_soc_acpi_link_adr *link_next;
742*4882a593Smuzhiyun 	struct snd_soc_dai_link_component *codecs;
743*4882a593Smuzhiyun 	int cpu_dai_id[SDW_MAX_CPU_DAIS];
744*4882a593Smuzhiyun 	int cpu_dai_num, cpu_dai_index;
745*4882a593Smuzhiyun 	unsigned int group_id;
746*4882a593Smuzhiyun 	int codec_idx = 0;
747*4882a593Smuzhiyun 	int i = 0, j = 0;
748*4882a593Smuzhiyun 	int codec_index;
749*4882a593Smuzhiyun 	int codec_num;
750*4882a593Smuzhiyun 	int stream;
751*4882a593Smuzhiyun 	int ret;
752*4882a593Smuzhiyun 	int k;
753*4882a593Smuzhiyun 
754*4882a593Smuzhiyun 	ret = get_slave_info(link, dev, cpu_dai_id, &cpu_dai_num, &codec_num,
755*4882a593Smuzhiyun 			     &group_id, group_generated);
756*4882a593Smuzhiyun 	if (ret)
757*4882a593Smuzhiyun 		return ret;
758*4882a593Smuzhiyun 
759*4882a593Smuzhiyun 	codecs = devm_kcalloc(dev, codec_num, sizeof(*codecs), GFP_KERNEL);
760*4882a593Smuzhiyun 	if (!codecs)
761*4882a593Smuzhiyun 		return -ENOMEM;
762*4882a593Smuzhiyun 
763*4882a593Smuzhiyun 	/* generate codec name on different links in the same group */
764*4882a593Smuzhiyun 	for (link_next = link; link_next && link_next->num_adr &&
765*4882a593Smuzhiyun 	     i < cpu_dai_num; link_next++) {
766*4882a593Smuzhiyun 		const struct snd_soc_acpi_endpoint *endpoints;
767*4882a593Smuzhiyun 
768*4882a593Smuzhiyun 		endpoints = link_next->adr_d->endpoints;
769*4882a593Smuzhiyun 		if (group_id && (!endpoints->aggregated ||
770*4882a593Smuzhiyun 				 endpoints->group_id != group_id))
771*4882a593Smuzhiyun 			continue;
772*4882a593Smuzhiyun 
773*4882a593Smuzhiyun 		/* skip the link excluded by this processed group */
774*4882a593Smuzhiyun 		if (cpu_dai_id[i] != ffs(link_next->mask) - 1)
775*4882a593Smuzhiyun 			continue;
776*4882a593Smuzhiyun 
777*4882a593Smuzhiyun 		ret = create_codec_dai_name(dev, link_next, codecs, codec_idx,
778*4882a593Smuzhiyun 					    codec_conf, codec_count, codec_conf_index);
779*4882a593Smuzhiyun 		if (ret < 0)
780*4882a593Smuzhiyun 			return ret;
781*4882a593Smuzhiyun 
782*4882a593Smuzhiyun 		/* check next link to create codec dai in the processed group */
783*4882a593Smuzhiyun 		i++;
784*4882a593Smuzhiyun 		codec_idx += link_next->num_adr;
785*4882a593Smuzhiyun 	}
786*4882a593Smuzhiyun 
787*4882a593Smuzhiyun 	/* find codec info to create BE DAI */
788*4882a593Smuzhiyun 	codec_index = find_codec_info_part(link->adr_d[0].adr);
789*4882a593Smuzhiyun 	if (codec_index < 0)
790*4882a593Smuzhiyun 		return codec_index;
791*4882a593Smuzhiyun 
792*4882a593Smuzhiyun 	if (codec_info_list[codec_index].ignore_pch_dmic)
793*4882a593Smuzhiyun 		*ignore_pch_dmic = true;
794*4882a593Smuzhiyun 
795*4882a593Smuzhiyun 	cpu_dai_index = *cpu_id;
796*4882a593Smuzhiyun 	for_each_pcm_streams(stream) {
797*4882a593Smuzhiyun 		char *name, *cpu_name;
798*4882a593Smuzhiyun 		int playback, capture;
799*4882a593Smuzhiyun 		static const char * const sdw_stream_name[] = {
800*4882a593Smuzhiyun 			"SDW%d-Playback",
801*4882a593Smuzhiyun 			"SDW%d-Capture",
802*4882a593Smuzhiyun 		};
803*4882a593Smuzhiyun 
804*4882a593Smuzhiyun 		if (!codec_info_list[codec_index].direction[stream])
805*4882a593Smuzhiyun 			continue;
806*4882a593Smuzhiyun 
807*4882a593Smuzhiyun 		/* create stream name according to first link id */
808*4882a593Smuzhiyun 		name = devm_kasprintf(dev, GFP_KERNEL,
809*4882a593Smuzhiyun 				      sdw_stream_name[stream], cpu_dai_id[0]);
810*4882a593Smuzhiyun 		if (!name)
811*4882a593Smuzhiyun 			return -ENOMEM;
812*4882a593Smuzhiyun 
813*4882a593Smuzhiyun 		/*
814*4882a593Smuzhiyun 		 * generate CPU DAI name base on the sdw link ID and
815*4882a593Smuzhiyun 		 * PIN ID with offset of 2 according to sdw dai driver.
816*4882a593Smuzhiyun 		 */
817*4882a593Smuzhiyun 		for (k = 0; k < cpu_dai_num; k++) {
818*4882a593Smuzhiyun 			cpu_name = devm_kasprintf(dev, GFP_KERNEL,
819*4882a593Smuzhiyun 						  "SDW%d Pin%d", cpu_dai_id[k],
820*4882a593Smuzhiyun 						  j + SDW_INTEL_BIDIR_PDI_BASE);
821*4882a593Smuzhiyun 			if (!cpu_name)
822*4882a593Smuzhiyun 				return -ENOMEM;
823*4882a593Smuzhiyun 
824*4882a593Smuzhiyun 			if (cpu_dai_index >= sdw_cpu_dai_num) {
825*4882a593Smuzhiyun 				dev_err(dev, "invalid cpu dai index %d",
826*4882a593Smuzhiyun 					cpu_dai_index);
827*4882a593Smuzhiyun 				return -EINVAL;
828*4882a593Smuzhiyun 			}
829*4882a593Smuzhiyun 
830*4882a593Smuzhiyun 			cpus[cpu_dai_index++].dai_name = cpu_name;
831*4882a593Smuzhiyun 		}
832*4882a593Smuzhiyun 
833*4882a593Smuzhiyun 		if (*be_index >= sdw_be_num) {
834*4882a593Smuzhiyun 			dev_err(dev, " invalid be dai index %d", *be_index);
835*4882a593Smuzhiyun 			return -EINVAL;
836*4882a593Smuzhiyun 		}
837*4882a593Smuzhiyun 
838*4882a593Smuzhiyun 		if (*cpu_id >= sdw_cpu_dai_num) {
839*4882a593Smuzhiyun 			dev_err(dev, " invalid cpu dai index %d", *cpu_id);
840*4882a593Smuzhiyun 			return -EINVAL;
841*4882a593Smuzhiyun 		}
842*4882a593Smuzhiyun 
843*4882a593Smuzhiyun 		playback = (stream == SNDRV_PCM_STREAM_PLAYBACK);
844*4882a593Smuzhiyun 		capture = (stream == SNDRV_PCM_STREAM_CAPTURE);
845*4882a593Smuzhiyun 		init_dai_link(dai_links + *be_index, *be_index, name,
846*4882a593Smuzhiyun 			      playback, capture,
847*4882a593Smuzhiyun 			      cpus + *cpu_id, cpu_dai_num,
848*4882a593Smuzhiyun 			      codecs, codec_num,
849*4882a593Smuzhiyun 			      NULL, &sdw_ops);
850*4882a593Smuzhiyun 		/*
851*4882a593Smuzhiyun 		 * SoundWire DAILINKs use 'stream' functions and Bank Switch operations
852*4882a593Smuzhiyun 		 * based on wait_for_completion(), tag them as 'nonatomic'.
853*4882a593Smuzhiyun 		 */
854*4882a593Smuzhiyun 		dai_links[*be_index].nonatomic = true;
855*4882a593Smuzhiyun 
856*4882a593Smuzhiyun 		ret = set_codec_init_func(link, dai_links + (*be_index)++,
857*4882a593Smuzhiyun 					  playback, group_id);
858*4882a593Smuzhiyun 		if (ret < 0) {
859*4882a593Smuzhiyun 			dev_err(dev, "failed to init codec %d", codec_index);
860*4882a593Smuzhiyun 			return ret;
861*4882a593Smuzhiyun 		}
862*4882a593Smuzhiyun 
863*4882a593Smuzhiyun 		*cpu_id += cpu_dai_num;
864*4882a593Smuzhiyun 		j++;
865*4882a593Smuzhiyun 	}
866*4882a593Smuzhiyun 
867*4882a593Smuzhiyun 	return 0;
868*4882a593Smuzhiyun }
869*4882a593Smuzhiyun 
870*4882a593Smuzhiyun /*
871*4882a593Smuzhiyun  * DAI link ID of SSP & DMIC & HDMI are based on last
872*4882a593Smuzhiyun  * link ID used by sdw link. Since be_id may be changed
873*4882a593Smuzhiyun  * in init func of sdw codec, it is not equal to be_id
874*4882a593Smuzhiyun  */
get_next_be_id(struct snd_soc_dai_link * links,int be_id)875*4882a593Smuzhiyun static inline int get_next_be_id(struct snd_soc_dai_link *links,
876*4882a593Smuzhiyun 				 int be_id)
877*4882a593Smuzhiyun {
878*4882a593Smuzhiyun 	return links[be_id - 1].id + 1;
879*4882a593Smuzhiyun }
880*4882a593Smuzhiyun 
881*4882a593Smuzhiyun #define IDISP_CODEC_MASK	0x4
882*4882a593Smuzhiyun 
sof_card_codec_conf_alloc(struct device * dev,struct snd_soc_acpi_mach_params * mach_params,struct snd_soc_codec_conf ** codec_conf,int * codec_conf_count)883*4882a593Smuzhiyun static int sof_card_codec_conf_alloc(struct device *dev,
884*4882a593Smuzhiyun 				     struct snd_soc_acpi_mach_params *mach_params,
885*4882a593Smuzhiyun 				     struct snd_soc_codec_conf **codec_conf,
886*4882a593Smuzhiyun 				     int *codec_conf_count)
887*4882a593Smuzhiyun {
888*4882a593Smuzhiyun 	const struct snd_soc_acpi_link_adr *adr_link;
889*4882a593Smuzhiyun 	struct snd_soc_codec_conf *c_conf;
890*4882a593Smuzhiyun 	int num_codecs = 0;
891*4882a593Smuzhiyun 	int i;
892*4882a593Smuzhiyun 
893*4882a593Smuzhiyun 	adr_link = mach_params->links;
894*4882a593Smuzhiyun 	if (!adr_link)
895*4882a593Smuzhiyun 		return -EINVAL;
896*4882a593Smuzhiyun 
897*4882a593Smuzhiyun 	/* generate DAI links by each sdw link */
898*4882a593Smuzhiyun 	for (; adr_link->num_adr; adr_link++) {
899*4882a593Smuzhiyun 		for (i = 0; i < adr_link->num_adr; i++) {
900*4882a593Smuzhiyun 			if (!adr_link->adr_d[i].name_prefix) {
901*4882a593Smuzhiyun 				dev_err(dev, "codec 0x%llx does not have a name prefix\n",
902*4882a593Smuzhiyun 					adr_link->adr_d[i].adr);
903*4882a593Smuzhiyun 				return -EINVAL;
904*4882a593Smuzhiyun 			}
905*4882a593Smuzhiyun 		}
906*4882a593Smuzhiyun 		num_codecs += adr_link->num_adr;
907*4882a593Smuzhiyun 	}
908*4882a593Smuzhiyun 
909*4882a593Smuzhiyun 	c_conf = devm_kzalloc(dev, num_codecs * sizeof(*c_conf), GFP_KERNEL);
910*4882a593Smuzhiyun 	if (!c_conf)
911*4882a593Smuzhiyun 		return -ENOMEM;
912*4882a593Smuzhiyun 
913*4882a593Smuzhiyun 	*codec_conf = c_conf;
914*4882a593Smuzhiyun 	*codec_conf_count = num_codecs;
915*4882a593Smuzhiyun 
916*4882a593Smuzhiyun 	return 0;
917*4882a593Smuzhiyun }
918*4882a593Smuzhiyun 
sof_card_dai_links_create(struct device * dev,struct snd_soc_acpi_mach * mach,struct snd_soc_card * card)919*4882a593Smuzhiyun static int sof_card_dai_links_create(struct device *dev,
920*4882a593Smuzhiyun 				     struct snd_soc_acpi_mach *mach,
921*4882a593Smuzhiyun 				     struct snd_soc_card *card)
922*4882a593Smuzhiyun {
923*4882a593Smuzhiyun 	int ssp_num, sdw_be_num = 0, hdmi_num = 0, dmic_num;
924*4882a593Smuzhiyun 	struct mc_private *ctx = snd_soc_card_get_drvdata(card);
925*4882a593Smuzhiyun 	struct snd_soc_dai_link_component *idisp_components;
926*4882a593Smuzhiyun 	struct snd_soc_dai_link_component *ssp_components;
927*4882a593Smuzhiyun 	struct snd_soc_acpi_mach_params *mach_params;
928*4882a593Smuzhiyun 	const struct snd_soc_acpi_link_adr *adr_link;
929*4882a593Smuzhiyun 	struct snd_soc_dai_link_component *cpus;
930*4882a593Smuzhiyun 	struct snd_soc_codec_conf *codec_conf;
931*4882a593Smuzhiyun 	bool ignore_pch_dmic = false;
932*4882a593Smuzhiyun 	int codec_conf_count;
933*4882a593Smuzhiyun 	int codec_conf_index = 0;
934*4882a593Smuzhiyun 	bool group_generated[SDW_MAX_GROUPS];
935*4882a593Smuzhiyun 	int ssp_codec_index, ssp_mask;
936*4882a593Smuzhiyun 	struct snd_soc_dai_link *links;
937*4882a593Smuzhiyun 	int num_links, link_id = 0;
938*4882a593Smuzhiyun 	char *name, *cpu_name;
939*4882a593Smuzhiyun 	int total_cpu_dai_num;
940*4882a593Smuzhiyun 	int sdw_cpu_dai_num;
941*4882a593Smuzhiyun 	int i, j, be_id = 0;
942*4882a593Smuzhiyun 	int cpu_id = 0;
943*4882a593Smuzhiyun 	int comp_num;
944*4882a593Smuzhiyun 	int ret;
945*4882a593Smuzhiyun 
946*4882a593Smuzhiyun 	mach_params = &mach->mach_params;
947*4882a593Smuzhiyun 
948*4882a593Smuzhiyun 	/* allocate codec conf, will be populated when dailinks are created */
949*4882a593Smuzhiyun 	ret = sof_card_codec_conf_alloc(dev, mach_params, &codec_conf, &codec_conf_count);
950*4882a593Smuzhiyun 	if (ret < 0)
951*4882a593Smuzhiyun 		return ret;
952*4882a593Smuzhiyun 
953*4882a593Smuzhiyun 	/* reset amp_num to ensure amp_num++ starts from 0 in each probe */
954*4882a593Smuzhiyun 	for (i = 0; i < ARRAY_SIZE(codec_info_list); i++)
955*4882a593Smuzhiyun 		codec_info_list[i].amp_num = 0;
956*4882a593Smuzhiyun 
957*4882a593Smuzhiyun 	if (sof_sdw_quirk & SOF_SDW_TGL_HDMI)
958*4882a593Smuzhiyun 		hdmi_num = SOF_TGL_HDMI_COUNT;
959*4882a593Smuzhiyun 	else
960*4882a593Smuzhiyun 		hdmi_num = SOF_PRE_TGL_HDMI_COUNT;
961*4882a593Smuzhiyun 
962*4882a593Smuzhiyun 	ssp_mask = SOF_SSP_GET_PORT(sof_sdw_quirk);
963*4882a593Smuzhiyun 	/*
964*4882a593Smuzhiyun 	 * on generic tgl platform, I2S or sdw mode is supported
965*4882a593Smuzhiyun 	 * based on board rework. A ACPI device is registered in
966*4882a593Smuzhiyun 	 * system only when I2S mode is supported, not sdw mode.
967*4882a593Smuzhiyun 	 * Here check ACPI ID to confirm I2S is supported.
968*4882a593Smuzhiyun 	 */
969*4882a593Smuzhiyun 	ssp_codec_index = find_codec_info_acpi(mach->id);
970*4882a593Smuzhiyun 	ssp_num = ssp_codec_index >= 0 ? hweight_long(ssp_mask) : 0;
971*4882a593Smuzhiyun 	comp_num = hdmi_num + ssp_num;
972*4882a593Smuzhiyun 
973*4882a593Smuzhiyun 	ret = get_sdw_dailink_info(mach_params->links,
974*4882a593Smuzhiyun 				   &sdw_be_num, &sdw_cpu_dai_num);
975*4882a593Smuzhiyun 	if (ret < 0) {
976*4882a593Smuzhiyun 		dev_err(dev, "failed to get sdw link info %d", ret);
977*4882a593Smuzhiyun 		return ret;
978*4882a593Smuzhiyun 	}
979*4882a593Smuzhiyun 
980*4882a593Smuzhiyun 	if (mach_params->codec_mask & IDISP_CODEC_MASK)
981*4882a593Smuzhiyun 		ctx->idisp_codec = true;
982*4882a593Smuzhiyun 
983*4882a593Smuzhiyun 	/* enable dmic01 & dmic16k */
984*4882a593Smuzhiyun 	dmic_num = (sof_sdw_quirk & SOF_SDW_PCH_DMIC || mach_params->dmic_num) ? 2 : 0;
985*4882a593Smuzhiyun 	comp_num += dmic_num;
986*4882a593Smuzhiyun 
987*4882a593Smuzhiyun 	dev_dbg(dev, "sdw %d, ssp %d, dmic %d, hdmi %d", sdw_be_num, ssp_num,
988*4882a593Smuzhiyun 		dmic_num, ctx->idisp_codec ? hdmi_num : 0);
989*4882a593Smuzhiyun 
990*4882a593Smuzhiyun 	/* allocate BE dailinks */
991*4882a593Smuzhiyun 	num_links = comp_num + sdw_be_num;
992*4882a593Smuzhiyun 	links = devm_kcalloc(dev, num_links, sizeof(*links), GFP_KERNEL);
993*4882a593Smuzhiyun 
994*4882a593Smuzhiyun 	/* allocated CPU DAIs */
995*4882a593Smuzhiyun 	total_cpu_dai_num = comp_num + sdw_cpu_dai_num;
996*4882a593Smuzhiyun 	cpus = devm_kcalloc(dev, total_cpu_dai_num, sizeof(*cpus),
997*4882a593Smuzhiyun 			    GFP_KERNEL);
998*4882a593Smuzhiyun 
999*4882a593Smuzhiyun 	if (!links || !cpus)
1000*4882a593Smuzhiyun 		return -ENOMEM;
1001*4882a593Smuzhiyun 
1002*4882a593Smuzhiyun 	/* SDW */
1003*4882a593Smuzhiyun 	if (!sdw_be_num)
1004*4882a593Smuzhiyun 		goto SSP;
1005*4882a593Smuzhiyun 
1006*4882a593Smuzhiyun 	adr_link = mach_params->links;
1007*4882a593Smuzhiyun 	if (!adr_link)
1008*4882a593Smuzhiyun 		return -EINVAL;
1009*4882a593Smuzhiyun 
1010*4882a593Smuzhiyun 	/*
1011*4882a593Smuzhiyun 	 * SoundWire Slaves aggregated in the same group may be
1012*4882a593Smuzhiyun 	 * located on different hardware links. Clear array to indicate
1013*4882a593Smuzhiyun 	 * CPU DAIs for this group have not been generated.
1014*4882a593Smuzhiyun 	 */
1015*4882a593Smuzhiyun 	for (i = 0; i < SDW_MAX_GROUPS; i++)
1016*4882a593Smuzhiyun 		group_generated[i] = false;
1017*4882a593Smuzhiyun 
1018*4882a593Smuzhiyun 	/* generate DAI links by each sdw link */
1019*4882a593Smuzhiyun 	for (; adr_link->num_adr; adr_link++) {
1020*4882a593Smuzhiyun 		const struct snd_soc_acpi_endpoint *endpoint;
1021*4882a593Smuzhiyun 
1022*4882a593Smuzhiyun 		endpoint = adr_link->adr_d->endpoints;
1023*4882a593Smuzhiyun 		if (endpoint->aggregated && !endpoint->group_id) {
1024*4882a593Smuzhiyun 			dev_err(dev, "invalid group id on link %x",
1025*4882a593Smuzhiyun 				adr_link->mask);
1026*4882a593Smuzhiyun 			continue;
1027*4882a593Smuzhiyun 		}
1028*4882a593Smuzhiyun 
1029*4882a593Smuzhiyun 		/* this group has been generated */
1030*4882a593Smuzhiyun 		if (endpoint->aggregated &&
1031*4882a593Smuzhiyun 		    group_generated[endpoint->group_id])
1032*4882a593Smuzhiyun 			continue;
1033*4882a593Smuzhiyun 
1034*4882a593Smuzhiyun 		ret = create_sdw_dailink(dev, &be_id, links, sdw_be_num,
1035*4882a593Smuzhiyun 					 sdw_cpu_dai_num, cpus, adr_link,
1036*4882a593Smuzhiyun 					 &cpu_id, group_generated,
1037*4882a593Smuzhiyun 					 codec_conf, codec_conf_count,
1038*4882a593Smuzhiyun 					 &codec_conf_index,
1039*4882a593Smuzhiyun 					 &ignore_pch_dmic);
1040*4882a593Smuzhiyun 		if (ret < 0) {
1041*4882a593Smuzhiyun 			dev_err(dev, "failed to create dai link %d", be_id);
1042*4882a593Smuzhiyun 			return -ENOMEM;
1043*4882a593Smuzhiyun 		}
1044*4882a593Smuzhiyun 	}
1045*4882a593Smuzhiyun 
1046*4882a593Smuzhiyun 	/* non-sdw DAI follows sdw DAI */
1047*4882a593Smuzhiyun 	link_id = be_id;
1048*4882a593Smuzhiyun 
1049*4882a593Smuzhiyun 	/* get BE ID for non-sdw DAI */
1050*4882a593Smuzhiyun 	be_id = get_next_be_id(links, be_id);
1051*4882a593Smuzhiyun 
1052*4882a593Smuzhiyun SSP:
1053*4882a593Smuzhiyun 	/* SSP */
1054*4882a593Smuzhiyun 	if (!ssp_num)
1055*4882a593Smuzhiyun 		goto DMIC;
1056*4882a593Smuzhiyun 
1057*4882a593Smuzhiyun 	for (i = 0, j = 0; ssp_mask; i++, ssp_mask >>= 1) {
1058*4882a593Smuzhiyun 		struct sof_sdw_codec_info *info;
1059*4882a593Smuzhiyun 		int playback, capture;
1060*4882a593Smuzhiyun 		char *codec_name;
1061*4882a593Smuzhiyun 
1062*4882a593Smuzhiyun 		if (!(ssp_mask & 0x1))
1063*4882a593Smuzhiyun 			continue;
1064*4882a593Smuzhiyun 
1065*4882a593Smuzhiyun 		name = devm_kasprintf(dev, GFP_KERNEL,
1066*4882a593Smuzhiyun 				      "SSP%d-Codec", i);
1067*4882a593Smuzhiyun 		if (!name)
1068*4882a593Smuzhiyun 			return -ENOMEM;
1069*4882a593Smuzhiyun 
1070*4882a593Smuzhiyun 		cpu_name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d Pin", i);
1071*4882a593Smuzhiyun 		if (!cpu_name)
1072*4882a593Smuzhiyun 			return -ENOMEM;
1073*4882a593Smuzhiyun 
1074*4882a593Smuzhiyun 		ssp_components = devm_kzalloc(dev, sizeof(*ssp_components),
1075*4882a593Smuzhiyun 					      GFP_KERNEL);
1076*4882a593Smuzhiyun 		if (!ssp_components)
1077*4882a593Smuzhiyun 			return -ENOMEM;
1078*4882a593Smuzhiyun 
1079*4882a593Smuzhiyun 		info = &codec_info_list[ssp_codec_index];
1080*4882a593Smuzhiyun 		codec_name = devm_kasprintf(dev, GFP_KERNEL, "i2c-%s:0%d",
1081*4882a593Smuzhiyun 					    info->acpi_id, j++);
1082*4882a593Smuzhiyun 		if (!codec_name)
1083*4882a593Smuzhiyun 			return -ENOMEM;
1084*4882a593Smuzhiyun 
1085*4882a593Smuzhiyun 		ssp_components->name = codec_name;
1086*4882a593Smuzhiyun 		ssp_components->dai_name = info->dai_name;
1087*4882a593Smuzhiyun 		cpus[cpu_id].dai_name = cpu_name;
1088*4882a593Smuzhiyun 
1089*4882a593Smuzhiyun 		playback = info->direction[SNDRV_PCM_STREAM_PLAYBACK];
1090*4882a593Smuzhiyun 		capture = info->direction[SNDRV_PCM_STREAM_CAPTURE];
1091*4882a593Smuzhiyun 		init_dai_link(links + link_id, be_id, name,
1092*4882a593Smuzhiyun 			      playback, capture,
1093*4882a593Smuzhiyun 			      cpus + cpu_id, 1,
1094*4882a593Smuzhiyun 			      ssp_components, 1,
1095*4882a593Smuzhiyun 			      NULL, info->ops);
1096*4882a593Smuzhiyun 
1097*4882a593Smuzhiyun 		ret = info->init(NULL, links + link_id, info, 0);
1098*4882a593Smuzhiyun 		if (ret < 0)
1099*4882a593Smuzhiyun 			return ret;
1100*4882a593Smuzhiyun 
1101*4882a593Smuzhiyun 		INC_ID(be_id, cpu_id, link_id);
1102*4882a593Smuzhiyun 	}
1103*4882a593Smuzhiyun 
1104*4882a593Smuzhiyun DMIC:
1105*4882a593Smuzhiyun 	/* dmic */
1106*4882a593Smuzhiyun 	if (dmic_num > 0) {
1107*4882a593Smuzhiyun 		if (ignore_pch_dmic) {
1108*4882a593Smuzhiyun 			dev_warn(dev, "Ignoring PCH DMIC\n");
1109*4882a593Smuzhiyun 			goto HDMI;
1110*4882a593Smuzhiyun 		}
1111*4882a593Smuzhiyun 		cpus[cpu_id].dai_name = "DMIC01 Pin";
1112*4882a593Smuzhiyun 		init_dai_link(links + link_id, be_id, "dmic01",
1113*4882a593Smuzhiyun 			      0, 1, // DMIC only supports capture
1114*4882a593Smuzhiyun 			      cpus + cpu_id, 1,
1115*4882a593Smuzhiyun 			      dmic_component, 1,
1116*4882a593Smuzhiyun 			      sof_sdw_dmic_init, NULL);
1117*4882a593Smuzhiyun 		INC_ID(be_id, cpu_id, link_id);
1118*4882a593Smuzhiyun 
1119*4882a593Smuzhiyun 		cpus[cpu_id].dai_name = "DMIC16k Pin";
1120*4882a593Smuzhiyun 		init_dai_link(links + link_id, be_id, "dmic16k",
1121*4882a593Smuzhiyun 			      0, 1, // DMIC only supports capture
1122*4882a593Smuzhiyun 			      cpus + cpu_id, 1,
1123*4882a593Smuzhiyun 			      dmic_component, 1,
1124*4882a593Smuzhiyun 			      /* don't call sof_sdw_dmic_init() twice */
1125*4882a593Smuzhiyun 			      NULL, NULL);
1126*4882a593Smuzhiyun 		INC_ID(be_id, cpu_id, link_id);
1127*4882a593Smuzhiyun 	}
1128*4882a593Smuzhiyun 
1129*4882a593Smuzhiyun HDMI:
1130*4882a593Smuzhiyun 	/* HDMI */
1131*4882a593Smuzhiyun 	if (hdmi_num > 0) {
1132*4882a593Smuzhiyun 		idisp_components = devm_kcalloc(dev, hdmi_num,
1133*4882a593Smuzhiyun 						sizeof(*idisp_components),
1134*4882a593Smuzhiyun 						GFP_KERNEL);
1135*4882a593Smuzhiyun 		if (!idisp_components)
1136*4882a593Smuzhiyun 			return -ENOMEM;
1137*4882a593Smuzhiyun 	}
1138*4882a593Smuzhiyun 
1139*4882a593Smuzhiyun 	for (i = 0; i < hdmi_num; i++) {
1140*4882a593Smuzhiyun 		name = devm_kasprintf(dev, GFP_KERNEL,
1141*4882a593Smuzhiyun 				      "iDisp%d", i + 1);
1142*4882a593Smuzhiyun 		if (!name)
1143*4882a593Smuzhiyun 			return -ENOMEM;
1144*4882a593Smuzhiyun 
1145*4882a593Smuzhiyun 		if (ctx->idisp_codec) {
1146*4882a593Smuzhiyun 			idisp_components[i].name = "ehdaudio0D2";
1147*4882a593Smuzhiyun 			idisp_components[i].dai_name = devm_kasprintf(dev,
1148*4882a593Smuzhiyun 								      GFP_KERNEL,
1149*4882a593Smuzhiyun 								      "intel-hdmi-hifi%d",
1150*4882a593Smuzhiyun 								      i + 1);
1151*4882a593Smuzhiyun 			if (!idisp_components[i].dai_name)
1152*4882a593Smuzhiyun 				return -ENOMEM;
1153*4882a593Smuzhiyun 		} else {
1154*4882a593Smuzhiyun 			idisp_components[i].name = "snd-soc-dummy";
1155*4882a593Smuzhiyun 			idisp_components[i].dai_name = "snd-soc-dummy-dai";
1156*4882a593Smuzhiyun 		}
1157*4882a593Smuzhiyun 
1158*4882a593Smuzhiyun 		cpu_name = devm_kasprintf(dev, GFP_KERNEL,
1159*4882a593Smuzhiyun 					  "iDisp%d Pin", i + 1);
1160*4882a593Smuzhiyun 		if (!cpu_name)
1161*4882a593Smuzhiyun 			return -ENOMEM;
1162*4882a593Smuzhiyun 
1163*4882a593Smuzhiyun 		cpus[cpu_id].dai_name = cpu_name;
1164*4882a593Smuzhiyun 		init_dai_link(links + link_id, be_id, name,
1165*4882a593Smuzhiyun 			      1, 0, // HDMI only supports playback
1166*4882a593Smuzhiyun 			      cpus + cpu_id, 1,
1167*4882a593Smuzhiyun 			      idisp_components + i, 1,
1168*4882a593Smuzhiyun 			      sof_sdw_hdmi_init, NULL);
1169*4882a593Smuzhiyun 		INC_ID(be_id, cpu_id, link_id);
1170*4882a593Smuzhiyun 	}
1171*4882a593Smuzhiyun 
1172*4882a593Smuzhiyun 	card->dai_link = links;
1173*4882a593Smuzhiyun 	card->num_links = num_links;
1174*4882a593Smuzhiyun 
1175*4882a593Smuzhiyun 	card->codec_conf = codec_conf;
1176*4882a593Smuzhiyun 	card->num_configs = codec_conf_count;
1177*4882a593Smuzhiyun 
1178*4882a593Smuzhiyun 	return 0;
1179*4882a593Smuzhiyun }
1180*4882a593Smuzhiyun 
sof_sdw_card_late_probe(struct snd_soc_card * card)1181*4882a593Smuzhiyun static int sof_sdw_card_late_probe(struct snd_soc_card *card)
1182*4882a593Smuzhiyun {
1183*4882a593Smuzhiyun 	int i, ret;
1184*4882a593Smuzhiyun 
1185*4882a593Smuzhiyun 	for (i = 0; i < ARRAY_SIZE(codec_info_list); i++) {
1186*4882a593Smuzhiyun 		if (!codec_info_list[i].late_probe)
1187*4882a593Smuzhiyun 			continue;
1188*4882a593Smuzhiyun 
1189*4882a593Smuzhiyun 		ret = codec_info_list[i].codec_card_late_probe(card);
1190*4882a593Smuzhiyun 		if (ret < 0)
1191*4882a593Smuzhiyun 			return ret;
1192*4882a593Smuzhiyun 	}
1193*4882a593Smuzhiyun 
1194*4882a593Smuzhiyun 	return sof_sdw_hdmi_card_late_probe(card);
1195*4882a593Smuzhiyun }
1196*4882a593Smuzhiyun 
1197*4882a593Smuzhiyun /* SoC card */
1198*4882a593Smuzhiyun static const char sdw_card_long_name[] = "Intel Soundwire SOF";
1199*4882a593Smuzhiyun 
1200*4882a593Smuzhiyun static struct snd_soc_card card_sof_sdw = {
1201*4882a593Smuzhiyun 	.name = "soundwire",
1202*4882a593Smuzhiyun 	.owner = THIS_MODULE,
1203*4882a593Smuzhiyun 	.late_probe = sof_sdw_card_late_probe,
1204*4882a593Smuzhiyun };
1205*4882a593Smuzhiyun 
mc_probe(struct platform_device * pdev)1206*4882a593Smuzhiyun static int mc_probe(struct platform_device *pdev)
1207*4882a593Smuzhiyun {
1208*4882a593Smuzhiyun 	struct snd_soc_card *card = &card_sof_sdw;
1209*4882a593Smuzhiyun 	struct snd_soc_acpi_mach *mach;
1210*4882a593Smuzhiyun 	struct mc_private *ctx;
1211*4882a593Smuzhiyun 	int amp_num = 0, i;
1212*4882a593Smuzhiyun 	int ret;
1213*4882a593Smuzhiyun 
1214*4882a593Smuzhiyun 	dev_dbg(&pdev->dev, "Entry %s\n", __func__);
1215*4882a593Smuzhiyun 
1216*4882a593Smuzhiyun 	ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL);
1217*4882a593Smuzhiyun 	if (!ctx)
1218*4882a593Smuzhiyun 		return -ENOMEM;
1219*4882a593Smuzhiyun 
1220*4882a593Smuzhiyun 	dmi_check_system(sof_sdw_quirk_table);
1221*4882a593Smuzhiyun 
1222*4882a593Smuzhiyun 	if (quirk_override != -1) {
1223*4882a593Smuzhiyun 		dev_info(&pdev->dev, "Overriding quirk 0x%lx => 0x%x\n",
1224*4882a593Smuzhiyun 			 sof_sdw_quirk, quirk_override);
1225*4882a593Smuzhiyun 		sof_sdw_quirk = quirk_override;
1226*4882a593Smuzhiyun 	}
1227*4882a593Smuzhiyun 	log_quirks(&pdev->dev);
1228*4882a593Smuzhiyun 
1229*4882a593Smuzhiyun 	INIT_LIST_HEAD(&ctx->hdmi_pcm_list);
1230*4882a593Smuzhiyun 
1231*4882a593Smuzhiyun 	card->dev = &pdev->dev;
1232*4882a593Smuzhiyun 	snd_soc_card_set_drvdata(card, ctx);
1233*4882a593Smuzhiyun 
1234*4882a593Smuzhiyun 	mach = pdev->dev.platform_data;
1235*4882a593Smuzhiyun 	ret = sof_card_dai_links_create(&pdev->dev, mach,
1236*4882a593Smuzhiyun 					card);
1237*4882a593Smuzhiyun 	if (ret < 0)
1238*4882a593Smuzhiyun 		return ret;
1239*4882a593Smuzhiyun 
1240*4882a593Smuzhiyun 	ctx->common_hdmi_codec_drv = mach->mach_params.common_hdmi_codec_drv;
1241*4882a593Smuzhiyun 
1242*4882a593Smuzhiyun 	/*
1243*4882a593Smuzhiyun 	 * the default amp_num is zero for each codec and
1244*4882a593Smuzhiyun 	 * amp_num will only be increased for active amp
1245*4882a593Smuzhiyun 	 * codecs on used platform
1246*4882a593Smuzhiyun 	 */
1247*4882a593Smuzhiyun 	for (i = 0; i < ARRAY_SIZE(codec_info_list); i++)
1248*4882a593Smuzhiyun 		amp_num += codec_info_list[i].amp_num;
1249*4882a593Smuzhiyun 
1250*4882a593Smuzhiyun 	card->components = devm_kasprintf(card->dev, GFP_KERNEL,
1251*4882a593Smuzhiyun 					  "cfg-spk:%d cfg-amp:%d",
1252*4882a593Smuzhiyun 					  (sof_sdw_quirk & SOF_SDW_FOUR_SPK)
1253*4882a593Smuzhiyun 					  ? 4 : 2, amp_num);
1254*4882a593Smuzhiyun 	if (!card->components)
1255*4882a593Smuzhiyun 		return -ENOMEM;
1256*4882a593Smuzhiyun 
1257*4882a593Smuzhiyun 	card->long_name = sdw_card_long_name;
1258*4882a593Smuzhiyun 
1259*4882a593Smuzhiyun 	/* Register the card */
1260*4882a593Smuzhiyun 	ret = devm_snd_soc_register_card(&pdev->dev, card);
1261*4882a593Smuzhiyun 	if (ret) {
1262*4882a593Smuzhiyun 		dev_err(card->dev, "snd_soc_register_card failed %d\n", ret);
1263*4882a593Smuzhiyun 		return ret;
1264*4882a593Smuzhiyun 	}
1265*4882a593Smuzhiyun 
1266*4882a593Smuzhiyun 	platform_set_drvdata(pdev, card);
1267*4882a593Smuzhiyun 
1268*4882a593Smuzhiyun 	return ret;
1269*4882a593Smuzhiyun }
1270*4882a593Smuzhiyun 
mc_remove(struct platform_device * pdev)1271*4882a593Smuzhiyun static int mc_remove(struct platform_device *pdev)
1272*4882a593Smuzhiyun {
1273*4882a593Smuzhiyun 	struct snd_soc_card *card = platform_get_drvdata(pdev);
1274*4882a593Smuzhiyun 	struct snd_soc_dai_link *link;
1275*4882a593Smuzhiyun 	int ret;
1276*4882a593Smuzhiyun 	int i, j;
1277*4882a593Smuzhiyun 
1278*4882a593Smuzhiyun 	for (i = 0; i < ARRAY_SIZE(codec_info_list); i++) {
1279*4882a593Smuzhiyun 		if (!codec_info_list[i].exit)
1280*4882a593Smuzhiyun 			continue;
1281*4882a593Smuzhiyun 		/*
1282*4882a593Smuzhiyun 		 * We don't need to call .exit function if there is no matched
1283*4882a593Smuzhiyun 		 * dai link found.
1284*4882a593Smuzhiyun 		 */
1285*4882a593Smuzhiyun 		for_each_card_prelinks(card, j, link) {
1286*4882a593Smuzhiyun 			if (!strcmp(link->codecs[0].dai_name,
1287*4882a593Smuzhiyun 				    codec_info_list[i].dai_name)) {
1288*4882a593Smuzhiyun 				ret = codec_info_list[i].exit(&pdev->dev, link);
1289*4882a593Smuzhiyun 				if (ret)
1290*4882a593Smuzhiyun 					dev_warn(&pdev->dev,
1291*4882a593Smuzhiyun 						 "codec exit failed %d\n",
1292*4882a593Smuzhiyun 						 ret);
1293*4882a593Smuzhiyun 				break;
1294*4882a593Smuzhiyun 			}
1295*4882a593Smuzhiyun 		}
1296*4882a593Smuzhiyun 	}
1297*4882a593Smuzhiyun 
1298*4882a593Smuzhiyun 	return 0;
1299*4882a593Smuzhiyun }
1300*4882a593Smuzhiyun 
1301*4882a593Smuzhiyun static struct platform_driver sof_sdw_driver = {
1302*4882a593Smuzhiyun 	.driver = {
1303*4882a593Smuzhiyun 		.name = "sof_sdw",
1304*4882a593Smuzhiyun 		.pm = &snd_soc_pm_ops,
1305*4882a593Smuzhiyun 	},
1306*4882a593Smuzhiyun 	.probe = mc_probe,
1307*4882a593Smuzhiyun 	.remove = mc_remove,
1308*4882a593Smuzhiyun };
1309*4882a593Smuzhiyun 
1310*4882a593Smuzhiyun module_platform_driver(sof_sdw_driver);
1311*4882a593Smuzhiyun 
1312*4882a593Smuzhiyun MODULE_DESCRIPTION("ASoC SoundWire Generic Machine driver");
1313*4882a593Smuzhiyun MODULE_AUTHOR("Bard Liao <yung-chuan.liao@linux.intel.com>");
1314*4882a593Smuzhiyun MODULE_AUTHOR("Rander Wang <rander.wang@linux.intel.com>");
1315*4882a593Smuzhiyun MODULE_AUTHOR("Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>");
1316*4882a593Smuzhiyun MODULE_LICENSE("GPL v2");
1317*4882a593Smuzhiyun MODULE_ALIAS("platform:sof_sdw");
1318