root/sound/soc/samsung/s3c-i2s-v2.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. to_info
  2. dbg_showcon
  3. dbg_showcon
  4. s3c2412_snd_txctrl
  5. s3c2412_snd_rxctrl
  6. s3c2412_snd_lrsync
  7. s3c2412_i2s_set_fmt
  8. s3c_i2sv2_hw_params
  9. s3c_i2sv2_set_sysclk
  10. s3c2412_i2s_trigger
  11. s3c2412_i2s_set_clkdiv
  12. s3c2412_i2s_delay
  13. s3c_i2sv2_get_clock
  14. s3c_i2sv2_iis_calc_rate
  15. s3c_i2sv2_probe
  16. s3c_i2sv2_cleanup
  17. s3c2412_i2s_suspend
  18. s3c2412_i2s_resume
  19. s3c_i2sv2_register_component

   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 
  37 static 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
  45 static 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
  66 static inline void dbg_showcon(const char *fn, u32 con)
  67 {
  68 }
  69 #endif
  70 
  71 /* Turn on or off the transmission path. */
  72 static 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 
 145 static 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  */
 218 static 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  */
 244 static 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 
 294 static 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 
 334 static 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 
 379 static 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 = substream->private_data;
 383         struct s3c_i2sv2_info *i2s = to_info(rtd->cpu_dai);
 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 
 436 exit_err:
 437         return ret;
 438 }
 439 
 440 /*
 441  * Set S3C2412 Clock dividers
 442  */
 443 static 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 
 526 static 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 
 541 struct 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 }
 551 EXPORT_SYMBOL_GPL(s3c_i2sv2_get_clock);
 552 
 553 /* default table of all avaialable root fs divisors */
 554 static unsigned int iis_fs_tab[] = { 256, 512, 384, 768 };
 555 
 556 int 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 }
 616 EXPORT_SYMBOL_GPL(s3c_i2sv2_iis_calc_rate);
 617 
 618 int s3c_i2sv2_probe(struct snd_soc_dai *dai,
 619                     struct s3c_i2sv2_info *i2s,
 620                     unsigned long base)
 621 {
 622         struct device *dev = dai->dev;
 623         unsigned int iismod;
 624 
 625         i2s->dev = dev;
 626 
 627         /* record our i2s structure for later use in the callbacks */
 628         snd_soc_dai_set_drvdata(dai, i2s);
 629 
 630         i2s->iis_pclk = clk_get(dev, "iis");
 631         if (IS_ERR(i2s->iis_pclk)) {
 632                 dev_err(dev, "failed to get iis_clock\n");
 633                 return -ENOENT;
 634         }
 635 
 636         clk_prepare_enable(i2s->iis_pclk);
 637 
 638         /* Mark ourselves as in TXRX mode so we can run through our cleanup
 639          * process without warnings. */
 640         iismod = readl(i2s->regs + S3C2412_IISMOD);
 641         iismod |= S3C2412_IISMOD_MODE_TXRX;
 642         writel(iismod, i2s->regs + S3C2412_IISMOD);
 643         s3c2412_snd_txctrl(i2s, 0);
 644         s3c2412_snd_rxctrl(i2s, 0);
 645 
 646         return 0;
 647 }
 648 EXPORT_SYMBOL_GPL(s3c_i2sv2_probe);
 649 
 650 void s3c_i2sv2_cleanup(struct snd_soc_dai *dai,
 651                       struct s3c_i2sv2_info *i2s)
 652 {
 653         clk_disable_unprepare(i2s->iis_pclk);
 654         clk_put(i2s->iis_pclk);
 655         i2s->iis_pclk = NULL;
 656 }
 657 EXPORT_SYMBOL_GPL(s3c_i2sv2_cleanup);
 658 
 659 #ifdef CONFIG_PM
 660 static int s3c2412_i2s_suspend(struct snd_soc_dai *dai)
 661 {
 662         struct s3c_i2sv2_info *i2s = to_info(dai);
 663         u32 iismod;
 664 
 665         if (dai->active) {
 666                 i2s->suspend_iismod = readl(i2s->regs + S3C2412_IISMOD);
 667                 i2s->suspend_iiscon = readl(i2s->regs + S3C2412_IISCON);
 668                 i2s->suspend_iispsr = readl(i2s->regs + S3C2412_IISPSR);
 669 
 670                 /* some basic suspend checks */
 671 
 672                 iismod = readl(i2s->regs + S3C2412_IISMOD);
 673 
 674                 if (iismod & S3C2412_IISCON_RXDMA_ACTIVE)
 675                         pr_warning("%s: RXDMA active?\n", __func__);
 676 
 677                 if (iismod & S3C2412_IISCON_TXDMA_ACTIVE)
 678                         pr_warning("%s: TXDMA active?\n", __func__);
 679 
 680                 if (iismod & S3C2412_IISCON_IIS_ACTIVE)
 681                         pr_warning("%s: IIS active\n", __func__);
 682         }
 683 
 684         return 0;
 685 }
 686 
 687 static int s3c2412_i2s_resume(struct snd_soc_dai *dai)
 688 {
 689         struct s3c_i2sv2_info *i2s = to_info(dai);
 690 
 691         pr_info("dai_active %d, IISMOD %08x, IISCON %08x\n",
 692                 dai->active, i2s->suspend_iismod, i2s->suspend_iiscon);
 693 
 694         if (dai->active) {
 695                 writel(i2s->suspend_iiscon, i2s->regs + S3C2412_IISCON);
 696                 writel(i2s->suspend_iismod, i2s->regs + S3C2412_IISMOD);
 697                 writel(i2s->suspend_iispsr, i2s->regs + S3C2412_IISPSR);
 698 
 699                 writel(S3C2412_IISFIC_RXFLUSH | S3C2412_IISFIC_TXFLUSH,
 700                        i2s->regs + S3C2412_IISFIC);
 701 
 702                 ndelay(250);
 703                 writel(0x0, i2s->regs + S3C2412_IISFIC);
 704         }
 705 
 706         return 0;
 707 }
 708 #else
 709 #define s3c2412_i2s_suspend NULL
 710 #define s3c2412_i2s_resume  NULL
 711 #endif
 712 
 713 int s3c_i2sv2_register_component(struct device *dev, int id,
 714                            const struct snd_soc_component_driver *cmp_drv,
 715                            struct snd_soc_dai_driver *dai_drv)
 716 {
 717         struct snd_soc_dai_ops *ops = (struct snd_soc_dai_ops *)dai_drv->ops;
 718 
 719         ops->trigger = s3c2412_i2s_trigger;
 720         if (!ops->hw_params)
 721                 ops->hw_params = s3c_i2sv2_hw_params;
 722         ops->set_fmt = s3c2412_i2s_set_fmt;
 723         ops->set_clkdiv = s3c2412_i2s_set_clkdiv;
 724         ops->set_sysclk = s3c_i2sv2_set_sysclk;
 725 
 726         /* Allow overriding by (for example) IISv4 */
 727         if (!ops->delay)
 728                 ops->delay = s3c2412_i2s_delay;
 729 
 730         dai_drv->suspend = s3c2412_i2s_suspend;
 731         dai_drv->resume = s3c2412_i2s_resume;
 732 
 733         return devm_snd_soc_register_component(dev, cmp_drv, dai_drv, 1);
 734 }
 735 EXPORT_SYMBOL_GPL(s3c_i2sv2_register_component);
 736 
 737 MODULE_LICENSE("GPL");

/* [<][>][^][v][top][bottom][index][help] */