diff mbox series

[07/17] ASoC: Intel: avs: Add module management requests

Message ID 20220207122108.3780926-8-cezary.rojewski@intel.com (mailing list archive)
State Superseded
Headers show
Series ASoC: Intel: AVS - Audio DSP for cAVS | expand

Commit Message

Cezary Rojewski Feb. 7, 2022, 12:20 p.m. UTC
Firmware modules implement processing algorithms. Their lifecycle,
similarly to pipelines is being controlled by IPCs: initialization,
deletion and (un)binding.

Modules can be configured at runtime - runtime parameters. This is done
with help of LARGE_CONFIG IPCs: getter and setter.

Signed-off-by: Amadeusz Sławiński <amadeuszx.slawinski@linux.intel.com>
Signed-off-by: Cezary Rojewski <cezary.rojewski@intel.com>
---
 sound/soc/intel/avs/ipc.c      |   8 +-
 sound/soc/intel/avs/messages.c | 208 +++++++++++++++++++++++++++++++++
 sound/soc/intel/avs/messages.h |  53 +++++++++
 3 files changed, 268 insertions(+), 1 deletion(-)

Comments

Pierre-Louis Bossart Feb. 25, 2022, 1:27 a.m. UTC | #1
> +int avs_ipc_init_instance(struct avs_dev *adev, u16 module_id, u8 instance_id,
> +			  u8 ppl_id, u8 core_id, u8 domain,

you should explain the relationship between ppl_id and core_id. It seems
that in the same pipeline different modules instances can be pegged to
different cores, which isn't very intuitive given the previous
explanation that a pipeline is a scheduling unit.

The domain as a u8 is not very clear either, I was under the impression
there were only two domains (LL and EDF)?

> +			  void *param, u32 param_size)
> +{
> +	union avs_module_msg msg = AVS_MODULE_REQUEST(INIT_INSTANCE);
> +	struct avs_ipc_msg request;
> +	int ret;
> +
> +	msg.module_id = module_id;
> +	msg.instance_id = instance_id;
> +	/* firmware expects size provided in dwords */
> +	msg.ext.init_instance.param_block_size =
> +			DIV_ROUND_UP(param_size, sizeof(u32));
> +	msg.ext.init_instance.ppl_instance_id = ppl_id;
> +	msg.ext.init_instance.core_id = core_id;
> +	msg.ext.init_instance.proc_domain = domain;
> +
> +	request.header = msg.val;
> +	request.data = param;
> +	request.size = param_size;

isn't there a need to check if the module can be initialized? there's
got to be some dependency on pipeline state?

> +
> +	ret = avs_dsp_send_msg(adev, &request, NULL);
> +	if (ret)
> +		avs_ipc_err(adev, &request, "init instance", ret);
> +
> +	return ret;
> +}
> +
> +int avs_ipc_delete_instance(struct avs_dev *adev, u16 module_id, u8 instance_id)
> +{
> +	union avs_module_msg msg = AVS_MODULE_REQUEST(DELETE_INSTANCE);
> +	struct avs_ipc_msg request = {0};
> +	int ret;
> +
> +	msg.module_id = module_id;
> +	msg.instance_id = instance_id;
> +	request.header = msg.val;
> +
> +	ret = avs_dsp_send_msg(adev, &request, NULL);
> +	if (ret)
> +		avs_ipc_err(adev, &request, "delete instance", ret);
> +
> +	return ret;

same here, can this be used in any pipeline state?

> +}
> +
> +int avs_ipc_bind(struct avs_dev *adev, u16 module_id, u8 instance_id,
> +		 u16 dst_module_id, u8 dst_instance_id,
> +		 u8 dst_queue, u8 src_queue)

what does a queue represent?

