18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * pxa2xx-i2s.c  --  ALSA Soc Audio Layer
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright 2005 Wolfson Microelectronics PLC.
68c2ecf20Sopenharmony_ci * Author: Liam Girdwood
78c2ecf20Sopenharmony_ci *         lrg@slimlogic.co.uk
88c2ecf20Sopenharmony_ci */
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#include <linux/init.h>
118c2ecf20Sopenharmony_ci#include <linux/module.h>
128c2ecf20Sopenharmony_ci#include <linux/device.h>
138c2ecf20Sopenharmony_ci#include <linux/delay.h>
148c2ecf20Sopenharmony_ci#include <linux/clk.h>
158c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
168c2ecf20Sopenharmony_ci#include <linux/io.h>
178c2ecf20Sopenharmony_ci#include <sound/core.h>
188c2ecf20Sopenharmony_ci#include <sound/pcm.h>
198c2ecf20Sopenharmony_ci#include <sound/initval.h>
208c2ecf20Sopenharmony_ci#include <sound/soc.h>
218c2ecf20Sopenharmony_ci#include <sound/pxa2xx-lib.h>
228c2ecf20Sopenharmony_ci#include <sound/dmaengine_pcm.h>
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci#include <mach/hardware.h>
258c2ecf20Sopenharmony_ci#include <mach/audio.h>
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci#include "pxa2xx-i2s.h"
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ci/*
308c2ecf20Sopenharmony_ci * I2S Controller Register and Bit Definitions
318c2ecf20Sopenharmony_ci */
328c2ecf20Sopenharmony_ci#define SACR0		__REG(0x40400000)  /* Global Control Register */
338c2ecf20Sopenharmony_ci#define SACR1		__REG(0x40400004)  /* Serial Audio I 2 S/MSB-Justified Control Register */
348c2ecf20Sopenharmony_ci#define SASR0		__REG(0x4040000C)  /* Serial Audio I 2 S/MSB-Justified Interface and FIFO Status Register */
358c2ecf20Sopenharmony_ci#define SAIMR		__REG(0x40400014)  /* Serial Audio Interrupt Mask Register */
368c2ecf20Sopenharmony_ci#define SAICR		__REG(0x40400018)  /* Serial Audio Interrupt Clear Register */
378c2ecf20Sopenharmony_ci#define SADIV		__REG(0x40400060)  /* Audio Clock Divider Register. */
388c2ecf20Sopenharmony_ci#define SADR		__REG(0x40400080)  /* Serial Audio Data Register (TX and RX FIFO access Register). */
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci#define SACR0_RFTH(x)	((x) << 12)	/* Rx FIFO Interrupt or DMA Trigger Threshold */
418c2ecf20Sopenharmony_ci#define SACR0_TFTH(x)	((x) << 8)	/* Tx FIFO Interrupt or DMA Trigger Threshold */
428c2ecf20Sopenharmony_ci#define SACR0_STRF	(1 << 5)	/* FIFO Select for EFWR Special Function */
438c2ecf20Sopenharmony_ci#define SACR0_EFWR	(1 << 4)	/* Enable EFWR Function  */
448c2ecf20Sopenharmony_ci#define SACR0_RST	(1 << 3)	/* FIFO, i2s Register Reset */
458c2ecf20Sopenharmony_ci#define SACR0_BCKD	(1 << 2)	/* Bit Clock Direction */
468c2ecf20Sopenharmony_ci#define SACR0_ENB	(1 << 0)	/* Enable I2S Link */
478c2ecf20Sopenharmony_ci#define SACR1_ENLBF	(1 << 5)	/* Enable Loopback */
488c2ecf20Sopenharmony_ci#define SACR1_DRPL	(1 << 4)	/* Disable Replaying Function */
498c2ecf20Sopenharmony_ci#define SACR1_DREC	(1 << 3)	/* Disable Recording Function */
508c2ecf20Sopenharmony_ci#define SACR1_AMSL	(1 << 0)	/* Specify Alternate Mode */
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci#define SASR0_I2SOFF	(1 << 7)	/* Controller Status */
538c2ecf20Sopenharmony_ci#define SASR0_ROR	(1 << 6)	/* Rx FIFO Overrun */
548c2ecf20Sopenharmony_ci#define SASR0_TUR	(1 << 5)	/* Tx FIFO Underrun */
558c2ecf20Sopenharmony_ci#define SASR0_RFS	(1 << 4)	/* Rx FIFO Service Request */
568c2ecf20Sopenharmony_ci#define SASR0_TFS	(1 << 3)	/* Tx FIFO Service Request */
578c2ecf20Sopenharmony_ci#define SASR0_BSY	(1 << 2)	/* I2S Busy */
588c2ecf20Sopenharmony_ci#define SASR0_RNE	(1 << 1)	/* Rx FIFO Not Empty */
598c2ecf20Sopenharmony_ci#define SASR0_TNF	(1 << 0)	/* Tx FIFO Not Empty */
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ci#define SAICR_ROR	(1 << 6)	/* Clear Rx FIFO Overrun Interrupt */
628c2ecf20Sopenharmony_ci#define SAICR_TUR	(1 << 5)	/* Clear Tx FIFO Underrun Interrupt */
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci#define SAIMR_ROR	(1 << 6)	/* Enable Rx FIFO Overrun Condition Interrupt */
658c2ecf20Sopenharmony_ci#define SAIMR_TUR	(1 << 5)	/* Enable Tx FIFO Underrun Condition Interrupt */
668c2ecf20Sopenharmony_ci#define SAIMR_RFS	(1 << 4)	/* Enable Rx FIFO Service Interrupt */
678c2ecf20Sopenharmony_ci#define SAIMR_TFS	(1 << 3)	/* Enable Tx FIFO Service Interrupt */
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_cistruct pxa_i2s_port {
708c2ecf20Sopenharmony_ci	u32 sadiv;
718c2ecf20Sopenharmony_ci	u32 sacr0;
728c2ecf20Sopenharmony_ci	u32 sacr1;
738c2ecf20Sopenharmony_ci	u32 saimr;
748c2ecf20Sopenharmony_ci	int master;
758c2ecf20Sopenharmony_ci	u32 fmt;
768c2ecf20Sopenharmony_ci};
778c2ecf20Sopenharmony_cistatic struct pxa_i2s_port pxa_i2s;
788c2ecf20Sopenharmony_cistatic struct clk *clk_i2s;
798c2ecf20Sopenharmony_cistatic int clk_ena = 0;
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_cistatic struct snd_dmaengine_dai_dma_data pxa2xx_i2s_pcm_stereo_out = {
828c2ecf20Sopenharmony_ci	.addr		= __PREG(SADR),
838c2ecf20Sopenharmony_ci	.addr_width	= DMA_SLAVE_BUSWIDTH_4_BYTES,
848c2ecf20Sopenharmony_ci	.chan_name	= "tx",
858c2ecf20Sopenharmony_ci	.maxburst	= 32,
868c2ecf20Sopenharmony_ci};
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_cistatic struct snd_dmaengine_dai_dma_data pxa2xx_i2s_pcm_stereo_in = {
898c2ecf20Sopenharmony_ci	.addr		= __PREG(SADR),
908c2ecf20Sopenharmony_ci	.addr_width	= DMA_SLAVE_BUSWIDTH_4_BYTES,
918c2ecf20Sopenharmony_ci	.chan_name	= "rx",
928c2ecf20Sopenharmony_ci	.maxburst	= 32,
938c2ecf20Sopenharmony_ci};
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_cistatic int pxa2xx_i2s_startup(struct snd_pcm_substream *substream,
968c2ecf20Sopenharmony_ci			      struct snd_soc_dai *dai)
978c2ecf20Sopenharmony_ci{
988c2ecf20Sopenharmony_ci	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
998c2ecf20Sopenharmony_ci	struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci	if (IS_ERR(clk_i2s))
1028c2ecf20Sopenharmony_ci		return PTR_ERR(clk_i2s);
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci	if (!snd_soc_dai_active(cpu_dai))
1058c2ecf20Sopenharmony_ci		SACR0 = 0;
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci	return 0;
1088c2ecf20Sopenharmony_ci}
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci/* wait for I2S controller to be ready */
1118c2ecf20Sopenharmony_cistatic int pxa_i2s_wait(void)
1128c2ecf20Sopenharmony_ci{
1138c2ecf20Sopenharmony_ci	int i;
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci	/* flush the Rx FIFO */
1168c2ecf20Sopenharmony_ci	for (i = 0; i < 16; i++)
1178c2ecf20Sopenharmony_ci		SADR;
1188c2ecf20Sopenharmony_ci	return 0;
1198c2ecf20Sopenharmony_ci}
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_cistatic int pxa2xx_i2s_set_dai_fmt(struct snd_soc_dai *cpu_dai,
1228c2ecf20Sopenharmony_ci		unsigned int fmt)
1238c2ecf20Sopenharmony_ci{
1248c2ecf20Sopenharmony_ci	/* interface format */
1258c2ecf20Sopenharmony_ci	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
1268c2ecf20Sopenharmony_ci	case SND_SOC_DAIFMT_I2S:
1278c2ecf20Sopenharmony_ci		pxa_i2s.fmt = 0;
1288c2ecf20Sopenharmony_ci		break;
1298c2ecf20Sopenharmony_ci	case SND_SOC_DAIFMT_LEFT_J:
1308c2ecf20Sopenharmony_ci		pxa_i2s.fmt = SACR1_AMSL;
1318c2ecf20Sopenharmony_ci		break;
1328c2ecf20Sopenharmony_ci	}
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
1358c2ecf20Sopenharmony_ci	case SND_SOC_DAIFMT_CBS_CFS:
1368c2ecf20Sopenharmony_ci		pxa_i2s.master = 1;
1378c2ecf20Sopenharmony_ci		break;
1388c2ecf20Sopenharmony_ci	case SND_SOC_DAIFMT_CBM_CFS:
1398c2ecf20Sopenharmony_ci		pxa_i2s.master = 0;
1408c2ecf20Sopenharmony_ci		break;
1418c2ecf20Sopenharmony_ci	default:
1428c2ecf20Sopenharmony_ci		break;
1438c2ecf20Sopenharmony_ci	}
1448c2ecf20Sopenharmony_ci	return 0;
1458c2ecf20Sopenharmony_ci}
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_cistatic int pxa2xx_i2s_set_dai_sysclk(struct snd_soc_dai *cpu_dai,
1488c2ecf20Sopenharmony_ci		int clk_id, unsigned int freq, int dir)
1498c2ecf20Sopenharmony_ci{
1508c2ecf20Sopenharmony_ci	if (clk_id != PXA2XX_I2S_SYSCLK)
1518c2ecf20Sopenharmony_ci		return -ENODEV;
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ci	return 0;
1548c2ecf20Sopenharmony_ci}
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_cistatic int pxa2xx_i2s_hw_params(struct snd_pcm_substream *substream,
1578c2ecf20Sopenharmony_ci				struct snd_pcm_hw_params *params,
1588c2ecf20Sopenharmony_ci				struct snd_soc_dai *dai)
1598c2ecf20Sopenharmony_ci{
1608c2ecf20Sopenharmony_ci	struct snd_dmaengine_dai_dma_data *dma_data;
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci	if (WARN_ON(IS_ERR(clk_i2s)))
1638c2ecf20Sopenharmony_ci		return -EINVAL;
1648c2ecf20Sopenharmony_ci	clk_prepare_enable(clk_i2s);
1658c2ecf20Sopenharmony_ci	clk_ena = 1;
1668c2ecf20Sopenharmony_ci	pxa_i2s_wait();
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_ci	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
1698c2ecf20Sopenharmony_ci		dma_data = &pxa2xx_i2s_pcm_stereo_out;
1708c2ecf20Sopenharmony_ci	else
1718c2ecf20Sopenharmony_ci		dma_data = &pxa2xx_i2s_pcm_stereo_in;
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_ci	snd_soc_dai_set_dma_data(dai, substream, dma_data);
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci	/* is port used by another stream */
1768c2ecf20Sopenharmony_ci	if (!(SACR0 & SACR0_ENB)) {
1778c2ecf20Sopenharmony_ci		SACR0 = 0;
1788c2ecf20Sopenharmony_ci		if (pxa_i2s.master)
1798c2ecf20Sopenharmony_ci			SACR0 |= SACR0_BCKD;
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ci		SACR0 |= SACR0_RFTH(14) | SACR0_TFTH(1);
1828c2ecf20Sopenharmony_ci		SACR1 |= pxa_i2s.fmt;
1838c2ecf20Sopenharmony_ci	}
1848c2ecf20Sopenharmony_ci	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
1858c2ecf20Sopenharmony_ci		SAIMR |= SAIMR_TFS;
1868c2ecf20Sopenharmony_ci	else
1878c2ecf20Sopenharmony_ci		SAIMR |= SAIMR_RFS;
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ci	switch (params_rate(params)) {
1908c2ecf20Sopenharmony_ci	case 8000:
1918c2ecf20Sopenharmony_ci		SADIV = 0x48;
1928c2ecf20Sopenharmony_ci		break;
1938c2ecf20Sopenharmony_ci	case 11025:
1948c2ecf20Sopenharmony_ci		SADIV = 0x34;
1958c2ecf20Sopenharmony_ci		break;
1968c2ecf20Sopenharmony_ci	case 16000:
1978c2ecf20Sopenharmony_ci		SADIV = 0x24;
1988c2ecf20Sopenharmony_ci		break;
1998c2ecf20Sopenharmony_ci	case 22050:
2008c2ecf20Sopenharmony_ci		SADIV = 0x1a;
2018c2ecf20Sopenharmony_ci		break;
2028c2ecf20Sopenharmony_ci	case 44100:
2038c2ecf20Sopenharmony_ci		SADIV = 0xd;
2048c2ecf20Sopenharmony_ci		break;
2058c2ecf20Sopenharmony_ci	case 48000:
2068c2ecf20Sopenharmony_ci		SADIV = 0xc;
2078c2ecf20Sopenharmony_ci		break;
2088c2ecf20Sopenharmony_ci	case 96000: /* not in manual and possibly slightly inaccurate */
2098c2ecf20Sopenharmony_ci		SADIV = 0x6;
2108c2ecf20Sopenharmony_ci		break;
2118c2ecf20Sopenharmony_ci	}
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_ci	return 0;
2148c2ecf20Sopenharmony_ci}
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_cistatic int pxa2xx_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
2178c2ecf20Sopenharmony_ci			      struct snd_soc_dai *dai)
2188c2ecf20Sopenharmony_ci{
2198c2ecf20Sopenharmony_ci	int ret = 0;
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_ci	switch (cmd) {
2228c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_START:
2238c2ecf20Sopenharmony_ci		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
2248c2ecf20Sopenharmony_ci			SACR1 &= ~SACR1_DRPL;
2258c2ecf20Sopenharmony_ci		else
2268c2ecf20Sopenharmony_ci			SACR1 &= ~SACR1_DREC;
2278c2ecf20Sopenharmony_ci		SACR0 |= SACR0_ENB;
2288c2ecf20Sopenharmony_ci		break;
2298c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_RESUME:
2308c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
2318c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_STOP:
2328c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_SUSPEND:
2338c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
2348c2ecf20Sopenharmony_ci		break;
2358c2ecf20Sopenharmony_ci	default:
2368c2ecf20Sopenharmony_ci		ret = -EINVAL;
2378c2ecf20Sopenharmony_ci	}
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_ci	return ret;
2408c2ecf20Sopenharmony_ci}
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_cistatic void pxa2xx_i2s_shutdown(struct snd_pcm_substream *substream,
2438c2ecf20Sopenharmony_ci				struct snd_soc_dai *dai)
2448c2ecf20Sopenharmony_ci{
2458c2ecf20Sopenharmony_ci	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
2468c2ecf20Sopenharmony_ci		SACR1 |= SACR1_DRPL;
2478c2ecf20Sopenharmony_ci		SAIMR &= ~SAIMR_TFS;
2488c2ecf20Sopenharmony_ci	} else {
2498c2ecf20Sopenharmony_ci		SACR1 |= SACR1_DREC;
2508c2ecf20Sopenharmony_ci		SAIMR &= ~SAIMR_RFS;
2518c2ecf20Sopenharmony_ci	}
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_ci	if ((SACR1 & (SACR1_DREC | SACR1_DRPL)) == (SACR1_DREC | SACR1_DRPL)) {
2548c2ecf20Sopenharmony_ci		SACR0 &= ~SACR0_ENB;
2558c2ecf20Sopenharmony_ci		pxa_i2s_wait();
2568c2ecf20Sopenharmony_ci		if (clk_ena) {
2578c2ecf20Sopenharmony_ci			clk_disable_unprepare(clk_i2s);
2588c2ecf20Sopenharmony_ci			clk_ena = 0;
2598c2ecf20Sopenharmony_ci		}
2608c2ecf20Sopenharmony_ci	}
2618c2ecf20Sopenharmony_ci}
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_ci#ifdef CONFIG_PM
2648c2ecf20Sopenharmony_cistatic int pxa2xx_soc_pcm_suspend(struct snd_soc_component *component)
2658c2ecf20Sopenharmony_ci{
2668c2ecf20Sopenharmony_ci	/* store registers */
2678c2ecf20Sopenharmony_ci	pxa_i2s.sacr0 = SACR0;
2688c2ecf20Sopenharmony_ci	pxa_i2s.sacr1 = SACR1;
2698c2ecf20Sopenharmony_ci	pxa_i2s.saimr = SAIMR;
2708c2ecf20Sopenharmony_ci	pxa_i2s.sadiv = SADIV;
2718c2ecf20Sopenharmony_ci
2728c2ecf20Sopenharmony_ci	/* deactivate link */
2738c2ecf20Sopenharmony_ci	SACR0 &= ~SACR0_ENB;
2748c2ecf20Sopenharmony_ci	pxa_i2s_wait();
2758c2ecf20Sopenharmony_ci	return 0;
2768c2ecf20Sopenharmony_ci}
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_cistatic int pxa2xx_soc_pcm_resume(struct snd_soc_component *component)
2798c2ecf20Sopenharmony_ci{
2808c2ecf20Sopenharmony_ci	pxa_i2s_wait();
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_ci	SACR0 = pxa_i2s.sacr0 & ~SACR0_ENB;
2838c2ecf20Sopenharmony_ci	SACR1 = pxa_i2s.sacr1;
2848c2ecf20Sopenharmony_ci	SAIMR = pxa_i2s.saimr;
2858c2ecf20Sopenharmony_ci	SADIV = pxa_i2s.sadiv;
2868c2ecf20Sopenharmony_ci
2878c2ecf20Sopenharmony_ci	SACR0 = pxa_i2s.sacr0;
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_ci	return 0;
2908c2ecf20Sopenharmony_ci}
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_ci#else
2938c2ecf20Sopenharmony_ci#define pxa2xx_soc_pcm_suspend	NULL
2948c2ecf20Sopenharmony_ci#define pxa2xx_soc_pcm_resume	NULL
2958c2ecf20Sopenharmony_ci#endif
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_cistatic int pxa2xx_i2s_probe(struct snd_soc_dai *dai)
2988c2ecf20Sopenharmony_ci{
2998c2ecf20Sopenharmony_ci	clk_i2s = clk_get(dai->dev, "I2SCLK");
3008c2ecf20Sopenharmony_ci	if (IS_ERR(clk_i2s))
3018c2ecf20Sopenharmony_ci		return PTR_ERR(clk_i2s);
3028c2ecf20Sopenharmony_ci
3038c2ecf20Sopenharmony_ci	/*
3048c2ecf20Sopenharmony_ci	 * PXA Developer's Manual:
3058c2ecf20Sopenharmony_ci	 * If SACR0[ENB] is toggled in the middle of a normal operation,
3068c2ecf20Sopenharmony_ci	 * the SACR0[RST] bit must also be set and cleared to reset all
3078c2ecf20Sopenharmony_ci	 * I2S controller registers.
3088c2ecf20Sopenharmony_ci	 */
3098c2ecf20Sopenharmony_ci	SACR0 = SACR0_RST;
3108c2ecf20Sopenharmony_ci	SACR0 = 0;
3118c2ecf20Sopenharmony_ci	/* Make sure RPL and REC are disabled */
3128c2ecf20Sopenharmony_ci	SACR1 = SACR1_DRPL | SACR1_DREC;
3138c2ecf20Sopenharmony_ci	/* Along with FIFO servicing */
3148c2ecf20Sopenharmony_ci	SAIMR &= ~(SAIMR_RFS | SAIMR_TFS);
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_ci	snd_soc_dai_init_dma_data(dai, &pxa2xx_i2s_pcm_stereo_out,
3178c2ecf20Sopenharmony_ci		&pxa2xx_i2s_pcm_stereo_in);
3188c2ecf20Sopenharmony_ci
3198c2ecf20Sopenharmony_ci	return 0;
3208c2ecf20Sopenharmony_ci}
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_cistatic int  pxa2xx_i2s_remove(struct snd_soc_dai *dai)
3238c2ecf20Sopenharmony_ci{
3248c2ecf20Sopenharmony_ci	clk_put(clk_i2s);
3258c2ecf20Sopenharmony_ci	clk_i2s = ERR_PTR(-ENOENT);
3268c2ecf20Sopenharmony_ci	return 0;
3278c2ecf20Sopenharmony_ci}
3288c2ecf20Sopenharmony_ci
3298c2ecf20Sopenharmony_ci#define PXA2XX_I2S_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
3308c2ecf20Sopenharmony_ci		SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_44100 | \
3318c2ecf20Sopenharmony_ci		SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000)
3328c2ecf20Sopenharmony_ci
3338c2ecf20Sopenharmony_cistatic const struct snd_soc_dai_ops pxa_i2s_dai_ops = {
3348c2ecf20Sopenharmony_ci	.startup	= pxa2xx_i2s_startup,
3358c2ecf20Sopenharmony_ci	.shutdown	= pxa2xx_i2s_shutdown,
3368c2ecf20Sopenharmony_ci	.trigger	= pxa2xx_i2s_trigger,
3378c2ecf20Sopenharmony_ci	.hw_params	= pxa2xx_i2s_hw_params,
3388c2ecf20Sopenharmony_ci	.set_fmt	= pxa2xx_i2s_set_dai_fmt,
3398c2ecf20Sopenharmony_ci	.set_sysclk	= pxa2xx_i2s_set_dai_sysclk,
3408c2ecf20Sopenharmony_ci};
3418c2ecf20Sopenharmony_ci
3428c2ecf20Sopenharmony_cistatic struct snd_soc_dai_driver pxa_i2s_dai = {
3438c2ecf20Sopenharmony_ci	.probe = pxa2xx_i2s_probe,
3448c2ecf20Sopenharmony_ci	.remove = pxa2xx_i2s_remove,
3458c2ecf20Sopenharmony_ci	.playback = {
3468c2ecf20Sopenharmony_ci		.channels_min = 2,
3478c2ecf20Sopenharmony_ci		.channels_max = 2,
3488c2ecf20Sopenharmony_ci		.rates = PXA2XX_I2S_RATES,
3498c2ecf20Sopenharmony_ci		.formats = SNDRV_PCM_FMTBIT_S16_LE,},
3508c2ecf20Sopenharmony_ci	.capture = {
3518c2ecf20Sopenharmony_ci		.channels_min = 2,
3528c2ecf20Sopenharmony_ci		.channels_max = 2,
3538c2ecf20Sopenharmony_ci		.rates = PXA2XX_I2S_RATES,
3548c2ecf20Sopenharmony_ci		.formats = SNDRV_PCM_FMTBIT_S16_LE,},
3558c2ecf20Sopenharmony_ci	.ops = &pxa_i2s_dai_ops,
3568c2ecf20Sopenharmony_ci	.symmetric_rates = 1,
3578c2ecf20Sopenharmony_ci};
3588c2ecf20Sopenharmony_ci
3598c2ecf20Sopenharmony_cistatic const struct snd_soc_component_driver pxa_i2s_component = {
3608c2ecf20Sopenharmony_ci	.name		= "pxa-i2s",
3618c2ecf20Sopenharmony_ci	.pcm_construct	= pxa2xx_soc_pcm_new,
3628c2ecf20Sopenharmony_ci	.pcm_destruct	= pxa2xx_soc_pcm_free,
3638c2ecf20Sopenharmony_ci	.open		= pxa2xx_soc_pcm_open,
3648c2ecf20Sopenharmony_ci	.close		= pxa2xx_soc_pcm_close,
3658c2ecf20Sopenharmony_ci	.hw_params	= pxa2xx_soc_pcm_hw_params,
3668c2ecf20Sopenharmony_ci	.hw_free	= pxa2xx_soc_pcm_hw_free,
3678c2ecf20Sopenharmony_ci	.prepare	= pxa2xx_soc_pcm_prepare,
3688c2ecf20Sopenharmony_ci	.trigger	= pxa2xx_soc_pcm_trigger,
3698c2ecf20Sopenharmony_ci	.pointer	= pxa2xx_soc_pcm_pointer,
3708c2ecf20Sopenharmony_ci	.mmap		= pxa2xx_soc_pcm_mmap,
3718c2ecf20Sopenharmony_ci	.suspend	= pxa2xx_soc_pcm_suspend,
3728c2ecf20Sopenharmony_ci	.resume		= pxa2xx_soc_pcm_resume,
3738c2ecf20Sopenharmony_ci};
3748c2ecf20Sopenharmony_ci
3758c2ecf20Sopenharmony_cistatic int pxa2xx_i2s_drv_probe(struct platform_device *pdev)
3768c2ecf20Sopenharmony_ci{
3778c2ecf20Sopenharmony_ci	return devm_snd_soc_register_component(&pdev->dev, &pxa_i2s_component,
3788c2ecf20Sopenharmony_ci					       &pxa_i2s_dai, 1);
3798c2ecf20Sopenharmony_ci}
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_cistatic struct platform_driver pxa2xx_i2s_driver = {
3828c2ecf20Sopenharmony_ci	.probe = pxa2xx_i2s_drv_probe,
3838c2ecf20Sopenharmony_ci
3848c2ecf20Sopenharmony_ci	.driver = {
3858c2ecf20Sopenharmony_ci		.name = "pxa2xx-i2s",
3868c2ecf20Sopenharmony_ci	},
3878c2ecf20Sopenharmony_ci};
3888c2ecf20Sopenharmony_ci
3898c2ecf20Sopenharmony_cistatic int __init pxa2xx_i2s_init(void)
3908c2ecf20Sopenharmony_ci{
3918c2ecf20Sopenharmony_ci	clk_i2s = ERR_PTR(-ENOENT);
3928c2ecf20Sopenharmony_ci	return platform_driver_register(&pxa2xx_i2s_driver);
3938c2ecf20Sopenharmony_ci}
3948c2ecf20Sopenharmony_ci
3958c2ecf20Sopenharmony_cistatic void __exit pxa2xx_i2s_exit(void)
3968c2ecf20Sopenharmony_ci{
3978c2ecf20Sopenharmony_ci	platform_driver_unregister(&pxa2xx_i2s_driver);
3988c2ecf20Sopenharmony_ci}
3998c2ecf20Sopenharmony_ci
4008c2ecf20Sopenharmony_cimodule_init(pxa2xx_i2s_init);
4018c2ecf20Sopenharmony_cimodule_exit(pxa2xx_i2s_exit);
4028c2ecf20Sopenharmony_ci
4038c2ecf20Sopenharmony_ci/* Module information */
4048c2ecf20Sopenharmony_ciMODULE_AUTHOR("Liam Girdwood, lrg@slimlogic.co.uk");
4058c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("pxa2xx I2S SoC Interface");
4068c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
4078c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:pxa2xx-i2s");
408