1 // SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
2 //
3 // This file is provided under a dual BSD/GPLv2 license. When using or
4 // redistributing this file, you may do so under either license.
5 //
6 // Copyright(c) 2025 Intel Corporation.
7 //
8
9 #include <linux/device.h>
10 #include <linux/errno.h>
11 #include <linux/firmware.h>
12 #include <sound/soc.h>
13 #include <sound/soc-acpi.h>
14 #include "sof-function-topology-lib.h"
15
16 enum tplg_device_id {
17 TPLG_DEVICE_SDCA_JACK,
18 TPLG_DEVICE_SDCA_AMP,
19 TPLG_DEVICE_SDCA_MIC,
20 TPLG_DEVICE_INTEL_PCH_DMIC,
21 TPLG_DEVICE_HDMI,
22 TPLG_DEVICE_LOOPBACK_VIRTUAL,
23 TPLG_DEVICE_MAX
24 };
25
26 #define SDCA_DEVICE_MASK (BIT(TPLG_DEVICE_SDCA_JACK) | BIT(TPLG_DEVICE_SDCA_AMP) | \
27 BIT(TPLG_DEVICE_SDCA_MIC))
28
29 #define SOF_INTEL_PLATFORM_NAME_MAX 4
30
sof_sdw_get_tplg_files(struct snd_soc_card * card,const struct snd_soc_acpi_mach * mach,const char * prefix,const char *** tplg_files,bool best_effort)31 int sof_sdw_get_tplg_files(struct snd_soc_card *card, const struct snd_soc_acpi_mach *mach,
32 const char *prefix, const char ***tplg_files, bool best_effort)
33 {
34 struct snd_soc_acpi_mach *card_mach = dev_get_platdata(card->dev);
35 /*
36 * Use the acpi mach from the machine driver because the machine driver
37 * may change the dmic_num based on the machine driver quirk.
38 */
39 struct snd_soc_acpi_mach_params mach_params = card_mach->mach_params;
40 struct snd_soc_dai_link *dai_link;
41 const struct firmware *fw;
42 char platform[SOF_INTEL_PLATFORM_NAME_MAX];
43 unsigned long tplg_mask = 0;
44 int tplg_num = 0;
45 int tplg_dev;
46 int ret;
47 int i;
48
49 ret = sscanf(mach->sof_tplg_filename, "sof-%3s-*.tplg", platform);
50 if (ret != 1) {
51 dev_err(card->dev, "Invalid platform name %s of tplg %s\n",
52 platform, mach->sof_tplg_filename);
53 return -EINVAL;
54 }
55
56 for_each_card_prelinks(card, i, dai_link) {
57 char *tplg_dev_name;
58
59 dev_dbg(card->dev, "dai_link %s id %d\n", dai_link->name, dai_link->id);
60 if (strstr(dai_link->name, "SimpleJack")) {
61 tplg_dev = TPLG_DEVICE_SDCA_JACK;
62 tplg_dev_name = "sdca-jack";
63 } else if (strstr(dai_link->name, "SmartAmp")) {
64 tplg_dev = TPLG_DEVICE_SDCA_AMP;
65 tplg_dev_name = devm_kasprintf(card->dev, GFP_KERNEL,
66 "sdca-%damp", dai_link->num_cpus);
67 if (!tplg_dev_name)
68 return -ENOMEM;
69 } else if (strstr(dai_link->name, "SmartMic")) {
70 tplg_dev = TPLG_DEVICE_SDCA_MIC;
71 tplg_dev_name = "sdca-mic";
72 } else if (strstr(dai_link->name, "dmic")) {
73 switch (mach_params.dmic_num) {
74 case 2:
75 tplg_dev_name = "dmic-2ch";
76 break;
77 case 4:
78 tplg_dev_name = "dmic-4ch";
79 break;
80 default:
81 dev_warn(card->dev,
82 "unsupported number of dmics: %d\n",
83 mach_params.dmic_num);
84 continue;
85 }
86 tplg_dev = TPLG_DEVICE_INTEL_PCH_DMIC;
87 } else if (strstr(dai_link->name, "iDisp")) {
88 tplg_dev = TPLG_DEVICE_HDMI;
89 tplg_dev_name = "hdmi-pcm5";
90 } else if (strstr(dai_link->name, "Loopback_Virtual")) {
91 tplg_dev = TPLG_DEVICE_LOOPBACK_VIRTUAL;
92 /*
93 * Mark the LOOPBACK_VIRTUAL device but no need to create the
94 * LOOPBACK_VIRTUAL topology. Just to avoid the dai_link is not supported
95 * error.
96 */
97 tplg_mask |= BIT(tplg_dev);
98 continue;
99 } else {
100 /* The dai link is not supported by separated tplg yet */
101 dev_dbg(card->dev,
102 "dai_link %s is not supported by separated tplg yet\n",
103 dai_link->name);
104 if (best_effort)
105 continue;
106
107 return 0;
108 }
109 if (tplg_mask & BIT(tplg_dev))
110 continue;
111
112 tplg_mask |= BIT(tplg_dev);
113
114 /*
115 * The tplg file naming rule is sof-<platform>-<function>-id<BE id number>.tplg
116 * where <platform> is only required for the DMIC function as the nhlt blob
117 * is platform dependent.
118 */
119 switch (tplg_dev) {
120 case TPLG_DEVICE_INTEL_PCH_DMIC:
121 (*tplg_files)[tplg_num] = devm_kasprintf(card->dev, GFP_KERNEL,
122 "%s/sof-%s-%s-id%d.tplg",
123 prefix, platform,
124 tplg_dev_name, dai_link->id);
125 break;
126 default:
127 (*tplg_files)[tplg_num] = devm_kasprintf(card->dev, GFP_KERNEL,
128 "%s/sof-%s-id%d.tplg",
129 prefix, tplg_dev_name,
130 dai_link->id);
131 break;
132 }
133 if (!(*tplg_files)[tplg_num])
134 return -ENOMEM;
135 tplg_num++;
136 }
137
138 dev_dbg(card->dev, "tplg_mask %#lx tplg_num %d\n", tplg_mask, tplg_num);
139
140 /* Check presence of sub-topologies */
141 for (i = 0; i < tplg_num; i++) {
142 ret = firmware_request_nowarn(&fw, (*tplg_files)[i], card->dev);
143 if (!ret) {
144 release_firmware(fw);
145 } else {
146 dev_warn(card->dev,
147 "Failed to open topology file: %s, you might need to\n",
148 (*tplg_files)[i]);
149 dev_warn(card->dev,
150 "download it from https://github.com/thesofproject/sof-bin/\n");
151 return 0;
152 }
153 }
154
155 return tplg_num;
156 }
157 EXPORT_SYMBOL_GPL(sof_sdw_get_tplg_files);
158