summaryrefslogtreecommitdiffstats
path: root/sys
diff options
context:
space:
mode:
Diffstat (limited to 'sys')
-rw-r--r--sys/dev/sound/pci/ich.c117
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;
}
OpenPOWER on IntegriCloud