aboutsummaryrefslogtreecommitdiffstats
path: root/sound/soc/ep93xx/ep93xx-i2s.c
diff options
context:
space:
mode:
authorAlexander Shiyan <shc_work@mail.ru>2012-08-21 11:21:51 -0400
committerMark Brown <broonie@opensource.wolfsonmicro.com>2012-08-28 13:24:21 -0400
commit0ed275eff31029c39355828cb48c46c0a006e2f8 (patch)
tree67130c274e9d90b717079ca4f50bd3ed55b4ae09 /sound/soc/ep93xx/ep93xx-i2s.c
parent3d721a34e66e8133404bda6852897b818df69b4c (diff)
ASoC: Rename ep93xx soc directory to cirrus
This patch is to rename the directory "ep93xx" in "cirrus". Name more accurately reflects the manufacturer and allows to add drivers not only for architecture ep93xx in this directory. Patch not contain any functional changes. Signed-off-by: Alexander Shiyan <shc_work@mail.ru> Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
Diffstat (limited to 'sound/soc/ep93xx/ep93xx-i2s.c')
-rw-r--r--sound/soc/ep93xx/ep93xx-i2s.c451
1 files changed, 0 insertions, 451 deletions
diff --git a/sound/soc/ep93xx/ep93xx-i2s.c b/sound/soc/ep93xx/ep93xx-i2s.c
deleted file mode 100644
index 8df8f6dc474f..000000000000
--- a/sound/soc/ep93xx/ep93xx-i2s.c
+++ /dev/null
@@ -1,451 +0,0 @@
1/*
2 * linux/sound/soc/ep93xx-i2s.c
3 * EP93xx I2S driver
4 *
5 * Copyright (C) 2010 Ryan Mallon
6 *
7 * Based on the original driver by:
8 * Copyright (C) 2007 Chase Douglas <chasedouglas@gmail>
9 * Copyright (C) 2006 Lennert Buytenhek <buytenh@wantstofly.org>
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License version 2 as
13 * published by the Free Software Foundation.
14 *
15 */
16
17#include <linux/module.h>
18#include <linux/init.h>
19#include <linux/slab.h>
20#include <linux/clk.h>
21#include <linux/io.h>
22
23#include <sound/core.h>
24#include <sound/pcm.h>
25#include <sound/pcm_params.h>
26#include <sound/initval.h>
27#include <sound/soc.h>
28
29#include <mach/hardware.h>
30#include <mach/ep93xx-regs.h>
31#include <mach/dma.h>
32
33#include "ep93xx-pcm.h"
34
35#define EP93XX_I2S_TXCLKCFG 0x00
36#define EP93XX_I2S_RXCLKCFG 0x04
37#define EP93XX_I2S_GLCTRL 0x0C
38
39#define EP93XX_I2S_TXLINCTRLDATA 0x28
40#define EP93XX_I2S_TXCTRL 0x2C
41#define EP93XX_I2S_TXWRDLEN 0x30
42#define EP93XX_I2S_TX0EN 0x34
43
44#define EP93XX_I2S_RXLINCTRLDATA 0x58
45#define EP93XX_I2S_RXCTRL 0x5C
46#define EP93XX_I2S_RXWRDLEN 0x60
47#define EP93XX_I2S_RX0EN 0x64
48
49#define EP93XX_I2S_WRDLEN_16 (0 << 0)
50#define EP93XX_I2S_WRDLEN_24 (1 << 0)
51#define EP93XX_I2S_WRDLEN_32 (2 << 0)
52
53#define EP93XX_I2S_LINCTRLDATA_R_JUST (1 << 2) /* Right justify */
54
55#define EP93XX_I2S_CLKCFG_LRS (1 << 0) /* lrclk polarity */
56#define EP93XX_I2S_CLKCFG_CKP (1 << 1) /* Bit clock polarity */
57#define EP93XX_I2S_CLKCFG_REL (1 << 2) /* First bit transition */
58#define EP93XX_I2S_CLKCFG_MASTER (1 << 3) /* Master mode */
59#define EP93XX_I2S_CLKCFG_NBCG (1 << 4) /* Not bit clock gating */
60
61struct ep93xx_i2s_info {
62 struct clk *mclk;
63 struct clk *sclk;
64 struct clk *lrclk;
65 struct ep93xx_pcm_dma_params *dma_params;
66 void __iomem *regs;
67};
68
69struct ep93xx_pcm_dma_params ep93xx_i2s_dma_params[] = {
70 [SNDRV_PCM_STREAM_PLAYBACK] = {
71 .name = "i2s-pcm-out",
72 .dma_port = EP93XX_DMA_I2S1,
73 },
74 [SNDRV_PCM_STREAM_CAPTURE] = {
75 .name = "i2s-pcm-in",
76 .dma_port = EP93XX_DMA_I2S1,
77 },
78};
79
80static inline void ep93xx_i2s_write_reg(struct ep93xx_i2s_info *info,
81 unsigned reg, unsigned val)
82{
83 __raw_writel(val, info->regs + reg);
84}
85
86static inline unsigned ep93xx_i2s_read_reg(struct ep93xx_i2s_info *info,
87 unsigned reg)
88{
89 return __raw_readl(info->regs + reg);
90}
91
92static void ep93xx_i2s_enable(struct ep93xx_i2s_info *info, int stream)
93{
94 unsigned base_reg;
95 int i;
96
97 if ((ep93xx_i2s_read_reg(info, EP93XX_I2S_TX0EN) & 0x1) == 0 &&
98 (ep93xx_i2s_read_reg(info, EP93XX_I2S_RX0EN) & 0x1) == 0) {
99 /* Enable clocks */
100 clk_enable(info->mclk);
101 clk_enable(info->sclk);
102 clk_enable(info->lrclk);
103
104 /* Enable i2s */
105 ep93xx_i2s_write_reg(info, EP93XX_I2S_GLCTRL, 1);
106 }
107
108 /* Enable fifos */
109 if (stream == SNDRV_PCM_STREAM_PLAYBACK)
110 base_reg = EP93XX_I2S_TX0EN;
111 else
112 base_reg = EP93XX_I2S_RX0EN;
113 for (i = 0; i < 3; i++)
114 ep93xx_i2s_write_reg(info, base_reg + (i * 4), 1);
115}
116
117static void ep93xx_i2s_disable(struct ep93xx_i2s_info *info, int stream)
118{
119 unsigned base_reg;
120 int i;
121
122 /* Disable fifos */
123 if (stream == SNDRV_PCM_STREAM_PLAYBACK)
124 base_reg = EP93XX_I2S_TX0EN;
125 else
126 base_reg = EP93XX_I2S_RX0EN;
127 for (i = 0; i < 3; i++)
128 ep93xx_i2s_write_reg(info, base_reg + (i * 4), 0);
129
130 if ((ep93xx_i2s_read_reg(info, EP93XX_I2S_TX0EN) & 0x1) == 0 &&
131 (ep93xx_i2s_read_reg(info, EP93XX_I2S_RX0EN) & 0x1) == 0) {
132 /* Disable i2s */
133 ep93xx_i2s_write_reg(info, EP93XX_I2S_GLCTRL, 0);
134
135 /* Disable clocks */
136 clk_disable(info->lrclk);
137 clk_disable(info->sclk);
138 clk_disable(info->mclk);
139 }
140}
141
142static int ep93xx_i2s_startup(struct snd_pcm_substream *substream,
143 struct snd_soc_dai *dai)
144{
145 struct snd_soc_pcm_runtime *rtd = substream->private_data;
146 struct ep93xx_i2s_info *info = snd_soc_dai_get_drvdata(dai);
147 struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
148
149 snd_soc_dai_set_dma_data(cpu_dai, substream,
150 &info->dma_params[substream->stream]);
151 return 0;
152}
153
154static void ep93xx_i2s_shutdown(struct snd_pcm_substream *substream,
155 struct snd_soc_dai *dai)
156{
157 struct ep93xx_i2s_info *info = snd_soc_dai_get_drvdata(dai);
158
159 ep93xx_i2s_disable(info, substream->stream);
160}
161
162static int ep93xx_i2s_set_dai_fmt(struct snd_soc_dai *cpu_dai,
163 unsigned int fmt)
164{
165 struct ep93xx_i2s_info *info = snd_soc_dai_get_drvdata(cpu_dai);
166 unsigned int clk_cfg, lin_ctrl;
167
168 clk_cfg = ep93xx_i2s_read_reg(info, EP93XX_I2S_RXCLKCFG);
169 lin_ctrl = ep93xx_i2s_read_reg(info, EP93XX_I2S_RXLINCTRLDATA);
170
171 switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
172 case SND_SOC_DAIFMT_I2S:
173 clk_cfg |= EP93XX_I2S_CLKCFG_REL;
174 lin_ctrl &= ~EP93XX_I2S_LINCTRLDATA_R_JUST;
175 break;
176
177 case SND_SOC_DAIFMT_LEFT_J:
178 clk_cfg &= ~EP93XX_I2S_CLKCFG_REL;
179 lin_ctrl &= ~EP93XX_I2S_LINCTRLDATA_R_JUST;
180 break;
181
182 case SND_SOC_DAIFMT_RIGHT_J:
183 clk_cfg &= ~EP93XX_I2S_CLKCFG_REL;
184 lin_ctrl |= EP93XX_I2S_LINCTRLDATA_R_JUST;
185 break;
186
187 default:
188 return -EINVAL;
189 }
190
191 switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
192 case SND_SOC_DAIFMT_CBS_CFS:
193 /* CPU is master */
194 clk_cfg |= EP93XX_I2S_CLKCFG_MASTER;
195 break;
196
197 case SND_SOC_DAIFMT_CBM_CFM:
198 /* Codec is master */
199 clk_cfg &= ~EP93XX_I2S_CLKCFG_MASTER;
200 break;
201
202 default:
203 return -EINVAL;
204 }
205
206 switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
207 case SND_SOC_DAIFMT_NB_NF:
208 /* Negative bit clock, lrclk low on left word */
209 clk_cfg &= ~(EP93XX_I2S_CLKCFG_CKP | EP93XX_I2S_CLKCFG_REL);
210 break;
211
212 case SND_SOC_DAIFMT_NB_IF:
213 /* Negative bit clock, lrclk low on right word */
214 clk_cfg &= ~EP93XX_I2S_CLKCFG_CKP;
215 clk_cfg |= EP93XX_I2S_CLKCFG_REL;
216 break;
217
218 case SND_SOC_DAIFMT_IB_NF:
219 /* Positive bit clock, lrclk low on left word */
220 clk_cfg |= EP93XX_I2S_CLKCFG_CKP;
221 clk_cfg &= ~EP93XX_I2S_CLKCFG_REL;
222 break;
223
224 case SND_SOC_DAIFMT_IB_IF:
225 /* Positive bit clock, lrclk low on right word */
226 clk_cfg |= EP93XX_I2S_CLKCFG_CKP | EP93XX_I2S_CLKCFG_REL;
227 break;
228 }
229
230 /* Write new register values */
231 ep93xx_i2s_write_reg(info, EP93XX_I2S_RXCLKCFG, clk_cfg);
232 ep93xx_i2s_write_reg(info, EP93XX_I2S_TXCLKCFG, clk_cfg);
233 ep93xx_i2s_write_reg(info, EP93XX_I2S_RXLINCTRLDATA, lin_ctrl);
234 ep93xx_i2s_write_reg(info, EP93XX_I2S_TXLINCTRLDATA, lin_ctrl);
235 return 0;
236}
237
238static int ep93xx_i2s_hw_params(struct snd_pcm_substream *substream,
239 struct snd_pcm_hw_params *params,
240 struct snd_soc_dai *dai)
241{
242 struct ep93xx_i2s_info *info = snd_soc_dai_get_drvdata(dai);
243 unsigned word_len, div, sdiv, lrdiv;
244 int err;
245
246 switch (params_format(params)) {
247 case SNDRV_PCM_FORMAT_S16_LE:
248 word_len = EP93XX_I2S_WRDLEN_16;
249 break;
250
251 case SNDRV_PCM_FORMAT_S24_LE:
252 word_len = EP93XX_I2S_WRDLEN_24;
253 break;
254
255 case SNDRV_PCM_FORMAT_S32_LE:
256 word_len = EP93XX_I2S_WRDLEN_32;
257 break;
258
259 default:
260 return -EINVAL;
261 }
262
263 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
264 ep93xx_i2s_write_reg(info, EP93XX_I2S_TXWRDLEN, word_len);
265 else
266 ep93xx_i2s_write_reg(info, EP93XX_I2S_RXWRDLEN, word_len);
267
268 /*
269 * EP93xx I2S module can be setup so SCLK / LRCLK value can be
270 * 32, 64, 128. MCLK / SCLK value can be 2 and 4.
271 * We set LRCLK equal to `rate' and minimum SCLK / LRCLK
272 * value is 64, because our sample size is 32 bit * 2 channels.
273 * I2S standard permits us to transmit more bits than
274 * the codec uses.
275 */
276 div = clk_get_rate(info->mclk) / params_rate(params);
277 sdiv = 4;
278 if (div > (256 + 512) / 2) {
279 lrdiv = 128;
280 } else {
281 lrdiv = 64;
282 if (div < (128 + 256) / 2)
283 sdiv = 2;
284 }
285
286 err = clk_set_rate(info->sclk, clk_get_rate(info->mclk) / sdiv);
287 if (err)
288 return err;
289
290 err = clk_set_rate(info->lrclk, clk_get_rate(info->sclk) / lrdiv);
291 if (err)
292 return err;
293
294 ep93xx_i2s_enable(info, substream->stream);
295 return 0;
296}
297
298static int ep93xx_i2s_set_sysclk(struct snd_soc_dai *cpu_dai, int clk_id,
299 unsigned int freq, int dir)
300{
301 struct ep93xx_i2s_info *info = snd_soc_dai_get_drvdata(cpu_dai);
302
303 if (dir == SND_SOC_CLOCK_IN || clk_id != 0)
304 return -EINVAL;
305
306 return clk_set_rate(info->mclk, freq);
307}
308
309#ifdef CONFIG_PM
310static int ep93xx_i2s_suspend(struct snd_soc_dai *dai)
311{
312 struct ep93xx_i2s_info *info = snd_soc_dai_get_drvdata(dai);
313
314 if (!dai->active)
315 return 0;
316
317 ep93xx_i2s_disable(info, SNDRV_PCM_STREAM_PLAYBACK);
318 ep93xx_i2s_disable(info, SNDRV_PCM_STREAM_CAPTURE);
319
320 return 0;
321}
322
323static int ep93xx_i2s_resume(struct snd_soc_dai *dai)
324{
325 struct ep93xx_i2s_info *info = snd_soc_dai_get_drvdata(dai);
326
327 if (!dai->active)
328 return 0;
329
330 ep93xx_i2s_enable(info, SNDRV_PCM_STREAM_PLAYBACK);
331 ep93xx_i2s_enable(info, SNDRV_PCM_STREAM_CAPTURE);
332
333 return 0;
334}
335#else
336#define ep93xx_i2s_suspend NULL
337#define ep93xx_i2s_resume NULL
338#endif
339
340static const struct snd_soc_dai_ops ep93xx_i2s_dai_ops = {
341 .startup = ep93xx_i2s_startup,
342 .shutdown = ep93xx_i2s_shutdown,
343 .hw_params = ep93xx_i2s_hw_params,
344 .set_sysclk = ep93xx_i2s_set_sysclk,
345 .set_fmt = ep93xx_i2s_set_dai_fmt,
346};
347
348#define EP93XX_I2S_FORMATS (SNDRV_PCM_FMTBIT_S32_LE)
349
350static struct snd_soc_dai_driver ep93xx_i2s_dai = {
351 .symmetric_rates= 1,
352 .suspend = ep93xx_i2s_suspend,
353 .resume = ep93xx_i2s_resume,
354 .playback = {
355 .channels_min = 2,
356 .channels_max = 2,
357 .rates = SNDRV_PCM_RATE_8000_192000,
358 .formats = EP93XX_I2S_FORMATS,
359 },
360 .capture = {
361 .channels_min = 2,
362 .channels_max = 2,
363 .rates = SNDRV_PCM_RATE_8000_192000,
364 .formats = EP93XX_I2S_FORMATS,
365 },
366 .ops = &ep93xx_i2s_dai_ops,
367};
368
369static int ep93xx_i2s_probe(struct platform_device *pdev)
370{
371 struct ep93xx_i2s_info *info;
372 struct resource *res;
373 int err;
374
375 info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
376 if (!info)
377 return -ENOMEM;
378
379 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
380 if (!res)
381 return -ENODEV;
382
383 info->regs = devm_request_and_ioremap(&pdev->dev, res);
384 if (!info->regs)
385 return -ENXIO;
386
387 info->mclk = clk_get(&pdev->dev, "mclk");
388 if (IS_ERR(info->mclk)) {
389 err = PTR_ERR(info->mclk);
390 goto fail;
391 }
392
393 info->sclk = clk_get(&pdev->dev, "sclk");
394 if (IS_ERR(info->sclk)) {
395 err = PTR_ERR(info->sclk);
396 goto fail_put_mclk;
397 }
398
399 info->lrclk = clk_get(&pdev->dev, "lrclk");
400 if (IS_ERR(info->lrclk)) {
401 err = PTR_ERR(info->lrclk);
402 goto fail_put_sclk;
403 }
404
405 dev_set_drvdata(&pdev->dev, info);
406 info->dma_params = ep93xx_i2s_dma_params;
407
408 err = snd_soc_register_dai(&pdev->dev, &ep93xx_i2s_dai);
409 if (err)
410 goto fail_put_lrclk;
411
412 return 0;
413
414fail_put_lrclk:
415 dev_set_drvdata(&pdev->dev, NULL);
416 clk_put(info->lrclk);
417fail_put_sclk:
418 clk_put(info->sclk);
419fail_put_mclk:
420 clk_put(info->mclk);
421fail:
422 return err;
423}
424
425static int __devexit ep93xx_i2s_remove(struct platform_device *pdev)
426{
427 struct ep93xx_i2s_info *info = dev_get_drvdata(&pdev->dev);
428
429 snd_soc_unregister_dai(&pdev->dev);
430 dev_set_drvdata(&pdev->dev, NULL);
431 clk_put(info->lrclk);
432 clk_put(info->sclk);
433 clk_put(info->mclk);
434 return 0;
435}
436
437static struct platform_driver ep93xx_i2s_driver = {
438 .probe = ep93xx_i2s_probe,
439 .remove = __devexit_p(ep93xx_i2s_remove),
440 .driver = {
441 .name = "ep93xx-i2s",
442 .owner = THIS_MODULE,
443 },
444};
445
446module_platform_driver(ep93xx_i2s_driver);
447
448MODULE_ALIAS("platform:ep93xx-i2s");
449MODULE_AUTHOR("Ryan Mallon");
450MODULE_DESCRIPTION("EP93XX I2S driver");
451MODULE_LICENSE("GPL");