xref: /OK3568_Linux_fs/kernel/sound/soc/samsung/s3c24xx-i2s.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0+
2*4882a593Smuzhiyun //
3*4882a593Smuzhiyun // s3c24xx-i2s.c  --  ALSA Soc Audio Layer
4*4882a593Smuzhiyun //
5*4882a593Smuzhiyun // (c) 2006 Wolfson Microelectronics PLC.
6*4882a593Smuzhiyun // Graeme Gregory graeme.gregory@wolfsonmicro.com or linux@wolfsonmicro.com
7*4882a593Smuzhiyun //
8*4882a593Smuzhiyun // Copyright 2004-2005 Simtec Electronics
9*4882a593Smuzhiyun //	http://armlinux.simtec.co.uk/
10*4882a593Smuzhiyun //	Ben Dooks <ben@simtec.co.uk>
11*4882a593Smuzhiyun 
12*4882a593Smuzhiyun #include <linux/delay.h>
13*4882a593Smuzhiyun #include <linux/clk.h>
14*4882a593Smuzhiyun #include <linux/io.h>
15*4882a593Smuzhiyun #include <linux/gpio.h>
16*4882a593Smuzhiyun #include <linux/module.h>
17*4882a593Smuzhiyun 
18*4882a593Smuzhiyun #include <sound/soc.h>
19*4882a593Smuzhiyun #include <sound/pcm_params.h>
20*4882a593Smuzhiyun 
21*4882a593Smuzhiyun #include "regs-iis.h"
22*4882a593Smuzhiyun #include "dma.h"
23*4882a593Smuzhiyun #include "s3c24xx-i2s.h"
24*4882a593Smuzhiyun 
25*4882a593Smuzhiyun static struct snd_dmaengine_dai_dma_data s3c24xx_i2s_pcm_stereo_out = {
26*4882a593Smuzhiyun 	.chan_name	= "tx",
27*4882a593Smuzhiyun 	.addr_width	= 2,
28*4882a593Smuzhiyun };
29*4882a593Smuzhiyun 
30*4882a593Smuzhiyun static struct snd_dmaengine_dai_dma_data s3c24xx_i2s_pcm_stereo_in = {
31*4882a593Smuzhiyun 	.chan_name	= "rx",
32*4882a593Smuzhiyun 	.addr_width	= 2,
33*4882a593Smuzhiyun };
34*4882a593Smuzhiyun 
35*4882a593Smuzhiyun struct s3c24xx_i2s_info {
36*4882a593Smuzhiyun 	void __iomem	*regs;
37*4882a593Smuzhiyun 	struct clk	*iis_clk;
38*4882a593Smuzhiyun 	u32		iiscon;
39*4882a593Smuzhiyun 	u32		iismod;
40*4882a593Smuzhiyun 	u32		iisfcon;
41*4882a593Smuzhiyun 	u32		iispsr;
42*4882a593Smuzhiyun };
43*4882a593Smuzhiyun static struct s3c24xx_i2s_info s3c24xx_i2s;
44*4882a593Smuzhiyun 
s3c24xx_snd_txctrl(int on)45*4882a593Smuzhiyun static void s3c24xx_snd_txctrl(int on)
46*4882a593Smuzhiyun {
47*4882a593Smuzhiyun 	u32 iisfcon;
48*4882a593Smuzhiyun 	u32 iiscon;
49*4882a593Smuzhiyun 	u32 iismod;
50*4882a593Smuzhiyun 
51*4882a593Smuzhiyun 	iisfcon = readl(s3c24xx_i2s.regs + S3C2410_IISFCON);
52*4882a593Smuzhiyun 	iiscon  = readl(s3c24xx_i2s.regs + S3C2410_IISCON);
53*4882a593Smuzhiyun 	iismod  = readl(s3c24xx_i2s.regs + S3C2410_IISMOD);
54*4882a593Smuzhiyun 
55*4882a593Smuzhiyun 	pr_debug("r: IISCON: %x IISMOD: %x IISFCON: %x\n", iiscon, iismod, iisfcon);
56*4882a593Smuzhiyun 
57*4882a593Smuzhiyun 	if (on) {
58*4882a593Smuzhiyun 		iisfcon |= S3C2410_IISFCON_TXDMA | S3C2410_IISFCON_TXENABLE;
59*4882a593Smuzhiyun 		iiscon  |= S3C2410_IISCON_TXDMAEN | S3C2410_IISCON_IISEN;
60*4882a593Smuzhiyun 		iiscon  &= ~S3C2410_IISCON_TXIDLE;
61*4882a593Smuzhiyun 		iismod  |= S3C2410_IISMOD_TXMODE;
62*4882a593Smuzhiyun 
63*4882a593Smuzhiyun 		writel(iismod,  s3c24xx_i2s.regs + S3C2410_IISMOD);
64*4882a593Smuzhiyun 		writel(iisfcon, s3c24xx_i2s.regs + S3C2410_IISFCON);
65*4882a593Smuzhiyun 		writel(iiscon,  s3c24xx_i2s.regs + S3C2410_IISCON);
66*4882a593Smuzhiyun 	} else {
67*4882a593Smuzhiyun 		/* note, we have to disable the FIFOs otherwise bad things
68*4882a593Smuzhiyun 		 * seem to happen when the DMA stops. According to the
69*4882a593Smuzhiyun 		 * Samsung supplied kernel, this should allow the DMA
70*4882a593Smuzhiyun 		 * engine and FIFOs to reset. If this isn't allowed, the
71*4882a593Smuzhiyun 		 * DMA engine will simply freeze randomly.
72*4882a593Smuzhiyun 		 */
73*4882a593Smuzhiyun 
74*4882a593Smuzhiyun 		iisfcon &= ~S3C2410_IISFCON_TXENABLE;
75*4882a593Smuzhiyun 		iisfcon &= ~S3C2410_IISFCON_TXDMA;
76*4882a593Smuzhiyun 		iiscon  |=  S3C2410_IISCON_TXIDLE;
77*4882a593Smuzhiyun 		iiscon  &= ~S3C2410_IISCON_TXDMAEN;
78*4882a593Smuzhiyun 		iismod  &= ~S3C2410_IISMOD_TXMODE;
79*4882a593Smuzhiyun 
80*4882a593Smuzhiyun 		writel(iiscon,  s3c24xx_i2s.regs + S3C2410_IISCON);
81*4882a593Smuzhiyun 		writel(iisfcon, s3c24xx_i2s.regs + S3C2410_IISFCON);
82*4882a593Smuzhiyun 		writel(iismod,  s3c24xx_i2s.regs + S3C2410_IISMOD);
83*4882a593Smuzhiyun 	}
84*4882a593Smuzhiyun 
85*4882a593Smuzhiyun 	pr_debug("w: IISCON: %x IISMOD: %x IISFCON: %x\n", iiscon, iismod, iisfcon);
86*4882a593Smuzhiyun }
87*4882a593Smuzhiyun 
s3c24xx_snd_rxctrl(int on)88*4882a593Smuzhiyun static void s3c24xx_snd_rxctrl(int on)
89*4882a593Smuzhiyun {
90*4882a593Smuzhiyun 	u32 iisfcon;
91*4882a593Smuzhiyun 	u32 iiscon;
92*4882a593Smuzhiyun 	u32 iismod;
93*4882a593Smuzhiyun 
94*4882a593Smuzhiyun 	iisfcon = readl(s3c24xx_i2s.regs + S3C2410_IISFCON);
95*4882a593Smuzhiyun 	iiscon  = readl(s3c24xx_i2s.regs + S3C2410_IISCON);
96*4882a593Smuzhiyun 	iismod  = readl(s3c24xx_i2s.regs + S3C2410_IISMOD);
97*4882a593Smuzhiyun 
98*4882a593Smuzhiyun 	pr_debug("r: IISCON: %x IISMOD: %x IISFCON: %x\n", iiscon, iismod, iisfcon);
99*4882a593Smuzhiyun 
100*4882a593Smuzhiyun 	if (on) {
101*4882a593Smuzhiyun 		iisfcon |= S3C2410_IISFCON_RXDMA | S3C2410_IISFCON_RXENABLE;
102*4882a593Smuzhiyun 		iiscon  |= S3C2410_IISCON_RXDMAEN | S3C2410_IISCON_IISEN;
103*4882a593Smuzhiyun 		iiscon  &= ~S3C2410_IISCON_RXIDLE;
104*4882a593Smuzhiyun 		iismod  |= S3C2410_IISMOD_RXMODE;
105*4882a593Smuzhiyun 
106*4882a593Smuzhiyun 		writel(iismod,  s3c24xx_i2s.regs + S3C2410_IISMOD);
107*4882a593Smuzhiyun 		writel(iisfcon, s3c24xx_i2s.regs + S3C2410_IISFCON);
108*4882a593Smuzhiyun 		writel(iiscon,  s3c24xx_i2s.regs + S3C2410_IISCON);
109*4882a593Smuzhiyun 	} else {
110*4882a593Smuzhiyun 		/* note, we have to disable the FIFOs otherwise bad things
111*4882a593Smuzhiyun 		 * seem to happen when the DMA stops. According to the
112*4882a593Smuzhiyun 		 * Samsung supplied kernel, this should allow the DMA
113*4882a593Smuzhiyun 		 * engine and FIFOs to reset. If this isn't allowed, the
114*4882a593Smuzhiyun 		 * DMA engine will simply freeze randomly.
115*4882a593Smuzhiyun 		 */
116*4882a593Smuzhiyun 
117*4882a593Smuzhiyun 		iisfcon &= ~S3C2410_IISFCON_RXENABLE;
118*4882a593Smuzhiyun 		iisfcon &= ~S3C2410_IISFCON_RXDMA;
119*4882a593Smuzhiyun 		iiscon  |= S3C2410_IISCON_RXIDLE;
120*4882a593Smuzhiyun 		iiscon  &= ~S3C2410_IISCON_RXDMAEN;
121*4882a593Smuzhiyun 		iismod  &= ~S3C2410_IISMOD_RXMODE;
122*4882a593Smuzhiyun 
123*4882a593Smuzhiyun 		writel(iisfcon, s3c24xx_i2s.regs + S3C2410_IISFCON);
124*4882a593Smuzhiyun 		writel(iiscon,  s3c24xx_i2s.regs + S3C2410_IISCON);
125*4882a593Smuzhiyun 		writel(iismod,  s3c24xx_i2s.regs + S3C2410_IISMOD);
126*4882a593Smuzhiyun 	}
127*4882a593Smuzhiyun 
128*4882a593Smuzhiyun 	pr_debug("w: IISCON: %x IISMOD: %x IISFCON: %x\n", iiscon, iismod, iisfcon);
129*4882a593Smuzhiyun }
130*4882a593Smuzhiyun 
131*4882a593Smuzhiyun /*
132*4882a593Smuzhiyun  * Wait for the LR signal to allow synchronisation to the L/R clock
133*4882a593Smuzhiyun  * from the codec. May only be needed for slave mode.
134*4882a593Smuzhiyun  */
s3c24xx_snd_lrsync(void)135*4882a593Smuzhiyun static int s3c24xx_snd_lrsync(void)
136*4882a593Smuzhiyun {
137*4882a593Smuzhiyun 	u32 iiscon;
138*4882a593Smuzhiyun 	int timeout = 50; /* 5ms */
139*4882a593Smuzhiyun 
140*4882a593Smuzhiyun 	while (1) {
141*4882a593Smuzhiyun 		iiscon = readl(s3c24xx_i2s.regs + S3C2410_IISCON);
142*4882a593Smuzhiyun 		if (iiscon & S3C2410_IISCON_LRINDEX)
143*4882a593Smuzhiyun 			break;
144*4882a593Smuzhiyun 
145*4882a593Smuzhiyun 		if (!timeout--)
146*4882a593Smuzhiyun 			return -ETIMEDOUT;
147*4882a593Smuzhiyun 		udelay(100);
148*4882a593Smuzhiyun 	}
149*4882a593Smuzhiyun 
150*4882a593Smuzhiyun 	return 0;
151*4882a593Smuzhiyun }
152*4882a593Smuzhiyun 
153*4882a593Smuzhiyun /*
154*4882a593Smuzhiyun  * Check whether CPU is the master or slave
155*4882a593Smuzhiyun  */
s3c24xx_snd_is_clkmaster(void)156*4882a593Smuzhiyun static inline int s3c24xx_snd_is_clkmaster(void)
157*4882a593Smuzhiyun {
158*4882a593Smuzhiyun 	return (readl(s3c24xx_i2s.regs + S3C2410_IISMOD) & S3C2410_IISMOD_SLAVE) ? 0:1;
159*4882a593Smuzhiyun }
160*4882a593Smuzhiyun 
161*4882a593Smuzhiyun /*
162*4882a593Smuzhiyun  * Set S3C24xx I2S DAI format
163*4882a593Smuzhiyun  */
s3c24xx_i2s_set_fmt(struct snd_soc_dai * cpu_dai,unsigned int fmt)164*4882a593Smuzhiyun static int s3c24xx_i2s_set_fmt(struct snd_soc_dai *cpu_dai,
165*4882a593Smuzhiyun 		unsigned int fmt)
166*4882a593Smuzhiyun {
167*4882a593Smuzhiyun 	u32 iismod;
168*4882a593Smuzhiyun 
169*4882a593Smuzhiyun 	iismod = readl(s3c24xx_i2s.regs + S3C2410_IISMOD);
170*4882a593Smuzhiyun 	pr_debug("hw_params r: IISMOD: %x \n", iismod);
171*4882a593Smuzhiyun 
172*4882a593Smuzhiyun 	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
173*4882a593Smuzhiyun 	case SND_SOC_DAIFMT_CBM_CFM:
174*4882a593Smuzhiyun 		iismod |= S3C2410_IISMOD_SLAVE;
175*4882a593Smuzhiyun 		break;
176*4882a593Smuzhiyun 	case SND_SOC_DAIFMT_CBS_CFS:
177*4882a593Smuzhiyun 		iismod &= ~S3C2410_IISMOD_SLAVE;
178*4882a593Smuzhiyun 		break;
179*4882a593Smuzhiyun 	default:
180*4882a593Smuzhiyun 		return -EINVAL;
181*4882a593Smuzhiyun 	}
182*4882a593Smuzhiyun 
183*4882a593Smuzhiyun 	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
184*4882a593Smuzhiyun 	case SND_SOC_DAIFMT_LEFT_J:
185*4882a593Smuzhiyun 		iismod |= S3C2410_IISMOD_MSB;
186*4882a593Smuzhiyun 		break;
187*4882a593Smuzhiyun 	case SND_SOC_DAIFMT_I2S:
188*4882a593Smuzhiyun 		iismod &= ~S3C2410_IISMOD_MSB;
189*4882a593Smuzhiyun 		break;
190*4882a593Smuzhiyun 	default:
191*4882a593Smuzhiyun 		return -EINVAL;
192*4882a593Smuzhiyun 	}
193*4882a593Smuzhiyun 
194*4882a593Smuzhiyun 	writel(iismod, s3c24xx_i2s.regs + S3C2410_IISMOD);
195*4882a593Smuzhiyun 	pr_debug("hw_params w: IISMOD: %x \n", iismod);
196*4882a593Smuzhiyun 
197*4882a593Smuzhiyun 	return 0;
198*4882a593Smuzhiyun }
199*4882a593Smuzhiyun 
s3c24xx_i2s_hw_params(struct snd_pcm_substream * substream,struct snd_pcm_hw_params * params,struct snd_soc_dai * dai)200*4882a593Smuzhiyun static int s3c24xx_i2s_hw_params(struct snd_pcm_substream *substream,
201*4882a593Smuzhiyun 				 struct snd_pcm_hw_params *params,
202*4882a593Smuzhiyun 				 struct snd_soc_dai *dai)
203*4882a593Smuzhiyun {
204*4882a593Smuzhiyun 	struct snd_dmaengine_dai_dma_data *dma_data;
205*4882a593Smuzhiyun 	u32 iismod;
206*4882a593Smuzhiyun 
207*4882a593Smuzhiyun 	dma_data = snd_soc_dai_get_dma_data(dai, substream);
208*4882a593Smuzhiyun 
209*4882a593Smuzhiyun 	/* Working copies of register */
210*4882a593Smuzhiyun 	iismod = readl(s3c24xx_i2s.regs + S3C2410_IISMOD);
211*4882a593Smuzhiyun 	pr_debug("hw_params r: IISMOD: %x\n", iismod);
212*4882a593Smuzhiyun 
213*4882a593Smuzhiyun 	switch (params_width(params)) {
214*4882a593Smuzhiyun 	case 8:
215*4882a593Smuzhiyun 		iismod &= ~S3C2410_IISMOD_16BIT;
216*4882a593Smuzhiyun 		dma_data->addr_width = 1;
217*4882a593Smuzhiyun 		break;
218*4882a593Smuzhiyun 	case 16:
219*4882a593Smuzhiyun 		iismod |= S3C2410_IISMOD_16BIT;
220*4882a593Smuzhiyun 		dma_data->addr_width = 2;
221*4882a593Smuzhiyun 		break;
222*4882a593Smuzhiyun 	default:
223*4882a593Smuzhiyun 		return -EINVAL;
224*4882a593Smuzhiyun 	}
225*4882a593Smuzhiyun 
226*4882a593Smuzhiyun 	writel(iismod, s3c24xx_i2s.regs + S3C2410_IISMOD);
227*4882a593Smuzhiyun 	pr_debug("hw_params w: IISMOD: %x\n", iismod);
228*4882a593Smuzhiyun 
229*4882a593Smuzhiyun 	return 0;
230*4882a593Smuzhiyun }
231*4882a593Smuzhiyun 
s3c24xx_i2s_trigger(struct snd_pcm_substream * substream,int cmd,struct snd_soc_dai * dai)232*4882a593Smuzhiyun static int s3c24xx_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
233*4882a593Smuzhiyun 			       struct snd_soc_dai *dai)
234*4882a593Smuzhiyun {
235*4882a593Smuzhiyun 	int ret = 0;
236*4882a593Smuzhiyun 
237*4882a593Smuzhiyun 	switch (cmd) {
238*4882a593Smuzhiyun 	case SNDRV_PCM_TRIGGER_START:
239*4882a593Smuzhiyun 	case SNDRV_PCM_TRIGGER_RESUME:
240*4882a593Smuzhiyun 	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
241*4882a593Smuzhiyun 		if (!s3c24xx_snd_is_clkmaster()) {
242*4882a593Smuzhiyun 			ret = s3c24xx_snd_lrsync();
243*4882a593Smuzhiyun 			if (ret)
244*4882a593Smuzhiyun 				goto exit_err;
245*4882a593Smuzhiyun 		}
246*4882a593Smuzhiyun 
247*4882a593Smuzhiyun 		if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
248*4882a593Smuzhiyun 			s3c24xx_snd_rxctrl(1);
249*4882a593Smuzhiyun 		else
250*4882a593Smuzhiyun 			s3c24xx_snd_txctrl(1);
251*4882a593Smuzhiyun 
252*4882a593Smuzhiyun 		break;
253*4882a593Smuzhiyun 	case SNDRV_PCM_TRIGGER_STOP:
254*4882a593Smuzhiyun 	case SNDRV_PCM_TRIGGER_SUSPEND:
255*4882a593Smuzhiyun 	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
256*4882a593Smuzhiyun 		if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
257*4882a593Smuzhiyun 			s3c24xx_snd_rxctrl(0);
258*4882a593Smuzhiyun 		else
259*4882a593Smuzhiyun 			s3c24xx_snd_txctrl(0);
260*4882a593Smuzhiyun 		break;
261*4882a593Smuzhiyun 	default:
262*4882a593Smuzhiyun 		ret = -EINVAL;
263*4882a593Smuzhiyun 		break;
264*4882a593Smuzhiyun 	}
265*4882a593Smuzhiyun 
266*4882a593Smuzhiyun exit_err:
267*4882a593Smuzhiyun 	return ret;
268*4882a593Smuzhiyun }
269*4882a593Smuzhiyun 
270*4882a593Smuzhiyun /*
271*4882a593Smuzhiyun  * Set S3C24xx Clock source
272*4882a593Smuzhiyun  */
s3c24xx_i2s_set_sysclk(struct snd_soc_dai * cpu_dai,int clk_id,unsigned int freq,int dir)273*4882a593Smuzhiyun static int s3c24xx_i2s_set_sysclk(struct snd_soc_dai *cpu_dai,
274*4882a593Smuzhiyun 	int clk_id, unsigned int freq, int dir)
275*4882a593Smuzhiyun {
276*4882a593Smuzhiyun 	u32 iismod = readl(s3c24xx_i2s.regs + S3C2410_IISMOD);
277*4882a593Smuzhiyun 
278*4882a593Smuzhiyun 	iismod &= ~S3C2440_IISMOD_MPLL;
279*4882a593Smuzhiyun 
280*4882a593Smuzhiyun 	switch (clk_id) {
281*4882a593Smuzhiyun 	case S3C24XX_CLKSRC_PCLK:
282*4882a593Smuzhiyun 		break;
283*4882a593Smuzhiyun 	case S3C24XX_CLKSRC_MPLL:
284*4882a593Smuzhiyun 		iismod |= S3C2440_IISMOD_MPLL;
285*4882a593Smuzhiyun 		break;
286*4882a593Smuzhiyun 	default:
287*4882a593Smuzhiyun 		return -EINVAL;
288*4882a593Smuzhiyun 	}
289*4882a593Smuzhiyun 
290*4882a593Smuzhiyun 	writel(iismod, s3c24xx_i2s.regs + S3C2410_IISMOD);
291*4882a593Smuzhiyun 	return 0;
292*4882a593Smuzhiyun }
293*4882a593Smuzhiyun 
294*4882a593Smuzhiyun /*
295*4882a593Smuzhiyun  * Set S3C24xx Clock dividers
296*4882a593Smuzhiyun  */
s3c24xx_i2s_set_clkdiv(struct snd_soc_dai * cpu_dai,int div_id,int div)297*4882a593Smuzhiyun static int s3c24xx_i2s_set_clkdiv(struct snd_soc_dai *cpu_dai,
298*4882a593Smuzhiyun 	int div_id, int div)
299*4882a593Smuzhiyun {
300*4882a593Smuzhiyun 	u32 reg;
301*4882a593Smuzhiyun 
302*4882a593Smuzhiyun 	switch (div_id) {
303*4882a593Smuzhiyun 	case S3C24XX_DIV_BCLK:
304*4882a593Smuzhiyun 		reg = readl(s3c24xx_i2s.regs + S3C2410_IISMOD) & ~S3C2410_IISMOD_FS_MASK;
305*4882a593Smuzhiyun 		writel(reg | div, s3c24xx_i2s.regs + S3C2410_IISMOD);
306*4882a593Smuzhiyun 		break;
307*4882a593Smuzhiyun 	case S3C24XX_DIV_MCLK:
308*4882a593Smuzhiyun 		reg = readl(s3c24xx_i2s.regs + S3C2410_IISMOD) & ~(S3C2410_IISMOD_384FS);
309*4882a593Smuzhiyun 		writel(reg | div, s3c24xx_i2s.regs + S3C2410_IISMOD);
310*4882a593Smuzhiyun 		break;
311*4882a593Smuzhiyun 	case S3C24XX_DIV_PRESCALER:
312*4882a593Smuzhiyun 		writel(div, s3c24xx_i2s.regs + S3C2410_IISPSR);
313*4882a593Smuzhiyun 		reg = readl(s3c24xx_i2s.regs + S3C2410_IISCON);
314*4882a593Smuzhiyun 		writel(reg | S3C2410_IISCON_PSCEN, s3c24xx_i2s.regs + S3C2410_IISCON);
315*4882a593Smuzhiyun 		break;
316*4882a593Smuzhiyun 	default:
317*4882a593Smuzhiyun 		return -EINVAL;
318*4882a593Smuzhiyun 	}
319*4882a593Smuzhiyun 
320*4882a593Smuzhiyun 	return 0;
321*4882a593Smuzhiyun }
322*4882a593Smuzhiyun 
323*4882a593Smuzhiyun /*
324*4882a593Smuzhiyun  * To avoid duplicating clock code, allow machine driver to
325*4882a593Smuzhiyun  * get the clockrate from here.
326*4882a593Smuzhiyun  */
s3c24xx_i2s_get_clockrate(void)327*4882a593Smuzhiyun u32 s3c24xx_i2s_get_clockrate(void)
328*4882a593Smuzhiyun {
329*4882a593Smuzhiyun 	return clk_get_rate(s3c24xx_i2s.iis_clk);
330*4882a593Smuzhiyun }
331*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(s3c24xx_i2s_get_clockrate);
332*4882a593Smuzhiyun 
s3c24xx_i2s_probe(struct snd_soc_dai * dai)333*4882a593Smuzhiyun static int s3c24xx_i2s_probe(struct snd_soc_dai *dai)
334*4882a593Smuzhiyun {
335*4882a593Smuzhiyun 	int ret;
336*4882a593Smuzhiyun 	snd_soc_dai_init_dma_data(dai, &s3c24xx_i2s_pcm_stereo_out,
337*4882a593Smuzhiyun 					&s3c24xx_i2s_pcm_stereo_in);
338*4882a593Smuzhiyun 
339*4882a593Smuzhiyun 	s3c24xx_i2s.iis_clk = devm_clk_get(dai->dev, "iis");
340*4882a593Smuzhiyun 	if (IS_ERR(s3c24xx_i2s.iis_clk)) {
341*4882a593Smuzhiyun 		pr_err("failed to get iis_clock\n");
342*4882a593Smuzhiyun 		return PTR_ERR(s3c24xx_i2s.iis_clk);
343*4882a593Smuzhiyun 	}
344*4882a593Smuzhiyun 	ret = clk_prepare_enable(s3c24xx_i2s.iis_clk);
345*4882a593Smuzhiyun 	if (ret)
346*4882a593Smuzhiyun 		return ret;
347*4882a593Smuzhiyun 
348*4882a593Smuzhiyun 	writel(S3C2410_IISCON_IISEN, s3c24xx_i2s.regs + S3C2410_IISCON);
349*4882a593Smuzhiyun 
350*4882a593Smuzhiyun 	s3c24xx_snd_txctrl(0);
351*4882a593Smuzhiyun 	s3c24xx_snd_rxctrl(0);
352*4882a593Smuzhiyun 
353*4882a593Smuzhiyun 	return 0;
354*4882a593Smuzhiyun }
355*4882a593Smuzhiyun 
356*4882a593Smuzhiyun #ifdef CONFIG_PM
s3c24xx_i2s_suspend(struct snd_soc_component * component)357*4882a593Smuzhiyun static int s3c24xx_i2s_suspend(struct snd_soc_component *component)
358*4882a593Smuzhiyun {
359*4882a593Smuzhiyun 	s3c24xx_i2s.iiscon = readl(s3c24xx_i2s.regs + S3C2410_IISCON);
360*4882a593Smuzhiyun 	s3c24xx_i2s.iismod = readl(s3c24xx_i2s.regs + S3C2410_IISMOD);
361*4882a593Smuzhiyun 	s3c24xx_i2s.iisfcon = readl(s3c24xx_i2s.regs + S3C2410_IISFCON);
362*4882a593Smuzhiyun 	s3c24xx_i2s.iispsr = readl(s3c24xx_i2s.regs + S3C2410_IISPSR);
363*4882a593Smuzhiyun 
364*4882a593Smuzhiyun 	clk_disable_unprepare(s3c24xx_i2s.iis_clk);
365*4882a593Smuzhiyun 
366*4882a593Smuzhiyun 	return 0;
367*4882a593Smuzhiyun }
368*4882a593Smuzhiyun 
s3c24xx_i2s_resume(struct snd_soc_component * component)369*4882a593Smuzhiyun static int s3c24xx_i2s_resume(struct snd_soc_component *component)
370*4882a593Smuzhiyun {
371*4882a593Smuzhiyun 	int ret;
372*4882a593Smuzhiyun 
373*4882a593Smuzhiyun 	ret = clk_prepare_enable(s3c24xx_i2s.iis_clk);
374*4882a593Smuzhiyun 	if (ret)
375*4882a593Smuzhiyun 		return ret;
376*4882a593Smuzhiyun 
377*4882a593Smuzhiyun 	writel(s3c24xx_i2s.iiscon, s3c24xx_i2s.regs + S3C2410_IISCON);
378*4882a593Smuzhiyun 	writel(s3c24xx_i2s.iismod, s3c24xx_i2s.regs + S3C2410_IISMOD);
379*4882a593Smuzhiyun 	writel(s3c24xx_i2s.iisfcon, s3c24xx_i2s.regs + S3C2410_IISFCON);
380*4882a593Smuzhiyun 	writel(s3c24xx_i2s.iispsr, s3c24xx_i2s.regs + S3C2410_IISPSR);
381*4882a593Smuzhiyun 
382*4882a593Smuzhiyun 	return 0;
383*4882a593Smuzhiyun }
384*4882a593Smuzhiyun #else
385*4882a593Smuzhiyun #define s3c24xx_i2s_suspend NULL
386*4882a593Smuzhiyun #define s3c24xx_i2s_resume NULL
387*4882a593Smuzhiyun #endif
388*4882a593Smuzhiyun 
389*4882a593Smuzhiyun #define S3C24XX_I2S_RATES \
390*4882a593Smuzhiyun 	(SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | SNDRV_PCM_RATE_16000 | \
391*4882a593Smuzhiyun 	SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
392*4882a593Smuzhiyun 	SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000)
393*4882a593Smuzhiyun 
394*4882a593Smuzhiyun static const struct snd_soc_dai_ops s3c24xx_i2s_dai_ops = {
395*4882a593Smuzhiyun 	.trigger	= s3c24xx_i2s_trigger,
396*4882a593Smuzhiyun 	.hw_params	= s3c24xx_i2s_hw_params,
397*4882a593Smuzhiyun 	.set_fmt	= s3c24xx_i2s_set_fmt,
398*4882a593Smuzhiyun 	.set_clkdiv	= s3c24xx_i2s_set_clkdiv,
399*4882a593Smuzhiyun 	.set_sysclk	= s3c24xx_i2s_set_sysclk,
400*4882a593Smuzhiyun };
401*4882a593Smuzhiyun 
402*4882a593Smuzhiyun static struct snd_soc_dai_driver s3c24xx_i2s_dai = {
403*4882a593Smuzhiyun 	.probe = s3c24xx_i2s_probe,
404*4882a593Smuzhiyun 	.playback = {
405*4882a593Smuzhiyun 		.channels_min = 2,
406*4882a593Smuzhiyun 		.channels_max = 2,
407*4882a593Smuzhiyun 		.rates = S3C24XX_I2S_RATES,
408*4882a593Smuzhiyun 		.formats = SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE,},
409*4882a593Smuzhiyun 	.capture = {
410*4882a593Smuzhiyun 		.channels_min = 2,
411*4882a593Smuzhiyun 		.channels_max = 2,
412*4882a593Smuzhiyun 		.rates = S3C24XX_I2S_RATES,
413*4882a593Smuzhiyun 		.formats = SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE,},
414*4882a593Smuzhiyun 	.ops = &s3c24xx_i2s_dai_ops,
415*4882a593Smuzhiyun };
416*4882a593Smuzhiyun 
417*4882a593Smuzhiyun static const struct snd_soc_component_driver s3c24xx_i2s_component = {
418*4882a593Smuzhiyun 	.name		= "s3c24xx-i2s",
419*4882a593Smuzhiyun 	.suspend	= s3c24xx_i2s_suspend,
420*4882a593Smuzhiyun 	.resume		= s3c24xx_i2s_resume,
421*4882a593Smuzhiyun };
422*4882a593Smuzhiyun 
s3c24xx_iis_dev_probe(struct platform_device * pdev)423*4882a593Smuzhiyun static int s3c24xx_iis_dev_probe(struct platform_device *pdev)
424*4882a593Smuzhiyun {
425*4882a593Smuzhiyun 	struct resource *res;
426*4882a593Smuzhiyun 	int ret;
427*4882a593Smuzhiyun 
428*4882a593Smuzhiyun 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
429*4882a593Smuzhiyun 	s3c24xx_i2s.regs = devm_ioremap_resource(&pdev->dev, res);
430*4882a593Smuzhiyun 	if (IS_ERR(s3c24xx_i2s.regs))
431*4882a593Smuzhiyun 		return PTR_ERR(s3c24xx_i2s.regs);
432*4882a593Smuzhiyun 
433*4882a593Smuzhiyun 	s3c24xx_i2s_pcm_stereo_out.addr = res->start + S3C2410_IISFIFO;
434*4882a593Smuzhiyun 	s3c24xx_i2s_pcm_stereo_in.addr = res->start + S3C2410_IISFIFO;
435*4882a593Smuzhiyun 
436*4882a593Smuzhiyun 	ret = samsung_asoc_dma_platform_register(&pdev->dev, NULL,
437*4882a593Smuzhiyun 						 "tx", "rx", NULL);
438*4882a593Smuzhiyun 	if (ret) {
439*4882a593Smuzhiyun 		dev_err(&pdev->dev, "Failed to register the DMA: %d\n", ret);
440*4882a593Smuzhiyun 		return ret;
441*4882a593Smuzhiyun 	}
442*4882a593Smuzhiyun 
443*4882a593Smuzhiyun 	ret = devm_snd_soc_register_component(&pdev->dev,
444*4882a593Smuzhiyun 			&s3c24xx_i2s_component, &s3c24xx_i2s_dai, 1);
445*4882a593Smuzhiyun 	if (ret)
446*4882a593Smuzhiyun 		dev_err(&pdev->dev, "Failed to register the DAI\n");
447*4882a593Smuzhiyun 
448*4882a593Smuzhiyun 	return ret;
449*4882a593Smuzhiyun }
450*4882a593Smuzhiyun 
451*4882a593Smuzhiyun static struct platform_driver s3c24xx_iis_driver = {
452*4882a593Smuzhiyun 	.probe  = s3c24xx_iis_dev_probe,
453*4882a593Smuzhiyun 	.driver = {
454*4882a593Smuzhiyun 		.name = "s3c24xx-iis",
455*4882a593Smuzhiyun 	},
456*4882a593Smuzhiyun };
457*4882a593Smuzhiyun 
458*4882a593Smuzhiyun module_platform_driver(s3c24xx_iis_driver);
459*4882a593Smuzhiyun 
460*4882a593Smuzhiyun /* Module information */
461*4882a593Smuzhiyun MODULE_AUTHOR("Ben Dooks, <ben@simtec.co.uk>");
462*4882a593Smuzhiyun MODULE_DESCRIPTION("s3c24xx I2S SoC Interface");
463*4882a593Smuzhiyun MODULE_LICENSE("GPL");
464*4882a593Smuzhiyun MODULE_ALIAS("platform:s3c24xx-iis");
465