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 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 */
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), 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 #endif
376
377 /* Lunar Lake */
378 #if IS_ENABLED(CONFIG_SND_SOC_SOF_LUNARLAKE)
379 /* Lunarlake-P */
380 {
381 .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
382 .device = PCI_DEVICE_ID_INTEL_HDA_LNL_P,
383 },
384 #endif
385 };
386
snd_intel_dsp_find_config(struct pci_dev * pci,const struct config_entry * table,u32 len)387 static const struct config_entry *snd_intel_dsp_find_config
388 (struct pci_dev *pci, const struct config_entry *table, u32 len)
389 {
390 u16 device;
391
392 device = pci->device;
393 for (; len > 0; len--, table++) {
394 if (table->device != device)
395 continue;
396 if (table->dmi_table && !dmi_check_system(table->dmi_table))
397 continue;
398 if (table->codec_hid[0] && !acpi_dev_present(table->codec_hid, NULL, -1))
399 continue;
400 return table;
401 }
402 return NULL;
403 }
404
snd_intel_dsp_check_dmic(struct pci_dev * pci)405 static int snd_intel_dsp_check_dmic(struct pci_dev *pci)
406 {
407 struct nhlt_acpi_table *nhlt;
408 int ret = 0;
409
410 nhlt = intel_nhlt_init(&pci->dev);
411 if (nhlt) {
412 if (intel_nhlt_get_dmic_geo(&pci->dev, nhlt))
413 ret = 1;
414 intel_nhlt_free(nhlt);
415 }
416 return ret;
417 }
418
419 #if IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL_SOUNDWIRE)
snd_intel_dsp_check_soundwire(struct pci_dev * pci)420 static int snd_intel_dsp_check_soundwire(struct pci_dev *pci)
421 {
422 struct sdw_intel_acpi_info info;
423 acpi_handle handle;
424 int ret;
425
426 handle = ACPI_HANDLE(&pci->dev);
427
428 ret = sdw_intel_acpi_scan(handle, &info);
429 if (ret < 0)
430 return ret;
431
432 return info.link_mask;
433 }
434 #else
snd_intel_dsp_check_soundwire(struct pci_dev * pci)435 static int snd_intel_dsp_check_soundwire(struct pci_dev *pci)
436 {
437 return 0;
438 }
439 #endif
440
snd_intel_dsp_driver_probe(struct pci_dev * pci)441 int snd_intel_dsp_driver_probe(struct pci_dev *pci)
442 {
443 const struct config_entry *cfg;
444
445 /* Intel vendor only */
446 if (pci->vendor != 0x8086)
447 return SND_INTEL_DSP_DRIVER_ANY;
448
449 if (dsp_driver > 0 && dsp_driver <= SND_INTEL_DSP_DRIVER_LAST)
450 return dsp_driver;
451
452 /*
453 * detect DSP by checking class/subclass/prog-id information
454 * class=04 subclass 03 prog-if 00: no DSP, use legacy driver
455 * class=04 subclass 01 prog-if 00: DSP is present
456 * (and may be required e.g. for DMIC or SSP support)
457 * class=04 subclass 03 prog-if 80: use DSP or legacy mode
458 */
459 if (pci->class == 0x040300)
460 return SND_INTEL_DSP_DRIVER_LEGACY;
461 if (pci->class != 0x040100 && pci->class != 0x040380) {
462 dev_err(&pci->dev, "Unknown PCI class/subclass/prog-if information (0x%06x) found, selecting HDAudio legacy driver\n", pci->class);
463 return SND_INTEL_DSP_DRIVER_LEGACY;
464 }
465
466 dev_info(&pci->dev, "DSP detected with PCI class/subclass/prog-if info 0x%06x\n", pci->class);
467
468 /* find the configuration for the specific device */
469 cfg = snd_intel_dsp_find_config(pci, config_table, ARRAY_SIZE(config_table));
470 if (!cfg)
471 return SND_INTEL_DSP_DRIVER_ANY;
472
473 if (cfg->flags & FLAG_SOF) {
474 if (cfg->flags & FLAG_SOF_ONLY_IF_SOUNDWIRE &&
475 snd_intel_dsp_check_soundwire(pci) > 0) {
476 dev_info(&pci->dev, "SoundWire enabled on CannonLake+ platform, using SOF driver\n");
477 return SND_INTEL_DSP_DRIVER_SOF;
478 }
479 if (cfg->flags & FLAG_SOF_ONLY_IF_DMIC &&
480 snd_intel_dsp_check_dmic(pci)) {
481 dev_info(&pci->dev, "Digital mics found on Skylake+ platform, using SOF driver\n");
482 return SND_INTEL_DSP_DRIVER_SOF;
483 }
484 if (!(cfg->flags & FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE))
485 return SND_INTEL_DSP_DRIVER_SOF;
486 }
487
488
489 if (cfg->flags & FLAG_SST) {
490 if (cfg->flags & FLAG_SST_ONLY_IF_DMIC) {
491 if (snd_intel_dsp_check_dmic(pci)) {
492 dev_info(&pci->dev, "Digital mics found on Skylake+ platform, using SST driver\n");
493 return SND_INTEL_DSP_DRIVER_SST;
494 }
495 } else {
496 return SND_INTEL_DSP_DRIVER_SST;
497 }
498 }
499
500 return SND_INTEL_DSP_DRIVER_LEGACY;
501 }
502 EXPORT_SYMBOL_GPL(snd_intel_dsp_driver_probe);
503
504 MODULE_LICENSE("GPL v2");
505 MODULE_DESCRIPTION("Intel DSP config driver");
506 MODULE_IMPORT_NS(SOUNDWIRE_INTEL_INIT);
507