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