xref: /linux/sound/soc/intel/common/sof-function-topology-lib.c (revision 33f016b23a219fe034213849b51436b8e79df251)
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 
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