diff options
author | Alexandre Ratchov <ratchov@cvs.openbsd.org> | 2010-03-07 18:55:46 +0000 |
---|---|---|
committer | Alexandre Ratchov <ratchov@cvs.openbsd.org> | 2010-03-07 18:55:46 +0000 |
commit | 9b0e0cfc703f7392ebdc4302f004b16469c25d57 (patch) | |
tree | 19cc236a366163b86b38b8243f7a5966950605d8 | |
parent | bedfa8ef23b842377326aac29080762ac51a451b (diff) |
Don't stop DMA in envy_halt_intput() and/or envy_halt_output().
Instead wait for the next interrupt and stop DMA in the interrupt
handler. This prevents the chip from entering a unstable state
in which DMA doesn't start cleanly.
-rw-r--r-- | sys/dev/pci/envy.c | 205 | ||||
-rw-r--r-- | sys/dev/pci/envyreg.h | 5 | ||||
-rw-r--r-- | sys/dev/pci/envyvar.h | 17 |
3 files changed, 182 insertions, 45 deletions
diff --git a/sys/dev/pci/envy.c b/sys/dev/pci/envy.c index 09a3e37b6a3..d0334d133a5 100644 --- a/sys/dev/pci/envy.c +++ b/sys/dev/pci/envy.c @@ -1,4 +1,4 @@ -/* $OpenBSD: envy.c,v 1.34 2010/02/25 21:25:03 ratchov Exp $ */ +/* $OpenBSD: envy.c,v 1.35 2010/03/07 18:55:45 ratchov Exp $ */ /* * Copyright (c) 2007 Alexandre Ratchov <alex@caoua.org> * @@ -32,6 +32,8 @@ #include <sys/ioctl.h> #include <sys/audioio.h> #include <sys/malloc.h> +#include <sys/proc.h> +#include <sys/kernel.h> #include <dev/audio_if.h> #include <dev/ic/ac97.h> #include <dev/pci/pcivar.h> @@ -74,6 +76,7 @@ int envy_eeprom_gpioxxx(struct envy_softc *, int); void envy_reset(struct envy_softc *); int envy_codec_read(struct envy_softc *, int, int); void envy_codec_write(struct envy_softc *, int, int, int); +void envy_pintr(struct envy_softc *); int envy_intr(void *); int envy_lineout_getsrc(struct envy_softc *, int); @@ -1158,36 +1161,16 @@ envy_reset(struct envy_softc *sc) if (sc->isht) { bus_space_write_1(sc->mt_iot, sc->mt_ioh, ENVY_MT_NSTREAM, 4 - sc->card->noch / 2); - bus_space_write_1(sc->mt_iot, sc->mt_ioh, ENVY_MT_IMASK, - ~(ENVY_MT_IMASK_PDMA0 | ENVY_MT_IMASK_RDMA0)); + bus_space_write_1(sc->mt_iot, sc->mt_ioh, + ENVY_MT_IMASK, ~(ENVY_MT_IMASK_PDMA0 | + ENVY_MT_IMASK_RDMA0 | ENVY_MT_IMASK_ERR)); } + sc->iactive = 0; + sc->oactive = 0; sc->card->init(sc); } int -envy_intr(void *self) -{ - struct envy_softc *sc = (struct envy_softc *)self; - int st; - - st = bus_space_read_1(sc->mt_iot, sc->mt_ioh, ENVY_MT_INTR); - if (!(st & (ENVY_MT_INTR_PACK | ENVY_MT_INTR_RACK))) { - return 0; - } - if (st & ENVY_MT_INTR_PACK) { - st = ENVY_MT_INTR_PACK; - bus_space_write_1(sc->mt_iot, sc->mt_ioh, ENVY_MT_INTR, st); - sc->ointr(sc->oarg); - } - if (st & ENVY_MT_INTR_RACK) { - st = ENVY_MT_INTR_RACK; - bus_space_write_1(sc->mt_iot, sc->mt_ioh, ENVY_MT_INTR, st); - sc->iintr(sc->iarg); - } - return 1; -} - -int envy_lineout_getsrc(struct envy_softc *sc, int out) { int reg, shift, src; @@ -1573,10 +1556,8 @@ envy_set_params(void *self, int setmode, int usemode, struct envy_softc *sc = (struct envy_softc *)self; int i, rate, reg; - if (setmode == 0) { - DPRINTF("%s: no params to set\n", DEVNAME(sc)); + if (setmode == 0) return 0; - } if (setmode == (AUMODE_PLAY | AUMODE_RECORD) && p->sample_rate != r->sample_rate) { DPRINTF("%s: play/rec rates mismatch\n", DEVNAME(sc)); @@ -1639,13 +1620,115 @@ envy_round_buffersize(void *self, int dir, size_t bufsz) return bufsz; } +#ifdef ENVY_DEBUG +void +envy_pintr(struct envy_softc *sc) +{ + int i; + + if (sc->spurious > 0 || envydebug >= 2) { + printf("%s: spurious = %u, start = %u.%ld\n", + DEVNAME(sc), sc->spurious, + sc->start_ts.tv_sec, sc->start_ts.tv_nsec); + for (i = 0; i < sc->nintr; i++) { + printf("%u.%09ld: " + "active=%d/%d pos=%d/%d st=%x/%x, ctl=%x\n", + sc->intrs[i].ts.tv_sec, + sc->intrs[i].ts.tv_nsec, + sc->intrs[i].iactive, + sc->intrs[i].oactive, + sc->intrs[i].ipos, + sc->intrs[i].opos, + sc->intrs[i].st, + sc->intrs[i].mask, + sc->intrs[i].ctl); + } + } +} +#endif + +int +envy_intr(void *self) +{ + struct envy_softc *sc = (struct envy_softc *)self; + int st, err, ctl; + + + st = bus_space_read_1(sc->mt_iot, sc->mt_ioh, ENVY_MT_INTR); + if (st == 0) + return 0; +#ifdef ENVY_DEBUG + if (sc->nintr < ENVY_NINTR) { + sc->intrs[sc->nintr].iactive = sc->iactive; + sc->intrs[sc->nintr].oactive = sc->oactive; + sc->intrs[sc->nintr].st = st; + sc->intrs[sc->nintr].ipos = + bus_space_read_2(sc->mt_iot, sc->mt_ioh, ENVY_MT_RBUFSZ); + sc->intrs[sc->nintr].opos = + bus_space_read_2(sc->mt_iot, sc->mt_ioh, ENVY_MT_PBUFSZ); + sc->intrs[sc->nintr].ctl = + bus_space_read_1(sc->mt_iot, sc->mt_ioh, ENVY_MT_CTL); + sc->intrs[sc->nintr].mask = + bus_space_read_1(sc->mt_iot, sc->mt_ioh, ENVY_MT_IMASK); + nanouptime(&sc->intrs[sc->nintr].ts); + sc->nintr++; + } +#endif + if (st & ENVY_MT_INTR_PACK) { + if (sc->oactive) + sc->ointr(sc->oarg); + else { + ctl = bus_space_read_1(sc->mt_iot, sc->mt_ioh, ENVY_MT_CTL); + if (ctl & ENVY_MT_CTL_PSTART) { + bus_space_write_1(sc->mt_iot, sc->mt_ioh, + ENVY_MT_INTR, ENVY_MT_INTR_PACK); + bus_space_write_1(sc->mt_iot, sc->mt_ioh, + ENVY_MT_CTL, ctl & ~ENVY_MT_CTL_PSTART); + st &= ~ENVY_MT_INTR_PACK; + sc->obusy = 0; + wakeup(&sc->obusy); + } +#ifdef ENVY_DEBUG + else + sc->spurious++; +#endif + } + } + if (st & ENVY_MT_INTR_RACK) { + if (sc->iactive) + sc->iintr(sc->iarg); + else { + ctl = bus_space_read_1(sc->mt_iot, sc->mt_ioh, ENVY_MT_CTL); + if (ctl & ENVY_MT_CTL_RSTART(sc)) { + bus_space_write_1(sc->mt_iot, sc->mt_ioh, + ENVY_MT_INTR, ENVY_MT_INTR_RACK); + bus_space_write_1(sc->mt_iot, sc->mt_ioh, + ENVY_MT_CTL, ctl & ~ENVY_MT_CTL_RSTART(sc)); + st &= ~ENVY_MT_INTR_RACK; + sc->ibusy = 0; + wakeup(&sc->ibusy); + } +#ifdef ENVY_DEBUG + else + sc->spurious++; +#endif + } + } + if (sc->isht && (st & ENVY_MT_INTR_ERR)) { + err = bus_space_read_1(sc->mt_iot, sc->mt_ioh, ENVY_MT_ERR); + bus_space_write_1(sc->mt_iot, sc->mt_ioh, ENVY_MT_ERR, err); + } + bus_space_write_1(sc->mt_iot, sc->mt_ioh, ENVY_MT_INTR, st); + return 1; +} + int envy_trigger_output(void *self, void *start, void *end, int blksz, void (*intr)(void *), void *arg, struct audio_params *param) { struct envy_softc *sc = (struct envy_softc *)self; size_t bufsz; - int st; + int s, st; bufsz = (char *)end - (char *)start; #ifdef ENVY_DEBUG @@ -1658,20 +1741,29 @@ envy_trigger_output(void *self, void *start, void *end, int blksz, return EINVAL; } #endif + s = splaudio(); bus_space_write_2(sc->mt_iot, sc->mt_ioh, ENVY_MT_PBUFSZ, bufsz / 4 - 1); bus_space_write_2(sc->mt_iot, sc->mt_ioh, ENVY_MT_PBLKSZ(sc), blksz / 4 - 1); +#ifdef ENVY_DEBUG + if (!sc->iactive) { + sc->nintr = 0; + sc->spurious = 0; + nanouptime(&sc->start_ts); + } +#endif sc->ointr = intr; sc->oarg = arg; - + sc->oactive = 1; + sc->obusy = 1; st = ENVY_MT_INTR_PACK; bus_space_write_1(sc->mt_iot, sc->mt_ioh, ENVY_MT_INTR, st); - st = bus_space_read_1(sc->mt_iot, sc->mt_ioh, ENVY_MT_CTL); st |= ENVY_MT_CTL_PSTART; bus_space_write_1(sc->mt_iot, sc->mt_ioh, ENVY_MT_CTL, st); + splx(s); return 0; } @@ -1681,7 +1773,7 @@ envy_trigger_input(void *self, void *start, void *end, int blksz, { struct envy_softc *sc = (struct envy_softc *)self; size_t bufsz; - int st; + int s, st; bufsz = (char *)end - (char *)start; #ifdef ENVY_DEBUG @@ -1694,20 +1786,29 @@ envy_trigger_input(void *self, void *start, void *end, int blksz, return EINVAL; } #endif + s = splaudio(); bus_space_write_2(sc->mt_iot, sc->mt_ioh, ENVY_MT_RBUFSZ, bufsz / 4 - 1); bus_space_write_2(sc->mt_iot, sc->mt_ioh, ENVY_MT_RBLKSZ, blksz / 4 - 1); +#ifdef ENVY_DEBUG + if (!sc->oactive) { + sc->nintr = 0; + sc->spurious = 0; + nanouptime(&sc->start_ts); + } +#endif sc->iintr = intr; sc->iarg = arg; - + sc->iactive = 1; + sc->ibusy = 1; st = ENVY_MT_INTR_RACK; bus_space_write_1(sc->mt_iot, sc->mt_ioh, ENVY_MT_INTR, st); - st = bus_space_read_1(sc->mt_iot, sc->mt_ioh, ENVY_MT_CTL); st |= ENVY_MT_CTL_RSTART(sc); bus_space_write_1(sc->mt_iot, sc->mt_ioh, ENVY_MT_CTL, st); + splx(s); return 0; } @@ -1715,11 +1816,20 @@ int envy_halt_output(void *self) { struct envy_softc *sc = (struct envy_softc *)self; - int st; + int s, err; - st = bus_space_read_1(sc->mt_iot, sc->mt_ioh, ENVY_MT_CTL); - st &= ~ENVY_MT_CTL_PSTART; - bus_space_write_1(sc->mt_iot, sc->mt_ioh, ENVY_MT_CTL, st); + s = splaudio(); + sc->oactive = 0; + if (sc->obusy) { + err = tsleep(&sc->obusy, PWAIT, "envyobus", 4 * hz); + if (err) + printf("%s: output DMA halt timeout\n", DEVNAME(sc)); + } + splx(s); +#ifdef ENVY_DEBUG + if (!sc->iactive) + envy_pintr(sc); +#endif return 0; } @@ -1727,11 +1837,20 @@ int envy_halt_input(void *self) { struct envy_softc *sc = (struct envy_softc *)self; - int st; + int s, err; - st = bus_space_read_1(sc->mt_iot, sc->mt_ioh, ENVY_MT_CTL); - st &= ~ENVY_MT_CTL_RSTART(sc); - bus_space_write_1(sc->mt_iot, sc->mt_ioh, ENVY_MT_CTL, st); + s = splaudio(); + sc->iactive = 0; + if (sc->ibusy) { + err = tsleep(&sc->ibusy, PWAIT, "envyibus", 4 * hz); + if (err) + printf("%s: input DMA halt timeout\n", DEVNAME(sc)); + } +#ifdef ENVY_DEBUG + if (!sc->oactive) + envy_pintr(sc); +#endif + splx(s); return 0; } diff --git a/sys/dev/pci/envyreg.h b/sys/dev/pci/envyreg.h index 7e18b245984..1368a3cef87 100644 --- a/sys/dev/pci/envyreg.h +++ b/sys/dev/pci/envyreg.h @@ -1,4 +1,4 @@ -/* $OpenBSD: envyreg.h,v 1.13 2010/02/25 21:19:37 ratchov Exp $ */ +/* $OpenBSD: envyreg.h,v 1.14 2010/03/07 18:55:45 ratchov Exp $ */ /* * Copyright (c) 2007 Alexandre Ratchov <alex@caoua.org> * @@ -100,6 +100,7 @@ #define ENVY_MT_INTR 0 #define ENVY_MT_INTR_PACK 0x01 #define ENVY_MT_INTR_RACK 0x02 +#define ENVY_MT_INTR_ERR 0x08 /* HT only fifo error */ #define ENVY_MT_INTR_PMASK 0x40 /* !HT only */ #define ENVY_MT_INTR_RMASK 0x80 /* !HT only */ #define ENVY_MT_RATE 1 @@ -107,6 +108,7 @@ #define ENVY_MT_IMASK 3 /* HT only */ #define ENVY_MT_IMASK_PDMA0 0x1 #define ENVY_MT_IMASK_RDMA0 0x2 +#define ENVY_MT_IMASK_ERR 0x8 #define ENVY_MT_AC97_IDX 4 #define ENVY_MT_AC97_CMD 5 #define ENVY_MT_AC97_READY 0x08 @@ -122,6 +124,7 @@ #define ENVY_MT_CTL_PSTART 0x01 #define ENVY_MT_CTL_RSTART(s) ((s)->isht ? 0x02 : 0x04) #define ENVY_MT_NSTREAM 0x19 /* HT only: 4 - active DACs */ +#define ENVY_MT_ERR 0x1a /* HT only: fifo error */ #define ENVY_MT_RADDR 0x20 #define ENVY_MT_RBUFSZ 0x24 #define ENVY_MT_RBLKSZ 0x26 diff --git a/sys/dev/pci/envyvar.h b/sys/dev/pci/envyvar.h index b37238e69c4..78f6dafe004 100644 --- a/sys/dev/pci/envyvar.h +++ b/sys/dev/pci/envyvar.h @@ -1,4 +1,4 @@ -/* $OpenBSD: envyvar.h,v 1.14 2010/02/25 21:19:37 ratchov Exp $ */ +/* $OpenBSD: envyvar.h,v 1.15 2010/03/07 18:55:45 ratchov Exp $ */ /* * Copyright (c) 2007 Alexandre Ratchov <alex@caoua.org> * @@ -20,6 +20,7 @@ #include <sys/types.h> #include <sys/device.h> +#include <sys/time.h> #include <dev/audio_if.h> struct envy_softc; @@ -67,6 +68,20 @@ struct envy_softc { bus_space_tag_t mt_iot; bus_space_handle_t mt_ioh; bus_size_t mt_iosz; + int iactive; /* trigger_input called */ + int oactive; /* trigger_output called */ + int ibusy; /* input DMA started */ + int obusy; /* output DMA started */ +#ifdef ENVY_DEBUG + unsigned spurious; + struct timespec start_ts; +#define ENVY_NINTR 16 + unsigned nintr; + struct envy_intr { + int ipos, opos, st, mask, ctl, iactive, oactive; + struct timespec ts; + } intrs[ENVY_NINTR]; +#endif struct envy_card *card; unsigned char shadow[4][16]; #define ENVY_EEPROM_MAXSZ 32 |