719 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Rockchip PDM ALSA SoC Digital Audio Interface(DAI) driver4 *5 * Copyright (C) 2017 Fuzhou Rockchip Electronics Co., Ltd6 */7 8#include <linux/module.h>9#include <linux/clk.h>10#include <linux/of.h>11#include <linux/pm_runtime.h>12#include <linux/rational.h>13#include <linux/regmap.h>14#include <linux/reset.h>15#include <sound/dmaengine_pcm.h>16#include <sound/pcm_params.h>17 18#include "rockchip_pdm.h"19 20#define PDM_DMA_BURST_SIZE (8) /* size * width: 8*4 = 32 bytes */21#define PDM_SIGNOFF_CLK_RATE (100000000)22#define PDM_PATH_MAX (4)23 24enum rk_pdm_version {25 RK_PDM_RK3229,26 RK_PDM_RK3308,27 RK_PDM_RV1126,28};29 30struct rk_pdm_dev {31 struct device *dev;32 struct clk *clk;33 struct clk *hclk;34 struct regmap *regmap;35 struct snd_dmaengine_dai_dma_data capture_dma_data;36 struct reset_control *reset;37 enum rk_pdm_version version;38};39 40struct rk_pdm_clkref {41 unsigned int sr;42 unsigned int clk;43 unsigned int clk_out;44};45 46struct rk_pdm_ds_ratio {47 unsigned int ratio;48 unsigned int sr;49};50 51static struct rk_pdm_clkref clkref[] = {52 { 8000, 40960000, 2048000 },53 { 11025, 56448000, 2822400 },54 { 12000, 61440000, 3072000 },55 { 8000, 98304000, 2048000 },56 { 12000, 98304000, 3072000 },57};58 59static struct rk_pdm_ds_ratio ds_ratio[] = {60 { 0, 192000 },61 { 0, 176400 },62 { 0, 128000 },63 { 1, 96000 },64 { 1, 88200 },65 { 1, 64000 },66 { 2, 48000 },67 { 2, 44100 },68 { 2, 32000 },69 { 3, 24000 },70 { 3, 22050 },71 { 3, 16000 },72 { 4, 12000 },73 { 4, 11025 },74 { 4, 8000 },75};76 77static unsigned int get_pdm_clk(struct rk_pdm_dev *pdm, unsigned int sr,78 unsigned int *clk_src, unsigned int *clk_out)79{80 unsigned int i, count, clk, div, rate;81 82 clk = 0;83 if (!sr)84 return clk;85 86 count = ARRAY_SIZE(clkref);87 for (i = 0; i < count; i++) {88 if (sr % clkref[i].sr)89 continue;90 div = sr / clkref[i].sr;91 if ((div & (div - 1)) == 0) {92 *clk_out = clkref[i].clk_out;93 rate = clk_round_rate(pdm->clk, clkref[i].clk);94 if (rate != clkref[i].clk)95 continue;96 clk = clkref[i].clk;97 *clk_src = clkref[i].clk;98 break;99 }100 }101 102 if (!clk) {103 clk = clk_round_rate(pdm->clk, PDM_SIGNOFF_CLK_RATE);104 *clk_src = clk;105 }106 return clk;107}108 109static unsigned int get_pdm_ds_ratio(unsigned int sr)110{111 unsigned int i, count, ratio;112 113 ratio = 0;114 if (!sr)115 return ratio;116 117 count = ARRAY_SIZE(ds_ratio);118 for (i = 0; i < count; i++) {119 if (sr == ds_ratio[i].sr)120 ratio = ds_ratio[i].ratio;121 }122 return ratio;123}124 125static unsigned int get_pdm_cic_ratio(unsigned int clk)126{127 switch (clk) {128 case 4096000:129 case 5644800:130 case 6144000:131 return 0;132 case 2048000:133 case 2822400:134 case 3072000:135 return 1;136 case 1024000:137 case 1411200:138 case 1536000:139 return 2;140 default:141 return 1;142 }143}144 145static unsigned int samplerate_to_bit(unsigned int samplerate)146{147 switch (samplerate) {148 case 8000:149 case 11025:150 case 12000:151 return 0;152 case 16000:153 case 22050:154 case 24000:155 return 1;156 case 32000:157 return 2;158 case 44100:159 case 48000:160 return 3;161 case 64000:162 case 88200:163 case 96000:164 return 4;165 case 128000:166 case 176400:167 case 192000:168 return 5;169 default:170 return 1;171 }172}173 174static inline struct rk_pdm_dev *to_info(struct snd_soc_dai *dai)175{176 return snd_soc_dai_get_drvdata(dai);177}178 179static void rockchip_pdm_rxctrl(struct rk_pdm_dev *pdm, int on)180{181 if (on) {182 regmap_update_bits(pdm->regmap, PDM_DMA_CTRL,183 PDM_DMA_RD_MSK, PDM_DMA_RD_EN);184 regmap_update_bits(pdm->regmap, PDM_SYSCONFIG,185 PDM_RX_MASK, PDM_RX_START);186 } else {187 regmap_update_bits(pdm->regmap, PDM_DMA_CTRL,188 PDM_DMA_RD_MSK, PDM_DMA_RD_DIS);189 regmap_update_bits(pdm->regmap, PDM_SYSCONFIG,190 PDM_RX_MASK | PDM_RX_CLR_MASK,191 PDM_RX_STOP | PDM_RX_CLR_WR);192 }193}194 195static int rockchip_pdm_hw_params(struct snd_pcm_substream *substream,196 struct snd_pcm_hw_params *params,197 struct snd_soc_dai *dai)198{199 struct rk_pdm_dev *pdm = to_info(dai);200 unsigned int val = 0;201 unsigned int clk_rate, clk_div, samplerate;202 unsigned int clk_src, clk_out = 0;203 unsigned long m, n;204 bool change;205 int ret;206 207 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)208 return 0;209 210 samplerate = params_rate(params);211 clk_rate = get_pdm_clk(pdm, samplerate, &clk_src, &clk_out);212 if (!clk_rate)213 return -EINVAL;214 215 ret = clk_set_rate(pdm->clk, clk_src);216 if (ret)217 return -EINVAL;218 219 if (pdm->version == RK_PDM_RK3308 ||220 pdm->version == RK_PDM_RV1126) {221 rational_best_approximation(clk_out, clk_src,222 GENMASK(16 - 1, 0),223 GENMASK(16 - 1, 0),224 &m, &n);225 226 val = (m << PDM_FD_NUMERATOR_SFT) |227 (n << PDM_FD_DENOMINATOR_SFT);228 regmap_update_bits_check(pdm->regmap, PDM_CTRL1,229 PDM_FD_NUMERATOR_MSK |230 PDM_FD_DENOMINATOR_MSK,231 val, &change);232 if (change) {233 reset_control_assert(pdm->reset);234 reset_control_deassert(pdm->reset);235 rockchip_pdm_rxctrl(pdm, 0);236 }237 clk_div = n / m;238 if (clk_div >= 40)239 val = PDM_CLK_FD_RATIO_40;240 else if (clk_div <= 35)241 val = PDM_CLK_FD_RATIO_35;242 else243 return -EINVAL;244 regmap_update_bits(pdm->regmap, PDM_CLK_CTRL,245 PDM_CLK_FD_RATIO_MSK,246 val);247 }248 249 if (pdm->version == RK_PDM_RV1126) {250 val = get_pdm_cic_ratio(clk_out);251 regmap_update_bits(pdm->regmap, PDM_CLK_CTRL, PDM_CIC_RATIO_MSK, val);252 val = samplerate_to_bit(samplerate);253 regmap_update_bits(pdm->regmap, PDM_CTRL0,254 PDM_SAMPLERATE_MSK, PDM_SAMPLERATE(val));255 } else {256 val = get_pdm_ds_ratio(samplerate);257 regmap_update_bits(pdm->regmap, PDM_CLK_CTRL, PDM_DS_RATIO_MSK, val);258 }259 260 regmap_update_bits(pdm->regmap, PDM_HPF_CTRL,261 PDM_HPF_CF_MSK, PDM_HPF_60HZ);262 regmap_update_bits(pdm->regmap, PDM_HPF_CTRL,263 PDM_HPF_LE | PDM_HPF_RE, PDM_HPF_LE | PDM_HPF_RE);264 regmap_update_bits(pdm->regmap, PDM_CLK_CTRL, PDM_CLK_EN, PDM_CLK_EN);265 if (pdm->version != RK_PDM_RK3229)266 regmap_update_bits(pdm->regmap, PDM_CTRL0,267 PDM_MODE_MSK, PDM_MODE_LJ);268 269 val = 0;270 switch (params_format(params)) {271 case SNDRV_PCM_FORMAT_S8:272 val |= PDM_VDW(8);273 break;274 case SNDRV_PCM_FORMAT_S16_LE:275 val |= PDM_VDW(16);276 break;277 case SNDRV_PCM_FORMAT_S20_3LE:278 val |= PDM_VDW(20);279 break;280 case SNDRV_PCM_FORMAT_S24_LE:281 val |= PDM_VDW(24);282 break;283 case SNDRV_PCM_FORMAT_S32_LE:284 val |= PDM_VDW(32);285 break;286 default:287 return -EINVAL;288 }289 290 switch (params_channels(params)) {291 case 8:292 val |= PDM_PATH3_EN;293 fallthrough;294 case 6:295 val |= PDM_PATH2_EN;296 fallthrough;297 case 4:298 val |= PDM_PATH1_EN;299 fallthrough;300 case 2:301 val |= PDM_PATH0_EN;302 break;303 default:304 dev_err(pdm->dev, "invalid channel: %d\n",305 params_channels(params));306 return -EINVAL;307 }308 309 regmap_update_bits(pdm->regmap, PDM_CTRL0,310 PDM_PATH_MSK | PDM_VDW_MSK,311 val);312 /* all channels share the single FIFO */313 regmap_update_bits(pdm->regmap, PDM_DMA_CTRL, PDM_DMA_RDL_MSK,314 PDM_DMA_RDL(8 * params_channels(params)));315 316 return 0;317}318 319static int rockchip_pdm_set_fmt(struct snd_soc_dai *cpu_dai,320 unsigned int fmt)321{322 struct rk_pdm_dev *pdm = to_info(cpu_dai);323 unsigned int mask = 0, val = 0;324 325 mask = PDM_CKP_MSK;326 switch (fmt & SND_SOC_DAIFMT_INV_MASK) {327 case SND_SOC_DAIFMT_NB_NF:328 val = PDM_CKP_NORMAL;329 break;330 case SND_SOC_DAIFMT_IB_NF:331 val = PDM_CKP_INVERTED;332 break;333 default:334 return -EINVAL;335 }336 337 pm_runtime_get_sync(cpu_dai->dev);338 regmap_update_bits(pdm->regmap, PDM_CLK_CTRL, mask, val);339 pm_runtime_put(cpu_dai->dev);340 341 return 0;342}343 344static int rockchip_pdm_trigger(struct snd_pcm_substream *substream, int cmd,345 struct snd_soc_dai *dai)346{347 struct rk_pdm_dev *pdm = to_info(dai);348 int ret = 0;349 350 switch (cmd) {351 case SNDRV_PCM_TRIGGER_START:352 case SNDRV_PCM_TRIGGER_RESUME:353 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:354 if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)355 rockchip_pdm_rxctrl(pdm, 1);356 break;357 case SNDRV_PCM_TRIGGER_SUSPEND:358 case SNDRV_PCM_TRIGGER_STOP:359 case SNDRV_PCM_TRIGGER_PAUSE_PUSH:360 if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)361 rockchip_pdm_rxctrl(pdm, 0);362 break;363 default:364 ret = -EINVAL;365 break;366 }367 368 return ret;369}370 371static int rockchip_pdm_dai_probe(struct snd_soc_dai *dai)372{373 struct rk_pdm_dev *pdm = to_info(dai);374 375 snd_soc_dai_dma_data_set_capture(dai, &pdm->capture_dma_data);376 377 return 0;378}379 380static const struct snd_soc_dai_ops rockchip_pdm_dai_ops = {381 .probe = rockchip_pdm_dai_probe,382 .set_fmt = rockchip_pdm_set_fmt,383 .trigger = rockchip_pdm_trigger,384 .hw_params = rockchip_pdm_hw_params,385};386 387#define ROCKCHIP_PDM_RATES SNDRV_PCM_RATE_8000_192000388#define ROCKCHIP_PDM_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \389 SNDRV_PCM_FMTBIT_S20_3LE | \390 SNDRV_PCM_FMTBIT_S24_LE | \391 SNDRV_PCM_FMTBIT_S32_LE)392 393static struct snd_soc_dai_driver rockchip_pdm_dai = {394 .capture = {395 .stream_name = "Capture",396 .channels_min = 2,397 .channels_max = 8,398 .rates = ROCKCHIP_PDM_RATES,399 .formats = ROCKCHIP_PDM_FORMATS,400 },401 .ops = &rockchip_pdm_dai_ops,402 .symmetric_rate = 1,403};404 405static const struct snd_soc_component_driver rockchip_pdm_component = {406 .name = "rockchip-pdm",407 .legacy_dai_naming = 1,408};409 410static int rockchip_pdm_runtime_suspend(struct device *dev)411{412 struct rk_pdm_dev *pdm = dev_get_drvdata(dev);413 414 clk_disable_unprepare(pdm->clk);415 clk_disable_unprepare(pdm->hclk);416 417 return 0;418}419 420static int rockchip_pdm_runtime_resume(struct device *dev)421{422 struct rk_pdm_dev *pdm = dev_get_drvdata(dev);423 int ret;424 425 ret = clk_prepare_enable(pdm->clk);426 if (ret) {427 dev_err(pdm->dev, "clock enable failed %d\n", ret);428 return ret;429 }430 431 ret = clk_prepare_enable(pdm->hclk);432 if (ret) {433 clk_disable_unprepare(pdm->clk);434 dev_err(pdm->dev, "hclock enable failed %d\n", ret);435 return ret;436 }437 438 return 0;439}440 441static bool rockchip_pdm_wr_reg(struct device *dev, unsigned int reg)442{443 switch (reg) {444 case PDM_SYSCONFIG:445 case PDM_CTRL0:446 case PDM_CTRL1:447 case PDM_CLK_CTRL:448 case PDM_HPF_CTRL:449 case PDM_FIFO_CTRL:450 case PDM_DMA_CTRL:451 case PDM_INT_EN:452 case PDM_INT_CLR:453 case PDM_DATA_VALID:454 return true;455 default:456 return false;457 }458}459 460static bool rockchip_pdm_rd_reg(struct device *dev, unsigned int reg)461{462 switch (reg) {463 case PDM_SYSCONFIG:464 case PDM_CTRL0:465 case PDM_CTRL1:466 case PDM_CLK_CTRL:467 case PDM_HPF_CTRL:468 case PDM_FIFO_CTRL:469 case PDM_DMA_CTRL:470 case PDM_INT_EN:471 case PDM_INT_CLR:472 case PDM_INT_ST:473 case PDM_DATA_VALID:474 case PDM_RXFIFO_DATA:475 case PDM_VERSION:476 return true;477 default:478 return false;479 }480}481 482static bool rockchip_pdm_volatile_reg(struct device *dev, unsigned int reg)483{484 switch (reg) {485 case PDM_SYSCONFIG:486 case PDM_FIFO_CTRL:487 case PDM_INT_CLR:488 case PDM_INT_ST:489 case PDM_RXFIFO_DATA:490 return true;491 default:492 return false;493 }494}495 496static bool rockchip_pdm_precious_reg(struct device *dev, unsigned int reg)497{498 switch (reg) {499 case PDM_RXFIFO_DATA:500 return true;501 default:502 return false;503 }504}505 506static const struct reg_default rockchip_pdm_reg_defaults[] = {507 { PDM_CTRL0, 0x78000017 },508 { PDM_CTRL1, 0x0bb8ea60 },509 { PDM_CLK_CTRL, 0x0000e401 },510 { PDM_DMA_CTRL, 0x0000001f },511};512 513static const struct regmap_config rockchip_pdm_regmap_config = {514 .reg_bits = 32,515 .reg_stride = 4,516 .val_bits = 32,517 .max_register = PDM_VERSION,518 .reg_defaults = rockchip_pdm_reg_defaults,519 .num_reg_defaults = ARRAY_SIZE(rockchip_pdm_reg_defaults),520 .writeable_reg = rockchip_pdm_wr_reg,521 .readable_reg = rockchip_pdm_rd_reg,522 .volatile_reg = rockchip_pdm_volatile_reg,523 .precious_reg = rockchip_pdm_precious_reg,524 .cache_type = REGCACHE_FLAT,525};526 527static const struct of_device_id rockchip_pdm_match[] __maybe_unused = {528 { .compatible = "rockchip,pdm",529 .data = (void *)RK_PDM_RK3229 },530 { .compatible = "rockchip,px30-pdm",531 .data = (void *)RK_PDM_RK3308 },532 { .compatible = "rockchip,rk1808-pdm",533 .data = (void *)RK_PDM_RK3308 },534 { .compatible = "rockchip,rk3308-pdm",535 .data = (void *)RK_PDM_RK3308 },536 { .compatible = "rockchip,rk3568-pdm",537 .data = (void *)RK_PDM_RV1126 },538 { .compatible = "rockchip,rv1126-pdm",539 .data = (void *)RK_PDM_RV1126 },540 {},541};542MODULE_DEVICE_TABLE(of, rockchip_pdm_match);543 544static int rockchip_pdm_path_parse(struct rk_pdm_dev *pdm, struct device_node *node)545{546 unsigned int path[PDM_PATH_MAX];547 int cnt = 0, ret = 0, i = 0, val = 0, msk = 0;548 549 cnt = of_count_phandle_with_args(node, "rockchip,path-map",550 NULL);551 if (cnt != PDM_PATH_MAX)552 return cnt;553 554 ret = of_property_read_u32_array(node, "rockchip,path-map",555 path, cnt);556 if (ret)557 return ret;558 559 for (i = 0; i < cnt; i++) {560 if (path[i] >= PDM_PATH_MAX)561 return -EINVAL;562 msk |= PDM_PATH_MASK(i);563 val |= PDM_PATH(i, path[i]);564 }565 566 regmap_update_bits(pdm->regmap, PDM_CLK_CTRL, msk, val);567 568 return 0;569}570 571static int rockchip_pdm_probe(struct platform_device *pdev)572{573 struct device_node *node = pdev->dev.of_node;574 struct rk_pdm_dev *pdm;575 struct resource *res;576 void __iomem *regs;577 int ret;578 579 pdm = devm_kzalloc(&pdev->dev, sizeof(*pdm), GFP_KERNEL);580 if (!pdm)581 return -ENOMEM;582 583 pdm->version = (enum rk_pdm_version)device_get_match_data(&pdev->dev);584 if (pdm->version == RK_PDM_RK3308) {585 pdm->reset = devm_reset_control_get(&pdev->dev, "pdm-m");586 if (IS_ERR(pdm->reset))587 return PTR_ERR(pdm->reset);588 }589 590 regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res);591 if (IS_ERR(regs))592 return PTR_ERR(regs);593 594 pdm->regmap = devm_regmap_init_mmio(&pdev->dev, regs,595 &rockchip_pdm_regmap_config);596 if (IS_ERR(pdm->regmap))597 return PTR_ERR(pdm->regmap);598 599 pdm->capture_dma_data.addr = res->start + PDM_RXFIFO_DATA;600 pdm->capture_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;601 pdm->capture_dma_data.maxburst = PDM_DMA_BURST_SIZE;602 603 pdm->dev = &pdev->dev;604 dev_set_drvdata(&pdev->dev, pdm);605 606 pdm->clk = devm_clk_get(&pdev->dev, "pdm_clk");607 if (IS_ERR(pdm->clk))608 return PTR_ERR(pdm->clk);609 610 pdm->hclk = devm_clk_get(&pdev->dev, "pdm_hclk");611 if (IS_ERR(pdm->hclk))612 return PTR_ERR(pdm->hclk);613 614 ret = clk_prepare_enable(pdm->hclk);615 if (ret)616 return ret;617 618 pm_runtime_enable(&pdev->dev);619 if (!pm_runtime_enabled(&pdev->dev)) {620 ret = rockchip_pdm_runtime_resume(&pdev->dev);621 if (ret)622 goto err_pm_disable;623 }624 625 ret = devm_snd_soc_register_component(&pdev->dev,626 &rockchip_pdm_component,627 &rockchip_pdm_dai, 1);628 629 if (ret) {630 dev_err(&pdev->dev, "could not register dai: %d\n", ret);631 goto err_suspend;632 }633 634 rockchip_pdm_rxctrl(pdm, 0);635 636 ret = rockchip_pdm_path_parse(pdm, node);637 if (ret != 0 && ret != -ENOENT)638 goto err_suspend;639 640 ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0);641 if (ret) {642 dev_err(&pdev->dev, "could not register pcm: %d\n", ret);643 goto err_suspend;644 }645 646 return 0;647 648err_suspend:649 if (!pm_runtime_status_suspended(&pdev->dev))650 rockchip_pdm_runtime_suspend(&pdev->dev);651err_pm_disable:652 pm_runtime_disable(&pdev->dev);653 654 clk_disable_unprepare(pdm->hclk);655 656 return ret;657}658 659static void rockchip_pdm_remove(struct platform_device *pdev)660{661 struct rk_pdm_dev *pdm = dev_get_drvdata(&pdev->dev);662 663 pm_runtime_disable(&pdev->dev);664 if (!pm_runtime_status_suspended(&pdev->dev))665 rockchip_pdm_runtime_suspend(&pdev->dev);666 667 clk_disable_unprepare(pdm->clk);668 clk_disable_unprepare(pdm->hclk);669}670 671#ifdef CONFIG_PM_SLEEP672static int rockchip_pdm_suspend(struct device *dev)673{674 struct rk_pdm_dev *pdm = dev_get_drvdata(dev);675 676 regcache_mark_dirty(pdm->regmap);677 678 return 0;679}680 681static int rockchip_pdm_resume(struct device *dev)682{683 struct rk_pdm_dev *pdm = dev_get_drvdata(dev);684 int ret;685 686 ret = pm_runtime_resume_and_get(dev);687 if (ret < 0)688 return ret;689 690 ret = regcache_sync(pdm->regmap);691 692 pm_runtime_put(dev);693 694 return ret;695}696#endif697 698static const struct dev_pm_ops rockchip_pdm_pm_ops = {699 SET_RUNTIME_PM_OPS(rockchip_pdm_runtime_suspend,700 rockchip_pdm_runtime_resume, NULL)701 SET_SYSTEM_SLEEP_PM_OPS(rockchip_pdm_suspend, rockchip_pdm_resume)702};703 704static struct platform_driver rockchip_pdm_driver = {705 .probe = rockchip_pdm_probe,706 .remove = rockchip_pdm_remove,707 .driver = {708 .name = "rockchip-pdm",709 .of_match_table = of_match_ptr(rockchip_pdm_match),710 .pm = &rockchip_pdm_pm_ops,711 },712};713 714module_platform_driver(rockchip_pdm_driver);715 716MODULE_AUTHOR("Sugar <sugar.zhang@rock-chips.com>");717MODULE_DESCRIPTION("Rockchip PDM Controller Driver");718MODULE_LICENSE("GPL v2");719