Commit | Line | Data |
---|---|---|
e973e31a SP |
1 | /* |
2 | * skl-sst-dsp.c - SKL SST library generic function | |
3 | * | |
4 | * Copyright (C) 2014-15, Intel Corporation. | |
5 | * Author:Rafal Redzimski <rafal.f.redzimski@intel.com> | |
6 | * Jeeja KP <jeeja.kp@intel.com> | |
7 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
8 | * | |
9 | * This program is free software; you can redistribute it and/or modify | |
10 | * it under the terms of the GNU General Public License as version 2, as | |
11 | * published by the Free Software Foundation. | |
12 | * | |
13 | * This program is distributed in the hope that it will be useful, but | |
14 | * WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
16 | * General Public License for more details. | |
17 | */ | |
18 | #include <sound/pcm.h> | |
19 | ||
20 | #include "../common/sst-dsp.h" | |
21 | #include "../common/sst-ipc.h" | |
22 | #include "../common/sst-dsp-priv.h" | |
23 | #include "skl-sst-ipc.h" | |
24 | ||
25 | /* various timeout values */ | |
26 | #define SKL_DSP_PU_TO 50 | |
27 | #define SKL_DSP_PD_TO 50 | |
28 | #define SKL_DSP_RESET_TO 50 | |
29 | ||
30 | void skl_dsp_set_state_locked(struct sst_dsp *ctx, int state) | |
31 | { | |
32 | mutex_lock(&ctx->mutex); | |
33 | ctx->sst_state = state; | |
34 | mutex_unlock(&ctx->mutex); | |
35 | } | |
36 | ||
a360b623 | 37 | static int skl_dsp_core_set_reset_state(struct sst_dsp *ctx) |
e973e31a SP |
38 | { |
39 | int ret; | |
40 | ||
41 | /* update bits */ | |
42 | sst_dsp_shim_update_bits_unlocked(ctx, | |
43 | SKL_ADSP_REG_ADSPCS, SKL_ADSPCS_CRST_MASK, | |
44 | SKL_ADSPCS_CRST(SKL_DSP_CORES_MASK)); | |
45 | ||
46 | /* poll with timeout to check if operation successful */ | |
47 | ret = sst_dsp_register_poll(ctx, | |
48 | SKL_ADSP_REG_ADSPCS, | |
49 | SKL_ADSPCS_CRST_MASK, | |
50 | SKL_ADSPCS_CRST(SKL_DSP_CORES_MASK), | |
51 | SKL_DSP_RESET_TO, | |
52 | "Set reset"); | |
53 | if ((sst_dsp_shim_read_unlocked(ctx, SKL_ADSP_REG_ADSPCS) & | |
54 | SKL_ADSPCS_CRST(SKL_DSP_CORES_MASK)) != | |
55 | SKL_ADSPCS_CRST(SKL_DSP_CORES_MASK)) { | |
56 | dev_err(ctx->dev, "Set reset state failed\n"); | |
57 | ret = -EIO; | |
58 | } | |
59 | ||
60 | return ret; | |
61 | } | |
62 | ||
a360b623 | 63 | static int skl_dsp_core_unset_reset_state(struct sst_dsp *ctx) |
e973e31a SP |
64 | { |
65 | int ret; | |
66 | ||
67 | dev_dbg(ctx->dev, "In %s\n", __func__); | |
68 | ||
69 | /* update bits */ | |
70 | sst_dsp_shim_update_bits_unlocked(ctx, SKL_ADSP_REG_ADSPCS, | |
71 | SKL_ADSPCS_CRST_MASK, 0); | |
72 | ||
73 | /* poll with timeout to check if operation successful */ | |
74 | ret = sst_dsp_register_poll(ctx, | |
75 | SKL_ADSP_REG_ADSPCS, | |
76 | SKL_ADSPCS_CRST_MASK, | |
77 | 0, | |
78 | SKL_DSP_RESET_TO, | |
79 | "Unset reset"); | |
80 | ||
81 | if ((sst_dsp_shim_read_unlocked(ctx, SKL_ADSP_REG_ADSPCS) & | |
82 | SKL_ADSPCS_CRST(SKL_DSP_CORES_MASK)) != 0) { | |
83 | dev_err(ctx->dev, "Unset reset state failed\n"); | |
84 | ret = -EIO; | |
85 | } | |
86 | ||
87 | return ret; | |
88 | } | |
89 | ||
a360b623 | 90 | static bool is_skl_dsp_core_enable(struct sst_dsp *ctx) |
e973e31a SP |
91 | { |
92 | int val; | |
93 | bool is_enable; | |
94 | ||
95 | val = sst_dsp_shim_read_unlocked(ctx, SKL_ADSP_REG_ADSPCS); | |
96 | ||
97 | is_enable = ((val & SKL_ADSPCS_CPA(SKL_DSP_CORES_MASK)) && | |
98 | (val & SKL_ADSPCS_SPA(SKL_DSP_CORES_MASK)) && | |
99 | !(val & SKL_ADSPCS_CRST(SKL_DSP_CORES_MASK)) && | |
100 | !(val & SKL_ADSPCS_CSTALL(SKL_DSP_CORES_MASK))); | |
101 | ||
102 | dev_dbg(ctx->dev, "DSP core is enabled=%d\n", is_enable); | |
103 | return is_enable; | |
104 | } | |
105 | ||
106 | static int skl_dsp_reset_core(struct sst_dsp *ctx) | |
107 | { | |
108 | /* stall core */ | |
109 | sst_dsp_shim_write_unlocked(ctx, SKL_ADSP_REG_ADSPCS, | |
110 | sst_dsp_shim_read_unlocked(ctx, SKL_ADSP_REG_ADSPCS) & | |
111 | SKL_ADSPCS_CSTALL(SKL_DSP_CORES_MASK)); | |
112 | ||
113 | /* set reset state */ | |
114 | return skl_dsp_core_set_reset_state(ctx); | |
115 | } | |
116 | ||
117 | static int skl_dsp_start_core(struct sst_dsp *ctx) | |
118 | { | |
119 | int ret; | |
120 | ||
121 | /* unset reset state */ | |
122 | ret = skl_dsp_core_unset_reset_state(ctx); | |
123 | if (ret < 0) { | |
124 | dev_dbg(ctx->dev, "dsp unset reset fails\n"); | |
125 | return ret; | |
126 | } | |
127 | ||
128 | /* run core */ | |
129 | dev_dbg(ctx->dev, "run core...\n"); | |
130 | sst_dsp_shim_write_unlocked(ctx, SKL_ADSP_REG_ADSPCS, | |
131 | sst_dsp_shim_read_unlocked(ctx, SKL_ADSP_REG_ADSPCS) & | |
132 | ~SKL_ADSPCS_CSTALL(SKL_DSP_CORES_MASK)); | |
133 | ||
134 | if (!is_skl_dsp_core_enable(ctx)) { | |
135 | skl_dsp_reset_core(ctx); | |
136 | dev_err(ctx->dev, "DSP core enable failed\n"); | |
137 | ret = -EIO; | |
138 | } | |
139 | ||
140 | return ret; | |
141 | } | |
142 | ||
a360b623 | 143 | static int skl_dsp_core_power_up(struct sst_dsp *ctx) |
e973e31a SP |
144 | { |
145 | int ret; | |
146 | ||
147 | /* update bits */ | |
148 | sst_dsp_shim_update_bits_unlocked(ctx, SKL_ADSP_REG_ADSPCS, | |
149 | SKL_ADSPCS_SPA_MASK, SKL_ADSPCS_SPA(SKL_DSP_CORES_MASK)); | |
150 | ||
151 | /* poll with timeout to check if operation successful */ | |
152 | ret = sst_dsp_register_poll(ctx, | |
153 | SKL_ADSP_REG_ADSPCS, | |
154 | SKL_ADSPCS_CPA_MASK, | |
155 | SKL_ADSPCS_CPA(SKL_DSP_CORES_MASK), | |
156 | SKL_DSP_PU_TO, | |
157 | "Power up"); | |
158 | ||
159 | if ((sst_dsp_shim_read_unlocked(ctx, SKL_ADSP_REG_ADSPCS) & | |
160 | SKL_ADSPCS_CPA(SKL_DSP_CORES_MASK)) != | |
161 | SKL_ADSPCS_CPA(SKL_DSP_CORES_MASK)) { | |
162 | dev_err(ctx->dev, "DSP core power up failed\n"); | |
163 | ret = -EIO; | |
164 | } | |
165 | ||
166 | return ret; | |
167 | } | |
168 | ||
a360b623 | 169 | static int skl_dsp_core_power_down(struct sst_dsp *ctx) |
e973e31a SP |
170 | { |
171 | /* update bits */ | |
172 | sst_dsp_shim_update_bits_unlocked(ctx, SKL_ADSP_REG_ADSPCS, | |
173 | SKL_ADSPCS_SPA_MASK, 0); | |
174 | ||
175 | /* poll with timeout to check if operation successful */ | |
176 | return sst_dsp_register_poll(ctx, | |
177 | SKL_ADSP_REG_ADSPCS, | |
6ea8ba33 | 178 | SKL_ADSPCS_CPA_MASK, |
e973e31a SP |
179 | 0, |
180 | SKL_DSP_PD_TO, | |
181 | "Power down"); | |
182 | } | |
183 | ||
130e69a4 | 184 | int skl_dsp_enable_core(struct sst_dsp *ctx) |
e973e31a SP |
185 | { |
186 | int ret; | |
187 | ||
188 | /* power up */ | |
189 | ret = skl_dsp_core_power_up(ctx); | |
190 | if (ret < 0) { | |
191 | dev_dbg(ctx->dev, "dsp core power up failed\n"); | |
192 | return ret; | |
193 | } | |
194 | ||
195 | return skl_dsp_start_core(ctx); | |
196 | } | |
197 | ||
a360b623 | 198 | int skl_dsp_disable_core(struct sst_dsp *ctx) |
e973e31a SP |
199 | { |
200 | int ret; | |
201 | ||
202 | ret = skl_dsp_reset_core(ctx); | |
203 | if (ret < 0) { | |
204 | dev_err(ctx->dev, "dsp core reset failed\n"); | |
205 | return ret; | |
206 | } | |
207 | ||
208 | /* power down core*/ | |
209 | ret = skl_dsp_core_power_down(ctx); | |
210 | if (ret < 0) { | |
211 | dev_err(ctx->dev, "dsp core power down failed\n"); | |
212 | return ret; | |
213 | } | |
214 | ||
215 | if (is_skl_dsp_core_enable(ctx)) { | |
216 | dev_err(ctx->dev, "DSP core disable failed\n"); | |
217 | ret = -EIO; | |
218 | } | |
219 | ||
220 | return ret; | |
221 | } | |
222 | ||
223 | int skl_dsp_boot(struct sst_dsp *ctx) | |
224 | { | |
225 | int ret; | |
226 | ||
227 | if (is_skl_dsp_core_enable(ctx)) { | |
228 | dev_dbg(ctx->dev, "dsp core is already enabled, so reset the dap core\n"); | |
229 | ret = skl_dsp_reset_core(ctx); | |
230 | if (ret < 0) { | |
231 | dev_err(ctx->dev, "dsp reset failed\n"); | |
232 | return ret; | |
233 | } | |
234 | ||
235 | ret = skl_dsp_start_core(ctx); | |
236 | if (ret < 0) { | |
237 | dev_err(ctx->dev, "dsp start failed\n"); | |
238 | return ret; | |
239 | } | |
240 | } else { | |
241 | dev_dbg(ctx->dev, "disable and enable to make sure DSP is invalid state\n"); | |
242 | ret = skl_dsp_disable_core(ctx); | |
243 | ||
244 | if (ret < 0) { | |
245 | dev_err(ctx->dev, "dsp disable core failes\n"); | |
246 | return ret; | |
247 | } | |
248 | ret = skl_dsp_enable_core(ctx); | |
249 | } | |
250 | ||
251 | return ret; | |
252 | } | |
253 | ||
254 | irqreturn_t skl_dsp_sst_interrupt(int irq, void *dev_id) | |
255 | { | |
256 | struct sst_dsp *ctx = dev_id; | |
257 | u32 val; | |
258 | irqreturn_t result = IRQ_NONE; | |
259 | ||
260 | spin_lock(&ctx->spinlock); | |
261 | ||
262 | val = sst_dsp_shim_read_unlocked(ctx, SKL_ADSP_REG_ADSPIS); | |
263 | ctx->intr_status = val; | |
264 | ||
def656fe JK |
265 | if (val == 0xffffffff) { |
266 | spin_unlock(&ctx->spinlock); | |
267 | return IRQ_NONE; | |
268 | } | |
269 | ||
e973e31a SP |
270 | if (val & SKL_ADSPIS_IPC) { |
271 | skl_ipc_int_disable(ctx); | |
272 | result = IRQ_WAKE_THREAD; | |
273 | } | |
274 | ||
6cb00333 SP |
275 | if (val & SKL_ADSPIS_CL_DMA) { |
276 | skl_cldma_int_disable(ctx); | |
277 | result = IRQ_WAKE_THREAD; | |
278 | } | |
279 | ||
e973e31a SP |
280 | spin_unlock(&ctx->spinlock); |
281 | ||
282 | return result; | |
283 | } | |
284 | ||
285 | int skl_dsp_wake(struct sst_dsp *ctx) | |
286 | { | |
287 | return ctx->fw_ops.set_state_D0(ctx); | |
288 | } | |
289 | EXPORT_SYMBOL_GPL(skl_dsp_wake); | |
290 | ||
291 | int skl_dsp_sleep(struct sst_dsp *ctx) | |
292 | { | |
293 | return ctx->fw_ops.set_state_D3(ctx); | |
294 | } | |
295 | EXPORT_SYMBOL_GPL(skl_dsp_sleep); | |
296 | ||
297 | struct sst_dsp *skl_dsp_ctx_init(struct device *dev, | |
298 | struct sst_dsp_device *sst_dev, int irq) | |
299 | { | |
300 | int ret; | |
301 | struct sst_dsp *sst; | |
302 | ||
303 | sst = devm_kzalloc(dev, sizeof(*sst), GFP_KERNEL); | |
304 | if (sst == NULL) | |
305 | return NULL; | |
306 | ||
307 | spin_lock_init(&sst->spinlock); | |
308 | mutex_init(&sst->mutex); | |
309 | sst->dev = dev; | |
310 | sst->sst_dev = sst_dev; | |
311 | sst->irq = irq; | |
312 | sst->ops = sst_dev->ops; | |
313 | sst->thread_context = sst_dev->thread_context; | |
314 | ||
315 | /* Initialise SST Audio DSP */ | |
316 | if (sst->ops->init) { | |
317 | ret = sst->ops->init(sst, NULL); | |
318 | if (ret < 0) | |
319 | return NULL; | |
320 | } | |
321 | ||
322 | /* Register the ISR */ | |
323 | ret = request_threaded_irq(sst->irq, sst->ops->irq_handler, | |
324 | sst_dev->thread, IRQF_SHARED, "AudioDSP", sst); | |
325 | if (ret) { | |
326 | dev_err(sst->dev, "unable to grab threaded IRQ %d, disabling device\n", | |
327 | sst->irq); | |
328 | return NULL; | |
329 | } | |
330 | ||
331 | return sst; | |
332 | } | |
333 | ||
334 | void skl_dsp_free(struct sst_dsp *dsp) | |
335 | { | |
336 | skl_ipc_int_disable(dsp); | |
337 | ||
338 | free_irq(dsp->irq, dsp); | |
3f7f8489 VK |
339 | skl_ipc_op_int_disable(dsp); |
340 | skl_ipc_int_disable(dsp); | |
341 | ||
e973e31a SP |
342 | skl_dsp_disable_core(dsp); |
343 | } | |
344 | EXPORT_SYMBOL_GPL(skl_dsp_free); | |
345 | ||
346 | bool is_skl_dsp_running(struct sst_dsp *ctx) | |
347 | { | |
348 | return (ctx->sst_state == SKL_DSP_RUNNING); | |
349 | } | |
350 | EXPORT_SYMBOL_GPL(is_skl_dsp_running); |