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