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