Commit | Line | Data |
---|---|---|
e149ca29 | 1 | // SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) |
8a300c8f LG |
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 | ||
4f6250b8 | 18 | #include <linux/module.h> |
8a300c8f LG |
19 | #include <sound/hdaudio_ext.h> |
20 | #include <sound/hda_register.h> | |
7e26df0c | 21 | #include <sound/hda_component.h> |
8a300c8f LG |
22 | #include "../ops.h" |
23 | #include "hda.h" | |
24 | ||
4f6250b8 KV |
25 | #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA) |
26 | static int hda_codec_mask = -1; | |
27 | module_param_named(codec_mask, hda_codec_mask, int, 0444); | |
28 | MODULE_PARM_DESC(codec_mask, "SOF HDA codec mask for probing"); | |
29 | #endif | |
30 | ||
8a300c8f LG |
31 | /* |
32 | * HDA Operations. | |
33 | */ | |
34 | ||
35 | int hda_dsp_ctrl_link_reset(struct snd_sof_dev *sdev, bool reset) | |
36 | { | |
37 | unsigned long timeout; | |
38 | u32 gctl = 0; | |
39 | u32 val; | |
40 | ||
41 | /* 0 to enter reset and 1 to exit reset */ | |
42 | val = reset ? 0 : SOF_HDA_GCTL_RESET; | |
43 | ||
44 | /* enter/exit HDA controller reset */ | |
45 | snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, SOF_HDA_GCTL, | |
46 | SOF_HDA_GCTL_RESET, val); | |
47 | ||
48 | /* wait to enter/exit reset */ | |
49 | timeout = jiffies + msecs_to_jiffies(HDA_DSP_CTRL_RESET_TIMEOUT); | |
50 | while (time_before(jiffies, timeout)) { | |
51 | gctl = snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, SOF_HDA_GCTL); | |
52 | if ((gctl & SOF_HDA_GCTL_RESET) == val) | |
53 | return 0; | |
54 | usleep_range(500, 1000); | |
55 | } | |
56 | ||
57 | /* enter/exit reset failed */ | |
58 | dev_err(sdev->dev, "error: failed to %s HDA controller gctl 0x%x\n", | |
59 | reset ? "reset" : "ready", gctl); | |
60 | return -EIO; | |
61 | } | |
62 | ||
63 | int hda_dsp_ctrl_get_caps(struct snd_sof_dev *sdev) | |
64 | { | |
65 | struct hdac_bus *bus = sof_to_bus(sdev); | |
66 | u32 cap, offset, feature; | |
67 | int count = 0; | |
f09e9c7f PLB |
68 | int ret; |
69 | ||
70 | /* | |
71 | * On some devices, one reset cycle is necessary before reading | |
72 | * capabilities | |
73 | */ | |
74 | ret = hda_dsp_ctrl_link_reset(sdev, true); | |
75 | if (ret < 0) | |
76 | return ret; | |
77 | ret = hda_dsp_ctrl_link_reset(sdev, false); | |
78 | if (ret < 0) | |
79 | return ret; | |
8a300c8f LG |
80 | |
81 | offset = snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, SOF_HDA_LLCH); | |
82 | ||
83 | do { | |
8a300c8f LG |
84 | dev_dbg(sdev->dev, "checking for capabilities at offset 0x%x\n", |
85 | offset & SOF_HDA_CAP_NEXT_MASK); | |
86 | ||
f09e9c7f PLB |
87 | cap = snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, offset); |
88 | ||
89 | if (cap == -1) { | |
90 | dev_dbg(bus->dev, "Invalid capability reg read\n"); | |
91 | break; | |
92 | } | |
93 | ||
8a300c8f LG |
94 | feature = (cap & SOF_HDA_CAP_ID_MASK) >> SOF_HDA_CAP_ID_OFF; |
95 | ||
96 | switch (feature) { | |
97 | case SOF_HDA_PP_CAP_ID: | |
98 | dev_dbg(sdev->dev, "found DSP capability at 0x%x\n", | |
99 | offset); | |
100 | bus->ppcap = bus->remap_addr + offset; | |
101 | sdev->bar[HDA_DSP_PP_BAR] = bus->ppcap; | |
102 | break; | |
103 | case SOF_HDA_SPIB_CAP_ID: | |
104 | dev_dbg(sdev->dev, "found SPIB capability at 0x%x\n", | |
105 | offset); | |
106 | bus->spbcap = bus->remap_addr + offset; | |
107 | sdev->bar[HDA_DSP_SPIB_BAR] = bus->spbcap; | |
108 | break; | |
109 | case SOF_HDA_DRSM_CAP_ID: | |
110 | dev_dbg(sdev->dev, "found DRSM capability at 0x%x\n", | |
111 | offset); | |
112 | bus->drsmcap = bus->remap_addr + offset; | |
113 | sdev->bar[HDA_DSP_DRSM_BAR] = bus->drsmcap; | |
114 | break; | |
115 | case SOF_HDA_GTS_CAP_ID: | |
116 | dev_dbg(sdev->dev, "found GTS capability at 0x%x\n", | |
117 | offset); | |
118 | bus->gtscap = bus->remap_addr + offset; | |
119 | break; | |
120 | case SOF_HDA_ML_CAP_ID: | |
121 | dev_dbg(sdev->dev, "found ML capability at 0x%x\n", | |
122 | offset); | |
123 | bus->mlcap = bus->remap_addr + offset; | |
124 | break; | |
125 | default: | |
f09e9c7f PLB |
126 | dev_dbg(sdev->dev, "found capability %d at 0x%x\n", |
127 | feature, offset); | |
8a300c8f LG |
128 | break; |
129 | } | |
130 | ||
131 | offset = cap & SOF_HDA_CAP_NEXT_MASK; | |
132 | } while (count++ <= SOF_HDA_MAX_CAPS && offset); | |
133 | ||
134 | return 0; | |
135 | } | |
136 | ||
137 | void hda_dsp_ctrl_ppcap_enable(struct snd_sof_dev *sdev, bool enable) | |
138 | { | |
139 | u32 val = enable ? SOF_HDA_PPCTL_GPROCEN : 0; | |
140 | ||
141 | snd_sof_dsp_update_bits(sdev, HDA_DSP_PP_BAR, SOF_HDA_REG_PP_PPCTL, | |
142 | SOF_HDA_PPCTL_GPROCEN, val); | |
143 | } | |
144 | ||
145 | void hda_dsp_ctrl_ppcap_int_enable(struct snd_sof_dev *sdev, bool enable) | |
146 | { | |
147 | u32 val = enable ? SOF_HDA_PPCTL_PIE : 0; | |
148 | ||
149 | snd_sof_dsp_update_bits(sdev, HDA_DSP_PP_BAR, SOF_HDA_REG_PP_PPCTL, | |
150 | SOF_HDA_PPCTL_PIE, val); | |
151 | } | |
152 | ||
153 | void hda_dsp_ctrl_misc_clock_gating(struct snd_sof_dev *sdev, bool enable) | |
154 | { | |
155 | u32 val = enable ? PCI_CGCTL_MISCBDCGE_MASK : 0; | |
156 | ||
157 | snd_sof_pci_update_bits(sdev, PCI_CGCTL, PCI_CGCTL_MISCBDCGE_MASK, val); | |
158 | } | |
159 | ||
160 | /* | |
161 | * enable/disable audio dsp clock gating and power gating bits. | |
162 | * This allows the HW to opportunistically power and clock gate | |
163 | * the audio dsp when it is idle | |
164 | */ | |
165 | int hda_dsp_ctrl_clock_power_gating(struct snd_sof_dev *sdev, bool enable) | |
166 | { | |
8a300c8f LG |
167 | u32 val; |
168 | ||
169 | /* enable/disable audio dsp clock gating */ | |
170 | val = enable ? PCI_CGCTL_ADSPDCGE : 0; | |
171 | snd_sof_pci_update_bits(sdev, PCI_CGCTL, PCI_CGCTL_ADSPDCGE, val); | |
172 | ||
43b2ab90 RS |
173 | /* enable/disable DMI Link L1 support */ |
174 | val = enable ? HDA_VS_INTEL_EM2_L1SEN : 0; | |
175 | snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, HDA_VS_INTEL_EM2, | |
176 | HDA_VS_INTEL_EM2_L1SEN, val); | |
8a300c8f LG |
177 | |
178 | /* enable/disable audio dsp power gating */ | |
179 | val = enable ? 0 : PCI_PGCTL_ADSPPGD; | |
180 | snd_sof_pci_update_bits(sdev, PCI_PGCTL, PCI_PGCTL_ADSPPGD, val); | |
181 | ||
182 | return 0; | |
183 | } | |
184 | ||
b48b77d8 | 185 | int hda_dsp_ctrl_init_chip(struct snd_sof_dev *sdev) |
8a300c8f LG |
186 | { |
187 | struct hdac_bus *bus = sof_to_bus(sdev); | |
be1b577d ZY |
188 | struct hdac_stream *stream; |
189 | int sd_offset, ret = 0; | |
190 | ||
191 | if (bus->chip_init) | |
192 | return 0; | |
8a300c8f | 193 | |
7e26df0c KV |
194 | #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA) |
195 | snd_hdac_set_codec_wakeup(bus, true); | |
196 | #endif | |
8a300c8f | 197 | hda_dsp_ctrl_misc_clock_gating(sdev, false); |
be1b577d | 198 | |
b48b77d8 PLB |
199 | /* reset HDA controller */ |
200 | ret = hda_dsp_ctrl_link_reset(sdev, true); | |
201 | if (ret < 0) { | |
202 | dev_err(sdev->dev, "error: failed to reset HDA controller\n"); | |
203 | goto err; | |
204 | } | |
be1b577d | 205 | |
b48b77d8 PLB |
206 | /* exit HDA controller reset */ |
207 | ret = hda_dsp_ctrl_link_reset(sdev, false); | |
208 | if (ret < 0) { | |
209 | dev_err(sdev->dev, "error: failed to exit HDA controller reset\n"); | |
210 | goto err; | |
be1b577d ZY |
211 | } |
212 | ||
213 | #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA) | |
be1b577d ZY |
214 | /* Accept unsolicited responses */ |
215 | snd_hdac_chip_updatel(bus, GCTL, AZX_GCTL_UNSOL, AZX_GCTL_UNSOL); | |
216 | ||
217 | /* detect codecs */ | |
218 | if (!bus->codec_mask) { | |
219 | bus->codec_mask = snd_hdac_chip_readw(bus, STATESTS); | |
220 | dev_dbg(bus->dev, "codec_mask = 0x%lx\n", bus->codec_mask); | |
221 | } | |
4f6250b8 KV |
222 | |
223 | if (hda_codec_mask != -1) { | |
224 | bus->codec_mask &= hda_codec_mask; | |
225 | dev_dbg(bus->dev, "filtered codec_mask = 0x%lx\n", | |
226 | bus->codec_mask); | |
227 | } | |
be1b577d ZY |
228 | #endif |
229 | ||
230 | /* clear stream status */ | |
231 | list_for_each_entry(stream, &bus->stream_list, list) { | |
232 | sd_offset = SOF_STREAM_SD_OFFSET(stream); | |
3a39e0ea | 233 | snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, |
38bf0780 | 234 | sd_offset + SOF_HDA_ADSP_REG_SD_STS, |
3a39e0ea | 235 | SOF_HDA_CL_DMA_SD_INT_MASK); |
be1b577d ZY |
236 | } |
237 | ||
238 | /* clear WAKESTS */ | |
3a39e0ea RS |
239 | snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, SOF_HDA_WAKESTS, |
240 | SOF_HDA_WAKESTS_INT_MASK); | |
be1b577d ZY |
241 | |
242 | #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA) | |
243 | /* clear rirb status */ | |
244 | snd_hdac_chip_writeb(bus, RIRBSTS, RIRB_INT_MASK); | |
245 | #endif | |
246 | ||
247 | /* clear interrupt status register */ | |
248 | snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, SOF_HDA_INTSTS, | |
249 | SOF_HDA_INT_CTRL_EN | SOF_HDA_INT_ALL_STREAM); | |
250 | ||
251 | #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA) | |
252 | /* initialize the codec command I/O */ | |
253 | snd_hdac_bus_init_cmd_io(bus); | |
254 | #endif | |
255 | ||
256 | /* enable CIE and GIE interrupts */ | |
257 | snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, SOF_HDA_INTCTL, | |
258 | SOF_HDA_INT_CTRL_EN | SOF_HDA_INT_GLOBAL_EN, | |
259 | SOF_HDA_INT_CTRL_EN | SOF_HDA_INT_GLOBAL_EN); | |
260 | ||
be1b577d ZY |
261 | /* program the position buffer */ |
262 | if (bus->use_posbuf && bus->posbuf.addr) { | |
061edb23 ZY |
263 | snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, SOF_HDA_ADSP_DPLBASE, |
264 | (u32)bus->posbuf.addr); | |
265 | snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, SOF_HDA_ADSP_DPUBASE, | |
266 | upper_32_bits(bus->posbuf.addr)); | |
be1b577d | 267 | } |
be1b577d | 268 | |
1a7d06ae | 269 | hda_bus_ml_reset_losidv(bus); |
d0697351 | 270 | |
be1b577d ZY |
271 | bus->chip_init = true; |
272 | ||
aae5a6e9 | 273 | err: |
8a300c8f | 274 | hda_dsp_ctrl_misc_clock_gating(sdev, true); |
7e26df0c KV |
275 | #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA) |
276 | snd_hdac_set_codec_wakeup(bus, false); | |
277 | #endif | |
8a300c8f LG |
278 | |
279 | return ret; | |
280 | } | |
13063a2c ZY |
281 | |
282 | void hda_dsp_ctrl_stop_chip(struct snd_sof_dev *sdev) | |
283 | { | |
284 | struct hdac_bus *bus = sof_to_bus(sdev); | |
285 | struct hdac_stream *stream; | |
286 | int sd_offset; | |
287 | ||
288 | if (!bus->chip_init) | |
289 | return; | |
290 | ||
291 | /* disable interrupts in stream descriptor */ | |
292 | list_for_each_entry(stream, &bus->stream_list, list) { | |
293 | sd_offset = SOF_STREAM_SD_OFFSET(stream); | |
294 | snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, | |
295 | sd_offset + | |
38bf0780 | 296 | SOF_HDA_ADSP_REG_SD_CTL, |
13063a2c ZY |
297 | SOF_HDA_CL_DMA_SD_INT_MASK, |
298 | 0); | |
299 | } | |
300 | ||
301 | /* disable SIE for all streams */ | |
302 | snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, SOF_HDA_INTCTL, | |
303 | SOF_HDA_INT_ALL_STREAM, 0); | |
304 | ||
305 | /* disable controller CIE and GIE */ | |
306 | snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, SOF_HDA_INTCTL, | |
307 | SOF_HDA_INT_CTRL_EN | SOF_HDA_INT_GLOBAL_EN, | |
308 | 0); | |
309 | ||
310 | /* clear stream status */ | |
311 | list_for_each_entry(stream, &bus->stream_list, list) { | |
312 | sd_offset = SOF_STREAM_SD_OFFSET(stream); | |
3a39e0ea | 313 | snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, |
38bf0780 | 314 | sd_offset + SOF_HDA_ADSP_REG_SD_STS, |
3a39e0ea | 315 | SOF_HDA_CL_DMA_SD_INT_MASK); |
13063a2c ZY |
316 | } |
317 | ||
318 | /* clear WAKESTS */ | |
3a39e0ea RS |
319 | snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, SOF_HDA_WAKESTS, |
320 | SOF_HDA_WAKESTS_INT_MASK); | |
13063a2c ZY |
321 | |
322 | #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA) | |
323 | /* clear rirb status */ | |
324 | snd_hdac_chip_writeb(bus, RIRBSTS, RIRB_INT_MASK); | |
325 | #endif | |
326 | ||
327 | /* clear interrupt status register */ | |
328 | snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, SOF_HDA_INTSTS, | |
329 | SOF_HDA_INT_CTRL_EN | SOF_HDA_INT_ALL_STREAM); | |
330 | ||
331 | #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA) | |
332 | /* disable CORB/RIRB */ | |
333 | snd_hdac_bus_stop_cmd_io(bus); | |
334 | #endif | |
335 | /* disable position buffer */ | |
12ce2138 | 336 | if (bus->use_posbuf && bus->posbuf.addr) { |
13063a2c ZY |
337 | snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, |
338 | SOF_HDA_ADSP_DPLBASE, 0); | |
339 | snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, | |
340 | SOF_HDA_ADSP_DPUBASE, 0); | |
341 | } | |
342 | ||
343 | bus->chip_init = false; | |
344 | } |