Linux Audio

Check our new training course

In-person Linux kernel drivers training

Jun 16-20, 2025
Register
Loading...
Note: File does not exist in v4.6.
  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};
 34
 35/*
 36 * configuration table
 37 * - the order of similar PCI ID entries is important!
 38 * - the first successful match will win
 39 */
 40static const struct config_entry config_table[] = {
 41/* Merrifield */
 42#if IS_ENABLED(CONFIG_SND_SOC_SOF_MERRIFIELD)
 43	{
 44		.flags = FLAG_SOF,
 45		.device = 0x119a,
 46	},
 47#endif
 48/* Broxton-T */
 49#if IS_ENABLED(CONFIG_SND_SOC_SOF_APOLLOLAKE)
 50	{
 51		.flags = FLAG_SOF,
 52		.device = 0x1a98,
 53	},
 54#endif
 55/*
 56 * Apollolake (Broxton-P)
 57 * the legacy HDAudio driver is used except on Up Squared (SOF) and
 58 * Chromebooks (SST)
 59 */
 60#if IS_ENABLED(CONFIG_SND_SOC_SOF_APOLLOLAKE)
 61	{
 62		.flags = FLAG_SOF,
 63		.device = 0x5a98,
 64		.dmi_table = (const struct dmi_system_id []) {
 65			{
 66				.ident = "Up Squared",
 67				.matches = {
 68					DMI_MATCH(DMI_SYS_VENDOR, "AAEON"),
 69					DMI_MATCH(DMI_BOARD_NAME, "UP-APL01"),
 70				}
 71			},
 72			{}
 73		}
 74	},
 75#endif
 76#if IS_ENABLED(CONFIG_SND_SOC_INTEL_APL)
 77	{
 78		.flags = FLAG_SST,
 79		.device = 0x5a98,
 80		.dmi_table = (const struct dmi_system_id []) {
 81			{
 82				.ident = "Google Chromebooks",
 83				.matches = {
 84					DMI_MATCH(DMI_SYS_VENDOR, "Google"),
 85				}
 86			},
 87			{}
 88		}
 89	},
 90#endif
 91/*
 92 * Skylake and Kabylake use legacy HDAudio driver except for Google
 93 * Chromebooks (SST)
 94 */
 95
 96/* Sunrise Point-LP */
 97#if IS_ENABLED(CONFIG_SND_SOC_INTEL_SKL)
 98	{
 99		.flags = FLAG_SST,
100		.device = 0x9d70,
101		.dmi_table = (const struct dmi_system_id []) {
102			{
103				.ident = "Google Chromebooks",
104				.matches = {
105					DMI_MATCH(DMI_SYS_VENDOR, "Google"),
106				}
107			},
108			{}
109		}
110	},
111	{
112		.flags = FLAG_SST | FLAG_SST_ONLY_IF_DMIC,
113		.device = 0x9d70,
114	},
115#endif
116/* Kabylake-LP */
117#if IS_ENABLED(CONFIG_SND_SOC_INTEL_KBL)
118	{
119		.flags = FLAG_SST,
120		.device = 0x9d71,
121		.dmi_table = (const struct dmi_system_id []) {
122			{
123				.ident = "Google Chromebooks",
124				.matches = {
125					DMI_MATCH(DMI_SYS_VENDOR, "Google"),
126				}
127			},
128			{}
129		}
130	},
131	{
132		.flags = FLAG_SST | FLAG_SST_ONLY_IF_DMIC,
133		.device = 0x9d71,
134	},
135#endif
136
137/*
138 * Geminilake uses legacy HDAudio driver except for Google
139 * Chromebooks
140 */
141/* Geminilake */
142#if IS_ENABLED(CONFIG_SND_SOC_SOF_GEMINILAKE)
143	{
144		.flags = FLAG_SOF,
145		.device = 0x3198,
146		.dmi_table = (const struct dmi_system_id []) {
147			{
148				.ident = "Google Chromebooks",
149				.matches = {
150					DMI_MATCH(DMI_SYS_VENDOR, "Google"),
151				}
152			},
153			{}
154		}
155	},
156#endif
157
158/*
159 * CoffeeLake, CannonLake, CometLake, IceLake, TigerLake use legacy
160 * HDAudio driver except for Google Chromebooks and when DMICs are
161 * present. Two cases are required since Coreboot does not expose NHLT
162 * tables.
163 *
164 * When the Chromebook quirk is not present, it's based on information
165 * that no such device exists. When the quirk is present, it could be
166 * either based on product information or a placeholder.
167 */
168
169/* Cannonlake */
170#if IS_ENABLED(CONFIG_SND_SOC_SOF_CANNONLAKE)
171	{
172		.flags = FLAG_SOF,
173		.device = 0x9dc8,
174		.dmi_table = (const struct dmi_system_id []) {
175			{
176				.ident = "Google Chromebooks",
177				.matches = {
178					DMI_MATCH(DMI_SYS_VENDOR, "Google"),
179				}
180			},
181			{}
182		}
183	},
184	{
185		.flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
186		.device = 0x9dc8,
187	},
188#endif
189
190/* Coffelake */
191#if IS_ENABLED(CONFIG_SND_SOC_SOF_COFFEELAKE)
192	{
193		.flags = FLAG_SOF,
194		.device = 0xa348,
195		.dmi_table = (const struct dmi_system_id []) {
196			{
197				.ident = "Google Chromebooks",
198				.matches = {
199					DMI_MATCH(DMI_SYS_VENDOR, "Google"),
200				}
201			},
202			{}
203		}
204	},
205	{
206		.flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
207		.device = 0xa348,
208	},
209#endif
210
211#if IS_ENABLED(CONFIG_SND_SOC_SOF_COMETLAKE)
212/* Cometlake-LP */
213	{
214		.flags = FLAG_SOF,
215		.device = 0x02c8,
216		.dmi_table = (const struct dmi_system_id []) {
217			{
218				.ident = "Google Chromebooks",
219				.matches = {
220					DMI_MATCH(DMI_SYS_VENDOR, "Google"),
221				}
222			},
223			{
224				.matches = {
225					DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
226					DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "09C6")
227				},
228			},
229			{
230				/* early version of SKU 09C6 */
231				.matches = {
232					DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
233					DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0983")
234				},
235			},
236			{}
237		}
238	},
239	{
240		.flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
241		.device = 0x02c8,
242	},
243/* Cometlake-H */
244	{
245		.flags = FLAG_SOF,
246		.device = 0x06c8,
247		.dmi_table = (const struct dmi_system_id []) {
248			{
249				.matches = {
250					DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
251					DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "098F"),
252				},
253			},
254			{
255				.matches = {
256					DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
257					DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0990"),
258				},
259			},
260			{}
261		}
262	},
263	{
264		.flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
265		.device = 0x06c8,
266	},
267#endif
268
269/* Icelake */
270#if IS_ENABLED(CONFIG_SND_SOC_SOF_ICELAKE)
271	{
272		.flags = FLAG_SOF,
273		.device = 0x34c8,
274		.dmi_table = (const struct dmi_system_id []) {
275			{
276				.ident = "Google Chromebooks",
277				.matches = {
278					DMI_MATCH(DMI_SYS_VENDOR, "Google"),
279				}
280			},
281			{}
282		}
283	},
284	{
285		.flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
286		.device = 0x34c8,
287	},
288#endif
289
290/* Tigerlake */
291#if IS_ENABLED(CONFIG_SND_SOC_SOF_TIGERLAKE)
292	{
293		.flags = FLAG_SOF,
294		.device = 0xa0c8,
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 = 0xa0c8,
308	},
309#endif
310
311/* Elkhart Lake */
312#if IS_ENABLED(CONFIG_SND_SOC_SOF_ELKHARTLAKE)
313	{
314		.flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC,
315		.device = 0x4b55,
316	},
317#endif
318
319};
320
321static const struct config_entry *snd_intel_dsp_find_config
322		(struct pci_dev *pci, const struct config_entry *table, u32 len)
323{
324	u16 device;
325
326	device = pci->device;
327	for (; len > 0; len--, table++) {
328		if (table->device != device)
329			continue;
330		if (table->dmi_table && !dmi_check_system(table->dmi_table))
331			continue;
332		return table;
333	}
334	return NULL;
335}
336
337static int snd_intel_dsp_check_dmic(struct pci_dev *pci)
338{
339	struct nhlt_acpi_table *nhlt;
340	int ret = 0;
341
342	nhlt = intel_nhlt_init(&pci->dev);
343	if (nhlt) {
344		if (intel_nhlt_get_dmic_geo(&pci->dev, nhlt))
345			ret = 1;
346		intel_nhlt_free(nhlt);
347	}
348	return ret;
349}
350
351#if IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL_SOUNDWIRE)
352static int snd_intel_dsp_check_soundwire(struct pci_dev *pci)
353{
354	struct sdw_intel_acpi_info info;
355	acpi_handle handle;
356	int ret;
357
358	handle = ACPI_HANDLE(&pci->dev);
359
360	ret = sdw_intel_acpi_scan(handle, &info);
361	if (ret < 0)
362		return ret;
363
364	return info.link_mask;
365}
366#else
367static int snd_intel_dsp_check_soundwire(struct pci_dev *pci)
368{
369	return 0;
370}
371#endif
372
373int snd_intel_dsp_driver_probe(struct pci_dev *pci)
374{
375	const struct config_entry *cfg;
376
377	/* Intel vendor only */
378	if (pci->vendor != 0x8086)
379		return SND_INTEL_DSP_DRIVER_ANY;
380
381	if (dsp_driver > 0 && dsp_driver <= SND_INTEL_DSP_DRIVER_LAST)
382		return dsp_driver;
383
384	/*
385	 * detect DSP by checking class/subclass/prog-id information
386	 * class=04 subclass 03 prog-if 00: no DSP, use legacy driver
387	 * class=04 subclass 01 prog-if 00: DSP is present
388	 *  (and may be required e.g. for DMIC or SSP support)
389	 * class=04 subclass 03 prog-if 80: use DSP or legacy mode
390	 */
391	if (pci->class == 0x040300)
392		return SND_INTEL_DSP_DRIVER_LEGACY;
393	if (pci->class != 0x040100 && pci->class != 0x040380) {
394		dev_err(&pci->dev, "Unknown PCI class/subclass/prog-if information (0x%06x) found, selecting HDAudio legacy driver\n", pci->class);
395		return SND_INTEL_DSP_DRIVER_LEGACY;
396	}
397
398	dev_info(&pci->dev, "DSP detected with PCI class/subclass/prog-if info 0x%06x\n", pci->class);
399
400	/* find the configuration for the specific device */
401	cfg = snd_intel_dsp_find_config(pci, config_table, ARRAY_SIZE(config_table));
402	if (!cfg)
403		return SND_INTEL_DSP_DRIVER_ANY;
404
405	if (cfg->flags & FLAG_SOF) {
406		if (cfg->flags & FLAG_SOF_ONLY_IF_SOUNDWIRE &&
407		    snd_intel_dsp_check_soundwire(pci) > 0) {
408			dev_info(&pci->dev, "SoundWire enabled on CannonLake+ platform, using SOF driver\n");
409			return SND_INTEL_DSP_DRIVER_SOF;
410		}
411		if (cfg->flags & FLAG_SOF_ONLY_IF_DMIC &&
412		    snd_intel_dsp_check_dmic(pci)) {
413			dev_info(&pci->dev, "Digital mics found on Skylake+ platform, using SOF driver\n");
414			return SND_INTEL_DSP_DRIVER_SOF;
415		}
416		if (!(cfg->flags & FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE))
417			return SND_INTEL_DSP_DRIVER_SOF;
418	}
419
420
421	if (cfg->flags & FLAG_SST) {
422		if (cfg->flags & FLAG_SST_ONLY_IF_DMIC) {
423			if (snd_intel_dsp_check_dmic(pci)) {
424				dev_info(&pci->dev, "Digital mics found on Skylake+ platform, using SST driver\n");
425				return SND_INTEL_DSP_DRIVER_SST;
426			}
427		} else {
428			return SND_INTEL_DSP_DRIVER_SST;
429		}
430	}
431
432	return SND_INTEL_DSP_DRIVER_LEGACY;
433}
434EXPORT_SYMBOL_GPL(snd_intel_dsp_driver_probe);
435
436MODULE_LICENSE("GPL v2");
437MODULE_DESCRIPTION("Intel DSP config driver");
438MODULE_IMPORT_NS(SOUNDWIRE_INTEL_INIT);