Merge branch 'topic/docbook-fix' into for-linus
[linux-2.6] / 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
42 static inline struct s3c_i2sv2_info *to_info(struct snd_soc_dai *cpu_dai)
43 {
44         return cpu_dai->private_data;
45 }
46
47 #define bit_set(v, b) (((v) & (b)) ? 1 : 0)
48
49 #if S3C2412_I2S_DEBUG_CON
50 static void dbg_showcon(const char *fn, u32 con)
51 {
52         printk(KERN_DEBUG "%s: LRI=%d, TXFEMPT=%d, RXFEMPT=%d, TXFFULL=%d, RXFFULL=%d\n", fn,
53                bit_set(con, S3C2412_IISCON_LRINDEX),
54                bit_set(con, S3C2412_IISCON_TXFIFO_EMPTY),
55                bit_set(con, S3C2412_IISCON_RXFIFO_EMPTY),
56                bit_set(con, S3C2412_IISCON_TXFIFO_FULL),
57                bit_set(con, S3C2412_IISCON_RXFIFO_FULL));
58
59         printk(KERN_DEBUG "%s: PAUSE: TXDMA=%d, RXDMA=%d, TXCH=%d, RXCH=%d\n",
60                fn,
61                bit_set(con, S3C2412_IISCON_TXDMA_PAUSE),
62                bit_set(con, S3C2412_IISCON_RXDMA_PAUSE),
63                bit_set(con, S3C2412_IISCON_TXCH_PAUSE),
64                bit_set(con, S3C2412_IISCON_RXCH_PAUSE));
65         printk(KERN_DEBUG "%s: ACTIVE: TXDMA=%d, RXDMA=%d, IIS=%d\n", fn,
66                bit_set(con, S3C2412_IISCON_TXDMA_ACTIVE),
67                bit_set(con, S3C2412_IISCON_RXDMA_ACTIVE),
68                bit_set(con, S3C2412_IISCON_IIS_ACTIVE));
69 }
70 #else
71 static inline void dbg_showcon(const char *fn, u32 con)
72 {
73 }
74 #endif
75
76
77 /* Turn on or off the transmission path. */
78 void s3c2412_snd_txctrl(struct s3c_i2sv2_info *i2s, int on)
79 {
80         void __iomem *regs = i2s->regs;
81         u32 fic, con, mod;
82
83         pr_debug("%s(%d)\n", __func__, on);
84
85         fic = readl(regs + S3C2412_IISFIC);
86         con = readl(regs + S3C2412_IISCON);
87         mod = readl(regs + S3C2412_IISMOD);
88
89         pr_debug("%s: IIS: CON=%x MOD=%x FIC=%x\n", __func__, con, mod, fic);
90
91         if (on) {
92                 con |= S3C2412_IISCON_TXDMA_ACTIVE | S3C2412_IISCON_IIS_ACTIVE;
93                 con &= ~S3C2412_IISCON_TXDMA_PAUSE;
94                 con &= ~S3C2412_IISCON_TXCH_PAUSE;
95
96                 switch (mod & S3C2412_IISMOD_MODE_MASK) {
97                 case S3C2412_IISMOD_MODE_TXONLY:
98                 case S3C2412_IISMOD_MODE_TXRX:
99                         /* do nothing, we are in the right mode */
100                         break;
101
102                 case S3C2412_IISMOD_MODE_RXONLY:
103                         mod &= ~S3C2412_IISMOD_MODE_MASK;
104                         mod |= S3C2412_IISMOD_MODE_TXRX;
105                         break;
106
107                 default:
108                         dev_err(i2s->dev, "TXEN: Invalid MODE in IISMOD\n");
109                 }
110
111                 writel(con, regs + S3C2412_IISCON);
112                 writel(mod, regs + S3C2412_IISMOD);
113         } else {
114                 /* Note, we do not have any indication that the FIFO problems
115                  * tha the S3C2410/2440 had apply here, so we should be able
116                  * to disable the DMA and TX without resetting the FIFOS.
117                  */
118
119                 con |=  S3C2412_IISCON_TXDMA_PAUSE;
120                 con |=  S3C2412_IISCON_TXCH_PAUSE;
121                 con &= ~S3C2412_IISCON_TXDMA_ACTIVE;
122
123                 switch (mod & S3C2412_IISMOD_MODE_MASK) {
124                 case S3C2412_IISMOD_MODE_TXRX:
125                         mod &= ~S3C2412_IISMOD_MODE_MASK;
126                         mod |= S3C2412_IISMOD_MODE_RXONLY;
127                         break;
128
129                 case S3C2412_IISMOD_MODE_TXONLY:
130                         mod &= ~S3C2412_IISMOD_MODE_MASK;
131                         con &= ~S3C2412_IISCON_IIS_ACTIVE;
132                         break;
133
134                 default:
135                         dev_err(i2s->dev, "TXDIS: Invalid MODE in IISMOD\n");
136                 }
137
138                 writel(mod, regs + S3C2412_IISMOD);
139                 writel(con, regs + S3C2412_IISCON);
140         }
141
142         fic = readl(regs + S3C2412_IISFIC);
143         dbg_showcon(__func__, con);
144         pr_debug("%s: IIS: CON=%x MOD=%x FIC=%x\n", __func__, con, mod, fic);
145 }
146 EXPORT_SYMBOL_GPL(s3c2412_snd_txctrl);
147
148 void s3c2412_snd_rxctrl(struct s3c_i2sv2_info *i2s, int on)
149 {
150         void __iomem *regs = i2s->regs;
151         u32 fic, con, mod;
152
153         pr_debug("%s(%d)\n", __func__, on);
154
155         fic = readl(regs + S3C2412_IISFIC);
156         con = readl(regs + S3C2412_IISCON);
157         mod = readl(regs + S3C2412_IISMOD);
158
159         pr_debug("%s: IIS: CON=%x MOD=%x FIC=%x\n", __func__, con, mod, fic);
160
161         if (on) {
162                 con |= S3C2412_IISCON_RXDMA_ACTIVE | S3C2412_IISCON_IIS_ACTIVE;
163                 con &= ~S3C2412_IISCON_RXDMA_PAUSE;
164                 con &= ~S3C2412_IISCON_RXCH_PAUSE;
165
166                 switch (mod & S3C2412_IISMOD_MODE_MASK) {
167                 case S3C2412_IISMOD_MODE_TXRX:
168                 case S3C2412_IISMOD_MODE_RXONLY:
169                         /* do nothing, we are in the right mode */
170                         break;
171
172                 case S3C2412_IISMOD_MODE_TXONLY:
173                         mod &= ~S3C2412_IISMOD_MODE_MASK;
174                         mod |= S3C2412_IISMOD_MODE_TXRX;
175                         break;
176
177                 default:
178                         dev_err(i2s->dev, "RXEN: Invalid MODE in IISMOD\n");
179                 }
180
181                 writel(mod, regs + S3C2412_IISMOD);
182                 writel(con, regs + S3C2412_IISCON);
183         } else {
184                 /* See txctrl notes on FIFOs. */
185
186                 con &= ~S3C2412_IISCON_RXDMA_ACTIVE;
187                 con |=  S3C2412_IISCON_RXDMA_PAUSE;
188                 con |=  S3C2412_IISCON_RXCH_PAUSE;
189
190                 switch (mod & S3C2412_IISMOD_MODE_MASK) {
191                 case S3C2412_IISMOD_MODE_RXONLY:
192                         con &= ~S3C2412_IISCON_IIS_ACTIVE;
193                         mod &= ~S3C2412_IISMOD_MODE_MASK;
194                         break;
195
196                 case S3C2412_IISMOD_MODE_TXRX:
197                         mod &= ~S3C2412_IISMOD_MODE_MASK;
198                         mod |= S3C2412_IISMOD_MODE_TXONLY;
199                         break;
200
201                 default:
202                         dev_err(i2s->dev, "RXEN: Invalid MODE in IISMOD\n");
203                 }
204
205                 writel(con, regs + S3C2412_IISCON);
206                 writel(mod, regs + S3C2412_IISMOD);
207         }
208
209         fic = readl(regs + S3C2412_IISFIC);
210         pr_debug("%s: IIS: CON=%x MOD=%x FIC=%x\n", __func__, con, mod, fic);
211 }
212 EXPORT_SYMBOL_GPL(s3c2412_snd_rxctrl);
213
214 /*
215  * Wait for the LR signal to allow synchronisation to the L/R clock
216  * from the codec. May only be needed for slave mode.
217  */
218 static int s3c2412_snd_lrsync(struct s3c_i2sv2_info *i2s)
219 {
220         u32 iiscon;
221         unsigned long timeout = jiffies + msecs_to_jiffies(5);
222
223         pr_debug("Entered %s\n", __func__);
224
225         while (1) {
226                 iiscon = readl(i2s->regs + S3C2412_IISCON);
227                 if (iiscon & S3C2412_IISCON_LRINDEX)
228                         break;
229
230                 if (timeout < jiffies) {
231                         printk(KERN_ERR "%s: timeout\n", __func__);
232                         return -ETIMEDOUT;
233                 }
234         }
235
236         return 0;
237 }
238
239 /*
240  * Set S3C2412 I2S DAI format
241  */
242 static int s3c2412_i2s_set_fmt(struct snd_soc_dai *cpu_dai,
243                                unsigned int fmt)
244 {
245         struct s3c_i2sv2_info *i2s = to_info(cpu_dai);
246         u32 iismod;
247
248         pr_debug("Entered %s\n", __func__);
249
250         iismod = readl(i2s->regs + S3C2412_IISMOD);
251         pr_debug("hw_params r: IISMOD: %x \n", iismod);
252
253 #if defined(CONFIG_CPU_S3C2412) || defined(CONFIG_CPU_S3C2413)
254 #define IISMOD_MASTER_MASK S3C2412_IISMOD_MASTER_MASK
255 #define IISMOD_SLAVE S3C2412_IISMOD_SLAVE
256 #define IISMOD_MASTER S3C2412_IISMOD_MASTER_INTERNAL
257 #endif
258
259 #if defined(CONFIG_PLAT_S3C64XX)
260 /* From Rev1.1 datasheet, we have two master and two slave modes:
261  * IMS[11:10]:
262  *      00 = master mode, fed from PCLK
263  *      01 = master mode, fed from CLKAUDIO
264  *      10 = slave mode, using PCLK
265  *      11 = slave mode, using I2SCLK
266  */
267 #define IISMOD_MASTER_MASK (1 << 11)
268 #define IISMOD_SLAVE (1 << 11)
269 #define IISMOD_MASTER (0x0)
270 #endif
271
272         switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
273         case SND_SOC_DAIFMT_CBM_CFM:
274                 i2s->master = 0;
275                 iismod &= ~IISMOD_MASTER_MASK;
276                 iismod |= IISMOD_SLAVE;
277                 break;
278         case SND_SOC_DAIFMT_CBS_CFS:
279                 i2s->master = 1;
280                 iismod &= ~IISMOD_MASTER_MASK;
281                 iismod |= IISMOD_MASTER;
282                 break;
283         default:
284                 pr_debug("unknwon master/slave format\n");
285                 return -EINVAL;
286         }
287
288         iismod &= ~S3C2412_IISMOD_SDF_MASK;
289
290         switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
291         case SND_SOC_DAIFMT_RIGHT_J:
292                 iismod |= S3C2412_IISMOD_SDF_MSB;
293                 break;
294         case SND_SOC_DAIFMT_LEFT_J:
295                 iismod |= S3C2412_IISMOD_SDF_LSB;
296                 break;
297         case SND_SOC_DAIFMT_I2S:
298                 iismod |= S3C2412_IISMOD_SDF_IIS;
299                 break;
300         default:
301                 pr_debug("Unknown data format\n");
302                 return -EINVAL;
303         }
304
305         writel(iismod, i2s->regs + S3C2412_IISMOD);
306         pr_debug("hw_params w: IISMOD: %x \n", iismod);
307         return 0;
308 }
309
310 static int s3c2412_i2s_hw_params(struct snd_pcm_substream *substream,
311                                  struct snd_pcm_hw_params *params,
312                                  struct snd_soc_dai *socdai)
313 {
314         struct snd_soc_pcm_runtime *rtd = substream->private_data;
315         struct snd_soc_dai_link *dai = rtd->dai;
316         struct s3c_i2sv2_info *i2s = to_info(dai->cpu_dai);
317         u32 iismod;
318
319         pr_debug("Entered %s\n", __func__);
320
321         if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
322                 dai->cpu_dai->dma_data = i2s->dma_playback;
323         else
324                 dai->cpu_dai->dma_data = i2s->dma_capture;
325
326         /* Working copies of register */
327         iismod = readl(i2s->regs + S3C2412_IISMOD);
328         pr_debug("%s: r: IISMOD: %x\n", __func__, iismod);
329
330         switch (params_format(params)) {
331         case SNDRV_PCM_FORMAT_S8:
332                 iismod |= S3C2412_IISMOD_8BIT;
333                 break;
334         case SNDRV_PCM_FORMAT_S16_LE:
335                 iismod &= ~S3C2412_IISMOD_8BIT;
336                 break;
337         }
338
339         writel(iismod, i2s->regs + S3C2412_IISMOD);
340         pr_debug("%s: w: IISMOD: %x\n", __func__, iismod);
341         return 0;
342 }
343
344 static int s3c2412_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
345                                struct snd_soc_dai *dai)
346 {
347         struct snd_soc_pcm_runtime *rtd = substream->private_data;
348         struct s3c_i2sv2_info *i2s = to_info(rtd->dai->cpu_dai);
349         int capture = (substream->stream == SNDRV_PCM_STREAM_CAPTURE);
350         unsigned long irqs;
351         int ret = 0;
352
353         pr_debug("Entered %s\n", __func__);
354
355         switch (cmd) {
356         case SNDRV_PCM_TRIGGER_START:
357                 /* On start, ensure that the FIFOs are cleared and reset. */
358
359                 writel(capture ? S3C2412_IISFIC_RXFLUSH : S3C2412_IISFIC_TXFLUSH,
360                        i2s->regs + S3C2412_IISFIC);
361
362                 /* clear again, just in case */
363                 writel(0x0, i2s->regs + S3C2412_IISFIC);
364
365         case SNDRV_PCM_TRIGGER_RESUME:
366         case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
367                 if (!i2s->master) {
368                         ret = s3c2412_snd_lrsync(i2s);
369                         if (ret)
370                                 goto exit_err;
371                 }
372
373                 local_irq_save(irqs);
374
375                 if (capture)
376                         s3c2412_snd_rxctrl(i2s, 1);
377                 else
378                         s3c2412_snd_txctrl(i2s, 1);
379
380                 local_irq_restore(irqs);
381                 break;
382
383         case SNDRV_PCM_TRIGGER_STOP:
384         case SNDRV_PCM_TRIGGER_SUSPEND:
385         case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
386                 local_irq_save(irqs);
387
388                 if (capture)
389                         s3c2412_snd_rxctrl(i2s, 0);
390                 else
391                         s3c2412_snd_txctrl(i2s, 0);
392
393                 local_irq_restore(irqs);
394                 break;
395         default:
396                 ret = -EINVAL;
397                 break;
398         }
399
400 exit_err:
401         return ret;
402 }
403
404 /*
405  * Set S3C2412 Clock dividers
406  */
407 static int s3c2412_i2s_set_clkdiv(struct snd_soc_dai *cpu_dai,
408                                   int div_id, int div)
409 {
410         struct s3c_i2sv2_info *i2s = to_info(cpu_dai);
411         u32 reg;
412
413         pr_debug("%s(%p, %d, %d)\n", __func__, cpu_dai, div_id, div);
414
415         switch (div_id) {
416         case S3C_I2SV2_DIV_BCLK:
417                 reg = readl(i2s->regs + S3C2412_IISMOD);
418                 reg &= ~S3C2412_IISMOD_BCLK_MASK;
419                 writel(reg | div, i2s->regs + S3C2412_IISMOD);
420
421                 pr_debug("%s: MOD=%08x\n", __func__, readl(i2s->regs + S3C2412_IISMOD));
422                 break;
423
424         case S3C_I2SV2_DIV_RCLK:
425                 if (div > 3) {
426                         /* convert value to bit field */
427
428                         switch (div) {
429                         case 256:
430                                 div = S3C2412_IISMOD_RCLK_256FS;
431                                 break;
432
433                         case 384:
434                                 div = S3C2412_IISMOD_RCLK_384FS;
435                                 break;
436
437                         case 512:
438                                 div = S3C2412_IISMOD_RCLK_512FS;
439                                 break;
440
441                         case 768:
442                                 div = S3C2412_IISMOD_RCLK_768FS;
443                                 break;
444
445                         default:
446                                 return -EINVAL;
447                         }
448                 }
449
450                 reg = readl(i2s->regs + S3C2412_IISMOD);
451                 reg &= ~S3C2412_IISMOD_RCLK_MASK;
452                 writel(reg | div, i2s->regs + S3C2412_IISMOD);
453                 pr_debug("%s: MOD=%08x\n", __func__, readl(i2s->regs + S3C2412_IISMOD));
454                 break;
455
456         case S3C_I2SV2_DIV_PRESCALER:
457                 if (div >= 0) {
458                         writel((div << 8) | S3C2412_IISPSR_PSREN,
459                                i2s->regs + S3C2412_IISPSR);
460                 } else {
461                         writel(0x0, i2s->regs + S3C2412_IISPSR);
462                 }
463                 pr_debug("%s: PSR=%08x\n", __func__, readl(i2s->regs + S3C2412_IISPSR));
464                 break;
465
466         default:
467                 return -EINVAL;
468         }
469
470         return 0;
471 }
472
473 /* default table of all avaialable root fs divisors */
474 static unsigned int iis_fs_tab[] = { 256, 512, 384, 768 };
475
476 int s3c2412_iis_calc_rate(struct s3c_i2sv2_rate_calc *info,
477                           unsigned int *fstab,
478                           unsigned int rate, struct clk *clk)
479 {
480         unsigned long clkrate = clk_get_rate(clk);
481         unsigned int div;
482         unsigned int fsclk;
483         unsigned int actual;
484         unsigned int fs;
485         unsigned int fsdiv;
486         signed int deviation = 0;
487         unsigned int best_fs = 0;
488         unsigned int best_div = 0;
489         unsigned int best_rate = 0;
490         unsigned int best_deviation = INT_MAX;
491
492         if (fstab == NULL)
493                 fstab = iis_fs_tab;
494
495         for (fs = 0; fs < ARRAY_SIZE(iis_fs_tab); fs++) {
496                 fsdiv = iis_fs_tab[fs];
497
498                 fsclk = clkrate / fsdiv;
499                 div = fsclk / rate;
500
501                 if ((fsclk % rate) > (rate / 2))
502                         div++;
503
504                 if (div <= 1)
505                         continue;
506
507                 actual = clkrate / (fsdiv * div);
508                 deviation = actual - rate;
509
510                 printk(KERN_DEBUG "%dfs: div %d => result %d, deviation %d\n",
511                        fsdiv, div, actual, deviation);
512
513                 deviation = abs(deviation);
514
515                 if (deviation < best_deviation) {
516                         best_fs = fsdiv;
517                         best_div = div;
518                         best_rate = actual;
519                         best_deviation = deviation;
520                 }
521
522                 if (deviation == 0)
523                         break;
524         }
525
526         printk(KERN_DEBUG "best: fs=%d, div=%d, rate=%d\n",
527                best_fs, best_div, best_rate);
528
529         info->fs_div = best_fs;
530         info->clk_div = best_div;
531
532         return 0;
533 }
534 EXPORT_SYMBOL_GPL(s3c2412_iis_calc_rate);
535
536 int s3c_i2sv2_probe(struct platform_device *pdev,
537                     struct snd_soc_dai *dai,
538                     struct s3c_i2sv2_info *i2s,
539                     unsigned long base)
540 {
541         struct device *dev = &pdev->dev;
542
543         i2s->dev = dev;
544
545         /* record our i2s structure for later use in the callbacks */
546         dai->private_data = i2s;
547
548         i2s->regs = ioremap(base, 0x100);
549         if (i2s->regs == NULL) {
550                 dev_err(dev, "cannot ioremap registers\n");
551                 return -ENXIO;
552         }
553
554         i2s->iis_pclk = clk_get(dev, "iis");
555         if (i2s->iis_pclk == NULL) {
556                 dev_err(dev, "failed to get iis_clock\n");
557                 iounmap(i2s->regs);
558                 return -ENOENT;
559         }
560
561         clk_enable(i2s->iis_pclk);
562
563         s3c2412_snd_txctrl(i2s, 0);
564         s3c2412_snd_rxctrl(i2s, 0);
565
566         return 0;
567 }
568
569 EXPORT_SYMBOL_GPL(s3c_i2sv2_probe);
570
571 #ifdef CONFIG_PM
572 static int s3c2412_i2s_suspend(struct snd_soc_dai *dai)
573 {
574         struct s3c_i2sv2_info *i2s = to_info(dai);
575         u32 iismod;
576
577         if (dai->active) {
578                 i2s->suspend_iismod = readl(i2s->regs + S3C2412_IISMOD);
579                 i2s->suspend_iiscon = readl(i2s->regs + S3C2412_IISCON);
580                 i2s->suspend_iispsr = readl(i2s->regs + S3C2412_IISPSR);
581
582                 /* some basic suspend checks */
583
584                 iismod = readl(i2s->regs + S3C2412_IISMOD);
585
586                 if (iismod & S3C2412_IISCON_RXDMA_ACTIVE)
587                         pr_warning("%s: RXDMA active?\n", __func__);
588
589                 if (iismod & S3C2412_IISCON_TXDMA_ACTIVE)
590                         pr_warning("%s: TXDMA active?\n", __func__);
591
592                 if (iismod & S3C2412_IISCON_IIS_ACTIVE)
593                         pr_warning("%s: IIS active\n", __func__);
594         }
595
596         return 0;
597 }
598
599 static int s3c2412_i2s_resume(struct snd_soc_dai *dai)
600 {
601         struct s3c_i2sv2_info *i2s = to_info(dai);
602
603         pr_info("dai_active %d, IISMOD %08x, IISCON %08x\n",
604                 dai->active, i2s->suspend_iismod, i2s->suspend_iiscon);
605
606         if (dai->active) {
607                 writel(i2s->suspend_iiscon, i2s->regs + S3C2412_IISCON);
608                 writel(i2s->suspend_iismod, i2s->regs + S3C2412_IISMOD);
609                 writel(i2s->suspend_iispsr, i2s->regs + S3C2412_IISPSR);
610
611                 writel(S3C2412_IISFIC_RXFLUSH | S3C2412_IISFIC_TXFLUSH,
612                        i2s->regs + S3C2412_IISFIC);
613
614                 ndelay(250);
615                 writel(0x0, i2s->regs + S3C2412_IISFIC);
616         }
617
618         return 0;
619 }
620 #else
621 #define s3c2412_i2s_suspend NULL
622 #define s3c2412_i2s_resume  NULL
623 #endif
624
625 int s3c_i2sv2_register_dai(struct snd_soc_dai *dai)
626 {
627         dai->ops.trigger = s3c2412_i2s_trigger;
628         dai->ops.hw_params = s3c2412_i2s_hw_params;
629         dai->ops.set_fmt = s3c2412_i2s_set_fmt;
630         dai->ops.set_clkdiv = s3c2412_i2s_set_clkdiv;
631
632         dai->suspend = s3c2412_i2s_suspend;
633         dai->resume = s3c2412_i2s_resume;
634
635         return snd_soc_register_dai(dai);
636 }
637
638 EXPORT_SYMBOL_GPL(s3c_i2sv2_register_dai);