diff --git a/sys/dev/sound/chip.h b/sys/dev/sound/chip.h index 82704ed03b0..012910c978d 100644 --- a/sys/dev/sound/chip.h +++ b/sys/dev/sound/chip.h @@ -38,9 +38,10 @@ enum { /* * This is the device information struct, used by - * sndcard device to pass the device function code - * to the driver. + * a bridge device to pass the device function code + * to the children. */ struct sndcard_func { - int func; + int func; /* The function code. */ + void *varinfo; /* Bridge-specific information. */ }; diff --git a/sys/dev/sound/pci/csa.c b/sys/dev/sound/pci/csa.c index 8ab596eed84..2a238698721 100644 --- a/sys/dev/sound/pci/csa.c +++ b/sys/dev/sound/pci/csa.c @@ -1,4 +1,4 @@ -/*- +/* * Copyright (c) 1999 Seigo Tanimura * All rights reserved. * @@ -68,6 +68,8 @@ struct csa_softc { void *midiintr_arg; /* midi intr arg */ #endif /* notyet */ void *ih; /* cookie */ + + struct csa_bridgeinfo binfo; /* The state of this bridge. */ }; typedef struct csa_softc *sc_p; @@ -80,8 +82,13 @@ static struct resource *csa_alloc_resource(device_t bus, device_t child, int typ u_long start, u_long end, u_long count, u_int flags); static int csa_release_resource(device_t bus, device_t child, int type, int rid, struct resource *r); +static int csa_setup_intr(device_t bus, device_t child, + struct resource *irq, int flags, + driver_intr_t *intr, void *arg, void **cookiep); +static int csa_teardown_intr(device_t bus, device_t child, + struct resource *irq, void *cookie); +static driver_intr_t csa_intr; static int csa_initialize(sc_p scp); -static void csa_clearserialfifos(csa_res *resp); static void csa_resetdsp(csa_res *resp); static int csa_downloadimage(csa_res *resp); static int csa_transferimage(csa_res *resp, u_long *src, u_long dest, u_long len); @@ -92,9 +99,7 @@ static devclass_t csa_devclass; static int csa_probe(device_t dev) { - device_t child; char *s; - struct sndcard_func *func; s = NULL; switch (pci_get_devid(dev)) { @@ -114,27 +119,6 @@ csa_probe(device_t dev) if (s != NULL) { device_set_desc(dev, s); - - /* PCM Audio */ - func = malloc(sizeof(struct sndcard_func), M_DEVBUF, M_NOWAIT); - if (func == NULL) - return (ENOMEM); - bzero(func, sizeof(*func)); - func->func = SCF_PCM; - child = device_add_child(dev, "pcm", -1); - device_set_ivars(child, func); - -#if notyet - /* Midi Interface */ - func = malloc(sizeof(struct sndcard_func), M_DEVBUF, M_NOWAIT); - if (func == NULL) - return (ENOMEM); - bzero(func, sizeof(*func)); - func->func = SCF_MIDI; - child = device_add_child(dev, "midi", -1); - device_set_ivars(child, func); -#endif /* notyet */ - return (0); } @@ -147,6 +131,7 @@ csa_attach(device_t dev) u_int32_t stcmd; sc_p scp; csa_res *resp; + struct sndcard_func *func; scp = device_get_softc(dev); @@ -160,10 +145,6 @@ csa_attach(device_t dev) stcmd |= (PCIM_CMD_MEMEN | PCIM_CMD_BUSMASTEREN); pci_write_config(dev, PCIR_COMMAND, 4, stcmd); } - stcmd = pci_read_config(dev, PCIR_LATTIMER, 4); - if (stcmd < 32) - stcmd = 32; - pci_write_config(dev, PCIR_LATTIMER, 4, stcmd); /* Allocate the resources. */ resp = &scp->res; @@ -185,6 +166,16 @@ csa_attach(device_t dev) return (ENXIO); } + /* Enable interrupt. */ + if (bus_setup_intr(dev, resp->irq, INTR_TYPE_TTY, csa_intr, scp, &scp->ih)) { + bus_release_resource(dev, SYS_RES_MEMORY, resp->io_rid, resp->io); + bus_release_resource(dev, SYS_RES_MEMORY, resp->mem_rid, resp->mem); + bus_release_resource(dev, SYS_RES_IRQ, resp->irq_rid, resp->irq); + return (ENXIO); + } + if ((csa_readio(resp, BA0_HISR) & HISR_INTENA) == 0) + csa_writeio(resp, BA0_HICR, HICR_IEV | HICR_CHGM); + /* Initialize the chip. */ if (csa_initialize(scp)) { bus_release_resource(dev, SYS_RES_MEMORY, resp->io_rid, resp->io); @@ -204,6 +195,30 @@ csa_attach(device_t dev) return (ENXIO); } + /* Attach the children. */ + + /* PCM Audio */ + func = malloc(sizeof(struct sndcard_func), M_DEVBUF, M_NOWAIT); + if (func == NULL) + return (ENOMEM); + bzero(func, sizeof(*func)); + func->varinfo = &scp->binfo; + func->func = SCF_PCM; + scp->pcm = device_add_child(dev, "pcm", -1); + device_set_ivars(scp->pcm, func); + +#if notyet + /* Midi Interface */ + func = malloc(sizeof(struct sndcard_func), M_DEVBUF, M_NOWAIT); + if (func == NULL) + return (ENOMEM); + bzero(func, sizeof(*func)); + func->varinfo = &scp->binfo; + func->func = SCF_MIDI; + scp->midi = device_add_child(dev, "midi", -1); + device_set_ivars(scp->midi, func); +#endif /* notyet */ + bus_generic_attach(dev); return (0); @@ -252,6 +267,133 @@ csa_release_resource(device_t bus, device_t child, int type, int rid, return (0); } +/* + * The following three functions deal with interrupt handling. + * An interrupt is primarily handled by the bridge driver. + * The bridge driver then determines the child devices to pass + * the interrupt. Certain information of the device can be read + * only once(eg the value of HISR). The bridge driver is responsible + * to pass such the information to the children. + */ + +static int +csa_setup_intr(device_t bus, device_t child, + struct resource *irq, int flags, + driver_intr_t *intr, void *arg, void **cookiep) +{ + sc_p scp; + csa_res *resp; + struct sndcard_func *func; + + scp = device_get_softc(bus); + resp = &scp->res; + + /* + * Look at the function code of the child to determine + * the appropriate hander for it. + */ + func = device_get_ivars(child); + if (func == NULL || irq != resp->irq) + return (EINVAL); + + switch (func->func) { + case SCF_PCM: + scp->pcmintr = intr; + scp->pcmintr_arg = arg; + break; + +#if notyet + case SCF_MIDI: + scp->midiintr = intr; + scp->midiintr_arg = arg; + break; +#endif /* notyet */ + + default: + return (EINVAL); + } + *cookiep = scp; + if ((csa_readio(resp, BA0_HISR) & HISR_INTENA) == 0) + csa_writeio(resp, BA0_HICR, HICR_IEV | HICR_CHGM); + + return (0); +} + +static int +csa_teardown_intr(device_t bus, device_t child, + struct resource *irq, void *cookie) +{ + sc_p scp; + csa_res *resp; + struct sndcard_func *func; + + scp = device_get_softc(bus); + resp = &scp->res; + + /* + * Look at the function code of the child to determine + * the appropriate hander for it. + */ + func = device_get_ivars(child); + if (func == NULL || irq != resp->irq || cookie != scp) + return (EINVAL); + + switch (func->func) { + case SCF_PCM: + scp->pcmintr = NULL; + scp->pcmintr_arg = NULL; + break; + +#if notyet + case SCF_MIDI: + scp->midiintr = NULL; + scp->midiintr_arg = NULL; + break; +#endif /* notyet */ + + default: + return (EINVAL); + } + + return (0); +} + +/* The interrupt handler */ +static void +csa_intr(void *arg) +{ + sc_p scp = arg; + csa_res *resp; + u_int32_t hisr; + + resp = &scp->res; + + /* Is this interrupt for us? */ + hisr = csa_readio(resp, BA0_HISR); + if ((hisr & ~HISR_INTENA) == 0) { + /* Throw an eoi. */ + csa_writeio(resp, BA0_HICR, HICR_IEV | HICR_CHGM); + return; + } + + /* + * Pass the value of HISR via struct csa_bridgeinfo. + * The children get access through their ivars. + */ + scp->binfo.hisr = hisr; + + /* Invoke the handlers of the children. */ + if ((hisr & (HISR_VC0 | HISR_VC1)) != 0 && scp->pcmintr != NULL) + scp->pcmintr(scp->pcmintr_arg); +#if notyet + if ((hisr & HISR_MIDI) != 0 && scp->midiintr != NULL) + scp->midiintr(scp->midiintr_arg); +#endif /* notyet */ + + /* Throw an eoi. */ + csa_writeio(resp, BA0_HICR, HICR_IEV | HICR_CHGM); +} + static int csa_initialize(sc_p scp) { @@ -448,7 +590,7 @@ csa_initialize(sc_p scp) return (0); } -static void +void csa_clearserialfifos(csa_res *resp) { int i, j, pwr; @@ -776,8 +918,8 @@ static device_method_t csa_methods[] = { DEVMETHOD(bus_release_resource, csa_release_resource), DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), - DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), - DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), + DEVMETHOD(bus_setup_intr, csa_setup_intr), + DEVMETHOD(bus_teardown_intr, csa_teardown_intr), { 0, 0 } }; diff --git a/sys/dev/sound/pci/csapcm.c b/sys/dev/sound/pci/csapcm.c index b67f2c92135..fff8bb24158 100644 --- a/sys/dev/sound/pci/csapcm.c +++ b/sys/dev/sound/pci/csapcm.c @@ -1,4 +1,4 @@ -/*- +/* * Copyright (c) 1999 Seigo Tanimura * All rights reserved. * @@ -55,6 +55,7 @@ struct csa_info { csa_res res; /* resource */ void *ih; /* Interrupt cookie */ bus_dma_tag_t parent_dmat; /* DMA tag */ + struct csa_bridgeinfo *binfo; /* The state of the parent. */ /* Contents of board's registers */ u_long pfie; @@ -438,6 +439,9 @@ csa_stopplaydma(struct csa_info *csa) csa->pctl = ul & 0xffff0000; csa_writemem(resp, BA1_PCTL, ul & 0x0000ffff); csa_writemem(resp, BA1_PVOL, 0xffffffff); + + /* Clear the serial fifos. */ + csa_clearserialfifos(resp); } static void @@ -619,27 +623,11 @@ static void csa_intr (void *p) { struct csa_info *csa = p; - csa_res *resp; - u_int hisr; - resp = &csa->res; - - /* Is this interrupt for us? */ - /* XXX The parent device should handle this. */ - hisr = csa_readio(resp, BA0_HISR); - if ((hisr & ~HISR_INTENA) == 0) { - /* Throw an eoi. */ - csa_writeio(resp, BA0_HICR, HICR_IEV | HICR_CHGM); - return; - } - - if ((hisr & HISR_VC0) != 0) + if ((csa->binfo->hisr & HISR_VC0) != 0) chn_intr(csa->pch.channel); - if ((hisr & HISR_VC1) != 0) + if ((csa->binfo->hisr & HISR_VC1) != 0) chn_intr(csa->rch.channel); - - /* Throw an eoi. */ - csa_writeio(resp, BA0_HICR, HICR_IEV | HICR_CHGM); } /* -------------------------------------------------------------------- */ @@ -756,6 +744,7 @@ pcmcsa_attach(device_t dev) int unit; char status[SND_STATUSLEN]; struct ac97_info *codec; + struct sndcard_func *func; devinfo = device_get_softc(dev); csa = malloc(sizeof(*csa), M_DEVBUF, M_NOWAIT); @@ -763,6 +752,8 @@ pcmcsa_attach(device_t dev) return (ENOMEM); bzero(csa, sizeof(*csa)); unit = device_get_unit(dev); + func = device_get_ivars(dev); + csa->binfo = func->varinfo; /* Allocate the resources. */ resp = &csa->res; @@ -790,8 +781,6 @@ pcmcsa_attach(device_t dev) csa_releaseres(csa, dev); return (ENXIO); } - if ((csa_readio(resp, BA0_HISR) & HISR_INTENA) == 0) - csa_writeio(resp, BA0_HICR, HICR_IEV | HICR_CHGM); csa_writemem(resp, BA1_PFIE, csa_readmem(resp, BA1_PFIE) & ~0x0000f03f); csa_writemem(resp, BA1_CIE, (csa_readmem(resp, BA1_CIE) & ~0x0000003f) | 0x00000001); diff --git a/sys/dev/sound/pci/csavar.h b/sys/dev/sound/pci/csavar.h index 0f3d4f9ef0f..fef2ce84d4b 100644 --- a/sys/dev/sound/pci/csavar.h +++ b/sys/dev/sound/pci/csavar.h @@ -40,6 +40,13 @@ struct csa_res { }; typedef struct csa_res csa_res; +/* State of the bridge. */ +struct csa_bridgeinfo { + u_int32_t hisr; /* The value of HISR on this interrupt. */ +}; + +void csa_clearserialfifos(csa_res *resp); + /* Common functions for csa. */ int csa_readcodec(csa_res *resp, u_long offset, u_int32_t *data); int csa_writecodec(csa_res *resp, u_long offset, u_int32_t data);