summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorcg <cg@FreeBSD.org>1999-09-01 04:01:57 +0000
committercg <cg@FreeBSD.org>1999-09-01 04:01:57 +0000
commitfeefbc8c0eaa6a6e695c1e9c3514be8d22e881ea (patch)
tree045ef45e49c561940485e33dec98ae361705568f
parent40676b1f20541a3bc3b49840b0eff5ec547720dd (diff)
downloadFreeBSD-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.c3700
-rw-r--r--sys/dev/pcm/isa/mss.h225
-rw-r--r--sys/dev/pcm/isa/sb.c2167
-rw-r--r--sys/dev/pcm/isa/sb.h70
-rw-r--r--sys/dev/sound/isa/mss.c3700
-rw-r--r--sys/dev/sound/isa/mss.h225
-rw-r--r--sys/dev/sound/isa/sb.c2167
-rw-r--r--sys/dev/sound/isa/sb.h70
-rw-r--r--sys/dev/sound/isa/sb16.c2167
-rw-r--r--sys/dev/sound/isa/sb8.c2167
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
OpenPOWER on IntegriCloud