162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * kirkwood-i2s.c
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * (c) 2010 Arnaud Patard <apatard@mandriva.com>
662306a36Sopenharmony_ci * (c) 2010 Arnaud Patard <arnaud.patard@rtp-net.org>
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include <linux/init.h>
1062306a36Sopenharmony_ci#include <linux/module.h>
1162306a36Sopenharmony_ci#include <linux/platform_device.h>
1262306a36Sopenharmony_ci#include <linux/io.h>
1362306a36Sopenharmony_ci#include <linux/slab.h>
1462306a36Sopenharmony_ci#include <linux/mbus.h>
1562306a36Sopenharmony_ci#include <linux/delay.h>
1662306a36Sopenharmony_ci#include <linux/clk.h>
1762306a36Sopenharmony_ci#include <sound/pcm.h>
1862306a36Sopenharmony_ci#include <sound/pcm_params.h>
1962306a36Sopenharmony_ci#include <sound/soc.h>
2062306a36Sopenharmony_ci#include <linux/platform_data/asoc-kirkwood.h>
2162306a36Sopenharmony_ci#include <linux/of.h>
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci#include "kirkwood.h"
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci#define KIRKWOOD_I2S_FORMATS \
2662306a36Sopenharmony_ci	(SNDRV_PCM_FMTBIT_S16_LE | \
2762306a36Sopenharmony_ci	 SNDRV_PCM_FMTBIT_S24_LE | \
2862306a36Sopenharmony_ci	 SNDRV_PCM_FMTBIT_S32_LE)
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci#define KIRKWOOD_SPDIF_FORMATS \
3162306a36Sopenharmony_ci	(SNDRV_PCM_FMTBIT_S16_LE | \
3262306a36Sopenharmony_ci	 SNDRV_PCM_FMTBIT_S24_LE)
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci/* These registers are relative to the second register region -
3562306a36Sopenharmony_ci * audio pll configuration.
3662306a36Sopenharmony_ci */
3762306a36Sopenharmony_ci#define A38X_PLL_CONF_REG0			0x0
3862306a36Sopenharmony_ci#define     A38X_PLL_FB_CLK_DIV_OFFSET		10
3962306a36Sopenharmony_ci#define     A38X_PLL_FB_CLK_DIV_MASK		0x7fc00
4062306a36Sopenharmony_ci#define A38X_PLL_CONF_REG1			0x4
4162306a36Sopenharmony_ci#define     A38X_PLL_FREQ_OFFSET_MASK		0xffff
4262306a36Sopenharmony_ci#define     A38X_PLL_FREQ_OFFSET_VALID		BIT(16)
4362306a36Sopenharmony_ci#define     A38X_PLL_SW_RESET			BIT(31)
4462306a36Sopenharmony_ci#define A38X_PLL_CONF_REG2			0x8
4562306a36Sopenharmony_ci#define     A38X_PLL_AUDIO_POSTDIV_MASK		0x7f
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci/* Bit below belongs to SoC control register corresponding to the third
4862306a36Sopenharmony_ci * register region.
4962306a36Sopenharmony_ci */
5062306a36Sopenharmony_ci#define A38X_SPDIF_MODE_ENABLE			BIT(27)
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_cistatic int armada_38x_i2s_init_quirk(struct platform_device *pdev,
5362306a36Sopenharmony_ci				     struct kirkwood_dma_data *priv,
5462306a36Sopenharmony_ci				     struct snd_soc_dai_driver *dai_drv)
5562306a36Sopenharmony_ci{
5662306a36Sopenharmony_ci	struct device_node *np = pdev->dev.of_node;
5762306a36Sopenharmony_ci	u32 reg_val;
5862306a36Sopenharmony_ci	int i;
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci	priv->pll_config = devm_platform_ioremap_resource_byname(pdev, "pll_regs");
6162306a36Sopenharmony_ci	if (IS_ERR(priv->pll_config))
6262306a36Sopenharmony_ci		return -ENOMEM;
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci	priv->soc_control = devm_platform_ioremap_resource_byname(pdev, "soc_ctrl");
6562306a36Sopenharmony_ci	if (IS_ERR(priv->soc_control))
6662306a36Sopenharmony_ci		return -ENOMEM;
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci	/* Select one of exceptive modes: I2S or S/PDIF */
6962306a36Sopenharmony_ci	reg_val = readl(priv->soc_control);
7062306a36Sopenharmony_ci	if (of_property_read_bool(np, "spdif-mode")) {
7162306a36Sopenharmony_ci		reg_val |= A38X_SPDIF_MODE_ENABLE;
7262306a36Sopenharmony_ci		dev_info(&pdev->dev, "using S/PDIF mode\n");
7362306a36Sopenharmony_ci	} else {
7462306a36Sopenharmony_ci		reg_val &= ~A38X_SPDIF_MODE_ENABLE;
7562306a36Sopenharmony_ci		dev_info(&pdev->dev, "using I2S mode\n");
7662306a36Sopenharmony_ci	}
7762306a36Sopenharmony_ci	writel(reg_val, priv->soc_control);
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci	/* Update available rates of mclk's fs */
8062306a36Sopenharmony_ci	for (i = 0; i < 2; i++) {
8162306a36Sopenharmony_ci		dai_drv[i].playback.rates |= SNDRV_PCM_RATE_192000;
8262306a36Sopenharmony_ci		dai_drv[i].capture.rates |= SNDRV_PCM_RATE_192000;
8362306a36Sopenharmony_ci	}
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci	return 0;
8662306a36Sopenharmony_ci}
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_cistatic inline void armada_38x_set_pll(void __iomem *base, unsigned long rate)
8962306a36Sopenharmony_ci{
9062306a36Sopenharmony_ci	u32 reg_val;
9162306a36Sopenharmony_ci	u16 freq_offset = 0x22b0;
9262306a36Sopenharmony_ci	u8 audio_postdiv, fb_clk_div = 0x1d;
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci	/* Set frequency offset value to not valid and enable PLL reset */
9562306a36Sopenharmony_ci	reg_val = readl(base + A38X_PLL_CONF_REG1);
9662306a36Sopenharmony_ci	reg_val &= ~A38X_PLL_FREQ_OFFSET_VALID;
9762306a36Sopenharmony_ci	reg_val &= ~A38X_PLL_SW_RESET;
9862306a36Sopenharmony_ci	writel(reg_val, base + A38X_PLL_CONF_REG1);
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci	udelay(1);
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci	/* Update PLL parameters */
10362306a36Sopenharmony_ci	switch (rate) {
10462306a36Sopenharmony_ci	default:
10562306a36Sopenharmony_ci	case 44100:
10662306a36Sopenharmony_ci		freq_offset = 0x735;
10762306a36Sopenharmony_ci		fb_clk_div = 0x1b;
10862306a36Sopenharmony_ci		audio_postdiv = 0xc;
10962306a36Sopenharmony_ci		break;
11062306a36Sopenharmony_ci	case 48000:
11162306a36Sopenharmony_ci		audio_postdiv = 0xc;
11262306a36Sopenharmony_ci		break;
11362306a36Sopenharmony_ci	case 96000:
11462306a36Sopenharmony_ci		audio_postdiv = 0x6;
11562306a36Sopenharmony_ci		break;
11662306a36Sopenharmony_ci	case 192000:
11762306a36Sopenharmony_ci		audio_postdiv = 0x3;
11862306a36Sopenharmony_ci		break;
11962306a36Sopenharmony_ci	}
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci	reg_val = readl(base + A38X_PLL_CONF_REG0);
12262306a36Sopenharmony_ci	reg_val &= ~A38X_PLL_FB_CLK_DIV_MASK;
12362306a36Sopenharmony_ci	reg_val |= (fb_clk_div << A38X_PLL_FB_CLK_DIV_OFFSET);
12462306a36Sopenharmony_ci	writel(reg_val, base + A38X_PLL_CONF_REG0);
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci	reg_val = readl(base + A38X_PLL_CONF_REG2);
12762306a36Sopenharmony_ci	reg_val &= ~A38X_PLL_AUDIO_POSTDIV_MASK;
12862306a36Sopenharmony_ci	reg_val |= audio_postdiv;
12962306a36Sopenharmony_ci	writel(reg_val, base + A38X_PLL_CONF_REG2);
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci	reg_val = readl(base + A38X_PLL_CONF_REG1);
13262306a36Sopenharmony_ci	reg_val &= ~A38X_PLL_FREQ_OFFSET_MASK;
13362306a36Sopenharmony_ci	reg_val |= freq_offset;
13462306a36Sopenharmony_ci	writel(reg_val, base + A38X_PLL_CONF_REG1);
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci	udelay(1);
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci	/* Disable reset */
13962306a36Sopenharmony_ci	reg_val |= A38X_PLL_SW_RESET;
14062306a36Sopenharmony_ci	writel(reg_val, base + A38X_PLL_CONF_REG1);
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci	/* Wait 50us for PLL to lock */
14362306a36Sopenharmony_ci	udelay(50);
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci	/* Restore frequency offset value validity */
14662306a36Sopenharmony_ci	reg_val |= A38X_PLL_FREQ_OFFSET_VALID;
14762306a36Sopenharmony_ci	writel(reg_val, base + A38X_PLL_CONF_REG1);
14862306a36Sopenharmony_ci}
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_cistatic int kirkwood_i2s_set_fmt(struct snd_soc_dai *cpu_dai,
15162306a36Sopenharmony_ci		unsigned int fmt)
15262306a36Sopenharmony_ci{
15362306a36Sopenharmony_ci	struct kirkwood_dma_data *priv = snd_soc_dai_get_drvdata(cpu_dai);
15462306a36Sopenharmony_ci	unsigned long mask;
15562306a36Sopenharmony_ci	unsigned long value;
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
15862306a36Sopenharmony_ci	case SND_SOC_DAIFMT_RIGHT_J:
15962306a36Sopenharmony_ci		mask = KIRKWOOD_I2S_CTL_RJ;
16062306a36Sopenharmony_ci		break;
16162306a36Sopenharmony_ci	case SND_SOC_DAIFMT_LEFT_J:
16262306a36Sopenharmony_ci		mask = KIRKWOOD_I2S_CTL_LJ;
16362306a36Sopenharmony_ci		break;
16462306a36Sopenharmony_ci	case SND_SOC_DAIFMT_I2S:
16562306a36Sopenharmony_ci		mask = KIRKWOOD_I2S_CTL_I2S;
16662306a36Sopenharmony_ci		break;
16762306a36Sopenharmony_ci	default:
16862306a36Sopenharmony_ci		return -EINVAL;
16962306a36Sopenharmony_ci	}
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci	/*
17262306a36Sopenharmony_ci	 * Set same format for playback and record
17362306a36Sopenharmony_ci	 * This avoids some troubles.
17462306a36Sopenharmony_ci	 */
17562306a36Sopenharmony_ci	value = readl(priv->io+KIRKWOOD_I2S_PLAYCTL);
17662306a36Sopenharmony_ci	value &= ~KIRKWOOD_I2S_CTL_JUST_MASK;
17762306a36Sopenharmony_ci	value |= mask;
17862306a36Sopenharmony_ci	writel(value, priv->io+KIRKWOOD_I2S_PLAYCTL);
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci	value = readl(priv->io+KIRKWOOD_I2S_RECCTL);
18162306a36Sopenharmony_ci	value &= ~KIRKWOOD_I2S_CTL_JUST_MASK;
18262306a36Sopenharmony_ci	value |= mask;
18362306a36Sopenharmony_ci	writel(value, priv->io+KIRKWOOD_I2S_RECCTL);
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci	return 0;
18662306a36Sopenharmony_ci}
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_cistatic inline void kirkwood_set_dco(void __iomem *io, unsigned long rate)
18962306a36Sopenharmony_ci{
19062306a36Sopenharmony_ci	unsigned long value;
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci	value = KIRKWOOD_DCO_CTL_OFFSET_0;
19362306a36Sopenharmony_ci	switch (rate) {
19462306a36Sopenharmony_ci	default:
19562306a36Sopenharmony_ci	case 44100:
19662306a36Sopenharmony_ci		value |= KIRKWOOD_DCO_CTL_FREQ_11;
19762306a36Sopenharmony_ci		break;
19862306a36Sopenharmony_ci	case 48000:
19962306a36Sopenharmony_ci		value |= KIRKWOOD_DCO_CTL_FREQ_12;
20062306a36Sopenharmony_ci		break;
20162306a36Sopenharmony_ci	case 96000:
20262306a36Sopenharmony_ci		value |= KIRKWOOD_DCO_CTL_FREQ_24;
20362306a36Sopenharmony_ci		break;
20462306a36Sopenharmony_ci	}
20562306a36Sopenharmony_ci	writel(value, io + KIRKWOOD_DCO_CTL);
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ci	/* wait for dco locked */
20862306a36Sopenharmony_ci	do {
20962306a36Sopenharmony_ci		cpu_relax();
21062306a36Sopenharmony_ci		value = readl(io + KIRKWOOD_DCO_SPCR_STATUS);
21162306a36Sopenharmony_ci		value &= KIRKWOOD_DCO_SPCR_STATUS_DCO_LOCK;
21262306a36Sopenharmony_ci	} while (value == 0);
21362306a36Sopenharmony_ci}
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_cistatic void kirkwood_set_rate(struct snd_soc_dai *dai,
21662306a36Sopenharmony_ci	struct kirkwood_dma_data *priv, unsigned long rate)
21762306a36Sopenharmony_ci{
21862306a36Sopenharmony_ci	uint32_t clks_ctrl;
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci	if (IS_ERR(priv->extclk)) {
22162306a36Sopenharmony_ci		/* use internal dco for the supported rates
22262306a36Sopenharmony_ci		 * defined in kirkwood_i2s_dai */
22362306a36Sopenharmony_ci		dev_dbg(dai->dev, "%s: dco set rate = %lu\n",
22462306a36Sopenharmony_ci			__func__, rate);
22562306a36Sopenharmony_ci		if (priv->pll_config)
22662306a36Sopenharmony_ci			armada_38x_set_pll(priv->pll_config, rate);
22762306a36Sopenharmony_ci		else
22862306a36Sopenharmony_ci			kirkwood_set_dco(priv->io, rate);
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci		clks_ctrl = KIRKWOOD_MCLK_SOURCE_DCO;
23162306a36Sopenharmony_ci	} else {
23262306a36Sopenharmony_ci		/* use the external clock for the other rates
23362306a36Sopenharmony_ci		 * defined in kirkwood_i2s_dai_extclk */
23462306a36Sopenharmony_ci		dev_dbg(dai->dev, "%s: extclk set rate = %lu -> %lu\n",
23562306a36Sopenharmony_ci			__func__, rate, 256 * rate);
23662306a36Sopenharmony_ci		clk_set_rate(priv->extclk, 256 * rate);
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci		clks_ctrl = KIRKWOOD_MCLK_SOURCE_EXTCLK;
23962306a36Sopenharmony_ci	}
24062306a36Sopenharmony_ci	writel(clks_ctrl, priv->io + KIRKWOOD_CLOCKS_CTRL);
24162306a36Sopenharmony_ci}
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_cistatic int kirkwood_i2s_startup(struct snd_pcm_substream *substream,
24462306a36Sopenharmony_ci		struct snd_soc_dai *dai)
24562306a36Sopenharmony_ci{
24662306a36Sopenharmony_ci	struct kirkwood_dma_data *priv = snd_soc_dai_get_drvdata(dai);
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ci	snd_soc_dai_set_dma_data(dai, substream, priv);
24962306a36Sopenharmony_ci	return 0;
25062306a36Sopenharmony_ci}
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_cistatic int kirkwood_i2s_hw_params(struct snd_pcm_substream *substream,
25362306a36Sopenharmony_ci				 struct snd_pcm_hw_params *params,
25462306a36Sopenharmony_ci				 struct snd_soc_dai *dai)
25562306a36Sopenharmony_ci{
25662306a36Sopenharmony_ci	struct kirkwood_dma_data *priv = snd_soc_dai_get_drvdata(dai);
25762306a36Sopenharmony_ci	uint32_t ctl_play, ctl_rec;
25862306a36Sopenharmony_ci	unsigned int i2s_reg;
25962306a36Sopenharmony_ci	unsigned long i2s_value;
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_ci	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
26262306a36Sopenharmony_ci		i2s_reg = KIRKWOOD_I2S_PLAYCTL;
26362306a36Sopenharmony_ci	} else {
26462306a36Sopenharmony_ci		i2s_reg = KIRKWOOD_I2S_RECCTL;
26562306a36Sopenharmony_ci	}
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_ci	kirkwood_set_rate(dai, priv, params_rate(params));
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci	i2s_value = readl(priv->io+i2s_reg);
27062306a36Sopenharmony_ci	i2s_value &= ~KIRKWOOD_I2S_CTL_SIZE_MASK;
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_ci	/*
27362306a36Sopenharmony_ci	 * Size settings in play/rec i2s control regs and play/rec control
27462306a36Sopenharmony_ci	 * regs must be the same.
27562306a36Sopenharmony_ci	 */
27662306a36Sopenharmony_ci	switch (params_format(params)) {
27762306a36Sopenharmony_ci	case SNDRV_PCM_FORMAT_S16_LE:
27862306a36Sopenharmony_ci		i2s_value |= KIRKWOOD_I2S_CTL_SIZE_16;
27962306a36Sopenharmony_ci		ctl_play = KIRKWOOD_PLAYCTL_SIZE_16_C |
28062306a36Sopenharmony_ci			   KIRKWOOD_PLAYCTL_I2S_EN |
28162306a36Sopenharmony_ci			   KIRKWOOD_PLAYCTL_SPDIF_EN;
28262306a36Sopenharmony_ci		ctl_rec = KIRKWOOD_RECCTL_SIZE_16_C |
28362306a36Sopenharmony_ci			  KIRKWOOD_RECCTL_I2S_EN |
28462306a36Sopenharmony_ci			  KIRKWOOD_RECCTL_SPDIF_EN;
28562306a36Sopenharmony_ci		break;
28662306a36Sopenharmony_ci	/*
28762306a36Sopenharmony_ci	 * doesn't work... S20_3LE != kirkwood 20bit format ?
28862306a36Sopenharmony_ci	 *
28962306a36Sopenharmony_ci	case SNDRV_PCM_FORMAT_S20_3LE:
29062306a36Sopenharmony_ci		i2s_value |= KIRKWOOD_I2S_CTL_SIZE_20;
29162306a36Sopenharmony_ci		ctl_play = KIRKWOOD_PLAYCTL_SIZE_20 |
29262306a36Sopenharmony_ci			   KIRKWOOD_PLAYCTL_I2S_EN;
29362306a36Sopenharmony_ci		ctl_rec = KIRKWOOD_RECCTL_SIZE_20 |
29462306a36Sopenharmony_ci			  KIRKWOOD_RECCTL_I2S_EN;
29562306a36Sopenharmony_ci		break;
29662306a36Sopenharmony_ci	*/
29762306a36Sopenharmony_ci	case SNDRV_PCM_FORMAT_S24_LE:
29862306a36Sopenharmony_ci		i2s_value |= KIRKWOOD_I2S_CTL_SIZE_24;
29962306a36Sopenharmony_ci		ctl_play = KIRKWOOD_PLAYCTL_SIZE_24 |
30062306a36Sopenharmony_ci			   KIRKWOOD_PLAYCTL_I2S_EN |
30162306a36Sopenharmony_ci			   KIRKWOOD_PLAYCTL_SPDIF_EN;
30262306a36Sopenharmony_ci		ctl_rec = KIRKWOOD_RECCTL_SIZE_24 |
30362306a36Sopenharmony_ci			  KIRKWOOD_RECCTL_I2S_EN |
30462306a36Sopenharmony_ci			  KIRKWOOD_RECCTL_SPDIF_EN;
30562306a36Sopenharmony_ci		break;
30662306a36Sopenharmony_ci	case SNDRV_PCM_FORMAT_S32_LE:
30762306a36Sopenharmony_ci		i2s_value |= KIRKWOOD_I2S_CTL_SIZE_32;
30862306a36Sopenharmony_ci		ctl_play = KIRKWOOD_PLAYCTL_SIZE_32 |
30962306a36Sopenharmony_ci			   KIRKWOOD_PLAYCTL_I2S_EN;
31062306a36Sopenharmony_ci		ctl_rec = KIRKWOOD_RECCTL_SIZE_32 |
31162306a36Sopenharmony_ci			  KIRKWOOD_RECCTL_I2S_EN;
31262306a36Sopenharmony_ci		break;
31362306a36Sopenharmony_ci	default:
31462306a36Sopenharmony_ci		return -EINVAL;
31562306a36Sopenharmony_ci	}
31662306a36Sopenharmony_ci
31762306a36Sopenharmony_ci	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
31862306a36Sopenharmony_ci		if (params_channels(params) == 1)
31962306a36Sopenharmony_ci			ctl_play |= KIRKWOOD_PLAYCTL_MONO_BOTH;
32062306a36Sopenharmony_ci		else
32162306a36Sopenharmony_ci			ctl_play |= KIRKWOOD_PLAYCTL_MONO_OFF;
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_ci		priv->ctl_play &= ~(KIRKWOOD_PLAYCTL_MONO_MASK |
32462306a36Sopenharmony_ci				    KIRKWOOD_PLAYCTL_ENABLE_MASK |
32562306a36Sopenharmony_ci				    KIRKWOOD_PLAYCTL_SIZE_MASK);
32662306a36Sopenharmony_ci		priv->ctl_play |= ctl_play;
32762306a36Sopenharmony_ci	} else {
32862306a36Sopenharmony_ci		priv->ctl_rec &= ~(KIRKWOOD_RECCTL_ENABLE_MASK |
32962306a36Sopenharmony_ci				   KIRKWOOD_RECCTL_SIZE_MASK);
33062306a36Sopenharmony_ci		priv->ctl_rec |= ctl_rec;
33162306a36Sopenharmony_ci	}
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_ci	writel(i2s_value, priv->io+i2s_reg);
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_ci	return 0;
33662306a36Sopenharmony_ci}
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_cistatic unsigned kirkwood_i2s_play_mute(unsigned ctl)
33962306a36Sopenharmony_ci{
34062306a36Sopenharmony_ci	if (!(ctl & KIRKWOOD_PLAYCTL_I2S_EN))
34162306a36Sopenharmony_ci		ctl |= KIRKWOOD_PLAYCTL_I2S_MUTE;
34262306a36Sopenharmony_ci	if (!(ctl & KIRKWOOD_PLAYCTL_SPDIF_EN))
34362306a36Sopenharmony_ci		ctl |= KIRKWOOD_PLAYCTL_SPDIF_MUTE;
34462306a36Sopenharmony_ci	return ctl;
34562306a36Sopenharmony_ci}
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_cistatic int kirkwood_i2s_play_trigger(struct snd_pcm_substream *substream,
34862306a36Sopenharmony_ci				int cmd, struct snd_soc_dai *dai)
34962306a36Sopenharmony_ci{
35062306a36Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
35162306a36Sopenharmony_ci	struct kirkwood_dma_data *priv = snd_soc_dai_get_drvdata(dai);
35262306a36Sopenharmony_ci	uint32_t ctl, value;
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_ci	ctl = readl(priv->io + KIRKWOOD_PLAYCTL);
35562306a36Sopenharmony_ci	if ((ctl & KIRKWOOD_PLAYCTL_ENABLE_MASK) == 0) {
35662306a36Sopenharmony_ci		unsigned timeout = 5000;
35762306a36Sopenharmony_ci		/*
35862306a36Sopenharmony_ci		 * The Armada510 spec says that if we enter pause mode, the
35962306a36Sopenharmony_ci		 * busy bit must be read back as clear _twice_.  Make sure
36062306a36Sopenharmony_ci		 * we respect that otherwise we get DMA underruns.
36162306a36Sopenharmony_ci		 */
36262306a36Sopenharmony_ci		do {
36362306a36Sopenharmony_ci			value = ctl;
36462306a36Sopenharmony_ci			ctl = readl(priv->io + KIRKWOOD_PLAYCTL);
36562306a36Sopenharmony_ci			if (!((ctl | value) & KIRKWOOD_PLAYCTL_PLAY_BUSY))
36662306a36Sopenharmony_ci				break;
36762306a36Sopenharmony_ci			udelay(1);
36862306a36Sopenharmony_ci		} while (timeout--);
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_ci		if ((ctl | value) & KIRKWOOD_PLAYCTL_PLAY_BUSY)
37162306a36Sopenharmony_ci			dev_notice(dai->dev, "timed out waiting for busy to deassert: %08x\n",
37262306a36Sopenharmony_ci				   ctl);
37362306a36Sopenharmony_ci	}
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_ci	switch (cmd) {
37662306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_START:
37762306a36Sopenharmony_ci		/* configure */
37862306a36Sopenharmony_ci		ctl = priv->ctl_play;
37962306a36Sopenharmony_ci		if (dai->id == 0)
38062306a36Sopenharmony_ci			ctl &= ~KIRKWOOD_PLAYCTL_SPDIF_EN;	/* i2s */
38162306a36Sopenharmony_ci		else
38262306a36Sopenharmony_ci			ctl &= ~KIRKWOOD_PLAYCTL_I2S_EN;	/* spdif */
38362306a36Sopenharmony_ci		ctl = kirkwood_i2s_play_mute(ctl);
38462306a36Sopenharmony_ci		value = ctl & ~KIRKWOOD_PLAYCTL_ENABLE_MASK;
38562306a36Sopenharmony_ci		writel(value, priv->io + KIRKWOOD_PLAYCTL);
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_ci		/* enable interrupts */
38862306a36Sopenharmony_ci		if (!runtime->no_period_wakeup) {
38962306a36Sopenharmony_ci			value = readl(priv->io + KIRKWOOD_INT_MASK);
39062306a36Sopenharmony_ci			value |= KIRKWOOD_INT_CAUSE_PLAY_BYTES;
39162306a36Sopenharmony_ci			writel(value, priv->io + KIRKWOOD_INT_MASK);
39262306a36Sopenharmony_ci		}
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_ci		/* enable playback */
39562306a36Sopenharmony_ci		writel(ctl, priv->io + KIRKWOOD_PLAYCTL);
39662306a36Sopenharmony_ci		break;
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_STOP:
39962306a36Sopenharmony_ci		/* stop audio, disable interrupts */
40062306a36Sopenharmony_ci		ctl |= KIRKWOOD_PLAYCTL_PAUSE | KIRKWOOD_PLAYCTL_I2S_MUTE |
40162306a36Sopenharmony_ci				KIRKWOOD_PLAYCTL_SPDIF_MUTE;
40262306a36Sopenharmony_ci		writel(ctl, priv->io + KIRKWOOD_PLAYCTL);
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_ci		value = readl(priv->io + KIRKWOOD_INT_MASK);
40562306a36Sopenharmony_ci		value &= ~KIRKWOOD_INT_CAUSE_PLAY_BYTES;
40662306a36Sopenharmony_ci		writel(value, priv->io + KIRKWOOD_INT_MASK);
40762306a36Sopenharmony_ci
40862306a36Sopenharmony_ci		/* disable all playbacks */
40962306a36Sopenharmony_ci		ctl &= ~KIRKWOOD_PLAYCTL_ENABLE_MASK;
41062306a36Sopenharmony_ci		writel(ctl, priv->io + KIRKWOOD_PLAYCTL);
41162306a36Sopenharmony_ci		break;
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
41462306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_SUSPEND:
41562306a36Sopenharmony_ci		ctl |= KIRKWOOD_PLAYCTL_PAUSE | KIRKWOOD_PLAYCTL_I2S_MUTE |
41662306a36Sopenharmony_ci				KIRKWOOD_PLAYCTL_SPDIF_MUTE;
41762306a36Sopenharmony_ci		writel(ctl, priv->io + KIRKWOOD_PLAYCTL);
41862306a36Sopenharmony_ci		break;
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_RESUME:
42162306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
42262306a36Sopenharmony_ci		ctl &= ~(KIRKWOOD_PLAYCTL_PAUSE | KIRKWOOD_PLAYCTL_I2S_MUTE |
42362306a36Sopenharmony_ci				KIRKWOOD_PLAYCTL_SPDIF_MUTE);
42462306a36Sopenharmony_ci		ctl = kirkwood_i2s_play_mute(ctl);
42562306a36Sopenharmony_ci		writel(ctl, priv->io + KIRKWOOD_PLAYCTL);
42662306a36Sopenharmony_ci		break;
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_ci	default:
42962306a36Sopenharmony_ci		return -EINVAL;
43062306a36Sopenharmony_ci	}
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_ci	return 0;
43362306a36Sopenharmony_ci}
43462306a36Sopenharmony_ci
43562306a36Sopenharmony_cistatic int kirkwood_i2s_rec_trigger(struct snd_pcm_substream *substream,
43662306a36Sopenharmony_ci				int cmd, struct snd_soc_dai *dai)
43762306a36Sopenharmony_ci{
43862306a36Sopenharmony_ci	struct kirkwood_dma_data *priv = snd_soc_dai_get_drvdata(dai);
43962306a36Sopenharmony_ci	uint32_t ctl, value;
44062306a36Sopenharmony_ci
44162306a36Sopenharmony_ci	value = readl(priv->io + KIRKWOOD_RECCTL);
44262306a36Sopenharmony_ci
44362306a36Sopenharmony_ci	switch (cmd) {
44462306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_START:
44562306a36Sopenharmony_ci		/* configure */
44662306a36Sopenharmony_ci		ctl = priv->ctl_rec;
44762306a36Sopenharmony_ci		if (dai->id == 0)
44862306a36Sopenharmony_ci			ctl &= ~KIRKWOOD_RECCTL_SPDIF_EN;	/* i2s */
44962306a36Sopenharmony_ci		else
45062306a36Sopenharmony_ci			ctl &= ~KIRKWOOD_RECCTL_I2S_EN;		/* spdif */
45162306a36Sopenharmony_ci
45262306a36Sopenharmony_ci		value = ctl & ~KIRKWOOD_RECCTL_ENABLE_MASK;
45362306a36Sopenharmony_ci		writel(value, priv->io + KIRKWOOD_RECCTL);
45462306a36Sopenharmony_ci
45562306a36Sopenharmony_ci		/* enable interrupts */
45662306a36Sopenharmony_ci		value = readl(priv->io + KIRKWOOD_INT_MASK);
45762306a36Sopenharmony_ci		value |= KIRKWOOD_INT_CAUSE_REC_BYTES;
45862306a36Sopenharmony_ci		writel(value, priv->io + KIRKWOOD_INT_MASK);
45962306a36Sopenharmony_ci
46062306a36Sopenharmony_ci		/* enable record */
46162306a36Sopenharmony_ci		writel(ctl, priv->io + KIRKWOOD_RECCTL);
46262306a36Sopenharmony_ci		break;
46362306a36Sopenharmony_ci
46462306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_STOP:
46562306a36Sopenharmony_ci		/* stop audio, disable interrupts */
46662306a36Sopenharmony_ci		value = readl(priv->io + KIRKWOOD_RECCTL);
46762306a36Sopenharmony_ci		value |= KIRKWOOD_RECCTL_PAUSE | KIRKWOOD_RECCTL_MUTE;
46862306a36Sopenharmony_ci		writel(value, priv->io + KIRKWOOD_RECCTL);
46962306a36Sopenharmony_ci
47062306a36Sopenharmony_ci		value = readl(priv->io + KIRKWOOD_INT_MASK);
47162306a36Sopenharmony_ci		value &= ~KIRKWOOD_INT_CAUSE_REC_BYTES;
47262306a36Sopenharmony_ci		writel(value, priv->io + KIRKWOOD_INT_MASK);
47362306a36Sopenharmony_ci
47462306a36Sopenharmony_ci		/* disable all records */
47562306a36Sopenharmony_ci		value = readl(priv->io + KIRKWOOD_RECCTL);
47662306a36Sopenharmony_ci		value &= ~KIRKWOOD_RECCTL_ENABLE_MASK;
47762306a36Sopenharmony_ci		writel(value, priv->io + KIRKWOOD_RECCTL);
47862306a36Sopenharmony_ci		break;
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
48162306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_SUSPEND:
48262306a36Sopenharmony_ci		value = readl(priv->io + KIRKWOOD_RECCTL);
48362306a36Sopenharmony_ci		value |= KIRKWOOD_RECCTL_PAUSE | KIRKWOOD_RECCTL_MUTE;
48462306a36Sopenharmony_ci		writel(value, priv->io + KIRKWOOD_RECCTL);
48562306a36Sopenharmony_ci		break;
48662306a36Sopenharmony_ci
48762306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_RESUME:
48862306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
48962306a36Sopenharmony_ci		value = readl(priv->io + KIRKWOOD_RECCTL);
49062306a36Sopenharmony_ci		value &= ~(KIRKWOOD_RECCTL_PAUSE | KIRKWOOD_RECCTL_MUTE);
49162306a36Sopenharmony_ci		writel(value, priv->io + KIRKWOOD_RECCTL);
49262306a36Sopenharmony_ci		break;
49362306a36Sopenharmony_ci
49462306a36Sopenharmony_ci	default:
49562306a36Sopenharmony_ci		return -EINVAL;
49662306a36Sopenharmony_ci	}
49762306a36Sopenharmony_ci
49862306a36Sopenharmony_ci	return 0;
49962306a36Sopenharmony_ci}
50062306a36Sopenharmony_ci
50162306a36Sopenharmony_cistatic int kirkwood_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
50262306a36Sopenharmony_ci			       struct snd_soc_dai *dai)
50362306a36Sopenharmony_ci{
50462306a36Sopenharmony_ci	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
50562306a36Sopenharmony_ci		return kirkwood_i2s_play_trigger(substream, cmd, dai);
50662306a36Sopenharmony_ci	else
50762306a36Sopenharmony_ci		return kirkwood_i2s_rec_trigger(substream, cmd, dai);
50862306a36Sopenharmony_ci
50962306a36Sopenharmony_ci	return 0;
51062306a36Sopenharmony_ci}
51162306a36Sopenharmony_ci
51262306a36Sopenharmony_cistatic int kirkwood_i2s_init(struct kirkwood_dma_data *priv)
51362306a36Sopenharmony_ci{
51462306a36Sopenharmony_ci	unsigned long value;
51562306a36Sopenharmony_ci	unsigned int reg_data;
51662306a36Sopenharmony_ci
51762306a36Sopenharmony_ci	/* put system in a "safe" state : */
51862306a36Sopenharmony_ci	/* disable audio interrupts */
51962306a36Sopenharmony_ci	writel(0xffffffff, priv->io + KIRKWOOD_INT_CAUSE);
52062306a36Sopenharmony_ci	writel(0, priv->io + KIRKWOOD_INT_MASK);
52162306a36Sopenharmony_ci
52262306a36Sopenharmony_ci	reg_data = readl(priv->io + 0x1200);
52362306a36Sopenharmony_ci	reg_data &= (~(0x333FF8));
52462306a36Sopenharmony_ci	reg_data |= 0x111D18;
52562306a36Sopenharmony_ci	writel(reg_data, priv->io + 0x1200);
52662306a36Sopenharmony_ci
52762306a36Sopenharmony_ci	msleep(500);
52862306a36Sopenharmony_ci
52962306a36Sopenharmony_ci	reg_data = readl(priv->io + 0x1200);
53062306a36Sopenharmony_ci	reg_data &= (~(0x333FF8));
53162306a36Sopenharmony_ci	reg_data |= 0x111D18;
53262306a36Sopenharmony_ci	writel(reg_data, priv->io + 0x1200);
53362306a36Sopenharmony_ci
53462306a36Sopenharmony_ci	/* disable playback/record */
53562306a36Sopenharmony_ci	value = readl(priv->io + KIRKWOOD_PLAYCTL);
53662306a36Sopenharmony_ci	value &= ~KIRKWOOD_PLAYCTL_ENABLE_MASK;
53762306a36Sopenharmony_ci	writel(value, priv->io + KIRKWOOD_PLAYCTL);
53862306a36Sopenharmony_ci
53962306a36Sopenharmony_ci	value = readl(priv->io + KIRKWOOD_RECCTL);
54062306a36Sopenharmony_ci	value &= ~KIRKWOOD_RECCTL_ENABLE_MASK;
54162306a36Sopenharmony_ci	writel(value, priv->io + KIRKWOOD_RECCTL);
54262306a36Sopenharmony_ci
54362306a36Sopenharmony_ci	return 0;
54462306a36Sopenharmony_ci
54562306a36Sopenharmony_ci}
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_cistatic const struct snd_soc_dai_ops kirkwood_i2s_dai_ops = {
54862306a36Sopenharmony_ci	.startup	= kirkwood_i2s_startup,
54962306a36Sopenharmony_ci	.trigger	= kirkwood_i2s_trigger,
55062306a36Sopenharmony_ci	.hw_params      = kirkwood_i2s_hw_params,
55162306a36Sopenharmony_ci	.set_fmt        = kirkwood_i2s_set_fmt,
55262306a36Sopenharmony_ci};
55362306a36Sopenharmony_ci
55462306a36Sopenharmony_cistatic struct snd_soc_dai_driver kirkwood_i2s_dai[2] = {
55562306a36Sopenharmony_ci    {
55662306a36Sopenharmony_ci	.name = "i2s",
55762306a36Sopenharmony_ci	.id = 0,
55862306a36Sopenharmony_ci	.playback = {
55962306a36Sopenharmony_ci		.channels_min = 1,
56062306a36Sopenharmony_ci		.channels_max = 2,
56162306a36Sopenharmony_ci		.rates = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |
56262306a36Sopenharmony_ci				SNDRV_PCM_RATE_96000,
56362306a36Sopenharmony_ci		.formats = KIRKWOOD_I2S_FORMATS,
56462306a36Sopenharmony_ci	},
56562306a36Sopenharmony_ci	.capture = {
56662306a36Sopenharmony_ci		.channels_min = 1,
56762306a36Sopenharmony_ci		.channels_max = 2,
56862306a36Sopenharmony_ci		.rates = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |
56962306a36Sopenharmony_ci				SNDRV_PCM_RATE_96000,
57062306a36Sopenharmony_ci		.formats = KIRKWOOD_I2S_FORMATS,
57162306a36Sopenharmony_ci	},
57262306a36Sopenharmony_ci	.ops = &kirkwood_i2s_dai_ops,
57362306a36Sopenharmony_ci    },
57462306a36Sopenharmony_ci    {
57562306a36Sopenharmony_ci	.name = "spdif",
57662306a36Sopenharmony_ci	.id = 1,
57762306a36Sopenharmony_ci	.playback = {
57862306a36Sopenharmony_ci		.channels_min = 1,
57962306a36Sopenharmony_ci		.channels_max = 2,
58062306a36Sopenharmony_ci		.rates = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |
58162306a36Sopenharmony_ci				SNDRV_PCM_RATE_96000,
58262306a36Sopenharmony_ci		.formats = KIRKWOOD_SPDIF_FORMATS,
58362306a36Sopenharmony_ci	},
58462306a36Sopenharmony_ci	.capture = {
58562306a36Sopenharmony_ci		.channels_min = 1,
58662306a36Sopenharmony_ci		.channels_max = 2,
58762306a36Sopenharmony_ci		.rates = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |
58862306a36Sopenharmony_ci				SNDRV_PCM_RATE_96000,
58962306a36Sopenharmony_ci		.formats = KIRKWOOD_SPDIF_FORMATS,
59062306a36Sopenharmony_ci	},
59162306a36Sopenharmony_ci	.ops = &kirkwood_i2s_dai_ops,
59262306a36Sopenharmony_ci    },
59362306a36Sopenharmony_ci};
59462306a36Sopenharmony_ci
59562306a36Sopenharmony_cistatic struct snd_soc_dai_driver kirkwood_i2s_dai_extclk[2] = {
59662306a36Sopenharmony_ci    {
59762306a36Sopenharmony_ci	.name = "i2s",
59862306a36Sopenharmony_ci	.id = 0,
59962306a36Sopenharmony_ci	.playback = {
60062306a36Sopenharmony_ci		.channels_min = 1,
60162306a36Sopenharmony_ci		.channels_max = 2,
60262306a36Sopenharmony_ci		.rates = SNDRV_PCM_RATE_CONTINUOUS,
60362306a36Sopenharmony_ci		.rate_min = 5512,
60462306a36Sopenharmony_ci		.rate_max = 192000,
60562306a36Sopenharmony_ci		.formats = KIRKWOOD_I2S_FORMATS,
60662306a36Sopenharmony_ci	},
60762306a36Sopenharmony_ci	.capture = {
60862306a36Sopenharmony_ci		.channels_min = 1,
60962306a36Sopenharmony_ci		.channels_max = 2,
61062306a36Sopenharmony_ci		.rates = SNDRV_PCM_RATE_CONTINUOUS,
61162306a36Sopenharmony_ci		.rate_min = 5512,
61262306a36Sopenharmony_ci		.rate_max = 192000,
61362306a36Sopenharmony_ci		.formats = KIRKWOOD_I2S_FORMATS,
61462306a36Sopenharmony_ci	},
61562306a36Sopenharmony_ci	.ops = &kirkwood_i2s_dai_ops,
61662306a36Sopenharmony_ci    },
61762306a36Sopenharmony_ci    {
61862306a36Sopenharmony_ci	.name = "spdif",
61962306a36Sopenharmony_ci	.id = 1,
62062306a36Sopenharmony_ci	.playback = {
62162306a36Sopenharmony_ci		.channels_min = 1,
62262306a36Sopenharmony_ci		.channels_max = 2,
62362306a36Sopenharmony_ci		.rates = SNDRV_PCM_RATE_CONTINUOUS,
62462306a36Sopenharmony_ci		.rate_min = 5512,
62562306a36Sopenharmony_ci		.rate_max = 192000,
62662306a36Sopenharmony_ci		.formats = KIRKWOOD_SPDIF_FORMATS,
62762306a36Sopenharmony_ci	},
62862306a36Sopenharmony_ci	.capture = {
62962306a36Sopenharmony_ci		.channels_min = 1,
63062306a36Sopenharmony_ci		.channels_max = 2,
63162306a36Sopenharmony_ci		.rates = SNDRV_PCM_RATE_CONTINUOUS,
63262306a36Sopenharmony_ci		.rate_min = 5512,
63362306a36Sopenharmony_ci		.rate_max = 192000,
63462306a36Sopenharmony_ci		.formats = KIRKWOOD_SPDIF_FORMATS,
63562306a36Sopenharmony_ci	},
63662306a36Sopenharmony_ci	.ops = &kirkwood_i2s_dai_ops,
63762306a36Sopenharmony_ci    },
63862306a36Sopenharmony_ci};
63962306a36Sopenharmony_ci
64062306a36Sopenharmony_cistatic int kirkwood_i2s_dev_probe(struct platform_device *pdev)
64162306a36Sopenharmony_ci{
64262306a36Sopenharmony_ci	struct kirkwood_asoc_platform_data *data = pdev->dev.platform_data;
64362306a36Sopenharmony_ci	struct snd_soc_dai_driver *soc_dai = kirkwood_i2s_dai;
64462306a36Sopenharmony_ci	struct kirkwood_dma_data *priv;
64562306a36Sopenharmony_ci	struct device_node *np = pdev->dev.of_node;
64662306a36Sopenharmony_ci	int err;
64762306a36Sopenharmony_ci
64862306a36Sopenharmony_ci	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
64962306a36Sopenharmony_ci	if (!priv)
65062306a36Sopenharmony_ci		return -ENOMEM;
65162306a36Sopenharmony_ci
65262306a36Sopenharmony_ci	dev_set_drvdata(&pdev->dev, priv);
65362306a36Sopenharmony_ci
65462306a36Sopenharmony_ci	if (of_device_is_compatible(np, "marvell,armada-380-audio"))
65562306a36Sopenharmony_ci		priv->io = devm_platform_ioremap_resource_byname(pdev, "i2s_regs");
65662306a36Sopenharmony_ci	else
65762306a36Sopenharmony_ci		priv->io = devm_platform_ioremap_resource(pdev, 0);
65862306a36Sopenharmony_ci	if (IS_ERR(priv->io))
65962306a36Sopenharmony_ci		return PTR_ERR(priv->io);
66062306a36Sopenharmony_ci
66162306a36Sopenharmony_ci	priv->irq = platform_get_irq(pdev, 0);
66262306a36Sopenharmony_ci	if (priv->irq < 0)
66362306a36Sopenharmony_ci		return priv->irq;
66462306a36Sopenharmony_ci
66562306a36Sopenharmony_ci	if (of_device_is_compatible(np, "marvell,armada-380-audio")) {
66662306a36Sopenharmony_ci		err = armada_38x_i2s_init_quirk(pdev, priv, soc_dai);
66762306a36Sopenharmony_ci		if (err < 0)
66862306a36Sopenharmony_ci			return err;
66962306a36Sopenharmony_ci		/* Set initial pll frequency */
67062306a36Sopenharmony_ci		armada_38x_set_pll(priv->pll_config, 44100);
67162306a36Sopenharmony_ci	}
67262306a36Sopenharmony_ci
67362306a36Sopenharmony_ci	if (np) {
67462306a36Sopenharmony_ci		priv->burst = 128;		/* might be 32 or 128 */
67562306a36Sopenharmony_ci	} else if (data) {
67662306a36Sopenharmony_ci		priv->burst = data->burst;
67762306a36Sopenharmony_ci	} else {
67862306a36Sopenharmony_ci		dev_err(&pdev->dev, "no DT nor platform data ?!\n");
67962306a36Sopenharmony_ci		return -EINVAL;
68062306a36Sopenharmony_ci	}
68162306a36Sopenharmony_ci
68262306a36Sopenharmony_ci	priv->clk = devm_clk_get(&pdev->dev, np ? "internal" : NULL);
68362306a36Sopenharmony_ci	if (IS_ERR(priv->clk)) {
68462306a36Sopenharmony_ci		dev_err(&pdev->dev, "no clock\n");
68562306a36Sopenharmony_ci		return PTR_ERR(priv->clk);
68662306a36Sopenharmony_ci	}
68762306a36Sopenharmony_ci
68862306a36Sopenharmony_ci	priv->extclk = devm_clk_get(&pdev->dev, "extclk");
68962306a36Sopenharmony_ci	if (IS_ERR(priv->extclk)) {
69062306a36Sopenharmony_ci		if (PTR_ERR(priv->extclk) == -EPROBE_DEFER)
69162306a36Sopenharmony_ci			return -EPROBE_DEFER;
69262306a36Sopenharmony_ci	} else {
69362306a36Sopenharmony_ci		if (clk_is_match(priv->extclk, priv->clk)) {
69462306a36Sopenharmony_ci			devm_clk_put(&pdev->dev, priv->extclk);
69562306a36Sopenharmony_ci			priv->extclk = ERR_PTR(-EINVAL);
69662306a36Sopenharmony_ci		} else {
69762306a36Sopenharmony_ci			dev_info(&pdev->dev, "found external clock\n");
69862306a36Sopenharmony_ci			clk_prepare_enable(priv->extclk);
69962306a36Sopenharmony_ci			soc_dai = kirkwood_i2s_dai_extclk;
70062306a36Sopenharmony_ci		}
70162306a36Sopenharmony_ci	}
70262306a36Sopenharmony_ci
70362306a36Sopenharmony_ci	err = clk_prepare_enable(priv->clk);
70462306a36Sopenharmony_ci	if (err < 0)
70562306a36Sopenharmony_ci		return err;
70662306a36Sopenharmony_ci
70762306a36Sopenharmony_ci	/* Some sensible defaults - this reflects the powerup values */
70862306a36Sopenharmony_ci	priv->ctl_play = KIRKWOOD_PLAYCTL_SIZE_24;
70962306a36Sopenharmony_ci	priv->ctl_rec = KIRKWOOD_RECCTL_SIZE_24;
71062306a36Sopenharmony_ci
71162306a36Sopenharmony_ci	/* Select the burst size */
71262306a36Sopenharmony_ci	if (priv->burst == 32) {
71362306a36Sopenharmony_ci		priv->ctl_play |= KIRKWOOD_PLAYCTL_BURST_32;
71462306a36Sopenharmony_ci		priv->ctl_rec |= KIRKWOOD_RECCTL_BURST_32;
71562306a36Sopenharmony_ci	} else {
71662306a36Sopenharmony_ci		priv->ctl_play |= KIRKWOOD_PLAYCTL_BURST_128;
71762306a36Sopenharmony_ci		priv->ctl_rec |= KIRKWOOD_RECCTL_BURST_128;
71862306a36Sopenharmony_ci	}
71962306a36Sopenharmony_ci
72062306a36Sopenharmony_ci	err = snd_soc_register_component(&pdev->dev, &kirkwood_soc_component,
72162306a36Sopenharmony_ci					 soc_dai, 2);
72262306a36Sopenharmony_ci	if (err) {
72362306a36Sopenharmony_ci		dev_err(&pdev->dev, "snd_soc_register_component failed\n");
72462306a36Sopenharmony_ci		goto err_component;
72562306a36Sopenharmony_ci	}
72662306a36Sopenharmony_ci
72762306a36Sopenharmony_ci	kirkwood_i2s_init(priv);
72862306a36Sopenharmony_ci
72962306a36Sopenharmony_ci	return 0;
73062306a36Sopenharmony_ci
73162306a36Sopenharmony_ci err_component:
73262306a36Sopenharmony_ci	if (!IS_ERR(priv->extclk))
73362306a36Sopenharmony_ci		clk_disable_unprepare(priv->extclk);
73462306a36Sopenharmony_ci	clk_disable_unprepare(priv->clk);
73562306a36Sopenharmony_ci
73662306a36Sopenharmony_ci	return err;
73762306a36Sopenharmony_ci}
73862306a36Sopenharmony_ci
73962306a36Sopenharmony_cistatic void kirkwood_i2s_dev_remove(struct platform_device *pdev)
74062306a36Sopenharmony_ci{
74162306a36Sopenharmony_ci	struct kirkwood_dma_data *priv = dev_get_drvdata(&pdev->dev);
74262306a36Sopenharmony_ci
74362306a36Sopenharmony_ci	snd_soc_unregister_component(&pdev->dev);
74462306a36Sopenharmony_ci	if (!IS_ERR(priv->extclk))
74562306a36Sopenharmony_ci		clk_disable_unprepare(priv->extclk);
74662306a36Sopenharmony_ci	clk_disable_unprepare(priv->clk);
74762306a36Sopenharmony_ci}
74862306a36Sopenharmony_ci
74962306a36Sopenharmony_ci#ifdef CONFIG_OF
75062306a36Sopenharmony_cistatic const struct of_device_id mvebu_audio_of_match[] = {
75162306a36Sopenharmony_ci	{ .compatible = "marvell,kirkwood-audio" },
75262306a36Sopenharmony_ci	{ .compatible = "marvell,dove-audio" },
75362306a36Sopenharmony_ci	{ .compatible = "marvell,armada370-audio" },
75462306a36Sopenharmony_ci	{ .compatible = "marvell,armada-380-audio" },
75562306a36Sopenharmony_ci	{ }
75662306a36Sopenharmony_ci};
75762306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, mvebu_audio_of_match);
75862306a36Sopenharmony_ci#endif
75962306a36Sopenharmony_ci
76062306a36Sopenharmony_cistatic struct platform_driver kirkwood_i2s_driver = {
76162306a36Sopenharmony_ci	.probe  = kirkwood_i2s_dev_probe,
76262306a36Sopenharmony_ci	.remove_new = kirkwood_i2s_dev_remove,
76362306a36Sopenharmony_ci	.driver = {
76462306a36Sopenharmony_ci		.name = DRV_NAME,
76562306a36Sopenharmony_ci		.of_match_table = of_match_ptr(mvebu_audio_of_match),
76662306a36Sopenharmony_ci	},
76762306a36Sopenharmony_ci};
76862306a36Sopenharmony_ci
76962306a36Sopenharmony_cimodule_platform_driver(kirkwood_i2s_driver);
77062306a36Sopenharmony_ci
77162306a36Sopenharmony_ci/* Module information */
77262306a36Sopenharmony_ciMODULE_AUTHOR("Arnaud Patard, <arnaud.patard@rtp-net.org>");
77362306a36Sopenharmony_ciMODULE_DESCRIPTION("Kirkwood I2S SoC Interface");
77462306a36Sopenharmony_ciMODULE_LICENSE("GPL");
77562306a36Sopenharmony_ciMODULE_ALIAS("platform:mvebu-audio");
776