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.