This diff simplifies the code that reads the DMA pointer and calls the audio(4) driver accordingly.
- use simpler (and less) data structures, which makes the code shorter. - if multiple interrupts are missed, call audio(4) as many times as we missed interrupts (unless the pointer wrapped, which this is undetectable, but upper layer handles it) - fetch the link fifos size in the interrupt handler, as, according to the spec it may change dynamically (i haven't seen such a device yet, but this costs nothing). - Certain devices expose the last offset of the link fifo size instead of the fifo size itself, in this case audio(4) stays one block off (i.e. we don't recover completely from missed interrupts). Fix this by incrementing the reported link fifo size if it's odd. OK? Index: azalia.c =================================================================== RCS file: /cvs/src/sys/dev/pci/azalia.c,v retrieving revision 1.221 diff -u -p -u -p -r1.221 azalia.c --- azalia.c 11 May 2015 06:46:21 -0000 1.221 +++ azalia.c 28 Jul 2015 07:36:45 -0000 @@ -123,11 +123,7 @@ typedef struct { int bufsize; uint16_t fmt; int blk; - 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 pos_offs; /* hardware fifo space */ + unsigned int swpos; /* position in the audio(4) layer */ } stream_t; #define STR_READ_1(s, r) \ bus_space_read_1((s)->az->iot, (s)->az->ioh, (s)->regbase + HDA_SD_##r) @@ -3672,7 +3668,6 @@ azalia_stream_init(stream_t *this, azali this->intr_bit = 1 << regindex; this->number = strnum; this->dir = dir; - this->pos_offs = STR_READ_2(this, FIFOS) & 0xff; /* setup BDL buffers */ err = azalia_alloc_dmamem(az, sizeof(bdlist_entry_t) * HDA_BDL_MAX, @@ -3764,7 +3759,6 @@ 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)); @@ -3813,7 +3807,7 @@ azalia_stream_halt(stream_t *this) int azalia_stream_intr(stream_t *this) { - u_long hwpos, swpos; + unsigned int lpib, fifos, hwpos, cnt; u_int8_t sts; sts = STR_READ_1(this, STS); @@ -3825,31 +3819,30 @@ azalia_stream_intr(stream_t *this) this->number, sts, HDA_SD_STS_BITS)); 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) { - DPRINTF(("%s: stream %d: swpos %lu hwpos %lu, adding intr\n", - __func__, this->number, swpos, hwpos)); + lpib = STR_READ_4(this, LPIB); + fifos = STR_READ_2(this, FIFOS); + if (fifos & 1) + fifos++; + hwpos = lpib; + if (this->dir == AUMODE_PLAY) + hwpos += fifos + 1; + if (hwpos >= this->bufsize) + hwpos -= this->bufsize; + DPRINTFN(2, ("%s: stream %d, pos = %d -> %d, " + "lpib = %u, fifos = %u\n", __func__, + this->number, this->swpos, hwpos, lpib, fifos)); + cnt = 0; + while (hwpos - this->swpos >= this->blk) { this->intr(this->intr_arg); this->swpos += this->blk; - } else if (swpos >= hwpos + this->blk) { - DPRINTF(("%s: stream %d: swpos %lu hwpos %lu, ignoring intr\n", - __func__, this->number, swpos, hwpos)); - return (1); + if (this->swpos == this->bufsize) + this->swpos = 0; + cnt++; + } + if (cnt != 1) { + DPRINTF(("%s: stream %d: hwpos %u, %u intrs\n", + __func__, this->number, this->swpos, cnt)); } - this->intr(this->intr_arg); - this->swpos += this->blk; } return (1); } @@ -4233,10 +4226,7 @@ azalia_trigger_output(void *v, void *sta az->pstream.fmt = fmt; 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); } @@ -4269,10 +4259,7 @@ azalia_trigger_input(void *v, void *star az->rstream.fmt = fmt; 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); }