summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorariff <ariff@FreeBSD.org>2006-02-18 10:24:48 +0000
committerariff <ariff@FreeBSD.org>2006-02-18 10:24:48 +0000
commit748737a8af54bcdc13e93e17e6d0ea0e4b3ff210 (patch)
tree954a397ca2741274b1d8e3eac4afddd0202b5c1e
parent8c27b94bed240dbd9276f9ec747d59d076bab785 (diff)
downloadFreeBSD-src-748737a8af54bcdc13e93e17e6d0ea0e4b3ff210.zip
FreeBSD-src-748737a8af54bcdc13e93e17e6d0ea0e4b3ff210.tar.gz
- Fix severe crackling after long running and abusive module load / unload by
forcing DMA alignment to default buffer size. - Make sure DMA pointer properly aligned to avoid being truncated by caller which causing severe underruns and random popping (especially in 32bit playback / recording). - Add AC97 inverted external amplifier quirk for Maxselect x710s - http://maxselect.ru/ MFC after: 1 week
-rw-r--r--sys/dev/sound/pci/atiixp.c74
1 files changed, 59 insertions, 15 deletions
diff --git a/sys/dev/sound/pci/atiixp.c b/sys/dev/sound/pci/atiixp.c
index f412cb2..10036b0 100644
--- a/sys/dev/sound/pci/atiixp.c
+++ b/sys/dev/sound/pci/atiixp.c
@@ -30,12 +30,14 @@
* Features
* * 16bit playback / recording
* * 32bit native playback - yay!
+ * * 32bit native recording (seems broken on few hardwares)
*
* Issues / TODO:
* * SPDIF
* * Support for more than 2 channels.
* * VRA ? VRM ? DRA ?
- * * 32bit native recording (seems broken, disabled)
+ * * 32bit native recording seems broken on few hardwares, most
+ * probably because of incomplete VRA/DRA cleanup.
*
*
* Thanks goes to:
@@ -65,10 +67,10 @@ SND_DECLARE_FILE("$FreeBSD$");
struct atiixp_dma_op {
- uint32_t addr;
- uint16_t status;
- uint16_t size;
- uint32_t next;
+ volatile uint32_t addr;
+ volatile uint16_t status;
+ volatile uint16_t size;
+ volatile uint32_t next;
};
struct atiixp_info;
@@ -468,8 +470,7 @@ atiixp_chan_setformat(kobj_t obj, void *data, uint32_t format)
if (ch->dir == PCMDIR_REC) {
value = atiixp_rd(sc, ATI_REG_CMD);
value &= ~ATI_REG_CMD_INTERLEAVE_IN;
- if (ch->caps_32bit == 0 ||
- (format & (AFMT_8BIT|AFMT_16BIT)) != 0)
+ if ((format & AFMT_32BIT) == 0)
value |= ATI_REG_CMD_INTERLEAVE_IN;
atiixp_wr(sc, ATI_REG_CMD, value);
} else {
@@ -482,8 +483,7 @@ atiixp_chan_setformat(kobj_t obj, void *data, uint32_t format)
atiixp_wr(sc, ATI_REG_OUT_DMA_SLOT, value);
value = atiixp_rd(sc, ATI_REG_CMD);
value &= ~ATI_REG_CMD_INTERLEAVE_OUT;
- if (ch->caps_32bit == 0 ||
- (format & (AFMT_8BIT|AFMT_16BIT)) != 0)
+ if ((format & AFMT_32BIT) == 0)
value |= ATI_REG_CMD_INTERLEAVE_OUT;
atiixp_wr(sc, ATI_REG_CMD, value);
value = atiixp_rd(sc, ATI_REG_6CH_REORDER);
@@ -585,13 +585,36 @@ atiixp_chan_getptr(kobj_t obj, void *data)
{
struct atiixp_chinfo *ch = data;
struct atiixp_info *sc = ch->parent;
- uint32_t ptr;
+ uint32_t addr, align, retry, sz;
+ volatile uint32_t ptr;
+
+ addr = sndbuf_getbufaddr(ch->buffer);
+ align = (ch->fmt & AFMT_32BIT) ? 7 : 3;
+ retry = 100;
+ sz = sndbuf_getblksz(ch->buffer) * ch->dma_segs;
atiixp_lock(sc);
- ptr = atiixp_rd(sc, ch->dma_dt_cur_bit);
+ do {
+ ptr = atiixp_rd(sc, ch->dma_dt_cur_bit);
+ if (ptr < addr)
+ continue;
+ ptr -= addr;
+ if (ptr < sz && !(ptr & align))
+ break;
+ } while (--retry);
atiixp_unlock(sc);
- return ptr;
+#if 0
+ if (retry != 100) {
+ device_printf(sc->dev,
+ "%saligned hwptr: dir=PCMDIR_%s ptr=%u fmt=0x%08x retry=%d\n",
+ (ptr & align) ? "un" : "",
+ (ch->dir == PCMDIR_PLAY) ? "PLAY" : "REC", ptr,
+ ch->fmt, 100 - retry);
+ }
+#endif
+
+ return (retry > 0) ? ptr & ~align : 0;
}
static struct pcmchan_caps *
@@ -709,6 +732,7 @@ static void
atiixp_chip_post_init(void *arg)
{
struct atiixp_info *sc = (struct atiixp_info *)arg;
+ uint32_t subdev;
int i, timeout, found;
char status[SND_STATUSLEN];
@@ -776,6 +800,15 @@ atiixp_chip_post_init(void *arg)
if (sc->codec == NULL)
goto postinitbad;
+ subdev = (pci_get_subdevice(sc->dev) << 16) | pci_get_subvendor(sc->dev);
+ switch (subdev) {
+ case 0x2043161f: /* Maxselect x710s - http://maxselect.ru/ */
+ ac97_setflags(sc->codec, ac97_getflags(sc->codec) | AC97_F_EAPD_INV);
+ break;
+ default:
+ break;
+ }
+
mixer_init(sc->dev, ac97_getmixerclass(), sc->codec);
if (pcm_register(sc->dev, sc, ATI_IXP_NPCHAN, ATI_IXP_NRCHAN))
@@ -893,14 +926,25 @@ atiixp_pci_attach(device_t dev)
i = ATI_IXP_DMA_CHSEGS_MIN;
if (i > ATI_IXP_DMA_CHSEGS_MAX)
i = ATI_IXP_DMA_CHSEGS_MAX;
- /* round the value */
- sc->dma_segs = i & ~1;
+ sc->dma_segs = i;
}
/*
+ * round the value to the nearest ^2
+ */
+ i = 0;
+ while (sc->dma_segs >> i)
+ i++;
+ sc->dma_segs = 1 << (i - 1);
+ if (sc->dma_segs < ATI_IXP_DMA_CHSEGS_MIN)
+ sc->dma_segs = ATI_IXP_DMA_CHSEGS_MIN;
+ else if (sc->dma_segs > ATI_IXP_DMA_CHSEGS_MAX)
+ sc->dma_segs = ATI_IXP_DMA_CHSEGS_MAX;
+
+ /*
* DMA tag for scatter-gather buffers and link pointers
*/
- if (bus_dma_tag_create(/*parent*/NULL, /*alignment*/2, /*boundary*/0,
+ if (bus_dma_tag_create(/*parent*/NULL, /*alignment*/sc->bufsz, /*boundary*/0,
/*lowaddr*/BUS_SPACE_MAXADDR_32BIT,
/*highaddr*/BUS_SPACE_MAXADDR,
/*filter*/NULL, /*filterarg*/NULL,
OpenPOWER on IntegriCloud