Commit | Line | Data |
---|---|---|
82d9d54a JK |
1 | // SPDX-License-Identifier: GPL-2.0 |
2 | // Copyright (c) 2019 Jaroslav Kysela <perex@perex.cz> | |
3 | ||
4 | #include <linux/bits.h> | |
5 | #include <linux/dmi.h> | |
6 | #include <linux/module.h> | |
7 | #include <linux/pci.h> | |
8 | #include <sound/core.h> | |
9 | #include <sound/intel-dsp-config.h> | |
10 | #include <sound/intel-nhlt.h> | |
11 | ||
12 | static int dsp_driver; | |
13 | ||
14 | module_param(dsp_driver, int, 0444); | |
15 | MODULE_PARM_DESC(dsp_driver, "Force the DSP driver for Intel DSP (0=auto, 1=legacy, 2=SST, 3=SOF)"); | |
16 | ||
17 | #define FLAG_SST BIT(0) | |
18 | #define FLAG_SOF BIT(1) | |
19 | #define FLAG_SOF_ONLY_IF_DMIC BIT(16) | |
20 | ||
21 | struct config_entry { | |
22 | u32 flags; | |
23 | u16 device; | |
24 | const struct dmi_system_id *dmi_table; | |
25 | }; | |
26 | ||
27 | /* | |
28 | * configuration table | |
29 | * - the order of similar PCI ID entries is important! | |
30 | * - the first successful match will win | |
31 | */ | |
32 | static const struct config_entry config_table[] = { | |
cc8f81c7 PLB |
33 | /* Merrifield */ |
34 | #if IS_ENABLED(CONFIG_SND_SOC_SOF_MERRIFIELD) | |
82d9d54a | 35 | { |
cc8f81c7 PLB |
36 | .flags = FLAG_SOF, |
37 | .device = 0x119a, | |
82d9d54a | 38 | }, |
cc8f81c7 PLB |
39 | #endif |
40 | /* Broxton-T */ | |
41 | #if IS_ENABLED(CONFIG_SND_SOC_SOF_APOLLOLAKE) | |
82d9d54a JK |
42 | { |
43 | .flags = FLAG_SOF, | |
cc8f81c7 | 44 | .device = 0x1a98, |
82d9d54a JK |
45 | }, |
46 | #endif | |
cc8f81c7 PLB |
47 | /* |
48 | * Apollolake (Broxton-P) | |
49 | * the legacy HDaudio driver is used except on Up Squared (SOF) and | |
50 | * Chromebooks (SST) | |
51 | */ | |
52 | #if IS_ENABLED(CONFIG_SND_SOC_SOF_APOLLOLAKE) | |
82d9d54a | 53 | { |
cc8f81c7 PLB |
54 | .flags = FLAG_SOF, |
55 | .device = 0x5a98, | |
56 | .dmi_table = (const struct dmi_system_id []) { | |
57 | { | |
58 | .ident = "Up Squared", | |
59 | .matches = { | |
60 | DMI_MATCH(DMI_SYS_VENDOR, "AAEON"), | |
61 | DMI_MATCH(DMI_BOARD_NAME, "UP-APL01"), | |
62 | } | |
63 | }, | |
64 | {} | |
65 | } | |
82d9d54a | 66 | }, |
cc8f81c7 PLB |
67 | #endif |
68 | #if IS_ENABLED(CONFIG_SND_SOC_INTEL_APL) | |
82d9d54a | 69 | { |
cc8f81c7 PLB |
70 | .flags = FLAG_SST, |
71 | .device = 0x5a98, | |
72 | .dmi_table = (const struct dmi_system_id []) { | |
73 | { | |
74 | .ident = "Google Chromebooks", | |
75 | .matches = { | |
76 | DMI_MATCH(DMI_SYS_VENDOR, "Google"), | |
77 | } | |
78 | }, | |
79 | {} | |
80 | } | |
82d9d54a JK |
81 | }, |
82 | #endif | |
cc8f81c7 PLB |
83 | /* |
84 | * Skylake and Kabylake use legacy HDaudio driver except for Google | |
85 | * Chromebooks (SST) | |
86 | */ | |
87 | ||
88 | /* Sunrise Point-LP */ | |
89 | #if IS_ENABLED(CONFIG_SND_SOC_INTEL_SKL) | |
82d9d54a | 90 | { |
cc8f81c7 PLB |
91 | .flags = FLAG_SST, |
92 | .device = 0x9d70, | |
93 | .dmi_table = (const struct dmi_system_id []) { | |
94 | { | |
95 | .ident = "Google Chromebooks", | |
96 | .matches = { | |
97 | DMI_MATCH(DMI_SYS_VENDOR, "Google"), | |
98 | } | |
99 | }, | |
100 | {} | |
101 | } | |
82d9d54a JK |
102 | }, |
103 | #endif | |
cc8f81c7 PLB |
104 | /* Kabylake-LP */ |
105 | #if IS_ENABLED(CONFIG_SND_SOC_INTEL_KBL) | |
82d9d54a | 106 | { |
cc8f81c7 PLB |
107 | .flags = FLAG_SST, |
108 | .device = 0x9d71, | |
109 | .dmi_table = (const struct dmi_system_id []) { | |
110 | { | |
111 | .ident = "Google Chromebooks", | |
112 | .matches = { | |
113 | DMI_MATCH(DMI_SYS_VENDOR, "Google"), | |
114 | } | |
115 | }, | |
116 | {} | |
117 | } | |
82d9d54a JK |
118 | }, |
119 | #endif | |
cc8f81c7 PLB |
120 | |
121 | /* | |
122 | * Geminilake uses legacy HDaudio driver except for Google | |
123 | * Chromebooks | |
124 | */ | |
82d9d54a JK |
125 | /* Geminilake */ |
126 | #if IS_ENABLED(CONFIG_SND_SOC_SOF_GEMINILAKE) | |
127 | { | |
128 | .flags = FLAG_SOF, | |
129 | .device = 0x3198, | |
130 | .dmi_table = (const struct dmi_system_id []) { | |
131 | { | |
132 | .ident = "Google Chromebooks", | |
133 | .matches = { | |
134 | DMI_MATCH(DMI_SYS_VENDOR, "Google"), | |
135 | } | |
136 | }, | |
137 | {} | |
138 | } | |
139 | }, | |
140 | #endif | |
cc8f81c7 PLB |
141 | |
142 | /* | |
143 | * CoffeeLake, CannonLake, CometLake, IceLake, TigerLake use legacy | |
144 | * HDaudio driver except for Google Chromebooks and when DMICs are | |
145 | * present. Two cases are required since Coreboot does not expose NHLT | |
146 | * tables. | |
147 | * | |
148 | * When the Chromebook quirk is not present, it's based on information | |
149 | * that no such device exists. When the quirk is present, it could be | |
150 | * either based on product information or a placeholder. | |
151 | */ | |
152 | ||
153 | /* Cannonlake */ | |
154 | #if IS_ENABLED(CONFIG_SND_SOC_SOF_CANNONLAKE) | |
82d9d54a | 155 | { |
cc8f81c7 PLB |
156 | .flags = FLAG_SOF, |
157 | .device = 0x9dc8, | |
158 | .dmi_table = (const struct dmi_system_id []) { | |
159 | { | |
160 | .ident = "Google Chromebooks", | |
161 | .matches = { | |
162 | DMI_MATCH(DMI_SYS_VENDOR, "Google"), | |
163 | } | |
164 | }, | |
165 | {} | |
166 | } | |
82d9d54a | 167 | }, |
82d9d54a JK |
168 | { |
169 | .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC, | |
cc8f81c7 | 170 | .device = 0x9dc8, |
82d9d54a JK |
171 | }, |
172 | #endif | |
cc8f81c7 PLB |
173 | |
174 | /* Coffelake */ | |
175 | #if IS_ENABLED(CONFIG_SND_SOC_SOF_COFFEELAKE) | |
176 | { | |
177 | .flags = FLAG_SOF, | |
178 | .device = 0xa348, | |
179 | .dmi_table = (const struct dmi_system_id []) { | |
180 | { | |
181 | .ident = "Google Chromebooks", | |
182 | .matches = { | |
183 | DMI_MATCH(DMI_SYS_VENDOR, "Google"), | |
184 | } | |
185 | }, | |
186 | {} | |
187 | } | |
188 | }, | |
82d9d54a JK |
189 | { |
190 | .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC, | |
cc8f81c7 | 191 | .device = 0xa348, |
82d9d54a JK |
192 | }, |
193 | #endif | |
cc8f81c7 PLB |
194 | |
195 | /* Cometlake-LP */ | |
196 | #if IS_ENABLED(CONFIG_SND_SOC_SOF_COMETLAKE_LP) | |
82d9d54a JK |
197 | { |
198 | .flags = FLAG_SOF, | |
cc8f81c7 | 199 | .device = 0x02c8, |
82d9d54a JK |
200 | .dmi_table = (const struct dmi_system_id []) { |
201 | { | |
cc8f81c7 | 202 | .ident = "Google Chromebooks", |
82d9d54a | 203 | .matches = { |
cc8f81c7 | 204 | DMI_MATCH(DMI_SYS_VENDOR, "Google"), |
82d9d54a JK |
205 | } |
206 | }, | |
207 | {} | |
208 | } | |
209 | }, | |
82d9d54a | 210 | { |
cc8f81c7 PLB |
211 | .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC, |
212 | .device = 0x02c8, | |
82d9d54a JK |
213 | }, |
214 | #endif | |
cc8f81c7 PLB |
215 | /* Cometlake-H */ |
216 | #if IS_ENABLED(CONFIG_SND_SOC_SOF_COMETLAKE_H) | |
82d9d54a JK |
217 | { |
218 | .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC, | |
cc8f81c7 | 219 | .device = 0x06c8, |
82d9d54a JK |
220 | }, |
221 | #endif | |
cc8f81c7 PLB |
222 | |
223 | /* Icelake */ | |
224 | #if IS_ENABLED(CONFIG_SND_SOC_SOF_ICELAKE) | |
82d9d54a | 225 | { |
cc8f81c7 PLB |
226 | .flags = FLAG_SOF, |
227 | .device = 0x34c8, | |
228 | .dmi_table = (const struct dmi_system_id []) { | |
229 | { | |
230 | .ident = "Google Chromebooks", | |
231 | .matches = { | |
232 | DMI_MATCH(DMI_SYS_VENDOR, "Google"), | |
233 | } | |
234 | }, | |
235 | {} | |
236 | } | |
82d9d54a | 237 | }, |
82d9d54a JK |
238 | { |
239 | .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC, | |
cc8f81c7 | 240 | .device = 0x34c8, |
82d9d54a JK |
241 | }, |
242 | #endif | |
cc8f81c7 | 243 | |
82d9d54a JK |
244 | /* Tigerlake */ |
245 | #if IS_ENABLED(CONFIG_SND_SOC_SOF_TIGERLAKE) | |
cc8f81c7 PLB |
246 | { |
247 | .flags = FLAG_SOF, | |
248 | .device = 0xa0c8, | |
249 | .dmi_table = (const struct dmi_system_id []) { | |
250 | { | |
251 | .ident = "Google Chromebooks", | |
252 | .matches = { | |
253 | DMI_MATCH(DMI_SYS_VENDOR, "Google"), | |
254 | } | |
255 | }, | |
256 | {} | |
257 | } | |
258 | }, | |
259 | ||
82d9d54a JK |
260 | { |
261 | .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC, | |
262 | .device = 0xa0c8, | |
263 | }, | |
264 | #endif | |
cc8f81c7 PLB |
265 | |
266 | /* Elkhart Lake */ | |
267 | #if IS_ENABLED(CONFIG_SND_SOC_SOF_ELKHARTLAKE) | |
82d9d54a JK |
268 | { |
269 | .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC, | |
cc8f81c7 | 270 | .device = 0x4b55, |
82d9d54a JK |
271 | }, |
272 | #endif | |
cc8f81c7 | 273 | |
82d9d54a JK |
274 | }; |
275 | ||
276 | static const struct config_entry *snd_intel_dsp_find_config | |
277 | (struct pci_dev *pci, const struct config_entry *table, u32 len) | |
278 | { | |
279 | u16 device; | |
280 | ||
281 | device = pci->device; | |
282 | for (; len > 0; len--, table++) { | |
283 | if (table->device != device) | |
284 | continue; | |
285 | if (table->dmi_table && !dmi_check_system(table->dmi_table)) | |
286 | continue; | |
287 | return table; | |
288 | } | |
289 | return NULL; | |
290 | } | |
291 | ||
292 | static int snd_intel_dsp_check_dmic(struct pci_dev *pci) | |
293 | { | |
294 | struct nhlt_acpi_table *nhlt; | |
295 | int ret = 0; | |
296 | ||
297 | nhlt = intel_nhlt_init(&pci->dev); | |
298 | if (nhlt) { | |
299 | if (intel_nhlt_get_dmic_geo(&pci->dev, nhlt)) | |
300 | ret = 1; | |
301 | intel_nhlt_free(nhlt); | |
302 | } | |
303 | return ret; | |
304 | } | |
305 | ||
306 | int snd_intel_dsp_driver_probe(struct pci_dev *pci) | |
307 | { | |
308 | const struct config_entry *cfg; | |
309 | ||
82d9d54a | 310 | /* Intel vendor only */ |
91636a82 | 311 | if (pci->vendor != 0x8086) |
82d9d54a JK |
312 | return SND_INTEL_DSP_DRIVER_ANY; |
313 | ||
91636a82 TI |
314 | if (dsp_driver > 0 && dsp_driver <= SND_INTEL_DSP_DRIVER_LAST) |
315 | return dsp_driver; | |
316 | ||
82d9d54a JK |
317 | /* |
318 | * detect DSP by checking class/subclass/prog-id information | |
319 | * class=04 subclass 03 prog-if 00: no DSP, use legacy driver | |
320 | * class=04 subclass 01 prog-if 00: DSP is present | |
321 | * (and may be required e.g. for DMIC or SSP support) | |
322 | * class=04 subclass 03 prog-if 80: use DSP or legacy mode | |
323 | */ | |
324 | if (pci->class == 0x040300) | |
325 | return SND_INTEL_DSP_DRIVER_LEGACY; | |
326 | if (pci->class != 0x040100 && pci->class != 0x040380) { | |
327 | dev_err(&pci->dev, "Unknown PCI class/subclass/prog-if information (0x%06x) found, selecting HDA legacy driver\n", pci->class); | |
328 | return SND_INTEL_DSP_DRIVER_LEGACY; | |
329 | } | |
330 | ||
331 | dev_info(&pci->dev, "DSP detected with PCI class/subclass/prog-if info 0x%06x\n", pci->class); | |
332 | ||
333 | /* find the configuration for the specific device */ | |
334 | cfg = snd_intel_dsp_find_config(pci, config_table, ARRAY_SIZE(config_table)); | |
335 | if (!cfg) | |
336 | return SND_INTEL_DSP_DRIVER_ANY; | |
337 | ||
338 | if (cfg->flags & FLAG_SOF) { | |
339 | if (cfg->flags & FLAG_SOF_ONLY_IF_DMIC) { | |
340 | if (snd_intel_dsp_check_dmic(pci)) { | |
341 | dev_info(&pci->dev, "Digital mics found on Skylake+ platform, using SOF driver\n"); | |
342 | return SND_INTEL_DSP_DRIVER_SOF; | |
343 | } | |
344 | } else { | |
345 | return SND_INTEL_DSP_DRIVER_SOF; | |
346 | } | |
347 | } | |
348 | ||
349 | if (cfg->flags & FLAG_SST) | |
350 | return SND_INTEL_DSP_DRIVER_SST; | |
351 | ||
352 | return SND_INTEL_DSP_DRIVER_LEGACY; | |
353 | } | |
354 | EXPORT_SYMBOL_GPL(snd_intel_dsp_driver_probe); | |
355 | ||
356 | MODULE_LICENSE("GPL v2"); | |
357 | MODULE_DESCRIPTION("Intel DSP config driver"); |