From f8a9041bd18b89e1e950baf80357c55cb756ae4c Mon Sep 17 00:00:00 2001 From: Stefan Ghetu Date: Mon, 2 Nov 2026 15:36:13 +0200 Subject: [PATCH 1/3] audio/sof/probe: fix IPC3 probe point buffer lookup ipc_get_comp_by_id() only searches COMP_TYPE_COMPONENT devices. Buffers are COMP_TYPE_BUFFER so they were never found on IPC3, making every PROBE_POINT_ADD return -EINVAL. The subsequent dev->type != COMP_TYPE_BUFFER check was therefore unreachable. Use COMP_TYPE_ANY in probe_point_add() and probe_point_remove() to find buffers while keeping the existing type check intact. Signed-off-by: Stefan Ghetu --- src/probe/probe.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/probe/probe.c b/src/probe/probe.c index 434317ffe093..44a608bd88ed 100644 --- a/src/probe/probe.c +++ b/src/probe/probe.c @@ -1249,7 +1249,7 @@ __cold int probe_point_add(uint32_t count, const struct probe_point *probe) #elif CONFIG_AMD_ACP_HOST_DMA_PROBE dev = ipc_get_buffer_by_id(ipc_get(), buf_id->full_id); #else - dev = ipc_get_comp_by_id(ipc_get(), buf_id->full_id); + dev = ipc_get_comp_dev(ipc_get(), COMP_TYPE_ANY, buf_id->full_id); #endif /* check if buffer exists */ if (!dev) { @@ -1512,7 +1512,7 @@ __cold int probe_point_remove(uint32_t count, const uint32_t *buffer_id) #if CONFIG_AMD_ACP_HOST_DMA_PROBE dev = ipc_get_buffer_by_id(ipc_get(), buffer_id[i]); #else - dev = ipc_get_comp_by_id(ipc_get(), buffer_id[i]); + dev = ipc_get_comp_dev(ipc_get(), COMP_TYPE_ANY, buffer_id[i]); #endif if (dev) { struct comp_buffer *probe_buf = From c955e4ca7b08574f78f068098c20cad7d4adee4d Mon Sep 17 00:00:00 2001 From: Stefan Ghetu Date: Mon, 2 Nov 2026 15:36:40 +0200 Subject: [PATCH 2/3] audio/sof/probe: zero-initialize DMA config structs The Zephyr-native probe_dma_init() left dma_config and dma_block_config uninitialized. Struct fields not explicitly assigned (e.g. dest_address for LMEM_TO_HMEM) contain stack garbage, which causes the Zephyr DMA driver to reject the config call. Add {0} initializers so unused fields default to zero. Signed-off-by: Stefan Ghetu --- src/probe/probe.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/probe/probe.c b/src/probe/probe.c index 44a608bd88ed..ca8a9530bb18 100644 --- a/src/probe/probe.c +++ b/src/probe/probe.c @@ -195,8 +195,8 @@ __cold static int probe_dma_init(struct probe_dma_ext *dma, uint32_t direction) { uint32_t addr_align; uint32_t channel; - struct dma_config dma_cfg; - struct dma_block_config dma_block_cfg; + struct dma_config dma_cfg = {0}; + struct dma_block_config dma_block_cfg = {0}; int err = 0; assert_can_be_cold(); From a638664372a5a1982a02b400209ae530f8962184 Mon Sep 17 00:00:00 2001 From: Stefan Ghetu Date: Mon, 2 Nov 2026 15:37:48 +0200 Subject: [PATCH 3/3] audio/sof/probe: IPC3+Zephyr: direct-write extraction to host ring The i.MX host DMA engine cannot transfer data to Linux RAM, so dma_reload() never delivered probe data. Instead, have the DSP write extraction packets directly into a coherent buffer whose bus address the kernel passes in the PROBE_INIT stream_tag field. Add PROBE_HOST_HDR_SIZE, three IPC3-only fields in probe_dma_ext (host_base/size/wr), probe_host_write() and wire it into probe_task() for CONFIG_IPC_MAJOR_3+ZEPHYR_NATIVE_DRIVERS. Use channel 0 in probe_dma_init() for IPC3 since stream_tag now carries an address, not a channel index. Also enable CONFIG_PROBE=y and CONFIG_PROBE_POINTS_MAX=16 for the i.MX8MP EVK ADSP board. Signed-off-by: Stefan Ghetu --- app/boards/imx8mp_evk_mimx8ml8_adsp.conf | 2 + src/probe/probe.c | 79 +++++++++++++++++++++++- 2 files changed, 80 insertions(+), 1 deletion(-) diff --git a/app/boards/imx8mp_evk_mimx8ml8_adsp.conf b/app/boards/imx8mp_evk_mimx8ml8_adsp.conf index da9e56d759a4..0f94141309e6 100644 --- a/app/boards/imx8mp_evk_mimx8ml8_adsp.conf +++ b/app/boards/imx8mp_evk_mimx8ml8_adsp.conf @@ -6,6 +6,8 @@ CONFIG_DMA=y CONFIG_DMA_NXP_SDMA=y CONFIG_DAI_NXP_MICFIL=y CONFIG_TRACE=n +CONFIG_PROBE=y +CONFIG_PROBE_POINTS_MAX=16 CONFIG_SHARED_INTERRUPTS=y CONFIG_ZEPHYR_NATIVE_DRIVERS=y CONFIG_SAI_HAS_MCLK_CONFIG_OPTION=y diff --git a/src/probe/probe.c b/src/probe/probe.c index ca8a9530bb18..9c56e7fa99cb 100644 --- a/src/probe/probe.c +++ b/src/probe/probe.c @@ -51,8 +51,17 @@ LOG_MODULE_REGISTER(probe, CONFIG_SOF_LOG_LEVEL); #define PROBE_POINT_INVALID 0xFFFFFFFF #define PROBE_BUFFER_LOCAL_SIZE 8192 +/* host buffer layout: u32 write counter in the first bytes, ring data after the header */ +#define PROBE_HOST_HDR_SIZE 4096 #define DMA_ELEM_SIZE 32 +/* i.MX IPC3: the DSP writes extraction data straight into the host ring */ +#if CONFIG_IPC_MAJOR_3 && CONFIG_ZEPHYR_NATIVE_DRIVERS && CONFIG_IMX +#define PROBE_HOST_RING 1 +#else +#define PROBE_HOST_RING 0 +#endif + /** * DMA buffer */ @@ -74,6 +83,11 @@ struct probe_dma_ext { struct dma_sg_config config; /**< DMA SG config */ struct probe_dma_buf dmapb; /**< DMA buffer pointer */ struct dma_copy dc; /**< DMA copy */ +#if PROBE_HOST_RING + uintptr_t host_base; /**< host buffer: header + ring (IPC3) */ + uint32_t host_size; /**< host ring size in bytes (IPC3) */ + uint32_t host_wr; /**< total bytes written to host ring (IPC3) */ +#endif }; /** @@ -201,7 +215,12 @@ __cold static int probe_dma_init(struct probe_dma_ext *dma, uint32_t direction) assert_can_be_cold(); +#if CONFIG_IPC_MAJOR_3 + /* IPC3: stream_tag carries the host buffer address; use host DMA channel 0 */ + channel = 0; +#else channel = ((union ipc4_connector_node_id)dma->stream_tag).f.v_index; +#endif /* request DMA in the dir LMEM->HMEM with shared access */ dma->dc.dmac = sof_dma_get(direction, 0, SOF_DMA_DEV_HOST, @@ -259,6 +278,10 @@ __cold static int probe_dma_init(struct probe_dma_ext *dma, uint32_t direction) case SOF_DMA_DIR_LMEM_TO_HMEM: dma_cfg.channel_direction = MEMORY_TO_HOST; dma_block_cfg.source_address = (uint32_t)dma->dmapb.addr; +#if PROBE_HOST_RING + /* never transferred, probe_host_write() copies; driver wants non-zero */ + dma_block_cfg.dest_address = (uint32_t)dma->dmapb.addr; +#endif break; case SOF_DMA_DIR_HMEM_TO_LMEM: dma_cfg.channel_direction = HOST_TO_MEMORY; @@ -320,6 +343,42 @@ __cold static int probe_dma_deinit(struct probe_dma_ext *dma) return 0; } +#if PROBE_HOST_RING +/* + * Copy extraction data straight into the host ring and publish the new + * write counter. The i.MX host DMA driver can only memcpy inside DSP + * memory, so the DSP writes to the host buffer itself and flushes its + * data cache. + */ +static int probe_host_write(struct probe_dma_ext *dma, const void *src, uint32_t bytes) +{ + uint8_t *ring = (uint8_t *)dma->host_base + PROBE_HOST_HDR_SIZE; + uint32_t off, head; + + if (!dma->host_base || !dma->host_size) + return -EINVAL; + + off = dma->host_wr % dma->host_size; + head = MIN(bytes, dma->host_size - off); + + memcpy(ring + off, src, head); + dcache_writeback_region((__sparse_force void __sparse_cache *)(ring + off), head); + if (bytes > head) { + memcpy(ring, (const uint8_t *)src + head, bytes - head); + dcache_writeback_region((__sparse_force void __sparse_cache *)ring, + bytes - head); + } + + /* host_wr wraps at 2^32; kernel side casts its counter to u32 before subtracting */ + dma->host_wr += bytes; + *(volatile uint32_t *)dma->host_base = dma->host_wr; + dcache_writeback_region((__sparse_force void __sparse_cache *)dma->host_base, + sizeof(uint32_t)); + + return 0; +} +#endif + /* * \brief Probe task for extraction. * @@ -351,7 +410,10 @@ static enum task_state probe_task(void *data) avail = _probe->ext_dma.dmapb.end_addr - _probe->ext_dma.dmapb.r_ptr; if (avail > 0) -#if CONFIG_ZEPHYR_NATIVE_DRIVERS +#if PROBE_HOST_RING + err = probe_host_write(&_probe->ext_dma, + (void *)_probe->ext_dma.dmapb.r_ptr, avail); +#elif CONFIG_ZEPHYR_NATIVE_DRIVERS err = dma_reload(_probe->ext_dma.dc.dmac->z_dev, _probe->ext_dma.dc.chan->index, 0, 0, avail); #else @@ -417,6 +479,15 @@ __cold int probe_init(const struct probe_dma *probe_dma) return -EINVAL; } +#if PROBE_HOST_RING + /* ring must hold a full local buffer and wrap cleanly at 2^32 */ + if (probe_dma && (probe_dma->dma_buffer_size < PROBE_BUFFER_LOCAL_SIZE || + (probe_dma->dma_buffer_size & (probe_dma->dma_buffer_size - 1)))) { + tr_err(&pr_tr, "bad host ring size %u", probe_dma->dma_buffer_size); + return -EINVAL; + } +#endif + /* alloc probes main struct */ sof_get()->probe = rzalloc(SOF_MEM_FLAG_USER, sizeof(*_probe)); @@ -446,6 +517,12 @@ __cold int probe_init(const struct probe_dma *probe_dma) _probe->ext_dma.stream_tag = probe_dma->stream_tag; _probe->ext_dma.dma_buffer_size = probe_dma->dma_buffer_size; +#if PROBE_HOST_RING + /* i.MX: stream_tag holds the physical address of the host ring buffer */ + _probe->ext_dma.host_base = probe_dma->stream_tag; + _probe->ext_dma.host_size = probe_dma->dma_buffer_size; + _probe->ext_dma.host_wr = 0; +#endif err = probe_dma_init(&_probe->ext_dma, SOF_DMA_DIR_LMEM_TO_HMEM); if (err < 0) {