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