diff options
Diffstat (limited to 'sys/dev/sound/isa/sbc.c')
-rw-r--r-- | sys/dev/sound/isa/sbc.c | 411 |
1 files changed, 315 insertions, 96 deletions
diff --git a/sys/dev/sound/isa/sbc.c b/sys/dev/sound/isa/sbc.c index 67a367c..01d6c6b 100644 --- a/sys/dev/sound/isa/sbc.c +++ b/sys/dev/sound/isa/sbc.c @@ -28,114 +28,298 @@ #include "isa.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 */ +#include <dev/sound/pcm/sound.h> +#define __SB_MIXER_C__ /* XXX warning... */ +#define SB_NOMIXER +#include <dev/sound/isa/sb.h> /* 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; + u_int32_t bd_ver; +}; -#if NISA > 0 static int sbc_probe(device_t dev); static int sbc_attach(device_t dev); -#endif /* NISA > 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); + 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); + struct resource *r); -static int alloc_resource(sc_p scp); -static int release_resource(sc_p scp); +static int alloc_resource(struct sbc_softc *scp); +static int release_resource(struct sbc_softc *scp); static devclass_t sbc_devclass; -#if NISA > 0 +static int io_range[3] = {0x10, 0x2, 0x4}; + +static int sb_rd(struct resource *io, int reg); +static void sb_wr(struct resource *io, int reg, u_int8_t val); +static int sb_dspready(struct resource *io); +static int sb_cmd(struct resource *io, u_char val); +static u_int sb_get_byte(struct resource *io); +static void sb_setmixer(struct resource *io, u_int port, u_int value); + +static int +sb_rd(struct resource *io, int reg) +{ + return bus_space_read_1(rman_get_bustag(io), + rman_get_bushandle(io), + reg); +} + +static void +sb_wr(struct resource *io, int reg, u_int8_t val) +{ + return bus_space_write_1(rman_get_bustag(io), + rman_get_bushandle(io), + reg, val); +} + +static int +sb_dspready(struct resource *io) +{ + return ((sb_rd(io, SBDSP_STATUS) & 0x80) == 0); +} + +static int +sb_dspwr(struct resource *io, u_char val) +{ + int i; + + for (i = 0; i < 1000; i++) { + if (sb_dspready(io)) { + sb_wr(io, SBDSP_CMD, val); + return 1; + } + if (i > 10) DELAY((i > 100)? 1000 : 10); + } + printf("sb_dspwr(0x%02x) timed out.\n", val); + return 0; +} + +static int +sb_cmd(struct resource *io, u_char val) +{ + return sb_dspwr(io, val); +} + +static void +sb_setmixer(struct resource *io, u_int port, u_int value) +{ + u_long flags; + + flags = spltty(); + sb_wr(io, SB_MIX_ADDR, (u_char) (port & 0xff)); /* Select register */ + DELAY(10); + sb_wr(io, SB_MIX_DATA, (u_char) (value & 0xff)); + DELAY(10); + splx(flags); +} + +static u_int +sb_get_byte(struct resource *io) +{ + int i; + + for (i = 1000; i > 0; i--) { + if (sb_rd(io, DSP_DATA_AVAIL) & 0x80) + return sb_rd(io, DSP_READ); + else + DELAY(20); + } + return 0xffff; +} + +static int +sb_reset_dsp(struct resource *io) +{ + sb_wr(io, SBDSP_RST, 3); + DELAY(100); + sb_wr(io, SBDSP_RST, 0); + return (sb_get_byte(io) == 0xAA)? 0 : ENXIO; +} + +static int +sb_identify_board(struct resource *io) +{ + int ver, essver, rev; + + sb_cmd(io, DSP_CMD_GETVER); /* Get version */ + ver = (sb_get_byte(io) << 8) | sb_get_byte(io); + if (ver < 0x100 || ver > 0x4ff) return 0; + if (ver == 0x0301) { + /* Try to detect ESS chips. */ + sb_cmd(io, DSP_CMD_GETID); /* Return ident. bytes. */ + essver = (sb_get_byte(io) << 8) | sb_get_byte(io); + rev = essver & 0x000f; + essver &= 0xfff0; + if (essver == 0x4880) ver |= 0x1000; + else if (essver == 0x6880) ver = 0x0500 | rev; + } + return ver; +} + static struct isa_pnp_id sbc_ids[] = { -#if notdef - {0x0000630e, "CS423x"}, -#endif - {0x01008c0e, "Creative ViBRA16C PnP"}, - {0x41008c0e, "Creative ViBRA16CL PnP"}, - {0x43008c0e, "Creative ViBRA16X PnP"}, - {0x31008c0e, "Creative SB16 PnP/SB32"}, + {0x01008c0e, "Creative ViBRA16C"}, + {0x31008c0e, "Creative SB16/SB32"}, + {0x41008c0e, "Creative SB16/SB32"}, {0x42008c0e, "Creative SB AWE64"}, /* CTL00c1 */ + {0x43008c0e, "Creative ViBRA16X"}, + {0x44008c0e, "Creative SB AWE64 Gold"}, {0x45008c0e, "Creative SB AWE64"}, /* CTL0045 */ -#if notdef - {0x01200001, "Avance Logic ALS120"}, + + {0x01000000, "Avance Asound 100"}, {0x01100001, "Avance Asound 110"}, - {0x68187316, "ESS ES1868 Plug and Play AudioDrive"}, /* ESS1868 */ - {0x79187316, "ESS ES1879 Plug and Play AudioDrive"}, /* ESS1879 */ - {0x2100a865, "Yamaha OPL3-SA2/SAX Sound Board"}, - {0x80719304, "Terratec Soundsystem Base 1"}, -#endif + {0x01200001, "Avance Logic ALS120"}, + + {0x68187316, "ESS ES1868"}, /* ESS1868 */ + {0x69187316, "ESS ES1869"}, /* ESS1869 */ + {0xacb0110e, "ESS ES1869 (Compaq OEM)"}, + {0x79187316, "ESS ES1879"}, /* ESS1879 */ + {0x88187316, "ESS ES1888"}, /* ESS1888 */ + {0} }; static int sbc_probe(device_t dev) { - int error; - - /* Check pnp ids */ - error = ISA_PNP_PROBE(device_get_parent(dev), dev, sbc_ids); - if (error) - return error; - else - return -100; + char *s = NULL; + u_int32_t logical_id = isa_get_logicalid(dev); + if (logical_id) { + /* Check pnp ids */ + return ISA_PNP_PROBE(device_get_parent(dev), dev, sbc_ids); + } else { + int rid = 0, ver; + struct resource *io; + + io = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, + 0, ~0, 16, RF_ACTIVE); + if (!io) goto bad; + if (sb_reset_dsp(io)) goto bad2; + ver = sb_identify_board(io); + if (ver == 0) goto bad2; + switch ((ver & 0x00000f00) >> 8) { + case 1: + case 2: + s = "Soundblaster"; + break; + + case 3: + s = (ver & 0x0000f000)? "ESS 488" : "Soundblaster Pro"; + break; + + case 4: + s = "Soundblaster 16"; + break; + + case 5: + s = (ver & 0x00000008)? "ESS 688" : "ESS 1688"; + break; + } + if (s) device_set_desc(dev, s); +bad2: bus_release_resource(dev, SYS_RES_IOPORT, rid, io); +bad: return s? 0 : ENXIO; + } } static int sbc_attach(device_t dev) { - sc_p scp; - device_t child; - int unit; + struct sbc_softc *scp; struct sndcard_func *func; + device_t child; + u_int32_t logical_id = isa_get_logicalid(dev); + int flags = device_get_flags(dev); + int f = 0, dh = -1, dl = -1; - scp = device_get_softc(dev); - unit = device_get_unit(dev); + if (!logical_id && (flags & DV_F_DUAL_DMA)) { + bus_set_resource(dev, SYS_RES_DRQ, 1, + flags & DV_F_DRQ_MASK, 1); + } + scp = device_get_softc(dev); bzero(scp, sizeof(*scp)); - scp->dev = dev; - if (alloc_resource(scp)) { - release_resource(scp); - return (ENXIO); + if (alloc_resource(scp)) goto bad; + + if (sb_reset_dsp(scp->io[0])) goto bad; + scp->bd_ver = sb_identify_board(scp->io[0]) & 0x00000fff; + if (scp->bd_ver == 0) goto bad; + switch ((scp->bd_ver & 0x0f00) >> 8) { + case 1: /* old sound blaster has nothing... */ + break; + + case 2: + f |= BD_F_DUP_MIDI; + if (scp->bd_ver > 0x200) f |= BD_F_MIX_CT1335; + break; + + case 5: + f |= BD_F_ESS; + scp->bd_ver = 0x0301; + case 3: + f |= BD_F_DUP_MIDI | BD_F_MIX_CT1345; + break; + + case 4: + f |= BD_F_SB16 | BD_F_MIX_CT1745; + if (scp->drq[0]) dl = rman_get_start(scp->drq[0]); + if (scp->drq[1]) dh = rman_get_start(scp->drq[1]); else dh = dl; + if (dh != dl) f |= BD_F_DUPLEX; + if (dh < dl) { + struct resource *r; + r = scp->drq[0]; + scp->drq[0] = scp->drq[1]; + scp->drq[1] = r; + dl = rman_get_start(scp->drq[0]); + dh = rman_get_start(scp->drq[1]); + } + if (!logical_id) { + /* soft irq/dma configuration */ + int x = -1, irq = rman_get_start(scp->irq); + if (irq == 5) x = 2; + else if (irq == 7) x = 4; + else if (irq == 9) x = 1; + else if (irq == 10) x = 8; + if (x == -1) device_printf(dev, + "bad irq %d (5/7/9/10 valid)\n", + irq); + else sb_setmixer(scp->io[0], IRQ_NR, x); + sb_setmixer(scp->io[0], DMA_NR, (1 << dh) | (1 << dl)); + device_printf(dev, "setting non-pnp card to irq %d, drq %d", irq, dl); + if (dl != dh) printf(", %d", dh); + printf("\n"); + } + break; + } + + switch (logical_id) { + case 0x01008c0e: /* CTL0001 */ + case 0x43008c0e: /* CTL0043 */ + f |= BD_F_SB16X; + break; } + scp->bd_ver |= f << 16; /* PCM Audio */ func = malloc(sizeof(struct sndcard_func), M_DEVBUF, M_NOWAIT); - if (func == NULL) - return (ENOMEM); + if (func == NULL) goto bad; bzero(func, sizeof(*func)); func->func = SCF_PCM; child = device_add_child(dev, "pcm", -1); @@ -144,8 +328,7 @@ sbc_attach(device_t dev) #if notyet /* Midi Interface */ func = malloc(sizeof(struct sndcard_func), M_DEVBUF, M_NOWAIT); - if (func == NULL) - return (ENOMEM); + if (func == NULL) goto bad; bzero(func, sizeof(*func)); func->func = SCF_MIDI; child = device_add_child(dev, "midi", -1); @@ -153,25 +336,27 @@ sbc_attach(device_t dev) /* OPL FM Synthesizer */ func = malloc(sizeof(struct sndcard_func), M_DEVBUF, M_NOWAIT); - if (func == NULL) - return (ENOMEM); + if (func == NULL) goto bad; bzero(func, sizeof(*func)); func->func = SCF_SYNTH; child = device_add_child(dev, "midi", -1); device_set_ivars(child, func); #endif /* notyet */ + /* probe/attach kids */ bus_generic_attach(dev); return (0); + +bad: release_resource(scp); + return (ENXIO); } -#endif /* NISA > 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; + struct sbc_softc *scp; int *alloced, rid_max, alloced_max; struct resource **res; @@ -183,18 +368,18 @@ sbc_alloc_resource(device_t bus, device_t child, int type, int *rid, 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; + 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; default: return (NULL); } @@ -203,14 +388,14 @@ sbc_alloc_resource(device_t bus, device_t child, int type, int *rid, return (NULL); alloced[*rid]++; - return (res[*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; + struct sbc_softc *scp; int *alloced, rid_max; scp = device_get_softc(bus); @@ -219,14 +404,14 @@ sbc_release_resource(device_t bus, device_t child, int type, int rid, 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; + case SYS_RES_IRQ: + alloced = &scp->irq_alloced; + rid_max = 0; + break; default: return (1); } @@ -238,9 +423,44 @@ sbc_release_resource(device_t bus, device_t child, int type, int rid, return (0); } -static int io_range[3] = {0x10, 0x4, 0x4}; static int -alloc_resource(sc_p scp) +sbc_read_ivar(device_t bus, device_t dev, int index, uintptr_t * result) +{ + struct sbc_softc *scp = device_get_softc(bus); + struct sndcard_func *func = device_get_ivars(dev); + + switch (index) { + case 0: + *result = func->func; + break; + + case 1: + *result = scp->bd_ver; + break; + + default: + return ENOENT; + } + + return 0; +} + +static int +sbc_write_ivar(device_t bus, device_t dev, + int index, uintptr_t value) +{ + switch (index) { + case 0: + case 1: + return EINVAL; + + default: + return (ENOENT); + } +} + +static int +alloc_resource(struct sbc_softc *scp) { int i; @@ -249,34 +469,34 @@ alloc_resource(sc_p scp) 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) + if (i == 0 && 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) + if (i == 0 && scp->drq[i] == NULL) return (1); scp->drq_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); + if (scp->irq == NULL) + return (1); + scp->irq_alloced = 0; + } return (0); } static int -release_resource(sc_p scp) +release_resource(struct sbc_softc *scp) { int i; @@ -299,7 +519,6 @@ release_resource(sc_p scp) return (0); } -#if NISA > 0 static device_method_t sbc_methods[] = { /* Device interface */ DEVMETHOD(device_probe, sbc_probe), @@ -310,6 +529,8 @@ static device_method_t sbc_methods[] = { DEVMETHOD(device_resume, bus_generic_resume), /* Bus interface */ + DEVMETHOD(bus_read_ivar, sbc_read_ivar), + DEVMETHOD(bus_write_ivar, sbc_write_ivar), DEVMETHOD(bus_print_child, bus_generic_print_child), DEVMETHOD(bus_alloc_resource, sbc_alloc_resource), DEVMETHOD(bus_release_resource, sbc_release_resource), @@ -327,8 +548,6 @@ static driver_t sbc_driver = { sizeof(struct sbc_softc), }; -/* - * sbc can be attached to an isa bus. - */ +/* sbc can be attached to an isa bus. */ DRIVER_MODULE(sbc, isa, sbc_driver, sbc_devclass, 0, 0); -#endif /* NISA > 0 */ + |