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 case SNDRV_PCM_TRIGGER_RESUME: 401 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 402 if (!i2s->master) { 403 ret = s3c2412_snd_lrsync(i2s); 404 if (ret) 405 goto exit_err; 406 } 407 408 local_irq_save(irqs); 409 410 if (capture) 411 s3c2412_snd_rxctrl(i2s, 1); 412 else 413 s3c2412_snd_txctrl(i2s, 1); 414 415 local_irq_restore(irqs); 416 417 break; 418 419 case SNDRV_PCM_TRIGGER_STOP: 420 case SNDRV_PCM_TRIGGER_SUSPEND: 421 case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 422 local_irq_save(irqs); 423 424 if (capture) 425 s3c2412_snd_rxctrl(i2s, 0); 426 else 427 s3c2412_snd_txctrl(i2s, 0); 428 429 local_irq_restore(irqs); 430 break; 431 default: 432 ret = -EINVAL; 433 break; 434 } 435 436exit_err: 437 return ret; 438} 439 440/* 441 * Set S3C2412 Clock dividers 442 */ 443static int s3c2412_i2s_set_clkdiv(struct snd_soc_dai *cpu_dai, 444 int div_id, int div) 445{ 446 struct s3c_i2sv2_info *i2s = to_info(cpu_dai); 447 u32 reg; 448 449 pr_debug("%s(%p, %d, %d)\n", __func__, cpu_dai, div_id, div); 450 451 switch (div_id) { 452 case S3C_I2SV2_DIV_BCLK: 453 switch (div) { 454 case 16: 455 div = S3C2412_IISMOD_BCLK_16FS; 456 break; 457 458 case 32: 459 div = S3C2412_IISMOD_BCLK_32FS; 460 break; 461 462 case 24: 463 div = S3C2412_IISMOD_BCLK_24FS; 464 break; 465 466 case 48: 467 div = S3C2412_IISMOD_BCLK_48FS; 468 break; 469 470 default: 471 return -EINVAL; 472 } 473 474 reg = readl(i2s->regs + S3C2412_IISMOD); 475 reg &= ~S3C2412_IISMOD_BCLK_MASK; 476 writel(reg | div, i2s->regs + S3C2412_IISMOD); 477 478 pr_debug("%s: MOD=%08x\n", __func__, readl(i2s->regs + S3C2412_IISMOD)); 479 break; 480 481 case S3C_I2SV2_DIV_RCLK: 482 switch (div) { 483 case 256: 484 div = S3C2412_IISMOD_RCLK_256FS; 485 break; 486 487 case 384: 488 div = S3C2412_IISMOD_RCLK_384FS; 489 break; 490 491 case 512: 492 div = S3C2412_IISMOD_RCLK_512FS; 493 break; 494 495 case 768: 496 div = S3C2412_IISMOD_RCLK_768FS; 497 break; 498 499 default: 500 return -EINVAL; 501 } 502 503 reg = readl(i2s->regs + S3C2412_IISMOD); 504 reg &= ~S3C2412_IISMOD_RCLK_MASK; 505 writel(reg | div, i2s->regs + S3C2412_IISMOD); 506 pr_debug("%s: MOD=%08x\n", __func__, readl(i2s->regs + S3C2412_IISMOD)); 507 break; 508 509 case S3C_I2SV2_DIV_PRESCALER: 510 if (div >= 0) { 511 writel((div << 8) | S3C2412_IISPSR_PSREN, 512 i2s->regs + S3C2412_IISPSR); 513 } else { 514 writel(0x0, i2s->regs + S3C2412_IISPSR); 515 } 516 pr_debug("%s: PSR=%08x\n", __func__, readl(i2s->regs + S3C2412_IISPSR)); 517 break; 518 519 default: 520 return -EINVAL; 521 } 522 523 return 0; 524} 525 526static snd_pcm_sframes_t s3c2412_i2s_delay(struct snd_pcm_substream *substream, 527 struct snd_soc_dai *dai) 528{ 529 struct s3c_i2sv2_info *i2s = to_info(dai); 530 u32 reg = readl(i2s->regs + S3C2412_IISFIC); 531 snd_pcm_sframes_t delay; 532 533 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 534 delay = S3C2412_IISFIC_TXCOUNT(reg); 535 else 536 delay = S3C2412_IISFIC_RXCOUNT(reg); 537 538 return delay; 539} 540 541struct clk *s3c_i2sv2_get_clock(struct snd_soc_dai *cpu_dai) 542{ 543 struct s3c_i2sv2_info *i2s = to_info(cpu_dai); 544 u32 iismod = readl(i2s->regs + S3C2412_IISMOD); 545 546 if (iismod & S3C2412_IISMOD_IMS_SYSMUX) 547 return i2s->iis_cclk; 548 else 549 return i2s->iis_pclk; 550} 551EXPORT_SYMBOL_GPL(s3c_i2sv2_get_clock); 552 553/* default table of all avaialable root fs divisors */ 554static unsigned int iis_fs_tab[] = { 256, 512, 384, 768 }; 555 556int s3c_i2sv2_iis_calc_rate(struct s3c_i2sv2_rate_calc *info, 557 unsigned int *fstab, 558 unsigned int rate, struct clk *clk) 559{ 560 unsigned long clkrate = clk_get_rate(clk); 561 unsigned int div; 562 unsigned int fsclk; 563 unsigned int actual; 564 unsigned int fs; 565 unsigned int fsdiv; 566 signed int deviation = 0; 567 unsigned int best_fs = 0; 568 unsigned int best_div = 0; 569 unsigned int best_rate = 0; 570 unsigned int best_deviation = INT_MAX; 571 572 pr_debug("Input clock rate %ldHz\n", clkrate); 573 574 if (fstab == NULL) 575 fstab = iis_fs_tab; 576 577 for (fs = 0; fs < ARRAY_SIZE(iis_fs_tab); fs++) { 578 fsdiv = iis_fs_tab[fs]; 579 580 fsclk = clkrate / fsdiv; 581 div = fsclk / rate; 582 583 if ((fsclk % rate) > (rate / 2)) 584 div++; 585 586 if (div <= 1) 587 continue; 588 589 actual = clkrate / (fsdiv * div); 590 deviation = actual - rate; 591 592 printk(KERN_DEBUG "%ufs: div %u => result %u, deviation %d\n", 593 fsdiv, div, actual, deviation); 594 595 deviation = abs(deviation); 596 597 if (deviation < best_deviation) { 598 best_fs = fsdiv; 599 best_div = div; 600 best_rate = actual; 601 best_deviation = deviation; 602 } 603 604 if (deviation == 0) 605 break; 606 } 607 608 printk(KERN_DEBUG "best: fs=%u, div=%u, rate=%u\n", 609 best_fs, best_div, best_rate); 610 611 info->fs_div = best_fs; 612 info->clk_div = best_div; 613 614 return 0; 615} 616EXPORT_SYMBOL_GPL(s3c_i2sv2_iis_calc_rate); 617 618int s3c_i2sv2_probe(struct snd_soc_dai *dai, 619 struct s3c_i2sv2_info *i2s) 620{ 621 struct device *dev = dai->dev; 622 unsigned int iismod; 623 624 i2s->dev = dev; 625 626 /* record our i2s structure for later use in the callbacks */ 627 snd_soc_dai_set_drvdata(dai, i2s); 628 629 i2s->iis_pclk = clk_get(dev, "iis"); 630 if (IS_ERR(i2s->iis_pclk)) { 631 dev_err(dev, "failed to get iis_clock\n"); 632 return -ENOENT; 633 } 634 635 clk_prepare_enable(i2s->iis_pclk); 636 637 /* Mark ourselves as in TXRX mode so we can run through our cleanup 638 * process without warnings. */ 639 iismod = readl(i2s->regs + S3C2412_IISMOD); 640 iismod |= S3C2412_IISMOD_MODE_TXRX; 641 writel(iismod, i2s->regs + S3C2412_IISMOD); 642 s3c2412_snd_txctrl(i2s, 0); 643 s3c2412_snd_rxctrl(i2s, 0); 644 645 return 0; 646} 647EXPORT_SYMBOL_GPL(s3c_i2sv2_probe); 648 649void s3c_i2sv2_cleanup(struct snd_soc_dai *dai, 650 struct s3c_i2sv2_info *i2s) 651{ 652 clk_disable_unprepare(i2s->iis_pclk); 653 clk_put(i2s->iis_pclk); 654 i2s->iis_pclk = NULL; 655} 656EXPORT_SYMBOL_GPL(s3c_i2sv2_cleanup); 657 658int s3c_i2sv2_register_component(struct device *dev, int id, 659 const struct snd_soc_component_driver *cmp_drv, 660 struct snd_soc_dai_driver *dai_drv) 661{ 662 struct snd_soc_dai_ops *ops = (struct snd_soc_dai_ops *)dai_drv->ops; 663 664 ops->trigger = s3c2412_i2s_trigger; 665 if (!ops->hw_params) 666 ops->hw_params = s3c_i2sv2_hw_params; 667 ops->set_fmt = s3c2412_i2s_set_fmt; 668 ops->set_clkdiv = s3c2412_i2s_set_clkdiv; 669 ops->set_sysclk = s3c_i2sv2_set_sysclk; 670 671 /* Allow overriding by (for example) IISv4 */ 672 if (!ops->delay) 673 ops->delay = s3c2412_i2s_delay; 674 675 return devm_snd_soc_register_component(dev, cmp_drv, dai_drv, 1); 676} 677EXPORT_SYMBOL_GPL(s3c_i2sv2_register_component); 678 679MODULE_LICENSE("GPL"); 680