Commit a062c889 authored by Peter Ujfalusi's avatar Peter Ujfalusi Committed by Mark Brown

ASoC: SOF: ipc4-control: Add support for bytes control get and put

Add support for bytes control by implementing bytes_get/put and
bytes_ext_get/put and blobs with either module init instance or
large config type.

For module init instance type the put will only update the stored
configuration blob and it is going to be taken into use next time the
module is (re-)initialized.

Large config type of blobs are sent to the firmware whenever the DSP is
powered up.
Signed-off-by: default avatarPeter Ujfalusi <peter.ujfalusi@linux.intel.com>
Reviewed-by: default avatarRanjani Sridharan <ranjani.sridharan@linux.intel.com>
Reviewed-by: default avatarJaska Uimonen <jaska.uimonen@linux.intel.com>
Reviewed-by: default avatarKai Vehmanen <kai.vehmanen@linux.intel.com>
Reviewed-by: default avatarPierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
Link: https://lore.kernel.org/r/20230313110344.16644-8-peter.ujfalusi@linux.intel.comSigned-off-by: default avatarMark Brown <broonie@kernel.org>
parent a382082f
...@@ -180,11 +180,237 @@ static int sof_ipc4_volume_get(struct snd_sof_control *scontrol, ...@@ -180,11 +180,237 @@ static int sof_ipc4_volume_get(struct snd_sof_control *scontrol,
return 0; return 0;
} }
static int sof_ipc4_set_get_bytes_data(struct snd_sof_dev *sdev,
struct snd_sof_control *scontrol,
bool set, bool lock)
{
struct sof_ipc4_control_data *cdata = scontrol->ipc_control_data;
struct sof_abi_hdr *data = cdata->data;
struct sof_ipc4_msg *msg = &cdata->msg;
int ret = 0;
/* Send the new data to the firmware only if it is powered up */
if (set && !pm_runtime_active(sdev->dev))
return 0;
msg->extension = SOF_IPC4_MOD_EXT_MSG_PARAM_ID(data->type);
msg->data_ptr = data->data;
msg->data_size = data->size;
ret = sof_ipc4_set_get_kcontrol_data(scontrol, set, lock);
if (ret < 0)
dev_err(sdev->dev, "Failed to %s for %s\n",
set ? "set bytes update" : "get bytes",
scontrol->name);
msg->data_ptr = NULL;
msg->data_size = 0;
return ret;
}
static int sof_ipc4_bytes_put(struct snd_sof_control *scontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct sof_ipc4_control_data *cdata = scontrol->ipc_control_data;
struct snd_soc_component *scomp = scontrol->scomp;
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
struct sof_abi_hdr *data = cdata->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;
}
/* scontrol->max_size has been verified to be >= sizeof(struct sof_abi_hdr) */
if (data->size > scontrol->max_size - sizeof(*data)) {
dev_err_ratelimited(scomp->dev,
"data size too big %u bytes max is %zu\n",
data->size, scontrol->max_size - sizeof(*data));
return -EINVAL;
}
size = data->size + sizeof(*data);
/* copy from kcontrol */
memcpy(data, ucontrol->value.bytes.data, size);
sof_ipc4_set_get_bytes_data(sdev, scontrol, true, true);
return 0;
}
static int sof_ipc4_bytes_get(struct snd_sof_control *scontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct sof_ipc4_control_data *cdata = scontrol->ipc_control_data;
struct snd_soc_component *scomp = scontrol->scomp;
struct sof_abi_hdr *data = cdata->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;
}
if (data->size > scontrol->max_size - sizeof(*data)) {
dev_err_ratelimited(scomp->dev,
"%u bytes of control data is invalid, max is %zu\n",
data->size, scontrol->max_size - 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_ipc4_bytes_ext_put(struct snd_sof_control *scontrol,
const unsigned int __user *binary_data,
unsigned int size)
{
struct snd_ctl_tlv __user *tlvd = (struct snd_ctl_tlv __user *)binary_data;
struct sof_ipc4_control_data *cdata = scontrol->ipc_control_data;
struct snd_soc_component *scomp = scontrol->scomp;
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
struct sof_abi_hdr *data = cdata->data;
struct sof_abi_hdr abi_hdr;
struct snd_ctl_tlv header;
/*
* 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) {
dev_err_ratelimited(scomp->dev,
"Bytes data size %d exceeds max %zu\n",
header.length, scontrol->max_size);
return -EINVAL;
}
/* Verify the ABI header first */
if (copy_from_user(&abi_hdr, tlvd->tlv, sizeof(abi_hdr)))
return -EFAULT;
if (abi_hdr.magic != SOF_IPC4_ABI_MAGIC) {
dev_err_ratelimited(scomp->dev, "Wrong ABI magic 0x%08x\n",
abi_hdr.magic);
return -EINVAL;
}
if (abi_hdr.size > scontrol->max_size - sizeof(abi_hdr)) {
dev_err_ratelimited(scomp->dev,
"%u bytes of control data is invalid, max is %zu\n",
abi_hdr.size, scontrol->max_size - sizeof(abi_hdr));
return -EINVAL;
}
/* Copy the whole binary data which includes the ABI header and the payload */
if (copy_from_user(data, tlvd->tlv, header.length))
return -EFAULT;
sof_ipc4_set_get_bytes_data(sdev, scontrol, true, true);
return 0;
}
static int _sof_ipc4_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_ipc4_control_data *cdata = scontrol->ipc_control_data;
struct snd_soc_component *scomp = scontrol->scomp;
struct sof_abi_hdr *data = cdata->data;
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);
/* get all the component data from DSP */
if (from_dsp) {
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
int ret = sof_ipc4_set_get_bytes_data(sdev, scontrol, false, true);
if (ret < 0)
return ret;
/* Set the ABI magic (if the control is not initialized) */
data->magic = SOF_IPC4_ABI_MAGIC;
}
if (data->size > scontrol->max_size - sizeof(*data)) {
dev_err_ratelimited(scomp->dev,
"%u bytes of control data is invalid, max is %zu\n",
data->size, scontrol->max_size - sizeof(*data));
return -EINVAL;
}
data_size = data->size + sizeof(struct sof_abi_hdr);
/* make sure we don't exceed size provided by user space for data */
if (data_size > size)
return -ENOSPC;
header.numid = scontrol->comp_id;
header.length = data_size;
if (copy_to_user(tlvd, &header, sizeof(struct snd_ctl_tlv)))
return -EFAULT;
if (copy_to_user(tlvd->tlv, data, data_size))
return -EFAULT;
return 0;
}
static int sof_ipc4_bytes_ext_get(struct snd_sof_control *scontrol,
const unsigned int __user *binary_data,
unsigned int size)
{
return _sof_ipc4_bytes_ext_get(scontrol, binary_data, size, false);
}
static int sof_ipc4_bytes_ext_volatile_get(struct snd_sof_control *scontrol,
const unsigned int __user *binary_data,
unsigned int size)
{
return _sof_ipc4_bytes_ext_get(scontrol, binary_data, size, true);
}
/* set up all controls for the widget */ /* set up all controls for the widget */
static int sof_ipc4_widget_kcontrol_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget) static int sof_ipc4_widget_kcontrol_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget)
{ {
struct snd_sof_control *scontrol; struct snd_sof_control *scontrol;
int ret; int ret = 0;
list_for_each_entry(scontrol, &sdev->kcontrol_list, list) { list_for_each_entry(scontrol, &sdev->kcontrol_list, list) {
if (scontrol->comp_id == swidget->comp_id) { if (scontrol->comp_id == swidget->comp_id) {
...@@ -194,11 +420,10 @@ static int sof_ipc4_widget_kcontrol_setup(struct snd_sof_dev *sdev, struct snd_s ...@@ -194,11 +420,10 @@ static int sof_ipc4_widget_kcontrol_setup(struct snd_sof_dev *sdev, struct snd_s
case SND_SOC_TPLG_CTL_VOLSW_XR_SX: case SND_SOC_TPLG_CTL_VOLSW_XR_SX:
ret = sof_ipc4_set_volume_data(sdev, swidget, ret = sof_ipc4_set_volume_data(sdev, swidget,
scontrol, false); scontrol, false);
if (ret < 0) { break;
dev_err(sdev->dev, "kcontrol %d set up failed for widget %s\n", case SND_SOC_TPLG_CTL_BYTES:
scontrol->comp_id, swidget->widget->name); ret = sof_ipc4_set_get_bytes_data(sdev, scontrol,
return ret; true, false);
}
break; break;
default: default:
break; break;
...@@ -206,7 +431,11 @@ static int sof_ipc4_widget_kcontrol_setup(struct snd_sof_dev *sdev, struct snd_s ...@@ -206,7 +431,11 @@ static int sof_ipc4_widget_kcontrol_setup(struct snd_sof_dev *sdev, struct snd_s
} }
} }
return 0; if (ret < 0)
dev_err(sdev->dev, "kcontrol %d set up failed for widget %s\n",
scontrol->comp_id, swidget->widget->name);
return ret;
} }
static int static int
...@@ -234,6 +463,11 @@ sof_ipc4_set_up_volume_table(struct snd_sof_control *scontrol, int tlv[SOF_TLV_I ...@@ -234,6 +463,11 @@ sof_ipc4_set_up_volume_table(struct snd_sof_control *scontrol, int tlv[SOF_TLV_I
const struct sof_ipc_tplg_control_ops tplg_ipc4_control_ops = { const struct sof_ipc_tplg_control_ops tplg_ipc4_control_ops = {
.volume_put = sof_ipc4_volume_put, .volume_put = sof_ipc4_volume_put,
.volume_get = sof_ipc4_volume_get, .volume_get = sof_ipc4_volume_get,
.bytes_put = sof_ipc4_bytes_put,
.bytes_get = sof_ipc4_bytes_get,
.bytes_ext_put = sof_ipc4_bytes_ext_put,
.bytes_ext_get = sof_ipc4_bytes_ext_get,
.bytes_ext_volatile_get = sof_ipc4_bytes_ext_volatile_get,
.widget_kcontrol_setup = sof_ipc4_widget_kcontrol_setup, .widget_kcontrol_setup = sof_ipc4_widget_kcontrol_setup,
.set_up_volume_table = sof_ipc4_set_up_volume_table, .set_up_volume_table = sof_ipc4_set_up_volume_table,
}; };
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment