Annotation of sys/arch/macppc/dev/xlights.c, Revision 1.1
1.1 ! nbrk 1: /* $OpenBSD: xlights.c,v 1.3 2007/06/01 08:29:30 gwk Exp $ */
! 2: /*
! 3: * Copyright (c) 2007 Gordon Willem Klok <gwk@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: #include <sys/types.h>
! 18: #include <sys/param.h>
! 19: #include <sys/systm.h>
! 20: #include <sys/proc.h>
! 21: #include <sys/device.h>
! 22: #include <sys/kthread.h>
! 23: #include <sys/timeout.h>
! 24: #include <dev/ofw/openfirm.h>
! 25:
! 26: #include <machine/bus.h>
! 27: #include <machine/autoconf.h>
! 28: #include <macppc/dev/dbdma.h>
! 29: #include <macppc/dev/i2sreg.h>
! 30:
! 31: struct xlights_softc {
! 32: struct device sc_dev;
! 33: int sc_node;
! 34: int sc_intr;
! 35: uint32_t sc_freq;
! 36: int sc_dmasts;
! 37:
! 38: u_char *sc_reg;
! 39: dbdma_regmap_t *sc_dma;
! 40: bus_dma_tag_t sc_dmat;
! 41: bus_dmamap_t sc_bufmap;
! 42: bus_dma_segment_t sc_bufseg[1];
! 43: dbdma_t sc_dbdma;
! 44: dbdma_command_t *sc_dmacmd;
! 45: uint32_t *sc_buf;
! 46: uint32_t *sc_bufpos;
! 47:
! 48: struct timeout sc_tmo;
! 49: };
! 50:
! 51: int xlights_match(struct device *, void *, void *);
! 52: void xlights_attach(struct device *, struct device *, void *);
! 53: int xlights_intr(void *);
! 54: void xlights_startdma(struct xlights_softc *);
! 55: void xlights_deferred(void *);
! 56: void xlights_theosDOT(void *);
! 57: void xlights_timeout(void *);
! 58: void xlights_pwm(struct xlights_softc *, u_char *, int);
! 59: extern void keylargo_fcr_enable(int, u_int32_t);
! 60: extern void keylargo_fcr_disable(int, u_int32_t);
! 61:
! 62: struct cfattach xlights_ca = {
! 63: sizeof(struct xlights_softc), xlights_match,
! 64: xlights_attach
! 65: };
! 66:
! 67: struct cfdriver xlights_cd = {
! 68: NULL, "xlights", DV_DULL
! 69: };
! 70:
! 71: #define BL_BUFSZ PAGE_SIZE
! 72: #define BL_DBDMA_CMDS 2
! 73:
! 74: int
! 75: xlights_match(struct device *parent, void *arg, void *aux)
! 76: {
! 77: struct confargs *ca = aux;
! 78: int soundbus, soundchip, error;
! 79: char compat[32];
! 80:
! 81: if (strcmp(ca->ca_name, "i2s") != 0)
! 82: return 0;
! 83: if ((soundbus = OF_child(ca->ca_node)) == 0)
! 84: return 0;
! 85: if ((soundchip = OF_child(soundbus)) == 0)
! 86: return 0;
! 87:
! 88: error = OF_getprop(soundchip, "virtual", compat, sizeof(compat));
! 89: if (error == -1) {
! 90: error = OF_getprop(soundchip, "name", compat,
! 91: sizeof(compat));
! 92:
! 93: if (error == -1 || (strcmp(compat, "lightshow")) != 0)
! 94: return 0;
! 95: }
! 96:
! 97: /* we require at least 4 registers */
! 98: if (ca->ca_nreg / sizeof(int) < 4)
! 99: return 0;
! 100: /* we require at least 3 interrupts */
! 101: if (ca->ca_nintr / sizeof(int) < 6)
! 102: return 0;
! 103:
! 104: return 1;
! 105: }
! 106:
! 107: void
! 108: xlights_attach(struct device *parent, struct device *self, void *aux)
! 109: {
! 110: struct xlights_softc *sc = (struct xlights_softc *)self;
! 111: struct confargs *ca = aux;
! 112: int nseg, error, intr[6];
! 113: u_int32_t reg[4];
! 114:
! 115: sc->sc_node = OF_child(ca->ca_node);
! 116:
! 117: OF_getprop(sc->sc_node, "reg", reg, sizeof(reg));
! 118: ca->ca_reg[0] += ca->ca_baseaddr;
! 119: ca->ca_reg[2] += ca->ca_baseaddr;
! 120:
! 121: if ((sc->sc_reg = mapiodev(ca->ca_reg[0], ca->ca_reg[1])) == NULL) {
! 122: printf(": cannot map registers\n");
! 123: return;
! 124: }
! 125: sc->sc_dmat = ca->ca_dmat;
! 126:
! 127: if ((sc->sc_dma = mapiodev(ca->ca_reg[2], ca->ca_reg[3])) == NULL) {
! 128: printf(": cannot map DMA registers\n");
! 129: goto nodma;
! 130: }
! 131:
! 132: if ((sc->sc_dbdma = dbdma_alloc(sc->sc_dmat, BL_DBDMA_CMDS)) == NULL) {
! 133: printf(": cannot alloc DMA descriptors\n");
! 134: goto nodbdma;
! 135: }
! 136: sc->sc_dmacmd = sc->sc_dbdma->d_addr;
! 137:
! 138: if ((error = bus_dmamem_alloc(sc->sc_dmat, BL_BUFSZ, 0, 0,
! 139: sc->sc_bufseg, 1, &nseg, BUS_DMA_NOWAIT))) {
! 140: printf(": cannot allocate DMA mem (%d)\n", error);
! 141: goto nodmamem;
! 142: }
! 143:
! 144: if ((error = bus_dmamem_map(sc->sc_dmat, sc->sc_bufseg, nseg,
! 145: BL_BUFSZ, (caddr_t *)&sc->sc_buf, BUS_DMA_NOWAIT))) {
! 146: printf(": cannot map DMA mem (%d)\n", error);
! 147: goto nodmamap;
! 148: }
! 149: sc->sc_bufpos = sc->sc_buf;
! 150:
! 151: if ((error = bus_dmamap_create(sc->sc_dmat, BL_BUFSZ, 1, BL_BUFSZ, 0,
! 152: BUS_DMA_NOWAIT | BUS_DMA_ALLOCNOW, &sc->sc_bufmap))) {
! 153: printf(": cannot create DMA map (%d)\n", error);
! 154: goto nodmacreate;
! 155: }
! 156:
! 157: if ((error = bus_dmamap_load(sc->sc_dmat, sc->sc_bufmap, sc->sc_buf,
! 158: BL_BUFSZ, NULL, BUS_DMA_NOWAIT))) {
! 159: printf(": cannot load DMA map (%d)\n", error);
! 160: goto nodmaload;
! 161: }
! 162: /* XXX: Should probably extract this from the clock data
! 163: * property of the soundchip node */
! 164: sc->sc_freq = 44100;
! 165:
! 166: OF_getprop(sc->sc_node, "interrupts", intr, sizeof(intr));
! 167: sc->sc_intr = intr[2];
! 168: printf(": irq %d\n", sc->sc_intr);
! 169:
! 170: keylargo_fcr_enable(I2SClockOffset, I2S0EN);
! 171: out32rb(sc->sc_reg + I2S_INT, I2S_INT_CLKSTOPPEND);
! 172: keylargo_fcr_disable(I2SClockOffset, I2S0CLKEN);
! 173: for (error = 0; error < 1000; error++) {
! 174: if (in32rb(sc->sc_reg + I2S_INT) & I2S_INT_CLKSTOPPEND) {
! 175: error = 0;
! 176: break;
! 177: }
! 178: delay(1);
! 179: }
! 180: if (error) {
! 181: printf("%s: i2s timeout\n", sc->sc_dev.dv_xname);
! 182: goto nodmaload;
! 183: }
! 184:
! 185: mac_intr_establish(parent, sc->sc_intr, intr[3] ? IST_LEVEL :
! 186: IST_EDGE, IPL_AUDIO, xlights_intr, sc, sc->sc_dev.dv_xname);
! 187:
! 188: out32rb(sc->sc_reg + I2S_FORMAT, CLKSRC_VS);
! 189: keylargo_fcr_enable(I2SClockOffset, I2S0CLKEN);
! 190:
! 191: kthread_create_deferred(xlights_deferred, sc);
! 192: timeout_set(&sc->sc_tmo, xlights_timeout, sc);
! 193: return;
! 194: nodmaload:
! 195: bus_dmamap_destroy(sc->sc_dmat, sc->sc_bufmap);
! 196: nodmacreate:
! 197: bus_dmamem_unmap(sc->sc_dmat, (caddr_t)sc->sc_buf, BL_BUFSZ);
! 198: nodmamap:
! 199: bus_dmamem_free(sc->sc_dmat, sc->sc_bufseg, nseg);
! 200: nodmamem:
! 201: dbdma_free(sc->sc_dbdma);
! 202: nodbdma:
! 203: unmapiodev((void *)sc->sc_dma, ca->ca_reg[3]);
! 204: nodma:
! 205: unmapiodev(sc->sc_reg, ca->ca_reg[1]);
! 206: }
! 207:
! 208: void
! 209: xlights_deferred(void *v)
! 210: {
! 211: kthread_create(xlights_theosDOT, v, NULL, "xlights");
! 212: }
! 213:
! 214: void
! 215: xlights_theosDOT(void *v)
! 216: {
! 217: struct xlights_softc *sc = (struct xlights_softc *)v;
! 218: u_char leds[16];
! 219: int offset, i, s, speed;
! 220:
! 221: while (1) {
! 222: speed = (averunnable.ldavg[0] / 100) / 2;
! 223: for (offset = 8; offset < 16; offset++) {
! 224: s = offset - 2;
! 225: if (s < 8) {
! 226: for (i = 1; i <= (8 - s); i++)
! 227: leds[i - 1] = 0x7f / i;
! 228: s += 8 - s;
! 229: }
! 230: for (i = offset; i >= s; i--)
! 231: leds[i] = 0xff / (offset + 1 - s);
! 232:
! 233: xlights_pwm(sc, leds, speed);
! 234: bzero(leds, sizeof(leds));
! 235: }
! 236: for (offset = 7; offset >= 0; offset--) {
! 237: s = offset + 2;
! 238: if (s > 7) {
! 239: for (i = 1; i <= (s - 7); i++)
! 240: leds[15 - (i - 1)] = (0xff / (s - 7)) / i;
! 241: s-= s - 7;
! 242: }
! 243: for (i = offset; i <= s; i++)
! 244: leds[i] = 0xff / (offset + 1) - s;
! 245: xlights_pwm(sc, leds, speed);
! 246: bzero(leds, sizeof(leds));
! 247: }
! 248: }
! 249: }
! 250:
! 251: void
! 252: xlights_pwm(struct xlights_softc *sc, u_char *leds, int msecs)
! 253: {
! 254: uint32_t *p;
! 255: int s, l, k, nsamp;
! 256: uint32_t freq[16] = {0};
! 257:
! 258: p = sc->sc_bufpos;
! 259:
! 260: nsamp = sc->sc_freq / 1000 * msecs;
! 261:
! 262: for (k = 1; k < nsamp;) {
! 263: if (p - sc->sc_buf < BL_BUFSZ / sizeof(uint32_t)) {
! 264: s = 0;
! 265: for (l = 0; l < 16; l++) {
! 266: s >>= 1;
! 267: if (leds[l] &&
! 268: ((k - freq[l]) == (0xff / leds[l]))) {
! 269: s |= 1 << 15;
! 270: freq[l] = k;
! 271: }
! 272: }
! 273: *p = (s << 17) | (s >> 15);
! 274: p++;
! 275: k++;
! 276: sc->sc_bufpos++;
! 277: } else {
! 278: xlights_startdma(sc);
! 279: while (sc->sc_dmasts)
! 280: tsleep(sc->sc_buf, PWAIT, "blinken", 0);
! 281: sc->sc_bufpos = p = sc->sc_buf;
! 282: }
! 283: }
! 284: }
! 285:
! 286: void
! 287: xlights_startdma(struct xlights_softc *sc)
! 288: {
! 289: dbdma_command_t *cmdp = sc->sc_dmacmd;
! 290:
! 291: sc->sc_dmasts = 1;
! 292: timeout_add(&sc->sc_tmo, 250);
! 293:
! 294: DBDMA_BUILD(cmdp, DBDMA_CMD_OUT_LAST, 0,
! 295: sc->sc_bufmap->dm_segs[0].ds_len,
! 296: sc->sc_bufmap->dm_segs[0].ds_addr, DBDMA_INT_ALWAYS,
! 297: DBDMA_WAIT_NEVER, DBDMA_BRANCH_NEVER);
! 298: cmdp++;
! 299:
! 300: DBDMA_BUILD(cmdp, DBDMA_CMD_STOP, 0, 0, 0, DBDMA_INT_NEVER,
! 301: DBDMA_WAIT_NEVER, DBDMA_BRANCH_NEVER);
! 302:
! 303: dbdma_start(sc->sc_dma, sc->sc_dbdma);
! 304: }
! 305:
! 306: void
! 307: xlights_timeout(void *v)
! 308: {
! 309: struct xlights_softc *sc = (struct xlights_softc *)v;
! 310:
! 311: dbdma_reset(sc->sc_dma);
! 312: timeout_del(&sc->sc_tmo);
! 313: sc->sc_dmasts = 0;
! 314: wakeup(sc->sc_buf);
! 315: }
! 316:
! 317: int
! 318: xlights_intr(void *v)
! 319: {
! 320: struct xlights_softc *sc = (struct xlights_softc *)v;
! 321: int status;
! 322: dbdma_command_t *cmd;
! 323:
! 324: cmd = sc->sc_dmacmd;
! 325: status = dbdma_ld16(&cmd->d_status);
! 326: if (sc->sc_dmasts) {
! 327: sc->sc_dmasts = 0;
! 328: timeout_del(&sc->sc_tmo);
! 329: wakeup(sc->sc_buf);
! 330: return (1);
! 331: }
! 332: return (0);
! 333: }
CVSweb