1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | // |
3 | // This driver supports the DMIC in Allwinner's H6 SoCs. |
4 | // |
5 | // Copyright 2021 Ban Tao <fengzheng923@gmail.com> |
6 | |
7 | #include <linux/clk.h> |
8 | #include <linux/device.h> |
9 | #include <linux/mod_devicetable.h> |
10 | #include <linux/module.h> |
11 | #include <linux/platform_device.h> |
12 | #include <linux/pm_runtime.h> |
13 | #include <linux/reset.h> |
14 | #include <sound/dmaengine_pcm.h> |
15 | #include <sound/pcm_params.h> |
16 | #include <sound/soc.h> |
17 | |
18 | #define SUN50I_DMIC_EN_CTL (0x00) |
19 | #define SUN50I_DMIC_EN_CTL_GLOBE BIT(8) |
20 | #define SUN50I_DMIC_EN_CTL_CHAN(v) ((v) << 0) |
21 | #define SUN50I_DMIC_EN_CTL_CHAN_MASK GENMASK(7, 0) |
22 | #define SUN50I_DMIC_SR (0x04) |
23 | #define SUN50I_DMIC_SR_SAMPLE_RATE(v) ((v) << 0) |
24 | #define SUN50I_DMIC_SR_SAMPLE_RATE_MASK GENMASK(2, 0) |
25 | #define SUN50I_DMIC_CTL (0x08) |
26 | #define SUN50I_DMIC_CTL_OVERSAMPLE_RATE BIT(0) |
27 | #define SUN50I_DMIC_DATA (0x10) |
28 | #define SUN50I_DMIC_INTC (0x14) |
29 | #define SUN50I_DMIC_FIFO_DRQ_EN BIT(2) |
30 | #define SUN50I_DMIC_INT_STA (0x18) |
31 | #define SUN50I_DMIC_INT_STA_OVERRUN_IRQ_PENDING BIT(1) |
32 | #define SUN50I_DMIC_INT_STA_DATA_IRQ_PENDING BIT(0) |
33 | #define SUN50I_DMIC_RXFIFO_CTL (0x1c) |
34 | #define SUN50I_DMIC_RXFIFO_CTL_FLUSH BIT(31) |
35 | #define SUN50I_DMIC_RXFIFO_CTL_MODE_MASK BIT(9) |
36 | #define SUN50I_DMIC_RXFIFO_CTL_MODE_LSB (0 << 9) |
37 | #define SUN50I_DMIC_RXFIFO_CTL_MODE_MSB (1 << 9) |
38 | #define SUN50I_DMIC_RXFIFO_CTL_SAMPLE_MASK BIT(8) |
39 | #define SUN50I_DMIC_RXFIFO_CTL_SAMPLE_16 (0 << 8) |
40 | #define SUN50I_DMIC_RXFIFO_CTL_SAMPLE_24 (1 << 8) |
41 | #define SUN50I_DMIC_CH_NUM (0x24) |
42 | #define SUN50I_DMIC_CH_NUM_N(v) ((v) << 0) |
43 | #define SUN50I_DMIC_CH_NUM_N_MASK GENMASK(2, 0) |
44 | #define SUN50I_DMIC_CNT (0x2c) |
45 | #define SUN50I_DMIC_CNT_N (1 << 0) |
46 | #define SUN50I_DMIC_HPF_CTRL (0x38) |
47 | #define SUN50I_DMIC_VERSION (0x50) |
48 | |
49 | struct sun50i_dmic_dev { |
50 | struct clk *dmic_clk; |
51 | struct clk *bus_clk; |
52 | struct reset_control *rst; |
53 | struct regmap *regmap; |
54 | struct snd_dmaengine_dai_dma_data dma_params_rx; |
55 | }; |
56 | |
57 | struct dmic_rate { |
58 | unsigned int samplerate; |
59 | unsigned int rate_bit; |
60 | }; |
61 | |
62 | static const struct dmic_rate dmic_rate_s[] = { |
63 | {48000, 0x0}, |
64 | {44100, 0x0}, |
65 | {32000, 0x1}, |
66 | {24000, 0x2}, |
67 | {22050, 0x2}, |
68 | {16000, 0x3}, |
69 | {12000, 0x4}, |
70 | {11025, 0x4}, |
71 | {8000, 0x5}, |
72 | }; |
73 | |
74 | static int sun50i_dmic_startup(struct snd_pcm_substream *substream, |
75 | struct snd_soc_dai *cpu_dai) |
76 | { |
77 | struct snd_soc_pcm_runtime *rtd = substream->private_data; |
78 | struct sun50i_dmic_dev *host = snd_soc_dai_get_drvdata(snd_soc_rtd_to_cpu(rtd, 0)); |
79 | |
80 | /* only support capture */ |
81 | if (substream->stream != SNDRV_PCM_STREAM_CAPTURE) |
82 | return -EINVAL; |
83 | |
84 | regmap_update_bits(map: host->regmap, SUN50I_DMIC_RXFIFO_CTL, |
85 | SUN50I_DMIC_RXFIFO_CTL_FLUSH, |
86 | SUN50I_DMIC_RXFIFO_CTL_FLUSH); |
87 | regmap_write(map: host->regmap, SUN50I_DMIC_CNT, SUN50I_DMIC_CNT_N); |
88 | |
89 | return 0; |
90 | } |
91 | |
92 | static int sun50i_dmic_hw_params(struct snd_pcm_substream *substream, |
93 | struct snd_pcm_hw_params *params, |
94 | struct snd_soc_dai *cpu_dai) |
95 | { |
96 | int i = 0; |
97 | unsigned long rate = params_rate(p: params); |
98 | unsigned int mclk = 0; |
99 | unsigned int channels = params_channels(p: params); |
100 | unsigned int chan_en = (1 << channels) - 1; |
101 | struct sun50i_dmic_dev *host = snd_soc_dai_get_drvdata(dai: cpu_dai); |
102 | |
103 | /* DMIC num is N+1 */ |
104 | regmap_update_bits(map: host->regmap, SUN50I_DMIC_CH_NUM, |
105 | SUN50I_DMIC_CH_NUM_N_MASK, |
106 | SUN50I_DMIC_CH_NUM_N(channels - 1)); |
107 | regmap_write(map: host->regmap, SUN50I_DMIC_HPF_CTRL, val: chan_en); |
108 | regmap_update_bits(map: host->regmap, SUN50I_DMIC_EN_CTL, |
109 | SUN50I_DMIC_EN_CTL_CHAN_MASK, |
110 | SUN50I_DMIC_EN_CTL_CHAN(chan_en)); |
111 | |
112 | switch (params_format(p: params)) { |
113 | case SNDRV_PCM_FORMAT_S16_LE: |
114 | regmap_update_bits(map: host->regmap, SUN50I_DMIC_RXFIFO_CTL, |
115 | SUN50I_DMIC_RXFIFO_CTL_SAMPLE_MASK, |
116 | SUN50I_DMIC_RXFIFO_CTL_SAMPLE_16); |
117 | break; |
118 | case SNDRV_PCM_FORMAT_S24_LE: |
119 | regmap_update_bits(map: host->regmap, SUN50I_DMIC_RXFIFO_CTL, |
120 | SUN50I_DMIC_RXFIFO_CTL_SAMPLE_MASK, |
121 | SUN50I_DMIC_RXFIFO_CTL_SAMPLE_24); |
122 | break; |
123 | default: |
124 | dev_err(cpu_dai->dev, "Invalid format!\n" ); |
125 | return -EINVAL; |
126 | } |
127 | /* The hardware supports FIFO mode 1 for 24-bit samples */ |
128 | regmap_update_bits(map: host->regmap, SUN50I_DMIC_RXFIFO_CTL, |
129 | SUN50I_DMIC_RXFIFO_CTL_MODE_MASK, |
130 | SUN50I_DMIC_RXFIFO_CTL_MODE_MSB); |
131 | |
132 | switch (rate) { |
133 | case 11025: |
134 | case 22050: |
135 | case 44100: |
136 | mclk = 22579200; |
137 | break; |
138 | case 8000: |
139 | case 12000: |
140 | case 16000: |
141 | case 24000: |
142 | case 32000: |
143 | case 48000: |
144 | mclk = 24576000; |
145 | break; |
146 | default: |
147 | dev_err(cpu_dai->dev, "Invalid rate!\n" ); |
148 | return -EINVAL; |
149 | } |
150 | |
151 | if (clk_set_rate(clk: host->dmic_clk, rate: mclk)) { |
152 | dev_err(cpu_dai->dev, "mclk : %u not support\n" , mclk); |
153 | return -EINVAL; |
154 | } |
155 | |
156 | for (i = 0; i < ARRAY_SIZE(dmic_rate_s); i++) { |
157 | if (dmic_rate_s[i].samplerate == rate) { |
158 | regmap_update_bits(map: host->regmap, SUN50I_DMIC_SR, |
159 | SUN50I_DMIC_SR_SAMPLE_RATE_MASK, |
160 | SUN50I_DMIC_SR_SAMPLE_RATE(dmic_rate_s[i].rate_bit)); |
161 | break; |
162 | } |
163 | } |
164 | |
165 | switch (params_physical_width(p: params)) { |
166 | case 16: |
167 | host->dma_params_rx.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; |
168 | break; |
169 | case 32: |
170 | host->dma_params_rx.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; |
171 | break; |
172 | default: |
173 | dev_err(cpu_dai->dev, "Unsupported physical sample width: %d\n" , |
174 | params_physical_width(params)); |
175 | return -EINVAL; |
176 | } |
177 | |
178 | /* oversamplerate adjust */ |
179 | if (params_rate(p: params) >= 24000) |
180 | regmap_update_bits(map: host->regmap, SUN50I_DMIC_CTL, |
181 | SUN50I_DMIC_CTL_OVERSAMPLE_RATE, |
182 | SUN50I_DMIC_CTL_OVERSAMPLE_RATE); |
183 | else |
184 | regmap_update_bits(map: host->regmap, SUN50I_DMIC_CTL, |
185 | SUN50I_DMIC_CTL_OVERSAMPLE_RATE, val: 0); |
186 | |
187 | return 0; |
188 | } |
189 | |
190 | static int sun50i_dmic_trigger(struct snd_pcm_substream *substream, int cmd, |
191 | struct snd_soc_dai *dai) |
192 | { |
193 | int ret = 0; |
194 | struct sun50i_dmic_dev *host = snd_soc_dai_get_drvdata(dai); |
195 | |
196 | if (substream->stream != SNDRV_PCM_STREAM_CAPTURE) |
197 | return -EINVAL; |
198 | |
199 | switch (cmd) { |
200 | case SNDRV_PCM_TRIGGER_START: |
201 | case SNDRV_PCM_TRIGGER_RESUME: |
202 | case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: |
203 | /* DRQ ENABLE */ |
204 | regmap_update_bits(map: host->regmap, SUN50I_DMIC_INTC, |
205 | SUN50I_DMIC_FIFO_DRQ_EN, |
206 | SUN50I_DMIC_FIFO_DRQ_EN); |
207 | /* Global enable */ |
208 | regmap_update_bits(map: host->regmap, SUN50I_DMIC_EN_CTL, |
209 | SUN50I_DMIC_EN_CTL_GLOBE, |
210 | SUN50I_DMIC_EN_CTL_GLOBE); |
211 | break; |
212 | case SNDRV_PCM_TRIGGER_STOP: |
213 | case SNDRV_PCM_TRIGGER_SUSPEND: |
214 | case SNDRV_PCM_TRIGGER_PAUSE_PUSH: |
215 | /* DRQ DISABLE */ |
216 | regmap_update_bits(map: host->regmap, SUN50I_DMIC_INTC, |
217 | SUN50I_DMIC_FIFO_DRQ_EN, val: 0); |
218 | /* Global disable */ |
219 | regmap_update_bits(map: host->regmap, SUN50I_DMIC_EN_CTL, |
220 | SUN50I_DMIC_EN_CTL_GLOBE, val: 0); |
221 | break; |
222 | default: |
223 | ret = -EINVAL; |
224 | break; |
225 | } |
226 | return ret; |
227 | } |
228 | |
229 | static int sun50i_dmic_soc_dai_probe(struct snd_soc_dai *dai) |
230 | { |
231 | struct sun50i_dmic_dev *host = snd_soc_dai_get_drvdata(dai); |
232 | |
233 | snd_soc_dai_init_dma_data(dai, NULL, capture: &host->dma_params_rx); |
234 | |
235 | return 0; |
236 | } |
237 | |
238 | static const struct snd_soc_dai_ops sun50i_dmic_dai_ops = { |
239 | .probe = sun50i_dmic_soc_dai_probe, |
240 | .startup = sun50i_dmic_startup, |
241 | .trigger = sun50i_dmic_trigger, |
242 | .hw_params = sun50i_dmic_hw_params, |
243 | }; |
244 | |
245 | static const struct regmap_config sun50i_dmic_regmap_config = { |
246 | .reg_bits = 32, |
247 | .reg_stride = 4, |
248 | .val_bits = 32, |
249 | .max_register = SUN50I_DMIC_VERSION, |
250 | .cache_type = REGCACHE_NONE, |
251 | }; |
252 | |
253 | #define SUN50I_DMIC_RATES (SNDRV_PCM_RATE_8000_48000) |
254 | #define SUN50I_DMIC_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE) |
255 | |
256 | static struct snd_soc_dai_driver sun50i_dmic_dai = { |
257 | .capture = { |
258 | .channels_min = 1, |
259 | .channels_max = 8, |
260 | .rates = SUN50I_DMIC_RATES, |
261 | .formats = SUN50I_DMIC_FORMATS, |
262 | .sig_bits = 21, |
263 | }, |
264 | .ops = &sun50i_dmic_dai_ops, |
265 | .name = "dmic" , |
266 | }; |
267 | |
268 | static const struct of_device_id sun50i_dmic_of_match[] = { |
269 | { |
270 | .compatible = "allwinner,sun50i-h6-dmic" , |
271 | }, |
272 | { /* sentinel */ } |
273 | }; |
274 | MODULE_DEVICE_TABLE(of, sun50i_dmic_of_match); |
275 | |
276 | static const struct snd_soc_component_driver sun50i_dmic_component = { |
277 | .name = "sun50i-dmic" , |
278 | }; |
279 | |
280 | static int sun50i_dmic_runtime_suspend(struct device *dev) |
281 | { |
282 | struct sun50i_dmic_dev *host = dev_get_drvdata(dev); |
283 | |
284 | clk_disable_unprepare(clk: host->dmic_clk); |
285 | clk_disable_unprepare(clk: host->bus_clk); |
286 | |
287 | return 0; |
288 | } |
289 | |
290 | static int sun50i_dmic_runtime_resume(struct device *dev) |
291 | { |
292 | struct sun50i_dmic_dev *host = dev_get_drvdata(dev); |
293 | int ret; |
294 | |
295 | ret = clk_prepare_enable(clk: host->dmic_clk); |
296 | if (ret) |
297 | return ret; |
298 | |
299 | ret = clk_prepare_enable(clk: host->bus_clk); |
300 | if (ret) { |
301 | clk_disable_unprepare(clk: host->dmic_clk); |
302 | return ret; |
303 | } |
304 | |
305 | return 0; |
306 | } |
307 | |
308 | static int sun50i_dmic_probe(struct platform_device *pdev) |
309 | { |
310 | struct sun50i_dmic_dev *host; |
311 | struct resource *res; |
312 | int ret; |
313 | void __iomem *base; |
314 | |
315 | host = devm_kzalloc(dev: &pdev->dev, size: sizeof(*host), GFP_KERNEL); |
316 | if (!host) |
317 | return -ENOMEM; |
318 | |
319 | /* Get the addresses */ |
320 | base = devm_platform_get_and_ioremap_resource(pdev, index: 0, res: &res); |
321 | if (IS_ERR(ptr: base)) |
322 | return dev_err_probe(dev: &pdev->dev, err: PTR_ERR(ptr: base), |
323 | fmt: "get resource failed.\n" ); |
324 | |
325 | host->regmap = devm_regmap_init_mmio(&pdev->dev, base, |
326 | &sun50i_dmic_regmap_config); |
327 | |
328 | /* Clocks */ |
329 | host->bus_clk = devm_clk_get(dev: &pdev->dev, id: "bus" ); |
330 | if (IS_ERR(ptr: host->bus_clk)) |
331 | return dev_err_probe(dev: &pdev->dev, err: PTR_ERR(ptr: host->bus_clk), |
332 | fmt: "failed to get bus clock.\n" ); |
333 | |
334 | host->dmic_clk = devm_clk_get(dev: &pdev->dev, id: "mod" ); |
335 | if (IS_ERR(ptr: host->dmic_clk)) |
336 | return dev_err_probe(dev: &pdev->dev, err: PTR_ERR(ptr: host->dmic_clk), |
337 | fmt: "failed to get dmic clock.\n" ); |
338 | |
339 | host->dma_params_rx.addr = res->start + SUN50I_DMIC_DATA; |
340 | host->dma_params_rx.maxburst = 8; |
341 | |
342 | platform_set_drvdata(pdev, data: host); |
343 | |
344 | host->rst = devm_reset_control_get_optional_exclusive(dev: &pdev->dev, NULL); |
345 | if (IS_ERR(ptr: host->rst)) |
346 | return dev_err_probe(dev: &pdev->dev, err: PTR_ERR(ptr: host->rst), |
347 | fmt: "Failed to get reset.\n" ); |
348 | reset_control_deassert(rstc: host->rst); |
349 | |
350 | ret = devm_snd_soc_register_component(dev: &pdev->dev, component_driver: &sun50i_dmic_component, |
351 | dai_drv: &sun50i_dmic_dai, num_dai: 1); |
352 | if (ret) |
353 | return dev_err_probe(dev: &pdev->dev, err: ret, |
354 | fmt: "failed to register component.\n" ); |
355 | |
356 | pm_runtime_enable(dev: &pdev->dev); |
357 | if (!pm_runtime_enabled(dev: &pdev->dev)) { |
358 | ret = sun50i_dmic_runtime_resume(dev: &pdev->dev); |
359 | if (ret) |
360 | goto err_disable_runtime_pm; |
361 | } |
362 | |
363 | ret = devm_snd_dmaengine_pcm_register(dev: &pdev->dev, NULL, flags: 0); |
364 | if (ret) |
365 | goto err_suspend; |
366 | |
367 | return 0; |
368 | err_suspend: |
369 | if (!pm_runtime_status_suspended(dev: &pdev->dev)) |
370 | sun50i_dmic_runtime_suspend(dev: &pdev->dev); |
371 | err_disable_runtime_pm: |
372 | pm_runtime_disable(dev: &pdev->dev); |
373 | return ret; |
374 | } |
375 | |
376 | static void sun50i_dmic_remove(struct platform_device *pdev) |
377 | { |
378 | pm_runtime_disable(dev: &pdev->dev); |
379 | if (!pm_runtime_status_suspended(dev: &pdev->dev)) |
380 | sun50i_dmic_runtime_suspend(dev: &pdev->dev); |
381 | } |
382 | |
383 | static const struct dev_pm_ops sun50i_dmic_pm = { |
384 | SET_RUNTIME_PM_OPS(sun50i_dmic_runtime_suspend, |
385 | sun50i_dmic_runtime_resume, NULL) |
386 | }; |
387 | |
388 | static struct platform_driver sun50i_dmic_driver = { |
389 | .driver = { |
390 | .name = "sun50i-dmic" , |
391 | .of_match_table = sun50i_dmic_of_match, |
392 | .pm = &sun50i_dmic_pm, |
393 | }, |
394 | .probe = sun50i_dmic_probe, |
395 | .remove_new = sun50i_dmic_remove, |
396 | }; |
397 | |
398 | module_platform_driver(sun50i_dmic_driver); |
399 | |
400 | MODULE_DESCRIPTION("Allwinner sun50i DMIC SoC Interface" ); |
401 | MODULE_AUTHOR("Ban Tao <fengzheng923@gmail.com>" ); |
402 | MODULE_LICENSE("GPL" ); |
403 | MODULE_ALIAS("platform:sun50i-dmic" ); |
404 | |