1698 lines
48 KiB
C
1698 lines
48 KiB
C
/**
|
|
* @file sdrv_pdm.c
|
|
*
|
|
* @brief SemiDrive pdm driver source file.
|
|
*
|
|
* @copyright Copyright (c) 2021 Semidrive Semiconductor.
|
|
* All rights reserved.
|
|
*
|
|
*/
|
|
#include <armv7-r/cache.h>
|
|
#include <bits.h>
|
|
#include <debug.h>
|
|
#include <irq.h>
|
|
#include <param.h>
|
|
#include <reg.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <udelay/udelay.h>
|
|
|
|
#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;
|
|
}
|