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 15static int dsp_driver; 16 17module_param(dsp_driver, int, 0444); 18MODULE_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 29struct config_entry { 30 u32 flags; 31 u16 device; 32 const struct dmi_system_id *dmi_table; 33 u8 codec_hid[ACPI_ID_LEN]; 34}; 35 36/* 37 * configuration table 38 * - the order of similar PCI ID entries is important! 39 * - the first successful match will win 40 */ 41static 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), as well as devices based on the ES8336 codec 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 { 77 .flags = FLAG_SOF, 78 .device = 0x5a98, 79 .codec_hid = "ESSX8336", 80 }, 81#endif 82#if IS_ENABLED(CONFIG_SND_SOC_INTEL_APL) 83 { 84 .flags = FLAG_SST, 85 .device = 0x5a98, 86 .dmi_table = (const struct dmi_system_id []) { 87 { 88 .ident = "Google Chromebooks", 89 .matches = { 90 DMI_MATCH(DMI_SYS_VENDOR, "Google"), 91 } 92 }, 93 {} 94 } 95 }, 96#endif 97/* 98 * Skylake and Kabylake use legacy HDAudio driver except for Google 99 * Chromebooks (SST) 100 */ 101 102/* Sunrise Point-LP */ 103#if IS_ENABLED(CONFIG_SND_SOC_INTEL_SKL) 104 { 105 .flags = FLAG_SST, 106 .device = 0x9d70, 107 .dmi_table = (const struct dmi_system_id []) { 108 { 109 .ident = "Google Chromebooks", 110 .matches = { 111 DMI_MATCH(DMI_SYS_VENDOR, "Google"), 112 } 113 }, 114 {} 115 } 116 }, 117 { 118 .flags = FLAG_SST | FLAG_SST_ONLY_IF_DMIC, 119 .device = 0x9d70, 120 }, 121#endif 122/* Kabylake-LP */ 123#if IS_ENABLED(CONFIG_SND_SOC_INTEL_KBL) 124 { 125 .flags = FLAG_SST, 126 .device = 0x9d71, 127 .dmi_table = (const struct dmi_system_id []) { 128 { 129 .ident = "Google Chromebooks", 130 .matches = { 131 DMI_MATCH(DMI_SYS_VENDOR, "Google"), 132 } 133 }, 134 {} 135 } 136 }, 137 { 138 .flags = FLAG_SST | FLAG_SST_ONLY_IF_DMIC, 139 .device = 0x9d71, 140 }, 141#endif 142 143/* 144 * Geminilake uses legacy HDAudio driver except for Google 145 * Chromebooks and devices based on the ES8336 codec 146 */ 147/* Geminilake */ 148#if IS_ENABLED(CONFIG_SND_SOC_SOF_GEMINILAKE) 149 { 150 .flags = FLAG_SOF, 151 .device = 0x3198, 152 .dmi_table = (const struct dmi_system_id []) { 153 { 154 .ident = "Google Chromebooks", 155 .matches = { 156 DMI_MATCH(DMI_SYS_VENDOR, "Google"), 157 } 158 }, 159 {} 160 } 161 }, 162 { 163 .flags = FLAG_SOF, 164 .device = 0x3198, 165 .codec_hid = "ESSX8336", 166 }, 167#endif 168 169/* 170 * CoffeeLake, CannonLake, CometLake, IceLake, TigerLake use legacy 171 * HDAudio driver except for Google Chromebooks and when DMICs are 172 * present. Two cases are required since Coreboot does not expose NHLT 173 * tables. 174 * 175 * When the Chromebook quirk is not present, it's based on information 176 * that no such device exists. When the quirk is present, it could be 177 * either based on product information or a placeholder. 178 */ 179 180/* Cannonlake */ 181#if IS_ENABLED(CONFIG_SND_SOC_SOF_CANNONLAKE) 182 { 183 .flags = FLAG_SOF, 184 .device = 0x9dc8, 185 .dmi_table = (const struct dmi_system_id []) { 186 { 187 .ident = "Google Chromebooks", 188 .matches = { 189 DMI_MATCH(DMI_SYS_VENDOR, "Google"), 190 } 191 }, 192 {} 193 } 194 }, 195 { 196 .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE, 197 .device = 0x9dc8, 198 }, 199#endif 200 201/* Coffelake */ 202#if IS_ENABLED(CONFIG_SND_SOC_SOF_COFFEELAKE) 203 { 204 .flags = FLAG_SOF, 205 .device = 0xa348, 206 .dmi_table = (const struct dmi_system_id []) { 207 { 208 .ident = "Google Chromebooks", 209 .matches = { 210 DMI_MATCH(DMI_SYS_VENDOR, "Google"), 211 } 212 }, 213 {} 214 } 215 }, 216 { 217 .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE, 218 .device = 0xa348, 219 }, 220#endif 221 222#if IS_ENABLED(CONFIG_SND_SOC_SOF_COMETLAKE) 223/* Cometlake-LP */ 224 { 225 .flags = FLAG_SOF, 226 .device = 0x02c8, 227 .dmi_table = (const struct dmi_system_id []) { 228 { 229 .ident = "Google Chromebooks", 230 .matches = { 231 DMI_MATCH(DMI_SYS_VENDOR, "Google"), 232 } 233 }, 234 { 235 .matches = { 236 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"), 237 DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "09C6") 238 }, 239 }, 240 { 241 /* early version of SKU 09C6 */ 242 .matches = { 243 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"), 244 DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0983") 245 }, 246 }, 247 {} 248 } 249 }, 250 { 251 .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE, 252 .device = 0x02c8, 253 }, 254 { 255 .flags = FLAG_SOF, 256 .device = 0x02c8, 257 .codec_hid = "ESSX8336", 258 }, 259/* Cometlake-H */ 260 { 261 .flags = FLAG_SOF, 262 .device = 0x06c8, 263 .dmi_table = (const struct dmi_system_id []) { 264 { 265 .matches = { 266 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"), 267 DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "098F"), 268 }, 269 }, 270 { 271 .matches = { 272 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"), 273 DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0990"), 274 }, 275 }, 276 {} 277 } 278 }, 279 { 280 .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE, 281 .device = 0x06c8, 282 }, 283 { 284 .flags = FLAG_SOF, 285 .device = 0x06c8, 286 .codec_hid = "ESSX8336", 287 }, 288#endif 289 290/* Icelake */ 291#if IS_ENABLED(CONFIG_SND_SOC_SOF_ICELAKE) 292 { 293 .flags = FLAG_SOF, 294 .device = 0x34c8, 295 .dmi_table = (const struct dmi_system_id []) { 296 { 297 .ident = "Google Chromebooks", 298 .matches = { 299 DMI_MATCH(DMI_SYS_VENDOR, "Google"), 300 } 301 }, 302 {} 303 } 304 }, 305 { 306 .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE, 307 .device = 0x34c8, 308 }, 309#endif 310 311/* JasperLake */ 312#if IS_ENABLED(CONFIG_SND_SOC_SOF_JASPERLAKE) 313 { 314 .flags = FLAG_SOF, 315 .device = 0x4dc8, 316 .codec_hid = "ESSX8336", 317 }, 318#endif 319 320/* Tigerlake */ 321#if IS_ENABLED(CONFIG_SND_SOC_SOF_TIGERLAKE) 322 { 323 .flags = FLAG_SOF, 324 .device = 0xa0c8, 325 .dmi_table = (const struct dmi_system_id []) { 326 { 327 .ident = "Google Chromebooks", 328 .matches = { 329 DMI_MATCH(DMI_SYS_VENDOR, "Google"), 330 } 331 }, 332 { 333 .ident = "Google firmware", 334 .matches = { 335 DMI_MATCH(DMI_BIOS_VERSION, "Google"), 336 } 337 }, 338 {} 339 } 340 }, 341 { 342 .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE, 343 .device = 0xa0c8, 344 }, 345 { 346 .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE, 347 .device = 0x43c8, 348 }, 349 { 350 .flags = FLAG_SOF, 351 .device = 0xa0c8, 352 .codec_hid = "ESSX8336", 353 }, 354#endif 355 356/* Elkhart Lake */ 357#if IS_ENABLED(CONFIG_SND_SOC_SOF_ELKHARTLAKE) 358 { 359 .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC, 360 .device = 0x4b55, 361 }, 362 { 363 .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC, 364 .device = 0x4b58, 365 }, 366#endif 367 368/* Meteor Lake */ 369#if IS_ENABLED(CONFIG_SND_SOC_SOF_METEORLAKE) 370 /* Meteorlake-P */ 371 { 372 .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE, 373 .device = 0x7e28, 374 }, 375 /* ArrowLake-S */ 376 { 377 .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE, 378 .device = PCI_DEVICE_ID_INTEL_HDA_ARL_S, 379 }, 380 /* ArrowLake */ 381 { 382 .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE, 383 .device = PCI_DEVICE_ID_INTEL_HDA_ARL, 384 }, 385#endif 386 387/* Lunar Lake */ 388#if IS_ENABLED(CONFIG_SND_SOC_SOF_LUNARLAKE) 389 /* Lunarlake-P */ 390 { 391 .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE, 392 .device = PCI_DEVICE_ID_INTEL_HDA_LNL_P, 393 }, 394#endif 395}; 396 397static const struct config_entry *snd_intel_dsp_find_config 398 (struct pci_dev *pci, const struct config_entry *table, u32 len) 399{ 400 u16 device; 401 402 device = pci->device; 403 for (; len > 0; len--, table++) { 404 if (table->device != device) 405 continue; 406 if (table->dmi_table && !dmi_check_system(table->dmi_table)) 407 continue; 408 if (table->codec_hid[0] && !acpi_dev_present(table->codec_hid, NULL, -1)) 409 continue; 410 return table; 411 } 412 return NULL; 413} 414 415static int snd_intel_dsp_check_dmic(struct pci_dev *pci) 416{ 417 struct nhlt_acpi_table *nhlt; 418 int ret = 0; 419 420 nhlt = intel_nhlt_init(&pci->dev); 421 if (nhlt) { 422 if (intel_nhlt_get_dmic_geo(&pci->dev, nhlt)) 423 ret = 1; 424 intel_nhlt_free(nhlt); 425 } 426 return ret; 427} 428 429#if IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL_SOUNDWIRE) 430static int snd_intel_dsp_check_soundwire(struct pci_dev *pci) 431{ 432 struct sdw_intel_acpi_info info; 433 acpi_handle handle; 434 int ret; 435 436 handle = ACPI_HANDLE(&pci->dev); 437 438 ret = sdw_intel_acpi_scan(handle, &info); 439 if (ret < 0) 440 return ret; 441 442 return info.link_mask; 443} 444#else 445static int snd_intel_dsp_check_soundwire(struct pci_dev *pci) 446{ 447 return 0; 448} 449#endif 450 451int snd_intel_dsp_driver_probe(struct pci_dev *pci) 452{ 453 const struct config_entry *cfg; 454 455 /* Intel vendor only */ 456 if (pci->vendor != 0x8086) 457 return SND_INTEL_DSP_DRIVER_ANY; 458 459 if (dsp_driver > 0 && dsp_driver <= SND_INTEL_DSP_DRIVER_LAST) 460 return dsp_driver; 461 462 /* 463 * detect DSP by checking class/subclass/prog-id information 464 * class=04 subclass 03 prog-if 00: no DSP, use legacy driver 465 * class=04 subclass 01 prog-if 00: DSP is present 466 * (and may be required e.g. for DMIC or SSP support) 467 * class=04 subclass 03 prog-if 80: use DSP or legacy mode 468 */ 469 if (pci->class == 0x040300) 470 return SND_INTEL_DSP_DRIVER_LEGACY; 471 if (pci->class != 0x040100 && pci->class != 0x040380) { 472 dev_err(&pci->dev, "Unknown PCI class/subclass/prog-if information (0x%06x) found, selecting HDAudio legacy driver\n", pci->class); 473 return SND_INTEL_DSP_DRIVER_LEGACY; 474 } 475 476 dev_info(&pci->dev, "DSP detected with PCI class/subclass/prog-if info 0x%06x\n", pci->class); 477 478 /* find the configuration for the specific device */ 479 cfg = snd_intel_dsp_find_config(pci, config_table, ARRAY_SIZE(config_table)); 480 if (!cfg) 481 return SND_INTEL_DSP_DRIVER_ANY; 482 483 if (cfg->flags & FLAG_SOF) { 484 if (cfg->flags & FLAG_SOF_ONLY_IF_SOUNDWIRE && 485 snd_intel_dsp_check_soundwire(pci) > 0) { 486 dev_info(&pci->dev, "SoundWire enabled on CannonLake+ platform, using SOF driver\n"); 487 return SND_INTEL_DSP_DRIVER_SOF; 488 } 489 if (cfg->flags & FLAG_SOF_ONLY_IF_DMIC && 490 snd_intel_dsp_check_dmic(pci)) { 491 dev_info(&pci->dev, "Digital mics found on Skylake+ platform, using SOF driver\n"); 492 return SND_INTEL_DSP_DRIVER_SOF; 493 } 494 if (!(cfg->flags & FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE)) 495 return SND_INTEL_DSP_DRIVER_SOF; 496 } 497 498 499 if (cfg->flags & FLAG_SST) { 500 if (cfg->flags & FLAG_SST_ONLY_IF_DMIC) { 501 if (snd_intel_dsp_check_dmic(pci)) { 502 dev_info(&pci->dev, "Digital mics found on Skylake+ platform, using SST driver\n"); 503 return SND_INTEL_DSP_DRIVER_SST; 504 } 505 } else { 506 return SND_INTEL_DSP_DRIVER_SST; 507 } 508 } 509 510 return SND_INTEL_DSP_DRIVER_LEGACY; 511} 512EXPORT_SYMBOL_GPL(snd_intel_dsp_driver_probe); 513 514MODULE_LICENSE("GPL v2"); 515MODULE_DESCRIPTION("Intel DSP config driver"); 516MODULE_IMPORT_NS(SOUNDWIRE_INTEL_INIT); 517