From 6d952e5f037cc1b4fa16352569f96a32e7eae337 Mon Sep 17 00:00:00 2001 From: Ievgen Ganakov Date: Fri, 8 Sep 2023 11:52:44 +0200 Subject: [PATCH 1/4] tlv: add tlv_value_ptr_get() function Add function which retrieves pointer to the TLV structure value of the specified type Signed-off-by: Ievgen Ganakov --- src/include/sof/tlv.h | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) 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__ */ From 791c163f6efd671b71337f60fe36fe093a66e092 Mon Sep 17 00:00:00 2001 From: Ievgen Ganakov Date: Fri, 26 Jan 2024 14:00:00 +0100 Subject: [PATCH 2/4] ipc4: add host_dma_config array Add host_dma_config array to be able to keep multiple DMA tlv pointers. This change is needed to enable SNDW FW aggregation using HD-A DMA on LNL Signed-off-by: Ievgen Ganakov --- src/include/sof/audio/ipc-config.h | 3 ++- src/include/sof/ipc/topology.h | 1 - src/ipc/ipc4/dai.c | 8 ++++---- src/ipc/ipc4/helper.c | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) 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..3664e61d7ff7 100644 --- a/src/include/sof/ipc/topology.h +++ b/src/include/sof/ipc/topology.h @@ -60,7 +60,6 @@ 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); - #else #error "No or invalid IPC MAJOR version selected." #endif diff --git a/src/ipc/ipc4/dai.c b/src/ipc/ipc4/dai.c index 69a4bdf0569a..bb47a6172811 100644 --- a/src/ipc/ipc4/dai.c +++ b/src/ipc/ipc4/dai.c @@ -44,7 +44,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 +57,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,8 +79,8 @@ 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: diff --git a/src/ipc/ipc4/helper.c b/src/ipc/ipc4/helper.c index 0bd224dec819..d0fa262c76c8 100644 --- a/src/ipc/ipc4/helper.c +++ b/src/ipc/ipc4/helper.c @@ -1043,7 +1043,7 @@ 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; } From 0785ac636b9b97535d079617da4d42e11f2df3ae Mon Sep 17 00:00:00 2001 From: Ievgen Ganakov Date: Fri, 26 Jan 2024 14:38:35 +0100 Subject: [PATCH 3/4] ipc4: add helper function to parse dma config Add helper function to parse muiltiple DMA config tlv structures added to copier Init Instance IPC in case of SNDW FW aggregation. To be able to find correct config we need to iterate over the sequence of DMA tlv with the same tlv type. Thus, we need to check if device_address value (which contains PDI) is equal to device_id parameter (passed with alh_id value). Signed-off-by: Ievgen Ganakov --- src/include/sof/ipc/topology.h | 2 ++ src/ipc/ipc4/helper.c | 30 ++++++++++++++++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/src/include/sof/ipc/topology.h b/src/include/sof/ipc/topology.h index 3664e61d7ff7..86a6bebdb6f2 100644 --- a/src/include/sof/ipc/topology.h +++ b/src/include/sof/ipc/topology.h @@ -60,6 +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/ipc/ipc4/helper.c b/src/ipc/ipc4/helper.c index d0fa262c76c8..285a9bee93b4 100644 --- a/src/ipc/ipc4/helper.c +++ b/src/ipc/ipc4/helper.c @@ -44,6 +44,7 @@ #include #include #include +#include #include #include @@ -1048,6 +1049,35 @@ int ipc4_find_dma_config(struct ipc_config_dai *dai, uint8_t *data_buffer, uint3 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) { From 3787071ff831ecfade9c357c4e27d1b71bfc9808 Mon Sep 17 00:00:00 2001 From: Ievgen Ganakov Date: Fri, 8 Sep 2023 12:19:56 +0200 Subject: [PATCH 4/4] copier: dai: use HD-A dai to configure soundwire on LNL Since LNL soundwire uses HD-A DMA to transfer data. Add LNL specific configuration to select HD-A DMA in case of SoundWire audio interface. Refactor copier dai code for sndw/alh node id type. Add support for sndw link aggregation mode for LNL platform based on DMA config being sent during Copier Init Instance IPC. Signed-off-by: Ievgen Ganakov --- src/audio/copier/copier_dai.c | 145 ++++++++++++++++++++++++---------- src/include/ipc4/alh.h | 8 ++ src/ipc/ipc4/dai.c | 24 ++++++ 3 files changed, 134 insertions(+), 43 deletions(-) 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/ipc/ipc4/dai.c b/src/ipc/ipc4/dai.c index bb47a6172811..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" @@ -84,6 +85,29 @@ int dai_config_dma_channel(struct dai_data *dd, struct comp_dev *dev, const void #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: