ASoC: SOF: topology: Make widget binding IPC agnostic

Make widget binding in the topology parser IPC agnostic by introducing a
new op, bind_event, in struct ipc_tplg_widget_ops. Also set the op
for all widget types in the IPC3 topology ops.

Signed-off-by: Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
Reviewed-by: Bard Liao <yung-chuan.liao@linux.intel.com>
Reviewed-by: Péter Ujfalusi <peter.ujfalusi@linux.intel.com>
Reviewed-by: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
Link: https://lore.kernel.org/r/20220314200520.1233427-19-ranjani.sridharan@linux.intel.com
Signed-off-by: Mark Brown <broonie@kernel.org>
This commit is contained in:
Ranjani Sridharan 2022-03-14 13:05:19 -07:00 committed by Mark Brown
parent b5cee8feb1
commit 8ef1439c51
No known key found for this signature in database
GPG key ID: 24D68B725D5487D0
2 changed files with 202 additions and 207 deletions

View file

@ -8,6 +8,7 @@
//
#include <uapi/sound/sof/tokens.h>
#include <sound/pcm_params.h>
#include "sof-priv.h"
#include "sof-audio.h"
#include "ops.h"
@ -1698,6 +1699,194 @@ static int sof_ipc3_control_free(struct snd_sof_dev *sdev, struct snd_sof_contro
return sof_ipc_tx_message(sdev->ipc, fcomp.hdr.cmd, &fcomp, sizeof(fcomp), NULL, 0);
}
/* send pcm params ipc */
static int sof_ipc3_keyword_detect_pcm_params(struct snd_sof_widget *swidget, int dir)
{
struct snd_soc_component *scomp = swidget->scomp;
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
struct sof_ipc_pcm_params_reply ipc_params_reply;
struct snd_pcm_hw_params *params;
struct sof_ipc_pcm_params pcm;
struct snd_sof_pcm *spcm;
int ret;
/* get runtime PCM params using widget's stream name */
spcm = snd_sof_find_spcm_name(scomp, swidget->widget->sname);
if (!spcm) {
dev_err(scomp->dev, "Cannot find PCM for %s\n", swidget->widget->name);
return -EINVAL;
}
params = &spcm->params[dir];
/* set IPC PCM params */
memset(&pcm, 0, sizeof(pcm));
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(scomp->dev, "%s: PCM params failed for %s\n", __func__,
swidget->widget->name);
return ret;
}
/* send stream trigger ipc */
static int sof_ipc3_keyword_detect_trigger(struct snd_sof_widget *swidget, int cmd)
{
struct snd_soc_component *scomp = swidget->scomp;
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
struct sof_ipc_stream stream;
struct sof_ipc_reply reply;
int ret;
/* 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(scomp->dev, "%s: Failed to trigger %s\n", __func__, swidget->widget->name);
return ret;
}
static int sof_ipc3_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_soc_component *scomp;
int stream = SNDRV_PCM_STREAM_CAPTURE;
struct snd_sof_pcm *spcm;
int ret = 0;
if (!swidget)
return 0;
scomp = swidget->scomp;
dev_dbg(scomp->dev, "received event %d for widget %s\n",
event, w->name);
/* get runtime PCM params using widget's stream name */
spcm = snd_sof_find_spcm_name(scomp, swidget->widget->sname);
if (!spcm) {
dev_err(scomp->dev, "%s: Cannot find PCM for %s\n", __func__,
swidget->widget->name);
return -EINVAL;
}
/* process events */
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
if (spcm->stream[stream].suspend_ignored) {
dev_dbg(scomp->dev, "PRE_PMU event ignored, KWD pipeline is already RUNNING\n");
return 0;
}
/* set pcm params */
ret = sof_ipc3_keyword_detect_pcm_params(swidget, stream);
if (ret < 0) {
dev_err(scomp->dev, "%s: Failed to set pcm params for widget %s\n",
__func__, swidget->widget->name);
break;
}
/* start trigger */
ret = sof_ipc3_keyword_detect_trigger(swidget, SOF_IPC_STREAM_TRIG_START);
if (ret < 0)
dev_err(scomp->dev, "%s: Failed to trigger widget %s\n", __func__,
swidget->widget->name);
break;
case SND_SOC_DAPM_POST_PMD:
if (spcm->stream[stream].suspend_ignored) {
dev_dbg(scomp->dev,
"POST_PMD event ignored, KWD pipeline will remain RUNNING\n");
return 0;
}
/* stop trigger */
ret = sof_ipc3_keyword_detect_trigger(swidget, SOF_IPC_STREAM_TRIG_STOP);
if (ret < 0)
dev_err(scomp->dev, "%s: Failed to trigger widget %s\n", __func__,
swidget->widget->name);
/* pcm free */
ret = sof_ipc3_keyword_detect_trigger(swidget, SOF_IPC_STREAM_PCM_FREE);
if (ret < 0)
dev_err(scomp->dev, "%s: Failed to free PCM for widget %s\n", __func__,
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_ipc3_keyword_dapm_event},
};
static int sof_ipc3_widget_bind_event(struct snd_soc_component *scomp,
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(scomp->dev, "Invalid event type %d for widget %s\n", event_type,
swidget->widget->name);
return -EINVAL;
}
/* token list for each topology object */
static enum sof_tokens host_token_list[] = {
SOF_CORE_TOKENS,
@ -1790,7 +1979,8 @@ static const struct sof_ipc_tplg_widget_ops tplg_ipc3_widget_ops[SND_SOC_DAPM_TY
comp_generic_token_list, ARRAY_SIZE(comp_generic_token_list),
NULL},
[snd_soc_dapm_effect] = {sof_widget_update_ipc_comp_process, sof_ipc3_widget_free_comp,
process_token_list, ARRAY_SIZE(process_token_list), NULL},
process_token_list, ARRAY_SIZE(process_token_list),
sof_ipc3_widget_bind_event},
};
static const struct sof_ipc_tplg_ops ipc3_tplg_ops = {

View file

@ -14,7 +14,6 @@
#include <linux/firmware.h>
#include <linux/workqueue.h>
#include <sound/tlv.h>
#include <sound/pcm_params.h>
#include <uapi/sound/sof/tokens.h>
#include "sof-priv.h"
#include "sof-audio.h"
@ -135,171 +134,6 @@ int sof_update_ipc_object(struct snd_soc_component *scomp, void *object, enum so
return 0;
}
/* 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_soc_component *scomp = swidget->scomp;
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
struct sof_ipc_pcm_params pcm;
struct snd_pcm_hw_params *params;
struct snd_sof_pcm *spcm;
int ret;
memset(&pcm, 0, sizeof(pcm));
/* get runtime PCM params using widget's stream name */
spcm = snd_sof_find_spcm_name(scomp, swidget->widget->sname);
if (!spcm) {
dev_err(scomp->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(scomp->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_soc_component *scomp = swidget->scomp;
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
struct sof_ipc_stream stream;
struct sof_ipc_reply reply;
int ret;
/* 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(scomp->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_soc_component *scomp;
int stream = SNDRV_PCM_STREAM_CAPTURE;
struct snd_sof_pcm *spcm;
int ret = 0;
if (!swidget)
return 0;
scomp = swidget->scomp;
dev_dbg(scomp->dev, "received event %d for widget %s\n",
event, w->name);
/* get runtime PCM params using widget's stream name */
spcm = snd_sof_find_spcm_name(scomp, swidget->widget->sname);
if (!spcm) {
dev_err(scomp->dev, "error: cannot find PCM for %s\n",
swidget->widget->name);
return -EINVAL;
}
/* process events */
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
if (spcm->stream[stream].suspend_ignored) {
dev_dbg(scomp->dev, "PRE_PMU event ignored, KWD pipeline is already RUNNING\n");
return 0;
}
/* set pcm params */
ret = ipc_pcm_params(swidget, stream);
if (ret < 0) {
dev_err(scomp->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(scomp->dev,
"error: failed to trigger widget %s\n",
swidget->widget->name);
break;
case SND_SOC_DAPM_POST_PMD:
if (spcm->stream[stream].suspend_ignored) {
dev_dbg(scomp->dev, "POST_PMD even ignored, KWD pipeline will remain RUNNING\n");
return 0;
}
/* stop trigger */
ret = ipc_trigger(swidget, SOF_IPC_STREAM_TRIG_STOP);
if (ret < 0)
dev_err(scomp->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(scomp->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 */
@ -1323,38 +1157,6 @@ static int sof_widget_parse_tokens(struct snd_soc_component *scomp, struct snd_s
return ret;
}
static int sof_widget_bind_event(struct snd_soc_component *scomp,
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(scomp->dev,
"error: invalid event type %d for widget %s\n",
event_type, swidget->widget->name);
return -EINVAL;
}
static int sof_get_token_value(u32 token_id, struct snd_sof_tuple *tuples, int num_tuples)
{
int i;
@ -1486,14 +1288,17 @@ static int sof_widget_ready(struct snd_soc_component *scomp, int index,
/* bind widget to external event */
if (tw->event_type) {
ret = sof_widget_bind_event(scomp, swidget,
le16_to_cpu(tw->event_type));
if (ret) {
dev_err(scomp->dev, "error: widget event binding failed\n");
kfree(swidget->private);
kfree(swidget->tuples);
kfree(swidget);
return ret;
if (widget_ops[w->id].bind_event) {
ret = widget_ops[w->id].bind_event(scomp, swidget,
le16_to_cpu(tw->event_type));
if (ret) {
dev_err(scomp->dev, "widget event binding failed for %s\n",
swidget->widget->name);
kfree(swidget->private);
kfree(swidget->tuples);
kfree(swidget);
return ret;
}
}
}