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__ */ 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 ``` 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..368327d4b955 100644 --- a/tools/testbench/include/testbench/topology_ipc4.h +++ b/tools/testbench/include/testbench/topology_ipc4.h @@ -10,7 +10,27 @@ #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_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 +#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); @@ -24,6 +44,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_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..d9077ba7df6b 100644 --- a/tools/testbench/topology_ipc4.c +++ b/tools/testbench/topology_ipc4.c @@ -9,8 +9,10 @@ #if CONFIG_IPC_MAJOR_4 #include +#include #include #include +#include #include #include @@ -329,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; @@ -358,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; } @@ -430,42 +527,94 @@ 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; } 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; } @@ -581,12 +730,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 +745,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 +768,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 +780,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 +799,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 +814,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 +836,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 +848,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 +862,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 +894,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); @@ -763,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); @@ -773,8 +933,14 @@ 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 */ + /* 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); + 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; @@ -783,11 +949,11 @@ 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 */ + /* 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; @@ -795,6 +961,9 @@ 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 + 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. */ @@ -990,6 +1159,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) @@ -1002,6 +1363,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"); @@ -1277,4 +1645,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"); } 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/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 5cbf0588d92f..04988d688082 100644 --- a/tools/tplg_parser/include/tplg_parser/topology.h +++ b/tools/tplg_parser/include/tplg_parser/topology.h @@ -20,6 +20,8 @@ #include #include #include +#include +#include #include "copier/copier.h" @@ -109,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 */ @@ -116,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; 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[] = {