From d30ba6493d1897d89ea1995d3406063e94981ab4 Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Tue, 12 Feb 2019 20:39:26 -0800 Subject: [PATCH 1/5] ASoC: SOF: topology: Improve handling of processing components Align all processing components to use the same IPC component constructor and bump up the ABI. Also, de-duplicate FIR and IIR handling code into one function that can deal with multiple processing component types. Signed-off-by: Liam Girdwood Signed-off-by: Ranjani Sridharan --- include/sound/sof/topology.h | 37 +--- include/uapi/sound/sof/abi.h | 2 +- include/uapi/sound/sof/tokens.h | 6 +- sound/soc/sof/topology.c | 316 ++++++++++++++++---------------- 4 files changed, 173 insertions(+), 188 deletions(-) diff --git a/include/sound/sof/topology.h b/include/sound/sof/topology.h index b60a6026df3aac..5cafee2ebddbcd 100644 --- a/include/sound/sof/topology.h +++ b/include/sound/sof/topology.h @@ -163,39 +163,22 @@ struct sof_ipc_comp_tone { int32_t ramp_step; } __packed; -/** \brief Types of EFFECT */ -enum sof_ipc_effect_type { - SOF_EFFECT_NONE = 0, /**< None */ - SOF_EFFECT_INTEL_EQFIR, /**< Intel FIR */ - SOF_EFFECT_INTEL_EQIIR, /**< Intel IIR */ +/** \brief Types of processing components */ +enum sof_ipc_process_type { + SOF_PROCESS_NONE = 0, /**< None */ + SOF_PROCESS_EQFIR, /**< Intel FIR */ + SOF_PROCESS_EQIIR, /**< Intel IIR */ }; -/* general purpose EFFECT configuration */ -struct sof_ipc_comp_effect { - struct sof_ipc_hdr hdr; - uint32_t type; /** sof_ipc_effect_type */ -} __packed; - -/* FIR equalizer component */ -struct sof_ipc_comp_eq_fir { - struct sof_ipc_comp comp; - struct sof_ipc_comp_config config; - uint32_t size; - - /* reserved for future use */ - uint32_t reserved[8]; - - unsigned char data[0]; -} __packed; - -/* IIR equalizer component */ -struct sof_ipc_comp_eq_iir { +/* generic "effect", "codec" or proprietary processing component */ +struct sof_ipc_comp_process { struct sof_ipc_comp comp; struct sof_ipc_comp_config config; - uint32_t size; + uint32_t size; /**< size of bespoke data section in bytes */ + uint32_t type; /**< sof_ipc_process_type */ /* reserved for future use */ - uint32_t reserved[8]; + uint32_t reserved[7]; unsigned char data[0]; } __packed; diff --git a/include/uapi/sound/sof/abi.h b/include/uapi/sound/sof/abi.h index 98da1236cd66b3..37e0a90dc9e6c3 100644 --- a/include/uapi/sound/sof/abi.h +++ b/include/uapi/sound/sof/abi.h @@ -26,7 +26,7 @@ /* SOF ABI version major, minor and patch numbers */ #define SOF_ABI_MAJOR 3 -#define SOF_ABI_MINOR 3 +#define SOF_ABI_MINOR 4 #define SOF_ABI_PATCH 0 /* SOF ABI version number. Format within 32bit word is MMmmmppp */ diff --git a/include/uapi/sound/sof/tokens.h b/include/uapi/sound/sof/tokens.h index 8c39da56d89140..53ea94bf1c08d3 100644 --- a/include/uapi/sound/sof/tokens.h +++ b/include/uapi/sound/sof/tokens.h @@ -98,6 +98,10 @@ /* Tone */ #define SOF_TKN_TONE_SAMPLE_RATE 800 -#define SOF_TKN_EFFECT_TYPE 900 +/* Processing Components */ +#define SOF_TKN_PROCESS_TYPE 900 + +/* for backward compatibility */ +#define SOF_TKN_EFFECT_TYPE SOF_TKN_PROCESS_TYPE #endif diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index f4c01a5af18f6c..2d1ca0180aeca3 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -235,26 +235,39 @@ static enum sof_ipc_frame find_format(const char *name) return SOF_IPC_FRAME_S32_LE; } -struct sof_effect_types { +struct sof_process_types { const char *name; - enum sof_ipc_effect_type type; + enum sof_ipc_process_type type; + enum sof_comp_type comp_type; }; -static const struct sof_effect_types sof_effects[] = { - {"EQFIR", SOF_EFFECT_INTEL_EQFIR}, - {"EQIIR", SOF_EFFECT_INTEL_EQIIR}, +static const struct sof_process_types sof_process[] = { + {"EQFIR", SOF_PROCESS_EQFIR, SOF_COMP_EQ_FIR}, + {"EQIIR", SOF_PROCESS_EQIIR, SOF_COMP_EQ_IIR}, }; -static enum sof_ipc_effect_type find_effect(const char *name) +static enum sof_ipc_process_type find_process(const char *name) { int i; - for (i = 0; i < ARRAY_SIZE(sof_effects); i++) { - if (strcmp(name, sof_effects[i].name) == 0) - return sof_effects[i].type; + for (i = 0; i < ARRAY_SIZE(sof_process); i++) { + if (strcmp(name, sof_process[i].name) == 0) + return sof_process[i].type; } - return SOF_EFFECT_NONE; + return SOF_PROCESS_NONE; +} + +static enum sof_comp_type find_process_comp_type(enum sof_ipc_process_type type) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(sof_process); i++) { + if (sof_process[i].type == type) + return sof_process[i].comp_type; + } + + return SOF_COMP_NONE; } /* @@ -463,12 +476,13 @@ static int get_token_dai_type(void *elem, void *object, u32 offset, u32 size) return 0; } -static int get_token_effect_type(void *elem, void *object, u32 offset, u32 size) +static int get_token_process_type(void *elem, void *object, u32 offset, + u32 size) { struct snd_soc_tplg_vendor_string_elem *velem = elem; u32 *val = (u32 *)((u8 *)object + offset); - *val = find_effect(velem->string); + *val = find_process(velem->string); return 0; } @@ -536,10 +550,10 @@ static const struct sof_topology_token tone_tokens[] = { }; /* EFFECT */ -static const struct sof_topology_token effect_tokens[] = { - {SOF_TKN_EFFECT_TYPE, SND_SOC_TPLG_TUPLE_TYPE_STRING, - get_token_effect_type, - offsetof(struct sof_ipc_comp_effect, type), 0}, +static const struct sof_topology_token process_tokens[] = { + {SOF_TKN_PROCESS_TYPE, SND_SOC_TPLG_TUPLE_TYPE_STRING, + get_token_process_type, + offsetof(struct sof_ipc_comp_process, type), 0}, }; /* PCM */ @@ -1557,212 +1571,195 @@ static int sof_widget_load_siggen(struct snd_soc_component *scomp, int index, return ret; } -static int sof_effect_fir_load(struct snd_soc_component *scomp, int index, - struct snd_sof_widget *swidget, - struct snd_soc_tplg_dapm_widget *tw, - struct sof_ipc_comp_reply *r) - +static int sof_process_load(struct snd_soc_component *scomp, int index, + struct snd_sof_widget *swidget, + struct snd_soc_tplg_dapm_widget *tw, + struct sof_ipc_comp_reply *r, + int type) { struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); struct snd_soc_tplg_private *private = &tw->priv; - struct snd_sof_control *scontrol = NULL; struct snd_soc_dapm_widget *widget = swidget->widget; const struct snd_kcontrol_new *kc = NULL; struct soc_bytes_ext *sbe; + struct soc_mixer_control *sm; + struct soc_enum *se; + struct snd_sof_control *scontrol = NULL; struct sof_abi_hdr *pdata = NULL; - struct sof_ipc_comp_eq_fir *fir; - size_t ipc_size = 0, fir_data_size = 0; - int ret; + struct sof_ipc_comp_process *process; + size_t ipc_size = 0, ipc_data_size = 0; + int ret, i, offset = 0; - /* get possible eq controls */ - kc = &widget->kcontrol_news[0]; - if (kc) { - sbe = (struct soc_bytes_ext *)kc->private_value; - scontrol = sbe->dobj.private; + if (type == SOF_COMP_NONE) { + dev_err(sdev->dev, "error: invalid process comp type %d\n", + type); + return -EINVAL; } /* - * Check if there's eq parameters in control's private member and set - * data size accordingly. If there's no parameters eq will use defaults - * in firmware (which in this case is passthrough). + * get possible component controls - get size of all pdata, + * then memcpy with headers */ - if (scontrol && scontrol->cmd == SOF_CTRL_CMD_BINARY) { - pdata = scontrol->control_data->data; - if (pdata->size > 0 && pdata->magic == SOF_ABI_MAGIC) - fir_data_size = pdata->size; - } - - ipc_size = sizeof(struct sof_ipc_comp_eq_fir) + - le32_to_cpu(private->size) + - fir_data_size; - - fir = kzalloc(ipc_size, GFP_KERNEL); - if (!fir) - return -ENOMEM; - - /* configure fir IPC message */ - fir->comp.hdr.size = ipc_size; - fir->comp.hdr.cmd = SOF_IPC_GLB_TPLG_MSG | SOF_IPC_TPLG_COMP_NEW; - fir->comp.id = swidget->comp_id; - fir->comp.type = SOF_COMP_EQ_FIR; - fir->comp.pipeline_id = index; - fir->config.hdr.size = sizeof(fir->config); - - ret = sof_parse_tokens(scomp, &fir->config, comp_tokens, - ARRAY_SIZE(comp_tokens), private->array, - le32_to_cpu(private->size)); - if (ret != 0) { - dev_err(sdev->dev, "error: parse fir.cfg tokens failed %d\n", - le32_to_cpu(private->size)); - goto err; - } - - sof_dbg_comp_config(scomp, &fir->config); + for (i = 0; i < widget->num_kcontrols; i++) { - /* we have a private data found in control, so copy it */ - if (fir_data_size > 0) { - memcpy(&fir->data, pdata->data, pdata->size); - fir->size = fir_data_size; - } + kc = &widget->kcontrol_news[i]; - swidget->private = (void *)fir; + switch (widget->dobj.widget.kcontrol_type) { + case SND_SOC_TPLG_TYPE_MIXER: + sm = (struct soc_mixer_control *)kc->private_value; + scontrol = sm->dobj.private; + break; + case SND_SOC_TPLG_TYPE_BYTES: + sbe = (struct soc_bytes_ext *)kc->private_value; + scontrol = sbe->dobj.private; + break; + case SND_SOC_TPLG_TYPE_ENUM: + se = (struct soc_enum *)kc->private_value; + scontrol = se->dobj.private; + break; + default: + dev_err(sdev->dev, "error: unknown kcontrol type %d in widget %s\n", + widget->dobj.widget.kcontrol_type, + widget->name); + return -EINVAL; + } - ret = sof_ipc_tx_message(sdev->ipc, fir->comp.hdr.cmd, fir, - ipc_size, r, sizeof(*r)); - if (ret >= 0) - return ret; -err: - kfree(fir); - return ret; -} + if (!scontrol) { + dev_err(sdev->dev, "error: no scontrol for widget %s\n", + widget->name); + return -EINVAL; + } -static int sof_effect_iir_load(struct snd_soc_component *scomp, int index, - struct snd_sof_widget *swidget, - struct snd_soc_tplg_dapm_widget *tw, - struct sof_ipc_comp_reply *r) -{ - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); - struct snd_soc_tplg_private *private = &tw->priv; - struct snd_soc_dapm_widget *widget = swidget->widget; - const struct snd_kcontrol_new *kc = NULL; - struct soc_bytes_ext *sbe; - struct snd_sof_control *scontrol = NULL; - struct sof_abi_hdr *pdata = NULL; - struct sof_ipc_comp_eq_iir *iir; - size_t ipc_size = 0, iir_data_size = 0; - int ret; + /* don't include if no private data */ + pdata = scontrol->control_data->data; + if (!pdata) + continue; - /* get possible eq controls */ - kc = &widget->kcontrol_news[0]; - if (kc) { - sbe = (struct soc_bytes_ext *)kc->private_value; - scontrol = sbe->dobj.private; - } + /* make sure data is valid - data can be updated at runtime */ + if (pdata->magic != SOF_ABI_MAGIC) + continue; - /* - * Check if there's eq parameters in control's private member and set - * data size accordingly. If there's no parameters eq will use defaults - * in firmware (which in this case is passthrough). - */ - if (scontrol && scontrol->cmd == SOF_CTRL_CMD_BINARY) { - pdata = scontrol->control_data->data; - if (pdata->size > 0 && pdata->magic == SOF_ABI_MAGIC) - iir_data_size = pdata->size; + ipc_data_size += pdata->size; } - ipc_size = sizeof(struct sof_ipc_comp_eq_iir) + + ipc_size = sizeof(struct sof_ipc_comp_process) + le32_to_cpu(private->size) + - iir_data_size; + ipc_data_size; - iir = kzalloc(ipc_size, GFP_KERNEL); - if (!iir) + process = kzalloc(ipc_size, GFP_KERNEL); + if (!process) return -ENOMEM; /* configure iir IPC message */ - iir->comp.hdr.size = ipc_size; - iir->comp.hdr.cmd = SOF_IPC_GLB_TPLG_MSG | SOF_IPC_TPLG_COMP_NEW; - iir->comp.id = swidget->comp_id; - iir->comp.type = SOF_COMP_EQ_IIR; - iir->comp.pipeline_id = index; - iir->config.hdr.size = sizeof(iir->config); - - ret = sof_parse_tokens(scomp, &iir->config, comp_tokens, + process->comp.hdr.size = ipc_size; + process->comp.hdr.cmd = SOF_IPC_GLB_TPLG_MSG | SOF_IPC_TPLG_COMP_NEW; + process->comp.id = swidget->comp_id; + process->comp.type = type; + process->comp.pipeline_id = index; + process->config.hdr.size = sizeof(process->config); + + ret = sof_parse_tokens(scomp, &process->config, comp_tokens, ARRAY_SIZE(comp_tokens), private->array, le32_to_cpu(private->size)); if (ret != 0) { - dev_err(sdev->dev, "error: parse iir.cfg tokens failed %d\n", + dev_err(sdev->dev, "error: parse process.cfg tokens failed %d\n", le32_to_cpu(private->size)); goto err; } - sof_dbg_comp_config(scomp, &iir->config); + sof_dbg_comp_config(scomp, &process->config); + + /* + * found private data in control, so copy it. + * get possible component controls - get size of all pdata, + * then memcpy with headers + */ + for (i = 0; i < widget->num_kcontrols; i++) { + kc = &widget->kcontrol_news[i]; + + switch (widget->dobj.widget.kcontrol_type) { + case SND_SOC_TPLG_TYPE_MIXER: + sm = (struct soc_mixer_control *)kc->private_value; + scontrol = sm->dobj.private; + break; + case SND_SOC_TPLG_TYPE_BYTES: + sbe = (struct soc_bytes_ext *)kc->private_value; + scontrol = sbe->dobj.private; + break; + case SND_SOC_TPLG_TYPE_ENUM: + se = (struct soc_enum *)kc->private_value; + scontrol = se->dobj.private; + break; + default: + dev_err(sdev->dev, "error: unknown kcontrol type %d in widget %s\n", + widget->dobj.widget.kcontrol_type, + widget->name); + return -EINVAL; + } + + /* don't include if no private data */ + pdata = scontrol->control_data->data; + if (!pdata) + continue; + + /* make sure data is valid - data can be updated at runtime */ + if (pdata->magic != SOF_ABI_MAGIC) + continue; - /* we have a private data found in control, so copy it */ - if (iir_data_size > 0) { - memcpy(&iir->data, pdata->data, pdata->size); - iir->size = iir_data_size; + memcpy(&process->data + offset, pdata->data, pdata->size); + offset += pdata->size; } - swidget->private = (void *)iir; + process->size = ipc_data_size; + swidget->private = (void *)process; - ret = sof_ipc_tx_message(sdev->ipc, iir->comp.hdr.cmd, iir, + ret = sof_ipc_tx_message(sdev->ipc, process->comp.hdr.cmd, process, ipc_size, r, sizeof(*r)); if (ret >= 0) return ret; err: - kfree(iir); + kfree(process); return ret; } /* - * Effect Topology + * Processing Component Topology - can be "effect", "codec", or general + * "processing". */ -static int sof_widget_load_effect(struct snd_soc_component *scomp, int index, - struct snd_sof_widget *swidget, - struct snd_soc_tplg_dapm_widget *tw, - struct sof_ipc_comp_reply *r) +static int sof_widget_load_process(struct snd_soc_component *scomp, int index, + struct snd_sof_widget *swidget, + struct snd_soc_tplg_dapm_widget *tw, + struct sof_ipc_comp_reply *r) { struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); struct snd_soc_tplg_private *private = &tw->priv; - struct sof_ipc_comp_effect config; + struct sof_ipc_comp_process config; int ret; - /* check we have some tokens - we need at least effect type */ + /* check we have some tokens - we need at least process type */ if (le32_to_cpu(private->size) == 0) { - dev_err(sdev->dev, "error: effect tokens not found\n"); + dev_err(sdev->dev, "error: process tokens not found\n"); return -EINVAL; } memset(&config, 0, sizeof(config)); - /* get the effect token */ - ret = sof_parse_tokens(scomp, &config, effect_tokens, - ARRAY_SIZE(effect_tokens), private->array, + /* get the process token */ + ret = sof_parse_tokens(scomp, &config, process_tokens, + ARRAY_SIZE(process_tokens), private->array, le32_to_cpu(private->size)); if (ret != 0) { - dev_err(sdev->dev, "error: parse effect tokens failed %d\n", + dev_err(sdev->dev, "error: parse process tokens failed %d\n", le32_to_cpu(private->size)); return ret; } - /* now load effect specific data and send IPC */ - switch (config.type) { - case SOF_EFFECT_INTEL_EQFIR: - ret = sof_effect_fir_load(scomp, index, swidget, tw, r); - break; - case SOF_EFFECT_INTEL_EQIIR: - ret = sof_effect_iir_load(scomp, index, swidget, tw, r); - break; - default: - dev_err(sdev->dev, "error: invalid effect type %d\n", - config.type); - ret = -EINVAL; - break; - } - + /* now load process specific data and send IPC */ + ret = sof_process_load(scomp, index, swidget, tw, r, + find_process_comp_type(config.type)); if (ret < 0) { - dev_err(sdev->dev, "error: effect loading failed\n"); + dev_err(sdev->dev, "error: process loading failed\n"); return ret; } @@ -1854,7 +1851,8 @@ static int sof_widget_ready(struct snd_soc_component *scomp, int index, ret = sof_widget_load_siggen(scomp, index, swidget, tw, &reply); break; case snd_soc_dapm_effect: - ret = sof_widget_load_effect(scomp, index, swidget, tw, &reply); + ret = sof_widget_load_process(scomp, index, swidget, tw, + &reply); break; case snd_soc_dapm_mux: case snd_soc_dapm_demux: From 464d9ef35a480ea81684f00d2f690f04ded1332a Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Tue, 12 Feb 2019 20:41:21 -0800 Subject: [PATCH 2/5] ASoC: SOF: add new process comp types for hotwording Add process comp type for keyword detect, KPB and channel selector components and support for loading such comps during topology load. Signed-off-by: Liam Girdwood Signed-off-by: Ranjani Sridharan --- include/sound/sof/topology.h | 11 +++++++++-- sound/soc/sof/topology.c | 3 +++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/include/sound/sof/topology.h b/include/sound/sof/topology.h index 5cafee2ebddbcd..9ab62a9b99eccf 100644 --- a/include/sound/sof/topology.h +++ b/include/sound/sof/topology.h @@ -32,8 +32,12 @@ enum sof_comp_type { SOF_COMP_BUFFER, SOF_COMP_EQ_IIR, SOF_COMP_EQ_FIR, - SOF_COMP_FILEREAD, /**< host test based file IO */ - SOF_COMP_FILEWRITE, /**< host test based file IO */ + SOF_COMP_KEYWORD_DETECT, + SOF_COMP_KPB, /* A key phrase buffer component */ + SOF_COMP_SELECTOR, /**< channel selector component */ + /* keep FILEREAD/FILEWRITE as the last ones */ + SOF_COMP_FILEREAD = 10000, /**< host test based file IO */ + SOF_COMP_FILEWRITE = 10001, /**< host test based file IO */ }; /* XRUN action for component */ @@ -168,6 +172,9 @@ enum sof_ipc_process_type { SOF_PROCESS_NONE = 0, /**< None */ SOF_PROCESS_EQFIR, /**< Intel FIR */ SOF_PROCESS_EQIIR, /**< Intel IIR */ + SOF_PROCESS_KEYWORD_DETECT, /**< Keyword Detection */ + SOF_PROCESS_KPB, /**< KeyPhrase Buffer Manager */ + SOF_PROCESS_CHAN_SELECTOR, /**< Channel Selector */ }; /* generic "effect", "codec" or proprietary processing component */ diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index 2d1ca0180aeca3..ed23f8b2f6600b 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -244,6 +244,9 @@ struct sof_process_types { static const struct sof_process_types sof_process[] = { {"EQFIR", SOF_PROCESS_EQFIR, SOF_COMP_EQ_FIR}, {"EQIIR", SOF_PROCESS_EQIIR, SOF_COMP_EQ_IIR}, + {"KEYWORD_DETECT", SOF_PROCESS_KEYWORD_DETECT, SOF_COMP_KEYWORD_DETECT}, + {"KPB", SOF_PROCESS_KPB, SOF_COMP_KPB}, + {"CHAN_SELECTOR", SOF_PROCESS_CHAN_SELECTOR, SOF_COMP_SELECTOR}, }; static enum sof_ipc_process_type find_process(const char *name) From 25321dbad6a97bfb45d76c73dceb12289bf27141 Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Tue, 5 Mar 2019 19:56:17 -0800 Subject: [PATCH 3/5] ASoC: SOF: add support for binding widgets with external events Add support for binding widgets to external dapm events. Define and implement the DAPM event handler for keyword detect component. The keyword detect component is responsible for setting the pipeline params and triggering all the components in the pipeline upon receiving a PRE_PMU or a POST_PMD DAPM event indicating that the audio pipeline it is connected to has been activated. Signed-off-by: Ranjani Sridharan --- include/sound/sof/topology.h | 6 ++ sound/soc/sof/topology.c | 188 +++++++++++++++++++++++++++++++++++ 2 files changed, 194 insertions(+) diff --git a/include/sound/sof/topology.h b/include/sound/sof/topology.h index 9ab62a9b99eccf..46b2a7e63167bb 100644 --- a/include/sound/sof/topology.h +++ b/include/sound/sof/topology.h @@ -247,4 +247,10 @@ struct sof_ipc_pipe_comp_connect { uint32_t sink_id; } __packed; +/* external events */ +enum sof_event_types { + SOF_EVENT_NONE = 0, + SOF_KEYWORD_DETECT_DAPM_EVENT, +}; + #endif diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index ed23f8b2f6600b..cc3127282bd440 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -10,6 +10,7 @@ #include #include +#include #include #include "sof-priv.h" #include "ops.h" @@ -38,6 +39,149 @@ #define TLV_STEP 1 #define TLV_MUTE 2 +/* send pcm params ipc */ +static int ipc_pcm_params(struct snd_sof_widget *swidget, int dir) +{ + struct sof_ipc_pcm_params_reply ipc_params_reply; + struct snd_sof_dev *sdev = swidget->sdev; + struct sof_ipc_pcm_params pcm; + struct snd_pcm_hw_params *params; + struct snd_sof_pcm *spcm; + int ret = 0; + + memset(&pcm, 0, sizeof(pcm)); + + /* get runtime PCM params using widget's stream name */ + spcm = snd_sof_find_spcm_name(sdev, swidget->widget->sname); + if (!spcm) { + dev_err(sdev->dev, "error: cannot find PCM for %s\n", + swidget->widget->name); + return -EINVAL; + } + + params = &spcm->params[dir]; + + /* set IPC PCM params */ + pcm.hdr.size = sizeof(pcm); + pcm.hdr.cmd = SOF_IPC_GLB_STREAM_MSG | SOF_IPC_STREAM_PCM_PARAMS; + pcm.comp_id = swidget->comp_id; + pcm.params.hdr.size = sizeof(pcm.params); + pcm.params.direction = dir; + pcm.params.sample_valid_bytes = params_width(params) >> 3; + pcm.params.buffer_fmt = SOF_IPC_BUFFER_INTERLEAVED; + pcm.params.rate = params_rate(params); + pcm.params.channels = params_channels(params); + pcm.params.host_period_bytes = params_period_bytes(params); + + /* set format */ + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16: + pcm.params.frame_fmt = SOF_IPC_FRAME_S16_LE; + break; + case SNDRV_PCM_FORMAT_S24: + pcm.params.frame_fmt = SOF_IPC_FRAME_S24_4LE; + break; + case SNDRV_PCM_FORMAT_S32: + pcm.params.frame_fmt = SOF_IPC_FRAME_S32_LE; + break; + default: + return -EINVAL; + } + + /* send IPC to the DSP */ + ret = sof_ipc_tx_message(sdev->ipc, pcm.hdr.cmd, &pcm, sizeof(pcm), + &ipc_params_reply, sizeof(ipc_params_reply)); + if (ret < 0) + dev_err(sdev->dev, "error: pcm params failed for %s\n", + swidget->widget->name); + + return ret; +} + + /* send stream trigger ipc */ +static int ipc_trigger(struct snd_sof_widget *swidget, int cmd) +{ + struct snd_sof_dev *sdev = swidget->sdev; + struct sof_ipc_stream stream; + struct sof_ipc_reply reply; + int ret = 0; + + /* set IPC stream params */ + stream.hdr.size = sizeof(stream); + stream.hdr.cmd = SOF_IPC_GLB_STREAM_MSG | cmd; + stream.comp_id = swidget->comp_id; + + /* send IPC to the DSP */ + ret = sof_ipc_tx_message(sdev->ipc, stream.hdr.cmd, &stream, + sizeof(stream), &reply, sizeof(reply)); + if (ret < 0) + dev_err(sdev->dev, "error: failed to trigger %s\n", + swidget->widget->name); + + return ret; +} + +static int sof_keyword_dapm_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *k, int event) +{ + struct snd_sof_widget *swidget = w->dobj.private; + struct snd_sof_dev *sdev; + int ret = 0; + + if (!swidget) + return 0; + + sdev = swidget->sdev; + + dev_dbg(sdev->dev, "received event %d for widget %s\n", + event, w->name); + + /* process events */ + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + /* set pcm params */ + ret = ipc_pcm_params(swidget, SOF_IPC_STREAM_CAPTURE); + if (ret < 0) { + dev_err(sdev->dev, + "error: failed to set pcm params for widget %s\n", + swidget->widget->name); + break; + } + + /* start trigger */ + ret = ipc_trigger(swidget, SOF_IPC_STREAM_TRIG_START); + if (ret < 0) + dev_err(sdev->dev, + "error: failed to trigger widget %s\n", + swidget->widget->name); + break; + case SND_SOC_DAPM_POST_PMD: + /* stop trigger */ + ret = ipc_trigger(swidget, SOF_IPC_STREAM_TRIG_STOP); + if (ret < 0) + dev_err(sdev->dev, + "error: failed to trigger widget %s\n", + swidget->widget->name); + + /* pcm free */ + ret = ipc_trigger(swidget, SOF_IPC_STREAM_PCM_FREE); + if (ret < 0) + dev_err(sdev->dev, + "error: failed to trigger widget %s\n", + swidget->widget->name); + break; + default: + break; + } + + return ret; +} + +/* event handlers for keyword detect component */ +static const struct snd_soc_tplg_widget_events sof_kwd_events[] = { + {SOF_KEYWORD_DETECT_DAPM_EVENT, sof_keyword_dapm_event}, +}; + static inline int get_tlv_data(const int *p, int tlv[TLV_ITEMS]) { /* we only support dB scale TLV type at the moment */ @@ -1769,6 +1913,38 @@ static int sof_widget_load_process(struct snd_soc_component *scomp, int index, return 0; } +static int sof_widget_bind_event(struct snd_sof_dev *sdev, + struct snd_sof_widget *swidget, + u16 event_type) +{ + struct sof_ipc_comp *ipc_comp; + + /* validate widget event type */ + switch (event_type) { + case SOF_KEYWORD_DETECT_DAPM_EVENT: + /* only KEYWORD_DETECT comps should handle this */ + if (swidget->id != snd_soc_dapm_effect) + break; + + ipc_comp = swidget->private; + if (ipc_comp && ipc_comp->type != SOF_COMP_KEYWORD_DETECT) + break; + + /* bind event to keyword detect comp */ + return snd_soc_tplg_widget_bind_event(swidget->widget, + sof_kwd_events, + ARRAY_SIZE(sof_kwd_events), + event_type); + default: + break; + } + + dev_err(sdev->dev, + "error: invalid event type %d for widget %s\n", + event_type, swidget->widget->name); + return -EINVAL; +} + /* external widget init - used for any driver specific init */ static int sof_widget_ready(struct snd_soc_component *scomp, int index, struct snd_soc_dapm_widget *w, @@ -1881,6 +2057,18 @@ static int sof_widget_ready(struct snd_soc_component *scomp, int index, return ret; } + /* bind widget to external event */ + if (tw->event_type) { + ret = sof_widget_bind_event(sdev, swidget, + le16_to_cpu(tw->event_type)); + if (ret) { + dev_err(sdev->dev, "error: widget event binding failed\n"); + kfree(swidget->private); + kfree(swidget); + return ret; + } + } + w->dobj.private = swidget; list_add(&swidget->list, &sdev->widget_list); return ret; From 7698c85743a863d79a1787e4e2db70ca76f6823c Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Tue, 5 Mar 2019 21:47:43 -0800 Subject: [PATCH 4/5] ASoC: SOF: do not send routes to/from output/out_drv widgets to the DSP output/out_drv type widgets may be added in topology for compatibility. In the case of hostless pipelines, these might be used to propagate DAPM events to the pipeline. Do not send such routes to the FW as they need not be handled by the DSP. Signed-off-by: Ranjani Sridharan --- sound/soc/sof/topology.c | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index cc3127282bd440..f5bcaf7f215292 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -2913,6 +2913,16 @@ static int sof_route_load(struct snd_soc_component *scomp, int index, goto err; } + /* + * Virtual widgets of type output/out_drv may be added in topology + * for compatibility. These are not handled by the FW. + * So, don't send routes whose source/sink widget is of such types + * to the DSP. + */ + if (source_swidget->id == snd_soc_dapm_out_drv || + source_swidget->id == snd_soc_dapm_output) + goto err; + connect->source_id = source_swidget->comp_id; /* sink component */ @@ -2924,11 +2934,18 @@ static int sof_route_load(struct snd_soc_component *scomp, int index, goto err; } + /* + * Don't send routes whose sink widget is of type + * output or out_drv to the DSP + */ + if (sink_swidget->id == snd_soc_dapm_out_drv || + sink_swidget->id == snd_soc_dapm_output) + goto err; + connect->sink_id = sink_swidget->comp_id; /* - * Some virtual routes and widgets may been added in topology for - * compatibility. For virtual routes, both sink and source are not + * For virtual routes, both sink and source are not * buffer. Since only buffer linked to component is supported by * FW, others are reported as error, add check in route function, * do not send it to FW when both source and sink are not buffer From 59f40b8c7afb2ed636a0e19b69b455731b8d5164 Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Thu, 7 Mar 2019 10:47:54 -0800 Subject: [PATCH 5/5] ASoC: SOF: fix bug in snd_sof_find_spcm_name() In the keyword detect pipeline, there is a need for the detect component to receive the runtime PCM params from the capture pipeline. So in order to facilitate retreiving the PCM params from the capture pipeline, the detect component has to have its stream_name set to the PCM name. The function snd_sof_find_spcm_name() is currently used to acquire the PCM for a widget by matching the widget's stream name with the PCM name. If the stream_name field for the widget is not set in topology the input argument "name" will be empty. Also, not all PCM's have both playback and capture capabilities set in topology ie the caps name could be empty. If the "name" argument happens to be empty because the stream name for the widget was not in topology, snd_sof_find_spcm_name() will return the first spcm that has an empty name for either its playback or capture caps. So, add the check to make sure that the caps name is set before matching with the input argument. Also, modify signature for snd_sof_find_spcm_name to take const char * as input. Signed-off-by: Ranjani Sridharan --- sound/soc/sof/core.c | 11 ++++++++--- sound/soc/sof/sof-priv.h | 2 +- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/sound/soc/sof/core.c b/sound/soc/sof/core.c index 2ca716f5dd8970..d684d8614336b3 100644 --- a/sound/soc/sof/core.c +++ b/sound/soc/sof/core.c @@ -25,18 +25,23 @@ */ struct snd_sof_pcm *snd_sof_find_spcm_name(struct snd_sof_dev *sdev, - char *name) + const char *name) { struct snd_sof_pcm *spcm = NULL; list_for_each_entry(spcm, &sdev->pcm_list, list) { + /* match with PCM dai name */ if (strcmp(spcm->pcm.dai_name, name) == 0) return spcm; - if (strcmp(spcm->pcm.caps[0].name, name) == 0) + /* match with playback caps name if set */ + if (*spcm->pcm.caps[0].name && + !strcmp(spcm->pcm.caps[0].name, name)) return spcm; - if (strcmp(spcm->pcm.caps[1].name, name) == 0) + /* match with capture caps name if set */ + if (*spcm->pcm.caps[1].name && + !strcmp(spcm->pcm.caps[1].name, name)) return spcm; } diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index c4465b2d3a0432..870820f13b3f32 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -481,7 +481,7 @@ struct snd_sof_pcm *snd_sof_find_spcm_dai(struct snd_sof_dev *sdev, } struct snd_sof_pcm *snd_sof_find_spcm_name(struct snd_sof_dev *sdev, - char *name); + const char *name); struct snd_sof_pcm *snd_sof_find_spcm_comp(struct snd_sof_dev *sdev, unsigned int comp_id, int *direction);