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