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 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/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/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/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); 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, 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/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; diff --git a/src/include/ipc4/notification.h b/src/include/ipc4/notification.h index eb2c034f1a01..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 { @@ -274,4 +289,12 @@ 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); + +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/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/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; 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/ipc/ipc4/notification.c b/src/ipc/ipc4/notification.c index 1e2ce62533cc..50af7dabe7e2 100644 --- a/src/ipc/ipc4/notification.c +++ b/src/ipc/ipc4/notification.c @@ -24,17 +24,59 @@ 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) +void copier_gateway_underrun_notif_msg_init(struct ipc_msg *msg, uint32_t pipeline_id) { - struct ipc4_resource_event_data_notification *notif_data = msg_xrun->tx_data; + 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; +} - resource_notif_header_init(msg_xrun); +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 = event_type; + notif_data->event_type = SOF_IPC4_GATEWAY_UNDERRUN_DETECTED; notif_data->resource_type = SOF_IPC4_GATEWAY; } -#endif + +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 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) 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;