0001
0002
0003
0004
0005
0006
0007
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
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
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
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
0054 #define S3C_PCM_TXFIFO_DVALID (0x1 << 16)
0055 #define S3C_PCM_TXFIFO_DATA_MSK (0xffff << 0)
0056
0057
0058 #define S3C_PCM_RXFIFO_DVALID (0x1 << 16)
0059 #define S3C_PCM_RXFIFO_DATA_MSK (0xffff << 0)
0060
0061
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
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
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
0107
0108
0109
0110
0111
0112
0113
0114
0115
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
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
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
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
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
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
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
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
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
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
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
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");