From 1f93a7dfd1f8d5ff7a5c53246c7534fe2332d6f4 Mon Sep 17 00:00:00 2001 From: hc <hc@nodka.com> Date: Mon, 11 Dec 2023 02:46:07 +0000 Subject: [PATCH] add audio --- kernel/sound/soc/rockchip/rockchip_pdm.c | 234 ++++++++++++++++++++++++++++++---------------------------- 1 files changed, 121 insertions(+), 113 deletions(-) diff --git a/kernel/sound/soc/rockchip/rockchip_pdm.c b/kernel/sound/soc/rockchip/rockchip_pdm.c index d7f1f97..ae529a6 100644 --- a/kernel/sound/soc/rockchip/rockchip_pdm.c +++ b/kernel/sound/soc/rockchip/rockchip_pdm.c @@ -1,17 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Rockchip PDM ALSA SoC Digital Audio Interface(DAI) driver * * Copyright (C) 2017 Fuzhou Rockchip Electronics Co., Ltd - * - * This software is licensed under the terms of the GNU General Public - * License version 2, as published by the Free Software Foundation, and - * may be copied, distributed, and modified under those terms. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * */ #include <linux/module.h> @@ -29,20 +20,23 @@ #include "rockchip_pdm.h" #define PDM_DMA_BURST_SIZE (8) /* size * width: 8*4 = 32 bytes */ -#define PDM_SIGNOFF_CLK_RATE (100000000) +#define PDM_SIGNOFF_CLK_100M (100000000) +#define PDM_SIGNOFF_CLK_300M (300000000) #define PDM_PATH_MAX (4) -#define CLK_PPM_MIN (-1000) -#define CLK_PPM_MAX (1000) #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) +#define PDM_CLK_SHIFT_PPM_MAX (1000000) /* 1 ppm */ +#define CLK_PPM_MIN (-1000) +#define CLK_PPM_MAX (1000) enum rk_pdm_version { + RK_PDM_RK3229, RK_PDM_RK3308, - RK_PDM_RK3328, + RK_PDM_RK3588, RK_PDM_RV1126, }; @@ -101,9 +95,10 @@ }; static unsigned int get_pdm_clk(struct rk_pdm_dev *pdm, unsigned int sr, - unsigned int *clk_src, unsigned int *clk_out) + unsigned int *clk_src, unsigned int *clk_out, + unsigned int signoff) { - unsigned int i, count, clk, div, rate; + unsigned int i, count, clk, div, rate, delta; clk = 0; if (!sr) @@ -122,7 +117,9 @@ break; } rate = clk_round_rate(pdm->clk, clkref[i].clk); - if (rate != clkref[i].clk) + delta = clkref[i].clk / PDM_CLK_SHIFT_PPM_MAX; + if (rate < clkref[i].clk - delta || + rate > clkref[i].clk + delta) continue; clk = clkref[i].clk; *clk_src = clkref[i].clk; @@ -131,7 +128,7 @@ } if (!clk) { - clk = clk_round_rate(pdm->clk, PDM_SIGNOFF_CLK_RATE); + clk = clk_round_rate(pdm->clk, signoff); *clk_src = clk; } return clk; @@ -207,22 +204,35 @@ 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) { + unsigned long flags; + if (on) { - rockchip_pdm_drop_fifo(pdm); + /* The PDM device need to delete some unused data + * since the pdm of various manufacturers can not + * be stable quickly. This is done by commit "ASoC: + * rockchip: pdm: Fix pop noise in the beginning". + * + * But we do not know how many data we delete, this + * cause channel disorder. For example, we record + * two channel 24-bit sound, then delete some starting + * data. Because the deleted starting data is uncertain, + * the next data may be left or right channel and cause + * channel disorder. + * + * Luckily, we can use the PDM_RX_CLR to fix this. + * Use the PDM_RX_CLR to clear fifo written data and + * address, but can not clear the read data and address. + * In initial state, the read data and address are zero. + */ + local_irq_save(flags); + regmap_update_bits(pdm->regmap, PDM_SYSCONFIG, + PDM_RX_CLR_MASK, + PDM_RX_CLR_WR); regmap_update_bits(pdm->regmap, PDM_DMA_CTRL, PDM_DMA_RD_MSK, PDM_DMA_RD_EN); + local_irq_restore(flags); } else { regmap_update_bits(pdm->regmap, PDM_DMA_CTRL, PDM_DMA_RD_MSK, PDM_DMA_RD_DIS); @@ -264,18 +274,20 @@ return ret; } -static int rockchip_pdm_set_samplerate(struct rk_pdm_dev *pdm, - unsigned int samplerate) +static int rockchip_pdm_set_samplerate(struct rk_pdm_dev *pdm, unsigned int samplerate) { - unsigned int clk_rate, clk_div; - unsigned int clk_src, clk_out = 0; - unsigned int val = 0, div = 0, rate, delta; + + unsigned int val = 0, div = 0; + unsigned int clk_rate, clk_div, rate, delta; + unsigned int clk_src = 0, clk_out = 0, signoff = PDM_SIGNOFF_CLK_100M; unsigned long m, n; uint64_t ppm; - int ret; bool change; + int ret; - clk_rate = get_pdm_clk(pdm, samplerate, &clk_src, &clk_out); + if (pdm->version == RK_PDM_RK3588) + signoff = PDM_SIGNOFF_CLK_300M; + clk_rate = get_pdm_clk(pdm, samplerate, &clk_src, &clk_out, signoff); if (!clk_rate) return -EINVAL; @@ -312,6 +324,7 @@ return ret; if (pdm->version == RK_PDM_RK3308 || + pdm->version == RK_PDM_RK3588 || pdm->version == RK_PDM_RV1126) { rational_best_approximation(clk_out, clk_src, GENMASK(16 - 1, 0), @@ -341,7 +354,7 @@ val); } - if (pdm->version == RK_PDM_RV1126) { + if (pdm->version == RK_PDM_RK3588 || pdm->version == RK_PDM_RV1126) { val = get_pdm_cic_ratio(clk_out); regmap_update_bits(pdm->regmap, PDM_CLK_CTRL, PDM_CIC_RATIO_MSK, val); val = samplerate_to_bit(samplerate); @@ -351,10 +364,11 @@ val = get_pdm_ds_ratio(samplerate); regmap_update_bits(pdm->regmap, PDM_CLK_CTRL, PDM_DS_RATIO_MSK, val); } - regmap_update_bits(pdm->regmap, PDM_HPF_CTRL, PDM_HPF_CF_MSK, PDM_HPF_60HZ); + + regmap_update_bits(pdm->regmap, PDM_HPF_CTRL, + 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); - return 0; } @@ -370,7 +384,7 @@ rockchip_pdm_set_samplerate(pdm, params_rate(params)); - if (pdm->version != RK_PDM_RK3328) + if (pdm->version != RK_PDM_RK3229) regmap_update_bits(pdm->regmap, PDM_CTRL0, PDM_MODE_MSK, PDM_MODE_LJ); @@ -398,13 +412,13 @@ switch (params_channels(params)) { case 8: val |= PDM_PATH3_EN; - /* fallthrough */ + fallthrough; case 6: val |= PDM_PATH2_EN; - /* fallthrough */ + fallthrough; case 4: val |= PDM_PATH1_EN; - /* fallthrough */ + fallthrough; case 2: val |= PDM_PATH0_EN; break; @@ -476,51 +490,6 @@ return ret; } -static int rockchip_pdm_clk_compensation_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 = CLK_PPM_MIN; - uinfo->value.integer.max = CLK_PPM_MAX; - uinfo->value.integer.step = 1; - - return 0; -} - -static int rockchip_pdm_clk_compensation_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->clk_ppm; - - return 0; -} - -static int rockchip_pdm_clk_compensation_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); - int ppm = ucontrol->value.integer.value[0]; - - if ((ucontrol->value.integer.value[0] < CLK_PPM_MIN) || - (ucontrol->value.integer.value[0] > CLK_PPM_MAX)) - return -EINVAL; - - return rockchip_pdm_clk_set_rate(pdm, pdm->clk_root, pdm->clk_root_rate, ppm); -} - -static struct snd_kcontrol_new rockchip_pdm_compensation_control = { - .iface = SNDRV_CTL_ELEM_IFACE_PCM, - .name = "PCM Clk Compensation In PPM", - .info = rockchip_pdm_clk_compensation_info, - .get = rockchip_pdm_clk_compensation_get, - .put = rockchip_pdm_clk_compensation_put, -}; - static int rockchip_pdm_start_delay_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { @@ -531,7 +500,6 @@ uinfo->value.integer.step = 1; return 0; - } static int rockchip_pdm_start_delay_get(struct snd_kcontrol *kcontrol, @@ -615,16 +583,64 @@ }, }; +static int rockchip_pdm_clk_compensation_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 = CLK_PPM_MIN; + uinfo->value.integer.max = CLK_PPM_MAX; + uinfo->value.integer.step = 1; + + return 0; +} + + +static int rockchip_pdm_clk_compensation_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->clk_ppm; + + return 0; +} + +static int rockchip_pdm_clk_compensation_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); + + int ppm = ucontrol->value.integer.value[0]; + + if ((ucontrol->value.integer.value[0] < CLK_PPM_MIN) || + (ucontrol->value.integer.value[0] > CLK_PPM_MAX)) + return -EINVAL; + + return rockchip_pdm_clk_set_rate(pdm, pdm->clk_root, pdm->clk_root_rate, ppm); +} + +static struct snd_kcontrol_new rockchip_pdm_compensation_control = { + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = "PDM PCM Clk Compensation In PPM", + .info = rockchip_pdm_clk_compensation_info, + .get = rockchip_pdm_clk_compensation_get, + .put = rockchip_pdm_clk_compensation_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; - - if (pdm->clk_calibrate) - snd_soc_add_dai_controls(dai, &rockchip_pdm_compensation_control, 1); snd_soc_add_dai_controls(dai, rockchip_pdm_controls, ARRAY_SIZE(rockchip_pdm_controls)); + if (pdm->clk_calibrate) + snd_soc_add_dai_controls(dai, &rockchip_pdm_compensation_control, 1); return 0; } @@ -735,6 +751,7 @@ return ret; } + rockchip_pdm_rxctrl(pdm, 0); regcache_cache_only(pdm->regmap, false); regcache_mark_dirty(pdm->regmap); ret = regcache_sync(pdm->regmap); @@ -831,31 +848,21 @@ .cache_type = REGCACHE_FLAT, }; -static const struct of_device_id rockchip_pdm_match[] = { -#ifdef CONFIG_CPU_PX30 +static const struct of_device_id rockchip_pdm_match[] __maybe_unused = { + { .compatible = "rockchip,pdm", + .data = (void *)RK_PDM_RK3229 }, { .compatible = "rockchip,px30-pdm", .data = (void *)RK_PDM_RK3308 }, -#endif -#ifdef CONFIG_CPU_RK1808 { .compatible = "rockchip,rk1808-pdm", .data = (void *)RK_PDM_RK3308 }, -#endif -#ifdef CONFIG_CPU_RK3308 { .compatible = "rockchip,rk3308-pdm", .data = (void *)RK_PDM_RK3308 }, -#endif -#ifdef CONFIG_CPU_RK3328 - { .compatible = "rockchip,rk3328-pdm", - .data = (void *)RK_PDM_RK3328 }, -#endif -#ifdef CONFIG_CPU_RK3568 { .compatible = "rockchip,rk3568-pdm", .data = (void *)RK_PDM_RV1126 }, -#endif -#ifdef CONFIG_CPU_RV1126 + { .compatible = "rockchip,rk3588-pdm", + .data = (void *)RK_PDM_RK3588 }, { .compatible = "rockchip,rv1126-pdm", .data = (void *)RK_PDM_RV1126 }, -#endif {}, }; MODULE_DEVICE_TABLE(of, rockchip_pdm_match); @@ -910,8 +917,7 @@ return PTR_ERR(pdm->reset); } - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - regs = devm_ioremap_resource(&pdev->dev, res); + regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res); if (IS_ERR(regs)) return PTR_ERR(regs); @@ -927,6 +933,9 @@ 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_calibrate = of_property_read_bool(node, "rockchip,mclk-calibrate"); if (pdm->clk_calibrate) { @@ -937,9 +946,6 @@ pdm->clk_root_initial_rate = clk_get_rate(pdm->clk_root); pdm->clk_root_rate = pdm->clk_root_initial_rate; } - - 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)) @@ -976,8 +982,10 @@ if (ret != 0 && ret != -ENOENT) goto err_suspend; - if (of_property_read_bool(node, "rockchip,no-dmaengine")) + if (of_property_read_bool(node, "rockchip,no-dmaengine")) { + dev_info(&pdev->dev, "Used for Multi-DAI\n"); return 0; + } ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0); if (ret) { -- Gitblit v1.6.2