Annotation of sys/dev/pci/neo.c, Revision 1.1
1.1 ! nbrk 1: /* $OpenBSD: neo.c,v 1.19 2005/08/09 04:10:13 mickey Exp $ */
! 2:
! 3: /*
! 4: * Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk>
! 5: * All rights reserved.
! 6: *
! 7: * Derived from the public domain Linux driver
! 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: *
! 18: * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
! 19: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
! 20: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
! 21: * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
! 22: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
! 23: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
! 24: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
! 25: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
! 26: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
! 27: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
! 28: * SUCH DAMAGE.
! 29: *
! 30: * $FreeBSD: src/sys/dev/sound/pci/neomagic.c,v 1.8 2000/03/20 15:30:50 cg Exp $
! 31: */
! 32:
! 33:
! 34:
! 35: #include <sys/param.h>
! 36: #include <sys/systm.h>
! 37: #include <sys/kernel.h>
! 38: #include <sys/malloc.h>
! 39: #include <sys/device.h>
! 40:
! 41: #include <dev/pci/pcidevs.h>
! 42: #include <dev/pci/pcivar.h>
! 43:
! 44: #include <sys/audioio.h>
! 45: #include <dev/audio_if.h>
! 46: #include <dev/mulaw.h>
! 47: #include <dev/auconv.h>
! 48: #include <dev/ic/ac97.h>
! 49:
! 50: #include <dev/pci/neoreg.h>
! 51:
! 52: /* -------------------------------------------------------------------- */
! 53: /*
! 54: * As of 04/13/00, public documentation on the Neomagic 256 is not available.
! 55: * These comments were gleaned by looking at the driver carefully.
! 56: *
! 57: * The Neomagic 256 AV/ZX chips provide both video and audio capabilities
! 58: * on one chip. About 2-6 megabytes of memory are associated with
! 59: * the chip. Most of this goes to video frame buffers, but some is used for
! 60: * audio buffering.
! 61: *
! 62: * Unlike most PCI audio chips, the Neomagic chip does not rely on DMA.
! 63: * Instead, the chip allows you to carve two ring buffers out of its
! 64: * memory. How you carve this and how much you can carve seems to be
! 65: * voodoo. The algorithm is in nm_init.
! 66: *
! 67: * Most Neomagic audio chips use the AC-97 codec interface. However, there
! 68: * seem to be a select few chips 256AV chips that do not support AC-97.
! 69: * This driver does not support them but there are rumors that it
! 70: * might work with wss isa drivers. This might require some playing around
! 71: * with your BIOS.
! 72: *
! 73: * The Neomagic 256 AV/ZX have 2 PCI I/O region descriptors. Both of
! 74: * them describe a memory region. The frame buffer is the first region
! 75: * and the register set is the second region.
! 76: *
! 77: * The register manipulation logic is taken from the Linux driver,
! 78: * which is in the public domain.
! 79: *
! 80: * The Neomagic is even nice enough to map the AC-97 codec registers into
! 81: * the register space to allow direct manipulation. Watch out, accessing
! 82: * AC-97 registers on the Neomagic requires great delicateness, otherwise
! 83: * the thing will hang the PCI bus, rendering your system frozen.
! 84: *
! 85: * For one, it seems the Neomagic status register that reports AC-97
! 86: * readiness should NOT be polled more often than once each 1ms.
! 87: *
! 88: * Also, writes to the AC-97 register space may take over 40us to
! 89: * complete.
! 90: *
! 91: * Unlike many sound engines, the Neomagic does not support (as fas as
! 92: * we know :) ) the notion of interrupting every n bytes transferred,
! 93: * unlike many DMA engines. Instead, it allows you to specify one
! 94: * location in each ring buffer (called the watermark). When the chip
! 95: * passes that location while playing, it signals an interrupt.
! 96: *
! 97: * The ring buffer size is currently 16k. That is about 100ms of audio
! 98: * at 44.1khz/stero/16 bit. However, to keep the buffer full, interrupts
! 99: * are generated more often than that, so 20-40 interrupts per second
! 100: * should not be unexpected. Increasing BUFFSIZE should help minimize
! 101: * the glitches due to drivers that spend too much time looping at high
! 102: * privelege levels as well as the impact of badly written audio
! 103: * interface clients.
! 104: *
! 105: * TO-DO list:
! 106: * neo_malloc/neo_free are still seriously broken.
! 107: *
! 108: * Figure out interaction with video stuff (look at Xfree86 driver?)
! 109: *
! 110: * Power management (neoactivate)
! 111: *
! 112: * Fix detection of Neo devices that don't work this driver (see neo_attach)
! 113: *
! 114: * Figure out how to shrink that huge table neo-coeff.h
! 115: */
! 116:
! 117: #define NM_BUFFSIZE 16384
! 118:
! 119: #define NM256AV_PCI_ID 0x800510c8
! 120: #define NM256ZX_PCI_ID 0x800610c8
! 121:
! 122: /* device private data */
! 123: struct neo_softc {
! 124: struct device dev;
! 125:
! 126: bus_space_tag_t bufiot;
! 127: bus_space_handle_t bufioh;
! 128:
! 129: bus_space_tag_t regiot;
! 130: bus_space_handle_t regioh;
! 131:
! 132: u_int32_t type;
! 133: void *ih;
! 134:
! 135: void (*pintr)(void *); /* dma completion intr handler */
! 136: void *parg; /* arg for intr() */
! 137:
! 138: void (*rintr)(void *); /* dma completion intr handler */
! 139: void *rarg; /* arg for intr() */
! 140:
! 141: u_int32_t ac97_base, ac97_status, ac97_busy;
! 142: u_int32_t buftop, pbuf, rbuf, cbuf, acbuf;
! 143: u_int32_t playint, recint, misc1int, misc2int;
! 144: u_int32_t irsz, badintr;
! 145:
! 146: u_int32_t pbufsize;
! 147: u_int32_t rbufsize;
! 148:
! 149: u_int32_t pblksize;
! 150: u_int32_t rblksize;
! 151:
! 152: u_int32_t pwmark;
! 153: u_int32_t rwmark;
! 154:
! 155: struct ac97_codec_if *codec_if;
! 156: struct ac97_host_if host_if;
! 157:
! 158: void *powerhook;
! 159: };
! 160:
! 161: static struct neo_firmware *nf;
! 162:
! 163: /* -------------------------------------------------------------------- */
! 164:
! 165: /*
! 166: * prototypes
! 167: */
! 168:
! 169: static int nm_waitcd(struct neo_softc *sc);
! 170: static int nm_loadcoeff(struct neo_softc *sc, int dir, int num);
! 171: static int nm_init(struct neo_softc *);
! 172:
! 173: int nmchan_getptr(struct neo_softc *, int);
! 174: /* talk to the card */
! 175: static u_int32_t nm_rd(struct neo_softc *, int, int);
! 176: static void nm_wr(struct neo_softc *, int, u_int32_t, int);
! 177: static u_int32_t nm_rdbuf(struct neo_softc *, int, int);
! 178: static void nm_wrbuf(struct neo_softc *, int, u_int32_t, int);
! 179:
! 180: int neo_match(struct device *, void *, void *);
! 181: void neo_attach(struct device *, struct device *, void *);
! 182: int neo_intr(void *);
! 183:
! 184: int neo_open(void *, int);
! 185: void neo_close(void *);
! 186: int neo_query_encoding(void *, struct audio_encoding *);
! 187: int neo_set_params(void *, int, int, struct audio_params *, struct audio_params *);
! 188: int neo_round_blocksize(void *, int);
! 189: int neo_trigger_output(void *, void *, void *, int, void (*)(void *),
! 190: void *, struct audio_params *);
! 191: int neo_trigger_input(void *, void *, void *, int, void (*)(void *),
! 192: void *, struct audio_params *);
! 193: int neo_halt_output(void *);
! 194: int neo_halt_input(void *);
! 195: int neo_getdev(void *, struct audio_device *);
! 196: int neo_mixer_set_port(void *, mixer_ctrl_t *);
! 197: int neo_mixer_get_port(void *, mixer_ctrl_t *);
! 198: int neo_attach_codec(void *sc, struct ac97_codec_if *);
! 199: int neo_read_codec(void *sc, u_int8_t a, u_int16_t *d);
! 200: int neo_write_codec(void *sc, u_int8_t a, u_int16_t d);
! 201: void neo_reset_codec(void *sc);
! 202: enum ac97_host_flags neo_flags_codec(void *sc);
! 203: int neo_query_devinfo(void *, mixer_devinfo_t *);
! 204: void *neo_malloc(void *, int, size_t, int, int);
! 205: void neo_free(void *, void *, int);
! 206: size_t neo_round_buffersize(void *, int, size_t);
! 207: int neo_get_props(void *);
! 208: void neo_set_mixer(struct neo_softc *sc, int a, int d);
! 209: void neo_power(int why, void *arg);
! 210:
! 211:
! 212: struct cfdriver neo_cd = {
! 213: NULL, "neo", DV_DULL
! 214: };
! 215:
! 216:
! 217: struct cfattach neo_ca = {
! 218: sizeof(struct neo_softc), neo_match, neo_attach
! 219: };
! 220:
! 221:
! 222: struct audio_device neo_device = {
! 223: "NeoMagic 256",
! 224: "",
! 225: "neo"
! 226: };
! 227:
! 228: #if 0
! 229: static u_int32_t badcards[] = {
! 230: 0x0007103c,
! 231: 0x008f1028,
! 232: };
! 233: #endif
! 234:
! 235: #define NUM_BADCARDS (sizeof(badcards) / sizeof(u_int32_t))
! 236:
! 237: /* The actual rates supported by the card. */
! 238: static int samplerates[9] = {
! 239: 8000,
! 240: 11025,
! 241: 16000,
! 242: 22050,
! 243: 24000,
! 244: 32000,
! 245: 44100,
! 246: 48000,
! 247: 99999999
! 248: };
! 249:
! 250: /* -------------------------------------------------------------------- */
! 251:
! 252: struct audio_hw_if neo_hw_if = {
! 253: neo_open,
! 254: neo_close,
! 255: NULL,
! 256: neo_query_encoding,
! 257: neo_set_params,
! 258: #if 1
! 259: neo_round_blocksize,
! 260: #else
! 261: NULL,
! 262: #endif
! 263: NULL,
! 264: NULL,
! 265: NULL,
! 266: NULL,
! 267: NULL,
! 268: neo_halt_output,
! 269: neo_halt_input,
! 270: NULL,
! 271: neo_getdev,
! 272: NULL,
! 273: neo_mixer_set_port,
! 274: neo_mixer_get_port,
! 275: neo_query_devinfo,
! 276: neo_malloc,
! 277: neo_free,
! 278: neo_round_buffersize,
! 279: 0, /* neo_mappage, */
! 280: neo_get_props,
! 281: neo_trigger_output,
! 282: neo_trigger_input,
! 283:
! 284: };
! 285:
! 286: /* -------------------------------------------------------------------- */
! 287:
! 288: /* Hardware */
! 289: static u_int32_t
! 290: nm_rd(struct neo_softc *sc, int regno, int size)
! 291: {
! 292: bus_space_tag_t st = sc->regiot;
! 293: bus_space_handle_t sh = sc->regioh;
! 294:
! 295: switch (size) {
! 296: case 1:
! 297: return bus_space_read_1(st, sh, regno);
! 298: case 2:
! 299: return bus_space_read_2(st, sh, regno);
! 300: case 4:
! 301: return bus_space_read_4(st, sh, regno);
! 302: default:
! 303: return (0xffffffff);
! 304: }
! 305: }
! 306:
! 307: static void
! 308: nm_wr(struct neo_softc *sc, int regno, u_int32_t data, int size)
! 309: {
! 310: bus_space_tag_t st = sc->regiot;
! 311: bus_space_handle_t sh = sc->regioh;
! 312:
! 313: switch (size) {
! 314: case 1:
! 315: bus_space_write_1(st, sh, regno, data);
! 316: break;
! 317: case 2:
! 318: bus_space_write_2(st, sh, regno, data);
! 319: break;
! 320: case 4:
! 321: bus_space_write_4(st, sh, regno, data);
! 322: break;
! 323: }
! 324: }
! 325:
! 326: static u_int32_t
! 327: nm_rdbuf(struct neo_softc *sc, int regno, int size)
! 328: {
! 329: bus_space_tag_t st = sc->bufiot;
! 330: bus_space_handle_t sh = sc->bufioh;
! 331:
! 332: switch (size) {
! 333: case 1:
! 334: return bus_space_read_1(st, sh, regno);
! 335: case 2:
! 336: return bus_space_read_2(st, sh, regno);
! 337: case 4:
! 338: return bus_space_read_4(st, sh, regno);
! 339: default:
! 340: return (0xffffffff);
! 341: }
! 342: }
! 343:
! 344: static void
! 345: nm_wrbuf(struct neo_softc *sc, int regno, u_int32_t data, int size)
! 346: {
! 347: bus_space_tag_t st = sc->bufiot;
! 348: bus_space_handle_t sh = sc->bufioh;
! 349:
! 350: switch (size) {
! 351: case 1:
! 352: bus_space_write_1(st, sh, regno, data);
! 353: break;
! 354: case 2:
! 355: bus_space_write_2(st, sh, regno, data);
! 356: break;
! 357: case 4:
! 358: bus_space_write_4(st, sh, regno, data);
! 359: break;
! 360: }
! 361: }
! 362:
! 363: /* ac97 codec */
! 364: static int
! 365: nm_waitcd(struct neo_softc *sc)
! 366: {
! 367: int cnt = 10;
! 368: int fail = 1;
! 369:
! 370: while (cnt-- > 0) {
! 371: if (nm_rd(sc, sc->ac97_status, 2) & sc->ac97_busy)
! 372: DELAY(100);
! 373: else {
! 374: fail = 0;
! 375: break;
! 376: }
! 377: }
! 378: return (fail);
! 379: }
! 380:
! 381:
! 382: static void
! 383: nm_ackint(struct neo_softc *sc, u_int32_t num)
! 384: {
! 385: if (sc->type == NM256AV_PCI_ID)
! 386: nm_wr(sc, NM_INT_REG, num << 1, 2);
! 387: else if (sc->type == NM256ZX_PCI_ID)
! 388: nm_wr(sc, NM_INT_REG, num, 4);
! 389: }
! 390:
! 391: static int
! 392: nm_loadcoeff(struct neo_softc *sc, int dir, int num)
! 393: {
! 394: int ofs, sz, i;
! 395: u_int32_t addr;
! 396:
! 397: if (nf == NULL) {
! 398: size_t buflen;
! 399: u_char *buf;
! 400: int error;
! 401:
! 402: error = loadfirmware("neo-coefficients", &buf, &buflen);
! 403: if (error)
! 404: return (error);
! 405: nf = (struct neo_firmware *)buf;
! 406: }
! 407:
! 408: addr = (dir == AUMODE_PLAY)? 0x01c : 0x21c;
! 409: if (dir == AUMODE_RECORD)
! 410: num += 8;
! 411: sz = nf->coefficientSizes[num];
! 412: ofs = 0;
! 413: while (num-- > 0)
! 414: ofs+= nf->coefficientSizes[num];
! 415: for (i = 0; i < sz; i++)
! 416: nm_wrbuf(sc, sc->cbuf + i, nf->coefficients[ofs + i], 1);
! 417: nm_wr(sc, addr, sc->cbuf, 4);
! 418: if (dir == AUMODE_PLAY)
! 419: sz--;
! 420: nm_wr(sc, addr + 4, sc->cbuf + sz, 4);
! 421: return (0);
! 422: }
! 423:
! 424: int
! 425: nmchan_getptr(sc, mode)
! 426: struct neo_softc *sc;
! 427: int mode;
! 428: {
! 429: if (mode == AUMODE_PLAY)
! 430: return (nm_rd(sc, NM_PBUFFER_CURRP, 4) - sc->pbuf);
! 431: else
! 432: return (nm_rd(sc, NM_RBUFFER_CURRP, 4) - sc->rbuf);
! 433: }
! 434:
! 435:
! 436: /* The interrupt handler */
! 437: int
! 438: neo_intr(void *p)
! 439: {
! 440: struct neo_softc *sc = (struct neo_softc *)p;
! 441: int status, x;
! 442: int rv = 0;
! 443:
! 444: status = nm_rd(sc, NM_INT_REG, sc->irsz);
! 445:
! 446: if (status & sc->playint) {
! 447: status &= ~sc->playint;
! 448:
! 449: sc->pwmark += sc->pblksize;
! 450: sc->pwmark %= sc->pbufsize;
! 451:
! 452: nm_wr(sc, NM_PBUFFER_WMARK, sc->pbuf + sc->pwmark, 4);
! 453:
! 454: nm_ackint(sc, sc->playint);
! 455:
! 456: if (sc->pintr)
! 457: (*sc->pintr)(sc->parg);
! 458:
! 459: rv = 1;
! 460: }
! 461: if (status & sc->recint) {
! 462: status &= ~sc->recint;
! 463:
! 464: sc->rwmark += sc->rblksize;
! 465: sc->rwmark %= sc->rbufsize;
! 466:
! 467: nm_ackint(sc, sc->recint);
! 468: if (sc->rintr)
! 469: (*sc->rintr)(sc->rarg);
! 470:
! 471: rv = 1;
! 472: }
! 473: if (status & sc->misc1int) {
! 474: status &= ~sc->misc1int;
! 475: nm_ackint(sc, sc->misc1int);
! 476: x = nm_rd(sc, 0x400, 1);
! 477: nm_wr(sc, 0x400, x | 2, 1);
! 478: printf("%s: misc int 1\n", sc->dev.dv_xname);
! 479: rv = 1;
! 480: }
! 481: if (status & sc->misc2int) {
! 482: status &= ~sc->misc2int;
! 483: nm_ackint(sc, sc->misc2int);
! 484: x = nm_rd(sc, 0x400, 1);
! 485: nm_wr(sc, 0x400, x & ~2, 1);
! 486: printf("%s: misc int 2\n", sc->dev.dv_xname);
! 487: rv = 1;
! 488: }
! 489: if (status) {
! 490: status &= ~sc->misc2int;
! 491: nm_ackint(sc, sc->misc2int);
! 492: printf("%s: unknown int\n", sc->dev.dv_xname);
! 493: rv = 1;
! 494: }
! 495:
! 496: return (rv);
! 497: }
! 498:
! 499: /* -------------------------------------------------------------------- */
! 500:
! 501: /*
! 502: * Probe and attach the card
! 503: */
! 504:
! 505: static int
! 506: nm_init(struct neo_softc *sc)
! 507: {
! 508: u_int32_t ofs, i;
! 509:
! 510: if (sc->type == NM256AV_PCI_ID) {
! 511: sc->ac97_base = NM_MIXER_OFFSET;
! 512: sc->ac97_status = NM_MIXER_STATUS_OFFSET;
! 513: sc->ac97_busy = NM_MIXER_READY_MASK;
! 514:
! 515: sc->buftop = 2560 * 1024;
! 516:
! 517: sc->irsz = 2;
! 518: sc->playint = NM_PLAYBACK_INT;
! 519: sc->recint = NM_RECORD_INT;
! 520: sc->misc1int = NM_MISC_INT_1;
! 521: sc->misc2int = NM_MISC_INT_2;
! 522: } else if (sc->type == NM256ZX_PCI_ID) {
! 523: sc->ac97_base = NM_MIXER_OFFSET;
! 524: sc->ac97_status = NM2_MIXER_STATUS_OFFSET;
! 525: sc->ac97_busy = NM2_MIXER_READY_MASK;
! 526:
! 527: sc->buftop = (nm_rd(sc, 0xa0b, 2)? 6144 : 4096) * 1024;
! 528:
! 529: sc->irsz = 4;
! 530: sc->playint = NM2_PLAYBACK_INT;
! 531: sc->recint = NM2_RECORD_INT;
! 532: sc->misc1int = NM2_MISC_INT_1;
! 533: sc->misc2int = NM2_MISC_INT_2;
! 534: } else return -1;
! 535: sc->badintr = 0;
! 536: ofs = sc->buftop - 0x0400;
! 537: sc->buftop -= 0x1400;
! 538:
! 539: if ((nm_rdbuf(sc, ofs, 4) & NM_SIG_MASK) == NM_SIGNATURE) {
! 540: i = nm_rdbuf(sc, ofs + 4, 4);
! 541: if (i != 0 && i != 0xffffffff)
! 542: sc->buftop = i;
! 543: }
! 544:
! 545: sc->cbuf = sc->buftop - NM_MAX_COEFFICIENT;
! 546: sc->rbuf = sc->cbuf - NM_BUFFSIZE;
! 547: sc->pbuf = sc->rbuf - NM_BUFFSIZE;
! 548: sc->acbuf = sc->pbuf - (NM_TOTAL_COEFF_COUNT * 4);
! 549:
! 550: nm_wr(sc, 0, 0x11, 1);
! 551: nm_wr(sc, NM_RECORD_ENABLE_REG, 0, 1);
! 552: nm_wr(sc, 0x214, 0, 2);
! 553:
! 554: return 0;
! 555: }
! 556:
! 557:
! 558: void
! 559: neo_attach(parent, self, aux)
! 560: struct device *parent;
! 561: struct device *self;
! 562: void *aux;
! 563: {
! 564: struct neo_softc *sc = (struct neo_softc *)self;
! 565: struct pci_attach_args *pa = (struct pci_attach_args *)aux;
! 566: pci_chipset_tag_t pc = pa->pa_pc;
! 567: char const *intrstr;
! 568: pci_intr_handle_t ih;
! 569: int error;
! 570:
! 571: sc->type = pa->pa_id;
! 572:
! 573: /* Map I/O register */
! 574: if (pci_mapreg_map(pa, PCI_MAPS, PCI_MAPREG_TYPE_MEM, 0,
! 575: &sc->bufiot, &sc->bufioh, NULL, NULL, 0)) {
! 576: printf("\n%s: can't map i/o space\n", sc->dev.dv_xname);
! 577: return;
! 578: }
! 579:
! 580:
! 581: if (pci_mapreg_map(pa, PCI_MAPS + 4, PCI_MAPREG_TYPE_MEM, 0,
! 582: &sc->regiot, &sc->regioh, NULL, NULL, 0)) {
! 583: printf("\n%s: can't map i/o space\n", sc->dev.dv_xname);
! 584: return;
! 585: }
! 586:
! 587: /* Map and establish the interrupt. */
! 588: if (pci_intr_map(pa, &ih)) {
! 589: printf("\n%s: couldn't map interrupt\n", sc->dev.dv_xname);
! 590: return;
! 591: }
! 592: intrstr = pci_intr_string(pc, ih);
! 593: sc->ih = pci_intr_establish(pc, ih, IPL_AUDIO, neo_intr, sc,
! 594: sc->dev.dv_xname);
! 595:
! 596: if (sc->ih == NULL) {
! 597: printf("\n%s: couldn't establish interrupt",
! 598: sc->dev.dv_xname);
! 599: if (intrstr != NULL)
! 600: printf(" at %s", intrstr);
! 601: printf("\n");
! 602: return;
! 603: }
! 604: printf(": %s\n", intrstr);
! 605:
! 606: if ((error = nm_init(sc)) != 0)
! 607: return;
! 608:
! 609: sc->host_if.arg = sc;
! 610:
! 611: sc->host_if.attach = neo_attach_codec;
! 612: sc->host_if.read = neo_read_codec;
! 613: sc->host_if.write = neo_write_codec;
! 614: sc->host_if.reset = neo_reset_codec;
! 615: sc->host_if.flags = neo_flags_codec;
! 616:
! 617: if ((error = ac97_attach(&sc->host_if)) != 0)
! 618: return;
! 619:
! 620: sc->powerhook = powerhook_establish(neo_power, sc);
! 621:
! 622: audio_attach_mi(&neo_hw_if, sc, &sc->dev);
! 623:
! 624: return;
! 625: }
! 626:
! 627: void
! 628: neo_power(int why, void *addr)
! 629: {
! 630: struct neo_softc *sc = (struct neo_softc *)addr;
! 631:
! 632: if (why == PWR_RESUME) {
! 633: nm_init(sc);
! 634: (sc->codec_if->vtbl->restore_ports)(sc->codec_if);
! 635: }
! 636: }
! 637:
! 638:
! 639:
! 640: int
! 641: neo_match(parent, match, aux)
! 642: struct device *parent;
! 643: void *match;
! 644: void *aux;
! 645: {
! 646: struct pci_attach_args *pa = (struct pci_attach_args *) aux;
! 647: #if 0
! 648: u_int32_t subdev, badcard;
! 649: #endif
! 650:
! 651: if (PCI_VENDOR(pa->pa_id) != PCI_VENDOR_NEOMAGIC)
! 652: return (0);
! 653:
! 654: #if 0
! 655: subdev = (pci_get_subdevice(dev) << 16) | pci_get_subvendor(dev);
! 656: #endif
! 657: switch (PCI_PRODUCT(pa->pa_id)) {
! 658: case PCI_PRODUCT_NEOMAGIC_NM256AV:
! 659: #if 0
! 660: i = 0;
! 661: while ((i < NUM_BADCARDS) && (badcards[i] != subdev))
! 662: i++;
! 663: if (i == NUM_BADCARDS)
! 664: s = "NeoMagic 256AV";
! 665: DEB(else)
! 666: DEB(device_printf(dev, "this is a non-ac97 NM256AV, not attaching\n"));
! 667: return (1);
! 668: #endif
! 669: case PCI_PRODUCT_NEOMAGIC_NM256ZX:
! 670: return (1);
! 671: }
! 672:
! 673: return (0);
! 674: }
! 675:
! 676: int
! 677: neo_read_codec(sc_, a, d)
! 678: void *sc_;
! 679: u_int8_t a;
! 680: u_int16_t *d;
! 681: {
! 682: struct neo_softc *sc = sc_;
! 683:
! 684: if (!nm_waitcd(sc)) {
! 685: *d = nm_rd(sc, sc->ac97_base + a, 2);
! 686: DELAY(1000);
! 687: return 0;
! 688: }
! 689:
! 690: return (ENXIO);
! 691: }
! 692:
! 693:
! 694: int
! 695: neo_write_codec(sc_, a, d)
! 696: void *sc_;
! 697: u_int8_t a;
! 698: u_int16_t d;
! 699: {
! 700: struct neo_softc *sc = sc_;
! 701: int cnt = 3;
! 702:
! 703: if (!nm_waitcd(sc)) {
! 704: while (cnt-- > 0) {
! 705: nm_wr(sc, sc->ac97_base + a, d, 2);
! 706: if (!nm_waitcd(sc)) {
! 707: DELAY(1000);
! 708: return (0);
! 709: }
! 710: }
! 711: }
! 712:
! 713: return (ENXIO);
! 714: }
! 715:
! 716:
! 717: int
! 718: neo_attach_codec(sc_, codec_if)
! 719: void *sc_;
! 720: struct ac97_codec_if *codec_if;
! 721: {
! 722: struct neo_softc *sc = sc_;
! 723:
! 724: sc->codec_if = codec_if;
! 725: return (0);
! 726: }
! 727:
! 728: void
! 729: neo_reset_codec(sc)
! 730: void *sc;
! 731: {
! 732: nm_wr(sc, 0x6c0, 0x01, 1);
! 733: nm_wr(sc, 0x6cc, 0x87, 1);
! 734: nm_wr(sc, 0x6cc, 0x80, 1);
! 735: nm_wr(sc, 0x6cc, 0x00, 1);
! 736:
! 737: return;
! 738: }
! 739:
! 740:
! 741: enum ac97_host_flags
! 742: neo_flags_codec(sc)
! 743: void *sc;
! 744: {
! 745: return (AC97_HOST_DONT_READANY);
! 746: }
! 747:
! 748: int
! 749: neo_open(addr, flags)
! 750: void *addr;
! 751: int flags;
! 752: {
! 753: return (0);
! 754: }
! 755:
! 756: /*
! 757: * Close function is called at splaudio().
! 758: */
! 759: void
! 760: neo_close(addr)
! 761: void *addr;
! 762: {
! 763: struct neo_softc *sc = addr;
! 764:
! 765: neo_halt_output(sc);
! 766: neo_halt_input(sc);
! 767:
! 768: sc->pintr = 0;
! 769: sc->rintr = 0;
! 770: }
! 771:
! 772: int
! 773: neo_query_encoding(addr, fp)
! 774: void *addr;
! 775: struct audio_encoding *fp;
! 776: {
! 777: switch (fp->index) {
! 778: case 0:
! 779: strlcpy(fp->name, AudioEulinear, sizeof fp->name);
! 780: fp->encoding = AUDIO_ENCODING_ULINEAR;
! 781: fp->precision = 8;
! 782: fp->flags = 0;
! 783: return (0);
! 784: case 1:
! 785: strlcpy(fp->name, AudioEmulaw, sizeof fp->name);
! 786: fp->encoding = AUDIO_ENCODING_ULAW;
! 787: fp->precision = 8;
! 788: fp->flags = AUDIO_ENCODINGFLAG_EMULATED;
! 789: return (0);
! 790: case 2:
! 791: strlcpy(fp->name, AudioEalaw, sizeof fp->name);
! 792: fp->encoding = AUDIO_ENCODING_ALAW;
! 793: fp->precision = 8;
! 794: fp->flags = AUDIO_ENCODINGFLAG_EMULATED;
! 795: return (0);
! 796: case 3:
! 797: strlcpy(fp->name, AudioEslinear, sizeof fp->name);
! 798: fp->encoding = AUDIO_ENCODING_SLINEAR;
! 799: fp->precision = 8;
! 800: fp->flags = AUDIO_ENCODINGFLAG_EMULATED;
! 801: return (0);
! 802: case 4:
! 803: strlcpy(fp->name, AudioEslinear_le, sizeof fp->name);
! 804: fp->encoding = AUDIO_ENCODING_SLINEAR_LE;
! 805: fp->precision = 16;
! 806: fp->flags = 0;
! 807: return (0);
! 808: case 5:
! 809: strlcpy(fp->name, AudioEulinear_le, sizeof fp->name);
! 810: fp->encoding = AUDIO_ENCODING_ULINEAR_LE;
! 811: fp->precision = 16;
! 812: fp->flags = AUDIO_ENCODINGFLAG_EMULATED;
! 813: return (0);
! 814: case 6:
! 815: strlcpy(fp->name, AudioEslinear_be, sizeof fp->name);
! 816: fp->encoding = AUDIO_ENCODING_SLINEAR_BE;
! 817: fp->precision = 16;
! 818: fp->flags = AUDIO_ENCODINGFLAG_EMULATED;
! 819: return (0);
! 820: case 7:
! 821: strlcpy(fp->name, AudioEulinear_be, sizeof fp->name);
! 822: fp->encoding = AUDIO_ENCODING_ULINEAR_BE;
! 823: fp->precision = 16;
! 824: fp->flags = AUDIO_ENCODINGFLAG_EMULATED;
! 825: return (0);
! 826: default:
! 827: return (EINVAL);
! 828: }
! 829: }
! 830:
! 831: /* Todo: don't commit settings to card until we've verified all parameters */
! 832: int
! 833: neo_set_params(addr, setmode, usemode, play, rec)
! 834: void *addr;
! 835: int setmode, usemode;
! 836: struct audio_params *play, *rec;
! 837: {
! 838: struct neo_softc *sc = addr;
! 839: u_int32_t base;
! 840: u_int8_t x;
! 841: int mode;
! 842: struct audio_params *p;
! 843:
! 844: for (mode = AUMODE_RECORD; mode != -1;
! 845: mode = mode == AUMODE_RECORD ? AUMODE_PLAY : -1) {
! 846: if ((setmode & mode) == 0)
! 847: continue;
! 848:
! 849: p = mode == AUMODE_PLAY ? play : rec;
! 850:
! 851: if (p == NULL) continue;
! 852:
! 853: for (x = 0; x < 8; x++)
! 854: if (p->sample_rate < (samplerates[x] + samplerates[x + 1]) / 2)
! 855: break;
! 856:
! 857: if (x == 8) return (EINVAL);
! 858:
! 859: p->sample_rate = samplerates[x];
! 860: nm_loadcoeff(sc, mode, x);
! 861:
! 862: x <<= 4;
! 863: x &= NM_RATE_MASK;
! 864: if (p->precision == 16) x |= NM_RATE_BITS_16;
! 865: if (p->channels == 2) x |= NM_RATE_STEREO;
! 866:
! 867: base = (mode == AUMODE_PLAY) ?
! 868: NM_PLAYBACK_REG_OFFSET : NM_RECORD_REG_OFFSET;
! 869: nm_wr(sc, base + NM_RATE_REG_OFFSET, x, 1);
! 870:
! 871: p->factor = 1;
! 872: p->sw_code = 0;
! 873: switch (p->encoding) {
! 874: case AUDIO_ENCODING_SLINEAR_BE:
! 875: if (p->precision == 16)
! 876: p->sw_code = swap_bytes;
! 877: else
! 878: p->sw_code = change_sign8;
! 879: break;
! 880: case AUDIO_ENCODING_SLINEAR_LE:
! 881: if (p->precision != 16)
! 882: p->sw_code = change_sign8;
! 883: break;
! 884: case AUDIO_ENCODING_ULINEAR_BE:
! 885: if (p->precision == 16) {
! 886: if (mode == AUMODE_PLAY)
! 887: p->sw_code = swap_bytes_change_sign16;
! 888: else
! 889: p->sw_code = change_sign16_swap_bytes;
! 890: }
! 891: break;
! 892: case AUDIO_ENCODING_ULINEAR_LE:
! 893: if (p->precision == 16)
! 894: p->sw_code = change_sign16;
! 895: break;
! 896: case AUDIO_ENCODING_ULAW:
! 897: if (mode == AUMODE_PLAY) {
! 898: p->factor = 2;
! 899: p->sw_code = mulaw_to_slinear16;
! 900: } else
! 901: p->sw_code = ulinear8_to_mulaw;
! 902: break;
! 903: case AUDIO_ENCODING_ALAW:
! 904: if (mode == AUMODE_PLAY) {
! 905: p->factor = 2;
! 906: p->sw_code = alaw_to_slinear16;
! 907: } else
! 908: p->sw_code = ulinear8_to_alaw;
! 909: break;
! 910: default:
! 911: return (EINVAL);
! 912: }
! 913: }
! 914:
! 915:
! 916: return (0);
! 917: }
! 918:
! 919: int
! 920: neo_round_blocksize(addr, blk)
! 921: void *addr;
! 922: int blk;
! 923: {
! 924: return (NM_BUFFSIZE / 2);
! 925: }
! 926:
! 927: int
! 928: neo_trigger_output(addr, start, end, blksize, intr, arg, param)
! 929: void *addr;
! 930: void *start, *end;
! 931: int blksize;
! 932: void (*intr)(void *);
! 933: void *arg;
! 934: struct audio_params *param;
! 935: {
! 936: struct neo_softc *sc = addr;
! 937: int ssz;
! 938:
! 939: sc->pintr = intr;
! 940: sc->parg = arg;
! 941:
! 942: ssz = (param->precision * param->factor == 16)? 2 : 1;
! 943: if (param->channels == 2)
! 944: ssz <<= 1;
! 945:
! 946: sc->pbufsize = ((char *)end - (char *)start);
! 947: sc->pblksize = blksize;
! 948: sc->pwmark = blksize;
! 949:
! 950: nm_wr(sc, NM_PBUFFER_START, sc->pbuf, 4);
! 951: nm_wr(sc, NM_PBUFFER_END, sc->pbuf + sc->pbufsize - ssz, 4);
! 952: nm_wr(sc, NM_PBUFFER_CURRP, sc->pbuf, 4);
! 953: nm_wr(sc, NM_PBUFFER_WMARK, sc->pbuf + sc->pwmark, 4);
! 954: nm_wr(sc, NM_PLAYBACK_ENABLE_REG, NM_PLAYBACK_FREERUN |
! 955: NM_PLAYBACK_ENABLE_FLAG, 1);
! 956: nm_wr(sc, NM_AUDIO_MUTE_REG, 0, 2);
! 957:
! 958: return (0);
! 959: }
! 960:
! 961:
! 962:
! 963: int
! 964: neo_trigger_input(addr, start, end, blksize, intr, arg, param)
! 965: void *addr;
! 966: void *start, *end;
! 967: int blksize;
! 968: void (*intr)(void *);
! 969: void *arg;
! 970: struct audio_params *param;
! 971: {
! 972: struct neo_softc *sc = addr;
! 973: int ssz;
! 974:
! 975: sc->rintr = intr;
! 976: sc->rarg = arg;
! 977:
! 978: ssz = (param->precision * param->factor == 16)? 2 : 1;
! 979: if (param->channels == 2)
! 980: ssz <<= 1;
! 981:
! 982: sc->rbufsize = ((char *)end - (char *)start);
! 983: sc->rblksize = blksize;
! 984: sc->rwmark = blksize;
! 985:
! 986: nm_wr(sc, NM_RBUFFER_START, sc->rbuf, 4);
! 987: nm_wr(sc, NM_RBUFFER_END, sc->rbuf + sc->rbufsize, 4);
! 988: nm_wr(sc, NM_RBUFFER_CURRP, sc->rbuf, 4);
! 989: nm_wr(sc, NM_RBUFFER_WMARK, sc->rbuf + sc->rwmark, 4);
! 990: nm_wr(sc, NM_RECORD_ENABLE_REG, NM_RECORD_FREERUN |
! 991: NM_RECORD_ENABLE_FLAG, 1);
! 992:
! 993: return (0);
! 994: }
! 995:
! 996: int
! 997: neo_halt_output(addr)
! 998: void *addr;
! 999: {
! 1000: struct neo_softc *sc = (struct neo_softc *)addr;
! 1001:
! 1002: nm_wr(sc, NM_PLAYBACK_ENABLE_REG, 0, 1);
! 1003: nm_wr(sc, NM_AUDIO_MUTE_REG, NM_AUDIO_MUTE_BOTH, 2);
! 1004:
! 1005: sc->pintr = 0;
! 1006:
! 1007: return (0);
! 1008: }
! 1009:
! 1010: int
! 1011: neo_halt_input(addr)
! 1012: void *addr;
! 1013: {
! 1014: struct neo_softc *sc = (struct neo_softc *)addr;
! 1015:
! 1016: nm_wr(sc, NM_RECORD_ENABLE_REG, 0, 1);
! 1017:
! 1018: sc->rintr = 0;
! 1019:
! 1020: return (0);
! 1021: }
! 1022:
! 1023: int
! 1024: neo_getdev(addr, retp)
! 1025: void *addr;
! 1026: struct audio_device *retp;
! 1027: {
! 1028: *retp = neo_device;
! 1029: return (0);
! 1030: }
! 1031:
! 1032: int
! 1033: neo_mixer_set_port(addr, cp)
! 1034: void *addr;
! 1035: mixer_ctrl_t *cp;
! 1036: {
! 1037: struct neo_softc *sc = addr;
! 1038:
! 1039: return ((sc->codec_if->vtbl->mixer_set_port)(sc->codec_if, cp));
! 1040: }
! 1041:
! 1042: int
! 1043: neo_mixer_get_port(addr, cp)
! 1044: void *addr;
! 1045: mixer_ctrl_t *cp;
! 1046: {
! 1047: struct neo_softc *sc = addr;
! 1048:
! 1049: return ((sc->codec_if->vtbl->mixer_get_port)(sc->codec_if, cp));
! 1050: }
! 1051:
! 1052: int
! 1053: neo_query_devinfo(addr, dip)
! 1054: void *addr;
! 1055: mixer_devinfo_t *dip;
! 1056: {
! 1057: struct neo_softc *sc = addr;
! 1058:
! 1059: return ((sc->codec_if->vtbl->query_devinfo)(sc->codec_if, dip));
! 1060: }
! 1061:
! 1062: void *
! 1063: neo_malloc(addr, direction, size, pool, flags)
! 1064: void *addr;
! 1065: int direction;
! 1066: size_t size;
! 1067: int pool, flags;
! 1068: {
! 1069: struct neo_softc *sc = addr;
! 1070: void *rv = 0;
! 1071:
! 1072: switch (direction) {
! 1073: case AUMODE_PLAY:
! 1074: rv = (char *)sc->bufioh + sc->pbuf;
! 1075: break;
! 1076: case AUMODE_RECORD:
! 1077: rv = (char *)sc->bufioh + sc->rbuf;
! 1078: break;
! 1079: default:
! 1080: break;
! 1081: }
! 1082:
! 1083: return (rv);
! 1084: }
! 1085:
! 1086: void
! 1087: neo_free(addr, ptr, pool)
! 1088: void *addr;
! 1089: void *ptr;
! 1090: int pool;
! 1091: {
! 1092: return;
! 1093: }
! 1094:
! 1095: size_t
! 1096: neo_round_buffersize(addr, direction, size)
! 1097: void *addr;
! 1098: int direction;
! 1099: size_t size;
! 1100: {
! 1101: return (NM_BUFFSIZE);
! 1102: }
! 1103:
! 1104:
! 1105: int
! 1106: neo_get_props(addr)
! 1107: void *addr;
! 1108: {
! 1109:
! 1110: return (AUDIO_PROP_INDEPENDENT | AUDIO_PROP_FULLDUPLEX);
! 1111: }
CVSweb