> +{
> +	union avs_module_msg msg = AVS_MODULE_REQUEST(BIND);
> +	struct avs_ipc_msg request = {0};
> +	int ret;
> +
> +	msg.module_id = module_id;
> +	msg.instance_id = instance_id;
> +	msg.ext.bind_unbind.dst_module_id = dst_module_id;
> +	msg.ext.bind_unbind.dst_instance_id = dst_instance_id;
> +	msg.ext.bind_unbind.dst_queue = dst_queue;
> +	msg.ext.bind_unbind.src_queue = src_queue;
> +	request.header = msg.val;
> +
> +	ret = avs_dsp_send_msg(adev, &request, NULL);
> +	if (ret)
> +		avs_ipc_err(adev, &request, "bind modules", ret);
> +
> +	return ret;
> +}
> +
> +int avs_ipc_unbind(struct avs_dev *adev, u16 module_id, u8 instance_id,
> +		   u16 dst_module_id, u8 dst_instance_id,
> +		   u8 dst_queue, u8 src_queue)
> +{
> +	union avs_module_msg msg = AVS_MODULE_REQUEST(UNBIND);
> +	struct avs_ipc_msg request = {0};
> +	int ret;
> +
> +	msg.module_id = module_id;
> +	msg.instance_id = instance_id;
> +	msg.ext.bind_unbind.dst_module_id = dst_module_id;
> +	msg.ext.bind_unbind.dst_instance_id = dst_instance_id;
> +	msg.ext.bind_unbind.dst_queue = dst_queue;
> +	msg.ext.bind_unbind.src_queue = src_queue;
> +	request.header = msg.val;
> +
> +	ret = avs_dsp_send_msg(adev, &request, NULL);
> +	if (ret)
> +		avs_ipc_err(adev, &request, "unbind modules", ret);
> +
> +	return ret;

can this be merged with the bind in a helper, the code looks
quasi-identical with just two lines different.

> +}
Cezary Rojewski Feb. 25, 2022, 6:50 p.m. UTC | #2
On 2022-02-25 2:27 AM, Pierre-Louis Bossart wrote:
>> +int avs_ipc_init_instance(struct avs_dev *adev, u16 module_id, u8 instance_id,
>> +			  u8 ppl_id, u8 core_id, u8 domain,
> 
> you should explain the relationship between ppl_id and core_id. It seems
> that in the same pipeline different modules instances can be pegged to
> different cores, which isn't very intuitive given the previous
> explanation that a pipeline is a scheduling unit.
> 
> The domain as a u8 is not very clear either, I was under the impression
> there were only two domains (LL and EDF)?


Hmm.. such explanations are supposed to be part of HW or FW 
specifications. I don't believe kernel is a place for that. Fields found 
here are needed to provide all the necessary information firmware 
expects when requesting INIT_INSTANCE. What's possible and how's 
everything handled internally is for firmware to decide and explain. 
There are no if-statements in the driver's code that force 
ppl_id/core_id relation so I don't see why reader would get an 
impression there is some dependency. What's in the topology gets routed 
to firmware with help of above function.

Just to confirm: yes, you can have multiple cores engaged in servicing 
modules found in single pipelines.

In regard to field name/sizes: again, these match firmware equivalents 
1:1 so it's easy to switch back and forth.

>> +			  void *param, u32 param_size)
>> +{
>> +	union avs_module_msg msg = AVS_MODULE_REQUEST(INIT_INSTANCE);
>> +	struct avs_ipc_msg request;
>> +	int ret;
>> +
>> +	msg.module_id = module_id;
>> +	msg.instance_id = instance_id;
>> +	/* firmware expects size provided in dwords */
>> +	msg.ext.init_instance.param_block_size =
>> +			DIV_ROUND_UP(param_size, sizeof(u32));
>> +	msg.ext.init_instance.ppl_instance_id = ppl_id;
>> +	msg.ext.init_instance.core_id = core_id;
>> +	msg.ext.init_instance.proc_domain = domain;
>> +
>> +	request.header = msg.val;
>> +	request.data = param;
>> +	request.size = param_size;
> 
> isn't there a need to check if the module can be initialized? there's
> got to be some dependency on pipeline state?


IPC handlers found in message.c have one and only one purpose only: send 
a message. Firmware will return an error if arguments passed are invalid.

Also, note that ALSA/ASoC already have a working state machine for 
streaming. There is no reason to re-implement it here.

>> +
>> +	ret = avs_dsp_send_msg(adev, &request, NULL);
>> +	if (ret)
>> +		avs_ipc_err(adev, &request, "init instance", ret);
>> +
>> +	return ret;
>> +}
>> +
>> +int avs_ipc_delete_instance(struct avs_dev *adev, u16 module_id, u8 instance_id)
>> +{
>> +	union avs_module_msg msg = AVS_MODULE_REQUEST(DELETE_INSTANCE);
>> +	struct avs_ipc_msg request = {0};
>> +	int ret;
>> +
>> +	msg.module_id = module_id;
>> +	msg.instance_id = instance_id;
>> +	request.header = msg.val;
>> +
>> +	ret = avs_dsp_send_msg(adev, &request, NULL);
>> +	if (ret)
>> +		avs_ipc_err(adev, &request, "delete instance", ret);
>> +
>> +	return ret;
> 
> same here, can this be used in any pipeline state?


Ditto.

>> +}
>> +
>> +int avs_ipc_bind(struct avs_dev *adev, u16 module_id, u8 instance_id,
>> +		 u16 dst_module_id, u8 dst_instance_id,
>> +		 u8 dst_queue, u8 src_queue)
> 
> what does a queue represent?


