summaryrefslogtreecommitdiffstats
path: root/sys/dev/sound/isa
diff options
context:
space:
mode:
authortanimura <tanimura@FreeBSD.org>1999-11-22 06:07:49 +0000
committertanimura <tanimura@FreeBSD.org>1999-11-22 06:07:49 +0000
commit05e9b4af3eeff6a4e22e35b13bbc16d56c3dce70 (patch)
treefaaea41e349959e04b3e8cfe96726269290a60c8 /sys/dev/sound/isa
parent913c1ce7346351f8ee703d9125f82353999c9d0a (diff)
downloadFreeBSD-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.c647
-rw-r--r--sys/dev/sound/isa/mss.c191
-rw-r--r--sys/dev/sound/isa/sb.c85
-rw-r--r--sys/dev/sound/isa/sb16.c85
-rw-r--r--sys/dev/sound/isa/sb8.c85
-rw-r--r--sys/dev/sound/isa/sbc.c393
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 */
OpenPOWER on IntegriCloud