xref: /OK3568_Linux_fs/kernel/sound/soc/kirkwood/kirkwood-i2s.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-or-later
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun  * kirkwood-i2s.c
4*4882a593Smuzhiyun  *
5*4882a593Smuzhiyun  * (c) 2010 Arnaud Patard <apatard@mandriva.com>
6*4882a593Smuzhiyun  * (c) 2010 Arnaud Patard <arnaud.patard@rtp-net.org>
7*4882a593Smuzhiyun  */
8*4882a593Smuzhiyun 
9*4882a593Smuzhiyun #include <linux/init.h>
10*4882a593Smuzhiyun #include <linux/module.h>
11*4882a593Smuzhiyun #include <linux/platform_device.h>
12*4882a593Smuzhiyun #include <linux/io.h>
13*4882a593Smuzhiyun #include <linux/slab.h>
14*4882a593Smuzhiyun #include <linux/mbus.h>
15*4882a593Smuzhiyun #include <linux/delay.h>
16*4882a593Smuzhiyun #include <linux/clk.h>
17*4882a593Smuzhiyun #include <sound/pcm.h>
18*4882a593Smuzhiyun #include <sound/pcm_params.h>
19*4882a593Smuzhiyun #include <sound/soc.h>
20*4882a593Smuzhiyun #include <linux/platform_data/asoc-kirkwood.h>
21*4882a593Smuzhiyun #include <linux/of.h>
22*4882a593Smuzhiyun 
23*4882a593Smuzhiyun #include "kirkwood.h"
24*4882a593Smuzhiyun 
25*4882a593Smuzhiyun #define KIRKWOOD_I2S_FORMATS \
26*4882a593Smuzhiyun 	(SNDRV_PCM_FMTBIT_S16_LE | \
27*4882a593Smuzhiyun 	 SNDRV_PCM_FMTBIT_S24_LE | \
28*4882a593Smuzhiyun 	 SNDRV_PCM_FMTBIT_S32_LE)
29*4882a593Smuzhiyun 
30*4882a593Smuzhiyun #define KIRKWOOD_SPDIF_FORMATS \
31*4882a593Smuzhiyun 	(SNDRV_PCM_FMTBIT_S16_LE | \
32*4882a593Smuzhiyun 	 SNDRV_PCM_FMTBIT_S24_LE)
33*4882a593Smuzhiyun 
kirkwood_i2s_set_fmt(struct snd_soc_dai * cpu_dai,unsigned int fmt)34*4882a593Smuzhiyun static int kirkwood_i2s_set_fmt(struct snd_soc_dai *cpu_dai,
35*4882a593Smuzhiyun 		unsigned int fmt)
36*4882a593Smuzhiyun {
37*4882a593Smuzhiyun 	struct kirkwood_dma_data *priv = snd_soc_dai_get_drvdata(cpu_dai);
38*4882a593Smuzhiyun 	unsigned long mask;
39*4882a593Smuzhiyun 	unsigned long value;
40*4882a593Smuzhiyun 
41*4882a593Smuzhiyun 	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
42*4882a593Smuzhiyun 	case SND_SOC_DAIFMT_RIGHT_J:
43*4882a593Smuzhiyun 		mask = KIRKWOOD_I2S_CTL_RJ;
44*4882a593Smuzhiyun 		break;
45*4882a593Smuzhiyun 	case SND_SOC_DAIFMT_LEFT_J:
46*4882a593Smuzhiyun 		mask = KIRKWOOD_I2S_CTL_LJ;
47*4882a593Smuzhiyun 		break;
48*4882a593Smuzhiyun 	case SND_SOC_DAIFMT_I2S:
49*4882a593Smuzhiyun 		mask = KIRKWOOD_I2S_CTL_I2S;
50*4882a593Smuzhiyun 		break;
51*4882a593Smuzhiyun 	default:
52*4882a593Smuzhiyun 		return -EINVAL;
53*4882a593Smuzhiyun 	}
54*4882a593Smuzhiyun 
55*4882a593Smuzhiyun 	/*
56*4882a593Smuzhiyun 	 * Set same format for playback and record
57*4882a593Smuzhiyun 	 * This avoids some troubles.
58*4882a593Smuzhiyun 	 */
59*4882a593Smuzhiyun 	value = readl(priv->io+KIRKWOOD_I2S_PLAYCTL);
60*4882a593Smuzhiyun 	value &= ~KIRKWOOD_I2S_CTL_JUST_MASK;
61*4882a593Smuzhiyun 	value |= mask;
62*4882a593Smuzhiyun 	writel(value, priv->io+KIRKWOOD_I2S_PLAYCTL);
63*4882a593Smuzhiyun 
64*4882a593Smuzhiyun 	value = readl(priv->io+KIRKWOOD_I2S_RECCTL);
65*4882a593Smuzhiyun 	value &= ~KIRKWOOD_I2S_CTL_JUST_MASK;
66*4882a593Smuzhiyun 	value |= mask;
67*4882a593Smuzhiyun 	writel(value, priv->io+KIRKWOOD_I2S_RECCTL);
68*4882a593Smuzhiyun 
69*4882a593Smuzhiyun 	return 0;
70*4882a593Smuzhiyun }
71*4882a593Smuzhiyun 
kirkwood_set_dco(void __iomem * io,unsigned long rate)72*4882a593Smuzhiyun static inline void kirkwood_set_dco(void __iomem *io, unsigned long rate)
73*4882a593Smuzhiyun {
74*4882a593Smuzhiyun 	unsigned long value;
75*4882a593Smuzhiyun 
76*4882a593Smuzhiyun 	value = KIRKWOOD_DCO_CTL_OFFSET_0;
77*4882a593Smuzhiyun 	switch (rate) {
78*4882a593Smuzhiyun 	default:
79*4882a593Smuzhiyun 	case 44100:
80*4882a593Smuzhiyun 		value |= KIRKWOOD_DCO_CTL_FREQ_11;
81*4882a593Smuzhiyun 		break;
82*4882a593Smuzhiyun 	case 48000:
83*4882a593Smuzhiyun 		value |= KIRKWOOD_DCO_CTL_FREQ_12;
84*4882a593Smuzhiyun 		break;
85*4882a593Smuzhiyun 	case 96000:
86*4882a593Smuzhiyun 		value |= KIRKWOOD_DCO_CTL_FREQ_24;
87*4882a593Smuzhiyun 		break;
88*4882a593Smuzhiyun 	}
89*4882a593Smuzhiyun 	writel(value, io + KIRKWOOD_DCO_CTL);
90*4882a593Smuzhiyun 
91*4882a593Smuzhiyun 	/* wait for dco locked */
92*4882a593Smuzhiyun 	do {
93*4882a593Smuzhiyun 		cpu_relax();
94*4882a593Smuzhiyun 		value = readl(io + KIRKWOOD_DCO_SPCR_STATUS);
95*4882a593Smuzhiyun 		value &= KIRKWOOD_DCO_SPCR_STATUS_DCO_LOCK;
96*4882a593Smuzhiyun 	} while (value == 0);
97*4882a593Smuzhiyun }
98*4882a593Smuzhiyun 
kirkwood_set_rate(struct snd_soc_dai * dai,struct kirkwood_dma_data * priv,unsigned long rate)99*4882a593Smuzhiyun static void kirkwood_set_rate(struct snd_soc_dai *dai,
100*4882a593Smuzhiyun 	struct kirkwood_dma_data *priv, unsigned long rate)
101*4882a593Smuzhiyun {
102*4882a593Smuzhiyun 	uint32_t clks_ctrl;
103*4882a593Smuzhiyun 
104*4882a593Smuzhiyun 	if (IS_ERR(priv->extclk)) {
105*4882a593Smuzhiyun 		/* use internal dco for the supported rates
106*4882a593Smuzhiyun 		 * defined in kirkwood_i2s_dai */
107*4882a593Smuzhiyun 		dev_dbg(dai->dev, "%s: dco set rate = %lu\n",
108*4882a593Smuzhiyun 			__func__, rate);
109*4882a593Smuzhiyun 		kirkwood_set_dco(priv->io, rate);
110*4882a593Smuzhiyun 
111*4882a593Smuzhiyun 		clks_ctrl = KIRKWOOD_MCLK_SOURCE_DCO;
112*4882a593Smuzhiyun 	} else {
113*4882a593Smuzhiyun 		/* use the external clock for the other rates
114*4882a593Smuzhiyun 		 * defined in kirkwood_i2s_dai_extclk */
115*4882a593Smuzhiyun 		dev_dbg(dai->dev, "%s: extclk set rate = %lu -> %lu\n",
116*4882a593Smuzhiyun 			__func__, rate, 256 * rate);
117*4882a593Smuzhiyun 		clk_set_rate(priv->extclk, 256 * rate);
118*4882a593Smuzhiyun 
119*4882a593Smuzhiyun 		clks_ctrl = KIRKWOOD_MCLK_SOURCE_EXTCLK;
120*4882a593Smuzhiyun 	}
121*4882a593Smuzhiyun 	writel(clks_ctrl, priv->io + KIRKWOOD_CLOCKS_CTRL);
122*4882a593Smuzhiyun }
123*4882a593Smuzhiyun 
kirkwood_i2s_startup(struct snd_pcm_substream * substream,struct snd_soc_dai * dai)124*4882a593Smuzhiyun static int kirkwood_i2s_startup(struct snd_pcm_substream *substream,
125*4882a593Smuzhiyun 		struct snd_soc_dai *dai)
126*4882a593Smuzhiyun {
127*4882a593Smuzhiyun 	struct kirkwood_dma_data *priv = snd_soc_dai_get_drvdata(dai);
128*4882a593Smuzhiyun 
129*4882a593Smuzhiyun 	snd_soc_dai_set_dma_data(dai, substream, priv);
130*4882a593Smuzhiyun 	return 0;
131*4882a593Smuzhiyun }
132*4882a593Smuzhiyun 
kirkwood_i2s_hw_params(struct snd_pcm_substream * substream,struct snd_pcm_hw_params * params,struct snd_soc_dai * dai)133*4882a593Smuzhiyun static int kirkwood_i2s_hw_params(struct snd_pcm_substream *substream,
134*4882a593Smuzhiyun 				 struct snd_pcm_hw_params *params,
135*4882a593Smuzhiyun 				 struct snd_soc_dai *dai)
136*4882a593Smuzhiyun {
137*4882a593Smuzhiyun 	struct kirkwood_dma_data *priv = snd_soc_dai_get_drvdata(dai);
138*4882a593Smuzhiyun 	uint32_t ctl_play, ctl_rec;
139*4882a593Smuzhiyun 	unsigned int i2s_reg;
140*4882a593Smuzhiyun 	unsigned long i2s_value;
141*4882a593Smuzhiyun 
142*4882a593Smuzhiyun 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
143*4882a593Smuzhiyun 		i2s_reg = KIRKWOOD_I2S_PLAYCTL;
144*4882a593Smuzhiyun 	} else {
145*4882a593Smuzhiyun 		i2s_reg = KIRKWOOD_I2S_RECCTL;
146*4882a593Smuzhiyun 	}
147*4882a593Smuzhiyun 
148*4882a593Smuzhiyun 	kirkwood_set_rate(dai, priv, params_rate(params));
149*4882a593Smuzhiyun 
150*4882a593Smuzhiyun 	i2s_value = readl(priv->io+i2s_reg);
151*4882a593Smuzhiyun 	i2s_value &= ~KIRKWOOD_I2S_CTL_SIZE_MASK;
152*4882a593Smuzhiyun 
153*4882a593Smuzhiyun 	/*
154*4882a593Smuzhiyun 	 * Size settings in play/rec i2s control regs and play/rec control
155*4882a593Smuzhiyun 	 * regs must be the same.
156*4882a593Smuzhiyun 	 */
157*4882a593Smuzhiyun 	switch (params_format(params)) {
158*4882a593Smuzhiyun 	case SNDRV_PCM_FORMAT_S16_LE:
159*4882a593Smuzhiyun 		i2s_value |= KIRKWOOD_I2S_CTL_SIZE_16;
160*4882a593Smuzhiyun 		ctl_play = KIRKWOOD_PLAYCTL_SIZE_16_C |
161*4882a593Smuzhiyun 			   KIRKWOOD_PLAYCTL_I2S_EN |
162*4882a593Smuzhiyun 			   KIRKWOOD_PLAYCTL_SPDIF_EN;
163*4882a593Smuzhiyun 		ctl_rec = KIRKWOOD_RECCTL_SIZE_16_C |
164*4882a593Smuzhiyun 			  KIRKWOOD_RECCTL_I2S_EN |
165*4882a593Smuzhiyun 			  KIRKWOOD_RECCTL_SPDIF_EN;
166*4882a593Smuzhiyun 		break;
167*4882a593Smuzhiyun 	/*
168*4882a593Smuzhiyun 	 * doesn't work... S20_3LE != kirkwood 20bit format ?
169*4882a593Smuzhiyun 	 *
170*4882a593Smuzhiyun 	case SNDRV_PCM_FORMAT_S20_3LE:
171*4882a593Smuzhiyun 		i2s_value |= KIRKWOOD_I2S_CTL_SIZE_20;
172*4882a593Smuzhiyun 		ctl_play = KIRKWOOD_PLAYCTL_SIZE_20 |
173*4882a593Smuzhiyun 			   KIRKWOOD_PLAYCTL_I2S_EN;
174*4882a593Smuzhiyun 		ctl_rec = KIRKWOOD_RECCTL_SIZE_20 |
175*4882a593Smuzhiyun 			  KIRKWOOD_RECCTL_I2S_EN;
176*4882a593Smuzhiyun 		break;
177*4882a593Smuzhiyun 	*/
178*4882a593Smuzhiyun 	case SNDRV_PCM_FORMAT_S24_LE:
179*4882a593Smuzhiyun 		i2s_value |= KIRKWOOD_I2S_CTL_SIZE_24;
180*4882a593Smuzhiyun 		ctl_play = KIRKWOOD_PLAYCTL_SIZE_24 |
181*4882a593Smuzhiyun 			   KIRKWOOD_PLAYCTL_I2S_EN |
182*4882a593Smuzhiyun 			   KIRKWOOD_PLAYCTL_SPDIF_EN;
183*4882a593Smuzhiyun 		ctl_rec = KIRKWOOD_RECCTL_SIZE_24 |
184*4882a593Smuzhiyun 			  KIRKWOOD_RECCTL_I2S_EN |
185*4882a593Smuzhiyun 			  KIRKWOOD_RECCTL_SPDIF_EN;
186*4882a593Smuzhiyun 		break;
187*4882a593Smuzhiyun 	case SNDRV_PCM_FORMAT_S32_LE:
188*4882a593Smuzhiyun 		i2s_value |= KIRKWOOD_I2S_CTL_SIZE_32;
189*4882a593Smuzhiyun 		ctl_play = KIRKWOOD_PLAYCTL_SIZE_32 |
190*4882a593Smuzhiyun 			   KIRKWOOD_PLAYCTL_I2S_EN;
191*4882a593Smuzhiyun 		ctl_rec = KIRKWOOD_RECCTL_SIZE_32 |
192*4882a593Smuzhiyun 			  KIRKWOOD_RECCTL_I2S_EN;
193*4882a593Smuzhiyun 		break;
194*4882a593Smuzhiyun 	default:
195*4882a593Smuzhiyun 		return -EINVAL;
196*4882a593Smuzhiyun 	}
197*4882a593Smuzhiyun 
198*4882a593Smuzhiyun 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
199*4882a593Smuzhiyun 		if (params_channels(params) == 1)
200*4882a593Smuzhiyun 			ctl_play |= KIRKWOOD_PLAYCTL_MONO_BOTH;
201*4882a593Smuzhiyun 		else
202*4882a593Smuzhiyun 			ctl_play |= KIRKWOOD_PLAYCTL_MONO_OFF;
203*4882a593Smuzhiyun 
204*4882a593Smuzhiyun 		priv->ctl_play &= ~(KIRKWOOD_PLAYCTL_MONO_MASK |
205*4882a593Smuzhiyun 				    KIRKWOOD_PLAYCTL_ENABLE_MASK |
206*4882a593Smuzhiyun 				    KIRKWOOD_PLAYCTL_SIZE_MASK);
207*4882a593Smuzhiyun 		priv->ctl_play |= ctl_play;
208*4882a593Smuzhiyun 	} else {
209*4882a593Smuzhiyun 		priv->ctl_rec &= ~(KIRKWOOD_RECCTL_ENABLE_MASK |
210*4882a593Smuzhiyun 				   KIRKWOOD_RECCTL_SIZE_MASK);
211*4882a593Smuzhiyun 		priv->ctl_rec |= ctl_rec;
212*4882a593Smuzhiyun 	}
213*4882a593Smuzhiyun 
214*4882a593Smuzhiyun 	writel(i2s_value, priv->io+i2s_reg);
215*4882a593Smuzhiyun 
216*4882a593Smuzhiyun 	return 0;
217*4882a593Smuzhiyun }
218*4882a593Smuzhiyun 
kirkwood_i2s_play_mute(unsigned ctl)219*4882a593Smuzhiyun static unsigned kirkwood_i2s_play_mute(unsigned ctl)
220*4882a593Smuzhiyun {
221*4882a593Smuzhiyun 	if (!(ctl & KIRKWOOD_PLAYCTL_I2S_EN))
222*4882a593Smuzhiyun 		ctl |= KIRKWOOD_PLAYCTL_I2S_MUTE;
223*4882a593Smuzhiyun 	if (!(ctl & KIRKWOOD_PLAYCTL_SPDIF_EN))
224*4882a593Smuzhiyun 		ctl |= KIRKWOOD_PLAYCTL_SPDIF_MUTE;
225*4882a593Smuzhiyun 	return ctl;
226*4882a593Smuzhiyun }
227*4882a593Smuzhiyun 
kirkwood_i2s_play_trigger(struct snd_pcm_substream * substream,int cmd,struct snd_soc_dai * dai)228*4882a593Smuzhiyun static int kirkwood_i2s_play_trigger(struct snd_pcm_substream *substream,
229*4882a593Smuzhiyun 				int cmd, struct snd_soc_dai *dai)
230*4882a593Smuzhiyun {
231*4882a593Smuzhiyun 	struct snd_pcm_runtime *runtime = substream->runtime;
232*4882a593Smuzhiyun 	struct kirkwood_dma_data *priv = snd_soc_dai_get_drvdata(dai);
233*4882a593Smuzhiyun 	uint32_t ctl, value;
234*4882a593Smuzhiyun 
235*4882a593Smuzhiyun 	ctl = readl(priv->io + KIRKWOOD_PLAYCTL);
236*4882a593Smuzhiyun 	if ((ctl & KIRKWOOD_PLAYCTL_ENABLE_MASK) == 0) {
237*4882a593Smuzhiyun 		unsigned timeout = 5000;
238*4882a593Smuzhiyun 		/*
239*4882a593Smuzhiyun 		 * The Armada510 spec says that if we enter pause mode, the
240*4882a593Smuzhiyun 		 * busy bit must be read back as clear _twice_.  Make sure
241*4882a593Smuzhiyun 		 * we respect that otherwise we get DMA underruns.
242*4882a593Smuzhiyun 		 */
243*4882a593Smuzhiyun 		do {
244*4882a593Smuzhiyun 			value = ctl;
245*4882a593Smuzhiyun 			ctl = readl(priv->io + KIRKWOOD_PLAYCTL);
246*4882a593Smuzhiyun 			if (!((ctl | value) & KIRKWOOD_PLAYCTL_PLAY_BUSY))
247*4882a593Smuzhiyun 				break;
248*4882a593Smuzhiyun 			udelay(1);
249*4882a593Smuzhiyun 		} while (timeout--);
250*4882a593Smuzhiyun 
251*4882a593Smuzhiyun 		if ((ctl | value) & KIRKWOOD_PLAYCTL_PLAY_BUSY)
252*4882a593Smuzhiyun 			dev_notice(dai->dev, "timed out waiting for busy to deassert: %08x\n",
253*4882a593Smuzhiyun 				   ctl);
254*4882a593Smuzhiyun 	}
255*4882a593Smuzhiyun 
256*4882a593Smuzhiyun 	switch (cmd) {
257*4882a593Smuzhiyun 	case SNDRV_PCM_TRIGGER_START:
258*4882a593Smuzhiyun 		/* configure */
259*4882a593Smuzhiyun 		ctl = priv->ctl_play;
260*4882a593Smuzhiyun 		if (dai->id == 0)
261*4882a593Smuzhiyun 			ctl &= ~KIRKWOOD_PLAYCTL_SPDIF_EN;	/* i2s */
262*4882a593Smuzhiyun 		else
263*4882a593Smuzhiyun 			ctl &= ~KIRKWOOD_PLAYCTL_I2S_EN;	/* spdif */
264*4882a593Smuzhiyun 		ctl = kirkwood_i2s_play_mute(ctl);
265*4882a593Smuzhiyun 		value = ctl & ~KIRKWOOD_PLAYCTL_ENABLE_MASK;
266*4882a593Smuzhiyun 		writel(value, priv->io + KIRKWOOD_PLAYCTL);
267*4882a593Smuzhiyun 
268*4882a593Smuzhiyun 		/* enable interrupts */
269*4882a593Smuzhiyun 		if (!runtime->no_period_wakeup) {
270*4882a593Smuzhiyun 			value = readl(priv->io + KIRKWOOD_INT_MASK);
271*4882a593Smuzhiyun 			value |= KIRKWOOD_INT_CAUSE_PLAY_BYTES;
272*4882a593Smuzhiyun 			writel(value, priv->io + KIRKWOOD_INT_MASK);
273*4882a593Smuzhiyun 		}
274*4882a593Smuzhiyun 
275*4882a593Smuzhiyun 		/* enable playback */
276*4882a593Smuzhiyun 		writel(ctl, priv->io + KIRKWOOD_PLAYCTL);
277*4882a593Smuzhiyun 		break;
278*4882a593Smuzhiyun 
279*4882a593Smuzhiyun 	case SNDRV_PCM_TRIGGER_STOP:
280*4882a593Smuzhiyun 		/* stop audio, disable interrupts */
281*4882a593Smuzhiyun 		ctl |= KIRKWOOD_PLAYCTL_PAUSE | KIRKWOOD_PLAYCTL_I2S_MUTE |
282*4882a593Smuzhiyun 				KIRKWOOD_PLAYCTL_SPDIF_MUTE;
283*4882a593Smuzhiyun 		writel(ctl, priv->io + KIRKWOOD_PLAYCTL);
284*4882a593Smuzhiyun 
285*4882a593Smuzhiyun 		value = readl(priv->io + KIRKWOOD_INT_MASK);
286*4882a593Smuzhiyun 		value &= ~KIRKWOOD_INT_CAUSE_PLAY_BYTES;
287*4882a593Smuzhiyun 		writel(value, priv->io + KIRKWOOD_INT_MASK);
288*4882a593Smuzhiyun 
289*4882a593Smuzhiyun 		/* disable all playbacks */
290*4882a593Smuzhiyun 		ctl &= ~KIRKWOOD_PLAYCTL_ENABLE_MASK;
291*4882a593Smuzhiyun 		writel(ctl, priv->io + KIRKWOOD_PLAYCTL);
292*4882a593Smuzhiyun 		break;
293*4882a593Smuzhiyun 
294*4882a593Smuzhiyun 	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
295*4882a593Smuzhiyun 	case SNDRV_PCM_TRIGGER_SUSPEND:
296*4882a593Smuzhiyun 		ctl |= KIRKWOOD_PLAYCTL_PAUSE | KIRKWOOD_PLAYCTL_I2S_MUTE |
297*4882a593Smuzhiyun 				KIRKWOOD_PLAYCTL_SPDIF_MUTE;
298*4882a593Smuzhiyun 		writel(ctl, priv->io + KIRKWOOD_PLAYCTL);
299*4882a593Smuzhiyun 		break;
300*4882a593Smuzhiyun 
301*4882a593Smuzhiyun 	case SNDRV_PCM_TRIGGER_RESUME:
302*4882a593Smuzhiyun 	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
303*4882a593Smuzhiyun 		ctl &= ~(KIRKWOOD_PLAYCTL_PAUSE | KIRKWOOD_PLAYCTL_I2S_MUTE |
304*4882a593Smuzhiyun 				KIRKWOOD_PLAYCTL_SPDIF_MUTE);
305*4882a593Smuzhiyun 		ctl = kirkwood_i2s_play_mute(ctl);
306*4882a593Smuzhiyun 		writel(ctl, priv->io + KIRKWOOD_PLAYCTL);
307*4882a593Smuzhiyun 		break;
308*4882a593Smuzhiyun 
309*4882a593Smuzhiyun 	default:
310*4882a593Smuzhiyun 		return -EINVAL;
311*4882a593Smuzhiyun 	}
312*4882a593Smuzhiyun 
313*4882a593Smuzhiyun 	return 0;
314*4882a593Smuzhiyun }
315*4882a593Smuzhiyun 
kirkwood_i2s_rec_trigger(struct snd_pcm_substream * substream,int cmd,struct snd_soc_dai * dai)316*4882a593Smuzhiyun static int kirkwood_i2s_rec_trigger(struct snd_pcm_substream *substream,
317*4882a593Smuzhiyun 				int cmd, struct snd_soc_dai *dai)
318*4882a593Smuzhiyun {
319*4882a593Smuzhiyun 	struct kirkwood_dma_data *priv = snd_soc_dai_get_drvdata(dai);
320*4882a593Smuzhiyun 	uint32_t ctl, value;
321*4882a593Smuzhiyun 
322*4882a593Smuzhiyun 	value = readl(priv->io + KIRKWOOD_RECCTL);
323*4882a593Smuzhiyun 
324*4882a593Smuzhiyun 	switch (cmd) {
325*4882a593Smuzhiyun 	case SNDRV_PCM_TRIGGER_START:
326*4882a593Smuzhiyun 		/* configure */
327*4882a593Smuzhiyun 		ctl = priv->ctl_rec;
328*4882a593Smuzhiyun 		if (dai->id == 0)
329*4882a593Smuzhiyun 			ctl &= ~KIRKWOOD_RECCTL_SPDIF_EN;	/* i2s */
330*4882a593Smuzhiyun 		else
331*4882a593Smuzhiyun 			ctl &= ~KIRKWOOD_RECCTL_I2S_EN;		/* spdif */
332*4882a593Smuzhiyun 
333*4882a593Smuzhiyun 		value = ctl & ~KIRKWOOD_RECCTL_ENABLE_MASK;
334*4882a593Smuzhiyun 		writel(value, priv->io + KIRKWOOD_RECCTL);
335*4882a593Smuzhiyun 
336*4882a593Smuzhiyun 		/* enable interrupts */
337*4882a593Smuzhiyun 		value = readl(priv->io + KIRKWOOD_INT_MASK);
338*4882a593Smuzhiyun 		value |= KIRKWOOD_INT_CAUSE_REC_BYTES;
339*4882a593Smuzhiyun 		writel(value, priv->io + KIRKWOOD_INT_MASK);
340*4882a593Smuzhiyun 
341*4882a593Smuzhiyun 		/* enable record */
342*4882a593Smuzhiyun 		writel(ctl, priv->io + KIRKWOOD_RECCTL);
343*4882a593Smuzhiyun 		break;
344*4882a593Smuzhiyun 
345*4882a593Smuzhiyun 	case SNDRV_PCM_TRIGGER_STOP:
346*4882a593Smuzhiyun 		/* stop audio, disable interrupts */
347*4882a593Smuzhiyun 		value = readl(priv->io + KIRKWOOD_RECCTL);
348*4882a593Smuzhiyun 		value |= KIRKWOOD_RECCTL_PAUSE | KIRKWOOD_RECCTL_MUTE;
349*4882a593Smuzhiyun 		writel(value, priv->io + KIRKWOOD_RECCTL);
350*4882a593Smuzhiyun 
351*4882a593Smuzhiyun 		value = readl(priv->io + KIRKWOOD_INT_MASK);
352*4882a593Smuzhiyun 		value &= ~KIRKWOOD_INT_CAUSE_REC_BYTES;
353*4882a593Smuzhiyun 		writel(value, priv->io + KIRKWOOD_INT_MASK);
354*4882a593Smuzhiyun 
355*4882a593Smuzhiyun 		/* disable all records */
356*4882a593Smuzhiyun 		value = readl(priv->io + KIRKWOOD_RECCTL);
357*4882a593Smuzhiyun 		value &= ~KIRKWOOD_RECCTL_ENABLE_MASK;
358*4882a593Smuzhiyun 		writel(value, priv->io + KIRKWOOD_RECCTL);
359*4882a593Smuzhiyun 		break;
360*4882a593Smuzhiyun 
361*4882a593Smuzhiyun 	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
362*4882a593Smuzhiyun 	case SNDRV_PCM_TRIGGER_SUSPEND:
363*4882a593Smuzhiyun 		value = readl(priv->io + KIRKWOOD_RECCTL);
364*4882a593Smuzhiyun 		value |= KIRKWOOD_RECCTL_PAUSE | KIRKWOOD_RECCTL_MUTE;
365*4882a593Smuzhiyun 		writel(value, priv->io + KIRKWOOD_RECCTL);
366*4882a593Smuzhiyun 		break;
367*4882a593Smuzhiyun 
368*4882a593Smuzhiyun 	case SNDRV_PCM_TRIGGER_RESUME:
369*4882a593Smuzhiyun 	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
370*4882a593Smuzhiyun 		value = readl(priv->io + KIRKWOOD_RECCTL);
371*4882a593Smuzhiyun 		value &= ~(KIRKWOOD_RECCTL_PAUSE | KIRKWOOD_RECCTL_MUTE);
372*4882a593Smuzhiyun 		writel(value, priv->io + KIRKWOOD_RECCTL);
373*4882a593Smuzhiyun 		break;
374*4882a593Smuzhiyun 
375*4882a593Smuzhiyun 	default:
376*4882a593Smuzhiyun 		return -EINVAL;
377*4882a593Smuzhiyun 	}
378*4882a593Smuzhiyun 
379*4882a593Smuzhiyun 	return 0;
380*4882a593Smuzhiyun }
381*4882a593Smuzhiyun 
kirkwood_i2s_trigger(struct snd_pcm_substream * substream,int cmd,struct snd_soc_dai * dai)382*4882a593Smuzhiyun static int kirkwood_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
383*4882a593Smuzhiyun 			       struct snd_soc_dai *dai)
384*4882a593Smuzhiyun {
385*4882a593Smuzhiyun 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
386*4882a593Smuzhiyun 		return kirkwood_i2s_play_trigger(substream, cmd, dai);
387*4882a593Smuzhiyun 	else
388*4882a593Smuzhiyun 		return kirkwood_i2s_rec_trigger(substream, cmd, dai);
389*4882a593Smuzhiyun 
390*4882a593Smuzhiyun 	return 0;
391*4882a593Smuzhiyun }
392*4882a593Smuzhiyun 
kirkwood_i2s_init(struct kirkwood_dma_data * priv)393*4882a593Smuzhiyun static int kirkwood_i2s_init(struct kirkwood_dma_data *priv)
394*4882a593Smuzhiyun {
395*4882a593Smuzhiyun 	unsigned long value;
396*4882a593Smuzhiyun 	unsigned int reg_data;
397*4882a593Smuzhiyun 
398*4882a593Smuzhiyun 	/* put system in a "safe" state : */
399*4882a593Smuzhiyun 	/* disable audio interrupts */
400*4882a593Smuzhiyun 	writel(0xffffffff, priv->io + KIRKWOOD_INT_CAUSE);
401*4882a593Smuzhiyun 	writel(0, priv->io + KIRKWOOD_INT_MASK);
402*4882a593Smuzhiyun 
403*4882a593Smuzhiyun 	reg_data = readl(priv->io + 0x1200);
404*4882a593Smuzhiyun 	reg_data &= (~(0x333FF8));
405*4882a593Smuzhiyun 	reg_data |= 0x111D18;
406*4882a593Smuzhiyun 	writel(reg_data, priv->io + 0x1200);
407*4882a593Smuzhiyun 
408*4882a593Smuzhiyun 	msleep(500);
409*4882a593Smuzhiyun 
410*4882a593Smuzhiyun 	reg_data = readl(priv->io + 0x1200);
411*4882a593Smuzhiyun 	reg_data &= (~(0x333FF8));
412*4882a593Smuzhiyun 	reg_data |= 0x111D18;
413*4882a593Smuzhiyun 	writel(reg_data, priv->io + 0x1200);
414*4882a593Smuzhiyun 
415*4882a593Smuzhiyun 	/* disable playback/record */
416*4882a593Smuzhiyun 	value = readl(priv->io + KIRKWOOD_PLAYCTL);
417*4882a593Smuzhiyun 	value &= ~KIRKWOOD_PLAYCTL_ENABLE_MASK;
418*4882a593Smuzhiyun 	writel(value, priv->io + KIRKWOOD_PLAYCTL);
419*4882a593Smuzhiyun 
420*4882a593Smuzhiyun 	value = readl(priv->io + KIRKWOOD_RECCTL);
421*4882a593Smuzhiyun 	value &= ~KIRKWOOD_RECCTL_ENABLE_MASK;
422*4882a593Smuzhiyun 	writel(value, priv->io + KIRKWOOD_RECCTL);
423*4882a593Smuzhiyun 
424*4882a593Smuzhiyun 	return 0;
425*4882a593Smuzhiyun 
426*4882a593Smuzhiyun }
427*4882a593Smuzhiyun 
428*4882a593Smuzhiyun static const struct snd_soc_dai_ops kirkwood_i2s_dai_ops = {
429*4882a593Smuzhiyun 	.startup	= kirkwood_i2s_startup,
430*4882a593Smuzhiyun 	.trigger	= kirkwood_i2s_trigger,
431*4882a593Smuzhiyun 	.hw_params      = kirkwood_i2s_hw_params,
432*4882a593Smuzhiyun 	.set_fmt        = kirkwood_i2s_set_fmt,
433*4882a593Smuzhiyun };
434*4882a593Smuzhiyun 
435*4882a593Smuzhiyun static struct snd_soc_dai_driver kirkwood_i2s_dai[2] = {
436*4882a593Smuzhiyun     {
437*4882a593Smuzhiyun 	.name = "i2s",
438*4882a593Smuzhiyun 	.id = 0,
439*4882a593Smuzhiyun 	.playback = {
440*4882a593Smuzhiyun 		.channels_min = 1,
441*4882a593Smuzhiyun 		.channels_max = 2,
442*4882a593Smuzhiyun 		.rates = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |
443*4882a593Smuzhiyun 				SNDRV_PCM_RATE_96000,
444*4882a593Smuzhiyun 		.formats = KIRKWOOD_I2S_FORMATS,
445*4882a593Smuzhiyun 	},
446*4882a593Smuzhiyun 	.capture = {
447*4882a593Smuzhiyun 		.channels_min = 1,
448*4882a593Smuzhiyun 		.channels_max = 2,
449*4882a593Smuzhiyun 		.rates = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |
450*4882a593Smuzhiyun 				SNDRV_PCM_RATE_96000,
451*4882a593Smuzhiyun 		.formats = KIRKWOOD_I2S_FORMATS,
452*4882a593Smuzhiyun 	},
453*4882a593Smuzhiyun 	.ops = &kirkwood_i2s_dai_ops,
454*4882a593Smuzhiyun     },
455*4882a593Smuzhiyun     {
456*4882a593Smuzhiyun 	.name = "spdif",
457*4882a593Smuzhiyun 	.id = 1,
458*4882a593Smuzhiyun 	.playback = {
459*4882a593Smuzhiyun 		.channels_min = 1,
460*4882a593Smuzhiyun 		.channels_max = 2,
461*4882a593Smuzhiyun 		.rates = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |
462*4882a593Smuzhiyun 				SNDRV_PCM_RATE_96000,
463*4882a593Smuzhiyun 		.formats = KIRKWOOD_SPDIF_FORMATS,
464*4882a593Smuzhiyun 	},
465*4882a593Smuzhiyun 	.capture = {
466*4882a593Smuzhiyun 		.channels_min = 1,
467*4882a593Smuzhiyun 		.channels_max = 2,
468*4882a593Smuzhiyun 		.rates = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |
469*4882a593Smuzhiyun 				SNDRV_PCM_RATE_96000,
470*4882a593Smuzhiyun 		.formats = KIRKWOOD_SPDIF_FORMATS,
471*4882a593Smuzhiyun 	},
472*4882a593Smuzhiyun 	.ops = &kirkwood_i2s_dai_ops,
473*4882a593Smuzhiyun     },
474*4882a593Smuzhiyun };
475*4882a593Smuzhiyun 
476*4882a593Smuzhiyun static struct snd_soc_dai_driver kirkwood_i2s_dai_extclk[2] = {
477*4882a593Smuzhiyun     {
478*4882a593Smuzhiyun 	.name = "i2s",
479*4882a593Smuzhiyun 	.id = 0,
480*4882a593Smuzhiyun 	.playback = {
481*4882a593Smuzhiyun 		.channels_min = 1,
482*4882a593Smuzhiyun 		.channels_max = 2,
483*4882a593Smuzhiyun 		.rates = SNDRV_PCM_RATE_CONTINUOUS,
484*4882a593Smuzhiyun 		.rate_min = 5512,
485*4882a593Smuzhiyun 		.rate_max = 192000,
486*4882a593Smuzhiyun 		.formats = KIRKWOOD_I2S_FORMATS,
487*4882a593Smuzhiyun 	},
488*4882a593Smuzhiyun 	.capture = {
489*4882a593Smuzhiyun 		.channels_min = 1,
490*4882a593Smuzhiyun 		.channels_max = 2,
491*4882a593Smuzhiyun 		.rates = SNDRV_PCM_RATE_CONTINUOUS,
492*4882a593Smuzhiyun 		.rate_min = 5512,
493*4882a593Smuzhiyun 		.rate_max = 192000,
494*4882a593Smuzhiyun 		.formats = KIRKWOOD_I2S_FORMATS,
495*4882a593Smuzhiyun 	},
496*4882a593Smuzhiyun 	.ops = &kirkwood_i2s_dai_ops,
497*4882a593Smuzhiyun     },
498*4882a593Smuzhiyun     {
499*4882a593Smuzhiyun 	.name = "spdif",
500*4882a593Smuzhiyun 	.id = 1,
501*4882a593Smuzhiyun 	.playback = {
502*4882a593Smuzhiyun 		.channels_min = 1,
503*4882a593Smuzhiyun 		.channels_max = 2,
504*4882a593Smuzhiyun 		.rates = SNDRV_PCM_RATE_CONTINUOUS,
505*4882a593Smuzhiyun 		.rate_min = 5512,
506*4882a593Smuzhiyun 		.rate_max = 192000,
507*4882a593Smuzhiyun 		.formats = KIRKWOOD_SPDIF_FORMATS,
508*4882a593Smuzhiyun 	},
509*4882a593Smuzhiyun 	.capture = {
510*4882a593Smuzhiyun 		.channels_min = 1,
511*4882a593Smuzhiyun 		.channels_max = 2,
512*4882a593Smuzhiyun 		.rates = SNDRV_PCM_RATE_CONTINUOUS,
513*4882a593Smuzhiyun 		.rate_min = 5512,
514*4882a593Smuzhiyun 		.rate_max = 192000,
515*4882a593Smuzhiyun 		.formats = KIRKWOOD_SPDIF_FORMATS,
516*4882a593Smuzhiyun 	},
517*4882a593Smuzhiyun 	.ops = &kirkwood_i2s_dai_ops,
518*4882a593Smuzhiyun     },
519*4882a593Smuzhiyun };
520*4882a593Smuzhiyun 
kirkwood_i2s_dev_probe(struct platform_device * pdev)521*4882a593Smuzhiyun static int kirkwood_i2s_dev_probe(struct platform_device *pdev)
522*4882a593Smuzhiyun {
523*4882a593Smuzhiyun 	struct kirkwood_asoc_platform_data *data = pdev->dev.platform_data;
524*4882a593Smuzhiyun 	struct snd_soc_dai_driver *soc_dai = kirkwood_i2s_dai;
525*4882a593Smuzhiyun 	struct kirkwood_dma_data *priv;
526*4882a593Smuzhiyun 	struct device_node *np = pdev->dev.of_node;
527*4882a593Smuzhiyun 	int err;
528*4882a593Smuzhiyun 
529*4882a593Smuzhiyun 	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
530*4882a593Smuzhiyun 	if (!priv)
531*4882a593Smuzhiyun 		return -ENOMEM;
532*4882a593Smuzhiyun 
533*4882a593Smuzhiyun 	dev_set_drvdata(&pdev->dev, priv);
534*4882a593Smuzhiyun 
535*4882a593Smuzhiyun 	priv->io = devm_platform_ioremap_resource(pdev, 0);
536*4882a593Smuzhiyun 	if (IS_ERR(priv->io))
537*4882a593Smuzhiyun 		return PTR_ERR(priv->io);
538*4882a593Smuzhiyun 
539*4882a593Smuzhiyun 	priv->irq = platform_get_irq(pdev, 0);
540*4882a593Smuzhiyun 	if (priv->irq < 0)
541*4882a593Smuzhiyun 		return priv->irq;
542*4882a593Smuzhiyun 
543*4882a593Smuzhiyun 	if (np) {
544*4882a593Smuzhiyun 		priv->burst = 128;		/* might be 32 or 128 */
545*4882a593Smuzhiyun 	} else if (data) {
546*4882a593Smuzhiyun 		priv->burst = data->burst;
547*4882a593Smuzhiyun 	} else {
548*4882a593Smuzhiyun 		dev_err(&pdev->dev, "no DT nor platform data ?!\n");
549*4882a593Smuzhiyun 		return -EINVAL;
550*4882a593Smuzhiyun 	}
551*4882a593Smuzhiyun 
552*4882a593Smuzhiyun 	priv->clk = devm_clk_get(&pdev->dev, np ? "internal" : NULL);
553*4882a593Smuzhiyun 	if (IS_ERR(priv->clk)) {
554*4882a593Smuzhiyun 		dev_err(&pdev->dev, "no clock\n");
555*4882a593Smuzhiyun 		return PTR_ERR(priv->clk);
556*4882a593Smuzhiyun 	}
557*4882a593Smuzhiyun 
558*4882a593Smuzhiyun 	priv->extclk = devm_clk_get(&pdev->dev, "extclk");
559*4882a593Smuzhiyun 	if (IS_ERR(priv->extclk)) {
560*4882a593Smuzhiyun 		if (PTR_ERR(priv->extclk) == -EPROBE_DEFER)
561*4882a593Smuzhiyun 			return -EPROBE_DEFER;
562*4882a593Smuzhiyun 	} else {
563*4882a593Smuzhiyun 		if (clk_is_match(priv->extclk, priv->clk)) {
564*4882a593Smuzhiyun 			devm_clk_put(&pdev->dev, priv->extclk);
565*4882a593Smuzhiyun 			priv->extclk = ERR_PTR(-EINVAL);
566*4882a593Smuzhiyun 		} else {
567*4882a593Smuzhiyun 			dev_info(&pdev->dev, "found external clock\n");
568*4882a593Smuzhiyun 			clk_prepare_enable(priv->extclk);
569*4882a593Smuzhiyun 			soc_dai = kirkwood_i2s_dai_extclk;
570*4882a593Smuzhiyun 		}
571*4882a593Smuzhiyun 	}
572*4882a593Smuzhiyun 
573*4882a593Smuzhiyun 	err = clk_prepare_enable(priv->clk);
574*4882a593Smuzhiyun 	if (err < 0)
575*4882a593Smuzhiyun 		return err;
576*4882a593Smuzhiyun 
577*4882a593Smuzhiyun 	/* Some sensible defaults - this reflects the powerup values */
578*4882a593Smuzhiyun 	priv->ctl_play = KIRKWOOD_PLAYCTL_SIZE_24;
579*4882a593Smuzhiyun 	priv->ctl_rec = KIRKWOOD_RECCTL_SIZE_24;
580*4882a593Smuzhiyun 
581*4882a593Smuzhiyun 	/* Select the burst size */
582*4882a593Smuzhiyun 	if (priv->burst == 32) {
583*4882a593Smuzhiyun 		priv->ctl_play |= KIRKWOOD_PLAYCTL_BURST_32;
584*4882a593Smuzhiyun 		priv->ctl_rec |= KIRKWOOD_RECCTL_BURST_32;
585*4882a593Smuzhiyun 	} else {
586*4882a593Smuzhiyun 		priv->ctl_play |= KIRKWOOD_PLAYCTL_BURST_128;
587*4882a593Smuzhiyun 		priv->ctl_rec |= KIRKWOOD_RECCTL_BURST_128;
588*4882a593Smuzhiyun 	}
589*4882a593Smuzhiyun 
590*4882a593Smuzhiyun 	err = snd_soc_register_component(&pdev->dev, &kirkwood_soc_component,
591*4882a593Smuzhiyun 					 soc_dai, 2);
592*4882a593Smuzhiyun 	if (err) {
593*4882a593Smuzhiyun 		dev_err(&pdev->dev, "snd_soc_register_component failed\n");
594*4882a593Smuzhiyun 		goto err_component;
595*4882a593Smuzhiyun 	}
596*4882a593Smuzhiyun 
597*4882a593Smuzhiyun 	kirkwood_i2s_init(priv);
598*4882a593Smuzhiyun 
599*4882a593Smuzhiyun 	return 0;
600*4882a593Smuzhiyun 
601*4882a593Smuzhiyun  err_component:
602*4882a593Smuzhiyun 	if (!IS_ERR(priv->extclk))
603*4882a593Smuzhiyun 		clk_disable_unprepare(priv->extclk);
604*4882a593Smuzhiyun 	clk_disable_unprepare(priv->clk);
605*4882a593Smuzhiyun 
606*4882a593Smuzhiyun 	return err;
607*4882a593Smuzhiyun }
608*4882a593Smuzhiyun 
kirkwood_i2s_dev_remove(struct platform_device * pdev)609*4882a593Smuzhiyun static int kirkwood_i2s_dev_remove(struct platform_device *pdev)
610*4882a593Smuzhiyun {
611*4882a593Smuzhiyun 	struct kirkwood_dma_data *priv = dev_get_drvdata(&pdev->dev);
612*4882a593Smuzhiyun 
613*4882a593Smuzhiyun 	snd_soc_unregister_component(&pdev->dev);
614*4882a593Smuzhiyun 	if (!IS_ERR(priv->extclk))
615*4882a593Smuzhiyun 		clk_disable_unprepare(priv->extclk);
616*4882a593Smuzhiyun 	clk_disable_unprepare(priv->clk);
617*4882a593Smuzhiyun 
618*4882a593Smuzhiyun 	return 0;
619*4882a593Smuzhiyun }
620*4882a593Smuzhiyun 
621*4882a593Smuzhiyun #ifdef CONFIG_OF
622*4882a593Smuzhiyun static const struct of_device_id mvebu_audio_of_match[] = {
623*4882a593Smuzhiyun 	{ .compatible = "marvell,kirkwood-audio" },
624*4882a593Smuzhiyun 	{ .compatible = "marvell,dove-audio" },
625*4882a593Smuzhiyun 	{ .compatible = "marvell,armada370-audio" },
626*4882a593Smuzhiyun 	{ }
627*4882a593Smuzhiyun };
628*4882a593Smuzhiyun MODULE_DEVICE_TABLE(of, mvebu_audio_of_match);
629*4882a593Smuzhiyun #endif
630*4882a593Smuzhiyun 
631*4882a593Smuzhiyun static struct platform_driver kirkwood_i2s_driver = {
632*4882a593Smuzhiyun 	.probe  = kirkwood_i2s_dev_probe,
633*4882a593Smuzhiyun 	.remove = kirkwood_i2s_dev_remove,
634*4882a593Smuzhiyun 	.driver = {
635*4882a593Smuzhiyun 		.name = DRV_NAME,
636*4882a593Smuzhiyun 		.of_match_table = of_match_ptr(mvebu_audio_of_match),
637*4882a593Smuzhiyun 	},
638*4882a593Smuzhiyun };
639*4882a593Smuzhiyun 
640*4882a593Smuzhiyun module_platform_driver(kirkwood_i2s_driver);
641*4882a593Smuzhiyun 
642*4882a593Smuzhiyun /* Module information */
643*4882a593Smuzhiyun MODULE_AUTHOR("Arnaud Patard, <arnaud.patard@rtp-net.org>");
644*4882a593Smuzhiyun MODULE_DESCRIPTION("Kirkwood I2S SoC Interface");
645*4882a593Smuzhiyun MODULE_LICENSE("GPL");
646*4882a593Smuzhiyun MODULE_ALIAS("platform:mvebu-audio");
647