1 /* ALSA Soc Audio Layer - I2S core for newer Samsung SoCs. 2 * 3 * Copyright (c) 2006 Wolfson Microelectronics PLC. 4 * Graeme Gregory graeme.gregory@wolfsonmicro.com 5 * linux@wolfsonmicro.com 6 * 7 * Copyright (c) 2008, 2007, 2004-2005 Simtec Electronics 8 * http://armlinux.simtec.co.uk/ 9 * Ben Dooks <ben@simtec.co.uk> 10 * 11 * This program is free software; you can redistribute it and/or modify it 12 * under the terms of the GNU General Public License as published by the 13 * Free Software Foundation; either version 2 of the License, or (at your 14 * option) any later version. 15 */ 16 17 #include <linux/module.h> 18 #include <linux/delay.h> 19 #include <linux/clk.h> 20 #include <linux/io.h> 21 22 #include <sound/soc.h> 23 #include <sound/pcm_params.h> 24 25 #include "regs-i2s-v2.h" 26 #include "s3c-i2s-v2.h" 27 28 #undef S3C_IIS_V2_SUPPORTED 29 30 #if defined(CONFIG_CPU_S3C2412) || defined(CONFIG_CPU_S3C2413) \ 31 || defined(CONFIG_ARCH_S3C64XX) || defined(CONFIG_CPU_S5PV210) 32 #define S3C_IIS_V2_SUPPORTED 33 #endif 34 35 #ifndef S3C_IIS_V2_SUPPORTED 36 #error Unsupported CPU model 37 #endif 38 39 #define S3C2412_I2S_DEBUG_CON 0 40 41 static inline struct s3c_i2sv2_info *to_info(struct snd_soc_dai *cpu_dai) 42 { 43 return snd_soc_dai_get_drvdata(cpu_dai); 44 } 45 46 #define bit_set(v, b) (((v) & (b)) ? 1 : 0) 47 48 #if S3C2412_I2S_DEBUG_CON 49 static void dbg_showcon(const char *fn, u32 con) 50 { 51 printk(KERN_DEBUG "%s: LRI=%d, TXFEMPT=%d, RXFEMPT=%d, TXFFULL=%d, RXFFULL=%d\n", fn, 52 bit_set(con, S3C2412_IISCON_LRINDEX), 53 bit_set(con, S3C2412_IISCON_TXFIFO_EMPTY), 54 bit_set(con, S3C2412_IISCON_RXFIFO_EMPTY), 55 bit_set(con, S3C2412_IISCON_TXFIFO_FULL), 56 bit_set(con, S3C2412_IISCON_RXFIFO_FULL)); 57 58 printk(KERN_DEBUG "%s: PAUSE: TXDMA=%d, RXDMA=%d, TXCH=%d, RXCH=%d\n", 59 fn, 60 bit_set(con, S3C2412_IISCON_TXDMA_PAUSE), 61 bit_set(con, S3C2412_IISCON_RXDMA_PAUSE), 62 bit_set(con, S3C2412_IISCON_TXCH_PAUSE), 63 bit_set(con, S3C2412_IISCON_RXCH_PAUSE)); 64 printk(KERN_DEBUG "%s: ACTIVE: TXDMA=%d, RXDMA=%d, IIS=%d\n", fn, 65 bit_set(con, S3C2412_IISCON_TXDMA_ACTIVE), 66 bit_set(con, S3C2412_IISCON_RXDMA_ACTIVE), 67 bit_set(con, S3C2412_IISCON_IIS_ACTIVE)); 68 } 69 #else 70 static inline void dbg_showcon(const char *fn, u32 con) 71 { 72 } 73 #endif 74 75 /* Turn on or off the transmission path. */ 76 static void s3c2412_snd_txctrl(struct s3c_i2sv2_info *i2s, int on) 77 { 78 void __iomem *regs = i2s->regs; 79 u32 fic, con, mod; 80 81 pr_debug("%s(%d)\n", __func__, on); 82 83 fic = readl(regs + S3C2412_IISFIC); 84 con = readl(regs + S3C2412_IISCON); 85 mod = readl(regs + S3C2412_IISMOD); 86 87 pr_debug("%s: IIS: CON=%x MOD=%x FIC=%x\n", __func__, con, mod, fic); 88 89 if (on) { 90 con |= S3C2412_IISCON_TXDMA_ACTIVE | S3C2412_IISCON_IIS_ACTIVE; 91 con &= ~S3C2412_IISCON_TXDMA_PAUSE; 92 con &= ~S3C2412_IISCON_TXCH_PAUSE; 93 94 switch (mod & S3C2412_IISMOD_MODE_MASK) { 95 case S3C2412_IISMOD_MODE_TXONLY: 96 case S3C2412_IISMOD_MODE_TXRX: 97 /* do nothing, we are in the right mode */ 98 break; 99 100 case S3C2412_IISMOD_MODE_RXONLY: 101 mod &= ~S3C2412_IISMOD_MODE_MASK; 102 mod |= S3C2412_IISMOD_MODE_TXRX; 103 break; 104 105 default: 106 dev_err(i2s->dev, "TXEN: Invalid MODE %x in IISMOD\n", 107 mod & S3C2412_IISMOD_MODE_MASK); 108 break; 109 } 110 111 writel(con, regs + S3C2412_IISCON); 112 writel(mod, regs + S3C2412_IISMOD); 113 } else { 114 /* Note, we do not have any indication that the FIFO problems 115 * tha the S3C2410/2440 had apply here, so we should be able 116 * to disable the DMA and TX without resetting the FIFOS. 117 */ 118 119 con |= S3C2412_IISCON_TXDMA_PAUSE; 120 con |= S3C2412_IISCON_TXCH_PAUSE; 121 con &= ~S3C2412_IISCON_TXDMA_ACTIVE; 122 123 switch (mod & S3C2412_IISMOD_MODE_MASK) { 124 case S3C2412_IISMOD_MODE_TXRX: 125 mod &= ~S3C2412_IISMOD_MODE_MASK; 126 mod |= S3C2412_IISMOD_MODE_RXONLY; 127 break; 128 129 case S3C2412_IISMOD_MODE_TXONLY: 130 mod &= ~S3C2412_IISMOD_MODE_MASK; 131 con &= ~S3C2412_IISCON_IIS_ACTIVE; 132 break; 133 134 default: 135 dev_err(i2s->dev, "TXDIS: Invalid MODE %x in IISMOD\n", 136 mod & S3C2412_IISMOD_MODE_MASK); 137 break; 138 } 139 140 writel(mod, regs + S3C2412_IISMOD); 141 writel(con, regs + S3C2412_IISCON); 142 } 143 144 fic = readl(regs + S3C2412_IISFIC); 145 dbg_showcon(__func__, con); 146 pr_debug("%s: IIS: CON=%x MOD=%x FIC=%x\n", __func__, con, mod, fic); 147 } 148 149 static void s3c2412_snd_rxctrl(struct s3c_i2sv2_info *i2s, int on) 150 { 151 void __iomem *regs = i2s->regs; 152 u32 fic, con, mod; 153 154 pr_debug("%s(%d)\n", __func__, on); 155 156 fic = readl(regs + S3C2412_IISFIC); 157 con = readl(regs + S3C2412_IISCON); 158 mod = readl(regs + S3C2412_IISMOD); 159 160 pr_debug("%s: IIS: CON=%x MOD=%x FIC=%x\n", __func__, con, mod, fic); 161 162 if (on) { 163 con |= S3C2412_IISCON_RXDMA_ACTIVE | S3C2412_IISCON_IIS_ACTIVE; 164 con &= ~S3C2412_IISCON_RXDMA_PAUSE; 165 con &= ~S3C2412_IISCON_RXCH_PAUSE; 166 167 switch (mod & S3C2412_IISMOD_MODE_MASK) { 168 case S3C2412_IISMOD_MODE_TXRX: 169 case S3C2412_IISMOD_MODE_RXONLY: 170 /* do nothing, we are in the right mode */ 171 break; 172 173 case S3C2412_IISMOD_MODE_TXONLY: 174 mod &= ~S3C2412_IISMOD_MODE_MASK; 175 mod |= S3C2412_IISMOD_MODE_TXRX; 176 break; 177 178 default: 179 dev_err(i2s->dev, "RXEN: Invalid MODE %x in IISMOD\n", 180 mod & S3C2412_IISMOD_MODE_MASK); 181 } 182 183 writel(mod, regs + S3C2412_IISMOD); 184 writel(con, regs + S3C2412_IISCON); 185 } else { 186 /* See txctrl notes on FIFOs. */ 187 188 con &= ~S3C2412_IISCON_RXDMA_ACTIVE; 189 con |= S3C2412_IISCON_RXDMA_PAUSE; 190 con |= S3C2412_IISCON_RXCH_PAUSE; 191 192 switch (mod & S3C2412_IISMOD_MODE_MASK) { 193 case S3C2412_IISMOD_MODE_RXONLY: 194 con &= ~S3C2412_IISCON_IIS_ACTIVE; 195 mod &= ~S3C2412_IISMOD_MODE_MASK; 196 break; 197 198 case S3C2412_IISMOD_MODE_TXRX: 199 mod &= ~S3C2412_IISMOD_MODE_MASK; 200 mod |= S3C2412_IISMOD_MODE_TXONLY; 201 break; 202 203 default: 204 dev_err(i2s->dev, "RXDIS: Invalid MODE %x in IISMOD\n", 205 mod & S3C2412_IISMOD_MODE_MASK); 206 } 207 208 writel(con, regs + S3C2412_IISCON); 209 writel(mod, regs + S3C2412_IISMOD); 210 } 211 212 fic = readl(regs + S3C2412_IISFIC); 213 pr_debug("%s: IIS: CON=%x MOD=%x FIC=%x\n", __func__, con, mod, fic); 214 } 215 216 #define msecs_to_loops(t) (loops_per_jiffy / 1000 * HZ * t) 217 218 /* 219 * Wait for the LR signal to allow synchronisation to the L/R clock 220 * from the codec. May only be needed for slave mode. 221 */ 222 static int s3c2412_snd_lrsync(struct s3c_i2sv2_info *i2s) 223 { 224 u32 iiscon; 225 unsigned long loops = msecs_to_loops(5); 226 227 pr_debug("Entered %s\n", __func__); 228 229 while (--loops) { 230 iiscon = readl(i2s->regs + S3C2412_IISCON); 231 if (iiscon & S3C2412_IISCON_LRINDEX) 232 break; 233 234 cpu_relax(); 235 } 236 237 if (!loops) { 238 printk(KERN_ERR "%s: timeout\n", __func__); 239 return -ETIMEDOUT; 240 } 241 242 return 0; 243 } 244 245 /* 246 * Set S3C2412 I2S DAI format 247 */ 248 static int s3c2412_i2s_set_fmt(struct snd_soc_dai *cpu_dai, 249 unsigned int fmt) 250 { 251 struct s3c_i2sv2_info *i2s = to_info(cpu_dai); 252 u32 iismod; 253 254 pr_debug("Entered %s\n", __func__); 255 256 iismod = readl(i2s->regs + S3C2412_IISMOD); 257 pr_debug("hw_params r: IISMOD: %x \n", iismod); 258 259 switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { 260 case SND_SOC_DAIFMT_CBM_CFM: 261 i2s->master = 0; 262 iismod |= S3C2412_IISMOD_SLAVE; 263 break; 264 case SND_SOC_DAIFMT_CBS_CFS: 265 i2s->master = 1; 266 iismod &= ~S3C2412_IISMOD_SLAVE; 267 break; 268 default: 269 pr_err("unknown master/slave format\n"); 270 return -EINVAL; 271 } 272 273 iismod &= ~S3C2412_IISMOD_SDF_MASK; 274 275 switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { 276 case SND_SOC_DAIFMT_RIGHT_J: 277 iismod |= S3C2412_IISMOD_LR_RLOW; 278 iismod |= S3C2412_IISMOD_SDF_MSB; 279 break; 280 case SND_SOC_DAIFMT_LEFT_J: 281 iismod |= S3C2412_IISMOD_LR_RLOW; 282 iismod |= S3C2412_IISMOD_SDF_LSB; 283 break; 284 case SND_SOC_DAIFMT_I2S: 285 iismod &= ~S3C2412_IISMOD_LR_RLOW; 286 iismod |= S3C2412_IISMOD_SDF_IIS; 287 break; 288 default: 289 pr_err("Unknown data format\n"); 290 return -EINVAL; 291 } 292 293 writel(iismod, i2s->regs + S3C2412_IISMOD); 294 pr_debug("hw_params w: IISMOD: %x \n", iismod); 295 return 0; 296 } 297 298 static int s3c_i2sv2_hw_params(struct snd_pcm_substream *substream, 299 struct snd_pcm_hw_params *params, 300 struct snd_soc_dai *dai) 301 { 302 struct s3c_i2sv2_info *i2s = to_info(dai); 303 struct snd_dmaengine_dai_dma_data *dma_data; 304 u32 iismod; 305 306 pr_debug("Entered %s\n", __func__); 307 308 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 309 dma_data = i2s->dma_playback; 310 else 311 dma_data = i2s->dma_capture; 312 313 snd_soc_dai_set_dma_data(dai, substream, dma_data); 314 315 /* Working copies of register */ 316 iismod = readl(i2s->regs + S3C2412_IISMOD); 317 pr_debug("%s: r: IISMOD: %x\n", __func__, iismod); 318 319 iismod &= ~S3C64XX_IISMOD_BLC_MASK; 320 /* Sample size */ 321 switch (params_width(params)) { 322 case 8: 323 iismod |= S3C64XX_IISMOD_BLC_8BIT; 324 break; 325 case 16: 326 break; 327 case 24: 328 iismod |= S3C64XX_IISMOD_BLC_24BIT; 329 break; 330 } 331 332 writel(iismod, i2s->regs + S3C2412_IISMOD); 333 pr_debug("%s: w: IISMOD: %x\n", __func__, iismod); 334 335 return 0; 336 } 337 338 static int s3c_i2sv2_set_sysclk(struct snd_soc_dai *cpu_dai, 339 int clk_id, unsigned int freq, int dir) 340 { 341 struct s3c_i2sv2_info *i2s = to_info(cpu_dai); 342 u32 iismod = readl(i2s->regs + S3C2412_IISMOD); 343 344 pr_debug("Entered %s\n", __func__); 345 pr_debug("%s r: IISMOD: %x\n", __func__, iismod); 346 347 switch (clk_id) { 348 case S3C_I2SV2_CLKSRC_PCLK: 349 iismod &= ~S3C2412_IISMOD_IMS_SYSMUX; 350 break; 351 352 case S3C_I2SV2_CLKSRC_AUDIOBUS: 353 iismod |= S3C2412_IISMOD_IMS_SYSMUX; 354 break; 355 356 case S3C_I2SV2_CLKSRC_CDCLK: 357 /* Error if controller doesn't have the CDCLKCON bit */ 358 if (!(i2s->feature & S3C_FEATURE_CDCLKCON)) 359 return -EINVAL; 360 361 switch (dir) { 362 case SND_SOC_CLOCK_IN: 363 iismod |= S3C64XX_IISMOD_CDCLKCON; 364 break; 365 case SND_SOC_CLOCK_OUT: 366 iismod &= ~S3C64XX_IISMOD_CDCLKCON; 367 break; 368 default: 369 return -EINVAL; 370 } 371 break; 372 373 default: 374 return -EINVAL; 375 } 376 377 writel(iismod, i2s->regs + S3C2412_IISMOD); 378 pr_debug("%s w: IISMOD: %x\n", __func__, iismod); 379 380 return 0; 381 } 382 383 static int s3c2412_i2s_trigger(struct snd_pcm_substream *substream, int cmd, 384 struct snd_soc_dai *dai) 385 { 386 struct snd_soc_pcm_runtime *rtd = substream->private_data; 387 struct s3c_i2sv2_info *i2s = to_info(rtd->cpu_dai); 388 int capture = (substream->stream == SNDRV_PCM_STREAM_CAPTURE); 389 unsigned long irqs; 390 int ret = 0; 391 392 pr_debug("Entered %s\n", __func__); 393 394 switch (cmd) { 395 case SNDRV_PCM_TRIGGER_START: 396 /* On start, ensure that the FIFOs are cleared and reset. */ 397 398 writel(capture ? S3C2412_IISFIC_RXFLUSH : S3C2412_IISFIC_TXFLUSH, 399 i2s->regs + S3C2412_IISFIC); 400 401 /* clear again, just in case */ 402 writel(0x0, i2s->regs + S3C2412_IISFIC); 403 404 case SNDRV_PCM_TRIGGER_RESUME: 405 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 406 if (!i2s->master) { 407 ret = s3c2412_snd_lrsync(i2s); 408 if (ret) 409 goto exit_err; 410 } 411 412 local_irq_save(irqs); 413 414 if (capture) 415 s3c2412_snd_rxctrl(i2s, 1); 416 else 417 s3c2412_snd_txctrl(i2s, 1); 418 419 local_irq_restore(irqs); 420 421 break; 422 423 case SNDRV_PCM_TRIGGER_STOP: 424 case SNDRV_PCM_TRIGGER_SUSPEND: 425 case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 426 local_irq_save(irqs); 427 428 if (capture) 429 s3c2412_snd_rxctrl(i2s, 0); 430 else 431 s3c2412_snd_txctrl(i2s, 0); 432 433 local_irq_restore(irqs); 434 break; 435 default: 436 ret = -EINVAL; 437 break; 438 } 439 440 exit_err: 441 return ret; 442 } 443 444 /* 445 * Set S3C2412 Clock dividers 446 */ 447 static int s3c2412_i2s_set_clkdiv(struct snd_soc_dai *cpu_dai, 448 int div_id, int div) 449 { 450 struct s3c_i2sv2_info *i2s = to_info(cpu_dai); 451 u32 reg; 452 453 pr_debug("%s(%p, %d, %d)\n", __func__, cpu_dai, div_id, div); 454 455 switch (div_id) { 456 case S3C_I2SV2_DIV_BCLK: 457 switch (div) { 458 case 16: 459 div = S3C2412_IISMOD_BCLK_16FS; 460 break; 461 462 case 32: 463 div = S3C2412_IISMOD_BCLK_32FS; 464 break; 465 466 case 24: 467 div = S3C2412_IISMOD_BCLK_24FS; 468 break; 469 470 case 48: 471 div = S3C2412_IISMOD_BCLK_48FS; 472 break; 473 474 default: 475 return -EINVAL; 476 } 477 478 reg = readl(i2s->regs + S3C2412_IISMOD); 479 reg &= ~S3C2412_IISMOD_BCLK_MASK; 480 writel(reg | div, i2s->regs + S3C2412_IISMOD); 481 482 pr_debug("%s: MOD=%08x\n", __func__, readl(i2s->regs + S3C2412_IISMOD)); 483 break; 484 485 case S3C_I2SV2_DIV_RCLK: 486 switch (div) { 487 case 256: 488 div = S3C2412_IISMOD_RCLK_256FS; 489 break; 490 491 case 384: 492 div = S3C2412_IISMOD_RCLK_384FS; 493 break; 494 495 case 512: 496 div = S3C2412_IISMOD_RCLK_512FS; 497 break; 498 499 case 768: 500 div = S3C2412_IISMOD_RCLK_768FS; 501 break; 502 503 default: 504 return -EINVAL; 505 } 506 507 reg = readl(i2s->regs + S3C2412_IISMOD); 508 reg &= ~S3C2412_IISMOD_RCLK_MASK; 509 writel(reg | div, i2s->regs + S3C2412_IISMOD); 510 pr_debug("%s: MOD=%08x\n", __func__, readl(i2s->regs + S3C2412_IISMOD)); 511 break; 512 513 case S3C_I2SV2_DIV_PRESCALER: 514 if (div >= 0) { 515 writel((div << 8) | S3C2412_IISPSR_PSREN, 516 i2s->regs + S3C2412_IISPSR); 517 } else { 518 writel(0x0, i2s->regs + S3C2412_IISPSR); 519 } 520 pr_debug("%s: PSR=%08x\n", __func__, readl(i2s->regs + S3C2412_IISPSR)); 521 break; 522 523 default: 524 return -EINVAL; 525 } 526 527 return 0; 528 } 529 530 static snd_pcm_sframes_t s3c2412_i2s_delay(struct snd_pcm_substream *substream, 531 struct snd_soc_dai *dai) 532 { 533 struct s3c_i2sv2_info *i2s = to_info(dai); 534 u32 reg = readl(i2s->regs + S3C2412_IISFIC); 535 snd_pcm_sframes_t delay; 536 537 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 538 delay = S3C2412_IISFIC_TXCOUNT(reg); 539 else 540 delay = S3C2412_IISFIC_RXCOUNT(reg); 541 542 return delay; 543 } 544 545 struct clk *s3c_i2sv2_get_clock(struct snd_soc_dai *cpu_dai) 546 { 547 struct s3c_i2sv2_info *i2s = to_info(cpu_dai); 548 u32 iismod = readl(i2s->regs + S3C2412_IISMOD); 549 550 if (iismod & S3C2412_IISMOD_IMS_SYSMUX) 551 return i2s->iis_cclk; 552 else 553 return i2s->iis_pclk; 554 } 555 EXPORT_SYMBOL_GPL(s3c_i2sv2_get_clock); 556 557 /* default table of all avaialable root fs divisors */ 558 static unsigned int iis_fs_tab[] = { 256, 512, 384, 768 }; 559 560 int s3c_i2sv2_iis_calc_rate(struct s3c_i2sv2_rate_calc *info, 561 unsigned int *fstab, 562 unsigned int rate, struct clk *clk) 563 { 564 unsigned long clkrate = clk_get_rate(clk); 565 unsigned int div; 566 unsigned int fsclk; 567 unsigned int actual; 568 unsigned int fs; 569 unsigned int fsdiv; 570 signed int deviation = 0; 571 unsigned int best_fs = 0; 572 unsigned int best_div = 0; 573 unsigned int best_rate = 0; 574 unsigned int best_deviation = INT_MAX; 575 576 pr_debug("Input clock rate %ldHz\n", clkrate); 577 578 if (fstab == NULL) 579 fstab = iis_fs_tab; 580 581 for (fs = 0; fs < ARRAY_SIZE(iis_fs_tab); fs++) { 582 fsdiv = iis_fs_tab[fs]; 583 584 fsclk = clkrate / fsdiv; 585 div = fsclk / rate; 586 587 if ((fsclk % rate) > (rate / 2)) 588 div++; 589 590 if (div <= 1) 591 continue; 592 593 actual = clkrate / (fsdiv * div); 594 deviation = actual - rate; 595 596 printk(KERN_DEBUG "%ufs: div %u => result %u, deviation %d\n", 597 fsdiv, div, actual, deviation); 598 599 deviation = abs(deviation); 600 601 if (deviation < best_deviation) { 602 best_fs = fsdiv; 603 best_div = div; 604 best_rate = actual; 605 best_deviation = deviation; 606 } 607 608 if (deviation == 0) 609 break; 610 } 611 612 printk(KERN_DEBUG "best: fs=%u, div=%u, rate=%u\n", 613 best_fs, best_div, best_rate); 614 615 info->fs_div = best_fs; 616 info->clk_div = best_div; 617 618 return 0; 619 } 620 EXPORT_SYMBOL_GPL(s3c_i2sv2_iis_calc_rate); 621 622 int s3c_i2sv2_probe(struct snd_soc_dai *dai, 623 struct s3c_i2sv2_info *i2s, 624 unsigned long base) 625 { 626 struct device *dev = dai->dev; 627 unsigned int iismod; 628 629 i2s->dev = dev; 630 631 /* record our i2s structure for later use in the callbacks */ 632 snd_soc_dai_set_drvdata(dai, i2s); 633 634 i2s->iis_pclk = clk_get(dev, "iis"); 635 if (IS_ERR(i2s->iis_pclk)) { 636 dev_err(dev, "failed to get iis_clock\n"); 637 iounmap(i2s->regs); 638 return -ENOENT; 639 } 640 641 clk_enable(i2s->iis_pclk); 642 643 /* Mark ourselves as in TXRX mode so we can run through our cleanup 644 * process without warnings. */ 645 iismod = readl(i2s->regs + S3C2412_IISMOD); 646 iismod |= S3C2412_IISMOD_MODE_TXRX; 647 writel(iismod, i2s->regs + S3C2412_IISMOD); 648 s3c2412_snd_txctrl(i2s, 0); 649 s3c2412_snd_rxctrl(i2s, 0); 650 651 return 0; 652 } 653 EXPORT_SYMBOL_GPL(s3c_i2sv2_probe); 654 655 #ifdef CONFIG_PM 656 static int s3c2412_i2s_suspend(struct snd_soc_dai *dai) 657 { 658 struct s3c_i2sv2_info *i2s = to_info(dai); 659 u32 iismod; 660 661 if (dai->active) { 662 i2s->suspend_iismod = readl(i2s->regs + S3C2412_IISMOD); 663 i2s->suspend_iiscon = readl(i2s->regs + S3C2412_IISCON); 664 i2s->suspend_iispsr = readl(i2s->regs + S3C2412_IISPSR); 665 666 /* some basic suspend checks */ 667 668 iismod = readl(i2s->regs + S3C2412_IISMOD); 669 670 if (iismod & S3C2412_IISCON_RXDMA_ACTIVE) 671 pr_warning("%s: RXDMA active?\n", __func__); 672 673 if (iismod & S3C2412_IISCON_TXDMA_ACTIVE) 674 pr_warning("%s: TXDMA active?\n", __func__); 675 676 if (iismod & S3C2412_IISCON_IIS_ACTIVE) 677 pr_warning("%s: IIS active\n", __func__); 678 } 679 680 return 0; 681 } 682 683 static int s3c2412_i2s_resume(struct snd_soc_dai *dai) 684 { 685 struct s3c_i2sv2_info *i2s = to_info(dai); 686 687 pr_info("dai_active %d, IISMOD %08x, IISCON %08x\n", 688 dai->active, i2s->suspend_iismod, i2s->suspend_iiscon); 689 690 if (dai->active) { 691 writel(i2s->suspend_iiscon, i2s->regs + S3C2412_IISCON); 692 writel(i2s->suspend_iismod, i2s->regs + S3C2412_IISMOD); 693 writel(i2s->suspend_iispsr, i2s->regs + S3C2412_IISPSR); 694 695 writel(S3C2412_IISFIC_RXFLUSH | S3C2412_IISFIC_TXFLUSH, 696 i2s->regs + S3C2412_IISFIC); 697 698 ndelay(250); 699 writel(0x0, i2s->regs + S3C2412_IISFIC); 700 } 701 702 return 0; 703 } 704 #else 705 #define s3c2412_i2s_suspend NULL 706 #define s3c2412_i2s_resume NULL 707 #endif 708 709 int s3c_i2sv2_register_component(struct device *dev, int id, 710 const struct snd_soc_component_driver *cmp_drv, 711 struct snd_soc_dai_driver *dai_drv) 712 { 713 struct snd_soc_dai_ops *ops = (struct snd_soc_dai_ops *)dai_drv->ops; 714 715 ops->trigger = s3c2412_i2s_trigger; 716 if (!ops->hw_params) 717 ops->hw_params = s3c_i2sv2_hw_params; 718 ops->set_fmt = s3c2412_i2s_set_fmt; 719 ops->set_clkdiv = s3c2412_i2s_set_clkdiv; 720 ops->set_sysclk = s3c_i2sv2_set_sysclk; 721 722 /* Allow overriding by (for example) IISv4 */ 723 if (!ops->delay) 724 ops->delay = s3c2412_i2s_delay; 725 726 dai_drv->suspend = s3c2412_i2s_suspend; 727 dai_drv->resume = s3c2412_i2s_resume; 728 729 return devm_snd_soc_register_component(dev, cmp_drv, dai_drv, 1); 730 } 731 EXPORT_SYMBOL_GPL(s3c_i2sv2_register_component); 732 733 MODULE_LICENSE("GPL"); 734
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.