In firmware's nomenclature pin/index/queue are synonyms when speaking 
about module instances.

>> +{
>> +	union avs_module_msg msg = AVS_MODULE_REQUEST(BIND);
>> +	struct avs_ipc_msg request = {0};
>> +	int ret;
>> +
>> +	msg.module_id = module_id;
>> +	msg.instance_id = instance_id;
>> +	msg.ext.bind_unbind.dst_module_id = dst_module_id;
>> +	msg.ext.bind_unbind.dst_instance_id = dst_instance_id;
>> +	msg.ext.bind_unbind.dst_queue = dst_queue;
>> +	msg.ext.bind_unbind.src_queue = src_queue;
>> +	request.header = msg.val;
>> +
>> +	ret = avs_dsp_send_msg(adev, &request, NULL);
>> +	if (ret)
>> +		avs_ipc_err(adev, &request, "bind modules", ret);
>> +
>> +	return ret;
>> +}
>> +
>> +int avs_ipc_unbind(struct avs_dev *adev, u16 module_id, u8 instance_id,
>> +		   u16 dst_module_id, u8 dst_instance_id,
>> +		   u8 dst_queue, u8 src_queue)
>> +{
>> +	union avs_module_msg msg = AVS_MODULE_REQUEST(UNBIND);
>> +	struct avs_ipc_msg request = {0};
>> +	int ret;
>> +
>> +	msg.module_id = module_id;
>> +	msg.instance_id = instance_id;
>> +	msg.ext.bind_unbind.dst_module_id = dst_module_id;
>> +	msg.ext.bind_unbind.dst_instance_id = dst_instance_id;
>> +	msg.ext.bind_unbind.dst_queue = dst_queue;
>> +	msg.ext.bind_unbind.src_queue = src_queue;
>> +	request.header = msg.val;
>> +
>> +	ret = avs_dsp_send_msg(adev, &request, NULL);
>> +	if (ret)
>> +		avs_ipc_err(adev, &request, "unbind modules", ret);
>> +
>> +	return ret;
> 
> can this be merged with the bind in a helper, the code looks
> quasi-identical with just two lines different.


We had these two coupled together in the past just like you mention. 
Lately we'd decided that having two if-statements (one for message type 
and the other for error message) just two reduce file by just few lines 
is not worth it. So we chose the readability over small file-size gain.
Pierre-Louis Bossart Feb. 25, 2022, 8:44 p.m. UTC | #3
On 2/25/22 12:50, Cezary Rojewski wrote:
> On 2022-02-25 2:27 AM, Pierre-Louis Bossart wrote:
>>> +int avs_ipc_init_instance(struct avs_dev *adev, u16 module_id, u8
>>> instance_id,
>>> +              u8 ppl_id, u8 core_id, u8 domain,
>>
>> you should explain the relationship between ppl_id and core_id. It seems
>> that in the same pipeline different modules instances can be pegged to
>> different cores, which isn't very intuitive given the previous
>> explanation that a pipeline is a scheduling unit.
>>
>> The domain as a u8 is not very clear either, I was under the impression
>> there were only two domains (LL and EDF)?
> 
> 
> Hmm.. such explanations are supposed to be part of HW or FW
> specifications. I don't believe kernel is a place for that. Fields found

how do you expect people with no access to those specs to understand
this code then?

You have to describe the concepts in vague-enough terms that someone
familiar with DSPs can understand.

> here are needed to provide all the necessary information firmware
> expects when requesting INIT_INSTANCE. What's possible and how's
> everything handled internally is for firmware to decide and explain.
> There are no if-statements in the driver's code that force
> ppl_id/core_id relation so I don't see why reader would get an
> impression there is some dependency. What's in the topology gets routed
> to firmware with help of above function.
> 
> Just to confirm: yes, you can have multiple cores engaged in servicing
> modules found in single pipelines.
> 
> In regard to field name/sizes: again, these match firmware equivalents
> 1:1 so it's easy to switch back and forth.

add comments then.

> 
>>> +              void *param, u32 param_size)
>>> +{
>>> +    union avs_module_msg msg = AVS_MODULE_REQUEST(INIT_INSTANCE);
>>> +    struct avs_ipc_msg request;
>>> +    int ret;
>>> +
>>> +    msg.module_id = module_id;
>>> +    msg.instance_id = instance_id;
>>> +    /* firmware expects size provided in dwords */
>>> +    msg.ext.init_instance.param_block_size =
>>> +            DIV_ROUND_UP(param_size, sizeof(u32));
>>> +    msg.ext.init_instance.ppl_instance_id = ppl_id;
>>> +    msg.ext.init_instance.core_id = core_id;
>>> +    msg.ext.init_instance.proc_domain = domain;
>>> +
>>> +    request.header = msg.val;
>>> +    request.data = param;
>>> +    request.size = param_size;
>>
>> isn't there a need to check if the module can be initialized? there's
>> got to be some dependency on pipeline state?
> 
> 
> IPC handlers found in message.c have one and only one purpose only: send
> a message. Firmware will return an error if arguments passed are invalid.
> 
> Also, note that ALSA/ASoC already have a working state machine for
> streaming. There is no reason to re-implement it here.

add a comment then.

> 
>>> +
>>> +    ret = avs_dsp_send_msg(adev, &request, NULL);
>>> +    if (ret)
>>> +        avs_ipc_err(adev, &request, "init instance", ret);
>>> +
>>> +    return ret;
>>> +}
>>> +
>>> +int avs_ipc_delete_instance(struct avs_dev *adev, u16 module_id, u8
>>> instance_id)
>>> +{
>>> +    union avs_module_msg msg = AVS_MODULE_REQUEST(DELETE_INSTANCE);
>>> +    struct avs_ipc_msg request = {0};
>>> +    int ret;
>>> +
>>> +    msg.module_id = module_id;
>>> +    msg.instance_id = instance_id;
>>> +    request.header = msg.val;
>>> +
>>> +    ret = avs_dsp_send_msg(adev, &request, NULL);
>>> +    if (ret)
>>> +        avs_ipc_err(adev, &request, "delete instance", ret);
>>> +
>>> +    return ret;
>>
>> same here, can this be used in any pipeline state?
> 
> 
> Ditto.
> 
>>> +}
>>> +
>>> +int avs_ipc_bind(struct avs_dev *adev, u16 module_id, u8 instance_id,
>>> +         u16 dst_module_id, u8 dst_instance_id,
>>> +         u8 dst_queue, u8 src_queue)
>>
>> what does a queue represent?
> 
> 
> In firmware's nomenclature pin/index/queue are synonyms when speaking
> about module instances.

