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