1 /* sound/soc/samsung/pcm.c 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 * This program is free software; you can redistribute it and/or modify 10 * it under the terms of the GNU General Public License version 2 as 11 * published by the Free Software Foundation. 12 */ 13 14 #include <linux/clk.h> 15 #include <linux/io.h> 16 #include <linux/module.h> 17 #include <linux/pm_runtime.h> 18 19 #include <sound/soc.h> 20 #include <sound/pcm_params.h> 21 22 #include <linux/platform_data/asoc-s3c.h> 23 24 #include "dma.h" 25 #include "pcm.h" 26 27 /*Register Offsets */ 28 #define S3C_PCM_CTL 0x00 29 #define S3C_PCM_CLKCTL 0x04 30 #define S3C_PCM_TXFIFO 0x08 31 #define S3C_PCM_RXFIFO 0x0C 32 #define S3C_PCM_IRQCTL 0x10 33 #define S3C_PCM_IRQSTAT 0x14 34 #define S3C_PCM_FIFOSTAT 0x18 35 #define S3C_PCM_CLRINT 0x20 36 37 /* PCM_CTL Bit-Fields */ 38 #define S3C_PCM_CTL_TXDIPSTICK_MASK 0x3f 39 #define S3C_PCM_CTL_TXDIPSTICK_SHIFT 13 40 #define S3C_PCM_CTL_RXDIPSTICK_MASK 0x3f 41 #define S3C_PCM_CTL_RXDIPSTICK_SHIFT 7 42 #define S3C_PCM_CTL_TXDMA_EN (0x1 << 6) 43 #define S3C_PCM_CTL_RXDMA_EN (0x1 << 5) 44 #define S3C_PCM_CTL_TXMSB_AFTER_FSYNC (0x1 << 4) 45 #define S3C_PCM_CTL_RXMSB_AFTER_FSYNC (0x1 << 3) 46 #define S3C_PCM_CTL_TXFIFO_EN (0x1 << 2) 47 #define S3C_PCM_CTL_RXFIFO_EN (0x1 << 1) 48 #define S3C_PCM_CTL_ENABLE (0x1 << 0) 49 50 /* PCM_CLKCTL Bit-Fields */ 51 #define S3C_PCM_CLKCTL_SERCLK_EN (0x1 << 19) 52 #define S3C_PCM_CLKCTL_SERCLKSEL_PCLK (0x1 << 18) 53 #define S3C_PCM_CLKCTL_SCLKDIV_MASK 0x1ff 54 #define S3C_PCM_CLKCTL_SYNCDIV_MASK 0x1ff 55 #define S3C_PCM_CLKCTL_SCLKDIV_SHIFT 9 56 #define S3C_PCM_CLKCTL_SYNCDIV_SHIFT 0 57 58 /* PCM_TXFIFO Bit-Fields */ 59 #define S3C_PCM_TXFIFO_DVALID (0x1 << 16) 60 #define S3C_PCM_TXFIFO_DATA_MSK (0xffff << 0) 61 62 /* PCM_RXFIFO Bit-Fields */ 63 #define S3C_PCM_RXFIFO_DVALID (0x1 << 16) 64 #define S3C_PCM_RXFIFO_DATA_MSK (0xffff << 0) 65 66 /* PCM_IRQCTL Bit-Fields */ 67 #define S3C_PCM_IRQCTL_IRQEN (0x1 << 14) 68 #define S3C_PCM_IRQCTL_WRDEN (0x1 << 12) 69 #define S3C_PCM_IRQCTL_TXEMPTYEN (0x1 << 11) 70 #define S3C_PCM_IRQCTL_TXALMSTEMPTYEN (0x1 << 10) 71 #define S3C_PCM_IRQCTL_TXFULLEN (0x1 << 9) 72 #define S3C_PCM_IRQCTL_TXALMSTFULLEN (0x1 << 8) 73 #define S3C_PCM_IRQCTL_TXSTARVEN (0x1 << 7) 74 #define S3C_PCM_IRQCTL_TXERROVRFLEN (0x1 << 6) 75 #define S3C_PCM_IRQCTL_RXEMPTEN (0x1 << 5) 76 #define S3C_PCM_IRQCTL_RXALMSTEMPTEN (0x1 << 4) 77 #define S3C_PCM_IRQCTL_RXFULLEN (0x1 << 3) 78 #define S3C_PCM_IRQCTL_RXALMSTFULLEN (0x1 << 2) 79 #define S3C_PCM_IRQCTL_RXSTARVEN (0x1 << 1) 80 #define S3C_PCM_IRQCTL_RXERROVRFLEN (0x1 << 0) 81 82 /* PCM_IRQSTAT Bit-Fields */ 83 #define S3C_PCM_IRQSTAT_IRQPND (0x1 << 13) 84 #define S3C_PCM_IRQSTAT_WRD_XFER (0x1 << 12) 85 #define S3C_PCM_IRQSTAT_TXEMPTY (0x1 << 11) 86 #define S3C_PCM_IRQSTAT_TXALMSTEMPTY (0x1 << 10) 87 #define S3C_PCM_IRQSTAT_TXFULL (0x1 << 9) 88 #define S3C_PCM_IRQSTAT_TXALMSTFULL (0x1 << 8) 89 #define S3C_PCM_IRQSTAT_TXSTARV (0x1 << 7) 90 #define S3C_PCM_IRQSTAT_TXERROVRFL (0x1 << 6) 91 #define S3C_PCM_IRQSTAT_RXEMPT (0x1 << 5) 92 #define S3C_PCM_IRQSTAT_RXALMSTEMPT (0x1 << 4) 93 #define S3C_PCM_IRQSTAT_RXFULL (0x1 << 3) 94 #define S3C_PCM_IRQSTAT_RXALMSTFULL (0x1 << 2) 95 #define S3C_PCM_IRQSTAT_RXSTARV (0x1 << 1) 96 #define S3C_PCM_IRQSTAT_RXERROVRFL (0x1 << 0) 97 98 /* PCM_FIFOSTAT Bit-Fields */ 99 #define S3C_PCM_FIFOSTAT_TXCNT_MSK (0x3f << 14) 100 #define S3C_PCM_FIFOSTAT_TXFIFOEMPTY (0x1 << 13) 101 #define S3C_PCM_FIFOSTAT_TXFIFOALMSTEMPTY (0x1 << 12) 102 #define S3C_PCM_FIFOSTAT_TXFIFOFULL (0x1 << 11) 103 #define S3C_PCM_FIFOSTAT_TXFIFOALMSTFULL (0x1 << 10) 104 #define S3C_PCM_FIFOSTAT_RXCNT_MSK (0x3f << 4) 105 #define S3C_PCM_FIFOSTAT_RXFIFOEMPTY (0x1 << 3) 106 #define S3C_PCM_FIFOSTAT_RXFIFOALMSTEMPTY (0x1 << 2) 107 #define S3C_PCM_FIFOSTAT_RXFIFOFULL (0x1 << 1) 108 #define S3C_PCM_FIFOSTAT_RXFIFOALMSTFULL (0x1 << 0) 109 110 /** 111 * struct s3c_pcm_info - S3C PCM Controller information 112 * @dev: The parent device passed to use from the probe. 113 * @regs: The pointer to the device register block. 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 s3c_dma_params *dma_playback; 131 struct s3c_dma_params *dma_capture; 132 }; 133 134 static struct s3c_dma_params s3c_pcm_stereo_out[] = { 135 [0] = { 136 .dma_size = 4, 137 }, 138 [1] = { 139 .dma_size = 4, 140 }, 141 }; 142 143 static struct s3c_dma_params s3c_pcm_stereo_in[] = { 144 [0] = { 145 .dma_size = 4, 146 }, 147 [1] = { 148 .dma_size = 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 = substream->private_data; 220 struct s3c_pcm_info *pcm = snd_soc_dai_get_drvdata(rtd->cpu_dai); 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 259 static 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 = substream->private_data; 264 struct s3c_pcm_info *pcm = snd_soc_dai_get_drvdata(rtd->cpu_dai); 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 318 static 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 383 exit: 384 spin_unlock_irqrestore(&pcm->lock, flags); 385 386 return ret; 387 } 388 389 static 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 406 static 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 435 static 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 443 static 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_rates = 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 471 static 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 482 static const struct snd_soc_component_driver s3c_pcm_component = { 483 .name = "s3c-pcm", 484 }; 485 486 static int s3c_pcm_dev_probe(struct platform_device *pdev) 487 { 488 struct s3c_pcm_info *pcm; 489 struct resource *mem_res, *dmatx_res, *dmarx_res; 490 struct s3c_audio_pdata *pcm_pdata; 491 int ret; 492 493 /* Check for valid device index */ 494 if ((pdev->id < 0) || pdev->id >= ARRAY_SIZE(s3c_pcm)) { 495 dev_err(&pdev->dev, "id %d out of range\n", pdev->id); 496 return -EINVAL; 497 } 498 499 pcm_pdata = pdev->dev.platform_data; 500 501 /* Check for availability of necessary resource */ 502 dmatx_res = platform_get_resource(pdev, IORESOURCE_DMA, 0); 503 if (!dmatx_res) { 504 dev_err(&pdev->dev, "Unable to get PCM-TX dma resource\n"); 505 return -ENXIO; 506 } 507 508 dmarx_res = platform_get_resource(pdev, IORESOURCE_DMA, 1); 509 if (!dmarx_res) { 510 dev_err(&pdev->dev, "Unable to get PCM-RX dma resource\n"); 511 return -ENXIO; 512 } 513 514 mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 515 if (!mem_res) { 516 dev_err(&pdev->dev, "Unable to get register resource\n"); 517 return -ENXIO; 518 } 519 520 if (pcm_pdata && pcm_pdata->cfg_gpio && pcm_pdata->cfg_gpio(pdev)) { 521 dev_err(&pdev->dev, "Unable to configure gpio\n"); 522 return -EINVAL; 523 } 524 525 pcm = &s3c_pcm[pdev->id]; 526 pcm->dev = &pdev->dev; 527 528 spin_lock_init(&pcm->lock); 529 530 /* Default is 128fs */ 531 pcm->sclk_per_fs = 128; 532 533 pcm->cclk = devm_clk_get(&pdev->dev, "audio-bus"); 534 if (IS_ERR(pcm->cclk)) { 535 dev_err(&pdev->dev, "failed to get audio-bus\n"); 536 ret = PTR_ERR(pcm->cclk); 537 goto err1; 538 } 539 clk_prepare_enable(pcm->cclk); 540 541 /* record our pcm structure for later use in the callbacks */ 542 dev_set_drvdata(&pdev->dev, pcm); 543 544 if (!request_mem_region(mem_res->start, 545 resource_size(mem_res), "samsung-pcm")) { 546 dev_err(&pdev->dev, "Unable to request register region\n"); 547 ret = -EBUSY; 548 goto err2; 549 } 550 551 pcm->regs = ioremap(mem_res->start, 0x100); 552 if (pcm->regs == NULL) { 553 dev_err(&pdev->dev, "cannot ioremap registers\n"); 554 ret = -ENXIO; 555 goto err3; 556 } 557 558 pcm->pclk = devm_clk_get(&pdev->dev, "pcm"); 559 if (IS_ERR(pcm->pclk)) { 560 dev_err(&pdev->dev, "failed to get pcm_clock\n"); 561 ret = -ENOENT; 562 goto err4; 563 } 564 clk_prepare_enable(pcm->pclk); 565 566 s3c_pcm_stereo_in[pdev->id].dma_addr = mem_res->start 567 + S3C_PCM_RXFIFO; 568 s3c_pcm_stereo_out[pdev->id].dma_addr = mem_res->start 569 + S3C_PCM_TXFIFO; 570 571 s3c_pcm_stereo_in[pdev->id].channel = dmarx_res->start; 572 s3c_pcm_stereo_out[pdev->id].channel = dmatx_res->start; 573 574 pcm->dma_capture = &s3c_pcm_stereo_in[pdev->id]; 575 pcm->dma_playback = &s3c_pcm_stereo_out[pdev->id]; 576 577 pm_runtime_enable(&pdev->dev); 578 579 ret = devm_snd_soc_register_component(&pdev->dev, &s3c_pcm_component, 580 &s3c_pcm_dai[pdev->id], 1); 581 if (ret != 0) { 582 dev_err(&pdev->dev, "failed to get register DAI: %d\n", ret); 583 goto err5; 584 } 585 586 ret = samsung_asoc_dma_platform_register(&pdev->dev); 587 if (ret) { 588 dev_err(&pdev->dev, "failed to get register DMA: %d\n", ret); 589 goto err5; 590 } 591 592 return 0; 593 594 err5: 595 clk_disable_unprepare(pcm->pclk); 596 err4: 597 iounmap(pcm->regs); 598 err3: 599 release_mem_region(mem_res->start, resource_size(mem_res)); 600 err2: 601 clk_disable_unprepare(pcm->cclk); 602 err1: 603 return ret; 604 } 605 606 static int s3c_pcm_dev_remove(struct platform_device *pdev) 607 { 608 struct s3c_pcm_info *pcm = &s3c_pcm[pdev->id]; 609 struct resource *mem_res; 610 611 pm_runtime_disable(&pdev->dev); 612 613 iounmap(pcm->regs); 614 615 mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 616 release_mem_region(mem_res->start, resource_size(mem_res)); 617 618 clk_disable_unprepare(pcm->cclk); 619 clk_disable_unprepare(pcm->pclk); 620 621 return 0; 622 } 623 624 static struct platform_driver s3c_pcm_driver = { 625 .probe = s3c_pcm_dev_probe, 626 .remove = s3c_pcm_dev_remove, 627 .driver = { 628 .name = "samsung-pcm", 629 }, 630 }; 631 632 module_platform_driver(s3c_pcm_driver); 633 634 /* Module information */ 635 MODULE_AUTHOR("Jaswinder Singh, <jassisinghbrar@gmail.com>"); 636 MODULE_DESCRIPTION("S3C PCM Controller Driver"); 637 MODULE_LICENSE("GPL"); 638 MODULE_ALIAS("platform:samsung-pcm"); 639