| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
|---|
| 1 | 2 | /* |
|---|
| 2 | 3 | * Rockchip PDM ALSA SoC Digital Audio Interface(DAI) driver |
|---|
| 3 | 4 | * |
|---|
| 4 | 5 | * Copyright (C) 2017 Fuzhou Rockchip Electronics Co., Ltd |
|---|
| 5 | | - * |
|---|
| 6 | | - * This software is licensed under the terms of the GNU General Public |
|---|
| 7 | | - * License version 2, as published by the Free Software Foundation, and |
|---|
| 8 | | - * may be copied, distributed, and modified under those terms. |
|---|
| 9 | | - * |
|---|
| 10 | | - * This program is distributed in the hope that it will be useful, |
|---|
| 11 | | - * but WITHOUT ANY WARRANTY; without even the implied warranty of |
|---|
| 12 | | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|---|
| 13 | | - * GNU General Public License for more details. |
|---|
| 14 | | - * |
|---|
| 15 | 6 | */ |
|---|
| 16 | 7 | |
|---|
| 17 | 8 | #include <linux/module.h> |
|---|
| .. | .. |
|---|
| 19 | 10 | #include <linux/clk/rockchip.h> |
|---|
| 20 | 11 | #include <linux/of.h> |
|---|
| 21 | 12 | #include <linux/of_device.h> |
|---|
| 13 | +#include <linux/pinctrl/consumer.h> |
|---|
| 22 | 14 | #include <linux/pm_runtime.h> |
|---|
| 23 | 15 | #include <linux/rational.h> |
|---|
| 24 | 16 | #include <linux/regmap.h> |
|---|
| .. | .. |
|---|
| 29 | 21 | #include "rockchip_pdm.h" |
|---|
| 30 | 22 | |
|---|
| 31 | 23 | #define PDM_DMA_BURST_SIZE (8) /* size * width: 8*4 = 32 bytes */ |
|---|
| 32 | | -#define PDM_SIGNOFF_CLK_RATE (100000000) |
|---|
| 24 | +#define PDM_SIGNOFF_CLK_100M (100000000) |
|---|
| 25 | +#define PDM_SIGNOFF_CLK_300M (300000000) |
|---|
| 33 | 26 | #define PDM_PATH_MAX (4) |
|---|
| 34 | | -#define CLK_PPM_MIN (-1000) |
|---|
| 35 | | -#define CLK_PPM_MAX (1000) |
|---|
| 36 | 27 | #define PDM_DEFAULT_RATE (48000) |
|---|
| 37 | 28 | #define PDM_START_DELAY_MS_DEFAULT (20) |
|---|
| 38 | 29 | #define PDM_START_DELAY_MS_MIN (0) |
|---|
| 39 | 30 | #define PDM_START_DELAY_MS_MAX (1000) |
|---|
| 40 | 31 | #define PDM_FILTER_DELAY_MS_MIN (20) |
|---|
| 41 | 32 | #define PDM_FILTER_DELAY_MS_MAX (1000) |
|---|
| 33 | +#define PDM_CLK_SHIFT_PPM_MAX (1000000) /* 1 ppm */ |
|---|
| 34 | +#define CLK_PPM_MIN (-1000) |
|---|
| 35 | +#define CLK_PPM_MAX (1000) |
|---|
| 36 | + |
|---|
| 37 | +#define QUIRK_ALWAYS_ON BIT(0) |
|---|
| 42 | 38 | |
|---|
| 43 | 39 | enum rk_pdm_version { |
|---|
| 40 | + RK_PDM_RK3229, |
|---|
| 44 | 41 | RK_PDM_RK3308, |
|---|
| 45 | | - RK_PDM_RK3328, |
|---|
| 42 | + RK_PDM_RK3588, |
|---|
| 46 | 43 | RK_PDM_RV1126, |
|---|
| 47 | 44 | }; |
|---|
| 48 | 45 | |
|---|
| .. | .. |
|---|
| 54 | 51 | struct regmap *regmap; |
|---|
| 55 | 52 | struct snd_dmaengine_dai_dma_data capture_dma_data; |
|---|
| 56 | 53 | struct reset_control *reset; |
|---|
| 54 | + struct pinctrl *pinctrl; |
|---|
| 55 | + struct pinctrl_state *clk_state; |
|---|
| 57 | 56 | unsigned int start_delay_ms; |
|---|
| 58 | 57 | unsigned int filter_delay_ms; |
|---|
| 59 | 58 | enum rk_pdm_version version; |
|---|
| 60 | 59 | unsigned int clk_root_rate; |
|---|
| 61 | 60 | unsigned int clk_root_initial_rate; |
|---|
| 61 | + unsigned int quirks; |
|---|
| 62 | 62 | int clk_ppm; |
|---|
| 63 | 63 | bool clk_calibrate; |
|---|
| 64 | 64 | }; |
|---|
| .. | .. |
|---|
| 100 | 100 | { 4, 8000 }, |
|---|
| 101 | 101 | }; |
|---|
| 102 | 102 | |
|---|
| 103 | +static const struct pdm_of_quirks { |
|---|
| 104 | + char *quirk; |
|---|
| 105 | + int id; |
|---|
| 106 | +} of_quirks[] = { |
|---|
| 107 | + { |
|---|
| 108 | + .quirk = "rockchip,always-on", |
|---|
| 109 | + .id = QUIRK_ALWAYS_ON, |
|---|
| 110 | + }, |
|---|
| 111 | +}; |
|---|
| 112 | + |
|---|
| 103 | 113 | static unsigned int get_pdm_clk(struct rk_pdm_dev *pdm, unsigned int sr, |
|---|
| 104 | | - unsigned int *clk_src, unsigned int *clk_out) |
|---|
| 114 | + unsigned int *clk_src, unsigned int *clk_out, |
|---|
| 115 | + unsigned int signoff) |
|---|
| 105 | 116 | { |
|---|
| 106 | | - unsigned int i, count, clk, div, rate; |
|---|
| 117 | + unsigned int i, count, clk, div, rate, delta; |
|---|
| 107 | 118 | |
|---|
| 108 | 119 | clk = 0; |
|---|
| 109 | 120 | if (!sr) |
|---|
| .. | .. |
|---|
| 122 | 133 | break; |
|---|
| 123 | 134 | } |
|---|
| 124 | 135 | rate = clk_round_rate(pdm->clk, clkref[i].clk); |
|---|
| 125 | | - if (rate != clkref[i].clk) |
|---|
| 136 | + delta = clkref[i].clk / PDM_CLK_SHIFT_PPM_MAX; |
|---|
| 137 | + if (rate < clkref[i].clk - delta || |
|---|
| 138 | + rate > clkref[i].clk + delta) |
|---|
| 126 | 139 | continue; |
|---|
| 127 | 140 | clk = clkref[i].clk; |
|---|
| 128 | 141 | *clk_src = clkref[i].clk; |
|---|
| .. | .. |
|---|
| 131 | 144 | } |
|---|
| 132 | 145 | |
|---|
| 133 | 146 | if (!clk) { |
|---|
| 134 | | - clk = clk_round_rate(pdm->clk, PDM_SIGNOFF_CLK_RATE); |
|---|
| 147 | + clk = clk_round_rate(pdm->clk, signoff); |
|---|
| 135 | 148 | *clk_src = clk; |
|---|
| 136 | 149 | } |
|---|
| 137 | 150 | return clk; |
|---|
| .. | .. |
|---|
| 207 | 220 | return snd_soc_dai_get_drvdata(dai); |
|---|
| 208 | 221 | } |
|---|
| 209 | 222 | |
|---|
| 210 | | -static void rockchip_pdm_drop_fifo(struct rk_pdm_dev *pdm) |
|---|
| 211 | | -{ |
|---|
| 212 | | - int cnt, val, i; |
|---|
| 213 | | - |
|---|
| 214 | | - /* drop the dirty data */ |
|---|
| 215 | | - regmap_read(pdm->regmap, PDM_FIFO_CTRL, &cnt); |
|---|
| 216 | | - for (i = 0; i < PDM_FIFO_CNT(cnt); i++) |
|---|
| 217 | | - regmap_read(pdm->regmap, PDM_RXFIFO_DATA, &val); |
|---|
| 218 | | -} |
|---|
| 219 | | - |
|---|
| 220 | 223 | static void rockchip_pdm_rxctrl(struct rk_pdm_dev *pdm, int on) |
|---|
| 221 | 224 | { |
|---|
| 225 | + unsigned long flags; |
|---|
| 226 | + |
|---|
| 222 | 227 | if (on) { |
|---|
| 223 | | - rockchip_pdm_drop_fifo(pdm); |
|---|
| 228 | + /* The PDM device need to delete some unused data |
|---|
| 229 | + * since the pdm of various manufacturers can not |
|---|
| 230 | + * be stable quickly. This is done by commit "ASoC: |
|---|
| 231 | + * rockchip: pdm: Fix pop noise in the beginning". |
|---|
| 232 | + * |
|---|
| 233 | + * But we do not know how many data we delete, this |
|---|
| 234 | + * cause channel disorder. For example, we record |
|---|
| 235 | + * two channel 24-bit sound, then delete some starting |
|---|
| 236 | + * data. Because the deleted starting data is uncertain, |
|---|
| 237 | + * the next data may be left or right channel and cause |
|---|
| 238 | + * channel disorder. |
|---|
| 239 | + * |
|---|
| 240 | + * Luckily, we can use the PDM_RX_CLR to fix this. |
|---|
| 241 | + * Use the PDM_RX_CLR to clear fifo written data and |
|---|
| 242 | + * address, but can not clear the read data and address. |
|---|
| 243 | + * In initial state, the read data and address are zero. |
|---|
| 244 | + */ |
|---|
| 245 | + local_irq_save(flags); |
|---|
| 246 | + regmap_update_bits(pdm->regmap, PDM_SYSCONFIG, |
|---|
| 247 | + PDM_RX_CLR_MASK, |
|---|
| 248 | + PDM_RX_CLR_WR); |
|---|
| 224 | 249 | regmap_update_bits(pdm->regmap, PDM_DMA_CTRL, |
|---|
| 225 | 250 | PDM_DMA_RD_MSK, PDM_DMA_RD_EN); |
|---|
| 251 | + local_irq_restore(flags); |
|---|
| 226 | 252 | } else { |
|---|
| 227 | 253 | regmap_update_bits(pdm->regmap, PDM_DMA_CTRL, |
|---|
| 228 | 254 | PDM_DMA_RD_MSK, PDM_DMA_RD_DIS); |
|---|
| .. | .. |
|---|
| 264 | 290 | return ret; |
|---|
| 265 | 291 | } |
|---|
| 266 | 292 | |
|---|
| 267 | | -static int rockchip_pdm_set_samplerate(struct rk_pdm_dev *pdm, |
|---|
| 268 | | - unsigned int samplerate) |
|---|
| 293 | +static int rockchip_pdm_set_samplerate(struct rk_pdm_dev *pdm, unsigned int samplerate) |
|---|
| 269 | 294 | { |
|---|
| 270 | | - unsigned int clk_rate, clk_div; |
|---|
| 271 | | - unsigned int clk_src, clk_out = 0; |
|---|
| 272 | | - unsigned int val = 0, div = 0, rate, delta; |
|---|
| 295 | + |
|---|
| 296 | + unsigned int val = 0, div = 0; |
|---|
| 297 | + unsigned int clk_rate, clk_div, rate, delta; |
|---|
| 298 | + unsigned int clk_src = 0, clk_out = 0, signoff = PDM_SIGNOFF_CLK_100M; |
|---|
| 273 | 299 | unsigned long m, n; |
|---|
| 274 | 300 | uint64_t ppm; |
|---|
| 275 | | - int ret; |
|---|
| 276 | 301 | bool change; |
|---|
| 302 | + int ret; |
|---|
| 277 | 303 | |
|---|
| 278 | | - clk_rate = get_pdm_clk(pdm, samplerate, &clk_src, &clk_out); |
|---|
| 304 | + if (pdm->version == RK_PDM_RK3588) |
|---|
| 305 | + signoff = PDM_SIGNOFF_CLK_300M; |
|---|
| 306 | + clk_rate = get_pdm_clk(pdm, samplerate, &clk_src, &clk_out, signoff); |
|---|
| 279 | 307 | if (!clk_rate) |
|---|
| 280 | 308 | return -EINVAL; |
|---|
| 281 | 309 | |
|---|
| .. | .. |
|---|
| 312 | 340 | return ret; |
|---|
| 313 | 341 | |
|---|
| 314 | 342 | if (pdm->version == RK_PDM_RK3308 || |
|---|
| 343 | + pdm->version == RK_PDM_RK3588 || |
|---|
| 315 | 344 | pdm->version == RK_PDM_RV1126) { |
|---|
| 316 | 345 | rational_best_approximation(clk_out, clk_src, |
|---|
| 317 | 346 | GENMASK(16 - 1, 0), |
|---|
| .. | .. |
|---|
| 341 | 370 | val); |
|---|
| 342 | 371 | } |
|---|
| 343 | 372 | |
|---|
| 344 | | - if (pdm->version == RK_PDM_RV1126) { |
|---|
| 373 | + if (pdm->version == RK_PDM_RK3588 || pdm->version == RK_PDM_RV1126) { |
|---|
| 345 | 374 | val = get_pdm_cic_ratio(clk_out); |
|---|
| 346 | 375 | regmap_update_bits(pdm->regmap, PDM_CLK_CTRL, PDM_CIC_RATIO_MSK, val); |
|---|
| 347 | 376 | val = samplerate_to_bit(samplerate); |
|---|
| .. | .. |
|---|
| 351 | 380 | val = get_pdm_ds_ratio(samplerate); |
|---|
| 352 | 381 | regmap_update_bits(pdm->regmap, PDM_CLK_CTRL, PDM_DS_RATIO_MSK, val); |
|---|
| 353 | 382 | } |
|---|
| 354 | | - regmap_update_bits(pdm->regmap, PDM_HPF_CTRL, PDM_HPF_CF_MSK, PDM_HPF_60HZ); |
|---|
| 383 | + |
|---|
| 384 | + regmap_update_bits(pdm->regmap, PDM_HPF_CTRL, |
|---|
| 385 | + PDM_HPF_CF_MSK, PDM_HPF_60HZ); |
|---|
| 355 | 386 | regmap_update_bits(pdm->regmap, PDM_HPF_CTRL, |
|---|
| 356 | 387 | PDM_HPF_LE | PDM_HPF_RE, PDM_HPF_LE | PDM_HPF_RE); |
|---|
| 357 | | - |
|---|
| 358 | 388 | return 0; |
|---|
| 359 | 389 | } |
|---|
| 360 | 390 | |
|---|
| .. | .. |
|---|
| 370 | 400 | |
|---|
| 371 | 401 | rockchip_pdm_set_samplerate(pdm, params_rate(params)); |
|---|
| 372 | 402 | |
|---|
| 373 | | - if (pdm->version != RK_PDM_RK3328) |
|---|
| 403 | + if (pdm->version != RK_PDM_RK3229) |
|---|
| 374 | 404 | regmap_update_bits(pdm->regmap, PDM_CTRL0, |
|---|
| 375 | 405 | PDM_MODE_MSK, PDM_MODE_LJ); |
|---|
| 376 | 406 | |
|---|
| .. | .. |
|---|
| 398 | 428 | switch (params_channels(params)) { |
|---|
| 399 | 429 | case 8: |
|---|
| 400 | 430 | val |= PDM_PATH3_EN; |
|---|
| 401 | | - /* fallthrough */ |
|---|
| 431 | + fallthrough; |
|---|
| 402 | 432 | case 6: |
|---|
| 403 | 433 | val |= PDM_PATH2_EN; |
|---|
| 404 | | - /* fallthrough */ |
|---|
| 434 | + fallthrough; |
|---|
| 405 | 435 | case 4: |
|---|
| 406 | 436 | val |= PDM_PATH1_EN; |
|---|
| 407 | | - /* fallthrough */ |
|---|
| 437 | + fallthrough; |
|---|
| 408 | 438 | case 2: |
|---|
| 409 | 439 | val |= PDM_PATH0_EN; |
|---|
| 410 | 440 | break; |
|---|
| .. | .. |
|---|
| 476 | 506 | return ret; |
|---|
| 477 | 507 | } |
|---|
| 478 | 508 | |
|---|
| 479 | | -static int rockchip_pdm_clk_compensation_info(struct snd_kcontrol *kcontrol, |
|---|
| 480 | | - struct snd_ctl_elem_info *uinfo) |
|---|
| 481 | | -{ |
|---|
| 482 | | - uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; |
|---|
| 483 | | - uinfo->count = 1; |
|---|
| 484 | | - uinfo->value.integer.min = CLK_PPM_MIN; |
|---|
| 485 | | - uinfo->value.integer.max = CLK_PPM_MAX; |
|---|
| 486 | | - uinfo->value.integer.step = 1; |
|---|
| 487 | | - |
|---|
| 488 | | - return 0; |
|---|
| 489 | | -} |
|---|
| 490 | | - |
|---|
| 491 | | -static int rockchip_pdm_clk_compensation_get(struct snd_kcontrol *kcontrol, |
|---|
| 492 | | - struct snd_ctl_elem_value *ucontrol) |
|---|
| 493 | | -{ |
|---|
| 494 | | - struct snd_soc_dai *dai = snd_kcontrol_chip(kcontrol); |
|---|
| 495 | | - struct rk_pdm_dev *pdm = snd_soc_dai_get_drvdata(dai); |
|---|
| 496 | | - |
|---|
| 497 | | - ucontrol->value.integer.value[0] = pdm->clk_ppm; |
|---|
| 498 | | - |
|---|
| 499 | | - return 0; |
|---|
| 500 | | -} |
|---|
| 501 | | - |
|---|
| 502 | | -static int rockchip_pdm_clk_compensation_put(struct snd_kcontrol *kcontrol, |
|---|
| 503 | | - struct snd_ctl_elem_value *ucontrol) |
|---|
| 504 | | -{ |
|---|
| 505 | | - struct snd_soc_dai *dai = snd_kcontrol_chip(kcontrol); |
|---|
| 506 | | - struct rk_pdm_dev *pdm = snd_soc_dai_get_drvdata(dai); |
|---|
| 507 | | - int ppm = ucontrol->value.integer.value[0]; |
|---|
| 508 | | - |
|---|
| 509 | | - if ((ucontrol->value.integer.value[0] < CLK_PPM_MIN) || |
|---|
| 510 | | - (ucontrol->value.integer.value[0] > CLK_PPM_MAX)) |
|---|
| 511 | | - return -EINVAL; |
|---|
| 512 | | - |
|---|
| 513 | | - return rockchip_pdm_clk_set_rate(pdm, pdm->clk_root, pdm->clk_root_rate, ppm); |
|---|
| 514 | | -} |
|---|
| 515 | | - |
|---|
| 516 | | -static struct snd_kcontrol_new rockchip_pdm_compensation_control = { |
|---|
| 517 | | - .iface = SNDRV_CTL_ELEM_IFACE_PCM, |
|---|
| 518 | | - .name = "PCM Clk Compensation In PPM", |
|---|
| 519 | | - .info = rockchip_pdm_clk_compensation_info, |
|---|
| 520 | | - .get = rockchip_pdm_clk_compensation_get, |
|---|
| 521 | | - .put = rockchip_pdm_clk_compensation_put, |
|---|
| 522 | | -}; |
|---|
| 523 | | - |
|---|
| 524 | 509 | static int rockchip_pdm_start_delay_info(struct snd_kcontrol *kcontrol, |
|---|
| 525 | 510 | struct snd_ctl_elem_info *uinfo) |
|---|
| 526 | 511 | { |
|---|
| .. | .. |
|---|
| 531 | 516 | uinfo->value.integer.step = 1; |
|---|
| 532 | 517 | |
|---|
| 533 | 518 | return 0; |
|---|
| 534 | | - |
|---|
| 535 | 519 | } |
|---|
| 536 | 520 | |
|---|
| 537 | 521 | static int rockchip_pdm_start_delay_get(struct snd_kcontrol *kcontrol, |
|---|
| 538 | 522 | struct snd_ctl_elem_value *ucontrol) |
|---|
| 539 | 523 | { |
|---|
| 540 | | - struct snd_soc_dai *dai = snd_kcontrol_chip(kcontrol); |
|---|
| 541 | | - struct rk_pdm_dev *pdm = snd_soc_dai_get_drvdata(dai); |
|---|
| 524 | + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); |
|---|
| 525 | + struct rk_pdm_dev *pdm = snd_soc_component_get_drvdata(component); |
|---|
| 542 | 526 | |
|---|
| 543 | 527 | ucontrol->value.integer.value[0] = pdm->start_delay_ms; |
|---|
| 544 | 528 | |
|---|
| .. | .. |
|---|
| 548 | 532 | static int rockchip_pdm_start_delay_put(struct snd_kcontrol *kcontrol, |
|---|
| 549 | 533 | struct snd_ctl_elem_value *ucontrol) |
|---|
| 550 | 534 | { |
|---|
| 551 | | - struct snd_soc_dai *dai = snd_kcontrol_chip(kcontrol); |
|---|
| 552 | | - struct rk_pdm_dev *pdm = snd_soc_dai_get_drvdata(dai); |
|---|
| 535 | + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); |
|---|
| 536 | + struct rk_pdm_dev *pdm = snd_soc_component_get_drvdata(component); |
|---|
| 553 | 537 | |
|---|
| 554 | 538 | if ((ucontrol->value.integer.value[0] < PDM_START_DELAY_MS_MIN) || |
|---|
| 555 | 539 | (ucontrol->value.integer.value[0] > PDM_START_DELAY_MS_MAX)) |
|---|
| .. | .. |
|---|
| 575 | 559 | static int rockchip_pdm_filter_delay_get(struct snd_kcontrol *kcontrol, |
|---|
| 576 | 560 | struct snd_ctl_elem_value *ucontrol) |
|---|
| 577 | 561 | { |
|---|
| 578 | | - struct snd_soc_dai *dai = snd_kcontrol_chip(kcontrol); |
|---|
| 579 | | - struct rk_pdm_dev *pdm = snd_soc_dai_get_drvdata(dai); |
|---|
| 562 | + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); |
|---|
| 563 | + struct rk_pdm_dev *pdm = snd_soc_component_get_drvdata(component); |
|---|
| 580 | 564 | |
|---|
| 581 | 565 | ucontrol->value.integer.value[0] = pdm->filter_delay_ms; |
|---|
| 582 | 566 | |
|---|
| .. | .. |
|---|
| 586 | 570 | static int rockchip_pdm_filter_delay_put(struct snd_kcontrol *kcontrol, |
|---|
| 587 | 571 | struct snd_ctl_elem_value *ucontrol) |
|---|
| 588 | 572 | { |
|---|
| 589 | | - struct snd_soc_dai *dai = snd_kcontrol_chip(kcontrol); |
|---|
| 590 | | - struct rk_pdm_dev *pdm = snd_soc_dai_get_drvdata(dai); |
|---|
| 573 | + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); |
|---|
| 574 | + struct rk_pdm_dev *pdm = snd_soc_component_get_drvdata(component); |
|---|
| 591 | 575 | |
|---|
| 592 | 576 | if ((ucontrol->value.integer.value[0] < PDM_FILTER_DELAY_MS_MIN) || |
|---|
| 593 | 577 | (ucontrol->value.integer.value[0] > PDM_FILTER_DELAY_MS_MAX)) |
|---|
| .. | .. |
|---|
| 598 | 582 | return 1; |
|---|
| 599 | 583 | } |
|---|
| 600 | 584 | |
|---|
| 585 | +static const char * const rpaths_text[] = { |
|---|
| 586 | + "From SDI0", "From SDI1", "From SDI2", "From SDI3" }; |
|---|
| 587 | + |
|---|
| 588 | +static SOC_ENUM_SINGLE_DECL(rpath3_enum, PDM_CLK_CTRL, 14, rpaths_text); |
|---|
| 589 | +static SOC_ENUM_SINGLE_DECL(rpath2_enum, PDM_CLK_CTRL, 12, rpaths_text); |
|---|
| 590 | +static SOC_ENUM_SINGLE_DECL(rpath1_enum, PDM_CLK_CTRL, 10, rpaths_text); |
|---|
| 591 | +static SOC_ENUM_SINGLE_DECL(rpath0_enum, PDM_CLK_CTRL, 8, rpaths_text); |
|---|
| 592 | + |
|---|
| 593 | +static const char * const hpf_cutoff_text[] = { |
|---|
| 594 | + "3.79Hz", "60Hz", "243Hz", "493Hz", |
|---|
| 595 | +}; |
|---|
| 596 | + |
|---|
| 597 | +static SOC_ENUM_SINGLE_DECL(hpf_cutoff_enum, PDM_HPF_CTRL, |
|---|
| 598 | + 0, hpf_cutoff_text); |
|---|
| 599 | + |
|---|
| 601 | 600 | static const struct snd_kcontrol_new rockchip_pdm_controls[] = { |
|---|
| 601 | + SOC_ENUM("Receive PATH3 Source Select", rpath3_enum), |
|---|
| 602 | + SOC_ENUM("Receive PATH2 Source Select", rpath2_enum), |
|---|
| 603 | + SOC_ENUM("Receive PATH1 Source Select", rpath1_enum), |
|---|
| 604 | + SOC_ENUM("Receive PATH0 Source Select", rpath0_enum), |
|---|
| 605 | + |
|---|
| 606 | + SOC_ENUM("HPF Cutoff", hpf_cutoff_enum), |
|---|
| 607 | + SOC_SINGLE("HPFL Switch", PDM_HPF_CTRL, 3, 1, 0), |
|---|
| 608 | + SOC_SINGLE("HPFR Switch", PDM_HPF_CTRL, 2, 1, 0), |
|---|
| 609 | + |
|---|
| 602 | 610 | { |
|---|
| 603 | 611 | .iface = SNDRV_CTL_ELEM_IFACE_PCM, |
|---|
| 604 | 612 | .name = "PDM Start Delay Ms", |
|---|
| .. | .. |
|---|
| 615 | 623 | }, |
|---|
| 616 | 624 | }; |
|---|
| 617 | 625 | |
|---|
| 626 | +static int rockchip_pdm_clk_compensation_info(struct snd_kcontrol *kcontrol, |
|---|
| 627 | + struct snd_ctl_elem_info *uinfo) |
|---|
| 628 | +{ |
|---|
| 629 | + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; |
|---|
| 630 | + uinfo->count = 1; |
|---|
| 631 | + uinfo->value.integer.min = CLK_PPM_MIN; |
|---|
| 632 | + uinfo->value.integer.max = CLK_PPM_MAX; |
|---|
| 633 | + uinfo->value.integer.step = 1; |
|---|
| 634 | + |
|---|
| 635 | + return 0; |
|---|
| 636 | +} |
|---|
| 637 | + |
|---|
| 638 | + |
|---|
| 639 | +static int rockchip_pdm_clk_compensation_get(struct snd_kcontrol *kcontrol, |
|---|
| 640 | + struct snd_ctl_elem_value *ucontrol) |
|---|
| 641 | + |
|---|
| 642 | +{ |
|---|
| 643 | + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); |
|---|
| 644 | + struct rk_pdm_dev *pdm = snd_soc_component_get_drvdata(component); |
|---|
| 645 | + |
|---|
| 646 | + ucontrol->value.integer.value[0] = pdm->clk_ppm; |
|---|
| 647 | + |
|---|
| 648 | + return 0; |
|---|
| 649 | +} |
|---|
| 650 | + |
|---|
| 651 | +static int rockchip_pdm_clk_compensation_put(struct snd_kcontrol *kcontrol, |
|---|
| 652 | + struct snd_ctl_elem_value *ucontrol) |
|---|
| 653 | +{ |
|---|
| 654 | + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); |
|---|
| 655 | + struct rk_pdm_dev *pdm = snd_soc_component_get_drvdata(component); |
|---|
| 656 | + |
|---|
| 657 | + int ppm = ucontrol->value.integer.value[0]; |
|---|
| 658 | + |
|---|
| 659 | + if ((ucontrol->value.integer.value[0] < CLK_PPM_MIN) || |
|---|
| 660 | + (ucontrol->value.integer.value[0] > CLK_PPM_MAX)) |
|---|
| 661 | + return -EINVAL; |
|---|
| 662 | + |
|---|
| 663 | + return rockchip_pdm_clk_set_rate(pdm, pdm->clk_root, pdm->clk_root_rate, ppm); |
|---|
| 664 | +} |
|---|
| 665 | + |
|---|
| 666 | +static struct snd_kcontrol_new rockchip_pdm_compensation_control = { |
|---|
| 667 | + .iface = SNDRV_CTL_ELEM_IFACE_PCM, |
|---|
| 668 | + .name = "PDM PCM Clk Compensation In PPM", |
|---|
| 669 | + .info = rockchip_pdm_clk_compensation_info, |
|---|
| 670 | + .get = rockchip_pdm_clk_compensation_get, |
|---|
| 671 | + .put = rockchip_pdm_clk_compensation_put, |
|---|
| 672 | + |
|---|
| 673 | +}; |
|---|
| 674 | + |
|---|
| 618 | 675 | static int rockchip_pdm_dai_probe(struct snd_soc_dai *dai) |
|---|
| 619 | 676 | { |
|---|
| 620 | 677 | struct rk_pdm_dev *pdm = to_info(dai); |
|---|
| .. | .. |
|---|
| 622 | 679 | dai->capture_dma_data = &pdm->capture_dma_data; |
|---|
| 623 | 680 | |
|---|
| 624 | 681 | if (pdm->clk_calibrate) |
|---|
| 625 | | - snd_soc_add_dai_controls(dai, &rockchip_pdm_compensation_control, 1); |
|---|
| 626 | | - snd_soc_add_dai_controls(dai, rockchip_pdm_controls, |
|---|
| 627 | | - ARRAY_SIZE(rockchip_pdm_controls)); |
|---|
| 682 | + snd_soc_add_component_controls(dai->component, |
|---|
| 683 | + &rockchip_pdm_compensation_control, |
|---|
| 684 | + 1); |
|---|
| 685 | + |
|---|
| 628 | 686 | return 0; |
|---|
| 629 | 687 | } |
|---|
| 630 | 688 | |
|---|
| .. | .. |
|---|
| 705 | 763 | |
|---|
| 706 | 764 | static const struct snd_soc_component_driver rockchip_pdm_component = { |
|---|
| 707 | 765 | .name = "rockchip-pdm", |
|---|
| 766 | + .controls = rockchip_pdm_controls, |
|---|
| 767 | + .num_controls = ARRAY_SIZE(rockchip_pdm_controls), |
|---|
| 708 | 768 | }; |
|---|
| 769 | + |
|---|
| 770 | +static int rockchip_pdm_pinctrl_select_clk_state(struct device *dev) |
|---|
| 771 | +{ |
|---|
| 772 | + struct rk_pdm_dev *pdm = dev_get_drvdata(dev); |
|---|
| 773 | + |
|---|
| 774 | + if (IS_ERR_OR_NULL(pdm->pinctrl) || !pdm->clk_state) |
|---|
| 775 | + return 0; |
|---|
| 776 | + |
|---|
| 777 | + /* |
|---|
| 778 | + * A necessary delay to make sure the correct |
|---|
| 779 | + * frac div has been applied when resume from |
|---|
| 780 | + * power down. |
|---|
| 781 | + */ |
|---|
| 782 | + udelay(10); |
|---|
| 783 | + |
|---|
| 784 | + /* |
|---|
| 785 | + * Must disable the clk to avoid clk glitch |
|---|
| 786 | + * when pinctrl switch from gpio to pdm clk. |
|---|
| 787 | + */ |
|---|
| 788 | + clk_disable_unprepare(pdm->clk); |
|---|
| 789 | + pinctrl_select_state(pdm->pinctrl, pdm->clk_state); |
|---|
| 790 | + clk_prepare_enable(pdm->clk); |
|---|
| 791 | + |
|---|
| 792 | + return 0; |
|---|
| 793 | +} |
|---|
| 709 | 794 | |
|---|
| 710 | 795 | static int rockchip_pdm_runtime_suspend(struct device *dev) |
|---|
| 711 | 796 | { |
|---|
| .. | .. |
|---|
| 714 | 799 | regcache_cache_only(pdm->regmap, true); |
|---|
| 715 | 800 | clk_disable_unprepare(pdm->clk); |
|---|
| 716 | 801 | clk_disable_unprepare(pdm->hclk); |
|---|
| 802 | + |
|---|
| 803 | + pinctrl_pm_select_idle_state(dev); |
|---|
| 717 | 804 | |
|---|
| 718 | 805 | return 0; |
|---|
| 719 | 806 | } |
|---|
| .. | .. |
|---|
| 724 | 811 | int ret; |
|---|
| 725 | 812 | |
|---|
| 726 | 813 | ret = clk_prepare_enable(pdm->clk); |
|---|
| 727 | | - if (ret) { |
|---|
| 728 | | - dev_err(pdm->dev, "clock enable failed %d\n", ret); |
|---|
| 729 | | - return ret; |
|---|
| 730 | | - } |
|---|
| 814 | + if (ret) |
|---|
| 815 | + goto err_clk; |
|---|
| 731 | 816 | |
|---|
| 732 | 817 | ret = clk_prepare_enable(pdm->hclk); |
|---|
| 733 | | - if (ret) { |
|---|
| 734 | | - dev_err(pdm->dev, "hclock enable failed %d\n", ret); |
|---|
| 735 | | - return ret; |
|---|
| 736 | | - } |
|---|
| 818 | + if (ret) |
|---|
| 819 | + goto err_hclk; |
|---|
| 737 | 820 | |
|---|
| 738 | 821 | regcache_cache_only(pdm->regmap, false); |
|---|
| 739 | 822 | regcache_mark_dirty(pdm->regmap); |
|---|
| 740 | 823 | ret = regcache_sync(pdm->regmap); |
|---|
| 741 | | - if (ret) { |
|---|
| 742 | | - clk_disable_unprepare(pdm->clk); |
|---|
| 743 | | - clk_disable_unprepare(pdm->hclk); |
|---|
| 744 | | - } |
|---|
| 824 | + if (ret) |
|---|
| 825 | + goto err_regmap; |
|---|
| 826 | + |
|---|
| 827 | + rockchip_pdm_rxctrl(pdm, 0); |
|---|
| 828 | + |
|---|
| 829 | + rockchip_pdm_pinctrl_select_clk_state(dev); |
|---|
| 830 | + |
|---|
| 745 | 831 | return 0; |
|---|
| 832 | + |
|---|
| 833 | +err_regmap: |
|---|
| 834 | + clk_disable_unprepare(pdm->hclk); |
|---|
| 835 | +err_hclk: |
|---|
| 836 | + clk_disable_unprepare(pdm->clk); |
|---|
| 837 | +err_clk: |
|---|
| 838 | + return ret; |
|---|
| 746 | 839 | } |
|---|
| 747 | 840 | |
|---|
| 748 | 841 | static bool rockchip_pdm_wr_reg(struct device *dev, unsigned int reg) |
|---|
| .. | .. |
|---|
| 831 | 924 | .cache_type = REGCACHE_FLAT, |
|---|
| 832 | 925 | }; |
|---|
| 833 | 926 | |
|---|
| 834 | | -static const struct of_device_id rockchip_pdm_match[] = { |
|---|
| 835 | | -#ifdef CONFIG_CPU_PX30 |
|---|
| 927 | +static const struct of_device_id rockchip_pdm_match[] __maybe_unused = { |
|---|
| 928 | + { .compatible = "rockchip,pdm", |
|---|
| 929 | + .data = (void *)RK_PDM_RK3229 }, |
|---|
| 836 | 930 | { .compatible = "rockchip,px30-pdm", |
|---|
| 837 | 931 | .data = (void *)RK_PDM_RK3308 }, |
|---|
| 838 | | -#endif |
|---|
| 839 | | -#ifdef CONFIG_CPU_RK1808 |
|---|
| 840 | 932 | { .compatible = "rockchip,rk1808-pdm", |
|---|
| 841 | 933 | .data = (void *)RK_PDM_RK3308 }, |
|---|
| 842 | | -#endif |
|---|
| 843 | | -#ifdef CONFIG_CPU_RK3308 |
|---|
| 844 | 934 | { .compatible = "rockchip,rk3308-pdm", |
|---|
| 845 | 935 | .data = (void *)RK_PDM_RK3308 }, |
|---|
| 846 | | -#endif |
|---|
| 847 | | -#ifdef CONFIG_CPU_RK3328 |
|---|
| 848 | | - { .compatible = "rockchip,rk3328-pdm", |
|---|
| 849 | | - .data = (void *)RK_PDM_RK3328 }, |
|---|
| 850 | | -#endif |
|---|
| 851 | | -#ifdef CONFIG_CPU_RK3568 |
|---|
| 852 | 936 | { .compatible = "rockchip,rk3568-pdm", |
|---|
| 853 | 937 | .data = (void *)RK_PDM_RV1126 }, |
|---|
| 854 | | -#endif |
|---|
| 855 | | -#ifdef CONFIG_CPU_RV1126 |
|---|
| 938 | + { .compatible = "rockchip,rk3588-pdm", |
|---|
| 939 | + .data = (void *)RK_PDM_RK3588 }, |
|---|
| 856 | 940 | { .compatible = "rockchip,rv1126-pdm", |
|---|
| 857 | 941 | .data = (void *)RK_PDM_RV1126 }, |
|---|
| 858 | | -#endif |
|---|
| 859 | 942 | {}, |
|---|
| 860 | 943 | }; |
|---|
| 861 | 944 | MODULE_DEVICE_TABLE(of, rockchip_pdm_match); |
|---|
| .. | .. |
|---|
| 887 | 970 | return 0; |
|---|
| 888 | 971 | } |
|---|
| 889 | 972 | |
|---|
| 973 | +static int rockchip_pdm_keep_clk_always_on(struct rk_pdm_dev *pdm) |
|---|
| 974 | +{ |
|---|
| 975 | + pm_runtime_forbid(pdm->dev); |
|---|
| 976 | + |
|---|
| 977 | + dev_info(pdm->dev, "CLK-ALWAYS-ON: samplerate: %d\n", PDM_DEFAULT_RATE); |
|---|
| 978 | + |
|---|
| 979 | + return 0; |
|---|
| 980 | +} |
|---|
| 981 | + |
|---|
| 982 | +static int rockchip_pdm_parse_quirks(struct rk_pdm_dev *pdm) |
|---|
| 983 | +{ |
|---|
| 984 | + int ret = 0, i = 0; |
|---|
| 985 | + |
|---|
| 986 | + for (i = 0; i < ARRAY_SIZE(of_quirks); i++) |
|---|
| 987 | + if (device_property_read_bool(pdm->dev, of_quirks[i].quirk)) |
|---|
| 988 | + pdm->quirks |= of_quirks[i].id; |
|---|
| 989 | + |
|---|
| 990 | + if (pdm->quirks & QUIRK_ALWAYS_ON) |
|---|
| 991 | + ret = rockchip_pdm_keep_clk_always_on(pdm); |
|---|
| 992 | + |
|---|
| 993 | + return ret; |
|---|
| 994 | +} |
|---|
| 995 | + |
|---|
| 890 | 996 | static int rockchip_pdm_probe(struct platform_device *pdev) |
|---|
| 891 | 997 | { |
|---|
| 892 | 998 | struct device_node *node = pdev->dev.of_node; |
|---|
| .. | .. |
|---|
| 910 | 1016 | return PTR_ERR(pdm->reset); |
|---|
| 911 | 1017 | } |
|---|
| 912 | 1018 | |
|---|
| 913 | | - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
|---|
| 914 | | - regs = devm_ioremap_resource(&pdev->dev, res); |
|---|
| 1019 | + regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res); |
|---|
| 915 | 1020 | if (IS_ERR(regs)) |
|---|
| 916 | 1021 | return PTR_ERR(regs); |
|---|
| 917 | 1022 | |
|---|
| .. | .. |
|---|
| 927 | 1032 | pdm->dev = &pdev->dev; |
|---|
| 928 | 1033 | dev_set_drvdata(&pdev->dev, pdm); |
|---|
| 929 | 1034 | |
|---|
| 1035 | + pdm->pinctrl = devm_pinctrl_get(&pdev->dev); |
|---|
| 1036 | + if (!IS_ERR_OR_NULL(pdm->pinctrl)) { |
|---|
| 1037 | + pdm->clk_state = pinctrl_lookup_state(pdm->pinctrl, "clk"); |
|---|
| 1038 | + if (IS_ERR(pdm->clk_state)) { |
|---|
| 1039 | + pdm->clk_state = NULL; |
|---|
| 1040 | + dev_dbg(pdm->dev, "Have no clk pinctrl state\n"); |
|---|
| 1041 | + } |
|---|
| 1042 | + } |
|---|
| 1043 | + |
|---|
| 1044 | + pdm->start_delay_ms = PDM_START_DELAY_MS_DEFAULT; |
|---|
| 1045 | + pdm->filter_delay_ms = PDM_FILTER_DELAY_MS_MIN; |
|---|
| 1046 | + |
|---|
| 930 | 1047 | pdm->clk_calibrate = |
|---|
| 931 | 1048 | of_property_read_bool(node, "rockchip,mclk-calibrate"); |
|---|
| 932 | 1049 | if (pdm->clk_calibrate) { |
|---|
| .. | .. |
|---|
| 937 | 1054 | pdm->clk_root_initial_rate = clk_get_rate(pdm->clk_root); |
|---|
| 938 | 1055 | pdm->clk_root_rate = pdm->clk_root_initial_rate; |
|---|
| 939 | 1056 | } |
|---|
| 940 | | - |
|---|
| 941 | | - pdm->start_delay_ms = PDM_START_DELAY_MS_DEFAULT; |
|---|
| 942 | | - pdm->filter_delay_ms = PDM_FILTER_DELAY_MS_MIN; |
|---|
| 943 | 1057 | |
|---|
| 944 | 1058 | pdm->clk = devm_clk_get(&pdev->dev, "pdm_clk"); |
|---|
| 945 | 1059 | if (IS_ERR(pdm->clk)) |
|---|
| .. | .. |
|---|
| 953 | 1067 | if (ret) |
|---|
| 954 | 1068 | return ret; |
|---|
| 955 | 1069 | |
|---|
| 1070 | + rockchip_pdm_set_samplerate(pdm, PDM_DEFAULT_RATE); |
|---|
| 1071 | + rockchip_pdm_rxctrl(pdm, 0); |
|---|
| 1072 | + |
|---|
| 1073 | + ret = rockchip_pdm_path_parse(pdm, node); |
|---|
| 1074 | + if (ret != 0 && ret != -ENOENT) |
|---|
| 1075 | + goto err_clk; |
|---|
| 1076 | + |
|---|
| 1077 | + ret = rockchip_pdm_parse_quirks(pdm); |
|---|
| 1078 | + if (ret) |
|---|
| 1079 | + goto err_clk; |
|---|
| 1080 | + |
|---|
| 1081 | + /* |
|---|
| 1082 | + * MUST: after pm_runtime_enable step, any register R/W |
|---|
| 1083 | + * should be wrapped with pm_runtime_get_sync/put. |
|---|
| 1084 | + * |
|---|
| 1085 | + * Another approach is to enable the regcache true to |
|---|
| 1086 | + * avoid access HW registers. |
|---|
| 1087 | + * |
|---|
| 1088 | + * Alternatively, performing the registers R/W before |
|---|
| 1089 | + * pm_runtime_enable is also a good option. |
|---|
| 1090 | + */ |
|---|
| 956 | 1091 | pm_runtime_enable(&pdev->dev); |
|---|
| 957 | 1092 | if (!pm_runtime_enabled(&pdev->dev)) { |
|---|
| 958 | 1093 | ret = rockchip_pdm_runtime_resume(&pdev->dev); |
|---|
| .. | .. |
|---|
| 969 | 1104 | goto err_suspend; |
|---|
| 970 | 1105 | } |
|---|
| 971 | 1106 | |
|---|
| 972 | | - rockchip_pdm_set_samplerate(pdm, PDM_DEFAULT_RATE); |
|---|
| 973 | | - rockchip_pdm_rxctrl(pdm, 0); |
|---|
| 974 | | - |
|---|
| 975 | | - ret = rockchip_pdm_path_parse(pdm, node); |
|---|
| 976 | | - if (ret != 0 && ret != -ENOENT) |
|---|
| 977 | | - goto err_suspend; |
|---|
| 978 | | - |
|---|
| 979 | | - if (of_property_read_bool(node, "rockchip,no-dmaengine")) |
|---|
| 1107 | + if (of_property_read_bool(node, "rockchip,no-dmaengine")) { |
|---|
| 1108 | + dev_info(&pdev->dev, "Used for Multi-DAI\n"); |
|---|
| 980 | 1109 | return 0; |
|---|
| 1110 | + } |
|---|
| 981 | 1111 | |
|---|
| 982 | 1112 | ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0); |
|---|
| 983 | 1113 | if (ret) { |
|---|
| 984 | 1114 | dev_err(&pdev->dev, "could not register pcm: %d\n", ret); |
|---|
| 985 | 1115 | goto err_suspend; |
|---|
| 986 | 1116 | } |
|---|
| 1117 | + |
|---|
| 1118 | + clk_disable_unprepare(pdm->hclk); |
|---|
| 987 | 1119 | |
|---|
| 988 | 1120 | return 0; |
|---|
| 989 | 1121 | |
|---|
| .. | .. |
|---|
| 992 | 1124 | rockchip_pdm_runtime_suspend(&pdev->dev); |
|---|
| 993 | 1125 | err_pm_disable: |
|---|
| 994 | 1126 | pm_runtime_disable(&pdev->dev); |
|---|
| 995 | | - |
|---|
| 1127 | +err_clk: |
|---|
| 996 | 1128 | clk_disable_unprepare(pdm->hclk); |
|---|
| 997 | 1129 | |
|---|
| 998 | 1130 | return ret; |
|---|
| .. | .. |
|---|
| 1000 | 1132 | |
|---|
| 1001 | 1133 | static int rockchip_pdm_remove(struct platform_device *pdev) |
|---|
| 1002 | 1134 | { |
|---|
| 1003 | | - struct rk_pdm_dev *pdm = dev_get_drvdata(&pdev->dev); |
|---|
| 1004 | | - |
|---|
| 1005 | 1135 | pm_runtime_disable(&pdev->dev); |
|---|
| 1006 | 1136 | if (!pm_runtime_status_suspended(&pdev->dev)) |
|---|
| 1007 | 1137 | rockchip_pdm_runtime_suspend(&pdev->dev); |
|---|
| 1008 | | - |
|---|
| 1009 | | - clk_disable_unprepare(pdm->clk); |
|---|
| 1010 | | - clk_disable_unprepare(pdm->hclk); |
|---|
| 1011 | 1138 | |
|---|
| 1012 | 1139 | return 0; |
|---|
| 1013 | 1140 | } |
|---|