• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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