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