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