soc/intel_adsp: Low level HDA driver and tests

Adds a header only low level driver for HDA streams along with smoke
tests to ensure basic host in and out stream functionality.

The tests require host side interaction. In cavstool a new HDAStream
class encapsulates somewhat a single stream and its registers. This
is manipulated in the tests using IPC with the Host ensuring that a
specific order of operations is done.

This low level driver allows testing certain hardware configurations
and flows with easy to use register dump debugging. It is not
intended to be the end API an application might use. That would be
a DMA driver using this.

Signed-off-by: Tom Burdick <thomas.burdick@intel.com>
This commit is contained in:
Tom Burdick 2022-03-02 11:43:51 -06:00 committed by Anas Nashif
commit cc6e9c094a
9 changed files with 764 additions and 7 deletions

View file

@ -0,0 +1,7 @@
# SPDX-License-Identifier: Apache-2.0
cmake_minimum_required(VERSION 3.20.0)
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
project(intel_adsp)
target_sources(app PRIVATE src/main.c src/smoke.c)

View file

@ -0,0 +1,2 @@
CONFIG_ZTEST=y
CONFIG_DMA=y

View file

@ -0,0 +1,17 @@
/* Copyright (c) 2022 Intel Corporation
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr.h>
#include <ztest.h>
#include <stdlib.h>
#include "tests.h"
void test_main(void)
{
ztest_test_suite(intel_adsp_hda,
ztest_unit_test(test_hda_host_in_smoke),
ztest_unit_test(test_hda_host_out_smoke)
);
ztest_run_test_suite(intel_adsp_hda);
}

View file

@ -0,0 +1,196 @@
/* Copyright (c) 2022 Intel Corporation
* SPDX-License-Identifier: Apache-2.0
*/
#include "arch/xtensa/cache.h"
#include <kernel.h>
#include <ztest.h>
#include <cavs_ipc.h>
#include <cavs_hda.h>
#include "tests.h"
#define IPC_TIMEOUT K_MSEC(500)
#define STREAM_ID 3U
#define HDA_BUF_SIZE 256
#define TRANSFER_COUNT 8
static __aligned(128) uint8_t hda_buf[HDA_BUF_SIZE];
static volatile int msg_cnt;
static volatile int msg_res;
static bool ipc_message(const struct device *dev, void *arg,
uint32_t data, uint32_t ext_data)
{
printk("HDA message received, data %u, ext_data %u\n", data, ext_data);
msg_res = data;
msg_cnt++;
return true;
}
/*
* Tests host input streams
*
* Note that the order of operations in this test are important and things potentially will not
* work in horrible and unexpected ways if not done as they are here.
*/
void test_hda_host_in_smoke(void)
{
int res;
uint32_t last_msg_cnt;
printk("smoke testing hda with fifo buffer at address %p, size %d\n",
hda_buf, HDA_BUF_SIZE);
cavs_ipc_set_message_handler(CAVS_HOST_DEV, ipc_message, NULL);
printk("Using buffer of size %d at addr %p\n", HDA_BUF_SIZE, hda_buf);
/* setup a ramp in the buffer */
for (uint32_t i = 0; i < HDA_BUF_SIZE; i++) {
hda_buf[i] = i & 0xff;
}
#if (IS_ENABLED(CONFIG_KERNEL_COHERENCE))
zassert_true(arch_mem_coherent(hda_buf), "Buffer is unexpectedly incoherent!");
#else
/* The buffer is in the cached address range and must be flushed */
zassert_false(arch_mem_coherent(hda_buf), "Buffer is unexpectedly coherent!");
z_xtensa_cache_flush(hda_buf, HDA_BUF_SIZE);
#endif
cavs_hda_init(HDA_HOST_IN_BASE, STREAM_ID);
printk("dsp init: "); cavs_hda_dbg("host_in", HDA_HOST_IN_BASE, STREAM_ID);
hda_ipc_msg(CAVS_HOST_DEV, IPCCMD_HDA_RESET, STREAM_ID, IPC_TIMEOUT);
printk("host reset: ");
cavs_hda_dbg("host_in", HDA_HOST_IN_BASE, STREAM_ID);
hda_ipc_msg(CAVS_HOST_DEV, IPCCMD_HDA_CONFIG,
STREAM_ID | (HDA_BUF_SIZE << 8), IPC_TIMEOUT);
printk("host config: ");
cavs_hda_dbg("host_in", HDA_HOST_IN_BASE, STREAM_ID);
res = cavs_hda_set_buffer(HDA_HOST_IN_BASE, STREAM_ID, hda_buf, HDA_BUF_SIZE);
printk("dsp set_buffer: "); cavs_hda_dbg("host_in", HDA_HOST_IN_BASE, STREAM_ID);
zassert_ok(res, "Expected set buffer to succeed");
cavs_hda_enable(HDA_HOST_IN_BASE, STREAM_ID);
printk("dsp enable: "); cavs_hda_dbg("host_in", HDA_HOST_IN_BASE, STREAM_ID);
hda_ipc_msg(CAVS_HOST_DEV, IPCCMD_HDA_START, STREAM_ID, IPC_TIMEOUT);
printk("host start: ");
cavs_hda_dbg("host_in", HDA_HOST_IN_BASE, STREAM_ID);
for (uint32_t i = 0; i < TRANSFER_COUNT; i++) {
cavs_hda_commit(HDA_HOST_IN_BASE, STREAM_ID, HDA_BUF_SIZE);
printk("dsp inc_pos: "); cavs_hda_dbg("host_in", HDA_HOST_IN_BASE, STREAM_ID);
WAIT_FOR(cavs_hda_wp_rp_eq(HDA_HOST_IN_BASE, STREAM_ID));
printk("dsp wp_rp_eq: "); cavs_hda_dbg("host_in", HDA_HOST_IN_BASE, STREAM_ID);
last_msg_cnt = msg_cnt;
hda_ipc_msg(CAVS_HOST_DEV, IPCCMD_HDA_VALIDATE, STREAM_ID,
IPC_TIMEOUT);
WAIT_FOR(msg_cnt > last_msg_cnt);
zassert_true(msg_res == 1,
"Expected data validation to be true from Host");
}
hda_ipc_msg(CAVS_HOST_DEV, IPCCMD_HDA_RESET, STREAM_ID, IPC_TIMEOUT);
cavs_hda_disable(HDA_HOST_IN_BASE, STREAM_ID);
}
/*
* Tests host output streams
*
* Note that the order of operations in this test are important and things potentially will not
* work in horrible and unexpected ways if not done as they are here.
*/
void test_hda_host_out_smoke(void)
{
int res;
bool is_ramp;
printk("smoke testing hda with fifo buffer at address %p, size %d\n",
hda_buf, HDA_BUF_SIZE);
cavs_ipc_set_message_handler(CAVS_HOST_DEV, ipc_message, NULL);
printk("Using buffer of size %d at addr %p\n", HDA_BUF_SIZE, hda_buf);
cavs_hda_init(HDA_HOST_OUT_BASE, STREAM_ID);
printk("dsp init: "); cavs_hda_dbg("host_out", HDA_HOST_OUT_BASE, STREAM_ID);
hda_ipc_msg(CAVS_HOST_DEV, IPCCMD_HDA_RESET, (STREAM_ID + 7), IPC_TIMEOUT);
printk("host reset: ");
cavs_hda_dbg("host_out", HDA_HOST_OUT_BASE, STREAM_ID);
hda_ipc_msg(CAVS_HOST_DEV, IPCCMD_HDA_CONFIG,
(STREAM_ID + 7) | (HDA_BUF_SIZE << 8), IPC_TIMEOUT);
printk("host config: ");
cavs_hda_dbg("host_out", HDA_HOST_OUT_BASE, STREAM_ID);
res = cavs_hda_set_buffer(HDA_HOST_OUT_BASE, STREAM_ID, hda_buf, HDA_BUF_SIZE);
printk("dsp set buffer: "); cavs_hda_dbg("host_out", HDA_HOST_OUT_BASE, STREAM_ID);
zassert_ok(res, "Expected set buffer to succeed");
hda_ipc_msg(CAVS_HOST_DEV, IPCCMD_HDA_START, (STREAM_ID + 7), IPC_TIMEOUT);
printk("host start: ");
cavs_hda_dbg("host_out", HDA_HOST_OUT_BASE, STREAM_ID);
cavs_hda_enable(HDA_HOST_OUT_BASE, STREAM_ID);
printk("dsp enable: ");
cavs_hda_dbg("host_out", HDA_HOST_OUT_BASE, STREAM_ID);
for (uint32_t i = 0; i < TRANSFER_COUNT; i++) {
for (int j = 0; j < HDA_BUF_SIZE; j++) {
hda_buf[j] = 0;
}
hda_ipc_msg(CAVS_HOST_DEV, IPCCMD_HDA_SEND,
(STREAM_ID + 7) | (HDA_BUF_SIZE << 8), IPC_TIMEOUT);
printk("host send: ");
cavs_hda_dbg("host_out", HDA_HOST_OUT_BASE, STREAM_ID);
WAIT_FOR(cavs_hda_buf_full(HDA_HOST_OUT_BASE, STREAM_ID));
printk("dsp wait for full: ");
cavs_hda_dbg("host_out", HDA_HOST_OUT_BASE, STREAM_ID);
#if (IS_ENABLED(CONFIG_KERNEL_COHERENCE))
zassert_true(arch_mem_coherent(hda_buf), "Buffer is unexpectedly incoherent!");
#else
/* The buffer is in the cached address range and must be invalidated
* prior to reading.
*/
zassert_false(arch_mem_coherent(hda_buf), "Buffer is unexpectedly coherent!");
z_xtensa_cache_inv(hda_buf, HDA_BUF_SIZE);
#endif
is_ramp = true;
for (int j = 0; j < HDA_BUF_SIZE; j++) {
/* printk("hda_buf[%d] = %d\n", j, hda_buf[j]); */ /* DEBUG HELPER */
if (hda_buf[j] != j) {
is_ramp = false;
}
}
zassert_true(is_ramp, "Expected data to be a ramp");
cavs_hda_commit(HDA_HOST_OUT_BASE, STREAM_ID, HDA_BUF_SIZE);
printk("dsp inc pos: "); cavs_hda_dbg("host_out", HDA_HOST_OUT_BASE, STREAM_ID);
}
hda_ipc_msg(CAVS_HOST_DEV, IPCCMD_HDA_RESET, (STREAM_ID + 7),
IPC_TIMEOUT);
printk("host reset: ");
cavs_hda_dbg("host_out", HDA_HOST_OUT_BASE, STREAM_ID);
cavs_hda_disable(HDA_HOST_OUT_BASE, STREAM_ID);
printk("dsp disable: "); cavs_hda_dbg("host_out", HDA_HOST_OUT_BASE, STREAM_ID);
}

