1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3 * 4 * Copyright 2008 by Marco Trillo. All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 20 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 21 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 22 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 23 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 * SUCH DAMAGE. 26 * 27 * $FreeBSD$ 28 */ 29 30 /* 31 * Apple Onboard Audio (AOA). 32 */ 33 34 #include <sys/cdefs.h> 35 36 #include <sys/param.h> 37 #include <sys/systm.h> 38 #include <sys/kernel.h> 39 #include <sys/bus.h> 40 #include <sys/malloc.h> 41 #include <sys/lock.h> 42 #include <sys/mutex.h> 43 #include <machine/dbdma.h> 44 #include <machine/resource.h> 45 #include <machine/bus.h> 46 #include <sys/rman.h> 47 #include <dev/ofw/ofw_bus.h> 48 49 #ifdef HAVE_KERNEL_OPTION_HEADERS 50 #include "opt_snd.h" 51 #endif 52 53 #include <dev/sound/pcm/sound.h> 54 #include <dev/sound/macio/aoa.h> 55 56 #include "mixer_if.h" 57 58 struct aoa_dma { 59 struct mtx mutex; 60 struct resource *reg; /* DBDMA registers */ 61 dbdma_channel_t *channel; /* DBDMA channel */ 62 bus_dma_tag_t tag; /* bus_dma tag */ 63 struct pcm_channel *pcm; /* PCM channel */ 64 struct snd_dbuf *buf; /* PCM buffer */ 65 u_int slots; /* # of slots */ 66 u_int slot; /* current slot */ 67 u_int bufsz; /* buffer size */ 68 u_int blksz; /* block size */ 69 int running; 70 }; 71 72 static void 73 aoa_dma_set_program(struct aoa_dma *dma) 74 { 75 u_int32_t addr; 76 int i; 77 78 addr = (u_int32_t) sndbuf_getbufaddr(dma->buf); 79 KASSERT(dma->bufsz == sndbuf_getsize(dma->buf), ("bad size")); 80 81 dma->slots = dma->bufsz / dma->blksz; 82 83 for (i = 0; i < dma->slots; ++i) { 84 dbdma_insert_command(dma->channel, 85 i, /* slot */ 86 DBDMA_OUTPUT_MORE, /* command */ 87 0, /* stream */ 88 addr, /* data */ 89 dma->blksz, /* count */ 90 DBDMA_ALWAYS, /* interrupt */ 91 DBDMA_COND_TRUE, /* branch */ 92 DBDMA_NEVER, /* wait */ 93 dma->slots + 1 /* branch_slot */ 94 ); 95 96 addr += dma->blksz; 97 } 98 99 /* Branch back to beginning. */ 100 dbdma_insert_branch(dma->channel, dma->slots, 0); 101 102 /* STOP command to branch when S0 is asserted. */ 103 dbdma_insert_stop(dma->channel, dma->slots + 1); 104 105 /* Set S0 as the condition to branch to STOP. */ 106 dbdma_set_branch_selector(dma->channel, 1 << 0, 1 << 0); 107 dbdma_set_device_status(dma->channel, 1 << 0, 0); 108 109 dbdma_sync_commands(dma->channel, BUS_DMASYNC_PREWRITE); 110 } 111 112 #define AOA_BUFFER_SIZE 65536 113 114 static struct aoa_dma * 115 aoa_dma_create(struct aoa_softc *sc) 116 { 117 struct aoa_dma *dma; 118 bus_dma_tag_t tag; 119 int err; 120 device_t self; 121 122 self = sc->sc_dev; 123 err = bus_dma_tag_create(bus_get_dma_tag(self), 124 4, 0, BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, 125 AOA_BUFFER_SIZE, 1, AOA_BUFFER_SIZE, 0, NULL, NULL, &tag); 126 if (err != 0) 127 return (NULL); 128 129 dma = malloc(sizeof(*dma), M_DEVBUF, M_WAITOK | M_ZERO); 130 dma->tag = tag; 131 dma->bufsz = AOA_BUFFER_SIZE; 132 dma->blksz = PAGE_SIZE; /* initial blocksize */ 133 134 mtx_init(&dma->mutex, "AOA", NULL, MTX_DEF); 135 136 sc->sc_intrp = dma; 137 138 return (dma); 139 } 140 141 static void 142 aoa_dma_delete(struct aoa_dma *dma) 143 { 144 bus_dma_tag_destroy(dma->tag); 145 mtx_destroy(&dma->mutex); 146 free(dma, M_DEVBUF); 147 } 148 149 static u_int32_t 150 aoa_chan_setblocksize(kobj_t obj, void *data, u_int32_t blocksz) 151 { 152 struct aoa_dma *dma = data; 153 int err, lz; 154 155 DPRINTF(("aoa_chan_setblocksize: blocksz = %u, dma->blksz = %u\n", 156 blocksz, dma->blksz)); 157 KASSERT(!dma->running, ("dma is running")); 158 KASSERT(blocksz > 0, ("bad blocksz")); 159 160 /* Round blocksz down to a power of two... */ 161 __asm volatile ("cntlzw %0,%1" : "=r"(lz) : "r"(blocksz)); 162 blocksz = 1 << (31 - lz); 163 DPRINTF(("blocksz = %u\n", blocksz)); 164 165 /* ...but no more than the buffer. */ 166 if (blocksz > dma->bufsz) 167 blocksz = dma->bufsz; 168 169 err = sndbuf_resize(dma->buf, dma->bufsz / blocksz, blocksz); 170 if (err != 0) { 171 DPRINTF(("sndbuf_resize returned %d\n", err)); 172 return (0); 173 } 174 175 if (blocksz == dma->blksz) 176 return (dma->blksz); 177 178 /* One slot per block plus branch to 0 plus STOP. */ 179 err = dbdma_resize_channel(dma->channel, 2 + dma->bufsz / blocksz); 180 if (err != 0) { 181 DPRINTF(("dbdma_resize_channel returned %d\n", err)); 182 return (0); 183 } 184 185 /* Set the new blocksize. */ 186 dma->blksz = blocksz; 187 aoa_dma_set_program(dma); 188 189 return (dma->blksz); 190 } 191 192 static int 193 aoa_chan_setformat(kobj_t obj, void *data, u_int32_t format) 194 { 195 DPRINTF(("aoa_chan_setformat: format = %u\n", format)); 196 197 if (format != SND_FORMAT(AFMT_S16_BE, 2, 0)) 198 return (EINVAL); 199 200 return (0); 201 } 202 203 static u_int32_t 204 aoa_chan_setspeed(kobj_t obj, void *data, u_int32_t speed) 205 { 206 DPRINTF(("aoa_chan_setspeed: speed = %u\n", speed)); 207 208 return (44100); 209 } 210 211 static u_int32_t 212 aoa_chan_getptr(kobj_t obj, void *data) 213 { 214 struct aoa_dma *dma = data; 215 216 if (!dma->running) 217 return (0); 218 219 return (dma->slot * dma->blksz); 220 } 221 222 static void * 223 aoa_chan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, 224 struct pcm_channel *c, int dir) 225 { 226 struct aoa_softc *sc = devinfo; 227 struct aoa_dma *dma; 228 int max_slots, err; 229 230 KASSERT(dir == PCMDIR_PLAY, ("bad dir")); 231 232 dma = aoa_dma_create(sc); 233 if (!dma) 234 return (NULL); 235 dma->pcm = c; 236 dma->buf = b; 237 dma->reg = sc->sc_odma; 238 239 /* One slot per block, plus branch to 0 plus STOP. */ 240 max_slots = 2 + dma->bufsz / dma->blksz; 241 err = dbdma_allocate_channel(dma->reg, 0, bus_get_dma_tag(sc->sc_dev), 242 max_slots, &dma->channel ); 243 if (err != 0) { 244 aoa_dma_delete(dma); 245 return (NULL); 246 } 247 248 if (sndbuf_alloc(dma->buf, dma->tag, 0, dma->bufsz) != 0) { 249 dbdma_free_channel(dma->channel); 250 aoa_dma_delete(dma); 251 return (NULL); 252 } 253 254 aoa_dma_set_program(dma); 255 256 return (dma); 257 } 258 259 static int 260 aoa_chan_trigger(kobj_t obj, void *data, int go) 261 { 262 struct aoa_dma *dma = data; 263 int i; 264 265 switch (go) { 266 case PCMTRIG_START: 267 268 /* Start the DMA. */ 269 dma->running = 1; 270 271 dma->slot = 0; 272 dbdma_set_current_cmd(dma->channel, dma->slot); 273 274 dbdma_run(dma->channel); 275 276 return (0); 277 278 case PCMTRIG_STOP: 279 case PCMTRIG_ABORT: 280 281 mtx_lock(&dma->mutex); 282 283 dma->running = 0; 284 285 /* Make it branch to the STOP command. */ 286 dbdma_set_device_status(dma->channel, 1 << 0, 1 << 0); 287 288 /* XXX should wait for DBDMA_ACTIVE to clear. */ 289 DELAY(40000); 290 291 /* Reset the DMA. */ 292 dbdma_stop(dma->channel); 293 dbdma_set_device_status(dma->channel, 1 << 0, 0); 294 295 for (i = 0; i < dma->slots; ++i) 296 dbdma_clear_cmd_status(dma->channel, i); 297 298 mtx_unlock(&dma->mutex); 299 300 return (0); 301 } 302 303 return (0); 304 } 305 306 static int 307 aoa_chan_free(kobj_t obj, void *data) 308 { 309 struct aoa_dma *dma = data; 310 311 sndbuf_free(dma->buf); 312 dbdma_free_channel(dma->channel); 313 aoa_dma_delete(dma); 314 315 return (0); 316 } 317 318 void 319 aoa_interrupt(void *xsc) 320 { 321 struct aoa_softc *sc = xsc; 322 struct aoa_dma *dma; 323 324 if (!(dma = sc->sc_intrp) || !dma->running) 325 return; 326 327 mtx_lock(&dma->mutex); 328 329 while (dbdma_get_cmd_status(dma->channel, dma->slot)) { 330 dbdma_clear_cmd_status(dma->channel, dma->slot); 331 dma->slot = (dma->slot + 1) % dma->slots; 332 333 mtx_unlock(&dma->mutex); 334 chn_intr(dma->pcm); 335 mtx_lock(&dma->mutex); 336 } 337 338 mtx_unlock(&dma->mutex); 339 } 340 341 static u_int32_t sc_fmt[] = { 342 SND_FORMAT(AFMT_S16_BE, 2, 0), 343 0 344 }; 345 static struct pcmchan_caps aoa_caps = {44100, 44100, sc_fmt, 0}; 346 347 static struct pcmchan_caps * 348 aoa_chan_getcaps(kobj_t obj, void *data) 349 { 350 return (&aoa_caps); 351 } 352 353 static kobj_method_t aoa_chan_methods[] = { 354 KOBJMETHOD(channel_init, aoa_chan_init), 355 KOBJMETHOD(channel_free, aoa_chan_free), 356 KOBJMETHOD(channel_setformat, aoa_chan_setformat), 357 KOBJMETHOD(channel_setspeed, aoa_chan_setspeed), 358 KOBJMETHOD(channel_setblocksize,aoa_chan_setblocksize), 359 KOBJMETHOD(channel_trigger, aoa_chan_trigger), 360 KOBJMETHOD(channel_getptr, aoa_chan_getptr), 361 KOBJMETHOD(channel_getcaps, aoa_chan_getcaps), 362 KOBJMETHOD_END 363 }; 364 CHANNEL_DECLARE(aoa_chan); 365 366 int 367 aoa_attach(void *xsc) 368 { 369 char status[SND_STATUSLEN]; 370 struct aoa_softc *sc; 371 device_t self; 372 int err __unused; 373 374 sc = xsc; 375 self = sc->sc_dev; 376 377 if (pcm_register(self, sc, 1, 0)) 378 return (ENXIO); 379 380 err = pcm_getbuffersize(self, AOA_BUFFER_SIZE, AOA_BUFFER_SIZE, 381 AOA_BUFFER_SIZE); 382 DPRINTF(("pcm_getbuffersize returned %d\n", err)); 383 384 pcm_addchan(self, PCMDIR_PLAY, &aoa_chan_class, sc); 385 386 snprintf(status, sizeof(status), "at %s", ofw_bus_get_name(self)); 387 pcm_setstatus(self, status); 388 389 return (0); 390 } 391