Hi,
Changes since v1: - correct SHAs for fixes tags - add Cc stable tag
We support bytes control type for set and get, but these are module specific controls and there is no way to handle notifications from them in a generic way. Each control have module specific param_id and this param_id is only valid in the module's scope, other modules might use the same id for different functions for example.
This series will add a new generic control type, similar to the existing ones for ENUM and SWITCH, which can be used to create bytes controls which can send notifications from firmware on change.
The new param_id is 202 and the sof_ipc4_control_msg_payload is updated to describe bytes payload also.
On set, the payload must include the sof_ipc4_control_msg_payload struct with the control's ID and data size, followed by the data.
On get, the kernel needs to send the sof_ipc4_control_msg_payload struct along with the LARGE_CONFIG_GET message as payload with the ID of the control that needs to be retrieved. The raw data is received back without additional header.
A notification might contain data, in this case the num_elems reflects the size in bytes, or without data. If no data is received then the control is marked as dirty and on read the kernel will refresh the data from firmware.
The series includes mandatory fixes for existing code and adds support for sending payload with LARGE_CONFIG_GET when the param_id is either generic ENUM, SWITCH or BYTES control.
Regards, Peter --- Peter Ujfalusi (8): ASoC: SOF: ipc4-control: If there is no data do not send bytes update ASoC: SOF: ipc4-topology: Correct the allocation size for bytes controls ASoC: SOF: ipc4-control: Use the correct size for scontrol->ipc_control_data ASoC: SOF: ipc4-control: Keep the payload size up to date ASoC: SOF: ipc4-topology: Set initial param_id for bytes control type ASoC: SOF: ipc4: Support for sending payload along with LARGE_CONFIG_GET ASoC: SOF: ipc4: Add definition for generic bytes control ASoC: SOF: ipc4-control: Add support for generic bytes control
sound/soc/sof/ipc4-control.c | 197 ++++++++++++++++++++++++++++++---- sound/soc/sof/ipc4-topology.c | 36 +++++-- sound/soc/sof/ipc4-topology.h | 9 +- sound/soc/sof/ipc4.c | 45 +++++++- 4 files changed, 252 insertions(+), 35 deletions(-)
When the bytes control have no data (payload) then there is no need to send an IPC message as there is nothing to send.
Fixes: a062c8899fed ("ASoC: SOF: ipc4-control: Add support for bytes control get and put") Cc: stable@vger.kernel.org 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 --- sound/soc/sof/ipc4-control.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-)
diff --git a/sound/soc/sof/ipc4-control.c b/sound/soc/sof/ipc4-control.c index 976a4794d610..0a05f66ec7d9 100644 --- a/sound/soc/sof/ipc4-control.c +++ b/sound/soc/sof/ipc4-control.c @@ -412,8 +412,16 @@ static int sof_ipc4_set_get_bytes_data(struct snd_sof_dev *sdev, 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; + if (set) { + if (!pm_runtime_active(sdev->dev)) + return 0; + + if (!data->size) { + dev_dbg(sdev->dev, "%s: No data to be sent.\n", + scontrol->name); + return 0; + } + }
msg->extension = SOF_IPC4_MOD_EXT_MSG_PARAM_ID(data->type);
The size of the data behind of scontrol->ipc_control_data for bytes controls is: [1] sizeof(struct sof_ipc4_control_data) + // kernel only struct [2] sizeof(struct sof_abi_hdr)) + payload
The max_size specifies the size of [2] and it is coming from topology.
Change the function to take this into account and allocate adequate amount of memory behind scontrol->ipc_control_data.
With the change we will allocate [1] amount more memory to be able to hold the full size of data.
Fixes: a062c8899fed ("ASoC: SOF: ipc4-topology: add byte kcontrol support") Cc: stable@vger.kernel.org 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 --- sound/soc/sof/ipc4-topology.c | 35 +++++++++++++++++++++++++++-------- 1 file changed, 27 insertions(+), 8 deletions(-)
diff --git a/sound/soc/sof/ipc4-topology.c b/sound/soc/sof/ipc4-topology.c index 221e9d4052b8..4272d84679ac 100644 --- a/sound/soc/sof/ipc4-topology.c +++ b/sound/soc/sof/ipc4-topology.c @@ -2855,22 +2855,41 @@ static int sof_ipc4_control_load_bytes(struct snd_sof_dev *sdev, struct snd_sof_ struct sof_ipc4_msg *msg; int ret;
- if (scontrol->max_size < (sizeof(*control_data) + sizeof(struct sof_abi_hdr))) { - dev_err(sdev->dev, "insufficient size for a bytes control %s: %zu.\n", + /* + * The max_size is coming from topology and indicates the maximum size + * of sof_abi_hdr plus the payload, which excludes the local only + * 'struct sof_ipc4_control_data' + */ + if (scontrol->max_size < sizeof(struct sof_abi_hdr)) { + dev_err(sdev->dev, + "insufficient maximum size for a bytes control %s: %zu.\n", scontrol->name, scontrol->max_size); return -EINVAL; }
- if (scontrol->priv_size > scontrol->max_size - sizeof(*control_data)) { - dev_err(sdev->dev, "scontrol %s bytes data size %zu exceeds max %zu.\n", - scontrol->name, scontrol->priv_size, - scontrol->max_size - sizeof(*control_data)); + if (scontrol->priv_size > scontrol->max_size) { + dev_err(sdev->dev, + "bytes control %s initial data size %zu exceeds max %zu.\n", + scontrol->name, scontrol->priv_size, scontrol->max_size); + return -EINVAL; + } + + if (scontrol->priv_size < sizeof(struct sof_abi_hdr)) { + dev_err(sdev->dev, + "bytes control %s initial data size %zu is insufficient.\n", + scontrol->name, scontrol->priv_size); return -EINVAL; }
- scontrol->size = sizeof(struct sof_ipc4_control_data) + scontrol->priv_size; + /* + * The used size behind the cdata pointer, which can be smaller than + * the maximum size + */ + scontrol->size = sizeof(*control_data) + scontrol->priv_size;
- scontrol->ipc_control_data = kzalloc(scontrol->max_size, GFP_KERNEL); + /* Allocate the cdata: local struct size + maximum payload size */ + scontrol->ipc_control_data = kzalloc(sizeof(*control_data) + scontrol->max_size, + GFP_KERNEL); if (!scontrol->ipc_control_data) return -ENOMEM;
The size of the data behind scontrol->ipc_control_data is stored in scontrol->size, use this when copying data for backup/restore.
Fixes: db38d86d0c54 ("ASoC: sof: Improve sof_ipc4_bytes_ext_put function") Cc: stable@vger.kernel.org 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 --- sound/soc/sof/ipc4-control.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/sound/soc/sof/ipc4-control.c b/sound/soc/sof/ipc4-control.c index 0a05f66ec7d9..80111672c179 100644 --- a/sound/soc/sof/ipc4-control.c +++ b/sound/soc/sof/ipc4-control.c @@ -66,7 +66,7 @@ static int sof_ipc4_set_get_kcontrol_data(struct snd_sof_control *scontrol, * configuration */ memcpy(scontrol->ipc_control_data, scontrol->old_ipc_control_data, - scontrol->max_size); + scontrol->size); kfree(scontrol->old_ipc_control_data); scontrol->old_ipc_control_data = NULL; /* Send the last known good configuration to firmware */ @@ -567,7 +567,7 @@ static int sof_ipc4_bytes_ext_put(struct snd_sof_control *scontrol, 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); + scontrol->size, GFP_KERNEL); if (!scontrol->old_ipc_control_data) return -ENOMEM; } @@ -575,7 +575,7 @@ static int sof_ipc4_bytes_ext_put(struct snd_sof_control *scontrol, /* Copy the whole binary data which includes the ABI header and the payload */ if (copy_from_user(data, tlvd->tlv, header.length)) { memcpy(scontrol->ipc_control_data, scontrol->old_ipc_control_data, - scontrol->max_size); + scontrol->size); kfree(scontrol->old_ipc_control_data); scontrol->old_ipc_control_data = NULL; return -EFAULT;
When the bytes data is read from the firmware, the size of the payload can be different than what it was previously. For example when the topology did not contained payload data at all for the control, the data size was 0. For get operation allow maximum size of payload to be read and then update the sizes according to the completed message.
Similarly, keep the size in sync when updating the data in firmware.
With the change we will be able to read data from firmware for bytes controls which did not had initial payload defined in topology.
Fixes: a062c8899fed ("ASoC: SOF: ipc4-control: Add support for bytes control get and put") Cc: stable@vger.kernel.org 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 --- sound/soc/sof/ipc4-control.c | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-)
diff --git a/sound/soc/sof/ipc4-control.c b/sound/soc/sof/ipc4-control.c index 80111672c179..453ed1643b89 100644 --- a/sound/soc/sof/ipc4-control.c +++ b/sound/soc/sof/ipc4-control.c @@ -426,13 +426,21 @@ static int sof_ipc4_set_get_bytes_data(struct snd_sof_dev *sdev, msg->extension = SOF_IPC4_MOD_EXT_MSG_PARAM_ID(data->type);
msg->data_ptr = data->data; - msg->data_size = data->size; + if (set) + msg->data_size = data->size; + else + msg->data_size = scontrol->max_size - sizeof(*data);
ret = sof_ipc4_set_get_kcontrol_data(scontrol, set, lock); - if (ret < 0) + if (ret < 0) { dev_err(sdev->dev, "Failed to %s for %s\n", set ? "set bytes update" : "get bytes", scontrol->name); + } else if (!set) { + /* Update the sizes according to the received payload data */ + data->size = msg->data_size; + scontrol->size = sizeof(*cdata) + sizeof(*data) + data->size; + }
msg->data_ptr = NULL; msg->data_size = 0; @@ -448,6 +456,7 @@ static int sof_ipc4_bytes_put(struct snd_sof_control *scontrol, struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); struct sof_abi_hdr *data = cdata->data; size_t size; + int ret;
if (scontrol->max_size > sizeof(ucontrol->value.bytes.data)) { dev_err_ratelimited(scomp->dev, @@ -469,9 +478,12 @@ static int sof_ipc4_bytes_put(struct snd_sof_control *scontrol, /* copy from kcontrol */ memcpy(data, ucontrol->value.bytes.data, size);
- sof_ipc4_set_get_bytes_data(sdev, scontrol, true, true); + ret = sof_ipc4_set_get_bytes_data(sdev, scontrol, true, true); + if (!ret) + /* Update the cdata size */ + scontrol->size = sizeof(*cdata) + size;
- return 0; + return ret; }
static int sof_ipc4_bytes_get(struct snd_sof_control *scontrol, @@ -581,6 +593,9 @@ static int sof_ipc4_bytes_ext_put(struct snd_sof_control *scontrol, return -EFAULT; }
+ /* Update the cdata size */ + scontrol->size = sizeof(*cdata) + header.length; + return sof_ipc4_set_get_bytes_data(sdev, scontrol, true, true); }
Set the param_id in extension based on the information we got from the topology. If the payload did not present then the param_id will remain 0.
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 --- sound/soc/sof/ipc4-topology.c | 1 + 1 file changed, 1 insertion(+)
diff --git a/sound/soc/sof/ipc4-topology.c b/sound/soc/sof/ipc4-topology.c index 4272d84679ac..d64e498c6985 100644 --- a/sound/soc/sof/ipc4-topology.c +++ b/sound/soc/sof/ipc4-topology.c @@ -2924,6 +2924,7 @@ static int sof_ipc4_control_load_bytes(struct snd_sof_dev *sdev, struct snd_sof_ msg->primary = SOF_IPC4_MSG_TYPE_SET(SOF_IPC4_MOD_LARGE_CONFIG_SET); msg->primary |= SOF_IPC4_MSG_DIR(SOF_IPC4_MSG_REQUEST); msg->primary |= SOF_IPC4_MSG_TARGET(SOF_IPC4_MODULE_MSG); + msg->extension = SOF_IPC4_MOD_EXT_MSG_PARAM_ID(control_data->data->type);
return 0;
There are message types when we would need to send a payload along with the LARGE_CONFIG_GET message to provide information to the firmware on what data is requested. Such cases are the ALSA Kcontrol related messages when the high level param_id tells only the type of the control, but the ID/index of the exact control is specified in the payload area.
The caller must place the payload for TX before calling the set_get_data() and this payload will be sent alongside with the message to the firmware.
The data area will be overwritten by the received data from firmware.
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 --- sound/soc/sof/ipc4.c | 44 ++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 42 insertions(+), 2 deletions(-)
diff --git a/sound/soc/sof/ipc4.c b/sound/soc/sof/ipc4.c index a4a090e6724a..20d723f48fff 100644 --- a/sound/soc/sof/ipc4.c +++ b/sound/soc/sof/ipc4.c @@ -15,6 +15,7 @@ #include "sof-audio.h" #include "ipc4-fw-reg.h" #include "ipc4-priv.h" +#include "ipc4-topology.h" #include "ipc4-telemetry.h" #include "ops.h"
@@ -433,6 +434,23 @@ static int sof_ipc4_tx_msg(struct snd_sof_dev *sdev, void *msg_data, size_t msg_ return ret; }
+static bool sof_ipc4_tx_payload_for_get_data(struct sof_ipc4_msg *tx) +{ + /* + * Messages that require TX payload with LARGE_CONFIG_GET. + * The TX payload is placed into the IPC message data section by caller, + * which needs to be copied to temporary buffer since the received data + * will overwrite it. + */ + switch (tx->extension & SOF_IPC4_MOD_EXT_MSG_PARAM_ID_MASK) { + case SOF_IPC4_MOD_EXT_MSG_PARAM_ID(SOF_IPC4_SWITCH_CONTROL_PARAM_ID): + case SOF_IPC4_MOD_EXT_MSG_PARAM_ID(SOF_IPC4_ENUM_CONTROL_PARAM_ID): + return true; + default: + return false; + } +} + static int sof_ipc4_set_get_data(struct snd_sof_dev *sdev, void *data, size_t payload_bytes, bool set) { @@ -444,6 +462,8 @@ static int sof_ipc4_set_get_data(struct snd_sof_dev *sdev, void *data, struct sof_ipc4_msg tx = {{ 0 }}; struct sof_ipc4_msg rx = {{ 0 }}; size_t remaining = payload_bytes; + void *tx_payload_for_get = NULL; + size_t tx_data_size = 0; size_t offset = 0; size_t chunk_size; int ret; @@ -469,10 +489,20 @@ static int sof_ipc4_set_get_data(struct snd_sof_dev *sdev, void *data,
tx.extension |= SOF_IPC4_MOD_EXT_MSG_FIRST_BLOCK(1);
+ if (sof_ipc4_tx_payload_for_get_data(&tx)) { + tx_data_size = min(ipc4_msg->data_size, payload_limit); + tx_payload_for_get = kmemdup(ipc4_msg->data_ptr, tx_data_size, + GFP_KERNEL); + if (!tx_payload_for_get) + return -ENOMEM; + } + /* ensure the DSP is in D0i0 before sending IPC */ ret = snd_sof_dsp_set_power_state(sdev, &target_state); - if (ret < 0) + if (ret < 0) { + kfree(tx_payload_for_get); return ret; + }
/* Serialise IPC TX */ mutex_lock(&sdev->ipc->tx_mutex); @@ -506,7 +536,15 @@ static int sof_ipc4_set_get_data(struct snd_sof_dev *sdev, void *data, rx.data_size = chunk_size; rx.data_ptr = ipc4_msg->data_ptr + offset;
- tx_size = 0; + if (tx_payload_for_get) { + tx_size = tx_data_size; + tx.data_size = tx_size; + tx.data_ptr = tx_payload_for_get; + } else { + tx_size = 0; + tx.data_size = 0; + tx.data_ptr = NULL; + } rx_size = chunk_size; }
@@ -553,6 +591,8 @@ static int sof_ipc4_set_get_data(struct snd_sof_dev *sdev, void *data,
mutex_unlock(&sdev->ipc->tx_mutex);
+ kfree(tx_payload_for_get); + return ret; }
Currently IPC4 only supports module specific custom bytes controls, where each control's param_id is module specific. These bytes controls cannot be handled in a generic way, there is no clean way to support for example notifications from firmware when their data has been changed.
Add definition for generic bytes control which should be handled in a similar way as the enum/switch controls.
The generic param id for BYTES is selected to be 202
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 --- sound/soc/sof/ipc4-topology.h | 9 +++++++-- sound/soc/sof/ipc4.c | 1 + 2 files changed, 8 insertions(+), 2 deletions(-)
diff --git a/sound/soc/sof/ipc4-topology.h b/sound/soc/sof/ipc4-topology.h index 191b51d97993..9a028a59c553 100644 --- a/sound/soc/sof/ipc4-topology.h +++ b/sound/soc/sof/ipc4-topology.h @@ -368,19 +368,24 @@ struct sof_ipc4_control_data {
#define SOF_IPC4_SWITCH_CONTROL_PARAM_ID 200 #define SOF_IPC4_ENUM_CONTROL_PARAM_ID 201 +#define SOF_IPC4_BYTES_CONTROL_PARAM_ID 202
/** * struct sof_ipc4_control_msg_payload - IPC payload for kcontrol parameters * @id: unique id of the control - * @num_elems: Number of elements in the chanv array + * @num_elems: Number of elements in the chanv array or number of bytes in data * @reserved: reserved for future use, must be set to 0 * @chanv: channel ID and value array + * @data: binary payload */ struct sof_ipc4_control_msg_payload { uint16_t id; uint16_t num_elems; uint32_t reserved[4]; - DECLARE_FLEX_ARRAY(struct sof_ipc4_ctrl_value_chan, chanv); + union { + DECLARE_FLEX_ARRAY(struct sof_ipc4_ctrl_value_chan, chanv); + DECLARE_FLEX_ARRAY(uint8_t, data); + }; } __packed;
/** diff --git a/sound/soc/sof/ipc4.c b/sound/soc/sof/ipc4.c index 20d723f48fff..2be8d8ce487a 100644 --- a/sound/soc/sof/ipc4.c +++ b/sound/soc/sof/ipc4.c @@ -445,6 +445,7 @@ static bool sof_ipc4_tx_payload_for_get_data(struct sof_ipc4_msg *tx) switch (tx->extension & SOF_IPC4_MOD_EXT_MSG_PARAM_ID_MASK) { case SOF_IPC4_MOD_EXT_MSG_PARAM_ID(SOF_IPC4_SWITCH_CONTROL_PARAM_ID): case SOF_IPC4_MOD_EXT_MSG_PARAM_ID(SOF_IPC4_ENUM_CONTROL_PARAM_ID): + case SOF_IPC4_MOD_EXT_MSG_PARAM_ID(SOF_IPC4_BYTES_CONTROL_PARAM_ID): return true; default: return false;
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 --- sound/soc/sof/ipc4-control.c | 156 +++++++++++++++++++++++++++++++---- 1 file changed, 142 insertions(+), 14 deletions(-)
diff --git a/sound/soc/sof/ipc4-control.c b/sound/soc/sof/ipc4-control.c index 453ed1643b89..0500b690f9a3 100644 --- a/sound/soc/sof/ipc4-control.c +++ b/sound/soc/sof/ipc4-control.c @@ -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; + + 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; } - - cdata->chanv[channel].value = msg_data->chanv[i].value; } } else { /*
linux-stable-mirror@lists.linaro.org