-
Notifications
You must be signed in to change notification settings - Fork 311
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Tools: Testbench: Add to IPC4 version UUID based components load, apply initial byte control, etc. #9542
Open
singalsu
wants to merge
6
commits into
thesofproject:main
Choose a base branch
from
singalsu:testbench_ipc4_uuid_bytecontrol
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+496
−48
Open
Tools: Testbench: Add to IPC4 version UUID based components load, apply initial byte control, etc. #9542
Changes from 1 commit
Commits
Show all changes
6 commits
Select commit
Hold shift + click to select a range
6a07a2d
Tools: Testbench: Add UUID based component load
singalsu 26d66e4
Tools: Testbench: Add support for parsing controls from topology
singalsu f647a52
Audio: SRC: Move SRC IPC config definition to src_ipc.h
singalsu b6650bc
Tools: Testbench: Fix SRC component load
singalsu 1a0470f
Tools: Testbench: Fix ASRC component load
singalsu 7cd25e1
Tools: Testbench: Fix typos in README.md
singalsu File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -9,6 +9,7 @@ | |
#if CONFIG_IPC_MAJOR_4 | ||
|
||
#include <ipc4/header.h> | ||
#include <kernel/header.h> | ||
#include <tplg_parser/tokens.h> | ||
#include <tplg_parser/topology.h> | ||
#include <stdio.h> | ||
|
@@ -797,9 +798,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; | ||
|
@@ -1006,6 +1007,200 @@ 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; | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @ranj063 These volume tables math functions would be a good start for a common utils library. Maybe there could be a place in tplg_parser? |
||
|
||
/* 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; | ||
|
||
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: | ||
struct snd_soc_tplg_mixer_control *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: | ||
struct snd_soc_tplg_enum_control *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: | ||
{ | ||
struct snd_soc_tplg_bytes_control *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) | ||
|
||
|
@@ -1018,6 +1213,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"); | ||
|
@@ -1293,4 +1495,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 */ |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we include the ABI header rather than duplicate ? I guess this comes from ALSA asoc.h ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure, this struct is a direct copy from SOF plugin. If we change, we should change both to be able to merge the sources later to a common library like tplg_parser. What do you think @ranj063 ?