xref: /linux/sound/soc/kirkwood/kirkwood-i2s.c (revision af64d7341ab51335eeb03453180cf200b120ec43)
1f9b95980Sapatard@mandriva.com /*
2f9b95980Sapatard@mandriva.com  * kirkwood-i2s.c
3f9b95980Sapatard@mandriva.com  *
4f9b95980Sapatard@mandriva.com  * (c) 2010 Arnaud Patard <apatard@mandriva.com>
569737897SArnaud Patard (Rtp)  * (c) 2010 Arnaud Patard <arnaud.patard@rtp-net.org>
6f9b95980Sapatard@mandriva.com  *
7f9b95980Sapatard@mandriva.com  *  This program is free software; you can redistribute  it and/or modify it
8f9b95980Sapatard@mandriva.com  *  under  the terms of  the GNU General  Public License as published by the
9f9b95980Sapatard@mandriva.com  *  Free Software Foundation;  either version 2 of the  License, or (at your
10f9b95980Sapatard@mandriva.com  *  option) any later version.
11f9b95980Sapatard@mandriva.com  */
12f9b95980Sapatard@mandriva.com 
13f9b95980Sapatard@mandriva.com #include <linux/init.h>
14f9b95980Sapatard@mandriva.com #include <linux/module.h>
15f9b95980Sapatard@mandriva.com #include <linux/platform_device.h>
16f9b95980Sapatard@mandriva.com #include <linux/io.h>
17f9b95980Sapatard@mandriva.com #include <linux/slab.h>
18f9b95980Sapatard@mandriva.com #include <linux/mbus.h>
19f9b95980Sapatard@mandriva.com #include <linux/delay.h>
20e919c716SAndrew Lunn #include <linux/clk.h>
21f9b95980Sapatard@mandriva.com #include <sound/pcm.h>
22f9b95980Sapatard@mandriva.com #include <sound/pcm_params.h>
23f9b95980Sapatard@mandriva.com #include <sound/soc.h>
24c02cecb9SArnd Bergmann #include <linux/platform_data/asoc-kirkwood.h>
25f9b95980Sapatard@mandriva.com #include "kirkwood.h"
26f9b95980Sapatard@mandriva.com 
27f9b95980Sapatard@mandriva.com #define DRV_NAME	"kirkwood-i2s"
28f9b95980Sapatard@mandriva.com 
29f9b95980Sapatard@mandriva.com #define KIRKWOOD_I2S_FORMATS \
30f9b95980Sapatard@mandriva.com 	(SNDRV_PCM_FMTBIT_S16_LE | \
31f9b95980Sapatard@mandriva.com 	 SNDRV_PCM_FMTBIT_S24_LE | \
32f9b95980Sapatard@mandriva.com 	 SNDRV_PCM_FMTBIT_S32_LE)
33f9b95980Sapatard@mandriva.com 
34f9b95980Sapatard@mandriva.com static int kirkwood_i2s_set_fmt(struct snd_soc_dai *cpu_dai,
35f9b95980Sapatard@mandriva.com 		unsigned int fmt)
36f9b95980Sapatard@mandriva.com {
37f0fba2adSLiam Girdwood 	struct kirkwood_dma_data *priv = snd_soc_dai_get_drvdata(cpu_dai);
38f9b95980Sapatard@mandriva.com 	unsigned long mask;
39f9b95980Sapatard@mandriva.com 	unsigned long value;
40f9b95980Sapatard@mandriva.com 
41f9b95980Sapatard@mandriva.com 	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
42f9b95980Sapatard@mandriva.com 	case SND_SOC_DAIFMT_RIGHT_J:
43f9b95980Sapatard@mandriva.com 		mask = KIRKWOOD_I2S_CTL_RJ;
44f9b95980Sapatard@mandriva.com 		break;
45f9b95980Sapatard@mandriva.com 	case SND_SOC_DAIFMT_LEFT_J:
46f9b95980Sapatard@mandriva.com 		mask = KIRKWOOD_I2S_CTL_LJ;
47f9b95980Sapatard@mandriva.com 		break;
48f9b95980Sapatard@mandriva.com 	case SND_SOC_DAIFMT_I2S:
49f9b95980Sapatard@mandriva.com 		mask = KIRKWOOD_I2S_CTL_I2S;
50f9b95980Sapatard@mandriva.com 		break;
51f9b95980Sapatard@mandriva.com 	default:
52f9b95980Sapatard@mandriva.com 		return -EINVAL;
53f9b95980Sapatard@mandriva.com 	}
54f9b95980Sapatard@mandriva.com 
55f9b95980Sapatard@mandriva.com 	/*
56f9b95980Sapatard@mandriva.com 	 * Set same format for playback and record
57f9b95980Sapatard@mandriva.com 	 * This avoids some troubles.
58f9b95980Sapatard@mandriva.com 	 */
59f9b95980Sapatard@mandriva.com 	value = readl(priv->io+KIRKWOOD_I2S_PLAYCTL);
60f9b95980Sapatard@mandriva.com 	value &= ~KIRKWOOD_I2S_CTL_JUST_MASK;
61f9b95980Sapatard@mandriva.com 	value |= mask;
62f9b95980Sapatard@mandriva.com 	writel(value, priv->io+KIRKWOOD_I2S_PLAYCTL);
63f9b95980Sapatard@mandriva.com 
64f9b95980Sapatard@mandriva.com 	value = readl(priv->io+KIRKWOOD_I2S_RECCTL);
65f9b95980Sapatard@mandriva.com 	value &= ~KIRKWOOD_I2S_CTL_JUST_MASK;
66f9b95980Sapatard@mandriva.com 	value |= mask;
67f9b95980Sapatard@mandriva.com 	writel(value, priv->io+KIRKWOOD_I2S_RECCTL);
68f9b95980Sapatard@mandriva.com 
69f9b95980Sapatard@mandriva.com 	return 0;
70f9b95980Sapatard@mandriva.com }
71f9b95980Sapatard@mandriva.com 
72f9b95980Sapatard@mandriva.com static inline void kirkwood_set_dco(void __iomem *io, unsigned long rate)
73f9b95980Sapatard@mandriva.com {
74f9b95980Sapatard@mandriva.com 	unsigned long value;
75f9b95980Sapatard@mandriva.com 
76f9b95980Sapatard@mandriva.com 	value = KIRKWOOD_DCO_CTL_OFFSET_0;
77f9b95980Sapatard@mandriva.com 	switch (rate) {
78f9b95980Sapatard@mandriva.com 	default:
79f9b95980Sapatard@mandriva.com 	case 44100:
80f9b95980Sapatard@mandriva.com 		value |= KIRKWOOD_DCO_CTL_FREQ_11;
81f9b95980Sapatard@mandriva.com 		break;
82f9b95980Sapatard@mandriva.com 	case 48000:
83f9b95980Sapatard@mandriva.com 		value |= KIRKWOOD_DCO_CTL_FREQ_12;
84f9b95980Sapatard@mandriva.com 		break;
85f9b95980Sapatard@mandriva.com 	case 96000:
86f9b95980Sapatard@mandriva.com 		value |= KIRKWOOD_DCO_CTL_FREQ_24;
87f9b95980Sapatard@mandriva.com 		break;
88f9b95980Sapatard@mandriva.com 	}
89f9b95980Sapatard@mandriva.com 	writel(value, io + KIRKWOOD_DCO_CTL);
90f9b95980Sapatard@mandriva.com 
91f9b95980Sapatard@mandriva.com 	/* wait for dco locked */
92f9b95980Sapatard@mandriva.com 	do {
93f9b95980Sapatard@mandriva.com 		cpu_relax();
94f9b95980Sapatard@mandriva.com 		value = readl(io + KIRKWOOD_DCO_SPCR_STATUS);
952424d458SRussell King 		value &= KIRKWOOD_DCO_SPCR_STATUS_DCO_LOCK;
96f9b95980Sapatard@mandriva.com 	} while (value == 0);
97f9b95980Sapatard@mandriva.com }
98f9b95980Sapatard@mandriva.com 
99363589bfSRussell King static void kirkwood_set_rate(struct snd_soc_dai *dai,
100363589bfSRussell King 	struct kirkwood_dma_data *priv, unsigned long rate)
101363589bfSRussell King {
102363589bfSRussell King 	uint32_t clks_ctrl;
103363589bfSRussell King 
104363589bfSRussell King 	if (rate == 44100 || rate == 48000 || rate == 96000) {
1058a537f85SJean-Francois Moine 		/* use internal dco for the supported rates
1068a537f85SJean-Francois Moine 		 * defined in kirkwood_i2s_dai */
107363589bfSRussell King 		dev_dbg(dai->dev, "%s: dco set rate = %lu\n",
108363589bfSRussell King 			__func__, rate);
109363589bfSRussell King 		kirkwood_set_dco(priv->io, rate);
110363589bfSRussell King 
111363589bfSRussell King 		clks_ctrl = KIRKWOOD_MCLK_SOURCE_DCO;
1128a537f85SJean-Francois Moine 	} else {
1138a537f85SJean-Francois Moine 		/* use the external clock for the other rates
1148a537f85SJean-Francois Moine 		 * defined in kirkwood_i2s_dai_extclk */
115363589bfSRussell King 		dev_dbg(dai->dev, "%s: extclk set rate = %lu -> %lu\n",
116363589bfSRussell King 			__func__, rate, 256 * rate);
117363589bfSRussell King 		clk_set_rate(priv->extclk, 256 * rate);
118363589bfSRussell King 
119363589bfSRussell King 		clks_ctrl = KIRKWOOD_MCLK_SOURCE_EXTCLK;
120363589bfSRussell King 	}
121363589bfSRussell King 	writel(clks_ctrl, priv->io + KIRKWOOD_CLOCKS_CTRL);
122363589bfSRussell King }
123363589bfSRussell King 
124f0fba2adSLiam Girdwood static int kirkwood_i2s_startup(struct snd_pcm_substream *substream,
125f0fba2adSLiam Girdwood 		struct snd_soc_dai *dai)
126f0fba2adSLiam Girdwood {
127f0fba2adSLiam Girdwood 	struct kirkwood_dma_data *priv = snd_soc_dai_get_drvdata(dai);
128f0fba2adSLiam Girdwood 
129f0fba2adSLiam Girdwood 	snd_soc_dai_set_dma_data(dai, substream, priv);
130f0fba2adSLiam Girdwood 	return 0;
131f0fba2adSLiam Girdwood }
132f0fba2adSLiam Girdwood 
133f9b95980Sapatard@mandriva.com static int kirkwood_i2s_hw_params(struct snd_pcm_substream *substream,
134f9b95980Sapatard@mandriva.com 				 struct snd_pcm_hw_params *params,
135f9b95980Sapatard@mandriva.com 				 struct snd_soc_dai *dai)
136f9b95980Sapatard@mandriva.com {
137f0fba2adSLiam Girdwood 	struct kirkwood_dma_data *priv = snd_soc_dai_get_drvdata(dai);
138d8d11ba5SRussell King 	uint32_t ctl_play, ctl_rec;
139d8d11ba5SRussell King 	unsigned int i2s_reg;
140d8d11ba5SRussell King 	unsigned long i2s_value;
141f9b95980Sapatard@mandriva.com 
142f9b95980Sapatard@mandriva.com 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
143f9b95980Sapatard@mandriva.com 		i2s_reg = KIRKWOOD_I2S_PLAYCTL;
144f9b95980Sapatard@mandriva.com 	} else {
145f9b95980Sapatard@mandriva.com 		i2s_reg = KIRKWOOD_I2S_RECCTL;
146f9b95980Sapatard@mandriva.com 	}
147f9b95980Sapatard@mandriva.com 
148363589bfSRussell King 	kirkwood_set_rate(dai, priv, params_rate(params));
149f9b95980Sapatard@mandriva.com 
150f9b95980Sapatard@mandriva.com 	i2s_value = readl(priv->io+i2s_reg);
151f9b95980Sapatard@mandriva.com 	i2s_value &= ~KIRKWOOD_I2S_CTL_SIZE_MASK;
152f9b95980Sapatard@mandriva.com 
153f9b95980Sapatard@mandriva.com 	/*
154f9b95980Sapatard@mandriva.com 	 * Size settings in play/rec i2s control regs and play/rec control
155f9b95980Sapatard@mandriva.com 	 * regs must be the same.
156f9b95980Sapatard@mandriva.com 	 */
157f9b95980Sapatard@mandriva.com 	switch (params_format(params)) {
158f9b95980Sapatard@mandriva.com 	case SNDRV_PCM_FORMAT_S16_LE:
159f9b95980Sapatard@mandriva.com 		i2s_value |= KIRKWOOD_I2S_CTL_SIZE_16;
160d8d11ba5SRussell King 		ctl_play = KIRKWOOD_PLAYCTL_SIZE_16_C |
161d8d11ba5SRussell King 			   KIRKWOOD_PLAYCTL_I2S_EN;
162d8d11ba5SRussell King 		ctl_rec = KIRKWOOD_RECCTL_SIZE_16_C |
163d8d11ba5SRussell King 			  KIRKWOOD_RECCTL_I2S_EN;
164f9b95980Sapatard@mandriva.com 		break;
165f9b95980Sapatard@mandriva.com 	/*
166f9b95980Sapatard@mandriva.com 	 * doesn't work... S20_3LE != kirkwood 20bit format ?
167f9b95980Sapatard@mandriva.com 	 *
168f9b95980Sapatard@mandriva.com 	case SNDRV_PCM_FORMAT_S20_3LE:
169f9b95980Sapatard@mandriva.com 		i2s_value |= KIRKWOOD_I2S_CTL_SIZE_20;
170d8d11ba5SRussell King 		ctl_play = KIRKWOOD_PLAYCTL_SIZE_20 |
171d8d11ba5SRussell King 			   KIRKWOOD_PLAYCTL_I2S_EN;
172d8d11ba5SRussell King 		ctl_rec = KIRKWOOD_RECCTL_SIZE_20 |
173d8d11ba5SRussell King 			  KIRKWOOD_RECCTL_I2S_EN;
174f9b95980Sapatard@mandriva.com 		break;
175f9b95980Sapatard@mandriva.com 	*/
176f9b95980Sapatard@mandriva.com 	case SNDRV_PCM_FORMAT_S24_LE:
177f9b95980Sapatard@mandriva.com 		i2s_value |= KIRKWOOD_I2S_CTL_SIZE_24;
178d8d11ba5SRussell King 		ctl_play = KIRKWOOD_PLAYCTL_SIZE_24 |
179d8d11ba5SRussell King 			   KIRKWOOD_PLAYCTL_I2S_EN;
180d8d11ba5SRussell King 		ctl_rec = KIRKWOOD_RECCTL_SIZE_24 |
181d8d11ba5SRussell King 			  KIRKWOOD_RECCTL_I2S_EN;
182f9b95980Sapatard@mandriva.com 		break;
183f9b95980Sapatard@mandriva.com 	case SNDRV_PCM_FORMAT_S32_LE:
184f9b95980Sapatard@mandriva.com 		i2s_value |= KIRKWOOD_I2S_CTL_SIZE_32;
185d8d11ba5SRussell King 		ctl_play = KIRKWOOD_PLAYCTL_SIZE_32 |
186d8d11ba5SRussell King 			   KIRKWOOD_PLAYCTL_I2S_EN;
187d8d11ba5SRussell King 		ctl_rec = KIRKWOOD_RECCTL_SIZE_32 |
188d8d11ba5SRussell King 			  KIRKWOOD_RECCTL_I2S_EN;
189f9b95980Sapatard@mandriva.com 		break;
190f9b95980Sapatard@mandriva.com 	default:
191f9b95980Sapatard@mandriva.com 		return -EINVAL;
192f9b95980Sapatard@mandriva.com 	}
193dfe4c936Sarnaud.patard@rtp-net.org 
194dfe4c936Sarnaud.patard@rtp-net.org 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
195dfe4c936Sarnaud.patard@rtp-net.org 		if (params_channels(params) == 1)
196d8d11ba5SRussell King 			ctl_play |= KIRKWOOD_PLAYCTL_MONO_BOTH;
197dfe4c936Sarnaud.patard@rtp-net.org 		else
198d8d11ba5SRussell King 			ctl_play |= KIRKWOOD_PLAYCTL_MONO_OFF;
199d8d11ba5SRussell King 
200d8d11ba5SRussell King 		priv->ctl_play &= ~(KIRKWOOD_PLAYCTL_MONO_MASK |
201d8d11ba5SRussell King 				    KIRKWOOD_PLAYCTL_I2S_EN |
202d8d11ba5SRussell King 				    KIRKWOOD_PLAYCTL_SPDIF_EN |
203d8d11ba5SRussell King 				    KIRKWOOD_PLAYCTL_SIZE_MASK);
204d8d11ba5SRussell King 		priv->ctl_play |= ctl_play;
205d8d11ba5SRussell King 	} else {
206d8d11ba5SRussell King 		priv->ctl_rec &= ~KIRKWOOD_RECCTL_SIZE_MASK;
207d8d11ba5SRussell King 		priv->ctl_rec |= ctl_rec;
208dfe4c936Sarnaud.patard@rtp-net.org 	}
209dfe4c936Sarnaud.patard@rtp-net.org 
210f9b95980Sapatard@mandriva.com 	writel(i2s_value, priv->io+i2s_reg);
211f9b95980Sapatard@mandriva.com 
212f9b95980Sapatard@mandriva.com 	return 0;
213f9b95980Sapatard@mandriva.com }
214f9b95980Sapatard@mandriva.com 
215f9b95980Sapatard@mandriva.com static int kirkwood_i2s_play_trigger(struct snd_pcm_substream *substream,
216f9b95980Sapatard@mandriva.com 				int cmd, struct snd_soc_dai *dai)
217f9b95980Sapatard@mandriva.com {
218f0fba2adSLiam Girdwood 	struct kirkwood_dma_data *priv = snd_soc_dai_get_drvdata(dai);
219982b604bSRussell King 	uint32_t ctl, value;
220f9b95980Sapatard@mandriva.com 
221982b604bSRussell King 	ctl = readl(priv->io + KIRKWOOD_PLAYCTL);
222982b604bSRussell King 	if (ctl & KIRKWOOD_PLAYCTL_PAUSE) {
223982b604bSRussell King 		unsigned timeout = 5000;
224f9b95980Sapatard@mandriva.com 		/*
225982b604bSRussell King 		 * The Armada510 spec says that if we enter pause mode, the
226982b604bSRussell King 		 * busy bit must be read back as clear _twice_.  Make sure
227982b604bSRussell King 		 * we respect that otherwise we get DMA underruns.
228f9b95980Sapatard@mandriva.com 		 */
229982b604bSRussell King 		do {
230982b604bSRussell King 			value = ctl;
231982b604bSRussell King 			ctl = readl(priv->io + KIRKWOOD_PLAYCTL);
232982b604bSRussell King 			if (!((ctl | value) & KIRKWOOD_PLAYCTL_PLAY_BUSY))
233982b604bSRussell King 				break;
234982b604bSRussell King 			udelay(1);
235982b604bSRussell King 		} while (timeout--);
236982b604bSRussell King 
237982b604bSRussell King 		if ((ctl | value) & KIRKWOOD_PLAYCTL_PLAY_BUSY)
238982b604bSRussell King 			dev_notice(dai->dev, "timed out waiting for busy to deassert: %08x\n",
239982b604bSRussell King 				   ctl);
240982b604bSRussell King 	}
241f9b95980Sapatard@mandriva.com 
242f9b95980Sapatard@mandriva.com 	switch (cmd) {
243f9b95980Sapatard@mandriva.com 	case SNDRV_PCM_TRIGGER_START:
244d8d11ba5SRussell King 		/* configure */
245d8d11ba5SRussell King 		ctl = priv->ctl_play;
246d8d11ba5SRussell King 		value = ctl & ~(KIRKWOOD_PLAYCTL_I2S_EN |
247d8d11ba5SRussell King 				KIRKWOOD_PLAYCTL_SPDIF_EN);
248d8d11ba5SRussell King 		writel(value, priv->io + KIRKWOOD_PLAYCTL);
249d8d11ba5SRussell King 
250d8d11ba5SRussell King 		/* enable interrupts */
251f9b95980Sapatard@mandriva.com 		value = readl(priv->io + KIRKWOOD_INT_MASK);
252f9b95980Sapatard@mandriva.com 		value |= KIRKWOOD_INT_CAUSE_PLAY_BYTES;
253f9b95980Sapatard@mandriva.com 		writel(value, priv->io + KIRKWOOD_INT_MASK);
254f9b95980Sapatard@mandriva.com 
255d8d11ba5SRussell King 		/* enable playback */
256982b604bSRussell King 		writel(ctl, priv->io + KIRKWOOD_PLAYCTL);
257f9b95980Sapatard@mandriva.com 		break;
258f9b95980Sapatard@mandriva.com 
259f9b95980Sapatard@mandriva.com 	case SNDRV_PCM_TRIGGER_STOP:
260f9b95980Sapatard@mandriva.com 		/* stop audio, disable interrupts */
261982b604bSRussell King 		ctl |= KIRKWOOD_PLAYCTL_PAUSE | KIRKWOOD_PLAYCTL_I2S_MUTE;
262982b604bSRussell King 		writel(ctl, priv->io + KIRKWOOD_PLAYCTL);
263f9b95980Sapatard@mandriva.com 
264f9b95980Sapatard@mandriva.com 		value = readl(priv->io + KIRKWOOD_INT_MASK);
265f9b95980Sapatard@mandriva.com 		value &= ~KIRKWOOD_INT_CAUSE_PLAY_BYTES;
266f9b95980Sapatard@mandriva.com 		writel(value, priv->io + KIRKWOOD_INT_MASK);
267f9b95980Sapatard@mandriva.com 
268f9b95980Sapatard@mandriva.com 		/* disable all playbacks */
269982b604bSRussell King 		ctl &= ~(KIRKWOOD_PLAYCTL_I2S_EN | KIRKWOOD_PLAYCTL_SPDIF_EN);
270982b604bSRussell King 		writel(ctl, priv->io + KIRKWOOD_PLAYCTL);
271f9b95980Sapatard@mandriva.com 		break;
272f9b95980Sapatard@mandriva.com 
273f9b95980Sapatard@mandriva.com 	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
274f9b95980Sapatard@mandriva.com 	case SNDRV_PCM_TRIGGER_SUSPEND:
275982b604bSRussell King 		ctl |= KIRKWOOD_PLAYCTL_PAUSE | KIRKWOOD_PLAYCTL_I2S_MUTE;
276982b604bSRussell King 		writel(ctl, priv->io + KIRKWOOD_PLAYCTL);
277f9b95980Sapatard@mandriva.com 		break;
278f9b95980Sapatard@mandriva.com 
279f9b95980Sapatard@mandriva.com 	case SNDRV_PCM_TRIGGER_RESUME:
280f9b95980Sapatard@mandriva.com 	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
281982b604bSRussell King 		ctl &= ~(KIRKWOOD_PLAYCTL_PAUSE | KIRKWOOD_PLAYCTL_I2S_MUTE);
282982b604bSRussell King 		writel(ctl, priv->io + KIRKWOOD_PLAYCTL);
283f9b95980Sapatard@mandriva.com 		break;
284f9b95980Sapatard@mandriva.com 
285f9b95980Sapatard@mandriva.com 	default:
286f9b95980Sapatard@mandriva.com 		return -EINVAL;
287f9b95980Sapatard@mandriva.com 	}
288f9b95980Sapatard@mandriva.com 
289f9b95980Sapatard@mandriva.com 	return 0;
290f9b95980Sapatard@mandriva.com }
291f9b95980Sapatard@mandriva.com 
292f9b95980Sapatard@mandriva.com static int kirkwood_i2s_rec_trigger(struct snd_pcm_substream *substream,
293f9b95980Sapatard@mandriva.com 				int cmd, struct snd_soc_dai *dai)
294f9b95980Sapatard@mandriva.com {
295f0fba2adSLiam Girdwood 	struct kirkwood_dma_data *priv = snd_soc_dai_get_drvdata(dai);
296d8d11ba5SRussell King 	uint32_t ctl, value;
297f9b95980Sapatard@mandriva.com 
298f9b95980Sapatard@mandriva.com 	value = readl(priv->io + KIRKWOOD_RECCTL);
299f9b95980Sapatard@mandriva.com 
300f9b95980Sapatard@mandriva.com 	switch (cmd) {
301f9b95980Sapatard@mandriva.com 	case SNDRV_PCM_TRIGGER_START:
302d8d11ba5SRussell King 		/* configure */
303d8d11ba5SRussell King 		ctl = priv->ctl_rec;
304d8d11ba5SRussell King 		value = ctl & ~KIRKWOOD_RECCTL_I2S_EN;
305d8d11ba5SRussell King 		writel(value, priv->io + KIRKWOOD_RECCTL);
306d8d11ba5SRussell King 
307d8d11ba5SRussell King 		/* enable interrupts */
308f9b95980Sapatard@mandriva.com 		value = readl(priv->io + KIRKWOOD_INT_MASK);
309f9b95980Sapatard@mandriva.com 		value |= KIRKWOOD_INT_CAUSE_REC_BYTES;
310f9b95980Sapatard@mandriva.com 		writel(value, priv->io + KIRKWOOD_INT_MASK);
311f9b95980Sapatard@mandriva.com 
312d8d11ba5SRussell King 		/* enable record */
313d8d11ba5SRussell King 		writel(ctl, priv->io + KIRKWOOD_RECCTL);
314f9b95980Sapatard@mandriva.com 		break;
315f9b95980Sapatard@mandriva.com 
316f9b95980Sapatard@mandriva.com 	case SNDRV_PCM_TRIGGER_STOP:
317f9b95980Sapatard@mandriva.com 		/* stop audio, disable interrupts */
318f9b95980Sapatard@mandriva.com 		value = readl(priv->io + KIRKWOOD_RECCTL);
319b424ec95Sarnaud.patard@rtp-net.org 		value |= KIRKWOOD_RECCTL_PAUSE | KIRKWOOD_RECCTL_MUTE;
320f9b95980Sapatard@mandriva.com 		writel(value, priv->io + KIRKWOOD_RECCTL);
321f9b95980Sapatard@mandriva.com 
322f9b95980Sapatard@mandriva.com 		value = readl(priv->io + KIRKWOOD_INT_MASK);
323f9b95980Sapatard@mandriva.com 		value &= ~KIRKWOOD_INT_CAUSE_REC_BYTES;
324f9b95980Sapatard@mandriva.com 		writel(value, priv->io + KIRKWOOD_INT_MASK);
325f9b95980Sapatard@mandriva.com 
326f9b95980Sapatard@mandriva.com 		/* disable all records */
327f9b95980Sapatard@mandriva.com 		value = readl(priv->io + KIRKWOOD_RECCTL);
328f9b95980Sapatard@mandriva.com 		value &= ~(KIRKWOOD_RECCTL_I2S_EN | KIRKWOOD_RECCTL_SPDIF_EN);
329f9b95980Sapatard@mandriva.com 		writel(value, priv->io + KIRKWOOD_RECCTL);
330f9b95980Sapatard@mandriva.com 		break;
331f9b95980Sapatard@mandriva.com 
332f9b95980Sapatard@mandriva.com 	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
333f9b95980Sapatard@mandriva.com 	case SNDRV_PCM_TRIGGER_SUSPEND:
334f9b95980Sapatard@mandriva.com 		value = readl(priv->io + KIRKWOOD_RECCTL);
335b424ec95Sarnaud.patard@rtp-net.org 		value |= KIRKWOOD_RECCTL_PAUSE | KIRKWOOD_RECCTL_MUTE;
336f9b95980Sapatard@mandriva.com 		writel(value, priv->io + KIRKWOOD_RECCTL);
337f9b95980Sapatard@mandriva.com 		break;
338f9b95980Sapatard@mandriva.com 
339f9b95980Sapatard@mandriva.com 	case SNDRV_PCM_TRIGGER_RESUME:
340f9b95980Sapatard@mandriva.com 	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
341f9b95980Sapatard@mandriva.com 		value = readl(priv->io + KIRKWOOD_RECCTL);
342b424ec95Sarnaud.patard@rtp-net.org 		value &= ~(KIRKWOOD_RECCTL_PAUSE | KIRKWOOD_RECCTL_MUTE);
343f9b95980Sapatard@mandriva.com 		writel(value, priv->io + KIRKWOOD_RECCTL);
344f9b95980Sapatard@mandriva.com 		break;
345f9b95980Sapatard@mandriva.com 
346f9b95980Sapatard@mandriva.com 	default:
347f9b95980Sapatard@mandriva.com 		return -EINVAL;
348f9b95980Sapatard@mandriva.com 	}
349f9b95980Sapatard@mandriva.com 
350f9b95980Sapatard@mandriva.com 	return 0;
351f9b95980Sapatard@mandriva.com }
352f9b95980Sapatard@mandriva.com 
353f9b95980Sapatard@mandriva.com static int kirkwood_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
354f9b95980Sapatard@mandriva.com 			       struct snd_soc_dai *dai)
355f9b95980Sapatard@mandriva.com {
356f9b95980Sapatard@mandriva.com 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
357f9b95980Sapatard@mandriva.com 		return kirkwood_i2s_play_trigger(substream, cmd, dai);
358f9b95980Sapatard@mandriva.com 	else
359f9b95980Sapatard@mandriva.com 		return kirkwood_i2s_rec_trigger(substream, cmd, dai);
360f9b95980Sapatard@mandriva.com 
361f9b95980Sapatard@mandriva.com 	return 0;
362f9b95980Sapatard@mandriva.com }
363f9b95980Sapatard@mandriva.com 
364f0fba2adSLiam Girdwood static int kirkwood_i2s_probe(struct snd_soc_dai *dai)
365f9b95980Sapatard@mandriva.com {
366f0fba2adSLiam Girdwood 	struct kirkwood_dma_data *priv = snd_soc_dai_get_drvdata(dai);
367f9b95980Sapatard@mandriva.com 	unsigned long value;
368f9b95980Sapatard@mandriva.com 	unsigned int reg_data;
369f9b95980Sapatard@mandriva.com 
370f9b95980Sapatard@mandriva.com 	/* put system in a "safe" state : */
371f9b95980Sapatard@mandriva.com 	/* disable audio interrupts */
372f9b95980Sapatard@mandriva.com 	writel(0xffffffff, priv->io + KIRKWOOD_INT_CAUSE);
373f9b95980Sapatard@mandriva.com 	writel(0, priv->io + KIRKWOOD_INT_MASK);
374f9b95980Sapatard@mandriva.com 
375f9b95980Sapatard@mandriva.com 	reg_data = readl(priv->io + 0x1200);
376f9b95980Sapatard@mandriva.com 	reg_data &= (~(0x333FF8));
377f9b95980Sapatard@mandriva.com 	reg_data |= 0x111D18;
378f9b95980Sapatard@mandriva.com 	writel(reg_data, priv->io + 0x1200);
379f9b95980Sapatard@mandriva.com 
380f9b95980Sapatard@mandriva.com 	msleep(500);
381f9b95980Sapatard@mandriva.com 
382f9b95980Sapatard@mandriva.com 	reg_data = readl(priv->io + 0x1200);
383f9b95980Sapatard@mandriva.com 	reg_data &= (~(0x333FF8));
384f9b95980Sapatard@mandriva.com 	reg_data |= 0x111D18;
385f9b95980Sapatard@mandriva.com 	writel(reg_data, priv->io + 0x1200);
386f9b95980Sapatard@mandriva.com 
387f9b95980Sapatard@mandriva.com 	/* disable playback/record */
388f9b95980Sapatard@mandriva.com 	value = readl(priv->io + KIRKWOOD_PLAYCTL);
389f9b95980Sapatard@mandriva.com 	value &= ~(KIRKWOOD_PLAYCTL_I2S_EN|KIRKWOOD_PLAYCTL_SPDIF_EN);
390f9b95980Sapatard@mandriva.com 	writel(value, priv->io + KIRKWOOD_PLAYCTL);
391f9b95980Sapatard@mandriva.com 
392f9b95980Sapatard@mandriva.com 	value = readl(priv->io + KIRKWOOD_RECCTL);
393f9b95980Sapatard@mandriva.com 	value &= ~(KIRKWOOD_RECCTL_I2S_EN | KIRKWOOD_RECCTL_SPDIF_EN);
394f9b95980Sapatard@mandriva.com 	writel(value, priv->io + KIRKWOOD_RECCTL);
395f9b95980Sapatard@mandriva.com 
396f9b95980Sapatard@mandriva.com 	return 0;
397f9b95980Sapatard@mandriva.com 
398f9b95980Sapatard@mandriva.com }
399f9b95980Sapatard@mandriva.com 
40085e7652dSLars-Peter Clausen static const struct snd_soc_dai_ops kirkwood_i2s_dai_ops = {
401f0fba2adSLiam Girdwood 	.startup	= kirkwood_i2s_startup,
402f9b95980Sapatard@mandriva.com 	.trigger	= kirkwood_i2s_trigger,
403f9b95980Sapatard@mandriva.com 	.hw_params      = kirkwood_i2s_hw_params,
404f9b95980Sapatard@mandriva.com 	.set_fmt        = kirkwood_i2s_set_fmt,
405f9b95980Sapatard@mandriva.com };
406f9b95980Sapatard@mandriva.com 
407f9b95980Sapatard@mandriva.com 
408f0fba2adSLiam Girdwood static struct snd_soc_dai_driver kirkwood_i2s_dai = {
409f9b95980Sapatard@mandriva.com 	.probe = kirkwood_i2s_probe,
410f9b95980Sapatard@mandriva.com 	.playback = {
411f9b95980Sapatard@mandriva.com 		.channels_min = 1,
412f9b95980Sapatard@mandriva.com 		.channels_max = 2,
4139e12cbd9SMark Brown 		.rates = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |
4149e12cbd9SMark Brown 				SNDRV_PCM_RATE_96000,
415363589bfSRussell King 		.formats = KIRKWOOD_I2S_FORMATS,
416363589bfSRussell King 	},
417f9b95980Sapatard@mandriva.com 	.capture = {
418f9b95980Sapatard@mandriva.com 		.channels_min = 1,
419f9b95980Sapatard@mandriva.com 		.channels_max = 2,
4209e12cbd9SMark Brown 		.rates = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |
4219e12cbd9SMark Brown 				SNDRV_PCM_RATE_96000,
422363589bfSRussell King 		.formats = KIRKWOOD_I2S_FORMATS,
423363589bfSRussell King 	},
424363589bfSRussell King 	.ops = &kirkwood_i2s_dai_ops,
425363589bfSRussell King };
426363589bfSRussell King 
427363589bfSRussell King static struct snd_soc_dai_driver kirkwood_i2s_dai_extclk = {
428363589bfSRussell King 	.probe = kirkwood_i2s_probe,
429363589bfSRussell King 	.playback = {
430363589bfSRussell King 		.channels_min = 1,
431363589bfSRussell King 		.channels_max = 2,
432363589bfSRussell King 		.rates = SNDRV_PCM_RATE_8000_192000 |
433363589bfSRussell King 			 SNDRV_PCM_RATE_CONTINUOUS |
434363589bfSRussell King 			 SNDRV_PCM_RATE_KNOT,
435363589bfSRussell King 		.formats = KIRKWOOD_I2S_FORMATS,
436363589bfSRussell King 	},
437363589bfSRussell King 	.capture = {
438363589bfSRussell King 		.channels_min = 1,
439363589bfSRussell King 		.channels_max = 2,
440363589bfSRussell King 		.rates = SNDRV_PCM_RATE_8000_192000 |
441363589bfSRussell King 			 SNDRV_PCM_RATE_CONTINUOUS |
442363589bfSRussell King 			 SNDRV_PCM_RATE_KNOT,
443363589bfSRussell King 		.formats = KIRKWOOD_I2S_FORMATS,
444363589bfSRussell King 	},
445f9b95980Sapatard@mandriva.com 	.ops = &kirkwood_i2s_dai_ops,
446f9b95980Sapatard@mandriva.com };
447f9b95980Sapatard@mandriva.com 
44883d85f53SKuninori Morimoto static const struct snd_soc_component_driver kirkwood_i2s_component = {
44983d85f53SKuninori Morimoto 	.name		= DRV_NAME,
45083d85f53SKuninori Morimoto };
45183d85f53SKuninori Morimoto 
45234e15fbdSBill Pemberton static int kirkwood_i2s_dev_probe(struct platform_device *pdev)
453f9b95980Sapatard@mandriva.com {
454363589bfSRussell King 	struct kirkwood_asoc_platform_data *data = pdev->dev.platform_data;
455363589bfSRussell King 	struct snd_soc_dai_driver *soc_dai = &kirkwood_i2s_dai;
456f0fba2adSLiam Girdwood 	struct kirkwood_dma_data *priv;
457363589bfSRussell King 	struct resource *mem;
458f9b95980Sapatard@mandriva.com 	int err;
459f9b95980Sapatard@mandriva.com 
460dbc517bfSRussell King 	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
461f9b95980Sapatard@mandriva.com 	if (!priv) {
462f9b95980Sapatard@mandriva.com 		dev_err(&pdev->dev, "allocation failed\n");
463dbc517bfSRussell King 		return -ENOMEM;
464f9b95980Sapatard@mandriva.com 	}
465f0fba2adSLiam Girdwood 	dev_set_drvdata(&pdev->dev, priv);
466f9b95980Sapatard@mandriva.com 
467f9b95980Sapatard@mandriva.com 	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
468b25b5aa0SThierry Reding 	priv->io = devm_ioremap_resource(&pdev->dev, mem);
469b25b5aa0SThierry Reding 	if (IS_ERR(priv->io))
470b25b5aa0SThierry Reding 		return PTR_ERR(priv->io);
471f9b95980Sapatard@mandriva.com 
472f9b95980Sapatard@mandriva.com 	priv->irq = platform_get_irq(pdev, 0);
473f9b95980Sapatard@mandriva.com 	if (priv->irq <= 0) {
474f9b95980Sapatard@mandriva.com 		dev_err(&pdev->dev, "platform_get_irq failed\n");
475dbc517bfSRussell King 		return -ENXIO;
476f9b95980Sapatard@mandriva.com 	}
477f9b95980Sapatard@mandriva.com 
478d4d9820bSAxel Lin 	if (!data) {
479f9b95980Sapatard@mandriva.com 		dev_err(&pdev->dev, "no platform data ?!\n");
480dbc517bfSRussell King 		return -EINVAL;
481f9b95980Sapatard@mandriva.com 	}
482f9b95980Sapatard@mandriva.com 
483f9b95980Sapatard@mandriva.com 	priv->burst = data->burst;
484f9b95980Sapatard@mandriva.com 
485dbc517bfSRussell King 	priv->clk = devm_clk_get(&pdev->dev, NULL);
486e919c716SAndrew Lunn 	if (IS_ERR(priv->clk)) {
487e919c716SAndrew Lunn 		dev_err(&pdev->dev, "no clock\n");
488dbc517bfSRussell King 		return PTR_ERR(priv->clk);
489e919c716SAndrew Lunn 	}
490dbc517bfSRussell King 
491dbc517bfSRussell King 	err = clk_prepare_enable(priv->clk);
492dbc517bfSRussell King 	if (err < 0)
493dbc517bfSRussell King 		return err;
494e919c716SAndrew Lunn 
4954734dc96SMark Brown 	priv->extclk = devm_clk_get(&pdev->dev, "extclk");
496d8d11ba5SRussell King 	if (!IS_ERR(priv->extclk)) {
497d8d11ba5SRussell King 		if (priv->extclk == priv->clk) {
498*af64d734SRussell King 			devm_clk_put(&pdev->dev, priv->extclk);
499d8d11ba5SRussell King 			priv->extclk = ERR_PTR(-EINVAL);
500d8d11ba5SRussell King 		} else {
501d8d11ba5SRussell King 			dev_info(&pdev->dev, "found external clock\n");
502d8d11ba5SRussell King 			clk_prepare_enable(priv->extclk);
503d8d11ba5SRussell King 			soc_dai = &kirkwood_i2s_dai_extclk;
504d8d11ba5SRussell King 		}
505d8d11ba5SRussell King 	}
506d8d11ba5SRussell King 
507d8d11ba5SRussell King 	/* Some sensible defaults - this reflects the powerup values */
508d8d11ba5SRussell King 	priv->ctl_play = KIRKWOOD_PLAYCTL_SIZE_24;
509d8d11ba5SRussell King 	priv->ctl_rec = KIRKWOOD_RECCTL_SIZE_24;
510d8d11ba5SRussell King 
511d8d11ba5SRussell King 	/* Select the burst size */
512d8d11ba5SRussell King 	if (data->burst == 32) {
513d8d11ba5SRussell King 		priv->ctl_play |= KIRKWOOD_PLAYCTL_BURST_32;
514d8d11ba5SRussell King 		priv->ctl_rec |= KIRKWOOD_RECCTL_BURST_32;
515d8d11ba5SRussell King 	} else {
516d8d11ba5SRussell King 		priv->ctl_play |= KIRKWOOD_PLAYCTL_BURST_128;
517d8d11ba5SRussell King 		priv->ctl_rec |= KIRKWOOD_RECCTL_BURST_128;
518d8d11ba5SRussell King 	}
519d8d11ba5SRussell King 
52083d85f53SKuninori Morimoto 	err = snd_soc_register_component(&pdev->dev, &kirkwood_i2s_component,
52183d85f53SKuninori Morimoto 					 soc_dai, 1);
522baffab28SSimon Baatz 	if (!err)
523baffab28SSimon Baatz 		return 0;
52483d85f53SKuninori Morimoto 	dev_err(&pdev->dev, "snd_soc_register_component failed\n");
525baffab28SSimon Baatz 
5264734dc96SMark Brown 	if (!IS_ERR(priv->extclk))
527363589bfSRussell King 		clk_disable_unprepare(priv->extclk);
528baffab28SSimon Baatz 	clk_disable_unprepare(priv->clk);
529f9b95980Sapatard@mandriva.com 
530f9b95980Sapatard@mandriva.com 	return err;
531f9b95980Sapatard@mandriva.com }
532f9b95980Sapatard@mandriva.com 
53334e15fbdSBill Pemberton static int kirkwood_i2s_dev_remove(struct platform_device *pdev)
534f9b95980Sapatard@mandriva.com {
535f0fba2adSLiam Girdwood 	struct kirkwood_dma_data *priv = dev_get_drvdata(&pdev->dev);
536f0fba2adSLiam Girdwood 
53783d85f53SKuninori Morimoto 	snd_soc_unregister_component(&pdev->dev);
538e919c716SAndrew Lunn 
5394734dc96SMark Brown 	if (!IS_ERR(priv->extclk))
540363589bfSRussell King 		clk_disable_unprepare(priv->extclk);
541e919c716SAndrew Lunn 	clk_disable_unprepare(priv->clk);
542f0fba2adSLiam Girdwood 
543f9b95980Sapatard@mandriva.com 	return 0;
544f9b95980Sapatard@mandriva.com }
545f9b95980Sapatard@mandriva.com 
546f9b95980Sapatard@mandriva.com static struct platform_driver kirkwood_i2s_driver = {
547f9b95980Sapatard@mandriva.com 	.probe  = kirkwood_i2s_dev_probe,
54834e15fbdSBill Pemberton 	.remove = kirkwood_i2s_dev_remove,
549f9b95980Sapatard@mandriva.com 	.driver = {
550f9b95980Sapatard@mandriva.com 		.name = DRV_NAME,
551f9b95980Sapatard@mandriva.com 		.owner = THIS_MODULE,
552f9b95980Sapatard@mandriva.com 	},
553f9b95980Sapatard@mandriva.com };
554f9b95980Sapatard@mandriva.com 
55541b10225SAxel Lin module_platform_driver(kirkwood_i2s_driver);
556f9b95980Sapatard@mandriva.com 
557f9b95980Sapatard@mandriva.com /* Module information */
55869737897SArnaud Patard (Rtp) MODULE_AUTHOR("Arnaud Patard, <arnaud.patard@rtp-net.org>");
559f9b95980Sapatard@mandriva.com MODULE_DESCRIPTION("Kirkwood I2S SoC Interface");
560f9b95980Sapatard@mandriva.com MODULE_LICENSE("GPL");
561f9b95980Sapatard@mandriva.com MODULE_ALIAS("platform:kirkwood-i2s");
562