2 * Au1000/Au1500/Au1100 Audio DMA support.
4 * (c) 2011 Manuel Lauss <manuel.lauss@googlemail.com>
6 * copied almost verbatim from the old ALSA driver, written by
7 * Charles Eidsness <charles@cooper-street.com>
10 #include <linux/module.h>
11 #include <linux/init.h>
12 #include <linux/platform_device.h>
13 #include <linux/slab.h>
14 #include <linux/dma-mapping.h>
15 #include <sound/core.h>
16 #include <sound/pcm.h>
17 #include <sound/pcm_params.h>
18 #include <sound/soc.h>
19 #include <asm/mach-au1x00/au1000.h>
20 #include <asm/mach-au1x00/au1000_dma.h>
26 u32 relative_end
; /* relative to start of buffer */
27 struct pcm_period
*next
;
31 struct snd_pcm_substream
*substream
;
33 struct pcm_period
*buffer
;
34 unsigned int period_size
;
38 struct alchemy_pcm_ctx
{
39 struct audio_stream stream
[2]; /* playback & capture */
42 static void au1000_release_dma_link(struct audio_stream
*stream
)
44 struct pcm_period
*pointer
;
45 struct pcm_period
*pointer_next
;
47 stream
->period_size
= 0;
49 pointer
= stream
->buffer
;
53 pointer_next
= pointer
->next
;
55 pointer
= pointer_next
;
56 } while (pointer
!= stream
->buffer
);
57 stream
->buffer
= NULL
;
60 static int au1000_setup_dma_link(struct audio_stream
*stream
,
61 unsigned int period_bytes
,
64 struct snd_pcm_substream
*substream
= stream
->substream
;
65 struct snd_pcm_runtime
*runtime
= substream
->runtime
;
66 struct pcm_period
*pointer
;
67 unsigned long dma_start
;
70 dma_start
= virt_to_phys(runtime
->dma_area
);
72 if (stream
->period_size
== period_bytes
&&
73 stream
->periods
== periods
)
74 return 0; /* not changed */
76 au1000_release_dma_link(stream
);
78 stream
->period_size
= period_bytes
;
79 stream
->periods
= periods
;
81 stream
->buffer
= kmalloc(sizeof(struct pcm_period
), GFP_KERNEL
);
84 pointer
= stream
->buffer
;
85 for (i
= 0; i
< periods
; i
++) {
86 pointer
->start
= (u32
)(dma_start
+ (i
* period_bytes
));
87 pointer
->relative_end
= (u32
) (((i
+1) * period_bytes
) - 0x1);
88 if (i
< periods
- 1) {
89 pointer
->next
= kmalloc(sizeof(struct pcm_period
),
92 au1000_release_dma_link(stream
);
95 pointer
= pointer
->next
;
98 pointer
->next
= stream
->buffer
;
102 static void au1000_dma_stop(struct audio_stream
*stream
)
105 disable_dma(stream
->dma
);
108 static void au1000_dma_start(struct audio_stream
*stream
)
113 init_dma(stream
->dma
);
114 if (get_dma_active_buffer(stream
->dma
) == 0) {
115 clear_dma_done0(stream
->dma
);
116 set_dma_addr0(stream
->dma
, stream
->buffer
->start
);
117 set_dma_count0(stream
->dma
, stream
->period_size
>> 1);
118 set_dma_addr1(stream
->dma
, stream
->buffer
->next
->start
);
119 set_dma_count1(stream
->dma
, stream
->period_size
>> 1);
121 clear_dma_done1(stream
->dma
);
122 set_dma_addr1(stream
->dma
, stream
->buffer
->start
);
123 set_dma_count1(stream
->dma
, stream
->period_size
>> 1);
124 set_dma_addr0(stream
->dma
, stream
->buffer
->next
->start
);
125 set_dma_count0(stream
->dma
, stream
->period_size
>> 1);
127 enable_dma_buffers(stream
->dma
);
128 start_dma(stream
->dma
);
131 static irqreturn_t
au1000_dma_interrupt(int irq
, void *ptr
)
133 struct audio_stream
*stream
= (struct audio_stream
*)ptr
;
134 struct snd_pcm_substream
*substream
= stream
->substream
;
136 switch (get_dma_buffer_done(stream
->dma
)) {
138 stream
->buffer
= stream
->buffer
->next
;
139 clear_dma_done0(stream
->dma
);
140 set_dma_addr0(stream
->dma
, stream
->buffer
->next
->start
);
141 set_dma_count0(stream
->dma
, stream
->period_size
>> 1);
142 enable_dma_buffer0(stream
->dma
);
145 stream
->buffer
= stream
->buffer
->next
;
146 clear_dma_done1(stream
->dma
);
147 set_dma_addr1(stream
->dma
, stream
->buffer
->next
->start
);
148 set_dma_count1(stream
->dma
, stream
->period_size
>> 1);
149 enable_dma_buffer1(stream
->dma
);
151 case (DMA_D0
| DMA_D1
):
152 pr_debug("DMA %d missed interrupt.\n", stream
->dma
);
153 au1000_dma_stop(stream
);
154 au1000_dma_start(stream
);
156 case (~DMA_D0
& ~DMA_D1
):
157 pr_debug("DMA %d empty irq.\n", stream
->dma
);
159 snd_pcm_period_elapsed(substream
);
163 static const struct snd_pcm_hardware alchemy_pcm_hardware
= {
164 .info
= SNDRV_PCM_INFO_MMAP
| SNDRV_PCM_INFO_MMAP_VALID
|
165 SNDRV_PCM_INFO_INTERLEAVED
| SNDRV_PCM_INFO_BATCH
,
166 .period_bytes_min
= 1024,
167 .period_bytes_max
= 16 * 1024 - 1,
170 .buffer_bytes_max
= 128 * 1024,
174 static inline struct alchemy_pcm_ctx
*ss_to_ctx(struct snd_pcm_substream
*ss
)
176 struct snd_soc_pcm_runtime
*rtd
= ss
->private_data
;
177 return snd_soc_platform_get_drvdata(rtd
->platform
);
180 static inline struct audio_stream
*ss_to_as(struct snd_pcm_substream
*ss
)
182 struct alchemy_pcm_ctx
*ctx
= ss_to_ctx(ss
);
183 return &(ctx
->stream
[ss
->stream
]);
186 static int alchemy_pcm_open(struct snd_pcm_substream
*substream
)
188 struct alchemy_pcm_ctx
*ctx
= ss_to_ctx(substream
);
189 struct snd_soc_pcm_runtime
*rtd
= substream
->private_data
;
190 int *dmaids
, s
= substream
->stream
;
193 dmaids
= snd_soc_dai_get_dma_data(rtd
->cpu_dai
, substream
);
195 return -ENODEV
; /* whoa, has ordering changed? */
198 name
= (s
== SNDRV_PCM_STREAM_PLAYBACK
) ? "audio-tx" : "audio-rx";
199 ctx
->stream
[s
].dma
= request_au1000_dma(dmaids
[s
], name
,
200 au1000_dma_interrupt
, 0,
202 set_dma_mode(ctx
->stream
[s
].dma
,
203 get_dma_mode(ctx
->stream
[s
].dma
) & ~DMA_NC
);
205 ctx
->stream
[s
].substream
= substream
;
206 ctx
->stream
[s
].buffer
= NULL
;
207 snd_soc_set_runtime_hwparams(substream
, &alchemy_pcm_hardware
);
212 static int alchemy_pcm_close(struct snd_pcm_substream
*substream
)
214 struct alchemy_pcm_ctx
*ctx
= ss_to_ctx(substream
);
215 int stype
= substream
->stream
;
217 ctx
->stream
[stype
].substream
= NULL
;
218 free_au1000_dma(ctx
->stream
[stype
].dma
);
223 static int alchemy_pcm_hw_params(struct snd_pcm_substream
*substream
,
224 struct snd_pcm_hw_params
*hw_params
)
226 struct audio_stream
*stream
= ss_to_as(substream
);
229 err
= snd_pcm_lib_malloc_pages(substream
,
230 params_buffer_bytes(hw_params
));
233 err
= au1000_setup_dma_link(stream
,
234 params_period_bytes(hw_params
),
235 params_periods(hw_params
));
237 snd_pcm_lib_free_pages(substream
);
242 static int alchemy_pcm_hw_free(struct snd_pcm_substream
*substream
)
244 struct audio_stream
*stream
= ss_to_as(substream
);
245 au1000_release_dma_link(stream
);
246 return snd_pcm_lib_free_pages(substream
);
249 static int alchemy_pcm_trigger(struct snd_pcm_substream
*substream
, int cmd
)
251 struct audio_stream
*stream
= ss_to_as(substream
);
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_pcm_substream
*ss
)
270 struct audio_stream
*stream
= ss_to_as(ss
);
273 location
= get_dma_residue(stream
->dma
);
274 location
= stream
->buffer
->relative_end
- location
;
277 return bytes_to_frames(ss
->runtime
, location
);
280 static struct snd_pcm_ops alchemy_pcm_ops
= {
281 .open
= alchemy_pcm_open
,
282 .close
= alchemy_pcm_close
,
283 .ioctl
= snd_pcm_lib_ioctl
,
284 .hw_params
= alchemy_pcm_hw_params
,
285 .hw_free
= alchemy_pcm_hw_free
,
286 .trigger
= alchemy_pcm_trigger
,
287 .pointer
= alchemy_pcm_pointer
,
290 static int alchemy_pcm_new(struct snd_soc_pcm_runtime
*rtd
)
292 struct snd_pcm
*pcm
= rtd
->pcm
;
294 snd_pcm_lib_preallocate_pages_for_all(pcm
, SNDRV_DMA_TYPE_CONTINUOUS
,
295 snd_dma_continuous_data(GFP_KERNEL
), 65536, (4096 * 1024) - 1);
300 static struct snd_soc_platform_driver alchemy_pcm_soc_platform
= {
301 .ops
= &alchemy_pcm_ops
,
302 .pcm_new
= alchemy_pcm_new
,
305 static int alchemy_pcm_drvprobe(struct platform_device
*pdev
)
307 struct alchemy_pcm_ctx
*ctx
;
309 ctx
= devm_kzalloc(&pdev
->dev
, sizeof(*ctx
), GFP_KERNEL
);
313 platform_set_drvdata(pdev
, ctx
);
315 return devm_snd_soc_register_platform(&pdev
->dev
,
316 &alchemy_pcm_soc_platform
);
319 static struct platform_driver alchemy_pcmdma_driver
= {
321 .name
= "alchemy-pcm-dma",
323 .probe
= alchemy_pcm_drvprobe
,
326 module_platform_driver(alchemy_pcmdma_driver
);
328 MODULE_LICENSE("GPL");
329 MODULE_DESCRIPTION("Au1000/Au1500/Au1100 Audio DMA driver");
330 MODULE_AUTHOR("Manuel Lauss");