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