18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * kirkwood-i2s.c
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * (c) 2010 Arnaud Patard <apatard@mandriva.com>
68c2ecf20Sopenharmony_ci * (c) 2010 Arnaud Patard <arnaud.patard@rtp-net.org>
78c2ecf20Sopenharmony_ci */
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci#include <linux/init.h>
108c2ecf20Sopenharmony_ci#include <linux/module.h>
118c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
128c2ecf20Sopenharmony_ci#include <linux/io.h>
138c2ecf20Sopenharmony_ci#include <linux/slab.h>
148c2ecf20Sopenharmony_ci#include <linux/mbus.h>
158c2ecf20Sopenharmony_ci#include <linux/delay.h>
168c2ecf20Sopenharmony_ci#include <linux/clk.h>
178c2ecf20Sopenharmony_ci#include <sound/pcm.h>
188c2ecf20Sopenharmony_ci#include <sound/pcm_params.h>
198c2ecf20Sopenharmony_ci#include <sound/soc.h>
208c2ecf20Sopenharmony_ci#include <linux/platform_data/asoc-kirkwood.h>
218c2ecf20Sopenharmony_ci#include <linux/of.h>
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci#include "kirkwood.h"
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci#define KIRKWOOD_I2S_FORMATS \
268c2ecf20Sopenharmony_ci	(SNDRV_PCM_FMTBIT_S16_LE | \
278c2ecf20Sopenharmony_ci	 SNDRV_PCM_FMTBIT_S24_LE | \
288c2ecf20Sopenharmony_ci	 SNDRV_PCM_FMTBIT_S32_LE)
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci#define KIRKWOOD_SPDIF_FORMATS \
318c2ecf20Sopenharmony_ci	(SNDRV_PCM_FMTBIT_S16_LE | \
328c2ecf20Sopenharmony_ci	 SNDRV_PCM_FMTBIT_S24_LE)
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_cistatic int kirkwood_i2s_set_fmt(struct snd_soc_dai *cpu_dai,
358c2ecf20Sopenharmony_ci		unsigned int fmt)
368c2ecf20Sopenharmony_ci{
378c2ecf20Sopenharmony_ci	struct kirkwood_dma_data *priv = snd_soc_dai_get_drvdata(cpu_dai);
388c2ecf20Sopenharmony_ci	unsigned long mask;
398c2ecf20Sopenharmony_ci	unsigned long value;
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
428c2ecf20Sopenharmony_ci	case SND_SOC_DAIFMT_RIGHT_J:
438c2ecf20Sopenharmony_ci		mask = KIRKWOOD_I2S_CTL_RJ;
448c2ecf20Sopenharmony_ci		break;
458c2ecf20Sopenharmony_ci	case SND_SOC_DAIFMT_LEFT_J:
468c2ecf20Sopenharmony_ci		mask = KIRKWOOD_I2S_CTL_LJ;
478c2ecf20Sopenharmony_ci		break;
488c2ecf20Sopenharmony_ci	case SND_SOC_DAIFMT_I2S:
498c2ecf20Sopenharmony_ci		mask = KIRKWOOD_I2S_CTL_I2S;
508c2ecf20Sopenharmony_ci		break;
518c2ecf20Sopenharmony_ci	default:
528c2ecf20Sopenharmony_ci		return -EINVAL;
538c2ecf20Sopenharmony_ci	}
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci	/*
568c2ecf20Sopenharmony_ci	 * Set same format for playback and record
578c2ecf20Sopenharmony_ci	 * This avoids some troubles.
588c2ecf20Sopenharmony_ci	 */
598c2ecf20Sopenharmony_ci	value = readl(priv->io+KIRKWOOD_I2S_PLAYCTL);
608c2ecf20Sopenharmony_ci	value &= ~KIRKWOOD_I2S_CTL_JUST_MASK;
618c2ecf20Sopenharmony_ci	value |= mask;
628c2ecf20Sopenharmony_ci	writel(value, priv->io+KIRKWOOD_I2S_PLAYCTL);
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci	value = readl(priv->io+KIRKWOOD_I2S_RECCTL);
658c2ecf20Sopenharmony_ci	value &= ~KIRKWOOD_I2S_CTL_JUST_MASK;
668c2ecf20Sopenharmony_ci	value |= mask;
678c2ecf20Sopenharmony_ci	writel(value, priv->io+KIRKWOOD_I2S_RECCTL);
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci	return 0;
708c2ecf20Sopenharmony_ci}
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_cistatic inline void kirkwood_set_dco(void __iomem *io, unsigned long rate)
738c2ecf20Sopenharmony_ci{
748c2ecf20Sopenharmony_ci	unsigned long value;
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci	value = KIRKWOOD_DCO_CTL_OFFSET_0;
778c2ecf20Sopenharmony_ci	switch (rate) {
788c2ecf20Sopenharmony_ci	default:
798c2ecf20Sopenharmony_ci	case 44100:
808c2ecf20Sopenharmony_ci		value |= KIRKWOOD_DCO_CTL_FREQ_11;
818c2ecf20Sopenharmony_ci		break;
828c2ecf20Sopenharmony_ci	case 48000:
838c2ecf20Sopenharmony_ci		value |= KIRKWOOD_DCO_CTL_FREQ_12;
848c2ecf20Sopenharmony_ci		break;
858c2ecf20Sopenharmony_ci	case 96000:
868c2ecf20Sopenharmony_ci		value |= KIRKWOOD_DCO_CTL_FREQ_24;
878c2ecf20Sopenharmony_ci		break;
888c2ecf20Sopenharmony_ci	}
898c2ecf20Sopenharmony_ci	writel(value, io + KIRKWOOD_DCO_CTL);
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci	/* wait for dco locked */
928c2ecf20Sopenharmony_ci	do {
938c2ecf20Sopenharmony_ci		cpu_relax();
948c2ecf20Sopenharmony_ci		value = readl(io + KIRKWOOD_DCO_SPCR_STATUS);
958c2ecf20Sopenharmony_ci		value &= KIRKWOOD_DCO_SPCR_STATUS_DCO_LOCK;
968c2ecf20Sopenharmony_ci	} while (value == 0);
978c2ecf20Sopenharmony_ci}
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_cistatic void kirkwood_set_rate(struct snd_soc_dai *dai,
1008c2ecf20Sopenharmony_ci	struct kirkwood_dma_data *priv, unsigned long rate)
1018c2ecf20Sopenharmony_ci{
1028c2ecf20Sopenharmony_ci	uint32_t clks_ctrl;
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci	if (IS_ERR(priv->extclk)) {
1058c2ecf20Sopenharmony_ci		/* use internal dco for the supported rates
1068c2ecf20Sopenharmony_ci		 * defined in kirkwood_i2s_dai */
1078c2ecf20Sopenharmony_ci		dev_dbg(dai->dev, "%s: dco set rate = %lu\n",
1088c2ecf20Sopenharmony_ci			__func__, rate);
1098c2ecf20Sopenharmony_ci		kirkwood_set_dco(priv->io, rate);
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci		clks_ctrl = KIRKWOOD_MCLK_SOURCE_DCO;
1128c2ecf20Sopenharmony_ci	} else {
1138c2ecf20Sopenharmony_ci		/* use the external clock for the other rates
1148c2ecf20Sopenharmony_ci		 * defined in kirkwood_i2s_dai_extclk */
1158c2ecf20Sopenharmony_ci		dev_dbg(dai->dev, "%s: extclk set rate = %lu -> %lu\n",
1168c2ecf20Sopenharmony_ci			__func__, rate, 256 * rate);
1178c2ecf20Sopenharmony_ci		clk_set_rate(priv->extclk, 256 * rate);
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ci		clks_ctrl = KIRKWOOD_MCLK_SOURCE_EXTCLK;
1208c2ecf20Sopenharmony_ci	}
1218c2ecf20Sopenharmony_ci	writel(clks_ctrl, priv->io + KIRKWOOD_CLOCKS_CTRL);
1228c2ecf20Sopenharmony_ci}
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_cistatic int kirkwood_i2s_startup(struct snd_pcm_substream *substream,
1258c2ecf20Sopenharmony_ci		struct snd_soc_dai *dai)
1268c2ecf20Sopenharmony_ci{
1278c2ecf20Sopenharmony_ci	struct kirkwood_dma_data *priv = snd_soc_dai_get_drvdata(dai);
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci	snd_soc_dai_set_dma_data(dai, substream, priv);
1308c2ecf20Sopenharmony_ci	return 0;
1318c2ecf20Sopenharmony_ci}
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_cistatic int kirkwood_i2s_hw_params(struct snd_pcm_substream *substream,
1348c2ecf20Sopenharmony_ci				 struct snd_pcm_hw_params *params,
1358c2ecf20Sopenharmony_ci				 struct snd_soc_dai *dai)
1368c2ecf20Sopenharmony_ci{
1378c2ecf20Sopenharmony_ci	struct kirkwood_dma_data *priv = snd_soc_dai_get_drvdata(dai);
1388c2ecf20Sopenharmony_ci	uint32_t ctl_play, ctl_rec;
1398c2ecf20Sopenharmony_ci	unsigned int i2s_reg;
1408c2ecf20Sopenharmony_ci	unsigned long i2s_value;
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
1438c2ecf20Sopenharmony_ci		i2s_reg = KIRKWOOD_I2S_PLAYCTL;
1448c2ecf20Sopenharmony_ci	} else {
1458c2ecf20Sopenharmony_ci		i2s_reg = KIRKWOOD_I2S_RECCTL;
1468c2ecf20Sopenharmony_ci	}
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci	kirkwood_set_rate(dai, priv, params_rate(params));
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci	i2s_value = readl(priv->io+i2s_reg);
1518c2ecf20Sopenharmony_ci	i2s_value &= ~KIRKWOOD_I2S_CTL_SIZE_MASK;
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ci	/*
1548c2ecf20Sopenharmony_ci	 * Size settings in play/rec i2s control regs and play/rec control
1558c2ecf20Sopenharmony_ci	 * regs must be the same.
1568c2ecf20Sopenharmony_ci	 */
1578c2ecf20Sopenharmony_ci	switch (params_format(params)) {
1588c2ecf20Sopenharmony_ci	case SNDRV_PCM_FORMAT_S16_LE:
1598c2ecf20Sopenharmony_ci		i2s_value |= KIRKWOOD_I2S_CTL_SIZE_16;
1608c2ecf20Sopenharmony_ci		ctl_play = KIRKWOOD_PLAYCTL_SIZE_16_C |
1618c2ecf20Sopenharmony_ci			   KIRKWOOD_PLAYCTL_I2S_EN |
1628c2ecf20Sopenharmony_ci			   KIRKWOOD_PLAYCTL_SPDIF_EN;
1638c2ecf20Sopenharmony_ci		ctl_rec = KIRKWOOD_RECCTL_SIZE_16_C |
1648c2ecf20Sopenharmony_ci			  KIRKWOOD_RECCTL_I2S_EN |
1658c2ecf20Sopenharmony_ci			  KIRKWOOD_RECCTL_SPDIF_EN;
1668c2ecf20Sopenharmony_ci		break;
1678c2ecf20Sopenharmony_ci	/*
1688c2ecf20Sopenharmony_ci	 * doesn't work... S20_3LE != kirkwood 20bit format ?
1698c2ecf20Sopenharmony_ci	 *
1708c2ecf20Sopenharmony_ci	case SNDRV_PCM_FORMAT_S20_3LE:
1718c2ecf20Sopenharmony_ci		i2s_value |= KIRKWOOD_I2S_CTL_SIZE_20;
1728c2ecf20Sopenharmony_ci		ctl_play = KIRKWOOD_PLAYCTL_SIZE_20 |
1738c2ecf20Sopenharmony_ci			   KIRKWOOD_PLAYCTL_I2S_EN;
1748c2ecf20Sopenharmony_ci		ctl_rec = KIRKWOOD_RECCTL_SIZE_20 |
1758c2ecf20Sopenharmony_ci			  KIRKWOOD_RECCTL_I2S_EN;
1768c2ecf20Sopenharmony_ci		break;
1778c2ecf20Sopenharmony_ci	*/
1788c2ecf20Sopenharmony_ci	case SNDRV_PCM_FORMAT_S24_LE:
1798c2ecf20Sopenharmony_ci		i2s_value |= KIRKWOOD_I2S_CTL_SIZE_24;
1808c2ecf20Sopenharmony_ci		ctl_play = KIRKWOOD_PLAYCTL_SIZE_24 |
1818c2ecf20Sopenharmony_ci			   KIRKWOOD_PLAYCTL_I2S_EN |
1828c2ecf20Sopenharmony_ci			   KIRKWOOD_PLAYCTL_SPDIF_EN;
1838c2ecf20Sopenharmony_ci		ctl_rec = KIRKWOOD_RECCTL_SIZE_24 |
1848c2ecf20Sopenharmony_ci			  KIRKWOOD_RECCTL_I2S_EN |
1858c2ecf20Sopenharmony_ci			  KIRKWOOD_RECCTL_SPDIF_EN;
1868c2ecf20Sopenharmony_ci		break;
1878c2ecf20Sopenharmony_ci	case SNDRV_PCM_FORMAT_S32_LE:
1888c2ecf20Sopenharmony_ci		i2s_value |= KIRKWOOD_I2S_CTL_SIZE_32;
1898c2ecf20Sopenharmony_ci		ctl_play = KIRKWOOD_PLAYCTL_SIZE_32 |
1908c2ecf20Sopenharmony_ci			   KIRKWOOD_PLAYCTL_I2S_EN;
1918c2ecf20Sopenharmony_ci		ctl_rec = KIRKWOOD_RECCTL_SIZE_32 |
1928c2ecf20Sopenharmony_ci			  KIRKWOOD_RECCTL_I2S_EN;
1938c2ecf20Sopenharmony_ci		break;
1948c2ecf20Sopenharmony_ci	default:
1958c2ecf20Sopenharmony_ci		return -EINVAL;
1968c2ecf20Sopenharmony_ci	}
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
1998c2ecf20Sopenharmony_ci		if (params_channels(params) == 1)
2008c2ecf20Sopenharmony_ci			ctl_play |= KIRKWOOD_PLAYCTL_MONO_BOTH;
2018c2ecf20Sopenharmony_ci		else
2028c2ecf20Sopenharmony_ci			ctl_play |= KIRKWOOD_PLAYCTL_MONO_OFF;
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_ci		priv->ctl_play &= ~(KIRKWOOD_PLAYCTL_MONO_MASK |
2058c2ecf20Sopenharmony_ci				    KIRKWOOD_PLAYCTL_ENABLE_MASK |
2068c2ecf20Sopenharmony_ci				    KIRKWOOD_PLAYCTL_SIZE_MASK);
2078c2ecf20Sopenharmony_ci		priv->ctl_play |= ctl_play;
2088c2ecf20Sopenharmony_ci	} else {
2098c2ecf20Sopenharmony_ci		priv->ctl_rec &= ~(KIRKWOOD_RECCTL_ENABLE_MASK |
2108c2ecf20Sopenharmony_ci				   KIRKWOOD_RECCTL_SIZE_MASK);
2118c2ecf20Sopenharmony_ci		priv->ctl_rec |= ctl_rec;
2128c2ecf20Sopenharmony_ci	}
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ci	writel(i2s_value, priv->io+i2s_reg);
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ci	return 0;
2178c2ecf20Sopenharmony_ci}
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_cistatic unsigned kirkwood_i2s_play_mute(unsigned ctl)
2208c2ecf20Sopenharmony_ci{
2218c2ecf20Sopenharmony_ci	if (!(ctl & KIRKWOOD_PLAYCTL_I2S_EN))
2228c2ecf20Sopenharmony_ci		ctl |= KIRKWOOD_PLAYCTL_I2S_MUTE;
2238c2ecf20Sopenharmony_ci	if (!(ctl & KIRKWOOD_PLAYCTL_SPDIF_EN))
2248c2ecf20Sopenharmony_ci		ctl |= KIRKWOOD_PLAYCTL_SPDIF_MUTE;
2258c2ecf20Sopenharmony_ci	return ctl;
2268c2ecf20Sopenharmony_ci}
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_cistatic int kirkwood_i2s_play_trigger(struct snd_pcm_substream *substream,
2298c2ecf20Sopenharmony_ci				int cmd, struct snd_soc_dai *dai)
2308c2ecf20Sopenharmony_ci{
2318c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
2328c2ecf20Sopenharmony_ci	struct kirkwood_dma_data *priv = snd_soc_dai_get_drvdata(dai);
2338c2ecf20Sopenharmony_ci	uint32_t ctl, value;
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_ci	ctl = readl(priv->io + KIRKWOOD_PLAYCTL);
2368c2ecf20Sopenharmony_ci	if ((ctl & KIRKWOOD_PLAYCTL_ENABLE_MASK) == 0) {
2378c2ecf20Sopenharmony_ci		unsigned timeout = 5000;
2388c2ecf20Sopenharmony_ci		/*
2398c2ecf20Sopenharmony_ci		 * The Armada510 spec says that if we enter pause mode, the
2408c2ecf20Sopenharmony_ci		 * busy bit must be read back as clear _twice_.  Make sure
2418c2ecf20Sopenharmony_ci		 * we respect that otherwise we get DMA underruns.
2428c2ecf20Sopenharmony_ci		 */
2438c2ecf20Sopenharmony_ci		do {
2448c2ecf20Sopenharmony_ci			value = ctl;
2458c2ecf20Sopenharmony_ci			ctl = readl(priv->io + KIRKWOOD_PLAYCTL);
2468c2ecf20Sopenharmony_ci			if (!((ctl | value) & KIRKWOOD_PLAYCTL_PLAY_BUSY))
2478c2ecf20Sopenharmony_ci				break;
2488c2ecf20Sopenharmony_ci			udelay(1);
2498c2ecf20Sopenharmony_ci		} while (timeout--);
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_ci		if ((ctl | value) & KIRKWOOD_PLAYCTL_PLAY_BUSY)
2528c2ecf20Sopenharmony_ci			dev_notice(dai->dev, "timed out waiting for busy to deassert: %08x\n",
2538c2ecf20Sopenharmony_ci				   ctl);
2548c2ecf20Sopenharmony_ci	}
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_ci	switch (cmd) {
2578c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_START:
2588c2ecf20Sopenharmony_ci		/* configure */
2598c2ecf20Sopenharmony_ci		ctl = priv->ctl_play;
2608c2ecf20Sopenharmony_ci		if (dai->id == 0)
2618c2ecf20Sopenharmony_ci			ctl &= ~KIRKWOOD_PLAYCTL_SPDIF_EN;	/* i2s */
2628c2ecf20Sopenharmony_ci		else
2638c2ecf20Sopenharmony_ci			ctl &= ~KIRKWOOD_PLAYCTL_I2S_EN;	/* spdif */
2648c2ecf20Sopenharmony_ci		ctl = kirkwood_i2s_play_mute(ctl);
2658c2ecf20Sopenharmony_ci		value = ctl & ~KIRKWOOD_PLAYCTL_ENABLE_MASK;
2668c2ecf20Sopenharmony_ci		writel(value, priv->io + KIRKWOOD_PLAYCTL);
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_ci		/* enable interrupts */
2698c2ecf20Sopenharmony_ci		if (!runtime->no_period_wakeup) {
2708c2ecf20Sopenharmony_ci			value = readl(priv->io + KIRKWOOD_INT_MASK);
2718c2ecf20Sopenharmony_ci			value |= KIRKWOOD_INT_CAUSE_PLAY_BYTES;
2728c2ecf20Sopenharmony_ci			writel(value, priv->io + KIRKWOOD_INT_MASK);
2738c2ecf20Sopenharmony_ci		}
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_ci		/* enable playback */
2768c2ecf20Sopenharmony_ci		writel(ctl, priv->io + KIRKWOOD_PLAYCTL);
2778c2ecf20Sopenharmony_ci		break;
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_STOP:
2808c2ecf20Sopenharmony_ci		/* stop audio, disable interrupts */
2818c2ecf20Sopenharmony_ci		ctl |= KIRKWOOD_PLAYCTL_PAUSE | KIRKWOOD_PLAYCTL_I2S_MUTE |
2828c2ecf20Sopenharmony_ci				KIRKWOOD_PLAYCTL_SPDIF_MUTE;
2838c2ecf20Sopenharmony_ci		writel(ctl, priv->io + KIRKWOOD_PLAYCTL);
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_ci		value = readl(priv->io + KIRKWOOD_INT_MASK);
2868c2ecf20Sopenharmony_ci		value &= ~KIRKWOOD_INT_CAUSE_PLAY_BYTES;
2878c2ecf20Sopenharmony_ci		writel(value, priv->io + KIRKWOOD_INT_MASK);
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_ci		/* disable all playbacks */
2908c2ecf20Sopenharmony_ci		ctl &= ~KIRKWOOD_PLAYCTL_ENABLE_MASK;
2918c2ecf20Sopenharmony_ci		writel(ctl, priv->io + KIRKWOOD_PLAYCTL);
2928c2ecf20Sopenharmony_ci		break;
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
2958c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_SUSPEND:
2968c2ecf20Sopenharmony_ci		ctl |= KIRKWOOD_PLAYCTL_PAUSE | KIRKWOOD_PLAYCTL_I2S_MUTE |
2978c2ecf20Sopenharmony_ci				KIRKWOOD_PLAYCTL_SPDIF_MUTE;
2988c2ecf20Sopenharmony_ci		writel(ctl, priv->io + KIRKWOOD_PLAYCTL);
2998c2ecf20Sopenharmony_ci		break;
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_RESUME:
3028c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
3038c2ecf20Sopenharmony_ci		ctl &= ~(KIRKWOOD_PLAYCTL_PAUSE | KIRKWOOD_PLAYCTL_I2S_MUTE |
3048c2ecf20Sopenharmony_ci				KIRKWOOD_PLAYCTL_SPDIF_MUTE);
3058c2ecf20Sopenharmony_ci		ctl = kirkwood_i2s_play_mute(ctl);
3068c2ecf20Sopenharmony_ci		writel(ctl, priv->io + KIRKWOOD_PLAYCTL);
3078c2ecf20Sopenharmony_ci		break;
3088c2ecf20Sopenharmony_ci
3098c2ecf20Sopenharmony_ci	default:
3108c2ecf20Sopenharmony_ci		return -EINVAL;
3118c2ecf20Sopenharmony_ci	}
3128c2ecf20Sopenharmony_ci
3138c2ecf20Sopenharmony_ci	return 0;
3148c2ecf20Sopenharmony_ci}
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_cistatic int kirkwood_i2s_rec_trigger(struct snd_pcm_substream *substream,
3178c2ecf20Sopenharmony_ci				int cmd, struct snd_soc_dai *dai)
3188c2ecf20Sopenharmony_ci{
3198c2ecf20Sopenharmony_ci	struct kirkwood_dma_data *priv = snd_soc_dai_get_drvdata(dai);
3208c2ecf20Sopenharmony_ci	uint32_t ctl, value;
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_ci	value = readl(priv->io + KIRKWOOD_RECCTL);
3238c2ecf20Sopenharmony_ci
3248c2ecf20Sopenharmony_ci	switch (cmd) {
3258c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_START:
3268c2ecf20Sopenharmony_ci		/* configure */
3278c2ecf20Sopenharmony_ci		ctl = priv->ctl_rec;
3288c2ecf20Sopenharmony_ci		if (dai->id == 0)
3298c2ecf20Sopenharmony_ci			ctl &= ~KIRKWOOD_RECCTL_SPDIF_EN;	/* i2s */
3308c2ecf20Sopenharmony_ci		else
3318c2ecf20Sopenharmony_ci			ctl &= ~KIRKWOOD_RECCTL_I2S_EN;		/* spdif */
3328c2ecf20Sopenharmony_ci
3338c2ecf20Sopenharmony_ci		value = ctl & ~KIRKWOOD_RECCTL_ENABLE_MASK;
3348c2ecf20Sopenharmony_ci		writel(value, priv->io + KIRKWOOD_RECCTL);
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_ci		/* enable interrupts */
3378c2ecf20Sopenharmony_ci		value = readl(priv->io + KIRKWOOD_INT_MASK);
3388c2ecf20Sopenharmony_ci		value |= KIRKWOOD_INT_CAUSE_REC_BYTES;
3398c2ecf20Sopenharmony_ci		writel(value, priv->io + KIRKWOOD_INT_MASK);
3408c2ecf20Sopenharmony_ci
3418c2ecf20Sopenharmony_ci		/* enable record */
3428c2ecf20Sopenharmony_ci		writel(ctl, priv->io + KIRKWOOD_RECCTL);
3438c2ecf20Sopenharmony_ci		break;
3448c2ecf20Sopenharmony_ci
3458c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_STOP:
3468c2ecf20Sopenharmony_ci		/* stop audio, disable interrupts */
3478c2ecf20Sopenharmony_ci		value = readl(priv->io + KIRKWOOD_RECCTL);
3488c2ecf20Sopenharmony_ci		value |= KIRKWOOD_RECCTL_PAUSE | KIRKWOOD_RECCTL_MUTE;
3498c2ecf20Sopenharmony_ci		writel(value, priv->io + KIRKWOOD_RECCTL);
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_ci		value = readl(priv->io + KIRKWOOD_INT_MASK);
3528c2ecf20Sopenharmony_ci		value &= ~KIRKWOOD_INT_CAUSE_REC_BYTES;
3538c2ecf20Sopenharmony_ci		writel(value, priv->io + KIRKWOOD_INT_MASK);
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_ci		/* disable all records */
3568c2ecf20Sopenharmony_ci		value = readl(priv->io + KIRKWOOD_RECCTL);
3578c2ecf20Sopenharmony_ci		value &= ~KIRKWOOD_RECCTL_ENABLE_MASK;
3588c2ecf20Sopenharmony_ci		writel(value, priv->io + KIRKWOOD_RECCTL);
3598c2ecf20Sopenharmony_ci		break;
3608c2ecf20Sopenharmony_ci
3618c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
3628c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_SUSPEND:
3638c2ecf20Sopenharmony_ci		value = readl(priv->io + KIRKWOOD_RECCTL);
3648c2ecf20Sopenharmony_ci		value |= KIRKWOOD_RECCTL_PAUSE | KIRKWOOD_RECCTL_MUTE;
3658c2ecf20Sopenharmony_ci		writel(value, priv->io + KIRKWOOD_RECCTL);
3668c2ecf20Sopenharmony_ci		break;
3678c2ecf20Sopenharmony_ci
3688c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_RESUME:
3698c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
3708c2ecf20Sopenharmony_ci		value = readl(priv->io + KIRKWOOD_RECCTL);
3718c2ecf20Sopenharmony_ci		value &= ~(KIRKWOOD_RECCTL_PAUSE | KIRKWOOD_RECCTL_MUTE);
3728c2ecf20Sopenharmony_ci		writel(value, priv->io + KIRKWOOD_RECCTL);
3738c2ecf20Sopenharmony_ci		break;
3748c2ecf20Sopenharmony_ci
3758c2ecf20Sopenharmony_ci	default:
3768c2ecf20Sopenharmony_ci		return -EINVAL;
3778c2ecf20Sopenharmony_ci	}
3788c2ecf20Sopenharmony_ci
3798c2ecf20Sopenharmony_ci	return 0;
3808c2ecf20Sopenharmony_ci}
3818c2ecf20Sopenharmony_ci
3828c2ecf20Sopenharmony_cistatic int kirkwood_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
3838c2ecf20Sopenharmony_ci			       struct snd_soc_dai *dai)
3848c2ecf20Sopenharmony_ci{
3858c2ecf20Sopenharmony_ci	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
3868c2ecf20Sopenharmony_ci		return kirkwood_i2s_play_trigger(substream, cmd, dai);
3878c2ecf20Sopenharmony_ci	else
3888c2ecf20Sopenharmony_ci		return kirkwood_i2s_rec_trigger(substream, cmd, dai);
3898c2ecf20Sopenharmony_ci
3908c2ecf20Sopenharmony_ci	return 0;
3918c2ecf20Sopenharmony_ci}
3928c2ecf20Sopenharmony_ci
3938c2ecf20Sopenharmony_cistatic int kirkwood_i2s_init(struct kirkwood_dma_data *priv)
3948c2ecf20Sopenharmony_ci{
3958c2ecf20Sopenharmony_ci	unsigned long value;
3968c2ecf20Sopenharmony_ci	unsigned int reg_data;
3978c2ecf20Sopenharmony_ci
3988c2ecf20Sopenharmony_ci	/* put system in a "safe" state : */
3998c2ecf20Sopenharmony_ci	/* disable audio interrupts */
4008c2ecf20Sopenharmony_ci	writel(0xffffffff, priv->io + KIRKWOOD_INT_CAUSE);
4018c2ecf20Sopenharmony_ci	writel(0, priv->io + KIRKWOOD_INT_MASK);
4028c2ecf20Sopenharmony_ci
4038c2ecf20Sopenharmony_ci	reg_data = readl(priv->io + 0x1200);
4048c2ecf20Sopenharmony_ci	reg_data &= (~(0x333FF8));
4058c2ecf20Sopenharmony_ci	reg_data |= 0x111D18;
4068c2ecf20Sopenharmony_ci	writel(reg_data, priv->io + 0x1200);
4078c2ecf20Sopenharmony_ci
4088c2ecf20Sopenharmony_ci	msleep(500);
4098c2ecf20Sopenharmony_ci
4108c2ecf20Sopenharmony_ci	reg_data = readl(priv->io + 0x1200);
4118c2ecf20Sopenharmony_ci	reg_data &= (~(0x333FF8));
4128c2ecf20Sopenharmony_ci	reg_data |= 0x111D18;
4138c2ecf20Sopenharmony_ci	writel(reg_data, priv->io + 0x1200);
4148c2ecf20Sopenharmony_ci
4158c2ecf20Sopenharmony_ci	/* disable playback/record */
4168c2ecf20Sopenharmony_ci	value = readl(priv->io + KIRKWOOD_PLAYCTL);
4178c2ecf20Sopenharmony_ci	value &= ~KIRKWOOD_PLAYCTL_ENABLE_MASK;
4188c2ecf20Sopenharmony_ci	writel(value, priv->io + KIRKWOOD_PLAYCTL);
4198c2ecf20Sopenharmony_ci
4208c2ecf20Sopenharmony_ci	value = readl(priv->io + KIRKWOOD_RECCTL);
4218c2ecf20Sopenharmony_ci	value &= ~KIRKWOOD_RECCTL_ENABLE_MASK;
4228c2ecf20Sopenharmony_ci	writel(value, priv->io + KIRKWOOD_RECCTL);
4238c2ecf20Sopenharmony_ci
4248c2ecf20Sopenharmony_ci	return 0;
4258c2ecf20Sopenharmony_ci
4268c2ecf20Sopenharmony_ci}
4278c2ecf20Sopenharmony_ci
4288c2ecf20Sopenharmony_cistatic const struct snd_soc_dai_ops kirkwood_i2s_dai_ops = {
4298c2ecf20Sopenharmony_ci	.startup	= kirkwood_i2s_startup,
4308c2ecf20Sopenharmony_ci	.trigger	= kirkwood_i2s_trigger,
4318c2ecf20Sopenharmony_ci	.hw_params      = kirkwood_i2s_hw_params,
4328c2ecf20Sopenharmony_ci	.set_fmt        = kirkwood_i2s_set_fmt,
4338c2ecf20Sopenharmony_ci};
4348c2ecf20Sopenharmony_ci
4358c2ecf20Sopenharmony_cistatic struct snd_soc_dai_driver kirkwood_i2s_dai[2] = {
4368c2ecf20Sopenharmony_ci    {
4378c2ecf20Sopenharmony_ci	.name = "i2s",
4388c2ecf20Sopenharmony_ci	.id = 0,
4398c2ecf20Sopenharmony_ci	.playback = {
4408c2ecf20Sopenharmony_ci		.channels_min = 1,
4418c2ecf20Sopenharmony_ci		.channels_max = 2,
4428c2ecf20Sopenharmony_ci		.rates = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |
4438c2ecf20Sopenharmony_ci				SNDRV_PCM_RATE_96000,
4448c2ecf20Sopenharmony_ci		.formats = KIRKWOOD_I2S_FORMATS,
4458c2ecf20Sopenharmony_ci	},
4468c2ecf20Sopenharmony_ci	.capture = {
4478c2ecf20Sopenharmony_ci		.channels_min = 1,
4488c2ecf20Sopenharmony_ci		.channels_max = 2,
4498c2ecf20Sopenharmony_ci		.rates = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |
4508c2ecf20Sopenharmony_ci				SNDRV_PCM_RATE_96000,
4518c2ecf20Sopenharmony_ci		.formats = KIRKWOOD_I2S_FORMATS,
4528c2ecf20Sopenharmony_ci	},
4538c2ecf20Sopenharmony_ci	.ops = &kirkwood_i2s_dai_ops,
4548c2ecf20Sopenharmony_ci    },
4558c2ecf20Sopenharmony_ci    {
4568c2ecf20Sopenharmony_ci	.name = "spdif",
4578c2ecf20Sopenharmony_ci	.id = 1,
4588c2ecf20Sopenharmony_ci	.playback = {
4598c2ecf20Sopenharmony_ci		.channels_min = 1,
4608c2ecf20Sopenharmony_ci		.channels_max = 2,
4618c2ecf20Sopenharmony_ci		.rates = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |
4628c2ecf20Sopenharmony_ci				SNDRV_PCM_RATE_96000,
4638c2ecf20Sopenharmony_ci		.formats = KIRKWOOD_SPDIF_FORMATS,
4648c2ecf20Sopenharmony_ci	},
4658c2ecf20Sopenharmony_ci	.capture = {
4668c2ecf20Sopenharmony_ci		.channels_min = 1,
4678c2ecf20Sopenharmony_ci		.channels_max = 2,
4688c2ecf20Sopenharmony_ci		.rates = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |
4698c2ecf20Sopenharmony_ci				SNDRV_PCM_RATE_96000,
4708c2ecf20Sopenharmony_ci		.formats = KIRKWOOD_SPDIF_FORMATS,
4718c2ecf20Sopenharmony_ci	},
4728c2ecf20Sopenharmony_ci	.ops = &kirkwood_i2s_dai_ops,
4738c2ecf20Sopenharmony_ci    },
4748c2ecf20Sopenharmony_ci};
4758c2ecf20Sopenharmony_ci
4768c2ecf20Sopenharmony_cistatic struct snd_soc_dai_driver kirkwood_i2s_dai_extclk[2] = {
4778c2ecf20Sopenharmony_ci    {
4788c2ecf20Sopenharmony_ci	.name = "i2s",
4798c2ecf20Sopenharmony_ci	.id = 0,
4808c2ecf20Sopenharmony_ci	.playback = {
4818c2ecf20Sopenharmony_ci		.channels_min = 1,
4828c2ecf20Sopenharmony_ci		.channels_max = 2,
4838c2ecf20Sopenharmony_ci		.rates = SNDRV_PCM_RATE_CONTINUOUS,
4848c2ecf20Sopenharmony_ci		.rate_min = 5512,
4858c2ecf20Sopenharmony_ci		.rate_max = 192000,
4868c2ecf20Sopenharmony_ci		.formats = KIRKWOOD_I2S_FORMATS,
4878c2ecf20Sopenharmony_ci	},
4888c2ecf20Sopenharmony_ci	.capture = {
4898c2ecf20Sopenharmony_ci		.channels_min = 1,
4908c2ecf20Sopenharmony_ci		.channels_max = 2,
4918c2ecf20Sopenharmony_ci		.rates = SNDRV_PCM_RATE_CONTINUOUS,
4928c2ecf20Sopenharmony_ci		.rate_min = 5512,
4938c2ecf20Sopenharmony_ci		.rate_max = 192000,
4948c2ecf20Sopenharmony_ci		.formats = KIRKWOOD_I2S_FORMATS,
4958c2ecf20Sopenharmony_ci	},
4968c2ecf20Sopenharmony_ci	.ops = &kirkwood_i2s_dai_ops,
4978c2ecf20Sopenharmony_ci    },
4988c2ecf20Sopenharmony_ci    {
4998c2ecf20Sopenharmony_ci	.name = "spdif",
5008c2ecf20Sopenharmony_ci	.id = 1,
5018c2ecf20Sopenharmony_ci	.playback = {
5028c2ecf20Sopenharmony_ci		.channels_min = 1,
5038c2ecf20Sopenharmony_ci		.channels_max = 2,
5048c2ecf20Sopenharmony_ci		.rates = SNDRV_PCM_RATE_CONTINUOUS,
5058c2ecf20Sopenharmony_ci		.rate_min = 5512,
5068c2ecf20Sopenharmony_ci		.rate_max = 192000,
5078c2ecf20Sopenharmony_ci		.formats = KIRKWOOD_SPDIF_FORMATS,
5088c2ecf20Sopenharmony_ci	},
5098c2ecf20Sopenharmony_ci	.capture = {
5108c2ecf20Sopenharmony_ci		.channels_min = 1,
5118c2ecf20Sopenharmony_ci		.channels_max = 2,
5128c2ecf20Sopenharmony_ci		.rates = SNDRV_PCM_RATE_CONTINUOUS,
5138c2ecf20Sopenharmony_ci		.rate_min = 5512,
5148c2ecf20Sopenharmony_ci		.rate_max = 192000,
5158c2ecf20Sopenharmony_ci		.formats = KIRKWOOD_SPDIF_FORMATS,
5168c2ecf20Sopenharmony_ci	},
5178c2ecf20Sopenharmony_ci	.ops = &kirkwood_i2s_dai_ops,
5188c2ecf20Sopenharmony_ci    },
5198c2ecf20Sopenharmony_ci};
5208c2ecf20Sopenharmony_ci
5218c2ecf20Sopenharmony_cistatic int kirkwood_i2s_dev_probe(struct platform_device *pdev)
5228c2ecf20Sopenharmony_ci{
5238c2ecf20Sopenharmony_ci	struct kirkwood_asoc_platform_data *data = pdev->dev.platform_data;
5248c2ecf20Sopenharmony_ci	struct snd_soc_dai_driver *soc_dai = kirkwood_i2s_dai;
5258c2ecf20Sopenharmony_ci	struct kirkwood_dma_data *priv;
5268c2ecf20Sopenharmony_ci	struct device_node *np = pdev->dev.of_node;
5278c2ecf20Sopenharmony_ci	int err;
5288c2ecf20Sopenharmony_ci
5298c2ecf20Sopenharmony_ci	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
5308c2ecf20Sopenharmony_ci	if (!priv)
5318c2ecf20Sopenharmony_ci		return -ENOMEM;
5328c2ecf20Sopenharmony_ci
5338c2ecf20Sopenharmony_ci	dev_set_drvdata(&pdev->dev, priv);
5348c2ecf20Sopenharmony_ci
5358c2ecf20Sopenharmony_ci	priv->io = devm_platform_ioremap_resource(pdev, 0);
5368c2ecf20Sopenharmony_ci	if (IS_ERR(priv->io))
5378c2ecf20Sopenharmony_ci		return PTR_ERR(priv->io);
5388c2ecf20Sopenharmony_ci
5398c2ecf20Sopenharmony_ci	priv->irq = platform_get_irq(pdev, 0);
5408c2ecf20Sopenharmony_ci	if (priv->irq < 0)
5418c2ecf20Sopenharmony_ci		return priv->irq;
5428c2ecf20Sopenharmony_ci
5438c2ecf20Sopenharmony_ci	if (np) {
5448c2ecf20Sopenharmony_ci		priv->burst = 128;		/* might be 32 or 128 */
5458c2ecf20Sopenharmony_ci	} else if (data) {
5468c2ecf20Sopenharmony_ci		priv->burst = data->burst;
5478c2ecf20Sopenharmony_ci	} else {
5488c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "no DT nor platform data ?!\n");
5498c2ecf20Sopenharmony_ci		return -EINVAL;
5508c2ecf20Sopenharmony_ci	}
5518c2ecf20Sopenharmony_ci
5528c2ecf20Sopenharmony_ci	priv->clk = devm_clk_get(&pdev->dev, np ? "internal" : NULL);
5538c2ecf20Sopenharmony_ci	if (IS_ERR(priv->clk)) {
5548c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "no clock\n");
5558c2ecf20Sopenharmony_ci		return PTR_ERR(priv->clk);
5568c2ecf20Sopenharmony_ci	}
5578c2ecf20Sopenharmony_ci
5588c2ecf20Sopenharmony_ci	priv->extclk = devm_clk_get(&pdev->dev, "extclk");
5598c2ecf20Sopenharmony_ci	if (IS_ERR(priv->extclk)) {
5608c2ecf20Sopenharmony_ci		if (PTR_ERR(priv->extclk) == -EPROBE_DEFER)
5618c2ecf20Sopenharmony_ci			return -EPROBE_DEFER;
5628c2ecf20Sopenharmony_ci	} else {
5638c2ecf20Sopenharmony_ci		if (clk_is_match(priv->extclk, priv->clk)) {
5648c2ecf20Sopenharmony_ci			devm_clk_put(&pdev->dev, priv->extclk);
5658c2ecf20Sopenharmony_ci			priv->extclk = ERR_PTR(-EINVAL);
5668c2ecf20Sopenharmony_ci		} else {
5678c2ecf20Sopenharmony_ci			dev_info(&pdev->dev, "found external clock\n");
5688c2ecf20Sopenharmony_ci			clk_prepare_enable(priv->extclk);
5698c2ecf20Sopenharmony_ci			soc_dai = kirkwood_i2s_dai_extclk;
5708c2ecf20Sopenharmony_ci		}
5718c2ecf20Sopenharmony_ci	}
5728c2ecf20Sopenharmony_ci
5738c2ecf20Sopenharmony_ci	err = clk_prepare_enable(priv->clk);
5748c2ecf20Sopenharmony_ci	if (err < 0)
5758c2ecf20Sopenharmony_ci		return err;
5768c2ecf20Sopenharmony_ci
5778c2ecf20Sopenharmony_ci	/* Some sensible defaults - this reflects the powerup values */
5788c2ecf20Sopenharmony_ci	priv->ctl_play = KIRKWOOD_PLAYCTL_SIZE_24;
5798c2ecf20Sopenharmony_ci	priv->ctl_rec = KIRKWOOD_RECCTL_SIZE_24;
5808c2ecf20Sopenharmony_ci
5818c2ecf20Sopenharmony_ci	/* Select the burst size */
5828c2ecf20Sopenharmony_ci	if (priv->burst == 32) {
5838c2ecf20Sopenharmony_ci		priv->ctl_play |= KIRKWOOD_PLAYCTL_BURST_32;
5848c2ecf20Sopenharmony_ci		priv->ctl_rec |= KIRKWOOD_RECCTL_BURST_32;
5858c2ecf20Sopenharmony_ci	} else {
5868c2ecf20Sopenharmony_ci		priv->ctl_play |= KIRKWOOD_PLAYCTL_BURST_128;
5878c2ecf20Sopenharmony_ci		priv->ctl_rec |= KIRKWOOD_RECCTL_BURST_128;
5888c2ecf20Sopenharmony_ci	}
5898c2ecf20Sopenharmony_ci
5908c2ecf20Sopenharmony_ci	err = snd_soc_register_component(&pdev->dev, &kirkwood_soc_component,
5918c2ecf20Sopenharmony_ci					 soc_dai, 2);
5928c2ecf20Sopenharmony_ci	if (err) {
5938c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "snd_soc_register_component failed\n");
5948c2ecf20Sopenharmony_ci		goto err_component;
5958c2ecf20Sopenharmony_ci	}
5968c2ecf20Sopenharmony_ci
5978c2ecf20Sopenharmony_ci	kirkwood_i2s_init(priv);
5988c2ecf20Sopenharmony_ci
5998c2ecf20Sopenharmony_ci	return 0;
6008c2ecf20Sopenharmony_ci
6018c2ecf20Sopenharmony_ci err_component:
6028c2ecf20Sopenharmony_ci	if (!IS_ERR(priv->extclk))
6038c2ecf20Sopenharmony_ci		clk_disable_unprepare(priv->extclk);
6048c2ecf20Sopenharmony_ci	clk_disable_unprepare(priv->clk);
6058c2ecf20Sopenharmony_ci
6068c2ecf20Sopenharmony_ci	return err;
6078c2ecf20Sopenharmony_ci}
6088c2ecf20Sopenharmony_ci
6098c2ecf20Sopenharmony_cistatic int kirkwood_i2s_dev_remove(struct platform_device *pdev)
6108c2ecf20Sopenharmony_ci{
6118c2ecf20Sopenharmony_ci	struct kirkwood_dma_data *priv = dev_get_drvdata(&pdev->dev);
6128c2ecf20Sopenharmony_ci
6138c2ecf20Sopenharmony_ci	snd_soc_unregister_component(&pdev->dev);
6148c2ecf20Sopenharmony_ci	if (!IS_ERR(priv->extclk))
6158c2ecf20Sopenharmony_ci		clk_disable_unprepare(priv->extclk);
6168c2ecf20Sopenharmony_ci	clk_disable_unprepare(priv->clk);
6178c2ecf20Sopenharmony_ci
6188c2ecf20Sopenharmony_ci	return 0;
6198c2ecf20Sopenharmony_ci}
6208c2ecf20Sopenharmony_ci
6218c2ecf20Sopenharmony_ci#ifdef CONFIG_OF
6228c2ecf20Sopenharmony_cistatic const struct of_device_id mvebu_audio_of_match[] = {
6238c2ecf20Sopenharmony_ci	{ .compatible = "marvell,kirkwood-audio" },
6248c2ecf20Sopenharmony_ci	{ .compatible = "marvell,dove-audio" },
6258c2ecf20Sopenharmony_ci	{ .compatible = "marvell,armada370-audio" },
6268c2ecf20Sopenharmony_ci	{ }
6278c2ecf20Sopenharmony_ci};
6288c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, mvebu_audio_of_match);
6298c2ecf20Sopenharmony_ci#endif
6308c2ecf20Sopenharmony_ci
6318c2ecf20Sopenharmony_cistatic struct platform_driver kirkwood_i2s_driver = {
6328c2ecf20Sopenharmony_ci	.probe  = kirkwood_i2s_dev_probe,
6338c2ecf20Sopenharmony_ci	.remove = kirkwood_i2s_dev_remove,
6348c2ecf20Sopenharmony_ci	.driver = {
6358c2ecf20Sopenharmony_ci		.name = DRV_NAME,
6368c2ecf20Sopenharmony_ci		.of_match_table = of_match_ptr(mvebu_audio_of_match),
6378c2ecf20Sopenharmony_ci	},
6388c2ecf20Sopenharmony_ci};
6398c2ecf20Sopenharmony_ci
6408c2ecf20Sopenharmony_cimodule_platform_driver(kirkwood_i2s_driver);
6418c2ecf20Sopenharmony_ci
6428c2ecf20Sopenharmony_ci/* Module information */
6438c2ecf20Sopenharmony_ciMODULE_AUTHOR("Arnaud Patard, <arnaud.patard@rtp-net.org>");
6448c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Kirkwood I2S SoC Interface");
6458c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
6468c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:mvebu-audio");
647