Files
E3/e3_176_ref/drivers/source/pdm/sdrv_pdm.c
2025-10-21 19:40:27 +08:00

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