ASoC: Split s3c2412-i2s.c into core and SoC specific parts
[pandora-kernel.git] / sound / soc / s3c24xx / s3c-i2s-v2.c
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
49 static 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
57 static 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
78 static inline void dbg_showcon(const char *fn, u32 con)
79 {
80 }
81 #endif
82
83
84 /* Turn on or off the transmission path. */
85 void 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 }
153 EXPORT_SYMBOL_GPL(s3c2412_snd_txctrl);
154
155 void 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 }
219 EXPORT_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  */
225 static 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  */
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         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
317 static 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
351 static 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
407 exit_err:
408         return ret;
409 }
410
411 /*
412  * Set S3C2412 Clock dividers
413  */
414 static 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 */
481 static unsigned int iis_fs_tab[] = { 256, 512, 384, 768 };
482
483 int 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 }
541 EXPORT_SYMBOL_GPL(s3c2412_iis_calc_rate);
542
543 int 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
576 EXPORT_SYMBOL_GPL(s3c_i2sv2_probe);
577
578 #ifdef CONFIG_PM
579 static 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
606 static 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
632 int 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
645 EXPORT_SYMBOL_GPL(s3c_i2sv2_register_dai);