Commit 3d069014 authored by Mark Brown's avatar Mark Brown

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
...@@ -162,6 +162,8 @@ int dpcm_be_dai_prepare(struct snd_soc_pcm_runtime *fe, int stream); ...@@ -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 dpcm_dapm_stream_event(struct snd_soc_pcm_runtime *fe, int dir,
int event); int event);
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);
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) \ #define dpcm_be_dai_startup_rollback(fe, stream, last) \
dpcm_be_dai_stop(fe, stream, 0, last) dpcm_be_dai_stop(fe, stream, 0, last)
......
...@@ -185,6 +185,9 @@ enum sof_ipc4_pipeline_state { ...@@ -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_MASK GENMASK(15, 0)
#define SOF_IPC4_GLB_PIPE_STATE(x) ((x) << SOF_IPC4_GLB_PIPE_STATE_SHIFT) #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 */ /* load library ipc msg */
#define SOF_IPC4_GLB_LOAD_LIBRARY_LIB_ID_SHIFT 16 #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) #define SOF_IPC4_GLB_LOAD_LIBRARY_LIB_ID(x) ((x) << SOF_IPC4_GLB_LOAD_LIBRARY_LIB_ID_SHIFT)
......
...@@ -1337,7 +1337,7 @@ static struct snd_soc_pcm_runtime *dpcm_get_be(struct snd_soc_card *card, ...@@ -1337,7 +1337,7 @@ static struct snd_soc_pcm_runtime *dpcm_get_be(struct snd_soc_card *card,
return NULL; 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 *widget)
{ {
struct snd_soc_dapm_widget *w; struct snd_soc_dapm_widget *w;
...@@ -1349,6 +1349,7 @@ static int widget_in_list(struct snd_soc_dapm_widget_list *list, ...@@ -1349,6 +1349,7 @@ static int widget_in_list(struct snd_soc_dapm_widget_list *list,
return 0; 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) bool dpcm_end_walk_at_be(struct snd_soc_dapm_widget *widget, enum snd_soc_dapm_direction dir)
{ {
......
...@@ -390,6 +390,7 @@ int snd_sof_device_probe(struct device *dev, struct snd_sof_pdata *plat_data) ...@@ -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->pcm_list);
INIT_LIST_HEAD(&sdev->kcontrol_list); INIT_LIST_HEAD(&sdev->kcontrol_list);
INIT_LIST_HEAD(&sdev->widget_list); INIT_LIST_HEAD(&sdev->widget_list);
INIT_LIST_HEAD(&sdev->pipeline_list);
INIT_LIST_HEAD(&sdev->dai_list); INIT_LIST_HEAD(&sdev->dai_list);
INIT_LIST_HEAD(&sdev->dai_link_list); INIT_LIST_HEAD(&sdev->dai_link_list);
INIT_LIST_HEAD(&sdev->route_list); INIT_LIST_HEAD(&sdev->route_list);
......
...@@ -450,6 +450,8 @@ static int ipc4_hda_dai_trigger(struct snd_pcm_substream *substream, ...@@ -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 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_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_soc_pcm_runtime *rtd;
struct snd_sof_widget *swidget; struct snd_sof_widget *swidget;
struct snd_soc_dapm_widget *w; struct snd_soc_dapm_widget *w;
...@@ -466,18 +468,30 @@ static int ipc4_hda_dai_trigger(struct snd_pcm_substream *substream, ...@@ -466,18 +468,30 @@ static int ipc4_hda_dai_trigger(struct snd_pcm_substream *substream,
w = snd_soc_dai_get_widget(dai, substream->stream); w = snd_soc_dai_get_widget(dai, substream->stream);
swidget = w->dobj.private; swidget = w->dobj.private;
pipe_widget = swidget->spipe->pipe_widget;
pipeline = pipe_widget->private;
switch (cmd) { switch (cmd) {
case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
snd_hdac_ext_stream_start(hext_stream); 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; break;
case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_STOP: 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, ret = sof_ipc4_set_pipeline_state(sdev, pipe_widget->instance_id,
SOF_IPC4_PIPE_PAUSED); SOF_IPC4_PIPE_PAUSED);
if (ret < 0) if (ret < 0)
...@@ -503,9 +517,6 @@ static int ipc4_hda_dai_trigger(struct snd_pcm_substream *substream, ...@@ -503,9 +517,6 @@ static int ipc4_hda_dai_trigger(struct snd_pcm_substream *substream,
} }
case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 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, ret = sof_ipc4_set_pipeline_state(sdev, pipe_widget->instance_id,
SOF_IPC4_PIPE_PAUSED); SOF_IPC4_PIPE_PAUSED);
if (ret < 0) if (ret < 0)
...@@ -703,64 +714,6 @@ static const struct snd_soc_dai_ops ipc3_ssp_dai_ops = { ...@@ -703,64 +714,6 @@ static const struct snd_soc_dai_ops ipc3_ssp_dai_ops = {
.shutdown = ssp_dai_shutdown, .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) void hda_set_dai_drv_ops(struct snd_sof_dev *sdev, struct snd_sof_dsp_ops *ops)
{ {
int i; int i;
...@@ -785,14 +738,6 @@ void hda_set_dai_drv_ops(struct snd_sof_dev *sdev, struct snd_sof_dsp_ops *ops) ...@@ -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; struct sof_ipc4_fw_data *ipc4_data = sdev->private;
for (i = 0; i < ops->num_drv; i++) { 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 IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC)
if (strstr(ops->drv[i].name, "iDisp") || if (strstr(ops->drv[i].name, "iDisp") ||
strstr(ops->drv[i].name, "Analog") || 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) ...@@ -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) if (!hda_use_tplg_nhlt)
ipc4_data->nhlt = intel_nhlt_init(sdev->dev); 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; break;
} }
default: default:
......
...@@ -12,7 +12,8 @@ ...@@ -12,7 +12,8 @@
#include "ipc3-priv.h" #include "ipc3-priv.h"
/* IPC set()/get() for kcontrols. */ /* IPC set()/get() for kcontrols. */
static int sof_ipc3_set_get_kcontrol_data(struct snd_sof_control *scontrol, bool set) static int sof_ipc3_set_get_kcontrol_data(struct snd_sof_control *scontrol,
bool set, bool lock)
{ {
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scontrol->scomp); struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scontrol->scomp);
struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data; struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data;
...@@ -21,6 +22,7 @@ static int sof_ipc3_set_get_kcontrol_data(struct snd_sof_control *scontrol, bool ...@@ -21,6 +22,7 @@ static int sof_ipc3_set_get_kcontrol_data(struct snd_sof_control *scontrol, bool
struct snd_sof_widget *swidget; struct snd_sof_widget *swidget;
bool widget_found = false; bool widget_found = false;
u32 ipc_cmd, msg_bytes; u32 ipc_cmd, msg_bytes;
int ret = 0;
list_for_each_entry(swidget, &sdev->widget_list, list) { list_for_each_entry(swidget, &sdev->widget_list, list) {
if (swidget->comp_id == scontrol->comp_id) { if (swidget->comp_id == scontrol->comp_id) {
...@@ -35,13 +37,18 @@ static int sof_ipc3_set_get_kcontrol_data(struct snd_sof_control *scontrol, bool ...@@ -35,13 +37,18 @@ static int sof_ipc3_set_get_kcontrol_data(struct snd_sof_control *scontrol, bool
return -EINVAL; return -EINVAL;
} }
if (lock)
mutex_lock(&swidget->setup_mutex);
else
lockdep_assert_held(&swidget->setup_mutex);
/* /*
* Volatile controls should always be part of static pipelines and the widget use_count * Volatile controls should always be part of static pipelines and the
* would always be > 0 in this case. For the others, just return the cached value if the * widget use_count would always be > 0 in this case. For the others,
* widget is not set up. * just return the cached value if the widget is not set up.
*/ */
if (!swidget->use_count) if (!swidget->use_count)
return 0; goto unlock;
/* /*
* Select the IPC cmd and the ctrl_type based on the ctrl_cmd and the * Select the IPC cmd and the ctrl_type based on the ctrl_cmd and the
...@@ -81,13 +88,20 @@ static int sof_ipc3_set_get_kcontrol_data(struct snd_sof_control *scontrol, bool ...@@ -81,13 +88,20 @@ static int sof_ipc3_set_get_kcontrol_data(struct snd_sof_control *scontrol, bool
sizeof(struct sof_abi_hdr); sizeof(struct sof_abi_hdr);
break; break;
default: default:
return -EINVAL; ret = -EINVAL;
goto unlock;
} }
cdata->rhdr.hdr.size = msg_bytes; cdata->rhdr.hdr.size = msg_bytes;
cdata->elems_remaining = 0; cdata->elems_remaining = 0;
return iops->set_get_data(sdev, cdata, cdata->rhdr.hdr.size, set); ret = iops->set_get_data(sdev, cdata, cdata->rhdr.hdr.size, set);
unlock:
if (lock)
mutex_unlock(&swidget->setup_mutex);
return ret;
} }
static void snd_sof_refresh_control(struct snd_sof_control *scontrol) static void snd_sof_refresh_control(struct snd_sof_control *scontrol)
...@@ -108,7 +122,7 @@ static void snd_sof_refresh_control(struct snd_sof_control *scontrol) ...@@ -108,7 +122,7 @@ static void snd_sof_refresh_control(struct snd_sof_control *scontrol)
/* refresh the component data from DSP */ /* refresh the component data from DSP */
scontrol->comp_data_dirty = false; scontrol->comp_data_dirty = false;
ret = sof_ipc3_set_get_kcontrol_data(scontrol, false); ret = sof_ipc3_set_get_kcontrol_data(scontrol, false, true);
if (ret < 0) { if (ret < 0) {
dev_err(scomp->dev, "Failed to get control data: %d\n", ret); dev_err(scomp->dev, "Failed to get control data: %d\n", ret);
...@@ -156,7 +170,7 @@ static bool sof_ipc3_volume_put(struct snd_sof_control *scontrol, ...@@ -156,7 +170,7 @@ static bool sof_ipc3_volume_put(struct snd_sof_control *scontrol,
/* notify DSP of mixer updates */ /* notify DSP of mixer updates */
if (pm_runtime_active(scomp->dev)) { if (pm_runtime_active(scomp->dev)) {
int ret = sof_ipc3_set_get_kcontrol_data(scontrol, true); int ret = sof_ipc3_set_get_kcontrol_data(scontrol, true, true);
if (ret < 0) { if (ret < 0) {
dev_err(scomp->dev, "Failed to set mixer updates for %s\n", dev_err(scomp->dev, "Failed to set mixer updates for %s\n",
...@@ -204,7 +218,7 @@ static bool sof_ipc3_switch_put(struct snd_sof_control *scontrol, ...@@ -204,7 +218,7 @@ static bool sof_ipc3_switch_put(struct snd_sof_control *scontrol,
/* notify DSP of mixer updates */ /* notify DSP of mixer updates */
if (pm_runtime_active(scomp->dev)) { if (pm_runtime_active(scomp->dev)) {
int ret = sof_ipc3_set_get_kcontrol_data(scontrol, true); int ret = sof_ipc3_set_get_kcontrol_data(scontrol, true, true);
if (ret < 0) { if (ret < 0) {
dev_err(scomp->dev, "Failed to set mixer updates for %s\n", dev_err(scomp->dev, "Failed to set mixer updates for %s\n",
...@@ -252,7 +266,7 @@ static bool sof_ipc3_enum_put(struct snd_sof_control *scontrol, ...@@ -252,7 +266,7 @@ static bool sof_ipc3_enum_put(struct snd_sof_control *scontrol,
/* notify DSP of enum updates */ /* notify DSP of enum updates */
if (pm_runtime_active(scomp->dev)) { if (pm_runtime_active(scomp->dev)) {
int ret = sof_ipc3_set_get_kcontrol_data(scontrol, true); int ret = sof_ipc3_set_get_kcontrol_data(scontrol, true, true);
if (ret < 0) { if (ret < 0) {
dev_err(scomp->dev, "Failed to set enum updates for %s\n", dev_err(scomp->dev, "Failed to set enum updates for %s\n",
...@@ -324,7 +338,7 @@ static int sof_ipc3_bytes_put(struct snd_sof_control *scontrol, ...@@ -324,7 +338,7 @@ static int sof_ipc3_bytes_put(struct snd_sof_control *scontrol,
/* notify DSP of byte control updates */ /* notify DSP of byte control updates */
if (pm_runtime_active(scomp->dev)) if (pm_runtime_active(scomp->dev))
return sof_ipc3_set_get_kcontrol_data(scontrol, true); return sof_ipc3_set_get_kcontrol_data(scontrol, true, true);
return 0; return 0;
} }
...@@ -438,7 +452,7 @@ static int sof_ipc3_bytes_ext_put(struct snd_sof_control *scontrol, ...@@ -438,7 +452,7 @@ static int sof_ipc3_bytes_ext_put(struct snd_sof_control *scontrol,
/* notify DSP of byte control updates */ /* notify DSP of byte control updates */
if (pm_runtime_active(scomp->dev)) if (pm_runtime_active(scomp->dev))
return sof_ipc3_set_get_kcontrol_data(scontrol, true); return sof_ipc3_set_get_kcontrol_data(scontrol, true, true);
return 0; return 0;
} }
...@@ -468,7 +482,7 @@ static int sof_ipc3_bytes_ext_volatile_get(struct snd_sof_control *scontrol, ...@@ -468,7 +482,7 @@ static int sof_ipc3_bytes_ext_volatile_get(struct snd_sof_control *scontrol,
cdata->data->abi = SOF_ABI_VERSION; cdata->data->abi = SOF_ABI_VERSION;
/* get all the component data from DSP */ /* get all the component data from DSP */
ret = sof_ipc3_set_get_kcontrol_data(scontrol, false); ret = sof_ipc3_set_get_kcontrol_data(scontrol, false, true);
if (ret < 0) if (ret < 0)
return ret; return ret;
...@@ -647,7 +661,7 @@ static int sof_ipc3_widget_kcontrol_setup(struct snd_sof_dev *sdev, ...@@ -647,7 +661,7 @@ static int sof_ipc3_widget_kcontrol_setup(struct snd_sof_dev *sdev,
list_for_each_entry(scontrol, &sdev->kcontrol_list, list) list_for_each_entry(scontrol, &sdev->kcontrol_list, list)
if (scontrol->comp_id == swidget->comp_id) { if (scontrol->comp_id == swidget->comp_id) {
/* set kcontrol data in DSP */ /* set kcontrol data in DSP */
ret = sof_ipc3_set_get_kcontrol_data(scontrol, true); ret = sof_ipc3_set_get_kcontrol_data(scontrol, true, false);
if (ret < 0) { if (ret < 0) {
dev_err(sdev->dev, dev_err(sdev->dev,
"kcontrol %d set up failed for widget %s\n", "kcontrol %d set up failed for widget %s\n",
...@@ -664,7 +678,7 @@ static int sof_ipc3_widget_kcontrol_setup(struct snd_sof_dev *sdev, ...@@ -664,7 +678,7 @@ static int sof_ipc3_widget_kcontrol_setup(struct snd_sof_dev *sdev,
if (swidget->dynamic_pipeline_widget) if (swidget->dynamic_pipeline_widget)
continue; continue;
ret = sof_ipc3_set_get_kcontrol_data(scontrol, false); ret = sof_ipc3_set_get_kcontrol_data(scontrol, false, false);
if (ret < 0) if (ret < 0)
dev_warn(sdev->dev, dev_warn(sdev->dev,
"kcontrol %d read failed for widget %s\n", "kcontrol %d read failed for widget %s\n",
......
...@@ -2233,9 +2233,9 @@ static int sof_ipc3_set_up_all_pipelines(struct snd_sof_dev *sdev, bool verify) ...@@ -2233,9 +2233,9 @@ static int sof_ipc3_set_up_all_pipelines(struct snd_sof_dev *sdev, bool verify)
return ret; return ret;
} }
swidget->complete = sof_ipc3_complete_pipeline(sdev, swidget); swidget->spipe->complete = sof_ipc3_complete_pipeline(sdev, swidget);
if (swidget->complete < 0) if (swidget->spipe->complete < 0)
return swidget->complete; return swidget->spipe->complete;
break; break;
default: default:
break; break;
...@@ -2264,7 +2264,7 @@ static int sof_tear_down_left_over_pipelines(struct snd_sof_dev *sdev) ...@@ -2264,7 +2264,7 @@ static int sof_tear_down_left_over_pipelines(struct snd_sof_dev *sdev)
for_each_pcm_streams(dir) { for_each_pcm_streams(dir) {
struct snd_pcm_substream *substream = spcm->stream[dir].substream; struct snd_pcm_substream *substream = spcm->stream[dir].substream;
if (!substream || !substream->runtime) if (!substream || !substream->runtime || spcm->stream[dir].suspend_ignored)
continue; continue;
if (spcm->stream[dir].list) { if (spcm->stream[dir].list) {
...@@ -2316,8 +2316,11 @@ static int sof_ipc3_tear_down_all_pipelines(struct snd_sof_dev *sdev, bool verif ...@@ -2316,8 +2316,11 @@ static int sof_ipc3_tear_down_all_pipelines(struct snd_sof_dev *sdev, bool verif
/* Do not free widgets for static pipelines with FW older than SOF2.2 */ /* Do not free widgets for static pipelines with FW older than SOF2.2 */
if (!verify && !swidget->dynamic_pipeline_widget && if (!verify && !swidget->dynamic_pipeline_widget &&
SOF_FW_VER(v->major, v->minor, v->micro) < SOF_FW_VER(2, 2, 0)) { SOF_FW_VER(v->major, v->minor, v->micro) < SOF_FW_VER(2, 2, 0)) {
mutex_lock(&swidget->setup_mutex);
swidget->use_count = 0; swidget->use_count = 0;
swidget->complete = 0; mutex_unlock(&swidget->setup_mutex);
if (swidget->spipe)
swidget->spipe->complete = 0;
continue; continue;
} }
...@@ -2426,6 +2429,24 @@ static int sof_ipc3_parse_manifest(struct snd_soc_component *scomp, int index, ...@@ -2426,6 +2429,24 @@ static int sof_ipc3_parse_manifest(struct snd_soc_component *scomp, int index,
return 0; return 0;
} }
static int sof_ipc3_link_setup(struct snd_sof_dev *sdev, struct snd_soc_dai_link *link)
{
if (link->no_pcm)
return 0;
/*
* set default trigger order for all links. Exceptions to
* the rule will be handled in sof_pcm_dai_link_fixup()
* For playback, the sequence is the following: start FE,
* start BE, stop BE, stop FE; for Capture the sequence is
* inverted start BE, start FE, stop FE, stop BE
*/
link->trigger[SNDRV_PCM_STREAM_PLAYBACK] = SND_SOC_DPCM_TRIGGER_PRE;
link->trigger[SNDRV_PCM_STREAM_CAPTURE] = SND_SOC_DPCM_TRIGGER_POST;
return 0;
}
/* token list for each topology object */ /* token list for each topology object */
static enum sof_tokens host_token_list[] = { static enum sof_tokens host_token_list[] = {
SOF_CORE_TOKENS, SOF_CORE_TOKENS,
...@@ -2537,4 +2558,5 @@ const struct sof_ipc_tplg_ops ipc3_tplg_ops = { ...@@ -2537,4 +2558,5 @@ const struct sof_ipc_tplg_ops ipc3_tplg_ops = {
.set_up_all_pipelines = sof_ipc3_set_up_all_pipelines, .set_up_all_pipelines = sof_ipc3_set_up_all_pipelines,
.tear_down_all_pipelines = sof_ipc3_tear_down_all_pipelines, .tear_down_all_pipelines = sof_ipc3_tear_down_all_pipelines,
.parse_manifest = sof_ipc3_parse_manifest, .parse_manifest = sof_ipc3_parse_manifest,
.link_setup = sof_ipc3_link_setup,
}; };
...@@ -12,7 +12,8 @@ ...@@ -12,7 +12,8 @@
#include "ipc4-priv.h" #include "ipc4-priv.h"
#include "ipc4-topology.h" #include "ipc4-topology.h"
static int sof_ipc4_set_get_kcontrol_data(struct snd_sof_control *scontrol, bool set) static int sof_ipc4_set_get_kcontrol_data(struct snd_sof_control *scontrol,
bool set, bool lock)
{ {
struct sof_ipc4_control_data *cdata = scontrol->ipc_control_data; struct sof_ipc4_control_data *cdata = scontrol->ipc_control_data;
struct snd_soc_component *scomp = scontrol->scomp; struct snd_soc_component *scomp = scontrol->scomp;
...@@ -21,6 +22,7 @@ static int sof_ipc4_set_get_kcontrol_data(struct snd_sof_control *scontrol, bool ...@@ -21,6 +22,7 @@ static int sof_ipc4_set_get_kcontrol_data(struct snd_sof_control *scontrol, bool
struct sof_ipc4_msg *msg = &cdata->msg; struct sof_ipc4_msg *msg = &cdata->msg;
struct snd_sof_widget *swidget; struct snd_sof_widget *swidget;
bool widget_found = false; bool widget_found = false;
int ret = 0;
/* find widget associated with the control */ /* find widget associated with the control */
list_for_each_entry(swidget, &sdev->widget_list, list) { list_for_each_entry(swidget, &sdev->widget_list, list) {
...@@ -35,23 +37,34 @@ static int sof_ipc4_set_get_kcontrol_data(struct snd_sof_control *scontrol, bool ...@@ -35,23 +37,34 @@ static int sof_ipc4_set_get_kcontrol_data(struct snd_sof_control *scontrol, bool
return -ENOENT; return -ENOENT;
} }
if (lock)
mutex_lock(&swidget->setup_mutex);
else
lockdep_assert_held(&swidget->setup_mutex);
/* /*
* Volatile controls should always be part of static pipelines and the widget use_count * Volatile controls should always be part of static pipelines and the
* would always be > 0 in this case. For the others, just return the cached value if the * widget use_count would always be > 0 in this case. For the others,
* widget is not set up. * just return the cached value if the widget is not set up.
*/ */
if (!swidget->use_count) if (!swidget->use_count)
return 0; goto unlock;
msg->primary &= ~SOF_IPC4_MOD_INSTANCE_MASK; msg->primary &= ~SOF_IPC4_MOD_INSTANCE_MASK;
msg->primary |= SOF_IPC4_MOD_INSTANCE(swidget->instance_id); msg->primary |= SOF_IPC4_MOD_INSTANCE(swidget->instance_id);
return iops->set_get_data(sdev, msg, msg->data_size, set); ret = iops->set_get_data(sdev, msg, msg->data_size, set);
unlock:
if (lock)
mutex_unlock(&swidget->setup_mutex);
return ret;
} }
static int static int
sof_ipc4_set_volume_data(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget, sof_ipc4_set_volume_data(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget,
struct snd_sof_control *scontrol) struct snd_sof_control *scontrol, bool lock)
{ {
struct sof_ipc4_control_data *cdata = scontrol->ipc_control_data; struct sof_ipc4_control_data *cdata = scontrol->ipc_control_data;
struct sof_ipc4_gain *gain = swidget->private; struct sof_ipc4_gain *gain = swidget->private;
...@@ -90,7 +103,7 @@ sof_ipc4_set_volume_data(struct snd_sof_dev *sdev, struct snd_sof_widget *swidge ...@@ -90,7 +103,7 @@ sof_ipc4_set_volume_data(struct snd_sof_dev *sdev, struct snd_sof_widget *swidge
msg->data_ptr = &data; msg->data_ptr = &data;
msg->data_size = sizeof(data); msg->data_size = sizeof(data);
ret = sof_ipc4_set_get_kcontrol_data(scontrol, true); ret = sof_ipc4_set_get_kcontrol_data(scontrol, true, lock);
msg->data_ptr = NULL; msg->data_ptr = NULL;
msg->data_size = 0; msg->data_size = 0;
if (ret < 0) { if (ret < 0) {
...@@ -145,7 +158,7 @@ static bool sof_ipc4_volume_put(struct snd_sof_control *scontrol, ...@@ -145,7 +158,7 @@ static bool sof_ipc4_volume_put(struct snd_sof_control *scontrol,
return false; return false;
} }
ret = sof_ipc4_set_volume_data(sdev, swidget, scontrol); ret = sof_ipc4_set_volume_data(sdev, swidget, scontrol, true);
if (ret < 0) if (ret < 0)
return false; return false;
...@@ -175,7 +188,7 @@ static int sof_ipc4_widget_kcontrol_setup(struct snd_sof_dev *sdev, struct snd_s ...@@ -175,7 +188,7 @@ static int sof_ipc4_widget_kcontrol_setup(struct snd_sof_dev *sdev, struct snd_s
list_for_each_entry(scontrol, &sdev->kcontrol_list, list) list_for_each_entry(scontrol, &sdev->kcontrol_list, list)
if (scontrol->comp_id == swidget->comp_id) { if (scontrol->comp_id == swidget->comp_id) {
ret = sof_ipc4_set_volume_data(sdev, swidget, scontrol); ret = sof_ipc4_set_volume_data(sdev, swidget, scontrol, false);
if (ret < 0) { if (ret < 0) {
dev_err(sdev->dev, "%s: kcontrol %d set up failed for widget %s\n", dev_err(sdev->dev, "%s: kcontrol %d set up failed for widget %s\n",
__func__, scontrol->comp_id, swidget->widget->name); __func__, scontrol->comp_id, swidget->widget->name);
......
This diff is collapsed.
...@@ -70,6 +70,7 @@ struct sof_ipc4_fw_library { ...@@ -70,6 +70,7 @@ struct sof_ipc4_fw_library {
* base firmware * base firmware
* *
* @load_library: Callback function for platform dependent library loading * @load_library: Callback function for platform dependent library loading
* @pipeline_state_mutex: Mutex to protect pipeline triggers, ref counts, states and deletion
*/ */
struct sof_ipc4_fw_data { struct sof_ipc4_fw_data {
u32 manifest_fw_hdr_offset; u32 manifest_fw_hdr_offset;
...@@ -82,6 +83,7 @@ struct sof_ipc4_fw_data { ...@@ -82,6 +83,7 @@ struct sof_ipc4_fw_data {
int (*load_library)(struct snd_sof_dev *sdev, int (*load_library)(struct snd_sof_dev *sdev,
struct sof_ipc4_fw_library *fw_lib, bool reload); struct sof_ipc4_fw_library *fw_lib, bool reload);
struct mutex pipeline_state_mutex; /* protect pipeline triggers, ref counts and states */
}; };
extern const struct sof_ipc_fw_loader_ops ipc4_loader_ops; extern const struct sof_ipc_fw_loader_ops ipc4_loader_ops;
......
...@@ -855,7 +855,7 @@ sof_ipc4_update_pipeline_mem_usage(struct snd_sof_dev *sdev, struct snd_sof_widg ...@@ -855,7 +855,7 @@ sof_ipc4_update_pipeline_mem_usage(struct snd_sof_dev *sdev, struct snd_sof_widg
total = SOF_IPC4_FW_PAGE(task_mem + queue_mem); total = SOF_IPC4_FW_PAGE(task_mem + queue_mem);
pipe_widget = swidget->pipe_widget; pipe_widget = swidget->spipe->pipe_widget;
pipeline = pipe_widget->private; pipeline = pipe_widget->private;
pipeline->mem_usage += total; pipeline->mem_usage += total;
} }
...@@ -969,7 +969,7 @@ static void sof_ipc4_unprepare_copier_module(struct snd_sof_widget *swidget) ...@@ -969,7 +969,7 @@ static void sof_ipc4_unprepare_copier_module(struct snd_sof_widget *swidget)
struct sof_ipc4_pipeline *pipeline; struct sof_ipc4_pipeline *pipeline;
/* reset pipeline memory usage */ /* reset pipeline memory usage */
pipe_widget = swidget->pipe_widget; pipe_widget = swidget->spipe->pipe_widget;
pipeline = pipe_widget->private; pipeline = pipe_widget->private;
pipeline->mem_usage = 0; pipeline->mem_usage = 0;
...@@ -1136,7 +1136,7 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget, ...@@ -1136,7 +1136,7 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget,
struct snd_sof_widget *pipe_widget; struct snd_sof_widget *pipe_widget;
struct sof_ipc4_pipeline *pipeline; struct sof_ipc4_pipeline *pipeline;
pipe_widget = swidget->pipe_widget; pipe_widget = swidget->spipe->pipe_widget;
pipeline = pipe_widget->private; pipeline = pipe_widget->private;
ipc4_copier = (struct sof_ipc4_copier *)swidget->private; ipc4_copier = (struct sof_ipc4_copier *)swidget->private;
gtw_attr = ipc4_copier->gtw_attr; gtw_attr = ipc4_copier->gtw_attr;
...@@ -1495,7 +1495,7 @@ static int sof_ipc4_control_setup(struct snd_sof_dev *sdev, struct snd_sof_contr ...@@ -1495,7 +1495,7 @@ static int sof_ipc4_control_setup(struct snd_sof_dev *sdev, struct snd_sof_contr
static int sof_ipc4_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget) static int sof_ipc4_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget)
{ {
struct snd_sof_widget *pipe_widget = swidget->pipe_widget; struct snd_sof_widget *pipe_widget = swidget->spipe->pipe_widget;
struct sof_ipc4_fw_data *ipc4_data = sdev->private; struct sof_ipc4_fw_data *ipc4_data = sdev->private;
struct sof_ipc4_pipeline *pipeline; struct sof_ipc4_pipeline *pipeline;
struct sof_ipc4_msg *msg; struct sof_ipc4_msg *msg;
...@@ -1625,8 +1625,11 @@ static int sof_ipc4_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget ...@@ -1625,8 +1625,11 @@ static int sof_ipc4_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget
static int sof_ipc4_widget_free(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget) static int sof_ipc4_widget_free(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget)
{ {
struct sof_ipc4_fw_module *fw_module = swidget->module_info; struct sof_ipc4_fw_module *fw_module = swidget->module_info;
struct sof_ipc4_fw_data *ipc4_data = sdev->private;
int ret = 0; int ret = 0;
mutex_lock(&ipc4_data->pipeline_state_mutex);
/* freeing a pipeline frees all the widgets associated with it */ /* freeing a pipeline frees all the widgets associated with it */
if (swidget->id == snd_soc_dapm_scheduler) { if (swidget->id == snd_soc_dapm_scheduler) {
struct sof_ipc4_pipeline *pipeline = swidget->private; struct sof_ipc4_pipeline *pipeline = swidget->private;
...@@ -1652,6 +1655,8 @@ static int sof_ipc4_widget_free(struct snd_sof_dev *sdev, struct snd_sof_widget ...@@ -1652,6 +1655,8 @@ static int sof_ipc4_widget_free(struct snd_sof_dev *sdev, struct snd_sof_widget
ida_free(&fw_module->m_ida, swidget->instance_id); ida_free(&fw_module->m_ida, swidget->instance_id);
} }
mutex_unlock(&ipc4_data->pipeline_state_mutex);
return ret; return ret;
} }
...@@ -1805,12 +1810,19 @@ static int sof_ipc4_route_free(struct snd_sof_dev *sdev, struct snd_sof_route *s ...@@ -1805,12 +1810,19 @@ static int sof_ipc4_route_free(struct snd_sof_dev *sdev, struct snd_sof_route *s
struct sof_ipc4_fw_module *sink_fw_module = sink_widget->module_info; struct sof_ipc4_fw_module *sink_fw_module = sink_widget->module_info;
struct sof_ipc4_msg msg = {{ 0 }}; struct sof_ipc4_msg msg = {{ 0 }};
u32 header, extension; u32 header, extension;
int ret; int ret = 0;
dev_dbg(sdev->dev, "unbind modules %s:%d -> %s:%d\n", dev_dbg(sdev->dev, "unbind modules %s:%d -> %s:%d\n",
src_widget->widget->name, sroute->src_queue_id, src_widget->widget->name, sroute->src_queue_id,
sink_widget->widget->name, sroute->dst_queue_id); sink_widget->widget->name, sroute->dst_queue_id);
/*
* routes belonging to the same pipeline will be disconnected by the FW when the pipeline
* is freed. So avoid sending this IPC which will be ignored by the FW anyway.
*/
if (src_widget->spipe->pipe_widget == sink_widget->spipe->pipe_widget)
goto out;
header = src_fw_module->man4_module_entry.id; header = src_fw_module->man4_module_entry.id;
header |= SOF_IPC4_MOD_INSTANCE(src_widget->instance_id); header |= SOF_IPC4_MOD_INSTANCE(src_widget->instance_id);
header |= SOF_IPC4_MSG_TYPE_SET(SOF_IPC4_MOD_UNBIND); header |= SOF_IPC4_MSG_TYPE_SET(SOF_IPC4_MOD_UNBIND);
...@@ -1829,7 +1841,7 @@ static int sof_ipc4_route_free(struct snd_sof_dev *sdev, struct snd_sof_route *s ...@@ -1829,7 +1841,7 @@ static int sof_ipc4_route_free(struct snd_sof_dev *sdev, struct snd_sof_route *s
if (ret < 0) if (ret < 0)
dev_err(sdev->dev, "failed to unbind modules %s -> %s\n", dev_err(sdev->dev, "failed to unbind modules %s -> %s\n",
src_widget->widget->name, sink_widget->widget->name); src_widget->widget->name, sink_widget->widget->name);
out:
sof_ipc4_put_queue_id(sink_widget, sroute->dst_queue_id, SOF_PIN_TYPE_SINK); sof_ipc4_put_queue_id(sink_widget, sroute->dst_queue_id, SOF_PIN_TYPE_SINK);
sof_ipc4_put_queue_id(src_widget, sroute->src_queue_id, SOF_PIN_TYPE_SOURCE); sof_ipc4_put_queue_id(src_widget, sroute->src_queue_id, SOF_PIN_TYPE_SOURCE);
...@@ -1839,7 +1851,7 @@ static int sof_ipc4_route_free(struct snd_sof_dev *sdev, struct snd_sof_route *s ...@@ -1839,7 +1851,7 @@ static int sof_ipc4_route_free(struct snd_sof_dev *sdev, struct snd_sof_route *s
static int sof_ipc4_dai_config(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget, static int sof_ipc4_dai_config(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget,
unsigned int flags, struct snd_sof_dai_config_data *data) unsigned int flags, struct snd_sof_dai_config_data *data)
{ {
struct snd_sof_widget *pipe_widget = swidget->pipe_widget; struct snd_sof_widget *pipe_widget = swidget->spipe->pipe_widget;
struct sof_ipc4_pipeline *pipeline = pipe_widget->private; struct sof_ipc4_pipeline *pipeline = pipe_widget->private;
struct snd_sof_dai *dai = swidget->private; struct snd_sof_dai *dai = swidget->private;
struct sof_ipc4_gtw_attributes *gtw_attr; struct sof_ipc4_gtw_attributes *gtw_attr;
...@@ -1862,6 +1874,7 @@ static int sof_ipc4_dai_config(struct snd_sof_dev *sdev, struct snd_sof_widget * ...@@ -1862,6 +1874,7 @@ static int sof_ipc4_dai_config(struct snd_sof_dev *sdev, struct snd_sof_widget *
case SOF_DAI_INTEL_HDA: case SOF_DAI_INTEL_HDA:
gtw_attr = ipc4_copier->gtw_attr; gtw_attr = ipc4_copier->gtw_attr;
gtw_attr->lp_buffer_alloc = pipeline->lp_mode; gtw_attr->lp_buffer_alloc = pipeline->lp_mode;
pipeline->skip_during_fe_trigger = true;
fallthrough; fallthrough;
case SOF_DAI_INTEL_ALH: case SOF_DAI_INTEL_ALH:
copier_data->gtw_cfg.node_id &= ~SOF_IPC4_NODE_INDEX_MASK; copier_data->gtw_cfg.node_id &= ~SOF_IPC4_NODE_INDEX_MASK;
...@@ -2018,7 +2031,7 @@ static int sof_ipc4_tear_down_all_pipelines(struct snd_sof_dev *sdev, bool verif ...@@ -2018,7 +2031,7 @@ static int sof_ipc4_tear_down_all_pipelines(struct snd_sof_dev *sdev, bool verif
for_each_pcm_streams(dir) { for_each_pcm_streams(dir) {
struct snd_pcm_substream *substream = spcm->stream[dir].substream; struct snd_pcm_substream *substream = spcm->stream[dir].substream;
if (!substream || !substream->runtime) if (!substream || !substream->runtime || spcm->stream[dir].suspend_ignored)
continue; continue;
if (spcm->stream[dir].list) { if (spcm->stream[dir].list) {
...@@ -2031,6 +2044,24 @@ static int sof_ipc4_tear_down_all_pipelines(struct snd_sof_dev *sdev, bool verif ...@@ -2031,6 +2044,24 @@ static int sof_ipc4_tear_down_all_pipelines(struct snd_sof_dev *sdev, bool verif
return 0; return 0;
} }
static int sof_ipc4_link_setup(struct snd_sof_dev *sdev, struct snd_soc_dai_link *link)
{
if (link->no_pcm)
return 0;
/*
* set default trigger order for all links. Exceptions to
* the rule will be handled in sof_pcm_dai_link_fixup()
* For playback, the sequence is the following: start BE,
* start FE, stop FE, stop BE; for Capture the sequence is
* inverted start FE, start BE, stop BE, stop FE
*/
link->trigger[SNDRV_PCM_STREAM_PLAYBACK] = SND_SOC_DPCM_TRIGGER_POST;
link->trigger[SNDRV_PCM_STREAM_CAPTURE] = SND_SOC_DPCM_TRIGGER_PRE;
return 0;
}
static enum sof_tokens common_copier_token_list[] = { static enum sof_tokens common_copier_token_list[] = {
SOF_COMP_TOKENS, SOF_COMP_TOKENS,
SOF_AUDIO_FMT_NUM_TOKENS, SOF_AUDIO_FMT_NUM_TOKENS,
...@@ -2137,4 +2168,5 @@ const struct sof_ipc_tplg_ops ipc4_tplg_ops = { ...@@ -2137,4 +2168,5 @@ const struct sof_ipc_tplg_ops ipc4_tplg_ops = {
.parse_manifest = sof_ipc4_parse_manifest, .parse_manifest = sof_ipc4_parse_manifest,
.dai_get_clk = sof_ipc4_dai_get_clk, .dai_get_clk = sof_ipc4_dai_get_clk,
.tear_down_all_pipelines = sof_ipc4_tear_down_all_pipelines, .tear_down_all_pipelines = sof_ipc4_tear_down_all_pipelines,
.link_setup = sof_ipc4_link_setup,
}; };
...@@ -73,6 +73,7 @@ ...@@ -73,6 +73,7 @@
* @mem_usage: Memory usage * @mem_usage: Memory usage
* @state: Pipeline state * @state: Pipeline state
* @msg: message structure for pipeline * @msg: message structure for pipeline
* @skip_during_fe_trigger: skip triggering this pipeline during the FE DAI trigger
*/ */
struct sof_ipc4_pipeline { struct sof_ipc4_pipeline {
uint32_t priority; uint32_t priority;
...@@ -80,8 +81,19 @@ struct sof_ipc4_pipeline { ...@@ -80,8 +81,19 @@ struct sof_ipc4_pipeline {
uint32_t mem_usage; uint32_t mem_usage;
int state; int state;
struct sof_ipc4_msg msg; struct sof_ipc4_msg msg;
bool skip_during_fe_trigger;
}; };
/**
* struct sof_ipc4_multi_pipeline_data - multi pipeline trigger IPC data
* @count: Number of pipelines to be triggered
* @pipeline_ids: Flexible array of IDs of the pipelines to be triggered
*/
struct ipc4_pipeline_set_state_data {
u32 count;
DECLARE_FLEX_ARRAY(u32, pipeline_ids);
} __packed;
/** /**
* struct sof_ipc4_available_audio_format - Available audio formats * struct sof_ipc4_available_audio_format - Available audio formats
* @base_config: Available base config * @base_config: Available base config
......
...@@ -662,6 +662,8 @@ static int sof_ipc4_init(struct snd_sof_dev *sdev) ...@@ -662,6 +662,8 @@ static int sof_ipc4_init(struct snd_sof_dev *sdev)
{ {
struct sof_ipc4_fw_data *ipc4_data = sdev->private; struct sof_ipc4_fw_data *ipc4_data = sdev->private;
mutex_init(&ipc4_data->pipeline_state_mutex);
xa_init_flags(&ipc4_data->fw_lib_xa, XA_FLAGS_ALLOC); xa_init_flags(&ipc4_data->fw_lib_xa, XA_FLAGS_ALLOC);
return 0; return 0;
......
...@@ -282,7 +282,6 @@ static int sof_pcm_trigger(struct snd_soc_component *component, ...@@ -282,7 +282,6 @@ static int sof_pcm_trigger(struct snd_soc_component *component,
const struct sof_ipc_pcm_ops *pcm_ops = sof_ipc_get_ops(sdev, pcm); const struct sof_ipc_pcm_ops *pcm_ops = sof_ipc_get_ops(sdev, pcm);
struct snd_sof_pcm *spcm; struct snd_sof_pcm *spcm;
bool reset_hw_params = false; bool reset_hw_params = false;
bool free_widget_list = false;
bool ipc_first = false; bool ipc_first = false;
int ret = 0; int ret = 0;
...@@ -326,7 +325,6 @@ static int sof_pcm_trigger(struct snd_soc_component *component, ...@@ -326,7 +325,6 @@ static int sof_pcm_trigger(struct snd_soc_component *component,
spcm->stream[substream->stream].suspend_ignored = true; spcm->stream[substream->stream].suspend_ignored = true;
return 0; return 0;
} }
free_widget_list = true;
fallthrough; fallthrough;
case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_STOP:
ipc_first = true; ipc_first = true;
...@@ -353,8 +351,7 @@ static int sof_pcm_trigger(struct snd_soc_component *component, ...@@ -353,8 +351,7 @@ static int sof_pcm_trigger(struct snd_soc_component *component,
/* free PCM if reset_hw_params is set and the STOP IPC is successful */ /* 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_stream_free(sdev, substream, spcm, substream->stream, ret = sof_pcm_stream_free(sdev, substream, spcm, substream->stream, false);
free_widget_list);
return ret; return ret;
} }
......
This diff is collapsed.
...@@ -85,6 +85,7 @@ struct snd_sof_widget; ...@@ -85,6 +85,7 @@ struct snd_sof_widget;
struct snd_sof_route; struct snd_sof_route;
struct snd_sof_control; struct snd_sof_control;
struct snd_sof_dai; struct snd_sof_dai;
struct snd_sof_pcm;
struct snd_sof_dai_config_data { struct snd_sof_dai_config_data {
int dai_index; int dai_index;
...@@ -97,6 +98,10 @@ struct snd_sof_dai_config_data { ...@@ -97,6 +98,10 @@ struct snd_sof_dai_config_data {
* @hw_free: Function pointer for hw_free * @hw_free: Function pointer for hw_free
* @trigger: Function pointer for trigger * @trigger: Function pointer for trigger
* @dai_link_fixup: Function pointer for DAI link fixup * @dai_link_fixup: Function pointer for DAI link fixup
* @pcm_setup: Function pointer for IPC-specific PCM set up that can be used for allocating
* additional memory in the SOF PCM stream structure
* @pcm_free: Function pointer for PCM free that can be used for freeing any
* additional memory in the SOF PCM stream structure
*/ */
struct sof_ipc_pcm_ops { struct sof_ipc_pcm_ops {
int (*hw_params)(struct snd_soc_component *component, struct snd_pcm_substream *substream, int (*hw_params)(struct snd_soc_component *component, struct snd_pcm_substream *substream,
...@@ -106,6 +111,8 @@ struct sof_ipc_pcm_ops { ...@@ -106,6 +111,8 @@ struct sof_ipc_pcm_ops {
int (*trigger)(struct snd_soc_component *component, struct snd_pcm_substream *substream, int (*trigger)(struct snd_soc_component *component, struct snd_pcm_substream *substream,
int cmd); int cmd);
int (*dai_link_fixup)(struct snd_soc_pcm_runtime *rtd, struct snd_pcm_hw_params *params); int (*dai_link_fixup)(struct snd_soc_pcm_runtime *rtd, struct snd_pcm_hw_params *params);
int (*pcm_setup)(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm);
void (*pcm_free)(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm);
}; };
/** /**
...@@ -180,6 +187,7 @@ struct sof_ipc_tplg_widget_ops { ...@@ -180,6 +187,7 @@ struct sof_ipc_tplg_widget_ops {
* @set_up_all_pipelines: Function pointer for setting up all topology pipelines * @set_up_all_pipelines: Function pointer for setting up all topology pipelines
* @tear_down_all_pipelines: Function pointer for tearing down all topology pipelines * @tear_down_all_pipelines: Function pointer for tearing down all topology pipelines
* @parse_manifest: Function pointer for ipc4 specific parsing of topology manifest * @parse_manifest: Function pointer for ipc4 specific parsing of topology manifest
* @link_setup: Function pointer for IPC-specific DAI link set up
* *
* Note: function pointers (ops) are optional * Note: function pointers (ops) are optional
*/ */
...@@ -201,6 +209,7 @@ struct sof_ipc_tplg_ops { ...@@ -201,6 +209,7 @@ struct sof_ipc_tplg_ops {
int (*tear_down_all_pipelines)(struct snd_sof_dev *sdev, bool verify); int (*tear_down_all_pipelines)(struct snd_sof_dev *sdev, bool verify);
int (*parse_manifest)(struct snd_soc_component *scomp, int index, int (*parse_manifest)(struct snd_soc_component *scomp, int index,
struct snd_soc_tplg_manifest *man); struct snd_soc_tplg_manifest *man);
int (*link_setup)(struct snd_sof_dev *sdev, struct snd_soc_dai_link *link);
}; };
/** struct snd_sof_tuple - Tuple info /** struct snd_sof_tuple - Tuple info
...@@ -276,6 +285,16 @@ struct sof_token_info { ...@@ -276,6 +285,16 @@ struct sof_token_info {
int count; int count;
}; };
/**
* struct snd_sof_pcm_stream_pipeline_list - List of pipelines associated with a PCM stream
* @count: number of pipeline widgets in the @pipe_widgets array
* @pipelines: array of pipelines
*/
struct snd_sof_pcm_stream_pipeline_list {
u32 count;
struct snd_sof_pipeline **pipelines;
};
/* PCM stream, mapped to FW component */ /* PCM stream, mapped to FW component */
struct snd_sof_pcm_stream { struct snd_sof_pcm_stream {
u32 comp_id; u32 comp_id;
...@@ -291,6 +310,7 @@ struct snd_sof_pcm_stream { ...@@ -291,6 +310,7 @@ struct snd_sof_pcm_stream {
* active or not while suspending the stream * active or not while suspending the stream
*/ */
bool suspend_ignored; bool suspend_ignored;
struct snd_sof_pcm_stream_pipeline_list pipeline_list;
}; };
/* ALSA SOF PCM device */ /* ALSA SOF PCM device */
...@@ -362,17 +382,21 @@ struct snd_sof_widget { ...@@ -362,17 +382,21 @@ struct snd_sof_widget {
struct snd_soc_component *scomp; struct snd_soc_component *scomp;
int comp_id; int comp_id;
int pipeline_id; int pipeline_id;
/*
* complete flag is used to indicate that pipeline set up is complete for scheduler type
* widgets. It is unused for all other widget types.
*/
int complete;
/* /*
* the prepared flag is used to indicate that a widget has been prepared for getting set * the prepared flag is used to indicate that a widget has been prepared for getting set
* up in the DSP. * up in the DSP.
*/ */
bool prepared; bool prepared;
int use_count; /* use_count will be protected by the PCM mutex held by the core */
struct mutex setup_mutex; /* to protect the swidget setup and free operations */
/*
* use_count is protected by the PCM mutex held by the core and the
* setup_mutex against non stream domain races (kcontrol access for
* example)
*/
int use_count;
int core; int core;
int id; /* id is the DAPM widget type */ int id; /* id is the DAPM widget type */
/* /*
...@@ -393,7 +417,7 @@ struct snd_sof_widget { ...@@ -393,7 +417,7 @@ struct snd_sof_widget {
struct snd_soc_dapm_widget *widget; struct snd_soc_dapm_widget *widget;
struct list_head list; /* list in sdev widget list */ struct list_head list; /* list in sdev widget list */
struct snd_sof_widget *pipe_widget; struct snd_sof_pipeline *spipe;
void *module_info; void *module_info;
const guid_t uuid; const guid_t uuid;
...@@ -431,6 +455,22 @@ struct snd_sof_widget { ...@@ -431,6 +455,22 @@ struct snd_sof_widget {
void *private; /* core does not touch this */ void *private; /* core does not touch this */
}; };
/** struct snd_sof_pipeline - ASoC SOF pipeline
* @pipe_widget: Pointer to the pipeline widget
* @started_count: Count of number of PCM's that have started this pipeline
* @paused_count: Count of number of PCM's that have started and have currently paused this
pipeline
* @complete: flag used to indicate that pipeline set up is complete.
* @list: List item in sdev pipeline_list
*/
struct snd_sof_pipeline {
struct snd_sof_widget *pipe_widget;
int started_count;
int paused_count;
int complete;
struct list_head list;
};
/* ASoC SOF DAPM route */ /* ASoC SOF DAPM route */
struct snd_sof_route { struct snd_sof_route {
struct snd_soc_component *scomp; struct snd_soc_component *scomp;
......
...@@ -578,6 +578,7 @@ struct snd_sof_dev { ...@@ -578,6 +578,7 @@ struct snd_sof_dev {
struct list_head pcm_list; struct list_head pcm_list;
struct list_head kcontrol_list; struct list_head kcontrol_list;
struct list_head widget_list; struct list_head widget_list;
struct list_head pipeline_list;
struct list_head dai_list; struct list_head dai_list;
struct list_head dai_link_list; struct list_head dai_link_list;
struct list_head route_list; struct list_head route_list;
......
...@@ -1402,10 +1402,11 @@ static int sof_widget_ready(struct snd_soc_component *scomp, int index, ...@@ -1402,10 +1402,11 @@ static int sof_widget_ready(struct snd_soc_component *scomp, int index,
swidget->scomp = scomp; swidget->scomp = scomp;
swidget->widget = w; swidget->widget = w;
swidget->comp_id = sdev->next_comp_id++; swidget->comp_id = sdev->next_comp_id++;
swidget->complete = 0;
swidget->id = w->id; swidget->id = w->id;
swidget->pipeline_id = index; swidget->pipeline_id = index;
swidget->private = NULL; swidget->private = NULL;
mutex_init(&swidget->setup_mutex);
ida_init(&swidget->src_queue_ida); ida_init(&swidget->src_queue_ida);
ida_init(&swidget->sink_queue_ida); ida_init(&swidget->sink_queue_ida);
...@@ -1553,6 +1554,23 @@ static int sof_widget_ready(struct snd_soc_component *scomp, int index, ...@@ -1553,6 +1554,23 @@ static int sof_widget_ready(struct snd_soc_component *scomp, int index,
} }
} }
/* create and add pipeline for scheduler type widgets */
if (w->id == snd_soc_dapm_scheduler) {
struct snd_sof_pipeline *spipe;
spipe = kzalloc(sizeof(*spipe), GFP_KERNEL);
if (!spipe) {
kfree(swidget->private);
kfree(swidget->tuples);
kfree(swidget);
return -ENOMEM;
}
spipe->pipe_widget = swidget;
swidget->spipe = spipe;
list_add(&spipe->list, &sdev->pipeline_list);
}
w->dobj.private = swidget; w->dobj.private = swidget;
list_add(&swidget->list, &sdev->widget_list); list_add(&swidget->list, &sdev->widget_list);
return ret; return ret;
...@@ -1608,6 +1626,15 @@ static int sof_widget_unload(struct snd_soc_component *scomp, ...@@ -1608,6 +1626,15 @@ static int sof_widget_unload(struct snd_soc_component *scomp,
sof_disconnect_dai_widget(scomp, widget); sof_disconnect_dai_widget(scomp, widget);
break; break;
case snd_soc_dapm_scheduler:
{
struct snd_sof_pipeline *spipe = swidget->spipe;
list_del(&spipe->list);
kfree(spipe);
swidget->spipe = NULL;
break;
}
default: default:
break; break;
} }
...@@ -1669,6 +1696,7 @@ static int sof_dai_load(struct snd_soc_component *scomp, int index, ...@@ -1669,6 +1696,7 @@ static int sof_dai_load(struct snd_soc_component *scomp, int index,
struct snd_soc_tplg_pcm *pcm, struct snd_soc_dai *dai) struct snd_soc_tplg_pcm *pcm, struct snd_soc_dai *dai)
{ {
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
const struct sof_ipc_pcm_ops *ipc_pcm_ops = sof_ipc_get_ops(sdev, pcm);
struct snd_soc_tplg_stream_caps *caps; struct snd_soc_tplg_stream_caps *caps;
struct snd_soc_tplg_private *private = &pcm->priv; struct snd_soc_tplg_private *private = &pcm->priv;
struct snd_sof_pcm *spcm; struct snd_sof_pcm *spcm;
...@@ -1696,6 +1724,13 @@ static int sof_dai_load(struct snd_soc_component *scomp, int index, ...@@ -1696,6 +1724,13 @@ static int sof_dai_load(struct snd_soc_component *scomp, int index,
spcm->pcm = *pcm; spcm->pcm = *pcm;
dev_dbg(scomp->dev, "tplg: load pcm %s\n", pcm->dai_name); dev_dbg(scomp->dev, "tplg: load pcm %s\n", pcm->dai_name);
/* perform pcm set op */
if (ipc_pcm_ops && ipc_pcm_ops->pcm_setup) {
ret = ipc_pcm_ops->pcm_setup(sdev, spcm);
if (ret < 0)
return ret;
}
dai_drv->dobj.private = spcm; dai_drv->dobj.private = spcm;
list_add(&spcm->list, &sdev->pcm_list); list_add(&spcm->list, &sdev->pcm_list);
...@@ -1773,6 +1808,8 @@ static int sof_dai_load(struct snd_soc_component *scomp, int index, ...@@ -1773,6 +1808,8 @@ static int sof_dai_load(struct snd_soc_component *scomp, int index,
static int sof_dai_unload(struct snd_soc_component *scomp, static int sof_dai_unload(struct snd_soc_component *scomp,
struct snd_soc_dobj *dobj) struct snd_soc_dobj *dobj)
{ {
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
const struct sof_ipc_pcm_ops *ipc_pcm_ops = sof_ipc_get_ops(sdev, pcm);
struct snd_sof_pcm *spcm = dobj->private; struct snd_sof_pcm *spcm = dobj->private;
/* free PCM DMA pages */ /* free PCM DMA pages */
...@@ -1782,6 +1819,10 @@ static int sof_dai_unload(struct snd_soc_component *scomp, ...@@ -1782,6 +1819,10 @@ static int sof_dai_unload(struct snd_soc_component *scomp,
if (spcm->pcm.capture) if (spcm->pcm.capture)
snd_dma_free_pages(&spcm->stream[SNDRV_PCM_STREAM_CAPTURE].page_table); snd_dma_free_pages(&spcm->stream[SNDRV_PCM_STREAM_CAPTURE].page_table);
/* perform pcm free op */
if (ipc_pcm_ops && ipc_pcm_ops->pcm_free)
ipc_pcm_ops->pcm_free(sdev, spcm);
/* remove from list and free spcm */ /* remove from list and free spcm */
list_del(&spcm->list); list_del(&spcm->list);
kfree(spcm); kfree(spcm);
...@@ -1813,26 +1854,15 @@ static int sof_link_load(struct snd_soc_component *scomp, int index, struct snd_ ...@@ -1813,26 +1854,15 @@ static int sof_link_load(struct snd_soc_component *scomp, int index, struct snd_
} }
link->platforms->name = dev_name(scomp->dev); link->platforms->name = dev_name(scomp->dev);
/* if (tplg_ops && tplg_ops->link_setup) {
* Set nonatomic property for FE dai links as their trigger action ret = tplg_ops->link_setup(sdev, link);
* involves IPC's. if (ret < 0)
*/ return ret;
}
/* Set nonatomic property for FE dai links as their trigger action involves IPC's */
if (!link->no_pcm) { if (!link->no_pcm) {
link->nonatomic = true; link->nonatomic = true;
/*
* set default trigger order for all links. Exceptions to
* the rule will be handled in sof_pcm_dai_link_fixup()
* For playback, the sequence is the following: start FE,
* start BE, stop BE, stop FE; for Capture the sequence is
* inverted start BE, start FE, stop FE, stop BE
*/
link->trigger[SNDRV_PCM_STREAM_PLAYBACK] =
SND_SOC_DPCM_TRIGGER_PRE;
link->trigger[SNDRV_PCM_STREAM_CAPTURE] =
SND_SOC_DPCM_TRIGGER_POST;
/* nothing more to do for FE dai links */
return 0; return 0;
} }
...@@ -2079,18 +2109,19 @@ static int sof_route_load(struct snd_soc_component *scomp, int index, ...@@ -2079,18 +2109,19 @@ static int sof_route_load(struct snd_soc_component *scomp, int index,
} }
/** /**
* sof_set_pipe_widget - Set pipe_widget for a component * sof_set_widget_pipeline - Set pipeline for a component
* @sdev: pointer to struct snd_sof_dev * @sdev: pointer to struct snd_sof_dev
* @pipe_widget: pointer to struct snd_sof_widget of type snd_soc_dapm_scheduler * @spipe: pointer to struct snd_sof_pipeline
* @swidget: pointer to struct snd_sof_widget that has the same pipeline ID as @pipe_widget * @swidget: pointer to struct snd_sof_widget that has the same pipeline ID as @pipe_widget
* *
* Return: 0 if successful, -EINVAL on error. * Return: 0 if successful, -EINVAL on error.
* The function checks if @swidget is associated with any volatile controls. If so, setting * The function checks if @swidget is associated with any volatile controls. If so, setting
* the dynamic_pipeline_widget is disallowed. * the dynamic_pipeline_widget is disallowed.
*/ */
static int sof_set_pipe_widget(struct snd_sof_dev *sdev, struct snd_sof_widget *pipe_widget, static int sof_set_widget_pipeline(struct snd_sof_dev *sdev, struct snd_sof_pipeline *spipe,
struct snd_sof_widget *swidget) struct snd_sof_widget *swidget)
{ {
struct snd_sof_widget *pipe_widget = spipe->pipe_widget;
struct snd_sof_control *scontrol; struct snd_sof_control *scontrol;
if (pipe_widget->dynamic_pipeline_widget) { if (pipe_widget->dynamic_pipeline_widget) {
...@@ -2105,8 +2136,8 @@ static int sof_set_pipe_widget(struct snd_sof_dev *sdev, struct snd_sof_widget * ...@@ -2105,8 +2136,8 @@ static int sof_set_pipe_widget(struct snd_sof_dev *sdev, struct snd_sof_widget *
} }
} }
/* set the pipe_widget and apply the dynamic_pipeline_widget_flag */ /* set the pipeline and apply the dynamic_pipeline_widget_flag */
swidget->pipe_widget = pipe_widget; swidget->spipe = spipe;
swidget->dynamic_pipeline_widget = pipe_widget->dynamic_pipeline_widget; swidget->dynamic_pipeline_widget = pipe_widget->dynamic_pipeline_widget;
return 0; return 0;
...@@ -2120,6 +2151,7 @@ static int sof_complete(struct snd_soc_component *scomp) ...@@ -2120,6 +2151,7 @@ static int sof_complete(struct snd_soc_component *scomp)
struct snd_sof_widget *swidget, *comp_swidget; struct snd_sof_widget *swidget, *comp_swidget;
const struct sof_ipc_tplg_widget_ops *widget_ops; const struct sof_ipc_tplg_widget_ops *widget_ops;
struct snd_sof_control *scontrol; struct snd_sof_control *scontrol;
struct snd_sof_pipeline *spipe;
int ret; int ret;
widget_ops = tplg_ops ? tplg_ops->widget : NULL; widget_ops = tplg_ops ? tplg_ops->widget : NULL;
...@@ -2152,23 +2184,21 @@ static int sof_complete(struct snd_soc_component *scomp) ...@@ -2152,23 +2184,21 @@ static int sof_complete(struct snd_soc_component *scomp)
} }
/* set the pipe_widget and apply the dynamic_pipeline_widget_flag */ /* set the pipe_widget and apply the dynamic_pipeline_widget_flag */
list_for_each_entry(swidget, &sdev->widget_list, list) { list_for_each_entry(spipe, &sdev->pipeline_list, list) {
switch (swidget->id) { struct snd_sof_widget *pipe_widget = spipe->pipe_widget;
case snd_soc_dapm_scheduler:
/* /*
* Apply the dynamic_pipeline_widget flag and set the pipe_widget field * 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 * for all widgets that have the same pipeline ID as the scheduler widget.
*/ * Skip the scheduler widgets as they have their pipeline set during widget_ready
list_for_each_entry(comp_swidget, &sdev->widget_list, list) */
if (comp_swidget->pipeline_id == swidget->pipeline_id) { list_for_each_entry(comp_swidget, &sdev->widget_list, list)
ret = sof_set_pipe_widget(sdev, swidget, comp_swidget); if (comp_swidget->widget->id != snd_soc_dapm_scheduler &&
if (ret < 0) comp_swidget->pipeline_id == pipe_widget->pipeline_id) {
return ret; ret = sof_set_widget_pipeline(sdev, spipe, comp_swidget);
} if (ret < 0)
break; return ret;
default: }
break;
}
} }
/* verify topology components loading including dynamic pipelines */ /* verify topology components loading including dynamic pipelines */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment