[BACK]Return to auvia.c CVS log [TXT][DIR] Up to [local] / sys / dev / pci

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