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 { 335 .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE, 336 .device = 0x51cc, 337 }, 338 #endif 339 340 }; 341 342 static const struct config_entry *snd_intel_dsp_find_config 343 (struct pci_dev *pci, const struct config_entry *table, u32 len) 344 { 345 u16 device; 346 347 device = pci->device; 348 for (; len > 0; len--, table++) { 349 if (table->device != device) 350 continue; 351 if (table->dmi_table && !dmi_check_system(table->dmi_table)) 352 continue; 353 return table; 354 } 355 return NULL; 356 } 357 358 static int snd_intel_dsp_check_dmic(struct pci_dev *pci) 359 { 360 struct nhlt_acpi_table *nhlt; 361 int ret = 0; 362 363 nhlt = intel_nhlt_init(&pci->dev); 364 if (nhlt) { 365 if (intel_nhlt_get_dmic_geo(&pci->dev, nhlt)) 366 ret = 1; 367 intel_nhlt_free(nhlt); 368 } 369 return ret; 370 } 371 372 #if IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL_SOUNDWIRE) 373 static int snd_intel_dsp_check_soundwire(struct pci_dev *pci) 374 { 375 struct sdw_intel_acpi_info info; 376 acpi_handle handle; 377 int ret; 378 379 handle = ACPI_HANDLE(&pci->dev); 380 381 ret = sdw_intel_acpi_scan(handle, &info); 382 if (ret < 0) 383 return ret; 384 385 return info.link_mask; 386 } 387 #else 388 static int snd_intel_dsp_check_soundwire(struct pci_dev *pci) 389 { 390 return 0; 391 } 392 #endif 393 394 int snd_intel_dsp_driver_probe(struct pci_dev *pci) 395 { 396 const struct config_entry *cfg; 397 398 /* Intel vendor only */ 399 if (pci->vendor != 0x8086) 400 return SND_INTEL_DSP_DRIVER_ANY; 401 402 /* 403 * Legacy devices don't have a PCI-based DSP and use HDaudio 404 * for HDMI/DP support, ignore kernel parameter 405 */ 406 switch (pci->device) { 407 case 0x160c: /* Broadwell */ 408 case 0x0a0c: /* Haswell */ 409 case 0x0c0c: 410 case 0x0d0c: 411 case 0x0f04: /* Baytrail */ 412 case 0x2284: /* Braswell */ 413 return SND_INTEL_DSP_DRIVER_ANY; 414 } 415 416 if (dsp_driver > 0 && dsp_driver <= SND_INTEL_DSP_DRIVER_LAST) 417 return dsp_driver; 418 419 /* 420 * detect DSP by checking class/subclass/prog-id information 421 * class=04 subclass 03 prog-if 00: no DSP, use legacy driver 422 * class=04 subclass 01 prog-if 00: DSP is present 423 * (and may be required e.g. for DMIC or SSP support) 424 * class=04 subclass 03 prog-if 80: use DSP or legacy mode 425 */ 426 if (pci->class == 0x040300) 427 return SND_INTEL_DSP_DRIVER_LEGACY; 428 if (pci->class != 0x040100 && pci->class != 0x040380) { 429 dev_err(&pci->dev, "Unknown PCI class/subclass/prog-if information (0x%06x) found, selecting HDAudio legacy driver\n", pci->class); 430 return SND_INTEL_DSP_DRIVER_LEGACY; 431 } 432 433 dev_info(&pci->dev, "DSP detected with PCI class/subclass/prog-if info 0x%06x\n", pci->class); 434 435 /* find the configuration for the specific device */ 436 cfg = snd_intel_dsp_find_config(pci, config_table, ARRAY_SIZE(config_table)); 437 if (!cfg) 438 return SND_INTEL_DSP_DRIVER_ANY; 439 440 if (cfg->flags & FLAG_SOF) { 441 if (cfg->flags & FLAG_SOF_ONLY_IF_SOUNDWIRE && 442 snd_intel_dsp_check_soundwire(pci) > 0) { 443 dev_info(&pci->dev, "SoundWire enabled on CannonLake+ platform, using SOF driver\n"); 444 return SND_INTEL_DSP_DRIVER_SOF; 445 } 446 if (cfg->flags & FLAG_SOF_ONLY_IF_DMIC && 447 snd_intel_dsp_check_dmic(pci)) { 448 dev_info(&pci->dev, "Digital mics found on Skylake+ platform, using SOF driver\n"); 449 return SND_INTEL_DSP_DRIVER_SOF; 450 } 451 if (!(cfg->flags & FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE)) 452 return SND_INTEL_DSP_DRIVER_SOF; 453 } 454 455 456 if (cfg->flags & FLAG_SST) { 457 if (cfg->flags & FLAG_SST_ONLY_IF_DMIC) { 458 if (snd_intel_dsp_check_dmic(pci)) { 459 dev_info(&pci->dev, "Digital mics found on Skylake+ platform, using SST driver\n"); 460 return SND_INTEL_DSP_DRIVER_SST; 461 } 462 } else { 463 return SND_INTEL_DSP_DRIVER_SST; 464 } 465 } 466 467 return SND_INTEL_DSP_DRIVER_LEGACY; 468 } 469 EXPORT_SYMBOL_GPL(snd_intel_dsp_driver_probe); 470 471 /* Should we default to SOF or SST for BYT/CHT ? */ 472 #if IS_ENABLED(CONFIG_SND_INTEL_BYT_PREFER_SOF) || \ 473 !IS_ENABLED(CONFIG_SND_SST_ATOM_HIFI2_PLATFORM_ACPI) 474 #define FLAG_SST_OR_SOF_BYT FLAG_SOF 475 #else 476 #define FLAG_SST_OR_SOF_BYT FLAG_SST 477 #endif 478 479 /* 480 * configuration table 481 * - the order of similar ACPI ID entries is important! 482 * - the first successful match will win 483 */ 484 static const struct config_entry acpi_config_table[] = { 485 #if IS_ENABLED(CONFIG_SND_SST_ATOM_HIFI2_PLATFORM_ACPI) || \ 486 IS_ENABLED(CONFIG_SND_SOC_SOF_BAYTRAIL) 487 /* BayTrail */ 488 { 489 .flags = FLAG_SST_OR_SOF_BYT, 490 .acpi_hid = "80860F28", 491 }, 492 /* CherryTrail */ 493 { 494 .flags = FLAG_SST_OR_SOF_BYT, 495 .acpi_hid = "808622A8", 496 }, 497 #endif 498 /* Broadwell */ 499 #if IS_ENABLED(CONFIG_SND_SOC_INTEL_CATPT) 500 { 501 .flags = FLAG_SST, 502 .acpi_hid = "INT3438" 503 }, 504 #endif 505 #if IS_ENABLED(CONFIG_SND_SOC_SOF_BROADWELL) 506 { 507 .flags = FLAG_SOF, 508 .acpi_hid = "INT3438" 509 }, 510 #endif 511 /* Haswell - not supported by SOF but added for consistency */ 512 #if IS_ENABLED(CONFIG_SND_SOC_INTEL_CATPT) 513 { 514 .flags = FLAG_SST, 515 .acpi_hid = "INT33C8" 516 }, 517 #endif 518 }; 519 520 static const struct config_entry *snd_intel_acpi_dsp_find_config(const u8 acpi_hid[ACPI_ID_LEN], 521 const struct config_entry *table, 522 u32 len) 523 { 524 for (; len > 0; len--, table++) { 525 if (memcmp(table->acpi_hid, acpi_hid, ACPI_ID_LEN)) 526 continue; 527 if (table->dmi_table && !dmi_check_system(table->dmi_table)) 528 continue; 529 return table; 530 } 531 return NULL; 532 } 533 534 int snd_intel_acpi_dsp_driver_probe(struct device *dev, const u8 acpi_hid[ACPI_ID_LEN]) 535 { 536 const struct config_entry *cfg; 537 538 if (dsp_driver > SND_INTEL_DSP_DRIVER_LEGACY && dsp_driver <= SND_INTEL_DSP_DRIVER_LAST) 539 return dsp_driver; 540 541 if (dsp_driver == SND_INTEL_DSP_DRIVER_LEGACY) { 542 dev_warn(dev, "dsp_driver parameter %d not supported, using automatic detection\n", 543 SND_INTEL_DSP_DRIVER_LEGACY); 544 } 545 546 /* find the configuration for the specific device */ 547 cfg = snd_intel_acpi_dsp_find_config(acpi_hid, acpi_config_table, 548 ARRAY_SIZE(acpi_config_table)); 549 if (!cfg) 550 return SND_INTEL_DSP_DRIVER_ANY; 551 552 if (cfg->flags & FLAG_SST) 553 return SND_INTEL_DSP_DRIVER_SST; 554 555 if (cfg->flags & FLAG_SOF) 556 return SND_INTEL_DSP_DRIVER_SOF; 557 558 return SND_INTEL_DSP_DRIVER_SST; 559 } 560 EXPORT_SYMBOL_GPL(snd_intel_acpi_dsp_driver_probe); 561 562 MODULE_LICENSE("GPL v2"); 563 MODULE_DESCRIPTION("Intel DSP config driver"); 564 MODULE_IMPORT_NS(SND_INTEL_SOUNDWIRE_ACPI); 565