12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
2f9b95980Sapatard@mandriva.com /*
3f9b95980Sapatard@mandriva.com * kirkwood-i2s.c
4f9b95980Sapatard@mandriva.com *
5f9b95980Sapatard@mandriva.com * (c) 2010 Arnaud Patard <apatard@mandriva.com>
669737897SArnaud Patard (Rtp) * (c) 2010 Arnaud Patard <arnaud.patard@rtp-net.org>
7f9b95980Sapatard@mandriva.com */
8f9b95980Sapatard@mandriva.com
9f9b95980Sapatard@mandriva.com #include <linux/init.h>
10f9b95980Sapatard@mandriva.com #include <linux/module.h>
11f9b95980Sapatard@mandriva.com #include <linux/platform_device.h>
12f9b95980Sapatard@mandriva.com #include <linux/io.h>
13f9b95980Sapatard@mandriva.com #include <linux/slab.h>
14f9b95980Sapatard@mandriva.com #include <linux/mbus.h>
15f9b95980Sapatard@mandriva.com #include <linux/delay.h>
16e919c716SAndrew Lunn #include <linux/clk.h>
17f9b95980Sapatard@mandriva.com #include <sound/pcm.h>
18f9b95980Sapatard@mandriva.com #include <sound/pcm_params.h>
19f9b95980Sapatard@mandriva.com #include <sound/soc.h>
20c02cecb9SArnd Bergmann #include <linux/platform_data/asoc-kirkwood.h>
21eb632318SJean-Francois Moine #include <linux/of.h>
22eb632318SJean-Francois Moine
23f9b95980Sapatard@mandriva.com #include "kirkwood.h"
24f9b95980Sapatard@mandriva.com
25f9b95980Sapatard@mandriva.com #define KIRKWOOD_I2S_FORMATS \
26f9b95980Sapatard@mandriva.com (SNDRV_PCM_FMTBIT_S16_LE | \
27f9b95980Sapatard@mandriva.com SNDRV_PCM_FMTBIT_S24_LE | \
28f9b95980Sapatard@mandriva.com SNDRV_PCM_FMTBIT_S32_LE)
29f9b95980Sapatard@mandriva.com
301c195ddbSJean-Francois Moine #define KIRKWOOD_SPDIF_FORMATS \
311c195ddbSJean-Francois Moine (SNDRV_PCM_FMTBIT_S16_LE | \
321c195ddbSJean-Francois Moine SNDRV_PCM_FMTBIT_S24_LE)
331c195ddbSJean-Francois Moine
342adfc688SMarcin Wojtas /* These registers are relative to the second register region -
352adfc688SMarcin Wojtas * audio pll configuration.
362adfc688SMarcin Wojtas */
372adfc688SMarcin Wojtas #define A38X_PLL_CONF_REG0 0x0
382adfc688SMarcin Wojtas #define A38X_PLL_FB_CLK_DIV_OFFSET 10
392adfc688SMarcin Wojtas #define A38X_PLL_FB_CLK_DIV_MASK 0x7fc00
402adfc688SMarcin Wojtas #define A38X_PLL_CONF_REG1 0x4
412adfc688SMarcin Wojtas #define A38X_PLL_FREQ_OFFSET_MASK 0xffff
422adfc688SMarcin Wojtas #define A38X_PLL_FREQ_OFFSET_VALID BIT(16)
432adfc688SMarcin Wojtas #define A38X_PLL_SW_RESET BIT(31)
442adfc688SMarcin Wojtas #define A38X_PLL_CONF_REG2 0x8
452adfc688SMarcin Wojtas #define A38X_PLL_AUDIO_POSTDIV_MASK 0x7f
462adfc688SMarcin Wojtas
472adfc688SMarcin Wojtas /* Bit below belongs to SoC control register corresponding to the third
482adfc688SMarcin Wojtas * register region.
492adfc688SMarcin Wojtas */
502adfc688SMarcin Wojtas #define A38X_SPDIF_MODE_ENABLE BIT(27)
512adfc688SMarcin Wojtas
armada_38x_i2s_init_quirk(struct platform_device * pdev,struct kirkwood_dma_data * priv,struct snd_soc_dai_driver * dai_drv)522adfc688SMarcin Wojtas static int armada_38x_i2s_init_quirk(struct platform_device *pdev,
532adfc688SMarcin Wojtas struct kirkwood_dma_data *priv,
542adfc688SMarcin Wojtas struct snd_soc_dai_driver *dai_drv)
552adfc688SMarcin Wojtas {
562adfc688SMarcin Wojtas struct device_node *np = pdev->dev.of_node;
572adfc688SMarcin Wojtas u32 reg_val;
582adfc688SMarcin Wojtas int i;
592adfc688SMarcin Wojtas
602adfc688SMarcin Wojtas priv->pll_config = devm_platform_ioremap_resource_byname(pdev, "pll_regs");
612adfc688SMarcin Wojtas if (IS_ERR(priv->pll_config))
622adfc688SMarcin Wojtas return -ENOMEM;
632adfc688SMarcin Wojtas
642adfc688SMarcin Wojtas priv->soc_control = devm_platform_ioremap_resource_byname(pdev, "soc_ctrl");
652adfc688SMarcin Wojtas if (IS_ERR(priv->soc_control))
662adfc688SMarcin Wojtas return -ENOMEM;
672adfc688SMarcin Wojtas
682adfc688SMarcin Wojtas /* Select one of exceptive modes: I2S or S/PDIF */
692adfc688SMarcin Wojtas reg_val = readl(priv->soc_control);
702adfc688SMarcin Wojtas if (of_property_read_bool(np, "spdif-mode")) {
712adfc688SMarcin Wojtas reg_val |= A38X_SPDIF_MODE_ENABLE;
722adfc688SMarcin Wojtas dev_info(&pdev->dev, "using S/PDIF mode\n");
732adfc688SMarcin Wojtas } else {
742adfc688SMarcin Wojtas reg_val &= ~A38X_SPDIF_MODE_ENABLE;
752adfc688SMarcin Wojtas dev_info(&pdev->dev, "using I2S mode\n");
762adfc688SMarcin Wojtas }
772adfc688SMarcin Wojtas writel(reg_val, priv->soc_control);
782adfc688SMarcin Wojtas
792adfc688SMarcin Wojtas /* Update available rates of mclk's fs */
802adfc688SMarcin Wojtas for (i = 0; i < 2; i++) {
812adfc688SMarcin Wojtas dai_drv[i].playback.rates |= SNDRV_PCM_RATE_192000;
822adfc688SMarcin Wojtas dai_drv[i].capture.rates |= SNDRV_PCM_RATE_192000;
832adfc688SMarcin Wojtas }
842adfc688SMarcin Wojtas
852adfc688SMarcin Wojtas return 0;
862adfc688SMarcin Wojtas }
872adfc688SMarcin Wojtas
armada_38x_set_pll(void __iomem * base,unsigned long rate)882adfc688SMarcin Wojtas static inline void armada_38x_set_pll(void __iomem *base, unsigned long rate)
892adfc688SMarcin Wojtas {
902adfc688SMarcin Wojtas u32 reg_val;
912adfc688SMarcin Wojtas u16 freq_offset = 0x22b0;
922adfc688SMarcin Wojtas u8 audio_postdiv, fb_clk_div = 0x1d;
932adfc688SMarcin Wojtas
942adfc688SMarcin Wojtas /* Set frequency offset value to not valid and enable PLL reset */
952adfc688SMarcin Wojtas reg_val = readl(base + A38X_PLL_CONF_REG1);
962adfc688SMarcin Wojtas reg_val &= ~A38X_PLL_FREQ_OFFSET_VALID;
972adfc688SMarcin Wojtas reg_val &= ~A38X_PLL_SW_RESET;
982adfc688SMarcin Wojtas writel(reg_val, base + A38X_PLL_CONF_REG1);
992adfc688SMarcin Wojtas
1002adfc688SMarcin Wojtas udelay(1);
1012adfc688SMarcin Wojtas
1022adfc688SMarcin Wojtas /* Update PLL parameters */
1032adfc688SMarcin Wojtas switch (rate) {
1042adfc688SMarcin Wojtas default:
1052adfc688SMarcin Wojtas case 44100:
1062adfc688SMarcin Wojtas freq_offset = 0x735;
1072adfc688SMarcin Wojtas fb_clk_div = 0x1b;
1082adfc688SMarcin Wojtas audio_postdiv = 0xc;
1092adfc688SMarcin Wojtas break;
1102adfc688SMarcin Wojtas case 48000:
1112adfc688SMarcin Wojtas audio_postdiv = 0xc;
1122adfc688SMarcin Wojtas break;
1132adfc688SMarcin Wojtas case 96000:
1142adfc688SMarcin Wojtas audio_postdiv = 0x6;
1152adfc688SMarcin Wojtas break;
1162adfc688SMarcin Wojtas case 192000:
1172adfc688SMarcin Wojtas audio_postdiv = 0x3;
1182adfc688SMarcin Wojtas break;
1192adfc688SMarcin Wojtas }
1202adfc688SMarcin Wojtas
1212adfc688SMarcin Wojtas reg_val = readl(base + A38X_PLL_CONF_REG0);
1222adfc688SMarcin Wojtas reg_val &= ~A38X_PLL_FB_CLK_DIV_MASK;
1232adfc688SMarcin Wojtas reg_val |= (fb_clk_div << A38X_PLL_FB_CLK_DIV_OFFSET);
1242adfc688SMarcin Wojtas writel(reg_val, base + A38X_PLL_CONF_REG0);
1252adfc688SMarcin Wojtas
1262adfc688SMarcin Wojtas reg_val = readl(base + A38X_PLL_CONF_REG2);
1272adfc688SMarcin Wojtas reg_val &= ~A38X_PLL_AUDIO_POSTDIV_MASK;
1282adfc688SMarcin Wojtas reg_val |= audio_postdiv;
1292adfc688SMarcin Wojtas writel(reg_val, base + A38X_PLL_CONF_REG2);
1302adfc688SMarcin Wojtas
1312adfc688SMarcin Wojtas reg_val = readl(base + A38X_PLL_CONF_REG1);
1322adfc688SMarcin Wojtas reg_val &= ~A38X_PLL_FREQ_OFFSET_MASK;
1332adfc688SMarcin Wojtas reg_val |= freq_offset;
1342adfc688SMarcin Wojtas writel(reg_val, base + A38X_PLL_CONF_REG1);
1352adfc688SMarcin Wojtas
1362adfc688SMarcin Wojtas udelay(1);
1372adfc688SMarcin Wojtas
1382adfc688SMarcin Wojtas /* Disable reset */
1392adfc688SMarcin Wojtas reg_val |= A38X_PLL_SW_RESET;
1402adfc688SMarcin Wojtas writel(reg_val, base + A38X_PLL_CONF_REG1);
1412adfc688SMarcin Wojtas
1422adfc688SMarcin Wojtas /* Wait 50us for PLL to lock */
1432adfc688SMarcin Wojtas udelay(50);
1442adfc688SMarcin Wojtas
1452adfc688SMarcin Wojtas /* Restore frequency offset value validity */
1462adfc688SMarcin Wojtas reg_val |= A38X_PLL_FREQ_OFFSET_VALID;
1472adfc688SMarcin Wojtas writel(reg_val, base + A38X_PLL_CONF_REG1);
1482adfc688SMarcin Wojtas }
1492adfc688SMarcin Wojtas
kirkwood_i2s_set_fmt(struct snd_soc_dai * cpu_dai,unsigned int fmt)150f9b95980Sapatard@mandriva.com static int kirkwood_i2s_set_fmt(struct snd_soc_dai *cpu_dai,
151f9b95980Sapatard@mandriva.com unsigned int fmt)
152f9b95980Sapatard@mandriva.com {
153f0fba2adSLiam Girdwood struct kirkwood_dma_data *priv = snd_soc_dai_get_drvdata(cpu_dai);
154f9b95980Sapatard@mandriva.com unsigned long mask;
155f9b95980Sapatard@mandriva.com unsigned long value;
156f9b95980Sapatard@mandriva.com
157f9b95980Sapatard@mandriva.com switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
158f9b95980Sapatard@mandriva.com case SND_SOC_DAIFMT_RIGHT_J:
159f9b95980Sapatard@mandriva.com mask = KIRKWOOD_I2S_CTL_RJ;
160f9b95980Sapatard@mandriva.com break;
161f9b95980Sapatard@mandriva.com case SND_SOC_DAIFMT_LEFT_J:
162f9b95980Sapatard@mandriva.com mask = KIRKWOOD_I2S_CTL_LJ;
163f9b95980Sapatard@mandriva.com break;
164f9b95980Sapatard@mandriva.com case SND_SOC_DAIFMT_I2S:
165f9b95980Sapatard@mandriva.com mask = KIRKWOOD_I2S_CTL_I2S;
166f9b95980Sapatard@mandriva.com break;
167f9b95980Sapatard@mandriva.com default:
168f9b95980Sapatard@mandriva.com return -EINVAL;
169f9b95980Sapatard@mandriva.com }
170f9b95980Sapatard@mandriva.com
171f9b95980Sapatard@mandriva.com /*
172f9b95980Sapatard@mandriva.com * Set same format for playback and record
173f9b95980Sapatard@mandriva.com * This avoids some troubles.
174f9b95980Sapatard@mandriva.com */
175f9b95980Sapatard@mandriva.com value = readl(priv->io+KIRKWOOD_I2S_PLAYCTL);
176f9b95980Sapatard@mandriva.com value &= ~KIRKWOOD_I2S_CTL_JUST_MASK;
177f9b95980Sapatard@mandriva.com value |= mask;
178f9b95980Sapatard@mandriva.com writel(value, priv->io+KIRKWOOD_I2S_PLAYCTL);
179f9b95980Sapatard@mandriva.com
180f9b95980Sapatard@mandriva.com value = readl(priv->io+KIRKWOOD_I2S_RECCTL);
181f9b95980Sapatard@mandriva.com value &= ~KIRKWOOD_I2S_CTL_JUST_MASK;
182f9b95980Sapatard@mandriva.com value |= mask;
183f9b95980Sapatard@mandriva.com writel(value, priv->io+KIRKWOOD_I2S_RECCTL);
184f9b95980Sapatard@mandriva.com
185f9b95980Sapatard@mandriva.com return 0;
186f9b95980Sapatard@mandriva.com }
187f9b95980Sapatard@mandriva.com
kirkwood_set_dco(void __iomem * io,unsigned long rate)188f9b95980Sapatard@mandriva.com static inline void kirkwood_set_dco(void __iomem *io, unsigned long rate)
189f9b95980Sapatard@mandriva.com {
190f9b95980Sapatard@mandriva.com unsigned long value;
191f9b95980Sapatard@mandriva.com
192f9b95980Sapatard@mandriva.com value = KIRKWOOD_DCO_CTL_OFFSET_0;
193f9b95980Sapatard@mandriva.com switch (rate) {
194f9b95980Sapatard@mandriva.com default:
195f9b95980Sapatard@mandriva.com case 44100:
196f9b95980Sapatard@mandriva.com value |= KIRKWOOD_DCO_CTL_FREQ_11;
197f9b95980Sapatard@mandriva.com break;
198f9b95980Sapatard@mandriva.com case 48000:
199f9b95980Sapatard@mandriva.com value |= KIRKWOOD_DCO_CTL_FREQ_12;
200f9b95980Sapatard@mandriva.com break;
201f9b95980Sapatard@mandriva.com case 96000:
202f9b95980Sapatard@mandriva.com value |= KIRKWOOD_DCO_CTL_FREQ_24;
203f9b95980Sapatard@mandriva.com break;
204f9b95980Sapatard@mandriva.com }
205f9b95980Sapatard@mandriva.com writel(value, io + KIRKWOOD_DCO_CTL);
206f9b95980Sapatard@mandriva.com
207f9b95980Sapatard@mandriva.com /* wait for dco locked */
208f9b95980Sapatard@mandriva.com do {
209f9b95980Sapatard@mandriva.com cpu_relax();
210f9b95980Sapatard@mandriva.com value = readl(io + KIRKWOOD_DCO_SPCR_STATUS);
2112424d458SRussell King value &= KIRKWOOD_DCO_SPCR_STATUS_DCO_LOCK;
212f9b95980Sapatard@mandriva.com } while (value == 0);
213f9b95980Sapatard@mandriva.com }
214f9b95980Sapatard@mandriva.com
kirkwood_set_rate(struct snd_soc_dai * dai,struct kirkwood_dma_data * priv,unsigned long rate)215363589bfSRussell King static void kirkwood_set_rate(struct snd_soc_dai *dai,
216363589bfSRussell King struct kirkwood_dma_data *priv, unsigned long rate)
217363589bfSRussell King {
218363589bfSRussell King uint32_t clks_ctrl;
219363589bfSRussell King
2201f1b6579SJean-Francois Moine if (IS_ERR(priv->extclk)) {
2218a537f85SJean-Francois Moine /* use internal dco for the supported rates
2228a537f85SJean-Francois Moine * defined in kirkwood_i2s_dai */
223363589bfSRussell King dev_dbg(dai->dev, "%s: dco set rate = %lu\n",
224363589bfSRussell King __func__, rate);
2252adfc688SMarcin Wojtas if (priv->pll_config)
2262adfc688SMarcin Wojtas armada_38x_set_pll(priv->pll_config, rate);
2272adfc688SMarcin Wojtas else
228363589bfSRussell King kirkwood_set_dco(priv->io, rate);
229363589bfSRussell King
230363589bfSRussell King clks_ctrl = KIRKWOOD_MCLK_SOURCE_DCO;
2318a537f85SJean-Francois Moine } else {
2328a537f85SJean-Francois Moine /* use the external clock for the other rates
2338a537f85SJean-Francois Moine * defined in kirkwood_i2s_dai_extclk */
234363589bfSRussell King dev_dbg(dai->dev, "%s: extclk set rate = %lu -> %lu\n",
235363589bfSRussell King __func__, rate, 256 * rate);
236363589bfSRussell King clk_set_rate(priv->extclk, 256 * rate);
237363589bfSRussell King
238363589bfSRussell King clks_ctrl = KIRKWOOD_MCLK_SOURCE_EXTCLK;
239363589bfSRussell King }
240363589bfSRussell King writel(clks_ctrl, priv->io + KIRKWOOD_CLOCKS_CTRL);
241363589bfSRussell King }
242363589bfSRussell King
kirkwood_i2s_startup(struct snd_pcm_substream * substream,struct snd_soc_dai * dai)243f0fba2adSLiam Girdwood static int kirkwood_i2s_startup(struct snd_pcm_substream *substream,
244f0fba2adSLiam Girdwood struct snd_soc_dai *dai)
245f0fba2adSLiam Girdwood {
246f0fba2adSLiam Girdwood struct kirkwood_dma_data *priv = snd_soc_dai_get_drvdata(dai);
247f0fba2adSLiam Girdwood
248f0fba2adSLiam Girdwood snd_soc_dai_set_dma_data(dai, substream, priv);
249f0fba2adSLiam Girdwood return 0;
250f0fba2adSLiam Girdwood }
251f0fba2adSLiam Girdwood
kirkwood_i2s_hw_params(struct snd_pcm_substream * substream,struct snd_pcm_hw_params * params,struct snd_soc_dai * dai)252f9b95980Sapatard@mandriva.com static int kirkwood_i2s_hw_params(struct snd_pcm_substream *substream,
253f9b95980Sapatard@mandriva.com struct snd_pcm_hw_params *params,
254f9b95980Sapatard@mandriva.com struct snd_soc_dai *dai)
255f9b95980Sapatard@mandriva.com {
256f0fba2adSLiam Girdwood struct kirkwood_dma_data *priv = snd_soc_dai_get_drvdata(dai);
257d8d11ba5SRussell King uint32_t ctl_play, ctl_rec;
258d8d11ba5SRussell King unsigned int i2s_reg;
259d8d11ba5SRussell King unsigned long i2s_value;
260f9b95980Sapatard@mandriva.com
261f9b95980Sapatard@mandriva.com if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
262f9b95980Sapatard@mandriva.com i2s_reg = KIRKWOOD_I2S_PLAYCTL;
263f9b95980Sapatard@mandriva.com } else {
264f9b95980Sapatard@mandriva.com i2s_reg = KIRKWOOD_I2S_RECCTL;
265f9b95980Sapatard@mandriva.com }
266f9b95980Sapatard@mandriva.com
267363589bfSRussell King kirkwood_set_rate(dai, priv, params_rate(params));
268f9b95980Sapatard@mandriva.com
269f9b95980Sapatard@mandriva.com i2s_value = readl(priv->io+i2s_reg);
270f9b95980Sapatard@mandriva.com i2s_value &= ~KIRKWOOD_I2S_CTL_SIZE_MASK;
271f9b95980Sapatard@mandriva.com
272f9b95980Sapatard@mandriva.com /*
273f9b95980Sapatard@mandriva.com * Size settings in play/rec i2s control regs and play/rec control
274f9b95980Sapatard@mandriva.com * regs must be the same.
275f9b95980Sapatard@mandriva.com */
276f9b95980Sapatard@mandriva.com switch (params_format(params)) {
277f9b95980Sapatard@mandriva.com case SNDRV_PCM_FORMAT_S16_LE:
278f9b95980Sapatard@mandriva.com i2s_value |= KIRKWOOD_I2S_CTL_SIZE_16;
279d8d11ba5SRussell King ctl_play = KIRKWOOD_PLAYCTL_SIZE_16_C |
28075b9b65eSJean-Francois Moine KIRKWOOD_PLAYCTL_I2S_EN |
28175b9b65eSJean-Francois Moine KIRKWOOD_PLAYCTL_SPDIF_EN;
282d8d11ba5SRussell King ctl_rec = KIRKWOOD_RECCTL_SIZE_16_C |
28375b9b65eSJean-Francois Moine KIRKWOOD_RECCTL_I2S_EN |
28475b9b65eSJean-Francois Moine KIRKWOOD_RECCTL_SPDIF_EN;
285f9b95980Sapatard@mandriva.com break;
286f9b95980Sapatard@mandriva.com /*
287f9b95980Sapatard@mandriva.com * doesn't work... S20_3LE != kirkwood 20bit format ?
288f9b95980Sapatard@mandriva.com *
289f9b95980Sapatard@mandriva.com case SNDRV_PCM_FORMAT_S20_3LE:
290f9b95980Sapatard@mandriva.com i2s_value |= KIRKWOOD_I2S_CTL_SIZE_20;
291d8d11ba5SRussell King ctl_play = KIRKWOOD_PLAYCTL_SIZE_20 |
292d8d11ba5SRussell King KIRKWOOD_PLAYCTL_I2S_EN;
293d8d11ba5SRussell King ctl_rec = KIRKWOOD_RECCTL_SIZE_20 |
294d8d11ba5SRussell King KIRKWOOD_RECCTL_I2S_EN;
295f9b95980Sapatard@mandriva.com break;
296f9b95980Sapatard@mandriva.com */
297f9b95980Sapatard@mandriva.com case SNDRV_PCM_FORMAT_S24_LE:
298f9b95980Sapatard@mandriva.com i2s_value |= KIRKWOOD_I2S_CTL_SIZE_24;
299d8d11ba5SRussell King ctl_play = KIRKWOOD_PLAYCTL_SIZE_24 |
30075b9b65eSJean-Francois Moine KIRKWOOD_PLAYCTL_I2S_EN |
30175b9b65eSJean-Francois Moine KIRKWOOD_PLAYCTL_SPDIF_EN;
302d8d11ba5SRussell King ctl_rec = KIRKWOOD_RECCTL_SIZE_24 |
30375b9b65eSJean-Francois Moine KIRKWOOD_RECCTL_I2S_EN |
30475b9b65eSJean-Francois Moine KIRKWOOD_RECCTL_SPDIF_EN;
305f9b95980Sapatard@mandriva.com break;
306f9b95980Sapatard@mandriva.com case SNDRV_PCM_FORMAT_S32_LE:
307f9b95980Sapatard@mandriva.com i2s_value |= KIRKWOOD_I2S_CTL_SIZE_32;
308d8d11ba5SRussell King ctl_play = KIRKWOOD_PLAYCTL_SIZE_32 |
309d8d11ba5SRussell King KIRKWOOD_PLAYCTL_I2S_EN;
310d8d11ba5SRussell King ctl_rec = KIRKWOOD_RECCTL_SIZE_32 |
311d8d11ba5SRussell King KIRKWOOD_RECCTL_I2S_EN;
312f9b95980Sapatard@mandriva.com break;
313f9b95980Sapatard@mandriva.com default:
314f9b95980Sapatard@mandriva.com return -EINVAL;
315f9b95980Sapatard@mandriva.com }
316dfe4c936Sarnaud.patard@rtp-net.org
317dfe4c936Sarnaud.patard@rtp-net.org if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
318dfe4c936Sarnaud.patard@rtp-net.org if (params_channels(params) == 1)
319d8d11ba5SRussell King ctl_play |= KIRKWOOD_PLAYCTL_MONO_BOTH;
320dfe4c936Sarnaud.patard@rtp-net.org else
321d8d11ba5SRussell King ctl_play |= KIRKWOOD_PLAYCTL_MONO_OFF;
322d8d11ba5SRussell King
323d8d11ba5SRussell King priv->ctl_play &= ~(KIRKWOOD_PLAYCTL_MONO_MASK |
324db43b16fSRussell King KIRKWOOD_PLAYCTL_ENABLE_MASK |
325d8d11ba5SRussell King KIRKWOOD_PLAYCTL_SIZE_MASK);
326d8d11ba5SRussell King priv->ctl_play |= ctl_play;
327d8d11ba5SRussell King } else {
32867721906SRussell King priv->ctl_rec &= ~(KIRKWOOD_RECCTL_ENABLE_MASK |
32967721906SRussell King KIRKWOOD_RECCTL_SIZE_MASK);
330d8d11ba5SRussell King priv->ctl_rec |= ctl_rec;
331dfe4c936Sarnaud.patard@rtp-net.org }
332dfe4c936Sarnaud.patard@rtp-net.org
333f9b95980Sapatard@mandriva.com writel(i2s_value, priv->io+i2s_reg);
334f9b95980Sapatard@mandriva.com
335f9b95980Sapatard@mandriva.com return 0;
336f9b95980Sapatard@mandriva.com }
337f9b95980Sapatard@mandriva.com
kirkwood_i2s_play_mute(unsigned ctl)3382fbc3821SRussell King static unsigned kirkwood_i2s_play_mute(unsigned ctl)
3392fbc3821SRussell King {
3402fbc3821SRussell King if (!(ctl & KIRKWOOD_PLAYCTL_I2S_EN))
3412fbc3821SRussell King ctl |= KIRKWOOD_PLAYCTL_I2S_MUTE;
3422fbc3821SRussell King if (!(ctl & KIRKWOOD_PLAYCTL_SPDIF_EN))
3432fbc3821SRussell King ctl |= KIRKWOOD_PLAYCTL_SPDIF_MUTE;
3442fbc3821SRussell King return ctl;
3452fbc3821SRussell King }
3462fbc3821SRussell King
kirkwood_i2s_play_trigger(struct snd_pcm_substream * substream,int cmd,struct snd_soc_dai * dai)347f9b95980Sapatard@mandriva.com static int kirkwood_i2s_play_trigger(struct snd_pcm_substream *substream,
348f9b95980Sapatard@mandriva.com int cmd, struct snd_soc_dai *dai)
349f9b95980Sapatard@mandriva.com {
350920ec4e5SRussell King struct snd_pcm_runtime *runtime = substream->runtime;
351f0fba2adSLiam Girdwood struct kirkwood_dma_data *priv = snd_soc_dai_get_drvdata(dai);
352982b604bSRussell King uint32_t ctl, value;
353f9b95980Sapatard@mandriva.com
354982b604bSRussell King ctl = readl(priv->io + KIRKWOOD_PLAYCTL);
3554d2097e5SRussell King if ((ctl & KIRKWOOD_PLAYCTL_ENABLE_MASK) == 0) {
356982b604bSRussell King unsigned timeout = 5000;
357f9b95980Sapatard@mandriva.com /*
358982b604bSRussell King * The Armada510 spec says that if we enter pause mode, the
359982b604bSRussell King * busy bit must be read back as clear _twice_. Make sure
360982b604bSRussell King * we respect that otherwise we get DMA underruns.
361f9b95980Sapatard@mandriva.com */
362982b604bSRussell King do {
363982b604bSRussell King value = ctl;
364982b604bSRussell King ctl = readl(priv->io + KIRKWOOD_PLAYCTL);
365982b604bSRussell King if (!((ctl | value) & KIRKWOOD_PLAYCTL_PLAY_BUSY))
366982b604bSRussell King break;
367982b604bSRussell King udelay(1);
368982b604bSRussell King } while (timeout--);
369982b604bSRussell King
370982b604bSRussell King if ((ctl | value) & KIRKWOOD_PLAYCTL_PLAY_BUSY)
371982b604bSRussell King dev_notice(dai->dev, "timed out waiting for busy to deassert: %08x\n",
372982b604bSRussell King ctl);
373982b604bSRussell King }
374f9b95980Sapatard@mandriva.com
3754f6f1478SJean-Francois Moine switch (cmd) {
3764f6f1478SJean-Francois Moine case SNDRV_PCM_TRIGGER_START:
3774f6f1478SJean-Francois Moine /* configure */
3784f6f1478SJean-Francois Moine ctl = priv->ctl_play;
37975b9b65eSJean-Francois Moine if (dai->id == 0)
38075b9b65eSJean-Francois Moine ctl &= ~KIRKWOOD_PLAYCTL_SPDIF_EN; /* i2s */
38175b9b65eSJean-Francois Moine else
38275b9b65eSJean-Francois Moine ctl &= ~KIRKWOOD_PLAYCTL_I2S_EN; /* spdif */
3832fbc3821SRussell King ctl = kirkwood_i2s_play_mute(ctl);
384db43b16fSRussell King value = ctl & ~KIRKWOOD_PLAYCTL_ENABLE_MASK;
385d8d11ba5SRussell King writel(value, priv->io + KIRKWOOD_PLAYCTL);
386d8d11ba5SRussell King
387d8d11ba5SRussell King /* enable interrupts */
388920ec4e5SRussell King if (!runtime->no_period_wakeup) {
389f9b95980Sapatard@mandriva.com value = readl(priv->io + KIRKWOOD_INT_MASK);
390f9b95980Sapatard@mandriva.com value |= KIRKWOOD_INT_CAUSE_PLAY_BYTES;
391f9b95980Sapatard@mandriva.com writel(value, priv->io + KIRKWOOD_INT_MASK);
392920ec4e5SRussell King }
393f9b95980Sapatard@mandriva.com
394d8d11ba5SRussell King /* enable playback */
395982b604bSRussell King writel(ctl, priv->io + KIRKWOOD_PLAYCTL);
396f9b95980Sapatard@mandriva.com break;
397f9b95980Sapatard@mandriva.com
398f9b95980Sapatard@mandriva.com case SNDRV_PCM_TRIGGER_STOP:
399f9b95980Sapatard@mandriva.com /* stop audio, disable interrupts */
40075b9b65eSJean-Francois Moine ctl |= KIRKWOOD_PLAYCTL_PAUSE | KIRKWOOD_PLAYCTL_I2S_MUTE |
40175b9b65eSJean-Francois Moine KIRKWOOD_PLAYCTL_SPDIF_MUTE;
402982b604bSRussell King writel(ctl, priv->io + KIRKWOOD_PLAYCTL);
403f9b95980Sapatard@mandriva.com
404f9b95980Sapatard@mandriva.com value = readl(priv->io + KIRKWOOD_INT_MASK);
405f9b95980Sapatard@mandriva.com value &= ~KIRKWOOD_INT_CAUSE_PLAY_BYTES;
406f9b95980Sapatard@mandriva.com writel(value, priv->io + KIRKWOOD_INT_MASK);
407f9b95980Sapatard@mandriva.com
408f9b95980Sapatard@mandriva.com /* disable all playbacks */
409db43b16fSRussell King ctl &= ~KIRKWOOD_PLAYCTL_ENABLE_MASK;
410982b604bSRussell King writel(ctl, priv->io + KIRKWOOD_PLAYCTL);
411f9b95980Sapatard@mandriva.com break;
412f9b95980Sapatard@mandriva.com
413f9b95980Sapatard@mandriva.com case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
414f9b95980Sapatard@mandriva.com case SNDRV_PCM_TRIGGER_SUSPEND:
41575b9b65eSJean-Francois Moine ctl |= KIRKWOOD_PLAYCTL_PAUSE | KIRKWOOD_PLAYCTL_I2S_MUTE |
41675b9b65eSJean-Francois Moine KIRKWOOD_PLAYCTL_SPDIF_MUTE;
417982b604bSRussell King writel(ctl, priv->io + KIRKWOOD_PLAYCTL);
418f9b95980Sapatard@mandriva.com break;
419f9b95980Sapatard@mandriva.com
420f9b95980Sapatard@mandriva.com case SNDRV_PCM_TRIGGER_RESUME:
421f9b95980Sapatard@mandriva.com case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
42275b9b65eSJean-Francois Moine ctl &= ~(KIRKWOOD_PLAYCTL_PAUSE | KIRKWOOD_PLAYCTL_I2S_MUTE |
42375b9b65eSJean-Francois Moine KIRKWOOD_PLAYCTL_SPDIF_MUTE);
4242fbc3821SRussell King ctl = kirkwood_i2s_play_mute(ctl);
425982b604bSRussell King writel(ctl, priv->io + KIRKWOOD_PLAYCTL);
426f9b95980Sapatard@mandriva.com break;
427f9b95980Sapatard@mandriva.com
428f9b95980Sapatard@mandriva.com default:
429f9b95980Sapatard@mandriva.com return -EINVAL;
430f9b95980Sapatard@mandriva.com }
431f9b95980Sapatard@mandriva.com
432f9b95980Sapatard@mandriva.com return 0;
433f9b95980Sapatard@mandriva.com }
434f9b95980Sapatard@mandriva.com
kirkwood_i2s_rec_trigger(struct snd_pcm_substream * substream,int cmd,struct snd_soc_dai * dai)435f9b95980Sapatard@mandriva.com static int kirkwood_i2s_rec_trigger(struct snd_pcm_substream *substream,
436f9b95980Sapatard@mandriva.com int cmd, struct snd_soc_dai *dai)
437f9b95980Sapatard@mandriva.com {
438f0fba2adSLiam Girdwood struct kirkwood_dma_data *priv = snd_soc_dai_get_drvdata(dai);
439d8d11ba5SRussell King uint32_t ctl, value;
440f9b95980Sapatard@mandriva.com
441f9b95980Sapatard@mandriva.com value = readl(priv->io + KIRKWOOD_RECCTL);
442f9b95980Sapatard@mandriva.com
443f9b95980Sapatard@mandriva.com switch (cmd) {
444f9b95980Sapatard@mandriva.com case SNDRV_PCM_TRIGGER_START:
445d8d11ba5SRussell King /* configure */
446d8d11ba5SRussell King ctl = priv->ctl_rec;
44775b9b65eSJean-Francois Moine if (dai->id == 0)
44875b9b65eSJean-Francois Moine ctl &= ~KIRKWOOD_RECCTL_SPDIF_EN; /* i2s */
44975b9b65eSJean-Francois Moine else
45075b9b65eSJean-Francois Moine ctl &= ~KIRKWOOD_RECCTL_I2S_EN; /* spdif */
45175b9b65eSJean-Francois Moine
45252b896cfSRussell King value = ctl & ~KIRKWOOD_RECCTL_ENABLE_MASK;
453d8d11ba5SRussell King writel(value, priv->io + KIRKWOOD_RECCTL);
454d8d11ba5SRussell King
455d8d11ba5SRussell King /* enable interrupts */
456f9b95980Sapatard@mandriva.com value = readl(priv->io + KIRKWOOD_INT_MASK);
457f9b95980Sapatard@mandriva.com value |= KIRKWOOD_INT_CAUSE_REC_BYTES;
458f9b95980Sapatard@mandriva.com writel(value, priv->io + KIRKWOOD_INT_MASK);
459f9b95980Sapatard@mandriva.com
460d8d11ba5SRussell King /* enable record */
461d8d11ba5SRussell King writel(ctl, priv->io + KIRKWOOD_RECCTL);
462f9b95980Sapatard@mandriva.com break;
463f9b95980Sapatard@mandriva.com
464f9b95980Sapatard@mandriva.com case SNDRV_PCM_TRIGGER_STOP:
465f9b95980Sapatard@mandriva.com /* stop audio, disable interrupts */
466f9b95980Sapatard@mandriva.com value = readl(priv->io + KIRKWOOD_RECCTL);
467b424ec95Sarnaud.patard@rtp-net.org value |= KIRKWOOD_RECCTL_PAUSE | KIRKWOOD_RECCTL_MUTE;
468f9b95980Sapatard@mandriva.com writel(value, priv->io + KIRKWOOD_RECCTL);
469f9b95980Sapatard@mandriva.com
470f9b95980Sapatard@mandriva.com value = readl(priv->io + KIRKWOOD_INT_MASK);
471f9b95980Sapatard@mandriva.com value &= ~KIRKWOOD_INT_CAUSE_REC_BYTES;
472f9b95980Sapatard@mandriva.com writel(value, priv->io + KIRKWOOD_INT_MASK);
473f9b95980Sapatard@mandriva.com
474f9b95980Sapatard@mandriva.com /* disable all records */
475f9b95980Sapatard@mandriva.com value = readl(priv->io + KIRKWOOD_RECCTL);
47652b896cfSRussell King value &= ~KIRKWOOD_RECCTL_ENABLE_MASK;
477f9b95980Sapatard@mandriva.com writel(value, priv->io + KIRKWOOD_RECCTL);
478f9b95980Sapatard@mandriva.com break;
479f9b95980Sapatard@mandriva.com
480f9b95980Sapatard@mandriva.com case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
481f9b95980Sapatard@mandriva.com case SNDRV_PCM_TRIGGER_SUSPEND:
482f9b95980Sapatard@mandriva.com value = readl(priv->io + KIRKWOOD_RECCTL);
483b424ec95Sarnaud.patard@rtp-net.org value |= KIRKWOOD_RECCTL_PAUSE | KIRKWOOD_RECCTL_MUTE;
484f9b95980Sapatard@mandriva.com writel(value, priv->io + KIRKWOOD_RECCTL);
485f9b95980Sapatard@mandriva.com break;
486f9b95980Sapatard@mandriva.com
487f9b95980Sapatard@mandriva.com case SNDRV_PCM_TRIGGER_RESUME:
488f9b95980Sapatard@mandriva.com case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
489f9b95980Sapatard@mandriva.com value = readl(priv->io + KIRKWOOD_RECCTL);
490b424ec95Sarnaud.patard@rtp-net.org value &= ~(KIRKWOOD_RECCTL_PAUSE | KIRKWOOD_RECCTL_MUTE);
491f9b95980Sapatard@mandriva.com writel(value, priv->io + KIRKWOOD_RECCTL);
492f9b95980Sapatard@mandriva.com break;
493f9b95980Sapatard@mandriva.com
494f9b95980Sapatard@mandriva.com default:
495f9b95980Sapatard@mandriva.com return -EINVAL;
496f9b95980Sapatard@mandriva.com }
497f9b95980Sapatard@mandriva.com
498f9b95980Sapatard@mandriva.com return 0;
499f9b95980Sapatard@mandriva.com }
500f9b95980Sapatard@mandriva.com
kirkwood_i2s_trigger(struct snd_pcm_substream * substream,int cmd,struct snd_soc_dai * dai)501f9b95980Sapatard@mandriva.com static int kirkwood_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
502f9b95980Sapatard@mandriva.com struct snd_soc_dai *dai)
503f9b95980Sapatard@mandriva.com {
504f9b95980Sapatard@mandriva.com if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
505f9b95980Sapatard@mandriva.com return kirkwood_i2s_play_trigger(substream, cmd, dai);
506f9b95980Sapatard@mandriva.com else
507f9b95980Sapatard@mandriva.com return kirkwood_i2s_rec_trigger(substream, cmd, dai);
508f9b95980Sapatard@mandriva.com
509f9b95980Sapatard@mandriva.com return 0;
510f9b95980Sapatard@mandriva.com }
511f9b95980Sapatard@mandriva.com
kirkwood_i2s_init(struct kirkwood_dma_data * priv)51275b9b65eSJean-Francois Moine static int kirkwood_i2s_init(struct kirkwood_dma_data *priv)
513f9b95980Sapatard@mandriva.com {
514f9b95980Sapatard@mandriva.com unsigned long value;
515f9b95980Sapatard@mandriva.com unsigned int reg_data;
516f9b95980Sapatard@mandriva.com
517f9b95980Sapatard@mandriva.com /* put system in a "safe" state : */
518f9b95980Sapatard@mandriva.com /* disable audio interrupts */
519f9b95980Sapatard@mandriva.com writel(0xffffffff, priv->io + KIRKWOOD_INT_CAUSE);
520f9b95980Sapatard@mandriva.com writel(0, priv->io + KIRKWOOD_INT_MASK);
521f9b95980Sapatard@mandriva.com
522f9b95980Sapatard@mandriva.com reg_data = readl(priv->io + 0x1200);
523f9b95980Sapatard@mandriva.com reg_data &= (~(0x333FF8));
524f9b95980Sapatard@mandriva.com reg_data |= 0x111D18;
525f9b95980Sapatard@mandriva.com writel(reg_data, priv->io + 0x1200);
526f9b95980Sapatard@mandriva.com
527f9b95980Sapatard@mandriva.com msleep(500);
528f9b95980Sapatard@mandriva.com
529f9b95980Sapatard@mandriva.com reg_data = readl(priv->io + 0x1200);
530f9b95980Sapatard@mandriva.com reg_data &= (~(0x333FF8));
531f9b95980Sapatard@mandriva.com reg_data |= 0x111D18;
532f9b95980Sapatard@mandriva.com writel(reg_data, priv->io + 0x1200);
533f9b95980Sapatard@mandriva.com
534f9b95980Sapatard@mandriva.com /* disable playback/record */
535f9b95980Sapatard@mandriva.com value = readl(priv->io + KIRKWOOD_PLAYCTL);
536db43b16fSRussell King value &= ~KIRKWOOD_PLAYCTL_ENABLE_MASK;
537f9b95980Sapatard@mandriva.com writel(value, priv->io + KIRKWOOD_PLAYCTL);
538f9b95980Sapatard@mandriva.com
539f9b95980Sapatard@mandriva.com value = readl(priv->io + KIRKWOOD_RECCTL);
54052b896cfSRussell King value &= ~KIRKWOOD_RECCTL_ENABLE_MASK;
541f9b95980Sapatard@mandriva.com writel(value, priv->io + KIRKWOOD_RECCTL);
542f9b95980Sapatard@mandriva.com
543f9b95980Sapatard@mandriva.com return 0;
544f9b95980Sapatard@mandriva.com
545f9b95980Sapatard@mandriva.com }
546f9b95980Sapatard@mandriva.com
54785e7652dSLars-Peter Clausen static const struct snd_soc_dai_ops kirkwood_i2s_dai_ops = {
548f0fba2adSLiam Girdwood .startup = kirkwood_i2s_startup,
549f9b95980Sapatard@mandriva.com .trigger = kirkwood_i2s_trigger,
550f9b95980Sapatard@mandriva.com .hw_params = kirkwood_i2s_hw_params,
551f9b95980Sapatard@mandriva.com .set_fmt = kirkwood_i2s_set_fmt,
552f9b95980Sapatard@mandriva.com };
553f9b95980Sapatard@mandriva.com
55475b9b65eSJean-Francois Moine static struct snd_soc_dai_driver kirkwood_i2s_dai[2] = {
55575b9b65eSJean-Francois Moine {
55675b9b65eSJean-Francois Moine .name = "i2s",
55775b9b65eSJean-Francois Moine .id = 0,
558f9b95980Sapatard@mandriva.com .playback = {
559f9b95980Sapatard@mandriva.com .channels_min = 1,
560f9b95980Sapatard@mandriva.com .channels_max = 2,
5619e12cbd9SMark Brown .rates = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |
5629e12cbd9SMark Brown SNDRV_PCM_RATE_96000,
563363589bfSRussell King .formats = KIRKWOOD_I2S_FORMATS,
564363589bfSRussell King },
565f9b95980Sapatard@mandriva.com .capture = {
566f9b95980Sapatard@mandriva.com .channels_min = 1,
567f9b95980Sapatard@mandriva.com .channels_max = 2,
5689e12cbd9SMark Brown .rates = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |
5699e12cbd9SMark Brown SNDRV_PCM_RATE_96000,
570363589bfSRussell King .formats = KIRKWOOD_I2S_FORMATS,
571363589bfSRussell King },
572363589bfSRussell King .ops = &kirkwood_i2s_dai_ops,
57375b9b65eSJean-Francois Moine },
57475b9b65eSJean-Francois Moine {
57575b9b65eSJean-Francois Moine .name = "spdif",
57675b9b65eSJean-Francois Moine .id = 1,
57775b9b65eSJean-Francois Moine .playback = {
57875b9b65eSJean-Francois Moine .channels_min = 1,
57975b9b65eSJean-Francois Moine .channels_max = 2,
58075b9b65eSJean-Francois Moine .rates = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |
58175b9b65eSJean-Francois Moine SNDRV_PCM_RATE_96000,
5821c195ddbSJean-Francois Moine .formats = KIRKWOOD_SPDIF_FORMATS,
58375b9b65eSJean-Francois Moine },
58475b9b65eSJean-Francois Moine .capture = {
58575b9b65eSJean-Francois Moine .channels_min = 1,
58675b9b65eSJean-Francois Moine .channels_max = 2,
58775b9b65eSJean-Francois Moine .rates = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |
58875b9b65eSJean-Francois Moine SNDRV_PCM_RATE_96000,
5891c195ddbSJean-Francois Moine .formats = KIRKWOOD_SPDIF_FORMATS,
59075b9b65eSJean-Francois Moine },
59175b9b65eSJean-Francois Moine .ops = &kirkwood_i2s_dai_ops,
59275b9b65eSJean-Francois Moine },
593363589bfSRussell King };
594363589bfSRussell King
59575b9b65eSJean-Francois Moine static struct snd_soc_dai_driver kirkwood_i2s_dai_extclk[2] = {
59675b9b65eSJean-Francois Moine {
59775b9b65eSJean-Francois Moine .name = "i2s",
59875b9b65eSJean-Francois Moine .id = 0,
599363589bfSRussell King .playback = {
600363589bfSRussell King .channels_min = 1,
601363589bfSRussell King .channels_max = 2,
60202fc17c1SJean-Francois Moine .rates = SNDRV_PCM_RATE_CONTINUOUS,
60302fc17c1SJean-Francois Moine .rate_min = 5512,
60402fc17c1SJean-Francois Moine .rate_max = 192000,
605363589bfSRussell King .formats = KIRKWOOD_I2S_FORMATS,
606363589bfSRussell King },
607363589bfSRussell King .capture = {
608363589bfSRussell King .channels_min = 1,
609363589bfSRussell King .channels_max = 2,
61002fc17c1SJean-Francois Moine .rates = SNDRV_PCM_RATE_CONTINUOUS,
61102fc17c1SJean-Francois Moine .rate_min = 5512,
61202fc17c1SJean-Francois Moine .rate_max = 192000,
613363589bfSRussell King .formats = KIRKWOOD_I2S_FORMATS,
614363589bfSRussell King },
615f9b95980Sapatard@mandriva.com .ops = &kirkwood_i2s_dai_ops,
61675b9b65eSJean-Francois Moine },
61775b9b65eSJean-Francois Moine {
61875b9b65eSJean-Francois Moine .name = "spdif",
61975b9b65eSJean-Francois Moine .id = 1,
62075b9b65eSJean-Francois Moine .playback = {
62175b9b65eSJean-Francois Moine .channels_min = 1,
62275b9b65eSJean-Francois Moine .channels_max = 2,
62302fc17c1SJean-Francois Moine .rates = SNDRV_PCM_RATE_CONTINUOUS,
62402fc17c1SJean-Francois Moine .rate_min = 5512,
62502fc17c1SJean-Francois Moine .rate_max = 192000,
6261c195ddbSJean-Francois Moine .formats = KIRKWOOD_SPDIF_FORMATS,
62775b9b65eSJean-Francois Moine },
62875b9b65eSJean-Francois Moine .capture = {
62975b9b65eSJean-Francois Moine .channels_min = 1,
63075b9b65eSJean-Francois Moine .channels_max = 2,
63102fc17c1SJean-Francois Moine .rates = SNDRV_PCM_RATE_CONTINUOUS,
63202fc17c1SJean-Francois Moine .rate_min = 5512,
63302fc17c1SJean-Francois Moine .rate_max = 192000,
6341c195ddbSJean-Francois Moine .formats = KIRKWOOD_SPDIF_FORMATS,
63575b9b65eSJean-Francois Moine },
63675b9b65eSJean-Francois Moine .ops = &kirkwood_i2s_dai_ops,
63775b9b65eSJean-Francois Moine },
638f9b95980Sapatard@mandriva.com };
639f9b95980Sapatard@mandriva.com
kirkwood_i2s_dev_probe(struct platform_device * pdev)64034e15fbdSBill Pemberton static int kirkwood_i2s_dev_probe(struct platform_device *pdev)
641f9b95980Sapatard@mandriva.com {
642363589bfSRussell King struct kirkwood_asoc_platform_data *data = pdev->dev.platform_data;
64375b9b65eSJean-Francois Moine struct snd_soc_dai_driver *soc_dai = kirkwood_i2s_dai;
644f0fba2adSLiam Girdwood struct kirkwood_dma_data *priv;
645eb632318SJean-Francois Moine struct device_node *np = pdev->dev.of_node;
646f9b95980Sapatard@mandriva.com int err;
647f9b95980Sapatard@mandriva.com
648dbc517bfSRussell King priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
649c7591edcSMarkus Elfring if (!priv)
650dbc517bfSRussell King return -ENOMEM;
651c7591edcSMarkus Elfring
652f0fba2adSLiam Girdwood dev_set_drvdata(&pdev->dev, priv);
653f9b95980Sapatard@mandriva.com
6542adfc688SMarcin Wojtas if (of_device_is_compatible(np, "marvell,armada-380-audio"))
6552adfc688SMarcin Wojtas priv->io = devm_platform_ioremap_resource_byname(pdev, "i2s_regs");
6562adfc688SMarcin Wojtas else
65789dd38bfSYueHaibing priv->io = devm_platform_ioremap_resource(pdev, 0);
658b25b5aa0SThierry Reding if (IS_ERR(priv->io))
659b25b5aa0SThierry Reding return PTR_ERR(priv->io);
660f9b95980Sapatard@mandriva.com
661f9b95980Sapatard@mandriva.com priv->irq = platform_get_irq(pdev, 0);
662cf9441adSStephen Boyd if (priv->irq < 0)
663e7b2e30aSGustavo A. R. Silva return priv->irq;
664f9b95980Sapatard@mandriva.com
6652adfc688SMarcin Wojtas if (of_device_is_compatible(np, "marvell,armada-380-audio")) {
6662adfc688SMarcin Wojtas err = armada_38x_i2s_init_quirk(pdev, priv, soc_dai);
6672adfc688SMarcin Wojtas if (err < 0)
6682adfc688SMarcin Wojtas return err;
6692adfc688SMarcin Wojtas /* Set initial pll frequency */
6702adfc688SMarcin Wojtas armada_38x_set_pll(priv->pll_config, 44100);
6712adfc688SMarcin Wojtas }
6722adfc688SMarcin Wojtas
673eb632318SJean-Francois Moine if (np) {
674eb632318SJean-Francois Moine priv->burst = 128; /* might be 32 or 128 */
675eb632318SJean-Francois Moine } else if (data) {
676eb632318SJean-Francois Moine priv->burst = data->burst;
677eb632318SJean-Francois Moine } else {
678eb632318SJean-Francois Moine dev_err(&pdev->dev, "no DT nor platform data ?!\n");
679dbc517bfSRussell King return -EINVAL;
680f9b95980Sapatard@mandriva.com }
681f9b95980Sapatard@mandriva.com
682eb632318SJean-Francois Moine priv->clk = devm_clk_get(&pdev->dev, np ? "internal" : NULL);
683e919c716SAndrew Lunn if (IS_ERR(priv->clk)) {
684e919c716SAndrew Lunn dev_err(&pdev->dev, "no clock\n");
685dbc517bfSRussell King return PTR_ERR(priv->clk);
686e919c716SAndrew Lunn }
687dbc517bfSRussell King
6884734dc96SMark Brown priv->extclk = devm_clk_get(&pdev->dev, "extclk");
68984aac6c7SJean-Francois Moine if (IS_ERR(priv->extclk)) {
69084aac6c7SJean-Francois Moine if (PTR_ERR(priv->extclk) == -EPROBE_DEFER)
69184aac6c7SJean-Francois Moine return -EPROBE_DEFER;
69284aac6c7SJean-Francois Moine } else {
693aaa6d062SShawn Guo if (clk_is_match(priv->extclk, priv->clk)) {
694af64d734SRussell King devm_clk_put(&pdev->dev, priv->extclk);
695d8d11ba5SRussell King priv->extclk = ERR_PTR(-EINVAL);
696d8d11ba5SRussell King } else {
697d8d11ba5SRussell King dev_info(&pdev->dev, "found external clock\n");
698d8d11ba5SRussell King clk_prepare_enable(priv->extclk);
69999d8d3baSJean-Francois Moine soc_dai = kirkwood_i2s_dai_extclk;
700d8d11ba5SRussell King }
701d8d11ba5SRussell King }
702d8d11ba5SRussell King
7034523817dSRussell King err = clk_prepare_enable(priv->clk);
7044523817dSRussell King if (err < 0)
7054523817dSRussell King return err;
7064523817dSRussell King
707d8d11ba5SRussell King /* Some sensible defaults - this reflects the powerup values */
708d8d11ba5SRussell King priv->ctl_play = KIRKWOOD_PLAYCTL_SIZE_24;
709d8d11ba5SRussell King priv->ctl_rec = KIRKWOOD_RECCTL_SIZE_24;
710d8d11ba5SRussell King
711d8d11ba5SRussell King /* Select the burst size */
712eb632318SJean-Francois Moine if (priv->burst == 32) {
713d8d11ba5SRussell King priv->ctl_play |= KIRKWOOD_PLAYCTL_BURST_32;
714d8d11ba5SRussell King priv->ctl_rec |= KIRKWOOD_RECCTL_BURST_32;
715d8d11ba5SRussell King } else {
716d8d11ba5SRussell King priv->ctl_play |= KIRKWOOD_PLAYCTL_BURST_128;
717d8d11ba5SRussell King priv->ctl_rec |= KIRKWOOD_RECCTL_BURST_128;
718d8d11ba5SRussell King }
719d8d11ba5SRussell King
720dc39596aSRussell King err = snd_soc_register_component(&pdev->dev, &kirkwood_soc_component,
72175b9b65eSJean-Francois Moine soc_dai, 2);
72264ddf1f8SRussell King if (err) {
72383d85f53SKuninori Morimoto dev_err(&pdev->dev, "snd_soc_register_component failed\n");
72464ddf1f8SRussell King goto err_component;
72564ddf1f8SRussell King }
726baffab28SSimon Baatz
72775b9b65eSJean-Francois Moine kirkwood_i2s_init(priv);
72875b9b65eSJean-Francois Moine
72964ddf1f8SRussell King return 0;
730f98fc0f8SKuninori Morimoto
73164ddf1f8SRussell King err_component:
7324734dc96SMark Brown if (!IS_ERR(priv->extclk))
733363589bfSRussell King clk_disable_unprepare(priv->extclk);
734baffab28SSimon Baatz clk_disable_unprepare(priv->clk);
735f9b95980Sapatard@mandriva.com
736f9b95980Sapatard@mandriva.com return err;
737f9b95980Sapatard@mandriva.com }
738f9b95980Sapatard@mandriva.com
kirkwood_i2s_dev_remove(struct platform_device * pdev)739*8c078706SUwe Kleine-König static void kirkwood_i2s_dev_remove(struct platform_device *pdev)
740f9b95980Sapatard@mandriva.com {
741f0fba2adSLiam Girdwood struct kirkwood_dma_data *priv = dev_get_drvdata(&pdev->dev);
742f0fba2adSLiam Girdwood
743dc39596aSRussell King snd_soc_unregister_component(&pdev->dev);
7444734dc96SMark Brown if (!IS_ERR(priv->extclk))
745363589bfSRussell King clk_disable_unprepare(priv->extclk);
746e919c716SAndrew Lunn clk_disable_unprepare(priv->clk);
747f9b95980Sapatard@mandriva.com }
748f9b95980Sapatard@mandriva.com
749eb632318SJean-Francois Moine #ifdef CONFIG_OF
7507f2c52afSFabian Frederick static const struct of_device_id mvebu_audio_of_match[] = {
751d098b2f0SThomas Petazzoni { .compatible = "marvell,kirkwood-audio" },
752d098b2f0SThomas Petazzoni { .compatible = "marvell,dove-audio" },
7539a0d5113SThomas Petazzoni { .compatible = "marvell,armada370-audio" },
7542adfc688SMarcin Wojtas { .compatible = "marvell,armada-380-audio" },
755eb632318SJean-Francois Moine { }
756eb632318SJean-Francois Moine };
757eb632318SJean-Francois Moine MODULE_DEVICE_TABLE(of, mvebu_audio_of_match);
758eb632318SJean-Francois Moine #endif
759eb632318SJean-Francois Moine
760f9b95980Sapatard@mandriva.com static struct platform_driver kirkwood_i2s_driver = {
761f9b95980Sapatard@mandriva.com .probe = kirkwood_i2s_dev_probe,
762*8c078706SUwe Kleine-König .remove_new = kirkwood_i2s_dev_remove,
763f9b95980Sapatard@mandriva.com .driver = {
764f9b95980Sapatard@mandriva.com .name = DRV_NAME,
765eb632318SJean-Francois Moine .of_match_table = of_match_ptr(mvebu_audio_of_match),
766f9b95980Sapatard@mandriva.com },
767f9b95980Sapatard@mandriva.com };
768f9b95980Sapatard@mandriva.com
76941b10225SAxel Lin module_platform_driver(kirkwood_i2s_driver);
770f9b95980Sapatard@mandriva.com
771f9b95980Sapatard@mandriva.com /* Module information */
77269737897SArnaud Patard (Rtp) MODULE_AUTHOR("Arnaud Patard, <arnaud.patard@rtp-net.org>");
773f9b95980Sapatard@mandriva.com MODULE_DESCRIPTION("Kirkwood I2S SoC Interface");
774f9b95980Sapatard@mandriva.com MODULE_LICENSE("GPL");
77564ddf1f8SRussell King MODULE_ALIAS("platform:mvebu-audio");
776