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