From b89f3e58400e090b32141e84357cf9d584dcd512 Mon Sep 17 00:00:00 2001 From: Tomasz Lissowski Date: Thu, 13 Jun 2024 19:33:41 +0200 Subject: [PATCH] dai: add support for Intel UAOL DAI This adds support for Intel USB Audio Offload Link (UAOL) DAI. Signed-off-by: Tomasz Lissowski --- src/audio/base_fw_intel.c | 83 +++++++++++++++++++++++++++++++- src/audio/copier/copier.c | 2 + src/audio/copier/copier_dai.c | 13 +++++ src/audio/dai-zephyr.c | 8 +++ src/include/ipc/dai.h | 3 +- src/include/sof/lib/dai-zephyr.h | 5 ++ src/ipc/ipc4/dai.c | 25 ++++++++++ src/ipc/ipc4/helper.c | 12 +++++ src/lib/dai.c | 76 ++++++++++++++++++++++++++++- 9 files changed, 224 insertions(+), 3 deletions(-) diff --git a/src/audio/base_fw_intel.c b/src/audio/base_fw_intel.c index d5ea9f8d4f89..fd3349b0dd48 100644 --- a/src/audio/base_fw_intel.c +++ b/src/audio/base_fw_intel.c @@ -19,6 +19,10 @@ #include #endif +#if CONFIG_UAOL_INTEL_ADSP +#include +#endif + #include #include @@ -27,6 +31,20 @@ struct ipc4_modules_info { struct sof_man_module modules[0]; } __packed __aligned(4); +struct ipc4_uaol_link_capabilities { + uint32_t input_streams_supported : 4; + uint32_t output_streams_supported : 4; + uint32_t bidirectional_streams_supported : 5; + uint32_t rsvd : 19; + uint32_t max_tx_fifo_size; + uint32_t max_rx_fifo_size; +}; + +struct ipc4_uaol_capabilities { + uint32_t link_count; + struct ipc4_uaol_link_capabilities link_caps[]; +} __packed __aligned(4); + LOG_MODULE_REGISTER(basefw_intel, CONFIG_SOF_LOG_LEVEL); int basefw_vendor_fw_config(uint32_t *data_offset, char *data) @@ -36,7 +54,7 @@ int basefw_vendor_fw_config(uint32_t *data_offset, char *data) tlv_value_uint32_set(tuple, IPC4_SLOW_CLOCK_FREQ_HZ_FW_CFG, IPC4_ALH_CAVS_1_8); tuple = tlv_next(tuple); - tlv_value_uint32_set(tuple, IPC4_UAOL_SUPPORT, 0); + tlv_value_uint32_set(tuple, IPC4_UAOL_SUPPORT, 1); tuple = tlv_next(tuple); tlv_value_uint32_set(tuple, IPC4_ALH_SUPPORT_LEVEL_FW_CFG, IPC4_ALH_CAVS_1_8); @@ -47,6 +65,41 @@ int basefw_vendor_fw_config(uint32_t *data_offset, char *data) return 0; } +#if CONFIG_UAOL_INTEL_ADSP + +#define UAOL_DEV(node) DEVICE_DT_GET(node), +static const struct device *uaol_devs[] = { + DT_FOREACH_STATUS_OKAY(intel_adsp_uaol, UAOL_DEV) +}; + +static void tlv_value_set_uaol_caps(struct sof_tlv *tuple, uint32_t type) +{ + size_t dev_count = ARRAY_SIZE(uaol_devs); + struct uaol_capabilities dev_cap; + struct ipc4_uaol_capabilities *caps = (struct ipc4_uaol_capabilities *)tuple->value; + size_t caps_size = offsetof(struct ipc4_uaol_capabilities, link_caps[dev_count]); + size_t i; + int ret; + + memset(caps, 0, caps_size); + + caps->link_count = dev_count; + for (i = 0; i < dev_count; i++) { + ret = uaol_get_capabilities(uaol_devs[i], &dev_cap); + if (ret) + continue; + + caps->link_caps[i].input_streams_supported = dev_cap.input_streams; + caps->link_caps[i].output_streams_supported = dev_cap.output_streams; + caps->link_caps[i].bidirectional_streams_supported = dev_cap.bidirectional_streams; + caps->link_caps[i].max_tx_fifo_size = dev_cap.max_tx_fifo_size; + caps->link_caps[i].max_rx_fifo_size = dev_cap.max_rx_fifo_size; + } + + tlv_value_set(tuple, type, caps_size, caps); +} +#endif + int basefw_vendor_hw_config(uint32_t *data_offset, char *data) { struct sof_tlv *tuple = (struct sof_tlv *)data; @@ -63,6 +116,11 @@ int basefw_vendor_hw_config(uint32_t *data_offset, char *data) tuple = tlv_next(tuple); tlv_value_uint32_set(tuple, IPC4_LP_EBB_COUNT_HW_CFG, PLATFORM_LPSRAM_EBB_COUNT); +#if CONFIG_UAOL_INTEL_ADSP + tuple = tlv_next(tuple); + tlv_value_set_uaol_caps(tuple, IPC4_UAOL_CAPS_HW_CFG); +#endif + tuple = tlv_next(tuple); *data_offset = (int)((char *)tuple - data); @@ -298,6 +356,27 @@ static int basefw_set_fw_config(bool first_block, return 0; } +static int basefw_dma_control(bool first_block, bool last_block, + uint32_t data_size, const char *data) +{ + struct ipc4_dma_control *dma_control; + int ret; + + if (!(first_block && last_block)) + return IPC4_INVALID_REQUEST; + + dma_control = (struct ipc4_dma_control *)data; + + if (data_size < offsetof(struct ipc4_dma_control, config_data[dma_control->config_length])) + return IPC4_INVALID_CONFIG_DATA_STRUCT; + + ret = dai_control(dma_control->node_id, data); + if (ret) + return IPC4_INVALID_REQUEST; + + return 0; +} + int basefw_vendor_set_large_config(struct comp_dev *dev, uint32_t param_id, bool first_block, @@ -308,6 +387,8 @@ int basefw_vendor_set_large_config(struct comp_dev *dev, switch (param_id) { case IPC4_FW_CONFIG: return basefw_set_fw_config(first_block, last_block, data_offset, data); + case IPC4_DMA_CONTROL: + return basefw_dma_control(first_block, last_block, data_offset, data); default: break; } diff --git a/src/audio/copier/copier.c b/src/audio/copier/copier.c index 0d1f87de55c4..1160bde04a1c 100644 --- a/src/audio/copier/copier.c +++ b/src/audio/copier/copier.c @@ -142,6 +142,8 @@ static int copier_init(struct processing_module *mod) case ipc4_i2s_link_input_class: case ipc4_alh_link_output_class: case ipc4_alh_link_input_class: + case ipc4_alh_uaol_stream_link_output_class: + case ipc4_alh_uaol_stream_link_input_class: ret = copier_dai_create(dev, cd, copier, ipc_pipe->pipeline); if (ret < 0) { comp_err(dev, "unable to create dai"); diff --git a/src/audio/copier/copier_dai.c b/src/audio/copier/copier_dai.c index 74c6d62a4d6b..4b0932e4313e 100644 --- a/src/audio/copier/copier_dai.c +++ b/src/audio/copier/copier_dai.c @@ -286,6 +286,19 @@ int copier_dai_create(struct comp_dev *dev, struct copier_data *cd, if (ret) return ret; break; + case ipc4_alh_uaol_stream_link_output_class: + case ipc4_alh_uaol_stream_link_input_class: + dai.type = SOF_DAI_INTEL_UAOL; + dai.is_config_blob = true; + type = ipc4_gtw_alh; + ret = ipc4_find_dma_config(&dai, (uint8_t *)cd->gtw_cfg, + copier->gtw_cfg.config_length * 4); + if (ret != 0) { + comp_err(dev, "No uaol dma_config found in blob!"); + return -EINVAL; + } + dai.out_fmt = &copier->out_fmt; + break; case ipc4_dmic_link_input_class: dai.type = SOF_DAI_INTEL_DMIC; dai.is_config_blob = true; diff --git a/src/audio/dai-zephyr.c b/src/audio/dai-zephyr.c index 872a1ee9bd61..130c00bb7373 100644 --- a/src/audio/dai-zephyr.c +++ b/src/audio/dai-zephyr.c @@ -147,6 +147,8 @@ int dai_set_config(struct dai *dai, struct ipc_config_dai *common_config, cfg.format = sof_cfg->format; cfg.options = sof_cfg->flags; cfg.rate = common_config->sampling_frequency; + cfg.channels = common_config->out_fmt->channels_count; + cfg.word_size = common_config->out_fmt->valid_bit_depth; switch (common_config->type) { case SOF_DAI_INTEL_SSP: @@ -177,6 +179,12 @@ int dai_set_config(struct dai *dai, struct ipc_config_dai *common_config, cfg.type = DAI_IMX_ESAI; cfg_params = &sof_cfg->esai; break; + case SOF_DAI_INTEL_UAOL: + cfg.type = DAI_INTEL_UAOL; + cfg_params = container_of(spec_config, + struct ipc4_copier_gateway_cfg, config_data); + dai_set_link_hda_config(&cfg.link_config, common_config, spec_config); + break; default: return -EINVAL; } diff --git a/src/include/ipc/dai.h b/src/include/ipc/dai.h index b5b29316f9e6..14c49b75681b 100644 --- a/src/include/ipc/dai.h +++ b/src/include/ipc/dai.h @@ -94,7 +94,8 @@ enum sof_ipc_dai_type { SOF_DAI_AMD_SP_VIRTUAL, /**host_dma_config[0]->stream_id; break; + case SOF_DAI_INTEL_UAOL: + link_cfg.full = 0; + link_cfg.part.hchan = out_fmt->channels_count - 1; + link_cfg.part.dir = common_config->direction; + link_cfg.part.stream = common_config->host_dma_config[0]->stream_id; + break; default: /* other types of DAIs not need link_config */ return; @@ -115,6 +121,15 @@ int dai_config_dma_channel(struct dai_data *dd, struct comp_dev *dev, const void */ channel = 0; break; + case SOF_DAI_INTEL_UAOL: +#if defined(CONFIG_ACE_VERSION_2_0) || defined(CONFIG_ACE_VERSION_3_0) + channel = 0; + if (dai->host_dma_config[0]->pre_allocated_by_host) + channel = dai->host_dma_config[0]->dma_channel_id; +#else + channel = copier_cfg->gtw_cfg.node_id.f.v_index; +#endif + break; default: /* other types of DAIs not handled for now */ comp_err(dev, "dai_config_dma_channel(): Unknown dai type %d", dai->type); @@ -177,6 +192,16 @@ int ipc_dai_data_config(struct dai_data *dd, struct comp_dev *dev) dev->ipc_config.frame_fmt, dd->stream_id); break; + case SOF_DAI_INTEL_UAOL: +#ifdef CONFIG_ZEPHYR_NATIVE_DRIVERS + dd->stream_id = dai_get_stream_id(dai_p, dai->direction); + dev->ipc_config.frame_fmt = SOF_IPC_FRAME_S32_LE; + dd->config.burst_elems = dai_get_fifo_depth(dd->dai, dai->direction); + break; +#else + /* only native Zephyr driver supported */ + return -EINVAL; +#endif default: /* other types of DAIs not handled for now */ comp_warn(dev, "dai_data_config(): Unknown dai type %d", dai->type); diff --git a/src/ipc/ipc4/helper.c b/src/ipc/ipc4/helper.c index 3774b4adca84..5dc264bfac3d 100644 --- a/src/ipc/ipc4/helper.c +++ b/src/ipc/ipc4/helper.c @@ -1076,6 +1076,18 @@ int ipc4_add_comp_dev(struct comp_dev *dev) int ipc4_find_dma_config(struct ipc_config_dai *dai, uint8_t *data_buffer, uint32_t size) { #if defined(CONFIG_ACE_VERSION_2_0) + if (dai->type == SOF_DAI_INTEL_UAOL) { + void *value_ptr = NULL; + uint32_t value_size; + + tlv_value_get(data_buffer, size, GTW_DMA_CONFIG_ID, &value_ptr, &value_size); + if (!value_ptr) + return IPC4_INVALID_REQUEST; + + dai->host_dma_config[0] = (struct ipc_dma_config *)value_ptr; + return IPC4_SUCCESS; + } + uint32_t *dma_config_id = GET_IPC_DMA_CONFIG_ID(data_buffer, size); if (*dma_config_id != GTW_DMA_CONFIG_ID) diff --git a/src/lib/dai.c b/src/lib/dai.c index 143047aebc4d..7bcceb28310f 100644 --- a/src/lib/dai.c +++ b/src/lib/dai.c @@ -151,6 +151,9 @@ const struct device *zephyr_dev[] = { #if CONFIG_DAI_NXP_ESAI DT_FOREACH_STATUS_OKAY(nxp_dai_esai, GET_DEVICE_LIST) #endif +#if CONFIG_DAI_INTEL_UAOL + DT_FOREACH_STATUS_OKAY(intel_uaol_dai, GET_DEVICE_LIST) +#endif }; static const struct device *dai_get_zephyr_device(uint32_t type, uint32_t index) @@ -202,6 +205,15 @@ static void dai_set_device_params(struct dai *d) d->dma_dev = DMA_DEV_HDA; d->dma_caps = DMA_CAP_HDA; break; + case SOF_DAI_INTEL_UAOL: +#ifdef CONFIG_DMA_INTEL_ADSP_GPDMA + d->dma_dev = DMA_DEV_ALH; + d->dma_caps = DMA_CAP_GP_LP | DMA_CAP_GP_HP; +#else + d->dma_dev = DMA_DEV_HDA; + d->dma_caps = DMA_CAP_HDA; +#endif + break; default: break; } @@ -212,8 +224,47 @@ struct dai *dai_get(uint32_t type, uint32_t index, uint32_t flags) { const struct device *dev; struct dai *d; + uint32_t z_type; - dev = dai_get_zephyr_device(type, index); + switch (type) { + case SOF_DAI_INTEL_SSP: + z_type = DAI_INTEL_SSP; + break; + case SOF_DAI_INTEL_DMIC: + z_type = DAI_INTEL_DMIC; + break; + case SOF_DAI_INTEL_HDA: + z_type = DAI_INTEL_HDA; + break; + case SOF_DAI_INTEL_ALH: + z_type = DAI_INTEL_ALH; + break; + case SOF_DAI_IMX_SAI: + z_type = DAI_IMX_SAI; + break; + case SOF_DAI_IMX_ESAI: + z_type = DAI_IMX_ESAI; + break; + case SOF_DAI_AMD_BT: + z_type = DAI_AMD_BT; + break; + case SOF_DAI_AMD_SP: + z_type = DAI_AMD_SP; + break; + case SOF_DAI_AMD_DMIC: + z_type = DAI_AMD_DMIC; + break; + case SOF_DAI_MEDIATEK_AFE: + z_type = DAI_MEDIATEK_AFE; + break; + case SOF_DAI_INTEL_UAOL: + z_type = DAI_INTEL_UAOL; + break; + default: + return NULL; + } + + dev = dai_get_zephyr_device(z_type, index); if (!dev) { tr_err(&dai_tr, "dai_get: failed to get dai with index %d type %d", index, type); @@ -253,6 +304,29 @@ void dai_put(struct dai *dai) rfree(dai); } + +int dai_control(uint32_t node_id, const void *data) +{ + union ipc4_connector_node_id node = { .dw = node_id }; + const struct device *dev; + uint32_t z_type; + int ret; + + switch (node.f.dma_type) { + case ipc4_alh_uaol_stream_link_output_class: + case ipc4_alh_uaol_stream_link_input_class: + z_type = DAI_INTEL_UAOL; + break; + default: + return -EINVAL; + } + + dev = dai_get_zephyr_device(z_type, node.f.v_index); + if (dev == NULL) + return -ENODEV; + + return dai_config_set(dev, NULL, data); +} #else static inline const struct dai_type_info *dai_find_type(uint32_t type) {