From 343716232771285b6e916dafdc2c867e484b7bb6 Mon Sep 17 00:00:00 2001 From: XiaoTan Luo Date: Sat, 27 Aug 2022 17:00:35 +0800 Subject: [PATCH] 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 Signed-off-by: Sugar Zhang Change-Id: I1b22de7c73cc181d2acd756ee2e32b8361db7822 --- sound/soc/rockchip/rockchip_pdm.c | 206 +++++++++++++++++++++++++++--- sound/soc/rockchip/rockchip_pdm.h | 3 + 2 files changed, 192 insertions(+), 17 deletions(-) diff --git a/sound/soc/rockchip/rockchip_pdm.c b/sound/soc/rockchip/rockchip_pdm.c index 4188083ab3cb..f4719f271793 100644 --- a/sound/soc/rockchip/rockchip_pdm.c +++ b/sound/soc/rockchip/rockchip_pdm.c @@ -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); diff --git a/sound/soc/rockchip/rockchip_pdm.h b/sound/soc/rockchip/rockchip_pdm.h index cab977272ee6..180b50b95d43 100644 --- a/sound/soc/rockchip/rockchip_pdm.h +++ b/sound/soc/rockchip/rockchip_pdm.h @@ -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)