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

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