diff options
Diffstat (limited to 'sound/soc/tegra')
-rw-r--r-- | sound/soc/tegra/tegra_das.c | 263 | ||||
-rw-r--r-- | sound/soc/tegra/tegra_das.h | 135 |
2 files changed, 398 insertions, 0 deletions
diff --git a/sound/soc/tegra/tegra_das.c b/sound/soc/tegra/tegra_das.c new file mode 100644 index 000000000000..796d36d5188a --- /dev/null +++ b/sound/soc/tegra/tegra_das.c | |||
@@ -0,0 +1,263 @@ | |||
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 | |||
36 | static struct tegra_das *das; | ||
37 | |||
38 | static inline void tegra_das_write(u32 reg, u32 val) | ||
39 | { | ||
40 | __raw_writel(val, das->regs + reg); | ||
41 | } | ||
42 | |||
43 | static inline u32 tegra_das_read(u32 reg) | ||
44 | { | ||
45 | return __raw_readl(das->regs + reg); | ||
46 | } | ||
47 | |||
48 | int 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 | } | ||
64 | EXPORT_SYMBOL_GPL(tegra_das_connect_dap_to_dac); | ||
65 | |||
66 | int 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 | } | ||
86 | EXPORT_SYMBOL_GPL(tegra_das_connect_dap_to_dap); | ||
87 | |||
88 | int 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 | } | ||
106 | EXPORT_SYMBOL_GPL(tegra_das_connect_dac_to_dap); | ||
107 | |||
108 | #ifdef CONFIG_DEBUG_FS | ||
109 | static 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 | |||
133 | static 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 | |||
138 | static 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 | |||
145 | static void tegra_das_debug_add(struct tegra_das *das) | ||
146 | { | ||
147 | das->debug = debugfs_create_file(DRV_NAME, S_IRUGO, asoc_debugfs_root, | ||
148 | das, &tegra_das_debug_fops); | ||
149 | } | ||
150 | |||
151 | static void tegra_das_debug_remove(struct tegra_das *das) | ||
152 | { | ||
153 | if (das->debug) | ||
154 | debugfs_remove(das->debug); | ||
155 | } | ||
156 | #else | ||
157 | static inline void tegra_das_debug_add(struct tegra_das *das) | ||
158 | { | ||
159 | } | ||
160 | |||
161 | static inline void tegra_das_debug_remove(struct tegra_das *das) | ||
162 | { | ||
163 | } | ||
164 | #endif | ||
165 | |||
166 | static int __devinit tegra_das_probe(struct platform_device *pdev) | ||
167 | { | ||
168 | struct resource *res, *region; | ||
169 | int ret = 0; | ||
170 | |||
171 | if (das) | ||
172 | return -ENODEV; | ||
173 | |||
174 | das = kzalloc(sizeof(struct tegra_das), GFP_KERNEL); | ||
175 | if (!das) { | ||
176 | dev_err(&pdev->dev, "Can't allocate tegra_das\n"); | ||
177 | ret = -ENOMEM; | ||
178 | goto exit; | ||
179 | } | ||
180 | das->dev = &pdev->dev; | ||
181 | |||
182 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
183 | if (!res) { | ||
184 | dev_err(&pdev->dev, "No memory resource\n"); | ||
185 | ret = -ENODEV; | ||
186 | goto err_free; | ||
187 | } | ||
188 | |||
189 | region = request_mem_region(res->start, resource_size(res), | ||
190 | pdev->name); | ||
191 | if (!region) { | ||
192 | dev_err(&pdev->dev, "Memory region already claimed\n"); | ||
193 | ret = -EBUSY; | ||
194 | goto err_free; | ||
195 | } | ||
196 | |||
197 | das->regs = ioremap(res->start, resource_size(res)); | ||
198 | if (!das->regs) { | ||
199 | dev_err(&pdev->dev, "ioremap failed\n"); | ||
200 | ret = -ENOMEM; | ||
201 | goto err_release; | ||
202 | } | ||
203 | |||
204 | tegra_das_debug_add(das); | ||
205 | |||
206 | platform_set_drvdata(pdev, das); | ||
207 | |||
208 | return 0; | ||
209 | |||
210 | err_release: | ||
211 | release_mem_region(res->start, resource_size(res)); | ||
212 | err_free: | ||
213 | kfree(das); | ||
214 | das = 0; | ||
215 | exit: | ||
216 | return ret; | ||
217 | } | ||
218 | |||
219 | static int __devexit tegra_das_remove(struct platform_device *pdev) | ||
220 | { | ||
221 | struct resource *res; | ||
222 | |||
223 | if (!das) | ||
224 | return -ENODEV; | ||
225 | |||
226 | platform_set_drvdata(pdev, NULL); | ||
227 | |||
228 | tegra_das_debug_remove(das); | ||
229 | |||
230 | iounmap(das->regs); | ||
231 | |||
232 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
233 | release_mem_region(res->start, resource_size(res)); | ||
234 | |||
235 | kfree(das); | ||
236 | das = 0; | ||
237 | |||
238 | return 0; | ||
239 | } | ||
240 | |||
241 | static struct platform_driver tegra_das_driver = { | ||
242 | .probe = tegra_das_probe, | ||
243 | .remove = __devexit_p(tegra_das_remove), | ||
244 | .driver = { | ||
245 | .name = DRV_NAME, | ||
246 | }, | ||
247 | }; | ||
248 | |||
249 | static int __init tegra_das_modinit(void) | ||
250 | { | ||
251 | return platform_driver_register(&tegra_das_driver); | ||
252 | } | ||
253 | module_init(tegra_das_modinit); | ||
254 | |||
255 | static void __exit tegra_das_modexit(void) | ||
256 | { | ||
257 | platform_driver_unregister(&tegra_das_driver); | ||
258 | } | ||
259 | module_exit(tegra_das_modexit); | ||
260 | |||
261 | MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>"); | ||
262 | MODULE_DESCRIPTION("Tegra DAS driver"); | ||
263 | MODULE_LICENSE("GPL"); | ||
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 | |||
86 | struct 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 | */ | ||
113 | extern 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 | */ | ||
123 | extern 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 | */ | ||
133 | extern int tegra_das_connect_dac_to_dap(int dac_id, int dap_sel); | ||
134 | |||
135 | #endif | ||