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 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 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 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 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 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 * 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 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 * 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 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 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 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