From 54646647c4936b3dfde57ddfda7b379756af4df6 Mon Sep 17 00:00:00 2001 From: Laurentiu Mihalcea Date: Thu, 23 Nov 2023 15:31:51 +0200 Subject: [PATCH 01/10] ipc3: host-page-table: Add native version for ipc_get_page_descriptors() Since the DMAC is configured in a somewhat different way in the native case vs the non-native case, add a native version of the ipc_get_page_descriptors() function and remove the Zephyr-specific functions from the non-native version of said function. Signed-off-by: Laurentiu Mihalcea --- src/ipc/ipc3/host-page-table.c | 66 +++++++++++++++++++++++++++++++--- 1 file changed, 62 insertions(+), 4 deletions(-) diff --git a/src/ipc/ipc3/host-page-table.c b/src/ipc/ipc3/host-page-table.c index 3019da987be2..414dd4f57ef0 100644 --- a/src/ipc/ipc3/host-page-table.c +++ b/src/ipc/ipc3/host-page-table.c @@ -81,9 +81,70 @@ static int ipc_parse_page_descriptors(uint8_t *page_table, return 0; } +#ifdef CONFIG_ZEPHYR_NATIVE_DRIVERS /* * Copy the audio buffer page tables from the host to the DSP max of 4K. */ +static int ipc_get_page_descriptors(struct dma *dmac, uint8_t *page_table, + struct sof_ipc_host_buffer *ring) +{ + struct dma_config cfg; + struct dma_block_config blk; + int channel, ret; + uint32_t align; + + /* TODO: ATM, all of this is somewhat NXP-specific as the + * DMA driver used by NXP performs the transfer via the + * reload() function which may not be the case for all + * vendors. + */ + if (!IS_ENABLED(CONFIG_DMA_NXP_SOF_HOST_DMA)) { + tr_err(&ipc_tr, "DMAC not supported for page transfer"); + return -ENOTSUP; + } + + channel = dma_request_channel(dmac->z_dev, 0); + if (channel < 0) { + tr_err(&ipc_tr, "failed to request channel"); + return channel; + } + + /* fetch copy alignment */ + ret = dma_get_attribute(dmac->z_dev, DMA_ATTR_COPY_ALIGNMENT, &align); + if (ret < 0) { + tr_err(&ipc_tr, "failed to fetch copy alignment"); + goto out_release_channel; + } + + /* prepare DMA configuration */ + cfg.source_data_size = sizeof(uint32_t); + cfg.dest_data_size = sizeof(uint32_t); + cfg.block_count = 1; + cfg.head_block = &blk; + cfg.channel_direction = HOST_TO_MEMORY; + + blk.source_address = POINTER_TO_UINT(host_to_local(ring->phy_addr)); + blk.dest_address = POINTER_TO_UINT(page_table); + blk.block_size = ALIGN_UP(SOF_DIV_ROUND_UP(ring->pages * 20, 8), align); + + /* commit configuration */ + ret = dma_config(dmac->z_dev, channel, &cfg); + if (ret < 0) { + tr_err(&ipc_tr, "failed to commit configuration"); + goto out_release_channel; + } + + /* do transfer */ + ret = dma_reload(dmac->z_dev, channel, 0, 0, 0); + if (ret < 0) + tr_err(&ipc_tr, "failed to perform transfer"); + +out_release_channel: + dma_release_channel(dmac->z_dev, channel); + + return ret; +} +#else static int ipc_get_page_descriptors(struct dma *dmac, uint8_t *page_table, struct sof_ipc_host_buffer *ring) { @@ -115,11 +176,7 @@ static int ipc_get_page_descriptors(struct dma *dmac, uint8_t *page_table, /* source buffer size is always PAGE_SIZE bytes */ /* 20 bits for each page, round up to minimum DMA copy size */ -#if CONFIG_ZEPHYR_NATIVE_DRIVERS - ret = dma_get_attribute(dmac->z_dev, DMA_ATTR_COPY_ALIGNMENT, &dma_copy_align); -#else ret = dma_get_attribute_legacy(dmac, DMA_ATTR_COPY_ALIGNMENT, &dma_copy_align); -#endif if (ret < 0) { tr_err(&ipc_tr, "ipc_get_page_descriptors(): dma_get_attribute() failed"); goto out; @@ -147,6 +204,7 @@ static int ipc_get_page_descriptors(struct dma *dmac, uint8_t *page_table, dma_channel_put_legacy(chan); return ret; } +#endif /* CONFIG_ZEPHYR_NATIVE_DRIVERS */ int ipc_process_host_buffer(struct ipc *ipc, struct sof_ipc_host_buffer *ring, From 136e1842942d8ae9b9d04c85df18f8086cc64361 Mon Sep 17 00:00:00 2001 From: Laurentiu Mihalcea Date: Thu, 23 Nov 2023 16:46:30 +0200 Subject: [PATCH 02/10] audio: host-zephyr: Update DMA block configuration when re-configuring With every copy() operation, the source and destination addresses keep getting modified. As such, for each re-configuration performed during host_copy_one_shot(), the HOST DMAC needs to be made aware of these changes. This commit changes host_copy_one_shot() such that on each dma_config() call the DMAC driver receives the updated addresses and size. Signed-off-by: Laurentiu Mihalcea --- src/audio/host-zephyr.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/audio/host-zephyr.c b/src/audio/host-zephyr.c index 864d2ee2c41f..551602c46114 100644 --- a/src/audio/host-zephyr.c +++ b/src/audio/host-zephyr.c @@ -203,6 +203,7 @@ static uint32_t host_get_copy_bytes_one_shot(struct host_data *hd) static int host_copy_one_shot(struct host_data *hd, struct comp_dev *dev, copy_callback_t cb) { uint32_t copy_bytes; + struct dma_sg_elem *local_elem = hd->config.elem_array.elems; int ret = 0; comp_dbg(dev, "host_copy_one_shot()"); @@ -213,6 +214,13 @@ static int host_copy_one_shot(struct host_data *hd, struct comp_dev *dev, copy_c return ret; } + /* SRC/DEST addresses have changed so the DMAC needs + * to be re-configured. + */ + hd->z_config.head_block->source_address = local_elem->src; + hd->z_config.head_block->dest_address = local_elem->dest; + hd->z_config.head_block->block_size = local_elem->size; + /* reconfigure transfer */ ret = dma_config(hd->chan->dma->z_dev, hd->chan->index, &hd->z_config); if (ret < 0) { From 987a59ea4cb40b9b8fd921966c4681ca8a2538b8 Mon Sep 17 00:00:00 2001 From: Laurentiu Mihalcea Date: Thu, 23 Nov 2023 16:48:58 +0200 Subject: [PATCH 03/10] audio: host-zephyr: Set SRC/DEST addresses during host_common_params() Source and destination addresses cannot be NULL. As such, set them to the values found in the first element of hd->config's elem_array. This is fine to do because the host component uses only 1 block. Signed-off-by: Laurentiu Mihalcea --- src/audio/host-zephyr.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/audio/host-zephyr.c b/src/audio/host-zephyr.c index 551602c46114..6f9d00a32401 100644 --- a/src/audio/host-zephyr.c +++ b/src/audio/host-zephyr.c @@ -935,10 +935,12 @@ int host_common_params(struct host_data *hd, struct comp_dev *dev, case DMA_DIR_LMEM_TO_HMEM: dma_cfg->channel_direction = MEMORY_TO_HOST; dma_block_cfg->source_address = buffer_addr; + dma_block_cfg->dest_address = hd->config.elem_array.elems[0].dest; break; case DMA_DIR_HMEM_TO_LMEM: dma_cfg->channel_direction = HOST_TO_MEMORY; dma_block_cfg->dest_address = buffer_addr; + dma_block_cfg->source_address = hd->config.elem_array.elems[0].src; break; } From ad6bcbd6c3ce12820d5afd40bf92fef4b75e80d2 Mon Sep 17 00:00:00 2001 From: Laurentiu Mihalcea Date: Thu, 23 Nov 2023 16:52:49 +0200 Subject: [PATCH 04/10] lib: dai: Add entry for NXP's SAI Since the Zephyr SAI driver is now enabled, add an entry in the zephyr_dev array, which will resolve to multiple "struct device *", one for each SAI node specified in the DTS. Signed-off-by: Laurentiu Mihalcea --- src/lib/dai.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/lib/dai.c b/src/lib/dai.c index 8ce98521508a..e056c873a7f8 100644 --- a/src/lib/dai.c +++ b/src/lib/dai.c @@ -145,6 +145,9 @@ const struct device *zephyr_dev[] = { #if CONFIG_DAI_INTEL_HDA DT_FOREACH_STATUS_OKAY(intel_hda_dai, GET_DEVICE_LIST) #endif +#if CONFIG_DAI_NXP_SAI + DT_FOREACH_STATUS_OKAY(nxp_dai_sai, GET_DEVICE_LIST) +#endif }; static const struct device *dai_get_zephyr_device(uint32_t type, uint32_t index) From bc832eeed46d39815cc1e92859ef5fe98c5f7950 Mon Sep 17 00:00:00 2001 From: Laurentiu Mihalcea Date: Fri, 24 Nov 2023 11:06:48 +0200 Subject: [PATCH 05/10] lib: dma: Add entries for i.MX93's EDMA and HOST DMAs Since the EDMA and HOST DMA nodes have been introduced to the i.MX93 overlay, add entries in the dma array which will create a struct dma for each of these DMACs. Signed-off-by: Laurentiu Mihalcea --- zephyr/lib/dma.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/zephyr/lib/dma.c b/zephyr/lib/dma.c index 5bb5b964151b..ec2ac28a2a5f 100644 --- a/zephyr/lib/dma.c +++ b/zephyr/lib/dma.c @@ -107,6 +107,29 @@ SHARED_DATA struct dma dma[] = { .z_dev = DEVICE_DT_GET(DT_NODELABEL(hda_link_out)), }, #endif +#ifdef CONFIG_SOC_SERIES_MIMX9_A55 +{ + .plat_data = { + .dir = DMA_DIR_MEM_TO_DEV | DMA_DIR_DEV_TO_MEM, + .devs = DMA_DEV_SAI, + /* TODO: might be worth using `dma-channels` here + * (needs to become a mandatory property) + */ + .channels = 64, + .period_count = 2, + }, + .z_dev = DEVICE_DT_GET(DT_NODELABEL(edma4)), +}, +{ + .plat_data = { + .dir = DMA_DIR_HMEM_TO_LMEM | DMA_DIR_LMEM_TO_HMEM, + .devs = DMA_DEV_HOST, + .channels = DT_PROP(DT_NODELABEL(host_dma), dma_channels), + .period_count = 2, + }, + .z_dev = DEVICE_DT_GET(DT_NODELABEL(host_dma)), +}, +#endif /* CONFIG_SOC_SERIES_MIMX9_A55 */ }; const struct dma_info lib_dma = { From 0c666ed9184e97ee80be1e3dca4bdef6617806e3 Mon Sep 17 00:00:00 2001 From: Laurentiu Mihalcea Date: Fri, 24 Nov 2023 11:09:19 +0200 Subject: [PATCH 06/10] audio: dai-zephyr: Allow dai_set_config() to work for NXP's SAI Since now there's a Zephyr driver for NXP's SAI, the dai_set_config() should be modified to also allow the configuration of the SAI. As such, this commit introduces a new case for NXP's SAI that does exactly that. Signed-off-by: Laurentiu Mihalcea --- src/audio/dai-zephyr.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/audio/dai-zephyr.c b/src/audio/dai-zephyr.c index 5ea7869ca396..3b2bfc48f439 100644 --- a/src/audio/dai-zephyr.c +++ b/src/audio/dai-zephyr.c @@ -161,6 +161,10 @@ int dai_set_config(struct dai *dai, struct ipc_config_dai *common_config, cfg.type = is_blob ? DAI_INTEL_HDA_NHLT : DAI_INTEL_HDA; cfg_params = is_blob ? spec_config : &sof_cfg->hda; break; + case SOF_DAI_IMX_SAI: + cfg.type = DAI_IMX_SAI; + cfg_params = &sof_cfg->sai; + break; default: return -EINVAL; } From 6c3ce5b5d073770245805b4df5d9b44dd09be0bf Mon Sep 17 00:00:00 2001 From: Laurentiu Mihalcea Date: Fri, 24 Nov 2023 11:16:21 +0200 Subject: [PATCH 07/10] audio: dai-zephyr: Set source/destination address adjustment Some DMACs (e.g: NXP's EDMA) can automatically adjust the source and destination addresses upon transfer completion. As such, we need to indicate how the adjustment should be performed. In the case of playback, the source address should be decremented, while in the case of capture, the destination address should be decremented. Signed-off-by: Laurentiu Mihalcea --- src/audio/dai-zephyr.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/audio/dai-zephyr.c b/src/audio/dai-zephyr.c index 3b2bfc48f439..134f73f71cd3 100644 --- a/src/audio/dai-zephyr.c +++ b/src/audio/dai-zephyr.c @@ -718,6 +718,13 @@ static int dai_set_dma_config(struct dai_data *dd, struct comp_dev *dev) dma_block_cfg->block_size = config->elem_array.elems[i].size; dma_block_cfg->source_address = config->elem_array.elems[i].src; dma_block_cfg->dest_address = config->elem_array.elems[i].dest; + if (dev->direction == SOF_IPC_STREAM_PLAYBACK) { + dma_block_cfg->source_addr_adj = DMA_ADDR_ADJ_DECREMENT; + dma_block_cfg->dest_addr_adj = DMA_ADDR_ADJ_INCREMENT; + } else { + dma_block_cfg->source_addr_adj = DMA_ADDR_ADJ_INCREMENT; + dma_block_cfg->dest_addr_adj = DMA_ADDR_ADJ_DECREMENT; + } prev = dma_block_cfg; prev->next_block = ++dma_block_cfg; } From 1bf6b8c04a686d96d003b1b134a71e0e5860d918 Mon Sep 17 00:00:00 2001 From: Laurentiu Mihalcea Date: Fri, 24 Nov 2023 11:17:48 +0200 Subject: [PATCH 08/10] audio: dai-zephyr: Add state change on DAI trigger Currently, the DAI component's state is not updated on dai_trigger() operation, which leads to pipeline_comp_copy() skipping the dai_copy() operation (since the DAI component never transitions to the ACTIVE state). To fix this, add a state transition in dai_comp_trigger_internal(). Also, make sure not to trigger the DAI component if already in the requested state. Signed-off-by: Laurentiu Mihalcea --- src/audio/dai-zephyr.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/audio/dai-zephyr.c b/src/audio/dai-zephyr.c index 134f73f71cd3..349e1f19ef19 100644 --- a/src/audio/dai-zephyr.c +++ b/src/audio/dai-zephyr.c @@ -1053,6 +1053,11 @@ static int dai_comp_trigger_internal(struct dai_data *dd, struct comp_dev *dev, comp_dbg(dev, "dai_comp_trigger_internal(), command = %u", cmd); +#ifdef CONFIG_IPC_MAJOR_3 + if (dev->state == comp_get_requested_state(cmd)) + return PPL_STATUS_PATH_STOP; +#endif /* CONFIG_IPC_MAJOR_3 */ + switch (cmd) { case COMP_TRIGGER_START: comp_dbg(dev, "dai_comp_trigger_internal(), START"); @@ -1151,6 +1156,17 @@ static int dai_comp_trigger_internal(struct dai_data *dd, struct comp_dev *dev, break; } +#ifdef CONFIG_IPC_MAJOR_3 + /* TODO: investigate why making this IPC version-agnostic + * breaks some Intel tests and check if doing so would be + * possible (or even make sense) on Intel platforms. + * + * See issue #8920 for details. + */ + if (!ret) + return comp_set_state(dev, cmd); +#endif /* CONFIG_IPC_MAJOR_3 */ + return ret; } From e347427bc27f2cd0f09742a8319c0533dd8b8829 Mon Sep 17 00:00:00 2001 From: Laurentiu Mihalcea Date: Thu, 8 Feb 2024 11:48:54 +0200 Subject: [PATCH 09/10] audio: dai-zephyr: add function for computing DMA slot In the case of some DAIs, the DMA slot may be encoded differently in the DAI configuration handshake. As such, we can't count on the fact that the handshake itself can be used as the DMA slot. To fix this, add a function which parses the handshake and allows each DAI to use its own encoding of the DMA slot inside the handshake. Signed-off-by: Laurentiu Mihalcea --- src/audio/dai-zephyr.c | 39 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 37 insertions(+), 2 deletions(-) diff --git a/src/audio/dai-zephyr.c b/src/audio/dai-zephyr.c index 349e1f19ef19..b689d8b1031b 100644 --- a/src/audio/dai-zephyr.c +++ b/src/audio/dai-zephyr.c @@ -569,6 +569,37 @@ static int dai_verify_params(struct dai_data *dd, struct comp_dev *dev, return 0; } +static int dai_get_dma_slot(struct dai_data *dd, struct comp_dev *dev, uint32_t *slot) +{ + struct dai_config cfg; + int ret; + int hs; + + ret = dai_config_get(dd->dai->dev, &cfg, dev->direction); + if (ret < 0) { + comp_err(dev, "failed to fetch DAI configuration"); + return ret; + } + + hs = dai_get_handshake(dd->dai, dev->direction, dd->stream_id); + if (ret < 0) { + comp_err(dev, "failed to fetch DAI handshake"); + return ret; + } + + switch (cfg.type) { + case DAI_IMX_SAI: + case DAI_IMX_ESAI: + *slot = (hs & GENMASK(15, 8)) >> 8; + break; + default: + *slot = hs; + break; + } + + return 0; +} + static int dai_set_sg_config(struct dai_data *dd, struct comp_dev *dev, uint32_t period_bytes, uint32_t period_count) { @@ -582,11 +613,15 @@ static int dai_set_sg_config(struct dai_data *dd, struct comp_dev *dev, uint32_t if (dev->direction == SOF_IPC_STREAM_PLAYBACK) { dd->process = pcm_get_conversion_function(local_fmt, dma_fmt); config->direction = DMA_DIR_MEM_TO_DEV; - config->dest_dev = dai_get_handshake(dd->dai, dev->direction, dd->stream_id); + err = dai_get_dma_slot(dd, dev, &config->dest_dev); + if (err < 0) + return err; } else { dd->process = pcm_get_conversion_function(dma_fmt, local_fmt); config->direction = DMA_DIR_DEV_TO_MEM; - config->src_dev = dai_get_handshake(dd->dai, dev->direction, dd->stream_id); + err = dai_get_dma_slot(dd, dev, &config->src_dev); + if (err < 0) + return err; } if (!dd->process) { From a0a034af71c1420f994ef0992d7216c6a6363228 Mon Sep 17 00:00:00 2001 From: Laurentiu Mihalcea Date: Thu, 8 Feb 2024 11:51:43 +0200 Subject: [PATCH 10/10] ipc3: dai: change channel decoding function for native SAI and ESAI The native SAI and ESAI drivers use a different handshake encoding. As such, when using native Zephyr drivers use a different function for decoding the channel from the handshake. Signed-off-by: Laurentiu Mihalcea --- src/ipc/ipc3/dai.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/ipc/ipc3/dai.c b/src/ipc/ipc3/dai.c index 697b00c960ca..8586dc779cff 100644 --- a/src/ipc/ipc3/dai.c +++ b/src/ipc/ipc3/dai.c @@ -63,7 +63,12 @@ int dai_config_dma_channel(struct dai_data *dd, struct comp_dev *dev, const void case SOF_DAI_IMX_ESAI: handshake = dai_get_handshake(dd->dai, dai->direction, dd->stream_id); +/* TODO: remove this when transition to native drivers is complete on all NXP platforms */ +#ifndef CONFIG_ZEPHYR_NATIVE_DRIVERS channel = EDMA_HS_GET_CHAN(handshake); +#else + channel = handshake & GENMASK(7, 0); +#endif /* CONFIG_ZEPHYR_NATIVE_DRIVERS */ break; case SOF_DAI_IMX_MICFIL: channel = dai_get_handshake(dd->dai, dai->direction,