1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * tegra20_i2s.c - Tegra20 I2S driver |
4 | * |
5 | * Author: Stephen Warren <swarren@nvidia.com> |
6 | * Copyright (C) 2010,2012 - NVIDIA, Inc. |
7 | * |
8 | * Based on code copyright/by: |
9 | * |
10 | * Copyright (c) 2009-2010, NVIDIA Corporation. |
11 | * Scott Peterson <speterson@nvidia.com> |
12 | * |
13 | * Copyright (C) 2010 Google, Inc. |
14 | * Iliyan Malchev <malchev@google.com> |
15 | */ |
16 | |
17 | #include <linux/clk.h> |
18 | #include <linux/device.h> |
19 | #include <linux/io.h> |
20 | #include <linux/module.h> |
21 | #include <linux/of.h> |
22 | #include <linux/platform_device.h> |
23 | #include <linux/pm_runtime.h> |
24 | #include <linux/regmap.h> |
25 | #include <linux/reset.h> |
26 | #include <linux/slab.h> |
27 | #include <sound/core.h> |
28 | #include <sound/pcm.h> |
29 | #include <sound/pcm_params.h> |
30 | #include <sound/soc.h> |
31 | #include <sound/dmaengine_pcm.h> |
32 | |
33 | #include "tegra20_i2s.h" |
34 | |
35 | #define DRV_NAME "tegra20-i2s" |
36 | |
37 | static __maybe_unused int tegra20_i2s_runtime_suspend(struct device *dev) |
38 | { |
39 | struct tegra20_i2s *i2s = dev_get_drvdata(dev); |
40 | |
41 | regcache_cache_only(map: i2s->regmap, enable: true); |
42 | |
43 | clk_disable_unprepare(clk: i2s->clk_i2s); |
44 | |
45 | return 0; |
46 | } |
47 | |
48 | static __maybe_unused int tegra20_i2s_runtime_resume(struct device *dev) |
49 | { |
50 | struct tegra20_i2s *i2s = dev_get_drvdata(dev); |
51 | int ret; |
52 | |
53 | ret = reset_control_assert(rstc: i2s->reset); |
54 | if (ret) |
55 | return ret; |
56 | |
57 | ret = clk_prepare_enable(clk: i2s->clk_i2s); |
58 | if (ret) { |
59 | dev_err(dev, "clk_enable failed: %d\n" , ret); |
60 | return ret; |
61 | } |
62 | |
63 | usleep_range(min: 10, max: 100); |
64 | |
65 | ret = reset_control_deassert(rstc: i2s->reset); |
66 | if (ret) |
67 | goto disable_clocks; |
68 | |
69 | regcache_cache_only(map: i2s->regmap, enable: false); |
70 | regcache_mark_dirty(map: i2s->regmap); |
71 | |
72 | ret = regcache_sync(map: i2s->regmap); |
73 | if (ret) |
74 | goto disable_clocks; |
75 | |
76 | return 0; |
77 | |
78 | disable_clocks: |
79 | clk_disable_unprepare(clk: i2s->clk_i2s); |
80 | |
81 | return ret; |
82 | } |
83 | |
84 | static int tegra20_i2s_set_fmt(struct snd_soc_dai *dai, |
85 | unsigned int fmt) |
86 | { |
87 | struct tegra20_i2s *i2s = snd_soc_dai_get_drvdata(dai); |
88 | unsigned int mask = 0, val = 0; |
89 | |
90 | switch (fmt & SND_SOC_DAIFMT_INV_MASK) { |
91 | case SND_SOC_DAIFMT_NB_NF: |
92 | break; |
93 | default: |
94 | return -EINVAL; |
95 | } |
96 | |
97 | mask |= TEGRA20_I2S_CTRL_MASTER_ENABLE; |
98 | switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { |
99 | case SND_SOC_DAIFMT_BP_FP: |
100 | val |= TEGRA20_I2S_CTRL_MASTER_ENABLE; |
101 | break; |
102 | case SND_SOC_DAIFMT_BC_FC: |
103 | break; |
104 | default: |
105 | return -EINVAL; |
106 | } |
107 | |
108 | mask |= TEGRA20_I2S_CTRL_BIT_FORMAT_MASK | |
109 | TEGRA20_I2S_CTRL_LRCK_MASK; |
110 | switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { |
111 | case SND_SOC_DAIFMT_DSP_A: |
112 | val |= TEGRA20_I2S_CTRL_BIT_FORMAT_DSP; |
113 | val |= TEGRA20_I2S_CTRL_LRCK_L_LOW; |
114 | break; |
115 | case SND_SOC_DAIFMT_DSP_B: |
116 | val |= TEGRA20_I2S_CTRL_BIT_FORMAT_DSP; |
117 | val |= TEGRA20_I2S_CTRL_LRCK_R_LOW; |
118 | break; |
119 | case SND_SOC_DAIFMT_I2S: |
120 | val |= TEGRA20_I2S_CTRL_BIT_FORMAT_I2S; |
121 | val |= TEGRA20_I2S_CTRL_LRCK_L_LOW; |
122 | break; |
123 | case SND_SOC_DAIFMT_RIGHT_J: |
124 | val |= TEGRA20_I2S_CTRL_BIT_FORMAT_RJM; |
125 | val |= TEGRA20_I2S_CTRL_LRCK_L_LOW; |
126 | break; |
127 | case SND_SOC_DAIFMT_LEFT_J: |
128 | val |= TEGRA20_I2S_CTRL_BIT_FORMAT_LJM; |
129 | val |= TEGRA20_I2S_CTRL_LRCK_L_LOW; |
130 | break; |
131 | default: |
132 | return -EINVAL; |
133 | } |
134 | |
135 | regmap_update_bits(map: i2s->regmap, TEGRA20_I2S_CTRL, mask, val); |
136 | |
137 | return 0; |
138 | } |
139 | |
140 | static int tegra20_i2s_hw_params(struct snd_pcm_substream *substream, |
141 | struct snd_pcm_hw_params *params, |
142 | struct snd_soc_dai *dai) |
143 | { |
144 | struct device *dev = dai->dev; |
145 | struct tegra20_i2s *i2s = snd_soc_dai_get_drvdata(dai); |
146 | unsigned int mask, val; |
147 | int ret, sample_size, srate, i2sclock, bitcnt; |
148 | |
149 | mask = TEGRA20_I2S_CTRL_BIT_SIZE_MASK; |
150 | switch (params_format(p: params)) { |
151 | case SNDRV_PCM_FORMAT_S16_LE: |
152 | val = TEGRA20_I2S_CTRL_BIT_SIZE_16; |
153 | sample_size = 16; |
154 | break; |
155 | case SNDRV_PCM_FORMAT_S24_LE: |
156 | val = TEGRA20_I2S_CTRL_BIT_SIZE_24; |
157 | sample_size = 24; |
158 | break; |
159 | case SNDRV_PCM_FORMAT_S32_LE: |
160 | val = TEGRA20_I2S_CTRL_BIT_SIZE_32; |
161 | sample_size = 32; |
162 | break; |
163 | default: |
164 | return -EINVAL; |
165 | } |
166 | |
167 | mask |= TEGRA20_I2S_CTRL_FIFO_FORMAT_MASK; |
168 | val |= TEGRA20_I2S_CTRL_FIFO_FORMAT_PACKED; |
169 | |
170 | regmap_update_bits(map: i2s->regmap, TEGRA20_I2S_CTRL, mask, val); |
171 | |
172 | srate = params_rate(p: params); |
173 | |
174 | /* Final "* 2" required by Tegra hardware */ |
175 | i2sclock = srate * params_channels(p: params) * sample_size * 2; |
176 | |
177 | ret = clk_set_rate(clk: i2s->clk_i2s, rate: i2sclock); |
178 | if (ret) { |
179 | dev_err(dev, "Can't set I2S clock rate: %d\n" , ret); |
180 | return ret; |
181 | } |
182 | |
183 | bitcnt = (i2sclock / (2 * srate)) - 1; |
184 | if (bitcnt < 0 || bitcnt > TEGRA20_I2S_TIMING_CHANNEL_BIT_COUNT_MASK_US) |
185 | return -EINVAL; |
186 | val = bitcnt << TEGRA20_I2S_TIMING_CHANNEL_BIT_COUNT_SHIFT; |
187 | |
188 | if (i2sclock % (2 * srate)) |
189 | val |= TEGRA20_I2S_TIMING_NON_SYM_ENABLE; |
190 | |
191 | regmap_write(map: i2s->regmap, TEGRA20_I2S_TIMING, val); |
192 | |
193 | regmap_write(map: i2s->regmap, TEGRA20_I2S_FIFO_SCR, |
194 | TEGRA20_I2S_FIFO_SCR_FIFO2_ATN_LVL_FOUR_SLOTS | |
195 | TEGRA20_I2S_FIFO_SCR_FIFO1_ATN_LVL_FOUR_SLOTS); |
196 | |
197 | return 0; |
198 | } |
199 | |
200 | static void tegra20_i2s_start_playback(struct tegra20_i2s *i2s) |
201 | { |
202 | regmap_update_bits(map: i2s->regmap, TEGRA20_I2S_CTRL, |
203 | TEGRA20_I2S_CTRL_FIFO1_ENABLE, |
204 | TEGRA20_I2S_CTRL_FIFO1_ENABLE); |
205 | } |
206 | |
207 | static void tegra20_i2s_stop_playback(struct tegra20_i2s *i2s) |
208 | { |
209 | regmap_update_bits(map: i2s->regmap, TEGRA20_I2S_CTRL, |
210 | TEGRA20_I2S_CTRL_FIFO1_ENABLE, val: 0); |
211 | } |
212 | |
213 | static void tegra20_i2s_start_capture(struct tegra20_i2s *i2s) |
214 | { |
215 | regmap_update_bits(map: i2s->regmap, TEGRA20_I2S_CTRL, |
216 | TEGRA20_I2S_CTRL_FIFO2_ENABLE, |
217 | TEGRA20_I2S_CTRL_FIFO2_ENABLE); |
218 | } |
219 | |
220 | static void tegra20_i2s_stop_capture(struct tegra20_i2s *i2s) |
221 | { |
222 | regmap_update_bits(map: i2s->regmap, TEGRA20_I2S_CTRL, |
223 | TEGRA20_I2S_CTRL_FIFO2_ENABLE, val: 0); |
224 | } |
225 | |
226 | static int tegra20_i2s_trigger(struct snd_pcm_substream *substream, int cmd, |
227 | struct snd_soc_dai *dai) |
228 | { |
229 | struct tegra20_i2s *i2s = snd_soc_dai_get_drvdata(dai); |
230 | |
231 | switch (cmd) { |
232 | case SNDRV_PCM_TRIGGER_START: |
233 | case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: |
234 | case SNDRV_PCM_TRIGGER_RESUME: |
235 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) |
236 | tegra20_i2s_start_playback(i2s); |
237 | else |
238 | tegra20_i2s_start_capture(i2s); |
239 | break; |
240 | case SNDRV_PCM_TRIGGER_STOP: |
241 | case SNDRV_PCM_TRIGGER_PAUSE_PUSH: |
242 | case SNDRV_PCM_TRIGGER_SUSPEND: |
243 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) |
244 | tegra20_i2s_stop_playback(i2s); |
245 | else |
246 | tegra20_i2s_stop_capture(i2s); |
247 | break; |
248 | default: |
249 | return -EINVAL; |
250 | } |
251 | |
252 | return 0; |
253 | } |
254 | |
255 | static int tegra20_i2s_probe(struct snd_soc_dai *dai) |
256 | { |
257 | struct tegra20_i2s *i2s = snd_soc_dai_get_drvdata(dai); |
258 | |
259 | snd_soc_dai_init_dma_data(dai, playback: &i2s->playback_dma_data, |
260 | capture: &i2s->capture_dma_data); |
261 | |
262 | return 0; |
263 | } |
264 | |
265 | static const unsigned int tegra20_i2s_rates[] = { |
266 | 8000, 11025, 16000, 22050, 32000, 44100, 48000, 64000, 88200, 96000 |
267 | }; |
268 | |
269 | static int tegra20_i2s_filter_rates(struct snd_pcm_hw_params *params, |
270 | struct snd_pcm_hw_rule *rule) |
271 | { |
272 | struct snd_interval *r = hw_param_interval(params, var: rule->var); |
273 | struct snd_soc_dai *dai = rule->private; |
274 | struct tegra20_i2s *i2s = dev_get_drvdata(dev: dai->dev); |
275 | struct clk *parent = clk_get_parent(clk: i2s->clk_i2s); |
276 | unsigned long i, parent_rate, valid_rates = 0; |
277 | |
278 | parent_rate = clk_get_rate(clk: parent); |
279 | if (!parent_rate) { |
280 | dev_err(dai->dev, "Can't get parent clock rate\n" ); |
281 | return -EINVAL; |
282 | } |
283 | |
284 | for (i = 0; i < ARRAY_SIZE(tegra20_i2s_rates); i++) { |
285 | if (parent_rate % (tegra20_i2s_rates[i] * 128) == 0) |
286 | valid_rates |= BIT(i); |
287 | } |
288 | |
289 | /* |
290 | * At least one rate must be valid, otherwise the parent clock isn't |
291 | * audio PLL. Nothing should be filtered in this case. |
292 | */ |
293 | if (!valid_rates) |
294 | valid_rates = BIT(ARRAY_SIZE(tegra20_i2s_rates)) - 1; |
295 | |
296 | return snd_interval_list(i: r, ARRAY_SIZE(tegra20_i2s_rates), |
297 | list: tegra20_i2s_rates, mask: valid_rates); |
298 | } |
299 | |
300 | static int tegra20_i2s_startup(struct snd_pcm_substream *substream, |
301 | struct snd_soc_dai *dai) |
302 | { |
303 | if (!device_property_read_bool(dev: dai->dev, propname: "nvidia,fixed-parent-rate" )) |
304 | return 0; |
305 | |
306 | return snd_pcm_hw_rule_add(runtime: substream->runtime, cond: 0, |
307 | SNDRV_PCM_HW_PARAM_RATE, |
308 | func: tegra20_i2s_filter_rates, private: dai, |
309 | SNDRV_PCM_HW_PARAM_RATE, -1); |
310 | } |
311 | |
312 | static const struct snd_soc_dai_ops tegra20_i2s_dai_ops = { |
313 | .probe = tegra20_i2s_probe, |
314 | .set_fmt = tegra20_i2s_set_fmt, |
315 | .hw_params = tegra20_i2s_hw_params, |
316 | .trigger = tegra20_i2s_trigger, |
317 | .startup = tegra20_i2s_startup, |
318 | }; |
319 | |
320 | static const struct snd_soc_dai_driver tegra20_i2s_dai_template = { |
321 | .playback = { |
322 | .stream_name = "Playback" , |
323 | .channels_min = 2, |
324 | .channels_max = 2, |
325 | .rates = SNDRV_PCM_RATE_8000_96000, |
326 | .formats = SNDRV_PCM_FMTBIT_S16_LE, |
327 | }, |
328 | .capture = { |
329 | .stream_name = "Capture" , |
330 | .channels_min = 2, |
331 | .channels_max = 2, |
332 | .rates = SNDRV_PCM_RATE_8000_96000, |
333 | .formats = SNDRV_PCM_FMTBIT_S16_LE, |
334 | }, |
335 | .ops = &tegra20_i2s_dai_ops, |
336 | .symmetric_rate = 1, |
337 | }; |
338 | |
339 | static const struct snd_soc_component_driver tegra20_i2s_component = { |
340 | .name = DRV_NAME, |
341 | .legacy_dai_naming = 1, |
342 | }; |
343 | |
344 | static bool tegra20_i2s_wr_rd_reg(struct device *dev, unsigned int reg) |
345 | { |
346 | switch (reg) { |
347 | case TEGRA20_I2S_CTRL: |
348 | case TEGRA20_I2S_STATUS: |
349 | case TEGRA20_I2S_TIMING: |
350 | case TEGRA20_I2S_FIFO_SCR: |
351 | case TEGRA20_I2S_PCM_CTRL: |
352 | case TEGRA20_I2S_NW_CTRL: |
353 | case TEGRA20_I2S_TDM_CTRL: |
354 | case TEGRA20_I2S_TDM_TX_RX_CTRL: |
355 | case TEGRA20_I2S_FIFO1: |
356 | case TEGRA20_I2S_FIFO2: |
357 | return true; |
358 | default: |
359 | return false; |
360 | } |
361 | } |
362 | |
363 | static bool tegra20_i2s_volatile_reg(struct device *dev, unsigned int reg) |
364 | { |
365 | switch (reg) { |
366 | case TEGRA20_I2S_STATUS: |
367 | case TEGRA20_I2S_FIFO_SCR: |
368 | case TEGRA20_I2S_FIFO1: |
369 | case TEGRA20_I2S_FIFO2: |
370 | return true; |
371 | default: |
372 | return false; |
373 | } |
374 | } |
375 | |
376 | static bool tegra20_i2s_precious_reg(struct device *dev, unsigned int reg) |
377 | { |
378 | switch (reg) { |
379 | case TEGRA20_I2S_FIFO1: |
380 | case TEGRA20_I2S_FIFO2: |
381 | return true; |
382 | default: |
383 | return false; |
384 | } |
385 | } |
386 | |
387 | static const struct regmap_config tegra20_i2s_regmap_config = { |
388 | .reg_bits = 32, |
389 | .reg_stride = 4, |
390 | .val_bits = 32, |
391 | .max_register = TEGRA20_I2S_FIFO2, |
392 | .writeable_reg = tegra20_i2s_wr_rd_reg, |
393 | .readable_reg = tegra20_i2s_wr_rd_reg, |
394 | .volatile_reg = tegra20_i2s_volatile_reg, |
395 | .precious_reg = tegra20_i2s_precious_reg, |
396 | .cache_type = REGCACHE_FLAT, |
397 | }; |
398 | |
399 | static int tegra20_i2s_platform_probe(struct platform_device *pdev) |
400 | { |
401 | struct tegra20_i2s *i2s; |
402 | struct resource *mem; |
403 | void __iomem *regs; |
404 | int ret; |
405 | |
406 | i2s = devm_kzalloc(dev: &pdev->dev, size: sizeof(struct tegra20_i2s), GFP_KERNEL); |
407 | if (!i2s) { |
408 | ret = -ENOMEM; |
409 | goto err; |
410 | } |
411 | dev_set_drvdata(dev: &pdev->dev, data: i2s); |
412 | |
413 | i2s->dai = tegra20_i2s_dai_template; |
414 | i2s->dai.name = dev_name(dev: &pdev->dev); |
415 | |
416 | i2s->reset = devm_reset_control_get_exclusive(dev: &pdev->dev, id: "i2s" ); |
417 | if (IS_ERR(ptr: i2s->reset)) { |
418 | dev_err(&pdev->dev, "Can't retrieve i2s reset\n" ); |
419 | return PTR_ERR(ptr: i2s->reset); |
420 | } |
421 | |
422 | i2s->clk_i2s = devm_clk_get(dev: &pdev->dev, NULL); |
423 | if (IS_ERR(ptr: i2s->clk_i2s)) { |
424 | dev_err(&pdev->dev, "Can't retrieve i2s clock\n" ); |
425 | ret = PTR_ERR(ptr: i2s->clk_i2s); |
426 | goto err; |
427 | } |
428 | |
429 | regs = devm_platform_get_and_ioremap_resource(pdev, index: 0, res: &mem); |
430 | if (IS_ERR(ptr: regs)) { |
431 | ret = PTR_ERR(ptr: regs); |
432 | goto err; |
433 | } |
434 | |
435 | i2s->regmap = devm_regmap_init_mmio(&pdev->dev, regs, |
436 | &tegra20_i2s_regmap_config); |
437 | if (IS_ERR(ptr: i2s->regmap)) { |
438 | dev_err(&pdev->dev, "regmap init failed\n" ); |
439 | ret = PTR_ERR(ptr: i2s->regmap); |
440 | goto err; |
441 | } |
442 | |
443 | i2s->capture_dma_data.addr = mem->start + TEGRA20_I2S_FIFO2; |
444 | i2s->capture_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; |
445 | i2s->capture_dma_data.maxburst = 4; |
446 | |
447 | i2s->playback_dma_data.addr = mem->start + TEGRA20_I2S_FIFO1; |
448 | i2s->playback_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; |
449 | i2s->playback_dma_data.maxburst = 4; |
450 | |
451 | pm_runtime_enable(dev: &pdev->dev); |
452 | |
453 | ret = snd_soc_register_component(dev: &pdev->dev, component_driver: &tegra20_i2s_component, |
454 | dai_drv: &i2s->dai, num_dai: 1); |
455 | if (ret) { |
456 | dev_err(&pdev->dev, "Could not register DAI: %d\n" , ret); |
457 | ret = -ENOMEM; |
458 | goto err_pm_disable; |
459 | } |
460 | |
461 | ret = tegra_pcm_platform_register(dev: &pdev->dev); |
462 | if (ret) { |
463 | dev_err(&pdev->dev, "Could not register PCM: %d\n" , ret); |
464 | goto err_unregister_component; |
465 | } |
466 | |
467 | return 0; |
468 | |
469 | err_unregister_component: |
470 | snd_soc_unregister_component(dev: &pdev->dev); |
471 | err_pm_disable: |
472 | pm_runtime_disable(dev: &pdev->dev); |
473 | err: |
474 | return ret; |
475 | } |
476 | |
477 | static void tegra20_i2s_platform_remove(struct platform_device *pdev) |
478 | { |
479 | tegra_pcm_platform_unregister(dev: &pdev->dev); |
480 | snd_soc_unregister_component(dev: &pdev->dev); |
481 | pm_runtime_disable(dev: &pdev->dev); |
482 | } |
483 | |
484 | static const struct of_device_id tegra20_i2s_of_match[] = { |
485 | { .compatible = "nvidia,tegra20-i2s" , }, |
486 | {}, |
487 | }; |
488 | |
489 | static const struct dev_pm_ops tegra20_i2s_pm_ops = { |
490 | SET_RUNTIME_PM_OPS(tegra20_i2s_runtime_suspend, |
491 | tegra20_i2s_runtime_resume, NULL) |
492 | SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, |
493 | pm_runtime_force_resume) |
494 | }; |
495 | |
496 | static struct platform_driver tegra20_i2s_driver = { |
497 | .driver = { |
498 | .name = DRV_NAME, |
499 | .of_match_table = tegra20_i2s_of_match, |
500 | .pm = &tegra20_i2s_pm_ops, |
501 | }, |
502 | .probe = tegra20_i2s_platform_probe, |
503 | .remove_new = tegra20_i2s_platform_remove, |
504 | }; |
505 | module_platform_driver(tegra20_i2s_driver); |
506 | |
507 | MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>" ); |
508 | MODULE_DESCRIPTION("Tegra20 I2S ASoC driver" ); |
509 | MODULE_LICENSE("GPL" ); |
510 | MODULE_ALIAS("platform:" DRV_NAME); |
511 | MODULE_DEVICE_TABLE(of, tegra20_i2s_of_match); |
512 | |