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