summaryrefslogtreecommitdiffstats
path: root/sound/soc/sof/ipc4-pcm.c
diff options
context:
space:
mode:
authorRanjani Sridharan <ranjani.sridharan@linux.intel.com>2023-01-27 14:00:24 +0200
committerMark Brown <broonie@kernel.org>2023-01-27 12:14:07 +0000
commit2d271af1af241e64726ada07c6bef6572f1be6a5 (patch)
tree397cedf0aa8fe15a64394154b42e286ea047647d /sound/soc/sof/ipc4-pcm.c
parent19137532dbe32ff2c8b5b1442c077bf3abff86f3 (diff)
downloadlinux-stable-2d271af1af241e64726ada07c6bef6572f1be6a5.tar.gz
linux-stable-2d271af1af241e64726ada07c6bef6572f1be6a5.tar.bz2
linux-stable-2d271af1af241e64726ada07c6bef6572f1be6a5.zip
ASoC: SOF: ipc4-pcm: Use the PCM stream's pipeline_info during trigger
Use the list of pipelines in the PCM stream's pipeline info to trigger the pipelines in the right order. Add a helper for triggering pipelines in batch mode that will be used to trigger multiple pipelines at the same time. Signed-off-by: Ranjani Sridharan <ranjani.sridharan@linux.intel.com> Reviewed-by: Libin Yang <libin.yang@intel.com> Reviewed-by: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com> Reviewed-by: Péter Ujfalusi <peter.ujfalusi@linux.intel.com> Signed-off-by: Peter Ujfalusi <peter.ujfalusi@linux.intel.com> Link: https://lore.kernel.org/r/20230127120031.10709-12-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown <broonie@kernel.org>
Diffstat (limited to 'sound/soc/sof/ipc4-pcm.c')
-rw-r--r--sound/soc/sof/ipc4-pcm.c137
1 files changed, 102 insertions, 35 deletions
diff --git a/sound/soc/sof/ipc4-pcm.c b/sound/soc/sof/ipc4-pcm.c
index db9d0adb2717..a5482185cd6c 100644
--- a/sound/soc/sof/ipc4-pcm.c
+++ b/sound/soc/sof/ipc4-pcm.c
@@ -13,6 +13,33 @@
#include "ipc4-priv.h"
#include "ipc4-topology.h"
+static int sof_ipc4_set_multi_pipeline_state(struct snd_sof_dev *sdev, u32 state,
+ struct ipc4_pipeline_set_state_data *data)
+{
+ struct sof_ipc4_msg msg = {{ 0 }};
+ u32 primary, ipc_size;
+
+ /* trigger a single pipeline */
+ if (data->count == 1)
+ return sof_ipc4_set_pipeline_state(sdev, data->pipeline_ids[0], state);
+
+ primary = state;
+ primary |= SOF_IPC4_MSG_TYPE_SET(SOF_IPC4_GLB_SET_PIPELINE_STATE);
+ primary |= SOF_IPC4_MSG_DIR(SOF_IPC4_MSG_REQUEST);
+ primary |= SOF_IPC4_MSG_TARGET(SOF_IPC4_FW_GEN_MSG);
+ msg.primary = primary;
+
+ /* trigger multiple pipelines with a single IPC */
+ msg.extension = SOF_IPC4_GLB_PIPE_STATE_EXT_MULTI;
+
+ /* ipc_size includes the count and the pipeline IDs for the number of pipelines */
+ ipc_size = sizeof(u32) * (data->count + 1);
+ msg.data_size = ipc_size;
+ msg.data_ptr = data;
+
+ return sof_ipc_tx_message(sdev->ipc, &msg, ipc_size, NULL, 0);
+}
+
int sof_ipc4_set_pipeline_state(struct snd_sof_dev *sdev, u32 id, u32 state)
{
struct sof_ipc4_msg msg = {{ 0 }};
@@ -37,60 +64,100 @@ static int sof_ipc4_trigger_pipelines(struct snd_soc_component *component,
{
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component);
struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
- struct snd_sof_widget *pipeline_widget;
- struct snd_soc_dapm_widget_list *list;
- struct snd_soc_dapm_widget *widget;
+ struct snd_sof_pcm_stream_pipeline_list *pipeline_list;
+ struct ipc4_pipeline_set_state_data *data;
+ struct snd_sof_widget *pipe_widget;
struct sof_ipc4_pipeline *pipeline;
- struct snd_sof_widget *swidget;
struct snd_sof_pcm *spcm;
- int ret = 0;
- int num_widgets;
+ int ret;
+ int i, j;
spcm = snd_sof_find_spcm_dai(component, rtd);
if (!spcm)
return -EINVAL;
- list = spcm->stream[substream->stream].list;
-
- for_each_dapm_widgets(list, num_widgets, widget) {
- swidget = widget->dobj.private;
+ pipeline_list = &spcm->stream[substream->stream].pipeline_list;
+
+ /* nothing to trigger if the list is empty */
+ if (!pipeline_list->pipe_widgets)
+ return 0;
+
+ /* allocate memory for the pipeline data */
+ data = kzalloc(struct_size(data, pipeline_ids, pipeline_list->count), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ /*
+ * IPC4 requires pipelines to be triggered in order starting at the sink and
+ * walking all the way to the source. So traverse the pipeline_list in the reverse order.
+ * Skip the pipelines that have their skip_during_fe_trigger flag set or if they're already
+ * in the requested state. If there is a fork in the pipeline, the order of triggering
+ * between the left/right paths will be indeterministic. But the sink->source trigger order
+ * sink->source would still be guaranteed for each fork independently.
+ */
+ for (i = pipeline_list->count - 1; i >= 0; i--) {
+ pipe_widget = pipeline_list->pipe_widgets[i];
+ pipeline = pipe_widget->private;
+ if (pipeline->state != state && !pipeline->skip_during_fe_trigger)
+ data->pipeline_ids[data->count++] = pipe_widget->instance_id;
+ }
- if (!swidget)
- continue;
+ /* return if all pipelines are in the requested state already */
+ if (!data->count) {
+ kfree(data);
+ return 0;
+ }
- pipeline_widget = swidget->pipe_widget;
- pipeline = (struct sof_ipc4_pipeline *)pipeline_widget->private;
+ /*
+ * Pause all pipelines. This could result in an extra IPC to pause all pipelines even if
+ * they are already paused. But it helps keep the logic simpler and the firmware handles
+ * the repeated pause gracefully. This can be optimized in the future if needed.
+ */
+ ret = sof_ipc4_set_multi_pipeline_state(sdev, SOF_IPC4_PIPE_PAUSED, data);
+ if (ret < 0) {
+ dev_err(sdev->dev, "failed to pause all pipelines\n");
+ goto free;
+ }
- if (pipeline->state == state || pipeline->skip_during_fe_trigger)
- continue;
+ /* update PAUSED state for all pipelines that were just triggered */
+ for (i = 0; i < data->count; i++) {
+ for (j = 0; j < pipeline_list->count; j++) {
+ pipe_widget = pipeline_list->pipe_widgets[j];
+ pipeline = pipe_widget->private;
- /* first set the pipeline to PAUSED state */
- if (pipeline->state != SOF_IPC4_PIPE_PAUSED) {
- ret = sof_ipc4_set_pipeline_state(sdev, pipeline_widget->instance_id,
- SOF_IPC4_PIPE_PAUSED);
- if (ret < 0) {
- dev_err(sdev->dev, "failed to pause pipeline %d\n",
- swidget->pipeline_id);
- return ret;
+ if (data->pipeline_ids[i] == pipe_widget->instance_id) {
+ pipeline->state = SOF_IPC4_PIPE_PAUSED;
+ break;
}
}
+ }
- pipeline->state = SOF_IPC4_PIPE_PAUSED;
+ /* return if this is the final state */
+ if (state == SOF_IPC4_PIPE_PAUSED)
+ goto free;
- if (pipeline->state == state)
- continue;
+ /* else set the final state in the DSP */
+ ret = sof_ipc4_set_multi_pipeline_state(sdev, state, data);
+ if (ret < 0) {
+ dev_err(sdev->dev, "failed to set final state %d for all pipelines\n", state);
+ goto free;
+ }
- /* then set the final state */
- ret = sof_ipc4_set_pipeline_state(sdev, pipeline_widget->instance_id, state);
- if (ret < 0) {
- dev_err(sdev->dev, "failed to set state %d for pipeline %d\n",
- state, swidget->pipeline_id);
- break;
- }
+ /* update final state for all pipelines that were just triggered */
+ for (i = 0; i < data->count; i++) {
+ for (j = 0; j < pipeline_list->count; j++) {
+ pipe_widget = pipeline_list->pipe_widgets[j];
+ pipeline = pipe_widget->private;
- pipeline->state = state;
+ if (data->pipeline_ids[i] == pipe_widget->instance_id) {
+ pipeline->state = state;
+ break;
+ }
+ }
}
+free:
+ kfree(data);
return ret;
}