ASoC: SOF: ipc4-topology: Modify the output format selection logic
authorRanjani Sridharan <ranjani.sridharan@linux.intel.com>
Mon, 15 May 2023 10:33:34 +0000 (13:33 +0300)
committerMark Brown <broonie@kernel.org>
Mon, 15 May 2023 11:12:55 +0000 (20:12 +0900)
Modify the output format selection when there are multiple output
formats available to choose the one that matches the reference params.
The reference params depend on the type of module. In the case of
processing modules, the reference params are based on the selected input
audio format. This would be the case when a processing module does not
perform any format conversion during processing.

The only special case is the copier module. The copier module is capable
of format conversion but it is only used in the case when the output
is fixed to a single format. In the case of a module copier, when there are
multiple formats, the reference params is based on the selected input
params and the output format must match that of the selected input
format. In the case of host copier, the reference params should be
based on the input audio format for playback and the FE hw_params for
capture. In the case DAI copier, the reference params should be based on
the input audio format for capture and the FE hw_params for playback
when there is no format conversion in the pipeline from the host to the
DAI.

Signed-off-by: Ranjani Sridharan <ranjani.sridharan@linux.intel.com
Reviewed-by: Bard Liao <yung-chuan.liao@linux.intel.com
Reviewed-by: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com
Signed-off-by: Peter Ujfalusi <peter.ujfalusi@linux.intel.com
Link: https://lore.kernel.org/r/20230515103336.16132-8-peter.ujfalusi@linux.intel.com
Signed-off-by: Mark Brown <broonie@kernel.org
sound/soc/sof/ipc4-topology.c

index 2903e78..7f54006 100644 (file)
@@ -1031,7 +1031,8 @@ static int sof_ipc4_update_hw_params(struct snd_sof_dev *sdev, struct snd_pcm_hw
 static int sof_ipc4_init_output_audio_fmt(struct snd_sof_dev *sdev,
                                          struct sof_ipc4_base_module_cfg *base_config,
                                          struct sof_ipc4_available_audio_format *available_fmt,
-                                         int input_audio_format_index)
+                                         u32 out_ref_rate, u32 out_ref_channels,
+                                         u32 out_ref_valid_bits)
 {
        struct sof_ipc4_audio_format *out_fmt;
        u32 out_rate, out_channels, out_valid_bits;
@@ -1063,16 +1064,31 @@ static int sof_ipc4_init_output_audio_fmt(struct snd_sof_dev *sdev,
        }
 
        /* pick the first format if there's only one available or if all formats are the same */
-       if (single_format)
-               i = 0;
-       else
-               i = input_audio_format_index;
+       if (single_format) {
+               base_config->obs = available_fmt->output_pin_fmts[0].buffer_size;
+               return 0;
+       }
+
+       /*
+        * if there are multiple output formats, then choose the output format that matches
+        * the reference params
+        */
+       for (i = 0; i < available_fmt->num_output_formats; i++) {
+               u32 _out_rate, _out_channels, _out_valid_bits;
 
-       if (available_fmt->num_output_formats && i < available_fmt->num_output_formats)
-               base_config->obs = available_fmt->output_pin_fmts[i].buffer_size;
+               out_fmt = &available_fmt->output_pin_fmts[i].audio_fmt;
+               _out_rate = out_fmt->sampling_frequency;
+               _out_channels = SOF_IPC4_AUDIO_FORMAT_CFG_CHANNELS_COUNT(out_fmt->fmt_cfg);
+               _out_valid_bits = SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(out_fmt->fmt_cfg);
 
-       /* Return the index of the chosen output format */
-       return i;
+               if (_out_rate == out_ref_rate && _out_channels == out_ref_channels &&
+                   _out_valid_bits == out_ref_valid_bits) {
+                       base_config->obs = available_fmt->output_pin_fmts[i].buffer_size;
+                       return i;
+               }
+       }
+
+       return -EINVAL;
 }
 
 static int sof_ipc4_get_valid_bits(struct snd_sof_dev *sdev, struct snd_pcm_hw_params *params)
@@ -1404,6 +1420,7 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget,
        int *ipc_config_size;
        u32 **data;
        int ipc_size, ret;
+       u32 out_ref_rate, out_ref_channels, out_ref_valid_bits;
        u32 deep_buffer_dma_ms = 0;
        u32 format_list_count;
        int output_fmt_index;
