18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) 28c2ecf20Sopenharmony_ci// 38c2ecf20Sopenharmony_ci// This file is provided under a dual BSD/GPLv2 license. When using or 48c2ecf20Sopenharmony_ci// redistributing this file, you may do so under either license. 58c2ecf20Sopenharmony_ci// 68c2ecf20Sopenharmony_ci// Copyright(c) 2018 Intel Corporation. All rights reserved. 78c2ecf20Sopenharmony_ci// 88c2ecf20Sopenharmony_ci// Author: Liam Girdwood <liam.r.girdwood@linux.intel.com> 98c2ecf20Sopenharmony_ci// 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci/* Mixer Controls */ 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h> 148c2ecf20Sopenharmony_ci#include <linux/leds.h> 158c2ecf20Sopenharmony_ci#include "sof-priv.h" 168c2ecf20Sopenharmony_ci#include "sof-audio.h" 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_cistatic void update_mute_led(struct snd_sof_control *scontrol, 198c2ecf20Sopenharmony_ci struct snd_kcontrol *kcontrol, 208c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 218c2ecf20Sopenharmony_ci{ 228c2ecf20Sopenharmony_ci int temp = 0; 238c2ecf20Sopenharmony_ci int mask; 248c2ecf20Sopenharmony_ci int i; 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci mask = 1U << snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci for (i = 0; i < scontrol->num_channels; i++) { 298c2ecf20Sopenharmony_ci if (ucontrol->value.integer.value[i]) { 308c2ecf20Sopenharmony_ci temp |= mask; 318c2ecf20Sopenharmony_ci break; 328c2ecf20Sopenharmony_ci } 338c2ecf20Sopenharmony_ci } 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci if (temp == scontrol->led_ctl.led_value) 368c2ecf20Sopenharmony_ci return; 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci scontrol->led_ctl.led_value = temp; 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci#if IS_REACHABLE(CONFIG_LEDS_TRIGGER_AUDIO) 418c2ecf20Sopenharmony_ci if (!scontrol->led_ctl.direction) 428c2ecf20Sopenharmony_ci ledtrig_audio_set(LED_AUDIO_MUTE, temp ? LED_OFF : LED_ON); 438c2ecf20Sopenharmony_ci else 448c2ecf20Sopenharmony_ci ledtrig_audio_set(LED_AUDIO_MICMUTE, temp ? LED_OFF : LED_ON); 458c2ecf20Sopenharmony_ci#endif 468c2ecf20Sopenharmony_ci} 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_cistatic inline u32 mixer_to_ipc(unsigned int value, u32 *volume_map, int size) 498c2ecf20Sopenharmony_ci{ 508c2ecf20Sopenharmony_ci if (value >= size) 518c2ecf20Sopenharmony_ci return volume_map[size - 1]; 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci return volume_map[value]; 548c2ecf20Sopenharmony_ci} 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_cistatic inline u32 ipc_to_mixer(u32 value, u32 *volume_map, int size) 578c2ecf20Sopenharmony_ci{ 588c2ecf20Sopenharmony_ci int i; 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci for (i = 0; i < size; i++) { 618c2ecf20Sopenharmony_ci if (volume_map[i] >= value) 628c2ecf20Sopenharmony_ci return i; 638c2ecf20Sopenharmony_ci } 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci return i - 1; 668c2ecf20Sopenharmony_ci} 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ciint snd_sof_volume_get(struct snd_kcontrol *kcontrol, 698c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 708c2ecf20Sopenharmony_ci{ 718c2ecf20Sopenharmony_ci struct soc_mixer_control *sm = 728c2ecf20Sopenharmony_ci (struct soc_mixer_control *)kcontrol->private_value; 738c2ecf20Sopenharmony_ci struct snd_sof_control *scontrol = sm->dobj.private; 748c2ecf20Sopenharmony_ci struct sof_ipc_ctrl_data *cdata = scontrol->control_data; 758c2ecf20Sopenharmony_ci unsigned int i, channels = scontrol->num_channels; 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci /* read back each channel */ 788c2ecf20Sopenharmony_ci for (i = 0; i < channels; i++) 798c2ecf20Sopenharmony_ci ucontrol->value.integer.value[i] = 808c2ecf20Sopenharmony_ci ipc_to_mixer(cdata->chanv[i].value, 818c2ecf20Sopenharmony_ci scontrol->volume_table, sm->max + 1); 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci return 0; 848c2ecf20Sopenharmony_ci} 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ciint snd_sof_volume_put(struct snd_kcontrol *kcontrol, 878c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 888c2ecf20Sopenharmony_ci{ 898c2ecf20Sopenharmony_ci struct soc_mixer_control *sm = 908c2ecf20Sopenharmony_ci (struct soc_mixer_control *)kcontrol->private_value; 918c2ecf20Sopenharmony_ci struct snd_sof_control *scontrol = sm->dobj.private; 928c2ecf20Sopenharmony_ci struct snd_soc_component *scomp = scontrol->scomp; 938c2ecf20Sopenharmony_ci struct sof_ipc_ctrl_data *cdata = scontrol->control_data; 948c2ecf20Sopenharmony_ci unsigned int i, channels = scontrol->num_channels; 958c2ecf20Sopenharmony_ci bool change = false; 968c2ecf20Sopenharmony_ci u32 value; 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci /* update each channel */ 998c2ecf20Sopenharmony_ci for (i = 0; i < channels; i++) { 1008c2ecf20Sopenharmony_ci value = mixer_to_ipc(ucontrol->value.integer.value[i], 1018c2ecf20Sopenharmony_ci scontrol->volume_table, sm->max + 1); 1028c2ecf20Sopenharmony_ci change = change || (value != cdata->chanv[i].value); 1038c2ecf20Sopenharmony_ci cdata->chanv[i].channel = i; 1048c2ecf20Sopenharmony_ci cdata->chanv[i].value = value; 1058c2ecf20Sopenharmony_ci } 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci /* notify DSP of mixer updates */ 1088c2ecf20Sopenharmony_ci if (pm_runtime_active(scomp->dev)) 1098c2ecf20Sopenharmony_ci snd_sof_ipc_set_get_comp_data(scontrol, 1108c2ecf20Sopenharmony_ci SOF_IPC_COMP_SET_VALUE, 1118c2ecf20Sopenharmony_ci SOF_CTRL_TYPE_VALUE_CHAN_GET, 1128c2ecf20Sopenharmony_ci SOF_CTRL_CMD_VOLUME, 1138c2ecf20Sopenharmony_ci true); 1148c2ecf20Sopenharmony_ci return change; 1158c2ecf20Sopenharmony_ci} 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ciint snd_sof_switch_get(struct snd_kcontrol *kcontrol, 1188c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 1198c2ecf20Sopenharmony_ci{ 1208c2ecf20Sopenharmony_ci struct soc_mixer_control *sm = 1218c2ecf20Sopenharmony_ci (struct soc_mixer_control *)kcontrol->private_value; 1228c2ecf20Sopenharmony_ci struct snd_sof_control *scontrol = sm->dobj.private; 1238c2ecf20Sopenharmony_ci struct sof_ipc_ctrl_data *cdata = scontrol->control_data; 1248c2ecf20Sopenharmony_ci unsigned int i, channels = scontrol->num_channels; 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci /* read back each channel */ 1278c2ecf20Sopenharmony_ci for (i = 0; i < channels; i++) 1288c2ecf20Sopenharmony_ci ucontrol->value.integer.value[i] = cdata->chanv[i].value; 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci return 0; 1318c2ecf20Sopenharmony_ci} 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ciint snd_sof_switch_put(struct snd_kcontrol *kcontrol, 1348c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 1358c2ecf20Sopenharmony_ci{ 1368c2ecf20Sopenharmony_ci struct soc_mixer_control *sm = 1378c2ecf20Sopenharmony_ci (struct soc_mixer_control *)kcontrol->private_value; 1388c2ecf20Sopenharmony_ci struct snd_sof_control *scontrol = sm->dobj.private; 1398c2ecf20Sopenharmony_ci struct snd_soc_component *scomp = scontrol->scomp; 1408c2ecf20Sopenharmony_ci struct sof_ipc_ctrl_data *cdata = scontrol->control_data; 1418c2ecf20Sopenharmony_ci unsigned int i, channels = scontrol->num_channels; 1428c2ecf20Sopenharmony_ci bool change = false; 1438c2ecf20Sopenharmony_ci u32 value; 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci /* update each channel */ 1468c2ecf20Sopenharmony_ci for (i = 0; i < channels; i++) { 1478c2ecf20Sopenharmony_ci value = ucontrol->value.integer.value[i]; 1488c2ecf20Sopenharmony_ci change = change || (value != cdata->chanv[i].value); 1498c2ecf20Sopenharmony_ci cdata->chanv[i].channel = i; 1508c2ecf20Sopenharmony_ci cdata->chanv[i].value = value; 1518c2ecf20Sopenharmony_ci } 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci if (scontrol->led_ctl.use_led) 1548c2ecf20Sopenharmony_ci update_mute_led(scontrol, kcontrol, ucontrol); 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci /* notify DSP of mixer updates */ 1578c2ecf20Sopenharmony_ci if (pm_runtime_active(scomp->dev)) 1588c2ecf20Sopenharmony_ci snd_sof_ipc_set_get_comp_data(scontrol, 1598c2ecf20Sopenharmony_ci SOF_IPC_COMP_SET_VALUE, 1608c2ecf20Sopenharmony_ci SOF_CTRL_TYPE_VALUE_CHAN_GET, 1618c2ecf20Sopenharmony_ci SOF_CTRL_CMD_SWITCH, 1628c2ecf20Sopenharmony_ci true); 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci return change; 1658c2ecf20Sopenharmony_ci} 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ciint snd_sof_enum_get(struct snd_kcontrol *kcontrol, 1688c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 1698c2ecf20Sopenharmony_ci{ 1708c2ecf20Sopenharmony_ci struct soc_enum *se = 1718c2ecf20Sopenharmony_ci (struct soc_enum *)kcontrol->private_value; 1728c2ecf20Sopenharmony_ci struct snd_sof_control *scontrol = se->dobj.private; 1738c2ecf20Sopenharmony_ci struct sof_ipc_ctrl_data *cdata = scontrol->control_data; 1748c2ecf20Sopenharmony_ci unsigned int i, channels = scontrol->num_channels; 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci /* read back each channel */ 1778c2ecf20Sopenharmony_ci for (i = 0; i < channels; i++) 1788c2ecf20Sopenharmony_ci ucontrol->value.enumerated.item[i] = cdata->chanv[i].value; 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci return 0; 1818c2ecf20Sopenharmony_ci} 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ciint snd_sof_enum_put(struct snd_kcontrol *kcontrol, 1848c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 1858c2ecf20Sopenharmony_ci{ 1868c2ecf20Sopenharmony_ci struct soc_enum *se = 1878c2ecf20Sopenharmony_ci (struct soc_enum *)kcontrol->private_value; 1888c2ecf20Sopenharmony_ci struct snd_sof_control *scontrol = se->dobj.private; 1898c2ecf20Sopenharmony_ci struct snd_soc_component *scomp = scontrol->scomp; 1908c2ecf20Sopenharmony_ci struct sof_ipc_ctrl_data *cdata = scontrol->control_data; 1918c2ecf20Sopenharmony_ci unsigned int i, channels = scontrol->num_channels; 1928c2ecf20Sopenharmony_ci bool change = false; 1938c2ecf20Sopenharmony_ci u32 value; 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci /* update each channel */ 1968c2ecf20Sopenharmony_ci for (i = 0; i < channels; i++) { 1978c2ecf20Sopenharmony_ci value = ucontrol->value.enumerated.item[i]; 1988c2ecf20Sopenharmony_ci change = change || (value != cdata->chanv[i].value); 1998c2ecf20Sopenharmony_ci cdata->chanv[i].channel = i; 2008c2ecf20Sopenharmony_ci cdata->chanv[i].value = value; 2018c2ecf20Sopenharmony_ci } 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci /* notify DSP of enum updates */ 2048c2ecf20Sopenharmony_ci if (pm_runtime_active(scomp->dev)) 2058c2ecf20Sopenharmony_ci snd_sof_ipc_set_get_comp_data(scontrol, 2068c2ecf20Sopenharmony_ci SOF_IPC_COMP_SET_VALUE, 2078c2ecf20Sopenharmony_ci SOF_CTRL_TYPE_VALUE_CHAN_GET, 2088c2ecf20Sopenharmony_ci SOF_CTRL_CMD_ENUM, 2098c2ecf20Sopenharmony_ci true); 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci return change; 2128c2ecf20Sopenharmony_ci} 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ciint snd_sof_bytes_get(struct snd_kcontrol *kcontrol, 2158c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 2168c2ecf20Sopenharmony_ci{ 2178c2ecf20Sopenharmony_ci struct soc_bytes_ext *be = 2188c2ecf20Sopenharmony_ci (struct soc_bytes_ext *)kcontrol->private_value; 2198c2ecf20Sopenharmony_ci struct snd_sof_control *scontrol = be->dobj.private; 2208c2ecf20Sopenharmony_ci struct snd_soc_component *scomp = scontrol->scomp; 2218c2ecf20Sopenharmony_ci struct sof_ipc_ctrl_data *cdata = scontrol->control_data; 2228c2ecf20Sopenharmony_ci struct sof_abi_hdr *data = cdata->data; 2238c2ecf20Sopenharmony_ci size_t size; 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci if (be->max > sizeof(ucontrol->value.bytes.data)) { 2268c2ecf20Sopenharmony_ci dev_err_ratelimited(scomp->dev, 2278c2ecf20Sopenharmony_ci "error: data max %d exceeds ucontrol data array size\n", 2288c2ecf20Sopenharmony_ci be->max); 2298c2ecf20Sopenharmony_ci return -EINVAL; 2308c2ecf20Sopenharmony_ci } 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci /* be->max has been verified to be >= sizeof(struct sof_abi_hdr) */ 2338c2ecf20Sopenharmony_ci if (data->size > be->max - sizeof(*data)) { 2348c2ecf20Sopenharmony_ci dev_err_ratelimited(scomp->dev, 2358c2ecf20Sopenharmony_ci "error: %u bytes of control data is invalid, max is %zu\n", 2368c2ecf20Sopenharmony_ci data->size, be->max - sizeof(*data)); 2378c2ecf20Sopenharmony_ci return -EINVAL; 2388c2ecf20Sopenharmony_ci } 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci size = data->size + sizeof(*data); 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci /* copy back to kcontrol */ 2438c2ecf20Sopenharmony_ci memcpy(ucontrol->value.bytes.data, data, size); 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci return 0; 2468c2ecf20Sopenharmony_ci} 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ciint snd_sof_bytes_put(struct snd_kcontrol *kcontrol, 2498c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 2508c2ecf20Sopenharmony_ci{ 2518c2ecf20Sopenharmony_ci struct soc_bytes_ext *be = 2528c2ecf20Sopenharmony_ci (struct soc_bytes_ext *)kcontrol->private_value; 2538c2ecf20Sopenharmony_ci struct snd_sof_control *scontrol = be->dobj.private; 2548c2ecf20Sopenharmony_ci struct snd_soc_component *scomp = scontrol->scomp; 2558c2ecf20Sopenharmony_ci struct sof_ipc_ctrl_data *cdata = scontrol->control_data; 2568c2ecf20Sopenharmony_ci struct sof_abi_hdr *data = cdata->data; 2578c2ecf20Sopenharmony_ci size_t size; 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci if (be->max > sizeof(ucontrol->value.bytes.data)) { 2608c2ecf20Sopenharmony_ci dev_err_ratelimited(scomp->dev, 2618c2ecf20Sopenharmony_ci "error: data max %d exceeds ucontrol data array size\n", 2628c2ecf20Sopenharmony_ci be->max); 2638c2ecf20Sopenharmony_ci return -EINVAL; 2648c2ecf20Sopenharmony_ci } 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci /* be->max has been verified to be >= sizeof(struct sof_abi_hdr) */ 2678c2ecf20Sopenharmony_ci if (data->size > be->max - sizeof(*data)) { 2688c2ecf20Sopenharmony_ci dev_err_ratelimited(scomp->dev, 2698c2ecf20Sopenharmony_ci "error: data size too big %u bytes max is %zu\n", 2708c2ecf20Sopenharmony_ci data->size, be->max - sizeof(*data)); 2718c2ecf20Sopenharmony_ci return -EINVAL; 2728c2ecf20Sopenharmony_ci } 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci size = data->size + sizeof(*data); 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci /* copy from kcontrol */ 2778c2ecf20Sopenharmony_ci memcpy(data, ucontrol->value.bytes.data, size); 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci /* notify DSP of byte control updates */ 2808c2ecf20Sopenharmony_ci if (pm_runtime_active(scomp->dev)) 2818c2ecf20Sopenharmony_ci snd_sof_ipc_set_get_comp_data(scontrol, 2828c2ecf20Sopenharmony_ci SOF_IPC_COMP_SET_DATA, 2838c2ecf20Sopenharmony_ci SOF_CTRL_TYPE_DATA_SET, 2848c2ecf20Sopenharmony_ci scontrol->cmd, 2858c2ecf20Sopenharmony_ci true); 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci return 0; 2888c2ecf20Sopenharmony_ci} 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ciint snd_sof_bytes_ext_put(struct snd_kcontrol *kcontrol, 2918c2ecf20Sopenharmony_ci const unsigned int __user *binary_data, 2928c2ecf20Sopenharmony_ci unsigned int size) 2938c2ecf20Sopenharmony_ci{ 2948c2ecf20Sopenharmony_ci struct soc_bytes_ext *be = 2958c2ecf20Sopenharmony_ci (struct soc_bytes_ext *)kcontrol->private_value; 2968c2ecf20Sopenharmony_ci struct snd_sof_control *scontrol = be->dobj.private; 2978c2ecf20Sopenharmony_ci struct snd_soc_component *scomp = scontrol->scomp; 2988c2ecf20Sopenharmony_ci struct sof_ipc_ctrl_data *cdata = scontrol->control_data; 2998c2ecf20Sopenharmony_ci struct snd_ctl_tlv header; 3008c2ecf20Sopenharmony_ci const struct snd_ctl_tlv __user *tlvd = 3018c2ecf20Sopenharmony_ci (const struct snd_ctl_tlv __user *)binary_data; 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci /* make sure we have at least a header */ 3048c2ecf20Sopenharmony_ci if (size < sizeof(struct snd_ctl_tlv)) 3058c2ecf20Sopenharmony_ci return -EINVAL; 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci /* 3088c2ecf20Sopenharmony_ci * The beginning of bytes data contains a header from where 3098c2ecf20Sopenharmony_ci * the length (as bytes) is needed to know the correct copy 3108c2ecf20Sopenharmony_ci * length of data from tlvd->tlv. 3118c2ecf20Sopenharmony_ci */ 3128c2ecf20Sopenharmony_ci if (copy_from_user(&header, tlvd, sizeof(const struct snd_ctl_tlv))) 3138c2ecf20Sopenharmony_ci return -EFAULT; 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci /* make sure TLV info is consistent */ 3168c2ecf20Sopenharmony_ci if (header.length + sizeof(struct snd_ctl_tlv) > size) { 3178c2ecf20Sopenharmony_ci dev_err_ratelimited(scomp->dev, "error: inconsistent TLV, data %d + header %zu > %d\n", 3188c2ecf20Sopenharmony_ci header.length, sizeof(struct snd_ctl_tlv), size); 3198c2ecf20Sopenharmony_ci return -EINVAL; 3208c2ecf20Sopenharmony_ci } 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci /* be->max is coming from topology */ 3238c2ecf20Sopenharmony_ci if (header.length > be->max) { 3248c2ecf20Sopenharmony_ci dev_err_ratelimited(scomp->dev, "error: Bytes data size %d exceeds max %d.\n", 3258c2ecf20Sopenharmony_ci header.length, be->max); 3268c2ecf20Sopenharmony_ci return -EINVAL; 3278c2ecf20Sopenharmony_ci } 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci /* Check that header id matches the command */ 3308c2ecf20Sopenharmony_ci if (header.numid != scontrol->cmd) { 3318c2ecf20Sopenharmony_ci dev_err_ratelimited(scomp->dev, 3328c2ecf20Sopenharmony_ci "error: incorrect numid %d\n", 3338c2ecf20Sopenharmony_ci header.numid); 3348c2ecf20Sopenharmony_ci return -EINVAL; 3358c2ecf20Sopenharmony_ci } 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci if (copy_from_user(cdata->data, tlvd->tlv, header.length)) 3388c2ecf20Sopenharmony_ci return -EFAULT; 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci if (cdata->data->magic != SOF_ABI_MAGIC) { 3418c2ecf20Sopenharmony_ci dev_err_ratelimited(scomp->dev, 3428c2ecf20Sopenharmony_ci "error: Wrong ABI magic 0x%08x.\n", 3438c2ecf20Sopenharmony_ci cdata->data->magic); 3448c2ecf20Sopenharmony_ci return -EINVAL; 3458c2ecf20Sopenharmony_ci } 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci if (SOF_ABI_VERSION_INCOMPATIBLE(SOF_ABI_VERSION, cdata->data->abi)) { 3488c2ecf20Sopenharmony_ci dev_err_ratelimited(scomp->dev, "error: Incompatible ABI version 0x%08x.\n", 3498c2ecf20Sopenharmony_ci cdata->data->abi); 3508c2ecf20Sopenharmony_ci return -EINVAL; 3518c2ecf20Sopenharmony_ci } 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci /* be->max has been verified to be >= sizeof(struct sof_abi_hdr) */ 3548c2ecf20Sopenharmony_ci if (cdata->data->size > be->max - sizeof(const struct sof_abi_hdr)) { 3558c2ecf20Sopenharmony_ci dev_err_ratelimited(scomp->dev, "error: Mismatch in ABI data size (truncated?).\n"); 3568c2ecf20Sopenharmony_ci return -EINVAL; 3578c2ecf20Sopenharmony_ci } 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci /* notify DSP of byte control updates */ 3608c2ecf20Sopenharmony_ci if (pm_runtime_active(scomp->dev)) 3618c2ecf20Sopenharmony_ci snd_sof_ipc_set_get_comp_data(scontrol, 3628c2ecf20Sopenharmony_ci SOF_IPC_COMP_SET_DATA, 3638c2ecf20Sopenharmony_ci SOF_CTRL_TYPE_DATA_SET, 3648c2ecf20Sopenharmony_ci scontrol->cmd, 3658c2ecf20Sopenharmony_ci true); 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci return 0; 3688c2ecf20Sopenharmony_ci} 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ciint snd_sof_bytes_ext_volatile_get(struct snd_kcontrol *kcontrol, unsigned int __user *binary_data, 3718c2ecf20Sopenharmony_ci unsigned int size) 3728c2ecf20Sopenharmony_ci{ 3738c2ecf20Sopenharmony_ci struct soc_bytes_ext *be = (struct soc_bytes_ext *)kcontrol->private_value; 3748c2ecf20Sopenharmony_ci struct snd_sof_control *scontrol = be->dobj.private; 3758c2ecf20Sopenharmony_ci struct snd_soc_component *scomp = scontrol->scomp; 3768c2ecf20Sopenharmony_ci struct sof_ipc_ctrl_data *cdata = scontrol->control_data; 3778c2ecf20Sopenharmony_ci struct snd_ctl_tlv header; 3788c2ecf20Sopenharmony_ci struct snd_ctl_tlv __user *tlvd = (struct snd_ctl_tlv __user *)binary_data; 3798c2ecf20Sopenharmony_ci size_t data_size; 3808c2ecf20Sopenharmony_ci int ret; 3818c2ecf20Sopenharmony_ci int err; 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci /* 3848c2ecf20Sopenharmony_ci * Decrement the limit by ext bytes header size to 3858c2ecf20Sopenharmony_ci * ensure the user space buffer is not exceeded. 3868c2ecf20Sopenharmony_ci */ 3878c2ecf20Sopenharmony_ci if (size < sizeof(struct snd_ctl_tlv)) 3888c2ecf20Sopenharmony_ci return -ENOSPC; 3898c2ecf20Sopenharmony_ci size -= sizeof(struct snd_ctl_tlv); 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci ret = pm_runtime_get_sync(scomp->dev); 3928c2ecf20Sopenharmony_ci if (ret < 0 && ret != -EACCES) { 3938c2ecf20Sopenharmony_ci dev_err_ratelimited(scomp->dev, "error: bytes_ext get failed to resume %d\n", ret); 3948c2ecf20Sopenharmony_ci pm_runtime_put_noidle(scomp->dev); 3958c2ecf20Sopenharmony_ci return ret; 3968c2ecf20Sopenharmony_ci } 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci /* set the ABI header values */ 3998c2ecf20Sopenharmony_ci cdata->data->magic = SOF_ABI_MAGIC; 4008c2ecf20Sopenharmony_ci cdata->data->abi = SOF_ABI_VERSION; 4018c2ecf20Sopenharmony_ci /* get all the component data from DSP */ 4028c2ecf20Sopenharmony_ci ret = snd_sof_ipc_set_get_comp_data(scontrol, SOF_IPC_COMP_GET_DATA, SOF_CTRL_TYPE_DATA_GET, 4038c2ecf20Sopenharmony_ci scontrol->cmd, false); 4048c2ecf20Sopenharmony_ci if (ret < 0) 4058c2ecf20Sopenharmony_ci goto out; 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci /* check data size doesn't exceed max coming from topology */ 4088c2ecf20Sopenharmony_ci if (cdata->data->size > be->max - sizeof(const struct sof_abi_hdr)) { 4098c2ecf20Sopenharmony_ci dev_err_ratelimited(scomp->dev, "error: user data size %d exceeds max size %zu.\n", 4108c2ecf20Sopenharmony_ci cdata->data->size, 4118c2ecf20Sopenharmony_ci be->max - sizeof(const struct sof_abi_hdr)); 4128c2ecf20Sopenharmony_ci ret = -EINVAL; 4138c2ecf20Sopenharmony_ci goto out; 4148c2ecf20Sopenharmony_ci } 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci data_size = cdata->data->size + sizeof(const struct sof_abi_hdr); 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci /* make sure we don't exceed size provided by user space for data */ 4198c2ecf20Sopenharmony_ci if (data_size > size) { 4208c2ecf20Sopenharmony_ci ret = -ENOSPC; 4218c2ecf20Sopenharmony_ci goto out; 4228c2ecf20Sopenharmony_ci } 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci header.numid = scontrol->cmd; 4258c2ecf20Sopenharmony_ci header.length = data_size; 4268c2ecf20Sopenharmony_ci if (copy_to_user(tlvd, &header, sizeof(const struct snd_ctl_tlv))) { 4278c2ecf20Sopenharmony_ci ret = -EFAULT; 4288c2ecf20Sopenharmony_ci goto out; 4298c2ecf20Sopenharmony_ci } 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci if (copy_to_user(tlvd->tlv, cdata->data, data_size)) 4328c2ecf20Sopenharmony_ci ret = -EFAULT; 4338c2ecf20Sopenharmony_ciout: 4348c2ecf20Sopenharmony_ci pm_runtime_mark_last_busy(scomp->dev); 4358c2ecf20Sopenharmony_ci err = pm_runtime_put_autosuspend(scomp->dev); 4368c2ecf20Sopenharmony_ci if (err < 0) 4378c2ecf20Sopenharmony_ci dev_err_ratelimited(scomp->dev, "error: bytes_ext get failed to idle %d\n", err); 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci return ret; 4408c2ecf20Sopenharmony_ci} 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_ciint snd_sof_bytes_ext_get(struct snd_kcontrol *kcontrol, 4438c2ecf20Sopenharmony_ci unsigned int __user *binary_data, 4448c2ecf20Sopenharmony_ci unsigned int size) 4458c2ecf20Sopenharmony_ci{ 4468c2ecf20Sopenharmony_ci struct soc_bytes_ext *be = 4478c2ecf20Sopenharmony_ci (struct soc_bytes_ext *)kcontrol->private_value; 4488c2ecf20Sopenharmony_ci struct snd_sof_control *scontrol = be->dobj.private; 4498c2ecf20Sopenharmony_ci struct snd_soc_component *scomp = scontrol->scomp; 4508c2ecf20Sopenharmony_ci struct sof_ipc_ctrl_data *cdata = scontrol->control_data; 4518c2ecf20Sopenharmony_ci struct snd_ctl_tlv header; 4528c2ecf20Sopenharmony_ci struct snd_ctl_tlv __user *tlvd = 4538c2ecf20Sopenharmony_ci (struct snd_ctl_tlv __user *)binary_data; 4548c2ecf20Sopenharmony_ci size_t data_size; 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci /* 4578c2ecf20Sopenharmony_ci * Decrement the limit by ext bytes header size to 4588c2ecf20Sopenharmony_ci * ensure the user space buffer is not exceeded. 4598c2ecf20Sopenharmony_ci */ 4608c2ecf20Sopenharmony_ci if (size < sizeof(struct snd_ctl_tlv)) 4618c2ecf20Sopenharmony_ci return -ENOSPC; 4628c2ecf20Sopenharmony_ci size -= sizeof(struct snd_ctl_tlv); 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_ci /* set the ABI header values */ 4658c2ecf20Sopenharmony_ci cdata->data->magic = SOF_ABI_MAGIC; 4668c2ecf20Sopenharmony_ci cdata->data->abi = SOF_ABI_VERSION; 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ci /* check data size doesn't exceed max coming from topology */ 4698c2ecf20Sopenharmony_ci if (cdata->data->size > be->max - sizeof(const struct sof_abi_hdr)) { 4708c2ecf20Sopenharmony_ci dev_err_ratelimited(scomp->dev, "error: user data size %d exceeds max size %zu.\n", 4718c2ecf20Sopenharmony_ci cdata->data->size, 4728c2ecf20Sopenharmony_ci be->max - sizeof(const struct sof_abi_hdr)); 4738c2ecf20Sopenharmony_ci return -EINVAL; 4748c2ecf20Sopenharmony_ci } 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_ci data_size = cdata->data->size + sizeof(const struct sof_abi_hdr); 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_ci /* make sure we don't exceed size provided by user space for data */ 4798c2ecf20Sopenharmony_ci if (data_size > size) 4808c2ecf20Sopenharmony_ci return -ENOSPC; 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_ci header.numid = scontrol->cmd; 4838c2ecf20Sopenharmony_ci header.length = data_size; 4848c2ecf20Sopenharmony_ci if (copy_to_user(tlvd, &header, sizeof(const struct snd_ctl_tlv))) 4858c2ecf20Sopenharmony_ci return -EFAULT; 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_ci if (copy_to_user(tlvd->tlv, cdata->data, data_size)) 4888c2ecf20Sopenharmony_ci return -EFAULT; 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_ci return 0; 4918c2ecf20Sopenharmony_ci} 492