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