1 // SPDX-License-Identifier: GPL-2.0
2 // Copyright (c) 2019 Jaroslav Kysela <perex@perex.cz>
3
4 #include <linux/acpi.h>
5 #include <linux/bits.h>
6 #include <linux/dmi.h>
7 #include <linux/module.h>
8 #include <linux/pci.h>
9 #include <linux/soundwire/sdw.h>
10 #include <linux/soundwire/sdw_intel.h>
11 #include <sound/core.h>
12 #include <sound/intel-dsp-config.h>
13 #include <sound/intel-nhlt.h>
14
15 static int dsp_driver;
16
17 module_param(dsp_driver, int, 0444);
18 MODULE_PARM_DESC(dsp_driver, "Force the DSP driver for Intel DSP (0=auto, 1=legacy, 2=SST, 3=SOF)");
19
20 #define FLAG_SST BIT(0)
21 #define FLAG_SOF BIT(1)
22 #define FLAG_SST_ONLY_IF_DMIC BIT(15)
23 #define FLAG_SOF_ONLY_IF_DMIC BIT(16)
24 #define FLAG_SOF_ONLY_IF_SOUNDWIRE BIT(17)
25
26 #define FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE (FLAG_SOF_ONLY_IF_DMIC | \
27 FLAG_SOF_ONLY_IF_SOUNDWIRE)
28
29 struct config_entry {
30 u32 flags;
31 u16 device;
32 const struct dmi_system_id *dmi_table;
33 u8 codec_hid[ACPI_ID_LEN];
34 };
35
36 /*
37 * configuration table
38 * - the order of similar PCI ID entries is important!
39 * - the first successful match will win
40 */
41 static const struct config_entry config_table[] = {
42 /* Merrifield */
43 #if IS_ENABLED(CONFIG_SND_SOC_SOF_MERRIFIELD)
44 {
45 .flags = FLAG_SOF,
46 .device = 0x119a,
47 },
48 #endif
49 /* Broxton-T */
50 #if IS_ENABLED(CONFIG_SND_SOC_SOF_APOLLOLAKE)
51 {
52 .flags = FLAG_SOF,
53 .device = 0x1a98,
54 },
55 #endif
56 /*
57 * Apollolake (Broxton-P)
58 * the legacy HDAudio driver is used except on Up Squared (SOF) and
59 * Chromebooks (SST), as well as devices based on the ES8336 codec
60 */
61 #if IS_ENABLED(CONFIG_SND_SOC_SOF_APOLLOLAKE)
62 {
63 .flags = FLAG_SOF,
64 .device = 0x5a98,
65 .dmi_table = (const struct dmi_system_id []) {
66 {
67 .ident = "Up Squared",
68 .matches = {
69 DMI_MATCH(DMI_SYS_VENDOR, "AAEON"),
70 DMI_MATCH(DMI_BOARD_NAME, "UP-APL01"),
71 }
72 },
73 {}
74 }
75 },
76 {
77 .flags = FLAG_SOF,
78 .device = 0x5a98,
79 .codec_hid = "ESSX8336",
80 },
81 #endif
82 #if IS_ENABLED(CONFIG_SND_SOC_INTEL_APL)
83 {
84 .flags = FLAG_SST,
85 .device = 0x5a98,
86 .dmi_table = (const struct dmi_system_id []) {
87 {
88 .ident = "Google Chromebooks",
89 .matches = {
90 DMI_MATCH(DMI_SYS_VENDOR, "Google"),
91 }
92 },
93 {}
94 }
95 },
96 #endif
97 /*
98 * Skylake and Kabylake use legacy HDAudio driver except for Google
99 * Chromebooks (SST)
100 */
101
102 /* Sunrise Point-LP */
103 #if IS_ENABLED(CONFIG_SND_SOC_INTEL_SKL)
104 {
105 .flags = FLAG_SST,
106 .device = 0x9d70,
107 .dmi_table = (const struct dmi_system_id []) {
108 {
109 .ident = "Google Chromebooks",
110 .matches = {
111 DMI_MATCH(DMI_SYS_VENDOR, "Google"),
112 }
113 },
114 {}
115 }
116 },
117 {
118 .flags = FLAG_SST | FLAG_SST_ONLY_IF_DMIC,
119 .device = 0x9d70,
120 },
121 #endif
122 /* Kabylake-LP */
123 #if IS_ENABLED(CONFIG_SND_SOC_INTEL_KBL)
124 {
125 .flags = FLAG_SST,
126 .device = 0x9d71,
127 .dmi_table = (const struct dmi_system_id []) {
128 {
129 .ident = "Google Chromebooks",
130 .matches = {
131 DMI_MATCH(DMI_SYS_VENDOR, "Google"),
132 }
133 },
134 {}
135 }
136 },
137 {
138 .flags = FLAG_SST | FLAG_SST_ONLY_IF_DMIC,
139 .device = 0x9d71,
140 },
141 #endif
142
143 /*
144 * Geminilake uses legacy HDAudio driver except for Google
145 * Chromebooks and devices based on the ES8336 codec
146 */
147 /* Geminilake */
148 #if IS_ENABLED(CONFIG_SND_SOC_SOF_GEMINILAKE)
149 {
150 .flags = FLAG_SOF,
151 .device = 0x3198,
152 .dmi_table = (const struct dmi_system_id []) {
153 {
154 .ident = "Google Chromebooks",
155 .matches = {
156 DMI_MATCH(DMI_SYS_VENDOR, "Google"),
157 }
158 },
159 {}
160 }
161 },
162 {
163 .flags = FLAG_SOF,
164 .device = 0x3198,
165 .codec_hid = "ESSX8336",
166 },
167 #endif
168
169 /*
170 * CoffeeLake, CannonLake, CometLake, IceLake, TigerLake use legacy
171 * HDAudio driver except for Google Chromebooks and when DMICs are
172 * present. Two cases are required since Coreboot does not expose NHLT
173 * tables.
174 *
175 * When the Chromebook quirk is not present, it's based on information
176 * that no such device exists. When the quirk is present, it could be
177 * either based on product information or a placeholder.
178 */
179
180 /* Cannonlake */
181 #if IS_ENABLED(CONFIG_SND_SOC_SOF_CANNONLAKE)
182 {
183 .flags = FLAG_SOF,
184 .device = 0x9dc8,
185 .dmi_table = (const struct dmi_system_id []) {
186 {
187 .ident = "Google Chromebooks",
188 .matches = {
189 DMI_MATCH(DMI_SYS_VENDOR, "Google"),
190 }
191 },
192 {}
193 }
194 },
195 {
196 .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
197 .device = 0x9dc8,
198 },
199 #endif
200
201 /* Coffelake */
202 #if IS_ENABLED(CONFIG_SND_SOC_SOF_COFFEELAKE)
203 {
204 .flags = FLAG_SOF,
205 .device = 0xa348,
206 .dmi_table = (const struct dmi_system_id []) {
207 {
208 .ident = "Google Chromebooks",
209 .matches = {
210 DMI_MATCH(DMI_SYS_VENDOR, "Google"),
211 }
212 },
213 {}
214 }
215 },
216 {
217 .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
218 .device = 0xa348,
219 },
220 #endif
221
222 #if IS_ENABLED(CONFIG_SND_SOC_SOF_COMETLAKE)
223 /* Cometlake-LP */
224 {
225 .flags = FLAG_SOF,
226 .device = 0x02c8,
227 .dmi_table = (const struct dmi_system_id []) {
228 {
229 .ident = "Google Chromebooks",
230 .matches = {
231 DMI_MATCH(DMI_SYS_VENDOR, "Google"),
232 }
233 },
234 {
235 .matches = {
236 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
237 DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "09C6")
238 },
239 },
240 {
241 /* early version of SKU 09C6 */
242 .matches = {
243 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
244 DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0983")
245 },
246 },
247 {}
248 }
249 },
250 {
251 .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
252 .device = 0x02c8,
253 },
254 {
255 .flags = FLAG_SOF,
256 .device = 0x02c8,
257 .codec_hid = "ESSX8336",
258 },
259 /* Cometlake-H */
260 {
261 .flags = FLAG_SOF,
262 .device = 0x06c8,
263 .dmi_table = (const struct dmi_system_id []) {
264 {
265 .matches = {
266 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
267 DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "098F"),
268 },
269 },
270 {
271 .matches = {
272 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
273 DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0990"),
274 },
275 },
276 {}
277 }
278 },
279 {
280 .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
281 .device = 0x06c8,
282 },
283 {
284 .flags = FLAG_SOF,
285 .device = 0x06c8,
286 .codec_hid = "ESSX8336",
287 },
288 #endif
289
290 /* Icelake */
291 #if IS_ENABLED(CONFIG_SND_SOC_SOF_ICELAKE)
292 {
293 .flags = FLAG_SOF,
294 .device = 0x34c8,
295 .dmi_table = (const struct dmi_system_id []) {
296 {
297 .ident = "Google Chromebooks",
298 .matches = {
299 DMI_MATCH(DMI_SYS_VENDOR, "Google"),
300 }
301 },
302 {}
303 }
304 },
305 {
306 .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
307 .device = 0x34c8,
308 },
309 #endif
310
311 /* JasperLake */
312 #if IS_ENABLED(CONFIG_SND_SOC_SOF_JASPERLAKE)
313 {
314 .flags = FLAG_SOF,
315 .device = 0x4dc8,
316 .codec_hid = "ESSX8336",
317 },
318 #endif
319
320 /* Tigerlake */
321 #if IS_ENABLED(CONFIG_SND_SOC_SOF_TIGERLAKE)
322 {
323 .flags = FLAG_SOF,
324 .device = 0xa0c8,
325 .dmi_table = (const struct dmi_system_id []) {
326 {
327 .ident = "Google Chromebooks",
328 .matches = {
329 DMI_MATCH(DMI_SYS_VENDOR, "Google"),
330 }
331 },
332 {}
333 }
334 },
335 {
336 .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
337 .device = 0xa0c8,
338 },
339 {
340 .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
341 .device = 0x43c8,
342 },
343 {
344 .flags = FLAG_SOF,
345 .device = 0xa0c8,
346 .codec_hid = "ESSX8336",
347 },
348 #endif
349
350 /* Elkhart Lake */
351 #if IS_ENABLED(CONFIG_SND_SOC_SOF_ELKHARTLAKE)
352 {
353 .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC,
354 .device = 0x4b55,
355 },
356 {
357 .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC,
358 .device = 0x4b58,
359 },
360 #endif
361
362 /* Meteor Lake */
363 #if IS_ENABLED(CONFIG_SND_SOC_SOF_METEORLAKE)
364 /* Meteorlake-P */
365 {
366 .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
367 .device = 0x7e28,
368 },
369 #endif
370
371 };
372
snd_intel_dsp_find_config(struct pci_dev * pci,const struct config_entry * table,u32 len)373 static const struct config_entry *snd_intel_dsp_find_config
374 (struct pci_dev *pci, const struct config_entry *table, u32 len)
375 {
376 u16 device;
377
378 device = pci->device;
379 for (; len > 0; len--, table++) {
380 if (table->device != device)
381 continue;
382 if (table->dmi_table && !dmi_check_system(table->dmi_table))
383 continue;
384 if (table->codec_hid[0] && !acpi_dev_present(table->codec_hid, NULL, -1))
385 continue;
386 return table;
387 }
388 return NULL;
389 }
390
snd_intel_dsp_check_dmic(struct pci_dev * pci)391 static int snd_intel_dsp_check_dmic(struct pci_dev *pci)
392 {
393 struct nhlt_acpi_table *nhlt;
394 int ret = 0;
395
396 nhlt = intel_nhlt_init(&pci->dev);
397 if (nhlt) {
398 if (intel_nhlt_get_dmic_geo(&pci->dev, nhlt))
399 ret = 1;
400 intel_nhlt_free(nhlt);
401 }
402 return ret;
403 }
404
405 #if IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL_SOUNDWIRE)
snd_intel_dsp_check_soundwire(struct pci_dev * pci)406 static int snd_intel_dsp_check_soundwire(struct pci_dev *pci)
407 {
408 struct sdw_intel_acpi_info info;
409 acpi_handle handle;
410 int ret;
411
412 handle = ACPI_HANDLE(&pci->dev);
413
414 ret = sdw_intel_acpi_scan(handle, &info);
415 if (ret < 0)
416 return ret;
417
418 return info.link_mask;
419 }
420 #else
snd_intel_dsp_check_soundwire(struct pci_dev * pci)421 static int snd_intel_dsp_check_soundwire(struct pci_dev *pci)
422 {
423 return 0;
424 }
425 #endif
426
snd_intel_dsp_driver_probe(struct pci_dev * pci)427 int snd_intel_dsp_driver_probe(struct pci_dev *pci)
428 {
429 const struct config_entry *cfg;
430
431 /* Intel vendor only */
432 if (pci->vendor != 0x8086)
433 return SND_INTEL_DSP_DRIVER_ANY;
434
435 if (dsp_driver > 0 && dsp_driver <= SND_INTEL_DSP_DRIVER_LAST)
436 return dsp_driver;
437
438 /*
439 * detect DSP by checking class/subclass/prog-id information
440 * class=04 subclass 03 prog-if 00: no DSP, use legacy driver
441 * class=04 subclass 01 prog-if 00: DSP is present
442 * (and may be required e.g. for DMIC or SSP support)
443 * class=04 subclass 03 prog-if 80: use DSP or legacy mode
444 */
445 if (pci->class == 0x040300)
446 return SND_INTEL_DSP_DRIVER_LEGACY;
447 if (pci->class != 0x040100 && pci->class != 0x040380) {
448 dev_err(&pci->dev, "Unknown PCI class/subclass/prog-if information (0x%06x) found, selecting HDAudio legacy driver\n", pci->class);
449 return SND_INTEL_DSP_DRIVER_LEGACY;
450 }
451
452 dev_info(&pci->dev, "DSP detected with PCI class/subclass/prog-if info 0x%06x\n", pci->class);
453
454 /* find the configuration for the specific device */
455 cfg = snd_intel_dsp_find_config(pci, config_table, ARRAY_SIZE(config_table));
456 if (!cfg)
457 return SND_INTEL_DSP_DRIVER_ANY;
458
459 if (cfg->flags & FLAG_SOF) {
460 if (cfg->flags & FLAG_SOF_ONLY_IF_SOUNDWIRE &&
461 snd_intel_dsp_check_soundwire(pci) > 0) {
462 dev_info(&pci->dev, "SoundWire enabled on CannonLake+ platform, using SOF driver\n");
463 return SND_INTEL_DSP_DRIVER_SOF;
464 }
465 if (cfg->flags & FLAG_SOF_ONLY_IF_DMIC &&
466 snd_intel_dsp_check_dmic(pci)) {
467 dev_info(&pci->dev, "Digital mics found on Skylake+ platform, using SOF driver\n");
468 return SND_INTEL_DSP_DRIVER_SOF;
469 }
470 if (!(cfg->flags & FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE))
471 return SND_INTEL_DSP_DRIVER_SOF;
472 }
473
474
475 if (cfg->flags & FLAG_SST) {
476 if (cfg->flags & FLAG_SST_ONLY_IF_DMIC) {
477 if (snd_intel_dsp_check_dmic(pci)) {
478 dev_info(&pci->dev, "Digital mics found on Skylake+ platform, using SST driver\n");
479 return SND_INTEL_DSP_DRIVER_SST;
480 }
481 } else {
482 return SND_INTEL_DSP_DRIVER_SST;
483 }
484 }
485
486 return SND_INTEL_DSP_DRIVER_LEGACY;
487 }
488 EXPORT_SYMBOL_GPL(snd_intel_dsp_driver_probe);
489
490 MODULE_LICENSE("GPL v2");
491 MODULE_DESCRIPTION("Intel DSP config driver");
492 MODULE_IMPORT_NS(SOUNDWIRE_INTEL_INIT);
493