1 // SPDX-License-Identifier: GPL-2.0-only
3 * Au1000/Au1500/Au1100 Audio DMA support.
5 * (c) 2011 Manuel Lauss <manuel.lauss@googlemail.com>
7 * copied almost verbatim from the old ALSA driver, written by
8 * Charles Eidsness <charles@cooper-street.com>
11 #include <linux/module.h>
12 #include <linux/init.h>
13 #include <linux/platform_device.h>
14 #include <linux/slab.h>
15 #include <linux/dma-mapping.h>
16 #include <sound/core.h>
17 #include <sound/pcm.h>
18 #include <sound/pcm_params.h>
19 #include <sound/soc.h>
20 #include <asm/mach-au1x00/au1000.h>
21 #include <asm/mach-au1x00/au1000_dma.h>
25 #define DRV_NAME "au1x_dma"
29 u32 relative_end
; /* relative to start of buffer */
30 struct pcm_period
*next
;
34 struct snd_pcm_substream
*substream
;
36 struct pcm_period
*buffer
;
37 unsigned int period_size
;
41 struct alchemy_pcm_ctx
{
42 struct audio_stream stream
[2]; /* playback & capture */
45 static void au1000_release_dma_link(struct audio_stream
*stream
)
47 struct pcm_period
*pointer
;
48 struct pcm_period
*pointer_next
;
50 stream
->period_size
= 0;
52 pointer
= stream
->buffer
;
56 pointer_next
= pointer
->next
;
58 pointer
= pointer_next
;
59 } while (pointer
!= stream
->buffer
);
60 stream
->buffer
= NULL
;
63 static int au1000_setup_dma_link(struct audio_stream
*stream
,
64 unsigned int period_bytes
,
67 struct snd_pcm_substream
*substream
= stream
->substream
;
68 struct snd_pcm_runtime
*runtime
= substream
->runtime
;
69 struct pcm_period
*pointer
;
70 unsigned long dma_start
;
73 dma_start
= virt_to_phys(runtime
->dma_area
);
75 if (stream
->period_size
== period_bytes
&&
76 stream
->periods
== periods
)
77 return 0; /* not changed */
79 au1000_release_dma_link(stream
);
81 stream
->period_size
= period_bytes
;
82 stream
->periods
= periods
;
84 stream
->buffer
= kmalloc(sizeof(struct pcm_period
), GFP_KERNEL
);
87 pointer
= stream
->buffer
;
88 for (i
= 0; i
< periods
; i
++) {
89 pointer
->start
= (u32
)(dma_start
+ (i
* period_bytes
));
90 pointer
->relative_end
= (u32
) (((i
+1) * period_bytes
) - 0x1);
91 if (i
< periods
- 1) {
92 pointer
->next
= kmalloc(sizeof(struct pcm_period
),
95 au1000_release_dma_link(stream
);
98 pointer
= pointer
->next
;
101 pointer
->next
= stream
->buffer
;
105 static void au1000_dma_stop(struct audio_stream
*stream
)
108 disable_dma(stream
->dma
);
111 static void au1000_dma_start(struct audio_stream
*stream
)
116 init_dma(stream
->dma
);
117 if (get_dma_active_buffer(stream
->dma
) == 0) {
118 clear_dma_done0(stream
->dma
);
119 set_dma_addr0(stream
->dma
, stream
->buffer
->start
);
120 set_dma_count0(stream
->dma
, stream
->period_size
>> 1);
121 set_dma_addr1(stream
->dma
, stream
->buffer
->next
->start
);
122 set_dma_count1(stream
->dma
, stream
->period_size
>> 1);
124 clear_dma_done1(stream
->dma
);
125 set_dma_addr1(stream
->dma
, stream
->buffer
->start
);
126 set_dma_count1(stream
->dma
, stream
->period_size
>> 1);
127 set_dma_addr0(stream
->dma
, stream
->buffer
->next
->start
);
128 set_dma_count0(stream
->dma
, stream
->period_size
>> 1);
130 enable_dma_buffers(stream
->dma
);
131 start_dma(stream
->dma
);
134 static irqreturn_t
au1000_dma_interrupt(int irq
, void *ptr
)
136 struct audio_stream
*stream
= (struct audio_stream
*)ptr
;
137 struct snd_pcm_substream
*substream
= stream
->substream
;
139 switch (get_dma_buffer_done(stream
->dma
)) {
141 stream
->buffer
= stream
->buffer
->next
;
142 clear_dma_done0(stream
->dma
);
143 set_dma_addr0(stream
->dma
, stream
->buffer
->next
->start
);
144 set_dma_count0(stream
->dma
, stream
->period_size
>> 1);
145 enable_dma_buffer0(stream
->dma
);
148 stream
->buffer
= stream
->buffer
->next
;
149 clear_dma_done1(stream
->dma
);
150 set_dma_addr1(stream
->dma
, stream
->buffer
->next
->start
);
151 set_dma_count1(stream
->dma
, stream
->period_size
>> 1);
152 enable_dma_buffer1(stream
->dma
);
154 case (DMA_D0
| DMA_D1
):
155 pr_debug("DMA %d missed interrupt.\n", stream
->dma
);
156 au1000_dma_stop(stream
);
157 au1000_dma_start(stream
);
159 case (~DMA_D0
& ~DMA_D1
):
160 pr_debug("DMA %d empty irq.\n", stream
->dma
);
162 snd_pcm_period_elapsed(substream
);
166 static const struct snd_pcm_hardware alchemy_pcm_hardware
= {
167 .info
= SNDRV_PCM_INFO_MMAP
| SNDRV_PCM_INFO_MMAP_VALID
|
168 SNDRV_PCM_INFO_INTERLEAVED
| SNDRV_PCM_INFO_BATCH
,
169 .period_bytes_min
= 1024,
170 .period_bytes_max
= 16 * 1024 - 1,
173 .buffer_bytes_max
= 128 * 1024,
177 static inline struct alchemy_pcm_ctx
*ss_to_ctx(struct snd_pcm_substream
*ss
,
178 struct snd_soc_component
*component
)
180 return snd_soc_component_get_drvdata(component
);
183 static inline struct audio_stream
*ss_to_as(struct snd_pcm_substream
*ss
,
184 struct snd_soc_component
*component
)
186 struct alchemy_pcm_ctx
*ctx
= ss_to_ctx(ss
, component
);
187 return &(ctx
->stream
[ss
->stream
]);
190 static int alchemy_pcm_open(struct snd_soc_component
*component
,
191 struct snd_pcm_substream
*substream
)
193 struct alchemy_pcm_ctx
*ctx
= ss_to_ctx(substream
, component
);
194 struct snd_soc_pcm_runtime
*rtd
= asoc_substream_to_rtd(substream
);
195 int *dmaids
, s
= substream
->stream
;
198 dmaids
= snd_soc_dai_get_dma_data(asoc_rtd_to_cpu(rtd
, 0), substream
);
200 return -ENODEV
; /* whoa, has ordering changed? */
203 name
= (s
== SNDRV_PCM_STREAM_PLAYBACK
) ? "audio-tx" : "audio-rx";
204 ctx
->stream
[s
].dma
= request_au1000_dma(dmaids
[s
], name
,
205 au1000_dma_interrupt
, 0,
207 set_dma_mode(ctx
->stream
[s
].dma
,
208 get_dma_mode(ctx
->stream
[s
].dma
) & ~DMA_NC
);
210 ctx
->stream
[s
].substream
= substream
;
211 ctx
->stream
[s
].buffer
= NULL
;
212 snd_soc_set_runtime_hwparams(substream
, &alchemy_pcm_hardware
);
217 static int alchemy_pcm_close(struct snd_soc_component
*component
,
218 struct snd_pcm_substream
*substream
)
220 struct alchemy_pcm_ctx
*ctx
= ss_to_ctx(substream
, component
);
221 int stype
= substream
->stream
;
223 ctx
->stream
[stype
].substream
= NULL
;
224 free_au1000_dma(ctx
->stream
[stype
].dma
);
229 static int alchemy_pcm_hw_params(struct snd_soc_component
*component
,
230 struct snd_pcm_substream
*substream
,
231 struct snd_pcm_hw_params
*hw_params
)
233 struct audio_stream
*stream
= ss_to_as(substream
, component
);
235 return au1000_setup_dma_link(stream
,
236 params_period_bytes(hw_params
),
237 params_periods(hw_params
));
240 static int alchemy_pcm_hw_free(struct snd_soc_component
*component
,
241 struct snd_pcm_substream
*substream
)
243 struct audio_stream
*stream
= ss_to_as(substream
, component
);
244 au1000_release_dma_link(stream
);
248 static int alchemy_pcm_trigger(struct snd_soc_component
*component
,
249 struct snd_pcm_substream
*substream
, int cmd
)
251 struct audio_stream
*stream
= ss_to_as(substream
, component
);
255 case SNDRV_PCM_TRIGGER_START
:
256 au1000_dma_start(stream
);
258 case SNDRV_PCM_TRIGGER_STOP
:
259 au1000_dma_stop(stream
);
268 static snd_pcm_uframes_t
alchemy_pcm_pointer(struct snd_soc_component
*component
,
269 struct snd_pcm_substream
*ss
)
271 struct audio_stream
*stream
= ss_to_as(ss
, component
);
274 location
= get_dma_residue(stream
->dma
);
275 location
= stream
->buffer
->relative_end
- location
;
278 return bytes_to_frames(ss
->runtime
, location
);
281 static int alchemy_pcm_new(struct snd_soc_component
*component
,
282 struct snd_soc_pcm_runtime
*rtd
)
284 struct snd_pcm
*pcm
= rtd
->pcm
;
286 snd_pcm_set_managed_buffer_all(pcm
, SNDRV_DMA_TYPE_CONTINUOUS
,
287 NULL
, 65536, (4096 * 1024) - 1);
292 static struct snd_soc_component_driver alchemy_pcm_soc_component
= {
294 .open
= alchemy_pcm_open
,
295 .close
= alchemy_pcm_close
,
296 .hw_params
= alchemy_pcm_hw_params
,
297 .hw_free
= alchemy_pcm_hw_free
,
298 .trigger
= alchemy_pcm_trigger
,
299 .pointer
= alchemy_pcm_pointer
,
300 .pcm_construct
= alchemy_pcm_new
,
303 static int alchemy_pcm_drvprobe(struct platform_device
*pdev
)
305 struct alchemy_pcm_ctx
*ctx
;
307 ctx
= devm_kzalloc(&pdev
->dev
, sizeof(*ctx
), GFP_KERNEL
);
311 platform_set_drvdata(pdev
, ctx
);
313 return devm_snd_soc_register_component(&pdev
->dev
,
314 &alchemy_pcm_soc_component
, NULL
, 0);
317 static struct platform_driver alchemy_pcmdma_driver
= {
319 .name
= "alchemy-pcm-dma",
321 .probe
= alchemy_pcm_drvprobe
,
324 module_platform_driver(alchemy_pcmdma_driver
);
326 MODULE_LICENSE("GPL");
327 MODULE_DESCRIPTION("Au1000/Au1500/Au1100 Audio DMA driver");
328 MODULE_AUTHOR("Manuel Lauss");