diff options
-rw-r--r-- | sound/soc/intel/skylake/skl-sst-dsp.h | 14 | ||||
-rw-r--r-- | sound/soc/intel/skylake/skl-sst-ipc.c | 53 | ||||
-rw-r--r-- | sound/soc/intel/skylake/skl-sst-ipc.h | 6 | ||||
-rw-r--r-- | sound/soc/intel/skylake/skl-sst.c | 175 | ||||
-rw-r--r-- | sound/soc/intel/skylake/skl-topology.c | 29 |
5 files changed, 276 insertions, 1 deletions
diff --git a/sound/soc/intel/skylake/skl-sst-dsp.h b/sound/soc/intel/skylake/skl-sst-dsp.h index f2a69d9e56b3..5d0947935e2b 100644 --- a/sound/soc/intel/skylake/skl-sst-dsp.h +++ b/sound/soc/intel/skylake/skl-sst-dsp.h | |||
@@ -114,6 +114,9 @@ struct skl_dsp_fw_ops { | |||
114 | int (*set_state_D0)(struct sst_dsp *ctx); | 114 | int (*set_state_D0)(struct sst_dsp *ctx); |
115 | int (*set_state_D3)(struct sst_dsp *ctx); | 115 | int (*set_state_D3)(struct sst_dsp *ctx); |
116 | unsigned int (*get_fw_errcode)(struct sst_dsp *ctx); | 116 | unsigned int (*get_fw_errcode)(struct sst_dsp *ctx); |
117 | int (*load_mod)(struct sst_dsp *ctx, u16 mod_id, char *mod_name); | ||
118 | int (*unload_mod)(struct sst_dsp *ctx, u16 mod_id); | ||
119 | |||
117 | }; | 120 | }; |
118 | 121 | ||
119 | struct skl_dsp_loader_ops { | 122 | struct skl_dsp_loader_ops { |
@@ -123,6 +126,17 @@ struct skl_dsp_loader_ops { | |||
123 | struct snd_dma_buffer *dmab); | 126 | struct snd_dma_buffer *dmab); |
124 | }; | 127 | }; |
125 | 128 | ||
129 | struct skl_load_module_info { | ||
130 | u16 mod_id; | ||
131 | const struct firmware *fw; | ||
132 | }; | ||
133 | |||
134 | struct skl_module_table { | ||
135 | struct skl_load_module_info *mod_info; | ||
136 | unsigned int usage_cnt; | ||
137 | struct list_head list; | ||
138 | }; | ||
139 | |||
126 | void skl_cldma_process_intr(struct sst_dsp *ctx); | 140 | void skl_cldma_process_intr(struct sst_dsp *ctx); |
127 | void skl_cldma_int_disable(struct sst_dsp *ctx); | 141 | void skl_cldma_int_disable(struct sst_dsp *ctx); |
128 | int skl_cldma_prepare(struct sst_dsp *ctx); | 142 | int skl_cldma_prepare(struct sst_dsp *ctx); |
diff --git a/sound/soc/intel/skylake/skl-sst-ipc.c b/sound/soc/intel/skylake/skl-sst-ipc.c index 95679c02c6ee..33860d2311c4 100644 --- a/sound/soc/intel/skylake/skl-sst-ipc.c +++ b/sound/soc/intel/skylake/skl-sst-ipc.c | |||
@@ -130,6 +130,11 @@ | |||
130 | #define IPC_SRC_QUEUE_MASK 0x7 | 130 | #define IPC_SRC_QUEUE_MASK 0x7 |
131 | #define IPC_SRC_QUEUE(x) (((x) & IPC_SRC_QUEUE_MASK) \ | 131 | #define IPC_SRC_QUEUE(x) (((x) & IPC_SRC_QUEUE_MASK) \ |
132 | << IPC_SRC_QUEUE_SHIFT) | 132 | << IPC_SRC_QUEUE_SHIFT) |
133 | /* Load Module count */ | ||
134 | #define IPC_LOAD_MODULE_SHIFT 0 | ||
135 | #define IPC_LOAD_MODULE_MASK 0xFF | ||
136 | #define IPC_LOAD_MODULE_CNT(x) (((x) & IPC_LOAD_MODULE_MASK) \ | ||
137 | << IPC_LOAD_MODULE_SHIFT) | ||
133 | 138 | ||
134 | /* Save pipeline messgae extension register */ | 139 | /* Save pipeline messgae extension register */ |
135 | #define IPC_DMA_ID_SHIFT 0 | 140 | #define IPC_DMA_ID_SHIFT 0 |
@@ -728,6 +733,54 @@ int skl_ipc_bind_unbind(struct sst_generic_ipc *ipc, | |||
728 | } | 733 | } |
729 | EXPORT_SYMBOL_GPL(skl_ipc_bind_unbind); | 734 | EXPORT_SYMBOL_GPL(skl_ipc_bind_unbind); |
730 | 735 | ||
736 | /* | ||
737 | * In order to load a module we need to send IPC to initiate that. DMA will | ||
738 | * performed to load the module memory. The FW supports multiple module load | ||
739 | * at single shot, so we can send IPC with N modules represented by | ||
740 | * module_cnt | ||
741 | */ | ||
742 | int skl_ipc_load_modules(struct sst_generic_ipc *ipc, | ||
743 | u8 module_cnt, void *data) | ||
744 | { | ||
745 | struct skl_ipc_header header = {0}; | ||
746 | u64 *ipc_header = (u64 *)(&header); | ||
747 | int ret; | ||
748 | |||
749 | header.primary = IPC_MSG_TARGET(IPC_FW_GEN_MSG); | ||
750 | header.primary |= IPC_MSG_DIR(IPC_MSG_REQUEST); | ||
751 | header.primary |= IPC_GLB_TYPE(IPC_GLB_LOAD_MULTIPLE_MODS); | ||
752 | header.primary |= IPC_LOAD_MODULE_CNT(module_cnt); | ||
753 | |||
754 | ret = sst_ipc_tx_message_wait(ipc, *ipc_header, data, | ||
755 | (sizeof(u16) * module_cnt), NULL, 0); | ||
756 | if (ret < 0) | ||
757 | dev_err(ipc->dev, "ipc: load modules failed :%d\n", ret); | ||
758 | |||
759 | return ret; | ||
760 | } | ||
761 | EXPORT_SYMBOL_GPL(skl_ipc_load_modules); | ||
762 | |||
763 | int skl_ipc_unload_modules(struct sst_generic_ipc *ipc, u8 module_cnt, | ||
764 | void *data) | ||
765 | { | ||
766 | struct skl_ipc_header header = {0}; | ||
767 | u64 *ipc_header = (u64 *)(&header); | ||
768 | int ret; | ||
769 | |||
770 | header.primary = IPC_MSG_TARGET(IPC_FW_GEN_MSG); | ||
771 | header.primary |= IPC_MSG_DIR(IPC_MSG_REQUEST); | ||
772 | header.primary |= IPC_GLB_TYPE(IPC_GLB_UNLOAD_MULTIPLE_MODS); | ||
773 | header.primary |= IPC_LOAD_MODULE_CNT(module_cnt); | ||
774 | |||
775 | ret = sst_ipc_tx_message_wait(ipc, *ipc_header, data, | ||
776 | (sizeof(u16) * module_cnt), NULL, 0); | ||
777 | if (ret < 0) | ||
778 | dev_err(ipc->dev, "ipc: unload modules failed :%d\n", ret); | ||
779 | |||
780 | return ret; | ||
781 | } | ||
782 | EXPORT_SYMBOL_GPL(skl_ipc_unload_modules); | ||
783 | |||
731 | int skl_ipc_set_large_config(struct sst_generic_ipc *ipc, | 784 | int skl_ipc_set_large_config(struct sst_generic_ipc *ipc, |
732 | struct skl_ipc_large_config_msg *msg, u32 *param) | 785 | struct skl_ipc_large_config_msg *msg, u32 *param) |
733 | { | 786 | { |
diff --git a/sound/soc/intel/skylake/skl-sst-ipc.h b/sound/soc/intel/skylake/skl-sst-ipc.h index f1a154e45dc3..e17012778560 100644 --- a/sound/soc/intel/skylake/skl-sst-ipc.h +++ b/sound/soc/intel/skylake/skl-sst-ipc.h | |||
@@ -108,6 +108,12 @@ int skl_ipc_init_instance(struct sst_generic_ipc *sst_ipc, | |||
108 | int skl_ipc_bind_unbind(struct sst_generic_ipc *sst_ipc, | 108 | int skl_ipc_bind_unbind(struct sst_generic_ipc *sst_ipc, |
109 | struct skl_ipc_bind_unbind_msg *msg); | 109 | struct skl_ipc_bind_unbind_msg *msg); |
110 | 110 | ||
111 | int skl_ipc_load_modules(struct sst_generic_ipc *ipc, | ||
112 | u8 module_cnt, void *data); | ||
113 | |||
114 | int skl_ipc_unload_modules(struct sst_generic_ipc *ipc, | ||
115 | u8 module_cnt, void *data); | ||
116 | |||
111 | int skl_ipc_set_dx(struct sst_generic_ipc *ipc, | 117 | int skl_ipc_set_dx(struct sst_generic_ipc *ipc, |
112 | u8 instance_id, u16 module_id, struct skl_ipc_dxstate_info *dx); | 118 | u8 instance_id, u16 module_id, struct skl_ipc_dxstate_info *dx); |
113 | 119 | ||
diff --git a/sound/soc/intel/skylake/skl-sst.c b/sound/soc/intel/skylake/skl-sst.c index e1d34d5c3f9a..8cd5cdb21fd5 100644 --- a/sound/soc/intel/skylake/skl-sst.c +++ b/sound/soc/intel/skylake/skl-sst.c | |||
@@ -38,6 +38,8 @@ | |||
38 | #define SKL_INSTANCE_ID 0 | 38 | #define SKL_INSTANCE_ID 0 |
39 | #define SKL_BASE_FW_MODULE_ID 0 | 39 | #define SKL_BASE_FW_MODULE_ID 0 |
40 | 40 | ||
41 | #define SKL_NUM_MODULES 1 | ||
42 | |||
41 | static bool skl_check_fw_status(struct sst_dsp *ctx, u32 status) | 43 | static bool skl_check_fw_status(struct sst_dsp *ctx, u32 status) |
42 | { | 44 | { |
43 | u32 cur_sts; | 45 | u32 cur_sts; |
@@ -202,11 +204,182 @@ static unsigned int skl_get_errorcode(struct sst_dsp *ctx) | |||
202 | return sst_dsp_shim_read(ctx, SKL_ADSP_ERROR_CODE); | 204 | return sst_dsp_shim_read(ctx, SKL_ADSP_ERROR_CODE); |
203 | } | 205 | } |
204 | 206 | ||
207 | /* | ||
208 | * since get/set_module are called from DAPM context, | ||
209 | * we don't need lock for usage count | ||
210 | */ | ||
211 | static unsigned int skl_get_module(struct sst_dsp *ctx, u16 mod_id) | ||
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 | |||
223 | static unsigned int skl_put_module(struct sst_dsp *ctx, u16 mod_id) | ||
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 | { | ||
343 | unsigned int usage_cnt; | ||
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 | |||
205 | static struct skl_dsp_fw_ops skl_fw_ops = { | 376 | static struct skl_dsp_fw_ops skl_fw_ops = { |
206 | .set_state_D0 = skl_set_dsp_D0, | 377 | .set_state_D0 = skl_set_dsp_D0, |
207 | .set_state_D3 = skl_set_dsp_D3, | 378 | .set_state_D3 = skl_set_dsp_D3, |
208 | .load_fw = skl_load_base_firmware, | 379 | .load_fw = skl_load_base_firmware, |
209 | .get_fw_errcode = skl_get_errorcode, | 380 | .get_fw_errcode = skl_get_errorcode, |
381 | .load_mod = skl_load_module, | ||
382 | .unload_mod = skl_unload_module, | ||
210 | }; | 383 | }; |
211 | 384 | ||
212 | static struct sst_ops skl_ops = { | 385 | static struct sst_ops skl_ops = { |
@@ -251,6 +424,7 @@ int skl_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq, | |||
251 | sst_dsp_mailbox_init(sst, (SKL_ADSP_SRAM0_BASE + SKL_ADSP_W0_STAT_SZ), | 424 | sst_dsp_mailbox_init(sst, (SKL_ADSP_SRAM0_BASE + SKL_ADSP_W0_STAT_SZ), |
252 | SKL_ADSP_W0_UP_SZ, SKL_ADSP_SRAM1_BASE, SKL_ADSP_W1_SZ); | 425 | SKL_ADSP_W0_UP_SZ, SKL_ADSP_SRAM1_BASE, SKL_ADSP_W1_SZ); |
253 | 426 | ||
427 | INIT_LIST_HEAD(&sst->module_list); | ||
254 | sst->dsp_ops = dsp_ops; | 428 | sst->dsp_ops = dsp_ops; |
255 | sst->fw_ops = skl_fw_ops; | 429 | sst->fw_ops = skl_fw_ops; |
256 | 430 | ||
@@ -277,6 +451,7 @@ EXPORT_SYMBOL_GPL(skl_sst_dsp_init); | |||
277 | 451 | ||
278 | void skl_sst_dsp_cleanup(struct device *dev, struct skl_sst *ctx) | 452 | void skl_sst_dsp_cleanup(struct device *dev, struct skl_sst *ctx) |
279 | { | 453 | { |
454 | skl_clear_module_table(ctx->dsp); | ||
280 | skl_ipc_free(&ctx->ipc); | 455 | skl_ipc_free(&ctx->ipc); |
281 | ctx->dsp->ops->free(ctx->dsp); | 456 | ctx->dsp->ops->free(ctx->dsp); |
282 | } | 457 | } |
diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c index 622f7430e100..32735eff386c 100644 --- a/sound/soc/intel/skylake/skl-topology.c +++ b/sound/soc/intel/skylake/skl-topology.c | |||
@@ -26,6 +26,8 @@ | |||
26 | #include "skl-topology.h" | 26 | #include "skl-topology.h" |
27 | #include "skl.h" | 27 | #include "skl.h" |
28 | #include "skl-tplg-interface.h" | 28 | #include "skl-tplg-interface.h" |
29 | #include "../common/sst-dsp.h" | ||
30 | #include "../common/sst-dsp-priv.h" | ||
29 | 31 | ||
30 | #define SKL_CH_FIXUP_MASK (1 << 0) | 32 | #define SKL_CH_FIXUP_MASK (1 << 0) |
31 | #define SKL_RATE_FIXUP_MASK (1 << 1) | 33 | #define SKL_RATE_FIXUP_MASK (1 << 1) |
@@ -412,6 +414,13 @@ skl_tplg_init_pipe_modules(struct skl *skl, struct skl_pipe *pipe) | |||
412 | if (!skl_tplg_alloc_pipe_mcps(skl, mconfig)) | 414 | if (!skl_tplg_alloc_pipe_mcps(skl, mconfig)) |
413 | return -ENOMEM; | 415 | return -ENOMEM; |
414 | 416 | ||
417 | if (mconfig->is_loadable && ctx->dsp->fw_ops.load_mod) { | ||
418 | ret = ctx->dsp->fw_ops.load_mod(ctx->dsp, | ||
419 | mconfig->id.module_id, mconfig->guid); | ||
420 | if (ret < 0) | ||
421 | return ret; | ||
422 | } | ||
423 | |||
415 | /* | 424 | /* |
416 | * apply fix/conversion to module params based on | 425 | * apply fix/conversion to module params based on |
417 | * FE/BE params | 426 | * FE/BE params |
@@ -431,6 +440,24 @@ skl_tplg_init_pipe_modules(struct skl *skl, struct skl_pipe *pipe) | |||
431 | return 0; | 440 | return 0; |
432 | } | 441 | } |
433 | 442 | ||
443 | static int skl_tplg_unload_pipe_modules(struct skl_sst *ctx, | ||
444 | struct skl_pipe *pipe) | ||
445 | { | ||
446 | struct skl_pipe_module *w_module = NULL; | ||
447 | struct skl_module_cfg *mconfig = NULL; | ||
448 | |||
449 | list_for_each_entry(w_module, &pipe->w_list, node) { | ||
450 | mconfig = w_module->w->priv; | ||
451 | |||
452 | if (mconfig->is_loadable && ctx->dsp->fw_ops.unload_mod) | ||
453 | return ctx->dsp->fw_ops.unload_mod(ctx->dsp, | ||
454 | mconfig->id.module_id); | ||
455 | } | ||
456 | |||
457 | /* no modules to unload in this path, so return */ | ||
458 | return 0; | ||
459 | } | ||
460 | |||
434 | /* | 461 | /* |
435 | * Mixer module represents a pipeline. So in the Pre-PMU event of mixer we | 462 | * Mixer module represents a pipeline. So in the Pre-PMU event of mixer we |
436 | * need create the pipeline. So we do following: | 463 | * need create the pipeline. So we do following: |
@@ -755,7 +782,7 @@ static int skl_tplg_mixer_dapm_post_pmd_event(struct snd_soc_dapm_widget *w, | |||
755 | 782 | ||
756 | ret = skl_delete_pipe(ctx, mconfig->pipe); | 783 | ret = skl_delete_pipe(ctx, mconfig->pipe); |
757 | 784 | ||
758 | return ret; | 785 | return skl_tplg_unload_pipe_modules(ctx, s_pipe); |
759 | } | 786 | } |
760 | 787 | ||
761 | /* | 788 | /* |