aboutsummaryrefslogtreecommitdiffstats
path: root/sound/soc/tegra/tegra30_spdif.c
diff options
context:
space:
mode:
Diffstat (limited to 'sound/soc/tegra/tegra30_spdif.c')
-rw-r--r--sound/soc/tegra/tegra30_spdif.c505
1 files changed, 505 insertions, 0 deletions
diff --git a/sound/soc/tegra/tegra30_spdif.c b/sound/soc/tegra/tegra30_spdif.c
new file mode 100644
index 00000000000..038127c0afb
--- /dev/null
+++ b/sound/soc/tegra/tegra30_spdif.c
@@ -0,0 +1,505 @@
1/*
2 * tegra30_spdif.c - Tegra30 SPDIF driver
3 *
4 * Author: Sumit Bhattacharya <sumitb@nvidia.com>
5 * Copyright (C) 2011 - NVIDIA, Inc.
6 *
7 * Based on code copyright/by:
8 *
9 * Copyright (c) 2009-2011, NVIDIA Corporation.
10 * Scott Peterson <speterson@nvidia.com>
11 *
12 * Copyright (C) 2010 Google, Inc.
13 * Iliyan Malchev <malchev@google.com>
14 *
15 * This program is free software; you can redistribute it and/or
16 * modify it under the terms of the GNU General Public License
17 * version 2 as published by the Free Software Foundation.
18 *
19 * This program is distributed in the hope that it will be useful, but
20 * WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22 * General Public License for more details.
23 *
24 * You should have received a copy of the GNU General Public License
25 * along with this program; if not, write to the Free Software
26 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
27 * 02110-1301 USA
28 *
29 */
30
31#include <linux/clk.h>
32#include <linux/module.h>
33#include <linux/debugfs.h>
34#include <linux/device.h>
35#include <linux/platform_device.h>
36#include <linux/seq_file.h>
37#include <linux/slab.h>
38#include <linux/io.h>
39#include <mach/iomap.h>
40#include <mach/hdmi-audio.h>
41#include <sound/core.h>
42#include <sound/pcm.h>
43#include <sound/pcm_params.h>
44#include <sound/soc.h>
45
46#include "tegra30_spdif.h"
47
48#define DRV_NAME "tegra30-spdif"
49
50static inline void tegra30_spdif_write(struct tegra30_spdif *spdif,
51 u32 reg, u32 val)
52{
53 __raw_writel(val, spdif->regs + reg);
54}
55
56static inline u32 tegra30_spdif_read(struct tegra30_spdif *spdif, u32 reg)
57{
58 return __raw_readl(spdif->regs + reg);
59}
60
61static void tegra30_spdif_enable_clocks(struct tegra30_spdif *spdif)
62{
63 clk_enable(spdif->clk_spdif_out);
64 tegra30_ahub_enable_clocks();
65}
66
67static void tegra30_spdif_disable_clocks(struct tegra30_spdif *spdif)
68{
69 tegra30_ahub_disable_clocks();
70 clk_disable(spdif->clk_spdif_out);
71}
72
73#ifdef CONFIG_DEBUG_FS
74static int tegra30_spdif_show(struct seq_file *s, void *unused)
75{
76#define REG(r) { r, #r }
77 static const struct {
78 int offset;
79 const char *name;
80 } regs[] = {
81 REG(TEGRA30_SPDIF_CTRL),
82 REG(TEGRA30_SPDIF_STROBE_CTRL),
83 REG(TEGRA30_SPDIF_CIF_TXD_CTRL),
84 REG(TEGRA30_SPDIF_CIF_RXD_CTRL),
85 REG(TEGRA30_SPDIF_CIF_TXU_CTRL),
86 REG(TEGRA30_SPDIF_CIF_RXU_CTRL),
87 REG(TEGRA30_SPDIF_CH_STA_RX_A),
88 REG(TEGRA30_SPDIF_CH_STA_RX_B),
89 REG(TEGRA30_SPDIF_CH_STA_RX_C),
90 REG(TEGRA30_SPDIF_CH_STA_RX_D),
91 REG(TEGRA30_SPDIF_CH_STA_RX_E),
92 REG(TEGRA30_SPDIF_CH_STA_RX_F),
93 REG(TEGRA30_SPDIF_CH_STA_TX_A),
94 REG(TEGRA30_SPDIF_CH_STA_TX_B),
95 REG(TEGRA30_SPDIF_CH_STA_TX_C),
96 REG(TEGRA30_SPDIF_CH_STA_TX_D),
97 REG(TEGRA30_SPDIF_CH_STA_TX_E),
98 REG(TEGRA30_SPDIF_CH_STA_TX_F),
99 REG(TEGRA30_SPDIF_FLOWCTL_CTRL),
100 REG(TEGRA30_SPDIF_TX_STEP),
101 REG(TEGRA30_SPDIF_FLOW_STATUS),
102 REG(TEGRA30_SPDIF_FLOW_TOTAL),
103 REG(TEGRA30_SPDIF_FLOW_OVER),
104 REG(TEGRA30_SPDIF_FLOW_UNDER),
105 REG(TEGRA30_SPDIF_LCOEF_1_4_0),
106 REG(TEGRA30_SPDIF_LCOEF_1_4_1),
107 REG(TEGRA30_SPDIF_LCOEF_1_4_2),
108 REG(TEGRA30_SPDIF_LCOEF_1_4_3),
109 REG(TEGRA30_SPDIF_LCOEF_1_4_4),
110 REG(TEGRA30_SPDIF_LCOEF_1_4_5),
111 REG(TEGRA30_SPDIF_LCOEF_2_4_0),
112 REG(TEGRA30_SPDIF_LCOEF_2_4_1),
113 REG(TEGRA30_SPDIF_LCOEF_2_4_2),
114 };
115#undef REG
116
117 struct tegra30_spdif *spdif = s->private;
118 int i;
119
120 tegra30_spdif_enable_clocks(spdif);
121
122 for (i = 0; i < ARRAY_SIZE(regs); i++) {
123 u32 val = tegra30_spdif_read(spdif, regs[i].offset);
124 seq_printf(s, "%s = %08x\n", regs[i].name, val);
125 }
126
127 tegra30_spdif_disable_clocks(spdif);
128
129 return 0;
130}
131
132static int tegra30_spdif_debug_open(struct inode *inode, struct file *file)
133{
134 return single_open(file, tegra30_spdif_show, inode->i_private);
135}
136
137static const struct file_operations tegra30_spdif_debug_fops = {
138 .open = tegra30_spdif_debug_open,
139 .read = seq_read,
140 .llseek = seq_lseek,
141 .release = single_release,
142};
143
144static void tegra30_spdif_debug_add(struct tegra30_spdif *spdif)
145{
146 char name[] = DRV_NAME;
147
148 spdif->debug = debugfs_create_file(name, S_IRUGO, snd_soc_debugfs_root,
149 spdif, &tegra30_spdif_debug_fops);
150}
151
152static void tegra30_spdif_debug_remove(struct tegra30_spdif *spdif)
153{
154 if (spdif->debug)
155 debugfs_remove(spdif->debug);
156}
157#else
158static inline void tegra30_spdif_debug_add(struct tegra30_spdif *spdif)
159{
160}
161
162static inline void tegra30_spdif_debug_remove(struct tegra30_spdif *spdif)
163{
164}
165#endif
166
167int tegra30_spdif_startup(struct snd_pcm_substream *substream,
168 struct snd_soc_dai *dai)
169{
170 struct tegra30_spdif *spdif = snd_soc_dai_get_drvdata(dai);
171 int ret = 0;
172
173 tegra30_spdif_enable_clocks(spdif);
174
175 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
176 ret = tegra30_ahub_allocate_tx_fifo(&spdif->txcif,
177 &spdif->playback_dma_data.addr,
178 &spdif->playback_dma_data.req_sel);
179 spdif->playback_dma_data.wrap = 4;
180 spdif->playback_dma_data.width = 32;
181 tegra30_ahub_set_rx_cif_source(TEGRA30_AHUB_RXCIF_SPDIF_RX0,
182 spdif->txcif);
183 }
184
185 tegra30_spdif_disable_clocks(spdif);
186
187 return ret;
188}
189
190void tegra30_spdif_shutdown(struct snd_pcm_substream *substream,
191 struct snd_soc_dai *dai)
192{
193 struct tegra30_spdif *spdif = snd_soc_dai_get_drvdata(dai);
194
195 tegra30_spdif_enable_clocks(spdif);
196
197 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
198 tegra30_ahub_unset_rx_cif_source(TEGRA30_AHUB_RXCIF_SPDIF_RX0);
199 tegra30_ahub_free_tx_fifo(spdif->txcif);
200 }
201
202 tegra30_spdif_disable_clocks(spdif);
203}
204
205static int tegra30_spdif_hw_params(struct snd_pcm_substream *substream,
206 struct snd_pcm_hw_params *params,
207 struct snd_soc_dai *dai)
208{
209 struct device *dev = substream->pcm->card->dev;
210 struct tegra30_spdif *spdif = snd_soc_dai_get_drvdata(dai);
211 int ret, srate, spdifclock;
212
213 if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK) {
214 dev_err(dev, "spdif capture is not supported\n");
215 return -EINVAL;
216 }
217
218 spdif->reg_ctrl &= ~TEGRA30_SPDIF_CTRL_BIT_MODE_MASK;
219 switch (params_format(params)) {
220 case SNDRV_PCM_FORMAT_S16_LE:
221 spdif->reg_ctrl |= TEGRA30_SPDIF_CTRL_PACK_ENABLE;
222 spdif->reg_ctrl |= TEGRA30_SPDIF_CTRL_BIT_MODE_16BIT;
223 break;
224 default:
225 return -EINVAL;
226 }
227
228 srate = params_rate(params);
229 spdif->reg_ch_sta_a &= ~TEGRA30_SPDIF_CH_STA_TX_A_SAMP_FREQ_MASK;
230 spdif->reg_ch_sta_b &= ~TEGRA30_SPDIF_CH_STA_TX_B_ORIG_SAMP_FREQ_MASK;
231 switch (srate) {
232 case 32000:
233 spdifclock = 4096000;
234 spdif->reg_ch_sta_a |=
235 TEGRA30_SPDIF_CH_STA_TX_A_SAMP_FREQ_32000;
236 spdif->reg_ch_sta_b |=
237 TEGRA30_SPDIF_CH_STA_TX_B_ORIG_SAMP_FREQ_32000;
238 break;
239 case 44100:
240 spdifclock = 5644800;
241 spdif->reg_ch_sta_a |=
242 TEGRA30_SPDIF_CH_STA_TX_A_SAMP_FREQ_44100;
243 spdif->reg_ch_sta_b |=
244 TEGRA30_SPDIF_CH_STA_TX_B_ORIG_SAMP_FREQ_44100;
245 break;
246 case 48000:
247 spdifclock = 6144000;
248 spdif->reg_ch_sta_a |=
249 TEGRA30_SPDIF_CH_STA_TX_A_SAMP_FREQ_48000;
250 spdif->reg_ch_sta_b |=
251 TEGRA30_SPDIF_CH_STA_TX_B_ORIG_SAMP_FREQ_48000;
252 break;
253 case 88200:
254 spdifclock = 11289600;
255 spdif->reg_ch_sta_a |=
256 TEGRA30_SPDIF_CH_STA_TX_A_SAMP_FREQ_88200;
257 spdif->reg_ch_sta_b |=
258 TEGRA30_SPDIF_CH_STA_TX_B_ORIG_SAMP_FREQ_88200;
259 break;
260 case 96000:
261 spdifclock = 12288000;
262 spdif->reg_ch_sta_a |=
263 TEGRA30_SPDIF_CH_STA_TX_A_SAMP_FREQ_96000;
264 spdif->reg_ch_sta_b |=
265 TEGRA30_SPDIF_CH_STA_TX_B_ORIG_SAMP_FREQ_96000;
266 break;
267 case 176400:
268 spdifclock = 22579200;
269 spdif->reg_ch_sta_a |=
270 TEGRA30_SPDIF_CH_STA_TX_A_SAMP_FREQ_176400;
271 spdif->reg_ch_sta_b |=
272 TEGRA30_SPDIF_CH_STA_TX_B_ORIG_SAMP_FREQ_176400;
273 break;
274 case 192000:
275 spdifclock = 24576000;
276 spdif->reg_ch_sta_a |=
277 TEGRA30_SPDIF_CH_STA_TX_A_SAMP_FREQ_192000;
278 spdif->reg_ch_sta_b |=
279 TEGRA30_SPDIF_CH_STA_TX_B_ORIG_SAMP_FREQ_192000;
280 break;
281 default:
282 return -EINVAL;
283 }
284
285 ret = clk_set_rate(spdif->clk_spdif_out, spdifclock);
286 if (ret) {
287 dev_err(dev, "Can't set SPDIF clock rate: %d\n", ret);
288 return ret;
289 }
290
291 tegra30_spdif_enable_clocks(spdif);
292
293 tegra30_spdif_write(spdif, TEGRA30_SPDIF_CH_STA_TX_A,
294 spdif->reg_ch_sta_a);
295 tegra30_spdif_write(spdif, TEGRA30_SPDIF_CH_STA_TX_B,
296 spdif->reg_ch_sta_b);
297
298 tegra30_spdif_disable_clocks(spdif);
299
300 ret = tegra_hdmi_setup_audio_freq_source(srate, SPDIF);
301 if (ret) {
302 dev_err(dev, "Can't set HDMI audio freq source: %d\n", ret);
303 return ret;
304 }
305
306 return 0;
307}
308
309static void tegra30_spdif_start_playback(struct tegra30_spdif *spdif)
310{
311 tegra30_ahub_enable_tx_fifo(spdif->txcif);
312 spdif->reg_ctrl |= TEGRA30_SPDIF_CTRL_TX_EN_ENABLE |
313 TEGRA30_SPDIF_CTRL_TC_EN_ENABLE;
314 tegra30_spdif_write(spdif, TEGRA30_SPDIF_CTRL, spdif->reg_ctrl);
315}
316
317static void tegra30_spdif_stop_playback(struct tegra30_spdif *spdif)
318{
319 tegra30_ahub_disable_tx_fifo(spdif->txcif);
320 spdif->reg_ctrl &= ~(TEGRA30_SPDIF_CTRL_TX_EN_ENABLE |
321 TEGRA30_SPDIF_CTRL_TC_EN_ENABLE);
322 tegra30_spdif_write(spdif, TEGRA30_SPDIF_CTRL, spdif->reg_ctrl);
323}
324
325static int tegra30_spdif_trigger(struct snd_pcm_substream *substream, int cmd,
326 struct snd_soc_dai *dai)
327{
328 struct tegra30_spdif *spdif = snd_soc_dai_get_drvdata(dai);
329
330 switch (cmd) {
331 case SNDRV_PCM_TRIGGER_START:
332 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
333 case SNDRV_PCM_TRIGGER_RESUME:
334 tegra30_spdif_enable_clocks(spdif);
335 tegra30_spdif_start_playback(spdif);
336 break;
337 case SNDRV_PCM_TRIGGER_STOP:
338 case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
339 case SNDRV_PCM_TRIGGER_SUSPEND:
340 tegra30_spdif_stop_playback(spdif);
341 tegra30_spdif_disable_clocks(spdif);
342 break;
343 default:
344 return -EINVAL;
345 }
346
347 return 0;
348}
349
350static int tegra30_spdif_probe(struct snd_soc_dai *dai)
351{
352 struct tegra30_spdif *spdif = snd_soc_dai_get_drvdata(dai);
353
354 dai->playback_dma_data = &spdif->playback_dma_data;
355 dai->capture_dma_data = NULL;
356
357 return 0;
358}
359
360static struct snd_soc_dai_ops tegra30_spdif_dai_ops = {
361 .startup = tegra30_spdif_startup,
362 .shutdown = tegra30_spdif_shutdown,
363 .hw_params = tegra30_spdif_hw_params,
364 .trigger = tegra30_spdif_trigger,
365};
366
367struct snd_soc_dai_driver tegra30_spdif_dai = {
368 .name = DRV_NAME,
369 .probe = tegra30_spdif_probe,
370 .playback = {
371 .channels_min = 2,
372 .channels_max = 2,
373 .rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |
374 SNDRV_PCM_RATE_48000,
375 .formats = SNDRV_PCM_FMTBIT_S16_LE,
376 },
377 .ops = &tegra30_spdif_dai_ops,
378};
379
380static __devinit int tegra30_spdif_platform_probe(struct platform_device *pdev)
381{
382 struct tegra30_spdif *spdif;
383 struct resource *mem, *memregion;
384 int ret;
385 u32 reg_val;
386
387 spdif = kzalloc(sizeof(struct tegra30_spdif), GFP_KERNEL);
388 if (!spdif) {
389 dev_err(&pdev->dev, "Can't allocate tegra30_spdif\n");
390 ret = -ENOMEM;
391 goto exit;
392 }
393 dev_set_drvdata(&pdev->dev, spdif);
394
395 spdif->clk_spdif_out = clk_get(&pdev->dev, "spdif_out");
396 if (IS_ERR(spdif->clk_spdif_out)) {
397 dev_err(&pdev->dev, "Can't retrieve spdif clock\n");
398 ret = PTR_ERR(spdif->clk_spdif_out);
399 goto err_free;
400 }
401
402 mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
403 if (!mem) {
404 dev_err(&pdev->dev, "No memory resource\n");
405 ret = -ENODEV;
406 goto err_clk_put_spdif;
407 }
408
409 memregion = request_mem_region(mem->start, resource_size(mem),
410 DRV_NAME);
411 if (!memregion) {
412 dev_err(&pdev->dev, "Memory region already claimed\n");
413 ret = -EBUSY;
414 goto err_clk_put_spdif;
415 }
416
417 spdif->regs = ioremap(mem->start, resource_size(mem));
418 if (!spdif->regs) {
419 dev_err(&pdev->dev, "ioremap failed\n");
420 ret = -ENOMEM;
421 goto err_release;
422 }
423
424 tegra30_spdif_enable_clocks(spdif);
425
426 reg_val = TEGRA30_SPDIF_CIF_TXD_CTRL_DIRECTION_RXCIF |
427 TEGRA30_SPDIF_CIF_TXD_CTRL_AUDIO_BIT16 |
428 TEGRA30_SPDIF_CIF_TXD_CTRL_CLIENT_BIT16 |
429 TEGRA30_SPDIF_CIF_TXD_CTRL_AUDIO_CH2 |
430 TEGRA30_SPDIF_CIF_TXD_CTRL_CLIENT_CH2 |
431 (3 << TEGRA30_SPDIF_CIF_TXD_CTRL_FIFO_TH_SHIFT);
432
433 tegra30_spdif_write(spdif, TEGRA30_SPDIF_CIF_TXD_CTRL, reg_val);
434
435 tegra30_spdif_disable_clocks(spdif);
436
437 ret = snd_soc_register_dai(&pdev->dev, &tegra30_spdif_dai);
438 if (ret) {
439 dev_err(&pdev->dev, "Could not register DAI: %d\n", ret);
440 ret = -ENOMEM;
441 goto err_unmap;
442 }
443
444 tegra30_spdif_debug_add(spdif);
445
446 return 0;
447
448err_unmap:
449 iounmap(spdif->regs);
450err_release:
451 release_mem_region(mem->start, resource_size(mem));
452err_clk_put_spdif:
453 clk_put(spdif->clk_spdif_out);
454err_free:
455 kfree(spdif);
456exit:
457 return ret;
458}
459
460static int __devexit tegra30_spdif_platform_remove(struct platform_device *pdev)
461{
462 struct tegra30_spdif *spdif = dev_get_drvdata(&pdev->dev);
463 struct resource *res;
464
465 snd_soc_unregister_dai(&pdev->dev);
466
467 tegra30_spdif_debug_remove(spdif);
468
469 iounmap(spdif->regs);
470
471 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
472 release_mem_region(res->start, resource_size(res));
473
474 clk_put(spdif->clk_spdif_out);
475
476 kfree(spdif);
477
478 return 0;
479}
480
481static struct platform_driver tegra30_spdif_driver = {
482 .driver = {
483 .name = DRV_NAME,
484 .owner = THIS_MODULE,
485 },
486 .probe = tegra30_spdif_platform_probe,
487 .remove = __devexit_p(tegra30_spdif_platform_remove),
488};
489
490static int __init snd_tegra30_spdif_init(void)
491{
492 return platform_driver_register(&tegra30_spdif_driver);
493}
494module_init(snd_tegra30_spdif_init);
495
496static void __exit snd_tegra30_spdif_exit(void)
497{
498 platform_driver_unregister(&tegra30_spdif_driver);
499}
500module_exit(snd_tegra30_spdif_exit);
501
502MODULE_AUTHOR("Sumit Bhattacharya <sumitb@nvidia.com>");
503MODULE_DESCRIPTION("Tegra30 SPDIF ASoC driver");
504MODULE_LICENSE("GPL");
505MODULE_ALIAS("platform:" DRV_NAME);