blob: 2a5ba9dca6b08bba5398862739bcbaee8d8987f2 [file] [log] [blame]
Olivier Deprez157378f2022-04-04 15:47:50 +02001// 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 }
334 },
335 {
336 .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
337 .device = 0xa0c8,
338 },
339 {
340 .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
341 .device = 0x43c8,
342 },
343 {
344 .flags = FLAG_SOF,
345 .device = 0xa0c8,
346 .codec_hid = "ESSX8336",
347 },
348#endif
349
350/* Elkhart Lake */
351#if IS_ENABLED(CONFIG_SND_SOC_SOF_ELKHARTLAKE)
352 {
353 .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC,
354 .device = 0x4b55,
355 },
356 {
357 .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC,
358 .device = 0x4b58,
359 },
360#endif
361
362};
363
364static const struct config_entry *snd_intel_dsp_find_config
365 (struct pci_dev *pci, const struct config_entry *table, u32 len)
366{
367 u16 device;
368
369 device = pci->device;
370 for (; len > 0; len--, table++) {
371 if (table->device != device)
372 continue;
373 if (table->dmi_table && !dmi_check_system(table->dmi_table))
374 continue;
375 if (table->codec_hid[0] && !acpi_dev_present(table->codec_hid, NULL, -1))
376 continue;
377 return table;
378 }
379 return NULL;
380}
381
382static int snd_intel_dsp_check_dmic(struct pci_dev *pci)
383{
384 struct nhlt_acpi_table *nhlt;
385 int ret = 0;
386
387 nhlt = intel_nhlt_init(&pci->dev);
388 if (nhlt) {
389 if (intel_nhlt_get_dmic_geo(&pci->dev, nhlt))
390 ret = 1;
391 intel_nhlt_free(nhlt);
392 }
393 return ret;
394}
395
396#if IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL_SOUNDWIRE)
397static int snd_intel_dsp_check_soundwire(struct pci_dev *pci)
398{
399 struct sdw_intel_acpi_info info;
400 acpi_handle handle;
401 int ret;
402
403 handle = ACPI_HANDLE(&pci->dev);
404
405 ret = sdw_intel_acpi_scan(handle, &info);
406 if (ret < 0)
407 return ret;
408
409 return info.link_mask;
410}
411#else
412static int snd_intel_dsp_check_soundwire(struct pci_dev *pci)
413{
414 return 0;
415}
416#endif
417
418int snd_intel_dsp_driver_probe(struct pci_dev *pci)
419{
420 const struct config_entry *cfg;
421
422 /* Intel vendor only */
423 if (pci->vendor != 0x8086)
424 return SND_INTEL_DSP_DRIVER_ANY;
425
426 if (dsp_driver > 0 && dsp_driver <= SND_INTEL_DSP_DRIVER_LAST)
427 return dsp_driver;
428
429 /*
430 * detect DSP by checking class/subclass/prog-id information
431 * class=04 subclass 03 prog-if 00: no DSP, use legacy driver
432 * class=04 subclass 01 prog-if 00: DSP is present
433 * (and may be required e.g. for DMIC or SSP support)
434 * class=04 subclass 03 prog-if 80: use DSP or legacy mode
435 */
436 if (pci->class == 0x040300)
437 return SND_INTEL_DSP_DRIVER_LEGACY;
438 if (pci->class != 0x040100 && pci->class != 0x040380) {
439 dev_err(&pci->dev, "Unknown PCI class/subclass/prog-if information (0x%06x) found, selecting HDAudio legacy driver\n", pci->class);
440 return SND_INTEL_DSP_DRIVER_LEGACY;
441 }
442
443 dev_info(&pci->dev, "DSP detected with PCI class/subclass/prog-if info 0x%06x\n", pci->class);
444
445 /* find the configuration for the specific device */
446 cfg = snd_intel_dsp_find_config(pci, config_table, ARRAY_SIZE(config_table));
447 if (!cfg)
448 return SND_INTEL_DSP_DRIVER_ANY;
449
450 if (cfg->flags & FLAG_SOF) {
451 if (cfg->flags & FLAG_SOF_ONLY_IF_SOUNDWIRE &&
452 snd_intel_dsp_check_soundwire(pci) > 0) {
453 dev_info(&pci->dev, "SoundWire enabled on CannonLake+ platform, using SOF driver\n");
454 return SND_INTEL_DSP_DRIVER_SOF;
455 }
456 if (cfg->flags & FLAG_SOF_ONLY_IF_DMIC &&
457 snd_intel_dsp_check_dmic(pci)) {
458 dev_info(&pci->dev, "Digital mics found on Skylake+ platform, using SOF driver\n");
459 return SND_INTEL_DSP_DRIVER_SOF;
460 }
461 if (!(cfg->flags & FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE))
462 return SND_INTEL_DSP_DRIVER_SOF;
463 }
464
465
466 if (cfg->flags & FLAG_SST) {
467 if (cfg->flags & FLAG_SST_ONLY_IF_DMIC) {
468 if (snd_intel_dsp_check_dmic(pci)) {
469 dev_info(&pci->dev, "Digital mics found on Skylake+ platform, using SST driver\n");
470 return SND_INTEL_DSP_DRIVER_SST;
471 }
472 } else {
473 return SND_INTEL_DSP_DRIVER_SST;
474 }
475 }
476
477 return SND_INTEL_DSP_DRIVER_LEGACY;
478}
479EXPORT_SYMBOL_GPL(snd_intel_dsp_driver_probe);
480
481MODULE_LICENSE("GPL v2");
482MODULE_DESCRIPTION("Intel DSP config driver");
483MODULE_IMPORT_NS(SOUNDWIRE_INTEL_INIT);