summaryrefslogtreecommitdiffstats
path: root/sound/soc/sof/ipc4-topology.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-08-07 13:17:52 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-08-07 13:17:52 +0000
commit3afb00d3f86d3d924f88b56fa8285d4e9db85852 (patch)
tree95a985d3019522cea546b7d8df621369bc44fc6c /sound/soc/sof/ipc4-topology.c
parentAdding debian version 6.9.12-1. (diff)
downloadlinux-3afb00d3f86d3d924f88b56fa8285d4e9db85852.tar.xz
linux-3afb00d3f86d3d924f88b56fa8285d4e9db85852.zip
Merging upstream version 6.10.3.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'sound/soc/sof/ipc4-topology.c')
-rw-r--r--sound/soc/sof/ipc4-topology.c489
1 files changed, 379 insertions, 110 deletions
diff --git a/sound/soc/sof/ipc4-topology.c b/sound/soc/sof/ipc4-topology.c
index 35941027b7..4d261b9e6d 100644
--- a/sound/soc/sof/ipc4-topology.c
+++ b/sound/soc/sof/ipc4-topology.c
@@ -3,7 +3,7 @@
// This file is provided under a dual BSD/GPLv2 license. When using or
// redistributing this file, you may do so under either license.
//
-// Copyright(c) 2022 Intel Corporation. All rights reserved.
+// Copyright(c) 2022 Intel Corporation
//
//
#include <linux/bitfield.h>
@@ -195,9 +195,10 @@ static void sof_ipc4_dbg_audio_format(struct device *dev, struct sof_ipc4_pin_fo
for (i = 0; i < num_formats; i++) {
struct sof_ipc4_audio_format *fmt = &pin_fmt[i].audio_fmt;
dev_dbg(dev,
- "Pin index #%d: %uHz, %ubit (ch_map %#x ch_cfg %u interleaving_style %u fmt_cfg %#x) buffer size %d\n",
- pin_fmt[i].pin_index, fmt->sampling_frequency, fmt->bit_depth, fmt->ch_map,
- fmt->ch_cfg, fmt->interleaving_style, fmt->fmt_cfg,
+ "Pin index #%d: %uHz, %ubit, %luch (ch_map %#x ch_cfg %u interleaving_style %u fmt_cfg %#x) buffer size %d\n",
+ pin_fmt[i].pin_index, fmt->sampling_frequency, fmt->bit_depth,
+ SOF_IPC4_AUDIO_FORMAT_CFG_CHANNELS_COUNT(fmt->fmt_cfg),
+ fmt->ch_map, fmt->ch_cfg, fmt->interleaving_style, fmt->fmt_cfg,
pin_fmt[i].buffer_size);
}
}
@@ -415,6 +416,52 @@ static void sof_ipc4_widget_update_kcontrol_module_id(struct snd_sof_widget *swi
}
}
+static int
+sof_ipc4_update_card_components_string(struct snd_sof_widget *swidget,
+ struct snd_sof_pcm *spcm, int dir)
+{
+ struct snd_sof_widget *pipe_widget = swidget->spipe->pipe_widget;
+ struct sof_ipc4_pipeline *pipeline = pipe_widget->private;
+ struct snd_soc_component *scomp = spcm->scomp;
+ struct snd_soc_card *card = scomp->card;
+ const char *pt_marker = "iec61937-pcm";
+
+ /*
+ * Update the card's components list with iec61937-pcm and a list of PCM
+ * ids where ChainDMA is enabled.
+ * These PCMs can be used for bytestream passthrough.
+ */
+ if (!pipeline->use_chain_dma)
+ return 0;
+
+ if (card->components) {
+ const char *tmp = card->components;
+
+ if (strstr(card->components, pt_marker))
+ card->components = devm_kasprintf(card->dev, GFP_KERNEL,
+ "%s,%d",
+ card->components,
+ spcm->pcm.pcm_id);
+ else
+ card->components = devm_kasprintf(card->dev, GFP_KERNEL,
+ "%s %s:%d",
+ card->components,
+ pt_marker,
+ spcm->pcm.pcm_id);
+
+ devm_kfree(card->dev, tmp);
+ } else {
+ card->components = devm_kasprintf(card->dev, GFP_KERNEL,
+ "%s:%d", pt_marker,
+ spcm->pcm.pcm_id);
+ }
+
+ if (!card->components)
+ return -ENOMEM;
+
+ return 0;
+}
+
static int sof_ipc4_widget_setup_pcm(struct snd_sof_widget *swidget)
{
struct sof_ipc4_available_audio_format *available_fmt;
@@ -460,6 +507,10 @@ static int sof_ipc4_widget_setup_pcm(struct snd_sof_widget *swidget)
if (!spcm)
goto skip_gtw_cfg;
+ ret = sof_ipc4_update_card_components_string(swidget, spcm, dir);
+ if (ret)
+ goto free_available_fmt;
+
if (dir == SNDRV_PCM_STREAM_PLAYBACK) {
struct snd_sof_pcm_stream *sps = &spcm->stream[dir];
@@ -594,7 +645,6 @@ static int sof_ipc4_widget_setup_comp_dai(struct snd_sof_widget *swidget)
switch (ipc4_copier->dai_type) {
case SOF_DAI_INTEL_ALH:
{
- struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
struct sof_ipc4_alh_configuration_blob *blob;
struct snd_soc_dapm_path *p;
struct snd_sof_widget *w;
@@ -1078,42 +1128,50 @@ static int sof_ipc4_widget_assign_instance_id(struct snd_sof_dev *sdev,
/* update hw_params based on the audio stream format */
static int sof_ipc4_update_hw_params(struct snd_sof_dev *sdev, struct snd_pcm_hw_params *params,
- struct sof_ipc4_audio_format *fmt)
+ struct sof_ipc4_audio_format *fmt, u32 param_to_update)
{
- snd_pcm_format_t snd_fmt;
struct snd_interval *i;
- struct snd_mask *m;
- int valid_bits = SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(fmt->fmt_cfg);
- unsigned int channels, rate;
- switch (valid_bits) {
- case 16:
- snd_fmt = SNDRV_PCM_FORMAT_S16_LE;
- break;
- case 24:
- snd_fmt = SNDRV_PCM_FORMAT_S24_LE;
- break;
- case 32:
- snd_fmt = SNDRV_PCM_FORMAT_S32_LE;
- break;
- default:
- dev_err(sdev->dev, "invalid PCM valid_bits %d\n", valid_bits);
- return -EINVAL;
+ if (param_to_update & BIT(SNDRV_PCM_HW_PARAM_FORMAT)) {
+ int valid_bits = SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(fmt->fmt_cfg);
+ snd_pcm_format_t snd_fmt;
+ struct snd_mask *m;
+
+ switch (valid_bits) {
+ case 16:
+ snd_fmt = SNDRV_PCM_FORMAT_S16_LE;
+ break;
+ case 24:
+ snd_fmt = SNDRV_PCM_FORMAT_S24_LE;
+ break;
+ case 32:
+ snd_fmt = SNDRV_PCM_FORMAT_S32_LE;
+ break;
+ default:
+ dev_err(sdev->dev, "invalid PCM valid_bits %d\n", valid_bits);
+ return -EINVAL;
+ }
+
+ m = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
+ snd_mask_none(m);
+ snd_mask_set_format(m, snd_fmt);
}
- m = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
- snd_mask_none(m);
- snd_mask_set_format(m, snd_fmt);
+ if (param_to_update & BIT(SNDRV_PCM_HW_PARAM_RATE)) {
+ unsigned int rate = fmt->sampling_frequency;
- rate = fmt->sampling_frequency;
- i = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
- i->min = rate;
- i->max = rate;
+ i = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
+ i->min = rate;
+ i->max = rate;
+ }
- channels = SOF_IPC4_AUDIO_FORMAT_CFG_CHANNELS_COUNT(fmt->fmt_cfg);
- i = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
- i->min = channels;
- i->max = channels;
+ if (param_to_update & BIT(SNDRV_PCM_HW_PARAM_CHANNELS)) {
+ unsigned int channels = SOF_IPC4_AUDIO_FORMAT_CFG_CHANNELS_COUNT(fmt->fmt_cfg);
+
+ i = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+ i->min = channels;
+ i->max = channels;
+ }
return 0;
}
@@ -1300,12 +1358,17 @@ static void sof_ipc4_unprepare_copier_module(struct snd_sof_widget *swidget)
ipc4_copier = dai->private;
if (pipeline->use_chain_dma) {
- pipeline->msg.primary = 0;
+ /*
+ * Preserve the DMA Link ID and clear other bits since
+ * the DMA Link ID is only configured once during
+ * dai_config, other fields are expected to be 0 for
+ * re-configuration
+ */
+ pipeline->msg.primary &= SOF_IPC4_GLB_CHAIN_DMA_LINK_ID_MASK;
pipeline->msg.extension = 0;
}
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;
@@ -1315,9 +1378,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;
}
}
@@ -1375,13 +1435,16 @@ static int snd_sof_get_hw_config_params(struct snd_sof_dev *sdev, struct snd_sof
return 0;
}
-static int snd_sof_get_nhlt_endpoint_data(struct snd_sof_dev *sdev, struct snd_sof_dai *dai,
- struct snd_pcm_hw_params *params, u32 dai_index,
- u32 linktype, u8 dir, u32 **dst, u32 *len)
+static int
+snd_sof_get_nhlt_endpoint_data(struct snd_sof_dev *sdev, struct snd_sof_dai *dai,
+ bool single_bitdepth,
+ struct snd_pcm_hw_params *params, u32 dai_index,
+ u32 linktype, u8 dir, u32 **dst, u32 *len)
{
struct sof_ipc4_fw_data *ipc4_data = sdev->private;
struct nhlt_specific_cfg *cfg;
int sample_rate, channel_count;
+ bool format_change = false;
int bit_depth, ret;
u32 nhlt_type;
int dev_type = 0;
@@ -1390,9 +1453,18 @@ static int snd_sof_get_nhlt_endpoint_data(struct snd_sof_dev *sdev, struct snd_s
switch (linktype) {
case SOF_DAI_INTEL_DMIC:
nhlt_type = NHLT_LINK_DMIC;
- bit_depth = params_width(params);
channel_count = params_channels(params);
sample_rate = params_rate(params);
+ bit_depth = params_width(params);
+ /*
+ * Look for 32-bit blob first instead of 16-bit if copier
+ * supports multiple formats
+ */
+ if (bit_depth == 16 && !single_bitdepth) {
+ dev_dbg(sdev->dev, "Looking for 32-bit blob first for DMIC\n");
+ format_change = true;
+ bit_depth = 32;
+ }
break;
case SOF_DAI_INTEL_SSP:
nhlt_type = NHLT_LINK_SSP;
@@ -1426,30 +1498,83 @@ static int snd_sof_get_nhlt_endpoint_data(struct snd_sof_dev *sdev, struct snd_s
dir, dev_type);
if (!cfg) {
+ bool get_new_blob = false;
+
+ if (format_change) {
+ /*
+ * The 32-bit blob was not found in NHLT table, try to
+ * look for one based on the params
+ */
+ bit_depth = params_width(params);
+ format_change = false;
+ get_new_blob = true;
+ } else if (linktype == SOF_DAI_INTEL_DMIC && !single_bitdepth) {
+ /*
+ * The requested 32-bit blob (no format change for the
+ * blob request) was not found in NHLT table, try to
+ * look for 16-bit blob if the copier supports multiple
+ * formats
+ */
+ bit_depth = 16;
+ format_change = true;
+ get_new_blob = true;
+ }
+
+ if (get_new_blob) {
+ cfg = intel_nhlt_get_endpoint_blob(sdev->dev, ipc4_data->nhlt,
+ dai_index, nhlt_type,
+ bit_depth, bit_depth,
+ channel_count, sample_rate,
+ dir, dev_type);
+ if (cfg)
+ goto out;
+ }
+
dev_err(sdev->dev,
"no matching blob for sample rate: %d sample width: %d channels: %d\n",
sample_rate, bit_depth, channel_count);
return -EINVAL;
}
+out:
/* config length should be in dwords */
*len = cfg->size >> 2;
*dst = (u32 *)cfg->caps;
+ if (format_change) {
+ /*
+ * Update the params to reflect that different blob was loaded
+ * instead of the requested bit depth (16 -> 32 or 32 -> 16).
+ * This information is going to be used by the caller to find
+ * matching copier format on the dai side.
+ */
+ struct snd_mask *m;
+
+ m = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
+ snd_mask_none(m);
+ if (bit_depth == 16)
+ snd_mask_set_format(m, SNDRV_PCM_FORMAT_S16_LE);
+ else
+ snd_mask_set_format(m, SNDRV_PCM_FORMAT_S32_LE);
+
+ }
+
return 0;
}
#else
-static int snd_sof_get_nhlt_endpoint_data(struct snd_sof_dev *sdev, struct snd_sof_dai *dai,
- struct snd_pcm_hw_params *params, u32 dai_index,
- u32 linktype, u8 dir, u32 **dst, u32 *len)
+static int
+snd_sof_get_nhlt_endpoint_data(struct snd_sof_dev *sdev, struct snd_sof_dai *dai,
+ bool single_bitdepth,
+ struct snd_pcm_hw_params *params, u32 dai_index,
+ u32 linktype, u8 dir, u32 **dst, u32 *len)
{
return 0;
}
#endif
-bool sof_ipc4_copier_is_single_format(struct snd_sof_dev *sdev,
- struct sof_ipc4_pin_format *pin_fmts,
- u32 pin_fmts_size)
+bool sof_ipc4_copier_is_single_bitdepth(struct snd_sof_dev *sdev,
+ struct sof_ipc4_pin_format *pin_fmts,
+ u32 pin_fmts_size)
{
struct sof_ipc4_audio_format *fmt;
u32 valid_bits;
@@ -1473,6 +1598,105 @@ bool sof_ipc4_copier_is_single_format(struct snd_sof_dev *sdev,
}
static int
+sof_ipc4_adjust_params_to_dai_format(struct snd_sof_dev *sdev,
+ struct snd_pcm_hw_params *params,
+ struct sof_ipc4_pin_format *pin_fmts,
+ u32 pin_fmts_size)
+{
+ u32 params_mask = BIT(SNDRV_PCM_HW_PARAM_RATE) |
+ BIT(SNDRV_PCM_HW_PARAM_CHANNELS) |
+ BIT(SNDRV_PCM_HW_PARAM_FORMAT);
+ struct sof_ipc4_audio_format *fmt;
+ u32 rate, channels, valid_bits;
+ int i;
+
+ fmt = &pin_fmts[0].audio_fmt;
+ rate = fmt->sampling_frequency;
+ channels = SOF_IPC4_AUDIO_FORMAT_CFG_CHANNELS_COUNT(fmt->fmt_cfg);
+ valid_bits = SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(fmt->fmt_cfg);
+
+ /* check if parameters in topology defined formats are the same */
+ for (i = 1; i < pin_fmts_size; i++) {
+ u32 val;
+
+ fmt = &pin_fmts[i].audio_fmt;
+
+ if (params_mask & BIT(SNDRV_PCM_HW_PARAM_RATE)) {
+ val = fmt->sampling_frequency;
+ if (val != rate)
+ params_mask &= ~BIT(SNDRV_PCM_HW_PARAM_RATE);
+ }
+ if (params_mask & BIT(SNDRV_PCM_HW_PARAM_CHANNELS)) {
+ val = SOF_IPC4_AUDIO_FORMAT_CFG_CHANNELS_COUNT(fmt->fmt_cfg);
+ if (val != channels)
+ params_mask &= ~BIT(SNDRV_PCM_HW_PARAM_CHANNELS);
+ }
+ if (params_mask & BIT(SNDRV_PCM_HW_PARAM_FORMAT)) {
+ val = SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(fmt->fmt_cfg);
+ if (val != valid_bits)
+ params_mask &= ~BIT(SNDRV_PCM_HW_PARAM_FORMAT);
+ }
+ }
+
+ if (params_mask)
+ return sof_ipc4_update_hw_params(sdev, params,
+ &pin_fmts[0].audio_fmt,
+ params_mask);
+
+ return 0;
+}
+
+static int
+sof_ipc4_prepare_dai_copier(struct snd_sof_dev *sdev, struct snd_sof_dai *dai,
+ struct snd_pcm_hw_params *params, int dir)
+{
+ struct sof_ipc4_available_audio_format *available_fmt;
+ struct snd_pcm_hw_params dai_params = *params;
+ struct sof_ipc4_copier_data *copier_data;
+ struct sof_ipc4_pin_format *pin_fmts;
+ struct sof_ipc4_copier *ipc4_copier;
+ bool single_bitdepth;
+ u32 num_pin_fmts;
+ int ret;
+
+ ipc4_copier = dai->private;
+ copier_data = &ipc4_copier->data;
+ available_fmt = &ipc4_copier->available_fmt;
+
+ /*
+ * Fixup the params based on the format parameters of the DAI. If any
+ * of the RATE, CHANNELS, bit depth is static among the formats then
+ * narrow the params to only allow that specific parameter value.
+ */
+ if (dir == SNDRV_PCM_STREAM_PLAYBACK) {
+ pin_fmts = available_fmt->output_pin_fmts;
+ num_pin_fmts = available_fmt->num_output_formats;
+ } else {
+ pin_fmts = available_fmt->input_pin_fmts;
+ num_pin_fmts = available_fmt->num_input_formats;
+ }
+
+ ret = sof_ipc4_adjust_params_to_dai_format(sdev, &dai_params, pin_fmts,
+ num_pin_fmts);
+ if (ret)
+ return ret;
+
+ single_bitdepth = sof_ipc4_copier_is_single_bitdepth(sdev, pin_fmts,
+ num_pin_fmts);
+ ret = snd_sof_get_nhlt_endpoint_data(sdev, dai, single_bitdepth,
+ &dai_params,
+ ipc4_copier->dai_index,
+ ipc4_copier->dai_type, dir,
+ &ipc4_copier->copier_config,
+ &copier_data->gtw_cfg.config_length);
+ /* Update the params to reflect the changes made in this function */
+ if (!ret)
+ *params = dai_params;
+
+ return ret;
+}
+
+static int
sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget,
struct snd_pcm_hw_params *fe_params,
struct snd_sof_platform_stream_params *platform_params,
@@ -1482,7 +1706,7 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget,
struct snd_soc_component *scomp = swidget->scomp;
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
struct sof_ipc4_copier_data *copier_data;
- struct snd_pcm_hw_params *ref_params;
+ struct snd_pcm_hw_params ref_params;
struct sof_ipc4_copier *ipc4_copier;
struct snd_sof_dai *dai;
u32 gtw_cfg_config_length;
@@ -1494,7 +1718,8 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget,
u32 out_ref_rate, out_ref_channels;
u32 deep_buffer_dma_ms = 0;
int output_fmt_index;
- bool single_output_format;
+ bool single_output_bitdepth;
+ int i;
dev_dbg(sdev->dev, "copier %s, type %d", swidget->widget->name, swidget->id);
@@ -1559,9 +1784,9 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget,
* for capture.
*/
if (dir == SNDRV_PCM_STREAM_PLAYBACK)
- ref_params = fe_params;
+ ref_params = *fe_params;
else
- ref_params = pipeline_params;
+ ref_params = *pipeline_params;
copier_data->gtw_cfg.node_id &= ~SOF_IPC4_NODE_INDEX_MASK;
copier_data->gtw_cfg.node_id |=
@@ -1587,23 +1812,25 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget,
available_fmt = &ipc4_copier->available_fmt;
/*
- * When there is format conversion within a pipeline, the number of supported
- * output formats is typically limited to just 1 for the DAI copiers. But when there
- * is no format conversion, the DAI copiers input format must match that of the
- * FE hw_params for capture and the pipeline params for playback.
+ * Use the fe_params as a base for the copier configuration.
+ * The ref_params might get updated to reflect what format is
+ * supported by the copier on the DAI side.
+ *
+ * In case of capture the ref_params returned will be used to
+ * find the input configuration of the copier.
*/
- if (dir == SNDRV_PCM_STREAM_PLAYBACK)
- ref_params = pipeline_params;
- else
- ref_params = fe_params;
-
- ret = snd_sof_get_nhlt_endpoint_data(sdev, dai, fe_params, ipc4_copier->dai_index,
- ipc4_copier->dai_type, dir,
- &ipc4_copier->copier_config,
- &copier_data->gtw_cfg.config_length);
+ ref_params = *fe_params;
+ ret = sof_ipc4_prepare_dai_copier(sdev, dai, &ref_params, dir);
if (ret < 0)
return ret;
+ /*
+ * For playback the pipeline_params needs to be used to find the
+ * input configuration of the copier.
+ */
+ if (dir == SNDRV_PCM_STREAM_PLAYBACK)
+ ref_params = *pipeline_params;
+
break;
}
case snd_soc_dapm_buffer:
@@ -1611,7 +1838,7 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget,
ipc4_copier = (struct sof_ipc4_copier *)swidget->private;
copier_data = &ipc4_copier->data;
available_fmt = &ipc4_copier->available_fmt;
- ref_params = pipeline_params;
+ ref_params = *pipeline_params;
break;
}
@@ -1622,15 +1849,15 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget,
}
/* set input and output audio formats */
- ret = sof_ipc4_init_input_audio_fmt(sdev, swidget, &copier_data->base_config, ref_params,
- available_fmt);
+ ret = sof_ipc4_init_input_audio_fmt(sdev, swidget, &copier_data->base_config,
+ &ref_params, available_fmt);
if (ret < 0)
return ret;
/* set the reference params for output format selection */
- single_output_format = sof_ipc4_copier_is_single_format(sdev,
- available_fmt->output_pin_fmts,
- available_fmt->num_output_formats);
+ single_output_bitdepth = sof_ipc4_copier_is_single_bitdepth(sdev,
+ available_fmt->output_pin_fmts,
+ available_fmt->num_output_formats);
switch (swidget->id) {
case snd_soc_dapm_aif_in:
case snd_soc_dapm_dai_out:
@@ -1642,7 +1869,7 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget,
out_ref_rate = in_fmt->sampling_frequency;
out_ref_channels = SOF_IPC4_AUDIO_FORMAT_CFG_CHANNELS_COUNT(in_fmt->fmt_cfg);
- if (!single_output_format)
+ if (!single_output_bitdepth)
out_ref_valid_bits =
SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(in_fmt->fmt_cfg);
break;
@@ -1651,7 +1878,7 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget,
case snd_soc_dapm_dai_in:
out_ref_rate = params_rate(fe_params);
out_ref_channels = params_channels(fe_params);
- if (!single_output_format) {
+ if (!single_output_bitdepth) {
out_ref_valid_bits = sof_ipc4_get_valid_bits(sdev, fe_params);
if (out_ref_valid_bits < 0)
return out_ref_valid_bits;
@@ -1669,7 +1896,7 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget,
* if the output format is the same across all available output formats, choose
* that as the reference.
*/
- if (single_output_format) {
+ if (single_output_bitdepth) {
struct sof_ipc4_audio_format *out_fmt;
out_fmt = &available_fmt->output_pin_fmts[0].audio_fmt;
@@ -1712,6 +1939,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;
@@ -1720,7 +1948,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;
@@ -1744,6 +1971,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;
@@ -1751,7 +1980,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,7 +2033,11 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget,
}
/* modify the input params for the next widget */
- ret = sof_ipc4_update_hw_params(sdev, pipeline_params, &copier_data->out_format);
+ ret = sof_ipc4_update_hw_params(sdev, pipeline_params,
+ &copier_data->out_format,
+ BIT(SNDRV_PCM_HW_PARAM_FORMAT) |
+ BIT(SNDRV_PCM_HW_PARAM_CHANNELS) |
+ BIT(SNDRV_PCM_HW_PARAM_RATE));
if (ret)
return ret;
@@ -1830,19 +2078,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 */
@@ -2015,7 +2262,10 @@ static int sof_ipc4_prepare_src_module(struct snd_sof_widget *swidget,
src->data.sink_rate = out_audio_fmt->sampling_frequency;
/* update pipeline_params for sink widgets */
- return sof_ipc4_update_hw_params(sdev, pipeline_params, out_audio_fmt);
+ return sof_ipc4_update_hw_params(sdev, pipeline_params, out_audio_fmt,
+ BIT(SNDRV_PCM_HW_PARAM_FORMAT) |
+ BIT(SNDRV_PCM_HW_PARAM_CHANNELS) |
+ BIT(SNDRV_PCM_HW_PARAM_RATE));
}
static int
@@ -2139,7 +2389,11 @@ static int sof_ipc4_prepare_process_module(struct snd_sof_widget *swidget,
sizeof(struct sof_ipc4_audio_format));
/* modify the pipeline params with the pin 0 output format */
- ret = sof_ipc4_update_hw_params(sdev, pipeline_params, &process->output_format);
+ ret = sof_ipc4_update_hw_params(sdev, pipeline_params,
+ &process->output_format,
+ BIT(SNDRV_PCM_HW_PARAM_FORMAT) |
+ BIT(SNDRV_PCM_HW_PARAM_CHANNELS) |
+ BIT(SNDRV_PCM_HW_PARAM_RATE));
if (ret)
return ret;
}
@@ -2621,7 +2875,7 @@ static void sof_ipc4_put_queue_id(struct snd_sof_widget *swidget, int queue_id,
static int sof_ipc4_set_copier_sink_format(struct snd_sof_dev *sdev,
struct snd_sof_widget *src_widget,
struct snd_sof_widget *sink_widget,
- int sink_id)
+ struct snd_sof_route *sroute)
{
struct sof_ipc4_copier_config_set_sink_format format;
const struct sof_ipc_ops *iops = sdev->ipc->ops;
@@ -2630,9 +2884,6 @@ static int sof_ipc4_set_copier_sink_format(struct snd_sof_dev *sdev,
struct sof_ipc4_fw_module *fw_module;
struct sof_ipc4_msg msg = {{ 0 }};
- dev_dbg(sdev->dev, "%s set copier sink %d format\n",
- src_widget->widget->name, sink_id);
-
if (WIDGET_IS_DAI(src_widget->id)) {
struct snd_sof_dai *dai = src_widget->private;
@@ -2643,13 +2894,15 @@ static int sof_ipc4_set_copier_sink_format(struct snd_sof_dev *sdev,
fw_module = src_widget->module_info;
- format.sink_id = sink_id;
+ format.sink_id = sroute->src_queue_id;
memcpy(&format.source_fmt, &src_config->audio_fmt, sizeof(format.source_fmt));
- pin_fmt = sof_ipc4_get_input_pin_audio_fmt(sink_widget, sink_id);
+ pin_fmt = sof_ipc4_get_input_pin_audio_fmt(sink_widget, sroute->dst_queue_id);
if (!pin_fmt) {
- dev_err(sdev->dev, "Unable to get pin %d format for %s",
- sink_id, sink_widget->widget->name);
+ dev_err(sdev->dev,
+ "Failed to get input audio format of %s:%d for output of %s:%d\n",
+ sink_widget->widget->name, sroute->dst_queue_id,
+ src_widget->widget->name, sroute->src_queue_id);
return -EINVAL;
}
@@ -2707,7 +2960,8 @@ static int sof_ipc4_route_setup(struct snd_sof_dev *sdev, struct snd_sof_route *
sroute->src_queue_id = sof_ipc4_get_queue_id(src_widget, sink_widget,
SOF_PIN_TYPE_OUTPUT);
if (sroute->src_queue_id < 0) {
- dev_err(sdev->dev, "failed to get queue ID for source widget: %s\n",
+ dev_err(sdev->dev,
+ "failed to get src_queue_id ID from source widget %s\n",
src_widget->widget->name);
return sroute->src_queue_id;
}
@@ -2715,7 +2969,8 @@ static int sof_ipc4_route_setup(struct snd_sof_dev *sdev, struct snd_sof_route *
sroute->dst_queue_id = sof_ipc4_get_queue_id(src_widget, sink_widget,
SOF_PIN_TYPE_INPUT);
if (sroute->dst_queue_id < 0) {
- dev_err(sdev->dev, "failed to get queue ID for sink widget: %s\n",
+ dev_err(sdev->dev,
+ "failed to get dst_queue_id ID from sink widget %s\n",
sink_widget->widget->name);
sof_ipc4_put_queue_id(src_widget, sroute->src_queue_id,
SOF_PIN_TYPE_OUTPUT);
@@ -2724,10 +2979,11 @@ static int sof_ipc4_route_setup(struct snd_sof_dev *sdev, struct snd_sof_route *
/* Pin 0 format is already set during copier module init */
if (sroute->src_queue_id > 0 && WIDGET_IS_COPIER(src_widget->id)) {
- ret = sof_ipc4_set_copier_sink_format(sdev, src_widget, sink_widget,
- sroute->src_queue_id);
+ ret = sof_ipc4_set_copier_sink_format(sdev, src_widget,
+ sink_widget, sroute);
if (ret < 0) {
- dev_err(sdev->dev, "failed to set sink format for %s source queue ID %d\n",
+ dev_err(sdev->dev,
+ "failed to set sink format for source %s:%d\n",
src_widget->widget->name, sroute->src_queue_id);
goto out;
}
@@ -2845,8 +3101,14 @@ static int sof_ipc4_dai_config(struct snd_sof_dev *sdev, struct snd_sof_widget *
return 0;
if (pipeline->use_chain_dma) {
- pipeline->msg.primary &= ~SOF_IPC4_GLB_CHAIN_DMA_LINK_ID_MASK;
- pipeline->msg.primary |= SOF_IPC4_GLB_CHAIN_DMA_LINK_ID(data->dai_data);
+ /*
+ * Only configure the DMA Link ID for ChainDMA when this op is
+ * invoked with SOF_DAI_CONFIG_FLAGS_HW_PARAMS
+ */
+ if (flags & SOF_DAI_CONFIG_FLAGS_HW_PARAMS) {
+ pipeline->msg.primary &= ~SOF_IPC4_GLB_CHAIN_DMA_LINK_ID_MASK;
+ pipeline->msg.primary |= SOF_IPC4_GLB_CHAIN_DMA_LINK_ID(data->dai_data);
+ }
return 0;
}
@@ -2854,17 +3116,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: