ASoC: rockchip: pdm: Fix pop noise in the beginning

Generally, DMICs require some delay to wake up to NORMAL state
after clk enabled (WAKE-UP Time), and it varies per dmic chips,
we should ignore the dirty data in this duration.

This patch introduce "start/filter-delay-ms" to skip the pop noise
in the duration, and export this delay to allow user to change it.

* PDM Start Delay Ms
  a necessary delay for dmics wake-up after clk enabled, and drop the
  dirty data in this duration.

* PDM Filter Delay Ms
  after xfer start, a necessary delay for filter to init and will drop
  the dirty data in the trigger-START late.

e.g.

/# amixer -c 3 contents
numid=4,iface=PCM,name='PDM Filter Delay Ms'
  ; type=INTEGER,access=rw------,values=1,min=20,max=1000,step=1
  : values=20
numid=3,iface=PCM,name='PDM Start Delay Ms'
  ; type=INTEGER,access=rw------,values=1,min=0,max=1000,step=1
  : values=20

/# amixer -c 3 cset numid=4 30
numid=4,iface=PCM,name='PDM Filter Delay Ms'
  ; type=INTEGER,access=rw------,values=1,min=20,max=1000,step=1
  : values=30
...

Signed-off-by: XiaoTan Luo <lxt@rock-chips.com>
Signed-off-by: Sugar Zhang <sugar.zhang@rock-chips.com>
Change-Id: I1b22de7c73cc181d2acd756ee2e32b8361db7822
This commit is contained in:
XiaoTan Luo
2022-08-27 17:00:35 +08:00
committed by Tao Huang
parent 6f3aa8f570
commit 3437162327
2 changed files with 192 additions and 17 deletions

View File

