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 {

Reply via email to