1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | // |
3 | // Copyright(c) 2021 Intel Corporation. All rights reserved. |
4 | // |
5 | // Authors: Cezary Rojewski <cezary.rojewski@intel.com> |
6 | // Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com> |
7 | // |
8 | |
9 | #include <linux/firmware.h> |
10 | #include <linux/uuid.h> |
11 | #include <sound/soc.h> |
12 | #include <sound/soc-acpi.h> |
13 | #include <sound/soc-topology.h> |
14 | #include <uapi/sound/intel/avs/tokens.h> |
15 | #include "avs.h" |
16 | #include "control.h" |
17 | #include "topology.h" |
18 | #include "utils.h" |
19 | |
20 | /* Get pointer to vendor array at the specified offset. */ |
21 | #define avs_tplg_vendor_array_at(array, offset) \ |
22 | ((struct snd_soc_tplg_vendor_array *)((u8 *)array + offset)) |
23 | |
24 | /* Get pointer to vendor array that is next in line. */ |
25 | #define avs_tplg_vendor_array_next(array) \ |
26 | (avs_tplg_vendor_array_at(array, le32_to_cpu((array)->size))) |
27 | |
28 | /* |
29 | * Scan provided block of tuples for the specified token. If found, |
30 | * @offset is updated with position at which first matching token is |
31 | * located. |
32 | * |
33 | * Returns 0 on success, -ENOENT if not found and error code otherwise. |
34 | */ |
35 | static int |
36 | avs_tplg_vendor_array_lookup(struct snd_soc_tplg_vendor_array *tuples, |
37 | u32 block_size, u32 token, u32 *offset) |
38 | { |
39 | u32 pos = 0; |
40 | |
41 | while (block_size > 0) { |
42 | struct snd_soc_tplg_vendor_value_elem *tuple; |
43 | u32 tuples_size = le32_to_cpu(tuples->size); |
44 | |
45 | if (tuples_size > block_size) |
46 | return -EINVAL; |
47 | |
48 | tuple = tuples->value; |
49 | if (le32_to_cpu(tuple->token) == token) { |
50 | *offset = pos; |
51 | return 0; |
52 | } |
53 | |
54 | block_size -= tuples_size; |
55 | pos += tuples_size; |
56 | tuples = avs_tplg_vendor_array_next(tuples); |
57 | } |
58 | |
59 | return -ENOENT; |
60 | } |
61 | |
62 | /* |
63 | * See avs_tplg_vendor_array_lookup() for description. |
64 | * |
65 | * Behaves exactly like avs_tplg_vendor_lookup() but starts from the |
66 | * next vendor array in line. Useful when searching for the finish line |
67 | * of an arbitrary entry in a list of entries where each is composed of |
68 | * several vendor tuples and a specific token marks the beginning of |
69 | * a new entry block. |
70 | */ |
71 | static int |
72 | avs_tplg_vendor_array_lookup_next(struct snd_soc_tplg_vendor_array *tuples, |
73 | u32 block_size, u32 token, u32 *offset) |
74 | { |
75 | u32 tuples_size = le32_to_cpu(tuples->size); |
76 | int ret; |
77 | |
78 | if (tuples_size > block_size) |
79 | return -EINVAL; |
80 | |
81 | tuples = avs_tplg_vendor_array_next(tuples); |
82 | block_size -= tuples_size; |
83 | |
84 | ret = avs_tplg_vendor_array_lookup(tuples, block_size, token, offset); |
85 | if (!ret) |
86 | *offset += tuples_size; |
87 | return ret; |
88 | } |
89 | |
90 | /* |
91 | * Scan provided block of tuples for the specified token which marks |
92 | * the border of an entry block. Behavior is similar to |
93 | * avs_tplg_vendor_array_lookup() except 0 is also returned if no |
94 | * matching token has been found. In such case, returned @size is |
95 | * assigned to @block_size as the entire block belongs to the current |
96 | * entry. |
97 | * |
98 | * Returns 0 on success, error code otherwise. |
99 | */ |
100 | static int |
101 | avs_tplg_vendor_entry_size(struct snd_soc_tplg_vendor_array *tuples, |
102 | u32 block_size, u32 entry_id_token, u32 *size) |
103 | { |
104 | int ret; |
105 | |
106 | ret = avs_tplg_vendor_array_lookup_next(tuples, block_size, token: entry_id_token, offset: size); |
107 | if (ret == -ENOENT) { |
108 | *size = block_size; |
109 | ret = 0; |
110 | } |
111 | |
112 | return ret; |
113 | } |
114 | |
115 | /* |
116 | * Vendor tuple parsing descriptor. |
117 | * |
118 | * @token: vendor specific token that identifies tuple |
119 | * @type: tuple type, one of SND_SOC_TPLG_TUPLE_TYPE_XXX |
120 | * @offset: offset of a struct's field to initialize |
121 | * @parse: parsing function, extracts and assigns value to object's field |
122 | */ |
123 | struct avs_tplg_token_parser { |
124 | enum avs_tplg_token token; |
125 | u32 type; |
126 | u32 offset; |
127 | int (*parse)(struct snd_soc_component *comp, void *elem, void *object, u32 offset); |
128 | }; |
129 | |
130 | static int |
131 | avs_parse_uuid_token(struct snd_soc_component *comp, void *elem, void *object, u32 offset) |
132 | { |
133 | struct snd_soc_tplg_vendor_uuid_elem *tuple = elem; |
134 | guid_t *val = (guid_t *)((u8 *)object + offset); |
135 | |
136 | guid_copy(dst: (guid_t *)val, src: (const guid_t *)&tuple->uuid); |
137 | |
138 | return 0; |
139 | } |
140 | |
141 | static int |
142 | avs_parse_bool_token(struct snd_soc_component *comp, void *elem, void *object, u32 offset) |
143 | { |
144 | struct snd_soc_tplg_vendor_value_elem *tuple = elem; |
145 | bool *val = (bool *)((u8 *)object + offset); |
146 | |
147 | *val = le32_to_cpu(tuple->value); |
148 | |
149 | return 0; |
150 | } |
151 | |
152 | static int |
153 | avs_parse_byte_token(struct snd_soc_component *comp, void *elem, void *object, u32 offset) |
154 | { |
155 | struct snd_soc_tplg_vendor_value_elem *tuple = elem; |
156 | u8 *val = ((u8 *)object + offset); |
157 | |
158 | *val = le32_to_cpu(tuple->value); |
159 | |
160 | return 0; |
161 | } |
162 | |
163 | static int |
164 | avs_parse_short_token(struct snd_soc_component *comp, void *elem, void *object, u32 offset) |
165 | { |
166 | struct snd_soc_tplg_vendor_value_elem *tuple = elem; |
167 | u16 *val = (u16 *)((u8 *)object + offset); |
168 | |
169 | *val = le32_to_cpu(tuple->value); |
170 | |
171 | return 0; |
172 | } |
173 | |
174 | static int |
175 | avs_parse_word_token(struct snd_soc_component *comp, void *elem, void *object, u32 offset) |
176 | { |
177 | struct snd_soc_tplg_vendor_value_elem *tuple = elem; |
178 | u32 *val = (u32 *)((u8 *)object + offset); |
179 | |
180 | *val = le32_to_cpu(tuple->value); |
181 | |
182 | return 0; |
183 | } |
184 | |
185 | static int |
186 | avs_parse_string_token(struct snd_soc_component *comp, void *elem, void *object, u32 offset) |
187 | { |
188 | struct snd_soc_tplg_vendor_string_elem *tuple = elem; |
189 | char *val = (char *)((u8 *)object + offset); |
190 | |
191 | snprintf(buf: val, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, fmt: "%s" , tuple->string); |
192 | |
193 | return 0; |
194 | } |
195 | |
196 | static int avs_parse_uuid_tokens(struct snd_soc_component *comp, void *object, |
197 | const struct avs_tplg_token_parser *parsers, int count, |
198 | struct snd_soc_tplg_vendor_array *tuples) |
199 | { |
200 | struct snd_soc_tplg_vendor_uuid_elem *tuple; |
201 | int ret, i, j; |
202 | |
203 | /* Parse element by element. */ |
204 | for (i = 0; i < le32_to_cpu(tuples->num_elems); i++) { |
205 | tuple = &tuples->uuid[i]; |
206 | |
207 | for (j = 0; j < count; j++) { |
208 | /* Ignore non-UUID tokens. */ |
209 | if (parsers[j].type != SND_SOC_TPLG_TUPLE_TYPE_UUID || |
210 | parsers[j].token != le32_to_cpu(tuple->token)) |
211 | continue; |
212 | |
213 | ret = parsers[j].parse(comp, tuple, object, parsers[j].offset); |
214 | if (ret) |
215 | return ret; |
216 | } |
217 | } |
218 | |
219 | return 0; |
220 | } |
221 | |
222 | static int avs_parse_string_tokens(struct snd_soc_component *comp, void *object, |
223 | const struct avs_tplg_token_parser *parsers, int count, |
224 | struct snd_soc_tplg_vendor_array *tuples) |
225 | { |
226 | struct snd_soc_tplg_vendor_string_elem *tuple; |
227 | int ret, i, j; |
228 | |
229 | /* Parse element by element. */ |
230 | for (i = 0; i < le32_to_cpu(tuples->num_elems); i++) { |
231 | tuple = &tuples->string[i]; |
232 | |
233 | for (j = 0; j < count; j++) { |
234 | /* Ignore non-string tokens. */ |
235 | if (parsers[j].type != SND_SOC_TPLG_TUPLE_TYPE_STRING || |
236 | parsers[j].token != le32_to_cpu(tuple->token)) |
237 | continue; |
238 | |
239 | ret = parsers[j].parse(comp, tuple, object, parsers[j].offset); |
240 | if (ret) |
241 | return ret; |
242 | } |
243 | } |
244 | |
245 | return 0; |
246 | } |
247 | |
248 | static int avs_parse_word_tokens(struct snd_soc_component *comp, void *object, |
249 | const struct avs_tplg_token_parser *parsers, int count, |
250 | struct snd_soc_tplg_vendor_array *tuples) |
251 | { |
252 | struct snd_soc_tplg_vendor_value_elem *tuple; |
253 | int ret, i, j; |
254 | |
255 | /* Parse element by element. */ |
256 | for (i = 0; i < le32_to_cpu(tuples->num_elems); i++) { |
257 | tuple = &tuples->value[i]; |
258 | |
259 | for (j = 0; j < count; j++) { |
260 | /* Ignore non-integer tokens. */ |
261 | if (!(parsers[j].type == SND_SOC_TPLG_TUPLE_TYPE_WORD || |
262 | parsers[j].type == SND_SOC_TPLG_TUPLE_TYPE_SHORT || |
263 | parsers[j].type == SND_SOC_TPLG_TUPLE_TYPE_BYTE || |
264 | parsers[j].type == SND_SOC_TPLG_TUPLE_TYPE_BOOL)) |
265 | continue; |
266 | |
267 | if (parsers[j].token != le32_to_cpu(tuple->token)) |
268 | continue; |
269 | |
270 | ret = parsers[j].parse(comp, tuple, object, parsers[j].offset); |
271 | if (ret) |
272 | return ret; |
273 | } |
274 | } |
275 | |
276 | return 0; |
277 | } |
278 | |
279 | static int avs_parse_tokens(struct snd_soc_component *comp, void *object, |
280 | const struct avs_tplg_token_parser *parsers, size_t count, |
281 | struct snd_soc_tplg_vendor_array *tuples, int priv_size) |
282 | { |
283 | int array_size, ret; |
284 | |
285 | while (priv_size > 0) { |
286 | array_size = le32_to_cpu(tuples->size); |
287 | |
288 | if (array_size <= 0) { |
289 | dev_err(comp->dev, "invalid array size 0x%x\n" , array_size); |
290 | return -EINVAL; |
291 | } |
292 | |
293 | /* Make sure there is enough data before parsing. */ |
294 | priv_size -= array_size; |
295 | if (priv_size < 0) { |
296 | dev_err(comp->dev, "invalid array size 0x%x\n" , array_size); |
297 | return -EINVAL; |
298 | } |
299 | |
300 | switch (le32_to_cpu(tuples->type)) { |
301 | case SND_SOC_TPLG_TUPLE_TYPE_UUID: |
302 | ret = avs_parse_uuid_tokens(comp, object, parsers, count, tuples); |
303 | break; |
304 | case SND_SOC_TPLG_TUPLE_TYPE_STRING: |
305 | ret = avs_parse_string_tokens(comp, object, parsers, count, tuples); |
306 | break; |
307 | case SND_SOC_TPLG_TUPLE_TYPE_BOOL: |
308 | case SND_SOC_TPLG_TUPLE_TYPE_BYTE: |
309 | case SND_SOC_TPLG_TUPLE_TYPE_SHORT: |
310 | case SND_SOC_TPLG_TUPLE_TYPE_WORD: |
311 | ret = avs_parse_word_tokens(comp, object, parsers, count, tuples); |
312 | break; |
313 | default: |
314 | dev_err(comp->dev, "unknown token type %d\n" , tuples->type); |
315 | ret = -EINVAL; |
316 | } |
317 | |
318 | if (ret) { |
319 | dev_err(comp->dev, "parsing %zu tokens of %d type failed: %d\n" , |
320 | count, tuples->type, ret); |
321 | return ret; |
322 | } |
323 | |
324 | tuples = avs_tplg_vendor_array_next(tuples); |
325 | } |
326 | |
327 | return 0; |
328 | } |
329 | |
330 | #define AVS_DEFINE_PTR_PARSER(name, type, member) \ |
331 | static int \ |
332 | avs_parse_##name##_ptr(struct snd_soc_component *comp, void *elem, void *object, u32 offset) \ |
333 | { \ |
334 | struct snd_soc_tplg_vendor_value_elem *tuple = elem; \ |
335 | struct avs_soc_component *acomp = to_avs_soc_component(comp); \ |
336 | type **val = (type **)(object + offset); \ |
337 | u32 idx; \ |
338 | \ |
339 | idx = le32_to_cpu(tuple->value); \ |
340 | if (idx >= acomp->tplg->num_##member) \ |
341 | return -EINVAL; \ |
342 | \ |
343 | *val = &acomp->tplg->member[idx]; \ |
344 | \ |
345 | return 0; \ |
346 | } |
347 | |
348 | AVS_DEFINE_PTR_PARSER(audio_format, struct avs_audio_format, fmts); |
349 | AVS_DEFINE_PTR_PARSER(modcfg_base, struct avs_tplg_modcfg_base, modcfgs_base); |
350 | AVS_DEFINE_PTR_PARSER(modcfg_ext, struct avs_tplg_modcfg_ext, modcfgs_ext); |
351 | AVS_DEFINE_PTR_PARSER(pplcfg, struct avs_tplg_pplcfg, pplcfgs); |
352 | AVS_DEFINE_PTR_PARSER(binding, struct avs_tplg_binding, bindings); |
353 | |
354 | static int |
355 | parse_audio_format_bitfield(struct snd_soc_component *comp, void *elem, void *object, u32 offset) |
356 | { |
357 | struct snd_soc_tplg_vendor_value_elem *velem = elem; |
358 | struct avs_audio_format *audio_format = object; |
359 | |
360 | switch (offset) { |
361 | case AVS_TKN_AFMT_NUM_CHANNELS_U32: |
362 | audio_format->num_channels = le32_to_cpu(velem->value); |
363 | break; |
364 | case AVS_TKN_AFMT_VALID_BIT_DEPTH_U32: |
365 | audio_format->valid_bit_depth = le32_to_cpu(velem->value); |
366 | break; |
367 | case AVS_TKN_AFMT_SAMPLE_TYPE_U32: |
368 | audio_format->sample_type = le32_to_cpu(velem->value); |
369 | break; |
370 | } |
371 | |
372 | return 0; |
373 | } |
374 | |
375 | static int avs_ssp_sprint(char *buf, size_t size, const char *fmt, int port, int tdm) |
376 | { |
377 | char *needle = strstr(fmt, "%d" ); |
378 | int retsize; |
379 | |
380 | /* |
381 | * If there is %d present in fmt string it should be replaced by either |
382 | * SSP or SSP:TDM, where SSP and TDM are numbers, all other formatting |
383 | * will be ignored. |
384 | */ |
385 | if (needle) { |
386 | retsize = scnprintf(buf, min_t(size_t, size, needle - fmt + 1), fmt: "%s" , fmt); |
387 | retsize += scnprintf(buf: buf + retsize, size: size - retsize, fmt: "%d" , port); |
388 | if (tdm) |
389 | retsize += scnprintf(buf: buf + retsize, size: size - retsize, fmt: ":%d" , tdm); |
390 | retsize += scnprintf(buf: buf + retsize, size: size - retsize, fmt: "%s" , needle + 2); |
391 | return retsize; |
392 | } |
393 | |
394 | return snprintf(buf, size, fmt: "%s" , fmt); |
395 | } |
396 | |
397 | static int parse_link_formatted_string(struct snd_soc_component *comp, void *elem, |
398 | void *object, u32 offset) |
399 | { |
400 | struct snd_soc_tplg_vendor_string_elem *tuple = elem; |
401 | struct snd_soc_acpi_mach *mach = dev_get_platdata(dev: comp->card->dev); |
402 | char *val = (char *)((u8 *)object + offset); |
403 | int ssp_port, tdm_slot; |
404 | |
405 | /* |
406 | * Dynamic naming - string formats, e.g.: ssp%d - supported only for |
407 | * topologies describing single device e.g.: an I2S codec on SSP0. |
408 | */ |
409 | if (!avs_mach_singular_ssp(mach)) |
410 | return avs_parse_string_token(comp, elem, object, offset); |
411 | |
412 | ssp_port = avs_mach_ssp_port(mach); |
413 | if (!avs_mach_singular_tdm(mach, port: ssp_port)) |
414 | return avs_parse_string_token(comp, elem, object, offset); |
415 | |
416 | tdm_slot = avs_mach_ssp_tdm(mach, port: ssp_port); |
417 | |
418 | avs_ssp_sprint(buf: val, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, fmt: tuple->string, port: ssp_port, tdm: tdm_slot); |
419 | |
420 | return 0; |
421 | } |
422 | |
423 | static int |
424 | (struct snd_soc_component *comp, |
425 | struct snd_soc_tplg_vendor_array *tuples, |
426 | void **dict, u32 *num_entries, size_t entry_size, |
427 | u32 num_entries_token) |
428 | { |
429 | struct snd_soc_tplg_vendor_value_elem *tuple; |
430 | |
431 | /* Dictionary header consists of single tuple - entry count. */ |
432 | tuple = tuples->value; |
433 | if (le32_to_cpu(tuple->token) != num_entries_token) { |
434 | dev_err(comp->dev, "invalid dictionary header, expected: %d\n" , |
435 | num_entries_token); |
436 | return -EINVAL; |
437 | } |
438 | |
439 | *num_entries = le32_to_cpu(tuple->value); |
440 | *dict = devm_kcalloc(dev: comp->card->dev, n: *num_entries, size: entry_size, GFP_KERNEL); |
441 | if (!*dict) |
442 | return -ENOMEM; |
443 | |
444 | return 0; |
445 | } |
446 | |
447 | static int |
448 | parse_dictionary_entries(struct snd_soc_component *comp, |
449 | struct snd_soc_tplg_vendor_array *tuples, u32 block_size, |
450 | void *dict, u32 num_entries, size_t entry_size, |
451 | u32 entry_id_token, |
452 | const struct avs_tplg_token_parser *parsers, size_t num_parsers) |
453 | { |
454 | void *pos = dict; |
455 | int i; |
456 | |
457 | for (i = 0; i < num_entries; i++) { |
458 | u32 esize; |
459 | int ret; |
460 | |
461 | ret = avs_tplg_vendor_entry_size(tuples, block_size, |
462 | entry_id_token, size: &esize); |
463 | if (ret) |
464 | return ret; |
465 | |
466 | ret = avs_parse_tokens(comp, object: pos, parsers, count: num_parsers, tuples, priv_size: esize); |
467 | if (ret < 0) { |
468 | dev_err(comp->dev, "parse entry: %d of type: %d failed: %d\n" , |
469 | i, entry_id_token, ret); |
470 | return ret; |
471 | } |
472 | |
473 | pos += entry_size; |
474 | block_size -= esize; |
475 | tuples = avs_tplg_vendor_array_at(tuples, esize); |
476 | } |
477 | |
478 | return 0; |
479 | } |
480 | |
481 | static int parse_dictionary(struct snd_soc_component *comp, |
482 | struct snd_soc_tplg_vendor_array *tuples, u32 block_size, |
483 | void **dict, u32 *num_entries, size_t entry_size, |
484 | u32 num_entries_token, u32 entry_id_token, |
485 | const struct avs_tplg_token_parser *parsers, size_t num_parsers) |
486 | { |
487 | int ret; |
488 | |
489 | ret = parse_dictionary_header(comp, tuples, dict, num_entries, |
490 | entry_size, num_entries_token); |
491 | if (ret) |
492 | return ret; |
493 | |
494 | block_size -= le32_to_cpu(tuples->size); |
495 | /* With header parsed, move on to parsing entries. */ |
496 | tuples = avs_tplg_vendor_array_next(tuples); |
497 | |
498 | return parse_dictionary_entries(comp, tuples, block_size, dict: *dict, |
499 | num_entries: *num_entries, entry_size, |
500 | entry_id_token, parsers, num_parsers); |
501 | } |
502 | |
503 | static const struct avs_tplg_token_parser library_parsers[] = { |
504 | { |
505 | .token = AVS_TKN_LIBRARY_NAME_STRING, |
506 | .type = SND_SOC_TPLG_TUPLE_TYPE_STRING, |
507 | .offset = offsetof(struct avs_tplg_library, name), |
508 | .parse = avs_parse_string_token, |
509 | }, |
510 | }; |
511 | |
512 | static int avs_tplg_parse_libraries(struct snd_soc_component *comp, |
513 | struct snd_soc_tplg_vendor_array *tuples, u32 block_size) |
514 | { |
515 | struct avs_soc_component *acomp = to_avs_soc_component(comp); |
516 | struct avs_tplg *tplg = acomp->tplg; |
517 | |
518 | return parse_dictionary(comp, tuples, block_size, dict: (void **)&tplg->libs, |
519 | num_entries: &tplg->num_libs, entry_size: sizeof(*tplg->libs), |
520 | num_entries_token: AVS_TKN_MANIFEST_NUM_LIBRARIES_U32, |
521 | entry_id_token: AVS_TKN_LIBRARY_ID_U32, |
522 | parsers: library_parsers, ARRAY_SIZE(library_parsers)); |
523 | } |
524 | |
525 | static const struct avs_tplg_token_parser audio_format_parsers[] = { |
526 | { |
527 | .token = AVS_TKN_AFMT_SAMPLE_RATE_U32, |
528 | .type = SND_SOC_TPLG_TUPLE_TYPE_WORD, |
529 | .offset = offsetof(struct avs_audio_format, sampling_freq), |
530 | .parse = avs_parse_word_token, |
531 | }, |
532 | { |
533 | .token = AVS_TKN_AFMT_BIT_DEPTH_U32, |
534 | .type = SND_SOC_TPLG_TUPLE_TYPE_WORD, |
535 | .offset = offsetof(struct avs_audio_format, bit_depth), |
536 | .parse = avs_parse_word_token, |
537 | }, |
538 | { |
539 | .token = AVS_TKN_AFMT_CHANNEL_MAP_U32, |
540 | .type = SND_SOC_TPLG_TUPLE_TYPE_WORD, |
541 | .offset = offsetof(struct avs_audio_format, channel_map), |
542 | .parse = avs_parse_word_token, |
543 | }, |
544 | { |
545 | .token = AVS_TKN_AFMT_CHANNEL_CFG_U32, |
546 | .type = SND_SOC_TPLG_TUPLE_TYPE_WORD, |
547 | .offset = offsetof(struct avs_audio_format, channel_config), |
548 | .parse = avs_parse_word_token, |
549 | }, |
550 | { |
551 | .token = AVS_TKN_AFMT_INTERLEAVING_U32, |
552 | .type = SND_SOC_TPLG_TUPLE_TYPE_WORD, |
553 | .offset = offsetof(struct avs_audio_format, interleaving), |
554 | .parse = avs_parse_word_token, |
555 | }, |
556 | { |
557 | .token = AVS_TKN_AFMT_NUM_CHANNELS_U32, |
558 | .type = SND_SOC_TPLG_TUPLE_TYPE_WORD, |
559 | .offset = AVS_TKN_AFMT_NUM_CHANNELS_U32, |
560 | .parse = parse_audio_format_bitfield, |
561 | }, |
562 | { |
563 | .token = AVS_TKN_AFMT_VALID_BIT_DEPTH_U32, |
564 | .type = SND_SOC_TPLG_TUPLE_TYPE_WORD, |
565 | .offset = AVS_TKN_AFMT_VALID_BIT_DEPTH_U32, |
566 | .parse = parse_audio_format_bitfield, |
567 | }, |
568 | { |
569 | .token = AVS_TKN_AFMT_SAMPLE_TYPE_U32, |
570 | .type = SND_SOC_TPLG_TUPLE_TYPE_WORD, |
571 | .offset = AVS_TKN_AFMT_SAMPLE_TYPE_U32, |
572 | .parse = parse_audio_format_bitfield, |
573 | }, |
574 | }; |
575 | |
576 | static int avs_tplg_parse_audio_formats(struct snd_soc_component *comp, |
577 | struct snd_soc_tplg_vendor_array *tuples, |
578 | u32 block_size) |
579 | { |
580 | struct avs_soc_component *acomp = to_avs_soc_component(comp); |
581 | struct avs_tplg *tplg = acomp->tplg; |
582 | |
583 | return parse_dictionary(comp, tuples, block_size, dict: (void **)&tplg->fmts, |
584 | num_entries: &tplg->num_fmts, entry_size: sizeof(*tplg->fmts), |
585 | num_entries_token: AVS_TKN_MANIFEST_NUM_AFMTS_U32, |
586 | entry_id_token: AVS_TKN_AFMT_ID_U32, |
587 | parsers: audio_format_parsers, ARRAY_SIZE(audio_format_parsers)); |
588 | } |
589 | |
590 | static const struct avs_tplg_token_parser modcfg_base_parsers[] = { |
591 | { |
592 | .token = AVS_TKN_MODCFG_BASE_CPC_U32, |
593 | .type = SND_SOC_TPLG_TUPLE_TYPE_WORD, |
594 | .offset = offsetof(struct avs_tplg_modcfg_base, cpc), |
595 | .parse = avs_parse_word_token, |
596 | }, |
597 | { |
598 | .token = AVS_TKN_MODCFG_BASE_IBS_U32, |
599 | .type = SND_SOC_TPLG_TUPLE_TYPE_WORD, |
600 | .offset = offsetof(struct avs_tplg_modcfg_base, ibs), |
601 | .parse = avs_parse_word_token, |
602 | }, |
603 | { |
604 | .token = AVS_TKN_MODCFG_BASE_OBS_U32, |
605 | .type = SND_SOC_TPLG_TUPLE_TYPE_WORD, |
606 | .offset = offsetof(struct avs_tplg_modcfg_base, obs), |
607 | .parse = avs_parse_word_token, |
608 | }, |
609 | { |
610 | .token = AVS_TKN_MODCFG_BASE_PAGES_U32, |
611 | .type = SND_SOC_TPLG_TUPLE_TYPE_WORD, |
612 | .offset = offsetof(struct avs_tplg_modcfg_base, is_pages), |
613 | .parse = avs_parse_word_token, |
614 | }, |
615 | }; |
616 | |
617 | static int avs_tplg_parse_modcfgs_base(struct snd_soc_component *comp, |
618 | struct snd_soc_tplg_vendor_array *tuples, |
619 | u32 block_size) |
620 | { |
621 | struct avs_soc_component *acomp = to_avs_soc_component(comp); |
622 | struct avs_tplg *tplg = acomp->tplg; |
623 | |
624 | return parse_dictionary(comp, tuples, block_size, dict: (void **)&tplg->modcfgs_base, |
625 | num_entries: &tplg->num_modcfgs_base, entry_size: sizeof(*tplg->modcfgs_base), |
626 | num_entries_token: AVS_TKN_MANIFEST_NUM_MODCFGS_BASE_U32, |
627 | entry_id_token: AVS_TKN_MODCFG_BASE_ID_U32, |
628 | parsers: modcfg_base_parsers, ARRAY_SIZE(modcfg_base_parsers)); |
629 | } |
630 | |
631 | static const struct avs_tplg_token_parser modcfg_ext_parsers[] = { |
632 | { |
633 | .token = AVS_TKN_MODCFG_EXT_TYPE_UUID, |
634 | .type = SND_SOC_TPLG_TUPLE_TYPE_UUID, |
635 | .offset = offsetof(struct avs_tplg_modcfg_ext, type), |
636 | .parse = avs_parse_uuid_token, |
637 | }, |
638 | { |
639 | .token = AVS_TKN_MODCFG_CPR_OUT_AFMT_ID_U32, |
640 | .type = SND_SOC_TPLG_TUPLE_TYPE_WORD, |
641 | .offset = offsetof(struct avs_tplg_modcfg_ext, copier.out_fmt), |
642 | .parse = avs_parse_audio_format_ptr, |
643 | }, |
644 | { |
645 | .token = AVS_TKN_MODCFG_CPR_FEATURE_MASK_U32, |
646 | .type = SND_SOC_TPLG_TUPLE_TYPE_WORD, |
647 | .offset = offsetof(struct avs_tplg_modcfg_ext, copier.feature_mask), |
648 | .parse = avs_parse_word_token, |
649 | }, |
650 | { |
651 | .token = AVS_TKN_MODCFG_CPR_VINDEX_U8, |
652 | .type = SND_SOC_TPLG_TUPLE_TYPE_BYTE, |
653 | .offset = offsetof(struct avs_tplg_modcfg_ext, copier.vindex), |
654 | .parse = avs_parse_byte_token, |
655 | }, |
656 | { |
657 | .token = AVS_TKN_MODCFG_CPR_DMA_TYPE_U32, |
658 | .type = SND_SOC_TPLG_TUPLE_TYPE_WORD, |
659 | .offset = offsetof(struct avs_tplg_modcfg_ext, copier.dma_type), |
660 | .parse = avs_parse_word_token, |
661 | }, |
662 | { |
663 | .token = AVS_TKN_MODCFG_CPR_DMABUFF_SIZE_U32, |
664 | .type = SND_SOC_TPLG_TUPLE_TYPE_WORD, |
665 | .offset = offsetof(struct avs_tplg_modcfg_ext, copier.dma_buffer_size), |
666 | .parse = avs_parse_word_token, |
667 | }, |
668 | { |
669 | .token = AVS_TKN_MODCFG_CPR_BLOB_FMT_ID_U32, |
670 | .type = SND_SOC_TPLG_TUPLE_TYPE_WORD, |
671 | .offset = offsetof(struct avs_tplg_modcfg_ext, copier.blob_fmt), |
672 | .parse = avs_parse_audio_format_ptr, |
673 | }, |
674 | { |
675 | .token = AVS_TKN_MODCFG_MICSEL_OUT_AFMT_ID_U32, |
676 | .type = SND_SOC_TPLG_TUPLE_TYPE_WORD, |
677 | .offset = offsetof(struct avs_tplg_modcfg_ext, micsel.out_fmt), |
678 | .parse = avs_parse_audio_format_ptr, |
679 | }, |
680 | { |
681 | .token = AVS_TKN_MODCFG_INTELWOV_CPC_LP_MODE_U32, |
682 | .type = SND_SOC_TPLG_TUPLE_TYPE_WORD, |
683 | .offset = offsetof(struct avs_tplg_modcfg_ext, wov.cpc_lp_mode), |
684 | .parse = avs_parse_word_token, |
685 | }, |
686 | { |
687 | .token = AVS_TKN_MODCFG_SRC_OUT_FREQ_U32, |
688 | .type = SND_SOC_TPLG_TUPLE_TYPE_WORD, |
689 | .offset = offsetof(struct avs_tplg_modcfg_ext, src.out_freq), |
690 | .parse = avs_parse_word_token, |
691 | }, |
692 | { |
693 | .token = AVS_TKN_MODCFG_MUX_REF_AFMT_ID_U32, |
694 | .type = SND_SOC_TPLG_TUPLE_TYPE_WORD, |
695 | .offset = offsetof(struct avs_tplg_modcfg_ext, mux.ref_fmt), |
696 | .parse = avs_parse_audio_format_ptr, |
697 | }, |
698 | { |
699 | .token = AVS_TKN_MODCFG_MUX_OUT_AFMT_ID_U32, |
700 | .type = SND_SOC_TPLG_TUPLE_TYPE_WORD, |
701 | .offset = offsetof(struct avs_tplg_modcfg_ext, mux.out_fmt), |
702 | .parse = avs_parse_audio_format_ptr, |
703 | }, |
704 | { |
705 | .token = AVS_TKN_MODCFG_AEC_REF_AFMT_ID_U32, |
706 | .type = SND_SOC_TPLG_TUPLE_TYPE_WORD, |
707 | .offset = offsetof(struct avs_tplg_modcfg_ext, aec.ref_fmt), |
708 | .parse = avs_parse_audio_format_ptr, |
709 | }, |
710 | { |
711 | .token = AVS_TKN_MODCFG_AEC_OUT_AFMT_ID_U32, |
712 | .type = SND_SOC_TPLG_TUPLE_TYPE_WORD, |
713 | .offset = offsetof(struct avs_tplg_modcfg_ext, aec.out_fmt), |
714 | .parse = avs_parse_audio_format_ptr, |
715 | }, |
716 | { |
717 | .token = AVS_TKN_MODCFG_AEC_CPC_LP_MODE_U32, |
718 | .type = SND_SOC_TPLG_TUPLE_TYPE_WORD, |
719 | .offset = offsetof(struct avs_tplg_modcfg_ext, aec.cpc_lp_mode), |
720 | .parse = avs_parse_word_token, |
721 | }, |
722 | { |
723 | .token = AVS_TKN_MODCFG_ASRC_OUT_FREQ_U32, |
724 | .type = SND_SOC_TPLG_TUPLE_TYPE_WORD, |
725 | .offset = offsetof(struct avs_tplg_modcfg_ext, asrc.out_freq), |
726 | .parse = avs_parse_word_token, |
727 | }, |
728 | { |
729 | .token = AVS_TKN_MODCFG_ASRC_MODE_U8, |
730 | .type = SND_SOC_TPLG_TUPLE_TYPE_BYTE, |
731 | .offset = offsetof(struct avs_tplg_modcfg_ext, asrc.mode), |
732 | .parse = avs_parse_byte_token, |
733 | }, |
734 | { |
735 | .token = AVS_TKN_MODCFG_ASRC_DISABLE_JITTER_U8, |
736 | .type = SND_SOC_TPLG_TUPLE_TYPE_BYTE, |
737 | .offset = offsetof(struct avs_tplg_modcfg_ext, asrc.disable_jitter_buffer), |
738 | .parse = avs_parse_byte_token, |
739 | }, |
740 | { |
741 | .token = AVS_TKN_MODCFG_UPDOWN_MIX_OUT_CHAN_CFG_U32, |
742 | .type = SND_SOC_TPLG_TUPLE_TYPE_WORD, |
743 | .offset = offsetof(struct avs_tplg_modcfg_ext, updown_mix.out_channel_config), |
744 | .parse = avs_parse_word_token, |
745 | }, |
746 | { |
747 | .token = AVS_TKN_MODCFG_UPDOWN_MIX_COEFF_SELECT_U32, |
748 | .type = SND_SOC_TPLG_TUPLE_TYPE_WORD, |
749 | .offset = offsetof(struct avs_tplg_modcfg_ext, updown_mix.coefficients_select), |
750 | .parse = avs_parse_word_token, |
751 | }, |
752 | { |
753 | .token = AVS_TKN_MODCFG_UPDOWN_MIX_COEFF_0_S32, |
754 | .type = SND_SOC_TPLG_TUPLE_TYPE_WORD, |
755 | .offset = offsetof(struct avs_tplg_modcfg_ext, updown_mix.coefficients[0]), |
756 | .parse = avs_parse_word_token, |
757 | }, |
758 | { |
759 | .token = AVS_TKN_MODCFG_UPDOWN_MIX_COEFF_1_S32, |
760 | .type = SND_SOC_TPLG_TUPLE_TYPE_WORD, |
761 | .offset = offsetof(struct avs_tplg_modcfg_ext, updown_mix.coefficients[1]), |
762 | .parse = avs_parse_word_token, |
763 | }, |
764 | { |
765 | .token = AVS_TKN_MODCFG_UPDOWN_MIX_COEFF_2_S32, |
766 | .type = SND_SOC_TPLG_TUPLE_TYPE_WORD, |
767 | .offset = offsetof(struct avs_tplg_modcfg_ext, updown_mix.coefficients[2]), |
768 | .parse = avs_parse_word_token, |
769 | }, |
770 | { |
771 | .token = AVS_TKN_MODCFG_UPDOWN_MIX_COEFF_3_S32, |
772 | .type = SND_SOC_TPLG_TUPLE_TYPE_WORD, |
773 | .offset = offsetof(struct avs_tplg_modcfg_ext, updown_mix.coefficients[3]), |
774 | .parse = avs_parse_word_token, |
775 | }, |
776 | { |
777 | .token = AVS_TKN_MODCFG_UPDOWN_MIX_COEFF_4_S32, |
778 | .type = SND_SOC_TPLG_TUPLE_TYPE_WORD, |
779 | .offset = offsetof(struct avs_tplg_modcfg_ext, updown_mix.coefficients[4]), |
780 | .parse = avs_parse_word_token, |
781 | }, |
782 | { |
783 | .token = AVS_TKN_MODCFG_UPDOWN_MIX_COEFF_5_S32, |
784 | .type = SND_SOC_TPLG_TUPLE_TYPE_WORD, |
785 | .offset = offsetof(struct avs_tplg_modcfg_ext, updown_mix.coefficients[5]), |
786 | .parse = avs_parse_word_token, |
787 | }, |
788 | { |
789 | .token = AVS_TKN_MODCFG_UPDOWN_MIX_COEFF_6_S32, |
790 | .type = SND_SOC_TPLG_TUPLE_TYPE_WORD, |
791 | .offset = offsetof(struct avs_tplg_modcfg_ext, updown_mix.coefficients[6]), |
792 | .parse = avs_parse_word_token, |
793 | }, |
794 | { |
795 | .token = AVS_TKN_MODCFG_UPDOWN_MIX_COEFF_7_S32, |
796 | .type = SND_SOC_TPLG_TUPLE_TYPE_WORD, |
797 | .offset = offsetof(struct avs_tplg_modcfg_ext, updown_mix.coefficients[7]), |
798 | .parse = avs_parse_word_token, |
799 | }, |
800 | { |
801 | .token = AVS_TKN_MODCFG_UPDOWN_MIX_CHAN_MAP_U32, |
802 | .type = SND_SOC_TPLG_TUPLE_TYPE_WORD, |
803 | .offset = offsetof(struct avs_tplg_modcfg_ext, updown_mix.channel_map), |
804 | .parse = avs_parse_word_token, |
805 | }, |
806 | { |
807 | .token = AVS_TKN_MODCFG_EXT_NUM_INPUT_PINS_U16, |
808 | .type = SND_SOC_TPLG_TUPLE_TYPE_SHORT, |
809 | .offset = offsetof(struct avs_tplg_modcfg_ext, generic.num_input_pins), |
810 | .parse = avs_parse_short_token, |
811 | }, |
812 | { |
813 | .token = AVS_TKN_MODCFG_EXT_NUM_OUTPUT_PINS_U16, |
814 | .type = SND_SOC_TPLG_TUPLE_TYPE_SHORT, |
815 | .offset = offsetof(struct avs_tplg_modcfg_ext, generic.num_output_pins), |
816 | .parse = avs_parse_short_token, |
817 | }, |
818 | }; |
819 | |
820 | static const struct avs_tplg_token_parser pin_format_parsers[] = { |
821 | { |
822 | .token = AVS_TKN_PIN_FMT_INDEX_U32, |
823 | .type = SND_SOC_TPLG_TUPLE_TYPE_WORD, |
824 | .offset = offsetof(struct avs_tplg_pin_format, pin_index), |
825 | .parse = avs_parse_word_token, |
826 | }, |
827 | { |
828 | .token = AVS_TKN_PIN_FMT_IOBS_U32, |
829 | .type = SND_SOC_TPLG_TUPLE_TYPE_WORD, |
830 | .offset = offsetof(struct avs_tplg_pin_format, iobs), |
831 | .parse = avs_parse_word_token, |
832 | }, |
833 | { |
834 | .token = AVS_TKN_PIN_FMT_AFMT_ID_U32, |
835 | .type = SND_SOC_TPLG_TUPLE_TYPE_WORD, |
836 | .offset = offsetof(struct avs_tplg_pin_format, fmt), |
837 | .parse = avs_parse_audio_format_ptr, |
838 | }, |
839 | }; |
840 | |
841 | static void |
842 | assign_copier_gtw_instance(struct snd_soc_component *comp, struct avs_tplg_modcfg_ext *cfg) |
843 | { |
844 | struct snd_soc_acpi_mach *mach; |
845 | int ssp_port, tdm_slot; |
846 | |
847 | if (!guid_equal(u1: &cfg->type, u2: &AVS_COPIER_MOD_UUID)) |
848 | return; |
849 | |
850 | /* Only I2S boards assign port instance in ->i2s_link_mask. */ |
851 | switch (cfg->copier.dma_type) { |
852 | case AVS_DMA_I2S_LINK_OUTPUT: |
853 | case AVS_DMA_I2S_LINK_INPUT: |
854 | break; |
855 | default: |
856 | return; |
857 | } |
858 | |
859 | /* If topology sets value don't overwrite it */ |
860 | if (cfg->copier.vindex.val) |
861 | return; |
862 | |
863 | mach = dev_get_platdata(dev: comp->card->dev); |
864 | |
865 | if (!avs_mach_singular_ssp(mach)) |
866 | return; |
867 | ssp_port = avs_mach_ssp_port(mach); |
868 | |
869 | if (!avs_mach_singular_tdm(mach, port: ssp_port)) |
870 | return; |
871 | tdm_slot = avs_mach_ssp_tdm(mach, port: ssp_port); |
872 | |
873 | cfg->copier.vindex.i2s.instance = ssp_port; |
874 | cfg->copier.vindex.i2s.time_slot = tdm_slot; |
875 | } |
876 | |
877 | static int avs_tplg_parse_modcfg_ext(struct snd_soc_component *comp, |
878 | struct avs_tplg_modcfg_ext *cfg, |
879 | struct snd_soc_tplg_vendor_array *tuples, |
880 | u32 block_size) |
881 | { |
882 | u32 esize; |
883 | int ret; |
884 | |
885 | /* See where pin block starts. */ |
886 | ret = avs_tplg_vendor_entry_size(tuples, block_size, |
887 | entry_id_token: AVS_TKN_PIN_FMT_INDEX_U32, size: &esize); |
888 | if (ret) |
889 | return ret; |
890 | |
891 | ret = avs_parse_tokens(comp, object: cfg, parsers: modcfg_ext_parsers, |
892 | ARRAY_SIZE(modcfg_ext_parsers), tuples, priv_size: esize); |
893 | if (ret) |
894 | return ret; |
895 | |
896 | /* Update copier gateway based on board's i2s_link_mask. */ |
897 | assign_copier_gtw_instance(comp, cfg); |
898 | |
899 | block_size -= esize; |
900 | /* Parse trailing in/out pin formats if any. */ |
901 | if (block_size) { |
902 | struct avs_tplg_pin_format *pins; |
903 | u32 num_pins; |
904 | |
905 | num_pins = cfg->generic.num_input_pins + cfg->generic.num_output_pins; |
906 | if (!num_pins) |
907 | return -EINVAL; |
908 | |
909 | pins = devm_kcalloc(dev: comp->card->dev, n: num_pins, size: sizeof(*pins), GFP_KERNEL); |
910 | if (!pins) |
911 | return -ENOMEM; |
912 | |
913 | tuples = avs_tplg_vendor_array_at(tuples, esize); |
914 | ret = parse_dictionary_entries(comp, tuples, block_size, |
915 | dict: pins, num_entries: num_pins, entry_size: sizeof(*pins), |
916 | entry_id_token: AVS_TKN_PIN_FMT_INDEX_U32, |
917 | parsers: pin_format_parsers, |
918 | ARRAY_SIZE(pin_format_parsers)); |
919 | if (ret) |
920 | return ret; |
921 | cfg->generic.pin_fmts = pins; |
922 | } |
923 | |
924 | return 0; |
925 | } |
926 | |
927 | static int avs_tplg_parse_modcfgs_ext(struct snd_soc_component *comp, |
928 | struct snd_soc_tplg_vendor_array *tuples, |
929 | u32 block_size) |
930 | { |
931 | struct avs_soc_component *acomp = to_avs_soc_component(comp); |
932 | struct avs_tplg *tplg = acomp->tplg; |
933 | int ret, i; |
934 | |
935 | ret = parse_dictionary_header(comp, tuples, dict: (void **)&tplg->modcfgs_ext, |
936 | num_entries: &tplg->num_modcfgs_ext, |
937 | entry_size: sizeof(*tplg->modcfgs_ext), |
938 | num_entries_token: AVS_TKN_MANIFEST_NUM_MODCFGS_EXT_U32); |
939 | if (ret) |
940 | return ret; |
941 | |
942 | block_size -= le32_to_cpu(tuples->size); |
943 | /* With header parsed, move on to parsing entries. */ |
944 | tuples = avs_tplg_vendor_array_next(tuples); |
945 | |
946 | for (i = 0; i < tplg->num_modcfgs_ext; i++) { |
947 | struct avs_tplg_modcfg_ext *cfg = &tplg->modcfgs_ext[i]; |
948 | u32 esize; |
949 | |
950 | ret = avs_tplg_vendor_entry_size(tuples, block_size, |
951 | entry_id_token: AVS_TKN_MODCFG_EXT_ID_U32, size: &esize); |
952 | if (ret) |
953 | return ret; |
954 | |
955 | ret = avs_tplg_parse_modcfg_ext(comp, cfg, tuples, block_size: esize); |
956 | if (ret) |
957 | return ret; |
958 | |
959 | block_size -= esize; |
960 | tuples = avs_tplg_vendor_array_at(tuples, esize); |
961 | } |
962 | |
963 | return 0; |
964 | } |
965 | |
966 | static const struct avs_tplg_token_parser pplcfg_parsers[] = { |
967 | { |
968 | .token = AVS_TKN_PPLCFG_REQ_SIZE_U16, |
969 | .type = SND_SOC_TPLG_TUPLE_TYPE_SHORT, |
970 | .offset = offsetof(struct avs_tplg_pplcfg, req_size), |
971 | .parse = avs_parse_short_token, |
972 | }, |
973 | { |
974 | .token = AVS_TKN_PPLCFG_PRIORITY_U8, |
975 | .type = SND_SOC_TPLG_TUPLE_TYPE_BYTE, |
976 | .offset = offsetof(struct avs_tplg_pplcfg, priority), |
977 | .parse = avs_parse_byte_token, |
978 | }, |
979 | { |
980 | .token = AVS_TKN_PPLCFG_LOW_POWER_BOOL, |
981 | .type = SND_SOC_TPLG_TUPLE_TYPE_BOOL, |
982 | .offset = offsetof(struct avs_tplg_pplcfg, lp), |
983 | .parse = avs_parse_bool_token, |
984 | }, |
985 | { |
986 | .token = AVS_TKN_PPLCFG_ATTRIBUTES_U16, |
987 | .type = SND_SOC_TPLG_TUPLE_TYPE_SHORT, |
988 | .offset = offsetof(struct avs_tplg_pplcfg, attributes), |
989 | .parse = avs_parse_short_token, |
990 | }, |
991 | { |
992 | .token = AVS_TKN_PPLCFG_TRIGGER_U32, |
993 | .type = SND_SOC_TPLG_TUPLE_TYPE_WORD, |
994 | .offset = offsetof(struct avs_tplg_pplcfg, trigger), |
995 | .parse = avs_parse_word_token, |
996 | }, |
997 | }; |
998 | |
999 | static int avs_tplg_parse_pplcfgs(struct snd_soc_component *comp, |
1000 | struct snd_soc_tplg_vendor_array *tuples, |
1001 | u32 block_size) |
1002 | { |
1003 | struct avs_soc_component *acomp = to_avs_soc_component(comp); |
1004 | struct avs_tplg *tplg = acomp->tplg; |
1005 | |
1006 | return parse_dictionary(comp, tuples, block_size, dict: (void **)&tplg->pplcfgs, |
1007 | num_entries: &tplg->num_pplcfgs, entry_size: sizeof(*tplg->pplcfgs), |
1008 | num_entries_token: AVS_TKN_MANIFEST_NUM_PPLCFGS_U32, |
1009 | entry_id_token: AVS_TKN_PPLCFG_ID_U32, |
1010 | parsers: pplcfg_parsers, ARRAY_SIZE(pplcfg_parsers)); |
1011 | } |
1012 | |
1013 | static const struct avs_tplg_token_parser binding_parsers[] = { |
1014 | { |
1015 | .token = AVS_TKN_BINDING_TARGET_TPLG_NAME_STRING, |
1016 | .type = SND_SOC_TPLG_TUPLE_TYPE_STRING, |
1017 | .offset = offsetof(struct avs_tplg_binding, target_tplg_name), |
1018 | .parse = parse_link_formatted_string, |
1019 | }, |
1020 | { |
1021 | .token = AVS_TKN_BINDING_TARGET_PATH_TMPL_ID_U32, |
1022 | .type = SND_SOC_TPLG_TUPLE_TYPE_WORD, |
1023 | .offset = offsetof(struct avs_tplg_binding, target_path_tmpl_id), |
1024 | .parse = avs_parse_word_token, |
1025 | }, |
1026 | { |
1027 | .token = AVS_TKN_BINDING_TARGET_PPL_ID_U32, |
1028 | .type = SND_SOC_TPLG_TUPLE_TYPE_WORD, |
1029 | .offset = offsetof(struct avs_tplg_binding, target_ppl_id), |
1030 | .parse = avs_parse_word_token, |
1031 | }, |
1032 | { |
1033 | .token = AVS_TKN_BINDING_TARGET_MOD_ID_U32, |
1034 | .type = SND_SOC_TPLG_TUPLE_TYPE_WORD, |
1035 | .offset = offsetof(struct avs_tplg_binding, target_mod_id), |
1036 | .parse = avs_parse_word_token, |
1037 | }, |
1038 | { |
1039 | .token = AVS_TKN_BINDING_TARGET_MOD_PIN_U8, |
1040 | .type = SND_SOC_TPLG_TUPLE_TYPE_BYTE, |
1041 | .offset = offsetof(struct avs_tplg_binding, target_mod_pin), |
1042 | .parse = avs_parse_byte_token, |
1043 | }, |
1044 | { |
1045 | .token = AVS_TKN_BINDING_MOD_ID_U32, |
1046 | .type = SND_SOC_TPLG_TUPLE_TYPE_WORD, |
1047 | .offset = offsetof(struct avs_tplg_binding, mod_id), |
1048 | .parse = avs_parse_word_token, |
1049 | }, |
1050 | { |
1051 | .token = AVS_TKN_BINDING_MOD_PIN_U8, |
1052 | .type = SND_SOC_TPLG_TUPLE_TYPE_BYTE, |
1053 | .offset = offsetof(struct avs_tplg_binding, mod_pin), |
1054 | .parse = avs_parse_byte_token, |
1055 | }, |
1056 | { |
1057 | .token = AVS_TKN_BINDING_IS_SINK_U8, |
1058 | .type = SND_SOC_TPLG_TUPLE_TYPE_BYTE, |
1059 | .offset = offsetof(struct avs_tplg_binding, is_sink), |
1060 | .parse = avs_parse_byte_token, |
1061 | }, |
1062 | }; |
1063 | |
1064 | static int avs_tplg_parse_bindings(struct snd_soc_component *comp, |
1065 | struct snd_soc_tplg_vendor_array *tuples, |
1066 | u32 block_size) |
1067 | { |
1068 | struct avs_soc_component *acomp = to_avs_soc_component(comp); |
1069 | struct avs_tplg *tplg = acomp->tplg; |
1070 | |
1071 | return parse_dictionary(comp, tuples, block_size, dict: (void **)&tplg->bindings, |
1072 | num_entries: &tplg->num_bindings, entry_size: sizeof(*tplg->bindings), |
1073 | num_entries_token: AVS_TKN_MANIFEST_NUM_BINDINGS_U32, |
1074 | entry_id_token: AVS_TKN_BINDING_ID_U32, |
1075 | parsers: binding_parsers, ARRAY_SIZE(binding_parsers)); |
1076 | } |
1077 | |
1078 | static const struct avs_tplg_token_parser module_parsers[] = { |
1079 | { |
1080 | .token = AVS_TKN_MOD_ID_U32, |
1081 | .type = SND_SOC_TPLG_TUPLE_TYPE_WORD, |
1082 | .offset = offsetof(struct avs_tplg_module, id), |
1083 | .parse = avs_parse_word_token, |
1084 | }, |
1085 | { |
1086 | .token = AVS_TKN_MOD_MODCFG_BASE_ID_U32, |
1087 | .type = SND_SOC_TPLG_TUPLE_TYPE_WORD, |
1088 | .offset = offsetof(struct avs_tplg_module, cfg_base), |
1089 | .parse = avs_parse_modcfg_base_ptr, |
1090 | }, |
1091 | { |
1092 | .token = AVS_TKN_MOD_IN_AFMT_ID_U32, |
1093 | .type = SND_SOC_TPLG_TUPLE_TYPE_WORD, |
1094 | .offset = offsetof(struct avs_tplg_module, in_fmt), |
1095 | .parse = avs_parse_audio_format_ptr, |
1096 | }, |
1097 | { |
1098 | .token = AVS_TKN_MOD_CORE_ID_U8, |
1099 | .type = SND_SOC_TPLG_TUPLE_TYPE_BYTE, |
1100 | .offset = offsetof(struct avs_tplg_module, core_id), |
1101 | .parse = avs_parse_byte_token, |
1102 | }, |
1103 | { |
1104 | .token = AVS_TKN_MOD_PROC_DOMAIN_U8, |
1105 | .type = SND_SOC_TPLG_TUPLE_TYPE_BYTE, |
1106 | .offset = offsetof(struct avs_tplg_module, domain), |
1107 | .parse = avs_parse_byte_token, |
1108 | }, |
1109 | { |
1110 | .token = AVS_TKN_MOD_MODCFG_EXT_ID_U32, |
1111 | .type = SND_SOC_TPLG_TUPLE_TYPE_WORD, |
1112 | .offset = offsetof(struct avs_tplg_module, cfg_ext), |
1113 | .parse = avs_parse_modcfg_ext_ptr, |
1114 | }, |
1115 | { |
1116 | .token = AVS_TKN_MOD_KCONTROL_ID_U32, |
1117 | .type = SND_SOC_TPLG_TUPLE_TYPE_WORD, |
1118 | .offset = offsetof(struct avs_tplg_module, ctl_id), |
1119 | .parse = avs_parse_byte_token, |
1120 | }, |
1121 | { |
1122 | .token = AVS_TKN_MOD_INIT_CONFIG_NUM_IDS_U32, |
1123 | .type = SND_SOC_TPLG_TUPLE_TYPE_WORD, |
1124 | .offset = offsetof(struct avs_tplg_module, num_config_ids), |
1125 | .parse = avs_parse_byte_token, |
1126 | }, |
1127 | }; |
1128 | |
1129 | static const struct avs_tplg_token_parser init_config_parsers[] = { |
1130 | { |
1131 | .token = AVS_TKN_MOD_INIT_CONFIG_ID_U32, |
1132 | .type = SND_SOC_TPLG_TUPLE_TYPE_WORD, |
1133 | .offset = 0, |
1134 | .parse = avs_parse_word_token, |
1135 | }, |
1136 | }; |
1137 | |
1138 | static struct avs_tplg_module * |
1139 | avs_tplg_module_create(struct snd_soc_component *comp, struct avs_tplg_pipeline *owner, |
1140 | struct snd_soc_tplg_vendor_array *tuples, u32 block_size) |
1141 | { |
1142 | struct avs_tplg_module *module; |
1143 | u32 esize; |
1144 | int ret; |
1145 | |
1146 | /* See where config id block starts. */ |
1147 | ret = avs_tplg_vendor_entry_size(tuples, block_size, |
1148 | entry_id_token: AVS_TKN_MOD_INIT_CONFIG_ID_U32, size: &esize); |
1149 | if (ret) |
1150 | return ERR_PTR(error: ret); |
1151 | |
1152 | module = devm_kzalloc(dev: comp->card->dev, size: sizeof(*module), GFP_KERNEL); |
1153 | if (!module) |
1154 | return ERR_PTR(error: -ENOMEM); |
1155 | |
1156 | ret = avs_parse_tokens(comp, object: module, parsers: module_parsers, |
1157 | ARRAY_SIZE(module_parsers), tuples, priv_size: esize); |
1158 | if (ret < 0) |
1159 | return ERR_PTR(error: ret); |
1160 | |
1161 | block_size -= esize; |
1162 | /* Parse trailing config ids if any. */ |
1163 | if (block_size) { |
1164 | u32 num_config_ids = module->num_config_ids; |
1165 | u32 *config_ids; |
1166 | |
1167 | if (!num_config_ids) |
1168 | return ERR_PTR(error: -EINVAL); |
1169 | |
1170 | config_ids = devm_kcalloc(dev: comp->card->dev, n: num_config_ids, size: sizeof(*config_ids), |
1171 | GFP_KERNEL); |
1172 | if (!config_ids) |
1173 | return ERR_PTR(error: -ENOMEM); |
1174 | |
1175 | tuples = avs_tplg_vendor_array_at(tuples, esize); |
1176 | ret = parse_dictionary_entries(comp, tuples, block_size, |
1177 | dict: config_ids, num_entries: num_config_ids, entry_size: sizeof(*config_ids), |
1178 | entry_id_token: AVS_TKN_MOD_INIT_CONFIG_ID_U32, |
1179 | parsers: init_config_parsers, |
1180 | ARRAY_SIZE(init_config_parsers)); |
1181 | if (ret) |
1182 | return ERR_PTR(error: ret); |
1183 | |
1184 | module->config_ids = config_ids; |
1185 | } |
1186 | |
1187 | module->owner = owner; |
1188 | INIT_LIST_HEAD(list: &module->node); |
1189 | |
1190 | return module; |
1191 | } |
1192 | |
1193 | static const struct avs_tplg_token_parser pipeline_parsers[] = { |
1194 | { |
1195 | .token = AVS_TKN_PPL_ID_U32, |
1196 | .type = SND_SOC_TPLG_TUPLE_TYPE_WORD, |
1197 | .offset = offsetof(struct avs_tplg_pipeline, id), |
1198 | .parse = avs_parse_word_token, |
1199 | }, |
1200 | { |
1201 | .token = AVS_TKN_PPL_PPLCFG_ID_U32, |
1202 | .type = SND_SOC_TPLG_TUPLE_TYPE_WORD, |
1203 | .offset = offsetof(struct avs_tplg_pipeline, cfg), |
1204 | .parse = avs_parse_pplcfg_ptr, |
1205 | }, |
1206 | { |
1207 | .token = AVS_TKN_PPL_NUM_BINDING_IDS_U32, |
1208 | .type = SND_SOC_TPLG_TUPLE_TYPE_WORD, |
1209 | .offset = offsetof(struct avs_tplg_pipeline, num_bindings), |
1210 | .parse = avs_parse_word_token, |
1211 | }, |
1212 | }; |
1213 | |
1214 | static const struct avs_tplg_token_parser bindings_parsers[] = { |
1215 | { |
1216 | .token = AVS_TKN_PPL_BINDING_ID_U32, |
1217 | .type = SND_SOC_TPLG_TUPLE_TYPE_WORD, |
1218 | .offset = 0, /* to treat pipeline->bindings as dictionary */ |
1219 | .parse = avs_parse_binding_ptr, |
1220 | }, |
1221 | }; |
1222 | |
1223 | static struct avs_tplg_pipeline * |
1224 | avs_tplg_pipeline_create(struct snd_soc_component *comp, struct avs_tplg_path *owner, |
1225 | struct snd_soc_tplg_vendor_array *tuples, u32 block_size) |
1226 | { |
1227 | struct avs_tplg_pipeline *pipeline; |
1228 | u32 modblk_size, offset; |
1229 | int ret; |
1230 | |
1231 | pipeline = devm_kzalloc(dev: comp->card->dev, size: sizeof(*pipeline), GFP_KERNEL); |
1232 | if (!pipeline) |
1233 | return ERR_PTR(error: -ENOMEM); |
1234 | |
1235 | pipeline->owner = owner; |
1236 | INIT_LIST_HEAD(list: &pipeline->mod_list); |
1237 | |
1238 | /* Pipeline header MUST be followed by at least one module. */ |
1239 | ret = avs_tplg_vendor_array_lookup(tuples, block_size, |
1240 | token: AVS_TKN_MOD_ID_U32, offset: &offset); |
1241 | if (!ret && !offset) |
1242 | ret = -EINVAL; |
1243 | if (ret) |
1244 | return ERR_PTR(error: ret); |
1245 | |
1246 | /* Process header which precedes module sections. */ |
1247 | ret = avs_parse_tokens(comp, object: pipeline, parsers: pipeline_parsers, |
1248 | ARRAY_SIZE(pipeline_parsers), tuples, priv_size: offset); |
1249 | if (ret < 0) |
1250 | return ERR_PTR(error: ret); |
1251 | |
1252 | block_size -= offset; |
1253 | tuples = avs_tplg_vendor_array_at(tuples, offset); |
1254 | |
1255 | /* Optionally, binding sections follow module ones. */ |
1256 | ret = avs_tplg_vendor_array_lookup_next(tuples, block_size, |
1257 | token: AVS_TKN_PPL_BINDING_ID_U32, offset: &offset); |
1258 | if (ret) { |
1259 | if (ret != -ENOENT) |
1260 | return ERR_PTR(error: ret); |
1261 | |
1262 | /* Does header information match actual block layout? */ |
1263 | if (pipeline->num_bindings) |
1264 | return ERR_PTR(error: -EINVAL); |
1265 | |
1266 | modblk_size = block_size; |
1267 | } else { |
1268 | pipeline->bindings = devm_kcalloc(dev: comp->card->dev, n: pipeline->num_bindings, |
1269 | size: sizeof(*pipeline->bindings), GFP_KERNEL); |
1270 | if (!pipeline->bindings) |
1271 | return ERR_PTR(error: -ENOMEM); |
1272 | |
1273 | modblk_size = offset; |
1274 | } |
1275 | |
1276 | block_size -= modblk_size; |
1277 | do { |
1278 | struct avs_tplg_module *module; |
1279 | u32 esize; |
1280 | |
1281 | ret = avs_tplg_vendor_entry_size(tuples, block_size: modblk_size, |
1282 | entry_id_token: AVS_TKN_MOD_ID_U32, size: &esize); |
1283 | if (ret) |
1284 | return ERR_PTR(error: ret); |
1285 | |
1286 | module = avs_tplg_module_create(comp, owner: pipeline, tuples, block_size: esize); |
1287 | if (IS_ERR(ptr: module)) { |
1288 | dev_err(comp->dev, "parse module failed: %ld\n" , |
1289 | PTR_ERR(module)); |
1290 | return ERR_CAST(ptr: module); |
1291 | } |
1292 | |
1293 | list_add_tail(new: &module->node, head: &pipeline->mod_list); |
1294 | modblk_size -= esize; |
1295 | tuples = avs_tplg_vendor_array_at(tuples, esize); |
1296 | } while (modblk_size > 0); |
1297 | |
1298 | /* What's left is optional range of bindings. */ |
1299 | ret = parse_dictionary_entries(comp, tuples, block_size, dict: pipeline->bindings, |
1300 | num_entries: pipeline->num_bindings, entry_size: sizeof(*pipeline->bindings), |
1301 | entry_id_token: AVS_TKN_PPL_BINDING_ID_U32, |
1302 | parsers: bindings_parsers, ARRAY_SIZE(bindings_parsers)); |
1303 | if (ret) |
1304 | return ERR_PTR(error: ret); |
1305 | |
1306 | return pipeline; |
1307 | } |
1308 | |
1309 | static const struct avs_tplg_token_parser path_parsers[] = { |
1310 | { |
1311 | .token = AVS_TKN_PATH_ID_U32, |
1312 | .type = SND_SOC_TPLG_TUPLE_TYPE_WORD, |
1313 | .offset = offsetof(struct avs_tplg_path, id), |
1314 | .parse = avs_parse_word_token, |
1315 | }, |
1316 | { |
1317 | .token = AVS_TKN_PATH_FE_FMT_ID_U32, |
1318 | .type = SND_SOC_TPLG_TUPLE_TYPE_WORD, |
1319 | .offset = offsetof(struct avs_tplg_path, fe_fmt), |
1320 | .parse = avs_parse_audio_format_ptr, |
1321 | }, |
1322 | { |
1323 | .token = AVS_TKN_PATH_BE_FMT_ID_U32, |
1324 | .type = SND_SOC_TPLG_TUPLE_TYPE_WORD, |
1325 | .offset = offsetof(struct avs_tplg_path, be_fmt), |
1326 | .parse = avs_parse_audio_format_ptr, |
1327 | }, |
1328 | }; |
1329 | |
1330 | static struct avs_tplg_path * |
1331 | avs_tplg_path_create(struct snd_soc_component *comp, struct avs_tplg_path_template *owner, |
1332 | struct snd_soc_tplg_vendor_array *tuples, u32 block_size, |
1333 | const struct avs_tplg_token_parser *parsers, u32 num_parsers) |
1334 | { |
1335 | struct avs_tplg_pipeline *pipeline; |
1336 | struct avs_tplg_path *path; |
1337 | u32 offset; |
1338 | int ret; |
1339 | |
1340 | path = devm_kzalloc(dev: comp->card->dev, size: sizeof(*path), GFP_KERNEL); |
1341 | if (!path) |
1342 | return ERR_PTR(error: -ENOMEM); |
1343 | |
1344 | path->owner = owner; |
1345 | INIT_LIST_HEAD(list: &path->ppl_list); |
1346 | INIT_LIST_HEAD(list: &path->node); |
1347 | |
1348 | /* Path header MAY be followed by one or more pipelines. */ |
1349 | ret = avs_tplg_vendor_array_lookup(tuples, block_size, |
1350 | token: AVS_TKN_PPL_ID_U32, offset: &offset); |
1351 | if (ret == -ENOENT) |
1352 | offset = block_size; |
1353 | else if (ret) |
1354 | return ERR_PTR(error: ret); |
1355 | else if (!offset) |
1356 | return ERR_PTR(error: -EINVAL); |
1357 | |
1358 | /* Process header which precedes pipeline sections. */ |
1359 | ret = avs_parse_tokens(comp, object: path, parsers, count: num_parsers, tuples, priv_size: offset); |
1360 | if (ret < 0) |
1361 | return ERR_PTR(error: ret); |
1362 | |
1363 | block_size -= offset; |
1364 | tuples = avs_tplg_vendor_array_at(tuples, offset); |
1365 | while (block_size > 0) { |
1366 | u32 esize; |
1367 | |
1368 | ret = avs_tplg_vendor_entry_size(tuples, block_size, |
1369 | entry_id_token: AVS_TKN_PPL_ID_U32, size: &esize); |
1370 | if (ret) |
1371 | return ERR_PTR(error: ret); |
1372 | |
1373 | pipeline = avs_tplg_pipeline_create(comp, owner: path, tuples, block_size: esize); |
1374 | if (IS_ERR(ptr: pipeline)) { |
1375 | dev_err(comp->dev, "parse pipeline failed: %ld\n" , |
1376 | PTR_ERR(pipeline)); |
1377 | return ERR_CAST(ptr: pipeline); |
1378 | } |
1379 | |
1380 | list_add_tail(new: &pipeline->node, head: &path->ppl_list); |
1381 | block_size -= esize; |
1382 | tuples = avs_tplg_vendor_array_at(tuples, esize); |
1383 | } |
1384 | |
1385 | return path; |
1386 | } |
1387 | |
1388 | static const struct avs_tplg_token_parser path_tmpl_parsers[] = { |
1389 | { |
1390 | .token = AVS_TKN_PATH_TMPL_ID_U32, |
1391 | .type = SND_SOC_TPLG_TUPLE_TYPE_WORD, |
1392 | .offset = offsetof(struct avs_tplg_path_template, id), |
1393 | .parse = avs_parse_word_token, |
1394 | }, |
1395 | }; |
1396 | |
1397 | static int parse_path_template(struct snd_soc_component *comp, |
1398 | struct snd_soc_tplg_vendor_array *tuples, u32 block_size, |
1399 | struct avs_tplg_path_template *template, |
1400 | const struct avs_tplg_token_parser *tmpl_tokens, u32 num_tmpl_tokens, |
1401 | const struct avs_tplg_token_parser *path_tokens, u32 num_path_tokens) |
1402 | { |
1403 | struct avs_tplg_path *path; |
1404 | u32 offset; |
1405 | int ret; |
1406 | |
1407 | /* Path template header MUST be followed by at least one path variant. */ |
1408 | ret = avs_tplg_vendor_array_lookup(tuples, block_size, |
1409 | token: AVS_TKN_PATH_ID_U32, offset: &offset); |
1410 | if (ret) |
1411 | return ret; |
1412 | |
1413 | /* Process header which precedes path variants sections. */ |
1414 | ret = avs_parse_tokens(comp, object: template, parsers: tmpl_tokens, count: num_tmpl_tokens, tuples, priv_size: offset); |
1415 | if (ret < 0) |
1416 | return ret; |
1417 | |
1418 | block_size -= offset; |
1419 | tuples = avs_tplg_vendor_array_at(tuples, offset); |
1420 | do { |
1421 | u32 esize; |
1422 | |
1423 | ret = avs_tplg_vendor_entry_size(tuples, block_size, |
1424 | entry_id_token: AVS_TKN_PATH_ID_U32, size: &esize); |
1425 | if (ret) |
1426 | return ret; |
1427 | |
1428 | path = avs_tplg_path_create(comp, owner: template, tuples, block_size: esize, parsers: path_tokens, |
1429 | num_parsers: num_path_tokens); |
1430 | if (IS_ERR(ptr: path)) { |
1431 | dev_err(comp->dev, "parse path failed: %ld\n" , PTR_ERR(path)); |
1432 | return PTR_ERR(ptr: path); |
1433 | } |
1434 | |
1435 | list_add_tail(new: &path->node, head: &template->path_list); |
1436 | block_size -= esize; |
1437 | tuples = avs_tplg_vendor_array_at(tuples, esize); |
1438 | } while (block_size > 0); |
1439 | |
1440 | return 0; |
1441 | } |
1442 | |
1443 | static struct avs_tplg_path_template * |
1444 | avs_tplg_path_template_create(struct snd_soc_component *comp, struct avs_tplg *owner, |
1445 | struct snd_soc_tplg_vendor_array *tuples, u32 block_size) |
1446 | { |
1447 | struct avs_tplg_path_template *template; |
1448 | int ret; |
1449 | |
1450 | template = devm_kzalloc(dev: comp->card->dev, size: sizeof(*template), GFP_KERNEL); |
1451 | if (!template) |
1452 | return ERR_PTR(error: -ENOMEM); |
1453 | |
1454 | template->owner = owner; /* Used to access component tplg is assigned to. */ |
1455 | INIT_LIST_HEAD(list: &template->path_list); |
1456 | INIT_LIST_HEAD(list: &template->node); |
1457 | |
1458 | ret = parse_path_template(comp, tuples, block_size, template, tmpl_tokens: path_tmpl_parsers, |
1459 | ARRAY_SIZE(path_tmpl_parsers), path_tokens: path_parsers, |
1460 | ARRAY_SIZE(path_parsers)); |
1461 | if (ret) |
1462 | return ERR_PTR(error: ret); |
1463 | |
1464 | return template; |
1465 | } |
1466 | |
1467 | static const struct avs_tplg_token_parser mod_init_config_parsers[] = { |
1468 | { |
1469 | .token = AVS_TKN_MOD_INIT_CONFIG_ID_U32, |
1470 | .type = SND_SOC_TPLG_TUPLE_TYPE_WORD, |
1471 | .offset = offsetof(struct avs_tplg_init_config, id), |
1472 | .parse = avs_parse_word_token, |
1473 | }, |
1474 | { |
1475 | .token = AVS_TKN_INIT_CONFIG_PARAM_U8, |
1476 | .type = SND_SOC_TPLG_TUPLE_TYPE_BYTE, |
1477 | .offset = offsetof(struct avs_tplg_init_config, param), |
1478 | .parse = avs_parse_byte_token, |
1479 | }, |
1480 | { |
1481 | .token = AVS_TKN_INIT_CONFIG_LENGTH_U32, |
1482 | .type = SND_SOC_TPLG_TUPLE_TYPE_WORD, |
1483 | .offset = offsetof(struct avs_tplg_init_config, length), |
1484 | .parse = avs_parse_word_token, |
1485 | }, |
1486 | }; |
1487 | |
1488 | static int avs_tplg_parse_initial_configs(struct snd_soc_component *comp, |
1489 | struct snd_soc_tplg_vendor_array *tuples, |
1490 | u32 block_size) |
1491 | { |
1492 | struct avs_soc_component *acomp = to_avs_soc_component(comp); |
1493 | struct avs_tplg *tplg = acomp->tplg; |
1494 | int ret, i; |
1495 | |
1496 | /* Parse tuple section telling how many init configs there are. */ |
1497 | ret = parse_dictionary_header(comp, tuples, dict: (void **)&tplg->init_configs, |
1498 | num_entries: &tplg->num_init_configs, |
1499 | entry_size: sizeof(*tplg->init_configs), |
1500 | num_entries_token: AVS_TKN_MANIFEST_NUM_INIT_CONFIGS_U32); |
1501 | if (ret) |
1502 | return ret; |
1503 | |
1504 | block_size -= le32_to_cpu(tuples->size); |
1505 | /* With header parsed, move on to parsing entries. */ |
1506 | tuples = avs_tplg_vendor_array_next(tuples); |
1507 | |
1508 | for (i = 0; i < tplg->num_init_configs && block_size > 0; i++) { |
1509 | struct avs_tplg_init_config *config = &tplg->init_configs[i]; |
1510 | struct snd_soc_tplg_vendor_array *tmp; |
1511 | void *init_config_data; |
1512 | u32 esize; |
1513 | |
1514 | /* |
1515 | * Usually to get section length we search for first token of next group of data, |
1516 | * but in this case we can't as tuples are followed by raw data. |
1517 | */ |
1518 | tmp = avs_tplg_vendor_array_next(tuples); |
1519 | esize = le32_to_cpu(tuples->size) + le32_to_cpu(tmp->size); |
1520 | |
1521 | ret = parse_dictionary_entries(comp, tuples, block_size: esize, dict: config, num_entries: 1, entry_size: sizeof(*config), |
1522 | entry_id_token: AVS_TKN_MOD_INIT_CONFIG_ID_U32, |
1523 | parsers: mod_init_config_parsers, |
1524 | ARRAY_SIZE(mod_init_config_parsers)); |
1525 | |
1526 | block_size -= esize; |
1527 | |
1528 | /* handle raw data section */ |
1529 | init_config_data = (void *)tuples + esize; |
1530 | esize = config->length; |
1531 | |
1532 | config->data = devm_kmemdup(dev: comp->card->dev, src: init_config_data, len: esize, GFP_KERNEL); |
1533 | if (!config->data) |
1534 | return -ENOMEM; |
1535 | |
1536 | tuples = init_config_data + esize; |
1537 | block_size -= esize; |
1538 | } |
1539 | |
1540 | return 0; |
1541 | } |
1542 | |
1543 | static int avs_route_load(struct snd_soc_component *comp, int index, |
1544 | struct snd_soc_dapm_route *route) |
1545 | { |
1546 | struct snd_soc_acpi_mach *mach = dev_get_platdata(dev: comp->card->dev); |
1547 | size_t len = SNDRV_CTL_ELEM_ID_NAME_MAXLEN; |
1548 | char buf[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; |
1549 | int ssp_port, tdm_slot; |
1550 | |
1551 | /* See parse_link_formatted_string() for dynamic naming when(s). */ |
1552 | if (!avs_mach_singular_ssp(mach)) |
1553 | return 0; |
1554 | ssp_port = avs_mach_ssp_port(mach); |
1555 | |
1556 | if (!avs_mach_singular_tdm(mach, port: ssp_port)) |
1557 | return 0; |
1558 | tdm_slot = avs_mach_ssp_tdm(mach, port: ssp_port); |
1559 | |
1560 | avs_ssp_sprint(buf, size: len, fmt: route->source, port: ssp_port, tdm: tdm_slot); |
1561 | strscpy((char *)route->source, buf, len); |
1562 | avs_ssp_sprint(buf, size: len, fmt: route->sink, port: ssp_port, tdm: tdm_slot); |
1563 | strscpy((char *)route->sink, buf, len); |
1564 | if (route->control) { |
1565 | avs_ssp_sprint(buf, size: len, fmt: route->control, port: ssp_port, tdm: tdm_slot); |
1566 | strscpy((char *)route->control, buf, len); |
1567 | } |
1568 | |
1569 | return 0; |
1570 | } |
1571 | |
1572 | static int avs_widget_load(struct snd_soc_component *comp, int index, |
1573 | struct snd_soc_dapm_widget *w, |
1574 | struct snd_soc_tplg_dapm_widget *dw) |
1575 | { |
1576 | struct snd_soc_acpi_mach *mach; |
1577 | struct avs_tplg_path_template *template; |
1578 | struct avs_soc_component *acomp = to_avs_soc_component(comp); |
1579 | struct avs_tplg *tplg; |
1580 | int ssp_port, tdm_slot; |
1581 | |
1582 | if (!le32_to_cpu(dw->priv.size)) |
1583 | return 0; |
1584 | |
1585 | if (w->ignore_suspend && !AVS_S0IX_SUPPORTED) { |
1586 | dev_info_once(comp->dev, "Device does not support S0IX, check BIOS settings\n" ); |
1587 | w->ignore_suspend = false; |
1588 | } |
1589 | |
1590 | tplg = acomp->tplg; |
1591 | mach = dev_get_platdata(dev: comp->card->dev); |
1592 | if (!avs_mach_singular_ssp(mach)) |
1593 | goto static_name; |
1594 | ssp_port = avs_mach_ssp_port(mach); |
1595 | |
1596 | /* See parse_link_formatted_string() for dynamic naming when(s). */ |
1597 | if (avs_mach_singular_tdm(mach, port: ssp_port)) { |
1598 | /* size is based on possible %d -> SSP:TDM, where SSP and TDM < 10 + '\0' */ |
1599 | size_t size = strlen(dw->name) + 2; |
1600 | char *buf; |
1601 | |
1602 | tdm_slot = avs_mach_ssp_tdm(mach, port: ssp_port); |
1603 | |
1604 | buf = kmalloc(size, GFP_KERNEL); |
1605 | if (!buf) |
1606 | return -ENOMEM; |
1607 | avs_ssp_sprint(buf, size, fmt: dw->name, port: ssp_port, tdm: tdm_slot); |
1608 | kfree(objp: w->name); |
1609 | /* w->name is freed later by soc_tplg_dapm_widget_create() */ |
1610 | w->name = buf; |
1611 | } |
1612 | |
1613 | static_name: |
1614 | template = avs_tplg_path_template_create(comp, owner: tplg, tuples: dw->priv.array, |
1615 | le32_to_cpu(dw->priv.size)); |
1616 | if (IS_ERR(ptr: template)) { |
1617 | dev_err(comp->dev, "widget %s load failed: %ld\n" , dw->name, |
1618 | PTR_ERR(template)); |
1619 | return PTR_ERR(ptr: template); |
1620 | } |
1621 | |
1622 | w->priv = template; /* link path information to widget */ |
1623 | list_add_tail(new: &template->node, head: &tplg->path_tmpl_list); |
1624 | return 0; |
1625 | } |
1626 | |
1627 | static int avs_widget_ready(struct snd_soc_component *comp, int index, |
1628 | struct snd_soc_dapm_widget *w, |
1629 | struct snd_soc_tplg_dapm_widget *dw) |
1630 | { |
1631 | struct avs_tplg_path_template *template = w->priv; |
1632 | |
1633 | template->w = w; |
1634 | return 0; |
1635 | } |
1636 | |
1637 | static int avs_dai_load(struct snd_soc_component *comp, int index, |
1638 | struct snd_soc_dai_driver *dai_drv, struct snd_soc_tplg_pcm *pcm, |
1639 | struct snd_soc_dai *dai) |
1640 | { |
1641 | u32 fe_subformats = SNDRV_PCM_SUBFMTBIT_MSBITS_20 | |
1642 | SNDRV_PCM_SUBFMTBIT_MSBITS_24 | |
1643 | SNDRV_PCM_SUBFMTBIT_MSBITS_MAX; |
1644 | |
1645 | if (pcm) { |
1646 | dai_drv->ops = &avs_dai_fe_ops; |
1647 | dai_drv->capture.subformats = fe_subformats; |
1648 | dai_drv->playback.subformats = fe_subformats; |
1649 | } |
1650 | |
1651 | return 0; |
1652 | } |
1653 | |
1654 | static int avs_link_load(struct snd_soc_component *comp, int index, struct snd_soc_dai_link *link, |
1655 | struct snd_soc_tplg_link_config *cfg) |
1656 | { |
1657 | if (link->ignore_suspend && !AVS_S0IX_SUPPORTED) { |
1658 | dev_info_once(comp->dev, "Device does not support S0IX, check BIOS settings\n" ); |
1659 | link->ignore_suspend = false; |
1660 | } |
1661 | |
1662 | if (!link->no_pcm) { |
1663 | /* Stream control handled by IPCs. */ |
1664 | link->nonatomic = true; |
1665 | |
1666 | /* Open LINK (BE) pipes last and close them first to prevent xruns. */ |
1667 | link->trigger[0] = SND_SOC_DPCM_TRIGGER_PRE; |
1668 | link->trigger[1] = SND_SOC_DPCM_TRIGGER_PRE; |
1669 | } else { |
1670 | /* Do not ignore codec capabilities. */ |
1671 | link->dpcm_merged_format = 1; |
1672 | } |
1673 | |
1674 | return 0; |
1675 | } |
1676 | |
1677 | static const struct avs_tplg_token_parser manifest_parsers[] = { |
1678 | { |
1679 | .token = AVS_TKN_MANIFEST_NAME_STRING, |
1680 | .type = SND_SOC_TPLG_TUPLE_TYPE_STRING, |
1681 | .offset = offsetof(struct avs_tplg, name), |
1682 | .parse = parse_link_formatted_string, |
1683 | }, |
1684 | { |
1685 | .token = AVS_TKN_MANIFEST_VERSION_U32, |
1686 | .type = SND_SOC_TPLG_TUPLE_TYPE_WORD, |
1687 | .offset = offsetof(struct avs_tplg, version), |
1688 | .parse = avs_parse_word_token, |
1689 | }, |
1690 | }; |
1691 | |
1692 | static int avs_manifest(struct snd_soc_component *comp, int index, |
1693 | struct snd_soc_tplg_manifest *manifest) |
1694 | { |
1695 | struct snd_soc_tplg_vendor_array *tuples = manifest->priv.array; |
1696 | struct avs_soc_component *acomp = to_avs_soc_component(comp); |
1697 | size_t remaining = le32_to_cpu(manifest->priv.size); |
1698 | bool has_init_config = true; |
1699 | u32 offset; |
1700 | int ret; |
1701 | |
1702 | ret = avs_tplg_vendor_array_lookup(tuples, block_size: remaining, |
1703 | token: AVS_TKN_MANIFEST_NUM_LIBRARIES_U32, offset: &offset); |
1704 | /* Manifest MUST begin with a header. */ |
1705 | if (!ret && !offset) |
1706 | ret = -EINVAL; |
1707 | if (ret) { |
1708 | dev_err(comp->dev, "incorrect manifest format: %d\n" , ret); |
1709 | return ret; |
1710 | } |
1711 | |
1712 | /* Process header which precedes any of the dictionaries. */ |
1713 | ret = avs_parse_tokens(comp, object: acomp->tplg, parsers: manifest_parsers, |
1714 | ARRAY_SIZE(manifest_parsers), tuples, priv_size: offset); |
1715 | if (ret < 0) |
1716 | return ret; |
1717 | |
1718 | remaining -= offset; |
1719 | tuples = avs_tplg_vendor_array_at(tuples, offset); |
1720 | |
1721 | ret = avs_tplg_vendor_array_lookup(tuples, block_size: remaining, |
1722 | token: AVS_TKN_MANIFEST_NUM_AFMTS_U32, offset: &offset); |
1723 | if (ret) { |
1724 | dev_err(comp->dev, "audio formats lookup failed: %d\n" , ret); |
1725 | return ret; |
1726 | } |
1727 | |
1728 | /* Libraries dictionary. */ |
1729 | ret = avs_tplg_parse_libraries(comp, tuples, block_size: offset); |
1730 | if (ret < 0) |
1731 | return ret; |
1732 | |
1733 | remaining -= offset; |
1734 | tuples = avs_tplg_vendor_array_at(tuples, offset); |
1735 | |
1736 | ret = avs_tplg_vendor_array_lookup(tuples, block_size: remaining, |
1737 | token: AVS_TKN_MANIFEST_NUM_MODCFGS_BASE_U32, offset: &offset); |
1738 | if (ret) { |
1739 | dev_err(comp->dev, "modcfgs_base lookup failed: %d\n" , ret); |
1740 | return ret; |
1741 | } |
1742 | |
1743 | /* Audio formats dictionary. */ |
1744 | ret = avs_tplg_parse_audio_formats(comp, tuples, block_size: offset); |
1745 | if (ret < 0) |
1746 | return ret; |
1747 | |
1748 | remaining -= offset; |
1749 | tuples = avs_tplg_vendor_array_at(tuples, offset); |
1750 | |
1751 | ret = avs_tplg_vendor_array_lookup(tuples, block_size: remaining, |
1752 | token: AVS_TKN_MANIFEST_NUM_MODCFGS_EXT_U32, offset: &offset); |
1753 | if (ret) { |
1754 | dev_err(comp->dev, "modcfgs_ext lookup failed: %d\n" , ret); |
1755 | return ret; |
1756 | } |
1757 | |
1758 | /* Module configs-base dictionary. */ |
1759 | ret = avs_tplg_parse_modcfgs_base(comp, tuples, block_size: offset); |
1760 | if (ret < 0) |
1761 | return ret; |
1762 | |
1763 | remaining -= offset; |
1764 | tuples = avs_tplg_vendor_array_at(tuples, offset); |
1765 | |
1766 | ret = avs_tplg_vendor_array_lookup(tuples, block_size: remaining, |
1767 | token: AVS_TKN_MANIFEST_NUM_PPLCFGS_U32, offset: &offset); |
1768 | if (ret) { |
1769 | dev_err(comp->dev, "pplcfgs lookup failed: %d\n" , ret); |
1770 | return ret; |
1771 | } |
1772 | |
1773 | /* Module configs-ext dictionary. */ |
1774 | ret = avs_tplg_parse_modcfgs_ext(comp, tuples, block_size: offset); |
1775 | if (ret < 0) |
1776 | return ret; |
1777 | |
1778 | remaining -= offset; |
1779 | tuples = avs_tplg_vendor_array_at(tuples, offset); |
1780 | |
1781 | ret = avs_tplg_vendor_array_lookup(tuples, block_size: remaining, |
1782 | token: AVS_TKN_MANIFEST_NUM_BINDINGS_U32, offset: &offset); |
1783 | if (ret) { |
1784 | dev_err(comp->dev, "bindings lookup failed: %d\n" , ret); |
1785 | return ret; |
1786 | } |
1787 | |
1788 | /* Pipeline configs dictionary. */ |
1789 | ret = avs_tplg_parse_pplcfgs(comp, tuples, block_size: offset); |
1790 | if (ret < 0) |
1791 | return ret; |
1792 | |
1793 | remaining -= offset; |
1794 | tuples = avs_tplg_vendor_array_at(tuples, offset); |
1795 | |
1796 | ret = avs_tplg_vendor_array_lookup(tuples, block_size: remaining, |
1797 | token: AVS_TKN_MANIFEST_NUM_CONDPATH_TMPLS_U32, offset: &offset); |
1798 | if (ret) { |
1799 | dev_err(comp->dev, "condpath lookup failed: %d\n" , ret); |
1800 | return ret; |
1801 | } |
1802 | |
1803 | /* Bindings dictionary. */ |
1804 | ret = avs_tplg_parse_bindings(comp, tuples, block_size: offset); |
1805 | if (ret < 0) |
1806 | return ret; |
1807 | |
1808 | remaining -= offset; |
1809 | tuples = avs_tplg_vendor_array_at(tuples, offset); |
1810 | |
1811 | ret = avs_tplg_vendor_array_lookup(tuples, block_size: remaining, |
1812 | token: AVS_TKN_MANIFEST_NUM_INIT_CONFIGS_U32, offset: &offset); |
1813 | if (ret == -ENOENT) { |
1814 | dev_dbg(comp->dev, "init config lookup failed: %d\n" , ret); |
1815 | has_init_config = false; |
1816 | } else if (ret) { |
1817 | dev_err(comp->dev, "init config lookup failed: %d\n" , ret); |
1818 | return ret; |
1819 | } |
1820 | |
1821 | if (!has_init_config) |
1822 | return 0; |
1823 | |
1824 | remaining -= offset; |
1825 | tuples = avs_tplg_vendor_array_at(tuples, offset); |
1826 | |
1827 | /* Initial configs dictionary. */ |
1828 | ret = avs_tplg_parse_initial_configs(comp, tuples, block_size: remaining); |
1829 | if (ret < 0) |
1830 | return ret; |
1831 | |
1832 | return 0; |
1833 | } |
1834 | |
1835 | #define AVS_CONTROL_OPS_VOLUME 257 |
1836 | |
1837 | static const struct snd_soc_tplg_kcontrol_ops avs_control_ops[] = { |
1838 | { |
1839 | .id = AVS_CONTROL_OPS_VOLUME, |
1840 | .get = avs_control_volume_get, |
1841 | .put = avs_control_volume_put, |
1842 | }, |
1843 | }; |
1844 | |
1845 | static const struct avs_tplg_token_parser control_parsers[] = { |
1846 | { |
1847 | .token = AVS_TKN_KCONTROL_ID_U32, |
1848 | .type = SND_SOC_TPLG_TUPLE_TYPE_WORD, |
1849 | .offset = offsetof(struct avs_control_data, id), |
1850 | .parse = avs_parse_word_token, |
1851 | }, |
1852 | }; |
1853 | |
1854 | static int |
1855 | avs_control_load(struct snd_soc_component *comp, int index, struct snd_kcontrol_new *ctmpl, |
1856 | struct snd_soc_tplg_ctl_hdr *hdr) |
1857 | { |
1858 | struct snd_soc_tplg_vendor_array *tuples; |
1859 | struct snd_soc_tplg_mixer_control *tmc; |
1860 | struct avs_control_data *ctl_data; |
1861 | struct soc_mixer_control *mc; |
1862 | size_t block_size; |
1863 | int ret; |
1864 | |
1865 | switch (le32_to_cpu(hdr->type)) { |
1866 | case SND_SOC_TPLG_TYPE_MIXER: |
1867 | tmc = container_of(hdr, typeof(*tmc), hdr); |
1868 | tuples = tmc->priv.array; |
1869 | block_size = le32_to_cpu(tmc->priv.size); |
1870 | break; |
1871 | default: |
1872 | return -EINVAL; |
1873 | } |
1874 | |
1875 | ctl_data = devm_kzalloc(dev: comp->card->dev, size: sizeof(*ctl_data), GFP_KERNEL); |
1876 | if (!ctl_data) |
1877 | return -ENOMEM; |
1878 | |
1879 | ret = parse_dictionary_entries(comp, tuples, block_size, dict: ctl_data, num_entries: 1, entry_size: sizeof(*ctl_data), |
1880 | entry_id_token: AVS_TKN_KCONTROL_ID_U32, parsers: control_parsers, |
1881 | ARRAY_SIZE(control_parsers)); |
1882 | if (ret) |
1883 | return ret; |
1884 | |
1885 | mc = (struct soc_mixer_control *)ctmpl->private_value; |
1886 | mc->dobj.private = ctl_data; |
1887 | return 0; |
1888 | } |
1889 | |
1890 | static struct snd_soc_tplg_ops avs_tplg_ops = { |
1891 | .io_ops = avs_control_ops, |
1892 | .io_ops_count = ARRAY_SIZE(avs_control_ops), |
1893 | .control_load = avs_control_load, |
1894 | .dapm_route_load = avs_route_load, |
1895 | .widget_load = avs_widget_load, |
1896 | .widget_ready = avs_widget_ready, |
1897 | .dai_load = avs_dai_load, |
1898 | .link_load = avs_link_load, |
1899 | .manifest = avs_manifest, |
1900 | }; |
1901 | |
1902 | struct avs_tplg *avs_tplg_new(struct snd_soc_component *comp) |
1903 | { |
1904 | struct avs_tplg *tplg; |
1905 | |
1906 | tplg = devm_kzalloc(dev: comp->card->dev, size: sizeof(*tplg), GFP_KERNEL); |
1907 | if (!tplg) |
1908 | return NULL; |
1909 | |
1910 | tplg->comp = comp; |
1911 | INIT_LIST_HEAD(list: &tplg->path_tmpl_list); |
1912 | |
1913 | return tplg; |
1914 | } |
1915 | |
1916 | int avs_load_topology(struct snd_soc_component *comp, const char *filename) |
1917 | { |
1918 | const struct firmware *fw; |
1919 | int ret; |
1920 | |
1921 | ret = request_firmware(fw: &fw, name: filename, device: comp->dev); |
1922 | if (ret < 0) { |
1923 | dev_err(comp->dev, "request topology \"%s\" failed: %d\n" , filename, ret); |
1924 | return ret; |
1925 | } |
1926 | |
1927 | ret = snd_soc_tplg_component_load(comp, ops: &avs_tplg_ops, fw); |
1928 | if (ret < 0) |
1929 | dev_err(comp->dev, "load topology \"%s\" failed: %d\n" , filename, ret); |
1930 | |
1931 | release_firmware(fw); |
1932 | return ret; |
1933 | } |
1934 | |
1935 | int avs_remove_topology(struct snd_soc_component *comp) |
1936 | { |
1937 | snd_soc_tplg_component_remove(comp); |
1938 | |
1939 | return 0; |
1940 | } |
1941 | |