- 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
This commit is contained in:
ariff 2006-02-18 10:24:48 +00:00
parent 8c27b94bed
commit 748737a8af

View File

@ -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,