Various cleanups. Revert DMA alignmnet back to 2 since the original

cause of DMA pointer train wreck has been solved.

Reminded by:	silby
MFC after:	1 month
This commit is contained in:
ariff 2006-10-01 13:30:30 +00:00
parent 09a0f85203
commit 845fa2db06
2 changed files with 171 additions and 100 deletions

View File

@ -65,6 +65,18 @@
SND_DECLARE_FILE("$FreeBSD$");
#define ATI_IXP_DMA_RETRY_MAX 100
#define ATI_IXP_DMA_CHSEGS_DEFAULT 2
#define ATI_IXP_BUFSZ_MIN 4096
#define ATI_IXP_BUFSZ_MAX 65536
#define ATI_IXP_BUFSZ_DEFAULT 16384
#ifdef ATI_IXP_DEBUG_VERBOSE
#undef ATI_IXP_DEBUG
#define ATI_IXP_DEBUG 1
#endif
struct atiixp_dma_op {
volatile uint32_t addr;
volatile uint16_t status;
@ -81,7 +93,10 @@ struct atiixp_chinfo {
struct atiixp_dma_op *sgd_table;
bus_addr_t sgd_addr;
uint32_t enable_bit, flush_bit, linkptr_bit, dma_dt_cur_bit;
uint32_t dma_segs;
uint32_t dma_segs, dma_blksz;
#ifdef ATI_IXP_DEBUG
uint32_t dma_ptr, dma_prevptr;
#endif
uint32_t fmt;
int caps_32bit, dir, active;
};
@ -141,7 +156,7 @@ static struct pcmchan_caps atiixp_caps_32bit = {
};
static struct pcmchan_caps atiixp_caps = {
ATI_IXP_BASE_RATE,
ATI_IXP_BASE_RATE,
ATI_IXP_BASE_RATE,
atiixp_fmt, 0
};
@ -159,9 +174,9 @@ static const struct {
static void atiixp_enable_interrupts(struct atiixp_info *);
static void atiixp_disable_interrupts(struct atiixp_info *);
static void atiixp_reset_aclink(struct atiixp_info *);
static void atiixp_flush_dma(struct atiixp_info *, struct atiixp_chinfo *);
static void atiixp_enable_dma(struct atiixp_info *, struct atiixp_chinfo *);
static void atiixp_disable_dma(struct atiixp_info *, struct atiixp_chinfo *);
static void atiixp_flush_dma(struct atiixp_chinfo *);
static void atiixp_enable_dma(struct atiixp_chinfo *);
static void atiixp_disable_dma(struct atiixp_chinfo *);
static int atiixp_waitready_codec(struct atiixp_info *);
static int atiixp_rdcd(kobj_t, void *, int);
@ -174,6 +189,7 @@ static int atiixp_chan_setspeed(kobj_t, void *, uint32_t);
static int atiixp_chan_setblocksize(kobj_t, void *, uint32_t);
static void atiixp_buildsgdt(struct atiixp_chinfo *);
static int atiixp_chan_trigger(kobj_t, void *, int);
static __inline uint32_t atiixp_dmapos(struct atiixp_chinfo *);
static int atiixp_chan_getptr(kobj_t, void *);
static struct pcmchan_caps *atiixp_chan_getcaps(kobj_t, void *);
@ -208,7 +224,10 @@ atiixp_enable_interrupts(struct atiixp_info *sc)
* Disable / ignore internal xrun/spdf interrupt flags
* since it doesn't interest us (for now).
*/
#if 0
#if 1
value &= ~(ATI_REG_IER_IN_XRUN_EN | ATI_REG_IER_OUT_XRUN_EN |
ATI_REG_IER_SPDF_XRUN_EN | ATI_REG_IER_SPDF_STATUS_EN);
#else
value |= ATI_REG_IER_IN_XRUN_EN;
value |= ATI_REG_IER_OUT_XRUN_EN;
@ -243,7 +262,7 @@ atiixp_reset_aclink(struct atiixp_info *sc)
/* have to wait at least 10 usec for it to initialise */
DELAY(20);
};
}
/* perform a soft reset */
value = atiixp_rd(sc, ATI_REG_CMD);
@ -284,7 +303,7 @@ atiixp_reset_aclink(struct atiixp_info *sc)
/* check if its active now */
value = atiixp_rd(sc, ATI_REG_CMD);
};
}
if (timeout == 0)
device_printf(sc->dev, "giving up aclink reset\n");
@ -300,32 +319,32 @@ atiixp_reset_aclink(struct atiixp_info *sc)
}
static void
atiixp_flush_dma(struct atiixp_info *sc, struct atiixp_chinfo *ch)
atiixp_flush_dma(struct atiixp_chinfo *ch)
{
atiixp_wr(sc, ATI_REG_FIFO_FLUSH, ch->flush_bit);
atiixp_wr(ch->parent, ATI_REG_FIFO_FLUSH, ch->flush_bit);
}
static void
atiixp_enable_dma(struct atiixp_info *sc, struct atiixp_chinfo *ch)
atiixp_enable_dma(struct atiixp_chinfo *ch)
{
uint32_t value;
value = atiixp_rd(sc, ATI_REG_CMD);
value = atiixp_rd(ch->parent, ATI_REG_CMD);
if (!(value & ch->enable_bit)) {
value |= ch->enable_bit;
atiixp_wr(sc, ATI_REG_CMD, value);
atiixp_wr(ch->parent, ATI_REG_CMD, value);
}
}
static void
atiixp_disable_dma(struct atiixp_info *sc, struct atiixp_chinfo *ch)
static void
atiixp_disable_dma(struct atiixp_chinfo *ch)
{
uint32_t value;
value = atiixp_rd(sc, ATI_REG_CMD);
value = atiixp_rd(ch->parent, ATI_REG_CMD);
if (value & ch->enable_bit) {
value &= ~ch->enable_bit;
atiixp_wr(sc, ATI_REG_CMD, value);
atiixp_wr(ch->parent, ATI_REG_CMD, value);
}
}
@ -340,11 +359,11 @@ atiixp_waitready_codec(struct atiixp_info *sc)
do {
if ((atiixp_rd(sc, ATI_REG_PHYS_OUT_ADDR) &
ATI_REG_PHYS_OUT_ADDR_EN) == 0)
return 0;
return (0);
DELAY(1);
} while (timeout--);
} while (--timeout);
return -1;
return (-1);
}
static int
@ -355,7 +374,7 @@ atiixp_rdcd(kobj_t obj, void *devinfo, int reg)
int timeout;
if (atiixp_waitready_codec(sc))
return -1;
return (-1);
data = (reg << ATI_REG_PHYS_OUT_ADDR_SHIFT) |
ATI_REG_PHYS_OUT_ADDR_EN |
@ -364,20 +383,20 @@ atiixp_rdcd(kobj_t obj, void *devinfo, int reg)
atiixp_wr(sc, ATI_REG_PHYS_OUT_ADDR, data);
if (atiixp_waitready_codec(sc))
return -1;
return (-1);
timeout = 500;
do {
data = atiixp_rd(sc, ATI_REG_PHYS_IN_ADDR);
if (data & ATI_REG_PHYS_IN_READ_FLAG)
return data >> ATI_REG_PHYS_IN_DATA_SHIFT;
return (data >> ATI_REG_PHYS_IN_DATA_SHIFT);
DELAY(1);
} while (timeout--);
} while (--timeout);
if (reg < 0x7c)
device_printf(sc->dev, "codec read timeout! (reg 0x%x)\n", reg);
return -1;
return (-1);
}
static int
@ -386,7 +405,7 @@ atiixp_wrcd(kobj_t obj, void *devinfo, int reg, uint32_t data)
struct atiixp_info *sc = devinfo;
if (atiixp_waitready_codec(sc))
return -1;
return (-1);
data = (data << ATI_REG_PHYS_OUT_DATA_SHIFT) |
(((uint32_t)reg) << ATI_REG_PHYS_OUT_ADDR_SHIFT) |
@ -394,7 +413,7 @@ atiixp_wrcd(kobj_t obj, void *devinfo, int reg, uint32_t data)
atiixp_wr(sc, ATI_REG_PHYS_OUT_ADDR, data);
return 0;
return (0);
}
static kobj_method_t atiixp_ac97_methods[] = {
@ -440,21 +459,22 @@ atiixp_chan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b,
ch->channel = c;
ch->dir = dir;
ch->dma_segs = sc->dma_segs;
ch->dma_blksz = sc->bufsz / sc->dma_segs;
atiixp_unlock(sc);
if (sndbuf_alloc(ch->buffer, sc->parent_dmat, sc->bufsz) == -1)
return NULL;
return (NULL);
atiixp_lock(sc);
num = sc->registered_channels++;
ch->sgd_table = &sc->sgd_table[num * ch->dma_segs];
ch->sgd_addr = sc->sgd_addr +
(num * ch->dma_segs * sizeof(struct atiixp_dma_op));
atiixp_disable_dma(sc, ch);
atiixp_disable_dma(ch);
atiixp_unlock(sc);
return ch;
return (ch);
}
static int
@ -491,14 +511,14 @@ atiixp_chan_setformat(kobj_t obj, void *data, uint32_t format)
ch->fmt = format;
atiixp_unlock(sc);
return 0;
return (0);
}
static int
atiixp_chan_setspeed(kobj_t obj, void *data, uint32_t spd)
{
/* XXX We're supposed to do VRA/DRA processing right here */
return ATI_IXP_BASE_RATE;
return (ATI_IXP_BASE_RATE);
}
static int
@ -507,31 +527,40 @@ atiixp_chan_setblocksize(kobj_t obj, void *data, uint32_t blksz)
struct atiixp_chinfo *ch = data;
struct atiixp_info *sc = ch->parent;
if (blksz > (sc->bufsz / ch->dma_segs))
blksz = sc->bufsz / ch->dma_segs;
/* XXX Force static blocksize */
sndbuf_resize(ch->buffer, ch->dma_segs, ch->dma_blksz);
sndbuf_resize(ch->buffer, ch->dma_segs, blksz);
if (ch->dma_blksz != sndbuf_getblksz(ch->buffer)) {
device_printf(sc->dev,
"%s: WARNING - dma_blksz=%u != blksz=%u !!!\n",
__func__, ch->dma_blksz, sndbuf_getblksz(ch->buffer));
ch->dma_blksz = sndbuf_getblksz(ch->buffer);
}
return sndbuf_getblksz(ch->buffer);
return (ch->dma_blksz);
}
static void
atiixp_buildsgdt(struct atiixp_chinfo *ch)
{
uint32_t addr, blksz;
uint32_t addr;
int i;
addr = sndbuf_getbufaddr(ch->buffer);
blksz = sndbuf_getblksz(ch->buffer);
for (i = 0; i < ch->dma_segs; i++) {
ch->sgd_table[i].addr = htole32(addr + (i * blksz));
ch->sgd_table[i].addr = htole32(addr + (i * ch->dma_blksz));
ch->sgd_table[i].status = htole16(0);
ch->sgd_table[i].size = htole16(blksz >> 2);
ch->sgd_table[i].next = htole32((uint32_t)ch->sgd_addr +
ch->sgd_table[i].size = htole16(ch->dma_blksz >> 2);
ch->sgd_table[i].next = htole32((uint32_t)ch->sgd_addr +
(((i + 1) % ch->dma_segs) *
sizeof(struct atiixp_dma_op)));
}
#ifdef ATI_IXP_DEBUG
ch->dma_ptr = 0;
ch->dma_prevptr = 0;
#endif
}
static int
@ -545,21 +574,21 @@ atiixp_chan_trigger(kobj_t obj, void *data, int go)
switch (go) {
case PCMTRIG_START:
atiixp_flush_dma(sc, ch);
atiixp_flush_dma(ch);
atiixp_buildsgdt(ch);
atiixp_wr(sc, ch->linkptr_bit, 0);
atiixp_enable_dma(sc, ch);
atiixp_enable_dma(ch);
atiixp_wr(sc, ch->linkptr_bit,
(uint32_t)ch->sgd_addr | ATI_REG_LINKPTR_EN);
break;
case PCMTRIG_STOP:
case PCMTRIG_ABORT:
atiixp_disable_dma(sc, ch);
atiixp_flush_dma(sc, ch);
atiixp_disable_dma(ch);
atiixp_flush_dma(ch);
break;
default:
atiixp_unlock(sc);
return 0;
return (0);
break;
}
@ -575,7 +604,60 @@ atiixp_chan_trigger(kobj_t obj, void *data, int go)
atiixp_unlock(sc);
return 0;
return (0);
}
static __inline uint32_t
atiixp_dmapos(struct atiixp_chinfo *ch)
{
struct atiixp_info *sc = ch->parent;
uint32_t reg, addr, sz, retry;
volatile uint32_t ptr;
reg = ch->dma_dt_cur_bit;
addr = sndbuf_getbufaddr(ch->buffer);
sz = ch->dma_segs * ch->dma_blksz;
retry = ATI_IXP_DMA_RETRY_MAX;
do {
ptr = atiixp_rd(sc, reg);
if (ptr < addr)
continue;
ptr -= addr;
if (ptr < sz) {
#ifdef ATI_IXP_DEBUG
if ((ptr & ~(ch->dma_blksz - 1)) != ch->dma_ptr) {
uint32_t delta;
delta = (sz + ptr - ch->dma_prevptr) % sz;
#ifndef ATI_IXP_DEBUG_VERBOSE
if (delta < ch->dma_blksz)
#endif
device_printf(sc->dev,
"PCMDIR_%s: incoherent DMA "
"dma_prevptr=%u ptr=%u "
"dma_ptr=%u dma_segs=%u "
"[delta=%u != dma_blksz=%u] "
"(%s)\n",
(ch->dir == PCMDIR_PLAY) ?
"PLAY" : "REC",
ch->dma_prevptr, ptr,
ch->dma_ptr, ch->dma_segs,
delta, ch->dma_blksz,
(delta < ch->dma_blksz) ?
"OVERLAPPED!" : "Ok");
ch->dma_ptr = ptr & ~(ch->dma_blksz - 1);
}
ch->dma_prevptr = ptr;
#endif
return (ptr);
}
} while (--retry);
device_printf(sc->dev, "PCMDIR_%s: invalid DMA pointer ptr=%u\n",
(ch->dir == PCMDIR_PLAY) ? "PLAY" : "REC", ptr);
return (0);
}
static int
@ -583,36 +665,13 @@ atiixp_chan_getptr(kobj_t obj, void *data)
{
struct atiixp_chinfo *ch = data;
struct atiixp_info *sc = ch->parent;
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;
uint32_t ptr;
atiixp_lock(sc);
do {
ptr = atiixp_rd(sc, ch->dma_dt_cur_bit);
if (ptr < addr)
continue;
ptr -= addr;
if (ptr < sz && !(ptr & align))
break;
} while (--retry);
ptr = atiixp_dmapos(ch);
atiixp_unlock(sc);
#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 : 0;
return (ptr);
}
static struct pcmchan_caps *
@ -621,8 +680,8 @@ atiixp_chan_getcaps(kobj_t obj, void *data)
struct atiixp_chinfo *ch = data;
if (ch->caps_32bit)
return &atiixp_caps_32bit;
return &atiixp_caps;
return (&atiixp_caps_32bit);
return (&atiixp_caps);
}
static kobj_method_t atiixp_chan_methods[] = {
@ -644,6 +703,7 @@ static void
atiixp_intr(void *p)
{
struct atiixp_info *sc = p;
struct atiixp_chinfo *ch;
uint32_t status, enable, detected_codecs;
atiixp_lock(sc);
@ -655,13 +715,23 @@ atiixp_intr(void *p)
}
if ((status & ATI_REG_ISR_IN_STATUS) && sc->rch.channel) {
ch = &sc->rch;
#ifdef ATI_IXP_DEBUG
ch->dma_ptr = (ch->dma_ptr + ch->dma_blksz) %
(ch->dma_segs * ch->dma_blksz);
#endif
atiixp_unlock(sc);
chn_intr(sc->rch.channel);
chn_intr(ch->channel);
atiixp_lock(sc);
}
if ((status & ATI_REG_ISR_OUT_STATUS) && sc->pch.channel) {
ch = &sc->pch;
#ifdef ATI_IXP_DEBUG
ch->dma_ptr = (ch->dma_ptr + ch->dma_blksz) %
(ch->dma_segs * ch->dma_blksz);
#endif
atiixp_unlock(sc);
chn_intr(sc->pch.channel);
chn_intr(ch->channel);
atiixp_lock(sc);
}
@ -743,11 +813,11 @@ atiixp_chip_post_init(void *arg)
/* wait for the interrupts to happen */
timeout = 100;
while (--timeout) {
do {
msleep(sc, sc->lock, PWAIT, "ixpslp", 1);
if (sc->codec_not_ready_bits)
break;
}
} while (--timeout);
atiixp_disable_interrupts(sc);
@ -814,7 +884,7 @@ atiixp_chip_post_init(void *arg)
for (i = 0; i < ATI_IXP_NRCHAN; i++)
pcm_addchan(sc->dev, PCMDIR_REC, &atiixp_chan_class, sc);
snprintf(status, SND_STATUSLEN, "at memory 0x%lx irq %ld %s",
snprintf(status, SND_STATUSLEN, "at memory 0x%lx irq %ld %s",
rman_get_start(sc->reg), rman_get_start(sc->irq),
PCM_KLDSTRING(snd_atiixp));
@ -877,15 +947,15 @@ atiixp_pci_probe(device_t dev)
vendor = pci_get_vendor(dev);
devid = pci_get_device(dev);
for (i = 0; i < sizeof(atiixp_hw)/sizeof(atiixp_hw[0]); i++) {
for (i = 0; i < sizeof(atiixp_hw) / sizeof(atiixp_hw[0]); i++) {
if (vendor == atiixp_hw[i].vendor &&
devid == atiixp_hw[i].devid) {
device_set_desc(dev, atiixp_hw[i].desc);
return BUS_PROBE_DEFAULT;
return (BUS_PROBE_DEFAULT);
}
}
return ENXIO;
return (ENXIO);
}
static int
@ -896,7 +966,7 @@ atiixp_pci_attach(device_t dev)
if ((sc = malloc(sizeof(*sc), M_DEVBUF, M_NOWAIT | M_ZERO)) == NULL) {
device_printf(dev, "cannot allocate softc\n");
return ENXIO;
return (ENXIO);
}
sc->lock = snd_mtxcreate(device_get_nameunit(dev), "sound softc");
@ -904,7 +974,7 @@ atiixp_pci_attach(device_t dev)
/*
* Default DMA segments per playback / recording channel
*/
sc->dma_segs = ATI_IXP_DMA_CHSEGS;
sc->dma_segs = ATI_IXP_DMA_CHSEGS_DEFAULT;
pci_set_powerstate(dev, PCI_POWERSTATE_D0);
pci_enable_busmaster(dev);
@ -922,12 +992,13 @@ atiixp_pci_attach(device_t dev)
sc->st = rman_get_bustag(sc->reg);
sc->sh = rman_get_bushandle(sc->reg);
sc->bufsz = pcm_getbuffersize(dev, 4096, ATI_IXP_DEFAULT_BUFSZ, 65536);
sc->bufsz = pcm_getbuffersize(dev, ATI_IXP_BUFSZ_MIN,
ATI_IXP_BUFSZ_DEFAULT, ATI_IXP_BUFSZ_MAX);
sc->irqid = 0;
sc->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &sc->irqid,
RF_ACTIVE | RF_SHAREABLE);
if (!sc->irq ||
if (!sc->irq ||
snd_setup_intr(dev, sc->irq, INTR_MPSAFE,
atiixp_intr, sc, &sc->ih)) {
device_printf(dev, "unable to map interrupt\n");
@ -962,7 +1033,7 @@ atiixp_pci_attach(device_t dev)
/*
* DMA tag for scatter-gather buffers and link pointers
*/
if (bus_dma_tag_create(/*parent*/NULL, /*alignment*/sc->bufsz, /*boundary*/0,
if (bus_dma_tag_create(/*parent*/NULL, /*alignment*/2, /*boundary*/0,
/*lowaddr*/BUS_SPACE_MAXADDR_32BIT,
/*highaddr*/BUS_SPACE_MAXADDR,
/*filter*/NULL, /*filterarg*/NULL,
@ -986,11 +1057,11 @@ atiixp_pci_attach(device_t dev)
goto bad;
}
if (bus_dmamem_alloc(sc->sgd_dmat, (void **)&sc->sgd_table,
if (bus_dmamem_alloc(sc->sgd_dmat, (void **)&sc->sgd_table,
BUS_DMA_NOWAIT, &sc->sgd_dmamap) == -1)
goto bad;
if (bus_dmamap_load(sc->sgd_dmat, sc->sgd_dmamap, sc->sgd_table,
if (bus_dmamap_load(sc->sgd_dmat, sc->sgd_dmamap, sc->sgd_table,
sc->dma_segs * ATI_IXP_NCHANS *
sizeof(struct atiixp_dma_op),
atiixp_dma_cb, sc, 0))
@ -1007,11 +1078,11 @@ atiixp_pci_attach(device_t dev)
atiixp_chip_post_init(sc);
}
return 0;
return (0);
bad:
atiixp_release_resource(sc);
return ENXIO;
return (ENXIO);
}
static int
@ -1025,14 +1096,15 @@ atiixp_pci_detach(device_t dev)
if (sc->codec != NULL) {
r = pcm_unregister(dev);
if (r)
return r;
return (r);
}
sc->codec = NULL;
atiixp_disable_interrupts(sc);
if (sc->st != 0 && sc->sh != 0)
atiixp_disable_interrupts(sc);
atiixp_release_resource(sc);
free(sc, M_DEVBUF);
}
return 0;
return (0);
}
static int
@ -1063,7 +1135,7 @@ atiixp_pci_suspend(device_t dev)
pci_set_powerstate(dev, PCI_POWERSTATE_D3);
atiixp_unlock(sc);
return 0;
return (0);
}
static int
@ -1082,7 +1154,7 @@ atiixp_pci_resume(device_t dev)
if (mixer_reinit(dev) == -1) {
device_printf(dev, "unable to reinitialize the mixer\n");
return ENXIO;
return (ENXIO);
}
/*
@ -1107,7 +1179,7 @@ atiixp_pci_resume(device_t dev)
atiixp_enable_interrupts(sc);
atiixp_unlock(sc);
return 0;
return (0);
}
static device_method_t atiixp_methods[] = {

View File

@ -46,9 +46,8 @@
#define ATI_IXP_DMA_CHSEGS_MIN 2
#define ATI_IXP_DMA_CHSEGS_MAX 256
#define ATI_IXP_DEFAULT_BUFSZ (1 << 13) /* 8192 */
#define ATI_VENDOR_ID 0x1002 /* ATI Technologies */
#define ATI_IXP_200_ID 0x4341
#define ATI_IXP_300_ID 0x4361
#define ATI_IXP_400_ID 0x4370