Unverified Commit 3d069014 authored by Mark Brown's avatar Mark Brown
Browse files

ASoC: SOF: ipc4: Multi-stream playback and capture support

Merge series from Peter Ujfalusi <peter.ujfalusi@linux.intel.com>:

The following series will enable multi-stream support for playback and capture
streams.
Currently only a single PCM can be connected to a DAI, with the multi-stream
support it is possible to connect multiple PCMs to a single DAI.

To achieve this we need to make sure that DAIs/AIF are only set up once since
other stream could be connected to it later.

We also need to introduce reference or use counting for widgets to make sure
that they are not going to be destroyed while other streams are still using
them.

With the multi-stream support we also need to extend our current locking scheme
which worked well for simple paths.
parents 6c9802b8 251a2b11
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -162,6 +162,8 @@ 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);
int widget_in_list(struct snd_soc_dapm_widget_list *list,
		   struct snd_soc_dapm_widget *widget);

#define dpcm_be_dai_startup_rollback(fe, stream, last)	\
						dpcm_be_dai_stop(fe, stream, 0, last)
+3 −0
Original line number Diff line number Diff line
@@ -185,6 +185,9 @@ enum sof_ipc4_pipeline_state {
#define SOF_IPC4_GLB_PIPE_STATE_MASK		GENMASK(15, 0)
#define SOF_IPC4_GLB_PIPE_STATE(x)		((x) << SOF_IPC4_GLB_PIPE_STATE_SHIFT)

/* pipeline set state IPC msg extension */
#define SOF_IPC4_GLB_PIPE_STATE_EXT_MULTI	BIT(0)

/* load library ipc msg */
#define SOF_IPC4_GLB_LOAD_LIBRARY_LIB_ID_SHIFT	16
#define SOF_IPC4_GLB_LOAD_LIBRARY_LIB_ID(x)	((x) << SOF_IPC4_GLB_LOAD_LIBRARY_LIB_ID_SHIFT)
+2 −1
Original line number Diff line number Diff line
@@ -1337,7 +1337,7 @@ static struct snd_soc_pcm_runtime *dpcm_get_be(struct snd_soc_card *card,
	return NULL;
}

static int widget_in_list(struct snd_soc_dapm_widget_list *list,
int widget_in_list(struct snd_soc_dapm_widget_list *list,
		struct snd_soc_dapm_widget *widget)
{
	struct snd_soc_dapm_widget *w;
@@ -1349,6 +1349,7 @@ static int widget_in_list(struct snd_soc_dapm_widget_list *list,

	return 0;
}
EXPORT_SYMBOL_GPL(widget_in_list);

bool dpcm_end_walk_at_be(struct snd_soc_dapm_widget *widget, enum snd_soc_dapm_direction dir)
{
+1 −0
Original line number Diff line number Diff line
@@ -390,6 +390,7 @@ int snd_sof_device_probe(struct device *dev, struct snd_sof_pdata *plat_data)
	INIT_LIST_HEAD(&sdev->pcm_list);
	INIT_LIST_HEAD(&sdev->kcontrol_list);
	INIT_LIST_HEAD(&sdev->widget_list);
	INIT_LIST_HEAD(&sdev->pipeline_list);
	INIT_LIST_HEAD(&sdev->dai_list);
	INIT_LIST_HEAD(&sdev->dai_link_list);
	INIT_LIST_HEAD(&sdev->route_list);
+17 −75
Original line number Diff line number Diff line
@@ -450,6 +450,8 @@ static int ipc4_hda_dai_trigger(struct snd_pcm_substream *substream,
{
	struct hdac_ext_stream *hext_stream = snd_soc_dai_get_dma_data(dai, substream);
	struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(dai->component);
	struct snd_sof_widget *pipe_widget;
	struct sof_ipc4_pipeline *pipeline;
	struct snd_soc_pcm_runtime *rtd;
	struct snd_sof_widget *swidget;
	struct snd_soc_dapm_widget *w;
@@ -466,18 +468,30 @@ static int ipc4_hda_dai_trigger(struct snd_pcm_substream *substream,

	w = snd_soc_dai_get_widget(dai, substream->stream);
	swidget = w->dobj.private;
	pipe_widget = swidget->spipe->pipe_widget;
	pipeline = pipe_widget->private;

	switch (cmd) {
	case SNDRV_PCM_TRIGGER_START:
	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
		snd_hdac_ext_stream_start(hext_stream);
		if (pipeline->state != SOF_IPC4_PIPE_PAUSED) {
			ret = sof_ipc4_set_pipeline_state(sdev, pipe_widget->instance_id,
							  SOF_IPC4_PIPE_PAUSED);
			if (ret < 0)
				return ret;
			pipeline->state = SOF_IPC4_PIPE_PAUSED;
		}

		ret = sof_ipc4_set_pipeline_state(sdev, pipe_widget->instance_id,
						  SOF_IPC4_PIPE_RUNNING);
		if (ret < 0)
			return ret;
		pipeline->state = SOF_IPC4_PIPE_RUNNING;
		break;
	case SNDRV_PCM_TRIGGER_SUSPEND:
	case SNDRV_PCM_TRIGGER_STOP:
	{
		struct snd_sof_widget *pipe_widget = swidget->pipe_widget;
		struct sof_ipc4_pipeline *pipeline = pipe_widget->private;

		ret = sof_ipc4_set_pipeline_state(sdev, pipe_widget->instance_id,
						  SOF_IPC4_PIPE_PAUSED);
		if (ret < 0)
@@ -503,9 +517,6 @@ static int ipc4_hda_dai_trigger(struct snd_pcm_substream *substream,
	}
	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
	{
		struct snd_sof_widget *pipe_widget = swidget->pipe_widget;
		struct sof_ipc4_pipeline *pipeline = pipe_widget->private;

		ret = sof_ipc4_set_pipeline_state(sdev, pipe_widget->instance_id,
						  SOF_IPC4_PIPE_PAUSED);
		if (ret < 0)
@@ -703,64 +714,6 @@ static const struct snd_soc_dai_ops ipc3_ssp_dai_ops = {
	.shutdown = ssp_dai_shutdown,
};

static int ipc4_be_dai_common_trigger(struct snd_soc_dai *dai, int cmd, int stream)
{
	struct snd_sof_widget *pipe_widget;
	struct sof_ipc4_pipeline *pipeline;
	struct snd_sof_widget *swidget;
	struct snd_soc_dapm_widget *w;
	struct snd_sof_dev *sdev;
	int ret;

	w = snd_soc_dai_get_widget(dai, stream);
	swidget = w->dobj.private;
	pipe_widget = swidget->pipe_widget;
	pipeline = pipe_widget->private;
	sdev = snd_soc_component_get_drvdata(swidget->scomp);

	switch (cmd) {
	case SNDRV_PCM_TRIGGER_SUSPEND:
	case SNDRV_PCM_TRIGGER_STOP:
		ret = sof_ipc4_set_pipeline_state(sdev, pipe_widget->instance_id,
						  SOF_IPC4_PIPE_PAUSED);
		if (ret < 0)
			return ret;
		pipeline->state = SOF_IPC4_PIPE_PAUSED;

		ret = sof_ipc4_set_pipeline_state(sdev, pipe_widget->instance_id,
						  SOF_IPC4_PIPE_RESET);
		if (ret < 0)
			return ret;
		pipeline->state = SOF_IPC4_PIPE_RESET;
		break;
	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
		ret = sof_ipc4_set_pipeline_state(sdev, pipe_widget->instance_id,
						  SOF_IPC4_PIPE_PAUSED);
		if (ret < 0)
			return ret;
		pipeline->state = SOF_IPC4_PIPE_PAUSED;
		break;
	default:
		break;
	}

	return 0;
}

static int ipc4_be_dai_trigger(struct snd_pcm_substream *substream,
			       int cmd, struct snd_soc_dai *dai)
{
	return ipc4_be_dai_common_trigger(dai, cmd, substream->stream);
}

static const struct snd_soc_dai_ops ipc4_dmic_dai_ops = {
	.trigger = ipc4_be_dai_trigger,
};

static const struct snd_soc_dai_ops ipc4_ssp_dai_ops = {
	.trigger = ipc4_be_dai_trigger,
};

void hda_set_dai_drv_ops(struct snd_sof_dev *sdev, struct snd_sof_dsp_ops *ops)
{
	int i;
@@ -785,14 +738,6 @@ void hda_set_dai_drv_ops(struct snd_sof_dev *sdev, struct snd_sof_dsp_ops *ops)
		struct sof_ipc4_fw_data *ipc4_data = sdev->private;

		for (i = 0; i < ops->num_drv; i++) {
			if (strstr(ops->drv[i].name, "DMIC")) {
				ops->drv[i].ops = &ipc4_dmic_dai_ops;
				continue;
			}
			if (strstr(ops->drv[i].name, "SSP")) {
				ops->drv[i].ops = &ipc4_ssp_dai_ops;
				continue;
			}
#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC)
			if (strstr(ops->drv[i].name, "iDisp") ||
			    strstr(ops->drv[i].name, "Analog") ||
@@ -804,9 +749,6 @@ void hda_set_dai_drv_ops(struct snd_sof_dev *sdev, struct snd_sof_dsp_ops *ops)
		if (!hda_use_tplg_nhlt)
			ipc4_data->nhlt = intel_nhlt_init(sdev->dev);

		if (IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL_SOUNDWIRE))
			sdw_callback.trigger = ipc4_be_dai_common_trigger;

		break;
	}
	default:
Loading