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 u8 acpi_hid[ACPI_ID_LEN];
33 const struct dmi_system_id *dmi_table;
34 u8 codec_hid[ACPI_ID_LEN];
35 };
36
37 /*
38 * configuration table
39 * - the order of similar PCI ID entries is important!
40 * - the first successful match will win
41 */
42 static const struct config_entry config_table[] = {
43 /* Merrifield */
44 #if IS_ENABLED(CONFIG_SND_SOC_SOF_MERRIFIELD)
45 {
46 .flags = FLAG_SOF,
47 .device = 0x119a,
48 },
49 #endif
50 /* Broxton-T */
51 #if IS_ENABLED(CONFIG_SND_SOC_SOF_APOLLOLAKE)
52 {
53 .flags = FLAG_SOF,
54 .device = 0x1a98,
55 },
56 #endif
57 /*
58 * Apollolake (Broxton-P)
59 * the legacy HDAudio driver is used except on Up Squared (SOF) and
60 * Chromebooks (SST), as well as devices based on the ES8336 codec
61 */
62 #if IS_ENABLED(CONFIG_SND_SOC_SOF_APOLLOLAKE)
63 {
64 .flags = FLAG_SOF,
65 .device = 0x5a98,
66 .dmi_table = (const struct dmi_system_id []) {
67 {
68 .ident = "Up Squared",
69 .matches = {
70 DMI_MATCH(DMI_SYS_VENDOR, "AAEON"),
71 DMI_MATCH(DMI_BOARD_NAME, "UP-APL01"),
72 }
73 },
74 {}
75 }
76 },
77 {
78 .flags = FLAG_SOF,
79 .device = 0x5a98,
80 .codec_hid = "ESSX8336",
81 },
82 #endif
83 #if IS_ENABLED(CONFIG_SND_SOC_INTEL_APL)
84 {
85 .flags = FLAG_SST,
86 .device = 0x5a98,
87 .dmi_table = (const struct dmi_system_id []) {
88 {
89 .ident = "Google Chromebooks",
90 .matches = {
91 DMI_MATCH(DMI_SYS_VENDOR, "Google"),
92 }
93 },
94 {}
95 }
96 },
97 #endif
98 /*
99 * Skylake and Kabylake use legacy HDAudio driver except for Google
100 * Chromebooks (SST)
101 */
102
103 /* Sunrise Point-LP */
104 #if IS_ENABLED(CONFIG_SND_SOC_INTEL_SKL)
105 {
106 .flags = FLAG_SST,
107 .device = 0x9d70,
108 .dmi_table = (const struct dmi_system_id []) {
109 {
110 .ident = "Google Chromebooks",
111 .matches = {
112 DMI_MATCH(DMI_SYS_VENDOR, "Google"),
113 }
114 },
115 {}
116 }
117 },
118 {
119 .flags = FLAG_SST | FLAG_SST_ONLY_IF_DMIC,
120 .device = 0x9d70,
121 },
122 #endif
123 /* Kabylake-LP */
124 #if IS_ENABLED(CONFIG_SND_SOC_INTEL_KBL)
125 {
126 .flags = FLAG_SST,
127 .device = 0x9d71,
128 .dmi_table = (const struct dmi_system_id []) {
129 {
130 .ident = "Google Chromebooks",
131 .matches = {
132 DMI_MATCH(DMI_SYS_VENDOR, "Google"),
133 }
134 },
135 {}
136 }
137 },
138 {
139 .flags = FLAG_SST | FLAG_SST_ONLY_IF_DMIC,
140 .device = 0x9d71,
141 },
142 #endif
143
144 /*
145 * Geminilake uses legacy HDAudio driver except for Google
146 * Chromebooks and devices based on the ES8336 codec
147 */
148 /* Geminilake */
149 #if IS_ENABLED(CONFIG_SND_SOC_SOF_GEMINILAKE)
150 {
151 .flags = FLAG_SOF,
152 .device = 0x3198,
153 .dmi_table = (const struct dmi_system_id []) {
154 {
155 .ident = "Google Chromebooks",
156 .matches = {
157 DMI_MATCH(DMI_SYS_VENDOR, "Google"),
158 }
159 },
160 {}
161 }
162 },
163 {
164 .flags = FLAG_SOF,
165 .device = 0x3198,
166 .codec_hid = "ESSX8336",
167 },
168 #endif
169
170 /*
171 * CoffeeLake, CannonLake, CometLake, IceLake, TigerLake use legacy
172 * HDAudio driver except for Google Chromebooks and when DMICs are
173 * present. Two cases are required since Coreboot does not expose NHLT
174 * tables.
175 *
176 * When the Chromebook quirk is not present, it's based on information
177 * that no such device exists. When the quirk is present, it could be
178 * either based on product information or a placeholder.
179 */
180
181 /* Cannonlake */
182 #if IS_ENABLED(CONFIG_SND_SOC_SOF_CANNONLAKE)
183 {
184 .flags = FLAG_SOF,
185 .device = 0x9dc8,
186 .dmi_table = (const struct dmi_system_id []) {
187 {
188 .ident = "Google Chromebooks",
189 .matches = {
190 DMI_MATCH(DMI_SYS_VENDOR, "Google"),
191 }
192 },
193 {}
194 }
195 },
196 {
197 .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
198 .device = 0x9dc8,
199 },
200 #endif
201
202 /* Coffelake */
203 #if IS_ENABLED(CONFIG_SND_SOC_SOF_COFFEELAKE)
204 {
205 .flags = FLAG_SOF,
206 .device = 0xa348,
207 .dmi_table = (const struct dmi_system_id []) {
208 {
209 .ident = "Google Chromebooks",
210 .matches = {
211 DMI_MATCH(DMI_SYS_VENDOR, "Google"),
212 }
213 },
214 {}
215 }
216 },
217 {
218 .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
219 .device = 0xa348,
220 },
221 #endif
222
223 #if IS_ENABLED(CONFIG_SND_SOC_SOF_COMETLAKE)
224 /* Cometlake-LP */
225 {
226 .flags = FLAG_SOF,
227 .device = 0x02c8,
228 .dmi_table = (const struct dmi_system_id []) {
229 {
230 .ident = "Google Chromebooks",
231 .matches = {
232 DMI_MATCH(DMI_SYS_VENDOR, "Google"),
233 }
234 },
235 {
236 .matches = {
237 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
238 DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "09C6")
239 },
240 },
241 {
242 /* early version of SKU 09C6 */
243 .matches = {
244 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
245 DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0983")
246 },
247 },
248 {}
249 }
250 },
251 {
252 .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
253 .device = 0x02c8,
254 },
255 {
256 .flags = FLAG_SOF,
257 .device = 0x02c8,
258 .codec_hid = "ESSX8336",
259 },
260 /* Cometlake-H */
261 {
262 .flags = FLAG_SOF,
263 .device = 0x06c8,
264 .dmi_table = (const struct dmi_system_id []) {
265 {
266 .matches = {
267 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
268 DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "098F"),
269 },
270 },
271 {
272 .matches = {
273 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
274 DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0990"),
275 },
276 },
277 {}
278 }
279 },
280 {
281 .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
282 .device = 0x06c8,
283 },
284 {
285 .flags = FLAG_SOF,
286 .device = 0x06c8,
287 .codec_hid = "ESSX8336",
288 },
289 #endif
290
291 /* Icelake */
292 #if IS_ENABLED(CONFIG_SND_SOC_SOF_ICELAKE)
293 {
294 .flags = FLAG_SOF,
295 .device = 0x34c8,
296 .dmi_table = (const struct dmi_system_id []) {
297 {
298 .ident = "Google Chromebooks",
299 .matches = {
300 DMI_MATCH(DMI_SYS_VENDOR, "Google"),
301 }
302 },
303 {}
304 }
305 },
306 {
307 .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
308 .device = 0x34c8,
309 },
310 #endif
311
312 /* JasperLake */
313 #if IS_ENABLED(CONFIG_SND_SOC_SOF_JASPERLAKE)
314 {
315 .flags = FLAG_SOF,
316 .device = 0x4dc8,
317 .codec_hid = "ESSX8336",
318 },
319 #endif
320
321 /* Tigerlake */
322 #if IS_ENABLED(CONFIG_SND_SOC_SOF_TIGERLAKE)
323 {
324 .flags = FLAG_SOF,
325 .device = 0xa0c8,
326 .dmi_table = (const struct dmi_system_id []) {
327 {
328 .ident = "Google Chromebooks",
329 .matches = {
330 DMI_MATCH(DMI_SYS_VENDOR, "Google"),
331 }
332 },
333 {
334 .ident = "Google firmware",
335 .matches = {
336 DMI_MATCH(DMI_BIOS_VERSION, "Google"),
337 }
338 },
339 {}
340 }
341 },
342 {
343 .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
344 .device = 0xa0c8,
345 },
346 {
347 .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
348 .device = 0x43c8,
349 },
350 {
351 .flags = FLAG_SOF,
352 .device = 0xa0c8,
353 .codec_hid = "ESSX8336",
354 },
355 #endif
356
357 /* Elkhart Lake */
358 #if IS_ENABLED(CONFIG_SND_SOC_SOF_ELKHARTLAKE)
359 {
360 .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC,
361 .device = 0x4b55,
362 },
363 {
364 .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC,
365 .device = 0x4b58,
366 },
367 #endif
368
369 /* Alder Lake */
370 #if IS_ENABLED(CONFIG_SND_SOC_SOF_ALDERLAKE)
371 {
372 .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
373 .device = 0x7ad0,
374 },
375 {
376 .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
377 .device = 0x51c8,
378 },
379 {
380 .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
381 .device = 0x51cc,
382 },
383 #endif
384
385 /* Meteor Lake */
386 #if IS_ENABLED(CONFIG_SND_SOC_SOF_METEORLAKE)
387 /* Meteorlake-P */
388 {
389 .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
390 .device = 0x7e28,
391 },
392 /* ArrowLake-S */
393 {
394 .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
395 .device = PCI_DEVICE_ID_INTEL_HDA_ARL_S,
396 },
397 /* ArrowLake */
398 {
399 .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
400 .device = PCI_DEVICE_ID_INTEL_HDA_ARL,
401 },
402 #endif
403
404 /* Lunar Lake */
405 #if IS_ENABLED(CONFIG_SND_SOC_SOF_LUNARLAKE)
406 /* Lunarlake-P */
407 {
408 .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
409 .device = PCI_DEVICE_ID_INTEL_HDA_LNL_P,
410 },
411 #endif
412 };
413
snd_intel_dsp_find_config(struct pci_dev * pci,const struct config_entry * table,u32 len)414 static const struct config_entry *snd_intel_dsp_find_config
415 (struct pci_dev *pci, const struct config_entry *table, u32 len)
416 {
417 u16 device;
418
419 device = pci->device;
420 for (; len > 0; len--, table++) {
421 if (table->device != device)
422 continue;
423 if (table->dmi_table && !dmi_check_system(table->dmi_table))
424 continue;
425 if (table->codec_hid[0] && !acpi_dev_present(table->codec_hid, NULL, -1))
426 continue;
427 return table;
428 }
429 return NULL;
430 }
431
snd_intel_dsp_check_dmic(struct pci_dev * pci)432 static int snd_intel_dsp_check_dmic(struct pci_dev *pci)
433 {
434 struct nhlt_acpi_table *nhlt;
435 int ret = 0;
436
437 nhlt = intel_nhlt_init(&pci->dev);
438 if (nhlt) {
439 if (intel_nhlt_get_dmic_geo(&pci->dev, nhlt))
440 ret = 1;
441 intel_nhlt_free(nhlt);
442 }
443 return ret;
444 }
445
446 #if IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL_SOUNDWIRE)
snd_intel_dsp_check_soundwire(struct pci_dev * pci)447 static int snd_intel_dsp_check_soundwire(struct pci_dev *pci)
448 {
449 struct sdw_intel_acpi_info info;
450 acpi_handle handle;
451 int ret;
452
453 handle = ACPI_HANDLE(&pci->dev);
454
455 ret = sdw_intel_acpi_scan(handle, &info);
456 if (ret < 0)
457 return ret;
458
459 return info.link_mask;
460 }
461 #else
snd_intel_dsp_check_soundwire(struct pci_dev * pci)462 static int snd_intel_dsp_check_soundwire(struct pci_dev *pci)
463 {
464 return 0;
465 }
466 #endif
467
snd_intel_dsp_driver_probe(struct pci_dev * pci)468 int snd_intel_dsp_driver_probe(struct pci_dev *pci)
469 {
470 const struct config_entry *cfg;
471
472 /* Intel vendor only */
473 if (pci->vendor != 0x8086)
474 return SND_INTEL_DSP_DRIVER_ANY;
475
476 /*
477 * Legacy devices don't have a PCI-based DSP and use HDaudio
478 * for HDMI/DP support, ignore kernel parameter
479 */
480 switch (pci->device) {
481 case 0x160c: /* Broadwell */
482 case 0x0a0c: /* Haswell */
483 case 0x0c0c:
484 case 0x0d0c:
485 case 0x0f04: /* Baytrail */
486 case 0x2284: /* Braswell */
487 return SND_INTEL_DSP_DRIVER_ANY;
488 }
489
490 if (dsp_driver > 0 && dsp_driver <= SND_INTEL_DSP_DRIVER_LAST)
491 return dsp_driver;
492
493 /*
494 * detect DSP by checking class/subclass/prog-id information
495 * class=04 subclass 03 prog-if 00: no DSP, use legacy driver
496 * class=04 subclass 01 prog-if 00: DSP is present
497 * (and may be required e.g. for DMIC or SSP support)
498 * class=04 subclass 03 prog-if 80: use DSP or legacy mode
499 */
500 if (pci->class == 0x040300)
501 return SND_INTEL_DSP_DRIVER_LEGACY;
502 if (pci->class != 0x040100 && pci->class != 0x040380) {
503 dev_err(&pci->dev, "Unknown PCI class/subclass/prog-if information (0x%06x) found, selecting HDAudio legacy driver\n", pci->class);
504 return SND_INTEL_DSP_DRIVER_LEGACY;
505 }
506
507 dev_info(&pci->dev, "DSP detected with PCI class/subclass/prog-if info 0x%06x\n", pci->class);
508
509 /* find the configuration for the specific device */
510 cfg = snd_intel_dsp_find_config(pci, config_table, ARRAY_SIZE(config_table));
511 if (!cfg)
512 return SND_INTEL_DSP_DRIVER_ANY;
513
514 if (cfg->flags & FLAG_SOF) {
515 if (cfg->flags & FLAG_SOF_ONLY_IF_SOUNDWIRE &&
516 snd_intel_dsp_check_soundwire(pci) > 0) {
517 dev_info(&pci->dev, "SoundWire enabled on CannonLake+ platform, using SOF driver\n");
518 return SND_INTEL_DSP_DRIVER_SOF;
519 }
520 if (cfg->flags & FLAG_SOF_ONLY_IF_DMIC &&
521 snd_intel_dsp_check_dmic(pci)) {
522 dev_info(&pci->dev, "Digital mics found on Skylake+ platform, using SOF driver\n");
523 return SND_INTEL_DSP_DRIVER_SOF;
524 }
525 if (!(cfg->flags & FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE))
526 return SND_INTEL_DSP_DRIVER_SOF;
527 }
528
529
530 if (cfg->flags & FLAG_SST) {
531 if (cfg->flags & FLAG_SST_ONLY_IF_DMIC) {
532 if (snd_intel_dsp_check_dmic(pci)) {
533 dev_info(&pci->dev, "Digital mics found on Skylake+ platform, using SST driver\n");
534 return SND_INTEL_DSP_DRIVER_SST;
535 }
536 } else {
537 return SND_INTEL_DSP_DRIVER_SST;
538 }
539 }
540
541 return SND_INTEL_DSP_DRIVER_LEGACY;
542 }
543 EXPORT_SYMBOL_GPL(snd_intel_dsp_driver_probe);
544
545 /* Should we default to SOF or SST for BYT/CHT ? */
546 #if IS_ENABLED(CONFIG_SND_INTEL_BYT_PREFER_SOF) || \
547 !IS_ENABLED(CONFIG_SND_SST_ATOM_HIFI2_PLATFORM_ACPI)
548 #define FLAG_SST_OR_SOF_BYT FLAG_SOF
549 #else
550 #define FLAG_SST_OR_SOF_BYT FLAG_SST
551 #endif
552
553 /*
554 * configuration table
555 * - the order of similar ACPI ID entries is important!
556 * - the first successful match will win
557 */
558 static const struct config_entry acpi_config_table[] = {
559 #if IS_ENABLED(CONFIG_SND_SST_ATOM_HIFI2_PLATFORM_ACPI) || \
560 IS_ENABLED(CONFIG_SND_SOC_SOF_BAYTRAIL)
561 /* BayTrail */
562 {
563 .flags = FLAG_SST_OR_SOF_BYT,
564 .acpi_hid = "80860F28",
565 },
566 /* CherryTrail */
567 {
568 .flags = FLAG_SST_OR_SOF_BYT,
569 .acpi_hid = "808622A8",
570 },
571 #endif
572 /* Broadwell */
573 #if IS_ENABLED(CONFIG_SND_SOC_INTEL_CATPT)
574 {
575 .flags = FLAG_SST,
576 .acpi_hid = "INT3438"
577 },
578 #endif
579 #if IS_ENABLED(CONFIG_SND_SOC_SOF_BROADWELL)
580 {
581 .flags = FLAG_SOF,
582 .acpi_hid = "INT3438"
583 },
584 #endif
585 /* Haswell - not supported by SOF but added for consistency */
586 #if IS_ENABLED(CONFIG_SND_SOC_INTEL_CATPT)
587 {
588 .flags = FLAG_SST,
589 .acpi_hid = "INT33C8"
590 },
591 #endif
592 };
593
snd_intel_acpi_dsp_find_config(const u8 acpi_hid[ACPI_ID_LEN],const struct config_entry * table,u32 len)594 static const struct config_entry *snd_intel_acpi_dsp_find_config(const u8 acpi_hid[ACPI_ID_LEN],
595 const struct config_entry *table,
596 u32 len)
597 {
598 for (; len > 0; len--, table++) {
599 if (memcmp(table->acpi_hid, acpi_hid, ACPI_ID_LEN))
600 continue;
601 if (table->dmi_table && !dmi_check_system(table->dmi_table))
602 continue;
603 return table;
604 }
605 return NULL;
606 }
607
snd_intel_acpi_dsp_driver_probe(struct device * dev,const u8 acpi_hid[ACPI_ID_LEN])608 int snd_intel_acpi_dsp_driver_probe(struct device *dev, const u8 acpi_hid[ACPI_ID_LEN])
609 {
610 const struct config_entry *cfg;
611
612 if (dsp_driver > SND_INTEL_DSP_DRIVER_LEGACY && dsp_driver <= SND_INTEL_DSP_DRIVER_LAST)
613 return dsp_driver;
614
615 if (dsp_driver == SND_INTEL_DSP_DRIVER_LEGACY) {
616 dev_warn(dev, "dsp_driver parameter %d not supported, using automatic detection\n",
617 SND_INTEL_DSP_DRIVER_LEGACY);
618 }
619
620 /* find the configuration for the specific device */
621 cfg = snd_intel_acpi_dsp_find_config(acpi_hid, acpi_config_table,
622 ARRAY_SIZE(acpi_config_table));
623 if (!cfg)
624 return SND_INTEL_DSP_DRIVER_ANY;
625
626 if (cfg->flags & FLAG_SST)
627 return SND_INTEL_DSP_DRIVER_SST;
628
629 if (cfg->flags & FLAG_SOF)
630 return SND_INTEL_DSP_DRIVER_SOF;
631
632 return SND_INTEL_DSP_DRIVER_SST;
633 }
634 EXPORT_SYMBOL_GPL(snd_intel_acpi_dsp_driver_probe);
635
636 MODULE_LICENSE("GPL v2");
637 MODULE_DESCRIPTION("Intel DSP config driver");
638 MODULE_IMPORT_NS(SND_INTEL_SOUNDWIRE_ACPI);
639