well, that's worthy of a comment...

> 
>>> +{
>>> +    union avs_module_msg msg = AVS_MODULE_REQUEST(BIND);
>>> +    struct avs_ipc_msg request = {0};
>>> +    int ret;
>>> +
>>> +    msg.module_id = module_id;
>>> +    msg.instance_id = instance_id;
>>> +    msg.ext.bind_unbind.dst_module_id = dst_module_id;
>>> +    msg.ext.bind_unbind.dst_instance_id = dst_instance_id;
>>> +    msg.ext.bind_unbind.dst_queue = dst_queue;
>>> +    msg.ext.bind_unbind.src_queue = src_queue;
>>> +    request.header = msg.val;
>>> +
>>> +    ret = avs_dsp_send_msg(adev, &request, NULL);
>>> +    if (ret)
>>> +        avs_ipc_err(adev, &request, "bind modules", ret);
>>> +
>>> +    return ret;
>>> +}
Cezary Rojewski Feb. 28, 2022, 3:26 p.m. UTC | #4
On 2022-02-25 9:44 PM, Pierre-Louis Bossart wrote:
> On 2/25/22 12:50, Cezary Rojewski wrote:
>> On 2022-02-25 2:27 AM, Pierre-Louis Bossart wrote:
>>>> +int avs_ipc_init_instance(struct avs_dev *adev, u16 module_id, u8
>>>> instance_id,
>>>> +              u8 ppl_id, u8 core_id, u8 domain,
>>>
>>> you should explain the relationship between ppl_id and core_id. It seems
>>> that in the same pipeline different modules instances can be pegged to
>>> different cores, which isn't very intuitive given the previous
>>> explanation that a pipeline is a scheduling unit.
>>>
>>> The domain as a u8 is not very clear either, I was under the impression
>>> there were only two domains (LL and EDF)?
>>
>>
>> Hmm.. such explanations are supposed to be part of HW or FW
>> specifications. I don't believe kernel is a place for that. Fields found
> 
> how do you expect people with no access to those specs to understand
> this code then?
> 
> You have to describe the concepts in vague-enough terms that someone
> familiar with DSPs can understand.


Added kernel-doc for said function to address this.

>> here are needed to provide all the necessary information firmware
>> expects when requesting INIT_INSTANCE. What's possible and how's
>> everything handled internally is for firmware to decide and explain.
>> There are no if-statements in the driver's code that force
>> ppl_id/core_id relation so I don't see why reader would get an
>> impression there is some dependency. What's in the topology gets routed
>> to firmware with help of above function.
>>
>> Just to confirm: yes, you can have multiple cores engaged in servicing
>> modules found in single pipelines.
>>
>> In regard to field name/sizes: again, these match firmware equivalents
>> 1:1 so it's easy to switch back and forth.
> 
> add comments then.


Ack.

>>
>>>> +              void *param, u32 param_size)
>>>> +{
>>>> +    union avs_module_msg msg = AVS_MODULE_REQUEST(INIT_INSTANCE);
>>>> +    struct avs_ipc_msg request;
>>>> +    int ret;
>>>> +
>>>> +    msg.module_id = module_id;
>>>> +    msg.instance_id = instance_id;
>>>> +    /* firmware expects size provided in dwords */
>>>> +    msg.ext.init_instance.param_block_size =
>>>> +            DIV_ROUND_UP(param_size, sizeof(u32));
>>>> +    msg.ext.init_instance.ppl_instance_id = ppl_id;
>>>> +    msg.ext.init_instance.core_id = core_id;
>>>> +    msg.ext.init_instance.proc_domain = domain;
>>>> +
>>>> +    request.header = msg.val;
>>>> +    request.data = param;
>>>> +    request.size = param_size;
>>>
>>> isn't there a need to check if the module can be initialized? there's
>>> got to be some dependency on pipeline state?
>>
>>
>> IPC handlers found in message.c have one and only one purpose only: send
>> a message. Firmware will return an error if arguments passed are invalid.
>>
>> Also, note that ALSA/ASoC already have a working state machine for
>> streaming. There is no reason to re-implement it here.
> 
> add a comment then.


Ack.

>>>> +}
>>>> +
>>>> +int avs_ipc_bind(struct avs_dev *adev, u16 module_id, u8 instance_id,
>>>> +         u16 dst_module_id, u8 dst_instance_id,
>>>> +         u8 dst_queue, u8 src_queue)
>>>
>>> what does a queue represent?
>>
>>
>> In firmware's nomenclature pin/index/queue are synonyms when speaking
>> about module instances.
> 
> well, that's worthy of a comment...


