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 */ 117 struct 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 134 static 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 143 static 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 152 static struct s3c_pcm_info s3c_pcm[2]; 153 154 static 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 185 static 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 216 static 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 = snd_soc_substream_to_rtd(substream); 220 struct s3c_pcm_info *pcm = snd_soc_dai_get_drvdata(snd_soc_rtd_to_cpu(rtd, 0)); 221 222 dev_dbg(pcm->dev, "Entered %s\n", __func__); 223 224 switch (cmd) { 225 case SNDRV_PCM_TRIGGER_START: 226 case SNDRV_PCM_TRIGGER_RESUME: 227 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 228 scoped_guard(spinlock_irqsave, &pcm->lock) { 229 if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) 230 s3c_pcm_snd_rxctrl(pcm, 1); 231 else 232 s3c_pcm_snd_txctrl(pcm, 1); 233 } 234 break; 235 236 case SNDRV_PCM_TRIGGER_STOP: 237 case SNDRV_PCM_TRIGGER_SUSPEND: 238 case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 239 scoped_guard(spinlock_irqsave, &pcm->lock) { 240 if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) 241 s3c_pcm_snd_rxctrl(pcm, 0); 242 else 243 s3c_pcm_snd_txctrl(pcm, 0); 244 } 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 = snd_soc_substream_to_rtd(substream); 259 struct s3c_pcm_info *pcm = snd_soc_dai_get_drvdata(snd_soc_rtd_to_cpu(rtd, 0)); 260 void __iomem *regs = pcm->regs; 261 struct clk *clk; 262 int sclk_div, sync_div; 263 u32 clkctl; 264 265 dev_dbg(pcm->dev, "Entered %s\n", __func__); 266 267 /* Strictly check for sample size */ 268 switch (params_width(params)) { 269 case 16: 270 break; 271 default: 272 return -EINVAL; 273 } 274 275 scoped_guard(spinlock_irqsave, &pcm->lock) { 276 /* Get hold of the PCMSOURCE_CLK */ 277 clkctl = readl(regs + S3C_PCM_CLKCTL); 278 if (clkctl & S3C_PCM_CLKCTL_SERCLKSEL_PCLK) 279 clk = pcm->pclk; 280 else 281 clk = pcm->cclk; 282 283 /* Set the SCLK divider */ 284 sclk_div = clk_get_rate(clk) / pcm->sclk_per_fs / 285 params_rate(params) / 2 - 1; 286 287 clkctl &= ~(S3C_PCM_CLKCTL_SCLKDIV_MASK 288 << S3C_PCM_CLKCTL_SCLKDIV_SHIFT); 289 clkctl |= ((sclk_div & S3C_PCM_CLKCTL_SCLKDIV_MASK) 290 << S3C_PCM_CLKCTL_SCLKDIV_SHIFT); 291 292 /* Set the SYNC divider */ 293 sync_div = pcm->sclk_per_fs - 1; 294 295 clkctl &= ~(S3C_PCM_CLKCTL_SYNCDIV_MASK 296 << S3C_PCM_CLKCTL_SYNCDIV_SHIFT); 297 clkctl |= ((sync_div & S3C_PCM_CLKCTL_SYNCDIV_MASK) 298 << S3C_PCM_CLKCTL_SYNCDIV_SHIFT); 299 300 writel(clkctl, regs + S3C_PCM_CLKCTL); 301 } 302 dev_dbg(pcm->dev, "PCMSOURCE_CLK-%lu SCLK=%ufs SCLK_DIV=%d SYNC_DIV=%d\n", 303 clk_get_rate(clk), pcm->sclk_per_fs, 304 sclk_div, sync_div); 305 306 return 0; 307 } 308 309 static int s3c_pcm_set_fmt(struct snd_soc_dai *cpu_dai, 310 unsigned int fmt) 311 { 312 struct s3c_pcm_info *pcm = snd_soc_dai_get_drvdata(cpu_dai); 313 void __iomem *regs = pcm->regs; 314 u32 ctl; 315 316 dev_dbg(pcm->dev, "Entered %s\n", __func__); 317 318 guard(spinlock_irqsave)(&pcm->lock); 319 320 ctl = readl(regs + S3C_PCM_CTL); 321 322 switch (fmt & SND_SOC_DAIFMT_INV_MASK) { 323 case SND_SOC_DAIFMT_IB_NF: 324 /* Nothing to do, IB_NF by default */ 325 break; 326 default: 327 dev_err(pcm->dev, "Unsupported clock inversion!\n"); 328 return -EINVAL; 329 } 330 331 switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { 332 case SND_SOC_DAIFMT_BP_FP: 333 /* Nothing to do, Master by default */ 334 break; 335 default: 336 dev_err(pcm->dev, "Unsupported master/slave format!\n"); 337 return -EINVAL; 338 } 339 340 switch (fmt & SND_SOC_DAIFMT_CLOCK_MASK) { 341 case SND_SOC_DAIFMT_CONT: 342 pcm->idleclk = 1; 343 break; 344 case SND_SOC_DAIFMT_GATED: 345 pcm->idleclk = 0; 346 break; 347 default: 348 dev_err(pcm->dev, "Invalid Clock gating request!\n"); 349 return -EINVAL; 350 } 351 352 switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { 353 case SND_SOC_DAIFMT_DSP_A: 354 ctl |= S3C_PCM_CTL_TXMSB_AFTER_FSYNC; 355 ctl |= S3C_PCM_CTL_RXMSB_AFTER_FSYNC; 356 break; 357 case SND_SOC_DAIFMT_DSP_B: 358 ctl &= ~S3C_PCM_CTL_TXMSB_AFTER_FSYNC; 359 ctl &= ~S3C_PCM_CTL_RXMSB_AFTER_FSYNC; 360 break; 361 default: 362 dev_err(pcm->dev, "Unsupported data format!\n"); 363 return -EINVAL; 364 } 365 366 writel(ctl, regs + S3C_PCM_CTL); 367 return 0; 368 } 369 370 static int s3c_pcm_set_clkdiv(struct snd_soc_dai *cpu_dai, 371 int div_id, int div) 372 { 373 struct s3c_pcm_info *pcm = snd_soc_dai_get_drvdata(cpu_dai); 374 375 switch (div_id) { 376 case S3C_PCM_SCLK_PER_FS: 377 pcm->sclk_per_fs = div; 378 break; 379 380 default: 381 return -EINVAL; 382 } 383 384 return 0; 385 } 386 387 static int s3c_pcm_set_sysclk(struct snd_soc_dai *cpu_dai, 388 int clk_id, unsigned int freq, int dir) 389 { 390 struct s3c_pcm_info *pcm = snd_soc_dai_get_drvdata(cpu_dai); 391 void __iomem *regs = pcm->regs; 392 u32 clkctl = readl(regs + S3C_PCM_CLKCTL); 393 394 switch (clk_id) { 395 case S3C_PCM_CLKSRC_PCLK: 396 clkctl |= S3C_PCM_CLKCTL_SERCLKSEL_PCLK; 397 break; 398 399 case S3C_PCM_CLKSRC_MUX: 400 clkctl &= ~S3C_PCM_CLKCTL_SERCLKSEL_PCLK; 401 402 if (clk_get_rate(pcm->cclk) != freq) 403 clk_set_rate(pcm->cclk, freq); 404 405 break; 406 407 default: 408 return -EINVAL; 409 } 410 411 writel(clkctl, regs + S3C_PCM_CLKCTL); 412 413 return 0; 414 } 415 416 static int s3c_pcm_dai_probe(struct snd_soc_dai *dai) 417 { 418 struct s3c_pcm_info *pcm = snd_soc_dai_get_drvdata(dai); 419 420 snd_soc_dai_init_dma_data(dai, pcm->dma_playback, pcm->dma_capture); 421 422 return 0; 423 } 424 425 static const struct snd_soc_dai_ops s3c_pcm_dai_ops = { 426 .probe = s3c_pcm_dai_probe, 427 .set_sysclk = s3c_pcm_set_sysclk, 428 .set_clkdiv = s3c_pcm_set_clkdiv, 429 .trigger = s3c_pcm_trigger, 430 .hw_params = s3c_pcm_hw_params, 431 .set_fmt = s3c_pcm_set_fmt, 432 }; 433 434 #define S3C_PCM_RATES SNDRV_PCM_RATE_8000_96000 435 436 #define S3C_PCM_DAI_DECLARE \ 437 .symmetric_rate = 1, \ 438 .ops = &s3c_pcm_dai_ops, \ 439 .playback = { \ 440 .channels_min = 2, \ 441 .channels_max = 2, \ 442 .rates = S3C_PCM_RATES, \ 443 .formats = SNDRV_PCM_FMTBIT_S16_LE, \ 444 }, \ 445 .capture = { \ 446 .channels_min = 2, \ 447 .channels_max = 2, \ 448 .rates = S3C_PCM_RATES, \ 449 .formats = SNDRV_PCM_FMTBIT_S16_LE, \ 450 } 451 452 static struct snd_soc_dai_driver s3c_pcm_dai[] = { 453 [0] = { 454 .name = "samsung-pcm.0", 455 S3C_PCM_DAI_DECLARE, 456 }, 457 [1] = { 458 .name = "samsung-pcm.1", 459 S3C_PCM_DAI_DECLARE, 460 }, 461 }; 462 463 static const struct snd_soc_component_driver s3c_pcm_component = { 464 .name = "s3c-pcm", 465 .legacy_dai_naming = 1, 466 }; 467 468 static int s3c_pcm_dev_probe(struct platform_device *pdev) 469 { 470 struct s3c_pcm_info *pcm; 471 struct resource *mem_res; 472 struct s3c_audio_pdata *pcm_pdata; 473 dma_filter_fn filter; 474 int ret; 475 476 /* Check for valid device index */ 477 if ((pdev->id < 0) || pdev->id >= ARRAY_SIZE(s3c_pcm)) { 478 dev_err(&pdev->dev, "id %d out of range\n", pdev->id); 479 return -EINVAL; 480 } 481 482 pcm_pdata = pdev->dev.platform_data; 483 484 if (pcm_pdata && pcm_pdata->cfg_gpio && pcm_pdata->cfg_gpio(pdev)) { 485 dev_err(&pdev->dev, "Unable to configure gpio\n"); 486 return -EINVAL; 487 } 488 489 pcm = &s3c_pcm[pdev->id]; 490 pcm->dev = &pdev->dev; 491 492 spin_lock_init(&pcm->lock); 493 494 /* Default is 128fs */ 495 pcm->sclk_per_fs = 128; 496 497 pcm->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &mem_res); 498 if (IS_ERR(pcm->regs)) 499 return PTR_ERR(pcm->regs); 500 501 pcm->cclk = devm_clk_get(&pdev->dev, "audio-bus"); 502 if (IS_ERR(pcm->cclk)) { 503 dev_err(&pdev->dev, "failed to get audio-bus clock\n"); 504 return PTR_ERR(pcm->cclk); 505 } 506 ret = clk_prepare_enable(pcm->cclk); 507 if (ret) 508 return ret; 509 510 /* record our pcm structure for later use in the callbacks */ 511 dev_set_drvdata(&pdev->dev, pcm); 512 513 pcm->pclk = devm_clk_get(&pdev->dev, "pcm"); 514 if (IS_ERR(pcm->pclk)) { 515 dev_err(&pdev->dev, "failed to get pcm clock\n"); 516 ret = PTR_ERR(pcm->pclk); 517 goto err_dis_cclk; 518 } 519 ret = clk_prepare_enable(pcm->pclk); 520 if (ret) 521 goto err_dis_cclk; 522 523 s3c_pcm_stereo_in[pdev->id].addr = mem_res->start + S3C_PCM_RXFIFO; 524 s3c_pcm_stereo_out[pdev->id].addr = mem_res->start + S3C_PCM_TXFIFO; 525 526 filter = NULL; 527 if (pcm_pdata) { 528 s3c_pcm_stereo_in[pdev->id].filter_data = pcm_pdata->dma_capture; 529 s3c_pcm_stereo_out[pdev->id].filter_data = pcm_pdata->dma_playback; 530 filter = pcm_pdata->dma_filter; 531 } 532 533 pcm->dma_capture = &s3c_pcm_stereo_in[pdev->id]; 534 pcm->dma_playback = &s3c_pcm_stereo_out[pdev->id]; 535 536 ret = samsung_asoc_dma_platform_register(&pdev->dev, filter, 537 NULL, NULL, NULL); 538 if (ret) { 539 dev_err(&pdev->dev, "failed to get register DMA: %d\n", ret); 540 goto err_dis_pclk; 541 } 542 543 pm_runtime_enable(&pdev->dev); 544 545 ret = devm_snd_soc_register_component(&pdev->dev, &s3c_pcm_component, 546 &s3c_pcm_dai[pdev->id], 1); 547 if (ret != 0) { 548 dev_err(&pdev->dev, "failed to get register DAI: %d\n", ret); 549 goto err_dis_pm; 550 } 551 552 return 0; 553 554 err_dis_pm: 555 pm_runtime_disable(&pdev->dev); 556 err_dis_pclk: 557 clk_disable_unprepare(pcm->pclk); 558 err_dis_cclk: 559 clk_disable_unprepare(pcm->cclk); 560 return ret; 561 } 562 563 static void s3c_pcm_dev_remove(struct platform_device *pdev) 564 { 565 struct s3c_pcm_info *pcm = &s3c_pcm[pdev->id]; 566 567 pm_runtime_disable(&pdev->dev); 568 clk_disable_unprepare(pcm->cclk); 569 clk_disable_unprepare(pcm->pclk); 570 } 571 572 static struct platform_driver s3c_pcm_driver = { 573 .probe = s3c_pcm_dev_probe, 574 .remove = s3c_pcm_dev_remove, 575 .driver = { 576 .name = "samsung-pcm", 577 }, 578 }; 579 580 module_platform_driver(s3c_pcm_driver); 581 582 /* Module information */ 583 MODULE_AUTHOR("Jaswinder Singh, <jassisinghbrar@gmail.com>"); 584 MODULE_DESCRIPTION("S3C PCM Controller Driver"); 585 MODULE_LICENSE("GPL"); 586 MODULE_ALIAS("platform:samsung-pcm"); 587