2 * s5p-i2s_sec.c -- Secondary Fifo driver for I2S_v5
4 * (c) 2009 Samsung Electronics Co. Ltd
5 * - Jaswinder Singh Brar <jassi.brar@samsung.com>
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the
9 * Free Software Foundation; either version 2 of the License, or (at your
10 * option) any later version.
14 #include <linux/delay.h>
15 #include <sound/core.h>
16 #include <sound/pcm.h>
17 #include <sound/pcm_params.h>
18 #include <sound/soc.h>
21 #include <mach/regs-clock.h>
22 /*#include <plat/regs-iis.h>*/
25 #include <mach/regs-audss.h>
27 #include "regs-i2s-v2.h"
28 #include "s3c-i2s-v2.h"
32 static void __iomem *s5p_i2s0_regs;
34 static struct s3c2410_dma_client s5p_dma_client_outs = {
35 .name = "I2S_Sec PCM Stereo out"
38 static struct s3c_dma_params s5p_i2s_sec_pcm_out = {
39 .channel = DMACH_I2S0S_TX,
40 .client = &s5p_dma_client_outs,
44 static void s5p_snd_txctrl(int on)
48 iiscon = readl(s5p_i2s0_regs + S3C2412_IISCON);
49 iismod = readl(s5p_i2s0_regs + S3C2412_IISMOD);
52 iiscon |= S3C2412_IISCON_IIS_ACTIVE;
53 iiscon &= ~S3C2412_IISCON_TXCH_PAUSE;
54 iiscon &= ~S5P_IISCON_TXSDMAPAUSE;
55 iiscon |= S5P_IISCON_TXSDMACTIVE;
57 switch (iismod & S3C2412_IISMOD_MODE_MASK) {
58 case S3C2412_IISMOD_MODE_TXONLY:
59 case S3C2412_IISMOD_MODE_TXRX:
60 /* do nothing, we are in the right mode */
63 case S3C2412_IISMOD_MODE_RXONLY:
64 iismod &= ~S3C2412_IISMOD_MODE_MASK;
65 iismod |= S3C2412_IISMOD_MODE_TXRX;
69 printk(KERN_ERR "TXEN: Invalid MODE %x in IISMOD\n",
70 iismod & S3C2412_IISMOD_MODE_MASK);
74 writel(iiscon, s5p_i2s0_regs + S3C2412_IISCON);
75 writel(iismod, s5p_i2s0_regs + S3C2412_IISMOD);
77 iiscon |= S5P_IISCON_TXSDMAPAUSE;
78 iiscon &= ~S5P_IISCON_TXSDMACTIVE;
80 /* return if primary is active */
81 if (iiscon & S3C2412_IISCON_TXDMA_ACTIVE) {
82 writel(iiscon, s5p_i2s0_regs + S3C2412_IISCON);
86 iiscon |= S3C2412_IISCON_TXCH_PAUSE;
88 switch (iismod & S3C2412_IISMOD_MODE_MASK) {
89 case S3C2412_IISMOD_MODE_TXRX:
90 iismod &= ~S3C2412_IISMOD_MODE_MASK;
91 iismod |= S3C2412_IISMOD_MODE_RXONLY;
94 case S3C2412_IISMOD_MODE_TXONLY:
95 iismod &= ~S3C2412_IISMOD_MODE_MASK;
96 iiscon &= ~S3C2412_IISCON_IIS_ACTIVE;
100 printk(KERN_ERR "TXDIS: Invalid MODE %x in IISMOD\n",
101 iismod & S3C2412_IISMOD_MODE_MASK);
105 writel(iismod, s5p_i2s0_regs + S3C2412_IISMOD);
106 writel(iiscon, s5p_i2s0_regs + S3C2412_IISCON);
110 #define msecs_to_loops(t) (loops_per_jiffy / 1000 * HZ * t)
113 * Wait for the LR signal to allow synchronisation to the L/R clock
114 * from the codec. May only be needed for slave mode.
116 static int s5p_snd_lrsync(void)
119 unsigned long loops = msecs_to_loops(1);
121 pr_debug("Entered %s\n", __func__);
124 iiscon = readl(s5p_i2s0_regs + S3C2412_IISCON);
125 if (iiscon & S3C2412_IISCON_LRINDEX)
132 pr_debug("%s: timeout\n", __func__);
139 int s5p_i2s_hw_params(struct snd_pcm_substream *substream,
140 struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
142 struct snd_soc_pcm_runtime *rtd = substream->private_data;
145 snd_soc_dai_set_dma_data(rtd->dai->cpu_dai, substream,
146 &s5p_i2s_sec_pcm_out);
148 iismod = readl(s5p_i2s0_regs + S3C2412_IISMOD);
150 /* Copy the same bps as Primary */
151 iismod &= ~S5P_IISMOD_BLCSMASK;
152 iismod |= ((iismod & S5P_IISMOD_BLCPMASK) << 2);
154 writel(iismod, s5p_i2s0_regs + S3C2412_IISMOD);
158 EXPORT_SYMBOL_GPL(s5p_i2s_hw_params);
160 int s5p_i2s_startup(struct snd_soc_dai *dai)
165 iiscon = readl(s5p_i2s0_regs + S3C2412_IISCON);
166 iismod = readl(s5p_i2s0_regs + S3C2412_IISMOD);
167 iisahb = readl(s5p_i2s0_regs + S5P_IISAHB);
169 iisahb |= (S5P_IISAHB_DMARLD | S5P_IISAHB_DISRLDINT);
170 iismod |= S5P_IISMOD_TXSLP;
172 writel(iisahb, s5p_i2s0_regs + S5P_IISAHB);
173 writel(iismod, s5p_i2s0_regs + S3C2412_IISMOD);
175 /* FIFOs must be flushed before enabling PSR
176 * and other MOD bits, so we do it here. */
177 if (iiscon & S5P_IISCON_TXSDMACTIVE)
180 iisfic = readl(s5p_i2s0_regs + S5P_IISFICS);
181 iisfic |= S3C2412_IISFIC_TXFLUSH;
182 writel(iisfic, s5p_i2s0_regs + S5P_IISFICS);
186 } while ((__raw_readl(s5p_i2s0_regs + S5P_IISFICS) >> 8) & 0x7f);
188 iisfic = readl(s5p_i2s0_regs + S5P_IISFICS);
189 iisfic &= ~S3C2412_IISFIC_TXFLUSH;
190 writel(iisfic, s5p_i2s0_regs + S5P_IISFICS);
194 EXPORT_SYMBOL_GPL(s5p_i2s_startup);
196 int s5p_i2s_trigger(struct snd_pcm_substream *substream,
197 int cmd, struct snd_soc_dai *dai)
200 case SNDRV_PCM_TRIGGER_START:
201 case SNDRV_PCM_TRIGGER_RESUME:
202 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
203 /* We don't configure clocks from this Sec i/f.
204 * So, we simply wait enough time for LRSYNC to
205 * get synced and not check return 'error'
211 case SNDRV_PCM_TRIGGER_STOP:
212 case SNDRV_PCM_TRIGGER_SUSPEND:
213 case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
220 EXPORT_SYMBOL_GPL(s5p_i2s_trigger);
222 void s5p_i2s_sec_init(void *regs, dma_addr_t phys_base)
224 #ifdef CONFIG_ARCH_S5PV210
225 /* S5PC110 or S5PV210 */
226 #define S3C_VA_AUDSS S3C_ADDR(0x01600000) /* Audio SubSystem */
227 #include <mach/regs-audss.h>
228 /* We use I2SCLK for rate generation, so set EPLLout as
229 * the parent of I2SCLK.
233 val = readl(S5P_CLKSRC_AUDSS);
236 writel(val, S5P_CLKSRC_AUDSS);
238 val = readl(S5P_CLKGATE_AUDSS);
240 writel(val, S5P_CLKGATE_AUDSS);
242 #elif defined(CONFIG_ARCH_S5PV310) || defined(CONFIG_ARCH_S5PC210)
245 #if defined(CONFIG_SND_SOC_SMDK_WM8994_MASTER) || defined(CONFIG_SND_SOC_C1_MC1N2)
246 /* I2S ratio for Codec master (3+1) = EPLL/4 = 181/4 = 45MHz */
249 /* I2S ratio for AP master (0+1) = EPLL/1 = 181/1 = 181MHz */
252 /* SRP ratio (15+1)= EPLL/16 = 181/16 = 11MHz
253 BUS ratio (1+1) = SRP/2 = 11/2 = 5MHz */
255 writel(val, S5P_CLKDIV_AUDSS);
257 writel(0x001, S5P_CLKSRC_AUDSS); /* I2S=Main CLK, ASS=FOUT_EPLL*/
259 /* CLKGATE should not be controled in here
260 writel(0x1FF, S5P_CLKGATE_AUDSS);
263 #error INITIALIZE HERE!
266 s5p_i2s0_regs = regs;
272 /* Module information */
273 MODULE_AUTHOR("Jaswinder Singh <jassi.brar@samsung.com>");
274 MODULE_DESCRIPTION("S5P I2S-SecFifo SoC Interface");
275 MODULE_LICENSE("GPL");