This diff allows the audio(4) driver to properly recover, when the envy(4) device misses interrupts. Fixes permanent distortion on MP systems.
OK? Index: envy.c =================================================================== RCS file: /cvs/src/sys/dev/pci/envy.c,v retrieving revision 1.60 diff -u -p -u -p -r1.60 envy.c --- envy.c 25 Jun 2015 06:43:46 -0000 1.60 +++ envy.c 29 Jul 2015 07:48:30 -0000 @@ -1921,6 +1921,7 @@ int envy_intr(void *self) { struct envy_softc *sc = (struct envy_softc *)self; + unsigned int reg, hwpos, cnt; int mintr, mstat, mdata; int st, err, ctl; int max; @@ -1965,9 +1966,26 @@ envy_intr(void *self) } } if (st & ENVY_MT_INTR_PACK) { - if (sc->oactive) - sc->ointr(sc->oarg); - else { + if (sc->oactive) { + reg = envy_mt_read_2(sc, ENVY_MT_PBUFSZ); + hwpos = sc->obuf.bufsz - 4 * (reg + 1); + if (hwpos >= sc->obuf.bufsz) + hwpos -= sc->obuf.bufsz; + DPRINTFN(2, "%s: play: reg = %u, pos: %u -> %u\n", + DEVNAME(sc), reg, sc->obuf.swpos, hwpos); + cnt = 0; + while (hwpos - sc->obuf.swpos >= sc->obuf.blksz) { + sc->ointr(sc->oarg); + sc->obuf.swpos += sc->obuf.blksz; + if (sc->obuf.swpos == sc->obuf.bufsz) + sc->obuf.swpos = 0; + cnt++; + } + if (cnt != 1) { + DPRINTFN(2, "%s: play: %u intrs\n", + DEVNAME(sc), cnt); + } + } else { ctl = envy_mt_read_1(sc, ENVY_MT_CTL); if (ctl & ENVY_MT_CTL_PSTART) { envy_mt_write_1(sc, @@ -1983,9 +2001,26 @@ envy_intr(void *self) } } if (st & ENVY_MT_INTR_RACK) { - if (sc->iactive) - sc->iintr(sc->iarg); - else { + if (sc->iactive) { + reg = envy_mt_read_2(sc, ENVY_MT_RBUFSZ); + hwpos = sc->ibuf.bufsz - 4 * (reg + 1); + if (hwpos >= sc->ibuf.bufsz) + hwpos -= sc->ibuf.bufsz; + DPRINTFN(2, "%s: rec: reg = %u, pos: %u -> %u\n", + DEVNAME(sc), reg, sc->ibuf.swpos, hwpos); + cnt = 0; + while (hwpos - sc->ibuf.swpos >= sc->ibuf.blksz) { + sc->iintr(sc->iarg); + sc->ibuf.swpos += sc->ibuf.blksz; + if (sc->ibuf.swpos == sc->ibuf.bufsz) + sc->ibuf.swpos = 0; + cnt++; + } + if (cnt != 1) { + DPRINTFN(2, "%s: rec: %u intrs\n", + DEVNAME(sc), cnt); + } + } else { ctl = envy_mt_read_1(sc, ENVY_MT_CTL); if (ctl & ENVY_MT_CTL_RSTART(sc)) { envy_mt_write_1(sc, @@ -2034,6 +2069,9 @@ envy_trigger_output(void *self, void *st nanouptime(&sc->start_ts); } #endif + sc->obuf.bufsz = bufsz; + sc->obuf.blksz = blksz; + sc->obuf.swpos = 0; sc->ointr = intr; sc->oarg = arg; sc->oactive = 1; @@ -2077,6 +2115,9 @@ envy_trigger_input(void *self, void *sta nanouptime(&sc->start_ts); } #endif + sc->ibuf.bufsz = bufsz; + sc->ibuf.blksz = blksz; + sc->ibuf.swpos = 0; sc->iintr = intr; sc->iarg = arg; sc->iactive = 1; Index: envyvar.h =================================================================== RCS file: /cvs/src/sys/dev/pci/envyvar.h,v retrieving revision 1.16 diff -u -p -u -p -r1.16 envyvar.h --- envyvar.h 4 Oct 2010 09:32:43 -0000 1.16 +++ envyvar.h 29 Jul 2015 07:48:30 -0000 @@ -31,6 +31,9 @@ struct envy_buf { bus_dmamap_t map; caddr_t addr; size_t size; + unsigned int swpos; + unsigned int bufsz; + unsigned int blksz; }; struct envy_codec {