@@ -1420,13 +1420,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_format,
+ 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;
@@ -1435,9 +1438,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_format) {
+ 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;
@@ -1471,22 +1483,56 @@ static int snd_sof_get_nhlt_endpoint_data(struct snd_sof_dev *sdev, struct snd_s
dir, dev_type);
if (!cfg) {
+ 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;
+
+ 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 we have loaded 32-bit blob
+ * instead of the 16-bit.
+ * 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);
+ 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_format,
+ struct snd_pcm_hw_params *params, u32 dai_index,
+ u32 linktype, u8 dir, u32 **dst, u32 *len)
{
return 0;
}
@@ -1517,6 +1563,68 @@ bool sof_ipc4_copier_is_single_format(struct snd_sof_dev *sdev,
return true;
}
+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_copier *ipc4_copier;
+ bool single_format;
+ int ret;
+
+ ipc4_copier = dai->private;
+ copier_data = &ipc4_copier->data;
+ available_fmt = &ipc4_copier->available_fmt;
+
+ /*
+ * If the copier on the DAI side supports only single bit depth then
+ * this depth (format) should be used to look for the NHLT blob (if
+ * needed) and in case of capture this should be used for the input
+ * format lookup
+ */
+ if (dir == SNDRV_PCM_STREAM_PLAYBACK) {
+ single_format = sof_ipc4_copier_is_single_format(sdev,
+ available_fmt->output_pin_fmts,
+ available_fmt->num_output_formats);
+
+ /* Update the dai_params with the only supported format */
+ if (single_format) {
+ ret = sof_ipc4_update_hw_params(sdev, &dai_params,
+ &available_fmt->output_pin_fmts[0].audio_fmt,
+ BIT(SNDRV_PCM_HW_PARAM_FORMAT));
+ if (ret)
+ return ret;
+ }
+ } else {
+ single_format = sof_ipc4_copier_is_single_format(sdev,
+ available_fmt->input_pin_fmts,
+ available_fmt->num_input_formats);
+
+ /* Update the dai_params with the only supported format */
+ if (single_format) {
+ ret = sof_ipc4_update_hw_params(sdev, &dai_params,
+ &available_fmt->input_pin_fmts[0].audio_fmt,
+ BIT(SNDRV_PCM_HW_PARAM_FORMAT));
+ if (ret)
+ return ret;
+ }
+ }
+
+ ret = snd_sof_get_nhlt_endpoint_data(sdev, dai, single_format,
+ &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,
@@ -1527,7 +1635,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;
@@ -1605,9 +1713,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 |=
@@ -1633,23 +1741,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:
@@ -1657,7 +1767,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;
}
@@ -1668,8 +1778,8 @@ 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;