Ack.
diff mbox series

Patch

diff --git a/sound/soc/intel/avs/ipc.c b/sound/soc/intel/avs/ipc.c
index 69178b5d39b1..63f4face5bae 100644
--- a/sound/soc/intel/avs/ipc.c
+++ b/sound/soc/intel/avs/ipc.c
@@ -20,9 +20,15 @@  static void avs_dsp_receive_rx(struct avs_dev *adev, u64 header)
 	union avs_reply_msg msg = AVS_MSG(header);
 
 	ipc->rx.header = header;
-	if (!msg.status)
+	if (!msg.status) {
+		/* update size in case of LARGE_CONFIG_GET */
+		if (msg.msg_target == AVS_MOD_MSG &&
+		    msg.global_msg_type == AVS_MOD_LARGE_CONFIG_GET)
+			ipc->rx.size = msg.ext.large_config.data_off_size;
+
 		memcpy_fromio(ipc->rx.data, avs_uplink_addr(adev),
 			      ipc->rx.size);
+	}
 }
 
 static void avs_dsp_process_notification(struct avs_dev *adev, u64 header)
diff --git a/sound/soc/intel/avs/messages.c b/sound/soc/intel/avs/messages.c
index ab13fc7809fe..e870d5792a77 100644
--- a/sound/soc/intel/avs/messages.c
+++ b/sound/soc/intel/avs/messages.c
@@ -6,6 +6,7 @@ 
 //          Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com>
 //
 
