diff options
-rw-r--r-- | include/sound/soc-acpi.h | 13 | ||||
-rw-r--r-- | sound/soc/intel/common/Makefile | 2 | ||||
-rw-r--r-- | sound/soc/intel/common/soc-acpi-intel-arl-match.c | 9 | ||||
-rw-r--r-- | sound/soc/intel/common/soc-acpi-intel-lnl-match.c | 9 | ||||
-rw-r--r-- | sound/soc/intel/common/soc-acpi-intel-mtl-match.c | 11 | ||||
-rw-r--r-- | sound/soc/intel/common/soc-acpi-intel-ptl-match.c | 17 | ||||
-rw-r--r-- | sound/soc/intel/common/sof-function-topology-lib.c | 135 | ||||
-rw-r--r-- | sound/soc/intel/common/sof-function-topology-lib.h | 15 | ||||
-rw-r--r-- | sound/soc/sdw_utils/soc_sdw_utils.c | 168 | ||||
-rw-r--r-- | sound/soc/sof/topology.c | 92 |
10 files changed, 428 insertions, 43 deletions
diff --git a/include/sound/soc-acpi.h b/include/sound/soc-acpi.h index 72e371a21767..b8af309c2683 100644 --- a/include/sound/soc-acpi.h +++ b/include/sound/soc-acpi.h @@ -10,6 +10,7 @@ #include <linux/acpi.h> #include <linux/mod_devicetable.h> #include <linux/soundwire/sdw.h> +#include <sound/soc.h> struct snd_soc_acpi_package_context { char *name; /* package name */ @@ -193,6 +194,15 @@ struct snd_soc_acpi_link_adr { * is not constant since this field may be updated at run-time * @sof_tplg_filename: Sound Open Firmware topology file name, if enabled * @tplg_quirk_mask: quirks to select different topology files dynamically + * @get_function_tplg_files: This is an optional callback, if specified then instead of + * the single sof_tplg_filename the callback will return the list of function topology + * files to be loaded. + * Return value: The number of the files or negative ERRNO. 0 means that the single topology + * file should be used, no function topology split can be used on the machine. + * @card: the pointer of the card + * @mach: the pointer of the machine driver + * @prefix: the prefix of the topology file name. Typically, it is the path. + * @tplg_files: the pointer of the array of the topology file names. */ /* Descriptor for SST ASoC machine driver */ struct snd_soc_acpi_mach { @@ -212,6 +222,9 @@ struct snd_soc_acpi_mach { struct snd_soc_acpi_mach_params mach_params; const char *sof_tplg_filename; const u32 tplg_quirk_mask; + int (*get_function_tplg_files)(struct snd_soc_card *card, + const struct snd_soc_acpi_mach *mach, + const char *prefix, const char ***tplg_files); }; #define SND_SOC_ACPI_MAX_CODECS 3 diff --git a/sound/soc/intel/common/Makefile b/sound/soc/intel/common/Makefile index 0afd114be9e5..7822bcae6c69 100644 --- a/sound/soc/intel/common/Makefile +++ b/sound/soc/intel/common/Makefile @@ -12,7 +12,7 @@ snd-soc-acpi-intel-match-y := soc-acpi-intel-byt-match.o soc-acpi-intel-cht-matc soc-acpi-intel-lnl-match.o \ soc-acpi-intel-ptl-match.o \ soc-acpi-intel-hda-match.o \ - soc-acpi-intel-sdw-mockup-match.o + soc-acpi-intel-sdw-mockup-match.o sof-function-topology-lib.o snd-soc-acpi-intel-match-y += soc-acpi-intel-ssp-common.o diff --git a/sound/soc/intel/common/soc-acpi-intel-arl-match.c b/sound/soc/intel/common/soc-acpi-intel-arl-match.c index 32147dc9d2d6..73e581e93755 100644 --- a/sound/soc/intel/common/soc-acpi-intel-arl-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-arl-match.c @@ -8,6 +8,7 @@ #include <sound/soc-acpi.h> #include <sound/soc-acpi-intel-match.h> #include <sound/soc-acpi-intel-ssp-common.h> +#include "sof-function-topology-lib.h" static const struct snd_soc_acpi_endpoint single_endpoint = { .num = 0, @@ -436,42 +437,49 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_arl_sdw_machines[] = { .links = arl_cs42l43_l0_cs35l56_l23, .drv_name = "sof_sdw", .sof_tplg_filename = "sof-arl-cs42l43-l0-cs35l56-l23.tplg", + .get_function_tplg_files = sof_sdw_get_tplg_files, }, { .link_mask = BIT(0) | BIT(2) | BIT(3), .links = arl_cs42l43_l0_cs35l56_2_l23, .drv_name = "sof_sdw", .sof_tplg_filename = "sof-arl-cs42l43-l0-cs35l56-l23.tplg", + .get_function_tplg_files = sof_sdw_get_tplg_files, }, { .link_mask = BIT(0) | BIT(2) | BIT(3), .links = arl_cs42l43_l0_cs35l56_3_l23, .drv_name = "sof_sdw", .sof_tplg_filename = "sof-arl-cs42l43-l0-cs35l56-l23.tplg", + .get_function_tplg_files = sof_sdw_get_tplg_files, }, { .link_mask = BIT(0) | BIT(2), .links = arl_cs42l43_l0_cs35l56_l2, .drv_name = "sof_sdw", .sof_tplg_filename = "sof-arl-cs42l43-l0-cs35l56-l2.tplg", + .get_function_tplg_files = sof_sdw_get_tplg_files, }, { .link_mask = BIT(0), .links = arl_cs42l43_l0, .drv_name = "sof_sdw", .sof_tplg_filename = "sof-arl-cs42l43-l0.tplg", + .get_function_tplg_files = sof_sdw_get_tplg_files, }, { .link_mask = BIT(2), .links = arl_cs42l43_l2, .drv_name = "sof_sdw", .sof_tplg_filename = "sof-arl-cs42l43-l2.tplg", + .get_function_tplg_files = sof_sdw_get_tplg_files, }, { .link_mask = BIT(2) | BIT(3), .links = arl_cs42l43_l2_cs35l56_l3, .drv_name = "sof_sdw", .sof_tplg_filename = "sof-arl-cs42l43-l2-cs35l56-l3.tplg", + .get_function_tplg_files = sof_sdw_get_tplg_files, }, { .link_mask = 0x1, /* link0 required */ @@ -490,6 +498,7 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_arl_sdw_machines[] = { .links = arl_rt722_l0_rt1320_l2, .drv_name = "sof_sdw", .sof_tplg_filename = "sof-arl-rt722-l0_rt1320-l2.tplg", + .get_function_tplg_files = sof_sdw_get_tplg_files, }, {}, }; diff --git a/sound/soc/intel/common/soc-acpi-intel-lnl-match.c b/sound/soc/intel/common/soc-acpi-intel-lnl-match.c index e04f6de746eb..a2bee667facb 100644 --- a/sound/soc/intel/common/soc-acpi-intel-lnl-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-lnl-match.c @@ -8,6 +8,7 @@ #include <sound/soc-acpi.h> #include <sound/soc-acpi-intel-match.h> +#include "sof-function-topology-lib.h" #include "soc-acpi-intel-sdca-quirks.h" #include "soc-acpi-intel-sdw-mockup-match.h" @@ -712,6 +713,7 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_lnl_sdw_machines[] = { .links = lnl_cs42l43_l0, .drv_name = "sof_sdw", .sof_tplg_filename = "sof-lnl-cs42l43-l0.tplg", + .get_function_tplg_files = sof_sdw_get_tplg_files, }, { .link_mask = BIT(0), @@ -730,6 +732,7 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_lnl_sdw_machines[] = { .links = lnl_rt722_only, .drv_name = "sof_sdw", .sof_tplg_filename = "sof-lnl-rt722-l0.tplg", + .get_function_tplg_files = sof_sdw_get_tplg_files, }, { .link_mask = GENMASK(2, 0), @@ -748,14 +751,16 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_lnl_sdw_machines[] = { .links = lnl_sdw_rt712_vb_l2_rt1320_l1, .drv_name = "sof_sdw", .machine_check = snd_soc_acpi_intel_sdca_is_device_rt712_vb, - .sof_tplg_filename = "sof-lnl-rt712-l2-rt1320-l1.tplg" + .sof_tplg_filename = "sof-lnl-rt712-l2-rt1320-l1.tplg", + .get_function_tplg_files = sof_sdw_get_tplg_files, }, { .link_mask = BIT(1) | BIT(2) | BIT(3), .links = lnl_sdw_rt713_vb_l2_rt1320_l13, .drv_name = "sof_sdw", .machine_check = snd_soc_acpi_intel_sdca_is_device_rt712_vb, - .sof_tplg_filename = "sof-lnl-rt713-l2-rt1320-l13.tplg" + .sof_tplg_filename = "sof-lnl-rt713-l2-rt1320-l13.tplg", + .get_function_tplg_files = sof_sdw_get_tplg_files, }, {}, }; diff --git a/sound/soc/intel/common/soc-acpi-intel-mtl-match.c b/sound/soc/intel/common/soc-acpi-intel-mtl-match.c index 9e611e3667ad..af131d26bd33 100644 --- a/sound/soc/intel/common/soc-acpi-intel-mtl-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-mtl-match.c @@ -11,6 +11,7 @@ #include <sound/soc-acpi.h> #include <sound/soc-acpi-intel-match.h> #include <sound/soc-acpi-intel-ssp-common.h> +#include "sof-function-topology-lib.h" #include "soc-acpi-intel-sdca-quirks.h" #include "soc-acpi-intel-sdw-mockup-match.h" @@ -1083,12 +1084,14 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_mtl_sdw_machines[] = { .drv_name = "sof_sdw", .machine_check = snd_soc_acpi_intel_sdca_is_device_rt712_vb, .sof_tplg_filename = "sof-mtl-rt712-vb-l0.tplg", + .get_function_tplg_files = sof_sdw_get_tplg_files, }, { .link_mask = BIT(0), .links = mtl_712_l0, .drv_name = "sof_sdw", .sof_tplg_filename = "sof-mtl-rt712-l0.tplg", + .get_function_tplg_files = sof_sdw_get_tplg_files, }, { .link_mask = GENMASK(2, 0), @@ -1101,30 +1104,35 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_mtl_sdw_machines[] = { .links = cs42l43_link0_cs35l56_link2_link3, .drv_name = "sof_sdw", .sof_tplg_filename = "sof-mtl-cs42l43-l0-cs35l56-l23.tplg", + .get_function_tplg_files = sof_sdw_get_tplg_files, }, { .link_mask = BIT(0) | BIT(1) | BIT(3), .links = cs42l43_link3_cs35l56_x4_link0_link1_spkagg, .drv_name = "sof_sdw", .sof_tplg_filename = "sof-mtl-cs42l43-l3-cs35l56-l01-spkagg.tplg", + .get_function_tplg_files = sof_sdw_get_tplg_files, }, { .link_mask = GENMASK(2, 0), .links = mtl_cs42l43_cs35l56, .drv_name = "sof_sdw", .sof_tplg_filename = "sof-mtl-cs42l43-l0-cs35l56-l12.tplg", + .get_function_tplg_files = sof_sdw_get_tplg_files, }, { .link_mask = BIT(0) | BIT(1), .links = mtl_cs35l56_x8_link0_link1_fb, .drv_name = "sof_sdw", - .sof_tplg_filename = "sof-mtl-cs35l56-l01-fb8.tplg" + .sof_tplg_filename = "sof-mtl-cs35l56-l01-fb8.tplg", + .get_function_tplg_files = sof_sdw_get_tplg_files, }, { .link_mask = BIT(0), .links = mtl_cs42l43_l0, .drv_name = "sof_sdw", .sof_tplg_filename = "sof-mtl-cs42l43-l0.tplg", + .get_function_tplg_files = sof_sdw_get_tplg_files, }, { .link_mask = GENMASK(3, 0), @@ -1143,6 +1151,7 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_mtl_sdw_machines[] = { .links = mtl_rt722_only, .drv_name = "sof_sdw", .sof_tplg_filename = "sof-mtl-rt722-l0.tplg", + .get_function_tplg_files = sof_sdw_get_tplg_files, }, { .link_mask = BIT(0), diff --git a/sound/soc/intel/common/soc-acpi-intel-ptl-match.c b/sound/soc/intel/common/soc-acpi-intel-ptl-match.c index 6603d8de501c..992729582562 100644 --- a/sound/soc/intel/common/soc-acpi-intel-ptl-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-ptl-match.c @@ -8,6 +8,7 @@ #include <sound/soc-acpi.h> #include <sound/soc-acpi-intel-match.h> +#include "sof-function-topology-lib.h" #include "soc-acpi-intel-sdca-quirks.h" #include "soc-acpi-intel-sdw-mockup-match.h" #include <sound/soc-acpi-intel-ssp-common.h> @@ -586,52 +587,60 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_ptl_sdw_machines[] = { .links = ptl_rt721_l3, .drv_name = "sof_sdw", .sof_tplg_filename = "sof-ptl-rt721.tplg", + .get_function_tplg_files = sof_sdw_get_tplg_files, }, { .link_mask = BIT(0), .links = ptl_rt722_only, .drv_name = "sof_sdw", .sof_tplg_filename = "sof-ptl-rt722.tplg", + .get_function_tplg_files = sof_sdw_get_tplg_files, }, { .link_mask = BIT(1), .links = ptl_rt722_l1, .drv_name = "sof_sdw", .sof_tplg_filename = "sof-ptl-rt722.tplg", + .get_function_tplg_files = sof_sdw_get_tplg_files, }, { .link_mask = BIT(3), .links = ptl_rt722_l3, .drv_name = "sof_sdw", .sof_tplg_filename = "sof-ptl-rt722.tplg", + .get_function_tplg_files = sof_sdw_get_tplg_files, }, { .link_mask = BIT(1) | BIT(2), .links = ptl_sdw_rt712_vb_l2_rt1320_l1, .drv_name = "sof_sdw", .machine_check = snd_soc_acpi_intel_sdca_is_device_rt712_vb, - .sof_tplg_filename = "sof-ptl-rt712-l2-rt1320-l1.tplg" + .sof_tplg_filename = "sof-ptl-rt712-l2-rt1320-l1.tplg", + .get_function_tplg_files = sof_sdw_get_tplg_files, }, { .link_mask = BIT(2) | BIT(3), .links = ptl_sdw_rt712_vb_l3_rt1320_l2, .drv_name = "sof_sdw", .machine_check = snd_soc_acpi_intel_sdca_is_device_rt712_vb, - .sof_tplg_filename = "sof-ptl-rt712-l3-rt1320-l2.tplg" + .sof_tplg_filename = "sof-ptl-rt712-l3-rt1320-l2.tplg", + .get_function_tplg_files = sof_sdw_get_tplg_files, }, { .link_mask = BIT(1) | BIT(2) | BIT(3), .links = ptl_sdw_rt713_vb_l2_rt1320_l13, .drv_name = "sof_sdw", .machine_check = snd_soc_acpi_intel_sdca_is_device_rt712_vb, - .sof_tplg_filename = "sof-ptl-rt713-l2-rt1320-l13.tplg" + .sof_tplg_filename = "sof-ptl-rt713-l2-rt1320-l13.tplg", + .get_function_tplg_files = sof_sdw_get_tplg_files, }, { .link_mask = BIT(1) | BIT(2) | BIT(3), .links = ptl_sdw_rt713_vb_l3_rt1320_l12, .drv_name = "sof_sdw", .machine_check = snd_soc_acpi_intel_sdca_is_device_rt712_vb, - .sof_tplg_filename = "sof-ptl-rt713-l3-rt1320-l12.tplg" + .sof_tplg_filename = "sof-ptl-rt713-l3-rt1320-l12.tplg", + .get_function_tplg_files = sof_sdw_get_tplg_files, }, {}, }; diff --git a/sound/soc/intel/common/sof-function-topology-lib.c b/sound/soc/intel/common/sof-function-topology-lib.c new file mode 100644 index 000000000000..90fe7aa3df1c --- /dev/null +++ b/sound/soc/intel/common/sof-function-topology-lib.c @@ -0,0 +1,135 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) +// +// 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) 2025 Intel Corporation. +// + +#include <linux/device.h> +#include <linux/errno.h> +#include <linux/firmware.h> +#include <sound/soc.h> +#include <sound/soc-acpi.h> +#include "sof-function-topology-lib.h" + +enum tplg_device_id { + TPLG_DEVICE_SDCA_JACK, + TPLG_DEVICE_SDCA_AMP, + TPLG_DEVICE_SDCA_MIC, + TPLG_DEVICE_INTEL_PCH_DMIC, + TPLG_DEVICE_HDMI, + TPLG_DEVICE_MAX +}; + +#define SDCA_DEVICE_MASK (BIT(TPLG_DEVICE_SDCA_JACK) | BIT(TPLG_DEVICE_SDCA_AMP) | \ + BIT(TPLG_DEVICE_SDCA_MIC)) + +#define SOF_INTEL_PLATFORM_NAME_MAX 4 + +int sof_sdw_get_tplg_files(struct snd_soc_card *card, const struct snd_soc_acpi_mach *mach, + const char *prefix, const char ***tplg_files) +{ + struct snd_soc_acpi_mach_params mach_params = mach->mach_params; + struct snd_soc_dai_link *dai_link; + const struct firmware *fw; + char platform[SOF_INTEL_PLATFORM_NAME_MAX]; + unsigned long tplg_mask = 0; + int tplg_num = 0; + int tplg_dev; + int ret; + int i; + + ret = sscanf(mach->sof_tplg_filename, "sof-%3s-*.tplg", platform); + if (ret != 1) { + dev_err(card->dev, "Invalid platform name %s of tplg %s\n", + platform, mach->sof_tplg_filename); + return -EINVAL; + } + + for_each_card_prelinks(card, i, dai_link) { + char *tplg_dev_name; + + dev_dbg(card->dev, "dai_link %s id %d\n", dai_link->name, dai_link->id); + if (strstr(dai_link->name, "SimpleJack")) { + tplg_dev = TPLG_DEVICE_SDCA_JACK; + tplg_dev_name = "sdca-jack"; + } else if (strstr(dai_link->name, "SmartAmp")) { + tplg_dev = TPLG_DEVICE_SDCA_AMP; + tplg_dev_name = devm_kasprintf(card->dev, GFP_KERNEL, + "sdca-%damp", dai_link->num_cpus); + if (!tplg_dev_name) + return -ENOMEM; + } else if (strstr(dai_link->name, "SmartMic")) { + tplg_dev = TPLG_DEVICE_SDCA_MIC; + tplg_dev_name = "sdca-mic"; + } else if (strstr(dai_link->name, "dmic")) { + switch (mach_params.dmic_num) { + case 2: + tplg_dev_name = "dmic-2ch"; + break; + case 4: + tplg_dev_name = "dmic-4ch"; + break; + default: + dev_warn(card->dev, + "only -2ch and -4ch are supported for dmic\n"); + continue; + } + tplg_dev = TPLG_DEVICE_INTEL_PCH_DMIC; + } else if (strstr(dai_link->name, "iDisp")) { + tplg_dev = TPLG_DEVICE_HDMI; + tplg_dev_name = "hdmi-pcm5"; + + } else { + /* The dai link is not supported by separated tplg yet */ + dev_dbg(card->dev, + "dai_link %s is not supported by separated tplg yet\n", + dai_link->name); + return 0; + } + if (tplg_mask & BIT(tplg_dev)) + continue; + + tplg_mask |= BIT(tplg_dev); + + /* + * The tplg file naming rule is sof-<platform>-<function>-id<BE id number>.tplg + * where <platform> is only required for the DMIC function as the nhlt blob + * is platform dependent. + */ + switch (tplg_dev) { + case TPLG_DEVICE_INTEL_PCH_DMIC: + (*tplg_files)[tplg_num] = devm_kasprintf(card->dev, GFP_KERNEL, + "%s/sof-%s-%s-id%d.tplg", + prefix, platform, + tplg_dev_name, dai_link->id); + break; + default: + (*tplg_files)[tplg_num] = devm_kasprintf(card->dev, GFP_KERNEL, + "%s/sof-%s-id%d.tplg", + prefix, tplg_dev_name, + dai_link->id); + break; + } + if (!(*tplg_files)[tplg_num]) + return -ENOMEM; + tplg_num++; + } + + dev_dbg(card->dev, "tplg_mask %#lx tplg_num %d\n", tplg_mask, tplg_num); + + /* Check presence of sub-topologies */ + for (i = 0; i < tplg_num; i++) { + ret = firmware_request_nowarn(&fw, (*tplg_files)[i], card->dev); + if (!ret) { + release_firmware(fw); + } else { + dev_dbg(card->dev, "Failed to open topology file: %s\n", (*tplg_files)[i]); + return 0; + } + } + + return tplg_num; +} + diff --git a/sound/soc/intel/common/sof-function-topology-lib.h b/sound/soc/intel/common/sof-function-topology-lib.h new file mode 100644 index 000000000000..e7d0c39d0788 --- /dev/null +++ b/sound/soc/intel/common/sof-function-topology-lib.h @@ -0,0 +1,15 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * soc-acpi-intel-get-tplg.h - get-tplg-files ops + * + * Copyright (c) 2025, Intel Corporation. + * + */ + +#ifndef _SND_SOC_ACPI_INTEL_GET_TPLG_H +#define _SND_SOC_ACPI_INTEL_GET_TPLG_H + +int sof_sdw_get_tplg_files(struct snd_soc_card *card, const struct snd_soc_acpi_mach *mach, + const char *prefix, const char ***tplg_files); + +#endif diff --git a/sound/soc/sdw_utils/soc_sdw_utils.c b/sound/soc/sdw_utils/soc_sdw_utils.c index 5175818ff2c1..60b731673f3b 100644 --- a/sound/soc/sdw_utils/soc_sdw_utils.c +++ b/sound/soc/sdw_utils/soc_sdw_utils.c @@ -10,6 +10,7 @@ #include <linux/module.h> #include <linux/soundwire/sdw.h> #include <linux/soundwire/sdw_type.h> +#include <sound/sdca_function.h> #include <sound/soc_sdw_utils.h> static const struct snd_soc_dapm_widget generic_dmic_widgets[] = { @@ -934,10 +935,10 @@ static bool asoc_sdw_is_unique_device(const struct snd_soc_acpi_link_adr *adr_li return true; } -const char *asoc_sdw_get_codec_name(struct device *dev, - const struct asoc_sdw_codec_info *codec_info, - const struct snd_soc_acpi_link_adr *adr_link, - int adr_index) +static const char *_asoc_sdw_get_codec_name(struct device *dev, + const struct asoc_sdw_codec_info *codec_info, + const struct snd_soc_acpi_link_adr *adr_link, + int adr_index) { u64 adr = adr_link->adr_d[adr_index].adr; unsigned int sdw_version = SDW_VERSION(adr); @@ -947,17 +948,24 @@ const char *asoc_sdw_get_codec_name(struct device *dev, unsigned int part_id = SDW_PART_ID(adr); unsigned int class_id = SDW_CLASS_ID(adr); - if (codec_info->codec_name) - return devm_kstrdup(dev, codec_info->codec_name, GFP_KERNEL); - else if (asoc_sdw_is_unique_device(adr_link, sdw_version, mfg_id, part_id, - class_id, adr_index)) + if (asoc_sdw_is_unique_device(adr_link, sdw_version, mfg_id, part_id, + class_id, adr_index)) return devm_kasprintf(dev, GFP_KERNEL, "sdw:0:%01x:%04x:%04x:%02x", link_id, mfg_id, part_id, class_id); - else - return devm_kasprintf(dev, GFP_KERNEL, "sdw:0:%01x:%04x:%04x:%02x:%01x", - link_id, mfg_id, part_id, class_id, unique_id); - return NULL; + return devm_kasprintf(dev, GFP_KERNEL, "sdw:0:%01x:%04x:%04x:%02x:%01x", + link_id, mfg_id, part_id, class_id, unique_id); +} + +const char *asoc_sdw_get_codec_name(struct device *dev, + const struct asoc_sdw_codec_info *codec_info, + const struct snd_soc_acpi_link_adr *adr_link, + int adr_index) +{ + if (codec_info->codec_name) + return devm_kstrdup(dev, codec_info->codec_name, GFP_KERNEL); + + return _asoc_sdw_get_codec_name(dev, codec_info, adr_link, adr_index); } EXPORT_SYMBOL_NS(asoc_sdw_get_codec_name, "SND_SOC_SDW_UTILS"); @@ -1124,6 +1132,106 @@ struct asoc_sdw_dailink *asoc_sdw_find_dailink(struct asoc_sdw_dailink *dailinks } EXPORT_SYMBOL_NS(asoc_sdw_find_dailink, "SND_SOC_SDW_UTILS"); +static int asoc_sdw_get_dai_type(u32 type) +{ + switch (type) { + case SDCA_FUNCTION_TYPE_SMART_AMP: + case SDCA_FUNCTION_TYPE_SIMPLE_AMP: + return SOC_SDW_DAI_TYPE_AMP; + case SDCA_FUNCTION_TYPE_SMART_MIC: + case SDCA_FUNCTION_TYPE_SIMPLE_MIC: + case SDCA_FUNCTION_TYPE_SPEAKER_MIC: + return SOC_SDW_DAI_TYPE_MIC; + case SDCA_FUNCTION_TYPE_UAJ: + case SDCA_FUNCTION_TYPE_RJ: + case SDCA_FUNCTION_TYPE_SIMPLE_JACK: + return SOC_SDW_DAI_TYPE_JACK; + default: + return -EINVAL; + } +} + +/* + * Check if the SDCA endpoint is present by the SDW peripheral + * + * @dev: Device pointer + * @codec_info: Codec info pointer + * @adr_link: ACPI link address + * @adr_index: Index of the ACPI link address + * @end_index: Index of the endpoint + * + * Return: 1 if the endpoint is present, + * 0 if the endpoint is not present, + * negative error code. + */ + +static int is_sdca_endpoint_present(struct device *dev, + struct asoc_sdw_codec_info *codec_info, + const struct snd_soc_acpi_link_adr *adr_link, + int adr_index, int end_index) +{ + const struct snd_soc_acpi_adr_device *adr_dev = &adr_link->adr_d[adr_index]; + const struct snd_soc_acpi_endpoint *adr_end; + const struct asoc_sdw_dai_info *dai_info; + struct snd_soc_dai_link_component *dlc; + struct snd_soc_dai *codec_dai; + struct sdw_slave *slave; + struct device *sdw_dev; + const char *sdw_codec_name; + int i; + + dlc = kzalloc(sizeof(*dlc), GFP_KERNEL); + + adr_end = &adr_dev->endpoints[end_index]; + dai_info = &codec_info->dais[adr_end->num]; + + dlc->dai_name = dai_info->dai_name; + codec_dai = snd_soc_find_dai_with_mutex(dlc); + if (!codec_dai) { + dev_warn(dev, "codec dai %s not registered yet\n", dlc->dai_name); + kfree(dlc); + return -EPROBE_DEFER; + } + kfree(dlc); + + sdw_codec_name = _asoc_sdw_get_codec_name(dev, codec_info, + adr_link, adr_index); + if (!sdw_codec_name) + return -ENOMEM; + + sdw_dev = bus_find_device_by_name(&sdw_bus_type, NULL, sdw_codec_name); + if (!sdw_dev) { + dev_err(dev, "codec %s not found\n", sdw_codec_name); + return -EINVAL; + } + + slave = dev_to_sdw_dev(sdw_dev); + if (!slave) + return -EINVAL; + + /* Make sure BIOS provides SDCA properties */ + if (!slave->sdca_data.interface_revision) { + dev_warn(&slave->dev, "SDCA properties not found in the BIOS\n"); + return 1; + } + + for (i = 0; i < slave->sdca_data.num_functions; i++) { + int dai_type = asoc_sdw_get_dai_type(slave->sdca_data.function[i].type); + + if (dai_type == dai_info->dai_type) { + dev_dbg(&slave->dev, "DAI type %d sdca function %s found\n", + dai_type, slave->sdca_data.function[i].name); + return 1; + } + } + + dev_dbg(&slave->dev, + "SDCA device function for DAI type %d not supported, skip endpoint\n", + dai_info->dai_type); + + return 0; +} + int asoc_sdw_parse_sdw_endpoints(struct snd_soc_card *card, struct asoc_sdw_dailink *soc_dais, struct asoc_sdw_endpoint *soc_ends, @@ -1152,6 +1260,7 @@ int asoc_sdw_parse_sdw_endpoints(struct snd_soc_card *card, const struct snd_soc_acpi_adr_device *adr_dev = &adr_link->adr_d[i]; struct asoc_sdw_codec_info *codec_info; const char *codec_name; + bool check_sdca = false; if (!adr_dev->name_prefix) { dev_err(dev, "codec 0x%llx does not have a name prefix\n", @@ -1182,6 +1291,9 @@ int asoc_sdw_parse_sdw_endpoints(struct snd_soc_card *card, soc_end->include_sidecar = true; } + if (SDW_CLASS_ID(adr_dev->adr) && adr_dev->num_endpoints > 1) + check_sdca = true; + for (j = 0; j < adr_dev->num_endpoints; j++) { const struct snd_soc_acpi_endpoint *adr_end; const struct asoc_sdw_dai_info *dai_info; @@ -1192,9 +1304,35 @@ int asoc_sdw_parse_sdw_endpoints(struct snd_soc_card *card, dai_info = &codec_info->dais[adr_end->num]; soc_dai = asoc_sdw_find_dailink(soc_dais, adr_end); - if (dai_info->quirk && - !(dai_info->quirk_exclude ^ !!(dai_info->quirk & ctx->mc_quirk))) - continue; + /* + * quirk should have higher priority than the sdca properties + * in the BIOS. We can't always check the DAI quirk because we + * will set the mc_quirk when the BIOS doesn't provide the right + * information. The endpoint will be skipped if the dai_info-> + * quirk_exclude and mc_quirk are both not set if we always skip + * the endpoint according to the quirk information. We need to + * keep the endpoint if it is present in the BIOS. So, only + * check the DAI quirk when the mc_quirk is set or SDCA endpoint + * present check is not needed. + */ + if (dai_info->quirk & ctx->mc_quirk || !check_sdca) { + /* + * Check the endpoint if a matching quirk is set or SDCA + * endpoint check is not necessary + */ + if (dai_info->quirk && + !(dai_info->quirk_exclude ^ !!(dai_info->quirk & ctx->mc_quirk))) + continue; + } else { + /* Check SDCA codec endpoint if there is no matching quirk */ + ret = is_sdca_endpoint_present(dev, codec_info, adr_link, i, j); + if (ret < 0) + return ret; + + /* The endpoint is not present, skip */ + if (!ret) + continue; + } dev_dbg(dev, "Add dev: %d, 0x%llx end: %d, dai: %d, %c/%c to %s: %d\n", diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index dc9cb8324067..e19ba94f2c80 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -571,7 +571,11 @@ static int sof_copy_tuples(struct snd_sof_dev *sdev, struct snd_soc_tplg_vendor_ continue; tuples[*num_copied_tuples].token = tokens[j].token; - tuples[*num_copied_tuples].value.s = elem->string; + tuples[*num_copied_tuples].value.s = + devm_kasprintf(sdev->dev, GFP_KERNEL, + "%s", elem->string); + if (!tuples[*num_copied_tuples].value.s) + return -ENOMEM; } else { struct snd_soc_tplg_vendor_value_elem *elem; @@ -2306,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, @@ -2463,36 +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); - ret = -EINVAL; + 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]); + + 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; + } } - release_firmware(fw); + /* 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); |