From 6ac9230ad7f0c1cfb20b226747208521fbe6609e Mon Sep 17 00:00:00 2001 From: Jaska Uimonen Date: Sun, 9 Sep 2018 19:54:52 +0300 Subject: [PATCH 1/2] ASOC: SOF: topology: introduce effect type for widgets Introduce effect type enumeration and vendor type to differentiate between effects. We can use these types to build multieffect pipelines in the topology and parse those in the sof driver to send the correct ipc messages to DSP firmware. Signed-off-by: Jaska Uimonen --- include/uapi/sound/sof-ipc.h | 12 ++++++++++ include/uapi/sound/sof-topology.h | 2 ++ sound/soc/sof/topology.c | 38 +++++++++++++++++++++++++++++++ 3 files changed, 52 insertions(+) diff --git a/include/uapi/sound/sof-ipc.h b/include/uapi/sound/sof-ipc.h index 29cf46da5f3103..9532674d521bbd 100644 --- a/include/uapi/sound/sof-ipc.h +++ b/include/uapi/sound/sof-ipc.h @@ -732,6 +732,18 @@ struct sof_ipc_comp_eq_iir { struct sof_ipc_comp_config config; } __attribute__((packed)); +/** \brief Types of EFFECT */ +enum sof_ipc_effect_type { + SOF_EFFECT_INTEL_NONE = 0, /**< None */ + SOF_EFFECT_INTEL_EQFIR, /**< Intel FIR */ + SOF_EFFECT_INTEL_EQIIR, /**< Intel IIR */ +}; + +/* general purpose EFFECT configuration */ +struct sof_ipc_comp_effect { + enum sof_ipc_effect_type type; +} __attribute__((packed)); + /* frees components, buffers and pipelines * SOF_IPC_TPLG_COMP_FREE, SOF_IPC_TPLG_PIPE_FREE, SOF_IPC_TPLG_BUFFER_FREE */ diff --git a/include/uapi/sound/sof-topology.h b/include/uapi/sound/sof-topology.h index 53d92c7b714d93..3c4ae99c8b5e0a 100644 --- a/include/uapi/sound/sof-topology.h +++ b/include/uapi/sound/sof-topology.h @@ -91,4 +91,6 @@ #define SOF_TKN_INTEL_DMIC_PDM_CLK_EDGE 705 #define SOF_TKN_INTEL_DMIC_PDM_SKEW 706 +#define SOF_TKN_EFFECT_TYPE 900 + #endif diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index 83090ae0353f2a..fcfc8c69447ec9 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -233,6 +233,28 @@ static enum sof_ipc_frame find_format(const char *name) return SOF_IPC_FRAME_S32_LE; } +struct sof_effect_types { + const char *name; + enum sof_ipc_effect_type type; +}; + +static const struct sof_effect_types sof_effects[] = { + {"EQFIR", SOF_EFFECT_INTEL_EQFIR}, + {"EQIIR", SOF_EFFECT_INTEL_EQIIR}, +}; + +static enum sof_ipc_effect_type find_effect(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; + } + + return SOF_EFFECT_INTEL_NONE; +} + /* * Standard Kcontrols. */ @@ -342,6 +364,15 @@ 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) +{ + struct snd_soc_tplg_vendor_string_elem *velem = elem; + u32 *val = object + offset; + + *val = find_effect(velem->string); + return 0; +} + /* Buffers */ static const struct sof_topology_token buffer_tokens[] = { {SOF_TKN_BUF_SIZE, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, @@ -415,6 +446,13 @@ static const struct sof_topology_token eq_fir_tokens[] = { static const struct sof_topology_token eq_iir_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}, +}; + /* PCM */ static const struct sof_topology_token pcm_tokens[] = { {SOF_TKN_PCM_DMAC_CONFIG, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, From 7b1967608836d1adf48b16b13d3d154057fa1519 Mon Sep 17 00:00:00 2001 From: Jaska Uimonen Date: Sun, 9 Sep 2018 20:01:30 +0300 Subject: [PATCH 2/2] ASOC: SOF: topology: change eq to use effect type Parse iir and fir eq from widget data field specifying the effect type. Parse also optional initial eq params sent in the eq control's private data. Signed-off-by: Jaska Uimonen --- include/uapi/sound/sof-ipc.h | 4 + sound/soc/sof/topology.c | 243 ++++++++++++++++++++++++++++------- 2 files changed, 204 insertions(+), 43 deletions(-) diff --git a/include/uapi/sound/sof-ipc.h b/include/uapi/sound/sof-ipc.h index 9532674d521bbd..48e6bc867bacf9 100644 --- a/include/uapi/sound/sof-ipc.h +++ b/include/uapi/sound/sof-ipc.h @@ -724,12 +724,16 @@ struct sof_ipc_comp_tone { struct sof_ipc_comp_eq_fir { struct sof_ipc_comp comp; struct sof_ipc_comp_config config; + int32_t size; + unsigned char data[0]; } __attribute__((packed)); /* IIR equalizer component */ struct sof_ipc_comp_eq_iir { struct sof_ipc_comp comp; struct sof_ipc_comp_config config; + int32_t size; + unsigned char data[0]; } __attribute__((packed)); /** \brief Types of EFFECT */ diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index fcfc8c69447ec9..976b01c3e8fae0 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -298,6 +298,8 @@ static int sof_control_load_bytes(struct snd_soc_component *scomp, { struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); struct sof_ipc_ctrl_data *cdata; + struct snd_soc_tplg_bytes_control *control = + (struct snd_soc_tplg_bytes_control *)hdr; /* init the get/put bytes data */ scontrol->size = SOF_IPC_MSG_MAX_SIZE; @@ -312,6 +314,21 @@ static int sof_control_load_bytes(struct snd_soc_component *scomp, dev_dbg(sdev->dev, "tplg: load kcontrol index %d chans %d\n", scontrol->comp_id, scontrol->num_channels); + if (le32_to_cpu(control->priv.size) > SOF_IPC_MSG_MAX_SIZE) { + dev_warn(sdev->dev, "bytes priv data size %d too big\n", + control->priv.size); + return -EINVAL; + } + + if (le32_to_cpu(control->priv.size) > 0) { + memcpy(cdata->data->data, control->priv.data, + le32_to_cpu(control->priv.size)); + cdata->data->size = control->priv.size; + cdata->data->magic = SOF_ABI_MAGIC; + cdata->data->abi = SOF_ABI_VERSION; + cdata->data->comp_abi = SOF_ABI_VERSION; + } + return 0; } @@ -436,16 +453,6 @@ static const struct sof_topology_token src_tokens[] = { static const struct sof_topology_token tone_tokens[] = { }; -/* EQ FIR */ -/* -static const struct sof_topology_token eq_fir_tokens[] = { -}; -*/ - -/* EQ IIR */ -static const struct sof_topology_token eq_iir_tokens[] = { -}; - /* EFFECT */ static const struct sof_topology_token effect_tokens[] = { {SOF_TKN_EFFECT_TYPE, SND_SOC_TPLG_TUPLE_TYPE_STRING, @@ -1383,67 +1390,217 @@ static int sof_widget_load_siggen(struct snd_soc_component *scomp, int index, kfree(tone); return ret; } -/* - * Effect Topology. Only IIR equalizer is supported at this moment. - * TODO: Need to add also FIR support and have a way to add other - * effects and enhancements. - */ -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_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) + { struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); struct snd_soc_tplg_private *private = &tw->priv; - struct sof_ipc_comp_eq_iir *eq; + 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 sof_abi_hdr *pdata = NULL; + struct sof_ipc_comp_eq_fir *fir; + size_t ipc_size = 0, fir_data_size = 0; int ret; - eq = kzalloc(sizeof(*eq), GFP_KERNEL); - if (!eq) + /* get possible eq controls */ + kc = &widget->kcontrol_news[0]; + if (kc) { + sbe = (struct soc_bytes_ext *)kc->private_value; + scontrol = sbe->dobj.private; + } + + /* + * 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) + 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 IIR EQ IPC message */ - eq->comp.hdr.size = sizeof(*eq); - eq->comp.hdr.cmd = SOF_IPC_GLB_TPLG_MSG | SOF_IPC_TPLG_COMP_NEW; - eq->comp.id = swidget->comp_id; - eq->comp.pipeline_id = index; + /* 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; - eq->comp.type = SOF_COMP_EQ_IIR; - ret = sof_parse_tokens(scomp, eq, eq_iir_tokens, - ARRAY_SIZE(eq_iir_tokens), private->array, + ret = sof_parse_tokens(scomp, &fir->config, comp_tokens, + ARRAY_SIZE(comp_tokens), private->array, le32_to_cpu(private->size)); - if (ret) { - dev_err(sdev->dev, "error: parse EQ tokens failed %d\n", - private->size); + if (ret != 0) { + dev_err(sdev->dev, "error: parse fir.cfg tokens failed %d\n", + le32_to_cpu(private->size)); goto err; } - ret = sof_parse_tokens(scomp, &eq->config, comp_tokens, + sof_dbg_comp_config(scomp, &fir->config); + + /* 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; + } + + swidget->private = (void *)fir; + + 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; +} + +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; + + /* get possible eq controls */ + kc = &widget->kcontrol_news[0]; + if (kc) { + sbe = (struct soc_bytes_ext *)kc->private_value; + scontrol = sbe->dobj.private; + } + + /* + * 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_size = sizeof(struct sof_ipc_comp_eq_iir) + + le32_to_cpu(private->size) + + iir_data_size; + + iir = kzalloc(ipc_size, GFP_KERNEL); + if (!iir) + 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; + + ret = sof_parse_tokens(scomp, &iir->config, comp_tokens, ARRAY_SIZE(comp_tokens), private->array, le32_to_cpu(private->size)); - if (ret) { - dev_err(sdev->dev, "error: parse EQ.cfg tokens failed %d\n", + if (ret != 0) { + dev_err(sdev->dev, "error: parse iir.cfg tokens failed %d\n", le32_to_cpu(private->size)); goto err; } - dev_dbg(sdev->dev, "eq iir %s created\n", swidget->widget->name); + sof_dbg_comp_config(scomp, &iir->config); - sof_dbg_comp_config(scomp, &eq->config); - - swidget->private = (void *)eq; + /* 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; + } - ret = sof_ipc_tx_message(sdev->ipc, eq->comp.hdr.cmd, eq, - sizeof(*eq), r, sizeof(*r)); + swidget->private = (void *)iir; + ret = sof_ipc_tx_message(sdev->ipc, iir->comp.hdr.cmd, iir, + ipc_size, r, sizeof(*r)); if (ret >= 0) return ret; err: - kfree(eq); + kfree(iir); return ret; } +/* + * Effect Topology + */ + +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) +{ + 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; + int ret; + + /* check we have some tokens - we need at least effect type */ + if (le32_to_cpu(private->size) == 0) { + dev_err(sdev->dev, "error: effect 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, + le32_to_cpu(private->size)); + if (ret != 0) { + dev_err(sdev->dev, "error: parse effect 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; + } + + if (ret < 0) { + dev_err(sdev->dev, "error: effect loading failed\n"); + return ret; + } + + return 0; +} + /* * Generic widget loader. */