WIP FPC-III support
[linux/fpc-iii.git] / sound / hda / intel-dsp-config.c
blob6a0d070c60c9ac489a9c1e9d35e1262e0b4bbe62
1 // SPDX-License-Identifier: GPL-2.0
2 // Copyright (c) 2019 Jaroslav Kysela <perex@perex.cz>
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>
15 static int dsp_driver;
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)");
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)
26 #define FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE (FLAG_SOF_ONLY_IF_DMIC | \
27 FLAG_SOF_ONLY_IF_SOUNDWIRE)
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;
37 * configuration table
38 * - the order of similar PCI ID entries is important!
39 * - the first successful match will win
41 static const struct config_entry config_table[] = {
42 /* Merrifield */
43 #if IS_ENABLED(CONFIG_SND_SOC_SOF_MERRIFIELD)
45 .flags = FLAG_SOF,
46 .device = 0x119a,
48 #endif
49 /* Broxton-T */
50 #if IS_ENABLED(CONFIG_SND_SOC_SOF_APOLLOLAKE)
52 .flags = FLAG_SOF,
53 .device = 0x1a98,
55 #endif
57 * Apollolake (Broxton-P)
58 * the legacy HDAudio driver is used except on Up Squared (SOF) and
59 * Chromebooks (SST)
61 #if IS_ENABLED(CONFIG_SND_SOC_SOF_APOLLOLAKE)
63 .flags = FLAG_SOF,
64 .device = 0x5a98,
65 .dmi_table = (const struct dmi_system_id []) {
67 .ident = "Up Squared",
68 .matches = {
69 DMI_MATCH(DMI_SYS_VENDOR, "AAEON"),
70 DMI_MATCH(DMI_BOARD_NAME, "UP-APL01"),
76 #endif
77 #if IS_ENABLED(CONFIG_SND_SOC_INTEL_APL)
79 .flags = FLAG_SST,
80 .device = 0x5a98,
81 .dmi_table = (const struct dmi_system_id []) {
83 .ident = "Google Chromebooks",
84 .matches = {
85 DMI_MATCH(DMI_SYS_VENDOR, "Google"),
91 #endif
93 * Skylake and Kabylake use legacy HDAudio driver except for Google
94 * Chromebooks (SST)
97 /* Sunrise Point-LP */
98 #if IS_ENABLED(CONFIG_SND_SOC_INTEL_SKL)
100 .flags = FLAG_SST,
101 .device = 0x9d70,
102 .dmi_table = (const struct dmi_system_id []) {
104 .ident = "Google Chromebooks",
105 .matches = {
106 DMI_MATCH(DMI_SYS_VENDOR, "Google"),
113 .flags = FLAG_SST | FLAG_SST_ONLY_IF_DMIC,
114 .device = 0x9d70,
116 #endif
117 /* Kabylake-LP */
118 #if IS_ENABLED(CONFIG_SND_SOC_INTEL_KBL)
120 .flags = FLAG_SST,
121 .device = 0x9d71,
122 .dmi_table = (const struct dmi_system_id []) {
124 .ident = "Google Chromebooks",
125 .matches = {
126 DMI_MATCH(DMI_SYS_VENDOR, "Google"),
133 .flags = FLAG_SST | FLAG_SST_ONLY_IF_DMIC,
134 .device = 0x9d71,
136 #endif
139 * Geminilake uses legacy HDAudio driver except for Google
140 * Chromebooks
142 /* Geminilake */
143 #if IS_ENABLED(CONFIG_SND_SOC_SOF_GEMINILAKE)
145 .flags = FLAG_SOF,
146 .device = 0x3198,
147 .dmi_table = (const struct dmi_system_id []) {
149 .ident = "Google Chromebooks",
150 .matches = {
151 DMI_MATCH(DMI_SYS_VENDOR, "Google"),
157 #endif
160 * CoffeeLake, CannonLake, CometLake, IceLake, TigerLake use legacy
161 * HDAudio driver except for Google Chromebooks and when DMICs are
162 * present. Two cases are required since Coreboot does not expose NHLT
163 * tables.
165 * When the Chromebook quirk is not present, it's based on information
166 * that no such device exists. When the quirk is present, it could be
167 * either based on product information or a placeholder.
170 /* Cannonlake */
171 #if IS_ENABLED(CONFIG_SND_SOC_SOF_CANNONLAKE)
173 .flags = FLAG_SOF,
174 .device = 0x9dc8,
175 .dmi_table = (const struct dmi_system_id []) {
177 .ident = "Google Chromebooks",
178 .matches = {
179 DMI_MATCH(DMI_SYS_VENDOR, "Google"),
186 .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
187 .device = 0x9dc8,
189 #endif
191 /* Coffelake */
192 #if IS_ENABLED(CONFIG_SND_SOC_SOF_COFFEELAKE)
194 .flags = FLAG_SOF,
195 .device = 0xa348,
196 .dmi_table = (const struct dmi_system_id []) {
198 .ident = "Google Chromebooks",
199 .matches = {
200 DMI_MATCH(DMI_SYS_VENDOR, "Google"),
207 .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
208 .device = 0xa348,
210 #endif
212 #if IS_ENABLED(CONFIG_SND_SOC_SOF_COMETLAKE)
213 /* Cometlake-LP */
215 .flags = FLAG_SOF,
216 .device = 0x02c8,
217 .dmi_table = (const struct dmi_system_id []) {
219 .ident = "Google Chromebooks",
220 .matches = {
221 DMI_MATCH(DMI_SYS_VENDOR, "Google"),
225 .matches = {
226 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
227 DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "09C6")
231 /* early version of SKU 09C6 */
232 .matches = {
233 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
234 DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0983")
241 .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
242 .device = 0x02c8,
244 /* Cometlake-H */
246 .flags = FLAG_SOF,
247 .device = 0x06c8,
248 .dmi_table = (const struct dmi_system_id []) {
250 .matches = {
251 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
252 DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "098F"),
256 .matches = {
257 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
258 DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0990"),
265 .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
266 .device = 0x06c8,
268 #endif
270 /* Icelake */
271 #if IS_ENABLED(CONFIG_SND_SOC_SOF_ICELAKE)
273 .flags = FLAG_SOF,
274 .device = 0x34c8,
275 .dmi_table = (const struct dmi_system_id []) {
277 .ident = "Google Chromebooks",
278 .matches = {
279 DMI_MATCH(DMI_SYS_VENDOR, "Google"),
286 .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
287 .device = 0x34c8,
289 #endif
291 /* Tigerlake */
292 #if IS_ENABLED(CONFIG_SND_SOC_SOF_TIGERLAKE)
294 .flags = FLAG_SOF,
295 .device = 0xa0c8,
296 .dmi_table = (const struct dmi_system_id []) {
298 .ident = "Google Chromebooks",
299 .matches = {
300 DMI_MATCH(DMI_SYS_VENDOR, "Google"),
307 .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
308 .device = 0xa0c8,
310 #endif
312 /* Elkhart Lake */
313 #if IS_ENABLED(CONFIG_SND_SOC_SOF_ELKHARTLAKE)
315 .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC,
316 .device = 0x4b55,
318 #endif
322 static const struct config_entry *snd_intel_dsp_find_config
323 (struct pci_dev *pci, const struct config_entry *table, u32 len)
325 u16 device;
327 device = pci->device;
328 for (; len > 0; len--, table++) {
329 if (table->device != device)
330 continue;
331 if (table->dmi_table && !dmi_check_system(table->dmi_table))
332 continue;
333 return table;
335 return NULL;
338 static int snd_intel_dsp_check_dmic(struct pci_dev *pci)
340 struct nhlt_acpi_table *nhlt;
341 int ret = 0;
343 nhlt = intel_nhlt_init(&pci->dev);
344 if (nhlt) {
345 if (intel_nhlt_get_dmic_geo(&pci->dev, nhlt))
346 ret = 1;
347 intel_nhlt_free(nhlt);
349 return ret;
352 #if IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL_SOUNDWIRE)
353 static int snd_intel_dsp_check_soundwire(struct pci_dev *pci)
355 struct sdw_intel_acpi_info info;
356 acpi_handle handle;
357 int ret;
359 handle = ACPI_HANDLE(&pci->dev);
361 ret = sdw_intel_acpi_scan(handle, &info);
362 if (ret < 0)
363 return ret;
365 return info.link_mask;
367 #else
368 static int snd_intel_dsp_check_soundwire(struct pci_dev *pci)
370 return 0;
372 #endif
374 int snd_intel_dsp_driver_probe(struct pci_dev *pci)
376 const struct config_entry *cfg;
378 /* Intel vendor only */
379 if (pci->vendor != 0x8086)
380 return SND_INTEL_DSP_DRIVER_ANY;
383 * Legacy devices don't have a PCI-based DSP and use HDaudio
384 * for HDMI/DP support, ignore kernel parameter
386 switch (pci->device) {
387 case 0x160c: /* Broadwell */
388 case 0x0a0c: /* Haswell */
389 case 0x0c0c:
390 case 0x0d0c:
391 case 0x0f04: /* Baytrail */
392 case 0x2284: /* Braswell */
393 return SND_INTEL_DSP_DRIVER_ANY;
396 if (dsp_driver > 0 && dsp_driver <= SND_INTEL_DSP_DRIVER_LAST)
397 return dsp_driver;
400 * detect DSP by checking class/subclass/prog-id information
401 * class=04 subclass 03 prog-if 00: no DSP, use legacy driver
402 * class=04 subclass 01 prog-if 00: DSP is present
403 * (and may be required e.g. for DMIC or SSP support)
404 * class=04 subclass 03 prog-if 80: use DSP or legacy mode
406 if (pci->class == 0x040300)
407 return SND_INTEL_DSP_DRIVER_LEGACY;
408 if (pci->class != 0x040100 && pci->class != 0x040380) {
409 dev_err(&pci->dev, "Unknown PCI class/subclass/prog-if information (0x%06x) found, selecting HDAudio legacy driver\n", pci->class);
410 return SND_INTEL_DSP_DRIVER_LEGACY;
413 dev_info(&pci->dev, "DSP detected with PCI class/subclass/prog-if info 0x%06x\n", pci->class);
415 /* find the configuration for the specific device */
416 cfg = snd_intel_dsp_find_config(pci, config_table, ARRAY_SIZE(config_table));
417 if (!cfg)
418 return SND_INTEL_DSP_DRIVER_ANY;
420 if (cfg->flags & FLAG_SOF) {
421 if (cfg->flags & FLAG_SOF_ONLY_IF_SOUNDWIRE &&
422 snd_intel_dsp_check_soundwire(pci) > 0) {
423 dev_info(&pci->dev, "SoundWire enabled on CannonLake+ platform, using SOF driver\n");
424 return SND_INTEL_DSP_DRIVER_SOF;
426 if (cfg->flags & FLAG_SOF_ONLY_IF_DMIC &&
427 snd_intel_dsp_check_dmic(pci)) {
428 dev_info(&pci->dev, "Digital mics found on Skylake+ platform, using SOF driver\n");
429 return SND_INTEL_DSP_DRIVER_SOF;
431 if (!(cfg->flags & FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE))
432 return SND_INTEL_DSP_DRIVER_SOF;
436 if (cfg->flags & FLAG_SST) {
437 if (cfg->flags & FLAG_SST_ONLY_IF_DMIC) {
438 if (snd_intel_dsp_check_dmic(pci)) {
439 dev_info(&pci->dev, "Digital mics found on Skylake+ platform, using SST driver\n");
440 return SND_INTEL_DSP_DRIVER_SST;
442 } else {
443 return SND_INTEL_DSP_DRIVER_SST;
447 return SND_INTEL_DSP_DRIVER_LEGACY;
449 EXPORT_SYMBOL_GPL(snd_intel_dsp_driver_probe);
452 * configuration table
453 * - the order of similar ACPI ID entries is important!
454 * - the first successful match will win
456 static const struct config_entry acpi_config_table[] = {
457 /* BayTrail */
458 #if IS_ENABLED(CONFIG_SND_SST_ATOM_HIFI2_PLATFORM_ACPI)
460 .flags = FLAG_SST,
461 .acpi_hid = "80860F28",
463 #endif
464 #if IS_ENABLED(CONFIG_SND_SOC_SOF_BAYTRAIL)
466 .flags = FLAG_SOF,
467 .acpi_hid = "80860F28",
469 #endif
470 /* CherryTrail */
471 #if IS_ENABLED(CONFIG_SND_SST_ATOM_HIFI2_PLATFORM_ACPI)
473 .flags = FLAG_SST,
474 .acpi_hid = "808622A8",
476 #endif
477 #if IS_ENABLED(CONFIG_SND_SOC_SOF_BAYTRAIL)
479 .flags = FLAG_SOF,
480 .acpi_hid = "808622A8",
482 #endif
483 /* Broadwell */
484 #if IS_ENABLED(CONFIG_SND_SOC_INTEL_CATPT)
486 .flags = FLAG_SST,
487 .acpi_hid = "INT3438"
489 #endif
490 #if IS_ENABLED(CONFIG_SND_SOC_SOF_BROADWELL)
492 .flags = FLAG_SOF,
493 .acpi_hid = "INT3438"
495 #endif
496 /* Haswell - not supported by SOF but added for consistency */
497 #if IS_ENABLED(CONFIG_SND_SOC_INTEL_CATPT)
499 .flags = FLAG_SST,
500 .acpi_hid = "INT33C8"
502 #endif
505 static const struct config_entry *snd_intel_acpi_dsp_find_config(const u8 acpi_hid[ACPI_ID_LEN],
506 const struct config_entry *table,
507 u32 len)
509 for (; len > 0; len--, table++) {
510 if (memcmp(table->acpi_hid, acpi_hid, ACPI_ID_LEN))
511 continue;
512 if (table->dmi_table && !dmi_check_system(table->dmi_table))
513 continue;
514 return table;
516 return NULL;
519 int snd_intel_acpi_dsp_driver_probe(struct device *dev, const u8 acpi_hid[ACPI_ID_LEN])
521 const struct config_entry *cfg;
523 if (dsp_driver > SND_INTEL_DSP_DRIVER_LEGACY && dsp_driver <= SND_INTEL_DSP_DRIVER_LAST)
524 return dsp_driver;
526 if (dsp_driver == SND_INTEL_DSP_DRIVER_LEGACY) {
527 dev_warn(dev, "dsp_driver parameter %d not supported, using automatic detection\n",
528 SND_INTEL_DSP_DRIVER_LEGACY);
531 /* find the configuration for the specific device */
532 cfg = snd_intel_acpi_dsp_find_config(acpi_hid, acpi_config_table,
533 ARRAY_SIZE(acpi_config_table));
534 if (!cfg)
535 return SND_INTEL_DSP_DRIVER_ANY;
537 if (cfg->flags & FLAG_SST)
538 return SND_INTEL_DSP_DRIVER_SST;
540 if (cfg->flags & FLAG_SOF)
541 return SND_INTEL_DSP_DRIVER_SOF;
543 return SND_INTEL_DSP_DRIVER_SST;
545 EXPORT_SYMBOL_GPL(snd_intel_acpi_dsp_driver_probe);
547 MODULE_LICENSE("GPL v2");
548 MODULE_DESCRIPTION("Intel DSP config driver");
549 MODULE_IMPORT_NS(SOUNDWIRE_INTEL_INIT);