diff options
author | cg <cg@FreeBSD.org> | 1999-09-01 04:01:57 +0000 |
---|---|---|
committer | cg <cg@FreeBSD.org> | 1999-09-01 04:01:57 +0000 |
commit | feefbc8c0eaa6a6e695c1e9c3514be8d22e881ea (patch) | |
tree | 045ef45e49c561940485e33dec98ae361705568f | |
parent | 40676b1f20541a3bc3b49840b0eff5ec547720dd (diff) | |
download | FreeBSD-src-feefbc8c0eaa6a6e695c1e9c3514be8d22e881ea.zip FreeBSD-src-feefbc8c0eaa6a6e695c1e9c3514be8d22e881ea.tar.gz |
mss and sb drivers updated for newpcm
Reviewed by: peter, dfr
-rw-r--r-- | sys/dev/pcm/isa/mss.c | 3700 | ||||
-rw-r--r-- | sys/dev/pcm/isa/mss.h | 225 | ||||
-rw-r--r-- | sys/dev/pcm/isa/sb.c | 2167 | ||||
-rw-r--r-- | sys/dev/pcm/isa/sb.h | 70 | ||||
-rw-r--r-- | sys/dev/sound/isa/mss.c | 3700 | ||||
-rw-r--r-- | sys/dev/sound/isa/mss.h | 225 | ||||
-rw-r--r-- | sys/dev/sound/isa/sb.c | 2167 | ||||
-rw-r--r-- | sys/dev/sound/isa/sb.h | 70 | ||||
-rw-r--r-- | sys/dev/sound/isa/sb16.c | 2167 | ||||
-rw-r--r-- | sys/dev/sound/isa/sb8.c | 2167 |
10 files changed, 7532 insertions, 9126 deletions
diff --git a/sys/dev/pcm/isa/mss.c b/sys/dev/pcm/isa/mss.c index 9d0a0cf..0380121 100644 --- a/sys/dev/pcm/isa/mss.c +++ b/sys/dev/pcm/isa/mss.c @@ -1,134 +1,420 @@ /* - * sound/ad1848.c - * - * Driver for Microsoft Sound System/Windows Sound System (mss) - * -compatible boards. This includes: - * - * AD1848, CS4248, CS423x, OPTi931, Yamaha OPL/SAx and many others. - * + * Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk> * Copyright Luigi Rizzo, 1997,1998 * Copyright by Hannu Savolainen 1994, 1995 - * + * 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. + * 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. - * - * Full data sheets in PDF format for the MSS-compatible chips - * are available at + * 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. * - * http://www.crystal.com/ for the CS42XX series, or - * http://www.opti.com/ for the OPTi931 + * $Id$ */ -#include <i386/isa/snd/sound.h> +#include <dev/pcm/sound.h> + #if NPCM > 0 -/* - * board-specific include files - */ +/* board-specific include files */ +#include <dev/pcm/isa/mss.h> -#include <i386/isa/snd/mss.h> +struct mss_info; -/* - * prototypes for procedures exported in the device descriptor - */ +struct mss_chinfo { + struct mss_info *parent; + pcm_channel *channel; + snd_dbuf *buffer; + int dir; +}; -static int mss_probe(struct isa_device *dev); -static int mss_attach(struct isa_device *dev); +struct mss_info { + struct resource *io_base; /* primary I/O address for the board */ + int io_rid; + struct resource *conf_base; /* and the opti931 also has a config space */ + int conf_rid; + struct resource *irq; + int irq_rid; + struct resource *drq1; /* play */ + int drq1_rid; + struct resource *drq2; /* rec */ + int drq2_rid; + bus_dma_tag_t parent_dmat; + + int pdma, rdma; + int bd_id; /* used to hold board-id info, eg. sb version, + * mss codec type, etc. etc. + */ + u_long bd_flags; /* board-specific flags */ + struct mss_chinfo pch, rch; +}; -d_open_t mss_open; /* this is a generic full-duplex open routine */ -static d_close_t mss_close; -static d_ioctl_t mss_ioctl; -static irq_proc_t mss_intr; -static irq_proc_t opti931_intr; -static snd_callback_t mss_callback; - -/* - * prototypes for local functions - */ +static int mss_probe(device_t dev); +static int mss_attach(device_t dev); + +static driver_intr_t mss_intr; -static void mss_reinit(snddev_info *d); -static int AD_WAIT_INIT(snddev_info *d, int x); -static int mss_mixer_set(snddev_info *d, int dev, int value); -static int mss_set_recsrc(snddev_info *d, int mask); -static void ad1848_mixer_reset(snddev_info *d); +/* prototypes for local functions */ +static int mss_detect(device_t dev, struct mss_info *mss); +static char *ymf_test(device_t dev, struct mss_info *mss); +static void ad_unmute(struct mss_info *mss); -static void opti_write(int io_base, u_char reg, u_char data); -static u_char opti_read(int io_base, u_char reg); -static void ad_write(snddev_info *d, int reg, u_char data); -static void ad_write_cnt(snddev_info *d, int reg, u_short data); -static int ad_read(snddev_info *d, int reg); +/* mixer set funcs */ +static int mss_mixer_set(struct mss_info *mss, int dev, int left, int right); +static int mss_set_recsrc(struct mss_info *mss, int mask); -#if NPNP > 0 /* the ad1816 is pnp only */ -/* ad1816 prototypes */ +/* io funcs */ +static int ad_wait_init(struct mss_info *mss, int x); +static int ad_read(struct mss_info *mss, int reg); +static void ad_write(struct mss_info *mss, int reg, u_char data); +static void ad_write_cnt(struct mss_info *mss, int reg, u_short data); + +/* io primitives */ +static void conf_wr(struct mss_info *mss, u_char reg, u_char data); +static u_char conf_rd(struct mss_info *mss, u_char reg); + +#if NPNP > 0 +static int pnpmss_probe(device_t dev); +static int pnpmss_attach(device_t dev); + +static driver_intr_t opti931_intr; +static driver_intr_t ad1816_intr; /* IO primitives */ -static int ad1816_wait_init(snddev_info * d, int x); -static u_short ad1816_read(snddev_info * d, u_int reg); -static void ad1816_write(snddev_info * d, u_int reg, u_short data); -/* intr and callback functions */ -static irq_proc_t ad1816_intr; -static snd_callback_t ad1816_callback; -/* device specific ioctl calls */ -static d_ioctl_t ad1816_ioctl; -/* parameter set functions */ -static void ad1816_reinit(snddev_info * d); -static int ad1816_mixer_set(snddev_info * d, int dev, int value); -static int ad1816_set_recsrc(snddev_info * d, int mask); -static void ad1816_mixer_reset(snddev_info * d); - -/* ad1816 prototypes end */ +static int ad1816_wait_init(struct mss_info *mss, int x); +static u_short ad1816_read(struct mss_info *mss, u_int reg); +static void ad1816_write(struct mss_info *mss, u_int reg, u_short data); + +/* mixer set functions */ +static int ad1816_mixer_set(struct mss_info *mss, int dev, int left, int right); +static int ad1816_set_recsrc(struct mss_info *mss, int mask); #endif -/* - * device descriptors for the boards supported by this module. - */ -snddev_info mss_op_desc = { - "mss", +static int mssmix_init(snd_mixer *m); +static int mssmix_set(snd_mixer *m, unsigned dev, unsigned left, unsigned right); +static int mssmix_setrecsrc(snd_mixer *m, u_int32_t src); +static snd_mixer mss_mixer = { + "MSS mixer", + mssmix_init, + mssmix_set, + mssmix_setrecsrc, +}; + +static int ymmix_init(snd_mixer *m); +static int ymmix_set(snd_mixer *m, unsigned dev, unsigned left, unsigned right); +static int ymmix_setrecsrc(snd_mixer *m, u_int32_t src); +static snd_mixer yamaha_mixer = { + "OPL3-SAx mixer", + ymmix_init, + ymmix_set, + ymmix_setrecsrc, +}; + +static devclass_t pcm_devclass; + +/* channel interface */ +static void *msschan_init(void *devinfo, snd_dbuf *b, pcm_channel *c, int dir); +static int msschan_setdir(void *data, int dir); +static int msschan_setformat(void *data, u_int32_t format); +static int msschan_setspeed(void *data, u_int32_t speed); +static int msschan_setblocksize(void *data, u_int32_t blocksize); +static int msschan_trigger(void *data, int go); +static int msschan_getptr(void *data); +static pcmchan_caps *msschan_getcaps(void *data); + +static pcmchan_caps mss_caps = { + 4000, 48000, + AFMT_STEREO | AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW, + AFMT_STEREO | AFMT_S16_LE +}; + +static pcmchan_caps ad1816_caps = { + 4000, 55200, + AFMT_STEREO | AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW, + AFMT_STEREO | AFMT_S16_LE +}; + +static pcmchan_caps guspnp_caps = { + 4000, 48000, + AFMT_STEREO | AFMT_U8 | AFMT_S16_LE | AFMT_A_LAW, + AFMT_STEREO | AFMT_S16_LE +}; + +static pcmchan_caps opti931_caps = { + 4000, 4800, + AFMT_STEREO | AFMT_U8 | AFMT_S16_LE, + AFMT_STEREO | AFMT_S16_LE +}; + +static pcm_channel mss_chantemplate = { + msschan_init, + msschan_setdir, + msschan_setformat, + msschan_setspeed, + msschan_setblocksize, + msschan_trigger, + msschan_getptr, + msschan_getcaps, +}; + +#define MD_AD1848 0x91 +#define MD_AD1845 0x92 +#define MD_AD1816 0x93 +#define MD_CS4248 0xA1 +#define MD_CS4231 0xA2 +#define MD_CS4231A 0xA3 +#define MD_CS4232 0xA4 +#define MD_CS4232A 0xA5 +#define MD_CS4236 0xA6 +#define MD_CS4237 0xA7 +#define MD_OPTI931 0xB1 +#define MD_OPTI925 0xB2 +#define MD_GUSPNP 0xB8 +#define MD_YM0020 0xC1 +#define MD_VIVO 0xD1 + +#define DV_F_TRUE_MSS 0x00010000 /* mss _with_ base regs */ + +#define FULL_DUPLEX(x) ((x)->bd_flags & BD_F_DUPLEX) + +static int +port_rd(struct resource *port, int off) +{ + if (port) + return bus_space_read_1(rman_get_bustag(port), + rman_get_bushandle(port), + off); + else + return -1; +} + +static void +port_wr(struct resource *port, int off, u_int8_t data) +{ + if (port) + return bus_space_write_1(rman_get_bustag(port), + rman_get_bushandle(port), + off, data); +} + +static int +io_rd(struct mss_info *mss, int reg) +{ + if (mss->bd_flags & BD_F_MSS_OFFSET) reg -= 4; + return port_rd(mss->io_base, reg); +} + +static void +io_wr(struct mss_info *mss, int reg, u_int8_t data) +{ + if (mss->bd_flags & BD_F_MSS_OFFSET) reg -= 4; + return port_wr(mss->io_base, reg, data); +} + +static void +conf_wr(struct mss_info *mss, u_char reg, u_char value) +{ + port_wr(mss->conf_base, 0, reg); + port_wr(mss->conf_base, 1, value); +} - SNDCARD_MSS, - mss_probe, - mss_attach, +static u_char +conf_rd(struct mss_info *mss, u_char reg) +{ + port_wr(mss->conf_base, 0, reg); + return port_rd(mss->conf_base, 1); +} + +#if NPNP > 0 +static void +gus_wr(struct mss_info *mss, u_char reg, u_char value) +{ + port_wr(mss->conf_base, 3, reg); + port_wr(mss->conf_base, 5, value); +} - mss_open, - mss_close, - NULL /* mss_read */, - NULL /* mss_write */, - mss_ioctl, - sndselect /* mss_select */, +static u_char +gus_rd(struct mss_info *mss, u_char reg) +{ + port_wr(mss->conf_base, 3, reg); + return port_rd(mss->conf_base, 5); +} +#endif - mss_intr, - mss_callback , +static void +mss_release_resources(struct mss_info *mss, device_t dev) +{ + if (mss->irq) { + bus_release_resource(dev, SYS_RES_IRQ, mss->irq_rid, + mss->irq); + mss->irq = 0; + } + if (mss->drq1) { + bus_release_resource(dev, SYS_RES_DRQ, mss->drq1_rid, + mss->drq1); + mss->drq1 = 0; + mss->pdma = -1; + } + if (mss->drq2) { + bus_release_resource(dev, SYS_RES_DRQ, mss->drq2_rid, + mss->drq2); + mss->drq2 = 0; + mss->rdma = -1; + } + if (mss->io_base) { + bus_release_resource(dev, SYS_RES_IOPORT, mss->io_rid, + mss->io_base); + mss->io_base = 0; + } + if (mss->conf_base) { + bus_release_resource(dev, SYS_RES_IOPORT, mss->conf_rid, + mss->conf_base); + mss->conf_base = 0; + } + free(mss, M_DEVBUF); +} - DSP_BUFFSIZE, /* bufsize */ +static int +mss_alloc_resources(struct mss_info *mss, device_t dev) +{ + int ok = 1; + if (!mss->io_base) + mss->io_base = bus_alloc_resource(dev, SYS_RES_IOPORT, &mss->io_rid, + 0, ~0, 1, RF_ACTIVE); + if (!mss->irq) + mss->irq = bus_alloc_resource(dev, SYS_RES_IRQ, &mss->irq_rid, + 0, ~0, 1, RF_ACTIVE); + if (!mss->drq1) + mss->drq1 = bus_alloc_resource(dev, SYS_RES_DRQ, &mss->drq1_rid, + 0, ~0, 1, RF_ACTIVE); + if (mss->conf_rid >= 0 && !mss->conf_base) + mss->conf_base = bus_alloc_resource(dev, SYS_RES_IOPORT, &mss->conf_rid, + 0, ~0, 1, RF_ACTIVE); + if (mss->drq2_rid >= 0 && !mss->drq2) + mss->drq2 = bus_alloc_resource(dev, SYS_RES_DRQ, &mss->drq2_rid, + 0, ~0, 1, RF_ACTIVE); + + if (!mss->io_base || !mss->drq1 || !mss->irq) ok = 0; + if (mss->conf_rid >= 0 && !mss->conf_base) ok = 0; + if (mss->drq2_rid >= 0 && !mss->drq2) ok = 0; + + if (ok) { + mss->pdma = rman_get_start(mss->drq1); + isa_dma_acquire(mss->pdma); + isa_dmainit(mss->pdma, DSP_BUFFSIZE); + mss->bd_flags &= ~BD_F_DUPLEX; + if (mss->drq2) { + mss->rdma = rman_get_start(mss->drq2); + isa_dma_acquire(mss->rdma); + isa_dmainit(mss->rdma, DSP_BUFFSIZE); + mss->bd_flags |= BD_F_DUPLEX; + } else mss->rdma = mss->pdma; + } + return ok; +} - AFMT_STEREO | - AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW, /* audio formats */ - /* - * the enhanced boards also have AFMT_IMA_ADPCM | AFMT_S16_BE - * but we do not use these modes. - */ -} ; +static int +mss_init(struct mss_info *mss, device_t dev) +{ + mss->bd_flags |= BD_F_MCE_BIT; + switch(mss->bd_id) { +#if NPNP > 0 + case MD_OPTI931: + conf_wr(mss, 4, 0xd6); /* fifo empty, OPL3, audio enable, SB3.2 */ + ad_write(mss, 10, 2); /* enable interrupts */ + conf_wr(mss, 6, 2); /* MCIR6: mss enable, sb disable */ + conf_wr(mss, 5, 0x28); /* MCIR5: codec in exp. mode,fifo */ + break; + + case MD_GUSPNP: + { + struct resource *alt; + int rid, tmp; + + gus_wr(mss, 0x4c /* _URSTI */, 0);/* Pull reset */ + DELAY(1000 * 30); + /* release reset and enable DAC */ + gus_wr(mss, 0x4c /* _URSTI */, 3); + DELAY(1000 * 30); + /* end of reset */ + + rid = 0; + alt = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, + 0, ~0, 1, RF_ACTIVE); + port_wr(alt, 0, 0xC); /* enable int and dma */ + bus_release_resource(dev, SYS_RES_IOPORT, rid, alt); + + /* + * unmute left & right line. Need to go in mode3, unmute, + * and back to mode 2 + */ + tmp = ad_read(mss, 0x0c); + ad_write(mss, 0x0c, 0x6c); /* special value to enter mode 3 */ + ad_write(mss, 0x19, 0); /* unmute left */ + ad_write(mss, 0x1b, 0); /* unmute right */ + ad_write(mss, 0x0c, tmp); /* restore old mode */ + + /* send codec interrupts on irq1 and only use that one */ + gus_wr(mss, 0x5a, 0x4f); + + /* enable access to hidden regs */ + tmp = gus_rd(mss, 0x5b /* IVERI */); + gus_wr(mss, 0x5b, tmp | 1); + BVDDB(printf("GUS: silicon rev %c\n", 'A' + ((tmp & 0xf) >> 4))); + break; + } + + case MD_AD1816: + ad1816_write(mss, 1, 0x2); /* disable interrupts */ + ad1816_write(mss, 32, 0x90F0); /* SoundSys Mode, split fmt */ + + ad1816_write(mss, 5, 0x8080); /* FM volume mute */ + ad1816_write(mss, 6, 0x8080); /* I2S1 volume mute */ + ad1816_write(mss, 7, 0x8080); /* I2S0 volume mute */ + ad1816_write(mss, 17, 0x8888); /* VID Volume mute */ + ad1816_write(mss, 20, 0x5050); /* recsrc mic, agc off */ + /* adc gain is set to 0 */ + break; +#endif + case MD_YM0020: + { + u_char r6, r9; + conf_wr(mss, OPL3SAx_DMACONF, 0xa9); /* dma-b rec, dma-a play */ + r6 = conf_rd(mss, OPL3SAx_DMACONF); + r9 = conf_rd(mss, OPL3SAx_MISC); /* version */ + BVDDB(printf("Yamaha: ver 0x%x DMA config 0x%x\n", r6, r9);) + /* yamaha - set volume to max */ + conf_wr(mss, OPL3SAx_VOLUMEL, 0); + conf_wr(mss, OPL3SAx_VOLUMER, 0); + conf_wr(mss, OPL3SAx_DMACONF, FULL_DUPLEX(mss)? 0xa9 : 0x8b); + break; + } + } + if (FULL_DUPLEX(mss) && mss->bd_id != MD_OPTI931) + ad_write(mss, 12, ad_read(mss, 12) | 0x40); /* mode 2 */ + ad_write(mss, 9, FULL_DUPLEX(mss)? 0 : 4); + ad_write(mss, 10, 2); /* int enable */ + io_wr(mss, MSS_STATUS, 0); /* Clear interrupt status */ + /* the following seem required on the CS4232 */ + ad_unmute(mss); + return 0; +} /* * mss_probe() is the probe routine. Note, it is not necessary to @@ -146,410 +432,471 @@ snddev_info mss_op_desc = { */ static int -mss_probe(struct isa_device *dev) -{ - u_char tmp; - int irq = ffs(dev->id_irq) - 1; - - bzero(&pcm_info[dev->id_unit], sizeof(pcm_info[dev->id_unit]) ); - if (dev->id_iobase == -1) { - dev->id_iobase = 0x530; - BVDDB(printf("mss_probe: no address supplied, try default 0x%x\n", - dev->id_iobase)); - } - if (snd_conflict(dev->id_iobase)) - return 0 ; - - if ( !(dev->id_flags & DV_F_TRUE_MSS) ) /* Has no IRQ/DMA registers */ - goto mss_probe_end; - - /* - * Check if the IO port returns valid signature. The original MS - * Sound system returns 0x04 while some cards - * (AudioTriX Pro for example) return 0x00 or 0x0f. - */ - - tmp = inb(dev->id_iobase + 3); - if (tmp == 0xff) { /* Bus float */ - BVDDB(printf("I/O address inactive (%x), try pseudo_mss\n", tmp)); - dev->id_flags &= ~DV_F_TRUE_MSS ; - goto mss_probe_end; - } - tmp &= 0x3f ; - if (tmp != 0x04 && tmp != 0x0f && tmp != 0x00) { - BVDDB(printf("No MSS signature detected on port 0x%x (0x%x)\n", - dev->id_iobase, inb(dev->id_iobase + 3))); - return 0; - } - if (irq > 11) { - printf("MSS: Bad IRQ %d\n", irq); - return 0; - } - if (dev->id_drq != 0 && dev->id_drq != 1 && dev->id_drq != 3) { - printf("MSS: Bad DMA %d\n", dev->id_drq); - return 0; - } - if (inb(dev->id_iobase + 3) & 0x80) { - /* 8-bit board: only drq1/3 and irq7/9 */ - if (dev->id_drq == 0) { - printf("MSS: Can't use DMA0 with a 8 bit card/slot\n"); - return 0; - } - if (irq != 7 && irq != 9) { - printf("MSS: Can't use IRQ%d with a 8 bit card/slot\n", irq); - return 0; - } - } -mss_probe_end: - return mss_detect(dev) ? 8 : 0 ; /* mss uses 8 regs */ -} - -#if NPNP > 0 -static int -ad1816_attach(struct isa_device *dev) +mss_probe(device_t dev) { - snddev_info *d = &(pcm_info[dev->id_unit]); - - dev->id_alive = 16; /* number of io ports */ - - if (FULL_DUPLEX(d)) - d->audio_fmt |= AFMT_FULLDUPLEX; - - ad1816_write(d, 1, 0x2);/* disable interrupts */ - ad1816_write(d, 32, 0x90F0); /* SoundSystem Mode, split format */ - - ad1816_write(d, 5, 0x8080); /* FM volume mute */ - ad1816_write(d, 6, 0x8080); /* I2S1 volume mute */ - ad1816_write(d, 7, 0x8080); /* I2S0 volume mute */ - ad1816_write(d, 17, 0x8888); /* VID Volume mute */ - ad1816_write(d, 20, 0x5050); /* Source select Mic & auto gain ctrl - * off */ - /* adc gain is set to 0 */ - ad1816_reinit(d); - ad1816_mixer_reset(d); - return 0 ; + u_char tmp, tmpx; + int flags, irq, drq, result = ENXIO, setres = 0; + struct mss_info *mss; + + if (isa_get_vendorid(dev)) return ENXIO; /* not yet */ + + mss = (struct mss_info *)malloc(sizeof *mss, M_DEVBUF, M_NOWAIT); + if (!mss) return ENXIO; + bzero(mss, sizeof *mss); + + mss->io_rid = 0; + mss->conf_rid = -1; + mss->irq_rid = 0; + mss->drq1_rid = 0; + mss->drq2_rid = -1; + mss->io_base = bus_alloc_resource(dev, SYS_RES_IOPORT, &mss->io_rid, + 0, ~0, 8, RF_ACTIVE); + if (!mss->io_base) { + BVDDB(printf("mss_probe: no address given, try 0x%x\n", 0x530)); + mss->io_rid = 0; + /* XXX verify this */ + setres = 1; + ISA_SET_RESOURCE(device_get_parent(dev), dev, SYS_RES_IOPORT, mss->io_rid, + 0x530, 8); + mss->io_base = bus_alloc_resource(dev, SYS_RES_IOPORT, &mss->io_rid, + 0, ~0, 8, RF_ACTIVE); + } + if (!mss->io_base) goto no; + + /* got irq/dma regs? */ + flags = isa_get_flags(dev); + irq = isa_get_irq(dev); + drq = isa_get_drq(dev); + + if (!(isa_get_flags(dev) & DV_F_TRUE_MSS)) goto mss_probe_end; + + /* + * Check if the IO port returns valid signature. The original MS + * Sound system returns 0x04 while some cards + * (AudioTriX Pro for example) return 0x00 or 0x0f. + */ + + device_set_desc(dev, "MSS"); + tmpx = tmp = io_rd(mss, 3); + if (tmp == 0xff) { /* Bus float */ + BVDDB(printf("I/O addr inactive (%x), try pseudo_mss\n", tmp)); + isa_set_flags(dev, flags & ~DV_F_TRUE_MSS); + goto mss_probe_end; + } + tmp &= 0x3f; + if (!(tmp == 0x04 || tmp == 0x0f || tmp == 0x00)) { + BVDDB(printf("No MSS signature detected on port 0x%lx (0x%x)\n", + rman_get_start(mss->io_base), tmpx)); + goto no; + } + if (irq > 11) { + printf("MSS: Bad IRQ %d\n", irq); + goto no; + } + if (!(drq == 0 || drq == 1 || drq == 3)) { + printf("MSS: Bad DMA %d\n", drq); + goto no; + } + if (tmpx & 0x80) { + /* 8-bit board: only drq1/3 and irq7/9 */ + if (drq == 0) { + printf("MSS: Can't use DMA0 with a 8 bit card/slot\n"); + goto no; + } + if (!(irq == 7 || irq == 9)) { + printf("MSS: Can't use IRQ%d with a 8 bit card/slot\n", irq); + goto no; + } + } + mss_probe_end: + result = mss_detect(dev, mss); + no: + if (setres) ISA_DELETE_RESOURCE(device_get_parent(dev), dev, + SYS_RES_IOPORT, mss->io_rid); /* XXX ? */ + mss_release_resources(mss, dev); + return result; } -#endif /* NPNP */ - -/* - * the address passed as io_base for mss_attach is also the old - * MSS base address (e.g. 0x530). The codec is four locations ahead. - * Note that the attach routine for PnP devices might support - * device-specific initializations. - */ static int -mss_attach(struct isa_device *dev) +mss_detect(device_t dev, struct mss_info *mss) { - snddev_info *d = &(pcm_info[dev->id_unit]); - - printf("mss_attach <%s>%d at 0x%x irq %d dma %d:%d flags 0x%x\n", - d->name, dev->id_unit, - d->io_base, d->irq, d->dbuf_out.chan, d->dbuf_in.chan, dev->id_flags); + int i; + u_char tmp, tmp1, tmp2; + char *name, *yamaha; -#if NPNP > 0 - if (d->bd_id == MD_AD1816) - return ad1816_attach(dev); + if (mss->bd_id != 0) { + device_printf(dev, "presel bd_id 0x%04x -- %s\n", mss->bd_id, + device_get_desc(dev)); + return 0; + } + + name = "AD1848"; + mss->bd_id = MD_AD1848; /* AD1848 or CS4248 */ + + /* + * Check that the I/O address is in use. + * + * bit 7 of the base I/O port is known to be 0 after the chip has + * performed its power on initialization. Just assume this has + * happened before the OS is starting. + * + * If the I/O address is unused, it typically returns 0xff. + */ + + for (i = 0; i < 10; i++) + if ((tmp = io_rd(mss, MSS_INDEX)) & MSS_IDXBUSY) DELAY(10000); + else break; + + if (i >= 10) { /* Not a AD1848 */ + BVDDB(printf("mss_detect, busy still set (0x%02x)\n", tmp)); + goto no; + } + /* + * Test if it's possible to change contents of the indirect + * registers. Registers 0 and 1 are ADC volume registers. The bit + * 0x10 is read only so try to avoid using it. + */ + + ad_write(mss, 0, 0xaa); + ad_write(mss, 1, 0x45);/* 0x55 with bit 0x10 clear */ + tmp1 = ad_read(mss, 0); + tmp2 = ad_read(mss, 1); + if (tmp1 != 0xaa || tmp2 != 0x45) { + BVDDB(printf("mss_detect error - IREG (%x/%x)\n", tmp1, tmp2)); + goto no; + } + + ad_write(mss, 0, 0x45); + ad_write(mss, 1, 0xaa); + tmp1 = ad_read(mss, 0); + tmp2 = ad_read(mss, 1); + if (tmp1 != 0x45 || tmp2 != 0xaa) { + BVDDB(printf("mss_detect error - IREG2 (%x/%x)\n", tmp1, tmp2)); + goto no; + } + + /* + * The indirect register I12 has some read only bits. Lets try to + * change them. + */ + + tmp = ad_read(mss, 12); + ad_write(mss, 12, (~tmp) & 0x0f); + tmp1 = ad_read(mss, 12); + + if ((tmp & 0x0f) != (tmp1 & 0x0f)) { + BVDDB(printf("mss_detect - I12 (0x%02x was 0x%02x)\n", tmp1, tmp)); + goto no; + } + + /* + * NOTE! Last 4 bits of the reg I12 tell the chip revision. + * 0x01=RevB + * 0x0A=RevC. also CS4231/CS4231A and OPTi931 + */ + + BVDDB(printf("mss_detect - chip revision 0x%02x\n", tmp & 0x0f);) + + /* + * The original AD1848/CS4248 has just 16 indirect registers. This + * means that I0 and I16 should return the same value (etc.). Ensure + * that the Mode2 enable bit of I12 is 0. Otherwise this test fails + * with new parts. + */ + + ad_write(mss, 12, 0); /* Mode2=disabled */ +#if 0 + for (i = 0; i < 16; i++) { + if ((tmp1 = ad_read(mss, i)) != (tmp2 = ad_read(mss, i + 16))) { + BVDDB(printf("mss_detect warning - I%d: 0x%02x/0x%02x\n", + i, tmp1, tmp2)); + /* + * note - this seems to fail on the 4232 on I11. So we just break + * rather than fail. (which makes this test pointless - cg) + */ + break; /* return 0; */ + } + } #endif - dev->id_alive = 8 ; /* number of io ports */ - /* should be already set but just in case... */ - - if ( dev->id_flags & DV_F_TRUE_MSS ) { - /* has IRQ/DMA registers, set IRQ and DMA addr */ - static char interrupt_bits[12] = { - -1, -1, -1, -1, -1, 0x28, -1, 0x08, -1, 0x10, 0x18, 0x20 - }; - static char dma_bits[4] = { 1, 2, 0, 3 }; - char bits ; - - if (d->irq == -1 || (bits = interrupt_bits[d->irq]) == -1) { - dev->id_irq = 0 ; /* makk invalid irq */ - return 0 ; + /* + * Try to switch the chip to mode2 (CS4231) by setting the MODE2 bit + * (0x40). The bit 0x80 is always 1 in CS4248 and CS4231. + * + * On the OPTi931, however, I12 is readonly and only contains the + * chip revision ID (as in the CS4231A). The upper bits return 0. + */ + + ad_write(mss, 12, 0x40); /* Set mode2, clear 0x80 */ + + tmp1 = ad_read(mss, 12); + if (tmp1 & 0x80) name = "CS4248"; /* Our best knowledge just now */ + if ((tmp1 & 0xf0) == 0x00) { + BVDDB(printf("this should be an OPTi931\n");) + } else if ((tmp1 & 0xc0) != 0xC0) goto gotit; + /* + * The 4231 has bit7=1 always, and bit6 we just set to 1. + * We want to check that this is really a CS4231 + * Verify that setting I0 doesn't change I16. + */ + ad_write(mss, 16, 0); /* Set I16 to known value */ + ad_write(mss, 0, 0x45); + if ((tmp1 = ad_read(mss, 16)) == 0x45) goto gotit; + + ad_write(mss, 0, 0xaa); + if ((tmp1 = ad_read(mss, 16)) == 0xaa) { /* Rotten bits? */ + BVDDB(printf("mss_detect error - step H(%x)\n", tmp1)); + goto no; } + /* Verify that some bits of I25 are read only. */ + tmp1 = ad_read(mss, 25); /* Original bits */ + ad_write(mss, 25, ~tmp1); /* Invert all bits */ + if ((ad_read(mss, 25) & 0xe7) == (tmp1 & 0xe7)) { + int id; - outb(dev->id_iobase, bits | 0x40); /* config port */ - if ((inb(dev->id_iobase + 3) & 0x40) == 0) /* version port */ - printf("[IRQ Conflict?]"); - - /* Write IRQ+DMA setup */ - if ( ! FULL_DUPLEX(d) ) /* single chan dma */ - outb(dev->id_iobase, bits | dma_bits[d->dbuf_out.chan]); - else { - if (d->dbuf_out.chan == 0 && d->dbuf_in.chan == 1) - bits |= 5 ; - else if (d->dbuf_out.chan == 1 && d->dbuf_in.chan == 0) - bits |= 6 ; - else if (d->dbuf_out.chan == 3 && d->dbuf_in.chan == 0) - bits |= 7 ; - else { - printf("invalid dual dma config %d:%d\n", - d->dbuf_out.chan, d->dbuf_in.chan); - dev->id_irq = 0 ; - dev->id_alive = 0 ; /* this makes attach fail. */ - return 0 ; - } - outb(dev->id_iobase, bits ); - } - } - if (1) { /* machine-specific code for the Toshiba Libretto */ - u_char r6, r9; - outb( 0x370, 6 /* dma config */ ); - outb( 0x371, 0xa9 /* config: DMA-B for rec, DMA-A for play */); - r6 = inb( 0x371 /* read */ ); - outb( 0x370, 0xa /* version */ ); - r9 = inb( 0x371 /* read */ ); - DEB(printf("Yamaha: ver 0x%x DMA config 0x%x\n", r6, r9);) - /* - * yamaha - set volume to max - */ - outb( 0x370, 7 /* volume left */ ); - outb( 0x371, 0 /* max level */ ); - outb( 0x370, 8 /* volume right */ ); - outb( 0x371, 0 /* max level */ ); - } - if ( FULL_DUPLEX(d) ) - d->audio_fmt |= AFMT_FULLDUPLEX ; - if (d->bd_id == MD_YM0020) { - DDB(printf("setting up yamaha registers\n")); - outb(0x370, 6 /* dma config */ ) ; - if (FULL_DUPLEX(d)) - outb(0x371, 0xa9 ); /* use both dma chans */ - else - outb(0x371, 0x8b ); /* use low dma chan */ - } - mss_reinit(d); - ad1848_mixer_reset(d); - return 0; -} + /* It's at least CS4231 */ + name = "CS4231"; + mss->bd_id = MD_CS4231; -int -mss_open(dev_t i_dev, int flags, int mode, struct proc * p) -{ - int unit; - int dev; - snddev_info *d; - u_long s; - - dev = minor(i_dev); - unit = dev >> 4 ; - dev &= 0xf ; - d = &pcm_info[unit] ; - - s = spltty(); - /* - * This was meant to support up to 2 open descriptors for the - * some device, and check proper device usage on open. - * Unfortunately, the kernel will trap all close() calls but - * the last one, with the consequence that we cannot really - * keep track of which channels are busy. - * So, the correct tests cannot be done :( and we must rely - * on the locks on concurrent operations of the same type and - * on some approximate tests... - */ - - if (dev == SND_DEV_AUDIO) - d->flags |= SND_F_BUSY_AUDIO ; - else if (dev == SND_DEV_DSP) - d->flags |= SND_F_BUSY_DSP ; - else if (dev == SND_DEV_DSP16) - d->flags |= SND_F_BUSY_DSP16 ; - if ( d->flags & SND_F_BUSY ) - splx(s); /* device was already set, no need to reinit */ - else { - /* - * device was idle. Do the necessary initialization, - * but no need keep interrupts blocked. - * will not get them - */ + /* + * It could be an AD1845 or CS4231A as well. + * CS4231 and AD1845 report the same revision info in I25 + * while the CS4231A reports different. + */ - splx(s); - d->play_speed = d->rec_speed = DSP_DEFAULT_SPEED ; - d->flags |= SND_F_BUSY ; + id = ad_read(mss, 25) & 0xe7; + /* + * b7-b5 = version number; + * 100 : all CS4231 + * 101 : CS4231A + * + * b2-b0 = chip id; + */ + switch (id) { - d->wsel.si_pid = 0; - d->wsel.si_flags = 0; + case 0xa0: + name = "CS4231A"; + mss->bd_id = MD_CS4231A; + break; - d->rsel.si_pid = 0; - d->rsel.si_flags = 0; + case 0xa2: + name = "CS4232"; + mss->bd_id = MD_CS4232; + break; - d->dbuf_out.total = d->dbuf_out.prev_total = 0 ; - d->dbuf_in.total = d->dbuf_in.prev_total = 0 ; + case 0xb2: + /* strange: the 4231 data sheet says b4-b3 are XX + * so this should be the same as 0xa2 + */ + name = "CS4232A"; + mss->bd_id = MD_CS4232A; + break; - if (flags & O_NONBLOCK) - d->flags |= SND_F_NBIO ; + case 0x80: + /* + * It must be a CS4231 or AD1845. The register I23 + * of CS4231 is undefined and it appears to be read + * only. AD1845 uses I23 for setting sample rate. + * Assume the chip is AD1845 if I23 is changeable. + */ + + tmp = ad_read(mss, 23); + + ad_write(mss, 23, ~tmp); + if (ad_read(mss, 23) != tmp) { /* AD1845 ? */ + name = "AD1845"; + mss->bd_id = MD_AD1845; + } + ad_write(mss, 23, tmp); /* Restore */ + + yamaha = ymf_test(dev, mss); + if (yamaha) { + mss->bd_id = MD_YM0020; + name = yamaha; + } + break; - switch (dev) { - default : - case SND_DEV_AUDIO : - d->play_fmt = d->rec_fmt = AFMT_MU_LAW ; - break ; - case SND_DEV_DSP : - d->play_fmt = d->rec_fmt = AFMT_U8 ; - break ; - case SND_DEV_DSP16 : - d->play_fmt = d->rec_fmt = AFMT_S16_LE ; - break; + case 0x83: /* CS4236 */ + case 0x03: /* CS4236 on Intel PR440FX motherboard XXX */ + name = "CS4236"; + mss->bd_id = MD_CS4236; + break; + + default: /* Assume CS4231 */ + BVDDB(printf("unknown id 0x%02x, assuming CS4231\n", id);) + mss->bd_id = MD_CS4231; + } } - ask_init(d); /* and reset buffers... */ - } - return 0 ; + ad_write(mss, 25, tmp1); /* Restore bits */ +gotit: + BVDDB(printf("mss_detect() - Detected %s\n", name)); + device_set_desc(dev, name); + isa_set_flags(dev, ((isa_get_flags(dev) & ~DV_F_DEV_MASK) | + ((mss->bd_id << DV_F_DEV_SHIFT) & DV_F_DEV_MASK))); + return 0; +no: + return ENXIO; } -static int -mss_close(dev_t i_dev, int flags, int mode, struct proc * p) +static char * +ymf_test(device_t dev, struct mss_info *mss) { - int unit; - int dev; - snddev_info *d; - u_long s; - - dev = minor(i_dev); - unit = dev >> 4 ; - dev &= 0xf; - d = &pcm_info[unit] ; - - /* - * We will only get a single close call when the last reference - * to the device is gone. But we must handle ourselves references - * through different devices. - */ - - s = spltty(); - - if (dev == SND_DEV_AUDIO) - d->flags &= ~SND_F_BUSY_AUDIO ; - else if (dev == SND_DEV_DSP) - d->flags &= ~SND_F_BUSY_DSP ; - else if (dev == SND_DEV_DSP16) - d->flags &= ~SND_F_BUSY_DSP16 ; - if ( d->flags & SND_F_BUSY_ANY ) /* still some device open */ - splx(s); - else { /* last one */ - d->flags |= SND_F_CLOSING ; - splx(s); /* is this ok here ? */ - snd_flush(d); - /* Clear interrupt status */ - if ( d->bd_id == MD_AD1816 ) - outb(ad1816_int(d), 0); - else - outb(io_Status(d), 0); - d->flags = 0 ; - } - return 0 ; + static int ports[] = {0x370, 0x310, 0x538}; + int p, i, j, version; + static char *chipset[] = { + NULL, /* 0 */ + "OPL3-SA2 (YMF711)", /* 1 */ + "OPL3-SA3 (YMF715)", /* 2 */ + "OPL3-SA3 (YMF715)", /* 3 */ + "OPL3-SAx (YMF719)", /* 4 */ + "OPL3-SAx (YMF719)", /* 5 */ + "OPL3-SAx (YMF719)", /* 6 */ + "OPL3-SAx (YMF719)", /* 7 */ + }; + + for (p = 0; p < 3; p++) { + mss->conf_rid = 1; + mss->conf_base = bus_alloc_resource(dev, + SYS_RES_IOPORT, + &mss->conf_rid, + ports[p], ports[p] + 1, 2, + RF_ACTIVE); + if (!mss->conf_base) return 0; + + /* Test the index port of the config registers */ + i = port_rd(mss->conf_base, 0); + port_wr(mss->conf_base, 0, OPL3SAx_DMACONF); + j = (port_rd(mss->conf_base, 0) == OPL3SAx_DMACONF)? 1 : 0; + port_wr(mss->conf_base, 0, i); + if (!j) { + bus_release_resource(dev, SYS_RES_IOPORT, + mss->conf_rid, mss->conf_base); + mss->conf_base = 0; + continue; + } + version = conf_rd(mss, OPL3SAx_MISC) & 0x07; + return chipset[version]; + } + return NULL; } static int -mss_ioctl(dev_t i_dev, u_long cmd, caddr_t arg, int mode, struct proc * p) +mss_doattach(device_t dev, struct mss_info *mss) { - snddev_info *d; - int unit; - int dev; - - dev = minor(i_dev); - unit = dev >> 4 ; - d = &pcm_info[unit] ; - /* - * handle mixer calls first. Reads are in the default handler, - * so do not bother about them. - */ - if ( (cmd & MIXER_WRITE(0)) == MIXER_WRITE(0) ) { - cmd &= 0xff ; - if (cmd == SOUND_MIXER_RECSRC) - return mss_set_recsrc(d, *(int *)arg) ; - else - return mss_mixer_set(d, cmd, *(int *)arg) ; - } - - return ENOSYS ; /* fallback to the default ioctl handler */ + snddev_info *d = device_get_softc(dev); + void *ih; + int flags = isa_get_flags(dev); + char status[SND_STATUSLEN]; + + if (!mss_alloc_resources(mss, dev)) goto no; + mss_init(mss, dev); + if (flags & DV_F_TRUE_MSS) { + /* has IRQ/DMA registers, set IRQ and DMA addr */ + static char interrupt_bits[12] = + {-1, -1, -1, -1, -1, 0x28, -1, 0x08, -1, 0x10, 0x18, 0x20}; + static char pdma_bits[4] = {1, 2, -1, 3}; + static char valid_rdma[4] = {1, 0, -1, 0}; + char bits; + + if (!mss->irq || (bits = interrupt_bits[rman_get_start(mss->irq)]) == -1) + goto no; + io_wr(mss, 0, bits | 0x40); /* config port */ + if ((io_rd(mss, 3) & 0x40) == 0) device_printf(dev, "IRQ Conflict?\n"); + /* Write IRQ+DMA setup */ + if (pdma_bits[mss->pdma] == -1) goto no; + bits |= pdma_bits[mss->pdma]; + if (mss->pdma != mss->rdma) { + if (mss->rdma == valid_rdma[mss->pdma]) bits |= 4; + else { + printf("invalid dual dma config %d:%d\n", + mss->pdma, mss->rdma); + goto no; + } + } + io_wr(mss, 0, bits); + printf("drq/irq conf %x\n", io_rd(mss, 0)); + } + mixer_init(d, (mss->bd_id == MD_YM0020)? &yamaha_mixer : &mss_mixer, mss); + switch (mss->bd_id) { + #if NPNP > 0 + case MD_AD1816: + bus_setup_intr(dev, mss->irq, INTR_TYPE_TTY, ad1816_intr, mss, &ih); + break; + + case MD_OPTI931: + bus_setup_intr(dev, mss->irq, INTR_TYPE_TTY, opti931_intr, mss, &ih); + break; + #endif + default: + bus_setup_intr(dev, mss->irq, INTR_TYPE_TTY, mss_intr, mss, &ih); + } + if (mss->pdma == mss->rdma) + pcm_setflags(dev, pcm_getflags(dev) | SD_F_SIMPLEX); + if (bus_dma_tag_create(/*parent*/NULL, /*alignment*/2, /*boundary*/0, + /*lowaddr*/BUS_SPACE_MAXADDR_24BIT, + /*highaddr*/BUS_SPACE_MAXADDR, + /*filter*/NULL, /*filterarg*/NULL, + /*maxsize*/DSP_BUFFSIZE, /*nsegments*/1, + /*maxsegz*/0x3ffff, + /*flags*/0, &mss->parent_dmat) != 0) { + device_printf(dev, "unable to create dma tag\n"); + goto no; + } + snprintf(status, SND_STATUSLEN, "at io 0x%lx irq %ld drq %d", + rman_get_start(mss->io_base), rman_get_start(mss->irq), mss->pdma); + if (mss->pdma != mss->rdma) snprintf(status + strlen(status), + SND_STATUSLEN - strlen(status), ":%d", mss->rdma); + + if (pcm_register(dev, mss, 1, 1)) goto no; + pcm_addchan(dev, PCMDIR_REC, &mss_chantemplate, mss); + pcm_addchan(dev, PCMDIR_PLAY, &mss_chantemplate, mss); + pcm_setstatus(dev, status); + + return 0; +no: + mss_release_resources(mss, dev); + return ENXIO; } - -/* - * the callback routine to handle all dma ops etc. - * With the exception of INIT, all other callbacks are invoked - * with interrupts disabled. - */ - static int -mss_callback(snddev_info *d, int reason) +mss_attach(device_t dev) { - u_char m; - int retry, wr, cnt; - - DEB(printf("-- mss_callback reason 0x%03x\n", reason)); - wr = reason & SND_CB_WR ; - reason &= SND_CB_REASON_MASK ; - switch (reason) { - case SND_CB_INIT : /* called with int enabled and no pending I/O */ - /* - * perform all necessary initializations for i/o - */ - d->rec_fmt = d->play_fmt ; /* no split format on the MSS */ - snd_set_blocksize(d); - mss_reinit(d); - reset_dbuf(& (d->dbuf_in), SND_CHAN_RD ); - reset_dbuf(& (d->dbuf_out), SND_CHAN_WR ); - return 1 ; - break ; - - case SND_CB_START : - cnt = wr ? d->dbuf_out.dl : d->dbuf_in.dl ; - if (d->play_fmt == AFMT_S16_LE) - cnt /= 2; - if (d->flags & SND_F_STEREO) - cnt /= 2; - cnt-- ; - - DEB(printf("-- (re)start cnt %d\n", cnt)); - m = ad_read(d,9) ; - DEB( if (m & 4) printf("OUCH! reg 9 0x%02x\n", m); ); - m |= wr ? I9_PEN : I9_CEN ; /* enable DMA */ - /* - * on the OPTi931 the enable bit seems hard to set... - */ - for (retry = 10; retry; retry--) { - ad_write(d, 9, m ); - if (ad_read(d,9) ==m) break; - } - if (retry == 0) - printf("start dma, failed to set bit 0x%02x 0x%02x\n", - m, ad_read(d, 9) ) ; - if (wr || ! FULL_DUPLEX(d) ) - ad_write_cnt(d, 14, cnt); - else - ad_write_cnt(d, 30, cnt); + struct mss_info *mss; + int flags = isa_get_flags(dev); + + mss = (struct mss_info *)malloc(sizeof *mss, M_DEVBUF, M_NOWAIT); + if (!mss) return ENXIO; + bzero(mss, sizeof *mss); + + mss->io_rid = 0; + mss->conf_rid = -1; + mss->irq_rid = 0; + mss->drq1_rid = 0; + mss->drq2_rid = -1; + if (flags & DV_F_DUAL_DMA) { + ISA_SET_RESOURCE(device_get_parent(dev), dev, SYS_RES_DRQ, 1, + flags & DV_F_DRQ_MASK, 1); + mss->drq2_rid = 1; + } + mss->bd_id = (isa_get_flags(dev) & DV_F_DEV_MASK) >> DV_F_DEV_SHIFT; + if (mss->bd_id == MD_YM0020) ymf_test(dev, mss); + return mss_doattach(dev, mss); +} - break ; +static device_method_t mss_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, mss_probe), + DEVMETHOD(device_attach, mss_attach), - case SND_CB_STOP : - case SND_CB_ABORT : /* XXX check this... */ - m = ad_read(d,9) ; - m &= wr ? ~I9_PEN : ~I9_CEN ; /* Stop DMA */ - /* - * on the OPTi931 the enable bit seems hard to set... - */ - for (retry = 10; retry ; retry-- ) { - ad_write(d, 9, m ); - if (ad_read(d,9) ==m) break; - } - if (retry == 0) - printf("start dma, failed to clear bit 0x%02x 0x%02x\n", - m, ad_read(d, 9) ) ; -#if 1 - /* - * try to disable DMA by clearing count registers. Not sure it - * is needed, and it might cause false interrupts when the - * DMA is re-enabled later. - */ - if (wr || ! FULL_DUPLEX(d) ) - ad_write_cnt(d, 14, 0); - else - ad_write_cnt(d, 30, 0); - break; -#endif - } - return 0 ; -} + { 0, 0 } +}; + +static driver_t mss_driver = { + "pcm", + mss_methods, + sizeof(snddev_info), +}; + +DRIVER_MODULE(mss, isa, mss_driver, pcm_devclass, 0, 0); /* * main irq handler for the CS423x. The OPTi931 code is @@ -567,492 +914,274 @@ mss_callback(snddev_info *d, int reason) */ static void -mss_intr(int unit) -{ - snddev_info *d = &pcm_info[unit]; - u_char c, served = 0; - int i; - - DEB(printf("mss_intr\n")); - ad_read(d, 11); /* fake read of status bits */ - - /* - * loop until there are interrupts, but no more than 10 times. - */ - for (i=10 ; i && inb(io_Status(d)) & 1 ; i-- ) { - /* get exact reason for full-duplex boards */ - c = FULL_DUPLEX(d) ? ad_read(d, 24) : 0x30 ; - c &= ~served ; - if ( d->dbuf_out.dl && (c & 0x10) ) { - served |= 0x10 ; - dsp_wrintr(d); - } - if ( d->dbuf_in.dl && (c & 0x20) ) { - served |= 0x20 ; - dsp_rdintr(d); - } - /* - * now ack the interrupt - */ - if ( FULL_DUPLEX(d) ) - ad_write(d, 24, ~c); /* ack selectively */ - else - outb(io_Status(d), 0); /* Clear interrupt status */ - } - if (served == 0) { - printf("How strange... mss_intr with no reason!\n"); - /* - * this should not happen... I have no idea what to do now. - * maybe should do a sanity check and restart dmas ? - */ - outb(io_Status(d), 0); /* Clear interrupt status */ - } -} - -/* - * the opti931 seems to miss interrupts when working in full - * duplex, so we try some heuristics to catch them. - */ -static void -opti931_intr(int unit) -{ - snddev_info *d = &pcm_info[unit]; - u_char masked=0, i11, mc11, c=0; - u_char reason; /* b0 = playback, b1 = capture, b2 = timer */ - int loops = 10; - -#if 0 - reason = inb(io_Status(d)); - if ( ! (reason & 1) ) {/* no int, maybe a shared line ? */ - printf("opti931_intr: flag 0, mcir11 0x%02x\n", ad_read(d,11)); - return; - } -#endif - i11 = ad_read(d, 11); /* XXX what's for ? */ -again: - - c=mc11 = FULL_DUPLEX(d) ? opti_read(d->conf_base, 11) : 0xc ; - mc11 &= 0x0c ; - if (c & 0x10) { - DEB(printf("Warning: CD interrupt\n");) - mc11 |= 0x10 ; - } - if (c & 0x20) { - DEB(printf("Warning: MPU interrupt\n");) - mc11 |= 0x20 ; - } - if (mc11 & masked) - printf("irq reset failed, mc11 0x%02x, masked 0x%02x\n", mc11, masked); - masked |= mc11 ; - /* - * the nice OPTi931 sets the IRQ line before setting the bits in - * mc11. So, on some occasions I have to retry (max 10 times). - */ - if ( mc11 == 0 ) { /* perhaps can return ... */ - reason = inb(io_Status(d)); - if (reason & 1) { - DEB(printf("one more try...\n");) - if (--loops) - goto again; - else - DDB(printf("opti_intr: irq but mc11 not set!...\n");) - } - if (loops==10) - printf("ouch, intr but nothing in mcir11 0x%02x\n", mc11); - return; - } - - if ( d->dbuf_in.dl && (mc11 & 8) ) { - dsp_rdintr(d); - } - if ( d->dbuf_out.dl && (mc11 & 4) ) { - dsp_wrintr(d); - } - opti_write(d->conf_base, 11, ~mc11); /* ack */ - if (--loops) - goto again; - DEB(printf("xxx too many loops\n");) -} - -/* - * Second part of the file: functions local to this module. - * in this section a few routines to access MSS registers - * - */ - -static void -opti_write(int io_base, u_char reg, u_char value) -{ - outb(io_base, reg); - outb(io_base+1, value); -} - -static u_char -opti_read(int io_base, u_char reg) -{ - outb(io_base, reg); - return inb(io_base+1); -} - -static void -gus_write(int io_base, u_char reg, u_char value) +mss_intr(void *arg) { - outb(io_base + 3, reg); - outb(io_base + 5, value); -} - -#if 0 -static void -gus_writew(int io_base, u_char reg, u_short value) -{ - outb(io_base + 3, reg); - outb(io_base + 4, value); -} -#endif - -static u_char -gus_read(int io_base, u_char reg) -{ - outb(io_base+3, reg); - return inb(io_base+5); -} - -#if 0 -static u_short -gus_readw(int io_base, u_char reg) -{ - outb(io_base+3, reg); - return inw(io_base+4); + struct mss_info *mss = arg; + u_char c = 0, served = 0; + int i; + + DEB(printf("mss_intr\n")); + ad_read(mss, 11); /* fake read of status bits */ + + /* loop until there are interrupts, but no more than 10 times. */ + for (i = 10; i > 0 && io_rd(mss, MSS_STATUS) & 1; i--) { + /* get exact reason for full-duplex boards */ + c = FULL_DUPLEX(mss)? ad_read(mss, 24) : 0x30; + c &= ~served; + if (mss->pch.buffer->dl && (c & 0x10)) { + served |= 0x10; + chn_intr(mss->pch.channel); + } + if (mss->rch.buffer->dl && (c & 0x20)) { + served |= 0x20; + chn_intr(mss->rch.channel); + } + /* now ack the interrupt */ + if (FULL_DUPLEX(mss)) ad_write(mss, 24, ~c); /* ack selectively */ + else io_wr(mss, MSS_STATUS, 0); /* Clear interrupt status */ + } + if (i == 10) printf("mss_intr: irq, but not from mss\n"); + else if (served == 0) { + printf("mss_intr: unexpected irq with reason %x\n", c); + /* + * this should not happen... I have no idea what to do now. + * maybe should do a sanity check and restart dmas ? + */ + io_wr(mss, MSS_STATUS, 0); /* Clear interrupt status */ + } } -#endif /* * AD_WAIT_INIT waits if we are initializing the board and * we cannot modify its settings */ static int -AD_WAIT_INIT(snddev_info *d, int x) +ad_wait_init(struct mss_info *mss, int x) { - int arg=x, n = 0; /* to shut up the compiler... */ - for (; x-- ; ) - if ( (n=inb(io_Index_Addr(d))) & IA_BUSY) - DELAY(10); - else - return n ; - printf("AD_WAIT_INIT FAILED %d 0x%02x\n", arg, n); - return n ; + int arg = x, n = 0; /* to shut up the compiler... */ + for (; x > 0; x--) + if ((n = io_rd(mss, MSS_INDEX)) & MSS_IDXBUSY) DELAY(10); + else return n; + printf("AD_WAIT_INIT FAILED %d 0x%02x\n", arg, n); + return n; } static int -ad_read(snddev_info *d, int reg) +ad_read(struct mss_info *mss, int reg) { - u_long flags; - int x; - - flags = spltty(); - AD_WAIT_INIT(d, 201); - x = inb(io_Index_Addr(d)) & ~IA_AMASK ; - outb(io_Index_Addr(d), (u_char) (reg & IA_AMASK) | x ) ; - x = inb(io_Indexed_Data(d)); - splx(flags); - return x; + u_long flags; + int x; + + flags = spltty(); + ad_wait_init(mss, 201); + x = io_rd(mss, MSS_INDEX) & ~MSS_IDXMASK; + io_wr(mss, MSS_INDEX, (u_char)(reg & MSS_IDXMASK) | x); + x = io_rd(mss, MSS_IDATA); + splx(flags); + return x; } static void -ad_write(snddev_info *d, int reg, u_char data) +ad_write(struct mss_info *mss, int reg, u_char data) { - u_long flags; - - int x ; - flags = spltty(); - AD_WAIT_INIT(d, 1002); - x = inb(io_Index_Addr(d)) & ~IA_AMASK ; - outb(io_Index_Addr(d), (u_char) (reg & IA_AMASK) | x ) ; - outb(io_Indexed_Data(d), data); - splx(flags); + u_long flags; + + int x; + flags = spltty(); + ad_wait_init(mss, 1002); + x = io_rd(mss, MSS_INDEX) & ~MSS_IDXMASK; + io_wr(mss, MSS_INDEX, (u_char)(reg & MSS_IDXMASK) | x); + io_wr(mss, MSS_IDATA, data); + splx(flags); } static void -ad_write_cnt(snddev_info *d, int reg, u_short cnt) +ad_write_cnt(struct mss_info *mss, int reg, u_short cnt) { - ad_write(d, reg+1, cnt & 0xff ); - ad_write(d, reg, cnt >> 8 ); /* upper base must be last */ + ad_write(mss, reg+1, cnt & 0xff); + ad_write(mss, reg, cnt >> 8); /* upper base must be last */ } static void -wait_for_calibration(snddev_info *d) +wait_for_calibration(struct mss_info *mss) { - int n, t; - - /* - * Wait until the auto calibration process has finished. - * - * 1) Wait until the chip becomes ready (reads don't return 0x80). - * 2) Wait until the ACI bit of I11 gets on - * 3) Wait until the ACI bit of I11 gets off - */ - - n = AD_WAIT_INIT(d, 1000); - if (n & IA_BUSY) - printf("mss: Auto calibration timed out(1).\n"); - - for (t = 100 ; t>0 && (ad_read(d, 11) & 0x20) == 0 ; t--) - DELAY(100); - for (t = 100 ; t>0 && ad_read(d, 11) & 0x20 ; t--) - DELAY(100); -} + int n, t; -#if 0 /* unused right now... */ -static void -ad_mute(snddev_info *d) -{ - ad_write(d, 6, ad_read(d,6) | I6_MUTE); - ad_write(d, 7, ad_read(d,7) | I6_MUTE); + /* + * Wait until the auto calibration process has finished. + * + * 1) Wait until the chip becomes ready (reads don't return 0x80). + * 2) Wait until the ACI bit of I11 gets on + * 3) Wait until the ACI bit of I11 gets off + */ + + 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); + for (t = 100; t > 0 && ad_read(mss, 11) & 0x20; t--) DELAY(100); } static void -ad_unmute(snddev_info *d) +ad_unmute(struct mss_info *mss) { - ad_write(d, 6, ad_read(d,6) & ~I6_MUTE); - ad_write(d, 7, ad_read(d,7) & ~I6_MUTE); + ad_write(mss, 6, ad_read(mss, 6) & ~I6_MUTE); + ad_write(mss, 7, ad_read(mss, 7) & ~I6_MUTE); } -#endif static void -ad_enter_MCE(snddev_info *d) +ad_enter_MCE(struct mss_info *mss) { - int prev; + int prev; - d->bd_flags |= BD_F_MCE_BIT; - AD_WAIT_INIT(d, 203); - prev = inb(io_Index_Addr(d)); - prev &= ~IA_TRD ; - outb(io_Index_Addr(d), prev | IA_MCE ) ; + mss->bd_flags |= BD_F_MCE_BIT; + ad_wait_init(mss, 203); + prev = io_rd(mss, MSS_INDEX); + prev &= ~MSS_TRD; + io_wr(mss, MSS_INDEX, prev | MSS_MCE); } static void -ad_leave_MCE(snddev_info *d) +ad_leave_MCE(struct mss_info *mss) { - u_long flags; - u_char prev; + u_long flags; + u_char prev; - if ( (d->bd_flags & BD_F_MCE_BIT) == 0 ) { - printf("--- hey, leave_MCE: MCE bit was not set!\n"); - return; - } + if ((mss->bd_flags & BD_F_MCE_BIT) == 0) { + printf("--- hey, leave_MCE: MCE bit was not set!\n"); + return; + } - AD_WAIT_INIT(d, 1000); + ad_wait_init(mss, 1000); - flags = spltty(); - d->bd_flags &= ~BD_F_MCE_BIT; + flags = spltty(); + mss->bd_flags &= ~BD_F_MCE_BIT; - prev = inb(io_Index_Addr(d)); - prev &= ~IA_TRD ; - outb(io_Index_Addr(d), prev & ~IA_MCE ); /* Clear the MCE bit */ - wait_for_calibration(d); - splx(flags); + prev = io_rd(mss, MSS_INDEX); + prev &= ~MSS_TRD; + io_wr(mss, MSS_INDEX, prev & ~MSS_MCE); /* Clear the MCE bit */ + wait_for_calibration(mss); + splx(flags); } /* * only one source can be set... */ static int -mss_set_recsrc(snddev_info *d, int mask) +mss_set_recsrc(struct mss_info *mss, int mask) { - u_char recdev; - - mask &= d->mix_rec_devs; - switch (mask) { - case SOUND_MASK_LINE: - case SOUND_MASK_LINE3: - recdev = 0; - break; - - case SOUND_MASK_CD: - case SOUND_MASK_LINE1: - recdev = 0x40; - break; - - case SOUND_MASK_IMIX: - recdev = 0xc0; - break; - - case SOUND_MASK_MIC: - default: - mask = SOUND_MASK_MIC; - recdev = 0x80; - } - - ad_write(d, 0, (ad_read(d, 0) & 0x3f) | recdev); - ad_write(d, 1, (ad_read(d, 1) & 0x3f) | recdev); - - d->mix_recsrc = mask; - return 0; + u_char recdev; + + switch (mask) { + case SOUND_MASK_LINE: + case SOUND_MASK_LINE3: + recdev = 0; + break; + + case SOUND_MASK_CD: + case SOUND_MASK_LINE1: + recdev = 0x40; + break; + + case SOUND_MASK_IMIX: + recdev = 0xc0; + break; + + case SOUND_MASK_MIC: + default: + mask = SOUND_MASK_MIC; + recdev = 0x80; + } + ad_write(mss, 0, (ad_read(mss, 0) & 0x3f) | recdev); + ad_write(mss, 1, (ad_read(mss, 1) & 0x3f) | recdev); + return mask; } -/* - * there are differences in the mixer depending on the actual sound - * card. - */ +/* there are differences in the mixer depending on the actual sound card. */ static int -mss_mixer_set(snddev_info *d, int dev, int value) -{ - int left = value & 0x000000ff; - int right = (value & 0x0000ff00) >> 8; - - int regoffs; - mixer_tab *mix_d = &mix_devices; - - u_char old, val; - - if (dev > 31) - return EINVAL; - - if (!(d->mix_devs & (1 << dev))) - return EINVAL; - - if (d->bd_id == MD_OPTI931) - mix_d = &(opti931_devices); - - if ((*mix_d)[dev][LEFT_CHN].nbits == 0) { - DEB(printf("nbits = 0 for dev %d\n", dev) ); - return EINVAL; - } - - if (left > 100) - left = 100; - if (right > 100) - right = 100; - - - if ( (*mix_d)[dev][RIGHT_CHN].nbits == 0) /* Mono control */ - right = left; - - d->mix_levels[dev] = left | (right << 8); - -#if 0 - /* Scale volumes */ - left = mix_cvt[left]; - right = mix_cvt[right]; -#endif - /* - * Set the left channel - */ - - regoffs = (*mix_d)[dev][LEFT_CHN].regno; - old = val = ad_read(d, regoffs); - /* - * if volume is 0, mute chan. Otherwise, unmute. - */ - if (regoffs != 0) /* main input is different */ - val = (left == 0 ) ? old | 0x80 : old & 0x7f ; - - change_bits(mix_d, &val, dev, LEFT_CHN, left); - ad_write(d, regoffs, val); - DEB(printf("LEFT: dev %d reg %d old 0x%02x new 0x%02x\n", - dev, regoffs, old, val)); - - if ((*mix_d)[dev][RIGHT_CHN].nbits != 0) { /* have stereo */ - /* - * Set the right channel - */ - regoffs = (*mix_d)[dev][RIGHT_CHN].regno; - old = val = ad_read(d, regoffs); - if (regoffs != 1) - val = (right == 0 ) ? old | 0x80 : old & 0x7f ; - change_bits(mix_d, &val, dev, RIGHT_CHN, right); - ad_write(d, regoffs, val); - DEB(printf("RIGHT: dev %d reg %d old 0x%02x new 0x%02x\n", - dev, regoffs, old, val)); - } - return 0; /* success */ -} - -static void -ad1848_mixer_reset(snddev_info *d) +mss_mixer_set(struct mss_info *mss, int dev, int left, int right) { - int i; - - if (d->bd_id == MD_OPTI931) - d->mix_devs = OPTI931_MIXER_DEVICES; - else if (d->bd_id != MD_AD1848) - d->mix_devs = MODE2_MIXER_DEVICES; - else - d->mix_devs = MODE1_MIXER_DEVICES; - - d->mix_rec_devs = MSS_REC_DEVICES; - - for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) - if (d->mix_devs & (1 << i)) - mss_mixer_set(d, i, default_mixer_levels[i]); - mss_set_recsrc(d, SOUND_MASK_MIC); - /* - * some device-specific things, mostly mute the mic to - * the output mixer so as to avoid hisses. In many cases this - * is the default after reset, this code is here mostly as a - * reminder that this might be necessary on other boards. - */ - switch(d->bd_id) { - case MD_OPTI931: - ad_write(d, 20, 0x88); - ad_write(d, 21, 0x88); - break; - - case MD_YM0020: - /* set master volume to max */ - DDB(printf("set yamaha master volume to max\n"); ) - outb(0x370, 7) ; - outb(0x371, 0) ; - outb(0x370, 8) ; - outb(0x371, 0) ; - break; - - case MD_GUSPNP: - /* this is only necessary in mode 3 ... */ - ad_write(d, 22, 0x88); - ad_write(d, 23, 0x88); - } + int regoffs; + mixer_tab *mix_d = (mss->bd_id == MD_OPTI931)? &opti931_devices : &mix_devices; + u_char old, val; + + if ((*mix_d)[dev][LEFT_CHN].nbits == 0) { + DEB(printf("nbits = 0 for dev %d\n", dev)); + return -1; + } + + if ((*mix_d)[dev][RIGHT_CHN].nbits == 0) right = left; /* mono */ + + /* Set the left channel */ + + regoffs = (*mix_d)[dev][LEFT_CHN].regno; + old = val = ad_read(mss, regoffs); + /* if volume is 0, mute chan. Otherwise, unmute. */ + if (regoffs != 0) val = (left == 0)? old | 0x80 : old & 0x7f; + change_bits(mix_d, &val, dev, LEFT_CHN, left); + ad_write(mss, regoffs, val); + + DEB(printf("LEFT: dev %d reg %d old 0x%02x new 0x%02x\n", + dev, regoffs, old, val)); + + if ((*mix_d)[dev][RIGHT_CHN].nbits != 0) { /* have stereo */ + /* Set the right channel */ + regoffs = (*mix_d)[dev][RIGHT_CHN].regno; + old = val = ad_read(mss, regoffs); + if (regoffs != 1) val = (right == 0)? old | 0x80 : old & 0x7f; + change_bits(mix_d, &val, dev, RIGHT_CHN, right); + ad_write(mss, regoffs, val); + + DEB(printf("RIGHT: dev %d reg %d old 0x%02x new 0x%02x\n", + dev, regoffs, old, val)); + } + return 0; /* success */ } -/* - * mss_speed processes the value in play_speed finding the +/* mss_speed processes the value in play_speed finding the * matching one. As a side effect, it returns the value to * be written in the speed bits of the codec. It does _NOT_ * set the speed of the device (but it should!) */ static int -mss_speed(snddev_info *d) +mss_speed(struct mss_chinfo *ch, int speed) { - /* - * In the CS4231, the low 4 bits of I8 are used to hold the - * sample rate. Only a fixed number of values is allowed. This - * table lists them. The speed-setting routines scans the table - * looking for the closest match. This is the only supported method. - * - * In the CS4236, there is an alternate metod (which we do not - * support yet) which provides almost arbitrary frequency setting. - * In the AD1845, it looks like the sample rate can be - * almost arbitrary, and written directly to a register. - * In the OPTi931, there is a SB command which provides for - * almost arbitrary frequency setting. - * - */ - static int speeds[] = { - 8000, 5512, 16000, 11025, 27429, 18900, 32000, 22050, - -1, 37800, -1, 44100, 48000, 33075, 9600, 6615 - }; - - int arg, i, sel = 0; /* assume entry 0 does not contain -1 */ - - arg = d->play_speed ; - - for (i=1; i < 16 ; i++) - if (speeds[i] >0 && abs(arg-speeds[i]) < abs(arg-speeds[sel]) ) - sel = i ; - - d->play_speed = d->rec_speed = speeds[sel] ; - return sel ; + struct mss_info *mss = ch->parent; + /* + * In the CS4231, the low 4 bits of I8 are used to hold the + * sample rate. Only a fixed number of values is allowed. This + * table lists them. The speed-setting routines scans the table + * looking for the closest match. This is the only supported method. + * + * In the CS4236, there is an alternate metod (which we do not + * support yet) which provides almost arbitrary frequency setting. + * In the AD1845, it looks like the sample rate can be + * almost arbitrary, and written directly to a register. + * In the OPTi931, there is a SB command which provides for + * almost arbitrary frequency setting. + * + */ + ad_enter_MCE(mss); + if (mss->bd_id == MD_AD1845) { /* Use alternate speed select regs */ + ad_write(mss, 22, (speed >> 8) & 0xff); /* Speed MSB */ + ad_write(mss, 23, speed & 0xff); /* Speed LSB */ + /* XXX must also do something in I27 for the ad1845 */ + } else { + int i, sel = 0; /* assume entry 0 does not contain -1 */ + static int speeds[] = + {8000, 5512, 16000, 11025, 27429, 18900, 32000, 22050, + -1, 37800, -1, 44100, 48000, 33075, 9600, 6615}; + + for (i = 1; i < 16; i++) + if (speeds[i] > 0 && + abs(speed-speeds[i]) < abs(speed-speeds[sel])) sel = i; + speed = speeds[sel]; + ad_write(mss, 8, (ad_read(mss, 8) & 0xf0) | sel); + } + ad_leave_MCE(mss); + + return speed; } /* @@ -1064,1276 +1193,753 @@ mss_speed(snddev_info *d) */ static int -mss_format(snddev_info *d) -{ - int i, arg = d->play_fmt ; - - /* - * The data format uses 3 bits (just 2 on the 1848). For each - * bit setting, the following array returns the corresponding format. - * The code scans the array looking for a suitable format. In - * case it is not found, default to AFMT_U8 (not such a good - * choice, but let's do it for compatibility...). - */ - - static int fmts[] = { - AFMT_U8, AFMT_MU_LAW, AFMT_S16_LE, AFMT_A_LAW, - -1, AFMT_IMA_ADPCM, AFMT_U16_BE, -1 - }; - - if ( (arg & d->audio_fmt) == 0 ) /* unsupported fmt, default to AFMT_U8 */ - arg = AFMT_U8 ; - - /* ulaw/alaw seems broken on the opti931... */ - if (d->bd_id == MD_OPTI931 || d->bd_id == MD_GUSPNP) { - if (arg == AFMT_MU_LAW) { - arg = AFMT_U8 ; - d->flags |= SND_F_XLAT8 ; - } else - d->flags &= ~SND_F_XLAT8 ; - } - /* - * check that arg is one of the supported formats in d->format; - * otherwise fallback to AFMT_U8 - */ - - for (i=0 ; i<8 ; i++) - if (arg == fmts[i]) break; - if (i==8) { /* not found, default to AFMT_U8 */ - arg = AFMT_U8 ; - i = 0 ; - } - d->play_fmt = d->rec_fmt = arg; - - return i ; -} - -/* - * mss_detect can be used in the probe and the attach routine. - * - * We store probe information in pcm_info[unit]. This descriptor - * is reinitialized just before the attach, so all relevant - * information is lost, and mss_detect must be run again in - * the attach routine if necessary. - */ - -int -mss_detect(struct isa_device *dev) +mss_format(struct mss_chinfo *ch, u_int32_t format) { - int i; - u_char tmp, tmp1, tmp2 ; - snddev_info *d = &(pcm_info[dev->id_unit]); - char *name; - - d->io_base = dev->id_iobase; - d->bd_flags |= BD_F_MCE_BIT ; - if (d->bd_id != 0) { - printf("preselected bd_id 0x%04x -- %s\n", - d->bd_id, d->name ? d->name : "???"); - return 1; - } - - name = "AD1848" ; - d->bd_id = MD_AD1848; /* AD1848 or CS4248 */ - - /* - * Check that the I/O address is in use. - * - * bit 7 of the base I/O port is known to be 0 after the chip has - * performed its power on initialization. Just assume this has - * happened before the OS is starting. - * - * If the I/O address is unused, it typically returns 0xff. - */ - - for (i=0; i<10; i++) - if (inb(io_Index_Addr(d)) & IA_BUSY) - DELAY(10000); /* maybe busy, wait & retry later */ - else - break ; - if ((inb(io_Index_Addr(d)) & IA_BUSY) != 0x00) { /* Not a AD1848 */ - BVDDB(printf("mss_detect error, busy still set (0x%02x)\n", - inb(io_Index_Addr(d)))); - return 0; - } - /* - * Test if it's possible to change contents of the indirect - * registers. Registers 0 and 1 are ADC volume registers. The bit - * 0x10 is read only so try to avoid using it. - */ - - ad_write(d, 0, 0xaa); - ad_write(d, 1, 0x45);/* 0x55 with bit 0x10 clear */ - tmp1 = ad_read(d, 0) ; - tmp2 = ad_read(d, 1) ; - if ( tmp1 != 0xaa || tmp2 != 0x45) { - BVDDB(printf("mss_detect error - IREG (0x%02x/0x%02x) want 0xaa/0x45\n", - tmp1, tmp2)); - return 0; - } - - ad_write(d, 0, 0x45); - ad_write(d, 1, 0xaa); - tmp1 = ad_read(d, 0) ; - tmp2 = ad_read(d, 1) ; - - if (tmp1 != 0x45 || tmp2 != 0xaa) { - BVDDB(printf("mss_detect error - IREG2 (%x/%x)\n", tmp1, tmp2)); - return 0; - } - - /* - * The indirect register I12 has some read only bits. Lets try to - * change them. - */ - - tmp = ad_read(d, 12); - ad_write(d, 12, (~tmp) & 0x0f); - tmp1 = ad_read(d, 12); - - if ((tmp & 0x0f) != (tmp1 & 0x0f)) { - BVDDB(printf("mss_detect error - I12 (0x%02x was 0x%02x)\n", - tmp1, tmp)); - return 0; - } - - /* - * NOTE! Last 4 bits of the reg I12 tell the chip revision. - * 0x01=RevB - * 0x0A=RevC. also CS4231/CS4231A and OPTi931 - */ - - BVDDB(printf("mss_detect - chip revision 0x%02x\n", tmp & 0x0f);) - - /* - * The original AD1848/CS4248 has just 16 indirect registers. This - * means that I0 and I16 should return the same value (etc.). Ensure - * that the Mode2 enable bit of I12 is 0. Otherwise this test fails - * with new parts. - */ - - ad_write(d, 12, 0); /* Mode2=disabled */ - - for (i = 0; i < 16; i++) - if ((tmp1 = ad_read(d, i)) != (tmp2 = ad_read(d, i + 16))) { - BVDDB(printf("mss_detect warning - I%d: 0x%02x/0x%02x\n", - i, tmp1, tmp2)); - /* - * note - this seems to fail on the 4232 on I11. So we just break - * rather than fail. - */ - break ; /* return 0; */ - } - /* - * Try to switch the chip to mode2 (CS4231) by setting the MODE2 bit - * (0x40). The bit 0x80 is always 1 in CS4248 and CS4231. - * - * On the OPTi931, however, I12 is readonly and only contains the - * chip revision ID (as in the CS4231A). The upper bits return 0. - */ - - ad_write(d, 12, 0x40); /* Set mode2, clear 0x80 */ - - tmp1 = ad_read(d, 12); - if (tmp1 & 0x80) { - name = "CS4248" ; /* Our best knowledge just now */ - } - if ((tmp1 & 0xf0) == 0x00) { - BVDDB(printf("this should be an OPTi931\n");) - } else if ((tmp1 & 0xc0) == 0xC0) { - /* - * The 4231 has bit7=1 always, and bit6 we just set to 1. - * We want to check that this is really a CS4231 - * Verify that setting I0 doesn't change I16. - */ - ad_write(d, 16, 0); /* Set I16 to known value */ - - ad_write(d, 0, 0x45); - if ((tmp1 = ad_read(d, 16)) != 0x45) { /* No change -> CS4231? */ - - ad_write(d, 0, 0xaa); - if ((tmp1 = ad_read(d, 16)) == 0xaa) { /* Rotten bits? */ - BVDDB(printf("mss_detect error - step H(%x)\n", tmp1)); - return 0; - } - /* - * Verify that some bits of I25 are read only. - */ - - tmp1 = ad_read(d, 25); /* Original bits */ - ad_write(d, 25, ~tmp1); /* Invert all bits */ - if ((ad_read(d, 25) & 0xe7) == (tmp1 & 0xe7)) { - int id; - - /* - * It's at least CS4231 - */ - name = "CS4231" ; - d->bd_id = MD_CS4231; - - /* - * It could be an AD1845 or CS4231A as well. - * CS4231 and AD1845 report the same revision info in I25 - * while the CS4231A reports different. - */ - - id = ad_read(d, 25) & 0xe7; - /* - * b7-b5 = version number; - * 100 : all CS4231 - * 101 : CS4231A - * - * b2-b0 = chip id; - */ - switch (id) { - - case 0xa0: - name = "CS4231A" ; - d->bd_id = MD_CS4231A; - break; - - case 0xa2: - name = "CS4232" ; - d->bd_id = MD_CS4232; - break; - - case 0xb2: - /* strange: the 4231 data sheet says b4-b3 are XX - * so this should be the same as 0xa2 - */ - name = "CS4232A" ; - d->bd_id = MD_CS4232A; - break; - - case 0x80: - /* - * It must be a CS4231 or AD1845. The register I23 - * of CS4231 is undefined and it appears to be read - * only. AD1845 uses I23 for setting sample rate. - * Assume the chip is AD1845 if I23 is changeable. - */ - - tmp = ad_read(d, 23); - - ad_write(d, 23, ~tmp); - if (ad_read(d, 23) != tmp) { /* AD1845 ? */ - name = "AD1845" ; - d->bd_id = MD_AD1845; - } - ad_write(d, 23, tmp); /* Restore */ - DDB(printf("... try to identify the yamaha\n") ;) - tmp = inb(0x370) ; - outb(0x370, 6 /* dma config */ ) ; - if (inb(0x370) != 6 ) /* not a yamaha... restore. */ - outb(0x370, tmp ) ; - else - d->bd_id = MD_YM0020 ; - break; - - case 0x83: /* CS4236 */ - case 0x03: /* CS4236 on Intel PR440FX motherboard XXX */ - name = "CS4236"; - d->bd_id = MD_CS4236; - break ; - - default: /* Assume CS4231 */ - BVDDB(printf("unknown id 0x%02x, assuming CS4231\n", id);) - d->bd_id = MD_CS4231; - } - } - ad_write(d, 25, tmp1); /* Restore bits */ - - } - } - BVDDB(printf("mss_detect() - Detected %s\n", name)); - snprintf(d->name, sizeof(d->name), "%s", name); - dev->id_flags &= ~DV_F_DEV_MASK ; - dev->id_flags |= (d->bd_id << DV_F_DEV_SHIFT) & DV_F_DEV_MASK ; - return 1; + struct mss_info *mss = ch->parent; + int i, arg = format & ~AFMT_STEREO; + + /* + * The data format uses 3 bits (just 2 on the 1848). For each + * bit setting, the following array returns the corresponding format. + * The code scans the array looking for a suitable format. In + * case it is not found, default to AFMT_U8 (not such a good + * choice, but let's do it for compatibility...). + */ + + static int fmts[] = + {AFMT_U8, AFMT_MU_LAW, AFMT_S16_LE, AFMT_A_LAW, + -1, AFMT_IMA_ADPCM, AFMT_U16_BE, -1}; + + for (i = 0; i < 8; i++) if (arg == fmts[i]) break; + arg = i << 1; + if (format & AFMT_STEREO) arg |= 1; + arg <<= 4; + ad_enter_MCE(mss); + ad_write(mss, 8, (ad_read(mss, 8) & 0x0f) | arg); + if (FULL_DUPLEX(mss)) ad_write(mss, 28, arg); /* capture mode */ + ad_leave_MCE(mss); + return format; } - -/* - * mss_reinit resets registers of the codec - */ -static void -mss_reinit(snddev_info *d) +static int +mss_trigger(struct mss_chinfo *ch, int go) { - u_char r; - - r = mss_speed(d) ; - r |= (mss_format(d) << 5) ; - if (d->flags & SND_F_STEREO) - r |= 0x10 ; - /* XXX check if MCE is necessary... */ - ad_enter_MCE(d); - - /* - * perhaps this is not the place to set mode2, should be done - * only once at attach time... - */ - if ( FULL_DUPLEX(d) && d->bd_id != MD_OPTI931) - /* - * set mode2 bit for dual dma op. This bit is not implemented - * on the OPTi931 - */ - ad_write(d, 12, ad_read(d, 12) | 0x40 /* mode 2 on the CS42xx */ ); - - /* - * XXX this should really go into mss-speed... - */ - if (d->bd_id == MD_AD1845) { /* Use alternate speed select regs */ - r &= 0xf0; /* Mask off the rate select bits */ - - ad_write(d, 22, (d->play_speed >> 8) & 0xff); /* Speed MSB */ - ad_write(d, 23, d->play_speed & 0xff); /* Speed LSB */ - /* - * XXX must also do something in I27 for the ad1845 - */ - } - - ad_write(d, 8, r) ; - if ( FULL_DUPLEX(d) ) { + struct mss_info *mss = ch->parent; + u_char m; + int retry, wr, cnt; + + wr = (ch->dir == PCMDIR_PLAY)? 1 : 0; + m = ad_read(mss, 9); + switch (go) { + case PCMTRIG_START: + cnt = (ch->buffer->dl / ch->buffer->sample_size) - 1; + + DEB(if (m & 4) printf("OUCH! reg 9 0x%02x\n", m);); + m |= wr? I9_PEN : I9_CEN; /* enable DMA */ + ad_write_cnt(mss, (wr || !FULL_DUPLEX(mss))? 14 : 30, cnt); + break; + + case PCMTRIG_STOP: + case PCMTRIG_ABORT: /* XXX check this... */ + m &= ~(wr? I9_PEN : I9_CEN); /* Stop DMA */ #if 0 - if (d->bd_id == MD_GUSPNP && d->play_fmt == AFMT_MU_LAW) { - printf("warning, cannot do ulaw rec + play on the GUS\n"); - r = 0 ; /* move to U8 */ - } + /* + * try to disable DMA by clearing count registers. Not sure it + * is needed, and it might cause false interrupts when the + * DMA is re-enabled later. + */ + ad_write_cnt(mss, (wr || !FULL_DUPLEX(mss))? 14 : 30, 0); #endif - ad_write(d, 28, r & 0xf0 ) ; /* capture mode */ - ad_write(d, 9, 0 /* no capture, no playback, dual dma */) ; - } else - ad_write(d, 9, 4 /* no capture, no playback, single dma */) ; - ad_leave_MCE(d); - /* - * not sure if this is really needed... - */ - ad_write_cnt(d, 14, 0 ); /* playback count */ - if ( FULL_DUPLEX(d) ) - ad_write_cnt(d, 30, 0 ); /* rec. count on dual dma */ - - ad_write(d, 10, 2 /* int enable */) ; - outb(io_Status(d), 0); /* Clear interrupt status */ - /* the following seem required on the CS4232 */ - ad_write(d, 6, ad_read(d,6) & ~I6_MUTE); - ad_write(d, 7, ad_read(d,7) & ~I6_MUTE); - - snd_set_blocksize(d); /* update blocksize if user did not force it */ + } + /* on the OPTi931 the enable bit seems hard to set... */ + for (retry = 10; retry > 0; retry--) { + ad_write(mss, 9, m); + if (ad_read(mss, 9) == m) break; + } + if (retry == 0) printf("start dma, failed to set bit 0x%02x 0x%02x\n", + m, ad_read(mss, 9)); + return 0; } -/* - * here we have support for PnP cards - * - */ - #if NPNP > 0 +static int +pnpmss_probe(device_t dev) +{ + char *s = NULL; + u_int32_t logical_id = isa_get_logicalid(dev); + u_int32_t vend_id = isa_get_vendorid(dev); + u_int32_t id = vend_id & 0xff00ffff; + + switch (logical_id) { + case 0x0000630e: /* CSC0000 */ + if (id == 0x3700630e) s = "CS4237"; + else if (id == 0x2500630e) s = "CS4235"; + else if (id == 0x3600630e) s = "CS4236"; + else if (id == 0x3500630e) s = "CS4236B"; + else if (id == 0x3200630e) s = "CS4232"; + else s = "Unknown CS"; + break; + + case 0x2100a865: /* YMH0021 */ + if (id == 0x2000a865) s = "Yamaha SA2"; + else if (id == 0x3000a865) s = "Yamaha SA3"; + else if (id == 0x0000a865) s = "Yamaha YMF719 OPL-SA3"; + else s = "Yamaha OPL-SAx"; + break; + + case 0x1110d315: /* ENS1011 */ + s = "ENSONIQ SoundscapeVIVO"; + break; + + case 0x80719304: /* ADS7180 */ + s = "Terratec Soundsystem BASE 1"; + break; + + case 0x1093143e: /* OPT9310 */ + s = "OPTi931"; + break; + + case 0x5092143e: /* OPT9250 XXX guessing */ + s = "OPTi925"; + break; + + case 0x0000561e: + s = "GusPnP"; + break; + + case 0x01000000: + if (vend_id == 0x0100a90d) s = "CMI8330"; + break; + } + + if (s) { + device_set_desc(dev, s); + return 0; + } + return ENXIO; +} -static char * cs423x_probe(u_long csn, u_long vend_id); -static void -cs423x_attach(u_long csn, u_long vend_id, char *name, - struct isa_device *dev); - -static struct pnp_device cs423x = { - "CS423x/Yamaha/AD1816", - cs423x_probe, - cs423x_attach, - &nsnd, /* use this for all sound cards */ - &tty_imask /* imask */ -}; -DATA_SET (pnpdevice_set, cs423x); - -static char * -cs423x_probe(u_long csn, u_long vend_id) +static int +pnpmss_attach(device_t dev) { - char *s = NULL ; - u_long id = vend_id & 0xff00ffff; - if ( id == 0x3700630e ) - s = "CS4237" ; - else if ( id == 0x2500630e ) - s = "CS4235" ; - else if ( id == 0x3600630e ) - s = "CS4236" ; - else if ( id == 0x3500630e ) - s = "CS4236B" ; - else if ( id == 0x3200630e) - s = "CS4232" ; - else if ( id == 0x2000a865) - s = "Yamaha SA2"; - else if ( id == 0x3000a865) - s = "Yamaha SA3"; - else if ( id == 0x0000a865) - s = "Yamaha YMF719 OPL-SA3"; - else if (vend_id == 0x8140d315) - s = "SoundscapeVIVO"; - else if (vend_id == 0x1114b250) - s = "Terratec Soundsystem BASE 1"; - else if (vend_id == 0x50719304) - s = "Generic AD1815"; - if (s) { - struct pnp_cinfo d; - read_pnp_parms(&d, 0); - if (d.enable == 0) { - printf("This is a %s, but LDN 0 is disabled\n", s); - return NULL ; - } - return s; - } + struct mss_info *mss; + u_int32_t vend_id = isa_get_vendorid(dev); - return NULL ; -} + mss = (struct mss_info *)malloc(sizeof *mss, M_DEVBUF, M_NOWAIT); + if (!mss) return ENXIO; + bzero(mss, sizeof *mss); -extern snddev_info sb_op_desc; + mss->io_rid = 0; + mss->conf_rid = -1; + mss->irq_rid = 0; + mss->drq1_rid = 0; + mss->drq2_rid = 1; -static void -cs423x_attach(u_long csn, u_long vend_id, char *name, - struct isa_device *dev) -{ - struct pnp_cinfo d ; - snddev_info tmp_d ; /* patched copy of the basic snddev_info */ - int ldn = 0 ; - - if (read_pnp_parms ( &d , ldn ) == 0 ) { - printf("failed to read pnp parms\n"); - return ; - } - snddev_last_probed = &tmp_d; - - /* AD1816 */ - if (vend_id == 0x1114b250 || vend_id == 0x50719304) { - dev->id_alive = 16; /* number of io ports ? */ - - tmp_d = mss_op_desc; /* copy it */ - - tmp_d.ioctl = ad1816_ioctl; - tmp_d.isr = ad1816_intr; - tmp_d.callback = ad1816_callback; - tmp_d.audio_fmt = AFMT_STEREO | AFMT_U8 | - AFMT_A_LAW | AFMT_MU_LAW | - AFMT_S16_LE | AFMT_S16_BE; - - dev->id_iobase = d.port[2]; - tmp_d.alt_base = d.port[0]; /* soundblaster comp. but we don't - * use that */ - tmp_d.bd_id = MD_AD1816; - strcpy(tmp_d.name, name); - } else if (d.flags & DV_PNP_SBCODEC) { /* use sb-compatible codec */ - dev->id_alive = 16 ; /* number of io ports ? */ - tmp_d = sb_op_desc ; - if (vend_id==0x2000a865 || vend_id==0x3000a865 || - vend_id==0x0008a865 || vend_id==0x8140d315) { - /* Yamaha SA2/SA3 or ENSONIQ SoundscapeVIVO ENS4081 */ - dev->id_iobase = d.port[0] ; - tmp_d.alt_base = d.port[1] ; - d.irq[1] = 0 ; /* only needed for the VIVO */ - } else { - dev->id_iobase = d.port[2] ; - tmp_d.alt_base = d.port[0] - 4; - } - d.drq[1] = 4 ; /* disable, it is not used ... */ - } else { /* mss-compatible codec */ - dev->id_alive = 8 ; /* number of io ports ? */ - tmp_d = mss_op_desc ; - dev->id_iobase = d.port[0] -4 ; /* XXX old mss have 4 bytes before... */ - tmp_d.alt_base = d.port[2]; switch (vend_id & 0xff00ffff) { + case 0x1100b250: /* terratec */ + case 0x50009304: /* generic ad1815 */ + mss->io_rid = 2; + mss->bd_id = MD_AD1816; + break; case 0x2000a865: /* Yamaha SA2 */ case 0x3000a865: /* Yamaha SA3 */ - case 0x0000a865: /* Yamaha TMF719 SA3 */ - dev->id_iobase = d.port[1]; - tmp_d.alt_base = d.port[0]; - tmp_d.conf_base = d.port[4]; - tmp_d.bd_id = MD_YM0020 ; + case 0x0000a865: /* Yamaha YMF719 SA3 */ + mss->io_rid = 1; + mss->conf_rid = 4; + mss->bd_id = MD_YM0020; break; case 0x8100d315: /* ENSONIQ SoundscapeVIVO */ - dev->id_iobase = d.port[1]; - tmp_d.alt_base = d.port[0]; - tmp_d.bd_id = MD_VIVO ; - d.irq[1] = 0 ; + mss->io_rid = 1; + mss->bd_id = MD_VIVO; break; case 0x3700630e: /* CS4237 */ - tmp_d.bd_id = MD_CS4237 ; - break; - case 0x2500630e: /* AOpen AW37, CS4235 */ - tmp_d.bd_id = MD_CS4237 ; - break ; + mss->bd_flags |= BD_F_MSS_OFFSET; + mss->bd_id = MD_CS4237; + break; case 0x3500630e: /* CS4236B */ case 0x3600630e: /* CS4236 */ - tmp_d.bd_id = MD_CS4236 ; + mss->bd_flags |= BD_F_MSS_OFFSET; + mss->bd_id = MD_CS4236; + break; + + case 0x3100143e: /* opti931 */ + mss->bd_flags |= BD_F_MSS_OFFSET; + mss->conf_rid = 3; + mss->bd_id = MD_OPTI931; + break; + + case 0x2500143e: /* opti925 */ + mss->io_rid = 1; + mss->conf_rid = 3; + mss->bd_id = MD_OPTI925; + break; + + case 0x0100561e: /* guspnp */ + mss->io_rid = 2; + mss->conf_rid = 1; + mss->drq1_rid = 1; + mss->drq2_rid = 0; + mss->bd_id = MD_GUSPNP; break; default: - tmp_d.bd_id = MD_CS4232; /* to short-circuit the - * detect routine */ + mss->bd_flags |= BD_F_MSS_OFFSET; + mss->bd_id = MD_CS4232; break; } - snprintf(tmp_d.name, sizeof(tmp_d.name), "%s", name); - tmp_d.audio_fmt |= AFMT_FULLDUPLEX ; - } - - write_pnp_parms( &d, ldn ); - enable_pnp_card(); - - if ( (vend_id & 0x0000ffff) == 0x0000a865 ) { - /* special volume setting for the Yamaha... */ - outb(tmp_d.conf_base, 7 /* volume, left */); - outb(tmp_d.conf_base+1, 0 ); - outb(tmp_d.conf_base, 8 /* volume, right */); - outb(tmp_d.conf_base+1, 0 ); - } - dev->id_drq = d.drq[0] ; /* primary dma */ - dev->id_irq = (1 << d.irq[0] ) ; - dev->id_ointr = pcmintr ; - dev->id_flags = DV_F_DUAL_DMA | (d.drq[1] ) ; - - tmp_d.synth_base = d.port[1]; /* XXX check this for yamaha */ - pcmattach(dev); + return mss_doattach(dev, mss); } -static char *opti931_probe(u_long csn, u_long vend_id); -static void opti931_attach(u_long csn, u_long vend_id, char *name, - struct isa_device *dev); -static struct pnp_device opti931 = { - "OPTi931", - opti931_probe, - opti931_attach, - &nsnd, /* use this for all sound cards */ - &tty_imask /* imask */ +static device_method_t pnpmss_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, pnpmss_probe), + DEVMETHOD(device_attach, pnpmss_attach), + + { 0, 0 } }; -DATA_SET (pnpdevice_set, opti931); -static char * -opti931_probe(u_long csn, u_long vend_id) -{ - if (vend_id == 0x3109143e) { - struct pnp_cinfo d; - read_pnp_parms(&d, 1); - if (d.enable == 0) { - printf("This is an OPTi931, but LDN 1 is disabled\n"); - return NULL ; - } - return "OPTi931" ; - } - return NULL ; -} +static driver_t pnpmss_driver = { + "pcm", + pnpmss_methods, + sizeof(snddev_info), +}; + +DRIVER_MODULE(pnpmss, isa, pnpmss_driver, pcm_devclass, 0, 0); +/* + * the opti931 seems to miss interrupts when working in full + * duplex, so we try some heuristics to catch them. + */ static void -opti931_attach(u_long csn, u_long vend_id, char *name, - struct isa_device *dev) +opti931_intr(void *arg) { - struct pnp_cinfo d ; - snddev_info tmp_d ; /* patched copy of the basic snddev_info */ - int p; - - read_pnp_parms ( &d , 3 ); /* free resources taken by LDN 3 */ - d.irq[0]=0; /* free irq... */ - d.port[0]=0; /* free address... */ - d.enable = 0 ; - write_pnp_parms ( &d , 3 ); - - read_pnp_parms ( &d , 2 ); /* disable LDN 2 */ - d.enable = 0 ; - write_pnp_parms ( &d , 2 ); - - read_pnp_parms ( &d , 1 ) ; - write_pnp_parms( &d, 1 ); - enable_pnp_card(); - - snddev_last_probed = &tmp_d; - tmp_d = d.flags & DV_PNP_SBCODEC ? sb_op_desc : mss_op_desc ; - - snprintf(tmp_d.name, sizeof(tmp_d.name), "%s", name); - - /* - * My MED3931 v.1.0 allocates 3 bytes for the config space, - * whereas v.2.0 allocates 4 bytes. What I know for sure is that the - * upper two ports must be used, and they should end on a boundary - * of 4 bytes. So I need the following trick... - */ - p = tmp_d.conf_base = (d.port[3] & ~3) + 2; /* config port */ - - /* - * now set default values for both modes. - */ - dev->id_iobase = d.port[0] - 4 ; /* old mss have 4 bytes before... */ - tmp_d.io_base = dev->id_iobase; /* needed for ad_write to work... */ - tmp_d.alt_base = d.port[2]; - tmp_d.synth_base = d.port[1]; - opti_write(p, 4, 0xd6 /* fifo empty, OPL3, audio enable, SB3.2 */ ); - ad_write (&tmp_d, 10, 2); /* enable interrupts */ - - if (d.flags & DV_PNP_SBCODEC) { /* sb-compatible codec */ - /* - * the 931 is not a real SB, it has important pieces of - * hardware controlled by both the MSS and the SB port... - */ - printf("--- opti931 in sb mode ---\n"); - opti_write(p, 6, 1); /* MCIR6 mss disable, sb enable */ - /* - * swap the main and alternate iobase address since we want - * to work in sb mode. - */ - dev->id_iobase = d.port[2] ; - tmp_d.alt_base = d.port[0] - 4; - dev->id_flags = DV_F_DUAL_DMA | d.drq[1] ; - } else { /* mss-compatible codec */ - tmp_d.bd_id = MD_OPTI931 ; /* to short-circuit the detect routine */ - opti_write(p, 6 , 2); /* MCIR6: mss enable, sb disable */ - opti_write(p, 5, 0x28); /* MCIR5: codec in exp. mode,fifo */ - dev->id_flags = DV_F_DUAL_DMA | d.drq[1] ; - tmp_d.audio_fmt |= AFMT_FULLDUPLEX ; /* not really well... */ - tmp_d.isr = opti931_intr; - } - dev->id_drq = d.drq[0] ; /* primary dma */ - dev->id_irq = (1 << d.irq[0] ) ; - dev->id_ointr = pcmintr ; - pcmattach(dev); + struct mss_info *mss = (struct mss_info *)arg; + u_char masked = 0, i11, mc11, c = 0; + u_char reason; /* b0 = playback, b1 = capture, b2 = timer */ + int loops = 10; + +#if 0 + reason = io_rd(mss, MSS_STATUS); + if (!(reason & 1)) {/* no int, maybe a shared line ? */ + printf("intr: flag 0, mcir11 0x%02x\n", ad_read(mss, 11)); + return; + } +#endif + i11 = ad_read(mss, 11); /* XXX what's for ? */ + again: + + c = mc11 = FULL_DUPLEX(mss)? conf_rd(mss, 11) : 0xc; + mc11 &= 0x0c; + if (c & 0x10) { + DEB(printf("Warning: CD interrupt\n");) + mc11 |= 0x10; + } + if (c & 0x20) { + DEB(printf("Warning: MPU interrupt\n");) + mc11 |= 0x20; + } + if (mc11 & masked) printf("irq reset failed, mc11 0x%02x, 0x%02x\n", + mc11, masked); + masked |= mc11; + /* + * the nice OPTi931 sets the IRQ line before setting the bits in + * mc11. So, on some occasions I have to retry (max 10 times). + */ + if (mc11 == 0) { /* perhaps can return ... */ + reason = io_rd(mss, MSS_STATUS); + if (reason & 1) { + DEB(printf("one more try...\n");) + if (--loops) goto again; + else DDB(printf("intr, but mc11 not set\n");) + } + if (loops == 0) printf("intr, nothing in mcir11 0x%02x\n", mc11); + return; + } + + if (mss->rch.buffer->dl && (mc11 & 8)) chn_intr(mss->rch.channel); + if (mss->pch.buffer->dl && (mc11 & 4)) chn_intr(mss->pch.channel); + conf_wr(mss, 11, ~mc11); /* ack */ + if (--loops) goto again; + DEB(printf("xxx too many loops\n");) } -static char *opti925_probe(u_long csn, u_long vend_id); -static void opti925_attach(u_long csn, u_long vend_id, char *name, - struct isa_device *dev); - -static struct pnp_device opti925 = { - "opti925", - opti925_probe, - opti925_attach, - &nsnd, /* use this for all sound cards */ - &tty_imask /* imask */ -}; -DATA_SET (pnpdevice_set, opti925); - -static char * -opti925_probe(u_long csn, u_long vend_id) -{ - if (vend_id == 0x2509143e) { - struct pnp_cinfo d ; - read_pnp_parms ( &d , 1 ) ; - if (d.enable == 0) { - printf("This is an OPTi925, but LDN 1 is disabled\n"); - return NULL; - } - return "OPTi925" ; - } - return NULL ; -} - -static void -opti925_attach(u_long csn, u_long vend_id, char *name, - struct isa_device *dev) +static void +ad1816_intr(void *arg) { - struct pnp_cinfo d ; - snddev_info tmp_d ; /* patched copy of the basic snddev_info */ - int the_irq = 0 ; - - tmp_d = mss_op_desc; - snddev_last_probed = &tmp_d; - - read_pnp_parms ( &d , 3 ); /* disable LDN 3 */ - the_irq = d.irq[0]; - d.port[0] = 0 ; - d.enable = 0 ; - write_pnp_parms ( &d , 3 ); - - read_pnp_parms ( &d , 2 ); /* disable LDN 2 */ - d.port[0] = 0 ; - d.enable = 0 ; - write_pnp_parms ( &d , 2 ); - - read_pnp_parms ( &d , 1 ) ; - d.irq[0] = the_irq ; - dev->id_iobase = d.port[1]; - tmp_d.alt_base = d.port[0]; - write_pnp_parms ( &d , 1 ); - enable_pnp_card(); - - tmp_d.conf_base = d.port[3]; - - dev->id_drq = d.drq[0] ; /* primary dma */ - dev->id_irq = (1 << d.irq[0] ) ; - dev->id_ointr = pcmintr ; - dev->id_flags = DV_F_DUAL_DMA | d.drq[1] ; - tmp_d.audio_fmt |= AFMT_FULLDUPLEX ; - - snddev_last_probed->probe(dev); /* not really necessary but doesn't harm */ - - pcmattach(dev); + struct mss_info *mss = (struct mss_info *)arg; + unsigned char c, served = 0; + + /* get interupt status */ + c = io_rd(mss, AD1816_INT); + + /* check for stray interupts */ + if (c & ~(AD1816_INTRCI | AD1816_INTRPI)) { + printf("pcm: stray int (%x)\n", c); + c &= AD1816_INTRCI | AD1816_INTRPI; + } + /* check for capture interupt */ + if (mss->rch.buffer->dl && (c & AD1816_INTRCI)) { + chn_intr(mss->rch.channel); + served |= AD1816_INTRCI; /* cp served */ + } + /* check for playback interupt */ + if (mss->pch.buffer->dl && (c & AD1816_INTRPI)) { + chn_intr(mss->pch.channel); + served |= AD1816_INTRPI; /* pb served */ + } + if (served == 0) { + /* this probably means this is not a (working) ad1816 chip, */ + /* or an error in dma handling */ + printf("pcm: int without reason (%x)\n", c); + c = 0; + } else c &= ~served; + io_wr(mss, AD1816_INT, c); + c = io_rd(mss, AD1816_INT); + if (c != 0) printf("pcm: int clear failed (%x)\n", c); } -#if 0 -static void gus_mem_cfg(snddev_info *tmp); -#endif +static int +ad1816_wait_init(struct mss_info *mss, int x) +{ + int n = 0; /* to shut up the compiler... */ -static char *guspnp_probe(u_long csn, u_long vend_id); -static void guspnp_attach(u_long csn, u_long vend_id, char *name, - struct isa_device *dev); -static struct pnp_device guspnp = { - "GusPnP", - guspnp_probe, - guspnp_attach, - &nsnd, /* use this for all sound cards */ - &tty_imask /* imask */ -}; -DATA_SET (pnpdevice_set, guspnp); + for (; x--;) + if ((n = (io_rd(mss, AD1816_ALE) & AD1816_BUSY)) == 0) DELAY(10); + else return n; + printf("ad1816_wait_init failed 0x%02x.\n", n); + return -1; +} -static char * -guspnp_probe(u_long csn, u_long vend_id) +static unsigned short +ad1816_read(struct mss_info *mss, unsigned int reg) { - if (vend_id == 0x0100561e) { - struct pnp_cinfo d; - read_pnp_parms(&d, 0); - if (d.enable == 0) { - printf("This is a GusPnP, but LDN 0 is disabled\n"); - return NULL ; - } - return "GusPnP" ; - } - return NULL ; + int flags; + u_short x = 0; + + /* we don't want to be blocked here */ + flags = spltty(); + if (ad1816_wait_init(mss, 100) == -1) return 0; + io_wr(mss, AD1816_ALE, 0); + io_wr(mss, AD1816_ALE, (reg & AD1816_ALEMASK)); + if (ad1816_wait_init(mss, 100) == -1) return 0; + x = (io_rd(mss, AD1816_HIGH) << 8) | io_rd(mss, AD1816_LOW); + splx(flags); + return x; } static void -guspnp_attach(u_long csn, u_long vend_id, char *name, - struct isa_device *dev) +ad1816_write(struct mss_info *mss, unsigned int reg, unsigned short data) { - struct pnp_cinfo d ; - snddev_info tmp_d ; /* patched copy of the basic snddev_info */ - - u_char tmp; - - read_pnp_parms ( &d , 0 ) ; - - /* d.irq[1] = d.irq[0] ; */ - pnp_write ( 0xf2, 0xff ); /* enable power on the guspnp */ - - write_pnp_parms ( &d , 0 ); - enable_pnp_card(); - - tmp_d = mss_op_desc ; - snddev_last_probed = &tmp_d; - - dev->id_iobase = d.port[2] - 4 ; /* room for 4 mss registers */ - dev->id_drq = d.drq[1] ; /* XXX PLAY dma */ - dev->id_irq = (1 << d.irq[0] ) ; - dev->id_ointr = pcmintr ; - dev->id_flags = DV_F_DUAL_DMA | d.drq[0] ; /* REC dma */ - - tmp_d.io_base = d.port[2] - 4; - tmp_d.alt_base = d.port[0]; /* 0x220 */ - tmp_d.conf_base = d.port[1]; /* gus control block... */ - tmp_d.bd_id = MD_GUSPNP ; - - /* reset */ - gus_write(tmp_d.conf_base, 0x4c /* _URSTI */, 0 );/* Pull reset */ - DELAY(1000 * 30); - /* release reset and enable DAC */ - gus_write(tmp_d.conf_base, 0x4c /* _URSTI */, 3 ); - DELAY(1000 * 30); - /* end of reset */ - - outb( tmp_d.alt_base, 0xC ); /* enable int and dma */ - - /* - * unmute left & right line. Need to go in mode3, unmute, - * and back to mode 2 - */ - tmp = ad_read(&tmp_d, 0x0c); - ad_write(&tmp_d, 0x0c, 0x6c ); /* special value to enter mode 3 */ - ad_write(&tmp_d, 0x19, 0 ); /* unmute left */ - ad_write(&tmp_d, 0x1b, 0 ); /* unmute right */ - ad_write(&tmp_d, 0x0c, tmp ); /* restore old mode */ - - /* send codec interrupts on irq1 and only use that one */ - gus_write(tmp_d.conf_base, 0x5a , 0x4f ); + int flags; + + flags = spltty(); + if (ad1816_wait_init(mss, 100) == -1) return; + io_wr(mss, AD1816_ALE, (reg & AD1816_ALEMASK)); + io_wr(mss, AD1816_LOW, (data & 0x000000ff)); + io_wr(mss, AD1816_HIGH, (data & 0x0000ff00) >> 8); + splx(flags); +} - /* enable access to hidden regs */ - tmp = gus_read(tmp_d.conf_base, 0x5b /* IVERI */ ); - gus_write(tmp_d.conf_base, 0x5b , tmp | 1 ); - BVDDB(printf("GUS: silicon rev %c\n", 'A' + ( ( tmp & 0xf ) >> 4) );) +/* only one rec source is possible */ +static int +ad1816_set_recsrc(struct mss_info *mss, int mask) +{ + int dev; + + switch (mask) { + case SOUND_MASK_LINE: + case SOUND_MASK_LINE3: + dev = 0x00; + break; + + case SOUND_MASK_CD: + case SOUND_MASK_LINE1: + dev = 0x20; + break; + + case SOUND_MASK_MIC: + default: + dev = 0x50; + mask = SOUND_MASK_MIC; + } + + dev |= dev << 8; + ad1816_write(mss, 20, (ad1816_read(mss, 20) & ~0x7070) | dev); + return mask; +} - snprintf(tmp_d.name, sizeof(tmp_d.name), "%s", name); +#define AD1816_MUTE 31 /* value for mute */ - pcmattach(dev); +static int +ad1816_mixer_set(struct mss_info *mss, int dev, int left, int right) +{ + u_short reg = 0; + + /* Scale volumes */ + left = AD1816_MUTE - (AD1816_MUTE * left) / 100; + right = AD1816_MUTE - (AD1816_MUTE * right) / 100; + + reg = (left << 8) | right; + + /* do channel selective muting if volume is zero */ + if (left == AD1816_MUTE) reg |= 0x8000; + if (right == AD1816_MUTE) reg |= 0x0080; + + switch (dev) { + case SOUND_MIXER_VOLUME: /* Register 14 master volume */ + ad1816_write(mss, 14, reg); + break; + + case SOUND_MIXER_CD: /* Register 15 cd */ + case SOUND_MIXER_LINE1: + ad1816_write(mss, 15, reg); + break; + + case SOUND_MIXER_SYNTH: /* Register 16 synth */ + ad1816_write(mss, 16, reg); + break; + + case SOUND_MIXER_PCM: /* Register 4 pcm */ + ad1816_write(mss, 4, reg); + break; + + case SOUND_MIXER_LINE: + case SOUND_MIXER_LINE3: /* Register 18 line in */ + ad1816_write(mss, 18, reg); + break; + + case SOUND_MIXER_MIC: /* Register 19 mic volume */ + ad1816_write(mss, 19, reg & ~0xff); /* mic is mono */ + break; + + case SOUND_MIXER_IGAIN: + /* and now to something completely different ... */ + ad1816_write(mss, 20, ((ad1816_read(mss, 20) & ~0x0f0f) + | (((AD1816_MUTE - left) / 2) << 8) /* four bits of adc gain */ + | ((AD1816_MUTE - right) / 2))); + break; + + default: + printf("ad1816_mixer_set(): unknown device.\n"); + break; + } + + return 0; /* success */ } -#if 0 -int -gus_mem_write(snddev_info *d, int addr, u_char data) +static int +ad1816_trigger(struct mss_chinfo *ch, int go) { - gus_writew(d->conf_base, 0x43 , addr & 0xffff ); - gus_write(d->conf_base, 0x44 , (addr>>16) & 0xff ); - outb(d->conf_base + 7, data); + int wr, reg; + struct mss_info *mss = ch->parent; + + wr = (ch->dir == PCMDIR_PLAY); + reg = wr? AD1816_PLAY : AD1816_CAPT; + + switch (go) { + case PCMTRIG_START: + /* start only if not already running */ + if (!(io_rd(mss, reg) & AD1816_ENABLE)) { + int cnt = ((ch->buffer->dl) >> 2) - 1; + ad1816_write(mss, wr? 8 : 10, cnt); /* count */ + ad1816_write(mss, 1, ad1816_read(mss, 1) | + (wr? 0x8000 : 0x4000)); /* enable int */ + /* enable playback */ + io_wr(mss, reg, io_rd(mss, reg) | AD1816_ENABLE); + if (!(io_rd(mss, reg) & AD1816_ENABLE)) + printf("ad1816: failed to start %s DMA!\n", + wr? "play" : "rec"); + } + break; + + case PCMTRIG_STOP: + case PCMTRIG_ABORT: /* XXX check this... */ + /* we don't test here if it is running... */ + if (wr) { + ad1816_write(mss, 1, ad1816_read(mss, 1) & + ~(wr? 0x8000 : 0x4000)); + /* disable int */ + io_wr(mss, reg, io_rd(mss, reg) & ~AD1816_ENABLE); + /* disable playback */ + if (io_rd(mss, reg) & AD1816_ENABLE) + printf("ad1816: failed to stop %s DMA!\n", + wr? "play" : "rec"); + ad1816_write(mss, wr? 8 : 10, 0); /* reset base cnt */ + ad1816_write(mss, wr? 9 : 11, 0); /* reset cur cnt */ + } + break; + } + return 0; } -u_char -gus_mem_read(snddev_info *d, int addr) + +static int +ad1816_speed(struct mss_chinfo *ch, u_int32_t speed) { - gus_writew(d->conf_base, 0x43 , addr & 0xffff ); - gus_write(d->conf_base, 0x44 , (addr>>16) & 0xff ); - return inb(d->conf_base + 7); + struct mss_info *mss = ch->parent; + + RANGE(speed, 4000, 55200); + ad1816_write(mss, (ch->dir == PCMDIR_PLAY)? 2 : 3, speed); + return speed; } -void -gus_mem_cfg(snddev_info *d) +static int +ad1816_format(struct mss_chinfo *ch, u_int32_t format) { - int base; - u_char old; - u_char a, b; - - printf("configuring gus memory...\n"); - gus_writew(d->conf_base, 0x52 /* LMCFI */, 1 /* 512K*/); - old = gus_read(d->conf_base, 0x19); - gus_write(d->conf_base, 0x19, old | 1); /* enable enhaced mode */ - for (base = 0; base < 1024; base++) { - a=gus_mem_read(d, base*1024); - a = ~a ; - gus_mem_write(d, base*1024, a); - b=gus_mem_read(d, base*1024); - if ( b != a ) - break ; - } - printf("Have found %d KB ( 0x%x != 0x%x)\n", base, a, b); + struct mss_info *mss = ch->parent; + + int fmt = AD1816_U8, reg; + if (ch->dir == PCMDIR_PLAY) { + reg = AD1816_PLAY; + ad1816_write(mss, 8, 0x0000); /* reset base and current counter */ + ad1816_write(mss, 9, 0x0000); /* for playback and capture */ + } else { + reg = AD1816_CAPT; + ad1816_write(mss, 10, 0x0000); + ad1816_write(mss, 11, 0x0000); + } + switch (format & ~AFMT_STEREO) { + case AFMT_A_LAW: + fmt = AD1816_ALAW; + break; + + case AFMT_MU_LAW: + fmt = AD1816_MULAW; + break; + + case AFMT_S16_LE: + fmt = AD1816_S16LE; + break; + + case AFMT_S16_BE: + fmt = AD1816_S16BE; + break; + + case AFMT_U8: + fmt = AD1816_U8; + break; + } + if (format & AFMT_STEREO) fmt |= AD1816_STEREO; + io_wr(mss, reg, fmt); + return format; } -#endif /* gus mem cfg... */ + +#endif /* NPNP > 0 */ static int -ad1816_ioctl(dev_t i_dev, u_long cmd, caddr_t arg, int mode, struct proc * p) +mssmix_init(snd_mixer *m) { - snddev_info *d; - int unit; - int dev; - - dev = minor(i_dev); - unit = dev >> 4; - d = &pcm_info[unit]; - - if ((cmd & MIXER_WRITE(0)) == MIXER_WRITE(0)) { - cmd &= 0xff; - if (cmd == SOUND_MIXER_RECSRC) - return ad1816_set_recsrc(d, *(int *) arg); - else - return ad1816_mixer_set(d, cmd, *(int *) arg); - } - switch (cmd) { /* driver specific ioctls other than mixer - * calls */ - /* ad1816 has special features */ - case AIOGCAP: /* get capabilities */ - { - snd_capabilities *p = (snd_capabilities *) arg; - p->rate_min = 4000; - p->rate_max = 55200; - p->bufsize = d->bufsize; - p->formats = d->audio_fmt; - p->mixers = 1; - p->inputs = d->mix_devs; - p->left = p->right = 100; - return 0; + struct mss_info *mss = mix_getdevinfo(m); + + mix_setdevs(m, MODE2_MIXER_DEVICES); + mix_setrecdevs(m, MSS_REC_DEVICES); + switch(mss->bd_id) { + case MD_AD1816: + mix_setdevs(m, AD1816_MIXER_DEVICES); + mix_setrecdevs(m, AD1816_REC_DEVICES); + break; + + case MD_OPTI931: + mix_setdevs(m, OPTI931_MIXER_DEVICES); + ad_write(mss, 20, 0x88); + ad_write(mss, 21, 0x88); + break; + + case MD_AD1848: + mix_setdevs(m, MODE1_MIXER_DEVICES); + break; + + case MD_GUSPNP: + /* this is only necessary in mode 3 ... */ + ad_write(mss, 22, 0x88); + ad_write(mss, 23, 0x88); + break; } - default: - { - return ENOSYS; /* fallback to default */ - } - break; - } + return 0; } static int -ad1816_callback(snddev_info * d, int reason) +mssmix_set(snd_mixer *m, unsigned dev, unsigned left, unsigned right) { - int wr, cnt; - - wr = reason & SND_CB_WR; - reason &= SND_CB_REASON_MASK; - - switch (reason) { - case SND_CB_INIT: - ad1816_reinit(d); - reset_dbuf(&(d->dbuf_in), SND_CHAN_RD); - reset_dbuf(&(d->dbuf_out), SND_CHAN_WR); - return 1; - break; - - case SND_CB_START: - cnt = wr ? d->dbuf_out.dl : d->dbuf_in.dl; - - cnt /= 4; - cnt--; - - /* start only if not already running */ - if (wr && !(inb(ad1816_play(d)) & AD1816_ENABLE)) { - /* set dma counter */ - ad1816_write(d, 8, cnt); /* playback count */ - /* int enable */ - ad1816_write(d, 1, ad1816_read(d, 1) | 0x8000); - /* enable playback */ - outb(ad1816_play(d), (inb(ad1816_play(d)) | AD1816_ENABLE)); - /* check if we succeeded */ - if (!(inb(ad1816_play(d)) & AD1816_ENABLE)) { - printf("ad1816: failed to start write (playback) DMA !\n"); - } - } else if (!wr && !(inb(ad1816_capt(d)) & AD1816_ENABLE)) { - /* same for capture */ - ad1816_write(d, 10, cnt); /* capture count */ - ad1816_write(d, 1, ad1816_read(d, 1) | 0x4000); /* int */ - outb(ad1816_capt(d), (inb(ad1816_capt(d)) | AD1816_ENABLE)); /* CEN */ - if (!(inb(ad1816_capt(d)) & AD1816_ENABLE)) { /* check */ - printf("ad1816: failed to start read (capture) DMA !\n"); - } - } - break; - - case SND_CB_STOP: - case SND_CB_ABORT: /* XXX check this... */ - /* we don't test here if it is running... */ - if (wr) { - ad1816_write(d, 1, ad1816_read(d, 1) & ~0x8000); - /* disable int */ - outb(ad1816_play(d), (inb(ad1816_play(d)) & ~AD1816_ENABLE)); - /* disable playback */ - if ((inb(ad1816_play(d)) & AD1816_ENABLE)) { - printf("ad1816: failed to stop write (playback) DMA !\n"); - } - ad1816_write(d, 8, 0); /* reset base counter */ - ad1816_write(d, 9, 0); /* reset cur counter */ - } else { - /* same for capture */ - ad1816_write(d, 1, ad1816_read(d, 1) & ~0x4000); - outb(ad1816_capt(d), (inb(ad1816_capt(d)) & ~AD1816_ENABLE)); - if ((inb(ad1816_capt(d)) & AD1816_ENABLE)) { - printf("ad1816: failed to stop read (capture) DMA !\n"); - } - ad1816_write(d, 10, 0); - ad1816_write(d, 11, 0); - } - break; - } + struct mss_info *mss = mix_getdevinfo(m); - return 0; -} +#if NPNP > 0 + if (mss->bd_id == MD_AD1816) ad1816_mixer_set(mss, dev, left, right); else +#endif + mss_mixer_set(mss, dev, left, right); -static void -ad1816_intr(int unit) -{ - snddev_info *d = &pcm_info[unit]; - unsigned char c, served = 0; - - /* get interupt status */ - c = inb(ad1816_int(d)); - - /* check for stray interupts */ - if (c & ~(AD1816_INTRCI | AD1816_INTRPI)) { - printf("ad1816: Stray interrupt 0x%x.\n", c); - c = c & (AD1816_INTRCI | AD1816_INTRPI); - outb(ad1816_int(d), c); /* ack it anyway */ - } - /* check for capture interupt */ - if (d->dbuf_in.dl && (c & AD1816_INTRCI)) { - outb(ad1816_int(d), c & ~AD1816_INTRCI); /* ack it */ - if (inb(ad1816_int(d)) & AD1816_INTRCI) - printf("ad1816: Failed to clear cp int !!!\n"); - dsp_rdintr(d); - served |= AD1816_INTRCI; /* cp served */ - } - /* check for playback interupt */ - if (d->dbuf_out.dl && (c & AD1816_INTRPI)) { - outb(ad1816_int(d), c & ~AD1816_INTRPI); /* ack it */ - if ((inb(ad1816_int(d)) & AD1816_INTRPI) != 0) - printf("ad1816: Failed to clear pb int !!!\n"); - dsp_wrintr(d); - served |= AD1816_INTRPI; /* pb served */ - } - if (served == 0) { - /* this probably means this is not a (working) ad1816 chip, */ - /* or an error in dma handling */ - printf("ad1816: raised an interrupt without reason 0x%x.\n", c); - outb(ad1816_int(d), 0); /* Clear interrupt status anyway */ - } + return left | (right << 8); } static int -ad1816_wait_init(snddev_info * d, int x) +mssmix_setrecsrc(snd_mixer *m, u_int32_t src) { - int n = 0; /* to shut up the compiler... */ + struct mss_info *mss = mix_getdevinfo(m); - for (; x--;) - if (((n = (inb(ad1816_ale(d)) & AD1816_BUSY))) == 0) - DELAY(10); - else - return n; - printf("ad1816_wait_init failed 0x%02x.\n", inb(ad1816_ale(d))); - return n; +#if NPNP > 0 + if (mss->bd_id == MD_AD1816) src = ad1816_set_recsrc(mss, src); else +#endif + src = mss_set_recsrc(mss, src); + return src; } -static unsigned short -ad1816_read(snddev_info * d, unsigned int reg) +static int +ymmix_init(snd_mixer *m) { - int flags; - u_short x; + struct mss_info *mss = mix_getdevinfo(m); + + mssmix_init(m); + mix_setdevs(m, mix_getdevs(m) | SOUND_MASK_VOLUME | SOUND_MASK_MIC); + /* Set master volume */ + conf_wr(mss, OPL3SAx_VOLUMEL, 7); + conf_wr(mss, OPL3SAx_VOLUMER, 7); - /* we don't want to be blocked here */ - flags = spltty(); - if (ad1816_wait_init(d, 100) == 0) { - printf("ad1816_read: chip timeout before read.\n"); - return 0; - } - outb(ad1816_ale(d), (u_char) 0); - outb(ad1816_ale(d), (u_char) (reg & AD1816_ALEMASK)); - if (ad1816_wait_init(d, 100) == 0) { - printf("ad1816_read: chip timeout during read.\n"); return 0; - } - x = (inb(ad1816_high(d)) << 8) | inb(ad1816_low(d)); - splx(flags); - return x; } -static void -ad1816_write(snddev_info * d, unsigned int reg, unsigned short data) +static int +ymmix_set(snd_mixer *m, unsigned dev, unsigned left, unsigned right) { - int flags; - - flags = spltty(); - if (ad1816_wait_init(d, 100) == 0) { - printf("ad1816_write: chip timeout before write.\n"); - return; - } - outb(ad1816_ale(d), (u_char) (reg & AD1816_ALEMASK)); - outb(ad1816_low(d), (u_char) (data & 0x000000ff)); - outb(ad1816_high(d), (u_char) ((data & 0x0000ff00) >> 8)); - splx(flags); + struct mss_info *mss = mix_getdevinfo(m); + int t; + + switch (dev) { + case SOUND_MIXER_VOLUME: + if (left) t = 15 - (left * 15) / 100; + else t = 0x80; /* mute */ + conf_wr(mss, OPL3SAx_VOLUMEL, t); + if (right) t = 15 - (right * 15) / 100; + else t = 0x80; /* mute */ + conf_wr(mss, OPL3SAx_VOLUMER, t); + break; + + case SOUND_MIXER_MIC: + t = left; + if (left) t = 31 - (left * 31) / 100; + else t = 0x80; /* mute */ + conf_wr(mss, OPL3SAx_MIC, t); + break; + + case SOUND_MIXER_BASS: + case SOUND_MIXER_TREBLE: + /* Later maybe */ + + default: + mss_mixer_set(mss, dev, left, right); + } + + return left | (right << 8); } -#if 0 /* unused right now..., and untested... */ -static void -ad1816_mute(snddev_info * d) +static int +ymmix_setrecsrc(snd_mixer *m, u_int32_t src) { - ad1816_write(d, 14, ad1816_read(d, 14) | 0x8000 | 0x80); + struct mss_info *mss = mix_getdevinfo(m); + src = mss_set_recsrc(mss, src); + return src; } -static void -ad1816_unmute(snddev_info * d) +/* channel interface */ +static void * +msschan_init(void *devinfo, snd_dbuf *b, pcm_channel *c, int dir) { - ad1816_write(d, 14, ad1816_read(d, 14) & ~(0x8000 | 0x80)); + struct mss_info *mss = devinfo; + struct mss_chinfo *ch = (dir == PCMDIR_PLAY)? &mss->pch : &mss->rch; + + ch->parent = mss; + ch->channel = c; + ch->buffer = b; + ch->buffer->bufsize = DSP_BUFFSIZE; + if (chn_allocbuf(ch->buffer, mss->parent_dmat) == -1) return NULL; + return ch; } -#endif - -/* only one rec source is possible */ static int -ad1816_set_recsrc(snddev_info * d, int mask) +msschan_setdir(void *data, int dir) { - mask &= d->mix_rec_devs; - - switch (mask) { - case SOUND_MASK_LINE: - case SOUND_MASK_LINE3: - ad1816_write(d, 20, (ad1816_read(d, 20) & ~0x7070) | 0x0000); - break; + struct mss_chinfo *ch = data; - case SOUND_MASK_CD: - case SOUND_MASK_LINE1: - ad1816_write(d, 20, (ad1816_read(d, 20) & ~0x7070) | 0x2020); - break; - - case SOUND_MASK_MIC: - default: - ad1816_write(d, 20, (ad1816_read(d, 20) & ~0x7070) | 0x5050); - } - - d->mix_recsrc = mask; - - return 0; /* success */ + ch->buffer->chan = (dir == PCMDIR_PLAY)? ch->parent->pdma : ch->parent->rdma; + ch->dir = dir; + return 0; } -#define AD1816_MUTE 31 /* value for mute */ - static int -ad1816_mixer_set(snddev_info * d, int dev, int value) +msschan_setformat(void *data, u_int32_t format) { - u_char left = (value & 0x000000ff); - u_char right = (value & 0x0000ff00) >> 8; - u_short reg = 0; - - if (dev > 31) - return EINVAL; - - if (!(d->mix_devs & (1 << dev))) - return EINVAL; - - if (left > 100) - left = 100; - if (right > 100) - right = 100; - - d->mix_levels[dev] = left | (right << 8); - - /* Scale volumes */ - left = AD1816_MUTE - (AD1816_MUTE * left) / 100; - right = AD1816_MUTE - (AD1816_MUTE * right) / 100; - - reg = (left << 8) | right; - - /* do channel selective muting if volume is zero */ - if (left == AD1816_MUTE) - reg |= 0x8000; - if (right == AD1816_MUTE) - reg |= 0x0080; - - switch (dev) { - case SOUND_MIXER_VOLUME: /* Register 14 master volume */ - ad1816_write(d, 14, reg); - break; - case SOUND_MIXER_CD: /* Register 15 cd */ - case SOUND_MIXER_LINE1: - ad1816_write(d, 15, reg); - break; - case SOUND_MIXER_SYNTH: /* Register 16 synth */ - ad1816_write(d, 16, reg); - break; - case SOUND_MIXER_PCM: /* Register 4 pcm */ - ad1816_write(d, 4, reg); - break; - case SOUND_MIXER_LINE: - case SOUND_MIXER_LINE3: /* Register 18 line in */ - ad1816_write(d, 18, reg); - break; - case SOUND_MIXER_MIC: /* Register 19 mic volume */ - ad1816_write(d, 19, reg & ~0xff); /* mic is mono */ - break; - case SOUND_MIXER_IGAIN: - /* and now to something completely different ... */ - ad1816_write(d, 20, ((ad1816_read(d, 20) & ~0x0f0f) - | (((AD1816_MUTE - left) / 2) << 8) /* four bits of adc gain */ - | ((AD1816_MUTE - right) / 2))); - break; - default: - printf("ad1816_mixer_set(): unknown device.\n"); - break; - } - - return 0; /* success */ + struct mss_chinfo *ch = data; + +#if NPNP > 0 + if (ch->parent->bd_id == MD_AD1816) ad1816_format(ch, format); else +#endif + mss_format(ch, format); + return 0; } -static void -ad1816_mixer_reset(snddev_info * d) +static int +msschan_setspeed(void *data, u_int32_t speed) { - int i; - - d->mix_devs = AD1816_MIXER_DEVICES; - d->mix_rec_devs = AD1816_REC_DEVICES; + struct mss_chinfo *ch = data; - for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) - if (d->mix_devs & (1 << i)) - ad1816_mixer_set(d, i, default_mixer_levels[i]); - ad1816_set_recsrc(d, SOUND_MASK_MIC); +#if NPNP > 0 + if (ch->parent->bd_id == MD_AD1816) return ad1816_speed(ch, speed); else +#endif + return mss_speed(ch, speed); } -/* Set the playback and capture rates. */ - static int -ad1816_speed(snddev_info * d) +msschan_setblocksize(void *data, u_int32_t blocksize) { - RANGE(d->play_speed,4000,55200); - RANGE(d->rec_speed,4000,55200); + return blocksize; +} - ad1816_write(d, 2, d->play_speed); - ad1816_write(d, 3, d->rec_speed); +static int +msschan_trigger(void *data, int go) +{ + struct mss_chinfo *ch = data; - return d->play_speed; + buf_isadma(ch->buffer, go); +#if NPNP > 0 + if (ch->parent->bd_id == MD_AD1816) ad1816_trigger(ch, go); else +#endif + mss_trigger(ch, go); + return 0; } -/* - * ad1816_format checks that the format is supported (or defaults to AFMT_U8) - * and sets the chip to the desired format. - */ - static int -ad1816_format(snddev_info * d) +msschan_getptr(void *data) { - int oldplay =inb(ad1816_play(d)) & ~AD1816_FORMASK; - int oldrec = inb(ad1816_capt(d)) & ~AD1816_FORMASK; - int play = (d->play_fmt & d->audio_fmt) ? d->play_fmt : AFMT_U8; - int rec = (d->rec_fmt & d->audio_fmt) ? d->rec_fmt : AFMT_U8; - - /* - * check that arg is one of the supported formats in d->format; otherwise - * fallback to AFMT_U8 - */ - - switch (play) { - case AFMT_A_LAW: - outb(ad1816_play(d), oldplay | AD1816_ALAW); - break; - case AFMT_MU_LAW: - outb(ad1816_play(d), oldplay | AD1816_MULAW); - break; - case AFMT_S16_LE: - outb(ad1816_play(d), oldplay | AD1816_S16LE); - break; - case AFMT_S16_BE: - outb(ad1816_play(d), oldplay | AD1816_S16BE); - break; - default: - /* unlikely to happen */ - printf("ad1816: unknown play format. defaulting to U8.\n"); - case AFMT_U8: - outb(ad1816_play(d), oldplay | AD1816_U8); - break; - } - - switch (rec) { - case AFMT_A_LAW: - outb(ad1816_capt(d), oldrec | AD1816_ALAW); - break; - case AFMT_MU_LAW: - outb(ad1816_capt(d), oldrec | AD1816_MULAW); - break; - case AFMT_S16_LE: - outb(ad1816_capt(d), oldrec | AD1816_S16LE); - break; - case AFMT_S16_BE: - outb(ad1816_capt(d), oldrec | AD1816_S16BE); - break; - default: - printf("ad1816: unknown capture format. defaulting to U8.\n"); - case AFMT_U8: - outb(ad1816_capt(d), oldrec | AD1816_U8); - break; - } - - d->play_fmt = play; - d->rec_fmt = rec; - - return (play); + struct mss_chinfo *ch = data; + return buf_isadmaptr(ch->buffer); } -/* - * ad1816_reinit resets codec registers - */ -static void -ad1816_reinit(snddev_info * d) +static pcmchan_caps * +msschan_getcaps(void *data) { - ad1816_write(d, 8, 0x0000); /* reset base and current counter */ - ad1816_write(d, 9, 0x0000); /* for playback and capture */ - ad1816_write(d, 10, 0x0000); - ad1816_write(d, 11, 0x0000); - - if (d->flags & SND_F_STEREO) { - outb((ad1816_play(d)), AD1816_STEREO); /* set playback to stereo */ - outb((ad1816_capt(d)), AD1816_STEREO); /* set capture to stereo */ - } else { - outb((ad1816_play(d)), 0x00); /* set playback to mono */ - outb((ad1816_capt(d)), 0x00); /* set capture to mono */ - } - - ad1816_format(d); - ad1816_speed(d); - - snd_set_blocksize(d); /* update blocksize if user did not force it */ + struct mss_chinfo *ch = data; + + switch(ch->parent->bd_id) { + case MD_OPTI931: + return &opti931_caps; + break; + + case MD_GUSPNP: + return &guspnp_caps; + break; + + case MD_AD1816: + return &ad1816_caps; + break; + + default: + return &mss_caps; + break; + } } -#endif /* NPNP > 0 */ #endif /* NPCM > 0 */ diff --git a/sys/dev/pcm/isa/mss.h b/sys/dev/pcm/isa/mss.h index 20ceaaf..ab41964 100644 --- a/sys/dev/pcm/isa/mss.h +++ b/sys/dev/pcm/isa/mss.h @@ -28,9 +28,9 @@ ahead. * */ -#define io_Index_Addr(d) ((d)->io_base + 4) -#define IA_BUSY 0x80 /* readonly, set when busy */ -#define IA_MCE 0x40 /* the MCE bit. */ +#define MSS_INDEX (0 + 4) +#define MSS_IDXBUSY 0x80 /* readonly, set when busy */ +#define MSS_MCE 0x40 /* the MCE bit. */ /* * the MCE bit must be set whenever the current mode of the * codec is changed; this in particular is true for the @@ -38,15 +38,15 @@ ahead. * Only exception are CEN and PEN which can be changed on the fly. * The DAC output is muted when MCE is set. */ -#define IA_TRD 0x20 /* Transfer request disable */ +#define MSS_TRD 0x20 /* Transfer request disable */ /* * When TRD is set, DMA transfers cease when the INT bit in * the MSS status reg is set. Must be cleared for automode * DMA, set otherwise. */ -#define IA_AMASK 0x1f /* mask for indirect address */ +#define MSS_IDXMASK 0x1f /* mask for indirect address */ -#define io_Indexed_Data(d) ((d)->io_base+1+4) +#define MSS_IDATA (1 + 4) /* * data to be transferred to the indirect register addressed * by index addr. During init and sw. powerdown, cannot be @@ -54,7 +54,7 @@ ahead. * busy flag). */ -#define io_Status(d) ((d)->io_base+2+4) +#define MSS_STATUS (2 + 4) #define IS_CUL 0x80 /* capture upper/lower */ #define IS_CLR 0x40 /* capture left/right */ @@ -67,11 +67,12 @@ ahead. /* * IS_INT is clreared by any write to the status register. */ - +#if 0 #define io_Polled_IO(d) ((d)->io_base+3+4) /* * this register is used in case of polled i/o */ +#endif /* * The MSS has a set of 16 (or 32 depending on the model) indirect @@ -104,18 +105,22 @@ ahead. #define BD_F_MCE_BIT 0x0001 #define BD_F_IRQ_OK 0x0002 #define BD_F_TMR_RUN 0x0004 +#define BD_F_MSS_OFFSET 0x0008 /* offset mss writes by -4 */ +#define BD_F_DUPLEX 0x0010 /* AD1816 register macros */ -#define ad1816_ale(d) ((d)->io_base+0) /* indirect reg access */ -#define ad1816_int(d) ((d)->io_base+1) /* interupt status */ -#define ad1816_low(d) ((d)->io_base+2) /* indirect low byte */ -#define ad1816_high(d) ((d)->io_base+3) /* indirect high byte */ -/* unused */ -#define ad1816_pioD(d) ((d)->io_base+4) /* PIO debug */ -#define ad1816_pios(d) ((d)->io_base+5) /* PIO status */ -#define ad1816_piod(d) ((d)->io_base+6) /* PIO data */ -/* end of unused */ +#define AD1816_ALE 0 /* indirect reg access */ +#define AD1816_INT 1 /* interupt status */ +#define AD1816_LOW 2 /* indirect low byte */ +#define AD1816_HIGH 3 /* indirect high byte */ + +#if 0 +#define ad1816_pioD(d) ((d)->io_base+4) /* PIO debug */ +#define ad1816_pios(d) ((d)->io_base+5) /* PIO status */ +#define ad1816_piod(d) ((d)->io_base+6) /* PIO data */ +#endif + /* values for playback/capture config: bits: 0 enable/disable 1 pio/dma @@ -127,19 +132,22 @@ ahead. 01 8bit alaw (comp) 11 16bit be (uncomp) */ -#define ad1816_play(d) ((d)->io_base+8) /* playback config */ -#define ad1816_capt(d) ((d)->io_base+9) /* capture config */ + +#define AD1816_PLAY 8 /* playback config */ +#define AD1816_CAPT 9 /* capture config */ #define AD1816_BUSY 0x80 /* chip is busy */ #define AD1816_ALEMASK 0x3F /* mask for indirect adr. */ -/* unusud */ + +#if 0 #define AD1816_INTRSI 0x01 /* sb intr */ #define AD1816_INTRGI 0x02 /* game intr */ #define AD1816_INTRRI 0x04 /* ring intr */ #define AD1816_INTRDI 0x08 /* dsp intr */ #define AD1816_INTRVI 0x10 /* vol intr */ #define AD1816_INTRTI 0x20 /* timer intr */ -/* used again */ +#endif + #define AD1816_INTRCI 0x40 /* capture intr */ #define AD1816_INTRPI 0x80 /* playback intr */ /* PIO stuff is not supplied here */ @@ -155,13 +163,16 @@ ahead. #define AD1816_S16BE 0x30 /* 16 bit linear big endian */ #define AD1816_FORMASK 0x38 /* format mask */ + + + /* * sound/ad1848_mixer.h - * + * * Definitions for the mixer of AD1848 and compatible codecs. - * + * * Copyright by Hannu Savolainen 1994 - * + * * 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 @@ -169,7 +180,7 @@ ahead. * 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 @@ -281,24 +292,148 @@ MIX_NONE(SOUND_MIXER_LINE3), (SOUND_MASK_VOLUME | SOUND_MASK_PCM | SOUND_MASK_SYNTH | \ SOUND_MASK_LINE | SOUND_MASK_MIC | SOUND_MASK_CD | SOUND_MASK_IGAIN) -static u_short default_mixer_levels[SOUND_MIXER_NRDEVICES] = { - 0x5a5a, /* Master Volume */ - 0x3232, /* Bass */ - 0x3232, /* Treble */ - 0x4b4b, /* FM */ - 0x4040, /* PCM */ - 0x4b4b, /* PC Speaker */ - 0x2020, /* Ext Line */ - 0x4040, /* Mic */ - 0x4b4b, /* CD */ - 0x0000, /* Recording monitor */ - 0x4b4b, /* SB PCM */ - 0x4b4b, /* Recording level */ - 0x2525, /* Input gain */ - 0x0000, /* Output gain */ - /* 0x4040, Line1 */ - 0x0000, /* Line1 */ - 0x0000, /* Line2 */ - 0x1515 /* Line3 (usually line in)*/ -}; +/*- + * Copyright (c) 1999 Doug Rabson + * 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. + * + * $Id$ + */ + +/* + * Register definitions for the Yamaha OPL3-SA[23x]. + */ +#define OPL3SAx_POWER 0x01 /* Power Management (R/W) */ +#define OPL3SAx_POWER_PDX 0x01 /* Set to 1 to halt oscillator */ +#define OPL3SAx_POWER_PDN 0x02 /* Set to 1 to power down */ +#define OPL3SAx_POWER_PSV 0x04 /* Set to 1 to power save */ +#define OPL3SAx_POWER_ADOWN 0x20 /* Analog power (?) */ + +#define OPL3SAx_SYSTEM 0x02 /* System control (R/W) */ +#define OPL3SAx_SYSTEM_VZE 0x01 /* I2S audio routing */ +#define OPL3SAx_SYSTEM_IDSEL 0x03 /* SB compat version select */ +#define OPL3SAx_SYSTEM_SBHE 0x80 /* 0 for AT bus, 1 for XT bus */ + +#define OPL3SAx_IRQCONF 0x03 /* Interrupt configuration (R/W */ +#define OPL3SAx_IRQCONF_WSSA 0x01 /* WSS interrupts through IRQA */ +#define OPL3SAx_IRQCONF_SBA 0x02 /* WSS interrupts through IRQA */ +#define OPL3SAx_IRQCONF_MPUA 0x04 /* WSS interrupts through IRQA */ +#define OPL3SAx_IRQCONF_OPL3A 0x08 /* WSS interrupts through IRQA */ +#define OPL3SAx_IRQCONF_WSSB 0x10 /* WSS interrupts through IRQB */ +#define OPL3SAx_IRQCONF_SBB 0x20 /* WSS interrupts through IRQB */ +#define OPL3SAx_IRQCONF_MPUB 0x40 /* WSS interrupts through IRQB */ +#define OPL3SAx_IRQCONF_OPL3B 0x80 /* WSS interrupts through IRQB */ + +#define OPL3SAx_IRQSTATUSA 0x04 /* Interrupt (IRQ-A) Status (RO) */ +#define OPL3SAx_IRQSTATUSB 0x05 /* Interrupt (IRQ-B) Status (RO) */ +#define OPL3SAx_IRQSTATUS_PI 0x01 /* Playback Flag of CODEC */ +#define OPL3SAx_IRQSTATUS_CI 0x02 /* Recording Flag of CODEC */ +#define OPL3SAx_IRQSTATUS_TI 0x04 /* Timer Flag of CODEC */ +#define OPL3SAx_IRQSTATUS_SB 0x08 /* SB compat Playback Interrupt Flag */ +#define OPL3SAx_IRQSTATUS_MPU 0x10 /* MPU401 Interrupt Flag */ +#define OPL3SAx_IRQSTATUS_OPL3 0x20 /* Internal FM Timer Flag */ +#define OPL3SAx_IRQSTATUS_MV 0x40 /* HW Volume Interrupt Flag */ +#define OPL3SAx_IRQSTATUS_PI 0x01 /* Playback Flag of CODEC */ +#define OPL3SAx_IRQSTATUS_CI 0x02 /* Recording Flag of CODEC */ +#define OPL3SAx_IRQSTATUS_TI 0x04 /* Timer Flag of CODEC */ +#define OPL3SAx_IRQSTATUS_SB 0x08 /* SB compat Playback Interrupt Flag */ +#define OPL3SAx_IRQSTATUS_MPU 0x10 /* MPU401 Interrupt Flag */ +#define OPL3SAx_IRQSTATUS_OPL3 0x20 /* Internal FM Timer Flag */ +#define OPL3SAx_IRQSTATUS_MV 0x40 /* HW Volume Interrupt Flag */ + +#define OPL3SAx_DMACONF 0x06 /* DMA configuration (R/W) */ +#define OPL3SAx_DMACONF_WSSPA 0x01 /* WSS Playback on DMA-A */ +#define OPL3SAx_DMACONF_WSSRA 0x02 /* WSS Recording on DMA-A */ +#define OPL3SAx_DMACONF_SBA 0x02 /* SB Playback on DMA-A */ +#define OPL3SAx_DMACONF_WSSPB 0x10 /* WSS Playback on DMA-A */ +#define OPL3SAx_DMACONF_WSSRB 0x20 /* WSS Recording on DMA-A */ +#define OPL3SAx_DMACONF_SBB 0x20 /* SB Playback on DMA-A */ + +#define OPL3SAx_VOLUMEL 0x07 /* Master Volume Left (R/W) */ +#define OPL3SAx_VOLUMEL_MVL 0x0f /* Attenuation level */ +#define OPL3SAx_VOLUMEL_MVLM 0x80 /* Mute */ + +#define OPL3SAx_VOLUMER 0x08 /* Master Volume Right (R/W) */ +#define OPL3SAx_VOLUMER_MVR 0x0f /* Attenuation level */ +#define OPL3SAx_VOLUMER_MVRM 0x80 /* Mute */ + +#define OPL3SAx_MIC 0x09 /* MIC Volume (R/W) */ +#define OPL3SAx_VOLUMER_MCV 0x1f /* Attenuation level */ +#define OPL3SAx_VOLUMER_MICM 0x80 /* Mute */ + +#define OPL3SAx_MISC 0x0a /* Miscellaneous */ +#define OPL3SAx_MISC_VER 0x07 /* Version */ +#define OPL3SAx_MISC_MODE 0x08 /* SB or WSS mode */ +#define OPL3SAx_MISC_MCSW 0x10 /* */ +#define OPL3SAx_MISC_VEN 0x80 /* Enable hardware volume control */ + +#define OPL3SAx_WSSDMA 0x0b /* WSS DMA Counter (RW) (4 regs) */ + +#define OPL3SAx_WSSIRQSCAN 0x0f /* WSS Interrupt Scan out/in (R/W) */ +#define OPL3SAx_WSSIRQSCAN_SPI 0x01 +#define OPL3SAx_WSSIRQSCAN_SCI 0x02 +#define OPL3SAx_WSSIRQSCAN_STI 0x04 + +#define OPL3SAx_SBSTATE 0x10 /* SB compat Internal State (R/W) */ +#define OPL3SAx_SBSTATE_SBPDR 0x01 /* SB Power Down Request */ +#define OPL3SAx_SBSTATE_SE 0x02 /* Scan Enable */ +#define OPL3SAx_SBSTATE_SM 0x04 /* Scan Mode */ +#define OPL3SAx_SBSTATE_SS 0x08 /* Scan Select */ +#define OPL3SAx_SBSTATE_SBPDA 0x80 /* SB Power Down Acknowledge */ + +#define OPL3SAx_SBDATA 0x11 /* SB compat State Scan Data (R/W) */ + +#define OPL3SAx_DIGITALPOWER 0x12 /* Digital Partial Power Down (R/W) */ +#define OPL3SAx_DIGITALPOWER_PnP 0x01 +#define OPL3SAx_DIGITALPOWER_SB 0x02 +#define OPL3SAx_DIGITALPOWER_WSSP 0x04 +#define OPL3SAx_DIGITALPOWER_WSSR 0x08 +#define OPL3SAx_DIGITALPOWER_FM 0x10 +#define OPL3SAx_DIGITALPOWER_MCLK0 0x20 +#define OPL3SAx_DIGITALPOWER_MPU 0x40 +#define OPL3SAx_DIGITALPOWER_JOY 0x80 + +#define OPL3SAx_ANALOGPOWER 0x13 /* Analog Partial Power Down (R/W) */ +#define OPL3SAx_ANALOGPOWER_WIDE 0x01 +#define OPL3SAx_ANALOGPOWER_SBDAC 0x02 +#define OPL3SAx_ANALOGPOWER_DA 0x04 +#define OPL3SAx_ANALOGPOWER_AD 0x08 +#define OPL3SAx_ANALOGPOWER_FMDAC 0x10 + +#define OPL3SAx_WIDE 0x14 /* Enhanced control(WIDE) (R/W) */ +#define OPL3SAx_WIDE_WIDEL 0x07 /* Wide level on Left Channel */ +#define OPL3SAx_WIDE_WIDER 0x70 /* Wide level on Right Channel */ + +#define OPL3SAx_BASS 0x15 /* Enhanced control(BASS) (R/W) */ +#define OPL3SAx_BASS_BASSL 0x07 /* Bass level on Left Channel */ +#define OPL3SAx_BASS_BASSR 0x70 /* Bass level on Right Channel */ + +#define OPL3SAx_TREBLE 0x16 /* Enhanced control(TREBLE) (R/W) */ +#define OPL3SAx_TREBLE_TREBLEL 0x07 /* Treble level on Left Channel */ +#define OPL3SAx_TREBLE_TREBLER 0x70 /* Treble level on Right Channel */ + +#define OPL3SAx_HWVOL 0x17 /* HW Volume IRQ Configuration (R/W) */ +#define OPL3SAx_HWVOL_IRQA 0x10 /* HW Volume IRQ on IRQ-A */ +#define OPL3SAx_HWVOL_IRQB 0x20 /* HW Volume IRQ on IRQ-B */ + diff --git a/sys/dev/pcm/isa/sb.c b/sys/dev/pcm/isa/sb.c index 7544dae..2a8d336 100644 --- a/sys/dev/pcm/isa/sb.c +++ b/sys/dev/pcm/isa/sb.c @@ -1,1366 +1,1117 @@ /* - * sound/sb_dsp.c - * - * driver for the SoundBlaster and clones. - * + * Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk> * Copyright 1997,1998 Luigi Rizzo. * * Derived from files in the Voxware 3.5 distribution, * Copyright by Hannu Savolainen 1994, under the same copyright * conditions. - * + * 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. + * 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. - * - */ - -/* - * use this as a template file for board-specific drivers. - * The next two lines (and the final #endif) are in all drivers: + * 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. + * + * $Id$ */ -#include <i386/isa/snd/sound.h> +#include <dev/pcm/sound.h> #if NPCM > 0 -/* - * Begin with the board-specific include files... - */ - #define __SB_MIXER_C__ /* XXX warning... */ -#include <i386/isa/snd/sbcard.h> - -/* - * then prototypes of functions which go in the snddev_info - * (usually static, unless they are shared by other modules)... - */ +#include <dev/pcm/isa/sb.h> + +/* channel interface */ +static void *sbchan_init(void *devinfo, snd_dbuf *b, pcm_channel *c, int dir); +static int sbchan_setdir(void *data, int dir); +static int sbchan_setformat(void *data, u_int32_t format); +static int sbchan_setspeed(void *data, u_int32_t speed); +static int sbchan_setblocksize(void *data, u_int32_t blocksize); +static int sbchan_trigger(void *data, int go); +static int sbchan_getptr(void *data); +static pcmchan_caps *sbchan_getcaps(void *data); + +static pcmchan_caps sb_playcaps = { + 4000, 22050, + AFMT_U8, + AFMT_U8 +}; -static int sb_probe(struct isa_device *dev); -static int sb_attach(struct isa_device *dev); +static pcmchan_caps sb_reccaps = { + 4000, 13000, + AFMT_U8, + AFMT_U8 +}; -static d_open_t sb_dsp_open; -static d_close_t sb_dsp_close; -static d_ioctl_t sb_dsp_ioctl; -static irq_proc_t sb_intr; -static snd_callback_t sb_callback; +static pcmchan_caps sbpro_playcaps = { + 4000, 45000, + AFMT_STEREO | AFMT_U8, + AFMT_STEREO | AFMT_U8 +}; -/* - * and prototypes for other private functions defined in this module. - */ +static pcmchan_caps sbpro_reccaps = { + 4000, 15000, + AFMT_STEREO | AFMT_U8, + AFMT_STEREO | AFMT_U8 +}; -static void sb_dsp_init(snddev_info *d, struct isa_device *dev); -static void sb_mix_init(snddev_info *d); -static int sb_mixer_set(snddev_info *d, int dev, int value); -static int dsp_speed(snddev_info *d); -static void sb_mixer_reset(snddev_info *d); +static pcmchan_caps sb16_playcaps = { + 5000, 45000, + AFMT_STEREO | AFMT_S16_LE, + AFMT_STEREO | AFMT_S16_LE +}; -u_int sb_get_byte(int io_base); -int ess_write(int io_base, u_char reg, int val); -int ess_read(int io_base, u_char reg); +static pcmchan_caps sb16_reccaps = { + 5000, 45000, + AFMT_STEREO | AFMT_U8, + AFMT_STEREO | AFMT_U8 +}; -/* - * Then put here the descriptors for the various boards supported - * by this module, properly initialized. - */ +static pcmchan_caps ess_playcaps = { + 5000, 49000, + AFMT_STEREO | AFMT_U8 | AFMT_S16_LE, + AFMT_STEREO | AFMT_S16_LE +}; -snddev_info sb_op_desc = { - "basic soundblaster", +static pcmchan_caps ess_reccaps = { + 5000, 49000, + AFMT_STEREO | AFMT_U8 | AFMT_S16_LE, + AFMT_STEREO | AFMT_S16_LE +}; - SNDCARD_SB, - sb_probe, - sb_attach, +static pcm_channel sb_chantemplate = { + sbchan_init, + sbchan_setdir, + sbchan_setformat, + sbchan_setspeed, + sbchan_setblocksize, + sbchan_trigger, + sbchan_getptr, + sbchan_getcaps, +}; - sb_dsp_open, - sb_dsp_close /* sb_close */, - NULL /* use generic sndread */, - NULL /* use generic sndwrite */, - sb_dsp_ioctl, - sndselect, +#define PLAIN_SB16(x) ((((x)->bd_flags) & (BD_F_SB16|BD_F_SB16X)) == BD_F_SB16) - sb_intr, - sb_callback, +struct sb_info; - DSP_BUFFSIZE, /* bufsize */ +struct sb_chinfo { + struct sb_info *parent; + pcm_channel *channel; + snd_dbuf *buffer; + int dir; + u_int32_t fmt; +}; - AFMT_STEREO | AFMT_U8, /* audio format */ +struct sb_info { + struct resource *io_base; /* I/O address for the board */ + int io_rid; + struct resource *irq; + int irq_rid; + struct resource *drq1; /* play */ + int drq1_rid; + struct resource *drq2; /* rec */ + int drq2_rid; + bus_dma_tag_t parent_dmat; + + int dma16, dma8; + int bd_id; + u_long bd_flags; /* board-specific flags */ + struct sb_chinfo pch, rch; +}; -} ; +static int sb_rd(struct sb_info *sb, int reg); +static void sb_wr(struct sb_info *sb, int reg, u_int8_t val); +static int sb_dspready(struct sb_info *sb); +static int sb_cmd(struct sb_info *sb, u_char val); +static int sb_cmd1(struct sb_info *sb, u_char cmd, int val); +static int sb_cmd2(struct sb_info *sb, u_char cmd, int val); +static u_int sb_get_byte(struct sb_info *sb); +static int ess_write(struct sb_info *sb, u_char reg, int val); +static int ess_read(struct sb_info *sb, u_char reg); /* - * Then the file continues with the body of all functions - * directly referenced in the descriptor. + * in the SB, there is a set of indirect "mixer" registers with + * address at offset 4, data at offset 5 */ +static void sb_setmixer(struct sb_info *sb, u_int port, u_int value); +static int sb_getmixer(struct sb_info *sb, u_int port); + +static void sb_intr(void *arg); +static int sb_init(device_t dev, struct sb_info *sb); +static int sb_reset_dsp(struct sb_info *sb); + +static int sb_format(struct sb_chinfo *ch, u_int32_t format); +static int sb_speed(struct sb_chinfo *ch, int speed); +static int sb_start(struct sb_chinfo *ch); +static int sb_stop(struct sb_chinfo *ch); + +static int sbmix_init(snd_mixer *m); +static int sbmix_set(snd_mixer *m, unsigned dev, unsigned left, unsigned right); +static int sbmix_setrecsrc(snd_mixer *m, u_int32_t src); + +static snd_mixer sb_mixer = { + "SoundBlaster mixer", + sbmix_init, + sbmix_set, + sbmix_setrecsrc, +}; + +static devclass_t pcm_devclass; /* - * the probe routine for the SoundBlaster only consists in - * resetting the dsp and testing if it is there. - * Version detection etc. will be done at attach time. + * Common code for the midi and pcm functions + * + * sb_cmd write a single byte to the CMD port. + * sb_cmd1 write a CMD + 1 byte arg + * sb_cmd2 write a CMD + 2 byte arg + * sb_get_byte returns a single byte from the DSP data port * - * Remember, ISA probe routines are supposed to return the - * size of io space used. + * ess_write is actually sb_cmd1 + * ess_read access ext. regs via sb_cmd(0xc0, reg) followed by sb_get_byte */ static int -sb_probe(struct isa_device *dev) +port_rd(struct resource *port, int off) { - bzero(&pcm_info[dev->id_unit], sizeof(pcm_info[dev->id_unit]) ); - if (dev->id_iobase == -1) { - dev->id_iobase = 0x220; - BVDDB(printf("sb_probe: no address supplied, try defaults (0x220,0x240)\n");) - if (snd_conflict(dev->id_iobase)) - dev->id_iobase = 0x240; - } - if (snd_conflict(dev->id_iobase)) - return 0 ; - - if (sb_reset_dsp(dev->id_iobase)) - return 16 ; /* the SB uses 16 registers... */ - else - return 0; + return bus_space_read_1(rman_get_bustag(port), + rman_get_bushandle(port), + off); +} + +static void +port_wr(struct resource *port, int off, u_int8_t data) +{ + return bus_space_write_1(rman_get_bustag(port), + rman_get_bushandle(port), + off, data); } static int -sb_attach(struct isa_device *dev) +sb_rd(struct sb_info *sb, int reg) { - snddev_info *d = &pcm_info[dev->id_unit] ; + return port_rd(sb->io_base, reg); +} - dev->id_alive = 16 ; /* number of io ports */ - /* should be already set but just in case... */ - sb_dsp_init(d, dev); - return 0 ; +static void +sb_wr(struct sb_info *sb, int reg, u_int8_t val) +{ + port_wr(sb->io_base, reg, val); } -/* - * here are the main routines from the switches. - */ +static int +sb_dspready(struct sb_info *sb) +{ + return ((sb_rd(sb, SBDSP_STATUS) & 0x80) == 0); +} -/* - * Unlike MSS, the sb only supports a single open (does not mean - * that only a single process is using it, since it can fork - * afterwards, or pass the descriptor to another process). - * - */ static int -sb_dsp_open(dev_t i_dev, int flags, int mode, struct proc * p) +sb_dspwr(struct sb_info *sb, u_char val) { - snddev_info *d; - int unit ; - int dev; + int i; - dev = minor(i_dev); - unit = dev >> 4 ; - d = &pcm_info[unit] ; + for (i = 0; i < 1000; i++) { + if (sb_dspready(sb)) { + sb_wr(sb, SBDSP_CMD, val); + return 1; + } + if (i > 10) DELAY((i > 100)? 1000 : 10); + } + printf("sb_dspwr(0x%02x) timed out.\n", val); + return 0; +} - DEB(printf("<%s>%d : open\n", d->name, unit)); +static int +sb_cmd(struct sb_info *sb, u_char val) +{ +#if 0 + printf("sb_cmd: %x\n", val); +#endif + return sb_dspwr(sb, val); +} - if (d->flags & SND_F_BUSY) { - DEB(printf("<%s>%d open: device busy\n", d->name, unit)); - return EBUSY ; - } +static int +sb_cmd1(struct sb_info *sb, u_char cmd, int val) +{ +#if 0 + printf("sb_cmd1: %x, %x\n", cmd, val); +#endif + if (sb_dspwr(sb, cmd)) { + return sb_dspwr(sb, val & 0xff); + } else return 0; +} - d->wsel.si_pid = 0; - d->wsel.si_flags = 0; +static int +sb_cmd2(struct sb_info *sb, u_char cmd, int val) +{ +#if 0 + printf("sb_cmd2: %x, %x\n", cmd, val); +#endif + if (sb_dspwr(sb, cmd)) { + return sb_dspwr(sb, val & 0xff) && + sb_dspwr(sb, (val >> 8) & 0xff); + } else return 0; +} - d->rsel.si_pid = 0; - d->rsel.si_flags = 0; +/* + * in the SB, there is a set of indirect "mixer" registers with + * address at offset 4, data at offset 5 + */ +static void +sb_setmixer(struct sb_info *sb, u_int port, u_int value) +{ + u_long flags; + + flags = spltty(); + sb_wr(sb, SB_MIX_ADDR, (u_char) (port & 0xff)); /* Select register */ + DELAY(10); + sb_wr(sb, SB_MIX_DATA, (u_char) (value & 0xff)); + DELAY(10); + splx(flags); +} - d->dbuf_out.total = d->dbuf_out.prev_total = 0 ; - d->dbuf_in.total = d->dbuf_in.prev_total = 0 ; +static int +sb_getmixer(struct sb_info *sb, u_int port) +{ + int val; + u_long flags; - d->flags = 0 ; - d->bd_flags &= ~BD_F_HISPEED ; + flags = spltty(); + sb_wr(sb, SB_MIX_ADDR, (u_char) (port & 0xff)); /* Select register */ + DELAY(10); + val = sb_rd(sb, SB_MIX_DATA); + DELAY(10); + splx(flags); - switch ( dev & 0xf ) { - case SND_DEV_DSP16 : - if ((d->audio_fmt & AFMT_S16_LE) == 0) { - printf("sorry, 16-bit not supported on SB %d.%02d\n", - (d->bd_id >>8) & 0xff, d->bd_id & 0xff); - return ENXIO; - } - d->play_fmt = d->rec_fmt = AFMT_S16_LE ; - break; - case SND_DEV_AUDIO : - d->play_fmt = d->rec_fmt = AFMT_MU_LAW ; - break ; - case SND_DEV_DSP : - d->play_fmt = d->rec_fmt = AFMT_U8 ; - break ; - } - /* - * since the SB is not simmetric, I use the open mode to select - * which channel should be privileged, and disable I/O in the - * other direction. - * In case the board is opened RW, we don't have enough - * information on what to do. Temporarily, privilege the - * playback channel, which is used more often, and set the other - * one to U8. - */ - if ( (flags & FREAD) == 0) /* opened write only */ - d->rec_fmt = 0 ; - else if ( (flags & FWRITE) == 0) /* opened read only */ - d->play_fmt = 0 ; - else /* opened read/write */ - d->rec_fmt = (d->play_fmt == AFMT_S16_LE) ? AFMT_U8 : AFMT_S16_LE ; - - d->flags |= SND_F_BUSY ; - d->play_speed = d->rec_speed = DSP_DEFAULT_SPEED ; - - if (flags & O_NONBLOCK) - d->flags |= SND_F_NBIO ; - - sb_reset_dsp(d->io_base); - if (d->bd_flags & BD_F_ESS) - sb_cmd(d->io_base, 0xc6 ); /* enable extended ESS mode */ - ask_init(d); - - return 0; + return val; } -static int -sb_dsp_close(dev_t i_dev, int flags, int mode, struct proc * p) +static u_int +sb_get_byte(struct sb_info *sb) { - int unit; - int dev; - snddev_info *d; - u_long s; + int i; - dev = minor(i_dev); - unit = dev >> 4 ; - d = &pcm_info[unit] ; - - s = spltty(); - d->flags |= SND_F_CLOSING ; - splx(s); - snd_flush(d); + for (i = 1000; i > 0; i--) { + if (sb_rd(sb, DSP_DATA_AVAIL) & 0x80) + return sb_rd(sb, DSP_READ); + else + DELAY(20); + } + return 0xffff; +} - sb_cmd(d->io_base, DSP_CMD_SPKOFF ); /* XXX useless ? */ +static int +ess_write(struct sb_info *sb, u_char reg, int val) +{ + return sb_cmd1(sb, reg, val); +} - d->flags = 0 ; - return 0 ; +static int +ess_read(struct sb_info *sb, u_char reg) +{ + return (sb_cmd(sb, 0xc0) && sb_cmd(sb, reg))? sb_get_byte(sb) : 0xffff; } static int -sb_dsp_ioctl(dev_t i_dev, u_long cmd, caddr_t arg, int mode, struct proc * p) +sb_reset_dsp(struct sb_info *sb) { - int unit; - int dev; - snddev_info *d; - - dev = minor(i_dev); - unit = dev >> 4 ; - d = &pcm_info[unit] ; - - /* - * handle mixer calls first. Reads are in the default handler, - * so do not bother about them. - */ - if ( (cmd & MIXER_WRITE(0)) == MIXER_WRITE(0) ) - return sb_mixer_set(d, cmd & 0xff, *(int *)arg) ; - - /* - * for the remaining functions, use the default handler. - * ENOSYS means that the default handler should take care - * of implementing the ioctl. - */ - - return ENOSYS ; + sb_wr(sb, SBDSP_RST, 3); + DELAY(100); + sb_wr(sb, SBDSP_RST, 0); + if (sb_get_byte(sb) != 0xAA) { + DEB(printf("sb_reset_dsp 0x%lx failed\n", + rman_get_start(d->io_base))); + return ENXIO; /* Sorry */ + } + if (sb->bd_flags & BD_F_ESS) sb_cmd(sb, 0xc6); + return 0; } static void -sb_intr(int unit) +sb_release_resources(struct sb_info *sb, device_t dev) { - snddev_info *d = &pcm_info[unit]; - int reason = 3, c=1, io_base = d->io_base; - - DEB(printf("got sb_intr for unit %d, flags 0x%08lx\n", unit, d->flags)); - - /* - * SB < 4.0 is half duplex and has only 1 bit for int source, - * so we fake it. SB 4.x (SB16) has the int source in a separate - * register. - * The Vibra16X has separate flags for 8 and 16 bit transfers, but - * I have no idea how to tell capture from playback interrupts... - */ -#define PLAIN_SB16(x) ( ( (x) & (BD_F_SB16|BD_F_SB16X) ) == BD_F_SB16) -again: - if (d->bd_flags & BD_F_SB16) { - c = sb_getmixer(io_base, IRQ_STAT); - /* this tells us if the source is 8-bit or 16-bit dma. We - * have to check the io channel to map it to read or write... - */ - reason = 0 ; - if ( c & 1 ) { /* 8-bit dma */ - if (d->play_fmt == AFMT_U8 || d->play_fmt == AFMT_MU_LAW ) - reason |= 1; - if (d->rec_fmt == AFMT_U8 || d->rec_fmt == AFMT_MU_LAW ) - reason |= 2; - } - if ( c & 2 ) { /* 16-bit dma */ - if (d->play_fmt == AFMT_S16_LE) - reason |= 1; - if (d->rec_fmt == AFMT_S16_LE) - reason |= 2; - } - } - /* XXX previous location of ack... */ - DEB(printf("sb_intr, flags 0x%08lx reason %d c 0x%x\n", - d->flags, reason, c)); - if ( reason & 1 ) { /* possibly a write interrupt */ - if ( d->dbuf_out.dl ) - dsp_wrintr(d); - } - if ( reason & 2 ) { - if ( d->dbuf_in.dl ) - dsp_rdintr(d); - } - if ( c & 2 ) - inb(DSP_DATA_AVL16); /* 16-bit int ack */ - if (c & 1) - inb(DSP_DATA_AVAIL); /* 8-bit int ack */ - - /* - * the sb16 might have multiple sources etc. - */ - if ((d->bd_flags & BD_F_SB16) && (c & 3)) - goto again; + /* should we bus_teardown_intr here? */ + if (sb->irq) { + bus_release_resource(dev, SYS_RES_IRQ, sb->irq_rid, sb->irq); + sb->irq = 0; + } + if (sb->drq1) { + bus_release_resource(dev, SYS_RES_DRQ, sb->drq1_rid, sb->drq1); + sb->drq1 = 0; + } + if (sb->drq2) { + bus_release_resource(dev, SYS_RES_DRQ, sb->drq2_rid, sb->drq2); + sb->drq2 = 0; + } + if (sb->io_base) { + bus_release_resource(dev, SYS_RES_IOPORT, sb->io_rid, + sb->io_base); + sb->io_base = 0; + } + free(sb, M_DEVBUF); } -/* - * device-specific function called back from the dma module. - * The reason of the callback is the second argument. - * NOTE: during operations, some ioctl can be called to change - * settings (e.g. speed, channels, format), and the default - * ioctl handler will just record the change and set the - * flag SND_F_INIT. The callback routine is in charge of applying - * the changes at the next convenient time (typically, at the - * start of operations). For full duplex devices, in some cases the - * init requires both channels to be idle. - */ static int -sb_callback(snddev_info *d, int reason) +sb_alloc_resources(struct sb_info *sb, device_t dev) { - int rd = reason & SND_CB_RD ; - snd_dbuf *b = (rd) ? & (d->dbuf_in) : & (d->dbuf_out) ; - int l = b->dl ; - - switch (reason & SND_CB_REASON_MASK) { - case SND_CB_INIT : /* called with int enabled and no pending io */ - /* - * set the speed - */ - dsp_speed(d); - /* - * set the desired DMA blocksize (influences select behaviour) - */ - snd_set_blocksize(d); - /* - * since native mulaw is not present, emulate it. - */ - if ( (d->play_fmt & AFMT_MU_LAW) || (d->rec_fmt & AFMT_MU_LAW) ) - d->flags |= SND_F_XLAT8 ; - else - d->flags &= ~SND_F_XLAT8 ; - - /* - * there are too many flavours of SB for my taste... here i try to do - * the proper initialization for each one. - */ - if (PLAIN_SB16(d->bd_flags)) { - - /* the original SB16 (non-PnP, or PnP, or Vibra16C) - * can do full duplex using one 16-bit channel - * and one 8-bit channel. It needs to be programmed to - * use split format though. - * I DON'T do this for the Vibra16X because I have no idea - * of what needs to be done there... - * - * I use the following algorithm: - * 1. check which direction(s) are active; - * 2. check if we should swap dma channels - * 3. check if we can do the swap. - */ - int swap = 1 ; /* default... */ - - if (d->play_fmt == 0) { - /* do whatever the read channel wants */ - if ( d->rec_fmt == AFMT_S16_LE && d->dbuf_in.chan > 4 ) - swap = 0; - if ( d->rec_fmt != AFMT_S16_LE && d->dbuf_in.chan < 4 ) - swap = 0; - } else { - /* privilege the write channel */ - if ( d->play_fmt == AFMT_S16_LE && d->dbuf_out.chan > 4 ) - swap = 0; - if ( d->play_fmt != AFMT_S16_LE && d->dbuf_out.chan < 4 ) - swap = 0; - if ( d->rec_fmt ) { - /* check for possible config errors. - * This cannot happen at open time since even in - * case of opening rw we privilege the play - * channel. - */ - if (d->rec_fmt == d->play_fmt) { - DDB(printf("sorry, read DMA channel unavailable\n")); - } + if (!sb->io_base) + sb->io_base = bus_alloc_resource(dev, SYS_RES_IOPORT, + &sb->io_rid, 0, ~0, 1, + RF_ACTIVE); + if (!sb->irq) + sb->irq = bus_alloc_resource(dev, SYS_RES_IRQ, + &sb->irq_rid, 0, ~0, 1, + RF_ACTIVE); + if (!sb->drq1) + sb->drq1 = bus_alloc_resource(dev, SYS_RES_DRQ, + &sb->drq1_rid, 0, ~0, 1, + RF_ACTIVE); + if (!sb->drq2 && sb->drq2_rid > 0) + sb->drq2 = bus_alloc_resource(dev, SYS_RES_DRQ, + &sb->drq2_rid, 0, ~0, 1, + RF_ACTIVE); + + if (sb->io_base && sb->drq1 && sb->irq) { + sb->dma8 = rman_get_start(sb->drq1); + isa_dma_acquire(sb->dma8); + isa_dmainit(sb->dma8, DSP_BUFFSIZE); + + if (sb->drq2) { + sb->dma16 = rman_get_start(sb->drq2); + isa_dma_acquire(sb->dma16); + isa_dmainit(sb->dma16, DSP_BUFFSIZE); + } else sb->dma16 = sb->dma8; + + if (sb->dma8 > sb->dma16) { + int tmp = sb->dma16; + sb->dma16 = sb->dma8; + sb->dma8 = tmp; } - } - DEB(printf("sb16: play_fmt %d, rec_fmt %x, swap %d\n", - d->play_fmt, d->rec_fmt, swap);) - if (swap) { - int c = d->dbuf_in.chan ; - d->dbuf_in.chan = d->dbuf_out.chan; - d->dbuf_out.chan = c ; - } - } - else if (d->bd_flags & BD_F_ESS) { - u_char c; + return 0; + } else return ENXIO; +} - DEB(printf("SND_CB_INIT, play_fmt == 0x%x, rec_fmt == 0x%x\n", - (int) d->play_fmt, (int) d->rec_fmt)); +static int +sb_identify_board(device_t dev, struct sb_info *sb) +{ + char *fmt = NULL; + static char buf[64]; - /* autoinit DMA mode */ - if (d->play_fmt) - ess_write(d->io_base, 0xb8, 0x04); - else - ess_write(d->io_base, 0xb8, 0x0e); - - c = (ess_read(d->io_base, 0xa8) & ~0x03) | 0x01; - if ((d->flags & SND_F_STEREO) == 0) - c++; - ess_write(d->io_base, 0xa8, c); /* select mono/stereo */ - ess_write(d->io_base, 0xb9, 2); /* demand 4 bytes/transfer */ - - switch (d->play_fmt ? d->play_fmt : d->rec_fmt) { - case AFMT_S16_LE: - if (d->flags & SND_F_STEREO) { - /* 16 bit stereo */ - if (d->play_fmt) - ess_write(d->io_base, 0xb6, 0x00); - ess_write(d->io_base, 0xb7, 0x71); - ess_write(d->io_base, 0xb7, 0xbc); - } - else { - /* 16 bit mono */ - if (d->play_fmt) - ess_write(d->io_base, 0xb6, 0x00); - ess_write(d->io_base, 0xb7, 0x71); - ess_write(d->io_base, 0xb7, 0xf4); - } - break; - case AFMT_U8: - if (d->flags & SND_F_STEREO) { - /* 8 bit stereo */ - if (d->play_fmt) - ess_write(d->io_base, 0xb6, 0x80); - ess_write(d->io_base, 0xb7, 0x51); - ess_write(d->io_base, 0xb7, 0x98); - } - else { - /* 8 bit mono */ - if (d->play_fmt) - ess_write(d->io_base, 0xb6, 0x80); - ess_write(d->io_base, 0xb7, 0x51); - ess_write(d->io_base, 0xb7, 0xd0); - } - break; - } - ess_write(d->io_base, 0xb1, - ess_read(d->io_base, 0xb1) | 0x50); - ess_write(d->io_base, 0xb2, - ess_read(d->io_base, 0xb1) | 0x50); - } - reset_dbuf(& (d->dbuf_in), SND_CHAN_RD ); - reset_dbuf(& (d->dbuf_out), SND_CHAN_WR ); - break ; - - case SND_CB_START : /* called with int disabled */ - if (d->bd_flags & BD_F_SB16) { - u_char c, c1 ; - - if (d->bd_flags & BD_F_SB16X) { - /* just a guess: on the Vibra16X, the first - * op started takes the first dma channel, - * the second one takes the next... - * The default is to be ready for play. - */ - DEB(printf("start %s -- now dma %d:%d\n", - rd ? "rd" : "wr", - d->dbuf_out.chan, d->dbuf_in.chan);); - /* swap only if both channels are idle - * play: dl=0, since there is no pause; - * rec: rl=0 - */ - if ( rd && d->dbuf_out.dl == 0 && d->dbuf_in.rl == 0 ) { - /* must swap channels, but also save dl */ - int c = d->dbuf_in.chan ; - int dl = d->dbuf_in.dl ; - d->dbuf_in.chan = d->dbuf_out.chan; - d->dbuf_out.chan = c ; - reset_dbuf(& (d->dbuf_in), SND_CHAN_RD ); - reset_dbuf(& (d->dbuf_out), SND_CHAN_WR ); - d->dbuf_in.dl = dl ; - printf("swapped -- now dma %d:%d\n", - d->dbuf_out.chan, d->dbuf_in.chan); - } - } - - /* - * XXX note: c1 and l should be set basing on d->rec_fmt, - * but there is no choice once a 16 or 8-bit channel - * is assigned. This means that if the application - * tries to use a bad format, the sound will not be nice. - */ - if ( b->chan > 4 - || (rd && d->rec_fmt == AFMT_S16_LE) - || (!rd && d->play_fmt == AFMT_S16_LE) - ) { - c = DSP_F16_AUTO | DSP_F16_FIFO_ON | DSP_DMA16 ; - c1 = DSP_F16_SIGNED ; - l /= 2 ; - } else { - c = DSP_F16_AUTO | DSP_F16_FIFO_ON | DSP_DMA8 ; - c1 = 0 ; - } - c |= (rd) ? DSP_F16_ADC : DSP_F16_DAC ; - if (d->flags & SND_F_STEREO) - c1 |= DSP_F16_STEREO ; - - sb_cmd(d->io_base, c ); - sb_cmd3(d->io_base, c1 , l - 1) ; - } else if (d->bd_flags & BD_F_ESS) { - u_long fmt = rd ? d->rec_fmt : d->play_fmt; - - DEB(printf("SND_CB_START: %s (%d)\n", rd ? "rd" : "wr", l)); - if (fmt == AFMT_S16_LE) - l >>= 1; - l--; - if (!rd) - sb_cmd(d->io_base, DSP_CMD_SPKON); - ess_write(d->io_base, 0xa4, l); - ess_write(d->io_base, 0xa5, l >> 8); - ess_write(d->io_base, 0xb8, - ess_read(d->io_base, 0xb8) | (rd ? 0x0f : 0x05)); - } else { /* SBPro -- stereo not supported */ - u_char c ; - if (!rd) - sb_cmd(d->io_base, DSP_CMD_SPKON); - /* code for the SB2 and SB3, only MONO */ - if (d->bd_flags & BD_F_HISPEED) - c = (rd) ? 0x98 : 0x90 ; - else - c = (rd) ? 0x2c : 0x1c ; - if (d->flags & SND_F_STEREO) - sb_setmixer(d->io_base, 0xe, 2 ); - else - sb_setmixer(d->io_base, 0xe, 0 ); - /* - * some ESS extensions -- they can do 16 bits - */ - if ( (rd && d->rec_fmt == AFMT_S16_LE) || - (!rd && d->play_fmt == AFMT_S16_LE) ) { - c |= 1; - l /= 2 ; - } - sb_cmd3(d->io_base, 0x48 , l - 1) ; - sb_cmd(d->io_base, c ) ; - } - break; - - case SND_CB_ABORT : /* XXX */ - case SND_CB_STOP : - { - int cmd = DSP_CMD_DMAPAUSE_8 ; /* default: halt 8 bit chan */ - DEB(printf("SND_CB_XXX: reason 0x%x\n", reason)); - if ( b->chan > 4 - || (rd && d->rec_fmt == AFMT_S16_LE) - || (!rd && d->play_fmt == AFMT_S16_LE) - ) - cmd = DSP_CMD_DMAPAUSE_16 ; - if (d->bd_flags & BD_F_HISPEED) { - sb_reset_dsp(d->io_base); - if (d->bd_flags & BD_F_ESS) - sb_cmd(d->io_base, 0xc6 ); /* enable extended ESS mode */ - d->flags |= SND_F_INIT ; - } else { - sb_cmd(d->io_base, cmd); /* pause dma. */ - /* - * The above seems to have the undocumented side effect of - * blocking the other side as well. If the other - * channel was active (SB16) I have to re-enable it :( - */ - if ( (rd && d->dbuf_out.dl) || - (!rd && d->dbuf_in.dl) ) - sb_cmd(d->io_base, cmd == DSP_CMD_DMAPAUSE_8 ? - 0xd6 : 0xd4); /* continue other dma */ - } - if (d->bd_flags & BD_F_SB16X) { - /* restore possible swapped channels. - * The default is to be ready for play. - * XXX right now, it kills all input on overflow - */ - if ( rd && d->dbuf_out.dl == 0 ) { - /* must swap channels ? */ - int c = d->dbuf_in.chan ; - d->dbuf_in.chan = d->dbuf_out.chan; - d->dbuf_out.chan = c ; - reset_dbuf(& (d->dbuf_in), SND_CHAN_RD ); - reset_dbuf(& (d->dbuf_out), SND_CHAN_WR ); - printf("restored -- now dma %d:%d\n", - d->dbuf_out.chan, d->dbuf_in.chan); - } - } - } - DEB( sb_cmd(d->io_base, DSP_CMD_SPKOFF) ); /* speaker off */ - break ; + sb_cmd(sb, DSP_CMD_GETVER); /* Get version */ + sb->bd_id = (sb_get_byte(sb) << 8) | sb_get_byte(sb); - } - return 0 ; -} + switch (sb->bd_id >> 8) { + case 1: /* old sound blaster has nothing... */ + case 2: + fmt = "SoundBlaster %d.%d" ; /* default */ + break; -/* - * The second part of the file contains all functions specific to - * the board and (usually) not exported to other modules. - */ + case 3: + fmt = "SoundBlaster Pro %d.%d"; + if (sb->bd_id == 0x301) { + int essver, rev; + + /* Try to detect ESS chips. */ + sb_cmd(sb, DSP_CMD_GETID); /* Return ident. bytes. */ + essver = (sb_get_byte(sb) << 8) | sb_get_byte(sb); + rev = essver & 0x000f; + essver &= 0xfff0; + if (essver == 0x4880) { + /* the ESS488 can be treated as an SBPRO */ + fmt = "SoundBlaster Pro (ESS488 rev %d)"; + } else if (essver == 0x6880) { + if (rev < 8) fmt = "SoundBlaster Pro (ESS688 rev %d)"; + else fmt = "SoundBlaster Pro (ESS1868 rev %d)"; + sb->bd_flags |= BD_F_ESS; + } else return ENXIO; + sb->bd_id &= 0xff00; + sb->bd_id |= ((essver & 0xf000) >> 8) | rev; + } + break; -int -sb_reset_dsp(int io_base) -{ - int loopc; - - outb(io_base + SBDSP_RST, 3); - DELAY(100); - outb(io_base + SBDSP_RST, 0); - for (loopc = 0; loopc<100 && !(inb(DSP_DATA_AVAIL) & 0x80); loopc++) - DELAY(30); - - if (inb(DSP_READ) != 0xAA) { - DEB(printf("sb_reset_dsp 0x%x failed\n", io_base)); - return 0; /* Sorry */ - } - return 1; -} + case 4: + sb->bd_flags |= BD_F_SB16; + fmt = "SoundBlaster 16 %d.%d"; + break; -/* - * only used in sb_attach from here. - */ + default: + device_printf(dev, "failed to get SB version (%x)\n", + sb->bd_id); + return ENXIO; + + } + if ((sb->bd_id >> 8) <= 4) snprintf(buf, sizeof buf, fmt, + sb->bd_id >> 8, sb->bd_id & 0xff); + else snprintf(buf, sizeof buf, fmt, sb->bd_id & 0x000f); + device_set_desc_copy(dev, buf); + return sb_reset_dsp(sb); +} -static void -sb_dsp_init(snddev_info *d, struct isa_device *dev) +static int +sb_init(device_t dev, struct sb_info *sb) { - int i, x; - char *fmt = NULL ; - int io_base = dev->id_iobase ; + int x, irq; - d->bd_id = 0 ; + sb->bd_flags &= ~BD_F_MIX_MASK; + /* do various initializations depending on board id. */ + switch (sb->bd_id >> 8) { + case 1: /* old sound blaster has nothing... */ + break; - sb_reset_dsp(io_base); - sb_cmd(io_base, DSP_CMD_GETVER); /* Get version */ + case 2: + sb->bd_flags |= BD_F_DUP_MIDI; + if (sb->bd_id > 0x200) sb->bd_flags |= BD_F_MIX_CT1335; + break; - for (i = 10000; i; i--) { /* perhaps wait longer on a fast machine ? */ - if (inb(DSP_DATA_AVAIL) & 0x80) { /* wait for Data Ready */ - if ( (d->bd_id & 0xff00) == 0) - d->bd_id = inb(DSP_READ) << 8; /* major */ - else { - d->bd_id |= inb(DSP_READ); /* minor */ + case 3: + sb->bd_flags |= BD_F_DUP_MIDI | BD_F_MIX_CT1345; break; - } - } else - DELAY(20); - } - - /* - * now do various initializations depending on board id. - */ - - fmt = "SoundBlaster %d.%d" ; /* default */ - - switch ( d->bd_id >> 8 ) { - case 0 : - printf("\n\nFailed to get SB version (%x) - possible I/O conflict\n\n", - inb(DSP_DATA_AVAIL)); - d->bd_id = 0x100; - case 1 : /* old sound blaster has nothing... */ - break ; - - case 2 : - d->dbuf_in.chan = d->dbuf_out.chan ; /* half duplex */ - d->bd_flags |= BD_F_DUP_MIDI ; - - if (d->bd_id == 0x200) - break ; /* no mixer on the 2.0 */ - d->bd_flags &= ~BD_F_MIX_MASK ; - d->bd_flags |= BD_F_MIX_CT1335 ; - - break ; - case 4 : - fmt = "SoundBlaster 16 %d.%d"; - d->audio_fmt |= AFMT_FULLDUPLEX | AFMT_WEIRD | AFMT_S8 | AFMT_S16_LE; - d->bd_flags |= BD_F_SB16; - d->bd_flags &= ~BD_F_MIX_MASK ; - d->bd_flags |= BD_F_MIX_CT1745 ; - - /* soft irq/dma configuration */ - x = -1 ; - if (d->irq == 5) x = 2; - else if (d->irq == 7) x = 4; - else if (d->irq == 9) x = 1; - else if (d->irq == 10) x = 8; - if (x == -1) - printf("<%s>%d: bad irq %d (only 5,7,9,10 allowed)\n", - d->name, dev->id_unit, d->irq); - else - sb_setmixer(io_base, IRQ_NR, x); - if (d->dbuf_out.chan == d->dbuf_in.chan) { - printf("WARNING: sb: misconfigured secondary DMA channel\n"); - } - sb_setmixer(io_base, DMA_NR, (1 << d->dbuf_out.chan) | (1 << d->dbuf_in.chan)); - break ; - - case 3 : - d->dbuf_in.chan = d->dbuf_out.chan ; /* half duplex */ - fmt = "SoundBlaster Pro %d.%d"; - d->bd_flags |= BD_F_DUP_MIDI ; - d->bd_flags &= ~BD_F_MIX_MASK ; - d->bd_flags |= BD_F_MIX_CT1345 ; - if (d->bd_id == 0x301) { - int ess_major = 0, ess_minor = 0; - - /* - * Try to detect ESS chips. - */ - - sb_cmd(io_base, DSP_CMD_GETID); /* Return ident. bytes. */ - - for (i = 1000; i; i--) { - if (inb(DSP_DATA_AVAIL) & 0x80) { /* wait for Data Ready */ - if (ess_major == 0) - ess_major = inb(DSP_READ); - else { - ess_minor = inb(DSP_READ); - break; - } - } else - DELAY(20); - } - - if (ess_major == 0x48 && (ess_minor & 0xf0) == 0x80) { - /* the ESS488 can be treated as an SBPRO */ - printf("ESS488 (rev %d)\n", ess_minor & 0x0f); - break ; - } - else if (ess_major == 0x68 && (ess_minor & 0xf0) == 0x80) { - int rev = ess_minor & 0xf; - - if (rev >= 8) - printf("ESS1868 (rev %d)\n", rev); - else - printf("ESS688 (rev %d)\n", rev); - d->bd_flags |= BD_F_ESS; - d->audio_fmt |= AFMT_S16_LE; - - /* enable extended ESS mode */ - sb_cmd(d->io_base, 0xc6); - break; - } else { - printf("Unknown card 0x%x 0x%x -- hope it is SBPRO\n", - ess_major, ess_minor); - break ; - } - } - } + case 4: + sb->bd_flags |= BD_F_SB16 | BD_F_MIX_CT1745; + if (sb->dma16 != sb->dma8) sb->bd_flags |= BD_F_DUPLEX; + + /* soft irq/dma configuration */ + x = -1; + irq = rman_get_start(sb->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(sb, IRQ_NR, x); + sb_setmixer(sb, DMA_NR, (1 << sb->dma16) | (1 << sb->dma8)); + break; + } + return 0; +} - snprintf(d->name, sizeof(d->name), - fmt, (d->bd_id >> 8) &0xff, d->bd_id & 0xff); +static int +sb_probe(device_t dev) +{ + snddev_info *d = device_get_softc(dev); + struct sb_info *sb; + int allocated, i; + int error; + + if (isa_get_vendorid(dev)) return ENXIO; /* not yet */ + + device_set_desc(dev, "SoundBlaster"); + bzero(d, sizeof *d); + sb = (struct sb_info *)malloc(sizeof *sb, M_DEVBUF, M_NOWAIT); + if (!sb) return ENXIO; + bzero(sb, sizeof *sb); + + allocated = 0; + sb->io_rid = 0; + sb->io_base = bus_alloc_resource(dev, SYS_RES_IOPORT, &sb->io_rid, + 0, ~0, 16, RF_ACTIVE); + if (!sb->io_base) { + BVDDB(printf("sb_probe: no addr, trying (0x220, 0x240)\n")); + allocated = 1; + sb->io_rid = 0; + sb->io_base = bus_alloc_resource(dev, SYS_RES_IOPORT, + &sb->io_rid, 0x220, 0x22f, + 16, RF_ACTIVE); + if (!sb->io_base) { + sb->io_base = bus_alloc_resource(dev, SYS_RES_IOPORT, + &sb->io_rid, 0x240, + 0x24f, 16, RF_ACTIVE); + } + } + if (!sb->io_base) return ENXIO; + + error = sb_reset_dsp(sb); + if (error) goto no; + error = sb_identify_board(dev, sb); + if (error) goto no; +no: + i = sb->io_rid; + sb_release_resources(sb, dev); + if (allocated) ISA_DELETE_RESOURCE(device_get_parent(dev), dev, + SYS_RES_IOPORT, i); + return error; +} - sb_mix_init(d); +static int +sb_doattach(device_t dev, struct sb_info *sb) +{ + snddev_info *d = device_get_softc(dev); + void *ih; + int error; + char status[SND_STATUSLEN]; + + sb->irq_rid = 0; + sb->drq1_rid = 0; + sb->drq2_rid = 1; + if (sb_alloc_resources(sb, dev)) goto no; + error = sb_reset_dsp(sb); + if (error) goto no; + error = sb_identify_board(dev, sb); + if (error) goto no; + + sb_init(dev, sb); + mixer_init(d, &sb_mixer, sb); + bus_setup_intr(dev, sb->irq, INTR_TYPE_TTY, sb_intr, sb, &ih); + + if (sb->bd_flags & BD_F_SB16) + pcm_setflags(dev, pcm_getflags(dev) | SD_F_EVILSB16); + if (sb->dma16 == sb->dma8) + pcm_setflags(dev, pcm_getflags(dev) | SD_F_SIMPLEX); + if (bus_dma_tag_create(/*parent*/NULL, /*alignment*/2, /*boundary*/0, + /*lowaddr*/BUS_SPACE_MAXADDR_24BIT, + /*highaddr*/BUS_SPACE_MAXADDR, + /*filter*/NULL, /*filterarg*/NULL, + /*maxsize*/DSP_BUFFSIZE, /*nsegments*/1, + /*maxsegz*/0x3ffff, + /*flags*/0, &sb->parent_dmat) != 0) { + device_printf(dev, "unable to create dma tag\n"); + goto no; + } + + snprintf(status, SND_STATUSLEN, "at io 0x%lx irq %ld drq %d", + rman_get_start(sb->io_base), rman_get_start(sb->irq), + sb->dma8); + if (sb->dma16 != sb->dma8) snprintf(status + strlen(status), + SND_STATUSLEN - strlen(status), ":%d", sb->dma16); + + if (pcm_register(dev, sb, 1, 1)) goto no; + pcm_addchan(dev, PCMDIR_REC, &sb_chantemplate, sb); + pcm_addchan(dev, PCMDIR_PLAY, &sb_chantemplate, sb); + pcm_setstatus(dev, status); + + return 0; + +no: + sb_release_resources(sb, dev); + return ENXIO; } -static void -sb_mix_init(snddev_info *d) +static int +sb_attach(device_t dev) { - switch (d->bd_flags & BD_F_MIX_MASK) { - case BD_F_MIX_CT1345 : /* SB 3.0 has 1345 mixer */ - - d->mix_devs = SBPRO_MIXER_DEVICES ; - d->mix_rec_devs = SBPRO_RECORDING_DEVICES ; - d->mix_recsrc = SOUND_MASK_MIC ; - - sb_setmixer(d->io_base, 0, 1 ); /* reset mixer */ - sb_setmixer(d->io_base, MIC_VOL , 0x6 ); /* mic volume max */ - sb_setmixer(d->io_base, RECORD_SRC , 0x0 ); /* mic source */ - sb_setmixer(d->io_base, FM_VOL , 0x0 ); /* no midi */ - break ; - - case BD_F_MIX_CT1745 : /* SB16 mixer ... */ - - d->mix_devs = SB16_MIXER_DEVICES ; - d->mix_rec_devs = SB16_RECORDING_DEVICES ; - d->mix_recsrc = SOUND_MASK_MIC ; - } - sb_mixer_reset(d); + struct sb_info *sb; + int flags = isa_get_flags(dev); + + if (flags & DV_F_DUAL_DMA) { + ISA_SET_RESOURCE(device_get_parent(dev), dev, SYS_RES_DRQ, 1, + flags & DV_F_DRQ_MASK, 1); + } + sb = (struct sb_info *)malloc(sizeof *sb, M_DEVBUF, M_NOWAIT); + if (!sb) return ENXIO; + bzero(sb, sizeof *sb); + + /* XXX in probe should set io resource to right val instead of this */ + sb->io_rid = 0; + sb->io_base = bus_alloc_resource(dev, SYS_RES_IOPORT, &sb->io_rid, + 0, ~0, 16, RF_ACTIVE); + if (!sb->io_base) { + BVDDB(printf("sb_probe: no addr, trying (0x220, 0x240)\n")); + sb->io_rid = 0; + sb->io_base = bus_alloc_resource(dev, SYS_RES_IOPORT, + &sb->io_rid, 0x220, 0x22f, + 16, RF_ACTIVE); + if (!sb->io_base) { + sb->io_base = bus_alloc_resource(dev, SYS_RES_IOPORT, + &sb->io_rid, 0x240, + 0x24f, 16, RF_ACTIVE); + } + } + if (!sb->io_base) return ENXIO; + + return sb_doattach(dev, sb); } -/* - * Common code for the midi and pcm functions - * - * sb_cmd write a single byte to the CMD port. - * sb_cmd2 write a CMD + 1 byte arg - * sb_cmd3 write a CMD + 2 byte arg - * sb_get_byte returns a single byte from the DSP data port - * - * ess_write is actually sb_cmd2 - * ess_read access ext. regs via sb_cmd(0xc0, reg) followed by sb_get_byte - */ +static device_method_t sb_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, sb_probe), + DEVMETHOD(device_attach, sb_attach), -int -sb_cmd(int io_base, u_char val) -{ - int i; + { 0, 0 } +}; - for (i = 0; i < 1000 ; i++) { - if ((inb(io_base + SBDSP_STATUS) & 0x80) == 0) { - outb(io_base + SBDSP_CMD, val); - return 1; - } - if (i > 10) - DELAY (i > 100 ? 1000 : 10 ); - } +static driver_t sb_driver = { + "pcm", + sb_methods, + sizeof(snddev_info), +}; + +DRIVER_MODULE(sb, isa, sb_driver, pcm_devclass, 0, 0); - printf("SoundBlaster: DSP Command(0x%02x) timeout. IRQ conflict ?\n", val); - return 0; +static void +sb_intr(void *arg) +{ + struct sb_info *sb = (struct sb_info *)arg; + int reason = 3, c; + + /* + * SB < 4.0 is half duplex and has only 1 bit for int source, + * so we fake it. SB 4.x (SB16) has the int source in a separate + * register. + * The Vibra16X has separate flags for 8 and 16 bit transfers, but + * I have no idea how to tell capture from playback interrupts... + */ + if (sb->bd_flags & BD_F_SB16) { + c = sb_getmixer(sb, IRQ_STAT); + /* this tells us if the source is 8-bit or 16-bit dma. We + * have to check the io channel to map it to read or write... + */ + reason = 0; + if (c & 1) { /* 8-bit dma */ + if (sb->pch.fmt & AFMT_U8) reason |= 1; + if (sb->rch.fmt & AFMT_U8) reason |= 2; + } + if (c & 2) { /* 16-bit dma */ + if (sb->pch.fmt & AFMT_S16_LE) reason |= 1; + if (sb->rch.fmt & AFMT_S16_LE) reason |= 2; + } + } else c = 1; +#if 0 + printf("sb_intr: reason=%d c=0x%x\n", reason, c); +#endif + if ((reason & 1) && (sb->pch.buffer->dl > 0)) + chn_intr(sb->pch.channel); + if ((reason & 2) && (sb->rch.buffer->dl > 0)) + chn_intr(sb->rch.channel); + if (c & 1) sb_rd(sb, DSP_DATA_AVAIL); /* 8-bit int ack */ + if (c & 2) sb_rd(sb, DSP_DATA_AVL16); /* 16-bit int ack */ } -int -sb_cmd3(int io_base, u_char cmd, int val) +static int +sb_format(struct sb_chinfo *ch, u_int32_t format) { - if (sb_cmd(io_base, cmd)) { - sb_cmd(io_base, val & 0xff ); - sb_cmd(io_base, (val>>8) & 0xff ); - return 1 ; - } else + struct sb_info *sb = ch->parent; + ch->fmt = format; + if (sb->bd_flags & BD_F_ESS) { + int play = (ch->dir == PCMDIR_PLAY)? 1 : 0; + int b16 = (ch->fmt & AFMT_S16_LE)? 1 : 0; + int stereo = (ch->fmt & AFMT_STEREO)? 1 : 0; + + /* autoinit DMA mode */ + ess_write(sb, 0xb8, 0x04 | play? 0x00 : 0x0a); + /* mono/stereo */ + ess_write(sb, 0xa8, + (ess_read(sb, 0xa8) & ~0x03) | stereo? 0x01 : 0x02); + /* demand mode, 4 bytes/xfer */ + ess_write(sb, 0xb9, 2); + /* setup dac/adc */ + if (play) ess_write(sb, 0xb6, b16? 0x00 : 0x80); + ess_write(sb, 0xb7, 0x51 | (b16? 0x20 : 0x00)); + ess_write(sb, 0xb7, + 0x98 + (b16? 0x24 : 0x00) + (stereo? 0x00 : 0x38)); + /* irq/drq control */ + ess_write(sb, 0xb1, ess_read(sb, 0xb1) | 0x50); + ess_write(sb, 0xb2, ess_read(sb, 0xb1) | 0x50); + } return 0; } -int -sb_cmd2(int io_base, u_char cmd, int val) +static int +sb_speed(struct sb_chinfo *ch, int speed) { - if (sb_cmd(io_base, cmd)) { - sb_cmd(io_base, val & 0xff ); - return 1 ; - } else - return 0; + struct sb_info *sb = ch->parent; + int play = (ch->dir == PCMDIR_PLAY)? 1 : 0; + int stereo = (ch->fmt & AFMT_STEREO)? 1 : 0; + + if (sb->bd_flags & BD_F_SB16) { + RANGE(speed, 5000, 45000); + sb_cmd(sb, 0x42 - play); + sb_cmd(sb, speed >> 8); + sb_cmd(sb, speed & 0xff); + } else if (sb->bd_flags & BD_F_ESS) { + int t; + RANGE(speed, 5000, 49000); + if (speed > 22000) { + t = (795500 + speed / 2) / speed; + speed = (795500 + t / 2) / t; + t = (256 - t) | 0x80; + } else { + t = (397700 + speed / 2) / speed; + speed = (397700 + t / 2) / t; + t = 128 - t; + } + ess_write(sb, 0xa1, t); /* set time constant */ + t = 256 - 7160000 / (((speed * 9) / 20) * 82); + ess_write(sb, 0xa2, t); + } else { + u_char tconst; + int max_speed = 45000, tmp; + u_long flags; + + /* here enforce speed limitations - max 22050 on sb 1.x*/ + if (sb->bd_id <= 0x200) max_speed = 22050; + + /* + * SB models earlier than SB Pro have low limit for the + * input rate. Note that this is only for input, but since + * we do not support separate values for rec & play.... + */ + if (!play) { + if (sb->bd_id <= 0x200) max_speed = 13000; + else if (sb->bd_id < 0x300) max_speed = 15000; + } + RANGE(speed, 4000, max_speed); + if (stereo) speed <<= 1; + + /* + * Now the speed should be valid. Compute the value to be + * programmed into the board. + */ + if (speed > 22050) { /* High speed mode on 2.01/3.xx */ + tconst = (u_char) + ((65536 - ((256000000 + speed / 2) / speed)) + >> 8); + sb->bd_flags |= BD_F_HISPEED; + tmp = 65536 - (tconst << 8); + speed = (256000000 + tmp / 2) / tmp; + } else { + sb->bd_flags &= ~BD_F_HISPEED; + tconst = (256 - ((1000000 + speed / 2) / speed)) & 0xff; + tmp = 256 - tconst; + speed = (1000000 + tmp / 2) / tmp; + } + flags = spltty(); + sb_cmd1(sb, 0x40, tconst); /* set time constant */ + splx(flags); + if (stereo) speed >>= 1; + } + return speed; } -/* - * in the SB, there is a set of indirect "mixer" registers with - * address at offset 4, data at offset 5 - */ -void -sb_setmixer(int io_base, u_int port, u_int value) +static int +sb_start(struct sb_chinfo *ch) { - u_long flags; - - flags = spltty(); - outb(io_base + SB_MIX_ADDR, (u_char) (port & 0xff)); /* Select register */ - DELAY(10); - outb(io_base + SB_MIX_DATA, (u_char) (value & 0xff)); - DELAY(10); - splx(flags); + struct sb_info *sb = ch->parent; + int play = (ch->dir == PCMDIR_PLAY)? 1 : 0; + int b16 = (ch->fmt & AFMT_S16_LE)? 1 : 0; + int stereo = (ch->fmt & AFMT_STEREO)? 1 : 0; + int l = ch->buffer->dl; + u_char i1, i2 = 0; + + if (b16) l >>= 1; + l--; + if (play) sb_cmd(sb, DSP_CMD_SPKON); + if (sb->bd_flags & BD_F_SB16) { + i1 = DSP_F16_AUTO | DSP_F16_FIFO_ON | + (play? DSP_F16_DAC : DSP_F16_ADC); + i1 |= (b16 && (sb->bd_flags & BD_F_DUPLEX))? DSP_DMA16 : DSP_DMA8; + i2 = (stereo? DSP_F16_STEREO : 0) | (b16? DSP_F16_SIGNED : 0); + sb_cmd(sb, i1); + sb_cmd2(sb, i2, l); + } else if (sb->bd_flags & BD_F_ESS) { + ess_write(sb, 0xa4, l); + ess_write(sb, 0xa5, l >> 8); + ess_write(sb, 0xb8, ess_read(sb, 0xb8) | (play? 0x05 : 0x0f)); + } else { + if (sb->bd_flags & BD_F_HISPEED) i1 = play? 0x90 : 0x98; + else i1 = play? 0x1c : 0x2c; + sb_setmixer(sb, 0x0e, stereo? 2 : 0); + /* an ESS extension -- they can do 16 bits */ + if (b16) i1 |= 1; + sb_cmd2(sb, 0x48, l); + sb_cmd(sb, i1); + } + sb->bd_flags |= BD_F_DMARUN << b16; + return 0; } -int -sb_getmixer(int io_base, u_int port) -{ - int val; - u_long flags; - - flags = spltty(); - outb(io_base + SB_MIX_ADDR, (u_char) (port & 0xff)); /* Select register */ - DELAY(10); - val = inb(io_base + SB_MIX_DATA); - DELAY(10); - splx(flags); - - return val; -} - -u_int -sb_get_byte(int io_base) +static int +sb_stop(struct sb_chinfo *ch) { - int i; + struct sb_info *sb = ch->parent; + int play = (ch->dir == PCMDIR_PLAY)? 1 : 0; + int b16 = (ch->fmt & AFMT_S16_LE)? 1 : 0; - for (i = 1000; i; i--) - if (inb(DSP_DATA_AVAIL) & 0x80) - return inb(DSP_READ); - else - DELAY(20); - return 0xffff; + if (sb->bd_flags & BD_F_HISPEED) sb_reset_dsp(sb); + else { + sb_cmd(sb, b16? DSP_CMD_DMAPAUSE_16 : DSP_CMD_DMAPAUSE_8); + /* + * The above seems to have the undocumented side effect of + * blocking the other side as well. If the other + * channel was active (SB16) I have to re-enable it :( + */ + if (sb->bd_flags & (BD_F_DMARUN << (1 - b16))) + sb_cmd(sb, b16? 0xd4 : 0xd6 ); + } + if (play) sb_cmd(sb, DSP_CMD_SPKOFF); /* speaker off */ + sb->bd_flags &= ~(BD_F_DMARUN << b16); + return 0; } -int -ess_write(int io_base, u_char reg, int val) +/* channel interface */ +static void * +sbchan_init(void *devinfo, snd_dbuf *b, pcm_channel *c, int dir) { - return sb_cmd2(io_base, reg, val); + struct sb_info *sb = devinfo; + struct sb_chinfo *ch = (dir == PCMDIR_PLAY)? &sb->pch : &sb->rch; + + ch->parent = sb; + ch->channel = c; + ch->buffer = b; + ch->buffer->bufsize = DSP_BUFFSIZE; + if (chn_allocbuf(ch->buffer, sb->parent_dmat) == -1) return NULL; + ch->buffer->chan = (dir == PCMDIR_PLAY)? sb->dma16 : sb->dma8; + return ch; } -int -ess_read(int io_base, u_char reg) +static int +sbchan_setdir(void *data, int dir) { - if (!sb_cmd(io_base, 0xc0) || !sb_cmd(io_base, reg) ) - return 0xffff ; - return sb_get_byte(io_base); + struct sb_chinfo *ch = data; + ch->dir = dir; + return 0; } +static int +sbchan_setformat(void *data, u_int32_t format) +{ + struct sb_chinfo *ch = data; + sb_format(ch, format); + return 0; +} -/* - * various utility functions for the DSP - */ +static int +sbchan_setspeed(void *data, u_int32_t speed) +{ + struct sb_chinfo *ch = data; + return sb_speed(ch, speed); +} -/* - * dsp_speed updates the speed setting from the descriptor. make sure - * it is called at spltty(). - * Besides, it takes care of stereo setting. - */ static int -dsp_speed(snddev_info *d) +sbchan_setblocksize(void *data, u_int32_t blocksize) { - u_char tconst; - u_long flags; - int max_speed = 44100, speed = d->play_speed ; - - /* - * special code for the SB16 - */ - if (d->bd_flags & BD_F_SB16) { - RANGE (speed, 5000, 45000); - d->play_speed = d->rec_speed = speed ; - sb_cmd(d->io_base, 0x41); - sb_cmd(d->io_base, d->play_speed >> 8 ); - sb_cmd(d->io_base, d->play_speed & 0xff ); - sb_cmd(d->io_base, 0x42); - sb_cmd(d->io_base, d->rec_speed >> 8 ); - sb_cmd(d->io_base, d->rec_speed & 0xff ); - return speed ; - } - - /* - * special code for the ESS ... - */ - if (d->bd_flags & BD_F_ESS) { - int t; - RANGE (speed, 5000, 49000); - if (speed > 22000) { - t = (795500 + speed / 2) / speed; - speed = (795500 + t / 2) / t ; - t = (256 - t ) | 0x80 ; - } else { - t = (397700 + speed / 2) / speed; - speed = (397700 + t / 2) / t ; - t = 128 - t ; - } - ess_write(d->io_base, 0xa1, t); /* set time constant */ - d->play_speed = d->rec_speed = speed ; - speed = (speed * 9 ) / 20 ; - t = 256-7160000/(speed*82); - ess_write(d->io_base,0xa2,t); - return speed ; - } - - /* - * This is code for the SB3.x and lower. - * Only some models can do stereo, and only if not - * simultaneously using midi. - * At the moment we do not support either... - */ -#if 0 - d->flags &= ~SND_F_STEREO; -#endif + return blocksize; +} - /* - * here enforce speed limitations. - */ - if (d->bd_id <= 0x200) - max_speed = 22050; /* max 22050 on SB 1.X */ +static int +sbchan_trigger(void *data, int go) +{ + struct sb_chinfo *ch = data; + buf_isadma(ch->buffer, go); + if (go == PCMTRIG_START) sb_start(ch); else sb_stop(ch); + return 0; +} - /* - * SB models earlier than SB Pro have low limit for the - * input rate. Note that this is only for input, but since - * we do not support separate values for rec & play.... - */ - if (d->bd_id <= 0x200) - max_speed = 13000; - else if (d->bd_id < 0x300) - max_speed = 15000; +static int +sbchan_getptr(void *data) +{ + struct sb_chinfo *ch = data; + return buf_isadmaptr(ch->buffer); +} - RANGE(speed, 4000, max_speed); +static pcmchan_caps * +sbchan_getcaps(void *data) +{ + struct sb_chinfo *ch = data; + int p = (ch->dir == PCMDIR_PLAY)? 1 : 0; + if (ch->parent->bd_flags & BD_F_ESS) + return p? &ess_playcaps : &ess_reccaps; + else if (ch->parent->bd_id <= 0x200) + return p? &sb_playcaps : &sb_reccaps; + else if (ch->parent->bd_id >= 0x400) + return p? &sb16_playcaps : &sb16_reccaps; + else + return p? &sbpro_playcaps : &sbpro_reccaps; +} - if (d->flags & SND_F_STEREO) /* really unused right now... */ - speed *= 2; +/************************************************************/ - /* - * Now the speed should be valid. Compute the value to be - * programmed into the board. - */ +static int +sbmix_init(snd_mixer *m) +{ + struct sb_info *sb = mix_getdevinfo(m); + + switch (sb->bd_flags & BD_F_MIX_MASK) { + case BD_F_MIX_CT1345: /* SB 3.0 has 1345 mixer */ + mix_setdevs(m, SBPRO_MIXER_DEVICES); + mix_setrecdevs(m, SBPRO_RECORDING_DEVICES); + sb_setmixer(sb, 0, 1); /* reset mixer */ + sb_setmixer(sb, MIC_VOL, 0x6); /* mic volume max */ + sb_setmixer(sb, RECORD_SRC, 0x0); /* mic source */ + sb_setmixer(sb, FM_VOL, 0x0); /* no midi */ + break; - if (speed > 22050) { /* High speed mode on 2.01/3.xx */ - int tmp; + case BD_F_MIX_CT1745: /* SB16 mixer ... */ + mix_setdevs(m, SB16_MIXER_DEVICES); + mix_setrecdevs(m, SB16_RECORDING_DEVICES); + sb_setmixer(sb, 0x3c, 0x1f); /* make all output active */ + sb_setmixer(sb, 0x3d, 0); /* make all inputs-l off */ + sb_setmixer(sb, 0x3e, 0); /* make all inputs-r off */ + } + return 0; +} - tconst = (u_char) ((65536 - ((256000000 + speed / 2) / speed)) >> 8) ; - d->bd_flags |= BD_F_HISPEED ; +static int +sbmix_set(snd_mixer *m, unsigned dev, unsigned left, unsigned right) +{ + struct sb_info *sb = mix_getdevinfo(m); + int regoffs; + u_char val; + mixer_tab *iomap; + + switch (sb->bd_flags & BD_F_MIX_MASK) { + case BD_F_MIX_CT1345: + iomap = &sbpro_mix; + break; - flags = spltty(); - sb_cmd2(d->io_base, 0x40, tconst); /* set time constant */ - splx(flags); + case BD_F_MIX_CT1745: + iomap = &sb16_mix; + break; - tmp = 65536 - (tconst << 8); - speed = (256000000 + tmp / 2) / tmp; - } else { - int tmp; + default: + return -1; + /* XXX how about the SG NX Pro, iomap = sgnxpro_mix */ + } + regoffs = (*iomap)[dev][LEFT_CHN].regno; + if (regoffs == 0) return -1; + val = sb_getmixer(sb, regoffs); + change_bits(iomap, &val, dev, LEFT_CHN, left); + sb_setmixer(sb, regoffs, val); + if ((*iomap)[dev][RIGHT_CHN].regno != regoffs) { /* Change register */ + regoffs = (*iomap)[dev][RIGHT_CHN].regno; + if (regoffs != 0) { + val = sb_getmixer(sb, regoffs); /* Read the new one */ + change_bits(iomap, &val, dev, RIGHT_CHN, right); + sb_setmixer(sb, regoffs, val); + } else right = left; + } else right = left; + return left | (right << 8); +} - d->bd_flags &= ~BD_F_HISPEED ; - tconst = (256 - ((1000000 + speed / 2) / speed)) & 0xff; +static int +sbmix_setrecsrc(snd_mixer *m, u_int32_t src) +{ + struct sb_info *sb = mix_getdevinfo(m); + u_char recdev; + + switch (sb->bd_flags & BD_F_MIX_MASK) { + case BD_F_MIX_CT1345: + if (src == SOUND_MASK_LINE) recdev = 0x06; + else if (src == SOUND_MASK_CD) recdev = 0x02; + else { /* default: mic */ + src = SOUND_MASK_MIC; + recdev = 0; + } + sb_setmixer(sb, RECORD_SRC, recdev | + (sb_getmixer(sb, RECORD_SRC) & ~0x07)); + break; - flags = spltty(); - sb_cmd2(d->io_base, 0x40, tconst); /* set time constant */ - splx(flags); + case BD_F_MIX_CT1745: /* sb16 */ + recdev = 0; + if (src & SOUND_MASK_MIC) recdev |= 0x01; /* mono mic */ + if (src & SOUND_MASK_CD) recdev |= 0x06; /* l+r cd */ + if (src & SOUND_MASK_LINE) recdev |= 0x18; /* l+r line */ + if (src & SOUND_MASK_SYNTH) recdev |= 0x60; /* l+r midi */ + sb_setmixer(sb, SB16_IMASK_L, recdev); + sb_setmixer(sb, SB16_IMASK_R, recdev); + /* + * since the same volume controls apply to the input and + * output sections, the best approach to have a consistent + * behaviour among cards would be to disable the output path + * on devices which are used to record. + * However, since users like to have feedback, we only disable + * the mic -- permanently. + */ + sb_setmixer(sb, SB16_OMASK, 0x1f & ~1); + break; + } + return src; +} - tmp = 256 - tconst; - speed = (1000000 + tmp / 2) / tmp; - } +#if NPNP > 0 +static int +sbpnp_probe(device_t dev) +{ + char *s = NULL; + u_int32_t logical_id = isa_get_logicalid(dev); - if (d->flags & SND_F_STEREO) /* really unused right now... */ - speed /= 2; + switch(logical_id) { + case 0x43008c0e: /* CTL0043 */ + case 0x01008c0e: /* CTL0001 */ + s = "Vibra16X"; + break; - d->play_speed = d->rec_speed = speed; - return speed; -} + case 0x31008c0e: /* CTL0031 */ + case 0x41008c0e: /* CTL0041 */ + case 0x42008c0e: /* CTL0042 */ + case 0x45008c0e: /* CTL0045 */ + s = "SB16 PnP"; + break; -/* - * mixer support, originally in sb_mixer.c - */ + case 0x01100000: /* @@@1001 */ + s = "Avance Asound 110"; + break; -static void -sb_set_recsrc(snddev_info *d, int mask) -{ - u_char recdev ; - - mask &= d->mix_rec_devs; - switch (d->bd_flags & BD_F_MIX_MASK) { - case BD_F_MIX_CT1345 : - if (mask == SOUND_MASK_LINE) - recdev = 6 ; - else if (mask == SOUND_MASK_CD) - recdev = 2 ; - else { /* default: mic */ - mask = SOUND_MASK_MIC ; - recdev = 0 ; - } - sb_setmixer(d->io_base, RECORD_SRC, - recdev | (sb_getmixer(d->io_base, RECORD_SRC) & ~7 )); - break ; - case BD_F_MIX_CT1745 : /* sb16 */ - if (mask == 0) - mask = SOUND_MASK_MIC ; /* XXX For compatibility. Bug ? */ - recdev = 0 ; - if (mask & SOUND_MASK_MIC) - recdev |= 1 ; - if (mask & SOUND_MASK_CD) - recdev |= 6 ; /* l+r cd */ - if (mask & SOUND_MASK_LINE) - recdev |= 0x18 ; /* l+r line */ - if (mask & SOUND_MASK_SYNTH) - recdev |= 0x60 ; /* l+r midi */ - sb_setmixer(d->io_base, SB16_IMASK_L, recdev); - sb_setmixer(d->io_base, SB16_IMASK_R, recdev); - /* - * since the same volume controls apply to the input and - * output sections, the best approach to have a consistent - * behaviour among cards would be to disable the output path - * on devices which are used to record. - * However, since users like to have feedback, we only disable - * the mike -- permanently. - */ - sb_setmixer(d->io_base, SB16_OMASK, 0x1f & ~1); - break ; - } - d->mix_recsrc = mask; -} + case 0x01200000: /* @@@2001 */ + s = "Avance Logic ALS120"; + break; -static void -sb_mixer_reset(snddev_info *d) -{ - int i; - - for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) - sb_mixer_set(d, i, levels[i]); - if (d->bd_flags & BD_F_SB16) { - sb_setmixer(d->io_base, 0x3c, 0x1f); /* make all output active */ - sb_setmixer(d->io_base, 0x3d, 0); /* make all inputs-l off */ - sb_setmixer(d->io_base, 0x3e, 0); /* make all inputs-r off */ - } - sb_set_recsrc(d, SOUND_MASK_MIC); + case 0x68187316: /* ESS1868 */ + s = "ESS1868"; + break; + } + if (s) { + device_set_desc(dev, s); + return 0; + } + return ENXIO; } static int -sb_mixer_set(snddev_info *d, int dev, int value) +sbpnp_attach(device_t dev) { - int left = value & 0x000000ff; - int right = (value & 0x0000ff00) >> 8; - int regoffs; - u_char val; - mixer_tab *iomap; - -#ifdef JAZZ16 - if (d->bd_flags & BD_F_JAZZ16 && d->bd_flags & BD_F_JAZZ16_2) - return smw_mixer_set(dev, value); -#endif - - if (dev == SOUND_MIXER_RECSRC) { - sb_set_recsrc(d, value); - return 0 ; - } - if (left > 100) - left = 100; - if (right > 100) - right = 100; - - if (dev > 31) - return EINVAL ; - - if (!(d->mix_devs & (1 << dev))) /* Not supported */ - return EINVAL; - - switch ( d->bd_flags & BD_F_MIX_MASK ) { - default: - /* mixer unknown, fail... */ - return EINVAL ;/* XXX change this */ - case BD_F_MIX_CT1345 : - iomap = &sbpro_mix ; - break; - case BD_F_MIX_CT1745 : - iomap = &sb16_mix ; - break; - /* XXX how about the SG NX Pro, iomap = sgnxpro_mix */ - } - regoffs = (*iomap)[dev][LEFT_CHN].regno; - if (regoffs == 0) - return EINVAL; - - val = sb_getmixer(d->io_base, regoffs); - - change_bits(iomap, &val, dev, LEFT_CHN, left); - - d->mix_levels[dev] = left | (left << 8); - - if ((*iomap)[dev][RIGHT_CHN].regno != regoffs) { /* Change register */ - sb_setmixer(d->io_base, regoffs, val); /* Save the old one */ - regoffs = (*iomap)[dev][RIGHT_CHN].regno; - - if (regoffs == 0) - return 0 ; /* Just left channel present */ - - val = sb_getmixer(d->io_base, regoffs); /* Read the new one */ - } - change_bits(iomap, &val, dev, RIGHT_CHN, right); - - sb_setmixer(d->io_base, regoffs, val); - - d->mix_levels[dev] = left | (right << 8); - return 0 ; /* ok */ + struct sb_info *sb; + u_int32_t vend_id = isa_get_vendorid(dev); + + 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); } -/* - * now support for some PnP boards. - */ +static device_method_t sbpnp_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, sbpnp_probe), + DEVMETHOD(device_attach, sbpnp_attach), -#if NPNP > 0 -static char *ess1868_probe(u_long csn, u_long vend_id); -static void ess1868_attach(u_long csn, u_long vend_id, char *name, - struct isa_device *dev); - -static struct pnp_device ess1868 = { - "ESS1868", - ess1868_probe, - ess1868_attach, - &nsnd, /* use this for all sound cards */ - &tty_imask /* imask */ + { 0, 0 } }; -DATA_SET (pnpdevice_set, ess1868); - -static char * -ess1868_probe(u_long csn, u_long vend_id) -{ - /* - * pnp X 1 os enable drq0 3 irq0 12 port0 0x240 - */ - if (vend_id == 0x68187316) { - struct pnp_cinfo d ; - read_pnp_parms ( &d , 1 ) ; - if (d.enable == 0) { - printf("This is an ESS1868, but LDN 1 is disabled\n"); - return NULL; - } - return "ESS1868" ; - } - return NULL ; -} - -static void -ess1868_attach(u_long csn, u_long vend_id, char *name, - struct isa_device *dev) -{ - struct pnp_cinfo d ; - snddev_info tmp_d ; /* patched copy of the basic snddev_info */ - - tmp_d = sb_op_desc; - snddev_last_probed = &tmp_d; -#if 0 - read_pnp_parms ( &d , 3 ); /* disable LDN 3 */ - d.port[0] = 0 ; - d.enable = 0 ; - write_pnp_parms ( &d , 3 ); - - read_pnp_parms ( &d , 2 ); /* disable LDN 2 */ - d.port[0] = 0 ; - d.enable = 0 ; - write_pnp_parms ( &d , 2 ); - read_pnp_parms ( &d , 0 ); /* read config base */ - tmp_d.conf_base = d.port[0]; - write_pnp_parms ( &d , 0 ); -#endif - - read_pnp_parms ( &d , 1 ) ; - dev->id_iobase = d.port[0]; - d.port[1] = 0 ; - d.port[2] = 0 ; - write_pnp_parms ( &d , 1 ); - enable_pnp_card(); - - dev->id_drq = d.drq[0] ; /* primary dma */ - dev->id_irq = (1 << d.irq[0] ) ; - dev->id_intr = (inthand2_t *)pcmintr ; - dev->id_flags = 0 /* DV_F_DUAL_DMA | (d.drq[1] ) */; +static driver_t sbpnp_driver = { + "pcm", + sbpnp_methods, + sizeof(snddev_info), +}; -#if 0 - snddev_last_probed->probe(dev); /* not really necessary but doesn't harm */ -#endif - pcmattach(dev); -} +DRIVER_MODULE(sbpnp, isa, sbpnp_driver, pcm_devclass, 0, 0); -/* - * A driver for some SB16pnp and compatibles... - * - * Avance Asound 100 -- 0x01009305 - * Avance Logic ALS100+ -- 0x10019305 - * Avance Logic ASound Gold ALS120 -- 0x20019305 - * xxx -- 0x2b008c0e - * - */ +#endif /* NPNP > 0 */ -static char *sb16pnp_probe(u_long csn, u_long vend_id); -static void sb16pnp_attach(u_long csn, u_long vend_id, char *name, - struct isa_device *dev); +#endif /* NPCM > 0 */ -static struct pnp_device sb16pnp = { - "SB16pnp", - sb16pnp_probe, - sb16pnp_attach, - &nsnd, /* use this for all sound cards */ - &tty_imask /* imask */ -}; -DATA_SET (pnpdevice_set, sb16pnp); - -static char * -sb16pnp_probe(u_long csn, u_long vend_id) -{ - char *s = NULL ; - - /* - * The SB16/AWExx cards seem to differ in the fourth byte of - * the vendor id, so I have just masked it for the time being... - * Reported values are: - * SB16 Value PnP: 0x2b008c0e - * SB AWExx PnP: 0x39008c0e 0x9d008c0e 0xc3008c0e - * Vibra16X: 0xf0008c0e - */ - if (vend_id == 0xf0008c0e) - s = "Vibra16X" ; - else if ( (vend_id & 0xffffff) == (0x9d008c0e & 0xffffff) ) - s = "SB16 PnP"; - else if (vend_id == 0x01009305) - s = "Avance Asound 100" ; - else if (vend_id == 0x10019305) - s = "Avance Logic 100+" ; /* Vibra16X-class */ - else if (vend_id == 0x20019305) - s = "Avance Logic ALS120" ; /* Vibra16X-class */ - if (s) { - struct pnp_cinfo d; - read_pnp_parms(&d, 0); - if (d.enable == 0) { - printf("This is a %s, but LDN 0 is disabled\n", s); - return NULL ; - } - return s ; - } - return NULL ; -} - -static void -sb16pnp_attach(u_long csn, u_long vend_id, char *name, - struct isa_device *dev) -{ - struct pnp_cinfo d ; - snddev_info tmp_d ; /* patched copy of the basic snddev_info */ - - tmp_d = sb_op_desc; - snddev_last_probed = &tmp_d; - - read_pnp_parms ( &d , 0 ) ; - d.port[1] = 0 ; /* only the first address is used */ - dev->id_iobase = d.port[0]; - tmp_d.synth_base = d.port[2]; - write_pnp_parms ( &d , 0 ); - enable_pnp_card(); - - dev->id_drq = d.drq[0] ; /* primary dma */ - dev->id_irq = (1 << d.irq[0] ) ; - dev->id_intr = (inthand2_t *)pcmintr ; - dev->id_flags = DV_F_DUAL_DMA | (d.drq[1] ) ; - - pcm_info[dev->id_unit] = tmp_d; /* pcm_info[] will be reinitialized after */ - snddev_last_probed->probe(dev); /* not really necessary but doesn't harm */ - - if (vend_id == 0x10019305 || vend_id == 0xf0008c0e - || vend_id == 0x20019305) { - /* - * XXX please add here the vend_id for other vibra16X cards... - * And remember, must change tmp_d, not - */ - tmp_d.bd_flags |= BD_F_SB16X ; - } - pcmattach(dev); -} -#endif /* NPNP */ -#endif diff --git a/sys/dev/pcm/isa/sb.h b/sys/dev/pcm/isa/sb.h index b05b365..cf39772 100644 --- a/sys/dev/pcm/isa/sb.h +++ b/sys/dev/pcm/isa/sb.h @@ -12,12 +12,12 @@ extern int sbc_major, sbc_minor ; */ #define SBDSP_RST 0x6 -#define DSP_READ (io_base + 0xA) -#define DSP_WRITE (io_base + 0xC) +#define DSP_READ 0xA +#define DSP_WRITE 0xC #define SBDSP_CMD 0xC #define SBDSP_STATUS 0xC -#define DSP_DATA_AVAIL (io_base + 0xE) -#define DSP_DATA_AVL16 (io_base + 0xF) +#define DSP_DATA_AVAIL 0xE +#define DSP_DATA_AVL16 0xF #define SB_MIX_ADDR 0x4 #define SB_MIX_DATA 0x5 @@ -96,7 +96,7 @@ extern int sbc_major, sbc_minor ; * in fact, for the SB16, dma commands are as follows: * * cmd, mode, len_low, len_high. - * + * * cmd is a combination of DSP_DMA16 or DSP_DMA8 and */ @@ -146,15 +146,16 @@ extern int sbc_major, sbc_minor ; * so that they can be restored later. */ #define BD_F_SWAPPED 0x1000 /* have swapped DMA channels */ - - +#define BD_F_DMARUN 0x2000 +#define BD_F_DMARUN2 0x4000 +#define BD_F_DUPLEX 0x8000 /* * sound/sb_mixer.h - * + * * Definitions for the SB Pro and SB16 mixers - * + * * Copyright by Hannu Savolainen 1993 - * + * * 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 @@ -162,7 +163,7 @@ extern int sbc_major, sbc_minor ; * 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 @@ -174,10 +175,10 @@ extern int sbc_major, sbc_minor ; * 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. - * + * * Modified: Hunyue Yau Jan 6 1994 Added defines for the Sound Galaxy NX Pro * mixer. - * + * */ #define SBPRO_RECORDING_DEVICES \ @@ -210,7 +211,7 @@ extern int sbc_major, sbc_minor ; /* * Mixer registers - * + * * NOTE! RECORD_SRC == IN_FILTER */ @@ -314,47 +315,6 @@ mixer_tab sb16_mix = { PMIX_ENT(SOUND_MIXER_OGAIN, 0x41, 6, 2, 0x42, 6, 2) }; -#ifdef SM_GAMES /* Master volume is lower and PCM & FM - * volumes higher than with SB Pro. This - * improves the sound quality */ - -static u_short levels[SOUND_MIXER_NRDEVICES] = -{ - 0x2020, /* Master Volume */ - 0x4b4b, /* Bass */ - 0x4b4b, /* Treble */ - 0x6464, /* FM */ - 0x6464, /* PCM */ - 0x4b4b, /* PC Speaker */ - 0x4b4b, /* Ext Line */ - 0x0000, /* Mic */ - 0x4b4b, /* CD */ - 0x4b4b, /* Recording monitor */ - 0x4b4b, /* SB PCM */ - 0x4b4b, /* Recording level */ - 0x4b4b, /* Input gain */ -0x4b4b}; /* Output gain */ - -#else /* If the user selected just plain SB Pro */ - -static u_short levels[SOUND_MIXER_NRDEVICES] = -{ - 0x5a5a, /* Master Volume */ - 0x4b4b, /* Bass */ - 0x4b4b, /* Treble */ - 0x4b4b, /* FM */ - 0x4b4b, /* PCM */ - 0x4b4b, /* PC Speaker */ - 0x4b4b, /* Ext Line */ - 0x1010, /* Mic */ - 0x4b4b, /* CD */ - 0x4b4b, /* Recording monitor */ - 0x4b4b, /* SB PCM */ - 0x4b4b, /* Recording level */ - 0x4b4b, /* Input gain */ -0x4b4b}; /* Output gain */ -#endif /* SM_GAMES */ - #if 0 static u_char sb16_recmasks_L[SOUND_MIXER_NRDEVICES] = { diff --git a/sys/dev/sound/isa/mss.c b/sys/dev/sound/isa/mss.c index 9d0a0cf..0380121 100644 --- a/sys/dev/sound/isa/mss.c +++ b/sys/dev/sound/isa/mss.c @@ -1,134 +1,420 @@ /* - * sound/ad1848.c - * - * Driver for Microsoft Sound System/Windows Sound System (mss) - * -compatible boards. This includes: - * - * AD1848, CS4248, CS423x, OPTi931, Yamaha OPL/SAx and many others. - * + * Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk> * Copyright Luigi Rizzo, 1997,1998 * Copyright by Hannu Savolainen 1994, 1995 - * + * 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. + * 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. - * - * Full data sheets in PDF format for the MSS-compatible chips - * are available at + * 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. * - * http://www.crystal.com/ for the CS42XX series, or - * http://www.opti.com/ for the OPTi931 + * $Id$ */ -#include <i386/isa/snd/sound.h> +#include <dev/pcm/sound.h> + #if NPCM > 0 -/* - * board-specific include files - */ +/* board-specific include files */ +#include <dev/pcm/isa/mss.h> -#include <i386/isa/snd/mss.h> +struct mss_info; -/* - * prototypes for procedures exported in the device descriptor - */ +struct mss_chinfo { + struct mss_info *parent; + pcm_channel *channel; + snd_dbuf *buffer; + int dir; +}; -static int mss_probe(struct isa_device *dev); -static int mss_attach(struct isa_device *dev); +struct mss_info { + struct resource *io_base; /* primary I/O address for the board */ + int io_rid; + struct resource *conf_base; /* and the opti931 also has a config space */ + int conf_rid; + struct resource *irq; + int irq_rid; + struct resource *drq1; /* play */ + int drq1_rid; + struct resource *drq2; /* rec */ + int drq2_rid; + bus_dma_tag_t parent_dmat; + + int pdma, rdma; + int bd_id; /* used to hold board-id info, eg. sb version, + * mss codec type, etc. etc. + */ + u_long bd_flags; /* board-specific flags */ + struct mss_chinfo pch, rch; +}; -d_open_t mss_open; /* this is a generic full-duplex open routine */ -static d_close_t mss_close; -static d_ioctl_t mss_ioctl; -static irq_proc_t mss_intr; -static irq_proc_t opti931_intr; -static snd_callback_t mss_callback; - -/* - * prototypes for local functions - */ +static int mss_probe(device_t dev); +static int mss_attach(device_t dev); + +static driver_intr_t mss_intr; -static void mss_reinit(snddev_info *d); -static int AD_WAIT_INIT(snddev_info *d, int x); -static int mss_mixer_set(snddev_info *d, int dev, int value); -static int mss_set_recsrc(snddev_info *d, int mask); -static void ad1848_mixer_reset(snddev_info *d); +/* prototypes for local functions */ +static int mss_detect(device_t dev, struct mss_info *mss); +static char *ymf_test(device_t dev, struct mss_info *mss); +static void ad_unmute(struct mss_info *mss); -static void opti_write(int io_base, u_char reg, u_char data); -static u_char opti_read(int io_base, u_char reg); -static void ad_write(snddev_info *d, int reg, u_char data); -static void ad_write_cnt(snddev_info *d, int reg, u_short data); -static int ad_read(snddev_info *d, int reg); +/* mixer set funcs */ +static int mss_mixer_set(struct mss_info *mss, int dev, int left, int right); +static int mss_set_recsrc(struct mss_info *mss, int mask); -#if NPNP > 0 /* the ad1816 is pnp only */ -/* ad1816 prototypes */ +/* io funcs */ +static int ad_wait_init(struct mss_info *mss, int x); +static int ad_read(struct mss_info *mss, int reg); +static void ad_write(struct mss_info *mss, int reg, u_char data); +static void ad_write_cnt(struct mss_info *mss, int reg, u_short data); + +/* io primitives */ +static void conf_wr(struct mss_info *mss, u_char reg, u_char data); +static u_char conf_rd(struct mss_info *mss, u_char reg); + +#if NPNP > 0 +static int pnpmss_probe(device_t dev); +static int pnpmss_attach(device_t dev); + +static driver_intr_t opti931_intr; +static driver_intr_t ad1816_intr; /* IO primitives */ -static int ad1816_wait_init(snddev_info * d, int x); -static u_short ad1816_read(snddev_info * d, u_int reg); -static void ad1816_write(snddev_info * d, u_int reg, u_short data); -/* intr and callback functions */ -static irq_proc_t ad1816_intr; -static snd_callback_t ad1816_callback; -/* device specific ioctl calls */ -static d_ioctl_t ad1816_ioctl; -/* parameter set functions */ -static void ad1816_reinit(snddev_info * d); -static int ad1816_mixer_set(snddev_info * d, int dev, int value); -static int ad1816_set_recsrc(snddev_info * d, int mask); -static void ad1816_mixer_reset(snddev_info * d); - -/* ad1816 prototypes end */ +static int ad1816_wait_init(struct mss_info *mss, int x); +static u_short ad1816_read(struct mss_info *mss, u_int reg); +static void ad1816_write(struct mss_info *mss, u_int reg, u_short data); + +/* mixer set functions */ +static int ad1816_mixer_set(struct mss_info *mss, int dev, int left, int right); +static int ad1816_set_recsrc(struct mss_info *mss, int mask); #endif -/* - * device descriptors for the boards supported by this module. - */ -snddev_info mss_op_desc = { - "mss", +static int mssmix_init(snd_mixer *m); +static int mssmix_set(snd_mixer *m, unsigned dev, unsigned left, unsigned right); +static int mssmix_setrecsrc(snd_mixer *m, u_int32_t src); +static snd_mixer mss_mixer = { + "MSS mixer", + mssmix_init, + mssmix_set, + mssmix_setrecsrc, +}; + +static int ymmix_init(snd_mixer *m); +static int ymmix_set(snd_mixer *m, unsigned dev, unsigned left, unsigned right); +static int ymmix_setrecsrc(snd_mixer *m, u_int32_t src); +static snd_mixer yamaha_mixer = { + "OPL3-SAx mixer", + ymmix_init, + ymmix_set, + ymmix_setrecsrc, +}; + +static devclass_t pcm_devclass; + +/* channel interface */ +static void *msschan_init(void *devinfo, snd_dbuf *b, pcm_channel *c, int dir); +static int msschan_setdir(void *data, int dir); +static int msschan_setformat(void *data, u_int32_t format); +static int msschan_setspeed(void *data, u_int32_t speed); +static int msschan_setblocksize(void *data, u_int32_t blocksize); +static int msschan_trigger(void *data, int go); +static int msschan_getptr(void *data); +static pcmchan_caps *msschan_getcaps(void *data); + +static pcmchan_caps mss_caps = { + 4000, 48000, + AFMT_STEREO | AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW, + AFMT_STEREO | AFMT_S16_LE +}; + +static pcmchan_caps ad1816_caps = { + 4000, 55200, + AFMT_STEREO | AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW, + AFMT_STEREO | AFMT_S16_LE +}; + +static pcmchan_caps guspnp_caps = { + 4000, 48000, + AFMT_STEREO | AFMT_U8 | AFMT_S16_LE | AFMT_A_LAW, + AFMT_STEREO | AFMT_S16_LE +}; + +static pcmchan_caps opti931_caps = { + 4000, 4800, + AFMT_STEREO | AFMT_U8 | AFMT_S16_LE, + AFMT_STEREO | AFMT_S16_LE +}; + +static pcm_channel mss_chantemplate = { + msschan_init, + msschan_setdir, + msschan_setformat, + msschan_setspeed, + msschan_setblocksize, + msschan_trigger, + msschan_getptr, + msschan_getcaps, +}; + +#define MD_AD1848 0x91 +#define MD_AD1845 0x92 +#define MD_AD1816 0x93 +#define MD_CS4248 0xA1 +#define MD_CS4231 0xA2 +#define MD_CS4231A 0xA3 +#define MD_CS4232 0xA4 +#define MD_CS4232A 0xA5 +#define MD_CS4236 0xA6 +#define MD_CS4237 0xA7 +#define MD_OPTI931 0xB1 +#define MD_OPTI925 0xB2 +#define MD_GUSPNP 0xB8 +#define MD_YM0020 0xC1 +#define MD_VIVO 0xD1 + +#define DV_F_TRUE_MSS 0x00010000 /* mss _with_ base regs */ + +#define FULL_DUPLEX(x) ((x)->bd_flags & BD_F_DUPLEX) + +static int +port_rd(struct resource *port, int off) +{ + if (port) + return bus_space_read_1(rman_get_bustag(port), + rman_get_bushandle(port), + off); + else + return -1; +} + +static void +port_wr(struct resource *port, int off, u_int8_t data) +{ + if (port) + return bus_space_write_1(rman_get_bustag(port), + rman_get_bushandle(port), + off, data); +} + +static int +io_rd(struct mss_info *mss, int reg) +{ + if (mss->bd_flags & BD_F_MSS_OFFSET) reg -= 4; + return port_rd(mss->io_base, reg); +} + +static void +io_wr(struct mss_info *mss, int reg, u_int8_t data) +{ + if (mss->bd_flags & BD_F_MSS_OFFSET) reg -= 4; + return port_wr(mss->io_base, reg, data); +} + +static void +conf_wr(struct mss_info *mss, u_char reg, u_char value) +{ + port_wr(mss->conf_base, 0, reg); + port_wr(mss->conf_base, 1, value); +} - SNDCARD_MSS, - mss_probe, - mss_attach, +static u_char +conf_rd(struct mss_info *mss, u_char reg) +{ + port_wr(mss->conf_base, 0, reg); + return port_rd(mss->conf_base, 1); +} + +#if NPNP > 0 +static void +gus_wr(struct mss_info *mss, u_char reg, u_char value) +{ + port_wr(mss->conf_base, 3, reg); + port_wr(mss->conf_base, 5, value); +} - mss_open, - mss_close, - NULL /* mss_read */, - NULL /* mss_write */, - mss_ioctl, - sndselect /* mss_select */, +static u_char +gus_rd(struct mss_info *mss, u_char reg) +{ + port_wr(mss->conf_base, 3, reg); + return port_rd(mss->conf_base, 5); +} +#endif - mss_intr, - mss_callback , +static void +mss_release_resources(struct mss_info *mss, device_t dev) +{ + if (mss->irq) { + bus_release_resource(dev, SYS_RES_IRQ, mss->irq_rid, + mss->irq); + mss->irq = 0; + } + if (mss->drq1) { + bus_release_resource(dev, SYS_RES_DRQ, mss->drq1_rid, + mss->drq1); + mss->drq1 = 0; + mss->pdma = -1; + } + if (mss->drq2) { + bus_release_resource(dev, SYS_RES_DRQ, mss->drq2_rid, + mss->drq2); + mss->drq2 = 0; + mss->rdma = -1; + } + if (mss->io_base) { + bus_release_resource(dev, SYS_RES_IOPORT, mss->io_rid, + mss->io_base); + mss->io_base = 0; + } + if (mss->conf_base) { + bus_release_resource(dev, SYS_RES_IOPORT, mss->conf_rid, + mss->conf_base); + mss->conf_base = 0; + } + free(mss, M_DEVBUF); +} - DSP_BUFFSIZE, /* bufsize */ +static int +mss_alloc_resources(struct mss_info *mss, device_t dev) +{ + int ok = 1; + if (!mss->io_base) + mss->io_base = bus_alloc_resource(dev, SYS_RES_IOPORT, &mss->io_rid, + 0, ~0, 1, RF_ACTIVE); + if (!mss->irq) + mss->irq = bus_alloc_resource(dev, SYS_RES_IRQ, &mss->irq_rid, + 0, ~0, 1, RF_ACTIVE); + if (!mss->drq1) + mss->drq1 = bus_alloc_resource(dev, SYS_RES_DRQ, &mss->drq1_rid, + 0, ~0, 1, RF_ACTIVE); + if (mss->conf_rid >= 0 && !mss->conf_base) + mss->conf_base = bus_alloc_resource(dev, SYS_RES_IOPORT, &mss->conf_rid, + 0, ~0, 1, RF_ACTIVE); + if (mss->drq2_rid >= 0 && !mss->drq2) + mss->drq2 = bus_alloc_resource(dev, SYS_RES_DRQ, &mss->drq2_rid, + 0, ~0, 1, RF_ACTIVE); + + if (!mss->io_base || !mss->drq1 || !mss->irq) ok = 0; + if (mss->conf_rid >= 0 && !mss->conf_base) ok = 0; + if (mss->drq2_rid >= 0 && !mss->drq2) ok = 0; + + if (ok) { + mss->pdma = rman_get_start(mss->drq1); + isa_dma_acquire(mss->pdma); + isa_dmainit(mss->pdma, DSP_BUFFSIZE); + mss->bd_flags &= ~BD_F_DUPLEX; + if (mss->drq2) { + mss->rdma = rman_get_start(mss->drq2); + isa_dma_acquire(mss->rdma); + isa_dmainit(mss->rdma, DSP_BUFFSIZE); + mss->bd_flags |= BD_F_DUPLEX; + } else mss->rdma = mss->pdma; + } + return ok; +} - AFMT_STEREO | - AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW, /* audio formats */ - /* - * the enhanced boards also have AFMT_IMA_ADPCM | AFMT_S16_BE - * but we do not use these modes. - */ -} ; +static int +mss_init(struct mss_info *mss, device_t dev) +{ + mss->bd_flags |= BD_F_MCE_BIT; + switch(mss->bd_id) { +#if NPNP > 0 + case MD_OPTI931: + conf_wr(mss, 4, 0xd6); /* fifo empty, OPL3, audio enable, SB3.2 */ + ad_write(mss, 10, 2); /* enable interrupts */ + conf_wr(mss, 6, 2); /* MCIR6: mss enable, sb disable */ + conf_wr(mss, 5, 0x28); /* MCIR5: codec in exp. mode,fifo */ + break; + + case MD_GUSPNP: + { + struct resource *alt; + int rid, tmp; + + gus_wr(mss, 0x4c /* _URSTI */, 0);/* Pull reset */ + DELAY(1000 * 30); + /* release reset and enable DAC */ + gus_wr(mss, 0x4c /* _URSTI */, 3); + DELAY(1000 * 30); + /* end of reset */ + + rid = 0; + alt = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, + 0, ~0, 1, RF_ACTIVE); + port_wr(alt, 0, 0xC); /* enable int and dma */ + bus_release_resource(dev, SYS_RES_IOPORT, rid, alt); + + /* + * unmute left & right line. Need to go in mode3, unmute, + * and back to mode 2 + */ + tmp = ad_read(mss, 0x0c); + ad_write(mss, 0x0c, 0x6c); /* special value to enter mode 3 */ + ad_write(mss, 0x19, 0); /* unmute left */ + ad_write(mss, 0x1b, 0); /* unmute right */ + ad_write(mss, 0x0c, tmp); /* restore old mode */ + + /* send codec interrupts on irq1 and only use that one */ + gus_wr(mss, 0x5a, 0x4f); + + /* enable access to hidden regs */ + tmp = gus_rd(mss, 0x5b /* IVERI */); + gus_wr(mss, 0x5b, tmp | 1); + BVDDB(printf("GUS: silicon rev %c\n", 'A' + ((tmp & 0xf) >> 4))); + break; + } + + case MD_AD1816: + ad1816_write(mss, 1, 0x2); /* disable interrupts */ + ad1816_write(mss, 32, 0x90F0); /* SoundSys Mode, split fmt */ + + ad1816_write(mss, 5, 0x8080); /* FM volume mute */ + ad1816_write(mss, 6, 0x8080); /* I2S1 volume mute */ + ad1816_write(mss, 7, 0x8080); /* I2S0 volume mute */ + ad1816_write(mss, 17, 0x8888); /* VID Volume mute */ + ad1816_write(mss, 20, 0x5050); /* recsrc mic, agc off */ + /* adc gain is set to 0 */ + break; +#endif + case MD_YM0020: + { + u_char r6, r9; + conf_wr(mss, OPL3SAx_DMACONF, 0xa9); /* dma-b rec, dma-a play */ + r6 = conf_rd(mss, OPL3SAx_DMACONF); + r9 = conf_rd(mss, OPL3SAx_MISC); /* version */ + BVDDB(printf("Yamaha: ver 0x%x DMA config 0x%x\n", r6, r9);) + /* yamaha - set volume to max */ + conf_wr(mss, OPL3SAx_VOLUMEL, 0); + conf_wr(mss, OPL3SAx_VOLUMER, 0); + conf_wr(mss, OPL3SAx_DMACONF, FULL_DUPLEX(mss)? 0xa9 : 0x8b); + break; + } + } + if (FULL_DUPLEX(mss) && mss->bd_id != MD_OPTI931) + ad_write(mss, 12, ad_read(mss, 12) | 0x40); /* mode 2 */ + ad_write(mss, 9, FULL_DUPLEX(mss)? 0 : 4); + ad_write(mss, 10, 2); /* int enable */ + io_wr(mss, MSS_STATUS, 0); /* Clear interrupt status */ + /* the following seem required on the CS4232 */ + ad_unmute(mss); + return 0; +} /* * mss_probe() is the probe routine. Note, it is not necessary to @@ -146,410 +432,471 @@ snddev_info mss_op_desc = { */ static int -mss_probe(struct isa_device *dev) -{ - u_char tmp; - int irq = ffs(dev->id_irq) - 1; - - bzero(&pcm_info[dev->id_unit], sizeof(pcm_info[dev->id_unit]) ); - if (dev->id_iobase == -1) { - dev->id_iobase = 0x530; - BVDDB(printf("mss_probe: no address supplied, try default 0x%x\n", - dev->id_iobase)); - } - if (snd_conflict(dev->id_iobase)) - return 0 ; - - if ( !(dev->id_flags & DV_F_TRUE_MSS) ) /* Has no IRQ/DMA registers */ - goto mss_probe_end; - - /* - * Check if the IO port returns valid signature. The original MS - * Sound system returns 0x04 while some cards - * (AudioTriX Pro for example) return 0x00 or 0x0f. - */ - - tmp = inb(dev->id_iobase + 3); - if (tmp == 0xff) { /* Bus float */ - BVDDB(printf("I/O address inactive (%x), try pseudo_mss\n", tmp)); - dev->id_flags &= ~DV_F_TRUE_MSS ; - goto mss_probe_end; - } - tmp &= 0x3f ; - if (tmp != 0x04 && tmp != 0x0f && tmp != 0x00) { - BVDDB(printf("No MSS signature detected on port 0x%x (0x%x)\n", - dev->id_iobase, inb(dev->id_iobase + 3))); - return 0; - } - if (irq > 11) { - printf("MSS: Bad IRQ %d\n", irq); - return 0; - } - if (dev->id_drq != 0 && dev->id_drq != 1 && dev->id_drq != 3) { - printf("MSS: Bad DMA %d\n", dev->id_drq); - return 0; - } - if (inb(dev->id_iobase + 3) & 0x80) { - /* 8-bit board: only drq1/3 and irq7/9 */ - if (dev->id_drq == 0) { - printf("MSS: Can't use DMA0 with a 8 bit card/slot\n"); - return 0; - } - if (irq != 7 && irq != 9) { - printf("MSS: Can't use IRQ%d with a 8 bit card/slot\n", irq); - return 0; - } - } -mss_probe_end: - return mss_detect(dev) ? 8 : 0 ; /* mss uses 8 regs */ -} - -#if NPNP > 0 -static int -ad1816_attach(struct isa_device *dev) +mss_probe(device_t dev) { - snddev_info *d = &(pcm_info[dev->id_unit]); - - dev->id_alive = 16; /* number of io ports */ - - if (FULL_DUPLEX(d)) - d->audio_fmt |= AFMT_FULLDUPLEX; - - ad1816_write(d, 1, 0x2);/* disable interrupts */ - ad1816_write(d, 32, 0x90F0); /* SoundSystem Mode, split format */ - - ad1816_write(d, 5, 0x8080); /* FM volume mute */ - ad1816_write(d, 6, 0x8080); /* I2S1 volume mute */ - ad1816_write(d, 7, 0x8080); /* I2S0 volume mute */ - ad1816_write(d, 17, 0x8888); /* VID Volume mute */ - ad1816_write(d, 20, 0x5050); /* Source select Mic & auto gain ctrl - * off */ - /* adc gain is set to 0 */ - ad1816_reinit(d); - ad1816_mixer_reset(d); - return 0 ; + u_char tmp, tmpx; + int flags, irq, drq, result = ENXIO, setres = 0; + struct mss_info *mss; + + if (isa_get_vendorid(dev)) return ENXIO; /* not yet */ + + mss = (struct mss_info *)malloc(sizeof *mss, M_DEVBUF, M_NOWAIT); + if (!mss) return ENXIO; + bzero(mss, sizeof *mss); + + mss->io_rid = 0; + mss->conf_rid = -1; + mss->irq_rid = 0; + mss->drq1_rid = 0; + mss->drq2_rid = -1; + mss->io_base = bus_alloc_resource(dev, SYS_RES_IOPORT, &mss->io_rid, + 0, ~0, 8, RF_ACTIVE); + if (!mss->io_base) { + BVDDB(printf("mss_probe: no address given, try 0x%x\n", 0x530)); + mss->io_rid = 0; + /* XXX verify this */ + setres = 1; + ISA_SET_RESOURCE(device_get_parent(dev), dev, SYS_RES_IOPORT, mss->io_rid, + 0x530, 8); + mss->io_base = bus_alloc_resource(dev, SYS_RES_IOPORT, &mss->io_rid, + 0, ~0, 8, RF_ACTIVE); + } + if (!mss->io_base) goto no; + + /* got irq/dma regs? */ + flags = isa_get_flags(dev); + irq = isa_get_irq(dev); + drq = isa_get_drq(dev); + + if (!(isa_get_flags(dev) & DV_F_TRUE_MSS)) goto mss_probe_end; + + /* + * Check if the IO port returns valid signature. The original MS + * Sound system returns 0x04 while some cards + * (AudioTriX Pro for example) return 0x00 or 0x0f. + */ + + device_set_desc(dev, "MSS"); + tmpx = tmp = io_rd(mss, 3); + if (tmp == 0xff) { /* Bus float */ + BVDDB(printf("I/O addr inactive (%x), try pseudo_mss\n", tmp)); + isa_set_flags(dev, flags & ~DV_F_TRUE_MSS); + goto mss_probe_end; + } + tmp &= 0x3f; + if (!(tmp == 0x04 || tmp == 0x0f || tmp == 0x00)) { + BVDDB(printf("No MSS signature detected on port 0x%lx (0x%x)\n", + rman_get_start(mss->io_base), tmpx)); + goto no; + } + if (irq > 11) { + printf("MSS: Bad IRQ %d\n", irq); + goto no; + } + if (!(drq == 0 || drq == 1 || drq == 3)) { + printf("MSS: Bad DMA %d\n", drq); + goto no; + } + if (tmpx & 0x80) { + /* 8-bit board: only drq1/3 and irq7/9 */ + if (drq == 0) { + printf("MSS: Can't use DMA0 with a 8 bit card/slot\n"); + goto no; + } + if (!(irq == 7 || irq == 9)) { + printf("MSS: Can't use IRQ%d with a 8 bit card/slot\n", irq); + goto no; + } + } + mss_probe_end: + result = mss_detect(dev, mss); + no: + if (setres) ISA_DELETE_RESOURCE(device_get_parent(dev), dev, + SYS_RES_IOPORT, mss->io_rid); /* XXX ? */ + mss_release_resources(mss, dev); + return result; } -#endif /* NPNP */ - -/* - * the address passed as io_base for mss_attach is also the old - * MSS base address (e.g. 0x530). The codec is four locations ahead. - * Note that the attach routine for PnP devices might support - * device-specific initializations. - */ static int -mss_attach(struct isa_device *dev) +mss_detect(device_t dev, struct mss_info *mss) { - snddev_info *d = &(pcm_info[dev->id_unit]); - - printf("mss_attach <%s>%d at 0x%x irq %d dma %d:%d flags 0x%x\n", - d->name, dev->id_unit, - d->io_base, d->irq, d->dbuf_out.chan, d->dbuf_in.chan, dev->id_flags); + int i; + u_char tmp, tmp1, tmp2; + char *name, *yamaha; -#if NPNP > 0 - if (d->bd_id == MD_AD1816) - return ad1816_attach(dev); + if (mss->bd_id != 0) { + device_printf(dev, "presel bd_id 0x%04x -- %s\n", mss->bd_id, + device_get_desc(dev)); + return 0; + } + + name = "AD1848"; + mss->bd_id = MD_AD1848; /* AD1848 or CS4248 */ + + /* + * Check that the I/O address is in use. + * + * bit 7 of the base I/O port is known to be 0 after the chip has + * performed its power on initialization. Just assume this has + * happened before the OS is starting. + * + * If the I/O address is unused, it typically returns 0xff. + */ + + for (i = 0; i < 10; i++) + if ((tmp = io_rd(mss, MSS_INDEX)) & MSS_IDXBUSY) DELAY(10000); + else break; + + if (i >= 10) { /* Not a AD1848 */ + BVDDB(printf("mss_detect, busy still set (0x%02x)\n", tmp)); + goto no; + } + /* + * Test if it's possible to change contents of the indirect + * registers. Registers 0 and 1 are ADC volume registers. The bit + * 0x10 is read only so try to avoid using it. + */ + + ad_write(mss, 0, 0xaa); + ad_write(mss, 1, 0x45);/* 0x55 with bit 0x10 clear */ + tmp1 = ad_read(mss, 0); + tmp2 = ad_read(mss, 1); + if (tmp1 != 0xaa || tmp2 != 0x45) { + BVDDB(printf("mss_detect error - IREG (%x/%x)\n", tmp1, tmp2)); + goto no; + } + + ad_write(mss, 0, 0x45); + ad_write(mss, 1, 0xaa); + tmp1 = ad_read(mss, 0); + tmp2 = ad_read(mss, 1); + if (tmp1 != 0x45 || tmp2 != 0xaa) { + BVDDB(printf("mss_detect error - IREG2 (%x/%x)\n", tmp1, tmp2)); + goto no; + } + + /* + * The indirect register I12 has some read only bits. Lets try to + * change them. + */ + + tmp = ad_read(mss, 12); + ad_write(mss, 12, (~tmp) & 0x0f); + tmp1 = ad_read(mss, 12); + + if ((tmp & 0x0f) != (tmp1 & 0x0f)) { + BVDDB(printf("mss_detect - I12 (0x%02x was 0x%02x)\n", tmp1, tmp)); + goto no; + } + + /* + * NOTE! Last 4 bits of the reg I12 tell the chip revision. + * 0x01=RevB + * 0x0A=RevC. also CS4231/CS4231A and OPTi931 + */ + + BVDDB(printf("mss_detect - chip revision 0x%02x\n", tmp & 0x0f);) + + /* + * The original AD1848/CS4248 has just 16 indirect registers. This + * means that I0 and I16 should return the same value (etc.). Ensure + * that the Mode2 enable bit of I12 is 0. Otherwise this test fails + * with new parts. + */ + + ad_write(mss, 12, 0); /* Mode2=disabled */ +#if 0 + for (i = 0; i < 16; i++) { + if ((tmp1 = ad_read(mss, i)) != (tmp2 = ad_read(mss, i + 16))) { + BVDDB(printf("mss_detect warning - I%d: 0x%02x/0x%02x\n", + i, tmp1, tmp2)); + /* + * note - this seems to fail on the 4232 on I11. So we just break + * rather than fail. (which makes this test pointless - cg) + */ + break; /* return 0; */ + } + } #endif - dev->id_alive = 8 ; /* number of io ports */ - /* should be already set but just in case... */ - - if ( dev->id_flags & DV_F_TRUE_MSS ) { - /* has IRQ/DMA registers, set IRQ and DMA addr */ - static char interrupt_bits[12] = { - -1, -1, -1, -1, -1, 0x28, -1, 0x08, -1, 0x10, 0x18, 0x20 - }; - static char dma_bits[4] = { 1, 2, 0, 3 }; - char bits ; - - if (d->irq == -1 || (bits = interrupt_bits[d->irq]) == -1) { - dev->id_irq = 0 ; /* makk invalid irq */ - return 0 ; + /* + * Try to switch the chip to mode2 (CS4231) by setting the MODE2 bit + * (0x40). The bit 0x80 is always 1 in CS4248 and CS4231. + * + * On the OPTi931, however, I12 is readonly and only contains the + * chip revision ID (as in the CS4231A). The upper bits return 0. + */ + + ad_write(mss, 12, 0x40); /* Set mode2, clear 0x80 */ + + tmp1 = ad_read(mss, 12); + if (tmp1 & 0x80) name = "CS4248"; /* Our best knowledge just now */ + if ((tmp1 & 0xf0) == 0x00) { + BVDDB(printf("this should be an OPTi931\n");) + } else if ((tmp1 & 0xc0) != 0xC0) goto gotit; + /* + * The 4231 has bit7=1 always, and bit6 we just set to 1. + * We want to check that this is really a CS4231 + * Verify that setting I0 doesn't change I16. + */ + ad_write(mss, 16, 0); /* Set I16 to known value */ + ad_write(mss, 0, 0x45); + if ((tmp1 = ad_read(mss, 16)) == 0x45) goto gotit; + + ad_write(mss, 0, 0xaa); + if ((tmp1 = ad_read(mss, 16)) == 0xaa) { /* Rotten bits? */ + BVDDB(printf("mss_detect error - step H(%x)\n", tmp1)); + goto no; } + /* Verify that some bits of I25 are read only. */ + tmp1 = ad_read(mss, 25); /* Original bits */ + ad_write(mss, 25, ~tmp1); /* Invert all bits */ + if ((ad_read(mss, 25) & 0xe7) == (tmp1 & 0xe7)) { + int id; - outb(dev->id_iobase, bits | 0x40); /* config port */ - if ((inb(dev->id_iobase + 3) & 0x40) == 0) /* version port */ - printf("[IRQ Conflict?]"); - - /* Write IRQ+DMA setup */ - if ( ! FULL_DUPLEX(d) ) /* single chan dma */ - outb(dev->id_iobase, bits | dma_bits[d->dbuf_out.chan]); - else { - if (d->dbuf_out.chan == 0 && d->dbuf_in.chan == 1) - bits |= 5 ; - else if (d->dbuf_out.chan == 1 && d->dbuf_in.chan == 0) - bits |= 6 ; - else if (d->dbuf_out.chan == 3 && d->dbuf_in.chan == 0) - bits |= 7 ; - else { - printf("invalid dual dma config %d:%d\n", - d->dbuf_out.chan, d->dbuf_in.chan); - dev->id_irq = 0 ; - dev->id_alive = 0 ; /* this makes attach fail. */ - return 0 ; - } - outb(dev->id_iobase, bits ); - } - } - if (1) { /* machine-specific code for the Toshiba Libretto */ - u_char r6, r9; - outb( 0x370, 6 /* dma config */ ); - outb( 0x371, 0xa9 /* config: DMA-B for rec, DMA-A for play */); - r6 = inb( 0x371 /* read */ ); - outb( 0x370, 0xa /* version */ ); - r9 = inb( 0x371 /* read */ ); - DEB(printf("Yamaha: ver 0x%x DMA config 0x%x\n", r6, r9);) - /* - * yamaha - set volume to max - */ - outb( 0x370, 7 /* volume left */ ); - outb( 0x371, 0 /* max level */ ); - outb( 0x370, 8 /* volume right */ ); - outb( 0x371, 0 /* max level */ ); - } - if ( FULL_DUPLEX(d) ) - d->audio_fmt |= AFMT_FULLDUPLEX ; - if (d->bd_id == MD_YM0020) { - DDB(printf("setting up yamaha registers\n")); - outb(0x370, 6 /* dma config */ ) ; - if (FULL_DUPLEX(d)) - outb(0x371, 0xa9 ); /* use both dma chans */ - else - outb(0x371, 0x8b ); /* use low dma chan */ - } - mss_reinit(d); - ad1848_mixer_reset(d); - return 0; -} + /* It's at least CS4231 */ + name = "CS4231"; + mss->bd_id = MD_CS4231; -int -mss_open(dev_t i_dev, int flags, int mode, struct proc * p) -{ - int unit; - int dev; - snddev_info *d; - u_long s; - - dev = minor(i_dev); - unit = dev >> 4 ; - dev &= 0xf ; - d = &pcm_info[unit] ; - - s = spltty(); - /* - * This was meant to support up to 2 open descriptors for the - * some device, and check proper device usage on open. - * Unfortunately, the kernel will trap all close() calls but - * the last one, with the consequence that we cannot really - * keep track of which channels are busy. - * So, the correct tests cannot be done :( and we must rely - * on the locks on concurrent operations of the same type and - * on some approximate tests... - */ - - if (dev == SND_DEV_AUDIO) - d->flags |= SND_F_BUSY_AUDIO ; - else if (dev == SND_DEV_DSP) - d->flags |= SND_F_BUSY_DSP ; - else if (dev == SND_DEV_DSP16) - d->flags |= SND_F_BUSY_DSP16 ; - if ( d->flags & SND_F_BUSY ) - splx(s); /* device was already set, no need to reinit */ - else { - /* - * device was idle. Do the necessary initialization, - * but no need keep interrupts blocked. - * will not get them - */ + /* + * It could be an AD1845 or CS4231A as well. + * CS4231 and AD1845 report the same revision info in I25 + * while the CS4231A reports different. + */ - splx(s); - d->play_speed = d->rec_speed = DSP_DEFAULT_SPEED ; - d->flags |= SND_F_BUSY ; + id = ad_read(mss, 25) & 0xe7; + /* + * b7-b5 = version number; + * 100 : all CS4231 + * 101 : CS4231A + * + * b2-b0 = chip id; + */ + switch (id) { - d->wsel.si_pid = 0; - d->wsel.si_flags = 0; + case 0xa0: + name = "CS4231A"; + mss->bd_id = MD_CS4231A; + break; - d->rsel.si_pid = 0; - d->rsel.si_flags = 0; + case 0xa2: + name = "CS4232"; + mss->bd_id = MD_CS4232; + break; - d->dbuf_out.total = d->dbuf_out.prev_total = 0 ; - d->dbuf_in.total = d->dbuf_in.prev_total = 0 ; + case 0xb2: + /* strange: the 4231 data sheet says b4-b3 are XX + * so this should be the same as 0xa2 + */ + name = "CS4232A"; + mss->bd_id = MD_CS4232A; + break; - if (flags & O_NONBLOCK) - d->flags |= SND_F_NBIO ; + case 0x80: + /* + * It must be a CS4231 or AD1845. The register I23 + * of CS4231 is undefined and it appears to be read + * only. AD1845 uses I23 for setting sample rate. + * Assume the chip is AD1845 if I23 is changeable. + */ + + tmp = ad_read(mss, 23); + + ad_write(mss, 23, ~tmp); + if (ad_read(mss, 23) != tmp) { /* AD1845 ? */ + name = "AD1845"; + mss->bd_id = MD_AD1845; + } + ad_write(mss, 23, tmp); /* Restore */ + + yamaha = ymf_test(dev, mss); + if (yamaha) { + mss->bd_id = MD_YM0020; + name = yamaha; + } + break; - switch (dev) { - default : - case SND_DEV_AUDIO : - d->play_fmt = d->rec_fmt = AFMT_MU_LAW ; - break ; - case SND_DEV_DSP : - d->play_fmt = d->rec_fmt = AFMT_U8 ; - break ; - case SND_DEV_DSP16 : - d->play_fmt = d->rec_fmt = AFMT_S16_LE ; - break; + case 0x83: /* CS4236 */ + case 0x03: /* CS4236 on Intel PR440FX motherboard XXX */ + name = "CS4236"; + mss->bd_id = MD_CS4236; + break; + + default: /* Assume CS4231 */ + BVDDB(printf("unknown id 0x%02x, assuming CS4231\n", id);) + mss->bd_id = MD_CS4231; + } } - ask_init(d); /* and reset buffers... */ - } - return 0 ; + ad_write(mss, 25, tmp1); /* Restore bits */ +gotit: + BVDDB(printf("mss_detect() - Detected %s\n", name)); + device_set_desc(dev, name); + isa_set_flags(dev, ((isa_get_flags(dev) & ~DV_F_DEV_MASK) | + ((mss->bd_id << DV_F_DEV_SHIFT) & DV_F_DEV_MASK))); + return 0; +no: + return ENXIO; } -static int -mss_close(dev_t i_dev, int flags, int mode, struct proc * p) +static char * +ymf_test(device_t dev, struct mss_info *mss) { - int unit; - int dev; - snddev_info *d; - u_long s; - - dev = minor(i_dev); - unit = dev >> 4 ; - dev &= 0xf; - d = &pcm_info[unit] ; - - /* - * We will only get a single close call when the last reference - * to the device is gone. But we must handle ourselves references - * through different devices. - */ - - s = spltty(); - - if (dev == SND_DEV_AUDIO) - d->flags &= ~SND_F_BUSY_AUDIO ; - else if (dev == SND_DEV_DSP) - d->flags &= ~SND_F_BUSY_DSP ; - else if (dev == SND_DEV_DSP16) - d->flags &= ~SND_F_BUSY_DSP16 ; - if ( d->flags & SND_F_BUSY_ANY ) /* still some device open */ - splx(s); - else { /* last one */ - d->flags |= SND_F_CLOSING ; - splx(s); /* is this ok here ? */ - snd_flush(d); - /* Clear interrupt status */ - if ( d->bd_id == MD_AD1816 ) - outb(ad1816_int(d), 0); - else - outb(io_Status(d), 0); - d->flags = 0 ; - } - return 0 ; + static int ports[] = {0x370, 0x310, 0x538}; + int p, i, j, version; + static char *chipset[] = { + NULL, /* 0 */ + "OPL3-SA2 (YMF711)", /* 1 */ + "OPL3-SA3 (YMF715)", /* 2 */ + "OPL3-SA3 (YMF715)", /* 3 */ + "OPL3-SAx (YMF719)", /* 4 */ + "OPL3-SAx (YMF719)", /* 5 */ + "OPL3-SAx (YMF719)", /* 6 */ + "OPL3-SAx (YMF719)", /* 7 */ + }; + + for (p = 0; p < 3; p++) { + mss->conf_rid = 1; + mss->conf_base = bus_alloc_resource(dev, + SYS_RES_IOPORT, + &mss->conf_rid, + ports[p], ports[p] + 1, 2, + RF_ACTIVE); + if (!mss->conf_base) return 0; + + /* Test the index port of the config registers */ + i = port_rd(mss->conf_base, 0); + port_wr(mss->conf_base, 0, OPL3SAx_DMACONF); + j = (port_rd(mss->conf_base, 0) == OPL3SAx_DMACONF)? 1 : 0; + port_wr(mss->conf_base, 0, i); + if (!j) { + bus_release_resource(dev, SYS_RES_IOPORT, + mss->conf_rid, mss->conf_base); + mss->conf_base = 0; + continue; + } + version = conf_rd(mss, OPL3SAx_MISC) & 0x07; + return chipset[version]; + } + return NULL; } static int -mss_ioctl(dev_t i_dev, u_long cmd, caddr_t arg, int mode, struct proc * p) +mss_doattach(device_t dev, struct mss_info *mss) { - snddev_info *d; - int unit; - int dev; - - dev = minor(i_dev); - unit = dev >> 4 ; - d = &pcm_info[unit] ; - /* - * handle mixer calls first. Reads are in the default handler, - * so do not bother about them. - */ - if ( (cmd & MIXER_WRITE(0)) == MIXER_WRITE(0) ) { - cmd &= 0xff ; - if (cmd == SOUND_MIXER_RECSRC) - return mss_set_recsrc(d, *(int *)arg) ; - else - return mss_mixer_set(d, cmd, *(int *)arg) ; - } - - return ENOSYS ; /* fallback to the default ioctl handler */ + snddev_info *d = device_get_softc(dev); + void *ih; + int flags = isa_get_flags(dev); + char status[SND_STATUSLEN]; + + if (!mss_alloc_resources(mss, dev)) goto no; + mss_init(mss, dev); + if (flags & DV_F_TRUE_MSS) { + /* has IRQ/DMA registers, set IRQ and DMA addr */ + static char interrupt_bits[12] = + {-1, -1, -1, -1, -1, 0x28, -1, 0x08, -1, 0x10, 0x18, 0x20}; + static char pdma_bits[4] = {1, 2, -1, 3}; + static char valid_rdma[4] = {1, 0, -1, 0}; + char bits; + + if (!mss->irq || (bits = interrupt_bits[rman_get_start(mss->irq)]) == -1) + goto no; + io_wr(mss, 0, bits | 0x40); /* config port */ + if ((io_rd(mss, 3) & 0x40) == 0) device_printf(dev, "IRQ Conflict?\n"); + /* Write IRQ+DMA setup */ + if (pdma_bits[mss->pdma] == -1) goto no; + bits |= pdma_bits[mss->pdma]; + if (mss->pdma != mss->rdma) { + if (mss->rdma == valid_rdma[mss->pdma]) bits |= 4; + else { + printf("invalid dual dma config %d:%d\n", + mss->pdma, mss->rdma); + goto no; + } + } + io_wr(mss, 0, bits); + printf("drq/irq conf %x\n", io_rd(mss, 0)); + } + mixer_init(d, (mss->bd_id == MD_YM0020)? &yamaha_mixer : &mss_mixer, mss); + switch (mss->bd_id) { + #if NPNP > 0 + case MD_AD1816: + bus_setup_intr(dev, mss->irq, INTR_TYPE_TTY, ad1816_intr, mss, &ih); + break; + + case MD_OPTI931: + bus_setup_intr(dev, mss->irq, INTR_TYPE_TTY, opti931_intr, mss, &ih); + break; + #endif + default: + bus_setup_intr(dev, mss->irq, INTR_TYPE_TTY, mss_intr, mss, &ih); + } + if (mss->pdma == mss->rdma) + pcm_setflags(dev, pcm_getflags(dev) | SD_F_SIMPLEX); + if (bus_dma_tag_create(/*parent*/NULL, /*alignment*/2, /*boundary*/0, + /*lowaddr*/BUS_SPACE_MAXADDR_24BIT, + /*highaddr*/BUS_SPACE_MAXADDR, + /*filter*/NULL, /*filterarg*/NULL, + /*maxsize*/DSP_BUFFSIZE, /*nsegments*/1, + /*maxsegz*/0x3ffff, + /*flags*/0, &mss->parent_dmat) != 0) { + device_printf(dev, "unable to create dma tag\n"); + goto no; + } + snprintf(status, SND_STATUSLEN, "at io 0x%lx irq %ld drq %d", + rman_get_start(mss->io_base), rman_get_start(mss->irq), mss->pdma); + if (mss->pdma != mss->rdma) snprintf(status + strlen(status), + SND_STATUSLEN - strlen(status), ":%d", mss->rdma); + + if (pcm_register(dev, mss, 1, 1)) goto no; + pcm_addchan(dev, PCMDIR_REC, &mss_chantemplate, mss); + pcm_addchan(dev, PCMDIR_PLAY, &mss_chantemplate, mss); + pcm_setstatus(dev, status); + + return 0; +no: + mss_release_resources(mss, dev); + return ENXIO; } - -/* - * the callback routine to handle all dma ops etc. - * With the exception of INIT, all other callbacks are invoked - * with interrupts disabled. - */ - static int -mss_callback(snddev_info *d, int reason) +mss_attach(device_t dev) { - u_char m; - int retry, wr, cnt; - - DEB(printf("-- mss_callback reason 0x%03x\n", reason)); - wr = reason & SND_CB_WR ; - reason &= SND_CB_REASON_MASK ; - switch (reason) { - case SND_CB_INIT : /* called with int enabled and no pending I/O */ - /* - * perform all necessary initializations for i/o - */ - d->rec_fmt = d->play_fmt ; /* no split format on the MSS */ - snd_set_blocksize(d); - mss_reinit(d); - reset_dbuf(& (d->dbuf_in), SND_CHAN_RD ); - reset_dbuf(& (d->dbuf_out), SND_CHAN_WR ); - return 1 ; - break ; - - case SND_CB_START : - cnt = wr ? d->dbuf_out.dl : d->dbuf_in.dl ; - if (d->play_fmt == AFMT_S16_LE) - cnt /= 2; - if (d->flags & SND_F_STEREO) - cnt /= 2; - cnt-- ; - - DEB(printf("-- (re)start cnt %d\n", cnt)); - m = ad_read(d,9) ; - DEB( if (m & 4) printf("OUCH! reg 9 0x%02x\n", m); ); - m |= wr ? I9_PEN : I9_CEN ; /* enable DMA */ - /* - * on the OPTi931 the enable bit seems hard to set... - */ - for (retry = 10; retry; retry--) { - ad_write(d, 9, m ); - if (ad_read(d,9) ==m) break; - } - if (retry == 0) - printf("start dma, failed to set bit 0x%02x 0x%02x\n", - m, ad_read(d, 9) ) ; - if (wr || ! FULL_DUPLEX(d) ) - ad_write_cnt(d, 14, cnt); - else - ad_write_cnt(d, 30, cnt); + struct mss_info *mss; + int flags = isa_get_flags(dev); + + mss = (struct mss_info *)malloc(sizeof *mss, M_DEVBUF, M_NOWAIT); + if (!mss) return ENXIO; + bzero(mss, sizeof *mss); + + mss->io_rid = 0; + mss->conf_rid = -1; + mss->irq_rid = 0; + mss->drq1_rid = 0; + mss->drq2_rid = -1; + if (flags & DV_F_DUAL_DMA) { + ISA_SET_RESOURCE(device_get_parent(dev), dev, SYS_RES_DRQ, 1, + flags & DV_F_DRQ_MASK, 1); + mss->drq2_rid = 1; + } + mss->bd_id = (isa_get_flags(dev) & DV_F_DEV_MASK) >> DV_F_DEV_SHIFT; + if (mss->bd_id == MD_YM0020) ymf_test(dev, mss); + return mss_doattach(dev, mss); +} - break ; +static device_method_t mss_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, mss_probe), + DEVMETHOD(device_attach, mss_attach), - case SND_CB_STOP : - case SND_CB_ABORT : /* XXX check this... */ - m = ad_read(d,9) ; - m &= wr ? ~I9_PEN : ~I9_CEN ; /* Stop DMA */ - /* - * on the OPTi931 the enable bit seems hard to set... - */ - for (retry = 10; retry ; retry-- ) { - ad_write(d, 9, m ); - if (ad_read(d,9) ==m) break; - } - if (retry == 0) - printf("start dma, failed to clear bit 0x%02x 0x%02x\n", - m, ad_read(d, 9) ) ; -#if 1 - /* - * try to disable DMA by clearing count registers. Not sure it - * is needed, and it might cause false interrupts when the - * DMA is re-enabled later. - */ - if (wr || ! FULL_DUPLEX(d) ) - ad_write_cnt(d, 14, 0); - else - ad_write_cnt(d, 30, 0); - break; -#endif - } - return 0 ; -} + { 0, 0 } +}; + +static driver_t mss_driver = { + "pcm", + mss_methods, + sizeof(snddev_info), +}; + +DRIVER_MODULE(mss, isa, mss_driver, pcm_devclass, 0, 0); /* * main irq handler for the CS423x. The OPTi931 code is @@ -567,492 +914,274 @@ mss_callback(snddev_info *d, int reason) */ static void -mss_intr(int unit) -{ - snddev_info *d = &pcm_info[unit]; - u_char c, served = 0; - int i; - - DEB(printf("mss_intr\n")); - ad_read(d, 11); /* fake read of status bits */ - - /* - * loop until there are interrupts, but no more than 10 times. - */ - for (i=10 ; i && inb(io_Status(d)) & 1 ; i-- ) { - /* get exact reason for full-duplex boards */ - c = FULL_DUPLEX(d) ? ad_read(d, 24) : 0x30 ; - c &= ~served ; - if ( d->dbuf_out.dl && (c & 0x10) ) { - served |= 0x10 ; - dsp_wrintr(d); - } - if ( d->dbuf_in.dl && (c & 0x20) ) { - served |= 0x20 ; - dsp_rdintr(d); - } - /* - * now ack the interrupt - */ - if ( FULL_DUPLEX(d) ) - ad_write(d, 24, ~c); /* ack selectively */ - else - outb(io_Status(d), 0); /* Clear interrupt status */ - } - if (served == 0) { - printf("How strange... mss_intr with no reason!\n"); - /* - * this should not happen... I have no idea what to do now. - * maybe should do a sanity check and restart dmas ? - */ - outb(io_Status(d), 0); /* Clear interrupt status */ - } -} - -/* - * the opti931 seems to miss interrupts when working in full - * duplex, so we try some heuristics to catch them. - */ -static void -opti931_intr(int unit) -{ - snddev_info *d = &pcm_info[unit]; - u_char masked=0, i11, mc11, c=0; - u_char reason; /* b0 = playback, b1 = capture, b2 = timer */ - int loops = 10; - -#if 0 - reason = inb(io_Status(d)); - if ( ! (reason & 1) ) {/* no int, maybe a shared line ? */ - printf("opti931_intr: flag 0, mcir11 0x%02x\n", ad_read(d,11)); - return; - } -#endif - i11 = ad_read(d, 11); /* XXX what's for ? */ -again: - - c=mc11 = FULL_DUPLEX(d) ? opti_read(d->conf_base, 11) : 0xc ; - mc11 &= 0x0c ; - if (c & 0x10) { - DEB(printf("Warning: CD interrupt\n");) - mc11 |= 0x10 ; - } - if (c & 0x20) { - DEB(printf("Warning: MPU interrupt\n");) - mc11 |= 0x20 ; - } - if (mc11 & masked) - printf("irq reset failed, mc11 0x%02x, masked 0x%02x\n", mc11, masked); - masked |= mc11 ; - /* - * the nice OPTi931 sets the IRQ line before setting the bits in - * mc11. So, on some occasions I have to retry (max 10 times). - */ - if ( mc11 == 0 ) { /* perhaps can return ... */ - reason = inb(io_Status(d)); - if (reason & 1) { - DEB(printf("one more try...\n");) - if (--loops) - goto again; - else - DDB(printf("opti_intr: irq but mc11 not set!...\n");) - } - if (loops==10) - printf("ouch, intr but nothing in mcir11 0x%02x\n", mc11); - return; - } - - if ( d->dbuf_in.dl && (mc11 & 8) ) { - dsp_rdintr(d); - } - if ( d->dbuf_out.dl && (mc11 & 4) ) { - dsp_wrintr(d); - } - opti_write(d->conf_base, 11, ~mc11); /* ack */ - if (--loops) - goto again; - DEB(printf("xxx too many loops\n");) -} - -/* - * Second part of the file: functions local to this module. - * in this section a few routines to access MSS registers - * - */ - -static void -opti_write(int io_base, u_char reg, u_char value) -{ - outb(io_base, reg); - outb(io_base+1, value); -} - -static u_char -opti_read(int io_base, u_char reg) -{ - outb(io_base, reg); - return inb(io_base+1); -} - -static void -gus_write(int io_base, u_char reg, u_char value) +mss_intr(void *arg) { - outb(io_base + 3, reg); - outb(io_base + 5, value); -} - -#if 0 -static void -gus_writew(int io_base, u_char reg, u_short value) -{ - outb(io_base + 3, reg); - outb(io_base + 4, value); -} -#endif - -static u_char -gus_read(int io_base, u_char reg) -{ - outb(io_base+3, reg); - return inb(io_base+5); -} - -#if 0 -static u_short -gus_readw(int io_base, u_char reg) -{ - outb(io_base+3, reg); - return inw(io_base+4); + struct mss_info *mss = arg; + u_char c = 0, served = 0; + int i; + + DEB(printf("mss_intr\n")); + ad_read(mss, 11); /* fake read of status bits */ + + /* loop until there are interrupts, but no more than 10 times. */ + for (i = 10; i > 0 && io_rd(mss, MSS_STATUS) & 1; i--) { + /* get exact reason for full-duplex boards */ + c = FULL_DUPLEX(mss)? ad_read(mss, 24) : 0x30; + c &= ~served; + if (mss->pch.buffer->dl && (c & 0x10)) { + served |= 0x10; + chn_intr(mss->pch.channel); + } + if (mss->rch.buffer->dl && (c & 0x20)) { + served |= 0x20; + chn_intr(mss->rch.channel); + } + /* now ack the interrupt */ + if (FULL_DUPLEX(mss)) ad_write(mss, 24, ~c); /* ack selectively */ + else io_wr(mss, MSS_STATUS, 0); /* Clear interrupt status */ + } + if (i == 10) printf("mss_intr: irq, but not from mss\n"); + else if (served == 0) { + printf("mss_intr: unexpected irq with reason %x\n", c); + /* + * this should not happen... I have no idea what to do now. + * maybe should do a sanity check and restart dmas ? + */ + io_wr(mss, MSS_STATUS, 0); /* Clear interrupt status */ + } } -#endif /* * AD_WAIT_INIT waits if we are initializing the board and * we cannot modify its settings */ static int -AD_WAIT_INIT(snddev_info *d, int x) +ad_wait_init(struct mss_info *mss, int x) { - int arg=x, n = 0; /* to shut up the compiler... */ - for (; x-- ; ) - if ( (n=inb(io_Index_Addr(d))) & IA_BUSY) - DELAY(10); - else - return n ; - printf("AD_WAIT_INIT FAILED %d 0x%02x\n", arg, n); - return n ; + int arg = x, n = 0; /* to shut up the compiler... */ + for (; x > 0; x--) + if ((n = io_rd(mss, MSS_INDEX)) & MSS_IDXBUSY) DELAY(10); + else return n; + printf("AD_WAIT_INIT FAILED %d 0x%02x\n", arg, n); + return n; } static int -ad_read(snddev_info *d, int reg) +ad_read(struct mss_info *mss, int reg) { - u_long flags; - int x; - - flags = spltty(); - AD_WAIT_INIT(d, 201); - x = inb(io_Index_Addr(d)) & ~IA_AMASK ; - outb(io_Index_Addr(d), (u_char) (reg & IA_AMASK) | x ) ; - x = inb(io_Indexed_Data(d)); - splx(flags); - return x; + u_long flags; + int x; + + flags = spltty(); + ad_wait_init(mss, 201); + x = io_rd(mss, MSS_INDEX) & ~MSS_IDXMASK; + io_wr(mss, MSS_INDEX, (u_char)(reg & MSS_IDXMASK) | x); + x = io_rd(mss, MSS_IDATA); + splx(flags); + return x; } static void -ad_write(snddev_info *d, int reg, u_char data) +ad_write(struct mss_info *mss, int reg, u_char data) { - u_long flags; - - int x ; - flags = spltty(); - AD_WAIT_INIT(d, 1002); - x = inb(io_Index_Addr(d)) & ~IA_AMASK ; - outb(io_Index_Addr(d), (u_char) (reg & IA_AMASK) | x ) ; - outb(io_Indexed_Data(d), data); - splx(flags); + u_long flags; + + int x; + flags = spltty(); + ad_wait_init(mss, 1002); + x = io_rd(mss, MSS_INDEX) & ~MSS_IDXMASK; + io_wr(mss, MSS_INDEX, (u_char)(reg & MSS_IDXMASK) | x); + io_wr(mss, MSS_IDATA, data); + splx(flags); } static void -ad_write_cnt(snddev_info *d, int reg, u_short cnt) +ad_write_cnt(struct mss_info *mss, int reg, u_short cnt) { - ad_write(d, reg+1, cnt & 0xff ); - ad_write(d, reg, cnt >> 8 ); /* upper base must be last */ + ad_write(mss, reg+1, cnt & 0xff); + ad_write(mss, reg, cnt >> 8); /* upper base must be last */ } static void -wait_for_calibration(snddev_info *d) +wait_for_calibration(struct mss_info *mss) { - int n, t; - - /* - * Wait until the auto calibration process has finished. - * - * 1) Wait until the chip becomes ready (reads don't return 0x80). - * 2) Wait until the ACI bit of I11 gets on - * 3) Wait until the ACI bit of I11 gets off - */ - - n = AD_WAIT_INIT(d, 1000); - if (n & IA_BUSY) - printf("mss: Auto calibration timed out(1).\n"); - - for (t = 100 ; t>0 && (ad_read(d, 11) & 0x20) == 0 ; t--) - DELAY(100); - for (t = 100 ; t>0 && ad_read(d, 11) & 0x20 ; t--) - DELAY(100); -} + int n, t; -#if 0 /* unused right now... */ -static void -ad_mute(snddev_info *d) -{ - ad_write(d, 6, ad_read(d,6) | I6_MUTE); - ad_write(d, 7, ad_read(d,7) | I6_MUTE); + /* + * Wait until the auto calibration process has finished. + * + * 1) Wait until the chip becomes ready (reads don't return 0x80). + * 2) Wait until the ACI bit of I11 gets on + * 3) Wait until the ACI bit of I11 gets off + */ + + 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); + for (t = 100; t > 0 && ad_read(mss, 11) & 0x20; t--) DELAY(100); } static void -ad_unmute(snddev_info *d) +ad_unmute(struct mss_info *mss) { - ad_write(d, 6, ad_read(d,6) & ~I6_MUTE); - ad_write(d, 7, ad_read(d,7) & ~I6_MUTE); + ad_write(mss, 6, ad_read(mss, 6) & ~I6_MUTE); + ad_write(mss, 7, ad_read(mss, 7) & ~I6_MUTE); } -#endif static void -ad_enter_MCE(snddev_info *d) +ad_enter_MCE(struct mss_info *mss) { - int prev; + int prev; - d->bd_flags |= BD_F_MCE_BIT; - AD_WAIT_INIT(d, 203); - prev = inb(io_Index_Addr(d)); - prev &= ~IA_TRD ; - outb(io_Index_Addr(d), prev | IA_MCE ) ; + mss->bd_flags |= BD_F_MCE_BIT; + ad_wait_init(mss, 203); + prev = io_rd(mss, MSS_INDEX); + prev &= ~MSS_TRD; + io_wr(mss, MSS_INDEX, prev | MSS_MCE); } static void -ad_leave_MCE(snddev_info *d) +ad_leave_MCE(struct mss_info *mss) { - u_long flags; - u_char prev; + u_long flags; + u_char prev; - if ( (d->bd_flags & BD_F_MCE_BIT) == 0 ) { - printf("--- hey, leave_MCE: MCE bit was not set!\n"); - return; - } + if ((mss->bd_flags & BD_F_MCE_BIT) == 0) { + printf("--- hey, leave_MCE: MCE bit was not set!\n"); + return; + } - AD_WAIT_INIT(d, 1000); + ad_wait_init(mss, 1000); - flags = spltty(); - d->bd_flags &= ~BD_F_MCE_BIT; + flags = spltty(); + mss->bd_flags &= ~BD_F_MCE_BIT; - prev = inb(io_Index_Addr(d)); - prev &= ~IA_TRD ; - outb(io_Index_Addr(d), prev & ~IA_MCE ); /* Clear the MCE bit */ - wait_for_calibration(d); - splx(flags); + prev = io_rd(mss, MSS_INDEX); + prev &= ~MSS_TRD; + io_wr(mss, MSS_INDEX, prev & ~MSS_MCE); /* Clear the MCE bit */ + wait_for_calibration(mss); + splx(flags); } /* * only one source can be set... */ static int -mss_set_recsrc(snddev_info *d, int mask) +mss_set_recsrc(struct mss_info *mss, int mask) { - u_char recdev; - - mask &= d->mix_rec_devs; - switch (mask) { - case SOUND_MASK_LINE: - case SOUND_MASK_LINE3: - recdev = 0; - break; - - case SOUND_MASK_CD: - case SOUND_MASK_LINE1: - recdev = 0x40; - break; - - case SOUND_MASK_IMIX: - recdev = 0xc0; - break; - - case SOUND_MASK_MIC: - default: - mask = SOUND_MASK_MIC; - recdev = 0x80; - } - - ad_write(d, 0, (ad_read(d, 0) & 0x3f) | recdev); - ad_write(d, 1, (ad_read(d, 1) & 0x3f) | recdev); - - d->mix_recsrc = mask; - return 0; + u_char recdev; + + switch (mask) { + case SOUND_MASK_LINE: + case SOUND_MASK_LINE3: + recdev = 0; + break; + + case SOUND_MASK_CD: + case SOUND_MASK_LINE1: + recdev = 0x40; + break; + + case SOUND_MASK_IMIX: + recdev = 0xc0; + break; + + case SOUND_MASK_MIC: + default: + mask = SOUND_MASK_MIC; + recdev = 0x80; + } + ad_write(mss, 0, (ad_read(mss, 0) & 0x3f) | recdev); + ad_write(mss, 1, (ad_read(mss, 1) & 0x3f) | recdev); + return mask; } -/* - * there are differences in the mixer depending on the actual sound - * card. - */ +/* there are differences in the mixer depending on the actual sound card. */ static int -mss_mixer_set(snddev_info *d, int dev, int value) -{ - int left = value & 0x000000ff; - int right = (value & 0x0000ff00) >> 8; - - int regoffs; - mixer_tab *mix_d = &mix_devices; - - u_char old, val; - - if (dev > 31) - return EINVAL; - - if (!(d->mix_devs & (1 << dev))) - return EINVAL; - - if (d->bd_id == MD_OPTI931) - mix_d = &(opti931_devices); - - if ((*mix_d)[dev][LEFT_CHN].nbits == 0) { - DEB(printf("nbits = 0 for dev %d\n", dev) ); - return EINVAL; - } - - if (left > 100) - left = 100; - if (right > 100) - right = 100; - - - if ( (*mix_d)[dev][RIGHT_CHN].nbits == 0) /* Mono control */ - right = left; - - d->mix_levels[dev] = left | (right << 8); - -#if 0 - /* Scale volumes */ - left = mix_cvt[left]; - right = mix_cvt[right]; -#endif - /* - * Set the left channel - */ - - regoffs = (*mix_d)[dev][LEFT_CHN].regno; - old = val = ad_read(d, regoffs); - /* - * if volume is 0, mute chan. Otherwise, unmute. - */ - if (regoffs != 0) /* main input is different */ - val = (left == 0 ) ? old | 0x80 : old & 0x7f ; - - change_bits(mix_d, &val, dev, LEFT_CHN, left); - ad_write(d, regoffs, val); - DEB(printf("LEFT: dev %d reg %d old 0x%02x new 0x%02x\n", - dev, regoffs, old, val)); - - if ((*mix_d)[dev][RIGHT_CHN].nbits != 0) { /* have stereo */ - /* - * Set the right channel - */ - regoffs = (*mix_d)[dev][RIGHT_CHN].regno; - old = val = ad_read(d, regoffs); - if (regoffs != 1) - val = (right == 0 ) ? old | 0x80 : old & 0x7f ; - change_bits(mix_d, &val, dev, RIGHT_CHN, right); - ad_write(d, regoffs, val); - DEB(printf("RIGHT: dev %d reg %d old 0x%02x new 0x%02x\n", - dev, regoffs, old, val)); - } - return 0; /* success */ -} - -static void -ad1848_mixer_reset(snddev_info *d) +mss_mixer_set(struct mss_info *mss, int dev, int left, int right) { - int i; - - if (d->bd_id == MD_OPTI931) - d->mix_devs = OPTI931_MIXER_DEVICES; - else if (d->bd_id != MD_AD1848) - d->mix_devs = MODE2_MIXER_DEVICES; - else - d->mix_devs = MODE1_MIXER_DEVICES; - - d->mix_rec_devs = MSS_REC_DEVICES; - - for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) - if (d->mix_devs & (1 << i)) - mss_mixer_set(d, i, default_mixer_levels[i]); - mss_set_recsrc(d, SOUND_MASK_MIC); - /* - * some device-specific things, mostly mute the mic to - * the output mixer so as to avoid hisses. In many cases this - * is the default after reset, this code is here mostly as a - * reminder that this might be necessary on other boards. - */ - switch(d->bd_id) { - case MD_OPTI931: - ad_write(d, 20, 0x88); - ad_write(d, 21, 0x88); - break; - - case MD_YM0020: - /* set master volume to max */ - DDB(printf("set yamaha master volume to max\n"); ) - outb(0x370, 7) ; - outb(0x371, 0) ; - outb(0x370, 8) ; - outb(0x371, 0) ; - break; - - case MD_GUSPNP: - /* this is only necessary in mode 3 ... */ - ad_write(d, 22, 0x88); - ad_write(d, 23, 0x88); - } + int regoffs; + mixer_tab *mix_d = (mss->bd_id == MD_OPTI931)? &opti931_devices : &mix_devices; + u_char old, val; + + if ((*mix_d)[dev][LEFT_CHN].nbits == 0) { + DEB(printf("nbits = 0 for dev %d\n", dev)); + return -1; + } + + if ((*mix_d)[dev][RIGHT_CHN].nbits == 0) right = left; /* mono */ + + /* Set the left channel */ + + regoffs = (*mix_d)[dev][LEFT_CHN].regno; + old = val = ad_read(mss, regoffs); + /* if volume is 0, mute chan. Otherwise, unmute. */ + if (regoffs != 0) val = (left == 0)? old | 0x80 : old & 0x7f; + change_bits(mix_d, &val, dev, LEFT_CHN, left); + ad_write(mss, regoffs, val); + + DEB(printf("LEFT: dev %d reg %d old 0x%02x new 0x%02x\n", + dev, regoffs, old, val)); + + if ((*mix_d)[dev][RIGHT_CHN].nbits != 0) { /* have stereo */ + /* Set the right channel */ + regoffs = (*mix_d)[dev][RIGHT_CHN].regno; + old = val = ad_read(mss, regoffs); + if (regoffs != 1) val = (right == 0)? old | 0x80 : old & 0x7f; + change_bits(mix_d, &val, dev, RIGHT_CHN, right); + ad_write(mss, regoffs, val); + + DEB(printf("RIGHT: dev %d reg %d old 0x%02x new 0x%02x\n", + dev, regoffs, old, val)); + } + return 0; /* success */ } -/* - * mss_speed processes the value in play_speed finding the +/* mss_speed processes the value in play_speed finding the * matching one. As a side effect, it returns the value to * be written in the speed bits of the codec. It does _NOT_ * set the speed of the device (but it should!) */ static int -mss_speed(snddev_info *d) +mss_speed(struct mss_chinfo *ch, int speed) { - /* - * In the CS4231, the low 4 bits of I8 are used to hold the - * sample rate. Only a fixed number of values is allowed. This - * table lists them. The speed-setting routines scans the table - * looking for the closest match. This is the only supported method. - * - * In the CS4236, there is an alternate metod (which we do not - * support yet) which provides almost arbitrary frequency setting. - * In the AD1845, it looks like the sample rate can be - * almost arbitrary, and written directly to a register. - * In the OPTi931, there is a SB command which provides for - * almost arbitrary frequency setting. - * - */ - static int speeds[] = { - 8000, 5512, 16000, 11025, 27429, 18900, 32000, 22050, - -1, 37800, -1, 44100, 48000, 33075, 9600, 6615 - }; - - int arg, i, sel = 0; /* assume entry 0 does not contain -1 */ - - arg = d->play_speed ; - - for (i=1; i < 16 ; i++) - if (speeds[i] >0 && abs(arg-speeds[i]) < abs(arg-speeds[sel]) ) - sel = i ; - - d->play_speed = d->rec_speed = speeds[sel] ; - return sel ; + struct mss_info *mss = ch->parent; + /* + * In the CS4231, the low 4 bits of I8 are used to hold the + * sample rate. Only a fixed number of values is allowed. This + * table lists them. The speed-setting routines scans the table + * looking for the closest match. This is the only supported method. + * + * In the CS4236, there is an alternate metod (which we do not + * support yet) which provides almost arbitrary frequency setting. + * In the AD1845, it looks like the sample rate can be + * almost arbitrary, and written directly to a register. + * In the OPTi931, there is a SB command which provides for + * almost arbitrary frequency setting. + * + */ + ad_enter_MCE(mss); + if (mss->bd_id == MD_AD1845) { /* Use alternate speed select regs */ + ad_write(mss, 22, (speed >> 8) & 0xff); /* Speed MSB */ + ad_write(mss, 23, speed & 0xff); /* Speed LSB */ + /* XXX must also do something in I27 for the ad1845 */ + } else { + int i, sel = 0; /* assume entry 0 does not contain -1 */ + static int speeds[] = + {8000, 5512, 16000, 11025, 27429, 18900, 32000, 22050, + -1, 37800, -1, 44100, 48000, 33075, 9600, 6615}; + + for (i = 1; i < 16; i++) + if (speeds[i] > 0 && + abs(speed-speeds[i]) < abs(speed-speeds[sel])) sel = i; + speed = speeds[sel]; + ad_write(mss, 8, (ad_read(mss, 8) & 0xf0) | sel); + } + ad_leave_MCE(mss); + + return speed; } /* @@ -1064,1276 +1193,753 @@ mss_speed(snddev_info *d) */ static int -mss_format(snddev_info *d) -{ - int i, arg = d->play_fmt ; - - /* - * The data format uses 3 bits (just 2 on the 1848). For each - * bit setting, the following array returns the corresponding format. - * The code scans the array looking for a suitable format. In - * case it is not found, default to AFMT_U8 (not such a good - * choice, but let's do it for compatibility...). - */ - - static int fmts[] = { - AFMT_U8, AFMT_MU_LAW, AFMT_S16_LE, AFMT_A_LAW, - -1, AFMT_IMA_ADPCM, AFMT_U16_BE, -1 - }; - - if ( (arg & d->audio_fmt) == 0 ) /* unsupported fmt, default to AFMT_U8 */ - arg = AFMT_U8 ; - - /* ulaw/alaw seems broken on the opti931... */ - if (d->bd_id == MD_OPTI931 || d->bd_id == MD_GUSPNP) { - if (arg == AFMT_MU_LAW) { - arg = AFMT_U8 ; - d->flags |= SND_F_XLAT8 ; - } else - d->flags &= ~SND_F_XLAT8 ; - } - /* - * check that arg is one of the supported formats in d->format; - * otherwise fallback to AFMT_U8 - */ - - for (i=0 ; i<8 ; i++) - if (arg == fmts[i]) break; - if (i==8) { /* not found, default to AFMT_U8 */ - arg = AFMT_U8 ; - i = 0 ; - } - d->play_fmt = d->rec_fmt = arg; - - return i ; -} - -/* - * mss_detect can be used in the probe and the attach routine. - * - * We store probe information in pcm_info[unit]. This descriptor - * is reinitialized just before the attach, so all relevant - * information is lost, and mss_detect must be run again in - * the attach routine if necessary. - */ - -int -mss_detect(struct isa_device *dev) +mss_format(struct mss_chinfo *ch, u_int32_t format) { - int i; - u_char tmp, tmp1, tmp2 ; - snddev_info *d = &(pcm_info[dev->id_unit]); - char *name; - - d->io_base = dev->id_iobase; - d->bd_flags |= BD_F_MCE_BIT ; - if (d->bd_id != 0) { - printf("preselected bd_id 0x%04x -- %s\n", - d->bd_id, d->name ? d->name : "???"); - return 1; - } - - name = "AD1848" ; - d->bd_id = MD_AD1848; /* AD1848 or CS4248 */ - - /* - * Check that the I/O address is in use. - * - * bit 7 of the base I/O port is known to be 0 after the chip has - * performed its power on initialization. Just assume this has - * happened before the OS is starting. - * - * If the I/O address is unused, it typically returns 0xff. - */ - - for (i=0; i<10; i++) - if (inb(io_Index_Addr(d)) & IA_BUSY) - DELAY(10000); /* maybe busy, wait & retry later */ - else - break ; - if ((inb(io_Index_Addr(d)) & IA_BUSY) != 0x00) { /* Not a AD1848 */ - BVDDB(printf("mss_detect error, busy still set (0x%02x)\n", - inb(io_Index_Addr(d)))); - return 0; - } - /* - * Test if it's possible to change contents of the indirect - * registers. Registers 0 and 1 are ADC volume registers. The bit - * 0x10 is read only so try to avoid using it. - */ - - ad_write(d, 0, 0xaa); - ad_write(d, 1, 0x45);/* 0x55 with bit 0x10 clear */ - tmp1 = ad_read(d, 0) ; - tmp2 = ad_read(d, 1) ; - if ( tmp1 != 0xaa || tmp2 != 0x45) { - BVDDB(printf("mss_detect error - IREG (0x%02x/0x%02x) want 0xaa/0x45\n", - tmp1, tmp2)); - return 0; - } - - ad_write(d, 0, 0x45); - ad_write(d, 1, 0xaa); - tmp1 = ad_read(d, 0) ; - tmp2 = ad_read(d, 1) ; - - if (tmp1 != 0x45 || tmp2 != 0xaa) { - BVDDB(printf("mss_detect error - IREG2 (%x/%x)\n", tmp1, tmp2)); - return 0; - } - - /* - * The indirect register I12 has some read only bits. Lets try to - * change them. - */ - - tmp = ad_read(d, 12); - ad_write(d, 12, (~tmp) & 0x0f); - tmp1 = ad_read(d, 12); - - if ((tmp & 0x0f) != (tmp1 & 0x0f)) { - BVDDB(printf("mss_detect error - I12 (0x%02x was 0x%02x)\n", - tmp1, tmp)); - return 0; - } - - /* - * NOTE! Last 4 bits of the reg I12 tell the chip revision. - * 0x01=RevB - * 0x0A=RevC. also CS4231/CS4231A and OPTi931 - */ - - BVDDB(printf("mss_detect - chip revision 0x%02x\n", tmp & 0x0f);) - - /* - * The original AD1848/CS4248 has just 16 indirect registers. This - * means that I0 and I16 should return the same value (etc.). Ensure - * that the Mode2 enable bit of I12 is 0. Otherwise this test fails - * with new parts. - */ - - ad_write(d, 12, 0); /* Mode2=disabled */ - - for (i = 0; i < 16; i++) - if ((tmp1 = ad_read(d, i)) != (tmp2 = ad_read(d, i + 16))) { - BVDDB(printf("mss_detect warning - I%d: 0x%02x/0x%02x\n", - i, tmp1, tmp2)); - /* - * note - this seems to fail on the 4232 on I11. So we just break - * rather than fail. - */ - break ; /* return 0; */ - } - /* - * Try to switch the chip to mode2 (CS4231) by setting the MODE2 bit - * (0x40). The bit 0x80 is always 1 in CS4248 and CS4231. - * - * On the OPTi931, however, I12 is readonly and only contains the - * chip revision ID (as in the CS4231A). The upper bits return 0. - */ - - ad_write(d, 12, 0x40); /* Set mode2, clear 0x80 */ - - tmp1 = ad_read(d, 12); - if (tmp1 & 0x80) { - name = "CS4248" ; /* Our best knowledge just now */ - } - if ((tmp1 & 0xf0) == 0x00) { - BVDDB(printf("this should be an OPTi931\n");) - } else if ((tmp1 & 0xc0) == 0xC0) { - /* - * The 4231 has bit7=1 always, and bit6 we just set to 1. - * We want to check that this is really a CS4231 - * Verify that setting I0 doesn't change I16. - */ - ad_write(d, 16, 0); /* Set I16 to known value */ - - ad_write(d, 0, 0x45); - if ((tmp1 = ad_read(d, 16)) != 0x45) { /* No change -> CS4231? */ - - ad_write(d, 0, 0xaa); - if ((tmp1 = ad_read(d, 16)) == 0xaa) { /* Rotten bits? */ - BVDDB(printf("mss_detect error - step H(%x)\n", tmp1)); - return 0; - } - /* - * Verify that some bits of I25 are read only. - */ - - tmp1 = ad_read(d, 25); /* Original bits */ - ad_write(d, 25, ~tmp1); /* Invert all bits */ - if ((ad_read(d, 25) & 0xe7) == (tmp1 & 0xe7)) { - int id; - - /* - * It's at least CS4231 - */ - name = "CS4231" ; - d->bd_id = MD_CS4231; - - /* - * It could be an AD1845 or CS4231A as well. - * CS4231 and AD1845 report the same revision info in I25 - * while the CS4231A reports different. - */ - - id = ad_read(d, 25) & 0xe7; - /* - * b7-b5 = version number; - * 100 : all CS4231 - * 101 : CS4231A - * - * b2-b0 = chip id; - */ - switch (id) { - - case 0xa0: - name = "CS4231A" ; - d->bd_id = MD_CS4231A; - break; - - case 0xa2: - name = "CS4232" ; - d->bd_id = MD_CS4232; - break; - - case 0xb2: - /* strange: the 4231 data sheet says b4-b3 are XX - * so this should be the same as 0xa2 - */ - name = "CS4232A" ; - d->bd_id = MD_CS4232A; - break; - - case 0x80: - /* - * It must be a CS4231 or AD1845. The register I23 - * of CS4231 is undefined and it appears to be read - * only. AD1845 uses I23 for setting sample rate. - * Assume the chip is AD1845 if I23 is changeable. - */ - - tmp = ad_read(d, 23); - - ad_write(d, 23, ~tmp); - if (ad_read(d, 23) != tmp) { /* AD1845 ? */ - name = "AD1845" ; - d->bd_id = MD_AD1845; - } - ad_write(d, 23, tmp); /* Restore */ - DDB(printf("... try to identify the yamaha\n") ;) - tmp = inb(0x370) ; - outb(0x370, 6 /* dma config */ ) ; - if (inb(0x370) != 6 ) /* not a yamaha... restore. */ - outb(0x370, tmp ) ; - else - d->bd_id = MD_YM0020 ; - break; - - case 0x83: /* CS4236 */ - case 0x03: /* CS4236 on Intel PR440FX motherboard XXX */ - name = "CS4236"; - d->bd_id = MD_CS4236; - break ; - - default: /* Assume CS4231 */ - BVDDB(printf("unknown id 0x%02x, assuming CS4231\n", id);) - d->bd_id = MD_CS4231; - } - } - ad_write(d, 25, tmp1); /* Restore bits */ - - } - } - BVDDB(printf("mss_detect() - Detected %s\n", name)); - snprintf(d->name, sizeof(d->name), "%s", name); - dev->id_flags &= ~DV_F_DEV_MASK ; - dev->id_flags |= (d->bd_id << DV_F_DEV_SHIFT) & DV_F_DEV_MASK ; - return 1; + struct mss_info *mss = ch->parent; + int i, arg = format & ~AFMT_STEREO; + + /* + * The data format uses 3 bits (just 2 on the 1848). For each + * bit setting, the following array returns the corresponding format. + * The code scans the array looking for a suitable format. In + * case it is not found, default to AFMT_U8 (not such a good + * choice, but let's do it for compatibility...). + */ + + static int fmts[] = + {AFMT_U8, AFMT_MU_LAW, AFMT_S16_LE, AFMT_A_LAW, + -1, AFMT_IMA_ADPCM, AFMT_U16_BE, -1}; + + for (i = 0; i < 8; i++) if (arg == fmts[i]) break; + arg = i << 1; + if (format & AFMT_STEREO) arg |= 1; + arg <<= 4; + ad_enter_MCE(mss); + ad_write(mss, 8, (ad_read(mss, 8) & 0x0f) | arg); + if (FULL_DUPLEX(mss)) ad_write(mss, 28, arg); /* capture mode */ + ad_leave_MCE(mss); + return format; } - -/* - * mss_reinit resets registers of the codec - */ -static void -mss_reinit(snddev_info *d) +static int +mss_trigger(struct mss_chinfo *ch, int go) { - u_char r; - - r = mss_speed(d) ; - r |= (mss_format(d) << 5) ; - if (d->flags & SND_F_STEREO) - r |= 0x10 ; - /* XXX check if MCE is necessary... */ - ad_enter_MCE(d); - - /* - * perhaps this is not the place to set mode2, should be done - * only once at attach time... - */ - if ( FULL_DUPLEX(d) && d->bd_id != MD_OPTI931) - /* - * set mode2 bit for dual dma op. This bit is not implemented - * on the OPTi931 - */ - ad_write(d, 12, ad_read(d, 12) | 0x40 /* mode 2 on the CS42xx */ ); - - /* - * XXX this should really go into mss-speed... - */ - if (d->bd_id == MD_AD1845) { /* Use alternate speed select regs */ - r &= 0xf0; /* Mask off the rate select bits */ - - ad_write(d, 22, (d->play_speed >> 8) & 0xff); /* Speed MSB */ - ad_write(d, 23, d->play_speed & 0xff); /* Speed LSB */ - /* - * XXX must also do something in I27 for the ad1845 - */ - } - - ad_write(d, 8, r) ; - if ( FULL_DUPLEX(d) ) { + struct mss_info *mss = ch->parent; + u_char m; + int retry, wr, cnt; + + wr = (ch->dir == PCMDIR_PLAY)? 1 : 0; + m = ad_read(mss, 9); + switch (go) { + case PCMTRIG_START: + cnt = (ch->buffer->dl / ch->buffer->sample_size) - 1; + + DEB(if (m & 4) printf("OUCH! reg 9 0x%02x\n", m);); + m |= wr? I9_PEN : I9_CEN; /* enable DMA */ + ad_write_cnt(mss, (wr || !FULL_DUPLEX(mss))? 14 : 30, cnt); + break; + + case PCMTRIG_STOP: + case PCMTRIG_ABORT: /* XXX check this... */ + m &= ~(wr? I9_PEN : I9_CEN); /* Stop DMA */ #if 0 - if (d->bd_id == MD_GUSPNP && d->play_fmt == AFMT_MU_LAW) { - printf("warning, cannot do ulaw rec + play on the GUS\n"); - r = 0 ; /* move to U8 */ - } + /* + * try to disable DMA by clearing count registers. Not sure it + * is needed, and it might cause false interrupts when the + * DMA is re-enabled later. + */ + ad_write_cnt(mss, (wr || !FULL_DUPLEX(mss))? 14 : 30, 0); #endif - ad_write(d, 28, r & 0xf0 ) ; /* capture mode */ - ad_write(d, 9, 0 /* no capture, no playback, dual dma */) ; - } else - ad_write(d, 9, 4 /* no capture, no playback, single dma */) ; - ad_leave_MCE(d); - /* - * not sure if this is really needed... - */ - ad_write_cnt(d, 14, 0 ); /* playback count */ - if ( FULL_DUPLEX(d) ) - ad_write_cnt(d, 30, 0 ); /* rec. count on dual dma */ - - ad_write(d, 10, 2 /* int enable */) ; - outb(io_Status(d), 0); /* Clear interrupt status */ - /* the following seem required on the CS4232 */ - ad_write(d, 6, ad_read(d,6) & ~I6_MUTE); - ad_write(d, 7, ad_read(d,7) & ~I6_MUTE); - - snd_set_blocksize(d); /* update blocksize if user did not force it */ + } + /* on the OPTi931 the enable bit seems hard to set... */ + for (retry = 10; retry > 0; retry--) { + ad_write(mss, 9, m); + if (ad_read(mss, 9) == m) break; + } + if (retry == 0) printf("start dma, failed to set bit 0x%02x 0x%02x\n", + m, ad_read(mss, 9)); + return 0; } -/* - * here we have support for PnP cards - * - */ - #if NPNP > 0 +static int +pnpmss_probe(device_t dev) +{ + char *s = NULL; + u_int32_t logical_id = isa_get_logicalid(dev); + u_int32_t vend_id = isa_get_vendorid(dev); + u_int32_t id = vend_id & 0xff00ffff; + + switch (logical_id) { + case 0x0000630e: /* CSC0000 */ + if (id == 0x3700630e) s = "CS4237"; + else if (id == 0x2500630e) s = "CS4235"; + else if (id == 0x3600630e) s = "CS4236"; + else if (id == 0x3500630e) s = "CS4236B"; + else if (id == 0x3200630e) s = "CS4232"; + else s = "Unknown CS"; + break; + + case 0x2100a865: /* YMH0021 */ + if (id == 0x2000a865) s = "Yamaha SA2"; + else if (id == 0x3000a865) s = "Yamaha SA3"; + else if (id == 0x0000a865) s = "Yamaha YMF719 OPL-SA3"; + else s = "Yamaha OPL-SAx"; + break; + + case 0x1110d315: /* ENS1011 */ + s = "ENSONIQ SoundscapeVIVO"; + break; + + case 0x80719304: /* ADS7180 */ + s = "Terratec Soundsystem BASE 1"; + break; + + case 0x1093143e: /* OPT9310 */ + s = "OPTi931"; + break; + + case 0x5092143e: /* OPT9250 XXX guessing */ + s = "OPTi925"; + break; + + case 0x0000561e: + s = "GusPnP"; + break; + + case 0x01000000: + if (vend_id == 0x0100a90d) s = "CMI8330"; + break; + } + + if (s) { + device_set_desc(dev, s); + return 0; + } + return ENXIO; +} -static char * cs423x_probe(u_long csn, u_long vend_id); -static void -cs423x_attach(u_long csn, u_long vend_id, char *name, - struct isa_device *dev); - -static struct pnp_device cs423x = { - "CS423x/Yamaha/AD1816", - cs423x_probe, - cs423x_attach, - &nsnd, /* use this for all sound cards */ - &tty_imask /* imask */ -}; -DATA_SET (pnpdevice_set, cs423x); - -static char * -cs423x_probe(u_long csn, u_long vend_id) +static int +pnpmss_attach(device_t dev) { - char *s = NULL ; - u_long id = vend_id & 0xff00ffff; - if ( id == 0x3700630e ) - s = "CS4237" ; - else if ( id == 0x2500630e ) - s = "CS4235" ; - else if ( id == 0x3600630e ) - s = "CS4236" ; - else if ( id == 0x3500630e ) - s = "CS4236B" ; - else if ( id == 0x3200630e) - s = "CS4232" ; - else if ( id == 0x2000a865) - s = "Yamaha SA2"; - else if ( id == 0x3000a865) - s = "Yamaha SA3"; - else if ( id == 0x0000a865) - s = "Yamaha YMF719 OPL-SA3"; - else if (vend_id == 0x8140d315) - s = "SoundscapeVIVO"; - else if (vend_id == 0x1114b250) - s = "Terratec Soundsystem BASE 1"; - else if (vend_id == 0x50719304) - s = "Generic AD1815"; - if (s) { - struct pnp_cinfo d; - read_pnp_parms(&d, 0); - if (d.enable == 0) { - printf("This is a %s, but LDN 0 is disabled\n", s); - return NULL ; - } - return s; - } + struct mss_info *mss; + u_int32_t vend_id = isa_get_vendorid(dev); - return NULL ; -} + mss = (struct mss_info *)malloc(sizeof *mss, M_DEVBUF, M_NOWAIT); + if (!mss) return ENXIO; + bzero(mss, sizeof *mss); -extern snddev_info sb_op_desc; + mss->io_rid = 0; + mss->conf_rid = -1; + mss->irq_rid = 0; + mss->drq1_rid = 0; + mss->drq2_rid = 1; -static void -cs423x_attach(u_long csn, u_long vend_id, char *name, - struct isa_device *dev) -{ - struct pnp_cinfo d ; - snddev_info tmp_d ; /* patched copy of the basic snddev_info */ - int ldn = 0 ; - - if (read_pnp_parms ( &d , ldn ) == 0 ) { - printf("failed to read pnp parms\n"); - return ; - } - snddev_last_probed = &tmp_d; - - /* AD1816 */ - if (vend_id == 0x1114b250 || vend_id == 0x50719304) { - dev->id_alive = 16; /* number of io ports ? */ - - tmp_d = mss_op_desc; /* copy it */ - - tmp_d.ioctl = ad1816_ioctl; - tmp_d.isr = ad1816_intr; - tmp_d.callback = ad1816_callback; - tmp_d.audio_fmt = AFMT_STEREO | AFMT_U8 | - AFMT_A_LAW | AFMT_MU_LAW | - AFMT_S16_LE | AFMT_S16_BE; - - dev->id_iobase = d.port[2]; - tmp_d.alt_base = d.port[0]; /* soundblaster comp. but we don't - * use that */ - tmp_d.bd_id = MD_AD1816; - strcpy(tmp_d.name, name); - } else if (d.flags & DV_PNP_SBCODEC) { /* use sb-compatible codec */ - dev->id_alive = 16 ; /* number of io ports ? */ - tmp_d = sb_op_desc ; - if (vend_id==0x2000a865 || vend_id==0x3000a865 || - vend_id==0x0008a865 || vend_id==0x8140d315) { - /* Yamaha SA2/SA3 or ENSONIQ SoundscapeVIVO ENS4081 */ - dev->id_iobase = d.port[0] ; - tmp_d.alt_base = d.port[1] ; - d.irq[1] = 0 ; /* only needed for the VIVO */ - } else { - dev->id_iobase = d.port[2] ; - tmp_d.alt_base = d.port[0] - 4; - } - d.drq[1] = 4 ; /* disable, it is not used ... */ - } else { /* mss-compatible codec */ - dev->id_alive = 8 ; /* number of io ports ? */ - tmp_d = mss_op_desc ; - dev->id_iobase = d.port[0] -4 ; /* XXX old mss have 4 bytes before... */ - tmp_d.alt_base = d.port[2]; switch (vend_id & 0xff00ffff) { + case 0x1100b250: /* terratec */ + case 0x50009304: /* generic ad1815 */ + mss->io_rid = 2; + mss->bd_id = MD_AD1816; + break; case 0x2000a865: /* Yamaha SA2 */ case 0x3000a865: /* Yamaha SA3 */ - case 0x0000a865: /* Yamaha TMF719 SA3 */ - dev->id_iobase = d.port[1]; - tmp_d.alt_base = d.port[0]; - tmp_d.conf_base = d.port[4]; - tmp_d.bd_id = MD_YM0020 ; + case 0x0000a865: /* Yamaha YMF719 SA3 */ + mss->io_rid = 1; + mss->conf_rid = 4; + mss->bd_id = MD_YM0020; break; case 0x8100d315: /* ENSONIQ SoundscapeVIVO */ - dev->id_iobase = d.port[1]; - tmp_d.alt_base = d.port[0]; - tmp_d.bd_id = MD_VIVO ; - d.irq[1] = 0 ; + mss->io_rid = 1; + mss->bd_id = MD_VIVO; break; case 0x3700630e: /* CS4237 */ - tmp_d.bd_id = MD_CS4237 ; - break; - case 0x2500630e: /* AOpen AW37, CS4235 */ - tmp_d.bd_id = MD_CS4237 ; - break ; + mss->bd_flags |= BD_F_MSS_OFFSET; + mss->bd_id = MD_CS4237; + break; case 0x3500630e: /* CS4236B */ case 0x3600630e: /* CS4236 */ - tmp_d.bd_id = MD_CS4236 ; + mss->bd_flags |= BD_F_MSS_OFFSET; + mss->bd_id = MD_CS4236; + break; + + case 0x3100143e: /* opti931 */ + mss->bd_flags |= BD_F_MSS_OFFSET; + mss->conf_rid = 3; + mss->bd_id = MD_OPTI931; + break; + + case 0x2500143e: /* opti925 */ + mss->io_rid = 1; + mss->conf_rid = 3; + mss->bd_id = MD_OPTI925; + break; + + case 0x0100561e: /* guspnp */ + mss->io_rid = 2; + mss->conf_rid = 1; + mss->drq1_rid = 1; + mss->drq2_rid = 0; + mss->bd_id = MD_GUSPNP; break; default: - tmp_d.bd_id = MD_CS4232; /* to short-circuit the - * detect routine */ + mss->bd_flags |= BD_F_MSS_OFFSET; + mss->bd_id = MD_CS4232; break; } - snprintf(tmp_d.name, sizeof(tmp_d.name), "%s", name); - tmp_d.audio_fmt |= AFMT_FULLDUPLEX ; - } - - write_pnp_parms( &d, ldn ); - enable_pnp_card(); - - if ( (vend_id & 0x0000ffff) == 0x0000a865 ) { - /* special volume setting for the Yamaha... */ - outb(tmp_d.conf_base, 7 /* volume, left */); - outb(tmp_d.conf_base+1, 0 ); - outb(tmp_d.conf_base, 8 /* volume, right */); - outb(tmp_d.conf_base+1, 0 ); - } - dev->id_drq = d.drq[0] ; /* primary dma */ - dev->id_irq = (1 << d.irq[0] ) ; - dev->id_ointr = pcmintr ; - dev->id_flags = DV_F_DUAL_DMA | (d.drq[1] ) ; - - tmp_d.synth_base = d.port[1]; /* XXX check this for yamaha */ - pcmattach(dev); + return mss_doattach(dev, mss); } -static char *opti931_probe(u_long csn, u_long vend_id); -static void opti931_attach(u_long csn, u_long vend_id, char *name, - struct isa_device *dev); -static struct pnp_device opti931 = { - "OPTi931", - opti931_probe, - opti931_attach, - &nsnd, /* use this for all sound cards */ - &tty_imask /* imask */ +static device_method_t pnpmss_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, pnpmss_probe), + DEVMETHOD(device_attach, pnpmss_attach), + + { 0, 0 } }; -DATA_SET (pnpdevice_set, opti931); -static char * -opti931_probe(u_long csn, u_long vend_id) -{ - if (vend_id == 0x3109143e) { - struct pnp_cinfo d; - read_pnp_parms(&d, 1); - if (d.enable == 0) { - printf("This is an OPTi931, but LDN 1 is disabled\n"); - return NULL ; - } - return "OPTi931" ; - } - return NULL ; -} +static driver_t pnpmss_driver = { + "pcm", + pnpmss_methods, + sizeof(snddev_info), +}; + +DRIVER_MODULE(pnpmss, isa, pnpmss_driver, pcm_devclass, 0, 0); +/* + * the opti931 seems to miss interrupts when working in full + * duplex, so we try some heuristics to catch them. + */ static void -opti931_attach(u_long csn, u_long vend_id, char *name, - struct isa_device *dev) +opti931_intr(void *arg) { - struct pnp_cinfo d ; - snddev_info tmp_d ; /* patched copy of the basic snddev_info */ - int p; - - read_pnp_parms ( &d , 3 ); /* free resources taken by LDN 3 */ - d.irq[0]=0; /* free irq... */ - d.port[0]=0; /* free address... */ - d.enable = 0 ; - write_pnp_parms ( &d , 3 ); - - read_pnp_parms ( &d , 2 ); /* disable LDN 2 */ - d.enable = 0 ; - write_pnp_parms ( &d , 2 ); - - read_pnp_parms ( &d , 1 ) ; - write_pnp_parms( &d, 1 ); - enable_pnp_card(); - - snddev_last_probed = &tmp_d; - tmp_d = d.flags & DV_PNP_SBCODEC ? sb_op_desc : mss_op_desc ; - - snprintf(tmp_d.name, sizeof(tmp_d.name), "%s", name); - - /* - * My MED3931 v.1.0 allocates 3 bytes for the config space, - * whereas v.2.0 allocates 4 bytes. What I know for sure is that the - * upper two ports must be used, and they should end on a boundary - * of 4 bytes. So I need the following trick... - */ - p = tmp_d.conf_base = (d.port[3] & ~3) + 2; /* config port */ - - /* - * now set default values for both modes. - */ - dev->id_iobase = d.port[0] - 4 ; /* old mss have 4 bytes before... */ - tmp_d.io_base = dev->id_iobase; /* needed for ad_write to work... */ - tmp_d.alt_base = d.port[2]; - tmp_d.synth_base = d.port[1]; - opti_write(p, 4, 0xd6 /* fifo empty, OPL3, audio enable, SB3.2 */ ); - ad_write (&tmp_d, 10, 2); /* enable interrupts */ - - if (d.flags & DV_PNP_SBCODEC) { /* sb-compatible codec */ - /* - * the 931 is not a real SB, it has important pieces of - * hardware controlled by both the MSS and the SB port... - */ - printf("--- opti931 in sb mode ---\n"); - opti_write(p, 6, 1); /* MCIR6 mss disable, sb enable */ - /* - * swap the main and alternate iobase address since we want - * to work in sb mode. - */ - dev->id_iobase = d.port[2] ; - tmp_d.alt_base = d.port[0] - 4; - dev->id_flags = DV_F_DUAL_DMA | d.drq[1] ; - } else { /* mss-compatible codec */ - tmp_d.bd_id = MD_OPTI931 ; /* to short-circuit the detect routine */ - opti_write(p, 6 , 2); /* MCIR6: mss enable, sb disable */ - opti_write(p, 5, 0x28); /* MCIR5: codec in exp. mode,fifo */ - dev->id_flags = DV_F_DUAL_DMA | d.drq[1] ; - tmp_d.audio_fmt |= AFMT_FULLDUPLEX ; /* not really well... */ - tmp_d.isr = opti931_intr; - } - dev->id_drq = d.drq[0] ; /* primary dma */ - dev->id_irq = (1 << d.irq[0] ) ; - dev->id_ointr = pcmintr ; - pcmattach(dev); + struct mss_info *mss = (struct mss_info *)arg; + u_char masked = 0, i11, mc11, c = 0; + u_char reason; /* b0 = playback, b1 = capture, b2 = timer */ + int loops = 10; + +#if 0 + reason = io_rd(mss, MSS_STATUS); + if (!(reason & 1)) {/* no int, maybe a shared line ? */ + printf("intr: flag 0, mcir11 0x%02x\n", ad_read(mss, 11)); + return; + } +#endif + i11 = ad_read(mss, 11); /* XXX what's for ? */ + again: + + c = mc11 = FULL_DUPLEX(mss)? conf_rd(mss, 11) : 0xc; + mc11 &= 0x0c; + if (c & 0x10) { + DEB(printf("Warning: CD interrupt\n");) + mc11 |= 0x10; + } + if (c & 0x20) { + DEB(printf("Warning: MPU interrupt\n");) + mc11 |= 0x20; + } + if (mc11 & masked) printf("irq reset failed, mc11 0x%02x, 0x%02x\n", + mc11, masked); + masked |= mc11; + /* + * the nice OPTi931 sets the IRQ line before setting the bits in + * mc11. So, on some occasions I have to retry (max 10 times). + */ + if (mc11 == 0) { /* perhaps can return ... */ + reason = io_rd(mss, MSS_STATUS); + if (reason & 1) { + DEB(printf("one more try...\n");) + if (--loops) goto again; + else DDB(printf("intr, but mc11 not set\n");) + } + if (loops == 0) printf("intr, nothing in mcir11 0x%02x\n", mc11); + return; + } + + if (mss->rch.buffer->dl && (mc11 & 8)) chn_intr(mss->rch.channel); + if (mss->pch.buffer->dl && (mc11 & 4)) chn_intr(mss->pch.channel); + conf_wr(mss, 11, ~mc11); /* ack */ + if (--loops) goto again; + DEB(printf("xxx too many loops\n");) } -static char *opti925_probe(u_long csn, u_long vend_id); -static void opti925_attach(u_long csn, u_long vend_id, char *name, - struct isa_device *dev); - -static struct pnp_device opti925 = { - "opti925", - opti925_probe, - opti925_attach, - &nsnd, /* use this for all sound cards */ - &tty_imask /* imask */ -}; -DATA_SET (pnpdevice_set, opti925); - -static char * -opti925_probe(u_long csn, u_long vend_id) -{ - if (vend_id == 0x2509143e) { - struct pnp_cinfo d ; - read_pnp_parms ( &d , 1 ) ; - if (d.enable == 0) { - printf("This is an OPTi925, but LDN 1 is disabled\n"); - return NULL; - } - return "OPTi925" ; - } - return NULL ; -} - -static void -opti925_attach(u_long csn, u_long vend_id, char *name, - struct isa_device *dev) +static void +ad1816_intr(void *arg) { - struct pnp_cinfo d ; - snddev_info tmp_d ; /* patched copy of the basic snddev_info */ - int the_irq = 0 ; - - tmp_d = mss_op_desc; - snddev_last_probed = &tmp_d; - - read_pnp_parms ( &d , 3 ); /* disable LDN 3 */ - the_irq = d.irq[0]; - d.port[0] = 0 ; - d.enable = 0 ; - write_pnp_parms ( &d , 3 ); - - read_pnp_parms ( &d , 2 ); /* disable LDN 2 */ - d.port[0] = 0 ; - d.enable = 0 ; - write_pnp_parms ( &d , 2 ); - - read_pnp_parms ( &d , 1 ) ; - d.irq[0] = the_irq ; - dev->id_iobase = d.port[1]; - tmp_d.alt_base = d.port[0]; - write_pnp_parms ( &d , 1 ); - enable_pnp_card(); - - tmp_d.conf_base = d.port[3]; - - dev->id_drq = d.drq[0] ; /* primary dma */ - dev->id_irq = (1 << d.irq[0] ) ; - dev->id_ointr = pcmintr ; - dev->id_flags = DV_F_DUAL_DMA | d.drq[1] ; - tmp_d.audio_fmt |= AFMT_FULLDUPLEX ; - - snddev_last_probed->probe(dev); /* not really necessary but doesn't harm */ - - pcmattach(dev); + struct mss_info *mss = (struct mss_info *)arg; + unsigned char c, served = 0; + + /* get interupt status */ + c = io_rd(mss, AD1816_INT); + + /* check for stray interupts */ + if (c & ~(AD1816_INTRCI | AD1816_INTRPI)) { + printf("pcm: stray int (%x)\n", c); + c &= AD1816_INTRCI | AD1816_INTRPI; + } + /* check for capture interupt */ + if (mss->rch.buffer->dl && (c & AD1816_INTRCI)) { + chn_intr(mss->rch.channel); + served |= AD1816_INTRCI; /* cp served */ + } + /* check for playback interupt */ + if (mss->pch.buffer->dl && (c & AD1816_INTRPI)) { + chn_intr(mss->pch.channel); + served |= AD1816_INTRPI; /* pb served */ + } + if (served == 0) { + /* this probably means this is not a (working) ad1816 chip, */ + /* or an error in dma handling */ + printf("pcm: int without reason (%x)\n", c); + c = 0; + } else c &= ~served; + io_wr(mss, AD1816_INT, c); + c = io_rd(mss, AD1816_INT); + if (c != 0) printf("pcm: int clear failed (%x)\n", c); } -#if 0 -static void gus_mem_cfg(snddev_info *tmp); -#endif +static int +ad1816_wait_init(struct mss_info *mss, int x) +{ + int n = 0; /* to shut up the compiler... */ -static char *guspnp_probe(u_long csn, u_long vend_id); -static void guspnp_attach(u_long csn, u_long vend_id, char *name, - struct isa_device *dev); -static struct pnp_device guspnp = { - "GusPnP", - guspnp_probe, - guspnp_attach, - &nsnd, /* use this for all sound cards */ - &tty_imask /* imask */ -}; -DATA_SET (pnpdevice_set, guspnp); + for (; x--;) + if ((n = (io_rd(mss, AD1816_ALE) & AD1816_BUSY)) == 0) DELAY(10); + else return n; + printf("ad1816_wait_init failed 0x%02x.\n", n); + return -1; +} -static char * -guspnp_probe(u_long csn, u_long vend_id) +static unsigned short +ad1816_read(struct mss_info *mss, unsigned int reg) { - if (vend_id == 0x0100561e) { - struct pnp_cinfo d; - read_pnp_parms(&d, 0); - if (d.enable == 0) { - printf("This is a GusPnP, but LDN 0 is disabled\n"); - return NULL ; - } - return "GusPnP" ; - } - return NULL ; + int flags; + u_short x = 0; + + /* we don't want to be blocked here */ + flags = spltty(); + if (ad1816_wait_init(mss, 100) == -1) return 0; + io_wr(mss, AD1816_ALE, 0); + io_wr(mss, AD1816_ALE, (reg & AD1816_ALEMASK)); + if (ad1816_wait_init(mss, 100) == -1) return 0; + x = (io_rd(mss, AD1816_HIGH) << 8) | io_rd(mss, AD1816_LOW); + splx(flags); + return x; } static void -guspnp_attach(u_long csn, u_long vend_id, char *name, - struct isa_device *dev) +ad1816_write(struct mss_info *mss, unsigned int reg, unsigned short data) { - struct pnp_cinfo d ; - snddev_info tmp_d ; /* patched copy of the basic snddev_info */ - - u_char tmp; - - read_pnp_parms ( &d , 0 ) ; - - /* d.irq[1] = d.irq[0] ; */ - pnp_write ( 0xf2, 0xff ); /* enable power on the guspnp */ - - write_pnp_parms ( &d , 0 ); - enable_pnp_card(); - - tmp_d = mss_op_desc ; - snddev_last_probed = &tmp_d; - - dev->id_iobase = d.port[2] - 4 ; /* room for 4 mss registers */ - dev->id_drq = d.drq[1] ; /* XXX PLAY dma */ - dev->id_irq = (1 << d.irq[0] ) ; - dev->id_ointr = pcmintr ; - dev->id_flags = DV_F_DUAL_DMA | d.drq[0] ; /* REC dma */ - - tmp_d.io_base = d.port[2] - 4; - tmp_d.alt_base = d.port[0]; /* 0x220 */ - tmp_d.conf_base = d.port[1]; /* gus control block... */ - tmp_d.bd_id = MD_GUSPNP ; - - /* reset */ - gus_write(tmp_d.conf_base, 0x4c /* _URSTI */, 0 );/* Pull reset */ - DELAY(1000 * 30); - /* release reset and enable DAC */ - gus_write(tmp_d.conf_base, 0x4c /* _URSTI */, 3 ); - DELAY(1000 * 30); - /* end of reset */ - - outb( tmp_d.alt_base, 0xC ); /* enable int and dma */ - - /* - * unmute left & right line. Need to go in mode3, unmute, - * and back to mode 2 - */ - tmp = ad_read(&tmp_d, 0x0c); - ad_write(&tmp_d, 0x0c, 0x6c ); /* special value to enter mode 3 */ - ad_write(&tmp_d, 0x19, 0 ); /* unmute left */ - ad_write(&tmp_d, 0x1b, 0 ); /* unmute right */ - ad_write(&tmp_d, 0x0c, tmp ); /* restore old mode */ - - /* send codec interrupts on irq1 and only use that one */ - gus_write(tmp_d.conf_base, 0x5a , 0x4f ); + int flags; + + flags = spltty(); + if (ad1816_wait_init(mss, 100) == -1) return; + io_wr(mss, AD1816_ALE, (reg & AD1816_ALEMASK)); + io_wr(mss, AD1816_LOW, (data & 0x000000ff)); + io_wr(mss, AD1816_HIGH, (data & 0x0000ff00) >> 8); + splx(flags); +} - /* enable access to hidden regs */ - tmp = gus_read(tmp_d.conf_base, 0x5b /* IVERI */ ); - gus_write(tmp_d.conf_base, 0x5b , tmp | 1 ); - BVDDB(printf("GUS: silicon rev %c\n", 'A' + ( ( tmp & 0xf ) >> 4) );) +/* only one rec source is possible */ +static int +ad1816_set_recsrc(struct mss_info *mss, int mask) +{ + int dev; + + switch (mask) { + case SOUND_MASK_LINE: + case SOUND_MASK_LINE3: + dev = 0x00; + break; + + case SOUND_MASK_CD: + case SOUND_MASK_LINE1: + dev = 0x20; + break; + + case SOUND_MASK_MIC: + default: + dev = 0x50; + mask = SOUND_MASK_MIC; + } + + dev |= dev << 8; + ad1816_write(mss, 20, (ad1816_read(mss, 20) & ~0x7070) | dev); + return mask; +} - snprintf(tmp_d.name, sizeof(tmp_d.name), "%s", name); +#define AD1816_MUTE 31 /* value for mute */ - pcmattach(dev); +static int +ad1816_mixer_set(struct mss_info *mss, int dev, int left, int right) +{ + u_short reg = 0; + + /* Scale volumes */ + left = AD1816_MUTE - (AD1816_MUTE * left) / 100; + right = AD1816_MUTE - (AD1816_MUTE * right) / 100; + + reg = (left << 8) | right; + + /* do channel selective muting if volume is zero */ + if (left == AD1816_MUTE) reg |= 0x8000; + if (right == AD1816_MUTE) reg |= 0x0080; + + switch (dev) { + case SOUND_MIXER_VOLUME: /* Register 14 master volume */ + ad1816_write(mss, 14, reg); + break; + + case SOUND_MIXER_CD: /* Register 15 cd */ + case SOUND_MIXER_LINE1: + ad1816_write(mss, 15, reg); + break; + + case SOUND_MIXER_SYNTH: /* Register 16 synth */ + ad1816_write(mss, 16, reg); + break; + + case SOUND_MIXER_PCM: /* Register 4 pcm */ + ad1816_write(mss, 4, reg); + break; + + case SOUND_MIXER_LINE: + case SOUND_MIXER_LINE3: /* Register 18 line in */ + ad1816_write(mss, 18, reg); + break; + + case SOUND_MIXER_MIC: /* Register 19 mic volume */ + ad1816_write(mss, 19, reg & ~0xff); /* mic is mono */ + break; + + case SOUND_MIXER_IGAIN: + /* and now to something completely different ... */ + ad1816_write(mss, 20, ((ad1816_read(mss, 20) & ~0x0f0f) + | (((AD1816_MUTE - left) / 2) << 8) /* four bits of adc gain */ + | ((AD1816_MUTE - right) / 2))); + break; + + default: + printf("ad1816_mixer_set(): unknown device.\n"); + break; + } + + return 0; /* success */ } -#if 0 -int -gus_mem_write(snddev_info *d, int addr, u_char data) +static int +ad1816_trigger(struct mss_chinfo *ch, int go) { - gus_writew(d->conf_base, 0x43 , addr & 0xffff ); - gus_write(d->conf_base, 0x44 , (addr>>16) & 0xff ); - outb(d->conf_base + 7, data); + int wr, reg; + struct mss_info *mss = ch->parent; + + wr = (ch->dir == PCMDIR_PLAY); + reg = wr? AD1816_PLAY : AD1816_CAPT; + + switch (go) { + case PCMTRIG_START: + /* start only if not already running */ + if (!(io_rd(mss, reg) & AD1816_ENABLE)) { + int cnt = ((ch->buffer->dl) >> 2) - 1; + ad1816_write(mss, wr? 8 : 10, cnt); /* count */ + ad1816_write(mss, 1, ad1816_read(mss, 1) | + (wr? 0x8000 : 0x4000)); /* enable int */ + /* enable playback */ + io_wr(mss, reg, io_rd(mss, reg) | AD1816_ENABLE); + if (!(io_rd(mss, reg) & AD1816_ENABLE)) + printf("ad1816: failed to start %s DMA!\n", + wr? "play" : "rec"); + } + break; + + case PCMTRIG_STOP: + case PCMTRIG_ABORT: /* XXX check this... */ + /* we don't test here if it is running... */ + if (wr) { + ad1816_write(mss, 1, ad1816_read(mss, 1) & + ~(wr? 0x8000 : 0x4000)); + /* disable int */ + io_wr(mss, reg, io_rd(mss, reg) & ~AD1816_ENABLE); + /* disable playback */ + if (io_rd(mss, reg) & AD1816_ENABLE) + printf("ad1816: failed to stop %s DMA!\n", + wr? "play" : "rec"); + ad1816_write(mss, wr? 8 : 10, 0); /* reset base cnt */ + ad1816_write(mss, wr? 9 : 11, 0); /* reset cur cnt */ + } + break; + } + return 0; } -u_char -gus_mem_read(snddev_info *d, int addr) + +static int +ad1816_speed(struct mss_chinfo *ch, u_int32_t speed) { - gus_writew(d->conf_base, 0x43 , addr & 0xffff ); - gus_write(d->conf_base, 0x44 , (addr>>16) & 0xff ); - return inb(d->conf_base + 7); + struct mss_info *mss = ch->parent; + + RANGE(speed, 4000, 55200); + ad1816_write(mss, (ch->dir == PCMDIR_PLAY)? 2 : 3, speed); + return speed; } -void -gus_mem_cfg(snddev_info *d) +static int +ad1816_format(struct mss_chinfo *ch, u_int32_t format) { - int base; - u_char old; - u_char a, b; - - printf("configuring gus memory...\n"); - gus_writew(d->conf_base, 0x52 /* LMCFI */, 1 /* 512K*/); - old = gus_read(d->conf_base, 0x19); - gus_write(d->conf_base, 0x19, old | 1); /* enable enhaced mode */ - for (base = 0; base < 1024; base++) { - a=gus_mem_read(d, base*1024); - a = ~a ; - gus_mem_write(d, base*1024, a); - b=gus_mem_read(d, base*1024); - if ( b != a ) - break ; - } - printf("Have found %d KB ( 0x%x != 0x%x)\n", base, a, b); + struct mss_info *mss = ch->parent; + + int fmt = AD1816_U8, reg; + if (ch->dir == PCMDIR_PLAY) { + reg = AD1816_PLAY; + ad1816_write(mss, 8, 0x0000); /* reset base and current counter */ + ad1816_write(mss, 9, 0x0000); /* for playback and capture */ + } else { + reg = AD1816_CAPT; + ad1816_write(mss, 10, 0x0000); + ad1816_write(mss, 11, 0x0000); + } + switch (format & ~AFMT_STEREO) { + case AFMT_A_LAW: + fmt = AD1816_ALAW; + break; + + case AFMT_MU_LAW: + fmt = AD1816_MULAW; + break; + + case AFMT_S16_LE: + fmt = AD1816_S16LE; + break; + + case AFMT_S16_BE: + fmt = AD1816_S16BE; + break; + + case AFMT_U8: + fmt = AD1816_U8; + break; + } + if (format & AFMT_STEREO) fmt |= AD1816_STEREO; + io_wr(mss, reg, fmt); + return format; } -#endif /* gus mem cfg... */ + +#endif /* NPNP > 0 */ static int -ad1816_ioctl(dev_t i_dev, u_long cmd, caddr_t arg, int mode, struct proc * p) +mssmix_init(snd_mixer *m) { - snddev_info *d; - int unit; - int dev; - - dev = minor(i_dev); - unit = dev >> 4; - d = &pcm_info[unit]; - - if ((cmd & MIXER_WRITE(0)) == MIXER_WRITE(0)) { - cmd &= 0xff; - if (cmd == SOUND_MIXER_RECSRC) - return ad1816_set_recsrc(d, *(int *) arg); - else - return ad1816_mixer_set(d, cmd, *(int *) arg); - } - switch (cmd) { /* driver specific ioctls other than mixer - * calls */ - /* ad1816 has special features */ - case AIOGCAP: /* get capabilities */ - { - snd_capabilities *p = (snd_capabilities *) arg; - p->rate_min = 4000; - p->rate_max = 55200; - p->bufsize = d->bufsize; - p->formats = d->audio_fmt; - p->mixers = 1; - p->inputs = d->mix_devs; - p->left = p->right = 100; - return 0; + struct mss_info *mss = mix_getdevinfo(m); + + mix_setdevs(m, MODE2_MIXER_DEVICES); + mix_setrecdevs(m, MSS_REC_DEVICES); + switch(mss->bd_id) { + case MD_AD1816: + mix_setdevs(m, AD1816_MIXER_DEVICES); + mix_setrecdevs(m, AD1816_REC_DEVICES); + break; + + case MD_OPTI931: + mix_setdevs(m, OPTI931_MIXER_DEVICES); + ad_write(mss, 20, 0x88); + ad_write(mss, 21, 0x88); + break; + + case MD_AD1848: + mix_setdevs(m, MODE1_MIXER_DEVICES); + break; + + case MD_GUSPNP: + /* this is only necessary in mode 3 ... */ + ad_write(mss, 22, 0x88); + ad_write(mss, 23, 0x88); + break; } - default: - { - return ENOSYS; /* fallback to default */ - } - break; - } + return 0; } static int -ad1816_callback(snddev_info * d, int reason) +mssmix_set(snd_mixer *m, unsigned dev, unsigned left, unsigned right) { - int wr, cnt; - - wr = reason & SND_CB_WR; - reason &= SND_CB_REASON_MASK; - - switch (reason) { - case SND_CB_INIT: - ad1816_reinit(d); - reset_dbuf(&(d->dbuf_in), SND_CHAN_RD); - reset_dbuf(&(d->dbuf_out), SND_CHAN_WR); - return 1; - break; - - case SND_CB_START: - cnt = wr ? d->dbuf_out.dl : d->dbuf_in.dl; - - cnt /= 4; - cnt--; - - /* start only if not already running */ - if (wr && !(inb(ad1816_play(d)) & AD1816_ENABLE)) { - /* set dma counter */ - ad1816_write(d, 8, cnt); /* playback count */ - /* int enable */ - ad1816_write(d, 1, ad1816_read(d, 1) | 0x8000); - /* enable playback */ - outb(ad1816_play(d), (inb(ad1816_play(d)) | AD1816_ENABLE)); - /* check if we succeeded */ - if (!(inb(ad1816_play(d)) & AD1816_ENABLE)) { - printf("ad1816: failed to start write (playback) DMA !\n"); - } - } else if (!wr && !(inb(ad1816_capt(d)) & AD1816_ENABLE)) { - /* same for capture */ - ad1816_write(d, 10, cnt); /* capture count */ - ad1816_write(d, 1, ad1816_read(d, 1) | 0x4000); /* int */ - outb(ad1816_capt(d), (inb(ad1816_capt(d)) | AD1816_ENABLE)); /* CEN */ - if (!(inb(ad1816_capt(d)) & AD1816_ENABLE)) { /* check */ - printf("ad1816: failed to start read (capture) DMA !\n"); - } - } - break; - - case SND_CB_STOP: - case SND_CB_ABORT: /* XXX check this... */ - /* we don't test here if it is running... */ - if (wr) { - ad1816_write(d, 1, ad1816_read(d, 1) & ~0x8000); - /* disable int */ - outb(ad1816_play(d), (inb(ad1816_play(d)) & ~AD1816_ENABLE)); - /* disable playback */ - if ((inb(ad1816_play(d)) & AD1816_ENABLE)) { - printf("ad1816: failed to stop write (playback) DMA !\n"); - } - ad1816_write(d, 8, 0); /* reset base counter */ - ad1816_write(d, 9, 0); /* reset cur counter */ - } else { - /* same for capture */ - ad1816_write(d, 1, ad1816_read(d, 1) & ~0x4000); - outb(ad1816_capt(d), (inb(ad1816_capt(d)) & ~AD1816_ENABLE)); - if ((inb(ad1816_capt(d)) & AD1816_ENABLE)) { - printf("ad1816: failed to stop read (capture) DMA !\n"); - } - ad1816_write(d, 10, 0); - ad1816_write(d, 11, 0); - } - break; - } + struct mss_info *mss = mix_getdevinfo(m); - return 0; -} +#if NPNP > 0 + if (mss->bd_id == MD_AD1816) ad1816_mixer_set(mss, dev, left, right); else +#endif + mss_mixer_set(mss, dev, left, right); -static void -ad1816_intr(int unit) -{ - snddev_info *d = &pcm_info[unit]; - unsigned char c, served = 0; - - /* get interupt status */ - c = inb(ad1816_int(d)); - - /* check for stray interupts */ - if (c & ~(AD1816_INTRCI | AD1816_INTRPI)) { - printf("ad1816: Stray interrupt 0x%x.\n", c); - c = c & (AD1816_INTRCI | AD1816_INTRPI); - outb(ad1816_int(d), c); /* ack it anyway */ - } - /* check for capture interupt */ - if (d->dbuf_in.dl && (c & AD1816_INTRCI)) { - outb(ad1816_int(d), c & ~AD1816_INTRCI); /* ack it */ - if (inb(ad1816_int(d)) & AD1816_INTRCI) - printf("ad1816: Failed to clear cp int !!!\n"); - dsp_rdintr(d); - served |= AD1816_INTRCI; /* cp served */ - } - /* check for playback interupt */ - if (d->dbuf_out.dl && (c & AD1816_INTRPI)) { - outb(ad1816_int(d), c & ~AD1816_INTRPI); /* ack it */ - if ((inb(ad1816_int(d)) & AD1816_INTRPI) != 0) - printf("ad1816: Failed to clear pb int !!!\n"); - dsp_wrintr(d); - served |= AD1816_INTRPI; /* pb served */ - } - if (served == 0) { - /* this probably means this is not a (working) ad1816 chip, */ - /* or an error in dma handling */ - printf("ad1816: raised an interrupt without reason 0x%x.\n", c); - outb(ad1816_int(d), 0); /* Clear interrupt status anyway */ - } + return left | (right << 8); } static int -ad1816_wait_init(snddev_info * d, int x) +mssmix_setrecsrc(snd_mixer *m, u_int32_t src) { - int n = 0; /* to shut up the compiler... */ + struct mss_info *mss = mix_getdevinfo(m); - for (; x--;) - if (((n = (inb(ad1816_ale(d)) & AD1816_BUSY))) == 0) - DELAY(10); - else - return n; - printf("ad1816_wait_init failed 0x%02x.\n", inb(ad1816_ale(d))); - return n; +#if NPNP > 0 + if (mss->bd_id == MD_AD1816) src = ad1816_set_recsrc(mss, src); else +#endif + src = mss_set_recsrc(mss, src); + return src; } -static unsigned short -ad1816_read(snddev_info * d, unsigned int reg) +static int +ymmix_init(snd_mixer *m) { - int flags; - u_short x; + struct mss_info *mss = mix_getdevinfo(m); + + mssmix_init(m); + mix_setdevs(m, mix_getdevs(m) | SOUND_MASK_VOLUME | SOUND_MASK_MIC); + /* Set master volume */ + conf_wr(mss, OPL3SAx_VOLUMEL, 7); + conf_wr(mss, OPL3SAx_VOLUMER, 7); - /* we don't want to be blocked here */ - flags = spltty(); - if (ad1816_wait_init(d, 100) == 0) { - printf("ad1816_read: chip timeout before read.\n"); - return 0; - } - outb(ad1816_ale(d), (u_char) 0); - outb(ad1816_ale(d), (u_char) (reg & AD1816_ALEMASK)); - if (ad1816_wait_init(d, 100) == 0) { - printf("ad1816_read: chip timeout during read.\n"); return 0; - } - x = (inb(ad1816_high(d)) << 8) | inb(ad1816_low(d)); - splx(flags); - return x; } -static void -ad1816_write(snddev_info * d, unsigned int reg, unsigned short data) +static int +ymmix_set(snd_mixer *m, unsigned dev, unsigned left, unsigned right) { - int flags; - - flags = spltty(); - if (ad1816_wait_init(d, 100) == 0) { - printf("ad1816_write: chip timeout before write.\n"); - return; - } - outb(ad1816_ale(d), (u_char) (reg & AD1816_ALEMASK)); - outb(ad1816_low(d), (u_char) (data & 0x000000ff)); - outb(ad1816_high(d), (u_char) ((data & 0x0000ff00) >> 8)); - splx(flags); + struct mss_info *mss = mix_getdevinfo(m); + int t; + + switch (dev) { + case SOUND_MIXER_VOLUME: + if (left) t = 15 - (left * 15) / 100; + else t = 0x80; /* mute */ + conf_wr(mss, OPL3SAx_VOLUMEL, t); + if (right) t = 15 - (right * 15) / 100; + else t = 0x80; /* mute */ + conf_wr(mss, OPL3SAx_VOLUMER, t); + break; + + case SOUND_MIXER_MIC: + t = left; + if (left) t = 31 - (left * 31) / 100; + else t = 0x80; /* mute */ + conf_wr(mss, OPL3SAx_MIC, t); + break; + + case SOUND_MIXER_BASS: + case SOUND_MIXER_TREBLE: + /* Later maybe */ + + default: + mss_mixer_set(mss, dev, left, right); + } + + return left | (right << 8); } -#if 0 /* unused right now..., and untested... */ -static void -ad1816_mute(snddev_info * d) +static int +ymmix_setrecsrc(snd_mixer *m, u_int32_t src) { - ad1816_write(d, 14, ad1816_read(d, 14) | 0x8000 | 0x80); + struct mss_info *mss = mix_getdevinfo(m); + src = mss_set_recsrc(mss, src); + return src; } -static void -ad1816_unmute(snddev_info * d) +/* channel interface */ +static void * +msschan_init(void *devinfo, snd_dbuf *b, pcm_channel *c, int dir) { - ad1816_write(d, 14, ad1816_read(d, 14) & ~(0x8000 | 0x80)); + struct mss_info *mss = devinfo; + struct mss_chinfo *ch = (dir == PCMDIR_PLAY)? &mss->pch : &mss->rch; + + ch->parent = mss; + ch->channel = c; + ch->buffer = b; + ch->buffer->bufsize = DSP_BUFFSIZE; + if (chn_allocbuf(ch->buffer, mss->parent_dmat) == -1) return NULL; + return ch; } -#endif - -/* only one rec source is possible */ static int -ad1816_set_recsrc(snddev_info * d, int mask) +msschan_setdir(void *data, int dir) { - mask &= d->mix_rec_devs; - - switch (mask) { - case SOUND_MASK_LINE: - case SOUND_MASK_LINE3: - ad1816_write(d, 20, (ad1816_read(d, 20) & ~0x7070) | 0x0000); - break; + struct mss_chinfo *ch = data; - case SOUND_MASK_CD: - case SOUND_MASK_LINE1: - ad1816_write(d, 20, (ad1816_read(d, 20) & ~0x7070) | 0x2020); - break; - - case SOUND_MASK_MIC: - default: - ad1816_write(d, 20, (ad1816_read(d, 20) & ~0x7070) | 0x5050); - } - - d->mix_recsrc = mask; - - return 0; /* success */ + ch->buffer->chan = (dir == PCMDIR_PLAY)? ch->parent->pdma : ch->parent->rdma; + ch->dir = dir; + return 0; } -#define AD1816_MUTE 31 /* value for mute */ - static int -ad1816_mixer_set(snddev_info * d, int dev, int value) +msschan_setformat(void *data, u_int32_t format) { - u_char left = (value & 0x000000ff); - u_char right = (value & 0x0000ff00) >> 8; - u_short reg = 0; - - if (dev > 31) - return EINVAL; - - if (!(d->mix_devs & (1 << dev))) - return EINVAL; - - if (left > 100) - left = 100; - if (right > 100) - right = 100; - - d->mix_levels[dev] = left | (right << 8); - - /* Scale volumes */ - left = AD1816_MUTE - (AD1816_MUTE * left) / 100; - right = AD1816_MUTE - (AD1816_MUTE * right) / 100; - - reg = (left << 8) | right; - - /* do channel selective muting if volume is zero */ - if (left == AD1816_MUTE) - reg |= 0x8000; - if (right == AD1816_MUTE) - reg |= 0x0080; - - switch (dev) { - case SOUND_MIXER_VOLUME: /* Register 14 master volume */ - ad1816_write(d, 14, reg); - break; - case SOUND_MIXER_CD: /* Register 15 cd */ - case SOUND_MIXER_LINE1: - ad1816_write(d, 15, reg); - break; - case SOUND_MIXER_SYNTH: /* Register 16 synth */ - ad1816_write(d, 16, reg); - break; - case SOUND_MIXER_PCM: /* Register 4 pcm */ - ad1816_write(d, 4, reg); - break; - case SOUND_MIXER_LINE: - case SOUND_MIXER_LINE3: /* Register 18 line in */ - ad1816_write(d, 18, reg); - break; - case SOUND_MIXER_MIC: /* Register 19 mic volume */ - ad1816_write(d, 19, reg & ~0xff); /* mic is mono */ - break; - case SOUND_MIXER_IGAIN: - /* and now to something completely different ... */ - ad1816_write(d, 20, ((ad1816_read(d, 20) & ~0x0f0f) - | (((AD1816_MUTE - left) / 2) << 8) /* four bits of adc gain */ - | ((AD1816_MUTE - right) / 2))); - break; - default: - printf("ad1816_mixer_set(): unknown device.\n"); - break; - } - - return 0; /* success */ + struct mss_chinfo *ch = data; + +#if NPNP > 0 + if (ch->parent->bd_id == MD_AD1816) ad1816_format(ch, format); else +#endif + mss_format(ch, format); + return 0; } -static void -ad1816_mixer_reset(snddev_info * d) +static int +msschan_setspeed(void *data, u_int32_t speed) { - int i; - - d->mix_devs = AD1816_MIXER_DEVICES; - d->mix_rec_devs = AD1816_REC_DEVICES; + struct mss_chinfo *ch = data; - for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) - if (d->mix_devs & (1 << i)) - ad1816_mixer_set(d, i, default_mixer_levels[i]); - ad1816_set_recsrc(d, SOUND_MASK_MIC); +#if NPNP > 0 + if (ch->parent->bd_id == MD_AD1816) return ad1816_speed(ch, speed); else +#endif + return mss_speed(ch, speed); } -/* Set the playback and capture rates. */ - static int -ad1816_speed(snddev_info * d) +msschan_setblocksize(void *data, u_int32_t blocksize) { - RANGE(d->play_speed,4000,55200); - RANGE(d->rec_speed,4000,55200); + return blocksize; +} - ad1816_write(d, 2, d->play_speed); - ad1816_write(d, 3, d->rec_speed); +static int +msschan_trigger(void *data, int go) +{ + struct mss_chinfo *ch = data; - return d->play_speed; + buf_isadma(ch->buffer, go); +#if NPNP > 0 + if (ch->parent->bd_id == MD_AD1816) ad1816_trigger(ch, go); else +#endif + mss_trigger(ch, go); + return 0; } -/* - * ad1816_format checks that the format is supported (or defaults to AFMT_U8) - * and sets the chip to the desired format. - */ - static int -ad1816_format(snddev_info * d) +msschan_getptr(void *data) { - int oldplay =inb(ad1816_play(d)) & ~AD1816_FORMASK; - int oldrec = inb(ad1816_capt(d)) & ~AD1816_FORMASK; - int play = (d->play_fmt & d->audio_fmt) ? d->play_fmt : AFMT_U8; - int rec = (d->rec_fmt & d->audio_fmt) ? d->rec_fmt : AFMT_U8; - - /* - * check that arg is one of the supported formats in d->format; otherwise - * fallback to AFMT_U8 - */ - - switch (play) { - case AFMT_A_LAW: - outb(ad1816_play(d), oldplay | AD1816_ALAW); - break; - case AFMT_MU_LAW: - outb(ad1816_play(d), oldplay | AD1816_MULAW); - break; - case AFMT_S16_LE: - outb(ad1816_play(d), oldplay | AD1816_S16LE); - break; - case AFMT_S16_BE: - outb(ad1816_play(d), oldplay | AD1816_S16BE); - break; - default: - /* unlikely to happen */ - printf("ad1816: unknown play format. defaulting to U8.\n"); - case AFMT_U8: - outb(ad1816_play(d), oldplay | AD1816_U8); - break; - } - - switch (rec) { - case AFMT_A_LAW: - outb(ad1816_capt(d), oldrec | AD1816_ALAW); - break; - case AFMT_MU_LAW: - outb(ad1816_capt(d), oldrec | AD1816_MULAW); - break; - case AFMT_S16_LE: - outb(ad1816_capt(d), oldrec | AD1816_S16LE); - break; - case AFMT_S16_BE: - outb(ad1816_capt(d), oldrec | AD1816_S16BE); - break; - default: - printf("ad1816: unknown capture format. defaulting to U8.\n"); - case AFMT_U8: - outb(ad1816_capt(d), oldrec | AD1816_U8); - break; - } - - d->play_fmt = play; - d->rec_fmt = rec; - - return (play); + struct mss_chinfo *ch = data; + return buf_isadmaptr(ch->buffer); } -/* - * ad1816_reinit resets codec registers - */ -static void -ad1816_reinit(snddev_info * d) +static pcmchan_caps * +msschan_getcaps(void *data) { - ad1816_write(d, 8, 0x0000); /* reset base and current counter */ - ad1816_write(d, 9, 0x0000); /* for playback and capture */ - ad1816_write(d, 10, 0x0000); - ad1816_write(d, 11, 0x0000); - - if (d->flags & SND_F_STEREO) { - outb((ad1816_play(d)), AD1816_STEREO); /* set playback to stereo */ - outb((ad1816_capt(d)), AD1816_STEREO); /* set capture to stereo */ - } else { - outb((ad1816_play(d)), 0x00); /* set playback to mono */ - outb((ad1816_capt(d)), 0x00); /* set capture to mono */ - } - - ad1816_format(d); - ad1816_speed(d); - - snd_set_blocksize(d); /* update blocksize if user did not force it */ + struct mss_chinfo *ch = data; + + switch(ch->parent->bd_id) { + case MD_OPTI931: + return &opti931_caps; + break; + + case MD_GUSPNP: + return &guspnp_caps; + break; + + case MD_AD1816: + return &ad1816_caps; + break; + + default: + return &mss_caps; + break; + } } -#endif /* NPNP > 0 */ #endif /* NPCM > 0 */ diff --git a/sys/dev/sound/isa/mss.h b/sys/dev/sound/isa/mss.h index 20ceaaf..ab41964 100644 --- a/sys/dev/sound/isa/mss.h +++ b/sys/dev/sound/isa/mss.h @@ -28,9 +28,9 @@ ahead. * */ -#define io_Index_Addr(d) ((d)->io_base + 4) -#define IA_BUSY 0x80 /* readonly, set when busy */ -#define IA_MCE 0x40 /* the MCE bit. */ +#define MSS_INDEX (0 + 4) +#define MSS_IDXBUSY 0x80 /* readonly, set when busy */ +#define MSS_MCE 0x40 /* the MCE bit. */ /* * the MCE bit must be set whenever the current mode of the * codec is changed; this in particular is true for the @@ -38,15 +38,15 @@ ahead. * Only exception are CEN and PEN which can be changed on the fly. * The DAC output is muted when MCE is set. */ -#define IA_TRD 0x20 /* Transfer request disable */ +#define MSS_TRD 0x20 /* Transfer request disable */ /* * When TRD is set, DMA transfers cease when the INT bit in * the MSS status reg is set. Must be cleared for automode * DMA, set otherwise. */ -#define IA_AMASK 0x1f /* mask for indirect address */ +#define MSS_IDXMASK 0x1f /* mask for indirect address */ -#define io_Indexed_Data(d) ((d)->io_base+1+4) +#define MSS_IDATA (1 + 4) /* * data to be transferred to the indirect register addressed * by index addr. During init and sw. powerdown, cannot be @@ -54,7 +54,7 @@ ahead. * busy flag). */ -#define io_Status(d) ((d)->io_base+2+4) +#define MSS_STATUS (2 + 4) #define IS_CUL 0x80 /* capture upper/lower */ #define IS_CLR 0x40 /* capture left/right */ @@ -67,11 +67,12 @@ ahead. /* * IS_INT is clreared by any write to the status register. */ - +#if 0 #define io_Polled_IO(d) ((d)->io_base+3+4) /* * this register is used in case of polled i/o */ +#endif /* * The MSS has a set of 16 (or 32 depending on the model) indirect @@ -104,18 +105,22 @@ ahead. #define BD_F_MCE_BIT 0x0001 #define BD_F_IRQ_OK 0x0002 #define BD_F_TMR_RUN 0x0004 +#define BD_F_MSS_OFFSET 0x0008 /* offset mss writes by -4 */ +#define BD_F_DUPLEX 0x0010 /* AD1816 register macros */ -#define ad1816_ale(d) ((d)->io_base+0) /* indirect reg access */ -#define ad1816_int(d) ((d)->io_base+1) /* interupt status */ -#define ad1816_low(d) ((d)->io_base+2) /* indirect low byte */ -#define ad1816_high(d) ((d)->io_base+3) /* indirect high byte */ -/* unused */ -#define ad1816_pioD(d) ((d)->io_base+4) /* PIO debug */ -#define ad1816_pios(d) ((d)->io_base+5) /* PIO status */ -#define ad1816_piod(d) ((d)->io_base+6) /* PIO data */ -/* end of unused */ +#define AD1816_ALE 0 /* indirect reg access */ +#define AD1816_INT 1 /* interupt status */ +#define AD1816_LOW 2 /* indirect low byte */ +#define AD1816_HIGH 3 /* indirect high byte */ + +#if 0 +#define ad1816_pioD(d) ((d)->io_base+4) /* PIO debug */ +#define ad1816_pios(d) ((d)->io_base+5) /* PIO status */ +#define ad1816_piod(d) ((d)->io_base+6) /* PIO data */ +#endif + /* values for playback/capture config: bits: 0 enable/disable 1 pio/dma @@ -127,19 +132,22 @@ ahead. 01 8bit alaw (comp) 11 16bit be (uncomp) */ -#define ad1816_play(d) ((d)->io_base+8) /* playback config */ -#define ad1816_capt(d) ((d)->io_base+9) /* capture config */ + +#define AD1816_PLAY 8 /* playback config */ +#define AD1816_CAPT 9 /* capture config */ #define AD1816_BUSY 0x80 /* chip is busy */ #define AD1816_ALEMASK 0x3F /* mask for indirect adr. */ -/* unusud */ + +#if 0 #define AD1816_INTRSI 0x01 /* sb intr */ #define AD1816_INTRGI 0x02 /* game intr */ #define AD1816_INTRRI 0x04 /* ring intr */ #define AD1816_INTRDI 0x08 /* dsp intr */ #define AD1816_INTRVI 0x10 /* vol intr */ #define AD1816_INTRTI 0x20 /* timer intr */ -/* used again */ +#endif + #define AD1816_INTRCI 0x40 /* capture intr */ #define AD1816_INTRPI 0x80 /* playback intr */ /* PIO stuff is not supplied here */ @@ -155,13 +163,16 @@ ahead. #define AD1816_S16BE 0x30 /* 16 bit linear big endian */ #define AD1816_FORMASK 0x38 /* format mask */ + + + /* * sound/ad1848_mixer.h - * + * * Definitions for the mixer of AD1848 and compatible codecs. - * + * * Copyright by Hannu Savolainen 1994 - * + * * 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 @@ -169,7 +180,7 @@ ahead. * 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 @@ -281,24 +292,148 @@ MIX_NONE(SOUND_MIXER_LINE3), (SOUND_MASK_VOLUME | SOUND_MASK_PCM | SOUND_MASK_SYNTH | \ SOUND_MASK_LINE | SOUND_MASK_MIC | SOUND_MASK_CD | SOUND_MASK_IGAIN) -static u_short default_mixer_levels[SOUND_MIXER_NRDEVICES] = { - 0x5a5a, /* Master Volume */ - 0x3232, /* Bass */ - 0x3232, /* Treble */ - 0x4b4b, /* FM */ - 0x4040, /* PCM */ - 0x4b4b, /* PC Speaker */ - 0x2020, /* Ext Line */ - 0x4040, /* Mic */ - 0x4b4b, /* CD */ - 0x0000, /* Recording monitor */ - 0x4b4b, /* SB PCM */ - 0x4b4b, /* Recording level */ - 0x2525, /* Input gain */ - 0x0000, /* Output gain */ - /* 0x4040, Line1 */ - 0x0000, /* Line1 */ - 0x0000, /* Line2 */ - 0x1515 /* Line3 (usually line in)*/ -}; +/*- + * Copyright (c) 1999 Doug Rabson + * 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. + * + * $Id$ + */ + +/* + * Register definitions for the Yamaha OPL3-SA[23x]. + */ +#define OPL3SAx_POWER 0x01 /* Power Management (R/W) */ +#define OPL3SAx_POWER_PDX 0x01 /* Set to 1 to halt oscillator */ +#define OPL3SAx_POWER_PDN 0x02 /* Set to 1 to power down */ +#define OPL3SAx_POWER_PSV 0x04 /* Set to 1 to power save */ +#define OPL3SAx_POWER_ADOWN 0x20 /* Analog power (?) */ + +#define OPL3SAx_SYSTEM 0x02 /* System control (R/W) */ +#define OPL3SAx_SYSTEM_VZE 0x01 /* I2S audio routing */ +#define OPL3SAx_SYSTEM_IDSEL 0x03 /* SB compat version select */ +#define OPL3SAx_SYSTEM_SBHE 0x80 /* 0 for AT bus, 1 for XT bus */ + +#define OPL3SAx_IRQCONF 0x03 /* Interrupt configuration (R/W */ +#define OPL3SAx_IRQCONF_WSSA 0x01 /* WSS interrupts through IRQA */ +#define OPL3SAx_IRQCONF_SBA 0x02 /* WSS interrupts through IRQA */ +#define OPL3SAx_IRQCONF_MPUA 0x04 /* WSS interrupts through IRQA */ +#define OPL3SAx_IRQCONF_OPL3A 0x08 /* WSS interrupts through IRQA */ +#define OPL3SAx_IRQCONF_WSSB 0x10 /* WSS interrupts through IRQB */ +#define OPL3SAx_IRQCONF_SBB 0x20 /* WSS interrupts through IRQB */ +#define OPL3SAx_IRQCONF_MPUB 0x40 /* WSS interrupts through IRQB */ +#define OPL3SAx_IRQCONF_OPL3B 0x80 /* WSS interrupts through IRQB */ + +#define OPL3SAx_IRQSTATUSA 0x04 /* Interrupt (IRQ-A) Status (RO) */ +#define OPL3SAx_IRQSTATUSB 0x05 /* Interrupt (IRQ-B) Status (RO) */ +#define OPL3SAx_IRQSTATUS_PI 0x01 /* Playback Flag of CODEC */ +#define OPL3SAx_IRQSTATUS_CI 0x02 /* Recording Flag of CODEC */ +#define OPL3SAx_IRQSTATUS_TI 0x04 /* Timer Flag of CODEC */ +#define OPL3SAx_IRQSTATUS_SB 0x08 /* SB compat Playback Interrupt Flag */ +#define OPL3SAx_IRQSTATUS_MPU 0x10 /* MPU401 Interrupt Flag */ +#define OPL3SAx_IRQSTATUS_OPL3 0x20 /* Internal FM Timer Flag */ +#define OPL3SAx_IRQSTATUS_MV 0x40 /* HW Volume Interrupt Flag */ +#define OPL3SAx_IRQSTATUS_PI 0x01 /* Playback Flag of CODEC */ +#define OPL3SAx_IRQSTATUS_CI 0x02 /* Recording Flag of CODEC */ +#define OPL3SAx_IRQSTATUS_TI 0x04 /* Timer Flag of CODEC */ +#define OPL3SAx_IRQSTATUS_SB 0x08 /* SB compat Playback Interrupt Flag */ +#define OPL3SAx_IRQSTATUS_MPU 0x10 /* MPU401 Interrupt Flag */ +#define OPL3SAx_IRQSTATUS_OPL3 0x20 /* Internal FM Timer Flag */ +#define OPL3SAx_IRQSTATUS_MV 0x40 /* HW Volume Interrupt Flag */ + +#define OPL3SAx_DMACONF 0x06 /* DMA configuration (R/W) */ +#define OPL3SAx_DMACONF_WSSPA 0x01 /* WSS Playback on DMA-A */ +#define OPL3SAx_DMACONF_WSSRA 0x02 /* WSS Recording on DMA-A */ +#define OPL3SAx_DMACONF_SBA 0x02 /* SB Playback on DMA-A */ +#define OPL3SAx_DMACONF_WSSPB 0x10 /* WSS Playback on DMA-A */ +#define OPL3SAx_DMACONF_WSSRB 0x20 /* WSS Recording on DMA-A */ +#define OPL3SAx_DMACONF_SBB 0x20 /* SB Playback on DMA-A */ + +#define OPL3SAx_VOLUMEL 0x07 /* Master Volume Left (R/W) */ +#define OPL3SAx_VOLUMEL_MVL 0x0f /* Attenuation level */ +#define OPL3SAx_VOLUMEL_MVLM 0x80 /* Mute */ + +#define OPL3SAx_VOLUMER 0x08 /* Master Volume Right (R/W) */ +#define OPL3SAx_VOLUMER_MVR 0x0f /* Attenuation level */ +#define OPL3SAx_VOLUMER_MVRM 0x80 /* Mute */ + +#define OPL3SAx_MIC 0x09 /* MIC Volume (R/W) */ +#define OPL3SAx_VOLUMER_MCV 0x1f /* Attenuation level */ +#define OPL3SAx_VOLUMER_MICM 0x80 /* Mute */ + +#define OPL3SAx_MISC 0x0a /* Miscellaneous */ +#define OPL3SAx_MISC_VER 0x07 /* Version */ +#define OPL3SAx_MISC_MODE 0x08 /* SB or WSS mode */ +#define OPL3SAx_MISC_MCSW 0x10 /* */ +#define OPL3SAx_MISC_VEN 0x80 /* Enable hardware volume control */ + +#define OPL3SAx_WSSDMA 0x0b /* WSS DMA Counter (RW) (4 regs) */ + +#define OPL3SAx_WSSIRQSCAN 0x0f /* WSS Interrupt Scan out/in (R/W) */ +#define OPL3SAx_WSSIRQSCAN_SPI 0x01 +#define OPL3SAx_WSSIRQSCAN_SCI 0x02 +#define OPL3SAx_WSSIRQSCAN_STI 0x04 + +#define OPL3SAx_SBSTATE 0x10 /* SB compat Internal State (R/W) */ +#define OPL3SAx_SBSTATE_SBPDR 0x01 /* SB Power Down Request */ +#define OPL3SAx_SBSTATE_SE 0x02 /* Scan Enable */ +#define OPL3SAx_SBSTATE_SM 0x04 /* Scan Mode */ +#define OPL3SAx_SBSTATE_SS 0x08 /* Scan Select */ +#define OPL3SAx_SBSTATE_SBPDA 0x80 /* SB Power Down Acknowledge */ + +#define OPL3SAx_SBDATA 0x11 /* SB compat State Scan Data (R/W) */ + +#define OPL3SAx_DIGITALPOWER 0x12 /* Digital Partial Power Down (R/W) */ +#define OPL3SAx_DIGITALPOWER_PnP 0x01 +#define OPL3SAx_DIGITALPOWER_SB 0x02 +#define OPL3SAx_DIGITALPOWER_WSSP 0x04 +#define OPL3SAx_DIGITALPOWER_WSSR 0x08 +#define OPL3SAx_DIGITALPOWER_FM 0x10 +#define OPL3SAx_DIGITALPOWER_MCLK0 0x20 +#define OPL3SAx_DIGITALPOWER_MPU 0x40 +#define OPL3SAx_DIGITALPOWER_JOY 0x80 + +#define OPL3SAx_ANALOGPOWER 0x13 /* Analog Partial Power Down (R/W) */ +#define OPL3SAx_ANALOGPOWER_WIDE 0x01 +#define OPL3SAx_ANALOGPOWER_SBDAC 0x02 +#define OPL3SAx_ANALOGPOWER_DA 0x04 +#define OPL3SAx_ANALOGPOWER_AD 0x08 +#define OPL3SAx_ANALOGPOWER_FMDAC 0x10 + +#define OPL3SAx_WIDE 0x14 /* Enhanced control(WIDE) (R/W) */ +#define OPL3SAx_WIDE_WIDEL 0x07 /* Wide level on Left Channel */ +#define OPL3SAx_WIDE_WIDER 0x70 /* Wide level on Right Channel */ + +#define OPL3SAx_BASS 0x15 /* Enhanced control(BASS) (R/W) */ +#define OPL3SAx_BASS_BASSL 0x07 /* Bass level on Left Channel */ +#define OPL3SAx_BASS_BASSR 0x70 /* Bass level on Right Channel */ + +#define OPL3SAx_TREBLE 0x16 /* Enhanced control(TREBLE) (R/W) */ +#define OPL3SAx_TREBLE_TREBLEL 0x07 /* Treble level on Left Channel */ +#define OPL3SAx_TREBLE_TREBLER 0x70 /* Treble level on Right Channel */ + +#define OPL3SAx_HWVOL 0x17 /* HW Volume IRQ Configuration (R/W) */ +#define OPL3SAx_HWVOL_IRQA 0x10 /* HW Volume IRQ on IRQ-A */ +#define OPL3SAx_HWVOL_IRQB 0x20 /* HW Volume IRQ on IRQ-B */ + diff --git a/sys/dev/sound/isa/sb.c b/sys/dev/sound/isa/sb.c index 7544dae..2a8d336 100644 --- a/sys/dev/sound/isa/sb.c +++ b/sys/dev/sound/isa/sb.c @@ -1,1366 +1,1117 @@ /* - * sound/sb_dsp.c - * - * driver for the SoundBlaster and clones. - * + * Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk> * Copyright 1997,1998 Luigi Rizzo. * * Derived from files in the Voxware 3.5 distribution, * Copyright by Hannu Savolainen 1994, under the same copyright * conditions. - * + * 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. + * 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. - * - */ - -/* - * use this as a template file for board-specific drivers. - * The next two lines (and the final #endif) are in all drivers: + * 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. + * + * $Id$ */ -#include <i386/isa/snd/sound.h> +#include <dev/pcm/sound.h> #if NPCM > 0 -/* - * Begin with the board-specific include files... - */ - #define __SB_MIXER_C__ /* XXX warning... */ -#include <i386/isa/snd/sbcard.h> - -/* - * then prototypes of functions which go in the snddev_info - * (usually static, unless they are shared by other modules)... - */ +#include <dev/pcm/isa/sb.h> + +/* channel interface */ +static void *sbchan_init(void *devinfo, snd_dbuf *b, pcm_channel *c, int dir); +static int sbchan_setdir(void *data, int dir); +static int sbchan_setformat(void *data, u_int32_t format); +static int sbchan_setspeed(void *data, u_int32_t speed); +static int sbchan_setblocksize(void *data, u_int32_t blocksize); +static int sbchan_trigger(void *data, int go); +static int sbchan_getptr(void *data); +static pcmchan_caps *sbchan_getcaps(void *data); + +static pcmchan_caps sb_playcaps = { + 4000, 22050, + AFMT_U8, + AFMT_U8 +}; -static int sb_probe(struct isa_device *dev); -static int sb_attach(struct isa_device *dev); +static pcmchan_caps sb_reccaps = { + 4000, 13000, + AFMT_U8, + AFMT_U8 +}; -static d_open_t sb_dsp_open; -static d_close_t sb_dsp_close; -static d_ioctl_t sb_dsp_ioctl; -static irq_proc_t sb_intr; -static snd_callback_t sb_callback; +static pcmchan_caps sbpro_playcaps = { + 4000, 45000, + AFMT_STEREO | AFMT_U8, + AFMT_STEREO | AFMT_U8 +}; -/* - * and prototypes for other private functions defined in this module. - */ +static pcmchan_caps sbpro_reccaps = { + 4000, 15000, + AFMT_STEREO | AFMT_U8, + AFMT_STEREO | AFMT_U8 +}; -static void sb_dsp_init(snddev_info *d, struct isa_device *dev); -static void sb_mix_init(snddev_info *d); -static int sb_mixer_set(snddev_info *d, int dev, int value); -static int dsp_speed(snddev_info *d); -static void sb_mixer_reset(snddev_info *d); +static pcmchan_caps sb16_playcaps = { + 5000, 45000, + AFMT_STEREO | AFMT_S16_LE, + AFMT_STEREO | AFMT_S16_LE +}; -u_int sb_get_byte(int io_base); -int ess_write(int io_base, u_char reg, int val); -int ess_read(int io_base, u_char reg); +static pcmchan_caps sb16_reccaps = { + 5000, 45000, + AFMT_STEREO | AFMT_U8, + AFMT_STEREO | AFMT_U8 +}; -/* - * Then put here the descriptors for the various boards supported - * by this module, properly initialized. - */ +static pcmchan_caps ess_playcaps = { + 5000, 49000, + AFMT_STEREO | AFMT_U8 | AFMT_S16_LE, + AFMT_STEREO | AFMT_S16_LE +}; -snddev_info sb_op_desc = { - "basic soundblaster", +static pcmchan_caps ess_reccaps = { + 5000, 49000, + AFMT_STEREO | AFMT_U8 | AFMT_S16_LE, + AFMT_STEREO | AFMT_S16_LE +}; - SNDCARD_SB, - sb_probe, - sb_attach, +static pcm_channel sb_chantemplate = { + sbchan_init, + sbchan_setdir, + sbchan_setformat, + sbchan_setspeed, + sbchan_setblocksize, + sbchan_trigger, + sbchan_getptr, + sbchan_getcaps, +}; - sb_dsp_open, - sb_dsp_close /* sb_close */, - NULL /* use generic sndread */, - NULL /* use generic sndwrite */, - sb_dsp_ioctl, - sndselect, +#define PLAIN_SB16(x) ((((x)->bd_flags) & (BD_F_SB16|BD_F_SB16X)) == BD_F_SB16) - sb_intr, - sb_callback, +struct sb_info; - DSP_BUFFSIZE, /* bufsize */ +struct sb_chinfo { + struct sb_info *parent; + pcm_channel *channel; + snd_dbuf *buffer; + int dir; + u_int32_t fmt; +}; - AFMT_STEREO | AFMT_U8, /* audio format */ +struct sb_info { + struct resource *io_base; /* I/O address for the board */ + int io_rid; + struct resource *irq; + int irq_rid; + struct resource *drq1; /* play */ + int drq1_rid; + struct resource *drq2; /* rec */ + int drq2_rid; + bus_dma_tag_t parent_dmat; + + int dma16, dma8; + int bd_id; + u_long bd_flags; /* board-specific flags */ + struct sb_chinfo pch, rch; +}; -} ; +static int sb_rd(struct sb_info *sb, int reg); +static void sb_wr(struct sb_info *sb, int reg, u_int8_t val); +static int sb_dspready(struct sb_info *sb); +static int sb_cmd(struct sb_info *sb, u_char val); +static int sb_cmd1(struct sb_info *sb, u_char cmd, int val); +static int sb_cmd2(struct sb_info *sb, u_char cmd, int val); +static u_int sb_get_byte(struct sb_info *sb); +static int ess_write(struct sb_info *sb, u_char reg, int val); +static int ess_read(struct sb_info *sb, u_char reg); /* - * Then the file continues with the body of all functions - * directly referenced in the descriptor. + * in the SB, there is a set of indirect "mixer" registers with + * address at offset 4, data at offset 5 */ +static void sb_setmixer(struct sb_info *sb, u_int port, u_int value); +static int sb_getmixer(struct sb_info *sb, u_int port); + +static void sb_intr(void *arg); +static int sb_init(device_t dev, struct sb_info *sb); +static int sb_reset_dsp(struct sb_info *sb); + +static int sb_format(struct sb_chinfo *ch, u_int32_t format); +static int sb_speed(struct sb_chinfo *ch, int speed); +static int sb_start(struct sb_chinfo *ch); +static int sb_stop(struct sb_chinfo *ch); + +static int sbmix_init(snd_mixer *m); +static int sbmix_set(snd_mixer *m, unsigned dev, unsigned left, unsigned right); +static int sbmix_setrecsrc(snd_mixer *m, u_int32_t src); + +static snd_mixer sb_mixer = { + "SoundBlaster mixer", + sbmix_init, + sbmix_set, + sbmix_setrecsrc, +}; + +static devclass_t pcm_devclass; /* - * the probe routine for the SoundBlaster only consists in - * resetting the dsp and testing if it is there. - * Version detection etc. will be done at attach time. + * Common code for the midi and pcm functions + * + * sb_cmd write a single byte to the CMD port. + * sb_cmd1 write a CMD + 1 byte arg + * sb_cmd2 write a CMD + 2 byte arg + * sb_get_byte returns a single byte from the DSP data port * - * Remember, ISA probe routines are supposed to return the - * size of io space used. + * ess_write is actually sb_cmd1 + * ess_read access ext. regs via sb_cmd(0xc0, reg) followed by sb_get_byte */ static int -sb_probe(struct isa_device *dev) +port_rd(struct resource *port, int off) { - bzero(&pcm_info[dev->id_unit], sizeof(pcm_info[dev->id_unit]) ); - if (dev->id_iobase == -1) { - dev->id_iobase = 0x220; - BVDDB(printf("sb_probe: no address supplied, try defaults (0x220,0x240)\n");) - if (snd_conflict(dev->id_iobase)) - dev->id_iobase = 0x240; - } - if (snd_conflict(dev->id_iobase)) - return 0 ; - - if (sb_reset_dsp(dev->id_iobase)) - return 16 ; /* the SB uses 16 registers... */ - else - return 0; + return bus_space_read_1(rman_get_bustag(port), + rman_get_bushandle(port), + off); +} + +static void +port_wr(struct resource *port, int off, u_int8_t data) +{ + return bus_space_write_1(rman_get_bustag(port), + rman_get_bushandle(port), + off, data); } static int -sb_attach(struct isa_device *dev) +sb_rd(struct sb_info *sb, int reg) { - snddev_info *d = &pcm_info[dev->id_unit] ; + return port_rd(sb->io_base, reg); +} - dev->id_alive = 16 ; /* number of io ports */ - /* should be already set but just in case... */ - sb_dsp_init(d, dev); - return 0 ; +static void +sb_wr(struct sb_info *sb, int reg, u_int8_t val) +{ + port_wr(sb->io_base, reg, val); } -/* - * here are the main routines from the switches. - */ +static int +sb_dspready(struct sb_info *sb) +{ + return ((sb_rd(sb, SBDSP_STATUS) & 0x80) == 0); +} -/* - * Unlike MSS, the sb only supports a single open (does not mean - * that only a single process is using it, since it can fork - * afterwards, or pass the descriptor to another process). - * - */ static int -sb_dsp_open(dev_t i_dev, int flags, int mode, struct proc * p) +sb_dspwr(struct sb_info *sb, u_char val) { - snddev_info *d; - int unit ; - int dev; + int i; - dev = minor(i_dev); - unit = dev >> 4 ; - d = &pcm_info[unit] ; + for (i = 0; i < 1000; i++) { + if (sb_dspready(sb)) { + sb_wr(sb, SBDSP_CMD, val); + return 1; + } + if (i > 10) DELAY((i > 100)? 1000 : 10); + } + printf("sb_dspwr(0x%02x) timed out.\n", val); + return 0; +} - DEB(printf("<%s>%d : open\n", d->name, unit)); +static int +sb_cmd(struct sb_info *sb, u_char val) +{ +#if 0 + printf("sb_cmd: %x\n", val); +#endif + return sb_dspwr(sb, val); +} - if (d->flags & SND_F_BUSY) { - DEB(printf("<%s>%d open: device busy\n", d->name, unit)); - return EBUSY ; - } +static int +sb_cmd1(struct sb_info *sb, u_char cmd, int val) +{ +#if 0 + printf("sb_cmd1: %x, %x\n", cmd, val); +#endif + if (sb_dspwr(sb, cmd)) { + return sb_dspwr(sb, val & 0xff); + } else return 0; +} - d->wsel.si_pid = 0; - d->wsel.si_flags = 0; +static int +sb_cmd2(struct sb_info *sb, u_char cmd, int val) +{ +#if 0 + printf("sb_cmd2: %x, %x\n", cmd, val); +#endif + if (sb_dspwr(sb, cmd)) { + return sb_dspwr(sb, val & 0xff) && + sb_dspwr(sb, (val >> 8) & 0xff); + } else return 0; +} - d->rsel.si_pid = 0; - d->rsel.si_flags = 0; +/* + * in the SB, there is a set of indirect "mixer" registers with + * address at offset 4, data at offset 5 + */ +static void +sb_setmixer(struct sb_info *sb, u_int port, u_int value) +{ + u_long flags; + + flags = spltty(); + sb_wr(sb, SB_MIX_ADDR, (u_char) (port & 0xff)); /* Select register */ + DELAY(10); + sb_wr(sb, SB_MIX_DATA, (u_char) (value & 0xff)); + DELAY(10); + splx(flags); +} - d->dbuf_out.total = d->dbuf_out.prev_total = 0 ; - d->dbuf_in.total = d->dbuf_in.prev_total = 0 ; +static int +sb_getmixer(struct sb_info *sb, u_int port) +{ + int val; + u_long flags; - d->flags = 0 ; - d->bd_flags &= ~BD_F_HISPEED ; + flags = spltty(); + sb_wr(sb, SB_MIX_ADDR, (u_char) (port & 0xff)); /* Select register */ + DELAY(10); + val = sb_rd(sb, SB_MIX_DATA); + DELAY(10); + splx(flags); - switch ( dev & 0xf ) { - case SND_DEV_DSP16 : - if ((d->audio_fmt & AFMT_S16_LE) == 0) { - printf("sorry, 16-bit not supported on SB %d.%02d\n", - (d->bd_id >>8) & 0xff, d->bd_id & 0xff); - return ENXIO; - } - d->play_fmt = d->rec_fmt = AFMT_S16_LE ; - break; - case SND_DEV_AUDIO : - d->play_fmt = d->rec_fmt = AFMT_MU_LAW ; - break ; - case SND_DEV_DSP : - d->play_fmt = d->rec_fmt = AFMT_U8 ; - break ; - } - /* - * since the SB is not simmetric, I use the open mode to select - * which channel should be privileged, and disable I/O in the - * other direction. - * In case the board is opened RW, we don't have enough - * information on what to do. Temporarily, privilege the - * playback channel, which is used more often, and set the other - * one to U8. - */ - if ( (flags & FREAD) == 0) /* opened write only */ - d->rec_fmt = 0 ; - else if ( (flags & FWRITE) == 0) /* opened read only */ - d->play_fmt = 0 ; - else /* opened read/write */ - d->rec_fmt = (d->play_fmt == AFMT_S16_LE) ? AFMT_U8 : AFMT_S16_LE ; - - d->flags |= SND_F_BUSY ; - d->play_speed = d->rec_speed = DSP_DEFAULT_SPEED ; - - if (flags & O_NONBLOCK) - d->flags |= SND_F_NBIO ; - - sb_reset_dsp(d->io_base); - if (d->bd_flags & BD_F_ESS) - sb_cmd(d->io_base, 0xc6 ); /* enable extended ESS mode */ - ask_init(d); - - return 0; + return val; } -static int -sb_dsp_close(dev_t i_dev, int flags, int mode, struct proc * p) +static u_int +sb_get_byte(struct sb_info *sb) { - int unit; - int dev; - snddev_info *d; - u_long s; + int i; - dev = minor(i_dev); - unit = dev >> 4 ; - d = &pcm_info[unit] ; - - s = spltty(); - d->flags |= SND_F_CLOSING ; - splx(s); - snd_flush(d); + for (i = 1000; i > 0; i--) { + if (sb_rd(sb, DSP_DATA_AVAIL) & 0x80) + return sb_rd(sb, DSP_READ); + else + DELAY(20); + } + return 0xffff; +} - sb_cmd(d->io_base, DSP_CMD_SPKOFF ); /* XXX useless ? */ +static int +ess_write(struct sb_info *sb, u_char reg, int val) +{ + return sb_cmd1(sb, reg, val); +} - d->flags = 0 ; - return 0 ; +static int +ess_read(struct sb_info *sb, u_char reg) +{ + return (sb_cmd(sb, 0xc0) && sb_cmd(sb, reg))? sb_get_byte(sb) : 0xffff; } static int -sb_dsp_ioctl(dev_t i_dev, u_long cmd, caddr_t arg, int mode, struct proc * p) +sb_reset_dsp(struct sb_info *sb) { - int unit; - int dev; - snddev_info *d; - - dev = minor(i_dev); - unit = dev >> 4 ; - d = &pcm_info[unit] ; - - /* - * handle mixer calls first. Reads are in the default handler, - * so do not bother about them. - */ - if ( (cmd & MIXER_WRITE(0)) == MIXER_WRITE(0) ) - return sb_mixer_set(d, cmd & 0xff, *(int *)arg) ; - - /* - * for the remaining functions, use the default handler. - * ENOSYS means that the default handler should take care - * of implementing the ioctl. - */ - - return ENOSYS ; + sb_wr(sb, SBDSP_RST, 3); + DELAY(100); + sb_wr(sb, SBDSP_RST, 0); + if (sb_get_byte(sb) != 0xAA) { + DEB(printf("sb_reset_dsp 0x%lx failed\n", + rman_get_start(d->io_base))); + return ENXIO; /* Sorry */ + } + if (sb->bd_flags & BD_F_ESS) sb_cmd(sb, 0xc6); + return 0; } static void -sb_intr(int unit) +sb_release_resources(struct sb_info *sb, device_t dev) { - snddev_info *d = &pcm_info[unit]; - int reason = 3, c=1, io_base = d->io_base; - - DEB(printf("got sb_intr for unit %d, flags 0x%08lx\n", unit, d->flags)); - - /* - * SB < 4.0 is half duplex and has only 1 bit for int source, - * so we fake it. SB 4.x (SB16) has the int source in a separate - * register. - * The Vibra16X has separate flags for 8 and 16 bit transfers, but - * I have no idea how to tell capture from playback interrupts... - */ -#define PLAIN_SB16(x) ( ( (x) & (BD_F_SB16|BD_F_SB16X) ) == BD_F_SB16) -again: - if (d->bd_flags & BD_F_SB16) { - c = sb_getmixer(io_base, IRQ_STAT); - /* this tells us if the source is 8-bit or 16-bit dma. We - * have to check the io channel to map it to read or write... - */ - reason = 0 ; - if ( c & 1 ) { /* 8-bit dma */ - if (d->play_fmt == AFMT_U8 || d->play_fmt == AFMT_MU_LAW ) - reason |= 1; - if (d->rec_fmt == AFMT_U8 || d->rec_fmt == AFMT_MU_LAW ) - reason |= 2; - } - if ( c & 2 ) { /* 16-bit dma */ - if (d->play_fmt == AFMT_S16_LE) - reason |= 1; - if (d->rec_fmt == AFMT_S16_LE) - reason |= 2; - } - } - /* XXX previous location of ack... */ - DEB(printf("sb_intr, flags 0x%08lx reason %d c 0x%x\n", - d->flags, reason, c)); - if ( reason & 1 ) { /* possibly a write interrupt */ - if ( d->dbuf_out.dl ) - dsp_wrintr(d); - } - if ( reason & 2 ) { - if ( d->dbuf_in.dl ) - dsp_rdintr(d); - } - if ( c & 2 ) - inb(DSP_DATA_AVL16); /* 16-bit int ack */ - if (c & 1) - inb(DSP_DATA_AVAIL); /* 8-bit int ack */ - - /* - * the sb16 might have multiple sources etc. - */ - if ((d->bd_flags & BD_F_SB16) && (c & 3)) - goto again; + /* should we bus_teardown_intr here? */ + if (sb->irq) { + bus_release_resource(dev, SYS_RES_IRQ, sb->irq_rid, sb->irq); + sb->irq = 0; + } + if (sb->drq1) { + bus_release_resource(dev, SYS_RES_DRQ, sb->drq1_rid, sb->drq1); + sb->drq1 = 0; + } + if (sb->drq2) { + bus_release_resource(dev, SYS_RES_DRQ, sb->drq2_rid, sb->drq2); + sb->drq2 = 0; + } + if (sb->io_base) { + bus_release_resource(dev, SYS_RES_IOPORT, sb->io_rid, + sb->io_base); + sb->io_base = 0; + } + free(sb, M_DEVBUF); } -/* - * device-specific function called back from the dma module. - * The reason of the callback is the second argument. - * NOTE: during operations, some ioctl can be called to change - * settings (e.g. speed, channels, format), and the default - * ioctl handler will just record the change and set the - * flag SND_F_INIT. The callback routine is in charge of applying - * the changes at the next convenient time (typically, at the - * start of operations). For full duplex devices, in some cases the - * init requires both channels to be idle. - */ static int -sb_callback(snddev_info *d, int reason) +sb_alloc_resources(struct sb_info *sb, device_t dev) { - int rd = reason & SND_CB_RD ; - snd_dbuf *b = (rd) ? & (d->dbuf_in) : & (d->dbuf_out) ; - int l = b->dl ; - - switch (reason & SND_CB_REASON_MASK) { - case SND_CB_INIT : /* called with int enabled and no pending io */ - /* - * set the speed - */ - dsp_speed(d); - /* - * set the desired DMA blocksize (influences select behaviour) - */ - snd_set_blocksize(d); - /* - * since native mulaw is not present, emulate it. - */ - if ( (d->play_fmt & AFMT_MU_LAW) || (d->rec_fmt & AFMT_MU_LAW) ) - d->flags |= SND_F_XLAT8 ; - else - d->flags &= ~SND_F_XLAT8 ; - - /* - * there are too many flavours of SB for my taste... here i try to do - * the proper initialization for each one. - */ - if (PLAIN_SB16(d->bd_flags)) { - - /* the original SB16 (non-PnP, or PnP, or Vibra16C) - * can do full duplex using one 16-bit channel - * and one 8-bit channel. It needs to be programmed to - * use split format though. - * I DON'T do this for the Vibra16X because I have no idea - * of what needs to be done there... - * - * I use the following algorithm: - * 1. check which direction(s) are active; - * 2. check if we should swap dma channels - * 3. check if we can do the swap. - */ - int swap = 1 ; /* default... */ - - if (d->play_fmt == 0) { - /* do whatever the read channel wants */ - if ( d->rec_fmt == AFMT_S16_LE && d->dbuf_in.chan > 4 ) - swap = 0; - if ( d->rec_fmt != AFMT_S16_LE && d->dbuf_in.chan < 4 ) - swap = 0; - } else { - /* privilege the write channel */ - if ( d->play_fmt == AFMT_S16_LE && d->dbuf_out.chan > 4 ) - swap = 0; - if ( d->play_fmt != AFMT_S16_LE && d->dbuf_out.chan < 4 ) - swap = 0; - if ( d->rec_fmt ) { - /* check for possible config errors. - * This cannot happen at open time since even in - * case of opening rw we privilege the play - * channel. - */ - if (d->rec_fmt == d->play_fmt) { - DDB(printf("sorry, read DMA channel unavailable\n")); - } + if (!sb->io_base) + sb->io_base = bus_alloc_resource(dev, SYS_RES_IOPORT, + &sb->io_rid, 0, ~0, 1, + RF_ACTIVE); + if (!sb->irq) + sb->irq = bus_alloc_resource(dev, SYS_RES_IRQ, + &sb->irq_rid, 0, ~0, 1, + RF_ACTIVE); + if (!sb->drq1) + sb->drq1 = bus_alloc_resource(dev, SYS_RES_DRQ, + &sb->drq1_rid, 0, ~0, 1, + RF_ACTIVE); + if (!sb->drq2 && sb->drq2_rid > 0) + sb->drq2 = bus_alloc_resource(dev, SYS_RES_DRQ, + &sb->drq2_rid, 0, ~0, 1, + RF_ACTIVE); + + if (sb->io_base && sb->drq1 && sb->irq) { + sb->dma8 = rman_get_start(sb->drq1); + isa_dma_acquire(sb->dma8); + isa_dmainit(sb->dma8, DSP_BUFFSIZE); + + if (sb->drq2) { + sb->dma16 = rman_get_start(sb->drq2); + isa_dma_acquire(sb->dma16); + isa_dmainit(sb->dma16, DSP_BUFFSIZE); + } else sb->dma16 = sb->dma8; + + if (sb->dma8 > sb->dma16) { + int tmp = sb->dma16; + sb->dma16 = sb->dma8; + sb->dma8 = tmp; } - } - DEB(printf("sb16: play_fmt %d, rec_fmt %x, swap %d\n", - d->play_fmt, d->rec_fmt, swap);) - if (swap) { - int c = d->dbuf_in.chan ; - d->dbuf_in.chan = d->dbuf_out.chan; - d->dbuf_out.chan = c ; - } - } - else if (d->bd_flags & BD_F_ESS) { - u_char c; + return 0; + } else return ENXIO; +} - DEB(printf("SND_CB_INIT, play_fmt == 0x%x, rec_fmt == 0x%x\n", - (int) d->play_fmt, (int) d->rec_fmt)); +static int +sb_identify_board(device_t dev, struct sb_info *sb) +{ + char *fmt = NULL; + static char buf[64]; - /* autoinit DMA mode */ - if (d->play_fmt) - ess_write(d->io_base, 0xb8, 0x04); - else - ess_write(d->io_base, 0xb8, 0x0e); - - c = (ess_read(d->io_base, 0xa8) & ~0x03) | 0x01; - if ((d->flags & SND_F_STEREO) == 0) - c++; - ess_write(d->io_base, 0xa8, c); /* select mono/stereo */ - ess_write(d->io_base, 0xb9, 2); /* demand 4 bytes/transfer */ - - switch (d->play_fmt ? d->play_fmt : d->rec_fmt) { - case AFMT_S16_LE: - if (d->flags & SND_F_STEREO) { - /* 16 bit stereo */ - if (d->play_fmt) - ess_write(d->io_base, 0xb6, 0x00); - ess_write(d->io_base, 0xb7, 0x71); - ess_write(d->io_base, 0xb7, 0xbc); - } - else { - /* 16 bit mono */ - if (d->play_fmt) - ess_write(d->io_base, 0xb6, 0x00); - ess_write(d->io_base, 0xb7, 0x71); - ess_write(d->io_base, 0xb7, 0xf4); - } - break; - case AFMT_U8: - if (d->flags & SND_F_STEREO) { - /* 8 bit stereo */ - if (d->play_fmt) - ess_write(d->io_base, 0xb6, 0x80); - ess_write(d->io_base, 0xb7, 0x51); - ess_write(d->io_base, 0xb7, 0x98); - } - else { - /* 8 bit mono */ - if (d->play_fmt) - ess_write(d->io_base, 0xb6, 0x80); - ess_write(d->io_base, 0xb7, 0x51); - ess_write(d->io_base, 0xb7, 0xd0); - } - break; - } - ess_write(d->io_base, 0xb1, - ess_read(d->io_base, 0xb1) | 0x50); - ess_write(d->io_base, 0xb2, - ess_read(d->io_base, 0xb1) | 0x50); - } - reset_dbuf(& (d->dbuf_in), SND_CHAN_RD ); - reset_dbuf(& (d->dbuf_out), SND_CHAN_WR ); - break ; - - case SND_CB_START : /* called with int disabled */ - if (d->bd_flags & BD_F_SB16) { - u_char c, c1 ; - - if (d->bd_flags & BD_F_SB16X) { - /* just a guess: on the Vibra16X, the first - * op started takes the first dma channel, - * the second one takes the next... - * The default is to be ready for play. - */ - DEB(printf("start %s -- now dma %d:%d\n", - rd ? "rd" : "wr", - d->dbuf_out.chan, d->dbuf_in.chan);); - /* swap only if both channels are idle - * play: dl=0, since there is no pause; - * rec: rl=0 - */ - if ( rd && d->dbuf_out.dl == 0 && d->dbuf_in.rl == 0 ) { - /* must swap channels, but also save dl */ - int c = d->dbuf_in.chan ; - int dl = d->dbuf_in.dl ; - d->dbuf_in.chan = d->dbuf_out.chan; - d->dbuf_out.chan = c ; - reset_dbuf(& (d->dbuf_in), SND_CHAN_RD ); - reset_dbuf(& (d->dbuf_out), SND_CHAN_WR ); - d->dbuf_in.dl = dl ; - printf("swapped -- now dma %d:%d\n", - d->dbuf_out.chan, d->dbuf_in.chan); - } - } - - /* - * XXX note: c1 and l should be set basing on d->rec_fmt, - * but there is no choice once a 16 or 8-bit channel - * is assigned. This means that if the application - * tries to use a bad format, the sound will not be nice. - */ - if ( b->chan > 4 - || (rd && d->rec_fmt == AFMT_S16_LE) - || (!rd && d->play_fmt == AFMT_S16_LE) - ) { - c = DSP_F16_AUTO | DSP_F16_FIFO_ON | DSP_DMA16 ; - c1 = DSP_F16_SIGNED ; - l /= 2 ; - } else { - c = DSP_F16_AUTO | DSP_F16_FIFO_ON | DSP_DMA8 ; - c1 = 0 ; - } - c |= (rd) ? DSP_F16_ADC : DSP_F16_DAC ; - if (d->flags & SND_F_STEREO) - c1 |= DSP_F16_STEREO ; - - sb_cmd(d->io_base, c ); - sb_cmd3(d->io_base, c1 , l - 1) ; - } else if (d->bd_flags & BD_F_ESS) { - u_long fmt = rd ? d->rec_fmt : d->play_fmt; - - DEB(printf("SND_CB_START: %s (%d)\n", rd ? "rd" : "wr", l)); - if (fmt == AFMT_S16_LE) - l >>= 1; - l--; - if (!rd) - sb_cmd(d->io_base, DSP_CMD_SPKON); - ess_write(d->io_base, 0xa4, l); - ess_write(d->io_base, 0xa5, l >> 8); - ess_write(d->io_base, 0xb8, - ess_read(d->io_base, 0xb8) | (rd ? 0x0f : 0x05)); - } else { /* SBPro -- stereo not supported */ - u_char c ; - if (!rd) - sb_cmd(d->io_base, DSP_CMD_SPKON); - /* code for the SB2 and SB3, only MONO */ - if (d->bd_flags & BD_F_HISPEED) - c = (rd) ? 0x98 : 0x90 ; - else - c = (rd) ? 0x2c : 0x1c ; - if (d->flags & SND_F_STEREO) - sb_setmixer(d->io_base, 0xe, 2 ); - else - sb_setmixer(d->io_base, 0xe, 0 ); - /* - * some ESS extensions -- they can do 16 bits - */ - if ( (rd && d->rec_fmt == AFMT_S16_LE) || - (!rd && d->play_fmt == AFMT_S16_LE) ) { - c |= 1; - l /= 2 ; - } - sb_cmd3(d->io_base, 0x48 , l - 1) ; - sb_cmd(d->io_base, c ) ; - } - break; - - case SND_CB_ABORT : /* XXX */ - case SND_CB_STOP : - { - int cmd = DSP_CMD_DMAPAUSE_8 ; /* default: halt 8 bit chan */ - DEB(printf("SND_CB_XXX: reason 0x%x\n", reason)); - if ( b->chan > 4 - || (rd && d->rec_fmt == AFMT_S16_LE) - || (!rd && d->play_fmt == AFMT_S16_LE) - ) - cmd = DSP_CMD_DMAPAUSE_16 ; - if (d->bd_flags & BD_F_HISPEED) { - sb_reset_dsp(d->io_base); - if (d->bd_flags & BD_F_ESS) - sb_cmd(d->io_base, 0xc6 ); /* enable extended ESS mode */ - d->flags |= SND_F_INIT ; - } else { - sb_cmd(d->io_base, cmd); /* pause dma. */ - /* - * The above seems to have the undocumented side effect of - * blocking the other side as well. If the other - * channel was active (SB16) I have to re-enable it :( - */ - if ( (rd && d->dbuf_out.dl) || - (!rd && d->dbuf_in.dl) ) - sb_cmd(d->io_base, cmd == DSP_CMD_DMAPAUSE_8 ? - 0xd6 : 0xd4); /* continue other dma */ - } - if (d->bd_flags & BD_F_SB16X) { - /* restore possible swapped channels. - * The default is to be ready for play. - * XXX right now, it kills all input on overflow - */ - if ( rd && d->dbuf_out.dl == 0 ) { - /* must swap channels ? */ - int c = d->dbuf_in.chan ; - d->dbuf_in.chan = d->dbuf_out.chan; - d->dbuf_out.chan = c ; - reset_dbuf(& (d->dbuf_in), SND_CHAN_RD ); - reset_dbuf(& (d->dbuf_out), SND_CHAN_WR ); - printf("restored -- now dma %d:%d\n", - d->dbuf_out.chan, d->dbuf_in.chan); - } - } - } - DEB( sb_cmd(d->io_base, DSP_CMD_SPKOFF) ); /* speaker off */ - break ; + sb_cmd(sb, DSP_CMD_GETVER); /* Get version */ + sb->bd_id = (sb_get_byte(sb) << 8) | sb_get_byte(sb); - } - return 0 ; -} + switch (sb->bd_id >> 8) { + case 1: /* old sound blaster has nothing... */ + case 2: + fmt = "SoundBlaster %d.%d" ; /* default */ + break; -/* - * The second part of the file contains all functions specific to - * the board and (usually) not exported to other modules. - */ + case 3: + fmt = "SoundBlaster Pro %d.%d"; + if (sb->bd_id == 0x301) { + int essver, rev; + + /* Try to detect ESS chips. */ + sb_cmd(sb, DSP_CMD_GETID); /* Return ident. bytes. */ + essver = (sb_get_byte(sb) << 8) | sb_get_byte(sb); + rev = essver & 0x000f; + essver &= 0xfff0; + if (essver == 0x4880) { + /* the ESS488 can be treated as an SBPRO */ + fmt = "SoundBlaster Pro (ESS488 rev %d)"; + } else if (essver == 0x6880) { + if (rev < 8) fmt = "SoundBlaster Pro (ESS688 rev %d)"; + else fmt = "SoundBlaster Pro (ESS1868 rev %d)"; + sb->bd_flags |= BD_F_ESS; + } else return ENXIO; + sb->bd_id &= 0xff00; + sb->bd_id |= ((essver & 0xf000) >> 8) | rev; + } + break; -int -sb_reset_dsp(int io_base) -{ - int loopc; - - outb(io_base + SBDSP_RST, 3); - DELAY(100); - outb(io_base + SBDSP_RST, 0); - for (loopc = 0; loopc<100 && !(inb(DSP_DATA_AVAIL) & 0x80); loopc++) - DELAY(30); - - if (inb(DSP_READ) != 0xAA) { - DEB(printf("sb_reset_dsp 0x%x failed\n", io_base)); - return 0; /* Sorry */ - } - return 1; -} + case 4: + sb->bd_flags |= BD_F_SB16; + fmt = "SoundBlaster 16 %d.%d"; + break; -/* - * only used in sb_attach from here. - */ + default: + device_printf(dev, "failed to get SB version (%x)\n", + sb->bd_id); + return ENXIO; + + } + if ((sb->bd_id >> 8) <= 4) snprintf(buf, sizeof buf, fmt, + sb->bd_id >> 8, sb->bd_id & 0xff); + else snprintf(buf, sizeof buf, fmt, sb->bd_id & 0x000f); + device_set_desc_copy(dev, buf); + return sb_reset_dsp(sb); +} -static void -sb_dsp_init(snddev_info *d, struct isa_device *dev) +static int +sb_init(device_t dev, struct sb_info *sb) { - int i, x; - char *fmt = NULL ; - int io_base = dev->id_iobase ; + int x, irq; - d->bd_id = 0 ; + sb->bd_flags &= ~BD_F_MIX_MASK; + /* do various initializations depending on board id. */ + switch (sb->bd_id >> 8) { + case 1: /* old sound blaster has nothing... */ + break; - sb_reset_dsp(io_base); - sb_cmd(io_base, DSP_CMD_GETVER); /* Get version */ + case 2: + sb->bd_flags |= BD_F_DUP_MIDI; + if (sb->bd_id > 0x200) sb->bd_flags |= BD_F_MIX_CT1335; + break; - for (i = 10000; i; i--) { /* perhaps wait longer on a fast machine ? */ - if (inb(DSP_DATA_AVAIL) & 0x80) { /* wait for Data Ready */ - if ( (d->bd_id & 0xff00) == 0) - d->bd_id = inb(DSP_READ) << 8; /* major */ - else { - d->bd_id |= inb(DSP_READ); /* minor */ + case 3: + sb->bd_flags |= BD_F_DUP_MIDI | BD_F_MIX_CT1345; break; - } - } else - DELAY(20); - } - - /* - * now do various initializations depending on board id. - */ - - fmt = "SoundBlaster %d.%d" ; /* default */ - - switch ( d->bd_id >> 8 ) { - case 0 : - printf("\n\nFailed to get SB version (%x) - possible I/O conflict\n\n", - inb(DSP_DATA_AVAIL)); - d->bd_id = 0x100; - case 1 : /* old sound blaster has nothing... */ - break ; - - case 2 : - d->dbuf_in.chan = d->dbuf_out.chan ; /* half duplex */ - d->bd_flags |= BD_F_DUP_MIDI ; - - if (d->bd_id == 0x200) - break ; /* no mixer on the 2.0 */ - d->bd_flags &= ~BD_F_MIX_MASK ; - d->bd_flags |= BD_F_MIX_CT1335 ; - - break ; - case 4 : - fmt = "SoundBlaster 16 %d.%d"; - d->audio_fmt |= AFMT_FULLDUPLEX | AFMT_WEIRD | AFMT_S8 | AFMT_S16_LE; - d->bd_flags |= BD_F_SB16; - d->bd_flags &= ~BD_F_MIX_MASK ; - d->bd_flags |= BD_F_MIX_CT1745 ; - - /* soft irq/dma configuration */ - x = -1 ; - if (d->irq == 5) x = 2; - else if (d->irq == 7) x = 4; - else if (d->irq == 9) x = 1; - else if (d->irq == 10) x = 8; - if (x == -1) - printf("<%s>%d: bad irq %d (only 5,7,9,10 allowed)\n", - d->name, dev->id_unit, d->irq); - else - sb_setmixer(io_base, IRQ_NR, x); - if (d->dbuf_out.chan == d->dbuf_in.chan) { - printf("WARNING: sb: misconfigured secondary DMA channel\n"); - } - sb_setmixer(io_base, DMA_NR, (1 << d->dbuf_out.chan) | (1 << d->dbuf_in.chan)); - break ; - - case 3 : - d->dbuf_in.chan = d->dbuf_out.chan ; /* half duplex */ - fmt = "SoundBlaster Pro %d.%d"; - d->bd_flags |= BD_F_DUP_MIDI ; - d->bd_flags &= ~BD_F_MIX_MASK ; - d->bd_flags |= BD_F_MIX_CT1345 ; - if (d->bd_id == 0x301) { - int ess_major = 0, ess_minor = 0; - - /* - * Try to detect ESS chips. - */ - - sb_cmd(io_base, DSP_CMD_GETID); /* Return ident. bytes. */ - - for (i = 1000; i; i--) { - if (inb(DSP_DATA_AVAIL) & 0x80) { /* wait for Data Ready */ - if (ess_major == 0) - ess_major = inb(DSP_READ); - else { - ess_minor = inb(DSP_READ); - break; - } - } else - DELAY(20); - } - - if (ess_major == 0x48 && (ess_minor & 0xf0) == 0x80) { - /* the ESS488 can be treated as an SBPRO */ - printf("ESS488 (rev %d)\n", ess_minor & 0x0f); - break ; - } - else if (ess_major == 0x68 && (ess_minor & 0xf0) == 0x80) { - int rev = ess_minor & 0xf; - - if (rev >= 8) - printf("ESS1868 (rev %d)\n", rev); - else - printf("ESS688 (rev %d)\n", rev); - d->bd_flags |= BD_F_ESS; - d->audio_fmt |= AFMT_S16_LE; - - /* enable extended ESS mode */ - sb_cmd(d->io_base, 0xc6); - break; - } else { - printf("Unknown card 0x%x 0x%x -- hope it is SBPRO\n", - ess_major, ess_minor); - break ; - } - } - } + case 4: + sb->bd_flags |= BD_F_SB16 | BD_F_MIX_CT1745; + if (sb->dma16 != sb->dma8) sb->bd_flags |= BD_F_DUPLEX; + + /* soft irq/dma configuration */ + x = -1; + irq = rman_get_start(sb->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(sb, IRQ_NR, x); + sb_setmixer(sb, DMA_NR, (1 << sb->dma16) | (1 << sb->dma8)); + break; + } + return 0; +} - snprintf(d->name, sizeof(d->name), - fmt, (d->bd_id >> 8) &0xff, d->bd_id & 0xff); +static int +sb_probe(device_t dev) +{ + snddev_info *d = device_get_softc(dev); + struct sb_info *sb; + int allocated, i; + int error; + + if (isa_get_vendorid(dev)) return ENXIO; /* not yet */ + + device_set_desc(dev, "SoundBlaster"); + bzero(d, sizeof *d); + sb = (struct sb_info *)malloc(sizeof *sb, M_DEVBUF, M_NOWAIT); + if (!sb) return ENXIO; + bzero(sb, sizeof *sb); + + allocated = 0; + sb->io_rid = 0; + sb->io_base = bus_alloc_resource(dev, SYS_RES_IOPORT, &sb->io_rid, + 0, ~0, 16, RF_ACTIVE); + if (!sb->io_base) { + BVDDB(printf("sb_probe: no addr, trying (0x220, 0x240)\n")); + allocated = 1; + sb->io_rid = 0; + sb->io_base = bus_alloc_resource(dev, SYS_RES_IOPORT, + &sb->io_rid, 0x220, 0x22f, + 16, RF_ACTIVE); + if (!sb->io_base) { + sb->io_base = bus_alloc_resource(dev, SYS_RES_IOPORT, + &sb->io_rid, 0x240, + 0x24f, 16, RF_ACTIVE); + } + } + if (!sb->io_base) return ENXIO; + + error = sb_reset_dsp(sb); + if (error) goto no; + error = sb_identify_board(dev, sb); + if (error) goto no; +no: + i = sb->io_rid; + sb_release_resources(sb, dev); + if (allocated) ISA_DELETE_RESOURCE(device_get_parent(dev), dev, + SYS_RES_IOPORT, i); + return error; +} - sb_mix_init(d); +static int +sb_doattach(device_t dev, struct sb_info *sb) +{ + snddev_info *d = device_get_softc(dev); + void *ih; + int error; + char status[SND_STATUSLEN]; + + sb->irq_rid = 0; + sb->drq1_rid = 0; + sb->drq2_rid = 1; + if (sb_alloc_resources(sb, dev)) goto no; + error = sb_reset_dsp(sb); + if (error) goto no; + error = sb_identify_board(dev, sb); + if (error) goto no; + + sb_init(dev, sb); + mixer_init(d, &sb_mixer, sb); + bus_setup_intr(dev, sb->irq, INTR_TYPE_TTY, sb_intr, sb, &ih); + + if (sb->bd_flags & BD_F_SB16) + pcm_setflags(dev, pcm_getflags(dev) | SD_F_EVILSB16); + if (sb->dma16 == sb->dma8) + pcm_setflags(dev, pcm_getflags(dev) | SD_F_SIMPLEX); + if (bus_dma_tag_create(/*parent*/NULL, /*alignment*/2, /*boundary*/0, + /*lowaddr*/BUS_SPACE_MAXADDR_24BIT, + /*highaddr*/BUS_SPACE_MAXADDR, + /*filter*/NULL, /*filterarg*/NULL, + /*maxsize*/DSP_BUFFSIZE, /*nsegments*/1, + /*maxsegz*/0x3ffff, + /*flags*/0, &sb->parent_dmat) != 0) { + device_printf(dev, "unable to create dma tag\n"); + goto no; + } + + snprintf(status, SND_STATUSLEN, "at io 0x%lx irq %ld drq %d", + rman_get_start(sb->io_base), rman_get_start(sb->irq), + sb->dma8); + if (sb->dma16 != sb->dma8) snprintf(status + strlen(status), + SND_STATUSLEN - strlen(status), ":%d", sb->dma16); + + if (pcm_register(dev, sb, 1, 1)) goto no; + pcm_addchan(dev, PCMDIR_REC, &sb_chantemplate, sb); + pcm_addchan(dev, PCMDIR_PLAY, &sb_chantemplate, sb); + pcm_setstatus(dev, status); + + return 0; + +no: + sb_release_resources(sb, dev); + return ENXIO; } -static void -sb_mix_init(snddev_info *d) +static int +sb_attach(device_t dev) { - switch (d->bd_flags & BD_F_MIX_MASK) { - case BD_F_MIX_CT1345 : /* SB 3.0 has 1345 mixer */ - - d->mix_devs = SBPRO_MIXER_DEVICES ; - d->mix_rec_devs = SBPRO_RECORDING_DEVICES ; - d->mix_recsrc = SOUND_MASK_MIC ; - - sb_setmixer(d->io_base, 0, 1 ); /* reset mixer */ - sb_setmixer(d->io_base, MIC_VOL , 0x6 ); /* mic volume max */ - sb_setmixer(d->io_base, RECORD_SRC , 0x0 ); /* mic source */ - sb_setmixer(d->io_base, FM_VOL , 0x0 ); /* no midi */ - break ; - - case BD_F_MIX_CT1745 : /* SB16 mixer ... */ - - d->mix_devs = SB16_MIXER_DEVICES ; - d->mix_rec_devs = SB16_RECORDING_DEVICES ; - d->mix_recsrc = SOUND_MASK_MIC ; - } - sb_mixer_reset(d); + struct sb_info *sb; + int flags = isa_get_flags(dev); + + if (flags & DV_F_DUAL_DMA) { + ISA_SET_RESOURCE(device_get_parent(dev), dev, SYS_RES_DRQ, 1, + flags & DV_F_DRQ_MASK, 1); + } + sb = (struct sb_info *)malloc(sizeof *sb, M_DEVBUF, M_NOWAIT); + if (!sb) return ENXIO; + bzero(sb, sizeof *sb); + + /* XXX in probe should set io resource to right val instead of this */ + sb->io_rid = 0; + sb->io_base = bus_alloc_resource(dev, SYS_RES_IOPORT, &sb->io_rid, + 0, ~0, 16, RF_ACTIVE); + if (!sb->io_base) { + BVDDB(printf("sb_probe: no addr, trying (0x220, 0x240)\n")); + sb->io_rid = 0; + sb->io_base = bus_alloc_resource(dev, SYS_RES_IOPORT, + &sb->io_rid, 0x220, 0x22f, + 16, RF_ACTIVE); + if (!sb->io_base) { + sb->io_base = bus_alloc_resource(dev, SYS_RES_IOPORT, + &sb->io_rid, 0x240, + 0x24f, 16, RF_ACTIVE); + } + } + if (!sb->io_base) return ENXIO; + + return sb_doattach(dev, sb); } -/* - * Common code for the midi and pcm functions - * - * sb_cmd write a single byte to the CMD port. - * sb_cmd2 write a CMD + 1 byte arg - * sb_cmd3 write a CMD + 2 byte arg - * sb_get_byte returns a single byte from the DSP data port - * - * ess_write is actually sb_cmd2 - * ess_read access ext. regs via sb_cmd(0xc0, reg) followed by sb_get_byte - */ +static device_method_t sb_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, sb_probe), + DEVMETHOD(device_attach, sb_attach), -int -sb_cmd(int io_base, u_char val) -{ - int i; + { 0, 0 } +}; - for (i = 0; i < 1000 ; i++) { - if ((inb(io_base + SBDSP_STATUS) & 0x80) == 0) { - outb(io_base + SBDSP_CMD, val); - return 1; - } - if (i > 10) - DELAY (i > 100 ? 1000 : 10 ); - } +static driver_t sb_driver = { + "pcm", + sb_methods, + sizeof(snddev_info), +}; + +DRIVER_MODULE(sb, isa, sb_driver, pcm_devclass, 0, 0); - printf("SoundBlaster: DSP Command(0x%02x) timeout. IRQ conflict ?\n", val); - return 0; +static void +sb_intr(void *arg) +{ + struct sb_info *sb = (struct sb_info *)arg; + int reason = 3, c; + + /* + * SB < 4.0 is half duplex and has only 1 bit for int source, + * so we fake it. SB 4.x (SB16) has the int source in a separate + * register. + * The Vibra16X has separate flags for 8 and 16 bit transfers, but + * I have no idea how to tell capture from playback interrupts... + */ + if (sb->bd_flags & BD_F_SB16) { + c = sb_getmixer(sb, IRQ_STAT); + /* this tells us if the source is 8-bit or 16-bit dma. We + * have to check the io channel to map it to read or write... + */ + reason = 0; + if (c & 1) { /* 8-bit dma */ + if (sb->pch.fmt & AFMT_U8) reason |= 1; + if (sb->rch.fmt & AFMT_U8) reason |= 2; + } + if (c & 2) { /* 16-bit dma */ + if (sb->pch.fmt & AFMT_S16_LE) reason |= 1; + if (sb->rch.fmt & AFMT_S16_LE) reason |= 2; + } + } else c = 1; +#if 0 + printf("sb_intr: reason=%d c=0x%x\n", reason, c); +#endif + if ((reason & 1) && (sb->pch.buffer->dl > 0)) + chn_intr(sb->pch.channel); + if ((reason & 2) && (sb->rch.buffer->dl > 0)) + chn_intr(sb->rch.channel); + if (c & 1) sb_rd(sb, DSP_DATA_AVAIL); /* 8-bit int ack */ + if (c & 2) sb_rd(sb, DSP_DATA_AVL16); /* 16-bit int ack */ } -int -sb_cmd3(int io_base, u_char cmd, int val) +static int +sb_format(struct sb_chinfo *ch, u_int32_t format) { - if (sb_cmd(io_base, cmd)) { - sb_cmd(io_base, val & 0xff ); - sb_cmd(io_base, (val>>8) & 0xff ); - return 1 ; - } else + struct sb_info *sb = ch->parent; + ch->fmt = format; + if (sb->bd_flags & BD_F_ESS) { + int play = (ch->dir == PCMDIR_PLAY)? 1 : 0; + int b16 = (ch->fmt & AFMT_S16_LE)? 1 : 0; + int stereo = (ch->fmt & AFMT_STEREO)? 1 : 0; + + /* autoinit DMA mode */ + ess_write(sb, 0xb8, 0x04 | play? 0x00 : 0x0a); + /* mono/stereo */ + ess_write(sb, 0xa8, + (ess_read(sb, 0xa8) & ~0x03) | stereo? 0x01 : 0x02); + /* demand mode, 4 bytes/xfer */ + ess_write(sb, 0xb9, 2); + /* setup dac/adc */ + if (play) ess_write(sb, 0xb6, b16? 0x00 : 0x80); + ess_write(sb, 0xb7, 0x51 | (b16? 0x20 : 0x00)); + ess_write(sb, 0xb7, + 0x98 + (b16? 0x24 : 0x00) + (stereo? 0x00 : 0x38)); + /* irq/drq control */ + ess_write(sb, 0xb1, ess_read(sb, 0xb1) | 0x50); + ess_write(sb, 0xb2, ess_read(sb, 0xb1) | 0x50); + } return 0; } -int -sb_cmd2(int io_base, u_char cmd, int val) +static int +sb_speed(struct sb_chinfo *ch, int speed) { - if (sb_cmd(io_base, cmd)) { - sb_cmd(io_base, val & 0xff ); - return 1 ; - } else - return 0; + struct sb_info *sb = ch->parent; + int play = (ch->dir == PCMDIR_PLAY)? 1 : 0; + int stereo = (ch->fmt & AFMT_STEREO)? 1 : 0; + + if (sb->bd_flags & BD_F_SB16) { + RANGE(speed, 5000, 45000); + sb_cmd(sb, 0x42 - play); + sb_cmd(sb, speed >> 8); + sb_cmd(sb, speed & 0xff); + } else if (sb->bd_flags & BD_F_ESS) { + int t; + RANGE(speed, 5000, 49000); + if (speed > 22000) { + t = (795500 + speed / 2) / speed; + speed = (795500 + t / 2) / t; + t = (256 - t) | 0x80; + } else { + t = (397700 + speed / 2) / speed; + speed = (397700 + t / 2) / t; + t = 128 - t; + } + ess_write(sb, 0xa1, t); /* set time constant */ + t = 256 - 7160000 / (((speed * 9) / 20) * 82); + ess_write(sb, 0xa2, t); + } else { + u_char tconst; + int max_speed = 45000, tmp; + u_long flags; + + /* here enforce speed limitations - max 22050 on sb 1.x*/ + if (sb->bd_id <= 0x200) max_speed = 22050; + + /* + * SB models earlier than SB Pro have low limit for the + * input rate. Note that this is only for input, but since + * we do not support separate values for rec & play.... + */ + if (!play) { + if (sb->bd_id <= 0x200) max_speed = 13000; + else if (sb->bd_id < 0x300) max_speed = 15000; + } + RANGE(speed, 4000, max_speed); + if (stereo) speed <<= 1; + + /* + * Now the speed should be valid. Compute the value to be + * programmed into the board. + */ + if (speed > 22050) { /* High speed mode on 2.01/3.xx */ + tconst = (u_char) + ((65536 - ((256000000 + speed / 2) / speed)) + >> 8); + sb->bd_flags |= BD_F_HISPEED; + tmp = 65536 - (tconst << 8); + speed = (256000000 + tmp / 2) / tmp; + } else { + sb->bd_flags &= ~BD_F_HISPEED; + tconst = (256 - ((1000000 + speed / 2) / speed)) & 0xff; + tmp = 256 - tconst; + speed = (1000000 + tmp / 2) / tmp; + } + flags = spltty(); + sb_cmd1(sb, 0x40, tconst); /* set time constant */ + splx(flags); + if (stereo) speed >>= 1; + } + return speed; } -/* - * in the SB, there is a set of indirect "mixer" registers with - * address at offset 4, data at offset 5 - */ -void -sb_setmixer(int io_base, u_int port, u_int value) +static int +sb_start(struct sb_chinfo *ch) { - u_long flags; - - flags = spltty(); - outb(io_base + SB_MIX_ADDR, (u_char) (port & 0xff)); /* Select register */ - DELAY(10); - outb(io_base + SB_MIX_DATA, (u_char) (value & 0xff)); - DELAY(10); - splx(flags); + struct sb_info *sb = ch->parent; + int play = (ch->dir == PCMDIR_PLAY)? 1 : 0; + int b16 = (ch->fmt & AFMT_S16_LE)? 1 : 0; + int stereo = (ch->fmt & AFMT_STEREO)? 1 : 0; + int l = ch->buffer->dl; + u_char i1, i2 = 0; + + if (b16) l >>= 1; + l--; + if (play) sb_cmd(sb, DSP_CMD_SPKON); + if (sb->bd_flags & BD_F_SB16) { + i1 = DSP_F16_AUTO | DSP_F16_FIFO_ON | + (play? DSP_F16_DAC : DSP_F16_ADC); + i1 |= (b16 && (sb->bd_flags & BD_F_DUPLEX))? DSP_DMA16 : DSP_DMA8; + i2 = (stereo? DSP_F16_STEREO : 0) | (b16? DSP_F16_SIGNED : 0); + sb_cmd(sb, i1); + sb_cmd2(sb, i2, l); + } else if (sb->bd_flags & BD_F_ESS) { + ess_write(sb, 0xa4, l); + ess_write(sb, 0xa5, l >> 8); + ess_write(sb, 0xb8, ess_read(sb, 0xb8) | (play? 0x05 : 0x0f)); + } else { + if (sb->bd_flags & BD_F_HISPEED) i1 = play? 0x90 : 0x98; + else i1 = play? 0x1c : 0x2c; + sb_setmixer(sb, 0x0e, stereo? 2 : 0); + /* an ESS extension -- they can do 16 bits */ + if (b16) i1 |= 1; + sb_cmd2(sb, 0x48, l); + sb_cmd(sb, i1); + } + sb->bd_flags |= BD_F_DMARUN << b16; + return 0; } -int -sb_getmixer(int io_base, u_int port) -{ - int val; - u_long flags; - - flags = spltty(); - outb(io_base + SB_MIX_ADDR, (u_char) (port & 0xff)); /* Select register */ - DELAY(10); - val = inb(io_base + SB_MIX_DATA); - DELAY(10); - splx(flags); - - return val; -} - -u_int -sb_get_byte(int io_base) +static int +sb_stop(struct sb_chinfo *ch) { - int i; + struct sb_info *sb = ch->parent; + int play = (ch->dir == PCMDIR_PLAY)? 1 : 0; + int b16 = (ch->fmt & AFMT_S16_LE)? 1 : 0; - for (i = 1000; i; i--) - if (inb(DSP_DATA_AVAIL) & 0x80) - return inb(DSP_READ); - else - DELAY(20); - return 0xffff; + if (sb->bd_flags & BD_F_HISPEED) sb_reset_dsp(sb); + else { + sb_cmd(sb, b16? DSP_CMD_DMAPAUSE_16 : DSP_CMD_DMAPAUSE_8); + /* + * The above seems to have the undocumented side effect of + * blocking the other side as well. If the other + * channel was active (SB16) I have to re-enable it :( + */ + if (sb->bd_flags & (BD_F_DMARUN << (1 - b16))) + sb_cmd(sb, b16? 0xd4 : 0xd6 ); + } + if (play) sb_cmd(sb, DSP_CMD_SPKOFF); /* speaker off */ + sb->bd_flags &= ~(BD_F_DMARUN << b16); + return 0; } -int -ess_write(int io_base, u_char reg, int val) +/* channel interface */ +static void * +sbchan_init(void *devinfo, snd_dbuf *b, pcm_channel *c, int dir) { - return sb_cmd2(io_base, reg, val); + struct sb_info *sb = devinfo; + struct sb_chinfo *ch = (dir == PCMDIR_PLAY)? &sb->pch : &sb->rch; + + ch->parent = sb; + ch->channel = c; + ch->buffer = b; + ch->buffer->bufsize = DSP_BUFFSIZE; + if (chn_allocbuf(ch->buffer, sb->parent_dmat) == -1) return NULL; + ch->buffer->chan = (dir == PCMDIR_PLAY)? sb->dma16 : sb->dma8; + return ch; } -int -ess_read(int io_base, u_char reg) +static int +sbchan_setdir(void *data, int dir) { - if (!sb_cmd(io_base, 0xc0) || !sb_cmd(io_base, reg) ) - return 0xffff ; - return sb_get_byte(io_base); + struct sb_chinfo *ch = data; + ch->dir = dir; + return 0; } +static int +sbchan_setformat(void *data, u_int32_t format) +{ + struct sb_chinfo *ch = data; + sb_format(ch, format); + return 0; +} -/* - * various utility functions for the DSP - */ +static int +sbchan_setspeed(void *data, u_int32_t speed) +{ + struct sb_chinfo *ch = data; + return sb_speed(ch, speed); +} -/* - * dsp_speed updates the speed setting from the descriptor. make sure - * it is called at spltty(). - * Besides, it takes care of stereo setting. - */ static int -dsp_speed(snddev_info *d) +sbchan_setblocksize(void *data, u_int32_t blocksize) { - u_char tconst; - u_long flags; - int max_speed = 44100, speed = d->play_speed ; - - /* - * special code for the SB16 - */ - if (d->bd_flags & BD_F_SB16) { - RANGE (speed, 5000, 45000); - d->play_speed = d->rec_speed = speed ; - sb_cmd(d->io_base, 0x41); - sb_cmd(d->io_base, d->play_speed >> 8 ); - sb_cmd(d->io_base, d->play_speed & 0xff ); - sb_cmd(d->io_base, 0x42); - sb_cmd(d->io_base, d->rec_speed >> 8 ); - sb_cmd(d->io_base, d->rec_speed & 0xff ); - return speed ; - } - - /* - * special code for the ESS ... - */ - if (d->bd_flags & BD_F_ESS) { - int t; - RANGE (speed, 5000, 49000); - if (speed > 22000) { - t = (795500 + speed / 2) / speed; - speed = (795500 + t / 2) / t ; - t = (256 - t ) | 0x80 ; - } else { - t = (397700 + speed / 2) / speed; - speed = (397700 + t / 2) / t ; - t = 128 - t ; - } - ess_write(d->io_base, 0xa1, t); /* set time constant */ - d->play_speed = d->rec_speed = speed ; - speed = (speed * 9 ) / 20 ; - t = 256-7160000/(speed*82); - ess_write(d->io_base,0xa2,t); - return speed ; - } - - /* - * This is code for the SB3.x and lower. - * Only some models can do stereo, and only if not - * simultaneously using midi. - * At the moment we do not support either... - */ -#if 0 - d->flags &= ~SND_F_STEREO; -#endif + return blocksize; +} - /* - * here enforce speed limitations. - */ - if (d->bd_id <= 0x200) - max_speed = 22050; /* max 22050 on SB 1.X */ +static int +sbchan_trigger(void *data, int go) +{ + struct sb_chinfo *ch = data; + buf_isadma(ch->buffer, go); + if (go == PCMTRIG_START) sb_start(ch); else sb_stop(ch); + return 0; +} - /* - * SB models earlier than SB Pro have low limit for the - * input rate. Note that this is only for input, but since - * we do not support separate values for rec & play.... - */ - if (d->bd_id <= 0x200) - max_speed = 13000; - else if (d->bd_id < 0x300) - max_speed = 15000; +static int +sbchan_getptr(void *data) +{ + struct sb_chinfo *ch = data; + return buf_isadmaptr(ch->buffer); +} - RANGE(speed, 4000, max_speed); +static pcmchan_caps * +sbchan_getcaps(void *data) +{ + struct sb_chinfo *ch = data; + int p = (ch->dir == PCMDIR_PLAY)? 1 : 0; + if (ch->parent->bd_flags & BD_F_ESS) + return p? &ess_playcaps : &ess_reccaps; + else if (ch->parent->bd_id <= 0x200) + return p? &sb_playcaps : &sb_reccaps; + else if (ch->parent->bd_id >= 0x400) + return p? &sb16_playcaps : &sb16_reccaps; + else + return p? &sbpro_playcaps : &sbpro_reccaps; +} - if (d->flags & SND_F_STEREO) /* really unused right now... */ - speed *= 2; +/************************************************************/ - /* - * Now the speed should be valid. Compute the value to be - * programmed into the board. - */ +static int +sbmix_init(snd_mixer *m) +{ + struct sb_info *sb = mix_getdevinfo(m); + + switch (sb->bd_flags & BD_F_MIX_MASK) { + case BD_F_MIX_CT1345: /* SB 3.0 has 1345 mixer */ + mix_setdevs(m, SBPRO_MIXER_DEVICES); + mix_setrecdevs(m, SBPRO_RECORDING_DEVICES); + sb_setmixer(sb, 0, 1); /* reset mixer */ + sb_setmixer(sb, MIC_VOL, 0x6); /* mic volume max */ + sb_setmixer(sb, RECORD_SRC, 0x0); /* mic source */ + sb_setmixer(sb, FM_VOL, 0x0); /* no midi */ + break; - if (speed > 22050) { /* High speed mode on 2.01/3.xx */ - int tmp; + case BD_F_MIX_CT1745: /* SB16 mixer ... */ + mix_setdevs(m, SB16_MIXER_DEVICES); + mix_setrecdevs(m, SB16_RECORDING_DEVICES); + sb_setmixer(sb, 0x3c, 0x1f); /* make all output active */ + sb_setmixer(sb, 0x3d, 0); /* make all inputs-l off */ + sb_setmixer(sb, 0x3e, 0); /* make all inputs-r off */ + } + return 0; +} - tconst = (u_char) ((65536 - ((256000000 + speed / 2) / speed)) >> 8) ; - d->bd_flags |= BD_F_HISPEED ; +static int +sbmix_set(snd_mixer *m, unsigned dev, unsigned left, unsigned right) +{ + struct sb_info *sb = mix_getdevinfo(m); + int regoffs; + u_char val; + mixer_tab *iomap; + + switch (sb->bd_flags & BD_F_MIX_MASK) { + case BD_F_MIX_CT1345: + iomap = &sbpro_mix; + break; - flags = spltty(); - sb_cmd2(d->io_base, 0x40, tconst); /* set time constant */ - splx(flags); + case BD_F_MIX_CT1745: + iomap = &sb16_mix; + break; - tmp = 65536 - (tconst << 8); - speed = (256000000 + tmp / 2) / tmp; - } else { - int tmp; + default: + return -1; + /* XXX how about the SG NX Pro, iomap = sgnxpro_mix */ + } + regoffs = (*iomap)[dev][LEFT_CHN].regno; + if (regoffs == 0) return -1; + val = sb_getmixer(sb, regoffs); + change_bits(iomap, &val, dev, LEFT_CHN, left); + sb_setmixer(sb, regoffs, val); + if ((*iomap)[dev][RIGHT_CHN].regno != regoffs) { /* Change register */ + regoffs = (*iomap)[dev][RIGHT_CHN].regno; + if (regoffs != 0) { + val = sb_getmixer(sb, regoffs); /* Read the new one */ + change_bits(iomap, &val, dev, RIGHT_CHN, right); + sb_setmixer(sb, regoffs, val); + } else right = left; + } else right = left; + return left | (right << 8); +} - d->bd_flags &= ~BD_F_HISPEED ; - tconst = (256 - ((1000000 + speed / 2) / speed)) & 0xff; +static int +sbmix_setrecsrc(snd_mixer *m, u_int32_t src) +{ + struct sb_info *sb = mix_getdevinfo(m); + u_char recdev; + + switch (sb->bd_flags & BD_F_MIX_MASK) { + case BD_F_MIX_CT1345: + if (src == SOUND_MASK_LINE) recdev = 0x06; + else if (src == SOUND_MASK_CD) recdev = 0x02; + else { /* default: mic */ + src = SOUND_MASK_MIC; + recdev = 0; + } + sb_setmixer(sb, RECORD_SRC, recdev | + (sb_getmixer(sb, RECORD_SRC) & ~0x07)); + break; - flags = spltty(); - sb_cmd2(d->io_base, 0x40, tconst); /* set time constant */ - splx(flags); + case BD_F_MIX_CT1745: /* sb16 */ + recdev = 0; + if (src & SOUND_MASK_MIC) recdev |= 0x01; /* mono mic */ + if (src & SOUND_MASK_CD) recdev |= 0x06; /* l+r cd */ + if (src & SOUND_MASK_LINE) recdev |= 0x18; /* l+r line */ + if (src & SOUND_MASK_SYNTH) recdev |= 0x60; /* l+r midi */ + sb_setmixer(sb, SB16_IMASK_L, recdev); + sb_setmixer(sb, SB16_IMASK_R, recdev); + /* + * since the same volume controls apply to the input and + * output sections, the best approach to have a consistent + * behaviour among cards would be to disable the output path + * on devices which are used to record. + * However, since users like to have feedback, we only disable + * the mic -- permanently. + */ + sb_setmixer(sb, SB16_OMASK, 0x1f & ~1); + break; + } + return src; +} - tmp = 256 - tconst; - speed = (1000000 + tmp / 2) / tmp; - } +#if NPNP > 0 +static int +sbpnp_probe(device_t dev) +{ + char *s = NULL; + u_int32_t logical_id = isa_get_logicalid(dev); - if (d->flags & SND_F_STEREO) /* really unused right now... */ - speed /= 2; + switch(logical_id) { + case 0x43008c0e: /* CTL0043 */ + case 0x01008c0e: /* CTL0001 */ + s = "Vibra16X"; + break; - d->play_speed = d->rec_speed = speed; - return speed; -} + case 0x31008c0e: /* CTL0031 */ + case 0x41008c0e: /* CTL0041 */ + case 0x42008c0e: /* CTL0042 */ + case 0x45008c0e: /* CTL0045 */ + s = "SB16 PnP"; + break; -/* - * mixer support, originally in sb_mixer.c - */ + case 0x01100000: /* @@@1001 */ + s = "Avance Asound 110"; + break; -static void -sb_set_recsrc(snddev_info *d, int mask) -{ - u_char recdev ; - - mask &= d->mix_rec_devs; - switch (d->bd_flags & BD_F_MIX_MASK) { - case BD_F_MIX_CT1345 : - if (mask == SOUND_MASK_LINE) - recdev = 6 ; - else if (mask == SOUND_MASK_CD) - recdev = 2 ; - else { /* default: mic */ - mask = SOUND_MASK_MIC ; - recdev = 0 ; - } - sb_setmixer(d->io_base, RECORD_SRC, - recdev | (sb_getmixer(d->io_base, RECORD_SRC) & ~7 )); - break ; - case BD_F_MIX_CT1745 : /* sb16 */ - if (mask == 0) - mask = SOUND_MASK_MIC ; /* XXX For compatibility. Bug ? */ - recdev = 0 ; - if (mask & SOUND_MASK_MIC) - recdev |= 1 ; - if (mask & SOUND_MASK_CD) - recdev |= 6 ; /* l+r cd */ - if (mask & SOUND_MASK_LINE) - recdev |= 0x18 ; /* l+r line */ - if (mask & SOUND_MASK_SYNTH) - recdev |= 0x60 ; /* l+r midi */ - sb_setmixer(d->io_base, SB16_IMASK_L, recdev); - sb_setmixer(d->io_base, SB16_IMASK_R, recdev); - /* - * since the same volume controls apply to the input and - * output sections, the best approach to have a consistent - * behaviour among cards would be to disable the output path - * on devices which are used to record. - * However, since users like to have feedback, we only disable - * the mike -- permanently. - */ - sb_setmixer(d->io_base, SB16_OMASK, 0x1f & ~1); - break ; - } - d->mix_recsrc = mask; -} + case 0x01200000: /* @@@2001 */ + s = "Avance Logic ALS120"; + break; -static void -sb_mixer_reset(snddev_info *d) -{ - int i; - - for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) - sb_mixer_set(d, i, levels[i]); - if (d->bd_flags & BD_F_SB16) { - sb_setmixer(d->io_base, 0x3c, 0x1f); /* make all output active */ - sb_setmixer(d->io_base, 0x3d, 0); /* make all inputs-l off */ - sb_setmixer(d->io_base, 0x3e, 0); /* make all inputs-r off */ - } - sb_set_recsrc(d, SOUND_MASK_MIC); + case 0x68187316: /* ESS1868 */ + s = "ESS1868"; + break; + } + if (s) { + device_set_desc(dev, s); + return 0; + } + return ENXIO; } static int -sb_mixer_set(snddev_info *d, int dev, int value) +sbpnp_attach(device_t dev) { - int left = value & 0x000000ff; - int right = (value & 0x0000ff00) >> 8; - int regoffs; - u_char val; - mixer_tab *iomap; - -#ifdef JAZZ16 - if (d->bd_flags & BD_F_JAZZ16 && d->bd_flags & BD_F_JAZZ16_2) - return smw_mixer_set(dev, value); -#endif - - if (dev == SOUND_MIXER_RECSRC) { - sb_set_recsrc(d, value); - return 0 ; - } - if (left > 100) - left = 100; - if (right > 100) - right = 100; - - if (dev > 31) - return EINVAL ; - - if (!(d->mix_devs & (1 << dev))) /* Not supported */ - return EINVAL; - - switch ( d->bd_flags & BD_F_MIX_MASK ) { - default: - /* mixer unknown, fail... */ - return EINVAL ;/* XXX change this */ - case BD_F_MIX_CT1345 : - iomap = &sbpro_mix ; - break; - case BD_F_MIX_CT1745 : - iomap = &sb16_mix ; - break; - /* XXX how about the SG NX Pro, iomap = sgnxpro_mix */ - } - regoffs = (*iomap)[dev][LEFT_CHN].regno; - if (regoffs == 0) - return EINVAL; - - val = sb_getmixer(d->io_base, regoffs); - - change_bits(iomap, &val, dev, LEFT_CHN, left); - - d->mix_levels[dev] = left | (left << 8); - - if ((*iomap)[dev][RIGHT_CHN].regno != regoffs) { /* Change register */ - sb_setmixer(d->io_base, regoffs, val); /* Save the old one */ - regoffs = (*iomap)[dev][RIGHT_CHN].regno; - - if (regoffs == 0) - return 0 ; /* Just left channel present */ - - val = sb_getmixer(d->io_base, regoffs); /* Read the new one */ - } - change_bits(iomap, &val, dev, RIGHT_CHN, right); - - sb_setmixer(d->io_base, regoffs, val); - - d->mix_levels[dev] = left | (right << 8); - return 0 ; /* ok */ + struct sb_info *sb; + u_int32_t vend_id = isa_get_vendorid(dev); + + 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); } -/* - * now support for some PnP boards. - */ +static device_method_t sbpnp_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, sbpnp_probe), + DEVMETHOD(device_attach, sbpnp_attach), -#if NPNP > 0 -static char *ess1868_probe(u_long csn, u_long vend_id); -static void ess1868_attach(u_long csn, u_long vend_id, char *name, - struct isa_device *dev); - -static struct pnp_device ess1868 = { - "ESS1868", - ess1868_probe, - ess1868_attach, - &nsnd, /* use this for all sound cards */ - &tty_imask /* imask */ + { 0, 0 } }; -DATA_SET (pnpdevice_set, ess1868); - -static char * -ess1868_probe(u_long csn, u_long vend_id) -{ - /* - * pnp X 1 os enable drq0 3 irq0 12 port0 0x240 - */ - if (vend_id == 0x68187316) { - struct pnp_cinfo d ; - read_pnp_parms ( &d , 1 ) ; - if (d.enable == 0) { - printf("This is an ESS1868, but LDN 1 is disabled\n"); - return NULL; - } - return "ESS1868" ; - } - return NULL ; -} - -static void -ess1868_attach(u_long csn, u_long vend_id, char *name, - struct isa_device *dev) -{ - struct pnp_cinfo d ; - snddev_info tmp_d ; /* patched copy of the basic snddev_info */ - - tmp_d = sb_op_desc; - snddev_last_probed = &tmp_d; -#if 0 - read_pnp_parms ( &d , 3 ); /* disable LDN 3 */ - d.port[0] = 0 ; - d.enable = 0 ; - write_pnp_parms ( &d , 3 ); - - read_pnp_parms ( &d , 2 ); /* disable LDN 2 */ - d.port[0] = 0 ; - d.enable = 0 ; - write_pnp_parms ( &d , 2 ); - read_pnp_parms ( &d , 0 ); /* read config base */ - tmp_d.conf_base = d.port[0]; - write_pnp_parms ( &d , 0 ); -#endif - - read_pnp_parms ( &d , 1 ) ; - dev->id_iobase = d.port[0]; - d.port[1] = 0 ; - d.port[2] = 0 ; - write_pnp_parms ( &d , 1 ); - enable_pnp_card(); - - dev->id_drq = d.drq[0] ; /* primary dma */ - dev->id_irq = (1 << d.irq[0] ) ; - dev->id_intr = (inthand2_t *)pcmintr ; - dev->id_flags = 0 /* DV_F_DUAL_DMA | (d.drq[1] ) */; +static driver_t sbpnp_driver = { + "pcm", + sbpnp_methods, + sizeof(snddev_info), +}; -#if 0 - snddev_last_probed->probe(dev); /* not really necessary but doesn't harm */ -#endif - pcmattach(dev); -} +DRIVER_MODULE(sbpnp, isa, sbpnp_driver, pcm_devclass, 0, 0); -/* - * A driver for some SB16pnp and compatibles... - * - * Avance Asound 100 -- 0x01009305 - * Avance Logic ALS100+ -- 0x10019305 - * Avance Logic ASound Gold ALS120 -- 0x20019305 - * xxx -- 0x2b008c0e - * - */ +#endif /* NPNP > 0 */ -static char *sb16pnp_probe(u_long csn, u_long vend_id); -static void sb16pnp_attach(u_long csn, u_long vend_id, char *name, - struct isa_device *dev); +#endif /* NPCM > 0 */ -static struct pnp_device sb16pnp = { - "SB16pnp", - sb16pnp_probe, - sb16pnp_attach, - &nsnd, /* use this for all sound cards */ - &tty_imask /* imask */ -}; -DATA_SET (pnpdevice_set, sb16pnp); - -static char * -sb16pnp_probe(u_long csn, u_long vend_id) -{ - char *s = NULL ; - - /* - * The SB16/AWExx cards seem to differ in the fourth byte of - * the vendor id, so I have just masked it for the time being... - * Reported values are: - * SB16 Value PnP: 0x2b008c0e - * SB AWExx PnP: 0x39008c0e 0x9d008c0e 0xc3008c0e - * Vibra16X: 0xf0008c0e - */ - if (vend_id == 0xf0008c0e) - s = "Vibra16X" ; - else if ( (vend_id & 0xffffff) == (0x9d008c0e & 0xffffff) ) - s = "SB16 PnP"; - else if (vend_id == 0x01009305) - s = "Avance Asound 100" ; - else if (vend_id == 0x10019305) - s = "Avance Logic 100+" ; /* Vibra16X-class */ - else if (vend_id == 0x20019305) - s = "Avance Logic ALS120" ; /* Vibra16X-class */ - if (s) { - struct pnp_cinfo d; - read_pnp_parms(&d, 0); - if (d.enable == 0) { - printf("This is a %s, but LDN 0 is disabled\n", s); - return NULL ; - } - return s ; - } - return NULL ; -} - -static void -sb16pnp_attach(u_long csn, u_long vend_id, char *name, - struct isa_device *dev) -{ - struct pnp_cinfo d ; - snddev_info tmp_d ; /* patched copy of the basic snddev_info */ - - tmp_d = sb_op_desc; - snddev_last_probed = &tmp_d; - - read_pnp_parms ( &d , 0 ) ; - d.port[1] = 0 ; /* only the first address is used */ - dev->id_iobase = d.port[0]; - tmp_d.synth_base = d.port[2]; - write_pnp_parms ( &d , 0 ); - enable_pnp_card(); - - dev->id_drq = d.drq[0] ; /* primary dma */ - dev->id_irq = (1 << d.irq[0] ) ; - dev->id_intr = (inthand2_t *)pcmintr ; - dev->id_flags = DV_F_DUAL_DMA | (d.drq[1] ) ; - - pcm_info[dev->id_unit] = tmp_d; /* pcm_info[] will be reinitialized after */ - snddev_last_probed->probe(dev); /* not really necessary but doesn't harm */ - - if (vend_id == 0x10019305 || vend_id == 0xf0008c0e - || vend_id == 0x20019305) { - /* - * XXX please add here the vend_id for other vibra16X cards... - * And remember, must change tmp_d, not - */ - tmp_d.bd_flags |= BD_F_SB16X ; - } - pcmattach(dev); -} -#endif /* NPNP */ -#endif diff --git a/sys/dev/sound/isa/sb.h b/sys/dev/sound/isa/sb.h index b05b365..cf39772 100644 --- a/sys/dev/sound/isa/sb.h +++ b/sys/dev/sound/isa/sb.h @@ -12,12 +12,12 @@ extern int sbc_major, sbc_minor ; */ #define SBDSP_RST 0x6 -#define DSP_READ (io_base + 0xA) -#define DSP_WRITE (io_base + 0xC) +#define DSP_READ 0xA +#define DSP_WRITE 0xC #define SBDSP_CMD 0xC #define SBDSP_STATUS 0xC -#define DSP_DATA_AVAIL (io_base + 0xE) -#define DSP_DATA_AVL16 (io_base + 0xF) +#define DSP_DATA_AVAIL 0xE +#define DSP_DATA_AVL16 0xF #define SB_MIX_ADDR 0x4 #define SB_MIX_DATA 0x5 @@ -96,7 +96,7 @@ extern int sbc_major, sbc_minor ; * in fact, for the SB16, dma commands are as follows: * * cmd, mode, len_low, len_high. - * + * * cmd is a combination of DSP_DMA16 or DSP_DMA8 and */ @@ -146,15 +146,16 @@ extern int sbc_major, sbc_minor ; * so that they can be restored later. */ #define BD_F_SWAPPED 0x1000 /* have swapped DMA channels */ - - +#define BD_F_DMARUN 0x2000 +#define BD_F_DMARUN2 0x4000 +#define BD_F_DUPLEX 0x8000 /* * sound/sb_mixer.h - * + * * Definitions for the SB Pro and SB16 mixers - * + * * Copyright by Hannu Savolainen 1993 - * + * * 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 @@ -162,7 +163,7 @@ extern int sbc_major, sbc_minor ; * 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 @@ -174,10 +175,10 @@ extern int sbc_major, sbc_minor ; * 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. - * + * * Modified: Hunyue Yau Jan 6 1994 Added defines for the Sound Galaxy NX Pro * mixer. - * + * */ #define SBPRO_RECORDING_DEVICES \ @@ -210,7 +211,7 @@ extern int sbc_major, sbc_minor ; /* * Mixer registers - * + * * NOTE! RECORD_SRC == IN_FILTER */ @@ -314,47 +315,6 @@ mixer_tab sb16_mix = { PMIX_ENT(SOUND_MIXER_OGAIN, 0x41, 6, 2, 0x42, 6, 2) }; -#ifdef SM_GAMES /* Master volume is lower and PCM & FM - * volumes higher than with SB Pro. This - * improves the sound quality */ - -static u_short levels[SOUND_MIXER_NRDEVICES] = -{ - 0x2020, /* Master Volume */ - 0x4b4b, /* Bass */ - 0x4b4b, /* Treble */ - 0x6464, /* FM */ - 0x6464, /* PCM */ - 0x4b4b, /* PC Speaker */ - 0x4b4b, /* Ext Line */ - 0x0000, /* Mic */ - 0x4b4b, /* CD */ - 0x4b4b, /* Recording monitor */ - 0x4b4b, /* SB PCM */ - 0x4b4b, /* Recording level */ - 0x4b4b, /* Input gain */ -0x4b4b}; /* Output gain */ - -#else /* If the user selected just plain SB Pro */ - -static u_short levels[SOUND_MIXER_NRDEVICES] = -{ - 0x5a5a, /* Master Volume */ - 0x4b4b, /* Bass */ - 0x4b4b, /* Treble */ - 0x4b4b, /* FM */ - 0x4b4b, /* PCM */ - 0x4b4b, /* PC Speaker */ - 0x4b4b, /* Ext Line */ - 0x1010, /* Mic */ - 0x4b4b, /* CD */ - 0x4b4b, /* Recording monitor */ - 0x4b4b, /* SB PCM */ - 0x4b4b, /* Recording level */ - 0x4b4b, /* Input gain */ -0x4b4b}; /* Output gain */ -#endif /* SM_GAMES */ - #if 0 static u_char sb16_recmasks_L[SOUND_MIXER_NRDEVICES] = { diff --git a/sys/dev/sound/isa/sb16.c b/sys/dev/sound/isa/sb16.c index 7544dae..2a8d336 100644 --- a/sys/dev/sound/isa/sb16.c +++ b/sys/dev/sound/isa/sb16.c @@ -1,1366 +1,1117 @@ /* - * sound/sb_dsp.c - * - * driver for the SoundBlaster and clones. - * + * Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk> * Copyright 1997,1998 Luigi Rizzo. * * Derived from files in the Voxware 3.5 distribution, * Copyright by Hannu Savolainen 1994, under the same copyright * conditions. - * + * 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. + * 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. - * - */ - -/* - * use this as a template file for board-specific drivers. - * The next two lines (and the final #endif) are in all drivers: + * 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. + * + * $Id$ */ -#include <i386/isa/snd/sound.h> +#include <dev/pcm/sound.h> #if NPCM > 0 -/* - * Begin with the board-specific include files... - */ - #define __SB_MIXER_C__ /* XXX warning... */ -#include <i386/isa/snd/sbcard.h> - -/* - * then prototypes of functions which go in the snddev_info - * (usually static, unless they are shared by other modules)... - */ +#include <dev/pcm/isa/sb.h> + +/* channel interface */ +static void *sbchan_init(void *devinfo, snd_dbuf *b, pcm_channel *c, int dir); +static int sbchan_setdir(void *data, int dir); +static int sbchan_setformat(void *data, u_int32_t format); +static int sbchan_setspeed(void *data, u_int32_t speed); +static int sbchan_setblocksize(void *data, u_int32_t blocksize); +static int sbchan_trigger(void *data, int go); +static int sbchan_getptr(void *data); +static pcmchan_caps *sbchan_getcaps(void *data); + +static pcmchan_caps sb_playcaps = { + 4000, 22050, + AFMT_U8, + AFMT_U8 +}; -static int sb_probe(struct isa_device *dev); -static int sb_attach(struct isa_device *dev); +static pcmchan_caps sb_reccaps = { + 4000, 13000, + AFMT_U8, + AFMT_U8 +}; -static d_open_t sb_dsp_open; -static d_close_t sb_dsp_close; -static d_ioctl_t sb_dsp_ioctl; -static irq_proc_t sb_intr; -static snd_callback_t sb_callback; +static pcmchan_caps sbpro_playcaps = { + 4000, 45000, + AFMT_STEREO | AFMT_U8, + AFMT_STEREO | AFMT_U8 +}; -/* - * and prototypes for other private functions defined in this module. - */ +static pcmchan_caps sbpro_reccaps = { + 4000, 15000, + AFMT_STEREO | AFMT_U8, + AFMT_STEREO | AFMT_U8 +}; -static void sb_dsp_init(snddev_info *d, struct isa_device *dev); -static void sb_mix_init(snddev_info *d); -static int sb_mixer_set(snddev_info *d, int dev, int value); -static int dsp_speed(snddev_info *d); -static void sb_mixer_reset(snddev_info *d); +static pcmchan_caps sb16_playcaps = { + 5000, 45000, + AFMT_STEREO | AFMT_S16_LE, + AFMT_STEREO | AFMT_S16_LE +}; -u_int sb_get_byte(int io_base); -int ess_write(int io_base, u_char reg, int val); -int ess_read(int io_base, u_char reg); +static pcmchan_caps sb16_reccaps = { + 5000, 45000, + AFMT_STEREO | AFMT_U8, + AFMT_STEREO | AFMT_U8 +}; -/* - * Then put here the descriptors for the various boards supported - * by this module, properly initialized. - */ +static pcmchan_caps ess_playcaps = { + 5000, 49000, + AFMT_STEREO | AFMT_U8 | AFMT_S16_LE, + AFMT_STEREO | AFMT_S16_LE +}; -snddev_info sb_op_desc = { - "basic soundblaster", +static pcmchan_caps ess_reccaps = { + 5000, 49000, + AFMT_STEREO | AFMT_U8 | AFMT_S16_LE, + AFMT_STEREO | AFMT_S16_LE +}; - SNDCARD_SB, - sb_probe, - sb_attach, +static pcm_channel sb_chantemplate = { + sbchan_init, + sbchan_setdir, + sbchan_setformat, + sbchan_setspeed, + sbchan_setblocksize, + sbchan_trigger, + sbchan_getptr, + sbchan_getcaps, +}; - sb_dsp_open, - sb_dsp_close /* sb_close */, - NULL /* use generic sndread */, - NULL /* use generic sndwrite */, - sb_dsp_ioctl, - sndselect, +#define PLAIN_SB16(x) ((((x)->bd_flags) & (BD_F_SB16|BD_F_SB16X)) == BD_F_SB16) - sb_intr, - sb_callback, +struct sb_info; - DSP_BUFFSIZE, /* bufsize */ +struct sb_chinfo { + struct sb_info *parent; + pcm_channel *channel; + snd_dbuf *buffer; + int dir; + u_int32_t fmt; +}; - AFMT_STEREO | AFMT_U8, /* audio format */ +struct sb_info { + struct resource *io_base; /* I/O address for the board */ + int io_rid; + struct resource *irq; + int irq_rid; + struct resource *drq1; /* play */ + int drq1_rid; + struct resource *drq2; /* rec */ + int drq2_rid; + bus_dma_tag_t parent_dmat; + + int dma16, dma8; + int bd_id; + u_long bd_flags; /* board-specific flags */ + struct sb_chinfo pch, rch; +}; -} ; +static int sb_rd(struct sb_info *sb, int reg); +static void sb_wr(struct sb_info *sb, int reg, u_int8_t val); +static int sb_dspready(struct sb_info *sb); +static int sb_cmd(struct sb_info *sb, u_char val); +static int sb_cmd1(struct sb_info *sb, u_char cmd, int val); +static int sb_cmd2(struct sb_info *sb, u_char cmd, int val); +static u_int sb_get_byte(struct sb_info *sb); +static int ess_write(struct sb_info *sb, u_char reg, int val); +static int ess_read(struct sb_info *sb, u_char reg); /* - * Then the file continues with the body of all functions - * directly referenced in the descriptor. + * in the SB, there is a set of indirect "mixer" registers with + * address at offset 4, data at offset 5 */ +static void sb_setmixer(struct sb_info *sb, u_int port, u_int value); +static int sb_getmixer(struct sb_info *sb, u_int port); + +static void sb_intr(void *arg); +static int sb_init(device_t dev, struct sb_info *sb); +static int sb_reset_dsp(struct sb_info *sb); + +static int sb_format(struct sb_chinfo *ch, u_int32_t format); +static int sb_speed(struct sb_chinfo *ch, int speed); +static int sb_start(struct sb_chinfo *ch); +static int sb_stop(struct sb_chinfo *ch); + +static int sbmix_init(snd_mixer *m); +static int sbmix_set(snd_mixer *m, unsigned dev, unsigned left, unsigned right); +static int sbmix_setrecsrc(snd_mixer *m, u_int32_t src); + +static snd_mixer sb_mixer = { + "SoundBlaster mixer", + sbmix_init, + sbmix_set, + sbmix_setrecsrc, +}; + +static devclass_t pcm_devclass; /* - * the probe routine for the SoundBlaster only consists in - * resetting the dsp and testing if it is there. - * Version detection etc. will be done at attach time. + * Common code for the midi and pcm functions + * + * sb_cmd write a single byte to the CMD port. + * sb_cmd1 write a CMD + 1 byte arg + * sb_cmd2 write a CMD + 2 byte arg + * sb_get_byte returns a single byte from the DSP data port * - * Remember, ISA probe routines are supposed to return the - * size of io space used. + * ess_write is actually sb_cmd1 + * ess_read access ext. regs via sb_cmd(0xc0, reg) followed by sb_get_byte */ static int -sb_probe(struct isa_device *dev) +port_rd(struct resource *port, int off) { - bzero(&pcm_info[dev->id_unit], sizeof(pcm_info[dev->id_unit]) ); - if (dev->id_iobase == -1) { - dev->id_iobase = 0x220; - BVDDB(printf("sb_probe: no address supplied, try defaults (0x220,0x240)\n");) - if (snd_conflict(dev->id_iobase)) - dev->id_iobase = 0x240; - } - if (snd_conflict(dev->id_iobase)) - return 0 ; - - if (sb_reset_dsp(dev->id_iobase)) - return 16 ; /* the SB uses 16 registers... */ - else - return 0; + return bus_space_read_1(rman_get_bustag(port), + rman_get_bushandle(port), + off); +} + +static void +port_wr(struct resource *port, int off, u_int8_t data) +{ + return bus_space_write_1(rman_get_bustag(port), + rman_get_bushandle(port), + off, data); } static int -sb_attach(struct isa_device *dev) +sb_rd(struct sb_info *sb, int reg) { - snddev_info *d = &pcm_info[dev->id_unit] ; + return port_rd(sb->io_base, reg); +} - dev->id_alive = 16 ; /* number of io ports */ - /* should be already set but just in case... */ - sb_dsp_init(d, dev); - return 0 ; +static void +sb_wr(struct sb_info *sb, int reg, u_int8_t val) +{ + port_wr(sb->io_base, reg, val); } -/* - * here are the main routines from the switches. - */ +static int +sb_dspready(struct sb_info *sb) +{ + return ((sb_rd(sb, SBDSP_STATUS) & 0x80) == 0); +} -/* - * Unlike MSS, the sb only supports a single open (does not mean - * that only a single process is using it, since it can fork - * afterwards, or pass the descriptor to another process). - * - */ static int -sb_dsp_open(dev_t i_dev, int flags, int mode, struct proc * p) +sb_dspwr(struct sb_info *sb, u_char val) { - snddev_info *d; - int unit ; - int dev; + int i; - dev = minor(i_dev); - unit = dev >> 4 ; - d = &pcm_info[unit] ; + for (i = 0; i < 1000; i++) { + if (sb_dspready(sb)) { + sb_wr(sb, SBDSP_CMD, val); + return 1; + } + if (i > 10) DELAY((i > 100)? 1000 : 10); + } + printf("sb_dspwr(0x%02x) timed out.\n", val); + return 0; +} - DEB(printf("<%s>%d : open\n", d->name, unit)); +static int +sb_cmd(struct sb_info *sb, u_char val) +{ +#if 0 + printf("sb_cmd: %x\n", val); +#endif + return sb_dspwr(sb, val); +} - if (d->flags & SND_F_BUSY) { - DEB(printf("<%s>%d open: device busy\n", d->name, unit)); - return EBUSY ; - } +static int +sb_cmd1(struct sb_info *sb, u_char cmd, int val) +{ +#if 0 + printf("sb_cmd1: %x, %x\n", cmd, val); +#endif + if (sb_dspwr(sb, cmd)) { + return sb_dspwr(sb, val & 0xff); + } else return 0; +} - d->wsel.si_pid = 0; - d->wsel.si_flags = 0; +static int +sb_cmd2(struct sb_info *sb, u_char cmd, int val) +{ +#if 0 + printf("sb_cmd2: %x, %x\n", cmd, val); +#endif + if (sb_dspwr(sb, cmd)) { + return sb_dspwr(sb, val & 0xff) && + sb_dspwr(sb, (val >> 8) & 0xff); + } else return 0; +} - d->rsel.si_pid = 0; - d->rsel.si_flags = 0; +/* + * in the SB, there is a set of indirect "mixer" registers with + * address at offset 4, data at offset 5 + */ +static void +sb_setmixer(struct sb_info *sb, u_int port, u_int value) +{ + u_long flags; + + flags = spltty(); + sb_wr(sb, SB_MIX_ADDR, (u_char) (port & 0xff)); /* Select register */ + DELAY(10); + sb_wr(sb, SB_MIX_DATA, (u_char) (value & 0xff)); + DELAY(10); + splx(flags); +} - d->dbuf_out.total = d->dbuf_out.prev_total = 0 ; - d->dbuf_in.total = d->dbuf_in.prev_total = 0 ; +static int +sb_getmixer(struct sb_info *sb, u_int port) +{ + int val; + u_long flags; - d->flags = 0 ; - d->bd_flags &= ~BD_F_HISPEED ; + flags = spltty(); + sb_wr(sb, SB_MIX_ADDR, (u_char) (port & 0xff)); /* Select register */ + DELAY(10); + val = sb_rd(sb, SB_MIX_DATA); + DELAY(10); + splx(flags); - switch ( dev & 0xf ) { - case SND_DEV_DSP16 : - if ((d->audio_fmt & AFMT_S16_LE) == 0) { - printf("sorry, 16-bit not supported on SB %d.%02d\n", - (d->bd_id >>8) & 0xff, d->bd_id & 0xff); - return ENXIO; - } - d->play_fmt = d->rec_fmt = AFMT_S16_LE ; - break; - case SND_DEV_AUDIO : - d->play_fmt = d->rec_fmt = AFMT_MU_LAW ; - break ; - case SND_DEV_DSP : - d->play_fmt = d->rec_fmt = AFMT_U8 ; - break ; - } - /* - * since the SB is not simmetric, I use the open mode to select - * which channel should be privileged, and disable I/O in the - * other direction. - * In case the board is opened RW, we don't have enough - * information on what to do. Temporarily, privilege the - * playback channel, which is used more often, and set the other - * one to U8. - */ - if ( (flags & FREAD) == 0) /* opened write only */ - d->rec_fmt = 0 ; - else if ( (flags & FWRITE) == 0) /* opened read only */ - d->play_fmt = 0 ; - else /* opened read/write */ - d->rec_fmt = (d->play_fmt == AFMT_S16_LE) ? AFMT_U8 : AFMT_S16_LE ; - - d->flags |= SND_F_BUSY ; - d->play_speed = d->rec_speed = DSP_DEFAULT_SPEED ; - - if (flags & O_NONBLOCK) - d->flags |= SND_F_NBIO ; - - sb_reset_dsp(d->io_base); - if (d->bd_flags & BD_F_ESS) - sb_cmd(d->io_base, 0xc6 ); /* enable extended ESS mode */ - ask_init(d); - - return 0; + return val; } -static int -sb_dsp_close(dev_t i_dev, int flags, int mode, struct proc * p) +static u_int +sb_get_byte(struct sb_info *sb) { - int unit; - int dev; - snddev_info *d; - u_long s; + int i; - dev = minor(i_dev); - unit = dev >> 4 ; - d = &pcm_info[unit] ; - - s = spltty(); - d->flags |= SND_F_CLOSING ; - splx(s); - snd_flush(d); + for (i = 1000; i > 0; i--) { + if (sb_rd(sb, DSP_DATA_AVAIL) & 0x80) + return sb_rd(sb, DSP_READ); + else + DELAY(20); + } + return 0xffff; +} - sb_cmd(d->io_base, DSP_CMD_SPKOFF ); /* XXX useless ? */ +static int +ess_write(struct sb_info *sb, u_char reg, int val) +{ + return sb_cmd1(sb, reg, val); +} - d->flags = 0 ; - return 0 ; +static int +ess_read(struct sb_info *sb, u_char reg) +{ + return (sb_cmd(sb, 0xc0) && sb_cmd(sb, reg))? sb_get_byte(sb) : 0xffff; } static int -sb_dsp_ioctl(dev_t i_dev, u_long cmd, caddr_t arg, int mode, struct proc * p) +sb_reset_dsp(struct sb_info *sb) { - int unit; - int dev; - snddev_info *d; - - dev = minor(i_dev); - unit = dev >> 4 ; - d = &pcm_info[unit] ; - - /* - * handle mixer calls first. Reads are in the default handler, - * so do not bother about them. - */ - if ( (cmd & MIXER_WRITE(0)) == MIXER_WRITE(0) ) - return sb_mixer_set(d, cmd & 0xff, *(int *)arg) ; - - /* - * for the remaining functions, use the default handler. - * ENOSYS means that the default handler should take care - * of implementing the ioctl. - */ - - return ENOSYS ; + sb_wr(sb, SBDSP_RST, 3); + DELAY(100); + sb_wr(sb, SBDSP_RST, 0); + if (sb_get_byte(sb) != 0xAA) { + DEB(printf("sb_reset_dsp 0x%lx failed\n", + rman_get_start(d->io_base))); + return ENXIO; /* Sorry */ + } + if (sb->bd_flags & BD_F_ESS) sb_cmd(sb, 0xc6); + return 0; } static void -sb_intr(int unit) +sb_release_resources(struct sb_info *sb, device_t dev) { - snddev_info *d = &pcm_info[unit]; - int reason = 3, c=1, io_base = d->io_base; - - DEB(printf("got sb_intr for unit %d, flags 0x%08lx\n", unit, d->flags)); - - /* - * SB < 4.0 is half duplex and has only 1 bit for int source, - * so we fake it. SB 4.x (SB16) has the int source in a separate - * register. - * The Vibra16X has separate flags for 8 and 16 bit transfers, but - * I have no idea how to tell capture from playback interrupts... - */ -#define PLAIN_SB16(x) ( ( (x) & (BD_F_SB16|BD_F_SB16X) ) == BD_F_SB16) -again: - if (d->bd_flags & BD_F_SB16) { - c = sb_getmixer(io_base, IRQ_STAT); - /* this tells us if the source is 8-bit or 16-bit dma. We - * have to check the io channel to map it to read or write... - */ - reason = 0 ; - if ( c & 1 ) { /* 8-bit dma */ - if (d->play_fmt == AFMT_U8 || d->play_fmt == AFMT_MU_LAW ) - reason |= 1; - if (d->rec_fmt == AFMT_U8 || d->rec_fmt == AFMT_MU_LAW ) - reason |= 2; - } - if ( c & 2 ) { /* 16-bit dma */ - if (d->play_fmt == AFMT_S16_LE) - reason |= 1; - if (d->rec_fmt == AFMT_S16_LE) - reason |= 2; - } - } - /* XXX previous location of ack... */ - DEB(printf("sb_intr, flags 0x%08lx reason %d c 0x%x\n", - d->flags, reason, c)); - if ( reason & 1 ) { /* possibly a write interrupt */ - if ( d->dbuf_out.dl ) - dsp_wrintr(d); - } - if ( reason & 2 ) { - if ( d->dbuf_in.dl ) - dsp_rdintr(d); - } - if ( c & 2 ) - inb(DSP_DATA_AVL16); /* 16-bit int ack */ - if (c & 1) - inb(DSP_DATA_AVAIL); /* 8-bit int ack */ - - /* - * the sb16 might have multiple sources etc. - */ - if ((d->bd_flags & BD_F_SB16) && (c & 3)) - goto again; + /* should we bus_teardown_intr here? */ + if (sb->irq) { + bus_release_resource(dev, SYS_RES_IRQ, sb->irq_rid, sb->irq); + sb->irq = 0; + } + if (sb->drq1) { + bus_release_resource(dev, SYS_RES_DRQ, sb->drq1_rid, sb->drq1); + sb->drq1 = 0; + } + if (sb->drq2) { + bus_release_resource(dev, SYS_RES_DRQ, sb->drq2_rid, sb->drq2); + sb->drq2 = 0; + } + if (sb->io_base) { + bus_release_resource(dev, SYS_RES_IOPORT, sb->io_rid, + sb->io_base); + sb->io_base = 0; + } + free(sb, M_DEVBUF); } -/* - * device-specific function called back from the dma module. - * The reason of the callback is the second argument. - * NOTE: during operations, some ioctl can be called to change - * settings (e.g. speed, channels, format), and the default - * ioctl handler will just record the change and set the - * flag SND_F_INIT. The callback routine is in charge of applying - * the changes at the next convenient time (typically, at the - * start of operations). For full duplex devices, in some cases the - * init requires both channels to be idle. - */ static int -sb_callback(snddev_info *d, int reason) +sb_alloc_resources(struct sb_info *sb, device_t dev) { - int rd = reason & SND_CB_RD ; - snd_dbuf *b = (rd) ? & (d->dbuf_in) : & (d->dbuf_out) ; - int l = b->dl ; - - switch (reason & SND_CB_REASON_MASK) { - case SND_CB_INIT : /* called with int enabled and no pending io */ - /* - * set the speed - */ - dsp_speed(d); - /* - * set the desired DMA blocksize (influences select behaviour) - */ - snd_set_blocksize(d); - /* - * since native mulaw is not present, emulate it. - */ - if ( (d->play_fmt & AFMT_MU_LAW) || (d->rec_fmt & AFMT_MU_LAW) ) - d->flags |= SND_F_XLAT8 ; - else - d->flags &= ~SND_F_XLAT8 ; - - /* - * there are too many flavours of SB for my taste... here i try to do - * the proper initialization for each one. - */ - if (PLAIN_SB16(d->bd_flags)) { - - /* the original SB16 (non-PnP, or PnP, or Vibra16C) - * can do full duplex using one 16-bit channel - * and one 8-bit channel. It needs to be programmed to - * use split format though. - * I DON'T do this for the Vibra16X because I have no idea - * of what needs to be done there... - * - * I use the following algorithm: - * 1. check which direction(s) are active; - * 2. check if we should swap dma channels - * 3. check if we can do the swap. - */ - int swap = 1 ; /* default... */ - - if (d->play_fmt == 0) { - /* do whatever the read channel wants */ - if ( d->rec_fmt == AFMT_S16_LE && d->dbuf_in.chan > 4 ) - swap = 0; - if ( d->rec_fmt != AFMT_S16_LE && d->dbuf_in.chan < 4 ) - swap = 0; - } else { - /* privilege the write channel */ - if ( d->play_fmt == AFMT_S16_LE && d->dbuf_out.chan > 4 ) - swap = 0; - if ( d->play_fmt != AFMT_S16_LE && d->dbuf_out.chan < 4 ) - swap = 0; - if ( d->rec_fmt ) { - /* check for possible config errors. - * This cannot happen at open time since even in - * case of opening rw we privilege the play - * channel. - */ - if (d->rec_fmt == d->play_fmt) { - DDB(printf("sorry, read DMA channel unavailable\n")); - } + if (!sb->io_base) + sb->io_base = bus_alloc_resource(dev, SYS_RES_IOPORT, + &sb->io_rid, 0, ~0, 1, + RF_ACTIVE); + if (!sb->irq) + sb->irq = bus_alloc_resource(dev, SYS_RES_IRQ, + &sb->irq_rid, 0, ~0, 1, + RF_ACTIVE); + if (!sb->drq1) + sb->drq1 = bus_alloc_resource(dev, SYS_RES_DRQ, + &sb->drq1_rid, 0, ~0, 1, + RF_ACTIVE); + if (!sb->drq2 && sb->drq2_rid > 0) + sb->drq2 = bus_alloc_resource(dev, SYS_RES_DRQ, + &sb->drq2_rid, 0, ~0, 1, + RF_ACTIVE); + + if (sb->io_base && sb->drq1 && sb->irq) { + sb->dma8 = rman_get_start(sb->drq1); + isa_dma_acquire(sb->dma8); + isa_dmainit(sb->dma8, DSP_BUFFSIZE); + + if (sb->drq2) { + sb->dma16 = rman_get_start(sb->drq2); + isa_dma_acquire(sb->dma16); + isa_dmainit(sb->dma16, DSP_BUFFSIZE); + } else sb->dma16 = sb->dma8; + + if (sb->dma8 > sb->dma16) { + int tmp = sb->dma16; + sb->dma16 = sb->dma8; + sb->dma8 = tmp; } - } - DEB(printf("sb16: play_fmt %d, rec_fmt %x, swap %d\n", - d->play_fmt, d->rec_fmt, swap);) - if (swap) { - int c = d->dbuf_in.chan ; - d->dbuf_in.chan = d->dbuf_out.chan; - d->dbuf_out.chan = c ; - } - } - else if (d->bd_flags & BD_F_ESS) { - u_char c; + return 0; + } else return ENXIO; +} - DEB(printf("SND_CB_INIT, play_fmt == 0x%x, rec_fmt == 0x%x\n", - (int) d->play_fmt, (int) d->rec_fmt)); +static int +sb_identify_board(device_t dev, struct sb_info *sb) +{ + char *fmt = NULL; + static char buf[64]; - /* autoinit DMA mode */ - if (d->play_fmt) - ess_write(d->io_base, 0xb8, 0x04); - else - ess_write(d->io_base, 0xb8, 0x0e); - - c = (ess_read(d->io_base, 0xa8) & ~0x03) | 0x01; - if ((d->flags & SND_F_STEREO) == 0) - c++; - ess_write(d->io_base, 0xa8, c); /* select mono/stereo */ - ess_write(d->io_base, 0xb9, 2); /* demand 4 bytes/transfer */ - - switch (d->play_fmt ? d->play_fmt : d->rec_fmt) { - case AFMT_S16_LE: - if (d->flags & SND_F_STEREO) { - /* 16 bit stereo */ - if (d->play_fmt) - ess_write(d->io_base, 0xb6, 0x00); - ess_write(d->io_base, 0xb7, 0x71); - ess_write(d->io_base, 0xb7, 0xbc); - } - else { - /* 16 bit mono */ - if (d->play_fmt) - ess_write(d->io_base, 0xb6, 0x00); - ess_write(d->io_base, 0xb7, 0x71); - ess_write(d->io_base, 0xb7, 0xf4); - } - break; - case AFMT_U8: - if (d->flags & SND_F_STEREO) { - /* 8 bit stereo */ - if (d->play_fmt) - ess_write(d->io_base, 0xb6, 0x80); - ess_write(d->io_base, 0xb7, 0x51); - ess_write(d->io_base, 0xb7, 0x98); - } - else { - /* 8 bit mono */ - if (d->play_fmt) - ess_write(d->io_base, 0xb6, 0x80); - ess_write(d->io_base, 0xb7, 0x51); - ess_write(d->io_base, 0xb7, 0xd0); - } - break; - } - ess_write(d->io_base, 0xb1, - ess_read(d->io_base, 0xb1) | 0x50); - ess_write(d->io_base, 0xb2, - ess_read(d->io_base, 0xb1) | 0x50); - } - reset_dbuf(& (d->dbuf_in), SND_CHAN_RD ); - reset_dbuf(& (d->dbuf_out), SND_CHAN_WR ); - break ; - - case SND_CB_START : /* called with int disabled */ - if (d->bd_flags & BD_F_SB16) { - u_char c, c1 ; - - if (d->bd_flags & BD_F_SB16X) { - /* just a guess: on the Vibra16X, the first - * op started takes the first dma channel, - * the second one takes the next... - * The default is to be ready for play. - */ - DEB(printf("start %s -- now dma %d:%d\n", - rd ? "rd" : "wr", - d->dbuf_out.chan, d->dbuf_in.chan);); - /* swap only if both channels are idle - * play: dl=0, since there is no pause; - * rec: rl=0 - */ - if ( rd && d->dbuf_out.dl == 0 && d->dbuf_in.rl == 0 ) { - /* must swap channels, but also save dl */ - int c = d->dbuf_in.chan ; - int dl = d->dbuf_in.dl ; - d->dbuf_in.chan = d->dbuf_out.chan; - d->dbuf_out.chan = c ; - reset_dbuf(& (d->dbuf_in), SND_CHAN_RD ); - reset_dbuf(& (d->dbuf_out), SND_CHAN_WR ); - d->dbuf_in.dl = dl ; - printf("swapped -- now dma %d:%d\n", - d->dbuf_out.chan, d->dbuf_in.chan); - } - } - - /* - * XXX note: c1 and l should be set basing on d->rec_fmt, - * but there is no choice once a 16 or 8-bit channel - * is assigned. This means that if the application - * tries to use a bad format, the sound will not be nice. - */ - if ( b->chan > 4 - || (rd && d->rec_fmt == AFMT_S16_LE) - || (!rd && d->play_fmt == AFMT_S16_LE) - ) { - c = DSP_F16_AUTO | DSP_F16_FIFO_ON | DSP_DMA16 ; - c1 = DSP_F16_SIGNED ; - l /= 2 ; - } else { - c = DSP_F16_AUTO | DSP_F16_FIFO_ON | DSP_DMA8 ; - c1 = 0 ; - } - c |= (rd) ? DSP_F16_ADC : DSP_F16_DAC ; - if (d->flags & SND_F_STEREO) - c1 |= DSP_F16_STEREO ; - - sb_cmd(d->io_base, c ); - sb_cmd3(d->io_base, c1 , l - 1) ; - } else if (d->bd_flags & BD_F_ESS) { - u_long fmt = rd ? d->rec_fmt : d->play_fmt; - - DEB(printf("SND_CB_START: %s (%d)\n", rd ? "rd" : "wr", l)); - if (fmt == AFMT_S16_LE) - l >>= 1; - l--; - if (!rd) - sb_cmd(d->io_base, DSP_CMD_SPKON); - ess_write(d->io_base, 0xa4, l); - ess_write(d->io_base, 0xa5, l >> 8); - ess_write(d->io_base, 0xb8, - ess_read(d->io_base, 0xb8) | (rd ? 0x0f : 0x05)); - } else { /* SBPro -- stereo not supported */ - u_char c ; - if (!rd) - sb_cmd(d->io_base, DSP_CMD_SPKON); - /* code for the SB2 and SB3, only MONO */ - if (d->bd_flags & BD_F_HISPEED) - c = (rd) ? 0x98 : 0x90 ; - else - c = (rd) ? 0x2c : 0x1c ; - if (d->flags & SND_F_STEREO) - sb_setmixer(d->io_base, 0xe, 2 ); - else - sb_setmixer(d->io_base, 0xe, 0 ); - /* - * some ESS extensions -- they can do 16 bits - */ - if ( (rd && d->rec_fmt == AFMT_S16_LE) || - (!rd && d->play_fmt == AFMT_S16_LE) ) { - c |= 1; - l /= 2 ; - } - sb_cmd3(d->io_base, 0x48 , l - 1) ; - sb_cmd(d->io_base, c ) ; - } - break; - - case SND_CB_ABORT : /* XXX */ - case SND_CB_STOP : - { - int cmd = DSP_CMD_DMAPAUSE_8 ; /* default: halt 8 bit chan */ - DEB(printf("SND_CB_XXX: reason 0x%x\n", reason)); - if ( b->chan > 4 - || (rd && d->rec_fmt == AFMT_S16_LE) - || (!rd && d->play_fmt == AFMT_S16_LE) - ) - cmd = DSP_CMD_DMAPAUSE_16 ; - if (d->bd_flags & BD_F_HISPEED) { - sb_reset_dsp(d->io_base); - if (d->bd_flags & BD_F_ESS) - sb_cmd(d->io_base, 0xc6 ); /* enable extended ESS mode */ - d->flags |= SND_F_INIT ; - } else { - sb_cmd(d->io_base, cmd); /* pause dma. */ - /* - * The above seems to have the undocumented side effect of - * blocking the other side as well. If the other - * channel was active (SB16) I have to re-enable it :( - */ - if ( (rd && d->dbuf_out.dl) || - (!rd && d->dbuf_in.dl) ) - sb_cmd(d->io_base, cmd == DSP_CMD_DMAPAUSE_8 ? - 0xd6 : 0xd4); /* continue other dma */ - } - if (d->bd_flags & BD_F_SB16X) { - /* restore possible swapped channels. - * The default is to be ready for play. - * XXX right now, it kills all input on overflow - */ - if ( rd && d->dbuf_out.dl == 0 ) { - /* must swap channels ? */ - int c = d->dbuf_in.chan ; - d->dbuf_in.chan = d->dbuf_out.chan; - d->dbuf_out.chan = c ; - reset_dbuf(& (d->dbuf_in), SND_CHAN_RD ); - reset_dbuf(& (d->dbuf_out), SND_CHAN_WR ); - printf("restored -- now dma %d:%d\n", - d->dbuf_out.chan, d->dbuf_in.chan); - } - } - } - DEB( sb_cmd(d->io_base, DSP_CMD_SPKOFF) ); /* speaker off */ - break ; + sb_cmd(sb, DSP_CMD_GETVER); /* Get version */ + sb->bd_id = (sb_get_byte(sb) << 8) | sb_get_byte(sb); - } - return 0 ; -} + switch (sb->bd_id >> 8) { + case 1: /* old sound blaster has nothing... */ + case 2: + fmt = "SoundBlaster %d.%d" ; /* default */ + break; -/* - * The second part of the file contains all functions specific to - * the board and (usually) not exported to other modules. - */ + case 3: + fmt = "SoundBlaster Pro %d.%d"; + if (sb->bd_id == 0x301) { + int essver, rev; + + /* Try to detect ESS chips. */ + sb_cmd(sb, DSP_CMD_GETID); /* Return ident. bytes. */ + essver = (sb_get_byte(sb) << 8) | sb_get_byte(sb); + rev = essver & 0x000f; + essver &= 0xfff0; + if (essver == 0x4880) { + /* the ESS488 can be treated as an SBPRO */ + fmt = "SoundBlaster Pro (ESS488 rev %d)"; + } else if (essver == 0x6880) { + if (rev < 8) fmt = "SoundBlaster Pro (ESS688 rev %d)"; + else fmt = "SoundBlaster Pro (ESS1868 rev %d)"; + sb->bd_flags |= BD_F_ESS; + } else return ENXIO; + sb->bd_id &= 0xff00; + sb->bd_id |= ((essver & 0xf000) >> 8) | rev; + } + break; -int -sb_reset_dsp(int io_base) -{ - int loopc; - - outb(io_base + SBDSP_RST, 3); - DELAY(100); - outb(io_base + SBDSP_RST, 0); - for (loopc = 0; loopc<100 && !(inb(DSP_DATA_AVAIL) & 0x80); loopc++) - DELAY(30); - - if (inb(DSP_READ) != 0xAA) { - DEB(printf("sb_reset_dsp 0x%x failed\n", io_base)); - return 0; /* Sorry */ - } - return 1; -} + case 4: + sb->bd_flags |= BD_F_SB16; + fmt = "SoundBlaster 16 %d.%d"; + break; -/* - * only used in sb_attach from here. - */ + default: + device_printf(dev, "failed to get SB version (%x)\n", + sb->bd_id); + return ENXIO; + + } + if ((sb->bd_id >> 8) <= 4) snprintf(buf, sizeof buf, fmt, + sb->bd_id >> 8, sb->bd_id & 0xff); + else snprintf(buf, sizeof buf, fmt, sb->bd_id & 0x000f); + device_set_desc_copy(dev, buf); + return sb_reset_dsp(sb); +} -static void -sb_dsp_init(snddev_info *d, struct isa_device *dev) +static int +sb_init(device_t dev, struct sb_info *sb) { - int i, x; - char *fmt = NULL ; - int io_base = dev->id_iobase ; + int x, irq; - d->bd_id = 0 ; + sb->bd_flags &= ~BD_F_MIX_MASK; + /* do various initializations depending on board id. */ + switch (sb->bd_id >> 8) { + case 1: /* old sound blaster has nothing... */ + break; - sb_reset_dsp(io_base); - sb_cmd(io_base, DSP_CMD_GETVER); /* Get version */ + case 2: + sb->bd_flags |= BD_F_DUP_MIDI; + if (sb->bd_id > 0x200) sb->bd_flags |= BD_F_MIX_CT1335; + break; - for (i = 10000; i; i--) { /* perhaps wait longer on a fast machine ? */ - if (inb(DSP_DATA_AVAIL) & 0x80) { /* wait for Data Ready */ - if ( (d->bd_id & 0xff00) == 0) - d->bd_id = inb(DSP_READ) << 8; /* major */ - else { - d->bd_id |= inb(DSP_READ); /* minor */ + case 3: + sb->bd_flags |= BD_F_DUP_MIDI | BD_F_MIX_CT1345; break; - } - } else - DELAY(20); - } - - /* - * now do various initializations depending on board id. - */ - - fmt = "SoundBlaster %d.%d" ; /* default */ - - switch ( d->bd_id >> 8 ) { - case 0 : - printf("\n\nFailed to get SB version (%x) - possible I/O conflict\n\n", - inb(DSP_DATA_AVAIL)); - d->bd_id = 0x100; - case 1 : /* old sound blaster has nothing... */ - break ; - - case 2 : - d->dbuf_in.chan = d->dbuf_out.chan ; /* half duplex */ - d->bd_flags |= BD_F_DUP_MIDI ; - - if (d->bd_id == 0x200) - break ; /* no mixer on the 2.0 */ - d->bd_flags &= ~BD_F_MIX_MASK ; - d->bd_flags |= BD_F_MIX_CT1335 ; - - break ; - case 4 : - fmt = "SoundBlaster 16 %d.%d"; - d->audio_fmt |= AFMT_FULLDUPLEX | AFMT_WEIRD | AFMT_S8 | AFMT_S16_LE; - d->bd_flags |= BD_F_SB16; - d->bd_flags &= ~BD_F_MIX_MASK ; - d->bd_flags |= BD_F_MIX_CT1745 ; - - /* soft irq/dma configuration */ - x = -1 ; - if (d->irq == 5) x = 2; - else if (d->irq == 7) x = 4; - else if (d->irq == 9) x = 1; - else if (d->irq == 10) x = 8; - if (x == -1) - printf("<%s>%d: bad irq %d (only 5,7,9,10 allowed)\n", - d->name, dev->id_unit, d->irq); - else - sb_setmixer(io_base, IRQ_NR, x); - if (d->dbuf_out.chan == d->dbuf_in.chan) { - printf("WARNING: sb: misconfigured secondary DMA channel\n"); - } - sb_setmixer(io_base, DMA_NR, (1 << d->dbuf_out.chan) | (1 << d->dbuf_in.chan)); - break ; - - case 3 : - d->dbuf_in.chan = d->dbuf_out.chan ; /* half duplex */ - fmt = "SoundBlaster Pro %d.%d"; - d->bd_flags |= BD_F_DUP_MIDI ; - d->bd_flags &= ~BD_F_MIX_MASK ; - d->bd_flags |= BD_F_MIX_CT1345 ; - if (d->bd_id == 0x301) { - int ess_major = 0, ess_minor = 0; - - /* - * Try to detect ESS chips. - */ - - sb_cmd(io_base, DSP_CMD_GETID); /* Return ident. bytes. */ - - for (i = 1000; i; i--) { - if (inb(DSP_DATA_AVAIL) & 0x80) { /* wait for Data Ready */ - if (ess_major == 0) - ess_major = inb(DSP_READ); - else { - ess_minor = inb(DSP_READ); - break; - } - } else - DELAY(20); - } - - if (ess_major == 0x48 && (ess_minor & 0xf0) == 0x80) { - /* the ESS488 can be treated as an SBPRO */ - printf("ESS488 (rev %d)\n", ess_minor & 0x0f); - break ; - } - else if (ess_major == 0x68 && (ess_minor & 0xf0) == 0x80) { - int rev = ess_minor & 0xf; - - if (rev >= 8) - printf("ESS1868 (rev %d)\n", rev); - else - printf("ESS688 (rev %d)\n", rev); - d->bd_flags |= BD_F_ESS; - d->audio_fmt |= AFMT_S16_LE; - - /* enable extended ESS mode */ - sb_cmd(d->io_base, 0xc6); - break; - } else { - printf("Unknown card 0x%x 0x%x -- hope it is SBPRO\n", - ess_major, ess_minor); - break ; - } - } - } + case 4: + sb->bd_flags |= BD_F_SB16 | BD_F_MIX_CT1745; + if (sb->dma16 != sb->dma8) sb->bd_flags |= BD_F_DUPLEX; + + /* soft irq/dma configuration */ + x = -1; + irq = rman_get_start(sb->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(sb, IRQ_NR, x); + sb_setmixer(sb, DMA_NR, (1 << sb->dma16) | (1 << sb->dma8)); + break; + } + return 0; +} - snprintf(d->name, sizeof(d->name), - fmt, (d->bd_id >> 8) &0xff, d->bd_id & 0xff); +static int +sb_probe(device_t dev) +{ + snddev_info *d = device_get_softc(dev); + struct sb_info *sb; + int allocated, i; + int error; + + if (isa_get_vendorid(dev)) return ENXIO; /* not yet */ + + device_set_desc(dev, "SoundBlaster"); + bzero(d, sizeof *d); + sb = (struct sb_info *)malloc(sizeof *sb, M_DEVBUF, M_NOWAIT); + if (!sb) return ENXIO; + bzero(sb, sizeof *sb); + + allocated = 0; + sb->io_rid = 0; + sb->io_base = bus_alloc_resource(dev, SYS_RES_IOPORT, &sb->io_rid, + 0, ~0, 16, RF_ACTIVE); + if (!sb->io_base) { + BVDDB(printf("sb_probe: no addr, trying (0x220, 0x240)\n")); + allocated = 1; + sb->io_rid = 0; + sb->io_base = bus_alloc_resource(dev, SYS_RES_IOPORT, + &sb->io_rid, 0x220, 0x22f, + 16, RF_ACTIVE); + if (!sb->io_base) { + sb->io_base = bus_alloc_resource(dev, SYS_RES_IOPORT, + &sb->io_rid, 0x240, + 0x24f, 16, RF_ACTIVE); + } + } + if (!sb->io_base) return ENXIO; + + error = sb_reset_dsp(sb); + if (error) goto no; + error = sb_identify_board(dev, sb); + if (error) goto no; +no: + i = sb->io_rid; + sb_release_resources(sb, dev); + if (allocated) ISA_DELETE_RESOURCE(device_get_parent(dev), dev, + SYS_RES_IOPORT, i); + return error; +} - sb_mix_init(d); +static int +sb_doattach(device_t dev, struct sb_info *sb) +{ + snddev_info *d = device_get_softc(dev); + void *ih; + int error; + char status[SND_STATUSLEN]; + + sb->irq_rid = 0; + sb->drq1_rid = 0; + sb->drq2_rid = 1; + if (sb_alloc_resources(sb, dev)) goto no; + error = sb_reset_dsp(sb); + if (error) goto no; + error = sb_identify_board(dev, sb); + if (error) goto no; + + sb_init(dev, sb); + mixer_init(d, &sb_mixer, sb); + bus_setup_intr(dev, sb->irq, INTR_TYPE_TTY, sb_intr, sb, &ih); + + if (sb->bd_flags & BD_F_SB16) + pcm_setflags(dev, pcm_getflags(dev) | SD_F_EVILSB16); + if (sb->dma16 == sb->dma8) + pcm_setflags(dev, pcm_getflags(dev) | SD_F_SIMPLEX); + if (bus_dma_tag_create(/*parent*/NULL, /*alignment*/2, /*boundary*/0, + /*lowaddr*/BUS_SPACE_MAXADDR_24BIT, + /*highaddr*/BUS_SPACE_MAXADDR, + /*filter*/NULL, /*filterarg*/NULL, + /*maxsize*/DSP_BUFFSIZE, /*nsegments*/1, + /*maxsegz*/0x3ffff, + /*flags*/0, &sb->parent_dmat) != 0) { + device_printf(dev, "unable to create dma tag\n"); + goto no; + } + + snprintf(status, SND_STATUSLEN, "at io 0x%lx irq %ld drq %d", + rman_get_start(sb->io_base), rman_get_start(sb->irq), + sb->dma8); + if (sb->dma16 != sb->dma8) snprintf(status + strlen(status), + SND_STATUSLEN - strlen(status), ":%d", sb->dma16); + + if (pcm_register(dev, sb, 1, 1)) goto no; + pcm_addchan(dev, PCMDIR_REC, &sb_chantemplate, sb); + pcm_addchan(dev, PCMDIR_PLAY, &sb_chantemplate, sb); + pcm_setstatus(dev, status); + + return 0; + +no: + sb_release_resources(sb, dev); + return ENXIO; } -static void -sb_mix_init(snddev_info *d) +static int +sb_attach(device_t dev) { - switch (d->bd_flags & BD_F_MIX_MASK) { - case BD_F_MIX_CT1345 : /* SB 3.0 has 1345 mixer */ - - d->mix_devs = SBPRO_MIXER_DEVICES ; - d->mix_rec_devs = SBPRO_RECORDING_DEVICES ; - d->mix_recsrc = SOUND_MASK_MIC ; - - sb_setmixer(d->io_base, 0, 1 ); /* reset mixer */ - sb_setmixer(d->io_base, MIC_VOL , 0x6 ); /* mic volume max */ - sb_setmixer(d->io_base, RECORD_SRC , 0x0 ); /* mic source */ - sb_setmixer(d->io_base, FM_VOL , 0x0 ); /* no midi */ - break ; - - case BD_F_MIX_CT1745 : /* SB16 mixer ... */ - - d->mix_devs = SB16_MIXER_DEVICES ; - d->mix_rec_devs = SB16_RECORDING_DEVICES ; - d->mix_recsrc = SOUND_MASK_MIC ; - } - sb_mixer_reset(d); + struct sb_info *sb; + int flags = isa_get_flags(dev); + + if (flags & DV_F_DUAL_DMA) { + ISA_SET_RESOURCE(device_get_parent(dev), dev, SYS_RES_DRQ, 1, + flags & DV_F_DRQ_MASK, 1); + } + sb = (struct sb_info *)malloc(sizeof *sb, M_DEVBUF, M_NOWAIT); + if (!sb) return ENXIO; + bzero(sb, sizeof *sb); + + /* XXX in probe should set io resource to right val instead of this */ + sb->io_rid = 0; + sb->io_base = bus_alloc_resource(dev, SYS_RES_IOPORT, &sb->io_rid, + 0, ~0, 16, RF_ACTIVE); + if (!sb->io_base) { + BVDDB(printf("sb_probe: no addr, trying (0x220, 0x240)\n")); + sb->io_rid = 0; + sb->io_base = bus_alloc_resource(dev, SYS_RES_IOPORT, + &sb->io_rid, 0x220, 0x22f, + 16, RF_ACTIVE); + if (!sb->io_base) { + sb->io_base = bus_alloc_resource(dev, SYS_RES_IOPORT, + &sb->io_rid, 0x240, + 0x24f, 16, RF_ACTIVE); + } + } + if (!sb->io_base) return ENXIO; + + return sb_doattach(dev, sb); } -/* - * Common code for the midi and pcm functions - * - * sb_cmd write a single byte to the CMD port. - * sb_cmd2 write a CMD + 1 byte arg - * sb_cmd3 write a CMD + 2 byte arg - * sb_get_byte returns a single byte from the DSP data port - * - * ess_write is actually sb_cmd2 - * ess_read access ext. regs via sb_cmd(0xc0, reg) followed by sb_get_byte - */ +static device_method_t sb_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, sb_probe), + DEVMETHOD(device_attach, sb_attach), -int -sb_cmd(int io_base, u_char val) -{ - int i; + { 0, 0 } +}; - for (i = 0; i < 1000 ; i++) { - if ((inb(io_base + SBDSP_STATUS) & 0x80) == 0) { - outb(io_base + SBDSP_CMD, val); - return 1; - } - if (i > 10) - DELAY (i > 100 ? 1000 : 10 ); - } +static driver_t sb_driver = { + "pcm", + sb_methods, + sizeof(snddev_info), +}; + +DRIVER_MODULE(sb, isa, sb_driver, pcm_devclass, 0, 0); - printf("SoundBlaster: DSP Command(0x%02x) timeout. IRQ conflict ?\n", val); - return 0; +static void +sb_intr(void *arg) +{ + struct sb_info *sb = (struct sb_info *)arg; + int reason = 3, c; + + /* + * SB < 4.0 is half duplex and has only 1 bit for int source, + * so we fake it. SB 4.x (SB16) has the int source in a separate + * register. + * The Vibra16X has separate flags for 8 and 16 bit transfers, but + * I have no idea how to tell capture from playback interrupts... + */ + if (sb->bd_flags & BD_F_SB16) { + c = sb_getmixer(sb, IRQ_STAT); + /* this tells us if the source is 8-bit or 16-bit dma. We + * have to check the io channel to map it to read or write... + */ + reason = 0; + if (c & 1) { /* 8-bit dma */ + if (sb->pch.fmt & AFMT_U8) reason |= 1; + if (sb->rch.fmt & AFMT_U8) reason |= 2; + } + if (c & 2) { /* 16-bit dma */ + if (sb->pch.fmt & AFMT_S16_LE) reason |= 1; + if (sb->rch.fmt & AFMT_S16_LE) reason |= 2; + } + } else c = 1; +#if 0 + printf("sb_intr: reason=%d c=0x%x\n", reason, c); +#endif + if ((reason & 1) && (sb->pch.buffer->dl > 0)) + chn_intr(sb->pch.channel); + if ((reason & 2) && (sb->rch.buffer->dl > 0)) + chn_intr(sb->rch.channel); + if (c & 1) sb_rd(sb, DSP_DATA_AVAIL); /* 8-bit int ack */ + if (c & 2) sb_rd(sb, DSP_DATA_AVL16); /* 16-bit int ack */ } -int -sb_cmd3(int io_base, u_char cmd, int val) +static int +sb_format(struct sb_chinfo *ch, u_int32_t format) { - if (sb_cmd(io_base, cmd)) { - sb_cmd(io_base, val & 0xff ); - sb_cmd(io_base, (val>>8) & 0xff ); - return 1 ; - } else + struct sb_info *sb = ch->parent; + ch->fmt = format; + if (sb->bd_flags & BD_F_ESS) { + int play = (ch->dir == PCMDIR_PLAY)? 1 : 0; + int b16 = (ch->fmt & AFMT_S16_LE)? 1 : 0; + int stereo = (ch->fmt & AFMT_STEREO)? 1 : 0; + + /* autoinit DMA mode */ + ess_write(sb, 0xb8, 0x04 | play? 0x00 : 0x0a); + /* mono/stereo */ + ess_write(sb, 0xa8, + (ess_read(sb, 0xa8) & ~0x03) | stereo? 0x01 : 0x02); + /* demand mode, 4 bytes/xfer */ + ess_write(sb, 0xb9, 2); + /* setup dac/adc */ + if (play) ess_write(sb, 0xb6, b16? 0x00 : 0x80); + ess_write(sb, 0xb7, 0x51 | (b16? 0x20 : 0x00)); + ess_write(sb, 0xb7, + 0x98 + (b16? 0x24 : 0x00) + (stereo? 0x00 : 0x38)); + /* irq/drq control */ + ess_write(sb, 0xb1, ess_read(sb, 0xb1) | 0x50); + ess_write(sb, 0xb2, ess_read(sb, 0xb1) | 0x50); + } return 0; } -int -sb_cmd2(int io_base, u_char cmd, int val) +static int +sb_speed(struct sb_chinfo *ch, int speed) { - if (sb_cmd(io_base, cmd)) { - sb_cmd(io_base, val & 0xff ); - return 1 ; - } else - return 0; + struct sb_info *sb = ch->parent; + int play = (ch->dir == PCMDIR_PLAY)? 1 : 0; + int stereo = (ch->fmt & AFMT_STEREO)? 1 : 0; + + if (sb->bd_flags & BD_F_SB16) { + RANGE(speed, 5000, 45000); + sb_cmd(sb, 0x42 - play); + sb_cmd(sb, speed >> 8); + sb_cmd(sb, speed & 0xff); + } else if (sb->bd_flags & BD_F_ESS) { + int t; + RANGE(speed, 5000, 49000); + if (speed > 22000) { + t = (795500 + speed / 2) / speed; + speed = (795500 + t / 2) / t; + t = (256 - t) | 0x80; + } else { + t = (397700 + speed / 2) / speed; + speed = (397700 + t / 2) / t; + t = 128 - t; + } + ess_write(sb, 0xa1, t); /* set time constant */ + t = 256 - 7160000 / (((speed * 9) / 20) * 82); + ess_write(sb, 0xa2, t); + } else { + u_char tconst; + int max_speed = 45000, tmp; + u_long flags; + + /* here enforce speed limitations - max 22050 on sb 1.x*/ + if (sb->bd_id <= 0x200) max_speed = 22050; + + /* + * SB models earlier than SB Pro have low limit for the + * input rate. Note that this is only for input, but since + * we do not support separate values for rec & play.... + */ + if (!play) { + if (sb->bd_id <= 0x200) max_speed = 13000; + else if (sb->bd_id < 0x300) max_speed = 15000; + } + RANGE(speed, 4000, max_speed); + if (stereo) speed <<= 1; + + /* + * Now the speed should be valid. Compute the value to be + * programmed into the board. + */ + if (speed > 22050) { /* High speed mode on 2.01/3.xx */ + tconst = (u_char) + ((65536 - ((256000000 + speed / 2) / speed)) + >> 8); + sb->bd_flags |= BD_F_HISPEED; + tmp = 65536 - (tconst << 8); + speed = (256000000 + tmp / 2) / tmp; + } else { + sb->bd_flags &= ~BD_F_HISPEED; + tconst = (256 - ((1000000 + speed / 2) / speed)) & 0xff; + tmp = 256 - tconst; + speed = (1000000 + tmp / 2) / tmp; + } + flags = spltty(); + sb_cmd1(sb, 0x40, tconst); /* set time constant */ + splx(flags); + if (stereo) speed >>= 1; + } + return speed; } -/* - * in the SB, there is a set of indirect "mixer" registers with - * address at offset 4, data at offset 5 - */ -void -sb_setmixer(int io_base, u_int port, u_int value) +static int +sb_start(struct sb_chinfo *ch) { - u_long flags; - - flags = spltty(); - outb(io_base + SB_MIX_ADDR, (u_char) (port & 0xff)); /* Select register */ - DELAY(10); - outb(io_base + SB_MIX_DATA, (u_char) (value & 0xff)); - DELAY(10); - splx(flags); + struct sb_info *sb = ch->parent; + int play = (ch->dir == PCMDIR_PLAY)? 1 : 0; + int b16 = (ch->fmt & AFMT_S16_LE)? 1 : 0; + int stereo = (ch->fmt & AFMT_STEREO)? 1 : 0; + int l = ch->buffer->dl; + u_char i1, i2 = 0; + + if (b16) l >>= 1; + l--; + if (play) sb_cmd(sb, DSP_CMD_SPKON); + if (sb->bd_flags & BD_F_SB16) { + i1 = DSP_F16_AUTO | DSP_F16_FIFO_ON | + (play? DSP_F16_DAC : DSP_F16_ADC); + i1 |= (b16 && (sb->bd_flags & BD_F_DUPLEX))? DSP_DMA16 : DSP_DMA8; + i2 = (stereo? DSP_F16_STEREO : 0) | (b16? DSP_F16_SIGNED : 0); + sb_cmd(sb, i1); + sb_cmd2(sb, i2, l); + } else if (sb->bd_flags & BD_F_ESS) { + ess_write(sb, 0xa4, l); + ess_write(sb, 0xa5, l >> 8); + ess_write(sb, 0xb8, ess_read(sb, 0xb8) | (play? 0x05 : 0x0f)); + } else { + if (sb->bd_flags & BD_F_HISPEED) i1 = play? 0x90 : 0x98; + else i1 = play? 0x1c : 0x2c; + sb_setmixer(sb, 0x0e, stereo? 2 : 0); + /* an ESS extension -- they can do 16 bits */ + if (b16) i1 |= 1; + sb_cmd2(sb, 0x48, l); + sb_cmd(sb, i1); + } + sb->bd_flags |= BD_F_DMARUN << b16; + return 0; } -int -sb_getmixer(int io_base, u_int port) -{ - int val; - u_long flags; - - flags = spltty(); - outb(io_base + SB_MIX_ADDR, (u_char) (port & 0xff)); /* Select register */ - DELAY(10); - val = inb(io_base + SB_MIX_DATA); - DELAY(10); - splx(flags); - - return val; -} - -u_int -sb_get_byte(int io_base) +static int +sb_stop(struct sb_chinfo *ch) { - int i; + struct sb_info *sb = ch->parent; + int play = (ch->dir == PCMDIR_PLAY)? 1 : 0; + int b16 = (ch->fmt & AFMT_S16_LE)? 1 : 0; - for (i = 1000; i; i--) - if (inb(DSP_DATA_AVAIL) & 0x80) - return inb(DSP_READ); - else - DELAY(20); - return 0xffff; + if (sb->bd_flags & BD_F_HISPEED) sb_reset_dsp(sb); + else { + sb_cmd(sb, b16? DSP_CMD_DMAPAUSE_16 : DSP_CMD_DMAPAUSE_8); + /* + * The above seems to have the undocumented side effect of + * blocking the other side as well. If the other + * channel was active (SB16) I have to re-enable it :( + */ + if (sb->bd_flags & (BD_F_DMARUN << (1 - b16))) + sb_cmd(sb, b16? 0xd4 : 0xd6 ); + } + if (play) sb_cmd(sb, DSP_CMD_SPKOFF); /* speaker off */ + sb->bd_flags &= ~(BD_F_DMARUN << b16); + return 0; } -int -ess_write(int io_base, u_char reg, int val) +/* channel interface */ +static void * +sbchan_init(void *devinfo, snd_dbuf *b, pcm_channel *c, int dir) { - return sb_cmd2(io_base, reg, val); + struct sb_info *sb = devinfo; + struct sb_chinfo *ch = (dir == PCMDIR_PLAY)? &sb->pch : &sb->rch; + + ch->parent = sb; + ch->channel = c; + ch->buffer = b; + ch->buffer->bufsize = DSP_BUFFSIZE; + if (chn_allocbuf(ch->buffer, sb->parent_dmat) == -1) return NULL; + ch->buffer->chan = (dir == PCMDIR_PLAY)? sb->dma16 : sb->dma8; + return ch; } -int -ess_read(int io_base, u_char reg) +static int +sbchan_setdir(void *data, int dir) { - if (!sb_cmd(io_base, 0xc0) || !sb_cmd(io_base, reg) ) - return 0xffff ; - return sb_get_byte(io_base); + struct sb_chinfo *ch = data; + ch->dir = dir; + return 0; } +static int +sbchan_setformat(void *data, u_int32_t format) +{ + struct sb_chinfo *ch = data; + sb_format(ch, format); + return 0; +} -/* - * various utility functions for the DSP - */ +static int +sbchan_setspeed(void *data, u_int32_t speed) +{ + struct sb_chinfo *ch = data; + return sb_speed(ch, speed); +} -/* - * dsp_speed updates the speed setting from the descriptor. make sure - * it is called at spltty(). - * Besides, it takes care of stereo setting. - */ static int -dsp_speed(snddev_info *d) +sbchan_setblocksize(void *data, u_int32_t blocksize) { - u_char tconst; - u_long flags; - int max_speed = 44100, speed = d->play_speed ; - - /* - * special code for the SB16 - */ - if (d->bd_flags & BD_F_SB16) { - RANGE (speed, 5000, 45000); - d->play_speed = d->rec_speed = speed ; - sb_cmd(d->io_base, 0x41); - sb_cmd(d->io_base, d->play_speed >> 8 ); - sb_cmd(d->io_base, d->play_speed & 0xff ); - sb_cmd(d->io_base, 0x42); - sb_cmd(d->io_base, d->rec_speed >> 8 ); - sb_cmd(d->io_base, d->rec_speed & 0xff ); - return speed ; - } - - /* - * special code for the ESS ... - */ - if (d->bd_flags & BD_F_ESS) { - int t; - RANGE (speed, 5000, 49000); - if (speed > 22000) { - t = (795500 + speed / 2) / speed; - speed = (795500 + t / 2) / t ; - t = (256 - t ) | 0x80 ; - } else { - t = (397700 + speed / 2) / speed; - speed = (397700 + t / 2) / t ; - t = 128 - t ; - } - ess_write(d->io_base, 0xa1, t); /* set time constant */ - d->play_speed = d->rec_speed = speed ; - speed = (speed * 9 ) / 20 ; - t = 256-7160000/(speed*82); - ess_write(d->io_base,0xa2,t); - return speed ; - } - - /* - * This is code for the SB3.x and lower. - * Only some models can do stereo, and only if not - * simultaneously using midi. - * At the moment we do not support either... - */ -#if 0 - d->flags &= ~SND_F_STEREO; -#endif + return blocksize; +} - /* - * here enforce speed limitations. - */ - if (d->bd_id <= 0x200) - max_speed = 22050; /* max 22050 on SB 1.X */ +static int +sbchan_trigger(void *data, int go) +{ + struct sb_chinfo *ch = data; + buf_isadma(ch->buffer, go); + if (go == PCMTRIG_START) sb_start(ch); else sb_stop(ch); + return 0; +} - /* - * SB models earlier than SB Pro have low limit for the - * input rate. Note that this is only for input, but since - * we do not support separate values for rec & play.... - */ - if (d->bd_id <= 0x200) - max_speed = 13000; - else if (d->bd_id < 0x300) - max_speed = 15000; +static int +sbchan_getptr(void *data) +{ + struct sb_chinfo *ch = data; + return buf_isadmaptr(ch->buffer); +} - RANGE(speed, 4000, max_speed); +static pcmchan_caps * +sbchan_getcaps(void *data) +{ + struct sb_chinfo *ch = data; + int p = (ch->dir == PCMDIR_PLAY)? 1 : 0; + if (ch->parent->bd_flags & BD_F_ESS) + return p? &ess_playcaps : &ess_reccaps; + else if (ch->parent->bd_id <= 0x200) + return p? &sb_playcaps : &sb_reccaps; + else if (ch->parent->bd_id >= 0x400) + return p? &sb16_playcaps : &sb16_reccaps; + else + return p? &sbpro_playcaps : &sbpro_reccaps; +} - if (d->flags & SND_F_STEREO) /* really unused right now... */ - speed *= 2; +/************************************************************/ - /* - * Now the speed should be valid. Compute the value to be - * programmed into the board. - */ +static int +sbmix_init(snd_mixer *m) +{ + struct sb_info *sb = mix_getdevinfo(m); + + switch (sb->bd_flags & BD_F_MIX_MASK) { + case BD_F_MIX_CT1345: /* SB 3.0 has 1345 mixer */ + mix_setdevs(m, SBPRO_MIXER_DEVICES); + mix_setrecdevs(m, SBPRO_RECORDING_DEVICES); + sb_setmixer(sb, 0, 1); /* reset mixer */ + sb_setmixer(sb, MIC_VOL, 0x6); /* mic volume max */ + sb_setmixer(sb, RECORD_SRC, 0x0); /* mic source */ + sb_setmixer(sb, FM_VOL, 0x0); /* no midi */ + break; - if (speed > 22050) { /* High speed mode on 2.01/3.xx */ - int tmp; + case BD_F_MIX_CT1745: /* SB16 mixer ... */ + mix_setdevs(m, SB16_MIXER_DEVICES); + mix_setrecdevs(m, SB16_RECORDING_DEVICES); + sb_setmixer(sb, 0x3c, 0x1f); /* make all output active */ + sb_setmixer(sb, 0x3d, 0); /* make all inputs-l off */ + sb_setmixer(sb, 0x3e, 0); /* make all inputs-r off */ + } + return 0; +} - tconst = (u_char) ((65536 - ((256000000 + speed / 2) / speed)) >> 8) ; - d->bd_flags |= BD_F_HISPEED ; +static int +sbmix_set(snd_mixer *m, unsigned dev, unsigned left, unsigned right) +{ + struct sb_info *sb = mix_getdevinfo(m); + int regoffs; + u_char val; + mixer_tab *iomap; + + switch (sb->bd_flags & BD_F_MIX_MASK) { + case BD_F_MIX_CT1345: + iomap = &sbpro_mix; + break; - flags = spltty(); - sb_cmd2(d->io_base, 0x40, tconst); /* set time constant */ - splx(flags); + case BD_F_MIX_CT1745: + iomap = &sb16_mix; + break; - tmp = 65536 - (tconst << 8); - speed = (256000000 + tmp / 2) / tmp; - } else { - int tmp; + default: + return -1; + /* XXX how about the SG NX Pro, iomap = sgnxpro_mix */ + } + regoffs = (*iomap)[dev][LEFT_CHN].regno; + if (regoffs == 0) return -1; + val = sb_getmixer(sb, regoffs); + change_bits(iomap, &val, dev, LEFT_CHN, left); + sb_setmixer(sb, regoffs, val); + if ((*iomap)[dev][RIGHT_CHN].regno != regoffs) { /* Change register */ + regoffs = (*iomap)[dev][RIGHT_CHN].regno; + if (regoffs != 0) { + val = sb_getmixer(sb, regoffs); /* Read the new one */ + change_bits(iomap, &val, dev, RIGHT_CHN, right); + sb_setmixer(sb, regoffs, val); + } else right = left; + } else right = left; + return left | (right << 8); +} - d->bd_flags &= ~BD_F_HISPEED ; - tconst = (256 - ((1000000 + speed / 2) / speed)) & 0xff; +static int +sbmix_setrecsrc(snd_mixer *m, u_int32_t src) +{ + struct sb_info *sb = mix_getdevinfo(m); + u_char recdev; + + switch (sb->bd_flags & BD_F_MIX_MASK) { + case BD_F_MIX_CT1345: + if (src == SOUND_MASK_LINE) recdev = 0x06; + else if (src == SOUND_MASK_CD) recdev = 0x02; + else { /* default: mic */ + src = SOUND_MASK_MIC; + recdev = 0; + } + sb_setmixer(sb, RECORD_SRC, recdev | + (sb_getmixer(sb, RECORD_SRC) & ~0x07)); + break; - flags = spltty(); - sb_cmd2(d->io_base, 0x40, tconst); /* set time constant */ - splx(flags); + case BD_F_MIX_CT1745: /* sb16 */ + recdev = 0; + if (src & SOUND_MASK_MIC) recdev |= 0x01; /* mono mic */ + if (src & SOUND_MASK_CD) recdev |= 0x06; /* l+r cd */ + if (src & SOUND_MASK_LINE) recdev |= 0x18; /* l+r line */ + if (src & SOUND_MASK_SYNTH) recdev |= 0x60; /* l+r midi */ + sb_setmixer(sb, SB16_IMASK_L, recdev); + sb_setmixer(sb, SB16_IMASK_R, recdev); + /* + * since the same volume controls apply to the input and + * output sections, the best approach to have a consistent + * behaviour among cards would be to disable the output path + * on devices which are used to record. + * However, since users like to have feedback, we only disable + * the mic -- permanently. + */ + sb_setmixer(sb, SB16_OMASK, 0x1f & ~1); + break; + } + return src; +} - tmp = 256 - tconst; - speed = (1000000 + tmp / 2) / tmp; - } +#if NPNP > 0 +static int +sbpnp_probe(device_t dev) +{ + char *s = NULL; + u_int32_t logical_id = isa_get_logicalid(dev); - if (d->flags & SND_F_STEREO) /* really unused right now... */ - speed /= 2; + switch(logical_id) { + case 0x43008c0e: /* CTL0043 */ + case 0x01008c0e: /* CTL0001 */ + s = "Vibra16X"; + break; - d->play_speed = d->rec_speed = speed; - return speed; -} + case 0x31008c0e: /* CTL0031 */ + case 0x41008c0e: /* CTL0041 */ + case 0x42008c0e: /* CTL0042 */ + case 0x45008c0e: /* CTL0045 */ + s = "SB16 PnP"; + break; -/* - * mixer support, originally in sb_mixer.c - */ + case 0x01100000: /* @@@1001 */ + s = "Avance Asound 110"; + break; -static void -sb_set_recsrc(snddev_info *d, int mask) -{ - u_char recdev ; - - mask &= d->mix_rec_devs; - switch (d->bd_flags & BD_F_MIX_MASK) { - case BD_F_MIX_CT1345 : - if (mask == SOUND_MASK_LINE) - recdev = 6 ; - else if (mask == SOUND_MASK_CD) - recdev = 2 ; - else { /* default: mic */ - mask = SOUND_MASK_MIC ; - recdev = 0 ; - } - sb_setmixer(d->io_base, RECORD_SRC, - recdev | (sb_getmixer(d->io_base, RECORD_SRC) & ~7 )); - break ; - case BD_F_MIX_CT1745 : /* sb16 */ - if (mask == 0) - mask = SOUND_MASK_MIC ; /* XXX For compatibility. Bug ? */ - recdev = 0 ; - if (mask & SOUND_MASK_MIC) - recdev |= 1 ; - if (mask & SOUND_MASK_CD) - recdev |= 6 ; /* l+r cd */ - if (mask & SOUND_MASK_LINE) - recdev |= 0x18 ; /* l+r line */ - if (mask & SOUND_MASK_SYNTH) - recdev |= 0x60 ; /* l+r midi */ - sb_setmixer(d->io_base, SB16_IMASK_L, recdev); - sb_setmixer(d->io_base, SB16_IMASK_R, recdev); - /* - * since the same volume controls apply to the input and - * output sections, the best approach to have a consistent - * behaviour among cards would be to disable the output path - * on devices which are used to record. - * However, since users like to have feedback, we only disable - * the mike -- permanently. - */ - sb_setmixer(d->io_base, SB16_OMASK, 0x1f & ~1); - break ; - } - d->mix_recsrc = mask; -} + case 0x01200000: /* @@@2001 */ + s = "Avance Logic ALS120"; + break; -static void -sb_mixer_reset(snddev_info *d) -{ - int i; - - for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) - sb_mixer_set(d, i, levels[i]); - if (d->bd_flags & BD_F_SB16) { - sb_setmixer(d->io_base, 0x3c, 0x1f); /* make all output active */ - sb_setmixer(d->io_base, 0x3d, 0); /* make all inputs-l off */ - sb_setmixer(d->io_base, 0x3e, 0); /* make all inputs-r off */ - } - sb_set_recsrc(d, SOUND_MASK_MIC); + case 0x68187316: /* ESS1868 */ + s = "ESS1868"; + break; + } + if (s) { + device_set_desc(dev, s); + return 0; + } + return ENXIO; } static int -sb_mixer_set(snddev_info *d, int dev, int value) +sbpnp_attach(device_t dev) { - int left = value & 0x000000ff; - int right = (value & 0x0000ff00) >> 8; - int regoffs; - u_char val; - mixer_tab *iomap; - -#ifdef JAZZ16 - if (d->bd_flags & BD_F_JAZZ16 && d->bd_flags & BD_F_JAZZ16_2) - return smw_mixer_set(dev, value); -#endif - - if (dev == SOUND_MIXER_RECSRC) { - sb_set_recsrc(d, value); - return 0 ; - } - if (left > 100) - left = 100; - if (right > 100) - right = 100; - - if (dev > 31) - return EINVAL ; - - if (!(d->mix_devs & (1 << dev))) /* Not supported */ - return EINVAL; - - switch ( d->bd_flags & BD_F_MIX_MASK ) { - default: - /* mixer unknown, fail... */ - return EINVAL ;/* XXX change this */ - case BD_F_MIX_CT1345 : - iomap = &sbpro_mix ; - break; - case BD_F_MIX_CT1745 : - iomap = &sb16_mix ; - break; - /* XXX how about the SG NX Pro, iomap = sgnxpro_mix */ - } - regoffs = (*iomap)[dev][LEFT_CHN].regno; - if (regoffs == 0) - return EINVAL; - - val = sb_getmixer(d->io_base, regoffs); - - change_bits(iomap, &val, dev, LEFT_CHN, left); - - d->mix_levels[dev] = left | (left << 8); - - if ((*iomap)[dev][RIGHT_CHN].regno != regoffs) { /* Change register */ - sb_setmixer(d->io_base, regoffs, val); /* Save the old one */ - regoffs = (*iomap)[dev][RIGHT_CHN].regno; - - if (regoffs == 0) - return 0 ; /* Just left channel present */ - - val = sb_getmixer(d->io_base, regoffs); /* Read the new one */ - } - change_bits(iomap, &val, dev, RIGHT_CHN, right); - - sb_setmixer(d->io_base, regoffs, val); - - d->mix_levels[dev] = left | (right << 8); - return 0 ; /* ok */ + struct sb_info *sb; + u_int32_t vend_id = isa_get_vendorid(dev); + + 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); } -/* - * now support for some PnP boards. - */ +static device_method_t sbpnp_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, sbpnp_probe), + DEVMETHOD(device_attach, sbpnp_attach), -#if NPNP > 0 -static char *ess1868_probe(u_long csn, u_long vend_id); -static void ess1868_attach(u_long csn, u_long vend_id, char *name, - struct isa_device *dev); - -static struct pnp_device ess1868 = { - "ESS1868", - ess1868_probe, - ess1868_attach, - &nsnd, /* use this for all sound cards */ - &tty_imask /* imask */ + { 0, 0 } }; -DATA_SET (pnpdevice_set, ess1868); - -static char * -ess1868_probe(u_long csn, u_long vend_id) -{ - /* - * pnp X 1 os enable drq0 3 irq0 12 port0 0x240 - */ - if (vend_id == 0x68187316) { - struct pnp_cinfo d ; - read_pnp_parms ( &d , 1 ) ; - if (d.enable == 0) { - printf("This is an ESS1868, but LDN 1 is disabled\n"); - return NULL; - } - return "ESS1868" ; - } - return NULL ; -} - -static void -ess1868_attach(u_long csn, u_long vend_id, char *name, - struct isa_device *dev) -{ - struct pnp_cinfo d ; - snddev_info tmp_d ; /* patched copy of the basic snddev_info */ - - tmp_d = sb_op_desc; - snddev_last_probed = &tmp_d; -#if 0 - read_pnp_parms ( &d , 3 ); /* disable LDN 3 */ - d.port[0] = 0 ; - d.enable = 0 ; - write_pnp_parms ( &d , 3 ); - - read_pnp_parms ( &d , 2 ); /* disable LDN 2 */ - d.port[0] = 0 ; - d.enable = 0 ; - write_pnp_parms ( &d , 2 ); - read_pnp_parms ( &d , 0 ); /* read config base */ - tmp_d.conf_base = d.port[0]; - write_pnp_parms ( &d , 0 ); -#endif - - read_pnp_parms ( &d , 1 ) ; - dev->id_iobase = d.port[0]; - d.port[1] = 0 ; - d.port[2] = 0 ; - write_pnp_parms ( &d , 1 ); - enable_pnp_card(); - - dev->id_drq = d.drq[0] ; /* primary dma */ - dev->id_irq = (1 << d.irq[0] ) ; - dev->id_intr = (inthand2_t *)pcmintr ; - dev->id_flags = 0 /* DV_F_DUAL_DMA | (d.drq[1] ) */; +static driver_t sbpnp_driver = { + "pcm", + sbpnp_methods, + sizeof(snddev_info), +}; -#if 0 - snddev_last_probed->probe(dev); /* not really necessary but doesn't harm */ -#endif - pcmattach(dev); -} +DRIVER_MODULE(sbpnp, isa, sbpnp_driver, pcm_devclass, 0, 0); -/* - * A driver for some SB16pnp and compatibles... - * - * Avance Asound 100 -- 0x01009305 - * Avance Logic ALS100+ -- 0x10019305 - * Avance Logic ASound Gold ALS120 -- 0x20019305 - * xxx -- 0x2b008c0e - * - */ +#endif /* NPNP > 0 */ -static char *sb16pnp_probe(u_long csn, u_long vend_id); -static void sb16pnp_attach(u_long csn, u_long vend_id, char *name, - struct isa_device *dev); +#endif /* NPCM > 0 */ -static struct pnp_device sb16pnp = { - "SB16pnp", - sb16pnp_probe, - sb16pnp_attach, - &nsnd, /* use this for all sound cards */ - &tty_imask /* imask */ -}; -DATA_SET (pnpdevice_set, sb16pnp); - -static char * -sb16pnp_probe(u_long csn, u_long vend_id) -{ - char *s = NULL ; - - /* - * The SB16/AWExx cards seem to differ in the fourth byte of - * the vendor id, so I have just masked it for the time being... - * Reported values are: - * SB16 Value PnP: 0x2b008c0e - * SB AWExx PnP: 0x39008c0e 0x9d008c0e 0xc3008c0e - * Vibra16X: 0xf0008c0e - */ - if (vend_id == 0xf0008c0e) - s = "Vibra16X" ; - else if ( (vend_id & 0xffffff) == (0x9d008c0e & 0xffffff) ) - s = "SB16 PnP"; - else if (vend_id == 0x01009305) - s = "Avance Asound 100" ; - else if (vend_id == 0x10019305) - s = "Avance Logic 100+" ; /* Vibra16X-class */ - else if (vend_id == 0x20019305) - s = "Avance Logic ALS120" ; /* Vibra16X-class */ - if (s) { - struct pnp_cinfo d; - read_pnp_parms(&d, 0); - if (d.enable == 0) { - printf("This is a %s, but LDN 0 is disabled\n", s); - return NULL ; - } - return s ; - } - return NULL ; -} - -static void -sb16pnp_attach(u_long csn, u_long vend_id, char *name, - struct isa_device *dev) -{ - struct pnp_cinfo d ; - snddev_info tmp_d ; /* patched copy of the basic snddev_info */ - - tmp_d = sb_op_desc; - snddev_last_probed = &tmp_d; - - read_pnp_parms ( &d , 0 ) ; - d.port[1] = 0 ; /* only the first address is used */ - dev->id_iobase = d.port[0]; - tmp_d.synth_base = d.port[2]; - write_pnp_parms ( &d , 0 ); - enable_pnp_card(); - - dev->id_drq = d.drq[0] ; /* primary dma */ - dev->id_irq = (1 << d.irq[0] ) ; - dev->id_intr = (inthand2_t *)pcmintr ; - dev->id_flags = DV_F_DUAL_DMA | (d.drq[1] ) ; - - pcm_info[dev->id_unit] = tmp_d; /* pcm_info[] will be reinitialized after */ - snddev_last_probed->probe(dev); /* not really necessary but doesn't harm */ - - if (vend_id == 0x10019305 || vend_id == 0xf0008c0e - || vend_id == 0x20019305) { - /* - * XXX please add here the vend_id for other vibra16X cards... - * And remember, must change tmp_d, not - */ - tmp_d.bd_flags |= BD_F_SB16X ; - } - pcmattach(dev); -} -#endif /* NPNP */ -#endif diff --git a/sys/dev/sound/isa/sb8.c b/sys/dev/sound/isa/sb8.c index 7544dae..2a8d336 100644 --- a/sys/dev/sound/isa/sb8.c +++ b/sys/dev/sound/isa/sb8.c @@ -1,1366 +1,1117 @@ /* - * sound/sb_dsp.c - * - * driver for the SoundBlaster and clones. - * + * Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk> * Copyright 1997,1998 Luigi Rizzo. * * Derived from files in the Voxware 3.5 distribution, * Copyright by Hannu Savolainen 1994, under the same copyright * conditions. - * + * 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. + * 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. - * - */ - -/* - * use this as a template file for board-specific drivers. - * The next two lines (and the final #endif) are in all drivers: + * 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. + * + * $Id$ */ -#include <i386/isa/snd/sound.h> +#include <dev/pcm/sound.h> #if NPCM > 0 -/* - * Begin with the board-specific include files... - */ - #define __SB_MIXER_C__ /* XXX warning... */ -#include <i386/isa/snd/sbcard.h> - -/* - * then prototypes of functions which go in the snddev_info - * (usually static, unless they are shared by other modules)... - */ +#include <dev/pcm/isa/sb.h> + +/* channel interface */ +static void *sbchan_init(void *devinfo, snd_dbuf *b, pcm_channel *c, int dir); +static int sbchan_setdir(void *data, int dir); +static int sbchan_setformat(void *data, u_int32_t format); +static int sbchan_setspeed(void *data, u_int32_t speed); +static int sbchan_setblocksize(void *data, u_int32_t blocksize); +static int sbchan_trigger(void *data, int go); +static int sbchan_getptr(void *data); +static pcmchan_caps *sbchan_getcaps(void *data); + +static pcmchan_caps sb_playcaps = { + 4000, 22050, + AFMT_U8, + AFMT_U8 +}; -static int sb_probe(struct isa_device *dev); -static int sb_attach(struct isa_device *dev); +static pcmchan_caps sb_reccaps = { + 4000, 13000, + AFMT_U8, + AFMT_U8 +}; -static d_open_t sb_dsp_open; -static d_close_t sb_dsp_close; -static d_ioctl_t sb_dsp_ioctl; -static irq_proc_t sb_intr; -static snd_callback_t sb_callback; +static pcmchan_caps sbpro_playcaps = { + 4000, 45000, + AFMT_STEREO | AFMT_U8, + AFMT_STEREO | AFMT_U8 +}; -/* - * and prototypes for other private functions defined in this module. - */ +static pcmchan_caps sbpro_reccaps = { + 4000, 15000, + AFMT_STEREO | AFMT_U8, + AFMT_STEREO | AFMT_U8 +}; -static void sb_dsp_init(snddev_info *d, struct isa_device *dev); -static void sb_mix_init(snddev_info *d); -static int sb_mixer_set(snddev_info *d, int dev, int value); -static int dsp_speed(snddev_info *d); -static void sb_mixer_reset(snddev_info *d); +static pcmchan_caps sb16_playcaps = { + 5000, 45000, + AFMT_STEREO | AFMT_S16_LE, + AFMT_STEREO | AFMT_S16_LE +}; -u_int sb_get_byte(int io_base); -int ess_write(int io_base, u_char reg, int val); -int ess_read(int io_base, u_char reg); +static pcmchan_caps sb16_reccaps = { + 5000, 45000, + AFMT_STEREO | AFMT_U8, + AFMT_STEREO | AFMT_U8 +}; -/* - * Then put here the descriptors for the various boards supported - * by this module, properly initialized. - */ +static pcmchan_caps ess_playcaps = { + 5000, 49000, + AFMT_STEREO | AFMT_U8 | AFMT_S16_LE, + AFMT_STEREO | AFMT_S16_LE +}; -snddev_info sb_op_desc = { - "basic soundblaster", +static pcmchan_caps ess_reccaps = { + 5000, 49000, + AFMT_STEREO | AFMT_U8 | AFMT_S16_LE, + AFMT_STEREO | AFMT_S16_LE +}; - SNDCARD_SB, - sb_probe, - sb_attach, +static pcm_channel sb_chantemplate = { + sbchan_init, + sbchan_setdir, + sbchan_setformat, + sbchan_setspeed, + sbchan_setblocksize, + sbchan_trigger, + sbchan_getptr, + sbchan_getcaps, +}; - sb_dsp_open, - sb_dsp_close /* sb_close */, - NULL /* use generic sndread */, - NULL /* use generic sndwrite */, - sb_dsp_ioctl, - sndselect, +#define PLAIN_SB16(x) ((((x)->bd_flags) & (BD_F_SB16|BD_F_SB16X)) == BD_F_SB16) - sb_intr, - sb_callback, +struct sb_info; - DSP_BUFFSIZE, /* bufsize */ +struct sb_chinfo { + struct sb_info *parent; + pcm_channel *channel; + snd_dbuf *buffer; + int dir; + u_int32_t fmt; +}; - AFMT_STEREO | AFMT_U8, /* audio format */ +struct sb_info { + struct resource *io_base; /* I/O address for the board */ + int io_rid; + struct resource *irq; + int irq_rid; + struct resource *drq1; /* play */ + int drq1_rid; + struct resource *drq2; /* rec */ + int drq2_rid; + bus_dma_tag_t parent_dmat; + + int dma16, dma8; + int bd_id; + u_long bd_flags; /* board-specific flags */ + struct sb_chinfo pch, rch; +}; -} ; +static int sb_rd(struct sb_info *sb, int reg); +static void sb_wr(struct sb_info *sb, int reg, u_int8_t val); +static int sb_dspready(struct sb_info *sb); +static int sb_cmd(struct sb_info *sb, u_char val); +static int sb_cmd1(struct sb_info *sb, u_char cmd, int val); +static int sb_cmd2(struct sb_info *sb, u_char cmd, int val); +static u_int sb_get_byte(struct sb_info *sb); +static int ess_write(struct sb_info *sb, u_char reg, int val); +static int ess_read(struct sb_info *sb, u_char reg); /* - * Then the file continues with the body of all functions - * directly referenced in the descriptor. + * in the SB, there is a set of indirect "mixer" registers with + * address at offset 4, data at offset 5 */ +static void sb_setmixer(struct sb_info *sb, u_int port, u_int value); +static int sb_getmixer(struct sb_info *sb, u_int port); + +static void sb_intr(void *arg); +static int sb_init(device_t dev, struct sb_info *sb); +static int sb_reset_dsp(struct sb_info *sb); + +static int sb_format(struct sb_chinfo *ch, u_int32_t format); +static int sb_speed(struct sb_chinfo *ch, int speed); +static int sb_start(struct sb_chinfo *ch); +static int sb_stop(struct sb_chinfo *ch); + +static int sbmix_init(snd_mixer *m); +static int sbmix_set(snd_mixer *m, unsigned dev, unsigned left, unsigned right); +static int sbmix_setrecsrc(snd_mixer *m, u_int32_t src); + +static snd_mixer sb_mixer = { + "SoundBlaster mixer", + sbmix_init, + sbmix_set, + sbmix_setrecsrc, +}; + +static devclass_t pcm_devclass; /* - * the probe routine for the SoundBlaster only consists in - * resetting the dsp and testing if it is there. - * Version detection etc. will be done at attach time. + * Common code for the midi and pcm functions + * + * sb_cmd write a single byte to the CMD port. + * sb_cmd1 write a CMD + 1 byte arg + * sb_cmd2 write a CMD + 2 byte arg + * sb_get_byte returns a single byte from the DSP data port * - * Remember, ISA probe routines are supposed to return the - * size of io space used. + * ess_write is actually sb_cmd1 + * ess_read access ext. regs via sb_cmd(0xc0, reg) followed by sb_get_byte */ static int -sb_probe(struct isa_device *dev) +port_rd(struct resource *port, int off) { - bzero(&pcm_info[dev->id_unit], sizeof(pcm_info[dev->id_unit]) ); - if (dev->id_iobase == -1) { - dev->id_iobase = 0x220; - BVDDB(printf("sb_probe: no address supplied, try defaults (0x220,0x240)\n");) - if (snd_conflict(dev->id_iobase)) - dev->id_iobase = 0x240; - } - if (snd_conflict(dev->id_iobase)) - return 0 ; - - if (sb_reset_dsp(dev->id_iobase)) - return 16 ; /* the SB uses 16 registers... */ - else - return 0; + return bus_space_read_1(rman_get_bustag(port), + rman_get_bushandle(port), + off); +} + +static void +port_wr(struct resource *port, int off, u_int8_t data) +{ + return bus_space_write_1(rman_get_bustag(port), + rman_get_bushandle(port), + off, data); } static int -sb_attach(struct isa_device *dev) +sb_rd(struct sb_info *sb, int reg) { - snddev_info *d = &pcm_info[dev->id_unit] ; + return port_rd(sb->io_base, reg); +} - dev->id_alive = 16 ; /* number of io ports */ - /* should be already set but just in case... */ - sb_dsp_init(d, dev); - return 0 ; +static void +sb_wr(struct sb_info *sb, int reg, u_int8_t val) +{ + port_wr(sb->io_base, reg, val); } -/* - * here are the main routines from the switches. - */ +static int +sb_dspready(struct sb_info *sb) +{ + return ((sb_rd(sb, SBDSP_STATUS) & 0x80) == 0); +} -/* - * Unlike MSS, the sb only supports a single open (does not mean - * that only a single process is using it, since it can fork - * afterwards, or pass the descriptor to another process). - * - */ static int -sb_dsp_open(dev_t i_dev, int flags, int mode, struct proc * p) +sb_dspwr(struct sb_info *sb, u_char val) { - snddev_info *d; - int unit ; - int dev; + int i; - dev = minor(i_dev); - unit = dev >> 4 ; - d = &pcm_info[unit] ; + for (i = 0; i < 1000; i++) { + if (sb_dspready(sb)) { + sb_wr(sb, SBDSP_CMD, val); + return 1; + } + if (i > 10) DELAY((i > 100)? 1000 : 10); + } + printf("sb_dspwr(0x%02x) timed out.\n", val); + return 0; +} - DEB(printf("<%s>%d : open\n", d->name, unit)); +static int +sb_cmd(struct sb_info *sb, u_char val) +{ +#if 0 + printf("sb_cmd: %x\n", val); +#endif + return sb_dspwr(sb, val); +} - if (d->flags & SND_F_BUSY) { - DEB(printf("<%s>%d open: device busy\n", d->name, unit)); - return EBUSY ; - } +static int +sb_cmd1(struct sb_info *sb, u_char cmd, int val) +{ +#if 0 + printf("sb_cmd1: %x, %x\n", cmd, val); +#endif + if (sb_dspwr(sb, cmd)) { + return sb_dspwr(sb, val & 0xff); + } else return 0; +} - d->wsel.si_pid = 0; - d->wsel.si_flags = 0; +static int +sb_cmd2(struct sb_info *sb, u_char cmd, int val) +{ +#if 0 + printf("sb_cmd2: %x, %x\n", cmd, val); +#endif + if (sb_dspwr(sb, cmd)) { + return sb_dspwr(sb, val & 0xff) && + sb_dspwr(sb, (val >> 8) & 0xff); + } else return 0; +} - d->rsel.si_pid = 0; - d->rsel.si_flags = 0; +/* + * in the SB, there is a set of indirect "mixer" registers with + * address at offset 4, data at offset 5 + */ +static void +sb_setmixer(struct sb_info *sb, u_int port, u_int value) +{ + u_long flags; + + flags = spltty(); + sb_wr(sb, SB_MIX_ADDR, (u_char) (port & 0xff)); /* Select register */ + DELAY(10); + sb_wr(sb, SB_MIX_DATA, (u_char) (value & 0xff)); + DELAY(10); + splx(flags); +} - d->dbuf_out.total = d->dbuf_out.prev_total = 0 ; - d->dbuf_in.total = d->dbuf_in.prev_total = 0 ; +static int +sb_getmixer(struct sb_info *sb, u_int port) +{ + int val; + u_long flags; - d->flags = 0 ; - d->bd_flags &= ~BD_F_HISPEED ; + flags = spltty(); + sb_wr(sb, SB_MIX_ADDR, (u_char) (port & 0xff)); /* Select register */ + DELAY(10); + val = sb_rd(sb, SB_MIX_DATA); + DELAY(10); + splx(flags); - switch ( dev & 0xf ) { - case SND_DEV_DSP16 : - if ((d->audio_fmt & AFMT_S16_LE) == 0) { - printf("sorry, 16-bit not supported on SB %d.%02d\n", - (d->bd_id >>8) & 0xff, d->bd_id & 0xff); - return ENXIO; - } - d->play_fmt = d->rec_fmt = AFMT_S16_LE ; - break; - case SND_DEV_AUDIO : - d->play_fmt = d->rec_fmt = AFMT_MU_LAW ; - break ; - case SND_DEV_DSP : - d->play_fmt = d->rec_fmt = AFMT_U8 ; - break ; - } - /* - * since the SB is not simmetric, I use the open mode to select - * which channel should be privileged, and disable I/O in the - * other direction. - * In case the board is opened RW, we don't have enough - * information on what to do. Temporarily, privilege the - * playback channel, which is used more often, and set the other - * one to U8. - */ - if ( (flags & FREAD) == 0) /* opened write only */ - d->rec_fmt = 0 ; - else if ( (flags & FWRITE) == 0) /* opened read only */ - d->play_fmt = 0 ; - else /* opened read/write */ - d->rec_fmt = (d->play_fmt == AFMT_S16_LE) ? AFMT_U8 : AFMT_S16_LE ; - - d->flags |= SND_F_BUSY ; - d->play_speed = d->rec_speed = DSP_DEFAULT_SPEED ; - - if (flags & O_NONBLOCK) - d->flags |= SND_F_NBIO ; - - sb_reset_dsp(d->io_base); - if (d->bd_flags & BD_F_ESS) - sb_cmd(d->io_base, 0xc6 ); /* enable extended ESS mode */ - ask_init(d); - - return 0; + return val; } -static int -sb_dsp_close(dev_t i_dev, int flags, int mode, struct proc * p) +static u_int +sb_get_byte(struct sb_info *sb) { - int unit; - int dev; - snddev_info *d; - u_long s; + int i; - dev = minor(i_dev); - unit = dev >> 4 ; - d = &pcm_info[unit] ; - - s = spltty(); - d->flags |= SND_F_CLOSING ; - splx(s); - snd_flush(d); + for (i = 1000; i > 0; i--) { + if (sb_rd(sb, DSP_DATA_AVAIL) & 0x80) + return sb_rd(sb, DSP_READ); + else + DELAY(20); + } + return 0xffff; +} - sb_cmd(d->io_base, DSP_CMD_SPKOFF ); /* XXX useless ? */ +static int +ess_write(struct sb_info *sb, u_char reg, int val) +{ + return sb_cmd1(sb, reg, val); +} - d->flags = 0 ; - return 0 ; +static int +ess_read(struct sb_info *sb, u_char reg) +{ + return (sb_cmd(sb, 0xc0) && sb_cmd(sb, reg))? sb_get_byte(sb) : 0xffff; } static int -sb_dsp_ioctl(dev_t i_dev, u_long cmd, caddr_t arg, int mode, struct proc * p) +sb_reset_dsp(struct sb_info *sb) { - int unit; - int dev; - snddev_info *d; - - dev = minor(i_dev); - unit = dev >> 4 ; - d = &pcm_info[unit] ; - - /* - * handle mixer calls first. Reads are in the default handler, - * so do not bother about them. - */ - if ( (cmd & MIXER_WRITE(0)) == MIXER_WRITE(0) ) - return sb_mixer_set(d, cmd & 0xff, *(int *)arg) ; - - /* - * for the remaining functions, use the default handler. - * ENOSYS means that the default handler should take care - * of implementing the ioctl. - */ - - return ENOSYS ; + sb_wr(sb, SBDSP_RST, 3); + DELAY(100); + sb_wr(sb, SBDSP_RST, 0); + if (sb_get_byte(sb) != 0xAA) { + DEB(printf("sb_reset_dsp 0x%lx failed\n", + rman_get_start(d->io_base))); + return ENXIO; /* Sorry */ + } + if (sb->bd_flags & BD_F_ESS) sb_cmd(sb, 0xc6); + return 0; } static void -sb_intr(int unit) +sb_release_resources(struct sb_info *sb, device_t dev) { - snddev_info *d = &pcm_info[unit]; - int reason = 3, c=1, io_base = d->io_base; - - DEB(printf("got sb_intr for unit %d, flags 0x%08lx\n", unit, d->flags)); - - /* - * SB < 4.0 is half duplex and has only 1 bit for int source, - * so we fake it. SB 4.x (SB16) has the int source in a separate - * register. - * The Vibra16X has separate flags for 8 and 16 bit transfers, but - * I have no idea how to tell capture from playback interrupts... - */ -#define PLAIN_SB16(x) ( ( (x) & (BD_F_SB16|BD_F_SB16X) ) == BD_F_SB16) -again: - if (d->bd_flags & BD_F_SB16) { - c = sb_getmixer(io_base, IRQ_STAT); - /* this tells us if the source is 8-bit or 16-bit dma. We - * have to check the io channel to map it to read or write... - */ - reason = 0 ; - if ( c & 1 ) { /* 8-bit dma */ - if (d->play_fmt == AFMT_U8 || d->play_fmt == AFMT_MU_LAW ) - reason |= 1; - if (d->rec_fmt == AFMT_U8 || d->rec_fmt == AFMT_MU_LAW ) - reason |= 2; - } - if ( c & 2 ) { /* 16-bit dma */ - if (d->play_fmt == AFMT_S16_LE) - reason |= 1; - if (d->rec_fmt == AFMT_S16_LE) - reason |= 2; - } - } - /* XXX previous location of ack... */ - DEB(printf("sb_intr, flags 0x%08lx reason %d c 0x%x\n", - d->flags, reason, c)); - if ( reason & 1 ) { /* possibly a write interrupt */ - if ( d->dbuf_out.dl ) - dsp_wrintr(d); - } - if ( reason & 2 ) { - if ( d->dbuf_in.dl ) - dsp_rdintr(d); - } - if ( c & 2 ) - inb(DSP_DATA_AVL16); /* 16-bit int ack */ - if (c & 1) - inb(DSP_DATA_AVAIL); /* 8-bit int ack */ - - /* - * the sb16 might have multiple sources etc. - */ - if ((d->bd_flags & BD_F_SB16) && (c & 3)) - goto again; + /* should we bus_teardown_intr here? */ + if (sb->irq) { + bus_release_resource(dev, SYS_RES_IRQ, sb->irq_rid, sb->irq); + sb->irq = 0; + } + if (sb->drq1) { + bus_release_resource(dev, SYS_RES_DRQ, sb->drq1_rid, sb->drq1); + sb->drq1 = 0; + } + if (sb->drq2) { + bus_release_resource(dev, SYS_RES_DRQ, sb->drq2_rid, sb->drq2); + sb->drq2 = 0; + } + if (sb->io_base) { + bus_release_resource(dev, SYS_RES_IOPORT, sb->io_rid, + sb->io_base); + sb->io_base = 0; + } + free(sb, M_DEVBUF); } -/* - * device-specific function called back from the dma module. - * The reason of the callback is the second argument. - * NOTE: during operations, some ioctl can be called to change - * settings (e.g. speed, channels, format), and the default - * ioctl handler will just record the change and set the - * flag SND_F_INIT. The callback routine is in charge of applying - * the changes at the next convenient time (typically, at the - * start of operations). For full duplex devices, in some cases the - * init requires both channels to be idle. - */ static int -sb_callback(snddev_info *d, int reason) +sb_alloc_resources(struct sb_info *sb, device_t dev) { - int rd = reason & SND_CB_RD ; - snd_dbuf *b = (rd) ? & (d->dbuf_in) : & (d->dbuf_out) ; - int l = b->dl ; - - switch (reason & SND_CB_REASON_MASK) { - case SND_CB_INIT : /* called with int enabled and no pending io */ - /* - * set the speed - */ - dsp_speed(d); - /* - * set the desired DMA blocksize (influences select behaviour) - */ - snd_set_blocksize(d); - /* - * since native mulaw is not present, emulate it. - */ - if ( (d->play_fmt & AFMT_MU_LAW) || (d->rec_fmt & AFMT_MU_LAW) ) - d->flags |= SND_F_XLAT8 ; - else - d->flags &= ~SND_F_XLAT8 ; - - /* - * there are too many flavours of SB for my taste... here i try to do - * the proper initialization for each one. - */ - if (PLAIN_SB16(d->bd_flags)) { - - /* the original SB16 (non-PnP, or PnP, or Vibra16C) - * can do full duplex using one 16-bit channel - * and one 8-bit channel. It needs to be programmed to - * use split format though. - * I DON'T do this for the Vibra16X because I have no idea - * of what needs to be done there... - * - * I use the following algorithm: - * 1. check which direction(s) are active; - * 2. check if we should swap dma channels - * 3. check if we can do the swap. - */ - int swap = 1 ; /* default... */ - - if (d->play_fmt == 0) { - /* do whatever the read channel wants */ - if ( d->rec_fmt == AFMT_S16_LE && d->dbuf_in.chan > 4 ) - swap = 0; - if ( d->rec_fmt != AFMT_S16_LE && d->dbuf_in.chan < 4 ) - swap = 0; - } else { - /* privilege the write channel */ - if ( d->play_fmt == AFMT_S16_LE && d->dbuf_out.chan > 4 ) - swap = 0; - if ( d->play_fmt != AFMT_S16_LE && d->dbuf_out.chan < 4 ) - swap = 0; - if ( d->rec_fmt ) { - /* check for possible config errors. - * This cannot happen at open time since even in - * case of opening rw we privilege the play - * channel. - */ - if (d->rec_fmt == d->play_fmt) { - DDB(printf("sorry, read DMA channel unavailable\n")); - } + if (!sb->io_base) + sb->io_base = bus_alloc_resource(dev, SYS_RES_IOPORT, + &sb->io_rid, 0, ~0, 1, + RF_ACTIVE); + if (!sb->irq) + sb->irq = bus_alloc_resource(dev, SYS_RES_IRQ, + &sb->irq_rid, 0, ~0, 1, + RF_ACTIVE); + if (!sb->drq1) + sb->drq1 = bus_alloc_resource(dev, SYS_RES_DRQ, + &sb->drq1_rid, 0, ~0, 1, + RF_ACTIVE); + if (!sb->drq2 && sb->drq2_rid > 0) + sb->drq2 = bus_alloc_resource(dev, SYS_RES_DRQ, + &sb->drq2_rid, 0, ~0, 1, + RF_ACTIVE); + + if (sb->io_base && sb->drq1 && sb->irq) { + sb->dma8 = rman_get_start(sb->drq1); + isa_dma_acquire(sb->dma8); + isa_dmainit(sb->dma8, DSP_BUFFSIZE); + + if (sb->drq2) { + sb->dma16 = rman_get_start(sb->drq2); + isa_dma_acquire(sb->dma16); + isa_dmainit(sb->dma16, DSP_BUFFSIZE); + } else sb->dma16 = sb->dma8; + + if (sb->dma8 > sb->dma16) { + int tmp = sb->dma16; + sb->dma16 = sb->dma8; + sb->dma8 = tmp; } - } - DEB(printf("sb16: play_fmt %d, rec_fmt %x, swap %d\n", - d->play_fmt, d->rec_fmt, swap);) - if (swap) { - int c = d->dbuf_in.chan ; - d->dbuf_in.chan = d->dbuf_out.chan; - d->dbuf_out.chan = c ; - } - } - else if (d->bd_flags & BD_F_ESS) { - u_char c; + return 0; + } else return ENXIO; +} - DEB(printf("SND_CB_INIT, play_fmt == 0x%x, rec_fmt == 0x%x\n", - (int) d->play_fmt, (int) d->rec_fmt)); +static int +sb_identify_board(device_t dev, struct sb_info *sb) +{ + char *fmt = NULL; + static char buf[64]; - /* autoinit DMA mode */ - if (d->play_fmt) - ess_write(d->io_base, 0xb8, 0x04); - else - ess_write(d->io_base, 0xb8, 0x0e); - - c = (ess_read(d->io_base, 0xa8) & ~0x03) | 0x01; - if ((d->flags & SND_F_STEREO) == 0) - c++; - ess_write(d->io_base, 0xa8, c); /* select mono/stereo */ - ess_write(d->io_base, 0xb9, 2); /* demand 4 bytes/transfer */ - - switch (d->play_fmt ? d->play_fmt : d->rec_fmt) { - case AFMT_S16_LE: - if (d->flags & SND_F_STEREO) { - /* 16 bit stereo */ - if (d->play_fmt) - ess_write(d->io_base, 0xb6, 0x00); - ess_write(d->io_base, 0xb7, 0x71); - ess_write(d->io_base, 0xb7, 0xbc); - } - else { - /* 16 bit mono */ - if (d->play_fmt) - ess_write(d->io_base, 0xb6, 0x00); - ess_write(d->io_base, 0xb7, 0x71); - ess_write(d->io_base, 0xb7, 0xf4); - } - break; - case AFMT_U8: - if (d->flags & SND_F_STEREO) { - /* 8 bit stereo */ - if (d->play_fmt) - ess_write(d->io_base, 0xb6, 0x80); - ess_write(d->io_base, 0xb7, 0x51); - ess_write(d->io_base, 0xb7, 0x98); - } - else { - /* 8 bit mono */ - if (d->play_fmt) - ess_write(d->io_base, 0xb6, 0x80); - ess_write(d->io_base, 0xb7, 0x51); - ess_write(d->io_base, 0xb7, 0xd0); - } - break; - } - ess_write(d->io_base, 0xb1, - ess_read(d->io_base, 0xb1) | 0x50); - ess_write(d->io_base, 0xb2, - ess_read(d->io_base, 0xb1) | 0x50); - } - reset_dbuf(& (d->dbuf_in), SND_CHAN_RD ); - reset_dbuf(& (d->dbuf_out), SND_CHAN_WR ); - break ; - - case SND_CB_START : /* called with int disabled */ - if (d->bd_flags & BD_F_SB16) { - u_char c, c1 ; - - if (d->bd_flags & BD_F_SB16X) { - /* just a guess: on the Vibra16X, the first - * op started takes the first dma channel, - * the second one takes the next... - * The default is to be ready for play. - */ - DEB(printf("start %s -- now dma %d:%d\n", - rd ? "rd" : "wr", - d->dbuf_out.chan, d->dbuf_in.chan);); - /* swap only if both channels are idle - * play: dl=0, since there is no pause; - * rec: rl=0 - */ - if ( rd && d->dbuf_out.dl == 0 && d->dbuf_in.rl == 0 ) { - /* must swap channels, but also save dl */ - int c = d->dbuf_in.chan ; - int dl = d->dbuf_in.dl ; - d->dbuf_in.chan = d->dbuf_out.chan; - d->dbuf_out.chan = c ; - reset_dbuf(& (d->dbuf_in), SND_CHAN_RD ); - reset_dbuf(& (d->dbuf_out), SND_CHAN_WR ); - d->dbuf_in.dl = dl ; - printf("swapped -- now dma %d:%d\n", - d->dbuf_out.chan, d->dbuf_in.chan); - } - } - - /* - * XXX note: c1 and l should be set basing on d->rec_fmt, - * but there is no choice once a 16 or 8-bit channel - * is assigned. This means that if the application - * tries to use a bad format, the sound will not be nice. - */ - if ( b->chan > 4 - || (rd && d->rec_fmt == AFMT_S16_LE) - || (!rd && d->play_fmt == AFMT_S16_LE) - ) { - c = DSP_F16_AUTO | DSP_F16_FIFO_ON | DSP_DMA16 ; - c1 = DSP_F16_SIGNED ; - l /= 2 ; - } else { - c = DSP_F16_AUTO | DSP_F16_FIFO_ON | DSP_DMA8 ; - c1 = 0 ; - } - c |= (rd) ? DSP_F16_ADC : DSP_F16_DAC ; - if (d->flags & SND_F_STEREO) - c1 |= DSP_F16_STEREO ; - - sb_cmd(d->io_base, c ); - sb_cmd3(d->io_base, c1 , l - 1) ; - } else if (d->bd_flags & BD_F_ESS) { - u_long fmt = rd ? d->rec_fmt : d->play_fmt; - - DEB(printf("SND_CB_START: %s (%d)\n", rd ? "rd" : "wr", l)); - if (fmt == AFMT_S16_LE) - l >>= 1; - l--; - if (!rd) - sb_cmd(d->io_base, DSP_CMD_SPKON); - ess_write(d->io_base, 0xa4, l); - ess_write(d->io_base, 0xa5, l >> 8); - ess_write(d->io_base, 0xb8, - ess_read(d->io_base, 0xb8) | (rd ? 0x0f : 0x05)); - } else { /* SBPro -- stereo not supported */ - u_char c ; - if (!rd) - sb_cmd(d->io_base, DSP_CMD_SPKON); - /* code for the SB2 and SB3, only MONO */ - if (d->bd_flags & BD_F_HISPEED) - c = (rd) ? 0x98 : 0x90 ; - else - c = (rd) ? 0x2c : 0x1c ; - if (d->flags & SND_F_STEREO) - sb_setmixer(d->io_base, 0xe, 2 ); - else - sb_setmixer(d->io_base, 0xe, 0 ); - /* - * some ESS extensions -- they can do 16 bits - */ - if ( (rd && d->rec_fmt == AFMT_S16_LE) || - (!rd && d->play_fmt == AFMT_S16_LE) ) { - c |= 1; - l /= 2 ; - } - sb_cmd3(d->io_base, 0x48 , l - 1) ; - sb_cmd(d->io_base, c ) ; - } - break; - - case SND_CB_ABORT : /* XXX */ - case SND_CB_STOP : - { - int cmd = DSP_CMD_DMAPAUSE_8 ; /* default: halt 8 bit chan */ - DEB(printf("SND_CB_XXX: reason 0x%x\n", reason)); - if ( b->chan > 4 - || (rd && d->rec_fmt == AFMT_S16_LE) - || (!rd && d->play_fmt == AFMT_S16_LE) - ) - cmd = DSP_CMD_DMAPAUSE_16 ; - if (d->bd_flags & BD_F_HISPEED) { - sb_reset_dsp(d->io_base); - if (d->bd_flags & BD_F_ESS) - sb_cmd(d->io_base, 0xc6 ); /* enable extended ESS mode */ - d->flags |= SND_F_INIT ; - } else { - sb_cmd(d->io_base, cmd); /* pause dma. */ - /* - * The above seems to have the undocumented side effect of - * blocking the other side as well. If the other - * channel was active (SB16) I have to re-enable it :( - */ - if ( (rd && d->dbuf_out.dl) || - (!rd && d->dbuf_in.dl) ) - sb_cmd(d->io_base, cmd == DSP_CMD_DMAPAUSE_8 ? - 0xd6 : 0xd4); /* continue other dma */ - } - if (d->bd_flags & BD_F_SB16X) { - /* restore possible swapped channels. - * The default is to be ready for play. - * XXX right now, it kills all input on overflow - */ - if ( rd && d->dbuf_out.dl == 0 ) { - /* must swap channels ? */ - int c = d->dbuf_in.chan ; - d->dbuf_in.chan = d->dbuf_out.chan; - d->dbuf_out.chan = c ; - reset_dbuf(& (d->dbuf_in), SND_CHAN_RD ); - reset_dbuf(& (d->dbuf_out), SND_CHAN_WR ); - printf("restored -- now dma %d:%d\n", - d->dbuf_out.chan, d->dbuf_in.chan); - } - } - } - DEB( sb_cmd(d->io_base, DSP_CMD_SPKOFF) ); /* speaker off */ - break ; + sb_cmd(sb, DSP_CMD_GETVER); /* Get version */ + sb->bd_id = (sb_get_byte(sb) << 8) | sb_get_byte(sb); - } - return 0 ; -} + switch (sb->bd_id >> 8) { + case 1: /* old sound blaster has nothing... */ + case 2: + fmt = "SoundBlaster %d.%d" ; /* default */ + break; -/* - * The second part of the file contains all functions specific to - * the board and (usually) not exported to other modules. - */ + case 3: + fmt = "SoundBlaster Pro %d.%d"; + if (sb->bd_id == 0x301) { + int essver, rev; + + /* Try to detect ESS chips. */ + sb_cmd(sb, DSP_CMD_GETID); /* Return ident. bytes. */ + essver = (sb_get_byte(sb) << 8) | sb_get_byte(sb); + rev = essver & 0x000f; + essver &= 0xfff0; + if (essver == 0x4880) { + /* the ESS488 can be treated as an SBPRO */ + fmt = "SoundBlaster Pro (ESS488 rev %d)"; + } else if (essver == 0x6880) { + if (rev < 8) fmt = "SoundBlaster Pro (ESS688 rev %d)"; + else fmt = "SoundBlaster Pro (ESS1868 rev %d)"; + sb->bd_flags |= BD_F_ESS; + } else return ENXIO; + sb->bd_id &= 0xff00; + sb->bd_id |= ((essver & 0xf000) >> 8) | rev; + } + break; -int -sb_reset_dsp(int io_base) -{ - int loopc; - - outb(io_base + SBDSP_RST, 3); - DELAY(100); - outb(io_base + SBDSP_RST, 0); - for (loopc = 0; loopc<100 && !(inb(DSP_DATA_AVAIL) & 0x80); loopc++) - DELAY(30); - - if (inb(DSP_READ) != 0xAA) { - DEB(printf("sb_reset_dsp 0x%x failed\n", io_base)); - return 0; /* Sorry */ - } - return 1; -} + case 4: + sb->bd_flags |= BD_F_SB16; + fmt = "SoundBlaster 16 %d.%d"; + break; -/* - * only used in sb_attach from here. - */ + default: + device_printf(dev, "failed to get SB version (%x)\n", + sb->bd_id); + return ENXIO; + + } + if ((sb->bd_id >> 8) <= 4) snprintf(buf, sizeof buf, fmt, + sb->bd_id >> 8, sb->bd_id & 0xff); + else snprintf(buf, sizeof buf, fmt, sb->bd_id & 0x000f); + device_set_desc_copy(dev, buf); + return sb_reset_dsp(sb); +} -static void -sb_dsp_init(snddev_info *d, struct isa_device *dev) +static int +sb_init(device_t dev, struct sb_info *sb) { - int i, x; - char *fmt = NULL ; - int io_base = dev->id_iobase ; + int x, irq; - d->bd_id = 0 ; + sb->bd_flags &= ~BD_F_MIX_MASK; + /* do various initializations depending on board id. */ + switch (sb->bd_id >> 8) { + case 1: /* old sound blaster has nothing... */ + break; - sb_reset_dsp(io_base); - sb_cmd(io_base, DSP_CMD_GETVER); /* Get version */ + case 2: + sb->bd_flags |= BD_F_DUP_MIDI; + if (sb->bd_id > 0x200) sb->bd_flags |= BD_F_MIX_CT1335; + break; - for (i = 10000; i; i--) { /* perhaps wait longer on a fast machine ? */ - if (inb(DSP_DATA_AVAIL) & 0x80) { /* wait for Data Ready */ - if ( (d->bd_id & 0xff00) == 0) - d->bd_id = inb(DSP_READ) << 8; /* major */ - else { - d->bd_id |= inb(DSP_READ); /* minor */ + case 3: + sb->bd_flags |= BD_F_DUP_MIDI | BD_F_MIX_CT1345; break; - } - } else - DELAY(20); - } - - /* - * now do various initializations depending on board id. - */ - - fmt = "SoundBlaster %d.%d" ; /* default */ - - switch ( d->bd_id >> 8 ) { - case 0 : - printf("\n\nFailed to get SB version (%x) - possible I/O conflict\n\n", - inb(DSP_DATA_AVAIL)); - d->bd_id = 0x100; - case 1 : /* old sound blaster has nothing... */ - break ; - - case 2 : - d->dbuf_in.chan = d->dbuf_out.chan ; /* half duplex */ - d->bd_flags |= BD_F_DUP_MIDI ; - - if (d->bd_id == 0x200) - break ; /* no mixer on the 2.0 */ - d->bd_flags &= ~BD_F_MIX_MASK ; - d->bd_flags |= BD_F_MIX_CT1335 ; - - break ; - case 4 : - fmt = "SoundBlaster 16 %d.%d"; - d->audio_fmt |= AFMT_FULLDUPLEX | AFMT_WEIRD | AFMT_S8 | AFMT_S16_LE; - d->bd_flags |= BD_F_SB16; - d->bd_flags &= ~BD_F_MIX_MASK ; - d->bd_flags |= BD_F_MIX_CT1745 ; - - /* soft irq/dma configuration */ - x = -1 ; - if (d->irq == 5) x = 2; - else if (d->irq == 7) x = 4; - else if (d->irq == 9) x = 1; - else if (d->irq == 10) x = 8; - if (x == -1) - printf("<%s>%d: bad irq %d (only 5,7,9,10 allowed)\n", - d->name, dev->id_unit, d->irq); - else - sb_setmixer(io_base, IRQ_NR, x); - if (d->dbuf_out.chan == d->dbuf_in.chan) { - printf("WARNING: sb: misconfigured secondary DMA channel\n"); - } - sb_setmixer(io_base, DMA_NR, (1 << d->dbuf_out.chan) | (1 << d->dbuf_in.chan)); - break ; - - case 3 : - d->dbuf_in.chan = d->dbuf_out.chan ; /* half duplex */ - fmt = "SoundBlaster Pro %d.%d"; - d->bd_flags |= BD_F_DUP_MIDI ; - d->bd_flags &= ~BD_F_MIX_MASK ; - d->bd_flags |= BD_F_MIX_CT1345 ; - if (d->bd_id == 0x301) { - int ess_major = 0, ess_minor = 0; - - /* - * Try to detect ESS chips. - */ - - sb_cmd(io_base, DSP_CMD_GETID); /* Return ident. bytes. */ - - for (i = 1000; i; i--) { - if (inb(DSP_DATA_AVAIL) & 0x80) { /* wait for Data Ready */ - if (ess_major == 0) - ess_major = inb(DSP_READ); - else { - ess_minor = inb(DSP_READ); - break; - } - } else - DELAY(20); - } - - if (ess_major == 0x48 && (ess_minor & 0xf0) == 0x80) { - /* the ESS488 can be treated as an SBPRO */ - printf("ESS488 (rev %d)\n", ess_minor & 0x0f); - break ; - } - else if (ess_major == 0x68 && (ess_minor & 0xf0) == 0x80) { - int rev = ess_minor & 0xf; - - if (rev >= 8) - printf("ESS1868 (rev %d)\n", rev); - else - printf("ESS688 (rev %d)\n", rev); - d->bd_flags |= BD_F_ESS; - d->audio_fmt |= AFMT_S16_LE; - - /* enable extended ESS mode */ - sb_cmd(d->io_base, 0xc6); - break; - } else { - printf("Unknown card 0x%x 0x%x -- hope it is SBPRO\n", - ess_major, ess_minor); - break ; - } - } - } + case 4: + sb->bd_flags |= BD_F_SB16 | BD_F_MIX_CT1745; + if (sb->dma16 != sb->dma8) sb->bd_flags |= BD_F_DUPLEX; + + /* soft irq/dma configuration */ + x = -1; + irq = rman_get_start(sb->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(sb, IRQ_NR, x); + sb_setmixer(sb, DMA_NR, (1 << sb->dma16) | (1 << sb->dma8)); + break; + } + return 0; +} - snprintf(d->name, sizeof(d->name), - fmt, (d->bd_id >> 8) &0xff, d->bd_id & 0xff); +static int +sb_probe(device_t dev) +{ + snddev_info *d = device_get_softc(dev); + struct sb_info *sb; + int allocated, i; + int error; + + if (isa_get_vendorid(dev)) return ENXIO; /* not yet */ + + device_set_desc(dev, "SoundBlaster"); + bzero(d, sizeof *d); + sb = (struct sb_info *)malloc(sizeof *sb, M_DEVBUF, M_NOWAIT); + if (!sb) return ENXIO; + bzero(sb, sizeof *sb); + + allocated = 0; + sb->io_rid = 0; + sb->io_base = bus_alloc_resource(dev, SYS_RES_IOPORT, &sb->io_rid, + 0, ~0, 16, RF_ACTIVE); + if (!sb->io_base) { + BVDDB(printf("sb_probe: no addr, trying (0x220, 0x240)\n")); + allocated = 1; + sb->io_rid = 0; + sb->io_base = bus_alloc_resource(dev, SYS_RES_IOPORT, + &sb->io_rid, 0x220, 0x22f, + 16, RF_ACTIVE); + if (!sb->io_base) { + sb->io_base = bus_alloc_resource(dev, SYS_RES_IOPORT, + &sb->io_rid, 0x240, + 0x24f, 16, RF_ACTIVE); + } + } + if (!sb->io_base) return ENXIO; + + error = sb_reset_dsp(sb); + if (error) goto no; + error = sb_identify_board(dev, sb); + if (error) goto no; +no: + i = sb->io_rid; + sb_release_resources(sb, dev); + if (allocated) ISA_DELETE_RESOURCE(device_get_parent(dev), dev, + SYS_RES_IOPORT, i); + return error; +} - sb_mix_init(d); +static int +sb_doattach(device_t dev, struct sb_info *sb) +{ + snddev_info *d = device_get_softc(dev); + void *ih; + int error; + char status[SND_STATUSLEN]; + + sb->irq_rid = 0; + sb->drq1_rid = 0; + sb->drq2_rid = 1; + if (sb_alloc_resources(sb, dev)) goto no; + error = sb_reset_dsp(sb); + if (error) goto no; + error = sb_identify_board(dev, sb); + if (error) goto no; + + sb_init(dev, sb); + mixer_init(d, &sb_mixer, sb); + bus_setup_intr(dev, sb->irq, INTR_TYPE_TTY, sb_intr, sb, &ih); + + if (sb->bd_flags & BD_F_SB16) + pcm_setflags(dev, pcm_getflags(dev) | SD_F_EVILSB16); + if (sb->dma16 == sb->dma8) + pcm_setflags(dev, pcm_getflags(dev) | SD_F_SIMPLEX); + if (bus_dma_tag_create(/*parent*/NULL, /*alignment*/2, /*boundary*/0, + /*lowaddr*/BUS_SPACE_MAXADDR_24BIT, + /*highaddr*/BUS_SPACE_MAXADDR, + /*filter*/NULL, /*filterarg*/NULL, + /*maxsize*/DSP_BUFFSIZE, /*nsegments*/1, + /*maxsegz*/0x3ffff, + /*flags*/0, &sb->parent_dmat) != 0) { + device_printf(dev, "unable to create dma tag\n"); + goto no; + } + + snprintf(status, SND_STATUSLEN, "at io 0x%lx irq %ld drq %d", + rman_get_start(sb->io_base), rman_get_start(sb->irq), + sb->dma8); + if (sb->dma16 != sb->dma8) snprintf(status + strlen(status), + SND_STATUSLEN - strlen(status), ":%d", sb->dma16); + + if (pcm_register(dev, sb, 1, 1)) goto no; + pcm_addchan(dev, PCMDIR_REC, &sb_chantemplate, sb); + pcm_addchan(dev, PCMDIR_PLAY, &sb_chantemplate, sb); + pcm_setstatus(dev, status); + + return 0; + +no: + sb_release_resources(sb, dev); + return ENXIO; } -static void -sb_mix_init(snddev_info *d) +static int +sb_attach(device_t dev) { - switch (d->bd_flags & BD_F_MIX_MASK) { - case BD_F_MIX_CT1345 : /* SB 3.0 has 1345 mixer */ - - d->mix_devs = SBPRO_MIXER_DEVICES ; - d->mix_rec_devs = SBPRO_RECORDING_DEVICES ; - d->mix_recsrc = SOUND_MASK_MIC ; - - sb_setmixer(d->io_base, 0, 1 ); /* reset mixer */ - sb_setmixer(d->io_base, MIC_VOL , 0x6 ); /* mic volume max */ - sb_setmixer(d->io_base, RECORD_SRC , 0x0 ); /* mic source */ - sb_setmixer(d->io_base, FM_VOL , 0x0 ); /* no midi */ - break ; - - case BD_F_MIX_CT1745 : /* SB16 mixer ... */ - - d->mix_devs = SB16_MIXER_DEVICES ; - d->mix_rec_devs = SB16_RECORDING_DEVICES ; - d->mix_recsrc = SOUND_MASK_MIC ; - } - sb_mixer_reset(d); + struct sb_info *sb; + int flags = isa_get_flags(dev); + + if (flags & DV_F_DUAL_DMA) { + ISA_SET_RESOURCE(device_get_parent(dev), dev, SYS_RES_DRQ, 1, + flags & DV_F_DRQ_MASK, 1); + } + sb = (struct sb_info *)malloc(sizeof *sb, M_DEVBUF, M_NOWAIT); + if (!sb) return ENXIO; + bzero(sb, sizeof *sb); + + /* XXX in probe should set io resource to right val instead of this */ + sb->io_rid = 0; + sb->io_base = bus_alloc_resource(dev, SYS_RES_IOPORT, &sb->io_rid, + 0, ~0, 16, RF_ACTIVE); + if (!sb->io_base) { + BVDDB(printf("sb_probe: no addr, trying (0x220, 0x240)\n")); + sb->io_rid = 0; + sb->io_base = bus_alloc_resource(dev, SYS_RES_IOPORT, + &sb->io_rid, 0x220, 0x22f, + 16, RF_ACTIVE); + if (!sb->io_base) { + sb->io_base = bus_alloc_resource(dev, SYS_RES_IOPORT, + &sb->io_rid, 0x240, + 0x24f, 16, RF_ACTIVE); + } + } + if (!sb->io_base) return ENXIO; + + return sb_doattach(dev, sb); } -/* - * Common code for the midi and pcm functions - * - * sb_cmd write a single byte to the CMD port. - * sb_cmd2 write a CMD + 1 byte arg - * sb_cmd3 write a CMD + 2 byte arg - * sb_get_byte returns a single byte from the DSP data port - * - * ess_write is actually sb_cmd2 - * ess_read access ext. regs via sb_cmd(0xc0, reg) followed by sb_get_byte - */ +static device_method_t sb_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, sb_probe), + DEVMETHOD(device_attach, sb_attach), -int -sb_cmd(int io_base, u_char val) -{ - int i; + { 0, 0 } +}; - for (i = 0; i < 1000 ; i++) { - if ((inb(io_base + SBDSP_STATUS) & 0x80) == 0) { - outb(io_base + SBDSP_CMD, val); - return 1; - } - if (i > 10) - DELAY (i > 100 ? 1000 : 10 ); - } +static driver_t sb_driver = { + "pcm", + sb_methods, + sizeof(snddev_info), +}; + +DRIVER_MODULE(sb, isa, sb_driver, pcm_devclass, 0, 0); - printf("SoundBlaster: DSP Command(0x%02x) timeout. IRQ conflict ?\n", val); - return 0; +static void +sb_intr(void *arg) +{ + struct sb_info *sb = (struct sb_info *)arg; + int reason = 3, c; + + /* + * SB < 4.0 is half duplex and has only 1 bit for int source, + * so we fake it. SB 4.x (SB16) has the int source in a separate + * register. + * The Vibra16X has separate flags for 8 and 16 bit transfers, but + * I have no idea how to tell capture from playback interrupts... + */ + if (sb->bd_flags & BD_F_SB16) { + c = sb_getmixer(sb, IRQ_STAT); + /* this tells us if the source is 8-bit or 16-bit dma. We + * have to check the io channel to map it to read or write... + */ + reason = 0; + if (c & 1) { /* 8-bit dma */ + if (sb->pch.fmt & AFMT_U8) reason |= 1; + if (sb->rch.fmt & AFMT_U8) reason |= 2; + } + if (c & 2) { /* 16-bit dma */ + if (sb->pch.fmt & AFMT_S16_LE) reason |= 1; + if (sb->rch.fmt & AFMT_S16_LE) reason |= 2; + } + } else c = 1; +#if 0 + printf("sb_intr: reason=%d c=0x%x\n", reason, c); +#endif + if ((reason & 1) && (sb->pch.buffer->dl > 0)) + chn_intr(sb->pch.channel); + if ((reason & 2) && (sb->rch.buffer->dl > 0)) + chn_intr(sb->rch.channel); + if (c & 1) sb_rd(sb, DSP_DATA_AVAIL); /* 8-bit int ack */ + if (c & 2) sb_rd(sb, DSP_DATA_AVL16); /* 16-bit int ack */ } -int -sb_cmd3(int io_base, u_char cmd, int val) +static int +sb_format(struct sb_chinfo *ch, u_int32_t format) { - if (sb_cmd(io_base, cmd)) { - sb_cmd(io_base, val & 0xff ); - sb_cmd(io_base, (val>>8) & 0xff ); - return 1 ; - } else + struct sb_info *sb = ch->parent; + ch->fmt = format; + if (sb->bd_flags & BD_F_ESS) { + int play = (ch->dir == PCMDIR_PLAY)? 1 : 0; + int b16 = (ch->fmt & AFMT_S16_LE)? 1 : 0; + int stereo = (ch->fmt & AFMT_STEREO)? 1 : 0; + + /* autoinit DMA mode */ + ess_write(sb, 0xb8, 0x04 | play? 0x00 : 0x0a); + /* mono/stereo */ + ess_write(sb, 0xa8, + (ess_read(sb, 0xa8) & ~0x03) | stereo? 0x01 : 0x02); + /* demand mode, 4 bytes/xfer */ + ess_write(sb, 0xb9, 2); + /* setup dac/adc */ + if (play) ess_write(sb, 0xb6, b16? 0x00 : 0x80); + ess_write(sb, 0xb7, 0x51 | (b16? 0x20 : 0x00)); + ess_write(sb, 0xb7, + 0x98 + (b16? 0x24 : 0x00) + (stereo? 0x00 : 0x38)); + /* irq/drq control */ + ess_write(sb, 0xb1, ess_read(sb, 0xb1) | 0x50); + ess_write(sb, 0xb2, ess_read(sb, 0xb1) | 0x50); + } return 0; } -int -sb_cmd2(int io_base, u_char cmd, int val) +static int +sb_speed(struct sb_chinfo *ch, int speed) { - if (sb_cmd(io_base, cmd)) { - sb_cmd(io_base, val & 0xff ); - return 1 ; - } else - return 0; + struct sb_info *sb = ch->parent; + int play = (ch->dir == PCMDIR_PLAY)? 1 : 0; + int stereo = (ch->fmt & AFMT_STEREO)? 1 : 0; + + if (sb->bd_flags & BD_F_SB16) { + RANGE(speed, 5000, 45000); + sb_cmd(sb, 0x42 - play); + sb_cmd(sb, speed >> 8); + sb_cmd(sb, speed & 0xff); + } else if (sb->bd_flags & BD_F_ESS) { + int t; + RANGE(speed, 5000, 49000); + if (speed > 22000) { + t = (795500 + speed / 2) / speed; + speed = (795500 + t / 2) / t; + t = (256 - t) | 0x80; + } else { + t = (397700 + speed / 2) / speed; + speed = (397700 + t / 2) / t; + t = 128 - t; + } + ess_write(sb, 0xa1, t); /* set time constant */ + t = 256 - 7160000 / (((speed * 9) / 20) * 82); + ess_write(sb, 0xa2, t); + } else { + u_char tconst; + int max_speed = 45000, tmp; + u_long flags; + + /* here enforce speed limitations - max 22050 on sb 1.x*/ + if (sb->bd_id <= 0x200) max_speed = 22050; + + /* + * SB models earlier than SB Pro have low limit for the + * input rate. Note that this is only for input, but since + * we do not support separate values for rec & play.... + */ + if (!play) { + if (sb->bd_id <= 0x200) max_speed = 13000; + else if (sb->bd_id < 0x300) max_speed = 15000; + } + RANGE(speed, 4000, max_speed); + if (stereo) speed <<= 1; + + /* + * Now the speed should be valid. Compute the value to be + * programmed into the board. + */ + if (speed > 22050) { /* High speed mode on 2.01/3.xx */ + tconst = (u_char) + ((65536 - ((256000000 + speed / 2) / speed)) + >> 8); + sb->bd_flags |= BD_F_HISPEED; + tmp = 65536 - (tconst << 8); + speed = (256000000 + tmp / 2) / tmp; + } else { + sb->bd_flags &= ~BD_F_HISPEED; + tconst = (256 - ((1000000 + speed / 2) / speed)) & 0xff; + tmp = 256 - tconst; + speed = (1000000 + tmp / 2) / tmp; + } + flags = spltty(); + sb_cmd1(sb, 0x40, tconst); /* set time constant */ + splx(flags); + if (stereo) speed >>= 1; + } + return speed; } -/* - * in the SB, there is a set of indirect "mixer" registers with - * address at offset 4, data at offset 5 - */ -void -sb_setmixer(int io_base, u_int port, u_int value) +static int +sb_start(struct sb_chinfo *ch) { - u_long flags; - - flags = spltty(); - outb(io_base + SB_MIX_ADDR, (u_char) (port & 0xff)); /* Select register */ - DELAY(10); - outb(io_base + SB_MIX_DATA, (u_char) (value & 0xff)); - DELAY(10); - splx(flags); + struct sb_info *sb = ch->parent; + int play = (ch->dir == PCMDIR_PLAY)? 1 : 0; + int b16 = (ch->fmt & AFMT_S16_LE)? 1 : 0; + int stereo = (ch->fmt & AFMT_STEREO)? 1 : 0; + int l = ch->buffer->dl; + u_char i1, i2 = 0; + + if (b16) l >>= 1; + l--; + if (play) sb_cmd(sb, DSP_CMD_SPKON); + if (sb->bd_flags & BD_F_SB16) { + i1 = DSP_F16_AUTO | DSP_F16_FIFO_ON | + (play? DSP_F16_DAC : DSP_F16_ADC); + i1 |= (b16 && (sb->bd_flags & BD_F_DUPLEX))? DSP_DMA16 : DSP_DMA8; + i2 = (stereo? DSP_F16_STEREO : 0) | (b16? DSP_F16_SIGNED : 0); + sb_cmd(sb, i1); + sb_cmd2(sb, i2, l); + } else if (sb->bd_flags & BD_F_ESS) { + ess_write(sb, 0xa4, l); + ess_write(sb, 0xa5, l >> 8); + ess_write(sb, 0xb8, ess_read(sb, 0xb8) | (play? 0x05 : 0x0f)); + } else { + if (sb->bd_flags & BD_F_HISPEED) i1 = play? 0x90 : 0x98; + else i1 = play? 0x1c : 0x2c; + sb_setmixer(sb, 0x0e, stereo? 2 : 0); + /* an ESS extension -- they can do 16 bits */ + if (b16) i1 |= 1; + sb_cmd2(sb, 0x48, l); + sb_cmd(sb, i1); + } + sb->bd_flags |= BD_F_DMARUN << b16; + return 0; } -int -sb_getmixer(int io_base, u_int port) -{ - int val; - u_long flags; - - flags = spltty(); - outb(io_base + SB_MIX_ADDR, (u_char) (port & 0xff)); /* Select register */ - DELAY(10); - val = inb(io_base + SB_MIX_DATA); - DELAY(10); - splx(flags); - - return val; -} - -u_int -sb_get_byte(int io_base) +static int +sb_stop(struct sb_chinfo *ch) { - int i; + struct sb_info *sb = ch->parent; + int play = (ch->dir == PCMDIR_PLAY)? 1 : 0; + int b16 = (ch->fmt & AFMT_S16_LE)? 1 : 0; - for (i = 1000; i; i--) - if (inb(DSP_DATA_AVAIL) & 0x80) - return inb(DSP_READ); - else - DELAY(20); - return 0xffff; + if (sb->bd_flags & BD_F_HISPEED) sb_reset_dsp(sb); + else { + sb_cmd(sb, b16? DSP_CMD_DMAPAUSE_16 : DSP_CMD_DMAPAUSE_8); + /* + * The above seems to have the undocumented side effect of + * blocking the other side as well. If the other + * channel was active (SB16) I have to re-enable it :( + */ + if (sb->bd_flags & (BD_F_DMARUN << (1 - b16))) + sb_cmd(sb, b16? 0xd4 : 0xd6 ); + } + if (play) sb_cmd(sb, DSP_CMD_SPKOFF); /* speaker off */ + sb->bd_flags &= ~(BD_F_DMARUN << b16); + return 0; } -int -ess_write(int io_base, u_char reg, int val) +/* channel interface */ +static void * +sbchan_init(void *devinfo, snd_dbuf *b, pcm_channel *c, int dir) { - return sb_cmd2(io_base, reg, val); + struct sb_info *sb = devinfo; + struct sb_chinfo *ch = (dir == PCMDIR_PLAY)? &sb->pch : &sb->rch; + + ch->parent = sb; + ch->channel = c; + ch->buffer = b; + ch->buffer->bufsize = DSP_BUFFSIZE; + if (chn_allocbuf(ch->buffer, sb->parent_dmat) == -1) return NULL; + ch->buffer->chan = (dir == PCMDIR_PLAY)? sb->dma16 : sb->dma8; + return ch; } -int -ess_read(int io_base, u_char reg) +static int +sbchan_setdir(void *data, int dir) { - if (!sb_cmd(io_base, 0xc0) || !sb_cmd(io_base, reg) ) - return 0xffff ; - return sb_get_byte(io_base); + struct sb_chinfo *ch = data; + ch->dir = dir; + return 0; } +static int +sbchan_setformat(void *data, u_int32_t format) +{ + struct sb_chinfo *ch = data; + sb_format(ch, format); + return 0; +} -/* - * various utility functions for the DSP - */ +static int +sbchan_setspeed(void *data, u_int32_t speed) +{ + struct sb_chinfo *ch = data; + return sb_speed(ch, speed); +} -/* - * dsp_speed updates the speed setting from the descriptor. make sure - * it is called at spltty(). - * Besides, it takes care of stereo setting. - */ static int -dsp_speed(snddev_info *d) +sbchan_setblocksize(void *data, u_int32_t blocksize) { - u_char tconst; - u_long flags; - int max_speed = 44100, speed = d->play_speed ; - - /* - * special code for the SB16 - */ - if (d->bd_flags & BD_F_SB16) { - RANGE (speed, 5000, 45000); - d->play_speed = d->rec_speed = speed ; - sb_cmd(d->io_base, 0x41); - sb_cmd(d->io_base, d->play_speed >> 8 ); - sb_cmd(d->io_base, d->play_speed & 0xff ); - sb_cmd(d->io_base, 0x42); - sb_cmd(d->io_base, d->rec_speed >> 8 ); - sb_cmd(d->io_base, d->rec_speed & 0xff ); - return speed ; - } - - /* - * special code for the ESS ... - */ - if (d->bd_flags & BD_F_ESS) { - int t; - RANGE (speed, 5000, 49000); - if (speed > 22000) { - t = (795500 + speed / 2) / speed; - speed = (795500 + t / 2) / t ; - t = (256 - t ) | 0x80 ; - } else { - t = (397700 + speed / 2) / speed; - speed = (397700 + t / 2) / t ; - t = 128 - t ; - } - ess_write(d->io_base, 0xa1, t); /* set time constant */ - d->play_speed = d->rec_speed = speed ; - speed = (speed * 9 ) / 20 ; - t = 256-7160000/(speed*82); - ess_write(d->io_base,0xa2,t); - return speed ; - } - - /* - * This is code for the SB3.x and lower. - * Only some models can do stereo, and only if not - * simultaneously using midi. - * At the moment we do not support either... - */ -#if 0 - d->flags &= ~SND_F_STEREO; -#endif + return blocksize; +} - /* - * here enforce speed limitations. - */ - if (d->bd_id <= 0x200) - max_speed = 22050; /* max 22050 on SB 1.X */ +static int +sbchan_trigger(void *data, int go) +{ + struct sb_chinfo *ch = data; + buf_isadma(ch->buffer, go); + if (go == PCMTRIG_START) sb_start(ch); else sb_stop(ch); + return 0; +} - /* - * SB models earlier than SB Pro have low limit for the - * input rate. Note that this is only for input, but since - * we do not support separate values for rec & play.... - */ - if (d->bd_id <= 0x200) - max_speed = 13000; - else if (d->bd_id < 0x300) - max_speed = 15000; +static int +sbchan_getptr(void *data) +{ + struct sb_chinfo *ch = data; + return buf_isadmaptr(ch->buffer); +} - RANGE(speed, 4000, max_speed); +static pcmchan_caps * +sbchan_getcaps(void *data) +{ + struct sb_chinfo *ch = data; + int p = (ch->dir == PCMDIR_PLAY)? 1 : 0; + if (ch->parent->bd_flags & BD_F_ESS) + return p? &ess_playcaps : &ess_reccaps; + else if (ch->parent->bd_id <= 0x200) + return p? &sb_playcaps : &sb_reccaps; + else if (ch->parent->bd_id >= 0x400) + return p? &sb16_playcaps : &sb16_reccaps; + else + return p? &sbpro_playcaps : &sbpro_reccaps; +} - if (d->flags & SND_F_STEREO) /* really unused right now... */ - speed *= 2; +/************************************************************/ - /* - * Now the speed should be valid. Compute the value to be - * programmed into the board. - */ +static int +sbmix_init(snd_mixer *m) +{ + struct sb_info *sb = mix_getdevinfo(m); + + switch (sb->bd_flags & BD_F_MIX_MASK) { + case BD_F_MIX_CT1345: /* SB 3.0 has 1345 mixer */ + mix_setdevs(m, SBPRO_MIXER_DEVICES); + mix_setrecdevs(m, SBPRO_RECORDING_DEVICES); + sb_setmixer(sb, 0, 1); /* reset mixer */ + sb_setmixer(sb, MIC_VOL, 0x6); /* mic volume max */ + sb_setmixer(sb, RECORD_SRC, 0x0); /* mic source */ + sb_setmixer(sb, FM_VOL, 0x0); /* no midi */ + break; - if (speed > 22050) { /* High speed mode on 2.01/3.xx */ - int tmp; + case BD_F_MIX_CT1745: /* SB16 mixer ... */ + mix_setdevs(m, SB16_MIXER_DEVICES); + mix_setrecdevs(m, SB16_RECORDING_DEVICES); + sb_setmixer(sb, 0x3c, 0x1f); /* make all output active */ + sb_setmixer(sb, 0x3d, 0); /* make all inputs-l off */ + sb_setmixer(sb, 0x3e, 0); /* make all inputs-r off */ + } + return 0; +} - tconst = (u_char) ((65536 - ((256000000 + speed / 2) / speed)) >> 8) ; - d->bd_flags |= BD_F_HISPEED ; +static int +sbmix_set(snd_mixer *m, unsigned dev, unsigned left, unsigned right) +{ + struct sb_info *sb = mix_getdevinfo(m); + int regoffs; + u_char val; + mixer_tab *iomap; + + switch (sb->bd_flags & BD_F_MIX_MASK) { + case BD_F_MIX_CT1345: + iomap = &sbpro_mix; + break; - flags = spltty(); - sb_cmd2(d->io_base, 0x40, tconst); /* set time constant */ - splx(flags); + case BD_F_MIX_CT1745: + iomap = &sb16_mix; + break; - tmp = 65536 - (tconst << 8); - speed = (256000000 + tmp / 2) / tmp; - } else { - int tmp; + default: + return -1; + /* XXX how about the SG NX Pro, iomap = sgnxpro_mix */ + } + regoffs = (*iomap)[dev][LEFT_CHN].regno; + if (regoffs == 0) return -1; + val = sb_getmixer(sb, regoffs); + change_bits(iomap, &val, dev, LEFT_CHN, left); + sb_setmixer(sb, regoffs, val); + if ((*iomap)[dev][RIGHT_CHN].regno != regoffs) { /* Change register */ + regoffs = (*iomap)[dev][RIGHT_CHN].regno; + if (regoffs != 0) { + val = sb_getmixer(sb, regoffs); /* Read the new one */ + change_bits(iomap, &val, dev, RIGHT_CHN, right); + sb_setmixer(sb, regoffs, val); + } else right = left; + } else right = left; + return left | (right << 8); +} - d->bd_flags &= ~BD_F_HISPEED ; - tconst = (256 - ((1000000 + speed / 2) / speed)) & 0xff; +static int +sbmix_setrecsrc(snd_mixer *m, u_int32_t src) +{ + struct sb_info *sb = mix_getdevinfo(m); + u_char recdev; + + switch (sb->bd_flags & BD_F_MIX_MASK) { + case BD_F_MIX_CT1345: + if (src == SOUND_MASK_LINE) recdev = 0x06; + else if (src == SOUND_MASK_CD) recdev = 0x02; + else { /* default: mic */ + src = SOUND_MASK_MIC; + recdev = 0; + } + sb_setmixer(sb, RECORD_SRC, recdev | + (sb_getmixer(sb, RECORD_SRC) & ~0x07)); + break; - flags = spltty(); - sb_cmd2(d->io_base, 0x40, tconst); /* set time constant */ - splx(flags); + case BD_F_MIX_CT1745: /* sb16 */ + recdev = 0; + if (src & SOUND_MASK_MIC) recdev |= 0x01; /* mono mic */ + if (src & SOUND_MASK_CD) recdev |= 0x06; /* l+r cd */ + if (src & SOUND_MASK_LINE) recdev |= 0x18; /* l+r line */ + if (src & SOUND_MASK_SYNTH) recdev |= 0x60; /* l+r midi */ + sb_setmixer(sb, SB16_IMASK_L, recdev); + sb_setmixer(sb, SB16_IMASK_R, recdev); + /* + * since the same volume controls apply to the input and + * output sections, the best approach to have a consistent + * behaviour among cards would be to disable the output path + * on devices which are used to record. + * However, since users like to have feedback, we only disable + * the mic -- permanently. + */ + sb_setmixer(sb, SB16_OMASK, 0x1f & ~1); + break; + } + return src; +} - tmp = 256 - tconst; - speed = (1000000 + tmp / 2) / tmp; - } +#if NPNP > 0 +static int +sbpnp_probe(device_t dev) +{ + char *s = NULL; + u_int32_t logical_id = isa_get_logicalid(dev); - if (d->flags & SND_F_STEREO) /* really unused right now... */ - speed /= 2; + switch(logical_id) { + case 0x43008c0e: /* CTL0043 */ + case 0x01008c0e: /* CTL0001 */ + s = "Vibra16X"; + break; - d->play_speed = d->rec_speed = speed; - return speed; -} + case 0x31008c0e: /* CTL0031 */ + case 0x41008c0e: /* CTL0041 */ + case 0x42008c0e: /* CTL0042 */ + case 0x45008c0e: /* CTL0045 */ + s = "SB16 PnP"; + break; -/* - * mixer support, originally in sb_mixer.c - */ + case 0x01100000: /* @@@1001 */ + s = "Avance Asound 110"; + break; -static void -sb_set_recsrc(snddev_info *d, int mask) -{ - u_char recdev ; - - mask &= d->mix_rec_devs; - switch (d->bd_flags & BD_F_MIX_MASK) { - case BD_F_MIX_CT1345 : - if (mask == SOUND_MASK_LINE) - recdev = 6 ; - else if (mask == SOUND_MASK_CD) - recdev = 2 ; - else { /* default: mic */ - mask = SOUND_MASK_MIC ; - recdev = 0 ; - } - sb_setmixer(d->io_base, RECORD_SRC, - recdev | (sb_getmixer(d->io_base, RECORD_SRC) & ~7 )); - break ; - case BD_F_MIX_CT1745 : /* sb16 */ - if (mask == 0) - mask = SOUND_MASK_MIC ; /* XXX For compatibility. Bug ? */ - recdev = 0 ; - if (mask & SOUND_MASK_MIC) - recdev |= 1 ; - if (mask & SOUND_MASK_CD) - recdev |= 6 ; /* l+r cd */ - if (mask & SOUND_MASK_LINE) - recdev |= 0x18 ; /* l+r line */ - if (mask & SOUND_MASK_SYNTH) - recdev |= 0x60 ; /* l+r midi */ - sb_setmixer(d->io_base, SB16_IMASK_L, recdev); - sb_setmixer(d->io_base, SB16_IMASK_R, recdev); - /* - * since the same volume controls apply to the input and - * output sections, the best approach to have a consistent - * behaviour among cards would be to disable the output path - * on devices which are used to record. - * However, since users like to have feedback, we only disable - * the mike -- permanently. - */ - sb_setmixer(d->io_base, SB16_OMASK, 0x1f & ~1); - break ; - } - d->mix_recsrc = mask; -} + case 0x01200000: /* @@@2001 */ + s = "Avance Logic ALS120"; + break; -static void -sb_mixer_reset(snddev_info *d) -{ - int i; - - for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) - sb_mixer_set(d, i, levels[i]); - if (d->bd_flags & BD_F_SB16) { - sb_setmixer(d->io_base, 0x3c, 0x1f); /* make all output active */ - sb_setmixer(d->io_base, 0x3d, 0); /* make all inputs-l off */ - sb_setmixer(d->io_base, 0x3e, 0); /* make all inputs-r off */ - } - sb_set_recsrc(d, SOUND_MASK_MIC); + case 0x68187316: /* ESS1868 */ + s = "ESS1868"; + break; + } + if (s) { + device_set_desc(dev, s); + return 0; + } + return ENXIO; } static int -sb_mixer_set(snddev_info *d, int dev, int value) +sbpnp_attach(device_t dev) { - int left = value & 0x000000ff; - int right = (value & 0x0000ff00) >> 8; - int regoffs; - u_char val; - mixer_tab *iomap; - -#ifdef JAZZ16 - if (d->bd_flags & BD_F_JAZZ16 && d->bd_flags & BD_F_JAZZ16_2) - return smw_mixer_set(dev, value); -#endif - - if (dev == SOUND_MIXER_RECSRC) { - sb_set_recsrc(d, value); - return 0 ; - } - if (left > 100) - left = 100; - if (right > 100) - right = 100; - - if (dev > 31) - return EINVAL ; - - if (!(d->mix_devs & (1 << dev))) /* Not supported */ - return EINVAL; - - switch ( d->bd_flags & BD_F_MIX_MASK ) { - default: - /* mixer unknown, fail... */ - return EINVAL ;/* XXX change this */ - case BD_F_MIX_CT1345 : - iomap = &sbpro_mix ; - break; - case BD_F_MIX_CT1745 : - iomap = &sb16_mix ; - break; - /* XXX how about the SG NX Pro, iomap = sgnxpro_mix */ - } - regoffs = (*iomap)[dev][LEFT_CHN].regno; - if (regoffs == 0) - return EINVAL; - - val = sb_getmixer(d->io_base, regoffs); - - change_bits(iomap, &val, dev, LEFT_CHN, left); - - d->mix_levels[dev] = left | (left << 8); - - if ((*iomap)[dev][RIGHT_CHN].regno != regoffs) { /* Change register */ - sb_setmixer(d->io_base, regoffs, val); /* Save the old one */ - regoffs = (*iomap)[dev][RIGHT_CHN].regno; - - if (regoffs == 0) - return 0 ; /* Just left channel present */ - - val = sb_getmixer(d->io_base, regoffs); /* Read the new one */ - } - change_bits(iomap, &val, dev, RIGHT_CHN, right); - - sb_setmixer(d->io_base, regoffs, val); - - d->mix_levels[dev] = left | (right << 8); - return 0 ; /* ok */ + struct sb_info *sb; + u_int32_t vend_id = isa_get_vendorid(dev); + + 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); } -/* - * now support for some PnP boards. - */ +static device_method_t sbpnp_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, sbpnp_probe), + DEVMETHOD(device_attach, sbpnp_attach), -#if NPNP > 0 -static char *ess1868_probe(u_long csn, u_long vend_id); -static void ess1868_attach(u_long csn, u_long vend_id, char *name, - struct isa_device *dev); - -static struct pnp_device ess1868 = { - "ESS1868", - ess1868_probe, - ess1868_attach, - &nsnd, /* use this for all sound cards */ - &tty_imask /* imask */ + { 0, 0 } }; -DATA_SET (pnpdevice_set, ess1868); - -static char * -ess1868_probe(u_long csn, u_long vend_id) -{ - /* - * pnp X 1 os enable drq0 3 irq0 12 port0 0x240 - */ - if (vend_id == 0x68187316) { - struct pnp_cinfo d ; - read_pnp_parms ( &d , 1 ) ; - if (d.enable == 0) { - printf("This is an ESS1868, but LDN 1 is disabled\n"); - return NULL; - } - return "ESS1868" ; - } - return NULL ; -} - -static void -ess1868_attach(u_long csn, u_long vend_id, char *name, - struct isa_device *dev) -{ - struct pnp_cinfo d ; - snddev_info tmp_d ; /* patched copy of the basic snddev_info */ - - tmp_d = sb_op_desc; - snddev_last_probed = &tmp_d; -#if 0 - read_pnp_parms ( &d , 3 ); /* disable LDN 3 */ - d.port[0] = 0 ; - d.enable = 0 ; - write_pnp_parms ( &d , 3 ); - - read_pnp_parms ( &d , 2 ); /* disable LDN 2 */ - d.port[0] = 0 ; - d.enable = 0 ; - write_pnp_parms ( &d , 2 ); - read_pnp_parms ( &d , 0 ); /* read config base */ - tmp_d.conf_base = d.port[0]; - write_pnp_parms ( &d , 0 ); -#endif - - read_pnp_parms ( &d , 1 ) ; - dev->id_iobase = d.port[0]; - d.port[1] = 0 ; - d.port[2] = 0 ; - write_pnp_parms ( &d , 1 ); - enable_pnp_card(); - - dev->id_drq = d.drq[0] ; /* primary dma */ - dev->id_irq = (1 << d.irq[0] ) ; - dev->id_intr = (inthand2_t *)pcmintr ; - dev->id_flags = 0 /* DV_F_DUAL_DMA | (d.drq[1] ) */; +static driver_t sbpnp_driver = { + "pcm", + sbpnp_methods, + sizeof(snddev_info), +}; -#if 0 - snddev_last_probed->probe(dev); /* not really necessary but doesn't harm */ -#endif - pcmattach(dev); -} +DRIVER_MODULE(sbpnp, isa, sbpnp_driver, pcm_devclass, 0, 0); -/* - * A driver for some SB16pnp and compatibles... - * - * Avance Asound 100 -- 0x01009305 - * Avance Logic ALS100+ -- 0x10019305 - * Avance Logic ASound Gold ALS120 -- 0x20019305 - * xxx -- 0x2b008c0e - * - */ +#endif /* NPNP > 0 */ -static char *sb16pnp_probe(u_long csn, u_long vend_id); -static void sb16pnp_attach(u_long csn, u_long vend_id, char *name, - struct isa_device *dev); +#endif /* NPCM > 0 */ -static struct pnp_device sb16pnp = { - "SB16pnp", - sb16pnp_probe, - sb16pnp_attach, - &nsnd, /* use this for all sound cards */ - &tty_imask /* imask */ -}; -DATA_SET (pnpdevice_set, sb16pnp); - -static char * -sb16pnp_probe(u_long csn, u_long vend_id) -{ - char *s = NULL ; - - /* - * The SB16/AWExx cards seem to differ in the fourth byte of - * the vendor id, so I have just masked it for the time being... - * Reported values are: - * SB16 Value PnP: 0x2b008c0e - * SB AWExx PnP: 0x39008c0e 0x9d008c0e 0xc3008c0e - * Vibra16X: 0xf0008c0e - */ - if (vend_id == 0xf0008c0e) - s = "Vibra16X" ; - else if ( (vend_id & 0xffffff) == (0x9d008c0e & 0xffffff) ) - s = "SB16 PnP"; - else if (vend_id == 0x01009305) - s = "Avance Asound 100" ; - else if (vend_id == 0x10019305) - s = "Avance Logic 100+" ; /* Vibra16X-class */ - else if (vend_id == 0x20019305) - s = "Avance Logic ALS120" ; /* Vibra16X-class */ - if (s) { - struct pnp_cinfo d; - read_pnp_parms(&d, 0); - if (d.enable == 0) { - printf("This is a %s, but LDN 0 is disabled\n", s); - return NULL ; - } - return s ; - } - return NULL ; -} - -static void -sb16pnp_attach(u_long csn, u_long vend_id, char *name, - struct isa_device *dev) -{ - struct pnp_cinfo d ; - snddev_info tmp_d ; /* patched copy of the basic snddev_info */ - - tmp_d = sb_op_desc; - snddev_last_probed = &tmp_d; - - read_pnp_parms ( &d , 0 ) ; - d.port[1] = 0 ; /* only the first address is used */ - dev->id_iobase = d.port[0]; - tmp_d.synth_base = d.port[2]; - write_pnp_parms ( &d , 0 ); - enable_pnp_card(); - - dev->id_drq = d.drq[0] ; /* primary dma */ - dev->id_irq = (1 << d.irq[0] ) ; - dev->id_intr = (inthand2_t *)pcmintr ; - dev->id_flags = DV_F_DUAL_DMA | (d.drq[1] ) ; - - pcm_info[dev->id_unit] = tmp_d; /* pcm_info[] will be reinitialized after */ - snddev_last_probed->probe(dev); /* not really necessary but doesn't harm */ - - if (vend_id == 0x10019305 || vend_id == 0xf0008c0e - || vend_id == 0x20019305) { - /* - * XXX please add here the vend_id for other vibra16X cards... - * And remember, must change tmp_d, not - */ - tmp_d.bd_flags |= BD_F_SB16X ; - } - pcmattach(dev); -} -#endif /* NPNP */ -#endif |