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