summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--sound/soc/sof/intel/hda-dai-ops.c41
-rw-r--r--sound/soc/sof/intel/hda-dai.c124
-rw-r--r--sound/soc/sof/intel/hda.c29
-rw-r--r--sound/soc/sof/intel/hda.h11
-rw-r--r--sound/soc/sof/ipc4-topology.c62
-rw-r--r--sound/soc/sof/ipc4-topology.h3
-rw-r--r--sound/soc/sof/pcm.c58
-rw-r--r--sound/soc/sof/sof-audio.c29
-rw-r--r--sound/soc/sof/sof-audio.h2
9 files changed, 232 insertions, 127 deletions
diff --git a/sound/soc/sof/intel/hda-dai-ops.c b/sound/soc/sof/intel/hda-dai-ops.c
index c50ca9e72d37..1afdb06499a3 100644
--- a/sound/soc/sof/intel/hda-dai-ops.c
+++ b/sound/soc/sof/intel/hda-dai-ops.c
@@ -145,17 +145,9 @@ static struct hdac_ext_stream *hda_assign_hext_stream(struct snd_sof_dev *sdev,
struct snd_soc_dai *cpu_dai,
struct snd_pcm_substream *substream)
{
- struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
- struct snd_soc_dai *dai;
struct hdac_ext_stream *hext_stream;
- /* only allocate a stream_tag for the first DAI in the dailink */
- dai = snd_soc_rtd_to_cpu(rtd, 0);
- if (dai == cpu_dai)
- hext_stream = hda_link_stream_assign(sof_to_bus(sdev), substream);
- else
- hext_stream = snd_soc_dai_get_dma_data(dai, substream);
-
+ hext_stream = hda_link_stream_assign(sof_to_bus(sdev), substream);
if (!hext_stream)
return NULL;
@@ -168,14 +160,9 @@ static void hda_release_hext_stream(struct snd_sof_dev *sdev, struct snd_soc_dai
struct snd_pcm_substream *substream)
{
struct hdac_ext_stream *hext_stream = hda_get_hext_stream(sdev, cpu_dai, substream);
- struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
- struct snd_soc_dai *dai;
- /* only release a stream_tag for the first DAI in the dailink */
- dai = snd_soc_rtd_to_cpu(rtd, 0);
- if (dai == cpu_dai)
- snd_hdac_ext_stream_release(hext_stream, HDAC_EXT_STREAM_TYPE_LINK);
snd_soc_dai_set_dma_data(cpu_dai, substream, NULL);
+ snd_hdac_ext_stream_release(hext_stream, HDAC_EXT_STREAM_TYPE_LINK);
}
static void hda_setup_hext_stream(struct snd_sof_dev *sdev, struct hdac_ext_stream *hext_stream,
@@ -435,28 +422,6 @@ out:
return ret;
}
-static struct hdac_ext_stream *sdw_hda_ipc4_get_hext_stream(struct snd_sof_dev *sdev,
- struct snd_soc_dai *cpu_dai,
- struct snd_pcm_substream *substream)
-{
- struct snd_soc_dapm_widget *w = snd_soc_dai_get_widget(cpu_dai, substream->stream);
- struct snd_sof_widget *swidget = w->dobj.private;
- struct snd_sof_dai *dai = swidget->private;
- struct sof_ipc4_copier *ipc4_copier = dai->private;
- struct sof_ipc4_alh_configuration_blob *blob;
-
- blob = (struct sof_ipc4_alh_configuration_blob *)ipc4_copier->copier_config;
-
- /*
- * Starting with ACE_2_0, re-setting the device_count is mandatory to avoid using
- * the multi-gateway firmware configuration. The DMA hardware can take care of
- * multiple links without needing any firmware assistance
- */
- blob->alh_cfg.device_count = 1;
-
- return hda_ipc4_get_hext_stream(sdev, cpu_dai, substream);
-}
-
static const struct hda_dai_widget_dma_ops hda_ipc4_dma_ops = {
.get_hext_stream = hda_ipc4_get_hext_stream,
.assign_hext_stream = hda_assign_hext_stream,
@@ -498,7 +463,7 @@ static const struct hda_dai_widget_dma_ops dmic_ipc4_dma_ops = {
};
static const struct hda_dai_widget_dma_ops sdw_ipc4_dma_ops = {
- .get_hext_stream = sdw_hda_ipc4_get_hext_stream,
+ .get_hext_stream = hda_ipc4_get_hext_stream,
.assign_hext_stream = hda_assign_hext_stream,
.release_hext_stream = hda_release_hext_stream,
.setup_hext_stream = hda_setup_hext_stream,
diff --git a/sound/soc/sof/intel/hda-dai.c b/sound/soc/sof/intel/hda-dai.c
index c1682bcdb5a6..3f2fd84907d2 100644
--- a/sound/soc/sof/intel/hda-dai.c
+++ b/sound/soc/sof/intel/hda-dai.c
@@ -29,14 +29,6 @@ static bool hda_use_tplg_nhlt;
module_param_named(sof_use_tplg_nhlt, hda_use_tplg_nhlt, bool, 0444);
MODULE_PARM_DESC(sof_use_tplg_nhlt, "SOF topology nhlt override");
-static struct snd_sof_dev *widget_to_sdev(struct snd_soc_dapm_widget *w)
-{
- struct snd_sof_widget *swidget = w->dobj.private;
- struct snd_soc_component *component = swidget->scomp;
-
- return snd_soc_component_get_drvdata(component);
-}
-
int hda_dai_config(struct snd_soc_dapm_widget *w, unsigned int flags,
struct snd_sof_dai_config_data *data)
{
@@ -221,15 +213,15 @@ static int __maybe_unused hda_dai_hw_free(struct snd_pcm_substream *substream,
return hda_link_dma_cleanup(substream, hext_stream, cpu_dai);
}
-static int __maybe_unused hda_dai_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params,
- struct snd_soc_dai *dai)
+static int __maybe_unused hda_dai_hw_params_data(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai,
+ struct snd_sof_dai_config_data *data,
+ unsigned int flags)
{
struct snd_soc_dapm_widget *w = snd_soc_dai_get_widget(dai, substream->stream);
const struct hda_dai_widget_dma_ops *ops = hda_dai_get_ops(substream, dai);
struct hdac_ext_stream *hext_stream;
- struct snd_sof_dai_config_data data = { 0 };
- unsigned int flags = SOF_DAI_CONFIG_FLAGS_HW_PARAMS;
struct snd_sof_dev *sdev = widget_to_sdev(w);
int ret;
@@ -249,9 +241,19 @@ static int __maybe_unused hda_dai_hw_params(struct snd_pcm_substream *substream,
hext_stream = ops->get_hext_stream(sdev, dai, substream);
flags |= SOF_DAI_CONFIG_FLAGS_2_STEP_STOP << SOF_DAI_CONFIG_FLAGS_QUIRK_SHIFT;
- data.dai_data = hdac_stream(hext_stream)->stream_tag - 1;
+ data->dai_data = hdac_stream(hext_stream)->stream_tag - 1;
- return hda_dai_config(w, flags, &data);
+ return hda_dai_config(w, flags, data);
+}
+
+static int __maybe_unused hda_dai_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_sof_dai_config_data data = { 0 };
+ unsigned int flags = SOF_DAI_CONFIG_FLAGS_HW_PARAMS;
+
+ return hda_dai_hw_params_data(substream, params, dai, &data, flags);
}
/*
@@ -341,11 +343,14 @@ static struct sof_ipc4_copier *widget_to_copier(struct snd_soc_dapm_widget *w)
return ipc4_copier;
}
-static int non_hda_dai_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params,
- struct snd_soc_dai *cpu_dai)
+static int non_hda_dai_hw_params_data(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *cpu_dai,
+ struct snd_sof_dai_config_data *data,
+ unsigned int flags)
{
struct snd_soc_dapm_widget *w = snd_soc_dai_get_widget(cpu_dai, substream->stream);
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
struct sof_ipc4_dma_config_tlv *dma_config_tlv;
const struct hda_dai_widget_dma_ops *ops;
struct sof_ipc4_dma_config *dma_config;
@@ -353,6 +358,8 @@ static int non_hda_dai_hw_params(struct snd_pcm_substream *substream,
struct hdac_ext_stream *hext_stream;
struct hdac_stream *hstream;
struct snd_sof_dev *sdev;
+ struct snd_soc_dai *dai;
+ int cpu_dai_id;
int stream_id;
int ret;
@@ -363,9 +370,9 @@ static int non_hda_dai_hw_params(struct snd_pcm_substream *substream,
}
/* use HDaudio stream handling */
- ret = hda_dai_hw_params(substream, params, cpu_dai);
+ ret = hda_dai_hw_params_data(substream, params, cpu_dai, data, flags);
if (ret < 0) {
- dev_err(cpu_dai->dev, "%s: hda_dai_hw_params failed: %d\n", __func__, ret);
+ dev_err(cpu_dai->dev, "%s: hda_dai_hw_params_data failed: %d\n", __func__, ret);
return ret;
}
@@ -392,7 +399,12 @@ static int non_hda_dai_hw_params(struct snd_pcm_substream *substream,
/* configure TLV */
ipc4_copier = widget_to_copier(w);
- dma_config_tlv = &ipc4_copier->dma_config_tlv;
+ for_each_rtd_cpu_dais(rtd, cpu_dai_id, dai) {
+ if (dai == cpu_dai)
+ break;
+ }
+
+ dma_config_tlv = &ipc4_copier->dma_config_tlv[cpu_dai_id];
dma_config_tlv->type = SOF_IPC4_GTW_DMA_CONFIG_ID;
/* dma_config_priv_size is zero */
dma_config_tlv->length = sizeof(dma_config_tlv->dma_config);
@@ -403,13 +415,27 @@ static int non_hda_dai_hw_params(struct snd_pcm_substream *substream,
dma_config->pre_allocated_by_host = 1;
dma_config->dma_channel_id = stream_id - 1;
dma_config->stream_id = stream_id;
- dma_config->dma_stream_channel_map.device_count = 0; /* mapping not used */
+ /*
+ * Currently we use a DMA for each device in ALH blob. The device will
+ * be copied in sof_ipc4_prepare_copier_module.
+ */
+ dma_config->dma_stream_channel_map.device_count = 1;
dma_config->dma_priv_config_size = 0;
skip_tlv:
return 0;
}
+static int non_hda_dai_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *cpu_dai)
+{
+ struct snd_sof_dai_config_data data = { 0 };
+ unsigned int flags = SOF_DAI_CONFIG_FLAGS_HW_PARAMS;
+
+ return non_hda_dai_hw_params_data(substream, params, cpu_dai, &data, flags);
+}
+
static int non_hda_dai_prepare(struct snd_pcm_substream *substream,
struct snd_soc_dai *cpu_dai)
{
@@ -436,15 +462,29 @@ static const struct snd_soc_dai_ops dmic_dai_ops = {
int sdw_hda_dai_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *cpu_dai,
- int link_id)
+ int link_id,
+ int intel_alh_id)
{
struct snd_soc_dapm_widget *w = snd_soc_dai_get_widget(cpu_dai, substream->stream);
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct sof_ipc4_dma_config_tlv *dma_config_tlv;
+ struct snd_sof_dai_config_data data = { 0 };
+ unsigned int flags = SOF_DAI_CONFIG_FLAGS_HW_PARAMS;
const struct hda_dai_widget_dma_ops *ops;
+ struct sof_ipc4_dma_config *dma_config;
+ struct sof_ipc4_copier *ipc4_copier;
struct hdac_ext_stream *hext_stream;
+ struct snd_soc_dai *dai;
struct snd_sof_dev *sdev;
+ bool cpu_dai_found = false;
+ int cpu_dai_id;
+ int ch_mask;
int ret;
+ int i;
- ret = non_hda_dai_hw_params(substream, params, cpu_dai);
+ data.dai_index = (link_id << 8) | cpu_dai->id;
+ data.dai_node_id = intel_alh_id;
+ ret = non_hda_dai_hw_params_data(substream, params, cpu_dai, &data, flags);
if (ret < 0) {
dev_err(cpu_dai->dev, "%s: non_hda_dai_hw_params failed %d\n", __func__, ret);
return ret;
@@ -457,9 +497,25 @@ int sdw_hda_dai_hw_params(struct snd_pcm_substream *substream,
if (!hext_stream)
return -ENODEV;
- /* in the case of SoundWire we need to program the PCMSyCM registers */
+ /*
+ * in the case of SoundWire we need to program the PCMSyCM registers. In case
+ * of aggregated devices, we need to define the channel mask for each sublink
+ * by reconstructing the split done in soc-pcm.c
+ */
+ for_each_rtd_cpu_dais(rtd, cpu_dai_id, dai) {
+ if (dai == cpu_dai) {
+ cpu_dai_found = true;
+ break;
+ }
+ }
+
+ if (!cpu_dai_found)
+ return -ENODEV;
+
+ ch_mask = GENMASK(params_channels(params) - 1, 0);
+
ret = hdac_bus_eml_sdw_map_stream_ch(sof_to_bus(sdev), link_id, cpu_dai->id,
- GENMASK(params_channels(params) - 1, 0),
+ ch_mask,
hdac_stream(hext_stream)->stream_tag,
substream->stream);
if (ret < 0) {
@@ -468,6 +524,22 @@ int sdw_hda_dai_hw_params(struct snd_pcm_substream *substream,
return ret;
}
+ ipc4_copier = widget_to_copier(w);
+ dma_config_tlv = &ipc4_copier->dma_config_tlv[cpu_dai_id];
+ dma_config = &dma_config_tlv->dma_config;
+ dma_config->dma_stream_channel_map.mapping[0].device = data.dai_index;
+ dma_config->dma_stream_channel_map.mapping[0].channel_mask = ch_mask;
+
+ /*
+ * copy the dma_config_tlv to all ipc4_copier in the same link. Because only one copier
+ * will be handled in sof_ipc4_prepare_copier_module.
+ */
+ for_each_rtd_cpu_dais(rtd, i, dai) {
+ w = snd_soc_dai_get_widget(dai, substream->stream);
+ ipc4_copier = widget_to_copier(w);
+ memcpy(&ipc4_copier->dma_config_tlv[cpu_dai_id], dma_config_tlv,
+ sizeof(*dma_config_tlv));
+ }
return 0;
}
diff --git a/sound/soc/sof/intel/hda.c b/sound/soc/sof/intel/hda.c
index e26b8fd682e5..d38dc43c2f1c 100644
--- a/sound/soc/sof/intel/hda.c
+++ b/sound/soc/sof/intel/hda.c
@@ -31,6 +31,7 @@
#include "../sof-audio.h"
#include "../sof-pci-dev.h"
#include "../ops.h"
+#include "../ipc4-topology.h"
#include "hda.h"
#include "telemetry.h"
@@ -145,12 +146,37 @@ static int sdw_params_stream(struct device *dev,
data.dai_index = (params_data->link_id << 8) | d->id;
data.dai_data = params_data->alh_stream_id;
+ data.dai_node_id = data.dai_data;
return hda_dai_config(w, SOF_DAI_CONFIG_FLAGS_HW_PARAMS, &data);
}
+static int sdw_params_free(struct device *dev, struct sdw_intel_stream_free_data *free_data)
+{
+ struct snd_soc_dai *d = free_data->dai;
+ struct snd_soc_dapm_widget *w = snd_soc_dai_get_widget(d, free_data->substream->stream);
+ struct snd_sof_dev *sdev = widget_to_sdev(w);
+
+ if (sdev->pdata->ipc_type == SOF_IPC_TYPE_4) {
+ struct snd_sof_widget *swidget = w->dobj.private;
+ struct snd_sof_dai *dai = swidget->private;
+ struct sof_ipc4_copier_data *copier_data;
+ struct sof_ipc4_copier *ipc4_copier;
+
+ ipc4_copier = dai->private;
+ ipc4_copier->dai_index = 0;
+ copier_data = &ipc4_copier->data;
+
+ /* clear the node ID */
+ copier_data->gtw_cfg.node_id &= ~SOF_IPC4_NODE_INDEX_MASK;
+ }
+
+ return 0;
+}
+
struct sdw_intel_ops sdw_callback = {
.params_stream = sdw_params_stream,
+ .free_stream = sdw_params_free,
};
static int sdw_ace2x_params_stream(struct device *dev,
@@ -159,7 +185,8 @@ static int sdw_ace2x_params_stream(struct device *dev,
return sdw_hda_dai_hw_params(params_data->substream,
params_data->hw_params,
params_data->dai,
- params_data->link_id);
+ params_data->link_id,
+ params_data->alh_stream_id);
}
static int sdw_ace2x_free_stream(struct device *dev,
diff --git a/sound/soc/sof/intel/hda.h b/sound/soc/sof/intel/hda.h
index b36eb7c78913..f530a05cfc92 100644
--- a/sound/soc/sof/intel/hda.h
+++ b/sound/soc/sof/intel/hda.h
@@ -844,7 +844,8 @@ static inline bool hda_common_check_sdw_irq(struct snd_sof_dev *sdev)
int sdw_hda_dai_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *cpu_dai,
- int link_id);
+ int link_id,
+ int intel_alh_id);
int sdw_hda_dai_hw_free(struct snd_pcm_substream *substream,
struct snd_soc_dai *cpu_dai,
@@ -999,4 +1000,12 @@ int hda_dai_config(struct snd_soc_dapm_widget *w, unsigned int flags,
int hda_link_dma_cleanup(struct snd_pcm_substream *substream, struct hdac_ext_stream *hext_stream,
struct snd_soc_dai *cpu_dai);
+static inline struct snd_sof_dev *widget_to_sdev(struct snd_soc_dapm_widget *w)
+{
+ struct snd_sof_widget *swidget = w->dobj.private;
+ struct snd_soc_component *component = swidget->scomp;
+
+ return snd_soc_component_get_drvdata(component);
+}
+
#endif
diff --git a/sound/soc/sof/ipc4-topology.c b/sound/soc/sof/ipc4-topology.c
index da4a83afb87a..793bca09bbf4 100644
--- a/sound/soc/sof/ipc4-topology.c
+++ b/sound/soc/sof/ipc4-topology.c
@@ -1277,7 +1277,6 @@ static void sof_ipc4_unprepare_copier_module(struct snd_sof_widget *swidget)
}
if (ipc4_copier->dai_type == SOF_DAI_INTEL_ALH) {
- struct sof_ipc4_copier_data *copier_data = &ipc4_copier->data;
struct sof_ipc4_alh_configuration_blob *blob;
unsigned int group_id;
@@ -1287,9 +1286,6 @@ static void sof_ipc4_unprepare_copier_module(struct snd_sof_widget *swidget)
ALH_MULTI_GTW_BASE;
ida_free(&alh_group_ida, group_id);
}
-
- /* clear the node ID */
- copier_data->gtw_cfg.node_id &= ~SOF_IPC4_NODE_INDEX_MASK;
}
}
@@ -1454,6 +1450,7 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget,
u32 deep_buffer_dma_ms = 0;
int output_fmt_index;
bool single_output_format;
+ int i;
dev_dbg(sdev->dev, "copier %s, type %d", swidget->widget->name, swidget->id);
@@ -1671,6 +1668,7 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget,
*/
if (ipc4_copier->dai_type == SOF_DAI_INTEL_ALH) {
struct sof_ipc4_alh_configuration_blob *blob;
+ struct sof_ipc4_dma_config *dma_config;
struct sof_ipc4_copier_data *alh_data;
struct sof_ipc4_copier *alh_copier;
struct snd_sof_widget *w;
@@ -1679,7 +1677,6 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget,
u32 ch_map;
u32 step;
u32 mask;
- int i;
blob = (struct sof_ipc4_alh_configuration_blob *)ipc4_copier->copier_config;
@@ -1703,6 +1700,8 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget,
*/
i = 0;
list_for_each_entry(w, &sdev->widget_list, list) {
+ u32 node_type;
+
if (w->widget->sname &&
strcmp(w->widget->sname, swidget->widget->sname))
continue;
@@ -1710,7 +1709,22 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget,
dai = w->private;
alh_copier = (struct sof_ipc4_copier *)dai->private;
alh_data = &alh_copier->data;
- blob->alh_cfg.mapping[i].device = alh_data->gtw_cfg.node_id;
+ node_type = SOF_IPC4_GET_NODE_TYPE(alh_data->gtw_cfg.node_id);
+ blob->alh_cfg.mapping[i].device = SOF_IPC4_NODE_TYPE(node_type);
+ blob->alh_cfg.mapping[i].device |=
+ SOF_IPC4_NODE_INDEX(alh_copier->dai_index);
+
+ /*
+ * The mapping[i] device in ALH blob should be the same as the
+ * dma_config_tlv[i] mapping device if a dma_config_tlv is present.
+ * The device id will be used for DMA tlv mapping purposes.
+ */
+ if (ipc4_copier->dma_config_tlv[i].length) {
+ dma_config = &ipc4_copier->dma_config_tlv[i].dma_config;
+ blob->alh_cfg.mapping[i].device =
+ dma_config->dma_stream_channel_map.mapping[0].device;
+ }
+
/*
* Set the same channel mask for playback as the audio data is
* duplicated for all speakers. For capture, split the channels
@@ -1789,19 +1803,18 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget,
gtw_cfg_config_length = copier_data->gtw_cfg.config_length * 4;
ipc_size = sizeof(*copier_data) + gtw_cfg_config_length;
- if (ipc4_copier->dma_config_tlv.type == SOF_IPC4_GTW_DMA_CONFIG_ID &&
- ipc4_copier->dma_config_tlv.length) {
- dma_config_tlv_size = sizeof(ipc4_copier->dma_config_tlv) +
- ipc4_copier->dma_config_tlv.dma_config.dma_priv_config_size;
-
- /* paranoia check on TLV size/length */
- if (dma_config_tlv_size != ipc4_copier->dma_config_tlv.length +
- sizeof(uint32_t) * 2) {
- dev_err(sdev->dev, "Invalid configuration, TLV size %d length %d\n",
- dma_config_tlv_size, ipc4_copier->dma_config_tlv.length);
- return -EINVAL;
- }
+ dma_config_tlv_size = 0;
+ for (i = 0; i < SOF_IPC4_DMA_DEVICE_MAX_COUNT; i++) {
+ if (ipc4_copier->dma_config_tlv[i].type != SOF_IPC4_GTW_DMA_CONFIG_ID)
+ continue;
+ dma_config_tlv_size += ipc4_copier->dma_config_tlv[i].length;
+ dma_config_tlv_size +=
+ ipc4_copier->dma_config_tlv[i].dma_config.dma_priv_config_size;
+ dma_config_tlv_size += (sizeof(ipc4_copier->dma_config_tlv[i]) -
+ sizeof(ipc4_copier->dma_config_tlv[i].dma_config));
+ }
+ if (dma_config_tlv_size) {
ipc_size += dma_config_tlv_size;
/* we also need to increase the size at the gtw level */
@@ -2813,17 +2826,24 @@ 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;
- fallthrough;
+ if (flags & SOF_DAI_CONFIG_FLAGS_HW_PARAMS) {
+ copier_data->gtw_cfg.node_id &= ~SOF_IPC4_NODE_INDEX_MASK;
+ copier_data->gtw_cfg.node_id |= SOF_IPC4_NODE_INDEX(data->dai_data);
+ }
+ break;
case SOF_DAI_INTEL_ALH:
/*
* Do not clear the node ID when this op is invoked with
* SOF_DAI_CONFIG_FLAGS_HW_FREE. It is needed to free the group_ida during
- * unprepare.
+ * unprepare. The node_id for multi-gateway DAI's will be overwritten with the
+ * group_id during copier's ipc_prepare op.
*/
if (flags & SOF_DAI_CONFIG_FLAGS_HW_PARAMS) {
+ ipc4_copier->dai_index = data->dai_node_id;
copier_data->gtw_cfg.node_id &= ~SOF_IPC4_NODE_INDEX_MASK;
- copier_data->gtw_cfg.node_id |= SOF_IPC4_NODE_INDEX(data->dai_data);
+ copier_data->gtw_cfg.node_id |= SOF_IPC4_NODE_INDEX(data->dai_node_id);
}
+
break;
case SOF_DAI_INTEL_DMIC:
case SOF_DAI_INTEL_SSP:
diff --git a/sound/soc/sof/ipc4-topology.h b/sound/soc/sof/ipc4-topology.h
index dce174a190dd..6e33208a357f 100644
--- a/sound/soc/sof/ipc4-topology.h
+++ b/sound/soc/sof/ipc4-topology.h
@@ -45,6 +45,7 @@
#define SOF_IPC4_NODE_INDEX_MASK 0xFF
#define SOF_IPC4_NODE_INDEX(x) ((x) & SOF_IPC4_NODE_INDEX_MASK)
#define SOF_IPC4_NODE_TYPE(x) ((x) << 8)
+#define SOF_IPC4_GET_NODE_TYPE(node_id) ((node_id) >> 8)
/* Node ID for SSP type DAI copiers */
#define SOF_IPC4_NODE_INDEX_INTEL_SSP(x) (((x) & 0xf) << 4)
@@ -313,7 +314,7 @@ struct sof_ipc4_copier {
struct sof_ipc4_gtw_attributes *gtw_attr;
u32 dai_type;
int dai_index;
- struct sof_ipc4_dma_config_tlv dma_config_tlv;
+ struct sof_ipc4_dma_config_tlv dma_config_tlv[SOF_IPC4_DMA_DEVICE_MAX_COUNT];
};
/**
diff --git a/sound/soc/sof/pcm.c b/sound/soc/sof/pcm.c
index 33d576b17647..7b88e24b7701 100644
--- a/sound/soc/sof/pcm.c
+++ b/sound/soc/sof/pcm.c
@@ -196,9 +196,8 @@ static int sof_pcm_hw_free(struct snd_soc_component *component,
{
struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component);
- const struct sof_ipc_pcm_ops *pcm_ops = sof_ipc_get_ops(sdev, pcm);
struct snd_sof_pcm *spcm;
- int ret, err = 0;
+ int ret;
/* nothing to do for BE */
if (rtd->dai_link->no_pcm)
@@ -211,42 +210,18 @@ static int sof_pcm_hw_free(struct snd_soc_component *component,
dev_dbg(component->dev, "pcm: free stream %d dir %d\n",
spcm->pcm.pcm_id, substream->stream);
- if (spcm->prepared[substream->stream]) {
- /* stop DMA first if needed */
- if (pcm_ops && pcm_ops->platform_stop_during_hw_free)
- snd_sof_pcm_platform_trigger(sdev, substream, SNDRV_PCM_TRIGGER_STOP);
-
- /* free PCM in the DSP */
- if (pcm_ops && pcm_ops->hw_free) {
- ret = pcm_ops->hw_free(component, substream);
- if (ret < 0)
- err = ret;
- }
-
- spcm->prepared[substream->stream] = false;
- }
-
- /* reset DMA */
- ret = snd_sof_pcm_platform_hw_free(sdev, substream);
- if (ret < 0) {
- dev_err(component->dev, "error: platform hw free failed\n");
- err = ret;
- }
-
- /* free the DAPM widget list */
- ret = sof_widget_list_free(sdev, spcm, substream->stream);
- if (ret < 0)
- err = ret;
+ ret = sof_pcm_stream_free(sdev, substream, spcm, substream->stream, true);
cancel_work_sync(&spcm->stream[substream->stream].period_elapsed_work);
- return err;
+ return ret;
}
static int sof_pcm_prepare(struct snd_soc_component *component,
struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component);
struct snd_sof_pcm *spcm;
int ret;
@@ -258,8 +233,18 @@ static int sof_pcm_prepare(struct snd_soc_component *component,
if (!spcm)
return -EINVAL;
- if (spcm->prepared[substream->stream])
- return 0;
+ if (spcm->prepared[substream->stream]) {
+ if (!spcm->pending_stop[substream->stream])
+ return 0;
+
+ /*
+ * this case should be reached in case of xruns where we absolutely
+ * want to free-up and reset all PCM/DMA resources
+ */
+ ret = sof_pcm_stream_free(sdev, substream, spcm, substream->stream, true);
+ if (ret < 0)
+ return ret;
+ }
dev_dbg(component->dev, "pcm: prepare stream %d dir %d\n",
spcm->pcm.pcm_id, substream->stream);
@@ -302,6 +287,8 @@ static int sof_pcm_trigger(struct snd_soc_component *component,
dev_dbg(component->dev, "pcm: trigger stream %d dir %d cmd %d\n",
spcm->pcm.pcm_id, substream->stream, cmd);
+ spcm->pending_stop[substream->stream] = false;
+
switch (cmd) {
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
ipc_first = true;
@@ -371,6 +358,15 @@ static int sof_pcm_trigger(struct snd_soc_component *component,
/* invoke platform trigger to stop DMA even if pcm_ops isn't set or if it failed */
if (!pcm_ops || !pcm_ops->platform_stop_during_hw_free)
snd_sof_pcm_platform_trigger(sdev, substream, cmd);
+
+ /*
+ * set the pending_stop flag to indicate that pipeline stop has been delayed.
+ * This will be used later to stop the pipelines during prepare when recovering
+ * from xruns.
+ */
+ if (pcm_ops && pcm_ops->platform_stop_during_hw_free &&
+ cmd == SNDRV_PCM_TRIGGER_STOP)
+ spcm->pending_stop[substream->stream] = true;
break;
default:
break;
diff --git a/sound/soc/sof/sof-audio.c b/sound/soc/sof/sof-audio.c
index e693dcb475e4..32fef64ef10d 100644
--- a/sound/soc/sof/sof-audio.c
+++ b/sound/soc/sof/sof-audio.c
@@ -834,35 +834,48 @@ int sof_pcm_stream_free(struct snd_sof_dev *sdev, struct snd_pcm_substream *subs
{
const struct sof_ipc_pcm_ops *pcm_ops = sof_ipc_get_ops(sdev, pcm);
int ret;
+ int err = 0;
if (spcm->prepared[substream->stream]) {
/* stop DMA first if needed */
if (pcm_ops && pcm_ops->platform_stop_during_hw_free)
snd_sof_pcm_platform_trigger(sdev, substream, SNDRV_PCM_TRIGGER_STOP);
- /* Send PCM_FREE IPC to reset pipeline */
+ /* free PCM in the DSP */
if (pcm_ops && pcm_ops->hw_free) {
ret = pcm_ops->hw_free(sdev->component, substream);
- if (ret < 0)
- return ret;
+ if (ret < 0) {
+ dev_err(sdev->dev, "%s: pcm_ops hw_free failed %d\n",
+ __func__, ret);
+ err = ret;
+ }
}
spcm->prepared[substream->stream] = false;
+ spcm->pending_stop[substream->stream] = false;
}
/* reset the DMA */
ret = snd_sof_pcm_platform_hw_free(sdev, substream);
- if (ret < 0)
- return ret;
+ if (ret < 0) {
+ dev_err(sdev->dev, "%s: platform hw free failed %d\n",
+ __func__, ret);
+ if (!err)
+ err = ret;
+ }
/* free widget list */
if (free_widget_list) {
ret = sof_widget_list_free(sdev, spcm, dir);
- if (ret < 0)
- dev_err(sdev->dev, "failed to free widgets during suspend\n");
+ if (ret < 0) {
+ dev_err(sdev->dev, "%s: sof_widget_list_free failed %d\n",
+ __func__, ret);
+ if (!err)
+ err = ret;
+ }
}
- return ret;
+ return err;
}
/*
diff --git a/sound/soc/sof/sof-audio.h b/sound/soc/sof/sof-audio.h
index 9ea2ac5adac7..80a5bd69ef1c 100644
--- a/sound/soc/sof/sof-audio.h
+++ b/sound/soc/sof/sof-audio.h
@@ -91,6 +91,7 @@ struct snd_sof_pcm;
struct snd_sof_dai_config_data {
int dai_index;
int dai_data; /* contains DAI-specific information */
+ int dai_node_id; /* contains DAI-specific information for Gateway configuration */
};
/**
@@ -341,6 +342,7 @@ struct snd_sof_pcm {
struct list_head list; /* list in sdev pcm list */
struct snd_pcm_hw_params params[2];
bool prepared[2]; /* PCM_PARAMS set successfully */
+ bool pending_stop[2]; /* only used if (!pcm_ops->platform_stop_during_hw_free) */
};
struct snd_sof_led_control {