linux/sound/soc/samsung/pcm.c
<<
>>
Prefs
   1/* sound/soc/samsung/pcm.c
   2 *
   3 * ALSA SoC Audio Layer - S3C PCM-Controller driver
   4 *
   5 * Copyright (c) 2009 Samsung Electronics Co. Ltd
   6 * Author: Jaswinder Singh <jassisinghbrar@gmail.com>
   7 * based upon I2S drivers by Ben Dooks.
   8 *
   9 * This program is free software; you can redistribute it and/or modify
  10 * it under the terms of the GNU General Public License version 2 as
  11 * published by the Free Software Foundation.
  12 */
  13
  14#include <linux/clk.h>
  15#include <linux/io.h>
  16#include <linux/module.h>
  17#include <linux/pm_runtime.h>
  18
  19#include <sound/soc.h>
  20#include <sound/pcm_params.h>
  21
  22#include <linux/platform_data/asoc-s3c.h>
  23#include <mach/dma.h>
  24
  25#include "dma.h"
  26#include "pcm.h"
  27
  28/*Register Offsets */
  29#define S3C_PCM_CTL             0x00
  30#define S3C_PCM_CLKCTL          0x04
  31#define S3C_PCM_TXFIFO          0x08
  32#define S3C_PCM_RXFIFO          0x0C
  33#define S3C_PCM_IRQCTL          0x10
  34#define S3C_PCM_IRQSTAT         0x14
  35#define S3C_PCM_FIFOSTAT        0x18
  36#define S3C_PCM_CLRINT          0x20
  37
  38/* PCM_CTL Bit-Fields */
  39#define S3C_PCM_CTL_TXDIPSTICK_MASK     0x3f
  40#define S3C_PCM_CTL_TXDIPSTICK_SHIFT    13
  41#define S3C_PCM_CTL_RXDIPSTICK_MASK     0x3f
  42#define S3C_PCM_CTL_RXDIPSTICK_SHIFT    7
  43#define S3C_PCM_CTL_TXDMA_EN            (0x1 << 6)
  44#define S3C_PCM_CTL_RXDMA_EN            (0x1 << 5)
  45#define S3C_PCM_CTL_TXMSB_AFTER_FSYNC   (0x1 << 4)
  46#define S3C_PCM_CTL_RXMSB_AFTER_FSYNC   (0x1 << 3)
  47#define S3C_PCM_CTL_TXFIFO_EN           (0x1 << 2)
  48#define S3C_PCM_CTL_RXFIFO_EN           (0x1 << 1)
  49#define S3C_PCM_CTL_ENABLE              (0x1 << 0)
  50
  51/* PCM_CLKCTL Bit-Fields */
  52#define S3C_PCM_CLKCTL_SERCLK_EN        (0x1 << 19)
  53#define S3C_PCM_CLKCTL_SERCLKSEL_PCLK   (0x1 << 18)
  54#define S3C_PCM_CLKCTL_SCLKDIV_MASK     0x1ff
  55#define S3C_PCM_CLKCTL_SYNCDIV_MASK     0x1ff
  56#define S3C_PCM_CLKCTL_SCLKDIV_SHIFT    9
  57#define S3C_PCM_CLKCTL_SYNCDIV_SHIFT    0
  58
  59/* PCM_TXFIFO Bit-Fields */
  60#define S3C_PCM_TXFIFO_DVALID   (0x1 << 16)
  61#define S3C_PCM_TXFIFO_DATA_MSK (0xffff << 0)
  62
  63/* PCM_RXFIFO Bit-Fields */
  64#define S3C_PCM_RXFIFO_DVALID   (0x1 << 16)
  65#define S3C_PCM_RXFIFO_DATA_MSK (0xffff << 0)
  66
  67/* PCM_IRQCTL Bit-Fields */
  68#define S3C_PCM_IRQCTL_IRQEN            (0x1 << 14)
  69#define S3C_PCM_IRQCTL_WRDEN            (0x1 << 12)
  70#define S3C_PCM_IRQCTL_TXEMPTYEN        (0x1 << 11)
  71#define S3C_PCM_IRQCTL_TXALMSTEMPTYEN   (0x1 << 10)
  72#define S3C_PCM_IRQCTL_TXFULLEN         (0x1 << 9)
  73#define S3C_PCM_IRQCTL_TXALMSTFULLEN    (0x1 << 8)
  74#define S3C_PCM_IRQCTL_TXSTARVEN        (0x1 << 7)
  75#define S3C_PCM_IRQCTL_TXERROVRFLEN     (0x1 << 6)
  76#define S3C_PCM_IRQCTL_RXEMPTEN         (0x1 << 5)
  77#define S3C_PCM_IRQCTL_RXALMSTEMPTEN    (0x1 << 4)
  78#define S3C_PCM_IRQCTL_RXFULLEN         (0x1 << 3)
  79#define S3C_PCM_IRQCTL_RXALMSTFULLEN    (0x1 << 2)
  80#define S3C_PCM_IRQCTL_RXSTARVEN        (0x1 << 1)
  81#define S3C_PCM_IRQCTL_RXERROVRFLEN     (0x1 << 0)
  82
  83/* PCM_IRQSTAT Bit-Fields */
  84#define S3C_PCM_IRQSTAT_IRQPND          (0x1 << 13)
  85#define S3C_PCM_IRQSTAT_WRD_XFER        (0x1 << 12)
  86#define S3C_PCM_IRQSTAT_TXEMPTY         (0x1 << 11)
  87#define S3C_PCM_IRQSTAT_TXALMSTEMPTY    (0x1 << 10)
  88#define S3C_PCM_IRQSTAT_TXFULL          (0x1 << 9)
  89#define S3C_PCM_IRQSTAT_TXALMSTFULL     (0x1 << 8)
  90#define S3C_PCM_IRQSTAT_TXSTARV         (0x1 << 7)
  91#define S3C_PCM_IRQSTAT_TXERROVRFL      (0x1 << 6)
  92#define S3C_PCM_IRQSTAT_RXEMPT          (0x1 << 5)
  93#define S3C_PCM_IRQSTAT_RXALMSTEMPT     (0x1 << 4)
  94#define S3C_PCM_IRQSTAT_RXFULL          (0x1 << 3)
  95#define S3C_PCM_IRQSTAT_RXALMSTFULL     (0x1 << 2)
  96#define S3C_PCM_IRQSTAT_RXSTARV         (0x1 << 1)
  97#define S3C_PCM_IRQSTAT_RXERROVRFL      (0x1 << 0)
  98
  99/* PCM_FIFOSTAT Bit-Fields */
 100#define S3C_PCM_FIFOSTAT_TXCNT_MSK              (0x3f << 14)
 101#define S3C_PCM_FIFOSTAT_TXFIFOEMPTY            (0x1 << 13)
 102#define S3C_PCM_FIFOSTAT_TXFIFOALMSTEMPTY       (0x1 << 12)
 103#define S3C_PCM_FIFOSTAT_TXFIFOFULL             (0x1 << 11)
 104#define S3C_PCM_FIFOSTAT_TXFIFOALMSTFULL        (0x1 << 10)
 105#define S3C_PCM_FIFOSTAT_RXCNT_MSK              (0x3f << 4)
 106#define S3C_PCM_FIFOSTAT_RXFIFOEMPTY            (0x1 << 3)
 107#define S3C_PCM_FIFOSTAT_RXFIFOALMSTEMPTY       (0x1 << 2)
 108#define S3C_PCM_FIFOSTAT_RXFIFOFULL             (0x1 << 1)
 109#define S3C_PCM_FIFOSTAT_RXFIFOALMSTFULL        (0x1 << 0)
 110
 111/**
 112 * struct s3c_pcm_info - S3C PCM Controller information
 113 * @dev: The parent device passed to use from the probe.
 114 * @regs: The pointer to the device register block.
 115 * @dma_playback: DMA information for playback channel.
 116 * @dma_capture: DMA information for capture channel.
 117 */
 118struct s3c_pcm_info {
 119        spinlock_t lock;
 120        struct device   *dev;
 121        void __iomem    *regs;
 122
 123        unsigned int sclk_per_fs;
 124
 125        /* Whether to keep PCMSCLK enabled even when idle(no active xfer) */
 126        unsigned int idleclk;
 127
 128        struct clk      *pclk;
 129        struct clk      *cclk;
 130
 131        struct s3c_dma_params   *dma_playback;
 132        struct s3c_dma_params   *dma_capture;
 133};
 134
 135static struct s3c2410_dma_client s3c_pcm_dma_client_out = {
 136        .name           = "PCM Stereo out"
 137};
 138
 139static struct s3c2410_dma_client s3c_pcm_dma_client_in = {
 140        .name           = "PCM Stereo in"
 141};
 142
 143static struct s3c_dma_params s3c_pcm_stereo_out[] = {
 144        [0] = {
 145                .client         = &s3c_pcm_dma_client_out,
 146                .dma_size       = 4,
 147        },
 148        [1] = {
 149                .client         = &s3c_pcm_dma_client_out,
 150                .dma_size       = 4,
 151        },
 152};
 153
 154static struct s3c_dma_params s3c_pcm_stereo_in[] = {
 155        [0] = {
 156                .client         = &s3c_pcm_dma_client_in,
 157                .dma_size       = 4,
 158        },
 159        [1] = {
 160                .client         = &s3c_pcm_dma_client_in,
 161                .dma_size       = 4,
 162        },
 163};
 164
 165static struct s3c_pcm_info s3c_pcm[2];
 166
 167static void s3c_pcm_snd_txctrl(struct s3c_pcm_info *pcm, int on)
 168{
 169        void __iomem *regs = pcm->regs;
 170        u32 ctl, clkctl;
 171
 172        clkctl = readl(regs + S3C_PCM_CLKCTL);
 173        ctl = readl(regs + S3C_PCM_CTL);
 174        ctl &= ~(S3C_PCM_CTL_TXDIPSTICK_MASK
 175                         << S3C_PCM_CTL_TXDIPSTICK_SHIFT);
 176
 177        if (on) {
 178                ctl |= S3C_PCM_CTL_TXDMA_EN;
 179                ctl |= S3C_PCM_CTL_TXFIFO_EN;
 180                ctl |= S3C_PCM_CTL_ENABLE;
 181                ctl |= (0x4<<S3C_PCM_CTL_TXDIPSTICK_SHIFT);
 182                clkctl |= S3C_PCM_CLKCTL_SERCLK_EN;
 183        } else {
 184                ctl &= ~S3C_PCM_CTL_TXDMA_EN;
 185                ctl &= ~S3C_PCM_CTL_TXFIFO_EN;
 186
 187                if (!(ctl & S3C_PCM_CTL_RXFIFO_EN)) {
 188                        ctl &= ~S3C_PCM_CTL_ENABLE;
 189                        if (!pcm->idleclk)
 190                                clkctl |= S3C_PCM_CLKCTL_SERCLK_EN;
 191                }
 192        }
 193
 194        writel(clkctl, regs + S3C_PCM_CLKCTL);
 195        writel(ctl, regs + S3C_PCM_CTL);
 196}
 197
 198static void s3c_pcm_snd_rxctrl(struct s3c_pcm_info *pcm, int on)
 199{
 200        void __iomem *regs = pcm->regs;
 201        u32 ctl, clkctl;
 202
 203        ctl = readl(regs + S3C_PCM_CTL);
 204        clkctl = readl(regs + S3C_PCM_CLKCTL);
 205        ctl &= ~(S3C_PCM_CTL_RXDIPSTICK_MASK
 206                         << S3C_PCM_CTL_RXDIPSTICK_SHIFT);
 207
 208        if (on) {
 209                ctl |= S3C_PCM_CTL_RXDMA_EN;
 210                ctl |= S3C_PCM_CTL_RXFIFO_EN;
 211                ctl |= S3C_PCM_CTL_ENABLE;
 212                ctl |= (0x20<<S3C_PCM_CTL_RXDIPSTICK_SHIFT);
 213                clkctl |= S3C_PCM_CLKCTL_SERCLK_EN;
 214        } else {
 215                ctl &= ~S3C_PCM_CTL_RXDMA_EN;
 216                ctl &= ~S3C_PCM_CTL_RXFIFO_EN;
 217
 218                if (!(ctl & S3C_PCM_CTL_TXFIFO_EN)) {
 219                        ctl &= ~S3C_PCM_CTL_ENABLE;
 220                        if (!pcm->idleclk)
 221                                clkctl |= S3C_PCM_CLKCTL_SERCLK_EN;
 222                }
 223        }
 224
 225        writel(clkctl, regs + S3C_PCM_CLKCTL);
 226        writel(ctl, regs + S3C_PCM_CTL);
 227}
 228
 229static int s3c_pcm_trigger(struct snd_pcm_substream *substream, int cmd,
 230                               struct snd_soc_dai *dai)
 231{
 232        struct snd_soc_pcm_runtime *rtd = substream->private_data;
 233        struct s3c_pcm_info *pcm = snd_soc_dai_get_drvdata(rtd->cpu_dai);
 234        unsigned long flags;
 235
 236        dev_dbg(pcm->dev, "Entered %s\n", __func__);
 237
 238        switch (cmd) {
 239        case SNDRV_PCM_TRIGGER_START:
 240        case SNDRV_PCM_TRIGGER_RESUME:
 241        case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
 242                spin_lock_irqsave(&pcm->lock, flags);
 243
 244                if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
 245                        s3c_pcm_snd_rxctrl(pcm, 1);
 246                else
 247                        s3c_pcm_snd_txctrl(pcm, 1);
 248
 249                spin_unlock_irqrestore(&pcm->lock, flags);
 250                break;
 251
 252        case SNDRV_PCM_TRIGGER_STOP:
 253        case SNDRV_PCM_TRIGGER_SUSPEND:
 254        case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
 255                spin_lock_irqsave(&pcm->lock, flags);
 256
 257                if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
 258                        s3c_pcm_snd_rxctrl(pcm, 0);
 259                else
 260                        s3c_pcm_snd_txctrl(pcm, 0);
 261
 262                spin_unlock_irqrestore(&pcm->lock, flags);
 263                break;
 264
 265        default:
 266                return -EINVAL;
 267        }
 268
 269        return 0;
 270}
 271
 272static int s3c_pcm_hw_params(struct snd_pcm_substream *substream,
 273                                 struct snd_pcm_hw_params *params,
 274                                 struct snd_soc_dai *socdai)
 275{
 276        struct snd_soc_pcm_runtime *rtd = substream->private_data;
 277        struct s3c_pcm_info *pcm = snd_soc_dai_get_drvdata(rtd->cpu_dai);
 278        struct s3c_dma_params *dma_data;
 279        void __iomem *regs = pcm->regs;
 280        struct clk *clk;
 281        int sclk_div, sync_div;
 282        unsigned long flags;
 283        u32 clkctl;
 284
 285        dev_dbg(pcm->dev, "Entered %s\n", __func__);
 286
 287        if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
 288                dma_data = pcm->dma_playback;
 289        else
 290                dma_data = pcm->dma_capture;
 291
 292        snd_soc_dai_set_dma_data(rtd->cpu_dai, substream, dma_data);
 293
 294        /* Strictly check for sample size */
 295        switch (params_format(params)) {
 296        case SNDRV_PCM_FORMAT_S16_LE:
 297                break;
 298        default:
 299                return -EINVAL;
 300        }
 301
 302        spin_lock_irqsave(&pcm->lock, flags);
 303
 304        /* Get hold of the PCMSOURCE_CLK */
 305        clkctl = readl(regs + S3C_PCM_CLKCTL);
 306        if (clkctl & S3C_PCM_CLKCTL_SERCLKSEL_PCLK)
 307                clk = pcm->pclk;
 308        else
 309                clk = pcm->cclk;
 310
 311        /* Set the SCLK divider */
 312        sclk_div = clk_get_rate(clk) / pcm->sclk_per_fs /
 313                                        params_rate(params) / 2 - 1;
 314
 315        clkctl &= ~(S3C_PCM_CLKCTL_SCLKDIV_MASK
 316                        << S3C_PCM_CLKCTL_SCLKDIV_SHIFT);
 317        clkctl |= ((sclk_div & S3C_PCM_CLKCTL_SCLKDIV_MASK)
 318                        << S3C_PCM_CLKCTL_SCLKDIV_SHIFT);
 319
 320        /* Set the SYNC divider */
 321        sync_div = pcm->sclk_per_fs - 1;
 322
 323        clkctl &= ~(S3C_PCM_CLKCTL_SYNCDIV_MASK
 324                                << S3C_PCM_CLKCTL_SYNCDIV_SHIFT);
 325        clkctl |= ((sync_div & S3C_PCM_CLKCTL_SYNCDIV_MASK)
 326                                << S3C_PCM_CLKCTL_SYNCDIV_SHIFT);
 327
 328        writel(clkctl, regs + S3C_PCM_CLKCTL);
 329
 330        spin_unlock_irqrestore(&pcm->lock, flags);
 331
 332        dev_dbg(pcm->dev, "PCMSOURCE_CLK-%lu SCLK=%ufs SCLK_DIV=%d SYNC_DIV=%d\n",
 333                                clk_get_rate(clk), pcm->sclk_per_fs,
 334                                sclk_div, sync_div);
 335
 336        return 0;
 337}
 338
 339static int s3c_pcm_set_fmt(struct snd_soc_dai *cpu_dai,
 340                               unsigned int fmt)
 341{
 342        struct s3c_pcm_info *pcm = snd_soc_dai_get_drvdata(cpu_dai);
 343        void __iomem *regs = pcm->regs;
 344        unsigned long flags;
 345        int ret = 0;
 346        u32 ctl;
 347
 348        dev_dbg(pcm->dev, "Entered %s\n", __func__);
 349
 350        spin_lock_irqsave(&pcm->lock, flags);
 351
 352        ctl = readl(regs + S3C_PCM_CTL);
 353
 354        switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
 355        case SND_SOC_DAIFMT_IB_NF:
 356                /* Nothing to do, IB_NF by default */
 357                break;
 358        default:
 359                dev_err(pcm->dev, "Unsupported clock inversion!\n");
 360                ret = -EINVAL;
 361                goto exit;
 362        }
 363
 364        switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
 365        case SND_SOC_DAIFMT_CBS_CFS:
 366                /* Nothing to do, Master by default */
 367                break;
 368        default:
 369                dev_err(pcm->dev, "Unsupported master/slave format!\n");
 370                ret = -EINVAL;
 371                goto exit;
 372        }
 373
 374        switch (fmt & SND_SOC_DAIFMT_CLOCK_MASK) {
 375        case SND_SOC_DAIFMT_CONT:
 376                pcm->idleclk = 1;
 377                break;
 378        case SND_SOC_DAIFMT_GATED:
 379                pcm->idleclk = 0;
 380                break;
 381        default:
 382                dev_err(pcm->dev, "Invalid Clock gating request!\n");
 383                ret = -EINVAL;
 384                goto exit;
 385        }
 386
 387        switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
 388        case SND_SOC_DAIFMT_DSP_A:
 389                ctl |= S3C_PCM_CTL_TXMSB_AFTER_FSYNC;
 390                ctl |= S3C_PCM_CTL_RXMSB_AFTER_FSYNC;
 391                break;
 392        case SND_SOC_DAIFMT_DSP_B:
 393                ctl &= ~S3C_PCM_CTL_TXMSB_AFTER_FSYNC;
 394                ctl &= ~S3C_PCM_CTL_RXMSB_AFTER_FSYNC;
 395                break;
 396        default:
 397                dev_err(pcm->dev, "Unsupported data format!\n");
 398                ret = -EINVAL;
 399                goto exit;
 400        }
 401
 402        writel(ctl, regs + S3C_PCM_CTL);
 403
 404exit:
 405        spin_unlock_irqrestore(&pcm->lock, flags);
 406
 407        return ret;
 408}
 409
 410static int s3c_pcm_set_clkdiv(struct snd_soc_dai *cpu_dai,
 411                                                int div_id, int div)
 412{
 413        struct s3c_pcm_info *pcm = snd_soc_dai_get_drvdata(cpu_dai);
 414
 415        switch (div_id) {
 416        case S3C_PCM_SCLK_PER_FS:
 417                pcm->sclk_per_fs = div;
 418                break;
 419
 420        default:
 421                return -EINVAL;
 422        }
 423
 424        return 0;
 425}
 426
 427static int s3c_pcm_set_sysclk(struct snd_soc_dai *cpu_dai,
 428                                  int clk_id, unsigned int freq, int dir)
 429{
 430        struct s3c_pcm_info *pcm = snd_soc_dai_get_drvdata(cpu_dai);
 431        void __iomem *regs = pcm->regs;
 432        u32 clkctl = readl(regs + S3C_PCM_CLKCTL);
 433
 434        switch (clk_id) {
 435        case S3C_PCM_CLKSRC_PCLK:
 436                clkctl |= S3C_PCM_CLKCTL_SERCLKSEL_PCLK;
 437                break;
 438
 439        case S3C_PCM_CLKSRC_MUX:
 440                clkctl &= ~S3C_PCM_CLKCTL_SERCLKSEL_PCLK;
 441
 442                if (clk_get_rate(pcm->cclk) != freq)
 443                        clk_set_rate(pcm->cclk, freq);
 444
 445                break;
 446
 447        default:
 448                return -EINVAL;
 449        }
 450
 451        writel(clkctl, regs + S3C_PCM_CLKCTL);
 452
 453        return 0;
 454}
 455
 456static const struct snd_soc_dai_ops s3c_pcm_dai_ops = {
 457        .set_sysclk     = s3c_pcm_set_sysclk,
 458        .set_clkdiv     = s3c_pcm_set_clkdiv,
 459        .trigger        = s3c_pcm_trigger,
 460        .hw_params      = s3c_pcm_hw_params,
 461        .set_fmt        = s3c_pcm_set_fmt,
 462};
 463
 464#define S3C_PCM_RATES  SNDRV_PCM_RATE_8000_96000
 465
 466#define S3C_PCM_DAI_DECLARE                     \
 467        .symmetric_rates = 1,                                   \
 468        .ops = &s3c_pcm_dai_ops,                                \
 469        .playback = {                                           \
 470                .channels_min   = 2,                            \
 471                .channels_max   = 2,                            \
 472                .rates          = S3C_PCM_RATES,                \
 473                .formats        = SNDRV_PCM_FMTBIT_S16_LE,      \
 474        },                                                      \
 475        .capture = {                                            \
 476                .channels_min   = 2,                            \
 477                .channels_max   = 2,                            \
 478                .rates          = S3C_PCM_RATES,                \
 479                .formats        = SNDRV_PCM_FMTBIT_S16_LE,      \
 480        }
 481
 482static struct snd_soc_dai_driver s3c_pcm_dai[] = {
 483        [0] = {
 484                .name   = "samsung-pcm.0",
 485                S3C_PCM_DAI_DECLARE,
 486        },
 487        [1] = {
 488                .name   = "samsung-pcm.1",
 489                S3C_PCM_DAI_DECLARE,
 490        },
 491};
 492
 493static const struct snd_soc_component_driver s3c_pcm_component = {
 494        .name           = "s3c-pcm",
 495};
 496
 497static int s3c_pcm_dev_probe(struct platform_device *pdev)
 498{
 499        struct s3c_pcm_info *pcm;
 500        struct resource *mem_res, *dmatx_res, *dmarx_res;
 501        struct s3c_audio_pdata *pcm_pdata;
 502        int ret;
 503
 504        /* Check for valid device index */
 505        if ((pdev->id < 0) || pdev->id >= ARRAY_SIZE(s3c_pcm)) {
 506                dev_err(&pdev->dev, "id %d out of range\n", pdev->id);
 507                return -EINVAL;
 508        }
 509
 510        pcm_pdata = pdev->dev.platform_data;
 511
 512        /* Check for availability of necessary resource */
 513        dmatx_res = platform_get_resource(pdev, IORESOURCE_DMA, 0);
 514        if (!dmatx_res) {
 515                dev_err(&pdev->dev, "Unable to get PCM-TX dma resource\n");
 516                return -ENXIO;
 517        }
 518
 519        dmarx_res = platform_get_resource(pdev, IORESOURCE_DMA, 1);
 520        if (!dmarx_res) {
 521                dev_err(&pdev->dev, "Unable to get PCM-RX dma resource\n");
 522                return -ENXIO;
 523        }
 524
 525        mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 526        if (!mem_res) {
 527                dev_err(&pdev->dev, "Unable to get register resource\n");
 528                return -ENXIO;
 529        }
 530
 531        if (pcm_pdata && pcm_pdata->cfg_gpio && pcm_pdata->cfg_gpio(pdev)) {
 532                dev_err(&pdev->dev, "Unable to configure gpio\n");
 533                return -EINVAL;
 534        }
 535
 536        pcm = &s3c_pcm[pdev->id];
 537        pcm->dev = &pdev->dev;
 538
 539        spin_lock_init(&pcm->lock);
 540
 541        /* Default is 128fs */
 542        pcm->sclk_per_fs = 128;
 543
 544        pcm->cclk = clk_get(&pdev->dev, "audio-bus");
 545        if (IS_ERR(pcm->cclk)) {
 546                dev_err(&pdev->dev, "failed to get audio-bus\n");
 547                ret = PTR_ERR(pcm->cclk);
 548                goto err1;
 549        }
 550        clk_prepare_enable(pcm->cclk);
 551
 552        /* record our pcm structure for later use in the callbacks */
 553        dev_set_drvdata(&pdev->dev, pcm);
 554
 555        if (!request_mem_region(mem_res->start,
 556                                resource_size(mem_res), "samsung-pcm")) {
 557                dev_err(&pdev->dev, "Unable to request register region\n");
 558                ret = -EBUSY;
 559                goto err2;
 560        }
 561
 562        pcm->regs = ioremap(mem_res->start, 0x100);
 563        if (pcm->regs == NULL) {
 564                dev_err(&pdev->dev, "cannot ioremap registers\n");
 565                ret = -ENXIO;
 566                goto err3;
 567        }
 568
 569        pcm->pclk = clk_get(&pdev->dev, "pcm");
 570        if (IS_ERR(pcm->pclk)) {
 571                dev_err(&pdev->dev, "failed to get pcm_clock\n");
 572                ret = -ENOENT;
 573                goto err4;
 574        }
 575        clk_prepare_enable(pcm->pclk);
 576
 577        s3c_pcm_stereo_in[pdev->id].dma_addr = mem_res->start
 578                                                        + S3C_PCM_RXFIFO;
 579        s3c_pcm_stereo_out[pdev->id].dma_addr = mem_res->start
 580                                                        + S3C_PCM_TXFIFO;
 581
 582        s3c_pcm_stereo_in[pdev->id].channel = dmarx_res->start;
 583        s3c_pcm_stereo_out[pdev->id].channel = dmatx_res->start;
 584
 585        pcm->dma_capture = &s3c_pcm_stereo_in[pdev->id];
 586        pcm->dma_playback = &s3c_pcm_stereo_out[pdev->id];
 587
 588        pm_runtime_enable(&pdev->dev);
 589
 590        ret = snd_soc_register_component(&pdev->dev, &s3c_pcm_component,
 591                                         &s3c_pcm_dai[pdev->id], 1);
 592        if (ret != 0) {
 593                dev_err(&pdev->dev, "failed to get register DAI: %d\n", ret);
 594                goto err5;
 595        }
 596
 597        ret = asoc_dma_platform_register(&pdev->dev);
 598        if (ret) {
 599                dev_err(&pdev->dev, "failed to get register DMA: %d\n", ret);
 600                goto err6;
 601        }
 602
 603        return 0;
 604
 605err6:
 606        snd_soc_unregister_component(&pdev->dev);
 607err5:
 608        clk_disable_unprepare(pcm->pclk);
 609        clk_put(pcm->pclk);
 610err4:
 611        iounmap(pcm->regs);
 612err3:
 613        release_mem_region(mem_res->start, resource_size(mem_res));
 614err2:
 615        clk_disable_unprepare(pcm->cclk);
 616        clk_put(pcm->cclk);
 617err1:
 618        return ret;
 619}
 620
 621static int s3c_pcm_dev_remove(struct platform_device *pdev)
 622{
 623        struct s3c_pcm_info *pcm = &s3c_pcm[pdev->id];
 624        struct resource *mem_res;
 625
 626        asoc_dma_platform_unregister(&pdev->dev);
 627        snd_soc_unregister_component(&pdev->dev);
 628
 629        pm_runtime_disable(&pdev->dev);
 630
 631        iounmap(pcm->regs);
 632
 633        mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 634        release_mem_region(mem_res->start, resource_size(mem_res));
 635
 636        clk_disable_unprepare(pcm->cclk);
 637        clk_disable_unprepare(pcm->pclk);
 638        clk_put(pcm->pclk);
 639        clk_put(pcm->cclk);
 640
 641        return 0;
 642}
 643
 644static struct platform_driver s3c_pcm_driver = {
 645        .probe  = s3c_pcm_dev_probe,
 646        .remove = s3c_pcm_dev_remove,
 647        .driver = {
 648                .name = "samsung-pcm",
 649                .owner = THIS_MODULE,
 650        },
 651};
 652
 653module_platform_driver(s3c_pcm_driver);
 654
 655/* Module information */
 656MODULE_AUTHOR("Jaswinder Singh, <jassisinghbrar@gmail.com>");
 657MODULE_DESCRIPTION("S3C PCM Controller Driver");
 658MODULE_LICENSE("GPL");
 659MODULE_ALIAS("platform:samsung-pcm");
 660