772 lines · 21.5 KB · C
RawDownload
// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)//// This file is provided under a dual BSD/GPLv2 license.  When using or// redistributing this file, you may do so under either license.//// Copyright(c) 2021 Intel Corporation//// #include "sof-priv.h"#include "sof-audio.h"#include "ipc3-priv.h" /* IPC set()/get() for kcontrols. */static int sof_ipc3_set_get_kcontrol_data(struct snd_sof_control *scontrol,					  bool set, bool lock){	struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scontrol->scomp);	struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data;	const struct sof_ipc_ops *iops = sdev->ipc->ops;	enum sof_ipc_ctrl_type ctrl_type;	struct snd_sof_widget *swidget;	bool widget_found = false;	u32 ipc_cmd, msg_bytes;	int ret = 0; 	list_for_each_entry(swidget, &sdev->widget_list, list) {		if (swidget->comp_id == scontrol->comp_id) {			widget_found = true;			break;		}	} 	if (!widget_found) {		dev_err(sdev->dev, "%s: can't find widget with id %d\n", __func__,			scontrol->comp_id);		return -EINVAL;	} 	if (lock)		mutex_lock(&swidget->setup_mutex);	else		lockdep_assert_held(&swidget->setup_mutex); 	/*	 * Volatile controls should always be part of static pipelines and the	 * widget use_count would always be > 0 in this case. For the others,	 * just return the cached value if the widget is not set up.	 */	if (!swidget->use_count)		goto unlock; 	/*	 * Select the IPC cmd and the ctrl_type based on the ctrl_cmd and the	 * direction	 * Note: SOF_CTRL_TYPE_VALUE_COMP_* is not used and supported currently	 *	 for ctrl_type	 */	if (cdata->cmd == SOF_CTRL_CMD_BINARY) {		ipc_cmd = set ? SOF_IPC_COMP_SET_DATA : SOF_IPC_COMP_GET_DATA;		ctrl_type = set ? SOF_CTRL_TYPE_DATA_SET : SOF_CTRL_TYPE_DATA_GET;	} else {		ipc_cmd = set ? SOF_IPC_COMP_SET_VALUE : SOF_IPC_COMP_GET_VALUE;		ctrl_type = set ? SOF_CTRL_TYPE_VALUE_CHAN_SET : SOF_CTRL_TYPE_VALUE_CHAN_GET;	} 	cdata->rhdr.hdr.cmd = SOF_IPC_GLB_COMP_MSG | ipc_cmd;	cdata->type = ctrl_type;	cdata->comp_id = scontrol->comp_id;	cdata->msg_index = 0; 	/* calculate header and data size */	switch (cdata->type) {	case SOF_CTRL_TYPE_VALUE_CHAN_GET:	case SOF_CTRL_TYPE_VALUE_CHAN_SET:		cdata->num_elems = scontrol->num_channels; 		msg_bytes = scontrol->num_channels *			    sizeof(struct sof_ipc_ctrl_value_chan);		msg_bytes += sizeof(struct sof_ipc_ctrl_data);		break;	case SOF_CTRL_TYPE_DATA_GET:	case SOF_CTRL_TYPE_DATA_SET:		cdata->num_elems = cdata->data->size; 		msg_bytes = cdata->data->size;		msg_bytes += sizeof(struct sof_ipc_ctrl_data) +			     sizeof(struct sof_abi_hdr);		break;	default:		ret = -EINVAL;		goto unlock;	} 	cdata->rhdr.hdr.size = msg_bytes;	cdata->elems_remaining = 0; 	ret = iops->set_get_data(sdev, cdata, cdata->rhdr.hdr.size, set);	if (!set)		goto unlock; 	/* It is a set-data operation, and we have a backup that we can restore */	if (ret < 0) {		if (!scontrol->old_ipc_control_data)			goto unlock;		/*		 * Current ipc_control_data is not valid, we use the last known good		 * configuration		 */		memcpy(scontrol->ipc_control_data, scontrol->old_ipc_control_data,		       scontrol->max_size);		kfree(scontrol->old_ipc_control_data);		scontrol->old_ipc_control_data = NULL;		/* Send the last known good configuration to firmware */		ret = iops->set_get_data(sdev, cdata, cdata->rhdr.hdr.size, set);		if (ret < 0)			goto unlock;	} unlock:	if (lock)		mutex_unlock(&swidget->setup_mutex); 	return ret;} static void sof_ipc3_refresh_control(struct snd_sof_control *scontrol){	struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data;	struct snd_soc_component *scomp = scontrol->scomp;	int ret; 	if (!scontrol->comp_data_dirty)		return; 	if (!pm_runtime_active(scomp->dev))		return; 	/* set the ABI header values */	cdata->data->magic = SOF_ABI_MAGIC;	cdata->data->abi = SOF_ABI_VERSION; 	/* refresh the component data from DSP */	scontrol->comp_data_dirty = false;	ret = sof_ipc3_set_get_kcontrol_data(scontrol, false, true);	if (ret < 0) {		dev_err(scomp->dev, "Failed to get control data: %d\n", ret); 		/* Set the flag to re-try next time to get the data */		scontrol->comp_data_dirty = true;	}} static int sof_ipc3_volume_get(struct snd_sof_control *scontrol,			       struct snd_ctl_elem_value *ucontrol){	struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data;	unsigned int channels = scontrol->num_channels;	unsigned int i; 	sof_ipc3_refresh_control(scontrol); 	/* read back each channel */	for (i = 0; i < channels; i++)		ucontrol->value.integer.value[i] = ipc_to_mixer(cdata->chanv[i].value,								scontrol->volume_table,								scontrol->max + 1); 	return 0;} static bool sof_ipc3_volume_put(struct snd_sof_control *scontrol,				struct snd_ctl_elem_value *ucontrol){	struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data;	struct snd_soc_component *scomp = scontrol->scomp;	unsigned int channels = scontrol->num_channels;	unsigned int i;	bool change = false; 	/* update each channel */	for (i = 0; i < channels; i++) {		u32 value = mixer_to_ipc(ucontrol->value.integer.value[i],					 scontrol->volume_table, scontrol->max + 1); 		change = change || (value != cdata->chanv[i].value);		cdata->chanv[i].channel = i;		cdata->chanv[i].value = value;	} 	/* notify DSP of mixer updates */	if (pm_runtime_active(scomp->dev)) {		int ret = sof_ipc3_set_get_kcontrol_data(scontrol, true, true); 		if (ret < 0) {			dev_err(scomp->dev, "Failed to set mixer updates for %s\n",				scontrol->name);			return false;		}	} 	return change;} static int sof_ipc3_switch_get(struct snd_sof_control *scontrol,			       struct snd_ctl_elem_value *ucontrol){	struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data;	unsigned int channels = scontrol->num_channels;	unsigned int i; 	sof_ipc3_refresh_control(scontrol); 	/* read back each channel */	for (i = 0; i < channels; i++)		ucontrol->value.integer.value[i] = cdata->chanv[i].value; 	return 0;} static bool sof_ipc3_switch_put(struct snd_sof_control *scontrol,				struct snd_ctl_elem_value *ucontrol){	struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data;	struct snd_soc_component *scomp = scontrol->scomp;	unsigned int channels = scontrol->num_channels;	unsigned int i;	bool change = false;	u32 value; 	/* update each channel */	for (i = 0; i < channels; i++) {		value = ucontrol->value.integer.value[i];		change = change || (value != cdata->chanv[i].value);		cdata->chanv[i].channel = i;		cdata->chanv[i].value = value;	} 	/* notify DSP of mixer updates */	if (pm_runtime_active(scomp->dev)) {		int ret = sof_ipc3_set_get_kcontrol_data(scontrol, true, true); 		if (ret < 0) {			dev_err(scomp->dev, "Failed to set mixer updates for %s\n",				scontrol->name);			return false;		}	} 	return change;} static int sof_ipc3_enum_get(struct snd_sof_control *scontrol,			     struct snd_ctl_elem_value *ucontrol){	struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data;	unsigned int channels = scontrol->num_channels;	unsigned int i; 	sof_ipc3_refresh_control(scontrol); 	/* read back each channel */	for (i = 0; i < channels; i++)		ucontrol->value.enumerated.item[i] = cdata->chanv[i].value; 	return 0;} static bool sof_ipc3_enum_put(struct snd_sof_control *scontrol,			      struct snd_ctl_elem_value *ucontrol){	struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data;	struct snd_soc_component *scomp = scontrol->scomp;	unsigned int channels = scontrol->num_channels;	unsigned int i;	bool change = false;	u32 value; 	/* update each channel */	for (i = 0; i < channels; i++) {		value = ucontrol->value.enumerated.item[i];		change = change || (value != cdata->chanv[i].value);		cdata->chanv[i].channel = i;		cdata->chanv[i].value = value;	} 	/* notify DSP of enum updates */	if (pm_runtime_active(scomp->dev)) {		int ret = sof_ipc3_set_get_kcontrol_data(scontrol, true, true); 		if (ret < 0) {			dev_err(scomp->dev, "Failed to set enum updates for %s\n",				scontrol->name);			return false;		}	} 	return change;} static int sof_ipc3_bytes_get(struct snd_sof_control *scontrol,			      struct snd_ctl_elem_value *ucontrol){	struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data;	struct snd_soc_component *scomp = scontrol->scomp;	struct sof_abi_hdr *data = cdata->data;	size_t size; 	sof_ipc3_refresh_control(scontrol); 	if (scontrol->max_size > sizeof(ucontrol->value.bytes.data)) {		dev_err_ratelimited(scomp->dev, "data max %zu exceeds ucontrol data array size\n",				    scontrol->max_size);		return -EINVAL;	} 	/* be->max has been verified to be >= sizeof(struct sof_abi_hdr) */	if (data->size > scontrol->max_size - sizeof(*cdata) -				    sizeof(*data)) {		dev_err_ratelimited(scomp->dev,				    "%u bytes of control data is invalid, max is %zu\n",				    data->size,				    scontrol->max_size - sizeof(*cdata) -				    sizeof(*data));		return -EINVAL;	} 	size = data->size + sizeof(*data); 	/* copy back to kcontrol */	memcpy(ucontrol->value.bytes.data, data, size); 	return 0;} static int sof_ipc3_bytes_put(struct snd_sof_control *scontrol,			      struct snd_ctl_elem_value *ucontrol){	struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data;	struct snd_soc_component *scomp = scontrol->scomp;	struct sof_abi_hdr *data = cdata->data;	const struct sof_abi_hdr *new_hdr =		(const struct sof_abi_hdr *)ucontrol->value.bytes.data;	size_t size; 	if (scontrol->max_size > sizeof(ucontrol->value.bytes.data)) {		dev_err_ratelimited(scomp->dev, "data max %zu exceeds ucontrol data array size\n",				    scontrol->max_size);		return -EINVAL;	} 	/* Validate the new data's size, not the old one */	if (new_hdr->size > scontrol->max_size - sizeof(*cdata) -				    sizeof(*new_hdr)) {		dev_err_ratelimited(scomp->dev,				    "data size too big %u bytes max is %zu\n",				    new_hdr->size,				    scontrol->max_size - sizeof(*cdata) -				    sizeof(*new_hdr));		return -EINVAL;	} 	size = new_hdr->size + sizeof(*new_hdr); 	/* copy from kcontrol */	memcpy(data, ucontrol->value.bytes.data, size); 	/* notify DSP of byte control updates */	if (pm_runtime_active(scomp->dev))		return sof_ipc3_set_get_kcontrol_data(scontrol, true, true); 	return 0;} static int sof_ipc3_bytes_ext_put(struct snd_sof_control *scontrol,				  const unsigned int __user *binary_data,				  unsigned int size){	const struct snd_ctl_tlv __user *tlvd = (const struct snd_ctl_tlv __user *)binary_data;	struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data;	struct snd_soc_component *scomp = scontrol->scomp;	struct snd_ctl_tlv header;	int ret = -EINVAL; 	/*	 * The beginning of bytes data contains a header from where	 * the length (as bytes) is needed to know the correct copy	 * length of data from tlvd->tlv.	 */	if (copy_from_user(&header, tlvd, sizeof(struct snd_ctl_tlv)))		return -EFAULT; 	/* make sure TLV info is consistent */	if (header.length + sizeof(struct snd_ctl_tlv) > size) {		dev_err_ratelimited(scomp->dev, "Inconsistent TLV, data %d + header %zu > %d\n",				    header.length, sizeof(struct snd_ctl_tlv), size);		return -EINVAL;	} 	/* be->max is coming from topology */	if (header.length > scontrol->max_size - sizeof(*cdata)) {		dev_err_ratelimited(scomp->dev, "Bytes data size %u exceeds max %zu\n",				    header.length, scontrol->max_size - sizeof(*cdata));		return -EINVAL;	} 	/* Ensure the data is large enough to contain the ABI header */	if (header.length < sizeof(struct sof_abi_hdr)) {		dev_err_ratelimited(scomp->dev,				    "Bytes data size %u less than ABI header %zu\n",				    header.length, sizeof(struct sof_abi_hdr));		return -EINVAL;	} 	/* Check that header id matches the command */	if (header.numid != cdata->cmd) {		dev_err_ratelimited(scomp->dev, "Incorrect command for bytes put %d\n",				    header.numid);		return -EINVAL;	} 	if (!scontrol->old_ipc_control_data) {		/* Create a backup of the current, valid bytes control */		scontrol->old_ipc_control_data = kmemdup(scontrol->ipc_control_data,							 scontrol->max_size, GFP_KERNEL);		if (!scontrol->old_ipc_control_data)			return -ENOMEM;	} 	if (copy_from_user(cdata->data, tlvd->tlv, header.length)) {		ret = -EFAULT;		goto err_restore;	} 	if (cdata->data->magic != SOF_ABI_MAGIC) {		dev_err_ratelimited(scomp->dev, "Wrong ABI magic 0x%08x\n", cdata->data->magic);		goto err_restore;	} 	if (SOF_ABI_VERSION_INCOMPATIBLE(SOF_ABI_VERSION, cdata->data->abi)) {		dev_err_ratelimited(scomp->dev, "Incompatible ABI version 0x%08x\n",				    cdata->data->abi);		goto err_restore;	} 	/* be->max has been verified to be >= sizeof(struct sof_abi_hdr) */	if (cdata->data->size > scontrol->max_size - sizeof(*cdata) - sizeof(struct sof_abi_hdr)) {		dev_err_ratelimited(scomp->dev, "Mismatch in ABI data size (truncated?)\n");		goto err_restore;	} 	/* notify DSP of byte control updates */	if (pm_runtime_active(scomp->dev)) {		/* Actually send the data to the DSP; this is an opportunity to validate the data */		return sof_ipc3_set_get_kcontrol_data(scontrol, true, true);	} 	return 0; err_restore:	/* If we have an issue, we restore the old, valid bytes control data */	if (scontrol->old_ipc_control_data) {		memcpy(cdata, scontrol->old_ipc_control_data, scontrol->max_size);		kfree(scontrol->old_ipc_control_data);		scontrol->old_ipc_control_data = NULL;	}	return ret;} static int _sof_ipc3_bytes_ext_get(struct snd_sof_control *scontrol,				   const unsigned int __user *binary_data,				   unsigned int size, bool from_dsp){	struct snd_ctl_tlv __user *tlvd = (struct snd_ctl_tlv __user *)binary_data;	struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data;	struct snd_soc_component *scomp = scontrol->scomp;	struct snd_ctl_tlv header;	size_t data_size; 	/*	 * Decrement the limit by ext bytes header size to	 * ensure the user space buffer is not exceeded.	 */	if (size < sizeof(struct snd_ctl_tlv))		return -ENOSPC; 	size -= sizeof(struct snd_ctl_tlv); 	/* set the ABI header values */	cdata->data->magic = SOF_ABI_MAGIC;	cdata->data->abi = SOF_ABI_VERSION; 	/* get all the component data from DSP */	if (from_dsp) {		int ret = sof_ipc3_set_get_kcontrol_data(scontrol, false, true); 		if (ret < 0)			return ret;	} 

Lines 1–500 of 772.