aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMaxime Ripard <maxime.ripard@free-electrons.com>2016-06-15 17:11:21 -0400
committerMark Brown <broonie@kernel.org>2016-06-29 17:42:53 -0400
commitfa7c0d13cb26216f6dec5ef19e028e68b300530d (patch)
tree1ea7ba23e8519a97dae2cd15a16ce63842f8d580
parent38c81719b1658fdff7d3509b8a0c6294ce29c3d4 (diff)
ASoC: sunxi: Add Allwinner A10 Digital Audio driver
The Allwinner A10 and later come with a hardware block that used for the PCM and I2S interfaces. Add a driver for it in ASoC. Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com> Tested-by: Chen-Yu Tsai <wens@csie.org> Signed-off-by: Mark Brown <broonie@kernel.org>
-rw-r--r--sound/soc/sunxi/Kconfig9
-rw-r--r--sound/soc/sunxi/Makefile2
-rw-r--r--sound/soc/sunxi/sun4i-i2s.c703
3 files changed, 713 insertions, 1 deletions
diff --git a/sound/soc/sunxi/Kconfig b/sound/soc/sunxi/Kconfig
index ae42294ef688..2a954bd01fd8 100644
--- a/sound/soc/sunxi/Kconfig
+++ b/sound/soc/sunxi/Kconfig
@@ -8,6 +8,15 @@ config SND_SUN4I_CODEC
8 Select Y or M to add support for the Codec embedded in the Allwinner 8 Select Y or M to add support for the Codec embedded in the Allwinner
9 A10 and affiliated SoCs. 9 A10 and affiliated SoCs.
10 10
11config SND_SUN4I_I2S
12 tristate "Allwinner A10 I2S Support"
13 select SND_SOC_GENERIC_DMAENGINE_PCM
14 select REGMAP_MMIO
15 help
16 Say Y or M if you want to add support for codecs attached to
17 the Allwinner A10 I2S. You will also need to select the
18 individual machine drivers to support below.
19
11config SND_SUN4I_SPDIF 20config SND_SUN4I_SPDIF
12 tristate "Allwinner A10 SPDIF Support" 21 tristate "Allwinner A10 SPDIF Support"
13 depends on OF 22 depends on OF
diff --git a/sound/soc/sunxi/Makefile b/sound/soc/sunxi/Makefile
index 8f5e889667f1..604c7b842837 100644
--- a/sound/soc/sunxi/Makefile
+++ b/sound/soc/sunxi/Makefile
@@ -1,3 +1,3 @@
1obj-$(CONFIG_SND_SUN4I_CODEC) += sun4i-codec.o 1obj-$(CONFIG_SND_SUN4I_CODEC) += sun4i-codec.o
2 2obj-$(CONFIG_SND_SUN4I_I2S) += sun4i-i2s.o
3obj-$(CONFIG_SND_SUN4I_SPDIF) += sun4i-spdif.o 3obj-$(CONFIG_SND_SUN4I_SPDIF) += sun4i-spdif.o
diff --git a/sound/soc/sunxi/sun4i-i2s.c b/sound/soc/sunxi/sun4i-i2s.c
new file mode 100644
index 000000000000..72ed2b8a93e7
--- /dev/null
+++ b/sound/soc/sunxi/sun4i-i2s.c
@@ -0,0 +1,703 @@
1/*
2 * Copyright (C) 2015 Andrea Venturi
3 * Andrea Venturi <be17068@iperbole.bo.it>
4 *
5 * Copyright (C) 2016 Maxime Ripard
6 * Maxime Ripard <maxime.ripard@free-electrons.com>
7 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License as
10 * published by the Free Software Foundation; either version 2 of
11 * the License, or (at your option) any later version.
12 */
13
14#include <linux/clk.h>
15#include <linux/dmaengine.h>
16#include <linux/module.h>
17#include <linux/platform_device.h>
18#include <linux/pm_runtime.h>
19#include <linux/regmap.h>
20
21#include <sound/dmaengine_pcm.h>
22#include <sound/pcm_params.h>
23#include <sound/soc.h>
24#include <sound/soc-dai.h>
25
26#define SUN4I_I2S_CTRL_REG 0x00
27#define SUN4I_I2S_CTRL_SDO_EN_MASK GENMASK(11, 8)
28#define SUN4I_I2S_CTRL_SDO_EN(sdo) BIT(8 + (sdo))
29#define SUN4I_I2S_CTRL_MODE_MASK BIT(5)
30#define SUN4I_I2S_CTRL_MODE_SLAVE (1 << 5)
31#define SUN4I_I2S_CTRL_MODE_MASTER (0 << 5)
32#define SUN4I_I2S_CTRL_TX_EN BIT(2)
33#define SUN4I_I2S_CTRL_RX_EN BIT(1)
34#define SUN4I_I2S_CTRL_GL_EN BIT(0)
35
36#define SUN4I_I2S_FMT0_REG 0x04
37#define SUN4I_I2S_FMT0_LRCLK_POLARITY_MASK BIT(7)
38#define SUN4I_I2S_FMT0_LRCLK_POLARITY_INVERTED (1 << 7)
39#define SUN4I_I2S_FMT0_LRCLK_POLARITY_NORMAL (0 << 7)
40#define SUN4I_I2S_FMT0_BCLK_POLARITY_MASK BIT(6)
41#define SUN4I_I2S_FMT0_BCLK_POLARITY_INVERTED (1 << 6)
42#define SUN4I_I2S_FMT0_BCLK_POLARITY_NORMAL (0 << 6)
43#define SUN4I_I2S_FMT0_SR_MASK GENMASK(5, 4)
44#define SUN4I_I2S_FMT0_SR(sr) ((sr) << 4)
45#define SUN4I_I2S_FMT0_WSS_MASK GENMASK(3, 2)
46#define SUN4I_I2S_FMT0_WSS(wss) ((wss) << 2)
47#define SUN4I_I2S_FMT0_FMT_MASK GENMASK(1, 0)
48#define SUN4I_I2S_FMT0_FMT_RIGHT_J (2 << 0)
49#define SUN4I_I2S_FMT0_FMT_LEFT_J (1 << 0)
50#define SUN4I_I2S_FMT0_FMT_I2S (0 << 0)
51
52#define SUN4I_I2S_FMT1_REG 0x08
53#define SUN4I_I2S_FIFO_TX_REG 0x0c
54#define SUN4I_I2S_FIFO_RX_REG 0x10
55
56#define SUN4I_I2S_FIFO_CTRL_REG 0x14
57#define SUN4I_I2S_FIFO_CTRL_FLUSH_TX BIT(25)
58#define SUN4I_I2S_FIFO_CTRL_FLUSH_RX BIT(24)
59#define SUN4I_I2S_FIFO_CTRL_TX_MODE_MASK BIT(2)
60#define SUN4I_I2S_FIFO_CTRL_TX_MODE(mode) ((mode) << 2)
61#define SUN4I_I2S_FIFO_CTRL_RX_MODE_MASK GENMASK(1, 0)
62#define SUN4I_I2S_FIFO_CTRL_RX_MODE(mode) (mode)
63
64#define SUN4I_I2S_FIFO_STA_REG 0x18
65
66#define SUN4I_I2S_DMA_INT_CTRL_REG 0x1c
67#define SUN4I_I2S_DMA_INT_CTRL_TX_DRQ_EN BIT(7)
68#define SUN4I_I2S_DMA_INT_CTRL_RX_DRQ_EN BIT(3)
69
70#define SUN4I_I2S_INT_STA_REG 0x20
71
72#define SUN4I_I2S_CLK_DIV_REG 0x24
73#define SUN4I_I2S_CLK_DIV_MCLK_EN BIT(7)
74#define SUN4I_I2S_CLK_DIV_BCLK_MASK GENMASK(6, 4)
75#define SUN4I_I2S_CLK_DIV_BCLK(bclk) ((bclk) << 4)
76#define SUN4I_I2S_CLK_DIV_MCLK_MASK GENMASK(3, 0)
77#define SUN4I_I2S_CLK_DIV_MCLK(mclk) ((mclk) << 0)
78
79#define SUN4I_I2S_RX_CNT_REG 0x28
80#define SUN4I_I2S_TX_CNT_REG 0x2c
81
82#define SUN4I_I2S_TX_CHAN_SEL_REG 0x30
83#define SUN4I_I2S_TX_CHAN_SEL(num_chan) (((num_chan) - 1) << 0)
84
85#define SUN4I_I2S_TX_CHAN_MAP_REG 0x34
86#define SUN4I_I2S_TX_CHAN_MAP(chan, sample) ((sample) << (chan << 2))
87
88#define SUN4I_I2S_RX_CHAN_SEL_REG 0x38
89#define SUN4I_I2S_RX_CHAN_MAP_REG 0x3c
90
91struct sun4i_i2s {
92 struct clk *bus_clk;
93 struct clk *mod_clk;
94 struct regmap *regmap;
95
96 struct snd_dmaengine_dai_dma_data playback_dma_data;
97};
98
99struct sun4i_i2s_clk_div {
100 u8 div;
101 u8 val;
102};
103
104static const struct sun4i_i2s_clk_div sun4i_i2s_bclk_div[] = {
105 { .div = 2, .val = 0 },
106 { .div = 4, .val = 1 },
107 { .div = 6, .val = 2 },
108 { .div = 8, .val = 3 },
109 { .div = 12, .val = 4 },
110 { .div = 16, .val = 5 },
111};
112
113static const struct sun4i_i2s_clk_div sun4i_i2s_mclk_div[] = {
114 { .div = 1, .val = 0 },
115 { .div = 2, .val = 1 },
116 { .div = 4, .val = 2 },
117 { .div = 6, .val = 3 },
118 { .div = 8, .val = 4 },
119 { .div = 12, .val = 5 },
120 { .div = 16, .val = 6 },
121 { .div = 24, .val = 7 },
122};
123
124static int sun4i_i2s_get_bclk_div(struct sun4i_i2s *i2s,
125 unsigned int oversample_rate,
126 unsigned int word_size)
127{
128 int div = oversample_rate / word_size / 2;
129 int i;
130
131 for (i = 0; i < ARRAY_SIZE(sun4i_i2s_bclk_div); i++) {
132 const struct sun4i_i2s_clk_div *bdiv = &sun4i_i2s_bclk_div[i];
133
134 if (bdiv->div == div)
135 return bdiv->val;
136 }
137
138 return -EINVAL;
139}
140
141static int sun4i_i2s_get_mclk_div(struct sun4i_i2s *i2s,
142 unsigned int oversample_rate,
143 unsigned int module_rate,
144 unsigned int sampling_rate)
145{
146 int div = module_rate / sampling_rate / oversample_rate;
147 int i;
148
149 for (i = 0; i < ARRAY_SIZE(sun4i_i2s_mclk_div); i++) {
150 const struct sun4i_i2s_clk_div *mdiv = &sun4i_i2s_mclk_div[i];
151
152 if (mdiv->div == div)
153 return mdiv->val;
154 }
155
156 return -EINVAL;
157}
158
159static int sun4i_i2s_oversample_rates[] = { 128, 192, 256, 384, 512, 768 };
160
161static int sun4i_i2s_set_clk_rate(struct sun4i_i2s *i2s,
162 unsigned int rate,
163 unsigned int word_size)
164{
165 unsigned int clk_rate;
166 int bclk_div, mclk_div;
167 int ret, i;
168
169 switch (rate) {
170 case 176400:
171 case 88200:
172 case 44100:
173 case 22050:
174 case 11025:
175 clk_rate = 22579200;
176 break;
177
178 case 192000:
179 case 128000:
180 case 96000:
181 case 64000:
182 case 48000:
183 case 32000:
184 case 24000:
185 case 16000:
186 case 12000:
187 case 8000:
188 clk_rate = 24576000;
189 break;
190
191 default:
192 return -EINVAL;
193 }
194
195 ret = clk_set_rate(i2s->mod_clk, clk_rate);
196 if (ret)
197 return ret;
198
199 /* Always favor the highest oversampling rate */
200 for (i = (ARRAY_SIZE(sun4i_i2s_oversample_rates) - 1); i >= 0; i--) {
201 unsigned int oversample_rate = sun4i_i2s_oversample_rates[i];
202
203 bclk_div = sun4i_i2s_get_bclk_div(i2s, oversample_rate,
204 word_size);
205 mclk_div = sun4i_i2s_get_mclk_div(i2s, oversample_rate,
206 clk_rate,
207 rate);
208
209 if ((bclk_div >= 0) && (mclk_div >= 0))
210 break;
211 }
212
213 if ((bclk_div < 0) || (mclk_div < 0))
214 return -EINVAL;
215
216 regmap_write(i2s->regmap, SUN4I_I2S_CLK_DIV_REG,
217 SUN4I_I2S_CLK_DIV_BCLK(bclk_div) |
218 SUN4I_I2S_CLK_DIV_MCLK(mclk_div) |
219 SUN4I_I2S_CLK_DIV_MCLK_EN);
220
221 return 0;
222}
223
224static int sun4i_i2s_hw_params(struct snd_pcm_substream *substream,
225 struct snd_pcm_hw_params *params,
226 struct snd_soc_dai *dai)
227{
228 struct sun4i_i2s *i2s = snd_soc_dai_get_drvdata(dai);
229 int sr, wss;
230 u32 width;
231
232 if (params_channels(params) != 2)
233 return -EINVAL;
234
235 switch (params_physical_width(params)) {
236 case 16:
237 width = DMA_SLAVE_BUSWIDTH_2_BYTES;
238 break;
239 default:
240 return -EINVAL;
241 }
242 i2s->playback_dma_data.addr_width = width;
243
244 switch (params_width(params)) {
245 case 16:
246 sr = 0;
247 wss = 0;
248 break;
249
250 default:
251 return -EINVAL;
252 }
253
254 regmap_update_bits(i2s->regmap, SUN4I_I2S_FMT0_REG,
255 SUN4I_I2S_FMT0_WSS_MASK | SUN4I_I2S_FMT0_SR_MASK,
256 SUN4I_I2S_FMT0_WSS(wss) | SUN4I_I2S_FMT0_SR(sr));
257
258 return sun4i_i2s_set_clk_rate(i2s, params_rate(params),
259 params_width(params));
260}
261
262static int sun4i_i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
263{
264 struct sun4i_i2s *i2s = snd_soc_dai_get_drvdata(dai);
265 u32 val;
266
267 /* DAI Mode */
268 switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
269 case SND_SOC_DAIFMT_I2S:
270 val = SUN4I_I2S_FMT0_FMT_I2S;
271 break;
272 case SND_SOC_DAIFMT_LEFT_J:
273 val = SUN4I_I2S_FMT0_FMT_LEFT_J;
274 break;
275 case SND_SOC_DAIFMT_RIGHT_J:
276 val = SUN4I_I2S_FMT0_FMT_RIGHT_J;
277 break;
278 default:
279 return -EINVAL;
280 }
281
282 regmap_update_bits(i2s->regmap, SUN4I_I2S_FMT0_REG,
283 SUN4I_I2S_FMT0_FMT_MASK,
284 val);
285
286 /* DAI clock polarity */
287 switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
288 case SND_SOC_DAIFMT_IB_IF:
289 /* Invert both clocks */
290 val = SUN4I_I2S_FMT0_BCLK_POLARITY_INVERTED |
291 SUN4I_I2S_FMT0_LRCLK_POLARITY_INVERTED;
292 break;
293 case SND_SOC_DAIFMT_IB_NF:
294 /* Invert bit clock */
295 val = SUN4I_I2S_FMT0_BCLK_POLARITY_INVERTED |
296 SUN4I_I2S_FMT0_LRCLK_POLARITY_NORMAL;
297 break;
298 case SND_SOC_DAIFMT_NB_IF:
299 /* Invert frame clock */
300 val = SUN4I_I2S_FMT0_LRCLK_POLARITY_INVERTED |
301 SUN4I_I2S_FMT0_BCLK_POLARITY_NORMAL;
302 break;
303 case SND_SOC_DAIFMT_NB_NF:
304 /* Nothing to do for both normal cases */
305 val = SUN4I_I2S_FMT0_BCLK_POLARITY_NORMAL |
306 SUN4I_I2S_FMT0_LRCLK_POLARITY_NORMAL;
307 break;
308 default:
309 return -EINVAL;
310 }
311
312 regmap_update_bits(i2s->regmap, SUN4I_I2S_FMT0_REG,
313 SUN4I_I2S_FMT0_BCLK_POLARITY_MASK |
314 SUN4I_I2S_FMT0_LRCLK_POLARITY_MASK,
315 val);
316
317 /* DAI clock master masks */
318 switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
319 case SND_SOC_DAIFMT_CBS_CFS:
320 /* BCLK and LRCLK master */
321 val = SUN4I_I2S_CTRL_MODE_MASTER;
322 break;
323 case SND_SOC_DAIFMT_CBM_CFM:
324 /* BCLK and LRCLK slave */
325 val = SUN4I_I2S_CTRL_MODE_SLAVE;
326 break;
327 default:
328 return -EINVAL;
329 }
330
331 regmap_update_bits(i2s->regmap, SUN4I_I2S_CTRL_REG,
332 SUN4I_I2S_CTRL_MODE_MASK,
333 val);
334
335 /* Set significant bits in our FIFOs */
336 regmap_update_bits(i2s->regmap, SUN4I_I2S_FIFO_CTRL_REG,
337 SUN4I_I2S_FIFO_CTRL_TX_MODE_MASK |
338 SUN4I_I2S_FIFO_CTRL_RX_MODE_MASK,
339 SUN4I_I2S_FIFO_CTRL_TX_MODE(1) |
340 SUN4I_I2S_FIFO_CTRL_RX_MODE(1));
341 return 0;
342}
343
344static void sun4i_i2s_start_playback(struct sun4i_i2s *i2s)
345{
346 /* Flush TX FIFO */
347 regmap_update_bits(i2s->regmap, SUN4I_I2S_FIFO_CTRL_REG,
348 SUN4I_I2S_FIFO_CTRL_FLUSH_TX,
349 SUN4I_I2S_FIFO_CTRL_FLUSH_TX);
350
351 /* Clear TX counter */
352 regmap_write(i2s->regmap, SUN4I_I2S_TX_CNT_REG, 0);
353
354 /* Enable TX Block */
355 regmap_update_bits(i2s->regmap, SUN4I_I2S_CTRL_REG,
356 SUN4I_I2S_CTRL_TX_EN,
357 SUN4I_I2S_CTRL_TX_EN);
358
359 /* Enable TX DRQ */
360 regmap_update_bits(i2s->regmap, SUN4I_I2S_DMA_INT_CTRL_REG,
361 SUN4I_I2S_DMA_INT_CTRL_TX_DRQ_EN,
362 SUN4I_I2S_DMA_INT_CTRL_TX_DRQ_EN);
363}
364
365
366static void sun4i_i2s_stop_playback(struct sun4i_i2s *i2s)
367{
368 /* Disable TX Block */
369 regmap_update_bits(i2s->regmap, SUN4I_I2S_CTRL_REG,
370 SUN4I_I2S_CTRL_TX_EN,
371 0);
372
373 /* Disable TX DRQ */
374 regmap_update_bits(i2s->regmap, SUN4I_I2S_DMA_INT_CTRL_REG,
375 SUN4I_I2S_DMA_INT_CTRL_TX_DRQ_EN,
376 0);
377}
378
379static int sun4i_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
380 struct snd_soc_dai *dai)
381{
382 struct sun4i_i2s *i2s = snd_soc_dai_get_drvdata(dai);
383
384 switch (cmd) {
385 case SNDRV_PCM_TRIGGER_START:
386 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
387 case SNDRV_PCM_TRIGGER_RESUME:
388 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
389 sun4i_i2s_start_playback(i2s);
390 else
391 return -EINVAL;
392 break;
393
394 case SNDRV_PCM_TRIGGER_STOP:
395 case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
396 case SNDRV_PCM_TRIGGER_SUSPEND:
397 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
398 sun4i_i2s_stop_playback(i2s);
399 else
400 return -EINVAL;
401 break;
402
403 default:
404 return -EINVAL;
405 }
406
407 return 0;
408}
409
410static int sun4i_i2s_startup(struct snd_pcm_substream *substream,
411 struct snd_soc_dai *dai)
412{
413 struct sun4i_i2s *i2s = snd_soc_dai_get_drvdata(dai);
414
415 /* Enable the whole hardware block */
416 regmap_write(i2s->regmap, SUN4I_I2S_CTRL_REG,
417 SUN4I_I2S_CTRL_GL_EN);
418
419 /* Enable the first output line */
420 regmap_update_bits(i2s->regmap, SUN4I_I2S_CTRL_REG,
421 SUN4I_I2S_CTRL_SDO_EN_MASK,
422 SUN4I_I2S_CTRL_SDO_EN(0));
423
424 /* Enable the first two channels */
425 regmap_write(i2s->regmap, SUN4I_I2S_TX_CHAN_SEL_REG,
426 SUN4I_I2S_TX_CHAN_SEL(2));
427
428 /* Map them to the two first samples coming in */
429 regmap_write(i2s->regmap, SUN4I_I2S_TX_CHAN_MAP_REG,
430 SUN4I_I2S_TX_CHAN_MAP(0, 0) | SUN4I_I2S_TX_CHAN_MAP(1, 1));
431
432 return clk_prepare_enable(i2s->mod_clk);
433}
434
435static void sun4i_i2s_shutdown(struct snd_pcm_substream *substream,
436 struct snd_soc_dai *dai)
437{
438 struct sun4i_i2s *i2s = snd_soc_dai_get_drvdata(dai);
439
440 clk_disable_unprepare(i2s->mod_clk);
441
442 /* Disable our output lines */
443 regmap_update_bits(i2s->regmap, SUN4I_I2S_CTRL_REG,
444 SUN4I_I2S_CTRL_SDO_EN_MASK, 0);
445
446 /* Disable the whole hardware block */
447 regmap_write(i2s->regmap, SUN4I_I2S_CTRL_REG, 0);
448}
449
450static const struct snd_soc_dai_ops sun4i_i2s_dai_ops = {
451 .hw_params = sun4i_i2s_hw_params,
452 .set_fmt = sun4i_i2s_set_fmt,
453 .shutdown = sun4i_i2s_shutdown,
454 .startup = sun4i_i2s_startup,
455 .trigger = sun4i_i2s_trigger,
456};
457
458static int sun4i_i2s_dai_probe(struct snd_soc_dai *dai)
459{
460 struct sun4i_i2s *i2s = snd_soc_dai_get_drvdata(dai);
461
462 snd_soc_dai_init_dma_data(dai, &i2s->playback_dma_data, NULL);
463
464 snd_soc_dai_set_drvdata(dai, i2s);
465
466 return 0;
467}
468
469static struct snd_soc_dai_driver sun4i_i2s_dai = {
470 .probe = sun4i_i2s_dai_probe,
471 .playback = {
472 .stream_name = "Playback",
473 .channels_min = 2,
474 .channels_max = 2,
475 .rates = SNDRV_PCM_RATE_8000_192000,
476 .formats = SNDRV_PCM_FMTBIT_S16_LE,
477 },
478 .ops = &sun4i_i2s_dai_ops,
479 .symmetric_rates = 1,
480};
481
482static const struct snd_soc_component_driver sun4i_i2s_component = {
483 .name = "sun4i-dai",
484};
485
486static bool sun4i_i2s_rd_reg(struct device *dev, unsigned int reg)
487{
488 switch (reg) {
489 case SUN4I_I2S_FIFO_TX_REG:
490 return false;
491
492 default:
493 return true;
494 }
495}
496
497static bool sun4i_i2s_wr_reg(struct device *dev, unsigned int reg)
498{
499 switch (reg) {
500 case SUN4I_I2S_FIFO_RX_REG:
501 case SUN4I_I2S_FIFO_STA_REG:
502 return false;
503
504 default:
505 return true;
506 }
507}
508
509static bool sun4i_i2s_volatile_reg(struct device *dev, unsigned int reg)
510{
511 switch (reg) {
512 case SUN4I_I2S_FIFO_RX_REG:
513 case SUN4I_I2S_INT_STA_REG:
514 case SUN4I_I2S_RX_CNT_REG:
515 case SUN4I_I2S_TX_CNT_REG:
516 return true;
517
518 default:
519 return false;
520 }
521}
522
523static const struct reg_default sun4i_i2s_reg_defaults[] = {
524 { SUN4I_I2S_CTRL_REG, 0x00000000 },
525 { SUN4I_I2S_FMT0_REG, 0x0000000c },
526 { SUN4I_I2S_FMT1_REG, 0x00004020 },
527 { SUN4I_I2S_FIFO_CTRL_REG, 0x000400f0 },
528 { SUN4I_I2S_DMA_INT_CTRL_REG, 0x00000000 },
529 { SUN4I_I2S_CLK_DIV_REG, 0x00000000 },
530 { SUN4I_I2S_TX_CHAN_SEL_REG, 0x00000001 },
531 { SUN4I_I2S_TX_CHAN_MAP_REG, 0x76543210 },
532 { SUN4I_I2S_RX_CHAN_SEL_REG, 0x00000001 },
533 { SUN4I_I2S_RX_CHAN_MAP_REG, 0x00003210 },
534};
535
536static const struct regmap_config sun4i_i2s_regmap_config = {
537 .reg_bits = 32,
538 .reg_stride = 4,
539 .val_bits = 32,
540 .max_register = SUN4I_I2S_RX_CHAN_MAP_REG,
541
542 .cache_type = REGCACHE_FLAT,
543 .reg_defaults = sun4i_i2s_reg_defaults,
544 .num_reg_defaults = ARRAY_SIZE(sun4i_i2s_reg_defaults),
545 .writeable_reg = sun4i_i2s_wr_reg,
546 .readable_reg = sun4i_i2s_rd_reg,
547 .volatile_reg = sun4i_i2s_volatile_reg,
548};
549
550static int sun4i_i2s_runtime_resume(struct device *dev)
551{
552 struct sun4i_i2s *i2s = dev_get_drvdata(dev);
553 int ret;
554
555 ret = clk_prepare_enable(i2s->bus_clk);
556 if (ret) {
557 dev_err(dev, "Failed to enable bus clock\n");
558 return ret;
559 }
560
561 regcache_cache_only(i2s->regmap, false);
562 regcache_mark_dirty(i2s->regmap);
563
564 ret = regcache_sync(i2s->regmap);
565 if (ret) {
566 dev_err(dev, "Failed to sync regmap cache\n");
567 goto err_disable_clk;
568 }
569
570 return 0;
571
572err_disable_clk:
573 clk_disable_unprepare(i2s->bus_clk);
574 return ret;
575}
576
577static int sun4i_i2s_runtime_suspend(struct device *dev)
578{
579 struct sun4i_i2s *i2s = dev_get_drvdata(dev);
580
581 regcache_cache_only(i2s->regmap, true);
582
583 clk_disable_unprepare(i2s->bus_clk);
584
585 return 0;
586}
587
588static int sun4i_i2s_probe(struct platform_device *pdev)
589{
590 struct sun4i_i2s *i2s;
591 struct resource *res;
592 void __iomem *regs;
593 int irq, ret;
594
595 i2s = devm_kzalloc(&pdev->dev, sizeof(*i2s), GFP_KERNEL);
596 if (!i2s)
597 return -ENOMEM;
598 platform_set_drvdata(pdev, i2s);
599
600 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
601 regs = devm_ioremap_resource(&pdev->dev, res);
602 if (IS_ERR(regs)) {
603 dev_err(&pdev->dev, "Can't request IO region\n");
604 return PTR_ERR(regs);
605 }
606
607 irq = platform_get_irq(pdev, 0);
608 if (irq < 0) {
609 dev_err(&pdev->dev, "Can't retrieve our interrupt\n");
610 return irq;
611 }
612
613 i2s->bus_clk = devm_clk_get(&pdev->dev, "apb");
614 if (IS_ERR(i2s->bus_clk)) {
615 dev_err(&pdev->dev, "Can't get our bus clock\n");
616 return PTR_ERR(i2s->bus_clk);
617 }
618
619 i2s->regmap = devm_regmap_init_mmio(&pdev->dev, regs,
620 &sun4i_i2s_regmap_config);
621 if (IS_ERR(i2s->regmap)) {
622 dev_err(&pdev->dev, "Regmap initialisation failed\n");
623 return PTR_ERR(i2s->regmap);
624 };
625
626 i2s->mod_clk = devm_clk_get(&pdev->dev, "mod");
627 if (IS_ERR(i2s->mod_clk)) {
628 dev_err(&pdev->dev, "Can't get our mod clock\n");
629 return PTR_ERR(i2s->mod_clk);
630 }
631
632 i2s->playback_dma_data.addr = res->start + SUN4I_I2S_FIFO_TX_REG;
633 i2s->playback_dma_data.maxburst = 4;
634
635 pm_runtime_enable(&pdev->dev);
636 if (!pm_runtime_enabled(&pdev->dev)) {
637 ret = sun4i_i2s_runtime_resume(&pdev->dev);
638 if (ret)
639 goto err_pm_disable;
640 }
641
642 ret = devm_snd_soc_register_component(&pdev->dev,
643 &sun4i_i2s_component,
644 &sun4i_i2s_dai, 1);
645 if (ret) {
646 dev_err(&pdev->dev, "Could not register DAI\n");
647 goto err_suspend;
648 }
649
650 ret = snd_dmaengine_pcm_register(&pdev->dev, NULL, 0);
651 if (ret) {
652 dev_err(&pdev->dev, "Could not register PCM\n");
653 goto err_suspend;
654 }
655
656 return 0;
657
658err_suspend:
659 if (!pm_runtime_status_suspended(&pdev->dev))
660 sun4i_i2s_runtime_suspend(&pdev->dev);
661err_pm_disable:
662 pm_runtime_disable(&pdev->dev);
663
664 return ret;
665}
666
667static int sun4i_i2s_remove(struct platform_device *pdev)
668{
669 snd_dmaengine_pcm_unregister(&pdev->dev);
670
671 pm_runtime_disable(&pdev->dev);
672 if (!pm_runtime_status_suspended(&pdev->dev))
673 sun4i_i2s_runtime_suspend(&pdev->dev);
674
675 return 0;
676}
677
678static const struct of_device_id sun4i_i2s_match[] = {
679 { .compatible = "allwinner,sun4i-a10-i2s", },
680 {}
681};
682MODULE_DEVICE_TABLE(of, sun4i_i2s_match);
683
684static const struct dev_pm_ops sun4i_i2s_pm_ops = {
685 .runtime_resume = sun4i_i2s_runtime_resume,
686 .runtime_suspend = sun4i_i2s_runtime_suspend,
687};
688
689static struct platform_driver sun4i_i2s_driver = {
690 .probe = sun4i_i2s_probe,
691 .remove = sun4i_i2s_remove,
692 .driver = {
693 .name = "sun4i-i2s",
694 .of_match_table = sun4i_i2s_match,
695 .pm = &sun4i_i2s_pm_ops,
696 },
697};
698module_platform_driver(sun4i_i2s_driver);
699
700MODULE_AUTHOR("Andrea Venturi <be17068@iperbole.bo.it>");
701MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>");
702MODULE_DESCRIPTION("Allwinner A10 I2S driver");
703MODULE_LICENSE("GPL");