+#include <linux/slab.h>
 #include "avs.h"
 #include "messages.h"
 
@@ -139,3 +140,210 @@  int avs_ipc_get_pipeline_state(struct avs_dev *adev, u8 instance_id,
 	*state = reply.rsp.ext.get_ppl_state.state;
 	return ret;
 }
+
+int avs_ipc_init_instance(struct avs_dev *adev, u16 module_id, u8 instance_id,
+			  u8 ppl_id, u8 core_id, u8 domain,
+			  void *param, u32 param_size)
+{
+	union avs_module_msg msg = AVS_MODULE_REQUEST(INIT_INSTANCE);
+	struct avs_ipc_msg request;
+	int ret;
+
+	msg.module_id = module_id;
+	msg.instance_id = instance_id;
+	/* firmware expects size provided in dwords */
+	msg.ext.init_instance.param_block_size =
+			DIV_ROUND_UP(param_size, sizeof(u32));
+	msg.ext.init_instance.ppl_instance_id = ppl_id;
+	msg.ext.init_instance.core_id = core_id;
+	msg.ext.init_instance.proc_domain = domain;
+
+	request.header = msg.val;
+	request.data = param;
+	request.size = param_size;
+
+	ret = avs_dsp_send_msg(adev, &request, NULL);
+	if (ret)
+		avs_ipc_err(adev, &request, "init instance", ret);
+
+	return ret;
+}
+
+int avs_ipc_delete_instance(struct avs_dev *adev, u16 module_id, u8 instance_id)
+{
+	union avs_module_msg msg = AVS_MODULE_REQUEST(DELETE_INSTANCE);
+	struct avs_ipc_msg request = {0};
+	int ret;
+
+	msg.module_id = module_id;
+	msg.instance_id = instance_id;
+	request.header = msg.val;
+
+	ret = avs_dsp_send_msg(adev, &request, NULL);
+	if (ret)
+		avs_ipc_err(adev, &request, "delete instance", ret);
+
+	return ret;
+}
+
+int avs_ipc_bind(struct avs_dev *adev, u16 module_id, u8 instance_id,
+		 u16 dst_module_id, u8 dst_instance_id,
+		 u8 dst_queue, u8 src_queue)
+{
+	union avs_module_msg msg = AVS_MODULE_REQUEST(BIND);
+	struct avs_ipc_msg request = {0};
+	int ret;
+
+	msg.module_id = module_id;
+	msg.instance_id = instance_id;
+	msg.ext.bind_unbind.dst_module_id = dst_module_id;
+	msg.ext.bind_unbind.dst_instance_id = dst_instance_id;
+	msg.ext.bind_unbind.dst_queue = dst_queue;
+	msg.ext.bind_unbind.src_queue = src_queue;
+	request.header = msg.val;
+
+	ret = avs_dsp_send_msg(adev, &request, NULL);
+	if (ret)
+		avs_ipc_err(adev, &request, "bind modules", ret);
+
+	return ret;
+}
+
+int avs_ipc_unbind(struct avs_dev *adev, u16 module_id, u8 instance_id,
+		   u16 dst_module_id, u8 dst_instance_id,
+		   u8 dst_queue, u8 src_queue)
+{
+	union avs_module_msg msg = AVS_MODULE_REQUEST(UNBIND);
+	struct avs_ipc_msg request = {0};
+	int ret;
+
+	msg.module_id = module_id;
+	msg.instance_id = instance_id;
+	msg.ext.bind_unbind.dst_module_id = dst_module_id;
+	msg.ext.bind_unbind.dst_instance_id = dst_instance_id;
+	msg.ext.bind_unbind.dst_queue = dst_queue;
+	msg.ext.bind_unbind.src_queue = src_queue;
+	request.header = msg.val;
+
+	ret = avs_dsp_send_msg(adev, &request, NULL);
+	if (ret)
+		avs_ipc_err(adev, &request, "unbind modules", ret);
+
+	return ret;
+}
+
+static int __avs_ipc_set_large_config(struct avs_dev *adev, u16 module_id, u8 instance_id,
+				      u8 param_id, bool init_block, bool final_block,
+				      u8 *request_data, size_t request_size, size_t off_size)
+{
+	union avs_module_msg msg = AVS_MODULE_REQUEST(LARGE_CONFIG_SET);
+	struct avs_ipc_msg request;
+	int ret;
+
+	msg.module_id = module_id;
+	msg.instance_id = instance_id;
+	msg.ext.large_config.data_off_size = off_size;
+	msg.ext.large_config.large_param_id = param_id;
+	msg.ext.large_config.final_block = final_block;
+	msg.ext.large_config.init_block = init_block;
+
+	request.header = msg.val;
+	request.data = request_data;
+	request.size = request_size;
+
+	ret = avs_dsp_send_msg(adev, &request, NULL);
+	if (ret)
+		avs_ipc_err(adev, &request, "large config set", ret);
+
+	return ret;
+}
+
+int avs_ipc_set_large_config(struct avs_dev *adev, u16 module_id,
+			     u8 instance_id, u8 param_id,
+			     u8 *request, size_t request_size)
+{
+	size_t remaining, tx_size;
+	bool final;
+	int ret;
+
+	remaining = request_size;
+	tx_size = min_t(size_t, AVS_MAILBOX_SIZE, remaining);
+	final = (tx_size == remaining);
+
+	/* Initial request states total payload size. */
+	ret = __avs_ipc_set_large_config(adev, module_id, instance_id,
+					 param_id, 1, final, request, tx_size,
+					 request_size);
+	if (ret)
+		return ret;
+
+	remaining -= tx_size;
+
+	/* Loop the rest only when payload exceeds mailbox's size. */
+	while (remaining) {
+		size_t offset;
+
+		offset = request_size - remaining;
+		tx_size = min_t(size_t, AVS_MAILBOX_SIZE, remaining);
+		final = (tx_size == remaining);
+
+		ret = __avs_ipc_set_large_config(adev, module_id, instance_id,
+						 param_id, 0, final,
+						 request + offset, tx_size,
+						 offset);
+		if (ret)
+			return ret;
+
+		remaining -= tx_size;
+	}
+
+	return 0;
+}
+
+int avs_ipc_get_large_config(struct avs_dev *adev, u16 module_id, u8 instance_id,
+			     u8 param_id, u8 *request_data, size_t request_size,
+			     u8 **reply_data, size_t *reply_size)
+{
+	union avs_module_msg msg = AVS_MODULE_REQUEST(LARGE_CONFIG_GET);
+	struct avs_ipc_msg request;
+	struct avs_ipc_msg reply = {0};
+	size_t size;
+	void *buf;
+	int ret;
+
+	reply.data = kzalloc(AVS_MAILBOX_SIZE, GFP_KERNEL);
+	if (!reply.data)
+		return -ENOMEM;
+
+	msg.module_id = module_id;
+	msg.instance_id = instance_id;
+	msg.ext.large_config.data_off_size = request_size;
+	msg.ext.large_config.large_param_id = param_id;
+	/* final_block is always 0 on request. Updated by fw on reply. */
+	msg.ext.large_config.final_block = 0;
+	msg.ext.large_config.init_block = 1;
+
+	request.header = msg.val;
+	request.data = request_data;
+	request.size = request_size;
+	reply.size = AVS_MAILBOX_SIZE;
+
+	ret = avs_dsp_send_msg(adev, &request, &reply);
+	if (ret) {
+		avs_ipc_err(adev, &request, "large config get", ret);
+		kfree(reply.data);
+		return ret;
+	}
+
+	size = reply.rsp.ext.large_config.data_off_size;
+	buf = krealloc(reply.data, size, GFP_KERNEL);
+	if (!buf) {
+		kfree(reply.data);
+		return -ENOMEM;
+	}
+
+	*reply_data = buf;
+	*reply_size = size;
+
+	return 0;
+}
diff --git a/sound/soc/intel/avs/messages.h b/sound/soc/intel/avs/messages.h
index 67f7e1826e45..1dabd1005327 100644
--- a/sound/soc/intel/avs/messages.h
+++ b/sound/soc/intel/avs/messages.h
@@ -95,6 +95,15 @@  struct avs_tlv {
 	u32 value[];
 } __packed;
 
