3782 lines
123 KiB
C
3782 lines
123 KiB
C
/**
|
|
* @file sdrv_i2s.c
|
|
* @brief E3 I2S controller driver.
|
|
*
|
|
* @copyright Copyright (c) 2021 Semidrive Semiconductor.
|
|
* All rights reserved.
|
|
*
|
|
*/
|
|
|
|
#include <armv7-r/cache.h>
|
|
#include <bits.h>
|
|
#include <compiler.h>
|
|
#include <debug.h>
|
|
#include <irq.h>
|
|
#include <param.h>
|
|
#include <reg.h>
|
|
#include <string.h>
|
|
#include <udelay/udelay.h>
|
|
|
|
#include "sdrv_i2s.h"
|
|
|
|
#include "regs_base.h"
|
|
#include "saci_regs.h"
|
|
|
|
#define SACI_DEBUG_LEVEL 7
|
|
#define SACI_REG_DEBUG_LEVEL 0
|
|
#define SACI_ERR SSDK_ERR
|
|
|
|
#if SACI_REG_DEBUG_LEVEL
|
|
#define saci_readl(reg) \
|
|
readl(reg); \
|
|
ssdk_printf(SACI_DEBUG_LEVEL, "r(0x%x, r(0x%08x)\r\n", reg, readl(reg));
|
|
#define saci_writel(val, reg) \
|
|
writel(val, reg); \
|
|
ssdk_printf(SACI_DEBUG_LEVEL, "w(0x%x, 0x%08x), r(0x%08x)\r\n", reg, val, \
|
|
readl(reg));
|
|
#else
|
|
#define saci_writel(val, reg) writel(val, reg)
|
|
#define saci_readl(reg) readl(reg)
|
|
#endif
|
|
|
|
/* sdrv i2s clk divider source selection */
|
|
#define CLK_DIVIDER_SOURCE_TX_I2S_CLK 0
|
|
#define CLK_DIVIDER_SOURCE_RX_I2S_CLK 1
|
|
#define CLK_DIVIDER_SOURCE_I2S_MCLK 2
|
|
#define CLK_DIVIDER_SOURCE_EXT_CLK 3
|
|
|
|
/* sdrv i2s clock work mode :master or slave */
|
|
#define I2S_MASTER 0
|
|
#define I2S_SLAVE 1
|
|
/* sdrv i2s rx clk sync selection */
|
|
#define RECEIVER_ASYNC 0
|
|
#define RECEIVER_SYNCFROM_INTX 1
|
|
#define RECEIVER_SYNCFROM_EXTX 2
|
|
#define RECEIVER_SYNCFROM_EXRX 3
|
|
|
|
/* sdrv i2s tx clk sync selection */
|
|
#define TRANSMITTER_ASYNC 0
|
|
#define TRANSMITTER_SYNCFROM_INRX 1
|
|
#define TRANSMITTER_SYNCFROM_EXTX 2
|
|
#define TRANSMITTER_SYNCFROM_EXRX 3
|
|
|
|
/* sdrv i2s data format selection */
|
|
#define SACI_I2S_STANDARD_PHILLIPS 0x00
|
|
#define SACI_I2S_RIGHT_JUSTIFIED 0x01
|
|
#define SACI_I2S_LEFT_JUSTIFIED 0x02
|
|
#define SACI_I2S_DSP_A 0x03
|
|
#define SACI_I2S_TDM 0x04
|
|
#define SACI_I2S_AC97 0x05
|
|
#define SACI_I2S_DSP_B 0x06
|
|
|
|
/* sdrv i2s fifo depth */
|
|
#define SACI_I2S_DEPTH 512
|
|
/* sdrv i2s fifo tx threshold level */
|
|
#define SACI_I2S_TX_THRESHOLD 128
|
|
/* sdrv i2s fifo rx threshold level */
|
|
#define SACI_I2S_RX_THRESHOLD 256
|
|
/* sdrv i2s stereo dma burst size */
|
|
#define SACI_DMA_BURST_STEREO 16
|
|
|
|
/* sdrv i2s ws clk width : one bclk */
|
|
#define SACI_WS_LEN_ONE_TSCK 0x1
|
|
|
|
/* sdrv i2s global reset */
|
|
#define SACI_GLOBAL_RESET 5
|
|
|
|
/* sdrv i2s tx or rx reset */
|
|
#define SACI_TX_RX_RESET 5
|
|
|
|
/* sdrv i2s expected period count*/
|
|
#define SACI_EXPECTED_PERIOD_COUNT 2
|
|
|
|
#define TDM_SELCH(n) ((__builtin_ffs(n) - 1) & 0xffff)
|
|
|
|
/* sdrv i2s data width */
|
|
#define DATA_WIDTH_8BITS 0
|
|
#define DATA_WIDTH_10BITS 1
|
|
#define DATA_WIDTH_12BITS 2
|
|
#define DATA_WIDTH_14BITS 3
|
|
#define DATA_WIDTH_16BITS 4
|
|
#define DATA_WIDTH_20BITS 5
|
|
#define DATA_WIDTH_24BITS 6
|
|
#define DATA_WIDTH_32BITS 7
|
|
|
|
/* sdrv i2s slot width */
|
|
#define SLOT_WIDTH_8BITS 0
|
|
#define SLOT_WIDTH_10BITS 1
|
|
#define SLOT_WIDTH_12BITS 2
|
|
#define SLOT_WIDTH_14BITS 3
|
|
#define SLOT_WIDTH_16BITS 4
|
|
#define SLOT_WIDTH_20BITS 5
|
|
#define SLOT_WIDTH_24BITS 6
|
|
#define SLOT_WIDTH_32BITS 7
|
|
|
|
/* sdrv i2s mclk_dir */
|
|
#define SACI_MCLK_INPUT 0
|
|
#define SACI_MCLK_OUT 1
|
|
/* sdrv i2s mclk_sel */
|
|
#define SACI_MCLK_SEL_CLKGEN 0
|
|
#define SACI_MCLK_SEL_IOPAD 1
|
|
|
|
/* sdrv i2s start stop cmd */
|
|
typedef enum i2s_trigger_op {
|
|
SDRV_I2S_TRIGGER_START,
|
|
SDRV_I2S_TRIGGER_STOP,
|
|
} sdrv_i2s_trigger_op_t;
|
|
|
|
/* sdrv i2s use dma linklist to cicular buff */
|
|
static sdrv_dma_linklist_descriptor_t dma_linklists[SDRV_I2S_STREAM_MAX]
|
|
[SDRV_I2S_XFER_NUM_BUFFERS]
|
|
__attribute__((__aligned__(CONFIG_ARCH_CACHE_LINE)));
|
|
|
|
#define min(a, b) (a) < (b) ? a : b
|
|
|
|
/**
|
|
* @brief sdrv 12s read reg bits
|
|
*
|
|
*
|
|
* @param reg_base
|
|
* @param mask
|
|
* @param offset
|
|
* @return uint32_t
|
|
*/
|
|
static inline uint32_t saci_rd_reg_bits(uint32_t reg_base, uint32_t mask,
|
|
uint32_t offset)
|
|
{
|
|
uint32_t rd_data = saci_readl(reg_base);
|
|
|
|
return ((rd_data & mask) >> offset);
|
|
}
|
|
|
|
/**
|
|
* @brief sdrv 12s write reg bits
|
|
*
|
|
* @param reg_base
|
|
* @param mask
|
|
* @param offset
|
|
* @param val
|
|
*/
|
|
static inline void saci_wr_reg_bits(uint32_t reg_base, uint32_t mask,
|
|
uint32_t offset, uint32_t val)
|
|
{
|
|
uint32_t wr_data = saci_readl(reg_base);
|
|
|
|
wr_data = wr_data & (~mask);
|
|
wr_data = wr_data | ((val << offset) & mask);
|
|
|
|
saci_writel(wr_data, reg_base);
|
|
}
|
|
|
|
/**
|
|
* @brief Calculate the number of 1 in a number
|
|
*
|
|
* @param x
|
|
* @return int
|
|
*/
|
|
static inline int bit_count(int x)
|
|
{
|
|
int n;
|
|
|
|
for (n = 0; x != 0; x &= (x - 1)) {
|
|
n++;
|
|
}
|
|
|
|
return n;
|
|
}
|
|
|
|
/**
|
|
* @brief calculate the number of valid bytes according to sample_width
|
|
*
|
|
* @param params
|
|
* @return uint8_t
|
|
*/
|
|
static uint8_t params_to_valid_bytes(sdrv_i2s_params_t *params)
|
|
{
|
|
uint8_t valid_bytes = 0;
|
|
|
|
switch (params->sample_width) {
|
|
case SDRV_I2S_SAMPLE_WIDTH_8BITS:
|
|
valid_bytes = 1;
|
|
break;
|
|
case SDRV_I2S_SAMPLE_WIDTH_16BITS:
|
|
valid_bytes = 2;
|
|
break;
|
|
|
|
case SDRV_I2S_SAMPLE_WIDTH_24BITS:
|
|
valid_bytes = 3;
|
|
break;
|
|
case SDRV_I2S_SAMPLE_WIDTH_32BITS:
|
|
valid_bytes = 4;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return valid_bytes;
|
|
}
|
|
|
|
/**
|
|
* @brief calculate how many bytes a sample uses
|
|
*
|
|
* @param width
|
|
* @return unsigned int
|
|
*/
|
|
static unsigned int to_saci_data_width(sdrv_i2s_sample_width_t width)
|
|
{
|
|
unsigned int datawidth = 0;
|
|
|
|
switch (width) {
|
|
case SDRV_I2S_SAMPLE_WIDTH_8BITS:
|
|
datawidth = DATA_WIDTH_8BITS;
|
|
break;
|
|
case SDRV_I2S_SAMPLE_WIDTH_16BITS:
|
|
datawidth = DATA_WIDTH_16BITS;
|
|
break;
|
|
case SDRV_I2S_SAMPLE_WIDTH_24BITS:
|
|
datawidth = DATA_WIDTH_24BITS;
|
|
break;
|
|
case SDRV_I2S_SAMPLE_WIDTH_32BITS:
|
|
datawidth = DATA_WIDTH_32BITS;
|
|
break;
|
|
|
|
default:
|
|
datawidth = DATA_WIDTH_16BITS;
|
|
}
|
|
return datawidth;
|
|
}
|
|
|
|
/**
|
|
* @brief slot_width convert to the value of the corresponding register
|
|
*
|
|
* @param width
|
|
* @return unsigned int
|
|
*/
|
|
static unsigned int to_saci_slot_width(sdrv_i2s_slot_width_t width)
|
|
{
|
|
unsigned int slot_width = 0;
|
|
|
|
switch (width) {
|
|
case SDRV_I2S_SLOT_WIDTH_8BITS:
|
|
slot_width = SLOT_WIDTH_8BITS;
|
|
break;
|
|
case SDRV_I2S_SLOT_WIDTH_16BITS:
|
|
slot_width = SLOT_WIDTH_16BITS;
|
|
break;
|
|
case SDRV_I2S_SLOT_WIDTH_24BITS:
|
|
slot_width = SLOT_WIDTH_24BITS;
|
|
break;
|
|
case SDRV_I2S_SLOT_WIDTH_32BITS:
|
|
slot_width = SLOT_WIDTH_32BITS;
|
|
break;
|
|
|
|
default:
|
|
slot_width = SLOT_WIDTH_16BITS;
|
|
}
|
|
return slot_width;
|
|
}
|
|
|
|
/**
|
|
* @brief sdrv i2s interrupt init default value
|
|
*
|
|
* @param i2s
|
|
* @param type
|
|
*/
|
|
static void saci_i2s_init_interrupt(sdrv_i2s_t *i2s,
|
|
sdrv_i2s_stream_type_t type)
|
|
{
|
|
sdrv_i2s_config_t *cfg = i2s->cfg;
|
|
uint32_t base_addr = cfg->base;
|
|
|
|
if (type == SDRV_I2S_STREAM_PLAYBACK) {
|
|
|
|
saci_wr_reg_bits(base_addr + SACI_0004_SACI_IRQ_MASK,
|
|
SACI_0004_TX_FIFO_UDR_MASK, SACI_0004_TX_FIFO_UDR_POS,
|
|
1);
|
|
saci_wr_reg_bits(base_addr + SACI_0004_SACI_IRQ_MASK,
|
|
SACI_0004_TX_FIFO_FULL_MASK,
|
|
SACI_0004_TX_FIFO_FULL_POS, 1);
|
|
saci_wr_reg_bits(base_addr + SACI_0004_SACI_IRQ_MASK,
|
|
SACI_0004_TX_FIFO_EMPTY_MASK,
|
|
SACI_0004_TX_FIFO_EMPTY_POS, 1);
|
|
saci_wr_reg_bits(base_addr + SACI_0004_SACI_IRQ_MASK,
|
|
SACI_0004_TX_WIDTH_MISM_MASK,
|
|
SACI_0004_TX_WIDTH_MISM_POS, 1);
|
|
saci_wr_reg_bits(base_addr + SACI_0004_SACI_IRQ_MASK,
|
|
SACI_0004_TX_FIFO_PRE_EMPTY_MASK,
|
|
SACI_0004_TX_FIFO_PRE_EMPTY_POS, 1);
|
|
saci_wr_reg_bits(base_addr + SACI_0004_SACI_IRQ_MASK,
|
|
SACI_0004_TX_FIFO_OVR_MASK, SACI_0004_TX_FIFO_OVR_POS,
|
|
1);
|
|
saci_wr_reg_bits(base_addr + SACI_0004_SACI_IRQ_MASK,
|
|
SACI_0004_TX_FIFO_PRE_EMPTY_MASK,
|
|
SACI_0004_TX_FIFO_PRE_EMPTY_POS, 1);
|
|
|
|
} else if (type == SDRV_I2S_STREAM_CAPTURE) {
|
|
|
|
saci_wr_reg_bits(base_addr + SACI_0004_SACI_IRQ_MASK,
|
|
SACI_0004_RX_FIFO_OVR_MASK, SACI_0004_RX_FIFO_OVR_POS,
|
|
1);
|
|
|
|
saci_wr_reg_bits(base_addr + SACI_0004_SACI_IRQ_MASK,
|
|
SACI_0004_RX_TIMEOUT_MASK, SACI_0004_RX_TIMEOUT_POS,
|
|
1);
|
|
|
|
saci_wr_reg_bits(base_addr + SACI_0004_SACI_IRQ_MASK,
|
|
SACI_0004_RX_FIFO_FULL_MASK,
|
|
SACI_0004_RX_FIFO_FULL_POS, 1);
|
|
|
|
saci_wr_reg_bits(base_addr + SACI_0004_SACI_IRQ_MASK,
|
|
SACI_0004_RX_FIFO_EMPTY_MASK,
|
|
SACI_0004_RX_FIFO_EMPTY_POS, 1);
|
|
|
|
saci_wr_reg_bits(base_addr + SACI_0004_SACI_IRQ_MASK,
|
|
SACI_0004_RX_WIDTH_MISM_MASK,
|
|
SACI_0004_RX_WIDTH_MISM_POS, 1);
|
|
saci_wr_reg_bits(base_addr + SACI_0004_SACI_IRQ_MASK,
|
|
SACI_0004_RX_FIFO_PRE_FULL_MASK,
|
|
SACI_0004_RX_FIFO_PRE_FULL_POS, 1);
|
|
saci_wr_reg_bits(base_addr + SACI_0004_SACI_IRQ_MASK,
|
|
SACI_0004_RX_FIFO_PRE_FULL_MASK,
|
|
SACI_0004_RX_FIFO_PRE_FULL_POS, 1);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief sdrv i2s irq reset
|
|
*
|
|
*
|
|
* @param i2s
|
|
* @param type
|
|
*/
|
|
static void saci_i2s_reset_interrupt(sdrv_i2s_t *i2s,
|
|
sdrv_i2s_stream_type_t type)
|
|
{
|
|
sdrv_i2s_config_t *cfg = i2s->cfg;
|
|
uint32_t base_addr = cfg->base;
|
|
|
|
if (type == SDRV_I2S_STREAM_PLAYBACK) {
|
|
|
|
saci_wr_reg_bits(base_addr + SACI_0004_SACI_IRQ_MASK,
|
|
SACI_0004_TX_FIFO_UDR_MASK, SACI_0004_TX_FIFO_UDR_POS,
|
|
1);
|
|
saci_wr_reg_bits(base_addr + SACI_0004_SACI_IRQ_MASK,
|
|
SACI_0004_TX_FIFO_FULL_MASK,
|
|
SACI_0004_TX_FIFO_FULL_POS, 1);
|
|
saci_wr_reg_bits(base_addr + SACI_0004_SACI_IRQ_MASK,
|
|
SACI_0004_TX_FIFO_EMPTY_MASK,
|
|
SACI_0004_TX_FIFO_EMPTY_POS, 1);
|
|
saci_wr_reg_bits(base_addr + SACI_0004_SACI_IRQ_MASK,
|
|
SACI_0004_TX_WIDTH_MISM_MASK,
|
|
SACI_0004_TX_WIDTH_MISM_POS, 1);
|
|
saci_wr_reg_bits(base_addr + SACI_0004_SACI_IRQ_MASK,
|
|
SACI_0004_TX_FIFO_PRE_EMPTY_MASK,
|
|
SACI_0004_TX_FIFO_PRE_EMPTY_POS, 1);
|
|
saci_wr_reg_bits(base_addr + SACI_0004_SACI_IRQ_MASK,
|
|
SACI_0004_TX_FIFO_OVR_MASK, SACI_0004_TX_FIFO_OVR_POS,
|
|
1);
|
|
|
|
} else if (type == SDRV_I2S_STREAM_CAPTURE) {
|
|
|
|
saci_wr_reg_bits(base_addr + SACI_0004_SACI_IRQ_MASK,
|
|
SACI_0004_RX_FIFO_OVR_MASK, SACI_0004_RX_FIFO_OVR_POS,
|
|
1);
|
|
|
|
saci_wr_reg_bits(base_addr + SACI_0004_SACI_IRQ_MASK,
|
|
SACI_0004_RX_TIMEOUT_MASK, SACI_0004_RX_TIMEOUT_POS,
|
|
1);
|
|
|
|
saci_wr_reg_bits(base_addr + SACI_0004_SACI_IRQ_MASK,
|
|
SACI_0004_RX_FIFO_FULL_MASK,
|
|
SACI_0004_RX_FIFO_FULL_POS, 1);
|
|
|
|
saci_wr_reg_bits(base_addr + SACI_0004_SACI_IRQ_MASK,
|
|
SACI_0004_RX_FIFO_EMPTY_MASK,
|
|
SACI_0004_RX_FIFO_EMPTY_POS, 1);
|
|
saci_wr_reg_bits(base_addr + SACI_0004_SACI_IRQ_MASK,
|
|
SACI_0004_RX_WIDTH_MISM_MASK,
|
|
SACI_0004_RX_WIDTH_MISM_POS, 1);
|
|
saci_wr_reg_bits(base_addr + SACI_0004_SACI_IRQ_MASK,
|
|
SACI_0004_RX_FIFO_PRE_FULL_MASK,
|
|
SACI_0004_RX_FIFO_PRE_FULL_POS, 1);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief clear tx fifo pre empty irq status
|
|
*
|
|
* @param base_addr
|
|
*/
|
|
static void saci_i2s_clear_tx_fifo_pre_empty_irq(uint32_t base_addr)
|
|
{
|
|
saci_wr_reg_bits(base_addr + SACI_0008_SACI_IRQ_STAT,
|
|
SACI_0008_TX_FIFO_PRE_EMPTY_MASK,
|
|
SACI_0008_TX_FIFO_PRE_EMPTY_POS, 1);
|
|
}
|
|
|
|
/**
|
|
* @brief clear tx fifo underrun irq status
|
|
*
|
|
* @param base_addr
|
|
*/
|
|
static void saci_i2s_clear_tx_fifo_udr_irq(uint32_t base_addr)
|
|
{
|
|
saci_wr_reg_bits(base_addr + SACI_0008_SACI_IRQ_STAT,
|
|
SACI_0008_TX_FIFO_UDR_MASK, SACI_0008_TX_FIFO_UDR_POS, 1);
|
|
}
|
|
|
|
/**
|
|
* @brief clear rx fifo prefull irq status
|
|
*
|
|
* @param base_addr
|
|
*/
|
|
static void saci_i2s_clear_rx_fifo_prefull_irq(uint32_t base_addr)
|
|
{
|
|
saci_wr_reg_bits(base_addr + SACI_0008_SACI_IRQ_STAT,
|
|
SACI_0008_RX_FIFO_PRE_FULL_MASK,
|
|
SACI_0008_RX_FIFO_PRE_FULL_POS, 1);
|
|
}
|
|
|
|
/**
|
|
* @brief clear rx fifo overrun irq status
|
|
*
|
|
* @param base_addr
|
|
*/
|
|
static void saci_i2s_clear_rx_fifo_ovr_irq(uint32_t base_addr)
|
|
{
|
|
saci_wr_reg_bits(base_addr + SACI_0008_SACI_IRQ_STAT,
|
|
SACI_0008_RX_FIFO_OVR_MASK, SACI_0008_RX_FIFO_OVR_POS, 1);
|
|
}
|
|
|
|
/**
|
|
* @brief clear rx fifo timeout irq status
|
|
*
|
|
* @param base_addr
|
|
*/
|
|
static void saci_i2s_clear_rx_fifo_timeout_irq(uint32_t base_addr)
|
|
{
|
|
saci_wr_reg_bits(base_addr + SACI_0008_SACI_IRQ_STAT,
|
|
SACI_0008_RX_TIMEOUT_MASK, SACI_0008_RX_TIMEOUT_POS, 1);
|
|
}
|
|
|
|
/**
|
|
* @brief enable data pin
|
|
*
|
|
* This function is used to enable the data pin,
|
|
*
|
|
* @param base_addr
|
|
* @param ch_sel
|
|
*/
|
|
static void saci_i2s_enable_chx(uint32_t base_addr, uint8_t ch_sel)
|
|
{
|
|
uint32_t reg_val, ch_val;
|
|
|
|
reg_val = saci_rd_reg_bits(base_addr + SACI_000C_SACI_CH_CTRL,
|
|
SACI_000C_CHx_EN_MASK, SACI_000C_CHx_EN_POS);
|
|
ch_val = reg_val | ch_sel;
|
|
saci_wr_reg_bits(base_addr + SACI_000C_SACI_CH_CTRL, SACI_000C_CHx_EN_MASK,
|
|
SACI_000C_CHx_EN_POS, ch_val);
|
|
}
|
|
|
|
/**
|
|
* @brief config tx data line
|
|
*
|
|
* This function is used to config tx data line,
|
|
*
|
|
* @param base_addr
|
|
* @param ch_sel
|
|
*/
|
|
static void saci_i2s_config_tx_data_line(uint32_t base_addr, uint8_t ch_sel)
|
|
{
|
|
uint32_t reg_val, ch_val;
|
|
|
|
saci_i2s_enable_chx(base_addr, ch_sel);
|
|
reg_val = saci_rd_reg_bits(base_addr + SACI_000C_SACI_CH_CTRL,
|
|
SACI_000C_CHx_DIR_MASK, SACI_000C_CHx_DIR_POS);
|
|
ch_val = reg_val | ch_sel;
|
|
saci_wr_reg_bits(base_addr + SACI_000C_SACI_CH_CTRL, SACI_000C_CHx_DIR_MASK,
|
|
SACI_000C_CHx_DIR_POS, ch_val);
|
|
}
|
|
|
|
/**
|
|
* @brief config tx data line
|
|
*
|
|
* This function is used to config rx data line,
|
|
*
|
|
* @param base_addr
|
|
* @param ch_sel
|
|
*/
|
|
static void saci_i2s_config_rx_data_line(uint32_t base_addr, uint8_t ch_sel)
|
|
{
|
|
uint32_t reg_val, ch_val;
|
|
|
|
saci_i2s_enable_chx(base_addr, ch_sel);
|
|
reg_val = saci_rd_reg_bits(base_addr + SACI_000C_SACI_CH_CTRL,
|
|
SACI_000C_CHx_DIR_MASK, SACI_000C_CHx_DIR_POS);
|
|
ch_val = ~(ch_sel);
|
|
ch_val = reg_val & ch_val;
|
|
saci_wr_reg_bits(base_addr + SACI_000C_SACI_CH_CTRL, SACI_000C_CHx_DIR_MASK,
|
|
SACI_000C_CHx_DIR_POS, ch_val);
|
|
}
|
|
|
|
/**
|
|
* @brief get status of irq
|
|
*
|
|
* This function is used to get status of irq
|
|
*
|
|
* @param base_addr
|
|
* @return the status of irq
|
|
*
|
|
*/
|
|
static uint32_t saci_i2s_get_status_irq(uint32_t base_addr)
|
|
{
|
|
return saci_readl(base_addr + SACI_0008_SACI_IRQ_STAT);
|
|
}
|
|
|
|
/**
|
|
* @brief get mask of irq
|
|
*
|
|
*
|
|
* @param base_addr
|
|
* @return the mask of irq
|
|
*/
|
|
static uint32_t saci_i2s_get_mask_irq(uint32_t base_addr)
|
|
{
|
|
return saci_readl(base_addr + SACI_0004_SACI_IRQ_MASK);
|
|
}
|
|
|
|
/**
|
|
* @brief get remaining data size of tx fifo
|
|
*
|
|
* This function is used to get remaining data size of tx fifo
|
|
*
|
|
* @param base_addr
|
|
* @return the remaining data size of tx fifo
|
|
*/
|
|
static uint32_t saci_i2s_get_tx_fifo_dpt(uint32_t base_addr)
|
|
{
|
|
return saci_rd_reg_bits(base_addr + SACI_100C_SACI_TX_FIFO_STA,
|
|
SACI_100C_FIFO_DPT_MASK, SACI_100C_FIFO_DPT_POS);
|
|
}
|
|
|
|
/**
|
|
* @brief write a sample to fifo in irq mode
|
|
*
|
|
* This function is used to write a sample to fifo in irq mode
|
|
*
|
|
* @param base_addr
|
|
* @param sample
|
|
*/
|
|
static void saci_i2s_write_tx_fifo_data(uint32_t base_addr, uint32_t sample)
|
|
{
|
|
writel(sample, base_addr + SACI_2000_SACI_TX_FIFO_DATA);
|
|
}
|
|
|
|
/**
|
|
* @brief get remaining data size of rx fifo
|
|
*
|
|
* @param base_addr
|
|
* @return the remaining data size of rx fifo
|
|
*/
|
|
static uint32_t saci_i2s_get_rx_fifo_dpt(uint32_t base_addr)
|
|
{
|
|
return saci_rd_reg_bits(base_addr + SACI_300C_SACI_RX_FIFO_STA,
|
|
SACI_300C_FIFO_DPT_MASK, SACI_300C_FIFO_DPT_POS);
|
|
}
|
|
|
|
/**
|
|
* @brief read a sample from rx fifo in irq mode
|
|
*
|
|
* This function is used to read a sample from rx fifo in irq mode
|
|
*
|
|
* @param base_addr
|
|
* @return uint32_t
|
|
*/
|
|
static uint32_t saci_i2s_read_rx_fifo_data(uint32_t base_addr)
|
|
{
|
|
return readl(base_addr + SACI_4000_SACI_RX_FIFO_DATA);
|
|
}
|
|
|
|
/**
|
|
* @brief calculate freq according to params
|
|
*
|
|
* @param i2s
|
|
* @param params
|
|
* @return uint32_t
|
|
*/
|
|
static uint32_t saci_i2s_calculate_freq(sdrv_i2s_params_t *params)
|
|
{
|
|
|
|
uint32_t channels = params->channels;
|
|
unsigned int srate = params->sample_rate;
|
|
uint32_t word_width = params->sample_width;
|
|
uint32_t slot_width = word_width;
|
|
uint32_t slots = (channels == 1) ? 2 : channels;
|
|
uint32_t freq = 0;
|
|
|
|
if (params->slots) {
|
|
slots = params->slots;
|
|
}
|
|
if (params->slot_width) {
|
|
slot_width = params->slot_width;
|
|
}
|
|
|
|
freq = slots * slot_width * srate;
|
|
|
|
return freq;
|
|
}
|
|
|
|
/**
|
|
* @brief config tx data format
|
|
*
|
|
* This function is used to config tx format
|
|
*
|
|
* @param i2s
|
|
* @param fmt
|
|
* @return true
|
|
* @return false
|
|
*/
|
|
static bool saci_i2s_set_format_tx(sdrv_i2s_t *i2s, unsigned int fmt)
|
|
{
|
|
sdrv_i2s_config_t *cfg = i2s->cfg;
|
|
uint32_t base_addr = cfg->base;
|
|
|
|
switch (fmt & SDRV_I2S_DAIFMT_FORMAT_MASK) {
|
|
|
|
case SDRV_I2S_DAIFMT_STANDARD_PHILLIPS:
|
|
|
|
/* config mode */
|
|
saci_wr_reg_bits(base_addr + SACI_1004_SACI_TX_CTRL,
|
|
SACI_1004_TIMING_MODE_MASK, SACI_1004_TIMING_MODE_POS,
|
|
SACI_I2S_STANDARD_PHILLIPS);
|
|
/* config ws polar */
|
|
saci_wr_reg_bits(base_addr + SACI_1004_SACI_TX_CTRL,
|
|
SACI_1004_TX_WS_POL_MASK, SACI_1004_TX_WS_POL_POS, 0);
|
|
/* config sck polar */
|
|
saci_wr_reg_bits(base_addr + SACI_1004_SACI_TX_CTRL,
|
|
SACI_1004_TSCK_POL_MASK, SACI_1004_TSCK_POL_POS, 0);
|
|
/* config WS 1 tsck udelay polar */
|
|
saci_wr_reg_bits(base_addr + SACI_1004_SACI_TX_CTRL,
|
|
SACI_1004_TX_WS_OFFSET_MASK,
|
|
SACI_1004_TX_WS_OFFSET_POS, 1);
|
|
/* config data align MSB */
|
|
saci_wr_reg_bits(base_addr + SACI_1004_SACI_TX_CTRL,
|
|
SACI_1004_DATA_ALIGN_MASK, SACI_1004_DATA_ALIGN_POS,
|
|
1);
|
|
|
|
/* config fifo align lsb */
|
|
saci_wr_reg_bits(base_addr + SACI_1008_SACI_TX_FIFO_CTRL,
|
|
SACI_1008_DATA_ALIGN_MASK, SACI_1008_DATA_ALIGN_POS,
|
|
0);
|
|
|
|
break;
|
|
|
|
case SDRV_I2S_DAIFMT_RIGHT_JUSTIFIED:
|
|
saci_wr_reg_bits(base_addr + SACI_1004_SACI_TX_CTRL,
|
|
SACI_1004_TIMING_MODE_MASK, SACI_1004_TIMING_MODE_POS,
|
|
SACI_I2S_RIGHT_JUSTIFIED);
|
|
|
|
/* config ws polar */
|
|
saci_wr_reg_bits(base_addr + SACI_1004_SACI_TX_CTRL,
|
|
SACI_1004_TX_WS_POL_MASK, SACI_1004_TX_WS_POL_POS, 0);
|
|
/* config sck polar */
|
|
saci_wr_reg_bits(base_addr + SACI_1004_SACI_TX_CTRL,
|
|
SACI_1004_TSCK_POL_MASK, SACI_1004_TSCK_POL_POS, 0);
|
|
/* config WS 0 tsck udelay polar */
|
|
saci_wr_reg_bits(base_addr + SACI_1004_SACI_TX_CTRL,
|
|
SACI_1004_TX_WS_OFFSET_MASK,
|
|
SACI_1004_TX_WS_OFFSET_POS, 0);
|
|
/* RIGHT_JUSTIFIED config data align must Be lsb */
|
|
saci_wr_reg_bits(base_addr + SACI_1004_SACI_TX_CTRL,
|
|
SACI_1004_DATA_ALIGN_MASK, SACI_1004_DATA_ALIGN_POS,
|
|
0);
|
|
|
|
/* config fifo align lsb */
|
|
saci_wr_reg_bits(base_addr + SACI_1008_SACI_TX_FIFO_CTRL,
|
|
SACI_1008_DATA_ALIGN_MASK, SACI_1008_DATA_ALIGN_POS,
|
|
0);
|
|
break;
|
|
|
|
case SDRV_I2S_DAIFMT_LEFT_JUSTIFIED:
|
|
|
|
saci_wr_reg_bits(base_addr + SACI_1004_SACI_TX_CTRL,
|
|
SACI_1004_TIMING_MODE_MASK, SACI_1004_TIMING_MODE_POS,
|
|
SACI_I2S_LEFT_JUSTIFIED);
|
|
|
|
/* config ws polar */
|
|
saci_wr_reg_bits(base_addr + SACI_1004_SACI_TX_CTRL,
|
|
SACI_1004_TX_WS_POL_MASK, SACI_1004_TX_WS_POL_POS, 0);
|
|
/* config sck polar */
|
|
saci_wr_reg_bits(base_addr + SACI_1004_SACI_TX_CTRL,
|
|
SACI_1004_TSCK_POL_MASK, SACI_1004_TSCK_POL_POS, 0);
|
|
/* config WS 0 tsck udelay polar */
|
|
saci_wr_reg_bits(base_addr + SACI_1004_SACI_TX_CTRL,
|
|
SACI_1004_TX_WS_OFFSET_MASK,
|
|
SACI_1004_TX_WS_OFFSET_POS, 0);
|
|
/* LEFT_JUSTIFIED config data align MSB */
|
|
saci_wr_reg_bits(base_addr + SACI_1004_SACI_TX_CTRL,
|
|
SACI_1004_DATA_ALIGN_MASK, SACI_1004_DATA_ALIGN_POS,
|
|
1);
|
|
|
|
/* config fifo align lsb */
|
|
saci_wr_reg_bits(base_addr + SACI_1008_SACI_TX_FIFO_CTRL,
|
|
SACI_1008_DATA_ALIGN_MASK, SACI_1008_DATA_ALIGN_POS,
|
|
0);
|
|
|
|
break;
|
|
|
|
/* DSP_A */
|
|
/* Data on rising edge of bclk, frame high, 1clk before data */
|
|
case SDRV_I2S_DAIFMT_DSP_A:
|
|
saci_wr_reg_bits(base_addr + SACI_1004_SACI_TX_CTRL,
|
|
SACI_1004_TIMING_MODE_MASK, SACI_1004_TIMING_MODE_POS,
|
|
SACI_I2S_TDM);
|
|
/* config ws length */
|
|
saci_wr_reg_bits(base_addr + SACI_1004_SACI_TX_CTRL,
|
|
SACI_1004_TX_WS_LEN_MASK, SACI_1004_TX_WS_LEN_POS,
|
|
SACI_WS_LEN_ONE_TSCK);
|
|
/* config ws polar */
|
|
saci_wr_reg_bits(base_addr + SACI_1004_SACI_TX_CTRL,
|
|
SACI_1004_TX_WS_POL_MASK, SACI_1004_TX_WS_POL_POS, 0);
|
|
/* config sck polar */
|
|
saci_wr_reg_bits(base_addr + SACI_1004_SACI_TX_CTRL,
|
|
SACI_1004_TSCK_POL_MASK, SACI_1004_TSCK_POL_POS, 0);
|
|
/* config WS 1 tsck udelay polar */
|
|
saci_wr_reg_bits(base_addr + SACI_1004_SACI_TX_CTRL,
|
|
SACI_1004_TX_WS_OFFSET_MASK,
|
|
SACI_1004_TX_WS_OFFSET_POS, 1);
|
|
/* config data align MSB */
|
|
saci_wr_reg_bits(base_addr + SACI_1004_SACI_TX_CTRL,
|
|
SACI_1004_DATA_ALIGN_MASK, SACI_1004_DATA_ALIGN_POS,
|
|
1);
|
|
|
|
/* config fifo align lsb */
|
|
saci_wr_reg_bits(base_addr + SACI_1008_SACI_TX_FIFO_CTRL,
|
|
SACI_1008_DATA_ALIGN_MASK, SACI_1008_DATA_ALIGN_POS,
|
|
0);
|
|
|
|
break;
|
|
/*
|
|
DSP_B
|
|
* Frame high, one bit for frame sync,
|
|
* frame sync asserts with the first bit of the frame.
|
|
* Data on rising edge of bclk, frame high
|
|
*/
|
|
case SDRV_I2S_DAIFMT_DSP_B:
|
|
saci_wr_reg_bits(base_addr + SACI_1004_SACI_TX_CTRL,
|
|
SACI_1004_TIMING_MODE_MASK, SACI_1004_TIMING_MODE_POS,
|
|
SACI_I2S_TDM);
|
|
/* config ws len channel length */
|
|
saci_wr_reg_bits(base_addr + SACI_1004_SACI_TX_CTRL,
|
|
SACI_1004_TX_WS_LEN_MASK, SACI_1004_TX_WS_LEN_POS,
|
|
SACI_WS_LEN_ONE_TSCK);
|
|
/* config ws polar */
|
|
saci_wr_reg_bits(base_addr + SACI_1004_SACI_TX_CTRL,
|
|
SACI_1004_TX_WS_POL_MASK, SACI_1004_TX_WS_POL_POS, 0);
|
|
/* config sck polar */
|
|
saci_wr_reg_bits(base_addr + SACI_1004_SACI_TX_CTRL,
|
|
SACI_1004_TSCK_POL_MASK, SACI_1004_TSCK_POL_POS, 0);
|
|
/* config WS 0 tsck udelay polar diff form DSP_A */
|
|
saci_wr_reg_bits(base_addr + SACI_1004_SACI_TX_CTRL,
|
|
SACI_1004_TX_WS_OFFSET_MASK,
|
|
SACI_1004_TX_WS_OFFSET_POS, 0);
|
|
/* config data align MSB */
|
|
saci_wr_reg_bits(base_addr + SACI_1004_SACI_TX_CTRL,
|
|
SACI_1004_DATA_ALIGN_MASK, SACI_1004_DATA_ALIGN_POS,
|
|
1);
|
|
|
|
/* config fifo align lsb */
|
|
saci_wr_reg_bits(base_addr + SACI_1008_SACI_TX_FIFO_CTRL,
|
|
SACI_1008_DATA_ALIGN_MASK, SACI_1008_DATA_ALIGN_POS,
|
|
0);
|
|
|
|
break;
|
|
default:
|
|
ssdk_printf(SACI_ERR, "%s unsupported fmt !!! \r\n", __func__);
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* @brief config rx data format
|
|
*
|
|
* This function is used to config rx format
|
|
*
|
|
* @param i2s
|
|
* @param fmt
|
|
* @return true
|
|
* @return false
|
|
*/
|
|
static bool saci_i2s_set_format_rx(sdrv_i2s_t *i2s, unsigned int fmt)
|
|
{
|
|
|
|
sdrv_i2s_config_t *cfg = i2s->cfg;
|
|
uint32_t base_addr = cfg->base;
|
|
|
|
switch (fmt & SDRV_I2S_DAIFMT_FORMAT_MASK) {
|
|
|
|
case SDRV_I2S_DAIFMT_STANDARD_PHILLIPS:
|
|
|
|
/* config mode */
|
|
saci_wr_reg_bits(base_addr + SACI_3004_SACI_RX_CTRL,
|
|
SACI_3004_TIMING_MODE_MASK, SACI_3004_TIMING_MODE_POS,
|
|
SACI_I2S_STANDARD_PHILLIPS);
|
|
/* config ws polar */
|
|
saci_wr_reg_bits(base_addr + SACI_3004_SACI_RX_CTRL,
|
|
SACI_3004_RX_WS_POL_MASK, SACI_3004_RX_WS_POL_POS, 0);
|
|
/* config sck polar */
|
|
saci_wr_reg_bits(base_addr + SACI_3004_SACI_RX_CTRL,
|
|
SACI_3004_RSCK_POL_MASK, SACI_3004_RSCK_POL_POS, 0);
|
|
/* must WS 1 tsck udelay polar */
|
|
saci_wr_reg_bits(base_addr + SACI_3004_SACI_RX_CTRL,
|
|
SACI_3004_RX_WS_OFFSET_MASK,
|
|
SACI_3004_RX_WS_OFFSET_POS, 1);
|
|
/* config data align MSB */
|
|
saci_wr_reg_bits(base_addr + SACI_3004_SACI_RX_CTRL,
|
|
SACI_3004_DATA_ALIGN_MASK, SACI_3004_DATA_ALIGN_POS,
|
|
1);
|
|
|
|
/* config fifo align lsb */
|
|
saci_wr_reg_bits(base_addr + SACI_3008_SACI_RX_FIFO_CTRL,
|
|
SACI_3008_DATA_ALIGN_MASK, SACI_3008_DATA_ALIGN_POS,
|
|
0);
|
|
break;
|
|
|
|
case SDRV_I2S_DAIFMT_RIGHT_JUSTIFIED:
|
|
|
|
saci_wr_reg_bits(base_addr + SACI_3004_SACI_RX_CTRL,
|
|
SACI_3004_TIMING_MODE_MASK, SACI_3004_TIMING_MODE_POS,
|
|
SACI_I2S_RIGHT_JUSTIFIED);
|
|
/* config ws polar */
|
|
saci_wr_reg_bits(base_addr + SACI_3004_SACI_RX_CTRL,
|
|
SACI_3004_RX_WS_POL_MASK, SACI_3004_RX_WS_POL_POS, 0);
|
|
/* config sck polar */
|
|
saci_wr_reg_bits(base_addr + SACI_3004_SACI_RX_CTRL,
|
|
SACI_3004_RSCK_POL_MASK, SACI_3004_RSCK_POL_POS, 0);
|
|
/* must WS 0 tsck udelay polar */
|
|
saci_wr_reg_bits(base_addr + SACI_3004_SACI_RX_CTRL,
|
|
SACI_3004_RX_WS_OFFSET_MASK,
|
|
SACI_3004_RX_WS_OFFSET_POS, 0);
|
|
/* RIGHT_JUSTIFIED config data align must Be lsb */
|
|
saci_wr_reg_bits(base_addr + SACI_3004_SACI_RX_CTRL,
|
|
SACI_3004_DATA_ALIGN_MASK, SACI_3004_DATA_ALIGN_POS,
|
|
0);
|
|
|
|
/* config fifo align lsb */
|
|
saci_wr_reg_bits(base_addr + SACI_3008_SACI_RX_FIFO_CTRL,
|
|
SACI_3008_DATA_ALIGN_MASK, SACI_3008_DATA_ALIGN_POS,
|
|
0);
|
|
break;
|
|
|
|
case SDRV_I2S_DAIFMT_LEFT_JUSTIFIED:
|
|
|
|
saci_wr_reg_bits(base_addr + SACI_3004_SACI_RX_CTRL,
|
|
SACI_3004_TIMING_MODE_MASK, SACI_3004_TIMING_MODE_POS,
|
|
SACI_I2S_LEFT_JUSTIFIED);
|
|
|
|
/* config ws polar */
|
|
saci_wr_reg_bits(base_addr + SACI_3004_SACI_RX_CTRL,
|
|
SACI_3004_RX_WS_POL_MASK, SACI_3004_RX_WS_POL_POS, 0);
|
|
/* config sck polar */
|
|
saci_wr_reg_bits(base_addr + SACI_3004_SACI_RX_CTRL,
|
|
SACI_3004_RSCK_POL_MASK, SACI_3004_RSCK_POL_POS, 0);
|
|
/* must WS 0 tsck udelay polar */
|
|
saci_wr_reg_bits(base_addr + SACI_3004_SACI_RX_CTRL,
|
|
SACI_3004_RX_WS_OFFSET_MASK,
|
|
SACI_3004_RX_WS_OFFSET_POS, 0);
|
|
/* LEFT_JUSTIFIED config data align ,left justified data align must be
|
|
* msb */
|
|
saci_wr_reg_bits(base_addr + SACI_3004_SACI_RX_CTRL,
|
|
SACI_3004_DATA_ALIGN_MASK, SACI_3004_DATA_ALIGN_POS,
|
|
1);
|
|
|
|
/* config fifo align lsb */
|
|
saci_wr_reg_bits(base_addr + SACI_3008_SACI_RX_FIFO_CTRL,
|
|
SACI_3008_DATA_ALIGN_MASK, SACI_3008_DATA_ALIGN_POS,
|
|
0);
|
|
break;
|
|
|
|
/* DSP_A */
|
|
|
|
/* Data on rising edge of bclk, frame high, 1clk before data */
|
|
case SDRV_I2S_DAIFMT_DSP_A:
|
|
|
|
saci_wr_reg_bits(base_addr + SACI_3004_SACI_RX_CTRL,
|
|
SACI_3004_TIMING_MODE_MASK, SACI_3004_TIMING_MODE_POS,
|
|
SACI_I2S_TDM);
|
|
/* config ws len channel length */
|
|
saci_wr_reg_bits(base_addr + SACI_3004_SACI_RX_CTRL,
|
|
SACI_3004_RX_WS_LEN_MASK, SACI_3004_RX_WS_LEN_POS,
|
|
SACI_WS_LEN_ONE_TSCK);
|
|
|
|
/* config ws polar */
|
|
saci_wr_reg_bits(base_addr + SACI_3004_SACI_RX_CTRL,
|
|
SACI_3004_RX_WS_POL_MASK, SACI_3004_RX_WS_POL_POS, 0);
|
|
/* config sck polar */
|
|
saci_wr_reg_bits(base_addr + SACI_3004_SACI_RX_CTRL,
|
|
SACI_3004_RSCK_POL_MASK, SACI_3004_RSCK_POL_POS, 0);
|
|
/* config WS 1 tsck udelay polar */
|
|
saci_wr_reg_bits(base_addr + SACI_3004_SACI_RX_CTRL,
|
|
SACI_3004_RX_WS_OFFSET_MASK,
|
|
SACI_3004_RX_WS_OFFSET_POS, 1);
|
|
/* config data align MSB */
|
|
saci_wr_reg_bits(base_addr + SACI_3004_SACI_RX_CTRL,
|
|
SACI_3004_DATA_ALIGN_MASK, SACI_3004_DATA_ALIGN_POS,
|
|
1);
|
|
|
|
/* config fifo align lsb */
|
|
saci_wr_reg_bits(base_addr + SACI_3008_SACI_RX_FIFO_CTRL,
|
|
SACI_3008_DATA_ALIGN_MASK, SACI_3008_DATA_ALIGN_POS,
|
|
0);
|
|
|
|
break;
|
|
|
|
/*
|
|
DSP_B
|
|
* Frame high, one bit for frame sync,
|
|
* frame sync asserts with the first bit of the frame.
|
|
* Data on rising edge of bclk, frame high
|
|
*/
|
|
case SDRV_I2S_DAIFMT_DSP_B:
|
|
|
|
saci_wr_reg_bits(base_addr + SACI_3004_SACI_RX_CTRL,
|
|
SACI_3004_TIMING_MODE_MASK, SACI_3004_TIMING_MODE_POS,
|
|
SACI_I2S_TDM);
|
|
/* config ws len channel length */
|
|
saci_wr_reg_bits(base_addr + SACI_3004_SACI_RX_CTRL,
|
|
SACI_3004_RX_WS_LEN_MASK, SACI_3004_RX_WS_LEN_POS,
|
|
SACI_WS_LEN_ONE_TSCK);
|
|
/* config ws polar */
|
|
saci_wr_reg_bits(base_addr + SACI_3004_SACI_RX_CTRL,
|
|
SACI_3004_RX_WS_POL_MASK, SACI_3004_RX_WS_POL_POS, 0);
|
|
/* config sck polar */
|
|
saci_wr_reg_bits(base_addr + SACI_3004_SACI_RX_CTRL,
|
|
SACI_3004_RSCK_POL_MASK, SACI_3004_RSCK_POL_POS, 0);
|
|
/* config WS 0 tsck udelay polar diff form DSP_A */
|
|
saci_wr_reg_bits(base_addr + SACI_3004_SACI_RX_CTRL,
|
|
SACI_3004_RX_WS_OFFSET_MASK,
|
|
SACI_3004_RX_WS_OFFSET_POS, 0);
|
|
/* config data align MSB */
|
|
saci_wr_reg_bits(base_addr + SACI_3004_SACI_RX_CTRL,
|
|
SACI_3004_DATA_ALIGN_MASK, SACI_3004_DATA_ALIGN_POS,
|
|
1);
|
|
|
|
/* config fifo align lsb */
|
|
saci_wr_reg_bits(base_addr + SACI_3008_SACI_RX_FIFO_CTRL,
|
|
SACI_3008_DATA_ALIGN_MASK, SACI_3008_DATA_ALIGN_POS,
|
|
0);
|
|
|
|
break;
|
|
|
|
default:
|
|
ssdk_printf(SACI_ERR, "rx %s unsupported fmt !!! \r\n", __func__);
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* @brief prepare init sdrv i2s
|
|
*
|
|
* This function is used to prepare init sdrv i2s.
|
|
*
|
|
* @param i2s
|
|
* @return true
|
|
* @return false
|
|
*/
|
|
static bool saci_i2s_startup(sdrv_i2s_t *i2s)
|
|
{
|
|
sdrv_i2s_config_t *cfg = i2s->cfg;
|
|
uint32_t base_addr = cfg->base;
|
|
uint32_t tx_reset = 0, rx_reset = 0, pdm0_reset = 0, pdm1_reset = 0;
|
|
uint32_t saci_rst;
|
|
|
|
tx_reset = saci_rd_reg_bits(base_addr + SACI_0000_SACI_CTRL,
|
|
SACI_0000_TX_RST_MASK, SACI_0000_TX_RST_POS);
|
|
rx_reset = saci_rd_reg_bits(base_addr + SACI_0000_SACI_CTRL,
|
|
SACI_0000_RX_RST_MASK, SACI_0000_RX_RST_POS);
|
|
pdm0_reset =
|
|
saci_rd_reg_bits(base_addr + SACI_0000_SACI_CTRL,
|
|
SACI_0000_PDM_CH0_RST_MASK, SACI_0000_PDM_CH0_RST_POS);
|
|
pdm1_reset =
|
|
saci_rd_reg_bits(base_addr + SACI_0000_SACI_CTRL,
|
|
SACI_0000_PDM_CH1_RST_MASK, SACI_0000_PDM_CH1_RST_POS);
|
|
|
|
saci_rst = tx_reset || rx_reset || pdm0_reset || pdm1_reset;
|
|
if (!saci_rst) {
|
|
saci_wr_reg_bits(base_addr + SACI_0000_SACI_CTRL,
|
|
SACI_0000_SACI_RST_MASK, SACI_0000_SACI_RST_POS, 0);
|
|
udelay(SACI_GLOBAL_RESET);
|
|
saci_wr_reg_bits(base_addr + SACI_0000_SACI_CTRL,
|
|
SACI_0000_SACI_RST_MASK, SACI_0000_SACI_RST_POS, 1);
|
|
}
|
|
|
|
if (cfg->tx_chsel & cfg->rx_chsel) {
|
|
ssdk_printf(SACI_ERR, "%s:%d : TX RX DATA LINE select err \r\n",
|
|
__func__, __LINE__);
|
|
return false;
|
|
}
|
|
|
|
if (cfg->clk) {
|
|
i2s->clk_rate = sdrv_ckgen_get_rate(cfg->clk);
|
|
sdrv_ckgen_set_rate(cfg->clk, i2s->clk_rate);
|
|
} else {
|
|
ssdk_printf(SACI_ERR, "err saci clk is null ! \r\n");
|
|
return false;
|
|
}
|
|
|
|
i2s->stream[SDRV_I2S_STREAM_PLAYBACK].pack_mode = true;
|
|
i2s->stream[SDRV_I2S_STREAM_PLAYBACK].threshold = SACI_I2S_TX_THRESHOLD;
|
|
i2s->stream[SDRV_I2S_STREAM_CAPTURE].pack_mode = true;
|
|
i2s->stream[SDRV_I2S_STREAM_CAPTURE].threshold = SACI_I2S_RX_THRESHOLD;
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* @brief reset tx or rx
|
|
*
|
|
* @param i2s
|
|
* @param type
|
|
*/
|
|
static void saci_i2s_reset(sdrv_i2s_t *i2s, sdrv_i2s_stream_type_t type)
|
|
{
|
|
sdrv_i2s_config_t *cfg = i2s->cfg;
|
|
uint32_t base_addr = cfg->base;
|
|
|
|
if (type == SDRV_I2S_STREAM_PLAYBACK) {
|
|
|
|
/* reset tx */
|
|
saci_wr_reg_bits(base_addr + SACI_0000_SACI_CTRL, SACI_0000_TX_RST_MASK,
|
|
SACI_0000_TX_RST_POS, 0);
|
|
udelay(SACI_TX_RX_RESET);
|
|
saci_wr_reg_bits(base_addr + SACI_0000_SACI_CTRL, SACI_0000_TX_RST_MASK,
|
|
SACI_0000_TX_RST_POS, 1);
|
|
|
|
saci_wr_reg_bits(base_addr + SACI_0000_SACI_CTRL,
|
|
SACI_0000_TX_FIFO_FLUSH_MASK,
|
|
SACI_0000_TX_FIFO_FLUSH_POS, 1);
|
|
} else {
|
|
|
|
/* reset rx */
|
|
saci_wr_reg_bits(base_addr + SACI_0000_SACI_CTRL, SACI_0000_RX_RST_MASK,
|
|
SACI_0000_RX_RST_POS, 0);
|
|
udelay(SACI_TX_RX_RESET);
|
|
saci_wr_reg_bits(base_addr + SACI_0000_SACI_CTRL, SACI_0000_RX_RST_MASK,
|
|
SACI_0000_RX_RST_POS, 1);
|
|
|
|
saci_wr_reg_bits(base_addr + SACI_0000_SACI_CTRL,
|
|
SACI_0000_RX_FIFO_FLUSH_MASK,
|
|
SACI_0000_RX_FIFO_FLUSH_POS, 1);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief config tx or rx clock work mode
|
|
*
|
|
* This function is used to config tx or rx clock work mode.
|
|
* SDRV_I2S_CLK_TX_SYNC:tx clk synchronization, rx shares tx clk;
|
|
* SDRV_I2S_CLK_RX_SYNC:rx clk synchronization, tx shares rx clk;
|
|
* SDRV_I2S_CLK_TX_RX_ASYNC: tx and rx work on their respective clocks;
|
|
*
|
|
* @param i2s
|
|
* @param sync_mode
|
|
* @param i2s_slave
|
|
* @param type
|
|
* @return true
|
|
* @return false
|
|
*/
|
|
static bool saci_i2s_config_clk_sync_mode(sdrv_i2s_t *i2s,
|
|
sdrv_i2s_clk_sync_type_t sync_mode,
|
|
bool i2s_slave,
|
|
sdrv_i2s_stream_type_t type)
|
|
{
|
|
sdrv_i2s_stream_t *playback_stream = &i2s->stream[SDRV_I2S_STREAM_PLAYBACK];
|
|
sdrv_i2s_stream_t *capture_stream = &i2s->stream[SDRV_I2S_STREAM_CAPTURE];
|
|
sdrv_i2s_config_t *cfg = i2s->cfg;
|
|
uint32_t base_addr = cfg->base;
|
|
|
|
bool active =
|
|
(playback_stream->active || capture_stream->active) ? true : false;
|
|
|
|
if (i2s_slave) {
|
|
saci_i2s_reset(i2s, type);
|
|
}
|
|
|
|
switch (sync_mode) {
|
|
case SDRV_I2S_CLK_TX_SYNC:
|
|
if (!i2s_slave) {
|
|
|
|
if (!active) {
|
|
saci_i2s_reset(i2s, SDRV_I2S_STREAM_PLAYBACK);
|
|
|
|
/* config tx i2s master mode */
|
|
saci_wr_reg_bits(base_addr + SACI_1004_SACI_TX_CTRL,
|
|
SACI_1004_TX_MODE_MASK, SACI_1004_TX_MODE_POS,
|
|
I2S_MASTER);
|
|
saci_wr_reg_bits(base_addr + SACI_1004_SACI_TX_CTRL,
|
|
SACI_1004_TX_SYNC_MODE_MASK,
|
|
SACI_1004_TX_SYNC_MODE_POS, TRANSMITTER_ASYNC);
|
|
|
|
if (type == SDRV_I2S_STREAM_CAPTURE) {
|
|
saci_i2s_reset(i2s, SDRV_I2S_STREAM_CAPTURE);
|
|
/* config rx i2s slave mode ,rx share tx clk */
|
|
saci_wr_reg_bits(base_addr + SACI_3004_SACI_RX_CTRL,
|
|
SACI_3004_RX_MODE_MASK,
|
|
SACI_3004_RX_MODE_POS, I2S_SLAVE);
|
|
saci_wr_reg_bits(base_addr + SACI_3004_SACI_RX_CTRL,
|
|
SACI_3004_RX_SYNC_MODE_MASK,
|
|
SACI_3004_RX_SYNC_MODE_POS,
|
|
RECEIVER_SYNCFROM_INTX);
|
|
}
|
|
|
|
} else {
|
|
if (type == SDRV_I2S_STREAM_CAPTURE) {
|
|
saci_i2s_reset(i2s, SDRV_I2S_STREAM_CAPTURE);
|
|
|
|
/* config rx i2s slave mode option */
|
|
saci_wr_reg_bits(base_addr + SACI_3004_SACI_RX_CTRL,
|
|
SACI_3004_RX_MODE_MASK,
|
|
SACI_3004_RX_MODE_POS, I2S_SLAVE);
|
|
saci_wr_reg_bits(base_addr + SACI_3004_SACI_RX_CTRL,
|
|
SACI_3004_RX_SYNC_MODE_MASK,
|
|
SACI_3004_RX_SYNC_MODE_POS,
|
|
RECEIVER_SYNCFROM_INTX);
|
|
}
|
|
}
|
|
|
|
} else {
|
|
if (type == SDRV_I2S_STREAM_PLAYBACK) {
|
|
/* config tx i2s slave mode ,tx */
|
|
saci_wr_reg_bits(base_addr + SACI_1004_SACI_TX_CTRL,
|
|
SACI_1004_TX_MODE_MASK, SACI_1004_TX_MODE_POS,
|
|
I2S_SLAVE);
|
|
saci_wr_reg_bits(base_addr + SACI_1004_SACI_TX_CTRL,
|
|
SACI_1004_TX_SYNC_MODE_MASK,
|
|
SACI_1004_TX_SYNC_MODE_POS, TRANSMITTER_ASYNC);
|
|
} else {
|
|
/* config rx i2s slave mode ,share tx clk */
|
|
saci_wr_reg_bits(base_addr + SACI_3004_SACI_RX_CTRL,
|
|
SACI_3004_RX_MODE_MASK, SACI_3004_RX_MODE_POS,
|
|
I2S_SLAVE);
|
|
saci_wr_reg_bits(base_addr + SACI_3004_SACI_RX_CTRL,
|
|
SACI_3004_RX_SYNC_MODE_MASK,
|
|
SACI_3004_RX_SYNC_MODE_POS,
|
|
RECEIVER_SYNCFROM_INTX);
|
|
}
|
|
}
|
|
|
|
break;
|
|
case SDRV_I2S_CLK_RX_SYNC:
|
|
if (!i2s_slave) {
|
|
/* config rx i2s master mode */
|
|
if (!active) {
|
|
saci_i2s_reset(i2s, SDRV_I2S_STREAM_CAPTURE);
|
|
|
|
/* config rx i2s master mode,rx clk */
|
|
saci_wr_reg_bits(base_addr + SACI_3004_SACI_RX_CTRL,
|
|
SACI_3004_RX_MODE_MASK, SACI_3004_RX_MODE_POS,
|
|
I2S_MASTER);
|
|
saci_wr_reg_bits(base_addr + SACI_3004_SACI_RX_CTRL,
|
|
SACI_3004_RX_SYNC_MODE_MASK,
|
|
SACI_3004_RX_SYNC_MODE_POS, RECEIVER_ASYNC);
|
|
|
|
if (type == SDRV_I2S_STREAM_PLAYBACK) {
|
|
/* reset tx */
|
|
saci_i2s_reset(i2s, SDRV_I2S_STREAM_PLAYBACK);
|
|
|
|
/* config tx i2s slave mode ,share rx clk*/
|
|
saci_wr_reg_bits(base_addr + SACI_1004_SACI_TX_CTRL,
|
|
SACI_1004_TX_MODE_MASK,
|
|
SACI_1004_TX_MODE_POS, I2S_SLAVE);
|
|
saci_wr_reg_bits(base_addr + SACI_1004_SACI_TX_CTRL,
|
|
SACI_1004_TX_SYNC_MODE_MASK,
|
|
SACI_1004_TX_SYNC_MODE_POS,
|
|
TRANSMITTER_SYNCFROM_INRX);
|
|
}
|
|
|
|
} else {
|
|
|
|
if (type == SDRV_I2S_STREAM_PLAYBACK) {
|
|
/* reset tx */
|
|
saci_i2s_reset(i2s, SDRV_I2S_STREAM_PLAYBACK);
|
|
|
|
/* config tx i2s slave mode ,share rx clk*/
|
|
saci_wr_reg_bits(base_addr + SACI_1004_SACI_TX_CTRL,
|
|
SACI_1004_TX_MODE_MASK,
|
|
SACI_1004_TX_MODE_POS, I2S_SLAVE);
|
|
saci_wr_reg_bits(base_addr + SACI_1004_SACI_TX_CTRL,
|
|
SACI_1004_TX_SYNC_MODE_MASK,
|
|
SACI_1004_TX_SYNC_MODE_POS,
|
|
TRANSMITTER_SYNCFROM_INRX);
|
|
}
|
|
}
|
|
} else {
|
|
if (type == SDRV_I2S_STREAM_PLAYBACK) {
|
|
/* config tx i2s slave mode share rx clk */
|
|
saci_wr_reg_bits(base_addr + SACI_1004_SACI_TX_CTRL,
|
|
SACI_1004_TX_MODE_MASK, SACI_1004_TX_MODE_POS,
|
|
I2S_SLAVE);
|
|
saci_wr_reg_bits(base_addr + SACI_1004_SACI_TX_CTRL,
|
|
SACI_1004_TX_SYNC_MODE_MASK,
|
|
SACI_1004_TX_SYNC_MODE_POS,
|
|
TRANSMITTER_SYNCFROM_INRX);
|
|
} else {
|
|
/* config rx i2s slave mode ,rx clk */
|
|
saci_wr_reg_bits(base_addr + SACI_3004_SACI_RX_CTRL,
|
|
SACI_3004_RX_MODE_MASK, SACI_3004_RX_MODE_POS,
|
|
I2S_SLAVE);
|
|
saci_wr_reg_bits(base_addr + SACI_3004_SACI_RX_CTRL,
|
|
SACI_3004_RX_SYNC_MODE_MASK,
|
|
SACI_3004_RX_SYNC_MODE_POS, RECEIVER_ASYNC);
|
|
}
|
|
}
|
|
|
|
break;
|
|
case SDRV_I2S_CLK_TX_RX_ASYNC:
|
|
if (!i2s_slave) {
|
|
|
|
/* reset */
|
|
saci_i2s_reset(i2s, type);
|
|
|
|
if (type == SDRV_I2S_STREAM_PLAYBACK) {
|
|
/* config tx i2s master mode */
|
|
saci_wr_reg_bits(base_addr + SACI_1004_SACI_TX_CTRL,
|
|
SACI_1004_TX_MODE_MASK, SACI_1004_TX_MODE_POS,
|
|
I2S_MASTER);
|
|
saci_wr_reg_bits(base_addr + SACI_1004_SACI_TX_CTRL,
|
|
SACI_1004_TX_SYNC_MODE_MASK,
|
|
SACI_1004_TX_SYNC_MODE_POS, TRANSMITTER_ASYNC);
|
|
|
|
} else {
|
|
/* config rx i2s master mode option */
|
|
saci_wr_reg_bits(base_addr + SACI_3004_SACI_RX_CTRL,
|
|
SACI_3004_RX_MODE_MASK, SACI_3004_RX_MODE_POS,
|
|
I2S_MASTER);
|
|
saci_wr_reg_bits(base_addr + SACI_3004_SACI_RX_CTRL,
|
|
SACI_3004_RX_SYNC_MODE_MASK,
|
|
SACI_3004_RX_SYNC_MODE_POS, RECEIVER_ASYNC);
|
|
}
|
|
} else {
|
|
|
|
if (type == SDRV_I2S_STREAM_PLAYBACK) {
|
|
/* config tx i2s slave mode ,async*/
|
|
saci_wr_reg_bits(base_addr + SACI_1004_SACI_TX_CTRL,
|
|
SACI_1004_TX_MODE_MASK, SACI_1004_TX_MODE_POS,
|
|
I2S_SLAVE);
|
|
|
|
saci_wr_reg_bits(base_addr + SACI_1004_SACI_TX_CTRL,
|
|
SACI_1004_TX_SYNC_MODE_MASK,
|
|
SACI_1004_TX_SYNC_MODE_POS, TRANSMITTER_ASYNC);
|
|
|
|
} else {
|
|
|
|
/* config rx i2s slave mode , async */
|
|
saci_wr_reg_bits(base_addr + SACI_3004_SACI_RX_CTRL,
|
|
SACI_3004_RX_MODE_MASK, SACI_3004_RX_MODE_POS,
|
|
I2S_SLAVE);
|
|
|
|
saci_wr_reg_bits(base_addr + SACI_3004_SACI_RX_CTRL,
|
|
SACI_3004_RX_SYNC_MODE_MASK,
|
|
SACI_3004_RX_SYNC_MODE_POS, RECEIVER_ASYNC);
|
|
}
|
|
}
|
|
|
|
break;
|
|
default:
|
|
ssdk_printf(SACI_ERR,
|
|
"<%s>:tx unsupported clk sync mode sync_mode(%d) \r\n",
|
|
__func__, sync_mode);
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* @brief config tdm slots slot_width when sdrv work in tdm mode
|
|
*
|
|
* @param i2s
|
|
* @param mask
|
|
* @param slots
|
|
* @param slot_width
|
|
* @param type
|
|
* @return true
|
|
* @return false
|
|
*/
|
|
static bool saci_i2s_set_tdm_slot(sdrv_i2s_t *i2s, unsigned int mask,
|
|
unsigned int slots,
|
|
sdrv_i2s_slot_width_t slot_width,
|
|
sdrv_i2s_stream_type_t type)
|
|
{
|
|
|
|
sdrv_i2s_config_t *cfg = i2s->cfg;
|
|
uint32_t base_addr = cfg->base;
|
|
|
|
if ((slots < 3) || (slots > 16)) {
|
|
return false;
|
|
}
|
|
|
|
if (cfg->tx_chsel & cfg->rx_chsel) {
|
|
ssdk_printf(SACI_ERR, "%s:%i : TX RX DATA LINE select err \r\n",
|
|
__func__, __LINE__);
|
|
return false;
|
|
}
|
|
|
|
if (SDRV_I2S_STREAM_PLAYBACK == type) {
|
|
if (mask) {
|
|
|
|
/* enable tdm channels to tx */
|
|
saci_wr_reg_bits(base_addr + SACI_1010_SACI_TX_TDM_CTRL,
|
|
SACI_1010_OUT_SEL_MASK, SACI_1010_OUT_SEL_POS,
|
|
TDM_SELCH(cfg->tx_chsel));
|
|
saci_wr_reg_bits(base_addr + SACI_1010_SACI_TX_TDM_CTRL,
|
|
SACI_1010_CH_NUM_MASK, SACI_1010_CH_NUM_POS,
|
|
slots - 1);
|
|
saci_wr_reg_bits(base_addr + SACI_1010_SACI_TX_TDM_CTRL,
|
|
SACI_1010_CH_EN_MASK, SACI_1010_CH_EN_POS, mask);
|
|
/* config slot_width mode */
|
|
saci_wr_reg_bits(
|
|
base_addr + SACI_1004_SACI_TX_CTRL, SACI_1004_TX_CH_WIDTH_MASK,
|
|
SACI_1004_TX_CH_WIDTH_POS, to_saci_slot_width(slot_width));
|
|
}
|
|
}
|
|
|
|
if (SDRV_I2S_STREAM_CAPTURE == type) {
|
|
if (mask) {
|
|
/* enable tdm channels to rx */
|
|
saci_wr_reg_bits(base_addr + SACI_3010_SACI_RX_TDM_CTRL,
|
|
SACI_3010_IN_SEL_MASK, SACI_3010_IN_SEL_POS,
|
|
TDM_SELCH(cfg->rx_chsel));
|
|
saci_wr_reg_bits(base_addr + SACI_3010_SACI_RX_TDM_CTRL,
|
|
SACI_3010_CH_NUM_MASK, SACI_3010_CH_NUM_POS,
|
|
slots - 1);
|
|
saci_wr_reg_bits(base_addr + SACI_3010_SACI_RX_TDM_CTRL,
|
|
SACI_3010_CH_EN_MASK, SACI_3010_CH_EN_POS, mask);
|
|
/* config slot_width mode */
|
|
saci_wr_reg_bits(
|
|
base_addr + SACI_3004_SACI_RX_CTRL, SACI_3004_RX_CH_WIDTH_MASK,
|
|
SACI_3004_RX_CH_WIDTH_POS, to_saci_slot_width(slot_width));
|
|
}
|
|
}
|
|
ssdk_printf(SACI_DEBUG_LEVEL,
|
|
"func<%s>: RX TDM setitng rx line is : "
|
|
"%d,slots=%d,rx_mask:0x%x \r\n",
|
|
__func__, TDM_SELCH(cfg->rx_chsel), slots, mask);
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* @brief config tx params
|
|
*
|
|
* This function is used to config tx params ,such as data fomart ,master or
|
|
* slave
|
|
*
|
|
* @param i2s
|
|
* @param params
|
|
* @return true
|
|
* @return false
|
|
*/
|
|
static bool saci_i2s_set_params_tx(sdrv_i2s_t *i2s, sdrv_i2s_params_t *params)
|
|
{
|
|
sdrv_i2s_config_t *cfg = i2s->cfg;
|
|
uint32_t base_addr = cfg->base;
|
|
sdrv_i2s_stream_t *i2s_stream = &i2s->stream[SDRV_I2S_STREAM_PLAYBACK];
|
|
uint32_t channels = params->channels;
|
|
bool mono = (channels == 1) ? true : false;
|
|
bool pack_mode = i2s_stream->pack_mode;
|
|
sdrv_i2s_slot_width_t slot_width =
|
|
(sdrv_i2s_slot_width_t)params->sample_width;
|
|
|
|
if (params->slot_width) {
|
|
slot_width = params->slot_width;
|
|
}
|
|
/* config slot_width */
|
|
saci_wr_reg_bits(base_addr + SACI_1004_SACI_TX_CTRL,
|
|
SACI_1004_TX_CH_WIDTH_MASK, SACI_1004_TX_CH_WIDTH_POS,
|
|
to_saci_slot_width(slot_width));
|
|
|
|
/* config data_with */
|
|
saci_wr_reg_bits(base_addr + SACI_1004_SACI_TX_CTRL,
|
|
SACI_1004_TX_DATA_WIDTH_MASK, SACI_1004_TX_DATA_WIDTH_POS,
|
|
to_saci_data_width(params->sample_width));
|
|
|
|
if (mono) {
|
|
saci_wr_reg_bits(base_addr + SACI_1004_SACI_TX_CTRL,
|
|
SACI_1004_TX_MONO_MASK, SACI_1004_TX_MONO_POS, 1);
|
|
} else {
|
|
saci_wr_reg_bits(base_addr + SACI_1004_SACI_TX_CTRL,
|
|
SACI_1004_TX_MONO_MASK, SACI_1004_TX_MONO_POS, 0);
|
|
}
|
|
|
|
if (pack_mode) {
|
|
/* config data pack mode */
|
|
saci_wr_reg_bits(base_addr + SACI_1008_SACI_TX_FIFO_CTRL,
|
|
SACI_1008_DATA_PACK_MODE_MASK,
|
|
SACI_1008_DATA_PACK_MODE_POS, 1);
|
|
} else {
|
|
/* config data loose mode */
|
|
saci_wr_reg_bits(base_addr + SACI_1008_SACI_TX_FIFO_CTRL,
|
|
SACI_1008_DATA_PACK_MODE_MASK,
|
|
SACI_1008_DATA_PACK_MODE_POS, 0);
|
|
}
|
|
|
|
/* config dma threshold */
|
|
saci_wr_reg_bits(base_addr + SACI_1008_SACI_TX_FIFO_CTRL,
|
|
SACI_1008_THRD_MASK, SACI_1008_THRD_POS,
|
|
i2s_stream->threshold);
|
|
/* config tx data line */
|
|
saci_i2s_config_tx_data_line(base_addr, cfg->tx_chsel);
|
|
|
|
saci_i2s_set_tdm_slot(i2s, params->tx_slot_mask, params->slots,
|
|
params->slot_width, SDRV_I2S_STREAM_PLAYBACK);
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* @brief config rx params
|
|
*
|
|
* This function is used to config rx params ,such as data fomart ,master or
|
|
* slave
|
|
* @param i2s
|
|
* @param params
|
|
* @return true
|
|
* @return false
|
|
*/
|
|
static bool saci_i2s_set_params_rx(sdrv_i2s_t *i2s, sdrv_i2s_params_t *params)
|
|
{
|
|
sdrv_i2s_config_t *cfg = i2s->cfg;
|
|
uint32_t base_addr = cfg->base;
|
|
sdrv_i2s_stream_t *i2s_stream = &i2s->stream[SDRV_I2S_STREAM_CAPTURE];
|
|
uint32_t channels = params->channels;
|
|
bool mono = (channels == 1) ? true : false;
|
|
bool pack_mode = i2s_stream->pack_mode;
|
|
sdrv_i2s_slot_width_t slot_width =
|
|
(sdrv_i2s_slot_width_t)params->sample_width;
|
|
|
|
if (params->slot_width) {
|
|
slot_width = params->slot_width;
|
|
}
|
|
/* config slot_width */
|
|
saci_wr_reg_bits(base_addr + SACI_3004_SACI_RX_CTRL,
|
|
SACI_3004_RX_CH_WIDTH_MASK, SACI_3004_RX_CH_WIDTH_POS,
|
|
to_saci_slot_width(slot_width));
|
|
|
|
/* config data_with */
|
|
saci_wr_reg_bits(base_addr + SACI_3004_SACI_RX_CTRL,
|
|
SACI_3004_RX_DATA_WIDTH_MASK, SACI_3004_RX_DATA_WIDTH_POS,
|
|
to_saci_data_width(params->sample_width));
|
|
|
|
if (mono) {
|
|
saci_wr_reg_bits(base_addr + SACI_3004_SACI_RX_CTRL,
|
|
SACI_3004_RX_MONO_MASK, SACI_3004_RX_MONO_POS, 1);
|
|
}
|
|
if (pack_mode) {
|
|
/* config data pack mode */
|
|
saci_wr_reg_bits(base_addr + SACI_3008_SACI_RX_FIFO_CTRL,
|
|
SACI_3008_DATA_PACK_MODE_MASK,
|
|
SACI_3008_DATA_PACK_MODE_POS, 1);
|
|
} else {
|
|
/* config data loose mode */
|
|
saci_wr_reg_bits(base_addr + SACI_3008_SACI_RX_FIFO_CTRL,
|
|
SACI_3008_DATA_PACK_MODE_MASK,
|
|
SACI_3008_DATA_PACK_MODE_POS, 0);
|
|
}
|
|
|
|
/* config dma threshold */
|
|
saci_wr_reg_bits(base_addr + SACI_3008_SACI_RX_FIFO_CTRL,
|
|
SACI_3008_THRD_MASK, SACI_3008_THRD_POS,
|
|
i2s_stream->threshold);
|
|
/* config rx data line */
|
|
saci_i2s_config_rx_data_line(base_addr, cfg->rx_chsel);
|
|
|
|
saci_i2s_set_tdm_slot(i2s, params->rx_slot_mask, params->slots,
|
|
params->slot_width, SDRV_I2S_STREAM_CAPTURE);
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* @brief config sdrv i2s work mode
|
|
*
|
|
* * This function is used to conffig sdrv i2s work mode .dma or irq
|
|
*
|
|
* @param i2s
|
|
* @param type
|
|
* @return true
|
|
* @return false
|
|
*/
|
|
static bool saci_i2s_enable_transfer_mode(sdrv_i2s_t *i2s,
|
|
sdrv_i2s_stream_type_t type)
|
|
{
|
|
sdrv_i2s_stream_t *i2s_stream = &i2s->stream[type];
|
|
sdrv_i2s_params_t *i2s_params = &i2s_stream->params;
|
|
sdrv_i2s_config_t *cfg = i2s->cfg;
|
|
uint32_t base_addr = cfg->base;
|
|
|
|
if (type == SDRV_I2S_STREAM_PLAYBACK) {
|
|
if (i2s_params->usedma) {
|
|
saci_wr_reg_bits(base_addr + SACI_1004_SACI_TX_CTRL,
|
|
SACI_1004_TX_DMA_EN_MASK, SACI_1004_TX_DMA_EN_POS,
|
|
1);
|
|
} else {
|
|
saci_wr_reg_bits(base_addr + SACI_0004_SACI_IRQ_MASK,
|
|
SACI_0004_TX_FIFO_PRE_EMPTY_MASK,
|
|
SACI_0004_TX_FIFO_PRE_EMPTY_POS, 0);
|
|
}
|
|
|
|
} else {
|
|
|
|
if (i2s_params->usedma) {
|
|
saci_wr_reg_bits(base_addr + SACI_3004_SACI_RX_CTRL,
|
|
SACI_3004_RX_DMA_EN_MASK, SACI_3004_RX_DMA_EN_POS,
|
|
1);
|
|
|
|
saci_wr_reg_bits(base_addr + SACI_0010_SACI_LBK_CTRL,
|
|
SACI_0010_RX_TIMEOUT_DMA_EN_MASK,
|
|
SACI_0010_RX_TIMEOUT_DMA_EN_POS, 0);
|
|
} else {
|
|
saci_wr_reg_bits(base_addr + SACI_0004_SACI_IRQ_MASK,
|
|
SACI_0004_RX_FIFO_PRE_FULL_MASK,
|
|
SACI_0004_RX_FIFO_PRE_FULL_POS, 0);
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* @brief disable sdrv i2s work mode
|
|
*
|
|
* * This function is used to disable sdrv i2s work mode
|
|
*
|
|
* @param i2s
|
|
* @param type
|
|
* @return true
|
|
* @return false
|
|
*/
|
|
static bool saci_i2s_disable_transfer_mode(sdrv_i2s_t *i2s,
|
|
sdrv_i2s_stream_type_t type)
|
|
{
|
|
sdrv_i2s_config_t *cfg = i2s->cfg;
|
|
uint32_t base_addr = cfg->base;
|
|
sdrv_i2s_stream_t *i2s_stream = &i2s->stream[type];
|
|
sdrv_i2s_params_t *i2s_params = &i2s_stream->params;
|
|
|
|
if (type == SDRV_I2S_STREAM_PLAYBACK) {
|
|
if (i2s_params->usedma) {
|
|
saci_wr_reg_bits(base_addr + SACI_1004_SACI_TX_CTRL,
|
|
SACI_1004_TX_DMA_EN_MASK, SACI_1004_TX_DMA_EN_POS,
|
|
0);
|
|
} else {
|
|
saci_wr_reg_bits(base_addr + SACI_0004_SACI_IRQ_MASK,
|
|
SACI_0004_TX_FIFO_PRE_EMPTY_MASK,
|
|
SACI_0004_TX_FIFO_PRE_EMPTY_POS, 1);
|
|
}
|
|
|
|
} else {
|
|
|
|
if (i2s_params->usedma) {
|
|
saci_wr_reg_bits(base_addr + SACI_3004_SACI_RX_CTRL,
|
|
SACI_3004_RX_DMA_EN_MASK, SACI_3004_RX_DMA_EN_POS,
|
|
0);
|
|
|
|
saci_wr_reg_bits(base_addr + SACI_0010_SACI_LBK_CTRL,
|
|
SACI_0010_RX_TIMEOUT_DMA_EN_MASK,
|
|
SACI_0010_RX_TIMEOUT_DMA_EN_POS, 0);
|
|
} else {
|
|
saci_wr_reg_bits(base_addr + SACI_0004_SACI_IRQ_MASK,
|
|
SACI_0004_RX_FIFO_PRE_FULL_MASK,
|
|
SACI_0004_RX_FIFO_PRE_FULL_POS, 1);
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* @brief determine whether it works in slave mode
|
|
*
|
|
* @param i2s
|
|
* @param fmt
|
|
* @param type
|
|
* @return true
|
|
* @return false
|
|
*/
|
|
static bool saci_i2s_is_slave(sdrv_i2s_t *i2s, uint32_t fmt,
|
|
sdrv_i2s_stream_type_t type)
|
|
{
|
|
bool i2s_slave = false;
|
|
|
|
switch (fmt & SDRV_I2S_DAIFMT_MASTER_MASK) {
|
|
|
|
case SDRV_I2S_DAIFMT_CODEC_SLAVE:
|
|
|
|
i2s_slave = false;
|
|
break;
|
|
|
|
case SDRV_I2S_DAIFMT_CODEC_MASTER:
|
|
|
|
i2s_slave = true;
|
|
break;
|
|
default:
|
|
ssdk_printf(SACI_ERR, "tx func<%s>: i2s mastet or slave ? ? \r\n",
|
|
__func__);
|
|
}
|
|
|
|
return i2s_slave;
|
|
}
|
|
|
|
/**
|
|
* @brief enable tx or rx clock
|
|
* This function is used to enable tx or rx clock
|
|
* @param i2s
|
|
* @param params
|
|
* @param type
|
|
* @return true
|
|
* @return false
|
|
*/
|
|
static bool saci_i2s_enable_clk(sdrv_i2s_t *i2s, sdrv_i2s_params_t *params,
|
|
sdrv_i2s_stream_type_t type)
|
|
{
|
|
sdrv_i2s_config_t *cfg = i2s->cfg;
|
|
uint32_t base_addr = cfg->base;
|
|
uint32_t freq = saci_i2s_calculate_freq(params);
|
|
uint32_t src_clk_freq = i2s->clk_rate;
|
|
uint32_t div = 0;
|
|
uint32_t mclk_dir, mclk_sel;
|
|
|
|
/* div calc div */
|
|
div = DIV_ROUND_UP(src_clk_freq, freq);
|
|
if (div >= 1) {
|
|
div = div - 1;
|
|
}
|
|
|
|
ssdk_printf(SACI_DEBUG_LEVEL, "src_clk_freq(%d) bck(%d) freq div=%d \r\n",
|
|
src_clk_freq, freq, div);
|
|
|
|
if (type == SDRV_I2S_STREAM_PLAYBACK) {
|
|
saci_wr_reg_bits(base_addr + SACI_1000_SACI_TX_CLK_CTRL,
|
|
SACI_1000_CLK_SEL_MASK, SACI_1000_CLK_SEL_POS,
|
|
CLK_DIVIDER_SOURCE_TX_I2S_CLK);
|
|
|
|
saci_wr_reg_bits(base_addr + SACI_1000_SACI_TX_CLK_CTRL,
|
|
SACI_1000_CLK_DIV_MASK, SACI_1000_CLK_DIV_POS, div);
|
|
saci_wr_reg_bits(base_addr + SACI_1000_SACI_TX_CLK_CTRL,
|
|
SACI_1000_DIV_CLK_EN_MASK, SACI_1000_DIV_CLK_EN_POS,
|
|
1);
|
|
|
|
saci_wr_reg_bits(base_addr + SACI_1000_SACI_TX_CLK_CTRL,
|
|
SACI_1000_CLK_O_EN_MASK, SACI_1000_CLK_O_EN_POS, 1);
|
|
saci_wr_reg_bits(base_addr + SACI_1014_SACI_TX_EN, SACI_1014_EN_MASK,
|
|
SACI_1014_EN_POS, 1);
|
|
|
|
} else {
|
|
/* sel clk source CLK_DIVIDER_SOURCE_TX_I2S_CLK */
|
|
saci_wr_reg_bits(base_addr + SACI_3000_SACI_RX_CLK_CTRL,
|
|
SACI_3000_CLK_SEL_MASK, SACI_3000_CLK_SEL_POS,
|
|
CLK_DIVIDER_SOURCE_TX_I2S_CLK);
|
|
saci_wr_reg_bits(base_addr + SACI_3000_SACI_RX_CLK_CTRL,
|
|
SACI_3000_CLK_DEV_MASK, SACI_3000_CLK_DEV_POS, div);
|
|
saci_wr_reg_bits(base_addr + SACI_3000_SACI_RX_CLK_CTRL,
|
|
SACI_3000_DIV_CLK_EN_MASK, SACI_3000_DIV_CLK_EN_POS,
|
|
1);
|
|
|
|
saci_wr_reg_bits(base_addr + SACI_3000_SACI_RX_CLK_CTRL,
|
|
SACI_3000_CLK_O_EN_MASK, SACI_3000_CLK_O_EN_POS, 1);
|
|
saci_wr_reg_bits(base_addr + SACI_3014_SACI_RX_EN, SACI_3014_EN_MASK,
|
|
SACI_3014_EN_POS, 1);
|
|
}
|
|
|
|
#if defined(APB_SACI1_BASE) && defined(APB_SACI2_BASE)
|
|
|
|
base_addr =
|
|
(cfg->mclk_sel == SDRV_I2S_MCLK_0) ? APB_SACI1_BASE : APB_SACI2_BASE;
|
|
#endif
|
|
if (cfg->mclk_dir == SDRV_I2S_MCLK_OUT) {
|
|
mclk_dir = SACI_MCLK_OUT;
|
|
mclk_sel = SACI_MCLK_SEL_CLKGEN;
|
|
} else {
|
|
mclk_dir = SACI_MCLK_INPUT;
|
|
mclk_sel = SACI_MCLK_SEL_IOPAD;
|
|
}
|
|
|
|
saci_wr_reg_bits(base_addr + SACI_0000_SACI_CTRL, SACI_0000_MCLK_DIR_MASK,
|
|
SACI_0000_MCLK_DIR_POS, mclk_dir);
|
|
saci_wr_reg_bits(base_addr + SACI_0000_SACI_CTRL, SACI_0000_MCLK_SEL_MASK,
|
|
SACI_0000_MCLK_SEL_POS, mclk_sel);
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* @brief disable tx or rx clock
|
|
*
|
|
* This function is used to disable tx or rx clock
|
|
*
|
|
* @param i2s
|
|
* @param type
|
|
* @return true
|
|
* @return false
|
|
*/
|
|
static bool saci_i2s_disable_clk(sdrv_i2s_t *i2s, sdrv_i2s_stream_type_t type)
|
|
{
|
|
sdrv_i2s_config_t *cfg = i2s->cfg;
|
|
uint32_t base_addr = cfg->base;
|
|
uint32_t tx_reset = 0, rx_reset = 0, pdm0_reset = 0, pdm1_reset = 0;
|
|
uint32_t saci_rst;
|
|
|
|
if (type == SDRV_I2S_STREAM_PLAYBACK) {
|
|
|
|
saci_wr_reg_bits(base_addr + SACI_1014_SACI_TX_EN, SACI_1014_EN_MASK,
|
|
SACI_1014_EN_POS, 0);
|
|
|
|
/* reset tx */
|
|
saci_wr_reg_bits(base_addr + SACI_0000_SACI_CTRL, SACI_0000_TX_RST_MASK,
|
|
SACI_0000_TX_RST_POS, 0);
|
|
|
|
saci_wr_reg_bits(base_addr + SACI_1000_SACI_TX_CLK_CTRL,
|
|
SACI_1000_CLK_O_EN_MASK, SACI_1000_CLK_O_EN_POS, 0);
|
|
saci_wr_reg_bits(base_addr + SACI_1000_SACI_TX_CLK_CTRL,
|
|
SACI_1000_DIV_CLK_EN_MASK, SACI_1000_DIV_CLK_EN_POS,
|
|
0);
|
|
saci_wr_reg_bits(base_addr + SACI_1000_SACI_TX_CLK_CTRL,
|
|
SACI_1000_CLK_DIV_MASK, SACI_1000_CLK_DIV_POS, 0);
|
|
|
|
} else {
|
|
saci_wr_reg_bits(base_addr + SACI_3014_SACI_RX_EN, SACI_3014_EN_MASK,
|
|
SACI_3014_EN_POS, 0);
|
|
|
|
/* reset rx */
|
|
saci_wr_reg_bits(base_addr + SACI_0000_SACI_CTRL, SACI_0000_RX_RST_MASK,
|
|
SACI_0000_RX_RST_POS, 0);
|
|
|
|
saci_wr_reg_bits(base_addr + SACI_3000_SACI_RX_CLK_CTRL,
|
|
SACI_3000_CLK_O_EN_MASK, SACI_3000_CLK_O_EN_POS, 0);
|
|
saci_wr_reg_bits(base_addr + SACI_3000_SACI_RX_CLK_CTRL,
|
|
SACI_3000_CLK_DEV_MASK, SACI_3000_CLK_DEV_POS, 0);
|
|
saci_wr_reg_bits(base_addr + SACI_3000_SACI_RX_CLK_CTRL,
|
|
SACI_3000_DIV_CLK_EN_MASK, SACI_3000_DIV_CLK_EN_POS,
|
|
0);
|
|
}
|
|
|
|
tx_reset = saci_rd_reg_bits(base_addr + SACI_0000_SACI_CTRL,
|
|
SACI_0000_TX_RST_MASK, SACI_0000_TX_RST_POS);
|
|
rx_reset = saci_rd_reg_bits(base_addr + SACI_0000_SACI_CTRL,
|
|
SACI_0000_RX_RST_MASK, SACI_0000_RX_RST_POS);
|
|
pdm0_reset =
|
|
saci_rd_reg_bits(base_addr + SACI_0000_SACI_CTRL,
|
|
SACI_0000_PDM_CH0_RST_MASK, SACI_0000_PDM_CH0_RST_POS);
|
|
pdm1_reset =
|
|
saci_rd_reg_bits(base_addr + SACI_0000_SACI_CTRL,
|
|
SACI_0000_PDM_CH1_RST_MASK, SACI_0000_PDM_CH1_RST_POS);
|
|
|
|
saci_rst = tx_reset || rx_reset || pdm0_reset || pdm1_reset;
|
|
if (!saci_rst) {
|
|
saci_wr_reg_bits(base_addr + SACI_0000_SACI_CTRL,
|
|
SACI_0000_SACI_RST_MASK, SACI_0000_SACI_RST_POS, 0);
|
|
udelay(SACI_GLOBAL_RESET);
|
|
}
|
|
|
|
#if defined(APB_SACI1_BASE) && defined(APB_SACI2_BASE)
|
|
|
|
base_addr =
|
|
(cfg->mclk_sel == SDRV_I2S_MCLK_0) ? APB_SACI1_BASE : APB_SACI2_BASE;
|
|
#endif
|
|
|
|
saci_wr_reg_bits(base_addr + SACI_0000_SACI_CTRL, SACI_0000_MCLK_DIR_MASK,
|
|
SACI_0000_MCLK_DIR_POS, SACI_MCLK_INPUT);
|
|
|
|
return true;
|
|
}
|
|
|
|
#if CONFIG_SACI_SLAVE
|
|
|
|
/**
|
|
* @brief config tx clock when in master mode
|
|
*
|
|
* @param i2s
|
|
* @return true
|
|
* @return false
|
|
*/
|
|
static bool saci_i2s_set_tx_master_clk(sdrv_i2s_t *i2s)
|
|
{
|
|
sdrv_i2s_config_t *cfg = i2s->cfg;
|
|
uint32_t base_addr = cfg->base;
|
|
sdrv_i2s_stream_type_t type = SDRV_I2S_STREAM_PLAYBACK;
|
|
sdrv_i2s_stream_t *i2s_stream = &i2s->stream[type];
|
|
sdrv_i2s_params_t *params = &i2s_stream->params;
|
|
|
|
saci_wr_reg_bits(base_addr + SACI_1004_SACI_TX_CTRL, SACI_1004_TX_MODE_MASK,
|
|
SACI_1004_TX_MODE_POS, I2S_MASTER);
|
|
saci_wr_reg_bits(base_addr + SACI_1004_SACI_TX_CTRL,
|
|
SACI_1004_TX_SYNC_MODE_MASK, SACI_1004_TX_SYNC_MODE_POS,
|
|
TRANSMITTER_ASYNC);
|
|
|
|
saci_i2s_enable_clk(i2s, params, SDRV_I2S_STREAM_PLAYBACK);
|
|
saci_wr_reg_bits(base_addr + SACI_1000_SACI_TX_CLK_CTRL,
|
|
SACI_1000_DIV_CLK_EN_MASK, SACI_1000_DIV_CLK_EN_POS, 1);
|
|
saci_wr_reg_bits(base_addr + SACI_1000_SACI_TX_CLK_CTRL,
|
|
SACI_1000_CLK_O_EN_MASK, SACI_1000_CLK_O_EN_POS, 0);
|
|
return true;
|
|
}
|
|
|
|
#endif
|
|
|
|
/**
|
|
* @brief reset sdrv i2s when in work slave mode
|
|
*
|
|
* @param i2s
|
|
* @param type
|
|
* @return true
|
|
* @return false
|
|
*/
|
|
static bool saci_i2s_slave_mode_reset(sdrv_i2s_t *i2s,
|
|
sdrv_i2s_stream_type_t type)
|
|
{
|
|
|
|
sdrv_i2s_config_t *cfg = i2s->cfg;
|
|
uint32_t base_addr = cfg->base;
|
|
|
|
#if !CONFIG_SACI_SLAVE
|
|
int try = 10;
|
|
uint32_t en_stata = 0;
|
|
#endif
|
|
|
|
if (type == SDRV_I2S_STREAM_PLAYBACK) {
|
|
|
|
saci_wr_reg_bits(base_addr + SACI_1014_SACI_TX_EN, SACI_1014_EN_MASK,
|
|
SACI_1014_EN_POS, 0);
|
|
|
|
#if CONFIG_SACI_SLAVE
|
|
saci_i2s_set_tx_master_clk(i2s);
|
|
|
|
#else
|
|
saci_wr_reg_bits(base_addr + SACI_1018_SACI_TX_OFF_WAIT_TIME,
|
|
SACI_1018_CNT_MASK, SACI_1018_CNT_POS, 0x1000);
|
|
|
|
saci_wr_reg_bits(base_addr + SACI_0000_SACI_CTRL,
|
|
SACI_0000_TX_ASYNC_RST_MASK,
|
|
SACI_0000_TX_ASYNC_RST_POS, 0x0);
|
|
|
|
saci_wr_reg_bits(base_addr + SACI_0000_SACI_CTRL,
|
|
SACI_0000_TX_FIFO_FLUSH_MASK,
|
|
SACI_0000_TX_FIFO_FLUSH_POS, 1);
|
|
|
|
while (try > 0) {
|
|
udelay(1);
|
|
en_stata =
|
|
saci_rd_reg_bits(base_addr + SACI_1014_SACI_TX_EN,
|
|
SACI_1014_EN_STAT_MASK, SACI_1014_EN_STAT_POS);
|
|
if ((!en_stata)) {
|
|
break;
|
|
}
|
|
try--;
|
|
}
|
|
|
|
saci_wr_reg_bits(base_addr + SACI_0000_SACI_CTRL,
|
|
SACI_0000_TX_ASYNC_RST_MASK,
|
|
SACI_0000_TX_ASYNC_RST_POS, 0x1);
|
|
saci_wr_reg_bits(base_addr + SACI_0000_SACI_CTRL, SACI_0000_TX_RST_MASK,
|
|
SACI_0000_TX_RST_POS, 0);
|
|
|
|
#endif
|
|
} else {
|
|
|
|
saci_wr_reg_bits(base_addr + SACI_3014_SACI_RX_EN, SACI_3014_EN_MASK,
|
|
SACI_3014_EN_POS, 0);
|
|
|
|
#if !CONFIG_SACI_SLAVE
|
|
|
|
saci_wr_reg_bits(base_addr + SACI_3024_SACI_RX_OFF_WAIT_TIME,
|
|
SACI_3024_CNT_MASK, SACI_3024_CNT_POS, 0x2000);
|
|
|
|
saci_wr_reg_bits(base_addr + SACI_0000_SACI_CTRL,
|
|
SACI_0000_RX_ASYNC_RST_MASK,
|
|
SACI_0000_RX_ASYNC_RST_POS, 0x0);
|
|
|
|
saci_wr_reg_bits(base_addr + SACI_0000_SACI_CTRL,
|
|
SACI_0000_RX_FIFO_FLUSH_MASK,
|
|
SACI_0000_RX_FIFO_FLUSH_POS, 1);
|
|
|
|
while (try > 0) {
|
|
|
|
udelay(1);
|
|
en_stata =
|
|
saci_rd_reg_bits(base_addr + SACI_3014_SACI_RX_EN,
|
|
SACI_3014_EN_STAT_MASK, SACI_3014_EN_STAT_POS);
|
|
if (!en_stata) {
|
|
break;
|
|
}
|
|
try--;
|
|
}
|
|
|
|
saci_wr_reg_bits(base_addr + SACI_0000_SACI_CTRL,
|
|
SACI_0000_RX_ASYNC_RST_MASK,
|
|
SACI_0000_RX_ASYNC_RST_POS, 0x1);
|
|
/* reset rx */
|
|
saci_wr_reg_bits(base_addr + SACI_0000_SACI_CTRL, SACI_0000_RX_RST_MASK,
|
|
SACI_0000_RX_RST_POS, 0);
|
|
|
|
#endif
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* @brief enable tx or rx clock sync mode
|
|
*
|
|
* This function is used to config tx or rx clock work mode.
|
|
* SDRV_I2S_CLK_TX_SYNC:tx clk synchronization, rx shares tx clk;
|
|
* SDRV_I2S_CLK_RX_SYNC:rx clk synchronization, tx shares rx clk;
|
|
* SDRV_I2S_CLK_TX_RX_ASYNC: tx and rx work on their respective clocks;
|
|
*
|
|
* @param i2s
|
|
* @param sync_mode
|
|
* @param type
|
|
* @return true
|
|
* @return false
|
|
*/
|
|
static bool saci_i2s_enable_sync_mode(sdrv_i2s_t *i2s,
|
|
sdrv_i2s_clk_sync_type_t sync_mode,
|
|
sdrv_i2s_stream_type_t type)
|
|
{
|
|
|
|
sdrv_i2s_config_t *cfg = i2s->cfg;
|
|
uint32_t base_addr = cfg->base;
|
|
sdrv_i2s_stream_t *i2s_stream = &i2s->stream[type];
|
|
sdrv_i2s_stream_t *playback_stream = &i2s->stream[SDRV_I2S_STREAM_PLAYBACK];
|
|
sdrv_i2s_stream_t *capture_stream = &i2s->stream[SDRV_I2S_STREAM_CAPTURE];
|
|
sdrv_i2s_params_t *params = &i2s_stream->params;
|
|
uint32_t channels = params->channels;
|
|
bool i2s_slave = saci_i2s_is_slave(i2s, params->fmt, type);
|
|
int i = 0;
|
|
|
|
saci_i2s_enable_transfer_mode(i2s, type);
|
|
if (!i2s_slave) {
|
|
switch (sync_mode) {
|
|
case SDRV_I2S_CLK_TX_SYNC:
|
|
|
|
if (!playback_stream->clk_enable) {
|
|
playback_stream->clk_enable = true;
|
|
|
|
if (type == SDRV_I2S_STREAM_CAPTURE) {
|
|
saci_wr_reg_bits(base_addr + SACI_3014_SACI_RX_EN,
|
|
SACI_3014_EN_MASK, SACI_3014_EN_POS, 1);
|
|
|
|
/* config dma threshold */
|
|
saci_wr_reg_bits(base_addr + SACI_1008_SACI_TX_FIFO_CTRL,
|
|
SACI_1008_THRD_MASK, SACI_1008_THRD_POS,
|
|
channels);
|
|
|
|
for (i = 0; i < channels; i++) {
|
|
|
|
writel(0x00, base_addr + SACI_2000_SACI_TX_FIFO_DATA);
|
|
}
|
|
}
|
|
|
|
saci_i2s_enable_clk(i2s, params, SDRV_I2S_STREAM_PLAYBACK);
|
|
/* config dma threshold */
|
|
saci_wr_reg_bits(base_addr + SACI_1008_SACI_TX_FIFO_CTRL,
|
|
SACI_1008_THRD_MASK, SACI_1008_THRD_POS,
|
|
playback_stream->threshold);
|
|
|
|
ssdk_printf(SACI_DEBUG_LEVEL,
|
|
"<%s>:enable tx clk first "
|
|
"sync_mode(%d),i2s_slave(%d)$$ \r\n",
|
|
__func__, sync_mode, i2s_slave);
|
|
} else {
|
|
|
|
if (type == SDRV_I2S_STREAM_CAPTURE) {
|
|
|
|
saci_wr_reg_bits(base_addr + SACI_3014_SACI_RX_EN,
|
|
SACI_3014_EN_MASK, SACI_3014_EN_POS, 1);
|
|
}
|
|
ssdk_printf(SACI_DEBUG_LEVEL,
|
|
"<%s>:tx clk is ready "
|
|
"sync_mode(%d),i2s_slave(%d)$$ \r\n",
|
|
__func__, sync_mode, i2s_slave);
|
|
}
|
|
|
|
break;
|
|
case SDRV_I2S_CLK_RX_SYNC:
|
|
|
|
if (!capture_stream->clk_enable) {
|
|
capture_stream->clk_enable = true;
|
|
|
|
if (type == SDRV_I2S_STREAM_PLAYBACK) {
|
|
saci_wr_reg_bits(base_addr + SACI_1014_SACI_TX_EN,
|
|
SACI_1014_EN_MASK, SACI_1014_EN_POS, 1);
|
|
}
|
|
saci_i2s_enable_clk(i2s, params, SDRV_I2S_STREAM_CAPTURE);
|
|
|
|
ssdk_printf(SACI_DEBUG_LEVEL,
|
|
"<%s>:enable rx clk first "
|
|
"sync_mode(%d),i2s_slave(%d) \r\n",
|
|
__func__, sync_mode, i2s_slave);
|
|
|
|
} else {
|
|
|
|
if (type == SDRV_I2S_STREAM_PLAYBACK) {
|
|
|
|
saci_wr_reg_bits(base_addr + SACI_1014_SACI_TX_EN,
|
|
SACI_1014_EN_MASK, SACI_1014_EN_POS, 1);
|
|
}
|
|
ssdk_printf(SACI_DEBUG_LEVEL,
|
|
"<%s>:rx clk is ready first "
|
|
"sync_mode(%d),i2s_slave(%d)$$ \r\n",
|
|
__func__, sync_mode, i2s_slave);
|
|
}
|
|
|
|
break;
|
|
case SDRV_I2S_CLK_TX_RX_ASYNC:
|
|
|
|
saci_i2s_enable_clk(i2s, params, type);
|
|
|
|
if (type == SDRV_I2S_STREAM_PLAYBACK) {
|
|
playback_stream->clk_enable = true;
|
|
ssdk_printf(SACI_DEBUG_LEVEL,
|
|
"<%s>:tx clk is ready first "
|
|
"sync_mode(%d),i2s_slave(%d)$$ \r\n",
|
|
__func__, sync_mode, i2s_slave);
|
|
|
|
} else {
|
|
capture_stream->clk_enable = true;
|
|
ssdk_printf(SACI_DEBUG_LEVEL,
|
|
"<%s>:rx clk is ready first "
|
|
"sync_mode(%d),i2s_slave(%d)$$ \r\n",
|
|
__func__, sync_mode, i2s_slave);
|
|
}
|
|
|
|
break;
|
|
default:
|
|
ssdk_printf(SACI_ERR,
|
|
"<%s>:tx unsupported clk sync mode sync_mode(%d) \r\n",
|
|
__func__, sync_mode);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (i2s_slave) {
|
|
|
|
if (type == SDRV_I2S_STREAM_PLAYBACK) {
|
|
|
|
saci_wr_reg_bits(base_addr + SACI_1014_SACI_TX_EN,
|
|
SACI_1014_EN_MASK, SACI_1014_EN_POS, 1);
|
|
} else {
|
|
|
|
saci_wr_reg_bits(base_addr + SACI_3014_SACI_RX_EN,
|
|
SACI_3014_EN_MASK, SACI_3014_EN_POS, 1);
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* @brief disable tx or rx sync mode
|
|
*
|
|
* @param i2s
|
|
* @param sync_mode
|
|
* @param type
|
|
* @return true
|
|
* @return false
|
|
*/
|
|
static bool saci_i2s_disable_sync_mode(sdrv_i2s_t *i2s,
|
|
sdrv_i2s_clk_sync_type_t sync_mode,
|
|
sdrv_i2s_stream_type_t type)
|
|
{
|
|
|
|
sdrv_i2s_stream_t *i2s_stream = &i2s->stream[type];
|
|
sdrv_i2s_params_t *params = &i2s_stream->params;
|
|
sdrv_i2s_stream_t *playback_stream = &i2s->stream[SDRV_I2S_STREAM_PLAYBACK];
|
|
sdrv_i2s_stream_t *capture_stream = &i2s->stream[SDRV_I2S_STREAM_CAPTURE];
|
|
bool i2s_slave = saci_i2s_is_slave(i2s, params->fmt, type);
|
|
bool active =
|
|
(playback_stream->active || capture_stream->active) ? true : false;
|
|
|
|
saci_i2s_disable_transfer_mode(i2s, type);
|
|
|
|
if (!i2s_slave) {
|
|
switch (sync_mode) {
|
|
case SDRV_I2S_CLK_TX_SYNC:
|
|
|
|
if (!active) {
|
|
playback_stream->clk_enable = false;
|
|
|
|
if (type == SDRV_I2S_STREAM_CAPTURE) {
|
|
/* add async reset for rx */
|
|
saci_i2s_slave_mode_reset(i2s, SDRV_I2S_STREAM_CAPTURE);
|
|
}
|
|
saci_i2s_disable_clk(i2s, SDRV_I2S_STREAM_PLAYBACK);
|
|
|
|
} else {
|
|
if (type == SDRV_I2S_STREAM_CAPTURE) {
|
|
/* add async reset for rx */
|
|
saci_i2s_slave_mode_reset(i2s, SDRV_I2S_STREAM_CAPTURE);
|
|
}
|
|
}
|
|
|
|
break;
|
|
|
|
case SDRV_I2S_CLK_RX_SYNC:
|
|
|
|
if (!active) {
|
|
capture_stream->clk_enable = false;
|
|
|
|
if (type == SDRV_I2S_STREAM_PLAYBACK) {
|
|
|
|
/* add async reset for tx */
|
|
saci_i2s_slave_mode_reset(i2s, SDRV_I2S_STREAM_PLAYBACK);
|
|
}
|
|
|
|
saci_i2s_disable_clk(i2s, SDRV_I2S_STREAM_CAPTURE);
|
|
|
|
} else {
|
|
|
|
if (type == SDRV_I2S_STREAM_PLAYBACK) {
|
|
|
|
/*add async reset for tx */
|
|
saci_i2s_slave_mode_reset(i2s, SDRV_I2S_STREAM_PLAYBACK);
|
|
}
|
|
}
|
|
|
|
break;
|
|
|
|
case SDRV_I2S_CLK_TX_RX_ASYNC:
|
|
|
|
if (type == SDRV_I2S_STREAM_PLAYBACK) {
|
|
playback_stream->clk_enable = false;
|
|
saci_i2s_disable_clk(i2s, type);
|
|
|
|
} else {
|
|
capture_stream->clk_enable = false;
|
|
saci_i2s_disable_clk(i2s, type);
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
ssdk_printf(SACI_ERR,
|
|
"<%s>:tx unsupported clk sync mode sync_mode(%d) \r\n",
|
|
__func__, sync_mode);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (i2s_slave) {
|
|
saci_i2s_slave_mode_reset(i2s, type);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* @brief config dma busrt
|
|
*
|
|
* @param i2s
|
|
* @param params
|
|
* @param type
|
|
* @return unsigned int
|
|
*/
|
|
static unsigned int saci_i2s_set_dma_burst(sdrv_i2s_t *i2s,
|
|
sdrv_i2s_params_t *params,
|
|
sdrv_i2s_stream_type_t type)
|
|
{
|
|
unsigned int maxburst;
|
|
unsigned int slot_mask = (type == SDRV_I2S_STREAM_PLAYBACK)
|
|
? params->tx_slot_mask
|
|
: params->rx_slot_mask;
|
|
|
|
switch (params->fmt & SDRV_I2S_DAIFMT_FORMAT_MASK) {
|
|
|
|
case SDRV_I2S_DAIFMT_STANDARD_PHILLIPS:
|
|
case SDRV_I2S_DAIFMT_RIGHT_JUSTIFIED:
|
|
case SDRV_I2S_DAIFMT_LEFT_JUSTIFIED:
|
|
maxburst = SACI_DMA_BURST_STEREO;
|
|
break;
|
|
case SDRV_I2S_DAIFMT_DSP_A:
|
|
case SDRV_I2S_DAIFMT_DSP_B:
|
|
maxburst = bit_count(slot_mask);
|
|
break;
|
|
|
|
default:
|
|
maxburst = SACI_DMA_BURST_STEREO;
|
|
}
|
|
|
|
return maxburst;
|
|
}
|
|
|
|
/**
|
|
* @brief config tx or rx params
|
|
*
|
|
* @param i2s
|
|
* @param params
|
|
* @param type
|
|
* @return true
|
|
* @return false
|
|
*/
|
|
static bool saci_i2s_set_params(sdrv_i2s_t *i2s, sdrv_i2s_params_t *params,
|
|
sdrv_i2s_stream_type_t type)
|
|
{
|
|
sdrv_i2s_config_t *cfg = i2s->cfg;
|
|
uint32_t base_addr = cfg->base;
|
|
sdrv_i2s_clk_sync_type_t sync_mode = cfg->sync_mode;
|
|
sdrv_i2s_stream_t *playback_stream = &i2s->stream[SDRV_I2S_STREAM_PLAYBACK];
|
|
sdrv_i2s_stream_t *capture_stream = &i2s->stream[SDRV_I2S_STREAM_CAPTURE];
|
|
sdrv_i2s_stream_t *i2s_stream = &i2s->stream[type];
|
|
sdrv_i2s_dma_data_t *dma_data = &i2s_stream->dma_data;
|
|
uint8_t valid_bytes = params_to_valid_bytes(params);
|
|
bool i2s_slave;
|
|
unsigned int maxburst;
|
|
|
|
if (!valid_bytes) {
|
|
|
|
return false;
|
|
}
|
|
|
|
bool active =
|
|
(playback_stream->active || capture_stream->active) ? true : false;
|
|
|
|
i2s_slave = saci_i2s_is_slave(i2s, params->fmt, type);
|
|
|
|
saci_i2s_config_clk_sync_mode(i2s, sync_mode, i2s_slave, type);
|
|
|
|
if (i2s_slave) {
|
|
if (type == SDRV_I2S_STREAM_PLAYBACK) {
|
|
saci_i2s_set_format_tx(i2s, params->fmt);
|
|
saci_i2s_set_params_tx(i2s, params);
|
|
} else {
|
|
saci_i2s_set_format_rx(i2s, params->fmt);
|
|
saci_i2s_set_params_rx(i2s, params);
|
|
}
|
|
} else {
|
|
|
|
switch (sync_mode) {
|
|
case SDRV_I2S_CLK_TX_SYNC:
|
|
|
|
if (!active) {
|
|
if (type == SDRV_I2S_STREAM_PLAYBACK) {
|
|
saci_i2s_set_format_tx(i2s, params->fmt);
|
|
saci_i2s_set_params_tx(i2s, params);
|
|
|
|
} else {
|
|
|
|
saci_i2s_set_format_tx(i2s, params->fmt);
|
|
saci_i2s_set_params_tx(i2s, params);
|
|
saci_i2s_set_format_rx(i2s, params->fmt);
|
|
saci_i2s_set_params_rx(i2s, params);
|
|
}
|
|
} else {
|
|
if (type == SDRV_I2S_STREAM_PLAYBACK) {
|
|
|
|
ssdk_printf(
|
|
SACI_DEBUG_LEVEL,
|
|
"func<%s>: tx no need config format params \r\n",
|
|
__func__);
|
|
|
|
} else {
|
|
|
|
saci_i2s_set_format_rx(i2s, params->fmt);
|
|
saci_i2s_set_params_rx(i2s, params);
|
|
}
|
|
}
|
|
|
|
break;
|
|
case SDRV_I2S_CLK_RX_SYNC: {
|
|
|
|
if (!active) {
|
|
if (type == SDRV_I2S_STREAM_CAPTURE) {
|
|
saci_i2s_set_format_rx(i2s, params->fmt);
|
|
saci_i2s_set_params_rx(i2s, params);
|
|
|
|
} else {
|
|
saci_i2s_set_format_tx(i2s, params->fmt);
|
|
saci_i2s_set_params_tx(i2s, params);
|
|
saci_i2s_set_format_rx(i2s, params->fmt);
|
|
saci_i2s_set_params_rx(i2s, params);
|
|
}
|
|
} else {
|
|
|
|
if (type == SDRV_I2S_STREAM_PLAYBACK) {
|
|
saci_i2s_set_format_tx(i2s, params->fmt);
|
|
saci_i2s_set_params_tx(i2s, params);
|
|
} else {
|
|
|
|
ssdk_printf(
|
|
SACI_DEBUG_LEVEL,
|
|
" func<%s>: rx no need config format params \r\n",
|
|
__func__);
|
|
}
|
|
}
|
|
} break;
|
|
case SDRV_I2S_CLK_TX_RX_ASYNC:
|
|
|
|
if (type == SDRV_I2S_STREAM_PLAYBACK) {
|
|
saci_i2s_set_format_tx(i2s, params->fmt);
|
|
saci_i2s_set_params_tx(i2s, params);
|
|
} else {
|
|
saci_i2s_set_format_rx(i2s, params->fmt);
|
|
saci_i2s_set_params_rx(i2s, params);
|
|
}
|
|
|
|
break;
|
|
default:
|
|
ssdk_printf(SACI_ERR,
|
|
"<%s>:tx unsupported clk sync mode sync_mode(%d) "
|
|
"i2s_slave(%d)\r\n",
|
|
__func__, sync_mode, i2s_slave);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
maxburst = saci_i2s_set_dma_burst(i2s, params, type);
|
|
|
|
if (type == SDRV_I2S_STREAM_PLAYBACK) {
|
|
|
|
dma_data->addr = base_addr + SACI_2000_SACI_TX_FIFO_DATA;
|
|
dma_data->fifo_size = SACI_I2S_DEPTH;
|
|
dma_data->maxburst = maxburst;
|
|
if (i2s_stream->pack_mode) {
|
|
dma_data->addr_width = SDRV_DMA_BUSWIDTH_4_BYTES;
|
|
} else {
|
|
dma_data->addr_width = (sdrv_dma_bus_width_e)valid_bytes;
|
|
}
|
|
}
|
|
|
|
if (type == SDRV_I2S_STREAM_CAPTURE) {
|
|
|
|
dma_data->addr = base_addr + SACI_4000_SACI_RX_FIFO_DATA;
|
|
dma_data->fifo_size = SACI_I2S_DEPTH;
|
|
dma_data->maxburst = maxburst;
|
|
dma_data->addr_width = SDRV_DMA_BUSWIDTH_4_BYTES;
|
|
if (i2s_stream->pack_mode) {
|
|
dma_data->addr_width = SDRV_DMA_BUSWIDTH_4_BYTES;
|
|
} else {
|
|
dma_data->addr_width = (sdrv_dma_bus_width_e)valid_bytes;
|
|
}
|
|
}
|
|
|
|
i2s_stream->active = true;
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* @brief start or stop sdrv i2s according to cmd
|
|
*
|
|
* @param i2s
|
|
* @param cmd
|
|
* @param type
|
|
* @return true
|
|
* @return false
|
|
*/
|
|
static bool saci_i2s_trigger(sdrv_i2s_t *i2s, sdrv_i2s_trigger_op_t cmd,
|
|
sdrv_i2s_stream_type_t type)
|
|
{
|
|
sdrv_i2s_stream_t *i2s_stream = &i2s->stream[type];
|
|
sdrv_i2s_config_t *cfg = i2s->cfg;
|
|
sdrv_i2s_clk_sync_type_t sync_mode = cfg->sync_mode;
|
|
|
|
switch (cmd) {
|
|
case SDRV_I2S_TRIGGER_START:
|
|
|
|
saci_i2s_init_interrupt(i2s, type);
|
|
saci_i2s_enable_sync_mode(i2s, sync_mode, type);
|
|
|
|
break;
|
|
|
|
case SDRV_I2S_TRIGGER_STOP:
|
|
|
|
i2s_stream->active = false;
|
|
|
|
saci_i2s_disable_sync_mode(i2s, sync_mode, type);
|
|
saci_i2s_reset_interrupt(i2s, type);
|
|
|
|
break;
|
|
|
|
default:
|
|
ssdk_printf(SACI_ERR, "func<%s>: cmd not support ? ?\r\n", __func__);
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* @brief config mclk dirction: in or out according to board config
|
|
*
|
|
* @param i2s
|
|
*/
|
|
static void saci_i2s_config_mclk_dir(sdrv_i2s_t *i2s)
|
|
{
|
|
|
|
sdrv_i2s_config_t *cfg = i2s->cfg;
|
|
uint32_t base_addr = cfg->base;
|
|
sdrv_i2s_stream_t *playback_stream = &i2s->stream[SDRV_I2S_STREAM_PLAYBACK];
|
|
sdrv_i2s_stream_t *capture_stream = &i2s->stream[SDRV_I2S_STREAM_CAPTURE];
|
|
|
|
bool active =
|
|
(playback_stream->active || capture_stream->active) ? true : false;
|
|
|
|
#if defined(APB_SACI1_BASE) && defined(APB_SACI2_BASE)
|
|
|
|
base_addr =
|
|
(cfg->mclk_sel == SDRV_I2S_MCLK_0) ? APB_SACI1_BASE : APB_SACI2_BASE;
|
|
#endif
|
|
|
|
if (!active) {
|
|
saci_wr_reg_bits(base_addr + SACI_0000_SACI_CTRL,
|
|
SACI_0000_MCLK_DIR_MASK, SACI_0000_MCLK_DIR_POS,
|
|
SACI_MCLK_INPUT);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief enqueue a transfer to queue
|
|
*
|
|
* * This fuction is used to submit transfer, which's max num is
|
|
* SDRV_I2S_XFER_NUM_BUFFERS
|
|
*
|
|
* @param i2s
|
|
* @param transfer
|
|
* @param type
|
|
* @return sdrv_i2s_status_t
|
|
*/
|
|
static sdrv_i2s_status_t i2s_enqueue_transfer(sdrv_i2s_t *i2s,
|
|
sdrv_i2s_transfer_t *transfer,
|
|
sdrv_i2s_stream_type_t type)
|
|
{
|
|
sdrv_i2s_stream_t *i2s_stream = &i2s->stream[type];
|
|
uint8_t queue_appl = i2s_stream->queue_appl;
|
|
|
|
if (!transfer->data) {
|
|
ssdk_printf(SACI_ERR, "%s: transfer->data is NULL:\r\n", __func__);
|
|
return SDRV_I2S_STATUS_INVALID_PARAMS;
|
|
}
|
|
|
|
if (transfer->length == 0U) {
|
|
/* No data to send or receive */
|
|
ssdk_printf(SACI_ERR, "%s: transfer->length is zero:\r\n", __func__);
|
|
return SDRV_I2S_STATUS_INVALID_PARAMS;
|
|
}
|
|
|
|
if (i2s_stream->queue[queue_appl].length != 0UL) {
|
|
ssdk_printf(SACI_ERR,
|
|
"%s: transfer->length Previously prepared buffers not "
|
|
"processed yet\r\n",
|
|
__func__);
|
|
/* Previously prepared buffers not processed yet, reject request */
|
|
return SDRV_I2S_STATUS_OVERRUN;
|
|
}
|
|
|
|
/* Enqueue data */
|
|
i2s_stream->queue[queue_appl].data = transfer->data;
|
|
i2s_stream->queue[queue_appl].length = transfer->length;
|
|
i2s_stream->queue[queue_appl].id = transfer->id;
|
|
i2s_stream->queue_appl = (queue_appl + 1U) % SDRV_I2S_XFER_NUM_BUFFERS;
|
|
|
|
return SDRV_I2S_STATUS_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
* @brief handle tx data in irq mode
|
|
*
|
|
* This fuction is used to handle tx transfer in irq mode
|
|
*
|
|
* @param i2s
|
|
*/
|
|
static void sdrv_i2s_handle_tx_data_irq(sdrv_i2s_t *i2s)
|
|
{
|
|
sdrv_i2s_config_t *cfg = i2s->cfg;
|
|
uint32_t base_addr = cfg->base;
|
|
sdrv_i2s_stream_type_t type = SDRV_I2S_STREAM_PLAYBACK;
|
|
sdrv_i2s_stream_t *i2s_stream = &i2s->stream[type];
|
|
sdrv_i2s_params_t *params = &i2s_stream->params;
|
|
uint8_t queue_hw = i2s_stream->queue_hw;
|
|
sdrv_i2s_transfer_t *transfer;
|
|
uint32_t sample = 0;
|
|
uint8_t fifo_bytes = 4;
|
|
uint8_t valid_bytes = params_to_valid_bytes(params);
|
|
uint32_t num = SACI_I2S_DEPTH - saci_i2s_get_tx_fifo_dpt(base_addr);
|
|
int write_bytes = 0, temp = 0, length;
|
|
int expected_bytes = num;
|
|
|
|
if (!i2s_stream->active) {
|
|
|
|
return;
|
|
}
|
|
|
|
if (i2s_stream->pack_mode) {
|
|
if (valid_bytes <= 2) {
|
|
valid_bytes = fifo_bytes;
|
|
}
|
|
}
|
|
|
|
/* dequeue data */
|
|
transfer = &i2s_stream->queue[queue_hw];
|
|
if ((!transfer->length) || (!transfer->data)) {
|
|
if (i2s_stream->callback) {
|
|
(i2s_stream->callback)(transfer, i2s_stream->context,
|
|
SDRV_I2S_STATUS_UNDERRUN);
|
|
}
|
|
return;
|
|
}
|
|
|
|
while (write_bytes < expected_bytes) {
|
|
temp = transfer->length;
|
|
while (num > 0 && transfer->length > 0) {
|
|
|
|
if (transfer->length >= valid_bytes) {
|
|
memcpy(&sample, transfer->data, valid_bytes);
|
|
transfer->data += valid_bytes;
|
|
transfer->length -= valid_bytes;
|
|
} else {
|
|
|
|
memcpy(&sample, transfer->data, transfer->length);
|
|
transfer->data += transfer->length;
|
|
transfer->length = 0;
|
|
}
|
|
saci_i2s_write_tx_fifo_data(base_addr, sample);
|
|
num--;
|
|
}
|
|
if (temp - transfer->length > 0) {
|
|
length = temp - transfer->length;
|
|
write_bytes += length;
|
|
if (0 == transfer->length) {
|
|
/* dequeue next transfer */
|
|
i2s_stream->queue_hw =
|
|
(i2s_stream->queue_hw + 1U) % SDRV_I2S_XFER_NUM_BUFFERS;
|
|
if (i2s_stream->callback) {
|
|
(i2s_stream->callback)(transfer, i2s_stream->context,
|
|
SDRV_I2S_STATUS_SUCCESS);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (write_bytes < expected_bytes) {
|
|
transfer = &i2s_stream->queue[i2s_stream->queue_hw];
|
|
if ((!transfer->length) || (!transfer->data)) {
|
|
if (i2s_stream->callback) {
|
|
(i2s_stream->callback)(transfer, i2s_stream->context,
|
|
SDRV_I2S_STATUS_UNDERRUN);
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief handle rx data in irq mode
|
|
*
|
|
* This fuction is used to handle rx transfer in irq mode
|
|
*
|
|
*
|
|
* @param i2s
|
|
*/
|
|
void sdrv_i2s_handle_rx_data_irq(sdrv_i2s_t *i2s)
|
|
{
|
|
sdrv_i2s_config_t *cfg = i2s->cfg;
|
|
uint32_t base_addr = cfg->base;
|
|
sdrv_i2s_stream_type_t type = SDRV_I2S_STREAM_CAPTURE;
|
|
sdrv_i2s_stream_t *i2s_stream = &i2s->stream[type];
|
|
sdrv_i2s_params_t *params = &i2s_stream->params;
|
|
uint8_t queue_hw = i2s_stream->queue_hw;
|
|
sdrv_i2s_transfer_t *transfer;
|
|
uint32_t sample = 0;
|
|
uint8_t fifo_bytes = 4;
|
|
uint8_t valid_bytes = params_to_valid_bytes(params);
|
|
uint32_t num = saci_i2s_get_rx_fifo_dpt(base_addr);
|
|
int read_bytes = 0, temp = 0, length;
|
|
int expected_bytes = num;
|
|
|
|
if (!i2s_stream->active) {
|
|
|
|
return;
|
|
}
|
|
|
|
if (num == 0) {
|
|
return;
|
|
}
|
|
|
|
if (i2s_stream->pack_mode) {
|
|
if (valid_bytes <= 2) {
|
|
valid_bytes = fifo_bytes;
|
|
}
|
|
}
|
|
|
|
/* dequeue data */
|
|
transfer = &i2s_stream->queue[queue_hw];
|
|
if ((!transfer->length) || (!transfer->data)) {
|
|
if (i2s_stream->callback) {
|
|
(i2s_stream->callback)(transfer, i2s_stream->context,
|
|
SDRV_I2S_STATUS_OVERRUN);
|
|
}
|
|
return;
|
|
}
|
|
|
|
while (read_bytes < expected_bytes) {
|
|
temp = transfer->length;
|
|
|
|
while (num > 0 && transfer->length > 0) {
|
|
|
|
if (transfer->length >= valid_bytes) {
|
|
sample = saci_i2s_read_rx_fifo_data(base_addr);
|
|
memcpy(transfer->data, &sample, valid_bytes);
|
|
num--;
|
|
transfer->data += valid_bytes;
|
|
transfer->length -= valid_bytes;
|
|
} else {
|
|
transfer->length = 0;
|
|
}
|
|
}
|
|
|
|
if (temp - transfer->length > 0) {
|
|
length = temp - transfer->length;
|
|
read_bytes += length;
|
|
if (0 == transfer->length) {
|
|
/* dequeue next transfer */
|
|
i2s_stream->queue_hw =
|
|
(i2s_stream->queue_hw + 1U) % SDRV_I2S_XFER_NUM_BUFFERS;
|
|
if (i2s_stream->callback) {
|
|
(i2s_stream->callback)(transfer, i2s_stream->context,
|
|
SDRV_I2S_STATUS_SUCCESS);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (read_bytes < expected_bytes) {
|
|
transfer = &i2s_stream->queue[i2s_stream->queue_hw];
|
|
if ((!transfer->length) || (!transfer->data)) {
|
|
if (i2s_stream->callback) {
|
|
(i2s_stream->callback)(transfer, i2s_stream->context,
|
|
SDRV_I2S_STATUS_OVERRUN);
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief handle sdrv i2s irq
|
|
*
|
|
* This fuction is used to handle sdrv i2s irq
|
|
*
|
|
* @param irq
|
|
* @param arg
|
|
* @return int
|
|
*/
|
|
static int saci_i2s_irq_handler(uint32_t irq, void *arg)
|
|
{
|
|
sdrv_i2s_t *i2s = (sdrv_i2s_t *)arg;
|
|
sdrv_i2s_config_t *cfg = i2s->cfg;
|
|
uint32_t base_addr = cfg->base;
|
|
uint32_t irq_stat = saci_i2s_get_status_irq(base_addr);
|
|
uint32_t irq_mask = saci_i2s_get_mask_irq(base_addr);
|
|
uint32_t mask_value, irq_value;
|
|
|
|
mask_value = !((irq_mask & SACI_0008_TX_FIFO_PRE_EMPTY_MASK) >>
|
|
SACI_0008_TX_FIFO_PRE_EMPTY_POS);
|
|
irq_value = (irq_stat & SACI_0008_TX_FIFO_PRE_EMPTY_MASK) >>
|
|
SACI_0008_TX_FIFO_PRE_EMPTY_POS;
|
|
|
|
if (irq_value & mask_value) {
|
|
sdrv_i2s_handle_tx_data_irq(i2s);
|
|
saci_i2s_clear_tx_fifo_pre_empty_irq(base_addr);
|
|
}
|
|
|
|
mask_value =
|
|
!((irq_mask & SACI_0004_TX_FIFO_UDR_MASK) >> SACI_0004_TX_FIFO_UDR_POS);
|
|
irq_value =
|
|
(irq_stat & SACI_0008_TX_FIFO_UDR_MASK) >> SACI_0008_TX_FIFO_UDR_POS;
|
|
if (irq_value & mask_value) {
|
|
saci_i2s_clear_tx_fifo_udr_irq(base_addr);
|
|
}
|
|
|
|
mask_value = !((irq_mask & SACI_0004_RX_FIFO_PRE_FULL_MASK) >>
|
|
SACI_0004_RX_FIFO_PRE_FULL_POS);
|
|
irq_value = (irq_stat & SACI_0008_RX_FIFO_PRE_FULL_MASK) >>
|
|
SACI_0008_RX_FIFO_PRE_FULL_POS;
|
|
if (irq_value & mask_value) {
|
|
sdrv_i2s_handle_rx_data_irq(i2s);
|
|
saci_i2s_clear_rx_fifo_prefull_irq(base_addr);
|
|
}
|
|
|
|
mask_value =
|
|
!((irq_mask & SACI_0008_RX_FIFO_OVR_MASK) >> SACI_0008_RX_FIFO_OVR_POS);
|
|
irq_value =
|
|
(irq_stat & SACI_0008_RX_FIFO_OVR_MASK) >> SACI_0008_RX_FIFO_OVR_POS;
|
|
if (irq_value & mask_value) {
|
|
|
|
saci_i2s_clear_rx_fifo_ovr_irq(base_addr);
|
|
}
|
|
|
|
mask_value =
|
|
!((irq_mask & SACI_0004_RX_TIMEOUT_MASK) >> SACI_0004_RX_TIMEOUT_POS);
|
|
irq_value =
|
|
(irq_stat & SACI_0008_RX_TIMEOUT_MASK) >> SACI_0008_RX_TIMEOUT_POS;
|
|
if (irq_value & mask_value) {
|
|
saci_i2s_clear_rx_fifo_timeout_irq(base_addr);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief Calculate how much data there is in the dma buff to write for playback
|
|
*
|
|
* @param dma_buf
|
|
* @return int32_t
|
|
*/
|
|
static int32_t sdrv_i2s_playback_avail(sdrv_i2s_dma_buf_t *dma_buf)
|
|
{
|
|
int32_t avail = dma_buf->buffer_bytes + dma_buf->hw_ptr - dma_buf->appl_ptr;
|
|
|
|
if (avail < 0)
|
|
avail += dma_buf->boundary;
|
|
else if ((uint32_t)avail >= dma_buf->boundary)
|
|
avail -= dma_buf->boundary;
|
|
|
|
return avail;
|
|
}
|
|
|
|
/**
|
|
* @brief
|
|
*
|
|
* @param dma_buf
|
|
* @param params
|
|
* @return int32_t
|
|
*/
|
|
static int32_t sdrv_i2s_playback_time_elapsed(sdrv_i2s_dma_buf_t *dma_buf,
|
|
sdrv_i2s_params_t *params)
|
|
{
|
|
uint32_t avail = sdrv_i2s_playback_avail(dma_buf);
|
|
uint32_t elapsed_span = 0;
|
|
uint8_t valid_bytes = params_to_valid_bytes(params);
|
|
uint8_t channels = params->channels;
|
|
uint32_t offset = 0;
|
|
uint32_t old_appl_ptr = 0;
|
|
|
|
if (avail > dma_buf->buffer_bytes) {
|
|
old_appl_ptr = dma_buf->appl_ptr;
|
|
offset = dma_buf->appl_ptr % (channels * valid_bytes);
|
|
dma_buf->appl_ptr = dma_buf->hw_ptr + dma_buf->period_bytes + offset;
|
|
|
|
if (dma_buf->appl_ptr >= dma_buf->boundary) {
|
|
dma_buf->appl_ptr -= dma_buf->boundary;
|
|
}
|
|
|
|
elapsed_span = dma_buf->appl_ptr - old_appl_ptr;
|
|
}
|
|
|
|
return elapsed_span;
|
|
}
|
|
|
|
/**
|
|
* @brief Calculate how much data there is in the dma buff to read for capture
|
|
*
|
|
* @param dma_buf
|
|
* @return int32_t
|
|
*/
|
|
static int32_t sdrv_i2s_capture_avail(sdrv_i2s_dma_buf_t *dma_buf)
|
|
{
|
|
int32_t avail = dma_buf->hw_ptr - dma_buf->appl_ptr;
|
|
|
|
if (avail < 0)
|
|
avail += dma_buf->boundary;
|
|
|
|
return avail;
|
|
}
|
|
|
|
/**
|
|
* @brief
|
|
*
|
|
* @param dma_buf
|
|
* @return int32_t
|
|
*/
|
|
static int32_t sdrv_i2s_capture_time_elapsed(sdrv_i2s_dma_buf_t *dma_buf,
|
|
sdrv_i2s_params_t *params)
|
|
{
|
|
uint32_t avail = sdrv_i2s_capture_avail(dma_buf);
|
|
uint32_t elapsed_span = 0;
|
|
uint32_t offset = 0;
|
|
uint32_t channels = params->channels;
|
|
uint8_t valid_bytes = params_to_valid_bytes(params);
|
|
uint32_t old_appl_ptr = 0;
|
|
|
|
if (avail > dma_buf->buffer_bytes) {
|
|
old_appl_ptr = dma_buf->appl_ptr;
|
|
offset = dma_buf->appl_ptr % (channels * valid_bytes);
|
|
dma_buf->appl_ptr = dma_buf->hw_ptr - dma_buf->period_bytes + offset;
|
|
elapsed_span = dma_buf->appl_ptr - old_appl_ptr;
|
|
}
|
|
|
|
return elapsed_span;
|
|
}
|
|
|
|
/* recalcuate the boundary within 32bit */
|
|
static uint32_t sdrv_i2s_recalculate_boundary(sdrv_i2s_dma_buf_t *dma_buf)
|
|
{
|
|
uint32_t boundary;
|
|
uint32_t max = 0x7fffffffUL - dma_buf->buffer_bytes;
|
|
|
|
if (!dma_buf->buffer_bytes)
|
|
return 0;
|
|
boundary = dma_buf->buffer_bytes;
|
|
while (boundary * 2 <= max) {
|
|
boundary *= 2;
|
|
}
|
|
return boundary;
|
|
}
|
|
|
|
/**
|
|
* @brief create dma buff
|
|
*
|
|
* @param dma_buf
|
|
* @param params
|
|
* @return true
|
|
* @return false
|
|
*/
|
|
static bool sdrv_i2s_dma_create(sdrv_i2s_dma_buf_t *dma_buf,
|
|
sdrv_i2s_params_t *params)
|
|
{
|
|
uint32_t length = dma_buf->period_bytes * dma_buf->periods;
|
|
|
|
if (!params->dma_buff) {
|
|
ssdk_printf(SACI_ERR, "%s: error:\r\n", __func__);
|
|
dma_buf->addr = NULL;
|
|
return false;
|
|
}
|
|
|
|
dma_buf->addr = params->dma_buff;
|
|
dma_buf->appl_ptr = 0;
|
|
dma_buf->hw_ptr = 0;
|
|
dma_buf->buffer_bytes = length;
|
|
dma_buf->boundary = sdrv_i2s_recalculate_boundary(dma_buf);
|
|
memset(dma_buf->addr, 0x00, length);
|
|
arch_clean_invalidate_cache_range((addr_t)dma_buf->addr, length);
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* @brief release dma buff
|
|
*
|
|
* @param dma_buf
|
|
*/
|
|
static void sdrv_i2s_dma_free(sdrv_i2s_dma_buf_t *dma_buf)
|
|
{
|
|
|
|
dma_buf->addr = NULL;
|
|
}
|
|
|
|
/**
|
|
* @brief write data to dma buff
|
|
*
|
|
* @param dma_buf
|
|
* @param buffer
|
|
* @param len
|
|
* @return int
|
|
*/
|
|
static int sdrv_i2s_dma_write(sdrv_i2s_dma_buf_t *dma_buf, uint8_t *buffer,
|
|
uint32_t len)
|
|
{
|
|
|
|
uint32_t offset;
|
|
uint32_t size;
|
|
uint32_t avail, appl_ptr;
|
|
|
|
if (!dma_buf->addr) {
|
|
ssdk_printf(SACI_ERR, "%s:dma_buf is null ,cannot write \r\n",
|
|
__func__);
|
|
return -1;
|
|
}
|
|
|
|
avail = sdrv_i2s_playback_avail(dma_buf);
|
|
if (!avail) {
|
|
return avail;
|
|
}
|
|
appl_ptr = dma_buf->appl_ptr;
|
|
offset = appl_ptr % dma_buf->buffer_bytes;
|
|
len = min(len, avail);
|
|
size = min(len, dma_buf->buffer_bytes - offset);
|
|
memcpy(dma_buf->addr + offset, buffer, size);
|
|
|
|
if ((len - size) > 0) {
|
|
memcpy(dma_buf->addr, buffer + size, len - size);
|
|
}
|
|
|
|
appl_ptr += len;
|
|
if (appl_ptr >= dma_buf->boundary) {
|
|
appl_ptr -= dma_buf->boundary;
|
|
}
|
|
|
|
dma_buf->appl_ptr = appl_ptr;
|
|
|
|
arch_clean_cache_range((addr_t)(dma_buf->addr + offset), size);
|
|
|
|
if ((len - size) > 0) {
|
|
arch_clean_cache_range((addr_t)dma_buf->addr, len - size);
|
|
}
|
|
|
|
return len;
|
|
}
|
|
|
|
/**
|
|
* @brief read data from dma buf
|
|
*
|
|
* @param dma_buf
|
|
* @param buffer
|
|
* @param len
|
|
* @return int
|
|
*/
|
|
static int sdrv_i2s_dma_read(sdrv_i2s_dma_buf_t *dma_buf, uint8_t *buffer,
|
|
uint32_t len)
|
|
{
|
|
uint32_t offset;
|
|
uint32_t size;
|
|
uint32_t avail, appl_ptr;
|
|
|
|
if (!dma_buf->addr) {
|
|
ssdk_printf(SACI_ERR, "%s:dma_buf is null ,cannot write \r\n",
|
|
__func__);
|
|
return -1;
|
|
}
|
|
|
|
avail = sdrv_i2s_capture_avail(dma_buf);
|
|
if (!avail) {
|
|
return avail;
|
|
}
|
|
appl_ptr = dma_buf->appl_ptr;
|
|
offset = appl_ptr % dma_buf->buffer_bytes;
|
|
len = min(len, avail);
|
|
size = min(len, dma_buf->buffer_bytes - offset);
|
|
|
|
arch_invalidate_cache_range((addr_t)(dma_buf->addr + offset), size);
|
|
|
|
if ((len - size) > 0) {
|
|
arch_invalidate_cache_range((addr_t)dma_buf->addr, len - size);
|
|
}
|
|
|
|
memcpy(buffer, dma_buf->addr + offset, size);
|
|
memcpy(buffer + size, dma_buf->addr, len - size);
|
|
|
|
appl_ptr += len;
|
|
if (appl_ptr >= dma_buf->boundary) {
|
|
appl_ptr -= dma_buf->boundary;
|
|
}
|
|
|
|
dma_buf->appl_ptr = appl_ptr;
|
|
|
|
return len;
|
|
}
|
|
|
|
static sdrv_i2s_status_t sdrv_i2s_handle_playback_dma_data(sdrv_i2s_t *i2s,
|
|
int expected_bytes)
|
|
{
|
|
sdrv_i2s_stream_type_t type = SDRV_I2S_STREAM_PLAYBACK;
|
|
sdrv_i2s_stream_t *i2s_stream = &i2s->stream[type];
|
|
sdrv_i2s_params_t *i2s_params = &i2s_stream->params;
|
|
sdrv_i2s_dma_buf_t *dma_buf = &i2s_stream->dma_buf;
|
|
uint8_t queue_hw = i2s_stream->queue_hw;
|
|
sdrv_i2s_transfer_t *transfer;
|
|
int write_bytes = 0, temp = 0;
|
|
|
|
/* dequeue data */
|
|
transfer = &i2s_stream->queue[queue_hw];
|
|
if ((!transfer->length) || (!transfer->data)) {
|
|
if (i2s_stream->callback) {
|
|
(i2s_stream->callback)(transfer, i2s_stream->context,
|
|
SDRV_I2S_STATUS_UNDERRUN);
|
|
}
|
|
return SDRV_I2S_STATUS_UNDERRUN;
|
|
}
|
|
|
|
sdrv_i2s_playback_time_elapsed(dma_buf, i2s_params);
|
|
|
|
while (write_bytes < expected_bytes) {
|
|
|
|
temp = sdrv_i2s_dma_write(dma_buf, transfer->data, transfer->length);
|
|
if (temp > 0) {
|
|
write_bytes += temp;
|
|
|
|
transfer->data += temp;
|
|
transfer->length -= temp;
|
|
if (0 == transfer->length) {
|
|
/* dequeue next transfer */
|
|
i2s_stream->queue_hw =
|
|
(i2s_stream->queue_hw + 1U) % SDRV_I2S_XFER_NUM_BUFFERS;
|
|
if (i2s_stream->callback) {
|
|
(i2s_stream->callback)(transfer, i2s_stream->context,
|
|
SDRV_I2S_STATUS_SUCCESS);
|
|
}
|
|
}
|
|
} else {
|
|
return SDRV_I2S_STATUS_SUCCESS;
|
|
}
|
|
|
|
if (write_bytes < expected_bytes) {
|
|
transfer = &i2s_stream->queue[i2s_stream->queue_hw];
|
|
if ((!transfer->length) || (!transfer->data)) {
|
|
if (i2s_stream->callback) {
|
|
(i2s_stream->callback)(transfer, i2s_stream->context,
|
|
SDRV_I2S_STATUS_UNDERRUN);
|
|
}
|
|
return SDRV_I2S_STATUS_UNDERRUN;
|
|
}
|
|
}
|
|
}
|
|
|
|
return SDRV_I2S_STATUS_SUCCESS;
|
|
}
|
|
|
|
static sdrv_i2s_status_t sdrv_i2s_handle_capture_dma_data(sdrv_i2s_t *i2s,
|
|
int expected_bytes)
|
|
{
|
|
sdrv_i2s_stream_type_t type = SDRV_I2S_STREAM_CAPTURE;
|
|
sdrv_i2s_stream_t *i2s_stream = &i2s->stream[type];
|
|
sdrv_i2s_params_t *i2s_params = &i2s_stream->params;
|
|
sdrv_i2s_dma_buf_t *dma_buf = &i2s_stream->dma_buf;
|
|
uint8_t queue_hw = i2s_stream->queue_hw;
|
|
sdrv_i2s_transfer_t *transfer;
|
|
int read_bytes = 0, temp = 0;
|
|
|
|
/* dequeue data */
|
|
transfer = &i2s_stream->queue[queue_hw];
|
|
if ((!transfer->length) || (!transfer->data)) {
|
|
if (i2s_stream->callback) {
|
|
(i2s_stream->callback)(transfer, i2s_stream->context,
|
|
SDRV_I2S_STATUS_OVERRUN);
|
|
}
|
|
return SDRV_I2S_STATUS_OVERRUN;
|
|
}
|
|
|
|
sdrv_i2s_capture_time_elapsed(dma_buf, i2s_params);
|
|
while (read_bytes < expected_bytes) {
|
|
|
|
temp = sdrv_i2s_dma_read(dma_buf, transfer->data, transfer->length);
|
|
if (temp > 0) {
|
|
read_bytes += temp;
|
|
|
|
transfer->data += temp;
|
|
transfer->length -= temp;
|
|
if (!transfer->length) {
|
|
/* dequeue next transfer */
|
|
i2s_stream->queue_hw =
|
|
(i2s_stream->queue_hw + 1U) % SDRV_I2S_XFER_NUM_BUFFERS;
|
|
if (i2s_stream->callback != NULL) {
|
|
(i2s_stream->callback)(transfer, i2s_stream->context,
|
|
SDRV_I2S_STATUS_SUCCESS);
|
|
}
|
|
}
|
|
} else {
|
|
return SDRV_I2S_STATUS_SUCCESS;
|
|
}
|
|
|
|
if (read_bytes < expected_bytes) {
|
|
transfer = &i2s_stream->queue[i2s_stream->queue_hw];
|
|
if ((!transfer->length) || (!transfer->data)) {
|
|
if (i2s_stream->callback) {
|
|
(i2s_stream->callback)(transfer, i2s_stream->context,
|
|
SDRV_I2S_STATUS_OVERRUN);
|
|
}
|
|
return SDRV_I2S_STATUS_OVERRUN;
|
|
}
|
|
}
|
|
}
|
|
|
|
return SDRV_I2S_STATUS_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
* @brief tx dma complete callback
|
|
*
|
|
* This fuction is used to handle tx transfer in dma mode
|
|
*
|
|
* @param status
|
|
* @param param
|
|
* @param context
|
|
*/
|
|
static void sdrv_i2s_tx_transfer_dma_callback(uint32_t status, uint32_t param,
|
|
void *context)
|
|
{
|
|
|
|
sdrv_i2s_t *i2s = (sdrv_i2s_t *)context;
|
|
sdrv_i2s_stream_type_t type = SDRV_I2S_STREAM_PLAYBACK;
|
|
sdrv_i2s_stream_t *i2s_stream = &i2s->stream[type];
|
|
sdrv_i2s_dma_buf_t *dma_buf = &i2s_stream->dma_buf;
|
|
uint32_t hw_ptr = dma_buf->hw_ptr;
|
|
int expected_bytes = dma_buf->period_bytes;
|
|
|
|
if (SDRV_DMA_COMPLETED == status) {
|
|
return;
|
|
}
|
|
|
|
if (!i2s_stream->active) {
|
|
return;
|
|
}
|
|
|
|
if (SDRV_DMA_BLOCK_DONE == status) {
|
|
hw_ptr += dma_buf->period_bytes;
|
|
if (hw_ptr >= dma_buf->boundary) {
|
|
hw_ptr -= dma_buf->boundary;
|
|
}
|
|
dma_buf->hw_ptr = hw_ptr;
|
|
sdrv_i2s_handle_playback_dma_data(i2s, expected_bytes);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief rx dma complete callback
|
|
*
|
|
* This fuction is used to handle rx transfer in dma mode
|
|
*
|
|
* @param status
|
|
* @param param
|
|
* @param context
|
|
*/
|
|
static void sdrv_i2s_rx_transfer_dma_callback(uint32_t status, uint32_t param,
|
|
void *context)
|
|
{
|
|
|
|
sdrv_i2s_t *i2s = (sdrv_i2s_t *)context;
|
|
sdrv_i2s_stream_type_t type = SDRV_I2S_STREAM_CAPTURE;
|
|
sdrv_i2s_stream_t *i2s_stream = &i2s->stream[type];
|
|
sdrv_i2s_dma_buf_t *dma_buf = &i2s_stream->dma_buf;
|
|
uint32_t hw_ptr = dma_buf->hw_ptr;
|
|
int expected_bytes = dma_buf->period_bytes;
|
|
|
|
if (SDRV_DMA_COMPLETED == status) {
|
|
return;
|
|
}
|
|
|
|
if (!i2s_stream->active) {
|
|
return;
|
|
}
|
|
|
|
if (SDRV_DMA_BLOCK_DONE == status) {
|
|
hw_ptr += dma_buf->period_bytes;
|
|
if (hw_ptr >= dma_buf->boundary) {
|
|
hw_ptr -= dma_buf->boundary;
|
|
}
|
|
dma_buf->hw_ptr = hw_ptr;
|
|
sdrv_i2s_handle_capture_dma_data(i2s, expected_bytes);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief config dma linklist for tx or rx
|
|
*
|
|
* @param i2s
|
|
* @param type
|
|
* @return true
|
|
* @return false
|
|
*/
|
|
static bool sdrv_i2s_set_dma_link_list(sdrv_i2s_t *i2s,
|
|
sdrv_i2s_stream_type_t type)
|
|
{
|
|
sdrv_i2s_stream_t *i2s_stream = &i2s->stream[type];
|
|
sdrv_i2s_config_t *cfg = i2s->cfg;
|
|
sdrv_i2s_dma_buf_t *dma_buf = &i2s_stream->dma_buf;
|
|
sdrv_i2s_dma_data_t *dma_data = &i2s_stream->dma_data;
|
|
sdrv_dma_channel_config_t xfer_config;
|
|
int index = 0;
|
|
|
|
/* 3.get default config */
|
|
sdrv_dma_init_channel_config(&xfer_config, &i2s_stream->dma_instance);
|
|
if (SDRV_I2S_STREAM_PLAYBACK == type) {
|
|
|
|
/* 4.modify config param */
|
|
xfer_config.channel_id = cfg->tx_channel_id; /* select channel */
|
|
xfer_config.xfer_type =
|
|
SDRV_DMA_DIR_MEM2DEV; /* mem2mem or mem2dev or dev2mem */
|
|
xfer_config.xfer_mode =
|
|
SDRV_DMA_TRANSFER_MODE_LINKLIST; /* single or continuous or
|
|
linklist*/
|
|
xfer_config.buffer_mode = SDRV_DMA_SINGLE_BUFFER;
|
|
xfer_config.src_inc =
|
|
SDRV_DMA_ADDR_INC; /* source address increase or not */
|
|
xfer_config.dst_inc =
|
|
SDRV_DMA_ADDR_NO_INC; /* destination address increase or not */
|
|
xfer_config.src_width = dma_data->addr_width; /* source address width */
|
|
xfer_config.dst_width =
|
|
dma_data->addr_width; /* destination address width */
|
|
xfer_config.src_burst_len =
|
|
dma_data->maxburst; /* source burst_length (transfer level) */
|
|
xfer_config.dst_burst_len =
|
|
dma_data->maxburst; /* source burst_length */
|
|
|
|
xfer_config.loop_mode =
|
|
SDRV_DMA_LOOP_MODE_1; /* mem2mem(MODE_0) mem2dev/dev2mem(MODE_1) */
|
|
xfer_config.dst_port_sel =
|
|
SDRV_DMA_PORT_AHB32; /* periph -> SDRV_DMA_PORT_AHB32 */
|
|
xfer_config.dst_cache =
|
|
0; /* mem2mem need to set 1, mem2dev/dev2mem set 0 */
|
|
xfer_config.interrupt_type =
|
|
SDRV_DMA_EVERY_MAD_DONE; /* set interrupt type */
|
|
xfer_config.trig_mode =
|
|
SDRV_DMA_TRIGGER_BY_HARDWARE; /* non-first linklist use
|
|
SDRV_DMA_TRIGGER_BY_INTERNAL_EVENT
|
|
*/
|
|
/* 5.init linklist descriptor */
|
|
for (index = 0; index < dma_buf->periods; index++) {
|
|
xfer_config.src_addr =
|
|
(paddr_t)(dma_buf->addr + dma_buf->period_bytes *
|
|
index); /* set source address */
|
|
xfer_config.dst_addr =
|
|
(paddr_t)dma_data->addr; /* set destination address */
|
|
xfer_config.xfer_bytes =
|
|
dma_buf->period_bytes; /* transaction bytes */
|
|
|
|
if (index == 0) { /* first linklist */
|
|
|
|
xfer_config.linklist_addr =
|
|
(paddr_t)&dma_linklists[type]
|
|
[(index + 1) % dma_buf->periods];
|
|
xfer_config.trig_mode = SDRV_DMA_TRIGGER_BY_HARDWARE;
|
|
xfer_config.linklist_mad_type = SDRV_DMA_LINKLIST_NORMAL_MAD;
|
|
} else if (index == (dma_buf->periods - 1)) { /* last linklist */
|
|
xfer_config.linklist_addr =
|
|
(paddr_t)&dma_linklists[type]
|
|
[0]; /* pointer to next linklist
|
|
descriptor,0 is over */
|
|
xfer_config.trig_mode = SDRV_DMA_TRIGGER_BY_HARDWARE;
|
|
|
|
xfer_config.linklist_mad_type = SDRV_DMA_LINKLIST_NORMAL_MAD;
|
|
} else {
|
|
xfer_config.linklist_addr =
|
|
(paddr_t)&dma_linklists[type]
|
|
[(index + 1) %
|
|
dma_buf->periods]; /* pointer
|
|
to next linklist descriptor */
|
|
xfer_config.trig_mode =
|
|
SDRV_DMA_TRIGGER_BY_HARDWARE; /* non-first linklist
|
|
use
|
|
SDRV_DMA_TRIGGER_BY_INTERNAL_EVENT
|
|
*/
|
|
xfer_config.linklist_mad_type = SDRV_DMA_LINKLIST_NORMAL_MAD;
|
|
}
|
|
|
|
sdrv_dma_init_linklist_entry(&dma_linklists[type][index],
|
|
&xfer_config); /* init linklst */
|
|
|
|
if (index == 0) {
|
|
/* 6.init channel config */
|
|
sdrv_dma_init_channel(&i2s_stream->dma_channel, &xfer_config);
|
|
}
|
|
}
|
|
} else {
|
|
|
|
/* 4.modify config param */
|
|
xfer_config.channel_id = cfg->rx_channel_id; /* select channel */
|
|
xfer_config.xfer_type =
|
|
SDRV_DMA_DIR_DEV2MEM; /* mem2mem or mem2dev or dev2mem */
|
|
xfer_config.xfer_mode =
|
|
SDRV_DMA_TRANSFER_MODE_LINKLIST; /* single or continuous or
|
|
linklist*/
|
|
xfer_config.buffer_mode = SDRV_DMA_SINGLE_BUFFER;
|
|
xfer_config.src_inc =
|
|
SDRV_DMA_ADDR_NO_INC; /* source address increase or not */
|
|
xfer_config.dst_inc =
|
|
SDRV_DMA_ADDR_INC; /* destination address increase or not */
|
|
xfer_config.src_width = dma_data->addr_width; /* source address width */
|
|
xfer_config.dst_width =
|
|
dma_data->addr_width; /* destination address width */
|
|
xfer_config.src_burst_len =
|
|
dma_data->maxburst; /* source burst_length (transfer level) */
|
|
xfer_config.dst_burst_len =
|
|
dma_data->maxburst; /* source burst_length */
|
|
|
|
xfer_config.loop_mode =
|
|
SDRV_DMA_LOOP_MODE_1; /* mem2mem(MODE_0) mem2dev/dev2mem(MODE_1) */
|
|
xfer_config.src_port_sel =
|
|
SDRV_DMA_PORT_AHB32; /* periph -> SDRV_DMA_PORT_AHB32 */
|
|
xfer_config.dst_port_sel =
|
|
SDRV_DMA_PORT_AXI64; /* periph -> SDRV_DMA_PORT_AXI64 */
|
|
xfer_config.dst_cache =
|
|
0; /* mem2mem need to set 1, mem2dev/dev2mem set 0 */
|
|
xfer_config.interrupt_type =
|
|
SDRV_DMA_EVERY_MAD_DONE; /* set interrupt type */
|
|
xfer_config.trig_mode =
|
|
SDRV_DMA_TRIGGER_BY_HARDWARE; /* non-first linklist use
|
|
SDRV_DMA_TRIGGER_BY_INTERNAL_EVENT
|
|
*/
|
|
/* 5.init linklist descriptor */
|
|
for (index = 0; index < dma_buf->periods; index++) {
|
|
xfer_config.dst_addr =
|
|
(paddr_t)(dma_buf->addr +
|
|
dma_buf->period_bytes *
|
|
index); /* set destination address */
|
|
xfer_config.src_addr =
|
|
(paddr_t)dma_data->addr; /* set source address */
|
|
xfer_config.xfer_bytes =
|
|
dma_buf->period_bytes; /* transaction bytes */
|
|
|
|
if (index == 0) { /* first linklist */
|
|
xfer_config.linklist_addr =
|
|
(paddr_t)&dma_linklists[type]
|
|
[(index + 1) %
|
|
dma_buf->periods]; /* pointer
|
|
to next linklist descriptor */
|
|
xfer_config.trig_mode =
|
|
SDRV_DMA_TRIGGER_BY_HARDWARE; /* first linklist use
|
|
SDRV_DMA_TRIGGER_BY_SOFTWARE
|
|
*/
|
|
xfer_config.linklist_mad_type = SDRV_DMA_LINKLIST_NORMAL_MAD;
|
|
|
|
} else if (index == (dma_buf->periods - 1)) { /* last linklist */
|
|
xfer_config.linklist_addr =
|
|
(paddr_t)&dma_linklists[type]
|
|
[0]; /* pointer to next linklist
|
|
descriptor,0 is over */
|
|
xfer_config.trig_mode = SDRV_DMA_TRIGGER_BY_HARDWARE;
|
|
|
|
xfer_config.linklist_mad_type = SDRV_DMA_LINKLIST_NORMAL_MAD;
|
|
} else {
|
|
xfer_config.linklist_addr =
|
|
(paddr_t)&dma_linklists[type]
|
|
[(index + 1) %
|
|
dma_buf->periods]; /* pointer
|
|
to next linklist descriptor */
|
|
xfer_config.trig_mode =
|
|
SDRV_DMA_TRIGGER_BY_HARDWARE; /* non-first linklist
|
|
use
|
|
SDRV_DMA_TRIGGER_BY_INTERNAL_EVENT
|
|
*/
|
|
xfer_config.linklist_mad_type = SDRV_DMA_LINKLIST_NORMAL_MAD;
|
|
}
|
|
|
|
sdrv_dma_init_linklist_entry(&dma_linklists[type][index],
|
|
&xfer_config); /* init linklst */
|
|
|
|
if (index == 0) {
|
|
/* 6.init channel config */
|
|
sdrv_dma_init_channel(&i2s_stream->dma_channel, &xfer_config);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* 6.1 must flush linklist cache */
|
|
arch_clean_cache_range((uint32_t)&dma_linklists[type][0],
|
|
sizeof(dma_linklists));
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* @brief prepare dma
|
|
*
|
|
* @param i2s
|
|
* @param type
|
|
* @return sdrv_i2s_status_t
|
|
*/
|
|
static sdrv_i2s_status_t sdrv_i2s_prepare_dma(sdrv_i2s_t *i2s,
|
|
sdrv_i2s_stream_type_t type)
|
|
{
|
|
sdrv_i2s_stream_t *i2s_stream = &i2s->stream[type];
|
|
sdrv_i2s_config_t *cfg = i2s->cfg;
|
|
sdrv_i2s_params_t *params = &i2s_stream->params;
|
|
sdrv_i2s_dma_buf_t *dma_buf = &i2s_stream->dma_buf;
|
|
|
|
params->periods = (params->periods > SDRV_I2S_XFER_NUM_BUFFERS)
|
|
? SDRV_I2S_XFER_NUM_BUFFERS
|
|
: params->periods;
|
|
|
|
dma_buf->period_bytes = params->period_bytes;
|
|
dma_buf->periods = params->periods;
|
|
|
|
sdrv_i2s_dma_create(dma_buf, params);
|
|
/* 2.create dma instance */
|
|
sdrv_dma_create_instance(&i2s_stream->dma_instance, cfg->dma_base);
|
|
|
|
sdrv_i2s_set_dma_link_list(i2s, type);
|
|
|
|
if (SDRV_I2S_STREAM_PLAYBACK == type) {
|
|
/* 6.set interrupt callback */
|
|
i2s_stream->dma_channel.irq_callback =
|
|
sdrv_i2s_tx_transfer_dma_callback;
|
|
i2s_stream->dma_channel.irq_context = (void *)i2s;
|
|
} else {
|
|
/* 6.set interrupt callback */
|
|
i2s_stream->dma_channel.irq_callback =
|
|
sdrv_i2s_rx_transfer_dma_callback;
|
|
i2s_stream->dma_channel.irq_context = (void *)i2s;
|
|
}
|
|
|
|
return SDRV_I2S_STATUS_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
* @brief start dma
|
|
*
|
|
* This fuction is used to start dma
|
|
*
|
|
*
|
|
* @param i2s
|
|
* @param type
|
|
* @return sdrv_i2s_status_t
|
|
*/
|
|
static sdrv_i2s_status_t sdrv_i2s_start_dma(sdrv_i2s_t *i2s,
|
|
sdrv_i2s_stream_type_t type)
|
|
{
|
|
sdrv_i2s_stream_t *i2s_stream = &i2s->stream[type];
|
|
sdrv_i2s_dma_buf_t *dma_buf = &i2s_stream->dma_buf;
|
|
|
|
if (!dma_buf->addr) {
|
|
ssdk_printf(SACI_ERR, "%s: no addr,start dma failed :\r\n", __func__);
|
|
return SDRV_I2S_STATUS_FAIL;
|
|
}
|
|
|
|
/* 7.start channel transfer */
|
|
sdrv_dma_start_channel_xfer(&i2s_stream->dma_channel);
|
|
return SDRV_I2S_STATUS_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
* @brief stop dma
|
|
*
|
|
* This fuction is used to start dma
|
|
*
|
|
* @param i2s
|
|
* @param type
|
|
* @return sdrv_i2s_status_t
|
|
*/
|
|
static sdrv_i2s_status_t sdrv_i2s_stop_dma(sdrv_i2s_t *i2s,
|
|
sdrv_i2s_stream_type_t type)
|
|
{
|
|
sdrv_i2s_stream_t *i2s_stream = &i2s->stream[type];
|
|
sdrv_i2s_dma_buf_t *dma_buf = &i2s_stream->dma_buf;
|
|
|
|
/* 9.stop channel transfer */
|
|
sdrv_dma_stop_channel_xfer(&i2s_stream->dma_channel);
|
|
sdrv_dma_deinit_channel(&i2s_stream->dma_channel);
|
|
sdrv_i2s_dma_free(dma_buf);
|
|
|
|
return SDRV_I2S_STATUS_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
* @brief initializes i2s module according to cfg
|
|
*
|
|
* This fuction initializes i2s module
|
|
* function.
|
|
*
|
|
* @param [in] i2s sdrv i2s controller
|
|
* @param [in] cfg sdrv i2s controller configurations
|
|
* @return sdrv_i2s_status_t
|
|
*
|
|
*/
|
|
sdrv_i2s_status_t sdrv_i2s_init(sdrv_i2s_t *i2s, sdrv_i2s_config_t *cfg)
|
|
{
|
|
|
|
if (i2s == NULL || cfg == NULL) {
|
|
ssdk_printf(SACI_ERR, "%s:err: i2s or cfg is null :\r\n", __func__);
|
|
return SDRV_I2S_STATUS_FAIL;
|
|
}
|
|
|
|
i2s->cfg = cfg;
|
|
saci_i2s_config_mclk_dir(i2s);
|
|
if (i2s->cfg->irq > 0) {
|
|
irq_attach(i2s->cfg->irq, saci_i2s_irq_handler, i2s);
|
|
irq_enable(i2s->cfg->irq);
|
|
}
|
|
|
|
return SDRV_I2S_STATUS_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
* @brief get i2s default params
|
|
*
|
|
* This fuction get i2s default params
|
|
*
|
|
* @param [in] i2s sdrv i2s controller
|
|
* @param [out] params sdrv i2s params
|
|
* @param [in] type playback or capture type
|
|
* @return sdrv_i2s_status_t
|
|
*
|
|
*/
|
|
sdrv_i2s_status_t sdrv_i2s_get_default_params(sdrv_i2s_t *i2s,
|
|
sdrv_i2s_params_t *params,
|
|
sdrv_i2s_stream_type_t type)
|
|
{
|
|
|
|
if (type >= SDRV_I2S_STREAM_MAX) {
|
|
ssdk_printf(SACI_ERR, "%s:err: stream type(%d) is out of range :\r\n",
|
|
__func__, type);
|
|
|
|
return SDRV_I2S_STATUS_INVALID_PARAMS;
|
|
}
|
|
if (i2s == NULL || params == NULL) {
|
|
ssdk_printf(SACI_ERR, "%s:err: i2s or params is null :\r\n", __func__);
|
|
return SDRV_I2S_STATUS_FAIL;
|
|
}
|
|
|
|
static const sdrv_i2s_params_t default_params = {
|
|
.fmt = SDRV_I2S_DAIFMT_STANDARD_PHILLIPS | SDRV_I2S_DAIFMT_CODEC_SLAVE,
|
|
.channels = SDRV_I2S_CHANNEL_NUM_STEREO,
|
|
.sample_width = SDRV_I2S_SAMPLE_WIDTH_16BITS,
|
|
.sample_rate = SDRV_I2S_SR_48000,
|
|
.tx_slot_mask = 0x00,
|
|
.rx_slot_mask = 0x00,
|
|
.dma_buff = NULL,
|
|
.period_bytes = SDRV_I2S_DMA_PERIOD_BYTES,
|
|
.periods = SDRV_I2S_DMA_PERIOD_COUNT,
|
|
.usedma = true,
|
|
};
|
|
|
|
*params = default_params;
|
|
|
|
return SDRV_I2S_STATUS_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
* @brief set i2s params
|
|
*
|
|
* This fuction is used to set i2s params
|
|
*
|
|
* @param [in] i2s sdrv i2s controller
|
|
* @param [in] params sdrv i2s params
|
|
* @param [in] type playback or capture type
|
|
* @ return sdrv_i2s_status_t
|
|
*
|
|
*
|
|
*/
|
|
sdrv_i2s_status_t sdrv_i2s_set_params(sdrv_i2s_t *i2s,
|
|
sdrv_i2s_params_t *params,
|
|
sdrv_i2s_stream_type_t type)
|
|
{
|
|
sdrv_i2s_stream_t *i2s_stream;
|
|
sdrv_i2s_params_t *i2s_params;
|
|
|
|
if (type >= SDRV_I2S_STREAM_MAX) {
|
|
ssdk_printf(SACI_ERR, "%s:err: stream type(%d) is out of range :\r\n",
|
|
__func__, type);
|
|
|
|
return SDRV_I2S_STATUS_INVALID_PARAMS;
|
|
}
|
|
|
|
if (i2s == NULL || params == NULL) {
|
|
ssdk_printf(SACI_ERR, "%s:err: i2s or params is null :\r\n", __func__);
|
|
return SDRV_I2S_STATUS_INVALID_PARAMS;
|
|
}
|
|
if (params->usedma) {
|
|
if (!params->dma_buff) {
|
|
ssdk_printf(SACI_ERR, "%s: dma mode need set dmabuf \r\n",
|
|
__func__);
|
|
return SDRV_I2S_STATUS_INVALID_PARAMS;
|
|
}
|
|
if ((!params->period_bytes) || (!params->periods)) {
|
|
ssdk_printf(SACI_ERR,
|
|
"%s: err:period_bytes or periods is not config \r\n",
|
|
__func__);
|
|
return SDRV_I2S_STATUS_INVALID_PARAMS;
|
|
}
|
|
}
|
|
|
|
if (!saci_i2s_startup(i2s)) {
|
|
return SDRV_I2S_STATUS_FAIL;
|
|
}
|
|
|
|
i2s_stream = &i2s->stream[type];
|
|
i2s_params = &i2s_stream->params;
|
|
memcpy(i2s_params, params, sizeof(sdrv_i2s_params_t));
|
|
|
|
if (!saci_i2s_set_params(i2s, params, type)) {
|
|
ssdk_printf(SACI_ERR, "%s:err: i2s ivalid params :\r\n", __func__);
|
|
return SDRV_I2S_STATUS_INVALID_PARAMS;
|
|
}
|
|
|
|
if (params->usedma) {
|
|
sdrv_i2s_prepare_dma(i2s, type);
|
|
}
|
|
|
|
return SDRV_I2S_STATUS_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
* @brief create transfer and attach transfer callback
|
|
*
|
|
* This fuction is used to create transfer and attach transfer callback
|
|
*
|
|
* @param [in] i2s sdrv i2s controller
|
|
* @param [in] callback sdrv i2s transfer callback function.
|
|
* @param [in] context sdrv i2s transfer callback param.
|
|
* @param [in] type playback or capture type
|
|
* @return sdrv_i2s_status_t
|
|
*/
|
|
sdrv_i2s_status_t sdrv_i2s_create_transfer(sdrv_i2s_t *i2s,
|
|
sdrv_i2s_callback callback,
|
|
void *context,
|
|
sdrv_i2s_stream_type_t type)
|
|
{
|
|
sdrv_i2s_stream_t *i2s_stream;
|
|
int i = 0;
|
|
|
|
if (type >= SDRV_I2S_STREAM_MAX) {
|
|
ssdk_printf(SACI_ERR, "%s:err: stream type(%d) is out of range :\r\n",
|
|
__func__, type);
|
|
|
|
return SDRV_I2S_STATUS_INVALID_PARAMS;
|
|
}
|
|
|
|
if (!i2s) {
|
|
ssdk_printf(SACI_ERR, "%s:err: i2s is null :\r\n", __func__);
|
|
return SDRV_I2S_STATUS_FAIL;
|
|
}
|
|
|
|
i2s_stream = &i2s->stream[type];
|
|
i2s_stream->callback = callback;
|
|
i2s_stream->context = context;
|
|
i2s_stream->queue_appl = 0;
|
|
i2s_stream->queue_hw = 0;
|
|
|
|
for (i = 0; i < SDRV_I2S_XFER_NUM_BUFFERS; i++) {
|
|
i2s_stream->queue[i].data = NULL;
|
|
i2s_stream->queue[i].length = 0;
|
|
i2s_stream->queue[i].id = 0;
|
|
}
|
|
|
|
return SDRV_I2S_STATUS_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
* @brief submit i2s transfer in queue
|
|
*
|
|
* This fuction is used to submit transfer, which's max num is
|
|
* SDRV_I2S_XFER_NUM_BUFFERS
|
|
*
|
|
* @param [in] i2s sdrv i2s controller
|
|
* @param [in] transfer sdrv i2s transfer
|
|
* @param [in] type playback or capture type
|
|
* @return sdrv_i2s_status_t
|
|
*/
|
|
sdrv_i2s_status_t sdrv_i2s_submit_transfer(sdrv_i2s_t *i2s,
|
|
sdrv_i2s_transfer_t *transfer,
|
|
sdrv_i2s_stream_type_t type)
|
|
{
|
|
if (type >= SDRV_I2S_STREAM_MAX) {
|
|
ssdk_printf(SACI_ERR, "%s:err: stream type(%d) is out of range :\r\n",
|
|
__func__, type);
|
|
|
|
return SDRV_I2S_STATUS_INVALID_PARAMS;
|
|
}
|
|
|
|
if (i2s == NULL || transfer == NULL) {
|
|
ssdk_printf(SACI_ERR, "%s:err: i2s or transfer is null :\r\n",
|
|
__func__);
|
|
return SDRV_I2S_STATUS_FAIL;
|
|
}
|
|
|
|
return i2s_enqueue_transfer(i2s, transfer, type);
|
|
}
|
|
|
|
/**
|
|
* @brief start i2s transfer
|
|
*
|
|
* This fuction is used to start i2s transfer
|
|
*
|
|
* @param [in] i2s sdrv i2s controller
|
|
* @param [in] type playback or capture type
|
|
* @return sdrv_i2s_status_t
|
|
*/
|
|
sdrv_i2s_status_t sdrv_i2s_start(sdrv_i2s_t *i2s, sdrv_i2s_stream_type_t type)
|
|
{
|
|
sdrv_i2s_stream_t *i2s_stream;
|
|
sdrv_i2s_params_t *params;
|
|
sdrv_i2s_dma_buf_t *dma_buf;
|
|
int expected_bytes;
|
|
bool ret;
|
|
|
|
if (type >= SDRV_I2S_STREAM_MAX) {
|
|
ssdk_printf(SACI_ERR, "%s:err: stream type(%d) is out of range :\r\n",
|
|
__func__, type);
|
|
|
|
return SDRV_I2S_STATUS_INVALID_PARAMS;
|
|
}
|
|
|
|
if (!i2s) {
|
|
ssdk_printf(SACI_ERR, "%s:err: i2s null :\r\n", __func__);
|
|
return SDRV_I2S_STATUS_FAIL;
|
|
}
|
|
|
|
i2s_stream = &i2s->stream[type];
|
|
params = &i2s_stream->params;
|
|
dma_buf = &i2s_stream->dma_buf;
|
|
expected_bytes = dma_buf->period_bytes * SACI_EXPECTED_PERIOD_COUNT;
|
|
|
|
if (SDRV_I2S_STREAM_PLAYBACK == type) {
|
|
if (params->usedma) {
|
|
|
|
sdrv_i2s_handle_playback_dma_data(i2s, expected_bytes);
|
|
} else {
|
|
|
|
sdrv_i2s_handle_tx_data_irq(i2s);
|
|
}
|
|
}
|
|
|
|
if (params->usedma) {
|
|
|
|
if (sdrv_i2s_start_dma(i2s, type)) {
|
|
ssdk_printf(SACI_ERR, "func<%s>: start dma type(%d) err \r\n",
|
|
__func__, type);
|
|
return SDRV_I2S_STATUS_FAIL;
|
|
}
|
|
}
|
|
|
|
ret = saci_i2s_trigger(i2s, SDRV_I2S_TRIGGER_START, type);
|
|
if (!ret) {
|
|
ssdk_printf(SACI_ERR, "func<%s>: start type(%d) err \r\n", __func__,
|
|
type);
|
|
return SDRV_I2S_STATUS_FAIL;
|
|
}
|
|
|
|
return SDRV_I2S_STATUS_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
* @brief stop i2s transfer
|
|
*
|
|
* This fuction is used to stop i2s transfer
|
|
*
|
|
* @param [in] i2s sdrv i2s controller
|
|
* @param [in] type playback or capture type
|
|
* @return sdrv_i2s_status_t
|
|
*/
|
|
sdrv_i2s_status_t sdrv_i2s_stop(sdrv_i2s_t *i2s, sdrv_i2s_stream_type_t type)
|
|
{
|
|
sdrv_i2s_stream_t *i2s_stream;
|
|
sdrv_i2s_params_t *params;
|
|
bool ret;
|
|
|
|
if (type >= SDRV_I2S_STREAM_MAX) {
|
|
ssdk_printf(SACI_ERR, "%s:err: stream type(%d) is out of range :\r\n",
|
|
__func__, type);
|
|
|
|
return SDRV_I2S_STATUS_INVALID_PARAMS;
|
|
}
|
|
if (!i2s) {
|
|
ssdk_printf(SACI_ERR, "%s:err: i2s is null :\r\n", __func__);
|
|
return SDRV_I2S_STATUS_FAIL;
|
|
}
|
|
|
|
i2s_stream = &i2s->stream[type];
|
|
params = &i2s_stream->params;
|
|
|
|
if (params->usedma) {
|
|
sdrv_i2s_stop_dma(i2s, type);
|
|
}
|
|
|
|
ret = saci_i2s_trigger(i2s, SDRV_I2S_TRIGGER_STOP, type);
|
|
if (!ret) {
|
|
ssdk_printf(SACI_ERR, "func<%s>: stop type(%d) err \r\n", __func__,
|
|
type);
|
|
return SDRV_I2S_STATUS_FAIL;
|
|
}
|
|
|
|
return SDRV_I2S_STATUS_SUCCESS;
|
|
}
|