From 2bfca84c2474d22cde2f8403ee303e63cce1217f Mon Sep 17 00:00:00 2001 From: Adrian Warecki Date: Thu, 27 Mar 2025 15:43:52 +0100 Subject: [PATCH 1/9] ipc4: notification: Add functions to prepare ipc xrun notifications Add functions to prepare ipc4 notifications about underrun/overrun detected by a dma. Signed-off-by: Adrian Warecki --- src/include/ipc4/notification.h | 5 +++++ src/ipc/ipc4/notification.c | 40 +++++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+) diff --git a/src/include/ipc4/notification.h b/src/include/ipc4/notification.h index eb2c034f1a01..ed93b9d1aac6 100644 --- a/src/include/ipc4/notification.h +++ b/src/include/ipc4/notification.h @@ -274,4 +274,9 @@ struct ipc4_resource_event_data_notification { void process_data_error_notif_msg_init(struct ipc_msg *msg, uint32_t resource_id, uint32_t error_code); +void copier_gateway_underrun_notif_msg_init(struct ipc_msg *msg, uint32_t pipeline_id); +void copier_gateway_overrun_notif_msg_init(struct ipc_msg *msg, uint32_t pipeline_id); +void gateway_underrun_notif_msg_init(struct ipc_msg *msg, uint32_t resource_id); +void gateway_overrun_notif_msg_init(struct ipc_msg *msg, uint32_t resource_id); + #endif /* __IPC4_NOTIFICATION_H__ */ diff --git a/src/ipc/ipc4/notification.c b/src/ipc/ipc4/notification.c index 1e2ce62533cc..45da12da7e18 100644 --- a/src/ipc/ipc4/notification.c +++ b/src/ipc/ipc4/notification.c @@ -36,6 +36,46 @@ void xrun_notif_msg_init(struct ipc_msg *msg_xrun, uint32_t resource_id, uint32_ } #endif +void copier_gateway_underrun_notif_msg_init(struct ipc_msg *msg, uint32_t pipeline_id) +{ + struct ipc4_resource_event_data_notification *notif_data = msg->tx_data; + + resource_notif_header_init(msg); + notif_data->resource_id = pipeline_id; + notif_data->event_type = SOF_IPC4_GATEWAY_UNDERRUN_DETECTED; + notif_data->resource_type = SOF_IPC4_PIPELINE; +} + +void gateway_underrun_notif_msg_init(struct ipc_msg *msg, uint32_t resource_id) +{ + struct ipc4_resource_event_data_notification *notif_data = msg->tx_data; + + resource_notif_header_init(msg); + notif_data->resource_id = resource_id; + notif_data->event_type = SOF_IPC4_GATEWAY_UNDERRUN_DETECTED; + notif_data->resource_type = SOF_IPC4_GATEWAY; +} + +void copier_gateway_overrun_notif_msg_init(struct ipc_msg *msg, uint32_t pipeline_id) +{ + struct ipc4_resource_event_data_notification *notif_data = msg->tx_data; + + resource_notif_header_init(msg); + notif_data->resource_id = pipeline_id; + notif_data->event_type = SOF_IPC4_GATEWAY_OVERRUN_DETECTED; + notif_data->resource_type = SOF_IPC4_PIPELINE; +} + +void gateway_overrun_notif_msg_init(struct ipc_msg *msg, uint32_t resource_id) +{ + struct ipc4_resource_event_data_notification *notif_data = msg->tx_data; + + resource_notif_header_init(msg); + notif_data->resource_id = resource_id; + notif_data->event_type = SOF_IPC4_GATEWAY_OVERRUN_DETECTED; + notif_data->resource_type = SOF_IPC4_GATEWAY; +} + void process_data_error_notif_msg_init(struct ipc_msg *msg, uint32_t resource_id, uint32_t error_code) { From cbf3b61e7ab90c06fb314aab40586629ba5d135a Mon Sep 17 00:00:00 2001 From: Adrian Warecki Date: Thu, 27 Mar 2025 15:20:17 +0100 Subject: [PATCH 2/9] chain_dma: xrun notification update Change xrun notifications to use the notification pool. Improve the logic for clearing the flag that informs the notification was sent. Previously, it was cleared when xrun occurred and the notification was sent, which meant that a notification was sent every second time xrun was detected, which could cause a flood of notifications. Signed-off-by: Adrian Warecki --- src/audio/chain_dma.c | 75 +++++++++++++------------------------ src/ipc/ipc4/notification.c | 12 ------ 2 files changed, 27 insertions(+), 60 deletions(-) diff --git a/src/audio/chain_dma.c b/src/audio/chain_dma.c index c133309fd6c7..3f865752c30c 100644 --- a/src/audio/chain_dma.c +++ b/src/audio/chain_dma.c @@ -26,9 +26,8 @@ #include #include #if CONFIG_XRUN_NOTIFICATIONS_ENABLE +#include #include -#include -#include #endif #define DT_NUM_HDA_HOST_IN DT_PROP(DT_INST(0, intel_adsp_hda_host_in), dma_channels) @@ -59,7 +58,6 @@ struct chain_dma_data { uint8_t cs; #if CONFIG_XRUN_NOTIFICATIONS_ENABLE bool xrun_notification_sent; - struct ipc_msg *msg_xrun; #endif /* local host DMA config */ @@ -145,33 +143,32 @@ static size_t chain_get_transferred_data_size(const uint32_t out_read_pos, const return buff_size - in_read_pos + out_read_pos; } -#if CONFIG_XRUN_NOTIFICATIONS_ENABLE -static void handle_xrun(struct chain_dma_data *cd) +/* get status from dma and check for xrun */ +static int chain_get_dma_status(struct chain_dma_data *cd, struct dma_chan_data *chan, + struct dma_status *stat) { - int ret; - - if (cd->link_connector_node_id.f.dma_type == ipc4_hda_link_output_class && - !cd->xrun_notification_sent) { - tr_warn(&chain_dma_tr, "handle_xrun(): underrun detected"); - xrun_notif_msg_init(cd->msg_xrun, cd->link_connector_node_id.dw, - SOF_IPC4_GATEWAY_UNDERRUN_DETECTED); - ipc_msg_send(cd->msg_xrun, NULL, true); - cd->xrun_notification_sent = true; - } else if (cd->link_connector_node_id.f.dma_type == ipc4_hda_link_input_class && - !cd->xrun_notification_sent) { - tr_warn(&chain_dma_tr, "handle_xrun(): overrun detected"); - xrun_notif_msg_init(cd->msg_xrun, cd->link_connector_node_id.dw, - SOF_IPC4_GATEWAY_OVERRUN_DETECTED); - ipc_msg_send(cd->msg_xrun, NULL, true); - cd->xrun_notification_sent = true; - } else { - /* if xrun_notification_sent is already set, then it means that link was - * able to reach stability therefore next underrun/overrun should be reported. - */ + int ret = dma_get_status(chan->dma->z_dev, chan->index, stat); +#if CONFIG_XRUN_NOTIFICATIONS_ENABLE + if (ret == -EPIPE && !cd->xrun_notification_sent) { + struct ipc_msg *notify = ipc_notification_pool_get(IPC4_RESOURCE_EVENT_SIZE); + + if (notify) { + if (cd->stream_direction == SOF_IPC_STREAM_PLAYBACK) + gateway_underrun_notif_msg_init(notify, + cd->link_connector_node_id.dw); + else + gateway_overrun_notif_msg_init(notify, + cd->link_connector_node_id.dw); + + ipc_msg_send(notify, notify->tx_data, false); + cd->xrun_notification_sent = true; + } + } else if (!ret) { cd->xrun_notification_sent = false; } -} #endif + return ret; +} static enum task_state chain_task_run(void *data) { @@ -185,16 +182,13 @@ static enum task_state chain_task_run(void *data) /* Link DMA can return -EPIPE and current status if xrun occurs, then it is not critical * and flow shall continue. Other error values will be treated as critical. */ - ret = dma_get_status(cd->chan_link->dma->z_dev, cd->chan_link->index, &stat); + ret = chain_get_dma_status(cd, cd->chan_link, &stat); switch (ret) { case 0: break; case -EPIPE: tr_warn(&chain_dma_tr, "chain_task_run(): dma_get_status() link xrun occurred," " ret = %d", ret); -#if CONFIG_XRUN_NOTIFICATIONS_ENABLE - handle_xrun(cd); -#endif break; default: tr_err(&chain_dma_tr, "chain_task_run(): dma_get_status() error, ret = %d", ret); @@ -206,7 +200,7 @@ static enum task_state chain_task_run(void *data) link_read_pos = stat.read_position; /* Host DMA does not report xruns. All error values will be treated as critical. */ - ret = dma_get_status(cd->chan_host->dma->z_dev, cd->chan_host->index, &stat); + ret = chain_get_dma_status(cd, cd->chan_host, &stat); if (ret < 0) { tr_err(&chain_dma_tr, "chain_task_run(): dma_get_status() error, ret = %d", ret); return SOF_TASK_STATE_COMPLETED; @@ -685,20 +679,9 @@ __cold static struct comp_dev *chain_task_create(const struct comp_driver *drv, comp_set_drvdata(dev, cd); ret = chain_task_init(dev, host_dma_id, link_dma_id, fifo_size); - if (ret) - goto error_cd; - -#if CONFIG_XRUN_NOTIFICATIONS_ENABLE - cd->msg_xrun = ipc_msg_init(header.dat, - sizeof(struct ipc4_resource_event_data_notification)); - if (!cd->msg_xrun) - goto error_cd; - cd->xrun_notification_sent = false; -#endif - - return dev; + if (!ret) + return dev; -error_cd: rfree(cd); error: rfree(dev); @@ -711,10 +694,6 @@ __cold static void chain_task_free(struct comp_dev *dev) assert_can_be_cold(); -#if CONFIG_XRUN_NOTIFICATIONS_ENABLE - ipc_msg_free(cd->msg_xrun); -#endif - chain_release(dev); rfree(cd); rfree(dev); diff --git a/src/ipc/ipc4/notification.c b/src/ipc/ipc4/notification.c index 45da12da7e18..fa97d63d4943 100644 --- a/src/ipc/ipc4/notification.c +++ b/src/ipc/ipc4/notification.c @@ -24,18 +24,6 @@ static void resource_notif_header_init(struct ipc_msg *msg) memset(¬if_data->event_data, 0, sizeof(notif_data->event_data)); } -#if CONFIG_XRUN_NOTIFICATIONS_ENABLE -void xrun_notif_msg_init(struct ipc_msg *msg_xrun, uint32_t resource_id, uint32_t event_type) -{ - struct ipc4_resource_event_data_notification *notif_data = msg_xrun->tx_data; - - resource_notif_header_init(msg_xrun); - notif_data->resource_id = resource_id; - notif_data->event_type = event_type; - notif_data->resource_type = SOF_IPC4_GATEWAY; -} -#endif - void copier_gateway_underrun_notif_msg_init(struct ipc_msg *msg, uint32_t pipeline_id) { struct ipc4_resource_event_data_notification *notif_data = msg->tx_data; From f3b49960a6220f969d55321461678ee7b0627537 Mon Sep 17 00:00:00 2001 From: Adrian Warecki Date: Thu, 27 Mar 2025 15:29:42 +0100 Subject: [PATCH 3/9] zephyr: host: Add support for xrun notifications in host Add support to the host for sending ipc4 notifications when dma reports an underrun/overrun occurrence. Signed-off-by: Adrian Warecki --- src/audio/copier/host_copier.h | 3 +++ src/audio/host-zephyr.c | 33 ++++++++++++++++++++++++++++++++- 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/src/audio/copier/host_copier.h b/src/audio/copier/host_copier.h index fe7a98de0804..10927abc02cf 100644 --- a/src/audio/copier/host_copier.h +++ b/src/audio/copier/host_copier.h @@ -98,6 +98,9 @@ struct host_data { /* stream info */ struct sof_ipc_stream_posn posn; /* TODO: update this */ struct ipc_msg *msg; /**< host notification */ +#if CONFIG_XRUN_NOTIFICATIONS_ENABLE + bool xrun_notification_sent; +#endif uint32_t dma_buffer_size; /* dma buffer size */ #if CONFIG_HOST_DMA_STREAM_SYNCHRONIZATION bool is_grouped; diff --git a/src/audio/host-zephyr.c b/src/audio/host-zephyr.c index c167e5256d95..c9ada6b62165 100644 --- a/src/audio/host-zephyr.c +++ b/src/audio/host-zephyr.c @@ -32,6 +32,11 @@ #include #include +#if CONFIG_XRUN_NOTIFICATIONS_ENABLE +#include +#include +#endif + #include "copier/copier.h" #include "copier/host_copier.h" @@ -359,6 +364,32 @@ static void host_dma_cb(struct comp_dev *dev, size_t bytes) host_common_one_shot(hd, bytes); } +/* get status from dma and check for xrun */ +static int host_get_status(struct comp_dev *dev, struct host_data *hd, struct dma_status *stat) +{ + int ret = dma_get_status(hd->chan->dma->z_dev, hd->chan->index, stat); +#if CONFIG_XRUN_NOTIFICATIONS_ENABLE + if (ret == -EPIPE && !hd->xrun_notification_sent) { + struct ipc_msg *notify = ipc_notification_pool_get(IPC4_RESOURCE_EVENT_SIZE); + + if (notify) { + if (dev->direction == SOF_IPC_STREAM_PLAYBACK) + copier_gateway_underrun_notif_msg_init(notify, + dev->pipeline->pipeline_id); + else + copier_gateway_overrun_notif_msg_init(notify, + dev->pipeline->pipeline_id); + + ipc_msg_send(notify, notify->tx_data, false); + hd->xrun_notification_sent = true; + } + } else if (!ret) { + hd->xrun_notification_sent = false; + } +#endif + return ret; +} + /* Minimum time between 2 consecutive "no bytes to copy" messages in milliseconds */ #define SOF_MIN_NO_BYTES_INTERVAL_MS 20 @@ -378,7 +409,7 @@ static uint32_t host_get_copy_bytes_normal(struct host_data *hd, struct comp_dev int ret; /* get data sizes from DMA */ - ret = dma_get_status(hd->chan->dma->z_dev, hd->chan->index, &dma_stat); + ret = host_get_status(dev, hd, &dma_stat); if (ret < 0) { comp_err(dev, "dma_get_status() failed, ret = %u", ret); From c7cb73cbd8c319db005e98243b350ef8e67ea260 Mon Sep 17 00:00:00 2001 From: Adrian Warecki Date: Thu, 27 Mar 2025 15:31:38 +0100 Subject: [PATCH 4/9] zephyr: dai: Add support for xrun notifications in dai Add support to dai for sending ipc4 notifications when dma reports an underrun/overrun occurrence. Signed-off-by: Adrian Warecki --- src/audio/dai-zephyr.c | 35 ++++++++++++++++++++++++++++++-- src/include/sof/lib/dai-zephyr.h | 3 +++ 2 files changed, 36 insertions(+), 2 deletions(-) diff --git a/src/audio/dai-zephyr.c b/src/audio/dai-zephyr.c index ae9d2cb8fc91..c36a89a9c57b 100644 --- a/src/audio/dai-zephyr.c +++ b/src/audio/dai-zephyr.c @@ -36,6 +36,11 @@ #include #include +#if CONFIG_XRUN_NOTIFICATIONS_ENABLE +#include +#include +#endif + #include "copier/copier.h" #include "copier/dai_copier.h" #include "copier/copier_gain.h" @@ -1462,6 +1467,32 @@ static int dai_comp_trigger(struct comp_dev *dev, int cmd) return dai_common_trigger(dd, dev, cmd); } +/* get status from dma and check for xrun */ +static int dai_get_status(struct comp_dev *dev, struct dai_data *dd, struct dma_status *stat) +{ + int ret = dma_get_status(dd->chan->dma->z_dev, dd->chan->index, stat); +#if CONFIG_XRUN_NOTIFICATIONS_ENABLE + if (ret == -EPIPE && !dd->xrun_notification_sent) { + struct ipc_msg *notify = ipc_notification_pool_get(IPC4_RESOURCE_EVENT_SIZE); + + if (notify) { + if (dev->direction == SOF_IPC_STREAM_PLAYBACK) + copier_gateway_underrun_notif_msg_init(notify, + dev->pipeline->pipeline_id); + else + copier_gateway_overrun_notif_msg_init(notify, + dev->pipeline->pipeline_id); + + ipc_msg_send(notify, notify->tx_data, false); + dd->xrun_notification_sent = true; + } + } else if (!ret) { + dd->xrun_notification_sent = false; + } +#endif + return ret; +} + /* report xrun occurrence */ static void dai_report_xrun(struct dai_data *dd, struct comp_dev *dev, uint32_t bytes) { @@ -1499,7 +1530,7 @@ int dai_zephyr_multi_endpoint_copy(struct dai_data **dd, struct comp_dev *dev, struct dma_status stat; /* get data sizes from DMA */ - ret = dma_get_status(dd[i]->chan->dma->z_dev, dd[i]->chan->index, &stat); + ret = dai_get_status(dev, dd[i], &stat); switch (ret) { case 0: break; @@ -1633,7 +1664,7 @@ int dai_common_copy(struct dai_data *dd, struct comp_dev *dev, pcm_converter_fun int ret; /* get data sizes from DMA */ - ret = dma_get_status(dd->chan->dma->z_dev, dd->chan->index, &stat); + ret = dai_get_status(dev, dd, &stat); switch (ret) { case 0: break; diff --git a/src/include/sof/lib/dai-zephyr.h b/src/include/sof/lib/dai-zephyr.h index 84b834138eae..4ac676ee8cde 100644 --- a/src/include/sof/lib/dai-zephyr.h +++ b/src/include/sof/lib/dai-zephyr.h @@ -158,6 +158,9 @@ struct dai_data { struct llp_slot_info slot_info; /* fast mode, use one byte memory to save repreated cycles */ bool fast_mode; +#if CONFIG_XRUN_NOTIFICATIONS_ENABLE + bool xrun_notification_sent; +#endif #ifdef CONFIG_SOF_TELEMETRY_IO_PERFORMANCE_MEASUREMENTS /* io performance measurement */ struct io_perf_data_item *io_perf_bytes_count; From b6b86f7b22695093839a40606b0871153225336c Mon Sep 17 00:00:00 2001 From: Adrian Warecki Date: Fri, 21 Mar 2025 14:04:16 +0100 Subject: [PATCH 5/9] ipc4: module: Add definitions for Config Get / Set ipc Using Module Config Get / Set command, host driver may send a parameter that fits into the header (a very short one), packed along with parameter id. Larger parameters require fragmentation and a series of Large Config Set commands. Signed-off-by: Adrian Warecki --- src/include/ipc4/module.h | 74 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) diff --git a/src/include/ipc4/module.h b/src/include/ipc4/module.h index 432d704fb2dd..f38621ead52c 100644 --- a/src/include/ipc4/module.h +++ b/src/include/ipc4/module.h @@ -225,6 +225,80 @@ struct ipc4_module_bind_unbind { } extension; } __attribute__((packed, aligned(4))); +/* + * Using Module Config Get / Set command, host driver may send a parameter + * that fits into the header (a very short one), packed along with parameter id. + * Larger parameters require fragmentation and a series of Large Config Set + * commands. + * + * param_id_data specifies both ID of the parameter, defined by the module + * and value of the parameter. + * It is up to the module how to distribute bits to ID and value of the parameter. + * If there are more bits required than available to value, then Input Data may + * be used to pass the value + * + * NOTE: Module Config Get/Set commands are used internally by the driver + * for small parameters defined by Intel components. While all externally + * developed components communicates with host using Large Config commands + * no matter what the size of parameter is. + */ +struct ipc4_module_config { + union { + uint32_t dat; + + struct { + uint32_t module_id : 16; /* module id */ + uint32_t instance_id : 8; /* instance id */ + /* SOF_IPC4_MOD_CONFIG_GET / SOF_IPC4_MOD_CONFIG_SET */ + uint32_t type : 5; + uint32_t rsp : 1; /* SOF_IPC4_MESSAGE_DIR_MSG_REQUEST */ + uint32_t msg_tgt : 1; /* SOF_IPC4_MESSAGE_TARGET_MODULE_MSG */ + uint32_t _reserved_0 : 1; + } r; + } primary; + + union { + uint32_t dat; + + struct { + /* Param id and data */ + uint32_t param_id_data : 30; + uint32_t _reserved_2 : 2; + } r; + } extension; +} __attribute__((packed, aligned(4))); + +/* + * Sent by FW in response to Module Config Get. + */ +struct ipc4_module_config_reply { + union { + uint32_t dat; + + struct { + uint32_t status : IPC4_IXC_STATUS_BITS; + uint32_t type : 5; /* SOF_IPC4_MOD_CONFIG_GET */ + uint32_t rsp : 1; /* SOF_IPC4_MESSAGE_DIR_MSG_REPLY */ + uint32_t msg_tgt : 1; /* SOF_IPC4_MESSAGE_TARGET_MODULE_MSG */ + uint32_t _reserved_0 : 1; + } r; + } primary; + + union { + uint32_t dat; + + struct { + /* + * Value of this field may be changed by the module + * if parameter value fits into the available bits, + * or stay intact if the value is copied to the Output Data. + */ + uint32_t param_id_data : 30; + uint32_t _reserved_2 : 2; + } r; + } extension; +} __attribute__((packed, aligned(4))); + struct ipc4_module_large_config { union { uint32_t dat; From a4fd2b6cbef4873e8f975736e2e632ab86d4350a Mon Sep 17 00:00:00 2001 From: Adrian Warecki Date: Fri, 21 Mar 2025 18:18:53 +0100 Subject: [PATCH 6/9] ipc4: module: interface: module_adapter: Add support for Config Get/Set ipc Add support for Config Get and Config Set ipc commands. Handle them through the get_attribute and set_attribute methods of the component with the COMP_ATTR_IPC4_CONFIG parameter. Add the set_config_param and get_config_param functions to the module interface. Extend the functionality of module adapter to call the above functions to handle ipc request. Increase the version of module api due to modification of module_interface structure. Using Module Config Get / Set command, host driver may send a parameter that fits into the header (a very short one), packed along with parameter id. Larger parameters require fragmentation and a series of Large Config Set commands. Signed-off-by: Adrian Warecki --- .../module_adapter/module_adapter_ipc4.c | 23 ++++++++ src/include/module/module/api_ver.h | 2 +- src/include/module/module/interface.h | 36 ++++++++++++ src/include/sof/audio/component.h | 1 + .../sof/audio/module_adapter/module/generic.h | 8 +++ src/ipc/ipc4/handler.c | 57 ++++++++++++++++++- src/library_manager/lib_manager.c | 1 + 7 files changed, 125 insertions(+), 3 deletions(-) diff --git a/src/audio/module_adapter/module_adapter_ipc4.c b/src/audio/module_adapter/module_adapter_ipc4.c index 7c959dcbceb6..fb7642212f4f 100644 --- a/src/audio/module_adapter/module_adapter_ipc4.c +++ b/src/audio/module_adapter/module_adapter_ipc4.c @@ -171,12 +171,17 @@ EXPORT_SYMBOL(module_get_large_config); int module_adapter_get_attribute(struct comp_dev *dev, uint32_t type, void *value) { struct processing_module *mod = comp_mod(dev); + const struct module_interface *const interface = mod->dev->drv->adapter_ops; switch (type) { case COMP_ATTR_BASE_CONFIG: memcpy_s(value, sizeof(struct ipc4_base_module_cfg), &mod->priv.cfg.base_cfg, sizeof(mod->priv.cfg.base_cfg)); break; + case COMP_ATTR_IPC4_CONFIG: + if (interface->get_config_param) + return interface->get_config_param(mod, (uint32_t *)value); + return -ENOEXEC; default: return -EINVAL; } @@ -185,6 +190,24 @@ int module_adapter_get_attribute(struct comp_dev *dev, uint32_t type, void *valu } EXPORT_SYMBOL(module_adapter_get_attribute); +int module_adapter_set_attribute(struct comp_dev *dev, uint32_t type, void *value) +{ + struct processing_module *mod = comp_mod(dev); + const struct module_interface *const interface = mod->dev->drv->adapter_ops; + + switch (type) { + case COMP_ATTR_IPC4_CONFIG: + if (interface->set_config_param) + return interface->set_config_param(mod, *(uint32_t *)value); + return -ENOEXEC; + default: + return -EINVAL; + } + + return 0; +} +EXPORT_SYMBOL(module_adapter_set_attribute); + static bool module_adapter_multi_sink_source_prepare(struct comp_dev *dev) { struct processing_module *mod = comp_mod(dev); diff --git a/src/include/module/module/api_ver.h b/src/include/module/module/api_ver.h index 680745f8c323..35aedb0305d7 100644 --- a/src/include/module/module/api_ver.h +++ b/src/include/module/module/api_ver.h @@ -21,7 +21,7 @@ #define SOF_MODULE_API_MAJOR_VERSION 5 #define SOF_MODULE_API_MIDDLE_VERSION 0 -#define SOF_MODULE_API_MINOR_VERSION 0 +#define SOF_MODULE_API_MINOR_VERSION 1 #define SOF_MODULE_API_CURRENT_VERSION MODULE_API_VERSION_ENCODE(SOF_MODULE_API_MAJOR_VERSION, \ SOF_MODULE_API_MIDDLE_VERSION, SOF_MODULE_API_MINOR_VERSION) diff --git a/src/include/module/module/interface.h b/src/include/module/module/interface.h index f80271400a45..75b4621c716c 100644 --- a/src/include/module/module/interface.h +++ b/src/include/module/module/interface.h @@ -66,6 +66,18 @@ struct processing_module; struct sof_source; struct sof_sink; +/* + * This structure may be used by modules to carry short 16bit parameters. + */ +union config_param_id_data { + uint32_t dw; + struct { + uint32_t data16 : 16; /* Input/Output small config data */ + uint32_t id : 14; /* input parameter ID */ + uint32_t _rsvd : 2; + } f; +}; + /** * \struct module_interface * \brief 3rd party processing module interface @@ -170,6 +182,30 @@ struct module_interface { struct output_stream_buffer *output_buffers, int num_output_buffers); + /** + * (optional) Set module configuration parameter + * + * Using Module Config Set command, host driver may send a parameter + * that fits into the header (a very short one), packed along with parameter id. + * + * param_id_data specifies both ID of the parameter, defined by the module + * and value of the parameter. + * It is up to the module how to distribute bits to ID and value of the parameter. + */ + int (*set_config_param)(struct processing_module *mod, uint32_t param_id_data); + + /** + * (optional) Get module configuration parameter + * + * Using Module Config Get command, host driver may send a parameter + * that fits into the header (a very short one), packed along with parameter id. + * + * param_id_data specifies both ID of the parameter, defined by the module + * and value of the parameter. + * It is up to the module how to distribute bits to ID and value of the parameter. + */ + int (*get_config_param)(struct processing_module *mod, uint32_t *param_id_data); + /** * (optional) Set module configuration for the given configuration ID * diff --git a/src/include/sof/audio/component.h b/src/include/sof/audio/component.h index 1b9c3474a8cf..0091419cc442 100644 --- a/src/include/sof/audio/component.h +++ b/src/include/sof/audio/component.h @@ -119,6 +119,7 @@ enum { #define COMP_ATTR_COPY_DIR 2 /**< Comp copy direction */ #define COMP_ATTR_VDMA_INDEX 3 /**< Comp index of the virtual DMA at the gateway. */ #define COMP_ATTR_BASE_CONFIG 4 /**< Component base config */ +#define COMP_ATTR_IPC4_CONFIG 5 /**< Component ipc4 set/get config */ /** @}*/ /** \name Trace macros diff --git a/src/include/sof/audio/module_adapter/module/generic.h b/src/include/sof/audio/module_adapter/module/generic.h index f8081c6b4c6a..60906685a21a 100644 --- a/src/include/sof/audio/module_adapter/module/generic.h +++ b/src/include/sof/audio/module_adapter/module/generic.h @@ -61,6 +61,7 @@ static const struct comp_driver comp_##adapter##_module = { \ .set_large_config = module_set_large_config,\ .get_large_config = module_get_large_config,\ .get_attribute = module_adapter_get_attribute,\ + .set_attribute = module_adapter_set_attribute,\ .bind = module_adapter_bind,\ .unbind = module_adapter_unbind,\ .get_total_data_processed = module_adapter_get_total_data_processed,\ @@ -224,6 +225,12 @@ int module_adapter_get_attribute(struct comp_dev *dev, uint32_t type, void *valu return -EINVAL; } +static inline +int module_adapter_set_attribute(struct comp_dev *dev, uint32_t type, void *value) +{ + return -EINVAL; +} + static inline int module_set_large_config(struct comp_dev *dev, uint32_t param_id, bool first_block, bool last_block, uint32_t data_offset, const char *data) @@ -273,6 +280,7 @@ int module_set_large_config(struct comp_dev *dev, uint32_t param_id, bool first_ int module_get_large_config(struct comp_dev *dev, uint32_t param_id, bool first_block, bool last_block, uint32_t *data_offset, char *data); int module_adapter_get_attribute(struct comp_dev *dev, uint32_t type, void *value); +int module_adapter_set_attribute(struct comp_dev *dev, uint32_t type, void *value); int module_adapter_bind(struct comp_dev *dev, void *data); int module_adapter_unbind(struct comp_dev *dev, void *data); uint64_t module_adapter_get_total_data_processed(struct comp_dev *dev, diff --git a/src/ipc/ipc4/handler.c b/src/ipc/ipc4/handler.c index a701889c68c1..d2199be999ce 100644 --- a/src/ipc/ipc4/handler.c +++ b/src/ipc/ipc4/handler.c @@ -968,6 +968,58 @@ __cold static int ipc4_unbind_module_instance(struct ipc4_message_request *ipc4) return ipc_comp_disconnect(ipc, (ipc_pipe_comp_connect *)&bu); } +static int ipc4_set_get_config_module_instance(struct ipc4_message_request *ipc4, bool set) +{ + struct ipc4_module_config *config = (struct ipc4_module_config *)ipc4; + int (*function)(struct comp_dev *dev, uint32_t type, void *value); + const struct comp_driver *drv; + struct comp_dev *dev = NULL; + int ret; + + tr_dbg(&ipc_tr, "ipc4_set_get_config_module_instance %x : %x, set %d", + (uint32_t)config->primary.r.module_id, (uint32_t)config->primary.r.instance_id, + !!set); + + /* get component dev for non-basefw since there is no component dev for basefw */ + if (config->primary.r.module_id) { + uint32_t comp_id; + + comp_id = IPC4_COMP_ID(config->primary.r.module_id, config->primary.r.instance_id); + dev = ipc4_get_comp_dev(comp_id); + if (!dev) + return IPC4_MOD_INVALID_ID; + + drv = dev->drv; + + /* Pass IPC to target core */ + if (!cpu_is_me(dev->ipc_config.core)) + return ipc4_process_on_core(dev->ipc_config.core, false); + } else { + drv = ipc4_get_comp_drv(config->primary.r.module_id); + } + + if (!drv) + return IPC4_MOD_INVALID_ID; + + function = set ? drv->ops.set_attribute : drv->ops.get_attribute; + if (!function) + return IPC4_INVALID_REQUEST; + + ret = function(dev, COMP_ATTR_IPC4_CONFIG, &config->extension.dat); + if (ret < 0) { + ipc_cmd_err(&ipc_tr, "ipc4_set_get_config_module_instance %x : %x failed %d, set %u, param %x", + (uint32_t)config->primary.r.module_id, + (uint32_t)config->primary.r.instance_id, ret, !!set, + (uint32_t)config->extension.dat); + ret = IPC4_INVALID_CONFIG_PARAM_ID; + } + + if (!set) + msg_reply.extension = config->extension.dat; + + return ret; +} + __cold static int ipc4_get_vendor_config_module_instance(struct comp_dev *dev, const struct comp_driver *drv, bool init_block, @@ -1463,9 +1515,10 @@ __cold static int ipc4_process_module_message(struct ipc4_message_request *ipc4) ret = ipc4_init_module_instance(ipc4); break; case SOF_IPC4_MOD_CONFIG_GET: + ret = ipc4_set_get_config_module_instance(ipc4, false); + break; case SOF_IPC4_MOD_CONFIG_SET: - ret = IPC4_UNAVAILABLE; - tr_info(&ipc_tr, "unsupported module CONFIG_GET"); + ret = ipc4_set_get_config_module_instance(ipc4, true); break; case SOF_IPC4_MOD_LARGE_CONFIG_GET: ret = ipc4_get_large_config_module_instance(ipc4); diff --git a/src/library_manager/lib_manager.c b/src/library_manager/lib_manager.c index 10bf60ed6aa6..7040e283db03 100644 --- a/src/library_manager/lib_manager.c +++ b/src/library_manager/lib_manager.c @@ -572,6 +572,7 @@ static void lib_manager_prepare_module_adapter(struct comp_driver *drv, const st drv->ops.set_large_config = module_set_large_config; drv->ops.get_large_config = module_get_large_config; drv->ops.get_attribute = module_adapter_get_attribute; + drv->ops.set_attribute = module_adapter_set_attribute; drv->ops.bind = module_adapter_bind; drv->ops.unbind = module_adapter_unbind; drv->ops.get_total_data_processed = module_adapter_get_total_data_processed; From 3bb9299cfe7e8d0d63169c05535dcc2b5f971f30 Mon Sep 17 00:00:00 2001 From: Adrian Warecki Date: Tue, 18 Mar 2025 15:38:11 +0100 Subject: [PATCH 7/9] ipc4: notification: Add function to prepare mixer underrun notification Add function preparing ipc4 notification about underrun detected by mixer. Signed-off-by: Adrian Warecki --- src/include/ipc4/notification.h | 18 ++++++++++++++++++ src/ipc/ipc4/notification.c | 14 ++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/src/include/ipc4/notification.h b/src/include/ipc4/notification.h index ed93b9d1aac6..0581159d90c7 100644 --- a/src/include/ipc4/notification.h +++ b/src/include/ipc4/notification.h @@ -242,6 +242,19 @@ struct ipc4_process_data_error_event_data { uint32_t error_code; }; +/** + * \brief This notification is sent by the mixer on stream underrun detection. The frequency of + * sending this notification by Mixer depends on the MixIn settings. + */ +struct ipc4_mixer_underrun_event_data { + /* Indicates EndOfStream */ + uint32_t eos_flag; + /* Data processed by module (in bytes) */ + uint32_t data_mixed; + /* Expected data to be processed (in bytes) */ + uint32_t expected_data_mixed; +}; + /** * \brief Input data payload is reserved field in parent technical spec which can be easily * extendable if needed by specific resource event types in the future. For backward compatibility @@ -252,6 +265,8 @@ union ipc4_resource_event_data { uint32_t dws[6]; /* Process Data Error Data (res type = MODULE_INSTANCE) */ struct ipc4_process_data_error_event_data process_data_error; + /* Mixer Underrun Detected Data (res type = PIPELINE) */ + struct ipc4_mixer_underrun_event_data mixer_underrun; }; struct ipc4_resource_event_data_notification { @@ -279,4 +294,7 @@ void copier_gateway_overrun_notif_msg_init(struct ipc_msg *msg, uint32_t pipelin void gateway_underrun_notif_msg_init(struct ipc_msg *msg, uint32_t resource_id); void gateway_overrun_notif_msg_init(struct ipc_msg *msg, uint32_t resource_id); +void mixer_underrun_notif_msg_init(struct ipc_msg *msg, uint32_t resource_id, uint32_t eos_flag, + uint32_t data_mixed, uint32_t expected_data_mixed); + #endif /* __IPC4_NOTIFICATION_H__ */ diff --git a/src/ipc/ipc4/notification.c b/src/ipc/ipc4/notification.c index fa97d63d4943..50af7dabe7e2 100644 --- a/src/ipc/ipc4/notification.c +++ b/src/ipc/ipc4/notification.c @@ -64,6 +64,20 @@ void gateway_overrun_notif_msg_init(struct ipc_msg *msg, uint32_t resource_id) notif_data->resource_type = SOF_IPC4_GATEWAY; } +void mixer_underrun_notif_msg_init(struct ipc_msg *msg, uint32_t resource_id, uint32_t eos_flag, + uint32_t data_mixed, uint32_t expected_data_mixed) +{ + struct ipc4_resource_event_data_notification *notif_data = msg->tx_data; + + resource_notif_header_init(msg); + notif_data->resource_id = resource_id; + notif_data->event_type = SOF_IPC4_MIXER_UNDERRUN_DETECTED; + notif_data->resource_type = SOF_IPC4_PIPELINE; + notif_data->event_data.mixer_underrun.eos_flag = eos_flag; + notif_data->event_data.mixer_underrun.data_mixed = data_mixed; + notif_data->event_data.mixer_underrun.expected_data_mixed = expected_data_mixed; +} + void process_data_error_notif_msg_init(struct ipc_msg *msg, uint32_t resource_id, uint32_t error_code) { From b1ad9568ff58532e6057cca3a0e715d4bcf85fa7 Mon Sep 17 00:00:00 2001 From: Adrian Warecki Date: Thu, 27 Mar 2025 15:36:00 +0100 Subject: [PATCH 8/9] mixer: Add support for underrun notifications Add to the mixin the capability to send notifications when an underrun occurs, leading to the generation of silence in the output. Signed-off-by: Adrian Warecki --- src/audio/mixin_mixout/mixin_mixout.c | 79 +++++++++++++++++++++++++++ src/audio/mixin_mixout/mixin_mixout.h | 9 +++ 2 files changed, 88 insertions(+) diff --git a/src/audio/mixin_mixout/mixin_mixout.c b/src/audio/mixin_mixout/mixin_mixout.c index 13e7d2caad0f..0bbceebba3c1 100644 --- a/src/audio/mixin_mixout/mixin_mixout.c +++ b/src/audio/mixin_mixout/mixin_mixout.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -24,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -74,6 +76,10 @@ struct mixin_data { mix_func mix; mix_func gain_mix; struct mixin_sink_config sink_config[MIXIN_MAX_SINKS]; +#if CONFIG_XRUN_NOTIFICATIONS_ENABLE + uint32_t last_reported_underrun; + uint32_t underrun_notification_period; +#endif }; /* @@ -138,6 +144,9 @@ static int mixin_init(struct processing_module *mod) return -ENOMEM; mod_data->private = md; +#if CONFIG_XRUN_NOTIFICATIONS_ENABLE + md->underrun_notification_period = MIXIN_MODULE_DEFAULT_UNDERRUN_NOTIFICATION_PERIOD; +#endif for (i = 0; i < MIXIN_MAX_SINKS; i++) { md->sink_config[i].mixer_mode = IPC4_MIXER_NORMAL_MODE; @@ -237,6 +246,29 @@ static void silence(struct cir_buf_ptr *stream, uint32_t start_offset, } } +#if CONFIG_XRUN_NOTIFICATIONS_ENABLE +static void mixin_check_notify_underrun(struct comp_dev *dev, struct mixin_data *mixin_data, + size_t source_avail, size_t sinks_free) +{ + struct ipc_msg *notify; + + mixin_data->last_reported_underrun++; + + if (!source_avail && mixin_data->last_reported_underrun >= + mixin_data->underrun_notification_period) { + mixin_data->last_reported_underrun = 0; + + notify = ipc_notification_pool_get(IPC4_RESOURCE_EVENT_SIZE); + if (!notify) + return; + + mixer_underrun_notif_msg_init(notify, dev->ipc_config.id, false, + source_avail, sinks_free); + ipc_msg_send(notify, notify->tx_data, false); + } +} +#endif + /* Most of the mixing is done here on mixin side. mixin mixes its source data * into each connected mixout sink buffer. Basically, if mixout sink buffer has * no data, mixin copies its source data into mixout sink buffer. If mixout sink @@ -356,6 +388,15 @@ static int mixin_process(struct processing_module *mod, if (sinks_free_frames == 0 || sinks_free_frames == INT32_MAX) return 0; +#if CONFIG_XRUN_NOTIFICATIONS_ENABLE + size_t frame_bytes = source_get_frame_bytes(sources[0]); + size_t min_frames = MIN(dev->frames, sinks_free_frames); + + mixin_check_notify_underrun(dev, mixin_data, + source_avail_frames * frame_bytes, + min_frames * frame_bytes); +#endif + if (source_avail_frames > 0) { size_t buf_size; @@ -917,11 +958,49 @@ static int mixin_set_config(struct processing_module *mod, uint32_t config_id, return 0; } +#if CONFIG_XRUN_NOTIFICATIONS_ENABLE +static int mixin_set_config_param(struct processing_module *mod, uint32_t param_id_data) +{ + struct mixin_data *mixin_data = module_get_private_data(mod); + union config_param_id_data cfg; + + cfg.dw = param_id_data; + + if (cfg.f.id == IPC4_MIXER_UNDERRUN_NOTIF_PERIOD) { + if (cfg.f.data16 < MIXIN_MODULE_MIN_UNDERRUN_NOTIFICATION_PERIOD) + return -EINVAL; + + mixin_data->underrun_notification_period = cfg.f.data16; + return 0; + } + return -EINVAL; +} + +static int mixin_get_config_param(struct processing_module *mod, uint32_t *param_id_data) +{ + struct mixin_data *mixin_data = module_get_private_data(mod); + union config_param_id_data cfg; + + cfg.dw = *param_id_data; + + if (cfg.f.id == IPC4_MIXER_UNDERRUN_NOTIF_PERIOD) { + cfg.f.data16 = mixin_data->underrun_notification_period; + *param_id_data = cfg.dw; + return 0; + } + return -EINVAL; +} +#endif + static const struct module_interface mixin_interface = { .init = mixin_init, .prepare = mixin_prepare, .process = mixin_process, .set_configuration = mixin_set_config, +#if CONFIG_XRUN_NOTIFICATIONS_ENABLE + .set_config_param = mixin_set_config_param, + .get_config_param = mixin_get_config_param, +#endif .reset = mixin_reset, .free = mixin_free }; diff --git a/src/audio/mixin_mixout/mixin_mixout.h b/src/audio/mixin_mixout/mixin_mixout.h index d7519ca80fd8..97aa0e12cdab 100644 --- a/src/audio/mixin_mixout/mixin_mixout.h +++ b/src/audio/mixin_mixout/mixin_mixout.h @@ -32,6 +32,8 @@ #include enum ipc4_mixin_config_param { + /* config_param param id to set/get underrun notification period. */ + IPC4_MIXER_UNDERRUN_NOTIF_PERIOD = 0, /* large_config_set param id for ipc4_mixer_mode_config */ IPC4_MIXER_MODE = 1 }; @@ -42,6 +44,13 @@ enum ipc4_mixin_config_param { /* Number of supported input pins that are mixed together */ #define IPC4_MIXOUT_MODULE_MAX_INPUT_QUEUES 8 +/* Each mixin instance by default has set default notification + * period to avoid notification flooding. + */ +#define MIXIN_MODULE_DEFAULT_UNDERRUN_NOTIFICATION_PERIOD 10 +#define MIXIN_MODULE_MIN_UNDERRUN_NOTIFICATION_PERIOD 1 +#define MIXIN_MODULE_MAX_UNDERRUN_NOTIFICATION_PERIOD 65535 + enum ipc4_mixer_mode { /* Normal mode, just mixing */ IPC4_MIXER_NORMAL_MODE = 0, From 3c7b50d61fa188c10f21e15367eda19678c6cfad Mon Sep 17 00:00:00 2001 From: Adrian Warecki Date: Thu, 27 Mar 2025 15:45:31 +0100 Subject: [PATCH 9/9] audio: ipc4: enable xrun notifications by default Change the configuration so that ipc underrun notifications are sent by default. Signed-off-by: Adrian Warecki --- src/audio/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/audio/Kconfig b/src/audio/Kconfig index 118a740a5906..0c35621b9292 100644 --- a/src/audio/Kconfig +++ b/src/audio/Kconfig @@ -58,7 +58,7 @@ config COMP_CHAIN_DMA config XRUN_NOTIFICATIONS_ENABLE bool "Enable xrun notification" - default n + default y depends on IPC_MAJOR_4 help Enable xrun notifications sending to host