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