xref: /openbsd-src/sys/arch/arm64/dev/apldma.c (revision 952ee722ff3e76fd82a065b2cc272b46ed9c22f9)
1*952ee722Skettenis /*	$OpenBSD: apldma.c,v 1.6 2023/07/26 11:09:24 kettenis Exp $	*/
2b1141157Skettenis /*
3b1141157Skettenis  * Copyright (c) 2022 Mark Kettenis <kettenis@openbsd.org>
4b1141157Skettenis  *
5b1141157Skettenis  * Permission to use, copy, modify, and distribute this software for any
6b1141157Skettenis  * purpose with or without fee is hereby granted, provided that the above
7b1141157Skettenis  * copyright notice and this permission notice appear in all copies.
8b1141157Skettenis  *
9b1141157Skettenis  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10b1141157Skettenis  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11b1141157Skettenis  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12b1141157Skettenis  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13b1141157Skettenis  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14b1141157Skettenis  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15b1141157Skettenis  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16b1141157Skettenis  */
17b1141157Skettenis 
18b1141157Skettenis #include <sys/param.h>
19b1141157Skettenis #include <sys/systm.h>
20b1141157Skettenis #include <sys/device.h>
21b1141157Skettenis #include <sys/malloc.h>
22b1141157Skettenis 
23b1141157Skettenis #include <machine/bus.h>
24b1141157Skettenis #include <machine/fdt.h>
25b1141157Skettenis 
26b1141157Skettenis #include <dev/ofw/openfirm.h>
27b1141157Skettenis #include <dev/ofw/ofw_power.h>
28b1141157Skettenis #include <dev/ofw/fdt.h>
29b1141157Skettenis 
30b1141157Skettenis #include <dev/audio_if.h>
31b1141157Skettenis 
32b1141157Skettenis #include <arm64/dev/apldma.h>
33b1141157Skettenis 
34b1141157Skettenis /*
352938582fSkettenis  * The device tree bindings for this hardware use separate Tx and Rx
362938582fSkettenis  * channels with Tx channels using even channel numbers and Rx
372938582fSkettenis  * channels using odd channel numbers.
382938582fSkettenis  */
392938582fSkettenis 
40b1141157Skettenis #define DMA_TX_EN			0x0000
41b1141157Skettenis #define DMA_TX_EN_CLR			0x0004
424b308aa3Skettenis #define DMA_TX_INTR(irq)		(0x0030 + (irq) * 4)
43b1141157Skettenis 
442938582fSkettenis #define DMA_TX_CTL(chan)		(0x8000 + ((chan) / 2) * 0x400)
45b1141157Skettenis #define  DMA_TX_CTL_RESET_RINGS		(1 << 0)
464b308aa3Skettenis #define DMA_TX_INTRSTAT(chan, irq)	(0x8010 + ((chan) / 2) * 0x400 + ((irq) * 4))
47b1141157Skettenis #define  DMA_TX_INTRSTAT_DESC_DONE	(1 << 0)
48b1141157Skettenis #define  DMA_TX_INTRSTAT_ERR		(1 << 6)
494b308aa3Skettenis #define DMA_TX_INTRMASK(chan, irq)	(0x8020 + ((chan) / 2) * 0x400 + ((irq) * 4))
50b1141157Skettenis #define  DMA_TX_INTRMASK_DESC_DONE	(1 << 0)
51b1141157Skettenis #define  DMA_TX_INTRMASK_ERR		(1 << 6)
522938582fSkettenis #define DMA_TX_BUS_WIDTH(chan)		(0x8040 + ((chan) / 2) * 0x400)
53b1141157Skettenis #define  DMA_TX_BUS_WIDTH_8BIT		(0 << 0)
54b1141157Skettenis #define  DMA_TX_BUS_WIDTH_16BIT		(1 << 0)
55b1141157Skettenis #define  DMA_TX_BUS_WIDTH_32BIT		(2 << 0)
56b1141157Skettenis #define  DMA_TX_BUS_WIDTH_FRAME_2_WORDS	(1 << 4)
57b1141157Skettenis #define  DMA_TX_BUS_WIDTH_FRAME_4_WORDS	(2 << 4)
582938582fSkettenis #define DMA_TX_BURST_SIZE(chan)		(0x8054 + ((chan) / 2) * 0x400)
592938582fSkettenis #define  DMA_TX_BURST_SIZE_MAGIC	0x00c00060
602938582fSkettenis #define DMA_TX_RESIDUE(chan)		(0x8064 + ((chan) / 2) * 0x400)
612938582fSkettenis #define DMA_TX_DESC_RING(chan)		(0x8070 + ((chan) / 2) * 0x400)
62b1141157Skettenis #define  DMA_TX_DESC_RING_FULL		(1 << 9)
632938582fSkettenis #define DMA_TX_REPORT_RING(chan)	(0x8074 + ((chan) / 2) * 0x400)
64b1141157Skettenis #define  DMA_TX_REPORT_RING_EMPTY	(1 << 8)
652938582fSkettenis #define DMA_TX_DESC_WRITE(chan)		(0x10000 + ((chan) / 2) * 4)
662938582fSkettenis #define DMA_TX_REPORT_READ(chan)	(0x10100 + ((chan) / 2) * 4)
67b1141157Skettenis 
68b1141157Skettenis #define DMA_DESC_NOTIFY			(1 << 16)
69b1141157Skettenis #define DMA_NUM_DESCRIPTORS		4
702938582fSkettenis #define DMA_NUM_INTERRUPTS		4
71b1141157Skettenis 
72b1141157Skettenis #define HREAD4(sc, reg)							\
73b1141157Skettenis 	(bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (reg)))
74b1141157Skettenis #define HWRITE4(sc, reg, val)						\
75b1141157Skettenis 	bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, (reg), (val))
76b1141157Skettenis 
77b1141157Skettenis struct apldma_channel {
78b1141157Skettenis 	struct apldma_softc	*ac_sc;
79b1141157Skettenis 	unsigned int		ac_chan;
80b1141157Skettenis 
81b1141157Skettenis 	bus_dmamap_t		ac_map;
82b1141157Skettenis 	bus_dma_segment_t	ac_seg;
83b1141157Skettenis 	caddr_t			ac_kva;
84b1141157Skettenis 	bus_size_t		ac_size;
85b1141157Skettenis 
86b1141157Skettenis 	bus_addr_t		ac_base;
87b1141157Skettenis 	bus_size_t		ac_len;
88b1141157Skettenis 	bus_size_t		ac_pos;
89b1141157Skettenis 	bus_size_t		ac_blksize;
90b1141157Skettenis 
91b1141157Skettenis 	void			(*ac_intr)(void *);
92b1141157Skettenis 	void			*ac_intrarg;
93b1141157Skettenis };
94b1141157Skettenis 
95b1141157Skettenis struct apldma_softc {
96b1141157Skettenis 	struct device		sc_dev;
97b1141157Skettenis 	bus_space_tag_t		sc_iot;
98b1141157Skettenis 	bus_space_handle_t	sc_ioh;
99b1141157Skettenis 
100b1141157Skettenis 	bus_dma_tag_t		sc_dmat;
101b1141157Skettenis 	int			sc_node;
102b1141157Skettenis 	void			*sc_ih;
1034b308aa3Skettenis 	int			sc_irq;
104b1141157Skettenis 
1052938582fSkettenis 	int			sc_nchannels;
1062938582fSkettenis 	struct apldma_channel	**sc_ac;
107b1141157Skettenis };
108b1141157Skettenis 
109b1141157Skettenis struct apldma_softc *apldma_sc;
110b1141157Skettenis 
111b1141157Skettenis int	apldma_match(struct device *, void *, void *);
112b1141157Skettenis void	apldma_attach(struct device *, struct device *, void *);
113*952ee722Skettenis int	apldma_activate(struct device *, int);
114b1141157Skettenis 
115b1141157Skettenis const struct cfattach apldma_ca = {
116*952ee722Skettenis 	sizeof (struct apldma_softc), apldma_match, apldma_attach, NULL,
117*952ee722Skettenis 	apldma_activate
118b1141157Skettenis };
119b1141157Skettenis 
120b1141157Skettenis struct cfdriver apldma_cd = {
121b1141157Skettenis 	NULL, "apldma", DV_DULL
122b1141157Skettenis };
123b1141157Skettenis 
124b1141157Skettenis int	apldma_intr(void *);
125b1141157Skettenis 
126b1141157Skettenis int
apldma_match(struct device * parent,void * match,void * aux)127b1141157Skettenis apldma_match(struct device *parent, void *match, void *aux)
128b1141157Skettenis {
129b1141157Skettenis 	struct fdt_attach_args *faa = aux;
130b1141157Skettenis 
131b1141157Skettenis 	return OF_is_compatible(faa->fa_node, "apple,admac");
132b1141157Skettenis }
133b1141157Skettenis 
134b1141157Skettenis void
apldma_attach(struct device * parent,struct device * self,void * aux)135b1141157Skettenis apldma_attach(struct device *parent, struct device *self, void *aux)
136b1141157Skettenis {
137b1141157Skettenis 	struct apldma_softc *sc = (struct apldma_softc *)self;
138b1141157Skettenis 	struct fdt_attach_args *faa = aux;
1394b308aa3Skettenis 	int irq;
140b1141157Skettenis 
141b1141157Skettenis 	if (faa->fa_nreg < 1) {
142b1141157Skettenis 		printf(": no registers\n");
143b1141157Skettenis 		return;
144b1141157Skettenis 	}
145b1141157Skettenis 
146b1141157Skettenis 	sc->sc_iot = faa->fa_iot;
147b1141157Skettenis 	if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr,
148b1141157Skettenis 	    faa->fa_reg[0].size, 0, &sc->sc_ioh)) {
149b1141157Skettenis 		printf(": can't map registers\n");
150b1141157Skettenis 		return;
151b1141157Skettenis 	}
152b1141157Skettenis 
1532938582fSkettenis 	sc->sc_nchannels = OF_getpropint(faa->fa_node, "dma-channels", 0);
1542938582fSkettenis 	if (sc->sc_nchannels == 0) {
1552938582fSkettenis 		printf(": no DMA channels\n");
1562938582fSkettenis 		goto unmap;
1572938582fSkettenis 	}
1582938582fSkettenis 	sc->sc_ac = mallocarray(sc->sc_nchannels,
159ddd5e87dSjsg 	    sizeof(struct apldma_channel *), M_DEVBUF, M_WAITOK | M_ZERO);
1602938582fSkettenis 
161b1141157Skettenis 	sc->sc_dmat = faa->fa_dmat;
162b1141157Skettenis 	sc->sc_node = faa->fa_node;
163b1141157Skettenis 
164b1141157Skettenis 	power_domain_enable(sc->sc_node);
165b1141157Skettenis 
1664b308aa3Skettenis 	/*
1674b308aa3Skettenis 	 * The hardware supports multiple interrupts; pick the first
1684b308aa3Skettenis 	 * one that is actually connected.
1694b308aa3Skettenis 	 */
1704b308aa3Skettenis 	for (irq = 0; irq < DMA_NUM_INTERRUPTS; irq++) {
1714b308aa3Skettenis 		sc->sc_ih = fdt_intr_establish_idx(faa->fa_node, irq,
1724b308aa3Skettenis 		    IPL_AUDIO | IPL_MPSAFE, apldma_intr, sc,
1734b308aa3Skettenis 		    sc->sc_dev.dv_xname);
1744b308aa3Skettenis 		if (sc->sc_ih)
1754b308aa3Skettenis 			break;
1764b308aa3Skettenis 	}
177b1141157Skettenis 	if (sc->sc_ih == NULL) {
178b1141157Skettenis 		printf(": can't establish interrupt\n");
1792938582fSkettenis 		goto free;
180b1141157Skettenis 	}
181b1141157Skettenis 
1824b308aa3Skettenis 	/*
1834b308aa3Skettenis 	 * The preliminary device tree bindings used a special
1844b308aa3Skettenis 	 * property to describe which interrupt was actually
1854b308aa3Skettenis 	 * connected.  Remove this in the near future.
1864b308aa3Skettenis 	 */
1874b308aa3Skettenis 	sc->sc_irq = OF_getpropint(faa->fa_node,
1884b308aa3Skettenis 	    "apple,internal-irq-destination", irq);
1894b308aa3Skettenis 
190b1141157Skettenis 	printf("\n");
191b1141157Skettenis 
192b1141157Skettenis 	apldma_sc = sc;
193b1141157Skettenis 	return;
194b1141157Skettenis 
1952938582fSkettenis free:
1962938582fSkettenis 	free(sc->sc_ac, M_DEVBUF,
197ddd5e87dSjsg 	    sc->sc_nchannels * sizeof(struct apldma_channel *));
198b1141157Skettenis unmap:
199b1141157Skettenis 	bus_space_unmap(sc->sc_iot, sc->sc_ioh, faa->fa_reg[0].size);
200b1141157Skettenis }
201b1141157Skettenis 
202*952ee722Skettenis int
apldma_activate(struct device * self,int act)203*952ee722Skettenis apldma_activate(struct device *self, int act)
204*952ee722Skettenis {
205*952ee722Skettenis 	struct apldma_softc *sc = (struct apldma_softc *)self;
206*952ee722Skettenis 
207*952ee722Skettenis 	switch (act) {
208*952ee722Skettenis 	case DVACT_SUSPEND:
209*952ee722Skettenis 		power_domain_disable(sc->sc_node);
210*952ee722Skettenis 		break;
211*952ee722Skettenis 	case DVACT_RESUME:
212*952ee722Skettenis 		power_domain_enable(sc->sc_node);
213*952ee722Skettenis 		break;
214*952ee722Skettenis 	}
215*952ee722Skettenis 
216*952ee722Skettenis 	return 0;
217*952ee722Skettenis }
218*952ee722Skettenis 
219b1141157Skettenis void
apldma_fill_descriptors(struct apldma_channel * ac)220b1141157Skettenis apldma_fill_descriptors(struct apldma_channel *ac)
221b1141157Skettenis {
222b1141157Skettenis 	struct apldma_softc *sc = ac->ac_sc;
223b1141157Skettenis 	unsigned int i;
224b1141157Skettenis 
225b1141157Skettenis 	for (i = 0; i < DMA_NUM_DESCRIPTORS; i++) {
226b1141157Skettenis 		bus_addr_t addr = ac->ac_base + ac->ac_pos;
227b1141157Skettenis 		uint32_t status;
228b1141157Skettenis 
229b1141157Skettenis 		status = HREAD4(sc, DMA_TX_DESC_RING(ac->ac_chan));
230b1141157Skettenis 		if (status & DMA_TX_DESC_RING_FULL)
231b1141157Skettenis 			break;
232b1141157Skettenis 
233b1141157Skettenis 		HWRITE4(sc, DMA_TX_DESC_WRITE(ac->ac_chan), addr);
234b1141157Skettenis 		HWRITE4(sc, DMA_TX_DESC_WRITE(ac->ac_chan), addr >> 32);
235b1141157Skettenis 		HWRITE4(sc, DMA_TX_DESC_WRITE(ac->ac_chan), ac->ac_blksize);
236b1141157Skettenis 		HWRITE4(sc, DMA_TX_DESC_WRITE(ac->ac_chan), DMA_DESC_NOTIFY);
237b1141157Skettenis 
238b1141157Skettenis 		ac->ac_pos += ac->ac_blksize;
239b1141157Skettenis 		if (ac->ac_pos > ac->ac_len - ac->ac_blksize)
240b1141157Skettenis 			ac->ac_pos = 0;
241b1141157Skettenis 	}
242b1141157Skettenis }
243b1141157Skettenis 
244b1141157Skettenis int
apldma_intr(void * arg)245b1141157Skettenis apldma_intr(void *arg)
246b1141157Skettenis {
247b1141157Skettenis 	struct apldma_softc *sc = arg;
248b1141157Skettenis 	uint32_t intr, intrstat;
249b1141157Skettenis 	unsigned int chan, i;
250b1141157Skettenis 
2514b308aa3Skettenis 	intr = HREAD4(sc, DMA_TX_INTR(sc->sc_irq));
2522938582fSkettenis 	for (chan = 0; chan < sc->sc_nchannels; chan += 2) {
2532938582fSkettenis 		if ((intr & (1 << (chan / 2))) == 0)
254b1141157Skettenis 			continue;
255b1141157Skettenis 
2564b308aa3Skettenis 		intrstat = HREAD4(sc, DMA_TX_INTRSTAT(chan, sc->sc_irq));
2574b308aa3Skettenis 		HWRITE4(sc, DMA_TX_INTRSTAT(chan, sc->sc_irq), intrstat);
258b1141157Skettenis 
259b1141157Skettenis 		if ((intrstat & DMA_TX_INTRSTAT_DESC_DONE) == 0)
260b1141157Skettenis 			continue;
261b1141157Skettenis 
262b1141157Skettenis 		for (i = 0; i < DMA_NUM_DESCRIPTORS; i++) {
263b1141157Skettenis 			uint32_t status;
264b1141157Skettenis 
265b1141157Skettenis 			status = HREAD4(sc, DMA_TX_REPORT_RING(chan));
266b1141157Skettenis 			if (status & DMA_TX_REPORT_RING_EMPTY)
267b1141157Skettenis 				break;
268b1141157Skettenis 
269b1141157Skettenis 			/* Consume report descriptor. */
270b1141157Skettenis 			HREAD4(sc, DMA_TX_REPORT_READ(chan));
271b1141157Skettenis 			HREAD4(sc, DMA_TX_REPORT_READ(chan));
272b1141157Skettenis 			HREAD4(sc, DMA_TX_REPORT_READ(chan));
273b1141157Skettenis 			HREAD4(sc, DMA_TX_REPORT_READ(chan));
274b1141157Skettenis 		}
275b1141157Skettenis 
276b1141157Skettenis 		mtx_enter(&audio_lock);
277b1141157Skettenis 		struct apldma_channel *ac = sc->sc_ac[chan];
278b1141157Skettenis 		ac->ac_intr(ac->ac_intrarg);
279b1141157Skettenis 		mtx_leave(&audio_lock);
280b1141157Skettenis 
281b1141157Skettenis 		apldma_fill_descriptors(sc->sc_ac[chan]);
282b1141157Skettenis 	}
283b1141157Skettenis 
284b1141157Skettenis 	return 1;
285b1141157Skettenis }
286b1141157Skettenis 
287b1141157Skettenis struct apldma_channel *
apldma_alloc_channel(unsigned int chan)288b1141157Skettenis apldma_alloc_channel(unsigned int chan)
289b1141157Skettenis {
290b1141157Skettenis 	struct apldma_softc *sc = apldma_sc;
291b1141157Skettenis 	struct apldma_channel *ac;
292b1141157Skettenis 
2934e642e0aStobhe 	if (sc == NULL || chan >= sc->sc_nchannels)
2942938582fSkettenis 		return NULL;
2952938582fSkettenis 
2962938582fSkettenis 	/* We only support Tx channels for now. */
2972938582fSkettenis 	if ((chan % 2) != 0)
298b1141157Skettenis 		return NULL;
299b1141157Skettenis 
300b1141157Skettenis 	ac = malloc(sizeof(*ac), M_DEVBUF, M_WAITOK);
301b1141157Skettenis 	ac->ac_sc = sc;
302b1141157Skettenis 	ac->ac_chan = chan;
303b1141157Skettenis 	sc->sc_ac[chan] = ac;
304b1141157Skettenis 	return ac;
305b1141157Skettenis }
306b1141157Skettenis 
307b1141157Skettenis void
apldma_free_channel(struct apldma_channel * ac)308b1141157Skettenis apldma_free_channel(struct apldma_channel *ac)
309b1141157Skettenis {
310b1141157Skettenis 	struct apldma_softc *sc = ac->ac_sc;
311b1141157Skettenis 
312b1141157Skettenis 	sc->sc_ac[ac->ac_chan] = NULL;
313b1141157Skettenis 	free(ac, M_DEVBUF, sizeof(*ac));
314b1141157Skettenis }
315b1141157Skettenis 
316b1141157Skettenis void *
apldma_allocm(struct apldma_channel * ac,size_t size,int flags)317b1141157Skettenis apldma_allocm(struct apldma_channel *ac, size_t size, int flags)
318b1141157Skettenis {
319b1141157Skettenis 	struct apldma_softc *sc = ac->ac_sc;
320b1141157Skettenis 	int nsegs;
321b1141157Skettenis 	int err;
322b1141157Skettenis 
323b1141157Skettenis 	flags = (flags & M_WAITOK) ? BUS_DMA_WAITOK : BUS_DMA_NOWAIT;
324b1141157Skettenis 
325b1141157Skettenis 	err = bus_dmamem_alloc(sc->sc_dmat, size, 0, 0, &ac->ac_seg, 1,
326b1141157Skettenis 	    &nsegs, flags);
327b1141157Skettenis 	if (err)
328b1141157Skettenis 		return NULL;
329b1141157Skettenis 	err = bus_dmamem_map(sc->sc_dmat, &ac->ac_seg, 1, size, &ac->ac_kva,
330b1141157Skettenis 	    flags | BUS_DMA_COHERENT);
331b1141157Skettenis 	if (err)
332b1141157Skettenis 		goto free;
333b1141157Skettenis 	err = bus_dmamap_create(sc->sc_dmat, size, 1, size, 0, flags,
334b1141157Skettenis 	    &ac->ac_map);
335b1141157Skettenis 	if (err)
336b1141157Skettenis 		goto unmap;
337b1141157Skettenis 	err = bus_dmamap_load(sc->sc_dmat, ac->ac_map, ac->ac_kva, size,
338b1141157Skettenis 	    NULL, flags);
339b1141157Skettenis 	if (err)
340b1141157Skettenis 		goto destroy;
341b1141157Skettenis 
342b1141157Skettenis 	ac->ac_size = size;
343b1141157Skettenis 	return ac->ac_kva;
344b1141157Skettenis 
345b1141157Skettenis destroy:
346b1141157Skettenis 	bus_dmamap_destroy(sc->sc_dmat, ac->ac_map);
347b1141157Skettenis unmap:
348b1141157Skettenis 	bus_dmamem_unmap(sc->sc_dmat, ac->ac_kva, size);
349b1141157Skettenis free:
350b1141157Skettenis 	bus_dmamem_free(sc->sc_dmat, &ac->ac_seg, 1);
351b1141157Skettenis 	return NULL;
352b1141157Skettenis }
353b1141157Skettenis 
354b1141157Skettenis void
apldma_freem(struct apldma_channel * ac)355b1141157Skettenis apldma_freem(struct apldma_channel *ac)
356b1141157Skettenis {
357b1141157Skettenis 	struct apldma_softc *sc = ac->ac_sc;
358b1141157Skettenis 
359b1141157Skettenis 	bus_dmamap_unload(sc->sc_dmat, ac->ac_map);
360b1141157Skettenis 	bus_dmamap_destroy(sc->sc_dmat, ac->ac_map);
361b1141157Skettenis 	bus_dmamem_unmap(sc->sc_dmat, ac->ac_kva, ac->ac_size);
362b1141157Skettenis 	bus_dmamem_free(sc->sc_dmat, &ac->ac_seg, 1);
363b1141157Skettenis }
364b1141157Skettenis 
365b1141157Skettenis int
apldma_trigger_output(struct apldma_channel * ac,void * start,void * end,int blksize,void (* intr)(void *),void * intrarg,struct audio_params * params)366b1141157Skettenis apldma_trigger_output(struct apldma_channel *ac, void *start, void *end,
367b1141157Skettenis     int blksize, void (*intr)(void *), void *intrarg,
368b1141157Skettenis     struct audio_params *params)
369b1141157Skettenis {
370b1141157Skettenis 	struct apldma_softc *sc = ac->ac_sc;
371b1141157Skettenis 
372b1141157Skettenis 	KASSERT(start == ac->ac_kva);
373b1141157Skettenis 
374b1141157Skettenis 	ac->ac_base = ac->ac_map->dm_segs[0].ds_addr;
375b1141157Skettenis 	ac->ac_len = end - start;
376b1141157Skettenis 	ac->ac_pos = 0;
377b1141157Skettenis 	ac->ac_blksize = blksize;
378b1141157Skettenis 
379b1141157Skettenis 	ac->ac_intr = intr;
380b1141157Skettenis 	ac->ac_intrarg = intrarg;
381b1141157Skettenis 
382b1141157Skettenis 	switch (params->bps) {
383b1141157Skettenis 	case 2:
384b1141157Skettenis 		HWRITE4(sc, DMA_TX_BUS_WIDTH(ac->ac_chan),
385b1141157Skettenis 		    DMA_TX_BUS_WIDTH_16BIT | DMA_TX_BUS_WIDTH_FRAME_4_WORDS);
386b1141157Skettenis 		break;
387b1141157Skettenis 	case 4:
388b1141157Skettenis 		HWRITE4(sc, DMA_TX_BUS_WIDTH(ac->ac_chan),
389b1141157Skettenis 		    DMA_TX_BUS_WIDTH_32BIT | DMA_TX_BUS_WIDTH_FRAME_4_WORDS);
390b1141157Skettenis 		break;
391b1141157Skettenis 	default:
392b1141157Skettenis 		return EINVAL;
393b1141157Skettenis 	}
3942938582fSkettenis 	HWRITE4(sc, DMA_TX_BURST_SIZE(ac->ac_chan), DMA_TX_BURST_SIZE_MAGIC);
395b1141157Skettenis 
396b1141157Skettenis 	/* Reset rings. */
397b1141157Skettenis 	HWRITE4(sc, DMA_TX_CTL(ac->ac_chan), DMA_TX_CTL_RESET_RINGS);
398b1141157Skettenis 	HWRITE4(sc, DMA_TX_CTL(ac->ac_chan), 0);
399b1141157Skettenis 
400b1141157Skettenis 	/* Clear and unmask interrupts. */
4014b308aa3Skettenis 	HWRITE4(sc, DMA_TX_INTRSTAT(ac->ac_chan, sc->sc_irq),
402b1141157Skettenis 	   DMA_TX_INTRSTAT_DESC_DONE | DMA_TX_INTRSTAT_ERR);
4034b308aa3Skettenis 	HWRITE4(sc, DMA_TX_INTRMASK(ac->ac_chan, sc->sc_irq),
404b1141157Skettenis 	   DMA_TX_INTRMASK_DESC_DONE | DMA_TX_INTRMASK_ERR);
405b1141157Skettenis 
406b1141157Skettenis 	apldma_fill_descriptors(ac);
407b1141157Skettenis 
408b1141157Skettenis 	/* Start DMA transfer. */
4092938582fSkettenis 	HWRITE4(sc, DMA_TX_EN, 1 << (ac->ac_chan / 2));
410b1141157Skettenis 
411b1141157Skettenis 	return 0;
412b1141157Skettenis }
413b1141157Skettenis 
414b1141157Skettenis int
apldma_halt_output(struct apldma_channel * ac)415b1141157Skettenis apldma_halt_output(struct apldma_channel *ac)
416b1141157Skettenis {
417b1141157Skettenis 	struct apldma_softc *sc = ac->ac_sc;
418b1141157Skettenis 
419b1141157Skettenis 	/* Stop DMA transfer. */
4202938582fSkettenis 	HWRITE4(sc, DMA_TX_EN_CLR, 1 << (ac->ac_chan / 2));
421b1141157Skettenis 
422b1141157Skettenis 	/* Mask all interrupts. */
4234b308aa3Skettenis 	HWRITE4(sc, DMA_TX_INTRMASK(ac->ac_chan, sc->sc_irq), 0);
424b1141157Skettenis 
425b1141157Skettenis 	return 0;
426b1141157Skettenis }
427