xref: /openbmc/linux/sound/soc/mxs/mxs-saif.c (revision 0bb98ba2)
12a24f2ceSDong Aisheng /*
22a24f2ceSDong Aisheng  * Copyright 2011 Freescale Semiconductor, Inc.
32a24f2ceSDong Aisheng  *
42a24f2ceSDong Aisheng  * This program is free software; you can redistribute it and/or modify
52a24f2ceSDong Aisheng  * it under the terms of the GNU General Public License as published by
62a24f2ceSDong Aisheng  * the Free Software Foundation; either version 2 of the License, or
72a24f2ceSDong Aisheng  * (at your option) any later version.
82a24f2ceSDong Aisheng  *
92a24f2ceSDong Aisheng  * This program is distributed in the hope that it will be useful,
102a24f2ceSDong Aisheng  * but WITHOUT ANY WARRANTY; without even the implied warranty of
112a24f2ceSDong Aisheng  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
122a24f2ceSDong Aisheng  * GNU General Public License for more details.
132a24f2ceSDong Aisheng  *
142a24f2ceSDong Aisheng  * You should have received a copy of the GNU General Public License along
152a24f2ceSDong Aisheng  * with this program; if not, write to the Free Software Foundation, Inc.,
162a24f2ceSDong Aisheng  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
172a24f2ceSDong Aisheng  */
182a24f2ceSDong Aisheng 
192a24f2ceSDong Aisheng #include <linux/module.h>
202a24f2ceSDong Aisheng #include <linux/init.h>
212a24f2ceSDong Aisheng #include <linux/platform_device.h>
222a24f2ceSDong Aisheng #include <linux/slab.h>
232a24f2ceSDong Aisheng #include <linux/dma-mapping.h>
242a24f2ceSDong Aisheng #include <linux/clk.h>
252a24f2ceSDong Aisheng #include <linux/delay.h>
262a24f2ceSDong Aisheng #include <sound/core.h>
272a24f2ceSDong Aisheng #include <sound/pcm.h>
282a24f2ceSDong Aisheng #include <sound/pcm_params.h>
292a24f2ceSDong Aisheng #include <sound/soc.h>
302a24f2ceSDong Aisheng #include <mach/dma.h>
312a24f2ceSDong Aisheng #include <asm/mach-types.h>
322a24f2ceSDong Aisheng #include <mach/hardware.h>
332a24f2ceSDong Aisheng #include <mach/mxs.h>
342a24f2ceSDong Aisheng 
352a24f2ceSDong Aisheng #include "mxs-saif.h"
362a24f2ceSDong Aisheng 
372a24f2ceSDong Aisheng static struct mxs_saif *mxs_saif[2];
382a24f2ceSDong Aisheng 
392a24f2ceSDong Aisheng static int mxs_saif_set_dai_sysclk(struct snd_soc_dai *cpu_dai,
402a24f2ceSDong Aisheng 			int clk_id, unsigned int freq, int dir)
412a24f2ceSDong Aisheng {
422a24f2ceSDong Aisheng 	struct mxs_saif *saif = snd_soc_dai_get_drvdata(cpu_dai);
432a24f2ceSDong Aisheng 
442a24f2ceSDong Aisheng 	switch (clk_id) {
452a24f2ceSDong Aisheng 	case MXS_SAIF_MCLK:
462a24f2ceSDong Aisheng 		saif->mclk = freq;
472a24f2ceSDong Aisheng 		break;
482a24f2ceSDong Aisheng 	default:
492a24f2ceSDong Aisheng 		return -EINVAL;
502a24f2ceSDong Aisheng 	}
512a24f2ceSDong Aisheng 	return 0;
522a24f2ceSDong Aisheng }
532a24f2ceSDong Aisheng 
542a24f2ceSDong Aisheng /*
552a24f2ceSDong Aisheng  * Set SAIF clock and MCLK
562a24f2ceSDong Aisheng  */
572a24f2ceSDong Aisheng static int mxs_saif_set_clk(struct mxs_saif *saif,
582a24f2ceSDong Aisheng 				  unsigned int mclk,
592a24f2ceSDong Aisheng 				  unsigned int rate)
602a24f2ceSDong Aisheng {
612a24f2ceSDong Aisheng 	u32 scr;
622a24f2ceSDong Aisheng 	int ret;
632a24f2ceSDong Aisheng 
642a24f2ceSDong Aisheng 	scr = __raw_readl(saif->base + SAIF_CTRL);
652a24f2ceSDong Aisheng 	scr &= ~BM_SAIF_CTRL_BITCLK_MULT_RATE;
662a24f2ceSDong Aisheng 	scr &= ~BM_SAIF_CTRL_BITCLK_BASE_RATE;
672a24f2ceSDong Aisheng 
682a24f2ceSDong Aisheng 	/*
692a24f2ceSDong Aisheng 	 * Set SAIF clock
702a24f2ceSDong Aisheng 	 *
712a24f2ceSDong Aisheng 	 * The SAIF clock should be either 384*fs or 512*fs.
722a24f2ceSDong Aisheng 	 * If MCLK is used, the SAIF clk ratio need to match mclk ratio.
732a24f2ceSDong Aisheng 	 *  For 32x mclk, set saif clk as 512*fs.
742a24f2ceSDong Aisheng 	 *  For 48x mclk, set saif clk as 384*fs.
752a24f2ceSDong Aisheng 	 *
762a24f2ceSDong Aisheng 	 * If MCLK is not used, we just set saif clk to 512*fs.
772a24f2ceSDong Aisheng 	 */
782a24f2ceSDong Aisheng 	if (saif->mclk_in_use) {
792a24f2ceSDong Aisheng 		if (mclk % 32 == 0) {
802a24f2ceSDong Aisheng 			scr &= ~BM_SAIF_CTRL_BITCLK_BASE_RATE;
812a24f2ceSDong Aisheng 			ret = clk_set_rate(saif->clk, 512 * rate);
822a24f2ceSDong Aisheng 		} else if (mclk % 48 == 0) {
832a24f2ceSDong Aisheng 			scr |= BM_SAIF_CTRL_BITCLK_BASE_RATE;
842a24f2ceSDong Aisheng 			ret = clk_set_rate(saif->clk, 384 * rate);
852a24f2ceSDong Aisheng 		} else {
862a24f2ceSDong Aisheng 			/* SAIF MCLK should be either 32x or 48x */
872a24f2ceSDong Aisheng 			return -EINVAL;
882a24f2ceSDong Aisheng 		}
892a24f2ceSDong Aisheng 	} else {
902a24f2ceSDong Aisheng 		ret = clk_set_rate(saif->clk, 512 * rate);
912a24f2ceSDong Aisheng 		scr &= ~BM_SAIF_CTRL_BITCLK_BASE_RATE;
922a24f2ceSDong Aisheng 	}
932a24f2ceSDong Aisheng 
942a24f2ceSDong Aisheng 	if (ret)
952a24f2ceSDong Aisheng 		return ret;
962a24f2ceSDong Aisheng 
972a24f2ceSDong Aisheng 	if (!saif->mclk_in_use) {
982a24f2ceSDong Aisheng 		__raw_writel(scr, saif->base + SAIF_CTRL);
992a24f2ceSDong Aisheng 		return 0;
1002a24f2ceSDong Aisheng 	}
1012a24f2ceSDong Aisheng 
1022a24f2ceSDong Aisheng 	/*
1032a24f2ceSDong Aisheng 	 * Program the over-sample rate for MCLK output
1042a24f2ceSDong Aisheng 	 *
1052a24f2ceSDong Aisheng 	 * The available MCLK range is 32x, 48x... 512x. The rate
1062a24f2ceSDong Aisheng 	 * could be from 8kHz to 192kH.
1072a24f2ceSDong Aisheng 	 */
1082a24f2ceSDong Aisheng 	switch (mclk / rate) {
1092a24f2ceSDong Aisheng 	case 32:
1102a24f2ceSDong Aisheng 		scr |= BF_SAIF_CTRL_BITCLK_MULT_RATE(4);
1112a24f2ceSDong Aisheng 		break;
1122a24f2ceSDong Aisheng 	case 64:
1132a24f2ceSDong Aisheng 		scr |= BF_SAIF_CTRL_BITCLK_MULT_RATE(3);
1142a24f2ceSDong Aisheng 		break;
1152a24f2ceSDong Aisheng 	case 128:
1162a24f2ceSDong Aisheng 		scr |= BF_SAIF_CTRL_BITCLK_MULT_RATE(2);
1172a24f2ceSDong Aisheng 		break;
1182a24f2ceSDong Aisheng 	case 256:
1192a24f2ceSDong Aisheng 		scr |= BF_SAIF_CTRL_BITCLK_MULT_RATE(1);
1202a24f2ceSDong Aisheng 		break;
1212a24f2ceSDong Aisheng 	case 512:
1222a24f2ceSDong Aisheng 		scr |= BF_SAIF_CTRL_BITCLK_MULT_RATE(0);
1232a24f2ceSDong Aisheng 		break;
1242a24f2ceSDong Aisheng 	case 48:
1252a24f2ceSDong Aisheng 		scr |= BF_SAIF_CTRL_BITCLK_MULT_RATE(3);
1262a24f2ceSDong Aisheng 		break;
1272a24f2ceSDong Aisheng 	case 96:
1282a24f2ceSDong Aisheng 		scr |= BF_SAIF_CTRL_BITCLK_MULT_RATE(2);
1292a24f2ceSDong Aisheng 		break;
1302a24f2ceSDong Aisheng 	case 192:
1312a24f2ceSDong Aisheng 		scr |= BF_SAIF_CTRL_BITCLK_MULT_RATE(1);
1322a24f2ceSDong Aisheng 		break;
1332a24f2ceSDong Aisheng 	case 384:
1342a24f2ceSDong Aisheng 		scr |= BF_SAIF_CTRL_BITCLK_MULT_RATE(0);
1352a24f2ceSDong Aisheng 		break;
1362a24f2ceSDong Aisheng 	default:
1372a24f2ceSDong Aisheng 		return -EINVAL;
1382a24f2ceSDong Aisheng 	}
1392a24f2ceSDong Aisheng 
1402a24f2ceSDong Aisheng 	__raw_writel(scr, saif->base + SAIF_CTRL);
1412a24f2ceSDong Aisheng 
1422a24f2ceSDong Aisheng 	return 0;
1432a24f2ceSDong Aisheng }
1442a24f2ceSDong Aisheng 
1452a24f2ceSDong Aisheng /*
1462a24f2ceSDong Aisheng  * Put and disable MCLK.
1472a24f2ceSDong Aisheng  */
1482a24f2ceSDong Aisheng int mxs_saif_put_mclk(unsigned int saif_id)
1492a24f2ceSDong Aisheng {
1502a24f2ceSDong Aisheng 	struct mxs_saif *saif = mxs_saif[saif_id];
1512a24f2ceSDong Aisheng 	u32 stat;
1522a24f2ceSDong Aisheng 
1532a24f2ceSDong Aisheng 	if (!saif)
1542a24f2ceSDong Aisheng 		return -EINVAL;
1552a24f2ceSDong Aisheng 
1562a24f2ceSDong Aisheng 	stat = __raw_readl(saif->base + SAIF_STAT);
1572a24f2ceSDong Aisheng 	if (stat & BM_SAIF_STAT_BUSY) {
1582a24f2ceSDong Aisheng 		dev_err(saif->dev, "error: busy\n");
1592a24f2ceSDong Aisheng 		return -EBUSY;
1602a24f2ceSDong Aisheng 	}
1612a24f2ceSDong Aisheng 
1622a24f2ceSDong Aisheng 	clk_disable(saif->clk);
1632a24f2ceSDong Aisheng 
1642a24f2ceSDong Aisheng 	/* disable MCLK output */
1652a24f2ceSDong Aisheng 	__raw_writel(BM_SAIF_CTRL_CLKGATE,
1662a24f2ceSDong Aisheng 		saif->base + SAIF_CTRL + MXS_SET_ADDR);
1672a24f2ceSDong Aisheng 	__raw_writel(BM_SAIF_CTRL_RUN,
1682a24f2ceSDong Aisheng 		saif->base + SAIF_CTRL + MXS_CLR_ADDR);
1692a24f2ceSDong Aisheng 
1702a24f2ceSDong Aisheng 	saif->mclk_in_use = 0;
1712a24f2ceSDong Aisheng 	return 0;
1722a24f2ceSDong Aisheng }
1732a24f2ceSDong Aisheng 
1742a24f2ceSDong Aisheng /*
1752a24f2ceSDong Aisheng  * Get MCLK and set clock rate, then enable it
1762a24f2ceSDong Aisheng  *
1772a24f2ceSDong Aisheng  * This interface is used for codecs who are using MCLK provided
1782a24f2ceSDong Aisheng  * by saif.
1792a24f2ceSDong Aisheng  */
1802a24f2ceSDong Aisheng int mxs_saif_get_mclk(unsigned int saif_id, unsigned int mclk,
1812a24f2ceSDong Aisheng 					unsigned int rate)
1822a24f2ceSDong Aisheng {
1832a24f2ceSDong Aisheng 	struct mxs_saif *saif = mxs_saif[saif_id];
1842a24f2ceSDong Aisheng 	u32 stat;
1852a24f2ceSDong Aisheng 	int ret;
1862a24f2ceSDong Aisheng 
1872a24f2ceSDong Aisheng 	if (!saif)
1882a24f2ceSDong Aisheng 		return -EINVAL;
1892a24f2ceSDong Aisheng 
190bbe8ff5eSDong Aisheng 	/* Clear Reset */
191bbe8ff5eSDong Aisheng 	__raw_writel(BM_SAIF_CTRL_SFTRST,
192bbe8ff5eSDong Aisheng 		saif->base + SAIF_CTRL + MXS_CLR_ADDR);
193bbe8ff5eSDong Aisheng 
194bbe8ff5eSDong Aisheng 	/* FIXME: need clear clk gate for register r/w */
195bbe8ff5eSDong Aisheng 	__raw_writel(BM_SAIF_CTRL_CLKGATE,
196bbe8ff5eSDong Aisheng 		saif->base + SAIF_CTRL + MXS_CLR_ADDR);
197bbe8ff5eSDong Aisheng 
1982a24f2ceSDong Aisheng 	stat = __raw_readl(saif->base + SAIF_STAT);
1992a24f2ceSDong Aisheng 	if (stat & BM_SAIF_STAT_BUSY) {
2002a24f2ceSDong Aisheng 		dev_err(saif->dev, "error: busy\n");
2012a24f2ceSDong Aisheng 		return -EBUSY;
2022a24f2ceSDong Aisheng 	}
2032a24f2ceSDong Aisheng 
2042a24f2ceSDong Aisheng 	saif->mclk_in_use = 1;
2052a24f2ceSDong Aisheng 	ret = mxs_saif_set_clk(saif, mclk, rate);
2062a24f2ceSDong Aisheng 	if (ret)
2072a24f2ceSDong Aisheng 		return ret;
2082a24f2ceSDong Aisheng 
2092a24f2ceSDong Aisheng 	ret = clk_enable(saif->clk);
2102a24f2ceSDong Aisheng 	if (ret)
2112a24f2ceSDong Aisheng 		return ret;
2122a24f2ceSDong Aisheng 
2132a24f2ceSDong Aisheng 	/* enable MCLK output */
2142a24f2ceSDong Aisheng 	__raw_writel(BM_SAIF_CTRL_RUN,
2152a24f2ceSDong Aisheng 		saif->base + SAIF_CTRL + MXS_SET_ADDR);
2162a24f2ceSDong Aisheng 
2172a24f2ceSDong Aisheng 	return 0;
2182a24f2ceSDong Aisheng }
2192a24f2ceSDong Aisheng 
2202a24f2ceSDong Aisheng /*
2212a24f2ceSDong Aisheng  * SAIF DAI format configuration.
2222a24f2ceSDong Aisheng  * Should only be called when port is inactive.
2232a24f2ceSDong Aisheng  */
2242a24f2ceSDong Aisheng static int mxs_saif_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt)
2252a24f2ceSDong Aisheng {
2262a24f2ceSDong Aisheng 	u32 scr, stat;
2272a24f2ceSDong Aisheng 	u32 scr0;
2282a24f2ceSDong Aisheng 	struct mxs_saif *saif = snd_soc_dai_get_drvdata(cpu_dai);
2292a24f2ceSDong Aisheng 
2302a24f2ceSDong Aisheng 	stat = __raw_readl(saif->base + SAIF_STAT);
2312a24f2ceSDong Aisheng 	if (stat & BM_SAIF_STAT_BUSY) {
2322a24f2ceSDong Aisheng 		dev_err(cpu_dai->dev, "error: busy\n");
2332a24f2ceSDong Aisheng 		return -EBUSY;
2342a24f2ceSDong Aisheng 	}
2352a24f2ceSDong Aisheng 
2362a24f2ceSDong Aisheng 	scr0 = __raw_readl(saif->base + SAIF_CTRL);
2372a24f2ceSDong Aisheng 	scr0 = scr0 & ~BM_SAIF_CTRL_BITCLK_EDGE & ~BM_SAIF_CTRL_LRCLK_POLARITY \
2382a24f2ceSDong Aisheng 		& ~BM_SAIF_CTRL_JUSTIFY & ~BM_SAIF_CTRL_DELAY;
2392a24f2ceSDong Aisheng 	scr = 0;
2402a24f2ceSDong Aisheng 
2412a24f2ceSDong Aisheng 	/* DAI mode */
2422a24f2ceSDong Aisheng 	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
2432a24f2ceSDong Aisheng 	case SND_SOC_DAIFMT_I2S:
2442a24f2ceSDong Aisheng 		/* data frame low 1clk before data */
2452a24f2ceSDong Aisheng 		scr |= BM_SAIF_CTRL_DELAY;
2462a24f2ceSDong Aisheng 		scr &= ~BM_SAIF_CTRL_LRCLK_POLARITY;
2472a24f2ceSDong Aisheng 		break;
2482a24f2ceSDong Aisheng 	case SND_SOC_DAIFMT_LEFT_J:
2492a24f2ceSDong Aisheng 		/* data frame high with data */
2502a24f2ceSDong Aisheng 		scr &= ~BM_SAIF_CTRL_DELAY;
2512a24f2ceSDong Aisheng 		scr &= ~BM_SAIF_CTRL_LRCLK_POLARITY;
2522a24f2ceSDong Aisheng 		scr &= ~BM_SAIF_CTRL_JUSTIFY;
2532a24f2ceSDong Aisheng 		break;
2542a24f2ceSDong Aisheng 	default:
2552a24f2ceSDong Aisheng 		return -EINVAL;
2562a24f2ceSDong Aisheng 	}
2572a24f2ceSDong Aisheng 
2582a24f2ceSDong Aisheng 	/* DAI clock inversion */
2592a24f2ceSDong Aisheng 	switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
2602a24f2ceSDong Aisheng 	case SND_SOC_DAIFMT_IB_IF:
2612a24f2ceSDong Aisheng 		scr |= BM_SAIF_CTRL_BITCLK_EDGE;
2622a24f2ceSDong Aisheng 		scr |= BM_SAIF_CTRL_LRCLK_POLARITY;
2632a24f2ceSDong Aisheng 		break;
2642a24f2ceSDong Aisheng 	case SND_SOC_DAIFMT_IB_NF:
2652a24f2ceSDong Aisheng 		scr |= BM_SAIF_CTRL_BITCLK_EDGE;
2662a24f2ceSDong Aisheng 		scr &= ~BM_SAIF_CTRL_LRCLK_POLARITY;
2672a24f2ceSDong Aisheng 		break;
2682a24f2ceSDong Aisheng 	case SND_SOC_DAIFMT_NB_IF:
2692a24f2ceSDong Aisheng 		scr &= ~BM_SAIF_CTRL_BITCLK_EDGE;
2702a24f2ceSDong Aisheng 		scr |= BM_SAIF_CTRL_LRCLK_POLARITY;
2712a24f2ceSDong Aisheng 		break;
2722a24f2ceSDong Aisheng 	case SND_SOC_DAIFMT_NB_NF:
2732a24f2ceSDong Aisheng 		scr &= ~BM_SAIF_CTRL_BITCLK_EDGE;
2742a24f2ceSDong Aisheng 		scr &= ~BM_SAIF_CTRL_LRCLK_POLARITY;
2752a24f2ceSDong Aisheng 		break;
2762a24f2ceSDong Aisheng 	}
2772a24f2ceSDong Aisheng 
2782a24f2ceSDong Aisheng 	/*
2792a24f2ceSDong Aisheng 	 * Note: We simply just support master mode since SAIF TX can only
2802a24f2ceSDong Aisheng 	 * work as master.
2812a24f2ceSDong Aisheng 	 */
2822a24f2ceSDong Aisheng 	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
2832a24f2ceSDong Aisheng 	case SND_SOC_DAIFMT_CBS_CFS:
2842a24f2ceSDong Aisheng 		scr &= ~BM_SAIF_CTRL_SLAVE_MODE;
2852a24f2ceSDong Aisheng 		__raw_writel(scr | scr0, saif->base + SAIF_CTRL);
2862a24f2ceSDong Aisheng 		break;
2872a24f2ceSDong Aisheng 	default:
2882a24f2ceSDong Aisheng 		return -EINVAL;
2892a24f2ceSDong Aisheng 	}
2902a24f2ceSDong Aisheng 
2912a24f2ceSDong Aisheng 	return 0;
2922a24f2ceSDong Aisheng }
2932a24f2ceSDong Aisheng 
2942a24f2ceSDong Aisheng static int mxs_saif_startup(struct snd_pcm_substream *substream,
2952a24f2ceSDong Aisheng 			   struct snd_soc_dai *cpu_dai)
2962a24f2ceSDong Aisheng {
2972a24f2ceSDong Aisheng 	struct mxs_saif *saif = snd_soc_dai_get_drvdata(cpu_dai);
2982a24f2ceSDong Aisheng 	snd_soc_dai_set_dma_data(cpu_dai, substream, &saif->dma_param);
2992a24f2ceSDong Aisheng 
3002a24f2ceSDong Aisheng 	/* clear error status to 0 for each re-open */
3012a24f2ceSDong Aisheng 	saif->fifo_underrun = 0;
3022a24f2ceSDong Aisheng 	saif->fifo_overrun = 0;
3032a24f2ceSDong Aisheng 
3042a24f2ceSDong Aisheng 	/* Clear Reset for normal operations */
3052a24f2ceSDong Aisheng 	__raw_writel(BM_SAIF_CTRL_SFTRST,
3062a24f2ceSDong Aisheng 		saif->base + SAIF_CTRL + MXS_CLR_ADDR);
3072a24f2ceSDong Aisheng 
308bbe8ff5eSDong Aisheng 	/* clear clock gate */
309bbe8ff5eSDong Aisheng 	__raw_writel(BM_SAIF_CTRL_CLKGATE,
310bbe8ff5eSDong Aisheng 		saif->base + SAIF_CTRL + MXS_CLR_ADDR);
311bbe8ff5eSDong Aisheng 
3122a24f2ceSDong Aisheng 	return 0;
3132a24f2ceSDong Aisheng }
3142a24f2ceSDong Aisheng 
3152a24f2ceSDong Aisheng /*
3162a24f2ceSDong Aisheng  * Should only be called when port is inactive.
3172a24f2ceSDong Aisheng  * although can be called multiple times by upper layers.
3182a24f2ceSDong Aisheng  */
3192a24f2ceSDong Aisheng static int mxs_saif_hw_params(struct snd_pcm_substream *substream,
3202a24f2ceSDong Aisheng 			     struct snd_pcm_hw_params *params,
3212a24f2ceSDong Aisheng 			     struct snd_soc_dai *cpu_dai)
3222a24f2ceSDong Aisheng {
3232a24f2ceSDong Aisheng 	struct mxs_saif *saif = snd_soc_dai_get_drvdata(cpu_dai);
3242a24f2ceSDong Aisheng 	u32 scr, stat;
3252a24f2ceSDong Aisheng 	int ret;
3262a24f2ceSDong Aisheng 
3272a24f2ceSDong Aisheng 	/* mclk should already be set */
3282a24f2ceSDong Aisheng 	if (!saif->mclk && saif->mclk_in_use) {
3292a24f2ceSDong Aisheng 		dev_err(cpu_dai->dev, "set mclk first\n");
3302a24f2ceSDong Aisheng 		return -EINVAL;
3312a24f2ceSDong Aisheng 	}
3322a24f2ceSDong Aisheng 
3332a24f2ceSDong Aisheng 	stat = __raw_readl(saif->base + SAIF_STAT);
3342a24f2ceSDong Aisheng 	if (stat & BM_SAIF_STAT_BUSY) {
3352a24f2ceSDong Aisheng 		dev_err(cpu_dai->dev, "error: busy\n");
3362a24f2ceSDong Aisheng 		return -EBUSY;
3372a24f2ceSDong Aisheng 	}
3382a24f2ceSDong Aisheng 
3392a24f2ceSDong Aisheng 	/*
3402a24f2ceSDong Aisheng 	 * Set saif clk based on sample rate.
3412a24f2ceSDong Aisheng 	 * If mclk is used, we also set mclk, if not, saif->mclk is
3422a24f2ceSDong Aisheng 	 * default 0, means not used.
3432a24f2ceSDong Aisheng 	 */
3442a24f2ceSDong Aisheng 	ret = mxs_saif_set_clk(saif, saif->mclk, params_rate(params));
3452a24f2ceSDong Aisheng 	if (ret) {
3462a24f2ceSDong Aisheng 		dev_err(cpu_dai->dev, "unable to get proper clk\n");
3472a24f2ceSDong Aisheng 		return ret;
3482a24f2ceSDong Aisheng 	}
3492a24f2ceSDong Aisheng 
3502a24f2ceSDong Aisheng 	scr = __raw_readl(saif->base + SAIF_CTRL);
3512a24f2ceSDong Aisheng 
3522a24f2ceSDong Aisheng 	scr &= ~BM_SAIF_CTRL_WORD_LENGTH;
3532a24f2ceSDong Aisheng 	scr &= ~BM_SAIF_CTRL_BITCLK_48XFS_ENABLE;
3542a24f2ceSDong Aisheng 	switch (params_format(params)) {
3552a24f2ceSDong Aisheng 	case SNDRV_PCM_FORMAT_S16_LE:
3562a24f2ceSDong Aisheng 		scr |= BF_SAIF_CTRL_WORD_LENGTH(0);
3572a24f2ceSDong Aisheng 		break;
3582a24f2ceSDong Aisheng 	case SNDRV_PCM_FORMAT_S20_3LE:
3592a24f2ceSDong Aisheng 		scr |= BF_SAIF_CTRL_WORD_LENGTH(4);
3602a24f2ceSDong Aisheng 		scr |= BM_SAIF_CTRL_BITCLK_48XFS_ENABLE;
3612a24f2ceSDong Aisheng 		break;
3622a24f2ceSDong Aisheng 	case SNDRV_PCM_FORMAT_S24_LE:
3632a24f2ceSDong Aisheng 		scr |= BF_SAIF_CTRL_WORD_LENGTH(8);
3642a24f2ceSDong Aisheng 		scr |= BM_SAIF_CTRL_BITCLK_48XFS_ENABLE;
3652a24f2ceSDong Aisheng 		break;
3662a24f2ceSDong Aisheng 	default:
3672a24f2ceSDong Aisheng 		return -EINVAL;
3682a24f2ceSDong Aisheng 	}
3692a24f2ceSDong Aisheng 
3702a24f2ceSDong Aisheng 	/* Tx/Rx config */
3712a24f2ceSDong Aisheng 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
3722a24f2ceSDong Aisheng 		/* enable TX mode */
3732a24f2ceSDong Aisheng 		scr &= ~BM_SAIF_CTRL_READ_MODE;
3742a24f2ceSDong Aisheng 	} else {
3752a24f2ceSDong Aisheng 		/* enable RX mode */
3762a24f2ceSDong Aisheng 		scr |= BM_SAIF_CTRL_READ_MODE;
3772a24f2ceSDong Aisheng 	}
3782a24f2ceSDong Aisheng 
3792a24f2ceSDong Aisheng 	__raw_writel(scr, saif->base + SAIF_CTRL);
3802a24f2ceSDong Aisheng 	return 0;
3812a24f2ceSDong Aisheng }
3822a24f2ceSDong Aisheng 
3832a24f2ceSDong Aisheng static int mxs_saif_prepare(struct snd_pcm_substream *substream,
3842a24f2ceSDong Aisheng 			   struct snd_soc_dai *cpu_dai)
3852a24f2ceSDong Aisheng {
3862a24f2ceSDong Aisheng 	struct mxs_saif *saif = snd_soc_dai_get_drvdata(cpu_dai);
3872a24f2ceSDong Aisheng 
3882a24f2ceSDong Aisheng 	/* enable FIFO error irqs */
3892a24f2ceSDong Aisheng 	__raw_writel(BM_SAIF_CTRL_FIFO_ERROR_IRQ_EN,
3902a24f2ceSDong Aisheng 		saif->base + SAIF_CTRL + MXS_SET_ADDR);
3912a24f2ceSDong Aisheng 
3922a24f2ceSDong Aisheng 	return 0;
3932a24f2ceSDong Aisheng }
3942a24f2ceSDong Aisheng 
3952a24f2ceSDong Aisheng static int mxs_saif_trigger(struct snd_pcm_substream *substream, int cmd,
3962a24f2ceSDong Aisheng 				struct snd_soc_dai *cpu_dai)
3972a24f2ceSDong Aisheng {
3982a24f2ceSDong Aisheng 	struct mxs_saif *saif = snd_soc_dai_get_drvdata(cpu_dai);
3992a24f2ceSDong Aisheng 
4002a24f2ceSDong Aisheng 	switch (cmd) {
4012a24f2ceSDong Aisheng 	case SNDRV_PCM_TRIGGER_START:
4022a24f2ceSDong Aisheng 	case SNDRV_PCM_TRIGGER_RESUME:
4032a24f2ceSDong Aisheng 	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
4042a24f2ceSDong Aisheng 		dev_dbg(cpu_dai->dev, "start\n");
4052a24f2ceSDong Aisheng 
4062a24f2ceSDong Aisheng 		clk_enable(saif->clk);
4072a24f2ceSDong Aisheng 		if (!saif->mclk_in_use)
4082a24f2ceSDong Aisheng 			__raw_writel(BM_SAIF_CTRL_RUN,
4092a24f2ceSDong Aisheng 				saif->base + SAIF_CTRL + MXS_SET_ADDR);
4102a24f2ceSDong Aisheng 
4112a24f2ceSDong Aisheng 		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
4122a24f2ceSDong Aisheng 			/*
4132a24f2ceSDong Aisheng 			 * write a data to saif data register to trigger
4142a24f2ceSDong Aisheng 			 * the transfer
4152a24f2ceSDong Aisheng 			 */
4162a24f2ceSDong Aisheng 			__raw_writel(0, saif->base + SAIF_DATA);
4172a24f2ceSDong Aisheng 		} else {
4182a24f2ceSDong Aisheng 			/*
4192a24f2ceSDong Aisheng 			 * read a data from saif data register to trigger
4202a24f2ceSDong Aisheng 			 * the receive
4212a24f2ceSDong Aisheng 			 */
4222a24f2ceSDong Aisheng 			__raw_readl(saif->base + SAIF_DATA);
4232a24f2ceSDong Aisheng 		}
4242a24f2ceSDong Aisheng 
4252a24f2ceSDong Aisheng 		dev_dbg(cpu_dai->dev, "CTRL 0x%x STAT 0x%x\n",
4262a24f2ceSDong Aisheng 			__raw_readl(saif->base + SAIF_CTRL),
4272a24f2ceSDong Aisheng 			__raw_readl(saif->base + SAIF_STAT));
4282a24f2ceSDong Aisheng 
4292a24f2ceSDong Aisheng 		break;
4302a24f2ceSDong Aisheng 	case SNDRV_PCM_TRIGGER_SUSPEND:
4312a24f2ceSDong Aisheng 	case SNDRV_PCM_TRIGGER_STOP:
4322a24f2ceSDong Aisheng 	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
4332a24f2ceSDong Aisheng 		dev_dbg(cpu_dai->dev, "stop\n");
4342a24f2ceSDong Aisheng 
4352a24f2ceSDong Aisheng 		clk_disable(saif->clk);
4362a24f2ceSDong Aisheng 		if (!saif->mclk_in_use)
4372a24f2ceSDong Aisheng 			__raw_writel(BM_SAIF_CTRL_RUN,
4382a24f2ceSDong Aisheng 				saif->base + SAIF_CTRL + MXS_CLR_ADDR);
4392a24f2ceSDong Aisheng 
4402a24f2ceSDong Aisheng 		break;
4412a24f2ceSDong Aisheng 	default:
4422a24f2ceSDong Aisheng 		return -EINVAL;
4432a24f2ceSDong Aisheng 	}
4442a24f2ceSDong Aisheng 
4452a24f2ceSDong Aisheng 	return 0;
4462a24f2ceSDong Aisheng }
4472a24f2ceSDong Aisheng 
4482a24f2ceSDong Aisheng #define MXS_SAIF_RATES		SNDRV_PCM_RATE_8000_192000
4492a24f2ceSDong Aisheng #define MXS_SAIF_FORMATS \
4502a24f2ceSDong Aisheng 	(SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
4512a24f2ceSDong Aisheng 	SNDRV_PCM_FMTBIT_S24_LE)
4522a24f2ceSDong Aisheng 
4532a24f2ceSDong Aisheng static struct snd_soc_dai_ops mxs_saif_dai_ops = {
4542a24f2ceSDong Aisheng 	.startup = mxs_saif_startup,
4552a24f2ceSDong Aisheng 	.trigger = mxs_saif_trigger,
4562a24f2ceSDong Aisheng 	.prepare = mxs_saif_prepare,
4572a24f2ceSDong Aisheng 	.hw_params = mxs_saif_hw_params,
4582a24f2ceSDong Aisheng 	.set_sysclk = mxs_saif_set_dai_sysclk,
4592a24f2ceSDong Aisheng 	.set_fmt = mxs_saif_set_dai_fmt,
4602a24f2ceSDong Aisheng };
4612a24f2ceSDong Aisheng 
4622a24f2ceSDong Aisheng static int mxs_saif_dai_probe(struct snd_soc_dai *dai)
4632a24f2ceSDong Aisheng {
4642a24f2ceSDong Aisheng 	struct mxs_saif *saif = dev_get_drvdata(dai->dev);
4652a24f2ceSDong Aisheng 
4662a24f2ceSDong Aisheng 	snd_soc_dai_set_drvdata(dai, saif);
4672a24f2ceSDong Aisheng 
4682a24f2ceSDong Aisheng 	return 0;
4692a24f2ceSDong Aisheng }
4702a24f2ceSDong Aisheng 
4712a24f2ceSDong Aisheng static struct snd_soc_dai_driver mxs_saif_dai = {
4722a24f2ceSDong Aisheng 	.name = "mxs-saif",
4732a24f2ceSDong Aisheng 	.probe = mxs_saif_dai_probe,
4742a24f2ceSDong Aisheng 	.playback = {
4752a24f2ceSDong Aisheng 		.channels_min = 2,
4762a24f2ceSDong Aisheng 		.channels_max = 2,
4772a24f2ceSDong Aisheng 		.rates = MXS_SAIF_RATES,
4782a24f2ceSDong Aisheng 		.formats = MXS_SAIF_FORMATS,
4792a24f2ceSDong Aisheng 	},
4802a24f2ceSDong Aisheng 	.capture = {
4812a24f2ceSDong Aisheng 		.channels_min = 2,
4822a24f2ceSDong Aisheng 		.channels_max = 2,
4832a24f2ceSDong Aisheng 		.rates = MXS_SAIF_RATES,
4842a24f2ceSDong Aisheng 		.formats = MXS_SAIF_FORMATS,
4852a24f2ceSDong Aisheng 	},
4862a24f2ceSDong Aisheng 	.ops = &mxs_saif_dai_ops,
4872a24f2ceSDong Aisheng };
4882a24f2ceSDong Aisheng 
4892a24f2ceSDong Aisheng static irqreturn_t mxs_saif_irq(int irq, void *dev_id)
4902a24f2ceSDong Aisheng {
4912a24f2ceSDong Aisheng 	struct mxs_saif *saif = dev_id;
4922a24f2ceSDong Aisheng 	unsigned int stat;
4932a24f2ceSDong Aisheng 
4942a24f2ceSDong Aisheng 	stat = __raw_readl(saif->base + SAIF_STAT);
4952a24f2ceSDong Aisheng 	if (!(stat & (BM_SAIF_STAT_FIFO_UNDERFLOW_IRQ |
4962a24f2ceSDong Aisheng 			BM_SAIF_STAT_FIFO_OVERFLOW_IRQ)))
4972a24f2ceSDong Aisheng 		return IRQ_NONE;
4982a24f2ceSDong Aisheng 
4992a24f2ceSDong Aisheng 	if (stat & BM_SAIF_STAT_FIFO_UNDERFLOW_IRQ) {
5002a24f2ceSDong Aisheng 		dev_dbg(saif->dev, "underrun!!! %d\n", ++saif->fifo_underrun);
5012a24f2ceSDong Aisheng 		__raw_writel(BM_SAIF_STAT_FIFO_UNDERFLOW_IRQ,
5022a24f2ceSDong Aisheng 				saif->base + SAIF_STAT + MXS_CLR_ADDR);
5032a24f2ceSDong Aisheng 	}
5042a24f2ceSDong Aisheng 
5052a24f2ceSDong Aisheng 	if (stat & BM_SAIF_STAT_FIFO_OVERFLOW_IRQ) {
5062a24f2ceSDong Aisheng 		dev_dbg(saif->dev, "overrun!!! %d\n", ++saif->fifo_overrun);
5072a24f2ceSDong Aisheng 		__raw_writel(BM_SAIF_STAT_FIFO_OVERFLOW_IRQ,
5082a24f2ceSDong Aisheng 				saif->base + SAIF_STAT + MXS_CLR_ADDR);
5092a24f2ceSDong Aisheng 	}
5102a24f2ceSDong Aisheng 
5112a24f2ceSDong Aisheng 	dev_dbg(saif->dev, "SAIF_CTRL %x SAIF_STAT %x\n",
5122a24f2ceSDong Aisheng 	       __raw_readl(saif->base + SAIF_CTRL),
5132a24f2ceSDong Aisheng 	       __raw_readl(saif->base + SAIF_STAT));
5142a24f2ceSDong Aisheng 
5152a24f2ceSDong Aisheng 	return IRQ_HANDLED;
5162a24f2ceSDong Aisheng }
5172a24f2ceSDong Aisheng 
5182a24f2ceSDong Aisheng static int mxs_saif_probe(struct platform_device *pdev)
5192a24f2ceSDong Aisheng {
5202a24f2ceSDong Aisheng 	struct resource *res;
5212a24f2ceSDong Aisheng 	struct mxs_saif *saif;
5222a24f2ceSDong Aisheng 	int ret = 0;
5232a24f2ceSDong Aisheng 
5240bb98ba2SJulia Lawall 	if (pdev->id >= ARRAY_SIZE(mxs_saif))
5250bb98ba2SJulia Lawall 		return -EINVAL;
5260bb98ba2SJulia Lawall 
5272a24f2ceSDong Aisheng 	saif = kzalloc(sizeof(*saif), GFP_KERNEL);
5282a24f2ceSDong Aisheng 	if (!saif)
5292a24f2ceSDong Aisheng 		return -ENOMEM;
5302a24f2ceSDong Aisheng 
5312a24f2ceSDong Aisheng 	mxs_saif[pdev->id] = saif;
5322a24f2ceSDong Aisheng 
5332a24f2ceSDong Aisheng 	saif->clk = clk_get(&pdev->dev, NULL);
5342a24f2ceSDong Aisheng 	if (IS_ERR(saif->clk)) {
5352a24f2ceSDong Aisheng 		ret = PTR_ERR(saif->clk);
5362a24f2ceSDong Aisheng 		dev_err(&pdev->dev, "Cannot get the clock: %d\n",
5372a24f2ceSDong Aisheng 			ret);
5382a24f2ceSDong Aisheng 		goto failed_clk;
5392a24f2ceSDong Aisheng 	}
5402a24f2ceSDong Aisheng 
5412a24f2ceSDong Aisheng 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
5422a24f2ceSDong Aisheng 	if (!res) {
5432a24f2ceSDong Aisheng 		ret = -ENODEV;
5442a24f2ceSDong Aisheng 		dev_err(&pdev->dev, "failed to get io resource: %d\n",
5452a24f2ceSDong Aisheng 			ret);
5462a24f2ceSDong Aisheng 		goto failed_get_resource;
5472a24f2ceSDong Aisheng 	}
5482a24f2ceSDong Aisheng 
5492a24f2ceSDong Aisheng 	if (!request_mem_region(res->start, resource_size(res), "mxs-saif")) {
5502a24f2ceSDong Aisheng 		dev_err(&pdev->dev, "request_mem_region failed\n");
5512a24f2ceSDong Aisheng 		ret = -EBUSY;
5522a24f2ceSDong Aisheng 		goto failed_get_resource;
5532a24f2ceSDong Aisheng 	}
5542a24f2ceSDong Aisheng 
5552a24f2ceSDong Aisheng 	saif->base = ioremap(res->start, resource_size(res));
5562a24f2ceSDong Aisheng 	if (!saif->base) {
5572a24f2ceSDong Aisheng 		dev_err(&pdev->dev, "ioremap failed\n");
5582a24f2ceSDong Aisheng 		ret = -ENODEV;
5592a24f2ceSDong Aisheng 		goto failed_ioremap;
5602a24f2ceSDong Aisheng 	}
5612a24f2ceSDong Aisheng 
5622a24f2ceSDong Aisheng 	res = platform_get_resource(pdev, IORESOURCE_DMA, 0);
5632a24f2ceSDong Aisheng 	if (!res) {
5642a24f2ceSDong Aisheng 		ret = -ENODEV;
5652a24f2ceSDong Aisheng 		dev_err(&pdev->dev, "failed to get dma resource: %d\n",
5662a24f2ceSDong Aisheng 			ret);
5672a24f2ceSDong Aisheng 		goto failed_ioremap;
5682a24f2ceSDong Aisheng 	}
5692a24f2ceSDong Aisheng 	saif->dma_param.chan_num = res->start;
5702a24f2ceSDong Aisheng 
5712a24f2ceSDong Aisheng 	saif->irq = platform_get_irq(pdev, 0);
5722a24f2ceSDong Aisheng 	if (saif->irq < 0) {
5732a24f2ceSDong Aisheng 		ret = saif->irq;
5742a24f2ceSDong Aisheng 		dev_err(&pdev->dev, "failed to get irq resource: %d\n",
5752a24f2ceSDong Aisheng 			ret);
5762a24f2ceSDong Aisheng 		goto failed_get_irq1;
5772a24f2ceSDong Aisheng 	}
5782a24f2ceSDong Aisheng 
5792a24f2ceSDong Aisheng 	saif->dev = &pdev->dev;
5802a24f2ceSDong Aisheng 	ret = request_irq(saif->irq, mxs_saif_irq, 0, "mxs-saif", saif);
5812a24f2ceSDong Aisheng 	if (ret) {
5822a24f2ceSDong Aisheng 		dev_err(&pdev->dev, "failed to request irq\n");
5832a24f2ceSDong Aisheng 		goto failed_get_irq1;
5842a24f2ceSDong Aisheng 	}
5852a24f2ceSDong Aisheng 
5862a24f2ceSDong Aisheng 	saif->dma_param.chan_irq = platform_get_irq(pdev, 1);
5872a24f2ceSDong Aisheng 	if (saif->dma_param.chan_irq < 0) {
5882a24f2ceSDong Aisheng 		ret = saif->dma_param.chan_irq;
5892a24f2ceSDong Aisheng 		dev_err(&pdev->dev, "failed to get dma irq resource: %d\n",
5902a24f2ceSDong Aisheng 			ret);
5912a24f2ceSDong Aisheng 		goto failed_get_irq2;
5922a24f2ceSDong Aisheng 	}
5932a24f2ceSDong Aisheng 
5942a24f2ceSDong Aisheng 	platform_set_drvdata(pdev, saif);
5952a24f2ceSDong Aisheng 
5962a24f2ceSDong Aisheng 	ret = snd_soc_register_dai(&pdev->dev, &mxs_saif_dai);
5972a24f2ceSDong Aisheng 	if (ret) {
5982a24f2ceSDong Aisheng 		dev_err(&pdev->dev, "register DAI failed\n");
5992a24f2ceSDong Aisheng 		goto failed_register;
6002a24f2ceSDong Aisheng 	}
6012a24f2ceSDong Aisheng 
6022a24f2ceSDong Aisheng 	saif->soc_platform_pdev = platform_device_alloc(
6032a24f2ceSDong Aisheng 					"mxs-pcm-audio", pdev->id);
6042a24f2ceSDong Aisheng 	if (!saif->soc_platform_pdev) {
6052a24f2ceSDong Aisheng 		ret = -ENOMEM;
6062a24f2ceSDong Aisheng 		goto failed_pdev_alloc;
6072a24f2ceSDong Aisheng 	}
6082a24f2ceSDong Aisheng 
6092a24f2ceSDong Aisheng 	platform_set_drvdata(saif->soc_platform_pdev, saif);
6102a24f2ceSDong Aisheng 	ret = platform_device_add(saif->soc_platform_pdev);
6112a24f2ceSDong Aisheng 	if (ret) {
6122a24f2ceSDong Aisheng 		dev_err(&pdev->dev, "failed to add soc platform device\n");
6132a24f2ceSDong Aisheng 		goto failed_pdev_add;
6142a24f2ceSDong Aisheng 	}
6152a24f2ceSDong Aisheng 
6162a24f2ceSDong Aisheng 	return 0;
6172a24f2ceSDong Aisheng 
6182a24f2ceSDong Aisheng failed_pdev_add:
6192a24f2ceSDong Aisheng 	platform_device_put(saif->soc_platform_pdev);
6202a24f2ceSDong Aisheng failed_pdev_alloc:
6212a24f2ceSDong Aisheng 	snd_soc_unregister_dai(&pdev->dev);
6222a24f2ceSDong Aisheng failed_register:
6232a24f2ceSDong Aisheng failed_get_irq2:
6242a24f2ceSDong Aisheng 	free_irq(saif->irq, saif);
6252a24f2ceSDong Aisheng failed_get_irq1:
6262a24f2ceSDong Aisheng 	iounmap(saif->base);
6272a24f2ceSDong Aisheng failed_ioremap:
6282a24f2ceSDong Aisheng 	release_mem_region(res->start, resource_size(res));
6292a24f2ceSDong Aisheng failed_get_resource:
6302a24f2ceSDong Aisheng 	clk_put(saif->clk);
6312a24f2ceSDong Aisheng failed_clk:
6322a24f2ceSDong Aisheng 	kfree(saif);
6332a24f2ceSDong Aisheng 
6342a24f2ceSDong Aisheng 	return ret;
6352a24f2ceSDong Aisheng }
6362a24f2ceSDong Aisheng 
6372a24f2ceSDong Aisheng static int __devexit mxs_saif_remove(struct platform_device *pdev)
6382a24f2ceSDong Aisheng {
6392a24f2ceSDong Aisheng 	struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
6402a24f2ceSDong Aisheng 	struct mxs_saif *saif = platform_get_drvdata(pdev);
6412a24f2ceSDong Aisheng 
6422a24f2ceSDong Aisheng 	platform_device_unregister(saif->soc_platform_pdev);
6432a24f2ceSDong Aisheng 
6442a24f2ceSDong Aisheng 	snd_soc_unregister_dai(&pdev->dev);
6452a24f2ceSDong Aisheng 
6462a24f2ceSDong Aisheng 	iounmap(saif->base);
6472a24f2ceSDong Aisheng 	release_mem_region(res->start, resource_size(res));
6482a24f2ceSDong Aisheng 	free_irq(saif->irq, saif);
6492a24f2ceSDong Aisheng 
6502a24f2ceSDong Aisheng 	clk_put(saif->clk);
6512a24f2ceSDong Aisheng 	kfree(saif);
6522a24f2ceSDong Aisheng 
6532a24f2ceSDong Aisheng 	return 0;
6542a24f2ceSDong Aisheng }
6552a24f2ceSDong Aisheng 
6562a24f2ceSDong Aisheng static struct platform_driver mxs_saif_driver = {
6572a24f2ceSDong Aisheng 	.probe = mxs_saif_probe,
6582a24f2ceSDong Aisheng 	.remove = __devexit_p(mxs_saif_remove),
6592a24f2ceSDong Aisheng 
6602a24f2ceSDong Aisheng 	.driver = {
6612a24f2ceSDong Aisheng 		.name = "mxs-saif",
6622a24f2ceSDong Aisheng 		.owner = THIS_MODULE,
6632a24f2ceSDong Aisheng 	},
6642a24f2ceSDong Aisheng };
6652a24f2ceSDong Aisheng 
6662a24f2ceSDong Aisheng static int __init mxs_saif_init(void)
6672a24f2ceSDong Aisheng {
6682a24f2ceSDong Aisheng 	return platform_driver_register(&mxs_saif_driver);
6692a24f2ceSDong Aisheng }
6702a24f2ceSDong Aisheng 
6712a24f2ceSDong Aisheng static void __exit mxs_saif_exit(void)
6722a24f2ceSDong Aisheng {
6732a24f2ceSDong Aisheng 	platform_driver_unregister(&mxs_saif_driver);
6742a24f2ceSDong Aisheng }
6752a24f2ceSDong Aisheng 
6762a24f2ceSDong Aisheng module_init(mxs_saif_init);
6772a24f2ceSDong Aisheng module_exit(mxs_saif_exit);
6782a24f2ceSDong Aisheng MODULE_AUTHOR("Freescale Semiconductor, Inc.");
6792a24f2ceSDong Aisheng MODULE_DESCRIPTION("MXS ASoC SAIF driver");
6802a24f2ceSDong Aisheng MODULE_LICENSE("GPL");
681