• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 	/* 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 
snd_intel_dsp_find_config(struct pci_dev * pci,const struct config_entry * table,u32 len)397 static 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 
snd_intel_dsp_check_dmic(struct pci_dev * pci)415 static 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)
snd_intel_dsp_check_soundwire(struct pci_dev * pci)430 static 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
snd_intel_dsp_check_soundwire(struct pci_dev * pci)445 static int snd_intel_dsp_check_soundwire(struct pci_dev *pci)
446 {
447 	return 0;
448 }
449 #endif
450 
snd_intel_dsp_driver_probe(struct pci_dev * pci)451 int 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 }
512 EXPORT_SYMBOL_GPL(snd_intel_dsp_driver_probe);
513 
514 MODULE_LICENSE("GPL v2");
515 MODULE_DESCRIPTION("Intel DSP config driver");
516 MODULE_IMPORT_NS(SOUNDWIRE_INTEL_INIT);
517