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