@@ -18,10 +18,16 @@
#include "rockchip_pdm.h"
#define PDM_DMA_BURST_SIZE (8) /* size * width: 8*4 = 32 bytes */
#define PDM_SIGNOFF_CLK_100M (100000000)
#define PDM_SIGNOFF_CLK_300M (300000000)
#define PDM_PATH_MAX (4)
#define PDM_DMA_BURST_SIZE (8) /* size * width: 8*4 = 32 bytes */
#define PDM_SIGNOFF_CLK_100M (100000000)
#define PDM_SIGNOFF_CLK_300M (300000000)
#define PDM_PATH_MAX (4)
#define PDM_DEFAULT_RATE (48000)
#define PDM_START_DELAY_MS_DEFAULT (20)
#define PDM_START_DELAY_MS_MIN (0)
#define PDM_START_DELAY_MS_MAX (1000)
#define PDM_FILTER_DELAY_MS_MIN (20)
#define PDM_FILTER_DELAY_MS_MAX (1000)
enum rk_pdm_version {
RK_PDM_RK3229,
@@ -37,6 +43,8 @@ struct rk_pdm_dev {
struct regmap *regmap;
struct snd_dmaengine_dai_dma_data capture_dma_data;
struct reset_control *reset;
unsigned int start_delay_ms;
unsigned int filter_delay_ms;
enum rk_pdm_version version;
};
@@ -180,13 +188,22 @@ static inline struct rk_pdm_dev *to_info(struct snd_soc_dai *dai)
return snd_soc_dai_get_drvdata(dai);
}
static void rockchip_pdm_drop_fifo(struct rk_pdm_dev *pdm)
{
int cnt, val, i;
/* drop the dirty data */
regmap_read(pdm->regmap, PDM_FIFO_CTRL, &cnt);
for (i = 0; i < PDM_FIFO_CNT(cnt); i++)
regmap_read(pdm->regmap, PDM_RXFIFO_DATA, &val);
}
static void rockchip_pdm_rxctrl(struct rk_pdm_dev *pdm, int on)
{
if (on) {
rockchip_pdm_drop_fifo(pdm);
regmap_update_bits(pdm->regmap, PDM_DMA_CTRL,
PDM_DMA_RD_MSK, PDM_DMA_RD_EN);
regmap_update_bits(pdm->regmap, PDM_SYSCONFIG,
PDM_RX_MASK, PDM_RX_START);
} else {
regmap_update_bits(pdm->regmap, PDM_DMA_CTRL,
PDM_DMA_RD_MSK, PDM_DMA_RD_DIS);
@@ -196,23 +213,16 @@ static void rockchip_pdm_rxctrl(struct rk_pdm_dev *pdm, int on)
}
}
static int rockchip_pdm_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
static int rockchip_pdm_set_samplerate(struct rk_pdm_dev *pdm, unsigned int samplerate)
{
struct rk_pdm_dev *pdm = to_info(dai);
unsigned int val = 0;
unsigned int clk_rate, clk_div, samplerate;
unsigned int clk_rate, clk_div;
unsigned int clk_src = 0, clk_out = 0, signoff = PDM_SIGNOFF_CLK_100M;
unsigned long m, n;
bool change;
int ret;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
return 0;
samplerate = params_rate(params);
if (pdm->version == RK_PDM_RK3588)
signoff = PDM_SIGNOFF_CLK_300M;
clk_rate = get_pdm_clk(pdm, samplerate, &clk_src, &clk_out, signoff);
@@ -269,7 +279,21 @@ static int rockchip_pdm_hw_params(struct snd_pcm_substream *substream,
PDM_HPF_CF_MSK, PDM_HPF_60HZ);
regmap_update_bits(pdm->regmap, PDM_HPF_CTRL,
PDM_HPF_LE | PDM_HPF_RE, PDM_HPF_LE | PDM_HPF_RE);
regmap_update_bits(pdm->regmap, PDM_CLK_CTRL, PDM_CLK_EN, PDM_CLK_EN);
return 0;
}
static int rockchip_pdm_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
struct rk_pdm_dev *pdm = to_info(dai);
unsigned int val = 0;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
return 0;
rockchip_pdm_set_samplerate(pdm, params_rate(params));
if (pdm->version != RK_PDM_RK3229)
regmap_update_bits(pdm->regmap, PDM_CTRL0,
PDM_MODE_MSK, PDM_MODE_LJ);
@@ -376,18 +400,162 @@ static int rockchip_pdm_trigger(struct snd_pcm_substream *substream, int cmd,
return ret;
}
static int rockchip_pdm_start_delay_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
uinfo->count = 1;
uinfo->value.integer.min = PDM_START_DELAY_MS_MIN;
uinfo->value.integer.max = PDM_START_DELAY_MS_MAX;
uinfo->value.integer.step = 1;
return 0;
}
static int rockchip_pdm_start_delay_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_dai *dai = snd_kcontrol_chip(kcontrol);
struct rk_pdm_dev *pdm = snd_soc_dai_get_drvdata(dai);
ucontrol->value.integer.value[0] = pdm->start_delay_ms;
return 0;
}
static int rockchip_pdm_start_delay_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_dai *dai = snd_kcontrol_chip(kcontrol);
struct rk_pdm_dev *pdm = snd_soc_dai_get_drvdata(dai);
if ((ucontrol->value.integer.value[0] < PDM_START_DELAY_MS_MIN) ||
(ucontrol->value.integer.value[0] > PDM_START_DELAY_MS_MAX))
return -EINVAL;
pdm->start_delay_ms = ucontrol->value.integer.value[0];
return 1;
}
static int rockchip_pdm_filter_delay_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
uinfo->count = 1;
uinfo->value.integer.min = PDM_FILTER_DELAY_MS_MIN;
uinfo->value.integer.max = PDM_FILTER_DELAY_MS_MAX;
uinfo->value.integer.step = 1;
return 0;
}
static int rockchip_pdm_filter_delay_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_dai *dai = snd_kcontrol_chip(kcontrol);
struct rk_pdm_dev *pdm = snd_soc_dai_get_drvdata(dai);
ucontrol->value.integer.value[0] = pdm->filter_delay_ms;
return 0;
}
static int rockchip_pdm_filter_delay_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_dai *dai = snd_kcontrol_chip(kcontrol);
struct rk_pdm_dev *pdm = snd_soc_dai_get_drvdata(dai);
if ((ucontrol->value.integer.value[0] < PDM_FILTER_DELAY_MS_MIN) ||
(ucontrol->value.integer.value[0] > PDM_FILTER_DELAY_MS_MAX))
return -EINVAL;
pdm->filter_delay_ms = ucontrol->value.integer.value[0];
return 1;
}
static const struct snd_kcontrol_new rockchip_pdm_controls[] = {
{
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
.name = "PDM Start Delay Ms",
.info = rockchip_pdm_start_delay_info,
.get = rockchip_pdm_start_delay_get,
.put = rockchip_pdm_start_delay_put,
},
{
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
.name = "PDM Filter Delay Ms",
.info = rockchip_pdm_filter_delay_info,
.get = rockchip_pdm_filter_delay_get,
.put = rockchip_pdm_filter_delay_put,
},
};
static int rockchip_pdm_dai_probe(struct snd_soc_dai *dai)
{
struct rk_pdm_dev *pdm = to_info(dai);
dai->capture_dma_data = &pdm->capture_dma_data;
snd_soc_add_dai_controls(dai, rockchip_pdm_controls,
ARRAY_SIZE(rockchip_pdm_controls));
return 0;
}
static void rockchip_pdm_shutdown(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct rk_pdm_dev *pdm = to_info(dai);
if (substream->stream != SNDRV_PCM_STREAM_CAPTURE)
return;
regmap_update_bits(pdm->regmap, PDM_CLK_CTRL, PDM_CLK_MSK, PDM_CLK_DIS);
}
static int rockchip_pdm_prepare(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct rk_pdm_dev *pdm = to_info(dai);
if (substream->stream != SNDRV_PCM_STREAM_CAPTURE)
return 0;
regmap_update_bits(pdm->regmap, PDM_SYSCONFIG, PDM_RX_MASK, PDM_RX_START);
/*
* after xfer start, a necessary delay for filter to init and will drop
* the dirty data in the trigger-START late.
*/
usleep_range((pdm->filter_delay_ms) * 1000, (pdm->filter_delay_ms + 1) * 1000);
return 0;
}
static int rockchip_pdm_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct rk_pdm_dev *pdm = to_info(dai);
if (substream->stream != SNDRV_PCM_STREAM_CAPTURE)
return 0;
regmap_update_bits(pdm->regmap, PDM_CLK_CTRL, PDM_CLK_MSK, PDM_CLK_EN);
/*
* a necessary delay for dmics wake-up after clk enabled, and drop the
* dirty data in this duration.
*/
usleep_range((pdm->start_delay_ms + 1) * 1000, (pdm->start_delay_ms + 2) * 1000);
return 0;
}
static const struct snd_soc_dai_ops rockchip_pdm_dai_ops = {
.startup = rockchip_pdm_startup,
.shutdown = rockchip_pdm_shutdown,
.set_fmt = rockchip_pdm_set_fmt,
.trigger = rockchip_pdm_trigger,
.prepare = rockchip_pdm_prepare,
.hw_params = rockchip_pdm_hw_params,
};
@@ -623,6 +791,9 @@ static int rockchip_pdm_probe(struct platform_device *pdev)
pdm->dev = &pdev->dev;
dev_set_drvdata(&pdev->dev, pdm);
pdm->start_delay_ms = PDM_START_DELAY_MS_DEFAULT;
pdm->filter_delay_ms = PDM_FILTER_DELAY_MS_MIN;
pdm->clk = devm_clk_get(&pdev->dev, "pdm_clk");
if (IS_ERR(pdm->clk))
return PTR_ERR(pdm->clk);
@@ -651,6 +822,7 @@ static int rockchip_pdm_probe(struct platform_device *pdev)
goto err_suspend;
}
rockchip_pdm_set_samplerate(pdm, PDM_DEFAULT_RATE);
rockchip_pdm_rxctrl(pdm, 0);
ret = rockchip_pdm_path_parse(pdm, node);

View File

@@ -82,6 +82,9 @@
#define PDM_HPF_243HZ (0x2 << 0)
#define PDM_HPF_493HZ (0x3 << 0)
/* PDM FIFO CTRL */
#define PDM_FIFO_CNT(x) ((x) & 0xff)
/* PDM DMA CTRL */
#define PDM_DMA_RD_MSK BIT(8)
#define PDM_DMA_RD_EN BIT(8)