162306a36Sopenharmony_ci// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) 262306a36Sopenharmony_ci// 362306a36Sopenharmony_ci// This file is provided under a dual BSD/GPLv2 license. When using or 462306a36Sopenharmony_ci// redistributing this file, you may do so under either license. 562306a36Sopenharmony_ci// 662306a36Sopenharmony_ci// Copyright(c) 2021 Intel Corporation. All rights reserved. 762306a36Sopenharmony_ci// 862306a36Sopenharmony_ci// 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include "sof-priv.h" 1162306a36Sopenharmony_ci#include "sof-audio.h" 1262306a36Sopenharmony_ci#include "ipc3-priv.h" 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci/* IPC set()/get() for kcontrols. */ 1562306a36Sopenharmony_cistatic int sof_ipc3_set_get_kcontrol_data(struct snd_sof_control *scontrol, 1662306a36Sopenharmony_ci bool set, bool lock) 1762306a36Sopenharmony_ci{ 1862306a36Sopenharmony_ci struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scontrol->scomp); 1962306a36Sopenharmony_ci struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data; 2062306a36Sopenharmony_ci const struct sof_ipc_ops *iops = sdev->ipc->ops; 2162306a36Sopenharmony_ci enum sof_ipc_ctrl_type ctrl_type; 2262306a36Sopenharmony_ci struct snd_sof_widget *swidget; 2362306a36Sopenharmony_ci bool widget_found = false; 2462306a36Sopenharmony_ci u32 ipc_cmd, msg_bytes; 2562306a36Sopenharmony_ci int ret = 0; 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci list_for_each_entry(swidget, &sdev->widget_list, list) { 2862306a36Sopenharmony_ci if (swidget->comp_id == scontrol->comp_id) { 2962306a36Sopenharmony_ci widget_found = true; 3062306a36Sopenharmony_ci break; 3162306a36Sopenharmony_ci } 3262306a36Sopenharmony_ci } 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci if (!widget_found) { 3562306a36Sopenharmony_ci dev_err(sdev->dev, "%s: can't find widget with id %d\n", __func__, 3662306a36Sopenharmony_ci scontrol->comp_id); 3762306a36Sopenharmony_ci return -EINVAL; 3862306a36Sopenharmony_ci } 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci if (lock) 4162306a36Sopenharmony_ci mutex_lock(&swidget->setup_mutex); 4262306a36Sopenharmony_ci else 4362306a36Sopenharmony_ci lockdep_assert_held(&swidget->setup_mutex); 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci /* 4662306a36Sopenharmony_ci * Volatile controls should always be part of static pipelines and the 4762306a36Sopenharmony_ci * widget use_count would always be > 0 in this case. For the others, 4862306a36Sopenharmony_ci * just return the cached value if the widget is not set up. 4962306a36Sopenharmony_ci */ 5062306a36Sopenharmony_ci if (!swidget->use_count) 5162306a36Sopenharmony_ci goto unlock; 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci /* 5462306a36Sopenharmony_ci * Select the IPC cmd and the ctrl_type based on the ctrl_cmd and the 5562306a36Sopenharmony_ci * direction 5662306a36Sopenharmony_ci * Note: SOF_CTRL_TYPE_VALUE_COMP_* is not used and supported currently 5762306a36Sopenharmony_ci * for ctrl_type 5862306a36Sopenharmony_ci */ 5962306a36Sopenharmony_ci if (cdata->cmd == SOF_CTRL_CMD_BINARY) { 6062306a36Sopenharmony_ci ipc_cmd = set ? SOF_IPC_COMP_SET_DATA : SOF_IPC_COMP_GET_DATA; 6162306a36Sopenharmony_ci ctrl_type = set ? SOF_CTRL_TYPE_DATA_SET : SOF_CTRL_TYPE_DATA_GET; 6262306a36Sopenharmony_ci } else { 6362306a36Sopenharmony_ci ipc_cmd = set ? SOF_IPC_COMP_SET_VALUE : SOF_IPC_COMP_GET_VALUE; 6462306a36Sopenharmony_ci ctrl_type = set ? SOF_CTRL_TYPE_VALUE_CHAN_SET : SOF_CTRL_TYPE_VALUE_CHAN_GET; 6562306a36Sopenharmony_ci } 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci cdata->rhdr.hdr.cmd = SOF_IPC_GLB_COMP_MSG | ipc_cmd; 6862306a36Sopenharmony_ci cdata->type = ctrl_type; 6962306a36Sopenharmony_ci cdata->comp_id = scontrol->comp_id; 7062306a36Sopenharmony_ci cdata->msg_index = 0; 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci /* calculate header and data size */ 7362306a36Sopenharmony_ci switch (cdata->type) { 7462306a36Sopenharmony_ci case SOF_CTRL_TYPE_VALUE_CHAN_GET: 7562306a36Sopenharmony_ci case SOF_CTRL_TYPE_VALUE_CHAN_SET: 7662306a36Sopenharmony_ci cdata->num_elems = scontrol->num_channels; 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci msg_bytes = scontrol->num_channels * 7962306a36Sopenharmony_ci sizeof(struct sof_ipc_ctrl_value_chan); 8062306a36Sopenharmony_ci msg_bytes += sizeof(struct sof_ipc_ctrl_data); 8162306a36Sopenharmony_ci break; 8262306a36Sopenharmony_ci case SOF_CTRL_TYPE_DATA_GET: 8362306a36Sopenharmony_ci case SOF_CTRL_TYPE_DATA_SET: 8462306a36Sopenharmony_ci cdata->num_elems = cdata->data->size; 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci msg_bytes = cdata->data->size; 8762306a36Sopenharmony_ci msg_bytes += sizeof(struct sof_ipc_ctrl_data) + 8862306a36Sopenharmony_ci sizeof(struct sof_abi_hdr); 8962306a36Sopenharmony_ci break; 9062306a36Sopenharmony_ci default: 9162306a36Sopenharmony_ci ret = -EINVAL; 9262306a36Sopenharmony_ci goto unlock; 9362306a36Sopenharmony_ci } 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci cdata->rhdr.hdr.size = msg_bytes; 9662306a36Sopenharmony_ci cdata->elems_remaining = 0; 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci ret = iops->set_get_data(sdev, cdata, cdata->rhdr.hdr.size, set); 9962306a36Sopenharmony_ci if (!set) 10062306a36Sopenharmony_ci goto unlock; 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci /* It is a set-data operation, and we have a backup that we can restore */ 10362306a36Sopenharmony_ci if (ret < 0) { 10462306a36Sopenharmony_ci if (!scontrol->old_ipc_control_data) 10562306a36Sopenharmony_ci goto unlock; 10662306a36Sopenharmony_ci /* 10762306a36Sopenharmony_ci * Current ipc_control_data is not valid, we use the last known good 10862306a36Sopenharmony_ci * configuration 10962306a36Sopenharmony_ci */ 11062306a36Sopenharmony_ci memcpy(scontrol->ipc_control_data, scontrol->old_ipc_control_data, 11162306a36Sopenharmony_ci scontrol->max_size); 11262306a36Sopenharmony_ci kfree(scontrol->old_ipc_control_data); 11362306a36Sopenharmony_ci scontrol->old_ipc_control_data = NULL; 11462306a36Sopenharmony_ci /* Send the last known good configuration to firmware */ 11562306a36Sopenharmony_ci ret = iops->set_get_data(sdev, cdata, cdata->rhdr.hdr.size, set); 11662306a36Sopenharmony_ci if (ret < 0) 11762306a36Sopenharmony_ci goto unlock; 11862306a36Sopenharmony_ci } 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ciunlock: 12162306a36Sopenharmony_ci if (lock) 12262306a36Sopenharmony_ci mutex_unlock(&swidget->setup_mutex); 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci return ret; 12562306a36Sopenharmony_ci} 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_cistatic void sof_ipc3_refresh_control(struct snd_sof_control *scontrol) 12862306a36Sopenharmony_ci{ 12962306a36Sopenharmony_ci struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data; 13062306a36Sopenharmony_ci struct snd_soc_component *scomp = scontrol->scomp; 13162306a36Sopenharmony_ci int ret; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci if (!scontrol->comp_data_dirty) 13462306a36Sopenharmony_ci return; 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci if (!pm_runtime_active(scomp->dev)) 13762306a36Sopenharmony_ci return; 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci /* set the ABI header values */ 14062306a36Sopenharmony_ci cdata->data->magic = SOF_ABI_MAGIC; 14162306a36Sopenharmony_ci cdata->data->abi = SOF_ABI_VERSION; 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci /* refresh the component data from DSP */ 14462306a36Sopenharmony_ci scontrol->comp_data_dirty = false; 14562306a36Sopenharmony_ci ret = sof_ipc3_set_get_kcontrol_data(scontrol, false, true); 14662306a36Sopenharmony_ci if (ret < 0) { 14762306a36Sopenharmony_ci dev_err(scomp->dev, "Failed to get control data: %d\n", ret); 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci /* Set the flag to re-try next time to get the data */ 15062306a36Sopenharmony_ci scontrol->comp_data_dirty = true; 15162306a36Sopenharmony_ci } 15262306a36Sopenharmony_ci} 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_cistatic int sof_ipc3_volume_get(struct snd_sof_control *scontrol, 15562306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 15662306a36Sopenharmony_ci{ 15762306a36Sopenharmony_ci struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data; 15862306a36Sopenharmony_ci unsigned int channels = scontrol->num_channels; 15962306a36Sopenharmony_ci unsigned int i; 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci sof_ipc3_refresh_control(scontrol); 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci /* read back each channel */ 16462306a36Sopenharmony_ci for (i = 0; i < channels; i++) 16562306a36Sopenharmony_ci ucontrol->value.integer.value[i] = ipc_to_mixer(cdata->chanv[i].value, 16662306a36Sopenharmony_ci scontrol->volume_table, 16762306a36Sopenharmony_ci scontrol->max + 1); 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci return 0; 17062306a36Sopenharmony_ci} 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_cistatic bool sof_ipc3_volume_put(struct snd_sof_control *scontrol, 17362306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 17462306a36Sopenharmony_ci{ 17562306a36Sopenharmony_ci struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data; 17662306a36Sopenharmony_ci struct snd_soc_component *scomp = scontrol->scomp; 17762306a36Sopenharmony_ci unsigned int channels = scontrol->num_channels; 17862306a36Sopenharmony_ci unsigned int i; 17962306a36Sopenharmony_ci bool change = false; 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci /* update each channel */ 18262306a36Sopenharmony_ci for (i = 0; i < channels; i++) { 18362306a36Sopenharmony_ci u32 value = mixer_to_ipc(ucontrol->value.integer.value[i], 18462306a36Sopenharmony_ci scontrol->volume_table, scontrol->max + 1); 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci change = change || (value != cdata->chanv[i].value); 18762306a36Sopenharmony_ci cdata->chanv[i].channel = i; 18862306a36Sopenharmony_ci cdata->chanv[i].value = value; 18962306a36Sopenharmony_ci } 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci /* notify DSP of mixer updates */ 19262306a36Sopenharmony_ci if (pm_runtime_active(scomp->dev)) { 19362306a36Sopenharmony_ci int ret = sof_ipc3_set_get_kcontrol_data(scontrol, true, true); 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci if (ret < 0) { 19662306a36Sopenharmony_ci dev_err(scomp->dev, "Failed to set mixer updates for %s\n", 19762306a36Sopenharmony_ci scontrol->name); 19862306a36Sopenharmony_ci return false; 19962306a36Sopenharmony_ci } 20062306a36Sopenharmony_ci } 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci return change; 20362306a36Sopenharmony_ci} 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_cistatic int sof_ipc3_switch_get(struct snd_sof_control *scontrol, 20662306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 20762306a36Sopenharmony_ci{ 20862306a36Sopenharmony_ci struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data; 20962306a36Sopenharmony_ci unsigned int channels = scontrol->num_channels; 21062306a36Sopenharmony_ci unsigned int i; 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci sof_ipc3_refresh_control(scontrol); 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci /* read back each channel */ 21562306a36Sopenharmony_ci for (i = 0; i < channels; i++) 21662306a36Sopenharmony_ci ucontrol->value.integer.value[i] = cdata->chanv[i].value; 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci return 0; 21962306a36Sopenharmony_ci} 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_cistatic bool sof_ipc3_switch_put(struct snd_sof_control *scontrol, 22262306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 22362306a36Sopenharmony_ci{ 22462306a36Sopenharmony_ci struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data; 22562306a36Sopenharmony_ci struct snd_soc_component *scomp = scontrol->scomp; 22662306a36Sopenharmony_ci unsigned int channels = scontrol->num_channels; 22762306a36Sopenharmony_ci unsigned int i; 22862306a36Sopenharmony_ci bool change = false; 22962306a36Sopenharmony_ci u32 value; 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci /* update each channel */ 23262306a36Sopenharmony_ci for (i = 0; i < channels; i++) { 23362306a36Sopenharmony_ci value = ucontrol->value.integer.value[i]; 23462306a36Sopenharmony_ci change = change || (value != cdata->chanv[i].value); 23562306a36Sopenharmony_ci cdata->chanv[i].channel = i; 23662306a36Sopenharmony_ci cdata->chanv[i].value = value; 23762306a36Sopenharmony_ci } 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci /* notify DSP of mixer updates */ 24062306a36Sopenharmony_ci if (pm_runtime_active(scomp->dev)) { 24162306a36Sopenharmony_ci int ret = sof_ipc3_set_get_kcontrol_data(scontrol, true, true); 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci if (ret < 0) { 24462306a36Sopenharmony_ci dev_err(scomp->dev, "Failed to set mixer updates for %s\n", 24562306a36Sopenharmony_ci scontrol->name); 24662306a36Sopenharmony_ci return false; 24762306a36Sopenharmony_ci } 24862306a36Sopenharmony_ci } 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci return change; 25162306a36Sopenharmony_ci} 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_cistatic int sof_ipc3_enum_get(struct snd_sof_control *scontrol, 25462306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 25562306a36Sopenharmony_ci{ 25662306a36Sopenharmony_ci struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data; 25762306a36Sopenharmony_ci unsigned int channels = scontrol->num_channels; 25862306a36Sopenharmony_ci unsigned int i; 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci sof_ipc3_refresh_control(scontrol); 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci /* read back each channel */ 26362306a36Sopenharmony_ci for (i = 0; i < channels; i++) 26462306a36Sopenharmony_ci ucontrol->value.enumerated.item[i] = cdata->chanv[i].value; 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci return 0; 26762306a36Sopenharmony_ci} 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_cistatic bool sof_ipc3_enum_put(struct snd_sof_control *scontrol, 27062306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 27162306a36Sopenharmony_ci{ 27262306a36Sopenharmony_ci struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data; 27362306a36Sopenharmony_ci struct snd_soc_component *scomp = scontrol->scomp; 27462306a36Sopenharmony_ci unsigned int channels = scontrol->num_channels; 27562306a36Sopenharmony_ci unsigned int i; 27662306a36Sopenharmony_ci bool change = false; 27762306a36Sopenharmony_ci u32 value; 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci /* update each channel */ 28062306a36Sopenharmony_ci for (i = 0; i < channels; i++) { 28162306a36Sopenharmony_ci value = ucontrol->value.enumerated.item[i]; 28262306a36Sopenharmony_ci change = change || (value != cdata->chanv[i].value); 28362306a36Sopenharmony_ci cdata->chanv[i].channel = i; 28462306a36Sopenharmony_ci cdata->chanv[i].value = value; 28562306a36Sopenharmony_ci } 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci /* notify DSP of enum updates */ 28862306a36Sopenharmony_ci if (pm_runtime_active(scomp->dev)) { 28962306a36Sopenharmony_ci int ret = sof_ipc3_set_get_kcontrol_data(scontrol, true, true); 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci if (ret < 0) { 29262306a36Sopenharmony_ci dev_err(scomp->dev, "Failed to set enum updates for %s\n", 29362306a36Sopenharmony_ci scontrol->name); 29462306a36Sopenharmony_ci return false; 29562306a36Sopenharmony_ci } 29662306a36Sopenharmony_ci } 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci return change; 29962306a36Sopenharmony_ci} 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_cistatic int sof_ipc3_bytes_get(struct snd_sof_control *scontrol, 30262306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 30362306a36Sopenharmony_ci{ 30462306a36Sopenharmony_ci struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data; 30562306a36Sopenharmony_ci struct snd_soc_component *scomp = scontrol->scomp; 30662306a36Sopenharmony_ci struct sof_abi_hdr *data = cdata->data; 30762306a36Sopenharmony_ci size_t size; 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci sof_ipc3_refresh_control(scontrol); 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci if (scontrol->max_size > sizeof(ucontrol->value.bytes.data)) { 31262306a36Sopenharmony_ci dev_err_ratelimited(scomp->dev, "data max %zu exceeds ucontrol data array size\n", 31362306a36Sopenharmony_ci scontrol->max_size); 31462306a36Sopenharmony_ci return -EINVAL; 31562306a36Sopenharmony_ci } 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci /* be->max has been verified to be >= sizeof(struct sof_abi_hdr) */ 31862306a36Sopenharmony_ci if (data->size > scontrol->max_size - sizeof(*data)) { 31962306a36Sopenharmony_ci dev_err_ratelimited(scomp->dev, 32062306a36Sopenharmony_ci "%u bytes of control data is invalid, max is %zu\n", 32162306a36Sopenharmony_ci data->size, scontrol->max_size - sizeof(*data)); 32262306a36Sopenharmony_ci return -EINVAL; 32362306a36Sopenharmony_ci } 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci size = data->size + sizeof(*data); 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci /* copy back to kcontrol */ 32862306a36Sopenharmony_ci memcpy(ucontrol->value.bytes.data, data, size); 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci return 0; 33162306a36Sopenharmony_ci} 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_cistatic int sof_ipc3_bytes_put(struct snd_sof_control *scontrol, 33462306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 33562306a36Sopenharmony_ci{ 33662306a36Sopenharmony_ci struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data; 33762306a36Sopenharmony_ci struct snd_soc_component *scomp = scontrol->scomp; 33862306a36Sopenharmony_ci struct sof_abi_hdr *data = cdata->data; 33962306a36Sopenharmony_ci size_t size; 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci if (scontrol->max_size > sizeof(ucontrol->value.bytes.data)) { 34262306a36Sopenharmony_ci dev_err_ratelimited(scomp->dev, "data max %zu exceeds ucontrol data array size\n", 34362306a36Sopenharmony_ci scontrol->max_size); 34462306a36Sopenharmony_ci return -EINVAL; 34562306a36Sopenharmony_ci } 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci /* scontrol->max_size has been verified to be >= sizeof(struct sof_abi_hdr) */ 34862306a36Sopenharmony_ci if (data->size > scontrol->max_size - sizeof(*data)) { 34962306a36Sopenharmony_ci dev_err_ratelimited(scomp->dev, "data size too big %u bytes max is %zu\n", 35062306a36Sopenharmony_ci data->size, scontrol->max_size - sizeof(*data)); 35162306a36Sopenharmony_ci return -EINVAL; 35262306a36Sopenharmony_ci } 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci size = data->size + sizeof(*data); 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci /* copy from kcontrol */ 35762306a36Sopenharmony_ci memcpy(data, ucontrol->value.bytes.data, size); 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci /* notify DSP of byte control updates */ 36062306a36Sopenharmony_ci if (pm_runtime_active(scomp->dev)) 36162306a36Sopenharmony_ci return sof_ipc3_set_get_kcontrol_data(scontrol, true, true); 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci return 0; 36462306a36Sopenharmony_ci} 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_cistatic int sof_ipc3_bytes_ext_put(struct snd_sof_control *scontrol, 36762306a36Sopenharmony_ci const unsigned int __user *binary_data, 36862306a36Sopenharmony_ci unsigned int size) 36962306a36Sopenharmony_ci{ 37062306a36Sopenharmony_ci const struct snd_ctl_tlv __user *tlvd = (const struct snd_ctl_tlv __user *)binary_data; 37162306a36Sopenharmony_ci struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data; 37262306a36Sopenharmony_ci struct snd_soc_component *scomp = scontrol->scomp; 37362306a36Sopenharmony_ci struct snd_ctl_tlv header; 37462306a36Sopenharmony_ci int ret = -EINVAL; 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci /* 37762306a36Sopenharmony_ci * The beginning of bytes data contains a header from where 37862306a36Sopenharmony_ci * the length (as bytes) is needed to know the correct copy 37962306a36Sopenharmony_ci * length of data from tlvd->tlv. 38062306a36Sopenharmony_ci */ 38162306a36Sopenharmony_ci if (copy_from_user(&header, tlvd, sizeof(struct snd_ctl_tlv))) 38262306a36Sopenharmony_ci return -EFAULT; 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci /* make sure TLV info is consistent */ 38562306a36Sopenharmony_ci if (header.length + sizeof(struct snd_ctl_tlv) > size) { 38662306a36Sopenharmony_ci dev_err_ratelimited(scomp->dev, "Inconsistent TLV, data %d + header %zu > %d\n", 38762306a36Sopenharmony_ci header.length, sizeof(struct snd_ctl_tlv), size); 38862306a36Sopenharmony_ci return -EINVAL; 38962306a36Sopenharmony_ci } 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci /* be->max is coming from topology */ 39262306a36Sopenharmony_ci if (header.length > scontrol->max_size) { 39362306a36Sopenharmony_ci dev_err_ratelimited(scomp->dev, "Bytes data size %d exceeds max %zu\n", 39462306a36Sopenharmony_ci header.length, scontrol->max_size); 39562306a36Sopenharmony_ci return -EINVAL; 39662306a36Sopenharmony_ci } 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci /* Check that header id matches the command */ 39962306a36Sopenharmony_ci if (header.numid != cdata->cmd) { 40062306a36Sopenharmony_ci dev_err_ratelimited(scomp->dev, "Incorrect command for bytes put %d\n", 40162306a36Sopenharmony_ci header.numid); 40262306a36Sopenharmony_ci return -EINVAL; 40362306a36Sopenharmony_ci } 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci if (!scontrol->old_ipc_control_data) { 40662306a36Sopenharmony_ci /* Create a backup of the current, valid bytes control */ 40762306a36Sopenharmony_ci scontrol->old_ipc_control_data = kmemdup(scontrol->ipc_control_data, 40862306a36Sopenharmony_ci scontrol->max_size, GFP_KERNEL); 40962306a36Sopenharmony_ci if (!scontrol->old_ipc_control_data) 41062306a36Sopenharmony_ci return -ENOMEM; 41162306a36Sopenharmony_ci } 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci if (copy_from_user(cdata->data, tlvd->tlv, header.length)) { 41462306a36Sopenharmony_ci ret = -EFAULT; 41562306a36Sopenharmony_ci goto err_restore; 41662306a36Sopenharmony_ci } 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci if (cdata->data->magic != SOF_ABI_MAGIC) { 41962306a36Sopenharmony_ci dev_err_ratelimited(scomp->dev, "Wrong ABI magic 0x%08x\n", cdata->data->magic); 42062306a36Sopenharmony_ci goto err_restore; 42162306a36Sopenharmony_ci } 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci if (SOF_ABI_VERSION_INCOMPATIBLE(SOF_ABI_VERSION, cdata->data->abi)) { 42462306a36Sopenharmony_ci dev_err_ratelimited(scomp->dev, "Incompatible ABI version 0x%08x\n", 42562306a36Sopenharmony_ci cdata->data->abi); 42662306a36Sopenharmony_ci goto err_restore; 42762306a36Sopenharmony_ci } 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci /* be->max has been verified to be >= sizeof(struct sof_abi_hdr) */ 43062306a36Sopenharmony_ci if (cdata->data->size > scontrol->max_size - sizeof(struct sof_abi_hdr)) { 43162306a36Sopenharmony_ci dev_err_ratelimited(scomp->dev, "Mismatch in ABI data size (truncated?)\n"); 43262306a36Sopenharmony_ci goto err_restore; 43362306a36Sopenharmony_ci } 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci /* notify DSP of byte control updates */ 43662306a36Sopenharmony_ci if (pm_runtime_active(scomp->dev)) { 43762306a36Sopenharmony_ci /* Actually send the data to the DSP; this is an opportunity to validate the data */ 43862306a36Sopenharmony_ci return sof_ipc3_set_get_kcontrol_data(scontrol, true, true); 43962306a36Sopenharmony_ci } 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci return 0; 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_cierr_restore: 44462306a36Sopenharmony_ci /* If we have an issue, we restore the old, valid bytes control data */ 44562306a36Sopenharmony_ci if (scontrol->old_ipc_control_data) { 44662306a36Sopenharmony_ci memcpy(cdata->data, scontrol->old_ipc_control_data, scontrol->max_size); 44762306a36Sopenharmony_ci kfree(scontrol->old_ipc_control_data); 44862306a36Sopenharmony_ci scontrol->old_ipc_control_data = NULL; 44962306a36Sopenharmony_ci } 45062306a36Sopenharmony_ci return ret; 45162306a36Sopenharmony_ci} 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_cistatic int _sof_ipc3_bytes_ext_get(struct snd_sof_control *scontrol, 45462306a36Sopenharmony_ci const unsigned int __user *binary_data, 45562306a36Sopenharmony_ci unsigned int size, bool from_dsp) 45662306a36Sopenharmony_ci{ 45762306a36Sopenharmony_ci struct snd_ctl_tlv __user *tlvd = (struct snd_ctl_tlv __user *)binary_data; 45862306a36Sopenharmony_ci struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data; 45962306a36Sopenharmony_ci struct snd_soc_component *scomp = scontrol->scomp; 46062306a36Sopenharmony_ci struct snd_ctl_tlv header; 46162306a36Sopenharmony_ci size_t data_size; 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci /* 46462306a36Sopenharmony_ci * Decrement the limit by ext bytes header size to 46562306a36Sopenharmony_ci * ensure the user space buffer is not exceeded. 46662306a36Sopenharmony_ci */ 46762306a36Sopenharmony_ci if (size < sizeof(struct snd_ctl_tlv)) 46862306a36Sopenharmony_ci return -ENOSPC; 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci size -= sizeof(struct snd_ctl_tlv); 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci /* set the ABI header values */ 47362306a36Sopenharmony_ci cdata->data->magic = SOF_ABI_MAGIC; 47462306a36Sopenharmony_ci cdata->data->abi = SOF_ABI_VERSION; 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ci /* get all the component data from DSP */ 47762306a36Sopenharmony_ci if (from_dsp) { 47862306a36Sopenharmony_ci int ret = sof_ipc3_set_get_kcontrol_data(scontrol, false, true); 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci if (ret < 0) 48162306a36Sopenharmony_ci return ret; 48262306a36Sopenharmony_ci } 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ci /* check data size doesn't exceed max coming from topology */ 48562306a36Sopenharmony_ci if (cdata->data->size > scontrol->max_size - sizeof(struct sof_abi_hdr)) { 48662306a36Sopenharmony_ci dev_err_ratelimited(scomp->dev, "User data size %d exceeds max size %zu\n", 48762306a36Sopenharmony_ci cdata->data->size, 48862306a36Sopenharmony_ci scontrol->max_size - sizeof(struct sof_abi_hdr)); 48962306a36Sopenharmony_ci return -EINVAL; 49062306a36Sopenharmony_ci } 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci data_size = cdata->data->size + sizeof(struct sof_abi_hdr); 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci /* make sure we don't exceed size provided by user space for data */ 49562306a36Sopenharmony_ci if (data_size > size) 49662306a36Sopenharmony_ci return -ENOSPC; 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ci header.numid = cdata->cmd; 49962306a36Sopenharmony_ci header.length = data_size; 50062306a36Sopenharmony_ci if (copy_to_user(tlvd, &header, sizeof(struct snd_ctl_tlv))) 50162306a36Sopenharmony_ci return -EFAULT; 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci if (copy_to_user(tlvd->tlv, cdata->data, data_size)) 50462306a36Sopenharmony_ci return -EFAULT; 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci return 0; 50762306a36Sopenharmony_ci} 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_cistatic int sof_ipc3_bytes_ext_get(struct snd_sof_control *scontrol, 51062306a36Sopenharmony_ci const unsigned int __user *binary_data, unsigned int size) 51162306a36Sopenharmony_ci{ 51262306a36Sopenharmony_ci return _sof_ipc3_bytes_ext_get(scontrol, binary_data, size, false); 51362306a36Sopenharmony_ci} 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_cistatic int sof_ipc3_bytes_ext_volatile_get(struct snd_sof_control *scontrol, 51662306a36Sopenharmony_ci const unsigned int __user *binary_data, 51762306a36Sopenharmony_ci unsigned int size) 51862306a36Sopenharmony_ci{ 51962306a36Sopenharmony_ci return _sof_ipc3_bytes_ext_get(scontrol, binary_data, size, true); 52062306a36Sopenharmony_ci} 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_cistatic void snd_sof_update_control(struct snd_sof_control *scontrol, 52362306a36Sopenharmony_ci struct sof_ipc_ctrl_data *cdata) 52462306a36Sopenharmony_ci{ 52562306a36Sopenharmony_ci struct snd_soc_component *scomp = scontrol->scomp; 52662306a36Sopenharmony_ci struct sof_ipc_ctrl_data *local_cdata; 52762306a36Sopenharmony_ci int i; 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_ci local_cdata = scontrol->ipc_control_data; 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_ci if (cdata->cmd == SOF_CTRL_CMD_BINARY) { 53262306a36Sopenharmony_ci if (cdata->num_elems != local_cdata->data->size) { 53362306a36Sopenharmony_ci dev_err(scomp->dev, "cdata binary size mismatch %u - %u\n", 53462306a36Sopenharmony_ci cdata->num_elems, local_cdata->data->size); 53562306a36Sopenharmony_ci return; 53662306a36Sopenharmony_ci } 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_ci /* copy the new binary data */ 53962306a36Sopenharmony_ci memcpy(local_cdata->data, cdata->data, cdata->num_elems); 54062306a36Sopenharmony_ci } else if (cdata->num_elems != scontrol->num_channels) { 54162306a36Sopenharmony_ci dev_err(scomp->dev, "cdata channel count mismatch %u - %d\n", 54262306a36Sopenharmony_ci cdata->num_elems, scontrol->num_channels); 54362306a36Sopenharmony_ci } else { 54462306a36Sopenharmony_ci /* copy the new values */ 54562306a36Sopenharmony_ci for (i = 0; i < cdata->num_elems; i++) 54662306a36Sopenharmony_ci local_cdata->chanv[i].value = cdata->chanv[i].value; 54762306a36Sopenharmony_ci } 54862306a36Sopenharmony_ci} 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_cistatic void sof_ipc3_control_update(struct snd_sof_dev *sdev, void *ipc_control_message) 55162306a36Sopenharmony_ci{ 55262306a36Sopenharmony_ci struct sof_ipc_ctrl_data *cdata = ipc_control_message; 55362306a36Sopenharmony_ci struct snd_soc_dapm_widget *widget; 55462306a36Sopenharmony_ci struct snd_sof_control *scontrol; 55562306a36Sopenharmony_ci struct snd_sof_widget *swidget; 55662306a36Sopenharmony_ci struct snd_kcontrol *kc = NULL; 55762306a36Sopenharmony_ci struct soc_mixer_control *sm; 55862306a36Sopenharmony_ci struct soc_bytes_ext *be; 55962306a36Sopenharmony_ci size_t expected_size; 56062306a36Sopenharmony_ci struct soc_enum *se; 56162306a36Sopenharmony_ci bool found = false; 56262306a36Sopenharmony_ci int i, type; 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_ci if (cdata->type == SOF_CTRL_TYPE_VALUE_COMP_GET || 56562306a36Sopenharmony_ci cdata->type == SOF_CTRL_TYPE_VALUE_COMP_SET) { 56662306a36Sopenharmony_ci dev_err(sdev->dev, "Component data is not supported in control notification\n"); 56762306a36Sopenharmony_ci return; 56862306a36Sopenharmony_ci } 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ci /* Find the swidget first */ 57162306a36Sopenharmony_ci list_for_each_entry(swidget, &sdev->widget_list, list) { 57262306a36Sopenharmony_ci if (swidget->comp_id == cdata->comp_id) { 57362306a36Sopenharmony_ci found = true; 57462306a36Sopenharmony_ci break; 57562306a36Sopenharmony_ci } 57662306a36Sopenharmony_ci } 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_ci if (!found) 57962306a36Sopenharmony_ci return; 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_ci /* Translate SOF cmd to TPLG type */ 58262306a36Sopenharmony_ci switch (cdata->cmd) { 58362306a36Sopenharmony_ci case SOF_CTRL_CMD_VOLUME: 58462306a36Sopenharmony_ci case SOF_CTRL_CMD_SWITCH: 58562306a36Sopenharmony_ci type = SND_SOC_TPLG_TYPE_MIXER; 58662306a36Sopenharmony_ci break; 58762306a36Sopenharmony_ci case SOF_CTRL_CMD_BINARY: 58862306a36Sopenharmony_ci type = SND_SOC_TPLG_TYPE_BYTES; 58962306a36Sopenharmony_ci break; 59062306a36Sopenharmony_ci case SOF_CTRL_CMD_ENUM: 59162306a36Sopenharmony_ci type = SND_SOC_TPLG_TYPE_ENUM; 59262306a36Sopenharmony_ci break; 59362306a36Sopenharmony_ci default: 59462306a36Sopenharmony_ci dev_err(sdev->dev, "Unknown cmd %u in %s\n", cdata->cmd, __func__); 59562306a36Sopenharmony_ci return; 59662306a36Sopenharmony_ci } 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_ci widget = swidget->widget; 59962306a36Sopenharmony_ci for (i = 0; i < widget->num_kcontrols; i++) { 60062306a36Sopenharmony_ci /* skip non matching types or non matching indexes within type */ 60162306a36Sopenharmony_ci if (widget->dobj.widget.kcontrol_type[i] == type && 60262306a36Sopenharmony_ci widget->kcontrol_news[i].index == cdata->index) { 60362306a36Sopenharmony_ci kc = widget->kcontrols[i]; 60462306a36Sopenharmony_ci break; 60562306a36Sopenharmony_ci } 60662306a36Sopenharmony_ci } 60762306a36Sopenharmony_ci 60862306a36Sopenharmony_ci if (!kc) 60962306a36Sopenharmony_ci return; 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_ci switch (cdata->cmd) { 61262306a36Sopenharmony_ci case SOF_CTRL_CMD_VOLUME: 61362306a36Sopenharmony_ci case SOF_CTRL_CMD_SWITCH: 61462306a36Sopenharmony_ci sm = (struct soc_mixer_control *)kc->private_value; 61562306a36Sopenharmony_ci scontrol = sm->dobj.private; 61662306a36Sopenharmony_ci break; 61762306a36Sopenharmony_ci case SOF_CTRL_CMD_BINARY: 61862306a36Sopenharmony_ci be = (struct soc_bytes_ext *)kc->private_value; 61962306a36Sopenharmony_ci scontrol = be->dobj.private; 62062306a36Sopenharmony_ci break; 62162306a36Sopenharmony_ci case SOF_CTRL_CMD_ENUM: 62262306a36Sopenharmony_ci se = (struct soc_enum *)kc->private_value; 62362306a36Sopenharmony_ci scontrol = se->dobj.private; 62462306a36Sopenharmony_ci break; 62562306a36Sopenharmony_ci default: 62662306a36Sopenharmony_ci return; 62762306a36Sopenharmony_ci } 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_ci expected_size = sizeof(struct sof_ipc_ctrl_data); 63062306a36Sopenharmony_ci switch (cdata->type) { 63162306a36Sopenharmony_ci case SOF_CTRL_TYPE_VALUE_CHAN_GET: 63262306a36Sopenharmony_ci case SOF_CTRL_TYPE_VALUE_CHAN_SET: 63362306a36Sopenharmony_ci expected_size += cdata->num_elems * 63462306a36Sopenharmony_ci sizeof(struct sof_ipc_ctrl_value_chan); 63562306a36Sopenharmony_ci break; 63662306a36Sopenharmony_ci case SOF_CTRL_TYPE_DATA_GET: 63762306a36Sopenharmony_ci case SOF_CTRL_TYPE_DATA_SET: 63862306a36Sopenharmony_ci expected_size += cdata->num_elems + sizeof(struct sof_abi_hdr); 63962306a36Sopenharmony_ci break; 64062306a36Sopenharmony_ci default: 64162306a36Sopenharmony_ci return; 64262306a36Sopenharmony_ci } 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_ci if (cdata->rhdr.hdr.size != expected_size) { 64562306a36Sopenharmony_ci dev_err(sdev->dev, "Component notification size mismatch\n"); 64662306a36Sopenharmony_ci return; 64762306a36Sopenharmony_ci } 64862306a36Sopenharmony_ci 64962306a36Sopenharmony_ci if (cdata->num_elems) 65062306a36Sopenharmony_ci /* 65162306a36Sopenharmony_ci * The message includes the updated value/data, update the 65262306a36Sopenharmony_ci * control's local cache using the received notification 65362306a36Sopenharmony_ci */ 65462306a36Sopenharmony_ci snd_sof_update_control(scontrol, cdata); 65562306a36Sopenharmony_ci else 65662306a36Sopenharmony_ci /* Mark the scontrol that the value/data is changed in SOF */ 65762306a36Sopenharmony_ci scontrol->comp_data_dirty = true; 65862306a36Sopenharmony_ci 65962306a36Sopenharmony_ci snd_ctl_notify_one(swidget->scomp->card->snd_card, SNDRV_CTL_EVENT_MASK_VALUE, kc, 0); 66062306a36Sopenharmony_ci} 66162306a36Sopenharmony_ci 66262306a36Sopenharmony_cistatic int sof_ipc3_widget_kcontrol_setup(struct snd_sof_dev *sdev, 66362306a36Sopenharmony_ci struct snd_sof_widget *swidget) 66462306a36Sopenharmony_ci{ 66562306a36Sopenharmony_ci struct snd_sof_control *scontrol; 66662306a36Sopenharmony_ci int ret; 66762306a36Sopenharmony_ci 66862306a36Sopenharmony_ci /* set up all controls for the widget */ 66962306a36Sopenharmony_ci list_for_each_entry(scontrol, &sdev->kcontrol_list, list) 67062306a36Sopenharmony_ci if (scontrol->comp_id == swidget->comp_id) { 67162306a36Sopenharmony_ci /* set kcontrol data in DSP */ 67262306a36Sopenharmony_ci ret = sof_ipc3_set_get_kcontrol_data(scontrol, true, false); 67362306a36Sopenharmony_ci if (ret < 0) { 67462306a36Sopenharmony_ci dev_err(sdev->dev, 67562306a36Sopenharmony_ci "kcontrol %d set up failed for widget %s\n", 67662306a36Sopenharmony_ci scontrol->comp_id, swidget->widget->name); 67762306a36Sopenharmony_ci return ret; 67862306a36Sopenharmony_ci } 67962306a36Sopenharmony_ci 68062306a36Sopenharmony_ci /* 68162306a36Sopenharmony_ci * Read back the data from the DSP for static widgets. 68262306a36Sopenharmony_ci * This is particularly useful for binary kcontrols 68362306a36Sopenharmony_ci * associated with static pipeline widgets to initialize 68462306a36Sopenharmony_ci * the data size to match that in the DSP. 68562306a36Sopenharmony_ci */ 68662306a36Sopenharmony_ci if (swidget->dynamic_pipeline_widget) 68762306a36Sopenharmony_ci continue; 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_ci ret = sof_ipc3_set_get_kcontrol_data(scontrol, false, false); 69062306a36Sopenharmony_ci if (ret < 0) 69162306a36Sopenharmony_ci dev_warn(sdev->dev, 69262306a36Sopenharmony_ci "kcontrol %d read failed for widget %s\n", 69362306a36Sopenharmony_ci scontrol->comp_id, swidget->widget->name); 69462306a36Sopenharmony_ci } 69562306a36Sopenharmony_ci 69662306a36Sopenharmony_ci return 0; 69762306a36Sopenharmony_ci} 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_cistatic int 70062306a36Sopenharmony_cisof_ipc3_set_up_volume_table(struct snd_sof_control *scontrol, int tlv[SOF_TLV_ITEMS], int size) 70162306a36Sopenharmony_ci{ 70262306a36Sopenharmony_ci int i; 70362306a36Sopenharmony_ci 70462306a36Sopenharmony_ci /* init the volume table */ 70562306a36Sopenharmony_ci scontrol->volume_table = kcalloc(size, sizeof(u32), GFP_KERNEL); 70662306a36Sopenharmony_ci if (!scontrol->volume_table) 70762306a36Sopenharmony_ci return -ENOMEM; 70862306a36Sopenharmony_ci 70962306a36Sopenharmony_ci /* populate the volume table */ 71062306a36Sopenharmony_ci for (i = 0; i < size ; i++) 71162306a36Sopenharmony_ci scontrol->volume_table[i] = vol_compute_gain(i, tlv); 71262306a36Sopenharmony_ci 71362306a36Sopenharmony_ci return 0; 71462306a36Sopenharmony_ci} 71562306a36Sopenharmony_ci 71662306a36Sopenharmony_ciconst struct sof_ipc_tplg_control_ops tplg_ipc3_control_ops = { 71762306a36Sopenharmony_ci .volume_put = sof_ipc3_volume_put, 71862306a36Sopenharmony_ci .volume_get = sof_ipc3_volume_get, 71962306a36Sopenharmony_ci .switch_put = sof_ipc3_switch_put, 72062306a36Sopenharmony_ci .switch_get = sof_ipc3_switch_get, 72162306a36Sopenharmony_ci .enum_put = sof_ipc3_enum_put, 72262306a36Sopenharmony_ci .enum_get = sof_ipc3_enum_get, 72362306a36Sopenharmony_ci .bytes_put = sof_ipc3_bytes_put, 72462306a36Sopenharmony_ci .bytes_get = sof_ipc3_bytes_get, 72562306a36Sopenharmony_ci .bytes_ext_put = sof_ipc3_bytes_ext_put, 72662306a36Sopenharmony_ci .bytes_ext_get = sof_ipc3_bytes_ext_get, 72762306a36Sopenharmony_ci .bytes_ext_volatile_get = sof_ipc3_bytes_ext_volatile_get, 72862306a36Sopenharmony_ci .update = sof_ipc3_control_update, 72962306a36Sopenharmony_ci .widget_kcontrol_setup = sof_ipc3_widget_kcontrol_setup, 73062306a36Sopenharmony_ci .set_up_volume_table = sof_ipc3_set_up_volume_table, 73162306a36Sopenharmony_ci}; 732