Skip to content

Commit

Permalink
soc/intel_adsp: Low level HDA driver and tests
Browse files Browse the repository at this point in the history
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 <[email protected]>
  • Loading branch information
teburd authored and nashif committed Apr 1, 2022
1 parent 345a536 commit cc6e9c0
Show file tree
Hide file tree
Showing 9 changed files with 764 additions and 7 deletions.
289 changes: 289 additions & 0 deletions soc/xtensa/intel_adsp/common/include/cavs_hda.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,289 @@
/* Copyright (c) 2022 Intel Corporation
* SPDX-License-Identifier: Apache-2.0
*/

#ifndef ZEPHYR_INCLUDE_CAVS_HDA_H
#define ZEPHYR_INCLUDE_CAVS_HDA_H

#include <arch/xtensa/cache.h>
#include <kernel.h>
#include <device.h>
#include <cavs-shim.h>
#include <cavs-mem.h>

/**
* @brief HDA stream functionality for cAVS
*
* Provides low level calls to support cAVS HDA streams with
* minimal abstraction that allows testing the hardware
* and its demands separately from the intended DMA API
* usage.
*/

#define HDA_HOST_OUT_BASE 0x72800
#define HDA_HOST_IN_BASE 0x72c00
#define HDA_STREAM_COUNT 7
#define HDA_REGBLOCK_SIZE 0x40

/* The read/write positions are masked to 24 bits */
#define HDA_RWP_MASK 0x00FFFFFF

/* Buffers must be 128 byte aligned, this mask enforces that */
#define HDA_ALIGN_MASK 0xFFFFFF80


#define HDA_ADDR(base, stream) ((base) + (stream)*HDA_REGBLOCK_SIZE)


/* Gateway Control and Status Register */
#define DGCS(base, stream) ((volatile uint32_t *)HDA_ADDR(base, stream))
#define DGCS_SCS BIT(31) /* Sample container size */
#define DGCS_GEN BIT(26) /* Gateway Enable */
#define DGCS_L1ETP BIT(25) /* L1 Enter Prevent */
#define DGCS_L1EXP BIT(25) /* L1 Exit Prevent */
#define DGCS_FWCB BIT(23) /* Firmware Control Buffer */
#define DGCS_GBUSY BIT(15) /* Gateway Busy */
#define DGCS_TE BIT(14) /* Transfer Error */
#define DGCS_BSC BIT(11) /* Buffer Segment Completion */
#define DGCS_BOR BIT(10) /* Buffer Overrun */
#define DGCS_BUR BIT(10) /* Buffer Underrun */
#define DGCS_BF BIT(9) /* Buffer Full */
#define DGCS_BNE BIT(8) /* Buffer Not Empty */
#define DGCS_FIFORDY BIT(5) /* Enable FIFO */
#define DGCS_BSCIE BIT(3) /* Buffer Segment Completion Interrupt Enable */

/* Gateway Buffer Base Address */
#define DGBBA(base, stream) ((volatile uint32_t *)(HDA_ADDR(base, stream) + 0x04))

/* Gateway Buffer Size */
#define DGBS(base, stream) ((volatile uint32_t *)(HDA_ADDR(base, stream) + 0x08))

/* Gateway Buffer Position Increment */
#define DGBFPI(base, stream) ((volatile uint32_t *)(HDA_ADDR(base, stream) + 0x0c))

/* Gateway Buffer Read Position */
#define DGBRP(base, stream) ((volatile uint32_t *)(HDA_ADDR(base, stream) + 0x10))

/* Gateway Buffer Write Position */
#define DGBWP(base, stream) ((volatile uint32_t *)(HDA_ADDR(base, stream) + 0x14))

/* Gateway Buffer Segment Position */
#define DGBSP(base, stream) ((volatile uint32_t *)(HDA_ADDR(base, stream) + 0x18))

/* Gateway Minimum Buffer Size */
#define DGMBS(base, stream) ((volatile uint32_t *)(HDA_ADDR(base, stream) + 0x1c))

