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 }; 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) 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 #endif 77 #if IS_ENABLED(CONFIG_SND_SOC_INTEL_APL) 78 { 79 .flags = FLAG_SST, 80 .device = 0x5a98, 81 .dmi_table = (const struct dmi_system_id []) { 82 { 83 .ident = "Google Chromebooks", 84 .matches = { 85 DMI_MATCH(DMI_SYS_VENDOR, "Google"), 86 } 87 }, 88 {} 89 } 90 }, 91 #endif 92 /* 93 * Skylake and Kabylake use legacy HDAudio driver except for Google 94 * Chromebooks (SST) 95 */ 96 97 /* Sunrise Point-LP */ 98 #if IS_ENABLED(CONFIG_SND_SOC_INTEL_SKL) 99 { 100 .flags = FLAG_SST, 101 .device = 0x9d70, 102 .dmi_table = (const struct dmi_system_id []) { 103 { 104 .ident = "Google Chromebooks", 105 .matches = { 106 DMI_MATCH(DMI_SYS_VENDOR, "Google"), 107 } 108 }, 109 {} 110 } 111 }, 112 { 113 .flags = FLAG_SST | FLAG_SST_ONLY_IF_DMIC, 114 .device = 0x9d70, 115 }, 116 #endif 117 /* Kabylake-LP */ 118 #if IS_ENABLED(CONFIG_SND_SOC_INTEL_KBL) 119 { 120 .flags = FLAG_SST, 121 .device = 0x9d71, 122 .dmi_table = (const struct dmi_system_id []) { 123 { 124 .ident = "Google Chromebooks", 125 .matches = { 126 DMI_MATCH(DMI_SYS_VENDOR, "Google"), 127 } 128 }, 129 {} 130 } 131 }, 132 { 133 .flags = FLAG_SST | FLAG_SST_ONLY_IF_DMIC, 134 .device = 0x9d71, 135 }, 136 #endif 137 138 /* 139 * Geminilake uses legacy HDAudio driver except for Google 140 * Chromebooks 141 */ 142 /* Geminilake */ 143 #if IS_ENABLED(CONFIG_SND_SOC_SOF_GEMINILAKE) 144 { 145 .flags = FLAG_SOF, 146 .device = 0x3198, 147 .dmi_table = (const struct dmi_system_id []) { 148 { 149 .ident = "Google Chromebooks", 150 .matches = { 151 DMI_MATCH(DMI_SYS_VENDOR, "Google"), 152 } 153 }, 154 {} 155 } 156 }, 157 #endif 158 159 /* 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. 164 * 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. 168 */ 169 170 /* Cannonlake */ 171 #if IS_ENABLED(CONFIG_SND_SOC_SOF_CANNONLAKE) 172 { 173 .flags = FLAG_SOF, 174 .device = 0x9dc8, 175 .dmi_table = (const struct dmi_system_id []) { 176 { 177 .ident = "Google Chromebooks", 178 .matches = { 179 DMI_MATCH(DMI_SYS_VENDOR, "Google"), 180 } 181 }, 182 {} 183 } 184 }, 185 { 186 .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE, 187 .device = 0x9dc8, 188 }, 189 #endif 190 191 /* Coffelake */ 192 #if IS_ENABLED(CONFIG_SND_SOC_SOF_COFFEELAKE) 193 { 194 .flags = FLAG_SOF, 195 .device = 0xa348, 196 .dmi_table = (const struct dmi_system_id []) { 197 { 198 .ident = "Google Chromebooks", 199 .matches = { 200 DMI_MATCH(DMI_SYS_VENDOR, "Google"), 201 } 202 }, 203 {} 204 } 205 }, 206 { 207 .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE, 208 .device = 0xa348, 209 }, 210 #endif 211 212 #if IS_ENABLED(CONFIG_SND_SOC_SOF_COMETLAKE) 213 /* Cometlake-LP */ 214 { 215 .flags = FLAG_SOF, 216 .device = 0x02c8, 217 .dmi_table = (const struct dmi_system_id []) { 218 { 219 .ident = "Google Chromebooks", 220 .matches = { 221 DMI_MATCH(DMI_SYS_VENDOR, "Google"), 222 } 223 }, 224 { 225 .matches = { 226 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"), 227 DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "09C6") 228 }, 229 }, 230 { 231 /* early version of SKU 09C6 */ 232 .matches = { 233 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"), 234 DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0983") 235 }, 236 }, 237 {} 238 } 239 }, 240 { 241 .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE, 242 .device = 0x02c8, 243 }, 244 /* Cometlake-H */ 245 { 246 .flags = FLAG_SOF, 247 .device = 0x06c8, 248 .dmi_table = (const struct dmi_system_id []) { 249 { 250 .matches = { 251 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"), 252 DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "098F"), 253 }, 254 }, 255 { 256 .matches = { 257 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"), 258 DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0990"), 259 }, 260 }, 261 {} 262 } 263 }, 264 { 265 .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE, 266 .device = 0x06c8, 267 }, 268 #endif 269 270 /* Icelake */ 271 #if IS_ENABLED(CONFIG_SND_SOC_SOF_ICELAKE) 272 { 273 .flags = FLAG_SOF, 274 .device = 0x34c8, 275 .dmi_table = (const struct dmi_system_id []) { 276 { 277 .ident = "Google Chromebooks", 278 .matches = { 279 DMI_MATCH(DMI_SYS_VENDOR, "Google"), 280 } 281 }, 282 {} 283 } 284 }, 285 { 286 .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE, 287 .device = 0x34c8, 288 }, 289 #endif 290 291 /* Tigerlake */ 292 #if IS_ENABLED(CONFIG_SND_SOC_SOF_TIGERLAKE) 293 { 294 .flags = FLAG_SOF, 295 .device = 0xa0c8, 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 = 0xa0c8, 309 }, 310 { 311 .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE, 312 .device = 0x43c8, 313 }, 314 #endif 315 316 /* Elkhart Lake */ 317 #if IS_ENABLED(CONFIG_SND_SOC_SOF_ELKHARTLAKE) 318 { 319 .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC, 320 .device = 0x4b55, 321 }, 322 #endif 323 324 /* Alder Lake */ 325 #if IS_ENABLED(CONFIG_SND_SOC_SOF_ALDERLAKE) 326 { 327 .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE, 328 .device = 0x7ad0, 329 }, 330 { 331 .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE, 332 .device = 0x51c8, 333 }, 334 #endif 335 336 }; 337 338 static const struct config_entry *snd_intel_dsp_find_config 339 (struct pci_dev *pci, const struct config_entry *table, u32 len) 340 { 341 u16 device; 342 343 device = pci->device; 344 for (; len > 0; len--, table++) { 345 if (table->device != device) 346 continue; 347 if (table->dmi_table && !dmi_check_system(table->dmi_table)) 348 continue; 349 return table; 350 } 351 return NULL; 352 } 353 354 static int snd_intel_dsp_check_dmic(struct pci_dev *pci) 355 { 356 struct nhlt_acpi_table *nhlt; 357 int ret = 0; 358 359 nhlt = intel_nhlt_init(&pci->dev); 360 if (nhlt) { 361 if (intel_nhlt_get_dmic_geo(&pci->dev, nhlt)) 362 ret = 1; 363 intel_nhlt_free(nhlt); 364 } 365 return ret; 366 } 367 368 #if IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL_SOUNDWIRE) 369 static int snd_intel_dsp_check_soundwire(struct pci_dev *pci) 370 { 371 struct sdw_intel_acpi_info info; 372 acpi_handle handle; 373 int ret; 374 375 handle = ACPI_HANDLE(&pci->dev); 376 377 ret = sdw_intel_acpi_scan(handle, &info); 378 if (ret < 0) 379 return ret; 380 381 return info.link_mask; 382 } 383 #else 384 static int snd_intel_dsp_check_soundwire(struct pci_dev *pci) 385 { 386 return 0; 387 } 388 #endif 389 390 int snd_intel_dsp_driver_probe(struct pci_dev *pci) 391 { 392 const struct config_entry *cfg; 393 394 /* Intel vendor only */ 395 if (pci->vendor != 0x8086) 396 return SND_INTEL_DSP_DRIVER_ANY; 397 398 /* 399 * Legacy devices don't have a PCI-based DSP and use HDaudio 400 * for HDMI/DP support, ignore kernel parameter 401 */ 402 switch (pci->device) { 403 case 0x160c: /* Broadwell */ 404 case 0x0a0c: /* Haswell */ 405 case 0x0c0c: 406 case 0x0d0c: 407 case 0x0f04: /* Baytrail */ 408 case 0x2284: /* Braswell */ 409 return SND_INTEL_DSP_DRIVER_ANY; 410 } 411 412 if (dsp_driver > 0 && dsp_driver <= SND_INTEL_DSP_DRIVER_LAST) 413 return dsp_driver; 414 415 /* 416 * detect DSP by checking class/subclass/prog-id information 417 * class=04 subclass 03 prog-if 00: no DSP, use legacy driver 418 * class=04 subclass 01 prog-if 00: DSP is present 419 * (and may be required e.g. for DMIC or SSP support) 420 * class=04 subclass 03 prog-if 80: use DSP or legacy mode 421 */ 422 if (pci->class == 0x040300) 423 return SND_INTEL_DSP_DRIVER_LEGACY; 424 if (pci->class != 0x040100 && pci->class != 0x040380) { 425 dev_err(&pci->dev, "Unknown PCI class/subclass/prog-if information (0x%06x) found, selecting HDAudio legacy driver\n", pci->class); 426 return SND_INTEL_DSP_DRIVER_LEGACY; 427 } 428 429 dev_info(&pci->dev, "DSP detected with PCI class/subclass/prog-if info 0x%06x\n", pci->class); 430 431 /* find the configuration for the specific device */ 432 cfg = snd_intel_dsp_find_config(pci, config_table, ARRAY_SIZE(config_table)); 433 if (!cfg) 434 return SND_INTEL_DSP_DRIVER_ANY; 435 436 if (cfg->flags & FLAG_SOF) { 437 if (cfg->flags & FLAG_SOF_ONLY_IF_SOUNDWIRE && 438 snd_intel_dsp_check_soundwire(pci) > 0) { 439 dev_info(&pci->dev, "SoundWire enabled on CannonLake+ platform, using SOF driver\n"); 440 return SND_INTEL_DSP_DRIVER_SOF; 441 } 442 if (cfg->flags & FLAG_SOF_ONLY_IF_DMIC && 443 snd_intel_dsp_check_dmic(pci)) { 444 dev_info(&pci->dev, "Digital mics found on Skylake+ platform, using SOF driver\n"); 445 return SND_INTEL_DSP_DRIVER_SOF; 446 } 447 if (!(cfg->flags & FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE)) 448 return SND_INTEL_DSP_DRIVER_SOF; 449 } 450 451 452 if (cfg->flags & FLAG_SST) { 453 if (cfg->flags & FLAG_SST_ONLY_IF_DMIC) { 454 if (snd_intel_dsp_check_dmic(pci)) { 455 dev_info(&pci->dev, "Digital mics found on Skylake+ platform, using SST driver\n"); 456 return SND_INTEL_DSP_DRIVER_SST; 457 } 458 } else { 459 return SND_INTEL_DSP_DRIVER_SST; 460 } 461 } 462 463 return SND_INTEL_DSP_DRIVER_LEGACY; 464 } 465 EXPORT_SYMBOL_GPL(snd_intel_dsp_driver_probe); 466 467 /* Should we default to SOF or SST for BYT/CHT ? */ 468 #if IS_ENABLED(CONFIG_SND_INTEL_BYT_PREFER_SOF) || \ 469 !IS_ENABLED(CONFIG_SND_SST_ATOM_HIFI2_PLATFORM_ACPI) 470 #define FLAG_SST_OR_SOF_BYT FLAG_SOF 471 #else 472 #define FLAG_SST_OR_SOF_BYT FLAG_SST 473 #endif 474 475 /* 476 * configuration table 477 * - the order of similar ACPI ID entries is important! 478 * - the first successful match will win 479 */ 480 static const struct config_entry acpi_config_table[] = { 481 #if IS_ENABLED(CONFIG_SND_SST_ATOM_HIFI2_PLATFORM_ACPI) || \ 482 IS_ENABLED(CONFIG_SND_SOC_SOF_BAYTRAIL) 483 /* BayTrail */ 484 { 485 .flags = FLAG_SST_OR_SOF_BYT, 486 .acpi_hid = "80860F28", 487 }, 488 /* CherryTrail */ 489 { 490 .flags = FLAG_SST_OR_SOF_BYT, 491 .acpi_hid = "808622A8", 492 }, 493 #endif 494 /* Broadwell */ 495 #if IS_ENABLED(CONFIG_SND_SOC_INTEL_CATPT) 496 { 497 .flags = FLAG_SST, 498 .acpi_hid = "INT3438" 499 }, 500 #endif 501 #if IS_ENABLED(CONFIG_SND_SOC_SOF_BROADWELL) 502 { 503 .flags = FLAG_SOF, 504 .acpi_hid = "INT3438" 505 }, 506 #endif 507 /* Haswell - not supported by SOF but added for consistency */ 508 #if IS_ENABLED(CONFIG_SND_SOC_INTEL_CATPT) 509 { 510 .flags = FLAG_SST, 511 .acpi_hid = "INT33C8" 512 }, 513 #endif 514 }; 515 516 static const struct config_entry *snd_intel_acpi_dsp_find_config(const u8 acpi_hid[ACPI_ID_LEN], 517 const struct config_entry *table, 518 u32 len) 519 { 520 for (; len > 0; len--, table++) { 521 if (memcmp(table->acpi_hid, acpi_hid, ACPI_ID_LEN)) 522 continue; 523 if (table->dmi_table && !dmi_check_system(table->dmi_table)) 524 continue; 525 return table; 526 } 527 return NULL; 528 } 529 530 int snd_intel_acpi_dsp_driver_probe(struct device *dev, const u8 acpi_hid[ACPI_ID_LEN]) 531 { 532 const struct config_entry *cfg; 533 534 if (dsp_driver > SND_INTEL_DSP_DRIVER_LEGACY && dsp_driver <= SND_INTEL_DSP_DRIVER_LAST) 535 return dsp_driver; 536 537 if (dsp_driver == SND_INTEL_DSP_DRIVER_LEGACY) { 538 dev_warn(dev, "dsp_driver parameter %d not supported, using automatic detection\n", 539 SND_INTEL_DSP_DRIVER_LEGACY); 540 } 541 542 /* find the configuration for the specific device */ 543 cfg = snd_intel_acpi_dsp_find_config(acpi_hid, acpi_config_table, 544 ARRAY_SIZE(acpi_config_table)); 545 if (!cfg) 546 return SND_INTEL_DSP_DRIVER_ANY; 547 548 if (cfg->flags & FLAG_SST) 549 return SND_INTEL_DSP_DRIVER_SST; 550 551 if (cfg->flags & FLAG_SOF) 552 return SND_INTEL_DSP_DRIVER_SOF; 553 554 return SND_INTEL_DSP_DRIVER_SST; 555 } 556 EXPORT_SYMBOL_GPL(snd_intel_acpi_dsp_driver_probe); 557 558 MODULE_LICENSE("GPL v2"); 559 MODULE_DESCRIPTION("Intel DSP config driver"); 560 MODULE_IMPORT_NS(SND_INTEL_SOUNDWIRE_ACPI); 561