1 | // SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) |
2 | // |
3 | // This file is provided under a dual BSD/GPLv2 license. When using or |
4 | // redistributing this file, you may do so under either license. |
5 | // |
6 | // Copyright(c) 2018 Intel Corporation. All rights reserved. |
7 | // |
8 | // Author: Liam Girdwood <liam.r.girdwood@linux.intel.com> |
9 | // |
10 | |
11 | #include <linux/firmware.h> |
12 | #include <linux/dmi.h> |
13 | #include <linux/module.h> |
14 | #include <linux/pci.h> |
15 | #include <linux/platform_data/x86/soc.h> |
16 | #include <linux/pm_runtime.h> |
17 | #include <sound/soc-acpi.h> |
18 | #include <sound/soc-acpi-intel-match.h> |
19 | #include <sound/sof.h> |
20 | #include "ops.h" |
21 | #include "sof-pci-dev.h" |
22 | |
23 | static char *fw_path; |
24 | module_param(fw_path, charp, 0444); |
25 | MODULE_PARM_DESC(fw_path, "alternate path for SOF firmware." ); |
26 | |
27 | static char *fw_filename; |
28 | module_param(fw_filename, charp, 0444); |
29 | MODULE_PARM_DESC(fw_filename, "alternate filename for SOF firmware." ); |
30 | |
31 | static char *lib_path; |
32 | module_param(lib_path, charp, 0444); |
33 | MODULE_PARM_DESC(lib_path, "alternate path for SOF firmware libraries." ); |
34 | |
35 | static char *tplg_path; |
36 | module_param(tplg_path, charp, 0444); |
37 | MODULE_PARM_DESC(tplg_path, "alternate path for SOF topology." ); |
38 | |
39 | static char *tplg_filename; |
40 | module_param(tplg_filename, charp, 0444); |
41 | MODULE_PARM_DESC(tplg_filename, "alternate filename for SOF topology." ); |
42 | |
43 | static int sof_pci_debug; |
44 | module_param_named(sof_pci_debug, sof_pci_debug, int, 0444); |
45 | MODULE_PARM_DESC(sof_pci_debug, "SOF PCI debug options (0x0 all off)" ); |
46 | |
47 | static int sof_pci_ipc_type = -1; |
48 | module_param_named(ipc_type, sof_pci_ipc_type, int, 0444); |
49 | MODULE_PARM_DESC(ipc_type, "Force SOF IPC type. 0 - IPC3, 1 - IPC4" ); |
50 | |
51 | static const char *sof_dmi_override_tplg_name; |
52 | static bool ; |
53 | |
54 | #define SOF_PCI_DISABLE_PM_RUNTIME BIT(0) |
55 | |
56 | static int sof_tplg_cb(const struct dmi_system_id *id) |
57 | { |
58 | sof_dmi_override_tplg_name = id->driver_data; |
59 | return 1; |
60 | } |
61 | |
62 | static const struct dmi_system_id sof_tplg_table[] = { |
63 | { |
64 | .callback = sof_tplg_cb, |
65 | .matches = { |
66 | DMI_MATCH(DMI_PRODUCT_FAMILY, "Google_Volteer" ), |
67 | DMI_MATCH(DMI_OEM_STRING, "AUDIO-MAX98373_ALC5682I_I2S_UP4" ), |
68 | }, |
69 | .driver_data = "sof-tgl-rt5682-ssp0-max98373-ssp2.tplg" , |
70 | }, |
71 | { |
72 | .callback = sof_tplg_cb, |
73 | .matches = { |
74 | DMI_MATCH(DMI_SYS_VENDOR, "Intel Corporation" ), |
75 | DMI_MATCH(DMI_PRODUCT_NAME, "Alder Lake Client Platform" ), |
76 | DMI_MATCH(DMI_OEM_STRING, "AUDIO-ADL_MAX98373_ALC5682I_I2S" ), |
77 | }, |
78 | .driver_data = "sof-adl-rt5682-ssp0-max98373-ssp2.tplg" , |
79 | }, |
80 | { |
81 | .callback = sof_tplg_cb, |
82 | .matches = { |
83 | DMI_MATCH(DMI_PRODUCT_FAMILY, "Google_Brya" ), |
84 | DMI_MATCH(DMI_OEM_STRING, "AUDIO-MAX98390_ALC5682I_I2S" ), |
85 | }, |
86 | .driver_data = "sof-adl-max98390-ssp2-rt5682-ssp0.tplg" , |
87 | }, |
88 | { |
89 | .callback = sof_tplg_cb, |
90 | .matches = { |
91 | DMI_MATCH(DMI_PRODUCT_FAMILY, "Google_Brya" ), |
92 | DMI_MATCH(DMI_OEM_STRING, "AUDIO_AMP-MAX98360_ALC5682VS_I2S_2WAY" ), |
93 | }, |
94 | .driver_data = "sof-adl-max98360a-rt5682-2way.tplg" , |
95 | }, |
96 | { |
97 | .callback = sof_tplg_cb, |
98 | .matches = { |
99 | DMI_MATCH(DMI_PRODUCT_FAMILY, "Google_Brya" ), |
100 | DMI_MATCH(DMI_OEM_STRING, "AUDIO-AUDIO_MAX98357_ALC5682I_I2S_2WAY" ), |
101 | }, |
102 | .driver_data = "sof-adl-max98357a-rt5682-2way.tplg" , |
103 | }, |
104 | { |
105 | .callback = sof_tplg_cb, |
106 | .matches = { |
107 | DMI_MATCH(DMI_PRODUCT_FAMILY, "Google_Brya" ), |
108 | DMI_MATCH(DMI_OEM_STRING, "AUDIO-MAX98360_ALC5682I_I2S_AMP_SSP2" ), |
109 | }, |
110 | .driver_data = "sof-adl-max98357a-rt5682.tplg" , |
111 | }, |
112 | {} |
113 | }; |
114 | |
115 | /* all Up boards use the community key */ |
116 | static int (const struct dmi_system_id *id) |
117 | { |
118 | sof_dmi_use_community_key = true; |
119 | return 1; |
120 | } |
121 | |
122 | /* |
123 | * For ApolloLake Chromebooks we want to force the use of the Intel production key. |
124 | * All newer platforms use the community key |
125 | */ |
126 | static int (const struct dmi_system_id *id) |
127 | { |
128 | if (!soc_intel_is_apl()) |
129 | sof_dmi_use_community_key = true; |
130 | return 1; |
131 | } |
132 | |
133 | static const struct dmi_system_id [] = { |
134 | { |
135 | .ident = "Up boards" , |
136 | .callback = up_use_community_key, |
137 | .matches = { |
138 | DMI_MATCH(DMI_SYS_VENDOR, "AAEON" ), |
139 | } |
140 | }, |
141 | { |
142 | .ident = "Google Chromebooks" , |
143 | .callback = chromebook_use_community_key, |
144 | .matches = { |
145 | DMI_MATCH(DMI_PRODUCT_FAMILY, "Google" ), |
146 | } |
147 | }, |
148 | { |
149 | .ident = "Google firmware" , |
150 | .callback = chromebook_use_community_key, |
151 | .matches = { |
152 | DMI_MATCH(DMI_BIOS_VERSION, "Google" ), |
153 | } |
154 | }, |
155 | {}, |
156 | }; |
157 | |
158 | const struct dev_pm_ops sof_pci_pm = { |
159 | .prepare = snd_sof_prepare, |
160 | .complete = snd_sof_complete, |
161 | SET_SYSTEM_SLEEP_PM_OPS(snd_sof_suspend, snd_sof_resume) |
162 | SET_RUNTIME_PM_OPS(snd_sof_runtime_suspend, snd_sof_runtime_resume, |
163 | snd_sof_runtime_idle) |
164 | }; |
165 | EXPORT_SYMBOL_NS(sof_pci_pm, SND_SOC_SOF_PCI_DEV); |
166 | |
167 | static void sof_pci_probe_complete(struct device *dev) |
168 | { |
169 | dev_dbg(dev, "Completing SOF PCI probe" ); |
170 | |
171 | if (sof_pci_debug & SOF_PCI_DISABLE_PM_RUNTIME) |
172 | return; |
173 | |
174 | /* allow runtime_pm */ |
175 | pm_runtime_set_autosuspend_delay(dev, SND_SOF_SUSPEND_DELAY_MS); |
176 | pm_runtime_use_autosuspend(dev); |
177 | |
178 | /* |
179 | * runtime pm for pci device is "forbidden" by default. |
180 | * so call pm_runtime_allow() to enable it. |
181 | */ |
182 | pm_runtime_allow(dev); |
183 | |
184 | /* mark last_busy for pm_runtime to make sure not suspend immediately */ |
185 | pm_runtime_mark_last_busy(dev); |
186 | |
187 | /* follow recommendation in pci-driver.c to decrement usage counter */ |
188 | pm_runtime_put_noidle(dev); |
189 | } |
190 | |
191 | int sof_pci_probe(struct pci_dev *pci, const struct pci_device_id *pci_id) |
192 | { |
193 | struct sof_loadable_file_profile *path_override; |
194 | struct device *dev = &pci->dev; |
195 | const struct sof_dev_desc *desc = |
196 | (const struct sof_dev_desc *)pci_id->driver_data; |
197 | struct snd_sof_pdata *sof_pdata; |
198 | int ret; |
199 | |
200 | dev_dbg(&pci->dev, "PCI DSP detected" ); |
201 | |
202 | if (!desc) { |
203 | dev_err(dev, "error: no matching PCI descriptor\n" ); |
204 | return -ENODEV; |
205 | } |
206 | |
207 | if (!desc->ops) { |
208 | dev_err(dev, "error: no matching PCI descriptor ops\n" ); |
209 | return -ENODEV; |
210 | } |
211 | |
212 | sof_pdata = devm_kzalloc(dev, size: sizeof(*sof_pdata), GFP_KERNEL); |
213 | if (!sof_pdata) |
214 | return -ENOMEM; |
215 | |
216 | ret = pcim_enable_device(pdev: pci); |
217 | if (ret < 0) |
218 | return ret; |
219 | |
220 | ret = pci_request_regions(pci, "Audio DSP" ); |
221 | if (ret < 0) |
222 | return ret; |
223 | |
224 | sof_pdata->name = pci_name(pdev: pci); |
225 | |
226 | /* PCI defines a vendor ID of 0xFFFF as invalid. */ |
227 | if (pci->subsystem_vendor != 0xFFFF) { |
228 | sof_pdata->subsystem_vendor = pci->subsystem_vendor; |
229 | sof_pdata->subsystem_device = pci->subsystem_device; |
230 | sof_pdata->subsystem_id_set = true; |
231 | } |
232 | |
233 | sof_pdata->desc = desc; |
234 | sof_pdata->dev = dev; |
235 | |
236 | path_override = &sof_pdata->ipc_file_profile_base; |
237 | |
238 | if (sof_pci_ipc_type < 0) { |
239 | path_override->ipc_type = desc->ipc_default; |
240 | } else if (sof_pci_ipc_type < SOF_IPC_TYPE_COUNT) { |
241 | path_override->ipc_type = sof_pci_ipc_type; |
242 | } else { |
243 | dev_err(dev, "Invalid IPC type requested: %d\n" , sof_pci_ipc_type); |
244 | ret = -EINVAL; |
245 | goto out; |
246 | } |
247 | |
248 | path_override->fw_path = fw_path; |
249 | path_override->fw_name = fw_filename; |
250 | path_override->fw_lib_path = lib_path; |
251 | path_override->tplg_path = tplg_path; |
252 | |
253 | if (dmi_check_system(list: community_key_platforms) && |
254 | sof_dmi_use_community_key) { |
255 | path_override->fw_path_postfix = "community" ; |
256 | path_override->fw_lib_path_postfix = "community" ; |
257 | } |
258 | |
259 | /* |
260 | * the topology filename will be provided in the machine descriptor, unless |
261 | * it is overridden by a module parameter or DMI quirk. |
262 | */ |
263 | if (tplg_filename) { |
264 | path_override->tplg_name = tplg_filename; |
265 | } else { |
266 | dmi_check_system(list: sof_tplg_table); |
267 | if (sof_dmi_override_tplg_name) |
268 | path_override->tplg_name = sof_dmi_override_tplg_name; |
269 | } |
270 | |
271 | /* set callback to be called on successful device probe to enable runtime_pm */ |
272 | sof_pdata->sof_probe_complete = sof_pci_probe_complete; |
273 | |
274 | /* call sof helper for DSP hardware probe */ |
275 | ret = snd_sof_device_probe(dev, plat_data: sof_pdata); |
276 | |
277 | out: |
278 | if (ret) |
279 | pci_release_regions(pci); |
280 | |
281 | return ret; |
282 | } |
283 | EXPORT_SYMBOL_NS(sof_pci_probe, SND_SOC_SOF_PCI_DEV); |
284 | |
285 | void sof_pci_remove(struct pci_dev *pci) |
286 | { |
287 | /* call sof helper for DSP hardware remove */ |
288 | snd_sof_device_remove(dev: &pci->dev); |
289 | |
290 | /* follow recommendation in pci-driver.c to increment usage counter */ |
291 | if (snd_sof_device_probe_completed(dev: &pci->dev) && |
292 | !(sof_pci_debug & SOF_PCI_DISABLE_PM_RUNTIME)) |
293 | pm_runtime_get_noresume(dev: &pci->dev); |
294 | |
295 | /* release pci regions and disable device */ |
296 | pci_release_regions(pci); |
297 | } |
298 | EXPORT_SYMBOL_NS(sof_pci_remove, SND_SOC_SOF_PCI_DEV); |
299 | |
300 | void sof_pci_shutdown(struct pci_dev *pci) |
301 | { |
302 | snd_sof_device_shutdown(dev: &pci->dev); |
303 | } |
304 | EXPORT_SYMBOL_NS(sof_pci_shutdown, SND_SOC_SOF_PCI_DEV); |
305 | |
306 | MODULE_LICENSE("Dual BSD/GPL" ); |
307 | |