1 // SPDX-License-Identifier: BSD-3-Clause
2 //
3 // Copyright(c) 2021 Intel Corporation. All rights reserved.
4 //
5 // Author: Jaska Uimonen <jaska.uimonen@linux.intel.com>
6
7 #include "aconfig.h"
8 #include <errno.h>
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include <stdbool.h>
13 #include <inttypes.h>
14 #include <alsa/global.h>
15 #include <alsa/input.h>
16 #include <alsa/output.h>
17 #include <alsa/conf.h>
18 #include <alsa/error.h>
19 #include "pre-process-external.h"
20 #include "nhlt.h"
21 #include "intel/intel-nhlt.h"
22 #include "intel/dmic-nhlt.h"
23 #include "intel/ssp-nhlt.h"
24
25 #define MAX_ENDPOINT_COUNT 20
26 #define ALSA_BYTE_CHARS 5
27 #define SOF_ABI_CHARS 29
28 #define SOF_MANIFEST_DATA_TYPE_NHLT 1
29
30 struct sof_manifest_tlv {
31 uint32_t type;
32 uint32_t size;
33 uint8_t data[];
34 } __attribute__((packed));
35
36 struct sof_manifest {
37 uint16_t abi_major;
38 uint16_t abi_minor;
39 uint16_t abi_patch;
40 uint16_t count;
41 struct sof_manifest_tlv items[];
42 } __attribute__((packed));
43
44 #ifdef NHLT_DEBUG
debug_print_nhlt(struct nhlt * blob,struct endpoint_descriptor ** eps)45 static void debug_print_nhlt(struct nhlt *blob, struct endpoint_descriptor **eps)
46 {
47 uint8_t *top_p = (uint8_t *)blob;
48 struct endpoint_descriptor *ep;
49 uint8_t *ep_p;
50 int i, j, k, lines, remain;
51
52 fprintf(stdout, "printing nhlt as bytes:\n");
53
54 lines = sizeof(struct nhlt) / 8;
55 remain = sizeof(struct nhlt) % 8;
56 for (i = 0; i < lines; i++) {
57 for (j = 0; j < 8; j++) {
58 fprintf(stdout, "0x%02x,", *top_p);
59 top_p++;
60 }
61 fprintf(stdout, "\n");
62 }
63 for (i = 0; i < remain; i++) {
64 fprintf(stdout, "0x%02x,", *top_p);
65 top_p++;
66 }
67 fprintf(stdout, "\n\n");
68
69 for (i = 0; i < blob->endpoint_count; i++) {
70 ep = eps[i];
71 ep_p = (uint8_t *)ep;
72 lines = ep->length / 8;
73 remain = ep->length % 8;
74 for (j = 0; j < lines; j++) {
75 for (k = 0; k < 8; k++) {
76 fprintf(stdout, "0x%02x,", *ep_p);
77 ep_p++;
78 }
79 fprintf(stdout, "\n");
80 }
81 for (j = 0; j < remain; j++) {
82 fprintf(stdout, "0x%02x,", *ep_p);
83 ep_p++;
84 }
85 fprintf(stdout, "\n");
86 }
87
88 fprintf(stdout, "\n");
89 }
90 #else
debug_print_nhlt(struct nhlt * blob ATTRIBUTE_UNUSED,struct endpoint_descriptor ** eps ATTRIBUTE_UNUSED)91 static void debug_print_nhlt(struct nhlt *blob ATTRIBUTE_UNUSED,
92 struct endpoint_descriptor **eps ATTRIBUTE_UNUSED) {}
93 #endif
94
print_as_hex_bytes(uint8_t * manifest_buffer,uint32_t manifest_size,uint8_t * nhlt_buffer,uint32_t nhlt_size,char ** src)95 static int print_as_hex_bytes(uint8_t *manifest_buffer, uint32_t manifest_size,
96 uint8_t *nhlt_buffer, uint32_t nhlt_size, char **src)
97 {
98 char *bytes_string_buffer;
99 char *dst;
100 unsigned int i;
101
102 bytes_string_buffer = calloc((manifest_size + nhlt_size) * ALSA_BYTE_CHARS + 1,
103 sizeof(uint8_t));
104 if (!bytes_string_buffer)
105 return -ENOMEM;
106
107 dst = bytes_string_buffer;
108 for (i = 0; i < manifest_size; i++) {
109 snprintf(dst, ALSA_BYTE_CHARS + 1, "0x%02x,", *manifest_buffer);
110 dst += ALSA_BYTE_CHARS;
111 manifest_buffer++;
112 }
113
114 for (i = 0; i < nhlt_size; i++) {
115 snprintf(dst, ALSA_BYTE_CHARS + 1, "0x%02x,", *nhlt_buffer);
116 dst += ALSA_BYTE_CHARS;
117 nhlt_buffer++;
118 }
119
120 /* remove the last comma... */
121 dst--;
122 *dst = '\0';
123
124 *src = bytes_string_buffer;
125
126 return 0;
127 }
128
merge_manifest_data(snd_config_t * cfg,uint8_t * manifest_buffer,uint32_t manifest_size,uint8_t * nhlt_buffer,uint32_t nhlt_size)129 static int merge_manifest_data(snd_config_t *cfg, uint8_t *manifest_buffer, uint32_t manifest_size,
130 uint8_t *nhlt_buffer, uint32_t nhlt_size)
131 {
132 const char *data_name = "SOF ABI";
133 snd_config_t *data_section;
134 snd_config_t *manifest;
135 snd_config_t *old_bytes;
136 snd_config_t *new_bytes;
137 char *src = NULL;
138 int ret;
139
140 /* merge manifest struct and nhlt bytes as new config into existing SectionData*/
141 ret = snd_config_search(cfg, "SectionData", &data_section);
142 if (ret < 0)
143 return ret;
144
145 ret = snd_config_search(data_section, data_name, &manifest);
146 if (ret < 0)
147 return ret;
148
149 ret = snd_config_search(manifest, "bytes", &old_bytes);
150 if (ret < 0)
151 return ret;
152
153 ret = snd_config_make(&new_bytes, "bytes", SND_CONFIG_TYPE_STRING);
154 if (ret < 0)
155 goto err;
156
157 ret = print_as_hex_bytes(manifest_buffer, manifest_size, nhlt_buffer, nhlt_size, &src);
158 if (ret < 0)
159 goto err;
160
161 ret = snd_config_set_string(new_bytes, src);
162 if (ret < 0)
163 goto err;
164
165 ret = snd_config_merge(old_bytes, new_bytes, true);
166 if (ret < 0)
167 goto err;
168
169 free(src);
170
171 return 0;
172 err:
173 if (new_bytes)
174 snd_config_delete(new_bytes);
175 if (src)
176 free(src);
177
178 return ret;
179 }
180
save_nhlt_binary(struct nhlt * blob,struct endpoint_descriptor ** eps,snd_config_t * cfg)181 static void save_nhlt_binary(struct nhlt *blob, struct endpoint_descriptor **eps, snd_config_t *cfg)
182 {
183 const char *bin_file = NULL;
184 snd_config_t *defines;
185 FILE *fp;
186 int ret;
187 int i;
188
189 ret = snd_config_search(cfg, "Define.NHLT_BIN", &defines);
190 if (ret < 0)
191 return;
192
193 if (snd_config_get_string(defines, &bin_file) < 0)
194 return;
195
196 fp = fopen(bin_file, "wb");
197 if (fp == NULL) {
198 fprintf(stderr, "can't open nhlt binary output file %s\n", bin_file);
199 return;
200 }
201
202 fprintf(stdout, "saving nhlt as binary in %s\n", bin_file);
203
204 fwrite(blob, 1, sizeof(struct nhlt), fp);
205
206 for (i = 0; i < blob->endpoint_count; i++)
207 fwrite(eps[i], eps[i]->length, sizeof(uint8_t), fp);
208
209 fclose(fp);
210 }
211
manifest_create(snd_config_t * input,uint8_t ** manifest_buffer,uint32_t * size,uint32_t nhlt_size)212 static int manifest_create(snd_config_t *input, uint8_t **manifest_buffer, uint32_t *size, uint32_t nhlt_size)
213 {
214 struct sof_manifest_tlv manifest_tlv;
215 struct sof_manifest manifest;
216 snd_config_t *data_section;
217 snd_config_t *data;
218 snd_config_t *old_bytes;
219 uint32_t manifest_size;
220 uint8_t *byte_buffer;
221 const char *abi;
222 uint8_t *top_p;
223 uint8_t *dst;
224 int ret;
225 unsigned int i;
226
227 ret = snd_config_search(input, "SectionData", &data_section);
228 if (ret < 0)
229 return ret;
230
231 ret = snd_config_search(data_section, "SOF ABI", &data);
232 if (ret < 0)
233 return ret;
234
235 ret = snd_config_search(data, "bytes", &old_bytes);
236 if (ret < 0)
237 return ret;
238
239 ret = snd_config_get_string(old_bytes, &abi);
240 if (ret < 0)
241 return ret;
242
243 /* we have something funny in abi string */
244 if (strlen(abi) != SOF_ABI_CHARS)
245 return -EINVAL;
246
247 manifest.count = 1;
248 manifest_tlv.type = SOF_MANIFEST_DATA_TYPE_NHLT;
249 manifest_tlv.size = nhlt_size;
250
251 manifest_size = sizeof(struct sof_manifest) + sizeof(struct sof_manifest_tlv);
252 byte_buffer = calloc(manifest_size, sizeof(uint8_t));
253 if (!byte_buffer)
254 return -ENOMEM;
255
256 *size = manifest_size;
257
258 dst = byte_buffer;
259
260 /* copy the ABI version bytes */
261 for (i = 0; i < 6; i++)
262 sscanf(&abi[i * ALSA_BYTE_CHARS], "%" SCNx8, dst++);
263
264 /* set the count */
265 *dst++ = manifest.count;
266 *dst++ = manifest.count >> 2;
267
268 top_p = (uint8_t *)&manifest_tlv;
269 for (i = 0; i < sizeof(struct sof_manifest_tlv); i++)
270 *dst++ = *top_p++;
271
272 *manifest_buffer = byte_buffer;
273
274 return 0;
275 }
276
nhlt_get_flat_buffer(struct nhlt * blob,struct endpoint_descriptor ** eps,uint32_t eps_count,uint32_t * size,uint8_t ** nhlt_buffer)277 static int nhlt_get_flat_buffer(struct nhlt *blob, struct endpoint_descriptor **eps,
278 uint32_t eps_count, uint32_t *size, uint8_t **nhlt_buffer)
279 {
280 uint8_t *top_p = (uint8_t *)blob;
281 struct endpoint_descriptor *ep;
282 uint8_t *byte_buffer;
283 uint32_t nhlt_size;
284 uint8_t *ep_p;
285 uint8_t *dst;
286 unsigned int i, j;
287
288 /* get blob total size */
289 nhlt_size = sizeof(struct nhlt);
290 for (i = 0; i < eps_count; i++) {
291 if (eps[i])
292 nhlt_size += eps[i]->length;
293 }
294
295 *size = nhlt_size;
296
297 byte_buffer = calloc(nhlt_size, sizeof(uint8_t));
298 if (!byte_buffer)
299 return -ENOMEM;
300
301 dst = byte_buffer;
302 for (i = 0; i < sizeof(struct nhlt); i++)
303 *dst++ = *top_p++;
304
305 for (i = 0; i < blob->endpoint_count; i++) {
306 ep = eps[i];
307 ep_p = (uint8_t *)ep;
308 for (j = 0; j < ep->length; j++)
309 *dst++ = *ep_p++;
310 }
311
312 *nhlt_buffer = byte_buffer;
313
314 return 0;
315 }
316
317 /* called at the end of topology pre-processing, create flat buffer from variable size nhlt */
nhlt_create(struct intel_nhlt_params * nhlt,snd_config_t * input,snd_config_t * output ATTRIBUTE_UNUSED,uint8_t ** nhlt_buffer,uint32_t * nhlt_size)318 static int nhlt_create(struct intel_nhlt_params *nhlt, snd_config_t *input,
319 snd_config_t *output ATTRIBUTE_UNUSED,
320 uint8_t **nhlt_buffer, uint32_t *nhlt_size)
321 {
322 struct endpoint_descriptor *eps[MAX_ENDPOINT_COUNT];
323 int eps_count = 0;
324 struct nhlt blob;
325 uint32_t size;
326 uint8_t dir;
327 int ret;
328 int i;
329
330 for (i = 0; i < MAX_ENDPOINT_COUNT; i++)
331 eps[i] = NULL;
332
333 /* we always have only 0 or 1 dmic ep */
334 for (i = 0; i < nhlt_dmic_get_ep_count(nhlt); i++) {
335 ret = nhlt_dmic_get_ep(nhlt, &eps[eps_count], i);
336 if (ret < 0)
337 return -EINVAL;
338 eps_count++;
339 }
340
341 /* we can have 0 to several ssp eps */
342 for (i = 0; i < nhlt_ssp_get_ep_count(nhlt); i++) {
343 nhlt_ssp_get_dir(nhlt, i, &dir);
344 /* duplicate endpoint for duplex dai */
345 if (dir > NHLT_ENDPOINT_DIRECTION_FEEDBACK_FOR_RENDER) {
346 ret = nhlt_ssp_get_ep(nhlt, &eps[eps_count], i,
347 NHLT_ENDPOINT_DIRECTION_RENDER);
348 if (ret < 0)
349 goto err;
350 eps_count++;
351 ret = nhlt_ssp_get_ep(nhlt, &eps[eps_count], i,
352 NHLT_ENDPOINT_DIRECTION_CAPTURE);
353 } else {
354 ret = nhlt_ssp_get_ep(nhlt, &eps[eps_count], i, dir);
355 }
356 if (ret < 0)
357 goto err;
358 eps_count++;
359 }
360
361 /* we don't have endpoints */
362 if (!eps_count)
363 return 0;
364
365 uint8_t sig[4] = {'N', 'H', 'L', 'T'};
366 blob.efi_acpi.signature = *((uint32_t *)sig);
367 blob.efi_acpi.length = 0;
368 blob.efi_acpi.revision = 0;
369 blob.efi_acpi.checksum = 0;
370 for (i = 0; i < 6; i++)
371 blob.efi_acpi.oem_id[i] = 0;
372 blob.efi_acpi.oem_table_id = 0;
373 blob.efi_acpi.oem_revision = 0;
374 blob.efi_acpi.creator_id = 0;
375 blob.efi_acpi.creator_revision = 0;
376
377 blob.endpoint_count = eps_count;
378
379 /* get blob total size */
380 size = sizeof(struct nhlt);
381 for (i = 0; i < eps_count; i++) {
382 if (eps[i])
383 size += eps[i]->length;
384 }
385
386 /* add the total length to top level struct */
387 blob.efi_acpi.length = size;
388
389 debug_print_nhlt(&blob, eps);
390
391 save_nhlt_binary(&blob, eps, input);
392
393 ret = nhlt_get_flat_buffer(&blob, eps, eps_count, nhlt_size, nhlt_buffer);
394
395 err:
396 /* remove all enpoints */
397 for (i = 0; i < eps_count; i++)
398 free(eps[i]);
399
400 return ret;
401 }
402
do_nhlt(struct intel_nhlt_params * nhlt,snd_config_t * input,snd_config_t * output)403 static int do_nhlt(struct intel_nhlt_params *nhlt, snd_config_t *input, snd_config_t *output)
404 {
405 uint8_t *manifest_buffer = NULL;
406 uint8_t *nhlt_buffer = NULL;
407 uint32_t manifest_size;
408 uint32_t nhlt_size = 0;
409 int ret = 0;
410
411 ret = nhlt_create(nhlt, input, output, &nhlt_buffer, &nhlt_size);
412 if (ret) {
413 fprintf(stderr, "can't create nhlt blob, err %d\n", ret);
414 return ret;
415 }
416
417 ret = manifest_create(output, &manifest_buffer, &manifest_size, nhlt_size);
418 if (ret) {
419 fprintf(stderr, "can't re-create manifest, err %d\n", ret);
420 goto err;
421 }
422
423 ret = merge_manifest_data(output, manifest_buffer, manifest_size, nhlt_buffer, nhlt_size);
424 if (ret)
425 fprintf(stderr, "can't merge manifest data, err %d\n", ret);
426
427 err:
428 if (manifest_buffer)
429 free(manifest_buffer);
430 if (nhlt_buffer)
431 free(nhlt_buffer);
432
433 return ret;
434 }
435
SND_TOPOLOGY_PLUGIN_DEFINE_FUNC(nhlt)436 SND_TOPOLOGY_PLUGIN_DEFINE_FUNC(nhlt)
437 {
438 snd_config_iterator_t i, i2, next, next2;
439 struct intel_nhlt_params nhlt;
440 snd_config_t *n, *n2;
441 snd_config_t *items;
442 const char *id, *id2;
443 int ret;
444
445 /* initialize the internal structs */
446 ret = nhlt_ssp_init_params(&nhlt);
447 if (ret < 0)
448 return ret;
449
450 ret = nhlt_dmic_init_params(&nhlt);
451 if (ret < 0)
452 return ret;
453
454 /* find DAIs and set internal parameters */
455 ret = snd_config_search(input, "Object.Dai", &items);
456 if (ret < 0)
457 return ret;
458
459 snd_config_for_each(i, next, items) {
460 n = snd_config_iterator_entry(i);
461
462 if (snd_config_get_id(n, &id) < 0)
463 continue;
464
465 snd_config_for_each(i2, next2, n) {
466 n2 = snd_config_iterator_entry(i2);
467
468 if (snd_config_get_id(n2, &id2) < 0)
469 continue;
470
471 /* set dai parameters here */
472 if (!strncmp(id, "DMIC", 4)) {
473 ret = nhlt_dmic_set_params(&nhlt, n2, input);
474 if (ret < 0)
475 return ret;
476 }
477
478 if (!strncmp(id, "SSP", 3)) {
479 ret = nhlt_ssp_set_params(&nhlt, n2, input);
480 if (ret < 0)
481 return ret;
482 }
483 }
484 }
485
486 /* create the nhlt blob from internal structs */
487 ret = do_nhlt(&nhlt, input, output);
488 if (ret)
489 fprintf(stderr, "error in nhlt processing\n");
490
491 free(nhlt.ssp_params);
492 free(nhlt.dmic_params);
493
494 return 0;
495 }
496