]>
Commit | Line | Data |
---|---|---|
747503b1 LG |
1 | // SPDX-License-Identifier: (GPL-2.0 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 | // Authors: Liam Girdwood <liam.r.girdwood@linux.intel.com> | |
9 | // Ranjani Sridharan <ranjani.sridharan@linux.intel.com> | |
10 | // Rander Wang <rander.wang@intel.com> | |
11 | // Keyon Jie <yang.jie@linux.intel.com> | |
12 | // | |
13 | ||
14 | /* | |
15 | * Hardware interface for generic Intel audio DSP HDA IP | |
16 | */ | |
17 | ||
18 | #include <sound/hdaudio_ext.h> | |
19 | #include <sound/hda_register.h> | |
20 | #include "../ops.h" | |
21 | #include "hda.h" | |
22 | ||
23 | /* | |
24 | * DSP Core control. | |
25 | */ | |
26 | ||
27 | int hda_dsp_core_reset_enter(struct snd_sof_dev *sdev, unsigned int core_mask) | |
28 | { | |
29 | u32 adspcs; | |
30 | u32 reset; | |
31 | int ret; | |
32 | ||
33 | /* set reset bits for cores */ | |
34 | reset = HDA_DSP_ADSPCS_CRST_MASK(core_mask); | |
35 | snd_sof_dsp_update_bits_unlocked(sdev, HDA_DSP_BAR, | |
36 | HDA_DSP_REG_ADSPCS, | |
37 | reset, reset), | |
38 | ||
39 | /* poll with timeout to check if operation successful */ | |
40 | ret = snd_sof_dsp_read_poll_timeout(sdev, HDA_DSP_BAR, | |
41 | HDA_DSP_REG_ADSPCS, adspcs, | |
42 | ((adspcs & reset) == reset), | |
43 | HDA_DSP_REG_POLL_INTERVAL_US, | |
44 | HDA_DSP_RESET_TIMEOUT_US); | |
45 | ||
46 | /* has core entered reset ? */ | |
47 | adspcs = snd_sof_dsp_read(sdev, HDA_DSP_BAR, | |
48 | HDA_DSP_REG_ADSPCS); | |
49 | if ((adspcs & HDA_DSP_ADSPCS_CRST_MASK(core_mask)) != | |
50 | HDA_DSP_ADSPCS_CRST_MASK(core_mask)) { | |
51 | dev_err(sdev->dev, | |
52 | "error: reset enter failed: core_mask %x adspcs 0x%x\n", | |
53 | core_mask, adspcs); | |
54 | ret = -EIO; | |
55 | } | |
56 | ||
57 | return ret; | |
58 | } | |
59 | ||
60 | int hda_dsp_core_reset_leave(struct snd_sof_dev *sdev, unsigned int core_mask) | |
61 | { | |
62 | unsigned int crst; | |
63 | u32 adspcs; | |
64 | int ret; | |
65 | ||
66 | /* clear reset bits for cores */ | |
67 | snd_sof_dsp_update_bits_unlocked(sdev, HDA_DSP_BAR, | |
68 | HDA_DSP_REG_ADSPCS, | |
69 | HDA_DSP_ADSPCS_CRST_MASK(core_mask), | |
70 | 0); | |
71 | ||
72 | /* poll with timeout to check if operation successful */ | |
73 | crst = HDA_DSP_ADSPCS_CRST_MASK(core_mask); | |
74 | ret = snd_sof_dsp_read_poll_timeout(sdev, HDA_DSP_BAR, | |
75 | HDA_DSP_REG_ADSPCS, adspcs, | |
76 | !(adspcs & crst), | |
77 | HDA_DSP_REG_POLL_INTERVAL_US, | |
78 | HDA_DSP_RESET_TIMEOUT_US); | |
79 | ||
80 | /* has core left reset ? */ | |
81 | adspcs = snd_sof_dsp_read(sdev, HDA_DSP_BAR, | |
82 | HDA_DSP_REG_ADSPCS); | |
83 | if ((adspcs & HDA_DSP_ADSPCS_CRST_MASK(core_mask)) != 0) { | |
84 | dev_err(sdev->dev, | |
85 | "error: reset leave failed: core_mask %x adspcs 0x%x\n", | |
86 | core_mask, adspcs); | |
87 | ret = -EIO; | |
88 | } | |
89 | ||
90 | return ret; | |
91 | } | |
92 | ||
93 | int hda_dsp_core_stall_reset(struct snd_sof_dev *sdev, unsigned int core_mask) | |
94 | { | |
95 | /* stall core */ | |
96 | snd_sof_dsp_update_bits_unlocked(sdev, HDA_DSP_BAR, | |
97 | HDA_DSP_REG_ADSPCS, | |
98 | HDA_DSP_ADSPCS_CSTALL_MASK(core_mask), | |
99 | HDA_DSP_ADSPCS_CSTALL_MASK(core_mask)); | |
100 | ||
101 | /* set reset state */ | |
102 | return hda_dsp_core_reset_enter(sdev, core_mask); | |
103 | } | |
104 | ||
105 | int hda_dsp_core_run(struct snd_sof_dev *sdev, unsigned int core_mask) | |
106 | { | |
107 | int ret; | |
108 | ||
109 | /* leave reset state */ | |
110 | ret = hda_dsp_core_reset_leave(sdev, core_mask); | |
111 | if (ret < 0) | |
112 | return ret; | |
113 | ||
114 | /* run core */ | |
115 | dev_dbg(sdev->dev, "unstall/run core: core_mask = %x\n", core_mask); | |
116 | snd_sof_dsp_update_bits_unlocked(sdev, HDA_DSP_BAR, | |
117 | HDA_DSP_REG_ADSPCS, | |
118 | HDA_DSP_ADSPCS_CSTALL_MASK(core_mask), | |
119 | 0); | |
120 | ||
121 | /* is core now running ? */ | |
122 | if (!hda_dsp_core_is_enabled(sdev, core_mask)) { | |
123 | hda_dsp_core_stall_reset(sdev, core_mask); | |
124 | dev_err(sdev->dev, "error: DSP start core failed: core_mask %x\n", | |
125 | core_mask); | |
126 | ret = -EIO; | |
127 | } | |
128 | ||
129 | return ret; | |
130 | } | |
131 | ||
132 | /* | |
133 | * Power Management. | |
134 | */ | |
135 | ||
136 | int hda_dsp_core_power_up(struct snd_sof_dev *sdev, unsigned int core_mask) | |
137 | { | |
138 | unsigned int cpa; | |
139 | u32 adspcs; | |
140 | int ret; | |
141 | ||
142 | /* update bits */ | |
143 | snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, HDA_DSP_REG_ADSPCS, | |
144 | HDA_DSP_ADSPCS_SPA_MASK(core_mask), | |
145 | HDA_DSP_ADSPCS_SPA_MASK(core_mask)); | |
146 | ||
147 | /* poll with timeout to check if operation successful */ | |
148 | cpa = HDA_DSP_ADSPCS_CPA_MASK(core_mask); | |
149 | ret = snd_sof_dsp_read_poll_timeout(sdev, HDA_DSP_BAR, | |
150 | HDA_DSP_REG_ADSPCS, adspcs, | |
151 | (adspcs & cpa) == cpa, | |
152 | HDA_DSP_REG_POLL_INTERVAL_US, | |
153 | HDA_DSP_RESET_TIMEOUT_US); | |
154 | if (ret < 0) | |
155 | dev_err(sdev->dev, "error: timeout on core powerup\n"); | |
156 | ||
157 | /* did core power up ? */ | |
158 | adspcs = snd_sof_dsp_read(sdev, HDA_DSP_BAR, | |
159 | HDA_DSP_REG_ADSPCS); | |
160 | if ((adspcs & HDA_DSP_ADSPCS_CPA_MASK(core_mask)) != | |
161 | HDA_DSP_ADSPCS_CPA_MASK(core_mask)) { | |
162 | dev_err(sdev->dev, | |
163 | "error: power up core failed core_mask %xadspcs 0x%x\n", | |
164 | core_mask, adspcs); | |
165 | ret = -EIO; | |
166 | } | |
167 | ||
168 | return ret; | |
169 | } | |
170 | ||
171 | int hda_dsp_core_power_down(struct snd_sof_dev *sdev, unsigned int core_mask) | |
172 | { | |
173 | u32 adspcs; | |
174 | ||
175 | /* update bits */ | |
176 | snd_sof_dsp_update_bits_unlocked(sdev, HDA_DSP_BAR, | |
177 | HDA_DSP_REG_ADSPCS, | |
178 | HDA_DSP_ADSPCS_SPA_MASK(core_mask), 0); | |
179 | ||
180 | return snd_sof_dsp_read_poll_timeout(sdev, HDA_DSP_BAR, | |
181 | HDA_DSP_REG_ADSPCS, adspcs, | |
182 | !(adspcs & HDA_DSP_ADSPCS_SPA_MASK(core_mask)), | |
183 | HDA_DSP_REG_POLL_INTERVAL_US, | |
184 | HDA_DSP_PD_TIMEOUT * USEC_PER_MSEC); | |
185 | } | |
186 | ||
187 | bool hda_dsp_core_is_enabled(struct snd_sof_dev *sdev, | |
188 | unsigned int core_mask) | |
189 | { | |
190 | int val; | |
191 | bool is_enable; | |
192 | ||
193 | val = snd_sof_dsp_read(sdev, HDA_DSP_BAR, HDA_DSP_REG_ADSPCS); | |
194 | ||
195 | is_enable = ((val & HDA_DSP_ADSPCS_CPA_MASK(core_mask)) && | |
196 | (val & HDA_DSP_ADSPCS_SPA_MASK(core_mask)) && | |
197 | !(val & HDA_DSP_ADSPCS_CRST_MASK(core_mask)) && | |
198 | !(val & HDA_DSP_ADSPCS_CSTALL_MASK(core_mask))); | |
199 | ||
200 | dev_dbg(sdev->dev, "DSP core(s) enabled? %d : core_mask %x\n", | |
201 | is_enable, core_mask); | |
202 | ||
203 | return is_enable; | |
204 | } | |
205 | ||
206 | int hda_dsp_enable_core(struct snd_sof_dev *sdev, unsigned int core_mask) | |
207 | { | |
208 | int ret; | |
209 | ||
210 | /* return if core is already enabled */ | |
211 | if (hda_dsp_core_is_enabled(sdev, core_mask)) | |
212 | return 0; | |
213 | ||
214 | /* power up */ | |
215 | ret = hda_dsp_core_power_up(sdev, core_mask); | |
216 | if (ret < 0) { | |
217 | dev_err(sdev->dev, "error: dsp core power up failed: core_mask %x\n", | |
218 | core_mask); | |
219 | return ret; | |
220 | } | |
221 | ||
222 | return hda_dsp_core_run(sdev, core_mask); | |
223 | } | |
224 | ||
225 | int hda_dsp_core_reset_power_down(struct snd_sof_dev *sdev, | |
226 | unsigned int core_mask) | |
227 | { | |
228 | int ret; | |
229 | ||
230 | /* place core in reset prior to power down */ | |
231 | ret = hda_dsp_core_stall_reset(sdev, core_mask); | |
232 | if (ret < 0) { | |
233 | dev_err(sdev->dev, "error: dsp core reset failed: core_mask %x\n", | |
234 | core_mask); | |
235 | return ret; | |
236 | } | |
237 | ||
238 | /* power down core */ | |
239 | ret = hda_dsp_core_power_down(sdev, core_mask); | |
240 | if (ret < 0) { | |
241 | dev_err(sdev->dev, "error: dsp core power down fail mask %x: %d\n", | |
242 | core_mask, ret); | |
243 | return ret; | |
244 | } | |
245 | ||
246 | /* make sure we are in OFF state */ | |
247 | if (hda_dsp_core_is_enabled(sdev, core_mask)) { | |
248 | dev_err(sdev->dev, "error: dsp core disable fail mask %x: %d\n", | |
249 | core_mask, ret); | |
250 | ret = -EIO; | |
251 | } | |
252 | ||
253 | return ret; | |
254 | } | |
255 | ||
256 | void hda_dsp_ipc_int_enable(struct snd_sof_dev *sdev) | |
257 | { | |
258 | struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata; | |
259 | const struct sof_intel_dsp_desc *chip = hda->desc; | |
260 | ||
261 | /* enable IPC DONE and BUSY interrupts */ | |
262 | snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, chip->ipc_ctl, | |
263 | HDA_DSP_REG_HIPCCTL_DONE | HDA_DSP_REG_HIPCCTL_BUSY, | |
264 | HDA_DSP_REG_HIPCCTL_DONE | HDA_DSP_REG_HIPCCTL_BUSY); | |
265 | ||
266 | /* enable IPC interrupt */ | |
267 | snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, HDA_DSP_REG_ADSPIC, | |
268 | HDA_DSP_ADSPIC_IPC, HDA_DSP_ADSPIC_IPC); | |
269 | } | |
270 | ||
271 | void hda_dsp_ipc_int_disable(struct snd_sof_dev *sdev) | |
272 | { | |
273 | struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata; | |
274 | const struct sof_intel_dsp_desc *chip = hda->desc; | |
275 | ||
276 | /* disable IPC interrupt */ | |
277 | snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, HDA_DSP_REG_ADSPIC, | |
278 | HDA_DSP_ADSPIC_IPC, 0); | |
279 | ||
280 | /* disable IPC BUSY and DONE interrupt */ | |
281 | snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, chip->ipc_ctl, | |
282 | HDA_DSP_REG_HIPCCTL_BUSY | HDA_DSP_REG_HIPCCTL_DONE, 0); | |
283 | } | |
284 | ||
285 | static int hda_suspend(struct snd_sof_dev *sdev, int state) | |
286 | { | |
287 | struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata; | |
288 | const struct sof_intel_dsp_desc *chip = hda->desc; | |
289 | #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA) | |
290 | struct hdac_bus *bus = sof_to_bus(sdev); | |
291 | #endif | |
292 | int ret; | |
293 | ||
294 | /* disable IPC interrupts */ | |
295 | hda_dsp_ipc_int_disable(sdev); | |
296 | ||
297 | #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA) | |
298 | /* power down all hda link */ | |
299 | snd_hdac_ext_bus_link_power_down_all(bus); | |
300 | #endif | |
301 | ||
302 | /* power down DSP */ | |
303 | ret = hda_dsp_core_reset_power_down(sdev, chip->cores_mask); | |
304 | if (ret < 0) { | |
305 | dev_err(sdev->dev, | |
306 | "error: failed to power down core during suspend\n"); | |
307 | return ret; | |
308 | } | |
309 | ||
310 | #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA) | |
311 | /* disable ppcap interrupt */ | |
312 | snd_hdac_ext_bus_ppcap_int_enable(bus, false); | |
313 | snd_hdac_ext_bus_ppcap_enable(bus, false); | |
314 | ||
315 | /* disable hda bus irq and i/o */ | |
316 | snd_hdac_bus_stop_chip(bus); | |
317 | #else | |
318 | /* disable ppcap interrupt */ | |
319 | hda_dsp_ctrl_ppcap_enable(sdev, false); | |
320 | hda_dsp_ctrl_ppcap_int_enable(sdev, false); | |
321 | ||
322 | /* disable hda bus irq */ | |
323 | snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, SOF_HDA_INTCTL, | |
324 | SOF_HDA_INT_CTRL_EN | SOF_HDA_INT_GLOBAL_EN, | |
325 | 0); | |
326 | #endif | |
327 | ||
328 | /* disable LP retention mode */ | |
329 | snd_sof_pci_update_bits(sdev, PCI_PGCTL, | |
330 | PCI_PGCTL_LSRMD_MASK, PCI_PGCTL_LSRMD_MASK); | |
331 | ||
332 | /* reset controller */ | |
333 | ret = hda_dsp_ctrl_link_reset(sdev, true); | |
334 | if (ret < 0) { | |
335 | dev_err(sdev->dev, | |
336 | "error: failed to reset controller during suspend\n"); | |
337 | return ret; | |
338 | } | |
339 | ||
340 | return 0; | |
341 | } | |
342 | ||
343 | static int hda_resume(struct snd_sof_dev *sdev) | |
344 | { | |
345 | #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA) | |
346 | struct hdac_bus *bus = sof_to_bus(sdev); | |
347 | struct hdac_ext_link *hlink = NULL; | |
348 | #endif | |
349 | int ret; | |
350 | ||
351 | /* | |
352 | * clear TCSEL to clear playback on some HD Audio | |
353 | * codecs. PCI TCSEL is defined in the Intel manuals. | |
354 | */ | |
355 | snd_sof_pci_update_bits(sdev, PCI_TCSEL, 0x07, 0); | |
356 | ||
357 | #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA) | |
358 | /* reset and start hda controller */ | |
359 | ret = hda_dsp_ctrl_init_chip(sdev, true); | |
360 | if (ret < 0) { | |
361 | dev_err(sdev->dev, | |
362 | "error: failed to start controller after resume\n"); | |
363 | return ret; | |
364 | } | |
365 | ||
366 | hda_dsp_ctrl_misc_clock_gating(sdev, false); | |
367 | ||
368 | /* Reset stream-to-link mapping */ | |
369 | list_for_each_entry(hlink, &bus->hlink_list, list) | |
370 | bus->io_ops->reg_writel(0, hlink->ml_addr + AZX_REG_ML_LOSIDV); | |
371 | ||
372 | hda_dsp_ctrl_misc_clock_gating(sdev, true); | |
373 | ||
374 | /* enable ppcap interrupt */ | |
375 | snd_hdac_ext_bus_ppcap_enable(bus, true); | |
376 | snd_hdac_ext_bus_ppcap_int_enable(bus, true); | |
377 | #else | |
378 | ||
379 | hda_dsp_ctrl_misc_clock_gating(sdev, false); | |
380 | ||
381 | /* reset controller */ | |
382 | ret = hda_dsp_ctrl_link_reset(sdev, true); | |
383 | if (ret < 0) { | |
384 | dev_err(sdev->dev, | |
385 | "error: failed to reset controller during resume\n"); | |
386 | return ret; | |
387 | } | |
388 | ||
389 | /* take controller out of reset */ | |
390 | ret = hda_dsp_ctrl_link_reset(sdev, false); | |
391 | if (ret < 0) { | |
392 | dev_err(sdev->dev, | |
393 | "error: failed to ready controller during resume\n"); | |
394 | return ret; | |
395 | } | |
396 | ||
397 | /* enable hda bus irq */ | |
398 | snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, SOF_HDA_INTCTL, | |
399 | SOF_HDA_INT_CTRL_EN | SOF_HDA_INT_GLOBAL_EN, | |
400 | SOF_HDA_INT_CTRL_EN | SOF_HDA_INT_GLOBAL_EN); | |
401 | ||
402 | hda_dsp_ctrl_misc_clock_gating(sdev, true); | |
403 | ||
404 | /* enable ppcap interrupt */ | |
405 | hda_dsp_ctrl_ppcap_enable(sdev, true); | |
406 | hda_dsp_ctrl_ppcap_int_enable(sdev, true); | |
407 | #endif | |
408 | ||
409 | #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA) | |
410 | /* turn off the links that were off before suspend */ | |
411 | list_for_each_entry(hlink, &bus->hlink_list, list) { | |
412 | if (!hlink->ref_count) | |
413 | snd_hdac_ext_bus_link_power_down(hlink); | |
414 | } | |
415 | ||
416 | /* check dma status and clean up CORB/RIRB buffers */ | |
417 | if (!bus->cmd_dma_state) | |
418 | snd_hdac_bus_stop_cmd_io(bus); | |
419 | #endif | |
420 | ||
421 | return 0; | |
422 | } | |
423 | ||
424 | int hda_dsp_resume(struct snd_sof_dev *sdev) | |
425 | { | |
426 | /* init hda controller. DSP cores will be powered up during fw boot */ | |
427 | return hda_resume(sdev); | |
428 | } | |
429 | ||
430 | int hda_dsp_runtime_resume(struct snd_sof_dev *sdev) | |
431 | { | |
432 | /* init hda controller. DSP cores will be powered up during fw boot */ | |
433 | return hda_resume(sdev); | |
434 | } | |
435 | ||
436 | int hda_dsp_runtime_suspend(struct snd_sof_dev *sdev, int state) | |
437 | { | |
438 | /* stop hda controller and power dsp off */ | |
439 | return hda_suspend(sdev, state); | |
440 | } | |
441 | ||
442 | int hda_dsp_suspend(struct snd_sof_dev *sdev, int state) | |
443 | { | |
444 | struct hdac_bus *bus = sof_to_bus(sdev); | |
445 | int ret; | |
446 | ||
447 | /* stop hda controller and power dsp off */ | |
448 | ret = hda_suspend(sdev, state); | |
449 | if (ret < 0) { | |
450 | dev_err(bus->dev, "error: suspending dsp\n"); | |
451 | return ret; | |
452 | } | |
453 | ||
454 | return 0; | |
455 | } | |
ed3baacd RS |
456 | |
457 | void hda_dsp_set_hw_params_upon_resume(struct snd_sof_dev *sdev) | |
458 | { | |
459 | struct hdac_bus *bus = sof_to_bus(sdev); | |
460 | struct sof_intel_hda_stream *hda_stream; | |
461 | struct hdac_ext_stream *stream; | |
462 | struct hdac_stream *s; | |
463 | ||
464 | /* set internal flag for BE */ | |
465 | list_for_each_entry(s, &bus->stream_list, list) { | |
466 | stream = stream_to_hdac_ext_stream(s); | |
467 | hda_stream = container_of(stream, struct sof_intel_hda_stream, | |
468 | hda_stream); | |
469 | hda_stream->hw_params_upon_resume = 1; | |
470 | } | |
471 | } |