diff --git a/src/audio/copier/copier_dai.c b/src/audio/copier/copier_dai.c index e84689b08eab..0fb7c58eb40a 100644 --- a/src/audio/copier/copier_dai.c +++ b/src/audio/copier/copier_dai.c @@ -64,6 +64,96 @@ static int copier_set_alh_multi_gtw_channel_map(struct comp_dev *dev, return 0; } +static int copier_alh_assign_dai_index(struct comp_dev *dev, + void *gtw_cfg_data, + union ipc4_connector_node_id node_id, + struct ipc_config_dai *dai, + int *dai_index, + int *dai_count) +{ + struct processing_module *mod = comp_get_drvdata(dev); + struct copier_data *cd = module_get_private_data(mod); + const struct sof_alh_configuration_blob *alh_blob = gtw_cfg_data; + uint8_t *dma_config; + size_t alh_cfg_size, dma_config_length; + int i, dai_num, ret; + + if (!cd->config.gtw_cfg.config_length) { + comp_err(mod->dev, "No gateway config found in blob!"); + return -EINVAL; + } + + switch (dai->type) { + case SOF_DAI_INTEL_HDA: + /* We use DAI_INTEL_HDA for ACE 2.0 platforms */ + alh_cfg_size = get_alh_config_size(alh_blob); + dma_config = (uint8_t *)gtw_cfg_data + alh_cfg_size; + dma_config_length = (cd->config.gtw_cfg.config_length << 2) - alh_cfg_size; + + /* Here we check node_id if we need to use FW aggregation, + * in other words do we need to create multiple dai or not + */ + if (!is_multi_gateway(node_id)) { + /* Find DMA config in blob and retrieve stream_id */ + ret = ipc4_find_dma_config_multiple(dai, dma_config, dma_config_length, + alh_blob->alh_cfg.mapping[0].alh_id, 0); + if (ret != 0) { + comp_err(mod->dev, "No sndw dma_config found in blob!"); + return -EINVAL; + } + dai_index[0] = dai->host_dma_config[0]->stream_id; + return 0; + } + + dai_num = alh_blob->alh_cfg.count; + if (dai_num > IPC4_ALH_MAX_NUMBER_OF_GTW || dai_num < 0) { + comp_err(mod->dev, "Invalid dai_count: %d", dai_num); + return -EINVAL; + } + + for (i = 0; i < dai_num; i++) { + ret = ipc4_find_dma_config_multiple(dai, dma_config, + dma_config_length, + alh_blob->alh_cfg.mapping[i].alh_id, i); + if (ret != 0) { + comp_err(mod->dev, "No sndw dma_config found in blob!"); + return -EINVAL; + } + + /* To process data on SoundWire interface HD-A DMA is used so it seems + * logical to me to use stream tag as a dai_index instead of PDI. + */ + dai_index[i] = dai->host_dma_config[i]->stream_id; + } + + *dai_count = dai_num; + break; + case SOF_DAI_INTEL_ALH: + /* Use DAI_INTEL_ALH for ACE 1.0 and older */ + if (!is_multi_gateway(node_id)) { + dai_index[0] = IPC4_ALH_DAI_INDEX(node_id.f.v_index); + return 0; + } + + dai_num = alh_blob->alh_cfg.count; + if (dai_num > IPC4_ALH_MAX_NUMBER_OF_GTW || dai_num < 0) { + comp_err(mod->dev, "Invalid dai_count: %d", dai_num); + return -EINVAL; + } + + for (i = 0; i < dai_num; i++) + dai_index[i] = IPC4_ALH_DAI_INDEX(alh_blob->alh_cfg.mapping[i].alh_id); + + *dai_count = dai_num; + break; + default: + comp_err(mod->dev, "Invalid dai type selected: %d", dai->type); + return -EINVAL; + } + + return 0; +} + static int copier_dai_init(struct comp_dev *dev, struct comp_ipc_config *config, const struct ipc4_copier_module_cfg *copier, @@ -100,7 +190,8 @@ static int copier_dai_init(struct comp_dev *dev, } /* save the channel map and count for ALH multi-gateway */ - if (type == ipc4_gtw_alh && is_multi_gateway(copier->gtw_cfg.node_id)) { + if ((type == ipc4_gtw_alh || type == ipc4_gtw_link) && + is_multi_gateway(copier->gtw_cfg.node_id)) { ret = copier_set_alh_multi_gtw_channel_map(dev, copier, index); if (ret < 0) return ret; @@ -182,50 +273,19 @@ int copier_dai_create(struct comp_dev *dev, struct copier_data *cd, break; case ipc4_alh_link_output_class: case ipc4_alh_link_input_class: +#if defined(CONFIG_ACE_VERSION_2_0) + dai.type = SOF_DAI_INTEL_HDA; + dai.is_config_blob = true; + type = ipc4_gtw_link; +#else dai.type = SOF_DAI_INTEL_ALH; dai.is_config_blob = true; type = ipc4_gtw_alh; - - /* copier - * { - * gtw_cfg - * { - * gtw_node_id; - * config_length; - * config_data - * { - * count; - * { - * node_id; \\ normal gtw id - * mask; - * } mapping[MAX_ALH_COUNT]; - * } - * } - * } - */ - /* get gtw node id in config data */ - if (is_multi_gateway(node_id)) { - if (copier->gtw_cfg.config_length) { - const struct sof_alh_configuration_blob *alh_blob = - (const struct sof_alh_configuration_blob *) - copier->gtw_cfg.config_data; - - dai_count = alh_blob->alh_cfg.count; - if (dai_count > IPC4_ALH_MAX_NUMBER_OF_GTW || dai_count < 0) { - comp_err(dev, "Invalid dai_count: %d", dai_count); - return -EINVAL; - } - for (i = 0; i < dai_count; i++) - dai_index[i] = - IPC4_ALH_DAI_INDEX(alh_blob->alh_cfg.mapping[i].alh_id); - } else { - comp_err(dev, "No ipc4_alh_multi_gtw_cfg found in blob!"); - return -EINVAL; - } - } else { - dai_index[dai_count - 1] = IPC4_ALH_DAI_INDEX(node_id.f.v_index); - } - +#endif /* defined(CONFIG_ACE_VERSION_2_0) */ + ret = copier_alh_assign_dai_index(dev, cd->gtw_cfg, node_id, + &dai, dai_index, &dai_count); + if (ret) + return ret; break; case ipc4_dmic_link_input_class: dai.type = SOF_DAI_INTEL_DMIC; @@ -425,7 +485,6 @@ int copier_dai_params(struct copier_data *cd, struct comp_dev *dev, cd->converter[IPC4_COPIER_GATEWAY_PIN]; return ret; } - /* For ALH multi-gateway case, params->channels is a total multiplexed * number of channels. Demultiplexed number of channels for each individual * gateway comes in blob's struct ipc4_alh_multi_gtw_cfg. diff --git a/src/include/ipc4/alh.h b/src/include/ipc4/alh.h index af30a2a36723..c9658c225d76 100644 --- a/src/include/ipc4/alh.h +++ b/src/include/ipc4/alh.h @@ -66,4 +66,12 @@ struct sof_alh_configuration_blob { struct ipc4_alh_multi_gtw_cfg alh_cfg; } __attribute__((packed, aligned(4))); +static inline size_t +get_alh_config_size(const struct sof_alh_configuration_blob *alh_blob) +{ + return sizeof(alh_blob->gtw_attributes) + + sizeof(alh_blob->alh_cfg.count) + + sizeof(alh_blob->alh_cfg.mapping[0]) * alh_blob->alh_cfg.count; +} + #endif diff --git a/src/include/sof/audio/ipc-config.h b/src/include/sof/audio/ipc-config.h index 8bedb7002e0c..ccabb8628e35 100644 --- a/src/include/sof/audio/ipc-config.h +++ b/src/include/sof/audio/ipc-config.h @@ -75,7 +75,8 @@ struct ipc_config_dai { uint32_t feature_mask; /**< copier feature mask (set directly from * ipc4_copier_module_cfg on init) */ - struct ipc_dma_config *host_dma_config; /**< DMA config - required for ACE 2.0 and newer */ + /**< DMA configs - required for ACE 2.0 and newer */ + struct ipc_dma_config *host_dma_config[GTW_DMA_DEVICE_MAX_COUNT]; const struct ipc4_audio_format *out_fmt;/**< audio format for output pin 0 - required * for ACE 2.0 and newer */ diff --git a/src/include/sof/ipc/topology.h b/src/include/sof/ipc/topology.h index f4caf828d7a2..86a6bebdb6f2 100644 --- a/src/include/sof/ipc/topology.h +++ b/src/include/sof/ipc/topology.h @@ -60,7 +60,8 @@ int ipc4_pipeline_complete(struct ipc *ipc, uint32_t comp_id, uint32_t cmd); int ipc4_find_dma_config(struct ipc_config_dai *dai, uint8_t *data_buffer, uint32_t size); int ipc4_pipeline_prepare(struct ipc_comp_dev *ppl_icd, uint32_t cmd); int ipc4_pipeline_trigger(struct ipc_comp_dev *ppl_icd, uint32_t cmd, bool *delayed); - +int ipc4_find_dma_config_multiple(struct ipc_config_dai *dai, uint8_t *data_buffer, + uint32_t size, uint32_t device_id, int dma_cfg_idx); #else #error "No or invalid IPC MAJOR version selected." #endif diff --git a/src/include/sof/tlv.h b/src/include/sof/tlv.h index 366d54debf83..4008ad3f24a4 100644 --- a/src/include/sof/tlv.h +++ b/src/include/sof/tlv.h @@ -33,6 +33,9 @@ struct sof_tlv { */ static inline struct sof_tlv *tlv_next(const struct sof_tlv *tlv) { + if (tlv->length % sizeof(uint32_t) != 0) + return NULL; + return (struct sof_tlv *)((char *)(tlv) + sizeof(*tlv) + tlv->length); } @@ -94,4 +97,22 @@ static inline void tlv_value_get(const void *data, } } +/** + * @brief Retrieves pointer to the TLV Structure value of the specified type + * + * @param tlv TLV struct pointer. + * @param type Value type. + * @return Value pointer + */ +static inline void *tlv_value_ptr_get(struct sof_tlv *tlv, uint32_t type) +{ + if ((uintptr_t)tlv % sizeof(uint32_t) != 0) + return NULL; + + if (tlv->type != type) + return NULL; + + return (void *)tlv->value; +} + #endif /* __SOF_TLV_H__ */ diff --git a/src/ipc/ipc4/dai.c b/src/ipc/ipc4/dai.c index 69a4bdf0569a..4bb662566155 100644 --- a/src/ipc/ipc4/dai.c +++ b/src/ipc/ipc4/dai.c @@ -26,6 +26,7 @@ #include #include #include +#include #include "../audio/copier/copier.h" #include "../audio/copier/dai_copier.h" @@ -44,7 +45,7 @@ void dai_set_link_hda_config(uint16_t *link_config, case SOF_DAI_INTEL_SSP: link_cfg.full = 0; link_cfg.part.dir = common_config->direction; - link_cfg.part.stream = common_config->host_dma_config->stream_id; + link_cfg.part.stream = common_config->host_dma_config[0]->stream_id; break; case SOF_DAI_INTEL_DMIC: link_cfg.full = 0; @@ -57,7 +58,7 @@ void dai_set_link_hda_config(uint16_t *link_config, } else { link_cfg.part.hchan = out_fmt->channels_count - 1; } - link_cfg.part.stream = common_config->host_dma_config->stream_id; + link_cfg.part.stream = common_config->host_dma_config[0]->stream_id; break; default: /* other types of DAIs not need link_config */ @@ -79,11 +80,34 @@ int dai_config_dma_channel(struct dai_data *dd, struct comp_dev *dev, const void case SOF_DAI_INTEL_DMIC: channel = 0; #if defined(CONFIG_ACE_VERSION_2_0) - if (dai->host_dma_config->pre_allocated_by_host) - channel = dai->host_dma_config->dma_channel_id; + if (dai->host_dma_config[0]->pre_allocated_by_host) + channel = dai->host_dma_config[0]->dma_channel_id; #endif break; case SOF_DAI_INTEL_HDA: +#if defined(CONFIG_ACE_VERSION_2_0) + if (copier_cfg->gtw_cfg.node_id.f.dma_type == ipc4_alh_link_output_class || + copier_cfg->gtw_cfg.node_id.f.dma_type == ipc4_alh_link_input_class) { + struct processing_module *mod = comp_get_drvdata(dev); + struct copier_data *cd = module_get_private_data(mod); + + if (!cd->gtw_cfg) { + comp_err(dev, "No gateway config found!"); + return DMA_CHAN_INVALID; + } + + channel = DMA_CHAN_INVALID; + const struct sof_alh_configuration_blob *alh_blob = cd->gtw_cfg; + + for (int i = 0; i < alh_blob->alh_cfg.count; i++) { + if (dai->host_dma_config[i]->stream_id == dai->dai_index) { + channel = dai->host_dma_config[i]->dma_channel_id; + break; + } + } + break; + } +#endif /* defined(CONFIG_ACE_VERSION_2_0) */ channel = copier_cfg->gtw_cfg.node_id.f.v_index; break; case SOF_DAI_INTEL_ALH: diff --git a/src/ipc/ipc4/helper.c b/src/ipc/ipc4/helper.c index 0bd224dec819..285a9bee93b4 100644 --- a/src/ipc/ipc4/helper.c +++ b/src/ipc/ipc4/helper.c @@ -44,6 +44,7 @@ #include #include #include +#include #include #include @@ -1043,11 +1044,40 @@ int ipc4_find_dma_config(struct ipc_config_dai *dai, uint8_t *data_buffer, uint3 if (*dma_config_id != GTW_DMA_CONFIG_ID) return IPC4_INVALID_REQUEST; - dai->host_dma_config = GET_IPC_DMA_CONFIG(data_buffer, size); + dai->host_dma_config[0] = GET_IPC_DMA_CONFIG(data_buffer, size); #endif return IPC4_SUCCESS; } +int ipc4_find_dma_config_multiple(struct ipc_config_dai *dai, uint8_t *data_buffer, + uint32_t size, uint32_t device_id, int dma_cfg_idx) +{ + uint32_t end_addr = (uint32_t)data_buffer + size; + struct ipc_dma_config *dma_cfg; + struct sof_tlv *tlvs; + + for (tlvs = (struct sof_tlv *)data_buffer; (uint32_t)tlvs < end_addr; + tlvs = tlv_next(tlvs)) { + dma_cfg = tlv_value_ptr_get(tlvs, GTW_DMA_CONFIG_ID); + if (!dma_cfg) + continue; + + /* To be able to retrieve proper DMA config we need to check if + * device_id value (which is alh_id) is equal to device_address. + * They both contain SNDW master id and PDI. If they match then + * proper config is found. + */ + for (uint32_t i = 0; i < dma_cfg->channel_map.device_count; i++) { + if (dma_cfg->channel_map.map[i].device_address == device_id) { + dai->host_dma_config[dma_cfg_idx] = dma_cfg; + return IPC4_SUCCESS; + } + } + } + + return IPC4_INVALID_REQUEST; +} + void ipc4_base_module_cfg_to_stream_params(const struct ipc4_base_module_cfg *base_cfg, struct sof_ipc_stream_params *params) {