xref: /openbmc/linux/sound/soc/fsl/fsl_esai.c (revision 43d24e76b69826ce32292f47060ad78cdd0197fa)
1*43d24e76SNicolin Chen /*
2*43d24e76SNicolin Chen  * Freescale ESAI ALSA SoC Digital Audio Interface (DAI) driver
3*43d24e76SNicolin Chen  *
4*43d24e76SNicolin Chen  * Copyright (C) 2014 Freescale Semiconductor, Inc.
5*43d24e76SNicolin Chen  *
6*43d24e76SNicolin Chen  * This file is licensed under the terms of the GNU General Public License
7*43d24e76SNicolin Chen  * version 2. This program is licensed "as is" without any warranty of any
8*43d24e76SNicolin Chen  * kind, whether express or implied.
9*43d24e76SNicolin Chen  */
10*43d24e76SNicolin Chen 
11*43d24e76SNicolin Chen #include <linux/clk.h>
12*43d24e76SNicolin Chen #include <linux/dmaengine.h>
13*43d24e76SNicolin Chen #include <linux/module.h>
14*43d24e76SNicolin Chen #include <linux/of_irq.h>
15*43d24e76SNicolin Chen #include <linux/of_platform.h>
16*43d24e76SNicolin Chen #include <sound/dmaengine_pcm.h>
17*43d24e76SNicolin Chen #include <sound/pcm_params.h>
18*43d24e76SNicolin Chen 
19*43d24e76SNicolin Chen #include "fsl_esai.h"
20*43d24e76SNicolin Chen #include "imx-pcm.h"
21*43d24e76SNicolin Chen 
22*43d24e76SNicolin Chen #define FSL_ESAI_RATES		SNDRV_PCM_RATE_8000_192000
23*43d24e76SNicolin Chen #define FSL_ESAI_FORMATS	(SNDRV_PCM_FMTBIT_S8 | \
24*43d24e76SNicolin Chen 				SNDRV_PCM_FMTBIT_S16_LE | \
25*43d24e76SNicolin Chen 				SNDRV_PCM_FMTBIT_S20_3LE | \
26*43d24e76SNicolin Chen 				SNDRV_PCM_FMTBIT_S24_LE)
27*43d24e76SNicolin Chen 
28*43d24e76SNicolin Chen /**
29*43d24e76SNicolin Chen  * fsl_esai: ESAI private data
30*43d24e76SNicolin Chen  *
31*43d24e76SNicolin Chen  * @dma_params_rx: DMA parameters for receive channel
32*43d24e76SNicolin Chen  * @dma_params_tx: DMA parameters for transmit channel
33*43d24e76SNicolin Chen  * @pdev: platform device pointer
34*43d24e76SNicolin Chen  * @regmap: regmap handler
35*43d24e76SNicolin Chen  * @coreclk: clock source to access register
36*43d24e76SNicolin Chen  * @extalclk: esai clock source to derive HCK, SCK and FS
37*43d24e76SNicolin Chen  * @fsysclk: system clock source to derive HCK, SCK and FS
38*43d24e76SNicolin Chen  * @fifo_depth: depth of tx/rx FIFO
39*43d24e76SNicolin Chen  * @slot_width: width of each DAI slot
40*43d24e76SNicolin Chen  * @hck_rate: clock rate of desired HCKx clock
41*43d24e76SNicolin Chen  * @sck_div: if using PSR/PM dividers for SCKx clock
42*43d24e76SNicolin Chen  * @slave_mode: if fully using DAI slave mode
43*43d24e76SNicolin Chen  * @synchronous: if using tx/rx synchronous mode
44*43d24e76SNicolin Chen  * @name: driver name
45*43d24e76SNicolin Chen  */
46*43d24e76SNicolin Chen struct fsl_esai {
47*43d24e76SNicolin Chen 	struct snd_dmaengine_dai_dma_data dma_params_rx;
48*43d24e76SNicolin Chen 	struct snd_dmaengine_dai_dma_data dma_params_tx;
49*43d24e76SNicolin Chen 	struct platform_device *pdev;
50*43d24e76SNicolin Chen 	struct regmap *regmap;
51*43d24e76SNicolin Chen 	struct clk *coreclk;
52*43d24e76SNicolin Chen 	struct clk *extalclk;
53*43d24e76SNicolin Chen 	struct clk *fsysclk;
54*43d24e76SNicolin Chen 	u32 fifo_depth;
55*43d24e76SNicolin Chen 	u32 slot_width;
56*43d24e76SNicolin Chen 	u32 hck_rate[2];
57*43d24e76SNicolin Chen 	bool sck_div[2];
58*43d24e76SNicolin Chen 	bool slave_mode;
59*43d24e76SNicolin Chen 	bool synchronous;
60*43d24e76SNicolin Chen 	char name[32];
61*43d24e76SNicolin Chen };
62*43d24e76SNicolin Chen 
63*43d24e76SNicolin Chen static irqreturn_t esai_isr(int irq, void *devid)
64*43d24e76SNicolin Chen {
65*43d24e76SNicolin Chen 	struct fsl_esai *esai_priv = (struct fsl_esai *)devid;
66*43d24e76SNicolin Chen 	struct platform_device *pdev = esai_priv->pdev;
67*43d24e76SNicolin Chen 	u32 esr;
68*43d24e76SNicolin Chen 
69*43d24e76SNicolin Chen 	regmap_read(esai_priv->regmap, REG_ESAI_ESR, &esr);
70*43d24e76SNicolin Chen 
71*43d24e76SNicolin Chen 	if (esr & ESAI_ESR_TINIT_MASK)
72*43d24e76SNicolin Chen 		dev_dbg(&pdev->dev, "isr: Transmition Initialized\n");
73*43d24e76SNicolin Chen 
74*43d24e76SNicolin Chen 	if (esr & ESAI_ESR_RFF_MASK)
75*43d24e76SNicolin Chen 		dev_warn(&pdev->dev, "isr: Receiving overrun\n");
76*43d24e76SNicolin Chen 
77*43d24e76SNicolin Chen 	if (esr & ESAI_ESR_TFE_MASK)
78*43d24e76SNicolin Chen 		dev_warn(&pdev->dev, "isr: Transmition underrun\n");
79*43d24e76SNicolin Chen 
80*43d24e76SNicolin Chen 	if (esr & ESAI_ESR_TLS_MASK)
81*43d24e76SNicolin Chen 		dev_dbg(&pdev->dev, "isr: Just transmitted the last slot\n");
82*43d24e76SNicolin Chen 
83*43d24e76SNicolin Chen 	if (esr & ESAI_ESR_TDE_MASK)
84*43d24e76SNicolin Chen 		dev_dbg(&pdev->dev, "isr: Transmition data exception\n");
85*43d24e76SNicolin Chen 
86*43d24e76SNicolin Chen 	if (esr & ESAI_ESR_TED_MASK)
87*43d24e76SNicolin Chen 		dev_dbg(&pdev->dev, "isr: Transmitting even slots\n");
88*43d24e76SNicolin Chen 
89*43d24e76SNicolin Chen 	if (esr & ESAI_ESR_TD_MASK)
90*43d24e76SNicolin Chen 		dev_dbg(&pdev->dev, "isr: Transmitting data\n");
91*43d24e76SNicolin Chen 
92*43d24e76SNicolin Chen 	if (esr & ESAI_ESR_RLS_MASK)
93*43d24e76SNicolin Chen 		dev_dbg(&pdev->dev, "isr: Just received the last slot\n");
94*43d24e76SNicolin Chen 
95*43d24e76SNicolin Chen 	if (esr & ESAI_ESR_RDE_MASK)
96*43d24e76SNicolin Chen 		dev_dbg(&pdev->dev, "isr: Receiving data exception\n");
97*43d24e76SNicolin Chen 
98*43d24e76SNicolin Chen 	if (esr & ESAI_ESR_RED_MASK)
99*43d24e76SNicolin Chen 		dev_dbg(&pdev->dev, "isr: Receiving even slots\n");
100*43d24e76SNicolin Chen 
101*43d24e76SNicolin Chen 	if (esr & ESAI_ESR_RD_MASK)
102*43d24e76SNicolin Chen 		dev_dbg(&pdev->dev, "isr: Receiving data\n");
103*43d24e76SNicolin Chen 
104*43d24e76SNicolin Chen 	return IRQ_HANDLED;
105*43d24e76SNicolin Chen }
106*43d24e76SNicolin Chen 
107*43d24e76SNicolin Chen /**
108*43d24e76SNicolin Chen  * This function is used to calculate the divisors of psr, pm, fp and it is
109*43d24e76SNicolin Chen  * supposed to be called in set_dai_sysclk() and set_bclk().
110*43d24e76SNicolin Chen  *
111*43d24e76SNicolin Chen  * @ratio: desired overall ratio for the paticipating dividers
112*43d24e76SNicolin Chen  * @usefp: for HCK setting, there is no need to set fp divider
113*43d24e76SNicolin Chen  * @fp: bypass other dividers by setting fp directly if fp != 0
114*43d24e76SNicolin Chen  * @tx: current setting is for playback or capture
115*43d24e76SNicolin Chen  */
116*43d24e76SNicolin Chen static int fsl_esai_divisor_cal(struct snd_soc_dai *dai, bool tx, u32 ratio,
117*43d24e76SNicolin Chen 				bool usefp, u32 fp)
118*43d24e76SNicolin Chen {
119*43d24e76SNicolin Chen 	struct fsl_esai *esai_priv = snd_soc_dai_get_drvdata(dai);
120*43d24e76SNicolin Chen 	u32 psr, pm = 999, maxfp, prod, sub, savesub, i, j;
121*43d24e76SNicolin Chen 
122*43d24e76SNicolin Chen 	maxfp = usefp ? 16 : 1;
123*43d24e76SNicolin Chen 
124*43d24e76SNicolin Chen 	if (usefp && fp)
125*43d24e76SNicolin Chen 		goto out_fp;
126*43d24e76SNicolin Chen 
127*43d24e76SNicolin Chen 	if (ratio > 2 * 8 * 256 * maxfp || ratio < 2) {
128*43d24e76SNicolin Chen 		dev_err(dai->dev, "the ratio is out of range (2 ~ %d)\n",
129*43d24e76SNicolin Chen 				2 * 8 * 256 * maxfp);
130*43d24e76SNicolin Chen 		return -EINVAL;
131*43d24e76SNicolin Chen 	} else if (ratio % 2) {
132*43d24e76SNicolin Chen 		dev_err(dai->dev, "the raio must be even if using upper divider\n");
133*43d24e76SNicolin Chen 		return -EINVAL;
134*43d24e76SNicolin Chen 	}
135*43d24e76SNicolin Chen 
136*43d24e76SNicolin Chen 	ratio /= 2;
137*43d24e76SNicolin Chen 
138*43d24e76SNicolin Chen 	psr = ratio <= 256 * maxfp ? ESAI_xCCR_xPSR_BYPASS : ESAI_xCCR_xPSR_DIV8;
139*43d24e76SNicolin Chen 
140*43d24e76SNicolin Chen 	/* Set the max fluctuation -- 0.1% of the max devisor */
141*43d24e76SNicolin Chen 	savesub = (psr ? 1 : 8)  * 256 * maxfp / 1000;
142*43d24e76SNicolin Chen 
143*43d24e76SNicolin Chen 	/* Find the best value for PM */
144*43d24e76SNicolin Chen 	for (i = 1; i <= 256; i++) {
145*43d24e76SNicolin Chen 		for (j = 1; j <= maxfp; j++) {
146*43d24e76SNicolin Chen 			/* PSR (1 or 8) * PM (1 ~ 256) * FP (1 ~ 16) */
147*43d24e76SNicolin Chen 			prod = (psr ? 1 : 8) * i * j;
148*43d24e76SNicolin Chen 
149*43d24e76SNicolin Chen 			if (prod == ratio)
150*43d24e76SNicolin Chen 				sub = 0;
151*43d24e76SNicolin Chen 			else if (prod / ratio == 1)
152*43d24e76SNicolin Chen 				sub = prod - ratio;
153*43d24e76SNicolin Chen 			else if (ratio / prod == 1)
154*43d24e76SNicolin Chen 				sub = ratio - prod;
155*43d24e76SNicolin Chen 			else
156*43d24e76SNicolin Chen 				continue;
157*43d24e76SNicolin Chen 
158*43d24e76SNicolin Chen 			/* Calculate the fraction */
159*43d24e76SNicolin Chen 			sub = sub * 1000 / ratio;
160*43d24e76SNicolin Chen 			if (sub < savesub) {
161*43d24e76SNicolin Chen 				savesub = sub;
162*43d24e76SNicolin Chen 				pm = i;
163*43d24e76SNicolin Chen 				fp = j;
164*43d24e76SNicolin Chen 			}
165*43d24e76SNicolin Chen 
166*43d24e76SNicolin Chen 			/* We are lucky */
167*43d24e76SNicolin Chen 			if (savesub == 0)
168*43d24e76SNicolin Chen 				goto out;
169*43d24e76SNicolin Chen 		}
170*43d24e76SNicolin Chen 	}
171*43d24e76SNicolin Chen 
172*43d24e76SNicolin Chen 	if (pm == 999) {
173*43d24e76SNicolin Chen 		dev_err(dai->dev, "failed to calculate proper divisors\n");
174*43d24e76SNicolin Chen 		return -EINVAL;
175*43d24e76SNicolin Chen 	}
176*43d24e76SNicolin Chen 
177*43d24e76SNicolin Chen out:
178*43d24e76SNicolin Chen 	regmap_update_bits(esai_priv->regmap, REG_ESAI_xCCR(tx),
179*43d24e76SNicolin Chen 			   ESAI_xCCR_xPSR_MASK | ESAI_xCCR_xPM_MASK,
180*43d24e76SNicolin Chen 			   psr | ESAI_xCCR_xPM(pm));
181*43d24e76SNicolin Chen 
182*43d24e76SNicolin Chen out_fp:
183*43d24e76SNicolin Chen 	/* Bypass fp if not being required */
184*43d24e76SNicolin Chen 	if (maxfp <= 1)
185*43d24e76SNicolin Chen 		return 0;
186*43d24e76SNicolin Chen 
187*43d24e76SNicolin Chen 	regmap_update_bits(esai_priv->regmap, REG_ESAI_xCCR(tx),
188*43d24e76SNicolin Chen 			   ESAI_xCCR_xFP_MASK, ESAI_xCCR_xFP(fp));
189*43d24e76SNicolin Chen 
190*43d24e76SNicolin Chen 	return 0;
191*43d24e76SNicolin Chen }
192*43d24e76SNicolin Chen 
193*43d24e76SNicolin Chen /**
194*43d24e76SNicolin Chen  * This function mainly configures the clock frequency of MCLK (HCKT/HCKR)
195*43d24e76SNicolin Chen  *
196*43d24e76SNicolin Chen  * @Parameters:
197*43d24e76SNicolin Chen  * clk_id: The clock source of HCKT/HCKR
198*43d24e76SNicolin Chen  *	  (Input from outside; output from inside, FSYS or EXTAL)
199*43d24e76SNicolin Chen  * freq: The required clock rate of HCKT/HCKR
200*43d24e76SNicolin Chen  * dir: The clock direction of HCKT/HCKR
201*43d24e76SNicolin Chen  *
202*43d24e76SNicolin Chen  * Note: If the direction is input, we do not care about clk_id.
203*43d24e76SNicolin Chen  */
204*43d24e76SNicolin Chen static int fsl_esai_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id,
205*43d24e76SNicolin Chen 				   unsigned int freq, int dir)
206*43d24e76SNicolin Chen {
207*43d24e76SNicolin Chen 	struct fsl_esai *esai_priv = snd_soc_dai_get_drvdata(dai);
208*43d24e76SNicolin Chen 	struct clk *clksrc = esai_priv->extalclk;
209*43d24e76SNicolin Chen 	bool tx = clk_id <= ESAI_HCKT_EXTAL;
210*43d24e76SNicolin Chen 	bool in = dir == SND_SOC_CLOCK_IN;
211*43d24e76SNicolin Chen 	u32 ret, ratio, ecr = 0;
212*43d24e76SNicolin Chen 	unsigned long clk_rate;
213*43d24e76SNicolin Chen 
214*43d24e76SNicolin Chen 	/* sck_div can be only bypassed if ETO/ERO=0 and SNC_SOC_CLOCK_OUT */
215*43d24e76SNicolin Chen 	esai_priv->sck_div[tx] = true;
216*43d24e76SNicolin Chen 
217*43d24e76SNicolin Chen 	/* Set the direction of HCKT/HCKR pins */
218*43d24e76SNicolin Chen 	regmap_update_bits(esai_priv->regmap, REG_ESAI_xCCR(tx),
219*43d24e76SNicolin Chen 			   ESAI_xCCR_xHCKD, in ? 0 : ESAI_xCCR_xHCKD);
220*43d24e76SNicolin Chen 
221*43d24e76SNicolin Chen 	if (in)
222*43d24e76SNicolin Chen 		goto out;
223*43d24e76SNicolin Chen 
224*43d24e76SNicolin Chen 	switch (clk_id) {
225*43d24e76SNicolin Chen 	case ESAI_HCKT_FSYS:
226*43d24e76SNicolin Chen 	case ESAI_HCKR_FSYS:
227*43d24e76SNicolin Chen 		clksrc = esai_priv->fsysclk;
228*43d24e76SNicolin Chen 		break;
229*43d24e76SNicolin Chen 	case ESAI_HCKT_EXTAL:
230*43d24e76SNicolin Chen 		ecr |= ESAI_ECR_ETI;
231*43d24e76SNicolin Chen 	case ESAI_HCKR_EXTAL:
232*43d24e76SNicolin Chen 		ecr |= ESAI_ECR_ERI;
233*43d24e76SNicolin Chen 		break;
234*43d24e76SNicolin Chen 	default:
235*43d24e76SNicolin Chen 		return -EINVAL;
236*43d24e76SNicolin Chen 	}
237*43d24e76SNicolin Chen 
238*43d24e76SNicolin Chen 	if (IS_ERR(clksrc)) {
239*43d24e76SNicolin Chen 		dev_err(dai->dev, "no assigned %s clock\n",
240*43d24e76SNicolin Chen 				clk_id % 2 ? "extal" : "fsys");
241*43d24e76SNicolin Chen 		return PTR_ERR(clksrc);
242*43d24e76SNicolin Chen 	}
243*43d24e76SNicolin Chen 	clk_rate = clk_get_rate(clksrc);
244*43d24e76SNicolin Chen 
245*43d24e76SNicolin Chen 	ratio = clk_rate / freq;
246*43d24e76SNicolin Chen 	if (ratio * freq > clk_rate)
247*43d24e76SNicolin Chen 		ret = ratio * freq - clk_rate;
248*43d24e76SNicolin Chen 	else if (ratio * freq < clk_rate)
249*43d24e76SNicolin Chen 		ret = clk_rate - ratio * freq;
250*43d24e76SNicolin Chen 	else
251*43d24e76SNicolin Chen 		ret = 0;
252*43d24e76SNicolin Chen 
253*43d24e76SNicolin Chen 	/* Block if clock source can not be divided into the required rate */
254*43d24e76SNicolin Chen 	if (ret != 0 && clk_rate / ret < 1000) {
255*43d24e76SNicolin Chen 		dev_err(dai->dev, "failed to derive required HCK%c rate\n",
256*43d24e76SNicolin Chen 				tx ? 'T' : 'R');
257*43d24e76SNicolin Chen 		return -EINVAL;
258*43d24e76SNicolin Chen 	}
259*43d24e76SNicolin Chen 
260*43d24e76SNicolin Chen 	if (ratio == 1) {
261*43d24e76SNicolin Chen 		/* Bypass all the dividers if not being needed */
262*43d24e76SNicolin Chen 		ecr |= tx ? ESAI_ECR_ETO : ESAI_ECR_ERO;
263*43d24e76SNicolin Chen 		goto out;
264*43d24e76SNicolin Chen 	}
265*43d24e76SNicolin Chen 
266*43d24e76SNicolin Chen 	ret = fsl_esai_divisor_cal(dai, tx, ratio, false, 0);
267*43d24e76SNicolin Chen 	if (ret)
268*43d24e76SNicolin Chen 		return ret;
269*43d24e76SNicolin Chen 
270*43d24e76SNicolin Chen 	esai_priv->sck_div[tx] = false;
271*43d24e76SNicolin Chen 
272*43d24e76SNicolin Chen out:
273*43d24e76SNicolin Chen 	esai_priv->hck_rate[tx] = freq;
274*43d24e76SNicolin Chen 
275*43d24e76SNicolin Chen 	regmap_update_bits(esai_priv->regmap, REG_ESAI_ECR,
276*43d24e76SNicolin Chen 			   tx ? ESAI_ECR_ETI | ESAI_ECR_ETO :
277*43d24e76SNicolin Chen 			   ESAI_ECR_ERI | ESAI_ECR_ERO, ecr);
278*43d24e76SNicolin Chen 
279*43d24e76SNicolin Chen 	return 0;
280*43d24e76SNicolin Chen }
281*43d24e76SNicolin Chen 
282*43d24e76SNicolin Chen /**
283*43d24e76SNicolin Chen  * This function configures the related dividers according to the bclk rate
284*43d24e76SNicolin Chen  */
285*43d24e76SNicolin Chen static int fsl_esai_set_bclk(struct snd_soc_dai *dai, bool tx, u32 freq)
286*43d24e76SNicolin Chen {
287*43d24e76SNicolin Chen 	struct fsl_esai *esai_priv = snd_soc_dai_get_drvdata(dai);
288*43d24e76SNicolin Chen 	u32 hck_rate = esai_priv->hck_rate[tx];
289*43d24e76SNicolin Chen 	u32 sub, ratio = hck_rate / freq;
290*43d24e76SNicolin Chen 
291*43d24e76SNicolin Chen 	/* Don't apply for fully slave mode*/
292*43d24e76SNicolin Chen 	if (esai_priv->slave_mode)
293*43d24e76SNicolin Chen 		return 0;
294*43d24e76SNicolin Chen 
295*43d24e76SNicolin Chen 	if (ratio * freq > hck_rate)
296*43d24e76SNicolin Chen 		sub = ratio * freq - hck_rate;
297*43d24e76SNicolin Chen 	else if (ratio * freq < hck_rate)
298*43d24e76SNicolin Chen 		sub = hck_rate - ratio * freq;
299*43d24e76SNicolin Chen 	else
300*43d24e76SNicolin Chen 		sub = 0;
301*43d24e76SNicolin Chen 
302*43d24e76SNicolin Chen 	/* Block if clock source can not be divided into the required rate */
303*43d24e76SNicolin Chen 	if (sub != 0 && hck_rate / sub < 1000) {
304*43d24e76SNicolin Chen 		dev_err(dai->dev, "failed to derive required SCK%c rate\n",
305*43d24e76SNicolin Chen 				tx ? 'T' : 'R');
306*43d24e76SNicolin Chen 		return -EINVAL;
307*43d24e76SNicolin Chen 	}
308*43d24e76SNicolin Chen 
309*43d24e76SNicolin Chen 	if (esai_priv->sck_div[tx] && (ratio > 16 || ratio == 0)) {
310*43d24e76SNicolin Chen 		dev_err(dai->dev, "the ratio is out of range (1 ~ 16)\n");
311*43d24e76SNicolin Chen 		return -EINVAL;
312*43d24e76SNicolin Chen 	}
313*43d24e76SNicolin Chen 
314*43d24e76SNicolin Chen 	return fsl_esai_divisor_cal(dai, tx, ratio, true,
315*43d24e76SNicolin Chen 			esai_priv->sck_div[tx] ? 0 : ratio);
316*43d24e76SNicolin Chen }
317*43d24e76SNicolin Chen 
318*43d24e76SNicolin Chen static int fsl_esai_set_dai_tdm_slot(struct snd_soc_dai *dai, u32 tx_mask,
319*43d24e76SNicolin Chen 				     u32 rx_mask, int slots, int slot_width)
320*43d24e76SNicolin Chen {
321*43d24e76SNicolin Chen 	struct fsl_esai *esai_priv = snd_soc_dai_get_drvdata(dai);
322*43d24e76SNicolin Chen 
323*43d24e76SNicolin Chen 	regmap_update_bits(esai_priv->regmap, REG_ESAI_TCCR,
324*43d24e76SNicolin Chen 			   ESAI_xCCR_xDC_MASK, ESAI_xCCR_xDC(slots));
325*43d24e76SNicolin Chen 
326*43d24e76SNicolin Chen 	regmap_update_bits(esai_priv->regmap, REG_ESAI_TSMA,
327*43d24e76SNicolin Chen 			   ESAI_xSMA_xS_MASK, ESAI_xSMA_xS(tx_mask));
328*43d24e76SNicolin Chen 	regmap_update_bits(esai_priv->regmap, REG_ESAI_TSMB,
329*43d24e76SNicolin Chen 			   ESAI_xSMA_xS_MASK, ESAI_xSMB_xS(tx_mask));
330*43d24e76SNicolin Chen 
331*43d24e76SNicolin Chen 	regmap_update_bits(esai_priv->regmap, REG_ESAI_RCCR,
332*43d24e76SNicolin Chen 			   ESAI_xCCR_xDC_MASK, ESAI_xCCR_xDC(slots));
333*43d24e76SNicolin Chen 
334*43d24e76SNicolin Chen 	regmap_update_bits(esai_priv->regmap, REG_ESAI_RSMA,
335*43d24e76SNicolin Chen 			   ESAI_xSMA_xS_MASK, ESAI_xSMA_xS(rx_mask));
336*43d24e76SNicolin Chen 	regmap_update_bits(esai_priv->regmap, REG_ESAI_RSMB,
337*43d24e76SNicolin Chen 			   ESAI_xSMA_xS_MASK, ESAI_xSMB_xS(rx_mask));
338*43d24e76SNicolin Chen 
339*43d24e76SNicolin Chen 	esai_priv->slot_width = slot_width;
340*43d24e76SNicolin Chen 
341*43d24e76SNicolin Chen 	return 0;
342*43d24e76SNicolin Chen }
343*43d24e76SNicolin Chen 
344*43d24e76SNicolin Chen static int fsl_esai_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
345*43d24e76SNicolin Chen {
346*43d24e76SNicolin Chen 	struct fsl_esai *esai_priv = snd_soc_dai_get_drvdata(dai);
347*43d24e76SNicolin Chen 	u32 xcr = 0, xccr = 0, mask;
348*43d24e76SNicolin Chen 
349*43d24e76SNicolin Chen 	/* DAI mode */
350*43d24e76SNicolin Chen 	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
351*43d24e76SNicolin Chen 	case SND_SOC_DAIFMT_I2S:
352*43d24e76SNicolin Chen 		/* Data on rising edge of bclk, frame low, 1clk before data */
353*43d24e76SNicolin Chen 		xcr |= ESAI_xCR_xFSR;
354*43d24e76SNicolin Chen 		xccr |= ESAI_xCCR_xFSP | ESAI_xCCR_xCKP | ESAI_xCCR_xHCKP;
355*43d24e76SNicolin Chen 		break;
356*43d24e76SNicolin Chen 	case SND_SOC_DAIFMT_LEFT_J:
357*43d24e76SNicolin Chen 		/* Data on rising edge of bclk, frame high */
358*43d24e76SNicolin Chen 		xccr |= ESAI_xCCR_xCKP | ESAI_xCCR_xHCKP;
359*43d24e76SNicolin Chen 		break;
360*43d24e76SNicolin Chen 	case SND_SOC_DAIFMT_RIGHT_J:
361*43d24e76SNicolin Chen 		/* Data on rising edge of bclk, frame high, right aligned */
362*43d24e76SNicolin Chen 		xccr |= ESAI_xCCR_xCKP | ESAI_xCCR_xHCKP | ESAI_xCR_xWA;
363*43d24e76SNicolin Chen 		break;
364*43d24e76SNicolin Chen 	case SND_SOC_DAIFMT_DSP_A:
365*43d24e76SNicolin Chen 		/* Data on rising edge of bclk, frame high, 1clk before data */
366*43d24e76SNicolin Chen 		xcr |= ESAI_xCR_xFSL | ESAI_xCR_xFSR;
367*43d24e76SNicolin Chen 		xccr |= ESAI_xCCR_xCKP | ESAI_xCCR_xHCKP;
368*43d24e76SNicolin Chen 		break;
369*43d24e76SNicolin Chen 	case SND_SOC_DAIFMT_DSP_B:
370*43d24e76SNicolin Chen 		/* Data on rising edge of bclk, frame high */
371*43d24e76SNicolin Chen 		xcr |= ESAI_xCR_xFSL;
372*43d24e76SNicolin Chen 		xccr |= ESAI_xCCR_xCKP | ESAI_xCCR_xHCKP;
373*43d24e76SNicolin Chen 		break;
374*43d24e76SNicolin Chen 	default:
375*43d24e76SNicolin Chen 		return -EINVAL;
376*43d24e76SNicolin Chen 	}
377*43d24e76SNicolin Chen 
378*43d24e76SNicolin Chen 	/* DAI clock inversion */
379*43d24e76SNicolin Chen 	switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
380*43d24e76SNicolin Chen 	case SND_SOC_DAIFMT_NB_NF:
381*43d24e76SNicolin Chen 		/* Nothing to do for both normal cases */
382*43d24e76SNicolin Chen 		break;
383*43d24e76SNicolin Chen 	case SND_SOC_DAIFMT_IB_NF:
384*43d24e76SNicolin Chen 		/* Invert bit clock */
385*43d24e76SNicolin Chen 		xccr ^= ESAI_xCCR_xCKP | ESAI_xCCR_xHCKP;
386*43d24e76SNicolin Chen 		break;
387*43d24e76SNicolin Chen 	case SND_SOC_DAIFMT_NB_IF:
388*43d24e76SNicolin Chen 		/* Invert frame clock */
389*43d24e76SNicolin Chen 		xccr ^= ESAI_xCCR_xFSP;
390*43d24e76SNicolin Chen 		break;
391*43d24e76SNicolin Chen 	case SND_SOC_DAIFMT_IB_IF:
392*43d24e76SNicolin Chen 		/* Invert both clocks */
393*43d24e76SNicolin Chen 		xccr ^= ESAI_xCCR_xCKP | ESAI_xCCR_xHCKP | ESAI_xCCR_xFSP;
394*43d24e76SNicolin Chen 		break;
395*43d24e76SNicolin Chen 	default:
396*43d24e76SNicolin Chen 		return -EINVAL;
397*43d24e76SNicolin Chen 	}
398*43d24e76SNicolin Chen 
399*43d24e76SNicolin Chen 	esai_priv->slave_mode = false;
400*43d24e76SNicolin Chen 
401*43d24e76SNicolin Chen 	/* DAI clock master masks */
402*43d24e76SNicolin Chen 	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
403*43d24e76SNicolin Chen 	case SND_SOC_DAIFMT_CBM_CFM:
404*43d24e76SNicolin Chen 		esai_priv->slave_mode = true;
405*43d24e76SNicolin Chen 		break;
406*43d24e76SNicolin Chen 	case SND_SOC_DAIFMT_CBS_CFM:
407*43d24e76SNicolin Chen 		xccr |= ESAI_xCCR_xCKD;
408*43d24e76SNicolin Chen 		break;
409*43d24e76SNicolin Chen 	case SND_SOC_DAIFMT_CBM_CFS:
410*43d24e76SNicolin Chen 		xccr |= ESAI_xCCR_xFSD;
411*43d24e76SNicolin Chen 		break;
412*43d24e76SNicolin Chen 	case SND_SOC_DAIFMT_CBS_CFS:
413*43d24e76SNicolin Chen 		xccr |= ESAI_xCCR_xFSD | ESAI_xCCR_xCKD;
414*43d24e76SNicolin Chen 		break;
415*43d24e76SNicolin Chen 	default:
416*43d24e76SNicolin Chen 		return -EINVAL;
417*43d24e76SNicolin Chen 	}
418*43d24e76SNicolin Chen 
419*43d24e76SNicolin Chen 	mask = ESAI_xCR_xFSL | ESAI_xCR_xFSR;
420*43d24e76SNicolin Chen 	regmap_update_bits(esai_priv->regmap, REG_ESAI_TCR, mask, xcr);
421*43d24e76SNicolin Chen 	regmap_update_bits(esai_priv->regmap, REG_ESAI_RCR, mask, xcr);
422*43d24e76SNicolin Chen 
423*43d24e76SNicolin Chen 	mask = ESAI_xCCR_xCKP | ESAI_xCCR_xHCKP | ESAI_xCCR_xFSP |
424*43d24e76SNicolin Chen 		ESAI_xCCR_xFSD | ESAI_xCCR_xCKD | ESAI_xCR_xWA;
425*43d24e76SNicolin Chen 	regmap_update_bits(esai_priv->regmap, REG_ESAI_TCCR, mask, xccr);
426*43d24e76SNicolin Chen 	regmap_update_bits(esai_priv->regmap, REG_ESAI_RCCR, mask, xccr);
427*43d24e76SNicolin Chen 
428*43d24e76SNicolin Chen 	return 0;
429*43d24e76SNicolin Chen }
430*43d24e76SNicolin Chen 
431*43d24e76SNicolin Chen static int fsl_esai_startup(struct snd_pcm_substream *substream,
432*43d24e76SNicolin Chen 			    struct snd_soc_dai *dai)
433*43d24e76SNicolin Chen {
434*43d24e76SNicolin Chen 	struct fsl_esai *esai_priv = snd_soc_dai_get_drvdata(dai);
435*43d24e76SNicolin Chen 
436*43d24e76SNicolin Chen 	/*
437*43d24e76SNicolin Chen 	 * Some platforms might use the same bit to gate all three or two of
438*43d24e76SNicolin Chen 	 * clocks, so keep all clocks open/close at the same time for safety
439*43d24e76SNicolin Chen 	 */
440*43d24e76SNicolin Chen 	clk_prepare_enable(esai_priv->coreclk);
441*43d24e76SNicolin Chen 	if (!IS_ERR(esai_priv->extalclk))
442*43d24e76SNicolin Chen 		clk_prepare_enable(esai_priv->extalclk);
443*43d24e76SNicolin Chen 	if (!IS_ERR(esai_priv->fsysclk))
444*43d24e76SNicolin Chen 		clk_prepare_enable(esai_priv->fsysclk);
445*43d24e76SNicolin Chen 
446*43d24e76SNicolin Chen 	if (!dai->active) {
447*43d24e76SNicolin Chen 		/* Reset Port C */
448*43d24e76SNicolin Chen 		regmap_update_bits(esai_priv->regmap, REG_ESAI_PRRC,
449*43d24e76SNicolin Chen 				   ESAI_PRRC_PDC_MASK, ESAI_PRRC_PDC(ESAI_GPIO));
450*43d24e76SNicolin Chen 		regmap_update_bits(esai_priv->regmap, REG_ESAI_PCRC,
451*43d24e76SNicolin Chen 				   ESAI_PCRC_PC_MASK, ESAI_PCRC_PC(ESAI_GPIO));
452*43d24e76SNicolin Chen 
453*43d24e76SNicolin Chen 		/* Set synchronous mode */
454*43d24e76SNicolin Chen 		regmap_update_bits(esai_priv->regmap, REG_ESAI_SAICR,
455*43d24e76SNicolin Chen 				   ESAI_SAICR_SYNC, esai_priv->synchronous ?
456*43d24e76SNicolin Chen 				   ESAI_SAICR_SYNC : 0);
457*43d24e76SNicolin Chen 
458*43d24e76SNicolin Chen 		/* Set a default slot number -- 2 */
459*43d24e76SNicolin Chen 		regmap_update_bits(esai_priv->regmap, REG_ESAI_TCCR,
460*43d24e76SNicolin Chen 				   ESAI_xCCR_xDC_MASK, ESAI_xCCR_xDC(2));
461*43d24e76SNicolin Chen 		regmap_update_bits(esai_priv->regmap, REG_ESAI_RCCR,
462*43d24e76SNicolin Chen 				   ESAI_xCCR_xDC_MASK, ESAI_xCCR_xDC(2));
463*43d24e76SNicolin Chen 	}
464*43d24e76SNicolin Chen 
465*43d24e76SNicolin Chen 	return 0;
466*43d24e76SNicolin Chen }
467*43d24e76SNicolin Chen 
468*43d24e76SNicolin Chen static int fsl_esai_hw_params(struct snd_pcm_substream *substream,
469*43d24e76SNicolin Chen 			      struct snd_pcm_hw_params *params,
470*43d24e76SNicolin Chen 			      struct snd_soc_dai *dai)
471*43d24e76SNicolin Chen {
472*43d24e76SNicolin Chen 	struct fsl_esai *esai_priv = snd_soc_dai_get_drvdata(dai);
473*43d24e76SNicolin Chen 	bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
474*43d24e76SNicolin Chen 	u32 width = snd_pcm_format_width(params_format(params));
475*43d24e76SNicolin Chen 	u32 channels = params_channels(params);
476*43d24e76SNicolin Chen 	u32 bclk, mask, val, ret;
477*43d24e76SNicolin Chen 
478*43d24e76SNicolin Chen 	bclk = params_rate(params) * esai_priv->slot_width * 2;
479*43d24e76SNicolin Chen 
480*43d24e76SNicolin Chen 	ret = fsl_esai_set_bclk(dai, tx, bclk);
481*43d24e76SNicolin Chen 	if (ret)
482*43d24e76SNicolin Chen 		return ret;
483*43d24e76SNicolin Chen 
484*43d24e76SNicolin Chen 	/* Use Normal mode to support monaural audio */
485*43d24e76SNicolin Chen 	regmap_update_bits(esai_priv->regmap, REG_ESAI_xCR(tx),
486*43d24e76SNicolin Chen 			   ESAI_xCR_xMOD_MASK, params_channels(params) > 1 ?
487*43d24e76SNicolin Chen 			   ESAI_xCR_xMOD_NETWORK : 0);
488*43d24e76SNicolin Chen 
489*43d24e76SNicolin Chen 	regmap_update_bits(esai_priv->regmap, REG_ESAI_xFCR(tx),
490*43d24e76SNicolin Chen 			   ESAI_xFCR_xFR_MASK, ESAI_xFCR_xFR);
491*43d24e76SNicolin Chen 
492*43d24e76SNicolin Chen 	mask = ESAI_xFCR_xFR_MASK | ESAI_xFCR_xWA_MASK | ESAI_xFCR_xFWM_MASK |
493*43d24e76SNicolin Chen 	      (tx ? ESAI_xFCR_TE_MASK | ESAI_xFCR_TIEN : ESAI_xFCR_RE_MASK);
494*43d24e76SNicolin Chen 	val = ESAI_xFCR_xWA(width) | ESAI_xFCR_xFWM(esai_priv->fifo_depth) |
495*43d24e76SNicolin Chen 	     (tx ? ESAI_xFCR_TE(channels) | ESAI_xFCR_TIEN : ESAI_xFCR_RE(channels));
496*43d24e76SNicolin Chen 
497*43d24e76SNicolin Chen 	regmap_update_bits(esai_priv->regmap, REG_ESAI_xFCR(tx), mask, val);
498*43d24e76SNicolin Chen 
499*43d24e76SNicolin Chen 	mask = ESAI_xCR_xSWS_MASK | (tx ? ESAI_xCR_PADC : 0);
500*43d24e76SNicolin Chen 	val = ESAI_xCR_xSWS(esai_priv->slot_width, width) | (tx ? ESAI_xCR_PADC : 0);
501*43d24e76SNicolin Chen 
502*43d24e76SNicolin Chen 	regmap_update_bits(esai_priv->regmap, REG_ESAI_xCR(tx), mask, val);
503*43d24e76SNicolin Chen 
504*43d24e76SNicolin Chen 	return 0;
505*43d24e76SNicolin Chen }
506*43d24e76SNicolin Chen 
507*43d24e76SNicolin Chen static void fsl_esai_shutdown(struct snd_pcm_substream *substream,
508*43d24e76SNicolin Chen 			      struct snd_soc_dai *dai)
509*43d24e76SNicolin Chen {
510*43d24e76SNicolin Chen 	struct fsl_esai *esai_priv = snd_soc_dai_get_drvdata(dai);
511*43d24e76SNicolin Chen 
512*43d24e76SNicolin Chen 	if (!IS_ERR(esai_priv->fsysclk))
513*43d24e76SNicolin Chen 		clk_disable_unprepare(esai_priv->fsysclk);
514*43d24e76SNicolin Chen 	if (!IS_ERR(esai_priv->extalclk))
515*43d24e76SNicolin Chen 		clk_disable_unprepare(esai_priv->extalclk);
516*43d24e76SNicolin Chen 	clk_disable_unprepare(esai_priv->coreclk);
517*43d24e76SNicolin Chen }
518*43d24e76SNicolin Chen 
519*43d24e76SNicolin Chen static int fsl_esai_trigger(struct snd_pcm_substream *substream, int cmd,
520*43d24e76SNicolin Chen 			    struct snd_soc_dai *dai)
521*43d24e76SNicolin Chen {
522*43d24e76SNicolin Chen 	struct fsl_esai *esai_priv = snd_soc_dai_get_drvdata(dai);
523*43d24e76SNicolin Chen 	bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
524*43d24e76SNicolin Chen 	u8 i, channels = substream->runtime->channels;
525*43d24e76SNicolin Chen 
526*43d24e76SNicolin Chen 	switch (cmd) {
527*43d24e76SNicolin Chen 	case SNDRV_PCM_TRIGGER_START:
528*43d24e76SNicolin Chen 	case SNDRV_PCM_TRIGGER_RESUME:
529*43d24e76SNicolin Chen 	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
530*43d24e76SNicolin Chen 		regmap_update_bits(esai_priv->regmap, REG_ESAI_xFCR(tx),
531*43d24e76SNicolin Chen 				   ESAI_xFCR_xFEN_MASK, ESAI_xFCR_xFEN);
532*43d24e76SNicolin Chen 
533*43d24e76SNicolin Chen 		/* Write initial words reqiured by ESAI as normal procedure */
534*43d24e76SNicolin Chen 		for (i = 0; tx && i < channels; i++)
535*43d24e76SNicolin Chen 			regmap_write(esai_priv->regmap, REG_ESAI_ETDR, 0x0);
536*43d24e76SNicolin Chen 
537*43d24e76SNicolin Chen 		regmap_update_bits(esai_priv->regmap, REG_ESAI_xCR(tx),
538*43d24e76SNicolin Chen 				   tx ? ESAI_xCR_TE_MASK : ESAI_xCR_RE_MASK,
539*43d24e76SNicolin Chen 				   tx ? ESAI_xCR_TE(channels) : ESAI_xCR_RE(channels));
540*43d24e76SNicolin Chen 		break;
541*43d24e76SNicolin Chen 	case SNDRV_PCM_TRIGGER_SUSPEND:
542*43d24e76SNicolin Chen 	case SNDRV_PCM_TRIGGER_STOP:
543*43d24e76SNicolin Chen 	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
544*43d24e76SNicolin Chen 		regmap_update_bits(esai_priv->regmap, REG_ESAI_xCR(tx),
545*43d24e76SNicolin Chen 				   tx ? ESAI_xCR_TE_MASK : ESAI_xCR_RE_MASK, 0);
546*43d24e76SNicolin Chen 
547*43d24e76SNicolin Chen 		/* Disable and reset FIFO */
548*43d24e76SNicolin Chen 		regmap_update_bits(esai_priv->regmap, REG_ESAI_xFCR(tx),
549*43d24e76SNicolin Chen 				   ESAI_xFCR_xFR | ESAI_xFCR_xFEN, ESAI_xFCR_xFR);
550*43d24e76SNicolin Chen 		regmap_update_bits(esai_priv->regmap, REG_ESAI_xFCR(tx),
551*43d24e76SNicolin Chen 				   ESAI_xFCR_xFR, 0);
552*43d24e76SNicolin Chen 		break;
553*43d24e76SNicolin Chen 	default:
554*43d24e76SNicolin Chen 		return -EINVAL;
555*43d24e76SNicolin Chen 	}
556*43d24e76SNicolin Chen 
557*43d24e76SNicolin Chen 	return 0;
558*43d24e76SNicolin Chen }
559*43d24e76SNicolin Chen 
560*43d24e76SNicolin Chen static struct snd_soc_dai_ops fsl_esai_dai_ops = {
561*43d24e76SNicolin Chen 	.startup = fsl_esai_startup,
562*43d24e76SNicolin Chen 	.shutdown = fsl_esai_shutdown,
563*43d24e76SNicolin Chen 	.trigger = fsl_esai_trigger,
564*43d24e76SNicolin Chen 	.hw_params = fsl_esai_hw_params,
565*43d24e76SNicolin Chen 	.set_sysclk = fsl_esai_set_dai_sysclk,
566*43d24e76SNicolin Chen 	.set_fmt = fsl_esai_set_dai_fmt,
567*43d24e76SNicolin Chen 	.set_tdm_slot = fsl_esai_set_dai_tdm_slot,
568*43d24e76SNicolin Chen };
569*43d24e76SNicolin Chen 
570*43d24e76SNicolin Chen static int fsl_esai_dai_probe(struct snd_soc_dai *dai)
571*43d24e76SNicolin Chen {
572*43d24e76SNicolin Chen 	struct fsl_esai *esai_priv = snd_soc_dai_get_drvdata(dai);
573*43d24e76SNicolin Chen 
574*43d24e76SNicolin Chen 	snd_soc_dai_init_dma_data(dai, &esai_priv->dma_params_tx,
575*43d24e76SNicolin Chen 				  &esai_priv->dma_params_rx);
576*43d24e76SNicolin Chen 
577*43d24e76SNicolin Chen 	return 0;
578*43d24e76SNicolin Chen }
579*43d24e76SNicolin Chen 
580*43d24e76SNicolin Chen static struct snd_soc_dai_driver fsl_esai_dai = {
581*43d24e76SNicolin Chen 	.probe = fsl_esai_dai_probe,
582*43d24e76SNicolin Chen 	.playback = {
583*43d24e76SNicolin Chen 		.channels_min = 1,
584*43d24e76SNicolin Chen 		.channels_max = 12,
585*43d24e76SNicolin Chen 		.rates = FSL_ESAI_RATES,
586*43d24e76SNicolin Chen 		.formats = FSL_ESAI_FORMATS,
587*43d24e76SNicolin Chen 	},
588*43d24e76SNicolin Chen 	.capture = {
589*43d24e76SNicolin Chen 		.channels_min = 1,
590*43d24e76SNicolin Chen 		.channels_max = 8,
591*43d24e76SNicolin Chen 		.rates = FSL_ESAI_RATES,
592*43d24e76SNicolin Chen 		.formats = FSL_ESAI_FORMATS,
593*43d24e76SNicolin Chen 	},
594*43d24e76SNicolin Chen 	.ops = &fsl_esai_dai_ops,
595*43d24e76SNicolin Chen };
596*43d24e76SNicolin Chen 
597*43d24e76SNicolin Chen static const struct snd_soc_component_driver fsl_esai_component = {
598*43d24e76SNicolin Chen 	.name		= "fsl-esai",
599*43d24e76SNicolin Chen };
600*43d24e76SNicolin Chen 
601*43d24e76SNicolin Chen static bool fsl_esai_readable_reg(struct device *dev, unsigned int reg)
602*43d24e76SNicolin Chen {
603*43d24e76SNicolin Chen 	switch (reg) {
604*43d24e76SNicolin Chen 	case REG_ESAI_ERDR:
605*43d24e76SNicolin Chen 	case REG_ESAI_ECR:
606*43d24e76SNicolin Chen 	case REG_ESAI_ESR:
607*43d24e76SNicolin Chen 	case REG_ESAI_TFCR:
608*43d24e76SNicolin Chen 	case REG_ESAI_TFSR:
609*43d24e76SNicolin Chen 	case REG_ESAI_RFCR:
610*43d24e76SNicolin Chen 	case REG_ESAI_RFSR:
611*43d24e76SNicolin Chen 	case REG_ESAI_RX0:
612*43d24e76SNicolin Chen 	case REG_ESAI_RX1:
613*43d24e76SNicolin Chen 	case REG_ESAI_RX2:
614*43d24e76SNicolin Chen 	case REG_ESAI_RX3:
615*43d24e76SNicolin Chen 	case REG_ESAI_SAISR:
616*43d24e76SNicolin Chen 	case REG_ESAI_SAICR:
617*43d24e76SNicolin Chen 	case REG_ESAI_TCR:
618*43d24e76SNicolin Chen 	case REG_ESAI_TCCR:
619*43d24e76SNicolin Chen 	case REG_ESAI_RCR:
620*43d24e76SNicolin Chen 	case REG_ESAI_RCCR:
621*43d24e76SNicolin Chen 	case REG_ESAI_TSMA:
622*43d24e76SNicolin Chen 	case REG_ESAI_TSMB:
623*43d24e76SNicolin Chen 	case REG_ESAI_RSMA:
624*43d24e76SNicolin Chen 	case REG_ESAI_RSMB:
625*43d24e76SNicolin Chen 	case REG_ESAI_PRRC:
626*43d24e76SNicolin Chen 	case REG_ESAI_PCRC:
627*43d24e76SNicolin Chen 		return true;
628*43d24e76SNicolin Chen 	default:
629*43d24e76SNicolin Chen 		return false;
630*43d24e76SNicolin Chen 	}
631*43d24e76SNicolin Chen }
632*43d24e76SNicolin Chen 
633*43d24e76SNicolin Chen static bool fsl_esai_writeable_reg(struct device *dev, unsigned int reg)
634*43d24e76SNicolin Chen {
635*43d24e76SNicolin Chen 	switch (reg) {
636*43d24e76SNicolin Chen 	case REG_ESAI_ETDR:
637*43d24e76SNicolin Chen 	case REG_ESAI_ECR:
638*43d24e76SNicolin Chen 	case REG_ESAI_TFCR:
639*43d24e76SNicolin Chen 	case REG_ESAI_RFCR:
640*43d24e76SNicolin Chen 	case REG_ESAI_TX0:
641*43d24e76SNicolin Chen 	case REG_ESAI_TX1:
642*43d24e76SNicolin Chen 	case REG_ESAI_TX2:
643*43d24e76SNicolin Chen 	case REG_ESAI_TX3:
644*43d24e76SNicolin Chen 	case REG_ESAI_TX4:
645*43d24e76SNicolin Chen 	case REG_ESAI_TX5:
646*43d24e76SNicolin Chen 	case REG_ESAI_TSR:
647*43d24e76SNicolin Chen 	case REG_ESAI_SAICR:
648*43d24e76SNicolin Chen 	case REG_ESAI_TCR:
649*43d24e76SNicolin Chen 	case REG_ESAI_TCCR:
650*43d24e76SNicolin Chen 	case REG_ESAI_RCR:
651*43d24e76SNicolin Chen 	case REG_ESAI_RCCR:
652*43d24e76SNicolin Chen 	case REG_ESAI_TSMA:
653*43d24e76SNicolin Chen 	case REG_ESAI_TSMB:
654*43d24e76SNicolin Chen 	case REG_ESAI_RSMA:
655*43d24e76SNicolin Chen 	case REG_ESAI_RSMB:
656*43d24e76SNicolin Chen 	case REG_ESAI_PRRC:
657*43d24e76SNicolin Chen 	case REG_ESAI_PCRC:
658*43d24e76SNicolin Chen 		return true;
659*43d24e76SNicolin Chen 	default:
660*43d24e76SNicolin Chen 		return false;
661*43d24e76SNicolin Chen 	}
662*43d24e76SNicolin Chen }
663*43d24e76SNicolin Chen 
664*43d24e76SNicolin Chen static const struct regmap_config fsl_esai_regmap_config = {
665*43d24e76SNicolin Chen 	.reg_bits = 32,
666*43d24e76SNicolin Chen 	.reg_stride = 4,
667*43d24e76SNicolin Chen 	.val_bits = 32,
668*43d24e76SNicolin Chen 
669*43d24e76SNicolin Chen 	.max_register = REG_ESAI_PCRC,
670*43d24e76SNicolin Chen 	.readable_reg = fsl_esai_readable_reg,
671*43d24e76SNicolin Chen 	.writeable_reg = fsl_esai_writeable_reg,
672*43d24e76SNicolin Chen };
673*43d24e76SNicolin Chen 
674*43d24e76SNicolin Chen static int fsl_esai_probe(struct platform_device *pdev)
675*43d24e76SNicolin Chen {
676*43d24e76SNicolin Chen 	struct device_node *np = pdev->dev.of_node;
677*43d24e76SNicolin Chen 	struct fsl_esai *esai_priv;
678*43d24e76SNicolin Chen 	struct resource *res;
679*43d24e76SNicolin Chen 	const uint32_t *iprop;
680*43d24e76SNicolin Chen 	void __iomem *regs;
681*43d24e76SNicolin Chen 	int irq, ret;
682*43d24e76SNicolin Chen 
683*43d24e76SNicolin Chen 	esai_priv = devm_kzalloc(&pdev->dev, sizeof(*esai_priv), GFP_KERNEL);
684*43d24e76SNicolin Chen 	if (!esai_priv)
685*43d24e76SNicolin Chen 		return -ENOMEM;
686*43d24e76SNicolin Chen 
687*43d24e76SNicolin Chen 	esai_priv->pdev = pdev;
688*43d24e76SNicolin Chen 	strcpy(esai_priv->name, np->name);
689*43d24e76SNicolin Chen 
690*43d24e76SNicolin Chen 	/* Get the addresses and IRQ */
691*43d24e76SNicolin Chen 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
692*43d24e76SNicolin Chen 	regs = devm_ioremap_resource(&pdev->dev, res);
693*43d24e76SNicolin Chen 	if (IS_ERR(regs))
694*43d24e76SNicolin Chen 		return PTR_ERR(regs);
695*43d24e76SNicolin Chen 
696*43d24e76SNicolin Chen 	esai_priv->regmap = devm_regmap_init_mmio_clk(&pdev->dev,
697*43d24e76SNicolin Chen 			"core", regs, &fsl_esai_regmap_config);
698*43d24e76SNicolin Chen 	if (IS_ERR(esai_priv->regmap)) {
699*43d24e76SNicolin Chen 		dev_err(&pdev->dev, "failed to init regmap: %ld\n",
700*43d24e76SNicolin Chen 				PTR_ERR(esai_priv->regmap));
701*43d24e76SNicolin Chen 		return PTR_ERR(esai_priv->regmap);
702*43d24e76SNicolin Chen 	}
703*43d24e76SNicolin Chen 
704*43d24e76SNicolin Chen 	esai_priv->coreclk = devm_clk_get(&pdev->dev, "core");
705*43d24e76SNicolin Chen 	if (IS_ERR(esai_priv->coreclk)) {
706*43d24e76SNicolin Chen 		dev_err(&pdev->dev, "failed to get core clock: %ld\n",
707*43d24e76SNicolin Chen 				PTR_ERR(esai_priv->coreclk));
708*43d24e76SNicolin Chen 		return PTR_ERR(esai_priv->coreclk);
709*43d24e76SNicolin Chen 	}
710*43d24e76SNicolin Chen 
711*43d24e76SNicolin Chen 	esai_priv->extalclk = devm_clk_get(&pdev->dev, "extal");
712*43d24e76SNicolin Chen 	if (IS_ERR(esai_priv->extalclk))
713*43d24e76SNicolin Chen 		dev_warn(&pdev->dev, "failed to get extal clock: %ld\n",
714*43d24e76SNicolin Chen 				PTR_ERR(esai_priv->extalclk));
715*43d24e76SNicolin Chen 
716*43d24e76SNicolin Chen 	esai_priv->fsysclk = devm_clk_get(&pdev->dev, "fsys");
717*43d24e76SNicolin Chen 	if (IS_ERR(esai_priv->fsysclk))
718*43d24e76SNicolin Chen 		dev_warn(&pdev->dev, "failed to get fsys clock: %ld\n",
719*43d24e76SNicolin Chen 				PTR_ERR(esai_priv->fsysclk));
720*43d24e76SNicolin Chen 
721*43d24e76SNicolin Chen 	irq = platform_get_irq(pdev, 0);
722*43d24e76SNicolin Chen 	if (irq < 0) {
723*43d24e76SNicolin Chen 		dev_err(&pdev->dev, "no irq for node %s\n", np->full_name);
724*43d24e76SNicolin Chen 		return irq;
725*43d24e76SNicolin Chen 	}
726*43d24e76SNicolin Chen 
727*43d24e76SNicolin Chen 	ret = devm_request_irq(&pdev->dev, irq, esai_isr, 0,
728*43d24e76SNicolin Chen 			       esai_priv->name, esai_priv);
729*43d24e76SNicolin Chen 	if (ret) {
730*43d24e76SNicolin Chen 		dev_err(&pdev->dev, "failed to claim irq %u\n", irq);
731*43d24e76SNicolin Chen 		return ret;
732*43d24e76SNicolin Chen 	}
733*43d24e76SNicolin Chen 
734*43d24e76SNicolin Chen 	/* Set a default slot size */
735*43d24e76SNicolin Chen 	esai_priv->slot_width = 32;
736*43d24e76SNicolin Chen 
737*43d24e76SNicolin Chen 	/* Set a default master/slave state */
738*43d24e76SNicolin Chen 	esai_priv->slave_mode = true;
739*43d24e76SNicolin Chen 
740*43d24e76SNicolin Chen 	/* Determine the FIFO depth */
741*43d24e76SNicolin Chen 	iprop = of_get_property(np, "fsl,fifo-depth", NULL);
742*43d24e76SNicolin Chen 	if (iprop)
743*43d24e76SNicolin Chen 		esai_priv->fifo_depth = be32_to_cpup(iprop);
744*43d24e76SNicolin Chen 	else
745*43d24e76SNicolin Chen 		esai_priv->fifo_depth = 64;
746*43d24e76SNicolin Chen 
747*43d24e76SNicolin Chen 	esai_priv->dma_params_tx.maxburst = 16;
748*43d24e76SNicolin Chen 	esai_priv->dma_params_rx.maxburst = 16;
749*43d24e76SNicolin Chen 	esai_priv->dma_params_tx.addr = res->start + REG_ESAI_ETDR;
750*43d24e76SNicolin Chen 	esai_priv->dma_params_rx.addr = res->start + REG_ESAI_ERDR;
751*43d24e76SNicolin Chen 
752*43d24e76SNicolin Chen 	esai_priv->synchronous =
753*43d24e76SNicolin Chen 		of_property_read_bool(np, "fsl,esai-synchronous");
754*43d24e76SNicolin Chen 
755*43d24e76SNicolin Chen 	/* Implement full symmetry for synchronous mode */
756*43d24e76SNicolin Chen 	if (esai_priv->synchronous) {
757*43d24e76SNicolin Chen 		fsl_esai_dai.symmetric_rates = 1;
758*43d24e76SNicolin Chen 		fsl_esai_dai.symmetric_channels = 1;
759*43d24e76SNicolin Chen 		fsl_esai_dai.symmetric_samplebits = 1;
760*43d24e76SNicolin Chen 	}
761*43d24e76SNicolin Chen 
762*43d24e76SNicolin Chen 	dev_set_drvdata(&pdev->dev, esai_priv);
763*43d24e76SNicolin Chen 
764*43d24e76SNicolin Chen 	/* Reset ESAI unit */
765*43d24e76SNicolin Chen 	ret = regmap_write(esai_priv->regmap, REG_ESAI_ECR, ESAI_ECR_ERST);
766*43d24e76SNicolin Chen 	if (ret) {
767*43d24e76SNicolin Chen 		dev_err(&pdev->dev, "failed to reset ESAI: %d\n", ret);
768*43d24e76SNicolin Chen 		return ret;
769*43d24e76SNicolin Chen 	}
770*43d24e76SNicolin Chen 
771*43d24e76SNicolin Chen 	/*
772*43d24e76SNicolin Chen 	 * We need to enable ESAI so as to access some of its registers.
773*43d24e76SNicolin Chen 	 * Otherwise, we would fail to dump regmap from user space.
774*43d24e76SNicolin Chen 	 */
775*43d24e76SNicolin Chen 	ret = regmap_write(esai_priv->regmap, REG_ESAI_ECR, ESAI_ECR_ESAIEN);
776*43d24e76SNicolin Chen 	if (ret) {
777*43d24e76SNicolin Chen 		dev_err(&pdev->dev, "failed to enable ESAI: %d\n", ret);
778*43d24e76SNicolin Chen 		return ret;
779*43d24e76SNicolin Chen 	}
780*43d24e76SNicolin Chen 
781*43d24e76SNicolin Chen 	ret = devm_snd_soc_register_component(&pdev->dev, &fsl_esai_component,
782*43d24e76SNicolin Chen 					      &fsl_esai_dai, 1);
783*43d24e76SNicolin Chen 	if (ret) {
784*43d24e76SNicolin Chen 		dev_err(&pdev->dev, "failed to register DAI: %d\n", ret);
785*43d24e76SNicolin Chen 		return ret;
786*43d24e76SNicolin Chen 	}
787*43d24e76SNicolin Chen 
788*43d24e76SNicolin Chen 	ret = imx_pcm_dma_init(pdev);
789*43d24e76SNicolin Chen 	if (ret)
790*43d24e76SNicolin Chen 		dev_err(&pdev->dev, "failed to init imx pcm dma: %d\n", ret);
791*43d24e76SNicolin Chen 
792*43d24e76SNicolin Chen 	return ret;
793*43d24e76SNicolin Chen }
794*43d24e76SNicolin Chen 
795*43d24e76SNicolin Chen static const struct of_device_id fsl_esai_dt_ids[] = {
796*43d24e76SNicolin Chen 	{ .compatible = "fsl,imx35-esai", },
797*43d24e76SNicolin Chen 	{}
798*43d24e76SNicolin Chen };
799*43d24e76SNicolin Chen MODULE_DEVICE_TABLE(of, fsl_esai_dt_ids);
800*43d24e76SNicolin Chen 
801*43d24e76SNicolin Chen static struct platform_driver fsl_esai_driver = {
802*43d24e76SNicolin Chen 	.probe = fsl_esai_probe,
803*43d24e76SNicolin Chen 	.driver = {
804*43d24e76SNicolin Chen 		.name = "fsl-esai-dai",
805*43d24e76SNicolin Chen 		.owner = THIS_MODULE,
806*43d24e76SNicolin Chen 		.of_match_table = fsl_esai_dt_ids,
807*43d24e76SNicolin Chen 	},
808*43d24e76SNicolin Chen };
809*43d24e76SNicolin Chen 
810*43d24e76SNicolin Chen module_platform_driver(fsl_esai_driver);
811*43d24e76SNicolin Chen 
812*43d24e76SNicolin Chen MODULE_AUTHOR("Freescale Semiconductor, Inc.");
813*43d24e76SNicolin Chen MODULE_DESCRIPTION("Freescale ESAI CPU DAI driver");
814*43d24e76SNicolin Chen MODULE_LICENSE("GPL v2");
815*43d24e76SNicolin Chen MODULE_ALIAS("platform:fsl-esai-dai");
816