forked from torvalds/linux
-
Notifications
You must be signed in to change notification settings - Fork 144
Expand file tree
/
Copy pathsof-function-topology-lib.c
More file actions
355 lines (314 loc) · 9.6 KB
/
sof-function-topology-lib.c
File metadata and controls
355 lines (314 loc) · 9.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
// 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_SSP_JACK,
TPLG_DEVICE_SSP_AMP,
TPLG_DEVICE_SSP_BT,
TPLG_DEVICE_SSP_HDMI_IN,
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
static int get_platform_name(struct snd_soc_card *card,
const struct snd_soc_acpi_mach *mach, char *platform)
{
int ret;
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;
}
return 0;
}
static bool all_tplg_files_exist(struct device *dev, const char ***tplg_files, int tplg_num)
{
const struct firmware *fw;
int ret;
int i;
for (i = 0; i < tplg_num; i++) {
ret = firmware_request_nowarn(&fw, (*tplg_files)[i], dev);
if (!ret) {
release_firmware(fw);
} else {
dev_warn(dev,
"Failed to open topology file: %s, you might need to\n",
(*tplg_files)[i]);
dev_warn(dev,
"download it from https://github.com/thesofproject/sof-bin/\n");
return false;
}
}
return true;
}
static char *get_tplg_filename(struct device *dev, const char *prefix,
const char *platform, const char *tplg_dev_name,
int dai_link_id, int tplg_dev)
{
char *filename = NULL;
/*
* The tplg file naming rule is sof-<platform>-<function>-id<BE id number>.tplg
* where <platform> is only required for the devices that need NHLT blob like DMIC
* as the nhlt blob is platform dependent.
*/
switch (tplg_dev) {
case TPLG_DEVICE_INTEL_PCH_DMIC:
case TPLG_DEVICE_SSP_JACK:
case TPLG_DEVICE_SSP_AMP:
case TPLG_DEVICE_SSP_BT:
case TPLG_DEVICE_SSP_HDMI_IN:
filename = devm_kasprintf(dev, GFP_KERNEL, "%s/sof-%s-%s-id%d.tplg",
prefix, platform, tplg_dev_name, dai_link_id);
break;
default:
filename = devm_kasprintf(dev, GFP_KERNEL, "%s/sof-%s-id%d.tplg",
prefix, tplg_dev_name, dai_link_id);
break;
}
return filename;
}
static int get_dmic_tplg_dev(struct device *dev, int dmic_num,
int *tplg_dev, char **tplg_dev_name)
{
switch (dmic_num) {
case 2:
*tplg_dev_name = "dmic-2ch";
break;
case 4:
*tplg_dev_name = "dmic-4ch";
break;
default:
dev_warn(dev,
"unsupported number of dmics: %d\n",
dmic_num);
return -EINVAL;
}
*tplg_dev = TPLG_DEVICE_INTEL_PCH_DMIC;
return 0;
}
static int get_ssp_tplg_dev(struct device *dev, struct snd_soc_dai_link *dai_link,
u16 *hdmi_in_mask, int *tplg_dev, char **tplg_dev_name)
{
unsigned int ssp_port;
if (sscanf(dai_link->name, "SSP%d", &ssp_port) != 1) {
dev_err(dev, "Invalid SSP port %d\n", ssp_port);
return -EINVAL;
}
if (strstr(dai_link->name, "Codec")) {
/*
* Assume DAI link 0 is jack which is true in all existing
* machine drivers
*/
if (dai_link->id == 0) {
*tplg_dev = TPLG_DEVICE_SSP_JACK;
*tplg_dev_name = devm_kasprintf(dev, GFP_KERNEL,
"ssp%d-jack", ssp_port);
} else {
*tplg_dev = TPLG_DEVICE_SSP_AMP;
*tplg_dev_name = devm_kasprintf(dev, GFP_KERNEL,
"ssp%d-amp", ssp_port);
}
} else if (strstr(dai_link->name, "BT")) {
*tplg_dev = TPLG_DEVICE_SSP_BT;
*tplg_dev_name = devm_kasprintf(dev, GFP_KERNEL,
"ssp%d-bt", ssp_port);
} else if (strstr(dai_link->name, "HDMI")) {
*hdmi_in_mask |= BIT(ssp_port);
/* The number of HDMI in dai link is always 2 right now */
if (hweight16(*hdmi_in_mask) != 2)
return -EINVAL;
*tplg_dev = TPLG_DEVICE_SSP_HDMI_IN;
*tplg_dev_name = devm_kasprintf(dev, GFP_KERNEL,
"ssp%x-hdmiin", *hdmi_in_mask);
} else {
dev_warn(dev,
"unsupported SSP link %s\n", dai_link->name);
return -EINVAL;
}
return 0;
}
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, bool best_effort)
{
struct snd_soc_acpi_mach_params mach_params = mach->mach_params;
struct snd_soc_dai_link *dai_link;
char platform[SOF_INTEL_PLATFORM_NAME_MAX];
unsigned long tplg_mask = 0;
u16 hdmi_in_mask = 0;
int tplg_num = 0;
int tplg_dev;
int ret;
int i;
ret = get_platform_name(card, mach, platform);
if (ret < 0)
return ret;
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")) {
if (get_dmic_tplg_dev(card->dev, mach_params.dmic_num,
&tplg_dev, &tplg_dev_name) < 0)
continue;
} else if (strstr(dai_link->name, "iDisp")) {
tplg_dev = TPLG_DEVICE_HDMI;
tplg_dev_name = "hdmi-pcm5";
} else if (strstr(dai_link->name, "SSP")) {
if (get_ssp_tplg_dev(card->dev, dai_link, &hdmi_in_mask,
&tplg_dev, &tplg_dev_name) < 0)
continue;
} 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);
if (best_effort)
continue;
return 0;
}
if (tplg_mask & BIT(tplg_dev))
continue;
tplg_mask |= BIT(tplg_dev);
(*tplg_files)[tplg_num] = get_tplg_filename(card->dev, prefix, platform,
tplg_dev_name, dai_link->id,
tplg_dev);
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 */
if (all_tplg_files_exist(card->dev, tplg_files, tplg_num))
return tplg_num;
/* return 0 to use monolithic topology */
return 0;
}
EXPORT_SYMBOL_GPL(sof_sdw_get_tplg_files);
int sof_i2s_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)
{
struct snd_soc_acpi_mach_params mach_params = mach->mach_params;
struct snd_soc_dai_link *dai_link;
char platform[SOF_INTEL_PLATFORM_NAME_MAX];
unsigned long tplg_mask = 0;
u16 hdmi_in_mask = 0;
int tplg_num = 0;
int tplg_dev;
int ret;
int i;
ret = get_platform_name(card, mach, platform);
if (ret < 0)
return ret;
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, "SSP")) {
unsigned int ssp_port;
if (sscanf(dai_link->name, "SSP%d", &ssp_port) != 1) {
dev_err(card->dev, "Invalid SSP port %d\n", ssp_port);
return -EINVAL;
}
if (strstr(dai_link->name, "Codec")) {
/*
* Assume DAI link 0 is jack which is true in all existing
* machine driver
*/
if (dai_link->id == 0) {
tplg_dev = TPLG_DEVICE_SSP_JACK;
tplg_dev_name = devm_kasprintf(card->dev, GFP_KERNEL,
"ssp%d-jack", ssp_port);
} else {
tplg_dev = TPLG_DEVICE_SSP_AMP;
tplg_dev_name = devm_kasprintf(card->dev, GFP_KERNEL,
"ssp%d-amp", ssp_port);
}
} else if (strstr(dai_link->name, "BT")) {
tplg_dev = TPLG_DEVICE_SSP_BT;
tplg_dev_name = devm_kasprintf(card->dev, GFP_KERNEL,
"ssp%d-bt", ssp_port);
} else if (strstr(dai_link->name, "HDMI")) {
hdmi_in_mask |= BIT(ssp_port);
/* The number of HDMI in dai link is always 2 right now */
if (hweight16(hdmi_in_mask) != 2)
continue;
tplg_dev = TPLG_DEVICE_SSP_HDMI_IN;
tplg_dev_name = devm_kasprintf(card->dev, GFP_KERNEL,
"ssp%x-hdmiin", hdmi_in_mask);
} else {
dev_warn(card->dev,
"unsupported SSP link %s\n", dai_link->name);
continue;
}
} 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,
"unsupported number of dmics: %d\n",
mach_params.dmic_num);
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);
if (best_effort)
continue;
return 0;
}
if (tplg_mask & BIT(tplg_dev))
continue;
tplg_mask |= BIT(tplg_dev);
(*tplg_files)[tplg_num] = get_tplg_filename(card->dev, prefix, platform,
tplg_dev_name, dai_link->id,
tplg_dev);
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 */
if (all_tplg_files_exist(card->dev, tplg_files, tplg_num))
return tplg_num;
/* return 0 to use monolithic topology */
return 0;
}
EXPORT_SYMBOL_GPL(sof_i2s_get_tplg_files);