Annotation of sys/dev/pci/auvia.c, Revision 1.1
1.1 ! nbrk 1: /* $OpenBSD: auvia.c,v 1.33 2005/05/06 01:45:22 miod Exp $ */
! 2: /* $NetBSD: auvia.c,v 1.7 2000/11/15 21:06:33 jdolecek Exp $ */
! 3:
! 4: /*-
! 5: * Copyright (c) 2000 The NetBSD Foundation, Inc.
! 6: * All rights reserved.
! 7: *
! 8: * This code is derived from software contributed to The NetBSD Foundation
! 9: * by Tyler C. Sarna
! 10: *
! 11: * Redistribution and use in source and binary forms, with or without
! 12: * modification, are permitted provided that the following conditions
! 13: * are met:
! 14: * 1. Redistributions of source code must retain the above copyright
! 15: * notice, this list of conditions and the following disclaimer.
! 16: * 2. Redistributions in binary form must reproduce the above copyright
! 17: * notice, this list of conditions and the following disclaimer in the
! 18: * documentation and/or other materials provided with the distribution.
! 19: * 3. All advertising materials mentioning features or use of this software
! 20: * must display the following acknowledgement:
! 21: * This product includes software developed by the NetBSD
! 22: * Foundation, Inc. and its contributors.
! 23: * 4. Neither the name of The NetBSD Foundation nor the names of its
! 24: * contributors may be used to endorse or promote products derived
! 25: * from this software without specific prior written permission.
! 26: *
! 27: * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
! 28: * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
! 29: * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
! 30: * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
! 31: * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
! 32: * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
! 33: * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
! 34: * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
! 35: * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
! 36: * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
! 37: * POSSIBILITY OF SUCH DAMAGE.
! 38: */
! 39:
! 40: /*
! 41: * VIA Technologies VT82C686A Southbridge Audio Driver
! 42: *
! 43: * Documentation links:
! 44: *
! 45: * ftp://ftp.alsa-project.org/pub/manuals/via/686a.pdf
! 46: */
! 47:
! 48: #include <sys/param.h>
! 49: #include <sys/systm.h>
! 50: #include <sys/malloc.h>
! 51: #include <sys/device.h>
! 52: #include <sys/audioio.h>
! 53:
! 54: #include <dev/pci/pcidevs.h>
! 55: #include <dev/pci/pcivar.h>
! 56:
! 57: #include <dev/audio_if.h>
! 58: #include <dev/mulaw.h>
! 59: #include <dev/auconv.h>
! 60:
! 61: #include <dev/ic/ac97.h>
! 62:
! 63: #include <dev/pci/auviavar.h>
! 64:
! 65: struct auvia_dma {
! 66: struct auvia_dma *next;
! 67: caddr_t addr;
! 68: size_t size;
! 69: bus_dmamap_t map;
! 70: bus_dma_segment_t seg;
! 71: };
! 72:
! 73: struct auvia_dma_op {
! 74: u_int32_t ptr;
! 75: u_int32_t flags;
! 76: #define AUVIA_DMAOP_EOL 0x80000000
! 77: #define AUVIA_DMAOP_FLAG 0x40000000
! 78: #define AUVIA_DMAOP_STOP 0x20000000
! 79: #define AUVIA_DMAOP_COUNT(x) ((x)&0x00FFFFFF)
! 80: };
! 81:
! 82: /* rev. H and later seem to support only fixed rate 44.1 kHz */
! 83: #define AUVIA_FIXED_RATE 44100
! 84:
! 85: int auvia_match(struct device *, void *, void *);
! 86: void auvia_attach(struct device *, struct device *, void *);
! 87: int auvia_open(void *, int);
! 88: void auvia_close(void *);
! 89: int auvia_query_encoding(void *addr, struct audio_encoding *fp);
! 90: int auvia_set_params(void *, int, int, struct audio_params *,
! 91: struct audio_params *);
! 92: int auvia_round_blocksize(void *, int);
! 93: int auvia_halt_output(void *);
! 94: int auvia_halt_input(void *);
! 95: int auvia_getdev(void *, struct audio_device *);
! 96: int auvia_set_port(void *, mixer_ctrl_t *);
! 97: int auvia_get_port(void *, mixer_ctrl_t *);
! 98: int auvia_query_devinfo(void *, mixer_devinfo_t *);
! 99: void * auvia_malloc(void *, int, size_t, int, int);
! 100: void auvia_free(void *, void *, int);
! 101: paddr_t auvia_mappage(void *, void *, off_t, int);
! 102: int auvia_get_props(void *);
! 103: int auvia_build_dma_ops(struct auvia_softc *, struct auvia_softc_chan *,
! 104: struct auvia_dma *, void *, void *, int);
! 105: int auvia_trigger_output(void *, void *, void *, int, void (*)(void *),
! 106: void *, struct audio_params *);
! 107: int auvia_trigger_input(void *, void *, void *, int, void (*)(void *),
! 108: void *, struct audio_params *);
! 109:
! 110: int auvia_intr(void *);
! 111:
! 112: struct cfdriver auvia_cd = {
! 113: NULL, "auvia", DV_DULL
! 114: };
! 115:
! 116: struct cfattach auvia_ca = {
! 117: sizeof (struct auvia_softc), auvia_match, auvia_attach
! 118: };
! 119:
! 120: #define AUVIA_PCICONF_JUNK 0x40
! 121: #define AUVIA_PCICONF_ENABLES 0x00FF0000 /* reg 42 mask */
! 122: #define AUVIA_PCICONF_ACLINKENAB 0x00008000 /* ac link enab */
! 123: #define AUVIA_PCICONF_ACNOTRST 0x00004000 /* ~(ac reset) */
! 124: #define AUVIA_PCICONF_ACSYNC 0x00002000 /* ac sync */
! 125: #define AUVIA_PCICONF_ACVSR 0x00000800 /* var. samp. rate */
! 126: #define AUVIA_PCICONF_ACSGD 0x00000400 /* SGD enab */
! 127: #define AUVIA_PCICONF_ACFM 0x00000200 /* FM enab */
! 128: #define AUVIA_PCICONF_ACSB 0x00000100 /* SB enab */
! 129: #define AUVIA_PCICONF_PRIVALID 0x00000001 /* primary codec rdy */
! 130:
! 131: #define AUVIA_PLAY_BASE 0x00
! 132: #define AUVIA_RECORD_BASE 0x10
! 133:
! 134: #define AUVIA_RP_STAT 0x00
! 135: #define AUVIA_RPSTAT_INTR 0x03
! 136: #define AUVIA_RP_CONTROL 0x01
! 137: #define AUVIA_RPCTRL_START 0x80
! 138: #define AUVIA_RPCTRL_TERMINATE 0x40
! 139: #define AUVIA_RPCTRL_AUTOSTART 0x20
! 140: /* The following are 8233 specific */
! 141: #define AUVIA_RPCTRL_STOP 0x04
! 142: #define AUVIA_RPCTRL_EOL 0x02
! 143: #define AUVIA_RPCTRL_FLAG 0x01
! 144: #define AUVIA_RP_MODE 0x02
! 145: #define AUVIA_RPMODE_INTR_FLAG 0x01
! 146: #define AUVIA_RPMODE_INTR_EOL 0x02
! 147: #define AUVIA_RPMODE_STEREO 0x10
! 148: #define AUVIA_RPMODE_16BIT 0x20
! 149: #define AUVIA_RPMODE_AUTOSTART 0x80
! 150: #define AUVIA_RP_DMAOPS_BASE 0x04
! 151:
! 152: #define VIA8233_RP_DXS_LVOL 0x02
! 153: #define VIA8233_RP_DXS_RVOL 0x03
! 154: #define VIA8233_RP_RATEFMT 0x08
! 155: #define VIA8233_RATEFMT_48K 0xfffff
! 156: #define VIA8233_RATEFMT_STEREO 0x00100000
! 157: #define VIA8233_RATEFMT_16BIT 0x00200000
! 158:
! 159: #define VIA_RP_DMAOPS_COUNT 0x0C
! 160:
! 161: #define AUVIA_CODEC_CTL 0x80
! 162: #define AUVIA_CODEC_READ 0x00800000
! 163: #define AUVIA_CODEC_BUSY 0x01000000
! 164: #define AUVIA_CODEC_PRIVALID 0x02000000
! 165: #define AUVIA_CODEC_INDEX(x) ((x)<<16)
! 166:
! 167: #define TIMEOUT 50
! 168:
! 169: struct audio_hw_if auvia_hw_if = {
! 170: auvia_open,
! 171: auvia_close,
! 172: NULL, /* drain */
! 173: auvia_query_encoding,
! 174: auvia_set_params,
! 175: auvia_round_blocksize,
! 176: NULL, /* commit_settings */
! 177: NULL, /* init_output */
! 178: NULL, /* init_input */
! 179: NULL, /* start_output */
! 180: NULL, /* start_input */
! 181: auvia_halt_output,
! 182: auvia_halt_input,
! 183: NULL, /* speaker_ctl */
! 184: auvia_getdev,
! 185: NULL, /* setfd */
! 186: auvia_set_port,
! 187: auvia_get_port,
! 188: auvia_query_devinfo,
! 189: auvia_malloc,
! 190: auvia_free,
! 191: NULL, /* auvia_round_buffersize */
! 192: auvia_mappage,
! 193: auvia_get_props,
! 194: auvia_trigger_output,
! 195: auvia_trigger_input
! 196: };
! 197:
! 198: int auvia_attach_codec(void *, struct ac97_codec_if *);
! 199: int auvia_write_codec(void *, u_int8_t, u_int16_t);
! 200: int auvia_read_codec(void *, u_int8_t, u_int16_t *);
! 201: void auvia_reset_codec(void *);
! 202: int auvia_waitready_codec(struct auvia_softc *sc);
! 203: int auvia_waitvalid_codec(struct auvia_softc *sc);
! 204:
! 205: const struct pci_matchid auvia_devices[] = {
! 206: { PCI_VENDOR_VIATECH, PCI_PRODUCT_VIATECH_VT82C686A_AC97 },
! 207: { PCI_VENDOR_VIATECH, PCI_PRODUCT_VIATECH_VT8233_AC97 },
! 208: };
! 209:
! 210: int
! 211: auvia_match(struct device *parent, void *match, void *aux)
! 212: {
! 213: return (pci_matchbyid((struct pci_attach_args *)aux, auvia_devices,
! 214: sizeof(auvia_devices)/sizeof(auvia_devices[0])));
! 215: }
! 216:
! 217:
! 218: void
! 219: auvia_attach(struct device *parent, struct device *self, void *aux)
! 220: {
! 221: struct pci_attach_args *pa = aux;
! 222: struct auvia_softc *sc = (struct auvia_softc *) self;
! 223: const char *intrstr = NULL;
! 224: struct mixer_ctrl ctl;
! 225: pci_chipset_tag_t pc = pa->pa_pc;
! 226: pcitag_t pt = pa->pa_tag;
! 227: pci_intr_handle_t ih;
! 228: bus_size_t iosize;
! 229: pcireg_t pr;
! 230: int r, i;
! 231:
! 232: if (PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_VIATECH_VT8233_AC97)
! 233: sc->sc_flags |= AUVIA_FLAGS_VT8233;
! 234:
! 235: if (pci_mapreg_map(pa, 0x10, PCI_MAPREG_TYPE_IO, 0, &sc->sc_iot,
! 236: &sc->sc_ioh, NULL, &iosize, 0)) {
! 237: printf(": can't map i/o space\n");
! 238: return;
! 239: }
! 240:
! 241: sc->sc_dmat = pa->pa_dmat;
! 242: sc->sc_pc = pc;
! 243: sc->sc_pt = pt;
! 244:
! 245: if (pci_intr_map(pa, &ih)) {
! 246: printf(": couldn't map interrupt\n");
! 247: bus_space_unmap(sc->sc_iot, sc->sc_ioh, iosize);
! 248: return;
! 249: }
! 250: intrstr = pci_intr_string(pc, ih);
! 251:
! 252: sc->sc_ih = pci_intr_establish(pc, ih, IPL_AUDIO, auvia_intr, sc,
! 253: sc->sc_dev.dv_xname);
! 254: if (sc->sc_ih == NULL) {
! 255: printf(": couldn't establish interrupt");
! 256: if (intrstr != NULL)
! 257: printf(" at %s", intrstr);
! 258: printf("\n");
! 259: bus_space_unmap(sc->sc_iot, sc->sc_ioh, iosize);
! 260: return;
! 261: }
! 262:
! 263: printf(": %s\n", intrstr);
! 264:
! 265: /* disable SBPro compat & others */
! 266: pr = pci_conf_read(pc, pt, AUVIA_PCICONF_JUNK);
! 267:
! 268: pr &= ~AUVIA_PCICONF_ENABLES; /* clear compat function enables */
! 269: /* XXX what to do about MIDI, FM, joystick? */
! 270:
! 271: pr |= (AUVIA_PCICONF_ACLINKENAB | AUVIA_PCICONF_ACNOTRST |
! 272: AUVIA_PCICONF_ACVSR | AUVIA_PCICONF_ACSGD);
! 273:
! 274: pr &= ~(AUVIA_PCICONF_ACFM | AUVIA_PCICONF_ACSB);
! 275:
! 276: pci_conf_write(pc, pt, AUVIA_PCICONF_JUNK, pr);
! 277:
! 278: sc->host_if.arg = sc;
! 279: sc->host_if.attach = auvia_attach_codec;
! 280: sc->host_if.read = auvia_read_codec;
! 281: sc->host_if.write = auvia_write_codec;
! 282: sc->host_if.reset = auvia_reset_codec;
! 283:
! 284: if ((r = ac97_attach(&sc->host_if)) != 0) {
! 285: printf("%s: can't attach codec (error 0x%X)\n",
! 286: sc->sc_dev.dv_xname, r);
! 287: pci_intr_disestablish(pc, sc->sc_ih);
! 288: bus_space_unmap(sc->sc_iot, sc->sc_ioh, iosize);
! 289: return;
! 290: }
! 291:
! 292: /* disable mutes */
! 293: for (i = 0; i < 4; i++) {
! 294: static struct {
! 295: char *class, *device;
! 296: } d[] = {
! 297: { AudioCoutputs, AudioNmaster},
! 298: { AudioCinputs, AudioNdac},
! 299: { AudioCinputs, AudioNcd},
! 300: { AudioCrecord, AudioNvolume},
! 301: };
! 302:
! 303: ctl.type = AUDIO_MIXER_ENUM;
! 304: ctl.un.ord = 0;
! 305:
! 306: ctl.dev = sc->codec_if->vtbl->get_portnum_by_name(sc->codec_if,
! 307: d[i].class, d[i].device, AudioNmute);
! 308: auvia_set_port(sc, &ctl);
! 309: }
! 310:
! 311: /* set a reasonable default volume */
! 312:
! 313: ctl.type = AUDIO_MIXER_VALUE;
! 314: ctl.un.value.num_channels = 2;
! 315: ctl.un.value.level[AUDIO_MIXER_LEVEL_LEFT] = \
! 316: ctl.un.value.level[AUDIO_MIXER_LEVEL_RIGHT] = 199;
! 317:
! 318: ctl.dev = sc->codec_if->vtbl->get_portnum_by_name(sc->codec_if,
! 319: AudioCoutputs, AudioNmaster, NULL);
! 320: auvia_set_port(sc, &ctl);
! 321:
! 322: audio_attach_mi(&auvia_hw_if, sc, &sc->sc_dev);
! 323: }
! 324:
! 325:
! 326: int
! 327: auvia_attach_codec(void *addr, struct ac97_codec_if *cif)
! 328: {
! 329: struct auvia_softc *sc = addr;
! 330:
! 331: sc->codec_if = cif;
! 332:
! 333: return 0;
! 334: }
! 335:
! 336:
! 337: void
! 338: auvia_reset_codec(void *addr)
! 339: {
! 340: int i;
! 341: struct auvia_softc *sc = addr;
! 342: pcireg_t r;
! 343:
! 344: /* perform a codec cold reset */
! 345:
! 346: r = pci_conf_read(sc->sc_pc, sc->sc_pt, AUVIA_PCICONF_JUNK);
! 347:
! 348: r &= ~AUVIA_PCICONF_ACNOTRST; /* enable RESET (active low) */
! 349: pci_conf_write(sc->sc_pc, sc->sc_pt, AUVIA_PCICONF_JUNK, r);
! 350: delay(2);
! 351:
! 352: r |= AUVIA_PCICONF_ACNOTRST; /* disable RESET (inactive high) */
! 353: pci_conf_write(sc->sc_pc, sc->sc_pt, AUVIA_PCICONF_JUNK, r);
! 354: delay(200);
! 355:
! 356: for (i = 500000; i != 0 && !(pci_conf_read(sc->sc_pc, sc->sc_pt,
! 357: AUVIA_PCICONF_JUNK) & AUVIA_PCICONF_PRIVALID); i--)
! 358: DELAY(1);
! 359: if (i == 0)
! 360: printf("%s: codec reset timed out\n", sc->sc_dev.dv_xname);
! 361: }
! 362:
! 363:
! 364: int
! 365: auvia_waitready_codec(struct auvia_softc *sc)
! 366: {
! 367: int i;
! 368:
! 369: /* poll until codec not busy */
! 370: for (i = 0; (i < TIMEOUT) && (bus_space_read_4(sc->sc_iot, sc->sc_ioh,
! 371: AUVIA_CODEC_CTL) & AUVIA_CODEC_BUSY); i++)
! 372: delay(1);
! 373:
! 374: if (i >= TIMEOUT) {
! 375: printf("%s: codec busy\n", sc->sc_dev.dv_xname);
! 376: return 1;
! 377: }
! 378:
! 379: return 0;
! 380: }
! 381:
! 382:
! 383: int
! 384: auvia_waitvalid_codec(struct auvia_softc *sc)
! 385: {
! 386: int i;
! 387:
! 388: /* poll until codec valid */
! 389: for (i = 0; (i < TIMEOUT) && !(bus_space_read_4(sc->sc_iot, sc->sc_ioh,
! 390: AUVIA_CODEC_CTL) & AUVIA_CODEC_PRIVALID); i++)
! 391: delay(1);
! 392:
! 393: if (i >= TIMEOUT) {
! 394: printf("%s: codec invalid\n", sc->sc_dev.dv_xname);
! 395: return 1;
! 396: }
! 397:
! 398: return 0;
! 399: }
! 400:
! 401:
! 402: int
! 403: auvia_write_codec(void *addr, u_int8_t reg, u_int16_t val)
! 404: {
! 405: struct auvia_softc *sc = addr;
! 406:
! 407: if (auvia_waitready_codec(sc))
! 408: return 1;
! 409:
! 410: bus_space_write_4(sc->sc_iot, sc->sc_ioh, AUVIA_CODEC_CTL,
! 411: AUVIA_CODEC_PRIVALID | AUVIA_CODEC_INDEX(reg) | val);
! 412:
! 413: return 0;
! 414: }
! 415:
! 416:
! 417: int
! 418: auvia_read_codec(void *addr, u_int8_t reg, u_int16_t *val)
! 419: {
! 420: struct auvia_softc *sc = addr;
! 421:
! 422: if (auvia_waitready_codec(sc))
! 423: return 1;
! 424:
! 425: bus_space_write_4(sc->sc_iot, sc->sc_ioh, AUVIA_CODEC_CTL,
! 426: AUVIA_CODEC_PRIVALID | AUVIA_CODEC_READ | AUVIA_CODEC_INDEX(reg));
! 427:
! 428: if (auvia_waitready_codec(sc))
! 429: return 1;
! 430:
! 431: if (auvia_waitvalid_codec(sc))
! 432: return 1;
! 433:
! 434: *val = bus_space_read_2(sc->sc_iot, sc->sc_ioh, AUVIA_CODEC_CTL);
! 435:
! 436: return 0;
! 437: }
! 438:
! 439:
! 440: int
! 441: auvia_open(void *addr, int flags)
! 442: {
! 443: return 0;
! 444: }
! 445:
! 446:
! 447: void
! 448: auvia_close(void *addr)
! 449: {
! 450: struct auvia_softc *sc = addr;
! 451:
! 452: auvia_halt_output(sc);
! 453: auvia_halt_input(sc);
! 454:
! 455: sc->sc_play.sc_intr = NULL;
! 456: sc->sc_record.sc_intr = NULL;
! 457: }
! 458:
! 459:
! 460: int
! 461: auvia_query_encoding(void *addr, struct audio_encoding *fp)
! 462: {
! 463: switch (fp->index) {
! 464: case 0:
! 465: strlcpy(fp->name, AudioEulinear, sizeof fp->name);
! 466: fp->encoding = AUDIO_ENCODING_ULINEAR;
! 467: fp->precision = 8;
! 468: fp->flags = 0;
! 469: return (0);
! 470: case 1:
! 471: strlcpy(fp->name, AudioEmulaw, sizeof fp->name);
! 472: fp->encoding = AUDIO_ENCODING_ULAW;
! 473: fp->precision = 8;
! 474: fp->flags = AUDIO_ENCODINGFLAG_EMULATED;
! 475: return (0);
! 476: case 2:
! 477: strlcpy(fp->name, AudioEalaw, sizeof fp->name);
! 478: fp->encoding = AUDIO_ENCODING_ALAW;
! 479: fp->precision = 8;
! 480: fp->flags = AUDIO_ENCODINGFLAG_EMULATED;
! 481: return (0);
! 482: case 3:
! 483: strlcpy(fp->name, AudioEslinear, sizeof fp->name);
! 484: fp->encoding = AUDIO_ENCODING_SLINEAR;
! 485: fp->precision = 8;
! 486: fp->flags = AUDIO_ENCODINGFLAG_EMULATED;
! 487: return (0);
! 488: case 4:
! 489: strlcpy(fp->name, AudioEslinear_le, sizeof fp->name);
! 490: fp->encoding = AUDIO_ENCODING_SLINEAR_LE;
! 491: fp->precision = 16;
! 492: fp->flags = 0;
! 493: return (0);
! 494: case 5:
! 495: strlcpy(fp->name, AudioEulinear_le, sizeof fp->name);
! 496: fp->encoding = AUDIO_ENCODING_ULINEAR_LE;
! 497: fp->precision = 16;
! 498: fp->flags = AUDIO_ENCODINGFLAG_EMULATED;
! 499: return (0);
! 500: case 6:
! 501: strlcpy(fp->name, AudioEslinear_be, sizeof fp->name);
! 502: fp->encoding = AUDIO_ENCODING_SLINEAR_BE;
! 503: fp->precision = 16;
! 504: fp->flags = AUDIO_ENCODINGFLAG_EMULATED;
! 505: return (0);
! 506: case 7:
! 507: strlcpy(fp->name, AudioEulinear_be, sizeof fp->name);
! 508: fp->encoding = AUDIO_ENCODING_ULINEAR_BE;
! 509: fp->precision = 16;
! 510: fp->flags = AUDIO_ENCODINGFLAG_EMULATED;
! 511: return (0);
! 512: default:
! 513: return (EINVAL);
! 514: }
! 515: }
! 516:
! 517:
! 518: int
! 519: auvia_set_params(void *addr, int setmode, int usemode,
! 520: struct audio_params *play, struct audio_params *rec)
! 521: {
! 522: struct auvia_softc *sc = addr;
! 523: struct audio_params *p;
! 524: u_int16_t regval;
! 525: int mode, base;
! 526:
! 527: /* for mode in (RECORD, PLAY) */
! 528: for (mode = AUMODE_RECORD; mode != -1;
! 529: mode = mode == AUMODE_RECORD ? AUMODE_PLAY : -1) {
! 530: if ((setmode & mode) == 0)
! 531: continue;
! 532:
! 533: if (mode == AUMODE_PLAY) {
! 534: p = play;
! 535: base = AUVIA_PLAY_BASE;
! 536: } else {
! 537: p = rec;
! 538: base = AUVIA_RECORD_BASE;
! 539: }
! 540:
! 541: if (sc->sc_flags & AUVIA_FLAGS_VT8233) {
! 542: u_int32_t v = bus_space_read_4(sc->sc_iot, sc->sc_ioh,
! 543: base + VIA8233_RP_RATEFMT) & ~(VIA8233_RATEFMT_48K
! 544: | VIA8233_RATEFMT_STEREO | VIA8233_RATEFMT_16BIT);
! 545:
! 546: v |= VIA8233_RATEFMT_48K *
! 547: (p->sample_rate / 20) / (48000 / 20);
! 548:
! 549: if (p->channels == 2)
! 550: v |= VIA8233_RATEFMT_STEREO;
! 551: if (p->precision == 16)
! 552: v |= VIA8233_RATEFMT_16BIT;
! 553:
! 554: bus_space_write_4(sc->sc_iot, sc->sc_ioh,
! 555: base + VIA8233_RP_RATEFMT, v);
! 556: }
! 557:
! 558: if (ac97_set_rate(sc->codec_if, p, mode))
! 559: return (EINVAL);
! 560:
! 561: if ((p->precision != 8 && p->precision != 16) ||
! 562: (p->channels != 1 && p->channels != 2))
! 563: return (EINVAL);
! 564:
! 565: p->factor = 1;
! 566: p->sw_code = 0;
! 567: switch (p->encoding) {
! 568: case AUDIO_ENCODING_SLINEAR_BE:
! 569: if (p->precision == 16)
! 570: p->sw_code = swap_bytes;
! 571: else
! 572: p->sw_code = change_sign8;
! 573: break;
! 574: case AUDIO_ENCODING_SLINEAR_LE:
! 575: if (p->precision != 16)
! 576: p->sw_code = change_sign8;
! 577: break;
! 578: case AUDIO_ENCODING_ULINEAR_BE:
! 579: if (p->precision == 16)
! 580: p->sw_code = mode == AUMODE_PLAY?
! 581: swap_bytes_change_sign16_le :
! 582: change_sign16_swap_bytes_le;
! 583: break;
! 584: case AUDIO_ENCODING_ULINEAR_LE:
! 585: if (p->precision == 16)
! 586: p->sw_code = change_sign16_le;
! 587: break;
! 588: case AUDIO_ENCODING_ULAW:
! 589: if (mode == AUMODE_PLAY) {
! 590: p->factor = 2;
! 591: p->sw_code = mulaw_to_slinear16_le;
! 592: } else
! 593: p->sw_code = ulinear8_to_mulaw;
! 594: break;
! 595: case AUDIO_ENCODING_ALAW:
! 596: if (mode == AUMODE_PLAY) {
! 597: p->factor = 2;
! 598: p->sw_code = alaw_to_slinear16_le;
! 599: } else
! 600: p->sw_code = ulinear8_to_alaw;
! 601: break;
! 602: case AUDIO_ENCODING_SLINEAR:
! 603: case AUDIO_ENCODING_ULINEAR:
! 604: break;
! 605: default:
! 606: return (EINVAL);
! 607: }
! 608:
! 609: regval = (p->channels == 2 ? AUVIA_RPMODE_STEREO : 0)
! 610: | (p->precision * p->factor == 16 ?
! 611: AUVIA_RPMODE_16BIT : 0)
! 612: | AUVIA_RPMODE_INTR_FLAG | AUVIA_RPMODE_INTR_EOL
! 613: | AUVIA_RPMODE_AUTOSTART;
! 614:
! 615: if (mode == AUMODE_PLAY)
! 616: sc->sc_play.sc_reg = regval;
! 617: else
! 618: sc->sc_record.sc_reg = regval;
! 619: }
! 620:
! 621: return 0;
! 622: }
! 623:
! 624:
! 625: int
! 626: auvia_round_blocksize(void *addr, int blk)
! 627: {
! 628: return ((blk + 31) & -32);
! 629: }
! 630:
! 631:
! 632: int
! 633: auvia_halt_output(void *addr)
! 634: {
! 635: struct auvia_softc *sc = addr;
! 636:
! 637: bus_space_write_1(sc->sc_iot, sc->sc_ioh,
! 638: AUVIA_PLAY_BASE + AUVIA_RP_CONTROL, AUVIA_RPCTRL_TERMINATE);
! 639:
! 640: return 0;
! 641: }
! 642:
! 643:
! 644: int
! 645: auvia_halt_input(void *addr)
! 646: {
! 647: struct auvia_softc *sc = addr;
! 648:
! 649: bus_space_write_1(sc->sc_iot, sc->sc_ioh,
! 650: AUVIA_RECORD_BASE + AUVIA_RP_CONTROL, AUVIA_RPCTRL_TERMINATE);
! 651:
! 652: return 0;
! 653: }
! 654:
! 655:
! 656: int
! 657: auvia_getdev(void *addr, struct audio_device *retp)
! 658: {
! 659: struct auvia_softc *sc = addr;
! 660:
! 661: if (retp) {
! 662: strncpy(retp->name,
! 663: sc->sc_flags & AUVIA_FLAGS_VT8233? "VIA VT8233" :
! 664: "VIA VT82C686A", sizeof(retp->name));
! 665: strncpy(retp->version, sc->sc_revision, sizeof(retp->version));
! 666: strncpy(retp->config, "auvia", sizeof(retp->config));
! 667: }
! 668:
! 669: return 0;
! 670: }
! 671:
! 672:
! 673: int
! 674: auvia_set_port(void *addr, mixer_ctrl_t *cp)
! 675: {
! 676: struct auvia_softc *sc = addr;
! 677:
! 678: return (sc->codec_if->vtbl->mixer_set_port(sc->codec_if, cp));
! 679: }
! 680:
! 681:
! 682: int
! 683: auvia_get_port(void *addr, mixer_ctrl_t *cp)
! 684: {
! 685: struct auvia_softc *sc = addr;
! 686:
! 687: return (sc->codec_if->vtbl->mixer_get_port(sc->codec_if, cp));
! 688: }
! 689:
! 690:
! 691: int
! 692: auvia_query_devinfo(void *addr, mixer_devinfo_t *dip)
! 693: {
! 694: struct auvia_softc *sc = addr;
! 695:
! 696: return (sc->codec_if->vtbl->query_devinfo(sc->codec_if, dip));
! 697: }
! 698:
! 699:
! 700: void *
! 701: auvia_malloc(void *addr, int direction, size_t size, int pool, int flags)
! 702: {
! 703: struct auvia_softc *sc = addr;
! 704: struct auvia_dma *p;
! 705: int error;
! 706: int rseg;
! 707:
! 708: p = malloc(sizeof(*p), pool, flags);
! 709: if (!p)
! 710: return 0;
! 711:
! 712: p->size = size;
! 713: if ((error = bus_dmamem_alloc(sc->sc_dmat, size, PAGE_SIZE, 0, &p->seg,
! 714: 1, &rseg, BUS_DMA_NOWAIT)) != 0) {
! 715: printf("%s: unable to allocate dma, error = %d\n",
! 716: sc->sc_dev.dv_xname, error);
! 717: goto fail_alloc;
! 718: }
! 719:
! 720: if ((error = bus_dmamem_map(sc->sc_dmat, &p->seg, rseg, size, &p->addr,
! 721: BUS_DMA_NOWAIT | BUS_DMA_COHERENT)) != 0) {
! 722: printf("%s: unable to map dma, error = %d\n",
! 723: sc->sc_dev.dv_xname, error);
! 724: goto fail_map;
! 725: }
! 726:
! 727: if ((error = bus_dmamap_create(sc->sc_dmat, size, 1, size, 0,
! 728: BUS_DMA_NOWAIT, &p->map)) != 0) {
! 729: printf("%s: unable to create dma map, error = %d\n",
! 730: sc->sc_dev.dv_xname, error);
! 731: goto fail_create;
! 732: }
! 733:
! 734: if ((error = bus_dmamap_load(sc->sc_dmat, p->map, p->addr, size, NULL,
! 735: BUS_DMA_NOWAIT)) != 0) {
! 736: printf("%s: unable to load dma map, error = %d\n",
! 737: sc->sc_dev.dv_xname, error);
! 738: goto fail_load;
! 739: }
! 740:
! 741: p->next = sc->sc_dmas;
! 742: sc->sc_dmas = p;
! 743:
! 744: return p->addr;
! 745:
! 746:
! 747: fail_load:
! 748: bus_dmamap_destroy(sc->sc_dmat, p->map);
! 749: fail_create:
! 750: bus_dmamem_unmap(sc->sc_dmat, p->addr, size);
! 751: fail_map:
! 752: bus_dmamem_free(sc->sc_dmat, &p->seg, 1);
! 753: fail_alloc:
! 754: free(p, pool);
! 755: return 0;
! 756: }
! 757:
! 758:
! 759: void
! 760: auvia_free(void *addr, void *ptr, int pool)
! 761: {
! 762: struct auvia_softc *sc = addr;
! 763: struct auvia_dma **pp, *p;
! 764:
! 765: for (pp = &(sc->sc_dmas); (p = *pp) != NULL; pp = &p->next)
! 766: if (p->addr == ptr) {
! 767: bus_dmamap_unload(sc->sc_dmat, p->map);
! 768: bus_dmamap_destroy(sc->sc_dmat, p->map);
! 769: bus_dmamem_unmap(sc->sc_dmat, p->addr, p->size);
! 770: bus_dmamem_free(sc->sc_dmat, &p->seg, 1);
! 771:
! 772: *pp = p->next;
! 773: free(p, pool);
! 774: return;
! 775: }
! 776:
! 777: panic("auvia_free: trying to free unallocated memory");
! 778: }
! 779:
! 780: paddr_t
! 781: auvia_mappage(void *addr, void *mem, off_t off, int prot)
! 782: {
! 783: struct auvia_softc *sc = addr;
! 784: struct auvia_dma *p;
! 785:
! 786: if (off < 0)
! 787: return -1;
! 788:
! 789: for (p = sc->sc_dmas; p && p->addr != mem; p = p->next)
! 790: ;
! 791:
! 792: if (!p)
! 793: return -1;
! 794:
! 795: return bus_dmamem_mmap(sc->sc_dmat, &p->seg, 1, off, prot,
! 796: BUS_DMA_WAITOK);
! 797: }
! 798:
! 799:
! 800: int
! 801: auvia_get_props(void *addr)
! 802: {
! 803: return (AUDIO_PROP_MMAP | AUDIO_PROP_INDEPENDENT |
! 804: AUDIO_PROP_FULLDUPLEX);
! 805: }
! 806:
! 807:
! 808: int
! 809: auvia_build_dma_ops(struct auvia_softc *sc, struct auvia_softc_chan *ch,
! 810: struct auvia_dma *p, void *start, void *end, int blksize)
! 811: {
! 812: struct auvia_dma_op *op;
! 813: struct auvia_dma *dp;
! 814: bus_addr_t s;
! 815: size_t l;
! 816: int segs;
! 817:
! 818: s = p->map->dm_segs[0].ds_addr;
! 819: l = (vaddr_t)end - (vaddr_t)start;
! 820: segs = howmany(l, blksize);
! 821:
! 822: if (segs > ch->sc_dma_op_count) {
! 823: /* if old list was too small, free it */
! 824: if (ch->sc_dma_ops)
! 825: auvia_free(sc, ch->sc_dma_ops, M_DEVBUF);
! 826:
! 827: ch->sc_dma_ops = auvia_malloc(sc, 0,
! 828: sizeof(struct auvia_dma_op) * segs, M_DEVBUF, M_WAITOK);
! 829:
! 830: for (dp = sc->sc_dmas; dp &&
! 831: dp->addr != (void *)(ch->sc_dma_ops); dp = dp->next)
! 832: ;
! 833:
! 834: if (!dp)
! 835: panic("%s: build_dma_ops: where'd my memory go??? "
! 836: "address (%p)", sc->sc_dev.dv_xname,
! 837: ch->sc_dma_ops);
! 838:
! 839: ch->sc_dma_op_count = segs;
! 840: ch->sc_dma_ops_dma = dp;
! 841: }
! 842:
! 843: dp = ch->sc_dma_ops_dma;
! 844: op = ch->sc_dma_ops;
! 845:
! 846: while (l) {
! 847: op->ptr = htole32(s);
! 848: l = l - min(l, blksize);
! 849: /* if last block */
! 850: op->flags = htole32((l? AUVIA_DMAOP_FLAG : AUVIA_DMAOP_EOL) | blksize);
! 851: s += blksize;
! 852: op++;
! 853: }
! 854:
! 855: return 0;
! 856: }
! 857:
! 858:
! 859: int
! 860: auvia_trigger_output(void *addr, void *start, void *end, int blksize,
! 861: void (*intr)(void *), void *arg, struct audio_params *param)
! 862: {
! 863: struct auvia_softc *sc = addr;
! 864: struct auvia_softc_chan *ch = &(sc->sc_play);
! 865: struct auvia_dma *p;
! 866:
! 867: for (p = sc->sc_dmas; p && p->addr != start; p = p->next)
! 868: ;
! 869:
! 870: if (!p)
! 871: panic("auvia_trigger_output: request with bad start "
! 872: "address (%p)", start);
! 873:
! 874: if (auvia_build_dma_ops(sc, ch, p, start, end, blksize)) {
! 875: return 1;
! 876: }
! 877:
! 878: ch->sc_intr = intr;
! 879: ch->sc_arg = arg;
! 880:
! 881: bus_space_write_4(sc->sc_iot, sc->sc_ioh,
! 882: AUVIA_PLAY_BASE + AUVIA_RP_DMAOPS_BASE,
! 883: ch->sc_dma_ops_dma->map->dm_segs[0].ds_addr);
! 884:
! 885: bus_space_write_1(sc->sc_iot, sc->sc_ioh,
! 886: AUVIA_PLAY_BASE + AUVIA_RP_MODE, ch->sc_reg);
! 887:
! 888: if (sc->sc_flags & AUVIA_FLAGS_VT8233) {
! 889: bus_space_write_1(sc->sc_iot, sc->sc_ioh,
! 890: AUVIA_PLAY_BASE + VIA8233_RP_DXS_LVOL, 0);
! 891: bus_space_write_1(sc->sc_iot, sc->sc_ioh,
! 892: AUVIA_PLAY_BASE + VIA8233_RP_DXS_RVOL, 0);
! 893: bus_space_write_1(sc->sc_iot, sc->sc_ioh,
! 894: AUVIA_PLAY_BASE + AUVIA_RP_CONTROL,
! 895: AUVIA_RPCTRL_START | AUVIA_RPCTRL_AUTOSTART |
! 896: AUVIA_RPCTRL_STOP | AUVIA_RPCTRL_EOL | AUVIA_RPCTRL_FLAG);
! 897: } else
! 898: bus_space_write_1(sc->sc_iot, sc->sc_ioh,
! 899: AUVIA_PLAY_BASE + AUVIA_RP_CONTROL, AUVIA_RPCTRL_START);
! 900:
! 901: return 0;
! 902: }
! 903:
! 904:
! 905: int
! 906: auvia_trigger_input(void *addr, void *start, void *end, int blksize,
! 907: void (*intr)(void *), void *arg, struct audio_params *param)
! 908: {
! 909: struct auvia_softc *sc = addr;
! 910: struct auvia_softc_chan *ch = &(sc->sc_record);
! 911: struct auvia_dma *p;
! 912:
! 913: for (p = sc->sc_dmas; p && p->addr != start; p = p->next)
! 914: ;
! 915:
! 916: if (!p)
! 917: panic("auvia_trigger_input: request with bad start "
! 918: "address (%p)", start);
! 919:
! 920: if (auvia_build_dma_ops(sc, ch, p, start, end, blksize))
! 921: return 1;
! 922:
! 923: ch->sc_intr = intr;
! 924: ch->sc_arg = arg;
! 925:
! 926: bus_space_write_4(sc->sc_iot, sc->sc_ioh,
! 927: AUVIA_RECORD_BASE + AUVIA_RP_DMAOPS_BASE,
! 928: ch->sc_dma_ops_dma->map->dm_segs[0].ds_addr);
! 929: bus_space_write_1(sc->sc_iot, sc->sc_ioh,
! 930: AUVIA_RECORD_BASE + AUVIA_RP_MODE, ch->sc_reg);
! 931:
! 932: if (sc->sc_flags & AUVIA_FLAGS_VT8233) {
! 933: bus_space_write_1(sc->sc_iot, sc->sc_ioh,
! 934: AUVIA_RECORD_BASE + VIA8233_RP_DXS_LVOL, 0);
! 935: bus_space_write_1(sc->sc_iot, sc->sc_ioh,
! 936: AUVIA_RECORD_BASE + VIA8233_RP_DXS_RVOL, 0);
! 937: bus_space_write_1(sc->sc_iot, sc->sc_ioh,
! 938: AUVIA_RECORD_BASE + AUVIA_RP_CONTROL,
! 939: AUVIA_RPCTRL_START | AUVIA_RPCTRL_AUTOSTART |
! 940: AUVIA_RPCTRL_STOP | AUVIA_RPCTRL_EOL | AUVIA_RPCTRL_FLAG);
! 941: } else
! 942: bus_space_write_1(sc->sc_iot, sc->sc_ioh,
! 943: AUVIA_RECORD_BASE + AUVIA_RP_CONTROL, AUVIA_RPCTRL_START);
! 944:
! 945: return 0;
! 946: }
! 947:
! 948:
! 949: int
! 950: auvia_intr(void *arg)
! 951: {
! 952: struct auvia_softc *sc = arg;
! 953: u_int8_t r;
! 954: int i = 0;
! 955:
! 956: r = bus_space_read_1(sc->sc_iot, sc->sc_ioh,
! 957: AUVIA_RECORD_BASE + AUVIA_RP_STAT);
! 958: if (r & AUVIA_RPSTAT_INTR) {
! 959: if (sc->sc_record.sc_intr)
! 960: sc->sc_record.sc_intr(sc->sc_record.sc_arg);
! 961:
! 962: /* clear interrupts */
! 963: bus_space_write_1(sc->sc_iot, sc->sc_ioh,
! 964: AUVIA_RECORD_BASE + AUVIA_RP_STAT, AUVIA_RPSTAT_INTR);
! 965:
! 966: i++;
! 967: }
! 968: r = bus_space_read_1(sc->sc_iot, sc->sc_ioh,
! 969: AUVIA_PLAY_BASE + AUVIA_RP_STAT);
! 970: if (r & AUVIA_RPSTAT_INTR) {
! 971: if (sc->sc_play.sc_intr)
! 972: sc->sc_play.sc_intr(sc->sc_play.sc_arg);
! 973:
! 974: /* clear interrupts */
! 975: bus_space_write_1(sc->sc_iot, sc->sc_ioh,
! 976: AUVIA_PLAY_BASE + AUVIA_RP_STAT, AUVIA_RPSTAT_INTR);
! 977:
! 978: i++;
! 979: }
! 980:
! 981: return (i? 1 : 0);
! 982: }
CVSweb