mirror of
https://github.com/torvalds/linux.git
synced 2026-03-08 04:44:45 +01:00
ASoC: SOF: ipc4-control: Add support for generic bytes control
The generic byte control can be used in cases when the bytes data can be changed by the firmware and it sends a notification about the change, similarly to the enum and switch controls. The generic control support is needed as from the param_id itself it is not possible to know which control has changed. The needed information is only available via generic control change notification. Generic bytes controls use param_id 202 and their change notification can contain payload with the change embedded or just the header message as notification. Signed-off-by: Peter Ujfalusi <peter.ujfalusi@linux.intel.com> Reviewed-by: Seppo Ingalsuo <seppo.ingalsuo@linux.intel.com> Reviewed-by: Ranjani Sridharan <ranjani.sridharan@linux.intel.com> Reviewed-by: Bard Liao <yung-chuan.liao@linux.intel.com> Reviewed-by: Kai Vehmanen <kai.vehmanen@linux.intel.com> Link: https://patch.msgid.link/20251217143945.2667-9-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown <broonie@kernel.org>
This commit is contained in:
parent
7fd8c216c4
commit
2a28b5240f
1 changed files with 141 additions and 13 deletions
|
|
@ -284,6 +284,105 @@ static void sof_ipc4_refresh_generic_control(struct snd_sof_control *scontrol)
|
|||
kfree(data);
|
||||
}
|
||||
|
||||
static int
|
||||
sof_ipc4_set_bytes_control_data(struct snd_sof_control *scontrol, bool lock)
|
||||
{
|
||||
struct sof_ipc4_control_data *cdata = scontrol->ipc_control_data;
|
||||
struct snd_soc_component *scomp = scontrol->scomp;
|
||||
struct sof_ipc4_control_msg_payload *msg_data;
|
||||
struct sof_abi_hdr *data = cdata->data;
|
||||
struct sof_ipc4_msg *msg = &cdata->msg;
|
||||
size_t data_size;
|
||||
int ret;
|
||||
|
||||
data_size = struct_size(msg_data, data, data->size);
|
||||
msg_data = kzalloc(data_size, GFP_KERNEL);
|
||||
if (!msg_data)
|
||||
return -ENOMEM;
|
||||
|
||||
msg_data->id = cdata->index;
|
||||
msg_data->num_elems = data->size;
|
||||
memcpy(msg_data->data, data->data, data->size);
|
||||
|
||||
msg->extension = SOF_IPC4_MOD_EXT_MSG_PARAM_ID(data->type);
|
||||
|
||||
msg->data_ptr = msg_data;
|
||||
msg->data_size = data_size;
|
||||
|
||||
ret = sof_ipc4_set_get_kcontrol_data(scontrol, true, lock);
|
||||
msg->data_ptr = NULL;
|
||||
msg->data_size = 0;
|
||||
if (ret < 0)
|
||||
dev_err(scomp->dev, "%s: Failed to set control update for %s\n",
|
||||
__func__, scontrol->name);
|
||||
|
||||
kfree(msg_data);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
sof_ipc4_refresh_bytes_control(struct snd_sof_control *scontrol, bool lock)
|
||||
{
|
||||
struct sof_ipc4_control_data *cdata = scontrol->ipc_control_data;
|
||||
struct snd_soc_component *scomp = scontrol->scomp;
|
||||
struct sof_ipc4_control_msg_payload *msg_data;
|
||||
struct sof_abi_hdr *data = cdata->data;
|
||||
struct sof_ipc4_msg *msg = &cdata->msg;
|
||||
size_t data_size;
|
||||
int ret = 0;
|
||||
|
||||
if (!scontrol->comp_data_dirty)
|
||||
return 0;
|
||||
|
||||
if (!pm_runtime_active(scomp->dev))
|
||||
return 0;
|
||||
|
||||
data_size = scontrol->max_size - sizeof(*data);
|
||||
if (data_size < sizeof(*msg_data))
|
||||
data_size = sizeof(*msg_data);
|
||||
|
||||
msg_data = kzalloc(data_size, GFP_KERNEL);
|
||||
if (!msg_data)
|
||||
return -ENOMEM;
|
||||
|
||||
msg->extension = SOF_IPC4_MOD_EXT_MSG_PARAM_ID(data->type);
|
||||
|
||||
msg_data->id = cdata->index;
|
||||
msg_data->num_elems = 0; /* ignored for bytes */
|
||||
|
||||
msg->data_ptr = msg_data;
|
||||
msg->data_size = data_size;
|
||||
|
||||
scontrol->comp_data_dirty = false;
|
||||
ret = sof_ipc4_set_get_kcontrol_data(scontrol, false, lock);
|
||||
if (!ret) {
|
||||
if (msg->data_size > scontrol->max_size - sizeof(*data)) {
|
||||
dev_err(scomp->dev,
|
||||
"%s: no space for data in %s (%zu, %zu)\n",
|
||||
__func__, scontrol->name, msg->data_size,
|
||||
scontrol->max_size - sizeof(*data));
|
||||
goto out;
|
||||
}
|
||||
|
||||
data->size = msg->data_size;
|
||||
scontrol->size = sizeof(*cdata) + sizeof(*data) + data->size;
|
||||
memcpy(data->data, msg->data_ptr, data->size);
|
||||
} else {
|
||||
dev_err(scomp->dev, "Failed to read control data for %s\n",
|
||||
scontrol->name);
|
||||
scontrol->comp_data_dirty = true;
|
||||
}
|
||||
|
||||
out:
|
||||
msg->data_ptr = NULL;
|
||||
msg->data_size = 0;
|
||||
|
||||
kfree(msg_data);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static bool sof_ipc4_switch_put(struct snd_sof_control *scontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
|
|
@ -423,6 +522,13 @@ static int sof_ipc4_set_get_bytes_data(struct snd_sof_dev *sdev,
|
|||
}
|
||||
}
|
||||
|
||||
if (data->type == SOF_IPC4_BYTES_CONTROL_PARAM_ID) {
|
||||
if (set)
|
||||
return sof_ipc4_set_bytes_control_data(scontrol, lock);
|
||||
else
|
||||
return sof_ipc4_refresh_bytes_control(scontrol, lock);
|
||||
}
|
||||
|
||||
msg->extension = SOF_IPC4_MOD_EXT_MSG_PARAM_ID(data->type);
|
||||
|
||||
msg->data_ptr = data->data;
|
||||
|
|
@ -507,6 +613,8 @@ static int sof_ipc4_bytes_get(struct snd_sof_control *scontrol,
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
sof_ipc4_refresh_bytes_control(scontrol, true);
|
||||
|
||||
size = data->size + sizeof(*data);
|
||||
|
||||
/* copy back to kcontrol */
|
||||
|
|
@ -661,6 +769,8 @@ static int sof_ipc4_bytes_ext_get(struct snd_sof_control *scontrol,
|
|||
const unsigned int __user *binary_data,
|
||||
unsigned int size)
|
||||
{
|
||||
sof_ipc4_refresh_bytes_control(scontrol, true);
|
||||
|
||||
return _sof_ipc4_bytes_ext_get(scontrol, binary_data, size, false);
|
||||
}
|
||||
|
||||
|
|
@ -714,6 +824,9 @@ static void sof_ipc4_control_update(struct snd_sof_dev *sdev, void *ipc_message)
|
|||
case SOF_IPC4_ENUM_CONTROL_PARAM_ID:
|
||||
type = SND_SOC_TPLG_TYPE_ENUM;
|
||||
break;
|
||||
case SOF_IPC4_BYTES_CONTROL_PARAM_ID:
|
||||
type = SND_SOC_TPLG_TYPE_BYTES;
|
||||
break;
|
||||
default:
|
||||
dev_err(sdev->dev,
|
||||
"%s: Invalid control type for module %u.%u: %u\n",
|
||||
|
|
@ -764,23 +877,38 @@ static void sof_ipc4_control_update(struct snd_sof_dev *sdev, void *ipc_message)
|
|||
* The message includes the updated value/data, update the
|
||||
* control's local cache using the received notification
|
||||
*/
|
||||
for (i = 0; i < msg_data->num_elems; i++) {
|
||||
u32 channel = msg_data->chanv[i].channel;
|
||||
if (type == SND_SOC_TPLG_TYPE_BYTES) {
|
||||
struct sof_abi_hdr *data = cdata->data;
|
||||
|
||||
if (channel >= scontrol->num_channels) {
|
||||
if (msg_data->num_elems > scontrol->max_size - sizeof(*data)) {
|
||||
dev_warn(sdev->dev,
|
||||
"Invalid channel index for %s: %u\n",
|
||||
scontrol->name, i);
|
||||
|
||||
/*
|
||||
* Mark the scontrol as dirty to force a refresh
|
||||
* on next read
|
||||
*/
|
||||
scontrol->comp_data_dirty = true;
|
||||
break;
|
||||
"%s: no space for data in %s (%u, %zu)\n",
|
||||
__func__, scontrol->name, msg_data->num_elems,
|
||||
scontrol->max_size - sizeof(*data));
|
||||
} else {
|
||||
memcpy(data->data, msg_data->data, msg_data->num_elems);
|
||||
data->size = msg_data->num_elems;
|
||||
scontrol->size = sizeof(*cdata) + sizeof(*data) + data->size;
|
||||
}
|
||||
} else {
|
||||
for (i = 0; i < msg_data->num_elems; i++) {
|
||||
u32 channel = msg_data->chanv[i].channel;
|
||||
|
||||
cdata->chanv[channel].value = msg_data->chanv[i].value;
|
||||
if (channel >= scontrol->num_channels) {
|
||||
dev_warn(sdev->dev,
|
||||
"Invalid channel index for %s: %u\n",
|
||||
scontrol->name, i);
|
||||
|
||||
/*
|
||||
* Mark the scontrol as dirty to force a refresh
|
||||
* on next read
|
||||
*/
|
||||
scontrol->comp_data_dirty = true;
|
||||
break;
|
||||
}
|
||||
|
||||
cdata->chanv[channel].value = msg_data->chanv[i].value;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue