Commit | Line | Data |
---|---|---|
a750ba5f SP |
1 | /* |
2 | * skl-sst.c - HDA DSP library functions for SKL platform | |
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 | ||
19 | #include <linux/module.h> | |
20 | #include <linux/delay.h> | |
21 | #include <linux/device.h> | |
53afce2c | 22 | #include <linux/err.h> |
09305da9 | 23 | #include <linux/uuid.h> |
a750ba5f SP |
24 | #include "../common/sst-dsp.h" |
25 | #include "../common/sst-dsp-priv.h" | |
26 | #include "../common/sst-ipc.h" | |
27 | #include "skl-sst-ipc.h" | |
28 | ||
29 | #define SKL_BASEFW_TIMEOUT 300 | |
30 | #define SKL_INIT_TIMEOUT 1000 | |
31 | ||
32 | /* Intel HD Audio SRAM Window 0*/ | |
33 | #define SKL_ADSP_SRAM0_BASE 0x8000 | |
34 | ||
35 | /* Firmware status window */ | |
36 | #define SKL_ADSP_FW_STATUS SKL_ADSP_SRAM0_BASE | |
37 | #define SKL_ADSP_ERROR_CODE (SKL_ADSP_FW_STATUS + 0x4) | |
38 | ||
6c5768b3 D |
39 | #define SKL_NUM_MODULES 1 |
40 | ||
a750ba5f SP |
41 | static bool skl_check_fw_status(struct sst_dsp *ctx, u32 status) |
42 | { | |
43 | u32 cur_sts; | |
44 | ||
45 | cur_sts = sst_dsp_shim_read(ctx, SKL_ADSP_FW_STATUS) & SKL_FW_STS_MASK; | |
46 | ||
47 | return (cur_sts == status); | |
48 | } | |
49 | ||
50 | static int skl_transfer_firmware(struct sst_dsp *ctx, | |
51 | const void *basefw, u32 base_fw_size) | |
52 | { | |
53 | int ret = 0; | |
54 | ||
55 | ret = ctx->cl_dev.ops.cl_copy_to_dmabuf(ctx, basefw, base_fw_size); | |
56 | if (ret < 0) | |
57 | return ret; | |
58 | ||
59 | ret = sst_dsp_register_poll(ctx, | |
60 | SKL_ADSP_FW_STATUS, | |
61 | SKL_FW_STS_MASK, | |
62 | SKL_FW_RFW_START, | |
63 | SKL_BASEFW_TIMEOUT, | |
64 | "Firmware boot"); | |
65 | ||
66 | ctx->cl_dev.ops.cl_stop_dma(ctx); | |
67 | ||
68 | return ret; | |
69 | } | |
70 | ||
71 | static int skl_load_base_firmware(struct sst_dsp *ctx) | |
72 | { | |
73 | int ret = 0, i; | |
a750ba5f SP |
74 | struct skl_sst *skl = ctx->thread_context; |
75 | u32 reg; | |
76 | ||
84c9e283 JK |
77 | skl->boot_complete = false; |
78 | init_waitqueue_head(&skl->boot_wait); | |
79 | ||
80 | if (ctx->fw == NULL) { | |
aecf6fd8 | 81 | ret = request_firmware(&ctx->fw, ctx->fw_name, ctx->dev); |
84c9e283 JK |
82 | if (ret < 0) { |
83 | dev_err(ctx->dev, "Request firmware failed %d\n", ret); | |
84 | skl_dsp_disable_core(ctx); | |
85 | return -EIO; | |
86 | } | |
87 | } | |
88 | ||
89 | ret = skl_dsp_boot(ctx); | |
a750ba5f | 90 | if (ret < 0) { |
84c9e283 JK |
91 | dev_err(ctx->dev, "Boot dsp core failed ret: %d", ret); |
92 | goto skl_load_base_firmware_failed; | |
93 | } | |
94 | ||
95 | ret = skl_cldma_prepare(ctx); | |
96 | if (ret < 0) { | |
97 | dev_err(ctx->dev, "CL dma prepare failed : %d", ret); | |
98 | goto skl_load_base_firmware_failed; | |
a750ba5f SP |
99 | } |
100 | ||
101 | /* enable Interrupt */ | |
102 | skl_ipc_int_enable(ctx); | |
103 | skl_ipc_op_int_enable(ctx); | |
104 | ||
105 | /* check ROM Status */ | |
106 | for (i = SKL_INIT_TIMEOUT; i > 0; --i) { | |
107 | if (skl_check_fw_status(ctx, SKL_FW_INIT)) { | |
108 | dev_dbg(ctx->dev, | |
109 | "ROM loaded, we can continue with FW loading\n"); | |
110 | break; | |
111 | } | |
112 | mdelay(1); | |
113 | } | |
114 | if (!i) { | |
115 | reg = sst_dsp_shim_read(ctx, SKL_ADSP_FW_STATUS); | |
116 | dev_err(ctx->dev, | |
117 | "Timeout waiting for ROM init done, reg:0x%x\n", reg); | |
118 | ret = -EIO; | |
ae395937 | 119 | goto transfer_firmware_failed; |
a750ba5f SP |
120 | } |
121 | ||
84c9e283 | 122 | ret = skl_transfer_firmware(ctx, ctx->fw->data, ctx->fw->size); |
a750ba5f SP |
123 | if (ret < 0) { |
124 | dev_err(ctx->dev, "Transfer firmware failed%d\n", ret); | |
ae395937 | 125 | goto transfer_firmware_failed; |
a750ba5f SP |
126 | } else { |
127 | ret = wait_event_timeout(skl->boot_wait, skl->boot_complete, | |
128 | msecs_to_jiffies(SKL_IPC_BOOT_MSECS)); | |
129 | if (ret == 0) { | |
130 | dev_err(ctx->dev, "DSP boot failed, FW Ready timed-out\n"); | |
131 | ret = -EIO; | |
ae395937 | 132 | goto transfer_firmware_failed; |
a750ba5f SP |
133 | } |
134 | ||
135 | dev_dbg(ctx->dev, "Download firmware successful%d\n", ret); | |
136 | skl_dsp_set_state_locked(ctx, SKL_DSP_RUNNING); | |
137 | } | |
a750ba5f | 138 | return 0; |
ae395937 JK |
139 | transfer_firmware_failed: |
140 | ctx->cl_dev.ops.cl_cleanup_controller(ctx); | |
a750ba5f SP |
141 | skl_load_base_firmware_failed: |
142 | skl_dsp_disable_core(ctx); | |
84c9e283 JK |
143 | release_firmware(ctx->fw); |
144 | ctx->fw = NULL; | |
a750ba5f SP |
145 | return ret; |
146 | } | |
147 | ||
148 | static int skl_set_dsp_D0(struct sst_dsp *ctx) | |
149 | { | |
150 | int ret; | |
151 | ||
152 | ret = skl_load_base_firmware(ctx); | |
153 | if (ret < 0) { | |
154 | dev_err(ctx->dev, "unable to load firmware\n"); | |
155 | return ret; | |
156 | } | |
157 | ||
158 | skl_dsp_set_state_locked(ctx, SKL_DSP_RUNNING); | |
159 | ||
160 | return ret; | |
161 | } | |
162 | ||
163 | static int skl_set_dsp_D3(struct sst_dsp *ctx) | |
164 | { | |
165 | int ret; | |
166 | struct skl_ipc_dxstate_info dx; | |
167 | struct skl_sst *skl = ctx->thread_context; | |
168 | ||
169 | dev_dbg(ctx->dev, "In %s:\n", __func__); | |
170 | mutex_lock(&ctx->mutex); | |
171 | if (!is_skl_dsp_running(ctx)) { | |
172 | mutex_unlock(&ctx->mutex); | |
173 | return 0; | |
174 | } | |
175 | mutex_unlock(&ctx->mutex); | |
176 | ||
177 | dx.core_mask = SKL_DSP_CORE0_MASK; | |
178 | dx.dx_mask = SKL_IPC_D3_MASK; | |
179 | ret = skl_ipc_set_dx(&skl->ipc, SKL_INSTANCE_ID, SKL_BASE_FW_MODULE_ID, &dx); | |
53afce2c JK |
180 | if (ret < 0) |
181 | dev_err(ctx->dev, | |
182 | "D3 request to FW failed, continuing reset: %d", ret); | |
183 | ||
184 | /* disable Interrupt */ | |
185 | ctx->cl_dev.ops.cl_cleanup_controller(ctx); | |
186 | skl_cldma_int_disable(ctx); | |
187 | skl_ipc_op_int_disable(ctx); | |
188 | skl_ipc_int_disable(ctx); | |
a750ba5f SP |
189 | |
190 | ret = skl_dsp_disable_core(ctx); | |
191 | if (ret < 0) { | |
192 | dev_err(ctx->dev, "disable dsp core failed ret: %d\n", ret); | |
193 | ret = -EIO; | |
194 | } | |
195 | skl_dsp_set_state_locked(ctx, SKL_DSP_RESET); | |
196 | ||
197 | return ret; | |
198 | } | |
199 | ||
200 | static unsigned int skl_get_errorcode(struct sst_dsp *ctx) | |
201 | { | |
202 | return sst_dsp_shim_read(ctx, SKL_ADSP_ERROR_CODE); | |
203 | } | |
204 | ||
6c5768b3 D |
205 | /* |
206 | * since get/set_module are called from DAPM context, | |
207 | * we don't need lock for usage count | |
208 | */ | |
db4e5613 | 209 | static int skl_get_module(struct sst_dsp *ctx, u16 mod_id) |
6c5768b3 D |
210 | { |
211 | struct skl_module_table *module; | |
212 | ||
213 | list_for_each_entry(module, &ctx->module_list, list) { | |
214 | if (module->mod_info->mod_id == mod_id) | |
215 | return ++module->usage_cnt; | |
216 | } | |
217 | ||
218 | return -EINVAL; | |
219 | } | |
220 | ||
db4e5613 | 221 | static int skl_put_module(struct sst_dsp *ctx, u16 mod_id) |
6c5768b3 D |
222 | { |
223 | struct skl_module_table *module; | |
224 | ||
225 | list_for_each_entry(module, &ctx->module_list, list) { | |
226 | if (module->mod_info->mod_id == mod_id) | |
227 | return --module->usage_cnt; | |
228 | } | |
229 | ||
230 | return -EINVAL; | |
231 | } | |
232 | ||
233 | static struct skl_module_table *skl_fill_module_table(struct sst_dsp *ctx, | |
234 | char *mod_name, int mod_id) | |
235 | { | |
236 | const struct firmware *fw; | |
237 | struct skl_module_table *skl_module; | |
238 | unsigned int size; | |
239 | int ret; | |
240 | ||
241 | ret = request_firmware(&fw, mod_name, ctx->dev); | |
242 | if (ret < 0) { | |
243 | dev_err(ctx->dev, "Request Module %s failed :%d\n", | |
244 | mod_name, ret); | |
245 | return NULL; | |
246 | } | |
247 | ||
248 | skl_module = devm_kzalloc(ctx->dev, sizeof(*skl_module), GFP_KERNEL); | |
249 | if (skl_module == NULL) { | |
250 | release_firmware(fw); | |
251 | return NULL; | |
252 | } | |
253 | ||
254 | size = sizeof(*skl_module->mod_info); | |
255 | skl_module->mod_info = devm_kzalloc(ctx->dev, size, GFP_KERNEL); | |
256 | if (skl_module->mod_info == NULL) { | |
257 | release_firmware(fw); | |
258 | return NULL; | |
259 | } | |
260 | ||
261 | skl_module->mod_info->mod_id = mod_id; | |
262 | skl_module->mod_info->fw = fw; | |
263 | list_add(&skl_module->list, &ctx->module_list); | |
264 | ||
265 | return skl_module; | |
266 | } | |
267 | ||
268 | /* get a module from it's unique ID */ | |
269 | static struct skl_module_table *skl_module_get_from_id( | |
270 | struct sst_dsp *ctx, u16 mod_id) | |
271 | { | |
272 | struct skl_module_table *module; | |
273 | ||
274 | if (list_empty(&ctx->module_list)) { | |
275 | dev_err(ctx->dev, "Module list is empty\n"); | |
276 | return NULL; | |
277 | } | |
278 | ||
279 | list_for_each_entry(module, &ctx->module_list, list) { | |
280 | if (module->mod_info->mod_id == mod_id) | |
281 | return module; | |
282 | } | |
283 | ||
284 | return NULL; | |
285 | } | |
286 | ||
287 | static int skl_transfer_module(struct sst_dsp *ctx, | |
288 | struct skl_load_module_info *module) | |
289 | { | |
290 | int ret; | |
291 | struct skl_sst *skl = ctx->thread_context; | |
292 | ||
293 | ret = ctx->cl_dev.ops.cl_copy_to_dmabuf(ctx, module->fw->data, | |
294 | module->fw->size); | |
295 | if (ret < 0) | |
296 | return ret; | |
297 | ||
298 | ret = skl_ipc_load_modules(&skl->ipc, SKL_NUM_MODULES, | |
299 | (void *)&module->mod_id); | |
300 | if (ret < 0) | |
301 | dev_err(ctx->dev, "Failed to Load module: %d\n", ret); | |
302 | ||
303 | ctx->cl_dev.ops.cl_stop_dma(ctx); | |
304 | ||
305 | return ret; | |
306 | } | |
307 | ||
09305da9 | 308 | static int skl_load_module(struct sst_dsp *ctx, u16 mod_id, u8 *guid) |
6c5768b3 D |
309 | { |
310 | struct skl_module_table *module_entry = NULL; | |
311 | int ret = 0; | |
312 | char mod_name[64]; /* guid str = 32 chars + 4 hyphens */ | |
09305da9 | 313 | uuid_le *uuid_mod; |
6c5768b3 | 314 | |
09305da9 SN |
315 | uuid_mod = (uuid_le *)guid; |
316 | snprintf(mod_name, sizeof(mod_name), "%s%pUL%s", | |
317 | "intel/dsp_fw_", uuid_mod, ".bin"); | |
6c5768b3 D |
318 | |
319 | module_entry = skl_module_get_from_id(ctx, mod_id); | |
320 | if (module_entry == NULL) { | |
321 | module_entry = skl_fill_module_table(ctx, mod_name, mod_id); | |
322 | if (module_entry == NULL) { | |
323 | dev_err(ctx->dev, "Failed to Load module\n"); | |
324 | return -EINVAL; | |
325 | } | |
326 | } | |
327 | ||
328 | if (!module_entry->usage_cnt) { | |
329 | ret = skl_transfer_module(ctx, module_entry->mod_info); | |
330 | if (ret < 0) { | |
331 | dev_err(ctx->dev, "Failed to Load module\n"); | |
332 | return ret; | |
333 | } | |
334 | } | |
335 | ||
336 | ret = skl_get_module(ctx, mod_id); | |
337 | ||
338 | return ret; | |
339 | } | |
340 | ||
341 | static int skl_unload_module(struct sst_dsp *ctx, u16 mod_id) | |
342 | { | |
db4e5613 | 343 | int usage_cnt; |
6c5768b3 D |
344 | struct skl_sst *skl = ctx->thread_context; |
345 | int ret = 0; | |
346 | ||
347 | usage_cnt = skl_put_module(ctx, mod_id); | |
348 | if (usage_cnt < 0) { | |
349 | dev_err(ctx->dev, "Module bad usage cnt!:%d\n", usage_cnt); | |
350 | return -EIO; | |
351 | } | |
352 | ret = skl_ipc_unload_modules(&skl->ipc, | |
353 | SKL_NUM_MODULES, &mod_id); | |
354 | if (ret < 0) { | |
355 | dev_err(ctx->dev, "Failed to UnLoad module\n"); | |
356 | skl_get_module(ctx, mod_id); | |
357 | return ret; | |
358 | } | |
359 | ||
360 | return ret; | |
361 | } | |
362 | ||
363 | static void skl_clear_module_table(struct sst_dsp *ctx) | |
364 | { | |
365 | struct skl_module_table *module, *tmp; | |
366 | ||
367 | if (list_empty(&ctx->module_list)) | |
368 | return; | |
369 | ||
370 | list_for_each_entry_safe(module, tmp, &ctx->module_list, list) { | |
371 | list_del(&module->list); | |
372 | release_firmware(module->mod_info->fw); | |
373 | } | |
374 | } | |
375 | ||
a750ba5f SP |
376 | static struct skl_dsp_fw_ops skl_fw_ops = { |
377 | .set_state_D0 = skl_set_dsp_D0, | |
378 | .set_state_D3 = skl_set_dsp_D3, | |
379 | .load_fw = skl_load_base_firmware, | |
380 | .get_fw_errcode = skl_get_errorcode, | |
6c5768b3 D |
381 | .load_mod = skl_load_module, |
382 | .unload_mod = skl_unload_module, | |
a750ba5f SP |
383 | }; |
384 | ||
385 | static struct sst_ops skl_ops = { | |
386 | .irq_handler = skl_dsp_sst_interrupt, | |
387 | .write = sst_shim32_write, | |
388 | .read = sst_shim32_read, | |
389 | .ram_read = sst_memcpy_fromio_32, | |
390 | .ram_write = sst_memcpy_toio_32, | |
391 | .free = skl_dsp_free, | |
392 | }; | |
393 | ||
394 | static struct sst_dsp_device skl_dev = { | |
395 | .thread = skl_dsp_irq_thread_handler, | |
396 | .ops = &skl_ops, | |
397 | }; | |
398 | ||
399 | int skl_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq, | |
aecf6fd8 | 400 | const char *fw_name, struct skl_dsp_loader_ops dsp_ops, struct skl_sst **dsp) |
a750ba5f SP |
401 | { |
402 | struct skl_sst *skl; | |
403 | struct sst_dsp *sst; | |
404 | int ret; | |
405 | ||
406 | skl = devm_kzalloc(dev, sizeof(*skl), GFP_KERNEL); | |
407 | if (skl == NULL) | |
408 | return -ENOMEM; | |
409 | ||
410 | skl->dev = dev; | |
411 | skl_dev.thread_context = skl; | |
412 | ||
413 | skl->dsp = skl_dsp_ctx_init(dev, &skl_dev, irq); | |
414 | if (!skl->dsp) { | |
415 | dev_err(skl->dev, "%s: no device\n", __func__); | |
416 | return -ENODEV; | |
417 | } | |
418 | ||
419 | sst = skl->dsp; | |
420 | ||
aecf6fd8 | 421 | sst->fw_name = fw_name; |
a750ba5f SP |
422 | sst->addr.lpe = mmio_base; |
423 | sst->addr.shim = mmio_base; | |
424 | sst_dsp_mailbox_init(sst, (SKL_ADSP_SRAM0_BASE + SKL_ADSP_W0_STAT_SZ), | |
425 | SKL_ADSP_W0_UP_SZ, SKL_ADSP_SRAM1_BASE, SKL_ADSP_W1_SZ); | |
426 | ||
6c5768b3 | 427 | INIT_LIST_HEAD(&sst->module_list); |
a750ba5f SP |
428 | sst->dsp_ops = dsp_ops; |
429 | sst->fw_ops = skl_fw_ops; | |
430 | ||
431 | ret = skl_ipc_init(dev, skl); | |
432 | if (ret) | |
433 | return ret; | |
434 | ||
a750ba5f SP |
435 | ret = sst->fw_ops.load_fw(sst); |
436 | if (ret < 0) { | |
437 | dev_err(dev, "Load base fw failed : %d", ret); | |
b4fe965f | 438 | goto cleanup; |
a750ba5f SP |
439 | } |
440 | ||
441 | if (dsp) | |
442 | *dsp = skl; | |
443 | ||
b4fe965f | 444 | return ret; |
a750ba5f | 445 | |
b4fe965f SP |
446 | cleanup: |
447 | skl_sst_dsp_cleanup(dev, skl); | |
a750ba5f SP |
448 | return ret; |
449 | } | |
450 | EXPORT_SYMBOL_GPL(skl_sst_dsp_init); | |
451 | ||
452 | void skl_sst_dsp_cleanup(struct device *dev, struct skl_sst *ctx) | |
453 | { | |
6c5768b3 | 454 | skl_clear_module_table(ctx->dsp); |
a750ba5f | 455 | skl_ipc_free(&ctx->ipc); |
a750ba5f | 456 | ctx->dsp->ops->free(ctx->dsp); |
95536d8c D |
457 | if (ctx->boot_complete) { |
458 | ctx->dsp->cl_dev.ops.cl_cleanup_controller(ctx->dsp); | |
459 | skl_cldma_int_disable(ctx->dsp); | |
460 | } | |
a750ba5f SP |
461 | } |
462 | EXPORT_SYMBOL_GPL(skl_sst_dsp_cleanup); | |
463 | ||
464 | MODULE_LICENSE("GPL v2"); | |
465 | MODULE_DESCRIPTION("Intel Skylake IPC driver"); |