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 .ident = "Google firmware",
334 .matches = {
335 DMI_MATCH(DMI_BIOS_VERSION, "Google"),
336 }
337 },
338 {}
339 }
340 },
341 {
342 .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
343 .device = 0xa0c8,
344 },
345 {
346 .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
347 .device = 0x43c8,
348 },
349 {
350 .flags = FLAG_SOF,
351 .device = 0xa0c8,
352 .codec_hid = "ESSX8336",
353 },
354 #endif
355
356 /* Elkhart Lake */
357 #if IS_ENABLED(CONFIG_SND_SOC_SOF_ELKHARTLAKE)
358 {
359 .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC,
360 .device = 0x4b55,
361 },
362 {
363 .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC,
364 .device = 0x4b58,
365 },
366 #endif
367
368 /* Meteor Lake */
369 #if IS_ENABLED(CONFIG_SND_SOC_SOF_METEORLAKE)
370 /* Meteorlake-P */
371 {
372 .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
373 .device = 0x7e28,
374 },
375 /* ArrowLake-S */
376 {
377 .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
378 .device = PCI_DEVICE_ID_INTEL_HDA_ARL_S,
379 },
380 /* ArrowLake */
381 {
382 .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
383 .device = PCI_DEVICE_ID_INTEL_HDA_ARL,
384 },
385 #endif
386
387 /* Lunar Lake */
388 #if IS_ENABLED(CONFIG_SND_SOC_SOF_LUNARLAKE)
389 /* Lunarlake-P */
390 {
391 .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
392 .device = PCI_DEVICE_ID_INTEL_HDA_LNL_P,
393 },
394 #endif
395 };
396
snd_intel_dsp_find_config(struct pci_dev * pci,const struct config_entry * table,u32 len)397 static const struct config_entry *snd_intel_dsp_find_config
398 (struct pci_dev *pci, const struct config_entry *table, u32 len)
399 {
400 u16 device;
401
402 device = pci->device;
403 for (; len > 0; len--, table++) {
404 if (table->device != device)
405 continue;
406 if (table->dmi_table && !dmi_check_system(table->dmi_table))
407 continue;
408 if (table->codec_hid[0] && !acpi_dev_present(table->codec_hid, NULL, -1))
409 continue;
410 return table;
411 }
412 return NULL;
413 }
414
snd_intel_dsp_check_dmic(struct pci_dev * pci)415 static int snd_intel_dsp_check_dmic(struct pci_dev *pci)
416 {
417 struct nhlt_acpi_table *nhlt;
418 int ret = 0;
419
420 nhlt = intel_nhlt_init(&pci->dev);
421 if (nhlt) {
422 if (intel_nhlt_get_dmic_geo(&pci->dev, nhlt))
423 ret = 1;
424 intel_nhlt_free(nhlt);
425 }
426 return ret;
427 }
428
429 #if IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL_SOUNDWIRE)
snd_intel_dsp_check_soundwire(struct pci_dev * pci)430 static int snd_intel_dsp_check_soundwire(struct pci_dev *pci)
431 {
432 struct sdw_intel_acpi_info info;
433 acpi_handle handle;
434 int ret;
435
436 handle = ACPI_HANDLE(&pci->dev);
437
438 ret = sdw_intel_acpi_scan(handle, &info);
439 if (ret < 0)
440 return ret;
441
442 return info.link_mask;
443 }
444 #else
snd_intel_dsp_check_soundwire(struct pci_dev * pci)445 static int snd_intel_dsp_check_soundwire(struct pci_dev *pci)
446 {
447 return 0;
448 }
449 #endif
450
snd_intel_dsp_driver_probe(struct pci_dev * pci)451 int snd_intel_dsp_driver_probe(struct pci_dev *pci)
452 {
453 const struct config_entry *cfg;
454
455 /* Intel vendor only */
456 if (pci->vendor != 0x8086)
457 return SND_INTEL_DSP_DRIVER_ANY;
458
459 if (dsp_driver > 0 && dsp_driver <= SND_INTEL_DSP_DRIVER_LAST)
460 return dsp_driver;
461
462 /*
463 * detect DSP by checking class/subclass/prog-id information
464 * class=04 subclass 03 prog-if 00: no DSP, use legacy driver
465 * class=04 subclass 01 prog-if 00: DSP is present
466 * (and may be required e.g. for DMIC or SSP support)
467 * class=04 subclass 03 prog-if 80: use DSP or legacy mode
468 */
469 if (pci->class == 0x040300)
470 return SND_INTEL_DSP_DRIVER_LEGACY;
471 if (pci->class != 0x040100 && pci->class != 0x040380) {
472 dev_err(&pci->dev, "Unknown PCI class/subclass/prog-if information (0x%06x) found, selecting HDAudio legacy driver\n", pci->class);
473 return SND_INTEL_DSP_DRIVER_LEGACY;
474 }
475
476 dev_info(&pci->dev, "DSP detected with PCI class/subclass/prog-if info 0x%06x\n", pci->class);
477
478 /* find the configuration for the specific device */
479 cfg = snd_intel_dsp_find_config(pci, config_table, ARRAY_SIZE(config_table));
480 if (!cfg)
481 return SND_INTEL_DSP_DRIVER_ANY;
482
483 if (cfg->flags & FLAG_SOF) {
484 if (cfg->flags & FLAG_SOF_ONLY_IF_SOUNDWIRE &&
485 snd_intel_dsp_check_soundwire(pci) > 0) {
486 dev_info(&pci->dev, "SoundWire enabled on CannonLake+ platform, using SOF driver\n");
487 return SND_INTEL_DSP_DRIVER_SOF;
488 }
489 if (cfg->flags & FLAG_SOF_ONLY_IF_DMIC &&
490 snd_intel_dsp_check_dmic(pci)) {
491 dev_info(&pci->dev, "Digital mics found on Skylake+ platform, using SOF driver\n");
492 return SND_INTEL_DSP_DRIVER_SOF;
493 }
494 if (!(cfg->flags & FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE))
495 return SND_INTEL_DSP_DRIVER_SOF;
496 }
497
498
499 if (cfg->flags & FLAG_SST) {
500 if (cfg->flags & FLAG_SST_ONLY_IF_DMIC) {
501 if (snd_intel_dsp_check_dmic(pci)) {
502 dev_info(&pci->dev, "Digital mics found on Skylake+ platform, using SST driver\n");
503 return SND_INTEL_DSP_DRIVER_SST;
504 }
505 } else {
506 return SND_INTEL_DSP_DRIVER_SST;
507 }
508 }
509
510 return SND_INTEL_DSP_DRIVER_LEGACY;
511 }
512 EXPORT_SYMBOL_GPL(snd_intel_dsp_driver_probe);
513
514 MODULE_LICENSE("GPL v2");
515 MODULE_DESCRIPTION("Intel DSP config driver");
516 MODULE_IMPORT_NS(SOUNDWIRE_INTEL_INIT);
517