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 #endif 311 312 /* Elkhart Lake */ 313 #if IS_ENABLED(CONFIG_SND_SOC_SOF_ELKHARTLAKE) 314 { 315 .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC, 316 .device = 0x4b55, 317 }, 318 #endif 319 320 }; 321 322 static const struct config_entry *snd_intel_dsp_find_config 323 (struct pci_dev *pci, const struct config_entry *table, u32 len) 324 { 325 u16 device; 326 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; 334 } 335 return NULL; 336 } 337 338 static int snd_intel_dsp_check_dmic(struct pci_dev *pci) 339 { 340 struct nhlt_acpi_table *nhlt; 341 int ret = 0; 342 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); 348 } 349 return ret; 350 } 351 352 #if IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL_SOUNDWIRE) 353 static int snd_intel_dsp_check_soundwire(struct pci_dev *pci) 354 { 355 struct sdw_intel_acpi_info info; 356 acpi_handle handle; 357 int ret; 358 359 handle = ACPI_HANDLE(&pci->dev); 360 361 ret = sdw_intel_acpi_scan(handle, &info); 362 if (ret < 0) 363 return ret; 364 365 return info.link_mask; 366 } 367 #else 368 static int snd_intel_dsp_check_soundwire(struct pci_dev *pci) 369 { 370 return 0; 371 } 372 #endif 373 374 int snd_intel_dsp_driver_probe(struct pci_dev *pci) 375 { 376 const struct config_entry *cfg; 377 378 /* Intel vendor only */ 379 if (pci->vendor != 0x8086) 380 return SND_INTEL_DSP_DRIVER_ANY; 381 382 /* 383 * Legacy devices don't have a PCI-based DSP and use HDaudio 384 * for HDMI/DP support, ignore kernel parameter 385 */ 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; 394 } 395 396 if (dsp_driver > 0 && dsp_driver <= SND_INTEL_DSP_DRIVER_LAST) 397 return dsp_driver; 398 399 /* 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 405 */ 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; 411 } 412 413 dev_info(&pci->dev, "DSP detected with PCI class/subclass/prog-if info 0x%06x\n", pci->class); 414 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; 419 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; 425 } 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; 430 } 431 if (!(cfg->flags & FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE)) 432 return SND_INTEL_DSP_DRIVER_SOF; 433 } 434 435 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; 441 } 442 } else { 443 return SND_INTEL_DSP_DRIVER_SST; 444 } 445 } 446 447 return SND_INTEL_DSP_DRIVER_LEGACY; 448 } 449 EXPORT_SYMBOL_GPL(snd_intel_dsp_driver_probe); 450 451 /* 452 * configuration table 453 * - the order of similar ACPI ID entries is important! 454 * - the first successful match will win 455 */ 456 static const struct config_entry acpi_config_table[] = { 457 /* BayTrail */ 458 #if IS_ENABLED(CONFIG_SND_SST_ATOM_HIFI2_PLATFORM_ACPI) 459 { 460 .flags = FLAG_SST, 461 .acpi_hid = "80860F28", 462 }, 463 #endif 464 #if IS_ENABLED(CONFIG_SND_SOC_SOF_BAYTRAIL) 465 { 466 .flags = FLAG_SOF, 467 .acpi_hid = "80860F28", 468 }, 469 #endif 470 /* CherryTrail */ 471 #if IS_ENABLED(CONFIG_SND_SST_ATOM_HIFI2_PLATFORM_ACPI) 472 { 473 .flags = FLAG_SST, 474 .acpi_hid = "808622A8", 475 }, 476 #endif 477 #if IS_ENABLED(CONFIG_SND_SOC_SOF_BAYTRAIL) 478 { 479 .flags = FLAG_SOF, 480 .acpi_hid = "808622A8", 481 }, 482 #endif 483 /* Broadwell */ 484 #if IS_ENABLED(CONFIG_SND_SOC_INTEL_CATPT) 485 { 486 .flags = FLAG_SST, 487 .acpi_hid = "INT3438" 488 }, 489 #endif 490 #if IS_ENABLED(CONFIG_SND_SOC_SOF_BROADWELL) 491 { 492 .flags = FLAG_SOF, 493 .acpi_hid = "INT3438" 494 }, 495 #endif 496 /* Haswell - not supported by SOF but added for consistency */ 497 #if IS_ENABLED(CONFIG_SND_SOC_INTEL_CATPT) 498 { 499 .flags = FLAG_SST, 500 .acpi_hid = "INT33C8" 501 }, 502 #endif 503 }; 504 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) 508 { 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; 515 } 516 return NULL; 517 } 518 519 int snd_intel_acpi_dsp_driver_probe(struct device *dev, const u8 acpi_hid[ACPI_ID_LEN]) 520 { 521 const struct config_entry *cfg; 522 523 if (dsp_driver > SND_INTEL_DSP_DRIVER_LEGACY && dsp_driver <= SND_INTEL_DSP_DRIVER_LAST) 524 return dsp_driver; 525 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); 529 } 530 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; 536 537 if (cfg->flags & FLAG_SST) 538 return SND_INTEL_DSP_DRIVER_SST; 539 540 if (cfg->flags & FLAG_SOF) 541 return SND_INTEL_DSP_DRIVER_SOF; 542 543 return SND_INTEL_DSP_DRIVER_SST; 544 } 545 EXPORT_SYMBOL_GPL(snd_intel_acpi_dsp_driver_probe); 546 547 MODULE_LICENSE("GPL v2"); 548 MODULE_DESCRIPTION("Intel DSP config driver"); 549 MODULE_IMPORT_NS(SOUNDWIRE_INTEL_INIT); 550