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