[alsa-devel] [PATCH 1/8] ASoC: Intel: add function to load firmware image
From: "Lu, Han" han.lu@intel.com
Add a general method to load firmware image, and apply to base firmware image loading. With the method, the driver will support loading multiple different modules in order to support different features.
Signed-off-by: Lu, Han han.lu@intel.com
diff --git a/sound/soc/intel/sst-dsp-priv.h b/sound/soc/intel/sst-dsp-priv.h index b9da030..396d545 100644 --- a/sound/soc/intel/sst-dsp-priv.h +++ b/sound/soc/intel/sst-dsp-priv.h @@ -173,6 +173,16 @@ struct sst_module_runtime_context { };
/* + * Audio DSP Module State + */ +enum sst_module_state { + SST_MODULE_STATE_UNLOADED = 0, /* default state */ + SST_MODULE_STATE_LOADED, + SST_MODULE_STATE_INITIALIZED, /* and inactive */ + SST_MODULE_STATE_ACTIVE, +}; + +/* * Audio DSP Generic Module. * * Each Firmware file can consist of 1..N modules. A module can span multiple @@ -203,6 +213,9 @@ struct sst_module { struct list_head list; /* DSP list of modules */ struct list_head list_fw; /* FW list of modules */ struct list_head runtime_list; /* list of runtime module objects*/ + + /* state */ + enum sst_module_state state; };
/* diff --git a/sound/soc/intel/sst-firmware.c b/sound/soc/intel/sst-firmware.c index 5f71ef6..5e58008 100644 --- a/sound/soc/intel/sst-firmware.c +++ b/sound/soc/intel/sst-firmware.c @@ -498,6 +498,7 @@ struct sst_module *sst_module_new(struct sst_fw *sst_fw, sst_module->scratch_size = template->scratch_size; sst_module->persistent_size = template->persistent_size; sst_module->entry = template->entry; + sst_module->state = SST_MODULE_STATE_UNLOADED;
INIT_LIST_HEAD(&sst_module->block_list); INIT_LIST_HEAD(&sst_module->runtime_list); diff --git a/sound/soc/intel/sst-haswell-dsp.c b/sound/soc/intel/sst-haswell-dsp.c index 402b728..8ad733b 100644 --- a/sound/soc/intel/sst-haswell-dsp.c +++ b/sound/soc/intel/sst-haswell-dsp.c @@ -169,6 +169,7 @@ static int hsw_parse_module(struct sst_dsp *dsp, struct sst_fw *fw,
block = (void *)block + sizeof(*block) + block->size; } + mod->state = SST_MODULE_STATE_LOADED;
return 0; } diff --git a/sound/soc/intel/sst-haswell-ipc.c b/sound/soc/intel/sst-haswell-ipc.c index 863a9ca..cbd8c00 100644 --- a/sound/soc/intel/sst-haswell-ipc.c +++ b/sound/soc/intel/sst-haswell-ipc.c @@ -1844,6 +1844,8 @@ int sst_hsw_dsp_runtime_resume(struct sst_hsw *hsw) if (ret < 0) dev_err(dev, "error: audio DSP boot failure\n");
+ sst_hsw_init_module_state(hsw); + ret = wait_event_timeout(hsw->boot_wait, hsw->boot_complete, msecs_to_jiffies(IPC_BOOT_MSECS)); if (ret == 0) { @@ -1886,6 +1888,75 @@ struct sst_dsp *sst_hsw_get_dsp(struct sst_hsw *hsw) return hsw->dsp; }
+void sst_hsw_init_module_state(struct sst_hsw *hsw) +{ + struct sst_module *module; + enum sst_hsw_module_id id; + + /* the base fw contains several modules */ + for (id = SST_HSW_MODULE_BASE_FW; id < SST_HSW_MAX_MODULE_ID; id++) { + module = sst_module_get_from_id(hsw->dsp, id); + if (module) + module->state = SST_MODULE_STATE_ACTIVE; + } +} + +int sst_hsw_module_load(struct sst_hsw *hsw, + u32 module_id, u32 instance_id, char *name) +{ + int ret = 0; + const struct firmware *fw = NULL; + struct sst_fw *hsw_sst_fw; + struct sst_module *module; + struct device *dev = hsw->dev; + struct sst_dsp *dsp = hsw->dsp; + + dev_dbg(dev, "sst_hsw_module_load id=%d, name='%s'", module_id, name); + + module = sst_module_get_from_id(dsp, module_id); + if (module == NULL) { + /* loading for the first time */ + if (module_id == SST_HSW_MODULE_BASE_FW) { + /* for base module: use fw requested in acpi probe */ + fw = dsp->pdata->fw; + if (!fw) { + dev_err(dev, "request Base fw failed\n"); + return -ENODEV; + } + } else { + /* try and load any other optional modules if they are + * available. Use dev_info instead of dev_err in case + * request firmware failed */ + ret = request_firmware(&fw, name, dev); + if (ret) { + dev_info(dev, "fw image %s not available(%d)\n", + name, ret); + return ret; + } + } + hsw_sst_fw = sst_fw_new(dsp, fw, hsw); + if (hsw_sst_fw == NULL) { + dev_err(dev, "error: failed to load firmware\n"); + ret = -ENOMEM; + goto out; + } + module = sst_module_get_from_id(dsp, module_id); + if (module == NULL) { + dev_err(dev, "error: no module %d in firmware %s\n", + module_id, name); + } + dev_info(dev, "firmware %s loaded successfuly\n", name); + } else + dev_info(dev, "module %d (%s) already loaded\n", + module_id, name); +out: + /* release fw, but base fw should be released by acpi driver */ + if (fw && module_id != SST_HSW_MODULE_BASE_FW) + release_firmware(fw); + + return ret; +} + static struct sst_dsp_device hsw_dev = { .thread = hsw_irq_thread, .ops = &haswell_ops, @@ -1947,12 +2018,10 @@ int sst_hsw_dsp_init(struct device *dev, struct sst_pdata *pdata) /* keep the DSP in reset state for base FW loading */ sst_dsp_reset(hsw->dsp);
- hsw->sst_fw = sst_fw_new(hsw->dsp, pdata->fw, hsw); - if (hsw->sst_fw == NULL) { - ret = -ENODEV; - dev_err(dev, "error: failed to load firmware\n"); + /* load base module and other modules in base firmware image */ + ret = sst_hsw_module_load(hsw, SST_HSW_MODULE_BASE_FW, 0, "Base"); + if (ret < 0) goto fw_err; - }
/* allocate scratch mem regions */ ret = sst_block_alloc_scratch(hsw->dsp); @@ -1971,6 +2040,9 @@ int sst_hsw_dsp_init(struct device *dev, struct sst_pdata *pdata) goto boot_err; }
+ /* init module state after boot */ + sst_hsw_init_module_state(hsw); + /* get the FW version */ sst_hsw_fw_get_version(hsw, &version);
@@ -1986,7 +2058,7 @@ int sst_hsw_dsp_init(struct device *dev, struct sst_pdata *pdata)
boot_err: sst_dsp_reset(hsw->dsp); - sst_fw_free(hsw->sst_fw); + sst_fw_free_all(hsw->dsp); fw_err: dma_free_coherent(hsw->dsp->dma_dev, SST_HSW_DX_CONTEXT_SIZE, hsw->dx_context, hsw->dx_context_paddr); diff --git a/sound/soc/intel/sst-haswell-ipc.h b/sound/soc/intel/sst-haswell-ipc.h index 8580960..e071b3a 100644 --- a/sound/soc/intel/sst-haswell-ipc.h +++ b/sound/soc/intel/sst-haswell-ipc.h @@ -467,6 +467,12 @@ int sst_hsw_dsp_init(struct device *dev, struct sst_pdata *pdata); void sst_hsw_dsp_free(struct device *dev, struct sst_pdata *pdata); struct sst_dsp *sst_hsw_get_dsp(struct sst_hsw *hsw);
+/* fw module function */ +void sst_hsw_init_module_state(struct sst_hsw *hsw); + +int sst_hsw_module_load(struct sst_hsw *hsw, + u32 module_id, u32 instance_id, char *name); + /* runtime module management */ struct sst_module_runtime *sst_hsw_runtime_module_create(struct sst_hsw *hsw, int mod_id, int offset);
From: "Lu, Han" han.lu@intel.com
Try to load module waves and allocate runtime blocks for it if the firmware image of module waves exists.
Signed-off-by: Lu, Han han.lu@intel.com
diff --git a/sound/soc/intel/sst-haswell-dsp.c b/sound/soc/intel/sst-haswell-dsp.c index 8ad733b..b3e957d 100644 --- a/sound/soc/intel/sst-haswell-dsp.c +++ b/sound/soc/intel/sst-haswell-dsp.c @@ -100,6 +100,7 @@ static int hsw_parse_module(struct sst_dsp *dsp, struct sst_fw *fw, && module->type != SST_HSW_MODULE_PCM && module->type != SST_HSW_MODULE_PCM_REFERENCE && module->type != SST_HSW_MODULE_PCM_CAPTURE + && module->type != SST_HSW_MODULE_WAVES && module->type != SST_HSW_MODULE_LPAL) return 0;
@@ -139,6 +140,7 @@ static int hsw_parse_module(struct sst_dsp *dsp, struct sst_fw *fw, mod->type = SST_MEM_IRAM; break; case SST_HSW_DRAM: + case SST_HSW_REGS: ram = dsp->addr.lpe; mod->offset = block->ram_offset; mod->type = SST_MEM_DRAM; diff --git a/sound/soc/intel/sst-haswell-ipc.c b/sound/soc/intel/sst-haswell-ipc.c index cbd8c00..711439e 100644 --- a/sound/soc/intel/sst-haswell-ipc.c +++ b/sound/soc/intel/sst-haswell-ipc.c @@ -1896,11 +1896,27 @@ void sst_hsw_init_module_state(struct sst_hsw *hsw) /* the base fw contains several modules */ for (id = SST_HSW_MODULE_BASE_FW; id < SST_HSW_MAX_MODULE_ID; id++) { module = sst_module_get_from_id(hsw->dsp, id); - if (module) - module->state = SST_MODULE_STATE_ACTIVE; + if (module) { + /* module waves is active only after being enabled */ + if (id == SST_HSW_MODULE_WAVES) + module->state = SST_MODULE_STATE_INITIALIZED; + else + module->state = SST_MODULE_STATE_ACTIVE; + } } }
+bool sst_hsw_is_module_loaded(struct sst_hsw *hsw, u32 module_id) +{ + struct sst_module *module; + + module = sst_module_get_from_id(hsw->dsp, module_id); + if (module == NULL || module->state == SST_MODULE_STATE_UNLOADED) + return false; + else + return true; +} + int sst_hsw_module_load(struct sst_hsw *hsw, u32 module_id, u32 instance_id, char *name) { @@ -2023,6 +2039,9 @@ int sst_hsw_dsp_init(struct device *dev, struct sst_pdata *pdata) if (ret < 0) goto fw_err;
+ /* try to load module waves */ + sst_hsw_module_load(hsw, SST_HSW_MODULE_WAVES, 0, "intel/IntcPP01.bin"); + /* allocate scratch mem regions */ ret = sst_block_alloc_scratch(hsw->dsp); if (ret < 0) diff --git a/sound/soc/intel/sst-haswell-ipc.h b/sound/soc/intel/sst-haswell-ipc.h index e071b3a..208724b 100644 --- a/sound/soc/intel/sst-haswell-ipc.h +++ b/sound/soc/intel/sst-haswell-ipc.h @@ -469,6 +469,7 @@ struct sst_dsp *sst_hsw_get_dsp(struct sst_hsw *hsw);
/* fw module function */ void sst_hsw_init_module_state(struct sst_hsw *hsw); +bool sst_hsw_is_module_loaded(struct sst_hsw *hsw, u32 module_id);
int sst_hsw_module_load(struct sst_hsw *hsw, u32 module_id, u32 instance_id, char *name); diff --git a/sound/soc/intel/sst-haswell-pcm.c b/sound/soc/intel/sst-haswell-pcm.c index 7e21e8f..a604cc4 100644 --- a/sound/soc/intel/sst-haswell-pcm.c +++ b/sound/soc/intel/sst-haswell-pcm.c @@ -807,6 +807,17 @@ static int hsw_pcm_create_modules(struct hsw_priv_data *pdata) pcm_data->runtime->persistent_offset; }
+ /* create runtime blocks for module waves */ + if (sst_hsw_is_module_loaded(hsw, SST_HSW_MODULE_WAVES)) { + pcm_data = &pdata->pcm[HSW_PCM_COUNT-1][0]; + pcm_data->runtime = sst_hsw_runtime_module_create(hsw, + SST_HSW_MODULE_WAVES, pcm_data->persistent_offset); + if (pcm_data->runtime == NULL) + goto err; + pcm_data->persistent_offset = + pcm_data->runtime->persistent_offset; + } + return 0;
err: @@ -820,12 +831,16 @@ err:
static void hsw_pcm_free_modules(struct hsw_priv_data *pdata) { + struct sst_hsw *hsw = pdata->hsw; struct hsw_pcm_data *pcm_data; int i;
for (i = 0; i < ARRAY_SIZE(mod_map); i++) { pcm_data = &pdata->pcm[mod_map[i].dai_id][mod_map[i].stream]; - + sst_hsw_runtime_module_free(pcm_data->runtime); + } + if (sst_hsw_is_module_loaded(hsw, SST_HSW_MODULE_WAVES)) { + pcm_data = &pdata->pcm[HSW_PCM_COUNT-1][0]; sst_hsw_runtime_module_free(pcm_data->runtime); } } @@ -984,7 +999,9 @@ static int hsw_pcm_probe(struct snd_soc_platform *platform) }
/* allocate runtime modules */ - hsw_pcm_create_modules(priv_data); + ret = hsw_pcm_create_modules(priv_data); + if (ret < 0) + goto err;
/* enable runtime PM with auto suspend */ pm_runtime_set_autosuspend_delay(platform->dev,
From: "Lu, Han" han.lu@intel.com
Signed-off-by: Lu, Han han.lu@intel.com
diff --git a/sound/soc/intel/sst-haswell-ipc.c b/sound/soc/intel/sst-haswell-ipc.c index 711439e..2c15da2 100644 --- a/sound/soc/intel/sst-haswell-ipc.c +++ b/sound/soc/intel/sst-haswell-ipc.c @@ -79,6 +79,15 @@ #define IPC_LOG_ID_MASK (0xf << IPC_LOG_ID_SHIFT) #define IPC_LOG_ID(x) (x << IPC_LOG_ID_SHIFT)
+/* Module Message */ +#define IPC_MODULE_OPERATION_SHIFT 20 +#define IPC_MODULE_OPERATION_MASK (0xf << IPC_MODULE_OPERATION_SHIFT) +#define IPC_MODULE_OPERATION(x) (x << IPC_MODULE_OPERATION_SHIFT) + +#define IPC_MODULE_ID_SHIFT 16 +#define IPC_MODULE_ID_MASK (0xf << IPC_MODULE_ID_SHIFT) +#define IPC_MODULE_ID(x) (x << IPC_MODULE_ID_SHIFT) + /* IPC message timeout (msecs) */ #define IPC_TIMEOUT_MSECS 300 #define IPC_BOOT_MSECS 200 @@ -115,6 +124,7 @@ enum ipc_glb_type { IPC_GLB_ENTER_DX_STATE = 12, IPC_GLB_GET_MIXER_STREAM_INFO = 13, /* Request mixer stream params */ IPC_GLB_DEBUG_LOG_MESSAGE = 14, /* Message to or from the debug logger. */ + IPC_GLB_MODULE_OPERATION = 15, /* Message to loadable fw module */ IPC_GLB_REQUEST_TRANSFER = 16, /* < Request Transfer for host */ IPC_GLB_MAX_IPC_MESSAGE_TYPE = 17, /* Maximum message number */ }; @@ -133,6 +143,16 @@ enum ipc_glb_reply { IPC_GLB_REPLY_SOURCE_NOT_STARTED = 10, /* Source was not started. */ };
+enum ipc_module_operation { + IPC_MODULE_NOTIFICATION = 0, + IPC_MODULE_ENABLE = 1, + IPC_MODULE_DISABLE = 2, + IPC_MODULE_GET_PARAMETER = 3, + IPC_MODULE_SET_PARAMETER = 4, + IPC_MODULE_GET_INFO = 5, + IPC_MODULE_MAX_MESSAGE +}; + /* Stream Message - Types */ enum ipc_str_operation { IPC_STR_RESET = 0, @@ -352,6 +372,16 @@ static inline u32 msg_get_notify_reason(u32 msg) return (msg & IPC_STG_TYPE_MASK) >> IPC_STG_TYPE_SHIFT; }
+static inline u32 msg_get_module_operation(u32 msg) +{ + return (msg & IPC_MODULE_OPERATION_MASK) >> IPC_MODULE_OPERATION_SHIFT; +} + +static inline u32 msg_get_module_id(u32 msg) +{ + return (msg & IPC_MODULE_ID_MASK) >> IPC_MODULE_ID_SHIFT; +} + u32 create_channel_map(enum sst_hsw_channel_config config) { switch (config) { @@ -795,6 +825,31 @@ static int hsw_process_reply(struct sst_hsw *hsw, u32 header) return 1; }
+static int hsw_module_message(struct sst_hsw *hsw, u32 header) +{ + u32 operation, module_id; + int handled = 0; + + operation = msg_get_module_operation(header); + module_id = msg_get_module_id(header); + dev_dbg(hsw->dev, "received module message header: 0x%8.8x\n", + header); + dev_dbg(hsw->dev, "operation: 0x%8.8x module_id: 0x%8.8x\n", + operation, module_id); + + switch (operation) { + case IPC_MODULE_NOTIFICATION: + dev_info(hsw->dev, "module notification received"); + handled = 1; + break; + default: + handled = hsw_process_reply(hsw, header); + break; + } + + return handled; +} + static int hsw_stream_message(struct sst_hsw *hsw, u32 header) { u32 stream_msg, stream_id, stage_type; @@ -890,6 +945,9 @@ static int hsw_process_notification(struct sst_hsw *hsw) case IPC_GLB_DEBUG_LOG_MESSAGE: handled = hsw_log_message(hsw, header); break; + case IPC_GLB_MODULE_OPERATION: + handled = hsw_module_message(hsw, header); + break; default: dev_err(hsw->dev, "error: unexpected type %d hdr 0x%8.8x\n", type, header); @@ -1917,6 +1975,17 @@ bool sst_hsw_is_module_loaded(struct sst_hsw *hsw, u32 module_id) return true; }
+bool sst_hsw_is_module_active(struct sst_hsw *hsw, u32 module_id) +{ + struct sst_module *module; + + module = sst_module_get_from_id(hsw->dsp, module_id); + if (module != NULL && module->state == SST_MODULE_STATE_ACTIVE) + return true; + else + return false; +} + int sst_hsw_module_load(struct sst_hsw *hsw, u32 module_id, u32 instance_id, char *name) { @@ -1973,6 +2042,115 @@ out: return ret; }
+int sst_hsw_module_enable(struct sst_hsw *hsw, + u32 module_id, u32 instance_id) +{ + int ret; + u32 header = 0; + struct sst_hsw_ipc_module_config config; + struct sst_module *module; + struct sst_module_runtime *runtime; + struct device *dev = hsw->dev; + struct sst_dsp *dsp = hsw->dsp; + + if (!sst_hsw_is_module_loaded(hsw, module_id)) { + dev_dbg(dev, "module %d not loaded\n", module_id); + return 0; + } + + if (sst_hsw_is_module_active(hsw, module_id)) { + dev_info(dev, "module %d already enabled\n", module_id); + return 0; + } + + module = sst_module_get_from_id(dsp, module_id); + if (module == NULL) { + dev_err(dev, "module %d not valid\n", module_id); + return -ENXIO; + } + + runtime = sst_module_runtime_get_from_id(module, module_id); + if (runtime == NULL) { + dev_err(dev, "runtime %d not valid", module_id); + return -ENXIO; + } + + header = IPC_GLB_TYPE(IPC_GLB_MODULE_OPERATION) | + IPC_MODULE_OPERATION(IPC_MODULE_ENABLE) | + IPC_MODULE_ID(module_id); + dev_dbg(dev, "module enable header: %x\n", header); + + config.map.module_entries_count = 1; + config.map.module_entries[0].module_id = module->id; + config.map.module_entries[0].entry_point = module->entry; + + config.persistent_mem.offset = + sst_dsp_get_offset(dsp, + runtime->persistent_offset, SST_MEM_DRAM); + config.persistent_mem.size = module->persistent_size; + + config.scratch_mem.offset = + sst_dsp_get_offset(dsp, + dsp->scratch_offset, SST_MEM_DRAM); + config.scratch_mem.size = module->scratch_size; + dev_dbg(dev, "mod %d enable p:%d @ %x, s:%d @ %x, ep: %x", + config.map.module_entries[0].module_id, + config.persistent_mem.size, + config.persistent_mem.offset, + config.scratch_mem.size, config.scratch_mem.offset, + config.map.module_entries[0].entry_point); + + ret = ipc_tx_message_wait(hsw, header, + &config, sizeof(config), NULL, 0); + if (ret < 0) + dev_err(dev, "ipc: module enable failed - %d\n", ret); + else { + dev_info(dev, "ipc: module enable success\n"); + module->state = SST_MODULE_STATE_ACTIVE; + } + + return ret; +} + +int sst_hsw_module_disable(struct sst_hsw *hsw, + u32 module_id, u32 instance_id) +{ + int ret; + u32 header; + struct sst_module *module; + struct device *dev = hsw->dev; + struct sst_dsp *dsp = hsw->dsp; + + if (!sst_hsw_is_module_loaded(hsw, module_id)) { + dev_dbg(dev, "module %d not loaded\n", module_id); + return 0; + } + + if (!sst_hsw_is_module_active(hsw, module_id)) { + dev_info(dev, "module %d already disabled\n", module_id); + return 0; + } + + module = sst_module_get_from_id(dsp, module_id); + if (module == NULL) { + dev_err(dev, "module %d not valid\n", module_id); + return -ENXIO; + } + + header = IPC_GLB_TYPE(IPC_GLB_MODULE_OPERATION) | + IPC_MODULE_OPERATION(IPC_MODULE_DISABLE) | + IPC_MODULE_ID(module_id); + + ret = ipc_tx_message_wait(hsw, header, NULL, 0, NULL, 0); + if (ret < 0) + dev_err(dev, "module disable failed - %d\n", ret); + else { + dev_info(dev, "module %d is now disabled\n", module_id); + module->state = SST_MODULE_STATE_INITIALIZED; + } + return ret; +} + static struct sst_dsp_device hsw_dev = { .thread = hsw_irq_thread, .ops = &haswell_ops, diff --git a/sound/soc/intel/sst-haswell-ipc.h b/sound/soc/intel/sst-haswell-ipc.h index 208724b..30c65b2 100644 --- a/sound/soc/intel/sst-haswell-ipc.h +++ b/sound/soc/intel/sst-haswell-ipc.h @@ -215,6 +215,12 @@ struct sst_hsw_fx_enable { struct sst_hsw_memory_info persistent_mem; } __attribute__((packed));
+struct sst_hsw_ipc_module_config { + struct sst_hsw_module_map map; + struct sst_hsw_memory_info persistent_mem; + struct sst_hsw_memory_info scratch_mem; +} __attribute__((packed)); + struct sst_hsw_get_fx_param { u32 parameter_id; u32 param_size; @@ -470,9 +476,14 @@ struct sst_dsp *sst_hsw_get_dsp(struct sst_hsw *hsw); /* fw module function */ void sst_hsw_init_module_state(struct sst_hsw *hsw); bool sst_hsw_is_module_loaded(struct sst_hsw *hsw, u32 module_id); +bool sst_hsw_is_module_active(struct sst_hsw *hsw, u32 module_id);
int sst_hsw_module_load(struct sst_hsw *hsw, u32 module_id, u32 instance_id, char *name); +int sst_hsw_module_enable(struct sst_hsw *hsw, + u32 module_id, u32 instance_id); +int sst_hsw_module_disable(struct sst_hsw *hsw, + u32 module_id, u32 instance_id);
/* runtime module management */ struct sst_module_runtime *sst_hsw_runtime_module_create(struct sst_hsw *hsw,
From: "Lu, Han" han.lu@intel.com
Add kcontrol to enable/disable module waves. IPC is valid only when module is loaded.
Signed-off-by: Lu, Han han.lu@intel.com
diff --git a/sound/soc/intel/sst-haswell-pcm.c b/sound/soc/intel/sst-haswell-pcm.c index a604cc4..3d10acc 100644 --- a/sound/soc/intel/sst-haswell-pcm.c +++ b/sound/soc/intel/sst-haswell-pcm.c @@ -318,6 +318,42 @@ static int hsw_volume_get(struct snd_kcontrol *kcontrol, return 0; }
+static int hsw_waves_switch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_platform *platform = snd_soc_kcontrol_platform(kcontrol); + struct hsw_priv_data *pdata = snd_soc_platform_get_drvdata(platform); + struct sst_hsw *hsw = pdata->hsw; + enum sst_hsw_module_id id = SST_HSW_MODULE_WAVES; + + ucontrol->value.integer.value[0] = sst_hsw_is_module_active(hsw, id); + return 0; +} + +static int hsw_waves_switch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_platform *platform = snd_soc_kcontrol_platform(kcontrol); + struct hsw_priv_data *pdata = snd_soc_platform_get_drvdata(platform); + struct sst_hsw *hsw = pdata->hsw; + int ret = 0; + enum sst_hsw_module_id id = SST_HSW_MODULE_WAVES; + bool switch_on = (bool)ucontrol->value.integer.value[0]; + + if (sst_hsw_is_module_loaded(hsw, id)) { + if (switch_on == sst_hsw_is_module_active(hsw, id)) + return 0; + + /* ipc is valid only when module is loaded */ + if (switch_on) + ret = sst_hsw_module_enable(hsw, id, 0); + else + ret = sst_hsw_module_disable(hsw, id, 0); + } + + return ret; +} + /* TLV used by both global and stream volumes */ static const DECLARE_TLV_DB_SCALE(hsw_vol_tlv, -9000, 300, 1);
@@ -339,6 +375,9 @@ static const struct snd_kcontrol_new hsw_volume_controls[] = { SOC_DOUBLE_EXT_TLV("Mic Capture Volume", 4, 0, 8, ARRAY_SIZE(volume_map) - 1, 0, hsw_stream_volume_get, hsw_stream_volume_put, hsw_vol_tlv), + /* enable/disable module waves */ + SOC_SINGLE_BOOL_EXT("Waves Switch", 0, + hsw_waves_switch_get, hsw_waves_switch_put), };
/* Create DMA buffer page table for DSP */
From: "Lu, Han" han.lu@intel.com
Track module state over suspend so it's state can be restored on resume.
Signed-off-by: Lu, Han han.lu@intel.com
diff --git a/sound/soc/intel/sst-haswell-ipc.c b/sound/soc/intel/sst-haswell-ipc.c index 2c15da2..d6ccb46 100644 --- a/sound/soc/intel/sst-haswell-ipc.c +++ b/sound/soc/intel/sst-haswell-ipc.c @@ -337,6 +337,10 @@ struct sst_hsw {
/* FW log stream */ struct sst_hsw_log_stream log_stream; + + /* flags bit field to track module state when resume from RTD3, + * each bit represent state (enabled/disabled) of single module */ + u32 enabled_modules_rtd3; };
#define CREATE_TRACE_POINTS @@ -1986,6 +1990,21 @@ bool sst_hsw_is_module_active(struct sst_hsw *hsw, u32 module_id) return false; }
+void sst_hsw_set_module_enabled_rtd3(struct sst_hsw *hsw, u32 module_id) +{ + hsw->enabled_modules_rtd3 |= (1 << module_id); +} + +void sst_hsw_set_module_disabled_rtd3(struct sst_hsw *hsw, u32 module_id) +{ + hsw->enabled_modules_rtd3 &= ~(1 << module_id); +} + +bool sst_hsw_is_module_enabled_rtd3(struct sst_hsw *hsw, u32 module_id) +{ + return hsw->enabled_modules_rtd3 & (1 << module_id); +} + int sst_hsw_module_load(struct sst_hsw *hsw, u32 module_id, u32 instance_id, char *name) { diff --git a/sound/soc/intel/sst-haswell-ipc.h b/sound/soc/intel/sst-haswell-ipc.h index 30c65b2..48290a1 100644 --- a/sound/soc/intel/sst-haswell-ipc.h +++ b/sound/soc/intel/sst-haswell-ipc.h @@ -477,6 +477,9 @@ struct sst_dsp *sst_hsw_get_dsp(struct sst_hsw *hsw); void sst_hsw_init_module_state(struct sst_hsw *hsw); bool sst_hsw_is_module_loaded(struct sst_hsw *hsw, u32 module_id); bool sst_hsw_is_module_active(struct sst_hsw *hsw, u32 module_id); +void sst_hsw_set_module_enabled_rtd3(struct sst_hsw *hsw, u32 module_id); +void sst_hsw_set_module_disabled_rtd3(struct sst_hsw *hsw, u32 module_id); +bool sst_hsw_is_module_enabled_rtd3(struct sst_hsw *hsw, u32 module_id);
int sst_hsw_module_load(struct sst_hsw *hsw, u32 module_id, u32 instance_id, char *name); diff --git a/sound/soc/intel/sst-haswell-pcm.c b/sound/soc/intel/sst-haswell-pcm.c index 3d10acc..f82560f 100644 --- a/sound/soc/intel/sst-haswell-pcm.c +++ b/sound/soc/intel/sst-haswell-pcm.c @@ -326,7 +326,9 @@ static int hsw_waves_switch_get(struct snd_kcontrol *kcontrol, struct sst_hsw *hsw = pdata->hsw; enum sst_hsw_module_id id = SST_HSW_MODULE_WAVES;
- ucontrol->value.integer.value[0] = sst_hsw_is_module_active(hsw, id); + ucontrol->value.integer.value[0] = + (sst_hsw_is_module_active(hsw, id) || + sst_hsw_is_module_enabled_rtd3(hsw, id)); return 0; }
@@ -349,6 +351,16 @@ static int hsw_waves_switch_put(struct snd_kcontrol *kcontrol, ret = sst_hsw_module_enable(hsw, id, 0); else ret = sst_hsw_module_disable(hsw, id, 0); + } else { + /* on RTD3, module is unloaded */ + if (switch_on == sst_hsw_is_module_enabled_rtd3(hsw, id)) + return 0; + + /* set flag for track */ + if (switch_on) + sst_hsw_set_module_enabled_rtd3(hsw, id); + else + sst_hsw_set_module_disabled_rtd3(hsw, id); }
return ret; @@ -1157,10 +1169,18 @@ static int hsw_pcm_runtime_suspend(struct device *dev) { struct hsw_priv_data *pdata = dev_get_drvdata(dev); struct sst_hsw *hsw = pdata->hsw; + int ret;
if (pdata->pm_state >= HSW_PM_STATE_RTD3) return 0;
+ /* fw modules will be unloaded on RTD3, set flag to trace */ + if (sst_hsw_is_module_active(hsw, SST_HSW_MODULE_WAVES)) { + ret = sst_hsw_module_disable(hsw, SST_HSW_MODULE_WAVES, 0); + if (ret < 0) + return ret; + sst_hsw_set_module_enabled_rtd3(hsw, SST_HSW_MODULE_WAVES); + } sst_hsw_dsp_runtime_suspend(hsw); sst_hsw_dsp_runtime_sleep(hsw); pdata->pm_state = HSW_PM_STATE_RTD3; @@ -1195,6 +1215,15 @@ static int hsw_pcm_runtime_resume(struct device *dev) else if (ret == 1) /* no action required */ return 0;
+ /* check flag when resume */ + if (sst_hsw_is_module_enabled_rtd3(hsw, SST_HSW_MODULE_WAVES)) { + ret = sst_hsw_module_enable(hsw, SST_HSW_MODULE_WAVES, 0); + if (ret < 0) + return ret; + /* unset flag */ + sst_hsw_set_module_disabled_rtd3(hsw, SST_HSW_MODULE_WAVES); + } + pdata->pm_state = HSW_PM_STATE_D0; return ret; }
From: "Lu, Han" han.lu@intel.com
Add function to set parameters to module waves. The parameters can be set only when module is enabled, and parameter size is limited to 500 Bytes.
Signed-off-by: Lu, Han han.lu@intel.com
diff --git a/sound/soc/intel/sst-haswell-ipc.c b/sound/soc/intel/sst-haswell-ipc.c index d6ccb46..7dda402 100644 --- a/sound/soc/intel/sst-haswell-ipc.c +++ b/sound/soc/intel/sst-haswell-ipc.c @@ -2170,6 +2170,67 @@ int sst_hsw_module_disable(struct sst_hsw *hsw, return ret; }
+int sst_hsw_module_set_param(struct sst_hsw *hsw, + u32 module_id, u32 instance_id, u32 parameter_id, + u32 param_size, char *param) +{ + int ret; + unsigned char *data = NULL; + u32 header = 0; + u32 payload_size = 0, transfer_parameter_size = 0; + dma_addr_t dma_addr = 0; + struct sst_hsw_transfer_parameter *parameter; + struct device *dev = hsw->dev; + + header = IPC_GLB_TYPE(IPC_GLB_MODULE_OPERATION) | + IPC_MODULE_OPERATION(IPC_MODULE_SET_PARAMETER) | + IPC_MODULE_ID(module_id); + dev_dbg(dev, "sst_hsw_module_set_param header=%x\n", header); + + payload_size = param_size + + sizeof(struct sst_hsw_transfer_parameter) - + sizeof(struct sst_hsw_transfer_list); + dev_dbg(dev, "parameter size : %d\n", param_size); + dev_dbg(dev, "payload size : %d\n", payload_size); + + if (payload_size <= SST_HSW_IPC_MAX_SHORT_PARAMETER_SIZE) { + /* short parameter, mailbox can contain data */ + dev_dbg(dev, "transfer parameter size : %d\n", + transfer_parameter_size); + + transfer_parameter_size = ALIGN(payload_size, 4); + dev_dbg(dev, "transfer parameter aligned size : %d\n", + transfer_parameter_size); + + parameter = kzalloc(transfer_parameter_size, GFP_KERNEL); + if (parameter == NULL) + return -ENOMEM; + + memcpy(parameter->data, param, param_size); + } else { + dev_warn(dev, "transfer parameter size too large!"); + return 0; + } + + parameter->parameter_id = parameter_id; + parameter->data_size = param_size; + + ret = ipc_tx_message_wait(hsw, header, + parameter, transfer_parameter_size , NULL, 0); + if (ret < 0) + dev_err(dev, "ipc: module set parameter failed - %d\n", ret); + else + dev_info(dev, "ipc: module set parameter success\n"); + + kfree(parameter); + + if (data) + dma_free_coherent(hsw->dsp->dma_dev, + param_size, (void *)data, dma_addr); + + return ret; +} + static struct sst_dsp_device hsw_dev = { .thread = hsw_irq_thread, .ops = &haswell_ops, diff --git a/sound/soc/intel/sst-haswell-ipc.h b/sound/soc/intel/sst-haswell-ipc.h index 48290a1..16bec43 100644 --- a/sound/soc/intel/sst-haswell-ipc.h +++ b/sound/soc/intel/sst-haswell-ipc.h @@ -37,6 +37,7 @@ #define SST_HSW_IPC_MAX_PAYLOAD_SIZE 400 #define SST_HSW_MAX_INFO_SIZE 64 #define SST_HSW_BUILD_HASH_LENGTH 40 +#define SST_HSW_IPC_MAX_SHORT_PARAMETER_SIZE 500
struct sst_hsw; struct sst_hsw_stream; @@ -187,6 +188,28 @@ enum sst_hsw_performance_action { SST_HSW_PERF_STOP = 1, };
+struct sst_hsw_transfer_info { + uint32_t destination; /* destination address */ + uint32_t reverse:1; /* if 1 data flows from destination */ + uint32_t size:31; /* transfer size in bytes.*/ + uint16_t first_page_offset; /* offset to data in the first page. */ + uint8_t packed_pages; /* page addresses. Each occupies 20 bits */ +} __attribute__((packed)); + +struct sst_hsw_transfer_list { + uint32_t transfers_count; + struct sst_hsw_transfer_info transfers; +} __attribute__((packed)); + +struct sst_hsw_transfer_parameter { + uint32_t parameter_id; + uint32_t data_size; + union { + uint8_t data[1]; + struct sst_hsw_transfer_list transfer_list; + }; +} __attribute__((packed)); + /* SST firmware module info */ struct sst_hsw_module_info { u8 name[SST_HSW_MAX_INFO_SIZE]; @@ -487,6 +510,9 @@ int sst_hsw_module_enable(struct sst_hsw *hsw, u32 module_id, u32 instance_id); int sst_hsw_module_disable(struct sst_hsw *hsw, u32 module_id, u32 instance_id); +int sst_hsw_module_set_param(struct sst_hsw *hsw, + u32 module_id, u32 instance_id, u32 parameter_id, + u32 param_size, char *param);
/* runtime module management */ struct sst_module_runtime *sst_hsw_runtime_module_create(struct sst_hsw *hsw,
On Mon, Mar 02, 2015 at 03:36:16PM +0800, han.lu@intel.com wrote:
- if (ret < 0)
dev_err(dev, "ipc: module set parameter failed - %d\n", ret);
- else
dev_info(dev, "ipc: module set parameter success\n");
Logging on error is good but logging every time someone sets a control is going to get too noisy, please remove the print there.
From: "Lu, Han" han.lu@intel.com
Each kcontrol command includes a line of parameters up to 128 bytes. kcontrol command to set param: cset "name='Waves Set Param' <0x01,0xff,...>" or cset-bin-file "name='Waves Set Param' <path/to/binary/config/file>" alsa-lib commit 6ea14c36 and f47480af are required for listed format.
Signed-off-by: Lu, Han han.lu@intel.com
diff --git a/sound/soc/intel/sst-haswell-ipc.h b/sound/soc/intel/sst-haswell-ipc.h index 16bec43..737f206 100644 --- a/sound/soc/intel/sst-haswell-ipc.h +++ b/sound/soc/intel/sst-haswell-ipc.h @@ -38,6 +38,7 @@ #define SST_HSW_MAX_INFO_SIZE 64 #define SST_HSW_BUILD_HASH_LENGTH 40 #define SST_HSW_IPC_MAX_SHORT_PARAMETER_SIZE 500 +#define WAVES_PARAM_COUNT 128
struct sst_hsw; struct sst_hsw_stream; diff --git a/sound/soc/intel/sst-haswell-pcm.c b/sound/soc/intel/sst-haswell-pcm.c index f82560f..e69b364 100644 --- a/sound/soc/intel/sst-haswell-pcm.c +++ b/sound/soc/intel/sst-haswell-pcm.c @@ -366,6 +366,33 @@ static int hsw_waves_switch_put(struct snd_kcontrol *kcontrol, return ret; }
+static int hsw_waves_param_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + /* keep an empty function here to match alsa-lib calling */ + return 0; +} + +static int hsw_waves_param_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_platform *platform = snd_soc_kcontrol_platform(kcontrol); + struct hsw_priv_data *pdata = snd_soc_platform_get_drvdata(platform); + struct sst_hsw *hsw = pdata->hsw; + int ret; + enum sst_hsw_module_id id = SST_HSW_MODULE_WAVES; + int param_id = ucontrol->value.bytes.data[0]; + int param_size = WAVES_PARAM_COUNT; + + if (sst_hsw_is_module_loaded(hsw, id)) { + if (!sst_hsw_is_module_active(hsw, id)) + return 0; + ret = sst_hsw_module_set_param(hsw, id, 0, param_id, + param_size, ucontrol->value.bytes.data); + } + return ret; +} + /* TLV used by both global and stream volumes */ static const DECLARE_TLV_DB_SCALE(hsw_vol_tlv, -9000, 300, 1);
@@ -390,6 +417,9 @@ static const struct snd_kcontrol_new hsw_volume_controls[] = { /* enable/disable module waves */ SOC_SINGLE_BOOL_EXT("Waves Switch", 0, hsw_waves_switch_get, hsw_waves_switch_put), + /* set parameters to module waves */ + SND_SOC_BYTES_EXT("Waves Set Param", WAVES_PARAM_COUNT, + hsw_waves_param_get, hsw_waves_param_put), };
/* Create DMA buffer page table for DSP */
On Mon, Mar 02, 2015 at 03:36:17PM +0800, han.lu@intel.com wrote:
+static int hsw_waves_param_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
+{
- /* keep an empty function here to match alsa-lib calling */
- return 0;
+}
No, if it's valid to omit this we should be able to remove the function - but I'd really expect to always be able to read the current state of any control. Applications should be able to do that and then write the value back without changing anything. Either we should actually read things back or this should return an error (which is presumably what the core does if the function is missing).
-----Original Message----- From: Mark Brown [mailto:broonie@kernel.org] Sent: Friday, March 6, 2015 7:35 PM To: Lu, Han Cc: alsa-devel@alsa-project.org; Girdwood, Liam R Subject: Re: [PATCH 7/8] ASoC: Intel: add kcontrol to set parameter to sound effect module waves
On Mon, Mar 02, 2015 at 03:36:17PM +0800, han.lu@intel.com wrote:
+static int hsw_waves_param_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol) {
- /* keep an empty function here to match alsa-lib calling */
- return 0;
+}
No, if it's valid to omit this we should be able to remove the function
- but I'd really expect to always be able to read the current state of any
control. Applications should be able to do that and then write the value back without changing anything. Either we should actually read things back or this should return an error (which is presumably what the core does if the function is missing).
[Han Lu] OK. I made patch v2 against current code, and remove unnecessary logs. For the read back kcontrol, I do not modify patch 7/8 but add actual read back code on patch 8/8, (merge patch 7 and 8 may make the patch too long and unclear?) please review. Thanks.
From: "Lu, Han" han.lu@intel.com
The parameters in DSP will lost on RTD3. To fix this, store parameter lines in a buffer, and put the buffer content to DSP when resume from RTD3. The size of buffer is 160 parameter lines. And add kcontrol command to reset the buffer: cset "name='Waves Set Param' 0xff"
Signed-off-by: Lu, Han han.lu@intel.com
diff --git a/sound/soc/intel/sst-haswell-ipc.c b/sound/soc/intel/sst-haswell-ipc.c index 7dda402..df19d6c 100644 --- a/sound/soc/intel/sst-haswell-ipc.c +++ b/sound/soc/intel/sst-haswell-ipc.c @@ -341,6 +341,10 @@ struct sst_hsw { /* flags bit field to track module state when resume from RTD3, * each bit represent state (enabled/disabled) of single module */ u32 enabled_modules_rtd3; + + /* buffer to store parameter lines */ + u32 param_idx; + u8 param_buf[WAVES_PARAM_LINES][WAVES_PARAM_COUNT]; };
#define CREATE_TRACE_POINTS @@ -2005,6 +2009,37 @@ bool sst_hsw_is_module_enabled_rtd3(struct sst_hsw *hsw, u32 module_id) return hsw->enabled_modules_rtd3 & (1 << module_id); }
+void sst_hsw_reset_param_buf(struct sst_hsw *hsw) +{ + hsw->param_idx = 0; + memset((void *)hsw->param_buf, 0, sizeof(hsw->param_buf)); +} + +int sst_hsw_store_param_line(struct sst_hsw *hsw, u8 *buf) +{ + if (hsw->param_idx > WAVES_PARAM_LINES - 1) { + dev_warn(hsw->dev, "warning: param buffer overflow!\n"); + return 0; + } + memcpy(hsw->param_buf[hsw->param_idx], buf, WAVES_PARAM_COUNT); + hsw->param_idx++; + return 0; +} + +int sst_hsw_launch_param_buf(struct sst_hsw *hsw) +{ + int ret, idx; + + for (idx = 0; idx < hsw->param_idx; idx++) { + ret = sst_hsw_module_set_param(hsw, + SST_HSW_MODULE_WAVES, 0, hsw->param_buf[idx][0], + WAVES_PARAM_COUNT, hsw->param_buf[idx]); + if (ret < 0) + return ret; + } + return 0; +} + int sst_hsw_module_load(struct sst_hsw *hsw, u32 module_id, u32 instance_id, char *name) { @@ -2305,6 +2340,9 @@ int sst_hsw_dsp_init(struct device *dev, struct sst_pdata *pdata) if (ret < 0) goto boot_err;
+ /* init param buffer */ + sst_hsw_reset_param_buf(hsw); + /* wait for DSP boot completion */ sst_dsp_boot(hsw->dsp); ret = wait_event_timeout(hsw->boot_wait, hsw->boot_complete, diff --git a/sound/soc/intel/sst-haswell-ipc.h b/sound/soc/intel/sst-haswell-ipc.h index 737f206..52fdea0 100644 --- a/sound/soc/intel/sst-haswell-ipc.h +++ b/sound/soc/intel/sst-haswell-ipc.h @@ -39,6 +39,7 @@ #define SST_HSW_BUILD_HASH_LENGTH 40 #define SST_HSW_IPC_MAX_SHORT_PARAMETER_SIZE 500 #define WAVES_PARAM_COUNT 128 +#define WAVES_PARAM_LINES 160
struct sst_hsw; struct sst_hsw_stream; @@ -504,6 +505,9 @@ bool sst_hsw_is_module_active(struct sst_hsw *hsw, u32 module_id); void sst_hsw_set_module_enabled_rtd3(struct sst_hsw *hsw, u32 module_id); void sst_hsw_set_module_disabled_rtd3(struct sst_hsw *hsw, u32 module_id); bool sst_hsw_is_module_enabled_rtd3(struct sst_hsw *hsw, u32 module_id); +void sst_hsw_reset_param_buf(struct sst_hsw *hsw); +int sst_hsw_store_param_line(struct sst_hsw *hsw, u8 *buf); +int sst_hsw_launch_param_buf(struct sst_hsw *hsw);
int sst_hsw_module_load(struct sst_hsw *hsw, u32 module_id, u32 instance_id, char *name); diff --git a/sound/soc/intel/sst-haswell-pcm.c b/sound/soc/intel/sst-haswell-pcm.c index e69b364..4ba684b 100644 --- a/sound/soc/intel/sst-haswell-pcm.c +++ b/sound/soc/intel/sst-haswell-pcm.c @@ -384,6 +384,17 @@ static int hsw_waves_param_put(struct snd_kcontrol *kcontrol, int param_id = ucontrol->value.bytes.data[0]; int param_size = WAVES_PARAM_COUNT;
+ /* clear param buffer and reset buffer index */ + if (param_id == 0xFF) { + sst_hsw_reset_param_buf(hsw); + return 0; + } + + /* store params into buffer */ + ret = sst_hsw_store_param_line(hsw, ucontrol->value.bytes.data); + if (ret < 0) + return ret; + if (sst_hsw_is_module_loaded(hsw, id)) { if (!sst_hsw_is_module_active(hsw, id)) return 0; @@ -1250,6 +1261,10 @@ static int hsw_pcm_runtime_resume(struct device *dev) ret = sst_hsw_module_enable(hsw, SST_HSW_MODULE_WAVES, 0); if (ret < 0) return ret; + /* put parameters from buffer to dsp */ + ret = sst_hsw_launch_param_buf(hsw); + if (ret < 0) + return ret; /* unset flag */ sst_hsw_set_module_disabled_rtd3(hsw, SST_HSW_MODULE_WAVES); }
On Mon, Mar 02, 2015 at 03:36:11PM +0800, han.lu@intel.com wrote:
From: "Lu, Han" han.lu@intel.com
Add a general method to load firmware image, and apply to base firmware image loading. With the method, the driver will support loading multiple different modules in order to support different features.
This doesn't apply against current code, please check and resend.
participants (3)
-
han.lu@intel.com
-
Lu, Han
-
Mark Brown