ASoC: SOF: ipc4-control: Support for Switch and Enum controls
Subsystems (3)
Peter Ujfalusi
maintainercover ASoC: SOF: ipc4-control: Support for Switch and Enum controls
Hi,
Currently IPC4 has no notion of a switch or enum type of control which is a generic concept in ALSA.
The generic support for these control types will be as follows:
- large config is used to send the channel-value par array
- param_id of a SWITCH type is 200
- param_id of an ENUM type is 201
Each module need to support a switch or/and enum must handle these universal param_ids.
The message payload is described by struct sof_ipc4_control_msg_payload.
Regards,
Peter
---
Peter Ujfalusi (3):
ASoC: SOF: ipc4-topology: Add definition for generic switch/enum
control
ASoC: SOF: ipc4-control: Add support for ALSA switch control
ASoC: SOF: ipc4-control: Add support for ALSA enum control
sound/soc/sof/ipc4-control.c | 175 +++++++++++++++++++++++++++++++++- sound/soc/sof/ipc4-topology.c | 49 +++++++++- sound/soc/sof/ipc4-topology.h | 19 +++- 3 files changed, 237 insertions(+), 6 deletions(-)
Signature · 1 line
2.42.0
Peter Ujfalusi
maintainerPATCH 1/3 ASoC: SOF: ipc4-topology: Add definition for generic switch/enum control
Currently IPC4 has no notion of a switch or enum type of control which is a generic concept in ALSA.
The generic support for these control types will be as follows:
- large config is used to send the channel-value par array
- param_id of a SWITCH type is 200
- param_id of an ENUM type is 201
Each module need to support a switch or/and enum must handle these universal param_ids.
The message payload is described by struct sof_ipc4_control_msg_payload.
--- sound/soc/sof/ipc4-topology.h | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-)
sound/soc/sof/ipc4-topology.h +18 -1
@@ -319,7 +319,7 @@ struct sof_ipc4_copier {/** * struct sof_ipc4_ctrl_value_chan: generic channel mapped value data * @channel: Channel ID -* @value: gain value +* @value: Value associated with @channel */ struct sof_ipc4_ctrl_value_chan {u32 channel;
@@ -343,6 +343,23 @@ struct sof_ipc4_control_data { }; }; +#define SOF_IPC4_SWITCH_CONTROL_PARAM_ID 200 +#define SOF_IPC4_ENUM_CONTROL_PARAM_ID 201 + +/** +* struct sof_ipc4_control_msg_payload - IPC payload for kcontrol parameters +* @id: unique id of the control +* @num_elems: Number of elements in the chanv array +* @reserved: reserved for future use, must be set to 0 +* @chanv: channel ID and value array +*/ +struct sof_ipc4_control_msg_payload { +uint16_t id; +uint16_t num_elems; +uint32_t reserved[4 ]; + DECLARE_FLEX_ARRAY(struct sof_ipc4_ctrl_value_chan, chanv); +} __packed; +/** * struct sof_ipc4_gain_data - IPC gain blob * @channels: Channels
Signature · 1 line
2.42.0
Peter Ujfalusi
maintainerPATCH 2/3 ASoC: SOF: ipc4-control: Add support for ALSA switch control
In reply to Peter UjfalusiVolume controls with a max value of 1 are switches.
Switch controls use generic param_id and a generic struct where the data is passed to the firmware.
--- sound/soc/sof/ipc4-control.c | 111 +++++++++++++++++++++++++++++++++- sound/soc/sof/ipc4-topology.c | 16 ++++- 2 files changed, 122 insertions(+), 5 deletions(-)
sound/soc/sof/ipc4-control.c +109 -2
@@ -201,6 +201,102 @@ static int sof_ipc4_volume_get(struct snd_sof_control *scontrol,return 0 ; } +static int +sof_ipc4_set_generic_control_data (struct snd_sof_dev *sdev, +struct snd_sof_widget *swidget, +struct snd_sof_control *scontrol,bool lock) +{ +struct sof_ipc4_control_data *cdata = scontrol->ipc_control_data; +struct sof_ipc4_control_msg_payload *data; +struct sof_ipc4_msg *msg = &cdata->msg; +size_t data_size; +unsigned int i; +int ret; + + data_size =struct_size (data, chanv, scontrol->num_channels); + data =kzalloc (data_size,GFP_KERNEL ); +if (!data) +return -ENOMEM ; + + data->id = cdata->index; + data->num_elems = scontrol->num_channels; +for (i =0 ; i < scontrol->num_channels; i++) { + data->chanv[i].channel = cdata->chanv[i].channel; + data->chanv[i].value = cdata->chanv[i].value; + } + + msg->data_ptr = data; + msg->data_size = data_size; + + ret =sof_ipc4_set_get_kcontrol_data (scontrol, true, lock); + msg->data_ptr =NULL ; + msg->data_size =0 ; +if (ret <0 ) +dev_err (sdev->dev,"Failed to set control update for %s\n" , + scontrol->name); + +kfree (data); + +return ret; +} + +static bool sof_ipc4_switch_put (struct snd_sof_control *scontrol, +struct snd_ctl_elem_value *ucontrol) +{ +struct sof_ipc4_control_data *cdata = scontrol->ipc_control_data; +struct snd_soc_component *scomp = scontrol->scomp; +struct snd_sof_dev *sdev =snd_soc_component_get_drvdata (scomp); +struct snd_sof_widget *swidget; +bool widget_found = false; +bool change = false; +unsigned int i; +u32 value; +int ret; + +/* update each channel */ +for (i =0 ; i < scontrol->num_channels; i++) { + value = ucontrol->value.integer.value[i]; + change = change || (value != cdata->chanv[i].value); + cdata->chanv[i].channel = i; + cdata->chanv[i].value = value; + } + +if (!pm_runtime_active (scomp->dev)) +return change; + +/* find widget associated with the control */ +list_for_each_entry (swidget, &sdev->widget_list, list) { +if (swidget->comp_id == scontrol->comp_id) { + widget_found = true; +break ; + } + } + +if (!widget_found) { +dev_err (scomp->dev,"Failed to find widget for kcontrol %s\n" , scontrol->name); +return false; + } + + ret =sof_ipc4_set_generic_control_data (sdev, swidget, scontrol, true); +if (ret <0 ) +return false; + +return change; +} + +static int sof_ipc4_switch_get (struct snd_sof_control *scontrol, +struct snd_ctl_elem_value *ucontrol) +{ +struct sof_ipc4_control_data *cdata = scontrol->ipc_control_data; +unsigned int i; + +/* read back each channel */ +for (i =0 ; i < scontrol->num_channels; i++) + ucontrol->value.integer.value[i] = cdata->chanv[i].value; + +return 0 ; +} +static int sof_ipc4_set_get_bytes_data (struct snd_sof_dev *sdev,struct snd_sof_control *scontrol,bool set,bool lock)
@@ -438,6 +534,16 @@ static int sof_ipc4_bytes_ext_volatile_get(struct snd_sof_control *scontrol,return _sof_ipc4_bytes_ext_get (scontrol, binary_data, size, true); } +static int +sof_ipc4_volsw_setup (struct snd_sof_dev *sdev,struct snd_sof_widget *swidget, +struct snd_sof_control *scontrol) +{ +if (scontrol->max ==1 ) +return sof_ipc4_set_generic_control_data (sdev, swidget, scontrol, false); + +return sof_ipc4_set_volume_data (sdev, swidget, scontrol, false); +} +/* set up all controls for the widget */ static int sof_ipc4_widget_kcontrol_setup (struct snd_sof_dev *sdev,struct snd_sof_widget *swidget) {
@@ -450,8 +556,7 @@ static int sof_ipc4_widget_kcontrol_setup(struct snd_sof_dev *sdev, struct snd_scase SND_SOC_TPLG_CTL_VOLSW :case SND_SOC_TPLG_CTL_VOLSW_SX :case SND_SOC_TPLG_CTL_VOLSW_XR_SX : - ret =sof_ipc4_set_volume_data (sdev, swidget, - scontrol, false); + ret =sof_ipc4_volsw_setup (sdev, swidget, scontrol);break ;case SND_SOC_TPLG_CTL_BYTES : ret = sof_ipc4_set_get_bytes_data(sdev, scontrol,
@@ -498,6 +603,8 @@ sof_ipc4_set_up_volume_table(struct snd_sof_control *scontrol, int tlv[SOF_TLV_Iconst struct sof_ipc_tplg_control_ops tplg_ipc4_control_ops = { .volume_put = sof_ipc4_volume_put, .volume_get = sof_ipc4_volume_get, + .switch_put = sof_ipc4_switch_put, + .switch_get = sof_ipc4_switch_get, .bytes_put = sof_ipc4_bytes_put, .bytes_get = sof_ipc4_bytes_get, .bytes_ext_put = sof_ipc4_bytes_ext_put,
sound/soc/sof/ipc4-topology.c +13 -3
@@ -2107,12 +2107,22 @@ static int sof_ipc4_control_load_volume(struct snd_sof_dev *sdev, struct snd_sof msg->primary |=SOF_IPC4_MSG_DIR (SOF_IPC4_MSG_REQUEST ); msg->primary |=SOF_IPC4_MSG_TARGET (SOF_IPC4_MODULE_MSG ); - msg->extension =SOF_IPC4_MOD_EXT_MSG_PARAM_ID (SOF_IPC4_GAIN_PARAM_ID ); +/* volume controls with range 0-1 (off/on) are switch controls */ +if (scontrol->max ==1 ) + msg->extension =SOF_IPC4_MOD_EXT_MSG_PARAM_ID (SOF_IPC4_SWITCH_CONTROL_PARAM_ID ); +else + msg->extension =SOF_IPC4_MOD_EXT_MSG_PARAM_ID (SOF_IPC4_GAIN_PARAM_ID ); -/* set default volume values to 0dB in control */ for (i =0 ; i < scontrol->num_channels; i++) { control_data->chanv[i].channel = i; - control_data->chanv[i].value =SOF_IPC4_VOL_ZERO_DB ; +/* +* Default, initial values: +* - 0dB for volume controls +* - off (0) for switch controls - value already zero after +* memory allocation +*/ +if (scontrol->max >1 ) + control_data->chanv[i].value =SOF_IPC4_VOL_ZERO_DB ; }return 0 ;
Signature · 1 line
2.42.0
Peter Ujfalusi
maintainerPATCH 3/3 ASoC: SOF: ipc4-control: Add support for ALSA enum control
In reply to Peter UjfalusiEnum controls use generic param_id and a generic struct where the data is passed to the firmware.
--- sound/soc/sof/ipc4-control.c | 64 +++++++++++++++++++++++++++++++++++ sound/soc/sof/ipc4-topology.c | 33 ++++++++++++++++++ 2 files changed, 97 insertions(+)
sound/soc/sof/ipc4-control.c +64 -0
@@ -297,6 +297,63 @@ static int sof_ipc4_switch_get(struct snd_sof_control *scontrol,return 0 ; } +static bool sof_ipc4_enum_put (struct snd_sof_control *scontrol, +struct snd_ctl_elem_value *ucontrol) +{ +struct sof_ipc4_control_data *cdata = scontrol->ipc_control_data; +struct snd_soc_component *scomp = scontrol->scomp; +struct snd_sof_dev *sdev =snd_soc_component_get_drvdata (scomp); +struct snd_sof_widget *swidget; +bool widget_found = false; +bool change = false; +unsigned int i; +u32 value; +int ret; + +/* update each channel */ +for (i =0 ; i < scontrol->num_channels; i++) { + value = ucontrol->value.enumerated.item[i]; + change = change || (value != cdata->chanv[i].value); + cdata->chanv[i].channel = i; + cdata->chanv[i].value = value; + } + +if (!pm_runtime_active (scomp->dev)) +return change; + +/* find widget associated with the control */ +list_for_each_entry (swidget, &sdev->widget_list, list) { +if (swidget->comp_id == scontrol->comp_id) { + widget_found = true; +break ; + } + } + +if (!widget_found) { +dev_err (scomp->dev,"Failed to find widget for kcontrol %s\n" , scontrol->name); +return false; + } + + ret =sof_ipc4_set_generic_control_data (sdev, swidget, scontrol, true); +if (ret <0 ) +return false; + +return change; +} + +static int sof_ipc4_enum_get (struct snd_sof_control *scontrol, +struct snd_ctl_elem_value *ucontrol) +{ +struct sof_ipc4_control_data *cdata = scontrol->ipc_control_data; +unsigned int i; + +/* read back each channel */ +for (i =0 ; i < scontrol->num_channels; i++) + ucontrol->value.enumerated.item[i] = cdata->chanv[i].value; + +return 0 ; +} +static int sof_ipc4_set_get_bytes_data (struct snd_sof_dev *sdev,struct snd_sof_control *scontrol,bool set,bool lock)
@@ -562,6 +619,11 @@ static int sof_ipc4_widget_kcontrol_setup(struct snd_sof_dev *sdev, struct snd_s ret =sof_ipc4_set_get_bytes_data (sdev, scontrol, true, false);break ; +case SND_SOC_TPLG_CTL_ENUM : +case SND_SOC_TPLG_CTL_ENUM_VALUE : + ret =sof_ipc4_set_generic_control_data (sdev, swidget, + scontrol, false); +break ;default :break ; }
@@ -605,6 +667,8 @@ const struct sof_ipc_tplg_control_ops tplg_ipc4_control_ops = { .volume_get = sof_ipc4_volume_get, .switch_put = sof_ipc4_switch_put, .switch_get = sof_ipc4_switch_get, + .enum_put = sof_ipc4_enum_put, + .enum_get = sof_ipc4_enum_get, .bytes_put = sof_ipc4_bytes_put, .bytes_get = sof_ipc4_bytes_get, .bytes_ext_put = sof_ipc4_bytes_ext_put,
sound/soc/sof/ipc4-topology.c +33 -0
@@ -2128,6 +2128,36 @@ static int sof_ipc4_control_load_volume(struct snd_sof_dev *sdev, struct snd_sofreturn 0 ; } +static int sof_ipc4_control_load_enum (struct snd_sof_dev *sdev,struct snd_sof_control *scontrol) +{ +struct sof_ipc4_control_data *control_data; +struct sof_ipc4_msg *msg; +int i; + + scontrol->size =struct_size (control_data, chanv, scontrol->num_channels); + +/* scontrol->ipc_control_data will be freed in sof_control_unload */ + scontrol->ipc_control_data =kzalloc (scontrol->size,GFP_KERNEL ); +if (!scontrol->ipc_control_data) +return -ENOMEM ; + + control_data = scontrol->ipc_control_data; + control_data->index = scontrol->index; + + msg = &control_data->msg; + msg->primary =SOF_IPC4_MSG_TYPE_SET (SOF_IPC4_MOD_LARGE_CONFIG_SET ); + msg->primary |=SOF_IPC4_MSG_DIR (SOF_IPC4_MSG_REQUEST ); + msg->primary |=SOF_IPC4_MSG_TARGET (SOF_IPC4_MODULE_MSG ); + + msg->extension =SOF_IPC4_MOD_EXT_MSG_PARAM_ID (SOF_IPC4_ENUM_CONTROL_PARAM_ID ); + +/* Default, initial value for enums: first enum entry is selected (0) */ +for (i =0 ; i < scontrol->num_channels; i++) + control_data->chanv[i].channel = i; + +return 0 ; +} +static int sof_ipc4_control_load_bytes (struct snd_sof_dev *sdev,struct snd_sof_control *scontrol) {struct sof_ipc4_control_data *control_data;
@@ -2202,6 +2232,9 @@ static int sof_ipc4_control_setup(struct snd_sof_dev *sdev, struct snd_sof_contrreturn sof_ipc4_control_load_volume (sdev, scontrol);case SND_SOC_TPLG_CTL_BYTES :return sof_ipc4_control_load_bytes (sdev, scontrol); +case SND_SOC_TPLG_CTL_ENUM : +case SND_SOC_TPLG_CTL_ENUM_VALUE : +return sof_ipc4_control_load_enum (sdev, scontrol);default :break ; }
Signature · 1 line
2.42.0
Mark Brown
maintainerQuoted 10 lines · Peter Ujfalusi
Currently IPC4 has no notion of a switch or enum type of control which is a generic concept in ALSA.
The generic support for these control types will be as follows:
- large config is used to send the channel-value par array
-param_idof a SWITCH type is 200
-param_idof an ENUM type is 201[...]
Applied to
https://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git for-next
Thanks!
[1/3] ASoC: SOF: ipc4-topology: Add definition for generic switch/enum control
commit: 060a07cd9bc69eba2da33ed96b1fa69ead60bab1
[2/3] ASoC: SOF: ipc4-control: Add support for ALSA switch control
commit: 4a2fd607b7ca6128ee3532161505da7624197f55
[3/3] ASoC: SOF: ipc4-control: Add support for ALSA enum control
commit: 07a866a41982c896dc46476f57d209a200602946All being well this means that it will be integrated into the linux-next tree (usually sometime in the next 24 hours) and sent to Linus during the next merge window (or sooner if it is a bug fix), however if problems are discovered then the patch may be dropped or reverted.
You may get further e-mails resulting from automated or manual testing and review of the tree, please engage with people reporting problems and send followup patches addressing any issues that are reported if needed.
If any updates are required or you are submitting further changes they should be sent as incremental updates against current git, existing patches will not be replaced.
Please add any relevant lists and maintainers to the CCs when replying to this mail.
Thanks,
Mark