Hi, 

here's a patch for auich(4). It fixes some issues with SIS 7012 
and possibly others. But since it relies on correct behaviour of 
the hardware at some places it needs some testing. Especially try 
to run something like:

aucat -l; play test.wav

right after booting and before anything else is played or 
recorded.

the typical output would be:

auich0: ac97 link rate calibration took 83337 us
sts=7<dch,celv,lvbci> civ=0 auich0: after calibration and reset
sts=1<dch> civ=0 auich0: measured ac97 link rate at 47997 Hz,
will use 48000 Hz auich_trigger_output(ed99000, eda7600, 11776,
0xffffffff80271e20, 0xffff80000015dc00, 0xffff80000015dd90)
sts=1<dch> auich_trigger_pipe: qptr=0
auich_trigger_input(eda9000, edb7600, 11776, 0xffffffff80272010,
0xffff80000015dc00, 0xffff80000015ddb8) sts=1<dch>
auich_trigger_pipe: qptr=0 auich0: halt_input auich0: halt_output

Important is the value of sts. dch means "DMA halted" - one 
should not set AUICH_RR before this bit is set.
In auich_calibrate() I wait for any of the three bits 
<dch,celv,lvbci> to be set.
If something goes wrong here you will see:
auich0: ac97 link rate timed out %d us sts=%b civ=%d\n"

If something goes wrong in halt_pipe you will see:
auich_halt_pipe: halt took %d cycles


please send me your dmesg if anything goes wrong or if you chip 
does set the sts flags in another way.

A short summary of the changes:

- According to specification AUICH_RR may only be set after DMA 
  is halted (AUICH_DCH is 0 in AUICH_STS). To accomplish this I 
revived auich_halt_pipe();
- auich_calibrate() did not clear interrupt and event bits in 
  AUICH_STS. Do that now. Further it did watch the CIV index 
counter to see when all samples are processed. I changed it to 
watch AUICH_STS instead and set LVI=CIV. therefore it won't 
change the CIV counter.
- my last patch introduced a small bug in auich_trigger_pipe() I 
  fixed that.


Cheers,

Christopher Zimmermann


Index: sys/dev/pci/auich.c
===================================================================
RCS file: /cvs/src/sys/dev/pci/auich.c,v
retrieving revision 1.81
diff -u -p -r1.81 auich.c
--- sys/dev/pci/auich.c 30 Mar 2010 09:38:07 -0000      1.81
+++ sys/dev/pci/auich.c 31 Mar 2010 12:30:28 -0000
@@ -26,7 +26,7 @@
  * THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-/* #define     AUICH_DEBUG */
+#define        AUICH_DEBUG
 /*
  * AC'97 audio found on Intel 810/815/820/440MX chipsets.
  *     http://developer.intel.com/design/chipsets/datashts/290655.htm
@@ -75,7 +75,7 @@
 #define                AUICH_BCIS      0x08    /* r- buf cmplt int sts; wr ack 
*/
 #define                AUICH_LVBCI     0x04    /* r- last valid bci, wr ack */
 #define                AUICH_CELV      0x02    /* current equals last valid */
-#define                AUICH_DCH               0x01    /* dma halted */
+#define                AUICH_DCH       0x01    /* dma halted */
 #define                AUICH_ISTS_BITS 
"\020\01dch\02celv\03lvbci\04bcis\05fifoe"
 #define        AUICH_PICB      0x08    /* 16 bits */
 #define        AUICH_PIV               0x0a    /* 5 bits prefetched index 
