]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
ASoC: SOF: ipc4-pcm: Look for best matching hw_config for SSP
authorPeter Ujfalusi <peter.ujfalusi@linux.intel.com>
Thu, 19 Jun 2025 10:26:57 +0000 (13:26 +0300)
committerMark Brown <broonie@kernel.org>
Thu, 19 Jun 2025 23:21:13 +0000 (00:21 +0100)
Instead of just looking for a hw_config with matching rate only it sounds
better to try to find the best matching configuration.

If we have multiple hw_configurations with the same rate, but each with
different format for example then we have been picking the first config
with the matching rate, which can be a problem and it wil depend on how
the configs are ordered.

Instead we should be trying to find the best match out of the configs
1. rate + format + channels are matching
2. rate + format are matching
3. rate matching

Signed-off-by: Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
Reviewed-by: Bard Liao <yung-chuan.liao@linux.intel.com>
Reviewed-by: Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
Link: https://patch.msgid.link/20250619102657.12109-1-peter.ujfalusi@linux.intel.com
Signed-off-by: Mark Brown <broonie@kernel.org>
sound/soc/sof/ipc4-pcm.c

index 8eee3e1aadf9325a0a158e2c98e7220b33d705ff..a5b365ab19454d65a3ebba4c2b293f4a3eae531f 100644 (file)
@@ -557,12 +557,15 @@ static int sof_ipc4_pcm_hw_free(struct snd_soc_component *component,
        return sof_ipc4_trigger_pipelines(component, substream, SOF_IPC4_PIPE_RESET, 0);
 }
 
-static void ipc4_ssp_dai_config_pcm_params_match(struct snd_sof_dev *sdev, const char *link_name,
-                                                struct snd_pcm_hw_params *params)
+static int ipc4_ssp_dai_config_pcm_params_match(struct snd_sof_dev *sdev,
+                                               const char *link_name,
+                                               struct snd_pcm_hw_params *params)
 {
        struct snd_sof_dai_link *slink;
        struct snd_sof_dai *dai;
        bool dai_link_found = false;
+       int current_config = -1;
+       bool partial_match;
        int i;
 
        list_for_each_entry(slink, &sdev->dai_link_list, list) {
@@ -573,19 +576,50 @@ static void ipc4_ssp_dai_config_pcm_params_match(struct snd_sof_dev *sdev, const
        }
 
        if (!dai_link_found)
-               return;
+               return 0;
 
+       /*
+        * Find the first best matching hardware config:
+        * rate + format + channels are matching
+        * rate + channel are matching
+        *
+        * The copier cannot do rate and/or channel conversion.
+        */
        for (i = 0; i < slink->num_hw_configs; i++) {
                struct snd_soc_tplg_hw_config *hw_config = &slink->hw_configs[i];
 
-               if (params_rate(params) == le32_to_cpu(hw_config->fsync_rate)) {
-                       /* set current config for all DAI's with matching name */
-                       list_for_each_entry(dai, &sdev->dai_list, list)
-                               if (!strcmp(slink->link->name, dai->name))
-                                       dai->current_config = le32_to_cpu(hw_config->id);
+               if (params_rate(params) == le32_to_cpu(hw_config->fsync_rate) &&
+                   params_width(params) == le32_to_cpu(hw_config->tdm_slot_width) &&
+                   params_channels(params) == le32_to_cpu(hw_config->tdm_slots)) {
+                       current_config = le32_to_cpu(hw_config->id);
+                       partial_match = false;
+                       /* best match found */
                        break;
+               } else if (current_config < 0 &&
+                          params_rate(params) == le32_to_cpu(hw_config->fsync_rate) &&
+                          params_channels(params) == le32_to_cpu(hw_config->tdm_slots)) {
+                       current_config = le32_to_cpu(hw_config->id);
+                       partial_match = true;
+                       /* keep looking for better match */
                }
        }
+
+       if (current_config < 0) {
+               dev_err(sdev->dev,
+                       "%s: No suitable hw_config found for %s (num_hw_configs: %d)\n",
+                       __func__, slink->link->name, slink->num_hw_configs);
+               return -EINVAL;
+       }
+
+       dev_dbg(sdev->dev,
+               "hw_config for %s: %d (num_hw_configs: %d) with %s match\n",
+               slink->link->name, current_config, slink->num_hw_configs,
+               partial_match ? "partial" : "full");
+       list_for_each_entry(dai, &sdev->dai_list, list)
+               if (!strcmp(slink->link->name, dai->name))
+                       dai->current_config = current_config;
+
+       return 0;
 }
 
 /*
@@ -728,13 +762,10 @@ static int sof_ipc4_pcm_dai_link_fixup(struct snd_soc_pcm_runtime *rtd,
                break;
        }
 
-       switch (ipc4_copier->dai_type) {
-       case SOF_DAI_INTEL_SSP:
-               ipc4_ssp_dai_config_pcm_params_match(sdev, (char *)rtd->dai_link->name, params);
-               break;
-       default:
-               break;
-       }
+       if (ipc4_copier->dai_type == SOF_DAI_INTEL_SSP)
+               return ipc4_ssp_dai_config_pcm_params_match(sdev,
+                                                           (char *)rtd->dai_link->name,
+                                                           params);
 
        return 0;
 }