]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
ASoC: SOF: ipc4: Support for sending payload along with LARGE_CONFIG_GET
authorPeter Ujfalusi <peter.ujfalusi@linux.intel.com>
Wed, 17 Dec 2025 14:39:43 +0000 (16:39 +0200)
committerMark Brown <broonie@kernel.org>
Wed, 17 Dec 2025 16:35:49 +0000 (16:35 +0000)
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>
Link: https://patch.msgid.link/20251217143945.2667-7-peter.ujfalusi@linux.intel.com
Signed-off-by: Mark Brown <broonie@kernel.org>
sound/soc/sof/ipc4.c

index a4a090e6724a6329e90d6a1dcf5154162ea4b0f4..20d723f48fff0ca17b3842578ef7b9efa7532ca4 100644 (file)
@@ -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 @@ out:
 
        mutex_unlock(&sdev->ipc->tx_mutex);
 
+       kfree(tx_payload_for_get);
+
        return ret;
 }