value */
@@ -232,7 +232,8 @@ struct auich_softc {
 
 #ifdef AUICH_DEBUG
 #define        DPRINTF(l,x)    do { if (auich_debug & (l)) printf x; } while(0)
-int auich_debug = 0xfffe;
+/*int auich_debug = 0xfffe;*/
+int auich_debug = 0x0002;
 #define        AUICH_DEBUG_CODECIO     0x0001
 #define        AUICH_DEBUG_DMA         0x0002
 #define        AUICH_DEBUG_INTR        0x0004
@@ -1174,14 +1175,22 @@ void
 auich_halt_pipe(struct auich_softc *sc, int pipe)
 {
        int i;
-       uint32_t status;
+       uint32_t sts;
 
        bus_space_write_1(sc->iot, sc->aud_ioh, pipe + AUICH_CTRL, 0);
-       for (i = 0; i < 100; i++) {
-               status = bus_space_read_4(sc->iot, sc->aud_ioh, pipe + 
AUICH_STS);
-               if (status & AUICH_DCH)
-                       break;
-               DELAY(1);
+
+       /* wait for DMA halted and clear interrupt / event bits if needed */
+       for (i = 0; i < 1000; i++) {
+           sts = bus_space_read_2(sc->iot, sc->aud_ioh, pipe + sc->sc_sts_reg);
+
+           if (sts & (AUICH_CELV | AUICH_LVBCI | AUICH_BCIS | AUICH_FIFOE))
+               bus_space_write_2(sc->iot, sc->aud_ioh, pipe + sc->sc_sts_reg,
+                       AUICH_CELV | AUICH_LVBCI | AUICH_BCIS | AUICH_FIFOE);
+
+           if (sts & AUICH_DCH)
+               break;
+
+           DELAY(100);
        }
        bus_space_write_1(sc->iot, sc->aud_ioh, pipe + AUICH_CTRL, AUICH_RR);
 
@@ -1199,7 +1208,8 @@ auich_halt_output(v)
 
        DPRINTF(AUICH_DEBUG_DMA, ("%s: halt_output\n", sc->sc_dev.dv_xname));
 
-       bus_space_write_1(sc->iot, sc->aud_ioh, AUICH_PCMO + AUICH_CTRL, 
AUICH_RR);
+       auich_halt_pipe(sc, AUICH_PCMO);
+
        sc->pcmo.intr = NULL;
 
        return 0;
@@ -1216,8 +1226,9 @@ auich_halt_input(v)
 
        /* XXX halt both unless known otherwise */
 
-       bus_space_write_1(sc->iot, sc->aud_ioh, AUICH_PCMI + AUICH_CTRL, 
AUICH_RR);
-       bus_space_write_1(sc->iot, sc->aud_ioh, AUICH_MICI + AUICH_CTRL, 
AUICH_RR);
+       auich_halt_pipe(sc, AUICH_PCMI);
+       auich_halt_pipe(sc, AUICH_MICI);
+
        sc->pcmi.intr = NULL;
 
        return 0;
@@ -1381,8 +1392,12 @@ auich_intr(v)
 
 #ifdef AUICH_DEBUG
                if (sts & AUICH_FIFOE) {
-                       printf("%s: fifo underrun # %u\n",
-                           sc->sc_dev.dv_xname, ++sc->pcmo_fifoe);
+                       uint8_t civ = bus_space_read_1(sc->iot, sc->aud_ioh, 
AUICH_PCMO + AUICH_CIV);
+                       uint8_t ctrl = bus_space_read_1(sc->iot, sc->aud_ioh, 
AUICH_PCMO + AUICH_CTRL);
+                       uint16_t sts = bus_space_read_2(sc->iot, sc->aud_ioh, 
AUICH_PCMO + sc->sc_sts_reg);
+                       printf("%s: in fifo underrun # %u cvi=%d ctrl=0x%x 
sts=%b\n",
+                           sc->sc_dev.dv_xname, ++sc->pcmo_fifoe,
+                           civ, ctrl, sts, AUICH_ISTS_BITS);
                }
 #endif
 
@@ -1405,8 +1420,12 @@ auich_intr(v)
 
 #ifdef AUICH_DEBUG
                if (sts & AUICH_FIFOE) {
-                       printf("%s: in fifo overrun # %u\n",
-                           sc->sc_dev.dv_xname, ++sc->pcmi_fifoe);
+                       uint8_t civ = bus_space_read_1(sc->iot, sc->aud_ioh, 
AUICH_PCMI + AUICH_CIV);
+                       uint8_t ctrl = bus_space_read_1(sc->iot, sc->aud_ioh, 
AUICH_PCMI + AUICH_CTRL);
+                       uint16_t sts = bus_space_read_2(sc->iot, sc->aud_ioh, 
AUICH_PCMI + sc->sc_sts_reg);
+                       printf("%s: in fifo overrun cvi=%d ctrl=0x%x sts=%b\n",
+                           sc->sc_dev.dv_xname, ++sc->pcmi_fifoe,
+                           civ, ctrl, sts, AUICH_ISTS_BITS);
                }
 #endif
 
@@ -1427,8 +1446,15 @@ auich_intr(v)
                DPRINTF(AUICH_DEBUG_INTR,
                    ("auich_intr: ists=%b\n", sts, AUICH_ISTS_BITS));
 #ifdef AUICH_DEBUG
-               if (sts & AUICH_FIFOE)
+               if (sts & AUICH_FIFOE) {
+                       uint8_t civ = bus_space_read_1(sc->iot, sc->aud_ioh, 
AUICH_MICI + AUICH_CIV);
+                       uint8_t ctrl = bus_space_read_1(sc->iot, sc->aud_ioh, 
AUICH_MICI + AUICH_CTRL);
+                       uint16_t sts = bus_space_read_2(sc->iot, sc->aud_ioh, 
AUICH_MICI + sc->sc_sts_reg);
+                       printf("%s: in fifo overrun # %u cvi=%d ctrl=0x%x 
sts=%b\n",
+                           sc->sc_dev.dv_xname,
+                           civ, ctrl, sts, AUICH_ISTS_BITS);
                        printf("%s: mic fifo overrun\n", sc->sc_dev.dv_xname);
+               }
 #endif
 
                if (sts & AUICH_BCIS)
@@ -1454,34 +1480,37 @@ auich_trigger_pipe(struct auich_softc *s
        struct auich_dmalist *q;
 
        blksize = ring->blksize;
+       qptr = oqptr = bus_space_read_1(sc->iot, sc->aud_ioh, pipe + AUICH_CIV);
 
-       DPRINTF(AUICH_DEBUG_INTR, ("auich_trigger_pipe: ring->qptr: %d\n",
-           ring->qptr));
-
-       qptr = bus_space_read_1(sc->iot, sc->aud_ioh, pipe + AUICH_CIV);
-       oqptr = qptr;
-
-       DPRINTF(AUICH_DEBUG_INTR, ("auich_trigger_pipe: qptr: %d\n", qptr));
-
+       /* XXX remove this when no one reports problems */
        if(oqptr >= AUICH_DMALIST_MAX) {
                printf("%s: Unexpected CIV: %d\n", sc->sc_dev.dv_xname, oqptr);
                qptr = oqptr = 0;
        }
+
        do {
                q = &ring->dmalist[qptr];
                q->base = ring->p;
                q->len = (blksize / sc->sc_sample_size) | AUICH_DMAF_IOC;
 
+               DPRINTF(AUICH_DEBUG_INTR,
+                   ("auich_trigger_pipe: %p, %p = %x @ 0x%x qptr=%d\n",
+                   &ring->dmalist[qptr], q, q->len, q->base, qptr));
+
                ring->p += blksize;
                if (ring->p >= ring->end)
                        ring->p = ring->start;
-               qptr++;
-               if (qptr == AUICH_DMALIST_MAX)
-                       qptr = 0;
+
+               qptr = (qptr + 1) & AUICH_LVI_MASK;
        } while (qptr != oqptr);
+
        ring->qptr = qptr;
+
+       DPRINTF(AUICH_DEBUG_DMA,
+           ("auich_trigger_pipe: qptr=%d\n", qptr));
+
        bus_space_write_1(sc->iot, sc->aud_ioh, pipe + AUICH_LVI,
-           (AUICH_DMALIST_MAX - 1) & AUICH_LVI_MASK);
+           (qptr - 1) & AUICH_LVI_MASK);
        bus_space_write_1(sc->iot, sc->aud_ioh, pipe + AUICH_CTRL,
            AUICH_IOCE | AUICH_FEIE | AUICH_RPBM);
 }
@@ -1502,8 +1531,8 @@ auich_intr_pipe(struct auich_softc *sc, 
                q->len = (blksize / sc->sc_sample_size) | AUICH_DMAF_IOC;
 
                DPRINTF(AUICH_DEBUG_INTR,
-                   ("auich_intr: %p, %p = %x @ 0x%x\n",
-                   &ring->dmalist[qptr], q, q->len, q->base));
+                   ("auich_intr: %p, %p = %x @ 0x%x qptr=%d\n",
+                   &ring->dmalist[qptr], q, q->len, q->base, qptr));
 
                ring->p += blksize;
                if (ring->p >= ring->end)
@@ -1512,6 +1541,8 @@ auich_intr_pipe(struct auich_softc *sc, 
                qptr = (qptr + 1) & AUICH_LVI_MASK;
                if (ring->intr)
                        ring->intr(ring->arg);
+               else
+                   printf("auich_intr: got progress with intr==NULL\n");
        }
        ring->qptr = qptr;
 
@@ -1532,10 +1563,14 @@ auich_trigger_output(v, start, end, blks
        struct auich_softc *sc = v;
        struct auich_dma *p;
        size_t size;
-
+#ifdef AUICH_DEBUG
+       uint16_t sts;
+       sts = bus_space_read_2(sc->iot, sc->aud_ioh,
+                                       AUICH_PCMO + sc->sc_sts_reg);
        DPRINTF(AUICH_DEBUG_DMA,
-           ("auich_trigger_output(%x, %x, %d, %p, %p, %p)\n",
-           start, end, blksize, intr, arg, param));
+           ("auich_trigger_output(%x, %x, %d, %p, %p, %p) sts=%b\n",
+           start, end, blksize, intr, arg, param, sts, AUICH_ISTS_BITS));
+#endif
 
        if (sc->sc_pdma->addr == start)
                p = sc->sc_pdma;
@@ -1576,10 +1611,14 @@ auich_trigger_input(v, start, end, blksi
        struct auich_softc *sc = v;
        struct auich_dma *p;
        size_t size;
-
+#ifdef AUICH_DEBUG
+       uint16_t sts;
+       sts = bus_space_read_2(sc->iot, sc->aud_ioh,
+                                       AUICH_PCMI + sc->sc_sts_reg);
        DPRINTF(AUICH_DEBUG_DMA,
-           ("auich_trigger_input(%x, %x, %d, %p, %p, %p)\n",
-           start, end, blksize, intr, arg, param));
+           ("auich_trigger_input(%x, %x, %d, %p, %p, %p) sts=%b\n",
+           start, end, blksize, intr, arg, param, sts, AUICH_ISTS_BITS));
+#endif
 
        if (sc->sc_rdma->addr == start)
                p = sc->sc_rdma;
@@ -1770,7 +1809,8 @@ unsigned int
 auich_calibrate(struct auich_softc *sc)
 {
        struct timeval t1, t2;
-       u_int8_t ociv, nciv;
+       u_int8_t civ, ociv;
+       uint16_t sts, osts;
        u_int32_t wait_us, actual_48k_rate, bytes, ac97rate;
        void *temp_buffer;
        struct auich_dma *p;
@@ -1781,6 +1821,8 @@ auich_calibrate(struct auich_softc *sc)
         * much we actually get with what we expect.  Interval needs
         * to be sufficiently short that no interrupts are
         * generated.
+        * XXX: Is this true? We don't request any interrupts,
+        * so why should the chip issue any?
         */
 
        /* Setup a buffer */
@@ -1795,8 +1837,10 @@ auich_calibrate(struct auich_softc *sc)
                return (ac97rate);
        }
 
-       sc->pcmi.dmalist[0].base = p->map->dm_segs[0].ds_addr;
-       sc->pcmi.dmalist[0].len = bytes / sc->sc_sample_size;
+       /* get current CIV (usually 0 after reboot) */
+       ociv = civ = bus_space_read_1(sc->iot, sc->aud_ioh, AUICH_PCMI + 
AUICH_CIV);
+       sc->pcmi.dmalist[civ].base = p->map->dm_segs[0].ds_addr;
+       sc->pcmi.dmalist[civ].len = bytes / sc->sc_sample_size;
 
 
        /*
@@ -1812,44 +1856,78 @@ auich_calibrate(struct auich_softc *sc)
         */
 
        /* prepare */
-       ociv = bus_space_read_1(sc->iot, sc->aud_ioh, AUICH_PCMI + AUICH_CIV);
-       nciv = ociv;
        bus_space_write_4(sc->iot, sc->aud_ioh, AUICH_PCMI + AUICH_BDBAR,
            sc->sc_cddma + AUICH_PCMI_OFF(0));
+       /* we got only one valid sample, so set LVI to CIV
+        * otherwise we provoke a AUICH_FIFOE FIFO error
+        * which will confuse the chip later on. */
        bus_space_write_1(sc->iot, sc->aud_ioh, AUICH_PCMI + AUICH_LVI,
-                         (0 - 1) & AUICH_LVI_MASK);
+                         civ & AUICH_LVI_MASK);
 
-       /* start */
+       /* start, but don't request any interupts */
        microuptime(&t1);
        bus_space_write_1(sc->iot, sc->aud_ioh, AUICH_PCMI + AUICH_CTRL,
            AUICH_RPBM);
 
+       /* XXX remove this sometime */
+       osts = bus_space_read_2(sc->iot, sc->aud_ioh,
+                                       AUICH_PCMI + sc->sc_sts_reg);
        /* wait */
-       while (nciv == ociv) {
-               microuptime(&t2);
-               if (t2.tv_sec - t1.tv_sec > 1)
-                       break;
-               nciv = bus_space_read_1(sc->iot, sc->aud_ioh,
-                                       AUICH_PCMI + AUICH_CIV);
+       while(1) {
+           microuptime(&t2);
+           sts = bus_space_read_2(sc->iot, sc->aud_ioh,
+                                   AUICH_PCMI + sc->sc_sts_reg);
+           civ = bus_space_read_1(sc->iot, sc->aud_ioh,
+                                   AUICH_PCMI + AUICH_CIV);
+
+           /* turn time delta into us */
+           wait_us = ((t2.tv_sec - t1.tv_sec) * 1000000) + t2.tv_usec - 
t1.tv_usec;
+
+           /* this should actually never happen because civ==lvi */
+           if ((civ & AUICH_LVI_MASK) != (ociv & AUICH_LVI_MASK)) {
+               printf("%s: ac97 CIV progressed after %d us sts=%b civ=%d\n",
+                   sc->sc_dev.dv_xname, wait_us, sts, AUICH_ISTS_BITS, civ);
+               ociv = civ;
+           }
+           /* normal completion */
+           if (sts & (AUICH_DCH | AUICH_CELV | AUICH_LVBCI))
+                   break;
+           /* check for strange changes in STS - XXX remove it when 
everythings fine*/
+           if (sts != osts) {
+               printf("%s: ac97 sts changed after %d us sts=%b civ=%d\n",
+                   sc->sc_dev.dv_xname, wait_us, sts, AUICH_ISTS_BITS, civ);
+               osts = sts;
+           }
+           /* timeout: we expect 83333 us for 48k sampling rate,
+            * 600000 us will be enough even for 8k sampling rate */
+           if (wait_us > 600000) {
+               printf("%s: ac97 link rate timed out %d us sts=%b civ=%d\n",
+                   sc->sc_dev.dv_xname, wait_us, sts, AUICH_ISTS_BITS, civ);
+               /* reset and clean up*/
+               auich_halt_pipe(sc, AUICH_PCMI);
+               auich_halt_pipe(sc, AUICH_MICI);
+               auich_freem(sc, temp_buffer, M_DEVBUF);
+               /* return default sample rate */
+               return (ac97rate);
+           }
        }
-       microuptime(&t2);
-
-       /* reset */
-       bus_space_write_1(sc->iot, sc->aud_ioh, AUICH_PCMI + AUICH_CTRL, 
AUICH_RR);
-       bus_space_write_1(sc->iot, sc->aud_ioh, AUICH_MICI + AUICH_CTRL, 
AUICH_RR);
-       DELAY(100);
 
-       /* turn time delta into us */
-       wait_us = ((t2.tv_sec - t1.tv_sec) * 1000000) + t2.tv_usec - t1.tv_usec;
+       printf("%s: ac97 link rate calibration took %d us sts=%b civ=%d\n",
+               sc->sc_dev.dv_xname, wait_us, sts, AUICH_ISTS_BITS, civ);
 
+       /* reset and clean up */
+       auich_halt_pipe(sc, AUICH_PCMI);
+       auich_halt_pipe(sc, AUICH_MICI);
        auich_freem(sc, temp_buffer, M_DEVBUF);
 
-       if (nciv == ociv) {
-               printf("%s: ac97 link rate calibration timed out after %d us\n",
-                      sc->sc_dev.dv_xname, wait_us);
-               return (ac97rate);
-       }
+#ifdef AUICH_DEBUG
+       sts = bus_space_read_2(sc->iot, sc->aud_ioh, AUICH_PCMI + 
sc->sc_sts_reg);
+       civ = bus_space_read_4(sc->iot, sc->aud_ioh, AUICH_PCMI + AUICH_CIV);
+       printf("%s: after calibration and reset sts=%b civ=%d\n",
+           sc->sc_dev.dv_xname, sts, AUICH_ISTS_BITS, civ);
+#endif
 
+       /* now finally calculate measured samplerate */
        actual_48k_rate = (bytes * 250000) / wait_us;
 
        if (actual_48k_rate <= 48500)

Reply via email to