diff options
author | tanimura <tanimura@FreeBSD.org> | 1999-11-22 06:07:49 +0000 |
---|---|---|
committer | tanimura <tanimura@FreeBSD.org> | 1999-11-22 06:07:49 +0000 |
commit | 05e9b4af3eeff6a4e22e35b13bbc16d56c3dce70 (patch) | |
tree | faaea41e349959e04b3e8cfe96726269290a60c8 /sys/dev/sound/isa | |
parent | 913c1ce7346351f8ee703d9125f82353999c9d0a (diff) | |
download | FreeBSD-src-05e9b4af3eeff6a4e22e35b13bbc16d56c3dce70.zip FreeBSD-src-05e9b4af3eeff6a4e22e35b13bbc16d56c3dce70.tar.gz |
- Introduce the bridge drivers for Sound Blaser, GUS and Crystal
Semiconductor CS461x/428x.
- Add support for GUS and CS461x/428x pcm.
Bridges reviewed by: dfr, cg
GUS non-PnP support submitted by: Ville-Pertti Keinonen <will@iki.fi>
GUS PnP support tested by: Michiru Saito <mich@mtci.ne.jp>
Diffstat (limited to 'sys/dev/sound/isa')
-rw-r--r-- | sys/dev/sound/isa/gusc.c | 647 | ||||
-rw-r--r-- | sys/dev/sound/isa/mss.c | 191 | ||||
-rw-r--r-- | sys/dev/sound/isa/sb.c | 85 | ||||
-rw-r--r-- | sys/dev/sound/isa/sb16.c | 85 | ||||
-rw-r--r-- | sys/dev/sound/isa/sb8.c | 85 | ||||
-rw-r--r-- | sys/dev/sound/isa/sbc.c | 393 |
6 files changed, 1422 insertions, 64 deletions
diff --git a/sys/dev/sound/isa/gusc.c b/sys/dev/sound/isa/gusc.c new file mode 100644 index 0000000..9c531b2 --- /dev/null +++ b/sys/dev/sound/isa/gusc.c @@ -0,0 +1,647 @@ +/*- + * Copyright (c) 1999 Seigo Tanimura + * Copyright (c) 1999 Ville-Pertti Keinonen + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#include "gusc.h" +#include "isa.h" +#include "pnp.h" + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/bus.h> +#include <sys/malloc.h> +#include <sys/module.h> +#include <machine/resource.h> +#include <machine/bus.h> +#include <machine/clock.h> +#include <sys/rman.h> +#include <sys/soundcard.h> +#include <dev/sound/pcm/sound.h> +#include <dev/sound/chip.h> +#include "bus_if.h" + + +#if NISA > 0 +#include <isa/isavar.h> +#include <isa/isa_common.h> +#ifdef __alpha__ /* XXX workaround a stupid warning */ +#include <alpha/isa/isavar.h> +#endif +#endif /* NISA > 0 */ + +#if NGUSC > 0 + +#define LOGICALID_PCM 0x0000561e +#define LOGICALID_OPL 0x0300561e +#define LOGICALID_MIDI 0x0400561e + +/* Interrupt handler. */ +struct gusc_ihandler { + void (*intr)(void *); + void *arg; +}; + +/* Here is the parameter structure per a device. */ +struct gusc_softc { + device_t dev; /* device */ + int io_rid[3]; /* io port rids */ + struct resource *io[3]; /* io port resources */ + int io_alloced[3]; /* io port alloc flag */ + int irq_rid; /* irq rids */ + struct resource *irq; /* irq resources */ + int irq_alloced; /* irq alloc flag */ + int drq_rid[2]; /* drq rids */ + struct resource *drq[2]; /* drq resources */ + int drq_alloced[2]; /* drq alloc flag */ + + /* Interrupts are shared (XXX non-PnP only?) */ + struct gusc_ihandler midi_intr; + struct gusc_ihandler pcm_intr; +}; + +typedef struct gusc_softc *sc_p; + +#if NISA > 0 +static int gusc_probe(device_t dev); +static int gusc_attach(device_t dev); +static int gusisa_probe(device_t dev); +static void gusc_intr(void *); +#endif /* NISA > 0 */ +static struct resource *gusc_alloc_resource(device_t bus, device_t child, int type, int *rid, + u_long start, u_long end, u_long count, u_int flags); +static int gusc_release_resource(device_t bus, device_t child, int type, int rid, + struct resource *r); + +#if notyet +static device_t find_masterdev(sc_p scp); +#endif /* notyet */ +static int alloc_resource(sc_p scp); +static int release_resource(sc_p scp); + +static devclass_t gusc_devclass; + +#if NISA > 0 + +static int +gusc_probe(device_t dev) +{ + u_int32_t vend_id, logical_id; + char *s; + struct sndcard_func *func; + + vend_id = isa_get_vendorid(dev); + if (vend_id == 0) + return gusisa_probe(dev); + +#if NPNP > 0 + logical_id = isa_get_logicalid(dev); + s = NULL; + + if (vend_id == 0x0100561e) { /* Gravis */ + switch (logical_id) { + case LOGICALID_PCM: + s = "Gravis UltraSound Plug & Play PCM"; + func = malloc(sizeof(struct sndcard_func), M_DEVBUF, M_NOWAIT); + if (func == NULL) + return (ENOMEM); + bzero(func, sizeof(*func)); + func->func = SCF_PCM; + device_add_child(dev, "pcm", -1, func); + break; +#if notyet + case LOGICALID_OPL: + s = "Gravis UltraSound Plug & Play OPL"; + func = malloc(sizeof(struct sndcard_func), M_DEVBUF, M_NOWAIT); + if (func == NULL) + return (ENOMEM); + bzero(func, sizeof(*func)); + func->func = SCF_SYNTH; + device_add_child(dev, "midi", -1, func); + break; + case LOGICALID_MIDI: + s = "Gravis UltraSound Plug & Play MIDI"; + func = malloc(sizeof(struct sndcard_func), M_DEVBUF, M_NOWAIT); + if (func == NULL) + return (ENOMEM); + bzero(func, sizeof(*func)); + func->func = SCF_MIDI; + device_add_child(dev, "midi", -1, func); + break; +#endif /* notyet */ + } + } + + if (s != NULL) { + device_set_desc(dev, s); + return (0); + } +#endif /* NPNP > 0 */ + + return (ENXIO); +} + +static void +port_wr(struct resource *r, int i, unsigned char v) +{ + bus_space_write_1(rman_get_bustag(r), rman_get_bushandle(r), i, v); +} + +static int +port_rd(struct resource *r, int i) +{ + return bus_space_read_1(rman_get_bustag(r), rman_get_bushandle(r), i); +} + +/* + * Probe for an old (non-PnP) GUS card on the ISA bus. + */ + +static int +gusisa_probe(device_t dev) +{ + struct resource *res, *res2; + int base, rid, rid2, s, flags; + unsigned char val; + + base = isa_get_port(dev); + flags = device_get_flags(dev); + rid = 1; + res = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, base + 0x100, + base + 0x107, 8, RF_ACTIVE); + + if (res == NULL) + return ENXIO; + + res2 = NULL; + + /* + * Check for the presence of some GUS card. Reset the card, + * then see if we can access the memory on it. + */ + + port_wr(res, 3, 0x4c); + port_wr(res, 5, 0); + DELAY(30 * 1000); + + port_wr(res, 3, 0x4c); + port_wr(res, 5, 1); + DELAY(30 * 1000); + + s = splhigh(); + + /* Write to DRAM. */ + + port_wr(res, 3, 0x43); /* Register select */ + port_wr(res, 4, 0); /* Low addr */ + port_wr(res, 5, 0); /* Med addr */ + + port_wr(res, 3, 0x44); /* Register select */ + port_wr(res, 4, 0); /* High addr */ + port_wr(res, 7, 0x55); /* DRAM */ + + /* Read from DRAM. */ + + port_wr(res, 3, 0x43); /* Register select */ + port_wr(res, 4, 0); /* Low addr */ + port_wr(res, 5, 0); /* Med addr */ + + port_wr(res, 3, 0x44); /* Register select */ + port_wr(res, 4, 0); /* High addr */ + val = port_rd(res, 7); /* DRAM */ + + splx(s); + + if (val != 0x55) + goto fail; + + rid2 = 0; + res2 = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid2, base, base, 1, + RF_ACTIVE); + + if (res2 == NULL) + goto fail; + + s = splhigh(); + port_wr(res2, 0x0f, 0x20); + val = port_rd(res2, 0x0f); + splx(s); + + if (val == 0xff || (val & 0x06) == 0) + val = 0; + else { + val = port_rd(res2, 0x506); /* XXX Out of range. */ + if (val == 0xff) + val = 0; + } + + bus_release_resource(dev, SYS_RES_IOPORT, rid2, res2); + bus_release_resource(dev, SYS_RES_IOPORT, rid, res); + + if (val >= 10) { + struct sndcard_func *func; + + /* Looks like a GUS MAX. Set the rest of the resources. */ + + bus_set_resource(dev, SYS_RES_IOPORT, 2, base + 0x10c, 8); + + if (flags & DV_F_DUAL_DMA) + bus_set_resource(dev, SYS_RES_DRQ, 1, + flags & DV_F_DRQ_MASK, 1); + +#if notyet + /* We can support the CS4231 and MIDI devices. */ + + func = malloc(sizeof(struct sndcard_func), M_DEVBUF, M_NOWAIT); + if (func == NULL) + return ENOMEM; + bzero(func, sizeof *func); + func->func = SCF_MIDI; + device_add_child(dev, "midi", -1, func); +#endif /* notyet */ + + func = malloc(sizeof(struct sndcard_func), M_DEVBUF, M_NOWAIT); + if (func == NULL) + printf("xxx: gus pcm not attached, out of memory\n"); + else { + bzero(func, sizeof *func); + func->func = SCF_PCM; + device_add_child(dev, "pcm", -1, func); + } + device_set_desc(dev, "Gravis UltraSound MAX"); + return 0; + } else { + + /* + * TODO: Support even older GUS cards. MIDI should work on + * all models. + */ + return ENXIO; + } + +fail: + bus_release_resource(dev, SYS_RES_IOPORT, rid, res); + return ENXIO; +} + +static int +gusc_attach(device_t dev) +{ + sc_p scp; + int unit; + void *ih; + + scp = device_get_softc(dev); + unit = device_get_unit(dev); + + bzero(scp, sizeof(*scp)); + + scp->dev = dev; + if (alloc_resource(scp)) { + release_resource(scp); + return (ENXIO); + } + + bus_setup_intr(dev, scp->irq, INTR_TYPE_TTY, gusc_intr, scp, &ih); + bus_generic_attach(dev); + + return (0); +} + +/* + * Handle interrupts on GUS devices until there aren't any left. + */ +static void +gusc_intr(void *arg) +{ + sc_p scp = (sc_p)arg; + int did_something; + + do { + did_something = 0; + if (scp->pcm_intr.intr != NULL && + (port_rd(scp->io[2], 2) & 1)) { + (*scp->pcm_intr.intr)(scp->pcm_intr.arg); + did_something = 1; + } +#if notyet + if (scp->midi_intr.intr != NULL && + (port_rd(scp->io[1], 0) & 0x80)) { + (*scp->midi_intr.intr)(scp->midi_intr.arg); + did_something = 1; + } +#endif /* notyet */ + } while (did_something != 0); +} +#endif /* NISA > 0 */ + +static struct resource * +gusc_alloc_resource(device_t bus, device_t child, int type, int *rid, + u_long start, u_long end, u_long count, u_int flags) +{ + sc_p scp; + int *alloced, rid_max, alloced_max; + struct resource **res; + + scp = device_get_softc(bus); + switch (type) { + case SYS_RES_IOPORT: + alloced = scp->io_alloced; + res = scp->io; + rid_max = 2; + alloced_max = 2; /* pcm + midi (more to include synth) */ + break; + case SYS_RES_IRQ: + alloced = &scp->irq_alloced; + res = &scp->irq; + rid_max = 0; + alloced_max = 2; /* pcm and midi share the single irq. */ + break; + case SYS_RES_DRQ: + alloced = scp->drq_alloced; + res = scp->drq; + rid_max = 1; + alloced_max = 1; + break; + default: + return (NULL); + } + + if (*rid > rid_max || alloced[*rid] == alloced_max) + return (NULL); + + alloced[*rid]++; + return (res[*rid]); +} + +static int +gusc_release_resource(device_t bus, device_t child, int type, int rid, + struct resource *r) +{ + sc_p scp; + int *alloced, rid_max; + + scp = device_get_softc(bus); + switch (type) { + case SYS_RES_IOPORT: + alloced = scp->io_alloced; + rid_max = 2; + break; + case SYS_RES_IRQ: + alloced = &scp->irq_alloced; + rid_max = 0; + break; + case SYS_RES_DRQ: + alloced = scp->drq_alloced; + rid_max = 1; + break; + default: + return (1); + } + + if (rid > rid_max || alloced[rid] == 0) + return (1); + + alloced[rid]--; + return (0); +} + +static int +gusc_setup_intr(device_t dev, device_t child, struct resource *irq, + int flags, driver_intr_t *intr, void *arg, void **cookiep) +{ + sc_p scp = (sc_p)device_get_softc(dev); + devclass_t devclass; + + devclass = device_get_devclass(child); + if (strcmp(devclass_get_name(devclass), "midi") == 0) { + scp->midi_intr.intr = intr; + scp->midi_intr.arg = arg; + return 0; + } else if (strcmp(devclass_get_name(devclass), "pcm") == 0) { + scp->pcm_intr.intr = intr; + scp->pcm_intr.arg = arg; + return 0; + } + return bus_generic_setup_intr(dev, child, irq, flags, intr, + arg, cookiep); +} + +#if notyet +static device_t +find_masterdev(sc_p scp) +{ + int i, units; + devclass_t devclass; + device_t dev; + + devclass = device_get_devclass(scp->dev); + units = devclass_get_maxunit(devclass); + dev = NULL; + for (i = 0 ; i < units ; i++) { + dev = devclass_get_device(devclass, i); + if (isa_get_vendorid(dev) == isa_get_vendorid(scp->dev) + && isa_get_logicalid(dev) == LOGICALID_PCM + && isa_get_serial(dev) == isa_get_serial(scp->dev)) + break; + } + if (i == units) + return (NULL); + + return (dev); +} +#endif /* notyet */ + +static int io_range[3] = {0x10, 0x4, 0x4}; +static int +alloc_resource(sc_p scp) +{ + int i; +#if notyet + device_t dev; +#endif /* notyet */ + + switch(isa_get_logicalid(scp->dev)) { + case LOGICALID_PCM: + default: /* XXX Non-PnP */ + for (i = 0 ; i < sizeof(scp->io) / sizeof(*scp->io) ; i++) { + if (scp->io[i] == NULL) { + scp->io_rid[i] = i; + scp->io[i] = bus_alloc_resource(scp->dev, SYS_RES_IOPORT, &scp->io_rid[i], + 0, ~0, io_range[i], RF_ACTIVE); + if (scp->io[i] == NULL) + return (1); + scp->io_alloced[i] = 0; + } + } + if (scp->irq == NULL) { + scp->irq_rid = 0; + scp->irq = bus_alloc_resource(scp->dev, SYS_RES_IRQ, &scp->irq_rid, + 0, ~0, 1, RF_ACTIVE | RF_SHAREABLE); + if (scp->irq == NULL) + return (1); + scp->irq_alloced = 0; + } + for (i = 0 ; i < sizeof(scp->drq) / sizeof(*scp->drq) ; i++) { + if (scp->drq[i] == NULL) { + scp->drq_rid[i] = i; + scp->drq[i] = bus_alloc_resource(scp->dev, SYS_RES_DRQ, &scp->drq_rid[i], + 0, ~0, 1, RF_ACTIVE); + if (scp->drq[i] == NULL) + return (1); + scp->drq_alloced[i] = 0; + } + } + break; +#if notyet + case LOGICALID_OPL: + if (scp->io[0] == NULL) { + scp->io_rid[0] = 0; + scp->io[0] = bus_alloc_resource(scp->dev, SYS_RES_IOPORT, &scp->io_rid[0], + 0, ~0, io_range[0], RF_ACTIVE); + if (scp->io[0] == NULL) + return (1); + scp->io_alloced[0] = 0; + } + break; + case LOGICALID_MIDI: + if (scp->io[0] == NULL) { + scp->io_rid[0] = 0; + scp->io[0] = bus_alloc_resource(scp->dev, SYS_RES_IOPORT, &scp->io_rid[0], + 0, ~0, io_range[0], RF_ACTIVE); + if (scp->io[0] == NULL) + return (1); + scp->io_alloced[0] = 0; + } + if (scp->irq == NULL) { + /* The irq is shared with pcm audio. */ + dev = find_masterdev(scp); + if (dev == NULL) + return (1); + scp->irq_rid = 0; + scp->irq = BUS_ALLOC_RESOURCE(dev, NULL, SYS_RES_IRQ, &scp->irq_rid, + 0, ~0, 1, RF_ACTIVE | RF_SHAREABLE); + if (scp->irq == NULL) + return (1); + scp->irq_alloced = 0; + } + break; +#endif /* notyet */ + } + return (0); +} + +static int +release_resource(sc_p scp) +{ + int i; +#if notyet + device_t dev; +#endif /* notyet */ + + switch(isa_get_logicalid(scp->dev)) { + case LOGICALID_PCM: + default: /* XXX Non-PnP */ + for (i = 0 ; i < sizeof(scp->io) / sizeof(*scp->io) ; i++) { + if (scp->io[i] != NULL) { + bus_release_resource(scp->dev, SYS_RES_IOPORT, scp->io_rid[i], scp->io[i]); + scp->io[i] = NULL; + } + } + if (scp->irq != NULL) { + bus_release_resource(scp->dev, SYS_RES_IRQ, scp->irq_rid, scp->irq); + scp->irq = NULL; + } + for (i = 0 ; i < sizeof(scp->drq) / sizeof(*scp->drq) ; i++) { + if (scp->drq[i] != NULL) { + bus_release_resource(scp->dev, SYS_RES_DRQ, scp->drq_rid[i], scp->drq[i]); + scp->drq[i] = NULL; + } + } + break; +#if notyet + case LOGICALID_OPL: + if (scp->io[0] != NULL) { + bus_release_resource(scp->dev, SYS_RES_IOPORT, scp->io_rid[0], scp->io[0]); + scp->io[0] = NULL; + } + break; + case LOGICALID_MIDI: + if (scp->io[0] != NULL) { + bus_release_resource(scp->dev, SYS_RES_IOPORT, scp->io_rid[0], scp->io[0]); + scp->io[0] = NULL; + } + if (scp->irq != NULL) { + /* The irq is shared with pcm audio. */ + dev = find_masterdev(scp); + if (dev == NULL) + return (1); + BUS_RELEASE_RESOURCE(dev, NULL, SYS_RES_IOPORT, scp->irq_rid, scp->irq); + scp->irq = NULL; + } + break; +#endif /* notyet */ + } + return (0); +} + +#if NISA > 0 +static device_method_t gusc_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, gusc_probe), + DEVMETHOD(device_attach, gusc_attach), + DEVMETHOD(device_detach, bus_generic_detach), + DEVMETHOD(device_shutdown, bus_generic_shutdown), + DEVMETHOD(device_suspend, bus_generic_suspend), + DEVMETHOD(device_resume, bus_generic_resume), + + /* Bus interface */ + DEVMETHOD(bus_print_child, bus_generic_print_child), + DEVMETHOD(bus_alloc_resource, gusc_alloc_resource), + DEVMETHOD(bus_release_resource, gusc_release_resource), + DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), + DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), + DEVMETHOD(bus_setup_intr, gusc_setup_intr), + DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), + + { 0, 0 } +}; + +static driver_t gusc_driver = { + "gusc", + gusc_methods, + sizeof(struct gusc_softc), +}; + +/* + * gusc can be attached to an isa bus. + */ +DRIVER_MODULE(gusc, isa, gusc_driver, gusc_devclass, 0, 0); +#endif /* NISA > 0 */ + +#endif /* NGUSC > 0 */ diff --git a/sys/dev/sound/isa/mss.c b/sys/dev/sound/isa/mss.c index e09923a..b929550 100644 --- a/sys/dev/sound/isa/mss.c +++ b/sys/dev/sound/isa/mss.c @@ -34,6 +34,12 @@ /* board-specific include files */ #include <dev/sound/isa/mss.h> +#include <dev/sound/chip.h> + +#include "gusc.h" +#if notyet +#include "midi.h" +#endif /* notyet */ #define abs(x) (((x) < 0) ? -(x) : (x)) @@ -172,6 +178,7 @@ static pcm_channel mss_chantemplate = { #define MD_OPTI931 0xB1 #define MD_OPTI925 0xB2 #define MD_GUSPNP 0xB8 +#define MD_GUSMAX 0xB9 #define MD_YM0020 0xC1 #define MD_VIVO 0xD1 @@ -241,7 +248,7 @@ opti_rd(struct mss_info *mss, u_char reg) return port_rd(mss->conf_base, mss->opti_offset + 1); } -#if NPNP > 0 +#if NPNP > 0 || NGUSC > 0 static void gus_wr(struct mss_info *mss, u_char reg, u_char value) { @@ -255,7 +262,7 @@ gus_rd(struct mss_info *mss, u_char reg) port_wr(mss->conf_base, 3, reg); return port_rd(mss->conf_base, 5); } -#endif +#endif /* NPNP > 0 || NGUSC > 0 */ static void mss_release_resources(struct mss_info *mss, device_t dev) @@ -329,6 +336,64 @@ mss_alloc_resources(struct mss_info *mss, device_t dev) return ok; } +#if NGUSC > 0 +/* + * XXX This might be better off in the gusc driver. + */ +static void +gusmax_setup(struct mss_info *mss, device_t dev, struct resource *alt) +{ + static const unsigned char irq_bits[16] = { + 0, 0, 0, 3, 0, 2, 0, 4, 0, 1, 0, 5, 6, 0, 0, 7 + }; + static const unsigned char dma_bits[8] = { + 0, 1, 0, 2, 0, 3, 4, 5 + }; + device_t parent = device_get_parent(dev); + unsigned char irqctl, dmactl; + int s; + + s = splhigh(); + + port_wr(alt, 0x0f, 0x05); + port_wr(alt, 0x00, 0x0c); + port_wr(alt, 0x0b, 0x00); + + port_wr(alt, 0x0f, 0x00); + + irqctl = irq_bits[isa_get_irq(parent)]; +#if notyet +#if NMIDI > 0 + /* Share the IRQ with the MIDI driver. */ + irqctl |= 0x40; +#endif /* NMIDI > 0 */ +#endif /* notyet */ + dmactl = dma_bits[isa_get_drq(parent)]; + if (device_get_flags(parent) & DV_F_DUAL_DMA) + dmactl |= dma_bits[device_get_flags(parent) & DV_F_DRQ_MASK] + << 3; + + /* + * Set the DMA and IRQ control latches. + */ + port_wr(alt, 0x00, 0x0c); + port_wr(alt, 0x0b, dmactl | 0x80); + port_wr(alt, 0x00, 0x4c); + port_wr(alt, 0x0b, irqctl); + + port_wr(alt, 0x00, 0x0c); + port_wr(alt, 0x0b, dmactl); + port_wr(alt, 0x00, 0x4c); + port_wr(alt, 0x0b, irqctl); + + port_wr(mss->conf_base, 2, 0); + port_wr(alt, 0x00, 0x0c); + port_wr(mss->conf_base, 2, 0); + + splx(s); +} +#endif /* NGUSC > 0 */ + static int mss_init(struct mss_info *mss, device_t dev) { @@ -356,8 +421,11 @@ mss_init(struct mss_info *mss, device_t dev) opti_wr(mss, 6, 2); /* MCIR6: mss enable, sb disable */ opti_wr(mss, 5, 0x28); /* MCIR5: codec in exp. mode,fifo */ break; +#endif /* NPNP > 0 */ +#if NPNP > 0 || NGUSC > 0 case MD_GUSPNP: + case MD_GUSMAX: gus_wr(mss, 0x4c /* _URSTI */, 0);/* Pull reset */ DELAY(1000 * 30); /* release reset and enable DAC */ @@ -368,7 +436,15 @@ mss_init(struct mss_info *mss, device_t dev) rid = 0; alt = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 0, ~0, 1, RF_ACTIVE); + if (alt == NULL) { + printf("XXX couldn't init GUS PnP/MAX\n"); + break; + } port_wr(alt, 0, 0xC); /* enable int and dma */ +#if NGUSC > 0 + if (mss->bd_id == MD_GUSMAX) + gusmax_setup(mss, dev, alt); +#endif bus_release_resource(dev, SYS_RES_IOPORT, rid, alt); /* @@ -389,7 +465,8 @@ mss_init(struct mss_info *mss, device_t dev) gus_wr(mss, 0x5b, tmp | 1); BVDDB(printf("GUS: silicon rev %c\n", 'A' + ((tmp & 0xf) >> 4))); break; -#endif +#endif /* NPNP > 0 || NGUSC > 0 */ + case MD_YM0020: conf_wr(mss, OPL3SAx_DMACONF, 0xa9); /* dma-b rec, dma-a play */ r6 = conf_rd(mss, OPL3SAx_DMACONF); @@ -1015,7 +1092,22 @@ wait_for_calibration(struct mss_info *mss) n = ad_wait_init(mss, 1000); if (n & MSS_IDXBUSY) printf("mss: Auto calibration timed out(1).\n"); - for (t = 100; t > 0 && (ad_read(mss, 11) & 0x20) == 0; t--) DELAY(100); + /* + * There is no guarantee that we'll ever see ACI go on, + * calibration may finish before we get here. + * + * XXX Are there docs that even state that it might ever be + * visible off before calibration starts using any chip? + */ + if (mss->bd_id == MD_GUSMAX) { + /* 10 ms of busy-waiting is not reasonable normal behavior */ + for (t = 100; t > 0 && (ad_read(mss, 11) & 0x20) == 0; t--) + ; + if (t > 0 && t != 100) + printf("debug: ACI turned on: t = %d\n", t); + } else { + for (t = 100; t > 0 && (ad_read(mss, 11) & 0x20) == 0; t--) DELAY(100); + } for (t = 100; t > 0 && ad_read(mss, 11) & 0x20; t--) DELAY(100); } @@ -1290,9 +1382,11 @@ pnpmss_probe(device_t dev) s = "OPTi925"; break; +#if 0 case 0x0000561e: s = "GusPnP"; break; +#endif case 0x01000000: if (vend_id == 0x0100a90d) s = "CMI8330"; @@ -1361,6 +1455,7 @@ pnpmss_attach(device_t dev) mss->bd_id = MD_OPTI925; break; +#if 0 case 0x0100561e: /* guspnp */ mss->bd_flags |= BD_F_MSS_OFFSET; mss->io_rid = 2; @@ -1369,6 +1464,7 @@ pnpmss_attach(device_t dev) mss->drq2_rid = 0; mss->bd_id = MD_GUSPNP; break; +#endif default: mss->bd_flags |= BD_F_MSS_OFFSET; @@ -1453,6 +1549,91 @@ opti931_intr(void *arg) #endif /* NPNP > 0 */ +#if NGUSC > 0 + +static int +guspcm_probe(device_t dev) +{ + struct sndcard_func *func; + + func = device_get_ivars(dev); + if (func == NULL || func->func != SCF_PCM) + return ENXIO; + + device_set_desc(dev, "GUS CS4231"); + return 0; +} + +static int +guspcm_attach(device_t dev) +{ + device_t parent = device_get_parent(dev); + struct mss_info *mss; + int base, flags; + unsigned char ctl; + + mss = (struct mss_info *)malloc(sizeof *mss, M_DEVBUF, M_NOWAIT); + if (mss == NULL) + return ENOMEM; + bzero(mss, sizeof *mss); + + mss->bd_flags = BD_F_MSS_OFFSET; + mss->io_rid = 2; + mss->conf_rid = 1; + mss->irq_rid = 0; + mss->drq1_rid = 1; + mss->drq2_rid = -1; + + if (isa_get_vendorid(parent) == 0) + mss->bd_id = MD_GUSMAX; + else { + mss->bd_id = MD_GUSPNP; + mss->drq2_rid = 0; + goto skip_setup; + } + + flags = device_get_flags(parent); + if (flags & DV_F_DUAL_DMA) + mss->drq2_rid = 0; + + mss->conf_base = bus_alloc_resource(dev, SYS_RES_IOPORT, &mss->conf_rid, + 0, ~0, 8, RF_ACTIVE); + + if (mss->conf_base == NULL) { + mss_release_resources(mss, dev); + return ENXIO; + } + + base = isa_get_port(parent); + + ctl = 0x40; /* CS4231 enable */ + if (isa_get_drq(dev) > 3) + ctl |= 0x10; /* 16-bit dma channel 1 */ + if ((flags & DV_F_DUAL_DMA) != 0 && (flags & DV_F_DRQ_MASK) > 3) + ctl |= 0x20; /* 16-bit dma channel 2 */ + ctl |= (base >> 4) & 0x0f; /* 2X0 -> 3XC */ + port_wr(mss->conf_base, 6, ctl); + +skip_setup: + return mss_doattach(dev, mss); +} + +static device_method_t guspcm_methods[] = { + DEVMETHOD(device_probe, guspcm_probe), + DEVMETHOD(device_attach, guspcm_attach), + + { 0, 0 } +}; + +static driver_t guspcm_driver = { + "pcm", + guspcm_methods, + sizeof(snddev_info), +}; + +DRIVER_MODULE(guspcm, gusc, guspcm_driver, pcm_devclass, 0, 0); +#endif /* NGUSC > 0 */ + static int mssmix_init(snd_mixer *m) { @@ -1472,6 +1653,7 @@ mssmix_init(snd_mixer *m) break; case MD_GUSPNP: + case MD_GUSMAX: /* this is only necessary in mode 3 ... */ ad_write(mss, 22, 0x88); ad_write(mss, 23, 0x88); @@ -1631,6 +1813,7 @@ msschan_getcaps(void *data) break; case MD_GUSPNP: + case MD_GUSMAX: return &guspnp_caps; break; diff --git a/sys/dev/sound/isa/sb.c b/sys/dev/sound/isa/sb.c index 8c41bac..89c355a 100644 --- a/sys/dev/sound/isa/sb.c +++ b/sys/dev/sound/isa/sb.c @@ -34,8 +34,11 @@ #include <dev/sound/pcm/sound.h> #if NPCM > 0 +#include "sbc.h" + #define __SB_MIXER_C__ /* XXX warning... */ #include <dev/sound/isa/sb.h> +#include <dev/sound/chip.h> /* channel interface */ static void *sbchan_init(void *devinfo, snd_dbuf *b, pcm_channel *c, int dir); @@ -1258,25 +1261,6 @@ sbpnp_probe(device_t dev) u_int32_t logical_id = isa_get_logicalid(dev); switch(logical_id) { - case 0x43008c0e: /* CTL0043 */ - case 0x01008c0e: /* CTL0001 */ - s = "Vibra16X"; - break; - - case 0x31008c0e: /* CTL0031 */ - case 0x41008c0e: /* CTL0041 */ - case 0x42008c0e: /* CTL0042 */ - s = "SB16 PnP"; - break; - - case 0x44008c0e: /* CTL0044 */ - s = "Creative SB AWE64 Gold"; - break; - - case 0x45008c0e: /* CTL0045 */ - s = "Creative AWE64 PnP"; - break; - case 0x01100000: /* @@@1001 */ s = "Avance Asound 110"; break; @@ -1299,7 +1283,7 @@ sbpnp_probe(device_t dev) } if (s) { device_set_desc(dev, s); - return 0; + return (0); } return ENXIO; } @@ -1342,6 +1326,67 @@ DRIVER_MODULE(sbpnp, isa, sbpnp_driver, pcm_devclass, 0, 0); #endif /* NPNP > 0 */ +#if NSBC > 0 +#define DESCSTR " PCM Audio" +static int +sbsbc_probe(device_t dev) +{ + char *s = NULL; + struct sndcard_func *func; + + /* The parent device has already been probed. */ + + func = device_get_ivars(dev); + if (func == NULL || func->func != SCF_PCM) + return (ENXIO); + + s = "SB PCM Audio"; + + device_set_desc(dev, s); + return 0; +} + +static int +sbsbc_attach(device_t dev) +{ + struct sb_info *sb; + u_int32_t vend_id; + device_t sbc; + + sbc = device_get_parent(dev); + vend_id = isa_get_vendorid(sbc); + sb = (struct sb_info *)malloc(sizeof *sb, M_DEVBUF, M_NOWAIT); + if (!sb) return ENXIO; + bzero(sb, sizeof *sb); + + switch(vend_id) { + case 0xf0008c0e: + case 0x10019305: + case 0x20019305: + /* XXX add here the vend_id for other vibra16X cards... */ + sb->bd_flags = BD_F_SB16X; + } + return sb_doattach(dev, sb); +} + +static device_method_t sbsbc_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, sbsbc_probe), + DEVMETHOD(device_attach, sbsbc_attach), + + { 0, 0 } +}; + +static driver_t sbsbc_driver = { + "pcm", + sbsbc_methods, + sizeof(snddev_info), +}; + +DRIVER_MODULE(sbsbc, sbc, sbsbc_driver, pcm_devclass, 0, 0); + +#endif /* NSBC > 0 */ + #endif /* NPCM > 0 */ diff --git a/sys/dev/sound/isa/sb16.c b/sys/dev/sound/isa/sb16.c index 8c41bac..89c355a 100644 --- a/sys/dev/sound/isa/sb16.c +++ b/sys/dev/sound/isa/sb16.c @@ -34,8 +34,11 @@ #include <dev/sound/pcm/sound.h> #if NPCM > 0 +#include "sbc.h" + #define __SB_MIXER_C__ /* XXX warning... */ #include <dev/sound/isa/sb.h> +#include <dev/sound/chip.h> /* channel interface */ static void *sbchan_init(void *devinfo, snd_dbuf *b, pcm_channel *c, int dir); @@ -1258,25 +1261,6 @@ sbpnp_probe(device_t dev) u_int32_t logical_id = isa_get_logicalid(dev); switch(logical_id) { - case 0x43008c0e: /* CTL0043 */ - case 0x01008c0e: /* CTL0001 */ - s = "Vibra16X"; - break; - - case 0x31008c0e: /* CTL0031 */ - case 0x41008c0e: /* CTL0041 */ - case 0x42008c0e: /* CTL0042 */ - s = "SB16 PnP"; - break; - - case 0x44008c0e: /* CTL0044 */ - s = "Creative SB AWE64 Gold"; - break; - - case 0x45008c0e: /* CTL0045 */ - s = "Creative AWE64 PnP"; - break; - case 0x01100000: /* @@@1001 */ s = "Avance Asound 110"; break; @@ -1299,7 +1283,7 @@ sbpnp_probe(device_t dev) } if (s) { device_set_desc(dev, s); - return 0; + return (0); } return ENXIO; } @@ -1342,6 +1326,67 @@ DRIVER_MODULE(sbpnp, isa, sbpnp_driver, pcm_devclass, 0, 0); #endif /* NPNP > 0 */ +#if NSBC > 0 +#define DESCSTR " PCM Audio" +static int +sbsbc_probe(device_t dev) +{ + char *s = NULL; + struct sndcard_func *func; + + /* The parent device has already been probed. */ + + func = device_get_ivars(dev); + if (func == NULL || func->func != SCF_PCM) + return (ENXIO); + + s = "SB PCM Audio"; + + device_set_desc(dev, s); + return 0; +} + +static int +sbsbc_attach(device_t dev) +{ + struct sb_info *sb; + u_int32_t vend_id; + device_t sbc; + + sbc = device_get_parent(dev); + vend_id = isa_get_vendorid(sbc); + sb = (struct sb_info *)malloc(sizeof *sb, M_DEVBUF, M_NOWAIT); + if (!sb) return ENXIO; + bzero(sb, sizeof *sb); + + switch(vend_id) { + case 0xf0008c0e: + case 0x10019305: + case 0x20019305: + /* XXX add here the vend_id for other vibra16X cards... */ + sb->bd_flags = BD_F_SB16X; + } + return sb_doattach(dev, sb); +} + +static device_method_t sbsbc_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, sbsbc_probe), + DEVMETHOD(device_attach, sbsbc_attach), + + { 0, 0 } +}; + +static driver_t sbsbc_driver = { + "pcm", + sbsbc_methods, + sizeof(snddev_info), +}; + +DRIVER_MODULE(sbsbc, sbc, sbsbc_driver, pcm_devclass, 0, 0); + +#endif /* NSBC > 0 */ + #endif /* NPCM > 0 */ diff --git a/sys/dev/sound/isa/sb8.c b/sys/dev/sound/isa/sb8.c index 8c41bac..89c355a 100644 --- a/sys/dev/sound/isa/sb8.c +++ b/sys/dev/sound/isa/sb8.c @@ -34,8 +34,11 @@ #include <dev/sound/pcm/sound.h> #if NPCM > 0 +#include "sbc.h" + #define __SB_MIXER_C__ /* XXX warning... */ #include <dev/sound/isa/sb.h> +#include <dev/sound/chip.h> /* channel interface */ static void *sbchan_init(void *devinfo, snd_dbuf *b, pcm_channel *c, int dir); @@ -1258,25 +1261,6 @@ sbpnp_probe(device_t dev) u_int32_t logical_id = isa_get_logicalid(dev); switch(logical_id) { - case 0x43008c0e: /* CTL0043 */ - case 0x01008c0e: /* CTL0001 */ - s = "Vibra16X"; - break; - - case 0x31008c0e: /* CTL0031 */ - case 0x41008c0e: /* CTL0041 */ - case 0x42008c0e: /* CTL0042 */ - s = "SB16 PnP"; - break; - - case 0x44008c0e: /* CTL0044 */ - s = "Creative SB AWE64 Gold"; - break; - - case 0x45008c0e: /* CTL0045 */ - s = "Creative AWE64 PnP"; - break; - case 0x01100000: /* @@@1001 */ s = "Avance Asound 110"; break; @@ -1299,7 +1283,7 @@ sbpnp_probe(device_t dev) } if (s) { device_set_desc(dev, s); - return 0; + return (0); } return ENXIO; } @@ -1342,6 +1326,67 @@ DRIVER_MODULE(sbpnp, isa, sbpnp_driver, pcm_devclass, 0, 0); #endif /* NPNP > 0 */ +#if NSBC > 0 +#define DESCSTR " PCM Audio" +static int +sbsbc_probe(device_t dev) +{ + char *s = NULL; + struct sndcard_func *func; + + /* The parent device has already been probed. */ + + func = device_get_ivars(dev); + if (func == NULL || func->func != SCF_PCM) + return (ENXIO); + + s = "SB PCM Audio"; + + device_set_desc(dev, s); + return 0; +} + +static int +sbsbc_attach(device_t dev) +{ + struct sb_info *sb; + u_int32_t vend_id; + device_t sbc; + + sbc = device_get_parent(dev); + vend_id = isa_get_vendorid(sbc); + sb = (struct sb_info *)malloc(sizeof *sb, M_DEVBUF, M_NOWAIT); + if (!sb) return ENXIO; + bzero(sb, sizeof *sb); + + switch(vend_id) { + case 0xf0008c0e: + case 0x10019305: + case 0x20019305: + /* XXX add here the vend_id for other vibra16X cards... */ + sb->bd_flags = BD_F_SB16X; + } + return sb_doattach(dev, sb); +} + +static device_method_t sbsbc_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, sbsbc_probe), + DEVMETHOD(device_attach, sbsbc_attach), + + { 0, 0 } +}; + +static driver_t sbsbc_driver = { + "pcm", + sbsbc_methods, + sizeof(snddev_info), +}; + +DRIVER_MODULE(sbsbc, sbc, sbsbc_driver, pcm_devclass, 0, 0); + +#endif /* NSBC > 0 */ + #endif /* NPCM > 0 */ diff --git a/sys/dev/sound/isa/sbc.c b/sys/dev/sound/isa/sbc.c new file mode 100644 index 0000000..367f20f --- /dev/null +++ b/sys/dev/sound/isa/sbc.c @@ -0,0 +1,393 @@ +/*- + * Copyright (c) 1999 Seigo Tanimura + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#include "sbc.h" +#include "isa.h" +#include "pnp.h" + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/bus.h> +#include <sys/malloc.h> +#include <sys/module.h> +#include <machine/resource.h> +#include <machine/bus.h> +#include <sys/rman.h> +#include <sys/soundcard.h> +#include <dev/sound/chip.h> + +#if NISA > 0 +#include <isa/isavar.h> +#include <isa/isa_common.h> +#ifdef __alpha__ /* XXX workaround a stupid warning */ +#include <alpha/isa/isavar.h> +#endif +#endif /* NISA > 0 */ + +#if NSBC > 0 + +/* Here is the parameter structure per a device. */ +struct sbc_softc { + device_t dev; /* device */ + int io_rid[3]; /* io port rids */ + struct resource *io[3]; /* io port resources */ + int io_alloced[3]; /* io port alloc flag */ + int irq_rid; /* irq rids */ + struct resource *irq; /* irq resources */ + int irq_alloced; /* irq alloc flag */ + int drq_rid[2]; /* drq rids */ + struct resource *drq[2]; /* drq resources */ + int drq_alloced[2]; /* drq alloc flag */ +}; + +typedef struct sbc_softc *sc_p; + +#if NISA > 0 && NPNP > 0 +static int sbc_probe(device_t dev); +static int sbc_attach(device_t dev); +#endif /* NISA > 0 && NPNP > 0 */ +static struct resource *sbc_alloc_resource(device_t bus, device_t child, int type, int *rid, + u_long start, u_long end, u_long count, u_int flags); +static int sbc_release_resource(device_t bus, device_t child, int type, int rid, + struct resource *r); + +static int alloc_resource(sc_p scp); +static int release_resource(sc_p scp); + +static devclass_t sbc_devclass; + +#if NISA > 0 && NPNP > 0 +static int +sbc_probe(device_t dev) +{ + u_int32_t vend_id, logical_id, vend_id2; + char *s; + struct sndcard_func *func; + + vend_id = isa_get_vendorid(dev); + vend_id2 = vend_id & 0xff00ffff; + logical_id = isa_get_logicalid(dev); + s = NULL; + + switch (logical_id) { +#if notdef + case 0x0000630e: /* Crystal Semiconductor */ + if (vend_id2 ==0x3600630e) /* CS4236 */ + s = "CS4236"; + else if (vend_id2 ==0x3200630e) /* CS4232 */ + s = "CS4232"; + else if (vend_id2 ==0x3500630e) /* CS4236B */ + s = "CS4236B"; + break; +#endif /* notdef */ + case 0x01008c0e: /* Creative ViBRA16C */ + if (vend_id2 == 0x70008c0e) + s = "Creative ViBRA16C PnP"; + break; + case 0x43008c0e: /* Creative ViBRA16X */ + if (vend_id2 == 0xf0008c0e) + s = "Creative ViBRA16C PnP"; + break; + case 0x31008c0e: /* Creative SB */ + if (vend_id2 == 0x26008c0e) + s = "Creative SB16 PnP"; + else if (vend_id2 == 0x42008c0e) + s = "Creative SB32 (CTL0042)"; + else if (vend_id2 == 0x44008c0e) + s = "Creative SB32 (CTL0044)"; + else if (vend_id2 == 0x49008c0e) + s = "Creative SB32 (CTL0049)"; + else if (vend_id2 == 0xf1008c0e) + s = "Creative SB32 (CTL00f1)"; + break; + case 0x42008c0e: /* Creative SB AWE64 (CTL00c1) */ + if (vend_id2 == 0xc1008c0e) + s = "Creative SB AWE64 (CTL00c1)"; + break; + case 0x45008c0e: /* Creative SB AWE64 (CTL0045) */ + if (vend_id2 == 0xe4008c0e) + s = "Creative SB AWE64 (CTL0045)"; + break; +#if notdef + case 0x01200001: /* Avance Logic */ + if (vend_id2 == 0x20009305) + s = "Avance Logic ALS120"; + break; + case 0x01100001: /* Avance Asound */ + if (vend_id2 == 0x10009305) + s = "Avance Asound 110"; + break; + case 0x68187316: /* ESS1868 */ + if (vend_id2 == 0x68007316) + s = "ESS ES1868 Plug and Play AudioDrive"; + break; + case 0x79187316: /* ESS1879 */ + if (vend_id2 == 0x79007316) + s = "ESS ES1879 Plug and Play AudioDrive"; + break; + case 0x2100a865: /* Yamaha */ + if (vend_id2 == 0x2000a865) + s = "Yamaha OPL3-SA2/SAX Sound Board"; + break; + case 0x80719304: /* Terratec */ + if (vend_id2 == 0x1114b250) + s = "Terratec Soundsystem Base 1"; + break; + case 0x0300561e: /* Gravis */ + if (vend_id2 == 0x0100561e) + s = "Gravis UltraSound Plug & Play"; + break; +#endif /* notdef */ + } + + 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; + device_add_child(dev, "pcm", -1, 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; + device_add_child(dev, "midi", -1, func); + + /* OPL FM Synthesizer */ + func = malloc(sizeof(struct sndcard_func), M_DEVBUF, M_NOWAIT); + if (func == NULL) + return (ENOMEM); + bzero(func, sizeof(*func)); + func->func = SCF_SYNTH; + device_add_child(dev, "midi", -1, func); +#endif /* notyet */ + + return (0); + } + + return (ENXIO); +} + +static int +sbc_attach(device_t dev) +{ + sc_p scp; + int unit; + + scp = device_get_softc(dev); + unit = device_get_unit(dev); + + bzero(scp, sizeof(*scp)); + + scp->dev = dev; + if (alloc_resource(scp)) { + release_resource(scp); + return (ENXIO); + } + + bus_generic_attach(dev); + + return (0); +} +#endif /* NISA > 0 && NPNP > 0 */ + +static struct resource * +sbc_alloc_resource(device_t bus, device_t child, int type, int *rid, + u_long start, u_long end, u_long count, u_int flags) +{ + sc_p scp; + int *alloced, rid_max, alloced_max; + struct resource **res; + + scp = device_get_softc(bus); + switch (type) { + case SYS_RES_IOPORT: + alloced = scp->io_alloced; + res = scp->io; + rid_max = 2; + alloced_max = 1; + break; + case SYS_RES_IRQ: + alloced = &scp->irq_alloced; + res = &scp->irq; + rid_max = 0; + alloced_max = 2; /* pcm and mpu may share the irq. */ + break; + case SYS_RES_DRQ: + alloced = scp->drq_alloced; + res = scp->drq; + rid_max = 1; + alloced_max = 1; + break; + default: + return (NULL); + } + + if (*rid > rid_max || alloced[*rid] == alloced_max) + return (NULL); + + alloced[*rid]++; + return (res[*rid]); +} + +static int +sbc_release_resource(device_t bus, device_t child, int type, int rid, + struct resource *r) +{ + sc_p scp; + int *alloced, rid_max; + + scp = device_get_softc(bus); + switch (type) { + case SYS_RES_IOPORT: + alloced = scp->io_alloced; + rid_max = 2; + break; + case SYS_RES_IRQ: + alloced = &scp->irq_alloced; + rid_max = 0; + break; + case SYS_RES_DRQ: + alloced = scp->drq_alloced; + rid_max = 1; + break; + default: + return (1); + } + + if (rid > rid_max || alloced[rid] == 0) + return (1); + + alloced[rid]--; + return (0); +} + +static int io_range[3] = {0x10, 0x4, 0x4}; +static int +alloc_resource(sc_p scp) +{ + int i; + + for (i = 0 ; i < sizeof(scp->io) / sizeof(*scp->io) ; i++) { + if (scp->io[i] == NULL) { + scp->io_rid[i] = i; + scp->io[i] = bus_alloc_resource(scp->dev, SYS_RES_IOPORT, &scp->io_rid[i], + 0, ~0, io_range[i], RF_ACTIVE); + if (scp->io[i] == NULL) + return (1); + scp->io_alloced[i] = 0; + } + } + if (scp->irq == NULL) { + scp->irq_rid = 0; + scp->irq = bus_alloc_resource(scp->dev, SYS_RES_IRQ, &scp->irq_rid, + 0, ~0, 1, RF_ACTIVE | RF_SHAREABLE); + if (scp->irq == NULL) + return (1); + scp->irq_alloced = 0; + } + for (i = 0 ; i < sizeof(scp->drq) / sizeof(*scp->drq) ; i++) { + if (scp->drq[i] == NULL) { + scp->drq_rid[i] = i; + scp->drq[i] = bus_alloc_resource(scp->dev, SYS_RES_DRQ, &scp->drq_rid[i], + 0, ~0, 1, RF_ACTIVE); + if (scp->drq[i] == NULL) + return (1); + scp->drq_alloced[i] = 0; + } + } + return (0); +} + +static int +release_resource(sc_p scp) +{ + int i; + + for (i = 0 ; i < sizeof(scp->io) / sizeof(*scp->io) ; i++) { + if (scp->io[i] != NULL) { + bus_release_resource(scp->dev, SYS_RES_IOPORT, scp->io_rid[i], scp->io[i]); + scp->io[i] = NULL; + } + } + if (scp->irq != NULL) { + bus_release_resource(scp->dev, SYS_RES_IRQ, scp->irq_rid, scp->irq); + scp->irq = NULL; + } + for (i = 0 ; i < sizeof(scp->drq) / sizeof(*scp->drq) ; i++) { + if (scp->drq[i] != NULL) { + bus_release_resource(scp->dev, SYS_RES_DRQ, scp->drq_rid[i], scp->drq[i]); + scp->drq[i] = NULL; + } + } + return (0); +} + +#if NISA > 0 && NPNP > 0 +static device_method_t sbc_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, sbc_probe), + DEVMETHOD(device_attach, sbc_attach), + DEVMETHOD(device_detach, bus_generic_detach), + DEVMETHOD(device_shutdown, bus_generic_shutdown), + DEVMETHOD(device_suspend, bus_generic_suspend), + DEVMETHOD(device_resume, bus_generic_resume), + + /* Bus interface */ + DEVMETHOD(bus_print_child, bus_generic_print_child), + DEVMETHOD(bus_alloc_resource, sbc_alloc_resource), + DEVMETHOD(bus_release_resource, sbc_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), + + { 0, 0 } +}; + +static driver_t sbc_driver = { + "sbc", + sbc_methods, + sizeof(struct sbc_softc), +}; + +/* + * sbc can be attached to an isa bus. + */ +DRIVER_MODULE(sbc, isa, sbc_driver, sbc_devclass, 0, 0); +#endif /* NISA > 0 && NPNP > 0 */ + +#endif /* NSBC > 0 */ |