Merge tag 'gpio-v5.2-3' of git://git.kernel.org/pub/scm/linux/kernel/git/linusw/linux...
[linux-2.6-block.git] / sound / soc / nuc900 / nuc900-pcm.c
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Copyright (c) 2010 Nuvoton technology corporation.
4  *
5  * Wan ZongShun <mcuos.com@gmail.com>
6  */
7
8 #include <linux/module.h>
9 #include <linux/init.h>
10 #include <linux/io.h>
11 #include <linux/platform_device.h>
12 #include <linux/slab.h>
13 #include <linux/dma-mapping.h>
14
15 #include <sound/core.h>
16 #include <sound/pcm.h>
17 #include <sound/pcm_params.h>
18 #include <sound/soc.h>
19
20 #include <mach/hardware.h>
21
22 #include "nuc900-audio.h"
23
24 static const struct snd_pcm_hardware nuc900_pcm_hardware = {
25         .info                   = SNDRV_PCM_INFO_INTERLEAVED |
26                                         SNDRV_PCM_INFO_BLOCK_TRANSFER |
27                                         SNDRV_PCM_INFO_MMAP |
28                                         SNDRV_PCM_INFO_MMAP_VALID |
29                                         SNDRV_PCM_INFO_PAUSE |
30                                         SNDRV_PCM_INFO_RESUME,
31         .buffer_bytes_max       = 4*1024,
32         .period_bytes_min       = 1*1024,
33         .period_bytes_max       = 4*1024,
34         .periods_min            = 1,
35         .periods_max            = 1024,
36 };
37
38 static int nuc900_dma_hw_params(struct snd_pcm_substream *substream,
39         struct snd_pcm_hw_params *params)
40 {
41         return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params));
42 }
43
44 static void nuc900_update_dma_register(struct snd_pcm_substream *substream)
45 {
46         struct snd_pcm_runtime *runtime = substream->runtime;
47         struct nuc900_audio *nuc900_audio = runtime->private_data;
48         void __iomem *mmio_addr, *mmio_len;
49
50         if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
51                 mmio_addr = nuc900_audio->mmio + ACTL_PDSTB;
52                 mmio_len = nuc900_audio->mmio + ACTL_PDST_LENGTH;
53         } else {
54                 mmio_addr = nuc900_audio->mmio + ACTL_RDSTB;
55                 mmio_len = nuc900_audio->mmio + ACTL_RDST_LENGTH;
56         }
57
58         AUDIO_WRITE(mmio_addr, runtime->dma_addr);
59         AUDIO_WRITE(mmio_len, runtime->dma_bytes);
60 }
61
62 static void nuc900_dma_start(struct snd_pcm_substream *substream)
63 {
64         struct snd_pcm_runtime *runtime = substream->runtime;
65         struct nuc900_audio *nuc900_audio = runtime->private_data;
66         unsigned long val;
67
68         val = AUDIO_READ(nuc900_audio->mmio + ACTL_CON);
69         val |= (T_DMA_IRQ | R_DMA_IRQ);
70         AUDIO_WRITE(nuc900_audio->mmio + ACTL_CON, val);
71 }
72
73 static void nuc900_dma_stop(struct snd_pcm_substream *substream)
74 {
75         struct snd_pcm_runtime *runtime = substream->runtime;
76         struct nuc900_audio *nuc900_audio = runtime->private_data;
77         unsigned long val;
78
79         val = AUDIO_READ(nuc900_audio->mmio + ACTL_CON);
80         val &= ~(T_DMA_IRQ | R_DMA_IRQ);
81         AUDIO_WRITE(nuc900_audio->mmio + ACTL_CON, val);
82 }
83
84 static irqreturn_t nuc900_dma_interrupt(int irq, void *dev_id)
85 {
86         struct snd_pcm_substream *substream = dev_id;
87         struct nuc900_audio *nuc900_audio = substream->runtime->private_data;
88         unsigned long val;
89
90         spin_lock(&nuc900_audio->lock);
91
92         val = AUDIO_READ(nuc900_audio->mmio + ACTL_CON);
93
94         if (val & R_DMA_IRQ) {
95                 AUDIO_WRITE(nuc900_audio->mmio + ACTL_CON, val | R_DMA_IRQ);
96
97                 val = AUDIO_READ(nuc900_audio->mmio + ACTL_RSR);
98
99                 if (val & R_DMA_MIDDLE_IRQ) {
100                         val |= R_DMA_MIDDLE_IRQ;
101                         AUDIO_WRITE(nuc900_audio->mmio + ACTL_RSR, val);
102                 }
103
104                 if (val & R_DMA_END_IRQ) {
105                         val |= R_DMA_END_IRQ;
106                         AUDIO_WRITE(nuc900_audio->mmio + ACTL_RSR, val);
107                 }
108         } else if (val & T_DMA_IRQ) {
109                 AUDIO_WRITE(nuc900_audio->mmio + ACTL_CON, val | T_DMA_IRQ);
110
111                 val = AUDIO_READ(nuc900_audio->mmio + ACTL_PSR);
112
113                 if (val & P_DMA_MIDDLE_IRQ) {
114                         val |= P_DMA_MIDDLE_IRQ;
115                         AUDIO_WRITE(nuc900_audio->mmio + ACTL_PSR, val);
116                 }
117
118                 if (val & P_DMA_END_IRQ) {
119                         val |= P_DMA_END_IRQ;
120                         AUDIO_WRITE(nuc900_audio->mmio + ACTL_PSR, val);
121                 }
122         } else {
123                 dev_err(nuc900_audio->dev, "Wrong DMA interrupt status!\n");
124                 spin_unlock(&nuc900_audio->lock);
125                 return IRQ_HANDLED;
126         }
127
128         spin_unlock(&nuc900_audio->lock);
129
130         snd_pcm_period_elapsed(substream);
131
132         return IRQ_HANDLED;
133 }
134
135 static int nuc900_dma_hw_free(struct snd_pcm_substream *substream)
136 {
137         snd_pcm_lib_free_pages(substream);
138         return 0;
139 }
140
141 static int nuc900_dma_prepare(struct snd_pcm_substream *substream)
142 {
143         struct snd_pcm_runtime *runtime = substream->runtime;
144         struct nuc900_audio *nuc900_audio = runtime->private_data;
145         unsigned long flags, val;
146         int ret = 0;
147
148         spin_lock_irqsave(&nuc900_audio->lock, flags);
149
150         nuc900_update_dma_register(substream);
151
152         val = AUDIO_READ(nuc900_audio->mmio + ACTL_RESET);
153
154         switch (runtime->channels) {
155         case 1:
156                 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
157                         val &= ~(PLAY_LEFT_CHNNEL | PLAY_RIGHT_CHNNEL);
158                         val |= PLAY_RIGHT_CHNNEL;
159                 } else {
160                         val &= ~(RECORD_LEFT_CHNNEL | RECORD_RIGHT_CHNNEL);
161                         val |= RECORD_RIGHT_CHNNEL;
162                 }
163                 AUDIO_WRITE(nuc900_audio->mmio + ACTL_RESET, val);
164                 break;
165         case 2:
166                 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
167                         val |= (PLAY_LEFT_CHNNEL | PLAY_RIGHT_CHNNEL);
168                 else
169                         val |= (RECORD_LEFT_CHNNEL | RECORD_RIGHT_CHNNEL);
170                 AUDIO_WRITE(nuc900_audio->mmio + ACTL_RESET, val);
171                 break;
172         default:
173                 ret = -EINVAL;
174         }
175         spin_unlock_irqrestore(&nuc900_audio->lock, flags);
176         return ret;
177 }
178
179 static int nuc900_dma_trigger(struct snd_pcm_substream *substream, int cmd)
180 {
181         int ret = 0;
182
183         switch (cmd) {
184         case SNDRV_PCM_TRIGGER_START:
185         case SNDRV_PCM_TRIGGER_RESUME:
186                 nuc900_dma_start(substream);
187                 break;
188
189         case SNDRV_PCM_TRIGGER_STOP:
190         case SNDRV_PCM_TRIGGER_SUSPEND:
191                 nuc900_dma_stop(substream);
192                 break;
193
194         default:
195                 ret = -EINVAL;
196                 break;
197         }
198
199         return ret;
200 }
201
202 static int nuc900_dma_getposition(struct snd_pcm_substream *substream,
203                                         dma_addr_t *src, dma_addr_t *dst)
204 {
205         struct snd_pcm_runtime *runtime = substream->runtime;
206         struct nuc900_audio *nuc900_audio = runtime->private_data;
207
208         if (src != NULL)
209                 *src = AUDIO_READ(nuc900_audio->mmio + ACTL_PDSTC);
210
211         if (dst != NULL)
212                 *dst = AUDIO_READ(nuc900_audio->mmio + ACTL_RDSTC);
213
214         return 0;
215 }
216
217 static snd_pcm_uframes_t nuc900_dma_pointer(struct snd_pcm_substream *substream)
218 {
219         struct snd_pcm_runtime *runtime = substream->runtime;
220         dma_addr_t src, dst;
221         unsigned long res;
222
223         nuc900_dma_getposition(substream, &src, &dst);
224
225         if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
226                 res = dst - runtime->dma_addr;
227         else
228                 res = src - runtime->dma_addr;
229
230         return bytes_to_frames(substream->runtime, res);
231 }
232
233 static int nuc900_dma_open(struct snd_pcm_substream *substream)
234 {
235         struct snd_pcm_runtime *runtime = substream->runtime;
236         struct nuc900_audio *nuc900_audio;
237
238         snd_soc_set_runtime_hwparams(substream, &nuc900_pcm_hardware);
239
240         nuc900_audio = nuc900_ac97_data;
241
242         if (request_irq(nuc900_audio->irq_num, nuc900_dma_interrupt,
243                         0, "nuc900-dma", substream))
244                 return -EBUSY;
245
246         runtime->private_data = nuc900_audio;
247
248         return 0;
249 }
250
251 static int nuc900_dma_close(struct snd_pcm_substream *substream)
252 {
253         struct snd_pcm_runtime *runtime = substream->runtime;
254         struct nuc900_audio *nuc900_audio = runtime->private_data;
255
256         free_irq(nuc900_audio->irq_num, substream);
257
258         return 0;
259 }
260
261 static int nuc900_dma_mmap(struct snd_pcm_substream *substream,
262         struct vm_area_struct *vma)
263 {
264         struct snd_pcm_runtime *runtime = substream->runtime;
265
266         return dma_mmap_wc(substream->pcm->card->dev, vma, runtime->dma_area,
267                            runtime->dma_addr, runtime->dma_bytes);
268 }
269
270 static const struct snd_pcm_ops nuc900_dma_ops = {
271         .open           = nuc900_dma_open,
272         .close          = nuc900_dma_close,
273         .ioctl          = snd_pcm_lib_ioctl,
274         .hw_params      = nuc900_dma_hw_params,
275         .hw_free        = nuc900_dma_hw_free,
276         .prepare        = nuc900_dma_prepare,
277         .trigger        = nuc900_dma_trigger,
278         .pointer        = nuc900_dma_pointer,
279         .mmap           = nuc900_dma_mmap,
280 };
281
282 static int nuc900_dma_new(struct snd_soc_pcm_runtime *rtd)
283 {
284         struct snd_card *card = rtd->card->snd_card;
285         struct snd_pcm *pcm = rtd->pcm;
286         int ret;
287
288         ret = dma_coerce_mask_and_coherent(card->dev, DMA_BIT_MASK(32));
289         if (ret)
290                 return ret;
291
292         snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
293                 card->dev, 4 * 1024, (4 * 1024) - 1);
294
295         return 0;
296 }
297
298 static const struct snd_soc_component_driver nuc900_soc_component = {
299         .ops            = &nuc900_dma_ops,
300         .pcm_new        = nuc900_dma_new,
301 };
302
303 static int nuc900_soc_platform_probe(struct platform_device *pdev)
304 {
305         return devm_snd_soc_register_component(&pdev->dev, &nuc900_soc_component,
306                                                NULL, 0);
307 }
308
309 static struct platform_driver nuc900_pcm_driver = {
310         .driver = {
311                         .name = "nuc900-pcm-audio",
312         },
313
314         .probe = nuc900_soc_platform_probe,
315 };
316
317 module_platform_driver(nuc900_pcm_driver);
318
319 MODULE_AUTHOR("Wan ZongShun, <mcuos.com@gmail.com>");
320 MODULE_DESCRIPTION("nuc900 Audio DMA module");
321 MODULE_LICENSE("GPL");