Part III - Adding Run-time Parameter Control¶
This lesson describes how to add startup and run-time parameters to your component. You will add a command handler to the “amp” to mute/unmute individual channels.
Changes in the topology definition are required as well. You will add binary bytes kcontrol connected to your widget in order to enable parameter transfer from a user space application, through the driver to the FW running your “amp” component.
This simple example defines the parameter blob as two 32-bit integer numbers, one per channel, where non-zero value causes the channel samples to pass while zero value “mutes” the channel.
Changing the FW Code¶
First, change the private data definition to store run-time parameters.
struct amp_comp_data {
int channel_volume[2];
};
Add start-up parameter handling to amp_new()
.
static struct comp_dev *amp_new(struct sof_ipc_comp *comp)
{
/* ... */
amp = COMP_GET_IPC(dev, sof_ipc_comp_process);
ret = memcpy_s(amp, sizeof(*amp), ipc_amp,
sizeof(struct sof_ipc_comp_process)));
assert(!ret);
cd->channel_volume[0] = 1;
cd->channel_volume[1] = 1;
if (ipc_amp->size == sizeof(cd->channel_volume)) {
memcpy_s(cd->channel_volume, sizeof(cd->channel_volume),
ipc_amp->data, ipc_amp->size);
}
comp_set_drvdata(dev, cd);
/* ... */
comp_dbg(dev, "amplifier created vol[0] %d vol[1] %d",
cd->channel_volume[0], cd->channel_volume[1]);
}
Modify amp_copy()
to pass/mute channels based on your settings.
static int amp_copy(struct comp_dev *dev)
{
struct amp_comp_data *cd = comp_get_drvdata(dev);
struct comp_copy_limits cl;
/* ... */
for (frame = 0; frame < cl.frames; frame++) {
for (channel = 0; channel < sink->stream.channels; channel++) {
src = audio_stream_read_frag_s16(&source->stream,
buff_frag);
dst = audio_stream_write_frag_s16(&sink->stream,
buff_frag);
if (cd->channel_volume[channel])
*dst = *src;
else
*dst = 0;
++buff_frag;
}
}
Add the command handlers to report parameters and receive updates.
First, add the handler to receive parameters.
static int amp_cmd_set_data(struct comp_dev *dev,
struct sof_ipc_ctrl_data *cdata)
{
struct amp_comp_data *cd = comp_get_drvdata(dev);
if (cdata->cmd != SOF_CTRL_CMD_BINARY) {
comp_err(dev, "amp_cmd_set_data(): invalid cmd %d",
cdata->cmd);
return -EINVAL;
}
if (cdata->data->size != sizeof(cd->channel_volume)) {
comp_err(dev, "amp_cmd_set_data(): invalid data size %d",
cdata->data->size);
return -EINVAL;
}
memcpy_s(cd->channel_volume, sizeof(cd->channel_volume),
cdata->data->data, cdata->data->size);
comp_dbg(dev, "amplifier new settings vol[0] %d vol[1] %d",
cd->channel_volume[0], cd->channel_volume[1]);
return 0;
}
Add another one to report parameters back to the host. Note how the
cdata->data
(struct sof_abi_hdr
) is updated.
static int amp_cmd_get_data(struct comp_dev *dev,
struct sof_ipc_ctrl_data *cdata, int max_size)
{
struct amp_comp_data *cd = comp_get_drvdata(dev);
if (cdata->cmd != SOF_CTRL_CMD_BINARY) {
comp_err(dev, "amp_cmd_get_data(): invalid cmd %d",
cdata->cmd);
return -EINVAL;
}
if (sizeof(cd->channel_volume) > max_size)
return -EINVAL;
memcpy_s(cdata->data->data,
((struct sof_abi_hdr *)(cdata->data))->size,
cd->channel_volume,
sizeof(cd->channel_volume));
cdata->data->abi = SOF_ABI_VERSION;
cdata->data->size = sizeof(cd->channel_volume);
return 0;
}
Put everything together as a command handler.
static int amp_cmd(struct comp_dev *dev, int cmd, void *data, int max_data_size)
{
struct sof_ipc_ctrl_data *cdata = data;
int ret = 0;
switch (cmd) {
case COMP_CMD_SET_DATA:
ret = amp_cmd_set_data(dev, cdata);
break;
case COMP_CMD_GET_DATA:
ret = amp_cmd_get_data(dev, cdata, max_data_size);
break;
default:
comp_err(dev, "amp_cmd(): unhandled command %d", cmd);
ret = -EINVAL;
break;
}
return ret;
}
Attach the handler to your component driver API.
struct comp_driver comp_amp = {
.type = SOF_COMP_AMP,
.ops = {
.new = amp_new,
.free = amp_free,
.params = NULL,
.cmd = amp_cmd,
.trigger = amp_trigger,
.prepare = amp_prepare,
.reset = amp_reset,
.copy = amp_copy,
.cache = NULL
},
};
Binary Bytes KControl in Topology¶
An example of data section for component parameters is presented as amp_bytes.m4 content in the previous part of the tutorial.