View file

@ -0,0 +1,24 @@
/* Copyright (c) 2022 Intel Corporation
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef ZEPHYR_TESTS_INTEL_ADSP_TESTS_H
#define ZEPHYR_TESTS_INTEL_ADSP_TESTS_H
#include "sys_clock.h"
#include <cavs_ipc.h>
#include <cavstool.h>
#include <stdint.h>
#include <device.h>
#include <ztest.h>
void test_hda_host_in_smoke(void);
void test_hda_host_out_smoke(void);
static inline void hda_ipc_msg(const struct device *dev, uint32_t data,
uint32_t ext, k_timeout_t timeout)
{
zassert_true(cavs_ipc_send_message_sync(dev, data, ext, timeout),
"Unexpected ipc send message failure, try increasing IPC_TIMEOUT");
}
#endif /* ZEPHYR_TESTS_INTEL_ADSP_TESTS_H */

View file

@ -0,0 +1,7 @@
tests:
boards.intel_adsp:
platform_allow: intel_adsp_cavs15 intel_adsp_cavs18 intel_adsp_cavs20 intel_adsp_cavs25
boards.intel_adsp.1cpu:
platform_allow: intel_adsp_cavs15 intel_adsp_cavs18 intel_adsp_cavs20 intel_adsp_cavs25
extra_configs:
- CONFIG_MP_NUM_CPUS=1