ASoC: fsl-sai: Use snd_soc_dai_init_dma_data()
[platform/kernel/linux-stable.git] / sound / soc / fsl / fsl_sai.c
1 /*
2  * Freescale ALSA SoC Digital Audio Interface (SAI) driver.
3  *
4  * Copyright 2012-2013 Freescale Semiconductor, Inc.
5  *
6  * This program is free software, you can redistribute it and/or modify it
7  * under the terms of the GNU General Public License as published by the
8  * Free Software Foundation, either version 2 of the License, or(at your
9  * option) any later version.
10  *
11  */
12
13 #include <linux/clk.h>
14 #include <linux/delay.h>
15 #include <linux/dmaengine.h>
16 #include <linux/module.h>
17 #include <linux/of_address.h>
18 #include <linux/slab.h>
19 #include <sound/core.h>
20 #include <sound/dmaengine_pcm.h>
21 #include <sound/pcm_params.h>
22
23 #include "fsl_sai.h"
24
25 static inline u32 sai_readl(struct fsl_sai *sai,
26                 const void __iomem *addr)
27 {
28         u32 val;
29
30         val = __raw_readl(addr);
31
32         if (likely(sai->big_endian_regs))
33                 val = be32_to_cpu(val);
34         else
35                 val = le32_to_cpu(val);
36         rmb();
37
38         return val;
39 }
40
41 static inline void sai_writel(struct fsl_sai *sai,
42                 u32 val, void __iomem *addr)
43 {
44         wmb();
45         if (likely(sai->big_endian_regs))
46                 val = cpu_to_be32(val);
47         else
48                 val = cpu_to_le32(val);
49
50         __raw_writel(val, addr);
51 }
52
53 static int fsl_sai_set_dai_sysclk_tr(struct snd_soc_dai *cpu_dai,
54                 int clk_id, unsigned int freq, int fsl_dir)
55 {
56         u32 val_cr2, reg_cr2;
57         struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai);
58
59         if (fsl_dir == FSL_FMT_TRANSMITTER)
60                 reg_cr2 = FSL_SAI_TCR2;
61         else
62                 reg_cr2 = FSL_SAI_RCR2;
63
64         val_cr2 = sai_readl(sai, sai->base + reg_cr2);
65         switch (clk_id) {
66         case FSL_SAI_CLK_BUS:
67                 val_cr2 &= ~FSL_SAI_CR2_MSEL_MASK;
68                 val_cr2 |= FSL_SAI_CR2_MSEL_BUS;
69                 break;
70         case FSL_SAI_CLK_MAST1:
71                 val_cr2 &= ~FSL_SAI_CR2_MSEL_MASK;
72                 val_cr2 |= FSL_SAI_CR2_MSEL_MCLK1;
73                 break;
74         case FSL_SAI_CLK_MAST2:
75                 val_cr2 &= ~FSL_SAI_CR2_MSEL_MASK;
76                 val_cr2 |= FSL_SAI_CR2_MSEL_MCLK2;
77                 break;
78         case FSL_SAI_CLK_MAST3:
79                 val_cr2 &= ~FSL_SAI_CR2_MSEL_MASK;
80                 val_cr2 |= FSL_SAI_CR2_MSEL_MCLK3;
81                 break;
82         default:
83                 return -EINVAL;
84         }
85         sai_writel(sai, val_cr2, sai->base + reg_cr2);
86
87         return 0;
88 }
89
90 static int fsl_sai_set_dai_sysclk(struct snd_soc_dai *cpu_dai,
91                 int clk_id, unsigned int freq, int dir)
92 {
93         int ret;
94         struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai);
95
96         if (dir == SND_SOC_CLOCK_IN)
97                 return 0;
98
99         ret = clk_prepare_enable(sai->clk);
100         if (ret)
101                 return ret;
102
103         sai_writel(sai, 0x0, sai->base + FSL_SAI_RCSR);
104         sai_writel(sai, 0x0, sai->base + FSL_SAI_TCSR);
105         sai_writel(sai, FSL_SAI_MAXBURST_TX * 2, sai->base + FSL_SAI_TCR1);
106         sai_writel(sai, FSL_SAI_MAXBURST_RX - 1, sai->base + FSL_SAI_RCR1);
107
108         ret = fsl_sai_set_dai_sysclk_tr(cpu_dai, clk_id, freq,
109                                         FSL_FMT_TRANSMITTER);
110         if (ret) {
111                 dev_err(cpu_dai->dev,
112                                 "Cannot set SAI's transmitter sysclk: %d\n",
113                                 ret);
114                 return ret;
115         }
116
117         ret = fsl_sai_set_dai_sysclk_tr(cpu_dai, clk_id, freq,
118                                         FSL_FMT_RECEIVER);
119         if (ret) {
120                 dev_err(cpu_dai->dev,
121                                 "Cannot set SAI's receiver sysclk: %d\n",
122                                 ret);
123                 return ret;
124         }
125
126         clk_disable_unprepare(sai->clk);
127
128         return 0;
129 }
130
131 static int fsl_sai_set_dai_fmt_tr(struct snd_soc_dai *cpu_dai,
132                                 unsigned int fmt, int fsl_dir)
133 {
134         u32 val_cr2, val_cr3, val_cr4, reg_cr2, reg_cr3, reg_cr4;
135         struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai);
136
137         if (fsl_dir == FSL_FMT_TRANSMITTER) {
138                 reg_cr2 = FSL_SAI_TCR2;
139                 reg_cr3 = FSL_SAI_TCR3;
140                 reg_cr4 = FSL_SAI_TCR4;
141         } else {
142                 reg_cr2 = FSL_SAI_RCR2;
143                 reg_cr3 = FSL_SAI_RCR3;
144                 reg_cr4 = FSL_SAI_RCR4;
145         }
146
147         val_cr2 = sai_readl(sai, sai->base + reg_cr2);
148         val_cr3 = sai_readl(sai, sai->base + reg_cr3);
149         val_cr4 = sai_readl(sai, sai->base + reg_cr4);
150
151         if (sai->big_endian_data)
152                 val_cr4 |= FSL_SAI_CR4_MF;
153         else
154                 val_cr4 &= ~FSL_SAI_CR4_MF;
155
156         switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
157         case SND_SOC_DAIFMT_I2S:
158                 val_cr4 |= FSL_SAI_CR4_FSE;
159                 val_cr4 |= FSL_SAI_CR4_FSP;
160                 break;
161         default:
162                 return -EINVAL;
163         }
164
165         switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
166         case SND_SOC_DAIFMT_IB_IF:
167                 val_cr4 |= FSL_SAI_CR4_FSP;
168                 val_cr2 &= ~FSL_SAI_CR2_BCP;
169                 break;
170         case SND_SOC_DAIFMT_IB_NF:
171                 val_cr4 &= ~FSL_SAI_CR4_FSP;
172                 val_cr2 &= ~FSL_SAI_CR2_BCP;
173                 break;
174         case SND_SOC_DAIFMT_NB_IF:
175                 val_cr4 |= FSL_SAI_CR4_FSP;
176                 val_cr2 |= FSL_SAI_CR2_BCP;
177                 break;
178         case SND_SOC_DAIFMT_NB_NF:
179                 val_cr4 &= ~FSL_SAI_CR4_FSP;
180                 val_cr2 |= FSL_SAI_CR2_BCP;
181                 break;
182         default:
183                 return -EINVAL;
184         }
185
186         switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
187         case SND_SOC_DAIFMT_CBS_CFS:
188                 val_cr2 |= FSL_SAI_CR2_BCD_MSTR;
189                 val_cr4 |= FSL_SAI_CR4_FSD_MSTR;
190                 break;
191         case SND_SOC_DAIFMT_CBM_CFM:
192                 val_cr2 &= ~FSL_SAI_CR2_BCD_MSTR;
193                 val_cr4 &= ~FSL_SAI_CR4_FSD_MSTR;
194                 break;
195         default:
196                 return -EINVAL;
197         }
198
199         val_cr3 |= FSL_SAI_CR3_TRCE;
200
201         if (fsl_dir == FSL_FMT_RECEIVER)
202                 val_cr2 |= FSL_SAI_CR2_SYNC;
203
204         sai_writel(sai, val_cr2, sai->base + reg_cr2);
205         sai_writel(sai, val_cr3, sai->base + reg_cr3);
206         sai_writel(sai, val_cr4, sai->base + reg_cr4);
207
208         return 0;
209 }
210
211 static int fsl_sai_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt)
212 {
213         int ret;
214         struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai);
215
216         ret = clk_prepare_enable(sai->clk);
217         if (ret)
218                 return ret;
219
220         ret = fsl_sai_set_dai_fmt_tr(cpu_dai, fmt, FSL_FMT_TRANSMITTER);
221         if (ret) {
222                 dev_err(cpu_dai->dev,
223                                 "Cannot set SAI's transmitter format: %d\n",
224                                 ret);
225                 return ret;
226         }
227
228         ret = fsl_sai_set_dai_fmt_tr(cpu_dai, fmt, FSL_FMT_RECEIVER);
229         if (ret) {
230                 dev_err(cpu_dai->dev,
231                                 "Cannot set SAI's receiver format: %d\n",
232                                 ret);
233                 return ret;
234         }
235
236         clk_disable_unprepare(sai->clk);
237
238         return 0;
239 }
240
241 static int fsl_sai_hw_params(struct snd_pcm_substream *substream,
242                 struct snd_pcm_hw_params *params,
243                 struct snd_soc_dai *cpu_dai)
244 {
245         u32 val_cr4, val_cr5, val_mr, reg_cr4, reg_cr5, reg_mr, word_width;
246         unsigned int channels = params_channels(params);
247         struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai);
248
249         if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
250                 reg_cr4 = FSL_SAI_TCR4;
251                 reg_cr5 = FSL_SAI_TCR5;
252                 reg_mr = FSL_SAI_TMR;
253         } else {
254                 reg_cr4 = FSL_SAI_RCR4;
255                 reg_cr5 = FSL_SAI_RCR5;
256                 reg_mr = FSL_SAI_RMR;
257         }
258
259         val_cr4 = sai_readl(sai, sai->base + reg_cr4);
260         val_cr4 &= ~FSL_SAI_CR4_SYWD_MASK;
261         val_cr4 &= ~FSL_SAI_CR4_FRSZ_MASK;
262
263         val_cr5 = sai_readl(sai, sai->base + reg_cr5);
264         val_cr5 &= ~FSL_SAI_CR5_WNW_MASK;
265         val_cr5 &= ~FSL_SAI_CR5_W0W_MASK;
266         val_cr5 &= ~FSL_SAI_CR5_FBT_MASK;
267
268         switch (params_format(params)) {
269         case SNDRV_PCM_FORMAT_S16_LE:
270                 word_width = 16;
271                 break;
272         case SNDRV_PCM_FORMAT_S20_3LE:
273                 word_width = 20;
274                 break;
275         case SNDRV_PCM_FORMAT_S24_LE:
276                 word_width = 24;
277                 break;
278         default:
279                 return -EINVAL;
280         }
281
282         val_cr4 |= FSL_SAI_CR4_SYWD(word_width);
283         val_cr5 |= FSL_SAI_CR5_WNW(word_width);
284         val_cr5 |= FSL_SAI_CR5_W0W(word_width);
285
286         if (sai->big_endian_data)
287                 val_cr5 |= FSL_SAI_CR5_FBT(word_width - 1);
288         else
289                 val_cr5 |= FSL_SAI_CR5_FBT(0);
290
291         val_cr4 |= FSL_SAI_CR4_FRSZ(channels);
292         if (channels == 2 || channels == 1)
293                 val_mr = ~0UL - ((1 << channels) - 1);
294         else
295                 return -EINVAL;
296
297         sai_writel(sai, val_cr4, sai->base + reg_cr4);
298         sai_writel(sai, val_cr5, sai->base + reg_cr5);
299         sai_writel(sai, val_mr, sai->base + reg_mr);
300
301         return 0;
302 }
303
304 static int fsl_sai_trigger(struct snd_pcm_substream *substream, int cmd,
305                 struct snd_soc_dai *cpu_dai)
306 {
307         struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai);
308         unsigned int tcsr, rcsr;
309
310         tcsr = sai_readl(sai, sai->base + FSL_SAI_TCSR);
311         rcsr = sai_readl(sai, sai->base + FSL_SAI_RCSR);
312
313         if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
314                 tcsr |= FSL_SAI_CSR_FRDE;
315                 rcsr &= ~FSL_SAI_CSR_FRDE;
316         } else {
317                 rcsr |= FSL_SAI_CSR_FRDE;
318                 tcsr &= ~FSL_SAI_CSR_FRDE;
319         }
320
321         switch (cmd) {
322         case SNDRV_PCM_TRIGGER_START:
323         case SNDRV_PCM_TRIGGER_RESUME:
324         case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
325                 tcsr |= FSL_SAI_CSR_TERE;
326                 rcsr |= FSL_SAI_CSR_TERE;
327                 sai_writel(sai, rcsr, sai->base + FSL_SAI_RCSR);
328                 sai_writel(sai, tcsr, sai->base + FSL_SAI_TCSR);
329                 break;
330
331         case SNDRV_PCM_TRIGGER_STOP:
332         case SNDRV_PCM_TRIGGER_SUSPEND:
333         case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
334                 if (!(cpu_dai->playback_active || cpu_dai->capture_active)) {
335                         tcsr &= ~FSL_SAI_CSR_TERE;
336                         rcsr &= ~FSL_SAI_CSR_TERE;
337                 }
338                 sai_writel(sai, tcsr, sai->base + FSL_SAI_TCSR);
339                 sai_writel(sai, rcsr, sai->base + FSL_SAI_RCSR);
340                 break;
341         default:
342                 return -EINVAL;
343         }
344
345         return 0;
346 }
347
348 static int fsl_sai_startup(struct snd_pcm_substream *substream,
349                 struct snd_soc_dai *cpu_dai)
350 {
351         int ret;
352         struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai);
353
354         ret = clk_prepare_enable(sai->clk);
355
356         return ret;
357 }
358
359 static void fsl_sai_shutdown(struct snd_pcm_substream *substream,
360                 struct snd_soc_dai *cpu_dai)
361 {
362         struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai);
363
364         clk_disable_unprepare(sai->clk);
365 }
366
367 static const struct snd_soc_dai_ops fsl_sai_pcm_dai_ops = {
368         .set_sysclk     = fsl_sai_set_dai_sysclk,
369         .set_fmt        = fsl_sai_set_dai_fmt,
370         .hw_params      = fsl_sai_hw_params,
371         .trigger        = fsl_sai_trigger,
372         .startup        = fsl_sai_startup,
373         .shutdown       = fsl_sai_shutdown,
374 };
375
376 static int fsl_sai_dai_probe(struct snd_soc_dai *cpu_dai)
377 {
378         struct fsl_sai *sai = dev_get_drvdata(cpu_dai->dev);
379
380         snd_soc_dai_init_dma_data(cpu_dai, &sai->dma_params_tx,
381                                 &sai->dma_params_rx);
382
383         snd_soc_dai_set_drvdata(cpu_dai, sai);
384
385         return 0;
386 }
387
388 static struct snd_soc_dai_driver fsl_sai_dai = {
389         .probe = fsl_sai_dai_probe,
390         .playback = {
391                 .channels_min = 1,
392                 .channels_max = 2,
393                 .rates = SNDRV_PCM_RATE_8000_96000,
394                 .formats = FSL_SAI_FORMATS,
395         },
396         .capture = {
397                 .channels_min = 1,
398                 .channels_max = 2,
399                 .rates = SNDRV_PCM_RATE_8000_96000,
400                 .formats = FSL_SAI_FORMATS,
401         },
402         .ops = &fsl_sai_pcm_dai_ops,
403 };
404
405 static const struct snd_soc_component_driver fsl_component = {
406         .name           = "fsl-sai",
407 };
408
409 static int fsl_sai_probe(struct platform_device *pdev)
410 {
411         int ret;
412         struct fsl_sai *sai;
413         struct resource *res;
414         struct device_node *np = pdev->dev.of_node;
415
416         sai = devm_kzalloc(&pdev->dev, sizeof(*sai), GFP_KERNEL);
417         if (!sai)
418                 return -ENOMEM;
419
420         res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
421         sai->base = devm_ioremap_resource(&pdev->dev, res);
422         if (IS_ERR(sai->base))
423                 return PTR_ERR(sai->base);
424
425         sai->clk = devm_clk_get(&pdev->dev, "sai");
426         if (IS_ERR(sai->clk)) {
427                 dev_err(&pdev->dev, "Cannot get SAI's clock\n");
428                 return PTR_ERR(sai->clk);
429         }
430
431         sai->dma_params_rx.addr = res->start + FSL_SAI_RDR;
432         sai->dma_params_tx.addr = res->start + FSL_SAI_TDR;
433         sai->dma_params_rx.maxburst = FSL_SAI_MAXBURST_RX;
434         sai->dma_params_tx.maxburst = FSL_SAI_MAXBURST_TX;
435
436         sai->big_endian_regs = of_property_read_bool(np, "big-endian-regs");
437         sai->big_endian_data = of_property_read_bool(np, "big-endian-data");
438
439         platform_set_drvdata(pdev, sai);
440
441         ret = devm_snd_soc_register_component(&pdev->dev, &fsl_component,
442                         &fsl_sai_dai, 1);
443         if (ret)
444                 return ret;
445
446         return devm_snd_dmaengine_pcm_register(&pdev->dev, NULL,
447                         SND_DMAENGINE_PCM_FLAG_NO_RESIDUE);
448 }
449
450 static const struct of_device_id fsl_sai_ids[] = {
451         { .compatible = "fsl,vf610-sai", },
452         { /* sentinel */ }
453 };
454
455 static struct platform_driver fsl_sai_driver = {
456         .probe = fsl_sai_probe,
457         .driver = {
458                 .name = "fsl-sai",
459                 .owner = THIS_MODULE,
460                 .of_match_table = fsl_sai_ids,
461         },
462 };
463 module_platform_driver(fsl_sai_driver);
464
465 MODULE_DESCRIPTION("Freescale Soc SAI Interface");
466 MODULE_AUTHOR("Xiubo Li, <Li.Xiubo@freescale.com>");
467 MODULE_ALIAS("platform:fsl-sai");
468 MODULE_LICENSE("GPL");