/* Gateway Linear Link Position Increment */
#define DGLLPI(base, stream) ((volatile uint32_t *)(HDA_ADDR(base, stream) + 0x24))

/* Gateway Linear Position In Buffer Increment */
#define DGLPIBI(base, stream) ((volatile uint32_t *)(HDA_ADDR(base, stream) + 0x28))

/**
* @brief Dump all the useful registers of an HDA stream to printk
*
* This can be invaluable when finding out why HDA isn't doing (or maybe is)
* doing what you want it to do. Macro so you get the file and line
* of the call site included.
*
* @param name String that contains a name of the hda stream (or anything really)
* @param base Base address of the IP register block
* @param sid Stream ID
*/
#define cavs_hda_dbg(name, base, sid) \
printk("%s:%u %s(%u:0x%p), dgcs: 0x%x, dgbba 0x%x, " \
"dgbs %u, dgbrp %u, dgbwp %u, dgbsp %u, " \
"dgmbs %u, dgbllpi 0x%x, dglpibi 0x%x\n", \
__FILE__, __LINE__, name, \
sid, DGCS(base, sid), \
*DGCS(base, sid), \
*DGBBA(base, sid), \
*DGBS(base, sid), \
*DGBRP(base, sid), \
*DGBWP(base, sid), \
*DGBSP(base, sid), \
*DGMBS(base, sid), \
*DGLLPI(base, sid), \
*DGLPIBI(base, sid))



/**
* @brief Initialize an HDA stream for use with the firmware
*
* @param hda Stream set to work with
* @param sid Stream ID
*/
static inline void cavs_hda_init(uint32_t base, uint32_t sid)
{
*DGCS(base, sid) |= DGCS_FWCB;
}

/**
* @brief Set the buffer, size, and element size for an HDA stream
*
* Sanity checks that the buffer address and size are valid and that the
* stream isn't enabled or busy.
*
* Prior to enabling an HDA stream to/from the host this is the minimum configuration
* that is required. It must be set *after* the host has configured its own buffers.
*
*
* @param hda Stream set to work with
* @param sid Stream ID
* @param buf Buffer address to use for the shared FIFO. Must be in L2 and 128 byte aligned.
* @param buf_size Buffer size in bytes Must be 128 byte aligned
*
* @retval -EBUSY if the HDA stream is already enabled
* @retval -EINVAL if the buf is not in L2, buf isn't aligned on 128 byte boundaries
* @retval 0 on Success
*/
static inline int cavs_hda_set_buffer(uint32_t base, uint32_t sid,
uint8_t *buf, uint32_t buf_size)
{
/* While we don't actually care if the pointer is in the cached
* region or not, we do need a consistent address space to check
* against for our assertion. This is cheap.
*/
uint32_t addr = (uint32_t)arch_xtensa_cached_ptr(buf);
uint32_t aligned_addr = addr & HDA_ALIGN_MASK;
uint32_t aligned_size = buf_size & HDA_ALIGN_MASK;

__ASSERT(aligned_addr == addr, "Buffer must be 128 byte aligned");
__ASSERT(aligned_addr >= L2_SRAM_BASE
&& aligned_addr < L2_SRAM_BASE + L2_SRAM_SIZE,
"Buffer must be in L2 address space");
__ASSERT(aligned_size == buf_size,
"Buffer must be 128 byte aligned in size");

__ASSERT(aligned_addr + aligned_size < L2_SRAM_BASE + L2_SRAM_SIZE,
"Buffer must end in L2 address space");

if (*DGCS(base, sid) & DGCS_GEN) {
return -EBUSY;
}

if (*DGCS(base, sid) & DGCS_GBUSY) {
return -EBUSY;
}

*DGBBA(base, sid) = aligned_addr;
*DGBS(base, sid) = aligned_size;

return 0;
}

/**
* @brief Enable the stream
*
* @param hda HDA stream set
* @param sid Stream ID
*/
static inline void cavs_hda_enable(uint32_t base, uint32_t sid)
{
*DGCS(base, sid) |= DGCS_GEN | DGCS_FIFORDY;
}

