1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | // ALSA SoC Audio Layer - Rockchip I2S/TDM Controller driver |
3 | |
4 | // Copyright (c) 2018 Rockchip Electronics Co. Ltd. |
5 | // Author: Sugar Zhang <sugar.zhang@rock-chips.com> |
6 | // Author: Nicolas Frattaroli <frattaroli.nicolas@gmail.com> |
7 | |
8 | #include <linux/clk.h> |
9 | #include <linux/clk-provider.h> |
10 | #include <linux/delay.h> |
11 | #include <linux/mfd/syscon.h> |
12 | #include <linux/module.h> |
13 | #include <linux/of.h> |
14 | #include <linux/pm_runtime.h> |
15 | #include <linux/regmap.h> |
16 | #include <linux/reset.h> |
17 | #include <linux/spinlock.h> |
18 | #include <sound/dmaengine_pcm.h> |
19 | #include <sound/pcm_params.h> |
20 | |
21 | #include "rockchip_i2s_tdm.h" |
22 | |
23 | #define DRV_NAME "rockchip-i2s-tdm" |
24 | |
25 | #define DEFAULT_MCLK_FS 256 |
26 | #define CH_GRP_MAX 4 /* The max channel 8 / 2 */ |
27 | #define MULTIPLEX_CH_MAX 10 |
28 | |
29 | #define TRCM_TXRX 0 |
30 | #define TRCM_TX 1 |
31 | #define TRCM_RX 2 |
32 | |
33 | struct txrx_config { |
34 | u32 addr; |
35 | u32 reg; |
36 | u32 txonly; |
37 | u32 rxonly; |
38 | }; |
39 | |
40 | struct rk_i2s_soc_data { |
41 | u32 softrst_offset; |
42 | u32 grf_reg_offset; |
43 | u32 grf_shift; |
44 | int config_count; |
45 | const struct txrx_config *configs; |
46 | int (*init)(struct device *dev, u32 addr); |
47 | }; |
48 | |
49 | struct rk_i2s_tdm_dev { |
50 | struct device *dev; |
51 | struct clk *hclk; |
52 | struct clk *mclk_tx; |
53 | struct clk *mclk_rx; |
54 | struct regmap *regmap; |
55 | struct regmap *grf; |
56 | struct snd_dmaengine_dai_dma_data capture_dma_data; |
57 | struct snd_dmaengine_dai_dma_data playback_dma_data; |
58 | struct reset_control *tx_reset; |
59 | struct reset_control *rx_reset; |
60 | const struct rk_i2s_soc_data *soc_data; |
61 | bool is_master_mode; |
62 | bool io_multiplex; |
63 | bool tdm_mode; |
64 | unsigned int frame_width; |
65 | unsigned int clk_trcm; |
66 | unsigned int i2s_sdis[CH_GRP_MAX]; |
67 | unsigned int i2s_sdos[CH_GRP_MAX]; |
68 | int refcount; |
69 | spinlock_t lock; /* xfer lock */ |
70 | bool has_playback; |
71 | bool has_capture; |
72 | struct snd_soc_dai_driver *dai; |
73 | }; |
74 | |
75 | static int to_ch_num(unsigned int val) |
76 | { |
77 | switch (val) { |
78 | case I2S_CHN_4: |
79 | return 4; |
80 | case I2S_CHN_6: |
81 | return 6; |
82 | case I2S_CHN_8: |
83 | return 8; |
84 | default: |
85 | return 2; |
86 | } |
87 | } |
88 | |
89 | static void i2s_tdm_disable_unprepare_mclk(struct rk_i2s_tdm_dev *i2s_tdm) |
90 | { |
91 | clk_disable_unprepare(clk: i2s_tdm->mclk_tx); |
92 | clk_disable_unprepare(clk: i2s_tdm->mclk_rx); |
93 | } |
94 | |
95 | /** |
96 | * i2s_tdm_prepare_enable_mclk - prepare to enable all mclks, disable them on |
97 | * failure. |
98 | * @i2s_tdm: rk_i2s_tdm_dev struct |
99 | * |
100 | * This function attempts to enable all mclk clocks, but cleans up after |
101 | * itself on failure. Guarantees to balance its calls. |
102 | * |
103 | * Returns success (0) or negative errno. |
104 | */ |
105 | static int i2s_tdm_prepare_enable_mclk(struct rk_i2s_tdm_dev *i2s_tdm) |
106 | { |
107 | int ret = 0; |
108 | |
109 | ret = clk_prepare_enable(clk: i2s_tdm->mclk_tx); |
110 | if (ret) |
111 | goto err_mclk_tx; |
112 | ret = clk_prepare_enable(clk: i2s_tdm->mclk_rx); |
113 | if (ret) |
114 | goto err_mclk_rx; |
115 | |
116 | return 0; |
117 | |
118 | err_mclk_rx: |
119 | clk_disable_unprepare(clk: i2s_tdm->mclk_tx); |
120 | err_mclk_tx: |
121 | return ret; |
122 | } |
123 | |
124 | static int __maybe_unused i2s_tdm_runtime_suspend(struct device *dev) |
125 | { |
126 | struct rk_i2s_tdm_dev *i2s_tdm = dev_get_drvdata(dev); |
127 | |
128 | regcache_cache_only(map: i2s_tdm->regmap, enable: true); |
129 | i2s_tdm_disable_unprepare_mclk(i2s_tdm); |
130 | |
131 | clk_disable_unprepare(clk: i2s_tdm->hclk); |
132 | |
133 | return 0; |
134 | } |
135 | |
136 | static int __maybe_unused i2s_tdm_runtime_resume(struct device *dev) |
137 | { |
138 | struct rk_i2s_tdm_dev *i2s_tdm = dev_get_drvdata(dev); |
139 | int ret; |
140 | |
141 | ret = clk_prepare_enable(clk: i2s_tdm->hclk); |
142 | if (ret) |
143 | goto err_hclk; |
144 | |
145 | ret = i2s_tdm_prepare_enable_mclk(i2s_tdm); |
146 | if (ret) |
147 | goto err_mclk; |
148 | |
149 | regcache_cache_only(map: i2s_tdm->regmap, enable: false); |
150 | regcache_mark_dirty(map: i2s_tdm->regmap); |
151 | |
152 | ret = regcache_sync(map: i2s_tdm->regmap); |
153 | if (ret) |
154 | goto err_regcache; |
155 | |
156 | return 0; |
157 | |
158 | err_regcache: |
159 | i2s_tdm_disable_unprepare_mclk(i2s_tdm); |
160 | err_mclk: |
161 | clk_disable_unprepare(clk: i2s_tdm->hclk); |
162 | err_hclk: |
163 | return ret; |
164 | } |
165 | |
166 | static inline struct rk_i2s_tdm_dev *to_info(struct snd_soc_dai *dai) |
167 | { |
168 | return snd_soc_dai_get_drvdata(dai); |
169 | } |
170 | |
171 | /* |
172 | * Makes sure that both tx and rx are reset at the same time to sync lrck |
173 | * when clk_trcm > 0. |
174 | */ |
175 | static void rockchip_snd_xfer_sync_reset(struct rk_i2s_tdm_dev *i2s_tdm) |
176 | { |
177 | /* This is technically race-y. |
178 | * |
179 | * In an ideal world, we could atomically assert both resets at the |
180 | * same time, through an atomic bulk reset API. This API however does |
181 | * not exist, so what the downstream vendor code used to do was |
182 | * implement half a reset controller here and require the CRU to be |
183 | * passed to the driver as a device tree node. Violating abstractions |
184 | * like that is bad, especially when it influences something like the |
185 | * bindings which are supposed to describe the hardware, not whatever |
186 | * workarounds the driver needs, so it was dropped. |
187 | * |
188 | * In practice, asserting the resets one by one appears to work just |
189 | * fine for playback. During duplex (playback + capture) operation, |
190 | * this might become an issue, but that should be solved by the |
191 | * implementation of the aforementioned API, not by shoving a reset |
192 | * controller into an audio driver. |
193 | */ |
194 | |
195 | reset_control_assert(rstc: i2s_tdm->tx_reset); |
196 | reset_control_assert(rstc: i2s_tdm->rx_reset); |
197 | udelay(10); |
198 | reset_control_deassert(rstc: i2s_tdm->tx_reset); |
199 | reset_control_deassert(rstc: i2s_tdm->rx_reset); |
200 | udelay(10); |
201 | } |
202 | |
203 | static void rockchip_snd_reset(struct reset_control *rc) |
204 | { |
205 | reset_control_assert(rstc: rc); |
206 | udelay(10); |
207 | reset_control_deassert(rstc: rc); |
208 | udelay(10); |
209 | } |
210 | |
211 | static void rockchip_snd_xfer_clear(struct rk_i2s_tdm_dev *i2s_tdm, |
212 | unsigned int clr) |
213 | { |
214 | unsigned int xfer_mask = 0; |
215 | unsigned int xfer_val = 0; |
216 | unsigned int val; |
217 | int retry = 10; |
218 | bool tx = clr & I2S_CLR_TXC; |
219 | bool rx = clr & I2S_CLR_RXC; |
220 | |
221 | if (!(rx || tx)) |
222 | return; |
223 | |
224 | if (tx) { |
225 | xfer_mask = I2S_XFER_TXS_START; |
226 | xfer_val = I2S_XFER_TXS_STOP; |
227 | } |
228 | if (rx) { |
229 | xfer_mask |= I2S_XFER_RXS_START; |
230 | xfer_val |= I2S_XFER_RXS_STOP; |
231 | } |
232 | |
233 | regmap_update_bits(map: i2s_tdm->regmap, I2S_XFER, mask: xfer_mask, val: xfer_val); |
234 | udelay(150); |
235 | regmap_update_bits(map: i2s_tdm->regmap, I2S_CLR, mask: clr, val: clr); |
236 | |
237 | regmap_read(map: i2s_tdm->regmap, I2S_CLR, val: &val); |
238 | /* Wait on the clear operation to finish */ |
239 | while (val) { |
240 | udelay(15); |
241 | regmap_read(map: i2s_tdm->regmap, I2S_CLR, val: &val); |
242 | retry--; |
243 | if (!retry) { |
244 | dev_warn(i2s_tdm->dev, "clear failed, reset %s%s\n" , |
245 | tx ? "tx" : "" , rx ? "rx" : "" ); |
246 | if (rx && tx) |
247 | rockchip_snd_xfer_sync_reset(i2s_tdm); |
248 | else if (tx) |
249 | rockchip_snd_reset(rc: i2s_tdm->tx_reset); |
250 | else if (rx) |
251 | rockchip_snd_reset(rc: i2s_tdm->rx_reset); |
252 | break; |
253 | } |
254 | } |
255 | } |
256 | |
257 | static inline void rockchip_enable_tde(struct regmap *regmap) |
258 | { |
259 | regmap_update_bits(map: regmap, I2S_DMACR, I2S_DMACR_TDE_ENABLE, |
260 | I2S_DMACR_TDE_ENABLE); |
261 | } |
262 | |
263 | static inline void rockchip_disable_tde(struct regmap *regmap) |
264 | { |
265 | regmap_update_bits(map: regmap, I2S_DMACR, I2S_DMACR_TDE_ENABLE, |
266 | I2S_DMACR_TDE_DISABLE); |
267 | } |
268 | |
269 | static inline void rockchip_enable_rde(struct regmap *regmap) |
270 | { |
271 | regmap_update_bits(map: regmap, I2S_DMACR, I2S_DMACR_RDE_ENABLE, |
272 | I2S_DMACR_RDE_ENABLE); |
273 | } |
274 | |
275 | static inline void rockchip_disable_rde(struct regmap *regmap) |
276 | { |
277 | regmap_update_bits(map: regmap, I2S_DMACR, I2S_DMACR_RDE_ENABLE, |
278 | I2S_DMACR_RDE_DISABLE); |
279 | } |
280 | |
281 | /* only used when clk_trcm > 0 */ |
282 | static void rockchip_snd_txrxctrl(struct snd_pcm_substream *substream, |
283 | struct snd_soc_dai *dai, int on) |
284 | { |
285 | struct rk_i2s_tdm_dev *i2s_tdm = to_info(dai); |
286 | unsigned long flags; |
287 | |
288 | spin_lock_irqsave(&i2s_tdm->lock, flags); |
289 | if (on) { |
290 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) |
291 | rockchip_enable_tde(regmap: i2s_tdm->regmap); |
292 | else |
293 | rockchip_enable_rde(regmap: i2s_tdm->regmap); |
294 | |
295 | if (++i2s_tdm->refcount == 1) { |
296 | rockchip_snd_xfer_sync_reset(i2s_tdm); |
297 | regmap_update_bits(map: i2s_tdm->regmap, I2S_XFER, |
298 | I2S_XFER_TXS_START | |
299 | I2S_XFER_RXS_START, |
300 | I2S_XFER_TXS_START | |
301 | I2S_XFER_RXS_START); |
302 | } |
303 | } else { |
304 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) |
305 | rockchip_disable_tde(regmap: i2s_tdm->regmap); |
306 | else |
307 | rockchip_disable_rde(regmap: i2s_tdm->regmap); |
308 | |
309 | if (--i2s_tdm->refcount == 0) { |
310 | rockchip_snd_xfer_clear(i2s_tdm, |
311 | I2S_CLR_TXC | I2S_CLR_RXC); |
312 | } |
313 | } |
314 | spin_unlock_irqrestore(lock: &i2s_tdm->lock, flags); |
315 | } |
316 | |
317 | static void rockchip_snd_txctrl(struct rk_i2s_tdm_dev *i2s_tdm, int on) |
318 | { |
319 | if (on) { |
320 | rockchip_enable_tde(regmap: i2s_tdm->regmap); |
321 | |
322 | regmap_update_bits(map: i2s_tdm->regmap, I2S_XFER, |
323 | I2S_XFER_TXS_START, |
324 | I2S_XFER_TXS_START); |
325 | } else { |
326 | rockchip_disable_tde(regmap: i2s_tdm->regmap); |
327 | |
328 | rockchip_snd_xfer_clear(i2s_tdm, I2S_CLR_TXC); |
329 | } |
330 | } |
331 | |
332 | static void rockchip_snd_rxctrl(struct rk_i2s_tdm_dev *i2s_tdm, int on) |
333 | { |
334 | if (on) { |
335 | rockchip_enable_rde(regmap: i2s_tdm->regmap); |
336 | |
337 | regmap_update_bits(map: i2s_tdm->regmap, I2S_XFER, |
338 | I2S_XFER_RXS_START, |
339 | I2S_XFER_RXS_START); |
340 | } else { |
341 | rockchip_disable_rde(regmap: i2s_tdm->regmap); |
342 | |
343 | rockchip_snd_xfer_clear(i2s_tdm, I2S_CLR_RXC); |
344 | } |
345 | } |
346 | |
347 | static int rockchip_i2s_tdm_set_fmt(struct snd_soc_dai *cpu_dai, |
348 | unsigned int fmt) |
349 | { |
350 | struct rk_i2s_tdm_dev *i2s_tdm = to_info(dai: cpu_dai); |
351 | unsigned int mask, val, tdm_val, txcr_val, rxcr_val; |
352 | int ret; |
353 | bool is_tdm = i2s_tdm->tdm_mode; |
354 | |
355 | ret = pm_runtime_resume_and_get(dev: cpu_dai->dev); |
356 | if (ret < 0 && ret != -EACCES) |
357 | return ret; |
358 | |
359 | mask = I2S_CKR_MSS_MASK; |
360 | switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { |
361 | case SND_SOC_DAIFMT_BP_FP: |
362 | val = I2S_CKR_MSS_MASTER; |
363 | i2s_tdm->is_master_mode = true; |
364 | break; |
365 | case SND_SOC_DAIFMT_BC_FC: |
366 | val = I2S_CKR_MSS_SLAVE; |
367 | i2s_tdm->is_master_mode = false; |
368 | break; |
369 | default: |
370 | ret = -EINVAL; |
371 | goto err_pm_put; |
372 | } |
373 | |
374 | regmap_update_bits(map: i2s_tdm->regmap, I2S_CKR, mask, val); |
375 | |
376 | mask = I2S_CKR_CKP_MASK | I2S_CKR_TLP_MASK | I2S_CKR_RLP_MASK; |
377 | switch (fmt & SND_SOC_DAIFMT_INV_MASK) { |
378 | case SND_SOC_DAIFMT_NB_NF: |
379 | val = I2S_CKR_CKP_NORMAL | |
380 | I2S_CKR_TLP_NORMAL | |
381 | I2S_CKR_RLP_NORMAL; |
382 | break; |
383 | case SND_SOC_DAIFMT_NB_IF: |
384 | val = I2S_CKR_CKP_NORMAL | |
385 | I2S_CKR_TLP_INVERTED | |
386 | I2S_CKR_RLP_INVERTED; |
387 | break; |
388 | case SND_SOC_DAIFMT_IB_NF: |
389 | val = I2S_CKR_CKP_INVERTED | |
390 | I2S_CKR_TLP_NORMAL | |
391 | I2S_CKR_RLP_NORMAL; |
392 | break; |
393 | case SND_SOC_DAIFMT_IB_IF: |
394 | val = I2S_CKR_CKP_INVERTED | |
395 | I2S_CKR_TLP_INVERTED | |
396 | I2S_CKR_RLP_INVERTED; |
397 | break; |
398 | default: |
399 | ret = -EINVAL; |
400 | goto err_pm_put; |
401 | } |
402 | |
403 | regmap_update_bits(map: i2s_tdm->regmap, I2S_CKR, mask, val); |
404 | |
405 | switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { |
406 | case SND_SOC_DAIFMT_RIGHT_J: |
407 | txcr_val = I2S_TXCR_IBM_RSJM; |
408 | rxcr_val = I2S_RXCR_IBM_RSJM; |
409 | break; |
410 | case SND_SOC_DAIFMT_LEFT_J: |
411 | txcr_val = I2S_TXCR_IBM_LSJM; |
412 | rxcr_val = I2S_RXCR_IBM_LSJM; |
413 | break; |
414 | case SND_SOC_DAIFMT_I2S: |
415 | txcr_val = I2S_TXCR_IBM_NORMAL; |
416 | rxcr_val = I2S_RXCR_IBM_NORMAL; |
417 | break; |
418 | case SND_SOC_DAIFMT_DSP_A: /* PCM delay 1 mode */ |
419 | txcr_val = I2S_TXCR_TFS_PCM | I2S_TXCR_PBM_MODE(1); |
420 | rxcr_val = I2S_RXCR_TFS_PCM | I2S_RXCR_PBM_MODE(1); |
421 | break; |
422 | case SND_SOC_DAIFMT_DSP_B: /* PCM no delay mode */ |
423 | txcr_val = I2S_TXCR_TFS_PCM; |
424 | rxcr_val = I2S_RXCR_TFS_PCM; |
425 | break; |
426 | default: |
427 | ret = -EINVAL; |
428 | goto err_pm_put; |
429 | } |
430 | |
431 | mask = I2S_TXCR_IBM_MASK | I2S_TXCR_TFS_MASK | I2S_TXCR_PBM_MASK; |
432 | regmap_update_bits(map: i2s_tdm->regmap, I2S_TXCR, mask, val: txcr_val); |
433 | |
434 | mask = I2S_RXCR_IBM_MASK | I2S_RXCR_TFS_MASK | I2S_RXCR_PBM_MASK; |
435 | regmap_update_bits(map: i2s_tdm->regmap, I2S_RXCR, mask, val: rxcr_val); |
436 | |
437 | if (is_tdm) { |
438 | switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { |
439 | case SND_SOC_DAIFMT_RIGHT_J: |
440 | val = I2S_TXCR_TFS_TDM_I2S; |
441 | tdm_val = TDM_SHIFT_CTRL(2); |
442 | break; |
443 | case SND_SOC_DAIFMT_LEFT_J: |
444 | val = I2S_TXCR_TFS_TDM_I2S; |
445 | tdm_val = TDM_SHIFT_CTRL(1); |
446 | break; |
447 | case SND_SOC_DAIFMT_I2S: |
448 | val = I2S_TXCR_TFS_TDM_I2S; |
449 | tdm_val = TDM_SHIFT_CTRL(0); |
450 | break; |
451 | case SND_SOC_DAIFMT_DSP_A: |
452 | val = I2S_TXCR_TFS_TDM_PCM; |
453 | tdm_val = TDM_SHIFT_CTRL(0); |
454 | break; |
455 | case SND_SOC_DAIFMT_DSP_B: |
456 | val = I2S_TXCR_TFS_TDM_PCM; |
457 | tdm_val = TDM_SHIFT_CTRL(2); |
458 | break; |
459 | default: |
460 | ret = -EINVAL; |
461 | goto err_pm_put; |
462 | } |
463 | |
464 | tdm_val |= TDM_FSYNC_WIDTH_SEL1(1); |
465 | tdm_val |= TDM_FSYNC_WIDTH_HALF_FRAME; |
466 | |
467 | mask = I2S_TXCR_TFS_MASK; |
468 | regmap_update_bits(map: i2s_tdm->regmap, I2S_TXCR, mask, val); |
469 | regmap_update_bits(map: i2s_tdm->regmap, I2S_RXCR, mask, val); |
470 | |
471 | mask = TDM_FSYNC_WIDTH_SEL1_MSK | TDM_FSYNC_WIDTH_SEL0_MSK | |
472 | TDM_SHIFT_CTRL_MSK; |
473 | regmap_update_bits(map: i2s_tdm->regmap, I2S_TDM_TXCR, |
474 | mask, val: tdm_val); |
475 | regmap_update_bits(map: i2s_tdm->regmap, I2S_TDM_RXCR, |
476 | mask, val: tdm_val); |
477 | } |
478 | |
479 | err_pm_put: |
480 | pm_runtime_put(dev: cpu_dai->dev); |
481 | |
482 | return ret; |
483 | } |
484 | |
485 | static void rockchip_i2s_tdm_xfer_pause(struct snd_pcm_substream *substream, |
486 | struct rk_i2s_tdm_dev *i2s_tdm) |
487 | { |
488 | int stream; |
489 | |
490 | stream = SNDRV_PCM_STREAM_LAST - substream->stream; |
491 | if (stream == SNDRV_PCM_STREAM_PLAYBACK) |
492 | rockchip_disable_tde(regmap: i2s_tdm->regmap); |
493 | else |
494 | rockchip_disable_rde(regmap: i2s_tdm->regmap); |
495 | |
496 | rockchip_snd_xfer_clear(i2s_tdm, I2S_CLR_TXC | I2S_CLR_RXC); |
497 | } |
498 | |
499 | static void rockchip_i2s_tdm_xfer_resume(struct snd_pcm_substream *substream, |
500 | struct rk_i2s_tdm_dev *i2s_tdm) |
501 | { |
502 | int stream; |
503 | |
504 | stream = SNDRV_PCM_STREAM_LAST - substream->stream; |
505 | if (stream == SNDRV_PCM_STREAM_PLAYBACK) |
506 | rockchip_enable_tde(regmap: i2s_tdm->regmap); |
507 | else |
508 | rockchip_enable_rde(regmap: i2s_tdm->regmap); |
509 | |
510 | regmap_update_bits(map: i2s_tdm->regmap, I2S_XFER, |
511 | I2S_XFER_TXS_START | |
512 | I2S_XFER_RXS_START, |
513 | I2S_XFER_TXS_START | |
514 | I2S_XFER_RXS_START); |
515 | } |
516 | |
517 | static int rockchip_i2s_ch_to_io(unsigned int ch, bool substream_capture) |
518 | { |
519 | if (substream_capture) { |
520 | switch (ch) { |
521 | case I2S_CHN_4: |
522 | return I2S_IO_6CH_OUT_4CH_IN; |
523 | case I2S_CHN_6: |
524 | return I2S_IO_4CH_OUT_6CH_IN; |
525 | case I2S_CHN_8: |
526 | return I2S_IO_2CH_OUT_8CH_IN; |
527 | default: |
528 | return I2S_IO_8CH_OUT_2CH_IN; |
529 | } |
530 | } else { |
531 | switch (ch) { |
532 | case I2S_CHN_4: |
533 | return I2S_IO_4CH_OUT_6CH_IN; |
534 | case I2S_CHN_6: |
535 | return I2S_IO_6CH_OUT_4CH_IN; |
536 | case I2S_CHN_8: |
537 | return I2S_IO_8CH_OUT_2CH_IN; |
538 | default: |
539 | return I2S_IO_2CH_OUT_8CH_IN; |
540 | } |
541 | } |
542 | } |
543 | |
544 | static int rockchip_i2s_io_multiplex(struct snd_pcm_substream *substream, |
545 | struct snd_soc_dai *dai) |
546 | { |
547 | struct rk_i2s_tdm_dev *i2s_tdm = to_info(dai); |
548 | int usable_chs = MULTIPLEX_CH_MAX; |
549 | unsigned int val = 0; |
550 | |
551 | if (!i2s_tdm->io_multiplex) |
552 | return 0; |
553 | |
554 | if (IS_ERR_OR_NULL(ptr: i2s_tdm->grf)) { |
555 | dev_err(i2s_tdm->dev, |
556 | "io multiplex not supported for this device\n" ); |
557 | return -EINVAL; |
558 | } |
559 | |
560 | if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { |
561 | struct snd_pcm_str *playback_str = |
562 | &substream->pcm->streams[SNDRV_PCM_STREAM_PLAYBACK]; |
563 | |
564 | if (playback_str->substream_opened) { |
565 | regmap_read(map: i2s_tdm->regmap, I2S_TXCR, val: &val); |
566 | val &= I2S_TXCR_CSR_MASK; |
567 | usable_chs = MULTIPLEX_CH_MAX - to_ch_num(val); |
568 | } |
569 | |
570 | regmap_read(map: i2s_tdm->regmap, I2S_RXCR, val: &val); |
571 | val &= I2S_RXCR_CSR_MASK; |
572 | |
573 | if (to_ch_num(val) > usable_chs) { |
574 | dev_err(i2s_tdm->dev, |
575 | "Capture channels (%d) > usable channels (%d)\n" , |
576 | to_ch_num(val), usable_chs); |
577 | return -EINVAL; |
578 | } |
579 | |
580 | rockchip_i2s_ch_to_io(ch: val, substream_capture: true); |
581 | } else { |
582 | struct snd_pcm_str *capture_str = |
583 | &substream->pcm->streams[SNDRV_PCM_STREAM_CAPTURE]; |
584 | |
585 | if (capture_str->substream_opened) { |
586 | regmap_read(map: i2s_tdm->regmap, I2S_RXCR, val: &val); |
587 | val &= I2S_RXCR_CSR_MASK; |
588 | usable_chs = MULTIPLEX_CH_MAX - to_ch_num(val); |
589 | } |
590 | |
591 | regmap_read(map: i2s_tdm->regmap, I2S_TXCR, val: &val); |
592 | val &= I2S_TXCR_CSR_MASK; |
593 | |
594 | if (to_ch_num(val) > usable_chs) { |
595 | dev_err(i2s_tdm->dev, |
596 | "Playback channels (%d) > usable channels (%d)\n" , |
597 | to_ch_num(val), usable_chs); |
598 | return -EINVAL; |
599 | } |
600 | } |
601 | |
602 | val <<= i2s_tdm->soc_data->grf_shift; |
603 | val |= (I2S_IO_DIRECTION_MASK << i2s_tdm->soc_data->grf_shift) << 16; |
604 | regmap_write(map: i2s_tdm->grf, reg: i2s_tdm->soc_data->grf_reg_offset, val); |
605 | |
606 | return 0; |
607 | } |
608 | |
609 | static int rockchip_i2s_trcm_mode(struct snd_pcm_substream *substream, |
610 | struct snd_soc_dai *dai, |
611 | unsigned int div_bclk, |
612 | unsigned int div_lrck, |
613 | unsigned int fmt) |
614 | { |
615 | struct rk_i2s_tdm_dev *i2s_tdm = to_info(dai); |
616 | unsigned long flags; |
617 | |
618 | if (!i2s_tdm->clk_trcm) |
619 | return 0; |
620 | |
621 | spin_lock_irqsave(&i2s_tdm->lock, flags); |
622 | if (i2s_tdm->refcount) |
623 | rockchip_i2s_tdm_xfer_pause(substream, i2s_tdm); |
624 | |
625 | regmap_update_bits(map: i2s_tdm->regmap, I2S_CLKDIV, |
626 | I2S_CLKDIV_TXM_MASK | I2S_CLKDIV_RXM_MASK, |
627 | I2S_CLKDIV_TXM(div_bclk) | I2S_CLKDIV_RXM(div_bclk)); |
628 | regmap_update_bits(map: i2s_tdm->regmap, I2S_CKR, |
629 | I2S_CKR_TSD_MASK | I2S_CKR_RSD_MASK, |
630 | I2S_CKR_TSD(div_lrck) | I2S_CKR_RSD(div_lrck)); |
631 | |
632 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) |
633 | regmap_update_bits(map: i2s_tdm->regmap, I2S_TXCR, |
634 | I2S_TXCR_VDW_MASK | I2S_TXCR_CSR_MASK, |
635 | val: fmt); |
636 | else |
637 | regmap_update_bits(map: i2s_tdm->regmap, I2S_RXCR, |
638 | I2S_RXCR_VDW_MASK | I2S_RXCR_CSR_MASK, |
639 | val: fmt); |
640 | |
641 | if (i2s_tdm->refcount) |
642 | rockchip_i2s_tdm_xfer_resume(substream, i2s_tdm); |
643 | spin_unlock_irqrestore(lock: &i2s_tdm->lock, flags); |
644 | |
645 | return 0; |
646 | } |
647 | |
648 | static int rockchip_i2s_tdm_hw_params(struct snd_pcm_substream *substream, |
649 | struct snd_pcm_hw_params *params, |
650 | struct snd_soc_dai *dai) |
651 | { |
652 | struct rk_i2s_tdm_dev *i2s_tdm = to_info(dai); |
653 | unsigned int val = 0; |
654 | unsigned int mclk_rate, bclk_rate, div_bclk = 4, div_lrck = 64; |
655 | int err; |
656 | |
657 | if (i2s_tdm->is_master_mode) { |
658 | struct clk *mclk = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ? |
659 | i2s_tdm->mclk_tx : i2s_tdm->mclk_rx; |
660 | |
661 | err = clk_set_rate(clk: mclk, DEFAULT_MCLK_FS * params_rate(p: params)); |
662 | if (err) |
663 | return err; |
664 | |
665 | mclk_rate = clk_get_rate(clk: mclk); |
666 | bclk_rate = i2s_tdm->frame_width * params_rate(p: params); |
667 | if (!bclk_rate) |
668 | return -EINVAL; |
669 | |
670 | div_bclk = DIV_ROUND_CLOSEST(mclk_rate, bclk_rate); |
671 | div_lrck = bclk_rate / params_rate(p: params); |
672 | } |
673 | |
674 | switch (params_format(p: params)) { |
675 | case SNDRV_PCM_FORMAT_S8: |
676 | val |= I2S_TXCR_VDW(8); |
677 | break; |
678 | case SNDRV_PCM_FORMAT_S16_LE: |
679 | val |= I2S_TXCR_VDW(16); |
680 | break; |
681 | case SNDRV_PCM_FORMAT_S20_3LE: |
682 | val |= I2S_TXCR_VDW(20); |
683 | break; |
684 | case SNDRV_PCM_FORMAT_S24_LE: |
685 | val |= I2S_TXCR_VDW(24); |
686 | break; |
687 | case SNDRV_PCM_FORMAT_S32_LE: |
688 | val |= I2S_TXCR_VDW(32); |
689 | break; |
690 | default: |
691 | return -EINVAL; |
692 | } |
693 | |
694 | switch (params_channels(p: params)) { |
695 | case 8: |
696 | val |= I2S_CHN_8; |
697 | break; |
698 | case 6: |
699 | val |= I2S_CHN_6; |
700 | break; |
701 | case 4: |
702 | val |= I2S_CHN_4; |
703 | break; |
704 | case 2: |
705 | val |= I2S_CHN_2; |
706 | break; |
707 | default: |
708 | return -EINVAL; |
709 | } |
710 | |
711 | if (i2s_tdm->clk_trcm) { |
712 | rockchip_i2s_trcm_mode(substream, dai, div_bclk, div_lrck, fmt: val); |
713 | } else if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { |
714 | regmap_update_bits(map: i2s_tdm->regmap, I2S_CLKDIV, |
715 | I2S_CLKDIV_TXM_MASK, |
716 | I2S_CLKDIV_TXM(div_bclk)); |
717 | regmap_update_bits(map: i2s_tdm->regmap, I2S_CKR, |
718 | I2S_CKR_TSD_MASK, |
719 | I2S_CKR_TSD(div_lrck)); |
720 | regmap_update_bits(map: i2s_tdm->regmap, I2S_TXCR, |
721 | I2S_TXCR_VDW_MASK | I2S_TXCR_CSR_MASK, |
722 | val); |
723 | } else { |
724 | regmap_update_bits(map: i2s_tdm->regmap, I2S_CLKDIV, |
725 | I2S_CLKDIV_RXM_MASK, |
726 | I2S_CLKDIV_RXM(div_bclk)); |
727 | regmap_update_bits(map: i2s_tdm->regmap, I2S_CKR, |
728 | I2S_CKR_RSD_MASK, |
729 | I2S_CKR_RSD(div_lrck)); |
730 | regmap_update_bits(map: i2s_tdm->regmap, I2S_RXCR, |
731 | I2S_RXCR_VDW_MASK | I2S_RXCR_CSR_MASK, |
732 | val); |
733 | } |
734 | |
735 | return rockchip_i2s_io_multiplex(substream, dai); |
736 | } |
737 | |
738 | static int rockchip_i2s_tdm_trigger(struct snd_pcm_substream *substream, |
739 | int cmd, struct snd_soc_dai *dai) |
740 | { |
741 | struct rk_i2s_tdm_dev *i2s_tdm = to_info(dai); |
742 | |
743 | switch (cmd) { |
744 | case SNDRV_PCM_TRIGGER_START: |
745 | case SNDRV_PCM_TRIGGER_RESUME: |
746 | case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: |
747 | if (i2s_tdm->clk_trcm) |
748 | rockchip_snd_txrxctrl(substream, dai, on: 1); |
749 | else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) |
750 | rockchip_snd_rxctrl(i2s_tdm, on: 1); |
751 | else |
752 | rockchip_snd_txctrl(i2s_tdm, on: 1); |
753 | break; |
754 | case SNDRV_PCM_TRIGGER_SUSPEND: |
755 | case SNDRV_PCM_TRIGGER_STOP: |
756 | case SNDRV_PCM_TRIGGER_PAUSE_PUSH: |
757 | if (i2s_tdm->clk_trcm) |
758 | rockchip_snd_txrxctrl(substream, dai, on: 0); |
759 | else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) |
760 | rockchip_snd_rxctrl(i2s_tdm, on: 0); |
761 | else |
762 | rockchip_snd_txctrl(i2s_tdm, on: 0); |
763 | break; |
764 | default: |
765 | return -EINVAL; |
766 | } |
767 | |
768 | return 0; |
769 | } |
770 | |
771 | static int rockchip_i2s_tdm_dai_probe(struct snd_soc_dai *dai) |
772 | { |
773 | struct rk_i2s_tdm_dev *i2s_tdm = snd_soc_dai_get_drvdata(dai); |
774 | |
775 | if (i2s_tdm->has_capture) |
776 | snd_soc_dai_dma_data_set_capture(dai, &i2s_tdm->capture_dma_data); |
777 | if (i2s_tdm->has_playback) |
778 | snd_soc_dai_dma_data_set_playback(dai, &i2s_tdm->playback_dma_data); |
779 | |
780 | return 0; |
781 | } |
782 | |
783 | static int rockchip_dai_tdm_slot(struct snd_soc_dai *dai, |
784 | unsigned int tx_mask, unsigned int rx_mask, |
785 | int slots, int slot_width) |
786 | { |
787 | struct rk_i2s_tdm_dev *i2s_tdm = snd_soc_dai_get_drvdata(dai); |
788 | unsigned int mask, val; |
789 | |
790 | i2s_tdm->tdm_mode = true; |
791 | i2s_tdm->frame_width = slots * slot_width; |
792 | mask = TDM_SLOT_BIT_WIDTH_MSK | TDM_FRAME_WIDTH_MSK; |
793 | val = TDM_SLOT_BIT_WIDTH(slot_width) | |
794 | TDM_FRAME_WIDTH(slots * slot_width); |
795 | regmap_update_bits(map: i2s_tdm->regmap, I2S_TDM_TXCR, |
796 | mask, val); |
797 | regmap_update_bits(map: i2s_tdm->regmap, I2S_TDM_RXCR, |
798 | mask, val); |
799 | |
800 | return 0; |
801 | } |
802 | |
803 | static int rockchip_i2s_tdm_set_bclk_ratio(struct snd_soc_dai *dai, |
804 | unsigned int ratio) |
805 | { |
806 | struct rk_i2s_tdm_dev *i2s_tdm = snd_soc_dai_get_drvdata(dai); |
807 | |
808 | if (ratio < 32 || ratio > 512 || ratio % 2 == 1) |
809 | return -EINVAL; |
810 | |
811 | i2s_tdm->frame_width = ratio; |
812 | |
813 | return 0; |
814 | } |
815 | |
816 | static const struct snd_soc_dai_ops rockchip_i2s_tdm_dai_ops = { |
817 | .probe = rockchip_i2s_tdm_dai_probe, |
818 | .hw_params = rockchip_i2s_tdm_hw_params, |
819 | .set_bclk_ratio = rockchip_i2s_tdm_set_bclk_ratio, |
820 | .set_fmt = rockchip_i2s_tdm_set_fmt, |
821 | .set_tdm_slot = rockchip_dai_tdm_slot, |
822 | .trigger = rockchip_i2s_tdm_trigger, |
823 | }; |
824 | |
825 | static const struct snd_soc_component_driver rockchip_i2s_tdm_component = { |
826 | .name = DRV_NAME, |
827 | .legacy_dai_naming = 1, |
828 | }; |
829 | |
830 | static bool rockchip_i2s_tdm_wr_reg(struct device *dev, unsigned int reg) |
831 | { |
832 | switch (reg) { |
833 | case I2S_TXCR: |
834 | case I2S_RXCR: |
835 | case I2S_CKR: |
836 | case I2S_DMACR: |
837 | case I2S_INTCR: |
838 | case I2S_XFER: |
839 | case I2S_CLR: |
840 | case I2S_TXDR: |
841 | case I2S_TDM_TXCR: |
842 | case I2S_TDM_RXCR: |
843 | case I2S_CLKDIV: |
844 | return true; |
845 | default: |
846 | return false; |
847 | } |
848 | } |
849 | |
850 | static bool rockchip_i2s_tdm_rd_reg(struct device *dev, unsigned int reg) |
851 | { |
852 | switch (reg) { |
853 | case I2S_TXCR: |
854 | case I2S_RXCR: |
855 | case I2S_CKR: |
856 | case I2S_DMACR: |
857 | case I2S_INTCR: |
858 | case I2S_XFER: |
859 | case I2S_CLR: |
860 | case I2S_TXDR: |
861 | case I2S_RXDR: |
862 | case I2S_TXFIFOLR: |
863 | case I2S_INTSR: |
864 | case I2S_RXFIFOLR: |
865 | case I2S_TDM_TXCR: |
866 | case I2S_TDM_RXCR: |
867 | case I2S_CLKDIV: |
868 | return true; |
869 | default: |
870 | return false; |
871 | } |
872 | } |
873 | |
874 | static bool rockchip_i2s_tdm_volatile_reg(struct device *dev, unsigned int reg) |
875 | { |
876 | switch (reg) { |
877 | case I2S_TXFIFOLR: |
878 | case I2S_INTSR: |
879 | case I2S_CLR: |
880 | case I2S_TXDR: |
881 | case I2S_RXDR: |
882 | case I2S_RXFIFOLR: |
883 | return true; |
884 | default: |
885 | return false; |
886 | } |
887 | } |
888 | |
889 | static bool rockchip_i2s_tdm_precious_reg(struct device *dev, unsigned int reg) |
890 | { |
891 | if (reg == I2S_RXDR) |
892 | return true; |
893 | return false; |
894 | } |
895 | |
896 | static const struct reg_default rockchip_i2s_tdm_reg_defaults[] = { |
897 | {0x00, 0x7200000f}, |
898 | {0x04, 0x01c8000f}, |
899 | {0x08, 0x00001f1f}, |
900 | {0x10, 0x001f0000}, |
901 | {0x14, 0x01f00000}, |
902 | {0x30, 0x00003eff}, |
903 | {0x34, 0x00003eff}, |
904 | {0x38, 0x00000707}, |
905 | }; |
906 | |
907 | static const struct regmap_config rockchip_i2s_tdm_regmap_config = { |
908 | .reg_bits = 32, |
909 | .reg_stride = 4, |
910 | .val_bits = 32, |
911 | .max_register = I2S_CLKDIV, |
912 | .reg_defaults = rockchip_i2s_tdm_reg_defaults, |
913 | .num_reg_defaults = ARRAY_SIZE(rockchip_i2s_tdm_reg_defaults), |
914 | .writeable_reg = rockchip_i2s_tdm_wr_reg, |
915 | .readable_reg = rockchip_i2s_tdm_rd_reg, |
916 | .volatile_reg = rockchip_i2s_tdm_volatile_reg, |
917 | .precious_reg = rockchip_i2s_tdm_precious_reg, |
918 | .cache_type = REGCACHE_FLAT, |
919 | }; |
920 | |
921 | static int common_soc_init(struct device *dev, u32 addr) |
922 | { |
923 | struct rk_i2s_tdm_dev *i2s_tdm = dev_get_drvdata(dev); |
924 | const struct txrx_config *configs = i2s_tdm->soc_data->configs; |
925 | u32 reg = 0, val = 0, trcm = i2s_tdm->clk_trcm; |
926 | int i; |
927 | |
928 | if (trcm == TRCM_TXRX) |
929 | return 0; |
930 | |
931 | if (IS_ERR_OR_NULL(ptr: i2s_tdm->grf)) { |
932 | dev_err(i2s_tdm->dev, |
933 | "no grf present but non-txrx TRCM specified\n" ); |
934 | return -EINVAL; |
935 | } |
936 | |
937 | for (i = 0; i < i2s_tdm->soc_data->config_count; i++) { |
938 | if (addr != configs[i].addr) |
939 | continue; |
940 | reg = configs[i].reg; |
941 | if (trcm == TRCM_TX) |
942 | val = configs[i].txonly; |
943 | else |
944 | val = configs[i].rxonly; |
945 | |
946 | if (reg) |
947 | regmap_write(map: i2s_tdm->grf, reg, val); |
948 | } |
949 | |
950 | return 0; |
951 | } |
952 | |
953 | static const struct txrx_config px30_txrx_config[] = { |
954 | { 0xff060000, 0x184, PX30_I2S0_CLK_TXONLY, PX30_I2S0_CLK_RXONLY }, |
955 | }; |
956 | |
957 | static const struct txrx_config rk1808_txrx_config[] = { |
958 | { 0xff7e0000, 0x190, RK1808_I2S0_CLK_TXONLY, RK1808_I2S0_CLK_RXONLY }, |
959 | }; |
960 | |
961 | static const struct txrx_config rk3308_txrx_config[] = { |
962 | { 0xff300000, 0x308, RK3308_I2S0_CLK_TXONLY, RK3308_I2S0_CLK_RXONLY }, |
963 | { 0xff310000, 0x308, RK3308_I2S1_CLK_TXONLY, RK3308_I2S1_CLK_RXONLY }, |
964 | }; |
965 | |
966 | static const struct txrx_config rk3568_txrx_config[] = { |
967 | { 0xfe410000, 0x504, RK3568_I2S1_CLK_TXONLY, RK3568_I2S1_CLK_RXONLY }, |
968 | { 0xfe410000, 0x508, RK3568_I2S1_MCLK_TX_OE, RK3568_I2S1_MCLK_RX_OE }, |
969 | { 0xfe420000, 0x508, RK3568_I2S2_MCLK_OE, RK3568_I2S2_MCLK_OE }, |
970 | { 0xfe430000, 0x504, RK3568_I2S3_CLK_TXONLY, RK3568_I2S3_CLK_RXONLY }, |
971 | { 0xfe430000, 0x508, RK3568_I2S3_MCLK_TXONLY, RK3568_I2S3_MCLK_RXONLY }, |
972 | { 0xfe430000, 0x508, RK3568_I2S3_MCLK_OE, RK3568_I2S3_MCLK_OE }, |
973 | }; |
974 | |
975 | static const struct txrx_config rv1126_txrx_config[] = { |
976 | { 0xff800000, 0x10260, RV1126_I2S0_CLK_TXONLY, RV1126_I2S0_CLK_RXONLY }, |
977 | }; |
978 | |
979 | static const struct rk_i2s_soc_data px30_i2s_soc_data = { |
980 | .softrst_offset = 0x0300, |
981 | .configs = px30_txrx_config, |
982 | .config_count = ARRAY_SIZE(px30_txrx_config), |
983 | .init = common_soc_init, |
984 | }; |
985 | |
986 | static const struct rk_i2s_soc_data rk1808_i2s_soc_data = { |
987 | .softrst_offset = 0x0300, |
988 | .configs = rk1808_txrx_config, |
989 | .config_count = ARRAY_SIZE(rk1808_txrx_config), |
990 | .init = common_soc_init, |
991 | }; |
992 | |
993 | static const struct rk_i2s_soc_data rk3308_i2s_soc_data = { |
994 | .softrst_offset = 0x0400, |
995 | .grf_reg_offset = 0x0308, |
996 | .grf_shift = 5, |
997 | .configs = rk3308_txrx_config, |
998 | .config_count = ARRAY_SIZE(rk3308_txrx_config), |
999 | .init = common_soc_init, |
1000 | }; |
1001 | |
1002 | static const struct rk_i2s_soc_data rk3568_i2s_soc_data = { |
1003 | .softrst_offset = 0x0400, |
1004 | .configs = rk3568_txrx_config, |
1005 | .config_count = ARRAY_SIZE(rk3568_txrx_config), |
1006 | .init = common_soc_init, |
1007 | }; |
1008 | |
1009 | static const struct rk_i2s_soc_data rv1126_i2s_soc_data = { |
1010 | .softrst_offset = 0x0300, |
1011 | .configs = rv1126_txrx_config, |
1012 | .config_count = ARRAY_SIZE(rv1126_txrx_config), |
1013 | .init = common_soc_init, |
1014 | }; |
1015 | |
1016 | static const struct of_device_id rockchip_i2s_tdm_match[] = { |
1017 | { .compatible = "rockchip,px30-i2s-tdm" , .data = &px30_i2s_soc_data }, |
1018 | { .compatible = "rockchip,rk1808-i2s-tdm" , .data = &rk1808_i2s_soc_data }, |
1019 | { .compatible = "rockchip,rk3308-i2s-tdm" , .data = &rk3308_i2s_soc_data }, |
1020 | { .compatible = "rockchip,rk3568-i2s-tdm" , .data = &rk3568_i2s_soc_data }, |
1021 | { .compatible = "rockchip,rk3588-i2s-tdm" }, |
1022 | { .compatible = "rockchip,rv1126-i2s-tdm" , .data = &rv1126_i2s_soc_data }, |
1023 | {}, |
1024 | }; |
1025 | |
1026 | static const struct snd_soc_dai_driver i2s_tdm_dai = { |
1027 | .ops = &rockchip_i2s_tdm_dai_ops, |
1028 | }; |
1029 | |
1030 | static int rockchip_i2s_tdm_init_dai(struct rk_i2s_tdm_dev *i2s_tdm) |
1031 | { |
1032 | struct snd_soc_dai_driver *dai; |
1033 | struct property *dma_names; |
1034 | const char *dma_name; |
1035 | u64 formats = (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE | |
1036 | SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE | |
1037 | SNDRV_PCM_FMTBIT_S32_LE); |
1038 | struct device_node *node = i2s_tdm->dev->of_node; |
1039 | |
1040 | of_property_for_each_string(node, "dma-names" , dma_names, dma_name) { |
1041 | if (!strcmp(dma_name, "tx" )) |
1042 | i2s_tdm->has_playback = true; |
1043 | if (!strcmp(dma_name, "rx" )) |
1044 | i2s_tdm->has_capture = true; |
1045 | } |
1046 | |
1047 | dai = devm_kmemdup(dev: i2s_tdm->dev, src: &i2s_tdm_dai, |
1048 | len: sizeof(*dai), GFP_KERNEL); |
1049 | if (!dai) |
1050 | return -ENOMEM; |
1051 | |
1052 | if (i2s_tdm->has_playback) { |
1053 | dai->playback.stream_name = "Playback" ; |
1054 | dai->playback.channels_min = 2; |
1055 | dai->playback.channels_max = 8; |
1056 | dai->playback.rates = SNDRV_PCM_RATE_8000_192000; |
1057 | dai->playback.formats = formats; |
1058 | } |
1059 | |
1060 | if (i2s_tdm->has_capture) { |
1061 | dai->capture.stream_name = "Capture" ; |
1062 | dai->capture.channels_min = 2; |
1063 | dai->capture.channels_max = 8; |
1064 | dai->capture.rates = SNDRV_PCM_RATE_8000_192000; |
1065 | dai->capture.formats = formats; |
1066 | } |
1067 | |
1068 | if (i2s_tdm->clk_trcm != TRCM_TXRX) |
1069 | dai->symmetric_rate = 1; |
1070 | |
1071 | i2s_tdm->dai = dai; |
1072 | |
1073 | return 0; |
1074 | } |
1075 | |
1076 | static int rockchip_i2s_tdm_path_check(struct rk_i2s_tdm_dev *i2s_tdm, |
1077 | int num, |
1078 | bool is_rx_path) |
1079 | { |
1080 | unsigned int *i2s_data; |
1081 | int i, j; |
1082 | |
1083 | if (is_rx_path) |
1084 | i2s_data = i2s_tdm->i2s_sdis; |
1085 | else |
1086 | i2s_data = i2s_tdm->i2s_sdos; |
1087 | |
1088 | for (i = 0; i < num; i++) { |
1089 | if (i2s_data[i] > CH_GRP_MAX - 1) { |
1090 | dev_err(i2s_tdm->dev, |
1091 | "%s path i2s_data[%d]: %d is too high, max is: %d\n" , |
1092 | is_rx_path ? "RX" : "TX" , |
1093 | i, i2s_data[i], CH_GRP_MAX); |
1094 | return -EINVAL; |
1095 | } |
1096 | |
1097 | for (j = 0; j < num; j++) { |
1098 | if (i == j) |
1099 | continue; |
1100 | |
1101 | if (i2s_data[i] == i2s_data[j]) { |
1102 | dev_err(i2s_tdm->dev, |
1103 | "%s path invalid routed i2s_data: [%d]%d == [%d]%d\n" , |
1104 | is_rx_path ? "RX" : "TX" , |
1105 | i, i2s_data[i], |
1106 | j, i2s_data[j]); |
1107 | return -EINVAL; |
1108 | } |
1109 | } |
1110 | } |
1111 | |
1112 | return 0; |
1113 | } |
1114 | |
1115 | static void rockchip_i2s_tdm_tx_path_config(struct rk_i2s_tdm_dev *i2s_tdm, |
1116 | int num) |
1117 | { |
1118 | int idx; |
1119 | |
1120 | for (idx = 0; idx < num; idx++) { |
1121 | regmap_update_bits(map: i2s_tdm->regmap, I2S_TXCR, |
1122 | I2S_TXCR_PATH_MASK(idx), |
1123 | I2S_TXCR_PATH(idx, i2s_tdm->i2s_sdos[idx])); |
1124 | } |
1125 | } |
1126 | |
1127 | static void rockchip_i2s_tdm_rx_path_config(struct rk_i2s_tdm_dev *i2s_tdm, |
1128 | int num) |
1129 | { |
1130 | int idx; |
1131 | |
1132 | for (idx = 0; idx < num; idx++) { |
1133 | regmap_update_bits(map: i2s_tdm->regmap, I2S_RXCR, |
1134 | I2S_RXCR_PATH_MASK(idx), |
1135 | I2S_RXCR_PATH(idx, i2s_tdm->i2s_sdis[idx])); |
1136 | } |
1137 | } |
1138 | |
1139 | static void rockchip_i2s_tdm_path_config(struct rk_i2s_tdm_dev *i2s_tdm, |
1140 | int num, bool is_rx_path) |
1141 | { |
1142 | if (is_rx_path) |
1143 | rockchip_i2s_tdm_rx_path_config(i2s_tdm, num); |
1144 | else |
1145 | rockchip_i2s_tdm_tx_path_config(i2s_tdm, num); |
1146 | } |
1147 | |
1148 | static int rockchip_i2s_tdm_path_prepare(struct rk_i2s_tdm_dev *i2s_tdm, |
1149 | struct device_node *np, |
1150 | bool is_rx_path) |
1151 | { |
1152 | char *i2s_tx_path_prop = "rockchip,i2s-tx-route" ; |
1153 | char *i2s_rx_path_prop = "rockchip,i2s-rx-route" ; |
1154 | char *i2s_path_prop; |
1155 | unsigned int *i2s_data; |
1156 | int num, ret = 0; |
1157 | |
1158 | if (is_rx_path) { |
1159 | i2s_path_prop = i2s_rx_path_prop; |
1160 | i2s_data = i2s_tdm->i2s_sdis; |
1161 | } else { |
1162 | i2s_path_prop = i2s_tx_path_prop; |
1163 | i2s_data = i2s_tdm->i2s_sdos; |
1164 | } |
1165 | |
1166 | num = of_count_phandle_with_args(np, list_name: i2s_path_prop, NULL); |
1167 | if (num < 0) { |
1168 | if (num != -ENOENT) { |
1169 | dev_err(i2s_tdm->dev, |
1170 | "Failed to read '%s' num: %d\n" , |
1171 | i2s_path_prop, num); |
1172 | ret = num; |
1173 | } |
1174 | return ret; |
1175 | } else if (num != CH_GRP_MAX) { |
1176 | dev_err(i2s_tdm->dev, |
1177 | "The num: %d should be: %d\n" , num, CH_GRP_MAX); |
1178 | return -EINVAL; |
1179 | } |
1180 | |
1181 | ret = of_property_read_u32_array(np, propname: i2s_path_prop, |
1182 | out_values: i2s_data, sz: num); |
1183 | if (ret < 0) { |
1184 | dev_err(i2s_tdm->dev, |
1185 | "Failed to read '%s': %d\n" , |
1186 | i2s_path_prop, ret); |
1187 | return ret; |
1188 | } |
1189 | |
1190 | ret = rockchip_i2s_tdm_path_check(i2s_tdm, num, is_rx_path); |
1191 | if (ret < 0) { |
1192 | dev_err(i2s_tdm->dev, |
1193 | "Failed to check i2s data bus: %d\n" , ret); |
1194 | return ret; |
1195 | } |
1196 | |
1197 | rockchip_i2s_tdm_path_config(i2s_tdm, num, is_rx_path); |
1198 | |
1199 | return 0; |
1200 | } |
1201 | |
1202 | static int rockchip_i2s_tdm_tx_path_prepare(struct rk_i2s_tdm_dev *i2s_tdm, |
1203 | struct device_node *np) |
1204 | { |
1205 | return rockchip_i2s_tdm_path_prepare(i2s_tdm, np, is_rx_path: 0); |
1206 | } |
1207 | |
1208 | static int rockchip_i2s_tdm_rx_path_prepare(struct rk_i2s_tdm_dev *i2s_tdm, |
1209 | struct device_node *np) |
1210 | { |
1211 | return rockchip_i2s_tdm_path_prepare(i2s_tdm, np, is_rx_path: 1); |
1212 | } |
1213 | |
1214 | static int rockchip_i2s_tdm_probe(struct platform_device *pdev) |
1215 | { |
1216 | struct device_node *node = pdev->dev.of_node; |
1217 | struct rk_i2s_tdm_dev *i2s_tdm; |
1218 | struct resource *res; |
1219 | void __iomem *regs; |
1220 | int ret; |
1221 | |
1222 | i2s_tdm = devm_kzalloc(dev: &pdev->dev, size: sizeof(*i2s_tdm), GFP_KERNEL); |
1223 | if (!i2s_tdm) |
1224 | return -ENOMEM; |
1225 | |
1226 | i2s_tdm->dev = &pdev->dev; |
1227 | |
1228 | spin_lock_init(&i2s_tdm->lock); |
1229 | i2s_tdm->soc_data = device_get_match_data(dev: &pdev->dev); |
1230 | i2s_tdm->frame_width = 64; |
1231 | |
1232 | i2s_tdm->clk_trcm = TRCM_TXRX; |
1233 | if (of_property_read_bool(np: node, propname: "rockchip,trcm-sync-tx-only" )) |
1234 | i2s_tdm->clk_trcm = TRCM_TX; |
1235 | if (of_property_read_bool(np: node, propname: "rockchip,trcm-sync-rx-only" )) { |
1236 | if (i2s_tdm->clk_trcm) { |
1237 | dev_err(i2s_tdm->dev, "invalid trcm-sync configuration\n" ); |
1238 | return -EINVAL; |
1239 | } |
1240 | i2s_tdm->clk_trcm = TRCM_RX; |
1241 | } |
1242 | |
1243 | ret = rockchip_i2s_tdm_init_dai(i2s_tdm); |
1244 | if (ret) |
1245 | return ret; |
1246 | |
1247 | i2s_tdm->grf = syscon_regmap_lookup_by_phandle(np: node, property: "rockchip,grf" ); |
1248 | i2s_tdm->tx_reset = devm_reset_control_get_optional_exclusive(dev: &pdev->dev, |
1249 | id: "tx-m" ); |
1250 | if (IS_ERR(ptr: i2s_tdm->tx_reset)) { |
1251 | ret = PTR_ERR(ptr: i2s_tdm->tx_reset); |
1252 | return dev_err_probe(dev: i2s_tdm->dev, err: ret, |
1253 | fmt: "Error in tx-m reset control\n" ); |
1254 | } |
1255 | |
1256 | i2s_tdm->rx_reset = devm_reset_control_get_optional_exclusive(dev: &pdev->dev, |
1257 | id: "rx-m" ); |
1258 | if (IS_ERR(ptr: i2s_tdm->rx_reset)) { |
1259 | ret = PTR_ERR(ptr: i2s_tdm->rx_reset); |
1260 | return dev_err_probe(dev: i2s_tdm->dev, err: ret, |
1261 | fmt: "Error in rx-m reset control\n" ); |
1262 | } |
1263 | |
1264 | i2s_tdm->hclk = devm_clk_get(dev: &pdev->dev, id: "hclk" ); |
1265 | if (IS_ERR(ptr: i2s_tdm->hclk)) { |
1266 | return dev_err_probe(dev: i2s_tdm->dev, err: PTR_ERR(ptr: i2s_tdm->hclk), |
1267 | fmt: "Failed to get clock hclk\n" ); |
1268 | } |
1269 | |
1270 | i2s_tdm->mclk_tx = devm_clk_get(dev: &pdev->dev, id: "mclk_tx" ); |
1271 | if (IS_ERR(ptr: i2s_tdm->mclk_tx)) { |
1272 | return dev_err_probe(dev: i2s_tdm->dev, err: PTR_ERR(ptr: i2s_tdm->mclk_tx), |
1273 | fmt: "Failed to get clock mclk_tx\n" ); |
1274 | } |
1275 | |
1276 | i2s_tdm->mclk_rx = devm_clk_get(dev: &pdev->dev, id: "mclk_rx" ); |
1277 | if (IS_ERR(ptr: i2s_tdm->mclk_rx)) { |
1278 | return dev_err_probe(dev: i2s_tdm->dev, err: PTR_ERR(ptr: i2s_tdm->mclk_rx), |
1279 | fmt: "Failed to get clock mclk_rx\n" ); |
1280 | } |
1281 | |
1282 | i2s_tdm->io_multiplex = |
1283 | of_property_read_bool(np: node, propname: "rockchip,io-multiplex" ); |
1284 | |
1285 | regs = devm_platform_get_and_ioremap_resource(pdev, index: 0, res: &res); |
1286 | if (IS_ERR(ptr: regs)) { |
1287 | return dev_err_probe(dev: i2s_tdm->dev, err: PTR_ERR(ptr: regs), |
1288 | fmt: "Failed to get resource IORESOURCE_MEM\n" ); |
1289 | } |
1290 | |
1291 | i2s_tdm->regmap = devm_regmap_init_mmio(&pdev->dev, regs, |
1292 | &rockchip_i2s_tdm_regmap_config); |
1293 | if (IS_ERR(ptr: i2s_tdm->regmap)) { |
1294 | return dev_err_probe(dev: i2s_tdm->dev, err: PTR_ERR(ptr: i2s_tdm->regmap), |
1295 | fmt: "Failed to initialise regmap\n" ); |
1296 | } |
1297 | |
1298 | if (i2s_tdm->has_playback) { |
1299 | i2s_tdm->playback_dma_data.addr = res->start + I2S_TXDR; |
1300 | i2s_tdm->playback_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; |
1301 | i2s_tdm->playback_dma_data.maxburst = 8; |
1302 | } |
1303 | |
1304 | if (i2s_tdm->has_capture) { |
1305 | i2s_tdm->capture_dma_data.addr = res->start + I2S_RXDR; |
1306 | i2s_tdm->capture_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; |
1307 | i2s_tdm->capture_dma_data.maxburst = 8; |
1308 | } |
1309 | |
1310 | ret = rockchip_i2s_tdm_tx_path_prepare(i2s_tdm, np: node); |
1311 | if (ret < 0) { |
1312 | dev_err(&pdev->dev, "I2S TX path prepare failed: %d\n" , ret); |
1313 | return ret; |
1314 | } |
1315 | |
1316 | ret = rockchip_i2s_tdm_rx_path_prepare(i2s_tdm, np: node); |
1317 | if (ret < 0) { |
1318 | dev_err(&pdev->dev, "I2S RX path prepare failed: %d\n" , ret); |
1319 | return ret; |
1320 | } |
1321 | |
1322 | dev_set_drvdata(dev: &pdev->dev, data: i2s_tdm); |
1323 | |
1324 | ret = clk_prepare_enable(clk: i2s_tdm->hclk); |
1325 | if (ret) { |
1326 | return dev_err_probe(dev: i2s_tdm->dev, err: ret, |
1327 | fmt: "Failed to enable clock hclk\n" ); |
1328 | } |
1329 | |
1330 | ret = i2s_tdm_prepare_enable_mclk(i2s_tdm); |
1331 | if (ret) { |
1332 | ret = dev_err_probe(dev: i2s_tdm->dev, err: ret, |
1333 | fmt: "Failed to enable one or more mclks\n" ); |
1334 | goto err_disable_hclk; |
1335 | } |
1336 | |
1337 | pm_runtime_enable(dev: &pdev->dev); |
1338 | |
1339 | regmap_update_bits(map: i2s_tdm->regmap, I2S_DMACR, I2S_DMACR_TDL_MASK, |
1340 | I2S_DMACR_TDL(16)); |
1341 | regmap_update_bits(map: i2s_tdm->regmap, I2S_DMACR, I2S_DMACR_RDL_MASK, |
1342 | I2S_DMACR_RDL(16)); |
1343 | regmap_update_bits(map: i2s_tdm->regmap, I2S_CKR, I2S_CKR_TRCM_MASK, |
1344 | val: i2s_tdm->clk_trcm << I2S_CKR_TRCM_SHIFT); |
1345 | |
1346 | if (i2s_tdm->soc_data && i2s_tdm->soc_data->init) |
1347 | i2s_tdm->soc_data->init(&pdev->dev, res->start); |
1348 | |
1349 | ret = devm_snd_soc_register_component(dev: &pdev->dev, |
1350 | component_driver: &rockchip_i2s_tdm_component, |
1351 | dai_drv: i2s_tdm->dai, num_dai: 1); |
1352 | |
1353 | if (ret) { |
1354 | dev_err(&pdev->dev, "Could not register DAI\n" ); |
1355 | goto err_suspend; |
1356 | } |
1357 | |
1358 | ret = devm_snd_dmaengine_pcm_register(dev: &pdev->dev, NULL, flags: 0); |
1359 | if (ret) { |
1360 | dev_err(&pdev->dev, "Could not register PCM\n" ); |
1361 | goto err_suspend; |
1362 | } |
1363 | |
1364 | return 0; |
1365 | |
1366 | err_suspend: |
1367 | if (!pm_runtime_status_suspended(dev: &pdev->dev)) |
1368 | i2s_tdm_runtime_suspend(dev: &pdev->dev); |
1369 | pm_runtime_disable(dev: &pdev->dev); |
1370 | |
1371 | err_disable_hclk: |
1372 | clk_disable_unprepare(clk: i2s_tdm->hclk); |
1373 | |
1374 | return ret; |
1375 | } |
1376 | |
1377 | static void rockchip_i2s_tdm_remove(struct platform_device *pdev) |
1378 | { |
1379 | if (!pm_runtime_status_suspended(dev: &pdev->dev)) |
1380 | i2s_tdm_runtime_suspend(dev: &pdev->dev); |
1381 | |
1382 | pm_runtime_disable(dev: &pdev->dev); |
1383 | } |
1384 | |
1385 | static int __maybe_unused rockchip_i2s_tdm_suspend(struct device *dev) |
1386 | { |
1387 | struct rk_i2s_tdm_dev *i2s_tdm = dev_get_drvdata(dev); |
1388 | |
1389 | regcache_mark_dirty(map: i2s_tdm->regmap); |
1390 | |
1391 | return 0; |
1392 | } |
1393 | |
1394 | static int __maybe_unused rockchip_i2s_tdm_resume(struct device *dev) |
1395 | { |
1396 | struct rk_i2s_tdm_dev *i2s_tdm = dev_get_drvdata(dev); |
1397 | int ret; |
1398 | |
1399 | ret = pm_runtime_resume_and_get(dev); |
1400 | if (ret < 0) |
1401 | return ret; |
1402 | ret = regcache_sync(map: i2s_tdm->regmap); |
1403 | pm_runtime_put(dev); |
1404 | |
1405 | return ret; |
1406 | } |
1407 | |
1408 | static const struct dev_pm_ops rockchip_i2s_tdm_pm_ops = { |
1409 | SET_RUNTIME_PM_OPS(i2s_tdm_runtime_suspend, i2s_tdm_runtime_resume, |
1410 | NULL) |
1411 | SET_SYSTEM_SLEEP_PM_OPS(rockchip_i2s_tdm_suspend, |
1412 | rockchip_i2s_tdm_resume) |
1413 | }; |
1414 | |
1415 | static struct platform_driver rockchip_i2s_tdm_driver = { |
1416 | .probe = rockchip_i2s_tdm_probe, |
1417 | .remove_new = rockchip_i2s_tdm_remove, |
1418 | .driver = { |
1419 | .name = DRV_NAME, |
1420 | .of_match_table = rockchip_i2s_tdm_match, |
1421 | .pm = &rockchip_i2s_tdm_pm_ops, |
1422 | }, |
1423 | }; |
1424 | module_platform_driver(rockchip_i2s_tdm_driver); |
1425 | |
1426 | MODULE_DESCRIPTION("ROCKCHIP I2S/TDM ASoC Interface" ); |
1427 | MODULE_AUTHOR("Sugar Zhang <sugar.zhang@rock-chips.com>" ); |
1428 | MODULE_LICENSE("GPL v2" ); |
1429 | MODULE_ALIAS("platform:" DRV_NAME); |
1430 | MODULE_DEVICE_TABLE(of, rockchip_i2s_tdm_match); |
1431 | |