diff options
Diffstat (limited to 'sys/dev/pci/azalia.c')
-rw-r--r-- | sys/dev/pci/azalia.c | 122 |
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) { |