diff options
author | Jacob Meuser <jakemsr@cvs.openbsd.org> | 2010-08-06 00:08:50 +0000 |
---|---|---|
committer | Jacob Meuser <jakemsr@cvs.openbsd.org> | 2010-08-06 00:08:50 +0000 |
commit | 19bfdb820881366d81776f90df258cad085c96bd (patch) | |
tree | c983098c234073777bb97438e4445aeccccbfe04 /sys/dev/pci | |
parent | 59909a9c54adeb4233d799044d42c2db4565c67d (diff) |
attempt to deal with situations where the hardware has processed more
than one block of data since the interrupt handler was last run.
when azalia detects these situations, it will print a line to the
dmesg buffer like:
stream 1: swpos XXXX hwpos XXXX, adding intr
getting these messages in itself is not bad, but if audio breaks, I
need to see these messages. this will be changed to a DPRINTF once
I get more feedback.
ok deraadt
Diffstat (limited to 'sys/dev/pci')
-rw-r--r-- | sys/dev/pci/azalia.c | 81 |
1 files changed, 60 insertions, 21 deletions
diff --git a/sys/dev/pci/azalia.c b/sys/dev/pci/azalia.c index a2ff8cc0ed1..3d9f6130394 100644 --- a/sys/dev/pci/azalia.c +++ b/sys/dev/pci/azalia.c @@ -1,4 +1,4 @@ -/* $OpenBSD: azalia.c,v 1.175 2010/07/29 01:39:03 jakemsr Exp $ */ +/* $OpenBSD: azalia.c,v 1.176 2010/08/06 00:08:49 jakemsr Exp $ */ /* $NetBSD: azalia.c,v 1.20 2006/05/07 08:31:44 kent Exp $ */ /*- @@ -127,7 +127,12 @@ typedef struct { int bufsize; uint16_t fmt; int blk; - int lpib; + int nblks; /* # of blocks in the buffer */ + u_long swpos; /* position in the audio(4) layer */ + u_int last_hwpos; /* last known lpib */ + u_long hw_base; /* this + lpib = overall position */ + u_int lpib; /* link position in buffer */ + u_int pos_offs; /* hardware fifo space */ } stream_t; #define STR_READ_1(s, r) \ bus_space_read_1((s)->az->iot, (s)->az->ioh, (s)->regbase + HDA_SD_##r) @@ -251,7 +256,7 @@ int azalia_stream_delete(stream_t *, azalia_t *); int azalia_stream_reset(stream_t *); int azalia_stream_start(stream_t *); int azalia_stream_halt(stream_t *); -int azalia_stream_intr(stream_t *, uint32_t); +int azalia_stream_intr(stream_t *); int azalia_open(void *, int); void azalia_close(void *); @@ -615,9 +620,7 @@ int azalia_intr(void *v) { azalia_t *az = v; - int ret = 0; uint32_t intsts; - uint8_t rirbsts, rirbctl; intsts = AZ_READ_4(az, INTSTS); if (intsts == 0) @@ -625,18 +628,16 @@ azalia_intr(void *v) AZ_WRITE_4(az, INTSTS, intsts); - ret += azalia_stream_intr(&az->pstream, intsts); - ret += azalia_stream_intr(&az->rstream, intsts); + if (intsts & az->pstream.intr_bit) + azalia_stream_intr(&az->pstream); - rirbctl = AZ_READ_1(az, RIRBCTL); - rirbsts = AZ_READ_1(az, RIRBSTS); + if (intsts & az->rstream.intr_bit) + azalia_stream_intr(&az->rstream); - if (intsts & HDA_INTSTS_CIS) { - if (rirbctl & HDA_RIRBCTL_RINTCTL) { - if (rirbsts & HDA_RIRBSTS_RINTFL) - azalia_rirb_intr(az); - } - } + if ((intsts & HDA_INTSTS_CIS) && + (AZ_READ_1(az, RIRBCTL) & HDA_RIRBCTL_RINTCTL) && + (AZ_READ_1(az, RIRBSTS) & HDA_RIRBSTS_RINTFL)) + azalia_rirb_intr(az); return (1); } @@ -3627,7 +3628,8 @@ azalia_widget_print_pin(const widget_t *this) {} * ================================================================ */ int -azalia_stream_init(stream_t *this, azalia_t *az, int regindex, int strnum, int dir) +azalia_stream_init(stream_t *this, azalia_t *az, int regindex, int strnum, + int dir) { int err; @@ -3637,6 +3639,7 @@ azalia_stream_init(stream_t *this, azalia_t *az, int regindex, int strnum, int d this->number = strnum; this->dir = dir; this->active = 0; + this->pos_offs = STR_READ_2(this, FIFOS) & 0xff; /* setup BDL buffers */ err = azalia_alloc_dmamem(az, sizeof(bdlist_entry_t) * HDA_BDL_MAX, @@ -3718,6 +3721,9 @@ azalia_stream_reset(stream_t *this) skip)); for (i = 0; i < skip; i++) this->intr(this->intr_arg); + this->swpos = 0; + this->last_hwpos = 0; + this->hw_base = 0; } this->active = 0; this->lpib = 0; @@ -3758,6 +3764,7 @@ azalia_stream_start(stream_t *this) break; } } + this->nblks = index; DPRINTFN(1, ("%s: size=%d fmt=0x%4.4x index=%d\n", __func__, this->bufsize, this->fmt, index)); @@ -3808,13 +3815,11 @@ azalia_stream_halt(stream_t *this) #define HDA_SD_STS_BITS "\20\3BCIS\4FIFOE\5DESE\6FIFORDY" int -azalia_stream_intr(stream_t *this, uint32_t intsts) +azalia_stream_intr(stream_t *this) { + u_long hwpos, swpos; u_int8_t sts; - if ((intsts & this->intr_bit) == 0) - return (0); - sts = STR_READ_1(this, STS); STR_WRITE_1(this, STS, sts | HDA_SD_STS_DESE | HDA_SD_STS_FIFOE | HDA_SD_STS_BCIS); @@ -3822,8 +3827,34 @@ azalia_stream_intr(stream_t *this, uint32_t intsts) if (sts & (HDA_SD_STS_DESE | HDA_SD_STS_FIFOE)) printf("%s: stream %d: sts=%b\n", XNAME(this->az), this->number, sts, HDA_SD_STS_BITS); - if (sts & HDA_SD_STS_BCIS) + + if (sts & HDA_SD_STS_BCIS) { + hwpos = STR_READ_4(this, LPIB) + this->pos_offs; + if (hwpos < this->last_hwpos) + this->hw_base += this->blk * this->nblks; + this->last_hwpos = hwpos; + hwpos += this->hw_base; + + /* + * We got the interrupt, so we should advance our count. + * But we might *not* advance the count if software is + * ahead. + */ + swpos = this->swpos + this->blk; + + if (hwpos >= swpos + this->blk) { + printf("stream %d: swpos %lu hwpos %lu, adding intr\n", + this->number, swpos, hwpos); + this->intr(this->intr_arg); + this->swpos += this->blk; + } else if (swpos >= hwpos + this->blk) { + printf("stream %d: swpos %lu hwpos %lu, ignoring intr\n", + this->number, swpos, hwpos); + return (1); + } this->intr(this->intr_arg); + this->swpos += this->blk; + } return (1); } @@ -4239,6 +4270,10 @@ azalia_trigger_output(void *v, void *start, void *end, int blk, az->pstream.intr = intr; az->pstream.intr_arg = arg; + az->pstream.swpos = 0; + az->pstream.last_hwpos = 0; + az->pstream.hw_base = 0; + return azalia_stream_start(&az->pstream); } @@ -4271,6 +4306,10 @@ azalia_trigger_input(void *v, void *start, void *end, int blk, az->rstream.intr = intr; az->rstream.intr_arg = arg; + az->rstream.swpos = 0; + az->rstream.last_hwpos = 0; + az->rstream.hw_base = 0; + return azalia_stream_start(&az->rstream); } |