Skip to content

Commit 4d5e735

Browse files
committed
zephyr: test: userspace: add tests for using Intel HDA DMA from user-space
Test the SOF DMA interface from a user-space thread. Use all the exported functionality using SOF_DMA_DEV_HOST. This test is using the zephyr/soc/intel/intel_adsp/tools/cavstool.py as the host-side tool. This is required as the DMA programming is split between host and DSP side and test sequences need a test endpoint on both sides. Signed-off-by: Kai Vehmanen <kai.vehmanen@linux.intel.com>
1 parent 1cec0c2 commit 4d5e735

File tree

3 files changed

+258
-0
lines changed

3 files changed

+258
-0
lines changed

zephyr/test/CMakeLists.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,9 @@ if (CONFIG_SOF_BOOT_TEST)
33
vmh.c
44
)
55
endif()
6+
7+
if (CONFIG_SOF_BOOT_TEST_STANDALONE)
8+
if (CONFIG_DT_HAS_INTEL_ADSP_HDA_HOST_IN_ENABLED AND CONFIG_USERSPACE)
9+
zephyr_library_sources(userspace/test_intel_hda_dma.c)
10+
endif()
11+
endif()

zephyr/test/userspace/README.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
intel_hda_dma test
2+
------------------
3+
4+
This is a standalone test to exercise the Intel HDA DMA host interface
5+
from a userspace Zephyr thread.
6+
Build with ("ptl" example):
7+
8+
./scripts/xtensa-build-zephyr.py --cmake-args=-DCONFIG_SOF_BOOT_TEST_STANDALONE=y \
9+
-o app/userspace_overlay.conf -o app/winconsole_overlay.conf ptl
10+
11+
Running test:
12+
- Copy resulting firmware (sof-ptl.ri) to device under test.
13+
- Boot and run the test with cavstool.py:
14+
sudo ./cavstool.py sof-ptl.ri
15+
- Test results printed to cavstool.py
16+
17+
References to related assets in Zephyr codebase:
18+
- cavstool.py
19+
- zephyr/soc/intel/intel_adsp/tools/cavstool.py
20+
- HD DMA tests in Zephyr
21+
- zephyr/tests/boards/intel_adsp/hda/
22+
- larger set in kernel space, using DMA interface directly without SOF dependencies
Lines changed: 230 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,230 @@
1+
// SPDX-License-Identifier: BSD-3-Clause
2+
/*
3+
* Copyright(c) 2025 Intel Corporation.
4+
*/
5+
6+
/*
7+
* Test case for user-space use of the SOF DMA interface. The tests
8+
* transfer data from DSP to host using the host HD DMA instance.
9+
* The test uses the cavstool.py infrastructure to perform host side
10+
* programming of the HDA DMA, and to verify the transferred data.
11+
*
12+
* This test is based on the Zephyr kernel tests for Intel HD DMA
13+
* driver (zephyr/tests/boards/intel_adsp/hda/) written by Tom
14+
* Burdick. This test performs only subset of flows. Driver testing
15+
* should primarily done with the Zephyr kernel tests and this test
16+
* is solely to test the added syscall layer added in SOF.
17+
*/
18+
19+
#include <sof/boot_test.h>
20+
21+
#include <zephyr/kernel.h>
22+
#include <zephyr/ztest.h>
23+
#include <zephyr/logging/log.h>
24+
#include <sof/lib/dma.h>
25+
#include <sof/sof_syscall.h>
26+
27+
LOG_MODULE_DECLARE(sof_boot_test, LOG_LEVEL_DBG);
28+
29+
#define USER_STACKSIZE 2048
30+
#define TEST_BUF_SIZE 256
31+
#define TEST_CHANNEL 0
32+
33+
static struct k_thread user_thread;
34+
static K_THREAD_STACK_DEFINE(user_stack, USER_STACKSIZE);
35+
36+
K_SEM_DEFINE(ipc_sem_wake_user, 0, 1);
37+
K_SEM_DEFINE(ipc_sem_wake_kernel, 0, 1);
38+
39+
static void intel_hda_dma_user(void *p1, void *p2, void *p3)
40+
{
41+
uint32_t addr_align = 0xdeadabba;
42+
struct sof_dma *dma;
43+
struct dma_config config;
44+
struct dma_block_config dma_block_cfg;
45+
struct dma_status stat;
46+
uint8_t data_buf[TEST_BUF_SIZE];
47+
int err, channel;
48+
49+
__ASSERT(k_is_user_context(), "isn't user");
50+
51+
LOG_INF("SOF thread %s (%s)",
52+
k_is_user_context() ? "UserSpace!" : "privileged mode.",
53+
CONFIG_BOARD_TARGET);
54+
55+
/*
56+
* note: this gets a pointer to kernel memory this thread
57+
* cannot access
58+
*/
59+
dma = sof_dma_get(SOF_DMA_DIR_LMEM_TO_HMEM, 0, SOF_DMA_DEV_HOST, SOF_DMA_ACCESS_SHARED);
60+
61+
#if NEEDS_FAULT_HANDLING
62+
/* test passing a invalid kernel handle */
63+
channel = sof_dma_request_channel(dma + 0x4000, 0);
64+
LOG_INF("sof_dma_request_channel/invalid: ret %d", channel);
65+
__ASSERT(channel == -ENOENT, "sof-dma-request-channel with invalid DMA handle");
66+
#endif
67+
68+
k_sem_take(&ipc_sem_wake_user, K_FOREVER);
69+
LOG_INF("configure DMA channel");
70+
71+
channel = sof_dma_request_channel(dma, TEST_CHANNEL);
72+
LOG_INF("sof_dma_request_channel: ret %d", channel);
73+
74+
err = sof_dma_get_attribute(dma, DMA_ATTR_BUFFER_ADDRESS_ALIGNMENT,
75+
&addr_align);
76+
__ASSERT(!err, "dma-attr-alignment error");
77+
__ASSERT(addr_align != 0xdeadabba, "dma-attr-alignment not reported");
78+
LOG_INF("DMA addr alignment %u", addr_align);
79+
80+
/* set up a DMA transfer */
81+
memset(&dma_block_cfg, 0, sizeof(dma_block_cfg));
82+
dma_block_cfg.dest_address = 0; /* host fifo */
83+
dma_block_cfg.source_address = (uintptr_t)data_buf;
84+
dma_block_cfg.block_size = sizeof(data_buf);
85+
86+
/*
87+
* fill data ramp, this payload is expected by host test
88+
* harness
89+
*/
90+
for (uint32_t i = 0; i < TEST_BUF_SIZE; i++) {
91+
data_buf[i] = i & 0xff;
92+
}
93+
sys_cache_data_flush_range(data_buf, sizeof(data_buf));
94+
95+
memset(&config, 0, sizeof(config));
96+
config.channel_direction = MEMORY_TO_HOST;
97+
config.block_count = 1;
98+
config.head_block = &dma_block_cfg;
99+
100+
err = sof_dma_config(dma, channel, &config);
101+
__ASSERT(!err, "dma-config error");
102+
LOG_INF("sof_dma_config: success");
103+
104+
err = sof_dma_start(dma, channel);
105+
LOG_INF("sof_dma_start: ret %d", err);
106+
107+
k_sem_give(&ipc_sem_wake_kernel);
108+
LOG_INF("setup ready, waking for kernel to configure host-side of the test");
109+
k_sem_take(&ipc_sem_wake_user, K_FOREVER);
110+
LOG_INF("start DMA test and transfer data");
111+
112+
err = sof_dma_get_status(dma, channel, &stat);
113+
LOG_INF("sof_dma_get_status start: ret %d pend %u free %u", err,
114+
stat.pending_length, stat.free);
115+
116+
err = sof_dma_reload(dma, channel, sizeof(data_buf));
117+
LOG_INF("sof_dma_reload: ret %d", err);
118+
119+
for (int i = 0; stat.pending_length < TEST_BUF_SIZE; i++) {
120+
err = sof_dma_get_status(dma, channel, &stat);
121+
LOG_INF("sof_dma_get_status %d: ret %d pend %u free %u", i, err,
122+
stat.pending_length, stat.free);
123+
124+
__ASSERT(i < 100, "DMA transfer completes in 100usec");
125+
126+
/* let DMA transfer complete */
127+
k_sleep(K_USEC(1));
128+
}
129+
130+
err = sof_dma_get_status(dma, channel, &stat);
131+
LOG_INF("sof_dma_get_status end: ret %d pend %u free %u", err,
132+
stat.pending_length, stat.free);
133+
134+
LOG_INF("transfer done, asking host to validate output");
135+
k_sem_give(&ipc_sem_wake_kernel);
136+
k_sem_take(&ipc_sem_wake_user, K_FOREVER);
137+
LOG_INF("test done, cleaning up resources");
138+
139+
err = sof_dma_stop(dma, channel);
140+
LOG_INF("sof_dma_stop: ret %d", err);
141+
142+
sof_dma_release_channel(dma, channel);
143+
LOG_INF("sof_dma_release_channel: success");
144+
145+
sof_dma_put(dma);
146+
LOG_INF("sof_dma_put: success");
147+
k_sem_give(&ipc_sem_wake_kernel);
148+
}
149+
150+
#define IPC_TIMEOUT K_MSEC(1500)
151+
#define DMA_BUF_SIZE 256
152+
153+
#define ALIGNMENT DMA_BUF_ADDR_ALIGNMENT(DT_NODELABEL(hda_host_in))
154+
static __aligned(ALIGNMENT) uint8_t dma_buf[DMA_BUF_SIZE];
155+
156+
#include <intel_adsp_hda.h>
157+
#include <../../../../zephyr/tests/boards/intel_adsp/hda/src/tests.h>
158+
159+
static int msg_validate_res;
160+
161+
static bool ipc_message(const struct device *dev, void *arg,
162+
uint32_t data, uint32_t ext_data)
163+
{
164+
LOG_DBG("HDA message received, data %u, ext_data %u", data, ext_data);
165+
msg_validate_res = ext_data;
166+
return true;
167+
}
168+
169+
static void intel_hda_dma_kernel(void)
170+
{
171+
const struct device *dma;
172+
173+
LOG_INF("run %s with buffer at address %p, size %d",
174+
__func__, (void *)dma_buf, DMA_BUF_SIZE);
175+
176+
intel_adsp_ipc_set_message_handler(INTEL_ADSP_IPC_HOST_DEV, ipc_message, NULL);
177+
178+
k_thread_create(&user_thread, user_stack, USER_STACKSIZE,
179+
intel_hda_dma_user, NULL, NULL, NULL,
180+
-1, K_USER, K_FOREVER);
181+
182+
k_thread_access_grant(&user_thread, &ipc_sem_wake_user);
183+
k_thread_access_grant(&user_thread, &ipc_sem_wake_kernel);
184+
185+
dma = DEVICE_DT_GET(DT_NODELABEL(hda_host_in));
186+
k_thread_access_grant(&user_thread, dma);
187+
188+
hda_ipc_msg(INTEL_ADSP_IPC_HOST_DEV, IPCCMD_HDA_RESET,
189+
TEST_CHANNEL, IPC_TIMEOUT);
190+
191+
hda_ipc_msg(INTEL_ADSP_IPC_HOST_DEV, IPCCMD_HDA_CONFIG,
192+
TEST_CHANNEL | (DMA_BUF_SIZE << 8), IPC_TIMEOUT);
193+
194+
k_thread_start(&user_thread);
195+
196+
LOG_INF("user started, waiting for it to be ready");
197+
198+
k_sem_give(&ipc_sem_wake_user);
199+
k_sem_take(&ipc_sem_wake_kernel, K_FOREVER);
200+
201+
LOG_INF("user ready, starting HDA test");
202+
203+
hda_ipc_msg(INTEL_ADSP_IPC_HOST_DEV, IPCCMD_HDA_START, TEST_CHANNEL, IPC_TIMEOUT);
204+
205+
k_sem_give(&ipc_sem_wake_user);
206+
k_sem_take(&ipc_sem_wake_kernel, K_FOREVER);
207+
208+
LOG_INF("transfer done, validating results");
209+
210+
hda_ipc_msg(INTEL_ADSP_IPC_HOST_DEV, IPCCMD_HDA_VALIDATE, TEST_CHANNEL,
211+
IPC_TIMEOUT);
212+
213+
hda_dump_regs(HOST_OUT, HDA_REGBLOCK_SIZE, TEST_CHANNEL, "host reset");
214+
215+
k_sem_give(&ipc_sem_wake_user);
216+
k_sem_take(&ipc_sem_wake_kernel, K_FOREVER);
217+
218+
LOG_INF("test done, terminate user");
219+
220+
k_thread_join(&user_thread, K_FOREVER);
221+
222+
__ASSERT(msg_validate_res == 1, "DMA transferred data invalid payload");
223+
}
224+
225+
ZTEST(sof_boot, userspace_intel_hda_dma)
226+
{
227+
intel_hda_dma_kernel();
228+
229+
ztest_test_pass();
230+
}

0 commit comments

Comments
 (0)