summaryrefslogtreecommitdiff
path: root/sys/dev/pci/azalia.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/dev/pci/azalia.c')
-rw-r--r--sys/dev/pci/azalia.c122
1 files changed, 113 insertions, 9 deletions
diff --git a/sys/dev/pci/azalia.c b/sys/dev/pci/azalia.c
index 8db07fd6b32..c3c543cea9f 100644
--- a/sys/dev/pci/azalia.c
+++ b/sys/dev/pci/azalia.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: azalia.c,v 1.24 2006/09/21 09:52:45 fgsch Exp $ */
+/* $OpenBSD: azalia.c,v 1.25 2007/05/02 17:01:22 deanna Exp $ */
/* $NetBSD: azalia.c,v 1.20 2006/05/07 08:31:44 kent Exp $ */
/*-
@@ -195,6 +195,11 @@ typedef struct azalia_t {
azalia_dma_t rirb_dma;
int rirb_size;
int rirb_rp;
+#define UNSOLQ_SIZE 256
+ rirb_entry_t *unsolq;
+ int unsolq_wp;
+ int unsolq_rp;
+ boolean_t unsolq_kick;
boolean_t ok64;
int nistreams, nostreams, nbstreams;
@@ -222,9 +227,11 @@ int azalia_init_corb(azalia_t *);
int azalia_delete_corb(azalia_t *);
int azalia_init_rirb(azalia_t *);
int azalia_delete_rirb(azalia_t *);
-int azalia_set_command(const azalia_t *, nid_t, int, uint32_t,
+int azalia_set_command(azalia_t *, nid_t, int, uint32_t,
uint32_t);
int azalia_get_response(azalia_t *, uint32_t *);
+void azalia_rirb_kick_unsol_events(azalia_t *);
+void azalia_rirb_intr(azalia_t *);
int azalia_alloc_dmamem(azalia_t *, size_t, size_t, azalia_dma_t *);
int azalia_free_dmamem(const azalia_t *, azalia_dma_t*);
@@ -475,7 +482,7 @@ azalia_intr(void *v)
azalia_t *az = v;
int ret = 0;
uint32_t intsts;
- uint8_t rirbsts;
+ uint8_t rirbsts, rirbctl;
intsts = AZ_READ_4(az, INTSTS);
if (intsts == 0)
@@ -488,10 +495,14 @@ azalia_intr(void *v)
ret += azalia_stream_intr(&az->rstream, intsts);
#endif
+ rirbctl = AZ_READ_1(az, RIRBCTL);
+ rirbsts = AZ_READ_1(az, RIRBSTS);
+
if (intsts & HDA_INTCTL_CIE) {
- rirbsts = AZ_READ_1(az, RIRBSTS);
- AZ_WRITE_1(az, RIRBSTS, rirbsts |
- HDA_RIRBSTS_RIRBOIS | HDA_RIRBSTS_RINTFL);
+ if (rirbctl & HDA_RIRBCTL_RINTCTL) {
+ if (rirbsts & HDA_RIRBSTS_RINTFL)
+ azalia_rirb_intr(az);
+ }
}
return (1);
@@ -547,6 +558,10 @@ azalia_attach(azalia_t *az)
return ETIMEDOUT;
}
+ /* enable unsolicited response */
+ gctl = AZ_READ_4(az, GCTL);
+ AZ_WRITE_4(az, GCTL, gctl | HDA_GCTL_UNSOL);
+
/* 4.3 Codec discovery */
DELAY(1000);
statests = AZ_READ_2(az, STATESTS);
@@ -770,6 +785,20 @@ azalia_init_rirb(azalia_t *az)
DPRINTF(("%s: RIRB allocation succeeded.\n", __func__));
+ /* setup the unsolicited response queue */
+ az->unsolq_rp = 0;
+ az->unsolq_wp = 0;
+ az->unsolq_kick = FALSE;
+ az->unsolq = malloc(sizeof(rirb_entry_t) * UNSOLQ_SIZE,
+ M_DEVBUF, M_NOWAIT);
+ if (az->unsolq == NULL) {
+ DPRINTF(("%s: can't allocate unsolicited response queue.\n",
+ XNAME(az)));
+ azalia_free_dmamem(az, &az->rirb_dma);
+ return ENOMEM;
+ }
+ bzero(az->unsolq, sizeof(rirb_entry_t) * UNSOLQ_SIZE);
+
/* reset the write pointer */
rirbwp = AZ_READ_2(az, RIRBWP);
AZ_WRITE_2(az, RIRBWP, rirbwp | HDA_RIRBWP_RIRBWPRST);
@@ -794,6 +823,10 @@ azalia_delete_rirb(azalia_t *az)
int i;
uint8_t rirbctl;
+ if (az->unsolq != NULL) {
+ free(az->unsolq, M_DEVBUF);
+ az->unsolq = NULL;
+ }
if (az->rirb_dma.addr == NULL)
return 0;
/* stop the RIRB */
@@ -810,13 +843,14 @@ azalia_delete_rirb(azalia_t *az)
}
int
-azalia_set_command(const azalia_t *az, int caddr, nid_t nid, uint32_t control,
+azalia_set_command(azalia_t *az, int caddr, nid_t nid, uint32_t control,
uint32_t param)
{
corb_entry_t *corb;
int wp;
uint32_t verb;
uint16_t corbwp;
+ uint8_t rirbctl;
#ifdef DIAGNOSTIC
if ((AZ_READ_1(az, CORBCTL) & HDA_CORBCTL_CORBRUN) == 0) {
@@ -831,6 +865,14 @@ azalia_set_command(const azalia_t *az, int caddr, nid_t nid, uint32_t control,
if (++wp >= az->corb_size)
wp = 0;
corb[wp] = verb;
+
+ /* disable RIRB interrupts */
+ rirbctl = AZ_READ_1(az, RIRBCTL);
+ if (rirbctl & HDA_RIRBCTL_RINTCTL) {
+ AZ_WRITE_1(az, RIRBCTL, rirbctl & ~HDA_RIRBCTL_RINTCTL);
+ azalia_rirb_intr(az);
+ }
+
AZ_WRITE_2(az, CORBWP, (corbwp & ~HDA_CORBWP_CORBWP) | wp);
#if 0
DPRINTF(("%s: caddr=%d nid=%d control=0x%x param=0x%x verb=0x%8.8x wp=%d\n",
@@ -845,6 +887,7 @@ azalia_get_response(azalia_t *az, uint32_t *result)
const rirb_entry_t *rirb;
int i;
uint16_t wp;
+ uint8_t rirbctl;
#ifdef DIAGNOSTIC
if ((AZ_READ_1(az, RIRBCTL) & HDA_RIRBCTL_RIRBDMAEN) == 0) {
@@ -866,14 +909,17 @@ azalia_get_response(azalia_t *az, uint32_t *result)
for (;;) {
if (++az->rirb_rp >= az->rirb_size)
az->rirb_rp = 0;
- if (rirb[az->rirb_rp].resp_ex & RIRB_UNSOLICITED_RESPONSE) {
- DPRINTF(("%s: unsolicited response\n", __func__));
+ if (rirb[az->rirb_rp].resp_ex & RIRB_RESP_UNSOL) {
+ az->unsolq[az->unsolq_wp].resp = rirb[az->rirb_rp].resp;
+ az->unsolq[az->unsolq_wp++].resp_ex = rirb[az->rirb_rp].resp_ex;
+ az->unsolq_wp %= UNSOLQ_SIZE;
} else
break;
}
if (result != NULL)
*result = rirb[az->rirb_rp].resp;
+ azalia_rirb_kick_unsol_events(az);
#if 0
for (i = 0; i < 16 /*az->rirb_size*/; i++) {
DPRINTF(("rirb[%d] 0x%8.8x:0x%8.8x ", i, rirb[i].resp, rirb[i].resp_ex));
@@ -881,9 +927,67 @@ azalia_get_response(azalia_t *az, uint32_t *result)
DPRINTF(("\n"));
}
#endif
+
+ /* re-enable RIRB interrupts */
+ rirbctl = AZ_READ_1(az, RIRBCTL);
+ AZ_WRITE_1(az, RIRBCTL, rirbctl | HDA_RIRBCTL_RINTCTL);
+
return 0;
}
+void
+azalia_rirb_kick_unsol_events(azalia_t *az)
+{
+ if (az->unsolq_kick)
+ return;
+ az->unsolq_kick = TRUE;
+ while (az->unsolq_rp != az->unsolq_wp) {
+ int i;
+ int tag;
+ codec_t *codec;
+ i = RIRB_RESP_CODEC(az->unsolq[az->unsolq_rp].resp_ex);
+ tag = RIRB_UNSOL_TAG(az->unsolq[az->unsolq_rp].resp);
+ codec = &az->codecs[i];
+ DPRINTF(("%s: codec#=%d tag=%d\n", __func__, i, tag));
+ az->unsolq_rp++;
+ az->unsolq_rp %= UNSOLQ_SIZE;
+ if (codec->unsol_event != NULL)
+ codec->unsol_event(codec, tag);
+ }
+ az->unsolq_kick = FALSE;
+}
+
+void
+azalia_rirb_intr(azalia_t *az)
+{
+ const rirb_entry_t *rirb;
+ uint16_t wp, rp;
+ uint8_t rirbsts;
+
+ rirbsts = AZ_READ_1(az, RIRBSTS);
+
+ wp = AZ_READ_2(az, RIRBWP) & HDA_RIRBWP_RIRBWP;
+ if (rp == wp)
+ return; /* interrupted but no data in RIRB */
+ rirb = (rirb_entry_t*)az->rirb_dma.addr;
+ while (az->rirb_rp != wp) {
+ if (++az->rirb_rp >= az->rirb_size)
+ az->rirb_rp = 0;
+ if (rirb[az->rirb_rp].resp_ex & RIRB_RESP_UNSOL) {
+ az->unsolq[az->unsolq_wp].resp = rirb[az->rirb_rp].resp;
+ az->unsolq[az->unsolq_wp++].resp_ex = rirb[az->rirb_rp].resp_ex;
+ az->unsolq_wp %= UNSOLQ_SIZE;
+ } else {
+ break;
+ }
+ }
+
+ azalia_rirb_kick_unsol_events(az);
+
+ AZ_WRITE_1(az, RIRBSTS,
+ rirbsts | HDA_RIRBSTS_RIRBOIS | HDA_RIRBSTS_RINTFL);
+}
+
int
azalia_alloc_dmamem(azalia_t *az, size_t size, size_t align, azalia_dma_t *d)
{