/**
* @brief Disable stream
*
* @param hda HDA stream set
* @param sid Stream ID
*/
static inline void cavs_hda_disable(uint32_t base, uint32_t sid)
{
*DGCS(base, sid) &= ~(DGCS_GEN | DGCS_FIFORDY);
}

/**
* @brief Determine the number of unused bytes in the buffer
*
* This is useful primarily for a host in (dsp -> host) stream.
*
* @param base Base address of the IP register block
* @param sid Stream ID within the register block
*
* @retval n Number of unused bytes
*/
static inline uint32_t cavs_hda_unused(uint32_t base, uint32_t sid)
{
uint32_t dgcs = *DGCS(base, sid);
uint32_t dgbs = *DGBS(base, sid);

/* Check if buffer is empty */
if ((dgcs & DGCS_BNE) == 0) {
return dgbs;
}

int32_t rp = *DGBRP(base, sid);
int32_t wp = *DGBWP(base, sid);
int32_t size = rp - wp;

if (size <= 0) {
size += dgbs;
}

return size;
}

/**
* @brief Commit a number of bytes that have been transferred
*
* Writes the length to BFPI. For host transfers LLPI and LPIB are
* also written to with the given length.
*
* This then updates the read or write position depending on the direction.
*
* LPIBI writes here can be seen on the host side of the transfer in the
* matching LPIB register.
*
* LLPI seems to, from behavior, inform the hardware to actually read/write
* from the buffer. Without updating BFPI AND LLPI, the transfer doesn't
* happen in testing for host transfers.
*
* @param base Base address of the IP register block
* @param sid Stream ID within the register block
* @param len Len to increment postion by
*/
static inline void cavs_hda_commit(uint32_t base, uint32_t sid, uint32_t len)
{
*DGBFPI(base, sid) = len;
if (base == HDA_HOST_IN_BASE || base == HDA_HOST_OUT_BASE) {
*DGLLPI(base, sid) = len;
*DGLPIBI(base, sid) = len;
}
}

/**
* @brief Read the buffer full bit of the given stream.
*
* @param base Base address of the IP register block
* @param sid Stream ID within the register block
*
* @retval true If the buffer full flag is set
*/
static inline bool cavs_hda_buf_full(uint32_t base, uint32_t sid)
{
return *DGCS(base, sid) & DGCS_BF;
}

/**
* @brief Check if the write and read position are equal
*
* For HDA this does not mean that the buffer is full or empty
* there are bit flags for those cases.
*
* This can let you wait on the hardware to catch up to your
* reads or writes (ex after a cavs_hda_commit)
*
* @param dev HDA Stream device
* @param sid Stream ID
*
* @retval true If the read and write positions are equal
*/
static inline bool cavs_hda_wp_rp_eq(uint32_t base, uint32_t sid)
{
return *DGBWP(base, sid) == *DGBRP(base, sid);
}

#endif /* ZEPHYR_INCLUDE_CAVS_HDA_H */
20 changes: 20 additions & 0 deletions soc/xtensa/intel_adsp/common/include/cavstool.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,26 @@ enum cavstool_cmd {

/* The host copies OUTBOX[ext_data >> 16] to INBOX[ext_data & 0xffff] */
IPCCMD_WINCOPY,

/* The host clears the run bit and resets the HDA stream */
IPCCMD_HDA_RESET,

/* The host configures an HDA stream (with provided buffer size and stream id) */
IPCCMD_HDA_CONFIG,

/* The host runs (sets the SDxCTL.RUN bit) for a given HDA stream */
IPCCMD_HDA_START,

/* The host stops (sets the SDxCTL.RUN bit) for a given HDA stream */
IPCCMD_HDA_STOP,

/* The host validates an HDA byte stream contains an 8bit counter and received a given
* number of bytes
*/
IPCCMD_HDA_VALIDATE,

/* Host sends some data */
IPCCMD_HDA_SEND
};

#endif /* ZEPHYR_INCLUDE_CAVS_TEST_H */
Loading

0 comments on commit cc6e9c0

Please sign in to comment.