18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Rockchip PDM ALSA SoC Digital Audio Interface(DAI) driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2017 Fuzhou Rockchip Electronics Co., Ltd 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/module.h> 98c2ecf20Sopenharmony_ci#include <linux/clk.h> 108c2ecf20Sopenharmony_ci#include <linux/of.h> 118c2ecf20Sopenharmony_ci#include <linux/of_device.h> 128c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h> 138c2ecf20Sopenharmony_ci#include <linux/rational.h> 148c2ecf20Sopenharmony_ci#include <linux/regmap.h> 158c2ecf20Sopenharmony_ci#include <linux/reset.h> 168c2ecf20Sopenharmony_ci#include <sound/dmaengine_pcm.h> 178c2ecf20Sopenharmony_ci#include <sound/pcm_params.h> 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci#include "rockchip_pdm.h" 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci#define PDM_DMA_BURST_SIZE (8) /* size * width: 8*4 = 32 bytes */ 228c2ecf20Sopenharmony_ci#define PDM_SIGNOFF_CLK_RATE (100000000) 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_cienum rk_pdm_version { 258c2ecf20Sopenharmony_ci RK_PDM_RK3229, 268c2ecf20Sopenharmony_ci RK_PDM_RK3308, 278c2ecf20Sopenharmony_ci}; 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_cistruct rk_pdm_dev { 308c2ecf20Sopenharmony_ci struct device *dev; 318c2ecf20Sopenharmony_ci struct clk *clk; 328c2ecf20Sopenharmony_ci struct clk *hclk; 338c2ecf20Sopenharmony_ci struct regmap *regmap; 348c2ecf20Sopenharmony_ci struct snd_dmaengine_dai_dma_data capture_dma_data; 358c2ecf20Sopenharmony_ci struct reset_control *reset; 368c2ecf20Sopenharmony_ci enum rk_pdm_version version; 378c2ecf20Sopenharmony_ci}; 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_cistruct rk_pdm_clkref { 408c2ecf20Sopenharmony_ci unsigned int sr; 418c2ecf20Sopenharmony_ci unsigned int clk; 428c2ecf20Sopenharmony_ci unsigned int clk_out; 438c2ecf20Sopenharmony_ci}; 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_cistruct rk_pdm_ds_ratio { 468c2ecf20Sopenharmony_ci unsigned int ratio; 478c2ecf20Sopenharmony_ci unsigned int sr; 488c2ecf20Sopenharmony_ci}; 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_cistatic struct rk_pdm_clkref clkref[] = { 518c2ecf20Sopenharmony_ci { 8000, 40960000, 2048000 }, 528c2ecf20Sopenharmony_ci { 11025, 56448000, 2822400 }, 538c2ecf20Sopenharmony_ci { 12000, 61440000, 3072000 }, 548c2ecf20Sopenharmony_ci { 8000, 98304000, 2048000 }, 558c2ecf20Sopenharmony_ci { 12000, 98304000, 3072000 }, 568c2ecf20Sopenharmony_ci}; 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_cistatic struct rk_pdm_ds_ratio ds_ratio[] = { 598c2ecf20Sopenharmony_ci { 0, 192000 }, 608c2ecf20Sopenharmony_ci { 0, 176400 }, 618c2ecf20Sopenharmony_ci { 0, 128000 }, 628c2ecf20Sopenharmony_ci { 1, 96000 }, 638c2ecf20Sopenharmony_ci { 1, 88200 }, 648c2ecf20Sopenharmony_ci { 1, 64000 }, 658c2ecf20Sopenharmony_ci { 2, 48000 }, 668c2ecf20Sopenharmony_ci { 2, 44100 }, 678c2ecf20Sopenharmony_ci { 2, 32000 }, 688c2ecf20Sopenharmony_ci { 3, 24000 }, 698c2ecf20Sopenharmony_ci { 3, 22050 }, 708c2ecf20Sopenharmony_ci { 3, 16000 }, 718c2ecf20Sopenharmony_ci { 4, 12000 }, 728c2ecf20Sopenharmony_ci { 4, 11025 }, 738c2ecf20Sopenharmony_ci { 4, 8000 }, 748c2ecf20Sopenharmony_ci}; 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_cistatic unsigned int get_pdm_clk(struct rk_pdm_dev *pdm, unsigned int sr, 778c2ecf20Sopenharmony_ci unsigned int *clk_src, unsigned int *clk_out) 788c2ecf20Sopenharmony_ci{ 798c2ecf20Sopenharmony_ci unsigned int i, count, clk, div, rate; 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci clk = 0; 828c2ecf20Sopenharmony_ci if (!sr) 838c2ecf20Sopenharmony_ci return clk; 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci count = ARRAY_SIZE(clkref); 868c2ecf20Sopenharmony_ci for (i = 0; i < count; i++) { 878c2ecf20Sopenharmony_ci if (sr % clkref[i].sr) 888c2ecf20Sopenharmony_ci continue; 898c2ecf20Sopenharmony_ci div = sr / clkref[i].sr; 908c2ecf20Sopenharmony_ci if ((div & (div - 1)) == 0) { 918c2ecf20Sopenharmony_ci *clk_out = clkref[i].clk_out; 928c2ecf20Sopenharmony_ci rate = clk_round_rate(pdm->clk, clkref[i].clk); 938c2ecf20Sopenharmony_ci if (rate != clkref[i].clk) 948c2ecf20Sopenharmony_ci continue; 958c2ecf20Sopenharmony_ci clk = clkref[i].clk; 968c2ecf20Sopenharmony_ci *clk_src = clkref[i].clk; 978c2ecf20Sopenharmony_ci break; 988c2ecf20Sopenharmony_ci } 998c2ecf20Sopenharmony_ci } 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci if (!clk) { 1028c2ecf20Sopenharmony_ci clk = clk_round_rate(pdm->clk, PDM_SIGNOFF_CLK_RATE); 1038c2ecf20Sopenharmony_ci *clk_src = clk; 1048c2ecf20Sopenharmony_ci } 1058c2ecf20Sopenharmony_ci return clk; 1068c2ecf20Sopenharmony_ci} 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_cistatic unsigned int get_pdm_ds_ratio(unsigned int sr) 1098c2ecf20Sopenharmony_ci{ 1108c2ecf20Sopenharmony_ci unsigned int i, count, ratio; 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci ratio = 0; 1138c2ecf20Sopenharmony_ci if (!sr) 1148c2ecf20Sopenharmony_ci return ratio; 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci count = ARRAY_SIZE(ds_ratio); 1178c2ecf20Sopenharmony_ci for (i = 0; i < count; i++) { 1188c2ecf20Sopenharmony_ci if (sr == ds_ratio[i].sr) 1198c2ecf20Sopenharmony_ci ratio = ds_ratio[i].ratio; 1208c2ecf20Sopenharmony_ci } 1218c2ecf20Sopenharmony_ci return ratio; 1228c2ecf20Sopenharmony_ci} 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_cistatic inline struct rk_pdm_dev *to_info(struct snd_soc_dai *dai) 1258c2ecf20Sopenharmony_ci{ 1268c2ecf20Sopenharmony_ci return snd_soc_dai_get_drvdata(dai); 1278c2ecf20Sopenharmony_ci} 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_cistatic void rockchip_pdm_rxctrl(struct rk_pdm_dev *pdm, int on) 1308c2ecf20Sopenharmony_ci{ 1318c2ecf20Sopenharmony_ci if (on) { 1328c2ecf20Sopenharmony_ci regmap_update_bits(pdm->regmap, PDM_DMA_CTRL, 1338c2ecf20Sopenharmony_ci PDM_DMA_RD_MSK, PDM_DMA_RD_EN); 1348c2ecf20Sopenharmony_ci regmap_update_bits(pdm->regmap, PDM_SYSCONFIG, 1358c2ecf20Sopenharmony_ci PDM_RX_MASK, PDM_RX_START); 1368c2ecf20Sopenharmony_ci } else { 1378c2ecf20Sopenharmony_ci regmap_update_bits(pdm->regmap, PDM_DMA_CTRL, 1388c2ecf20Sopenharmony_ci PDM_DMA_RD_MSK, PDM_DMA_RD_DIS); 1398c2ecf20Sopenharmony_ci regmap_update_bits(pdm->regmap, PDM_SYSCONFIG, 1408c2ecf20Sopenharmony_ci PDM_RX_MASK | PDM_RX_CLR_MASK, 1418c2ecf20Sopenharmony_ci PDM_RX_STOP | PDM_RX_CLR_WR); 1428c2ecf20Sopenharmony_ci } 1438c2ecf20Sopenharmony_ci} 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_cistatic int rockchip_pdm_hw_params(struct snd_pcm_substream *substream, 1468c2ecf20Sopenharmony_ci struct snd_pcm_hw_params *params, 1478c2ecf20Sopenharmony_ci struct snd_soc_dai *dai) 1488c2ecf20Sopenharmony_ci{ 1498c2ecf20Sopenharmony_ci struct rk_pdm_dev *pdm = to_info(dai); 1508c2ecf20Sopenharmony_ci unsigned int val = 0; 1518c2ecf20Sopenharmony_ci unsigned int clk_rate, clk_div, samplerate; 1528c2ecf20Sopenharmony_ci unsigned int clk_src, clk_out = 0; 1538c2ecf20Sopenharmony_ci unsigned long m, n; 1548c2ecf20Sopenharmony_ci bool change; 1558c2ecf20Sopenharmony_ci int ret; 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 1588c2ecf20Sopenharmony_ci return 0; 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci samplerate = params_rate(params); 1618c2ecf20Sopenharmony_ci clk_rate = get_pdm_clk(pdm, samplerate, &clk_src, &clk_out); 1628c2ecf20Sopenharmony_ci if (!clk_rate) 1638c2ecf20Sopenharmony_ci return -EINVAL; 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci ret = clk_set_rate(pdm->clk, clk_src); 1668c2ecf20Sopenharmony_ci if (ret) 1678c2ecf20Sopenharmony_ci return -EINVAL; 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci if (pdm->version == RK_PDM_RK3308) { 1708c2ecf20Sopenharmony_ci rational_best_approximation(clk_out, clk_src, 1718c2ecf20Sopenharmony_ci GENMASK(16 - 1, 0), 1728c2ecf20Sopenharmony_ci GENMASK(16 - 1, 0), 1738c2ecf20Sopenharmony_ci &m, &n); 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci val = (m << PDM_FD_NUMERATOR_SFT) | 1768c2ecf20Sopenharmony_ci (n << PDM_FD_DENOMINATOR_SFT); 1778c2ecf20Sopenharmony_ci regmap_update_bits_check(pdm->regmap, PDM_CTRL1, 1788c2ecf20Sopenharmony_ci PDM_FD_NUMERATOR_MSK | 1798c2ecf20Sopenharmony_ci PDM_FD_DENOMINATOR_MSK, 1808c2ecf20Sopenharmony_ci val, &change); 1818c2ecf20Sopenharmony_ci if (change) { 1828c2ecf20Sopenharmony_ci reset_control_assert(pdm->reset); 1838c2ecf20Sopenharmony_ci reset_control_deassert(pdm->reset); 1848c2ecf20Sopenharmony_ci rockchip_pdm_rxctrl(pdm, 0); 1858c2ecf20Sopenharmony_ci } 1868c2ecf20Sopenharmony_ci clk_div = n / m; 1878c2ecf20Sopenharmony_ci if (clk_div >= 40) 1888c2ecf20Sopenharmony_ci val = PDM_CLK_FD_RATIO_40; 1898c2ecf20Sopenharmony_ci else if (clk_div <= 35) 1908c2ecf20Sopenharmony_ci val = PDM_CLK_FD_RATIO_35; 1918c2ecf20Sopenharmony_ci else 1928c2ecf20Sopenharmony_ci return -EINVAL; 1938c2ecf20Sopenharmony_ci regmap_update_bits(pdm->regmap, PDM_CLK_CTRL, 1948c2ecf20Sopenharmony_ci PDM_CLK_FD_RATIO_MSK, 1958c2ecf20Sopenharmony_ci val); 1968c2ecf20Sopenharmony_ci } 1978c2ecf20Sopenharmony_ci val = get_pdm_ds_ratio(samplerate); 1988c2ecf20Sopenharmony_ci regmap_update_bits(pdm->regmap, PDM_CLK_CTRL, PDM_DS_RATIO_MSK, val); 1998c2ecf20Sopenharmony_ci regmap_update_bits(pdm->regmap, PDM_HPF_CTRL, 2008c2ecf20Sopenharmony_ci PDM_HPF_CF_MSK, PDM_HPF_60HZ); 2018c2ecf20Sopenharmony_ci regmap_update_bits(pdm->regmap, PDM_HPF_CTRL, 2028c2ecf20Sopenharmony_ci PDM_HPF_LE | PDM_HPF_RE, PDM_HPF_LE | PDM_HPF_RE); 2038c2ecf20Sopenharmony_ci regmap_update_bits(pdm->regmap, PDM_CLK_CTRL, PDM_CLK_EN, PDM_CLK_EN); 2048c2ecf20Sopenharmony_ci if (pdm->version != RK_PDM_RK3229) 2058c2ecf20Sopenharmony_ci regmap_update_bits(pdm->regmap, PDM_CTRL0, 2068c2ecf20Sopenharmony_ci PDM_MODE_MSK, PDM_MODE_LJ); 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci val = 0; 2098c2ecf20Sopenharmony_ci switch (params_format(params)) { 2108c2ecf20Sopenharmony_ci case SNDRV_PCM_FORMAT_S8: 2118c2ecf20Sopenharmony_ci val |= PDM_VDW(8); 2128c2ecf20Sopenharmony_ci break; 2138c2ecf20Sopenharmony_ci case SNDRV_PCM_FORMAT_S16_LE: 2148c2ecf20Sopenharmony_ci val |= PDM_VDW(16); 2158c2ecf20Sopenharmony_ci break; 2168c2ecf20Sopenharmony_ci case SNDRV_PCM_FORMAT_S20_3LE: 2178c2ecf20Sopenharmony_ci val |= PDM_VDW(20); 2188c2ecf20Sopenharmony_ci break; 2198c2ecf20Sopenharmony_ci case SNDRV_PCM_FORMAT_S24_LE: 2208c2ecf20Sopenharmony_ci val |= PDM_VDW(24); 2218c2ecf20Sopenharmony_ci break; 2228c2ecf20Sopenharmony_ci case SNDRV_PCM_FORMAT_S32_LE: 2238c2ecf20Sopenharmony_ci val |= PDM_VDW(32); 2248c2ecf20Sopenharmony_ci break; 2258c2ecf20Sopenharmony_ci default: 2268c2ecf20Sopenharmony_ci return -EINVAL; 2278c2ecf20Sopenharmony_ci } 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci switch (params_channels(params)) { 2308c2ecf20Sopenharmony_ci case 8: 2318c2ecf20Sopenharmony_ci val |= PDM_PATH3_EN; 2328c2ecf20Sopenharmony_ci fallthrough; 2338c2ecf20Sopenharmony_ci case 6: 2348c2ecf20Sopenharmony_ci val |= PDM_PATH2_EN; 2358c2ecf20Sopenharmony_ci fallthrough; 2368c2ecf20Sopenharmony_ci case 4: 2378c2ecf20Sopenharmony_ci val |= PDM_PATH1_EN; 2388c2ecf20Sopenharmony_ci fallthrough; 2398c2ecf20Sopenharmony_ci case 2: 2408c2ecf20Sopenharmony_ci val |= PDM_PATH0_EN; 2418c2ecf20Sopenharmony_ci break; 2428c2ecf20Sopenharmony_ci default: 2438c2ecf20Sopenharmony_ci dev_err(pdm->dev, "invalid channel: %d\n", 2448c2ecf20Sopenharmony_ci params_channels(params)); 2458c2ecf20Sopenharmony_ci return -EINVAL; 2468c2ecf20Sopenharmony_ci } 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci regmap_update_bits(pdm->regmap, PDM_CTRL0, 2498c2ecf20Sopenharmony_ci PDM_PATH_MSK | PDM_VDW_MSK, 2508c2ecf20Sopenharmony_ci val); 2518c2ecf20Sopenharmony_ci /* all channels share the single FIFO */ 2528c2ecf20Sopenharmony_ci regmap_update_bits(pdm->regmap, PDM_DMA_CTRL, PDM_DMA_RDL_MSK, 2538c2ecf20Sopenharmony_ci PDM_DMA_RDL(8 * params_channels(params))); 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci return 0; 2568c2ecf20Sopenharmony_ci} 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_cistatic int rockchip_pdm_set_fmt(struct snd_soc_dai *cpu_dai, 2598c2ecf20Sopenharmony_ci unsigned int fmt) 2608c2ecf20Sopenharmony_ci{ 2618c2ecf20Sopenharmony_ci struct rk_pdm_dev *pdm = to_info(cpu_dai); 2628c2ecf20Sopenharmony_ci unsigned int mask = 0, val = 0; 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci mask = PDM_CKP_MSK; 2658c2ecf20Sopenharmony_ci switch (fmt & SND_SOC_DAIFMT_INV_MASK) { 2668c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_NB_NF: 2678c2ecf20Sopenharmony_ci val = PDM_CKP_NORMAL; 2688c2ecf20Sopenharmony_ci break; 2698c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_IB_NF: 2708c2ecf20Sopenharmony_ci val = PDM_CKP_INVERTED; 2718c2ecf20Sopenharmony_ci break; 2728c2ecf20Sopenharmony_ci default: 2738c2ecf20Sopenharmony_ci return -EINVAL; 2748c2ecf20Sopenharmony_ci } 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci pm_runtime_get_sync(cpu_dai->dev); 2778c2ecf20Sopenharmony_ci regmap_update_bits(pdm->regmap, PDM_CLK_CTRL, mask, val); 2788c2ecf20Sopenharmony_ci pm_runtime_put(cpu_dai->dev); 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci return 0; 2818c2ecf20Sopenharmony_ci} 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_cistatic int rockchip_pdm_trigger(struct snd_pcm_substream *substream, int cmd, 2848c2ecf20Sopenharmony_ci struct snd_soc_dai *dai) 2858c2ecf20Sopenharmony_ci{ 2868c2ecf20Sopenharmony_ci struct rk_pdm_dev *pdm = to_info(dai); 2878c2ecf20Sopenharmony_ci int ret = 0; 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci switch (cmd) { 2908c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_START: 2918c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_RESUME: 2928c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 2938c2ecf20Sopenharmony_ci if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) 2948c2ecf20Sopenharmony_ci rockchip_pdm_rxctrl(pdm, 1); 2958c2ecf20Sopenharmony_ci break; 2968c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_SUSPEND: 2978c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_STOP: 2988c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 2998c2ecf20Sopenharmony_ci if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) 3008c2ecf20Sopenharmony_ci rockchip_pdm_rxctrl(pdm, 0); 3018c2ecf20Sopenharmony_ci break; 3028c2ecf20Sopenharmony_ci default: 3038c2ecf20Sopenharmony_ci ret = -EINVAL; 3048c2ecf20Sopenharmony_ci break; 3058c2ecf20Sopenharmony_ci } 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci return ret; 3088c2ecf20Sopenharmony_ci} 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_cistatic int rockchip_pdm_dai_probe(struct snd_soc_dai *dai) 3118c2ecf20Sopenharmony_ci{ 3128c2ecf20Sopenharmony_ci struct rk_pdm_dev *pdm = to_info(dai); 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci dai->capture_dma_data = &pdm->capture_dma_data; 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci return 0; 3178c2ecf20Sopenharmony_ci} 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_cistatic const struct snd_soc_dai_ops rockchip_pdm_dai_ops = { 3208c2ecf20Sopenharmony_ci .set_fmt = rockchip_pdm_set_fmt, 3218c2ecf20Sopenharmony_ci .trigger = rockchip_pdm_trigger, 3228c2ecf20Sopenharmony_ci .hw_params = rockchip_pdm_hw_params, 3238c2ecf20Sopenharmony_ci}; 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci#define ROCKCHIP_PDM_RATES SNDRV_PCM_RATE_8000_192000 3268c2ecf20Sopenharmony_ci#define ROCKCHIP_PDM_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ 3278c2ecf20Sopenharmony_ci SNDRV_PCM_FMTBIT_S20_3LE | \ 3288c2ecf20Sopenharmony_ci SNDRV_PCM_FMTBIT_S24_LE | \ 3298c2ecf20Sopenharmony_ci SNDRV_PCM_FMTBIT_S32_LE) 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_cistatic struct snd_soc_dai_driver rockchip_pdm_dai = { 3328c2ecf20Sopenharmony_ci .probe = rockchip_pdm_dai_probe, 3338c2ecf20Sopenharmony_ci .capture = { 3348c2ecf20Sopenharmony_ci .stream_name = "Capture", 3358c2ecf20Sopenharmony_ci .channels_min = 2, 3368c2ecf20Sopenharmony_ci .channels_max = 8, 3378c2ecf20Sopenharmony_ci .rates = ROCKCHIP_PDM_RATES, 3388c2ecf20Sopenharmony_ci .formats = ROCKCHIP_PDM_FORMATS, 3398c2ecf20Sopenharmony_ci }, 3408c2ecf20Sopenharmony_ci .ops = &rockchip_pdm_dai_ops, 3418c2ecf20Sopenharmony_ci .symmetric_rates = 1, 3428c2ecf20Sopenharmony_ci}; 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_cistatic const struct snd_soc_component_driver rockchip_pdm_component = { 3458c2ecf20Sopenharmony_ci .name = "rockchip-pdm", 3468c2ecf20Sopenharmony_ci}; 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_cistatic int rockchip_pdm_runtime_suspend(struct device *dev) 3498c2ecf20Sopenharmony_ci{ 3508c2ecf20Sopenharmony_ci struct rk_pdm_dev *pdm = dev_get_drvdata(dev); 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci clk_disable_unprepare(pdm->clk); 3538c2ecf20Sopenharmony_ci clk_disable_unprepare(pdm->hclk); 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci return 0; 3568c2ecf20Sopenharmony_ci} 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_cistatic int rockchip_pdm_runtime_resume(struct device *dev) 3598c2ecf20Sopenharmony_ci{ 3608c2ecf20Sopenharmony_ci struct rk_pdm_dev *pdm = dev_get_drvdata(dev); 3618c2ecf20Sopenharmony_ci int ret; 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci ret = clk_prepare_enable(pdm->clk); 3648c2ecf20Sopenharmony_ci if (ret) { 3658c2ecf20Sopenharmony_ci dev_err(pdm->dev, "clock enable failed %d\n", ret); 3668c2ecf20Sopenharmony_ci return ret; 3678c2ecf20Sopenharmony_ci } 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci ret = clk_prepare_enable(pdm->hclk); 3708c2ecf20Sopenharmony_ci if (ret) { 3718c2ecf20Sopenharmony_ci clk_disable_unprepare(pdm->clk); 3728c2ecf20Sopenharmony_ci dev_err(pdm->dev, "hclock enable failed %d\n", ret); 3738c2ecf20Sopenharmony_ci return ret; 3748c2ecf20Sopenharmony_ci } 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci return 0; 3778c2ecf20Sopenharmony_ci} 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_cistatic bool rockchip_pdm_wr_reg(struct device *dev, unsigned int reg) 3808c2ecf20Sopenharmony_ci{ 3818c2ecf20Sopenharmony_ci switch (reg) { 3828c2ecf20Sopenharmony_ci case PDM_SYSCONFIG: 3838c2ecf20Sopenharmony_ci case PDM_CTRL0: 3848c2ecf20Sopenharmony_ci case PDM_CTRL1: 3858c2ecf20Sopenharmony_ci case PDM_CLK_CTRL: 3868c2ecf20Sopenharmony_ci case PDM_HPF_CTRL: 3878c2ecf20Sopenharmony_ci case PDM_FIFO_CTRL: 3888c2ecf20Sopenharmony_ci case PDM_DMA_CTRL: 3898c2ecf20Sopenharmony_ci case PDM_INT_EN: 3908c2ecf20Sopenharmony_ci case PDM_INT_CLR: 3918c2ecf20Sopenharmony_ci case PDM_DATA_VALID: 3928c2ecf20Sopenharmony_ci return true; 3938c2ecf20Sopenharmony_ci default: 3948c2ecf20Sopenharmony_ci return false; 3958c2ecf20Sopenharmony_ci } 3968c2ecf20Sopenharmony_ci} 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_cistatic bool rockchip_pdm_rd_reg(struct device *dev, unsigned int reg) 3998c2ecf20Sopenharmony_ci{ 4008c2ecf20Sopenharmony_ci switch (reg) { 4018c2ecf20Sopenharmony_ci case PDM_SYSCONFIG: 4028c2ecf20Sopenharmony_ci case PDM_CTRL0: 4038c2ecf20Sopenharmony_ci case PDM_CTRL1: 4048c2ecf20Sopenharmony_ci case PDM_CLK_CTRL: 4058c2ecf20Sopenharmony_ci case PDM_HPF_CTRL: 4068c2ecf20Sopenharmony_ci case PDM_FIFO_CTRL: 4078c2ecf20Sopenharmony_ci case PDM_DMA_CTRL: 4088c2ecf20Sopenharmony_ci case PDM_INT_EN: 4098c2ecf20Sopenharmony_ci case PDM_INT_CLR: 4108c2ecf20Sopenharmony_ci case PDM_INT_ST: 4118c2ecf20Sopenharmony_ci case PDM_DATA_VALID: 4128c2ecf20Sopenharmony_ci case PDM_RXFIFO_DATA: 4138c2ecf20Sopenharmony_ci case PDM_VERSION: 4148c2ecf20Sopenharmony_ci return true; 4158c2ecf20Sopenharmony_ci default: 4168c2ecf20Sopenharmony_ci return false; 4178c2ecf20Sopenharmony_ci } 4188c2ecf20Sopenharmony_ci} 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_cistatic bool rockchip_pdm_volatile_reg(struct device *dev, unsigned int reg) 4218c2ecf20Sopenharmony_ci{ 4228c2ecf20Sopenharmony_ci switch (reg) { 4238c2ecf20Sopenharmony_ci case PDM_SYSCONFIG: 4248c2ecf20Sopenharmony_ci case PDM_FIFO_CTRL: 4258c2ecf20Sopenharmony_ci case PDM_INT_CLR: 4268c2ecf20Sopenharmony_ci case PDM_INT_ST: 4278c2ecf20Sopenharmony_ci case PDM_RXFIFO_DATA: 4288c2ecf20Sopenharmony_ci return true; 4298c2ecf20Sopenharmony_ci default: 4308c2ecf20Sopenharmony_ci return false; 4318c2ecf20Sopenharmony_ci } 4328c2ecf20Sopenharmony_ci} 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_cistatic bool rockchip_pdm_precious_reg(struct device *dev, unsigned int reg) 4358c2ecf20Sopenharmony_ci{ 4368c2ecf20Sopenharmony_ci switch (reg) { 4378c2ecf20Sopenharmony_ci case PDM_RXFIFO_DATA: 4388c2ecf20Sopenharmony_ci return true; 4398c2ecf20Sopenharmony_ci default: 4408c2ecf20Sopenharmony_ci return false; 4418c2ecf20Sopenharmony_ci } 4428c2ecf20Sopenharmony_ci} 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_cistatic const struct reg_default rockchip_pdm_reg_defaults[] = { 4458c2ecf20Sopenharmony_ci {0x04, 0x78000017}, 4468c2ecf20Sopenharmony_ci {0x08, 0x0bb8ea60}, 4478c2ecf20Sopenharmony_ci {0x18, 0x0000001f}, 4488c2ecf20Sopenharmony_ci}; 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_cistatic const struct regmap_config rockchip_pdm_regmap_config = { 4518c2ecf20Sopenharmony_ci .reg_bits = 32, 4528c2ecf20Sopenharmony_ci .reg_stride = 4, 4538c2ecf20Sopenharmony_ci .val_bits = 32, 4548c2ecf20Sopenharmony_ci .max_register = PDM_VERSION, 4558c2ecf20Sopenharmony_ci .reg_defaults = rockchip_pdm_reg_defaults, 4568c2ecf20Sopenharmony_ci .num_reg_defaults = ARRAY_SIZE(rockchip_pdm_reg_defaults), 4578c2ecf20Sopenharmony_ci .writeable_reg = rockchip_pdm_wr_reg, 4588c2ecf20Sopenharmony_ci .readable_reg = rockchip_pdm_rd_reg, 4598c2ecf20Sopenharmony_ci .volatile_reg = rockchip_pdm_volatile_reg, 4608c2ecf20Sopenharmony_ci .precious_reg = rockchip_pdm_precious_reg, 4618c2ecf20Sopenharmony_ci .cache_type = REGCACHE_FLAT, 4628c2ecf20Sopenharmony_ci}; 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_cistatic const struct of_device_id rockchip_pdm_match[] = { 4658c2ecf20Sopenharmony_ci { .compatible = "rockchip,pdm", 4668c2ecf20Sopenharmony_ci .data = (void *)RK_PDM_RK3229 }, 4678c2ecf20Sopenharmony_ci { .compatible = "rockchip,px30-pdm", 4688c2ecf20Sopenharmony_ci .data = (void *)RK_PDM_RK3308 }, 4698c2ecf20Sopenharmony_ci { .compatible = "rockchip,rk1808-pdm", 4708c2ecf20Sopenharmony_ci .data = (void *)RK_PDM_RK3308 }, 4718c2ecf20Sopenharmony_ci { .compatible = "rockchip,rk3308-pdm", 4728c2ecf20Sopenharmony_ci .data = (void *)RK_PDM_RK3308 }, 4738c2ecf20Sopenharmony_ci {}, 4748c2ecf20Sopenharmony_ci}; 4758c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, rockchip_pdm_match); 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_cistatic int rockchip_pdm_probe(struct platform_device *pdev) 4788c2ecf20Sopenharmony_ci{ 4798c2ecf20Sopenharmony_ci const struct of_device_id *match; 4808c2ecf20Sopenharmony_ci struct rk_pdm_dev *pdm; 4818c2ecf20Sopenharmony_ci struct resource *res; 4828c2ecf20Sopenharmony_ci void __iomem *regs; 4838c2ecf20Sopenharmony_ci int ret; 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_ci pdm = devm_kzalloc(&pdev->dev, sizeof(*pdm), GFP_KERNEL); 4868c2ecf20Sopenharmony_ci if (!pdm) 4878c2ecf20Sopenharmony_ci return -ENOMEM; 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ci match = of_match_device(rockchip_pdm_match, &pdev->dev); 4908c2ecf20Sopenharmony_ci if (match) 4918c2ecf20Sopenharmony_ci pdm->version = (enum rk_pdm_version)match->data; 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_ci if (pdm->version == RK_PDM_RK3308) { 4948c2ecf20Sopenharmony_ci pdm->reset = devm_reset_control_get(&pdev->dev, "pdm-m"); 4958c2ecf20Sopenharmony_ci if (IS_ERR(pdm->reset)) 4968c2ecf20Sopenharmony_ci return PTR_ERR(pdm->reset); 4978c2ecf20Sopenharmony_ci } 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 5008c2ecf20Sopenharmony_ci regs = devm_ioremap_resource(&pdev->dev, res); 5018c2ecf20Sopenharmony_ci if (IS_ERR(regs)) 5028c2ecf20Sopenharmony_ci return PTR_ERR(regs); 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_ci pdm->regmap = devm_regmap_init_mmio(&pdev->dev, regs, 5058c2ecf20Sopenharmony_ci &rockchip_pdm_regmap_config); 5068c2ecf20Sopenharmony_ci if (IS_ERR(pdm->regmap)) 5078c2ecf20Sopenharmony_ci return PTR_ERR(pdm->regmap); 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_ci pdm->capture_dma_data.addr = res->start + PDM_RXFIFO_DATA; 5108c2ecf20Sopenharmony_ci pdm->capture_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; 5118c2ecf20Sopenharmony_ci pdm->capture_dma_data.maxburst = PDM_DMA_BURST_SIZE; 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_ci pdm->dev = &pdev->dev; 5148c2ecf20Sopenharmony_ci dev_set_drvdata(&pdev->dev, pdm); 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_ci pdm->clk = devm_clk_get(&pdev->dev, "pdm_clk"); 5178c2ecf20Sopenharmony_ci if (IS_ERR(pdm->clk)) 5188c2ecf20Sopenharmony_ci return PTR_ERR(pdm->clk); 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_ci pdm->hclk = devm_clk_get(&pdev->dev, "pdm_hclk"); 5218c2ecf20Sopenharmony_ci if (IS_ERR(pdm->hclk)) 5228c2ecf20Sopenharmony_ci return PTR_ERR(pdm->hclk); 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_ci ret = clk_prepare_enable(pdm->hclk); 5258c2ecf20Sopenharmony_ci if (ret) 5268c2ecf20Sopenharmony_ci return ret; 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_ci pm_runtime_enable(&pdev->dev); 5298c2ecf20Sopenharmony_ci if (!pm_runtime_enabled(&pdev->dev)) { 5308c2ecf20Sopenharmony_ci ret = rockchip_pdm_runtime_resume(&pdev->dev); 5318c2ecf20Sopenharmony_ci if (ret) 5328c2ecf20Sopenharmony_ci goto err_pm_disable; 5338c2ecf20Sopenharmony_ci } 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_ci ret = devm_snd_soc_register_component(&pdev->dev, 5368c2ecf20Sopenharmony_ci &rockchip_pdm_component, 5378c2ecf20Sopenharmony_ci &rockchip_pdm_dai, 1); 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_ci if (ret) { 5408c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "could not register dai: %d\n", ret); 5418c2ecf20Sopenharmony_ci goto err_suspend; 5428c2ecf20Sopenharmony_ci } 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_ci rockchip_pdm_rxctrl(pdm, 0); 5458c2ecf20Sopenharmony_ci ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0); 5468c2ecf20Sopenharmony_ci if (ret) { 5478c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "could not register pcm: %d\n", ret); 5488c2ecf20Sopenharmony_ci goto err_suspend; 5498c2ecf20Sopenharmony_ci } 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_ci return 0; 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_cierr_suspend: 5548c2ecf20Sopenharmony_ci if (!pm_runtime_status_suspended(&pdev->dev)) 5558c2ecf20Sopenharmony_ci rockchip_pdm_runtime_suspend(&pdev->dev); 5568c2ecf20Sopenharmony_cierr_pm_disable: 5578c2ecf20Sopenharmony_ci pm_runtime_disable(&pdev->dev); 5588c2ecf20Sopenharmony_ci 5598c2ecf20Sopenharmony_ci clk_disable_unprepare(pdm->hclk); 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_ci return ret; 5628c2ecf20Sopenharmony_ci} 5638c2ecf20Sopenharmony_ci 5648c2ecf20Sopenharmony_cistatic int rockchip_pdm_remove(struct platform_device *pdev) 5658c2ecf20Sopenharmony_ci{ 5668c2ecf20Sopenharmony_ci struct rk_pdm_dev *pdm = dev_get_drvdata(&pdev->dev); 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_ci pm_runtime_disable(&pdev->dev); 5698c2ecf20Sopenharmony_ci if (!pm_runtime_status_suspended(&pdev->dev)) 5708c2ecf20Sopenharmony_ci rockchip_pdm_runtime_suspend(&pdev->dev); 5718c2ecf20Sopenharmony_ci 5728c2ecf20Sopenharmony_ci clk_disable_unprepare(pdm->clk); 5738c2ecf20Sopenharmony_ci clk_disable_unprepare(pdm->hclk); 5748c2ecf20Sopenharmony_ci 5758c2ecf20Sopenharmony_ci return 0; 5768c2ecf20Sopenharmony_ci} 5778c2ecf20Sopenharmony_ci 5788c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 5798c2ecf20Sopenharmony_cistatic int rockchip_pdm_suspend(struct device *dev) 5808c2ecf20Sopenharmony_ci{ 5818c2ecf20Sopenharmony_ci struct rk_pdm_dev *pdm = dev_get_drvdata(dev); 5828c2ecf20Sopenharmony_ci 5838c2ecf20Sopenharmony_ci regcache_mark_dirty(pdm->regmap); 5848c2ecf20Sopenharmony_ci 5858c2ecf20Sopenharmony_ci return 0; 5868c2ecf20Sopenharmony_ci} 5878c2ecf20Sopenharmony_ci 5888c2ecf20Sopenharmony_cistatic int rockchip_pdm_resume(struct device *dev) 5898c2ecf20Sopenharmony_ci{ 5908c2ecf20Sopenharmony_ci struct rk_pdm_dev *pdm = dev_get_drvdata(dev); 5918c2ecf20Sopenharmony_ci int ret; 5928c2ecf20Sopenharmony_ci 5938c2ecf20Sopenharmony_ci ret = pm_runtime_get_sync(dev); 5948c2ecf20Sopenharmony_ci if (ret < 0) { 5958c2ecf20Sopenharmony_ci pm_runtime_put(dev); 5968c2ecf20Sopenharmony_ci return ret; 5978c2ecf20Sopenharmony_ci } 5988c2ecf20Sopenharmony_ci 5998c2ecf20Sopenharmony_ci ret = regcache_sync(pdm->regmap); 6008c2ecf20Sopenharmony_ci 6018c2ecf20Sopenharmony_ci pm_runtime_put(dev); 6028c2ecf20Sopenharmony_ci 6038c2ecf20Sopenharmony_ci return ret; 6048c2ecf20Sopenharmony_ci} 6058c2ecf20Sopenharmony_ci#endif 6068c2ecf20Sopenharmony_ci 6078c2ecf20Sopenharmony_cistatic const struct dev_pm_ops rockchip_pdm_pm_ops = { 6088c2ecf20Sopenharmony_ci SET_RUNTIME_PM_OPS(rockchip_pdm_runtime_suspend, 6098c2ecf20Sopenharmony_ci rockchip_pdm_runtime_resume, NULL) 6108c2ecf20Sopenharmony_ci SET_SYSTEM_SLEEP_PM_OPS(rockchip_pdm_suspend, rockchip_pdm_resume) 6118c2ecf20Sopenharmony_ci}; 6128c2ecf20Sopenharmony_ci 6138c2ecf20Sopenharmony_cistatic struct platform_driver rockchip_pdm_driver = { 6148c2ecf20Sopenharmony_ci .probe = rockchip_pdm_probe, 6158c2ecf20Sopenharmony_ci .remove = rockchip_pdm_remove, 6168c2ecf20Sopenharmony_ci .driver = { 6178c2ecf20Sopenharmony_ci .name = "rockchip-pdm", 6188c2ecf20Sopenharmony_ci .of_match_table = of_match_ptr(rockchip_pdm_match), 6198c2ecf20Sopenharmony_ci .pm = &rockchip_pdm_pm_ops, 6208c2ecf20Sopenharmony_ci }, 6218c2ecf20Sopenharmony_ci}; 6228c2ecf20Sopenharmony_ci 6238c2ecf20Sopenharmony_cimodule_platform_driver(rockchip_pdm_driver); 6248c2ecf20Sopenharmony_ci 6258c2ecf20Sopenharmony_ciMODULE_AUTHOR("Sugar <sugar.zhang@rock-chips.com>"); 6268c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Rockchip PDM Controller Driver"); 6278c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 628