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