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 */ 112 struct 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 129 static 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 138 static 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 147 static struct s3c_pcm_info s3c_pcm[2]; 148 149 static 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 180 static 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 211 static 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 254 static 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 313 static 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 378 exit: 379 spin_unlock_irqrestore(&pcm->lock, flags); 380 381 return ret; 382 } 383 384 static 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 401 static 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 430 static 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 438 static 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 466 static 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 477 static const struct snd_soc_component_driver s3c_pcm_component = { 478 .name = "s3c-pcm", 479 }; 480 481 static 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 568 err_dis_pm: 569 pm_runtime_disable(&pdev->dev); 570 err_dis_pclk: 571 clk_disable_unprepare(pcm->pclk); 572 err_dis_cclk: 573 clk_disable_unprepare(pcm->cclk); 574 return ret; 575 } 576 577 static 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 588 static 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 596 module_platform_driver(s3c_pcm_driver); 597 598 /* Module information */ 599 MODULE_AUTHOR("Jaswinder Singh, <jassisinghbrar@gmail.com>"); 600 MODULE_DESCRIPTION("S3C PCM Controller Driver"); 601 MODULE_LICENSE("GPL"); 602 MODULE_ALIAS("platform:samsung-pcm"); 603