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);
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)
......
......@@ -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)
......
......@@ -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)
{
......
......@@ -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);
......
......@@ -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:
......
......@@ -12,7 +12,8 @@
#include "ipc3-priv.h"
/* 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 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
struct snd_sof_widget *swidget;
bool widget_found = false;
u32 ipc_cmd, msg_bytes;
int ret = 0;
list_for_each_entry(swidget, &sdev->widget_list, list) {
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
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
* would always be > 0 in this case. For the others, just return the cached value if the
* widget is not set up.
* 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;
goto unlock;
/*
* 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
sizeof(struct sof_abi_hdr);
break;
default:
return -EINVAL;
ret = -EINVAL;
goto unlock;
}
cdata->rhdr.hdr.size = msg_bytes;
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)
......@@ -108,7 +122,7 @@ static void snd_sof_refresh_control(struct snd_sof_control *scontrol)
/* refresh the component data from DSP */
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) {
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,
/* notify DSP of mixer updates */
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) {
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,
/* notify DSP of mixer updates */
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) {
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,
/* notify DSP of enum updates */
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) {
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,
/* notify DSP of byte control updates */
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;
}
......@@ -438,7 +452,7 @@ static int sof_ipc3_bytes_ext_put(struct snd_sof_control *scontrol,
/* notify DSP of byte control updates */
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;
}
......@@ -468,7 +482,7 @@ static int sof_ipc3_bytes_ext_volatile_get(struct snd_sof_control *scontrol,
cdata->data->abi = SOF_ABI_VERSION;
/* 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)
return ret;
......@@ -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)
if (scontrol->comp_id == swidget->comp_id) {
/* 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) {
dev_err(sdev->dev,
"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,
if (swidget->dynamic_pipeline_widget)
continue;
ret = sof_ipc3_set_get_kcontrol_data(scontrol, false);
ret = sof_ipc3_set_get_kcontrol_data(scontrol, false, false);
if (ret < 0)
dev_warn(sdev->dev,
"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)
return ret;
}
swidget->complete = sof_ipc3_complete_pipeline(sdev, swidget);
if (swidget->complete < 0)
return swidget->complete;
swidget->spipe->complete = sof_ipc3_complete_pipeline(sdev, swidget);
if (swidget->spipe->complete < 0)
return swidget->spipe->complete;
break;
default:
break;
......@@ -2264,7 +2264,7 @@ static int sof_tear_down_left_over_pipelines(struct snd_sof_dev *sdev)
for_each_pcm_streams(dir) {
struct snd_pcm_substream *substream = spcm->stream[dir].substream;
if (!substream || !substream->runtime)
if (!substream || !substream->runtime || spcm->stream[dir].suspend_ignored)
continue;
if (spcm->stream[dir].list) {
......@@ -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 */
if (!verify && !swidget->dynamic_pipeline_widget &&
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->complete = 0;
mutex_unlock(&swidget->setup_mutex);
if (swidget->spipe)
swidget->spipe->complete = 0;
continue;
}
......@@ -2426,6 +2429,24 @@ static int sof_ipc3_parse_manifest(struct snd_soc_component *scomp, int index,
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 */
static enum sof_tokens host_token_list[] = {
SOF_CORE_TOKENS,
......@@ -2537,4 +2558,5 @@ const struct sof_ipc_tplg_ops ipc3_tplg_ops = {
.set_up_all_pipelines = sof_ipc3_set_up_all_pipelines,
.tear_down_all_pipelines = sof_ipc3_tear_down_all_pipelines,
.parse_manifest = sof_ipc3_parse_manifest,
.link_setup = sof_ipc3_link_setup,
};
......@@ -12,7 +12,8 @@
#include "ipc4-priv.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 snd_soc_component *scomp = scontrol->scomp;
......@@ -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 snd_sof_widget *swidget;
bool widget_found = false;
int ret = 0;
/* find widget associated with the control */
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
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
* would always be > 0 in this case. For the others, just return the cached value if the
* widget is not set up.
* 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;
goto unlock;
msg->primary &= ~SOF_IPC4_MOD_INSTANCE_MASK;
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
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_gain *gain = swidget->private;
......@@ -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_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_size = 0;
if (ret < 0) {
......@@ -145,7 +158,7 @@ static bool sof_ipc4_volume_put(struct snd_sof_control *scontrol,
return false;
}
ret = sof_ipc4_set_volume_data(sdev, swidget, scontrol);
ret = sof_ipc4_set_volume_data(sdev, swidget, scontrol, true);
if (ret < 0)
return false;
......@@ -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)
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) {
dev_err(sdev->dev, "%s: kcontrol %d set up failed for widget %s\n",
__func__, scontrol->comp_id, swidget->widget->name);
......
This diff is collapsed.
......@@ -70,6 +70,7 @@ struct sof_ipc4_fw_library {
* base firmware
*
* @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 {
u32 manifest_fw_hdr_offset;
......@@ -82,6 +83,7 @@ struct sof_ipc4_fw_data {
int (*load_library)(struct snd_sof_dev *sdev,
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;
......
......@@ -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);
pipe_widget = swidget->pipe_widget;
pipe_widget = swidget->spipe->pipe_widget;
pipeline = pipe_widget->private;
pipeline->mem_usage += total;
}
......@@ -969,7 +969,7 @@ static void sof_ipc4_unprepare_copier_module(struct snd_sof_widget *swidget)
struct sof_ipc4_pipeline *pipeline;
/* reset pipeline memory usage */
pipe_widget = swidget->pipe_widget;
pipe_widget = swidget->spipe->pipe_widget;
pipeline = pipe_widget->private;
pipeline->mem_usage = 0;
......@@ -1136,7 +1136,7 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget,
struct snd_sof_widget *pipe_widget;
struct sof_ipc4_pipeline *pipeline;
pipe_widget = swidget->pipe_widget;
pipe_widget = swidget->spipe->pipe_widget;
pipeline = pipe_widget->private;
ipc4_copier = (struct sof_ipc4_copier *)swidget->private;
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
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_pipeline *pipeline;
struct sof_ipc4_msg *msg;
......@@ -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)
{
struct sof_ipc4_fw_module *fw_module = swidget->module_info;
struct sof_ipc4_fw_data *ipc4_data = sdev->private;
int ret = 0;
mutex_lock(&ipc4_data->pipeline_state_mutex);
/* freeing a pipeline frees all the widgets associated with it */
if (swidget->id == snd_soc_dapm_scheduler) {
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
ida_free(&fw_module->m_ida, swidget->instance_id);
}
mutex_unlock(&ipc4_data->pipeline_state_mutex);
return ret;
}
......@@ -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_msg msg = {{ 0 }};
u32 header, extension;
int ret;
int ret = 0;
dev_dbg(sdev->dev, "unbind modules %s:%d -> %s:%d\n",
src_widget->widget->name, sroute->src_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 |= SOF_IPC4_MOD_INSTANCE(src_widget->instance_id);
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
if (ret < 0)
dev_err(sdev->dev, "failed to unbind modules %s -> %s\n",
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(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
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)
{
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 snd_sof_dai *dai = swidget->private;
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 *
case SOF_DAI_INTEL_HDA:
gtw_attr = ipc4_copier->gtw_attr;
gtw_attr->lp_buffer_alloc = pipeline->lp_mode;
pipeline->skip_during_fe_trigger = true;
fallthrough;
case SOF_DAI_INTEL_ALH:
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
for_each_pcm_streams(dir) {
struct snd_pcm_substream *substream = spcm->stream[dir].substream;
if (!substream || !substream->runtime)
if (!substream || !substream->runtime || spcm->stream[dir].suspend_ignored)
continue;
if (spcm->stream[dir].list) {
......@@ -2031,6 +2044,24 @@ static int sof_ipc4_tear_down_all_pipelines(struct snd_sof_dev *sdev, bool verif
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[] = {
SOF_COMP_TOKENS,
SOF_AUDIO_FMT_NUM_TOKENS,
......@@ -2137,4 +2168,5 @@ const struct sof_ipc_tplg_ops ipc4_tplg_ops = {
.parse_manifest = sof_ipc4_parse_manifest,
.dai_get_clk = sof_ipc4_dai_get_clk,
.tear_down_all_pipelines = sof_ipc4_tear_down_all_pipelines,
.link_setup = sof_ipc4_link_setup,
};
......@@ -73,6 +73,7 @@
* @mem_usage: Memory usage
* @state: Pipeline state
* @msg: message structure for pipeline
* @skip_during_fe_trigger: skip triggering this pipeline during the FE DAI trigger
*/
struct sof_ipc4_pipeline {
uint32_t priority;
......@@ -80,8 +81,19 @@ struct sof_ipc4_pipeline {
uint32_t mem_usage;
int state;
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
* @base_config: Available base config
......
......@@ -662,6 +662,8 @@ static int sof_ipc4_init(struct snd_sof_dev *sdev)
{
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);
return 0;
......
......@@ -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);
struct snd_sof_pcm *spcm;
bool reset_hw_params = false;
bool free_widget_list = false;
bool ipc_first = false;
int ret = 0;
......@@ -326,7 +325,6 @@ 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:
ipc_first = true;
......@@ -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 */
if (!ret && reset_hw_params)
ret = sof_pcm_stream_free(sdev, substream, spcm, substream->stream,
free_widget_list);
ret = sof_pcm_stream_free(sdev, substream, spcm, substream->stream, false);
return ret;
}
......
This diff is collapsed.
......@@ -85,6 +85,7 @@ struct snd_sof_widget;
struct snd_sof_route;
struct snd_sof_control;
struct snd_sof_dai;
struct snd_sof_pcm;
struct snd_sof_dai_config_data {
int dai_index;
......@@ -97,6 +98,10 @@ struct snd_sof_dai_config_data {
* @hw_free: Function pointer for hw_free
* @trigger: Function pointer for trigger
* @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 {
int (*hw_params)(struct snd_soc_component *component, struct snd_pcm_substream *substream,
......@@ -106,6 +111,8 @@ struct sof_ipc_pcm_ops {
int (*trigger)(struct snd_soc_component *component, struct snd_pcm_substream *substream,
int cmd);
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 {
* @set_up_all_pipelines: Function pointer for setting up 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
* @link_setup: Function pointer for IPC-specific DAI link set up
*
* Note: function pointers (ops) are optional
*/
......@@ -201,6 +209,7 @@ struct sof_ipc_tplg_ops {
int (*tear_down_all_pipelines)(struct snd_sof_dev *sdev, bool verify);
int (*parse_manifest)(struct snd_soc_component *scomp, int index,
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
......@@ -276,6 +285,16 @@ struct sof_token_info {
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 */
struct snd_sof_pcm_stream {
u32 comp_id;
......@@ -291,6 +310,7 @@ struct snd_sof_pcm_stream {
* active or not while suspending the stream
*/
bool suspend_ignored;
struct snd_sof_pcm_stream_pipeline_list pipeline_list;
};
/* ALSA SOF PCM device */
......@@ -362,17 +382,21 @@ struct snd_sof_widget {
struct snd_soc_component *scomp;
int comp_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
* up in the DSP.
*/
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 id; /* id is the DAPM widget type */
/*
......@@ -393,7 +417,7 @@ struct snd_sof_widget {
struct snd_soc_dapm_widget *widget;
struct list_head list; /* list in sdev widget list */
struct snd_sof_widget *pipe_widget;
struct snd_sof_pipeline *spipe;
void *module_info;
const guid_t uuid;
......@@ -431,6 +455,22 @@ struct snd_sof_widget {
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 */
struct snd_sof_route {
struct snd_soc_component *scomp;
......
......@@ -578,6 +578,7 @@ struct snd_sof_dev {
struct list_head pcm_list;
struct list_head kcontrol_list;
struct list_head widget_list;
struct list_head pipeline_list;
struct list_head dai_list;
struct list_head dai_link_list;
struct list_head route_list;
......
......@@ -1402,10 +1402,11 @@ static int sof_widget_ready(struct snd_soc_component *scomp, int index,
swidget->scomp = scomp;
swidget->widget = w;
swidget->comp_id = sdev->next_comp_id++;
swidget->complete = 0;
swidget->id = w->id;
swidget->pipeline_id = index;
swidget->private = NULL;
mutex_init(&swidget->setup_mutex);
ida_init(&swidget->src_queue_ida);
ida_init(&swidget->sink_queue_ida);
......@@ -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;
list_add(&swidget->list, &sdev->widget_list);
return ret;
......@@ -1608,6 +1626,15 @@ static int sof_widget_unload(struct snd_soc_component *scomp,
sof_disconnect_dai_widget(scomp, widget);
break;
case snd_soc_dapm_scheduler:
{
struct snd_sof_pipeline *spipe = swidget->spipe;
list_del(&spipe->list);
kfree(spipe);
swidget->spipe = NULL;
break;
}
default:
break;
}
......@@ -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_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_private *private = &pcm->priv;
struct snd_sof_pcm *spcm;
......@@ -1696,6 +1724,13 @@ static int sof_dai_load(struct snd_soc_component *scomp, int index,
spcm->pcm = *pcm;
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;
list_add(&spcm->list, &sdev->pcm_list);
......@@ -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,
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;
/* free PCM DMA pages */
......@@ -1782,6 +1819,10 @@ static int sof_dai_unload(struct snd_soc_component *scomp,
if (spcm->pcm.capture)
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 */
list_del(&spcm->list);
kfree(spcm);
......@@ -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);
/*
* Set nonatomic property for FE dai links as their trigger action
* involves IPC's.
*/
if (tplg_ops && tplg_ops->link_setup) {
ret = tplg_ops->link_setup(sdev, link);
if (ret < 0)
return ret;
}
/* Set nonatomic property for FE dai links as their trigger action involves IPC's */
if (!link->no_pcm) {
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;
}
......@@ -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
* @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
*
* 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)
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 *pipe_widget = spipe->pipe_widget;
struct snd_sof_control *scontrol;
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 *
}
}
/* set the pipe_widget and apply the dynamic_pipeline_widget_flag */
swidget->pipe_widget = pipe_widget;
/* set the pipeline and apply the dynamic_pipeline_widget_flag */
swidget->spipe = spipe;
swidget->dynamic_pipeline_widget = pipe_widget->dynamic_pipeline_widget;
return 0;
......@@ -2120,6 +2151,7 @@ static int sof_complete(struct snd_soc_component *scomp)
struct snd_sof_widget *swidget, *comp_swidget;
const struct sof_ipc_tplg_widget_ops *widget_ops;
struct snd_sof_control *scontrol;
struct snd_sof_pipeline *spipe;
int ret;
widget_ops = tplg_ops ? tplg_ops->widget : NULL;
......@@ -2152,23 +2184,21 @@ static int sof_complete(struct snd_soc_component *scomp)
}
/* set the pipe_widget and apply the dynamic_pipeline_widget_flag */
list_for_each_entry(swidget, &sdev->widget_list, list) {
switch (swidget->id) {
case snd_soc_dapm_scheduler:
/*
* 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(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;
}
list_for_each_entry(spipe, &sdev->pipeline_list, list) {
struct snd_sof_widget *pipe_widget = spipe->pipe_widget;
/*
* 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.
* 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->widget->id != snd_soc_dapm_scheduler &&
comp_swidget->pipeline_id == pipe_widget->pipeline_id) {
ret = sof_set_widget_pipeline(sdev, spipe, comp_swidget);
if (ret < 0)
return ret;
}
}
/* 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