1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * kirkwood-i2s.c
4 *
5 * (c) 2010 Arnaud Patard <apatard@mandriva.com>
6 * (c) 2010 Arnaud Patard <arnaud.patard@rtp-net.org>
7 */
8
9#include <linux/init.h>
10#include <linux/module.h>
11#include <linux/platform_device.h>
12#include <linux/io.h>
13#include <linux/slab.h>
14#include <linux/mbus.h>
15#include <linux/delay.h>
16#include <linux/clk.h>
17#include <sound/pcm.h>
18#include <sound/pcm_params.h>
19#include <sound/soc.h>
20#include <linux/platform_data/asoc-kirkwood.h>
21#include <linux/of.h>
22
23#include "kirkwood.h"
24
25#define KIRKWOOD_I2S_FORMATS \
26 (SNDRV_PCM_FMTBIT_S16_LE | \
27 SNDRV_PCM_FMTBIT_S24_LE | \
28 SNDRV_PCM_FMTBIT_S32_LE)
29
30#define KIRKWOOD_SPDIF_FORMATS \
31 (SNDRV_PCM_FMTBIT_S16_LE | \
32 SNDRV_PCM_FMTBIT_S24_LE)
33
34/* These registers are relative to the second register region -
35 * audio pll configuration.
36 */
37#define A38X_PLL_CONF_REG0 0x0
38#define A38X_PLL_FB_CLK_DIV_OFFSET 10
39#define A38X_PLL_FB_CLK_DIV_MASK 0x7fc00
40#define A38X_PLL_CONF_REG1 0x4
41#define A38X_PLL_FREQ_OFFSET_MASK 0xffff
42#define A38X_PLL_FREQ_OFFSET_VALID BIT(16)
43#define A38X_PLL_SW_RESET BIT(31)
44#define A38X_PLL_CONF_REG2 0x8
45#define A38X_PLL_AUDIO_POSTDIV_MASK 0x7f
46
47/* Bit below belongs to SoC control register corresponding to the third
48 * register region.
49 */
50#define A38X_SPDIF_MODE_ENABLE BIT(27)
51
52static int armada_38x_i2s_init_quirk(struct platform_device *pdev,
53 struct kirkwood_dma_data *priv,
54 struct snd_soc_dai_driver *dai_drv)
55{
56 struct device_node *np = pdev->dev.of_node;
57 u32 reg_val;
58 int i;
59
60 priv->pll_config = devm_platform_ioremap_resource_byname(pdev, name: "pll_regs");
61 if (IS_ERR(ptr: priv->pll_config))
62 return -ENOMEM;
63
64 priv->soc_control = devm_platform_ioremap_resource_byname(pdev, name: "soc_ctrl");
65 if (IS_ERR(ptr: priv->soc_control))
66 return -ENOMEM;
67
68 /* Select one of exceptive modes: I2S or S/PDIF */
69 reg_val = readl(addr: priv->soc_control);
70 if (of_property_read_bool(np, propname: "spdif-mode")) {
71 reg_val |= A38X_SPDIF_MODE_ENABLE;
72 dev_info(&pdev->dev, "using S/PDIF mode\n");
73 } else {
74 reg_val &= ~A38X_SPDIF_MODE_ENABLE;
75 dev_info(&pdev->dev, "using I2S mode\n");
76 }
77 writel(val: reg_val, addr: priv->soc_control);
78
79 /* Update available rates of mclk's fs */
80 for (i = 0; i < 2; i++) {
81 dai_drv[i].playback.rates |= SNDRV_PCM_RATE_192000;
82 dai_drv[i].capture.rates |= SNDRV_PCM_RATE_192000;
83 }
84
85 return 0;
86}
87
88static inline void armada_38x_set_pll(void __iomem *base, unsigned long rate)
89{
90 u32 reg_val;
91 u16 freq_offset = 0x22b0;
92 u8 audio_postdiv, fb_clk_div = 0x1d;
93
94 /* Set frequency offset value to not valid and enable PLL reset */
95 reg_val = readl(addr: base + A38X_PLL_CONF_REG1);
96 reg_val &= ~A38X_PLL_FREQ_OFFSET_VALID;
97 reg_val &= ~A38X_PLL_SW_RESET;
98 writel(val: reg_val, addr: base + A38X_PLL_CONF_REG1);
99
100 udelay(1);
101
102 /* Update PLL parameters */
103 switch (rate) {
104 default:
105 case 44100:
106 freq_offset = 0x735;
107 fb_clk_div = 0x1b;
108 audio_postdiv = 0xc;
109 break;
110 case 48000:
111 audio_postdiv = 0xc;
112 break;
113 case 96000:
114 audio_postdiv = 0x6;
115 break;
116 case 192000:
117 audio_postdiv = 0x3;
118 break;
119 }
120
121 reg_val = readl(addr: base + A38X_PLL_CONF_REG0);
122 reg_val &= ~A38X_PLL_FB_CLK_DIV_MASK;
123 reg_val |= (fb_clk_div << A38X_PLL_FB_CLK_DIV_OFFSET);
124 writel(val: reg_val, addr: base + A38X_PLL_CONF_REG0);
125
126 reg_val = readl(addr: base + A38X_PLL_CONF_REG2);
127 reg_val &= ~A38X_PLL_AUDIO_POSTDIV_MASK;
128 reg_val |= audio_postdiv;
129 writel(val: reg_val, addr: base + A38X_PLL_CONF_REG2);
130
131 reg_val = readl(addr: base + A38X_PLL_CONF_REG1);
132 reg_val &= ~A38X_PLL_FREQ_OFFSET_MASK;
133 reg_val |= freq_offset;
134 writel(val: reg_val, addr: base + A38X_PLL_CONF_REG1);
135
136 udelay(1);
137
138 /* Disable reset */
139 reg_val |= A38X_PLL_SW_RESET;
140 writel(val: reg_val, addr: base + A38X_PLL_CONF_REG1);
141
142 /* Wait 50us for PLL to lock */
143 udelay(50);
144
145 /* Restore frequency offset value validity */
146 reg_val |= A38X_PLL_FREQ_OFFSET_VALID;
147 writel(val: reg_val, addr: base + A38X_PLL_CONF_REG1);
148}
149
150static int kirkwood_i2s_set_fmt(struct snd_soc_dai *cpu_dai,
151 unsigned int fmt)
152{
153 struct kirkwood_dma_data *priv = snd_soc_dai_get_drvdata(dai: cpu_dai);
154 unsigned long mask;
155 unsigned long value;
156
157 switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
158 case SND_SOC_DAIFMT_RIGHT_J:
159 mask = KIRKWOOD_I2S_CTL_RJ;
160 break;
161 case SND_SOC_DAIFMT_LEFT_J:
162 mask = KIRKWOOD_I2S_CTL_LJ;
163 break;
164 case SND_SOC_DAIFMT_I2S:
165 mask = KIRKWOOD_I2S_CTL_I2S;
166 break;
167 default:
168 return -EINVAL;
169 }
170
171 /*
172 * Set same format for playback and record
173 * This avoids some troubles.
174 */
175 value = readl(addr: priv->io+KIRKWOOD_I2S_PLAYCTL);
176 value &= ~KIRKWOOD_I2S_CTL_JUST_MASK;
177 value |= mask;
178 writel(val: value, addr: priv->io+KIRKWOOD_I2S_PLAYCTL);
179
180 value = readl(addr: priv->io+KIRKWOOD_I2S_RECCTL);
181 value &= ~KIRKWOOD_I2S_CTL_JUST_MASK;
182 value |= mask;
183 writel(val: value, addr: priv->io+KIRKWOOD_I2S_RECCTL);
184
185 return 0;
186}
187
188static inline void kirkwood_set_dco(void __iomem *io, unsigned long rate)
189{
190 unsigned long value;
191
192 value = KIRKWOOD_DCO_CTL_OFFSET_0;
193 switch (rate) {
194 default:
195 case 44100:
196 value |= KIRKWOOD_DCO_CTL_FREQ_11;
197 break;
198 case 48000:
199 value |= KIRKWOOD_DCO_CTL_FREQ_12;
200 break;
201 case 96000:
202 value |= KIRKWOOD_DCO_CTL_FREQ_24;
203 break;
204 }
205 writel(val: value, addr: io + KIRKWOOD_DCO_CTL);
206
207 /* wait for dco locked */
208 do {
209 cpu_relax();
210 value = readl(addr: io + KIRKWOOD_DCO_SPCR_STATUS);
211 value &= KIRKWOOD_DCO_SPCR_STATUS_DCO_LOCK;
212 } while (value == 0);
213}
214
215static void kirkwood_set_rate(struct snd_soc_dai *dai,
216 struct kirkwood_dma_data *priv, unsigned long rate)
217{
218 uint32_t clks_ctrl;
219
220 if (IS_ERR(ptr: priv->extclk)) {
221 /* use internal dco for the supported rates
222 * defined in kirkwood_i2s_dai */
223 dev_dbg(dai->dev, "%s: dco set rate = %lu\n",
224 __func__, rate);
225 if (priv->pll_config)
226 armada_38x_set_pll(base: priv->pll_config, rate);
227 else
228 kirkwood_set_dco(io: priv->io, rate);
229
230 clks_ctrl = KIRKWOOD_MCLK_SOURCE_DCO;
231 } else {
232 /* use the external clock for the other rates
233 * defined in kirkwood_i2s_dai_extclk */
234 dev_dbg(dai->dev, "%s: extclk set rate = %lu -> %lu\n",
235 __func__, rate, 256 * rate);
236 clk_set_rate(clk: priv->extclk, rate: 256 * rate);
237
238 clks_ctrl = KIRKWOOD_MCLK_SOURCE_EXTCLK;
239 }
240 writel(val: clks_ctrl, addr: priv->io + KIRKWOOD_CLOCKS_CTRL);
241}
242
243static int kirkwood_i2s_startup(struct snd_pcm_substream *substream,
244 struct snd_soc_dai *dai)
245{
246 struct kirkwood_dma_data *priv = snd_soc_dai_get_drvdata(dai);
247
248 snd_soc_dai_set_dma_data(dai, substream, priv);
249 return 0;
250}
251
252static int kirkwood_i2s_hw_params(struct snd_pcm_substream *substream,
253 struct snd_pcm_hw_params *params,
254 struct snd_soc_dai *dai)
255{
256 struct kirkwood_dma_data *priv = snd_soc_dai_get_drvdata(dai);
257 uint32_t ctl_play, ctl_rec;
258 unsigned int i2s_reg;
259 unsigned long i2s_value;
260
261 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
262 i2s_reg = KIRKWOOD_I2S_PLAYCTL;
263 } else {
264 i2s_reg = KIRKWOOD_I2S_RECCTL;
265 }
266
267 kirkwood_set_rate(dai, priv, rate: params_rate(p: params));
268
269 i2s_value = readl(addr: priv->io+i2s_reg);
270 i2s_value &= ~KIRKWOOD_I2S_CTL_SIZE_MASK;
271
272 /*
273 * Size settings in play/rec i2s control regs and play/rec control
274 * regs must be the same.
275 */
276 switch (params_format(p: params)) {
277 case SNDRV_PCM_FORMAT_S16_LE:
278 i2s_value |= KIRKWOOD_I2S_CTL_SIZE_16;
279 ctl_play = KIRKWOOD_PLAYCTL_SIZE_16_C |
280 KIRKWOOD_PLAYCTL_I2S_EN |
281 KIRKWOOD_PLAYCTL_SPDIF_EN;
282 ctl_rec = KIRKWOOD_RECCTL_SIZE_16_C |
283 KIRKWOOD_RECCTL_I2S_EN |
284 KIRKWOOD_RECCTL_SPDIF_EN;
285 break;
286 /*
287 * doesn't work... S20_3LE != kirkwood 20bit format ?
288 *
289 case SNDRV_PCM_FORMAT_S20_3LE:
290 i2s_value |= KIRKWOOD_I2S_CTL_SIZE_20;
291 ctl_play = KIRKWOOD_PLAYCTL_SIZE_20 |
292 KIRKWOOD_PLAYCTL_I2S_EN;
293 ctl_rec = KIRKWOOD_RECCTL_SIZE_20 |
294 KIRKWOOD_RECCTL_I2S_EN;
295 break;
296 */
297 case SNDRV_PCM_FORMAT_S24_LE:
298 i2s_value |= KIRKWOOD_I2S_CTL_SIZE_24;
299 ctl_play = KIRKWOOD_PLAYCTL_SIZE_24 |
300 KIRKWOOD_PLAYCTL_I2S_EN |
301 KIRKWOOD_PLAYCTL_SPDIF_EN;
302 ctl_rec = KIRKWOOD_RECCTL_SIZE_24 |
303 KIRKWOOD_RECCTL_I2S_EN |
304 KIRKWOOD_RECCTL_SPDIF_EN;
305 break;
306 case SNDRV_PCM_FORMAT_S32_LE:
307 i2s_value |= KIRKWOOD_I2S_CTL_SIZE_32;
308 ctl_play = KIRKWOOD_PLAYCTL_SIZE_32 |
309 KIRKWOOD_PLAYCTL_I2S_EN;
310 ctl_rec = KIRKWOOD_RECCTL_SIZE_32 |
311 KIRKWOOD_RECCTL_I2S_EN;
312 break;
313 default:
314 return -EINVAL;
315 }
316
317 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
318 if (params_channels(p: params) == 1)
319 ctl_play |= KIRKWOOD_PLAYCTL_MONO_BOTH;
320 else
321 ctl_play |= KIRKWOOD_PLAYCTL_MONO_OFF;
322
323 priv->ctl_play &= ~(KIRKWOOD_PLAYCTL_MONO_MASK |
324 KIRKWOOD_PLAYCTL_ENABLE_MASK |
325 KIRKWOOD_PLAYCTL_SIZE_MASK);
326 priv->ctl_play |= ctl_play;
327 } else {
328 priv->ctl_rec &= ~(KIRKWOOD_RECCTL_ENABLE_MASK |
329 KIRKWOOD_RECCTL_SIZE_MASK);
330 priv->ctl_rec |= ctl_rec;
331 }
332
333 writel(val: i2s_value, addr: priv->io+i2s_reg);
334
335 return 0;
336}
337
338static unsigned kirkwood_i2s_play_mute(unsigned ctl)
339{
340 if (!(ctl & KIRKWOOD_PLAYCTL_I2S_EN))
341 ctl |= KIRKWOOD_PLAYCTL_I2S_MUTE;
342 if (!(ctl & KIRKWOOD_PLAYCTL_SPDIF_EN))
343 ctl |= KIRKWOOD_PLAYCTL_SPDIF_MUTE;
344 return ctl;
345}
346
347static int kirkwood_i2s_play_trigger(struct snd_pcm_substream *substream,
348 int cmd, struct snd_soc_dai *dai)
349{
350 struct snd_pcm_runtime *runtime = substream->runtime;
351 struct kirkwood_dma_data *priv = snd_soc_dai_get_drvdata(dai);
352 uint32_t ctl, value;
353
354 ctl = readl(addr: priv->io + KIRKWOOD_PLAYCTL);
355 if ((ctl & KIRKWOOD_PLAYCTL_ENABLE_MASK) == 0) {
356 unsigned timeout = 5000;
357 /*
358 * The Armada510 spec says that if we enter pause mode, the
359 * busy bit must be read back as clear _twice_. Make sure
360 * we respect that otherwise we get DMA underruns.
361 */
362 do {
363 value = ctl;
364 ctl = readl(addr: priv->io + KIRKWOOD_PLAYCTL);
365 if (!((ctl | value) & KIRKWOOD_PLAYCTL_PLAY_BUSY))
366 break;
367 udelay(1);
368 } while (timeout--);
369
370 if ((ctl | value) & KIRKWOOD_PLAYCTL_PLAY_BUSY)
371 dev_notice(dai->dev, "timed out waiting for busy to deassert: %08x\n",
372 ctl);
373 }
374
375 switch (cmd) {
376 case SNDRV_PCM_TRIGGER_START:
377 /* configure */
378 ctl = priv->ctl_play;
379 if (dai->id == 0)
380 ctl &= ~KIRKWOOD_PLAYCTL_SPDIF_EN; /* i2s */
381 else
382 ctl &= ~KIRKWOOD_PLAYCTL_I2S_EN; /* spdif */
383 ctl = kirkwood_i2s_play_mute(ctl);
384 value = ctl & ~KIRKWOOD_PLAYCTL_ENABLE_MASK;
385 writel(val: value, addr: priv->io + KIRKWOOD_PLAYCTL);
386
387 /* enable interrupts */
388 if (!runtime->no_period_wakeup) {
389 value = readl(addr: priv->io + KIRKWOOD_INT_MASK);
390 value |= KIRKWOOD_INT_CAUSE_PLAY_BYTES;
391 writel(val: value, addr: priv->io + KIRKWOOD_INT_MASK);
392 }
393
394 /* enable playback */
395 writel(val: ctl, addr: priv->io + KIRKWOOD_PLAYCTL);
396 break;
397
398 case SNDRV_PCM_TRIGGER_STOP:
399 /* stop audio, disable interrupts */
400 ctl |= KIRKWOOD_PLAYCTL_PAUSE | KIRKWOOD_PLAYCTL_I2S_MUTE |
401 KIRKWOOD_PLAYCTL_SPDIF_MUTE;
402 writel(val: ctl, addr: priv->io + KIRKWOOD_PLAYCTL);
403
404 value = readl(addr: priv->io + KIRKWOOD_INT_MASK);
405 value &= ~KIRKWOOD_INT_CAUSE_PLAY_BYTES;
406 writel(val: value, addr: priv->io + KIRKWOOD_INT_MASK);
407
408 /* disable all playbacks */
409 ctl &= ~KIRKWOOD_PLAYCTL_ENABLE_MASK;
410 writel(val: ctl, addr: priv->io + KIRKWOOD_PLAYCTL);
411 break;
412
413 case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
414 case SNDRV_PCM_TRIGGER_SUSPEND:
415 ctl |= KIRKWOOD_PLAYCTL_PAUSE | KIRKWOOD_PLAYCTL_I2S_MUTE |
416 KIRKWOOD_PLAYCTL_SPDIF_MUTE;
417 writel(val: ctl, addr: priv->io + KIRKWOOD_PLAYCTL);
418 break;
419
420 case SNDRV_PCM_TRIGGER_RESUME:
421 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
422 ctl &= ~(KIRKWOOD_PLAYCTL_PAUSE | KIRKWOOD_PLAYCTL_I2S_MUTE |
423 KIRKWOOD_PLAYCTL_SPDIF_MUTE);
424 ctl = kirkwood_i2s_play_mute(ctl);
425 writel(val: ctl, addr: priv->io + KIRKWOOD_PLAYCTL);
426 break;
427
428 default:
429 return -EINVAL;
430 }
431
432 return 0;
433}
434
435static int kirkwood_i2s_rec_trigger(struct snd_pcm_substream *substream,
436 int cmd, struct snd_soc_dai *dai)
437{
438 struct kirkwood_dma_data *priv = snd_soc_dai_get_drvdata(dai);
439 uint32_t ctl, value;
440
441 value = readl(addr: priv->io + KIRKWOOD_RECCTL);
442
443 switch (cmd) {
444 case SNDRV_PCM_TRIGGER_START:
445 /* configure */
446 ctl = priv->ctl_rec;
447 if (dai->id == 0)
448 ctl &= ~KIRKWOOD_RECCTL_SPDIF_EN; /* i2s */
449 else
450 ctl &= ~KIRKWOOD_RECCTL_I2S_EN; /* spdif */
451
452 value = ctl & ~KIRKWOOD_RECCTL_ENABLE_MASK;
453 writel(val: value, addr: priv->io + KIRKWOOD_RECCTL);
454
455 /* enable interrupts */
456 value = readl(addr: priv->io + KIRKWOOD_INT_MASK);
457 value |= KIRKWOOD_INT_CAUSE_REC_BYTES;
458 writel(val: value, addr: priv->io + KIRKWOOD_INT_MASK);
459
460 /* enable record */
461 writel(val: ctl, addr: priv->io + KIRKWOOD_RECCTL);
462 break;
463
464 case SNDRV_PCM_TRIGGER_STOP:
465 /* stop audio, disable interrupts */
466 value = readl(addr: priv->io + KIRKWOOD_RECCTL);
467 value |= KIRKWOOD_RECCTL_PAUSE | KIRKWOOD_RECCTL_MUTE;
468 writel(val: value, addr: priv->io + KIRKWOOD_RECCTL);
469
470 value = readl(addr: priv->io + KIRKWOOD_INT_MASK);
471 value &= ~KIRKWOOD_INT_CAUSE_REC_BYTES;
472 writel(val: value, addr: priv->io + KIRKWOOD_INT_MASK);
473
474 /* disable all records */
475 value = readl(addr: priv->io + KIRKWOOD_RECCTL);
476 value &= ~KIRKWOOD_RECCTL_ENABLE_MASK;
477 writel(val: value, addr: priv->io + KIRKWOOD_RECCTL);
478 break;
479
480 case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
481 case SNDRV_PCM_TRIGGER_SUSPEND:
482 value = readl(addr: priv->io + KIRKWOOD_RECCTL);
483 value |= KIRKWOOD_RECCTL_PAUSE | KIRKWOOD_RECCTL_MUTE;
484 writel(val: value, addr: priv->io + KIRKWOOD_RECCTL);
485 break;
486
487 case SNDRV_PCM_TRIGGER_RESUME:
488 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
489 value = readl(addr: priv->io + KIRKWOOD_RECCTL);
490 value &= ~(KIRKWOOD_RECCTL_PAUSE | KIRKWOOD_RECCTL_MUTE);
491 writel(val: value, addr: priv->io + KIRKWOOD_RECCTL);
492 break;
493
494 default:
495 return -EINVAL;
496 }
497
498 return 0;
499}
500
501static int kirkwood_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
502 struct snd_soc_dai *dai)
503{
504 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
505 return kirkwood_i2s_play_trigger(substream, cmd, dai);
506 else
507 return kirkwood_i2s_rec_trigger(substream, cmd, dai);
508
509 return 0;
510}
511
512static int kirkwood_i2s_init(struct kirkwood_dma_data *priv)
513{
514 unsigned long value;
515 unsigned int reg_data;
516
517 /* put system in a "safe" state : */
518 /* disable audio interrupts */
519 writel(val: 0xffffffff, addr: priv->io + KIRKWOOD_INT_CAUSE);
520 writel(val: 0, addr: priv->io + KIRKWOOD_INT_MASK);
521
522 reg_data = readl(addr: priv->io + 0x1200);
523 reg_data &= (~(0x333FF8));
524 reg_data |= 0x111D18;
525 writel(val: reg_data, addr: priv->io + 0x1200);
526
527 msleep(msecs: 500);
528
529 reg_data = readl(addr: priv->io + 0x1200);
530 reg_data &= (~(0x333FF8));
531 reg_data |= 0x111D18;
532 writel(val: reg_data, addr: priv->io + 0x1200);
533
534 /* disable playback/record */
535 value = readl(addr: priv->io + KIRKWOOD_PLAYCTL);
536 value &= ~KIRKWOOD_PLAYCTL_ENABLE_MASK;
537 writel(val: value, addr: priv->io + KIRKWOOD_PLAYCTL);
538
539 value = readl(addr: priv->io + KIRKWOOD_RECCTL);
540 value &= ~KIRKWOOD_RECCTL_ENABLE_MASK;
541 writel(val: value, addr: priv->io + KIRKWOOD_RECCTL);
542
543 return 0;
544
545}
546
547static const struct snd_soc_dai_ops kirkwood_i2s_dai_ops = {
548 .startup = kirkwood_i2s_startup,
549 .trigger = kirkwood_i2s_trigger,
550 .hw_params = kirkwood_i2s_hw_params,
551 .set_fmt = kirkwood_i2s_set_fmt,
552};
553
554static struct snd_soc_dai_driver kirkwood_i2s_dai[2] = {
555 {
556 .name = "i2s",
557 .id = 0,
558 .playback = {
559 .channels_min = 1,
560 .channels_max = 2,
561 .rates = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |
562 SNDRV_PCM_RATE_96000,
563 .formats = KIRKWOOD_I2S_FORMATS,
564 },
565 .capture = {
566 .channels_min = 1,
567 .channels_max = 2,
568 .rates = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |
569 SNDRV_PCM_RATE_96000,
570 .formats = KIRKWOOD_I2S_FORMATS,
571 },
572 .ops = &kirkwood_i2s_dai_ops,
573 },
574 {
575 .name = "spdif",
576 .id = 1,
577 .playback = {
578 .channels_min = 1,
579 .channels_max = 2,
580 .rates = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |
581 SNDRV_PCM_RATE_96000,
582 .formats = KIRKWOOD_SPDIF_FORMATS,
583 },
584 .capture = {
585 .channels_min = 1,
586 .channels_max = 2,
587 .rates = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |
588 SNDRV_PCM_RATE_96000,
589 .formats = KIRKWOOD_SPDIF_FORMATS,
590 },
591 .ops = &kirkwood_i2s_dai_ops,
592 },
593};
594
595static struct snd_soc_dai_driver kirkwood_i2s_dai_extclk[2] = {
596 {
597 .name = "i2s",
598 .id = 0,
599 .playback = {
600 .channels_min = 1,
601 .channels_max = 2,
602 .rates = SNDRV_PCM_RATE_CONTINUOUS,
603 .rate_min = 5512,
604 .rate_max = 192000,
605 .formats = KIRKWOOD_I2S_FORMATS,
606 },
607 .capture = {
608 .channels_min = 1,
609 .channels_max = 2,
610 .rates = SNDRV_PCM_RATE_CONTINUOUS,
611 .rate_min = 5512,
612 .rate_max = 192000,
613 .formats = KIRKWOOD_I2S_FORMATS,
614 },
615 .ops = &kirkwood_i2s_dai_ops,
616 },
617 {
618 .name = "spdif",
619 .id = 1,
620 .playback = {
621 .channels_min = 1,
622 .channels_max = 2,
623 .rates = SNDRV_PCM_RATE_CONTINUOUS,
624 .rate_min = 5512,
625 .rate_max = 192000,
626 .formats = KIRKWOOD_SPDIF_FORMATS,
627 },
628 .capture = {
629 .channels_min = 1,
630 .channels_max = 2,
631 .rates = SNDRV_PCM_RATE_CONTINUOUS,
632 .rate_min = 5512,
633 .rate_max = 192000,
634 .formats = KIRKWOOD_SPDIF_FORMATS,
635 },
636 .ops = &kirkwood_i2s_dai_ops,
637 },
638};
639
640static int kirkwood_i2s_dev_probe(struct platform_device *pdev)
641{
642 struct kirkwood_asoc_platform_data *data = pdev->dev.platform_data;
643 struct snd_soc_dai_driver *soc_dai = kirkwood_i2s_dai;
644 struct kirkwood_dma_data *priv;
645 struct device_node *np = pdev->dev.of_node;
646 int err;
647
648 priv = devm_kzalloc(dev: &pdev->dev, size: sizeof(*priv), GFP_KERNEL);
649 if (!priv)
650 return -ENOMEM;
651
652 dev_set_drvdata(dev: &pdev->dev, data: priv);
653
654 if (of_device_is_compatible(device: np, "marvell,armada-380-audio"))
655 priv->io = devm_platform_ioremap_resource_byname(pdev, name: "i2s_regs");
656 else
657 priv->io = devm_platform_ioremap_resource(pdev, index: 0);
658 if (IS_ERR(ptr: priv->io))
659 return PTR_ERR(ptr: priv->io);
660
661 priv->irq = platform_get_irq(pdev, 0);
662 if (priv->irq < 0)
663 return priv->irq;
664
665 if (of_device_is_compatible(device: np, "marvell,armada-380-audio")) {
666 err = armada_38x_i2s_init_quirk(pdev, priv, dai_drv: soc_dai);
667 if (err < 0)
668 return err;
669 /* Set initial pll frequency */
670 armada_38x_set_pll(base: priv->pll_config, rate: 44100);
671 }
672
673 if (np) {
674 priv->burst = 128; /* might be 32 or 128 */
675 } else if (data) {
676 priv->burst = data->burst;
677 } else {
678 dev_err(&pdev->dev, "no DT nor platform data ?!\n");
679 return -EINVAL;
680 }
681
682 priv->clk = devm_clk_get(dev: &pdev->dev, id: np ? "internal" : NULL);
683 if (IS_ERR(ptr: priv->clk)) {
684 dev_err(&pdev->dev, "no clock\n");
685 return PTR_ERR(ptr: priv->clk);
686 }
687
688 priv->extclk = devm_clk_get(dev: &pdev->dev, id: "extclk");
689 if (IS_ERR(ptr: priv->extclk)) {
690 if (PTR_ERR(ptr: priv->extclk) == -EPROBE_DEFER)
691 return -EPROBE_DEFER;
692 } else {
693 if (clk_is_match(p: priv->extclk, q: priv->clk)) {
694 devm_clk_put(dev: &pdev->dev, clk: priv->extclk);
695 priv->extclk = ERR_PTR(error: -EINVAL);
696 } else {
697 dev_info(&pdev->dev, "found external clock\n");
698 clk_prepare_enable(clk: priv->extclk);
699 soc_dai = kirkwood_i2s_dai_extclk;
700 }
701 }
702
703 err = clk_prepare_enable(clk: priv->clk);
704 if (err < 0)
705 return err;
706
707 /* Some sensible defaults - this reflects the powerup values */
708 priv->ctl_play = KIRKWOOD_PLAYCTL_SIZE_24;
709 priv->ctl_rec = KIRKWOOD_RECCTL_SIZE_24;
710
711 /* Select the burst size */
712 if (priv->burst == 32) {
713 priv->ctl_play |= KIRKWOOD_PLAYCTL_BURST_32;
714 priv->ctl_rec |= KIRKWOOD_RECCTL_BURST_32;
715 } else {
716 priv->ctl_play |= KIRKWOOD_PLAYCTL_BURST_128;
717 priv->ctl_rec |= KIRKWOOD_RECCTL_BURST_128;
718 }
719
720 err = snd_soc_register_component(dev: &pdev->dev, component_driver: &kirkwood_soc_component,
721 dai_drv: soc_dai, num_dai: 2);
722 if (err) {
723 dev_err(&pdev->dev, "snd_soc_register_component failed\n");
724 goto err_component;
725 }
726
727 kirkwood_i2s_init(priv);
728
729 return 0;
730
731 err_component:
732 if (!IS_ERR(ptr: priv->extclk))
733 clk_disable_unprepare(clk: priv->extclk);
734 clk_disable_unprepare(clk: priv->clk);
735
736 return err;
737}
738
739static void kirkwood_i2s_dev_remove(struct platform_device *pdev)
740{
741 struct kirkwood_dma_data *priv = dev_get_drvdata(dev: &pdev->dev);
742
743 snd_soc_unregister_component(dev: &pdev->dev);
744 if (!IS_ERR(ptr: priv->extclk))
745 clk_disable_unprepare(clk: priv->extclk);
746 clk_disable_unprepare(clk: priv->clk);
747}
748
749#ifdef CONFIG_OF
750static const struct of_device_id mvebu_audio_of_match[] = {
751 { .compatible = "marvell,kirkwood-audio" },
752 { .compatible = "marvell,dove-audio" },
753 { .compatible = "marvell,armada370-audio" },
754 { .compatible = "marvell,armada-380-audio" },
755 { }
756};
757MODULE_DEVICE_TABLE(of, mvebu_audio_of_match);
758#endif
759
760static struct platform_driver kirkwood_i2s_driver = {
761 .probe = kirkwood_i2s_dev_probe,
762 .remove_new = kirkwood_i2s_dev_remove,
763 .driver = {
764 .name = DRV_NAME,
765 .of_match_table = of_match_ptr(mvebu_audio_of_match),
766 },
767};
768
769module_platform_driver(kirkwood_i2s_driver);
770
771/* Module information */
772MODULE_AUTHOR("Arnaud Patard, <arnaud.patard@rtp-net.org>");
773MODULE_DESCRIPTION("Kirkwood I2S SoC Interface");
774MODULE_LICENSE("GPL");
775MODULE_ALIAS("platform:mvebu-audio");
776

source code of linux/sound/soc/kirkwood/kirkwood-i2s.c