From 5439a0801c80dfaf8dcf18094b7095c12313b1e0 Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Wed, 23 May 2018 18:45:42 -0700 Subject: [PATCH 1/7] 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 2/7] 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 3/7] 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 4/7] 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 5/7] 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 6/7] 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 7/7] 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;