Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0+
0002 //
0003 // ALSA Soc Audio Layer - I2S core for newer Samsung SoCs.
0004 //
0005 // Copyright (c) 2006 Wolfson Microelectronics PLC.
0006 //  Graeme Gregory graeme.gregory@wolfsonmicro.com
0007 //  linux@wolfsonmicro.com
0008 //
0009 // Copyright (c) 2008, 2007, 2004-2005 Simtec Electronics
0010 //  http://armlinux.simtec.co.uk/
0011 //  Ben Dooks <ben@simtec.co.uk>
0012 
0013 #include <linux/module.h>
0014 #include <linux/delay.h>
0015 #include <linux/clk.h>
0016 #include <linux/io.h>
0017 
0018 #include <sound/soc.h>
0019 #include <sound/pcm_params.h>
0020 
0021 #include "regs-i2s-v2.h"
0022 #include "s3c-i2s-v2.h"
0023 
0024 #define S3C2412_I2S_DEBUG_CON 0
0025 
0026 static inline struct s3c_i2sv2_info *to_info(struct snd_soc_dai *cpu_dai)
0027 {
0028     return snd_soc_dai_get_drvdata(cpu_dai);
0029 }
0030 
0031 #define bit_set(v, b) (((v) & (b)) ? 1 : 0)
0032 
0033 #if S3C2412_I2S_DEBUG_CON
0034 static void dbg_showcon(const char *fn, u32 con)
0035 {
0036     printk(KERN_DEBUG "%s: LRI=%d, TXFEMPT=%d, RXFEMPT=%d, TXFFULL=%d, RXFFULL=%d\n", fn,
0037            bit_set(con, S3C2412_IISCON_LRINDEX),
0038            bit_set(con, S3C2412_IISCON_TXFIFO_EMPTY),
0039            bit_set(con, S3C2412_IISCON_RXFIFO_EMPTY),
0040            bit_set(con, S3C2412_IISCON_TXFIFO_FULL),
0041            bit_set(con, S3C2412_IISCON_RXFIFO_FULL));
0042 
0043     printk(KERN_DEBUG "%s: PAUSE: TXDMA=%d, RXDMA=%d, TXCH=%d, RXCH=%d\n",
0044            fn,
0045            bit_set(con, S3C2412_IISCON_TXDMA_PAUSE),
0046            bit_set(con, S3C2412_IISCON_RXDMA_PAUSE),
0047            bit_set(con, S3C2412_IISCON_TXCH_PAUSE),
0048            bit_set(con, S3C2412_IISCON_RXCH_PAUSE));
0049     printk(KERN_DEBUG "%s: ACTIVE: TXDMA=%d, RXDMA=%d, IIS=%d\n", fn,
0050            bit_set(con, S3C2412_IISCON_TXDMA_ACTIVE),
0051            bit_set(con, S3C2412_IISCON_RXDMA_ACTIVE),
0052            bit_set(con, S3C2412_IISCON_IIS_ACTIVE));
0053 }
0054 #else
0055 static inline void dbg_showcon(const char *fn, u32 con)
0056 {
0057 }
0058 #endif
0059 
0060 /* Turn on or off the transmission path. */
0061 static void s3c2412_snd_txctrl(struct s3c_i2sv2_info *i2s, int on)
0062 {
0063     void __iomem *regs = i2s->regs;
0064     u32 fic, con, mod;
0065 
0066     pr_debug("%s(%d)\n", __func__, on);
0067 
0068     fic = readl(regs + S3C2412_IISFIC);
0069     con = readl(regs + S3C2412_IISCON);
0070     mod = readl(regs + S3C2412_IISMOD);
0071 
0072     pr_debug("%s: IIS: CON=%x MOD=%x FIC=%x\n", __func__, con, mod, fic);
0073 
0074     if (on) {
0075         con |= S3C2412_IISCON_TXDMA_ACTIVE | S3C2412_IISCON_IIS_ACTIVE;
0076         con &= ~S3C2412_IISCON_TXDMA_PAUSE;
0077         con &= ~S3C2412_IISCON_TXCH_PAUSE;
0078 
0079         switch (mod & S3C2412_IISMOD_MODE_MASK) {
0080         case S3C2412_IISMOD_MODE_TXONLY:
0081         case S3C2412_IISMOD_MODE_TXRX:
0082             /* do nothing, we are in the right mode */
0083             break;
0084 
0085         case S3C2412_IISMOD_MODE_RXONLY:
0086             mod &= ~S3C2412_IISMOD_MODE_MASK;
0087             mod |= S3C2412_IISMOD_MODE_TXRX;
0088             break;
0089 
0090         default:
0091             dev_err(i2s->dev, "TXEN: Invalid MODE %x in IISMOD\n",
0092                 mod & S3C2412_IISMOD_MODE_MASK);
0093             break;
0094         }
0095 
0096         writel(con, regs + S3C2412_IISCON);
0097         writel(mod, regs + S3C2412_IISMOD);
0098     } else {
0099         /* Note, we do not have any indication that the FIFO problems
0100          * tha the S3C2410/2440 had apply here, so we should be able
0101          * to disable the DMA and TX without resetting the FIFOS.
0102          */
0103 
0104         con |=  S3C2412_IISCON_TXDMA_PAUSE;
0105         con |=  S3C2412_IISCON_TXCH_PAUSE;
0106         con &= ~S3C2412_IISCON_TXDMA_ACTIVE;
0107 
0108         switch (mod & S3C2412_IISMOD_MODE_MASK) {
0109         case S3C2412_IISMOD_MODE_TXRX:
0110             mod &= ~S3C2412_IISMOD_MODE_MASK;
0111             mod |= S3C2412_IISMOD_MODE_RXONLY;
0112             break;
0113 
0114         case S3C2412_IISMOD_MODE_TXONLY:
0115             mod &= ~S3C2412_IISMOD_MODE_MASK;
0116             con &= ~S3C2412_IISCON_IIS_ACTIVE;
0117             break;
0118 
0119         default:
0120             dev_err(i2s->dev, "TXDIS: Invalid MODE %x in IISMOD\n",
0121                 mod & S3C2412_IISMOD_MODE_MASK);
0122             break;
0123         }
0124 
0125         writel(mod, regs + S3C2412_IISMOD);
0126         writel(con, regs + S3C2412_IISCON);
0127     }
0128 
0129     fic = readl(regs + S3C2412_IISFIC);
0130     dbg_showcon(__func__, con);
0131     pr_debug("%s: IIS: CON=%x MOD=%x FIC=%x\n", __func__, con, mod, fic);
0132 }
0133 
0134 static void s3c2412_snd_rxctrl(struct s3c_i2sv2_info *i2s, int on)
0135 {
0136     void __iomem *regs = i2s->regs;
0137     u32 fic, con, mod;
0138 
0139     pr_debug("%s(%d)\n", __func__, on);
0140 
0141     fic = readl(regs + S3C2412_IISFIC);
0142     con = readl(regs + S3C2412_IISCON);
0143     mod = readl(regs + S3C2412_IISMOD);
0144 
0145     pr_debug("%s: IIS: CON=%x MOD=%x FIC=%x\n", __func__, con, mod, fic);
0146 
0147     if (on) {
0148         con |= S3C2412_IISCON_RXDMA_ACTIVE | S3C2412_IISCON_IIS_ACTIVE;
0149         con &= ~S3C2412_IISCON_RXDMA_PAUSE;
0150         con &= ~S3C2412_IISCON_RXCH_PAUSE;
0151 
0152         switch (mod & S3C2412_IISMOD_MODE_MASK) {
0153         case S3C2412_IISMOD_MODE_TXRX:
0154         case S3C2412_IISMOD_MODE_RXONLY:
0155             /* do nothing, we are in the right mode */
0156             break;
0157 
0158         case S3C2412_IISMOD_MODE_TXONLY:
0159             mod &= ~S3C2412_IISMOD_MODE_MASK;
0160             mod |= S3C2412_IISMOD_MODE_TXRX;
0161             break;
0162 
0163         default:
0164             dev_err(i2s->dev, "RXEN: Invalid MODE %x in IISMOD\n",
0165                 mod & S3C2412_IISMOD_MODE_MASK);
0166         }
0167 
0168         writel(mod, regs + S3C2412_IISMOD);
0169         writel(con, regs + S3C2412_IISCON);
0170     } else {
0171         /* See txctrl notes on FIFOs. */
0172 
0173         con &= ~S3C2412_IISCON_RXDMA_ACTIVE;
0174         con |=  S3C2412_IISCON_RXDMA_PAUSE;
0175         con |=  S3C2412_IISCON_RXCH_PAUSE;
0176 
0177         switch (mod & S3C2412_IISMOD_MODE_MASK) {
0178         case S3C2412_IISMOD_MODE_RXONLY:
0179             con &= ~S3C2412_IISCON_IIS_ACTIVE;
0180             mod &= ~S3C2412_IISMOD_MODE_MASK;
0181             break;
0182 
0183         case S3C2412_IISMOD_MODE_TXRX:
0184             mod &= ~S3C2412_IISMOD_MODE_MASK;
0185             mod |= S3C2412_IISMOD_MODE_TXONLY;
0186             break;
0187 
0188         default:
0189             dev_err(i2s->dev, "RXDIS: Invalid MODE %x in IISMOD\n",
0190                 mod & S3C2412_IISMOD_MODE_MASK);
0191         }
0192 
0193         writel(con, regs + S3C2412_IISCON);
0194         writel(mod, regs + S3C2412_IISMOD);
0195     }
0196 
0197     fic = readl(regs + S3C2412_IISFIC);
0198     pr_debug("%s: IIS: CON=%x MOD=%x FIC=%x\n", __func__, con, mod, fic);
0199 }
0200 
0201 #define msecs_to_loops(t) (loops_per_jiffy / 1000 * HZ * t)
0202 
0203 /*
0204  * Wait for the LR signal to allow synchronisation to the L/R clock
0205  * from the codec. May only be needed for slave mode.
0206  */
0207 static int s3c2412_snd_lrsync(struct s3c_i2sv2_info *i2s)
0208 {
0209     u32 iiscon;
0210     unsigned long loops = msecs_to_loops(5);
0211 
0212     pr_debug("Entered %s\n", __func__);
0213 
0214     while (--loops) {
0215         iiscon = readl(i2s->regs + S3C2412_IISCON);
0216         if (iiscon & S3C2412_IISCON_LRINDEX)
0217             break;
0218 
0219         cpu_relax();
0220     }
0221 
0222     if (!loops) {
0223         printk(KERN_ERR "%s: timeout\n", __func__);
0224         return -ETIMEDOUT;
0225     }
0226 
0227     return 0;
0228 }
0229 
0230 /*
0231  * Set S3C2412 I2S DAI format
0232  */
0233 static int s3c2412_i2s_set_fmt(struct snd_soc_dai *cpu_dai,
0234                    unsigned int fmt)
0235 {
0236     struct s3c_i2sv2_info *i2s = to_info(cpu_dai);
0237     u32 iismod;
0238 
0239     pr_debug("Entered %s\n", __func__);
0240 
0241     iismod = readl(i2s->regs + S3C2412_IISMOD);
0242     pr_debug("hw_params r: IISMOD: %x \n", iismod);
0243 
0244     switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
0245     case SND_SOC_DAIFMT_BC_FC:
0246         i2s->master = 0;
0247         iismod |= S3C2412_IISMOD_SLAVE;
0248         break;
0249     case SND_SOC_DAIFMT_BP_FP:
0250         i2s->master = 1;
0251         iismod &= ~S3C2412_IISMOD_SLAVE;
0252         break;
0253     default:
0254         pr_err("unknown master/slave format\n");
0255         return -EINVAL;
0256     }
0257 
0258     iismod &= ~S3C2412_IISMOD_SDF_MASK;
0259 
0260     switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
0261     case SND_SOC_DAIFMT_RIGHT_J:
0262         iismod |= S3C2412_IISMOD_LR_RLOW;
0263         iismod |= S3C2412_IISMOD_SDF_MSB;
0264         break;
0265     case SND_SOC_DAIFMT_LEFT_J:
0266         iismod |= S3C2412_IISMOD_LR_RLOW;
0267         iismod |= S3C2412_IISMOD_SDF_LSB;
0268         break;
0269     case SND_SOC_DAIFMT_I2S:
0270         iismod &= ~S3C2412_IISMOD_LR_RLOW;
0271         iismod |= S3C2412_IISMOD_SDF_IIS;
0272         break;
0273     default:
0274         pr_err("Unknown data format\n");
0275         return -EINVAL;
0276     }
0277 
0278     writel(iismod, i2s->regs + S3C2412_IISMOD);
0279     pr_debug("hw_params w: IISMOD: %x \n", iismod);
0280     return 0;
0281 }
0282 
0283 static int s3c_i2sv2_hw_params(struct snd_pcm_substream *substream,
0284                  struct snd_pcm_hw_params *params,
0285                  struct snd_soc_dai *dai)
0286 {
0287     struct s3c_i2sv2_info *i2s = to_info(dai);
0288     struct snd_dmaengine_dai_dma_data *dma_data;
0289     u32 iismod;
0290 
0291     pr_debug("Entered %s\n", __func__);
0292 
0293     if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
0294         dma_data = i2s->dma_playback;
0295     else
0296         dma_data = i2s->dma_capture;
0297 
0298     snd_soc_dai_set_dma_data(dai, substream, dma_data);
0299 
0300     /* Working copies of register */
0301     iismod = readl(i2s->regs + S3C2412_IISMOD);
0302     pr_debug("%s: r: IISMOD: %x\n", __func__, iismod);
0303 
0304     iismod &= ~S3C64XX_IISMOD_BLC_MASK;
0305     /* Sample size */
0306     switch (params_width(params)) {
0307     case 8:
0308         iismod |= S3C64XX_IISMOD_BLC_8BIT;
0309         break;
0310     case 16:
0311         break;
0312     case 24:
0313         iismod |= S3C64XX_IISMOD_BLC_24BIT;
0314         break;
0315     }
0316 
0317     writel(iismod, i2s->regs + S3C2412_IISMOD);
0318     pr_debug("%s: w: IISMOD: %x\n", __func__, iismod);
0319 
0320     return 0;
0321 }
0322 
0323 static int s3c_i2sv2_set_sysclk(struct snd_soc_dai *cpu_dai,
0324                   int clk_id, unsigned int freq, int dir)
0325 {
0326     struct s3c_i2sv2_info *i2s = to_info(cpu_dai);
0327     u32 iismod = readl(i2s->regs + S3C2412_IISMOD);
0328 
0329     pr_debug("Entered %s\n", __func__);
0330     pr_debug("%s r: IISMOD: %x\n", __func__, iismod);
0331 
0332     switch (clk_id) {
0333     case S3C_I2SV2_CLKSRC_PCLK:
0334         iismod &= ~S3C2412_IISMOD_IMS_SYSMUX;
0335         break;
0336 
0337     case S3C_I2SV2_CLKSRC_AUDIOBUS:
0338         iismod |= S3C2412_IISMOD_IMS_SYSMUX;
0339         break;
0340 
0341     case S3C_I2SV2_CLKSRC_CDCLK:
0342         /* Error if controller doesn't have the CDCLKCON bit */
0343         if (!(i2s->feature & S3C_FEATURE_CDCLKCON))
0344             return -EINVAL;
0345 
0346         switch (dir) {
0347         case SND_SOC_CLOCK_IN:
0348             iismod |= S3C64XX_IISMOD_CDCLKCON;
0349             break;
0350         case SND_SOC_CLOCK_OUT:
0351             iismod &= ~S3C64XX_IISMOD_CDCLKCON;
0352             break;
0353         default:
0354             return -EINVAL;
0355         }
0356         break;
0357 
0358     default:
0359         return -EINVAL;
0360     }
0361 
0362     writel(iismod, i2s->regs + S3C2412_IISMOD);
0363     pr_debug("%s w: IISMOD: %x\n", __func__, iismod);
0364 
0365     return 0;
0366 }
0367 
0368 static int s3c2412_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
0369                    struct snd_soc_dai *dai)
0370 {
0371     struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
0372     struct s3c_i2sv2_info *i2s = to_info(asoc_rtd_to_cpu(rtd, 0));
0373     int capture = (substream->stream == SNDRV_PCM_STREAM_CAPTURE);
0374     unsigned long irqs;
0375     int ret = 0;
0376 
0377     pr_debug("Entered %s\n", __func__);
0378 
0379     switch (cmd) {
0380     case SNDRV_PCM_TRIGGER_START:
0381         /* On start, ensure that the FIFOs are cleared and reset. */
0382 
0383         writel(capture ? S3C2412_IISFIC_RXFLUSH : S3C2412_IISFIC_TXFLUSH,
0384                i2s->regs + S3C2412_IISFIC);
0385 
0386         /* clear again, just in case */
0387         writel(0x0, i2s->regs + S3C2412_IISFIC);
0388 
0389         fallthrough;
0390 
0391     case SNDRV_PCM_TRIGGER_RESUME:
0392     case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
0393         if (!i2s->master) {
0394             ret = s3c2412_snd_lrsync(i2s);
0395             if (ret)
0396                 goto exit_err;
0397         }
0398 
0399         local_irq_save(irqs);
0400 
0401         if (capture)
0402             s3c2412_snd_rxctrl(i2s, 1);
0403         else
0404             s3c2412_snd_txctrl(i2s, 1);
0405 
0406         local_irq_restore(irqs);
0407 
0408         break;
0409 
0410     case SNDRV_PCM_TRIGGER_STOP:
0411     case SNDRV_PCM_TRIGGER_SUSPEND:
0412     case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
0413         local_irq_save(irqs);
0414 
0415         if (capture)
0416             s3c2412_snd_rxctrl(i2s, 0);
0417         else
0418             s3c2412_snd_txctrl(i2s, 0);
0419 
0420         local_irq_restore(irqs);
0421         break;
0422     default:
0423         ret = -EINVAL;
0424         break;
0425     }
0426 
0427 exit_err:
0428     return ret;
0429 }
0430 
0431 /*
0432  * Set S3C2412 Clock dividers
0433  */
0434 static int s3c2412_i2s_set_clkdiv(struct snd_soc_dai *cpu_dai,
0435                   int div_id, int div)
0436 {
0437     struct s3c_i2sv2_info *i2s = to_info(cpu_dai);
0438     u32 reg;
0439 
0440     pr_debug("%s(%p, %d, %d)\n", __func__, cpu_dai, div_id, div);
0441 
0442     switch (div_id) {
0443     case S3C_I2SV2_DIV_BCLK:
0444         switch (div) {
0445         case 16:
0446             div = S3C2412_IISMOD_BCLK_16FS;
0447             break;
0448 
0449         case 32:
0450             div = S3C2412_IISMOD_BCLK_32FS;
0451             break;
0452 
0453         case 24:
0454             div = S3C2412_IISMOD_BCLK_24FS;
0455             break;
0456 
0457         case 48:
0458             div = S3C2412_IISMOD_BCLK_48FS;
0459             break;
0460 
0461         default:
0462             return -EINVAL;
0463         }
0464 
0465         reg = readl(i2s->regs + S3C2412_IISMOD);
0466         reg &= ~S3C2412_IISMOD_BCLK_MASK;
0467         writel(reg | div, i2s->regs + S3C2412_IISMOD);
0468 
0469         pr_debug("%s: MOD=%08x\n", __func__, readl(i2s->regs + S3C2412_IISMOD));
0470         break;
0471 
0472     case S3C_I2SV2_DIV_RCLK:
0473         switch (div) {
0474         case 256:
0475             div = S3C2412_IISMOD_RCLK_256FS;
0476             break;
0477 
0478         case 384:
0479             div = S3C2412_IISMOD_RCLK_384FS;
0480             break;
0481 
0482         case 512:
0483             div = S3C2412_IISMOD_RCLK_512FS;
0484             break;
0485 
0486         case 768:
0487             div = S3C2412_IISMOD_RCLK_768FS;
0488             break;
0489 
0490         default:
0491             return -EINVAL;
0492         }
0493 
0494         reg = readl(i2s->regs + S3C2412_IISMOD);
0495         reg &= ~S3C2412_IISMOD_RCLK_MASK;
0496         writel(reg | div, i2s->regs + S3C2412_IISMOD);
0497         pr_debug("%s: MOD=%08x\n", __func__, readl(i2s->regs + S3C2412_IISMOD));
0498         break;
0499 
0500     case S3C_I2SV2_DIV_PRESCALER:
0501         if (div >= 0) {
0502             writel((div << 8) | S3C2412_IISPSR_PSREN,
0503                    i2s->regs + S3C2412_IISPSR);
0504         } else {
0505             writel(0x0, i2s->regs + S3C2412_IISPSR);
0506         }
0507         pr_debug("%s: PSR=%08x\n", __func__, readl(i2s->regs + S3C2412_IISPSR));
0508         break;
0509 
0510     default:
0511         return -EINVAL;
0512     }
0513 
0514     return 0;
0515 }
0516 
0517 static snd_pcm_sframes_t s3c2412_i2s_delay(struct snd_pcm_substream *substream,
0518                        struct snd_soc_dai *dai)
0519 {
0520     struct s3c_i2sv2_info *i2s = to_info(dai);
0521     u32 reg = readl(i2s->regs + S3C2412_IISFIC);
0522     snd_pcm_sframes_t delay;
0523 
0524     if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
0525         delay = S3C2412_IISFIC_TXCOUNT(reg);
0526     else
0527         delay = S3C2412_IISFIC_RXCOUNT(reg);
0528 
0529     return delay;
0530 }
0531 
0532 struct clk *s3c_i2sv2_get_clock(struct snd_soc_dai *cpu_dai)
0533 {
0534     struct s3c_i2sv2_info *i2s = to_info(cpu_dai);
0535     u32 iismod = readl(i2s->regs + S3C2412_IISMOD);
0536 
0537     if (iismod & S3C2412_IISMOD_IMS_SYSMUX)
0538         return i2s->iis_cclk;
0539     else
0540         return i2s->iis_pclk;
0541 }
0542 EXPORT_SYMBOL_GPL(s3c_i2sv2_get_clock);
0543 
0544 /* default table of all avaialable root fs divisors */
0545 static unsigned int iis_fs_tab[] = { 256, 512, 384, 768 };
0546 
0547 int s3c_i2sv2_iis_calc_rate(struct s3c_i2sv2_rate_calc *info,
0548                 unsigned int *fstab,
0549                 unsigned int rate, struct clk *clk)
0550 {
0551     unsigned long clkrate = clk_get_rate(clk);
0552     unsigned int div;
0553     unsigned int fsclk;
0554     unsigned int actual;
0555     unsigned int fs;
0556     unsigned int fsdiv;
0557     signed int deviation = 0;
0558     unsigned int best_fs = 0;
0559     unsigned int best_div = 0;
0560     unsigned int best_rate = 0;
0561     unsigned int best_deviation = INT_MAX;
0562 
0563     pr_debug("Input clock rate %ldHz\n", clkrate);
0564 
0565     if (fstab == NULL)
0566         fstab = iis_fs_tab;
0567 
0568     for (fs = 0; fs < ARRAY_SIZE(iis_fs_tab); fs++) {
0569         fsdiv = iis_fs_tab[fs];
0570 
0571         fsclk = clkrate / fsdiv;
0572         div = fsclk / rate;
0573 
0574         if ((fsclk % rate) > (rate / 2))
0575             div++;
0576 
0577         if (div <= 1)
0578             continue;
0579 
0580         actual = clkrate / (fsdiv * div);
0581         deviation = actual - rate;
0582 
0583         printk(KERN_DEBUG "%ufs: div %u => result %u, deviation %d\n",
0584                fsdiv, div, actual, deviation);
0585 
0586         deviation = abs(deviation);
0587 
0588         if (deviation < best_deviation) {
0589             best_fs = fsdiv;
0590             best_div = div;
0591             best_rate = actual;
0592             best_deviation = deviation;
0593         }
0594 
0595         if (deviation == 0)
0596             break;
0597     }
0598 
0599     printk(KERN_DEBUG "best: fs=%u, div=%u, rate=%u\n",
0600            best_fs, best_div, best_rate);
0601 
0602     info->fs_div = best_fs;
0603     info->clk_div = best_div;
0604 
0605     return 0;
0606 }
0607 EXPORT_SYMBOL_GPL(s3c_i2sv2_iis_calc_rate);
0608 
0609 int s3c_i2sv2_probe(struct snd_soc_dai *dai,
0610             struct s3c_i2sv2_info *i2s)
0611 {
0612     struct device *dev = dai->dev;
0613     unsigned int iismod;
0614 
0615     i2s->dev = dev;
0616 
0617     /* record our i2s structure for later use in the callbacks */
0618     snd_soc_dai_set_drvdata(dai, i2s);
0619 
0620     i2s->iis_pclk = clk_get(dev, "iis");
0621     if (IS_ERR(i2s->iis_pclk)) {
0622         dev_err(dev, "failed to get iis_clock\n");
0623         return -ENOENT;
0624     }
0625 
0626     clk_prepare_enable(i2s->iis_pclk);
0627 
0628     /* Mark ourselves as in TXRX mode so we can run through our cleanup
0629      * process without warnings. */
0630     iismod = readl(i2s->regs + S3C2412_IISMOD);
0631     iismod |= S3C2412_IISMOD_MODE_TXRX;
0632     writel(iismod, i2s->regs + S3C2412_IISMOD);
0633     s3c2412_snd_txctrl(i2s, 0);
0634     s3c2412_snd_rxctrl(i2s, 0);
0635 
0636     return 0;
0637 }
0638 EXPORT_SYMBOL_GPL(s3c_i2sv2_probe);
0639 
0640 void s3c_i2sv2_cleanup(struct snd_soc_dai *dai,
0641               struct s3c_i2sv2_info *i2s)
0642 {
0643     clk_disable_unprepare(i2s->iis_pclk);
0644     clk_put(i2s->iis_pclk);
0645     i2s->iis_pclk = NULL;
0646 }
0647 EXPORT_SYMBOL_GPL(s3c_i2sv2_cleanup);
0648 
0649 int s3c_i2sv2_register_component(struct device *dev, int id,
0650                const struct snd_soc_component_driver *cmp_drv,
0651                struct snd_soc_dai_driver *dai_drv)
0652 {
0653     struct snd_soc_dai_ops *ops = (struct snd_soc_dai_ops *)dai_drv->ops;
0654 
0655     ops->trigger = s3c2412_i2s_trigger;
0656     if (!ops->hw_params)
0657         ops->hw_params = s3c_i2sv2_hw_params;
0658     ops->set_fmt = s3c2412_i2s_set_fmt;
0659     ops->set_clkdiv = s3c2412_i2s_set_clkdiv;
0660     ops->set_sysclk = s3c_i2sv2_set_sysclk;
0661 
0662     /* Allow overriding by (for example) IISv4 */
0663     if (!ops->delay)
0664         ops->delay = s3c2412_i2s_delay;
0665 
0666     return devm_snd_soc_register_component(dev, cmp_drv, dai_drv, 1);
0667 }
0668 EXPORT_SYMBOL_GPL(s3c_i2sv2_register_component);
0669 
0670 MODULE_LICENSE("GPL");