aboutsummaryrefslogtreecommitdiffstats
path: root/sound
diff options
context:
space:
mode:
authorMarcus Cooper <codekipper@gmail.com>2016-02-08 12:09:21 -0500
committerMark Brown <broonie@kernel.org>2016-02-19 11:25:51 -0500
commitf8260afa444b670016f22f2ba1440d9d2e74dcb6 (patch)
tree47ce83c423ad037fe08d1da08f297dc82d044104 /sound
parent8020e1bbaa5e8d8340ab78c71a69ccdd362ab203 (diff)
ASoC: sunxi: Add support for the SPDIF block
The sun4i, sun5i and sun7i SoC families have an SPDIF block which is capable of playback and capture. This patch enables the playback of this block for the sun4i families. Signed-off-by: Marcus Cooper <codekipper@gmail.com> Acked-by: Maxime Ripard <maxime.ripard@free-electrons.com> Signed-off-by: Mark Brown <broonie@kernel.org>
Diffstat (limited to 'sound')
-rw-r--r--sound/soc/sunxi/Kconfig8
-rw-r--r--sound/soc/sunxi/Makefile1
-rw-r--r--sound/soc/sunxi/sun4i-spdif.c550
3 files changed, 559 insertions, 0 deletions
diff --git a/sound/soc/sunxi/Kconfig b/sound/soc/sunxi/Kconfig
index 84c72ec6ad73..ae42294ef688 100644
--- a/sound/soc/sunxi/Kconfig
+++ b/sound/soc/sunxi/Kconfig
@@ -8,4 +8,12 @@ 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_SPDIF
12 tristate "Allwinner A10 SPDIF Support"
13 depends on OF
14 select SND_SOC_GENERIC_DMAENGINE_PCM
15 select REGMAP_MMIO
16 help
17 Say Y or M to add support for the S/PDIF audio block in the Allwinner
18 A10 and affiliated SoCs.
11endmenu 19endmenu
diff --git a/sound/soc/sunxi/Makefile b/sound/soc/sunxi/Makefile
index ea8a08c881d6..8f5e889667f1 100644
--- a/sound/soc/sunxi/Makefile
+++ b/sound/soc/sunxi/Makefile
@@ -1,2 +1,3 @@
1obj-$(CONFIG_SND_SUN4I_CODEC) += sun4i-codec.o 1obj-$(CONFIG_SND_SUN4I_CODEC) += sun4i-codec.o
2 2
3obj-$(CONFIG_SND_SUN4I_SPDIF) += sun4i-spdif.o
diff --git a/sound/soc/sunxi/sun4i-spdif.c b/sound/soc/sunxi/sun4i-spdif.c
new file mode 100644
index 000000000000..0b04fb02125c
--- /dev/null
+++ b/sound/soc/sunxi/sun4i-spdif.c
@@ -0,0 +1,550 @@
1/*
2 * ALSA SoC SPDIF Audio Layer
3 *
4 * Copyright 2015 Andrea Venturi <be17068@iperbole.bo.it>
5 * Copyright 2015 Marcus Cooper <codekipper@gmail.com>
6 *
7 * Based on the Allwinner SDK driver, released under the GPL.
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 */
19
20#include <linux/clk.h>
21#include <linux/delay.h>
22#include <linux/device.h>
23#include <linux/kernel.h>
24#include <linux/init.h>
25#include <linux/regmap.h>
26#include <linux/of_address.h>
27#include <linux/of_device.h>
28#include <linux/ioport.h>
29#include <linux/module.h>
30#include <linux/platform_device.h>
31#include <linux/pm_runtime.h>
32#include <sound/dmaengine_pcm.h>
33#include <sound/pcm_params.h>
34#include <sound/soc.h>
35
36#define SUN4I_SPDIF_CTL (0x00)
37 #define SUN4I_SPDIF_CTL_MCLKDIV(v) ((v) << 4) /* v even */
38 #define SUN4I_SPDIF_CTL_MCLKOUTEN BIT(2)
39 #define SUN4I_SPDIF_CTL_GEN BIT(1)
40 #define SUN4I_SPDIF_CTL_RESET BIT(0)
41
42#define SUN4I_SPDIF_TXCFG (0x04)
43 #define SUN4I_SPDIF_TXCFG_SINGLEMOD BIT(31)
44 #define SUN4I_SPDIF_TXCFG_ASS BIT(17)
45 #define SUN4I_SPDIF_TXCFG_NONAUDIO BIT(16)
46 #define SUN4I_SPDIF_TXCFG_TXRATIO(v) ((v) << 4)
47 #define SUN4I_SPDIF_TXCFG_TXRATIO_MASK GENMASK(8, 4)
48 #define SUN4I_SPDIF_TXCFG_FMTRVD GENMASK(3, 2)
49 #define SUN4I_SPDIF_TXCFG_FMT16BIT (0 << 2)
50 #define SUN4I_SPDIF_TXCFG_FMT20BIT (1 << 2)
51 #define SUN4I_SPDIF_TXCFG_FMT24BIT (2 << 2)
52 #define SUN4I_SPDIF_TXCFG_CHSTMODE BIT(1)
53 #define SUN4I_SPDIF_TXCFG_TXEN BIT(0)
54
55#define SUN4I_SPDIF_RXCFG (0x08)
56 #define SUN4I_SPDIF_RXCFG_LOCKFLAG BIT(4)
57 #define SUN4I_SPDIF_RXCFG_CHSTSRC BIT(3)
58 #define SUN4I_SPDIF_RXCFG_CHSTCP BIT(1)
59 #define SUN4I_SPDIF_RXCFG_RXEN BIT(0)
60
61#define SUN4I_SPDIF_TXFIFO (0x0C)
62
63#define SUN4I_SPDIF_RXFIFO (0x10)
64
65#define SUN4I_SPDIF_FCTL (0x14)
66 #define SUN4I_SPDIF_FCTL_FIFOSRC BIT(31)
67 #define SUN4I_SPDIF_FCTL_FTX BIT(17)
68 #define SUN4I_SPDIF_FCTL_FRX BIT(16)
69 #define SUN4I_SPDIF_FCTL_TXTL(v) ((v) << 8)
70 #define SUN4I_SPDIF_FCTL_TXTL_MASK GENMASK(12, 8)
71 #define SUN4I_SPDIF_FCTL_RXTL(v) ((v) << 3)
72 #define SUN4I_SPDIF_FCTL_RXTL_MASK GENMASK(7, 3)
73 #define SUN4I_SPDIF_FCTL_TXIM BIT(2)
74 #define SUN4I_SPDIF_FCTL_RXOM(v) ((v) << 0)
75 #define SUN4I_SPDIF_FCTL_RXOM_MASK GENMASK(1, 0)
76
77#define SUN4I_SPDIF_FSTA (0x18)
78 #define SUN4I_SPDIF_FSTA_TXE BIT(14)
79 #define SUN4I_SPDIF_FSTA_TXECNTSHT (8)
80 #define SUN4I_SPDIF_FSTA_RXA BIT(6)
81 #define SUN4I_SPDIF_FSTA_RXACNTSHT (0)
82
83#define SUN4I_SPDIF_INT (0x1C)
84 #define SUN4I_SPDIF_INT_RXLOCKEN BIT(18)
85 #define SUN4I_SPDIF_INT_RXUNLOCKEN BIT(17)
86 #define SUN4I_SPDIF_INT_RXPARERREN BIT(16)
87 #define SUN4I_SPDIF_INT_TXDRQEN BIT(7)
88 #define SUN4I_SPDIF_INT_TXUIEN BIT(6)
89 #define SUN4I_SPDIF_INT_TXOIEN BIT(5)
90 #define SUN4I_SPDIF_INT_TXEIEN BIT(4)
91 #define SUN4I_SPDIF_INT_RXDRQEN BIT(2)
92 #define SUN4I_SPDIF_INT_RXOIEN BIT(1)
93 #define SUN4I_SPDIF_INT_RXAIEN BIT(0)
94
95#define SUN4I_SPDIF_ISTA (0x20)
96 #define SUN4I_SPDIF_ISTA_RXLOCKSTA BIT(18)
97 #define SUN4I_SPDIF_ISTA_RXUNLOCKSTA BIT(17)
98 #define SUN4I_SPDIF_ISTA_RXPARERRSTA BIT(16)
99 #define SUN4I_SPDIF_ISTA_TXUSTA BIT(6)
100 #define SUN4I_SPDIF_ISTA_TXOSTA BIT(5)
101 #define SUN4I_SPDIF_ISTA_TXESTA BIT(4)
102 #define SUN4I_SPDIF_ISTA_RXOSTA BIT(1)
103 #define SUN4I_SPDIF_ISTA_RXASTA BIT(0)
104
105#define SUN4I_SPDIF_TXCNT (0x24)
106
107#define SUN4I_SPDIF_RXCNT (0x28)
108
109#define SUN4I_SPDIF_TXCHSTA0 (0x2C)
110 #define SUN4I_SPDIF_TXCHSTA0_CLK(v) ((v) << 28)
111 #define SUN4I_SPDIF_TXCHSTA0_SAMFREQ(v) ((v) << 24)
112 #define SUN4I_SPDIF_TXCHSTA0_SAMFREQ_MASK GENMASK(27, 24)
113 #define SUN4I_SPDIF_TXCHSTA0_CHNUM(v) ((v) << 20)
114 #define SUN4I_SPDIF_TXCHSTA0_CHNUM_MASK GENMASK(23, 20)
115 #define SUN4I_SPDIF_TXCHSTA0_SRCNUM(v) ((v) << 16)
116 #define SUN4I_SPDIF_TXCHSTA0_CATACOD(v) ((v) << 8)
117 #define SUN4I_SPDIF_TXCHSTA0_MODE(v) ((v) << 6)
118 #define SUN4I_SPDIF_TXCHSTA0_EMPHASIS(v) ((v) << 3)
119 #define SUN4I_SPDIF_TXCHSTA0_CP BIT(2)
120 #define SUN4I_SPDIF_TXCHSTA0_AUDIO BIT(1)
121 #define SUN4I_SPDIF_TXCHSTA0_PRO BIT(0)
122
123#define SUN4I_SPDIF_TXCHSTA1 (0x30)
124 #define SUN4I_SPDIF_TXCHSTA1_CGMSA(v) ((v) << 8)
125 #define SUN4I_SPDIF_TXCHSTA1_ORISAMFREQ(v) ((v) << 4)
126 #define SUN4I_SPDIF_TXCHSTA1_ORISAMFREQ_MASK GENMASK(7, 4)
127 #define SUN4I_SPDIF_TXCHSTA1_SAMWORDLEN(v) ((v) << 1)
128 #define SUN4I_SPDIF_TXCHSTA1_MAXWORDLEN BIT(0)
129
130#define SUN4I_SPDIF_RXCHSTA0 (0x34)
131 #define SUN4I_SPDIF_RXCHSTA0_CLK(v) ((v) << 28)
132 #define SUN4I_SPDIF_RXCHSTA0_SAMFREQ(v) ((v) << 24)
133 #define SUN4I_SPDIF_RXCHSTA0_CHNUM(v) ((v) << 20)
134 #define SUN4I_SPDIF_RXCHSTA0_SRCNUM(v) ((v) << 16)
135 #define SUN4I_SPDIF_RXCHSTA0_CATACOD(v) ((v) << 8)
136 #define SUN4I_SPDIF_RXCHSTA0_MODE(v) ((v) << 6)
137 #define SUN4I_SPDIF_RXCHSTA0_EMPHASIS(v) ((v) << 3)
138 #define SUN4I_SPDIF_RXCHSTA0_CP BIT(2)
139 #define SUN4I_SPDIF_RXCHSTA0_AUDIO BIT(1)
140 #define SUN4I_SPDIF_RXCHSTA0_PRO BIT(0)
141
142#define SUN4I_SPDIF_RXCHSTA1 (0x38)
143 #define SUN4I_SPDIF_RXCHSTA1_CGMSA(v) ((v) << 8)
144 #define SUN4I_SPDIF_RXCHSTA1_ORISAMFREQ(v) ((v) << 4)
145 #define SUN4I_SPDIF_RXCHSTA1_SAMWORDLEN(v) ((v) << 1)
146 #define SUN4I_SPDIF_RXCHSTA1_MAXWORDLEN BIT(0)
147
148/* Defines for Sampling Frequency */
149#define SUN4I_SPDIF_SAMFREQ_44_1KHZ 0x0
150#define SUN4I_SPDIF_SAMFREQ_NOT_INDICATED 0x1
151#define SUN4I_SPDIF_SAMFREQ_48KHZ 0x2
152#define SUN4I_SPDIF_SAMFREQ_32KHZ 0x3
153#define SUN4I_SPDIF_SAMFREQ_22_05KHZ 0x4
154#define SUN4I_SPDIF_SAMFREQ_24KHZ 0x6
155#define SUN4I_SPDIF_SAMFREQ_88_2KHZ 0x8
156#define SUN4I_SPDIF_SAMFREQ_76_8KHZ 0x9
157#define SUN4I_SPDIF_SAMFREQ_96KHZ 0xa
158#define SUN4I_SPDIF_SAMFREQ_176_4KHZ 0xc
159#define SUN4I_SPDIF_SAMFREQ_192KHZ 0xe
160
161struct sun4i_spdif_dev {
162 struct platform_device *pdev;
163 struct clk *spdif_clk;
164 struct clk *apb_clk;
165 struct snd_soc_dai_driver cpu_dai_drv;
166 struct regmap *regmap;
167 struct snd_dmaengine_dai_dma_data dma_params_tx;
168};
169
170static void sun4i_spdif_configure(struct sun4i_spdif_dev *host)
171{
172 /* soft reset SPDIF */
173 regmap_write(host->regmap, SUN4I_SPDIF_CTL, SUN4I_SPDIF_CTL_RESET);
174
175 /* flush TX FIFO */
176 regmap_update_bits(host->regmap, SUN4I_SPDIF_FCTL,
177 SUN4I_SPDIF_FCTL_FTX, SUN4I_SPDIF_FCTL_FTX);
178
179 /* clear TX counter */
180 regmap_write(host->regmap, SUN4I_SPDIF_TXCNT, 0);
181}
182
183static void sun4i_snd_txctrl_on(struct snd_pcm_substream *substream,
184 struct sun4i_spdif_dev *host)
185{
186 if (substream->runtime->channels == 1)
187 regmap_update_bits(host->regmap, SUN4I_SPDIF_TXCFG,
188 SUN4I_SPDIF_TXCFG_SINGLEMOD,
189 SUN4I_SPDIF_TXCFG_SINGLEMOD);
190
191 /* SPDIF TX ENABLE */
192 regmap_update_bits(host->regmap, SUN4I_SPDIF_TXCFG,
193 SUN4I_SPDIF_TXCFG_TXEN, SUN4I_SPDIF_TXCFG_TXEN);
194
195 /* DRQ ENABLE */
196 regmap_update_bits(host->regmap, SUN4I_SPDIF_INT,
197 SUN4I_SPDIF_INT_TXDRQEN, SUN4I_SPDIF_INT_TXDRQEN);
198
199 /* Global enable */
200 regmap_update_bits(host->regmap, SUN4I_SPDIF_CTL,
201 SUN4I_SPDIF_CTL_GEN, SUN4I_SPDIF_CTL_GEN);
202}
203
204static void sun4i_snd_txctrl_off(struct snd_pcm_substream *substream,
205 struct sun4i_spdif_dev *host)
206{
207 /* SPDIF TX DISABLE */
208 regmap_update_bits(host->regmap, SUN4I_SPDIF_TXCFG,
209 SUN4I_SPDIF_TXCFG_TXEN, 0);
210
211 /* DRQ DISABLE */
212 regmap_update_bits(host->regmap, SUN4I_SPDIF_INT,
213 SUN4I_SPDIF_INT_TXDRQEN, 0);
214
215 /* Global disable */
216 regmap_update_bits(host->regmap, SUN4I_SPDIF_CTL,
217 SUN4I_SPDIF_CTL_GEN, 0);
218}
219
220static int sun4i_spdif_startup(struct snd_pcm_substream *substream,
221 struct snd_soc_dai *cpu_dai)
222{
223 struct snd_soc_pcm_runtime *rtd = substream->private_data;
224 struct sun4i_spdif_dev *host = snd_soc_dai_get_drvdata(rtd->cpu_dai);
225
226 if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK)
227 return -EINVAL;
228
229 sun4i_spdif_configure(host);
230
231 return 0;
232}
233
234static int sun4i_spdif_hw_params(struct snd_pcm_substream *substream,
235 struct snd_pcm_hw_params *params,
236 struct snd_soc_dai *cpu_dai)
237{
238 int ret = 0;
239 int fmt;
240 unsigned long rate = params_rate(params);
241 u32 mclk_div = 0;
242 unsigned int mclk = 0;
243 u32 reg_val;
244 struct sun4i_spdif_dev *host = snd_soc_dai_get_drvdata(cpu_dai);
245 struct platform_device *pdev = host->pdev;
246
247 /* Add the PCM and raw data select interface */
248 switch (params_channels(params)) {
249 case 1: /* PCM mode */
250 case 2:
251 fmt = 0;
252 break;
253 case 4: /* raw data mode */
254 fmt = SUN4I_SPDIF_TXCFG_NONAUDIO;
255 break;
256 default:
257 return -EINVAL;
258 }
259
260 switch (params_format(params)) {
261 case SNDRV_PCM_FORMAT_S16_LE:
262 fmt |= SUN4I_SPDIF_TXCFG_FMT16BIT;
263 break;
264 case SNDRV_PCM_FORMAT_S20_3LE:
265 fmt |= SUN4I_SPDIF_TXCFG_FMT20BIT;
266 break;
267 case SNDRV_PCM_FORMAT_S24_LE:
268 fmt |= SUN4I_SPDIF_TXCFG_FMT24BIT;
269 break;
270 default:
271 return -EINVAL;
272 }
273
274 switch (rate) {
275 case 22050:
276 case 44100:
277 case 88200:
278 case 176400:
279 mclk = 22579200;
280 break;
281 case 24000:
282 case 32000:
283 case 48000:
284 case 96000:
285 case 192000:
286 mclk = 24576000;
287 break;
288 default:
289 return -EINVAL;
290 }
291
292 ret = clk_set_rate(host->spdif_clk, mclk);
293 if (ret < 0) {
294 dev_err(&pdev->dev,
295 "Setting SPDIF clock rate for %d Hz failed!\n", mclk);
296 return ret;
297 }
298
299 regmap_update_bits(host->regmap, SUN4I_SPDIF_FCTL,
300 SUN4I_SPDIF_FCTL_TXIM, SUN4I_SPDIF_FCTL_TXIM);
301
302 switch (rate) {
303 case 22050:
304 case 24000:
305 mclk_div = 8;
306 break;
307 case 32000:
308 mclk_div = 6;
309 break;
310 case 44100:
311 case 48000:
312 mclk_div = 4;
313 break;
314 case 88200:
315 case 96000:
316 mclk_div = 2;
317 break;
318 case 176400:
319 case 192000:
320 mclk_div = 1;
321 break;
322 default:
323 return -EINVAL;
324 }
325
326 reg_val = 0;
327 reg_val |= SUN4I_SPDIF_TXCFG_ASS;
328 reg_val |= fmt; /* set non audio and bit depth */
329 reg_val |= SUN4I_SPDIF_TXCFG_CHSTMODE;
330 reg_val |= SUN4I_SPDIF_TXCFG_TXRATIO(mclk_div - 1);
331 regmap_write(host->regmap, SUN4I_SPDIF_TXCFG, reg_val);
332
333 return 0;
334}
335
336static int sun4i_spdif_trigger(struct snd_pcm_substream *substream, int cmd,
337 struct snd_soc_dai *dai)
338{
339 int ret = 0;
340 struct sun4i_spdif_dev *host = snd_soc_dai_get_drvdata(dai);
341
342 if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK)
343 return -EINVAL;
344
345 switch (cmd) {
346 case SNDRV_PCM_TRIGGER_START:
347 case SNDRV_PCM_TRIGGER_RESUME:
348 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
349 sun4i_snd_txctrl_on(substream, host);
350 break;
351
352 case SNDRV_PCM_TRIGGER_STOP:
353 case SNDRV_PCM_TRIGGER_SUSPEND:
354 case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
355 sun4i_snd_txctrl_off(substream, host);
356 break;
357
358 default:
359 ret = -EINVAL;
360 break;
361 }
362 return ret;
363}
364
365static int sun4i_spdif_soc_dai_probe(struct snd_soc_dai *dai)
366{
367 struct sun4i_spdif_dev *host = snd_soc_dai_get_drvdata(dai);
368
369 snd_soc_dai_init_dma_data(dai, &host->dma_params_tx, NULL);
370 return 0;
371}
372
373static const struct snd_soc_dai_ops sun4i_spdif_dai_ops = {
374 .startup = sun4i_spdif_startup,
375 .trigger = sun4i_spdif_trigger,
376 .hw_params = sun4i_spdif_hw_params,
377};
378
379static const struct regmap_config sun4i_spdif_regmap_config = {
380 .reg_bits = 32,
381 .reg_stride = 4,
382 .val_bits = 32,
383 .max_register = SUN4I_SPDIF_RXCHSTA1,
384};
385
386#define SUN4I_RATES SNDRV_PCM_RATE_8000_192000
387
388#define SUN4I_FORMATS (SNDRV_PCM_FORMAT_S16_LE | \
389 SNDRV_PCM_FORMAT_S20_3LE | \
390 SNDRV_PCM_FORMAT_S24_LE)
391
392static struct snd_soc_dai_driver sun4i_spdif_dai = {
393 .playback = {
394 .channels_min = 1,
395 .channels_max = 2,
396 .rates = SUN4I_RATES,
397 .formats = SUN4I_FORMATS,
398 },
399 .probe = sun4i_spdif_soc_dai_probe,
400 .ops = &sun4i_spdif_dai_ops,
401 .name = "spdif",
402};
403
404static const struct snd_soc_dapm_widget dit_widgets[] = {
405 SND_SOC_DAPM_OUTPUT("spdif-out"),
406};
407
408static const struct snd_soc_dapm_route dit_routes[] = {
409 { "spdif-out", NULL, "Playback" },
410};
411
412static const struct of_device_id sun4i_spdif_of_match[] = {
413 { .compatible = "allwinner,sun4i-a10-spdif", },
414 { /* sentinel */ }
415};
416MODULE_DEVICE_TABLE(of, sun4i_spdif_of_match);
417
418static const struct snd_soc_component_driver sun4i_spdif_component = {
419 .name = "sun4i-spdif",
420};
421
422static int sun4i_spdif_runtime_suspend(struct device *dev)
423{
424 struct sun4i_spdif_dev *host = dev_get_drvdata(dev);
425
426 clk_disable_unprepare(host->spdif_clk);
427 clk_disable_unprepare(host->apb_clk);
428
429 return 0;
430}
431
432static int sun4i_spdif_runtime_resume(struct device *dev)
433{
434 struct sun4i_spdif_dev *host = dev_get_drvdata(dev);
435
436 clk_prepare_enable(host->spdif_clk);
437 clk_prepare_enable(host->apb_clk);
438
439 return 0;
440}
441
442static int sun4i_spdif_probe(struct platform_device *pdev)
443{
444 struct sun4i_spdif_dev *host;
445 struct resource *res;
446 int ret;
447 void __iomem *base;
448
449 dev_dbg(&pdev->dev, "Entered %s\n", __func__);
450
451 host = devm_kzalloc(&pdev->dev, sizeof(*host), GFP_KERNEL);
452 if (!host)
453 return -ENOMEM;
454
455 host->pdev = pdev;
456
457 /* Initialize this copy of the CPU DAI driver structure */
458 memcpy(&host->cpu_dai_drv, &sun4i_spdif_dai, sizeof(sun4i_spdif_dai));
459 host->cpu_dai_drv.name = dev_name(&pdev->dev);
460
461 /* Get the addresses */
462 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
463 base = devm_ioremap_resource(&pdev->dev, res);
464 if (IS_ERR(base))
465 return PTR_ERR(base);
466
467 host->regmap = devm_regmap_init_mmio(&pdev->dev, base,
468 &sun4i_spdif_regmap_config);
469
470 /* Clocks */
471 host->apb_clk = devm_clk_get(&pdev->dev, "apb");
472 if (IS_ERR(host->apb_clk)) {
473 dev_err(&pdev->dev, "failed to get a apb clock.\n");
474 return PTR_ERR(host->apb_clk);
475 }
476
477 host->spdif_clk = devm_clk_get(&pdev->dev, "spdif");
478 if (IS_ERR(host->spdif_clk)) {
479 dev_err(&pdev->dev, "failed to get a spdif clock.\n");
480 ret = PTR_ERR(host->spdif_clk);
481 goto err_disable_apb_clk;
482 }
483
484 host->dma_params_tx.addr = res->start + SUN4I_SPDIF_TXFIFO;
485 host->dma_params_tx.maxburst = 4;
486 host->dma_params_tx.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
487
488 platform_set_drvdata(pdev, host);
489
490 ret = devm_snd_soc_register_component(&pdev->dev,
491 &sun4i_spdif_component, &sun4i_spdif_dai, 1);
492 if (ret)
493 goto err_disable_apb_clk;
494
495 pm_runtime_enable(&pdev->dev);
496 if (!pm_runtime_enabled(&pdev->dev)) {
497 ret = sun4i_spdif_runtime_resume(&pdev->dev);
498 if (ret)
499 goto err_unregister;
500 }
501
502 ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0);
503 if (ret)
504 goto err_suspend;
505 return 0;
506err_suspend:
507 if (!pm_runtime_status_suspended(&pdev->dev))
508 sun4i_spdif_runtime_suspend(&pdev->dev);
509err_unregister:
510 pm_runtime_disable(&pdev->dev);
511 snd_soc_unregister_component(&pdev->dev);
512err_disable_apb_clk:
513 clk_disable_unprepare(host->apb_clk);
514 return ret;
515}
516
517static int sun4i_spdif_remove(struct platform_device *pdev)
518{
519 pm_runtime_disable(&pdev->dev);
520 if (!pm_runtime_status_suspended(&pdev->dev))
521 sun4i_spdif_runtime_suspend(&pdev->dev);
522
523 snd_soc_unregister_platform(&pdev->dev);
524 snd_soc_unregister_component(&pdev->dev);
525
526 return 0;
527}
528
529static const struct dev_pm_ops sun4i_spdif_pm = {
530 SET_RUNTIME_PM_OPS(sun4i_spdif_runtime_suspend,
531 sun4i_spdif_runtime_resume, NULL)
532};
533
534static struct platform_driver sun4i_spdif_driver = {
535 .driver = {
536 .name = "sun4i-spdif",
537 .of_match_table = of_match_ptr(sun4i_spdif_of_match),
538 .pm = &sun4i_spdif_pm,
539 },
540 .probe = sun4i_spdif_probe,
541 .remove = sun4i_spdif_remove,
542};
543
544module_platform_driver(sun4i_spdif_driver);
545
546MODULE_AUTHOR("Marcus Cooper <codekipper@gmail.com>");
547MODULE_AUTHOR("Andrea Venturi <be17068@iperbole.bo.it>");
548MODULE_DESCRIPTION("Allwinner sun4i SPDIF SoC Interface");
549MODULE_LICENSE("GPL");
550MODULE_ALIAS("platform:sun4i-spdif");