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_client s3c_pcm_dma_client_out = { 135 .name = "PCM Stereo out" 136 }; 137 138 static struct s3c_dma_client s3c_pcm_dma_client_in = { 139 .name = "PCM Stereo in" 140 }; 141 142 static struct s3c_dma_params s3c_pcm_stereo_out[] = { 143 [0] = { 144 .client = &s3c_pcm_dma_client_out, 145 .dma_size = 4, 146 }, 147 [1] = { 148 .client = &s3c_pcm_dma_client_out, 149 .dma_size = 4, 150 }, 151 }; 152 153 static struct s3c_dma_params s3c_pcm_stereo_in[] = { 154 [0] = { 155 .client = &s3c_pcm_dma_client_in, 156 .dma_size = 4, 157 }, 158 [1] = { 159 .client = &s3c_pcm_dma_client_in, 160 .dma_size = 4, 161 }, 162 }; 163 164 static struct s3c_pcm_info s3c_pcm[2]; 165 166 static void s3c_pcm_snd_txctrl(struct s3c_pcm_info *pcm, int on) 167 { 168 void __iomem *regs = pcm->regs; 169 u32 ctl, clkctl; 170 171 clkctl = readl(regs + S3C_PCM_CLKCTL); 172 ctl = readl(regs + S3C_PCM_CTL); 173 ctl &= ~(S3C_PCM_CTL_TXDIPSTICK_MASK 174 << S3C_PCM_CTL_TXDIPSTICK_SHIFT); 175 176 if (on) { 177 ctl |= S3C_PCM_CTL_TXDMA_EN; 178 ctl |= S3C_PCM_CTL_TXFIFO_EN; 179 ctl |= S3C_PCM_CTL_ENABLE; 180 ctl |= (0x4<<S3C_PCM_CTL_TXDIPSTICK_SHIFT); 181 clkctl |= S3C_PCM_CLKCTL_SERCLK_EN; 182 } else { 183 ctl &= ~S3C_PCM_CTL_TXDMA_EN; 184 ctl &= ~S3C_PCM_CTL_TXFIFO_EN; 185 186 if (!(ctl & S3C_PCM_CTL_RXFIFO_EN)) { 187 ctl &= ~S3C_PCM_CTL_ENABLE; 188 if (!pcm->idleclk) 189 clkctl |= S3C_PCM_CLKCTL_SERCLK_EN; 190 } 191 } 192 193 writel(clkctl, regs + S3C_PCM_CLKCTL); 194 writel(ctl, regs + S3C_PCM_CTL); 195 } 196 197 static void s3c_pcm_snd_rxctrl(struct s3c_pcm_info *pcm, int on) 198 { 199 void __iomem *regs = pcm->regs; 200 u32 ctl, clkctl; 201 202 ctl = readl(regs + S3C_PCM_CTL); 203 clkctl = readl(regs + S3C_PCM_CLKCTL); 204 ctl &= ~(S3C_PCM_CTL_RXDIPSTICK_MASK 205 << S3C_PCM_CTL_RXDIPSTICK_SHIFT); 206 207 if (on) { 208 ctl |= S3C_PCM_CTL_RXDMA_EN; 209 ctl |= S3C_PCM_CTL_RXFIFO_EN; 210 ctl |= S3C_PCM_CTL_ENABLE; 211 ctl |= (0x20<<S3C_PCM_CTL_RXDIPSTICK_SHIFT); 212 clkctl |= S3C_PCM_CLKCTL_SERCLK_EN; 213 } else { 214 ctl &= ~S3C_PCM_CTL_RXDMA_EN; 215 ctl &= ~S3C_PCM_CTL_RXFIFO_EN; 216 217 if (!(ctl & S3C_PCM_CTL_TXFIFO_EN)) { 218 ctl &= ~S3C_PCM_CTL_ENABLE; 219 if (!pcm->idleclk) 220 clkctl |= S3C_PCM_CLKCTL_SERCLK_EN; 221 } 222 } 223 224 writel(clkctl, regs + S3C_PCM_CLKCTL); 225 writel(ctl, regs + S3C_PCM_CTL); 226 } 227 228 static int s3c_pcm_trigger(struct snd_pcm_substream *substream, int cmd, 229 struct snd_soc_dai *dai) 230 { 231 struct snd_soc_pcm_runtime *rtd = substream->private_data; 232 struct s3c_pcm_info *pcm = snd_soc_dai_get_drvdata(rtd->cpu_dai); 233 unsigned long flags; 234 235 dev_dbg(pcm->dev, "Entered %s\n", __func__); 236 237 switch (cmd) { 238 case SNDRV_PCM_TRIGGER_START: 239 case SNDRV_PCM_TRIGGER_RESUME: 240 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 241 spin_lock_irqsave(&pcm->lock, flags); 242 243 if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) 244 s3c_pcm_snd_rxctrl(pcm, 1); 245 else 246 s3c_pcm_snd_txctrl(pcm, 1); 247 248 spin_unlock_irqrestore(&pcm->lock, flags); 249 break; 250 251 case SNDRV_PCM_TRIGGER_STOP: 252 case SNDRV_PCM_TRIGGER_SUSPEND: 253 case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 254 spin_lock_irqsave(&pcm->lock, flags); 255 256 if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) 257 s3c_pcm_snd_rxctrl(pcm, 0); 258 else 259 s3c_pcm_snd_txctrl(pcm, 0); 260 261 spin_unlock_irqrestore(&pcm->lock, flags); 262 break; 263 264 default: 265 return -EINVAL; 266 } 267 268 return 0; 269 } 270 271 static int s3c_pcm_hw_params(struct snd_pcm_substream *substream, 272 struct snd_pcm_hw_params *params, 273 struct snd_soc_dai *socdai) 274 { 275 struct snd_soc_pcm_runtime *rtd = substream->private_data; 276 struct s3c_pcm_info *pcm = snd_soc_dai_get_drvdata(rtd->cpu_dai); 277 void __iomem *regs = pcm->regs; 278 struct clk *clk; 279 int sclk_div, sync_div; 280 unsigned long flags; 281 u32 clkctl; 282 283 dev_dbg(pcm->dev, "Entered %s\n", __func__); 284 285 /* Strictly check for sample size */ 286 switch (params_width(params)) { 287 case 16: 288 break; 289 default: 290 return -EINVAL; 291 } 292 293 spin_lock_irqsave(&pcm->lock, flags); 294 295 /* Get hold of the PCMSOURCE_CLK */ 296 clkctl = readl(regs + S3C_PCM_CLKCTL); 297 if (clkctl & S3C_PCM_CLKCTL_SERCLKSEL_PCLK) 298 clk = pcm->pclk; 299 else 300 clk = pcm->cclk; 301 302 /* Set the SCLK divider */ 303 sclk_div = clk_get_rate(clk) / pcm->sclk_per_fs / 304 params_rate(params) / 2 - 1; 305 306 clkctl &= ~(S3C_PCM_CLKCTL_SCLKDIV_MASK 307 << S3C_PCM_CLKCTL_SCLKDIV_SHIFT); 308 clkctl |= ((sclk_div & S3C_PCM_CLKCTL_SCLKDIV_MASK) 309 << S3C_PCM_CLKCTL_SCLKDIV_SHIFT); 310 311 /* Set the SYNC divider */ 312 sync_div = pcm->sclk_per_fs - 1; 313 314 clkctl &= ~(S3C_PCM_CLKCTL_SYNCDIV_MASK 315 << S3C_PCM_CLKCTL_SYNCDIV_SHIFT); 316 clkctl |= ((sync_div & S3C_PCM_CLKCTL_SYNCDIV_MASK) 317 << S3C_PCM_CLKCTL_SYNCDIV_SHIFT); 318 319 writel(clkctl, regs + S3C_PCM_CLKCTL); 320 321 spin_unlock_irqrestore(&pcm->lock, flags); 322 323 dev_dbg(pcm->dev, "PCMSOURCE_CLK-%lu SCLK=%ufs SCLK_DIV=%d SYNC_DIV=%d\n", 324 clk_get_rate(clk), pcm->sclk_per_fs, 325 sclk_div, sync_div); 326 327 return 0; 328 } 329 330 static int s3c_pcm_set_fmt(struct snd_soc_dai *cpu_dai, 331 unsigned int fmt) 332 { 333 struct s3c_pcm_info *pcm = snd_soc_dai_get_drvdata(cpu_dai); 334 void __iomem *regs = pcm->regs; 335 unsigned long flags; 336 int ret = 0; 337 u32 ctl; 338 339 dev_dbg(pcm->dev, "Entered %s\n", __func__); 340 341 spin_lock_irqsave(&pcm->lock, flags); 342 343 ctl = readl(regs + S3C_PCM_CTL); 344 345 switch (fmt & SND_SOC_DAIFMT_INV_MASK) { 346 case SND_SOC_DAIFMT_IB_NF: 347 /* Nothing to do, IB_NF by default */ 348 break; 349 default: 350 dev_err(pcm->dev, "Unsupported clock inversion!\n"); 351 ret = -EINVAL; 352 goto exit; 353 } 354 355 switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { 356 case SND_SOC_DAIFMT_CBS_CFS: 357 /* Nothing to do, Master by default */ 358 break; 359 default: 360 dev_err(pcm->dev, "Unsupported master/slave format!\n"); 361 ret = -EINVAL; 362 goto exit; 363 } 364 365 switch (fmt & SND_SOC_DAIFMT_CLOCK_MASK) { 366 case SND_SOC_DAIFMT_CONT: 367 pcm->idleclk = 1; 368 break; 369 case SND_SOC_DAIFMT_GATED: 370 pcm->idleclk = 0; 371 break; 372 default: 373 dev_err(pcm->dev, "Invalid Clock gating request!\n"); 374 ret = -EINVAL; 375 goto exit; 376 } 377 378 switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { 379 case SND_SOC_DAIFMT_DSP_A: 380 ctl |= S3C_PCM_CTL_TXMSB_AFTER_FSYNC; 381 ctl |= S3C_PCM_CTL_RXMSB_AFTER_FSYNC; 382 break; 383 case SND_SOC_DAIFMT_DSP_B: 384 ctl &= ~S3C_PCM_CTL_TXMSB_AFTER_FSYNC; 385 ctl &= ~S3C_PCM_CTL_RXMSB_AFTER_FSYNC; 386 break; 387 default: 388 dev_err(pcm->dev, "Unsupported data format!\n"); 389 ret = -EINVAL; 390 goto exit; 391 } 392 393 writel(ctl, regs + S3C_PCM_CTL); 394 395 exit: 396 spin_unlock_irqrestore(&pcm->lock, flags); 397 398 return ret; 399 } 400 401 static int s3c_pcm_set_clkdiv(struct snd_soc_dai *cpu_dai, 402 int div_id, int div) 403 { 404 struct s3c_pcm_info *pcm = snd_soc_dai_get_drvdata(cpu_dai); 405 406 switch (div_id) { 407 case S3C_PCM_SCLK_PER_FS: 408 pcm->sclk_per_fs = div; 409 break; 410 411 default: 412 return -EINVAL; 413 } 414 415 return 0; 416 } 417 418 static int s3c_pcm_set_sysclk(struct snd_soc_dai *cpu_dai, 419 int clk_id, unsigned int freq, int dir) 420 { 421 struct s3c_pcm_info *pcm = snd_soc_dai_get_drvdata(cpu_dai); 422 void __iomem *regs = pcm->regs; 423 u32 clkctl = readl(regs + S3C_PCM_CLKCTL); 424 425 switch (clk_id) { 426 case S3C_PCM_CLKSRC_PCLK: 427 clkctl |= S3C_PCM_CLKCTL_SERCLKSEL_PCLK; 428 break; 429 430 case S3C_PCM_CLKSRC_MUX: 431 clkctl &= ~S3C_PCM_CLKCTL_SERCLKSEL_PCLK; 432 433 if (clk_get_rate(pcm->cclk) != freq) 434 clk_set_rate(pcm->cclk, freq); 435 436 break; 437 438 default: 439 return -EINVAL; 440 } 441 442 writel(clkctl, regs + S3C_PCM_CLKCTL); 443 444 return 0; 445 } 446 447 static const struct snd_soc_dai_ops s3c_pcm_dai_ops = { 448 .set_sysclk = s3c_pcm_set_sysclk, 449 .set_clkdiv = s3c_pcm_set_clkdiv, 450 .trigger = s3c_pcm_trigger, 451 .hw_params = s3c_pcm_hw_params, 452 .set_fmt = s3c_pcm_set_fmt, 453 }; 454 455 static int s3c_pcm_dai_probe(struct snd_soc_dai *dai) 456 { 457 struct s3c_pcm_info *pcm = snd_soc_dai_get_drvdata(dai); 458 459 snd_soc_dai_init_dma_data(dai, pcm->dma_playback, pcm->dma_capture); 460 461 return 0; 462 } 463 464 #define S3C_PCM_RATES SNDRV_PCM_RATE_8000_96000 465 466 #define S3C_PCM_DAI_DECLARE \ 467 .symmetric_rates = 1, \ 468 .probe = s3c_pcm_dai_probe, \ 469 .ops = &s3c_pcm_dai_ops, \ 470 .playback = { \ 471 .channels_min = 2, \ 472 .channels_max = 2, \ 473 .rates = S3C_PCM_RATES, \ 474 .formats = SNDRV_PCM_FMTBIT_S16_LE, \ 475 }, \ 476 .capture = { \ 477 .channels_min = 2, \ 478 .channels_max = 2, \ 479 .rates = S3C_PCM_RATES, \ 480 .formats = SNDRV_PCM_FMTBIT_S16_LE, \ 481 } 482 483 static struct snd_soc_dai_driver s3c_pcm_dai[] = { 484 [0] = { 485 .name = "samsung-pcm.0", 486 S3C_PCM_DAI_DECLARE, 487 }, 488 [1] = { 489 .name = "samsung-pcm.1", 490 S3C_PCM_DAI_DECLARE, 491 }, 492 }; 493 494 static const struct snd_soc_component_driver s3c_pcm_component = { 495 .name = "s3c-pcm", 496 }; 497 498 static int s3c_pcm_dev_probe(struct platform_device *pdev) 499 { 500 struct s3c_pcm_info *pcm; 501 struct resource *mem_res, *dmatx_res, *dmarx_res; 502 struct s3c_audio_pdata *pcm_pdata; 503 int ret; 504 505 /* Check for valid device index */ 506 if ((pdev->id < 0) || pdev->id >= ARRAY_SIZE(s3c_pcm)) { 507 dev_err(&pdev->dev, "id %d out of range\n", pdev->id); 508 return -EINVAL; 509 } 510 511 pcm_pdata = pdev->dev.platform_data; 512 513 /* Check for availability of necessary resource */ 514 dmatx_res = platform_get_resource(pdev, IORESOURCE_DMA, 0); 515 if (!dmatx_res) { 516 dev_err(&pdev->dev, "Unable to get PCM-TX dma resource\n"); 517 return -ENXIO; 518 } 519 520 dmarx_res = platform_get_resource(pdev, IORESOURCE_DMA, 1); 521 if (!dmarx_res) { 522 dev_err(&pdev->dev, "Unable to get PCM-RX dma resource\n"); 523 return -ENXIO; 524 } 525 526 mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 527 if (!mem_res) { 528 dev_err(&pdev->dev, "Unable to get register resource\n"); 529 return -ENXIO; 530 } 531 532 if (pcm_pdata && pcm_pdata->cfg_gpio && pcm_pdata->cfg_gpio(pdev)) { 533 dev_err(&pdev->dev, "Unable to configure gpio\n"); 534 return -EINVAL; 535 } 536 537 pcm = &s3c_pcm[pdev->id]; 538 pcm->dev = &pdev->dev; 539 540 spin_lock_init(&pcm->lock); 541 542 /* Default is 128fs */ 543 pcm->sclk_per_fs = 128; 544 545 pcm->cclk = devm_clk_get(&pdev->dev, "audio-bus"); 546 if (IS_ERR(pcm->cclk)) { 547 dev_err(&pdev->dev, "failed to get audio-bus\n"); 548 ret = PTR_ERR(pcm->cclk); 549 goto err1; 550 } 551 clk_prepare_enable(pcm->cclk); 552 553 /* record our pcm structure for later use in the callbacks */ 554 dev_set_drvdata(&pdev->dev, pcm); 555 556 if (!request_mem_region(mem_res->start, 557 resource_size(mem_res), "samsung-pcm")) { 558 dev_err(&pdev->dev, "Unable to request register region\n"); 559 ret = -EBUSY; 560 goto err2; 561 } 562 563 pcm->regs = ioremap(mem_res->start, 0x100); 564 if (pcm->regs == NULL) { 565 dev_err(&pdev->dev, "cannot ioremap registers\n"); 566 ret = -ENXIO; 567 goto err3; 568 } 569 570 pcm->pclk = devm_clk_get(&pdev->dev, "pcm"); 571 if (IS_ERR(pcm->pclk)) { 572 dev_err(&pdev->dev, "failed to get pcm_clock\n"); 573 ret = -ENOENT; 574 goto err4; 575 } 576 clk_prepare_enable(pcm->pclk); 577 578 s3c_pcm_stereo_in[pdev->id].dma_addr = mem_res->start 579 + S3C_PCM_RXFIFO; 580 s3c_pcm_stereo_out[pdev->id].dma_addr = mem_res->start 581 + S3C_PCM_TXFIFO; 582 583 s3c_pcm_stereo_in[pdev->id].channel = dmarx_res->start; 584 s3c_pcm_stereo_out[pdev->id].channel = dmatx_res->start; 585 586 pcm->dma_capture = &s3c_pcm_stereo_in[pdev->id]; 587 pcm->dma_playback = &s3c_pcm_stereo_out[pdev->id]; 588 589 pm_runtime_enable(&pdev->dev); 590 591 ret = devm_snd_soc_register_component(&pdev->dev, &s3c_pcm_component, 592 &s3c_pcm_dai[pdev->id], 1); 593 if (ret != 0) { 594 dev_err(&pdev->dev, "failed to get register DAI: %d\n", ret); 595 goto err5; 596 } 597 598 ret = samsung_asoc_dma_platform_register(&pdev->dev); 599 if (ret) { 600 dev_err(&pdev->dev, "failed to get register DMA: %d\n", ret); 601 goto err5; 602 } 603 604 return 0; 605 606 err5: 607 clk_disable_unprepare(pcm->pclk); 608 err4: 609 iounmap(pcm->regs); 610 err3: 611 release_mem_region(mem_res->start, resource_size(mem_res)); 612 err2: 613 clk_disable_unprepare(pcm->cclk); 614 err1: 615 return ret; 616 } 617 618 static int s3c_pcm_dev_remove(struct platform_device *pdev) 619 { 620 struct s3c_pcm_info *pcm = &s3c_pcm[pdev->id]; 621 struct resource *mem_res; 622 623 pm_runtime_disable(&pdev->dev); 624 625 iounmap(pcm->regs); 626 627 mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 628 release_mem_region(mem_res->start, resource_size(mem_res)); 629 630 clk_disable_unprepare(pcm->cclk); 631 clk_disable_unprepare(pcm->pclk); 632 633 return 0; 634 } 635 636 static struct platform_driver s3c_pcm_driver = { 637 .probe = s3c_pcm_dev_probe, 638 .remove = s3c_pcm_dev_remove, 639 .driver = { 640 .name = "samsung-pcm", 641 .owner = THIS_MODULE, 642 }, 643 }; 644 645 module_platform_driver(s3c_pcm_driver); 646 647 /* Module information */ 648 MODULE_AUTHOR("Jaswinder Singh, <jassisinghbrar@gmail.com>"); 649 MODULE_DESCRIPTION("S3C PCM Controller Driver"); 650 MODULE_LICENSE("GPL"); 651 MODULE_ALIAS("platform:samsung-pcm"); 652