From 4831c5805c51f24647f4566ad7f97f4126ab4030 Mon Sep 17 00:00:00 2001 From: Seppo Ingalsuo Date: Fri, 20 Sep 2024 15:34:54 +0300 Subject: [PATCH 1/8] Tools: Testbench: Add UUID based component load This patch copies similar change from SOF plugin. The load of host copier, DAI copier, PGA, and process components are updated to use UUID that is appended to end of IPC4 base module configuration and other component specific init IPC. Signed-off-by: Seppo Ingalsuo --- tools/testbench/include/testbench/file.h | 5 ++ .../include/testbench/topology_ipc4.h | 7 +++ tools/testbench/topology_ipc3.c | 5 -- tools/testbench/topology_ipc4.c | 48 ++++++++++++------- 4 files changed, 42 insertions(+), 23 deletions(-) diff --git a/tools/testbench/include/testbench/file.h b/tools/testbench/include/testbench/file.h index 869772b5ee62..889649094429 100644 --- a/tools/testbench/include/testbench/file.h +++ b/tools/testbench/include/testbench/file.h @@ -19,6 +19,11 @@ #define FILE_BYTES_TO_S16_SAMPLES(s) ((s) >> 1) #define FILE_BYTES_TO_S32_SAMPLES(s) ((s) >> 2) +/* bfc7488c-75aa-4ce8-9dbed8da08a698c2 */ +static const struct sof_uuid tb_file_uuid = { + 0xbfc7488c, 0x75aa, 0x4ce8, {0x9d, 0xbe, 0xd8, 0xda, 0x08, 0xa6, 0x98, 0xc2} +}; + /* file component modes */ enum file_mode { FILE_READ = 0, diff --git a/tools/testbench/include/testbench/topology_ipc4.h b/tools/testbench/include/testbench/topology_ipc4.h index 66f4a10b00ef..33ab522e357c 100644 --- a/tools/testbench/include/testbench/topology_ipc4.h +++ b/tools/testbench/include/testbench/topology_ipc4.h @@ -12,6 +12,13 @@ #define TB_IPC4_MAX_TPLG_OBJECT_SIZE 4096 #define TB_IPC4_MAX_MSG_SIZE 384 +#define TB_PROCESS_MODULE_ID 0x95 +#define TB_PGA_MODULE_ID 0x6 +#define TB_FILE_OUT_AIF_MODULE_ID 0x9a +#define TB_FILE_IN_AIF_MODULE_ID 0x9b +#define TB_FILE_OUT_DAI_MODULE_ID 0x9c +#define TB_FILE_IN_DAI_MODULE_ID 0x9d + int tb_delete_pipeline(struct testbench_prm *tp, struct tplg_pipeline_info *pipe_info); int tb_free_all_pipelines(struct testbench_prm *tp); int tb_free_route(struct testbench_prm *tp, struct tplg_route_info *route_info); diff --git a/tools/testbench/topology_ipc3.c b/tools/testbench/topology_ipc3.c index a0082a370613..48af3fd99ccc 100644 --- a/tools/testbench/topology_ipc3.c +++ b/tools/testbench/topology_ipc3.c @@ -28,11 +28,6 @@ #define MAX_TPLG_OBJECT_SIZE 4096 -/* bfc7488c-75aa-4ce8-9dbed8da08a698c2 */ -static const struct sof_uuid tb_file_uuid = { - 0xbfc7488c, 0x75aa, 0x4ce8, {0x9d, 0xbe, 0xd8, 0xda, 0x08, 0xa6, 0x98, 0xc2} -}; - /* load asrc dapm widget */ static int tb_register_asrc(struct testbench_prm *tp, struct tplg_context *ctx) { diff --git a/tools/testbench/topology_ipc4.c b/tools/testbench/topology_ipc4.c index 8fd35d6d57db..6ece690d3b74 100644 --- a/tools/testbench/topology_ipc4.c +++ b/tools/testbench/topology_ipc4.c @@ -581,12 +581,11 @@ int tb_new_aif_in_out(struct testbench_prm *tp, int dir) if (ret < 0) return ret; - comp_info->ipc_payload = calloc(sizeof(struct ipc4_file_module_cfg), 1); + comp_info->ipc_size = sizeof(struct ipc4_file_module_cfg) + sizeof(struct sof_uuid); + comp_info->ipc_payload = calloc(comp_info->ipc_size, 1); if (!comp_info->ipc_payload) return -ENOMEM; - comp_info->ipc_size = sizeof(struct ipc4_file_module_cfg); - if (dir == SOF_IPC_STREAM_PLAYBACK) { /* Set from testbench command line*/ file = (struct ipc4_file_module_cfg *)comp_info->ipc_payload; @@ -597,7 +596,7 @@ int tb_new_aif_in_out(struct testbench_prm *tp, int dir) file->config.direction = dir; comp_info->instance_id = tp->instance_ids[SND_SOC_TPLG_DAPM_AIF_IN]++; - comp_info->module_id = 0x9a; + comp_info->module_id = TB_FILE_OUT_AIF_MODULE_ID; if (tb_is_pipeline_enabled(tp, ctx->pipeline_id)) { if (tp->input_file_index >= tp->input_file_num) { fprintf(stderr, "error: not enough input files for aif\n"); @@ -620,7 +619,7 @@ int tb_new_aif_in_out(struct testbench_prm *tp, int dir) file->config.direction = dir; comp_info->instance_id = tp->instance_ids[SND_SOC_TPLG_DAPM_AIF_OUT]++; - comp_info->module_id = 0x9b; + comp_info->module_id = TB_FILE_IN_AIF_MODULE_ID; if (tb_is_pipeline_enabled(tp, ctx->pipeline_id)) { if (tp->output_file_index >= tp->output_file_num) { fprintf(stderr, "error: not enough output files for aif\n"); @@ -632,9 +631,11 @@ int tb_new_aif_in_out(struct testbench_prm *tp, int dir) tp->fw[tp->output_file_index].pipeline_id = ctx->pipeline_id; tp->output_file_index++; } - tb_setup_widget_ipc_msg(comp_info); } + tb_setup_widget_ipc_msg(comp_info); + memcpy(comp_info->ipc_payload + sizeof(struct ipc4_file_module_cfg), &tb_file_uuid, + sizeof(struct sof_uuid)); return 0; } @@ -649,12 +650,11 @@ int tb_new_dai_in_out(struct testbench_prm *tp, int dir) if (ret < 0) return ret; - comp_info->ipc_payload = calloc(sizeof(struct ipc4_file_module_cfg), 1); + comp_info->ipc_size = sizeof(struct ipc4_file_module_cfg) + sizeof(struct sof_uuid); + comp_info->ipc_payload = calloc(comp_info->ipc_size, 1); if (!comp_info->ipc_payload) return -ENOMEM; - comp_info->ipc_size = sizeof(struct ipc4_file_module_cfg); - if (dir == SOF_IPC_STREAM_PLAYBACK) { /* Set from testbench command line*/ file = (struct ipc4_file_module_cfg *)comp_info->ipc_payload; @@ -665,7 +665,7 @@ int tb_new_dai_in_out(struct testbench_prm *tp, int dir) file->config.direction = dir; comp_info->instance_id = tp->instance_ids[SND_SOC_TPLG_DAPM_DAI_OUT]++; - comp_info->module_id = 0x9c; + comp_info->module_id = TB_FILE_OUT_DAI_MODULE_ID; if (tb_is_pipeline_enabled(tp, ctx->pipeline_id)) { if (tp->output_file_index >= tp->output_file_num) { fprintf(stderr, "error: not enough output files for dai\n"); @@ -687,7 +687,7 @@ int tb_new_dai_in_out(struct testbench_prm *tp, int dir) file->config.direction = dir; comp_info->instance_id = tp->instance_ids[SND_SOC_TPLG_DAPM_DAI_IN]++; - comp_info->module_id = 0x9d; + comp_info->module_id = TB_FILE_IN_DAI_MODULE_ID; if (tb_is_pipeline_enabled(tp, ctx->pipeline_id)) { if (tp->input_file_index >= tp->input_file_num) { fprintf(stderr, "error: not enough input files for dai\n"); @@ -699,9 +699,11 @@ int tb_new_dai_in_out(struct testbench_prm *tp, int dir) tp->fr[tp->input_file_index].pipeline_id = ctx->pipeline_id; tp->input_file_index++; } - tb_setup_widget_ipc_msg(comp_info); } + tb_setup_widget_ipc_msg(comp_info); + memcpy(comp_info->ipc_payload + sizeof(struct ipc4_file_module_cfg), &tb_file_uuid, + sizeof(struct sof_uuid)); return 0; } @@ -711,17 +713,20 @@ int tb_new_pga(struct testbench_prm *tp) struct tplg_comp_info *comp_info = ctx->current_comp_info; struct ipc4_peak_volume_config volume; struct snd_soc_tplg_ctl_hdr *tplg_ctl; + size_t uuid_offset; int ret; - comp_info->ipc_size = - sizeof(struct ipc4_peak_volume_config) + sizeof(struct ipc4_base_module_cfg); + comp_info->ipc_size = sizeof(struct ipc4_peak_volume_config); + comp_info->ipc_size += sizeof(struct ipc4_base_module_cfg); + uuid_offset = comp_info->ipc_size; + comp_info->ipc_size += sizeof(struct sof_uuid); comp_info->ipc_payload = calloc(comp_info->ipc_size, 1); if (!comp_info->ipc_payload) return -ENOMEM; /* FIXME: move this to when the widget is actually set up */ comp_info->instance_id = tp->instance_ids[SND_SOC_TPLG_DAPM_PGA]++; - comp_info->module_id = 0x6; + comp_info->module_id = TB_PGA_MODULE_ID; tplg_ctl = calloc(ctx->hdr->payload_size, 1); if (!tplg_ctl) { @@ -740,6 +745,9 @@ int tb_new_pga(struct testbench_prm *tp) memcpy(comp_info->ipc_payload + sizeof(struct ipc4_base_module_cfg), &volume, sizeof(struct ipc4_peak_volume_config)); + /* copy uuid to the end of the payload */ + memcpy(comp_info->ipc_payload + uuid_offset, &comp_info->uuid, sizeof(struct sof_uuid)); + /* skip kcontrols for now */ ret = tplg_create_controls(ctx, ctx->widget->num_kcontrols, tplg_ctl, ctx->hdr->payload_size, &volume); @@ -773,8 +781,8 @@ int tb_new_process(struct testbench_prm *tp) if (!tplg_ctl) return -ENOMEM; - /* only base config supported for now. extn support will be added later */ - comp_info->ipc_size = sizeof(struct ipc4_base_module_cfg); + /* use base config variant with uuid */ + comp_info->ipc_size = sizeof(struct ipc4_base_module_cfg) + sizeof(struct sof_uuid); comp_info->ipc_payload = calloc(comp_info->ipc_size, 1); if (!comp_info->ipc_payload) { ret = ENOMEM; @@ -783,7 +791,7 @@ int tb_new_process(struct testbench_prm *tp) /* FIXME: move this to when the widget is actually set up */ comp_info->instance_id = tp->instance_ids[SND_SOC_TPLG_DAPM_EFFECT]++; - comp_info->module_id = 0x9e; /* dcblock */ + comp_info->module_id = TB_PROCESS_MODULE_ID; /* skip kcontrols for now, set object to NULL */ ret = tplg_create_controls(ctx, ctx->widget->num_kcontrols, tplg_ctl, @@ -795,6 +803,10 @@ int tb_new_process(struct testbench_prm *tp) tb_setup_widget_ipc_msg(comp_info); + /* copy uuid to the end of the payload */ + memcpy(comp_info->ipc_payload + sizeof(struct ipc4_base_module_cfg), &comp_info->uuid, + sizeof(struct sof_uuid)); + /* TODO: drop tplg_ctl to avoid memory leak. Need to store and handle this * to support controls. */ From dbeaa6944d08ad973bc864fcd1bc2494d7e646e5 Mon Sep 17 00:00:00 2001 From: Seppo Ingalsuo Date: Wed, 25 Sep 2024 17:56:48 +0300 Subject: [PATCH 2/8] Tools: Testbench: Add support for parsing controls from topology This adds parse of controls and set of byte control to components after initialize. Control types mixer, switch, enum are parsed but not yet applied to components. The code is copied and adapted from SOF plugin. As addition the bytes control is split to several messages if the IPC message size limit is reached. Signed-off-by: Seppo Ingalsuo --- .../include/testbench/topology_ipc4.h | 10 +- tools/testbench/include/testbench/utils.h | 25 ++ tools/testbench/topology_ipc4.c | 285 +++++++++++++++++- tools/testbench/utils_ipc4.c | 34 ++- 4 files changed, 350 insertions(+), 4 deletions(-) diff --git a/tools/testbench/include/testbench/topology_ipc4.h b/tools/testbench/include/testbench/topology_ipc4.h index 33ab522e357c..29a42532e92d 100644 --- a/tools/testbench/include/testbench/topology_ipc4.h +++ b/tools/testbench/include/testbench/topology_ipc4.h @@ -10,7 +10,13 @@ #include "testbench/utils.h" #define TB_IPC4_MAX_TPLG_OBJECT_SIZE 4096 -#define TB_IPC4_MAX_MSG_SIZE 384 + +/* See module_set_large_config() where message fragment is + * MAILBOX_DSPBOX_SIZE. The add of header size (8) is because + * testbench and plugin have the set large config header in + * same memory as the payload. + */ +#define TB_IPC4_MAX_MSG_SIZE (MAILBOX_DSPBOX_SIZE + sizeof(struct ipc4_module_large_config)) #define TB_PROCESS_MODULE_ID 0x95 #define TB_PGA_MODULE_ID 0x6 @@ -31,6 +37,8 @@ int tb_new_dai_in_out(struct testbench_prm *tp, int dir); int tb_new_pga(struct testbench_prm *tp); int tb_new_process(struct testbench_prm *tp); int tb_pipelines_set_state(struct testbench_prm *tp, int state, int dir); +int tb_send_bytes_data(struct tb_mq_desc *ipc_tx, struct tb_mq_desc *ipc_rx, + uint32_t module_id, uint32_t instance_id, struct sof_abi_hdr *abi); int tb_set_reset_state(struct testbench_prm *tp); int tb_set_running_state(struct testbench_prm *tp); int tb_set_up_pipeline(struct testbench_prm *tp, struct tplg_pipeline_info *pipe_info); diff --git a/tools/testbench/include/testbench/utils.h b/tools/testbench/include/testbench/utils.h index af299d67a21a..7da46fe780c1 100644 --- a/tools/testbench/include/testbench/utils.h +++ b/tools/testbench/include/testbench/utils.h @@ -37,6 +37,9 @@ struct file_comp_lookup { #define TB_NAME_SIZE 256 #define TB_MAX_CONFIG_COUNT 2 #define TB_MAX_CONFIG_NAME_SIZE 64 +#define TB_MAX_VOLUME_SIZE 120 +#define TB_MAX_DATA_SIZE 512 +#define TB_MAX_CTLS 16 struct tb_mq_desc { char queue_name[TB_NAME_SIZE]; @@ -52,6 +55,27 @@ struct tb_config { int channels; unsigned long format; }; + +struct tb_ctl { + unsigned int module_id; + unsigned int instance_id; + unsigned int type; + unsigned int volume_table[TB_MAX_VOLUME_SIZE]; + unsigned int index; + char data[TB_MAX_DATA_SIZE]; + union { + struct snd_soc_tplg_mixer_control mixer_ctl; + struct snd_soc_tplg_enum_control enum_ctl; + struct snd_soc_tplg_bytes_control bytes_ctl; + }; +}; + +struct tb_glb_state { + char magic[8]; /* SOF_MAGIC */ + uint32_t num_ctls; /* number of ctls */ + size_t size; /* size of this structure in bytes */ + struct tb_ctl *ctl; +}; #endif /* @@ -114,6 +138,7 @@ struct testbench_prm { struct tb_config config[TB_MAX_CONFIG_COUNT]; int num_configs; size_t period_size; + struct tb_glb_state glb_ctx; #endif }; diff --git a/tools/testbench/topology_ipc4.c b/tools/testbench/topology_ipc4.c index 6ece690d3b74..227eeabd22b6 100644 --- a/tools/testbench/topology_ipc4.c +++ b/tools/testbench/topology_ipc4.c @@ -9,6 +9,7 @@ #if CONFIG_IPC_MAJOR_4 #include +#include #include #include #include @@ -793,9 +794,9 @@ int tb_new_process(struct testbench_prm *tp) comp_info->instance_id = tp->instance_ids[SND_SOC_TPLG_DAPM_EFFECT]++; comp_info->module_id = TB_PROCESS_MODULE_ID; - /* skip kcontrols for now, set object to NULL */ + /* set up kcontrols */ ret = tplg_create_controls(ctx, ctx->widget->num_kcontrols, tplg_ctl, - ctx->hdr->payload_size, NULL); + ctx->hdr->payload_size, comp_info); if (ret < 0) { fprintf(stderr, "error: loading controls\n"); goto out; @@ -1002,6 +1003,198 @@ static int tb_load_widget(struct testbench_prm *tp) return 0; } +#define SOF_IPC4_VOL_ZERO_DB 0x7fffffff +#define VOLUME_FWL 16 +/* + * Constants used in the computation of linear volume gain + * from dB gain 20th root of 10 in Q1.16 fixed-point notation + */ +#define VOL_TWENTIETH_ROOT_OF_TEN 73533 +/* 40th root of 10 in Q1.16 fixed-point notation*/ +#define VOL_FORTIETH_ROOT_OF_TEN 69419 + +/* 0.5 dB step value in topology TLV */ +#define VOL_HALF_DB_STEP 50 + +/* + * Function to truncate an unsigned 64-bit number + * by x bits and return 32-bit unsigned number. This + * function also takes care of rounding while truncating + */ +static uint32_t vol_shift_64(uint64_t i, uint32_t x) +{ + if (x == 0) + return (uint32_t)i; + + /* do not truncate more than 32 bits */ + if (x > 32) + x = 32; + + return (uint32_t)(((i >> (x - 1)) + 1) >> 1); +} + +/* + * Function to compute a ** exp where, + * a is a fractional number represented by a fixed-point integer with a fractional word length + * of "fwl" + * exp is an integer + * fwl is the fractional word length + * Return value is a fractional number represented by a fixed-point integer with a fractional + * word length of "fwl" + */ +static uint32_t vol_pow32(uint32_t a, int exp, uint32_t fwl) +{ + int i, iter; + uint32_t power = 1 << fwl; + unsigned long long numerator; + + /* if exponent is 0, return 1 */ + if (exp == 0) + return power; + + /* determine the number of iterations based on the exponent */ + if (exp < 0) + iter = exp * -1; + else + iter = exp; + + /* multiply a "iter" times to compute power */ + for (i = 0; i < iter; i++) { + /* + * Product of 2 Qx.fwl fixed-point numbers yields a Q2*x.2*fwl + * Truncate product back to fwl fractional bits with rounding + */ + power = vol_shift_64((uint64_t)power * a, fwl); + } + + if (exp > 0) { + /* if exp is positive, return the result */ + return power; + } + + /* if exp is negative, return the multiplicative inverse */ + numerator = (uint64_t)1 << (fwl << 1); + numerator /= power; + + return (uint32_t)numerator; +} + +/* + * Function to calculate volume gain from TLV data. + * This function can only handle gain steps that are multiples of 0.5 dB + */ +static uint32_t vol_compute_gain(uint32_t value, struct snd_soc_tplg_tlv_dbscale *scale) +{ + int dB_gain; + uint32_t linear_gain; + int f_step; + + /* mute volume */ + if (value == 0 && scale->mute) + return 0; + + /* compute dB gain from tlv. tlv_step in topology is multiplied by 100 */ + dB_gain = (int)scale->min / 100 + (value * scale->step) / 100; + + /* compute linear gain represented by fixed-point int with VOLUME_FWL fractional bits */ + linear_gain = vol_pow32(VOL_TWENTIETH_ROOT_OF_TEN, dB_gain, VOLUME_FWL); + + /* extract the fractional part of volume step */ + f_step = scale->step - (scale->step / 100); + + /* if volume step is an odd multiple of 0.5 dB */ + if (f_step == VOL_HALF_DB_STEP && (value & 1)) + linear_gain = vol_shift_64((uint64_t)linear_gain * VOL_FORTIETH_ROOT_OF_TEN, + VOLUME_FWL); + + return linear_gain; +} + +/* helper function to add new kcontrols to the list of kcontrols */ +static int tb_kcontrol_cb_new(struct snd_soc_tplg_ctl_hdr *tplg_ctl, + void *comp, void *arg, int index) +{ + struct tplg_comp_info *comp_info = comp; + struct testbench_prm *tp = arg; + struct tb_glb_state *glb = &tp->glb_ctx; + struct tb_ctl *ctl; + struct snd_soc_tplg_mixer_control *tplg_mixer; + struct snd_soc_tplg_enum_control *tplg_enum; + struct snd_soc_tplg_bytes_control *tplg_bytes; + + if (glb->num_ctls >= TB_MAX_CTLS) { + fprintf(stderr, "Error: Too many controls already.\n"); + return -EINVAL; + } + + fprintf(stderr, "Info: Found control type %d: %s\n", tplg_ctl->type, tplg_ctl->name); + + switch (tplg_ctl->ops.info) { + case SND_SOC_TPLG_CTL_RANGE: + case SND_SOC_TPLG_CTL_STROBE: + fprintf(stderr, "Warning: Not supported ctl type %d\n", tplg_ctl->type); + return 0; + case SND_SOC_TPLG_CTL_VOLSW: + case SND_SOC_TPLG_CTL_VOLSW_SX: + case SND_SOC_TPLG_CTL_VOLSW_XR_SX: + tplg_mixer = (struct snd_soc_tplg_mixer_control *)tplg_ctl; + struct snd_soc_tplg_ctl_tlv *tlv; + struct snd_soc_tplg_tlv_dbscale *scale; + int i; + + glb->size += sizeof(struct tb_ctl); + ctl = &glb->ctl[glb->num_ctls++]; + ctl->module_id = comp_info->module_id; + ctl->instance_id = comp_info->instance_id; + ctl->mixer_ctl = *tplg_mixer; + ctl->index = index; + ctl->type = tplg_ctl->type; + tlv = &tplg_ctl->tlv; + scale = &tlv->scale; + + /* populate the volume table */ + for (i = 0; i < tplg_mixer->max + 1 ; i++) { + uint32_t val = vol_compute_gain(i, scale); + + /* Can be over Q1.31, need to saturate */ + uint64_t q31val = ((uint64_t)val) << 15; + + ctl->volume_table[i] = q31val > SOF_IPC4_VOL_ZERO_DB ? + SOF_IPC4_VOL_ZERO_DB : q31val; + } + break; + case SND_SOC_TPLG_CTL_ENUM: + case SND_SOC_TPLG_CTL_ENUM_VALUE: + tplg_enum = (struct snd_soc_tplg_enum_control *)tplg_ctl; + glb->size += sizeof(struct tb_ctl); + ctl = &glb->ctl[glb->num_ctls++]; + ctl->module_id = comp_info->module_id; + ctl->instance_id = comp_info->instance_id; + ctl->enum_ctl = *tplg_enum; + ctl->index = index; + ctl->type = tplg_ctl->type; + break; + case SND_SOC_TPLG_CTL_BYTES: + { + tplg_bytes = (struct snd_soc_tplg_bytes_control *)tplg_ctl; + glb->size += sizeof(struct tb_ctl); + ctl = &glb->ctl[glb->num_ctls++]; + ctl->module_id = comp_info->module_id; + ctl->instance_id = comp_info->instance_id; + ctl->bytes_ctl = *tplg_bytes; + ctl->index = index; + ctl->type = tplg_ctl->type; + memcpy(ctl->data, tplg_bytes->priv.data, tplg_bytes->priv.size); + break; + } + default: + fprintf(stderr, "Error: Invalid ctl type %d\n", tplg_ctl->type); + return -EINVAL; + } + + return 0; +} + /* parse topology file and set up pipeline */ int tb_parse_topology(struct testbench_prm *tp) @@ -1014,6 +1207,13 @@ int tb_parse_topology(struct testbench_prm *tp) FILE *file; ctx->ipc_major = 4; + ctx->ctl_arg = tp; + ctx->ctl_cb = tb_kcontrol_cb_new; + tp->glb_ctx.ctl = calloc(TB_MAX_CTLS, sizeof(struct tb_ctl)); + if (!tp->glb_ctx.ctl) { + fprintf(stderr, "error: failed to allocate for controls.\n"); + return -ENOMEM; + } /* open topology file */ file = fopen(ctx->tplg_file, "rb"); @@ -1289,4 +1489,85 @@ int tb_free_route(struct testbench_prm *tp, struct tplg_route_info *route_info) return 0; } +static void tb_ctl_ipc_message(struct ipc4_module_large_config *config, int param_id, + size_t offset_or_size, uint32_t module_id, uint32_t instance_id, + uint32_t type) +{ + config->primary.r.type = type; + config->primary.r.msg_tgt = SOF_IPC4_MESSAGE_TARGET_MODULE_MSG; + config->primary.r.rsp = SOF_IPC4_MESSAGE_DIR_MSG_REQUEST; + config->primary.r.module_id = module_id; + config->primary.r.instance_id = instance_id; + + config->extension.r.data_off_size = offset_or_size; + config->extension.r.large_param_id = param_id; +} + +int tb_send_bytes_data(struct tb_mq_desc *ipc_tx, struct tb_mq_desc *ipc_rx, + uint32_t module_id, uint32_t instance_id, struct sof_abi_hdr *abi) +{ + struct ipc4_module_large_config config = {{ 0 }}; + struct ipc4_message_reply reply; + void *msg; + int ret; + size_t chunk_size; + size_t msg_size; + size_t msg_size_full = sizeof(config) + abi->size; + size_t remaining = abi->size; + size_t payload_limit = TB_IPC4_MAX_MSG_SIZE - sizeof(config); + size_t offset = 0; + + /* allocate memory for IPC message */ + msg_size = MIN(msg_size_full, TB_IPC4_MAX_MSG_SIZE); + msg = calloc(msg_size, 1); + if (!msg) + return -ENOMEM; + + config.extension.r.init_block = 1; + + /* configure the IPC message */ + tb_ctl_ipc_message(&config, abi->type, remaining, module_id, instance_id, + SOF_IPC4_MOD_LARGE_CONFIG_SET); + + do { + if (remaining > payload_limit) { + chunk_size = payload_limit; + } else { + chunk_size = remaining; + config.extension.r.final_block = 1; + } + + if (offset) { + config.extension.r.init_block = 0; + tb_ctl_ipc_message(&config, abi->type, offset, module_id, instance_id, + SOF_IPC4_MOD_LARGE_CONFIG_SET); + } + + /* set the IPC message data */ + memcpy(msg, &config, sizeof(config)); + memcpy(msg + sizeof(config), (char *)abi->data + offset, chunk_size); + + /* send the message and check status */ + ret = tb_mq_cmd_tx_rx(ipc_tx, ipc_rx, msg, chunk_size + sizeof(config), + &reply, sizeof(reply)); + if (ret < 0) { + fprintf(stderr, "Error: Failed to send IPC to set bytes data.\n"); + goto out; + } + + if (reply.primary.r.status != IPC4_SUCCESS) { + fprintf(stderr, "Error: Failed with status %d.\n", reply.primary.r.status); + ret = -EINVAL; + goto out; + } + + offset += chunk_size; + remaining -= chunk_size; + } while (remaining); + +out: + free(msg); + return ret; +} + #endif /* CONFIG_IPC_MAJOR_4 */ diff --git a/tools/testbench/utils_ipc4.c b/tools/testbench/utils_ipc4.c index a86a8e02c585..ec9557c91a51 100644 --- a/tools/testbench/utils_ipc4.c +++ b/tools/testbench/utils_ipc4.c @@ -251,7 +251,10 @@ static int tb_prepare_widgets_capture(struct testbench_prm *tp, struct tplg_pcm_ static int tb_set_up_widget(struct testbench_prm *tp, struct tplg_comp_info *comp_info) { struct tplg_pipeline_info *pipe_info = comp_info->pipe_info; + struct tb_glb_state *glb = &tp->glb_ctx; + struct tb_ctl *ctl; int ret; + int i; pipe_info->usage_count++; @@ -265,7 +268,35 @@ static int tb_set_up_widget(struct testbench_prm *tp, struct tplg_comp_info *com } /* now set up the widget */ - return tb_set_up_widget_ipc(tp, comp_info); + ret = tb_set_up_widget_ipc(tp, comp_info); + if (ret < 0) + return ret; + + /* send kcontrol bytes data */ + for (i = 0; i < glb->num_ctls; i++) { + struct sof_abi_hdr *abi; + + ctl = &glb->ctl[i]; + + /* send the bytes data from kcontrols associated with current widget */ + if (ctl->module_id != comp_info->module_id || + ctl->instance_id != comp_info->instance_id || + ctl->type != SND_SOC_TPLG_TYPE_BYTES) + continue; + + abi = (struct sof_abi_hdr *)ctl->data; + + /* send IPC with kcontrol data */ + ret = tb_send_bytes_data(&tp->ipc_tx, &tp->ipc_rx, + comp_info->module_id, comp_info->instance_id, abi); + if (ret < 0) { + fprintf(stderr, "Error: Failed to set bytes data for widget %s.\n", + comp_info->name); + return ret; + } + } + + return 0; } static int tb_set_up_widgets_playback(struct testbench_prm *tp, @@ -589,6 +620,7 @@ void tb_free_topology(struct testbench_prm *tp) } free(ctx->tplg_base); + free(tp->glb_ctx.ctl); tb_debug_print("freed all pipelines, widgets, routes and pcms\n"); } From b6fd7730950fa732b1b5b447941419b37fd6c823 Mon Sep 17 00:00:00 2001 From: Seppo Ingalsuo Date: Thu, 3 Oct 2024 14:21:27 +0300 Subject: [PATCH 3/8] Audio: SRC: Move SRC IPC config definition to src_ipc.h If kept in src_common.h the inclusion to tplg_parser would create need to include even more headers from SOF. The simple src_ipc.h contains the only needed SRC IPC4 definition. The __SOF_AUDIO_SRC_SRC_H__ is updated to match header name and the ending #endif is moved file end where it should be. Signed-off-by: Seppo Ingalsuo --- src/audio/src/src_common.h | 18 +++++------------- src/audio/src/src_ipc.h | 19 +++++++++++++++++++ 2 files changed, 24 insertions(+), 13 deletions(-) create mode 100644 src/audio/src/src_ipc.h diff --git a/src/audio/src/src_common.h b/src/audio/src/src_common.h index 1963e0fcafd9..d3ed218016db 100644 --- a/src/audio/src/src_common.h +++ b/src/audio/src/src_common.h @@ -1,12 +1,12 @@ /* SPDX-License-Identifier: BSD-3-Clause * - * Copyright(c) 2017 Intel Corporation. All rights reserved. + * Copyright(c) 2017-2024 Intel Corporation. * * Author: Seppo Ingalsuo */ -#ifndef __SOF_AUDIO_SRC_SRC_H__ -#define __SOF_AUDIO_SRC_SRC_H__ +#ifndef __SOF_AUDIO_SRC_SRC_COMMON_H__ +#define __SOF_AUDIO_SRC_SRC_COMMON_H__ #include #include @@ -15,6 +15,7 @@ #include #include #include +#include "src_ipc.h" struct src_stage { const int idm; @@ -144,14 +145,6 @@ int32_t src_output_rates(void); void src_set_alignment(struct sof_source *source, struct sof_sink *sink); -#if CONFIG_IPC_MAJOR_4 -/* src component private data */ -struct ipc4_config_src { - struct ipc4_base_module_cfg base; - uint32_t sink_rate; -}; -#endif - struct comp_data { #if CONFIG_IPC_MAJOR_4 struct ipc4_config_src ipc_config; @@ -176,8 +169,6 @@ struct comp_data { void (*polyphase_func)(struct src_stage_prm *s); }; -#endif /* __SOF_AUDIO_SRC_SRC_H__ */ - #if CONFIG_IPC_MAJOR_4 int src_stream_pcm_source_rate_check(struct ipc4_config_src cfg, @@ -265,3 +256,4 @@ extern struct tr_ctx src_tr; #endif extern const struct sof_uuid SRC_UUID; +#endif /* __SOF_AUDIO_SRC_SRC_COMMON_H__ */ diff --git a/src/audio/src/src_ipc.h b/src/audio/src/src_ipc.h new file mode 100644 index 000000000000..cd6fdb6af3a2 --- /dev/null +++ b/src/audio/src/src_ipc.h @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * + * Copyright(c) 2017-2024 Intel Corporation + */ + +#ifndef __SOF_AUDIO_SRC_SRC_IPC_H__ +#define __SOF_AUDIO_SRC_SRC_IPC_H__ + +#include +#include +#include + +/* src component private data */ +struct ipc4_config_src { + struct ipc4_base_module_cfg base; + uint32_t sink_rate; +}; + +#endif /* __SOF_AUDIO_SRC_SRC_IPC_H__ */ From a768eba0b0c05ffd2a7fd5d9343728812ae06b2b Mon Sep 17 00:00:00 2001 From: Seppo Ingalsuo Date: Thu, 3 Oct 2024 15:10:02 +0300 Subject: [PATCH 4/8] Tools: Testbench: Fix SRC component load This replaces for IPC4 testbench the previous non-working version that was a copy of IPC3 version. Signed-off-by: Seppo Ingalsuo --- .../include/testbench/topology_ipc4.h | 1 + tools/testbench/topology_ipc4.c | 34 ++++++++++++++++--- .../include/tplg_parser/topology.h | 1 + tools/tplg_parser/src.c | 5 ++- 4 files changed, 36 insertions(+), 5 deletions(-) diff --git a/tools/testbench/include/testbench/topology_ipc4.h b/tools/testbench/include/testbench/topology_ipc4.h index 29a42532e92d..5e681de77584 100644 --- a/tools/testbench/include/testbench/topology_ipc4.h +++ b/tools/testbench/include/testbench/topology_ipc4.h @@ -20,6 +20,7 @@ #define TB_PROCESS_MODULE_ID 0x95 #define TB_PGA_MODULE_ID 0x6 +#define TB_SRC_MODULE_ID 0x7 #define TB_FILE_OUT_AIF_MODULE_ID 0x9a #define TB_FILE_IN_AIF_MODULE_ID 0x9b #define TB_FILE_OUT_DAI_MODULE_ID 0x9c diff --git a/tools/testbench/topology_ipc4.c b/tools/testbench/topology_ipc4.c index 227eeabd22b6..46953653a1af 100644 --- a/tools/testbench/topology_ipc4.c +++ b/tools/testbench/topology_ipc4.c @@ -431,21 +431,47 @@ int tb_pipelines_set_state(struct testbench_prm *tp, int state, int dir) static int tb_new_src(struct testbench_prm *tp) { + struct ipc4_config_src src; struct tplg_context *ctx = &tp->tplg; - char tplg_object[TB_IPC4_MAX_TPLG_OBJECT_SIZE] = {0}; - struct sof_ipc_comp_src *src = (struct sof_ipc_comp_src *)tplg_object; + struct tplg_comp_info *comp_info = ctx->current_comp_info; struct snd_soc_tplg_ctl_hdr *tplg_ctl; + size_t uuid_offset; int ret; + ret = tplg_parse_widget_audio_formats(ctx); + if (ret < 0) + return ret; + tplg_ctl = calloc(ctx->hdr->payload_size, 1); if (!tplg_ctl) return -ENOMEM; - ret = tplg_new_src(ctx, &src->comp, TB_IPC4_MAX_TPLG_OBJECT_SIZE, + comp_info->ipc_size = sizeof(struct ipc4_config_src); + uuid_offset = comp_info->ipc_size; + comp_info->ipc_size += sizeof(struct sof_uuid); + comp_info->ipc_payload = calloc(comp_info->ipc_size, 1); + if (!comp_info->ipc_payload) + return -ENOMEM; + + comp_info->instance_id = tp->instance_ids[SND_SOC_TPLG_DAPM_EFFECT]++; + comp_info->module_id = TB_SRC_MODULE_ID; + + ret = tplg_new_src(ctx, &src, sizeof(struct ipc4_config_src), tplg_ctl, ctx->hdr->payload_size); - if (ret < 0) + if (ret < 0) { fprintf(stderr, "error: failed to create SRC\n"); + goto out; + } + + /* copy volume data to ipc_payload */ + memcpy(comp_info->ipc_payload, &src, sizeof(struct ipc4_config_src)); + /* copy uuid to the end of the payload */ + memcpy(comp_info->ipc_payload + uuid_offset, &comp_info->uuid, sizeof(struct sof_uuid)); + + tb_setup_widget_ipc_msg(comp_info); + +out: free(tplg_ctl); return ret; } diff --git a/tools/tplg_parser/include/tplg_parser/topology.h b/tools/tplg_parser/include/tplg_parser/topology.h index 5cbf0588d92f..f4ca6c001a53 100644 --- a/tools/tplg_parser/include/tplg_parser/topology.h +++ b/tools/tplg_parser/include/tplg_parser/topology.h @@ -20,6 +20,7 @@ #include #include #include +#include #include "copier/copier.h" diff --git a/tools/tplg_parser/src.c b/tools/tplg_parser/src.c index fa8203c3a7aa..a6eb6149963a 100644 --- a/tools/tplg_parser/src.c +++ b/tools/tplg_parser/src.c @@ -17,6 +17,7 @@ #include #include #include +#include /* SRC - IPC3 */ static const struct sof_topology_token src3_tokens[] = { @@ -55,7 +56,9 @@ static int src_ipc3_build(struct tplg_context *ctx, void *_src) /* ASRC - IPC4 */ static const struct sof_topology_token src4_tokens[] = { - /* TODO */ + {SOF_TKN_SRC_RATE_OUT, SND_SOC_TPLG_TUPLE_TYPE_WORD, + tplg_token_get_uint32_t, + offsetof(struct ipc4_config_src, sink_rate), 0}, }; static const struct sof_topology_token_group src_ipc4_tokens[] = { From c6a0b84b2cb241d92ebe4e255d349e87eec64fa0 Mon Sep 17 00:00:00 2001 From: Seppo Ingalsuo Date: Thu, 3 Oct 2024 14:52:28 +0300 Subject: [PATCH 5/8] Tools: Testbench: Fix ASRC component load This replaces for IPC4 testbench the previous non-working version that was a copy of IPC3 version. Signed-off-by: Seppo Ingalsuo --- .../include/testbench/topology_ipc4.h | 3 +- tools/testbench/topology_ipc4.c | 36 ++++++++++++++++--- tools/tplg_parser/asrc.c | 5 ++- .../include/tplg_parser/topology.h | 1 + 4 files changed, 38 insertions(+), 7 deletions(-) diff --git a/tools/testbench/include/testbench/topology_ipc4.h b/tools/testbench/include/testbench/topology_ipc4.h index 5e681de77584..3c9f9d60c70c 100644 --- a/tools/testbench/include/testbench/topology_ipc4.h +++ b/tools/testbench/include/testbench/topology_ipc4.h @@ -18,9 +18,10 @@ */ #define TB_IPC4_MAX_MSG_SIZE (MAILBOX_DSPBOX_SIZE + sizeof(struct ipc4_module_large_config)) -#define TB_PROCESS_MODULE_ID 0x95 #define TB_PGA_MODULE_ID 0x6 #define TB_SRC_MODULE_ID 0x7 +#define TB_ASRC_MODULE_ID 0x8 +#define TB_PROCESS_MODULE_ID 0x95 #define TB_FILE_OUT_AIF_MODULE_ID 0x9a #define TB_FILE_IN_AIF_MODULE_ID 0x9b #define TB_FILE_OUT_DAI_MODULE_ID 0x9c diff --git a/tools/testbench/topology_ipc4.c b/tools/testbench/topology_ipc4.c index 46953653a1af..9ea308cecc38 100644 --- a/tools/testbench/topology_ipc4.c +++ b/tools/testbench/topology_ipc4.c @@ -478,21 +478,47 @@ static int tb_new_src(struct testbench_prm *tp) static int tb_new_asrc(struct testbench_prm *tp) { + struct ipc4_asrc_module_cfg asrc; struct tplg_context *ctx = &tp->tplg; - char tplg_object[TB_IPC4_MAX_TPLG_OBJECT_SIZE] = {0}; - struct sof_ipc_comp_asrc *asrc = (struct sof_ipc_comp_asrc *)tplg_object; + struct tplg_comp_info *comp_info = ctx->current_comp_info; struct snd_soc_tplg_ctl_hdr *tplg_ctl; + size_t uuid_offset; int ret; + ret = tplg_parse_widget_audio_formats(ctx); + if (ret < 0) + return ret; + tplg_ctl = calloc(ctx->hdr->payload_size, 1); if (!tplg_ctl) return -ENOMEM; - ret = tplg_new_asrc(ctx, &asrc->comp, TB_IPC4_MAX_TPLG_OBJECT_SIZE, + comp_info->ipc_size = sizeof(struct ipc4_asrc_module_cfg); + uuid_offset = comp_info->ipc_size; + comp_info->ipc_size += sizeof(struct sof_uuid); + comp_info->ipc_payload = calloc(comp_info->ipc_size, 1); + if (!comp_info->ipc_payload) + return -ENOMEM; + + comp_info->instance_id = tp->instance_ids[SND_SOC_TPLG_DAPM_EFFECT]++; + comp_info->module_id = TB_ASRC_MODULE_ID; + + ret = tplg_new_asrc(ctx, &asrc, sizeof(struct ipc4_asrc_module_cfg), tplg_ctl, ctx->hdr->payload_size); - if (ret < 0) - fprintf(stderr, "error: failed to create ASRC\n"); + if (ret < 0) { + fprintf(stderr, "error: failed to create SRC\n"); + goto out; + } + + /* copy volume data to ipc_payload */ + memcpy(comp_info->ipc_payload, &asrc, sizeof(struct ipc4_asrc_module_cfg)); + + /* copy uuid to the end of the payload */ + memcpy(comp_info->ipc_payload + uuid_offset, &comp_info->uuid, sizeof(struct sof_uuid)); + tb_setup_widget_ipc_msg(comp_info); + +out: free(tplg_ctl); return ret; } diff --git a/tools/tplg_parser/asrc.c b/tools/tplg_parser/asrc.c index fbba88f87f7a..fd098e610260 100644 --- a/tools/tplg_parser/asrc.c +++ b/tools/tplg_parser/asrc.c @@ -64,7 +64,10 @@ static int asrc_ipc3_build(struct tplg_context *ctx, void *_asrc) /* ASRC - IPC4 */ static const struct sof_topology_token asrc4_tokens[] = { - /* TODO */ + {SOF_TKN_ASRC_RATE_OUT, SND_SOC_TPLG_TUPLE_TYPE_WORD, + tplg_token_get_uint32_t, offsetof(struct ipc4_asrc_module_cfg, out_freq), 0}, + {SOF_TKN_ASRC_OPERATION_MODE, SND_SOC_TPLG_TUPLE_TYPE_WORD, + tplg_token_get_uint32_t, offsetof(struct ipc4_asrc_module_cfg, asrc_mode), 0}, }; static const struct sof_topology_token_group asrc_ipc4_tokens[] = { diff --git a/tools/tplg_parser/include/tplg_parser/topology.h b/tools/tplg_parser/include/tplg_parser/topology.h index f4ca6c001a53..8cedca32430f 100644 --- a/tools/tplg_parser/include/tplg_parser/topology.h +++ b/tools/tplg_parser/include/tplg_parser/topology.h @@ -21,6 +21,7 @@ #include #include #include +#include #include "copier/copier.h" From d9d360e14b6993dbcaf1683e2ac341daad007ea6 Mon Sep 17 00:00:00 2001 From: Seppo Ingalsuo Date: Fri, 4 Oct 2024 16:34:48 +0300 Subject: [PATCH 6/8] Tools: Testbench: Fix typos in README.md The examples need to convert the testbench out.raw to wav format, not the converted input for testbench. Signed-off-by: Seppo Ingalsuo --- tools/testbench/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/testbench/README.md b/tools/testbench/README.md index f4fbd3edefe5..f56f07510bab 100644 --- a/tools/testbench/README.md +++ b/tools/testbench/README.md @@ -44,7 +44,7 @@ sox --encoding signed-integer /usr/share/sounds/alsa/Front_Left.wav -L -r 48000 tools/testbench/build_testbench/install/bin/testbench -r 48000 -R 48000 -c 2 -n 2 -b S32_LE \ -t tools/build_tools/test/topology/test-playback-ssp5-mclk-0-I2S-dcblock-s32le-s32le-48k-24576k-codec.tplg \ -i in.raw -o out.raw -sox --encoding signed-integer -L -r 48000 -c 2 -b 32 in.raw out.wav +sox --encoding signed-integer -L -r 48000 -c 2 -b 32 out.raw out.wav aplay out.wav ``` @@ -138,7 +138,7 @@ sox --encoding signed-integer /usr/share/sounds/alsa/Front_Center.wav -L -r 4800 tools/testbench/build_testbench/install/bin/testbench -r 48000 -R 48000 -c 2 -n 2 -b S32_LE -p 1,2 \ -t tools/build_tools/topology/topology2/development/sof-hda-benchmark-dcblock32.tplg \ -i in.raw -o out.raw -sox --encoding signed-integer -L -r 48000 -c 2 -b 32 in.raw out.wav +sox --encoding signed-integer -L -r 48000 -c 2 -b 32 out.raw out.wav aplay out.wav ``` From 14933a4f4820294a2c57562ac801ee0bbf4fd52a Mon Sep 17 00:00:00 2001 From: Seppo Ingalsuo Date: Tue, 8 Oct 2024 18:05:26 +0300 Subject: [PATCH 7/8] Tools: Tplg_parser: Add parse of input and output pins count This patch adds to the parser the capability to parse the tokens and place the found values to pins_info member in tplg_comp_info struct. Signed-off-by: Seppo Ingalsuo --- tools/tplg_parser/audio_formats.c | 15 ++++++++++++++- tools/tplg_parser/include/tplg_parser/topology.h | 6 ++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/tools/tplg_parser/audio_formats.c b/tools/tplg_parser/audio_formats.c index 53166f81b620..39a54e7f6100 100644 --- a/tools/tplg_parser/audio_formats.c +++ b/tools/tplg_parser/audio_formats.c @@ -68,6 +68,13 @@ static const struct sof_topology_token ipc4_out_audio_format_tokens[] = { offsetof(struct sof_ipc4_pin_format, buffer_size)}, }; +static const struct sof_topology_token ipc4_comp_pin_tokens[] = { + {SOF_TKN_COMP_NUM_INPUT_PINS, SND_SOC_TPLG_TUPLE_TYPE_WORD, + tplg_token_get_uint32_t, offsetof(struct tplg_pins_info, num_input_pins)}, + {SOF_TKN_COMP_NUM_OUTPUT_PINS, SND_SOC_TPLG_TUPLE_TYPE_WORD, + tplg_token_get_uint32_t, offsetof(struct tplg_pins_info, num_output_pins)}, +}; + int tplg_parse_widget_audio_formats(struct tplg_context *ctx) { struct snd_soc_tplg_vendor_array *array = &ctx->widget->priv.array[0]; @@ -146,5 +153,11 @@ int tplg_parse_widget_audio_formats(struct tplg_context *ctx) pin_fmt[i].buffer_size); } - return 0; + ret = sof_parse_token_sets(&comp_info->pins_info, ipc4_comp_pin_tokens, + ARRAY_SIZE(ipc4_comp_pin_tokens), array, size, 1, 0); + if (ret < 0) + fprintf(stderr, "widget: %s: Failed to parse ipc4_comp_pin_tokens\n", + ctx->widget->name); + + return ret; } diff --git a/tools/tplg_parser/include/tplg_parser/topology.h b/tools/tplg_parser/include/tplg_parser/topology.h index 8cedca32430f..04988d688082 100644 --- a/tools/tplg_parser/include/tplg_parser/topology.h +++ b/tools/tplg_parser/include/tplg_parser/topology.h @@ -111,6 +111,11 @@ struct tplg_pipeline_info { struct list_item item; /* item in a list */ }; +struct tplg_pins_info { + uint32_t num_input_pins; + uint32_t num_output_pins; +}; + struct tplg_comp_info { struct list_item item; /* item in a list */ struct sof_ipc4_available_audio_format available_fmt; /* available formats in tplg */ @@ -118,6 +123,7 @@ struct tplg_comp_info { struct ipc4_base_module_cfg basecfg; struct tplg_pipeline_info *pipe_info; struct sof_uuid uuid; + struct tplg_pins_info pins_info; char *name; char *stream_name; int id; From cb5946e290b7ce53ab873a0c6428f27420e9c6b0 Mon Sep 17 00:00:00 2001 From: Seppo Ingalsuo Date: Wed, 9 Oct 2024 17:39:04 +0300 Subject: [PATCH 8/8] Tools: Testbench: Add extended base configuration to process type The input and output pins count and format for each pin is added into the "struct ipc4_base_module_cfg_ext" that is placed after "struct ipc4_base_module_cfg" and before test bench and plugin specific UUID. This allows to load components those use the extended configuration. Since there is no harm to apply it to every process type component it done simply for all. Signed-off-by: Seppo Ingalsuo --- .../include/testbench/topology_ipc4.h | 5 + tools/testbench/topology_ipc4.c | 110 +++++++++++++++++- 2 files changed, 112 insertions(+), 3 deletions(-) diff --git a/tools/testbench/include/testbench/topology_ipc4.h b/tools/testbench/include/testbench/topology_ipc4.h index 3c9f9d60c70c..368327d4b955 100644 --- a/tools/testbench/include/testbench/topology_ipc4.h +++ b/tools/testbench/include/testbench/topology_ipc4.h @@ -27,6 +27,11 @@ #define TB_FILE_OUT_DAI_MODULE_ID 0x9c #define TB_FILE_IN_DAI_MODULE_ID 0x9d +enum tb_pin_type { + TB_PIN_TYPE_INPUT = 0, + TB_PIN_TYPE_OUTPUT, +}; + int tb_delete_pipeline(struct testbench_prm *tp, struct tplg_pipeline_info *pipe_info); int tb_free_all_pipelines(struct testbench_prm *tp); int tb_free_route(struct testbench_prm *tp, struct tplg_route_info *route_info); diff --git a/tools/testbench/topology_ipc4.c b/tools/testbench/topology_ipc4.c index 9ea308cecc38..d9077ba7df6b 100644 --- a/tools/testbench/topology_ipc4.c +++ b/tools/testbench/topology_ipc4.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include @@ -330,10 +331,90 @@ int tb_match_audio_format(struct testbench_prm *tp, struct tplg_comp_info *comp_ return 0; } +static int tb_set_pin_formats(struct tplg_comp_info *comp_info, + struct ipc4_base_module_cfg *base_cfg, + struct ipc4_base_module_cfg_ext *base_cfg_ext, + enum tb_pin_type pin_type) +{ + struct sof_ipc4_pin_format *formats_array; + struct sof_ipc4_pin_format *pin_format_item; + struct sof_ipc4_pin_format *pin_format; + struct sof_ipc4_pin_format *base_cfg_ext_pin_formats = + (struct sof_ipc4_pin_format *)base_cfg_ext->pin_formats; + int format_list_count; + int num_pins; + int i, j; + int pin_format_offset = 0; + + switch (pin_type) { + case TB_PIN_TYPE_INPUT: + num_pins = base_cfg_ext->nb_input_pins; + formats_array = comp_info->available_fmt.input_pin_fmts; + format_list_count = comp_info->available_fmt.num_input_formats; + break; + case TB_PIN_TYPE_OUTPUT: + num_pins = base_cfg_ext->nb_output_pins; + pin_format_offset = base_cfg_ext->nb_input_pins; + formats_array = comp_info->available_fmt.output_pin_fmts; + format_list_count = comp_info->available_fmt.num_output_formats; + break; + default: + fprintf(stderr, "Error: Illegal pin type %d for %s\n", pin_type, comp_info->name); + } + + for (i = pin_format_offset; i < num_pins + pin_format_offset; i++) { + pin_format = &base_cfg_ext_pin_formats[i]; + + if (i == pin_format_offset) { + if (pin_type == TB_PIN_TYPE_INPUT) { + pin_format->buffer_size = base_cfg->ibs; + /* Note: Copy "struct ipc4_audio_format" to + * "struct sof_ipc4_pin_format". They are same + * but separate definitions. The last member fmt_cfg + * is defined with bitfields for channels_count, valid_bit_depth, + * s_type, reservedin ipc4_audio_format. + */ + memcpy(&pin_format->audio_fmt, &base_cfg->audio_fmt, + sizeof(pin_format->audio_fmt)); + } else { + pin_format->buffer_size = base_cfg->obs; + /* TODO: Using workaround, need to find out how to do this. This + * is copied kernel function sof_ipc4_process_set_pin_formats() + * from process->output_format. It's set in + * sof_ipc4_prepare_process_module(). + */ + memcpy(&pin_format->audio_fmt, &base_cfg->audio_fmt, + sizeof(pin_format->audio_fmt)); + } + continue; + } + + for (j = 0; j < format_list_count; j++) { + pin_format_item = &formats_array[j]; + if (pin_format_item->pin_index == i - pin_format_offset) { + *pin_format = *pin_format_item; + break; + } + } + + if (j == format_list_count) { + fprintf(stderr, "%s pin %d format not found for %s\n", + (pin_type == TB_PIN_TYPE_INPUT) ? "input" : "output", + i - pin_format_offset, comp_info->name); + return -EINVAL; + } + } + + return 0; +} + int tb_set_up_widget_base_config(struct testbench_prm *tp, struct tplg_comp_info *comp_info) { char *config_name = tp->config[0].name; + struct ipc4_base_module_cfg *base_cfg; + struct ipc4_base_module_cfg_ext *base_cfg_ext; struct tb_config *config; + struct tplg_pins_info *pins = &comp_info->pins_info; bool config_found = false; int ret, i; @@ -359,6 +440,21 @@ int tb_set_up_widget_base_config(struct testbench_prm *tp, struct tplg_comp_info /* copy the basecfg into the ipc payload */ memcpy(comp_info->ipc_payload, &comp_info->basecfg, sizeof(struct ipc4_base_module_cfg)); + /* Copy ext config for process type */ + if (comp_info->module_id == TB_PROCESS_MODULE_ID) { + base_cfg = comp_info->ipc_payload; + base_cfg_ext = comp_info->ipc_payload + sizeof(*base_cfg); + base_cfg_ext->nb_input_pins = pins->num_input_pins; + base_cfg_ext->nb_output_pins = pins->num_output_pins; + ret = tb_set_pin_formats(comp_info, base_cfg, base_cfg_ext, TB_PIN_TYPE_INPUT); + if (ret) + return ret; + + ret = tb_set_pin_formats(comp_info, base_cfg, base_cfg_ext, TB_PIN_TYPE_OUTPUT); + if (ret) + return ret; + } + return 0; } @@ -824,6 +920,9 @@ int tb_new_process(struct testbench_prm *tp) struct tplg_context *ctx = &tp->tplg; struct tplg_comp_info *comp_info = ctx->current_comp_info; struct snd_soc_tplg_ctl_hdr *tplg_ctl; + struct tplg_pins_info *pins = &comp_info->pins_info; + size_t ext_size; + size_t uuid_offset; int ret; ret = tplg_parse_widget_audio_formats(ctx); @@ -834,8 +933,14 @@ int tb_new_process(struct testbench_prm *tp) if (!tplg_ctl) return -ENOMEM; + /* Ext config size */ + ext_size = ipc4_calc_base_module_cfg_ext_size(pins->num_input_pins, pins->num_output_pins); + /* use base config variant with uuid */ - comp_info->ipc_size = sizeof(struct ipc4_base_module_cfg) + sizeof(struct sof_uuid); + comp_info->ipc_size = sizeof(struct ipc4_base_module_cfg); + comp_info->ipc_size += ext_size; + uuid_offset = comp_info->ipc_size; + comp_info->ipc_size += sizeof(struct sof_uuid); comp_info->ipc_payload = calloc(comp_info->ipc_size, 1); if (!comp_info->ipc_payload) { ret = ENOMEM; @@ -857,8 +962,7 @@ int tb_new_process(struct testbench_prm *tp) tb_setup_widget_ipc_msg(comp_info); /* copy uuid to the end of the payload */ - memcpy(comp_info->ipc_payload + sizeof(struct ipc4_base_module_cfg), &comp_info->uuid, - sizeof(struct sof_uuid)); + memcpy(comp_info->ipc_payload + uuid_offset, &comp_info->uuid, sizeof(struct sof_uuid)); /* TODO: drop tplg_ctl to avoid memory leak. Need to store and handle this * to support controls.