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> |
a750ba5f SP |
23 | #include "../common/sst-dsp.h" |
24 | #include "../common/sst-dsp-priv.h" | |
25 | #include "../common/sst-ipc.h" | |
26 | #include "skl-sst-ipc.h" | |
27 | ||
28 | #define SKL_BASEFW_TIMEOUT 300 | |
29 | #define SKL_INIT_TIMEOUT 1000 | |
30 | ||
31 | /* Intel HD Audio SRAM Window 0*/ | |
32 | #define SKL_ADSP_SRAM0_BASE 0x8000 | |
33 | ||
34 | /* Firmware status window */ | |
35 | #define SKL_ADSP_FW_STATUS SKL_ADSP_SRAM0_BASE | |
36 | #define SKL_ADSP_ERROR_CODE (SKL_ADSP_FW_STATUS + 0x4) | |
37 | ||
38 | #define SKL_INSTANCE_ID 0 | |
39 | #define SKL_BASE_FW_MODULE_ID 0 | |
40 | ||
6c5768b3 D |
41 | #define SKL_NUM_MODULES 1 |
42 | ||
a750ba5f SP |
43 | static bool skl_check_fw_status(struct sst_dsp *ctx, u32 status) |
44 | { | |
45 | u32 cur_sts; | |
46 | ||
47 | cur_sts = sst_dsp_shim_read(ctx, SKL_ADSP_FW_STATUS) & SKL_FW_STS_MASK; | |
48 | ||
49 | return (cur_sts == status); | |
50 | } | |
51 | ||
52 | static int skl_transfer_firmware(struct sst_dsp *ctx, | |
53 | const void *basefw, u32 base_fw_size) | |
54 | { | |
55 | int ret = 0; | |
56 | ||
57 | ret = ctx->cl_dev.ops.cl_copy_to_dmabuf(ctx, basefw, base_fw_size); | |
58 | if (ret < 0) | |
59 | return ret; | |
60 | ||
61 | ret = sst_dsp_register_poll(ctx, | |
62 | SKL_ADSP_FW_STATUS, | |
63 | SKL_FW_STS_MASK, | |
64 | SKL_FW_RFW_START, | |
65 | SKL_BASEFW_TIMEOUT, | |
66 | "Firmware boot"); | |
67 | ||
68 | ctx->cl_dev.ops.cl_stop_dma(ctx); | |
69 | ||
70 | return ret; | |
71 | } | |
72 | ||
73 | static int skl_load_base_firmware(struct sst_dsp *ctx) | |
74 | { | |
75 | int ret = 0, i; | |
a750ba5f SP |
76 | struct skl_sst *skl = ctx->thread_context; |
77 | u32 reg; | |
78 | ||
84c9e283 JK |
79 | skl->boot_complete = false; |
80 | init_waitqueue_head(&skl->boot_wait); | |
81 | ||
82 | if (ctx->fw == NULL) { | |
aecf6fd8 | 83 | ret = request_firmware(&ctx->fw, ctx->fw_name, ctx->dev); |
84c9e283 JK |
84 | if (ret < 0) { |
85 | dev_err(ctx->dev, "Request firmware failed %d\n", ret); | |
86 | skl_dsp_disable_core(ctx); | |
87 | return -EIO; | |
88 | } | |
89 | } | |
90 | ||
91 | ret = skl_dsp_boot(ctx); | |
a750ba5f | 92 | if (ret < 0) { |
84c9e283 JK |
93 | dev_err(ctx->dev, "Boot dsp core failed ret: %d", ret); |
94 | goto skl_load_base_firmware_failed; | |
95 | } | |
96 | ||
97 | ret = skl_cldma_prepare(ctx); | |
98 | if (ret < 0) { | |
99 | dev_err(ctx->dev, "CL dma prepare failed : %d", ret); | |
100 | goto skl_load_base_firmware_failed; | |
a750ba5f SP |
101 | } |
102 | ||
103 | /* enable Interrupt */ | |
104 | skl_ipc_int_enable(ctx); | |
105 | skl_ipc_op_int_enable(ctx); | |
106 | ||
107 | /* check ROM Status */ | |
108 | for (i = SKL_INIT_TIMEOUT; i > 0; --i) { | |
109 | if (skl_check_fw_status(ctx, SKL_FW_INIT)) { | |
110 | dev_dbg(ctx->dev, | |
111 | "ROM loaded, we can continue with FW loading\n"); | |
112 | break; | |
113 | } | |
114 | mdelay(1); | |
115 | } | |
116 | if (!i) { | |
117 | reg = sst_dsp_shim_read(ctx, SKL_ADSP_FW_STATUS); | |
118 | dev_err(ctx->dev, | |
119 | "Timeout waiting for ROM init done, reg:0x%x\n", reg); | |
120 | ret = -EIO; | |
ae395937 | 121 | goto transfer_firmware_failed; |
a750ba5f SP |
122 | } |
123 | ||
84c9e283 | 124 | ret = skl_transfer_firmware(ctx, ctx->fw->data, ctx->fw->size); |
a750ba5f SP |
125 | if (ret < 0) { |
126 | dev_err(ctx->dev, "Transfer firmware failed%d\n", ret); | |
ae395937 | 127 | goto transfer_firmware_failed; |
a750ba5f SP |
128 | } else { |
129 | ret = wait_event_timeout(skl->boot_wait, skl->boot_complete, | |
130 | msecs_to_jiffies(SKL_IPC_BOOT_MSECS)); | |
131 | if (ret == 0) { | |
132 | dev_err(ctx->dev, "DSP boot failed, FW Ready timed-out\n"); | |
133 | ret = -EIO; | |
ae395937 | 134 | goto transfer_firmware_failed; |
a750ba5f SP |
135 | } |
136 | ||
137 | dev_dbg(ctx->dev, "Download firmware successful%d\n", ret); | |
138 | skl_dsp_set_state_locked(ctx, SKL_DSP_RUNNING); | |
139 | } | |
a750ba5f | 140 | return 0; |
ae395937 JK |
141 | transfer_firmware_failed: |
142 | ctx->cl_dev.ops.cl_cleanup_controller(ctx); | |
a750ba5f SP |
143 | skl_load_base_firmware_failed: |
144 | skl_dsp_disable_core(ctx); | |
84c9e283 JK |
145 | release_firmware(ctx->fw); |
146 | ctx->fw = NULL; | |
a750ba5f SP |
147 | return ret; |
148 | } | |
149 | ||
150 | static int skl_set_dsp_D0(struct sst_dsp *ctx) | |
151 | { | |
152 | int ret; | |
153 | ||
154 | ret = skl_load_base_firmware(ctx); | |
155 | if (ret < 0) { | |
156 | dev_err(ctx->dev, "unable to load firmware\n"); | |
157 | return ret; | |
158 | } | |
159 | ||
160 | skl_dsp_set_state_locked(ctx, SKL_DSP_RUNNING); | |
161 | ||
162 | return ret; | |
163 | } | |
164 | ||
165 | static int skl_set_dsp_D3(struct sst_dsp *ctx) | |
166 | { | |
167 | int ret; | |
168 | struct skl_ipc_dxstate_info dx; | |
169 | struct skl_sst *skl = ctx->thread_context; | |
170 | ||
171 | dev_dbg(ctx->dev, "In %s:\n", __func__); | |
172 | mutex_lock(&ctx->mutex); | |
173 | if (!is_skl_dsp_running(ctx)) { | |
174 | mutex_unlock(&ctx->mutex); | |
175 | return 0; | |
176 | } | |
177 | mutex_unlock(&ctx->mutex); | |
178 | ||
179 | dx.core_mask = SKL_DSP_CORE0_MASK; | |
180 | dx.dx_mask = SKL_IPC_D3_MASK; | |
181 | ret = skl_ipc_set_dx(&skl->ipc, SKL_INSTANCE_ID, SKL_BASE_FW_MODULE_ID, &dx); | |
53afce2c JK |
182 | if (ret < 0) |
183 | dev_err(ctx->dev, | |
184 | "D3 request to FW failed, continuing reset: %d", ret); | |
185 | ||
186 | /* disable Interrupt */ | |
187 | ctx->cl_dev.ops.cl_cleanup_controller(ctx); | |
188 | skl_cldma_int_disable(ctx); | |
189 | skl_ipc_op_int_disable(ctx); | |
190 | skl_ipc_int_disable(ctx); | |
a750ba5f SP |
191 | |
192 | ret = skl_dsp_disable_core(ctx); | |
193 | if (ret < 0) { | |
194 | dev_err(ctx->dev, "disable dsp core failed ret: %d\n", ret); | |
195 | ret = -EIO; | |
196 | } | |
197 | skl_dsp_set_state_locked(ctx, SKL_DSP_RESET); | |
198 | ||
199 | return ret; | |
200 | } | |
201 | ||
202 | static unsigned int skl_get_errorcode(struct sst_dsp *ctx) | |
203 | { | |
204 | return sst_dsp_shim_read(ctx, SKL_ADSP_ERROR_CODE); | |
205 | } | |
206 | ||
6c5768b3 D |
207 | /* |
208 | * since get/set_module are called from DAPM context, | |
209 | * we don't need lock for usage count | |
210 | */ | |
db4e5613 | 211 | static int skl_get_module(struct sst_dsp *ctx, u16 mod_id) |
6c5768b3 D |
212 | { |
213 | struct skl_module_table *module; | |
214 | ||
215 | list_for_each_entry(module, &ctx->module_list, list) { | |
216 | if (module->mod_info->mod_id == mod_id) | |
217 | return ++module->usage_cnt; | |
218 | } | |
219 | ||
220 | return -EINVAL; | |
221 | } | |
222 | ||
db4e5613 | 223 | static int skl_put_module(struct sst_dsp *ctx, u16 mod_id) |
6c5768b3 D |
224 | { |
225 | struct skl_module_table *module; | |
226 | ||
227 | list_for_each_entry(module, &ctx->module_list, list) { | |
228 | if (module->mod_info->mod_id == mod_id) | |
229 | return --module->usage_cnt; | |
230 | } | |
231 | ||
232 | return -EINVAL; | |
233 | } | |
234 | ||
235 | static struct skl_module_table *skl_fill_module_table(struct sst_dsp *ctx, | |
236 | char *mod_name, int mod_id) | |
237 | { | |
238 | const struct firmware *fw; | |
239 | struct skl_module_table *skl_module; | |
240 | unsigned int size; | |
241 | int ret; | |
242 | ||
243 | ret = request_firmware(&fw, mod_name, ctx->dev); | |
244 | if (ret < 0) { | |
245 | dev_err(ctx->dev, "Request Module %s failed :%d\n", | |
246 | mod_name, ret); | |
247 | return NULL; | |
248 | } | |
249 | ||
250 | skl_module = devm_kzalloc(ctx->dev, sizeof(*skl_module), GFP_KERNEL); | |
251 | if (skl_module == NULL) { | |
252 | release_firmware(fw); | |
253 | return NULL; | |
254 | } | |
255 | ||
256 | size = sizeof(*skl_module->mod_info); | |
257 | skl_module->mod_info = devm_kzalloc(ctx->dev, size, GFP_KERNEL); | |
258 | if (skl_module->mod_info == NULL) { | |
259 | release_firmware(fw); | |
260 | return NULL; | |
261 | } | |
262 | ||
263 | skl_module->mod_info->mod_id = mod_id; | |
264 | skl_module->mod_info->fw = fw; | |
265 | list_add(&skl_module->list, &ctx->module_list); | |
266 | ||
267 | return skl_module; | |
268 | } | |
269 | ||
270 | /* get a module from it's unique ID */ | |
271 | static struct skl_module_table *skl_module_get_from_id( | |
272 | struct sst_dsp *ctx, u16 mod_id) | |
273 | { | |
274 | struct skl_module_table *module; | |
275 | ||
276 | if (list_empty(&ctx->module_list)) { | |
277 | dev_err(ctx->dev, "Module list is empty\n"); | |
278 | return NULL; | |
279 | } | |
280 | ||
281 | list_for_each_entry(module, &ctx->module_list, list) { | |
282 | if (module->mod_info->mod_id == mod_id) | |
283 | return module; | |
284 | } | |
285 | ||
286 | return NULL; | |
287 | } | |
288 | ||
289 | static int skl_transfer_module(struct sst_dsp *ctx, | |
290 | struct skl_load_module_info *module) | |
291 | { | |
292 | int ret; | |
293 | struct skl_sst *skl = ctx->thread_context; | |
294 | ||
295 | ret = ctx->cl_dev.ops.cl_copy_to_dmabuf(ctx, module->fw->data, | |
296 | module->fw->size); | |
297 | if (ret < 0) | |
298 | return ret; | |
299 | ||
300 | ret = skl_ipc_load_modules(&skl->ipc, SKL_NUM_MODULES, | |
301 | (void *)&module->mod_id); | |
302 | if (ret < 0) | |
303 | dev_err(ctx->dev, "Failed to Load module: %d\n", ret); | |
304 | ||
305 | ctx->cl_dev.ops.cl_stop_dma(ctx); | |
306 | ||
307 | return ret; | |
308 | } | |
309 | ||
310 | static int skl_load_module(struct sst_dsp *ctx, u16 mod_id, char *guid) | |
311 | { | |
312 | struct skl_module_table *module_entry = NULL; | |
313 | int ret = 0; | |
314 | char mod_name[64]; /* guid str = 32 chars + 4 hyphens */ | |
315 | ||
316 | snprintf(mod_name, sizeof(mod_name), "%s%s%s", | |
317 | "intel/dsp_fw_", guid, ".bin"); | |
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 SP |
456 | ctx->dsp->ops->free(ctx->dsp); |
457 | } | |
458 | EXPORT_SYMBOL_GPL(skl_sst_dsp_cleanup); | |
459 | ||
460 | MODULE_LICENSE("GPL v2"); | |
461 | MODULE_DESCRIPTION("Intel Skylake IPC driver"); |