aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDharageswari R <dharageswari.r@intel.com>2015-12-03 12:59:50 -0500
committerMark Brown <broonie@kernel.org>2015-12-08 12:57:51 -0500
commit6c5768b3aa6f554a719834591ad2c6b4e1291397 (patch)
treeceee435cff939b6c9407065b0730ae2b1341a060
parent140adfba5280617487a848a0fa84f7523d999cf3 (diff)
ASoC: Intel: Skylake: Add support for Loadable modules
A module is loaded when the path consisting the module is opened. The module binary(ies) is loaded from file system and cached in kernel memory for future use. This is downloaded to DSP using DMA and invoking Load module IPCs This patch adds support for load/unload module IPCs, DMAing modules and manging the modules Signed-off-by: Dharageswari R <dharageswari.r@intel.com> Signed-off-by: Jeeja KP <jeeja.kp@intel.com> Signed-off-by: Vinod Koul <vinod.koul@intel.com> Signed-off-by: Mark Brown <broonie@kernel.org>
-rw-r--r--sound/soc/intel/skylake/skl-sst-dsp.h14
-rw-r--r--sound/soc/intel/skylake/skl-sst-ipc.c53
-rw-r--r--sound/soc/intel/skylake/skl-sst-ipc.h6
-rw-r--r--sound/soc/intel/skylake/skl-sst.c175
-rw-r--r--sound/soc/intel/skylake/skl-topology.c29
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
119struct skl_dsp_loader_ops { 122struct 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
129struct skl_load_module_info {
130 u16 mod_id;
131 const struct firmware *fw;
132};
133
134struct skl_module_table {
135 struct skl_load_module_info *mod_info;
136 unsigned int usage_cnt;
137 struct list_head list;
138};
139
126void skl_cldma_process_intr(struct sst_dsp *ctx); 140void skl_cldma_process_intr(struct sst_dsp *ctx);
127void skl_cldma_int_disable(struct sst_dsp *ctx); 141void skl_cldma_int_disable(struct sst_dsp *ctx);
128int skl_cldma_prepare(struct sst_dsp *ctx); 142int 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}
729EXPORT_SYMBOL_GPL(skl_ipc_bind_unbind); 734EXPORT_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 */
742int 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}
761EXPORT_SYMBOL_GPL(skl_ipc_load_modules);
762
763int 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}
782EXPORT_SYMBOL_GPL(skl_ipc_unload_modules);
783
731int skl_ipc_set_large_config(struct sst_generic_ipc *ipc, 784int 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,
108int skl_ipc_bind_unbind(struct sst_generic_ipc *sst_ipc, 108int 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
111int skl_ipc_load_modules(struct sst_generic_ipc *ipc,
112 u8 module_cnt, void *data);
113
114int skl_ipc_unload_modules(struct sst_generic_ipc *ipc,
115 u8 module_cnt, void *data);
116
111int skl_ipc_set_dx(struct sst_generic_ipc *ipc, 117int 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
41static bool skl_check_fw_status(struct sst_dsp *ctx, u32 status) 43static 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 */
211static 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
223static 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
235static 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 */
271static 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
289static 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
310static 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
341static 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
363static 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
205static struct skl_dsp_fw_ops skl_fw_ops = { 376static 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
212static struct sst_ops skl_ops = { 385static 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
278void skl_sst_dsp_cleanup(struct device *dev, struct skl_sst *ctx) 452void 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
443static 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/*