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