162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * pxa2xx-i2s.c  --  ALSA Soc Audio Layer
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright 2005 Wolfson Microelectronics PLC.
662306a36Sopenharmony_ci * Author: Liam Girdwood
762306a36Sopenharmony_ci *         lrg@slimlogic.co.uk
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <linux/init.h>
1162306a36Sopenharmony_ci#include <linux/module.h>
1262306a36Sopenharmony_ci#include <linux/device.h>
1362306a36Sopenharmony_ci#include <linux/delay.h>
1462306a36Sopenharmony_ci#include <linux/clk.h>
1562306a36Sopenharmony_ci#include <linux/platform_device.h>
1662306a36Sopenharmony_ci#include <linux/io.h>
1762306a36Sopenharmony_ci#include <sound/core.h>
1862306a36Sopenharmony_ci#include <sound/pcm.h>
1962306a36Sopenharmony_ci#include <sound/initval.h>
2062306a36Sopenharmony_ci#include <sound/soc.h>
2162306a36Sopenharmony_ci#include <sound/pxa2xx-lib.h>
2262306a36Sopenharmony_ci#include <sound/dmaengine_pcm.h>
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci#include <linux/platform_data/asoc-pxa.h>
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci#include "pxa2xx-i2s.h"
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci/*
2962306a36Sopenharmony_ci * I2S Controller Register and Bit Definitions
3062306a36Sopenharmony_ci */
3162306a36Sopenharmony_ci#define SACR0		(0x0000)	/* Global Control Register */
3262306a36Sopenharmony_ci#define SACR1		(0x0004)	/* Serial Audio I 2 S/MSB-Justified Control Register */
3362306a36Sopenharmony_ci#define SASR0		(0x000C)	/* Serial Audio I 2 S/MSB-Justified Interface and FIFO Status Register */
3462306a36Sopenharmony_ci#define SAIMR		(0x0014)	/* Serial Audio Interrupt Mask Register */
3562306a36Sopenharmony_ci#define SAICR		(0x0018)	/* Serial Audio Interrupt Clear Register */
3662306a36Sopenharmony_ci#define SADIV		(0x0060)	/* Audio Clock Divider Register. */
3762306a36Sopenharmony_ci#define SADR		(0x0080)	/* Serial Audio Data Register (TX and RX FIFO access Register). */
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci#define SACR0_RFTH(x)	((x) << 12)	/* Rx FIFO Interrupt or DMA Trigger Threshold */
4062306a36Sopenharmony_ci#define SACR0_TFTH(x)	((x) << 8)	/* Tx FIFO Interrupt or DMA Trigger Threshold */
4162306a36Sopenharmony_ci#define SACR0_STRF	(1 << 5)	/* FIFO Select for EFWR Special Function */
4262306a36Sopenharmony_ci#define SACR0_EFWR	(1 << 4)	/* Enable EFWR Function  */
4362306a36Sopenharmony_ci#define SACR0_RST	(1 << 3)	/* FIFO, i2s Register Reset */
4462306a36Sopenharmony_ci#define SACR0_BCKD	(1 << 2)	/* Bit Clock Direction */
4562306a36Sopenharmony_ci#define SACR0_ENB	(1 << 0)	/* Enable I2S Link */
4662306a36Sopenharmony_ci#define SACR1_ENLBF	(1 << 5)	/* Enable Loopback */
4762306a36Sopenharmony_ci#define SACR1_DRPL	(1 << 4)	/* Disable Replaying Function */
4862306a36Sopenharmony_ci#define SACR1_DREC	(1 << 3)	/* Disable Recording Function */
4962306a36Sopenharmony_ci#define SACR1_AMSL	(1 << 0)	/* Specify Alternate Mode */
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci#define SASR0_I2SOFF	(1 << 7)	/* Controller Status */
5262306a36Sopenharmony_ci#define SASR0_ROR	(1 << 6)	/* Rx FIFO Overrun */
5362306a36Sopenharmony_ci#define SASR0_TUR	(1 << 5)	/* Tx FIFO Underrun */
5462306a36Sopenharmony_ci#define SASR0_RFS	(1 << 4)	/* Rx FIFO Service Request */
5562306a36Sopenharmony_ci#define SASR0_TFS	(1 << 3)	/* Tx FIFO Service Request */
5662306a36Sopenharmony_ci#define SASR0_BSY	(1 << 2)	/* I2S Busy */
5762306a36Sopenharmony_ci#define SASR0_RNE	(1 << 1)	/* Rx FIFO Not Empty */
5862306a36Sopenharmony_ci#define SASR0_TNF	(1 << 0)	/* Tx FIFO Not Empty */
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci#define SAICR_ROR	(1 << 6)	/* Clear Rx FIFO Overrun Interrupt */
6162306a36Sopenharmony_ci#define SAICR_TUR	(1 << 5)	/* Clear Tx FIFO Underrun Interrupt */
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci#define SAIMR_ROR	(1 << 6)	/* Enable Rx FIFO Overrun Condition Interrupt */
6462306a36Sopenharmony_ci#define SAIMR_TUR	(1 << 5)	/* Enable Tx FIFO Underrun Condition Interrupt */
6562306a36Sopenharmony_ci#define SAIMR_RFS	(1 << 4)	/* Enable Rx FIFO Service Interrupt */
6662306a36Sopenharmony_ci#define SAIMR_TFS	(1 << 3)	/* Enable Tx FIFO Service Interrupt */
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_cistruct pxa_i2s_port {
6962306a36Sopenharmony_ci	u32 sadiv;
7062306a36Sopenharmony_ci	u32 sacr0;
7162306a36Sopenharmony_ci	u32 sacr1;
7262306a36Sopenharmony_ci	u32 saimr;
7362306a36Sopenharmony_ci	int master;
7462306a36Sopenharmony_ci	u32 fmt;
7562306a36Sopenharmony_ci};
7662306a36Sopenharmony_cistatic struct pxa_i2s_port pxa_i2s;
7762306a36Sopenharmony_cistatic struct clk *clk_i2s;
7862306a36Sopenharmony_cistatic int clk_ena = 0;
7962306a36Sopenharmony_cistatic void __iomem *i2s_reg_base;
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_cistatic struct snd_dmaengine_dai_dma_data pxa2xx_i2s_pcm_stereo_out = {
8262306a36Sopenharmony_ci	.addr_width	= DMA_SLAVE_BUSWIDTH_4_BYTES,
8362306a36Sopenharmony_ci	.chan_name	= "tx",
8462306a36Sopenharmony_ci	.maxburst	= 32,
8562306a36Sopenharmony_ci};
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_cistatic struct snd_dmaengine_dai_dma_data pxa2xx_i2s_pcm_stereo_in = {
8862306a36Sopenharmony_ci	.addr_width	= DMA_SLAVE_BUSWIDTH_4_BYTES,
8962306a36Sopenharmony_ci	.chan_name	= "rx",
9062306a36Sopenharmony_ci	.maxburst	= 32,
9162306a36Sopenharmony_ci};
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_cistatic int pxa2xx_i2s_startup(struct snd_pcm_substream *substream,
9462306a36Sopenharmony_ci			      struct snd_soc_dai *dai)
9562306a36Sopenharmony_ci{
9662306a36Sopenharmony_ci	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
9762306a36Sopenharmony_ci	struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci	if (IS_ERR(clk_i2s))
10062306a36Sopenharmony_ci		return PTR_ERR(clk_i2s);
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci	if (!snd_soc_dai_active(cpu_dai))
10362306a36Sopenharmony_ci		writel(0, i2s_reg_base + SACR0);
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci	return 0;
10662306a36Sopenharmony_ci}
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci/* wait for I2S controller to be ready */
10962306a36Sopenharmony_cistatic int pxa_i2s_wait(void)
11062306a36Sopenharmony_ci{
11162306a36Sopenharmony_ci	int i;
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci	/* flush the Rx FIFO */
11462306a36Sopenharmony_ci	for (i = 0; i < 16; i++)
11562306a36Sopenharmony_ci		readl(i2s_reg_base + SADR);
11662306a36Sopenharmony_ci	return 0;
11762306a36Sopenharmony_ci}
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_cistatic int pxa2xx_i2s_set_dai_fmt(struct snd_soc_dai *cpu_dai,
12062306a36Sopenharmony_ci		unsigned int fmt)
12162306a36Sopenharmony_ci{
12262306a36Sopenharmony_ci	/* interface format */
12362306a36Sopenharmony_ci	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
12462306a36Sopenharmony_ci	case SND_SOC_DAIFMT_I2S:
12562306a36Sopenharmony_ci		pxa_i2s.fmt = 0;
12662306a36Sopenharmony_ci		break;
12762306a36Sopenharmony_ci	case SND_SOC_DAIFMT_LEFT_J:
12862306a36Sopenharmony_ci		pxa_i2s.fmt = SACR1_AMSL;
12962306a36Sopenharmony_ci		break;
13062306a36Sopenharmony_ci	}
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci	switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
13362306a36Sopenharmony_ci	case SND_SOC_DAIFMT_BP_FP:
13462306a36Sopenharmony_ci		pxa_i2s.master = 1;
13562306a36Sopenharmony_ci		break;
13662306a36Sopenharmony_ci	case SND_SOC_DAIFMT_BC_FP:
13762306a36Sopenharmony_ci		pxa_i2s.master = 0;
13862306a36Sopenharmony_ci		break;
13962306a36Sopenharmony_ci	default:
14062306a36Sopenharmony_ci		break;
14162306a36Sopenharmony_ci	}
14262306a36Sopenharmony_ci	return 0;
14362306a36Sopenharmony_ci}
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_cistatic int pxa2xx_i2s_set_dai_sysclk(struct snd_soc_dai *cpu_dai,
14662306a36Sopenharmony_ci		int clk_id, unsigned int freq, int dir)
14762306a36Sopenharmony_ci{
14862306a36Sopenharmony_ci	if (clk_id != PXA2XX_I2S_SYSCLK)
14962306a36Sopenharmony_ci		return -ENODEV;
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci	return 0;
15262306a36Sopenharmony_ci}
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_cistatic int pxa2xx_i2s_hw_params(struct snd_pcm_substream *substream,
15562306a36Sopenharmony_ci				struct snd_pcm_hw_params *params,
15662306a36Sopenharmony_ci				struct snd_soc_dai *dai)
15762306a36Sopenharmony_ci{
15862306a36Sopenharmony_ci	struct snd_dmaengine_dai_dma_data *dma_data;
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci	if (WARN_ON(IS_ERR(clk_i2s)))
16162306a36Sopenharmony_ci		return -EINVAL;
16262306a36Sopenharmony_ci	clk_prepare_enable(clk_i2s);
16362306a36Sopenharmony_ci	clk_ena = 1;
16462306a36Sopenharmony_ci	pxa_i2s_wait();
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
16762306a36Sopenharmony_ci		dma_data = &pxa2xx_i2s_pcm_stereo_out;
16862306a36Sopenharmony_ci	else
16962306a36Sopenharmony_ci		dma_data = &pxa2xx_i2s_pcm_stereo_in;
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci	snd_soc_dai_set_dma_data(dai, substream, dma_data);
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci	/* is port used by another stream */
17462306a36Sopenharmony_ci	if (!(SACR0 & SACR0_ENB)) {
17562306a36Sopenharmony_ci		writel(0, i2s_reg_base + SACR0);
17662306a36Sopenharmony_ci		if (pxa_i2s.master)
17762306a36Sopenharmony_ci			writel(readl(i2s_reg_base + SACR0) | (SACR0_BCKD), i2s_reg_base + SACR0);
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci		writel(readl(i2s_reg_base + SACR0) | (SACR0_RFTH(14) | SACR0_TFTH(1)), i2s_reg_base + SACR0);
18062306a36Sopenharmony_ci		writel(readl(i2s_reg_base + SACR1) | (pxa_i2s.fmt), i2s_reg_base + SACR1);
18162306a36Sopenharmony_ci	}
18262306a36Sopenharmony_ci	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
18362306a36Sopenharmony_ci		writel(readl(i2s_reg_base + SAIMR) | (SAIMR_TFS), i2s_reg_base + SAIMR);
18462306a36Sopenharmony_ci	else
18562306a36Sopenharmony_ci		writel(readl(i2s_reg_base + SAIMR) | (SAIMR_RFS), i2s_reg_base + SAIMR);
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci	switch (params_rate(params)) {
18862306a36Sopenharmony_ci	case 8000:
18962306a36Sopenharmony_ci		writel(0x48, i2s_reg_base + SADIV);
19062306a36Sopenharmony_ci		break;
19162306a36Sopenharmony_ci	case 11025:
19262306a36Sopenharmony_ci		writel(0x34, i2s_reg_base + SADIV);
19362306a36Sopenharmony_ci		break;
19462306a36Sopenharmony_ci	case 16000:
19562306a36Sopenharmony_ci		writel(0x24, i2s_reg_base + SADIV);
19662306a36Sopenharmony_ci		break;
19762306a36Sopenharmony_ci	case 22050:
19862306a36Sopenharmony_ci		writel(0x1a, i2s_reg_base + SADIV);
19962306a36Sopenharmony_ci		break;
20062306a36Sopenharmony_ci	case 44100:
20162306a36Sopenharmony_ci		writel(0xd, i2s_reg_base + SADIV);
20262306a36Sopenharmony_ci		break;
20362306a36Sopenharmony_ci	case 48000:
20462306a36Sopenharmony_ci		writel(0xc, i2s_reg_base + SADIV);
20562306a36Sopenharmony_ci		break;
20662306a36Sopenharmony_ci	case 96000: /* not in manual and possibly slightly inaccurate */
20762306a36Sopenharmony_ci		writel(0x6, i2s_reg_base + SADIV);
20862306a36Sopenharmony_ci		break;
20962306a36Sopenharmony_ci	}
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci	return 0;
21262306a36Sopenharmony_ci}
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_cistatic int pxa2xx_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
21562306a36Sopenharmony_ci			      struct snd_soc_dai *dai)
21662306a36Sopenharmony_ci{
21762306a36Sopenharmony_ci	int ret = 0;
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ci	switch (cmd) {
22062306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_START:
22162306a36Sopenharmony_ci		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
22262306a36Sopenharmony_ci			writel(readl(i2s_reg_base + SACR1) & (~SACR1_DRPL), i2s_reg_base + SACR1);
22362306a36Sopenharmony_ci		else
22462306a36Sopenharmony_ci			writel(readl(i2s_reg_base + SACR1) & (~SACR1_DREC), i2s_reg_base + SACR1);
22562306a36Sopenharmony_ci		writel(readl(i2s_reg_base + SACR0) | (SACR0_ENB), i2s_reg_base + SACR0);
22662306a36Sopenharmony_ci		break;
22762306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_RESUME:
22862306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
22962306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_STOP:
23062306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_SUSPEND:
23162306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
23262306a36Sopenharmony_ci		break;
23362306a36Sopenharmony_ci	default:
23462306a36Sopenharmony_ci		ret = -EINVAL;
23562306a36Sopenharmony_ci	}
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_ci	return ret;
23862306a36Sopenharmony_ci}
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_cistatic void pxa2xx_i2s_shutdown(struct snd_pcm_substream *substream,
24162306a36Sopenharmony_ci				struct snd_soc_dai *dai)
24262306a36Sopenharmony_ci{
24362306a36Sopenharmony_ci	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
24462306a36Sopenharmony_ci		writel(readl(i2s_reg_base + SACR1) | (SACR1_DRPL), i2s_reg_base + SACR1);
24562306a36Sopenharmony_ci		writel(readl(i2s_reg_base + SAIMR) & (~SAIMR_TFS), i2s_reg_base + SAIMR);
24662306a36Sopenharmony_ci	} else {
24762306a36Sopenharmony_ci		writel(readl(i2s_reg_base + SACR1) | (SACR1_DREC), i2s_reg_base + SACR1);
24862306a36Sopenharmony_ci		writel(readl(i2s_reg_base + SAIMR) & (~SAIMR_RFS), i2s_reg_base + SAIMR);
24962306a36Sopenharmony_ci	}
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci	if ((readl(i2s_reg_base + SACR1) & (SACR1_DREC | SACR1_DRPL)) == (SACR1_DREC | SACR1_DRPL)) {
25262306a36Sopenharmony_ci		writel(readl(i2s_reg_base + SACR0) & (~SACR0_ENB), i2s_reg_base + SACR0);
25362306a36Sopenharmony_ci		pxa_i2s_wait();
25462306a36Sopenharmony_ci		if (clk_ena) {
25562306a36Sopenharmony_ci			clk_disable_unprepare(clk_i2s);
25662306a36Sopenharmony_ci			clk_ena = 0;
25762306a36Sopenharmony_ci		}
25862306a36Sopenharmony_ci	}
25962306a36Sopenharmony_ci}
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_ci#ifdef CONFIG_PM
26262306a36Sopenharmony_cistatic int pxa2xx_soc_pcm_suspend(struct snd_soc_component *component)
26362306a36Sopenharmony_ci{
26462306a36Sopenharmony_ci	/* store registers */
26562306a36Sopenharmony_ci	pxa_i2s.sacr0 = readl(i2s_reg_base + SACR0);
26662306a36Sopenharmony_ci	pxa_i2s.sacr1 = readl(i2s_reg_base + SACR1);
26762306a36Sopenharmony_ci	pxa_i2s.saimr = readl(i2s_reg_base + SAIMR);
26862306a36Sopenharmony_ci	pxa_i2s.sadiv = readl(i2s_reg_base + SADIV);
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci	/* deactivate link */
27162306a36Sopenharmony_ci	writel(readl(i2s_reg_base + SACR0) & (~SACR0_ENB), i2s_reg_base + SACR0);
27262306a36Sopenharmony_ci	pxa_i2s_wait();
27362306a36Sopenharmony_ci	return 0;
27462306a36Sopenharmony_ci}
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_cistatic int pxa2xx_soc_pcm_resume(struct snd_soc_component *component)
27762306a36Sopenharmony_ci{
27862306a36Sopenharmony_ci	pxa_i2s_wait();
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ci	writel(pxa_i2s.sacr0 & ~SACR0_ENB, i2s_reg_base + SACR0);
28162306a36Sopenharmony_ci	writel(pxa_i2s.sacr1, i2s_reg_base + SACR1);
28262306a36Sopenharmony_ci	writel(pxa_i2s.saimr, i2s_reg_base + SAIMR);
28362306a36Sopenharmony_ci	writel(pxa_i2s.sadiv, i2s_reg_base + SADIV);
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci	writel(pxa_i2s.sacr0, i2s_reg_base + SACR0);
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_ci	return 0;
28862306a36Sopenharmony_ci}
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_ci#else
29162306a36Sopenharmony_ci#define pxa2xx_soc_pcm_suspend	NULL
29262306a36Sopenharmony_ci#define pxa2xx_soc_pcm_resume	NULL
29362306a36Sopenharmony_ci#endif
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_cistatic int pxa2xx_i2s_probe(struct snd_soc_dai *dai)
29662306a36Sopenharmony_ci{
29762306a36Sopenharmony_ci	clk_i2s = clk_get(dai->dev, "I2SCLK");
29862306a36Sopenharmony_ci	if (IS_ERR(clk_i2s))
29962306a36Sopenharmony_ci		return PTR_ERR(clk_i2s);
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_ci	/*
30262306a36Sopenharmony_ci	 * PXA Developer's Manual:
30362306a36Sopenharmony_ci	 * If SACR0[ENB] is toggled in the middle of a normal operation,
30462306a36Sopenharmony_ci	 * the SACR0[RST] bit must also be set and cleared to reset all
30562306a36Sopenharmony_ci	 * I2S controller registers.
30662306a36Sopenharmony_ci	 */
30762306a36Sopenharmony_ci	writel(SACR0_RST, i2s_reg_base + SACR0);
30862306a36Sopenharmony_ci	writel(0, i2s_reg_base + SACR0);
30962306a36Sopenharmony_ci	/* Make sure RPL and REC are disabled */
31062306a36Sopenharmony_ci	writel(SACR1_DRPL | SACR1_DREC, i2s_reg_base + SACR1);
31162306a36Sopenharmony_ci	/* Along with FIFO servicing */
31262306a36Sopenharmony_ci	writel(readl(i2s_reg_base + SAIMR) & (~(SAIMR_RFS | SAIMR_TFS)), i2s_reg_base + SAIMR);
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_ci	snd_soc_dai_init_dma_data(dai, &pxa2xx_i2s_pcm_stereo_out,
31562306a36Sopenharmony_ci		&pxa2xx_i2s_pcm_stereo_in);
31662306a36Sopenharmony_ci
31762306a36Sopenharmony_ci	return 0;
31862306a36Sopenharmony_ci}
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_cistatic int  pxa2xx_i2s_remove(struct snd_soc_dai *dai)
32162306a36Sopenharmony_ci{
32262306a36Sopenharmony_ci	clk_put(clk_i2s);
32362306a36Sopenharmony_ci	clk_i2s = ERR_PTR(-ENOENT);
32462306a36Sopenharmony_ci	return 0;
32562306a36Sopenharmony_ci}
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_ci#define PXA2XX_I2S_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
32862306a36Sopenharmony_ci		SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_44100 | \
32962306a36Sopenharmony_ci		SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000)
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_cistatic const struct snd_soc_dai_ops pxa_i2s_dai_ops = {
33262306a36Sopenharmony_ci	.probe		= pxa2xx_i2s_probe,
33362306a36Sopenharmony_ci	.remove		= pxa2xx_i2s_remove,
33462306a36Sopenharmony_ci	.startup	= pxa2xx_i2s_startup,
33562306a36Sopenharmony_ci	.shutdown	= pxa2xx_i2s_shutdown,
33662306a36Sopenharmony_ci	.trigger	= pxa2xx_i2s_trigger,
33762306a36Sopenharmony_ci	.hw_params	= pxa2xx_i2s_hw_params,
33862306a36Sopenharmony_ci	.set_fmt	= pxa2xx_i2s_set_dai_fmt,
33962306a36Sopenharmony_ci	.set_sysclk	= pxa2xx_i2s_set_dai_sysclk,
34062306a36Sopenharmony_ci};
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_cistatic struct snd_soc_dai_driver pxa_i2s_dai = {
34362306a36Sopenharmony_ci	.playback = {
34462306a36Sopenharmony_ci		.channels_min = 2,
34562306a36Sopenharmony_ci		.channels_max = 2,
34662306a36Sopenharmony_ci		.rates = PXA2XX_I2S_RATES,
34762306a36Sopenharmony_ci		.formats = SNDRV_PCM_FMTBIT_S16_LE,},
34862306a36Sopenharmony_ci	.capture = {
34962306a36Sopenharmony_ci		.channels_min = 2,
35062306a36Sopenharmony_ci		.channels_max = 2,
35162306a36Sopenharmony_ci		.rates = PXA2XX_I2S_RATES,
35262306a36Sopenharmony_ci		.formats = SNDRV_PCM_FMTBIT_S16_LE,},
35362306a36Sopenharmony_ci	.ops = &pxa_i2s_dai_ops,
35462306a36Sopenharmony_ci	.symmetric_rate = 1,
35562306a36Sopenharmony_ci};
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_cistatic const struct snd_soc_component_driver pxa_i2s_component = {
35862306a36Sopenharmony_ci	.name			= "pxa-i2s",
35962306a36Sopenharmony_ci	.pcm_construct		= pxa2xx_soc_pcm_new,
36062306a36Sopenharmony_ci	.open			= pxa2xx_soc_pcm_open,
36162306a36Sopenharmony_ci	.close			= pxa2xx_soc_pcm_close,
36262306a36Sopenharmony_ci	.hw_params		= pxa2xx_soc_pcm_hw_params,
36362306a36Sopenharmony_ci	.prepare		= pxa2xx_soc_pcm_prepare,
36462306a36Sopenharmony_ci	.trigger		= pxa2xx_soc_pcm_trigger,
36562306a36Sopenharmony_ci	.pointer		= pxa2xx_soc_pcm_pointer,
36662306a36Sopenharmony_ci	.suspend		= pxa2xx_soc_pcm_suspend,
36762306a36Sopenharmony_ci	.resume			= pxa2xx_soc_pcm_resume,
36862306a36Sopenharmony_ci	.legacy_dai_naming	= 1,
36962306a36Sopenharmony_ci};
37062306a36Sopenharmony_ci
37162306a36Sopenharmony_cistatic int pxa2xx_i2s_drv_probe(struct platform_device *pdev)
37262306a36Sopenharmony_ci{
37362306a36Sopenharmony_ci	struct resource *res;
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_ci	i2s_reg_base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
37662306a36Sopenharmony_ci	if (IS_ERR(i2s_reg_base))
37762306a36Sopenharmony_ci		return PTR_ERR(i2s_reg_base);
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_ci	pxa2xx_i2s_pcm_stereo_out.addr = res->start + SADR;
38062306a36Sopenharmony_ci	pxa2xx_i2s_pcm_stereo_in.addr = res->start + SADR;
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_ci	return devm_snd_soc_register_component(&pdev->dev, &pxa_i2s_component,
38362306a36Sopenharmony_ci					       &pxa_i2s_dai, 1);
38462306a36Sopenharmony_ci}
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_cistatic struct platform_driver pxa2xx_i2s_driver = {
38762306a36Sopenharmony_ci	.probe = pxa2xx_i2s_drv_probe,
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_ci	.driver = {
39062306a36Sopenharmony_ci		.name = "pxa2xx-i2s",
39162306a36Sopenharmony_ci	},
39262306a36Sopenharmony_ci};
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_cistatic int __init pxa2xx_i2s_init(void)
39562306a36Sopenharmony_ci{
39662306a36Sopenharmony_ci	clk_i2s = ERR_PTR(-ENOENT);
39762306a36Sopenharmony_ci	return platform_driver_register(&pxa2xx_i2s_driver);
39862306a36Sopenharmony_ci}
39962306a36Sopenharmony_ci
40062306a36Sopenharmony_cistatic void __exit pxa2xx_i2s_exit(void)
40162306a36Sopenharmony_ci{
40262306a36Sopenharmony_ci	platform_driver_unregister(&pxa2xx_i2s_driver);
40362306a36Sopenharmony_ci}
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_cimodule_init(pxa2xx_i2s_init);
40662306a36Sopenharmony_cimodule_exit(pxa2xx_i2s_exit);
40762306a36Sopenharmony_ci
40862306a36Sopenharmony_ci/* Module information */
40962306a36Sopenharmony_ciMODULE_AUTHOR("Liam Girdwood, lrg@slimlogic.co.uk");
41062306a36Sopenharmony_ciMODULE_DESCRIPTION("pxa2xx I2S SoC Interface");
41162306a36Sopenharmony_ciMODULE_LICENSE("GPL");
41262306a36Sopenharmony_ciMODULE_ALIAS("platform:pxa2xx-i2s");
413