aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/sound/soc-dpcm.h1
-rw-r--r--include/sound/soc-topology.h2
-rw-r--r--include/uapi/sound/sof/tokens.h1
-rw-r--r--sound/soc/intel/skylake/skl-topology.c6
-rw-r--r--sound/soc/soc-dapm.c2
-rw-r--r--sound/soc/soc-pcm.c4
-rw-r--r--sound/soc/soc-topology.c10
-rw-r--r--sound/soc/sof/intel/hda-dai.c174
-rw-r--r--sound/soc/sof/intel/hda.c177
-rw-r--r--sound/soc/sof/intel/hda.h5
-rw-r--r--sound/soc/sof/ipc.c22
-rw-r--r--sound/soc/sof/pcm.c58
-rw-r--r--sound/soc/sof/pm.c4
-rw-r--r--sound/soc/sof/sof-audio.c709
-rw-r--r--sound/soc/sof/sof-audio.h32
-rw-r--r--sound/soc/sof/sof-priv.h1
-rw-r--r--sound/soc/sof/topology.c362
17 files changed, 1032 insertions, 538 deletions
diff --git a/include/sound/soc-dpcm.h b/include/sound/soc-dpcm.h
index e296a3949b18..bc7af90099a8 100644
--- a/include/sound/soc-dpcm.h
+++ b/include/sound/soc-dpcm.h
@@ -159,6 +159,7 @@ int dpcm_be_dai_trigger(struct snd_soc_pcm_runtime *fe, int stream, int cmd);
int dpcm_be_dai_prepare(struct snd_soc_pcm_runtime *fe, int stream);
int dpcm_dapm_stream_event(struct snd_soc_pcm_runtime *fe, int dir,
int event);
+bool dpcm_end_walk_at_be(struct snd_soc_dapm_widget *widget, enum snd_soc_dapm_direction dir);
#define dpcm_be_dai_startup_rollback(fe, stream, last) \
dpcm_be_dai_stop(fe, stream, 0, last)
diff --git a/include/sound/soc-topology.h b/include/sound/soc-topology.h
index 4afd667e124c..7f33de8ffd95 100644
--- a/include/sound/soc-topology.h
+++ b/include/sound/soc-topology.h
@@ -151,7 +151,7 @@ struct snd_soc_tplg_ops {
struct snd_soc_tplg_hdr *);
/* completion - called at completion of firmware loading */
- void (*complete)(struct snd_soc_component *);
+ int (*complete)(struct snd_soc_component *comp);
/* manifest - optional to inform component of manifest */
int (*manifest)(struct snd_soc_component *, int index,
diff --git a/include/uapi/sound/sof/tokens.h b/include/uapi/sound/sof/tokens.h
index a642bf30c027..02b71a8deea4 100644
--- a/include/uapi/sound/sof/tokens.h
+++ b/include/uapi/sound/sof/tokens.h
@@ -51,6 +51,7 @@
#define SOF_TKN_SCHED_CORE 203
#define SOF_TKN_SCHED_FRAMES 204
#define SOF_TKN_SCHED_TIME_DOMAIN 205
+#define SOF_TKN_SCHED_DYNAMIC_PIPELINE 206
/* volume */
#define SOF_TKN_VOLUME_RAMP_STEP_TYPE 250
diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c
index b036852d6889..89e4231304dd 100644
--- a/sound/soc/intel/skylake/skl-topology.c
+++ b/sound/soc/intel/skylake/skl-topology.c
@@ -3637,7 +3637,7 @@ static int skl_manifest_load(struct snd_soc_component *cmpnt, int index,
return 0;
}
-static void skl_tplg_complete(struct snd_soc_component *component)
+static int skl_tplg_complete(struct snd_soc_component *component)
{
struct snd_soc_dobj *dobj;
struct snd_soc_acpi_mach *mach;
@@ -3646,7 +3646,7 @@ static void skl_tplg_complete(struct snd_soc_component *component)
val = kmalloc(sizeof(*val), GFP_KERNEL);
if (!val)
- return;
+ return -ENOMEM;
mach = dev_get_platdata(component->card->dev);
list_for_each_entry(dobj, &component->dobj_list, list) {
@@ -3671,7 +3671,9 @@ static void skl_tplg_complete(struct snd_soc_component *component)
}
}
}
+
kfree(val);
+ return 0;
}
static struct snd_soc_tplg_ops skl_tplg_ops = {
diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c
index 7b67f1e19ae9..44c4d105ffdb 100644
--- a/sound/soc/soc-dapm.c
+++ b/sound/soc/soc-dapm.c
@@ -1331,11 +1331,13 @@ int snd_soc_dapm_dai_get_connected_widgets(struct snd_soc_dai *dai, int stream,
return paths;
}
+EXPORT_SYMBOL_GPL(snd_soc_dapm_dai_get_connected_widgets);
void snd_soc_dapm_dai_free_widgets(struct snd_soc_dapm_widget_list **list)
{
dapm_widget_list_free(list);
}
+EXPORT_SYMBOL_GPL(snd_soc_dapm_dai_free_widgets);
/*
* Handler for regulator supply widget.
diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c
index e30cb5abaf7a..b5825d8d6caa 100644
--- a/sound/soc/soc-pcm.c
+++ b/sound/soc/soc-pcm.c
@@ -1262,8 +1262,7 @@ static int widget_in_list(struct snd_soc_dapm_widget_list *list,
return 0;
}
-static bool dpcm_end_walk_at_be(struct snd_soc_dapm_widget *widget,
- enum snd_soc_dapm_direction dir)
+bool dpcm_end_walk_at_be(struct snd_soc_dapm_widget *widget, enum snd_soc_dapm_direction dir)
{
struct snd_soc_card *card = widget->dapm->card;
struct snd_soc_pcm_runtime *rtd;
@@ -1281,6 +1280,7 @@ static bool dpcm_end_walk_at_be(struct snd_soc_dapm_widget *widget,
return false;
}
+EXPORT_SYMBOL_GPL(dpcm_end_walk_at_be);
int dpcm_path_get(struct snd_soc_pcm_runtime *fe,
int stream, struct snd_soc_dapm_widget_list **list)
diff --git a/sound/soc/soc-topology.c b/sound/soc/soc-topology.c
index 73e1b7b48ce9..88f849b119da 100644
--- a/sound/soc/soc-topology.c
+++ b/sound/soc/soc-topology.c
@@ -78,7 +78,7 @@ struct soc_tplg {
};
static int soc_tplg_process_headers(struct soc_tplg *tplg);
-static void soc_tplg_complete(struct soc_tplg *tplg);
+static int soc_tplg_complete(struct soc_tplg *tplg);
/* check we dont overflow the data for this control chunk */
static int soc_tplg_check_elem_count(struct soc_tplg *tplg, size_t elem_size,
@@ -312,10 +312,12 @@ static int soc_tplg_dai_link_load(struct soc_tplg *tplg,
}
/* tell the component driver that all firmware has been loaded in this request */
-static void soc_tplg_complete(struct soc_tplg *tplg)
+static int soc_tplg_complete(struct soc_tplg *tplg)
{
if (tplg->ops && tplg->ops->complete)
- tplg->ops->complete(tplg->comp);
+ return tplg->ops->complete(tplg->comp);
+
+ return 0;
}
/* add a dynamic kcontrol */
@@ -2625,7 +2627,7 @@ static int soc_tplg_load(struct soc_tplg *tplg)
ret = soc_tplg_process_headers(tplg);
if (ret == 0)
- soc_tplg_complete(tplg);
+ return soc_tplg_complete(tplg);
return ret;
}
diff --git a/sound/soc/sof/intel/hda-dai.c b/sound/soc/sof/intel/hda-dai.c
index 2f54a659b78a..59d6750c6a20 100644
--- a/sound/soc/sof/intel/hda-dai.c
+++ b/sound/soc/sof/intel/hda-dai.c
@@ -155,49 +155,68 @@ static int hda_link_dma_params(struct hdac_ext_stream *stream,
return 0;
}
-/* Send DAI_CONFIG IPC to the DAI that matches the dai_name and direction */
-static int hda_link_config_ipc(struct sof_intel_hda_stream *hda_stream,
- const char *dai_name, int channel, int dir)
+/* Update config for the DAI widget */
+static struct sof_ipc_dai_config *hda_dai_update_config(struct snd_soc_dapm_widget *w,
+ int channel)
{
+ struct snd_sof_widget *swidget = w->dobj.private;
struct sof_ipc_dai_config *config;
struct snd_sof_dai *sof_dai;
- struct sof_ipc_reply reply;
- int ret = 0;
- list_for_each_entry(sof_dai, &hda_stream->sdev->dai_list, list) {
- if (!sof_dai->cpu_dai_name)
- continue;
+ if (!swidget)
+ return NULL;
- if (!strcmp(dai_name, sof_dai->cpu_dai_name) &&
- dir == sof_dai->comp_dai.direction) {
- config = sof_dai->dai_config;
+ sof_dai = swidget->private;
- if (!config) {
- dev_err(hda_stream->sdev->dev,
- "error: no config for DAI %s\n",
- sof_dai->name);
- return -EINVAL;
- }
+ if (!sof_dai || !sof_dai->dai_config) {
+ dev_err(swidget->scomp->dev, "error: No config for DAI %s\n", w->name);
+ return NULL;
+ }
- /* update config with stream tag */
- config->hda.link_dma_ch = channel;
+ config = &sof_dai->dai_config[sof_dai->current_config];
- /* send IPC */
- ret = sof_ipc_tx_message(hda_stream->sdev->ipc,
- config->hdr.cmd,
- config,
- config->hdr.size,
- &reply, sizeof(reply));
+ /* update config with stream tag */
+ config->hda.link_dma_ch = channel;
- if (ret < 0)
- dev_err(hda_stream->sdev->dev,
- "error: failed to set dai config for %s\n",
- sof_dai->name);
- return ret;
- }
+ return config;
+}
+
+static int hda_link_config_ipc(struct sof_intel_hda_stream *hda_stream,
+ struct snd_soc_dapm_widget *w, int channel)
+{
+ struct snd_sof_dev *sdev = hda_stream->sdev;
+ struct sof_ipc_dai_config *config;
+ struct sof_ipc_reply reply;
+
+ config = hda_dai_update_config(w, channel);
+ if (!config) {
+ dev_err(sdev->dev, "error: no config for DAI %s\n", w->name);
+ return -ENOENT;
+ }
+
+ /* send DAI_CONFIG IPC */
+ return sof_ipc_tx_message(sdev->ipc, config->hdr.cmd, config, config->hdr.size,
+ &reply, sizeof(reply));
+}
+
+static int hda_link_dai_widget_update(struct sof_intel_hda_stream *hda_stream,
+ struct snd_soc_dapm_widget *w,
+ int channel, bool widget_setup)
+{
+ struct snd_sof_dev *sdev = hda_stream->sdev;
+ struct sof_ipc_dai_config *config;
+
+ config = hda_dai_update_config(w, channel);
+ if (!config) {
+ dev_err(sdev->dev, "error: no config for DAI %s\n", w->name);
+ return -ENOENT;
}
- return -EINVAL;
+ /* set up/free DAI widget and send DAI_CONFIG IPC */
+ if (widget_setup)
+ return hda_ctrl_dai_widget_setup(w);
+
+ return hda_ctrl_dai_widget_free(w);
}
static int hda_link_hw_params(struct snd_pcm_substream *substream,
@@ -211,6 +230,7 @@ static int hda_link_hw_params(struct snd_pcm_substream *substream,
struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
struct sof_intel_hda_stream *hda_stream;
struct hda_pipe_params p_params = {0};
+ struct snd_soc_dapm_widget *w;
struct hdac_ext_link *link;
int stream_tag;
int ret;
@@ -229,9 +249,13 @@ static int hda_link_hw_params(struct snd_pcm_substream *substream,
hda_stream = hstream_to_sof_hda_stream(link_dev);
- /* update the DSP with the new tag */
- ret = hda_link_config_ipc(hda_stream, dai->name, stream_tag - 1,
- substream->stream);
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ w = dai->playback_widget;
+ else
+ w = dai->capture_widget;
+
+ /* set up the DAI widget and send the DAI_CONFIG with the new tag */
+ ret = hda_link_dai_widget_update(hda_stream, w, stream_tag - 1, true);
if (ret < 0)
return ret;
@@ -287,6 +311,7 @@ static int hda_link_pcm_trigger(struct snd_pcm_substream *substream,
snd_soc_dai_get_dma_data(dai, substream);
struct sof_intel_hda_stream *hda_stream;
struct snd_soc_pcm_runtime *rtd;
+ struct snd_soc_dapm_widget *w;
struct hdac_ext_link *link;
struct hdac_stream *hstream;
struct hdac_bus *bus;
@@ -321,12 +346,16 @@ static int hda_link_pcm_trigger(struct snd_pcm_substream *substream,
break;
case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_STOP:
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ w = dai->playback_widget;
+ else
+ w = dai->capture_widget;
+
/*
* clear link DMA channel. It will be assigned when
* hw_params is set up again after resume.
*/
- ret = hda_link_config_ipc(hda_stream, dai->name,
- DMA_CHAN_INVALID, substream->stream);
+ ret = hda_link_config_ipc(hda_stream, w, DMA_CHAN_INVALID);
if (ret < 0)
return ret;
@@ -357,6 +386,7 @@ static int hda_link_hw_free(struct snd_pcm_substream *substream,
struct hdac_stream *hstream;
struct snd_soc_pcm_runtime *rtd;
struct hdac_ext_stream *link_dev;
+ struct snd_soc_dapm_widget *w;
int ret;
hstream = substream->runtime->private_data;
@@ -372,9 +402,13 @@ static int hda_link_hw_free(struct snd_pcm_substream *substream,
hda_stream = hstream_to_sof_hda_stream(link_dev);
- /* free the link DMA channel in the FW */
- ret = hda_link_config_ipc(hda_stream, dai->name, DMA_CHAN_INVALID,
- substream->stream);
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ w = dai->playback_widget;
+ else
+ w = dai->capture_widget;
+
+ /* free the link DMA channel in the FW and the DAI widget */
+ ret = hda_link_dai_widget_update(hda_stream, w, DMA_CHAN_INVALID, false);
if (ret < 0)
return ret;
@@ -406,47 +440,51 @@ static const struct snd_soc_dai_ops hda_link_dai_ops = {
#endif
-static int ssp_dai_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params,
- struct snd_soc_dai *dai)
+static int ssp_dai_setup_or_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai,
+ bool setup)
{
- struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
- struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, SOF_AUDIO_PCM_DRV_NAME);
- struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component);
- struct sof_ipc_fw_version *v = &sdev->fw_ready.version;
- struct sof_ipc_dai_config *config;
- struct snd_sof_dai *sof_dai;
- struct sof_ipc_reply reply;
- int ret;
+ struct snd_soc_component *component;
+ struct snd_sof_widget *swidget;
+ struct snd_soc_dapm_widget *w;
+ struct sof_ipc_fw_version *v;
+ struct snd_sof_dev *sdev;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ w = dai->playback_widget;
+ else
+ w = dai->capture_widget;
+
+ swidget = w->dobj.private;
+ component = swidget->scomp;
+ sdev = snd_soc_component_get_drvdata(component);
+ v = &sdev->fw_ready.version;
/* DAI_CONFIG IPC during hw_params is not supported in older firmware */
if (v->abi_version < SOF_ABI_VER(3, 18, 0))
return 0;
- list_for_each_entry(sof_dai, &sdev->dai_list, list) {
- if (!sof_dai->cpu_dai_name || !sof_dai->dai_config)
- continue;
-
- if (!strcmp(dai->name, sof_dai->cpu_dai_name) &&
- substream->stream == sof_dai->comp_dai.direction) {
- config = &sof_dai->dai_config[sof_dai->current_config];
+ if (setup)
+ return hda_ctrl_dai_widget_setup(w);
- /* send IPC */
- ret = sof_ipc_tx_message(sdev->ipc, config->hdr.cmd, config,
- config->hdr.size, &reply, sizeof(reply));
+ return hda_ctrl_dai_widget_free(w);
+}
- if (ret < 0)
- dev_err(sdev->dev, "error: failed to set DAI config for %s\n",
- sof_dai->name);
- return ret;
- }
- }
+static int ssp_dai_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ return ssp_dai_setup_or_free(substream, dai, true);
+}
- return 0;
+static int ssp_dai_hw_free(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ return ssp_dai_setup_or_free(substream, dai, false);
}
static const struct snd_soc_dai_ops ssp_dai_ops = {
.hw_params = ssp_dai_hw_params,
+ .hw_free = ssp_dai_hw_free,
};
/*
diff --git a/sound/soc/sof/intel/hda.c b/sound/soc/sof/intel/hda.c
index c11e4c14d875..066e90506ae6 100644
--- a/sound/soc/sof/intel/hda.c
+++ b/sound/soc/sof/intel/hda.c
@@ -41,6 +41,86 @@
#define EXCEPT_MAX_HDR_SIZE 0x400
#define HDA_EXT_ROM_STATUS_SIZE 8
+int hda_ctrl_dai_widget_setup(struct snd_soc_dapm_widget *w)
+{
+ struct snd_sof_widget *swidget = w->dobj.private;
+ struct snd_soc_component *component = swidget->scomp;
+ struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component);
+ struct sof_ipc_dai_config *config;
+ struct snd_sof_dai *sof_dai;
+ struct sof_ipc_reply reply;
+ int ret;
+
+ sof_dai = swidget->private;
+
+ if (!sof_dai || !sof_dai->dai_config) {
+ dev_err(sdev->dev, "No config for DAI %s\n", w->name);
+ return -EINVAL;
+ }
+
+ config = &sof_dai->dai_config[sof_dai->current_config];
+
+ /*
+ * For static pipelines, the DAI widget would already be set up and calling
+ * sof_widget_setup() simply returns without doing anything.
+ * For dynamic pipelines, the DAI widget will be set up now.
+ */
+ ret = sof_widget_setup(sdev, swidget);
+ if (ret < 0) {
+ dev_err(sdev->dev, "error: failed setting up DAI widget %s\n", w->name);
+ return ret;
+ }
+
+ /* send DAI_CONFIG IPC */
+ ret = sof_ipc_tx_message(sdev->ipc, config->hdr.cmd, config, config->hdr.size,
+ &reply, sizeof(reply));
+ if (ret < 0) {
+ dev_err(sdev->dev, "error: failed setting DAI config for %s\n", w->name);
+ return ret;
+ }
+
+ sof_dai->configured = true;
+
+ return 0;
+}
+
+int hda_ctrl_dai_widget_free(struct snd_soc_dapm_widget *w)
+{
+ struct snd_sof_widget *swidget = w->dobj.private;
+ struct snd_soc_component *component = swidget->scomp;
+ struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component);
+ struct sof_ipc_dai_config *config;
+ struct snd_sof_dai *sof_dai;
+ struct sof_ipc_reply reply;
+ int ret;
+
+ sof_dai = swidget->private;
+
+ if (!sof_dai || !sof_dai->dai_config) {
+ dev_err(sdev->dev, "error: No config to free DAI %s\n", w->name);
+ return -EINVAL;
+ }
+
+ /* nothing to do if hw_free() is called without restarting the stream after resume. */
+ if (!sof_dai->configured)
+ return 0;
+
+ config = &sof_dai->dai_config[sof_dai->current_config];
+
+ ret = sof_ipc_tx_message(sdev->ipc, config->hdr.cmd, config, config->hdr.size,
+ &reply, sizeof(reply));
+ if (ret < 0)
+ dev_err(sdev->dev, "error: failed resetting DAI config for %s\n", w->name);
+
+ /*
+ * Reset the configured_flag and free the widget even if the IPC fails to keep
+ * the widget use_count balanced
+ */
+ sof_dai->configured = false;
+
+ return sof_widget_free(sdev, swidget);
+}
+
static const struct sof_intel_dsp_desc
*get_chip_info(struct snd_sof_pdata *pdata)
{
@@ -64,67 +144,70 @@ static int sdw_clock_stop_quirks = SDW_INTEL_CLK_STOP_BUS_RESET;
module_param(sdw_clock_stop_quirks, int, 0444);
MODULE_PARM_DESC(sdw_clock_stop_quirks, "SOF SoundWire clock stop quirks");
+static int sdw_dai_config_ipc(struct snd_sof_dev *sdev,
+ struct snd_soc_dapm_widget *w,
+ int link_id, int alh_stream_id, int dai_id, bool setup)
+{
+ struct snd_sof_widget *swidget = w->dobj.private;
+ struct sof_ipc_dai_config *config;
+ struct snd_sof_dai *sof_dai;
+
+ if (!swidget) {
+ dev_err(sdev->dev, "error: No private data for widget %s\n", w->name);
+ return -EINVAL;
+ }
+
+ sof_dai = swidget->private;
+
+ if (!sof_dai || !sof_dai->dai_config) {
+ dev_err(sdev->dev, "error: No config for DAI %s\n", w->name);
+ return -EINVAL;
+ }
+
+ config = &sof_dai->dai_config[sof_dai->current_config];
+
+ /* update config with link and stream ID */
+ config->dai_index = (link_id << 8) | dai_id;
+ config->alh.stream_id = alh_stream_id;
+
+ if (setup)
+ return hda_ctrl_dai_widget_setup(w);
+
+ return hda_ctrl_dai_widget_free(w);
+}
+
static int sdw_params_stream(struct device *dev,
struct sdw_intel_stream_params_data *params_data)
{
+ struct snd_pcm_substream *substream = params_data->substream;
struct snd_sof_dev *sdev = dev_get_drvdata(dev);
struct snd_soc_dai *d = params_data->dai;
- struct sof_ipc_dai_config config;
- struct sof_ipc_reply reply;
- int link_id = params_data->link_id;
- int alh_stream_id = params_data->alh_stream_id;
- int ret;
- u32 size = sizeof(config);
-
- memset(&config, 0, size);
- config.hdr.size = size;
- config.hdr.cmd = SOF_IPC_GLB_DAI_MSG | SOF_IPC_DAI_CONFIG;
- config.type = SOF_DAI_INTEL_ALH;
- config.dai_index = (link_id << 8) | (d->id);
- config.alh.stream_id = alh_stream_id;
-
- /* send message to DSP */
- ret = sof_ipc_tx_message(sdev->ipc,
- config.hdr.cmd, &config, size, &reply,
- sizeof(reply));
- if (ret < 0) {
- dev_err(sdev->dev,
- "error: failed to set DAI hw_params for link %d dai->id %d ALH %d\n",
- link_id, d->id, alh_stream_id);
- }
+ struct snd_soc_dapm_widget *w;
- return ret;
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ w = d->playback_widget;
+ else
+ w = d->capture_widget;
+
+ return sdw_dai_config_ipc(sdev, w, params_data->link_id, params_data->alh_stream_id,
+ d->id, true);
}
static int sdw_free_stream(struct device *dev,
struct sdw_intel_stream_free_data *free_data)
{
+ struct snd_pcm_substream *substream = free_data->substream;
struct snd_sof_dev *sdev = dev_get_drvdata(dev);
struct snd_soc_dai *d = free_data->dai;
- struct sof_ipc_dai_config config;
- struct sof_ipc_reply reply;
- int link_id = free_data->link_id;
- int ret;
- u32 size = sizeof(config);
-
- memset(&config, 0, size);
- config.hdr.size = size;
- config.hdr.cmd = SOF_IPC_GLB_DAI_MSG | SOF_IPC_DAI_CONFIG;
- config.type = SOF_DAI_INTEL_ALH;
- config.dai_index = (link_id << 8) | d->id;
- config.alh.stream_id = 0xFFFF; /* invalid value on purpose */
-
- /* send message to DSP */
- ret = sof_ipc_tx_message(sdev->ipc,
- config.hdr.cmd, &config, size, &reply,
- sizeof(reply));
- if (ret < 0) {
- dev_err(sdev->dev,
- "error: failed to free stream for link %d dai->id %d\n",
- link_id, d->id);
- }
+ struct snd_soc_dapm_widget *w;
- return ret;
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ w = d->playback_widget;
+ else
+ w = d->capture_widget;
+
+ /* send invalid stream_id */
+ return sdw_dai_config_ipc(sdev, w, free_data->link_id, 0xFFFF, d->id, false);
}
static const struct sdw_intel_ops sdw_callback = {
diff --git a/sound/soc/sof/intel/hda.h b/sound/soc/sof/intel/hda.h
index cb5a1b17f153..1195018a1f4f 100644
--- a/sound/soc/sof/intel/hda.h
+++ b/sound/soc/sof/intel/hda.h
@@ -733,4 +733,9 @@ void hda_set_mach_params(const struct snd_soc_acpi_mach *mach,
/* PCI driver selection and probe */
int hda_pci_intel_probe(struct pci_dev *pci, const struct pci_device_id *pci_id);
+struct snd_sof_dai;
+struct sof_ipc_dai_config;
+int hda_ctrl_dai_widget_setup(struct snd_soc_dapm_widget *w);
+int hda_ctrl_dai_widget_free(struct snd_soc_dapm_widget *w);
+
#endif
diff --git a/sound/soc/sof/ipc.c b/sound/soc/sof/ipc.c
index 0034e7a34a1d..53593df95155 100644
--- a/sound/soc/sof/ipc.c
+++ b/sound/soc/sof/ipc.c
@@ -744,9 +744,31 @@ int snd_sof_ipc_set_get_comp_data(struct snd_sof_control *scontrol,
struct sof_ipc_fw_ready *ready = &sdev->fw_ready;
struct sof_ipc_fw_version *v = &ready->version;
struct sof_ipc_ctrl_data_params sparams;
+ struct snd_sof_widget *swidget;
+ bool widget_found = false;
size_t send_bytes;
int err;
+ list_for_each_entry(swidget, &sdev->widget_list, list) {
+ if (swidget->comp_id == scontrol->comp_id) {
+ widget_found = true;
+ break;
+ }
+ }
+
+ if (!widget_found) {
+ dev_err(sdev->dev, "error: can't find widget with id %d\n", scontrol->comp_id);
+ return -EINVAL;
+ }
+
+ /*
+ * Volatile controls should always be part of static pipelines and the widget use_count
+ * would always be > 0 in this case. For the others, just return the cached value if the
+ * widget is not set up.
+ */
+ if (!swidget->use_count)
+ return 0;
+
/* read or write firmware volume */
if (scontrol->readback_offset != 0) {
/* write/read value header via mmaped region */
diff --git a/sound/soc/sof/pcm.c b/sound/soc/sof/pcm.c
index b4280459e5db..374df2dfa816 100644
--- a/sound/soc/sof/pcm.c
+++ b/sound/soc/sof/pcm.c
@@ -116,6 +116,40 @@ static int sof_pcm_dsp_pcm_free(struct snd_pcm_substream *substream,
return ret;
}
+static int sof_pcm_setup_connected_widgets(struct snd_sof_dev *sdev,
+ struct snd_soc_pcm_runtime *rtd,
+ struct snd_sof_pcm *spcm, int dir)
+{
+ struct snd_soc_dai *dai;
+ int ret, j;
+
+ /* query DAPM for list of connected widgets and set them up */
+ for_each_rtd_cpu_dais(rtd, j, dai) {
+ struct snd_soc_dapm_widget_list *list;
+
+ ret = snd_soc_dapm_dai_get_connected_widgets(dai, dir, &list,
+ dpcm_end_walk_at_be);
+ if (ret < 0) {
+ dev_err(sdev->dev, "error: dai %s has no valid %s path\n", dai->name,
+ dir == SNDRV_PCM_STREAM_PLAYBACK ? "playback" : "capture");
+ return ret;
+ }
+
+ spcm->stream[dir].list = list;
+
+ ret = sof_widget_list_setup(sdev, spcm, dir);
+ if (ret < 0) {
+ dev_err(sdev->dev, "error: failed widget list set up for pcm %d dir %d\n",
+ spcm->pcm.pcm_id, dir);
+ spcm->stream[dir].list = NULL;
+ snd_soc_dapm_dai_free_widgets(&list);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
static int sof_pcm_hw_params(struct snd_soc_component *component,
struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
@@ -213,7 +247,14 @@ static int sof_pcm_hw_params(struct snd_soc_component *component,
dev_dbg(component->dev, "stream_tag %d", pcm.params.stream_tag);
- /* send IPC to the DSP */
+ /* if this is a repeated hw_params without hw_free, skip setting up widgets */
+ if (!spcm->stream[substream->stream].list) {
+ ret = sof_pcm_setup_connected_widgets(sdev, rtd, spcm, substream->stream);
+ if (ret < 0)
+ return ret;
+ }
+
+ /* send hw_params 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) {
@@ -259,6 +300,10 @@ static int sof_pcm_hw_free(struct snd_soc_component *component,
err = ret;
}
+ ret = sof_widget_list_free(sdev, spcm, substream->stream);
+ if (ret < 0)
+ err = ret;
+
cancel_work_sync(&spcm->stream[substream->stream].period_elapsed_work);
ret = snd_sof_pcm_platform_hw_free(sdev, substream);
@@ -316,6 +361,7 @@ static int sof_pcm_trigger(struct snd_soc_component *component,
struct sof_ipc_stream stream;
struct sof_ipc_reply reply;
bool reset_hw_params = false;
+ bool free_widget_list = false;
bool ipc_first = false;
int ret;
@@ -386,6 +432,7 @@ static int sof_pcm_trigger(struct snd_soc_component *component,
spcm->stream[substream->stream].suspend_ignored = true;
return 0;
}
+ free_widget_list = true;
fallthrough;
case SNDRV_PCM_TRIGGER_STOP:
stream.hdr.cmd |= SOF_IPC_STREAM_TRIG_STOP;
@@ -414,8 +461,15 @@ static int sof_pcm_trigger(struct snd_soc_component *component,
snd_sof_pcm_platform_trigger(sdev, substream, cmd);
/* free PCM if reset_hw_params is set and the STOP IPC is successful */
- if (!ret && reset_hw_params)
+ if (!ret && reset_hw_params) {
ret = sof_pcm_dsp_pcm_free(substream, sdev, spcm);
+ if (ret < 0)
+ return ret;
+
+ /* free widget list only for SUSPEND trigger */
+ if (free_widget_list)
+ ret = sof_widget_list_free(sdev, spcm, substream->stream);
+ }
return ret;
}
diff --git a/sound/soc/sof/pm.c b/sound/soc/sof/pm.c
index 89b4b3d76539..77a3496d3dbd 100644
--- a/sound/soc/sof/pm.c
+++ b/sound/soc/sof/pm.c
@@ -157,7 +157,7 @@ static int sof_resume(struct device *dev, bool runtime_resume)
}
/* restore pipelines */
- ret = sof_restore_pipelines(sdev->dev);
+ ret = sof_set_up_pipelines(sdev->dev, false);
if (ret < 0) {
dev_err(sdev->dev,
"error: failed to restore pipeline after resume %d\n",
@@ -208,6 +208,8 @@ static int sof_suspend(struct device *dev, bool runtime_suspend)
if (target_state == SOF_DSP_PM_D0)
goto suspend;
+ sof_tear_down_pipelines(dev, false);
+
/* release trace */
snd_sof_release_trace(sdev);
diff --git a/sound/soc/sof/sof-audio.c b/sound/soc/sof/sof-audio.c
index 989912f2b739..c4cabe26b157 100644
--- a/sound/soc/sof/sof-audio.c
+++ b/sound/soc/sof/sof-audio.c
@@ -11,6 +11,486 @@
#include "sof-audio.h"
#include "ops.h"
+static int sof_kcontrol_setup(struct snd_sof_dev *sdev, struct snd_sof_control *scontrol)
+{
+ int ipc_cmd, ctrl_type;
+ int ret;
+
+ /* reset readback offset for scontrol */
+ scontrol->readback_offset = 0;
+
+ /* notify DSP of kcontrol values */
+ switch (scontrol->cmd) {
+ case SOF_CTRL_CMD_VOLUME:
+ case SOF_CTRL_CMD_ENUM:
+ case SOF_CTRL_CMD_SWITCH:
+ ipc_cmd = SOF_IPC_COMP_SET_VALUE;
+ ctrl_type = SOF_CTRL_TYPE_VALUE_CHAN_SET;
+ break;
+ case SOF_CTRL_CMD_BINARY:
+ ipc_cmd = SOF_IPC_COMP_SET_DATA;
+ ctrl_type = SOF_CTRL_TYPE_DATA_SET;
+ break;
+ default:
+ return 0;
+ }
+
+ ret = snd_sof_ipc_set_get_comp_data(scontrol, ipc_cmd, ctrl_type, scontrol->cmd, true);
+ if (ret < 0)
+ dev_err(sdev->dev, "error: failed kcontrol value set for widget: %d\n",
+ scontrol->comp_id);
+
+ return ret;
+}
+
+static int sof_dai_config_setup(struct snd_sof_dev *sdev, struct snd_sof_dai *dai)
+{
+ struct sof_ipc_dai_config *config;
+ struct sof_ipc_reply reply;
+ int ret;
+
+ config = &dai->dai_config[dai->current_config];
+ if (!config) {
+ dev_err(sdev->dev, "error: no config for DAI %s\n", dai->name);
+ return -EINVAL;
+ }
+
+ ret = sof_ipc_tx_message(sdev->ipc, config->hdr.cmd, config, config->hdr.size,
+ &reply, sizeof(reply));
+
+ if (ret < 0)
+ dev_err(sdev->dev, "error: failed to set dai config for %s\n", dai->name);
+
+ return ret;
+}
+
+static int sof_widget_kcontrol_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget)
+{
+ struct snd_sof_control *scontrol;
+ int ret;
+
+ /* set up all controls for the widget */
+ list_for_each_entry(scontrol, &sdev->kcontrol_list, list)
+ if (scontrol->comp_id == swidget->comp_id) {
+ ret = sof_kcontrol_setup(sdev, scontrol);
+ if (ret < 0) {
+ dev_err(sdev->dev, "error: fail to set up kcontrols for widget %s\n",
+ swidget->widget->name);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static void sof_reset_route_setup_status(struct snd_sof_dev *sdev, struct snd_sof_widget *widget)
+{
+ struct snd_sof_route *sroute;
+
+ list_for_each_entry(sroute, &sdev->route_list, list)
+ if (sroute->src_widget == widget || sroute->sink_widget == widget)
+ sroute->setup = false;
+}
+
+int sof_widget_free(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget)
+{
+ struct sof_ipc_free ipc_free = {
+ .hdr = {
+ .size = sizeof(ipc_free),
+ .cmd = SOF_IPC_GLB_TPLG_MSG,
+ },
+ .id = swidget->comp_id,
+ };
+ struct sof_ipc_reply reply;
+ int ret;
+
+ if (!swidget->private)
+ return 0;
+
+ /* only free when use_count is 0 */
+ if (--swidget->use_count)
+ return 0;
+
+ switch (swidget->id) {
+ case snd_soc_dapm_scheduler:
+ ipc_free.hdr.cmd |= SOF_IPC_TPLG_PIPE_FREE;
+ break;
+ case snd_soc_dapm_buffer:
+ ipc_free.hdr.cmd |= SOF_IPC_TPLG_BUFFER_FREE;
+ break;
+ default:
+ ipc_free.hdr.cmd |= SOF_IPC_TPLG_COMP_FREE;
+ break;
+ }
+
+ ret = sof_ipc_tx_message(sdev->ipc, ipc_free.hdr.cmd, &ipc_free, sizeof(ipc_free),
+ &reply, sizeof(reply));
+ if (ret < 0) {
+ dev_err(sdev->dev, "error: failed to free widget %s\n", swidget->widget->name);
+ swidget->use_count++;
+ return ret;
+ }
+
+ /* reset route setup status for all routes that contain this widget */
+ sof_reset_route_setup_status(sdev, swidget);
+ swidget->complete = 0;
+ dev_dbg(sdev->dev, "widget %s freed\n", swidget->widget->name);
+
+ return 0;
+}
+EXPORT_SYMBOL(sof_widget_free);
+
+int sof_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget)
+{
+ struct sof_ipc_pipe_new *pipeline;
+ struct sof_ipc_comp_reply r;
+ struct sof_ipc_cmd_hdr *hdr;
+ struct sof_ipc_comp *comp;
+ struct snd_sof_dai *dai;
+ size_t ipc_size;
+ int ret;
+
+ /* skip if there is no private data */
+ if (!swidget->private)
+ return 0;
+
+ /* widget already set up */
+ if (++swidget->use_count > 1)
+ return 0;
+
+ ret = sof_pipeline_core_enable(sdev, swidget);
+ if (ret < 0) {
+ dev_err(sdev->dev, "error: failed to enable target core: %d for widget %s\n",
+ ret, swidget->widget->name);
+ goto use_count_dec;
+ }
+
+ switch (swidget->id) {
+ case snd_soc_dapm_dai_in:
+ case snd_soc_dapm_dai_out:
+ ipc_size = sizeof(struct sof_ipc_comp_dai) + sizeof(struct sof_ipc_comp_ext);
+ comp = kzalloc(ipc_size, GFP_KERNEL);
+ if (!comp)
+ return -ENOMEM;
+
+ dai = swidget->private;
+ dai->configured = false;
+ memcpy(comp, &dai->comp_dai, sizeof(struct sof_ipc_comp_dai));
+
+ /* append extended data to the end of the component */
+ memcpy((u8 *)comp + sizeof(struct sof_ipc_comp_dai), &swidget->comp_ext,
+ sizeof(swidget->comp_ext));
+
+ ret = sof_ipc_tx_message(sdev->ipc, comp->hdr.cmd, comp, ipc_size, &r, sizeof(r));
+ kfree(comp);
+ if (ret < 0) {
+ dev_err(sdev->dev, "error: failed to load widget %s\n",
+ swidget->widget->name);
+ goto use_count_dec;
+ }
+
+ ret = sof_dai_config_setup(sdev, dai);
+ if (ret < 0) {
+ dev_err(sdev->dev, "error: failed to load dai config for DAI %s\n",
+ swidget->widget->name);
+ sof_widget_free(sdev, swidget);
+ return ret;
+ }
+ break;
+ case snd_soc_dapm_scheduler:
+ pipeline = swidget->private;
+ ret = sof_load_pipeline_ipc(sdev->dev, pipeline, &r);
+ break;
+ default:
+ hdr = swidget->private;
+ ret = sof_ipc_tx_message(sdev->ipc, hdr->cmd, swidget->private, hdr->size,
+ &r, sizeof(r));
+ break;
+ }
+ if (ret < 0) {
+ dev_err(sdev->dev, "error: failed to load widget %s\n", swidget->widget->name);
+ goto use_count_dec;
+ }
+
+ /* restore kcontrols for widget */
+ ret = sof_widget_kcontrol_setup(sdev, swidget);
+ if (ret < 0) {
+ dev_err(sdev->dev, "error: failed to restore kcontrols for widget %s\n",
+ swidget->widget->name);
+ sof_widget_free(sdev, swidget);
+ return ret;
+ }
+
+ dev_dbg(sdev->dev, "widget %s setup complete\n", swidget->widget->name);
+
+ return 0;
+
+use_count_dec:
+ swidget->use_count--;
+ return ret;
+}
+EXPORT_SYMBOL(sof_widget_setup);
+
+static int sof_route_setup_ipc(struct snd_sof_dev *sdev, struct snd_sof_route *sroute)
+{
+ struct sof_ipc_pipe_comp_connect *connect;
+ struct sof_ipc_reply reply;
+ int ret;
+
+ /* skip if there's no private data */
+ if (!sroute->private)
+ return 0;
+
+ /* nothing to do if route is already set up */
+ if (sroute->setup)
+ return 0;
+
+ connect = sroute->private;
+
+ dev_dbg(sdev->dev, "setting up route %s -> %s\n",
+ sroute->src_widget->widget->name,
+ sroute->sink_widget->widget->name);
+
+ /* send ipc */
+ ret = sof_ipc_tx_message(sdev->ipc,
+ connect->hdr.cmd,
+ connect, sizeof(*connect),
+ &reply, sizeof(reply));
+ if (ret < 0) {
+ dev_err(sdev->dev, "%s: route setup failed %d\n", __func__, ret);
+ return ret;
+ }
+
+ sroute->setup = true;
+
+ return 0;
+}
+
+static int sof_route_setup(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget *wsource,
+ struct snd_soc_dapm_widget *wsink)
+{
+ struct snd_sof_widget *src_widget = wsource->dobj.private;
+ struct snd_sof_widget *sink_widget = wsink->dobj.private;
+ struct snd_sof_route *sroute;
+ bool route_found = false;
+
+ /* ignore routes involving virtual widgets in topology */
+ switch (src_widget->id) {
+ case snd_soc_dapm_out_drv:
+ case snd_soc_dapm_output:
+ case snd_soc_dapm_input:
+ return 0;
+ default:
+ break;
+ }
+
+ switch (sink_widget->id) {
+ case snd_soc_dapm_out_drv:
+ case snd_soc_dapm_output:
+ case snd_soc_dapm_input:
+ return 0;
+ default:
+ break;
+ }
+
+ /* find route matching source and sink widgets */
+ list_for_each_entry(sroute, &sdev->route_list, list)
+ if (sroute->src_widget == src_widget && sroute->sink_widget == sink_widget) {
+ route_found = true;
+ break;
+ }
+
+ if (!route_found) {
+ dev_err(sdev->dev, "error: cannot find SOF route for source %s -> %s sink\n",
+ wsource->name, wsink->name);
+ return -EINVAL;
+ }
+
+ return sof_route_setup_ipc(sdev, sroute);
+}
+
+static int sof_setup_pipeline_connections(struct snd_sof_dev *sdev,
+ struct snd_soc_dapm_widget_list *list, int dir)
+{
+ struct snd_soc_dapm_widget *widget;
+ struct snd_soc_dapm_path *p;
+ int ret;
+ int i;
+
+ /*
+ * Set up connections between widgets in the sink/source paths based on direction.
+ * Some non-SOF widgets exist in topology either for compatibility or for the
+ * purpose of connecting a pipeline from a host to a DAI in order to receive the DAPM
+ * events. But they are not handled by the firmware. So ignore them.
+ */
+ if (dir == SNDRV_PCM_STREAM_PLAYBACK) {
+ for_each_dapm_widgets(list, i, widget) {
+ if (!widget->dobj.private)
+ continue;
+
+ snd_soc_dapm_widget_for_each_sink_path(widget, p)
+ if (p->sink->dobj.private) {
+ ret = sof_route_setup(sdev, widget, p->sink);
+ if (ret < 0)
+ return ret;
+ }
+ }
+ } else {
+ for_each_dapm_widgets(list, i, widget) {
+ if (!widget->dobj.private)
+ continue;
+
+ snd_soc_dapm_widget_for_each_source_path(widget, p)
+ if (p->source->dobj.private) {
+ ret = sof_route_setup(sdev, p->source, widget);
+ if (ret < 0)
+ return ret;
+ }
+ }
+ }
+
+ return 0;
+}
+
+int sof_widget_list_setup(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, int dir)
+{
+ struct snd_soc_dapm_widget_list *list = spcm->stream[dir].list;
+ struct snd_soc_dapm_widget *widget;
+ int i, ret, num_widgets;
+
+ /* nothing to set up */
+ if (!list)
+ return 0;
+
+ /* set up widgets in the list */
+ for_each_dapm_widgets(list, num_widgets, widget) {
+ struct snd_sof_widget *swidget = widget->dobj.private;
+ struct snd_sof_widget *pipe_widget;
+
+ if (!swidget)
+ continue;
+
+ /*
+ * The scheduler widget for a pipeline is not part of the connected DAPM
+ * widget list and it needs to be set up before the widgets in the pipeline
+ * are set up. The use_count for the scheduler widget is incremented for every
+ * widget in a given pipeline to ensure that it is freed only after the last
+ * widget in the pipeline is freed.
+ */
+ pipe_widget = swidget->pipe_widget;
+ if (!pipe_widget) {
+ dev_err(sdev->dev, "error: no pipeline widget found for %s\n",
+ swidget->widget->name);
+ ret = -EINVAL;
+ goto widget_free;
+ }
+
+ ret = sof_widget_setup(sdev, pipe_widget);
+ if (ret < 0)
+ goto widget_free;
+
+ /* set up the widget */
+ ret = sof_widget_setup(sdev, swidget);
+ if (ret < 0) {
+ sof_widget_free(sdev, pipe_widget);
+ goto widget_free;
+ }
+ }
+
+ /*
+ * error in setting pipeline connections will result in route status being reset for
+ * routes that were successfully set up when the widgets are freed.
+ */
+ ret = sof_setup_pipeline_connections(sdev, list, dir);
+ if (ret < 0)
+ goto widget_free;
+
+ /* complete pipelines */
+ for_each_dapm_widgets(list, i, widget) {
+ struct snd_sof_widget *swidget = widget->dobj.private;
+ struct snd_sof_widget *pipe_widget;
+
+ if (!swidget)
+ continue;
+
+ pipe_widget = swidget->pipe_widget;
+ if (!pipe_widget) {
+ dev_err(sdev->dev, "error: no pipeline widget found for %s\n",
+ swidget->widget->name);
+ ret = -EINVAL;
+ goto widget_free;
+ }
+
+ if (pipe_widget->complete)
+ continue;
+
+ pipe_widget->complete = snd_sof_complete_pipeline(sdev->dev, pipe_widget);
+ if (pipe_widget->complete < 0) {
+ ret = pipe_widget->complete;
+ goto widget_free;
+ }
+ }
+
+ return 0;
+
+widget_free:
+ /* free all widgets that have been set up successfully */
+ for_each_dapm_widgets(list, i, widget) {
+ struct snd_sof_widget *swidget = widget->dobj.private;
+
+ if (!swidget)
+ continue;
+
+ if (!num_widgets--)
+ break;
+
+ sof_widget_free(sdev, swidget);
+ sof_widget_free(sdev, swidget->pipe_widget);
+ }
+
+ return ret;
+}
+
+int sof_widget_list_free(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, int dir)
+{
+ struct snd_soc_dapm_widget_list *list = spcm->stream[dir].list;
+ struct snd_soc_dapm_widget *widget;
+ int i, ret;
+ int ret1 = 0;
+
+ /* nothing to free */
+ if (!list)
+ return 0;
+
+ /*
+ * Free widgets in the list. This can fail but continue freeing other widgets to keep
+ * use_counts balanced.
+ */
+ for_each_dapm_widgets(list, i, widget) {
+ struct snd_sof_widget *swidget = widget->dobj.private;
+
+ if (!swidget)
+ continue;
+
+ /*
+ * free widget and its pipe_widget. Either of these can fail, but free as many as
+ * possible before freeing the list and returning the error.
+ */
+ ret = sof_widget_free(sdev, swidget);
+ if (ret < 0)
+ ret1 = ret;
+
+ ret = sof_widget_free(sdev, swidget->pipe_widget);
+ if (ret < 0)
+ ret1 = ret;
+ }
+
+ snd_soc_dapm_dai_free_widgets(&list);
+ spcm->stream[dir].list = NULL;
+
+ return ret1;
+}
+
/*
* helper to determine if there are only D0i3 compatible
* streams active
@@ -93,55 +573,6 @@ int sof_set_hw_params_upon_resume(struct device *dev)
return snd_sof_dsp_hw_params_upon_resume(sdev);
}
-static int sof_restore_kcontrols(struct device *dev)
-{
- struct snd_sof_dev *sdev = dev_get_drvdata(dev);
- struct snd_sof_control *scontrol;
- int ipc_cmd, ctrl_type;
- int ret = 0;
-
- /* restore kcontrol values */
- list_for_each_entry(scontrol, &sdev->kcontrol_list, list) {
- /* reset readback offset for scontrol after resuming */
- scontrol->readback_offset = 0;
-
- /* notify DSP of kcontrol values */
- switch (scontrol->cmd) {
- case SOF_CTRL_CMD_VOLUME:
- case SOF_CTRL_CMD_ENUM:
- case SOF_CTRL_CMD_SWITCH:
- ipc_cmd = SOF_IPC_COMP_SET_VALUE;
- ctrl_type = SOF_CTRL_TYPE_VALUE_CHAN_SET;
- ret = snd_sof_ipc_set_get_comp_data(scontrol,
- ipc_cmd, ctrl_type,
- scontrol->cmd,
- true);
- break;
- case SOF_CTRL_CMD_BINARY:
- ipc_cmd = SOF_IPC_COMP_SET_DATA;
- ctrl_type = SOF_CTRL_TYPE_DATA_SET;
- ret = snd_sof_ipc_set_get_comp_data(scontrol,
- ipc_cmd, ctrl_type,
- scontrol->cmd,
- true);
- break;
-
- default:
- break;
- }
-
- if (ret < 0) {
- dev_err(dev,
- "error: failed kcontrol value set for widget: %d\n",
- scontrol->comp_id);
-
- return ret;
- }
- }
-
- return 0;
-}
-
const struct sof_ipc_pipe_new *snd_sof_pipeline_find(struct snd_sof_dev *sdev,
int pipeline_id)
{
@@ -158,142 +589,54 @@ const struct sof_ipc_pipe_new *snd_sof_pipeline_find(struct snd_sof_dev *sdev,
return NULL;
}
-int sof_restore_pipelines(struct device *dev)
+int sof_set_up_pipelines(struct device *dev, bool verify)
{
struct snd_sof_dev *sdev = dev_get_drvdata(dev);
struct snd_sof_widget *swidget;
struct snd_sof_route *sroute;
- struct sof_ipc_pipe_new *pipeline;
- struct snd_sof_dai *dai;
- struct sof_ipc_cmd_hdr *hdr;
- struct sof_ipc_comp *comp;
- size_t ipc_size;
int ret;
/* restore pipeline components */
list_for_each_entry_reverse(swidget, &sdev->widget_list, list) {
- struct sof_ipc_comp_reply r;
-
- /* skip if there is no private data */
- if (!swidget->private)
+ /* only set up the widgets belonging to static pipelines */
+ if (!verify && swidget->dynamic_pipeline_widget)
continue;
- ret = sof_pipeline_core_enable(sdev, swidget);
- if (ret < 0) {
- dev_err(dev,
- "error: failed to enable target core: %d\n",
- ret);
+ /* update DAI config. The IPC will be sent in sof_widget_setup() */
+ if (WIDGET_IS_DAI(swidget->id)) {
+ struct snd_sof_dai *dai = swidget->private;
+ struct sof_ipc_dai_config *config;
- return ret;
- }
-
- switch (swidget->id) {
- case snd_soc_dapm_dai_in:
- case snd_soc_dapm_dai_out:
- ipc_size = sizeof(struct sof_ipc_comp_dai) +
- sizeof(struct sof_ipc_comp_ext);
- comp = kzalloc(ipc_size, GFP_KERNEL);
- if (!comp)
- return -ENOMEM;
-
- dai = swidget->private;
- memcpy(comp, &dai->comp_dai,
- sizeof(struct sof_ipc_comp_dai));
-
- /* append extended data to the end of the component */
- memcpy((u8 *)comp + sizeof(struct sof_ipc_comp_dai),
- &swidget->comp_ext, sizeof(swidget->comp_ext));
-
- ret = sof_ipc_tx_message(sdev->ipc, comp->hdr.cmd,
- comp, ipc_size,
- &r, sizeof(r));
- kfree(comp);
- break;
- case snd_soc_dapm_scheduler:
+ if (!dai || !dai->dai_config)
+ continue;
+ config = dai->dai_config;
/*
- * During suspend, all DSP cores are powered off.
- * Therefore upon resume, create the pipeline comp
- * and power up the core that the pipeline is
- * scheduled on.
+ * The link DMA channel would be invalidated for running
+ * streams but not for streams that were in the PAUSED
+ * state during suspend. So invalidate it here before setting
+ * the dai config in the DSP.
*/
- pipeline = swidget->private;
- ret = sof_load_pipeline_ipc(dev, pipeline, &r);
- break;
- default:
- hdr = swidget->private;
- ret = sof_ipc_tx_message(sdev->ipc, hdr->cmd,
- swidget->private, hdr->size,
- &r, sizeof(r));
- break;
+ if (config->type == SOF_DAI_INTEL_HDA)
+ config->hda.link_dma_ch = DMA_CHAN_INVALID;
}
- if (ret < 0) {
- dev_err(dev,
- "error: failed to load widget type %d with ID: %d\n",
- swidget->widget->id, swidget->comp_id);
+ ret = sof_widget_setup(sdev, swidget);
+ if (ret < 0)
return ret;
- }
}
/* restore pipeline connections */
- list_for_each_entry_reverse(sroute, &sdev->route_list, list) {
- struct sof_ipc_pipe_comp_connect *connect;
- struct sof_ipc_reply reply;
+ list_for_each_entry(sroute, &sdev->route_list, list) {
- /* skip if there's no private data */
- if (!sroute->private)
+ /* only set up routes belonging to static pipelines */
+ if (!verify && (sroute->src_widget->dynamic_pipeline_widget ||
+ sroute->sink_widget->dynamic_pipeline_widget))
continue;
- connect = sroute->private;
-
- /* send ipc */
- ret = sof_ipc_tx_message(sdev->ipc,
- connect->hdr.cmd,
- connect, sizeof(*connect),
- &reply, sizeof(reply));
+ ret = sof_route_setup_ipc(sdev, sroute);
if (ret < 0) {
- dev_err(dev,
- "error: failed to load route sink %s control %s source %s\n",
- sroute->route->sink,
- sroute->route->control ? sroute->route->control
- : "none",
- sroute->route->source);
-
- return ret;
- }
- }
-
- /* restore dai links */
- list_for_each_entry_reverse(dai, &sdev->dai_list, list) {
- struct sof_ipc_reply reply;
- struct sof_ipc_dai_config *config = &dai->dai_config[dai->current_config];
-
- if (!config) {
- dev_err(dev, "error: no config for DAI %s\n",
- dai->name);
- continue;
- }
-
- /*
- * The link DMA channel would be invalidated for running
- * streams but not for streams that were in the PAUSED
- * state during suspend. So invalidate it here before setting
- * the dai config in the DSP.
- */
- if (config->type == SOF_DAI_INTEL_HDA)
- config->hda.link_dma_ch = DMA_CHAN_INVALID;
-
- ret = sof_ipc_tx_message(sdev->ipc,
- config->hdr.cmd, config,
- config->hdr.size,
- &reply, sizeof(reply));
-
- if (ret < 0) {
- dev_err(dev,
- "error: failed to set dai config for %s\n",
- dai->name);
-
+ dev_err(sdev->dev, "%s: restore pipeline connections failed\n", __func__);
return ret;
}
}
@@ -302,6 +645,10 @@ int sof_restore_pipelines(struct device *dev)
list_for_each_entry(swidget, &sdev->widget_list, list) {
switch (swidget->id) {
case snd_soc_dapm_scheduler:
+ /* only complete static pipelines */
+ if (!verify && swidget->dynamic_pipeline_widget)
+ continue;
+
swidget->complete =
snd_sof_complete_pipeline(dev, swidget);
break;
@@ -310,13 +657,41 @@ int sof_restore_pipelines(struct device *dev)
}
}
- /* restore pipeline kcontrols */
- ret = sof_restore_kcontrols(dev);
- if (ret < 0)
- dev_err(dev,
- "error: restoring kcontrols after resume\n");
+ return 0;
+}
- return ret;
+/*
+ * This function doesn't free widgets during suspend. It only resets the set up status for all
+ * routes and use_count for all widgets.
+ */
+int sof_tear_down_pipelines(struct device *dev, bool verify)
+{
+ struct snd_sof_dev *sdev = dev_get_drvdata(dev);
+ struct snd_sof_widget *swidget;
+ struct snd_sof_route *sroute;
+ int ret;
+
+ /*
+ * This function is called during suspend and for one-time topology verification during
+ * first boot. In both cases, there is no need to protect swidget->use_count and
+ * sroute->setup because during suspend all streams are suspended and during topology
+ * loading the sound card unavailable to open PCMs.
+ */
+ list_for_each_entry_reverse(swidget, &sdev->widget_list, list) {
+ if (!verify) {
+ swidget->use_count = 0;
+ continue;
+ }
+
+ ret = sof_widget_free(sdev, swidget);
+ if (ret < 0)
+ return ret;
+ }
+
+ list_for_each_entry(sroute, &sdev->route_list, list)
+ sroute->setup = false;
+
+ return 0;
}
/*
diff --git a/sound/soc/sof/sof-audio.h b/sound/soc/sof/sof-audio.h
index 9a8d005e75a0..149b3dbcddd1 100644
--- a/sound/soc/sof/sof-audio.h
+++ b/sound/soc/sof/sof-audio.h
@@ -28,6 +28,8 @@
#define DMA_CHAN_INVALID 0xFFFFFFFF
+#define WIDGET_IS_DAI(id) ((id) == snd_soc_dapm_dai_in || (id) == snd_soc_dapm_dai_out)
+
/* PCM stream, mapped to FW component */
struct snd_sof_pcm_stream {
u32 comp_id;
@@ -35,6 +37,7 @@ struct snd_sof_pcm_stream {
struct sof_ipc_stream_posn posn;
struct snd_pcm_substream *substream;
struct work_struct period_elapsed_work;
+ struct snd_soc_dapm_widget_list *list; /* list of connected DAPM widgets */
bool d0i3_compatible; /* DSP can be in D0I3 when this pcm is opened */
/*
* flag to indicate that the DSP pipelines should be kept
@@ -66,6 +69,7 @@ struct snd_sof_control {
int min_volume_step; /* min volume step for volume_table */
int max_volume_step; /* max volume step for volume_table */
int num_channels;
+ unsigned int access;
u32 readback_offset; /* offset to mmapped data if used */
struct sof_ipc_ctrl_data *control_data;
u32 size; /* cdata size */
@@ -80,17 +84,31 @@ struct snd_sof_control {
bool comp_data_dirty;
};
+struct snd_sof_widget;
+
/* ASoC SOF DAPM widget */
struct snd_sof_widget {
struct snd_soc_component *scomp;
int comp_id;
int pipeline_id;
int complete;
+ int use_count; /* use_count will be protected by the PCM mutex held by the core */
int core;
int id;
+ /*
+ * Flag indicating if the widget should be set up dynamically when a PCM is opened.
+ * This flag is only set for the scheduler type widget in topology. During topology
+ * loading, this flag is propagated to all the widgets belonging to the same pipeline.
+ * When this flag is not set, a widget is set up at the time of topology loading
+ * and retained until the DSP enters D3. It will need to be set up again when resuming
+ * from D3.
+ */
+ bool dynamic_pipeline_widget;
+
struct snd_soc_dapm_widget *widget;
struct list_head list; /* list in sdev widget list */
+ struct snd_sof_widget *pipe_widget;
/* extended data for UUID components */
struct sof_ipc_comp_ext comp_ext;
@@ -104,6 +122,9 @@ struct snd_sof_route {
struct snd_soc_dapm_route *route;
struct list_head list; /* list in sdev route list */
+ struct snd_sof_widget *src_widget;
+ struct snd_sof_widget *sink_widget;
+ bool setup;
void *private;
};
@@ -112,11 +133,11 @@ struct snd_sof_route {
struct snd_sof_dai {
struct snd_soc_component *scomp;
const char *name;
- const char *cpu_dai_name;
struct sof_ipc_comp_dai comp_dai;
int number_configs;
int current_config;
+ bool configured; /* DAI configured during BE hw_params */
struct sof_ipc_dai_config *dai_config;
struct list_head list; /* list in sdev dai list */
};
@@ -225,7 +246,8 @@ int snd_sof_ipc_set_get_comp_data(struct snd_sof_control *scontrol,
int sof_pcm_dai_link_fixup(struct snd_soc_pcm_runtime *rtd, struct snd_pcm_hw_params *params);
/* PM */
-int sof_restore_pipelines(struct device *dev);
+int sof_set_up_pipelines(struct device *dev, bool verify);
+int sof_tear_down_pipelines(struct device *dev, bool verify);
int sof_set_hw_params_upon_resume(struct device *dev);
bool snd_sof_stream_suspend_ignored(struct snd_sof_dev *sdev);
bool snd_sof_dsp_only_d0i3_compatible_stream_active(struct snd_sof_dev *sdev);
@@ -234,4 +256,10 @@ bool snd_sof_dsp_only_d0i3_compatible_stream_active(struct snd_sof_dev *sdev);
int sof_machine_register(struct snd_sof_dev *sdev, void *pdata);
void sof_machine_unregister(struct snd_sof_dev *sdev, void *pdata);
+int sof_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget);
+int sof_widget_free(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget);
+
+/* PCM */
+int sof_widget_list_setup(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, int dir);
+int sof_widget_list_free(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, int dir);
#endif
diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h
index a6ded5bd0674..1289e2efeb62 100644
--- a/sound/soc/sof/sof-priv.h
+++ b/sound/soc/sof/sof-priv.h
@@ -23,6 +23,7 @@
/* debug flags */
#define SOF_DBG_ENABLE_TRACE BIT(0)
#define SOF_DBG_RETAIN_CTX BIT(1) /* prevent DSP D3 on FW exception */
+#define SOF_DBG_VERIFY_TPLG BIT(2) /* verify topology during load */
#define SOF_DBG_DUMP_REGS BIT(0)
#define SOF_DBG_DUMP_MBOX BIT(1)
diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c
index cc9585bfa4e9..44d60081bc26 100644
--- a/sound/soc/sof/topology.c
+++ b/sound/soc/sof/topology.c
@@ -572,6 +572,12 @@ static const struct sof_topology_token sched_tokens[] = {
offsetof(struct sof_ipc_pipe_new, time_domain), 0},
};
+static const struct sof_topology_token pipeline_tokens[] = {
+ {SOF_TKN_SCHED_DYNAMIC_PIPELINE, SND_SOC_TPLG_TUPLE_TYPE_BOOL, get_token_u16,
+ offsetof(struct snd_sof_widget, dynamic_pipeline_widget), 0},
+
+};
+
/* volume */
static const struct sof_topology_token volume_tokens[] = {
{SOF_TKN_VOLUME_RAMP_STEP_TYPE, SND_SOC_TPLG_TUPLE_TYPE_WORD,
@@ -1250,6 +1256,7 @@ static int sof_control_load(struct snd_soc_component *scomp, int index,
return -ENOMEM;
scontrol->scomp = scomp;
+ scontrol->access = kc->access;
switch (le32_to_cpu(hdr->ops.info)) {
case SND_SOC_TPLG_CTL_VOLSW:
@@ -1512,10 +1519,8 @@ static struct sof_ipc_comp *sof_comp_alloc(struct snd_sof_widget *swidget,
static int sof_widget_load_dai(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_dai *dai)
{
- struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
struct snd_soc_tplg_private *private = &tw->priv;
struct sof_ipc_comp_dai *comp_dai;
size_t ipc_size = sizeof(*comp_dai);
@@ -1552,10 +1557,7 @@ static int sof_widget_load_dai(struct snd_soc_component *scomp, int index,
swidget->widget->name, comp_dai->type, comp_dai->dai_index);
sof_dbg_comp_config(scomp, &comp_dai->config);
- ret = sof_ipc_tx_message(sdev->ipc, comp_dai->comp.hdr.cmd,
- comp_dai, ipc_size, r, sizeof(*r));
-
- if (ret == 0 && dai) {
+ if (dai) {
dai->scomp = scomp;
/*
@@ -1577,10 +1579,8 @@ finish:
static int sof_widget_load_buffer(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_soc_tplg_dapm_widget *tw)
{
- struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
struct snd_soc_tplg_private *private = &tw->priv;
struct sof_ipc_buffer *buffer;
int ret;
@@ -1612,15 +1612,7 @@ static int sof_widget_load_buffer(struct snd_soc_component *scomp, int index,
swidget->private = buffer;
- ret = sof_ipc_tx_message(sdev->ipc, buffer->comp.hdr.cmd, buffer,
- sizeof(*buffer), r, sizeof(*r));
- if (ret < 0) {
- dev_err(scomp->dev, "error: buffer %s load failed\n",
- swidget->widget->name);
- kfree(buffer);
- }
-
- return ret;
+ return 0;
}
/* bind PCM ID to host component ID */
@@ -1649,10 +1641,8 @@ static int spcm_bind(struct snd_soc_component *scomp, struct snd_sof_pcm *spcm,
static int sof_widget_load_pcm(struct snd_soc_component *scomp, int index,
struct snd_sof_widget *swidget,
enum sof_ipc_stream_direction dir,
- struct snd_soc_tplg_dapm_widget *tw,
- struct sof_ipc_comp_reply *r)
+ struct snd_soc_tplg_dapm_widget *tw)
{
- struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
struct snd_soc_tplg_private *private = &tw->priv;
struct sof_ipc_comp_host *host;
size_t ipc_size = sizeof(*host);
@@ -1691,10 +1681,7 @@ static int sof_widget_load_pcm(struct snd_soc_component *scomp, int index,
swidget->private = host;
- ret = sof_ipc_tx_message(sdev->ipc, host->comp.hdr.cmd, host,
- ipc_size, r, sizeof(*r));
- if (ret >= 0)
- return ret;
+ return 0;
err:
kfree(host);
return ret;
@@ -1723,8 +1710,7 @@ int sof_load_pipeline_ipc(struct device *dev,
static int sof_widget_load_pipeline(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_soc_tplg_dapm_widget *tw)
{
struct snd_soc_tplg_private *private = &tw->priv;
struct sof_ipc_pipe_new *pipeline;
@@ -1764,16 +1750,22 @@ static int sof_widget_load_pipeline(struct snd_soc_component *scomp, int index,
goto err;
}
+ ret = sof_parse_tokens(scomp, swidget, pipeline_tokens,
+ ARRAY_SIZE(pipeline_tokens), private->array,
+ le32_to_cpu(private->size));
+ if (ret != 0) {
+ dev_err(scomp->dev, "error: parse dynamic pipeline token failed %d\n",
+ private->size);
+ goto err;
+ }
+
dev_dbg(scomp->dev, "pipeline %s: period %d pri %d mips %d core %d frames %d\n",
swidget->widget->name, pipeline->period, pipeline->priority,
pipeline->period_mips, pipeline->core, pipeline->frames_per_sched);
swidget->private = pipeline;
- /* send ipc's to create pipeline comp and power up schedule core */
- ret = sof_load_pipeline_ipc(scomp->dev, pipeline, r);
- if (ret >= 0)
- return ret;
+ return 0;
err:
kfree(pipeline);
return ret;
@@ -1785,10 +1777,8 @@ err:
static int sof_widget_load_mixer(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_soc_tplg_dapm_widget *tw)
{
- struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
struct snd_soc_tplg_private *private = &tw->priv;
struct sof_ipc_comp_mixer *mixer;
size_t ipc_size = sizeof(*mixer);
@@ -1817,12 +1807,7 @@ static int sof_widget_load_mixer(struct snd_soc_component *scomp, int index,
swidget->private = mixer;
- ret = sof_ipc_tx_message(sdev->ipc, mixer->comp.hdr.cmd, mixer,
- ipc_size, r, sizeof(*r));
- if (ret < 0)
- kfree(mixer);
-
- return ret;
+ return 0;
}
/*
@@ -1830,10 +1815,8 @@ static int sof_widget_load_mixer(struct snd_soc_component *scomp, int index,
*/
static int sof_widget_load_mux(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_soc_tplg_dapm_widget *tw)
{
- struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
struct snd_soc_tplg_private *private = &tw->priv;
struct sof_ipc_comp_mux *mux;
size_t ipc_size = sizeof(*mux);
@@ -1862,12 +1845,7 @@ static int sof_widget_load_mux(struct snd_soc_component *scomp, int index,
swidget->private = mux;
- ret = sof_ipc_tx_message(sdev->ipc, mux->comp.hdr.cmd, mux,
- ipc_size, r, sizeof(*r));
- if (ret < 0)
- kfree(mux);
-
- return ret;
+ return 0;
}
/*
@@ -1876,8 +1854,7 @@ static int sof_widget_load_mux(struct snd_soc_component *scomp, int index,
static int sof_widget_load_pga(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_soc_tplg_dapm_widget *tw)
{
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
struct snd_soc_tplg_private *private = &tw->priv;
@@ -1937,10 +1914,7 @@ static int sof_widget_load_pga(struct snd_soc_component *scomp, int index,
}
}
- ret = sof_ipc_tx_message(sdev->ipc, volume->comp.hdr.cmd, volume,
- ipc_size, r, sizeof(*r));
- if (ret >= 0)
- return ret;
+ return 0;
err:
kfree(volume);
return ret;
@@ -1952,10 +1926,8 @@ err:
static int sof_widget_load_src(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_soc_tplg_dapm_widget *tw)
{
- struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
struct snd_soc_tplg_private *private = &tw->priv;
struct sof_ipc_comp_src *src;
size_t ipc_size = sizeof(*src);
@@ -1994,10 +1966,7 @@ static int sof_widget_load_src(struct snd_soc_component *scomp, int index,
swidget->private = src;
- ret = sof_ipc_tx_message(sdev->ipc, src->comp.hdr.cmd, src,
- ipc_size, r, sizeof(*r));
- if (ret >= 0)
- return ret;
+ return 0;
err:
kfree(src);
return ret;
@@ -2009,10 +1978,8 @@ err:
static int sof_widget_load_asrc(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_soc_tplg_dapm_widget *tw)
{
- struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
struct snd_soc_tplg_private *private = &tw->priv;
struct sof_ipc_comp_asrc *asrc;
size_t ipc_size = sizeof(*asrc);
@@ -2053,10 +2020,7 @@ static int sof_widget_load_asrc(struct snd_soc_component *scomp, int index,
swidget->private = asrc;
- ret = sof_ipc_tx_message(sdev->ipc, asrc->comp.hdr.cmd, asrc,
- ipc_size, r, sizeof(*r));
- if (ret >= 0)
- return ret;
+ return 0;
err:
kfree(asrc);
return ret;
@@ -2068,10 +2032,8 @@ err:
static int sof_widget_load_siggen(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_soc_tplg_dapm_widget *tw)
{
- struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
struct snd_soc_tplg_private *private = &tw->priv;
struct sof_ipc_comp_tone *tone;
size_t ipc_size = sizeof(*tone);
@@ -2110,10 +2072,7 @@ static int sof_widget_load_siggen(struct snd_soc_component *scomp, int index,
swidget->private = tone;
- ret = sof_ipc_tx_message(sdev->ipc, tone->comp.hdr.cmd, tone,
- ipc_size, r, sizeof(*r));
- if (ret >= 0)
- return ret;
+ return 0;
err:
kfree(tone);
return ret;
@@ -2195,10 +2154,8 @@ static int sof_get_control_data(struct snd_soc_component *scomp,
static int sof_process_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,
int type)
{
- 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;
struct sof_ipc_comp_process *process;
@@ -2272,33 +2229,6 @@ static int sof_process_load(struct snd_soc_component *scomp, int index,
process->size = ipc_data_size;
swidget->private = process;
-
- ret = sof_ipc_tx_message(sdev->ipc, process->comp.hdr.cmd, process,
- ipc_size, r, sizeof(*r));
-
- if (ret < 0) {
- dev_err(scomp->dev, "error: create process failed\n");
- goto err;
- }
-
- /* we sent the data in single message so return */
- if (ipc_data_size)
- goto out;
-
- /* send control data with large message supported method */
- for (i = 0; i < widget->num_kcontrols; i++) {
- wdata[i].control->readback_offset = 0;
- ret = snd_sof_ipc_set_get_comp_data(wdata[i].control,
- wdata[i].ipc_cmd,
- wdata[i].ctrl_type,
- wdata[i].control->cmd,
- true);
- if (ret != 0) {
- dev_err(scomp->dev, "error: send control failed\n");
- break;
- }
- }
-
err:
if (ret < 0)
kfree(process);
@@ -2314,8 +2244,7 @@ out:
static int sof_widget_load_process(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_soc_tplg_dapm_widget *tw)
{
struct snd_soc_tplg_private *private = &tw->priv;
struct sof_ipc_comp_process config;
@@ -2341,8 +2270,7 @@ static int sof_widget_load_process(struct snd_soc_component *scomp, int index,
}
/* now load process specific data and send IPC */
- ret = sof_process_load(scomp, index, swidget, tw, r,
- find_process_comp_type(config.type));
+ ret = sof_process_load(scomp, index, swidget, tw, find_process_comp_type(config.type));
if (ret < 0) {
dev_err(scomp->dev, "error: process loading failed\n");
return ret;
@@ -2391,8 +2319,6 @@ static int sof_widget_ready(struct snd_soc_component *scomp, int index,
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
struct snd_sof_widget *swidget;
struct snd_sof_dai *dai;
- struct sof_ipc_comp_reply reply;
- struct snd_sof_control *scontrol;
struct sof_ipc_comp comp = {
.core = SOF_DSP_PRIMARY_CORE,
};
@@ -2409,7 +2335,6 @@ static int sof_widget_ready(struct snd_soc_component *scomp, int index,
swidget->id = w->id;
swidget->pipeline_id = index;
swidget->private = NULL;
- memset(&reply, 0, sizeof(reply));
dev_dbg(scomp->dev, "tplg: ready widget id %d pipe %d type %d name : %s stream %s\n",
swidget->comp_id, index, swidget->id, tw->name,
@@ -2428,14 +2353,6 @@ static int sof_widget_ready(struct snd_soc_component *scomp, int index,
swidget->core = comp.core;
- /* default is primary core, safe to call for already enabled cores */
- ret = sof_core_enable(sdev, comp.core);
- if (ret < 0) {
- dev_err(scomp->dev, "error: enable core: %d\n", ret);
- kfree(swidget);
- return ret;
- }
-
ret = sof_parse_tokens(scomp, &swidget->comp_ext, comp_ext_tokens,
ARRAY_SIZE(comp_ext_tokens), tw->priv.array,
le32_to_cpu(tw->priv.size));
@@ -2456,7 +2373,7 @@ static int sof_widget_ready(struct snd_soc_component *scomp, int index,
return -ENOMEM;
}
- ret = sof_widget_load_dai(scomp, index, swidget, tw, &reply, dai);
+ ret = sof_widget_load_dai(scomp, index, swidget, tw, dai);
if (ret == 0) {
sof_connect_dai_widget(scomp, w, tw, dai);
list_add(&dai->list, &sdev->dai_list);
@@ -2466,47 +2383,40 @@ static int sof_widget_ready(struct snd_soc_component *scomp, int index,
}
break;
case snd_soc_dapm_mixer:
- ret = sof_widget_load_mixer(scomp, index, swidget, tw, &reply);
+ ret = sof_widget_load_mixer(scomp, index, swidget, tw);
break;
case snd_soc_dapm_pga:
- ret = sof_widget_load_pga(scomp, index, swidget, tw, &reply);
- /* Find scontrol for this pga and set readback offset*/
- list_for_each_entry(scontrol, &sdev->kcontrol_list, list) {
- if (scontrol->comp_id == swidget->comp_id) {
- scontrol->readback_offset = reply.offset;
- break;
- }
- }
+ ret = sof_widget_load_pga(scomp, index, swidget, tw);
break;
case snd_soc_dapm_buffer:
- ret = sof_widget_load_buffer(scomp, index, swidget, tw, &reply);
+ ret = sof_widget_load_buffer(scomp, index, swidget, tw);
break;
case snd_soc_dapm_scheduler:
- ret = sof_widget_load_pipeline(scomp, index, swidget, tw, &reply);
+ ret = sof_widget_load_pipeline(scomp, index, swidget, tw);
break;
case snd_soc_dapm_aif_out:
ret = sof_widget_load_pcm(scomp, index, swidget,
- SOF_IPC_STREAM_CAPTURE, tw, &reply);
+ SOF_IPC_STREAM_CAPTURE, tw);
break;
case snd_soc_dapm_aif_in:
ret = sof_widget_load_pcm(scomp, index, swidget,
- SOF_IPC_STREAM_PLAYBACK, tw, &reply);
+ SOF_IPC_STREAM_PLAYBACK, tw);
break;
case snd_soc_dapm_src:
- ret = sof_widget_load_src(scomp, index, swidget, tw, &reply);
+ ret = sof_widget_load_src(scomp, index, swidget, tw);
break;
case snd_soc_dapm_asrc:
- ret = sof_widget_load_asrc(scomp, index, swidget, tw, &reply);
+ ret = sof_widget_load_asrc(scomp, index, swidget, tw);
break;
case snd_soc_dapm_siggen:
- ret = sof_widget_load_siggen(scomp, index, swidget, tw, &reply);
+ ret = sof_widget_load_siggen(scomp, index, swidget, tw);
break;
case snd_soc_dapm_effect:
- ret = sof_widget_load_process(scomp, index, swidget, tw, &reply);
+ ret = sof_widget_load_process(scomp, index, swidget, tw);
break;
case snd_soc_dapm_mux:
case snd_soc_dapm_demux:
- ret = sof_widget_load_mux(scomp, index, swidget, tw, &reply);
+ ret = sof_widget_load_mux(scomp, index, swidget, tw);
break;
case snd_soc_dapm_switch:
case snd_soc_dapm_dai_link:
@@ -2517,12 +2427,12 @@ static int sof_widget_ready(struct snd_soc_component *scomp, int index,
}
/* check IPC reply */
- if (ret < 0 || reply.rhdr.error < 0) {
+ if (ret < 0) {
dev_err(scomp->dev,
- "error: DSP failed to add widget id %d type %d name : %s stream %s reply %d\n",
+ "error: failed to add widget id %d type %d name : %s stream %s\n",
tw->shift, swidget->id, tw->name,
strnlen(tw->sname, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) > 0
- ? tw->sname : "none", reply.rhdr.error);
+ ? tw->sname : "none");
kfree(swidget);
return ret;
}
@@ -2828,9 +2738,6 @@ static int sof_set_dai_config_multi(struct snd_sof_dev *sdev, u32 size,
continue;
if (strcmp(link->name, dai->name) == 0) {
- struct sof_ipc_reply reply;
- int ret;
-
/*
* the same dai config will be applied to all DAIs in
* the same dai link. We have to ensure that the ipc
@@ -2842,18 +2749,6 @@ static int sof_set_dai_config_multi(struct snd_sof_dev *sdev, u32 size,
dev_dbg(sdev->dev, "set DAI config for %s index %d\n",
dai->name, config[curr_conf].dai_index);
- /* send message to DSP */
- ret = sof_ipc_tx_message(sdev->ipc,
- config[curr_conf].hdr.cmd,
- &config[curr_conf], size,
- &reply, sizeof(reply));
-
- if (ret < 0) {
- dev_err(sdev->dev,
- "error: failed to set DAI config for %s index %d\n",
- dai->name, config[curr_conf].dai_index);
- return ret;
- }
dai->number_configs = num_conf;
dai->current_config = curr_conf;
@@ -2861,9 +2756,6 @@ static int sof_set_dai_config_multi(struct snd_sof_dev *sdev, u32 size,
if (!dai->dai_config)
return -ENOMEM;
- /* set cpu_dai_name */
- dai->cpu_dai_name = link->cpus->dai_name;
-
found = 1;
}
}
@@ -3383,7 +3275,6 @@ static int sof_route_load(struct snd_soc_component *scomp, int index,
struct snd_sof_widget *source_swidget, *sink_swidget;
struct snd_soc_dobj *dobj = &route->dobj;
struct snd_sof_route *sroute;
- struct sof_ipc_reply reply;
int ret = 0;
/* allocate memory for sroute and connect */
@@ -3458,33 +3349,11 @@ static int sof_route_load(struct snd_soc_component *scomp, int index,
route->source, route->sink);
goto err;
} else {
- ret = sof_ipc_tx_message(sdev->ipc,
- connect->hdr.cmd,
- connect, sizeof(*connect),
- &reply, sizeof(reply));
-
- /* check IPC return value */
- if (ret < 0) {
- dev_err(scomp->dev, "error: failed to add route sink %s control %s source %s\n",
- route->sink,
- route->control ? route->control : "none",
- route->source);
- goto err;
- }
-
- /* check IPC reply */
- if (reply.error < 0) {
- dev_err(scomp->dev, "error: DSP failed to add route sink %s control %s source %s result %d\n",
- route->sink,
- route->control ? route->control : "none",
- route->source, reply.error);
- ret = reply.error;
- goto err;
- }
-
sroute->route = route;
dobj->private = sroute;
sroute->private = connect;
+ sroute->src_widget = source_swidget;
+ sroute->sink_widget = sink_swidget;
/* add route to route list */
list_add(&sroute->list, &sdev->route_list);
@@ -3498,50 +3367,6 @@ err:
return ret;
}
-/* Function to set the initial value of SOF kcontrols.
- * The value will be stored in scontrol->control_data
- */
-static int snd_sof_cache_kcontrol_val(struct snd_soc_component *scomp)
-{
- struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
- struct snd_sof_control *scontrol = NULL;
- int ipc_cmd, ctrl_type;
- int ret = 0;
-
- list_for_each_entry(scontrol, &sdev->kcontrol_list, list) {
-
- /* notify DSP of kcontrol values */
- switch (scontrol->cmd) {
- case SOF_CTRL_CMD_VOLUME:
- case SOF_CTRL_CMD_ENUM:
- case SOF_CTRL_CMD_SWITCH:
- ipc_cmd = SOF_IPC_COMP_GET_VALUE;
- ctrl_type = SOF_CTRL_TYPE_VALUE_CHAN_GET;
- break;
- case SOF_CTRL_CMD_BINARY:
- ipc_cmd = SOF_IPC_COMP_GET_DATA;
- ctrl_type = SOF_CTRL_TYPE_DATA_GET;
- break;
- default:
- dev_err(scomp->dev,
- "error: Invalid scontrol->cmd: %d\n",
- scontrol->cmd);
- return -EINVAL;
- }
- ret = snd_sof_ipc_set_get_comp_data(scontrol,
- ipc_cmd, ctrl_type,
- scontrol->cmd,
- false);
- if (ret < 0) {
- dev_warn(scomp->dev,
- "error: kcontrol value get for widget: %d\n",
- scontrol->comp_id);
- }
- }
-
- return ret;
-}
-
int snd_sof_complete_pipeline(struct device *dev,
struct snd_sof_widget *swidget)
{
@@ -3566,31 +3391,84 @@ int snd_sof_complete_pipeline(struct device *dev,
return 1;
}
+/**
+ * sof_set_pipe_widget - Set pipe_widget for a component
+ * @sdev: pointer to struct snd_sof_dev
+ * @pipe_widget: pointer to struct snd_sof_widget of type snd_soc_dapm_scheduler
+ * @swidget: pointer to struct snd_sof_widget that has the same pipeline ID as @pipe_widget
+ *
+ * Return: 0 if successful, -EINVAL on error.
+ * The function checks if @swidget is associated with any volatile controls. If so, setting
+ * the dynamic_pipeline_widget is disallowed.
+ */
+static int sof_set_pipe_widget(struct snd_sof_dev *sdev, struct snd_sof_widget *pipe_widget,
+ struct snd_sof_widget *swidget)
+{
+ struct snd_sof_control *scontrol;
+
+ if (pipe_widget->dynamic_pipeline_widget) {
+ /* dynamic widgets cannot have volatile kcontrols */
+ list_for_each_entry(scontrol, &sdev->kcontrol_list, list)
+ if (scontrol->comp_id == swidget->comp_id &&
+ (scontrol->access & SNDRV_CTL_ELEM_ACCESS_VOLATILE)) {
+ dev_err(sdev->dev,
+ "error: volatile control found for dynamic widget %s\n",
+ swidget->widget->name);
+ return -EINVAL;
+ }
+ }
+
+ /* set the pipe_widget and apply the dynamic_pipeline_widget_flag */
+ swidget->pipe_widget = pipe_widget;
+ swidget->dynamic_pipeline_widget = pipe_widget->dynamic_pipeline_widget;
+
+ return 0;
+}
+
/* completion - called at completion of firmware loading */
-static void sof_complete(struct snd_soc_component *scomp)
+static int sof_complete(struct snd_soc_component *scomp)
{
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
- struct snd_sof_widget *swidget;
+ struct snd_sof_widget *swidget, *comp_swidget;
+ int ret;
- /* some widget types require completion notificattion */
+ /* set the pipe_widget and apply the dynamic_pipeline_widget_flag */
list_for_each_entry(swidget, &sdev->widget_list, list) {
- if (swidget->complete)
- continue;
-
switch (swidget->id) {
case snd_soc_dapm_scheduler:
- swidget->complete =
- snd_sof_complete_pipeline(scomp->dev, swidget);
+ /*
+ * Apply the dynamic_pipeline_widget flag and set the pipe_widget field
+ * for all widgets that have the same pipeline ID as the scheduler widget
+ */
+ list_for_each_entry_reverse(comp_swidget, &sdev->widget_list, list)
+ if (comp_swidget->pipeline_id == swidget->pipeline_id) {
+ ret = sof_set_pipe_widget(sdev, swidget, comp_swidget);
+ if (ret < 0)
+ return ret;
+ }
break;
default:
break;
}
}
- /*
- * cache initial values of SOF kcontrols by reading DSP value over
- * IPC. It may be overwritten by alsa-mixer after booting up
- */
- snd_sof_cache_kcontrol_val(scomp);
+
+ /* verify topology components loading including dynamic pipelines */
+ if (sof_core_debug & SOF_DBG_VERIFY_TPLG) {
+ ret = sof_set_up_pipelines(scomp->dev, true);
+ if (ret < 0) {
+ dev_err(sdev->dev, "error: topology verification failed %d\n", ret);
+ return ret;
+ }
+
+ ret = sof_tear_down_pipelines(scomp->dev, true);
+ if (ret < 0) {
+ dev_err(sdev->dev, "error: topology tear down pipelines failed %d\n", ret);
+ return ret;
+ }
+ }
+
+ /* set up static pipelines */
+ return sof_set_up_pipelines(scomp->dev, false);
}
/* manifest - optional to inform component of manifest */