110f461d7SRanjani Sridharan // SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) 210f461d7SRanjani Sridharan // 310f461d7SRanjani Sridharan // This file is provided under a dual BSD/GPLv2 license. When using or 410f461d7SRanjani Sridharan // redistributing this file, you may do so under either license. 510f461d7SRanjani Sridharan // 610f461d7SRanjani Sridharan // Copyright(c) 2021 Intel Corporation. All rights reserved. 710f461d7SRanjani Sridharan // 810f461d7SRanjani Sridharan // 910f461d7SRanjani Sridharan 1010f461d7SRanjani Sridharan #include "sof-priv.h" 1110f461d7SRanjani Sridharan #include "sof-audio.h" 12f80beaf6SPeter Ujfalusi #include "ipc3-priv.h" 1310f461d7SRanjani Sridharan 14e760f102SPeter Ujfalusi /* IPC set()/get() for kcontrols. */ 15e760f102SPeter Ujfalusi static int sof_ipc3_set_get_kcontrol_data(struct snd_sof_control *scontrol, bool set) 16e760f102SPeter Ujfalusi { 17e760f102SPeter Ujfalusi struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scontrol->scomp); 18e760f102SPeter Ujfalusi struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data; 19e760f102SPeter Ujfalusi const struct sof_ipc_ops *iops = sdev->ipc->ops; 20e760f102SPeter Ujfalusi enum sof_ipc_ctrl_type ctrl_type; 21e760f102SPeter Ujfalusi struct snd_sof_widget *swidget; 22e760f102SPeter Ujfalusi bool widget_found = false; 23e760f102SPeter Ujfalusi u32 ipc_cmd, msg_bytes; 24e760f102SPeter Ujfalusi 25e760f102SPeter Ujfalusi list_for_each_entry(swidget, &sdev->widget_list, list) { 26e760f102SPeter Ujfalusi if (swidget->comp_id == scontrol->comp_id) { 27e760f102SPeter Ujfalusi widget_found = true; 28e760f102SPeter Ujfalusi break; 29e760f102SPeter Ujfalusi } 30e760f102SPeter Ujfalusi } 31e760f102SPeter Ujfalusi 32e760f102SPeter Ujfalusi if (!widget_found) { 33e760f102SPeter Ujfalusi dev_err(sdev->dev, "%s: can't find widget with id %d\n", __func__, 34e760f102SPeter Ujfalusi scontrol->comp_id); 35e760f102SPeter Ujfalusi return -EINVAL; 36e760f102SPeter Ujfalusi } 37e760f102SPeter Ujfalusi 38e760f102SPeter Ujfalusi /* 39e760f102SPeter Ujfalusi * Volatile controls should always be part of static pipelines and the widget use_count 40e760f102SPeter Ujfalusi * would always be > 0 in this case. For the others, just return the cached value if the 41e760f102SPeter Ujfalusi * widget is not set up. 42e760f102SPeter Ujfalusi */ 43e760f102SPeter Ujfalusi if (!swidget->use_count) 44e760f102SPeter Ujfalusi return 0; 45e760f102SPeter Ujfalusi 46e760f102SPeter Ujfalusi /* 47e760f102SPeter Ujfalusi * Select the IPC cmd and the ctrl_type based on the ctrl_cmd and the 48e760f102SPeter Ujfalusi * direction 49e760f102SPeter Ujfalusi * Note: SOF_CTRL_TYPE_VALUE_COMP_* is not used and supported currently 50e760f102SPeter Ujfalusi * for ctrl_type 51e760f102SPeter Ujfalusi */ 52e760f102SPeter Ujfalusi if (cdata->cmd == SOF_CTRL_CMD_BINARY) { 53e760f102SPeter Ujfalusi ipc_cmd = set ? SOF_IPC_COMP_SET_DATA : SOF_IPC_COMP_GET_DATA; 54e760f102SPeter Ujfalusi ctrl_type = set ? SOF_CTRL_TYPE_DATA_SET : SOF_CTRL_TYPE_DATA_GET; 55e760f102SPeter Ujfalusi } else { 56e760f102SPeter Ujfalusi ipc_cmd = set ? SOF_IPC_COMP_SET_VALUE : SOF_IPC_COMP_GET_VALUE; 57e760f102SPeter Ujfalusi ctrl_type = set ? SOF_CTRL_TYPE_VALUE_CHAN_SET : SOF_CTRL_TYPE_VALUE_CHAN_GET; 58e760f102SPeter Ujfalusi } 59e760f102SPeter Ujfalusi 60e760f102SPeter Ujfalusi cdata->rhdr.hdr.cmd = SOF_IPC_GLB_COMP_MSG | ipc_cmd; 61e760f102SPeter Ujfalusi cdata->type = ctrl_type; 62e760f102SPeter Ujfalusi cdata->comp_id = scontrol->comp_id; 63e760f102SPeter Ujfalusi cdata->msg_index = 0; 64e760f102SPeter Ujfalusi 65e760f102SPeter Ujfalusi /* calculate header and data size */ 66e760f102SPeter Ujfalusi switch (cdata->type) { 67e760f102SPeter Ujfalusi case SOF_CTRL_TYPE_VALUE_CHAN_GET: 68e760f102SPeter Ujfalusi case SOF_CTRL_TYPE_VALUE_CHAN_SET: 69e760f102SPeter Ujfalusi cdata->num_elems = scontrol->num_channels; 70e760f102SPeter Ujfalusi 71e760f102SPeter Ujfalusi msg_bytes = scontrol->num_channels * 72e760f102SPeter Ujfalusi sizeof(struct sof_ipc_ctrl_value_chan); 73e760f102SPeter Ujfalusi msg_bytes += sizeof(struct sof_ipc_ctrl_data); 74e760f102SPeter Ujfalusi break; 75e760f102SPeter Ujfalusi case SOF_CTRL_TYPE_DATA_GET: 76e760f102SPeter Ujfalusi case SOF_CTRL_TYPE_DATA_SET: 77e760f102SPeter Ujfalusi cdata->num_elems = cdata->data->size; 78e760f102SPeter Ujfalusi 79e760f102SPeter Ujfalusi msg_bytes = cdata->data->size; 80e760f102SPeter Ujfalusi msg_bytes += sizeof(struct sof_ipc_ctrl_data) + 81e760f102SPeter Ujfalusi sizeof(struct sof_abi_hdr); 82e760f102SPeter Ujfalusi break; 83e760f102SPeter Ujfalusi default: 84e760f102SPeter Ujfalusi return -EINVAL; 85e760f102SPeter Ujfalusi } 86e760f102SPeter Ujfalusi 87e760f102SPeter Ujfalusi cdata->rhdr.hdr.size = msg_bytes; 88e760f102SPeter Ujfalusi cdata->elems_remaining = 0; 89e760f102SPeter Ujfalusi 90e760f102SPeter Ujfalusi return iops->set_get_data(sdev, cdata, cdata->rhdr.hdr.size, set); 91e760f102SPeter Ujfalusi } 92e760f102SPeter Ujfalusi 93838d04f3SRanjani Sridharan static void snd_sof_refresh_control(struct snd_sof_control *scontrol) 94838d04f3SRanjani Sridharan { 95838d04f3SRanjani Sridharan struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data; 96838d04f3SRanjani Sridharan struct snd_soc_component *scomp = scontrol->scomp; 97838d04f3SRanjani Sridharan int ret; 98838d04f3SRanjani Sridharan 99838d04f3SRanjani Sridharan if (!scontrol->comp_data_dirty) 100838d04f3SRanjani Sridharan return; 101838d04f3SRanjani Sridharan 102838d04f3SRanjani Sridharan if (!pm_runtime_active(scomp->dev)) 103838d04f3SRanjani Sridharan return; 104838d04f3SRanjani Sridharan 105838d04f3SRanjani Sridharan /* set the ABI header values */ 106838d04f3SRanjani Sridharan cdata->data->magic = SOF_ABI_MAGIC; 107838d04f3SRanjani Sridharan cdata->data->abi = SOF_ABI_VERSION; 108838d04f3SRanjani Sridharan 109838d04f3SRanjani Sridharan /* refresh the component data from DSP */ 110838d04f3SRanjani Sridharan scontrol->comp_data_dirty = false; 111e760f102SPeter Ujfalusi ret = sof_ipc3_set_get_kcontrol_data(scontrol, false); 112838d04f3SRanjani Sridharan if (ret < 0) { 113838d04f3SRanjani Sridharan dev_err(scomp->dev, "Failed to get control data: %d\n", ret); 114838d04f3SRanjani Sridharan 115838d04f3SRanjani Sridharan /* Set the flag to re-try next time to get the data */ 116838d04f3SRanjani Sridharan scontrol->comp_data_dirty = true; 117838d04f3SRanjani Sridharan } 118838d04f3SRanjani Sridharan } 119838d04f3SRanjani Sridharan 120838d04f3SRanjani Sridharan static int sof_ipc3_volume_get(struct snd_sof_control *scontrol, 121838d04f3SRanjani Sridharan struct snd_ctl_elem_value *ucontrol) 122838d04f3SRanjani Sridharan { 123838d04f3SRanjani Sridharan struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data; 124838d04f3SRanjani Sridharan unsigned int channels = scontrol->num_channels; 125838d04f3SRanjani Sridharan unsigned int i; 126838d04f3SRanjani Sridharan 127838d04f3SRanjani Sridharan snd_sof_refresh_control(scontrol); 128838d04f3SRanjani Sridharan 129838d04f3SRanjani Sridharan /* read back each channel */ 130838d04f3SRanjani Sridharan for (i = 0; i < channels; i++) 131838d04f3SRanjani Sridharan ucontrol->value.integer.value[i] = ipc_to_mixer(cdata->chanv[i].value, 132838d04f3SRanjani Sridharan scontrol->volume_table, 133838d04f3SRanjani Sridharan scontrol->max + 1); 134838d04f3SRanjani Sridharan 135838d04f3SRanjani Sridharan return 0; 136838d04f3SRanjani Sridharan } 137838d04f3SRanjani Sridharan 138838d04f3SRanjani Sridharan static bool sof_ipc3_volume_put(struct snd_sof_control *scontrol, 139838d04f3SRanjani Sridharan struct snd_ctl_elem_value *ucontrol) 140838d04f3SRanjani Sridharan { 141838d04f3SRanjani Sridharan struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data; 142838d04f3SRanjani Sridharan struct snd_soc_component *scomp = scontrol->scomp; 143838d04f3SRanjani Sridharan unsigned int channels = scontrol->num_channels; 144838d04f3SRanjani Sridharan unsigned int i; 145838d04f3SRanjani Sridharan bool change = false; 146838d04f3SRanjani Sridharan 147838d04f3SRanjani Sridharan /* update each channel */ 148838d04f3SRanjani Sridharan for (i = 0; i < channels; i++) { 149838d04f3SRanjani Sridharan u32 value = mixer_to_ipc(ucontrol->value.integer.value[i], 150838d04f3SRanjani Sridharan scontrol->volume_table, scontrol->max + 1); 151838d04f3SRanjani Sridharan 152838d04f3SRanjani Sridharan change = change || (value != cdata->chanv[i].value); 153838d04f3SRanjani Sridharan cdata->chanv[i].channel = i; 154838d04f3SRanjani Sridharan cdata->chanv[i].value = value; 155838d04f3SRanjani Sridharan } 156838d04f3SRanjani Sridharan 157838d04f3SRanjani Sridharan /* notify DSP of mixer updates */ 158838d04f3SRanjani Sridharan if (pm_runtime_active(scomp->dev)) { 159e760f102SPeter Ujfalusi int ret = sof_ipc3_set_get_kcontrol_data(scontrol, true); 160838d04f3SRanjani Sridharan 161838d04f3SRanjani Sridharan if (ret < 0) { 162838d04f3SRanjani Sridharan dev_err(scomp->dev, "Failed to set mixer updates for %s\n", 163838d04f3SRanjani Sridharan scontrol->name); 164838d04f3SRanjani Sridharan return false; 165838d04f3SRanjani Sridharan } 166838d04f3SRanjani Sridharan } 167838d04f3SRanjani Sridharan 168838d04f3SRanjani Sridharan return change; 169838d04f3SRanjani Sridharan } 170838d04f3SRanjani Sridharan 171a6668746SRanjani Sridharan static int sof_ipc3_switch_get(struct snd_sof_control *scontrol, 172a6668746SRanjani Sridharan struct snd_ctl_elem_value *ucontrol) 173a6668746SRanjani Sridharan { 174a6668746SRanjani Sridharan struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data; 175a6668746SRanjani Sridharan unsigned int channels = scontrol->num_channels; 176a6668746SRanjani Sridharan unsigned int i; 177a6668746SRanjani Sridharan 178a6668746SRanjani Sridharan snd_sof_refresh_control(scontrol); 179a6668746SRanjani Sridharan 180a6668746SRanjani Sridharan /* read back each channel */ 181a6668746SRanjani Sridharan for (i = 0; i < channels; i++) 182a6668746SRanjani Sridharan ucontrol->value.integer.value[i] = cdata->chanv[i].value; 183a6668746SRanjani Sridharan 184a6668746SRanjani Sridharan return 0; 185a6668746SRanjani Sridharan } 186a6668746SRanjani Sridharan 187a6668746SRanjani Sridharan static bool sof_ipc3_switch_put(struct snd_sof_control *scontrol, 188a6668746SRanjani Sridharan struct snd_ctl_elem_value *ucontrol) 189a6668746SRanjani Sridharan { 190a6668746SRanjani Sridharan struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data; 191a6668746SRanjani Sridharan struct snd_soc_component *scomp = scontrol->scomp; 192a6668746SRanjani Sridharan unsigned int channels = scontrol->num_channels; 193a6668746SRanjani Sridharan unsigned int i; 194a6668746SRanjani Sridharan bool change = false; 195a6668746SRanjani Sridharan u32 value; 196a6668746SRanjani Sridharan 197a6668746SRanjani Sridharan /* update each channel */ 198a6668746SRanjani Sridharan for (i = 0; i < channels; i++) { 199a6668746SRanjani Sridharan value = ucontrol->value.integer.value[i]; 200a6668746SRanjani Sridharan change = change || (value != cdata->chanv[i].value); 201a6668746SRanjani Sridharan cdata->chanv[i].channel = i; 202a6668746SRanjani Sridharan cdata->chanv[i].value = value; 203a6668746SRanjani Sridharan } 204a6668746SRanjani Sridharan 205a6668746SRanjani Sridharan /* notify DSP of mixer updates */ 206a6668746SRanjani Sridharan if (pm_runtime_active(scomp->dev)) { 207e760f102SPeter Ujfalusi int ret = sof_ipc3_set_get_kcontrol_data(scontrol, true); 208a6668746SRanjani Sridharan 209a6668746SRanjani Sridharan if (ret < 0) { 210a6668746SRanjani Sridharan dev_err(scomp->dev, "Failed to set mixer updates for %s\n", 211a6668746SRanjani Sridharan scontrol->name); 212a6668746SRanjani Sridharan return false; 213a6668746SRanjani Sridharan } 214a6668746SRanjani Sridharan } 215a6668746SRanjani Sridharan 216a6668746SRanjani Sridharan return change; 217a6668746SRanjani Sridharan } 218a6668746SRanjani Sridharan 219049307aaSRanjani Sridharan static int sof_ipc3_enum_get(struct snd_sof_control *scontrol, 220049307aaSRanjani Sridharan struct snd_ctl_elem_value *ucontrol) 221049307aaSRanjani Sridharan { 222049307aaSRanjani Sridharan struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data; 223049307aaSRanjani Sridharan unsigned int channels = scontrol->num_channels; 224049307aaSRanjani Sridharan unsigned int i; 225049307aaSRanjani Sridharan 226049307aaSRanjani Sridharan snd_sof_refresh_control(scontrol); 227049307aaSRanjani Sridharan 228049307aaSRanjani Sridharan /* read back each channel */ 229049307aaSRanjani Sridharan for (i = 0; i < channels; i++) 230049307aaSRanjani Sridharan ucontrol->value.enumerated.item[i] = cdata->chanv[i].value; 231049307aaSRanjani Sridharan 232049307aaSRanjani Sridharan return 0; 233049307aaSRanjani Sridharan } 234049307aaSRanjani Sridharan 235049307aaSRanjani Sridharan static bool sof_ipc3_enum_put(struct snd_sof_control *scontrol, 236049307aaSRanjani Sridharan struct snd_ctl_elem_value *ucontrol) 237049307aaSRanjani Sridharan { 238049307aaSRanjani Sridharan struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data; 239049307aaSRanjani Sridharan struct snd_soc_component *scomp = scontrol->scomp; 240049307aaSRanjani Sridharan unsigned int channels = scontrol->num_channels; 241049307aaSRanjani Sridharan unsigned int i; 242049307aaSRanjani Sridharan bool change = false; 243049307aaSRanjani Sridharan u32 value; 244049307aaSRanjani Sridharan 245049307aaSRanjani Sridharan /* update each channel */ 246049307aaSRanjani Sridharan for (i = 0; i < channels; i++) { 247049307aaSRanjani Sridharan value = ucontrol->value.enumerated.item[i]; 248049307aaSRanjani Sridharan change = change || (value != cdata->chanv[i].value); 249049307aaSRanjani Sridharan cdata->chanv[i].channel = i; 250049307aaSRanjani Sridharan cdata->chanv[i].value = value; 251049307aaSRanjani Sridharan } 252049307aaSRanjani Sridharan 253049307aaSRanjani Sridharan /* notify DSP of enum updates */ 254049307aaSRanjani Sridharan if (pm_runtime_active(scomp->dev)) { 255e760f102SPeter Ujfalusi int ret = sof_ipc3_set_get_kcontrol_data(scontrol, true); 256049307aaSRanjani Sridharan 257049307aaSRanjani Sridharan if (ret < 0) { 258049307aaSRanjani Sridharan dev_err(scomp->dev, "Failed to set enum updates for %s\n", 259049307aaSRanjani Sridharan scontrol->name); 260049307aaSRanjani Sridharan return false; 261049307aaSRanjani Sridharan } 262049307aaSRanjani Sridharan } 263049307aaSRanjani Sridharan 264049307aaSRanjani Sridharan return change; 265049307aaSRanjani Sridharan } 266049307aaSRanjani Sridharan 267544ac885SRanjani Sridharan static int sof_ipc3_bytes_get(struct snd_sof_control *scontrol, 268544ac885SRanjani Sridharan struct snd_ctl_elem_value *ucontrol) 269544ac885SRanjani Sridharan { 270544ac885SRanjani Sridharan struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data; 271544ac885SRanjani Sridharan struct snd_soc_component *scomp = scontrol->scomp; 272544ac885SRanjani Sridharan struct sof_abi_hdr *data = cdata->data; 273544ac885SRanjani Sridharan size_t size; 274544ac885SRanjani Sridharan 275544ac885SRanjani Sridharan snd_sof_refresh_control(scontrol); 276544ac885SRanjani Sridharan 277544ac885SRanjani Sridharan if (scontrol->max_size > sizeof(ucontrol->value.bytes.data)) { 278544ac885SRanjani Sridharan dev_err_ratelimited(scomp->dev, "data max %zu exceeds ucontrol data array size\n", 279544ac885SRanjani Sridharan scontrol->max_size); 280544ac885SRanjani Sridharan return -EINVAL; 281544ac885SRanjani Sridharan } 282544ac885SRanjani Sridharan 283544ac885SRanjani Sridharan /* be->max has been verified to be >= sizeof(struct sof_abi_hdr) */ 284544ac885SRanjani Sridharan if (data->size > scontrol->max_size - sizeof(*data)) { 285544ac885SRanjani Sridharan dev_err_ratelimited(scomp->dev, 286544ac885SRanjani Sridharan "%u bytes of control data is invalid, max is %zu\n", 287544ac885SRanjani Sridharan data->size, scontrol->max_size - sizeof(*data)); 288544ac885SRanjani Sridharan return -EINVAL; 289544ac885SRanjani Sridharan } 290544ac885SRanjani Sridharan 291544ac885SRanjani Sridharan size = data->size + sizeof(*data); 292544ac885SRanjani Sridharan 293544ac885SRanjani Sridharan /* copy back to kcontrol */ 294544ac885SRanjani Sridharan memcpy(ucontrol->value.bytes.data, data, size); 295544ac885SRanjani Sridharan 296544ac885SRanjani Sridharan return 0; 297544ac885SRanjani Sridharan } 298544ac885SRanjani Sridharan 299544ac885SRanjani Sridharan static int sof_ipc3_bytes_put(struct snd_sof_control *scontrol, 300544ac885SRanjani Sridharan struct snd_ctl_elem_value *ucontrol) 301544ac885SRanjani Sridharan { 302544ac885SRanjani Sridharan struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data; 303544ac885SRanjani Sridharan struct snd_soc_component *scomp = scontrol->scomp; 304544ac885SRanjani Sridharan struct sof_abi_hdr *data = cdata->data; 305544ac885SRanjani Sridharan size_t size; 306544ac885SRanjani Sridharan 307544ac885SRanjani Sridharan if (scontrol->max_size > sizeof(ucontrol->value.bytes.data)) { 308544ac885SRanjani Sridharan dev_err_ratelimited(scomp->dev, "data max %zu exceeds ucontrol data array size\n", 309544ac885SRanjani Sridharan scontrol->max_size); 310544ac885SRanjani Sridharan return -EINVAL; 311544ac885SRanjani Sridharan } 312544ac885SRanjani Sridharan 313544ac885SRanjani Sridharan /* scontrol->max_size has been verified to be >= sizeof(struct sof_abi_hdr) */ 314544ac885SRanjani Sridharan if (data->size > scontrol->max_size - sizeof(*data)) { 315544ac885SRanjani Sridharan dev_err_ratelimited(scomp->dev, "data size too big %u bytes max is %zu\n", 316544ac885SRanjani Sridharan data->size, scontrol->max_size - sizeof(*data)); 317544ac885SRanjani Sridharan return -EINVAL; 318544ac885SRanjani Sridharan } 319544ac885SRanjani Sridharan 320544ac885SRanjani Sridharan size = data->size + sizeof(*data); 321544ac885SRanjani Sridharan 322544ac885SRanjani Sridharan /* copy from kcontrol */ 323544ac885SRanjani Sridharan memcpy(data, ucontrol->value.bytes.data, size); 324544ac885SRanjani Sridharan 325544ac885SRanjani Sridharan /* notify DSP of byte control updates */ 326544ac885SRanjani Sridharan if (pm_runtime_active(scomp->dev)) 327e760f102SPeter Ujfalusi return sof_ipc3_set_get_kcontrol_data(scontrol, true); 328544ac885SRanjani Sridharan 329544ac885SRanjani Sridharan return 0; 330544ac885SRanjani Sridharan } 331544ac885SRanjani Sridharan 33267ec2a09SRanjani Sridharan static int sof_ipc3_bytes_ext_get(struct snd_sof_control *scontrol, 33367ec2a09SRanjani Sridharan const unsigned int __user *binary_data, unsigned int size) 33467ec2a09SRanjani Sridharan { 33567ec2a09SRanjani Sridharan struct snd_ctl_tlv __user *tlvd = (struct snd_ctl_tlv __user *)binary_data; 33667ec2a09SRanjani Sridharan struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data; 33767ec2a09SRanjani Sridharan struct snd_soc_component *scomp = scontrol->scomp; 33867ec2a09SRanjani Sridharan struct snd_ctl_tlv header; 33967ec2a09SRanjani Sridharan size_t data_size; 34067ec2a09SRanjani Sridharan 34167ec2a09SRanjani Sridharan snd_sof_refresh_control(scontrol); 34267ec2a09SRanjani Sridharan 34367ec2a09SRanjani Sridharan /* 34467ec2a09SRanjani Sridharan * Decrement the limit by ext bytes header size to 34567ec2a09SRanjani Sridharan * ensure the user space buffer is not exceeded. 34667ec2a09SRanjani Sridharan */ 34767ec2a09SRanjani Sridharan if (size < sizeof(struct snd_ctl_tlv)) 34867ec2a09SRanjani Sridharan return -ENOSPC; 34967ec2a09SRanjani Sridharan 35067ec2a09SRanjani Sridharan size -= sizeof(struct snd_ctl_tlv); 35167ec2a09SRanjani Sridharan 35267ec2a09SRanjani Sridharan /* set the ABI header values */ 35367ec2a09SRanjani Sridharan cdata->data->magic = SOF_ABI_MAGIC; 35467ec2a09SRanjani Sridharan cdata->data->abi = SOF_ABI_VERSION; 35567ec2a09SRanjani Sridharan 35667ec2a09SRanjani Sridharan /* check data size doesn't exceed max coming from topology */ 35767ec2a09SRanjani Sridharan if (cdata->data->size > scontrol->max_size - sizeof(struct sof_abi_hdr)) { 35867ec2a09SRanjani Sridharan dev_err_ratelimited(scomp->dev, "User data size %d exceeds max size %zu\n", 35967ec2a09SRanjani Sridharan cdata->data->size, 36067ec2a09SRanjani Sridharan scontrol->max_size - sizeof(struct sof_abi_hdr)); 36167ec2a09SRanjani Sridharan return -EINVAL; 36267ec2a09SRanjani Sridharan } 36367ec2a09SRanjani Sridharan 36467ec2a09SRanjani Sridharan data_size = cdata->data->size + sizeof(struct sof_abi_hdr); 36567ec2a09SRanjani Sridharan 36667ec2a09SRanjani Sridharan /* make sure we don't exceed size provided by user space for data */ 36767ec2a09SRanjani Sridharan if (data_size > size) 36867ec2a09SRanjani Sridharan return -ENOSPC; 36967ec2a09SRanjani Sridharan 37067ec2a09SRanjani Sridharan header.numid = cdata->cmd; 37167ec2a09SRanjani Sridharan header.length = data_size; 37267ec2a09SRanjani Sridharan if (copy_to_user(tlvd, &header, sizeof(struct snd_ctl_tlv))) 37367ec2a09SRanjani Sridharan return -EFAULT; 37467ec2a09SRanjani Sridharan 37567ec2a09SRanjani Sridharan if (copy_to_user(tlvd->tlv, cdata->data, data_size)) 37667ec2a09SRanjani Sridharan return -EFAULT; 37767ec2a09SRanjani Sridharan 37867ec2a09SRanjani Sridharan return 0; 37967ec2a09SRanjani Sridharan } 38067ec2a09SRanjani Sridharan 38167ec2a09SRanjani Sridharan static int sof_ipc3_bytes_ext_put(struct snd_sof_control *scontrol, 38267ec2a09SRanjani Sridharan const unsigned int __user *binary_data, 38367ec2a09SRanjani Sridharan unsigned int size) 38467ec2a09SRanjani Sridharan { 38567ec2a09SRanjani Sridharan const struct snd_ctl_tlv __user *tlvd = (const struct snd_ctl_tlv __user *)binary_data; 38667ec2a09SRanjani Sridharan struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data; 38767ec2a09SRanjani Sridharan struct snd_soc_component *scomp = scontrol->scomp; 38867ec2a09SRanjani Sridharan struct snd_ctl_tlv header; 38967ec2a09SRanjani Sridharan 39067ec2a09SRanjani Sridharan /* 39167ec2a09SRanjani Sridharan * The beginning of bytes data contains a header from where 39267ec2a09SRanjani Sridharan * the length (as bytes) is needed to know the correct copy 39367ec2a09SRanjani Sridharan * length of data from tlvd->tlv. 39467ec2a09SRanjani Sridharan */ 39567ec2a09SRanjani Sridharan if (copy_from_user(&header, tlvd, sizeof(struct snd_ctl_tlv))) 39667ec2a09SRanjani Sridharan return -EFAULT; 39767ec2a09SRanjani Sridharan 39867ec2a09SRanjani Sridharan /* make sure TLV info is consistent */ 39967ec2a09SRanjani Sridharan if (header.length + sizeof(struct snd_ctl_tlv) > size) { 40067ec2a09SRanjani Sridharan dev_err_ratelimited(scomp->dev, "Inconsistent TLV, data %d + header %zu > %d\n", 40167ec2a09SRanjani Sridharan header.length, sizeof(struct snd_ctl_tlv), size); 40267ec2a09SRanjani Sridharan return -EINVAL; 40367ec2a09SRanjani Sridharan } 40467ec2a09SRanjani Sridharan 40567ec2a09SRanjani Sridharan /* be->max is coming from topology */ 40667ec2a09SRanjani Sridharan if (header.length > scontrol->max_size) { 40767ec2a09SRanjani Sridharan dev_err_ratelimited(scomp->dev, "Bytes data size %d exceeds max %zu\n", 40867ec2a09SRanjani Sridharan header.length, scontrol->max_size); 40967ec2a09SRanjani Sridharan return -EINVAL; 41067ec2a09SRanjani Sridharan } 41167ec2a09SRanjani Sridharan 41267ec2a09SRanjani Sridharan /* Check that header id matches the command */ 41367ec2a09SRanjani Sridharan if (header.numid != cdata->cmd) { 41467ec2a09SRanjani Sridharan dev_err_ratelimited(scomp->dev, "Incorrect command for bytes put %d\n", 41567ec2a09SRanjani Sridharan header.numid); 41667ec2a09SRanjani Sridharan return -EINVAL; 41767ec2a09SRanjani Sridharan } 41867ec2a09SRanjani Sridharan 41967ec2a09SRanjani Sridharan if (copy_from_user(cdata->data, tlvd->tlv, header.length)) 42067ec2a09SRanjani Sridharan return -EFAULT; 42167ec2a09SRanjani Sridharan 42267ec2a09SRanjani Sridharan if (cdata->data->magic != SOF_ABI_MAGIC) { 42367ec2a09SRanjani Sridharan dev_err_ratelimited(scomp->dev, "Wrong ABI magic 0x%08x\n", cdata->data->magic); 42467ec2a09SRanjani Sridharan return -EINVAL; 42567ec2a09SRanjani Sridharan } 42667ec2a09SRanjani Sridharan 42767ec2a09SRanjani Sridharan if (SOF_ABI_VERSION_INCOMPATIBLE(SOF_ABI_VERSION, cdata->data->abi)) { 42867ec2a09SRanjani Sridharan dev_err_ratelimited(scomp->dev, "Incompatible ABI version 0x%08x\n", 42967ec2a09SRanjani Sridharan cdata->data->abi); 43067ec2a09SRanjani Sridharan return -EINVAL; 43167ec2a09SRanjani Sridharan } 43267ec2a09SRanjani Sridharan 43367ec2a09SRanjani Sridharan /* be->max has been verified to be >= sizeof(struct sof_abi_hdr) */ 43467ec2a09SRanjani Sridharan if (cdata->data->size > scontrol->max_size - sizeof(struct sof_abi_hdr)) { 43567ec2a09SRanjani Sridharan dev_err_ratelimited(scomp->dev, "Mismatch in ABI data size (truncated?)\n"); 43667ec2a09SRanjani Sridharan return -EINVAL; 43767ec2a09SRanjani Sridharan } 43867ec2a09SRanjani Sridharan 43967ec2a09SRanjani Sridharan /* notify DSP of byte control updates */ 44067ec2a09SRanjani Sridharan if (pm_runtime_active(scomp->dev)) 441e760f102SPeter Ujfalusi return sof_ipc3_set_get_kcontrol_data(scontrol, true); 44267ec2a09SRanjani Sridharan 44367ec2a09SRanjani Sridharan return 0; 44467ec2a09SRanjani Sridharan } 44567ec2a09SRanjani Sridharan 44667ec2a09SRanjani Sridharan static int sof_ipc3_bytes_ext_volatile_get(struct snd_sof_control *scontrol, 44767ec2a09SRanjani Sridharan const unsigned int __user *binary_data, 44867ec2a09SRanjani Sridharan unsigned int size) 44967ec2a09SRanjani Sridharan { 45067ec2a09SRanjani Sridharan struct snd_ctl_tlv __user *tlvd = (struct snd_ctl_tlv __user *)binary_data; 45167ec2a09SRanjani Sridharan struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data; 45267ec2a09SRanjani Sridharan struct snd_soc_component *scomp = scontrol->scomp; 45367ec2a09SRanjani Sridharan struct snd_ctl_tlv header; 45467ec2a09SRanjani Sridharan size_t data_size; 45567ec2a09SRanjani Sridharan int ret; 45667ec2a09SRanjani Sridharan 45767ec2a09SRanjani Sridharan /* 45867ec2a09SRanjani Sridharan * Decrement the limit by ext bytes header size to 45967ec2a09SRanjani Sridharan * ensure the user space buffer is not exceeded. 46067ec2a09SRanjani Sridharan */ 46167ec2a09SRanjani Sridharan if (size < sizeof(struct snd_ctl_tlv)) 46267ec2a09SRanjani Sridharan return -ENOSPC; 46367ec2a09SRanjani Sridharan 46467ec2a09SRanjani Sridharan size -= sizeof(struct snd_ctl_tlv); 46567ec2a09SRanjani Sridharan 46667ec2a09SRanjani Sridharan /* set the ABI header values */ 46767ec2a09SRanjani Sridharan cdata->data->magic = SOF_ABI_MAGIC; 46867ec2a09SRanjani Sridharan cdata->data->abi = SOF_ABI_VERSION; 46967ec2a09SRanjani Sridharan 47067ec2a09SRanjani Sridharan /* get all the component data from DSP */ 471e760f102SPeter Ujfalusi ret = sof_ipc3_set_get_kcontrol_data(scontrol, false); 47267ec2a09SRanjani Sridharan if (ret < 0) 47367ec2a09SRanjani Sridharan return ret; 47467ec2a09SRanjani Sridharan 47567ec2a09SRanjani Sridharan /* check data size doesn't exceed max coming from topology */ 47667ec2a09SRanjani Sridharan if (cdata->data->size > scontrol->max_size - sizeof(struct sof_abi_hdr)) { 47767ec2a09SRanjani Sridharan dev_err_ratelimited(scomp->dev, "User data size %d exceeds max size %zu\n", 47867ec2a09SRanjani Sridharan cdata->data->size, 47967ec2a09SRanjani Sridharan scontrol->max_size - sizeof(struct sof_abi_hdr)); 48067ec2a09SRanjani Sridharan return -EINVAL; 48167ec2a09SRanjani Sridharan } 48267ec2a09SRanjani Sridharan 48367ec2a09SRanjani Sridharan data_size = cdata->data->size + sizeof(struct sof_abi_hdr); 48467ec2a09SRanjani Sridharan 48567ec2a09SRanjani Sridharan /* make sure we don't exceed size provided by user space for data */ 48667ec2a09SRanjani Sridharan if (data_size > size) 48767ec2a09SRanjani Sridharan return -ENOSPC; 48867ec2a09SRanjani Sridharan 48967ec2a09SRanjani Sridharan header.numid = cdata->cmd; 49067ec2a09SRanjani Sridharan header.length = data_size; 49167ec2a09SRanjani Sridharan if (copy_to_user(tlvd, &header, sizeof(struct snd_ctl_tlv))) 49267ec2a09SRanjani Sridharan return -EFAULT; 49367ec2a09SRanjani Sridharan 49467ec2a09SRanjani Sridharan if (copy_to_user(tlvd->tlv, cdata->data, data_size)) 49567ec2a09SRanjani Sridharan return -EFAULT; 49667ec2a09SRanjani Sridharan 49767ec2a09SRanjani Sridharan return ret; 49867ec2a09SRanjani Sridharan } 49967ec2a09SRanjani Sridharan 50010f461d7SRanjani Sridharan static void snd_sof_update_control(struct snd_sof_control *scontrol, 50110f461d7SRanjani Sridharan struct sof_ipc_ctrl_data *cdata) 50210f461d7SRanjani Sridharan { 50310f461d7SRanjani Sridharan struct snd_soc_component *scomp = scontrol->scomp; 50410f461d7SRanjani Sridharan struct sof_ipc_ctrl_data *local_cdata; 50510f461d7SRanjani Sridharan int i; 50610f461d7SRanjani Sridharan 50710f461d7SRanjani Sridharan local_cdata = scontrol->ipc_control_data; 50810f461d7SRanjani Sridharan 50910f461d7SRanjani Sridharan if (cdata->cmd == SOF_CTRL_CMD_BINARY) { 51010f461d7SRanjani Sridharan if (cdata->num_elems != local_cdata->data->size) { 51110f461d7SRanjani Sridharan dev_err(scomp->dev, "cdata binary size mismatch %u - %u\n", 51210f461d7SRanjani Sridharan cdata->num_elems, local_cdata->data->size); 51310f461d7SRanjani Sridharan return; 51410f461d7SRanjani Sridharan } 51510f461d7SRanjani Sridharan 51610f461d7SRanjani Sridharan /* copy the new binary data */ 51710f461d7SRanjani Sridharan memcpy(local_cdata->data, cdata->data, cdata->num_elems); 51810f461d7SRanjani Sridharan } else if (cdata->num_elems != scontrol->num_channels) { 51910f461d7SRanjani Sridharan dev_err(scomp->dev, "cdata channel count mismatch %u - %d\n", 52010f461d7SRanjani Sridharan cdata->num_elems, scontrol->num_channels); 52110f461d7SRanjani Sridharan } else { 52210f461d7SRanjani Sridharan /* copy the new values */ 52310f461d7SRanjani Sridharan for (i = 0; i < cdata->num_elems; i++) 52410f461d7SRanjani Sridharan local_cdata->chanv[i].value = cdata->chanv[i].value; 52510f461d7SRanjani Sridharan } 52610f461d7SRanjani Sridharan } 52710f461d7SRanjani Sridharan 52810f461d7SRanjani Sridharan static void sof_ipc3_control_update(struct snd_sof_dev *sdev, void *ipc_control_message) 52910f461d7SRanjani Sridharan { 53010f461d7SRanjani Sridharan struct sof_ipc_ctrl_data *cdata = ipc_control_message; 53110f461d7SRanjani Sridharan struct snd_soc_dapm_widget *widget; 53210f461d7SRanjani Sridharan struct snd_sof_control *scontrol; 53310f461d7SRanjani Sridharan struct snd_sof_widget *swidget; 53410f461d7SRanjani Sridharan struct snd_kcontrol *kc = NULL; 53510f461d7SRanjani Sridharan struct soc_mixer_control *sm; 53610f461d7SRanjani Sridharan struct soc_bytes_ext *be; 53710f461d7SRanjani Sridharan size_t expected_size; 53810f461d7SRanjani Sridharan struct soc_enum *se; 53910f461d7SRanjani Sridharan bool found = false; 54010f461d7SRanjani Sridharan int i, type; 54110f461d7SRanjani Sridharan 54210f461d7SRanjani Sridharan if (cdata->type == SOF_CTRL_TYPE_VALUE_COMP_GET || 54310f461d7SRanjani Sridharan cdata->type == SOF_CTRL_TYPE_VALUE_COMP_SET) { 54410f461d7SRanjani Sridharan dev_err(sdev->dev, "Component data is not supported in control notification\n"); 54510f461d7SRanjani Sridharan return; 54610f461d7SRanjani Sridharan } 54710f461d7SRanjani Sridharan 54810f461d7SRanjani Sridharan /* Find the swidget first */ 54910f461d7SRanjani Sridharan list_for_each_entry(swidget, &sdev->widget_list, list) { 55010f461d7SRanjani Sridharan if (swidget->comp_id == cdata->comp_id) { 55110f461d7SRanjani Sridharan found = true; 55210f461d7SRanjani Sridharan break; 55310f461d7SRanjani Sridharan } 55410f461d7SRanjani Sridharan } 55510f461d7SRanjani Sridharan 55610f461d7SRanjani Sridharan if (!found) 55710f461d7SRanjani Sridharan return; 55810f461d7SRanjani Sridharan 55910f461d7SRanjani Sridharan /* Translate SOF cmd to TPLG type */ 56010f461d7SRanjani Sridharan switch (cdata->cmd) { 56110f461d7SRanjani Sridharan case SOF_CTRL_CMD_VOLUME: 56210f461d7SRanjani Sridharan case SOF_CTRL_CMD_SWITCH: 56310f461d7SRanjani Sridharan type = SND_SOC_TPLG_TYPE_MIXER; 56410f461d7SRanjani Sridharan break; 56510f461d7SRanjani Sridharan case SOF_CTRL_CMD_BINARY: 56610f461d7SRanjani Sridharan type = SND_SOC_TPLG_TYPE_BYTES; 56710f461d7SRanjani Sridharan break; 56810f461d7SRanjani Sridharan case SOF_CTRL_CMD_ENUM: 56910f461d7SRanjani Sridharan type = SND_SOC_TPLG_TYPE_ENUM; 57010f461d7SRanjani Sridharan break; 57110f461d7SRanjani Sridharan default: 57210f461d7SRanjani Sridharan dev_err(sdev->dev, "Unknown cmd %u in %s\n", cdata->cmd, __func__); 57310f461d7SRanjani Sridharan return; 57410f461d7SRanjani Sridharan } 57510f461d7SRanjani Sridharan 57610f461d7SRanjani Sridharan widget = swidget->widget; 57710f461d7SRanjani Sridharan for (i = 0; i < widget->num_kcontrols; i++) { 57810f461d7SRanjani Sridharan /* skip non matching types or non matching indexes within type */ 57910f461d7SRanjani Sridharan if (widget->dobj.widget.kcontrol_type[i] == type && 58010f461d7SRanjani Sridharan widget->kcontrol_news[i].index == cdata->index) { 58110f461d7SRanjani Sridharan kc = widget->kcontrols[i]; 58210f461d7SRanjani Sridharan break; 58310f461d7SRanjani Sridharan } 58410f461d7SRanjani Sridharan } 58510f461d7SRanjani Sridharan 58610f461d7SRanjani Sridharan if (!kc) 58710f461d7SRanjani Sridharan return; 58810f461d7SRanjani Sridharan 58910f461d7SRanjani Sridharan switch (cdata->cmd) { 59010f461d7SRanjani Sridharan case SOF_CTRL_CMD_VOLUME: 59110f461d7SRanjani Sridharan case SOF_CTRL_CMD_SWITCH: 59210f461d7SRanjani Sridharan sm = (struct soc_mixer_control *)kc->private_value; 59310f461d7SRanjani Sridharan scontrol = sm->dobj.private; 59410f461d7SRanjani Sridharan break; 59510f461d7SRanjani Sridharan case SOF_CTRL_CMD_BINARY: 59610f461d7SRanjani Sridharan be = (struct soc_bytes_ext *)kc->private_value; 59710f461d7SRanjani Sridharan scontrol = be->dobj.private; 59810f461d7SRanjani Sridharan break; 59910f461d7SRanjani Sridharan case SOF_CTRL_CMD_ENUM: 60010f461d7SRanjani Sridharan se = (struct soc_enum *)kc->private_value; 60110f461d7SRanjani Sridharan scontrol = se->dobj.private; 60210f461d7SRanjani Sridharan break; 60310f461d7SRanjani Sridharan default: 60410f461d7SRanjani Sridharan return; 60510f461d7SRanjani Sridharan } 60610f461d7SRanjani Sridharan 60710f461d7SRanjani Sridharan expected_size = sizeof(struct sof_ipc_ctrl_data); 60810f461d7SRanjani Sridharan switch (cdata->type) { 60910f461d7SRanjani Sridharan case SOF_CTRL_TYPE_VALUE_CHAN_GET: 61010f461d7SRanjani Sridharan case SOF_CTRL_TYPE_VALUE_CHAN_SET: 61110f461d7SRanjani Sridharan expected_size += cdata->num_elems * 61210f461d7SRanjani Sridharan sizeof(struct sof_ipc_ctrl_value_chan); 61310f461d7SRanjani Sridharan break; 61410f461d7SRanjani Sridharan case SOF_CTRL_TYPE_DATA_GET: 61510f461d7SRanjani Sridharan case SOF_CTRL_TYPE_DATA_SET: 61610f461d7SRanjani Sridharan expected_size += cdata->num_elems + sizeof(struct sof_abi_hdr); 61710f461d7SRanjani Sridharan break; 61810f461d7SRanjani Sridharan default: 61910f461d7SRanjani Sridharan return; 62010f461d7SRanjani Sridharan } 62110f461d7SRanjani Sridharan 62210f461d7SRanjani Sridharan if (cdata->rhdr.hdr.size != expected_size) { 62310f461d7SRanjani Sridharan dev_err(sdev->dev, "Component notification size mismatch\n"); 62410f461d7SRanjani Sridharan return; 62510f461d7SRanjani Sridharan } 62610f461d7SRanjani Sridharan 62710f461d7SRanjani Sridharan if (cdata->num_elems) 62810f461d7SRanjani Sridharan /* 62910f461d7SRanjani Sridharan * The message includes the updated value/data, update the 63010f461d7SRanjani Sridharan * control's local cache using the received notification 63110f461d7SRanjani Sridharan */ 63210f461d7SRanjani Sridharan snd_sof_update_control(scontrol, cdata); 63310f461d7SRanjani Sridharan else 63410f461d7SRanjani Sridharan /* Mark the scontrol that the value/data is changed in SOF */ 63510f461d7SRanjani Sridharan scontrol->comp_data_dirty = true; 63610f461d7SRanjani Sridharan 63710f461d7SRanjani Sridharan snd_ctl_notify_one(swidget->scomp->card->snd_card, SNDRV_CTL_EVENT_MASK_VALUE, kc, 0); 63810f461d7SRanjani Sridharan } 63910f461d7SRanjani Sridharan 640e394ffb8SPeter Ujfalusi static int sof_ipc3_widget_kcontrol_setup(struct snd_sof_dev *sdev, 641e394ffb8SPeter Ujfalusi struct snd_sof_widget *swidget) 642e394ffb8SPeter Ujfalusi { 643e394ffb8SPeter Ujfalusi struct snd_sof_control *scontrol; 644e394ffb8SPeter Ujfalusi int ret; 645e394ffb8SPeter Ujfalusi 646e394ffb8SPeter Ujfalusi /* set up all controls for the widget */ 647e394ffb8SPeter Ujfalusi list_for_each_entry(scontrol, &sdev->kcontrol_list, list) 648e394ffb8SPeter Ujfalusi if (scontrol->comp_id == swidget->comp_id) { 649e394ffb8SPeter Ujfalusi /* set kcontrol data in DSP */ 650e760f102SPeter Ujfalusi ret = sof_ipc3_set_get_kcontrol_data(scontrol, true); 651e394ffb8SPeter Ujfalusi if (ret < 0) { 652e394ffb8SPeter Ujfalusi dev_err(sdev->dev, 653e394ffb8SPeter Ujfalusi "kcontrol %d set up failed for widget %s\n", 654e394ffb8SPeter Ujfalusi scontrol->comp_id, swidget->widget->name); 655e394ffb8SPeter Ujfalusi return ret; 656e394ffb8SPeter Ujfalusi } 657e394ffb8SPeter Ujfalusi 658e394ffb8SPeter Ujfalusi /* 659e394ffb8SPeter Ujfalusi * Read back the data from the DSP for static widgets. 660e394ffb8SPeter Ujfalusi * This is particularly useful for binary kcontrols 661e394ffb8SPeter Ujfalusi * associated with static pipeline widgets to initialize 662e394ffb8SPeter Ujfalusi * the data size to match that in the DSP. 663e394ffb8SPeter Ujfalusi */ 664e394ffb8SPeter Ujfalusi if (swidget->dynamic_pipeline_widget) 665e394ffb8SPeter Ujfalusi continue; 666e394ffb8SPeter Ujfalusi 667e760f102SPeter Ujfalusi ret = sof_ipc3_set_get_kcontrol_data(scontrol, false); 668e394ffb8SPeter Ujfalusi if (ret < 0) 669e394ffb8SPeter Ujfalusi dev_warn(sdev->dev, 670e394ffb8SPeter Ujfalusi "kcontrol %d read failed for widget %s\n", 671e394ffb8SPeter Ujfalusi scontrol->comp_id, swidget->widget->name); 672e394ffb8SPeter Ujfalusi } 673e394ffb8SPeter Ujfalusi 674e394ffb8SPeter Ujfalusi return 0; 675e394ffb8SPeter Ujfalusi } 676e394ffb8SPeter Ujfalusi 67748d2a1ceSRanjani Sridharan static int 67848d2a1ceSRanjani Sridharan sof_ipc3_set_up_volume_table(struct snd_sof_control *scontrol, int tlv[SOF_TLV_ITEMS], int size) 67948d2a1ceSRanjani Sridharan { 68048d2a1ceSRanjani Sridharan int i; 68148d2a1ceSRanjani Sridharan 68248d2a1ceSRanjani Sridharan /* init the volume table */ 68348d2a1ceSRanjani Sridharan scontrol->volume_table = kcalloc(size, sizeof(u32), GFP_KERNEL); 68448d2a1ceSRanjani Sridharan if (!scontrol->volume_table) 68548d2a1ceSRanjani Sridharan return -ENOMEM; 68648d2a1ceSRanjani Sridharan 68748d2a1ceSRanjani Sridharan /* populate the volume table */ 68848d2a1ceSRanjani Sridharan for (i = 0; i < size ; i++) 68948d2a1ceSRanjani Sridharan scontrol->volume_table[i] = vol_compute_gain(i, tlv); 69048d2a1ceSRanjani Sridharan 69148d2a1ceSRanjani Sridharan return 0; 69248d2a1ceSRanjani Sridharan } 69348d2a1ceSRanjani Sridharan 69410f461d7SRanjani Sridharan const struct sof_ipc_tplg_control_ops tplg_ipc3_control_ops = { 695838d04f3SRanjani Sridharan .volume_put = sof_ipc3_volume_put, 696838d04f3SRanjani Sridharan .volume_get = sof_ipc3_volume_get, 697a6668746SRanjani Sridharan .switch_put = sof_ipc3_switch_put, 698a6668746SRanjani Sridharan .switch_get = sof_ipc3_switch_get, 699049307aaSRanjani Sridharan .enum_put = sof_ipc3_enum_put, 700049307aaSRanjani Sridharan .enum_get = sof_ipc3_enum_get, 701544ac885SRanjani Sridharan .bytes_put = sof_ipc3_bytes_put, 702544ac885SRanjani Sridharan .bytes_get = sof_ipc3_bytes_get, 70367ec2a09SRanjani Sridharan .bytes_ext_put = sof_ipc3_bytes_ext_put, 70467ec2a09SRanjani Sridharan .bytes_ext_get = sof_ipc3_bytes_ext_get, 70567ec2a09SRanjani Sridharan .bytes_ext_volatile_get = sof_ipc3_bytes_ext_volatile_get, 70610f461d7SRanjani Sridharan .update = sof_ipc3_control_update, 707e394ffb8SPeter Ujfalusi .widget_kcontrol_setup = sof_ipc3_widget_kcontrol_setup, 70848d2a1ceSRanjani Sridharan .set_up_volume_table = sof_ipc3_set_up_volume_table, 70910f461d7SRanjani Sridharan }; 710