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
30struct sof_manifest_tlv {
31	uint32_t type;
32	uint32_t size;
33	uint8_t data[];
34} __attribute__((packed));
35
36struct 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
45static 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
91static void debug_print_nhlt(struct nhlt *blob ATTRIBUTE_UNUSED,
92			     struct endpoint_descriptor **eps ATTRIBUTE_UNUSED) {}
93#endif
94
95static 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
129static 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;
172err:
173	if (new_bytes)
174		snd_config_delete(new_bytes);
175	if (src)
176		free(src);
177
178	return ret;
179}
180
181static 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
212static 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
277static 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 */
318static 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
395err:
396	/* remove all enpoints */
397	for (i = 0; i < eps_count; i++)
398		free(eps[i]);
399
400	return ret;
401}
402
403static 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
427err:
428	if (manifest_buffer)
429		free(manifest_buffer);
430	if (nhlt_buffer)
431		free(nhlt_buffer);
432
433	return ret;
434}
435
436SND_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