+enum avs_module_msg_type {
+	AVS_MOD_INIT_INSTANCE = 0,
+	AVS_MOD_LARGE_CONFIG_GET = 3,
+	AVS_MOD_LARGE_CONFIG_SET = 4,
+	AVS_MOD_BIND = 5,
+	AVS_MOD_UNBIND = 6,
+	AVS_MOD_DELETE_INSTANCE = 11,
+};
+
 union avs_module_msg {
 	u64 val;
 	struct {
@@ -110,6 +119,24 @@  union avs_module_msg {
 		};
 		union {
 			u32 val;
+			struct {
+				u32 param_block_size:16;
+				u32 ppl_instance_id:8;
+				u32 core_id:4;
+				u32 proc_domain:1;
+			} init_instance;
+			struct {
+				u32 data_off_size:20;
+				u32 large_param_id:8;
+				u32 final_block:1;
+				u32 init_block:1;
+			} large_config;
+			struct {
+				u32 dst_module_id:16;
+				u32 dst_instance_id:8;
+				u32 dst_queue:3;
+				u32 src_queue:3;
+			} bind_unbind;
 		} ext;
 	};
 } __packed;
@@ -136,6 +163,13 @@  union avs_reply_msg {
 			struct {
 				u32 state:5;
 			} get_ppl_state;
+			/* module management */
+			struct {
+				u32 data_off_size:20;
+				u32 large_param_id:8;
+				u32 final_block:1;
+				u32 init_block:1;
+			} large_config;
 		} ext;
 	};
 } __packed;
