aboutsummaryrefslogtreecommitdiffstats
path: root/sound/soc/tegra
diff options
context:
space:
mode:
Diffstat (limited to 'sound/soc/tegra')
-rw-r--r--sound/soc/tegra/Kconfig40
-rw-r--r--sound/soc/tegra/Makefile17
-rw-r--r--sound/soc/tegra/tegra_asoc_utils.c156
-rw-r--r--sound/soc/tegra/tegra_asoc_utils.h45
-rw-r--r--sound/soc/tegra/tegra_das.c265
-rw-r--r--sound/soc/tegra/tegra_das.h135
-rw-r--r--sound/soc/tegra/tegra_i2s.c509
-rw-r--r--sound/soc/tegra/tegra_i2s.h165
-rw-r--r--sound/soc/tegra/tegra_pcm.c404
-rw-r--r--sound/soc/tegra/tegra_pcm.h55
-rw-r--r--sound/soc/tegra/tegra_wm8903.c475
-rw-r--r--sound/soc/tegra/trimslice.c228
12 files changed, 2494 insertions, 0 deletions
diff --git a/sound/soc/tegra/Kconfig b/sound/soc/tegra/Kconfig
new file mode 100644
index 000000000000..035d39a4beb4
--- /dev/null
+++ b/sound/soc/tegra/Kconfig
@@ -0,0 +1,40 @@
1config SND_SOC_TEGRA
2 tristate "SoC Audio for the Tegra System-on-Chip"
3 depends on ARCH_TEGRA && TEGRA_SYSTEM_DMA
4 help
5 Say Y or M here if you want support for SoC audio on Tegra.
6
7config SND_SOC_TEGRA_I2S
8 tristate
9 depends on SND_SOC_TEGRA
10 help
11 Say Y or M if you want to add support for codecs attached to the
12 Tegra I2S interface. You will also need to select the individual
13 machine drivers to support below.
14
15config MACH_HAS_SND_SOC_TEGRA_WM8903
16 bool
17 help
18 Machines that use the SND_SOC_TEGRA_WM8903 driver should select
19 this config option, in order to allow the user to enable
20 SND_SOC_TEGRA_WM8903.
21
22config SND_SOC_TEGRA_WM8903
23 tristate "SoC Audio support for Tegra boards using a WM8903 codec"
24 depends on SND_SOC_TEGRA && I2C
25 depends on MACH_HAS_SND_SOC_TEGRA_WM8903
26 select SND_SOC_TEGRA_I2S
27 select SND_SOC_WM8903
28 help
29 Say Y or M here if you want to add support for SoC audio on Tegra
30 boards using the WM8093 codec. Currently, the supported boards are
31 Harmony, Ventana, Seaboard, Kaen, and Aebl.
32
33config SND_SOC_TEGRA_TRIMSLICE
34 tristate "SoC Audio support for TrimSlice board"
35 depends on SND_SOC_TEGRA && MACH_TRIMSLICE && I2C
36 select SND_SOC_TEGRA_I2S
37 select SND_SOC_TLV320AIC23
38 help
39 Say Y or M here if you want to add support for SoC audio on the
40 TrimSlice platform.
diff --git a/sound/soc/tegra/Makefile b/sound/soc/tegra/Makefile
new file mode 100644
index 000000000000..fa6574d92a31
--- /dev/null
+++ b/sound/soc/tegra/Makefile
@@ -0,0 +1,17 @@
1# Tegra platform Support
2snd-soc-tegra-das-objs := tegra_das.o
3snd-soc-tegra-pcm-objs := tegra_pcm.o
4snd-soc-tegra-i2s-objs := tegra_i2s.o
5snd-soc-tegra-utils-objs += tegra_asoc_utils.o
6
7obj-$(CONFIG_SND_SOC_TEGRA) += snd-soc-tegra-utils.o
8obj-$(CONFIG_SND_SOC_TEGRA) += snd-soc-tegra-das.o
9obj-$(CONFIG_SND_SOC_TEGRA) += snd-soc-tegra-pcm.o
10obj-$(CONFIG_SND_SOC_TEGRA_I2S) += snd-soc-tegra-i2s.o
11
12# Tegra machine Support
13snd-soc-tegra-wm8903-objs := tegra_wm8903.o
14snd-soc-tegra-trimslice-objs := trimslice.o
15
16obj-$(CONFIG_SND_SOC_TEGRA_WM8903) += snd-soc-tegra-wm8903.o
17obj-$(CONFIG_SND_SOC_TEGRA_TRIMSLICE) += snd-soc-tegra-trimslice.o
diff --git a/sound/soc/tegra/tegra_asoc_utils.c b/sound/soc/tegra/tegra_asoc_utils.c
new file mode 100644
index 000000000000..dfa85cbb05c8
--- /dev/null
+++ b/sound/soc/tegra/tegra_asoc_utils.c
@@ -0,0 +1,156 @@
1/*
2 * tegra_asoc_utils.c - Harmony machine ASoC driver
3 *
4 * Author: Stephen Warren <swarren@nvidia.com>
5 * Copyright (C) 2010 - 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/device.h>
25#include <linux/err.h>
26#include <linux/kernel.h>
27
28#include "tegra_asoc_utils.h"
29
30int tegra_asoc_utils_set_rate(struct tegra_asoc_utils_data *data, int srate,
31 int mclk)
32{
33 int new_baseclock;
34 bool clk_change;
35 int err;
36
37 switch (srate) {
38 case 11025:
39 case 22050:
40 case 44100:
41 case 88200:
42 new_baseclock = 56448000;
43 break;
44 case 8000:
45 case 16000:
46 case 32000:
47 case 48000:
48 case 64000:
49 case 96000:
50 new_baseclock = 73728000;
51 break;
52 default:
53 return -EINVAL;
54 }
55
56 clk_change = ((new_baseclock != data->set_baseclock) ||
57 (mclk != data->set_mclk));
58 if (!clk_change)
59 return 0;
60
61 data->set_baseclock = 0;
62 data->set_mclk = 0;
63
64 clk_disable(data->clk_cdev1);
65 clk_disable(data->clk_pll_a_out0);
66 clk_disable(data->clk_pll_a);
67
68 err = clk_set_rate(data->clk_pll_a, new_baseclock);
69 if (err) {
70 dev_err(data->dev, "Can't set pll_a rate: %d\n", err);
71 return err;
72 }
73
74 err = clk_set_rate(data->clk_pll_a_out0, mclk);
75 if (err) {
76 dev_err(data->dev, "Can't set pll_a_out0 rate: %d\n", err);
77 return err;
78 }
79
80 /* Don't set cdev1 rate; its locked to pll_a_out0 */
81
82 err = clk_enable(data->clk_pll_a);
83 if (err) {
84 dev_err(data->dev, "Can't enable pll_a: %d\n", err);
85 return err;
86 }
87
88 err = clk_enable(data->clk_pll_a_out0);
89 if (err) {
90 dev_err(data->dev, "Can't enable pll_a_out0: %d\n", err);
91 return err;
92 }
93
94 err = clk_enable(data->clk_cdev1);
95 if (err) {
96 dev_err(data->dev, "Can't enable cdev1: %d\n", err);
97 return err;
98 }
99
100 data->set_baseclock = new_baseclock;
101 data->set_mclk = mclk;
102
103 return 0;
104}
105EXPORT_SYMBOL_GPL(tegra_asoc_utils_set_rate);
106
107int tegra_asoc_utils_init(struct tegra_asoc_utils_data *data,
108 struct device *dev)
109{
110 int ret;
111
112 data->dev = dev;
113
114 data->clk_pll_a = clk_get_sys(NULL, "pll_a");
115 if (IS_ERR(data->clk_pll_a)) {
116 dev_err(data->dev, "Can't retrieve clk pll_a\n");
117 ret = PTR_ERR(data->clk_pll_a);
118 goto err;
119 }
120
121 data->clk_pll_a_out0 = clk_get_sys(NULL, "pll_a_out0");
122 if (IS_ERR(data->clk_pll_a_out0)) {
123 dev_err(data->dev, "Can't retrieve clk pll_a_out0\n");
124 ret = PTR_ERR(data->clk_pll_a_out0);
125 goto err_put_pll_a;
126 }
127
128 data->clk_cdev1 = clk_get_sys(NULL, "cdev1");
129 if (IS_ERR(data->clk_cdev1)) {
130 dev_err(data->dev, "Can't retrieve clk cdev1\n");
131 ret = PTR_ERR(data->clk_cdev1);
132 goto err_put_pll_a_out0;
133 }
134
135 return 0;
136
137err_put_pll_a_out0:
138 clk_put(data->clk_pll_a_out0);
139err_put_pll_a:
140 clk_put(data->clk_pll_a);
141err:
142 return ret;
143}
144EXPORT_SYMBOL_GPL(tegra_asoc_utils_init);
145
146void tegra_asoc_utils_fini(struct tegra_asoc_utils_data *data)
147{
148 clk_put(data->clk_cdev1);
149 clk_put(data->clk_pll_a_out0);
150 clk_put(data->clk_pll_a);
151}
152EXPORT_SYMBOL_GPL(tegra_asoc_utils_fini);
153
154MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>");
155MODULE_DESCRIPTION("Tegra ASoC utility code");
156MODULE_LICENSE("GPL");
diff --git a/sound/soc/tegra/tegra_asoc_utils.h b/sound/soc/tegra/tegra_asoc_utils.h
new file mode 100644
index 000000000000..4818195da25c
--- /dev/null
+++ b/sound/soc/tegra/tegra_asoc_utils.h
@@ -0,0 +1,45 @@
1/*
2 * tegra_asoc_utils.h - Definitions for Tegra DAS driver
3 *
4 * Author: Stephen Warren <swarren@nvidia.com>
5 * Copyright (C) 2010 - 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#ifndef __TEGRA_ASOC_UTILS_H__
24#define __TEGRA_ASOC_UTILS_H_
25
26struct clk;
27struct device;
28
29struct tegra_asoc_utils_data {
30 struct device *dev;
31 struct clk *clk_pll_a;
32 struct clk *clk_pll_a_out0;
33 struct clk *clk_cdev1;
34 int set_baseclock;
35 int set_mclk;
36};
37
38int tegra_asoc_utils_set_rate(struct tegra_asoc_utils_data *data, int srate,
39 int mclk);
40int tegra_asoc_utils_init(struct tegra_asoc_utils_data *data,
41 struct device *dev);
42void tegra_asoc_utils_fini(struct tegra_asoc_utils_data *data);
43
44#endif
45
diff --git a/sound/soc/tegra/tegra_das.c b/sound/soc/tegra/tegra_das.c
new file mode 100644
index 000000000000..9f24ef73f2cb
--- /dev/null
+++ b/sound/soc/tegra/tegra_das.c
@@ -0,0 +1,265 @@
1/*
2 * tegra_das.c - Tegra DAS driver
3 *
4 * Author: Stephen Warren <swarren@nvidia.com>
5 * Copyright (C) 2010 - 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/module.h>
24#include <linux/debugfs.h>
25#include <linux/device.h>
26#include <linux/platform_device.h>
27#include <linux/seq_file.h>
28#include <linux/slab.h>
29#include <linux/io.h>
30#include <mach/iomap.h>
31#include <sound/soc.h>
32#include "tegra_das.h"
33
34#define DRV_NAME "tegra-das"
35
36static struct tegra_das *das;
37
38static inline void tegra_das_write(u32 reg, u32 val)
39{
40 __raw_writel(val, das->regs + reg);
41}
42
43static inline u32 tegra_das_read(u32 reg)
44{
45 return __raw_readl(das->regs + reg);
46}
47
48int tegra_das_connect_dap_to_dac(int dap, int dac)
49{
50 u32 addr;
51 u32 reg;
52
53 if (!das)
54 return -ENODEV;
55
56 addr = TEGRA_DAS_DAP_CTRL_SEL +
57 (dap * TEGRA_DAS_DAP_CTRL_SEL_STRIDE);
58 reg = dac << TEGRA_DAS_DAP_CTRL_SEL_DAP_CTRL_SEL_P;
59
60 tegra_das_write(addr, reg);
61
62 return 0;
63}
64EXPORT_SYMBOL_GPL(tegra_das_connect_dap_to_dac);
65
66int tegra_das_connect_dap_to_dap(int dap, int otherdap, int master,
67 int sdata1rx, int sdata2rx)
68{
69 u32 addr;
70 u32 reg;
71
72 if (!das)
73 return -ENODEV;
74
75 addr = TEGRA_DAS_DAP_CTRL_SEL +
76 (dap * TEGRA_DAS_DAP_CTRL_SEL_STRIDE);
77 reg = otherdap << TEGRA_DAS_DAP_CTRL_SEL_DAP_CTRL_SEL_P |
78 !!sdata2rx << TEGRA_DAS_DAP_CTRL_SEL_DAP_SDATA2_TX_RX_P |
79 !!sdata1rx << TEGRA_DAS_DAP_CTRL_SEL_DAP_SDATA1_TX_RX_P |
80 !!master << TEGRA_DAS_DAP_CTRL_SEL_DAP_MS_SEL_P;
81
82 tegra_das_write(addr, reg);
83
84 return 0;
85}
86EXPORT_SYMBOL_GPL(tegra_das_connect_dap_to_dap);
87
88int tegra_das_connect_dac_to_dap(int dac, int dap)
89{
90 u32 addr;
91 u32 reg;
92
93 if (!das)
94 return -ENODEV;
95
96 addr = TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL +
97 (dac * TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_STRIDE);
98 reg = dap << TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_CLK_SEL_P |
99 dap << TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_SDATA1_SEL_P |
100 dap << TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_SDATA2_SEL_P;
101
102 tegra_das_write(addr, reg);
103
104 return 0;
105}
106EXPORT_SYMBOL_GPL(tegra_das_connect_dac_to_dap);
107
108#ifdef CONFIG_DEBUG_FS
109static int tegra_das_show(struct seq_file *s, void *unused)
110{
111 int i;
112 u32 addr;
113 u32 reg;
114
115 for (i = 0; i < TEGRA_DAS_DAP_CTRL_SEL_COUNT; i++) {
116 addr = TEGRA_DAS_DAP_CTRL_SEL +
117 (i * TEGRA_DAS_DAP_CTRL_SEL_STRIDE);
118 reg = tegra_das_read(addr);
119 seq_printf(s, "TEGRA_DAS_DAP_CTRL_SEL[%d] = %08x\n", i, reg);
120 }
121
122 for (i = 0; i < TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_COUNT; i++) {
123 addr = TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL +
124 (i * TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_STRIDE);
125 reg = tegra_das_read(addr);
126 seq_printf(s, "TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL[%d] = %08x\n",
127 i, reg);
128 }
129
130 return 0;
131}
132
133static int tegra_das_debug_open(struct inode *inode, struct file *file)
134{
135 return single_open(file, tegra_das_show, inode->i_private);
136}
137
138static const struct file_operations tegra_das_debug_fops = {
139 .open = tegra_das_debug_open,
140 .read = seq_read,
141 .llseek = seq_lseek,
142 .release = single_release,
143};
144
145static void tegra_das_debug_add(struct tegra_das *das)
146{
147 das->debug = debugfs_create_file(DRV_NAME, S_IRUGO,
148 snd_soc_debugfs_root, das,
149 &tegra_das_debug_fops);
150}
151
152static void tegra_das_debug_remove(struct tegra_das *das)
153{
154 if (das->debug)
155 debugfs_remove(das->debug);
156}
157#else
158static inline void tegra_das_debug_add(struct tegra_das *das)
159{
160}
161
162static inline void tegra_das_debug_remove(struct tegra_das *das)
163{
164}
165#endif
166
167static int __devinit tegra_das_probe(struct platform_device *pdev)
168{
169 struct resource *res, *region;
170 int ret = 0;
171
172 if (das)
173 return -ENODEV;
174
175 das = kzalloc(sizeof(struct tegra_das), GFP_KERNEL);
176 if (!das) {
177 dev_err(&pdev->dev, "Can't allocate tegra_das\n");
178 ret = -ENOMEM;
179 goto exit;
180 }
181 das->dev = &pdev->dev;
182
183 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
184 if (!res) {
185 dev_err(&pdev->dev, "No memory resource\n");
186 ret = -ENODEV;
187 goto err_free;
188 }
189
190 region = request_mem_region(res->start, resource_size(res),
191 pdev->name);
192 if (!region) {
193 dev_err(&pdev->dev, "Memory region already claimed\n");
194 ret = -EBUSY;
195 goto err_free;
196 }
197
198 das->regs = ioremap(res->start, resource_size(res));
199 if (!das->regs) {
200 dev_err(&pdev->dev, "ioremap failed\n");
201 ret = -ENOMEM;
202 goto err_release;
203 }
204
205 tegra_das_debug_add(das);
206
207 platform_set_drvdata(pdev, das);
208
209 return 0;
210
211err_release:
212 release_mem_region(res->start, resource_size(res));
213err_free:
214 kfree(das);
215 das = 0;
216exit:
217 return ret;
218}
219
220static int __devexit tegra_das_remove(struct platform_device *pdev)
221{
222 struct resource *res;
223
224 if (!das)
225 return -ENODEV;
226
227 platform_set_drvdata(pdev, NULL);
228
229 tegra_das_debug_remove(das);
230
231 iounmap(das->regs);
232
233 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
234 release_mem_region(res->start, resource_size(res));
235
236 kfree(das);
237 das = 0;
238
239 return 0;
240}
241
242static struct platform_driver tegra_das_driver = {
243 .probe = tegra_das_probe,
244 .remove = __devexit_p(tegra_das_remove),
245 .driver = {
246 .name = DRV_NAME,
247 },
248};
249
250static int __init tegra_das_modinit(void)
251{
252 return platform_driver_register(&tegra_das_driver);
253}
254module_init(tegra_das_modinit);
255
256static void __exit tegra_das_modexit(void)
257{
258 platform_driver_unregister(&tegra_das_driver);
259}
260module_exit(tegra_das_modexit);
261
262MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>");
263MODULE_DESCRIPTION("Tegra DAS driver");
264MODULE_LICENSE("GPL");
265MODULE_ALIAS("platform:" DRV_NAME);
diff --git a/sound/soc/tegra/tegra_das.h b/sound/soc/tegra/tegra_das.h
new file mode 100644
index 000000000000..2c96c7b3c459
--- /dev/null
+++ b/sound/soc/tegra/tegra_das.h
@@ -0,0 +1,135 @@
1/*
2 * tegra_das.h - Definitions for Tegra DAS driver
3 *
4 * Author: Stephen Warren <swarren@nvidia.com>
5 * Copyright (C) 2010 - 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#ifndef __TEGRA_DAS_H__
24#define __TEGRA_DAS_H__
25
26/* Register TEGRA_DAS_DAP_CTRL_SEL */
27#define TEGRA_DAS_DAP_CTRL_SEL 0x00
28#define TEGRA_DAS_DAP_CTRL_SEL_COUNT 5
29#define TEGRA_DAS_DAP_CTRL_SEL_STRIDE 4
30#define TEGRA_DAS_DAP_CTRL_SEL_DAP_MS_SEL_P 31
31#define TEGRA_DAS_DAP_CTRL_SEL_DAP_MS_SEL_S 1
32#define TEGRA_DAS_DAP_CTRL_SEL_DAP_SDATA1_TX_RX_P 30
33#define TEGRA_DAS_DAP_CTRL_SEL_DAP_SDATA1_TX_RX_S 1
34#define TEGRA_DAS_DAP_CTRL_SEL_DAP_SDATA2_TX_RX_P 29
35#define TEGRA_DAS_DAP_CTRL_SEL_DAP_SDATA2_TX_RX_S 1
36#define TEGRA_DAS_DAP_CTRL_SEL_DAP_CTRL_SEL_P 0
37#define TEGRA_DAS_DAP_CTRL_SEL_DAP_CTRL_SEL_S 5
38
39/* Values for field TEGRA_DAS_DAP_CTRL_SEL_DAP_CTRL_SEL */
40#define TEGRA_DAS_DAP_SEL_DAC1 0
41#define TEGRA_DAS_DAP_SEL_DAC2 1
42#define TEGRA_DAS_DAP_SEL_DAC3 2
43#define TEGRA_DAS_DAP_SEL_DAP1 16
44#define TEGRA_DAS_DAP_SEL_DAP2 17
45#define TEGRA_DAS_DAP_SEL_DAP3 18
46#define TEGRA_DAS_DAP_SEL_DAP4 19
47#define TEGRA_DAS_DAP_SEL_DAP5 20
48
49/* Register TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL */
50#define TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL 0x40
51#define TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_COUNT 3
52#define TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_STRIDE 4
53#define TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_SDATA2_SEL_P 28
54#define TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_SDATA2_SEL_S 4
55#define TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_SDATA1_SEL_P 24
56#define TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_SDATA1_SEL_S 4
57#define TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_CLK_SEL_P 0
58#define TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_CLK_SEL_S 4
59
60/*
61 * Values for:
62 * TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_SDATA2_SEL
63 * TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_SDATA1_SEL
64 * TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_CLK_SEL
65 */
66#define TEGRA_DAS_DAC_SEL_DAP1 0
67#define TEGRA_DAS_DAC_SEL_DAP2 1
68#define TEGRA_DAS_DAC_SEL_DAP3 2
69#define TEGRA_DAS_DAC_SEL_DAP4 3
70#define TEGRA_DAS_DAC_SEL_DAP5 4
71
72/*
73 * Names/IDs of the DACs/DAPs.
74 */
75
76#define TEGRA_DAS_DAP_ID_1 0
77#define TEGRA_DAS_DAP_ID_2 1
78#define TEGRA_DAS_DAP_ID_3 2
79#define TEGRA_DAS_DAP_ID_4 3
80#define TEGRA_DAS_DAP_ID_5 4
81
82#define TEGRA_DAS_DAC_ID_1 0
83#define TEGRA_DAS_DAC_ID_2 1
84#define TEGRA_DAS_DAC_ID_3 2
85
86struct tegra_das {
87 struct device *dev;
88 void __iomem *regs;
89 struct dentry *debug;
90};
91
92/*
93 * Terminology:
94 * DAS: Digital audio switch (HW module controlled by this driver)
95 * DAP: Digital audio port (port/pins on Tegra device)
96 * DAC: Digital audio controller (e.g. I2S or AC97 controller elsewhere)
97 *
98 * The Tegra DAS is a mux/cross-bar which can connect each DAP to a specific
99 * DAC, or another DAP. When DAPs are connected, one must be the master and
100 * one the slave. Each DAC allows selection of a specific DAP for input, to
101 * cater for the case where N DAPs are connected to 1 DAC for broadcast
102 * output.
103 *
104 * This driver is dumb; no attempt is made to ensure that a valid routing
105 * configuration is programmed.
106 */
107
108/*
109 * Connect a DAP to to a DAC
110 * dap_id: DAP to connect: TEGRA_DAS_DAP_ID_*
111 * dac_sel: DAC to connect to: TEGRA_DAS_DAP_SEL_DAC*
112 */
113extern int tegra_das_connect_dap_to_dac(int dap_id, int dac_sel);
114
115/*
116 * Connect a DAP to to another DAP
117 * dap_id: DAP to connect: TEGRA_DAS_DAP_ID_*
118 * other_dap_sel: DAP to connect to: TEGRA_DAS_DAP_SEL_DAP*
119 * master: Is this DAP the master (1) or slave (0)
120 * sdata1rx: Is this DAP's SDATA1 pin RX (1) or TX (0)
121 * sdata2rx: Is this DAP's SDATA2 pin RX (1) or TX (0)
122 */
123extern int tegra_das_connect_dap_to_dap(int dap_id, int other_dap_sel,
124 int master, int sdata1rx,
125 int sdata2rx);
126
127/*
128 * Connect a DAC's input to a DAP
129 * (DAC outputs are selected by the DAP)
130 * dac_id: DAC ID to connect: TEGRA_DAS_DAC_ID_*
131 * dap_sel: DAP to receive input from: TEGRA_DAS_DAC_SEL_DAP*
132 */
133extern int tegra_das_connect_dac_to_dap(int dac_id, int dap_sel);
134
135#endif
diff --git a/sound/soc/tegra/tegra_i2s.c b/sound/soc/tegra/tegra_i2s.c
new file mode 100644
index 000000000000..95f03c10b4f7
--- /dev/null
+++ b/sound/soc/tegra/tegra_i2s.c
@@ -0,0 +1,509 @@
1/*
2 * tegra_i2s.c - Tegra I2S driver
3 *
4 * Author: Stephen Warren <swarren@nvidia.com>
5 * Copyright (C) 2010 - NVIDIA, Inc.
6 *
7 * Based on code copyright/by:
8 *
9 * Copyright (c) 2009-2010, 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 <sound/core.h>
41#include <sound/pcm.h>
42#include <sound/pcm_params.h>
43#include <sound/soc.h>
44
45#include "tegra_das.h"
46#include "tegra_i2s.h"
47
48#define DRV_NAME "tegra-i2s"
49
50static inline void tegra_i2s_write(struct tegra_i2s *i2s, u32 reg, u32 val)
51{
52 __raw_writel(val, i2s->regs + reg);
53}
54
55static inline u32 tegra_i2s_read(struct tegra_i2s *i2s, u32 reg)
56{
57 return __raw_readl(i2s->regs + reg);
58}
59
60#ifdef CONFIG_DEBUG_FS
61static int tegra_i2s_show(struct seq_file *s, void *unused)
62{
63#define REG(r) { r, #r }
64 static const struct {
65 int offset;
66 const char *name;
67 } regs[] = {
68 REG(TEGRA_I2S_CTRL),
69 REG(TEGRA_I2S_STATUS),
70 REG(TEGRA_I2S_TIMING),
71 REG(TEGRA_I2S_FIFO_SCR),
72 REG(TEGRA_I2S_PCM_CTRL),
73 REG(TEGRA_I2S_NW_CTRL),
74 REG(TEGRA_I2S_TDM_CTRL),
75 REG(TEGRA_I2S_TDM_TX_RX_CTRL),
76 };
77#undef REG
78
79 struct tegra_i2s *i2s = s->private;
80 int i;
81
82 for (i = 0; i < ARRAY_SIZE(regs); i++) {
83 u32 val = tegra_i2s_read(i2s, regs[i].offset);
84 seq_printf(s, "%s = %08x\n", regs[i].name, val);
85 }
86
87 return 0;
88}
89
90static int tegra_i2s_debug_open(struct inode *inode, struct file *file)
91{
92 return single_open(file, tegra_i2s_show, inode->i_private);
93}
94
95static const struct file_operations tegra_i2s_debug_fops = {
96 .open = tegra_i2s_debug_open,
97 .read = seq_read,
98 .llseek = seq_lseek,
99 .release = single_release,
100};
101
102static void tegra_i2s_debug_add(struct tegra_i2s *i2s, int id)
103{
104 char name[] = DRV_NAME ".0";
105
106 snprintf(name, sizeof(name), DRV_NAME".%1d", id);
107 i2s->debug = debugfs_create_file(name, S_IRUGO, snd_soc_debugfs_root,
108 i2s, &tegra_i2s_debug_fops);
109}
110
111static void tegra_i2s_debug_remove(struct tegra_i2s *i2s)
112{
113 if (i2s->debug)
114 debugfs_remove(i2s->debug);
115}
116#else
117static inline void tegra_i2s_debug_add(struct tegra_i2s *i2s, int id)
118{
119}
120
121static inline void tegra_i2s_debug_remove(struct tegra_i2s *i2s)
122{
123}
124#endif
125
126static int tegra_i2s_set_fmt(struct snd_soc_dai *dai,
127 unsigned int fmt)
128{
129 struct tegra_i2s *i2s = snd_soc_dai_get_drvdata(dai);
130
131 switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
132 case SND_SOC_DAIFMT_NB_NF:
133 break;
134 default:
135 return -EINVAL;
136 }
137
138 i2s->reg_ctrl &= ~TEGRA_I2S_CTRL_MASTER_ENABLE;
139 switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
140 case SND_SOC_DAIFMT_CBS_CFS:
141 i2s->reg_ctrl |= TEGRA_I2S_CTRL_MASTER_ENABLE;
142 break;
143 case SND_SOC_DAIFMT_CBM_CFM:
144 break;
145 default:
146 return -EINVAL;
147 }
148
149 i2s->reg_ctrl &= ~(TEGRA_I2S_CTRL_BIT_FORMAT_MASK |
150 TEGRA_I2S_CTRL_LRCK_MASK);
151 switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
152 case SND_SOC_DAIFMT_DSP_A:
153 i2s->reg_ctrl |= TEGRA_I2S_CTRL_BIT_FORMAT_DSP;
154 i2s->reg_ctrl |= TEGRA_I2S_CTRL_LRCK_L_LOW;
155 break;
156 case SND_SOC_DAIFMT_DSP_B:
157 i2s->reg_ctrl |= TEGRA_I2S_CTRL_BIT_FORMAT_DSP;
158 i2s->reg_ctrl |= TEGRA_I2S_CTRL_LRCK_R_LOW;
159 break;
160 case SND_SOC_DAIFMT_I2S:
161 i2s->reg_ctrl |= TEGRA_I2S_CTRL_BIT_FORMAT_I2S;
162 i2s->reg_ctrl |= TEGRA_I2S_CTRL_LRCK_L_LOW;
163 break;
164 case SND_SOC_DAIFMT_RIGHT_J:
165 i2s->reg_ctrl |= TEGRA_I2S_CTRL_BIT_FORMAT_RJM;
166 i2s->reg_ctrl |= TEGRA_I2S_CTRL_LRCK_L_LOW;
167 break;
168 case SND_SOC_DAIFMT_LEFT_J:
169 i2s->reg_ctrl |= TEGRA_I2S_CTRL_BIT_FORMAT_LJM;
170 i2s->reg_ctrl |= TEGRA_I2S_CTRL_LRCK_L_LOW;
171 break;
172 default:
173 return -EINVAL;
174 }
175
176 return 0;
177}
178
179static int tegra_i2s_hw_params(struct snd_pcm_substream *substream,
180 struct snd_pcm_hw_params *params,
181 struct snd_soc_dai *dai)
182{
183 struct device *dev = substream->pcm->card->dev;
184 struct tegra_i2s *i2s = snd_soc_dai_get_drvdata(dai);
185 u32 reg;
186 int ret, sample_size, srate, i2sclock, bitcnt;
187
188 i2s->reg_ctrl &= ~TEGRA_I2S_CTRL_BIT_SIZE_MASK;
189 switch (params_format(params)) {
190 case SNDRV_PCM_FORMAT_S16_LE:
191 i2s->reg_ctrl |= TEGRA_I2S_CTRL_BIT_SIZE_16;
192 sample_size = 16;
193 break;
194 case SNDRV_PCM_FORMAT_S24_LE:
195 i2s->reg_ctrl |= TEGRA_I2S_CTRL_BIT_SIZE_24;
196 sample_size = 24;
197 break;
198 case SNDRV_PCM_FORMAT_S32_LE:
199 i2s->reg_ctrl |= TEGRA_I2S_CTRL_BIT_SIZE_32;
200 sample_size = 32;
201 break;
202 default:
203 return -EINVAL;
204 }
205
206 srate = params_rate(params);
207
208 /* Final "* 2" required by Tegra hardware */
209 i2sclock = srate * params_channels(params) * sample_size * 2;
210
211 ret = clk_set_rate(i2s->clk_i2s, i2sclock);
212 if (ret) {
213 dev_err(dev, "Can't set I2S clock rate: %d\n", ret);
214 return ret;
215 }
216
217 bitcnt = (i2sclock / (2 * srate)) - 1;
218 if (bitcnt < 0 || bitcnt > TEGRA_I2S_TIMING_CHANNEL_BIT_COUNT_MASK_US)
219 return -EINVAL;
220 reg = bitcnt << TEGRA_I2S_TIMING_CHANNEL_BIT_COUNT_SHIFT;
221
222 if (i2sclock % (2 * srate))
223 reg |= TEGRA_I2S_TIMING_NON_SYM_ENABLE;
224
225 if (!i2s->clk_refs)
226 clk_enable(i2s->clk_i2s);
227
228 tegra_i2s_write(i2s, TEGRA_I2S_TIMING, reg);
229
230 tegra_i2s_write(i2s, TEGRA_I2S_FIFO_SCR,
231 TEGRA_I2S_FIFO_SCR_FIFO2_ATN_LVL_FOUR_SLOTS |
232 TEGRA_I2S_FIFO_SCR_FIFO1_ATN_LVL_FOUR_SLOTS);
233
234 if (!i2s->clk_refs)
235 clk_disable(i2s->clk_i2s);
236
237 return 0;
238}
239
240static void tegra_i2s_start_playback(struct tegra_i2s *i2s)
241{
242 i2s->reg_ctrl |= TEGRA_I2S_CTRL_FIFO1_ENABLE;
243 tegra_i2s_write(i2s, TEGRA_I2S_CTRL, i2s->reg_ctrl);
244}
245
246static void tegra_i2s_stop_playback(struct tegra_i2s *i2s)
247{
248 i2s->reg_ctrl &= ~TEGRA_I2S_CTRL_FIFO1_ENABLE;
249 tegra_i2s_write(i2s, TEGRA_I2S_CTRL, i2s->reg_ctrl);
250}
251
252static void tegra_i2s_start_capture(struct tegra_i2s *i2s)
253{
254 i2s->reg_ctrl |= TEGRA_I2S_CTRL_FIFO2_ENABLE;
255 tegra_i2s_write(i2s, TEGRA_I2S_CTRL, i2s->reg_ctrl);
256}
257
258static void tegra_i2s_stop_capture(struct tegra_i2s *i2s)
259{
260 i2s->reg_ctrl &= ~TEGRA_I2S_CTRL_FIFO2_ENABLE;
261 tegra_i2s_write(i2s, TEGRA_I2S_CTRL, i2s->reg_ctrl);
262}
263
264static int tegra_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
265 struct snd_soc_dai *dai)
266{
267 struct tegra_i2s *i2s = snd_soc_dai_get_drvdata(dai);
268
269 switch (cmd) {
270 case SNDRV_PCM_TRIGGER_START:
271 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
272 case SNDRV_PCM_TRIGGER_RESUME:
273 if (!i2s->clk_refs)
274 clk_enable(i2s->clk_i2s);
275 i2s->clk_refs++;
276 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
277 tegra_i2s_start_playback(i2s);
278 else
279 tegra_i2s_start_capture(i2s);
280 break;
281 case SNDRV_PCM_TRIGGER_STOP:
282 case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
283 case SNDRV_PCM_TRIGGER_SUSPEND:
284 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
285 tegra_i2s_stop_playback(i2s);
286 else
287 tegra_i2s_stop_capture(i2s);
288 i2s->clk_refs--;
289 if (!i2s->clk_refs)
290 clk_disable(i2s->clk_i2s);
291 break;
292 default:
293 return -EINVAL;
294 }
295
296 return 0;
297}
298
299static int tegra_i2s_probe(struct snd_soc_dai *dai)
300{
301 struct tegra_i2s * i2s = snd_soc_dai_get_drvdata(dai);
302
303 dai->capture_dma_data = &i2s->capture_dma_data;
304 dai->playback_dma_data = &i2s->playback_dma_data;
305
306 return 0;
307}
308
309static struct snd_soc_dai_ops tegra_i2s_dai_ops = {
310 .set_fmt = tegra_i2s_set_fmt,
311 .hw_params = tegra_i2s_hw_params,
312 .trigger = tegra_i2s_trigger,
313};
314
315struct snd_soc_dai_driver tegra_i2s_dai[] = {
316 {
317 .name = DRV_NAME ".0",
318 .probe = tegra_i2s_probe,
319 .playback = {
320 .channels_min = 2,
321 .channels_max = 2,
322 .rates = SNDRV_PCM_RATE_8000_96000,
323 .formats = SNDRV_PCM_FMTBIT_S16_LE,
324 },
325 .capture = {
326 .channels_min = 2,
327 .channels_max = 2,
328 .rates = SNDRV_PCM_RATE_8000_96000,
329 .formats = SNDRV_PCM_FMTBIT_S16_LE,
330 },
331 .ops = &tegra_i2s_dai_ops,
332 .symmetric_rates = 1,
333 },
334 {
335 .name = DRV_NAME ".1",
336 .probe = tegra_i2s_probe,
337 .playback = {
338 .channels_min = 2,
339 .channels_max = 2,
340 .rates = SNDRV_PCM_RATE_8000_96000,
341 .formats = SNDRV_PCM_FMTBIT_S16_LE,
342 },
343 .capture = {
344 .channels_min = 2,
345 .channels_max = 2,
346 .rates = SNDRV_PCM_RATE_8000_96000,
347 .formats = SNDRV_PCM_FMTBIT_S16_LE,
348 },
349 .ops = &tegra_i2s_dai_ops,
350 .symmetric_rates = 1,
351 },
352};
353
354static __devinit int tegra_i2s_platform_probe(struct platform_device *pdev)
355{
356 struct tegra_i2s * i2s;
357 char clk_name[12]; /* tegra-i2s.0 */
358 struct resource *mem, *memregion, *dmareq;
359 int ret;
360
361 if ((pdev->id < 0) ||
362 (pdev->id >= ARRAY_SIZE(tegra_i2s_dai))) {
363 dev_err(&pdev->dev, "ID %d out of range\n", pdev->id);
364 return -EINVAL;
365 }
366
367 /*
368 * FIXME: Until a codec driver exists for the tegra DAS, hard-code a
369 * 1:1 mapping between audio controllers and audio ports.
370 */
371 ret = tegra_das_connect_dap_to_dac(TEGRA_DAS_DAP_ID_1 + pdev->id,
372 TEGRA_DAS_DAP_SEL_DAC1 + pdev->id);
373 if (ret) {
374 dev_err(&pdev->dev, "Can't set up DAP connection\n");
375 return ret;
376 }
377 ret = tegra_das_connect_dac_to_dap(TEGRA_DAS_DAC_ID_1 + pdev->id,
378 TEGRA_DAS_DAC_SEL_DAP1 + pdev->id);
379 if (ret) {
380 dev_err(&pdev->dev, "Can't set up DAC connection\n");
381 return ret;
382 }
383
384 i2s = kzalloc(sizeof(struct tegra_i2s), GFP_KERNEL);
385 if (!i2s) {
386 dev_err(&pdev->dev, "Can't allocate tegra_i2s\n");
387 ret = -ENOMEM;
388 goto exit;
389 }
390 dev_set_drvdata(&pdev->dev, i2s);
391
392 snprintf(clk_name, sizeof(clk_name), DRV_NAME ".%d", pdev->id);
393 i2s->clk_i2s = clk_get_sys(clk_name, NULL);
394 if (IS_ERR(i2s->clk_i2s)) {
395 dev_err(&pdev->dev, "Can't retrieve i2s clock\n");
396 ret = PTR_ERR(i2s->clk_i2s);
397 goto err_free;
398 }
399
400 mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
401 if (!mem) {
402 dev_err(&pdev->dev, "No memory resource\n");
403 ret = -ENODEV;
404 goto err_clk_put;
405 }
406
407 dmareq = platform_get_resource(pdev, IORESOURCE_DMA, 0);
408 if (!dmareq) {
409 dev_err(&pdev->dev, "No DMA resource\n");
410 ret = -ENODEV;
411 goto err_clk_put;
412 }
413
414 memregion = request_mem_region(mem->start, resource_size(mem),
415 DRV_NAME);
416 if (!memregion) {
417 dev_err(&pdev->dev, "Memory region already claimed\n");
418 ret = -EBUSY;
419 goto err_clk_put;
420 }
421
422 i2s->regs = ioremap(mem->start, resource_size(mem));
423 if (!i2s->regs) {
424 dev_err(&pdev->dev, "ioremap failed\n");
425 ret = -ENOMEM;
426 goto err_release;
427 }
428
429 i2s->capture_dma_data.addr = mem->start + TEGRA_I2S_FIFO2;
430 i2s->capture_dma_data.wrap = 4;
431 i2s->capture_dma_data.width = 32;
432 i2s->capture_dma_data.req_sel = dmareq->start;
433
434 i2s->playback_dma_data.addr = mem->start + TEGRA_I2S_FIFO1;
435 i2s->playback_dma_data.wrap = 4;
436 i2s->playback_dma_data.width = 32;
437 i2s->playback_dma_data.req_sel = dmareq->start;
438
439 i2s->reg_ctrl = TEGRA_I2S_CTRL_FIFO_FORMAT_PACKED;
440
441 ret = snd_soc_register_dai(&pdev->dev, &tegra_i2s_dai[pdev->id]);
442 if (ret) {
443 dev_err(&pdev->dev, "Could not register DAI: %d\n", ret);
444 ret = -ENOMEM;
445 goto err_unmap;
446 }
447
448 tegra_i2s_debug_add(i2s, pdev->id);
449
450 return 0;
451
452err_unmap:
453 iounmap(i2s->regs);
454err_release:
455 release_mem_region(mem->start, resource_size(mem));
456err_clk_put:
457 clk_put(i2s->clk_i2s);
458err_free:
459 kfree(i2s);
460exit:
461 return ret;
462}
463
464static int __devexit tegra_i2s_platform_remove(struct platform_device *pdev)
465{
466 struct tegra_i2s *i2s = dev_get_drvdata(&pdev->dev);
467 struct resource *res;
468
469 snd_soc_unregister_dai(&pdev->dev);
470
471 tegra_i2s_debug_remove(i2s);
472
473 iounmap(i2s->regs);
474
475 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
476 release_mem_region(res->start, resource_size(res));
477
478 clk_put(i2s->clk_i2s);
479
480 kfree(i2s);
481
482 return 0;
483}
484
485static struct platform_driver tegra_i2s_driver = {
486 .driver = {
487 .name = DRV_NAME,
488 .owner = THIS_MODULE,
489 },
490 .probe = tegra_i2s_platform_probe,
491 .remove = __devexit_p(tegra_i2s_platform_remove),
492};
493
494static int __init snd_tegra_i2s_init(void)
495{
496 return platform_driver_register(&tegra_i2s_driver);
497}
498module_init(snd_tegra_i2s_init);
499
500static void __exit snd_tegra_i2s_exit(void)
501{
502 platform_driver_unregister(&tegra_i2s_driver);
503}
504module_exit(snd_tegra_i2s_exit);
505
506MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>");
507MODULE_DESCRIPTION("Tegra I2S ASoC driver");
508MODULE_LICENSE("GPL");
509MODULE_ALIAS("platform:" DRV_NAME);
diff --git a/sound/soc/tegra/tegra_i2s.h b/sound/soc/tegra/tegra_i2s.h
new file mode 100644
index 000000000000..2b38a096f46c
--- /dev/null
+++ b/sound/soc/tegra/tegra_i2s.h
@@ -0,0 +1,165 @@
1/*
2 * tegra_i2s.h - Definitions for Tegra I2S driver
3 *
4 * Author: Stephen Warren <swarren@nvidia.com>
5 * Copyright (C) 2010 - NVIDIA, Inc.
6 *
7 * Based on code copyright/by:
8 *
9 * Copyright (c) 2009-2010, 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#ifndef __TEGRA_I2S_H__
32#define __TEGRA_I2S_H__
33
34#include "tegra_pcm.h"
35
36/* Register offsets from TEGRA_I2S1_BASE and TEGRA_I2S2_BASE */
37
38#define TEGRA_I2S_CTRL 0x00
39#define TEGRA_I2S_STATUS 0x04
40#define TEGRA_I2S_TIMING 0x08
41#define TEGRA_I2S_FIFO_SCR 0x0c
42#define TEGRA_I2S_PCM_CTRL 0x10
43#define TEGRA_I2S_NW_CTRL 0x14
44#define TEGRA_I2S_TDM_CTRL 0x20
45#define TEGRA_I2S_TDM_TX_RX_CTRL 0x24
46#define TEGRA_I2S_FIFO1 0x40
47#define TEGRA_I2S_FIFO2 0x80
48
49/* Fields in TEGRA_I2S_CTRL */
50
51#define TEGRA_I2S_CTRL_FIFO2_TX_ENABLE (1 << 30)
52#define TEGRA_I2S_CTRL_FIFO1_ENABLE (1 << 29)
53#define TEGRA_I2S_CTRL_FIFO2_ENABLE (1 << 28)
54#define TEGRA_I2S_CTRL_FIFO1_RX_ENABLE (1 << 27)
55#define TEGRA_I2S_CTRL_FIFO_LPBK_ENABLE (1 << 26)
56#define TEGRA_I2S_CTRL_MASTER_ENABLE (1 << 25)
57
58#define TEGRA_I2S_LRCK_LEFT_LOW 0
59#define TEGRA_I2S_LRCK_RIGHT_LOW 1
60
61#define TEGRA_I2S_CTRL_LRCK_SHIFT 24
62#define TEGRA_I2S_CTRL_LRCK_MASK (1 << TEGRA_I2S_CTRL_LRCK_SHIFT)
63#define TEGRA_I2S_CTRL_LRCK_L_LOW (TEGRA_I2S_LRCK_LEFT_LOW << TEGRA_I2S_CTRL_LRCK_SHIFT)
64#define TEGRA_I2S_CTRL_LRCK_R_LOW (TEGRA_I2S_LRCK_RIGHT_LOW << TEGRA_I2S_CTRL_LRCK_SHIFT)
65
66#define TEGRA_I2S_BIT_FORMAT_I2S 0
67#define TEGRA_I2S_BIT_FORMAT_RJM 1
68#define TEGRA_I2S_BIT_FORMAT_LJM 2
69#define TEGRA_I2S_BIT_FORMAT_DSP 3
70
71#define TEGRA_I2S_CTRL_BIT_FORMAT_SHIFT 10
72#define TEGRA_I2S_CTRL_BIT_FORMAT_MASK (3 << TEGRA_I2S_CTRL_BIT_FORMAT_SHIFT)
73#define TEGRA_I2S_CTRL_BIT_FORMAT_I2S (TEGRA_I2S_BIT_FORMAT_I2S << TEGRA_I2S_CTRL_BIT_FORMAT_SHIFT)
74#define TEGRA_I2S_CTRL_BIT_FORMAT_RJM (TEGRA_I2S_BIT_FORMAT_RJM << TEGRA_I2S_CTRL_BIT_FORMAT_SHIFT)
75#define TEGRA_I2S_CTRL_BIT_FORMAT_LJM (TEGRA_I2S_BIT_FORMAT_LJM << TEGRA_I2S_CTRL_BIT_FORMAT_SHIFT)
76#define TEGRA_I2S_CTRL_BIT_FORMAT_DSP (TEGRA_I2S_BIT_FORMAT_DSP << TEGRA_I2S_CTRL_BIT_FORMAT_SHIFT)
77
78#define TEGRA_I2S_BIT_SIZE_16 0
79#define TEGRA_I2S_BIT_SIZE_20 1
80#define TEGRA_I2S_BIT_SIZE_24 2
81#define TEGRA_I2S_BIT_SIZE_32 3
82
83#define TEGRA_I2S_CTRL_BIT_SIZE_SHIFT 8
84#define TEGRA_I2S_CTRL_BIT_SIZE_MASK (3 << TEGRA_I2S_CTRL_BIT_SIZE_SHIFT)
85#define TEGRA_I2S_CTRL_BIT_SIZE_16 (TEGRA_I2S_BIT_SIZE_16 << TEGRA_I2S_CTRL_BIT_SIZE_SHIFT)
86#define TEGRA_I2S_CTRL_BIT_SIZE_20 (TEGRA_I2S_BIT_SIZE_20 << TEGRA_I2S_CTRL_BIT_SIZE_SHIFT)
87#define TEGRA_I2S_CTRL_BIT_SIZE_24 (TEGRA_I2S_BIT_SIZE_24 << TEGRA_I2S_CTRL_BIT_SIZE_SHIFT)
88#define TEGRA_I2S_CTRL_BIT_SIZE_32 (TEGRA_I2S_BIT_SIZE_32 << TEGRA_I2S_CTRL_BIT_SIZE_SHIFT)
89
90#define TEGRA_I2S_FIFO_16_LSB 0
91#define TEGRA_I2S_FIFO_20_LSB 1
92#define TEGRA_I2S_FIFO_24_LSB 2
93#define TEGRA_I2S_FIFO_32 3
94#define TEGRA_I2S_FIFO_PACKED 7
95
96#define TEGRA_I2S_CTRL_FIFO_FORMAT_SHIFT 4
97#define TEGRA_I2S_CTRL_FIFO_FORMAT_MASK (7 << TEGRA_I2S_CTRL_FIFO_FORMAT_SHIFT)
98#define TEGRA_I2S_CTRL_FIFO_FORMAT_16_LSB (TEGRA_I2S_FIFO_16_LSB << TEGRA_I2S_CTRL_FIFO_FORMAT_SHIFT)
99#define TEGRA_I2S_CTRL_FIFO_FORMAT_20_LSB (TEGRA_I2S_FIFO_20_LSB << TEGRA_I2S_CTRL_FIFO_FORMAT_SHIFT)
100#define TEGRA_I2S_CTRL_FIFO_FORMAT_24_LSB (TEGRA_I2S_FIFO_24_LSB << TEGRA_I2S_CTRL_FIFO_FORMAT_SHIFT)
101#define TEGRA_I2S_CTRL_FIFO_FORMAT_32 (TEGRA_I2S_FIFO_32 << TEGRA_I2S_CTRL_FIFO_FORMAT_SHIFT)
102#define TEGRA_I2S_CTRL_FIFO_FORMAT_PACKED (TEGRA_I2S_FIFO_PACKED << TEGRA_I2S_CTRL_FIFO_FORMAT_SHIFT)
103
104#define TEGRA_I2S_CTRL_IE_FIFO1_ERR (1 << 3)
105#define TEGRA_I2S_CTRL_IE_FIFO2_ERR (1 << 2)
106#define TEGRA_I2S_CTRL_QE_FIFO1 (1 << 1)
107#define TEGRA_I2S_CTRL_QE_FIFO2 (1 << 0)
108
109/* Fields in TEGRA_I2S_STATUS */
110
111#define TEGRA_I2S_STATUS_FIFO1_RDY (1 << 31)
112#define TEGRA_I2S_STATUS_FIFO2_RDY (1 << 30)
113#define TEGRA_I2S_STATUS_FIFO1_BSY (1 << 29)
114#define TEGRA_I2S_STATUS_FIFO2_BSY (1 << 28)
115#define TEGRA_I2S_STATUS_FIFO1_ERR (1 << 3)
116#define TEGRA_I2S_STATUS_FIFO2_ERR (1 << 2)
117#define TEGRA_I2S_STATUS_QS_FIFO1 (1 << 1)
118#define TEGRA_I2S_STATUS_QS_FIFO2 (1 << 0)
119
120/* Fields in TEGRA_I2S_TIMING */
121
122#define TEGRA_I2S_TIMING_NON_SYM_ENABLE (1 << 12)
123#define TEGRA_I2S_TIMING_CHANNEL_BIT_COUNT_SHIFT 0
124#define TEGRA_I2S_TIMING_CHANNEL_BIT_COUNT_MASK_US 0x7fff
125#define TEGRA_I2S_TIMING_CHANNEL_BIT_COUNT_MASK (TEGRA_I2S_TIMING_CHANNEL_BIT_COUNT_MASK_US << TEGRA_I2S_TIMING_CHANNEL_BIT_COUNT_SHIFT)
126
127/* Fields in TEGRA_I2S_FIFO_SCR */
128
129#define TEGRA_I2S_FIFO_SCR_FIFO2_FULL_EMPTY_COUNT_SHIFT 24
130#define TEGRA_I2S_FIFO_SCR_FIFO1_FULL_EMPTY_COUNT_SHIFT 16
131#define TEGRA_I2S_FIFO_SCR_FIFO_FULL_EMPTY_COUNT_MASK 0x3f
132
133#define TEGRA_I2S_FIFO_SCR_FIFO2_CLR (1 << 12)
134#define TEGRA_I2S_FIFO_SCR_FIFO1_CLR (1 << 8)
135
136#define TEGRA_I2S_FIFO_ATN_LVL_ONE_SLOT 0
137#define TEGRA_I2S_FIFO_ATN_LVL_FOUR_SLOTS 1
138#define TEGRA_I2S_FIFO_ATN_LVL_EIGHT_SLOTS 2
139#define TEGRA_I2S_FIFO_ATN_LVL_TWELVE_SLOTS 3
140
141#define TEGRA_I2S_FIFO_SCR_FIFO2_ATN_LVL_SHIFT 4
142#define TEGRA_I2S_FIFO_SCR_FIFO2_ATN_LVL_MASK (3 << TEGRA_I2S_FIFO_SCR_FIFO2_ATN_LVL_SHIFT)
143#define TEGRA_I2S_FIFO_SCR_FIFO2_ATN_LVL_ONE_SLOT (TEGRA_I2S_FIFO_ATN_LVL_ONE_SLOT << TEGRA_I2S_FIFO_SCR_FIFO2_ATN_LVL_SHIFT)
144#define TEGRA_I2S_FIFO_SCR_FIFO2_ATN_LVL_FOUR_SLOTS (TEGRA_I2S_FIFO_ATN_LVL_FOUR_SLOTS << TEGRA_I2S_FIFO_SCR_FIFO2_ATN_LVL_SHIFT)
145#define TEGRA_I2S_FIFO_SCR_FIFO2_ATN_LVL_EIGHT_SLOTS (TEGRA_I2S_FIFO_ATN_LVL_EIGHT_SLOTS << TEGRA_I2S_FIFO_SCR_FIFO2_ATN_LVL_SHIFT)
146#define TEGRA_I2S_FIFO_SCR_FIFO2_ATN_LVL_TWELVE_SLOTS (TEGRA_I2S_FIFO_ATN_LVL_TWELVE_SLOTS << TEGRA_I2S_FIFO_SCR_FIFO2_ATN_LVL_SHIFT)
147
148#define TEGRA_I2S_FIFO_SCR_FIFO1_ATN_LVL_SHIFT 0
149#define TEGRA_I2S_FIFO_SCR_FIFO1_ATN_LVL_MASK (3 << TEGRA_I2S_FIFO_SCR_FIFO1_ATN_LVL_SHIFT)
150#define TEGRA_I2S_FIFO_SCR_FIFO1_ATN_LVL_ONE_SLOT (TEGRA_I2S_FIFO_ATN_LVL_ONE_SLOT << TEGRA_I2S_FIFO_SCR_FIFO1_ATN_LVL_SHIFT)
151#define TEGRA_I2S_FIFO_SCR_FIFO1_ATN_LVL_FOUR_SLOTS (TEGRA_I2S_FIFO_ATN_LVL_FOUR_SLOTS << TEGRA_I2S_FIFO_SCR_FIFO1_ATN_LVL_SHIFT)
152#define TEGRA_I2S_FIFO_SCR_FIFO1_ATN_LVL_EIGHT_SLOTS (TEGRA_I2S_FIFO_ATN_LVL_EIGHT_SLOTS << TEGRA_I2S_FIFO_SCR_FIFO1_ATN_LVL_SHIFT)
153#define TEGRA_I2S_FIFO_SCR_FIFO1_ATN_LVL_TWELVE_SLOTS (TEGRA_I2S_FIFO_ATN_LVL_TWELVE_SLOTS << TEGRA_I2S_FIFO_SCR_FIFO1_ATN_LVL_SHIFT)
154
155struct tegra_i2s {
156 struct clk *clk_i2s;
157 int clk_refs;
158 struct tegra_pcm_dma_params capture_dma_data;
159 struct tegra_pcm_dma_params playback_dma_data;
160 void __iomem *regs;
161 struct dentry *debug;
162 u32 reg_ctrl;
163};
164
165#endif
diff --git a/sound/soc/tegra/tegra_pcm.c b/sound/soc/tegra/tegra_pcm.c
new file mode 100644
index 000000000000..3c271f953582
--- /dev/null
+++ b/sound/soc/tegra/tegra_pcm.c
@@ -0,0 +1,404 @@
1/*
2 * tegra_pcm.c - Tegra PCM driver
3 *
4 * Author: Stephen Warren <swarren@nvidia.com>
5 * Copyright (C) 2010 - NVIDIA, Inc.
6 *
7 * Based on code copyright/by:
8 *
9 * Copyright (c) 2009-2010, NVIDIA Corporation.
10 * Scott Peterson <speterson@nvidia.com>
11 * Vijay Mali <vmali@nvidia.com>
12 *
13 * Copyright (C) 2010 Google, Inc.
14 * Iliyan Malchev <malchev@google.com>
15 *
16 * This program is free software; you can redistribute it and/or
17 * modify it under the terms of the GNU General Public License
18 * version 2 as published by the Free Software Foundation.
19 *
20 * This program is distributed in the hope that it will be useful, but
21 * WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
23 * General Public License for more details.
24 *
25 * You should have received a copy of the GNU General Public License
26 * along with this program; if not, write to the Free Software
27 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
28 * 02110-1301 USA
29 *
30 */
31
32#include <linux/module.h>
33#include <linux/dma-mapping.h>
34#include <linux/slab.h>
35#include <sound/core.h>
36#include <sound/pcm.h>
37#include <sound/pcm_params.h>
38#include <sound/soc.h>
39
40#include "tegra_pcm.h"
41
42#define DRV_NAME "tegra-pcm-audio"
43
44static const struct snd_pcm_hardware tegra_pcm_hardware = {
45 .info = SNDRV_PCM_INFO_MMAP |
46 SNDRV_PCM_INFO_MMAP_VALID |
47 SNDRV_PCM_INFO_PAUSE |
48 SNDRV_PCM_INFO_RESUME |
49 SNDRV_PCM_INFO_INTERLEAVED,
50 .formats = SNDRV_PCM_FMTBIT_S16_LE,
51 .channels_min = 2,
52 .channels_max = 2,
53 .period_bytes_min = 1024,
54 .period_bytes_max = PAGE_SIZE,
55 .periods_min = 2,
56 .periods_max = 8,
57 .buffer_bytes_max = PAGE_SIZE * 8,
58 .fifo_size = 4,
59};
60
61static void tegra_pcm_queue_dma(struct tegra_runtime_data *prtd)
62{
63 struct snd_pcm_substream *substream = prtd->substream;
64 struct snd_dma_buffer *buf = &substream->dma_buffer;
65 struct tegra_dma_req *dma_req;
66 unsigned long addr;
67
68 dma_req = &prtd->dma_req[prtd->dma_req_idx];
69 prtd->dma_req_idx = 1 - prtd->dma_req_idx;
70
71 addr = buf->addr + prtd->dma_pos;
72 prtd->dma_pos += dma_req->size;
73 if (prtd->dma_pos >= prtd->dma_pos_end)
74 prtd->dma_pos = 0;
75
76 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
77 dma_req->source_addr = addr;
78 else
79 dma_req->dest_addr = addr;
80
81 tegra_dma_enqueue_req(prtd->dma_chan, dma_req);
82}
83
84static void dma_complete_callback(struct tegra_dma_req *req)
85{
86 struct tegra_runtime_data *prtd = (struct tegra_runtime_data *)req->dev;
87 struct snd_pcm_substream *substream = prtd->substream;
88 struct snd_pcm_runtime *runtime = substream->runtime;
89
90 spin_lock(&prtd->lock);
91
92 if (!prtd->running) {
93 spin_unlock(&prtd->lock);
94 return;
95 }
96
97 if (++prtd->period_index >= runtime->periods)
98 prtd->period_index = 0;
99
100 tegra_pcm_queue_dma(prtd);
101
102 spin_unlock(&prtd->lock);
103
104 snd_pcm_period_elapsed(substream);
105}
106
107static void setup_dma_tx_request(struct tegra_dma_req *req,
108 struct tegra_pcm_dma_params * dmap)
109{
110 req->complete = dma_complete_callback;
111 req->to_memory = false;
112 req->dest_addr = dmap->addr;
113 req->dest_wrap = dmap->wrap;
114 req->source_bus_width = 32;
115 req->source_wrap = 0;
116 req->dest_bus_width = dmap->width;
117 req->req_sel = dmap->req_sel;
118}
119
120static void setup_dma_rx_request(struct tegra_dma_req *req,
121 struct tegra_pcm_dma_params * dmap)
122{
123 req->complete = dma_complete_callback;
124 req->to_memory = true;
125 req->source_addr = dmap->addr;
126 req->dest_wrap = 0;
127 req->source_bus_width = dmap->width;
128 req->source_wrap = dmap->wrap;
129 req->dest_bus_width = 32;
130 req->req_sel = dmap->req_sel;
131}
132
133static int tegra_pcm_open(struct snd_pcm_substream *substream)
134{
135 struct snd_pcm_runtime *runtime = substream->runtime;
136 struct tegra_runtime_data *prtd;
137 struct snd_soc_pcm_runtime *rtd = substream->private_data;
138 struct tegra_pcm_dma_params * dmap;
139 int ret = 0;
140
141 prtd = kzalloc(sizeof(struct tegra_runtime_data), GFP_KERNEL);
142 if (prtd == NULL)
143 return -ENOMEM;
144
145 runtime->private_data = prtd;
146 prtd->substream = substream;
147
148 spin_lock_init(&prtd->lock);
149
150 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
151 dmap = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
152 setup_dma_tx_request(&prtd->dma_req[0], dmap);
153 setup_dma_tx_request(&prtd->dma_req[1], dmap);
154 } else {
155 dmap = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
156 setup_dma_rx_request(&prtd->dma_req[0], dmap);
157 setup_dma_rx_request(&prtd->dma_req[1], dmap);
158 }
159
160 prtd->dma_req[0].dev = prtd;
161 prtd->dma_req[1].dev = prtd;
162
163 prtd->dma_chan = tegra_dma_allocate_channel(TEGRA_DMA_MODE_ONESHOT);
164 if (prtd->dma_chan == NULL) {
165 ret = -ENOMEM;
166 goto err;
167 }
168
169 /* Set HW params now that initialization is complete */
170 snd_soc_set_runtime_hwparams(substream, &tegra_pcm_hardware);
171
172 /* Ensure that buffer size is a multiple of period size */
173 ret = snd_pcm_hw_constraint_integer(runtime,
174 SNDRV_PCM_HW_PARAM_PERIODS);
175 if (ret < 0)
176 goto err;
177
178 return 0;
179
180err:
181 if (prtd->dma_chan) {
182 tegra_dma_free_channel(prtd->dma_chan);
183 }
184
185 kfree(prtd);
186
187 return ret;
188}
189
190static int tegra_pcm_close(struct snd_pcm_substream *substream)
191{
192 struct snd_pcm_runtime *runtime = substream->runtime;
193 struct tegra_runtime_data *prtd = runtime->private_data;
194
195 tegra_dma_free_channel(prtd->dma_chan);
196
197 kfree(prtd);
198
199 return 0;
200}
201
202static int tegra_pcm_hw_params(struct snd_pcm_substream *substream,
203 struct snd_pcm_hw_params *params)
204{
205 struct snd_pcm_runtime *runtime = substream->runtime;
206 struct tegra_runtime_data *prtd = runtime->private_data;
207
208 snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
209
210 prtd->dma_req[0].size = params_period_bytes(params);
211 prtd->dma_req[1].size = prtd->dma_req[0].size;
212
213 return 0;
214}
215
216static int tegra_pcm_hw_free(struct snd_pcm_substream *substream)
217{
218 snd_pcm_set_runtime_buffer(substream, NULL);
219
220 return 0;
221}
222
223static int tegra_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
224{
225 struct snd_pcm_runtime *runtime = substream->runtime;
226 struct tegra_runtime_data *prtd = runtime->private_data;
227 unsigned long flags;
228
229 switch (cmd) {
230 case SNDRV_PCM_TRIGGER_START:
231 prtd->dma_pos = 0;
232 prtd->dma_pos_end = frames_to_bytes(runtime, runtime->periods * runtime->period_size);
233 prtd->period_index = 0;
234 prtd->dma_req_idx = 0;
235 /* Fall-through */
236 case SNDRV_PCM_TRIGGER_RESUME:
237 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
238 spin_lock_irqsave(&prtd->lock, flags);
239 prtd->running = 1;
240 spin_unlock_irqrestore(&prtd->lock, flags);
241 tegra_pcm_queue_dma(prtd);
242 tegra_pcm_queue_dma(prtd);
243 break;
244 case SNDRV_PCM_TRIGGER_STOP:
245 case SNDRV_PCM_TRIGGER_SUSPEND:
246 case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
247 spin_lock_irqsave(&prtd->lock, flags);
248 prtd->running = 0;
249 spin_unlock_irqrestore(&prtd->lock, flags);
250 tegra_dma_dequeue_req(prtd->dma_chan, &prtd->dma_req[0]);
251 tegra_dma_dequeue_req(prtd->dma_chan, &prtd->dma_req[1]);
252 break;
253 default:
254 return -EINVAL;
255 }
256
257 return 0;
258}
259
260static snd_pcm_uframes_t tegra_pcm_pointer(struct snd_pcm_substream *substream)
261{
262 struct snd_pcm_runtime *runtime = substream->runtime;
263 struct tegra_runtime_data *prtd = runtime->private_data;
264
265 return prtd->period_index * runtime->period_size;
266}
267
268
269static int tegra_pcm_mmap(struct snd_pcm_substream *substream,
270 struct vm_area_struct *vma)
271{
272 struct snd_pcm_runtime *runtime = substream->runtime;
273
274 return dma_mmap_writecombine(substream->pcm->card->dev, vma,
275 runtime->dma_area,
276 runtime->dma_addr,
277 runtime->dma_bytes);
278}
279
280static struct snd_pcm_ops tegra_pcm_ops = {
281 .open = tegra_pcm_open,
282 .close = tegra_pcm_close,
283 .ioctl = snd_pcm_lib_ioctl,
284 .hw_params = tegra_pcm_hw_params,
285 .hw_free = tegra_pcm_hw_free,
286 .trigger = tegra_pcm_trigger,
287 .pointer = tegra_pcm_pointer,
288 .mmap = tegra_pcm_mmap,
289};
290
291static int tegra_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
292{
293 struct snd_pcm_substream *substream = pcm->streams[stream].substream;
294 struct snd_dma_buffer *buf = &substream->dma_buffer;
295 size_t size = tegra_pcm_hardware.buffer_bytes_max;
296
297 buf->area = dma_alloc_writecombine(pcm->card->dev, size,
298 &buf->addr, GFP_KERNEL);
299 if (!buf->area)
300 return -ENOMEM;
301
302 buf->dev.type = SNDRV_DMA_TYPE_DEV;
303 buf->dev.dev = pcm->card->dev;
304 buf->private_data = NULL;
305 buf->bytes = size;
306
307 return 0;
308}
309
310static void tegra_pcm_deallocate_dma_buffer(struct snd_pcm *pcm, int stream)
311{
312 struct snd_pcm_substream *substream = pcm->streams[stream].substream;
313 struct snd_dma_buffer *buf = &substream->dma_buffer;
314
315 if (!buf->area)
316 return;
317
318 dma_free_writecombine(pcm->card->dev, buf->bytes,
319 buf->area, buf->addr);
320 buf->area = NULL;
321}
322
323static u64 tegra_dma_mask = DMA_BIT_MASK(32);
324
325static int tegra_pcm_new(struct snd_card *card,
326 struct snd_soc_dai *dai, struct snd_pcm *pcm)
327{
328 int ret = 0;
329
330 if (!card->dev->dma_mask)
331 card->dev->dma_mask = &tegra_dma_mask;
332 if (!card->dev->coherent_dma_mask)
333 card->dev->coherent_dma_mask = 0xffffffff;
334
335 if (dai->driver->playback.channels_min) {
336 ret = tegra_pcm_preallocate_dma_buffer(pcm,
337 SNDRV_PCM_STREAM_PLAYBACK);
338 if (ret)
339 goto err;
340 }
341
342 if (dai->driver->capture.channels_min) {
343 ret = tegra_pcm_preallocate_dma_buffer(pcm,
344 SNDRV_PCM_STREAM_CAPTURE);
345 if (ret)
346 goto err_free_play;
347 }
348
349 return 0;
350
351err_free_play:
352 tegra_pcm_deallocate_dma_buffer(pcm, SNDRV_PCM_STREAM_PLAYBACK);
353err:
354 return ret;
355}
356
357static void tegra_pcm_free(struct snd_pcm *pcm)
358{
359 tegra_pcm_deallocate_dma_buffer(pcm, SNDRV_PCM_STREAM_CAPTURE);
360 tegra_pcm_deallocate_dma_buffer(pcm, SNDRV_PCM_STREAM_PLAYBACK);
361}
362
363struct snd_soc_platform_driver tegra_pcm_platform = {
364 .ops = &tegra_pcm_ops,
365 .pcm_new = tegra_pcm_new,
366 .pcm_free = tegra_pcm_free,
367};
368
369static int __devinit tegra_pcm_platform_probe(struct platform_device *pdev)
370{
371 return snd_soc_register_platform(&pdev->dev, &tegra_pcm_platform);
372}
373
374static int __devexit tegra_pcm_platform_remove(struct platform_device *pdev)
375{
376 snd_soc_unregister_platform(&pdev->dev);
377 return 0;
378}
379
380static struct platform_driver tegra_pcm_driver = {
381 .driver = {
382 .name = DRV_NAME,
383 .owner = THIS_MODULE,
384 },
385 .probe = tegra_pcm_platform_probe,
386 .remove = __devexit_p(tegra_pcm_platform_remove),
387};
388
389static int __init snd_tegra_pcm_init(void)
390{
391 return platform_driver_register(&tegra_pcm_driver);
392}
393module_init(snd_tegra_pcm_init);
394
395static void __exit snd_tegra_pcm_exit(void)
396{
397 platform_driver_unregister(&tegra_pcm_driver);
398}
399module_exit(snd_tegra_pcm_exit);
400
401MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>");
402MODULE_DESCRIPTION("Tegra PCM ASoC driver");
403MODULE_LICENSE("GPL");
404MODULE_ALIAS("platform:" DRV_NAME);
diff --git a/sound/soc/tegra/tegra_pcm.h b/sound/soc/tegra/tegra_pcm.h
new file mode 100644
index 000000000000..dbb90339fe0d
--- /dev/null
+++ b/sound/soc/tegra/tegra_pcm.h
@@ -0,0 +1,55 @@
1/*
2 * tegra_pcm.h - Definitions for Tegra PCM driver
3 *
4 * Author: Stephen Warren <swarren@nvidia.com>
5 * Copyright (C) 2010 - NVIDIA, Inc.
6 *
7 * Based on code copyright/by:
8 *
9 * Copyright (c) 2009-2010, 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#ifndef __TEGRA_PCM_H__
32#define __TEGRA_PCM_H__
33
34#include <mach/dma.h>
35
36struct tegra_pcm_dma_params {
37 unsigned long addr;
38 unsigned long wrap;
39 unsigned long width;
40 unsigned long req_sel;
41};
42
43struct tegra_runtime_data {
44 struct snd_pcm_substream *substream;
45 spinlock_t lock;
46 int running;
47 int dma_pos;
48 int dma_pos_end;
49 int period_index;
50 int dma_req_idx;
51 struct tegra_dma_req dma_req[2];
52 struct tegra_dma_channel *dma_chan;
53};
54
55#endif
diff --git a/sound/soc/tegra/tegra_wm8903.c b/sound/soc/tegra/tegra_wm8903.c
new file mode 100644
index 000000000000..0d6738a8b29a
--- /dev/null
+++ b/sound/soc/tegra/tegra_wm8903.c
@@ -0,0 +1,475 @@
1/*
2 * tegra_wm8903.c - Tegra machine ASoC driver for boards using WM8903 codec.
3 *
4 * Author: Stephen Warren <swarren@nvidia.com>
5 * Copyright (C) 2010-2011 - NVIDIA, Inc.
6 *
7 * Based on code copyright/by:
8 *
9 * (c) 2009, 2010 Nvidia Graphics Pvt. Ltd.
10 *
11 * Copyright 2007 Wolfson Microelectronics PLC.
12 * Author: Graeme Gregory
13 * graeme.gregory@wolfsonmicro.com or linux@wolfsonmicro.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 <asm/mach-types.h>
32
33#include <linux/module.h>
34#include <linux/platform_device.h>
35#include <linux/slab.h>
36#include <linux/gpio.h>
37
38#include <mach/tegra_wm8903_pdata.h>
39
40#include <sound/core.h>
41#include <sound/jack.h>
42#include <sound/pcm.h>
43#include <sound/pcm_params.h>
44#include <sound/soc.h>
45
46#include "../codecs/wm8903.h"
47
48#include "tegra_das.h"
49#include "tegra_i2s.h"
50#include "tegra_pcm.h"
51#include "tegra_asoc_utils.h"
52
53#define DRV_NAME "tegra-snd-wm8903"
54
55#define GPIO_SPKR_EN BIT(0)
56#define GPIO_HP_MUTE BIT(1)
57#define GPIO_INT_MIC_EN BIT(2)
58#define GPIO_EXT_MIC_EN BIT(3)
59
60struct tegra_wm8903 {
61 struct tegra_asoc_utils_data util_data;
62 struct tegra_wm8903_platform_data *pdata;
63 int gpio_requested;
64};
65
66static int tegra_wm8903_hw_params(struct snd_pcm_substream *substream,
67 struct snd_pcm_hw_params *params)
68{
69 struct snd_soc_pcm_runtime *rtd = substream->private_data;
70 struct snd_soc_dai *codec_dai = rtd->codec_dai;
71 struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
72 struct snd_soc_codec *codec = rtd->codec;
73 struct snd_soc_card *card = codec->card;
74 struct tegra_wm8903 *machine = snd_soc_card_get_drvdata(card);
75 int srate, mclk;
76 int err;
77
78 srate = params_rate(params);
79 switch (srate) {
80 case 64000:
81 case 88200:
82 case 96000:
83 mclk = 128 * srate;
84 break;
85 default:
86 mclk = 256 * srate;
87 break;
88 }
89 /* FIXME: Codec only requires >= 3MHz if OSR==0 */
90 while (mclk < 6000000)
91 mclk *= 2;
92
93 err = tegra_asoc_utils_set_rate(&machine->util_data, srate, mclk);
94 if (err < 0) {
95 dev_err(card->dev, "Can't configure clocks\n");
96 return err;
97 }
98
99 err = snd_soc_dai_set_fmt(codec_dai,
100 SND_SOC_DAIFMT_I2S |
101 SND_SOC_DAIFMT_NB_NF |
102 SND_SOC_DAIFMT_CBS_CFS);
103 if (err < 0) {
104 dev_err(card->dev, "codec_dai fmt not set\n");
105 return err;
106 }
107
108 err = snd_soc_dai_set_fmt(cpu_dai,
109 SND_SOC_DAIFMT_I2S |
110 SND_SOC_DAIFMT_NB_NF |
111 SND_SOC_DAIFMT_CBS_CFS);
112 if (err < 0) {
113 dev_err(card->dev, "cpu_dai fmt not set\n");
114 return err;
115 }
116
117 err = snd_soc_dai_set_sysclk(codec_dai, 0, mclk,
118 SND_SOC_CLOCK_IN);
119 if (err < 0) {
120 dev_err(card->dev, "codec_dai clock not set\n");
121 return err;
122 }
123
124 return 0;
125}
126
127static struct snd_soc_ops tegra_wm8903_ops = {
128 .hw_params = tegra_wm8903_hw_params,
129};
130
131static struct snd_soc_jack tegra_wm8903_hp_jack;
132
133static struct snd_soc_jack_pin tegra_wm8903_hp_jack_pins[] = {
134 {
135 .pin = "Headphone Jack",
136 .mask = SND_JACK_HEADPHONE,
137 },
138};
139
140static struct snd_soc_jack_gpio tegra_wm8903_hp_jack_gpio = {
141 .name = "headphone detect",
142 .report = SND_JACK_HEADPHONE,
143 .debounce_time = 150,
144 .invert = 1,
145};
146
147static struct snd_soc_jack tegra_wm8903_mic_jack;
148
149static struct snd_soc_jack_pin tegra_wm8903_mic_jack_pins[] = {
150 {
151 .pin = "Mic Jack",
152 .mask = SND_JACK_MICROPHONE,
153 },
154};
155
156static int tegra_wm8903_event_int_spk(struct snd_soc_dapm_widget *w,
157 struct snd_kcontrol *k, int event)
158{
159 struct snd_soc_dapm_context *dapm = w->dapm;
160 struct snd_soc_card *card = dapm->card;
161 struct tegra_wm8903 *machine = snd_soc_card_get_drvdata(card);
162 struct tegra_wm8903_platform_data *pdata = machine->pdata;
163
164 if (!(machine->gpio_requested & GPIO_SPKR_EN))
165 return 0;
166
167 gpio_set_value_cansleep(pdata->gpio_spkr_en,
168 SND_SOC_DAPM_EVENT_ON(event));
169
170 return 0;
171}
172
173static int tegra_wm8903_event_hp(struct snd_soc_dapm_widget *w,
174 struct snd_kcontrol *k, int event)
175{
176 struct snd_soc_dapm_context *dapm = w->dapm;
177 struct snd_soc_card *card = dapm->card;
178 struct tegra_wm8903 *machine = snd_soc_card_get_drvdata(card);
179 struct tegra_wm8903_platform_data *pdata = machine->pdata;
180
181 if (!(machine->gpio_requested & GPIO_HP_MUTE))
182 return 0;
183
184 gpio_set_value_cansleep(pdata->gpio_hp_mute,
185 !SND_SOC_DAPM_EVENT_ON(event));
186
187 return 0;
188}
189
190static const struct snd_soc_dapm_widget tegra_wm8903_dapm_widgets[] = {
191 SND_SOC_DAPM_SPK("Int Spk", tegra_wm8903_event_int_spk),
192 SND_SOC_DAPM_HP("Headphone Jack", tegra_wm8903_event_hp),
193 SND_SOC_DAPM_MIC("Mic Jack", NULL),
194};
195
196static const struct snd_soc_dapm_route harmony_audio_map[] = {
197 {"Headphone Jack", NULL, "HPOUTR"},
198 {"Headphone Jack", NULL, "HPOUTL"},
199 {"Int Spk", NULL, "ROP"},
200 {"Int Spk", NULL, "RON"},
201 {"Int Spk", NULL, "LOP"},
202 {"Int Spk", NULL, "LON"},
203 {"Mic Bias", NULL, "Mic Jack"},
204 {"IN1L", NULL, "Mic Bias"},
205};
206
207static const struct snd_soc_dapm_route seaboard_audio_map[] = {
208 {"Headphone Jack", NULL, "HPOUTR"},
209 {"Headphone Jack", NULL, "HPOUTL"},
210 {"Int Spk", NULL, "ROP"},
211 {"Int Spk", NULL, "RON"},
212 {"Int Spk", NULL, "LOP"},
213 {"Int Spk", NULL, "LON"},
214 {"Mic Bias", NULL, "Mic Jack"},
215 {"IN1R", NULL, "Mic Bias"},
216};
217
218static const struct snd_soc_dapm_route kaen_audio_map[] = {
219 {"Headphone Jack", NULL, "HPOUTR"},
220 {"Headphone Jack", NULL, "HPOUTL"},
221 {"Int Spk", NULL, "ROP"},
222 {"Int Spk", NULL, "RON"},
223 {"Int Spk", NULL, "LOP"},
224 {"Int Spk", NULL, "LON"},
225 {"Mic Bias", NULL, "Mic Jack"},
226 {"IN2R", NULL, "Mic Bias"},
227};
228
229static const struct snd_soc_dapm_route aebl_audio_map[] = {
230 {"Headphone Jack", NULL, "HPOUTR"},
231 {"Headphone Jack", NULL, "HPOUTL"},
232 {"Int Spk", NULL, "LINEOUTR"},
233 {"Int Spk", NULL, "LINEOUTL"},
234 {"Mic Bias", NULL, "Mic Jack"},
235 {"IN1R", NULL, "Mic Bias"},
236};
237
238static const struct snd_kcontrol_new tegra_wm8903_controls[] = {
239 SOC_DAPM_PIN_SWITCH("Int Spk"),
240};
241
242static int tegra_wm8903_init(struct snd_soc_pcm_runtime *rtd)
243{
244 struct snd_soc_codec *codec = rtd->codec;
245 struct snd_soc_dapm_context *dapm = &codec->dapm;
246 struct snd_soc_card *card = codec->card;
247 struct tegra_wm8903 *machine = snd_soc_card_get_drvdata(card);
248 struct tegra_wm8903_platform_data *pdata = machine->pdata;
249 int ret;
250
251 if (gpio_is_valid(pdata->gpio_spkr_en)) {
252 ret = gpio_request(pdata->gpio_spkr_en, "spkr_en");
253 if (ret) {
254 dev_err(card->dev, "cannot get spkr_en gpio\n");
255 return ret;
256 }
257 machine->gpio_requested |= GPIO_SPKR_EN;
258
259 gpio_direction_output(pdata->gpio_spkr_en, 0);
260 }
261
262 if (gpio_is_valid(pdata->gpio_hp_mute)) {
263 ret = gpio_request(pdata->gpio_hp_mute, "hp_mute");
264 if (ret) {
265 dev_err(card->dev, "cannot get hp_mute gpio\n");
266 return ret;
267 }
268 machine->gpio_requested |= GPIO_HP_MUTE;
269
270 gpio_direction_output(pdata->gpio_hp_mute, 0);
271 }
272
273 if (gpio_is_valid(pdata->gpio_int_mic_en)) {
274 ret = gpio_request(pdata->gpio_int_mic_en, "int_mic_en");
275 if (ret) {
276 dev_err(card->dev, "cannot get int_mic_en gpio\n");
277 return ret;
278 }
279 machine->gpio_requested |= GPIO_INT_MIC_EN;
280
281 /* Disable int mic; enable signal is active-high */
282 gpio_direction_output(pdata->gpio_int_mic_en, 0);
283 }
284
285 if (gpio_is_valid(pdata->gpio_ext_mic_en)) {
286 ret = gpio_request(pdata->gpio_ext_mic_en, "ext_mic_en");
287 if (ret) {
288 dev_err(card->dev, "cannot get ext_mic_en gpio\n");
289 return ret;
290 }
291 machine->gpio_requested |= GPIO_EXT_MIC_EN;
292
293 /* Enable ext mic; enable signal is active-low */
294 gpio_direction_output(pdata->gpio_ext_mic_en, 0);
295 }
296
297 if (gpio_is_valid(pdata->gpio_hp_det)) {
298 tegra_wm8903_hp_jack_gpio.gpio = pdata->gpio_hp_det;
299 snd_soc_jack_new(codec, "Headphone Jack", SND_JACK_HEADPHONE,
300 &tegra_wm8903_hp_jack);
301 snd_soc_jack_add_pins(&tegra_wm8903_hp_jack,
302 ARRAY_SIZE(tegra_wm8903_hp_jack_pins),
303 tegra_wm8903_hp_jack_pins);
304 snd_soc_jack_add_gpios(&tegra_wm8903_hp_jack,
305 1,
306 &tegra_wm8903_hp_jack_gpio);
307 }
308
309 snd_soc_jack_new(codec, "Mic Jack", SND_JACK_MICROPHONE,
310 &tegra_wm8903_mic_jack);
311 snd_soc_jack_add_pins(&tegra_wm8903_mic_jack,
312 ARRAY_SIZE(tegra_wm8903_mic_jack_pins),
313 tegra_wm8903_mic_jack_pins);
314 wm8903_mic_detect(codec, &tegra_wm8903_mic_jack, SND_JACK_MICROPHONE,
315 0);
316
317 snd_soc_dapm_force_enable_pin(dapm, "Mic Bias");
318
319 /* FIXME: Calculate automatically based on DAPM routes? */
320 if (!machine_is_harmony() && !machine_is_ventana())
321 snd_soc_dapm_nc_pin(dapm, "IN1L");
322 if (!machine_is_seaboard() && !machine_is_aebl())
323 snd_soc_dapm_nc_pin(dapm, "IN1R");
324 snd_soc_dapm_nc_pin(dapm, "IN2L");
325 if (!machine_is_kaen())
326 snd_soc_dapm_nc_pin(dapm, "IN2R");
327 snd_soc_dapm_nc_pin(dapm, "IN3L");
328 snd_soc_dapm_nc_pin(dapm, "IN3R");
329
330 if (machine_is_aebl()) {
331 snd_soc_dapm_nc_pin(dapm, "LON");
332 snd_soc_dapm_nc_pin(dapm, "RON");
333 snd_soc_dapm_nc_pin(dapm, "ROP");
334 snd_soc_dapm_nc_pin(dapm, "LOP");
335 } else {
336 snd_soc_dapm_nc_pin(dapm, "LINEOUTR");
337 snd_soc_dapm_nc_pin(dapm, "LINEOUTL");
338 }
339
340 snd_soc_dapm_sync(dapm);
341
342 return 0;
343}
344
345static struct snd_soc_dai_link tegra_wm8903_dai = {
346 .name = "WM8903",
347 .stream_name = "WM8903 PCM",
348 .codec_name = "wm8903.0-001a",
349 .platform_name = "tegra-pcm-audio",
350 .cpu_dai_name = "tegra-i2s.0",
351 .codec_dai_name = "wm8903-hifi",
352 .init = tegra_wm8903_init,
353 .ops = &tegra_wm8903_ops,
354};
355
356static struct snd_soc_card snd_soc_tegra_wm8903 = {
357 .name = "tegra-wm8903",
358 .dai_link = &tegra_wm8903_dai,
359 .num_links = 1,
360
361 .controls = tegra_wm8903_controls,
362 .num_controls = ARRAY_SIZE(tegra_wm8903_controls),
363 .dapm_widgets = tegra_wm8903_dapm_widgets,
364 .num_dapm_widgets = ARRAY_SIZE(tegra_wm8903_dapm_widgets),
365};
366
367static __devinit int tegra_wm8903_driver_probe(struct platform_device *pdev)
368{
369 struct snd_soc_card *card = &snd_soc_tegra_wm8903;
370 struct tegra_wm8903 *machine;
371 struct tegra_wm8903_platform_data *pdata;
372 int ret;
373
374 pdata = pdev->dev.platform_data;
375 if (!pdata) {
376 dev_err(&pdev->dev, "No platform data supplied\n");
377 return -EINVAL;
378 }
379
380 machine = kzalloc(sizeof(struct tegra_wm8903), GFP_KERNEL);
381 if (!machine) {
382 dev_err(&pdev->dev, "Can't allocate tegra_wm8903 struct\n");
383 return -ENOMEM;
384 }
385
386 machine->pdata = pdata;
387
388 ret = tegra_asoc_utils_init(&machine->util_data, &pdev->dev);
389 if (ret)
390 goto err_free_machine;
391
392 card->dev = &pdev->dev;
393 platform_set_drvdata(pdev, card);
394 snd_soc_card_set_drvdata(card, machine);
395
396 if (machine_is_harmony() || machine_is_ventana()) {
397 card->dapm_routes = harmony_audio_map;
398 card->num_dapm_routes = ARRAY_SIZE(harmony_audio_map);
399 } else if (machine_is_seaboard()) {
400 card->dapm_routes = seaboard_audio_map;
401 card->num_dapm_routes = ARRAY_SIZE(seaboard_audio_map);
402 } else if (machine_is_kaen()) {
403 card->dapm_routes = kaen_audio_map;
404 card->num_dapm_routes = ARRAY_SIZE(kaen_audio_map);
405 } else {
406 card->dapm_routes = aebl_audio_map;
407 card->num_dapm_routes = ARRAY_SIZE(aebl_audio_map);
408 }
409
410 ret = snd_soc_register_card(card);
411 if (ret) {
412 dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n",
413 ret);
414 goto err_fini_utils;
415 }
416
417 return 0;
418
419err_fini_utils:
420 tegra_asoc_utils_fini(&machine->util_data);
421err_free_machine:
422 kfree(machine);
423 return ret;
424}
425
426static int __devexit tegra_wm8903_driver_remove(struct platform_device *pdev)
427{
428 struct snd_soc_card *card = platform_get_drvdata(pdev);
429 struct tegra_wm8903 *machine = snd_soc_card_get_drvdata(card);
430 struct tegra_wm8903_platform_data *pdata = machine->pdata;
431
432 snd_soc_unregister_card(card);
433
434 tegra_asoc_utils_fini(&machine->util_data);
435
436 if (machine->gpio_requested & GPIO_EXT_MIC_EN)
437 gpio_free(pdata->gpio_ext_mic_en);
438 if (machine->gpio_requested & GPIO_INT_MIC_EN)
439 gpio_free(pdata->gpio_int_mic_en);
440 if (machine->gpio_requested & GPIO_HP_MUTE)
441 gpio_free(pdata->gpio_hp_mute);
442 if (machine->gpio_requested & GPIO_SPKR_EN)
443 gpio_free(pdata->gpio_spkr_en);
444
445 kfree(machine);
446
447 return 0;
448}
449
450static struct platform_driver tegra_wm8903_driver = {
451 .driver = {
452 .name = DRV_NAME,
453 .owner = THIS_MODULE,
454 .pm = &snd_soc_pm_ops,
455 },
456 .probe = tegra_wm8903_driver_probe,
457 .remove = __devexit_p(tegra_wm8903_driver_remove),
458};
459
460static int __init tegra_wm8903_modinit(void)
461{
462 return platform_driver_register(&tegra_wm8903_driver);
463}
464module_init(tegra_wm8903_modinit);
465
466static void __exit tegra_wm8903_modexit(void)
467{
468 platform_driver_unregister(&tegra_wm8903_driver);
469}
470module_exit(tegra_wm8903_modexit);
471
472MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>");
473MODULE_DESCRIPTION("Tegra+WM8903 machine ASoC driver");
474MODULE_LICENSE("GPL");
475MODULE_ALIAS("platform:" DRV_NAME);
diff --git a/sound/soc/tegra/trimslice.c b/sound/soc/tegra/trimslice.c
new file mode 100644
index 000000000000..8fc07e9adf2e
--- /dev/null
+++ b/sound/soc/tegra/trimslice.c
@@ -0,0 +1,228 @@
1/*
2 * trimslice.c - TrimSlice machine ASoC driver
3 *
4 * Copyright (C) 2011 - CompuLab, Ltd.
5 * Author: Mike Rapoport <mike@compulab.co.il>
6 *
7 * Based on code copyright/by:
8 * Author: Stephen Warren <swarren@nvidia.com>
9 * Copyright (C) 2010-2011 - NVIDIA, Inc.
10 *
11 * This program is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU General Public License
13 * version 2 as published by the Free Software Foundation.
14 *
15 * This program is distributed in the hope that it will be useful, but
16 * WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
23 * 02110-1301 USA
24 *
25 */
26
27#include <asm/mach-types.h>
28
29#include <linux/module.h>
30#include <linux/platform_device.h>
31#include <linux/slab.h>
32
33#include <sound/core.h>
34#include <sound/jack.h>
35#include <sound/pcm.h>
36#include <sound/pcm_params.h>
37#include <sound/soc.h>
38
39#include "../codecs/tlv320aic23.h"
40
41#include "tegra_das.h"
42#include "tegra_i2s.h"
43#include "tegra_pcm.h"
44#include "tegra_asoc_utils.h"
45
46#define DRV_NAME "tegra-snd-trimslice"
47
48struct tegra_trimslice {
49 struct tegra_asoc_utils_data util_data;
50};
51
52static int trimslice_asoc_hw_params(struct snd_pcm_substream *substream,
53 struct snd_pcm_hw_params *params)
54{
55 struct snd_soc_pcm_runtime *rtd = substream->private_data;
56 struct snd_soc_dai *codec_dai = rtd->codec_dai;
57 struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
58 struct snd_soc_codec *codec = rtd->codec;
59 struct snd_soc_card *card = codec->card;
60 struct tegra_trimslice *trimslice = snd_soc_card_get_drvdata(card);
61 int srate, mclk;
62 int err;
63
64 srate = params_rate(params);
65 mclk = 128 * srate;
66
67 err = tegra_asoc_utils_set_rate(&trimslice->util_data, srate, mclk);
68 if (err < 0) {
69 dev_err(card->dev, "Can't configure clocks\n");
70 return err;
71 }
72
73 err = snd_soc_dai_set_fmt(codec_dai,
74 SND_SOC_DAIFMT_I2S |
75 SND_SOC_DAIFMT_NB_NF |
76 SND_SOC_DAIFMT_CBS_CFS);
77 if (err < 0) {
78 dev_err(card->dev, "codec_dai fmt not set\n");
79 return err;
80 }
81
82 err = snd_soc_dai_set_fmt(cpu_dai,
83 SND_SOC_DAIFMT_I2S |
84 SND_SOC_DAIFMT_NB_NF |
85 SND_SOC_DAIFMT_CBS_CFS);
86 if (err < 0) {
87 dev_err(card->dev, "cpu_dai fmt not set\n");
88 return err;
89 }
90
91 err = snd_soc_dai_set_sysclk(codec_dai, 0, mclk,
92 SND_SOC_CLOCK_IN);
93 if (err < 0) {
94 dev_err(card->dev, "codec_dai clock not set\n");
95 return err;
96 }
97
98 return 0;
99}
100
101static struct snd_soc_ops trimslice_asoc_ops = {
102 .hw_params = trimslice_asoc_hw_params,
103};
104
105static const struct snd_soc_dapm_widget trimslice_dapm_widgets[] = {
106 SND_SOC_DAPM_HP("Line Out", NULL),
107 SND_SOC_DAPM_LINE("Line In", NULL),
108};
109
110static const struct snd_soc_dapm_route trimslice_audio_map[] = {
111 {"Line Out", NULL, "LOUT"},
112 {"Line Out", NULL, "ROUT"},
113
114 {"LLINEIN", NULL, "Line In"},
115 {"RLINEIN", NULL, "Line In"},
116};
117
118static int trimslice_asoc_init(struct snd_soc_pcm_runtime *rtd)
119{
120 struct snd_soc_codec *codec = rtd->codec;
121 struct snd_soc_dapm_context *dapm = &codec->dapm;
122
123 snd_soc_dapm_nc_pin(dapm, "LHPOUT");
124 snd_soc_dapm_nc_pin(dapm, "RHPOUT");
125 snd_soc_dapm_nc_pin(dapm, "MICIN");
126
127 snd_soc_dapm_sync(dapm);
128
129 return 0;
130}
131
132static struct snd_soc_dai_link trimslice_tlv320aic23_dai = {
133 .name = "TLV320AIC23",
134 .stream_name = "AIC23",
135 .codec_name = "tlv320aic23-codec.2-001a",
136 .platform_name = "tegra-pcm-audio",
137 .cpu_dai_name = "tegra-i2s.0",
138 .codec_dai_name = "tlv320aic23-hifi",
139 .init = trimslice_asoc_init,
140 .ops = &trimslice_asoc_ops,
141};
142
143static struct snd_soc_card snd_soc_trimslice = {
144 .name = "tegra-trimslice",
145 .dai_link = &trimslice_tlv320aic23_dai,
146 .num_links = 1,
147
148 .dapm_widgets = trimslice_dapm_widgets,
149 .num_dapm_widgets = ARRAY_SIZE(trimslice_dapm_widgets),
150 .dapm_routes = trimslice_audio_map,
151 .num_dapm_routes = ARRAY_SIZE(trimslice_audio_map),
152};
153
154static __devinit int tegra_snd_trimslice_probe(struct platform_device *pdev)
155{
156 struct snd_soc_card *card = &snd_soc_trimslice;
157 struct tegra_trimslice *trimslice;
158 int ret;
159
160 trimslice = kzalloc(sizeof(struct tegra_trimslice), GFP_KERNEL);
161 if (!trimslice) {
162 dev_err(&pdev->dev, "Can't allocate tegra_trimslice\n");
163 return -ENOMEM;
164 }
165
166 ret = tegra_asoc_utils_init(&trimslice->util_data, &pdev->dev);
167 if (ret)
168 goto err_free_trimslice;
169
170 card->dev = &pdev->dev;
171 platform_set_drvdata(pdev, card);
172 snd_soc_card_set_drvdata(card, trimslice);
173
174 ret = snd_soc_register_card(card);
175 if (ret) {
176 dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n",
177 ret);
178 goto err_fini_utils;
179 }
180
181 return 0;
182
183err_fini_utils:
184 tegra_asoc_utils_fini(&trimslice->util_data);
185err_free_trimslice:
186 kfree(trimslice);
187 return ret;
188}
189
190static int __devexit tegra_snd_trimslice_remove(struct platform_device *pdev)
191{
192 struct snd_soc_card *card = platform_get_drvdata(pdev);
193 struct tegra_trimslice *trimslice = snd_soc_card_get_drvdata(card);
194
195 snd_soc_unregister_card(card);
196
197 tegra_asoc_utils_fini(&trimslice->util_data);
198
199 kfree(trimslice);
200
201 return 0;
202}
203
204static struct platform_driver tegra_snd_trimslice_driver = {
205 .driver = {
206 .name = DRV_NAME,
207 .owner = THIS_MODULE,
208 },
209 .probe = tegra_snd_trimslice_probe,
210 .remove = __devexit_p(tegra_snd_trimslice_remove),
211};
212
213static int __init snd_tegra_trimslice_init(void)
214{
215 return platform_driver_register(&tegra_snd_trimslice_driver);
216}
217module_init(snd_tegra_trimslice_init);
218
219static void __exit snd_tegra_trimslice_exit(void)
220{
221 platform_driver_unregister(&tegra_snd_trimslice_driver);
222}
223module_exit(snd_tegra_trimslice_exit);
224
225MODULE_AUTHOR("Mike Rapoport <mike@compulab.co.il>");
226MODULE_DESCRIPTION("Trimslice machine ASoC driver");
227MODULE_LICENSE("GPL");
228MODULE_ALIAS("platform:" DRV_NAME);