diff --git a/src/audio/dai-zephyr.c b/src/audio/dai-zephyr.c index 5ea7869ca396..b689d8b1031b 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; } @@ -565,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) { @@ -578,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) { @@ -714,6 +753,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; } @@ -1042,6 +1088,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"); @@ -1140,6 +1191,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; } diff --git a/src/audio/host-zephyr.c b/src/audio/host-zephyr.c index 864d2ee2c41f..6f9d00a32401 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) { @@ -927,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; } 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, 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, 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) 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 = {