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) 2022 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 "ipc4-priv.h" 1362306a36Sopenharmony_ci#include "ipc4-topology.h" 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_cistatic int sof_ipc4_set_get_kcontrol_data(struct snd_sof_control *scontrol, 1662306a36Sopenharmony_ci bool set, bool lock) 1762306a36Sopenharmony_ci{ 1862306a36Sopenharmony_ci struct sof_ipc4_control_data *cdata = scontrol->ipc_control_data; 1962306a36Sopenharmony_ci struct snd_soc_component *scomp = scontrol->scomp; 2062306a36Sopenharmony_ci struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); 2162306a36Sopenharmony_ci const struct sof_ipc_ops *iops = sdev->ipc->ops; 2262306a36Sopenharmony_ci struct sof_ipc4_msg *msg = &cdata->msg; 2362306a36Sopenharmony_ci struct snd_sof_widget *swidget; 2462306a36Sopenharmony_ci bool widget_found = false; 2562306a36Sopenharmony_ci int ret = 0; 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci /* find widget associated with the control */ 2862306a36Sopenharmony_ci list_for_each_entry(swidget, &sdev->widget_list, list) { 2962306a36Sopenharmony_ci if (swidget->comp_id == scontrol->comp_id) { 3062306a36Sopenharmony_ci widget_found = true; 3162306a36Sopenharmony_ci break; 3262306a36Sopenharmony_ci } 3362306a36Sopenharmony_ci } 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci if (!widget_found) { 3662306a36Sopenharmony_ci dev_err(scomp->dev, "Failed to find widget for kcontrol %s\n", scontrol->name); 3762306a36Sopenharmony_ci return -ENOENT; 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 msg->primary &= ~SOF_IPC4_MOD_INSTANCE_MASK; 5462306a36Sopenharmony_ci msg->primary |= SOF_IPC4_MOD_INSTANCE(swidget->instance_id); 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci ret = iops->set_get_data(sdev, msg, msg->data_size, set); 5762306a36Sopenharmony_ci if (!set) 5862306a36Sopenharmony_ci goto unlock; 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci /* It is a set-data operation, and we have a valid backup that we can restore */ 6162306a36Sopenharmony_ci if (ret < 0) { 6262306a36Sopenharmony_ci if (!scontrol->old_ipc_control_data) 6362306a36Sopenharmony_ci goto unlock; 6462306a36Sopenharmony_ci /* 6562306a36Sopenharmony_ci * Current ipc_control_data is not valid, we use the last known good 6662306a36Sopenharmony_ci * configuration 6762306a36Sopenharmony_ci */ 6862306a36Sopenharmony_ci memcpy(scontrol->ipc_control_data, scontrol->old_ipc_control_data, 6962306a36Sopenharmony_ci scontrol->max_size); 7062306a36Sopenharmony_ci kfree(scontrol->old_ipc_control_data); 7162306a36Sopenharmony_ci scontrol->old_ipc_control_data = NULL; 7262306a36Sopenharmony_ci /* Send the last known good configuration to firmware */ 7362306a36Sopenharmony_ci ret = iops->set_get_data(sdev, msg, msg->data_size, set); 7462306a36Sopenharmony_ci if (ret < 0) 7562306a36Sopenharmony_ci goto unlock; 7662306a36Sopenharmony_ci } 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ciunlock: 7962306a36Sopenharmony_ci if (lock) 8062306a36Sopenharmony_ci mutex_unlock(&swidget->setup_mutex); 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci return ret; 8362306a36Sopenharmony_ci} 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_cistatic int 8662306a36Sopenharmony_cisof_ipc4_set_volume_data(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget, 8762306a36Sopenharmony_ci struct snd_sof_control *scontrol, bool lock) 8862306a36Sopenharmony_ci{ 8962306a36Sopenharmony_ci struct sof_ipc4_control_data *cdata = scontrol->ipc_control_data; 9062306a36Sopenharmony_ci struct sof_ipc4_gain *gain = swidget->private; 9162306a36Sopenharmony_ci struct sof_ipc4_msg *msg = &cdata->msg; 9262306a36Sopenharmony_ci struct sof_ipc4_gain_params params; 9362306a36Sopenharmony_ci bool all_channels_equal = true; 9462306a36Sopenharmony_ci u32 value; 9562306a36Sopenharmony_ci int ret, i; 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci /* check if all channel values are equal */ 9862306a36Sopenharmony_ci value = cdata->chanv[0].value; 9962306a36Sopenharmony_ci for (i = 1; i < scontrol->num_channels; i++) { 10062306a36Sopenharmony_ci if (cdata->chanv[i].value != value) { 10162306a36Sopenharmony_ci all_channels_equal = false; 10262306a36Sopenharmony_ci break; 10362306a36Sopenharmony_ci } 10462306a36Sopenharmony_ci } 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci /* 10762306a36Sopenharmony_ci * notify DSP with a single IPC message if all channel values are equal. Otherwise send 10862306a36Sopenharmony_ci * a separate IPC for each channel. 10962306a36Sopenharmony_ci */ 11062306a36Sopenharmony_ci for (i = 0; i < scontrol->num_channels; i++) { 11162306a36Sopenharmony_ci if (all_channels_equal) { 11262306a36Sopenharmony_ci params.channels = SOF_IPC4_GAIN_ALL_CHANNELS_MASK; 11362306a36Sopenharmony_ci params.init_val = cdata->chanv[0].value; 11462306a36Sopenharmony_ci } else { 11562306a36Sopenharmony_ci params.channels = cdata->chanv[i].channel; 11662306a36Sopenharmony_ci params.init_val = cdata->chanv[i].value; 11762306a36Sopenharmony_ci } 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci /* set curve type and duration from topology */ 12062306a36Sopenharmony_ci params.curve_duration_l = gain->data.params.curve_duration_l; 12162306a36Sopenharmony_ci params.curve_duration_h = gain->data.params.curve_duration_h; 12262306a36Sopenharmony_ci params.curve_type = gain->data.params.curve_type; 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci msg->data_ptr = ¶ms; 12562306a36Sopenharmony_ci msg->data_size = sizeof(params); 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci ret = sof_ipc4_set_get_kcontrol_data(scontrol, true, lock); 12862306a36Sopenharmony_ci msg->data_ptr = NULL; 12962306a36Sopenharmony_ci msg->data_size = 0; 13062306a36Sopenharmony_ci if (ret < 0) { 13162306a36Sopenharmony_ci dev_err(sdev->dev, "Failed to set volume update for %s\n", 13262306a36Sopenharmony_ci scontrol->name); 13362306a36Sopenharmony_ci return ret; 13462306a36Sopenharmony_ci } 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci if (all_channels_equal) 13762306a36Sopenharmony_ci break; 13862306a36Sopenharmony_ci } 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci return 0; 14162306a36Sopenharmony_ci} 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_cistatic bool sof_ipc4_volume_put(struct snd_sof_control *scontrol, 14462306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 14562306a36Sopenharmony_ci{ 14662306a36Sopenharmony_ci struct sof_ipc4_control_data *cdata = scontrol->ipc_control_data; 14762306a36Sopenharmony_ci struct snd_soc_component *scomp = scontrol->scomp; 14862306a36Sopenharmony_ci struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); 14962306a36Sopenharmony_ci unsigned int channels = scontrol->num_channels; 15062306a36Sopenharmony_ci struct snd_sof_widget *swidget; 15162306a36Sopenharmony_ci bool widget_found = false; 15262306a36Sopenharmony_ci bool change = false; 15362306a36Sopenharmony_ci unsigned int i; 15462306a36Sopenharmony_ci int ret; 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci /* update each channel */ 15762306a36Sopenharmony_ci for (i = 0; i < channels; i++) { 15862306a36Sopenharmony_ci u32 value = mixer_to_ipc(ucontrol->value.integer.value[i], 15962306a36Sopenharmony_ci scontrol->volume_table, scontrol->max + 1); 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci change = change || (value != cdata->chanv[i].value); 16262306a36Sopenharmony_ci cdata->chanv[i].channel = i; 16362306a36Sopenharmony_ci cdata->chanv[i].value = value; 16462306a36Sopenharmony_ci } 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci if (!pm_runtime_active(scomp->dev)) 16762306a36Sopenharmony_ci return change; 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci /* find widget associated with the control */ 17062306a36Sopenharmony_ci list_for_each_entry(swidget, &sdev->widget_list, list) { 17162306a36Sopenharmony_ci if (swidget->comp_id == scontrol->comp_id) { 17262306a36Sopenharmony_ci widget_found = true; 17362306a36Sopenharmony_ci break; 17462306a36Sopenharmony_ci } 17562306a36Sopenharmony_ci } 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci if (!widget_found) { 17862306a36Sopenharmony_ci dev_err(scomp->dev, "Failed to find widget for kcontrol %s\n", scontrol->name); 17962306a36Sopenharmony_ci return false; 18062306a36Sopenharmony_ci } 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci ret = sof_ipc4_set_volume_data(sdev, swidget, scontrol, true); 18362306a36Sopenharmony_ci if (ret < 0) 18462306a36Sopenharmony_ci return false; 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci return change; 18762306a36Sopenharmony_ci} 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_cistatic int sof_ipc4_volume_get(struct snd_sof_control *scontrol, 19062306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 19162306a36Sopenharmony_ci{ 19262306a36Sopenharmony_ci struct sof_ipc4_control_data *cdata = scontrol->ipc_control_data; 19362306a36Sopenharmony_ci unsigned int channels = scontrol->num_channels; 19462306a36Sopenharmony_ci unsigned int i; 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci for (i = 0; i < channels; i++) 19762306a36Sopenharmony_ci ucontrol->value.integer.value[i] = ipc_to_mixer(cdata->chanv[i].value, 19862306a36Sopenharmony_ci scontrol->volume_table, 19962306a36Sopenharmony_ci scontrol->max + 1); 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci return 0; 20262306a36Sopenharmony_ci} 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_cistatic int sof_ipc4_set_get_bytes_data(struct snd_sof_dev *sdev, 20562306a36Sopenharmony_ci struct snd_sof_control *scontrol, 20662306a36Sopenharmony_ci bool set, bool lock) 20762306a36Sopenharmony_ci{ 20862306a36Sopenharmony_ci struct sof_ipc4_control_data *cdata = scontrol->ipc_control_data; 20962306a36Sopenharmony_ci struct sof_abi_hdr *data = cdata->data; 21062306a36Sopenharmony_ci struct sof_ipc4_msg *msg = &cdata->msg; 21162306a36Sopenharmony_ci int ret = 0; 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci /* Send the new data to the firmware only if it is powered up */ 21462306a36Sopenharmony_ci if (set && !pm_runtime_active(sdev->dev)) 21562306a36Sopenharmony_ci return 0; 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci msg->extension = SOF_IPC4_MOD_EXT_MSG_PARAM_ID(data->type); 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci msg->data_ptr = data->data; 22062306a36Sopenharmony_ci msg->data_size = data->size; 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci ret = sof_ipc4_set_get_kcontrol_data(scontrol, set, lock); 22362306a36Sopenharmony_ci if (ret < 0) 22462306a36Sopenharmony_ci dev_err(sdev->dev, "Failed to %s for %s\n", 22562306a36Sopenharmony_ci set ? "set bytes update" : "get bytes", 22662306a36Sopenharmony_ci scontrol->name); 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci msg->data_ptr = NULL; 22962306a36Sopenharmony_ci msg->data_size = 0; 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci return ret; 23262306a36Sopenharmony_ci} 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_cistatic int sof_ipc4_bytes_put(struct snd_sof_control *scontrol, 23562306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 23662306a36Sopenharmony_ci{ 23762306a36Sopenharmony_ci struct sof_ipc4_control_data *cdata = scontrol->ipc_control_data; 23862306a36Sopenharmony_ci struct snd_soc_component *scomp = scontrol->scomp; 23962306a36Sopenharmony_ci struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); 24062306a36Sopenharmony_ci struct sof_abi_hdr *data = cdata->data; 24162306a36Sopenharmony_ci size_t size; 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci if (scontrol->max_size > sizeof(ucontrol->value.bytes.data)) { 24462306a36Sopenharmony_ci dev_err_ratelimited(scomp->dev, 24562306a36Sopenharmony_ci "data max %zu exceeds ucontrol data array size\n", 24662306a36Sopenharmony_ci scontrol->max_size); 24762306a36Sopenharmony_ci return -EINVAL; 24862306a36Sopenharmony_ci } 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci /* scontrol->max_size has been verified to be >= sizeof(struct sof_abi_hdr) */ 25162306a36Sopenharmony_ci if (data->size > scontrol->max_size - sizeof(*data)) { 25262306a36Sopenharmony_ci dev_err_ratelimited(scomp->dev, 25362306a36Sopenharmony_ci "data size too big %u bytes max is %zu\n", 25462306a36Sopenharmony_ci data->size, scontrol->max_size - sizeof(*data)); 25562306a36Sopenharmony_ci return -EINVAL; 25662306a36Sopenharmony_ci } 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci size = data->size + sizeof(*data); 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci /* copy from kcontrol */ 26162306a36Sopenharmony_ci memcpy(data, ucontrol->value.bytes.data, size); 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci sof_ipc4_set_get_bytes_data(sdev, scontrol, true, true); 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci return 0; 26662306a36Sopenharmony_ci} 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_cistatic int sof_ipc4_bytes_get(struct snd_sof_control *scontrol, 26962306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 27062306a36Sopenharmony_ci{ 27162306a36Sopenharmony_ci struct sof_ipc4_control_data *cdata = scontrol->ipc_control_data; 27262306a36Sopenharmony_ci struct snd_soc_component *scomp = scontrol->scomp; 27362306a36Sopenharmony_ci struct sof_abi_hdr *data = cdata->data; 27462306a36Sopenharmony_ci size_t size; 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci if (scontrol->max_size > sizeof(ucontrol->value.bytes.data)) { 27762306a36Sopenharmony_ci dev_err_ratelimited(scomp->dev, "data max %zu exceeds ucontrol data array size\n", 27862306a36Sopenharmony_ci scontrol->max_size); 27962306a36Sopenharmony_ci return -EINVAL; 28062306a36Sopenharmony_ci } 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci if (data->size > scontrol->max_size - sizeof(*data)) { 28362306a36Sopenharmony_ci dev_err_ratelimited(scomp->dev, 28462306a36Sopenharmony_ci "%u bytes of control data is invalid, max is %zu\n", 28562306a36Sopenharmony_ci data->size, scontrol->max_size - sizeof(*data)); 28662306a36Sopenharmony_ci return -EINVAL; 28762306a36Sopenharmony_ci } 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci size = data->size + sizeof(*data); 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci /* copy back to kcontrol */ 29262306a36Sopenharmony_ci memcpy(ucontrol->value.bytes.data, data, size); 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci return 0; 29562306a36Sopenharmony_ci} 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_cistatic int sof_ipc4_bytes_ext_put(struct snd_sof_control *scontrol, 29862306a36Sopenharmony_ci const unsigned int __user *binary_data, 29962306a36Sopenharmony_ci unsigned int size) 30062306a36Sopenharmony_ci{ 30162306a36Sopenharmony_ci struct snd_ctl_tlv __user *tlvd = (struct snd_ctl_tlv __user *)binary_data; 30262306a36Sopenharmony_ci struct sof_ipc4_control_data *cdata = scontrol->ipc_control_data; 30362306a36Sopenharmony_ci struct snd_soc_component *scomp = scontrol->scomp; 30462306a36Sopenharmony_ci struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); 30562306a36Sopenharmony_ci struct sof_abi_hdr *data = cdata->data; 30662306a36Sopenharmony_ci struct sof_abi_hdr abi_hdr; 30762306a36Sopenharmony_ci struct snd_ctl_tlv header; 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci /* 31062306a36Sopenharmony_ci * The beginning of bytes data contains a header from where 31162306a36Sopenharmony_ci * the length (as bytes) is needed to know the correct copy 31262306a36Sopenharmony_ci * length of data from tlvd->tlv. 31362306a36Sopenharmony_ci */ 31462306a36Sopenharmony_ci if (copy_from_user(&header, tlvd, sizeof(struct snd_ctl_tlv))) 31562306a36Sopenharmony_ci return -EFAULT; 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci /* make sure TLV info is consistent */ 31862306a36Sopenharmony_ci if (header.length + sizeof(struct snd_ctl_tlv) > size) { 31962306a36Sopenharmony_ci dev_err_ratelimited(scomp->dev, 32062306a36Sopenharmony_ci "Inconsistent TLV, data %d + header %zu > %d\n", 32162306a36Sopenharmony_ci header.length, sizeof(struct snd_ctl_tlv), size); 32262306a36Sopenharmony_ci return -EINVAL; 32362306a36Sopenharmony_ci } 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci /* be->max is coming from topology */ 32662306a36Sopenharmony_ci if (header.length > scontrol->max_size) { 32762306a36Sopenharmony_ci dev_err_ratelimited(scomp->dev, 32862306a36Sopenharmony_ci "Bytes data size %d exceeds max %zu\n", 32962306a36Sopenharmony_ci header.length, scontrol->max_size); 33062306a36Sopenharmony_ci return -EINVAL; 33162306a36Sopenharmony_ci } 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci /* Verify the ABI header first */ 33462306a36Sopenharmony_ci if (copy_from_user(&abi_hdr, tlvd->tlv, sizeof(abi_hdr))) 33562306a36Sopenharmony_ci return -EFAULT; 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci if (abi_hdr.magic != SOF_IPC4_ABI_MAGIC) { 33862306a36Sopenharmony_ci dev_err_ratelimited(scomp->dev, "Wrong ABI magic 0x%08x\n", 33962306a36Sopenharmony_ci abi_hdr.magic); 34062306a36Sopenharmony_ci return -EINVAL; 34162306a36Sopenharmony_ci } 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci if (abi_hdr.size > scontrol->max_size - sizeof(abi_hdr)) { 34462306a36Sopenharmony_ci dev_err_ratelimited(scomp->dev, 34562306a36Sopenharmony_ci "%u bytes of control data is invalid, max is %zu\n", 34662306a36Sopenharmony_ci abi_hdr.size, scontrol->max_size - sizeof(abi_hdr)); 34762306a36Sopenharmony_ci return -EINVAL; 34862306a36Sopenharmony_ci } 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci if (!scontrol->old_ipc_control_data) { 35162306a36Sopenharmony_ci /* Create a backup of the current, valid bytes control */ 35262306a36Sopenharmony_ci scontrol->old_ipc_control_data = kmemdup(scontrol->ipc_control_data, 35362306a36Sopenharmony_ci scontrol->max_size, GFP_KERNEL); 35462306a36Sopenharmony_ci if (!scontrol->old_ipc_control_data) 35562306a36Sopenharmony_ci return -ENOMEM; 35662306a36Sopenharmony_ci } 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci /* Copy the whole binary data which includes the ABI header and the payload */ 35962306a36Sopenharmony_ci if (copy_from_user(data, tlvd->tlv, header.length)) { 36062306a36Sopenharmony_ci memcpy(scontrol->ipc_control_data, scontrol->old_ipc_control_data, 36162306a36Sopenharmony_ci scontrol->max_size); 36262306a36Sopenharmony_ci kfree(scontrol->old_ipc_control_data); 36362306a36Sopenharmony_ci scontrol->old_ipc_control_data = NULL; 36462306a36Sopenharmony_ci return -EFAULT; 36562306a36Sopenharmony_ci } 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci return sof_ipc4_set_get_bytes_data(sdev, scontrol, true, true); 36862306a36Sopenharmony_ci} 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_cistatic int _sof_ipc4_bytes_ext_get(struct snd_sof_control *scontrol, 37162306a36Sopenharmony_ci const unsigned int __user *binary_data, 37262306a36Sopenharmony_ci unsigned int size, bool from_dsp) 37362306a36Sopenharmony_ci{ 37462306a36Sopenharmony_ci struct snd_ctl_tlv __user *tlvd = (struct snd_ctl_tlv __user *)binary_data; 37562306a36Sopenharmony_ci struct sof_ipc4_control_data *cdata = scontrol->ipc_control_data; 37662306a36Sopenharmony_ci struct snd_soc_component *scomp = scontrol->scomp; 37762306a36Sopenharmony_ci struct sof_abi_hdr *data = cdata->data; 37862306a36Sopenharmony_ci struct snd_ctl_tlv header; 37962306a36Sopenharmony_ci size_t data_size; 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci /* 38262306a36Sopenharmony_ci * Decrement the limit by ext bytes header size to ensure the user space 38362306a36Sopenharmony_ci * buffer is not exceeded. 38462306a36Sopenharmony_ci */ 38562306a36Sopenharmony_ci if (size < sizeof(struct snd_ctl_tlv)) 38662306a36Sopenharmony_ci return -ENOSPC; 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci size -= sizeof(struct snd_ctl_tlv); 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci /* get all the component data from DSP */ 39162306a36Sopenharmony_ci if (from_dsp) { 39262306a36Sopenharmony_ci struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); 39362306a36Sopenharmony_ci int ret = sof_ipc4_set_get_bytes_data(sdev, scontrol, false, true); 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci if (ret < 0) 39662306a36Sopenharmony_ci return ret; 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci /* Set the ABI magic (if the control is not initialized) */ 39962306a36Sopenharmony_ci data->magic = SOF_IPC4_ABI_MAGIC; 40062306a36Sopenharmony_ci } 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci if (data->size > scontrol->max_size - sizeof(*data)) { 40362306a36Sopenharmony_ci dev_err_ratelimited(scomp->dev, 40462306a36Sopenharmony_ci "%u bytes of control data is invalid, max is %zu\n", 40562306a36Sopenharmony_ci data->size, scontrol->max_size - sizeof(*data)); 40662306a36Sopenharmony_ci return -EINVAL; 40762306a36Sopenharmony_ci } 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci data_size = data->size + sizeof(struct sof_abi_hdr); 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci /* make sure we don't exceed size provided by user space for data */ 41262306a36Sopenharmony_ci if (data_size > size) 41362306a36Sopenharmony_ci return -ENOSPC; 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci header.numid = scontrol->comp_id; 41662306a36Sopenharmony_ci header.length = data_size; 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci if (copy_to_user(tlvd, &header, sizeof(struct snd_ctl_tlv))) 41962306a36Sopenharmony_ci return -EFAULT; 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci if (copy_to_user(tlvd->tlv, data, data_size)) 42262306a36Sopenharmony_ci return -EFAULT; 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci return 0; 42562306a36Sopenharmony_ci} 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_cistatic int sof_ipc4_bytes_ext_get(struct snd_sof_control *scontrol, 42862306a36Sopenharmony_ci const unsigned int __user *binary_data, 42962306a36Sopenharmony_ci unsigned int size) 43062306a36Sopenharmony_ci{ 43162306a36Sopenharmony_ci return _sof_ipc4_bytes_ext_get(scontrol, binary_data, size, false); 43262306a36Sopenharmony_ci} 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_cistatic int sof_ipc4_bytes_ext_volatile_get(struct snd_sof_control *scontrol, 43562306a36Sopenharmony_ci const unsigned int __user *binary_data, 43662306a36Sopenharmony_ci unsigned int size) 43762306a36Sopenharmony_ci{ 43862306a36Sopenharmony_ci return _sof_ipc4_bytes_ext_get(scontrol, binary_data, size, true); 43962306a36Sopenharmony_ci} 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci/* set up all controls for the widget */ 44262306a36Sopenharmony_cistatic int sof_ipc4_widget_kcontrol_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget) 44362306a36Sopenharmony_ci{ 44462306a36Sopenharmony_ci struct snd_sof_control *scontrol; 44562306a36Sopenharmony_ci int ret = 0; 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci list_for_each_entry(scontrol, &sdev->kcontrol_list, list) { 44862306a36Sopenharmony_ci if (scontrol->comp_id == swidget->comp_id) { 44962306a36Sopenharmony_ci switch (scontrol->info_type) { 45062306a36Sopenharmony_ci case SND_SOC_TPLG_CTL_VOLSW: 45162306a36Sopenharmony_ci case SND_SOC_TPLG_CTL_VOLSW_SX: 45262306a36Sopenharmony_ci case SND_SOC_TPLG_CTL_VOLSW_XR_SX: 45362306a36Sopenharmony_ci ret = sof_ipc4_set_volume_data(sdev, swidget, 45462306a36Sopenharmony_ci scontrol, false); 45562306a36Sopenharmony_ci break; 45662306a36Sopenharmony_ci case SND_SOC_TPLG_CTL_BYTES: 45762306a36Sopenharmony_ci ret = sof_ipc4_set_get_bytes_data(sdev, scontrol, 45862306a36Sopenharmony_ci true, false); 45962306a36Sopenharmony_ci break; 46062306a36Sopenharmony_ci default: 46162306a36Sopenharmony_ci break; 46262306a36Sopenharmony_ci } 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci if (ret < 0) { 46562306a36Sopenharmony_ci dev_err(sdev->dev, 46662306a36Sopenharmony_ci "kcontrol %d set up failed for widget %s\n", 46762306a36Sopenharmony_ci scontrol->comp_id, swidget->widget->name); 46862306a36Sopenharmony_ci return ret; 46962306a36Sopenharmony_ci } 47062306a36Sopenharmony_ci } 47162306a36Sopenharmony_ci } 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci return 0; 47462306a36Sopenharmony_ci} 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_cistatic int 47762306a36Sopenharmony_cisof_ipc4_set_up_volume_table(struct snd_sof_control *scontrol, int tlv[SOF_TLV_ITEMS], int size) 47862306a36Sopenharmony_ci{ 47962306a36Sopenharmony_ci int i; 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci /* init the volume table */ 48262306a36Sopenharmony_ci scontrol->volume_table = kcalloc(size, sizeof(u32), GFP_KERNEL); 48362306a36Sopenharmony_ci if (!scontrol->volume_table) 48462306a36Sopenharmony_ci return -ENOMEM; 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci /* populate the volume table */ 48762306a36Sopenharmony_ci for (i = 0; i < size ; i++) { 48862306a36Sopenharmony_ci u32 val = vol_compute_gain(i, tlv); 48962306a36Sopenharmony_ci u64 q31val = ((u64)val) << 15; /* Can be over Q1.31, need to saturate */ 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_ci scontrol->volume_table[i] = q31val > SOF_IPC4_VOL_ZERO_DB ? 49262306a36Sopenharmony_ci SOF_IPC4_VOL_ZERO_DB : q31val; 49362306a36Sopenharmony_ci } 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_ci return 0; 49662306a36Sopenharmony_ci} 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ciconst struct sof_ipc_tplg_control_ops tplg_ipc4_control_ops = { 49962306a36Sopenharmony_ci .volume_put = sof_ipc4_volume_put, 50062306a36Sopenharmony_ci .volume_get = sof_ipc4_volume_get, 50162306a36Sopenharmony_ci .bytes_put = sof_ipc4_bytes_put, 50262306a36Sopenharmony_ci .bytes_get = sof_ipc4_bytes_get, 50362306a36Sopenharmony_ci .bytes_ext_put = sof_ipc4_bytes_ext_put, 50462306a36Sopenharmony_ci .bytes_ext_get = sof_ipc4_bytes_ext_get, 50562306a36Sopenharmony_ci .bytes_ext_volatile_get = sof_ipc4_bytes_ext_volatile_get, 50662306a36Sopenharmony_ci .widget_kcontrol_setup = sof_ipc4_widget_kcontrol_setup, 50762306a36Sopenharmony_ci .set_up_volume_table = sof_ipc4_set_up_volume_table, 50862306a36Sopenharmony_ci}; 509