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