Annotation of sys/dev/pci/sv.c, Revision 1.1
1.1 ! nbrk 1: /* $OpenBSD: sv.c,v 1.21 2005/09/11 18:17:08 mickey Exp $ */
! 2:
! 3: /*
! 4: * Copyright (c) 1998 Constantine Paul Sapuntzakis
! 5: * All rights reserved
! 6: *
! 7: * Author: Constantine Paul Sapuntzakis (csapuntz@cvs.openbsd.org)
! 8: *
! 9: * Redistribution and use in source and binary forms, with or without
! 10: * modification, are permitted provided that the following conditions
! 11: * are met:
! 12: * 1. Redistributions of source code must retain the above copyright
! 13: * notice, this list of conditions and the following disclaimer.
! 14: * 2. Redistributions in binary form must reproduce the above copyright
! 15: * notice, this list of conditions and the following disclaimer in the
! 16: * documentation and/or other materials provided with the distribution.
! 17: * 3. The author's name or those of the contributors may be used to
! 18: * endorse or promote products derived from this software without
! 19: * specific prior written permission.
! 20: *
! 21: * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) AND CONTRIBUTORS
! 22: * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
! 23: * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
! 24: * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
! 25: * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
! 26: * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
! 27: * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
! 28: * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
! 29: * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
! 30: * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
! 31: * POSSIBILITY OF SUCH DAMAGE.
! 32: */
! 33:
! 34: /*
! 35: * S3 SonicVibes driver
! 36: * Heavily based on the eap driver by Lennart Augustsson
! 37: */
! 38:
! 39: #include <sys/param.h>
! 40: #include <sys/systm.h>
! 41: #include <sys/kernel.h>
! 42: #include <sys/malloc.h>
! 43: #include <sys/device.h>
! 44:
! 45: #include <dev/pci/pcireg.h>
! 46: #include <dev/pci/pcivar.h>
! 47: #include <dev/pci/pcidevs.h>
! 48:
! 49: #include <sys/audioio.h>
! 50: #include <dev/audio_if.h>
! 51: #include <dev/mulaw.h>
! 52: #include <dev/auconv.h>
! 53:
! 54: #include <dev/ic/i8237reg.h>
! 55: #include <dev/ic/s3_617.h>
! 56:
! 57:
! 58: #include <machine/bus.h>
! 59:
! 60: #ifdef __OpenBSD__
! 61: struct cfdriver sv_cd = {
! 62: NULL, "sv", DV_DULL
! 63: };
! 64: #endif
! 65:
! 66: #ifdef AUDIO_DEBUG
! 67: #define DPRINTF(x) if (svdebug) printf x
! 68: #define DPRINTFN(n,x) if (svdebug>(n)) printf x
! 69: static int svdebug = 100;
! 70: #else
! 71: #define DPRINTF(x)
! 72: #define DPRINTFN(n,x)
! 73: #endif
! 74:
! 75: int sv_match(struct device *, void *, void *);
! 76: static void sv_attach(struct device *, struct device *, void *);
! 77: int sv_intr(void *);
! 78:
! 79: struct sv_dma {
! 80: bus_dmamap_t map;
! 81: caddr_t addr;
! 82: bus_dma_segment_t segs[1];
! 83: int nsegs;
! 84: size_t size;
! 85: struct sv_dma *next;
! 86: };
! 87: #define DMAADDR(map) ((map)->segs[0].ds_addr)
! 88: #define KERNADDR(map) ((void *)((map)->addr))
! 89:
! 90: enum {
! 91: SV_DMAA_CONFIGURED = 1,
! 92: SV_DMAC_CONFIGURED = 2,
! 93: SV_DMAA_TRIED_CONFIGURE = 4,
! 94: SV_DMAC_TRIED_CONFIGURE = 8
! 95: };
! 96:
! 97: struct sv_softc {
! 98: struct device sc_dev; /* base device */
! 99: void *sc_ih; /* interrupt vectoring */
! 100:
! 101: pci_chipset_tag_t sc_pci_chipset_tag;
! 102: pcitag_t sc_pci_tag;
! 103:
! 104: bus_space_tag_t sc_iot;
! 105: bus_space_handle_t sc_ioh;
! 106: bus_space_handle_t sc_dmaa_ioh;
! 107: bus_space_handle_t sc_dmac_ioh;
! 108: bus_dma_tag_t sc_dmatag; /* DMA tag */
! 109:
! 110: struct sv_dma *sc_dmas;
! 111:
! 112: void (*sc_pintr)(void *); /* dma completion intr handler */
! 113: void *sc_parg; /* arg for sc_intr() */
! 114:
! 115: void (*sc_rintr)(void *); /* dma completion intr handler */
! 116: void *sc_rarg; /* arg for sc_intr() */
! 117: char sc_enable;
! 118: char sc_trd;
! 119:
! 120: char sc_dma_configured;
! 121: u_int sc_record_source; /* recording source mask */
! 122: };
! 123:
! 124:
! 125: struct cfattach sv_ca = {
! 126: sizeof(struct sv_softc), sv_match, sv_attach
! 127: };
! 128:
! 129: struct audio_device sv_device = {
! 130: "S3 SonicVibes",
! 131: "",
! 132: "sv"
! 133: };
! 134:
! 135: #define ARRAY_SIZE(foo) ((sizeof(foo)) / sizeof(foo[0]))
! 136:
! 137: int sv_allocmem(struct sv_softc *, size_t, size_t, struct sv_dma *);
! 138: int sv_freemem(struct sv_softc *, struct sv_dma *);
! 139:
! 140: int sv_open(void *, int);
! 141: void sv_close(void *);
! 142: int sv_query_encoding(void *, struct audio_encoding *);
! 143: int sv_set_params(void *, int, int, struct audio_params *, struct audio_params *);
! 144: int sv_round_blocksize(void *, int);
! 145: int sv_dma_init_output(void *, void *, int);
! 146: int sv_dma_init_input(void *, void *, int);
! 147: int sv_dma_output(void *, void *, int, void (*)(void *), void *);
! 148: int sv_dma_input(void *, void *, int, void (*)(void *), void *);
! 149: int sv_halt_in_dma(void *);
! 150: int sv_halt_out_dma(void *);
! 151: int sv_getdev(void *, struct audio_device *);
! 152: int sv_mixer_set_port(void *, mixer_ctrl_t *);
! 153: int sv_mixer_get_port(void *, mixer_ctrl_t *);
! 154: int sv_query_devinfo(void *, mixer_devinfo_t *);
! 155: void *sv_malloc(void *, int, size_t, int, int);
! 156: void sv_free(void *, void *, int);
! 157: paddr_t sv_mappage(void *, void *, off_t, int);
! 158: int sv_get_props(void *);
! 159:
! 160: void sv_dumpregs(struct sv_softc *sc);
! 161:
! 162: struct audio_hw_if sv_hw_if = {
! 163: sv_open,
! 164: sv_close,
! 165: NULL,
! 166: sv_query_encoding,
! 167: sv_set_params,
! 168: sv_round_blocksize,
! 169: NULL,
! 170: sv_dma_init_output,
! 171: sv_dma_init_input,
! 172: sv_dma_output,
! 173: sv_dma_input,
! 174: sv_halt_out_dma,
! 175: sv_halt_in_dma,
! 176: NULL,
! 177: sv_getdev,
! 178: NULL,
! 179: sv_mixer_set_port,
! 180: sv_mixer_get_port,
! 181: sv_query_devinfo,
! 182: sv_malloc,
! 183: sv_free,
! 184: NULL,
! 185: sv_mappage,
! 186: sv_get_props,
! 187: NULL,
! 188: NULL
! 189: };
! 190:
! 191:
! 192: static __inline__ u_int8_t sv_read(struct sv_softc *, u_int8_t);
! 193: static __inline__ u_int8_t sv_read_indirect(struct sv_softc *, u_int8_t);
! 194: static __inline__ void sv_write(struct sv_softc *, u_int8_t, u_int8_t );
! 195: static __inline__ void sv_write_indirect(struct sv_softc *, u_int8_t, u_int8_t );
! 196: static void sv_init_mixer(struct sv_softc *);
! 197:
! 198: static __inline__ void
! 199: sv_write (sc, reg, val)
! 200: struct sv_softc *sc;
! 201: u_int8_t reg, val;
! 202:
! 203: {
! 204: bus_space_write_1(sc->sc_iot, sc->sc_ioh, reg, val);
! 205: }
! 206:
! 207: static __inline__ u_int8_t
! 208: sv_read (sc, reg)
! 209: struct sv_softc *sc;
! 210: u_int8_t reg;
! 211:
! 212: {
! 213: return (bus_space_read_1(sc->sc_iot, sc->sc_ioh, reg));
! 214: }
! 215:
! 216: static __inline__ u_int8_t
! 217: sv_read_indirect (sc, reg)
! 218: struct sv_softc *sc;
! 219: u_int8_t reg;
! 220: {
! 221: u_int8_t iaddr = 0;
! 222:
! 223: if (sc->sc_trd > 0)
! 224: iaddr |= SV_IADDR_TRD;
! 225:
! 226: iaddr |= (reg & SV_IADDR_MASK);
! 227: sv_write (sc, SV_CODEC_IADDR, iaddr);
! 228:
! 229: return (sv_read(sc, SV_CODEC_IDATA));
! 230: }
! 231:
! 232: static __inline__ void
! 233: sv_write_indirect (sc, reg, val)
! 234: struct sv_softc *sc;
! 235: u_int8_t reg, val;
! 236: {
! 237: u_int8_t iaddr = 0;
! 238: #ifdef DIAGNOSTIC
! 239: if (reg > 0x3f) {
! 240: printf ("Invalid register\n");
! 241: return;
! 242: }
! 243: #endif
! 244:
! 245: if (reg == SV_DMA_DATA_FORMAT)
! 246: iaddr |= SV_IADDR_MCE;
! 247:
! 248: if (sc->sc_trd > 0)
! 249: iaddr |= SV_IADDR_TRD;
! 250:
! 251: iaddr |= (reg & SV_IADDR_MASK);
! 252: sv_write (sc, SV_CODEC_IADDR, iaddr);
! 253: sv_write (sc, SV_CODEC_IDATA, val);
! 254: }
! 255:
! 256: int
! 257: sv_match(parent, match, aux)
! 258: struct device *parent;
! 259: void *match, *aux;
! 260: {
! 261: struct pci_attach_args *pa = aux;
! 262:
! 263: if (PCI_VENDOR(pa->pa_id) == PCI_VENDOR_S3 &&
! 264: PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_S3_SONICVIBES)
! 265: return (1);
! 266:
! 267: return (0);
! 268: }
! 269:
! 270: static void
! 271: sv_attach(parent, self, aux)
! 272: struct device *parent, *self;
! 273: void *aux;
! 274:
! 275: {
! 276: struct sv_softc *sc = (struct sv_softc *)self;
! 277: struct pci_attach_args *pa = aux;
! 278: pci_chipset_tag_t pc = pa->pa_pc;
! 279: pci_intr_handle_t ih;
! 280: bus_size_t iosize;
! 281: char const *intrstr;
! 282: u_int32_t dmareg, dmaio;
! 283: u_int8_t reg;
! 284:
! 285: sc->sc_pci_chipset_tag = pc;
! 286: sc->sc_pci_tag = pa->pa_tag;
! 287:
! 288: /* Map the enhanced port only */
! 289: if (pci_mapreg_map(pa, SV_ENHANCED_PORTBASE_SLOT, PCI_MAPREG_TYPE_IO, 0,
! 290: &sc->sc_iot, &sc->sc_ioh, NULL, &iosize, 0)) {
! 291: printf (": Couldn't map enhanced synth I/O range\n");
! 292: return;
! 293: }
! 294:
! 295: sc->sc_dmatag = pa->pa_dmat;
! 296:
! 297: dmareg = pci_conf_read(pa->pa_pc, pa->pa_tag, SV_DMAA_CONFIG_OFF);
! 298: iosize = 0x10;
! 299: dmaio = dmareg & ~(iosize - 1);
! 300:
! 301: if (dmaio) {
! 302: dmareg &= 0xF;
! 303:
! 304: if (bus_space_map(sc->sc_iot, dmaio, iosize, 0, &sc->sc_dmaa_ioh)) {
! 305: /* The BIOS assigned us some bad I/O address! Make sure to clear
! 306: and disable this DMA before we enable the device */
! 307: pci_conf_write(pa->pa_pc, pa->pa_tag, SV_DMAA_CONFIG_OFF, 0);
! 308:
! 309: printf (": can't map DMA i/o space\n");
! 310: goto enable;
! 311: }
! 312:
! 313: pci_conf_write(pa->pa_pc, pa->pa_tag, SV_DMAA_CONFIG_OFF,
! 314: dmaio | dmareg |
! 315: SV_DMA_CHANNEL_ENABLE | SV_DMAA_EXTENDED_ADDR);
! 316: sc->sc_dma_configured |= SV_DMAA_CONFIGURED;
! 317: }
! 318:
! 319: dmareg = pci_conf_read(pa->pa_pc, pa->pa_tag, SV_DMAC_CONFIG_OFF);
! 320: dmaio = dmareg & ~(iosize - 1);
! 321: if (dmaio) {
! 322: dmareg &= 0xF;
! 323:
! 324: if (bus_space_map(sc->sc_iot, dmaio, iosize, 0, &sc->sc_dmac_ioh)) {
! 325: /* The BIOS assigned us some bad I/O address! Make sure to clear
! 326: and disable this DMA before we enable the device */
! 327: pci_conf_write (pa->pa_pc, pa->pa_tag, SV_DMAC_CONFIG_OFF,
! 328: dmareg & ~SV_DMA_CHANNEL_ENABLE);
! 329: printf (": can't map DMA i/o space\n");
! 330: goto enable;
! 331: }
! 332:
! 333: pci_conf_write(pa->pa_pc, pa->pa_tag, SV_DMAC_CONFIG_OFF,
! 334: dmaio | dmareg | SV_DMA_CHANNEL_ENABLE);
! 335: sc->sc_dma_configured |= SV_DMAC_CONFIGURED;
! 336: }
! 337:
! 338: /* Enable the device. */
! 339: enable:
! 340: sv_write_indirect(sc, SV_ANALOG_POWER_DOWN_CONTROL, 0);
! 341: sv_write_indirect(sc, SV_DIGITAL_POWER_DOWN_CONTROL, 0);
! 342:
! 343: /* initialize codec registers */
! 344: reg = sv_read(sc, SV_CODEC_CONTROL);
! 345: reg |= SV_CTL_RESET;
! 346: sv_write(sc, SV_CODEC_CONTROL, reg);
! 347: delay(50);
! 348:
! 349: reg = sv_read(sc, SV_CODEC_CONTROL);
! 350: reg &= ~SV_CTL_RESET;
! 351: reg |= SV_CTL_INTA | SV_CTL_ENHANCED;
! 352:
! 353: /* This write clears the reset */
! 354: sv_write(sc, SV_CODEC_CONTROL, reg);
! 355: delay(50);
! 356:
! 357: /* This write actually shoves the new values in */
! 358: sv_write(sc, SV_CODEC_CONTROL, reg);
! 359:
! 360: DPRINTF (("reg: %x\n", sv_read(sc, SV_CODEC_CONTROL)));
! 361:
! 362: /* Enable DMA interrupts */
! 363: reg = sv_read(sc, SV_CODEC_INTMASK);
! 364: reg &= ~(SV_INTMASK_DMAA | SV_INTMASK_DMAC);
! 365: reg |= SV_INTMASK_UD | SV_INTMASK_SINT | SV_INTMASK_MIDI;
! 366: sv_write(sc, SV_CODEC_INTMASK, reg);
! 367:
! 368: sv_read(sc, SV_CODEC_STATUS);
! 369:
! 370: sc->sc_trd = 0;
! 371: sc->sc_enable = 0;
! 372:
! 373: /* Map and establish the interrupt. */
! 374: if (pci_intr_map(pa, &ih)) {
! 375: printf(": couldn't map interrupt\n");
! 376: return;
! 377: }
! 378: intrstr = pci_intr_string(pc, ih);
! 379: sc->sc_ih = pci_intr_establish(pc, ih, IPL_AUDIO, sv_intr, sc,
! 380: sc->sc_dev.dv_xname);
! 381: if (sc->sc_ih == NULL) {
! 382: printf(": couldn't establish interrupt");
! 383: if (intrstr != NULL)
! 384: printf(" at %s", intrstr);
! 385: printf("\n");
! 386: return;
! 387: }
! 388: printf(": %s\n", intrstr);
! 389:
! 390: sv_init_mixer(sc);
! 391:
! 392: audio_attach_mi(&sv_hw_if, sc, &sc->sc_dev);
! 393: }
! 394:
! 395: #ifdef AUDIO_DEBUG
! 396: void
! 397: sv_dumpregs(sc)
! 398: struct sv_softc *sc;
! 399: {
! 400: int idx;
! 401:
! 402: { int idx;
! 403: for (idx = 0; idx < 0x50; idx += 4) {
! 404: printf ("%02x = %x\n", idx, pci_conf_read(sc->sc_pci_chipset_tag,
! 405: sc->sc_pci_tag, idx));
! 406: }
! 407: }
! 408:
! 409: for (idx = 0; idx < 6; idx++) {
! 410: printf ("REG %02x = %02x\n", idx, sv_read(sc, idx));
! 411: }
! 412:
! 413: for (idx = 0; idx < 0x32; idx++) {
! 414: printf ("IREG %02x = %02x\n", idx, sv_read_indirect(sc, idx));
! 415: }
! 416:
! 417: for (idx = 0; idx < 0x10; idx++) {
! 418: printf ("DMA %02x = %02x\n", idx,
! 419: bus_space_read_1(sc->sc_iot, sc->sc_dmaa_ioh, idx));
! 420: }
! 421:
! 422: return;
! 423: }
! 424: #endif
! 425:
! 426: int
! 427: sv_intr(p)
! 428: void *p;
! 429: {
! 430: struct sv_softc *sc = p;
! 431: u_int8_t intr;
! 432:
! 433: intr = sv_read(sc, SV_CODEC_STATUS);
! 434:
! 435: if (!(intr & (SV_INTSTATUS_DMAA | SV_INTSTATUS_DMAC)))
! 436: return (0);
! 437:
! 438: if (intr & SV_INTSTATUS_DMAA) {
! 439: if (sc->sc_pintr)
! 440: sc->sc_pintr(sc->sc_parg);
! 441: }
! 442:
! 443: if (intr & SV_INTSTATUS_DMAC) {
! 444: if (sc->sc_rintr)
! 445: sc->sc_rintr(sc->sc_rarg);
! 446: }
! 447:
! 448: return (1);
! 449: }
! 450:
! 451: int
! 452: sv_allocmem(sc, size, align, p)
! 453: struct sv_softc *sc;
! 454: size_t size;
! 455: size_t align;
! 456: struct sv_dma *p;
! 457: {
! 458: int error;
! 459:
! 460: p->size = size;
! 461: error = bus_dmamem_alloc(sc->sc_dmatag, p->size, align, 0,
! 462: p->segs, ARRAY_SIZE(p->segs),
! 463: &p->nsegs, BUS_DMA_NOWAIT);
! 464: if (error)
! 465: return (error);
! 466:
! 467: error = bus_dmamem_map(sc->sc_dmatag, p->segs, p->nsegs, p->size,
! 468: &p->addr, BUS_DMA_NOWAIT|BUS_DMA_COHERENT);
! 469: if (error)
! 470: goto free;
! 471:
! 472: error = bus_dmamap_create(sc->sc_dmatag, p->size, 1, p->size,
! 473: 0, BUS_DMA_NOWAIT, &p->map);
! 474: if (error)
! 475: goto unmap;
! 476:
! 477: error = bus_dmamap_load(sc->sc_dmatag, p->map, p->addr, p->size, NULL,
! 478: BUS_DMA_NOWAIT);
! 479: if (error)
! 480: goto destroy;
! 481: return (0);
! 482:
! 483: destroy:
! 484: bus_dmamap_destroy(sc->sc_dmatag, p->map);
! 485: unmap:
! 486: bus_dmamem_unmap(sc->sc_dmatag, p->addr, p->size);
! 487: free:
! 488: bus_dmamem_free(sc->sc_dmatag, p->segs, p->nsegs);
! 489: return (error);
! 490: }
! 491:
! 492: int
! 493: sv_freemem(sc, p)
! 494: struct sv_softc *sc;
! 495: struct sv_dma *p;
! 496: {
! 497: bus_dmamap_unload(sc->sc_dmatag, p->map);
! 498: bus_dmamap_destroy(sc->sc_dmatag, p->map);
! 499: bus_dmamem_unmap(sc->sc_dmatag, p->addr, p->size);
! 500: bus_dmamem_free(sc->sc_dmatag, p->segs, p->nsegs);
! 501: return (0);
! 502: }
! 503:
! 504: int
! 505: sv_open(addr, flags)
! 506: void *addr;
! 507: int flags;
! 508: {
! 509:
! 510: struct sv_softc *sc = addr;
! 511: int intr_mask = 0;
! 512: u_int8_t reg;
! 513:
! 514: /* Map the DMA channels, if necessary */
! 515: if (!(sc->sc_dma_configured & SV_DMAA_CONFIGURED)) {
! 516: /* XXX - there seems to be no general way to find an
! 517: I/O range */
! 518: int dmaio;
! 519: int iosize = 0x10;
! 520:
! 521: if (sc->sc_dma_configured & SV_DMAA_TRIED_CONFIGURE)
! 522: return (ENXIO);
! 523:
! 524: for (dmaio = 0xa000; dmaio < 0xb000; dmaio += iosize) {
! 525: if (!bus_space_map(sc->sc_iot, dmaio, iosize, 0,
! 526: &sc->sc_dmaa_ioh)) {
! 527: goto found_dmaa;
! 528: }
! 529: }
! 530:
! 531: sc->sc_dma_configured |= SV_DMAA_TRIED_CONFIGURE;
! 532: return (ENXIO);
! 533: found_dmaa:
! 534:
! 535: pci_conf_write(sc->sc_pci_chipset_tag, sc->sc_pci_tag,
! 536: SV_DMAA_CONFIG_OFF,
! 537: dmaio | SV_DMA_CHANNEL_ENABLE
! 538: | SV_DMAA_EXTENDED_ADDR);
! 539:
! 540: sc->sc_dma_configured |= SV_DMAA_CONFIGURED;
! 541: intr_mask = 1;
! 542: }
! 543:
! 544: if (!(sc->sc_dma_configured & SV_DMAC_CONFIGURED)) {
! 545: /* XXX - there seems to be no general way to find an
! 546: I/O range */
! 547: int dmaio;
! 548: int iosize = 0x10;
! 549:
! 550: if (sc->sc_dma_configured & SV_DMAC_TRIED_CONFIGURE)
! 551: return (ENXIO);
! 552:
! 553: for (dmaio = 0xa000; dmaio < 0xb000; dmaio += iosize) {
! 554: if (!bus_space_map(sc->sc_iot, dmaio, iosize, 0,
! 555: &sc->sc_dmac_ioh)) {
! 556: goto found_dmac;
! 557: }
! 558: }
! 559:
! 560: sc->sc_dma_configured |= SV_DMAC_TRIED_CONFIGURE;
! 561: return (ENXIO);
! 562: found_dmac:
! 563:
! 564: pci_conf_write(sc->sc_pci_chipset_tag, sc->sc_pci_tag,
! 565: SV_DMAC_CONFIG_OFF,
! 566: dmaio | SV_DMA_CHANNEL_ENABLE);
! 567:
! 568: sc->sc_dma_configured |= SV_DMAC_CONFIGURED;
! 569: intr_mask = 1;
! 570: }
! 571:
! 572: /* Make sure DMA interrupts are enabled */
! 573: if (intr_mask) {
! 574: reg = sv_read(sc, SV_CODEC_INTMASK);
! 575: reg &= ~(SV_INTMASK_DMAA | SV_INTMASK_DMAC);
! 576: reg |= SV_INTMASK_UD | SV_INTMASK_SINT | SV_INTMASK_MIDI;
! 577: sv_write(sc, SV_CODEC_INTMASK, reg);
! 578: }
! 579:
! 580: sc->sc_pintr = 0;
! 581: sc->sc_rintr = 0;
! 582:
! 583: return (0);
! 584: }
! 585:
! 586: /*
! 587: * Close function is called at splaudio().
! 588: */
! 589: void
! 590: sv_close(addr)
! 591: void *addr;
! 592: {
! 593: struct sv_softc *sc = addr;
! 594:
! 595: sv_halt_in_dma(sc);
! 596: sv_halt_out_dma(sc);
! 597:
! 598: sc->sc_pintr = 0;
! 599: sc->sc_rintr = 0;
! 600: }
! 601:
! 602: int
! 603: sv_query_encoding(addr, fp)
! 604: void *addr;
! 605: struct audio_encoding *fp;
! 606: {
! 607: switch (fp->index) {
! 608: case 0:
! 609: strlcpy(fp->name, AudioEulinear, sizeof fp->name);
! 610: fp->encoding = AUDIO_ENCODING_ULINEAR;
! 611: fp->precision = 8;
! 612: fp->flags = 0;
! 613: return (0);
! 614: case 1:
! 615: strlcpy(fp->name, AudioEmulaw, sizeof fp->name);
! 616: fp->encoding = AUDIO_ENCODING_ULAW;
! 617: fp->precision = 8;
! 618: fp->flags = AUDIO_ENCODINGFLAG_EMULATED;
! 619: return (0);
! 620: case 2:
! 621: strlcpy(fp->name, AudioEalaw, sizeof fp->name);
! 622: fp->encoding = AUDIO_ENCODING_ALAW;
! 623: fp->precision = 8;
! 624: fp->flags = AUDIO_ENCODINGFLAG_EMULATED;
! 625: return (0);
! 626: case 3:
! 627: strlcpy(fp->name, AudioEslinear, sizeof fp->name);
! 628: fp->encoding = AUDIO_ENCODING_SLINEAR;
! 629: fp->precision = 8;
! 630: fp->flags = AUDIO_ENCODINGFLAG_EMULATED;
! 631: return (0);
! 632: case 4:
! 633: strlcpy(fp->name, AudioEslinear_le, sizeof fp->name);
! 634: fp->encoding = AUDIO_ENCODING_SLINEAR_LE;
! 635: fp->precision = 16;
! 636: fp->flags = 0;
! 637: return (0);
! 638: case 5:
! 639: strlcpy(fp->name, AudioEulinear_le, sizeof fp->name);
! 640: fp->encoding = AUDIO_ENCODING_ULINEAR_LE;
! 641: fp->precision = 16;
! 642: fp->flags = AUDIO_ENCODINGFLAG_EMULATED;
! 643: return (0);
! 644: case 6:
! 645: strlcpy(fp->name, AudioEslinear_be, sizeof fp->name);
! 646: fp->encoding = AUDIO_ENCODING_SLINEAR_BE;
! 647: fp->precision = 16;
! 648: fp->flags = AUDIO_ENCODINGFLAG_EMULATED;
! 649: return (0);
! 650: case 7:
! 651: strlcpy(fp->name, AudioEulinear_be, sizeof fp->name);
! 652: fp->encoding = AUDIO_ENCODING_ULINEAR_BE;
! 653: fp->precision = 16;
! 654: fp->flags = AUDIO_ENCODINGFLAG_EMULATED;
! 655: return (0);
! 656: default:
! 657: return (EINVAL);
! 658: }
! 659: }
! 660:
! 661: int
! 662: sv_set_params(addr, setmode, usemode, p, r)
! 663: void *addr;
! 664: int setmode, usemode;
! 665: struct audio_params *p, *r;
! 666: {
! 667: struct sv_softc *sc = addr;
! 668: void (*pswcode)(void *, u_char *buf, int cnt);
! 669: void (*rswcode)(void *, u_char *buf, int cnt);
! 670: u_int32_t mode, val;
! 671: u_int8_t reg;
! 672:
! 673: pswcode = rswcode = 0;
! 674: switch (p->encoding) {
! 675: case AUDIO_ENCODING_SLINEAR_BE:
! 676: if (p->precision == 16)
! 677: rswcode = pswcode = swap_bytes;
! 678: else
! 679: pswcode = rswcode = change_sign8;
! 680: break;
! 681: case AUDIO_ENCODING_SLINEAR_LE:
! 682: if (p->precision != 16)
! 683: pswcode = rswcode = change_sign8;
! 684: break;
! 685: case AUDIO_ENCODING_ULINEAR_BE:
! 686: if (p->precision == 16) {
! 687: pswcode = swap_bytes_change_sign16;
! 688: rswcode = change_sign16_swap_bytes;
! 689: }
! 690: break;
! 691: case AUDIO_ENCODING_ULINEAR_LE:
! 692: if (p->precision == 16)
! 693: pswcode = rswcode = change_sign16;
! 694: break;
! 695: case AUDIO_ENCODING_ULAW:
! 696: pswcode = mulaw_to_ulinear8;
! 697: rswcode = ulinear8_to_mulaw;
! 698: break;
! 699: case AUDIO_ENCODING_ALAW:
! 700: pswcode = alaw_to_ulinear8;
! 701: rswcode = ulinear8_to_alaw;
! 702: break;
! 703: default:
! 704: return (EINVAL);
! 705: }
! 706:
! 707: if (p->precision == 16)
! 708: mode = SV_DMAA_FORMAT16 | SV_DMAC_FORMAT16;
! 709: else
! 710: mode = 0;
! 711: if (p->channels == 2)
! 712: mode |= SV_DMAA_STEREO | SV_DMAC_STEREO;
! 713: else if (p->channels != 1)
! 714: return (EINVAL);
! 715: if (p->sample_rate < 2000 || p->sample_rate > 48000)
! 716: return (EINVAL);
! 717:
! 718: p->sw_code = pswcode;
! 719: r->sw_code = rswcode;
! 720:
! 721: /* Set the encoding */
! 722: reg = sv_read_indirect(sc, SV_DMA_DATA_FORMAT);
! 723: reg &= ~(SV_DMAA_FORMAT16 | SV_DMAC_FORMAT16 | SV_DMAA_STEREO |
! 724: SV_DMAC_STEREO);
! 725: reg |= (mode);
! 726: sv_write_indirect(sc, SV_DMA_DATA_FORMAT, reg);
! 727:
! 728: val = p->sample_rate * 65536 / 48000;
! 729:
! 730: sv_write_indirect(sc, SV_PCM_SAMPLE_RATE_0, (val & 0xff));
! 731: sv_write_indirect(sc, SV_PCM_SAMPLE_RATE_1, (val >> 8));
! 732:
! 733: #define F_REF 24576000
! 734:
! 735: if (setmode & AUMODE_RECORD)
! 736: {
! 737: /* The ADC reference frequency (f_out) is 512 * the sample rate */
! 738:
! 739: /* f_out is dervied from the 24.576MHZ crystal by three values:
! 740: M & N & R. The equation is as follows:
! 741:
! 742: f_out = (m + 2) * f_ref / ((n + 2) * (2 ^ a))
! 743:
! 744: with the constraint that:
! 745:
! 746: 80 MhZ < (m + 2) / (n + 2) * f_ref <= 150MHz
! 747: and n, m >= 1
! 748: */
! 749:
! 750: int goal_f_out = 512 * r->sample_rate;
! 751: int a, n, m, best_n, best_m, best_error = 10000000;
! 752: int pll_sample;
! 753:
! 754: for (a = 0; a < 8; a++) {
! 755: if ((goal_f_out * (1 << a)) >= 80000000)
! 756: break;
! 757: }
! 758:
! 759: /* a != 8 because sample_rate >= 2000 */
! 760:
! 761: for (n = 33; n > 2; n--) {
! 762: int error;
! 763:
! 764: m = (goal_f_out * n * (1 << a)) / F_REF;
! 765:
! 766: if ((m > 257) || (m < 3)) continue;
! 767:
! 768: pll_sample = (m * F_REF) / (n * (1 << a));
! 769: pll_sample /= 512;
! 770:
! 771: /* Threshold might be good here */
! 772: error = pll_sample - r->sample_rate;
! 773: error = abs(error);
! 774:
! 775: if (error < best_error) {
! 776: best_error = error;
! 777: best_n = n;
! 778: best_m = m;
! 779: if (error == 0) break;
! 780: }
! 781: }
! 782:
! 783:
! 784: best_n -= 2;
! 785: best_m -= 2;
! 786:
! 787: sv_write_indirect(sc, SV_ADC_PLL_M, best_m);
! 788: sv_write_indirect(sc, SV_ADC_PLL_N, best_n | (a << SV_PLL_R_SHIFT));
! 789: }
! 790: return (0);
! 791: }
! 792:
! 793: int
! 794: sv_round_blocksize(addr, blk)
! 795: void *addr;
! 796: int blk;
! 797: {
! 798: return ((blk + 31) & -32); /* keep good alignment */
! 799: }
! 800:
! 801: int
! 802: sv_dma_init_input(addr, buf, cc)
! 803: void *addr;
! 804: void *buf;
! 805: int cc;
! 806: {
! 807: struct sv_softc *sc = addr;
! 808: struct sv_dma *p;
! 809: int dma_count;
! 810:
! 811: DPRINTF(("sv_dma_init_input: dma start loop input addr=%p cc=%d\n",
! 812: buf, cc));
! 813: for (p = sc->sc_dmas; p && KERNADDR(p) != buf; p = p->next)
! 814: ;
! 815: if (!p) {
! 816: printf("sv_dma_init_input: bad addr %p\n", buf);
! 817: return (EINVAL);
! 818: }
! 819:
! 820: dma_count = (cc >> 1) - 1;
! 821:
! 822: bus_space_write_4(sc->sc_iot, sc->sc_dmac_ioh, SV_DMA_ADDR0,
! 823: DMAADDR(p));
! 824: bus_space_write_4(sc->sc_iot, sc->sc_dmac_ioh, SV_DMA_COUNT0,
! 825: dma_count);
! 826: bus_space_write_1(sc->sc_iot, sc->sc_dmac_ioh, SV_DMA_MODE,
! 827: DMA37MD_WRITE | DMA37MD_LOOP);
! 828:
! 829: return (0);
! 830: }
! 831:
! 832: int
! 833: sv_dma_init_output(addr, buf, cc)
! 834: void *addr;
! 835: void *buf;
! 836: int cc;
! 837: {
! 838: struct sv_softc *sc = addr;
! 839: struct sv_dma *p;
! 840: int dma_count;
! 841:
! 842: DPRINTF(("eap: dma start loop output buf=%p cc=%d\n", buf, cc));
! 843: for (p = sc->sc_dmas; p && KERNADDR(p) != buf; p = p->next)
! 844: ;
! 845: if (!p) {
! 846: printf("sv_dma_init_output: bad addr %p\n", buf);
! 847: return (EINVAL);
! 848: }
! 849:
! 850: dma_count = cc - 1;
! 851:
! 852: bus_space_write_4(sc->sc_iot, sc->sc_dmaa_ioh, SV_DMA_ADDR0,
! 853: DMAADDR(p));
! 854: bus_space_write_4(sc->sc_iot, sc->sc_dmaa_ioh, SV_DMA_COUNT0,
! 855: dma_count);
! 856: bus_space_write_1(sc->sc_iot, sc->sc_dmaa_ioh, SV_DMA_MODE,
! 857: DMA37MD_READ | DMA37MD_LOOP);
! 858:
! 859: return (0);
! 860: }
! 861:
! 862: int
! 863: sv_dma_output(addr, p, cc, intr, arg)
! 864: void *addr;
! 865: void *p;
! 866: int cc;
! 867: void (*intr)(void *);
! 868: void *arg;
! 869: {
! 870: struct sv_softc *sc = addr;
! 871: u_int8_t mode;
! 872:
! 873: DPRINTFN(1,
! 874: ("sv_dma_output: sc=%p buf=%p cc=%d intr=%p(%p)\n",
! 875: addr, p, cc, intr, arg));
! 876:
! 877: sc->sc_pintr = intr;
! 878: sc->sc_parg = arg;
! 879: if (!(sc->sc_enable & SV_PLAY_ENABLE)) {
! 880: int dma_count = cc - 1;
! 881:
! 882: sv_write_indirect(sc, SV_DMAA_COUNT1, dma_count >> 8);
! 883: sv_write_indirect(sc, SV_DMAA_COUNT0, (dma_count & 0xFF));
! 884:
! 885: mode = sv_read_indirect(sc, SV_PLAY_RECORD_ENABLE);
! 886: mode |= SV_PLAY_ENABLE;
! 887: sv_write_indirect(sc, SV_PLAY_RECORD_ENABLE, mode);
! 888: sc->sc_enable |= SV_PLAY_ENABLE;
! 889: }
! 890: return (0);
! 891: }
! 892:
! 893: int
! 894: sv_dma_input(addr, p, cc, intr, arg)
! 895: void *addr;
! 896: void *p;
! 897: int cc;
! 898: void (*intr)(void *);
! 899: void *arg;
! 900: {
! 901: struct sv_softc *sc = addr;
! 902: u_int8_t mode;
! 903:
! 904: DPRINTFN(1, ("sv_dma_input: sc=%p buf=%p cc=%d intr=%p(%p)\n",
! 905: addr, p, cc, intr, arg));
! 906: sc->sc_rintr = intr;
! 907: sc->sc_rarg = arg;
! 908: if (!(sc->sc_enable & SV_RECORD_ENABLE)) {
! 909: int dma_count = (cc >> 1) - 1;
! 910:
! 911: sv_write_indirect(sc, SV_DMAC_COUNT1, dma_count >> 8);
! 912: sv_write_indirect(sc, SV_DMAC_COUNT0, (dma_count & 0xFF));
! 913:
! 914: mode = sv_read_indirect(sc, SV_PLAY_RECORD_ENABLE);
! 915: mode |= SV_RECORD_ENABLE;
! 916: sv_write_indirect(sc, SV_PLAY_RECORD_ENABLE, mode);
! 917: sc->sc_enable |= SV_RECORD_ENABLE;
! 918: }
! 919: return (0);
! 920: }
! 921:
! 922: int
! 923: sv_halt_out_dma(addr)
! 924: void *addr;
! 925: {
! 926: struct sv_softc *sc = addr;
! 927: u_int8_t mode;
! 928:
! 929: DPRINTF(("eap: sv_halt_out_dma\n"));
! 930: mode = sv_read_indirect(sc, SV_PLAY_RECORD_ENABLE);
! 931: mode &= ~SV_PLAY_ENABLE;
! 932: sc->sc_enable &= ~SV_PLAY_ENABLE;
! 933: sv_write_indirect(sc, SV_PLAY_RECORD_ENABLE, mode);
! 934:
! 935: return (0);
! 936: }
! 937:
! 938: int
! 939: sv_halt_in_dma(addr)
! 940: void *addr;
! 941: {
! 942: struct sv_softc *sc = addr;
! 943: u_int8_t mode;
! 944:
! 945: DPRINTF(("eap: sv_halt_in_dma\n"));
! 946: mode = sv_read_indirect(sc, SV_PLAY_RECORD_ENABLE);
! 947: mode &= ~SV_RECORD_ENABLE;
! 948: sc->sc_enable &= ~SV_RECORD_ENABLE;
! 949: sv_write_indirect(sc, SV_PLAY_RECORD_ENABLE, mode);
! 950:
! 951: return (0);
! 952: }
! 953:
! 954: int
! 955: sv_getdev(addr, retp)
! 956: void *addr;
! 957: struct audio_device *retp;
! 958: {
! 959: *retp = sv_device;
! 960: return (0);
! 961: }
! 962:
! 963:
! 964: /*
! 965: * Mixer related code is here
! 966: *
! 967: */
! 968:
! 969: #define SV_INPUT_CLASS 0
! 970: #define SV_OUTPUT_CLASS 1
! 971: #define SV_RECORD_CLASS 2
! 972:
! 973: #define SV_LAST_CLASS 2
! 974:
! 975: static const char *mixer_classes[] = { AudioCinputs, AudioCoutputs, AudioCrecord };
! 976:
! 977: static const struct {
! 978: u_int8_t l_port;
! 979: u_int8_t r_port;
! 980: u_int8_t mask;
! 981: u_int8_t class;
! 982: const char *audio;
! 983: } ports[] = {
! 984: { SV_LEFT_AUX1_INPUT_CONTROL, SV_RIGHT_AUX1_INPUT_CONTROL, SV_AUX1_MASK,
! 985: SV_INPUT_CLASS, "aux1" },
! 986: { SV_LEFT_CD_INPUT_CONTROL, SV_RIGHT_CD_INPUT_CONTROL, SV_CD_MASK,
! 987: SV_INPUT_CLASS, AudioNcd },
! 988: { SV_LEFT_LINE_IN_INPUT_CONTROL, SV_RIGHT_LINE_IN_INPUT_CONTROL, SV_LINE_IN_MASK,
! 989: SV_INPUT_CLASS, AudioNline },
! 990: { SV_MIC_INPUT_CONTROL, 0, SV_MIC_MASK, SV_INPUT_CLASS, AudioNmicrophone },
! 991: { SV_LEFT_SYNTH_INPUT_CONTROL, SV_RIGHT_SYNTH_INPUT_CONTROL,
! 992: SV_SYNTH_MASK, SV_INPUT_CLASS, AudioNfmsynth },
! 993: { SV_LEFT_AUX2_INPUT_CONTROL, SV_RIGHT_AUX2_INPUT_CONTROL, SV_AUX2_MASK,
! 994: SV_INPUT_CLASS, "aux2" },
! 995: { SV_LEFT_PCM_INPUT_CONTROL, SV_RIGHT_PCM_INPUT_CONTROL, SV_PCM_MASK,
! 996: SV_INPUT_CLASS, AudioNdac },
! 997: { SV_LEFT_MIXER_OUTPUT_CONTROL, SV_RIGHT_MIXER_OUTPUT_CONTROL,
! 998: SV_MIXER_OUT_MASK, SV_OUTPUT_CLASS, AudioNmaster }
! 999: };
! 1000:
! 1001:
! 1002: static const struct {
! 1003: int idx;
! 1004: const char *name;
! 1005: } record_sources[] = {
! 1006: { SV_REC_CD, AudioNcd },
! 1007: { SV_REC_DAC, AudioNdac },
! 1008: { SV_REC_AUX2, "aux2" },
! 1009: { SV_REC_LINE, AudioNline },
! 1010: { SV_REC_AUX1, "aux1" },
! 1011: { SV_REC_MIC, AudioNmicrophone },
! 1012: { SV_REC_MIXER, AudioNmixerout }
! 1013: };
! 1014:
! 1015:
! 1016: #define SV_DEVICES_PER_PORT 2
! 1017: #define SV_FIRST_MIXER (SV_LAST_CLASS + 1)
! 1018: #define SV_LAST_MIXER (SV_DEVICES_PER_PORT * (ARRAY_SIZE(ports)) + SV_LAST_CLASS)
! 1019: #define SV_RECORD_SOURCE (SV_LAST_MIXER + 1)
! 1020: #define SV_MIC_BOOST (SV_LAST_MIXER + 2)
! 1021: #define SV_RECORD_GAIN (SV_LAST_MIXER + 3)
! 1022: #define SV_SRS_MODE (SV_LAST_MIXER + 4)
! 1023:
! 1024: int
! 1025: sv_query_devinfo(addr, dip)
! 1026: void *addr;
! 1027: mixer_devinfo_t *dip;
! 1028: {
! 1029:
! 1030: /* It's a class */
! 1031: if (dip->index <= SV_LAST_CLASS) {
! 1032: dip->type = AUDIO_MIXER_CLASS;
! 1033: dip->mixer_class = dip->index;
! 1034: dip->next = dip->prev = AUDIO_MIXER_LAST;
! 1035: strlcpy(dip->label.name, mixer_classes[dip->index],
! 1036: sizeof dip->label.name);
! 1037: return (0);
! 1038: }
! 1039:
! 1040: if (dip->index >= SV_FIRST_MIXER &&
! 1041: dip->index <= SV_LAST_MIXER) {
! 1042: int off = dip->index - SV_FIRST_MIXER;
! 1043: int mute = (off % SV_DEVICES_PER_PORT);
! 1044: int idx = off / SV_DEVICES_PER_PORT;
! 1045:
! 1046: dip->mixer_class = ports[idx].class;
! 1047: strlcpy(dip->label.name, ports[idx].audio, sizeof dip->label.name);
! 1048:
! 1049: if (!mute) {
! 1050: dip->type = AUDIO_MIXER_VALUE;
! 1051: dip->prev = AUDIO_MIXER_LAST;
! 1052: dip->next = dip->index + 1;
! 1053:
! 1054: if (ports[idx].r_port != 0)
! 1055: dip->un.v.num_channels = 2;
! 1056: else
! 1057: dip->un.v.num_channels = 1;
! 1058:
! 1059: strlcpy(dip->un.v.units.name, AudioNvolume, sizeof dip->un.v.units.name);
! 1060:
! 1061: } else {
! 1062: dip->type = AUDIO_MIXER_ENUM;
! 1063: dip->prev = dip->index - 1;
! 1064: dip->next = AUDIO_MIXER_LAST;
! 1065:
! 1066: strlcpy(dip->label.name, AudioNmute, sizeof dip->label.name);
! 1067: dip->un.e.num_mem = 2;
! 1068: strlcpy(dip->un.e.member[0].label.name, AudioNoff,
! 1069: sizeof dip->un.e.member[0].label.name);
! 1070: dip->un.e.member[0].ord = 0;
! 1071: strlcpy(dip->un.e.member[1].label.name, AudioNon,
! 1072: sizeof dip->un.e.member[1].label.name);
! 1073: dip->un.e.member[1].ord = 1;
! 1074:
! 1075: }
! 1076:
! 1077: return (0);
! 1078: }
! 1079:
! 1080: switch (dip->index) {
! 1081: case SV_RECORD_SOURCE:
! 1082: dip->mixer_class = SV_RECORD_CLASS;
! 1083: dip->prev = AUDIO_MIXER_LAST;
! 1084: dip->next = SV_RECORD_GAIN;
! 1085: strlcpy(dip->label.name, AudioNsource, sizeof dip->label.name);
! 1086: dip->type = AUDIO_MIXER_ENUM;
! 1087:
! 1088: dip->un.e.num_mem = ARRAY_SIZE(record_sources);
! 1089:
! 1090: {
! 1091: int idx;
! 1092: for (idx = 0; idx < ARRAY_SIZE(record_sources); idx++) {
! 1093: strlcpy(dip->un.e.member[idx].label.name, record_sources[idx].name,
! 1094: sizeof dip->un.e.member[idx].label.name);
! 1095: dip->un.e.member[idx].ord = record_sources[idx].idx;
! 1096: }
! 1097: }
! 1098: return (0);
! 1099:
! 1100: case SV_RECORD_GAIN:
! 1101: dip->mixer_class = SV_RECORD_CLASS;
! 1102: dip->prev = SV_RECORD_SOURCE;
! 1103: dip->next = AUDIO_MIXER_LAST;
! 1104: strlcpy(dip->label.name, "gain", sizeof dip->label.name);
! 1105: dip->type = AUDIO_MIXER_VALUE;
! 1106: dip->un.v.num_channels = 1;
! 1107: strlcpy(dip->un.v.units.name, AudioNvolume, sizeof dip->un.v.units.name);
! 1108: return (0);
! 1109:
! 1110: case SV_MIC_BOOST:
! 1111: dip->mixer_class = SV_RECORD_CLASS;
! 1112: dip->prev = AUDIO_MIXER_LAST;
! 1113: dip->next = AUDIO_MIXER_LAST;
! 1114: strlcpy(dip->label.name, "micboost", sizeof dip->label.name);
! 1115: goto on_off;
! 1116:
! 1117: case SV_SRS_MODE:
! 1118: dip->mixer_class = SV_OUTPUT_CLASS;
! 1119: dip->prev = dip->next = AUDIO_MIXER_LAST;
! 1120: strlcpy(dip->label.name, AudioNspatial, sizeof dip->label.name);
! 1121:
! 1122: on_off:
! 1123: dip->type = AUDIO_MIXER_ENUM;
! 1124: dip->un.e.num_mem = 2;
! 1125: strlcpy(dip->un.e.member[0].label.name, AudioNoff,
! 1126: sizeof dip->un.e.member[0].label.name);
! 1127: dip->un.e.member[0].ord = 0;
! 1128: strlcpy(dip->un.e.member[1].label.name, AudioNon,
! 1129: sizeof dip->un.e.member[1].label.name);
! 1130: dip->un.e.member[1].ord = 1;
! 1131: return (0);
! 1132: }
! 1133:
! 1134: return (ENXIO);
! 1135: }
! 1136:
! 1137: int
! 1138: sv_mixer_set_port(addr, cp)
! 1139: void *addr;
! 1140: mixer_ctrl_t *cp;
! 1141: {
! 1142: struct sv_softc *sc = addr;
! 1143: u_int8_t reg;
! 1144: int idx;
! 1145:
! 1146: if (cp->dev >= SV_FIRST_MIXER &&
! 1147: cp->dev <= SV_LAST_MIXER) {
! 1148: int off = cp->dev - SV_FIRST_MIXER;
! 1149: int mute = (off % SV_DEVICES_PER_PORT);
! 1150: idx = off / SV_DEVICES_PER_PORT;
! 1151:
! 1152: if (mute) {
! 1153: if (cp->type != AUDIO_MIXER_ENUM)
! 1154: return (EINVAL);
! 1155:
! 1156: reg = sv_read_indirect(sc, ports[idx].l_port);
! 1157: if (cp->un.ord)
! 1158: reg |= SV_MUTE_BIT;
! 1159: else
! 1160: reg &= ~SV_MUTE_BIT;
! 1161: sv_write_indirect(sc, ports[idx].l_port, reg);
! 1162:
! 1163: if (ports[idx].r_port) {
! 1164: reg = sv_read_indirect(sc, ports[idx].r_port);
! 1165: if (cp->un.ord)
! 1166: reg |= SV_MUTE_BIT;
! 1167: else
! 1168: reg &= ~SV_MUTE_BIT;
! 1169: sv_write_indirect(sc, ports[idx].r_port, reg);
! 1170: }
! 1171: } else {
! 1172: int lval, rval;
! 1173:
! 1174: if (cp->type != AUDIO_MIXER_VALUE)
! 1175: return (EINVAL);
! 1176:
! 1177: if (cp->un.value.num_channels != 1 &&
! 1178: cp->un.value.num_channels != 2)
! 1179: return (EINVAL);
! 1180:
! 1181: if (ports[idx].r_port == 0) {
! 1182: if (cp->un.value.num_channels != 1)
! 1183: return (EINVAL);
! 1184: lval = cp->un.value.level[AUDIO_MIXER_LEVEL_MONO];
! 1185: } else {
! 1186: if (cp->un.value.num_channels != 2)
! 1187: return (EINVAL);
! 1188:
! 1189: lval = cp->un.value.level[AUDIO_MIXER_LEVEL_LEFT];
! 1190: rval = cp->un.value.level[AUDIO_MIXER_LEVEL_RIGHT];
! 1191: }
! 1192:
! 1193: sc->sc_trd = 1;
! 1194:
! 1195: reg = sv_read_indirect(sc, ports[idx].l_port);
! 1196: reg &= ~(ports[idx].mask);
! 1197: lval = ((AUDIO_MAX_GAIN - lval) * ports[idx].mask) / AUDIO_MAX_GAIN;
! 1198: reg |= lval;
! 1199: sv_write_indirect(sc, ports[idx].l_port, reg);
! 1200:
! 1201: if (ports[idx].r_port != 0) {
! 1202: reg = sv_read_indirect(sc, ports[idx].r_port);
! 1203: reg &= ~(ports[idx].mask);
! 1204:
! 1205: rval = ((AUDIO_MAX_GAIN - rval) * ports[idx].mask) / AUDIO_MAX_GAIN;
! 1206: reg |= rval;
! 1207:
! 1208: sv_write_indirect(sc, ports[idx].r_port, reg);
! 1209: }
! 1210:
! 1211: sc->sc_trd = 0;
! 1212: sv_read_indirect(sc, ports[idx].l_port);
! 1213: }
! 1214:
! 1215: return (0);
! 1216: }
! 1217:
! 1218:
! 1219: switch (cp->dev) {
! 1220: case SV_RECORD_SOURCE:
! 1221: if (cp->type != AUDIO_MIXER_ENUM)
! 1222: return (EINVAL);
! 1223:
! 1224: for (idx = 0; idx < ARRAY_SIZE(record_sources); idx++) {
! 1225: if (record_sources[idx].idx == cp->un.ord)
! 1226: goto found;
! 1227: }
! 1228:
! 1229: return (EINVAL);
! 1230:
! 1231: found:
! 1232: reg = sv_read_indirect(sc, SV_LEFT_ADC_INPUT_CONTROL);
! 1233: reg &= ~SV_REC_SOURCE_MASK;
! 1234: reg |= (((cp->un.ord) << SV_REC_SOURCE_SHIFT) & SV_REC_SOURCE_MASK);
! 1235: sv_write_indirect(sc, SV_LEFT_ADC_INPUT_CONTROL, reg);
! 1236:
! 1237: reg = sv_read_indirect(sc, SV_RIGHT_ADC_INPUT_CONTROL);
! 1238: reg &= ~SV_REC_SOURCE_MASK;
! 1239: reg |= (((cp->un.ord) << SV_REC_SOURCE_SHIFT) & SV_REC_SOURCE_MASK);
! 1240: sv_write_indirect(sc, SV_RIGHT_ADC_INPUT_CONTROL, reg);
! 1241: return (0);
! 1242:
! 1243: case SV_RECORD_GAIN:
! 1244: {
! 1245: int val;
! 1246:
! 1247: if (cp->type != AUDIO_MIXER_VALUE)
! 1248: return (EINVAL);
! 1249:
! 1250: if (cp->un.value.num_channels != 1)
! 1251: return (EINVAL);
! 1252:
! 1253: val = (cp->un.value.level[AUDIO_MIXER_LEVEL_MONO] * SV_REC_GAIN_MASK)
! 1254: / AUDIO_MAX_GAIN;
! 1255:
! 1256: reg = sv_read_indirect(sc, SV_LEFT_ADC_INPUT_CONTROL);
! 1257: reg &= ~SV_REC_GAIN_MASK;
! 1258: reg |= val;
! 1259: sv_write_indirect(sc, SV_LEFT_ADC_INPUT_CONTROL, reg);
! 1260:
! 1261: reg = sv_read_indirect(sc, SV_RIGHT_ADC_INPUT_CONTROL);
! 1262: reg &= ~SV_REC_GAIN_MASK;
! 1263: reg |= val;
! 1264: sv_write_indirect(sc, SV_RIGHT_ADC_INPUT_CONTROL, reg);
! 1265:
! 1266: }
! 1267:
! 1268: return (0);
! 1269:
! 1270: case SV_MIC_BOOST:
! 1271: if (cp->type != AUDIO_MIXER_ENUM)
! 1272: return (EINVAL);
! 1273:
! 1274: reg = sv_read_indirect(sc, SV_LEFT_ADC_INPUT_CONTROL);
! 1275: if (cp->un.ord) {
! 1276: reg |= SV_MIC_BOOST_BIT;
! 1277: } else {
! 1278: reg &= ~SV_MIC_BOOST_BIT;
! 1279: }
! 1280:
! 1281: sv_write_indirect(sc, SV_LEFT_ADC_INPUT_CONTROL, reg);
! 1282: return (0);
! 1283:
! 1284: case SV_SRS_MODE:
! 1285: if (cp->type != AUDIO_MIXER_ENUM)
! 1286: return (EINVAL);
! 1287:
! 1288: reg = sv_read_indirect(sc, SV_SRS_SPACE_CONTROL);
! 1289: if (cp->un.ord) {
! 1290: reg &= ~SV_SRS_SPACE_ONOFF;
! 1291: } else {
! 1292: reg |= SV_SRS_SPACE_ONOFF;
! 1293: }
! 1294:
! 1295: sv_write_indirect(sc, SV_SRS_SPACE_CONTROL, reg);
! 1296: return (0);
! 1297: }
! 1298:
! 1299: return (EINVAL);
! 1300: }
! 1301:
! 1302: int
! 1303: sv_mixer_get_port(addr, cp)
! 1304: void *addr;
! 1305: mixer_ctrl_t *cp;
! 1306: {
! 1307: struct sv_softc *sc = addr;
! 1308: int val;
! 1309: u_int8_t reg;
! 1310:
! 1311: if (cp->dev >= SV_FIRST_MIXER &&
! 1312: cp->dev <= SV_LAST_MIXER) {
! 1313: int off = cp->dev - SV_FIRST_MIXER;
! 1314: int mute = (off % 2);
! 1315: int idx = off / 2;
! 1316:
! 1317: if (mute) {
! 1318: if (cp->type != AUDIO_MIXER_ENUM)
! 1319: return (EINVAL);
! 1320:
! 1321: reg = sv_read_indirect(sc, ports[idx].l_port);
! 1322: cp->un.ord = ((reg & SV_MUTE_BIT) ? 1 : 0);
! 1323: } else {
! 1324: if (cp->type != AUDIO_MIXER_VALUE)
! 1325: return (EINVAL);
! 1326:
! 1327: if (cp->un.value.num_channels != 1 &&
! 1328: cp->un.value.num_channels != 2)
! 1329: return (EINVAL);
! 1330:
! 1331: if ((ports[idx].r_port == 0 &&
! 1332: cp->un.value.num_channels != 1) ||
! 1333: (ports[idx].r_port != 0 &&
! 1334: cp->un.value.num_channels != 2))
! 1335: return (EINVAL);
! 1336:
! 1337: reg = sv_read_indirect(sc, ports[idx].l_port);
! 1338: reg &= ports[idx].mask;
! 1339:
! 1340: val = AUDIO_MAX_GAIN - ((reg * AUDIO_MAX_GAIN) / ports[idx].mask);
! 1341:
! 1342: if (ports[idx].r_port != 0) {
! 1343: cp->un.value.level[AUDIO_MIXER_LEVEL_LEFT] = val;
! 1344:
! 1345: reg = sv_read_indirect(sc, ports[idx].r_port);
! 1346: reg &= ports[idx].mask;
! 1347:
! 1348: val = AUDIO_MAX_GAIN - ((reg * AUDIO_MAX_GAIN) / ports[idx].mask);
! 1349: cp->un.value.level[AUDIO_MIXER_LEVEL_RIGHT] = val;
! 1350: } else
! 1351: cp->un.value.level[AUDIO_MIXER_LEVEL_MONO] = val;
! 1352: }
! 1353:
! 1354: return (0);
! 1355: }
! 1356:
! 1357: switch (cp->dev) {
! 1358: case SV_RECORD_SOURCE:
! 1359: if (cp->type != AUDIO_MIXER_ENUM)
! 1360: return (EINVAL);
! 1361:
! 1362: reg = sv_read_indirect(sc, SV_LEFT_ADC_INPUT_CONTROL);
! 1363: cp->un.ord = ((reg & SV_REC_SOURCE_MASK) >> SV_REC_SOURCE_SHIFT);
! 1364:
! 1365: return (0);
! 1366:
! 1367: case SV_RECORD_GAIN:
! 1368: if (cp->type != AUDIO_MIXER_VALUE)
! 1369: return (EINVAL);
! 1370:
! 1371: if (cp->un.value.num_channels != 1)
! 1372: return (EINVAL);
! 1373:
! 1374: reg = sv_read_indirect(sc, SV_LEFT_ADC_INPUT_CONTROL) & SV_REC_GAIN_MASK;
! 1375: cp->un.value.level[AUDIO_MIXER_LEVEL_MONO] =
! 1376: (((unsigned int)reg) * AUDIO_MAX_GAIN) / SV_REC_GAIN_MASK;
! 1377:
! 1378: return (0);
! 1379:
! 1380: case SV_MIC_BOOST:
! 1381: if (cp->type != AUDIO_MIXER_ENUM)
! 1382: return (EINVAL);
! 1383:
! 1384: reg = sv_read_indirect(sc, SV_LEFT_ADC_INPUT_CONTROL);
! 1385: cp->un.ord = ((reg & SV_MIC_BOOST_BIT) ? 1 : 0);
! 1386:
! 1387: return (0);
! 1388:
! 1389:
! 1390: case SV_SRS_MODE:
! 1391: if (cp->type != AUDIO_MIXER_ENUM)
! 1392: return (EINVAL);
! 1393:
! 1394: reg = sv_read_indirect(sc, SV_SRS_SPACE_CONTROL);
! 1395:
! 1396: cp->un.ord = ((reg & SV_SRS_SPACE_ONOFF) ? 0 : 1);
! 1397: return (0);
! 1398: }
! 1399:
! 1400: return (EINVAL);
! 1401: }
! 1402:
! 1403:
! 1404: static void
! 1405: sv_init_mixer(sc)
! 1406: struct sv_softc *sc;
! 1407: {
! 1408: mixer_ctrl_t cp;
! 1409: int idx;
! 1410:
! 1411: cp.type = AUDIO_MIXER_ENUM;
! 1412: cp.dev = SV_SRS_MODE;
! 1413: cp.un.ord = 0;
! 1414:
! 1415: sv_mixer_set_port(sc, &cp);
! 1416:
! 1417: for (idx = 0; idx < ARRAY_SIZE(ports); idx++) {
! 1418: if (ports[idx].audio == AudioNdac) {
! 1419: cp.type = AUDIO_MIXER_ENUM;
! 1420: cp.dev = SV_FIRST_MIXER + idx * SV_DEVICES_PER_PORT + 1;
! 1421: cp.un.ord = 0;
! 1422: sv_mixer_set_port(sc, &cp);
! 1423: break;
! 1424: }
! 1425: }
! 1426: }
! 1427:
! 1428: void *
! 1429: sv_malloc(addr, direction, size, pool, flags)
! 1430: void *addr;
! 1431: int direction;
! 1432: size_t size;
! 1433: int pool;
! 1434: int flags;
! 1435: {
! 1436: struct sv_softc *sc = addr;
! 1437: struct sv_dma *p;
! 1438: int error;
! 1439:
! 1440: p = malloc(sizeof(*p), pool, flags);
! 1441: if (!p)
! 1442: return (0);
! 1443: error = sv_allocmem(sc, size, 16, p);
! 1444: if (error) {
! 1445: free(p, pool);
! 1446: return (0);
! 1447: }
! 1448: p->next = sc->sc_dmas;
! 1449: sc->sc_dmas = p;
! 1450: return (KERNADDR(p));
! 1451: }
! 1452:
! 1453: void
! 1454: sv_free(addr, ptr, pool)
! 1455: void *addr;
! 1456: void *ptr;
! 1457: int pool;
! 1458: {
! 1459: struct sv_softc *sc = addr;
! 1460: struct sv_dma **p;
! 1461:
! 1462: for (p = &sc->sc_dmas; *p; p = &(*p)->next) {
! 1463: if (KERNADDR(*p) == ptr) {
! 1464: sv_freemem(sc, *p);
! 1465: *p = (*p)->next;
! 1466: free(*p, pool);
! 1467: return;
! 1468: }
! 1469: }
! 1470: }
! 1471:
! 1472: paddr_t
! 1473: sv_mappage(addr, mem, off, prot)
! 1474: void *addr;
! 1475: void *mem;
! 1476: off_t off;
! 1477: int prot;
! 1478: {
! 1479: struct sv_softc *sc = addr;
! 1480: struct sv_dma *p;
! 1481:
! 1482: for (p = sc->sc_dmas; p && KERNADDR(p) != mem; p = p->next)
! 1483: ;
! 1484: if (!p)
! 1485: return (-1);
! 1486: return (bus_dmamem_mmap(sc->sc_dmatag, p->segs, p->nsegs,
! 1487: off, prot, BUS_DMA_WAITOK));
! 1488: }
! 1489:
! 1490: int
! 1491: sv_get_props(addr)
! 1492: void *addr;
! 1493: {
! 1494: return (AUDIO_PROP_MMAP | AUDIO_PROP_FULLDUPLEX);
! 1495: }
CVSweb