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