@@ -245,4 +279,23 @@  int avs_ipc_set_pipeline_state(struct avs_dev *adev, u8 instance_id,
 int avs_ipc_get_pipeline_state(struct avs_dev *adev, u8 instance_id,
 			       enum avs_pipeline_state *state);
 
+/* Module management messages */
+int avs_ipc_init_instance(struct avs_dev *adev, u16 module_id, u8 instance_id,
+			  u8 ppl_id, u8 core_id, u8 domain,
+			  void *param, u32 param_size);
+int avs_ipc_delete_instance(struct avs_dev *adev, u16 module_id,
+			    u8 instance_id);
+int avs_ipc_bind(struct avs_dev *adev, u16 module_id, u8 instance_id,
+		 u16 dst_module_id, u8 dst_instance_id,
+		 u8 dst_queue, u8 src_queue);
+int avs_ipc_unbind(struct avs_dev *adev, u16 module_id, u8 instance_id,
+		   u16 dst_module_id, u8 dst_instance_id,
+		   u8 dst_queue, u8 src_queue);
+int avs_ipc_set_large_config(struct avs_dev *adev, u16 module_id,
+			     u8 instance_id, u8 param_id,
+			     u8 *request, size_t request_size);
+int avs_ipc_get_large_config(struct avs_dev *adev, u16 module_id, u8 instance_id,
+			     u8 param_id, u8 *request_data, size_t request_size,
+			     u8 **reply_data, size_t *reply_size);
+
 #endif /* __SOUND_SOC_INTEL_AVS_MSGS_H */