ASoC: SOF: topology: load multiple topologies
authorBard Liao <yung-chuan.liao@linux.intel.com>
Mon, 14 Apr 2025 06:32:32 +0000 (14:32 +0800)
committerMark Brown <broonie@kernel.org>
Mon, 14 Apr 2025 10:22:19 +0000 (11:22 +0100)
Currently, we always use single topology file to describe the widgets.
However, with SDCA, we want to be able to load sub-topologies based on
the supported device functions.

Signed-off-by: Bard Liao <yung-chuan.liao@linux.intel.com>
Reviewed-by: Liam Girdwood <liam.r.girdwood@intel.com>
Reviewed-by: Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
Reviewed-by: Péter Ujfalusi <peter.ujfalusi@linux.intel.com>
Link: https://patch.msgid.link/20250414063239.85200-5-yung-chuan.liao@linux.intel.com
Signed-off-by: Mark Brown <broonie@kernel.org>
sound/soc/sof/topology.c

index fc4a9cddd162aff43cfdee6c85cabf21825a36e4..e19ba94f2c80a43731b90351bacfde2720db50ed 100644 (file)
@@ -2310,8 +2310,10 @@ static const struct snd_soc_tplg_ops sof_tplg_ops = {
        .link_load      = sof_link_load,
        .link_unload    = sof_link_unload,
 
-       /* completion - called at completion of firmware loading */
-       .complete       = sof_complete,
+       /*
+        * No need to set the complete callback. sof_complete will be called explicitly after
+        * topology loading is complete.
+        */
 
        /* manifest - optional to inform component of manifest */
        .manifest       = sof_manifest,
@@ -2467,34 +2469,82 @@ static const struct snd_soc_tplg_ops sof_dspless_tplg_ops = {
 int snd_sof_load_topology(struct snd_soc_component *scomp, const char *file)
 {
        struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
+       struct snd_sof_pdata *sof_pdata = sdev->pdata;
+       const char *tplg_filename_prefix = sof_pdata->tplg_filename_prefix;
        const struct firmware *fw;
+       const char **tplg_files;
+       int tplg_cnt = 0;
        int ret;
+       int i;
 
-       dev_dbg(scomp->dev, "loading topology:%s\n", file);
+       tplg_files = kcalloc(scomp->card->num_links, sizeof(char *), GFP_KERNEL);
+       if (!tplg_files)
+               return -ENOMEM;
 
-       ret = request_firmware(&fw, file, scomp->dev);
-       if (ret < 0) {
-               dev_err(scomp->dev, "error: tplg request firmware %s failed err: %d\n",
-                       file, ret);
-               dev_err(scomp->dev,
-                       "you may need to download the firmware from https://github.com/thesofproject/sof-bin/\n");
-               return ret;
+       if (sof_pdata->machine->get_function_tplg_files) {
+               tplg_cnt = sof_pdata->machine->get_function_tplg_files(scomp->card,
+                                                                      sof_pdata->machine,
+                                                                      tplg_filename_prefix,
+                                                                      &tplg_files);
+               if (tplg_cnt < 0) {
+                       kfree(tplg_files);
+                       return tplg_cnt;
+               }
        }
 
-       if (sdev->dspless_mode_selected)
-               ret = snd_soc_tplg_component_load(scomp, &sof_dspless_tplg_ops, fw);
-       else
-               ret = snd_soc_tplg_component_load(scomp, &sof_tplg_ops, fw);
+       /*
+        * The monolithic topology will be used if there is no get_function_tplg_files
+        * callback or the callback returns 0.
+        */
+       if (!tplg_cnt) {
+               tplg_files[0] = file;
+               tplg_cnt = 1;
+               dev_dbg(scomp->dev, "loading topology: %s\n", file);
+       } else {
+               dev_info(scomp->dev, "Using function topologies instead %s\n", file);
+       }
 
-       if (ret < 0)
-               dev_err(scomp->dev, "error: tplg component load failed %d\n",
-                       ret);
+       for (i = 0; i < tplg_cnt; i++) {
+               /* Only print the file names if the function topologies are used */
+               if (tplg_files[0] != file)
+                       dev_info(scomp->dev, "loading topology %d: %s\n", i, tplg_files[i]);
 
-       release_firmware(fw);
+               ret = request_firmware(&fw, tplg_files[i], scomp->dev);
+               if (ret < 0) {
+                       /*
+                        * snd_soc_tplg_component_remove(scomp) will be called
+                        * if snd_soc_tplg_component_load(scomp) failed and all
+                        * objects in the scomp will be removed. No need to call
+                        * snd_soc_tplg_component_remove(scomp) here.
+                        */
+                       dev_err(scomp->dev, "tplg request firmware %s failed err: %d\n",
+                               tplg_files[i], ret);
+                       goto out;
+               }
 
+               if (sdev->dspless_mode_selected)
+                       ret = snd_soc_tplg_component_load(scomp, &sof_dspless_tplg_ops, fw);
+               else
+                       ret = snd_soc_tplg_component_load(scomp, &sof_tplg_ops, fw);
+
+               release_firmware(fw);
+
+               if (ret < 0) {
+                       dev_err(scomp->dev, "tplg %s component load failed %d\n",
+                               tplg_files[i], ret);
+                       goto out;
+               }
+       }
+
+       /* call sof_complete when topologies are loaded successfully */
+       ret = sof_complete(scomp);
+
+out:
        if (ret >= 0 && sdev->led_present)
                ret = snd_ctl_led_request();
 
+       kfree(tplg_files);
+
        return ret;
 }
 EXPORT_SYMBOL(snd_sof_load_topology);