Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions include/uapi/sound/sof-ipc.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
4 changes: 4 additions & 0 deletions include/uapi/sound/sof-topology.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -88,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
2 changes: 2 additions & 0 deletions sound/soc/soc-dapm.c
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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);
Expand Down
234 changes: 234 additions & 0 deletions sound/soc/sof/control.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,101 @@
#include <uapi/sound/sof-ipc.h>
#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;
}

/* 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)
Expand Down Expand Up @@ -195,3 +290,142 @@ 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;
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;

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;
}

/*
* 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:

/*
* 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;
}

/*
* 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;
}

pm_runtime_mark_last_busy(sdev->dev);
pm_runtime_put_autosuspend(sdev->dev);
return changed;
}
4 changes: 4 additions & 0 deletions sound/soc/sof/sof-priv.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
34 changes: 32 additions & 2 deletions sound/soc/sof/topology.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand Down Expand Up @@ -1172,10 +1174,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);
Expand All @@ -1202,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,
Expand Down Expand Up @@ -1932,6 +1961,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 */
Expand Down