GNU Linux-libre 4.9.309-gnu1
[releases.git] / sound / soc / samsung / s3c-i2s-v2.c
1 /* ALSA Soc Audio Layer - I2S core for newer Samsung SoCs.
2  *
3  * Copyright (c) 2006 Wolfson Microelectronics PLC.
4  *      Graeme Gregory graeme.gregory@wolfsonmicro.com
5  *      linux@wolfsonmicro.com
6  *
7  * Copyright (c) 2008, 2007, 2004-2005 Simtec Electronics
8  *      http://armlinux.simtec.co.uk/
9  *      Ben Dooks <ben@simtec.co.uk>
10  *
11  * This program is free software; you can redistribute  it and/or modify it
12  * under  the terms of  the GNU General  Public License as published by the
13  * Free Software Foundation;  either version 2 of the  License, or (at your
14  * option) any later version.
15  */
16
17 #include <linux/module.h>
18 #include <linux/delay.h>
19 #include <linux/clk.h>
20 #include <linux/io.h>
21
22 #include <sound/soc.h>
23 #include <sound/pcm_params.h>
24
25 #include "regs-i2s-v2.h"
26 #include "s3c-i2s-v2.h"
27
28 #undef S3C_IIS_V2_SUPPORTED
29
30 #if defined(CONFIG_CPU_S3C2412) || defined(CONFIG_CPU_S3C2413) \
31         || defined(CONFIG_ARCH_S3C64XX) || defined(CONFIG_CPU_S5PV210)
32 #define S3C_IIS_V2_SUPPORTED
33 #endif
34
35 #ifndef S3C_IIS_V2_SUPPORTED
36 #error Unsupported CPU model
37 #endif
38
39 #define S3C2412_I2S_DEBUG_CON 0
40
41 static inline struct s3c_i2sv2_info *to_info(struct snd_soc_dai *cpu_dai)
42 {
43         return snd_soc_dai_get_drvdata(cpu_dai);
44 }
45
46 #define bit_set(v, b) (((v) & (b)) ? 1 : 0)
47
48 #if S3C2412_I2S_DEBUG_CON
49 static void dbg_showcon(const char *fn, u32 con)
50 {
51         printk(KERN_DEBUG "%s: LRI=%d, TXFEMPT=%d, RXFEMPT=%d, TXFFULL=%d, RXFFULL=%d\n", fn,
52                bit_set(con, S3C2412_IISCON_LRINDEX),
53                bit_set(con, S3C2412_IISCON_TXFIFO_EMPTY),
54                bit_set(con, S3C2412_IISCON_RXFIFO_EMPTY),
55                bit_set(con, S3C2412_IISCON_TXFIFO_FULL),
56                bit_set(con, S3C2412_IISCON_RXFIFO_FULL));
57
58         printk(KERN_DEBUG "%s: PAUSE: TXDMA=%d, RXDMA=%d, TXCH=%d, RXCH=%d\n",
59                fn,
60                bit_set(con, S3C2412_IISCON_TXDMA_PAUSE),
61                bit_set(con, S3C2412_IISCON_RXDMA_PAUSE),
62                bit_set(con, S3C2412_IISCON_TXCH_PAUSE),
63                bit_set(con, S3C2412_IISCON_RXCH_PAUSE));
64         printk(KERN_DEBUG "%s: ACTIVE: TXDMA=%d, RXDMA=%d, IIS=%d\n", fn,
65                bit_set(con, S3C2412_IISCON_TXDMA_ACTIVE),
66                bit_set(con, S3C2412_IISCON_RXDMA_ACTIVE),
67                bit_set(con, S3C2412_IISCON_IIS_ACTIVE));
68 }
69 #else
70 static inline void dbg_showcon(const char *fn, u32 con)
71 {
72 }
73 #endif
74
75
76 /* Turn on or off the transmission path. */
77 static void s3c2412_snd_txctrl(struct s3c_i2sv2_info *i2s, int on)
78 {
79         void __iomem *regs = i2s->regs;
80         u32 fic, con, mod;
81
82         pr_debug("%s(%d)\n", __func__, on);
83
84         fic = readl(regs + S3C2412_IISFIC);
85         con = readl(regs + S3C2412_IISCON);
86         mod = readl(regs + S3C2412_IISMOD);
87
88         pr_debug("%s: IIS: CON=%x MOD=%x FIC=%x\n", __func__, con, mod, fic);
89
90         if (on) {
91                 con |= S3C2412_IISCON_TXDMA_ACTIVE | S3C2412_IISCON_IIS_ACTIVE;
92                 con &= ~S3C2412_IISCON_TXDMA_PAUSE;
93                 con &= ~S3C2412_IISCON_TXCH_PAUSE;
94
95                 switch (mod & S3C2412_IISMOD_MODE_MASK) {
96                 case S3C2412_IISMOD_MODE_TXONLY:
97                 case S3C2412_IISMOD_MODE_TXRX:
98                         /* do nothing, we are in the right mode */
99                         break;
100
101                 case S3C2412_IISMOD_MODE_RXONLY:
102                         mod &= ~S3C2412_IISMOD_MODE_MASK;
103                         mod |= S3C2412_IISMOD_MODE_TXRX;
104                         break;
105
106                 default:
107                         dev_err(i2s->dev, "TXEN: Invalid MODE %x in IISMOD\n",
108                                 mod & S3C2412_IISMOD_MODE_MASK);
109                         break;
110                 }
111
112                 writel(con, regs + S3C2412_IISCON);
113                 writel(mod, regs + S3C2412_IISMOD);
114         } else {
115                 /* Note, we do not have any indication that the FIFO problems
116                  * tha the S3C2410/2440 had apply here, so we should be able
117                  * to disable the DMA and TX without resetting the FIFOS.
118                  */
119
120                 con |=  S3C2412_IISCON_TXDMA_PAUSE;
121                 con |=  S3C2412_IISCON_TXCH_PAUSE;
122                 con &= ~S3C2412_IISCON_TXDMA_ACTIVE;
123
124                 switch (mod & S3C2412_IISMOD_MODE_MASK) {
125                 case S3C2412_IISMOD_MODE_TXRX:
126                         mod &= ~S3C2412_IISMOD_MODE_MASK;
127                         mod |= S3C2412_IISMOD_MODE_RXONLY;
128                         break;
129
130                 case S3C2412_IISMOD_MODE_TXONLY:
131                         mod &= ~S3C2412_IISMOD_MODE_MASK;
132                         con &= ~S3C2412_IISCON_IIS_ACTIVE;
133                         break;
134
135                 default:
136                         dev_err(i2s->dev, "TXDIS: Invalid MODE %x in IISMOD\n",
137                                 mod & S3C2412_IISMOD_MODE_MASK);
138                         break;
139                 }
140
141                 writel(mod, regs + S3C2412_IISMOD);
142                 writel(con, regs + S3C2412_IISCON);
143         }
144
145         fic = readl(regs + S3C2412_IISFIC);
146         dbg_showcon(__func__, con);
147         pr_debug("%s: IIS: CON=%x MOD=%x FIC=%x\n", __func__, con, mod, fic);
148 }
149
150 static void s3c2412_snd_rxctrl(struct s3c_i2sv2_info *i2s, int on)
151 {
152         void __iomem *regs = i2s->regs;
153         u32 fic, con, mod;
154
155         pr_debug("%s(%d)\n", __func__, on);
156
157         fic = readl(regs + S3C2412_IISFIC);
158         con = readl(regs + S3C2412_IISCON);
159         mod = readl(regs + S3C2412_IISMOD);
160
161         pr_debug("%s: IIS: CON=%x MOD=%x FIC=%x\n", __func__, con, mod, fic);
162
163         if (on) {
164                 con |= S3C2412_IISCON_RXDMA_ACTIVE | S3C2412_IISCON_IIS_ACTIVE;
165                 con &= ~S3C2412_IISCON_RXDMA_PAUSE;
166                 con &= ~S3C2412_IISCON_RXCH_PAUSE;
167
168                 switch (mod & S3C2412_IISMOD_MODE_MASK) {
169                 case S3C2412_IISMOD_MODE_TXRX:
170                 case S3C2412_IISMOD_MODE_RXONLY:
171                         /* do nothing, we are in the right mode */
172                         break;
173
174                 case S3C2412_IISMOD_MODE_TXONLY:
175                         mod &= ~S3C2412_IISMOD_MODE_MASK;
176                         mod |= S3C2412_IISMOD_MODE_TXRX;
177                         break;
178
179                 default:
180                         dev_err(i2s->dev, "RXEN: Invalid MODE %x in IISMOD\n",
181                                 mod & S3C2412_IISMOD_MODE_MASK);
182                 }
183
184                 writel(mod, regs + S3C2412_IISMOD);
185                 writel(con, regs + S3C2412_IISCON);
186         } else {
187                 /* See txctrl notes on FIFOs. */
188
189                 con &= ~S3C2412_IISCON_RXDMA_ACTIVE;
190                 con |=  S3C2412_IISCON_RXDMA_PAUSE;
191                 con |=  S3C2412_IISCON_RXCH_PAUSE;
192
193                 switch (mod & S3C2412_IISMOD_MODE_MASK) {
194                 case S3C2412_IISMOD_MODE_RXONLY:
195                         con &= ~S3C2412_IISCON_IIS_ACTIVE;
196                         mod &= ~S3C2412_IISMOD_MODE_MASK;
197                         break;
198
199                 case S3C2412_IISMOD_MODE_TXRX:
200                         mod &= ~S3C2412_IISMOD_MODE_MASK;
201                         mod |= S3C2412_IISMOD_MODE_TXONLY;
202                         break;
203
204                 default:
205                         dev_err(i2s->dev, "RXDIS: Invalid MODE %x in IISMOD\n",
206                                 mod & S3C2412_IISMOD_MODE_MASK);
207                 }
208
209                 writel(con, regs + S3C2412_IISCON);
210                 writel(mod, regs + S3C2412_IISMOD);
211         }
212
213         fic = readl(regs + S3C2412_IISFIC);
214         pr_debug("%s: IIS: CON=%x MOD=%x FIC=%x\n", __func__, con, mod, fic);
215 }
216
217 #define msecs_to_loops(t) (loops_per_jiffy / 1000 * HZ * t)
218
219 /*
220  * Wait for the LR signal to allow synchronisation to the L/R clock
221  * from the codec. May only be needed for slave mode.
222  */
223 static int s3c2412_snd_lrsync(struct s3c_i2sv2_info *i2s)
224 {
225         u32 iiscon;
226         unsigned long loops = msecs_to_loops(5);
227
228         pr_debug("Entered %s\n", __func__);
229
230         while (--loops) {
231                 iiscon = readl(i2s->regs + S3C2412_IISCON);
232                 if (iiscon & S3C2412_IISCON_LRINDEX)
233                         break;
234
235                 cpu_relax();
236         }
237
238         if (!loops) {
239                 printk(KERN_ERR "%s: timeout\n", __func__);
240                 return -ETIMEDOUT;
241         }
242
243         return 0;
244 }
245
246 /*
247  * Set S3C2412 I2S DAI format
248  */
249 static 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         pr_debug("Entered %s\n", __func__);
256
257         iismod = readl(i2s->regs + S3C2412_IISMOD);
258         pr_debug("hw_params r: IISMOD: %x \n", iismod);
259
260         switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
261         case SND_SOC_DAIFMT_CBM_CFM:
262                 i2s->master = 0;
263                 iismod |= S3C2412_IISMOD_SLAVE;
264                 break;
265         case SND_SOC_DAIFMT_CBS_CFS:
266                 i2s->master = 1;
267                 iismod &= ~S3C2412_IISMOD_SLAVE;
268                 break;
269         default:
270                 pr_err("unknown master/slave format\n");
271                 return -EINVAL;
272         }
273
274         iismod &= ~S3C2412_IISMOD_SDF_MASK;
275
276         switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
277         case SND_SOC_DAIFMT_RIGHT_J:
278                 iismod |= S3C2412_IISMOD_LR_RLOW;
279                 iismod |= S3C2412_IISMOD_SDF_MSB;
280                 break;
281         case SND_SOC_DAIFMT_LEFT_J:
282                 iismod |= S3C2412_IISMOD_LR_RLOW;
283                 iismod |= S3C2412_IISMOD_SDF_LSB;
284                 break;
285         case SND_SOC_DAIFMT_I2S:
286                 iismod &= ~S3C2412_IISMOD_LR_RLOW;
287                 iismod |= S3C2412_IISMOD_SDF_IIS;
288                 break;
289         default:
290                 pr_err("Unknown data format\n");
291                 return -EINVAL;
292         }
293
294         writel(iismod, i2s->regs + S3C2412_IISMOD);
295         pr_debug("hw_params w: IISMOD: %x \n", iismod);
296         return 0;
297 }
298
299 static int s3c_i2sv2_hw_params(struct snd_pcm_substream *substream,
300                                  struct snd_pcm_hw_params *params,
301                                  struct snd_soc_dai *dai)
302 {
303         struct s3c_i2sv2_info *i2s = to_info(dai);
304         struct snd_dmaengine_dai_dma_data *dma_data;
305         u32 iismod;
306
307         pr_debug("Entered %s\n", __func__);
308
309         if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
310                 dma_data = i2s->dma_playback;
311         else
312                 dma_data = i2s->dma_capture;
313
314         snd_soc_dai_set_dma_data(dai, substream, dma_data);
315
316         /* Working copies of register */
317         iismod = readl(i2s->regs + S3C2412_IISMOD);
318         pr_debug("%s: r: IISMOD: %x\n", __func__, iismod);
319
320         iismod &= ~S3C64XX_IISMOD_BLC_MASK;
321         /* Sample size */
322         switch (params_width(params)) {
323         case 8:
324                 iismod |= S3C64XX_IISMOD_BLC_8BIT;
325                 break;
326         case 16:
327                 break;
328         case 24:
329                 iismod |= S3C64XX_IISMOD_BLC_24BIT;
330                 break;
331         }
332
333         writel(iismod, i2s->regs + S3C2412_IISMOD);
334         pr_debug("%s: w: IISMOD: %x\n", __func__, iismod);
335
336         return 0;
337 }
338
339 static int s3c_i2sv2_set_sysclk(struct snd_soc_dai *cpu_dai,
340                                   int clk_id, unsigned int freq, int dir)
341 {
342         struct s3c_i2sv2_info *i2s = to_info(cpu_dai);
343         u32 iismod = readl(i2s->regs + S3C2412_IISMOD);
344
345         pr_debug("Entered %s\n", __func__);
346         pr_debug("%s r: IISMOD: %x\n", __func__, iismod);
347
348         switch (clk_id) {
349         case S3C_I2SV2_CLKSRC_PCLK:
350                 iismod &= ~S3C2412_IISMOD_IMS_SYSMUX;
351                 break;
352
353         case S3C_I2SV2_CLKSRC_AUDIOBUS:
354                 iismod |= S3C2412_IISMOD_IMS_SYSMUX;
355                 break;
356
357         case S3C_I2SV2_CLKSRC_CDCLK:
358                 /* Error if controller doesn't have the CDCLKCON bit */
359                 if (!(i2s->feature & S3C_FEATURE_CDCLKCON))
360                         return -EINVAL;
361
362                 switch (dir) {
363                 case SND_SOC_CLOCK_IN:
364                         iismod |= S3C64XX_IISMOD_CDCLKCON;
365                         break;
366                 case SND_SOC_CLOCK_OUT:
367                         iismod &= ~S3C64XX_IISMOD_CDCLKCON;
368                         break;
369                 default:
370                         return -EINVAL;
371                 }
372                 break;
373
374         default:
375                 return -EINVAL;
376         }
377
378         writel(iismod, i2s->regs + S3C2412_IISMOD);
379         pr_debug("%s w: IISMOD: %x\n", __func__, iismod);
380
381         return 0;
382 }
383
384 static int s3c2412_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
385                                struct snd_soc_dai *dai)
386 {
387         struct snd_soc_pcm_runtime *rtd = substream->private_data;
388         struct s3c_i2sv2_info *i2s = to_info(rtd->cpu_dai);
389         int capture = (substream->stream == SNDRV_PCM_STREAM_CAPTURE);
390         unsigned long irqs;
391         int ret = 0;
392
393         pr_debug("Entered %s\n", __func__);
394
395         switch (cmd) {
396         case SNDRV_PCM_TRIGGER_START:
397                 /* On start, ensure that the FIFOs are cleared and reset. */
398
399                 writel(capture ? S3C2412_IISFIC_RXFLUSH : S3C2412_IISFIC_TXFLUSH,
400                        i2s->regs + S3C2412_IISFIC);
401
402                 /* clear again, just in case */
403                 writel(0x0, i2s->regs + S3C2412_IISFIC);
404
405         case SNDRV_PCM_TRIGGER_RESUME:
406         case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
407                 if (!i2s->master) {
408                         ret = s3c2412_snd_lrsync(i2s);
409                         if (ret)
410                                 goto exit_err;
411                 }
412
413                 local_irq_save(irqs);
414
415                 if (capture)
416                         s3c2412_snd_rxctrl(i2s, 1);
417                 else
418                         s3c2412_snd_txctrl(i2s, 1);
419
420                 local_irq_restore(irqs);
421
422                 break;
423
424         case SNDRV_PCM_TRIGGER_STOP:
425         case SNDRV_PCM_TRIGGER_SUSPEND:
426         case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
427                 local_irq_save(irqs);
428
429                 if (capture)
430                         s3c2412_snd_rxctrl(i2s, 0);
431                 else
432                         s3c2412_snd_txctrl(i2s, 0);
433
434                 local_irq_restore(irqs);
435                 break;
436         default:
437                 ret = -EINVAL;
438                 break;
439         }
440
441 exit_err:
442         return ret;
443 }
444
445 /*
446  * Set S3C2412 Clock dividers
447  */
448 static int s3c2412_i2s_set_clkdiv(struct snd_soc_dai *cpu_dai,
449                                   int div_id, int div)
450 {
451         struct s3c_i2sv2_info *i2s = to_info(cpu_dai);
452         u32 reg;
453
454         pr_debug("%s(%p, %d, %d)\n", __func__, cpu_dai, div_id, div);
455
456         switch (div_id) {
457         case S3C_I2SV2_DIV_BCLK:
458                 switch (div) {
459                 case 16:
460                         div = S3C2412_IISMOD_BCLK_16FS;
461                         break;
462
463                 case 32:
464                         div = S3C2412_IISMOD_BCLK_32FS;
465                         break;
466
467                 case 24:
468                         div = S3C2412_IISMOD_BCLK_24FS;
469                         break;
470
471                 case 48:
472                         div = S3C2412_IISMOD_BCLK_48FS;
473                         break;
474
475                 default:
476                         return -EINVAL;
477                 }
478
479                 reg = readl(i2s->regs + S3C2412_IISMOD);
480                 reg &= ~S3C2412_IISMOD_BCLK_MASK;
481                 writel(reg | div, i2s->regs + S3C2412_IISMOD);
482
483                 pr_debug("%s: MOD=%08x\n", __func__, readl(i2s->regs + S3C2412_IISMOD));
484                 break;
485
486         case S3C_I2SV2_DIV_RCLK:
487                 switch (div) {
488                 case 256:
489                         div = S3C2412_IISMOD_RCLK_256FS;
490                         break;
491
492                 case 384:
493                         div = S3C2412_IISMOD_RCLK_384FS;
494                         break;
495
496                 case 512:
497                         div = S3C2412_IISMOD_RCLK_512FS;
498                         break;
499
500                 case 768:
501                         div = S3C2412_IISMOD_RCLK_768FS;
502                         break;
503
504                 default:
505                         return -EINVAL;
506                 }
507
508                 reg = readl(i2s->regs + S3C2412_IISMOD);
509                 reg &= ~S3C2412_IISMOD_RCLK_MASK;
510                 writel(reg | div, i2s->regs + S3C2412_IISMOD);
511                 pr_debug("%s: MOD=%08x\n", __func__, readl(i2s->regs + S3C2412_IISMOD));
512                 break;
513
514         case S3C_I2SV2_DIV_PRESCALER:
515                 if (div >= 0) {
516                         writel((div << 8) | S3C2412_IISPSR_PSREN,
517                                i2s->regs + S3C2412_IISPSR);
518                 } else {
519                         writel(0x0, i2s->regs + S3C2412_IISPSR);
520                 }
521                 pr_debug("%s: PSR=%08x\n", __func__, readl(i2s->regs + S3C2412_IISPSR));
522                 break;
523
524         default:
525                 return -EINVAL;
526         }
527
528         return 0;
529 }
530
531 static snd_pcm_sframes_t s3c2412_i2s_delay(struct snd_pcm_substream *substream,
532                                            struct snd_soc_dai *dai)
533 {
534         struct s3c_i2sv2_info *i2s = to_info(dai);
535         u32 reg = readl(i2s->regs + S3C2412_IISFIC);
536         snd_pcm_sframes_t delay;
537
538         if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
539                 delay = S3C2412_IISFIC_TXCOUNT(reg);
540         else
541                 delay = S3C2412_IISFIC_RXCOUNT(reg);
542
543         return delay;
544 }
545
546 struct clk *s3c_i2sv2_get_clock(struct snd_soc_dai *cpu_dai)
547 {
548         struct s3c_i2sv2_info *i2s = to_info(cpu_dai);
549         u32 iismod = readl(i2s->regs + S3C2412_IISMOD);
550
551         if (iismod & S3C2412_IISMOD_IMS_SYSMUX)
552                 return i2s->iis_cclk;
553         else
554                 return i2s->iis_pclk;
555 }
556 EXPORT_SYMBOL_GPL(s3c_i2sv2_get_clock);
557
558 /* default table of all avaialable root fs divisors */
559 static unsigned int iis_fs_tab[] = { 256, 512, 384, 768 };
560
561 int s3c_i2sv2_iis_calc_rate(struct s3c_i2sv2_rate_calc *info,
562                             unsigned int *fstab,
563                             unsigned int rate, struct clk *clk)
564 {
565         unsigned long clkrate = clk_get_rate(clk);
566         unsigned int div;
567         unsigned int fsclk;
568         unsigned int actual;
569         unsigned int fs;
570         unsigned int fsdiv;
571         signed int deviation = 0;
572         unsigned int best_fs = 0;
573         unsigned int best_div = 0;
574         unsigned int best_rate = 0;
575         unsigned int best_deviation = INT_MAX;
576
577         pr_debug("Input clock rate %ldHz\n", clkrate);
578
579         if (fstab == NULL)
580                 fstab = iis_fs_tab;
581
582         for (fs = 0; fs < ARRAY_SIZE(iis_fs_tab); fs++) {
583                 fsdiv = iis_fs_tab[fs];
584
585                 fsclk = clkrate / fsdiv;
586                 div = fsclk / rate;
587
588                 if ((fsclk % rate) > (rate / 2))
589                         div++;
590
591                 if (div <= 1)
592                         continue;
593
594                 actual = clkrate / (fsdiv * div);
595                 deviation = actual - rate;
596
597                 printk(KERN_DEBUG "%ufs: div %u => result %u, deviation %d\n",
598                        fsdiv, div, actual, deviation);
599
600                 deviation = abs(deviation);
601
602                 if (deviation < best_deviation) {
603                         best_fs = fsdiv;
604                         best_div = div;
605                         best_rate = actual;
606                         best_deviation = deviation;
607                 }
608
609                 if (deviation == 0)
610                         break;
611         }
612
613         printk(KERN_DEBUG "best: fs=%u, div=%u, rate=%u\n",
614                best_fs, best_div, best_rate);
615
616         info->fs_div = best_fs;
617         info->clk_div = best_div;
618
619         return 0;
620 }
621 EXPORT_SYMBOL_GPL(s3c_i2sv2_iis_calc_rate);
622
623 int s3c_i2sv2_probe(struct snd_soc_dai *dai,
624                     struct s3c_i2sv2_info *i2s,
625                     unsigned long base)
626 {
627         struct device *dev = dai->dev;
628         unsigned int iismod;
629
630         i2s->dev = dev;
631
632         /* record our i2s structure for later use in the callbacks */
633         snd_soc_dai_set_drvdata(dai, i2s);
634
635         i2s->iis_pclk = clk_get(dev, "iis");
636         if (IS_ERR(i2s->iis_pclk)) {
637                 dev_err(dev, "failed to get iis_clock\n");
638                 iounmap(i2s->regs);
639                 return -ENOENT;
640         }
641
642         clk_enable(i2s->iis_pclk);
643
644         /* Mark ourselves as in TXRX mode so we can run through our cleanup
645          * process without warnings. */
646         iismod = readl(i2s->regs + S3C2412_IISMOD);
647         iismod |= S3C2412_IISMOD_MODE_TXRX;
648         writel(iismod, i2s->regs + S3C2412_IISMOD);
649         s3c2412_snd_txctrl(i2s, 0);
650         s3c2412_snd_rxctrl(i2s, 0);
651
652         return 0;
653 }
654 EXPORT_SYMBOL_GPL(s3c_i2sv2_probe);
655
656 #ifdef CONFIG_PM
657 static int s3c2412_i2s_suspend(struct snd_soc_dai *dai)
658 {
659         struct s3c_i2sv2_info *i2s = to_info(dai);
660         u32 iismod;
661
662         if (dai->active) {
663                 i2s->suspend_iismod = readl(i2s->regs + S3C2412_IISMOD);
664                 i2s->suspend_iiscon = readl(i2s->regs + S3C2412_IISCON);
665                 i2s->suspend_iispsr = readl(i2s->regs + S3C2412_IISPSR);
666
667                 /* some basic suspend checks */
668
669                 iismod = readl(i2s->regs + S3C2412_IISMOD);
670
671                 if (iismod & S3C2412_IISCON_RXDMA_ACTIVE)
672                         pr_warning("%s: RXDMA active?\n", __func__);
673
674                 if (iismod & S3C2412_IISCON_TXDMA_ACTIVE)
675                         pr_warning("%s: TXDMA active?\n", __func__);
676
677                 if (iismod & S3C2412_IISCON_IIS_ACTIVE)
678                         pr_warning("%s: IIS active\n", __func__);
679         }
680
681         return 0;
682 }
683
684 static int s3c2412_i2s_resume(struct snd_soc_dai *dai)
685 {
686         struct s3c_i2sv2_info *i2s = to_info(dai);
687
688         pr_info("dai_active %d, IISMOD %08x, IISCON %08x\n",
689                 dai->active, i2s->suspend_iismod, i2s->suspend_iiscon);
690
691         if (dai->active) {
692                 writel(i2s->suspend_iiscon, i2s->regs + S3C2412_IISCON);
693                 writel(i2s->suspend_iismod, i2s->regs + S3C2412_IISMOD);
694                 writel(i2s->suspend_iispsr, i2s->regs + S3C2412_IISPSR);
695
696                 writel(S3C2412_IISFIC_RXFLUSH | S3C2412_IISFIC_TXFLUSH,
697                        i2s->regs + S3C2412_IISFIC);
698
699                 ndelay(250);
700                 writel(0x0, i2s->regs + S3C2412_IISFIC);
701         }
702
703         return 0;
704 }
705 #else
706 #define s3c2412_i2s_suspend NULL
707 #define s3c2412_i2s_resume  NULL
708 #endif
709
710 int s3c_i2sv2_register_component(struct device *dev, int id,
711                            const struct snd_soc_component_driver *cmp_drv,
712                            struct snd_soc_dai_driver *dai_drv)
713 {
714         struct snd_soc_dai_ops *ops = (struct snd_soc_dai_ops *)dai_drv->ops;
715
716         ops->trigger = s3c2412_i2s_trigger;
717         if (!ops->hw_params)
718                 ops->hw_params = s3c_i2sv2_hw_params;
719         ops->set_fmt = s3c2412_i2s_set_fmt;
720         ops->set_clkdiv = s3c2412_i2s_set_clkdiv;
721         ops->set_sysclk = s3c_i2sv2_set_sysclk;
722
723         /* Allow overriding by (for example) IISv4 */
724         if (!ops->delay)
725                 ops->delay = s3c2412_i2s_delay;
726
727         dai_drv->suspend = s3c2412_i2s_suspend;
728         dai_drv->resume = s3c2412_i2s_resume;
729
730         return devm_snd_soc_register_component(dev, cmp_drv, dai_drv, 1);
731 }
732 EXPORT_SYMBOL_GPL(s3c_i2sv2_register_component);
733
734 MODULE_LICENSE("GPL");