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)