diff --git a/tools/plugin/alsaplug/ctl.c b/tools/plugin/alsaplug/ctl.c index f8bfdf61a3a9..be38587135c0 100644 --- a/tools/plugin/alsaplug/ctl.c +++ b/tools/plugin/alsaplug/ctl.c @@ -119,7 +119,7 @@ static int plug_ctl_get_attribute(snd_ctl_ext_t *ext, snd_ctl_ext_key_t key, struct snd_soc_tplg_bytes_control *bytes_ctl; int err = 0; - switch (hdr->type) { + switch (hdr->ops.info) { case SND_SOC_TPLG_CTL_VOLSW: case SND_SOC_TPLG_CTL_VOLSW_SX: case SND_SOC_TPLG_CTL_VOLSW_XR_SX: @@ -138,7 +138,7 @@ static int plug_ctl_get_attribute(snd_ctl_ext_t *ext, snd_ctl_ext_key_t key, case SND_SOC_TPLG_CTL_ENUM_VALUE: enum_ctl = (struct snd_soc_tplg_enum_control *)hdr; *type = SND_CTL_ELEM_TYPE_ENUMERATED; - *count = enum_ctl->num_channels; + *count = enum_ctl->items; break; case SND_SOC_TPLG_CTL_RANGE: case SND_SOC_TPLG_CTL_STROBE: @@ -198,6 +198,20 @@ static int plug_ctl_get_integer_info(snd_ctl_ext_t *ext, snd_ctl_ext_key_t key, return err; } +static void plug_ctl_ipc_message(struct ipc4_module_large_config *config, int param_id, + size_t 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 = size; + config->extension.r.large_param_id = param_id; +} + static int plug_ctl_read_integer(snd_ctl_ext_t *ext, snd_ctl_ext_key_t key, long *value) { snd_sof_ctl_t *ctl = ext->private_data; @@ -212,14 +226,9 @@ static int plug_ctl_read_integer(snd_ctl_ext_t *ext, snd_ctl_ext_key_t key, long int i, err; /* configure the IPC message */ - config.primary.r.type = SOF_IPC4_MOD_LARGE_CONFIG_GET; - 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 = ctl->glb->ctl[key].module_id; - config.primary.r.instance_id = ctl->glb->ctl[key].instance_id; - - config.extension.r.data_off_size = sizeof(volume); - config.extension.r.large_param_id = IPC4_VOLUME; + plug_ctl_ipc_message(&config, IPC4_VOLUME, sizeof(volume), ctl->glb->ctl[key].module_id, + ctl->glb->ctl[key].instance_id, SOF_IPC4_MOD_LARGE_CONFIG_GET); + config.extension.r.final_block = 1; config.extension.r.init_block = 1; @@ -229,7 +238,7 @@ static int plug_ctl_read_integer(snd_ctl_ext_t *ext, snd_ctl_ext_key_t key, long return -ENOMEM; /* reply contains both the requested data and the reply status */ - reply_data_size = sizeof(reply) + mixer_ctl->num_channels * sizeof(*volume); + reply_data_size = sizeof(*reply) + mixer_ctl->num_channels * sizeof(*volume); reply_data = calloc(reply_data_size, 1); if (!reply_data_size) { free(msg); @@ -316,14 +325,9 @@ static int plug_ctl_write_integer(snd_ctl_ext_t *ext, snd_ctl_ext_key_t key, lon volume.curve_duration = 200000; /* configure the IPC message */ - config.primary.r.type = SOF_IPC4_MOD_LARGE_CONFIG_SET; - 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 = ctl->glb->ctl[key].module_id; - config.primary.r.instance_id = ctl->glb->ctl[key].instance_id; - - config.extension.r.data_off_size = sizeof(volume); - config.extension.r.large_param_id = IPC4_VOLUME; + plug_ctl_ipc_message(&config, IPC4_VOLUME, sizeof(volume), + ctl->glb->ctl[key].module_id, ctl->glb->ctl[key].instance_id, + SOF_IPC4_MOD_LARGE_CONFIG_SET); config.extension.r.final_block = 1; config.extension.r.init_block = 1; @@ -369,7 +373,7 @@ static int plug_ctl_get_enumerated_info(snd_ctl_ext_t *ext, snd_ctl_ext_key_t ke (struct snd_soc_tplg_enum_control *)hdr; int err = 0; - switch (hdr->type) { + switch (hdr->ops.info) { case SND_SOC_TPLG_CTL_ENUM: case SND_SOC_TPLG_CTL_ENUM_VALUE: *items = enum_ctl->items; @@ -391,10 +395,8 @@ static int plug_ctl_get_enumerated_name(snd_ctl_ext_t *ext, snd_ctl_ext_key_t ke struct snd_soc_tplg_enum_control *enum_ctl = (struct snd_soc_tplg_enum_control *)hdr; - printf("%s %d\n", __func__, __LINE__); - - if (item >= enum_ctl->count) { - SNDERR("invalid item %d for enum using key %d", item, key); + if (item >= enum_ctl->items) { + SNDERR("invalid item %d for enum using key %d\n", item, key); return -EINVAL; } @@ -405,12 +407,143 @@ static int plug_ctl_get_enumerated_name(snd_ctl_ext_t *ext, snd_ctl_ext_key_t ke static int plug_ctl_read_enumerated(snd_ctl_ext_t *ext, snd_ctl_ext_key_t key, unsigned int *items) { + snd_sof_ctl_t *ctl = ext->private_data; + struct snd_soc_tplg_enum_control *enum_ctl = CTL_GET_TPLG_ENUM(ctl, key); + struct ipc4_module_large_config config = {{ 0 }}; + struct ipc4_module_large_config_reply *reply; + struct sof_ipc4_control_msg_payload *data; + char *reply_data; + void *msg; + int size, reply_data_size; + int i, err; + + /* configure the IPC message */ + plug_ctl_ipc_message(&config, SOF_IPC4_ENUM_CONTROL_PARAM_ID, 0, + ctl->glb->ctl[key].module_id, ctl->glb->ctl[key].instance_id, + SOF_IPC4_MOD_LARGE_CONFIG_GET); + + config.extension.r.final_block = 1; + config.extension.r.init_block = 1; + + size = sizeof(config); + msg = calloc(size, 1); + if (!msg) + return -ENOMEM; + + /* reply contains both the requested data and the reply status */ + reply_data_size = sizeof(*reply) + sizeof(*data) + + enum_ctl->num_channels * sizeof(data->chanv[0]); + reply_data = calloc(reply_data_size, 1); + if (!reply_data_size) { + free(msg); + return -ENOMEM; + } + + /* send the IPC message */ + memcpy(msg, &config, sizeof(config)); + err = plug_mq_cmd_tx_rx(&ctl->ipc_tx, &ctl->ipc_rx, + msg, size, reply_data, reply_data_size); + free(msg); + if (err < 0) { + SNDERR("failed to get enum items for control %s\n", enum_ctl->hdr.name); + goto out; + } + + reply = (struct ipc4_module_large_config_reply *)reply_data; + if (reply->primary.r.status != IPC4_SUCCESS) { + SNDERR("enum control %s get failed with status %d\n", + enum_ctl->hdr.name, reply->primary.r.status); + err = -EINVAL; + goto out; + } + + /* check data sanity */ + data = (struct sof_ipc4_control_msg_payload *)(reply_data + sizeof(*reply)); + if (data->num_elems != enum_ctl->num_channels) { + SNDERR("Channel count %d doesn't match the expected value %d for enum ctl %s\n", + data->num_elems, enum_ctl->num_channels, enum_ctl->hdr.name); + err = -EINVAL; + goto out; + } + + /* set the enum items based on the received data */ + for (i = 0; i < enum_ctl->num_channels; i++) + items[i] = data->chanv[i].value; +out: + free(reply_data); return 0; } static int plug_ctl_write_enumerated(snd_ctl_ext_t *ext, snd_ctl_ext_key_t key, unsigned int *items) { + snd_sof_ctl_t *ctl = ext->private_data; + struct snd_soc_tplg_enum_control *enum_ctl = CTL_GET_TPLG_ENUM(ctl, key); + struct ipc4_module_large_config config = {{ 0 }}; + struct sof_ipc4_control_msg_payload *data; + struct ipc4_message_reply reply; + void *msg; + int data_size, msg_size; + int err, i; + + /* size of control data */ + data_size = enum_ctl->num_channels * sizeof(struct sof_ipc4_ctrl_value_chan) + + sizeof(*data); + + /* allocate memory for control data */ + data = calloc(data_size, 1); + if (!data) + return -ENOMEM; + + /* set param ID and number of channels */ + data->id = ctl->glb->ctl[key].index; + data->num_elems = enum_ctl->num_channels; + + /* set the enum values */ + for (i = 0; i < data->num_elems; i++) { + data->chanv[i].channel = i; + data->chanv[i].value = items[i]; + } + + /* configure the IPC message */ + plug_ctl_ipc_message(&config, SOF_IPC4_ENUM_CONTROL_PARAM_ID, data_size, + ctl->glb->ctl[key].module_id, ctl->glb->ctl[key].instance_id, + SOF_IPC4_MOD_LARGE_CONFIG_SET); + + /* + * enum controls can have a maximum of 16 texts/values. So the entire data can be sent + * in a single IPC message + */ + config.extension.r.final_block = 1; + config.extension.r.init_block = 1; + + /* allocate memory for IPC message */ + msg_size = sizeof(config) + data_size; + msg = calloc(msg_size, 1); + if (!msg) { + free(data); + return -ENOMEM; + } + + /* set the IPC message data */ + memcpy(msg, &config, sizeof(config)); + memcpy(msg + sizeof(config), data, data_size); + free(data); + + /* send the message and check status */ + err = plug_mq_cmd_tx_rx(&ctl->ipc_tx, &ctl->ipc_rx, msg, msg_size, &reply, sizeof(reply)); + free(msg); + if (err < 0) { + SNDERR("failed to set enum control %s\n", enum_ctl->hdr.name); + return err; + } + + if (reply.primary.r.status != IPC4_SUCCESS) { + SNDERR("enum control %s set failed with status %d\n", + enum_ctl->hdr.name, reply.primary.r.status); + return -EINVAL; + } + return 0; } diff --git a/tools/plugin/alsaplug/plugin.h b/tools/plugin/alsaplug/plugin.h index db4bb2538bf5..37d93cc82701 100644 --- a/tools/plugin/alsaplug/plugin.h +++ b/tools/plugin/alsaplug/plugin.h @@ -64,6 +64,6 @@ int plug_parse_topology(snd_sof_plug_t *plug); int plug_set_up_pipelines(snd_sof_plug_t *plug, int dir); int plug_free_pipelines(snd_sof_plug_t *plug, struct tplg_pipeline_list *pipeline_list, int dir); void plug_free_topology(snd_sof_plug_t *plug); -int plug_kcontrol_cb_new(struct snd_soc_tplg_ctl_hdr *tplg_ctl, void *_comp, void *arg); +int plug_kcontrol_cb_new(struct snd_soc_tplg_ctl_hdr *tplg_ctl, void *_comp, void *arg, int index); #endif diff --git a/tools/plugin/alsaplug/tplg_ctl.c b/tools/plugin/alsaplug/tplg_ctl.c index e37e2a2142f6..db3248ed5b76 100644 --- a/tools/plugin/alsaplug/tplg_ctl.c +++ b/tools/plugin/alsaplug/tplg_ctl.c @@ -141,8 +141,9 @@ static uint32_t vol_compute_gain(uint32_t value, struct snd_soc_tplg_tlv_dbscale } /* helper function to add new kcontrols to the list of kcontrols in the global context */ -int plug_kcontrol_cb_new(struct snd_soc_tplg_ctl_hdr *tplg_ctl, void *_comp, void *arg) +int plug_kcontrol_cb_new(struct snd_soc_tplg_ctl_hdr *tplg_ctl, void *_comp, void *arg, int index) { + struct tplg_comp_info *comp_info = _comp; snd_sof_plug_t *plug = arg; struct plug_shm_glb_state *glb = plug->glb_ctx.addr; struct plug_shm_ctl *ctl; @@ -152,14 +153,13 @@ int plug_kcontrol_cb_new(struct snd_soc_tplg_ctl_hdr *tplg_ctl, void *_comp, voi return -EINVAL; } - switch (tplg_ctl->type) { + switch (tplg_ctl->ops.info) { 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 tplg_comp_info *comp_info = _comp; struct snd_soc_tplg_ctl_tlv *tlv; struct snd_soc_tplg_tlv_dbscale *scale; int i; @@ -169,6 +169,7 @@ int plug_kcontrol_cb_new(struct snd_soc_tplg_ctl_hdr *tplg_ctl, void *_comp, voi ctl->module_id = comp_info->module_id; ctl->instance_id = comp_info->instance_id; ctl->mixer_ctl = *tplg_mixer; + ctl->index = index; tlv = &tplg_ctl->tlv; scale = &tlv->scale; @@ -186,6 +187,18 @@ int plug_kcontrol_cb_new(struct snd_soc_tplg_ctl_hdr *tplg_ctl, void *_comp, voi } 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 plug_shm_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; + break; + } case SND_SOC_TPLG_CTL_BYTES: break; case SND_SOC_TPLG_CTL_RANGE: diff --git a/tools/plugin/common.h b/tools/plugin/common.h index faa15f36c6ba..5297d7564302 100644 --- a/tools/plugin/common.h +++ b/tools/plugin/common.h @@ -66,6 +66,7 @@ struct plug_shm_ctl { unsigned int instance_id; unsigned int type; unsigned int volume_table[MAX_VOLUME_SIZE]; + unsigned int index; union { struct snd_soc_tplg_mixer_control mixer_ctl; struct snd_soc_tplg_enum_control enum_ctl; diff --git a/tools/tplg_parser/control.c b/tools/tplg_parser/control.c index f14cd7964daa..e8e8048b4939 100644 --- a/tools/tplg_parser/control.c +++ b/tools/tplg_parser/control.c @@ -91,6 +91,10 @@ int tplg_create_controls(struct tplg_context *ctx, int num_kcontrols, struct snd_soc_tplg_mixer_control *mixer_ctl; struct snd_soc_tplg_enum_control *enum_ctl; struct snd_soc_tplg_bytes_control *bytes_ctl; + int num_mixers = 0; + int num_enums = 0; + int num_byte_controls = 0; + int index; int j, ret = 0; for (j = 0; j < num_kcontrols; j++) { @@ -105,6 +109,7 @@ int tplg_create_controls(struct tplg_context *ctx, int num_kcontrols, case SND_SOC_TPLG_CTL_VOLSW_XR_SX: case SND_SOC_TPLG_CTL_RANGE: case SND_SOC_TPLG_DAPM_CTL_VOLSW: + index = num_mixers++; /* load mixer type control */ mixer_ctl = (struct snd_soc_tplg_mixer_control *)ctl_hdr; /* ctl is after private data */ @@ -116,6 +121,7 @@ int tplg_create_controls(struct tplg_context *ctx, int num_kcontrols, case SND_SOC_TPLG_DAPM_CTL_ENUM_DOUBLE: case SND_SOC_TPLG_DAPM_CTL_ENUM_VIRT: case SND_SOC_TPLG_DAPM_CTL_ENUM_VALUE: + index = num_enums++; /* load enum_ctl type control */ enum_ctl = (struct snd_soc_tplg_enum_control *)ctl_hdr; /* ctl is after private data */ @@ -123,6 +129,7 @@ int tplg_create_controls(struct tplg_context *ctx, int num_kcontrols, break; case SND_SOC_TPLG_CTL_BYTES: + index = num_byte_controls++; /* load bytes_ctl type control */ bytes_ctl = (struct snd_soc_tplg_bytes_control *)ctl_hdr; /* ctl is after private data */ @@ -135,7 +142,7 @@ int tplg_create_controls(struct tplg_context *ctx, int num_kcontrols, } if (ctx->ctl_cb && object) - ctx->ctl_cb(ctl_hdr, object, ctx->ctl_arg); + ctx->ctl_cb(ctl_hdr, object, ctx->ctl_arg, index); } if (rctl && ctl_hdr) { diff --git a/tools/tplg_parser/include/tplg_parser/topology.h b/tools/tplg_parser/include/tplg_parser/topology.h index 3e95e5c3bc50..5cbf0588d92f 100644 --- a/tools/tplg_parser/include/tplg_parser/topology.h +++ b/tools/tplg_parser/include/tplg_parser/topology.h @@ -181,7 +181,7 @@ struct tplg_context { /* kcontrol creation */ void *ctl_arg; int (*ctl_cb)(struct snd_soc_tplg_ctl_hdr *tplg_ctl, - void *comp, void *arg); + void *comp, void *arg, int index); }; #define tplg_get(ctx) ((void *)(ctx->tplg_base + ctx->tplg_offset)) diff --git a/tools/tplg_parser/pga.c b/tools/tplg_parser/pga.c index 5a23f349d679..b4d86a78e0df 100644 --- a/tools/tplg_parser/pga.c +++ b/tools/tplg_parser/pga.c @@ -73,7 +73,7 @@ static int pga_ipc3_build(struct tplg_context *ctx, void *_pga) /* call ctl creation callback if needed */ if (ctx->ctl_cb) - ctx->ctl_cb(ctl, volume, ctx->ctl_arg); + ctx->ctl_cb(ctl, volume, ctx->ctl_arg, 0); /* we only care about the volume ctl - ignore others atm */ if (ctl->ops.get != 256) diff --git a/tools/tplg_parser/process.c b/tools/tplg_parser/process.c index e3b12ec55b8b..5f0d8cdddaf2 100644 --- a/tools/tplg_parser/process.c +++ b/tools/tplg_parser/process.c @@ -225,7 +225,7 @@ int tplg_new_process(struct tplg_context *ctx, void *process, size_t process_siz /* call ctl creation callback if needed */ if (ctx->ctl_cb) - ctx->ctl_cb(ctl, process, ctx->ctl_arg); + ctx->ctl_cb(ctl, process, ctx->ctl_arg, 0); /* Merge process and priv_data into process_ipc */ if (!priv_data)