diff options
author | orion <orion@FreeBSD.org> | 2002-01-18 18:44:41 +0000 |
---|---|---|
committer | orion <orion@FreeBSD.org> | 2002-01-18 18:44:41 +0000 |
commit | 98cc9db9b57000c648346bbbd421a1541beb4577 (patch) | |
tree | 6f93350975efe102c98f178950685b95b48a9cb7 /sys | |
parent | 1a5d09da65396eea992b33aa6d0253aecf4c1c41 (diff) | |
download | FreeBSD-src-98cc9db9b57000c648346bbbd421a1541beb4577.zip FreeBSD-src-98cc9db9b57000c648346bbbd421a1541beb4577.tar.gz |
Change ich_calibrate to busy wait on buffer fill level and use a more
likely looking rate calculation.
Install interrupt handler before calling ich_init as the initialization
occasionally generates spurious interrupts.
These changes are derived from cg's work in progress version of this
driver.
Diffstat (limited to 'sys')
-rw-r--r-- | sys/dev/sound/pci/ich.c | 117 |
1 files changed, 77 insertions, 40 deletions
diff --git a/sys/dev/sound/pci/ich.c b/sys/dev/sound/pci/ich.c index 9b0a757..64cd5c7 100644 --- a/sys/dev/sound/pci/ich.c +++ b/sys/dev/sound/pci/ich.c @@ -293,9 +293,8 @@ ichchan_setspeed(kobj_t obj, void *data, u_int32_t speed) int r; if (sc->ac97rate <= 32000 || sc->ac97rate >= 64000) sc->ac97rate = 48000; - r = speed * 48000 / sc->ac97rate; - ch->spd = ac97_setrate(sc->codec, ch->spdreg, r) * - sc->ac97rate / 48000; + r = (speed * 48000) / sc->ac97rate; + ch->spd = (ac97_setrate(sc->codec, ch->spdreg, r) * sc->ac97rate) / 48000; } else { ch->spd = 48000; } @@ -325,7 +324,7 @@ ichchan_trigger(kobj_t obj, void *data, int go) case PCMTRIG_START: ch->run = 1; ich_wr(sc, ch->regbase + ICH_REG_X_BDBAR, (u_int32_t)vtophys(ch->dtbl), 4); - ich_wr(sc, ch->regbase + ICH_REG_X_CR, ICH_X_CR_RPBM | ICH_X_CR_LVBIE | ICH_X_CR_IOCE | ICH_X_CR_FEIE, 1); + ich_wr(sc, ch->regbase + ICH_REG_X_CR, ICH_X_CR_RPBM | ICH_X_CR_LVBIE | ICH_X_CR_IOCE, 1); break; case PCMTRIG_ABORT: @@ -445,44 +444,82 @@ ich_initsys(struct sc_info* sc) static unsigned int ich_calibrate(struct sc_info *sc) { - /* Grab audio from input for fixed interval and compare how + struct sc_chinfo *ch = &sc->ch[1]; + struct timeval t1, t2; + u_int8_t ociv, nciv; + u_int32_t wait_us, actual_48k_rate, bytes; + + /* + * Grab audio from input for fixed interval and compare how * much we actually get with what we expect. Interval needs * to be sufficiently short that no interrupts are - * generated. */ - struct sc_chinfo *ch = &sc->ch[1]; - u_int16_t target_picb, actual_picb; - u_int32_t wait_us, actual_48k_rate; - + * generated. + */ + KASSERT(ch->regbase == ICH_REG_PI_BASE, ("wrong direction")); - - ichchan_setspeed(0, ch, 48000); - ichchan_setblocksize(0, ch, ICH_DEFAULT_BUFSZ); - target_picb = ch->dtbl[0].length / 2; /* half interrupt interval */ - wait_us = target_picb * 1000 / (2 * 48); /* (2 == stereo -> mono) */ + bytes = sndbuf_getsize(ch->buffer) / 2; + ichchan_setblocksize(0, ch, bytes); + + /* + * our data format is stereo, 16 bit so each sample is 4 bytes. + * assuming we get 48000 samples per second, we get 192000 bytes/sec. + * we're going to start recording with interrupts disabled and measure + * the time taken for one block to complete. we know the block size, + * we know the time in microseconds, we calculate the sample rate: + * + * actual_rate [bps] = bytes / (time [s] * 4) + * actual_rate [bps] = (bytes * 1000000) / (time [us] * 4) + * actual_rate [Hz] = (bytes * 250000) / time [us] + */ + + /* prepare */ + ociv = ich_rd(sc, ch->regbase + ICH_REG_X_CIV, 1); + nciv = ociv; + ich_wr(sc, ch->regbase + ICH_REG_X_BDBAR, (u_int32_t)vtophys(ch->dtbl), 4); + + /* start */ + microtime(&t1); + ich_wr(sc, ch->regbase + ICH_REG_X_CR, ICH_X_CR_RPBM, 1); - if (bootverbose) - device_printf(sc->dev, "Calibration interval %d us\n", - wait_us); + /* wait */ + while (nciv == ociv) { + microtime(&t2); + if (t2.tv_sec - t1.tv_sec > 1) + break; + nciv = ich_rd(sc, ch->regbase + ICH_REG_X_CIV, 1); + } + microtime(&t2); - ichchan_trigger(0, ch, PCMTRIG_START); - DELAY(wait_us); - actual_picb = ich_rd(sc, ch->regbase + ICH_REG_X_PICB, 2); - ichchan_trigger(0, ch, PCMTRIG_ABORT); + /* stop */ + ich_wr(sc, ch->regbase + ICH_REG_X_CR, 0, 1); - actual_48k_rate = 48000 * (2 * target_picb - actual_picb) / - (target_picb); + /* reset */ + DELAY(100); + ich_wr(sc, ch->regbase + ICH_REG_X_CR, ICH_X_CR_RR, 1); - if (actual_48k_rate > 48500 || actual_48k_rate < 47500) { + /* turn time delta into us */ + wait_us = ((t2.tv_sec - t1.tv_sec) * 1000000) + t2.tv_usec - t1.tv_usec; + + if (nciv == ociv) { + device_printf(sc->dev, "ac97 link rate calibration timed out after %d us\n", wait_us); + return 0; + } + + actual_48k_rate = (bytes * 250000) / wait_us; + + if (actual_48k_rate < 47500 || actual_48k_rate > 48500) { sc->ac97rate = actual_48k_rate; } else { sc->ac97rate = 48000; } - if (bootverbose) - device_printf(sc->dev, - "Estimated AC97 link rate %d, using %d\n", - actual_48k_rate, sc->ac97rate); + if (bootverbose || sc->ac97rate != 48000) { + device_printf(sc->dev, "measured ac97 link rate at %d Hz", actual_48k_rate); + if (sc->ac97rate != actual_48k_rate) + printf(", will use %d Hz", sc->ac97rate); + printf("\n"); + } return sc->ac97rate; } @@ -600,6 +637,13 @@ ich_pci_attach(device_t dev) goto bad; } + sc->irqid = 0; + sc->irq = bus_alloc_resource(dev, SYS_RES_IRQ, &sc->irqid, 0, ~0, 1, RF_ACTIVE | RF_SHAREABLE); + if (!sc->irq || snd_setup_intr(dev, sc->irq, INTR_MPSAFE, ich_intr, sc, &sc->ih)) { + device_printf(dev, "unable to map interrupt\n"); + goto bad; + } + if (ich_init(sc)) { device_printf(dev, "unable to initialize the card\n"); goto bad; @@ -617,13 +661,6 @@ ich_pci_attach(device_t dev) sc->hasmic = extcaps & AC97_CAP_MICCHANNEL; ac97_setextmode(sc->codec, sc->hasvra | sc->hasvrm | sc->hasmic); - sc->irqid = 0; - sc->irq = bus_alloc_resource(dev, SYS_RES_IRQ, &sc->irqid, 0, ~0, 1, RF_ACTIVE | RF_SHAREABLE); - if (!sc->irq || snd_setup_intr(dev, sc->irq, INTR_MPSAFE, ich_intr, sc, &sc->ih)) { - device_printf(dev, "unable to map interrupt\n"); - goto bad; - } - if (pcm_register(dev, sc, 1, sc->hasmic? 2 : 1)) goto bad; @@ -645,16 +682,16 @@ ich_pci_attach(device_t dev) bad: if (sc->codec) ac97_destroy(sc->codec); + if (sc->ih) + bus_teardown_intr(dev, sc->irq, sc->ih); + if (sc->irq) + bus_release_resource(dev, SYS_RES_IRQ, sc->irqid, sc->irq); if (sc->nambar) bus_release_resource(dev, SYS_RES_IOPORT, sc->nambarid, sc->nambar); if (sc->nabmbar) bus_release_resource(dev, SYS_RES_IOPORT, sc->nabmbarid, sc->nabmbar); - if (sc->ih) - bus_teardown_intr(dev, sc->irq, sc->ih); - if (sc->irq) - bus_release_resource(dev, SYS_RES_IRQ, sc->irqid, sc->irq); free(sc, M_DEVBUF); return ENXIO; } |