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