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