Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 //
0003 // ALSA SoC Audio Layer - S3C PCM-Controller driver
0004 //
0005 // Copyright (c) 2009 Samsung Electronics Co. Ltd
0006 // Author: Jaswinder Singh <jassisinghbrar@gmail.com>
0007 // based upon I2S drivers by Ben Dooks.
0008 
0009 #include <linux/clk.h>
0010 #include <linux/io.h>
0011 #include <linux/module.h>
0012 #include <linux/pm_runtime.h>
0013 
0014 #include <sound/soc.h>
0015 #include <sound/pcm_params.h>
0016 
0017 #include <linux/platform_data/asoc-s3c.h>
0018 
0019 #include "dma.h"
0020 #include "pcm.h"
0021 
0022 /*Register Offsets */
0023 #define S3C_PCM_CTL     0x00
0024 #define S3C_PCM_CLKCTL      0x04
0025 #define S3C_PCM_TXFIFO      0x08
0026 #define S3C_PCM_RXFIFO      0x0C
0027 #define S3C_PCM_IRQCTL      0x10
0028 #define S3C_PCM_IRQSTAT     0x14
0029 #define S3C_PCM_FIFOSTAT    0x18
0030 #define S3C_PCM_CLRINT      0x20
0031 
0032 /* PCM_CTL Bit-Fields */
0033 #define S3C_PCM_CTL_TXDIPSTICK_MASK 0x3f
0034 #define S3C_PCM_CTL_TXDIPSTICK_SHIFT    13
0035 #define S3C_PCM_CTL_RXDIPSTICK_MASK 0x3f
0036 #define S3C_PCM_CTL_RXDIPSTICK_SHIFT    7
0037 #define S3C_PCM_CTL_TXDMA_EN        (0x1 << 6)
0038 #define S3C_PCM_CTL_RXDMA_EN        (0x1 << 5)
0039 #define S3C_PCM_CTL_TXMSB_AFTER_FSYNC   (0x1 << 4)
0040 #define S3C_PCM_CTL_RXMSB_AFTER_FSYNC   (0x1 << 3)
0041 #define S3C_PCM_CTL_TXFIFO_EN       (0x1 << 2)
0042 #define S3C_PCM_CTL_RXFIFO_EN       (0x1 << 1)
0043 #define S3C_PCM_CTL_ENABLE      (0x1 << 0)
0044 
0045 /* PCM_CLKCTL Bit-Fields */
0046 #define S3C_PCM_CLKCTL_SERCLK_EN    (0x1 << 19)
0047 #define S3C_PCM_CLKCTL_SERCLKSEL_PCLK   (0x1 << 18)
0048 #define S3C_PCM_CLKCTL_SCLKDIV_MASK 0x1ff
0049 #define S3C_PCM_CLKCTL_SYNCDIV_MASK 0x1ff
0050 #define S3C_PCM_CLKCTL_SCLKDIV_SHIFT    9
0051 #define S3C_PCM_CLKCTL_SYNCDIV_SHIFT    0
0052 
0053 /* PCM_TXFIFO Bit-Fields */
0054 #define S3C_PCM_TXFIFO_DVALID   (0x1 << 16)
0055 #define S3C_PCM_TXFIFO_DATA_MSK (0xffff << 0)
0056 
0057 /* PCM_RXFIFO Bit-Fields */
0058 #define S3C_PCM_RXFIFO_DVALID   (0x1 << 16)
0059 #define S3C_PCM_RXFIFO_DATA_MSK (0xffff << 0)
0060 
0061 /* PCM_IRQCTL Bit-Fields */
0062 #define S3C_PCM_IRQCTL_IRQEN        (0x1 << 14)
0063 #define S3C_PCM_IRQCTL_WRDEN        (0x1 << 12)
0064 #define S3C_PCM_IRQCTL_TXEMPTYEN    (0x1 << 11)
0065 #define S3C_PCM_IRQCTL_TXALMSTEMPTYEN   (0x1 << 10)
0066 #define S3C_PCM_IRQCTL_TXFULLEN     (0x1 << 9)
0067 #define S3C_PCM_IRQCTL_TXALMSTFULLEN    (0x1 << 8)
0068 #define S3C_PCM_IRQCTL_TXSTARVEN    (0x1 << 7)
0069 #define S3C_PCM_IRQCTL_TXERROVRFLEN (0x1 << 6)
0070 #define S3C_PCM_IRQCTL_RXEMPTEN     (0x1 << 5)
0071 #define S3C_PCM_IRQCTL_RXALMSTEMPTEN    (0x1 << 4)
0072 #define S3C_PCM_IRQCTL_RXFULLEN     (0x1 << 3)
0073 #define S3C_PCM_IRQCTL_RXALMSTFULLEN    (0x1 << 2)
0074 #define S3C_PCM_IRQCTL_RXSTARVEN    (0x1 << 1)
0075 #define S3C_PCM_IRQCTL_RXERROVRFLEN (0x1 << 0)
0076 
0077 /* PCM_IRQSTAT Bit-Fields */
0078 #define S3C_PCM_IRQSTAT_IRQPND      (0x1 << 13)
0079 #define S3C_PCM_IRQSTAT_WRD_XFER    (0x1 << 12)
0080 #define S3C_PCM_IRQSTAT_TXEMPTY     (0x1 << 11)
0081 #define S3C_PCM_IRQSTAT_TXALMSTEMPTY    (0x1 << 10)
0082 #define S3C_PCM_IRQSTAT_TXFULL      (0x1 << 9)
0083 #define S3C_PCM_IRQSTAT_TXALMSTFULL (0x1 << 8)
0084 #define S3C_PCM_IRQSTAT_TXSTARV     (0x1 << 7)
0085 #define S3C_PCM_IRQSTAT_TXERROVRFL  (0x1 << 6)
0086 #define S3C_PCM_IRQSTAT_RXEMPT      (0x1 << 5)
0087 #define S3C_PCM_IRQSTAT_RXALMSTEMPT (0x1 << 4)
0088 #define S3C_PCM_IRQSTAT_RXFULL      (0x1 << 3)
0089 #define S3C_PCM_IRQSTAT_RXALMSTFULL (0x1 << 2)
0090 #define S3C_PCM_IRQSTAT_RXSTARV     (0x1 << 1)
0091 #define S3C_PCM_IRQSTAT_RXERROVRFL  (0x1 << 0)
0092 
0093 /* PCM_FIFOSTAT Bit-Fields */
0094 #define S3C_PCM_FIFOSTAT_TXCNT_MSK      (0x3f << 14)
0095 #define S3C_PCM_FIFOSTAT_TXFIFOEMPTY        (0x1 << 13)
0096 #define S3C_PCM_FIFOSTAT_TXFIFOALMSTEMPTY   (0x1 << 12)
0097 #define S3C_PCM_FIFOSTAT_TXFIFOFULL     (0x1 << 11)
0098 #define S3C_PCM_FIFOSTAT_TXFIFOALMSTFULL    (0x1 << 10)
0099 #define S3C_PCM_FIFOSTAT_RXCNT_MSK      (0x3f << 4)
0100 #define S3C_PCM_FIFOSTAT_RXFIFOEMPTY        (0x1 << 3)
0101 #define S3C_PCM_FIFOSTAT_RXFIFOALMSTEMPTY   (0x1 << 2)
0102 #define S3C_PCM_FIFOSTAT_RXFIFOFULL     (0x1 << 1)
0103 #define S3C_PCM_FIFOSTAT_RXFIFOALMSTFULL    (0x1 << 0)
0104 
0105 /**
0106  * struct s3c_pcm_info - S3C PCM Controller information
0107  * @lock: Spin lock
0108  * @dev: The parent device passed to use from the probe.
0109  * @regs: The pointer to the device register block.
0110  * @sclk_per_fs: number of sclk per frame sync
0111  * @idleclk: Whether to keep PCMSCLK enabled even when idle (no active xfer)
0112  * @pclk: the PCLK_PCM (pcm) clock pointer
0113  * @cclk: the SCLK_AUDIO (audio-bus) clock pointer
0114  * @dma_playback: DMA information for playback channel.
0115  * @dma_capture: DMA information for capture channel.
0116  */
0117 struct s3c_pcm_info {
0118     spinlock_t lock;
0119     struct device   *dev;
0120     void __iomem    *regs;
0121 
0122     unsigned int sclk_per_fs;
0123 
0124     /* Whether to keep PCMSCLK enabled even when idle(no active xfer) */
0125     unsigned int idleclk;
0126 
0127     struct clk  *pclk;
0128     struct clk  *cclk;
0129 
0130     struct snd_dmaengine_dai_dma_data *dma_playback;
0131     struct snd_dmaengine_dai_dma_data *dma_capture;
0132 };
0133 
0134 static struct snd_dmaengine_dai_dma_data s3c_pcm_stereo_out[] = {
0135     [0] = {
0136         .addr_width = 4,
0137     },
0138     [1] = {
0139         .addr_width = 4,
0140     },
0141 };
0142 
0143 static struct snd_dmaengine_dai_dma_data s3c_pcm_stereo_in[] = {
0144     [0] = {
0145         .addr_width = 4,
0146     },
0147     [1] = {
0148         .addr_width = 4,
0149     },
0150 };
0151 
0152 static struct s3c_pcm_info s3c_pcm[2];
0153 
0154 static void s3c_pcm_snd_txctrl(struct s3c_pcm_info *pcm, int on)
0155 {
0156     void __iomem *regs = pcm->regs;
0157     u32 ctl, clkctl;
0158 
0159     clkctl = readl(regs + S3C_PCM_CLKCTL);
0160     ctl = readl(regs + S3C_PCM_CTL);
0161     ctl &= ~(S3C_PCM_CTL_TXDIPSTICK_MASK
0162              << S3C_PCM_CTL_TXDIPSTICK_SHIFT);
0163 
0164     if (on) {
0165         ctl |= S3C_PCM_CTL_TXDMA_EN;
0166         ctl |= S3C_PCM_CTL_TXFIFO_EN;
0167         ctl |= S3C_PCM_CTL_ENABLE;
0168         ctl |= (0x4<<S3C_PCM_CTL_TXDIPSTICK_SHIFT);
0169         clkctl |= S3C_PCM_CLKCTL_SERCLK_EN;
0170     } else {
0171         ctl &= ~S3C_PCM_CTL_TXDMA_EN;
0172         ctl &= ~S3C_PCM_CTL_TXFIFO_EN;
0173 
0174         if (!(ctl & S3C_PCM_CTL_RXFIFO_EN)) {
0175             ctl &= ~S3C_PCM_CTL_ENABLE;
0176             if (!pcm->idleclk)
0177                 clkctl |= S3C_PCM_CLKCTL_SERCLK_EN;
0178         }
0179     }
0180 
0181     writel(clkctl, regs + S3C_PCM_CLKCTL);
0182     writel(ctl, regs + S3C_PCM_CTL);
0183 }
0184 
0185 static void s3c_pcm_snd_rxctrl(struct s3c_pcm_info *pcm, int on)
0186 {
0187     void __iomem *regs = pcm->regs;
0188     u32 ctl, clkctl;
0189 
0190     ctl = readl(regs + S3C_PCM_CTL);
0191     clkctl = readl(regs + S3C_PCM_CLKCTL);
0192     ctl &= ~(S3C_PCM_CTL_RXDIPSTICK_MASK
0193              << S3C_PCM_CTL_RXDIPSTICK_SHIFT);
0194 
0195     if (on) {
0196         ctl |= S3C_PCM_CTL_RXDMA_EN;
0197         ctl |= S3C_PCM_CTL_RXFIFO_EN;
0198         ctl |= S3C_PCM_CTL_ENABLE;
0199         ctl |= (0x20<<S3C_PCM_CTL_RXDIPSTICK_SHIFT);
0200         clkctl |= S3C_PCM_CLKCTL_SERCLK_EN;
0201     } else {
0202         ctl &= ~S3C_PCM_CTL_RXDMA_EN;
0203         ctl &= ~S3C_PCM_CTL_RXFIFO_EN;
0204 
0205         if (!(ctl & S3C_PCM_CTL_TXFIFO_EN)) {
0206             ctl &= ~S3C_PCM_CTL_ENABLE;
0207             if (!pcm->idleclk)
0208                 clkctl |= S3C_PCM_CLKCTL_SERCLK_EN;
0209         }
0210     }
0211 
0212     writel(clkctl, regs + S3C_PCM_CLKCTL);
0213     writel(ctl, regs + S3C_PCM_CTL);
0214 }
0215 
0216 static int s3c_pcm_trigger(struct snd_pcm_substream *substream, int cmd,
0217                    struct snd_soc_dai *dai)
0218 {
0219     struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
0220     struct s3c_pcm_info *pcm = snd_soc_dai_get_drvdata(asoc_rtd_to_cpu(rtd, 0));
0221     unsigned long flags;
0222 
0223     dev_dbg(pcm->dev, "Entered %s\n", __func__);
0224 
0225     switch (cmd) {
0226     case SNDRV_PCM_TRIGGER_START:
0227     case SNDRV_PCM_TRIGGER_RESUME:
0228     case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
0229         spin_lock_irqsave(&pcm->lock, flags);
0230 
0231         if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
0232             s3c_pcm_snd_rxctrl(pcm, 1);
0233         else
0234             s3c_pcm_snd_txctrl(pcm, 1);
0235 
0236         spin_unlock_irqrestore(&pcm->lock, flags);
0237         break;
0238 
0239     case SNDRV_PCM_TRIGGER_STOP:
0240     case SNDRV_PCM_TRIGGER_SUSPEND:
0241     case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
0242         spin_lock_irqsave(&pcm->lock, flags);
0243 
0244         if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
0245             s3c_pcm_snd_rxctrl(pcm, 0);
0246         else
0247             s3c_pcm_snd_txctrl(pcm, 0);
0248 
0249         spin_unlock_irqrestore(&pcm->lock, flags);
0250         break;
0251 
0252     default:
0253         return -EINVAL;
0254     }
0255 
0256     return 0;
0257 }
0258 
0259 static int s3c_pcm_hw_params(struct snd_pcm_substream *substream,
0260                  struct snd_pcm_hw_params *params,
0261                  struct snd_soc_dai *socdai)
0262 {
0263     struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
0264     struct s3c_pcm_info *pcm = snd_soc_dai_get_drvdata(asoc_rtd_to_cpu(rtd, 0));
0265     void __iomem *regs = pcm->regs;
0266     struct clk *clk;
0267     int sclk_div, sync_div;
0268     unsigned long flags;
0269     u32 clkctl;
0270 
0271     dev_dbg(pcm->dev, "Entered %s\n", __func__);
0272 
0273     /* Strictly check for sample size */
0274     switch (params_width(params)) {
0275     case 16:
0276         break;
0277     default:
0278         return -EINVAL;
0279     }
0280 
0281     spin_lock_irqsave(&pcm->lock, flags);
0282 
0283     /* Get hold of the PCMSOURCE_CLK */
0284     clkctl = readl(regs + S3C_PCM_CLKCTL);
0285     if (clkctl & S3C_PCM_CLKCTL_SERCLKSEL_PCLK)
0286         clk = pcm->pclk;
0287     else
0288         clk = pcm->cclk;
0289 
0290     /* Set the SCLK divider */
0291     sclk_div = clk_get_rate(clk) / pcm->sclk_per_fs /
0292                     params_rate(params) / 2 - 1;
0293 
0294     clkctl &= ~(S3C_PCM_CLKCTL_SCLKDIV_MASK
0295             << S3C_PCM_CLKCTL_SCLKDIV_SHIFT);
0296     clkctl |= ((sclk_div & S3C_PCM_CLKCTL_SCLKDIV_MASK)
0297             << S3C_PCM_CLKCTL_SCLKDIV_SHIFT);
0298 
0299     /* Set the SYNC divider */
0300     sync_div = pcm->sclk_per_fs - 1;
0301 
0302     clkctl &= ~(S3C_PCM_CLKCTL_SYNCDIV_MASK
0303                 << S3C_PCM_CLKCTL_SYNCDIV_SHIFT);
0304     clkctl |= ((sync_div & S3C_PCM_CLKCTL_SYNCDIV_MASK)
0305                 << S3C_PCM_CLKCTL_SYNCDIV_SHIFT);
0306 
0307     writel(clkctl, regs + S3C_PCM_CLKCTL);
0308 
0309     spin_unlock_irqrestore(&pcm->lock, flags);
0310 
0311     dev_dbg(pcm->dev, "PCMSOURCE_CLK-%lu SCLK=%ufs SCLK_DIV=%d SYNC_DIV=%d\n",
0312                 clk_get_rate(clk), pcm->sclk_per_fs,
0313                 sclk_div, sync_div);
0314 
0315     return 0;
0316 }
0317 
0318 static int s3c_pcm_set_fmt(struct snd_soc_dai *cpu_dai,
0319                    unsigned int fmt)
0320 {
0321     struct s3c_pcm_info *pcm = snd_soc_dai_get_drvdata(cpu_dai);
0322     void __iomem *regs = pcm->regs;
0323     unsigned long flags;
0324     int ret = 0;
0325     u32 ctl;
0326 
0327     dev_dbg(pcm->dev, "Entered %s\n", __func__);
0328 
0329     spin_lock_irqsave(&pcm->lock, flags);
0330 
0331     ctl = readl(regs + S3C_PCM_CTL);
0332 
0333     switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
0334     case SND_SOC_DAIFMT_IB_NF:
0335         /* Nothing to do, IB_NF by default */
0336         break;
0337     default:
0338         dev_err(pcm->dev, "Unsupported clock inversion!\n");
0339         ret = -EINVAL;
0340         goto exit;
0341     }
0342 
0343     switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
0344     case SND_SOC_DAIFMT_BP_FP:
0345         /* Nothing to do, Master by default */
0346         break;
0347     default:
0348         dev_err(pcm->dev, "Unsupported master/slave format!\n");
0349         ret = -EINVAL;
0350         goto exit;
0351     }
0352 
0353     switch (fmt & SND_SOC_DAIFMT_CLOCK_MASK) {
0354     case SND_SOC_DAIFMT_CONT:
0355         pcm->idleclk = 1;
0356         break;
0357     case SND_SOC_DAIFMT_GATED:
0358         pcm->idleclk = 0;
0359         break;
0360     default:
0361         dev_err(pcm->dev, "Invalid Clock gating request!\n");
0362         ret = -EINVAL;
0363         goto exit;
0364     }
0365 
0366     switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
0367     case SND_SOC_DAIFMT_DSP_A:
0368         ctl |= S3C_PCM_CTL_TXMSB_AFTER_FSYNC;
0369         ctl |= S3C_PCM_CTL_RXMSB_AFTER_FSYNC;
0370         break;
0371     case SND_SOC_DAIFMT_DSP_B:
0372         ctl &= ~S3C_PCM_CTL_TXMSB_AFTER_FSYNC;
0373         ctl &= ~S3C_PCM_CTL_RXMSB_AFTER_FSYNC;
0374         break;
0375     default:
0376         dev_err(pcm->dev, "Unsupported data format!\n");
0377         ret = -EINVAL;
0378         goto exit;
0379     }
0380 
0381     writel(ctl, regs + S3C_PCM_CTL);
0382 
0383 exit:
0384     spin_unlock_irqrestore(&pcm->lock, flags);
0385 
0386     return ret;
0387 }
0388 
0389 static int s3c_pcm_set_clkdiv(struct snd_soc_dai *cpu_dai,
0390                         int div_id, int div)
0391 {
0392     struct s3c_pcm_info *pcm = snd_soc_dai_get_drvdata(cpu_dai);
0393 
0394     switch (div_id) {
0395     case S3C_PCM_SCLK_PER_FS:
0396         pcm->sclk_per_fs = div;
0397         break;
0398 
0399     default:
0400         return -EINVAL;
0401     }
0402 
0403     return 0;
0404 }
0405 
0406 static int s3c_pcm_set_sysclk(struct snd_soc_dai *cpu_dai,
0407                   int clk_id, unsigned int freq, int dir)
0408 {
0409     struct s3c_pcm_info *pcm = snd_soc_dai_get_drvdata(cpu_dai);
0410     void __iomem *regs = pcm->regs;
0411     u32 clkctl = readl(regs + S3C_PCM_CLKCTL);
0412 
0413     switch (clk_id) {
0414     case S3C_PCM_CLKSRC_PCLK:
0415         clkctl |= S3C_PCM_CLKCTL_SERCLKSEL_PCLK;
0416         break;
0417 
0418     case S3C_PCM_CLKSRC_MUX:
0419         clkctl &= ~S3C_PCM_CLKCTL_SERCLKSEL_PCLK;
0420 
0421         if (clk_get_rate(pcm->cclk) != freq)
0422             clk_set_rate(pcm->cclk, freq);
0423 
0424         break;
0425 
0426     default:
0427         return -EINVAL;
0428     }
0429 
0430     writel(clkctl, regs + S3C_PCM_CLKCTL);
0431 
0432     return 0;
0433 }
0434 
0435 static const struct snd_soc_dai_ops s3c_pcm_dai_ops = {
0436     .set_sysclk = s3c_pcm_set_sysclk,
0437     .set_clkdiv = s3c_pcm_set_clkdiv,
0438     .trigger    = s3c_pcm_trigger,
0439     .hw_params  = s3c_pcm_hw_params,
0440     .set_fmt    = s3c_pcm_set_fmt,
0441 };
0442 
0443 static int s3c_pcm_dai_probe(struct snd_soc_dai *dai)
0444 {
0445     struct s3c_pcm_info *pcm = snd_soc_dai_get_drvdata(dai);
0446 
0447     snd_soc_dai_init_dma_data(dai, pcm->dma_playback, pcm->dma_capture);
0448 
0449     return 0;
0450 }
0451 
0452 #define S3C_PCM_RATES  SNDRV_PCM_RATE_8000_96000
0453 
0454 #define S3C_PCM_DAI_DECLARE         \
0455     .symmetric_rate = 1,                    \
0456     .probe = s3c_pcm_dai_probe,             \
0457     .ops = &s3c_pcm_dai_ops,                \
0458     .playback = {                       \
0459         .channels_min   = 2,                \
0460         .channels_max   = 2,                \
0461         .rates      = S3C_PCM_RATES,        \
0462         .formats    = SNDRV_PCM_FMTBIT_S16_LE,  \
0463     },                          \
0464     .capture = {                        \
0465         .channels_min   = 2,                \
0466         .channels_max   = 2,                \
0467         .rates      = S3C_PCM_RATES,        \
0468         .formats    = SNDRV_PCM_FMTBIT_S16_LE,  \
0469     }
0470 
0471 static struct snd_soc_dai_driver s3c_pcm_dai[] = {
0472     [0] = {
0473         .name   = "samsung-pcm.0",
0474         S3C_PCM_DAI_DECLARE,
0475     },
0476     [1] = {
0477         .name   = "samsung-pcm.1",
0478         S3C_PCM_DAI_DECLARE,
0479     },
0480 };
0481 
0482 static const struct snd_soc_component_driver s3c_pcm_component = {
0483     .name           = "s3c-pcm",
0484     .legacy_dai_naming  = 1,
0485 };
0486 
0487 static int s3c_pcm_dev_probe(struct platform_device *pdev)
0488 {
0489     struct s3c_pcm_info *pcm;
0490     struct resource *mem_res;
0491     struct s3c_audio_pdata *pcm_pdata;
0492     dma_filter_fn filter;
0493     int ret;
0494 
0495     /* Check for valid device index */
0496     if ((pdev->id < 0) || pdev->id >= ARRAY_SIZE(s3c_pcm)) {
0497         dev_err(&pdev->dev, "id %d out of range\n", pdev->id);
0498         return -EINVAL;
0499     }
0500 
0501     pcm_pdata = pdev->dev.platform_data;
0502 
0503     if (pcm_pdata && pcm_pdata->cfg_gpio && pcm_pdata->cfg_gpio(pdev)) {
0504         dev_err(&pdev->dev, "Unable to configure gpio\n");
0505         return -EINVAL;
0506     }
0507 
0508     pcm = &s3c_pcm[pdev->id];
0509     pcm->dev = &pdev->dev;
0510 
0511     spin_lock_init(&pcm->lock);
0512 
0513     /* Default is 128fs */
0514     pcm->sclk_per_fs = 128;
0515 
0516     pcm->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &mem_res);
0517     if (IS_ERR(pcm->regs))
0518         return PTR_ERR(pcm->regs);
0519 
0520     pcm->cclk = devm_clk_get(&pdev->dev, "audio-bus");
0521     if (IS_ERR(pcm->cclk)) {
0522         dev_err(&pdev->dev, "failed to get audio-bus clock\n");
0523         return PTR_ERR(pcm->cclk);
0524     }
0525     ret = clk_prepare_enable(pcm->cclk);
0526     if (ret)
0527         return ret;
0528 
0529     /* record our pcm structure for later use in the callbacks */
0530     dev_set_drvdata(&pdev->dev, pcm);
0531 
0532     pcm->pclk = devm_clk_get(&pdev->dev, "pcm");
0533     if (IS_ERR(pcm->pclk)) {
0534         dev_err(&pdev->dev, "failed to get pcm clock\n");
0535         ret = PTR_ERR(pcm->pclk);
0536         goto err_dis_cclk;
0537     }
0538     ret = clk_prepare_enable(pcm->pclk);
0539     if (ret)
0540         goto err_dis_cclk;
0541 
0542     s3c_pcm_stereo_in[pdev->id].addr = mem_res->start + S3C_PCM_RXFIFO;
0543     s3c_pcm_stereo_out[pdev->id].addr = mem_res->start + S3C_PCM_TXFIFO;
0544 
0545     filter = NULL;
0546     if (pcm_pdata) {
0547         s3c_pcm_stereo_in[pdev->id].filter_data = pcm_pdata->dma_capture;
0548         s3c_pcm_stereo_out[pdev->id].filter_data = pcm_pdata->dma_playback;
0549         filter = pcm_pdata->dma_filter;
0550     }
0551 
0552     pcm->dma_capture = &s3c_pcm_stereo_in[pdev->id];
0553     pcm->dma_playback = &s3c_pcm_stereo_out[pdev->id];
0554 
0555     ret = samsung_asoc_dma_platform_register(&pdev->dev, filter,
0556                          NULL, NULL, NULL);
0557     if (ret) {
0558         dev_err(&pdev->dev, "failed to get register DMA: %d\n", ret);
0559         goto err_dis_pclk;
0560     }
0561 
0562     pm_runtime_enable(&pdev->dev);
0563 
0564     ret = devm_snd_soc_register_component(&pdev->dev, &s3c_pcm_component,
0565                      &s3c_pcm_dai[pdev->id], 1);
0566     if (ret != 0) {
0567         dev_err(&pdev->dev, "failed to get register DAI: %d\n", ret);
0568         goto err_dis_pm;
0569     }
0570 
0571     return 0;
0572 
0573 err_dis_pm:
0574     pm_runtime_disable(&pdev->dev);
0575 err_dis_pclk:
0576     clk_disable_unprepare(pcm->pclk);
0577 err_dis_cclk:
0578     clk_disable_unprepare(pcm->cclk);
0579     return ret;
0580 }
0581 
0582 static int s3c_pcm_dev_remove(struct platform_device *pdev)
0583 {
0584     struct s3c_pcm_info *pcm = &s3c_pcm[pdev->id];
0585 
0586     pm_runtime_disable(&pdev->dev);
0587     clk_disable_unprepare(pcm->cclk);
0588     clk_disable_unprepare(pcm->pclk);
0589 
0590     return 0;
0591 }
0592 
0593 static struct platform_driver s3c_pcm_driver = {
0594     .probe  = s3c_pcm_dev_probe,
0595     .remove = s3c_pcm_dev_remove,
0596     .driver = {
0597         .name = "samsung-pcm",
0598     },
0599 };
0600 
0601 module_platform_driver(s3c_pcm_driver);
0602 
0603 /* Module information */
0604 MODULE_AUTHOR("Jaswinder Singh, <jassisinghbrar@gmail.com>");
0605 MODULE_DESCRIPTION("S3C PCM Controller Driver");
0606 MODULE_LICENSE("GPL");
0607 MODULE_ALIAS("platform:samsung-pcm");