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