1 | /* |
2 | * ALSA SoC Synopsys I2S Audio Layer |
3 | * |
4 | * sound/soc/dwc/designware_i2s.c |
5 | * |
6 | * Copyright (C) 2010 ST Microelectronics |
7 | * Rajeev Kumar <rajeevkumar.linux@gmail.com> |
8 | * |
9 | * This file is licensed under the terms of the GNU General Public |
10 | * License version 2. This program is licensed "as is" without any |
11 | * warranty of any kind, whether express or implied. |
12 | */ |
13 | |
14 | #include <linux/clk.h> |
15 | #include <linux/device.h> |
16 | #include <linux/init.h> |
17 | #include <linux/io.h> |
18 | #include <linux/interrupt.h> |
19 | #include <linux/mfd/syscon.h> |
20 | #include <linux/module.h> |
21 | #include <linux/reset.h> |
22 | #include <linux/slab.h> |
23 | #include <linux/pm_runtime.h> |
24 | #include <sound/designware_i2s.h> |
25 | #include <sound/pcm.h> |
26 | #include <sound/pcm_params.h> |
27 | #include <sound/soc.h> |
28 | #include <sound/dmaengine_pcm.h> |
29 | #include "local.h" |
30 | |
31 | static inline void i2s_write_reg(void __iomem *io_base, int reg, u32 val) |
32 | { |
33 | writel(val, addr: io_base + reg); |
34 | } |
35 | |
36 | static inline u32 i2s_read_reg(void __iomem *io_base, int reg) |
37 | { |
38 | return readl(addr: io_base + reg); |
39 | } |
40 | |
41 | static inline void i2s_disable_channels(struct dw_i2s_dev *dev, u32 stream) |
42 | { |
43 | u32 i = 0; |
44 | |
45 | if (stream == SNDRV_PCM_STREAM_PLAYBACK) { |
46 | for (i = 0; i < 4; i++) |
47 | i2s_write_reg(io_base: dev->i2s_base, TER(i), val: 0); |
48 | } else { |
49 | for (i = 0; i < 4; i++) |
50 | i2s_write_reg(io_base: dev->i2s_base, RER(i), val: 0); |
51 | } |
52 | } |
53 | |
54 | static inline void i2s_clear_irqs(struct dw_i2s_dev *dev, u32 stream) |
55 | { |
56 | u32 i = 0; |
57 | |
58 | if (stream == SNDRV_PCM_STREAM_PLAYBACK) { |
59 | for (i = 0; i < 4; i++) |
60 | i2s_read_reg(io_base: dev->i2s_base, TOR(i)); |
61 | } else { |
62 | for (i = 0; i < 4; i++) |
63 | i2s_read_reg(io_base: dev->i2s_base, ROR(i)); |
64 | } |
65 | } |
66 | |
67 | static inline void i2s_disable_irqs(struct dw_i2s_dev *dev, u32 stream, |
68 | int chan_nr) |
69 | { |
70 | u32 i, irq; |
71 | |
72 | if (stream == SNDRV_PCM_STREAM_PLAYBACK) { |
73 | for (i = 0; i < (chan_nr / 2); i++) { |
74 | irq = i2s_read_reg(io_base: dev->i2s_base, IMR(i)); |
75 | i2s_write_reg(io_base: dev->i2s_base, IMR(i), val: irq | 0x30); |
76 | } |
77 | } else { |
78 | for (i = 0; i < (chan_nr / 2); i++) { |
79 | irq = i2s_read_reg(io_base: dev->i2s_base, IMR(i)); |
80 | i2s_write_reg(io_base: dev->i2s_base, IMR(i), val: irq | 0x03); |
81 | } |
82 | } |
83 | } |
84 | |
85 | static inline void i2s_enable_irqs(struct dw_i2s_dev *dev, u32 stream, |
86 | int chan_nr) |
87 | { |
88 | u32 i, irq; |
89 | |
90 | if (stream == SNDRV_PCM_STREAM_PLAYBACK) { |
91 | for (i = 0; i < (chan_nr / 2); i++) { |
92 | irq = i2s_read_reg(io_base: dev->i2s_base, IMR(i)); |
93 | i2s_write_reg(io_base: dev->i2s_base, IMR(i), val: irq & ~0x30); |
94 | } |
95 | } else { |
96 | for (i = 0; i < (chan_nr / 2); i++) { |
97 | irq = i2s_read_reg(io_base: dev->i2s_base, IMR(i)); |
98 | i2s_write_reg(io_base: dev->i2s_base, IMR(i), val: irq & ~0x03); |
99 | } |
100 | } |
101 | } |
102 | |
103 | static irqreturn_t i2s_irq_handler(int irq, void *dev_id) |
104 | { |
105 | struct dw_i2s_dev *dev = dev_id; |
106 | bool irq_valid = false; |
107 | u32 isr[4]; |
108 | int i; |
109 | |
110 | for (i = 0; i < 4; i++) |
111 | isr[i] = i2s_read_reg(io_base: dev->i2s_base, ISR(i)); |
112 | |
113 | i2s_clear_irqs(dev, stream: SNDRV_PCM_STREAM_PLAYBACK); |
114 | i2s_clear_irqs(dev, stream: SNDRV_PCM_STREAM_CAPTURE); |
115 | |
116 | for (i = 0; i < 4; i++) { |
117 | /* |
118 | * Check if TX fifo is empty. If empty fill FIFO with samples |
119 | * NOTE: Only two channels supported |
120 | */ |
121 | if ((isr[i] & ISR_TXFE) && (i == 0) && dev->use_pio) { |
122 | dw_pcm_push_tx(dev); |
123 | irq_valid = true; |
124 | } |
125 | |
126 | /* |
127 | * Data available. Retrieve samples from FIFO |
128 | * NOTE: Only two channels supported |
129 | */ |
130 | if ((isr[i] & ISR_RXDA) && (i == 0) && dev->use_pio) { |
131 | dw_pcm_pop_rx(dev); |
132 | irq_valid = true; |
133 | } |
134 | |
135 | /* Error Handling: TX */ |
136 | if (isr[i] & ISR_TXFO) { |
137 | dev_err_ratelimited(dev->dev, "TX overrun (ch_id=%d)\n" , i); |
138 | irq_valid = true; |
139 | } |
140 | |
141 | /* Error Handling: TX */ |
142 | if (isr[i] & ISR_RXFO) { |
143 | dev_err_ratelimited(dev->dev, "RX overrun (ch_id=%d)\n" , i); |
144 | irq_valid = true; |
145 | } |
146 | } |
147 | |
148 | if (irq_valid) |
149 | return IRQ_HANDLED; |
150 | else |
151 | return IRQ_NONE; |
152 | } |
153 | |
154 | static void i2s_enable_dma(struct dw_i2s_dev *dev, u32 stream) |
155 | { |
156 | u32 dma_reg = i2s_read_reg(io_base: dev->i2s_base, I2S_DMACR); |
157 | |
158 | /* Enable DMA handshake for stream */ |
159 | if (stream == SNDRV_PCM_STREAM_PLAYBACK) |
160 | dma_reg |= I2S_DMAEN_TXBLOCK; |
161 | else |
162 | dma_reg |= I2S_DMAEN_RXBLOCK; |
163 | |
164 | i2s_write_reg(io_base: dev->i2s_base, I2S_DMACR, val: dma_reg); |
165 | } |
166 | |
167 | static void i2s_disable_dma(struct dw_i2s_dev *dev, u32 stream) |
168 | { |
169 | u32 dma_reg = i2s_read_reg(io_base: dev->i2s_base, I2S_DMACR); |
170 | |
171 | /* Disable DMA handshake for stream */ |
172 | if (stream == SNDRV_PCM_STREAM_PLAYBACK) { |
173 | dma_reg &= ~I2S_DMAEN_TXBLOCK; |
174 | i2s_write_reg(io_base: dev->i2s_base, I2S_RTXDMA, val: 1); |
175 | } else { |
176 | dma_reg &= ~I2S_DMAEN_RXBLOCK; |
177 | i2s_write_reg(io_base: dev->i2s_base, I2S_RRXDMA, val: 1); |
178 | } |
179 | i2s_write_reg(io_base: dev->i2s_base, I2S_DMACR, val: dma_reg); |
180 | } |
181 | |
182 | static void i2s_start(struct dw_i2s_dev *dev, |
183 | struct snd_pcm_substream *substream) |
184 | { |
185 | struct i2s_clk_config_data *config = &dev->config; |
186 | |
187 | u32 reg = IER_IEN; |
188 | |
189 | if (dev->tdm_slots) { |
190 | reg |= (dev->tdm_slots - 1) << IER_TDM_SLOTS_SHIFT; |
191 | reg |= IER_INTF_TYPE; |
192 | reg |= dev->frame_offset << IER_FRAME_OFF_SHIFT; |
193 | } |
194 | |
195 | i2s_write_reg(io_base: dev->i2s_base, IER, val: reg); |
196 | |
197 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) |
198 | i2s_write_reg(io_base: dev->i2s_base, ITER, val: 1); |
199 | else |
200 | i2s_write_reg(io_base: dev->i2s_base, IRER, val: 1); |
201 | |
202 | /* I2S needs to enable IRQ to make a handshake with DMAC on the JH7110 SoC */ |
203 | if (dev->use_pio || dev->is_jh7110) |
204 | i2s_enable_irqs(dev, stream: substream->stream, chan_nr: config->chan_nr); |
205 | else |
206 | i2s_enable_dma(dev, stream: substream->stream); |
207 | |
208 | i2s_write_reg(io_base: dev->i2s_base, CER, val: 1); |
209 | } |
210 | |
211 | static void i2s_stop(struct dw_i2s_dev *dev, |
212 | struct snd_pcm_substream *substream) |
213 | { |
214 | |
215 | i2s_clear_irqs(dev, stream: substream->stream); |
216 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) |
217 | i2s_write_reg(io_base: dev->i2s_base, ITER, val: 0); |
218 | else |
219 | i2s_write_reg(io_base: dev->i2s_base, IRER, val: 0); |
220 | |
221 | if (dev->use_pio || dev->is_jh7110) |
222 | i2s_disable_irqs(dev, stream: substream->stream, chan_nr: 8); |
223 | else |
224 | i2s_disable_dma(dev, stream: substream->stream); |
225 | |
226 | if (!dev->active) { |
227 | i2s_write_reg(io_base: dev->i2s_base, CER, val: 0); |
228 | i2s_write_reg(io_base: dev->i2s_base, IER, val: 0); |
229 | } |
230 | } |
231 | |
232 | static int dw_i2s_startup(struct snd_pcm_substream *substream, |
233 | struct snd_soc_dai *cpu_dai) |
234 | { |
235 | struct dw_i2s_dev *dev = snd_soc_dai_get_drvdata(dai: cpu_dai); |
236 | |
237 | if (dev->is_jh7110) { |
238 | struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); |
239 | struct snd_soc_dai_link *dai_link = rtd->dai_link; |
240 | |
241 | dai_link->trigger_stop = SND_SOC_TRIGGER_ORDER_LDC; |
242 | } |
243 | |
244 | return 0; |
245 | } |
246 | |
247 | static void dw_i2s_config(struct dw_i2s_dev *dev, int stream) |
248 | { |
249 | u32 ch_reg; |
250 | struct i2s_clk_config_data *config = &dev->config; |
251 | |
252 | |
253 | i2s_disable_channels(dev, stream); |
254 | |
255 | for (ch_reg = 0; ch_reg < (config->chan_nr / 2); ch_reg++) { |
256 | if (stream == SNDRV_PCM_STREAM_PLAYBACK) { |
257 | i2s_write_reg(io_base: dev->i2s_base, TCR(ch_reg), |
258 | val: dev->xfer_resolution); |
259 | i2s_write_reg(io_base: dev->i2s_base, TFCR(ch_reg), |
260 | val: dev->fifo_th - 1); |
261 | i2s_write_reg(io_base: dev->i2s_base, TER(ch_reg), TER_TXCHEN | |
262 | dev->tdm_mask << TER_TXSLOT_SHIFT); |
263 | } else { |
264 | i2s_write_reg(io_base: dev->i2s_base, RCR(ch_reg), |
265 | val: dev->xfer_resolution); |
266 | i2s_write_reg(io_base: dev->i2s_base, RFCR(ch_reg), |
267 | val: dev->fifo_th - 1); |
268 | i2s_write_reg(io_base: dev->i2s_base, RER(ch_reg), RER_RXCHEN | |
269 | dev->tdm_mask << RER_RXSLOT_SHIFT); |
270 | } |
271 | |
272 | } |
273 | } |
274 | |
275 | static int dw_i2s_hw_params(struct snd_pcm_substream *substream, |
276 | struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) |
277 | { |
278 | struct dw_i2s_dev *dev = snd_soc_dai_get_drvdata(dai); |
279 | struct i2s_clk_config_data *config = &dev->config; |
280 | int ret; |
281 | |
282 | switch (params_format(p: params)) { |
283 | case SNDRV_PCM_FORMAT_S16_LE: |
284 | config->data_width = 16; |
285 | dev->ccr = 0x00; |
286 | dev->xfer_resolution = 0x02; |
287 | break; |
288 | |
289 | case SNDRV_PCM_FORMAT_S24_LE: |
290 | config->data_width = 24; |
291 | dev->ccr = 0x08; |
292 | dev->xfer_resolution = 0x04; |
293 | break; |
294 | |
295 | case SNDRV_PCM_FORMAT_S32_LE: |
296 | config->data_width = 32; |
297 | dev->ccr = 0x10; |
298 | dev->xfer_resolution = 0x05; |
299 | break; |
300 | |
301 | default: |
302 | dev_err(dev->dev, "designware-i2s: unsupported PCM fmt" ); |
303 | return -EINVAL; |
304 | } |
305 | |
306 | if (dev->tdm_slots) |
307 | config->data_width = 32; |
308 | |
309 | config->chan_nr = params_channels(p: params); |
310 | |
311 | switch (config->chan_nr) { |
312 | case EIGHT_CHANNEL_SUPPORT: |
313 | case SIX_CHANNEL_SUPPORT: |
314 | case FOUR_CHANNEL_SUPPORT: |
315 | case TWO_CHANNEL_SUPPORT: |
316 | break; |
317 | default: |
318 | dev_err(dev->dev, "channel not supported\n" ); |
319 | return -EINVAL; |
320 | } |
321 | |
322 | dw_i2s_config(dev, stream: substream->stream); |
323 | |
324 | i2s_write_reg(io_base: dev->i2s_base, CCR, val: dev->ccr); |
325 | |
326 | config->sample_rate = params_rate(p: params); |
327 | |
328 | if (dev->capability & DW_I2S_MASTER) { |
329 | if (dev->i2s_clk_cfg) { |
330 | ret = dev->i2s_clk_cfg(config); |
331 | if (ret < 0) { |
332 | dev_err(dev->dev, "runtime audio clk config fail\n" ); |
333 | return ret; |
334 | } |
335 | } else { |
336 | u32 bitclk = config->sample_rate * |
337 | config->data_width * 2; |
338 | |
339 | ret = clk_set_rate(clk: dev->clk, rate: bitclk); |
340 | if (ret) { |
341 | dev_err(dev->dev, "Can't set I2S clock rate: %d\n" , |
342 | ret); |
343 | return ret; |
344 | } |
345 | } |
346 | } |
347 | return 0; |
348 | } |
349 | |
350 | static int dw_i2s_prepare(struct snd_pcm_substream *substream, |
351 | struct snd_soc_dai *dai) |
352 | { |
353 | struct dw_i2s_dev *dev = snd_soc_dai_get_drvdata(dai); |
354 | |
355 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) |
356 | i2s_write_reg(io_base: dev->i2s_base, TXFFR, val: 1); |
357 | else |
358 | i2s_write_reg(io_base: dev->i2s_base, RXFFR, val: 1); |
359 | |
360 | return 0; |
361 | } |
362 | |
363 | static int dw_i2s_trigger(struct snd_pcm_substream *substream, |
364 | int cmd, struct snd_soc_dai *dai) |
365 | { |
366 | struct dw_i2s_dev *dev = snd_soc_dai_get_drvdata(dai); |
367 | int ret = 0; |
368 | |
369 | switch (cmd) { |
370 | case SNDRV_PCM_TRIGGER_START: |
371 | case SNDRV_PCM_TRIGGER_RESUME: |
372 | case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: |
373 | dev->active++; |
374 | i2s_start(dev, substream); |
375 | break; |
376 | |
377 | case SNDRV_PCM_TRIGGER_STOP: |
378 | case SNDRV_PCM_TRIGGER_SUSPEND: |
379 | case SNDRV_PCM_TRIGGER_PAUSE_PUSH: |
380 | dev->active--; |
381 | i2s_stop(dev, substream); |
382 | break; |
383 | default: |
384 | ret = -EINVAL; |
385 | break; |
386 | } |
387 | return ret; |
388 | } |
389 | |
390 | static int dw_i2s_set_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt) |
391 | { |
392 | struct dw_i2s_dev *dev = snd_soc_dai_get_drvdata(dai: cpu_dai); |
393 | int ret = 0; |
394 | |
395 | switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { |
396 | case SND_SOC_DAIFMT_BC_FC: |
397 | if (dev->capability & DW_I2S_SLAVE) |
398 | ret = 0; |
399 | else |
400 | ret = -EINVAL; |
401 | break; |
402 | case SND_SOC_DAIFMT_BP_FP: |
403 | if (dev->capability & DW_I2S_MASTER) |
404 | ret = 0; |
405 | else |
406 | ret = -EINVAL; |
407 | break; |
408 | case SND_SOC_DAIFMT_BC_FP: |
409 | case SND_SOC_DAIFMT_BP_FC: |
410 | ret = -EINVAL; |
411 | break; |
412 | default: |
413 | dev_dbg(dev->dev, "dwc : Invalid clock provider format\n" ); |
414 | ret = -EINVAL; |
415 | break; |
416 | } |
417 | |
418 | switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { |
419 | case SND_SOC_DAIFMT_I2S: |
420 | case SND_SOC_DAIFMT_LEFT_J: |
421 | case SND_SOC_DAIFMT_RIGHT_J: |
422 | break; |
423 | case SND_SOC_DAIFMT_DSP_A: |
424 | dev->frame_offset = 1; |
425 | break; |
426 | case SND_SOC_DAIFMT_DSP_B: |
427 | dev->frame_offset = 0; |
428 | break; |
429 | default: |
430 | dev_err(dev->dev, "DAI format unsupported" ); |
431 | return -EINVAL; |
432 | } |
433 | |
434 | return ret; |
435 | } |
436 | |
437 | static int dw_i2s_set_tdm_slot(struct snd_soc_dai *cpu_dai, unsigned int tx_mask, |
438 | unsigned int rx_mask, int slots, int slot_width) |
439 | { |
440 | struct dw_i2s_dev *dev = snd_soc_dai_get_drvdata(dai: cpu_dai); |
441 | |
442 | if (slot_width != 32) |
443 | return -EINVAL; |
444 | |
445 | if (slots < 0 || slots > 16) |
446 | return -EINVAL; |
447 | |
448 | if (rx_mask != tx_mask) |
449 | return -EINVAL; |
450 | |
451 | if (!rx_mask) |
452 | return -EINVAL; |
453 | |
454 | dev->tdm_slots = slots; |
455 | dev->tdm_mask = rx_mask; |
456 | |
457 | dev->l_reg = RSLOT_TSLOT(ffs(rx_mask) - 1); |
458 | dev->r_reg = RSLOT_TSLOT(fls(rx_mask) - 1); |
459 | |
460 | return 0; |
461 | } |
462 | |
463 | static int dw_i2s_dai_probe(struct snd_soc_dai *dai) |
464 | { |
465 | struct dw_i2s_dev *dev = snd_soc_dai_get_drvdata(dai); |
466 | |
467 | snd_soc_dai_init_dma_data(dai, playback: &dev->play_dma_data, capture: &dev->capture_dma_data); |
468 | return 0; |
469 | } |
470 | |
471 | static const struct snd_soc_dai_ops dw_i2s_dai_ops = { |
472 | .probe = dw_i2s_dai_probe, |
473 | .startup = dw_i2s_startup, |
474 | .hw_params = dw_i2s_hw_params, |
475 | .prepare = dw_i2s_prepare, |
476 | .trigger = dw_i2s_trigger, |
477 | .set_fmt = dw_i2s_set_fmt, |
478 | .set_tdm_slot = dw_i2s_set_tdm_slot, |
479 | }; |
480 | |
481 | #ifdef CONFIG_PM |
482 | static int dw_i2s_runtime_suspend(struct device *dev) |
483 | { |
484 | struct dw_i2s_dev *dw_dev = dev_get_drvdata(dev); |
485 | |
486 | if (dw_dev->capability & DW_I2S_MASTER) |
487 | clk_disable(clk: dw_dev->clk); |
488 | return 0; |
489 | } |
490 | |
491 | static int dw_i2s_runtime_resume(struct device *dev) |
492 | { |
493 | struct dw_i2s_dev *dw_dev = dev_get_drvdata(dev); |
494 | int ret; |
495 | |
496 | if (dw_dev->capability & DW_I2S_MASTER) { |
497 | ret = clk_enable(clk: dw_dev->clk); |
498 | if (ret) |
499 | return ret; |
500 | } |
501 | return 0; |
502 | } |
503 | |
504 | static int dw_i2s_suspend(struct snd_soc_component *component) |
505 | { |
506 | struct dw_i2s_dev *dev = snd_soc_component_get_drvdata(c: component); |
507 | |
508 | if (dev->capability & DW_I2S_MASTER) |
509 | clk_disable(clk: dev->clk); |
510 | return 0; |
511 | } |
512 | |
513 | static int dw_i2s_resume(struct snd_soc_component *component) |
514 | { |
515 | struct dw_i2s_dev *dev = snd_soc_component_get_drvdata(c: component); |
516 | struct snd_soc_dai *dai; |
517 | int stream, ret; |
518 | |
519 | if (dev->capability & DW_I2S_MASTER) { |
520 | ret = clk_enable(clk: dev->clk); |
521 | if (ret) |
522 | return ret; |
523 | } |
524 | |
525 | for_each_component_dais(component, dai) { |
526 | for_each_pcm_streams(stream) |
527 | if (snd_soc_dai_stream_active(dai, stream)) |
528 | dw_i2s_config(dev, stream); |
529 | } |
530 | |
531 | return 0; |
532 | } |
533 | |
534 | #else |
535 | #define dw_i2s_suspend NULL |
536 | #define dw_i2s_resume NULL |
537 | #endif |
538 | |
539 | static const struct snd_soc_component_driver dw_i2s_component = { |
540 | .name = "dw-i2s" , |
541 | .suspend = dw_i2s_suspend, |
542 | .resume = dw_i2s_resume, |
543 | .legacy_dai_naming = 1, |
544 | }; |
545 | |
546 | /* |
547 | * The following tables allow a direct lookup of various parameters |
548 | * defined in the I2S block's configuration in terms of sound system |
549 | * parameters. Each table is sized to the number of entries possible |
550 | * according to the number of configuration bits describing an I2S |
551 | * block parameter. |
552 | */ |
553 | |
554 | /* Maximum bit resolution of a channel - not uniformly spaced */ |
555 | static const u32 fifo_width[COMP_MAX_WORDSIZE] = { |
556 | 12, 16, 20, 24, 32, 0, 0, 0 |
557 | }; |
558 | |
559 | /* Width of (DMA) bus */ |
560 | static const u32 bus_widths[COMP_MAX_DATA_WIDTH] = { |
561 | DMA_SLAVE_BUSWIDTH_1_BYTE, |
562 | DMA_SLAVE_BUSWIDTH_2_BYTES, |
563 | DMA_SLAVE_BUSWIDTH_4_BYTES, |
564 | DMA_SLAVE_BUSWIDTH_UNDEFINED |
565 | }; |
566 | |
567 | /* PCM format to support channel resolution */ |
568 | static const u32 formats[COMP_MAX_WORDSIZE] = { |
569 | SNDRV_PCM_FMTBIT_S16_LE, |
570 | SNDRV_PCM_FMTBIT_S16_LE, |
571 | SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE, |
572 | SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE, |
573 | SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE, |
574 | 0, |
575 | 0, |
576 | 0 |
577 | }; |
578 | |
579 | static int dw_configure_dai(struct dw_i2s_dev *dev, |
580 | struct snd_soc_dai_driver *dw_i2s_dai, |
581 | unsigned int rates) |
582 | { |
583 | /* |
584 | * Read component parameter registers to extract |
585 | * the I2S block's configuration. |
586 | */ |
587 | u32 comp1 = i2s_read_reg(io_base: dev->i2s_base, reg: dev->i2s_reg_comp1); |
588 | u32 comp2 = i2s_read_reg(io_base: dev->i2s_base, reg: dev->i2s_reg_comp2); |
589 | u32 fifo_depth = 1 << (1 + COMP1_FIFO_DEPTH_GLOBAL(comp1)); |
590 | u32 idx; |
591 | |
592 | if (dev->capability & DWC_I2S_RECORD && |
593 | dev->quirks & DW_I2S_QUIRK_COMP_PARAM1) |
594 | comp1 = comp1 & ~BIT(5); |
595 | |
596 | if (dev->capability & DWC_I2S_PLAY && |
597 | dev->quirks & DW_I2S_QUIRK_COMP_PARAM1) |
598 | comp1 = comp1 & ~BIT(6); |
599 | |
600 | if (COMP1_TX_ENABLED(comp1)) { |
601 | dev_dbg(dev->dev, " designware: play supported\n" ); |
602 | idx = COMP1_TX_WORDSIZE_0(comp1); |
603 | if (WARN_ON(idx >= ARRAY_SIZE(formats))) |
604 | return -EINVAL; |
605 | if (dev->quirks & DW_I2S_QUIRK_16BIT_IDX_OVERRIDE) |
606 | idx = 1; |
607 | dw_i2s_dai->playback.channels_min = MIN_CHANNEL_NUM; |
608 | dw_i2s_dai->playback.channels_max = |
609 | 1 << (COMP1_TX_CHANNELS(comp1) + 1); |
610 | dw_i2s_dai->playback.formats = formats[idx]; |
611 | dw_i2s_dai->playback.rates = rates; |
612 | } |
613 | |
614 | if (COMP1_RX_ENABLED(comp1)) { |
615 | dev_dbg(dev->dev, "designware: record supported\n" ); |
616 | idx = COMP2_RX_WORDSIZE_0(comp2); |
617 | if (WARN_ON(idx >= ARRAY_SIZE(formats))) |
618 | return -EINVAL; |
619 | if (dev->quirks & DW_I2S_QUIRK_16BIT_IDX_OVERRIDE) |
620 | idx = 1; |
621 | dw_i2s_dai->capture.channels_min = MIN_CHANNEL_NUM; |
622 | dw_i2s_dai->capture.channels_max = |
623 | 1 << (COMP1_RX_CHANNELS(comp1) + 1); |
624 | dw_i2s_dai->capture.formats = formats[idx]; |
625 | dw_i2s_dai->capture.rates = rates; |
626 | } |
627 | |
628 | if (COMP1_MODE_EN(comp1)) { |
629 | dev_dbg(dev->dev, "designware: i2s master mode supported\n" ); |
630 | dev->capability |= DW_I2S_MASTER; |
631 | } else { |
632 | dev_dbg(dev->dev, "designware: i2s slave mode supported\n" ); |
633 | dev->capability |= DW_I2S_SLAVE; |
634 | } |
635 | |
636 | dev->fifo_th = fifo_depth / 2; |
637 | return 0; |
638 | } |
639 | |
640 | static int dw_configure_dai_by_pd(struct dw_i2s_dev *dev, |
641 | struct snd_soc_dai_driver *dw_i2s_dai, |
642 | struct resource *res, |
643 | const struct i2s_platform_data *pdata) |
644 | { |
645 | u32 comp1 = i2s_read_reg(io_base: dev->i2s_base, reg: dev->i2s_reg_comp1); |
646 | u32 idx = COMP1_APB_DATA_WIDTH(comp1); |
647 | int ret; |
648 | |
649 | if (WARN_ON(idx >= ARRAY_SIZE(bus_widths))) |
650 | return -EINVAL; |
651 | |
652 | ret = dw_configure_dai(dev, dw_i2s_dai, rates: pdata->snd_rates); |
653 | if (ret < 0) |
654 | return ret; |
655 | |
656 | if (dev->quirks & DW_I2S_QUIRK_16BIT_IDX_OVERRIDE) |
657 | idx = 1; |
658 | |
659 | if (dev->is_jh7110) { |
660 | /* Use platform data and snd_dmaengine_dai_dma_data struct at the same time */ |
661 | u32 comp2 = i2s_read_reg(io_base: dev->i2s_base, I2S_COMP_PARAM_2); |
662 | u32 idx2; |
663 | |
664 | if (COMP1_TX_ENABLED(comp1)) { |
665 | idx2 = COMP1_TX_WORDSIZE_0(comp1); |
666 | dev->play_dma_data.dt.addr = res->start + I2S_TXDMA; |
667 | dev->play_dma_data.dt.fifo_size = dev->fifo_th * 2 * |
668 | (fifo_width[idx2]) >> 8; |
669 | dev->play_dma_data.dt.maxburst = 16; |
670 | } |
671 | if (COMP1_RX_ENABLED(comp1)) { |
672 | idx2 = COMP2_RX_WORDSIZE_0(comp2); |
673 | dev->capture_dma_data.dt.addr = res->start + I2S_RXDMA; |
674 | dev->capture_dma_data.dt.fifo_size = dev->fifo_th * 2 * |
675 | (fifo_width[idx2] >> 8); |
676 | dev->capture_dma_data.dt.maxburst = 16; |
677 | } |
678 | } else { |
679 | /* Set DMA slaves info */ |
680 | dev->play_dma_data.pd.data = pdata->play_dma_data; |
681 | dev->capture_dma_data.pd.data = pdata->capture_dma_data; |
682 | dev->play_dma_data.pd.addr = res->start + I2S_TXDMA; |
683 | dev->capture_dma_data.pd.addr = res->start + I2S_RXDMA; |
684 | dev->play_dma_data.pd.max_burst = 16; |
685 | dev->capture_dma_data.pd.max_burst = 16; |
686 | dev->play_dma_data.pd.addr_width = bus_widths[idx]; |
687 | dev->capture_dma_data.pd.addr_width = bus_widths[idx]; |
688 | dev->play_dma_data.pd.filter = pdata->filter; |
689 | dev->capture_dma_data.pd.filter = pdata->filter; |
690 | } |
691 | |
692 | return 0; |
693 | } |
694 | |
695 | static int dw_configure_dai_by_dt(struct dw_i2s_dev *dev, |
696 | struct snd_soc_dai_driver *dw_i2s_dai, |
697 | struct resource *res) |
698 | { |
699 | u32 comp1 = i2s_read_reg(io_base: dev->i2s_base, I2S_COMP_PARAM_1); |
700 | u32 comp2 = i2s_read_reg(io_base: dev->i2s_base, I2S_COMP_PARAM_2); |
701 | u32 fifo_depth = 1 << (1 + COMP1_FIFO_DEPTH_GLOBAL(comp1)); |
702 | u32 idx2; |
703 | int ret; |
704 | |
705 | ret = dw_configure_dai(dev, dw_i2s_dai, SNDRV_PCM_RATE_8000_192000); |
706 | if (ret < 0) |
707 | return ret; |
708 | |
709 | if (COMP1_TX_ENABLED(comp1)) { |
710 | idx2 = COMP1_TX_WORDSIZE_0(comp1); |
711 | |
712 | dev->capability |= DWC_I2S_PLAY; |
713 | dev->play_dma_data.dt.addr = res->start + I2S_TXDMA; |
714 | dev->play_dma_data.dt.fifo_size = fifo_depth * |
715 | (fifo_width[idx2]) >> 8; |
716 | dev->play_dma_data.dt.maxburst = 16; |
717 | } |
718 | if (COMP1_RX_ENABLED(comp1)) { |
719 | idx2 = COMP2_RX_WORDSIZE_0(comp2); |
720 | |
721 | dev->capability |= DWC_I2S_RECORD; |
722 | dev->capture_dma_data.dt.addr = res->start + I2S_RXDMA; |
723 | dev->capture_dma_data.dt.fifo_size = fifo_depth * |
724 | (fifo_width[idx2] >> 8); |
725 | dev->capture_dma_data.dt.maxburst = 16; |
726 | } |
727 | |
728 | return 0; |
729 | |
730 | } |
731 | |
732 | #ifdef CONFIG_OF |
733 | /* clocks initialization with master mode on JH7110 SoC */ |
734 | static int jh7110_i2s_crg_master_init(struct dw_i2s_dev *dev) |
735 | { |
736 | static struct clk_bulk_data clks[] = { |
737 | { .id = "mclk" }, |
738 | { .id = "mclk_ext" }, |
739 | { .id = "mclk_inner" }, |
740 | { .id = "apb" }, |
741 | { .id = "i2sclk" }, |
742 | }; |
743 | struct reset_control *resets = devm_reset_control_array_get_exclusive(dev: dev->dev); |
744 | int ret; |
745 | struct clk *pclk; |
746 | struct clk *bclk_mst; |
747 | struct clk *mclk; |
748 | struct clk *mclk_ext; |
749 | struct clk *mclk_inner; |
750 | |
751 | if (IS_ERR(ptr: resets)) |
752 | return dev_err_probe(dev: dev->dev, err: PTR_ERR(ptr: resets), fmt: "failed to get i2s resets\n" ); |
753 | |
754 | ret = clk_bulk_get(dev: dev->dev, ARRAY_SIZE(clks), clks); |
755 | if (ret) |
756 | return dev_err_probe(dev: dev->dev, err: ret, fmt: "failed to get i2s clocks\n" ); |
757 | |
758 | mclk = clks[0].clk; |
759 | mclk_ext = clks[1].clk; |
760 | mclk_inner = clks[2].clk; |
761 | pclk = clks[3].clk; |
762 | bclk_mst = clks[4].clk; |
763 | |
764 | ret = clk_prepare_enable(clk: pclk); |
765 | if (ret) |
766 | goto exit; |
767 | |
768 | /* Use inner mclk first and avoid uninitialized gpio for external mclk */ |
769 | ret = clk_set_parent(clk: mclk, parent: mclk_inner); |
770 | if (ret) |
771 | goto err_dis_pclk; |
772 | |
773 | ret = clk_prepare_enable(clk: bclk_mst); |
774 | if (ret) |
775 | goto err_dis_pclk; |
776 | |
777 | /* deassert resets before set clock parent */ |
778 | ret = reset_control_deassert(rstc: resets); |
779 | if (ret) |
780 | goto err_dis_all; |
781 | |
782 | /* external clock (12.288MHz) for Audio */ |
783 | ret = clk_set_parent(clk: mclk, parent: mclk_ext); |
784 | if (ret) |
785 | goto err_dis_all; |
786 | |
787 | /* i2sclk will be got and enabled repeatedly later and should be disabled now. */ |
788 | clk_disable_unprepare(clk: bclk_mst); |
789 | clk_bulk_put(ARRAY_SIZE(clks), clks); |
790 | dev->is_jh7110 = true; |
791 | |
792 | return 0; |
793 | |
794 | err_dis_all: |
795 | clk_disable_unprepare(clk: bclk_mst); |
796 | err_dis_pclk: |
797 | clk_disable_unprepare(clk: pclk); |
798 | exit: |
799 | clk_bulk_put(ARRAY_SIZE(clks), clks); |
800 | return ret; |
801 | } |
802 | |
803 | /* clocks initialization with slave mode on JH7110 SoC */ |
804 | static int jh7110_i2s_crg_slave_init(struct dw_i2s_dev *dev) |
805 | { |
806 | static struct clk_bulk_data clks[] = { |
807 | { .id = "mclk" }, |
808 | { .id = "mclk_ext" }, |
809 | { .id = "apb" }, |
810 | { .id = "bclk_ext" }, |
811 | { .id = "lrck_ext" }, |
812 | { .id = "bclk" }, |
813 | { .id = "lrck" }, |
814 | { .id = "mclk_inner" }, |
815 | { .id = "i2sclk" }, |
816 | }; |
817 | struct reset_control *resets = devm_reset_control_array_get_exclusive(dev: dev->dev); |
818 | int ret; |
819 | struct clk *pclk; |
820 | struct clk *bclk_mst; |
821 | struct clk *bclk_ext; |
822 | struct clk *lrck_ext; |
823 | struct clk *bclk; |
824 | struct clk *lrck; |
825 | struct clk *mclk; |
826 | struct clk *mclk_ext; |
827 | struct clk *mclk_inner; |
828 | |
829 | if (IS_ERR(ptr: resets)) |
830 | return dev_err_probe(dev: dev->dev, err: PTR_ERR(ptr: resets), fmt: "failed to get i2s resets\n" ); |
831 | |
832 | ret = clk_bulk_get(dev: dev->dev, ARRAY_SIZE(clks), clks); |
833 | if (ret) |
834 | return dev_err_probe(dev: dev->dev, err: ret, fmt: "failed to get i2s clocks\n" ); |
835 | |
836 | mclk = clks[0].clk; |
837 | mclk_ext = clks[1].clk; |
838 | pclk = clks[2].clk; |
839 | bclk_ext = clks[3].clk; |
840 | lrck_ext = clks[4].clk; |
841 | bclk = clks[5].clk; |
842 | lrck = clks[6].clk; |
843 | mclk_inner = clks[7].clk; |
844 | bclk_mst = clks[8].clk; |
845 | |
846 | ret = clk_prepare_enable(clk: pclk); |
847 | if (ret) |
848 | goto exit; |
849 | |
850 | ret = clk_set_parent(clk: mclk, parent: mclk_inner); |
851 | if (ret) |
852 | goto err_dis_pclk; |
853 | |
854 | ret = clk_prepare_enable(clk: bclk_mst); |
855 | if (ret) |
856 | goto err_dis_pclk; |
857 | |
858 | ret = reset_control_deassert(rstc: resets); |
859 | if (ret) |
860 | goto err_dis_all; |
861 | |
862 | /* The sources of BCLK and LRCK are the external codec. */ |
863 | ret = clk_set_parent(clk: bclk, parent: bclk_ext); |
864 | if (ret) |
865 | goto err_dis_all; |
866 | |
867 | ret = clk_set_parent(clk: lrck, parent: lrck_ext); |
868 | if (ret) |
869 | goto err_dis_all; |
870 | |
871 | ret = clk_set_parent(clk: mclk, parent: mclk_ext); |
872 | if (ret) |
873 | goto err_dis_all; |
874 | |
875 | /* The i2sclk will be got and enabled repeatedly later and should be disabled now. */ |
876 | clk_disable_unprepare(clk: bclk_mst); |
877 | clk_bulk_put(ARRAY_SIZE(clks), clks); |
878 | dev->is_jh7110 = true; |
879 | |
880 | return 0; |
881 | |
882 | err_dis_all: |
883 | clk_disable_unprepare(clk: bclk_mst); |
884 | err_dis_pclk: |
885 | clk_disable_unprepare(clk: pclk); |
886 | exit: |
887 | clk_bulk_put(ARRAY_SIZE(clks), clks); |
888 | return ret; |
889 | } |
890 | |
891 | /* Special syscon initialization about RX channel with slave mode on JH7110 SoC */ |
892 | static int jh7110_i2srx_crg_init(struct dw_i2s_dev *dev) |
893 | { |
894 | struct regmap *regmap; |
895 | unsigned int args[2]; |
896 | |
897 | regmap = syscon_regmap_lookup_by_phandle_args(np: dev->dev->of_node, |
898 | property: "starfive,syscon" , |
899 | arg_count: 2, out_args: args); |
900 | if (IS_ERR(ptr: regmap)) |
901 | return dev_err_probe(dev: dev->dev, err: PTR_ERR(ptr: regmap), fmt: "getting the regmap failed\n" ); |
902 | |
903 | /* Enable I2Srx with syscon register, args[0]: offset, args[1]: mask */ |
904 | regmap_update_bits(map: regmap, reg: args[0], mask: args[1], val: args[1]); |
905 | |
906 | return jh7110_i2s_crg_slave_init(dev); |
907 | } |
908 | |
909 | static int jh7110_i2stx0_clk_cfg(struct i2s_clk_config_data *config) |
910 | { |
911 | struct dw_i2s_dev *dev = container_of(config, struct dw_i2s_dev, config); |
912 | u32 bclk_rate = config->sample_rate * 64; |
913 | |
914 | return clk_set_rate(clk: dev->clk, rate: bclk_rate); |
915 | } |
916 | #endif /* CONFIG_OF */ |
917 | |
918 | static int dw_i2s_probe(struct platform_device *pdev) |
919 | { |
920 | const struct i2s_platform_data *pdata = pdev->dev.platform_data; |
921 | struct dw_i2s_dev *dev; |
922 | struct resource *res; |
923 | int ret, irq; |
924 | struct snd_soc_dai_driver *dw_i2s_dai; |
925 | const char *clk_id; |
926 | |
927 | dev = devm_kzalloc(dev: &pdev->dev, size: sizeof(*dev), GFP_KERNEL); |
928 | if (!dev) |
929 | return -ENOMEM; |
930 | |
931 | dw_i2s_dai = devm_kzalloc(dev: &pdev->dev, size: sizeof(*dw_i2s_dai), GFP_KERNEL); |
932 | if (!dw_i2s_dai) |
933 | return -ENOMEM; |
934 | |
935 | dw_i2s_dai->ops = &dw_i2s_dai_ops; |
936 | |
937 | dev->i2s_base = devm_platform_get_and_ioremap_resource(pdev, index: 0, res: &res); |
938 | if (IS_ERR(ptr: dev->i2s_base)) |
939 | return PTR_ERR(ptr: dev->i2s_base); |
940 | |
941 | dev->dev = &pdev->dev; |
942 | dev->is_jh7110 = false; |
943 | if (pdata) { |
944 | if (pdata->i2s_pd_init) { |
945 | ret = pdata->i2s_pd_init(dev); |
946 | if (ret) |
947 | return ret; |
948 | } |
949 | } |
950 | |
951 | if (!dev->is_jh7110) { |
952 | dev->reset = devm_reset_control_array_get_optional_shared(dev: &pdev->dev); |
953 | if (IS_ERR(ptr: dev->reset)) |
954 | return PTR_ERR(ptr: dev->reset); |
955 | |
956 | ret = reset_control_deassert(rstc: dev->reset); |
957 | if (ret) |
958 | return ret; |
959 | } |
960 | |
961 | irq = platform_get_irq_optional(pdev, 0); |
962 | if (irq >= 0) { |
963 | ret = devm_request_irq(dev: &pdev->dev, irq, handler: i2s_irq_handler, irqflags: 0, |
964 | devname: pdev->name, dev_id: dev); |
965 | if (ret < 0) { |
966 | dev_err(&pdev->dev, "failed to request irq\n" ); |
967 | goto err_assert_reset; |
968 | } |
969 | } |
970 | |
971 | dev->i2s_reg_comp1 = I2S_COMP_PARAM_1; |
972 | dev->i2s_reg_comp2 = I2S_COMP_PARAM_2; |
973 | if (pdata) { |
974 | dev->capability = pdata->cap; |
975 | clk_id = NULL; |
976 | dev->quirks = pdata->quirks; |
977 | if (dev->quirks & DW_I2S_QUIRK_COMP_REG_OFFSET) { |
978 | dev->i2s_reg_comp1 = pdata->i2s_reg_comp1; |
979 | dev->i2s_reg_comp2 = pdata->i2s_reg_comp2; |
980 | } |
981 | ret = dw_configure_dai_by_pd(dev, dw_i2s_dai, res, pdata); |
982 | } else { |
983 | clk_id = "i2sclk" ; |
984 | ret = dw_configure_dai_by_dt(dev, dw_i2s_dai, res); |
985 | } |
986 | if (ret < 0) |
987 | goto err_assert_reset; |
988 | |
989 | if (dev->capability & DW_I2S_MASTER) { |
990 | if (pdata) { |
991 | dev->i2s_clk_cfg = pdata->i2s_clk_cfg; |
992 | if (!dev->i2s_clk_cfg) { |
993 | dev_err(&pdev->dev, "no clock configure method\n" ); |
994 | ret = -ENODEV; |
995 | goto err_assert_reset; |
996 | } |
997 | } |
998 | dev->clk = devm_clk_get(dev: &pdev->dev, id: clk_id); |
999 | |
1000 | if (IS_ERR(ptr: dev->clk)) { |
1001 | ret = PTR_ERR(ptr: dev->clk); |
1002 | goto err_assert_reset; |
1003 | } |
1004 | |
1005 | ret = clk_prepare_enable(clk: dev->clk); |
1006 | if (ret < 0) |
1007 | goto err_assert_reset; |
1008 | } |
1009 | |
1010 | dev_set_drvdata(dev: &pdev->dev, data: dev); |
1011 | ret = devm_snd_soc_register_component(dev: &pdev->dev, component_driver: &dw_i2s_component, |
1012 | dai_drv: dw_i2s_dai, num_dai: 1); |
1013 | if (ret != 0) { |
1014 | dev_err(&pdev->dev, "not able to register dai\n" ); |
1015 | goto err_clk_disable; |
1016 | } |
1017 | |
1018 | if (!pdata || dev->is_jh7110) { |
1019 | if (irq >= 0) { |
1020 | ret = dw_pcm_register(pdev); |
1021 | dev->use_pio = true; |
1022 | dev->l_reg = LRBR_LTHR(0); |
1023 | dev->r_reg = RRBR_RTHR(0); |
1024 | } else { |
1025 | ret = devm_snd_dmaengine_pcm_register(dev: &pdev->dev, NULL, |
1026 | flags: 0); |
1027 | dev->use_pio = false; |
1028 | } |
1029 | |
1030 | if (ret) { |
1031 | dev_err(&pdev->dev, "could not register pcm: %d\n" , |
1032 | ret); |
1033 | goto err_clk_disable; |
1034 | } |
1035 | } |
1036 | |
1037 | pm_runtime_enable(dev: &pdev->dev); |
1038 | return 0; |
1039 | |
1040 | err_clk_disable: |
1041 | if (dev->capability & DW_I2S_MASTER) |
1042 | clk_disable_unprepare(clk: dev->clk); |
1043 | err_assert_reset: |
1044 | reset_control_assert(rstc: dev->reset); |
1045 | return ret; |
1046 | } |
1047 | |
1048 | static void dw_i2s_remove(struct platform_device *pdev) |
1049 | { |
1050 | struct dw_i2s_dev *dev = dev_get_drvdata(dev: &pdev->dev); |
1051 | |
1052 | if (dev->capability & DW_I2S_MASTER) |
1053 | clk_disable_unprepare(clk: dev->clk); |
1054 | |
1055 | reset_control_assert(rstc: dev->reset); |
1056 | pm_runtime_disable(dev: &pdev->dev); |
1057 | } |
1058 | |
1059 | #ifdef CONFIG_OF |
1060 | static const struct i2s_platform_data jh7110_i2stx0_data = { |
1061 | .cap = DWC_I2S_PLAY | DW_I2S_MASTER, |
1062 | .channel = TWO_CHANNEL_SUPPORT, |
1063 | .snd_fmts = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE, |
1064 | .snd_rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000, |
1065 | .i2s_clk_cfg = jh7110_i2stx0_clk_cfg, |
1066 | .i2s_pd_init = jh7110_i2s_crg_master_init, |
1067 | }; |
1068 | |
1069 | static const struct i2s_platform_data jh7110_i2stx1_data = { |
1070 | .cap = DWC_I2S_PLAY | DW_I2S_SLAVE, |
1071 | .channel = TWO_CHANNEL_SUPPORT, |
1072 | .snd_fmts = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE, |
1073 | .snd_rates = SNDRV_PCM_RATE_8000_192000, |
1074 | .i2s_pd_init = jh7110_i2s_crg_slave_init, |
1075 | }; |
1076 | |
1077 | static const struct i2s_platform_data jh7110_i2srx_data = { |
1078 | .cap = DWC_I2S_RECORD | DW_I2S_SLAVE, |
1079 | .channel = TWO_CHANNEL_SUPPORT, |
1080 | .snd_fmts = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE, |
1081 | .snd_rates = SNDRV_PCM_RATE_8000_192000, |
1082 | .i2s_pd_init = jh7110_i2srx_crg_init, |
1083 | }; |
1084 | |
1085 | static const struct of_device_id dw_i2s_of_match[] = { |
1086 | { .compatible = "snps,designware-i2s" , }, |
1087 | { .compatible = "starfive,jh7110-i2stx0" , .data = &jh7110_i2stx0_data, }, |
1088 | { .compatible = "starfive,jh7110-i2stx1" , .data = &jh7110_i2stx1_data,}, |
1089 | { .compatible = "starfive,jh7110-i2srx" , .data = &jh7110_i2srx_data,}, |
1090 | {}, |
1091 | }; |
1092 | |
1093 | MODULE_DEVICE_TABLE(of, dw_i2s_of_match); |
1094 | #endif |
1095 | |
1096 | static const struct dev_pm_ops dwc_pm_ops = { |
1097 | SET_RUNTIME_PM_OPS(dw_i2s_runtime_suspend, dw_i2s_runtime_resume, NULL) |
1098 | }; |
1099 | |
1100 | static struct platform_driver dw_i2s_driver = { |
1101 | .probe = dw_i2s_probe, |
1102 | .remove_new = dw_i2s_remove, |
1103 | .driver = { |
1104 | .name = "designware-i2s" , |
1105 | .of_match_table = of_match_ptr(dw_i2s_of_match), |
1106 | .pm = &dwc_pm_ops, |
1107 | }, |
1108 | }; |
1109 | |
1110 | module_platform_driver(dw_i2s_driver); |
1111 | |
1112 | MODULE_AUTHOR("Rajeev Kumar <rajeevkumar.linux@gmail.com>" ); |
1113 | MODULE_DESCRIPTION("DESIGNWARE I2S SoC Interface" ); |
1114 | MODULE_LICENSE("GPL" ); |
1115 | MODULE_ALIAS("platform:designware_i2s" ); |
1116 | |