// SPDX-License-Identifier: BSD-3-Clause // // Copyright(c) 2021 Intel Corporation. All rights reserved. // // Author: Jaska Uimonen #include #include #include #include #include #include #include #include #include #include #include "pre-process-external.h" #include "nhlt.h" #include "intel/intel-nhlt.h" #include "intel/dmic-nhlt.h" #include "intel/ssp-nhlt.h" #define MAX_ENDPOINT_COUNT 20 #define ALSA_BYTE_CHARS 5 #define SOF_ABI_CHARS 29 #define SOF_MANIFEST_DATA_TYPE_NHLT 1 struct sof_manifest_tlv { uint32_t type; uint32_t size; uint8_t data[]; } __attribute__((packed)); struct sof_manifest { uint16_t abi_major; uint16_t abi_minor; uint16_t abi_patch; uint16_t count; struct sof_manifest_tlv items[]; } __attribute__((packed)); #ifdef NHLT_DEBUG static void debug_print_nhlt(struct nhlt *blob, struct endpoint_descriptor **eps) { uint8_t *top_p = (uint8_t *)blob; struct endpoint_descriptor *ep; uint8_t *ep_p; int i, j, k, lines, remain; fprintf(stdout, "printing nhlt as bytes:\n"); lines = sizeof(struct nhlt) / 8; remain = sizeof(struct nhlt) % 8; for (i = 0; i < lines; i++) { for (j = 0; j < 8; j++) { fprintf(stdout, "0x%02x,", *top_p); top_p++; } fprintf(stdout, "\n"); } for (i = 0; i < remain; i++) { fprintf(stdout, "0x%02x,", *top_p); top_p++; } fprintf(stdout, "\n\n"); for (i = 0; i < blob->endpoint_count; i++) { ep = eps[i]; ep_p = (uint8_t *)ep; lines = ep->length / 8; remain = ep->length % 8; for (j = 0; j < lines; j++) { for (k = 0; k < 8; k++) { fprintf(stdout, "0x%02x,", *ep_p); ep_p++; } fprintf(stdout, "\n"); } for (j = 0; j < remain; j++) { fprintf(stdout, "0x%02x,", *ep_p); ep_p++; } fprintf(stdout, "\n"); } fprintf(stdout, "\n"); } #else static void debug_print_nhlt(struct nhlt *blob, struct endpoint_descriptor **eps) {} #endif static int print_as_hex_bytes(uint8_t *manifest_buffer, uint32_t manifest_size, uint8_t *nhlt_buffer, uint32_t nhlt_size, char **src) { char *bytes_string_buffer; char *dst; int i; bytes_string_buffer = calloc((manifest_size + nhlt_size) * ALSA_BYTE_CHARS + 1, sizeof(uint8_t)); if (!bytes_string_buffer) return -ENOMEM; dst = bytes_string_buffer; for (i = 0; i < manifest_size; i++) { snprintf(dst, ALSA_BYTE_CHARS + 1, "0x%02x,", *manifest_buffer); dst += ALSA_BYTE_CHARS; manifest_buffer++; } for (i = 0; i < nhlt_size; i++) { snprintf(dst, ALSA_BYTE_CHARS + 1, "0x%02x,", *nhlt_buffer); dst += ALSA_BYTE_CHARS; nhlt_buffer++; } /* remove the last comma... */ dst--; *dst = '\0'; *src = bytes_string_buffer; return 0; } static int merge_manifest_data(snd_config_t *cfg, uint8_t *manifest_buffer, uint32_t manifest_size, uint8_t *nhlt_buffer, uint32_t nhlt_size) { const char *data_name = "SOF ABI"; snd_config_t *data_section; snd_config_t *manifest; snd_config_t *old_bytes; snd_config_t *new_bytes; char *src = NULL; int ret; /* merge manifest struct and nhlt bytes as new config into existing SectionData*/ ret = snd_config_search(cfg, "SectionData", &data_section); if (ret < 0) return ret; ret = snd_config_search(data_section, data_name, &manifest); if (ret < 0) return ret; ret = snd_config_search(manifest, "bytes", &old_bytes); if (ret < 0) return ret; ret = snd_config_make(&new_bytes, "bytes", SND_CONFIG_TYPE_STRING); if (ret < 0) goto err; ret = print_as_hex_bytes(manifest_buffer, manifest_size, nhlt_buffer, nhlt_size, &src); if (ret < 0) goto err; ret = snd_config_set_string(new_bytes, src); if (ret < 0) goto err; ret = snd_config_merge(old_bytes, new_bytes, true); if (ret < 0) goto err; free(src); return 0; err: if (new_bytes) snd_config_delete(new_bytes); if (src) free(src); return ret; } static void save_nhlt_binary(struct nhlt *blob, struct endpoint_descriptor **eps, snd_config_t *cfg) { const char *bin_file = NULL; snd_config_t *defines; FILE *fp; int ret; int i; ret = snd_config_search(cfg, "Define.NHLT_BIN", &defines); if (ret < 0) return; if (snd_config_get_string(defines, &bin_file) < 0) return; fp = fopen(bin_file, "wb"); if (fp == NULL) { fprintf(stderr, "can't open nhlt binary output file %s\n", bin_file); return; } fprintf(stdout, "saving nhlt as binary in %s\n", bin_file); fwrite(blob, 1, sizeof(struct nhlt), fp); for (i = 0; i < blob->endpoint_count; i++) fwrite(eps[i], eps[i]->length, sizeof(uint8_t), fp); fclose(fp); } static int manifest_create(snd_config_t *input, uint8_t **manifest_buffer, uint32_t *size, uint32_t nhlt_size) { struct sof_manifest_tlv manifest_tlv; struct sof_manifest manifest; snd_config_t *data_section; snd_config_t *data; snd_config_t *old_bytes; uint32_t manifest_size; uint8_t *byte_buffer; const char *abi; uint8_t *top_p; uint8_t *dst; int ret; int i; ret = snd_config_search(input, "SectionData", &data_section); if (ret < 0) return ret; ret = snd_config_search(data_section, "SOF ABI", &data); if (ret < 0) return ret; ret = snd_config_search(data, "bytes", &old_bytes); if (ret < 0) return ret; ret = snd_config_get_string(old_bytes, &abi); if (ret < 0) return ret; /* we have something funny in abi string */ if (strlen(abi) != SOF_ABI_CHARS) return -EINVAL; manifest.count = 1; manifest_tlv.type = SOF_MANIFEST_DATA_TYPE_NHLT; manifest_tlv.size = nhlt_size; manifest_size = sizeof(struct sof_manifest) + sizeof(struct sof_manifest_tlv); byte_buffer = calloc(manifest_size, sizeof(uint8_t)); if (!byte_buffer) return -ENOMEM; *size = manifest_size; dst = byte_buffer; /* copy the ABI version bytes */ for (i = 0; i < 6; i++) sscanf(&abi[i * ALSA_BYTE_CHARS], "%" SCNx8, dst++); /* set the count */ *dst++ = manifest.count; *dst++ = manifest.count >> 2; top_p = (uint8_t *)&manifest_tlv; for (i = 0; i < sizeof(struct sof_manifest_tlv); i++) *dst++ = *top_p++; *manifest_buffer = byte_buffer; return 0; } static int nhlt_get_flat_buffer(struct nhlt *blob, struct endpoint_descriptor **eps, uint32_t eps_count, uint32_t *size, uint8_t **nhlt_buffer) { uint8_t *top_p = (uint8_t *)blob; struct endpoint_descriptor *ep; uint8_t *byte_buffer; uint32_t nhlt_size; uint8_t *ep_p; uint8_t *dst; int i, j; /* get blob total size */ nhlt_size = sizeof(struct nhlt); for (i = 0; i < eps_count; i++) { if (eps[i]) nhlt_size += eps[i]->length; } *size = nhlt_size; byte_buffer = calloc(nhlt_size, sizeof(uint8_t)); if (!byte_buffer) return -ENOMEM; dst = byte_buffer; for (i = 0; i < sizeof(struct nhlt); i++) *dst++ = *top_p++; for (i = 0; i < blob->endpoint_count; i++) { ep = eps[i]; ep_p = (uint8_t *)ep; for (j = 0; j < ep->length; j++) *dst++ = *ep_p++; } *nhlt_buffer = byte_buffer; return 0; } /* called at the end of topology pre-processing, create flat buffer from variable size nhlt */ static int nhlt_create(struct intel_nhlt_params *nhlt, snd_config_t *input, snd_config_t *output, uint8_t **nhlt_buffer, uint32_t *nhlt_size) { struct endpoint_descriptor *eps[MAX_ENDPOINT_COUNT]; int eps_count = 0; struct nhlt blob; uint32_t size; uint8_t dir; int ret; int i; for (i = 0; i < MAX_ENDPOINT_COUNT; i++) eps[i] = NULL; /* we always have only 0 or 1 dmic ep */ if (nhlt_dmic_get_ep_count(nhlt)) { /* the index is always 0 in dmic case */ ret = nhlt_dmic_get_ep(nhlt, &eps[eps_count], 0); if (ret < 0) return -EINVAL; eps_count++; } /* we can have 0 to several ssp eps */ for (i = 0; i < nhlt_ssp_get_ep_count(nhlt); i++) { nhlt_ssp_get_dir(nhlt, i, &dir); /* duplicate endpoint for duplex dai */ if (dir > NHLT_ENDPOINT_DIRECTION_FEEDBACK_FOR_RENDER) { ret = nhlt_ssp_get_ep(nhlt, &eps[eps_count], i, NHLT_ENDPOINT_DIRECTION_RENDER); if (ret < 0) goto err; eps_count++; ret = nhlt_ssp_get_ep(nhlt, &eps[eps_count], i, NHLT_ENDPOINT_DIRECTION_CAPTURE); } else { ret = nhlt_ssp_get_ep(nhlt, &eps[eps_count], i, dir); } if (ret < 0) goto err; eps_count++; } /* we don't have endpoints */ if (!eps_count) return 0; uint8_t sig[4] = {'N', 'H', 'L', 'T'}; blob.efi_acpi.signature = *((uint32_t *)sig); blob.efi_acpi.length = 0; blob.efi_acpi.revision = 0; blob.efi_acpi.checksum = 0; for (i = 0; i < 6; i++) blob.efi_acpi.oem_id[i] = 0; blob.efi_acpi.oem_table_id = 0; blob.efi_acpi.oem_revision = 0; blob.efi_acpi.creator_id = 0; blob.efi_acpi.creator_revision = 0; blob.endpoint_count = eps_count; /* get blob total size */ size = sizeof(struct nhlt); for (i = 0; i < eps_count; i++) { if (eps[i]) size += eps[i]->length; } /* add the total length to top level struct */ blob.efi_acpi.length = size; debug_print_nhlt(&blob, eps); save_nhlt_binary(&blob, eps, input); ret = nhlt_get_flat_buffer(&blob, eps, eps_count, nhlt_size, nhlt_buffer); err: /* remove all enpoints */ for (i = 0; i < eps_count; i++) free(eps[i]); return ret; } static int do_nhlt(struct intel_nhlt_params *nhlt, snd_config_t *input, snd_config_t *output) { uint8_t *manifest_buffer = NULL; uint8_t *nhlt_buffer = NULL; uint32_t manifest_size; uint32_t nhlt_size = 0; int ret = 0; ret = nhlt_create(nhlt, input, output, &nhlt_buffer, &nhlt_size); if (ret) { fprintf(stderr, "can't create nhlt blob, err %d\n", ret); return ret; } ret = manifest_create(output, &manifest_buffer, &manifest_size, nhlt_size); if (ret) { fprintf(stderr, "can't re-create manifest, err %d\n", ret); goto err; } ret = merge_manifest_data(output, manifest_buffer, manifest_size, nhlt_buffer, nhlt_size); if (ret) fprintf(stderr, "can't merge manifest data, err %d\n", ret); err: if (manifest_buffer) free(manifest_buffer); if (nhlt_buffer) free(nhlt_buffer); return ret; } SND_TOPOLOGY_PLUGIN_DEFINE_FUNC(nhlt) { snd_config_iterator_t i, i2, next, next2; struct intel_nhlt_params nhlt; snd_config_t *n, *n2; snd_config_t *items; const char *id, *id2; int ret; /* initialize the internal structs */ ret = nhlt_ssp_init_params(&nhlt); if (ret < 0) return ret; ret = nhlt_dmic_init_params(&nhlt); if (ret < 0) return ret; /* find DAIs and set internal parameters */ ret = snd_config_search(input, "Object.Dai", &items); if (ret < 0) return ret; snd_config_for_each(i, next, items) { n = snd_config_iterator_entry(i); if (snd_config_get_id(n, &id) < 0) continue; snd_config_for_each(i2, next2, n) { n2 = snd_config_iterator_entry(i2); if (snd_config_get_id(n2, &id2) < 0) continue; /* set dai parameters here */ if (!strncmp(id, "DMIC", 4)) { ret = nhlt_dmic_set_params(&nhlt, n2, input); if (ret < 0) return ret; } if (!strncmp(id, "SSP", 3)) { ret = nhlt_ssp_set_params(&nhlt, n2, input); if (ret < 0) return ret; } } } /* create the nhlt blob from internal structs */ ret = do_nhlt(&nhlt, input, output); if (ret) fprintf(stderr, "error in nhlt processing\n"); free(nhlt.ssp_params); free(nhlt.dmic_params); return 0; }