/** * @file sdrv_pdm.c * * @brief SemiDrive pdm driver source file. * * @copyright Copyright (c) 2021 Semidrive Semiconductor. * All rights reserved. * */ #include #include #include #include #include #include #include #include #include #include "sdrv_pdm.h" #include "saci_pdm_regs.h" #define PDM_DEBUG_LEVEL SSDK_DEBUG #define PDM_REG_DEBUG_LEVEL 0 #define PDM_ERR SSDK_DEBUG /* pdm average value */ #define PDM_MID_VALUE_16 1 << (16 - 1) /* pdm fifo depth */ #define SACI_PDM_DEPTH 64 /* pdm dma max burst */ #define SACI_DMA_MAX_BURST 32 /* pdm dma fifo threshold */ #define SACI_FIFO_THRESHOLD 32 /* pdm fifo pack width 16bit or 32bit */ #define PDM_FIFO_PACK_WIDTH_32 7 #define PDM_FIFO_PACK_WIDTH_16 4 /* pdm start stop cmd */ #define SDRV_PDM_TRIGGER_START 0 #define SDRV_PDM_TRIGGER_STOP 1 /* pdm channel mode */ #define PDM_CHMODE_DISABLE 0x00 #define PDM_CHMODE_LEFT 0x01 #define PDM_CHMODE_RIGHT 0x02 #define PDM_CHMODE_STEREO 0x03 /* data pack mode in pdm fifo */ #define PDM_FIFO_LOOSE 0x00 #define PDM_FIFO_PACK 0x01 #define PDM_FILTER_DELAY 45 /* sdrv saci global reset */ #define SACI_GLOBAL_RESET 5 /* sdrv pdm use dma linklist to cicular buff */ static sdrv_dma_linklist_descriptor_t pdm_dma_linklists[SDRV_PDM_XFER_NUM_TRANSFERS] __attribute__((__aligned__(CONFIG_ARCH_CACHE_LINE))); /** * @brief mdelay * * @param ms */ static void pdm_delay(int ms) { udelay(ms * 1000); } #if PDM_REG_DEBUG_LEVEL #define pdm_readl(reg) \ readl(reg); \ ssdk_printf(PDM_DEBUG_LEVEL, "r(0x%x, r(0x%08x)\r\n", reg, readl(reg)); #define pdm_writel(val, reg) \ writel(val, reg); \ ssdk_printf(PDM_DEBUG_LEVEL, "w(0x%x, 0x%08x), r(0x%08x)\r\n", reg, val, \ readl(reg)); #else #define pdm_writel(val, reg) writel(val, reg) #define pdm_readl(reg) readl(reg) #endif /** * @brief sdrv pdm read reg bits * * @param reg_base * @param mask * @param offset * @return uint32_t */ static inline uint32_t pdm_rd_reg_bits(uint32_t reg_base, uint32_t mask, uint32_t offset) { uint32_t rd_data = pdm_readl(reg_base); return ((rd_data & mask) >> offset); } /** * @brief sdrv pdm write reg bits * * @param reg_base * @param mask * @param offset * @param val */ static inline void pdm_wr_reg_bits(uint32_t reg_base, uint32_t mask, uint32_t offset, uint32_t val) { uint32_t wr_data = pdm_readl(reg_base); wr_data = wr_data & (~mask); wr_data = wr_data | ((val << offset) & mask); pdm_writel(wr_data, reg_base); } /** * @brief get status of irq * * This function is used to get status of pdm irq. * * @param base_addr * @return uint32_t */ static uint32_t saci_pdm_get_status_irq(uint32_t base_addr) { return pdm_readl(base_addr + PDM_0008_IRQ_STAT); } /** * @brief get mask of irq * * This function is used to get mask of pdm irq. * * @param base_addr * @return uint32_t */ static uint32_t saci_pdm_get_mask_irq(uint32_t base_addr) { return pdm_readl(base_addr + PDM_0004_IRQ_MASK); } /** * @brief clear pre full status of pdm irq. * * This function is used to clear pre full status of pdm irq.. * * @param base_addr * @param id */ static void saci_pdm_clr_rx_fifo_pre_full_irq(uint32_t base_addr, int id) { pdm_wr_reg_bits(base_addr + PDM_0008_IRQ_STAT, PDM_IRQ_STAT_CHX_FIFO_PRE_FULL_MASK(id), PDM_IRQ_STAT_CHX_FIFO_PRE_FULL_POS(id), 1); } /** * @brief clear rx full status of pdm irq * * This function is used to clear rx full status of pdm irq * * @param base_addr * @param id */ static void saci_pdm_clr_rx_fifo_full_irq(uint32_t base_addr, int id) { pdm_wr_reg_bits(base_addr + PDM_0008_IRQ_STAT, PDM_IRQ_STAT_CHX_FIFO_FULL_MASK(id), PDM_IRQ_STAT_CHX_FIFO_FULL_POS(id), 1); } /** * @brief clear rx overrun status of pdm irq * * This function is used to clear rx overrun status of pdm irq * * @param base_addr * @param id */ static void saci_pdm_clr_rx_fifo_ovr_irq(uint32_t base_addr, int id) { pdm_wr_reg_bits(base_addr + PDM_0008_IRQ_STAT, PDM_IRQ_STAT_CHX_FIFO_OVR_MASK(id), PDM_IRQ_STAT_CHX_FIFO_OVR_POS(id), 1); } /** * @brief get remaining data size of pdm fifo * * This function is used to get remaining data size of pdm fifo * * @param base_addr * @return the remaining data size of rx fifo */ static uint32_t saci_pdm_get_rx_fifo_dpt(uint32_t base_addr, int id) { return pdm_rd_reg_bits(base_addr + PDM_FIFO_STA_X(id), PDM_FIFO_DPT_MASK, PDM_FIFO_DPT_POS); } /** * @brief read a sample from pdm fifo in irq mode * * This function is used to read a sample from pdm fifo in irq mode * * @param base_addr * @param id * @return uint32_t */ static uint32_t saci_pdm_get_rx_fifo_data(uint32_t base_addr, int id) { return readl(base_addr + PDM_CHX_FIFO_DATA(id)); } /** * @brief reset pdm irq status to default value * * @param pdm */ static void saci_pdm_reset_irq(sdrv_pdm_t *pdm) { sdrv_pdm_config_t *config = pdm->config; uint32_t base_addr = config->base; int id = config->id; pdm_wr_reg_bits(base_addr + PDM_0004_IRQ_MASK, PDM_CHX_WID_MISM_MASK(id), PDM_CHX_WID_MISM_POS(id), 1); pdm_wr_reg_bits(base_addr + PDM_0004_IRQ_MASK, PDM_CHX_FIFO_OVR_MASK(id), PDM_CHX_FIFO_OVR_POS(id), 1); pdm_wr_reg_bits(base_addr + PDM_0004_IRQ_MASK, PDM_CHX_FIFO_FULL_MASK(id), PDM_CHX_FIFO_FULL_POS(id), 1); pdm_wr_reg_bits(base_addr + PDM_0004_IRQ_MASK, PDM_CHX_FIFO_EMPTY_MASK(id), PDM_CHX_FIFO_EMPTY_POS(id), 1); pdm_wr_reg_bits(base_addr + PDM_0004_IRQ_MASK, PDM_CHX_FIFO_PRE_FULL_MASK(id), PDM_CHX_FIFO_PRE_FULL_POS(id), 1); } /** * @brief config pdm to work in irq mode * * This function is used to config pdm to work in irq mode * * @param pdm */ static void saci_pdm_irq_mode(sdrv_pdm_t *pdm) { sdrv_pdm_config_t *config = pdm->config; uint32_t base_addr = config->base; int id = config->id; saci_pdm_reset_irq(pdm); pdm_wr_reg_bits(base_addr + PDM_0004_IRQ_MASK, PDM_CHX_FIFO_PRE_FULL_MASK(id), PDM_CHX_FIFO_PRE_FULL_POS(id), 0); } /** * @brief config pdm clock according to sample_rate * * This function is used to config pdm clock according to sample_rate * clk Influence elements : order dec_rate *case 0 16K * PDMCLK 1024K * 16K filter_order 5 dec_rate 64 width 31 * 16K filter_order 4 dec_rate 64 width 25 * 16K filter_order 3 dec_rate 64 width 19 * PDMCLK 2048K * 16K filter_order 4 dec_rate 128 width 29 * 16K filter_order 3 dec_rate 128 width 22 * * case 8K * PDMCLK 1024K * 8K filter_order 4 dec_rate 128 width 29 * 8K filter_order 3 dec_rate 128 width 25 * PDMCLK 2048K * 8K filter_order 3 dec_rate 256 width 25 * 8K filter_order 2 dec_rate 256 width 17 * case 32K * PDMCLK 1024K * 32K filter_order 5 dec_rate 32 width 31 * 32K filter_order 4 dec_rate 32 width 21 * 32K filter_order 3 dec_rate 32 width 16 * PDMCLK 2048K * 32K filter_order 5 dec_rate 64 width 31 * 32K filter_order 4 dec_rate 64 width 25 * 32K filter_order 3 dec_rate 64 width 19 * case 48K * PDMCLK 3072K * 48K filter_order 5 dec_rate 64 width 31 * 48K filter_order 4 dec_rate 64 width 25 * 48K filter_order 3 dec_rate 64 width 19 * * @param dev * @param pcm_sample_rate */ static void saci_pdm_config_clk(sdrv_pdm_t *pdm, sdrv_pdm_sample_rate_t sample_rate) { sdrv_pdm_config_t *config = pdm->config; int id = config->id; uint32_t base_addr = config->base; uint32_t src_clk_freq = pdm->clk_rate; uint32_t dec_rat = 0; uint32_t filter_order = 0; uint32_t pdm_clk = 0; uint32_t pdm_data_with = 0; uint32_t div = 0; switch (sample_rate) { case SDRV_PDM_SR_8000: filter_order = PDM_FILTER_3ORDER; dec_rat = PDM_DEC_RATE_256; pdm_data_with = PDM_DATA_WITH_25BITS; break; case SDRV_PDM_SR_16000: filter_order = PDM_FILTER_3ORDER; dec_rat = PDM_DEC_RATE_128; pdm_data_with = PDM_DATA_WITH_22BITS; break; case SDRV_PDM_SR_32000: filter_order = PDM_FILTER_5ORDER; dec_rat = PDM_DEC_RATE_64; pdm_data_with = PDM_DATA_WITH_31BITS; break; case SDRV_PDM_SR_48000: filter_order = PDM_FILTER_5ORDER; dec_rat = PDM_DEC_RATE_64; pdm_data_with = PDM_DATA_WITH_31BITS; break; default: ssdk_printf( PDM_ERR, "func<%s>: unsupported sample_rate , default PDM_SR_16000 \r\n", __func__); filter_order = PDM_FILTER_4ORDER; dec_rat = PDM_DEC_RATE_128; pdm_data_with = PDM_DATA_WITH_29BITS; sample_rate = SDRV_PDM_SR_16000; } pdm_clk = sample_rate * (dec_rat); /* calc div */ div = DIV_ROUND_UP(src_clk_freq, pdm_clk); if (div >= 1) { div = div - 1; } pdm_wr_reg_bits(base_addr + PDM_FIFO_CTRL_X(id), PDM_DATA_WITH_MASK, PDM_DATA_WITH_POS, pdm_data_with); /* src clk sel */ pdm_wr_reg_bits(base_addr + PDM_CLK_X_CTRL(id), PDM_CLK_SEL_MASK, PDM_CLK_SEL_POS, CLK_DIVIDER_SOURCE_PDM_PER_CLK); /* config clk freq */ pdm_wr_reg_bits(base_addr + PDM_CLK_X_CTRL(id), PDM_CLK_POL_MASK, PDM_CLK_POL_POS, 0); pdm_wr_reg_bits(base_addr + PDM_CLK_X_CTRL(id), PDM_CLK_DIV_MASK, PDM_CLK_DIV_POS, div); /* dec_rat Affect pcm_width clock */ pdm_wr_reg_bits(base_addr + PDM_FILTER_CTRL_X(id), PDM_DEC_RATE_MASK, PDM_DEC_RATE_POS, dec_rat - 1); pdm_wr_reg_bits(base_addr + PDM_FILTER_CTRL_X(id), PDM_FLT_ORDER_MASK, PDM_FLT_ORDER_POS, filter_order); } /** * @brief start pdm capture * * This function is used to start pdm capture * * @param pdm */ static void saci_pdm_start_capture(sdrv_pdm_t *pdm) { sdrv_pdm_config_t *config = pdm->config; sdrv_pdm_stream_t *pdm_stream = &pdm->stream; sdrv_pdm_params_t *pdm_params = &pdm_stream->params; uint32_t base_addr = config->base; int id = config->id; uint32_t dma_mode = pdm_params->usedma; if (dma_mode) { pdm_wr_reg_bits(base_addr + PDM_CTRL_X(id), PDM_DMA_EN_MASK, PDM_DMA_EN_POS, 1); } else { pdm_wr_reg_bits(base_addr + PDM_CTRL_X(id), PDM_DMA_EN_MASK, PDM_DMA_EN_POS, 0); } pdm_wr_reg_bits(base_addr + PDM_CLK_X_CTRL(id), PDM_DIV_CLK_EN_MASK, PDM_DIV_CLK_EN_POS, 1); pdm_wr_reg_bits(base_addr + PDM_CLK_X_CTRL(id), PDM_CLK_O_EN_MASK, PDM_CLK_O_EN_POS, 1); pdm_delay(PDM_FILTER_DELAY); pdm_wr_reg_bits(base_addr + PDM_EN_X(id), PDM_EN_MASK, PDM_EN_POS, 1); udelay(1); pdm_wr_reg_bits(base_addr + PDM_EN_X(id), PDM_FIFO_EN_MASK, PDM_FIFO_EN_POS, 1); if (dma_mode) { saci_pdm_reset_irq(pdm); } else { saci_pdm_irq_mode(pdm); } } /** * @brief stop pdm capture * * This function is used to stop pdm capture * * @param pdm */ static void saci_pdm_stop_capture(sdrv_pdm_t *pdm) { sdrv_pdm_config_t *config = pdm->config; sdrv_pdm_stream_t *pdm_stream = &pdm->stream; sdrv_pdm_params_t *pdm_params = &pdm_stream->params; uint32_t base_addr = config->base; int id = config->id; uint32_t dma_mode = pdm_params->usedma; uint32_t tx_reset = 0, rx_reset = 0, pdm0_reset = 0, pdm1_reset = 0; uint32_t saci_rst; pdm_wr_reg_bits(base_addr + PDM_EN_X(id), PDM_EN_MASK, PDM_EN_POS, 0); if (dma_mode) { pdm_wr_reg_bits(base_addr + PDM_CTRL_X(id), PDM_DMA_EN_MASK, PDM_DMA_EN_POS, 0); } pdm_wr_reg_bits(base_addr + PDM_CLK_X_CTRL(id), PDM_DIV_CLK_EN_MASK, PDM_DIV_CLK_EN_POS, 0); pdm_wr_reg_bits(base_addr + PDM_CLK_X_CTRL(id), PDM_CLK_O_EN_MASK, PDM_CLK_O_EN_POS, 0); pdm_wr_reg_bits(base_addr + PDM_EN_X(id), PDM_FIFO_EN_MASK, PDM_FIFO_EN_POS, 0); saci_pdm_reset_irq(pdm); pdm_wr_reg_bits(base_addr + PDM_0000_SACI_CTRL, PDM_CHX_RST_MASK(id), PDM_CHX_RST_POS(id), 0); tx_reset = pdm_rd_reg_bits(base_addr + SACI_0000_SACI_CTRL, SACI_0000_TX_RST_MASK, SACI_0000_TX_RST_POS); rx_reset = pdm_rd_reg_bits(base_addr + SACI_0000_SACI_CTRL, SACI_0000_RX_RST_MASK, SACI_0000_RX_RST_POS); pdm0_reset = pdm_rd_reg_bits(base_addr + SACI_0000_SACI_CTRL, SACI_0000_PDM_CH0_RST_MASK, SACI_0000_PDM_CH0_RST_POS); pdm1_reset = pdm_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) { pdm_wr_reg_bits(base_addr + SACI_0000_SACI_CTRL, SACI_0000_SACI_RST_MASK, SACI_0000_SACI_RST_POS, 0); udelay(SACI_GLOBAL_RESET); } } /** * @brief swap channel to pdm register value * * @param channel * @return uint32_t */ static uint32_t to_pdm_chan_mode(sdrv_pdm_channel_t channel) { uint32_t chan_mode = PDM_CHMODE_DISABLE; switch (channel) { case SDRV_PDM_CHANNEL_LEFT: chan_mode = PDM_CHMODE_LEFT; break; case SDRV_PDM_CHANNEL_RIGHT: chan_mode = PDM_CHMODE_RIGHT; break; case SDRV_PDM_CHANNEL_STEREO: chan_mode = PDM_CHMODE_STEREO; break; default: ssdk_printf(PDM_ERR, "func<%s>: unsupported channel mode \r\n", __func__); } return chan_mode; } /** * @brief swap gain to pdm register value * * @param p_gain * @return uint32_t */ static uint32_t to_pdm_data_gain(sdrv_pdm_gain_t p_gain) { uint32_t gain = PDM_GAIN_VALUE_0DB; switch (p_gain) { case SDRV_PDM_GAIN_N6DB: gain = PDM_GAIN_VALUE_N6DB; break; case SDRV_PDM_GAIN_N12DB: gain = PDM_GAIN_VALUE_N12DB; break; case SDRV_PDM_GAIN_N18DB: gain = PDM_GAIN_VALUE_N18DB; break; case SDRV_PDM_GAIN_N24DB: gain = PDM_GAIN_VALUE_N24DB; break; case SDRV_PDM_GAIN_P0DB: gain = PDM_GAIN_VALUE_0DB; break; case SDRV_PDM_GAIN_P6DB: gain = PDM_GAIN_VALUE_P6DB; break; case SDRV_PDM_GAIN_P12DB: gain = PDM_GAIN_VALUE_P12DB; break; case SDRV_PDM_GAIN_P18DB: gain = PDM_GAIN_VALUE_P18DB; break; case SDRV_PDM_GAIN_P24DB: gain = PDM_GAIN_VALUE_P24DB; break; default: ssdk_printf(PDM_ERR, "func<%s>: unsupported gain , default 0db \r\n", __func__); } return gain; } /** * @brief swap sample_width to pdm register value * * * @param sample_width * @return uint32_t */ static uint32_t to_pdm_pack_width(sdrv_pdm_sample_width_t sample_width) { uint32_t pack_width = 0x00; switch (sample_width) { case SDRV_PDM_SAMPLE_WIDTH_16BITS: pack_width = PDM_FIFO_PACK_WIDTH_16; break; default: { ssdk_printf(PDM_ERR, "func<%s>: unsupported width:%d ,only support 16bit \r\n", __func__, sample_width); } } return pack_width; } /** * @brief prepare init sdrv pdm * * This function is used to prepare init sdrv pdm. * * @param pdm * @return true * @return false */ static bool saci_pdm_startup(sdrv_pdm_t *pdm) { sdrv_pdm_config_t *config = pdm->config; sdrv_pdm_stream_t *pdm_stream = &pdm->stream; sdrv_pdm_dma_data_t *dma_data = &pdm_stream->dma_data; uint32_t base_addr = config->base; int id = config->id; sdrv_ckgen_node_t *clk = config->clk; uint32_t tx_reset = 0, rx_reset = 0, pdm0_reset = 0, pdm1_reset = 0; uint32_t saci_rst; tx_reset = pdm_rd_reg_bits(base_addr + SACI_0000_SACI_CTRL, SACI_0000_TX_RST_MASK, SACI_0000_TX_RST_POS); rx_reset = pdm_rd_reg_bits(base_addr + SACI_0000_SACI_CTRL, SACI_0000_RX_RST_MASK, SACI_0000_RX_RST_POS); pdm0_reset = pdm_rd_reg_bits(base_addr + SACI_0000_SACI_CTRL, SACI_0000_PDM_CH0_RST_MASK, SACI_0000_PDM_CH0_RST_POS); pdm1_reset = pdm_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) { pdm_wr_reg_bits(base_addr + SACI_0000_SACI_CTRL, SACI_0000_SACI_RST_MASK, SACI_0000_SACI_RST_POS, 0); udelay(SACI_GLOBAL_RESET); pdm_wr_reg_bits(base_addr + SACI_0000_SACI_CTRL, SACI_0000_SACI_RST_MASK, SACI_0000_SACI_RST_POS, 1); } pdm_wr_reg_bits(base_addr + PDM_0000_SACI_CTRL, PDM_CHX_RST_MASK(id), PDM_CHX_RST_POS(id), 0); udelay(1); pdm_wr_reg_bits(base_addr + PDM_0000_SACI_CTRL, PDM_CHX_RST_MASK(id), PDM_CHX_RST_POS(id), 1); pdm_wr_reg_bits(base_addr + PDM_0000_SACI_CTRL, PDM_CHX_FIFO_FLUSH_MASK(id), PDM_CHX_FIFO_FLUSH_POS(id), 1); pdm_stream->pack_mode = true; pdm_stream->data_lsb = true; pdm_stream->threshold = SACI_FIFO_THRESHOLD; dma_data->addr = base_addr + PDM_CHX_FIFO_DATA(id); dma_data->fifo_size = SACI_PDM_DEPTH; dma_data->max_burst = SACI_DMA_MAX_BURST; dma_data->addr_width = SDRV_DMA_BUSWIDTH_4_BYTES; pdm_wr_reg_bits(base_addr + PDM_FIFO_CTRL_X(id), PDM_THRD_MASK, PDM_THRD_POS, pdm_stream->threshold); if (clk) { pdm->clk_rate = sdrv_ckgen_get_rate(clk); sdrv_ckgen_set_rate(clk, pdm->clk_rate); } else { ssdk_printf(PDM_ERR, "err pdm clk is null \r\n"); return false; } return true; } /** * @brief config pdm params * * This function is used to config pdm params ,such as data format ,channels * ,gain * * @param pdm * @param params * @return true * @return false */ static bool saci_pdm_set_params(sdrv_pdm_t *pdm, sdrv_pdm_params_t *params) { sdrv_pdm_config_t *config = pdm->config; sdrv_pdm_stream_t *pdm_stream = &pdm->stream; sdrv_pdm_params_t *pdm_params = &pdm_stream->params; uint32_t base_addr = config->base; int id = config->id; sdrv_pdm_sample_rate_t sample_rate = params->sample_rate; uint32_t chan_mode = to_pdm_chan_mode(params->channels); uint32_t gain = to_pdm_data_gain(params->gain); uint32_t pack_with = to_pdm_pack_width(params->sample_width); bool pack_mode = pdm_stream->pack_mode; bool data_lsb = pdm_stream->data_lsb; if (params->sample_width != SDRV_PDM_SAMPLE_WIDTH_16BITS) { ssdk_printf(PDM_ERR, "func<%s>: unsupported width:%d ,only support 16bit \r\n", __func__, params->sample_width); return false; } if (pack_mode) { pdm_wr_reg_bits(base_addr + PDM_FIFO_CTRL_X(id), PDM_DATA_PACK_MODE_MASK, PDM_DATA_PACK_MODE_POS, PDM_FIFO_PACK); } else { pdm_wr_reg_bits(base_addr + PDM_FIFO_CTRL_X(id), PDM_DATA_PACK_MODE_MASK, PDM_DATA_PACK_MODE_POS, PDM_FIFO_LOOSE); } pdm_wr_reg_bits(base_addr + PDM_CTRL_X(id), PDM_MODE_MASK, PDM_MODE_POS, chan_mode); pdm_wr_reg_bits(base_addr + PDM_FIFO_CTRL_X(id), PDM_FIFO_PACK_WIDTH_MASK, PDM_FIFO_PACK_WIDTH_POS, pack_with); if (SDRV_PDM_GAIN_P0DB != params->gain) { pdm_wr_reg_bits(base_addr + PDM_CTRL_X(id), PDM_GAIN_EN_MASK, PDM_GAIN_EN_POS, 1); pdm_wr_reg_bits(base_addr + PDM_CTRL_X(id), PDM_GAIN_VALUE_MASK, PDM_GAIN_VALUE_POS, gain); } else { pdm_wr_reg_bits(base_addr + PDM_CTRL_X(id), PDM_GAIN_EN_MASK, PDM_GAIN_EN_POS, 0); } pdm_wr_reg_bits(base_addr + PDM_FIFO_CTRL_X(id), PDM_FIFO16BIT_VLD_MASK, PDM_FIFO16BIT_VLD_POS, 1); pdm_wr_reg_bits(base_addr + PDM_MID_VALUE, PDM_MID_VALUE_MASK, PDM_MID_VALUE_POS, PDM_MID_VALUE_16); pdm_wr_reg_bits(base_addr + PDM_CTRL_X(id), PDM_MINUS_MID_EN_MASK, PDM_MINUS_MID_EN_POS, 1); if (data_lsb) { pdm_wr_reg_bits(base_addr + PDM_FIFO_CTRL_X(id), PDM_DATA_ALIGN_MASK, PDM_DATA_ALIGN_POS, 0); } else { pdm_wr_reg_bits(base_addr + PDM_FIFO_CTRL_X(id), PDM_DATA_ALIGN_MASK, PDM_DATA_ALIGN_POS, 1); } memcpy(pdm_params, params, sizeof(sdrv_pdm_params_t)); saci_pdm_config_clk(pdm, sample_rate); return true; } /** * @brief start or stop sdrv pdm according to cmd * * This function is used to start or stop pdm * * @param pdm * @param pdm_cmd * @return true * @return false */ static bool saci_pdm_trigger(sdrv_pdm_t *pdm, uint8_t pdm_cmd) { sdrv_pdm_stream_t *pdm_stream = &pdm->stream; switch (pdm_cmd) { case SDRV_PDM_TRIGGER_START: pdm_stream->active = true; saci_pdm_start_capture(pdm); break; case SDRV_PDM_TRIGGER_STOP: saci_pdm_stop_capture(pdm); pdm_stream->active = false; break; default: ssdk_printf(PDM_ERR, "func<%s>: pdm pdm_cmd not support(%d)\r\n", __func__, pdm_cmd); return false; } return true; } /** * @brief calculate the number of valid bytes according to sample_width * * @param params * @return uint8_t */ static uint8_t params_to_valid_bytes(sdrv_pdm_params_t *params) { uint8_t valid_bytes = 0; switch (params->sample_width) { case SDRV_PDM_SAMPLE_WIDTH_8BITS: valid_bytes = 1; break; case SDRV_PDM_SAMPLE_WIDTH_16BITS: valid_bytes = 2; break; case SDRV_PDM_SAMPLE_WIDTH_24BITS: valid_bytes = 3; break; case SDRV_PDM_SAMPLE_WIDTH_32BITS: valid_bytes = 4; break; default: valid_bytes = 2; } return valid_bytes; } /** * @brief handle rx data in irq mode * * This function is used to handle rx transfer in irq mode * * * @param pdm */ void sdrv_pdm_handle_rx_data_irq(sdrv_pdm_t *pdm) { sdrv_pdm_config_t *config = pdm->config; sdrv_pdm_stream_t *pdm_stream = &pdm->stream; sdrv_pdm_params_t *pdm_params = &pdm_stream->params; uint32_t base_addr = config->base; int id = config->id; uint8_t queue_hw = pdm_stream->queue_hw; sdrv_pdm_transfer_t *transfer; uint32_t sample = 0; uint8_t fifo_bytes = 4; uint8_t valid_bytes = params_to_valid_bytes(pdm_params); uint32_t num = saci_pdm_get_rx_fifo_dpt(base_addr, id); int read_bytes = 0, temp = 0, length; int expected_bytes = num; if (!pdm_stream->active) { return; } if (pdm_stream->pack_mode) { if (valid_bytes <= 2) { valid_bytes = fifo_bytes; } } /* dequeue data */ transfer = &pdm_stream->queue[queue_hw]; if ((!transfer->length) || (!transfer->data)) { if (pdm_stream->callback) { (pdm_stream->callback)(transfer, pdm_stream->context, SDRV_PDM_STATUS_OVERRUN); } return; } while (read_bytes < expected_bytes) { temp = transfer->length; while (num > 0 && transfer->length > 0) { if (transfer->length >= valid_bytes) { sample = saci_pdm_get_rx_fifo_data(base_addr, id); 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 */ pdm_stream->queue_hw = (pdm_stream->queue_hw + 1U) % SDRV_PDM_XFER_NUM_TRANSFERS; if (pdm_stream->callback) { (pdm_stream->callback)(transfer, pdm_stream->context, SDRV_PDM_STATUS_SUCCESS); } } } if (read_bytes < expected_bytes) { transfer = &pdm_stream->queue[pdm_stream->queue_hw]; if ((!transfer->length) || (!transfer->data)) { if (pdm_stream->callback) { (pdm_stream->callback)(transfer, pdm_stream->context, SDRV_PDM_STATUS_OVERRUN); } return; } } } } /** * @brief handle sdrv pdm irq * * This function is used to handle sdrv pdm irq * * @param irq * @param arg * @return int */ static int sdrv_pdm_irq_handler(uint32_t irq, void *arg) { sdrv_pdm_t *pdm = (sdrv_pdm_t *)arg; sdrv_pdm_config_t *config = pdm->config; int id = config->id; uint32_t base_addr = config->base; uint32_t irq_stat = saci_pdm_get_status_irq(base_addr); uint32_t irq_mask = saci_pdm_get_mask_irq(base_addr); uint32_t mask_value, irq_value; mask_value = !((irq_mask & PDM_CHX_FIFO_PRE_FULL_MASK(id)) >> PDM_CHX_FIFO_PRE_FULL_POS(id)); irq_value = (irq_stat & PDM_IRQ_STAT_CHX_FIFO_PRE_FULL_MASK(id)) >> PDM_IRQ_STAT_CHX_FIFO_PRE_FULL_POS(id); if (mask_value & irq_value) { sdrv_pdm_handle_rx_data_irq(pdm); saci_pdm_clr_rx_fifo_pre_full_irq(base_addr, id); } mask_value = !((irq_mask & PDM_CHX_FIFO_FULL_MASK(id)) >> PDM_CHX_FIFO_FULL_POS(id)); irq_value = (irq_stat & PDM_IRQ_STAT_CHX_FIFO_FULL_MASK(id)) >> PDM_IRQ_STAT_CHX_FIFO_FULL_POS(id); if (mask_value & irq_value) { saci_pdm_clr_rx_fifo_full_irq(base_addr, id); } mask_value = !((irq_mask & PDM_CHX_FIFO_OVR_MASK(id)) >> PDM_CHX_FIFO_OVR_POS(id)); irq_value = (irq_stat & PDM_IRQ_STAT_CHX_FIFO_OVR_MASK(id)) >> PDM_IRQ_STAT_CHX_FIFO_OVR_POS(id); if (mask_value & irq_value) { saci_pdm_clr_rx_fifo_ovr_irq(base_addr, id); } return 0; } /** * @brief Calculate how much data there is in the dma buff to read for pdm * capture * * @param dma_buf * @return int32_t */ static int32_t sdrv_pdm_capture_avail(sdrv_pdm_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_pdm_capture_time_elapsed(sdrv_pdm_dma_buf_t *dma_buf, sdrv_pdm_params_t *params) { uint32_t avail = sdrv_pdm_capture_avail(dma_buf); uint32_t elapsed_span = 0; uint32_t offset = 0; uint32_t channels = 1; uint8_t valid_bytes = params_to_valid_bytes(params); uint32_t old_appl_ptr = 0; channels = (params->channels == SDRV_PDM_CHANNEL_STEREO) ? 2 : 1; 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; } /* recalculate the boundary within 32bit */ static uint32_t sdrv_pdm_recalculate_boundary(sdrv_pdm_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 * * This function is used to create dma buff, which addr is from * params->dma_buff,so user must set it in dma mode * * @param dma_buf * @return true * @return false */ static bool sdrv_pdm_dma_create(sdrv_pdm_dma_buf_t *dma_buf, sdrv_pdm_params_t *params) { uint32_t length = dma_buf->period_bytes * dma_buf->periods; if (!params->dma_buff) { ssdk_printf(PDM_ERR, "%s: error: params->dma_buff is null \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_pdm_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_pdm_dma_free(sdrv_pdm_dma_buf_t *dma_buf) { dma_buf->addr = NULL; } /** * @brief read pdm capture data from dma buf * * This function is used to read pdm capture data from dma buf * * @param dma_buf * @param buffer * @param len * @return int */ static int sdrv_pdm_dma_read(sdrv_pdm_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(SSDK_ALERT, "%s:dma_buf is null ,cannot read \r\n", __func__); return -1; } avail = sdrv_pdm_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_pdm_status_t sdrv_pdm_handle_capture_dma_data(sdrv_pdm_t *pdm, int expected_bytes) { sdrv_pdm_stream_t *stream = &pdm->stream; sdrv_pdm_dma_buf_t *dma_buf = &stream->dma_buf; uint8_t queue_hw = stream->queue_hw; sdrv_pdm_params_t *params = &stream->params; sdrv_pdm_transfer_t *transfer; int read_bytes = 0, temp = 0; /* dequeue data */ transfer = &stream->queue[queue_hw]; if ((!transfer->length) || (!transfer->data)) { if (stream->callback) { (stream->callback)(transfer, stream->context, SDRV_PDM_STATUS_OVERRUN); } return SDRV_PDM_STATUS_OVERRUN; } sdrv_pdm_capture_time_elapsed(dma_buf, params); while (read_bytes < expected_bytes) { temp = sdrv_pdm_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 */ stream->queue_hw = (stream->queue_hw + 1U) % SDRV_PDM_XFER_NUM_TRANSFERS; if (stream->callback != NULL) { (stream->callback)(transfer, stream->context, SDRV_PDM_STATUS_SUCCESS); } } } else { return SDRV_PDM_STATUS_SUCCESS; } if (read_bytes < expected_bytes) { transfer = &stream->queue[stream->queue_hw]; if ((!transfer->length) || (!transfer->data)) { if (stream->callback) { (stream->callback)(transfer, stream->context, SDRV_PDM_STATUS_OVERRUN); } return SDRV_PDM_STATUS_OVERRUN; } } } return SDRV_PDM_STATUS_SUCCESS; } /** * @brief pdm dma complete callback * * This function is used to handle pdm transfer in dma mode * * @param status * @param param * @param context */ static void sdrv_pdm_rx_transfer_dma_callback(uint32_t status, uint32_t param, void *context) { sdrv_pdm_t *pdm = (sdrv_pdm_t *)context; sdrv_pdm_stream_t *pdm_stream = &pdm->stream; sdrv_pdm_dma_buf_t *dma_buf = &pdm_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 (!pdm_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_pdm_handle_capture_dma_data(pdm, expected_bytes); } } /** * @brief config dma linklist for pdm dma transfer * * @param pdm * @return true * @return false */ static bool sdrv_pdm_set_dma_link_list(sdrv_pdm_t *pdm) { sdrv_pdm_config_t *config = pdm->config; sdrv_pdm_stream_t *pdm_stream = &pdm->stream; sdrv_pdm_dma_buf_t *dma_buf = &pdm_stream->dma_buf; sdrv_pdm_dma_data_t *dma_data = &pdm_stream->dma_data; sdrv_dma_channel_config_t xfer_config; int index = 0; /* 3.get default config */ sdrv_dma_init_channel_config(&xfer_config, &pdm_stream->dma_instance); /* 4.modify config param */ xfer_config.channel_id = config->dma_channel; /* 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->max_burst; /* source burst_length (transfer level) */ xfer_config.dst_burst_len = dma_data->max_burst; /* 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_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)&pdm_dma_linklists[(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_HARDWARE */ xfer_config.linklist_mad_type = SDRV_DMA_LINKLIST_FIRST_MAD; } else if (index == (dma_buf->periods - 1)) { /* last linklist */ xfer_config.linklist_addr = (paddr_t)&pdm_dma_linklists[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)&pdm_dma_linklists[(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_HARDWARE */ xfer_config.linklist_mad_type = SDRV_DMA_LINKLIST_NORMAL_MAD; } sdrv_dma_init_linklist_entry(&pdm_dma_linklists[index], &xfer_config); /* init linklst */ if (index == 0) { /* 6.init channel config */ sdrv_dma_init_channel(&pdm_stream->dma_channel, &xfer_config); } } /* 6.1 must flush linklist cache */ arch_clean_cache_range((uint32_t)&pdm_dma_linklists[0], sizeof(pdm_dma_linklists)); return true; } /** * @brief prepare pdm dma * * This function is used to prepare dma transfer, create dma buff,create dma * instance ;config dma linklist, attack callback and so on * * @param pdm * @return sdrv_pdm_status_t */ static sdrv_pdm_status_t sdrv_pdm_prepare_dma(sdrv_pdm_t *pdm) { sdrv_pdm_config_t *config = pdm->config; sdrv_pdm_stream_t *pdm_stream = &pdm->stream; sdrv_pdm_params_t *params = &pdm_stream->params; sdrv_pdm_dma_buf_t *dma_buf = &pdm_stream->dma_buf; params->periods = (params->periods > SDRV_PDM_XFER_NUM_TRANSFERS) ? SDRV_PDM_XFER_NUM_TRANSFERS : params->periods; dma_buf->period_bytes = params->period_bytes; dma_buf->periods = params->periods; pdm_stream->dma_abort = false; if (!params->dma_buff) { ssdk_printf(SSDK_CRIT, "%s: dma mode need set dmabuf,assert !\r\n", __func__); return SDRV_PDM_STATUS_FAIL; } if (!sdrv_pdm_dma_create(dma_buf, params)) { ssdk_printf(PDM_ERR, "%s: failed create dma buf \r\n", __func__); return SDRV_PDM_STATUS_FAIL; } /* 2.create dma instance */ sdrv_dma_create_instance(&pdm_stream->dma_instance, config->dma_base); sdrv_pdm_set_dma_link_list(pdm); /* 6.set interrupt callback */ pdm_stream->dma_channel.irq_callback = sdrv_pdm_rx_transfer_dma_callback; pdm_stream->dma_channel.irq_context = (void *)pdm; return SDRV_PDM_STATUS_SUCCESS; } /** * @brief start pdm in dma mode * * This fuction is used to start dma transfer * * @param pdm * @return sdrv_pdm_status_t * */ static sdrv_pdm_status_t sdrv_pdm_start_dma(sdrv_pdm_t *pdm) { sdrv_pdm_stream_t *pdm_stream = &pdm->stream; uint8_t queue_hw = pdm_stream->queue_hw; sdrv_pdm_transfer_t *transfer; /* dequeue data */ transfer = &pdm_stream->queue[queue_hw]; if ((!transfer->length) || (!transfer->data)) { return SDRV_PDM_STATUS_UNDERRUN; } /* 7.start channel transfer */ sdrv_dma_start_channel_xfer(&pdm_stream->dma_channel); return SDRV_PDM_STATUS_SUCCESS; } /** * @brief stop pdm in dma mode * * This function is used to stop dma transfer * * @param pdm * @return sdrv_pdm_status_t * */ static sdrv_pdm_status_t sdrv_pdm_stop_dma(sdrv_pdm_t *pdm) { sdrv_pdm_stream_t *pdm_stream = &pdm->stream; sdrv_pdm_dma_buf_t *dma_buf = &pdm_stream->dma_buf; /* 9.stop channel transfer */ sdrv_dma_stop_channel_xfer(&pdm_stream->dma_channel); sdrv_dma_deinit_channel(&pdm_stream->dma_channel); sdrv_pdm_dma_free(dma_buf); return SDRV_PDM_STATUS_SUCCESS; } /** * @brief enqueue a transfer to queue * * * This function is used to submit transfer, which's max num is * SDRV_PDM_XFER_NUM_TRANSFERS * * @param pdm * @param transfer * @param type * @return sdrv_pdm_status_t */ static sdrv_pdm_status_t sdrv_pdm_enqueue_transfer(sdrv_pdm_t *pdm, sdrv_pdm_transfer_t *transfer) { sdrv_pdm_stream_t *pdm_stream = &pdm->stream; uint8_t queue_appl = pdm_stream->queue_appl; if (!transfer->data) { ssdk_printf(PDM_ERR, "%s: pdm transfer->data is null\r\n", __func__); return SDRV_PDM_STATUS_INVALID_PARAMS; } if (transfer->length == 0U) { /* No data to send or receive */ ssdk_printf(PDM_ERR, "%s:pdm transfer->length is zero\r\n", __func__); return SDRV_PDM_STATUS_INVALID_PARAMS; } if (pdm_stream->queue[queue_appl].length != 0UL) { /* Previously prepared buffers not processed yet, reject request */ ssdk_printf(PDM_ERR, "%s: pdm transfer->length Previously prepared buffers not " "processed yet\r\n", __func__); return SDRV_PDM_STATUS_OVERRUN; } /* Enqueue data */ pdm_stream->queue[queue_appl].data = transfer->data; pdm_stream->queue[queue_appl].length = transfer->length; pdm_stream->queue[queue_appl].id = transfer->id; pdm_stream->queue_appl = (queue_appl + 1U) % SDRV_PDM_XFER_NUM_TRANSFERS; return SDRV_PDM_STATUS_SUCCESS; } /** * @brief sdrv_pdm_init * * This function is used to initializes pdm module * function. * * @param [in] pdm sdrv pdm controller * @param [in] cfg sdrv pdm controller configurations * @return sdrv_pdm_status_t * */ sdrv_pdm_status_t sdrv_pdm_init(sdrv_pdm_t *pdm, sdrv_pdm_config_t *cfg) { if (pdm == NULL || cfg == NULL) { ssdk_printf(PDM_ERR, "%s:err: pdm or cfg is null\r\n", __func__); return SDRV_PDM_STATUS_FAIL; } pdm->config = cfg; if (cfg->irq > 0) { irq_attach(cfg->irq, sdrv_pdm_irq_handler, pdm); irq_enable(cfg->irq); } return SDRV_PDM_STATUS_SUCCESS; } /** * @brief sdrv_pdm_get_default_params * * This function is used to get pdm default params * * @param [in] pdm sdrv pdm controller * @param [out] params sdrv pdm params * @return sdrv_pdm_status_t * */ sdrv_pdm_status_t sdrv_pdm_get_default_params(sdrv_pdm_t *pdm, sdrv_pdm_params_t *params) { sdrv_pdm_stream_t *pdm_stream = &pdm->stream; sdrv_pdm_params_t *pdm_params = &pdm_stream->params; if (pdm == NULL || params == NULL) { ssdk_printf(PDM_ERR, "%s:err: pdm or params is null\r\n", __func__); return SDRV_PDM_STATUS_FAIL; } const sdrv_pdm_params_t default_params = { .channels = SDRV_PDM_CHANNEL_LEFT, .sample_width = SDRV_PDM_SAMPLE_WIDTH_16BITS, .sample_rate = SDRV_PDM_SR_16000, .gain = SDRV_PDM_GAIN_P0DB, .dma_buff = NULL, .period_bytes = SDRV_PDM_DMA_PERIOD_SIZE, .periods = SDRV_PDM_DMA_PERIOD_COUNT, .usedma = true, }; *params = default_params; memcpy(pdm_params, &default_params, sizeof(sdrv_pdm_params_t)); return SDRV_PDM_STATUS_SUCCESS; } /** * @brief sdrv_pdm_set_params * * This function is used to set pdm params * * @param [in] pdm sdrv pdm controller * @param [in] params sdrv pdm params * @ return sdrv_pdm_status_t * * */ sdrv_pdm_status_t sdrv_pdm_set_params(sdrv_pdm_t *pdm, sdrv_pdm_params_t *params) { if (pdm == NULL || params == NULL) { ssdk_printf(PDM_ERR, "%s:err: pdm or params is null\r\n", __func__); return SDRV_PDM_STATUS_INVALID_PARAMS; } if (params->usedma) { if (!params->dma_buff) { ssdk_printf(PDM_ERR, "%s: dma mode need set dmabuf \r\n", __func__); return SDRV_PDM_STATUS_INVALID_PARAMS; } if ((!params->period_bytes) || (!params->periods)) { ssdk_printf( PDM_ERR, "%s: err:dma mode period_bytes or periods is not config \r\n", __func__); return SDRV_PDM_STATUS_INVALID_PARAMS; } } if (!saci_pdm_startup(pdm)) { return SDRV_PDM_STATUS_FAIL; } if (!saci_pdm_set_params(pdm, params)) { return SDRV_PDM_STATUS_INVALID_PARAMS; } if (params->usedma) { sdrv_pdm_prepare_dma(pdm); } return SDRV_PDM_STATUS_SUCCESS; } /** * @brief sdrv_pdm_create_transfer * * This function is used to create transfer and attach transfer callback * * @param [in] pdm sdrv pdm controller * @param [in] callback sdrv pdm transfer callback function. * @param [in] context sdrv pdm transfer callback param. * @return sdrv_pdm_status_t */ sdrv_pdm_status_t sdrv_pdm_create_transfer(sdrv_pdm_t *pdm, sdrv_pdm_callback callback, void *context) { sdrv_pdm_stream_t *pdm_stream; int i = 0; if (!pdm) { ssdk_printf(PDM_ERR, "%s:err: pdm is null\r\n", __func__); return SDRV_PDM_STATUS_FAIL; } pdm_stream = &pdm->stream; pdm_stream->callback = callback; pdm_stream->context = context; pdm_stream->queue_appl = 0; pdm_stream->queue_hw = 0; for (i = 0; i < SDRV_PDM_XFER_NUM_TRANSFERS; i++) { pdm_stream->queue[i].data = NULL; pdm_stream->queue[i].length = 0; pdm_stream->queue[i].id = 0; } return SDRV_PDM_STATUS_SUCCESS; } /** * @brief sdrv_pdm_submit_transfer * * This function is used to submit transfer, which's max num is * SDRV_PDM_XFER_NUM_BUFFERS * * @param [in] pdm sdrv pdm controller * @param [in] transfer sdrv pdm transfer * @return sdrv_pdm_status_t */ sdrv_pdm_status_t sdrv_pdm_submit_transfer(sdrv_pdm_t *pdm, sdrv_pdm_transfer_t *transfer) { if (pdm == NULL || transfer == NULL) { ssdk_printf(PDM_ERR, "%s:err: pdm or transfer is null\r\n", __func__); return SDRV_PDM_STATUS_FAIL; } return sdrv_pdm_enqueue_transfer(pdm, transfer); } /** * @brief sdrv_pdm_start * * This fuction is used to start pdm transfer * * @param [in] pdm sdrv pdm controller * @return sdrv_pdm_status_t */ sdrv_pdm_status_t sdrv_pdm_start(sdrv_pdm_t *pdm) { sdrv_pdm_stream_t *pdm_stream; sdrv_pdm_params_t *params; bool ret; if (!pdm) { ssdk_printf(PDM_ERR, "%s:err: pdm is null \r\n", __func__); return SDRV_PDM_STATUS_FAIL; } pdm_stream = &pdm->stream; params = &pdm_stream->params; if (params->usedma) { sdrv_pdm_start_dma(pdm); } ret = saci_pdm_trigger(pdm, SDRV_PDM_TRIGGER_START); if (!ret) { ssdk_printf(PDM_ERR, "func<%s>: pdm start err \r\n", __func__); return SDRV_PDM_STATUS_FAIL; } return SDRV_PDM_STATUS_SUCCESS; } /** * @brief sdrv_pdm_stop * * This function is used to stop pdm transfer * * @param [in] pdm sdrv pdm controller * @return sdrv_pdm_status_t */ sdrv_pdm_status_t sdrv_pdm_stop(sdrv_pdm_t *pdm) { sdrv_pdm_stream_t *pdm_stream; sdrv_pdm_params_t *params; bool ret; if (!pdm) { ssdk_printf(PDM_ERR, "%s:err: pdm is null \r\n", __func__); return SDRV_PDM_STATUS_FAIL; } pdm_stream = &pdm->stream; params = &pdm_stream->params; if (params->usedma) { pdm_stream->dma_abort = true; sdrv_pdm_stop_dma(pdm); } ret = saci_pdm_trigger(pdm, SDRV_PDM_TRIGGER_STOP); if (!ret) { ssdk_printf(PDM_ERR, "func<%s>: pdm stop err \r\n", __func__); return SDRV_PDM_STATUS_FAIL; } return SDRV_PDM_STATUS_SUCCESS; }