@@ -1551,10 +1568,42 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget,
        if (ret < 0)
                return ret;
 
+       /* set the reference params for output format selection */
+       switch (swidget->id) {
+       case snd_soc_dapm_aif_in:
+       case snd_soc_dapm_dai_out:
+       case snd_soc_dapm_buffer:
+       {
+               struct sof_ipc4_audio_format *in_fmt;
+
+               in_fmt = &available_fmt->input_pin_fmts[ret].audio_fmt;
+               out_ref_rate = in_fmt->sampling_frequency;
+               out_ref_channels = SOF_IPC4_AUDIO_FORMAT_CFG_CHANNELS_COUNT(in_fmt->fmt_cfg);
+               out_ref_valid_bits = SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(in_fmt->fmt_cfg);
+               break;
+       }
+       case snd_soc_dapm_aif_out:
+       case snd_soc_dapm_dai_in:
+               out_ref_valid_bits = sof_ipc4_get_valid_bits(sdev, fe_params);
+               if (out_ref_valid_bits < 0)
+                       return out_ref_valid_bits;
+
+               out_ref_rate = params_rate(fe_params);
+               out_ref_channels = params_channels(fe_params);
+               break;
+       default:
+               /*
+                * Unsupported type should be caught by the former switch default
+                * case, this should never happen in reality.
+                */
+               return -EINVAL;
+       }
+
        output_fmt_index = sof_ipc4_init_output_audio_fmt(sdev, &copier_data->base_config,
-                                                         available_fmt, ret);
+                                                         available_fmt, out_ref_rate,
+                                                         out_ref_channels, out_ref_valid_bits);
        if (output_fmt_index < 0) {
-               dev_err(sdev->dev, "No output formats in topology for copier %s",
+               dev_err(sdev->dev, "Failed to initialize output format for %s",
                        swidget->widget->name);
                return output_fmt_index;
        }
@@ -1727,6 +1776,8 @@ static int sof_ipc4_prepare_gain_module(struct snd_sof_widget *swidget,
        struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
        struct sof_ipc4_gain *gain = swidget->private;
        struct sof_ipc4_available_audio_format *available_fmt = &gain->available_fmt;
+       struct sof_ipc4_audio_format *in_fmt;
+       u32 out_ref_rate, out_ref_channels, out_ref_valid_bits;
        int ret;
 
        ret = sof_ipc4_init_input_audio_fmt(sdev, swidget, &gain->base_config,
@@ -1736,9 +1787,16 @@ static int sof_ipc4_prepare_gain_module(struct snd_sof_widget *swidget,
        if (ret < 0)
                return ret;
 
-       ret = sof_ipc4_init_output_audio_fmt(sdev, &gain->base_config, available_fmt, ret);
+       in_fmt = &available_fmt->input_pin_fmts[ret].audio_fmt;
+       out_ref_rate = in_fmt->sampling_frequency;
+       out_ref_channels = SOF_IPC4_AUDIO_FORMAT_CFG_CHANNELS_COUNT(in_fmt->fmt_cfg);
+       out_ref_valid_bits = SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(in_fmt->fmt_cfg);
+
+       ret = sof_ipc4_init_output_audio_fmt(sdev, &gain->base_config, available_fmt,
+                                            out_ref_rate, out_ref_channels, out_ref_valid_bits);
        if (ret < 0) {
-               dev_err(sdev->dev, "No output formats for %s", swidget->widget->name);
+               dev_err(sdev->dev, "Failed to initialize output format for %s",
+                       swidget->widget->name);
                return ret;
        }
 
@@ -1757,6 +1815,8 @@ static int sof_ipc4_prepare_mixer_module(struct snd_sof_widget *swidget,
        struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
        struct sof_ipc4_mixer *mixer = swidget->private;
        struct sof_ipc4_available_audio_format *available_fmt = &mixer->available_fmt;
+       struct sof_ipc4_audio_format *in_fmt;
+       u32 out_ref_rate, out_ref_channels, out_ref_valid_bits;
        int ret;
 
        ret = sof_ipc4_init_input_audio_fmt(sdev, swidget, &mixer->base_config,
@@ -1766,9 +1826,16 @@ static int sof_ipc4_prepare_mixer_module(struct snd_sof_widget *swidget,
        if (ret < 0)
                return ret;
 
-       ret = sof_ipc4_init_output_audio_fmt(sdev, &mixer->base_config, available_fmt, ret);
+       in_fmt = &available_fmt->input_pin_fmts[ret].audio_fmt;
+       out_ref_rate = in_fmt->sampling_frequency;
+       out_ref_channels = SOF_IPC4_AUDIO_FORMAT_CFG_CHANNELS_COUNT(in_fmt->fmt_cfg);
+       out_ref_valid_bits = SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(in_fmt->fmt_cfg);
+
+       ret = sof_ipc4_init_output_audio_fmt(sdev, &mixer->base_config, available_fmt,
+                                            out_ref_rate, out_ref_channels, out_ref_valid_bits);
        if (ret < 0) {
-               dev_err(sdev->dev, "No output formats for %s", swidget->widget->name);
+               dev_err(sdev->dev, "Failed to initialize output format for %s",
+                       swidget->widget->name);
                return ret;
        }
 
@@ -1787,6 +1854,8 @@ static int sof_ipc4_prepare_src_module(struct snd_sof_widget *swidget,
        struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
        struct sof_ipc4_src *src = swidget->private;
        struct sof_ipc4_available_audio_format *available_fmt = &src->available_fmt;
+       struct sof_ipc4_audio_format *in_fmt;
+       u32 out_ref_rate, out_ref_channels, out_ref_valid_bits;
        struct snd_interval *rate;
        int ret;
 
@@ -1797,10 +1866,16 @@ static int sof_ipc4_prepare_src_module(struct snd_sof_widget *swidget,
        if (ret < 0)
                return ret;
 
-       ret = sof_ipc4_init_output_audio_fmt(sdev, &src->base_config, available_fmt, ret);
+       in_fmt = &available_fmt->input_pin_fmts[ret].audio_fmt;
+       out_ref_rate = in_fmt->sampling_frequency;
+       out_ref_channels = SOF_IPC4_AUDIO_FORMAT_CFG_CHANNELS_COUNT(in_fmt->fmt_cfg);
+       out_ref_valid_bits = SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(in_fmt->fmt_cfg);
+
+       ret = sof_ipc4_init_output_audio_fmt(sdev, &src->base_config, available_fmt,
+                                            out_ref_rate, out_ref_channels, out_ref_valid_bits);
        if (ret < 0) {
-               dev_err(sdev->dev, "No output formats for %s", swidget->widget->name);
-               return ret;
+               dev_err(sdev->dev, "Failed to initialize output format for %s",
+                       swidget->widget->name);
        }
 
        /* update pipeline memory usage */
@@ -1901,6 +1976,8 @@ static int sof_ipc4_prepare_process_module(struct snd_sof_widget *swidget,
        struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
        struct sof_ipc4_process *process = swidget->private;
        struct sof_ipc4_available_audio_format *available_fmt = &process->available_fmt;
+       struct sof_ipc4_audio_format *in_fmt;
+       u32 out_ref_rate, out_ref_channels, out_ref_valid_bits;
        void *cfg = process->ipc_config_data;
        int output_fmt_index;
        int ret;
@@ -1912,9 +1989,19 @@ static int sof_ipc4_prepare_process_module(struct snd_sof_widget *swidget,
        if (ret < 0)
                return ret;
 
-       /* No need to check the return value. Some processing modules do not have output pins */
+       in_fmt = &available_fmt->input_pin_fmts[ret].audio_fmt;
+       out_ref_rate = in_fmt->sampling_frequency;
+       out_ref_channels = SOF_IPC4_AUDIO_FORMAT_CFG_CHANNELS_COUNT(in_fmt->fmt_cfg);
+       out_ref_valid_bits = SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(in_fmt->fmt_cfg);
+
        output_fmt_index = sof_ipc4_init_output_audio_fmt(sdev, &process->base_config,
-                                                         available_fmt, ret);
+                                                         available_fmt, out_ref_rate,
+                                                         out_ref_channels, out_ref_valid_bits);
+       if (output_fmt_index < 0 && available_fmt->num_output_formats) {
+               dev_err(sdev->dev, "Failed to initialize output format for %s",
+                       swidget->widget->name);
+               return output_fmt_index;
+       }
 
        /* copy Pin 0 output format */
        if (available_fmt->num_output_formats &&