aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBen Dooks <ben@simtec.co.uk>2009-03-03 19:49:30 -0500
committerMark Brown <broonie@opensource.wolfsonmicro.com>2009-03-06 08:37:08 -0500
commitdc85447b196a683784eb85654c436fd58c3e2ed1 (patch)
treefa4bfa4ddcea242060294ab5db8be598973e31ea
parent3093e48c48b69ccc06a1f78ffe7ece7ee2ee09ef (diff)
ASoC: Split s3c2412-i2s.c into core and SoC specific parts
The S3C2412 I2S (IIS) interface is replicated on further Samsung SoC parts in a broadly compatible way, so split the common code out into a core called s3c-i2s-v2.[ch] so that the newer SoCs such as the S3C6410 can make use of it. As such, all the original s3c2412 functions are currently being left with their original names, and will be renamed later in the series. Signed-off-by: Ben Dooks <ben@simtec.co.uk> Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
-rw-r--r--sound/soc/s3c24xx/Kconfig4
-rw-r--r--sound/soc/s3c24xx/Makefile2
-rw-r--r--sound/soc/s3c24xx/jive_wm8750.c6
-rw-r--r--sound/soc/s3c24xx/s3c-i2s-v2.c645
-rw-r--r--sound/soc/s3c24xx/s3c-i2s-v2.h90
-rw-r--r--sound/soc/s3c24xx/s3c2412-i2s.c596
-rw-r--r--sound/soc/s3c24xx/s3c2412-i2s.h17
7 files changed, 767 insertions, 593 deletions
diff --git a/sound/soc/s3c24xx/Kconfig b/sound/soc/s3c24xx/Kconfig
index 7803218d1903..bd98adaa5b0b 100644
--- a/sound/soc/s3c24xx/Kconfig
+++ b/sound/soc/s3c24xx/Kconfig
@@ -9,8 +9,12 @@ config SND_S3C24XX_SOC
9config SND_S3C24XX_SOC_I2S 9config SND_S3C24XX_SOC_I2S
10 tristate 10 tristate
11 11
12config SND_S3C_I2SV2_SOC
13 tristate
14
12config SND_S3C2412_SOC_I2S 15config SND_S3C2412_SOC_I2S
13 tristate 16 tristate
17 select SND_S3C_I2SV2_SOC
14 18
15config SND_S3C2443_SOC_AC97 19config SND_S3C2443_SOC_AC97
16 tristate 20 tristate
diff --git a/sound/soc/s3c24xx/Makefile b/sound/soc/s3c24xx/Makefile
index bc11976e269c..848981e18ac3 100644
--- a/sound/soc/s3c24xx/Makefile
+++ b/sound/soc/s3c24xx/Makefile
@@ -3,11 +3,13 @@ snd-soc-s3c24xx-objs := s3c24xx-pcm.o
3snd-soc-s3c24xx-i2s-objs := s3c24xx-i2s.o 3snd-soc-s3c24xx-i2s-objs := s3c24xx-i2s.o
4snd-soc-s3c2412-i2s-objs := s3c2412-i2s.o 4snd-soc-s3c2412-i2s-objs := s3c2412-i2s.o
5snd-soc-s3c2443-ac97-objs := s3c2443-ac97.o 5snd-soc-s3c2443-ac97-objs := s3c2443-ac97.o
6snd-soc-s3c-i2s-v2-objs := s3c-i2s-v2.o
6 7
7obj-$(CONFIG_SND_S3C24XX_SOC) += snd-soc-s3c24xx.o 8obj-$(CONFIG_SND_S3C24XX_SOC) += snd-soc-s3c24xx.o
8obj-$(CONFIG_SND_S3C24XX_SOC_I2S) += snd-soc-s3c24xx-i2s.o 9obj-$(CONFIG_SND_S3C24XX_SOC_I2S) += snd-soc-s3c24xx-i2s.o
9obj-$(CONFIG_SND_S3C2443_SOC_AC97) += snd-soc-s3c2443-ac97.o 10obj-$(CONFIG_SND_S3C2443_SOC_AC97) += snd-soc-s3c2443-ac97.o
10obj-$(CONFIG_SND_S3C2412_SOC_I2S) += snd-soc-s3c2412-i2s.o 11obj-$(CONFIG_SND_S3C2412_SOC_I2S) += snd-soc-s3c2412-i2s.o
12obj-$(CONFIG_SND_S3C_I2SV2_SOC) += snd-soc-s3c-i2s-v2.o
11 13
12# S3C24XX Machine Support 14# S3C24XX Machine Support
13snd-soc-jive-wm8750-objs := jive_wm8750.o 15snd-soc-jive-wm8750-objs := jive_wm8750.o
diff --git a/sound/soc/s3c24xx/jive_wm8750.c b/sound/soc/s3c24xx/jive_wm8750.c
index fe466c1f71ec..7dfe26ea8f40 100644
--- a/sound/soc/s3c24xx/jive_wm8750.c
+++ b/sound/soc/s3c24xx/jive_wm8750.c
@@ -65,7 +65,7 @@ static int jive_hw_params(struct snd_pcm_substream *substream,
65 struct snd_soc_pcm_runtime *rtd = substream->private_data; 65 struct snd_soc_pcm_runtime *rtd = substream->private_data;
66 struct snd_soc_dai *codec_dai = rtd->dai->codec_dai; 66 struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
67 struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; 67 struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
68 struct s3c2412_rate_calc div; 68 struct s3c_i2sv2_rate_calc div;
69 unsigned int clk = 0; 69 unsigned int clk = 0;
70 int ret = 0; 70 int ret = 0;
71 71
@@ -83,8 +83,8 @@ static int jive_hw_params(struct snd_pcm_substream *substream,
83 break; 83 break;
84 } 84 }
85 85
86 s3c2412_iis_calc_rate(&div, NULL, params_rate(params), 86 s3c_i2sv2_calc_rate(&div, NULL, params_rate(params),
87 s3c2412_get_iisclk()); 87 s3c2412_get_iisclk());
88 88
89 /* set codec DAI configuration */ 89 /* set codec DAI configuration */
90 ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S | 90 ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
diff --git a/sound/soc/s3c24xx/s3c-i2s-v2.c b/sound/soc/s3c24xx/s3c-i2s-v2.c
new file mode 100644
index 000000000000..43262e1e8f98
--- /dev/null
+++ b/sound/soc/s3c24xx/s3c-i2s-v2.c
@@ -0,0 +1,645 @@
1/* sound/soc/s3c24xx/s3c-i2c-v2.c
2 *
3 * ALSA Soc Audio Layer - I2S core for newer Samsung SoCs.
4 *
5 * Copyright (c) 2006 Wolfson Microelectronics PLC.
6 * Graeme Gregory graeme.gregory@wolfsonmicro.com
7 * linux@wolfsonmicro.com
8 *
9 * Copyright (c) 2008, 2007, 2004-2005 Simtec Electronics
10 * http://armlinux.simtec.co.uk/
11 * Ben Dooks <ben@simtec.co.uk>
12 *
13 * This program is free software; you can redistribute it and/or modify it
14 * under the terms of the GNU General Public License as published by the
15 * Free Software Foundation; either version 2 of the License, or (at your
16 * option) any later version.
17 */
18
19#include <linux/init.h>
20#include <linux/module.h>
21#include <linux/device.h>
22#include <linux/delay.h>
23#include <linux/clk.h>
24#include <linux/kernel.h>
25#include <linux/io.h>
26
27#include <sound/core.h>
28#include <sound/pcm.h>
29#include <sound/pcm_params.h>
30#include <sound/initval.h>
31#include <sound/soc.h>
32
33#include <plat/regs-s3c2412-iis.h>
34
35#include <plat/audio.h>
36#include <mach/dma.h>
37
38#include "s3c-i2s-v2.h"
39
40#define S3C2412_I2S_DEBUG_CON 0
41#define S3C2412_I2S_DEBUG 0
42
43#if S3C2412_I2S_DEBUG
44#define DBG(x...) printk(KERN_INFO x)
45#else
46#define DBG(x...) do { } while (0)
47#endif
48
49static inline struct s3c_i2sv2_info *to_info(struct snd_soc_dai *cpu_dai)
50{
51 return cpu_dai->private_data;
52}
53
54#define bit_set(v, b) (((v) & (b)) ? 1 : 0)
55
56#if S3C2412_I2S_DEBUG_CON
57static void dbg_showcon(const char *fn, u32 con)
58{
59 printk(KERN_DEBUG "%s: LRI=%d, TXFEMPT=%d, RXFEMPT=%d, TXFFULL=%d, RXFFULL=%d\n", fn,
60 bit_set(con, S3C2412_IISCON_LRINDEX),
61 bit_set(con, S3C2412_IISCON_TXFIFO_EMPTY),
62 bit_set(con, S3C2412_IISCON_RXFIFO_EMPTY),
63 bit_set(con, S3C2412_IISCON_TXFIFO_FULL),
64 bit_set(con, S3C2412_IISCON_RXFIFO_FULL));
65
66 printk(KERN_DEBUG "%s: PAUSE: TXDMA=%d, RXDMA=%d, TXCH=%d, RXCH=%d\n",
67 fn,
68 bit_set(con, S3C2412_IISCON_TXDMA_PAUSE),
69 bit_set(con, S3C2412_IISCON_RXDMA_PAUSE),
70 bit_set(con, S3C2412_IISCON_TXCH_PAUSE),
71 bit_set(con, S3C2412_IISCON_RXCH_PAUSE));
72 printk(KERN_DEBUG "%s: ACTIVE: TXDMA=%d, RXDMA=%d, IIS=%d\n", fn,
73 bit_set(con, S3C2412_IISCON_TXDMA_ACTIVE),
74 bit_set(con, S3C2412_IISCON_RXDMA_ACTIVE),
75 bit_set(con, S3C2412_IISCON_IIS_ACTIVE));
76}
77#else
78static inline void dbg_showcon(const char *fn, u32 con)
79{
80}
81#endif
82
83
84/* Turn on or off the transmission path. */
85void s3c2412_snd_txctrl(struct s3c_i2sv2_info *i2s, int on)
86{
87 void __iomem *regs = i2s->regs;
88 u32 fic, con, mod;
89
90 DBG("%s(%d)\n", __func__, on);
91
92 fic = readl(regs + S3C2412_IISFIC);
93 con = readl(regs + S3C2412_IISCON);
94 mod = readl(regs + S3C2412_IISMOD);
95
96 DBG("%s: IIS: CON=%x MOD=%x FIC=%x\n", __func__, con, mod, fic);
97
98 if (on) {
99 con |= S3C2412_IISCON_TXDMA_ACTIVE | S3C2412_IISCON_IIS_ACTIVE;
100 con &= ~S3C2412_IISCON_TXDMA_PAUSE;
101 con &= ~S3C2412_IISCON_TXCH_PAUSE;
102
103 switch (mod & S3C2412_IISMOD_MODE_MASK) {
104 case S3C2412_IISMOD_MODE_TXONLY:
105 case S3C2412_IISMOD_MODE_TXRX:
106 /* do nothing, we are in the right mode */
107 break;
108
109 case S3C2412_IISMOD_MODE_RXONLY:
110 mod &= ~S3C2412_IISMOD_MODE_MASK;
111 mod |= S3C2412_IISMOD_MODE_TXRX;
112 break;
113
114 default:
115 dev_err(i2s->dev, "TXEN: Invalid MODE in IISMOD\n");
116 }
117
118 writel(con, regs + S3C2412_IISCON);
119 writel(mod, regs + S3C2412_IISMOD);
120 } else {
121 /* Note, we do not have any indication that the FIFO problems
122 * tha the S3C2410/2440 had apply here, so we should be able
123 * to disable the DMA and TX without resetting the FIFOS.
124 */
125
126 con |= S3C2412_IISCON_TXDMA_PAUSE;
127 con |= S3C2412_IISCON_TXCH_PAUSE;
128 con &= ~S3C2412_IISCON_TXDMA_ACTIVE;
129
130 switch (mod & S3C2412_IISMOD_MODE_MASK) {
131 case S3C2412_IISMOD_MODE_TXRX:
132 mod &= ~S3C2412_IISMOD_MODE_MASK;
133 mod |= S3C2412_IISMOD_MODE_RXONLY;
134 break;
135
136 case S3C2412_IISMOD_MODE_TXONLY:
137 mod &= ~S3C2412_IISMOD_MODE_MASK;
138 con &= ~S3C2412_IISCON_IIS_ACTIVE;
139 break;
140
141 default:
142 dev_err(i2s->dev, "TXDIS: Invalid MODE in IISMOD\n");
143 }
144
145 writel(mod, regs + S3C2412_IISMOD);
146 writel(con, regs + S3C2412_IISCON);
147 }
148
149 fic = readl(regs + S3C2412_IISFIC);
150 dbg_showcon(__func__, con);
151 DBG("%s: IIS: CON=%x MOD=%x FIC=%x\n", __func__, con, mod, fic);
152}
153EXPORT_SYMBOL_GPL(s3c2412_snd_txctrl);
154
155void s3c2412_snd_rxctrl(struct s3c_i2sv2_info *i2s, int on)
156{
157 void __iomem *regs = i2s->regs;
158 u32 fic, con, mod;
159
160 DBG("%s(%d)\n", __func__, on);
161
162 fic = readl(regs + S3C2412_IISFIC);
163 con = readl(regs + S3C2412_IISCON);
164 mod = readl(regs + S3C2412_IISMOD);
165
166 DBG("%s: IIS: CON=%x MOD=%x FIC=%x\n", __func__, con, mod, fic);
167
168 if (on) {
169 con |= S3C2412_IISCON_RXDMA_ACTIVE | S3C2412_IISCON_IIS_ACTIVE;
170 con &= ~S3C2412_IISCON_RXDMA_PAUSE;
171 con &= ~S3C2412_IISCON_RXCH_PAUSE;
172
173 switch (mod & S3C2412_IISMOD_MODE_MASK) {
174 case S3C2412_IISMOD_MODE_TXRX:
175 case S3C2412_IISMOD_MODE_RXONLY:
176 /* do nothing, we are in the right mode */
177 break;
178
179 case S3C2412_IISMOD_MODE_TXONLY:
180 mod &= ~S3C2412_IISMOD_MODE_MASK;
181 mod |= S3C2412_IISMOD_MODE_TXRX;
182 break;
183
184 default:
185 dev_err(i2s->dev, "RXEN: Invalid MODE in IISMOD\n");
186 }
187
188 writel(mod, regs + S3C2412_IISMOD);
189 writel(con, regs + S3C2412_IISCON);
190 } else {
191 /* See txctrl notes on FIFOs. */
192
193 con &= ~S3C2412_IISCON_RXDMA_ACTIVE;
194 con |= S3C2412_IISCON_RXDMA_PAUSE;
195 con |= S3C2412_IISCON_RXCH_PAUSE;
196
197 switch (mod & S3C2412_IISMOD_MODE_MASK) {
198 case S3C2412_IISMOD_MODE_RXONLY:
199 con &= ~S3C2412_IISCON_IIS_ACTIVE;
200 mod &= ~S3C2412_IISMOD_MODE_MASK;
201 break;
202
203 case S3C2412_IISMOD_MODE_TXRX:
204 mod &= ~S3C2412_IISMOD_MODE_MASK;
205 mod |= S3C2412_IISMOD_MODE_TXONLY;
206 break;
207
208 default:
209 dev_err(i2s->dev, "RXEN: Invalid MODE in IISMOD\n");
210 }
211
212 writel(con, regs + S3C2412_IISCON);
213 writel(mod, regs + S3C2412_IISMOD);
214 }
215
216 fic = readl(regs + S3C2412_IISFIC);
217 DBG("%s: IIS: CON=%x MOD=%x FIC=%x\n", __func__, con, mod, fic);
218}
219EXPORT_SYMBOL_GPL(s3c2412_snd_rxctrl);
220
221/*
222 * Wait for the LR signal to allow synchronisation to the L/R clock
223 * from the codec. May only be needed for slave mode.
224 */
225static int s3c2412_snd_lrsync(struct s3c_i2sv2_info *i2s)
226{
227 u32 iiscon;
228 unsigned long timeout = jiffies + msecs_to_jiffies(5);
229
230 DBG("Entered %s\n", __func__);
231
232 while (1) {
233 iiscon = readl(i2s->regs + S3C2412_IISCON);
234 if (iiscon & S3C2412_IISCON_LRINDEX)
235 break;
236
237 if (timeout < jiffies) {
238 printk(KERN_ERR "%s: timeout\n", __func__);
239 return -ETIMEDOUT;
240 }
241 }
242
243 return 0;
244}
245
246/*
247 * Set S3C2412 I2S DAI format
248 */
249static int s3c2412_i2s_set_fmt(struct snd_soc_dai *cpu_dai,
250 unsigned int fmt)
251{
252 struct s3c_i2sv2_info *i2s = to_info(cpu_dai);
253 u32 iismod;
254
255 DBG("Entered %s\n", __func__);
256
257 iismod = readl(i2s->regs + S3C2412_IISMOD);
258 DBG("hw_params r: IISMOD: %x \n", iismod);
259
260#if defined(CONFIG_CPU_S3C2412) || defined(CONFIG_CPU_S3C2413)
261#define IISMOD_MASTER_MASK S3C2412_IISMOD_MASTER_MASK
262#define IISMOD_SLAVE S3C2412_IISMOD_SLAVE
263#define IISMOD_MASTER S3C2412_IISMOD_MASTER_INTERNAL
264#endif
265
266#if defined(CONFIG_PLAT_S3C64XX)
267/* From Rev1.1 datasheet, we have two master and two slave modes:
268 * IMS[11:10]:
269 * 00 = master mode, fed from PCLK
270 * 01 = master mode, fed from CLKAUDIO
271 * 10 = slave mode, using PCLK
272 * 11 = slave mode, using I2SCLK
273 */
274#define IISMOD_MASTER_MASK (1 << 11)
275#define IISMOD_SLAVE (1 << 11)
276#define IISMOD_MASTER (0x0)
277#endif
278
279 switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
280 case SND_SOC_DAIFMT_CBM_CFM:
281 i2s->master = 0;
282 iismod &= ~IISMOD_MASTER_MASK;
283 iismod |= IISMOD_SLAVE;
284 break;
285 case SND_SOC_DAIFMT_CBS_CFS:
286 i2s->master = 1;
287 iismod &= ~IISMOD_MASTER_MASK;
288 iismod |= IISMOD_MASTER;
289 break;
290 default:
291 DBG("unknwon master/slave format\n");
292 return -EINVAL;
293 }
294
295 iismod &= ~S3C2412_IISMOD_SDF_MASK;
296
297 switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
298 case SND_SOC_DAIFMT_RIGHT_J:
299 iismod |= S3C2412_IISMOD_SDF_MSB;
300 break;
301 case SND_SOC_DAIFMT_LEFT_J:
302 iismod |= S3C2412_IISMOD_SDF_LSB;
303 break;
304 case SND_SOC_DAIFMT_I2S:
305 iismod |= S3C2412_IISMOD_SDF_IIS;
306 break;
307 default:
308 DBG("Unknown data format\n");
309 return -EINVAL;
310 }
311
312 writel(iismod, i2s->regs + S3C2412_IISMOD);
313 DBG("hw_params w: IISMOD: %x \n", iismod);
314 return 0;
315}
316
317static int s3c2412_i2s_hw_params(struct snd_pcm_substream *substream,
318 struct snd_pcm_hw_params *params,
319 struct snd_soc_dai *socdai)
320{
321 struct snd_soc_pcm_runtime *rtd = substream->private_data;
322 struct snd_soc_dai_link *dai = rtd->dai;
323 struct s3c_i2sv2_info *i2s = to_info(dai->cpu_dai);
324 u32 iismod;
325
326 DBG("Entered %s\n", __func__);
327
328 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
329 dai->cpu_dai->dma_data = i2s->dma_playback;
330 else
331 dai->cpu_dai->dma_data = i2s->dma_capture;
332
333 /* Working copies of register */
334 iismod = readl(i2s->regs + S3C2412_IISMOD);
335 DBG("%s: r: IISMOD: %x\n", __func__, iismod);
336
337 switch (params_format(params)) {
338 case SNDRV_PCM_FORMAT_S8:
339 iismod |= S3C2412_IISMOD_8BIT;
340 break;
341 case SNDRV_PCM_FORMAT_S16_LE:
342 iismod &= ~S3C2412_IISMOD_8BIT;
343 break;
344 }
345
346 writel(iismod, i2s->regs + S3C2412_IISMOD);
347 DBG("%s: w: IISMOD: %x\n", __func__, iismod);
348 return 0;
349}
350
351static int s3c2412_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
352 struct snd_soc_dai *dai)
353{
354 struct snd_soc_pcm_runtime *rtd = substream->private_data;
355 struct s3c_i2sv2_info *i2s = to_info(rtd->dai->cpu_dai);
356 int capture = (substream->stream == SNDRV_PCM_STREAM_CAPTURE);
357 unsigned long irqs;
358 int ret = 0;
359
360 DBG("Entered %s\n", __func__);
361
362 switch (cmd) {
363 case SNDRV_PCM_TRIGGER_START:
364 /* On start, ensure that the FIFOs are cleared and reset. */
365
366 writel(capture ? S3C2412_IISFIC_RXFLUSH : S3C2412_IISFIC_TXFLUSH,
367 i2s->regs + S3C2412_IISFIC);
368
369 /* clear again, just in case */
370 writel(0x0, i2s->regs + S3C2412_IISFIC);
371
372 case SNDRV_PCM_TRIGGER_RESUME:
373 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
374 if (!i2s->master) {
375 ret = s3c2412_snd_lrsync(i2s);
376 if (ret)
377 goto exit_err;
378 }
379
380 local_irq_save(irqs);
381
382 if (capture)
383 s3c2412_snd_rxctrl(i2s, 1);
384 else
385 s3c2412_snd_txctrl(i2s, 1);
386
387 local_irq_restore(irqs);
388 break;
389
390 case SNDRV_PCM_TRIGGER_STOP:
391 case SNDRV_PCM_TRIGGER_SUSPEND:
392 case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
393 local_irq_save(irqs);
394
395 if (capture)
396 s3c2412_snd_rxctrl(i2s, 0);
397 else
398 s3c2412_snd_txctrl(i2s, 0);
399
400 local_irq_restore(irqs);
401 break;
402 default:
403 ret = -EINVAL;
404 break;
405 }
406
407exit_err:
408 return ret;
409}
410
411/*
412 * Set S3C2412 Clock dividers
413 */
414static int s3c2412_i2s_set_clkdiv(struct snd_soc_dai *cpu_dai,
415 int div_id, int div)
416{
417 struct s3c_i2sv2_info *i2s = to_info(cpu_dai);
418 u32 reg;
419
420 DBG("%s(%p, %d, %d)\n", __func__, cpu_dai, div_id, div);
421
422 switch (div_id) {
423 case S3C_I2SV2_DIV_BCLK:
424 reg = readl(i2s->regs + S3C2412_IISMOD);
425 reg &= ~S3C2412_IISMOD_BCLK_MASK;
426 writel(reg | div, i2s->regs + S3C2412_IISMOD);
427
428 DBG("%s: MOD=%08x\n", __func__, readl(i2s->regs + S3C2412_IISMOD));
429 break;
430
431 case S3C_I2SV2_DIV_RCLK:
432 if (div > 3) {
433 /* convert value to bit field */
434
435 switch (div) {
436 case 256:
437 div = S3C2412_IISMOD_RCLK_256FS;
438 break;
439
440 case 384:
441 div = S3C2412_IISMOD_RCLK_384FS;
442 break;
443
444 case 512:
445 div = S3C2412_IISMOD_RCLK_512FS;
446 break;
447
448 case 768:
449 div = S3C2412_IISMOD_RCLK_768FS;
450 break;
451
452 default:
453 return -EINVAL;
454 }
455 }
456
457 reg = readl(i2s->regs + S3C2412_IISMOD);
458 reg &= ~S3C2412_IISMOD_RCLK_MASK;
459 writel(reg | div, i2s->regs + S3C2412_IISMOD);
460 DBG("%s: MOD=%08x\n", __func__, readl(i2s->regs + S3C2412_IISMOD));
461 break;
462
463 case S3C_I2SV2_DIV_PRESCALER:
464 if (div >= 0) {
465 writel((div << 8) | S3C2412_IISPSR_PSREN,
466 i2s->regs + S3C2412_IISPSR);
467 } else {
468 writel(0x0, i2s->regs + S3C2412_IISPSR);
469 }
470 DBG("%s: PSR=%08x\n", __func__, readl(i2s->regs + S3C2412_IISPSR));
471 break;
472
473 default:
474 return -EINVAL;
475 }
476
477 return 0;
478}
479
480/* default table of all avaialable root fs divisors */
481static unsigned int iis_fs_tab[] = { 256, 512, 384, 768 };
482
483int s3c2412_iis_calc_rate(struct s3c_i2sv2_rate_calc *info,
484 unsigned int *fstab,
485 unsigned int rate, struct clk *clk)
486{
487 unsigned long clkrate = clk_get_rate(clk);
488 unsigned int div;
489 unsigned int fsclk;
490 unsigned int actual;
491 unsigned int fs;
492 unsigned int fsdiv;
493 signed int deviation = 0;
494 unsigned int best_fs = 0;
495 unsigned int best_div = 0;
496 unsigned int best_rate = 0;
497 unsigned int best_deviation = INT_MAX;
498
499 if (fstab == NULL)
500 fstab = iis_fs_tab;
501
502 for (fs = 0; fs < ARRAY_SIZE(iis_fs_tab); fs++) {
503 fsdiv = iis_fs_tab[fs];
504
505 fsclk = clkrate / fsdiv;
506 div = fsclk / rate;
507
508 if ((fsclk % rate) > (rate / 2))
509 div++;
510
511 if (div <= 1)
512 continue;
513
514 actual = clkrate / (fsdiv * div);
515 deviation = actual - rate;
516
517 printk(KERN_DEBUG "%dfs: div %d => result %d, deviation %d\n",
518 fsdiv, div, actual, deviation);
519
520 deviation = abs(deviation);
521
522 if (deviation < best_deviation) {
523 best_fs = fsdiv;
524 best_div = div;
525 best_rate = actual;
526 best_deviation = deviation;
527 }
528
529 if (deviation == 0)
530 break;
531 }
532
533 printk(KERN_DEBUG "best: fs=%d, div=%d, rate=%d\n",
534 best_fs, best_div, best_rate);
535
536 info->fs_div = best_fs;
537 info->clk_div = best_div;
538
539 return 0;
540}
541EXPORT_SYMBOL_GPL(s3c2412_iis_calc_rate);
542
543int s3c_i2sv2_probe(struct platform_device *pdev,
544 struct snd_soc_dai *dai,
545 struct s3c_i2sv2_info *i2s,
546 unsigned long base)
547{
548 struct device *dev = &pdev->dev;
549
550 i2s->dev = dev;
551
552 /* record our i2s structure for later use in the callbacks */
553 dai->private_data = i2s;
554
555 i2s->regs = ioremap(base, 0x100);
556 if (i2s->regs == NULL) {
557 dev_err(dev, "cannot ioremap registers\n");
558 return -ENXIO;
559 }
560
561 i2s->iis_pclk = clk_get(dev, "iis");
562 if (i2s->iis_pclk == NULL) {
563 DBG("failed to get iis_clock\n");
564 iounmap(i2s->regs);
565 return -ENOENT;
566 }
567
568 clk_enable(i2s->iis_pclk);
569
570 s3c2412_snd_txctrl(i2s, 0);
571 s3c2412_snd_rxctrl(i2s, 0);
572
573 return 0;
574}
575
576EXPORT_SYMBOL_GPL(s3c_i2sv2_probe);
577
578#ifdef CONFIG_PM
579static int s3c2412_i2s_suspend(struct snd_soc_dai *dai)
580{
581 struct s3c_i2sv2_info *i2s = to_info(dai);
582 u32 iismod;
583
584 if (dai->active) {
585 i2s->suspend_iismod = readl(i2s->regs + S3C2412_IISMOD);
586 i2s->suspend_iiscon = readl(i2s->regs + S3C2412_IISCON);
587 i2s->suspend_iispsr = readl(i2s->regs + S3C2412_IISPSR);
588
589 /* some basic suspend checks */
590
591 iismod = readl(i2s->regs + S3C2412_IISMOD);
592
593 if (iismod & S3C2412_IISCON_RXDMA_ACTIVE)
594 pr_warning("%s: RXDMA active?\n", __func__);
595
596 if (iismod & S3C2412_IISCON_TXDMA_ACTIVE)
597 pr_warning("%s: TXDMA active?\n", __func__);
598
599 if (iismod & S3C2412_IISCON_IIS_ACTIVE)
600 pr_warning("%s: IIS active\n", __func__);
601 }
602
603 return 0;
604}
605
606static int s3c2412_i2s_resume(struct snd_soc_dai *dai)
607{
608 struct s3c_i2sv2_info *i2s = to_info(dai);
609
610 pr_info("dai_active %d, IISMOD %08x, IISCON %08x\n",
611 dai->active, i2s->suspend_iismod, i2s->suspend_iiscon);
612
613 if (dai->active) {
614 writel(i2s->suspend_iiscon, i2s->regs + S3C2412_IISCON);
615 writel(i2s->suspend_iismod, i2s->regs + S3C2412_IISMOD);
616 writel(i2s->suspend_iispsr, i2s->regs + S3C2412_IISPSR);
617
618 writel(S3C2412_IISFIC_RXFLUSH | S3C2412_IISFIC_TXFLUSH,
619 i2s->regs + S3C2412_IISFIC);
620
621 ndelay(250);
622 writel(0x0, i2s->regs + S3C2412_IISFIC);
623 }
624
625 return 0;
626}
627#else
628#define s3c2412_i2s_suspend NULL
629#define s3c2412_i2s_resume NULL
630#endif
631
632int s3c_i2sv2_register_dai(struct snd_soc_dai *dai)
633{
634 dai->ops.trigger = s3c2412_i2s_trigger;
635 dai->ops.hw_params = s3c2412_i2s_hw_params;
636 dai->ops.set_fmt = s3c2412_i2s_set_fmt;
637 dai->ops.set_clkdiv = s3c2412_i2s_set_clkdiv;
638
639 dai->suspend = s3c2412_i2s_suspend;
640 dai->resume = s3c2412_i2s_resume;
641
642 return snd_soc_register_dai(dai);
643}
644
645EXPORT_SYMBOL_GPL(s3c_i2sv2_register_dai);
diff --git a/sound/soc/s3c24xx/s3c-i2s-v2.h b/sound/soc/s3c24xx/s3c-i2s-v2.h
new file mode 100644
index 000000000000..f66854a77fb2
--- /dev/null
+++ b/sound/soc/s3c24xx/s3c-i2s-v2.h
@@ -0,0 +1,90 @@
1/* sound/soc/s3c24xx/s3c-i2s-v2.h
2 *
3 * ALSA Soc Audio Layer - S3C_I2SV2 I2S driver
4 *
5 * Copyright (c) 2007 Simtec Electronics
6 * http://armlinux.simtec.co.uk/
7 * Ben Dooks <ben@simtec.co.uk>
8 *
9 * This program is free software; you can redistribute it and/or modify it
10 * under the terms of the GNU General Public License as published by the
11 * Free Software Foundation; either version 2 of the License, or (at your
12 * option) any later version.
13*/
14
15/* This code is the core support for the I2S block found in a number of
16 * Samsung SoC devices which is unofficially named I2S-V2. Currently the
17 * S3C2412 and the S3C64XX series use this block to provide 1 or 2 I2S
18 * channels via configurable GPIO.
19 */
20
21#ifndef __SND_SOC_S3C24XX_S3C_I2SV2_I2S_H
22#define __SND_SOC_S3C24XX_S3C_I2SV2_I2S_H __FILE__
23
24#define S3C_I2SV2_DIV_BCLK (1)
25#define S3C_I2SV2_DIV_RCLK (2)
26#define S3C_I2SV2_DIV_PRESCALER (3)
27
28/**
29 * struct s3c_i2sv2_info - S3C I2S-V2 information
30 * @dev: The parent device passed to use from the probe.
31 * @regs: The pointer to the device registe block.
32 * @master: True if the I2S core is the I2S bit clock master.
33 * @dma_playback: DMA information for playback channel.
34 * @dma_capture: DMA information for capture channel.
35 * @suspend_iismod: PM save for the IISMOD register.
36 * @suspend_iiscon: PM save for the IISCON register.
37 * @suspend_iispsr: PM save for the IISPSR register.
38 *
39 * This is the private codec state for the hardware associated with an
40 * I2S channel such as the register mappings and clock sources.
41 */
42struct s3c_i2sv2_info {
43 struct device *dev;
44 void __iomem *regs;
45
46 struct clk *iis_pclk;
47 struct clk *iis_cclk;
48 struct clk *iis_clk;
49
50 unsigned char master;
51
52 struct s3c24xx_pcm_dma_params *dma_playback;
53 struct s3c24xx_pcm_dma_params *dma_capture;
54
55 u32 suspend_iismod;
56 u32 suspend_iiscon;
57 u32 suspend_iispsr;
58};
59
60struct s3c_i2sv2_rate_calc {
61 unsigned int clk_div; /* for prescaler */
62 unsigned int fs_div; /* for root frame clock */
63};
64
65extern int s3c_i2sv2_iis_calc_rate(struct s3c_i2sv2_rate_calc *info,
66 unsigned int *fstab,
67 unsigned int rate, struct clk *clk);
68
69/**
70 * s3c_i2sv2_probe - probe for i2s device helper
71 * @pdev: The platform device supplied to the original probe.
72 * @dai: The ASoC DAI structure supplied to the original probe.
73 * @i2s: Our local i2s structure to fill in.
74 * @base: The base address for the registers.
75 */
76extern int s3c_i2sv2_probe(struct platform_device *pdev,
77 struct snd_soc_dai *dai,
78 struct s3c_i2sv2_info *i2s,
79 unsigned long base);
80
81/**
82 * s3c_i2sv2_register_dai - register dai with soc core
83 * @dai: The snd_soc_dai structure to register
84 *
85 * Fill in any missing fields and then register the given dai with the
86 * soc core.
87 */
88extern int s3c_i2sv2_register_dai(struct snd_soc_dai *dai);
89
90#endif /* __SND_SOC_S3C24XX_S3C_I2SV2_I2S_H */
diff --git a/sound/soc/s3c24xx/s3c2412-i2s.c b/sound/soc/s3c24xx/s3c2412-i2s.c
index 3297698ff294..5099d9396676 100644
--- a/sound/soc/s3c24xx/s3c2412-i2s.c
+++ b/sound/soc/s3c24xx/s3c2412-i2s.c
@@ -33,7 +33,7 @@
33 33
34#include <plat/regs-s3c2412-iis.h> 34#include <plat/regs-s3c2412-iis.h>
35 35
36#include <mach/regs-gpio.h> 36#include <plat/regs-gpio.h>
37#include <plat/audio.h> 37#include <plat/audio.h>
38#include <mach/dma.h> 38#include <mach/dma.h>
39 39
@@ -41,7 +41,6 @@
41#include "s3c2412-i2s.h" 41#include "s3c2412-i2s.h"
42 42
43#define S3C2412_I2S_DEBUG 0 43#define S3C2412_I2S_DEBUG 0
44#define S3C2412_I2S_DEBUG_CON 0
45 44
46#if S3C2412_I2S_DEBUG 45#if S3C2412_I2S_DEBUG
47#define DBG(x...) printk(KERN_INFO x) 46#define DBG(x...) printk(KERN_INFO x)
@@ -71,431 +70,7 @@ static struct s3c24xx_pcm_dma_params s3c2412_i2s_pcm_stereo_in = {
71 .dma_size = 4, 70 .dma_size = 4,
72}; 71};
73 72
74struct s3c2412_i2s_info { 73static struct s3c_i2sv2_info s3c2412_i2s;
75 struct device *dev;
76 void __iomem *regs;
77 struct clk *iis_clk;
78 struct clk *iis_pclk;
79 struct clk *iis_cclk;
80
81 u32 suspend_iismod;
82 u32 suspend_iiscon;
83 u32 suspend_iispsr;
84};
85
86static struct s3c2412_i2s_info s3c2412_i2s;
87
88#define bit_set(v, b) (((v) & (b)) ? 1 : 0)
89
90#if S3C2412_I2S_DEBUG_CON
91static void dbg_showcon(const char *fn, u32 con)
92{
93 printk(KERN_DEBUG "%s: LRI=%d, TXFEMPT=%d, RXFEMPT=%d, TXFFULL=%d, RXFFULL=%d\n", fn,
94 bit_set(con, S3C2412_IISCON_LRINDEX),
95 bit_set(con, S3C2412_IISCON_TXFIFO_EMPTY),
96 bit_set(con, S3C2412_IISCON_RXFIFO_EMPTY),
97 bit_set(con, S3C2412_IISCON_TXFIFO_FULL),
98 bit_set(con, S3C2412_IISCON_RXFIFO_FULL));
99
100 printk(KERN_DEBUG "%s: PAUSE: TXDMA=%d, RXDMA=%d, TXCH=%d, RXCH=%d\n",
101 fn,
102 bit_set(con, S3C2412_IISCON_TXDMA_PAUSE),
103 bit_set(con, S3C2412_IISCON_RXDMA_PAUSE),
104 bit_set(con, S3C2412_IISCON_TXCH_PAUSE),
105 bit_set(con, S3C2412_IISCON_RXCH_PAUSE));
106 printk(KERN_DEBUG "%s: ACTIVE: TXDMA=%d, RXDMA=%d, IIS=%d\n", fn,
107 bit_set(con, S3C2412_IISCON_TXDMA_ACTIVE),
108 bit_set(con, S3C2412_IISCON_RXDMA_ACTIVE),
109 bit_set(con, S3C2412_IISCON_IIS_ACTIVE));
110}
111#else
112static inline void dbg_showcon(const char *fn, u32 con)
113{
114}
115#endif
116
117/* Turn on or off the transmission path. */
118static void s3c2412_snd_txctrl(int on)
119{
120 struct s3c2412_i2s_info *i2s = &s3c2412_i2s;
121 void __iomem *regs = i2s->regs;
122 u32 fic, con, mod;
123
124 DBG("%s(%d)\n", __func__, on);
125
126 fic = readl(regs + S3C2412_IISFIC);
127 con = readl(regs + S3C2412_IISCON);
128 mod = readl(regs + S3C2412_IISMOD);
129
130 DBG("%s: IIS: CON=%x MOD=%x FIC=%x\n", __func__, con, mod, fic);
131
132 if (on) {
133 con |= S3C2412_IISCON_TXDMA_ACTIVE | S3C2412_IISCON_IIS_ACTIVE;
134 con &= ~S3C2412_IISCON_TXDMA_PAUSE;
135 con &= ~S3C2412_IISCON_TXCH_PAUSE;
136
137 switch (mod & S3C2412_IISMOD_MODE_MASK) {
138 case S3C2412_IISMOD_MODE_TXONLY:
139 case S3C2412_IISMOD_MODE_TXRX:
140 /* do nothing, we are in the right mode */
141 break;
142
143 case S3C2412_IISMOD_MODE_RXONLY:
144 mod &= ~S3C2412_IISMOD_MODE_MASK;
145 mod |= S3C2412_IISMOD_MODE_TXRX;
146 break;
147
148 default:
149 dev_err(i2s->dev, "TXEN: Invalid MODE in IISMOD\n");
150 }
151
152 writel(con, regs + S3C2412_IISCON);
153 writel(mod, regs + S3C2412_IISMOD);
154 } else {
155 /* Note, we do not have any indication that the FIFO problems
156 * tha the S3C2410/2440 had apply here, so we should be able
157 * to disable the DMA and TX without resetting the FIFOS.
158 */
159
160 con |= S3C2412_IISCON_TXDMA_PAUSE;
161 con |= S3C2412_IISCON_TXCH_PAUSE;
162 con &= ~S3C2412_IISCON_TXDMA_ACTIVE;
163
164 switch (mod & S3C2412_IISMOD_MODE_MASK) {
165 case S3C2412_IISMOD_MODE_TXRX:
166 mod &= ~S3C2412_IISMOD_MODE_MASK;
167 mod |= S3C2412_IISMOD_MODE_RXONLY;
168 break;
169
170 case S3C2412_IISMOD_MODE_TXONLY:
171 mod &= ~S3C2412_IISMOD_MODE_MASK;
172 con &= ~S3C2412_IISCON_IIS_ACTIVE;
173 break;
174
175 default:
176 dev_err(i2s->dev, "TXDIS: Invalid MODE in IISMOD\n");
177 }
178
179 writel(mod, regs + S3C2412_IISMOD);
180 writel(con, regs + S3C2412_IISCON);
181 }
182
183 fic = readl(regs + S3C2412_IISFIC);
184 dbg_showcon(__func__, con);
185 DBG("%s: IIS: CON=%x MOD=%x FIC=%x\n", __func__, con, mod, fic);
186}
187
188static void s3c2412_snd_rxctrl(int on)
189{
190 struct s3c2412_i2s_info *i2s = &s3c2412_i2s;
191 void __iomem *regs = i2s->regs;
192 u32 fic, con, mod;
193
194 DBG("%s(%d)\n", __func__, on);
195
196 fic = readl(regs + S3C2412_IISFIC);
197 con = readl(regs + S3C2412_IISCON);
198 mod = readl(regs + S3C2412_IISMOD);
199
200 DBG("%s: IIS: CON=%x MOD=%x FIC=%x\n", __func__, con, mod, fic);
201
202 if (on) {
203 con |= S3C2412_IISCON_RXDMA_ACTIVE | S3C2412_IISCON_IIS_ACTIVE;
204 con &= ~S3C2412_IISCON_RXDMA_PAUSE;
205 con &= ~S3C2412_IISCON_RXCH_PAUSE;
206
207 switch (mod & S3C2412_IISMOD_MODE_MASK) {
208 case S3C2412_IISMOD_MODE_TXRX:
209 case S3C2412_IISMOD_MODE_RXONLY:
210 /* do nothing, we are in the right mode */
211 break;
212
213 case S3C2412_IISMOD_MODE_TXONLY:
214 mod &= ~S3C2412_IISMOD_MODE_MASK;
215 mod |= S3C2412_IISMOD_MODE_TXRX;
216 break;
217
218 default:
219 dev_err(i2s->dev, "RXEN: Invalid MODE in IISMOD\n");
220 }
221
222 writel(mod, regs + S3C2412_IISMOD);
223 writel(con, regs + S3C2412_IISCON);
224 } else {
225 /* See txctrl notes on FIFOs. */
226
227 con &= ~S3C2412_IISCON_RXDMA_ACTIVE;
228 con |= S3C2412_IISCON_RXDMA_PAUSE;
229 con |= S3C2412_IISCON_RXCH_PAUSE;
230
231 switch (mod & S3C2412_IISMOD_MODE_MASK) {
232 case S3C2412_IISMOD_MODE_RXONLY:
233 con &= ~S3C2412_IISCON_IIS_ACTIVE;
234 mod &= ~S3C2412_IISMOD_MODE_MASK;
235 break;
236
237 case S3C2412_IISMOD_MODE_TXRX:
238 mod &= ~S3C2412_IISMOD_MODE_MASK;
239 mod |= S3C2412_IISMOD_MODE_TXONLY;
240 break;
241
242 default:
243 dev_err(i2s->dev, "RXEN: Invalid MODE in IISMOD\n");
244 }
245
246 writel(con, regs + S3C2412_IISCON);
247 writel(mod, regs + S3C2412_IISMOD);
248 }
249
250 fic = readl(regs + S3C2412_IISFIC);
251 DBG("%s: IIS: CON=%x MOD=%x FIC=%x\n", __func__, con, mod, fic);
252}
253
254
255/*
256 * Wait for the LR signal to allow synchronisation to the L/R clock
257 * from the codec. May only be needed for slave mode.
258 */
259static int s3c2412_snd_lrsync(void)
260{
261 u32 iiscon;
262 unsigned long timeout = jiffies + msecs_to_jiffies(5);
263
264 DBG("Entered %s\n", __func__);
265
266 while (1) {
267 iiscon = readl(s3c2412_i2s.regs + S3C2412_IISCON);
268 if (iiscon & S3C2412_IISCON_LRINDEX)
269 break;
270
271 if (timeout < jiffies) {
272 printk(KERN_ERR "%s: timeout\n", __func__);
273 return -ETIMEDOUT;
274 }
275 }
276
277 return 0;
278}
279
280/*
281 * Check whether CPU is the master or slave
282 */
283static inline int s3c2412_snd_is_clkmaster(void)
284{
285 u32 iismod = readl(s3c2412_i2s.regs + S3C2412_IISMOD);
286
287 DBG("Entered %s\n", __func__);
288
289 iismod &= S3C2412_IISMOD_MASTER_MASK;
290 return !(iismod == S3C2412_IISMOD_SLAVE);
291}
292
293/*
294 * Set S3C2412 I2S DAI format
295 */
296static int s3c2412_i2s_set_fmt(struct snd_soc_dai *cpu_dai,
297 unsigned int fmt)
298{
299 u32 iismod;
300
301
302 DBG("Entered %s\n", __func__);
303
304 iismod = readl(s3c2412_i2s.regs + S3C2412_IISMOD);
305 DBG("hw_params r: IISMOD: %x \n", iismod);
306
307 switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
308 case SND_SOC_DAIFMT_CBM_CFM:
309 iismod &= ~S3C2412_IISMOD_MASTER_MASK;
310 iismod |= S3C2412_IISMOD_SLAVE;
311 break;
312 case SND_SOC_DAIFMT_CBS_CFS:
313 iismod &= ~S3C2412_IISMOD_MASTER_MASK;
314 iismod |= S3C2412_IISMOD_MASTER_INTERNAL;
315 break;
316 default:
317 DBG("unknwon master/slave format\n");
318 return -EINVAL;
319 }
320
321 iismod &= ~S3C2412_IISMOD_SDF_MASK;
322
323 switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
324 case SND_SOC_DAIFMT_RIGHT_J:
325 iismod |= S3C2412_IISMOD_SDF_MSB;
326 break;
327 case SND_SOC_DAIFMT_LEFT_J:
328 iismod |= S3C2412_IISMOD_SDF_LSB;
329 break;
330 case SND_SOC_DAIFMT_I2S:
331 iismod |= S3C2412_IISMOD_SDF_IIS;
332 break;
333 default:
334 DBG("Unknown data format\n");
335 return -EINVAL;
336 }
337
338 writel(iismod, s3c2412_i2s.regs + S3C2412_IISMOD);
339 DBG("hw_params w: IISMOD: %x \n", iismod);
340 return 0;
341}
342
343static int s3c2412_i2s_hw_params(struct snd_pcm_substream *substream,
344 struct snd_pcm_hw_params *params,
345 struct snd_soc_dai *dai)
346{
347 struct snd_soc_pcm_runtime *rtd = substream->private_data;
348 u32 iismod;
349
350 DBG("Entered %s\n", __func__);
351
352 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
353 rtd->dai->cpu_dai->dma_data = &s3c2412_i2s_pcm_stereo_out;
354 else
355 rtd->dai->cpu_dai->dma_data = &s3c2412_i2s_pcm_stereo_in;
356
357 /* Working copies of register */
358 iismod = readl(s3c2412_i2s.regs + S3C2412_IISMOD);
359 DBG("%s: r: IISMOD: %x\n", __func__, iismod);
360
361 switch (params_format(params)) {
362 case SNDRV_PCM_FORMAT_S8:
363 iismod |= S3C2412_IISMOD_8BIT;
364 break;
365 case SNDRV_PCM_FORMAT_S16_LE:
366 iismod &= ~S3C2412_IISMOD_8BIT;
367 break;
368 }
369
370 writel(iismod, s3c2412_i2s.regs + S3C2412_IISMOD);
371 DBG("%s: w: IISMOD: %x\n", __func__, iismod);
372 return 0;
373}
374
375static int s3c2412_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
376 struct snd_soc_dai *dai)
377{
378 int capture = (substream->stream == SNDRV_PCM_STREAM_CAPTURE);
379 unsigned long irqs;
380 int ret = 0;
381
382 DBG("Entered %s\n", __func__);
383
384 switch (cmd) {
385 case SNDRV_PCM_TRIGGER_START:
386 /* On start, ensure that the FIFOs are cleared and reset. */
387
388 writel(capture ? S3C2412_IISFIC_RXFLUSH : S3C2412_IISFIC_TXFLUSH,
389 s3c2412_i2s.regs + S3C2412_IISFIC);
390
391 /* clear again, just in case */
392 writel(0x0, s3c2412_i2s.regs + S3C2412_IISFIC);
393
394 case SNDRV_PCM_TRIGGER_RESUME:
395 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
396 if (!s3c2412_snd_is_clkmaster()) {
397 ret = s3c2412_snd_lrsync();
398 if (ret)
399 goto exit_err;
400 }
401
402 local_irq_save(irqs);
403
404 if (capture)
405 s3c2412_snd_rxctrl(1);
406 else
407 s3c2412_snd_txctrl(1);
408
409 local_irq_restore(irqs);
410 break;
411
412 case SNDRV_PCM_TRIGGER_STOP:
413 case SNDRV_PCM_TRIGGER_SUSPEND:
414 case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
415 local_irq_save(irqs);
416
417 if (capture)
418 s3c2412_snd_rxctrl(0);
419 else
420 s3c2412_snd_txctrl(0);
421
422 local_irq_restore(irqs);
423 break;
424 default:
425 ret = -EINVAL;
426 break;
427 }
428
429exit_err:
430 return ret;
431}
432
433/* default table of all avaialable root fs divisors */
434static unsigned int s3c2412_iis_fs[] = { 256, 512, 384, 768, 0 };
435
436int s3c2412_iis_calc_rate(struct s3c2412_rate_calc *info,
437 unsigned int *fstab,
438 unsigned int rate, struct clk *clk)
439{
440 unsigned long clkrate = clk_get_rate(clk);
441 unsigned int div;
442 unsigned int fsclk;
443 unsigned int actual;
444 unsigned int fs;
445 unsigned int fsdiv;
446 signed int deviation = 0;
447 unsigned int best_fs = 0;
448 unsigned int best_div = 0;
449 unsigned int best_rate = 0;
450 unsigned int best_deviation = INT_MAX;
451
452
453 if (fstab == NULL)
454 fstab = s3c2412_iis_fs;
455
456 for (fs = 0;; fs++) {
457 fsdiv = s3c2412_iis_fs[fs];
458
459 if (fsdiv == 0)
460 break;
461
462 fsclk = clkrate / fsdiv;
463 div = fsclk / rate;
464
465 if ((fsclk % rate) > (rate / 2))
466 div++;
467
468 if (div <= 1)
469 continue;
470
471 actual = clkrate / (fsdiv * div);
472 deviation = actual - rate;
473
474 printk(KERN_DEBUG "%dfs: div %d => result %d, deviation %d\n",
475 fsdiv, div, actual, deviation);
476
477 deviation = abs(deviation);
478
479 if (deviation < best_deviation) {
480 best_fs = fsdiv;
481 best_div = div;
482 best_rate = actual;
483 best_deviation = deviation;
484 }
485
486 if (deviation == 0)
487 break;
488 }
489
490 printk(KERN_DEBUG "best: fs=%d, div=%d, rate=%d\n",
491 best_fs, best_div, best_rate);
492
493 info->fs_div = best_fs;
494 info->clk_div = best_div;
495
496 return 0;
497}
498EXPORT_SYMBOL_GPL(s3c2412_iis_calc_rate);
499 74
500/* 75/*
501 * Set S3C2412 Clock source 76 * Set S3C2412 Clock source
@@ -510,10 +85,12 @@ static int s3c2412_i2s_set_sysclk(struct snd_soc_dai *cpu_dai,
510 85
511 switch (clk_id) { 86 switch (clk_id) {
512 case S3C2412_CLKSRC_PCLK: 87 case S3C2412_CLKSRC_PCLK:
88 s3c2412_i2s.master = 1;
513 iismod &= ~S3C2412_IISMOD_MASTER_MASK; 89 iismod &= ~S3C2412_IISMOD_MASTER_MASK;
514 iismod |= S3C2412_IISMOD_MASTER_INTERNAL; 90 iismod |= S3C2412_IISMOD_MASTER_INTERNAL;
515 break; 91 break;
516 case S3C2412_CLKSRC_I2SCLK: 92 case S3C2412_CLKSRC_I2SCLK:
93 s3c2412_i2s.master = 0;
517 iismod &= ~S3C2412_IISMOD_MASTER_MASK; 94 iismod &= ~S3C2412_IISMOD_MASTER_MASK;
518 iismod |= S3C2412_IISMOD_MASTER_EXTERNAL; 95 iismod |= S3C2412_IISMOD_MASTER_EXTERNAL;
519 break; 96 break;
@@ -525,74 +102,6 @@ static int s3c2412_i2s_set_sysclk(struct snd_soc_dai *cpu_dai,
525 return 0; 102 return 0;
526} 103}
527 104
528/*
529 * Set S3C2412 Clock dividers
530 */
531static int s3c2412_i2s_set_clkdiv(struct snd_soc_dai *cpu_dai,
532 int div_id, int div)
533{
534 struct s3c2412_i2s_info *i2s = &s3c2412_i2s;
535 u32 reg;
536
537 DBG("%s(%p, %d, %d)\n", __func__, cpu_dai, div_id, div);
538
539 switch (div_id) {
540 case S3C2412_DIV_BCLK:
541 reg = readl(i2s->regs + S3C2412_IISMOD);
542 reg &= ~S3C2412_IISMOD_BCLK_MASK;
543 writel(reg | div, i2s->regs + S3C2412_IISMOD);
544
545 DBG("%s: MOD=%08x\n", __func__, readl(i2s->regs + S3C2412_IISMOD));
546 break;
547
548 case S3C2412_DIV_RCLK:
549 if (div > 3) {
550 /* convert value to bit field */
551
552 switch (div) {
553 case 256:
554 div = S3C2412_IISMOD_RCLK_256FS;
555 break;
556
557 case 384:
558 div = S3C2412_IISMOD_RCLK_384FS;
559 break;
560
561 case 512:
562 div = S3C2412_IISMOD_RCLK_512FS;
563 break;
564
565 case 768:
566 div = S3C2412_IISMOD_RCLK_768FS;
567 break;
568
569 default:
570 return -EINVAL;
571 }
572 }
573
574 reg = readl(s3c2412_i2s.regs + S3C2412_IISMOD);
575 reg &= ~S3C2412_IISMOD_RCLK_MASK;
576 writel(reg | div, i2s->regs + S3C2412_IISMOD);
577 DBG("%s: MOD=%08x\n", __func__, readl(i2s->regs + S3C2412_IISMOD));
578 break;
579
580 case S3C2412_DIV_PRESCALER:
581 if (div >= 0) {
582 writel((div << 8) | S3C2412_IISPSR_PSREN,
583 i2s->regs + S3C2412_IISPSR);
584 } else {
585 writel(0x0, i2s->regs + S3C2412_IISPSR);
586 }
587 DBG("%s: PSR=%08x\n", __func__, readl(i2s->regs + S3C2412_IISPSR));
588 break;
589
590 default:
591 return -EINVAL;
592 }
593
594 return 0;
595}
596 105
597struct clk *s3c2412_get_iisclk(void) 106struct clk *s3c2412_get_iisclk(void)
598{ 107{
@@ -604,20 +113,16 @@ EXPORT_SYMBOL_GPL(s3c2412_get_iisclk);
604static int s3c2412_i2s_probe(struct platform_device *pdev, 113static int s3c2412_i2s_probe(struct platform_device *pdev,
605 struct snd_soc_dai *dai) 114 struct snd_soc_dai *dai)
606{ 115{
607 DBG("Entered %s\n", __func__); 116 int ret;
608 117
609 s3c2412_i2s.dev = &pdev->dev; 118 DBG("Entered %s\n", __func__);
610 119
611 s3c2412_i2s.regs = ioremap(S3C2410_PA_IIS, 0x100); 120 ret = s3c_i2sv2_probe(pdev, dai, &s3c2412_i2s, S3C2410_PA_IIS);
612 if (s3c2412_i2s.regs == NULL) 121 if (ret)
613 return -ENXIO; 122 return ret;
614 123
615 s3c2412_i2s.iis_pclk = clk_get(&pdev->dev, "iis"); 124 s3c2412_i2s.dma_capture = &s3c2412_i2s_pcm_stereo_in;
616 if (s3c2412_i2s.iis_pclk == NULL) { 125 s3c2412_i2s.dma_playback = &s3c2412_i2s_pcm_stereo_out;
617 DBG("failed to get iis_clock\n");
618 iounmap(s3c2412_i2s.regs);
619 return -ENODEV;
620 }
621 126
622 s3c2412_i2s.iis_cclk = clk_get(&pdev->dev, "i2sclk"); 127 s3c2412_i2s.iis_cclk = clk_get(&pdev->dev, "i2sclk");
623 if (s3c2412_i2s.iis_cclk == NULL) { 128 if (s3c2412_i2s.iis_cclk == NULL) {
@@ -626,12 +131,12 @@ static int s3c2412_i2s_probe(struct platform_device *pdev,
626 return -ENODEV; 131 return -ENODEV;
627 } 132 }
628 133
629 clk_set_parent(s3c2412_i2s.iis_cclk, clk_get(NULL, "mpll")); 134 /* Set MPLL as the source for IIS CLK */
630 135
631 clk_enable(s3c2412_i2s.iis_pclk); 136 clk_set_parent(s3c2412_i2s.iis_cclk, clk_get(NULL, "mpll"));
632 clk_enable(s3c2412_i2s.iis_cclk); 137 clk_enable(s3c2412_i2s.iis_cclk);
633 138
634 s3c2412_i2s.iis_clk = s3c2412_i2s.iis_pclk; 139 s3c2412_i2s.iis_cclk = s3c2412_i2s.iis_pclk;
635 140
636 /* Configure the I2S pins in correct mode */ 141 /* Configure the I2S pins in correct mode */
637 s3c2410_gpio_cfgpin(S3C2410_GPE0, S3C2410_GPE0_I2SLRCK); 142 s3c2410_gpio_cfgpin(S3C2410_GPE0, S3C2410_GPE0_I2SLRCK);
@@ -640,78 +145,18 @@ static int s3c2412_i2s_probe(struct platform_device *pdev,
640 s3c2410_gpio_cfgpin(S3C2410_GPE3, S3C2410_GPE3_I2SSDI); 145 s3c2410_gpio_cfgpin(S3C2410_GPE3, S3C2410_GPE3_I2SSDI);
641 s3c2410_gpio_cfgpin(S3C2410_GPE4, S3C2410_GPE4_I2SSDO); 146 s3c2410_gpio_cfgpin(S3C2410_GPE4, S3C2410_GPE4_I2SSDO);
642 147
643 s3c2412_snd_txctrl(0);
644 s3c2412_snd_rxctrl(0);
645
646 return 0;
647}
648
649#ifdef CONFIG_PM
650static int s3c2412_i2s_suspend(struct snd_soc_dai *dai)
651{
652 struct s3c2412_i2s_info *i2s = &s3c2412_i2s;
653 u32 iismod;
654
655 if (dai->active) {
656 i2s->suspend_iismod = readl(i2s->regs + S3C2412_IISMOD);
657 i2s->suspend_iiscon = readl(i2s->regs + S3C2412_IISCON);
658 i2s->suspend_iispsr = readl(i2s->regs + S3C2412_IISPSR);
659
660 /* some basic suspend checks */
661
662 iismod = readl(i2s->regs + S3C2412_IISMOD);
663
664 if (iismod & S3C2412_IISCON_RXDMA_ACTIVE)
665 pr_warning("%s: RXDMA active?\n", __func__);
666
667 if (iismod & S3C2412_IISCON_TXDMA_ACTIVE)
668 pr_warning("%s: TXDMA active?\n", __func__);
669
670 if (iismod & S3C2412_IISCON_IIS_ACTIVE)
671 pr_warning("%s: IIS active\n", __func__);
672 }
673
674 return 0; 148 return 0;
675} 149}
676 150
677static int s3c2412_i2s_resume(struct snd_soc_dai *dai)
678{
679 struct s3c2412_i2s_info *i2s = &s3c2412_i2s;
680
681 pr_info("dai_active %d, IISMOD %08x, IISCON %08x\n",
682 dai->active, i2s->suspend_iismod, i2s->suspend_iiscon);
683
684 if (dai->active) {
685 writel(i2s->suspend_iiscon, i2s->regs + S3C2412_IISCON);
686 writel(i2s->suspend_iismod, i2s->regs + S3C2412_IISMOD);
687 writel(i2s->suspend_iispsr, i2s->regs + S3C2412_IISPSR);
688
689 writel(S3C2412_IISFIC_RXFLUSH | S3C2412_IISFIC_TXFLUSH,
690 i2s->regs + S3C2412_IISFIC);
691
692 ndelay(250);
693 writel(0x0, i2s->regs + S3C2412_IISFIC);
694
695 }
696
697 return 0;
698}
699#else
700#define s3c2412_i2s_suspend NULL
701#define s3c2412_i2s_resume NULL
702#endif /* CONFIG_PM */
703
704#define S3C2412_I2S_RATES \ 151#define S3C2412_I2S_RATES \
705 (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | SNDRV_PCM_RATE_16000 | \ 152 (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | SNDRV_PCM_RATE_16000 | \
706 SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \ 153 SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
707 SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000) 154 SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000)
708 155
709struct snd_soc_dai s3c2412_i2s_dai = { 156struct snd_soc_dai s3c2412_i2s_dai = {
710 .name = "s3c2412-i2s", 157 .name = "s3c2412-i2s",
711 .id = 0, 158 .id = 0,
712 .probe = s3c2412_i2s_probe, 159 .probe = s3c2412_i2s_probe,
713 .suspend = s3c2412_i2s_suspend,
714 .resume = s3c2412_i2s_resume,
715 .playback = { 160 .playback = {
716 .channels_min = 2, 161 .channels_min = 2,
717 .channels_max = 2, 162 .channels_max = 2,
@@ -725,10 +170,6 @@ struct snd_soc_dai s3c2412_i2s_dai = {
725 .formats = SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE, 170 .formats = SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE,
726 }, 171 },
727 .ops = { 172 .ops = {
728 .trigger = s3c2412_i2s_trigger,
729 .hw_params = s3c2412_i2s_hw_params,
730 .set_fmt = s3c2412_i2s_set_fmt,
731 .set_clkdiv = s3c2412_i2s_set_clkdiv,
732 .set_sysclk = s3c2412_i2s_set_sysclk, 173 .set_sysclk = s3c2412_i2s_set_sysclk,
733 }, 174 },
734}; 175};
@@ -736,7 +177,7 @@ EXPORT_SYMBOL_GPL(s3c2412_i2s_dai);
736 177
737static int __init s3c2412_i2s_init(void) 178static int __init s3c2412_i2s_init(void)
738{ 179{
739 return snd_soc_register_dai(&s3c2412_i2s_dai); 180 return s3c_i2sv2_register_dai(&s3c2412_i2s_dai);
740} 181}
741module_init(s3c2412_i2s_init); 182module_init(s3c2412_i2s_init);
742 183
@@ -746,7 +187,6 @@ static void __exit s3c2412_i2s_exit(void)
746} 187}
747module_exit(s3c2412_i2s_exit); 188module_exit(s3c2412_i2s_exit);
748 189
749
750/* Module information */ 190/* Module information */
751MODULE_AUTHOR("Ben Dooks, <ben@simtec.co.uk>"); 191MODULE_AUTHOR("Ben Dooks, <ben@simtec.co.uk>");
752MODULE_DESCRIPTION("S3C2412 I2S SoC Interface"); 192MODULE_DESCRIPTION("S3C2412 I2S SoC Interface");
diff --git a/sound/soc/s3c24xx/s3c2412-i2s.h b/sound/soc/s3c24xx/s3c2412-i2s.h
index aac08a25e541..92848e54be16 100644
--- a/sound/soc/s3c24xx/s3c2412-i2s.h
+++ b/sound/soc/s3c24xx/s3c2412-i2s.h
@@ -15,9 +15,11 @@
15#ifndef __SND_SOC_S3C24XX_S3C2412_I2S_H 15#ifndef __SND_SOC_S3C24XX_S3C2412_I2S_H
16#define __SND_SOC_S3C24XX_S3C2412_I2S_H __FILE__ 16#define __SND_SOC_S3C24XX_S3C2412_I2S_H __FILE__
17 17
18#define S3C2412_DIV_BCLK (1) 18#include "s3c-i2s-v2.h"
19#define S3C2412_DIV_RCLK (2) 19
20#define S3C2412_DIV_PRESCALER (3) 20#define S3C2412_DIV_BCLK S3C_I2SV2_DIV_BCLK
21#define S3C2412_DIV_RCLK S3C_I2SV2_DIV_RCLK
22#define S3C2412_DIV_PRESCALER S3C_I2SV2_DIV_PRESCALER
21 23
22#define S3C2412_CLKSRC_PCLK (0) 24#define S3C2412_CLKSRC_PCLK (0)
23#define S3C2412_CLKSRC_I2SCLK (1) 25#define S3C2412_CLKSRC_I2SCLK (1)
@@ -26,13 +28,4 @@ extern struct clk *s3c2412_get_iisclk(void);
26 28
27extern struct snd_soc_dai s3c2412_i2s_dai; 29extern struct snd_soc_dai s3c2412_i2s_dai;
28 30
29struct s3c2412_rate_calc {
30 unsigned int clk_div; /* for prescaler */
31 unsigned int fs_div; /* for root frame clock */
32};
33
34extern int s3c2412_iis_calc_rate(struct s3c2412_rate_calc *info,
35 unsigned int *fstab,
36 unsigned int rate, struct clk *clk);
37
38#endif /* __SND_SOC_S3C24XX_S3C2412_I2S_H */ 31#endif /* __SND_SOC_S3C24XX_S3C2412_I2S_H */