Files
1CAR/drivers/source/i2s/sdrv_i2s.c
2025-10-25 21:11:06 +08:00

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;
}