diff --git a/include/sound/sof/topology.h b/include/sound/sof/topology.h index b60a6026df3aac..46b2a7e63167bb 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 */ @@ -163,39 +167,25 @@ 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 */ + SOF_PROCESS_KEYWORD_DETECT, /**< Keyword Detection */ + SOF_PROCESS_KPB, /**< KeyPhrase Buffer Manager */ + SOF_PROCESS_CHAN_SELECTOR, /**< Channel Selector */ }; -/* 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; @@ -257,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/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/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); diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index f4c01a5af18f6c..f5bcaf7f215292 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 */ @@ -235,26 +379,42 @@ 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}, + {"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_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 +623,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 +697,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,218 +1718,233 @@ 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; } 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, @@ -1854,7 +2030,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: @@ -1880,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; @@ -2724,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 */ @@ -2735,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