diff options
author | Stephen Warren <swarren@nvidia.com> | 2012-04-05 17:54:53 -0400 |
---|---|---|
committer | Mark Brown <broonie@opensource.wolfsonmicro.com> | 2012-04-06 09:17:45 -0400 |
commit | ef280d3907cea21b6093802398bbe4193e221a64 (patch) | |
tree | 77c78952531a4cdbc3732ccde794821976bc8cdb /sound/soc/tegra/tegra20_i2s.c | |
parent | 5fa87d34846e347b62bebf40edf51167e7ffb081 (diff) |
ASoC: tegra: rename Tegra20-specific driver files
Rename these files so they include a specific hardware version in their
filenames. The contents is only touched minimally so that git's rename
tracking operates correctly; renaming all symbols in the files results
in a diff so large that the rename detection fails.
Signed-off-by: Stephen Warren <swarren@nvidia.com>
Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
Diffstat (limited to 'sound/soc/tegra/tegra20_i2s.c')
-rw-r--r-- | sound/soc/tegra/tegra20_i2s.c | 458 |
1 files changed, 458 insertions, 0 deletions
diff --git a/sound/soc/tegra/tegra20_i2s.c b/sound/soc/tegra/tegra20_i2s.c new file mode 100644 index 000000000000..e24759ae291b --- /dev/null +++ b/sound/soc/tegra/tegra20_i2s.c | |||
@@ -0,0 +1,458 @@ | |||
1 | /* | ||
2 | * tegra20_i2s.c - Tegra20 I2S driver | ||
3 | * | ||
4 | * Author: Stephen Warren <swarren@nvidia.com> | ||
5 | * Copyright (C) 2010,2012 - 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 <linux/of.h> | ||
40 | #include <mach/iomap.h> | ||
41 | #include <sound/core.h> | ||
42 | #include <sound/pcm.h> | ||
43 | #include <sound/pcm_params.h> | ||
44 | #include <sound/soc.h> | ||
45 | |||
46 | #include "tegra20_i2s.h" | ||
47 | |||
48 | #define DRV_NAME "tegra-i2s" | ||
49 | |||
50 | static inline void tegra_i2s_write(struct tegra_i2s *i2s, u32 reg, u32 val) | ||
51 | { | ||
52 | __raw_writel(val, i2s->regs + reg); | ||
53 | } | ||
54 | |||
55 | static 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 | ||
61 | static 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 | |||
90 | static 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 | |||
95 | static 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 | |||
102 | static void tegra_i2s_debug_add(struct tegra_i2s *i2s) | ||
103 | { | ||
104 | i2s->debug = debugfs_create_file(i2s->dai.name, S_IRUGO, | ||
105 | snd_soc_debugfs_root, i2s, | ||
106 | &tegra_i2s_debug_fops); | ||
107 | } | ||
108 | |||
109 | static void tegra_i2s_debug_remove(struct tegra_i2s *i2s) | ||
110 | { | ||
111 | if (i2s->debug) | ||
112 | debugfs_remove(i2s->debug); | ||
113 | } | ||
114 | #else | ||
115 | static inline void tegra_i2s_debug_add(struct tegra_i2s *i2s, int id) | ||
116 | { | ||
117 | } | ||
118 | |||
119 | static inline void tegra_i2s_debug_remove(struct tegra_i2s *i2s) | ||
120 | { | ||
121 | } | ||
122 | #endif | ||
123 | |||
124 | static int tegra_i2s_set_fmt(struct snd_soc_dai *dai, | ||
125 | unsigned int fmt) | ||
126 | { | ||
127 | struct tegra_i2s *i2s = snd_soc_dai_get_drvdata(dai); | ||
128 | |||
129 | switch (fmt & SND_SOC_DAIFMT_INV_MASK) { | ||
130 | case SND_SOC_DAIFMT_NB_NF: | ||
131 | break; | ||
132 | default: | ||
133 | return -EINVAL; | ||
134 | } | ||
135 | |||
136 | i2s->reg_ctrl &= ~TEGRA_I2S_CTRL_MASTER_ENABLE; | ||
137 | switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { | ||
138 | case SND_SOC_DAIFMT_CBS_CFS: | ||
139 | i2s->reg_ctrl |= TEGRA_I2S_CTRL_MASTER_ENABLE; | ||
140 | break; | ||
141 | case SND_SOC_DAIFMT_CBM_CFM: | ||
142 | break; | ||
143 | default: | ||
144 | return -EINVAL; | ||
145 | } | ||
146 | |||
147 | i2s->reg_ctrl &= ~(TEGRA_I2S_CTRL_BIT_FORMAT_MASK | | ||
148 | TEGRA_I2S_CTRL_LRCK_MASK); | ||
149 | switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { | ||
150 | case SND_SOC_DAIFMT_DSP_A: | ||
151 | i2s->reg_ctrl |= TEGRA_I2S_CTRL_BIT_FORMAT_DSP; | ||
152 | i2s->reg_ctrl |= TEGRA_I2S_CTRL_LRCK_L_LOW; | ||
153 | break; | ||
154 | case SND_SOC_DAIFMT_DSP_B: | ||
155 | i2s->reg_ctrl |= TEGRA_I2S_CTRL_BIT_FORMAT_DSP; | ||
156 | i2s->reg_ctrl |= TEGRA_I2S_CTRL_LRCK_R_LOW; | ||
157 | break; | ||
158 | case SND_SOC_DAIFMT_I2S: | ||
159 | i2s->reg_ctrl |= TEGRA_I2S_CTRL_BIT_FORMAT_I2S; | ||
160 | i2s->reg_ctrl |= TEGRA_I2S_CTRL_LRCK_L_LOW; | ||
161 | break; | ||
162 | case SND_SOC_DAIFMT_RIGHT_J: | ||
163 | i2s->reg_ctrl |= TEGRA_I2S_CTRL_BIT_FORMAT_RJM; | ||
164 | i2s->reg_ctrl |= TEGRA_I2S_CTRL_LRCK_L_LOW; | ||
165 | break; | ||
166 | case SND_SOC_DAIFMT_LEFT_J: | ||
167 | i2s->reg_ctrl |= TEGRA_I2S_CTRL_BIT_FORMAT_LJM; | ||
168 | i2s->reg_ctrl |= TEGRA_I2S_CTRL_LRCK_L_LOW; | ||
169 | break; | ||
170 | default: | ||
171 | return -EINVAL; | ||
172 | } | ||
173 | |||
174 | return 0; | ||
175 | } | ||
176 | |||
177 | static int tegra_i2s_hw_params(struct snd_pcm_substream *substream, | ||
178 | struct snd_pcm_hw_params *params, | ||
179 | struct snd_soc_dai *dai) | ||
180 | { | ||
181 | struct device *dev = substream->pcm->card->dev; | ||
182 | struct tegra_i2s *i2s = snd_soc_dai_get_drvdata(dai); | ||
183 | u32 reg; | ||
184 | int ret, sample_size, srate, i2sclock, bitcnt; | ||
185 | |||
186 | i2s->reg_ctrl &= ~TEGRA_I2S_CTRL_BIT_SIZE_MASK; | ||
187 | switch (params_format(params)) { | ||
188 | case SNDRV_PCM_FORMAT_S16_LE: | ||
189 | i2s->reg_ctrl |= TEGRA_I2S_CTRL_BIT_SIZE_16; | ||
190 | sample_size = 16; | ||
191 | break; | ||
192 | case SNDRV_PCM_FORMAT_S24_LE: | ||
193 | i2s->reg_ctrl |= TEGRA_I2S_CTRL_BIT_SIZE_24; | ||
194 | sample_size = 24; | ||
195 | break; | ||
196 | case SNDRV_PCM_FORMAT_S32_LE: | ||
197 | i2s->reg_ctrl |= TEGRA_I2S_CTRL_BIT_SIZE_32; | ||
198 | sample_size = 32; | ||
199 | break; | ||
200 | default: | ||
201 | return -EINVAL; | ||
202 | } | ||
203 | |||
204 | srate = params_rate(params); | ||
205 | |||
206 | /* Final "* 2" required by Tegra hardware */ | ||
207 | i2sclock = srate * params_channels(params) * sample_size * 2; | ||
208 | |||
209 | ret = clk_set_rate(i2s->clk_i2s, i2sclock); | ||
210 | if (ret) { | ||
211 | dev_err(dev, "Can't set I2S clock rate: %d\n", ret); | ||
212 | return ret; | ||
213 | } | ||
214 | |||
215 | bitcnt = (i2sclock / (2 * srate)) - 1; | ||
216 | if (bitcnt < 0 || bitcnt > TEGRA_I2S_TIMING_CHANNEL_BIT_COUNT_MASK_US) | ||
217 | return -EINVAL; | ||
218 | reg = bitcnt << TEGRA_I2S_TIMING_CHANNEL_BIT_COUNT_SHIFT; | ||
219 | |||
220 | if (i2sclock % (2 * srate)) | ||
221 | reg |= TEGRA_I2S_TIMING_NON_SYM_ENABLE; | ||
222 | |||
223 | clk_enable(i2s->clk_i2s); | ||
224 | |||
225 | tegra_i2s_write(i2s, TEGRA_I2S_TIMING, reg); | ||
226 | |||
227 | tegra_i2s_write(i2s, TEGRA_I2S_FIFO_SCR, | ||
228 | TEGRA_I2S_FIFO_SCR_FIFO2_ATN_LVL_FOUR_SLOTS | | ||
229 | TEGRA_I2S_FIFO_SCR_FIFO1_ATN_LVL_FOUR_SLOTS); | ||
230 | |||
231 | clk_disable(i2s->clk_i2s); | ||
232 | |||
233 | return 0; | ||
234 | } | ||
235 | |||
236 | static void tegra_i2s_start_playback(struct tegra_i2s *i2s) | ||
237 | { | ||
238 | i2s->reg_ctrl |= TEGRA_I2S_CTRL_FIFO1_ENABLE; | ||
239 | tegra_i2s_write(i2s, TEGRA_I2S_CTRL, i2s->reg_ctrl); | ||
240 | } | ||
241 | |||
242 | static void tegra_i2s_stop_playback(struct tegra_i2s *i2s) | ||
243 | { | ||
244 | i2s->reg_ctrl &= ~TEGRA_I2S_CTRL_FIFO1_ENABLE; | ||
245 | tegra_i2s_write(i2s, TEGRA_I2S_CTRL, i2s->reg_ctrl); | ||
246 | } | ||
247 | |||
248 | static void tegra_i2s_start_capture(struct tegra_i2s *i2s) | ||
249 | { | ||
250 | i2s->reg_ctrl |= TEGRA_I2S_CTRL_FIFO2_ENABLE; | ||
251 | tegra_i2s_write(i2s, TEGRA_I2S_CTRL, i2s->reg_ctrl); | ||
252 | } | ||
253 | |||
254 | static void tegra_i2s_stop_capture(struct tegra_i2s *i2s) | ||
255 | { | ||
256 | i2s->reg_ctrl &= ~TEGRA_I2S_CTRL_FIFO2_ENABLE; | ||
257 | tegra_i2s_write(i2s, TEGRA_I2S_CTRL, i2s->reg_ctrl); | ||
258 | } | ||
259 | |||
260 | static int tegra_i2s_trigger(struct snd_pcm_substream *substream, int cmd, | ||
261 | struct snd_soc_dai *dai) | ||
262 | { | ||
263 | struct tegra_i2s *i2s = snd_soc_dai_get_drvdata(dai); | ||
264 | |||
265 | switch (cmd) { | ||
266 | case SNDRV_PCM_TRIGGER_START: | ||
267 | case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: | ||
268 | case SNDRV_PCM_TRIGGER_RESUME: | ||
269 | clk_enable(i2s->clk_i2s); | ||
270 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) | ||
271 | tegra_i2s_start_playback(i2s); | ||
272 | else | ||
273 | tegra_i2s_start_capture(i2s); | ||
274 | break; | ||
275 | case SNDRV_PCM_TRIGGER_STOP: | ||
276 | case SNDRV_PCM_TRIGGER_PAUSE_PUSH: | ||
277 | case SNDRV_PCM_TRIGGER_SUSPEND: | ||
278 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) | ||
279 | tegra_i2s_stop_playback(i2s); | ||
280 | else | ||
281 | tegra_i2s_stop_capture(i2s); | ||
282 | clk_disable(i2s->clk_i2s); | ||
283 | break; | ||
284 | default: | ||
285 | return -EINVAL; | ||
286 | } | ||
287 | |||
288 | return 0; | ||
289 | } | ||
290 | |||
291 | static int tegra_i2s_probe(struct snd_soc_dai *dai) | ||
292 | { | ||
293 | struct tegra_i2s *i2s = snd_soc_dai_get_drvdata(dai); | ||
294 | |||
295 | dai->capture_dma_data = &i2s->capture_dma_data; | ||
296 | dai->playback_dma_data = &i2s->playback_dma_data; | ||
297 | |||
298 | return 0; | ||
299 | } | ||
300 | |||
301 | static const struct snd_soc_dai_ops tegra_i2s_dai_ops = { | ||
302 | .set_fmt = tegra_i2s_set_fmt, | ||
303 | .hw_params = tegra_i2s_hw_params, | ||
304 | .trigger = tegra_i2s_trigger, | ||
305 | }; | ||
306 | |||
307 | static const struct snd_soc_dai_driver tegra_i2s_dai_template = { | ||
308 | .probe = tegra_i2s_probe, | ||
309 | .playback = { | ||
310 | .channels_min = 2, | ||
311 | .channels_max = 2, | ||
312 | .rates = SNDRV_PCM_RATE_8000_96000, | ||
313 | .formats = SNDRV_PCM_FMTBIT_S16_LE, | ||
314 | }, | ||
315 | .capture = { | ||
316 | .channels_min = 2, | ||
317 | .channels_max = 2, | ||
318 | .rates = SNDRV_PCM_RATE_8000_96000, | ||
319 | .formats = SNDRV_PCM_FMTBIT_S16_LE, | ||
320 | }, | ||
321 | .ops = &tegra_i2s_dai_ops, | ||
322 | .symmetric_rates = 1, | ||
323 | }; | ||
324 | |||
325 | static __devinit int tegra_i2s_platform_probe(struct platform_device *pdev) | ||
326 | { | ||
327 | struct tegra_i2s *i2s; | ||
328 | struct resource *mem, *memregion, *dmareq; | ||
329 | u32 of_dma[2]; | ||
330 | u32 dma_ch; | ||
331 | int ret; | ||
332 | |||
333 | i2s = devm_kzalloc(&pdev->dev, sizeof(struct tegra_i2s), GFP_KERNEL); | ||
334 | if (!i2s) { | ||
335 | dev_err(&pdev->dev, "Can't allocate tegra_i2s\n"); | ||
336 | ret = -ENOMEM; | ||
337 | goto err; | ||
338 | } | ||
339 | dev_set_drvdata(&pdev->dev, i2s); | ||
340 | |||
341 | i2s->dai = tegra_i2s_dai_template; | ||
342 | i2s->dai.name = dev_name(&pdev->dev); | ||
343 | |||
344 | i2s->clk_i2s = clk_get(&pdev->dev, NULL); | ||
345 | if (IS_ERR(i2s->clk_i2s)) { | ||
346 | dev_err(&pdev->dev, "Can't retrieve i2s clock\n"); | ||
347 | ret = PTR_ERR(i2s->clk_i2s); | ||
348 | goto err; | ||
349 | } | ||
350 | |||
351 | mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
352 | if (!mem) { | ||
353 | dev_err(&pdev->dev, "No memory resource\n"); | ||
354 | ret = -ENODEV; | ||
355 | goto err_clk_put; | ||
356 | } | ||
357 | |||
358 | dmareq = platform_get_resource(pdev, IORESOURCE_DMA, 0); | ||
359 | if (!dmareq) { | ||
360 | if (of_property_read_u32_array(pdev->dev.of_node, | ||
361 | "nvidia,dma-request-selector", | ||
362 | of_dma, 2) < 0) { | ||
363 | dev_err(&pdev->dev, "No DMA resource\n"); | ||
364 | ret = -ENODEV; | ||
365 | goto err_clk_put; | ||
366 | } | ||
367 | dma_ch = of_dma[1]; | ||
368 | } else { | ||
369 | dma_ch = dmareq->start; | ||
370 | } | ||
371 | |||
372 | memregion = devm_request_mem_region(&pdev->dev, mem->start, | ||
373 | resource_size(mem), DRV_NAME); | ||
374 | if (!memregion) { | ||
375 | dev_err(&pdev->dev, "Memory region already claimed\n"); | ||
376 | ret = -EBUSY; | ||
377 | goto err_clk_put; | ||
378 | } | ||
379 | |||
380 | i2s->regs = devm_ioremap(&pdev->dev, mem->start, resource_size(mem)); | ||
381 | if (!i2s->regs) { | ||
382 | dev_err(&pdev->dev, "ioremap failed\n"); | ||
383 | ret = -ENOMEM; | ||
384 | goto err_clk_put; | ||
385 | } | ||
386 | |||
387 | i2s->capture_dma_data.addr = mem->start + TEGRA_I2S_FIFO2; | ||
388 | i2s->capture_dma_data.wrap = 4; | ||
389 | i2s->capture_dma_data.width = 32; | ||
390 | i2s->capture_dma_data.req_sel = dma_ch; | ||
391 | |||
392 | i2s->playback_dma_data.addr = mem->start + TEGRA_I2S_FIFO1; | ||
393 | i2s->playback_dma_data.wrap = 4; | ||
394 | i2s->playback_dma_data.width = 32; | ||
395 | i2s->playback_dma_data.req_sel = dma_ch; | ||
396 | |||
397 | i2s->reg_ctrl = TEGRA_I2S_CTRL_FIFO_FORMAT_PACKED; | ||
398 | |||
399 | ret = snd_soc_register_dai(&pdev->dev, &i2s->dai); | ||
400 | if (ret) { | ||
401 | dev_err(&pdev->dev, "Could not register DAI: %d\n", ret); | ||
402 | ret = -ENOMEM; | ||
403 | goto err_clk_put; | ||
404 | } | ||
405 | |||
406 | ret = tegra_pcm_platform_register(&pdev->dev); | ||
407 | if (ret) { | ||
408 | dev_err(&pdev->dev, "Could not register PCM: %d\n", ret); | ||
409 | goto err_unregister_dai; | ||
410 | } | ||
411 | |||
412 | tegra_i2s_debug_add(i2s); | ||
413 | |||
414 | return 0; | ||
415 | |||
416 | err_unregister_dai: | ||
417 | snd_soc_unregister_dai(&pdev->dev); | ||
418 | err_clk_put: | ||
419 | clk_put(i2s->clk_i2s); | ||
420 | err: | ||
421 | return ret; | ||
422 | } | ||
423 | |||
424 | static int __devexit tegra_i2s_platform_remove(struct platform_device *pdev) | ||
425 | { | ||
426 | struct tegra_i2s *i2s = dev_get_drvdata(&pdev->dev); | ||
427 | |||
428 | tegra_pcm_platform_unregister(&pdev->dev); | ||
429 | snd_soc_unregister_dai(&pdev->dev); | ||
430 | |||
431 | tegra_i2s_debug_remove(i2s); | ||
432 | |||
433 | clk_put(i2s->clk_i2s); | ||
434 | |||
435 | return 0; | ||
436 | } | ||
437 | |||
438 | static const struct of_device_id tegra_i2s_of_match[] __devinitconst = { | ||
439 | { .compatible = "nvidia,tegra20-i2s", }, | ||
440 | {}, | ||
441 | }; | ||
442 | |||
443 | static struct platform_driver tegra_i2s_driver = { | ||
444 | .driver = { | ||
445 | .name = DRV_NAME, | ||
446 | .owner = THIS_MODULE, | ||
447 | .of_match_table = tegra_i2s_of_match, | ||
448 | }, | ||
449 | .probe = tegra_i2s_platform_probe, | ||
450 | .remove = __devexit_p(tegra_i2s_platform_remove), | ||
451 | }; | ||
452 | module_platform_driver(tegra_i2s_driver); | ||
453 | |||
454 | MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>"); | ||
455 | MODULE_DESCRIPTION("Tegra I2S ASoC driver"); | ||
456 | MODULE_LICENSE("GPL"); | ||
457 | MODULE_ALIAS("platform:" DRV_NAME); | ||
458 | MODULE_DEVICE_TABLE(of, tegra_i2s_of_match); | ||