From 5439a0801c80dfaf8dcf18094b7095c12313b1e0 Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Wed, 23 May 2018 18:45:42 -0700 Subject: [PATCH 01/15] ASoC: dapm: enable kcontrols for siggen type widgets This patch proposes to enable kcontrols for siggen type dapm widgets. This will allow triggering the tone generating siggen component and also modifying the run-time attributes such as frequency, amplitude etc. Signed-off-by: Ranjani Sridharan --- sound/soc/soc-dapm.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 6d54128e44b46b..04f84b2cc04f83 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -855,6 +855,7 @@ static int dapm_create_or_share_kcontrol(struct snd_soc_dapm_widget *w, kcname_in_long_name = true; } else { switch (w->id) { + case snd_soc_dapm_siggen: case snd_soc_dapm_switch: case snd_soc_dapm_mixer: case snd_soc_dapm_pga: @@ -3076,6 +3077,7 @@ int snd_soc_dapm_new_widgets(struct snd_soc_card *card) case snd_soc_dapm_demux: dapm_new_mux(w); break; + case snd_soc_dapm_siggen: case snd_soc_dapm_pga: case snd_soc_dapm_out_drv: dapm_new_pga(w); From d7239f3e22ae46c2d17f30e8d56d2712136a4141 Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Mon, 28 May 2018 19:24:37 -0700 Subject: [PATCH 02/15] ASoC: SOF: topology: set up kcontrol for siggen widget Check if the siggen comp has exactly 1 kcontrol for triggering. Since the kcontrol for the siggen serves as an ON/OFF switch, its max value should be 1. Signed-off-by: Ranjani Sridharan --- sound/soc/sof/topology.c | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index eb9e16f643b5a5..c5a6f7767017fb 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -1172,10 +1172,36 @@ static int sof_widget_load_siggen(struct snd_soc_component *scomp, int index, struct sof_ipc_comp_reply *r) { struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct snd_soc_dapm_widget *widget = swidget->widget; struct snd_soc_tplg_private *private = &tw->priv; + const struct snd_kcontrol_new *kc = NULL; struct sof_ipc_comp_tone tone; + struct soc_mixer_control *sm; + struct snd_sof_control *scontrol; int ret; + /* siggen needs 1 mixer type control to act as a trigger */ + if (tw->num_kcontrols != 1) { + dev_err(sdev->dev, "error: invalid kcontrol count %d for siggen\n", + tw->num_kcontrols); + return -EINVAL; + } + + /* get mixer control */ + kc = &widget->kcontrol_news[0]; + sm = (struct soc_mixer_control *)kc->private_value; + scontrol = sm->dobj.private; + + /* + * siggen kcontrol needs only 2 values + * 0 for disabling and 1 for enabling the comp + */ + if (sm->max != 1) { + dev_err(sdev->dev, "error: invalid max %d for siggen control\n", + sm->max); + return -EINVAL; + } + /* configure mixer IPC message */ memset(&tone, 0, sizeof(tone)); tone.comp.hdr.size = sizeof(tone); From cb27a458f9cb3ff561dd07e4c81c61ef28e513e1 Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Fri, 8 Jun 2018 20:33:50 -0700 Subject: [PATCH 03/15] ASoC: SOF: bespoke IO handler for switch type kcontrols This patch proposes a new IO handler for switch type kcontrols which can be used to trigger components such as siggen or control volume mute functionality. Signed-off-by: Ranjani Sridharan --- include/uapi/sound/sof-topology.h | 1 + sound/soc/sof/control.c | 101 ++++++++++++++++++++++++++++++ sound/soc/sof/sof-priv.h | 4 ++ sound/soc/sof/topology.c | 1 + 4 files changed, 107 insertions(+) diff --git a/include/uapi/sound/sof-topology.h b/include/uapi/sound/sof-topology.h index 1a4957b7fd049d..042d1fb48ccb9b 100644 --- a/include/uapi/sound/sof-topology.h +++ b/include/uapi/sound/sof-topology.h @@ -24,6 +24,7 @@ #define SOF_TPLG_KCTL_VOL_ID 256 #define SOF_TPLG_KCTL_ENUM_ID 257 #define SOF_TPLG_KCTL_BYTES_ID 258 +#define SOF_TPLG_KCTL_SWITCH_ID 259 /* * Tokens - must match values in topology configurations diff --git a/sound/soc/sof/control.c b/sound/soc/sof/control.c index db09ba63154b69..84b18535ad2093 100644 --- a/sound/soc/sof/control.c +++ b/sound/soc/sof/control.c @@ -20,6 +20,21 @@ #include #include "sof-priv.h" +/* return the widget type of the comp the kcontrol is attached to */ +static int get_widget_type(struct snd_sof_dev *sdev, + struct snd_sof_control *scontrol) +{ + struct snd_sof_widget *swidget; + + list_for_each_entry(swidget, &sdev->widget_list, list) { + if (swidget->comp_id == scontrol->comp_id) + return swidget->id; + } + + /* standalone kcontrol */ + return -EINVAL; +} + static inline u32 mixer_to_ipc(unsigned int value, u32 *volume_map, int size) { if (value >= size) @@ -195,3 +210,89 @@ int snd_sof_bytes_put(struct snd_kcontrol *kcontrol, pm_runtime_put_autosuspend(sdev->dev); return 0; } + +/* IO handlers for switch kcontrol handlers */ +int snd_sof_switch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_mixer_control *sm = + (struct soc_mixer_control *)kcontrol->private_value; + struct snd_sof_control *scontrol = sm->dobj.private; + struct snd_sof_dev *sdev = scontrol->sdev; + struct sof_ipc_ctrl_data *cdata = scontrol->control_data; + unsigned int i, channels = scontrol->num_channels; + + pm_runtime_get_sync(sdev->dev); + + /* get all the mixer data from DSP */ + snd_sof_ipc_get_comp_data(sdev->ipc, scontrol, SOF_IPC_COMP_GET_VALUE, + SOF_CTRL_TYPE_VALUE_CHAN_GET, + SOF_CTRL_CMD_SWITCH); + + /* read back each channel */ + for (i = 0; i < channels; i++) + ucontrol->value.integer.value[i] = cdata->chanv[i].value; + + pm_runtime_mark_last_busy(sdev->dev); + pm_runtime_put_autosuspend(sdev->dev); + return 0; +} + +int snd_sof_switch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_mixer_control *sm = + (struct soc_mixer_control *)kcontrol->private_value; + struct snd_sof_control *scontrol = sm->dobj.private; + struct snd_sof_dev *sdev = scontrol->sdev; + struct sof_ipc_ctrl_data *cdata = scontrol->control_data; + unsigned int i, channels = scontrol->num_channels; + int ret = 0, new_state, old_state; + int changed = 0; + + pm_runtime_get_sync(sdev->dev); + + switch (get_widget_type(sdev, scontrol)) { + case snd_soc_dapm_pga: + + /* + * if the kcontrol is used for processing as in the case of pga, + * values are channel-specific + */ + for (i = 0; i < channels; i++) { + new_state = ucontrol->value.integer.value[i]; + old_state = cdata->chanv[i].value; + if (new_state != old_state) + changed = 1; + cdata->chanv[i].value = new_state; + cdata->chanv[i].channel = i; + } + break; + default: + + /* + * if the kcontrol is for routing or a standalone control, + * all channel values are assumed to be identical + */ + for (i = 0; i < channels; i++) { + new_state = ucontrol->value.integer.value[0]; + old_state = cdata->chanv[0].value; + if (new_state != old_state) + changed = 1; + cdata->chanv[i].value = new_state; + cdata->chanv[i].channel = i; + } + break; + } + + /* notify DSP of switch state update if the control values are different */ + if (changed) + snd_sof_ipc_set_comp_data(sdev->ipc, scontrol, + SOF_IPC_COMP_SET_VALUE, + SOF_CTRL_TYPE_VALUE_CHAN_GET, + SOF_CTRL_CMD_SWITCH); + + pm_runtime_mark_last_busy(sdev->dev); + pm_runtime_put_autosuspend(sdev->dev); + return changed; +} diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index fabfcb25180940..4a97bc73ff6146 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -454,6 +454,10 @@ int snd_sof_bytes_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); int snd_sof_bytes_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); +int snd_sof_switch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); +int snd_sof_switch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); /* * DSP Architectures. diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index c5a6f7767017fb..31d60d1851f546 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -1958,6 +1958,7 @@ static const struct snd_soc_tplg_kcontrol_ops sof_io_ops[] = { {SOF_TPLG_KCTL_VOL_ID, snd_sof_volume_get, snd_sof_volume_put}, {SOF_TPLG_KCTL_ENUM_ID, snd_sof_enum_get, snd_sof_enum_put}, {SOF_TPLG_KCTL_BYTES_ID, snd_sof_bytes_get, snd_sof_bytes_put}, + {SOF_TPLG_KCTL_SWITCH_ID, snd_sof_switch_get, snd_sof_switch_put}, }; /* vendor specific bytes ext handlers available for binding */ From 9507adb6a5484fbb165ec7321bdf43daa31c75c6 Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Wed, 13 Jun 2018 19:49:57 -0700 Subject: [PATCH 04/15] ASoC: uapi: sof: add tone sample rate token Signed-off-by: Ranjani Sridharan --- include/uapi/sound/sof-topology.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/include/uapi/sound/sof-topology.h b/include/uapi/sound/sof-topology.h index 042d1fb48ccb9b..5c40a897c44cf9 100644 --- a/include/uapi/sound/sof-topology.h +++ b/include/uapi/sound/sof-topology.h @@ -89,4 +89,7 @@ #define SOF_TKN_INTEL_DMIC_PDM_CLK_EDGE 705 #define SOF_TKN_INTEL_DMIC_PDM_SKEW 706 +/* Tone */ +#define SOF_TKN_TONE_SAMPLE_RATE 800 + #endif From 4acfc564e4a9c2025ec94302ed874290a110eb6f Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Wed, 13 Jun 2018 19:50:49 -0700 Subject: [PATCH 05/15] ASoC: uapi: sof: add sample_rate member to the tone ipc comp def Signed-off-by: Ranjani Sridharan --- include/uapi/sound/sof-ipc.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/uapi/sound/sof-ipc.h b/include/uapi/sound/sof-ipc.h index e3c3de0a2d19f0..cb606d24c81320 100644 --- a/include/uapi/sound/sof-ipc.h +++ b/include/uapi/sound/sof-ipc.h @@ -649,6 +649,7 @@ struct sof_ipc_comp_mux { struct sof_ipc_comp_tone { struct sof_ipc_comp comp; struct sof_ipc_comp_config config; + int32_t sample_rate; int32_t frequency; int32_t amplitude; int32_t freq_mult; From ee73865b7ee08bbb50e8735e1d16b4c9818fb1ef Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Wed, 13 Jun 2018 19:51:42 -0700 Subject: [PATCH 06/15] ASoC: SOF: topology: Add sample rate token to tone tokens for parsing Signed-off-by: Ranjani Sridharan --- sound/soc/sof/topology.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index 31d60d1851f546..f0d968fadd3d8c 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -376,6 +376,8 @@ static const struct sof_topology_token src_tokens[] = { /* Tone */ static const struct sof_topology_token tone_tokens[] = { + {SOF_TKN_TONE_SAMPLE_RATE, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_comp_tone, sample_rate), 0}, }; /* PCM */ @@ -1228,8 +1230,9 @@ static int sof_widget_load_siggen(struct snd_soc_component *scomp, int index, return ret; } - dev_dbg(sdev->dev, "tone %s: frequency %d amplitude %d\n", - swidget->widget->name, tone.frequency, tone.amplitude); + dev_dbg(sdev->dev, "tone %s: frequency %d amplitude %d sample rate %d\n", + swidget->widget->name, tone.frequency, tone.amplitude, + tone.sample_rate); sof_dbg_comp_config(scomp, &tone.config); return sof_ipc_tx_message(sdev->ipc, From cca82b73f2924346dec0c6ea9ed01d7e199f690b Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Wed, 13 Jun 2018 19:53:27 -0700 Subject: [PATCH 07/15] ASoC: SOF: add support for triggering siggen pipeline in switch kcontrol IO handler This patch adds the changes to the switch control IO handler to handle the additional steps required to trigger the siggen pipeline based on user input.wq When the user switches the siggen control ON, the IO handler sends the ipc messages required to set up the pcm params and start the pipeline. When the user switches it off, it frees the pcm and resets the pipeline. Signed-off-by: Ranjani Sridharan --- sound/soc/sof/control.c | 147 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 140 insertions(+), 7 deletions(-) diff --git a/sound/soc/sof/control.c b/sound/soc/sof/control.c index 84b18535ad2093..b912f8265a28b4 100644 --- a/sound/soc/sof/control.c +++ b/sound/soc/sof/control.c @@ -35,6 +35,86 @@ static int get_widget_type(struct snd_sof_dev *sdev, return -EINVAL; } +/* helper function to send pcm params ipc */ +static int siggen_pcm_params(struct snd_sof_control *scontrol, + struct snd_sof_dev *sdev) +{ + struct sof_ipc_pcm_params_reply ipc_params_reply; + struct sof_ipc_pcm_params pcm; + int ret = 0; + + memset(&pcm, 0, sizeof(pcm)); + + /* set IPC PCM parameters */ + pcm.hdr.size = sizeof(pcm); + pcm.hdr.cmd = SOF_IPC_GLB_STREAM_MSG | SOF_IPC_STREAM_PCM_PARAMS; + pcm.comp_id = scontrol->comp_id; + pcm.params.channels = scontrol->num_channels; + pcm.params.direction = SOF_IPC_STREAM_PLAYBACK; + + /* 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: setting pcm params for siggen\n"); + + return ret; +} + +/* helper function to send stream trigger ipc for siggen pipeline */ +static int siggen_trigger(struct snd_sof_control *scontrol, + struct snd_sof_dev *sdev, int cmd) +{ + 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 = scontrol->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 stream\n"); + + return ret; +} + +/* + * Helper function to send ipc's to trigger siggen pipeline + * The siggen pipeline is enabled/disabled only if the + * control values change from the old state. + */ +static int siggen_pipeline_trigger(struct snd_sof_control *scontrol, + struct snd_sof_dev *sdev, + struct snd_soc_card *card, + int new_state) +{ + int ret = 0; + + if (!new_state) { + + /* free pcm and reset pipeline */ + ret = siggen_trigger(scontrol, sdev, + SOF_IPC_STREAM_PCM_FREE); + } else { + + /* set pcm params */ + ret = siggen_pcm_params(scontrol, sdev); + if (ret < 0) + return ret; + + /* enable siggen */ + ret = siggen_trigger(scontrol, sdev, + SOF_IPC_STREAM_TRIG_START); + } + + return ret; +} + static inline u32 mixer_to_ipc(unsigned int value, u32 *volume_map, int size) { if (value >= size) @@ -246,6 +326,9 @@ int snd_sof_switch_put(struct snd_kcontrol *kcontrol, struct snd_sof_control *scontrol = sm->dobj.private; struct snd_sof_dev *sdev = scontrol->sdev; struct sof_ipc_ctrl_data *cdata = scontrol->control_data; + struct snd_soc_dapm_context *dapm = + snd_soc_dapm_kcontrol_dapm(kcontrol); + struct snd_soc_card *card = dapm->card; unsigned int i, channels = scontrol->num_channels; int ret = 0, new_state, old_state; int changed = 0; @@ -267,6 +350,52 @@ int snd_sof_switch_put(struct snd_kcontrol *kcontrol, cdata->chanv[i].value = new_state; cdata->chanv[i].channel = i; } + + /* + * notify DSP of switch state update + * if the control values are different + */ + if (changed) + snd_sof_ipc_set_comp_data(sdev->ipc, scontrol, + SOF_IPC_COMP_SET_VALUE, + SOF_CTRL_TYPE_VALUE_CHAN_GET, + SOF_CTRL_CMD_SWITCH); + break; + case snd_soc_dapm_siggen: + + /* + * A siggen kcontrol is used as an ON/OFF switch, + * so all channel values are assumed to be identical + */ + for (i = 0; i < channels; i++) { + new_state = ucontrol->value.integer.value[0]; + old_state = cdata->chanv[0].value; + if (new_state != old_state) + changed = 1; + cdata->chanv[i].value = new_state; + cdata->chanv[i].channel = i; + } + + if (changed) { + + /* + * notify DSP of switch state update + * if the control values are different + */ + snd_sof_ipc_set_comp_data(sdev->ipc, scontrol, + SOF_IPC_COMP_SET_VALUE, + SOF_CTRL_TYPE_VALUE_CHAN_GET, + SOF_CTRL_CMD_SWITCH); + + /* trigger siggen pipeline */ + ret = siggen_pipeline_trigger(scontrol, sdev, + card, new_state); + if (ret < 0) { + dev_err(sdev->dev, + "error: triggering siggen pipeline\n"); + changed = ret; + } + } break; default: @@ -282,16 +411,20 @@ int snd_sof_switch_put(struct snd_kcontrol *kcontrol, cdata->chanv[i].value = new_state; cdata->chanv[i].channel = i; } + + /* + * notify DSP of switch state update + * if the control values are different + */ + if (changed) + snd_sof_ipc_set_comp_data(sdev->ipc, scontrol, + SOF_IPC_COMP_SET_VALUE, + SOF_CTRL_TYPE_VALUE_CHAN_GET, + SOF_CTRL_CMD_SWITCH); + break; } - /* notify DSP of switch state update if the control values are different */ - if (changed) - snd_sof_ipc_set_comp_data(sdev->ipc, scontrol, - SOF_IPC_COMP_SET_VALUE, - SOF_CTRL_TYPE_VALUE_CHAN_GET, - SOF_CTRL_CMD_SWITCH); - pm_runtime_mark_last_busy(sdev->dev); pm_runtime_put_autosuspend(sdev->dev); return changed; From c6127f35538156a0cb34ed9edfe28a7e38269118 Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Fri, 22 Jun 2018 21:09:39 -0700 Subject: [PATCH 08/15] ASoC: add "virtual" flag to dai link structure Hostless pipelines lack a FE dai that is used to establish a path to the BE and enable the codecs. Virtual FE dai links can be used in such cases to establish a connection to the BE. This patch adds a new flag named "virtual" to the dai link structure that will be used to check if a dai link is virtual. Signed-off-by: Ranjani Sridharan --- include/sound/soc.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/include/sound/soc.h b/include/sound/soc.h index 71a745fa8131d4..812a1de3792472 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -1134,6 +1134,9 @@ struct snd_soc_dai_link { /* Do not create a PCM for this DAI link (Backend link) */ unsigned int ignore:1; + /* virtual link */ + unsigned int virtual:1; + struct list_head list; /* DAI link list of the soc card */ struct snd_soc_dobj dobj; /* For topology */ }; From bfd6c302136e56b60478a80d5421c26ece7740ba Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Fri, 22 Jun 2018 21:13:45 -0700 Subject: [PATCH 09/15] ASoC: pcm: add methods to create/remove virtual dai link This patch adds the methods for create a virtual FE dal link and add it to the sound card. It also adds the method to free the virtual FE connected to the card. Signed-off-by: Ranjani Sridharan --- include/sound/soc-dpcm.h | 5 +- sound/soc/soc-pcm.c | 109 ++++++++++++++++++++++++++++++++++++++- 2 files changed, 112 insertions(+), 2 deletions(-) diff --git a/include/sound/soc-dpcm.h b/include/sound/soc-dpcm.h index 806059052bfcca..0373d79a8985c8 100644 --- a/include/sound/soc-dpcm.h +++ b/include/sound/soc-dpcm.h @@ -158,5 +158,8 @@ static inline void dpcm_path_put(struct snd_soc_dapm_widget_list **list) kfree(*list); } - +/* create/free virtual FE dai links */ +int soc_dpcm_vfe_new(struct snd_soc_card *, int index, const char *link_name, + const char *cpu_dai_name, const char *platform_name); +int soc_dpcm_vfe_free(struct snd_soc_card *card); #endif diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index 4ce489165a6d9b..42788dc2a292e3 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -1582,7 +1582,6 @@ static int dpcm_add_paths(struct snd_soc_pcm_runtime *fe, int stream, /* Create any new FE <--> BE connections */ for (i = 0; i < list->num_widgets; i++) { - switch (list->widgets[i]->id) { case snd_soc_dapm_dai_in: if (stream != SNDRV_PCM_STREAM_PLAYBACK) @@ -2789,6 +2788,114 @@ int soc_dpcm_be_digital_mute(struct snd_soc_pcm_runtime *fe, int mute) return 0; } +/* + * create a virtual FE DAI link + * Virtual FE DAI links are used in hostless pipelines + * to enable the codecs when the pipeline is triggered + */ +int soc_dpcm_vfe_new(struct snd_soc_card *card, int index, + const char *link_name, const char *cpu_dai_name, + const char *platform_name) +{ + struct snd_soc_dai_link *link; + + link = kzalloc(sizeof(*link), GFP_KERNEL); + if (!link) + return -ENOMEM; + + dev_dbg(card->dev, "ASoC: adding new virtual FE DAI link %s\n", + link_name); + + /* define virtual FE DAI link */ + link->virtual = 1; + link->name = link_name; + link->id = 1; + link->cpu_dai_name = cpu_dai_name; + link->platform_name = platform_name; + link->codec_name = "snd-soc-dummy"; + link->codec_dai_name = "snd-soc-dummy-dai"; + link->num_codecs = 1; + + /* allocate memory for link codecs */ + link->codecs = devm_kzalloc(card->dev, + sizeof(struct snd_soc_dai_link_component), + GFP_KERNEL); + if (!link->codecs) + return -ENOMEM; + + link->codecs[0].name = link->codec_name; + link->codecs[0].dai_name = link->codec_dai_name; + + /* enable DPCM */ + link->dynamic = 1; + + /*TODO: check if we need to handle capture for virtual FE */ + link->dpcm_playback = 1; + + link->dobj.index = index; + link->dobj.type = SND_SOC_DOBJ_DAI_LINK; + + /* add virtual dai link to card dai link list */ + snd_soc_add_dai_link(card, link); + + return 0; +} +EXPORT_SYMBOL_GPL(soc_dpcm_vfe_new); + +/* free virtual FE DAI link */ +int soc_dpcm_vfe_free(struct snd_soc_card *card) +{ + struct snd_soc_rtdcom_list *rtdcom1, *rtdcom2; + struct snd_soc_pcm_runtime *rtd; + struct snd_pcm_str *pstr; + int stream_dir; + + list_for_each_entry(rtd, &card->rtd_list, list) { + + /* check if this is a virtual dai link */ + if (rtd->dai_link->virtual) { + + if (rtd->dai_link->dpcm_playback) { + stream_dir = SNDRV_PCM_STREAM_PLAYBACK; + + /* disconnect FE from BE */ + dpcm_be_disconnect(rtd, stream_dir); + + /* free pcm runtime */ + kfree(rtd->dpcm[stream_dir].runtime); + + pstr = &rtd->pcm->streams[stream_dir]; + + /* free pcm substream amd pcm */ + kfree(pstr->substream); + } + + /* free pcm */ + kfree(rtd->pcm); + + /* free codec dais and component list */ + kfree(rtd->codec_dais); + + for_each_rtdcom_safe(rtd, rtdcom1, rtdcom2) + kfree(rtdcom1); + + INIT_LIST_HEAD(&rtd->component_list); + + /* remove dai_link from card */ + snd_soc_remove_dai_link(card, rtd->dai_link); + + /* free link */ + kfree(rtd->dai_link); + + /* free runtime */ + kfree(rtd); + } + } + + return 0; +} +EXPORT_SYMBOL_GPL(soc_dpcm_vfe_free); + static int dpcm_fe_dai_open(struct snd_pcm_substream *fe_substream) { struct snd_soc_pcm_runtime *fe = fe_substream->private_data; From 709b04b5de87779ff588e5826f6a666830ffbb82 Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Fri, 22 Jun 2018 21:15:04 -0700 Subject: [PATCH 10/15] ASoC: dapm: dont delete starting point if list is singular When walking the graph to discover the path from the virtual FE to the BE, there is only one widget in the path. Do not remove this BE widget from the list, so it can be used to connect with the virtual FE dai link. Signed-off-by: Ranjani Sridharan --- sound/soc/soc-dapm.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 04f84b2cc04f83..a0c1527e1b2917 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -1247,7 +1247,8 @@ int snd_soc_dapm_dai_get_connected_widgets(struct snd_soc_dai *dai, int stream, custom_stop_condition); /* Drop starting point */ - list_del(widgets.next); + if (!list_is_singular(&widgets)) + list_del(widgets.next); ret = dapm_widget_list_create(list, &widgets); if (ret) From 521240e96bed0e701509b238e79e7f4d54feea7e Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Fri, 22 Jun 2018 21:16:12 -0700 Subject: [PATCH 11/15] ASoC: SOF: topology: add/remove virtual FE when loading/unloading a siggen widget Since hostless pipelines do not have a PCM, they need a virtual FE dai link that can be used to startup the BE dai links when the pipeline is triggered. Without this, the BE dai links do not get enabled and the tone would be inaudible. Signed-off-by: Ranjani Sridharan --- sound/soc/sof/topology.c | 30 +++++++++++++++++++++++++++--- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index f0d968fadd3d8c..6f831d38ceb82d 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -1180,6 +1181,9 @@ static int sof_widget_load_siggen(struct snd_soc_component *scomp, int index, struct sof_ipc_comp_tone tone; struct soc_mixer_control *sm; struct snd_sof_control *scontrol; + const char *link_name = "TONE-VFE0"; + const char *cpu_dai_name = "sof-audio"; + const char *platform_name = "sof-audio"; int ret; /* siggen needs 1 mixer type control to act as a trigger */ @@ -1235,9 +1239,26 @@ static int sof_widget_load_siggen(struct snd_soc_component *scomp, int index, tone.sample_rate); sof_dbg_comp_config(scomp, &tone.config); - return sof_ipc_tx_message(sdev->ipc, - tone.comp.hdr.cmd, &tone, sizeof(tone), r, - sizeof(*r)); + ret = sof_ipc_tx_message(sdev->ipc, + tone.comp.hdr.cmd, &tone, sizeof(tone), r, + sizeof(*r)); + if (ret < 0) + return ret; + + /* + * create virtual FE link + * This will be used to enable the codec when + * the siggen pipeline is triggered + */ + ret = soc_dpcm_vfe_new(scomp->card, index, link_name, cpu_dai_name, + platform_name); + if (ret < 0) { + dev_err(sdev->dev, "error: creating virtual FE %d\n", + private->size); + return ret; + } + + return 0; } /* @@ -1396,6 +1417,9 @@ static int sof_widget_unload(struct snd_soc_component *scomp, /* free volume table */ kfree(scontrol->volume_table); break; + case snd_soc_dapm_siggen: + soc_dpcm_vfe_free(scomp->card); + break; default: break; } From 1b97d1178fbb63e12412a2f8d22cfa5ddf1b704e Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Fri, 22 Jun 2018 21:27:16 -0700 Subject: [PATCH 12/15] ASoC: core: set runtime and the cpu_dai/codec_dai active status for virtual dai link Virtual FE dai links should be manually set to running state by default with a pcm runtime. The active count of their cpu_dai and codec_dai's should also be updated. This is required to establish FE-BE connection and enable the BE DAI when the dpcm runtime is updated. Signed-off-by: Ranjani Sridharan --- sound/soc/soc-core.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index a0160a409e4094..4a9bdcde3aa036 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -1047,6 +1047,7 @@ static int soc_bind_dai_link(struct snd_soc_card *card, struct snd_soc_dai **codec_dais; struct snd_soc_platform *platform; struct device_node *platform_of_node; + struct snd_pcm_runtime *runtime; const char *platform_name; int i; @@ -1134,6 +1135,25 @@ static int soc_bind_dai_link(struct snd_soc_card *card, } soc_add_pcm_runtime(card, rtd); + + /* if the dai link is virtual, create runtime to set it as running */ + if (rtd->dai_link->virtual) { + runtime = kzalloc(sizeof(*runtime), + GFP_KERNEL); + if (!runtime) + return -ENOMEM; + + if (rtd->dai_link->dpcm_playback) { + rtd->dpcm[SNDRV_PCM_STREAM_PLAYBACK].runtime = runtime; + rtd->cpu_dai->playback_active = 1; + rtd->codec_dai->playback_active = 1; + } + + /* increment the active count for cpu dai */ + rtd->cpu_dai->active++; + + /* does virtual FE for capture make sense */ + } return 0; _err_defer: From 337e4f273add60a9f4a8dec37adf7dcd88306a4f Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Sat, 23 Jun 2018 20:48:41 -0700 Subject: [PATCH 13/15] ASoC: pcm: do no register pcm device for virtual FE dai links Virtual FE dai links do not need to register the pcm device. So just create the empty pcm device and substream in the requested direction. Signed-off-by: Ranjani Sridharan --- sound/soc/soc-pcm.c | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index 42788dc2a292e3..64f459030f1c68 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -3111,7 +3111,9 @@ int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num) struct snd_soc_dai *cpu_dai = rtd->cpu_dai; struct snd_soc_component *component; struct snd_soc_rtdcom_list *rtdcom; + struct snd_pcm_substream *substream; struct snd_pcm *pcm; + int stream_dir; char new_name[64]; int ret = 0, playback = 0, capture = 0; int i; @@ -3150,6 +3152,44 @@ int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num) ret = snd_pcm_new_internal(rtd->card->snd_card, new_name, num, playback, capture, &pcm); } else { + + /* + * for virtual FE dai links, there is no need + * to register PCM device. So only allocate memory for + * pcm device and substream for the requested direction + */ + if (rtd->dai_link->virtual) { + struct snd_pcm_str *pstr; + + if (rtd->dai_link->dpcm_playback) + stream_dir = SNDRV_PCM_STREAM_PLAYBACK; + + pcm = kzalloc(sizeof(*pcm), GFP_KERNEL); + if (!pcm) + return -ENOMEM; + + pstr = &pcm->streams[stream_dir]; + + substream = kzalloc(sizeof(*substream), GFP_KERNEL); + if (!substream) + return -ENOMEM; + + substream->pcm = pcm; + substream->pstr = pstr; + substream->number = 0; + substream->stream = stream_dir; + sprintf(substream->name, "subdevice #%i", 0); + substream->buffer_bytes_max = UINT_MAX; + + pstr->substream = substream; + + pcm->nonatomic = rtd->dai_link->nonatomic; + rtd->pcm = pcm; + pcm->private_data = rtd; + + goto out; + } + if (rtd->dai_link->dynamic) snprintf(new_name, sizeof(new_name), "%s (*)", rtd->dai_link->stream_name); From 3bfa4e1060d338a56306ecd4e890f331355e6430 Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Sat, 23 Jun 2018 21:46:34 -0700 Subject: [PATCH 14/15] ASoC: pcm: make soc_dpcm_runtime_update() accessible from modules the soc_dpcm_runtime_update() method will be called to establish a connection to the BE and enable the codec. So make this method accessible to modules. Signed-off-by: Ranjani Sridharan --- sound/soc/soc-pcm.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index 64f459030f1c68..40a3f34ef2d66d 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -2758,6 +2758,8 @@ int soc_dpcm_runtime_update(struct snd_soc_card *card) mutex_unlock(&card->mutex); return 0; } +EXPORT_SYMBOL_GPL(soc_dpcm_runtime_update); + int soc_dpcm_be_digital_mute(struct snd_soc_pcm_runtime *fe, int mute) { struct snd_soc_dpcm *dpcm; From 0dfc4317b6b78f0c8072de32266dbcdd8ea4625d Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Sat, 23 Jun 2018 22:31:11 -0700 Subject: [PATCH 15/15] ASoC: SOF: enable BE dai before triggering siggen pipeline Enable BE dai before triggering the siggen pipeline by calling the soc_dpcm_runtime_update() method. Signed-off-by: Ranjani Sridharan --- sound/soc/sof/control.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sound/soc/sof/control.c b/sound/soc/sof/control.c index b912f8265a28b4..b4ca0b1aad6544 100644 --- a/sound/soc/sof/control.c +++ b/sound/soc/sof/control.c @@ -102,6 +102,9 @@ static int siggen_pipeline_trigger(struct snd_sof_control *scontrol, SOF_IPC_STREAM_PCM_FREE); } else { + /* enable BE DAI */ + soc_dpcm_runtime_update(card); + /* set pcm params */ ret = siggen_pcm_params(scontrol, sdev); if (ret < 0)