/** * @file sdrv_i2s.c * @brief E3 I2S controller driver. * * @copyright Copyright (c) 2021 Semidrive Semiconductor. * All rights reserved. * */ #include #include #include #include #include #include #include #include #include #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; }