mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-06 10:58:48 +09:00
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:
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user