cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

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");