aboutsummaryrefslogtreecommitdiffstats
path: root/sound/soc/tegra/tegra30_dam.c
diff options
context:
space:
mode:
Diffstat (limited to 'sound/soc/tegra/tegra30_dam.c')
-rw-r--r--sound/soc/tegra/tegra30_dam.c650
1 files changed, 650 insertions, 0 deletions
diff --git a/sound/soc/tegra/tegra30_dam.c b/sound/soc/tegra/tegra30_dam.c
new file mode 100644
index 00000000000..d308179110c
--- /dev/null
+++ b/sound/soc/tegra/tegra30_dam.c
@@ -0,0 +1,650 @@
1/*
2 * tegra30_dam.c - Tegra 30 DAM driver
3 *
4 * Author: Nikesh Oswal <noswal@nvidia.com>
5 * Copyright (C) 2011 - NVIDIA, Inc.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * version 2 as published by the Free Software Foundation.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
19 * 02110-1301 USA
20 *
21 */
22
23#include <linux/clk.h>
24#include <linux/module.h>
25#include <linux/debugfs.h>
26#include <linux/device.h>
27#include <linux/platform_device.h>
28#include <linux/seq_file.h>
29#include <linux/slab.h>
30#include <linux/io.h>
31#include <sound/soc.h>
32#include "tegra30_dam.h"
33#include "tegra30_ahub.h"
34
35#define DRV_NAME "tegra30-dam"
36
37static struct tegra30_dam_context *dams_cont_info[TEGRA30_NR_DAM_IFC];
38
39enum {
40 dam_ch_in0 = 0x0,
41 dam_ch_in1,
42 dam_ch_out,
43 dam_ch_maxnum
44} tegra30_dam_chtype;
45
46struct tegra30_dam_src_step_table step_table[] = {
47 { 8000, 44100, 80 },
48 { 8000, 48000, 1 },
49 { 16000, 44100, 160 },
50 { 16000, 48000, 1 },
51 { 44100, 8000, 441 },
52 { 48000, 8000, 0 },
53 { 44100, 16000, 441 },
54 { 48000, 16000, 0 },
55};
56
57static void tegra30_dam_set_output_samplerate(struct tegra30_dam_context *dam,
58 int fsout);
59static void tegra30_dam_set_input_samplerate(struct tegra30_dam_context *dam,
60 int fsin);
61static int tegra30_dam_set_step_reset(struct tegra30_dam_context *dam,
62 int insample, int outsample);
63static void tegra30_dam_ch0_set_step(struct tegra30_dam_context *dam, int step);
64
65static inline void tegra30_dam_writel(struct tegra30_dam_context *dam,
66 u32 val, u32 reg)
67{
68#ifdef CONFIG_PM
69 dam->reg_cache[reg >> 2] = val;
70#endif
71 __raw_writel(val, dam->damregs + reg);
72}
73
74static inline u32 tegra30_dam_readl(struct tegra30_dam_context *dam, u32 reg)
75{
76 u32 val = __raw_readl(dam->damregs + reg);
77
78 return val;
79}
80
81#ifdef CONFIG_PM
82int tegra30_dam_resume(int ifc)
83{
84 int i = 0;
85 struct tegra30_dam_context *dam;
86
87 if (ifc >= TEGRA30_NR_DAM_IFC)
88 return -EINVAL;
89
90 dam = dams_cont_info[ifc];
91
92 if (dam->in_use) {
93 tegra30_dam_enable_clock(ifc);
94
95 for (i = 0; i <= TEGRA30_DAM_CTRL_REGINDEX; i++) {
96 if ((i == TEGRA30_DAM_CTRL_RSVD_6) ||
97 (i == TEGRA30_DAM_CTRL_RSVD_10))
98 continue;
99
100 tegra30_dam_writel(dam, dam->reg_cache[i],
101 (i << 2));
102 }
103
104 tegra30_dam_disable_clock(ifc);
105 }
106
107 return 0;
108}
109#endif
110
111void tegra30_dam_disable_clock(int ifc)
112{
113 struct tegra30_dam_context *dam;
114
115 if (ifc >= TEGRA30_NR_DAM_IFC)
116 return;
117
118 dam = dams_cont_info[ifc];
119 clk_disable(dam->dam_clk);
120 tegra30_ahub_disable_clocks();
121}
122
123int tegra30_dam_enable_clock(int ifc)
124{
125 struct tegra30_dam_context *dam;
126
127 if (ifc >= TEGRA30_NR_DAM_IFC)
128 return -EINVAL;
129
130 dam = dams_cont_info[ifc];
131 tegra30_ahub_enable_clocks();
132 clk_enable(dam->dam_clk);
133
134 return 0;
135}
136
137#ifdef CONFIG_DEBUG_FS
138static int tegra30_dam_show(struct seq_file *s, void *unused)
139{
140#define REG(r) { r, #r }
141 static const struct {
142 int offset;
143 const char *name;
144 } regs[] = {
145 REG(TEGRA30_DAM_CTRL),
146 REG(TEGRA30_DAM_CLIP),
147 REG(TEGRA30_DAM_CLIP_THRESHOLD),
148 REG(TEGRA30_DAM_AUDIOCIF_OUT_CTRL),
149 REG(TEGRA30_DAM_CH0_CTRL),
150 REG(TEGRA30_DAM_CH0_CONV),
151 REG(TEGRA30_DAM_AUDIOCIF_CH0_CTRL),
152 REG(TEGRA30_DAM_CH1_CTRL),
153 REG(TEGRA30_DAM_CH1_CONV),
154 REG(TEGRA30_DAM_AUDIOCIF_CH1_CTRL),
155 };
156#undef REG
157
158 struct tegra30_dam_context *dam = s->private;
159 int i;
160
161 clk_enable(dam->dam_clk);
162
163 for (i = 0; i < ARRAY_SIZE(regs); i++) {
164 u32 val = tegra30_dam_readl(dam, regs[i].offset);
165 seq_printf(s, "%s = %08x\n", regs[i].name, val);
166 }
167
168 clk_disable(dam->dam_clk);
169
170 return 0;
171}
172
173static int tegra30_dam_debug_open(struct inode *inode, struct file *file)
174{
175 return single_open(file, tegra30_dam_show, inode->i_private);
176}
177
178static const struct file_operations tegra30_dam_debug_fops = {
179 .open = tegra30_dam_debug_open,
180 .read = seq_read,
181 .llseek = seq_lseek,
182 .release = single_release,
183};
184
185static void tegra30_dam_debug_add(struct tegra30_dam_context *dam, int id)
186{
187 char name[] = DRV_NAME ".0";
188
189 snprintf(name, sizeof(name), DRV_NAME".%1d", id);
190 dam->debug = debugfs_create_file(name, S_IRUGO, snd_soc_debugfs_root,
191 dam, &tegra30_dam_debug_fops);
192}
193
194static void tegra30_dam_debug_remove(struct tegra30_dam_context *dam)
195{
196 if (dam->debug)
197 debugfs_remove(dam->debug);
198}
199#else
200static inline void tegra30_dam_debug_add(struct tegra30_dam_context *dam,
201 int id)
202{
203}
204
205static inline void tegra30_dam_debug_remove(struct tegra30_dam_context *dam)
206{
207}
208#endif
209
210int tegra30_dam_allocate_controller()
211{
212 int i = 0;
213 struct tegra30_dam_context *dam = NULL;
214
215 for (i = 0; i < TEGRA30_NR_DAM_IFC; i++) {
216
217 dam = dams_cont_info[i];
218
219 if (!dam->in_use) {
220 dam->in_use = true;
221 return i;
222 }
223 }
224
225 return -ENOENT;
226}
227
228int tegra30_dam_allocate_channel(int ifc, int chid)
229{
230 struct tegra30_dam_context *dam = NULL;
231
232 if (ifc >= TEGRA30_NR_DAM_IFC)
233 return -EINVAL;
234
235 dam = dams_cont_info[ifc];
236
237 if (!dam->ch_alloc[chid]) {
238 dam->ch_alloc[chid] = true;
239 return 0;
240 }
241
242 return -ENOENT;
243}
244
245int tegra30_dam_free_channel(int ifc, int chid)
246{
247 struct tegra30_dam_context *dam = NULL;
248
249 if (ifc >= TEGRA30_NR_DAM_IFC)
250 return -EINVAL;
251
252 dam = dams_cont_info[ifc];
253
254 if (dam->ch_alloc[chid]) {
255 dam->ch_alloc[chid] = false;
256 return 0;
257 }
258
259 return -EINVAL;
260}
261
262int tegra30_dam_free_controller(int ifc)
263{
264 struct tegra30_dam_context *dam = NULL;
265
266 if (ifc >= TEGRA30_NR_DAM_IFC)
267 return -EINVAL;
268
269 dam = dams_cont_info[ifc];
270
271 if (!dam->ch_alloc[dam_ch_in0] &&
272 !dam->ch_alloc[dam_ch_in1]) {
273 dam->in_use = false;
274 return 0;
275 }
276
277 return -EINVAL;
278}
279
280void tegra30_dam_set_samplerate(int ifc, int chid, int samplerate)
281{
282 struct tegra30_dam_context *dam = dams_cont_info[ifc];
283
284 if (ifc >= TEGRA30_NR_DAM_IFC)
285 return;
286
287 switch (chid) {
288 case dam_ch_in0:
289 tegra30_dam_set_input_samplerate(dam, samplerate);
290 dam->ch_insamplerate[dam_ch_in0] = samplerate;
291 tegra30_dam_set_step_reset(dam, samplerate, dam->outsamplerate);
292 break;
293 case dam_ch_in1:
294 if (samplerate != dam->outsamplerate)
295 return;
296 dam->ch_insamplerate[dam_ch_in1] = samplerate;
297 break;
298 case dam_ch_out:
299 tegra30_dam_set_output_samplerate(dam, samplerate);
300 dam->outsamplerate = samplerate;
301 break;
302 default:
303 break;
304 }
305}
306
307void tegra30_dam_set_output_samplerate(struct tegra30_dam_context *dam,
308 int fsout)
309{
310 u32 val;
311
312 val = tegra30_dam_readl(dam, TEGRA30_DAM_CTRL);
313 val &= ~TEGRA30_DAM_CTRL_FSOUT_MASK;
314
315 switch (fsout) {
316 case TEGRA30_AUDIO_SAMPLERATE_8000:
317 val |= TEGRA30_DAM_CTRL_FSOUT_FS8;
318 break;
319 case TEGRA30_AUDIO_SAMPLERATE_16000:
320 val |= TEGRA30_DAM_CTRL_FSOUT_FS16;
321 break;
322 case TEGRA30_AUDIO_SAMPLERATE_44100:
323 val |= TEGRA30_DAM_CTRL_FSOUT_FS44;
324 break;
325 case TEGRA30_AUDIO_SAMPLERATE_48000:
326 val |= TEGRA30_DAM_CTRL_FSOUT_FS48;
327 break;
328 default:
329 break;
330 }
331
332 tegra30_dam_writel(dam, val, TEGRA30_DAM_CTRL);
333}
334
335void tegra30_dam_set_input_samplerate(struct tegra30_dam_context *dam, int fsin)
336{
337 u32 val;
338
339 val = tegra30_dam_readl(dam, TEGRA30_DAM_CH0_CTRL);
340 val &= ~TEGRA30_DAM_CH0_CTRL_FSIN_MASK;
341
342 switch (fsin) {
343 case TEGRA30_AUDIO_SAMPLERATE_8000:
344 val |= TEGRA30_DAM_CH0_CTRL_FSIN_FS8;
345 break;
346 case TEGRA30_AUDIO_SAMPLERATE_16000:
347 val |= TEGRA30_DAM_CH0_CTRL_FSIN_FS16;
348 break;
349 case TEGRA30_AUDIO_SAMPLERATE_44100:
350 val |= TEGRA30_DAM_CH0_CTRL_FSIN_FS44;
351 break;
352 case TEGRA30_AUDIO_SAMPLERATE_48000:
353 val |= TEGRA30_DAM_CH0_CTRL_FSIN_FS48;
354 break;
355 default:
356 break;
357 }
358
359 tegra30_dam_writel(dam, val, TEGRA30_DAM_CH0_CTRL);
360}
361
362int tegra30_dam_set_step_reset(struct tegra30_dam_context *dam,
363 int insample, int outsample)
364{
365 int step_reset = 0;
366 int i = 0;
367
368 for (i = 0; i < ARRAY_SIZE(step_table); i++) {
369 if ((insample == step_table[i].insample) &&
370 (outsample == step_table[i].outsample))
371 step_reset = step_table[i].stepreset;
372 }
373
374 tegra30_dam_ch0_set_step(dam, step_reset);
375
376 return 0;
377}
378
379void tegra30_dam_ch0_set_step(struct tegra30_dam_context *dam, int step)
380{
381 u32 val;
382
383 val = tegra30_dam_readl(dam, TEGRA30_DAM_CH0_CTRL);
384 val &= ~TEGRA30_DAM_CH0_CTRL_STEP_MASK;
385 val |= step << TEGRA30_DAM_CH0_CTRL_STEP_SHIFT;
386 tegra30_dam_writel(dam, val, TEGRA30_DAM_CH0_CTRL);
387}
388
389int tegra30_dam_set_gain(int ifc, int chid, int gain)
390{
391
392 if (ifc >= TEGRA30_NR_DAM_IFC)
393 return -EINVAL;
394
395 switch (chid) {
396 case dam_ch_in0:
397 tegra30_dam_writel(dams_cont_info[ifc], gain,
398 TEGRA30_DAM_CH0_CONV);
399 break;
400 case dam_ch_in1:
401 tegra30_dam_writel(dams_cont_info[ifc], gain,
402 TEGRA30_DAM_CH1_CONV);
403 break;
404 default:
405 break;
406 }
407
408 return 0;
409}
410
411int tegra30_dam_set_acif(int ifc, int chid, unsigned int audio_channels,
412 unsigned int audio_bits, unsigned int client_channels,
413 unsigned int client_bits)
414{
415 unsigned int reg;
416 unsigned int value = 0;
417
418 if (ifc >= TEGRA30_NR_DAM_IFC)
419 return -EINVAL;
420
421 /*ch0 takes input as mono/16bit always*/
422 if ((chid == dam_ch_in0) &&
423 ((client_channels != 1) || (client_bits != 16)))
424 return -EINVAL;
425
426 value |= TEGRA30_CIF_MONOCONV_COPY;
427 value |= TEGRA30_CIF_STEREOCONV_CH0;
428 value |= (audio_channels-1) << TEGRA30_AUDIO_CHANNELS_SHIFT;
429 value |= (((audio_bits>>2)-1)<<TEGRA30_AUDIO_BITS_SHIFT);
430 value |= (client_channels-1) << TEGRA30_CLIENT_CHANNELS_SHIFT;
431 value |= (((client_bits>>2)-1)<<TEGRA30_CLIENT_BITS_SHIFT);
432
433 switch (chid) {
434 case dam_ch_out:
435 value |= TEGRA30_CIF_DIRECTION_TX;
436 reg = TEGRA30_DAM_AUDIOCIF_OUT_CTRL;
437 break;
438 case dam_ch_in0:
439 value |= TEGRA30_CIF_DIRECTION_RX;
440 reg = TEGRA30_DAM_AUDIOCIF_CH0_CTRL;
441 break;
442 case dam_ch_in1:
443 value |= TEGRA30_CIF_DIRECTION_RX;
444 reg = TEGRA30_DAM_AUDIOCIF_CH1_CTRL;
445 break;
446 default:
447 return -EINVAL;
448 }
449
450 tegra30_dam_writel(dams_cont_info[ifc], value, reg);
451
452 return 0;
453}
454
455void tegra30_dam_enable(int ifc, int on, int chid)
456{
457 u32 old_val, val, enreg;
458 struct tegra30_dam_context *dam = dams_cont_info[ifc];
459
460 if (ifc >= TEGRA30_NR_DAM_IFC)
461 return;
462
463 if (chid == dam_ch_in0)
464 enreg = TEGRA30_DAM_CH0_CTRL;
465 else
466 enreg = TEGRA30_DAM_CH1_CTRL;
467
468 old_val = val = tegra30_dam_readl(dam, enreg);
469
470 if (on) {
471 if (!dam->ch_enable_refcnt[chid]++)
472 val |= TEGRA30_DAM_CH0_CTRL_EN;
473 } else if (dam->ch_enable_refcnt[chid]) {
474 dam->ch_enable_refcnt[chid]--;
475 if (!dam->ch_enable_refcnt[chid])
476 val &= ~TEGRA30_DAM_CH0_CTRL_EN;
477 }
478
479 if (val != old_val)
480 tegra30_dam_writel(dam, val, enreg);
481
482 old_val = val = tegra30_dam_readl(dam, TEGRA30_DAM_CTRL);
483
484 if (dam->ch_enable_refcnt[dam_ch_in0] ||
485 dam->ch_enable_refcnt[dam_ch_in1])
486 val |= TEGRA30_DAM_CTRL_DAM_EN;
487 else
488 val &= ~TEGRA30_DAM_CTRL_DAM_EN;
489
490 if (old_val != val)
491 tegra30_dam_writel(dam, val, TEGRA30_DAM_CTRL);
492}
493
494void tegra30_dam_ch0_set_datasync(struct tegra30_dam_context *dam, int datasync)
495{
496 u32 val;
497
498 val = tegra30_dam_readl(dam, TEGRA30_DAM_CH0_CTRL);
499 val &= ~TEGRA30_DAM_CH0_CTRL_DATA_SYNC_MASK;
500 val |= datasync << TEGRA30_DAM_DATA_SYNC_SHIFT;
501 tegra30_dam_writel(dam, val, TEGRA30_DAM_CH0_CTRL);
502}
503
504void tegra30_dam_ch1_set_datasync(struct tegra30_dam_context *dam, int datasync)
505{
506 u32 val;
507
508 val = tegra30_dam_readl(dam, TEGRA30_DAM_CH1_CTRL);
509 val &= ~TEGRA30_DAM_CH1_CTRL_DATA_SYNC_MASK;
510 val |= datasync << TEGRA30_DAM_DATA_SYNC_SHIFT;
511 tegra30_dam_writel(dam, val, TEGRA30_DAM_CH1_CTRL);
512}
513
514void tegra30_dam_enable_clip_counter(struct tegra30_dam_context *dam, int on)
515{
516 u32 val;
517
518 val = tegra30_dam_readl(dam, TEGRA30_DAM_CLIP);
519 val &= ~TEGRA30_DAM_CLIP_COUNTER_ENABLE;
520 val |= on ? TEGRA30_DAM_CLIP_COUNTER_ENABLE : 0;
521 tegra30_dam_writel(dam, val, TEGRA30_DAM_CLIP);
522}
523
524static int __devinit tegra30_dam_probe(struct platform_device *pdev)
525{
526 struct resource *res, *region;
527 struct tegra30_dam_context *dam;
528 int ret = 0;
529#ifdef CONFIG_PM
530 int i;
531#endif
532 int clkm_rate;
533
534 if ((pdev->id < 0) ||
535 (pdev->id >= TEGRA30_NR_DAM_IFC)) {
536 dev_err(&pdev->dev, "ID %d out of range\n", pdev->id);
537 return -EINVAL;
538 }
539
540 dams_cont_info[pdev->id] = devm_kzalloc(&pdev->dev,
541 sizeof(struct tegra30_dam_context),
542 GFP_KERNEL);
543 if (!dams_cont_info[pdev->id]) {
544 dev_err(&pdev->dev, "Can't allocate dam context\n");
545 ret = -ENOMEM;
546 goto exit;
547 }
548 dam = dams_cont_info[pdev->id];
549
550 dam->dam_clk = clk_get(&pdev->dev, NULL);
551 if (IS_ERR(dam->dam_clk)) {
552 dev_err(&pdev->dev, "Can't retrieve dam clock\n");
553 ret = PTR_ERR(dam->dam_clk);
554 goto err_free;
555 }
556 clkm_rate = clk_get_rate(clk_get_parent(dam->dam_clk));
557 while (clkm_rate > 12000000)
558 clkm_rate >>= 1;
559
560 clk_set_rate(dam->dam_clk,clkm_rate);
561
562 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
563 if (!res) {
564 dev_err(&pdev->dev, "No memory 0 resource\n");
565 ret = -ENODEV;
566 goto err_clk_put_dam;
567 }
568
569 region = devm_request_mem_region(&pdev->dev, res->start,
570 resource_size(res), pdev->name);
571 if (!region) {
572 dev_err(&pdev->dev, "Memory region 0 already claimed\n");
573 ret = -EBUSY;
574 goto err_clk_put_dam;
575 }
576
577 dam->damregs = devm_ioremap(&pdev->dev, res->start, resource_size(res));
578 if (!dam->damregs) {
579 dev_err(&pdev->dev, "ioremap 0 failed\n");
580 ret = -ENOMEM;
581 goto err_clk_put_dam;
582 }
583
584#ifdef CONFIG_PM
585 /* cache the POR values of DAM regs*/
586 tegra30_dam_enable_clock(pdev->id);
587
588 for (i = 0; i <= TEGRA30_DAM_CTRL_REGINDEX; i++) {
589 if ((i == TEGRA30_DAM_CTRL_RSVD_6) ||
590 (i == TEGRA30_DAM_CTRL_RSVD_10))
591 continue;
592
593 dam->reg_cache[i] =
594 tegra30_dam_readl(dam, i << 2);
595 }
596
597 tegra30_dam_disable_clock(pdev->id);
598#endif
599
600 platform_set_drvdata(pdev, dam);
601
602 tegra30_dam_debug_add(dam, pdev->id);
603
604 return 0;
605
606err_clk_put_dam:
607 clk_put(dam->dam_clk);
608err_free:
609 dams_cont_info[pdev->id] = NULL;
610exit:
611 return ret;
612}
613
614static int __devexit tegra30_dam_remove(struct platform_device *pdev)
615{
616 struct tegra30_dam_context *dam;
617
618 dam = platform_get_drvdata(pdev);
619 clk_put(dam->dam_clk);
620 tegra30_dam_debug_remove(dam);
621 dams_cont_info[pdev->id] = NULL;
622
623 return 0;
624}
625
626static struct platform_driver tegra30_dam_driver = {
627 .probe = tegra30_dam_probe,
628 .remove = __devexit_p(tegra30_dam_remove),
629 .driver = {
630 .name = DRV_NAME,
631 .owner = THIS_MODULE,
632 },
633};
634
635static int __init tegra30_dam_modinit(void)
636{
637 return platform_driver_register(&tegra30_dam_driver);
638}
639module_init(tegra30_dam_modinit);
640
641static void __exit tegra30_dam_modexit(void)
642{
643 platform_driver_unregister(&tegra30_dam_driver);
644}
645module_exit(tegra30_dam_modexit);
646
647MODULE_AUTHOR("Nikesh Oswal <noswal@nvidia.com>");
648MODULE_DESCRIPTION("Tegra 30 DAM driver");
649MODULE_LICENSE("GPL");
650MODULE_ALIAS("platform:" DRV_NAME);