summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--sys/dev/pci/azalia.c81
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);
}