summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorcg <cg@FreeBSD.org>1999-09-01 04:08:39 +0000
committercg <cg@FreeBSD.org>1999-09-01 04:08:39 +0000
commit73a7a67d13cba01eb39397ff54125ce8d3e6f8cc (patch)
tree45f897f70e6c72dbb1af7c4e6103dfa70ae3542b
parentfeefbc8c0eaa6a6e695c1e9c3514be8d22e881ea (diff)
downloadFreeBSD-src-73a7a67d13cba01eb39397ff54125ce8d3e6f8cc.zip
FreeBSD-src-73a7a67d13cba01eb39397ff54125ce8d3e6f8cc.tar.gz
say hello to newpcm. it is not yet enabled, requiring new pnp code from dfr
to compile successfully. further details will be provided in the commit enabling newpcm.
-rw-r--r--sys/dev/pcm/ac97.c335
-rw-r--r--sys/dev/pcm/ac97.h35
-rw-r--r--sys/dev/pcm/channel.c737
-rw-r--r--sys/dev/pcm/channel.h80
-rw-r--r--sys/dev/pcm/datatypes.h156
-rw-r--r--sys/dev/pcm/dsp.c543
-rw-r--r--sys/dev/pcm/dsp.h37
-rw-r--r--sys/dev/pcm/fake.c115
-rw-r--r--sys/dev/pcm/feeder.c403
-rw-r--r--sys/dev/pcm/mixer.c202
-rw-r--r--sys/dev/pcm/mixer.h42
-rw-r--r--sys/dev/pcm/pci/aureal.c693
-rw-r--r--sys/dev/pcm/pci/aureal.h99
-rw-r--r--sys/dev/pcm/pci/es1370.c543
-rw-r--r--sys/dev/pcm/pci/es1370.h134
-rw-r--r--sys/dev/pcm/pci/es137x.c543
-rw-r--r--sys/dev/pcm/pci/es137x.h134
-rw-r--r--sys/dev/pcm/pci/t4dwave.c688
-rw-r--r--sys/dev/pcm/pci/t4dwave.h103
-rw-r--r--sys/dev/pcm/sound.c438
-rw-r--r--sys/dev/pcm/sound.h181
-rw-r--r--sys/dev/sound/pci/aureal.c693
-rw-r--r--sys/dev/sound/pci/aureal.h99
-rw-r--r--sys/dev/sound/pci/es137x.c543
-rw-r--r--sys/dev/sound/pci/es137x.h134
-rw-r--r--sys/dev/sound/pci/t4dwave.c688
-rw-r--r--sys/dev/sound/pci/t4dwave.h103
-rw-r--r--sys/dev/sound/pcm/ac97.c335
-rw-r--r--sys/dev/sound/pcm/ac97.h35
-rw-r--r--sys/dev/sound/pcm/channel.c737
-rw-r--r--sys/dev/sound/pcm/channel.h80
-rw-r--r--sys/dev/sound/pcm/datatypes.h156
-rw-r--r--sys/dev/sound/pcm/dsp.c543
-rw-r--r--sys/dev/sound/pcm/dsp.h37
-rw-r--r--sys/dev/sound/pcm/fake.c115
-rw-r--r--sys/dev/sound/pcm/feeder.c403
-rw-r--r--sys/dev/sound/pcm/mixer.c202
-rw-r--r--sys/dev/sound/pcm/mixer.h42
-rw-r--r--sys/dev/sound/pcm/sound.c438
-rw-r--r--sys/dev/sound/pcm/sound.h181
40 files changed, 11805 insertions, 0 deletions
diff --git a/sys/dev/pcm/ac97.c b/sys/dev/pcm/ac97.c
new file mode 100644
index 0000000..b3da9cb
--- /dev/null
+++ b/sys/dev/pcm/ac97.c
@@ -0,0 +1,335 @@
+/*
+ * Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk>
+ * 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$
+ */
+
+#include <dev/pcm/sound.h>
+#include <dev/pcm/ac97.h>
+
+#define AC97_MUTE 0x8000
+
+#define AC97_REG_RESET 0x00
+#define AC97_MIX_MASTER 0x02
+#define AC97_MIX_PHONES 0x04
+#define AC97_MIX_MONO 0x06
+#define AC97_MIX_TONE 0x08
+#define AC97_MIX_BEEP 0x0a
+#define AC97_MIX_PHONE 0x0c
+#define AC97_MIX_MIC 0x0e
+#define AC97_MIX_LINE 0x10
+#define AC97_MIX_CD 0x12
+#define AC97_MIX_VIDEO 0x14
+#define AC97_MIX_AUX 0x16
+#define AC97_MIX_PCM 0x18
+#define AC97_REG_RECSEL 0x1a
+#define AC97_MIX_RGAIN 0x1c
+#define AC97_MIX_MGAIN 0x1e
+#define AC97_REG_GEN 0x20
+#define AC97_REG_3D 0x22
+#define AC97_REG_POWER 0x26
+#define AC97_REG_ID1 0x7c
+#define AC97_REG_ID2 0x7e
+
+struct ac97mixtable_entry {
+ int reg:8;
+ unsigned bits:4;
+ unsigned ofs:4;
+ unsigned stereo:1;
+ unsigned mute:1;
+ unsigned recidx:4;
+ unsigned mask:1;
+};
+
+struct ac97_info {
+ ac97_read *read;
+ ac97_write *write;
+ void *devinfo;
+ char id[4];
+ char rev;
+ unsigned caps, se;
+ struct ac97mixtable_entry mix[32];
+};
+
+struct ac97_codecid {
+ u_int32_t id;
+ char *name;
+};
+
+static const struct ac97mixtable_entry ac97mixtable_default[32] = {
+ [SOUND_MIXER_VOLUME] = { AC97_MIX_MASTER, 5, 0, 1, 1, 6, 0 },
+ [SOUND_MIXER_BASS] = { AC97_MIX_TONE, 4, 8, 0, 0, 0, 1 },
+ [SOUND_MIXER_TREBLE] = { AC97_MIX_TONE, 4, 0, 0, 0, 0, 1 },
+ [SOUND_MIXER_PCM] = { AC97_MIX_PCM, 5, 0, 1, 1, 0, 0 },
+ [SOUND_MIXER_SPEAKER] = { AC97_MIX_BEEP, 4, 1, 0, 1, 0, 0 },
+ [SOUND_MIXER_LINE] = { AC97_MIX_LINE, 5, 0, 1, 1, 5, 0 },
+ [SOUND_MIXER_MIC] = { AC97_MIX_MIC, 5, 0, 0, 1, 1, 0 },
+ [SOUND_MIXER_CD] = { AC97_MIX_CD, 5, 0, 1, 1, 2, 0 },
+ [SOUND_MIXER_LINE1] = { AC97_MIX_AUX, 5, 0, 1, 1, 4, 0 },
+ [SOUND_MIXER_VIDEO] = { AC97_MIX_VIDEO, 5, 0, 1, 1, 3, 0 },
+ [SOUND_MIXER_RECLEV] = { -AC97_MIX_RGAIN, 4, 0, 1, 1, 0, 0 }
+};
+
+static const unsigned ac97mixdevs =
+ SOUND_MASK_VOLUME |
+ SOUND_MASK_PCM | SOUND_MASK_SPEAKER | SOUND_MASK_LINE |
+ SOUND_MASK_MIC | SOUND_MASK_CD | SOUND_MASK_LINE1 |
+ SOUND_MASK_VIDEO | SOUND_MASK_RECLEV;
+
+static const unsigned ac97recdevs =
+ SOUND_MASK_VOLUME | SOUND_MASK_LINE | SOUND_MASK_MIC |
+ SOUND_MASK_CD | SOUND_MASK_LINE1 | SOUND_MASK_VIDEO;
+
+static struct ac97_codecid ac97codecid[] = {
+ { 0x414B4D00, "Asahi Kasei AK4540" },
+ { 0x43525900, "Cirrus Logic CS4297" },
+ { 0x83847600, "SigmaTel STAC????" },
+ { 0x83847604, "SigmaTel STAC9701/3/4/5" },
+ { 0x83847605, "SigmaTel STAC9704" },
+ { 0x83847608, "SigmaTel STAC9708" },
+ { 0x83847609, "SigmaTel STAC9721" },
+ { 0, NULL }
+};
+
+static char *ac97enhancement[] = {
+ "",
+ "Analog Devices Phat Stereo",
+ "Creative Stereo Enhancement",
+ "National Semi 3D Stereo Enhancement",
+ "Yamaha Ymersion",
+ "BBE 3D Stereo Enhancement",
+ "Crystal Semi 3D Stereo Enhancement",
+ "Qsound QXpander",
+ "Spatializer 3D Stereo Enhancement",
+ "SRS 3D Stereo Enhancement",
+ "Platform Tech 3D Stereo Enhancement",
+ "AKM 3D Audio",
+ "Aureal Stereo Enhancement",
+ "Aztech 3D Enhancement",
+ "Binaura 3D Audio Enhancement",
+ "ESS Technology Stereo Enhancement",
+ "Harman International VMAx",
+ "Nvidea 3D Stereo Enhancement",
+ "Philips Incredible Sound",
+ "Texas Instruments 3D Stereo Enhancement",
+ "VLSI Technology 3D Stereo Enhancement",
+ "TriTech 3D Stereo Enhancement",
+ "Realtek 3D Stereo Enhancement",
+ "Samsung 3D Stereo Enhancement",
+ "Wolfson Microelectronics 3D Enhancement",
+ "Delta Integration 3D Enhancement",
+ "SigmaTel 3D Enhancement",
+ "Reserved 27",
+ "Rockwell 3D Stereo Enhancement",
+ "Reserved 29",
+ "Reserved 30",
+ "Reserved 31"
+};
+
+static char *ac97feature[] = {
+ "mic channel",
+ "reserved",
+ "tone",
+ "simulated stereo",
+ "headphone",
+ "bass boost",
+ "18 bit DAC",
+ "20 bit DAC",
+ "18 bit ADC",
+ "20 bit ADC"
+};
+
+static int
+ac97_setrecsrc(struct ac97_info *codec, int channel)
+{
+ struct ac97mixtable_entry *e = &codec->mix[channel];
+ if (e->recidx > 0) {
+ int val = e->recidx - 1;
+ val |= val << 8;
+ codec->write(codec->devinfo, AC97_REG_RECSEL, val);
+ return 0;
+ } else return -1;
+}
+
+static int
+ac97_setmixer(struct ac97_info *codec, unsigned channel, unsigned left, unsigned right)
+{
+ struct ac97mixtable_entry *e = &codec->mix[channel];
+ if (e->reg != 0) {
+ int max, val;
+
+ if (!e->stereo) right = left;
+ if (e->reg > 0) {
+ left = 100 - left;
+ right = 100 - right;
+ }
+
+ max = (1 << e->bits) - 1;
+ left = (left * max) / 100;
+ right = (right * max) / 100;
+
+ val = (left << 8) | right;
+
+ left = (left * 100) / max;
+ right = (right * 100) / max;
+
+ if (e->reg > 0) {
+ left = 100 - left;
+ right = 100 - right;
+ }
+
+ if (!e->stereo) {
+ val &= max;
+ val <<= e->ofs;
+ if (e->mask) {
+ int cur = codec->read(codec->devinfo, e->reg);
+ val |= cur & ~(max << e->ofs);
+ }
+ }
+ if (left == 0 && right == 0 && e->mute == 1) val = AC97_MUTE;
+ codec->write(codec->devinfo, abs(e->reg), val);
+ return left | (right << 8);
+ } else return -1;
+}
+
+#if 0
+static int
+ac97_getmixer(struct ac97_info *codec, int channel)
+{
+ struct ac97mixtable_entry *e = &codec->mix[channel];
+ if (channel < SOUND_MIXER_NRDEVICES && e->reg != 0) {
+ int max, val, volume;
+
+ max = (1 << e->bits) - 1;
+ val = codec->read(codec->devinfo, e->reg);
+ if (val == AC97_MUTE && e->mute == 1) volume = 0;
+ else {
+ if (e->stereo == 0) val >>= e->ofs;
+ val &= max;
+ volume = (val * 100) / max;
+ if (e->reg > 0) volume = 100 - volume;
+ }
+ return volume;
+ } else return -1;
+}
+#endif
+
+static unsigned
+ac97_init(struct ac97_info *codec)
+{
+ unsigned i, j;
+ u_int32_t id;
+
+ for (i = 0; i < 32; i++) codec->mix[i] = ac97mixtable_default[i];
+
+ codec->write(codec->devinfo, AC97_REG_POWER, 0);
+ codec->write(codec->devinfo, AC97_REG_RESET, 0);
+ DELAY(10000);
+
+ i = codec->read(codec->devinfo, AC97_REG_RESET);
+ codec->caps = i & 0x03ff;
+ codec->se = (i & 0x7c00) >> 10;
+
+ id = (codec->read(codec->devinfo, AC97_REG_ID1) << 16) |
+ codec->read(codec->devinfo, AC97_REG_ID2);
+ codec->rev = id & 0x000000ff;
+
+ codec->write(codec->devinfo, AC97_MIX_MASTER, 0x20);
+ if ((codec->read(codec->devinfo, AC97_MIX_MASTER) & 0x20) == 0x20)
+ codec->mix[SOUND_MIXER_VOLUME].bits++;
+ codec->write(codec->devinfo, AC97_MIX_MASTER, 0x00);
+
+ if (bootverbose) {
+ printf("ac97: codec id 0x%8x", id);
+ for (i = 0; ac97codecid[i].id; i++) {
+ if (ac97codecid[i].id == id) printf(" (%s)", ac97codecid[i].name);
+ }
+ printf("\nac97: codec features ");
+ for (i = j = 0; i < 10; i++) {
+ if (codec->caps & (1 << i)) {
+ printf("%s%s", j? ", " : "", ac97feature[i]);
+ j++;
+ }
+ }
+ printf("%s%d bit master volume", j? ", " : "", codec->mix[SOUND_MIXER_VOLUME].bits);
+ printf("%s%s\n", j? ", " : "", ac97enhancement[codec->se]);
+ }
+
+ if ((codec->read(codec->devinfo, AC97_REG_POWER) & 2) == 0)
+ printf("ac97: dac not ready\n");
+ return 0;
+}
+
+struct ac97_info *
+ac97_create(void *devinfo, ac97_read *rd, ac97_write *wr)
+{
+ struct ac97_info *codec;
+
+ codec = (struct ac97_info *)malloc(sizeof *codec, M_DEVBUF, M_NOWAIT);
+ if (codec != NULL) {
+ codec->read = rd;
+ codec->write = wr;
+ codec->devinfo = devinfo;
+ }
+ return codec;
+}
+
+static int
+ac97mix_init(snd_mixer *m)
+{
+ struct ac97_info *codec = mix_getdevinfo(m);
+ if (codec == NULL) return -1;
+ ac97_init(codec);
+ mix_setdevs(m, ac97mixdevs | ((codec->caps & 4)? SOUND_MASK_BASS | SOUND_MASK_TREBLE : 0));
+ mix_setrecdevs(m, ac97recdevs);
+ return 0;
+}
+
+static int
+ac97mix_set(snd_mixer *m, unsigned dev, unsigned left, unsigned right)
+{
+ struct ac97_info *codec = mix_getdevinfo(m);
+ if (codec == NULL) return -1;
+ return ac97_setmixer(codec, dev, left, right);
+}
+
+static int
+ac97mix_setrecsrc(snd_mixer *m, u_int32_t src)
+{
+ int i;
+ struct ac97_info *codec = mix_getdevinfo(m);
+ if (codec == NULL) return -1;
+ for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
+ if ((src & (1 << i)) != 0) break;
+ return (ac97_setrecsrc(codec, i) == 0)? 1 << i : -1;
+}
+
+snd_mixer ac97_mixer = {
+ "AC97 mixer",
+ ac97mix_init,
+ ac97mix_set,
+ ac97mix_setrecsrc,
+};
+
diff --git a/sys/dev/pcm/ac97.h b/sys/dev/pcm/ac97.h
new file mode 100644
index 0000000..c3460a2
--- /dev/null
+++ b/sys/dev/pcm/ac97.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk>
+ * 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$
+ */
+
+typedef u_int32_t (ac97_read)(void *devinfo, int regno);
+typedef void (ac97_write)(void *devinfo, int regno, u_int32_t data);
+
+extern snd_mixer ac97_mixer;
+struct ac97_info;
+
+struct ac97_info *ac97_create(void *devinfo, ac97_read *rd, ac97_write *wr);
diff --git a/sys/dev/pcm/channel.c b/sys/dev/pcm/channel.c
new file mode 100644
index 0000000..dd383e5
--- /dev/null
+++ b/sys/dev/pcm/channel.c
@@ -0,0 +1,737 @@
+/*
+ * Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk>
+ * Portions Copyright by Luigi Rizzo - 1997-99
+ * 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$
+ */
+
+#include <dev/pcm/sound.h>
+
+#define MIN_CHUNK_SIZE 256 /* for uiomove etc. */
+#define DMA_ALIGN_THRESHOLD 4
+#define DMA_ALIGN_MASK (~(DMA_ALIGN_THRESHOLD - 1))
+
+#define ISA_DMA(b) (((b)->chan >= 0 && (b)->chan != 4 && (b)->chan < 8))
+#define CANCHANGE(c) (!(c)->buffer.dl)
+
+static int chn_reinit(pcm_channel *c);
+static void chn_stintr(pcm_channel *c);
+/*
+ * SOUND OUTPUT
+
+We use a circular buffer to store samples directed to the DAC.
+The buffer is split into two variable-size regions, each identified
+by an offset in the buffer (rp,fp) and a length (rl,fl):
+
+ 0 rp,rl fp,fl bufsize
+ |__________>____________>________|
+ FREE d READY w FREE
+
+ READY: data written from the process and ready to be sent to the DAC;
+ FREE: free part of the buffer.
+
+Both regions can wrap around the end of the buffer. At initialization,
+READY is empty, FREE takes all the available space, and dma is
+idle. dl contains the length of the current DMA transfer, dl=0
+means that the dma is idle.
+
+The two boundaries (rp,fp) in the buffers are advanced by DMA [d]
+and write() [w] operations. The first portion of the READY region
+is used for DMA transfers. The transfer is started at rp and with
+chunks of length dl. During DMA operations, dsp_wr_dmaupdate()
+updates rp, rl and fl tracking the ISA DMA engine as the transfer
+makes progress.
+When a new block is written, fp advances and rl,fl are updated
+accordingly.
+
+The code works as follows: the user write routine dsp_write_body()
+fills up the READY region with new data (reclaiming space from the
+FREE region) and starts the write DMA engine if inactive. When a
+DMA transfer is complete, an interrupt causes dsp_wrintr() to be
+called which extends the FREE region and possibly starts the next
+transfer.
+
+In some cases, the code tries to track the current status of DMA
+operations by calling dsp_wr_dmaupdate() which changes rp, rl and fl.
+
+The sistem tries to make all DMA transfers use the same size,
+play_blocksize or rec_blocksize. The size is either selected by
+the user, or computed by the system to correspond to about .25s of
+audio. The blocksize must be within a range which is currently:
+
+ min(5ms, 40 bytes) ... 1/2 buffer size.
+
+When there aren't enough data (write) or space (read), a transfer
+is started with a reduced size.
+
+To reduce problems in case of overruns, the routine which fills up
+the buffer should initialize (e.g. by repeating the last value) a
+reasonably long area after the last block so that no noise is
+produced on overruns.
+
+ *
+ */
+
+
+/* XXX this is broken: in the event a bounce buffer is used, data never
+ * gets copied in or out of the real buffer. fix requires mods to isa_dma.c
+ * and possibly fixes to other autodma mode clients
+ */
+static void
+chn_isadmabounce(pcm_channel *c)
+{
+ if (ISA_DMA(&c->buffer)) {
+ /* tell isa_dma to bounce data in/out */
+ } else panic("chn_isadmabounce called on invalid channel");
+}
+
+static int
+chn_polltrigger(pcm_channel *c)
+{
+ snd_dbuf *b = &c->buffer;
+ unsigned lim = (c->flags & CHN_F_HAS_SIZE)? c->blocksize : 1;
+ int trig = 0;
+
+ if (c->flags & CHN_F_MAPPED)
+ trig = ((b->int_count > b->prev_int_count) || b->first_poll);
+ else trig = (((c->direction == PCMDIR_PLAY)? b->fl : b->rl) >= lim);
+ return trig;
+}
+
+static int
+chn_pollreset(pcm_channel *c)
+{
+ snd_dbuf *b = &c->buffer;
+
+ if (c->flags & CHN_F_MAPPED) b->prev_int_count = b->int_count;
+ b->first_poll = 0;
+ return 1;
+}
+
+/*
+ * chn_dmadone() updates pointers and wakes up any process sleeping
+ * or waiting on a select().
+ * Must be called at spltty().
+ */
+static void
+chn_dmadone(pcm_channel *c)
+{
+ snd_dbuf *b = &c->buffer;
+
+ chn_dmaupdate(c);
+ if (ISA_DMA(b)) chn_isadmabounce(c); /* sync bounce buffer */
+ wakeup(b);
+ b->int_count++;
+ if (b->sel.si_pid && chn_polltrigger(c)) selwakeup(&b->sel);
+}
+
+/*
+ * chn_dmaupdate() tracks the status of a dma transfer,
+ * updating pointers. It must be called at spltty().
+ *
+ * NOTE: when we are using auto dma in the device, rl might become
+ * negative.
+ */
+void
+chn_dmaupdate(pcm_channel *c)
+{
+ snd_dbuf *b = &c->buffer;
+ int delta, hwptr = chn_getptr(c);
+
+ if (c->direction == PCMDIR_PLAY) {
+ delta = (b->bufsize + hwptr - b->rp) % b->bufsize;
+ b->rp = hwptr;
+ b->rl -= delta;
+ b->fl += delta;
+ } else {
+ delta = (b->bufsize + hwptr - b->fp) % b->bufsize;
+ b->fp = hwptr;
+ b->rl += delta;
+ b->fl -= delta;
+ }
+ b->total += delta;
+}
+
+/*
+ * Write interrupt routine. Can be called from other places (e.g.
+ * to start a paused transfer), but with interrupts disabled.
+ */
+static void
+chn_wrintr(pcm_channel *c)
+{
+ snd_dbuf *b = &c->buffer;
+ int start;
+
+ if (b->dl) chn_dmadone(c);
+
+ /*
+ * start another dma operation only if have ready data in the buffer,
+ * there is no pending abort, have a full-duplex device, or have a
+ * half duplex device and there is no pending op on the other side.
+ *
+ * Force transfers to be aligned to a boundary of 4, which is
+ * needed when doing stereo and 16-bit.
+ */
+ if (c->flags & CHN_F_MAPPED) start = c->flags & CHN_F_TRIGGERED;
+ else start = (b->rl >= DMA_ALIGN_THRESHOLD && !(c->flags & CHN_F_ABORTING));
+ if (start) {
+ int l;
+ chn_dmaupdate(c);
+ l = min(b->rl, c->blocksize) & DMA_ALIGN_MASK;
+ if (c->flags & CHN_F_MAPPED) l = c->blocksize;
+ /*
+ * check if we need to reprogram the DMA on the sound card.
+ * This happens if the size has changed _and_ the new size
+ * is smaller, or it matches the blocksize.
+ *
+ * 0 <= l <= blocksize
+ * 0 <= dl <= blocksize
+ * reprog if (dl == 0 || l != dl)
+ * was:
+ * l != b->dl && (b->dl == 0 || l < b->dl || l == c->blocksize)
+ */
+ if (b->dl == 0 || l != b->dl) {
+ /* size has changed. Stop and restart */
+ DEB(printf("wrintr: bsz %d -> %d, rp %d rl %d\n",
+ b->dl, l, b->rp, b->rl));
+ if (b->dl) chn_trigger(c, PCMTRIG_STOP);
+ b->dl = l; /* record new transfer size */
+ chn_trigger(c, PCMTRIG_START);
+ }
+ } else {
+ /* cannot start a new dma transfer */
+ DEB(printf("cannot start wr-dma flags 0x%08x rp %d rl %d\n",
+ c->flags, b->rp, b->rl));
+ if (b->dl) { /* was active */
+ b->dl = 0;
+ chn_trigger(c, PCMTRIG_STOP);
+#if 0
+ if (c->flags & CHN_F_WRITING)
+ DEB(printf("got wrint while reloading\n"));
+ else if (b->rl <= 0) /* XXX added 980110 lr */
+ chn_resetbuf(c);
+#endif
+ }
+ }
+}
+
+/*
+ * user write routine
+ *
+ * advance the boundary between READY and FREE, fill the space with
+ * uiomove(), and possibly start DMA. Do the above until the transfer
+ * is complete.
+ *
+ * To minimize latency in case a pending DMA transfer is about to end,
+ * we do the transfer in pieces of increasing sizes, extending the
+ * READY area at every checkpoint. In the (necessary) assumption that
+ * memory bandwidth is larger than the rate at which the dma consumes
+ * data, we reduce the latency to something proportional to the length
+ * of the first piece, while keeping the overhead low and being able
+ * to feed the DMA with large blocks.
+ */
+
+int
+chn_write(pcm_channel *c, struct uio *buf)
+{
+ int l, w, timeout, ret = 0;
+ long s;
+ snd_dbuf *b = &c->buffer;
+
+ if (c->flags & CHN_F_WRITING) {
+ /* This shouldn't happen and is actually silly
+ * - will never wake up, just timeout; why not sleep on b?
+ */
+ tsleep(&s, PZERO, "pcmwrW", hz);
+ return EBUSY;
+ }
+ c->flags |= CHN_F_WRITING;
+ while (buf->uio_resid >= DMA_ALIGN_THRESHOLD) {
+ s = spltty();
+ chn_dmaupdate(c);
+ splx(s);
+ if (b->fl < DMA_ALIGN_THRESHOLD) {
+ if (c->flags & CHN_F_NBIO) break;
+ timeout = (buf->uio_resid >= b->dl)? hz : 1;
+ ret = tsleep(b, PRIBIO | PCATCH, "pcmwr", timeout);
+ if (ret == EINTR) chn_abort(c);
+ if (ret == EINTR || ret == ERESTART) break;
+ ret = 0;
+ continue;
+ }
+ /* ensure we always have a whole number of samples */
+ l = min(b->fl, b->bufsize - b->fp) & DMA_ALIGN_MASK;
+ w = c->feeder->feed(c->feeder, b->buf + b->fp, l, buf);
+ s = spltty();
+ b->rl += w;
+ b->fl -= w;
+ b->fp = (b->fp + w) % b->bufsize;
+ splx(s);
+ if (b->rl && !b->dl) chn_stintr(c);
+ }
+ c->flags &= ~CHN_F_WRITING;
+ return ret;
+}
+
+/*
+ * SOUND INPUT
+ *
+
+The input part is similar to the output one, with a circular buffer
+split in two regions, and boundaries advancing because of read() calls
+[r] or dma operation [d]. At initialization, as for the write
+routine, READY is empty, and FREE takes all the space.
+
+ 0 rp,rl fp,fl bufsize
+ |__________>____________>________|
+ FREE r READY d FREE
+
+Operation is as follows: upon user read (dsp_read_body()) a DMA read
+is started if not already active (marked by b->dl > 0),
+then as soon as data are available in the READY region they are
+transferred to the user buffer, thus advancing the boundary between FREE
+and READY. Upon interrupts, caused by a completion of a DMA transfer,
+the READY region is extended and possibly a new transfer is started.
+
+When necessary, dsp_rd_dmaupdate() is called to advance fp (and update
+rl,fl accordingly). Upon user reads, rp is advanced and rl,fl are
+updated accordingly.
+
+The rules to choose the size of the new DMA area are similar to
+the other case, with a preferred constant transfer size equal to
+rec_blocksize, and fallback to smaller sizes if no space is available.
+
+ */
+
+/* read interrupt routine. Must be called with interrupts blocked. */
+static void
+chn_rdintr(pcm_channel *c)
+{
+ snd_dbuf *b = &c->buffer;
+ int start;
+
+ if (b->dl) chn_dmadone(c);
+
+ DEB(printf("rdintr: start dl %d, rp:rl %d:%d, fp:fl %d:%d\n",
+ b->dl, b->rp, b->rl, b->fp, b->fl));
+ /* Restart if have enough free space to absorb overruns */
+ if (c->flags & CHN_F_MAPPED) start = c->flags & CHN_F_TRIGGERED;
+ else start = (b->fl > 0x200 && !(c->flags & CHN_F_ABORTING));
+ if (start) {
+ int l = min(b->fl - 0x100, c->blocksize);
+ if (c->flags & CHN_F_MAPPED) l = c->blocksize;
+ l &= DMA_ALIGN_MASK ; /* realign sizes */
+
+ DEB(printf("rdintr: dl %d -> %d\n", b->dl, l);)
+ if (l != b->dl) {
+ /* size has changed. Stop and restart */
+ if (b->dl) {
+ chn_trigger(c, PCMTRIG_STOP);
+ chn_dmaupdate(c);
+ l = min(b->fl - 0x100, c->blocksize);
+ l &= DMA_ALIGN_MASK ; /* realign sizes */
+ }
+ b->dl = l;
+ chn_trigger(c, PCMTRIG_START);
+ }
+ } else {
+ if (b->dl) { /* was active */
+ b->dl = 0;
+ chn_dmaupdate(c);
+ chn_trigger(c, PCMTRIG_STOP);
+ }
+ }
+}
+
+/*
+ * body of user-read routine
+ *
+ * Start DMA if not active; wait for READY not empty.
+ * Transfer data from READY region using uiomove(), advance boundary
+ * between FREE and READY. Repeat until transfer is complete.
+ *
+ * To avoid excessive latency in freeing up space for the DMA
+ * engine, transfers are done in blocks of increasing size, so that
+ * the latency is proportional to the size of the smallest block, but
+ * we have a low overhead and are able to feed the dma engine with
+ * large blocks.
+ *
+ * NOTE: in the current version, read will not return more than
+ * blocksize bytes at once (unless more are already available), to
+ * avoid that requests using very large buffers block for too long.
+ */
+
+int
+chn_read(pcm_channel *c, struct uio *buf)
+{
+ int w, l, timeout, limit, ret = 0;
+ long s;
+ snd_dbuf *b = &c->buffer;
+
+ if (c->flags & CHN_F_READING) {
+ /* This shouldn't happen and is actually silly */
+ tsleep(&s, PZERO, "pcmrdR", hz);
+ return (EBUSY);
+ }
+
+ if (!b->rl & !b->dl) chn_stintr(c);
+ c->flags |= CHN_F_READING;
+ limit = buf->uio_resid - c->blocksize;
+ if (limit < 0) limit = 0;
+ while (buf->uio_resid > limit) {
+ s = spltty();
+ chn_dmaupdate(c);
+ splx(s);
+ if (b->rl < DMA_ALIGN_THRESHOLD) {
+ if (c->flags & CHN_F_NBIO) break;
+ timeout = (buf->uio_resid - limit >= b->dl)? hz : 1;
+ ret = tsleep(b, PRIBIO | PCATCH, "pcmrd", timeout);
+ if (ret == EINTR) chn_abort(c);
+ if (ret == EINTR || ret == ERESTART) break;
+ ret = 0;
+ continue;
+ }
+ /* ensure we always have a whole number of samples */
+ l = min(b->rl, b->bufsize - b->rp) & DMA_ALIGN_MASK;
+ w = c->feeder->feed(c->feeder, b->buf + b->rp, l, buf);
+ s = spltty();
+ b->rl -= w;
+ b->fl += w;
+ b->rp = (b->rp + w) % b->bufsize;
+ splx(s);
+ }
+ c->flags &= ~CHN_F_READING;
+ return ret;
+}
+
+void
+chn_intr(pcm_channel *c)
+{
+ if (!c->buffer.dl) chn_reinit(c);
+ if (c->direction == PCMDIR_PLAY) chn_wrintr(c); else chn_rdintr(c);
+}
+
+static void
+chn_stintr(pcm_channel *c)
+{
+ u_long s;
+ s = spltty();
+ chn_intr(c);
+ splx(s);
+}
+
+static void
+chn_dma_setmap(void *arg, bus_dma_segment_t *segs, int nseg, int error)
+{
+ snd_dbuf *b = (snd_dbuf *)arg;
+
+ if (bootverbose) {
+ printf("pcm: setmap %lx, %lx; ", (unsigned long)segs->ds_addr,
+ (unsigned long)segs->ds_len);
+ printf("%p -> %lx\n", b->buf, (unsigned long)vtophys(b->buf));
+ }
+}
+
+int
+chn_allocbuf(snd_dbuf *b, bus_dma_tag_t parent_dmat)
+{
+ if (bus_dmamem_alloc(parent_dmat, (void **)&b->buf,
+ BUS_DMA_NOWAIT, &b->dmamap)) return -1;
+ if (bus_dmamap_load(parent_dmat, b->dmamap, b->buf,
+ b->bufsize, chn_dma_setmap, b, 0)) return -1;
+ return 0;
+}
+
+void
+chn_resetbuf(pcm_channel *c)
+{
+ snd_dbuf *b = &c->buffer;
+ u_int16_t data, *p;
+ u_int32_t i;
+
+ c->buffer.sample_size = 1;
+ c->buffer.sample_size <<= (c->hwfmt & AFMT_STEREO)? 1 : 0;
+ c->buffer.sample_size <<= (c->hwfmt & AFMT_16BIT)? 1 : 0;
+ /* rely on bufsize & 3 == 0 */
+ if (c->hwfmt & AFMT_SIGNED) data = 0x00; else data = 0x80;
+ if (c->hwfmt & AFMT_16BIT) data <<= 8; else data |= data << 8;
+ if (c->hwfmt & AFMT_BIGENDIAN)
+ data = ((data >> 8) & 0x00ff) | ((data << 8) & 0xff00);
+ for (i = 0, p = (u_int16_t *)b->buf; i < b->bufsize; i += 2)
+ *p++ = data;
+ b->rp = b->fp = 0;
+ b->dl = b->rl = 0;
+ b->prev_total = b->total = 0;
+ b->prev_int_count = b->int_count = 0;
+ b->first_poll = 1;
+ b->fl = b->bufsize;
+}
+
+void
+buf_isadma(snd_dbuf *b, int go)
+{
+ if (ISA_DMA(b)) {
+ if (go == PCMTRIG_START) isa_dmastart(b->dir | B_RAW, b->buf,
+ b->bufsize, b->chan);
+ else {
+ isa_dmastop(b->chan);
+ isa_dmadone(b->dir | B_RAW, b->buf, b->bufsize,
+ b->chan);
+ }
+ } else panic("buf_isadma called on invalid channel");
+}
+
+int
+buf_isadmaptr(snd_dbuf *b)
+{
+ if (ISA_DMA(b)) {
+ int i = b->dl? isa_dmastatus(b->chan) : b->bufsize;
+ if (i < 0) i = 0;
+ return b->bufsize - i;
+ } else panic("buf_isadmaptr called on invalid channel");
+ return -1;
+}
+
+/*
+ * snd_sync waits until the space in the given channel goes above
+ * a threshold. The threshold is checked against fl or rl respectively.
+ * Assume that the condition can become true, do not check here...
+ */
+int
+chn_sync(pcm_channel *c, int threshold)
+{
+ u_long s, rdy;
+ int ret;
+ snd_dbuf *b = &c->buffer;
+
+ for (;;) {
+ s = spltty();
+ chn_dmaupdate(c);
+ rdy = (c->direction == PCMDIR_PLAY)? b->fl : b->rl;
+ if (rdy <= threshold) {
+ ret = tsleep((caddr_t)b, PRIBIO | PCATCH, "pcmsyn", 1);
+ splx(s);
+ if (ret == ERESTART || ret == EINTR) {
+ printf("tsleep returns %d\n", ret);
+ return -1;
+ }
+ } else break;
+ }
+ splx(s);
+ return 0;
+}
+
+int
+chn_poll(pcm_channel *c, int ev, struct proc *p)
+{
+ snd_dbuf *b = &c->buffer;
+ u_long s = spltty();
+ if (b->dl) chn_dmaupdate(c);
+ splx(s);
+ if (chn_polltrigger(c) && chn_pollreset(c)) return ev;
+ else {
+ selrecord(p, &b->sel);
+ return 0;
+ }
+}
+
+/*
+ * chn_abort is a non-blocking function which aborts a pending
+ * DMA transfer and flushes the buffers.
+ * It returns the number of bytes that have not been transferred.
+ */
+int
+chn_abort(pcm_channel *c)
+{
+ long s;
+ int missing = 0;
+ snd_dbuf *b = &c->buffer;
+
+ s = spltty();
+ if (b->dl) {
+ b->dl = 0;
+ c->flags &= ~((c->direction == PCMDIR_PLAY)? CHN_F_WRITING : CHN_F_READING);
+ chn_trigger(c, PCMTRIG_ABORT);
+ chn_dmadone(c);
+ }
+ missing = b->rl;
+ splx(s);
+ return missing;
+}
+
+/*
+ * this routine tries to flush the dma transfer. It is called
+ * on a close. We immediately abort any read DMA
+ * operation, and then wait for the play buffer to drain.
+ */
+
+int
+chn_flush(pcm_channel *c)
+{
+ int ret, count = 10;
+ snd_dbuf *b = &c->buffer;
+
+ DEB(printf("snd_flush c->flags 0x%08x\n", c->flags));
+ c->flags |= CHN_F_CLOSING;
+ if (c->direction != PCMDIR_PLAY) chn_abort(c);
+ else while (b->dl) {
+ /* still pending output data. */
+ ret = tsleep((caddr_t)b, PRIBIO | PCATCH, "pcmflu", hz);
+ chn_dmaupdate(c);
+ DEB(printf("snd_sync: now rl : fl %d : %d\n", b->rl, b->fl));
+ if (ret == EINTR) {
+ printf("tsleep returns %d\n", ret);
+ return -1;
+ }
+ if (ret && --count == 0) {
+ printf("timeout flushing dbuf_out, cnt 0x%x flags 0x%x\n",
+ b->rl, c->flags);
+ break;
+ }
+ }
+ c->flags &= ~CHN_F_CLOSING;
+ if (c->direction == PCMDIR_PLAY) chn_abort(c);
+ return 0;
+}
+
+int
+chn_reset(pcm_channel *c)
+{
+ chn_abort(c);
+ c->flags &= CHN_F_RESET;
+ chn_resetbuf(c);
+ c->flags |= CHN_F_INIT;
+ return 0;
+}
+
+static int
+chn_reinit(pcm_channel *c)
+{
+ if ((c->flags & CHN_F_INIT) && CANCHANGE(c)) {
+ chn_setformat(c, c->format);
+ chn_setspeed(c, c->speed);
+ chn_setblocksize(c, c->blocksize);
+ chn_setvolume(c, (c->volume >> 8) & 0xff, c->volume & 0xff);
+ c->flags &= ~CHN_F_INIT;
+ return 1;
+ }
+ return 0;
+}
+
+int
+chn_init(pcm_channel *c, void *devinfo, int dir)
+{
+ c->flags = 0;
+ c->feeder = &feeder_root;
+ c->buffer.chan = -1;
+ c->devinfo = c->init(devinfo, &c->buffer, c, dir);
+ chn_setdir(c, dir);
+ return 0;
+}
+
+int
+chn_setdir(pcm_channel *c, int dir)
+{
+ c->direction = dir;
+ if (ISA_DMA(&c->buffer))
+ c->buffer.dir = (dir == PCMDIR_PLAY)? B_WRITE : B_READ;
+ return c->setdir(c->devinfo, c->direction);
+}
+
+int
+chn_setvolume(pcm_channel *c, int left, int right)
+{
+ /* could add a feeder for volume changing if channel returns -1 */
+ if (CANCHANGE(c)) {
+ return -1;
+ }
+ c->volume = (left << 8) | right;
+ c->flags |= CHN_F_INIT;
+ return 0;
+}
+
+int
+chn_setspeed(pcm_channel *c, int speed)
+{
+ /* could add a feeder for rate conversion */
+ if (CANCHANGE(c)) {
+ c->speed = c->setspeed(c->devinfo, speed);
+ return c->speed;
+ }
+ c->speed = speed;
+ c->flags |= CHN_F_INIT;
+ return 0;
+}
+
+int
+chn_setformat(pcm_channel *c, u_int32_t fmt)
+{
+ if (CANCHANGE(c)) {
+ c->hwfmt = c->format = fmt;
+ c->hwfmt = chn_feedchain(c);
+ chn_resetbuf(c);
+ c->setformat(c->devinfo, c->hwfmt);
+ return fmt;
+ }
+ c->format = fmt;
+ c->flags |= CHN_F_INIT;
+ return 0;
+}
+
+int
+chn_setblocksize(pcm_channel *c, int blksz)
+{
+ if (CANCHANGE(c)) {
+ c->flags &= ~CHN_F_HAS_SIZE;
+ if (blksz >= 2) c->flags |= CHN_F_HAS_SIZE;
+ blksz = abs(blksz);
+ if (blksz < 2) blksz = (c->buffer.sample_size * c->speed) >> 2;
+ RANGE(blksz, 1024, c->buffer.bufsize / 4);
+ blksz &= ~3;
+ c->blocksize = c->setblocksize(c->devinfo, blksz);
+ return c->blocksize;
+ }
+ c->blocksize = blksz;
+ c->flags |= CHN_F_INIT;
+ return 0;
+}
+
+int
+chn_trigger(pcm_channel *c, int go)
+{
+ return c->trigger(c->devinfo, go);
+}
+
+int
+chn_getptr(pcm_channel *c)
+{
+ return c->getptr(c->devinfo);
+}
+
+pcmchan_caps *
+chn_getcaps(pcm_channel *c)
+{
+ return c->getcaps(c->devinfo);
+}
diff --git a/sys/dev/pcm/channel.h b/sys/dev/pcm/channel.h
new file mode 100644
index 0000000..8667a9f
--- /dev/null
+++ b/sys/dev/pcm/channel.h
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk>
+ * 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$
+ */
+
+int chn_write(pcm_channel *c, struct uio *buf);
+int chn_read(pcm_channel *c, struct uio *buf);
+int chn_sync(pcm_channel *c, int threshold);
+int chn_flush(pcm_channel *c);
+int chn_poll(pcm_channel *c, int ev, struct proc *p);
+
+int chn_init(pcm_channel *c, void *devinfo, int dir);
+int chn_setdir(pcm_channel *c, int dir);
+int chn_reset(pcm_channel *c);
+int chn_setvolume(pcm_channel *c, int left, int right);
+int chn_setspeed(pcm_channel *c, int speed);
+int chn_setformat(pcm_channel *c, u_int32_t fmt);
+int chn_setblocksize(pcm_channel *c, int blksz);
+int chn_trigger(pcm_channel *c, int go);
+int chn_getptr(pcm_channel *c);
+pcmchan_caps *chn_getcaps(pcm_channel *c);
+
+int chn_allocbuf(snd_dbuf *b, bus_dma_tag_t parent_dmat);
+void chn_resetbuf(pcm_channel *c);
+void chn_intr(pcm_channel *c);
+void chn_dmaupdate(pcm_channel *c);
+int chn_abort(pcm_channel *c);
+
+void buf_isadma(snd_dbuf *b, int go);
+int buf_isadmaptr(snd_dbuf *b);
+int chn_feedchain(pcm_channel *c);
+
+extern pcm_feeder feeder_root;
+
+#define PCMDIR_PLAY 1
+#define PCMDIR_REC -1
+
+#define PCMTRIG_START 1
+#define PCMTRIG_STOP 0
+#define PCMTRIG_ABORT -1
+
+#define CHN_F_READING 0x00000001 /* have a pending read */
+#define CHN_F_WRITING 0x00000002 /* have a pending write */
+#define CHN_F_CLOSING 0x00000004 /* a pending close */
+#define CHN_F_ABORTING 0x00000008 /* a pending abort */
+#define CHN_F_PENDING_IO (CHN_F_READING | CHN_F_WRITING)
+#define CHN_F_RUNNING 0x00000010 /* dma is running */
+#define CHN_F_TRIGGERED 0x00000020
+
+#define CHN_F_BUSY 0x00001000 /* has been opened */
+#define CHN_F_HAS_SIZE 0x00002000 /* user set block size */
+#define CHN_F_NBIO 0x00004000 /* do non-blocking i/o */
+#define CHN_F_INIT 0x00008000 /* changed parameters. need init */
+#define CHN_F_MAPPED 0x00010000 /* has been mmap()ed */
+
+
+#define CHN_F_RESET (CHN_F_BUSY)
diff --git a/sys/dev/pcm/datatypes.h b/sys/dev/pcm/datatypes.h
new file mode 100644
index 0000000..50928f6
--- /dev/null
+++ b/sys/dev/pcm/datatypes.h
@@ -0,0 +1,156 @@
+/*
+ * Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk>
+ * 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$
+ */
+
+typedef struct _snd_mixer snd_mixer;
+typedef struct _snd_dbuf snd_dbuf;
+typedef struct _snddev_info snddev_info;
+typedef struct _pcmchan_caps pcmchan_caps;
+typedef struct _pcm_feeder pcm_feeder;
+typedef struct _pcm_channel pcm_channel;
+
+typedef int (mix_set_t)(snd_mixer *m, unsigned dev, unsigned left, unsigned right);
+typedef int (mix_recsrc_t)(snd_mixer *m, u_int32_t src);
+typedef int (mix_init_t)(snd_mixer *m);
+
+struct _snd_mixer {
+ char name[64];
+ mix_init_t *init;
+ mix_set_t *set;
+ mix_recsrc_t *setrecsrc;
+
+ void *devinfo;
+ u_int32_t devs;
+ u_int32_t recdevs;
+ u_int32_t recsrc;
+ u_int16_t level[32];
+};
+
+/*
+ * descriptor of a dma buffer. See dmabuf.c for documentation.
+ * (rp,rl) and (fp,fl) identify the READY and FREE regions of the
+ * buffer. dl contains the length used for dma transfer, dl>0 also
+ * means that the channel is busy and there is a DMA transfer in progress.
+ */
+
+struct _snd_dbuf {
+ char *buf;
+ int bufsize;
+ volatile int rp, fp; /* pointers to the ready and free area */
+ volatile int dl; /* transfer size */
+ volatile int rl, fl; /* lenght of ready and free areas. */
+ volatile u_int32_t int_count, prev_int_count;
+ int chan, dir; /* dma channel */
+ int sample_size; /* 1, 2, 4 */
+ struct selinfo sel;
+ u_long total; /* total bytes processed */
+ u_long prev_total; /* copy of the above when GETxPTR called */
+ int first_poll;
+ bus_dmamap_t dmamap;
+};
+
+typedef int (pcmfeed_init_t)(pcm_feeder *feeder);
+typedef int (pcmfeed_free_t)(pcm_feeder *feeder);
+typedef int (pcmfeed_feed_t)(pcm_feeder *feeder, u_int8_t *buffer, u_int32_t count, struct uio *stream);
+
+struct _pcm_feeder {
+ char name[16];
+ pcmfeed_init_t *init;
+ pcmfeed_free_t *free;
+ pcmfeed_feed_t *feed;
+ void *data;
+ pcm_feeder *source;
+};
+
+struct _pcmchan_caps {
+ u_int32_t minspeed, maxspeed;
+ u_int32_t formats, bestfmt;
+};
+
+typedef void *(pcmchan_init_t)(void *devinfo, snd_dbuf *b, pcm_channel *c, int dir);
+typedef int (pcmchan_setdir_t)(void *data, int dir);
+typedef int (pcmchan_setformat_t)(void *data, u_int32_t format);
+typedef int (pcmchan_setspeed_t)(void *data, u_int32_t speed);
+typedef int (pcmchan_setblocksize_t)(void *data, u_int32_t blocksize);
+typedef int (pcmchan_trigger_t)(void *data, int go);
+typedef int (pcmchan_getptr_t)(void *data);
+typedef pcmchan_caps *(pcmchan_getcaps_t)(void *data);
+
+struct _pcm_channel {
+ pcmchan_init_t *init;
+ pcmchan_setdir_t *setdir;
+ pcmchan_setformat_t *setformat;
+ pcmchan_setspeed_t *setspeed;
+ pcmchan_setblocksize_t *setblocksize;
+ pcmchan_trigger_t *trigger;
+ pcmchan_getptr_t *getptr;
+ pcmchan_getcaps_t *getcaps;
+ pcm_feeder *feeder;
+
+ int volume;
+ u_int32_t speed;
+ u_int32_t flags;
+ u_int32_t format, hwfmt;
+ u_int32_t blocksize;
+
+ int direction;
+ snd_dbuf buffer;
+ void *devinfo;
+};
+
+#define SND_STATUSLEN 64
+/* descriptor of audio device */
+struct _snddev_info {
+ pcm_channel *play, *rec, **aplay, **arec, fakechan;
+ unsigned playcount, reccount, chancount;
+ snd_mixer mixer;
+ u_long magic;
+ unsigned flags;
+ void *devinfo;
+ char status[SND_STATUSLEN];
+};
+
+/* mixer description structure and macros - these should go away,
+ * only sb.[ch] and mss.[ch] use them
+ */
+struct mixer_def {
+ u_int regno:7;
+ u_int polarity:1; /* 1 means reversed */
+ u_int bitoffs:4;
+ u_int nbits:4;
+};
+typedef struct mixer_def mixer_ent;
+typedef struct mixer_def mixer_tab[32][2];
+
+#define MIX_ENT(name, reg_l, pol_l, pos_l, len_l, reg_r, pol_r, pos_r, len_r) \
+ {{reg_l, pol_l, pos_l, len_l}, {reg_r, pol_r, pos_r, len_r}}
+
+#define PMIX_ENT(name, reg_l, pos_l, len_l, reg_r, pos_r, len_r) \
+ {{reg_l, 0, pos_l, len_l}, {reg_r, 0, pos_r, len_r}}
+
+#define MIX_NONE(name) MIX_ENT(name, 0,0,0,0, 0,0,0,0)
+
diff --git a/sys/dev/pcm/dsp.c b/sys/dev/pcm/dsp.c
new file mode 100644
index 0000000..a868e92
--- /dev/null
+++ b/sys/dev/pcm/dsp.c
@@ -0,0 +1,543 @@
+/*
+ * Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk>
+ * 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$
+ */
+
+#include <sys/param.h>
+#include <sys/queue.h>
+#include <sys/kernel.h>
+
+#include <dev/pcm/sound.h>
+
+static int getchns(snddev_info *d, int chan, pcm_channel **rdch, pcm_channel **wrch);
+
+static pcm_channel *
+allocchn(snddev_info *d, int direction)
+{
+ pcm_channel *chns = (direction == PCMDIR_PLAY)? d->play : d->rec;
+ int i, cnt = (direction == PCMDIR_PLAY)? d->playcount : d->reccount;
+ for (i = 0; i < cnt; i++) {
+ if (!(chns[i].flags & CHN_F_BUSY)) {
+ chns[i].flags |= CHN_F_BUSY;
+ return &chns[i];
+ }
+ }
+ return NULL;
+}
+
+static int
+getchns(snddev_info *d, int chan, pcm_channel **rdch, pcm_channel **wrch)
+{
+ if ((d->flags & SD_F_PRIO_SET) == SD_F_PRIO_SET)
+ panic("read and write both prioritised");
+ if (d->flags & SD_F_SIMPLEX) {
+ *rdch = (d->flags & SD_F_PRIO_RD)? d->arec[chan] : &d->fakechan;
+ *wrch = (d->flags & SD_F_PRIO_WR)? d->aplay[chan] : &d->fakechan;
+ } else {
+ *rdch = d->arec[chan];
+ *wrch = d->aplay[chan];
+ }
+ return 0;
+}
+
+static void
+setchns(snddev_info *d, int chan)
+{
+ if ((d->flags & SD_F_PRIO_SET) == SD_F_PRIO_SET)
+ panic("read and write both prioritised");
+ d->flags |= SD_F_DIR_SET;
+ if (d->flags & SD_F_EVILSB16) {
+ if ((d->flags & SD_F_PRIO_RD) && (d->aplay[chan])) {
+ pcm_channel *tmp;
+ tmp = d->arec[chan];
+ d->arec[chan] = d->aplay[chan];
+ d->aplay[chan] = tmp;
+ }
+ if (d->aplay[chan]) chn_setdir(d->aplay[chan], PCMDIR_PLAY);
+ if (d->arec[chan]) chn_setdir(d->arec[chan], PCMDIR_REC);
+ }
+}
+
+int
+dsp_open(snddev_info *d, int chan, int oflags, int devtype)
+{
+ pcm_channel *rdch = NULL, *wrch = NULL;
+ u_int32_t fmt;
+
+ if (chan >= d->chancount) return ENODEV;
+ if (d->aplay[chan] || d->arec[chan]) return EBUSY;
+ if (oflags & FREAD) {
+ rdch = allocchn(d, PCMDIR_REC);
+ if (!rdch) return EBUSY;
+ }
+ if (oflags & FWRITE) {
+ wrch = allocchn(d, PCMDIR_PLAY);
+ if (!wrch) {
+ if (rdch) rdch->flags &= ~CHN_F_BUSY;
+ return EBUSY;
+ }
+ }
+ d->aplay[chan] = wrch;
+ d->arec[chan] = rdch;
+ switch (devtype) {
+ case SND_DEV_DSP16:
+ fmt = AFMT_S16_LE;
+ break;
+
+ case SND_DEV_DSP:
+ fmt = AFMT_U8;
+ break;
+
+ case SND_DEV_AUDIO:
+ fmt = AFMT_MU_LAW;
+ break;
+
+ default:
+ return ENXIO;
+ }
+
+ if (rdch) {
+ chn_reset(rdch);
+ if (oflags & O_NONBLOCK) rdch->flags |= CHN_F_NBIO;
+ rdch->volume = (100 << 8) | 100;
+ rdch->format = fmt;
+ rdch->speed = DSP_DEFAULT_SPEED;
+ rdch->blocksize = 2048;
+ }
+ if (wrch) {
+ chn_reset(wrch);
+ if (oflags & O_NONBLOCK) wrch->flags |= CHN_F_NBIO;
+ wrch->volume = (100 << 8) | 100;
+ wrch->format = fmt;
+ wrch->speed = DSP_DEFAULT_SPEED;
+ wrch->blocksize = 2048;
+ }
+ return 0;
+}
+
+int
+dsp_close(snddev_info *d, int chan, int devtype)
+{
+ pcm_channel *rdch, *wrch;
+
+ d->flags &= ~SD_F_TRANSIENT;
+ getchns(d, chan, &rdch, &wrch);
+
+ if (rdch) {
+ chn_abort(rdch);
+ rdch->flags &= ~(CHN_F_BUSY | CHN_F_RUNNING | CHN_F_MAPPED);
+ }
+ if (wrch) wrch->flags &= ~(CHN_F_BUSY | CHN_F_RUNNING | CHN_F_MAPPED);
+ d->aplay[chan] = NULL;
+ d->arec[chan] = NULL;
+ return 0;
+}
+
+int
+dsp_read(snddev_info *d, int chan, struct uio *buf, int flag)
+{
+ pcm_channel *rdch, *wrch;
+
+ if (!(d->flags & SD_F_PRIO_SET)) d->flags |= SD_F_PRIO_RD;
+ if (!(d->flags & SD_F_DIR_SET)) setchns(d, chan);
+ getchns(d, chan, &rdch, &wrch);
+ if (!rdch || !(rdch->flags & CHN_F_BUSY))
+ panic("dsp_read: non%s channel", rdch? "busy" : "existant");
+ if (rdch->flags & CHN_F_MAPPED) return EINVAL;
+ if (!(rdch->flags & CHN_F_RUNNING)) rdch->flags |= CHN_F_RUNNING;
+ return chn_read(rdch, buf);
+}
+
+int
+dsp_write(snddev_info *d, int chan, struct uio *buf, int flag)
+{
+ pcm_channel *rdch, *wrch;
+
+ if (!(d->flags & SD_F_PRIO_SET)) d->flags |= SD_F_PRIO_WR;
+ if (!(d->flags & SD_F_DIR_SET)) setchns(d, chan);
+ getchns(d, chan, &rdch, &wrch);
+ if (!wrch || !(wrch->flags & CHN_F_BUSY))
+ panic("dsp_write: non%s channel", wrch? "busy" : "existant");
+ if (wrch->flags & CHN_F_MAPPED) return EINVAL;
+ if (!(wrch->flags & CHN_F_RUNNING)) wrch->flags |= CHN_F_RUNNING;
+ return chn_write(wrch, buf);
+}
+
+int
+dsp_ioctl(snddev_info *d, int chan, u_long cmd, caddr_t arg)
+{
+ int ret = 0, *arg_i = (int *)arg;
+ u_long s;
+ pcm_channel *wrch = NULL, *rdch = NULL;
+
+ getchns(d, chan, &rdch, &wrch);
+
+ /*
+ * all routines are called with int. blocked. Make sure that
+ * ints are re-enabled when calling slow or blocking functions!
+ */
+ s = spltty();
+ switch(cmd) {
+
+ /*
+ * we start with the new ioctl interface.
+ */
+ case AIONWRITE: /* how many bytes can write ? */
+ if (wrch && wrch->buffer.dl) chn_dmaupdate(wrch);
+ *arg_i = wrch? wrch->buffer.fl : 0;
+ break;
+
+ case AIOSSIZE: /* set the current blocksize */
+ {
+ struct snd_size *p = (struct snd_size *)arg;
+ splx(s);
+ if (wrch) chn_setblocksize(wrch, p->play_size);
+ if (rdch) chn_setblocksize(rdch, p->rec_size);
+ }
+ /* FALLTHROUGH */
+ case AIOGSIZE: /* get the current blocksize */
+ {
+ struct snd_size *p = (struct snd_size *)arg;
+ if (wrch) p->play_size = wrch->blocksize;
+ if (rdch) p->rec_size = rdch->blocksize;
+ }
+ break;
+
+ case AIOSFMT:
+ {
+ snd_chan_param *p = (snd_chan_param *)arg;
+ splx(s);
+ if (wrch) {
+ chn_setformat(wrch, p->play_format);
+ chn_setspeed(wrch, p->play_rate);
+ }
+ if (rdch) {
+ chn_setformat(rdch, p->rec_format);
+ chn_setspeed(rdch, p->rec_rate);
+ }
+ }
+ /* FALLTHROUGH */
+
+ case AIOGFMT:
+ {
+ snd_chan_param *p = (snd_chan_param *)arg;
+ p->play_rate = wrch? wrch->speed : 0;
+ p->rec_rate = rdch? rdch->speed : 0;
+ p->play_format = wrch? wrch->format : 0;
+ p->rec_format = rdch? rdch->format : 0;
+ }
+ break;
+
+ case AIOGCAP: /* get capabilities */
+ {
+ snd_capabilities *p = (snd_capabilities *)arg;
+ pcmchan_caps *pcaps = NULL, *rcaps = NULL;
+ if (rdch) rcaps = chn_getcaps(rdch);
+ if (wrch) pcaps = chn_getcaps(wrch);
+ p->rate_min = max(rcaps? rcaps->minspeed : 0,
+ pcaps? pcaps->minspeed : 0);
+ p->rate_max = min(rcaps? rcaps->maxspeed : 1000000,
+ pcaps? pcaps->maxspeed : 1000000);
+ p->bufsize = min(rdch? rdch->buffer.bufsize : 1000000,
+ wrch? wrch->buffer.bufsize : 1000000);
+ /* XXX bad on sb16 */
+ p->formats = (rcaps? rcaps->formats : 0xffffffff) &
+ (pcaps? pcaps->formats : 0xffffffff);
+ p->mixers = 1; /* default: one mixer */
+ p->inputs = d->mixer.devs;
+ p->left = p->right = 100;
+ }
+ break;
+
+ case AIOSTOP:
+ if (*arg_i == AIOSYNC_PLAY && wrch) *arg_i = chn_abort(wrch);
+ else if (*arg_i == AIOSYNC_CAPTURE && rdch) *arg_i = chn_abort(rdch);
+ else {
+ splx(s);
+ printf("AIOSTOP: bad channel 0x%x\n", *arg_i);
+ *arg_i = 0;
+ }
+ break;
+
+ case AIOSYNC:
+ printf("AIOSYNC chan 0x%03lx pos %lu unimplemented\n",
+ ((snd_sync_parm *)arg)->chan, ((snd_sync_parm *)arg)->pos);
+ break;
+ /*
+ * here follow the standard ioctls (filio.h etc.)
+ */
+ case FIONREAD: /* get # bytes to read */
+ if (rdch && rdch->buffer.dl) chn_dmaupdate(rdch);
+ *arg_i = rdch? rdch->buffer.rl : 0;
+ break;
+
+ case FIOASYNC: /*set/clear async i/o */
+ DEB( printf("FIOASYNC\n") ; )
+ break;
+
+ case SNDCTL_DSP_NONBLOCK:
+ case FIONBIO: /* set/clear non-blocking i/o */
+ if (rdch) rdch->flags &= ~CHN_F_NBIO;
+ if (wrch) wrch->flags &= ~CHN_F_NBIO;
+ if (*arg_i) {
+ if (rdch) rdch->flags |= CHN_F_NBIO;
+ if (wrch) wrch->flags |= CHN_F_NBIO;
+ }
+ break;
+
+ /*
+ * Finally, here is the linux-compatible ioctl interface
+ */
+ #define THE_REAL_SNDCTL_DSP_GETBLKSIZE _IOWR('P', 4, int)
+ case THE_REAL_SNDCTL_DSP_GETBLKSIZE:
+ case SNDCTL_DSP_GETBLKSIZE:
+ *arg_i = wrch? wrch->blocksize : 0; /* XXX rdch? */
+ break ;
+
+ case SNDCTL_DSP_SETBLKSIZE:
+ splx(s);
+ if (wrch) chn_setblocksize(wrch, *arg_i);
+ if (rdch) chn_setblocksize(rdch, *arg_i);
+ break;
+
+ case SNDCTL_DSP_RESET:
+ DEB(printf("dsp reset\n"));
+ if (wrch) chn_abort(wrch);
+ if (rdch) chn_abort(rdch);
+ break;
+
+ case SNDCTL_DSP_SYNC:
+ printf("dsp sync\n");
+ splx(s);
+ if (wrch) chn_sync(wrch, wrch->buffer.bufsize - 4);
+ break;
+
+ case SNDCTL_DSP_SPEED:
+ splx(s);
+ if (wrch) chn_setspeed(wrch, *arg_i);
+ if (rdch) chn_setspeed(rdch, *arg_i);
+ /* fallthru */
+
+ case SOUND_PCM_READ_RATE:
+ *arg_i = wrch? wrch->speed : rdch->speed;
+ break;
+
+ case SNDCTL_DSP_STEREO:
+ splx(s);
+ if (wrch) chn_setformat(wrch, (wrch->format & ~AFMT_STEREO) |
+ ((*arg_i)? AFMT_STEREO : 0));
+ if (rdch) chn_setformat(rdch, (rdch->format & ~AFMT_STEREO) |
+ ((*arg_i)? AFMT_STEREO : 0));
+ *arg_i = ((wrch? wrch->format : rdch->format) & AFMT_STEREO)? 1 : 0;
+ break;
+
+ case SOUND_PCM_WRITE_CHANNELS:
+ splx(s);
+ if (wrch) chn_setformat(wrch, (wrch->format & ~AFMT_STEREO) |
+ ((*arg_i == 2)? AFMT_STEREO : 0));
+ if (rdch) chn_setformat(rdch, (rdch->format & ~AFMT_STEREO) |
+ ((*arg_i == 2)? AFMT_STEREO : 0));
+ /* fallthru */
+
+ case SOUND_PCM_READ_CHANNELS:
+ *arg_i = ((wrch? wrch->format : rdch->format) & AFMT_STEREO)? 2 : 1;
+ break;
+
+ case SNDCTL_DSP_GETFMTS: /* returns a mask of supported fmts */
+ *arg_i = wrch? chn_getcaps(wrch)->formats : chn_getcaps(rdch)->formats;
+ break ;
+
+ case SNDCTL_DSP_SETFMT: /* sets _one_ format */
+ splx(s);
+ if (wrch) chn_setformat(wrch, *arg_i);
+ if (rdch) chn_setformat(rdch, *arg_i);
+ *arg_i = wrch? wrch->format : rdch->format;
+ break;
+
+ case SNDCTL_DSP_SUBDIVIDE:
+ /* XXX watch out, this is RW! */
+ DEB(printf("SNDCTL_DSP_SUBDIVIDE unimplemented\n");)
+ break;
+
+ case SNDCTL_DSP_SETFRAGMENT:
+ /* XXX watch out, this is RW! */
+ DEB(printf("SNDCTL_DSP_SETFRAGMENT 0x%08x\n", *(int *)arg));
+ {
+ int bytes = 1 << min(*arg_i & 0xffff, 16);
+ int count = (*arg_i >> 16) & 0xffff;
+ pcm_channel *c = wrch? wrch : rdch;
+ splx(s);
+ if (rdch) chn_setblocksize(rdch, bytes);
+ if (wrch) chn_setblocksize(wrch, bytes);
+
+ /* eg: 4dwave can only interrupt at buffer midpoint, so
+ * it will force blocksize == bufsize/2
+ */
+ count = c->buffer.bufsize / c->blocksize;
+ bytes = ffs(c->blocksize) - 1;
+ *arg_i = (count << 16) | bytes;
+ }
+ break;
+
+ case SNDCTL_DSP_GETISPACE:
+ /* return space available in the input queue */
+ {
+ audio_buf_info *a = (audio_buf_info *)arg;
+ if (rdch) {
+ snd_dbuf *b = &rdch->buffer;
+ if (b->dl) chn_dmaupdate(rdch);
+ a->bytes = b->fl;
+ a->fragments = 1;
+ a->fragstotal = b->bufsize / rdch->blocksize;
+ a->fragsize = rdch->blocksize;
+ }
+ }
+ break;
+
+ case SNDCTL_DSP_GETOSPACE:
+ /* return space available in the output queue */
+ {
+ audio_buf_info *a = (audio_buf_info *)arg;
+ if (wrch) {
+ snd_dbuf *b = &wrch->buffer;
+ if (b->dl) chn_dmaupdate(wrch);
+ a->bytes = b->fl;
+ a->fragments = 1;
+ a->fragstotal = b->bufsize / wrch->blocksize;
+ a->fragsize = wrch->blocksize;
+ }
+ }
+ break;
+
+ case SNDCTL_DSP_GETIPTR:
+ {
+ count_info *a = (count_info *)arg;
+ if (rdch) {
+ snd_dbuf *b = &rdch->buffer;
+ if (b->dl) chn_dmaupdate(rdch);
+ a->bytes = b->total;
+ a->blocks = (b->total - b->prev_total) / rdch->blocksize;
+ a->ptr = b->fp;
+ b->prev_total += a->blocks * rdch->blocksize;
+ } else ret = EINVAL;
+ }
+ break;
+
+ case SNDCTL_DSP_GETOPTR:
+ {
+ count_info *a = (count_info *)arg;
+ if (wrch) {
+ snd_dbuf *b = &wrch->buffer;
+ if (b->dl) chn_dmaupdate(wrch);
+ a->bytes = b->total;
+ a->blocks = (b->total - b->prev_total) / wrch->blocksize;
+ a->ptr = b->rp;
+ b->prev_total += a->blocks * wrch->blocksize;
+ } else ret = EINVAL;
+ }
+ break;
+
+ case SNDCTL_DSP_GETCAPS:
+ *arg_i = DSP_CAP_REALTIME | DSP_CAP_MMAP | DSP_CAP_TRIGGER;
+ if (rdch && wrch && !(d->flags & SD_F_SIMPLEX))
+ *arg_i |= DSP_CAP_DUPLEX;
+ break;
+
+ case SOUND_PCM_READ_BITS:
+ *arg_i = ((wrch? wrch->format : rdch->format) & AFMT_16BIT)? 16 : 8;
+ break;
+
+ case SNDCTL_DSP_SETTRIGGER:
+ if (rdch) {
+ rdch->flags &= ~CHN_F_TRIGGERED;
+ if (*arg_i & PCM_ENABLE_INPUT)
+ rdch->flags |= CHN_F_TRIGGERED;
+ chn_intr(rdch);
+ }
+ if (wrch) {
+ wrch->flags &= ~CHN_F_TRIGGERED;
+ if (*arg_i & PCM_ENABLE_OUTPUT)
+ wrch->flags |= CHN_F_TRIGGERED;
+ chn_intr(wrch);
+ }
+ break;
+
+ case SNDCTL_DSP_GETTRIGGER:
+ *arg_i = 0;
+ if (wrch && wrch->flags & CHN_F_TRIGGERED)
+ *arg_i |= PCM_ENABLE_OUTPUT;
+ if (rdch && rdch->flags & CHN_F_TRIGGERED)
+ *arg_i |= PCM_ENABLE_INPUT;
+ break;
+
+ case SNDCTL_DSP_MAPINBUF:
+ case SNDCTL_DSP_MAPOUTBUF:
+ case SNDCTL_DSP_SETSYNCRO:
+ /* undocumented */
+
+ case SNDCTL_DSP_POST:
+ case SOUND_PCM_WRITE_FILTER:
+ case SOUND_PCM_READ_FILTER:
+ /* dunno what these do, don't sound important */
+ default:
+ DEB(printf("default ioctl snd%d fn 0x%08x fail\n", unit, cmd));
+ ret = EINVAL;
+ break;
+ }
+ splx(s);
+ return ret;
+}
+
+int
+dsp_poll(snddev_info *d, int chan, int events, struct proc *p)
+{
+ int ret = 0, e;
+ pcm_channel *wrch = NULL, *rdch = NULL;
+
+ getchns(d, chan, &rdch, &wrch);
+ e = events & (POLLOUT | POLLWRNORM);
+ if (wrch && e) ret |= chn_poll(wrch, e, p);
+ e = events & (POLLIN | POLLRDNORM);
+ if (rdch && e) ret |= chn_poll(rdch, e, p);
+ return ret;
+}
+
+int
+dsp_mmap(snddev_info *d, int chan, vm_offset_t offset, int nprot)
+{
+ pcm_channel *wrch = NULL, *rdch = NULL, *c = NULL;
+
+ getchns(d, chan, &rdch, &wrch);
+ /* XXX this is broken by line 204 of vm/device_pager.c, so force write buffer */
+ if (1 || (wrch && (nprot & PROT_WRITE))) c = wrch;
+ else if (rdch && (nprot & PROT_READ)) c = rdch;
+ if (c) {
+ c->flags |= CHN_F_MAPPED;
+ return atop(vtophys(c->buffer.buf + offset));
+ }
+ return -1;
+}
+
diff --git a/sys/dev/pcm/dsp.h b/sys/dev/pcm/dsp.h
new file mode 100644
index 0000000..27b7e3d
--- /dev/null
+++ b/sys/dev/pcm/dsp.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk>
+ * 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$
+ */
+
+int dsp_open(snddev_info *d, int chan, int oflags, int devtype);
+int dsp_close(snddev_info *d, int chan, int devtype);
+int dsp_read(snddev_info *d, int chan, struct uio *buf, int flag);
+int dsp_write(snddev_info *d, int chan, struct uio *buf, int flag);
+int dsp_ioctl(snddev_info *d, int chan, u_long cmd, caddr_t arg);
+int dsp_poll(snddev_info *d, int chan, int events, struct proc *p);
+int dsp_mmap(snddev_info *d, int chan, vm_offset_t offset, int nprot);
+
+
diff --git a/sys/dev/pcm/fake.c b/sys/dev/pcm/fake.c
new file mode 100644
index 0000000..d050eef
--- /dev/null
+++ b/sys/dev/pcm/fake.c
@@ -0,0 +1,115 @@
+/*
+ * Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk>
+ * 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$
+ */
+
+#include "pcm.h"
+
+#include <dev/pcm/sound.h>
+
+/* channel interface */
+static void *fkchan_init(void *devinfo, snd_dbuf *b, pcm_channel *c, int dir);
+static int fkchan_setdir(void *data, int dir);
+static int fkchan_setformat(void *data, u_int32_t format);
+static int fkchan_setspeed(void *data, u_int32_t speed);
+static int fkchan_setblocksize(void *data, u_int32_t blocksize);
+static int fkchan_trigger(void *data, int go);
+static int fkchan_getptr(void *data);
+static pcmchan_caps *fkchan_getcaps(void *data);
+
+static pcmchan_caps fk_caps = {
+ 4000, 48000,
+ AFMT_STEREO | AFMT_U8 | AFMT_S8 | AFMT_S16_LE | AFMT_S16_BE | AFMT_U16_LE | AFMT_U16_BE
+};
+
+static pcm_channel fk_chantemplate = {
+ fkchan_init,
+ fkchan_setdir,
+ fkchan_setformat,
+ fkchan_setspeed,
+ fkchan_setblocksize,
+ fkchan_trigger,
+ fkchan_getptr,
+ fkchan_getcaps,
+};
+
+/* channel interface */
+static void *
+fkchan_init(void *devinfo, snd_dbuf *b, pcm_channel *c, int dir)
+{
+ b->bufsize = 16384;
+ b->buf = malloc(b->bufsize, M_DEVBUF, M_NOWAIT);
+ return (void *)0xbabef00d;
+}
+
+static int
+fkchan_setdir(void *data, int dir)
+{
+ return 0;
+}
+
+static int
+fkchan_setformat(void *data, u_int32_t format)
+{
+ return 0;
+}
+
+static int
+fkchan_setspeed(void *data, u_int32_t speed)
+{
+ return speed;
+}
+
+static int
+fkchan_setblocksize(void *data, u_int32_t blocksize)
+{
+ return blocksize;
+}
+
+static int
+fkchan_trigger(void *data, int go)
+{
+ return 0;
+}
+
+static int
+fkchan_getptr(void *data)
+{
+ return 0;
+}
+
+static pcmchan_caps *
+fkchan_getcaps(void *data)
+{
+ return &fk_caps;
+}
+
+int
+fkchan_setup(pcm_channel *c)
+{
+ *c = fk_chantemplate;
+ return 0;
+}
diff --git a/sys/dev/pcm/feeder.c b/sys/dev/pcm/feeder.c
new file mode 100644
index 0000000..3e51d74
--- /dev/null
+++ b/sys/dev/pcm/feeder.c
@@ -0,0 +1,403 @@
+/*
+ * Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk>
+ * 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$
+ */
+
+#include <dev/pcm/sound.h>
+
+static int chn_addfeeder(pcm_channel *c, pcm_feeder *f);
+static int chn_removefeeder(pcm_channel *c);
+
+#define FEEDBUFSZ 8192
+
+static unsigned char ulaw_to_u8[] = {
+ 3, 7, 11, 15, 19, 23, 27, 31,
+ 35, 39, 43, 47, 51, 55, 59, 63,
+ 66, 68, 70, 72, 74, 76, 78, 80,
+ 82, 84, 86, 88, 90, 92, 94, 96,
+ 98, 99, 100, 101, 102, 103, 104, 105,
+ 106, 107, 108, 109, 110, 111, 112, 113,
+ 113, 114, 114, 115, 115, 116, 116, 117,
+ 117, 118, 118, 119, 119, 120, 120, 121,
+ 121, 121, 122, 122, 122, 122, 123, 123,
+ 123, 123, 124, 124, 124, 124, 125, 125,
+ 125, 125, 125, 125, 126, 126, 126, 126,
+ 126, 126, 126, 126, 127, 127, 127, 127,
+ 127, 127, 127, 127, 127, 127, 127, 127,
+ 128, 128, 128, 128, 128, 128, 128, 128,
+ 128, 128, 128, 128, 128, 128, 128, 128,
+ 128, 128, 128, 128, 128, 128, 128, 128,
+ 253, 249, 245, 241, 237, 233, 229, 225,
+ 221, 217, 213, 209, 205, 201, 197, 193,
+ 190, 188, 186, 184, 182, 180, 178, 176,
+ 174, 172, 170, 168, 166, 164, 162, 160,
+ 158, 157, 156, 155, 154, 153, 152, 151,
+ 150, 149, 148, 147, 146, 145, 144, 143,
+ 143, 142, 142, 141, 141, 140, 140, 139,
+ 139, 138, 138, 137, 137, 136, 136, 135,
+ 135, 135, 134, 134, 134, 134, 133, 133,
+ 133, 133, 132, 132, 132, 132, 131, 131,
+ 131, 131, 131, 131, 130, 130, 130, 130,
+ 130, 130, 130, 130, 129, 129, 129, 129,
+ 129, 129, 129, 129, 129, 129, 129, 129,
+ 128, 128, 128, 128, 128, 128, 128, 128,
+ 128, 128, 128, 128, 128, 128, 128, 128,
+ 128, 128, 128, 128, 128, 128, 128, 128,
+};
+
+static unsigned char u8_to_ulaw[] = {
+ 0, 0, 0, 0, 0, 1, 1, 1,
+ 1, 2, 2, 2, 2, 3, 3, 3,
+ 3, 4, 4, 4, 4, 5, 5, 5,
+ 5, 6, 6, 6, 6, 7, 7, 7,
+ 7, 8, 8, 8, 8, 9, 9, 9,
+ 9, 10, 10, 10, 10, 11, 11, 11,
+ 11, 12, 12, 12, 12, 13, 13, 13,
+ 13, 14, 14, 14, 14, 15, 15, 15,
+ 15, 16, 16, 17, 17, 18, 18, 19,
+ 19, 20, 20, 21, 21, 22, 22, 23,
+ 23, 24, 24, 25, 25, 26, 26, 27,
+ 27, 28, 28, 29, 29, 30, 30, 31,
+ 31, 32, 33, 34, 35, 36, 37, 38,
+ 39, 40, 41, 42, 43, 44, 45, 46,
+ 47, 49, 51, 53, 55, 57, 59, 61,
+ 63, 66, 70, 74, 78, 84, 92, 104,
+ 254, 231, 219, 211, 205, 201, 197, 193,
+ 190, 188, 186, 184, 182, 180, 178, 176,
+ 175, 174, 173, 172, 171, 170, 169, 168,
+ 167, 166, 165, 164, 163, 162, 161, 160,
+ 159, 159, 158, 158, 157, 157, 156, 156,
+ 155, 155, 154, 154, 153, 153, 152, 152,
+ 151, 151, 150, 150, 149, 149, 148, 148,
+ 147, 147, 146, 146, 145, 145, 144, 144,
+ 143, 143, 143, 143, 142, 142, 142, 142,
+ 141, 141, 141, 141, 140, 140, 140, 140,
+ 139, 139, 139, 139, 138, 138, 138, 138,
+ 137, 137, 137, 137, 136, 136, 136, 136,
+ 135, 135, 135, 135, 134, 134, 134, 134,
+ 133, 133, 133, 133, 132, 132, 132, 132,
+ 131, 131, 131, 131, 130, 130, 130, 130,
+ 129, 129, 129, 129, 128, 128, 128, 128,
+};
+
+/*****************************************************************************/
+
+static int
+feed_root(pcm_feeder *feeder, u_int8_t *buffer, u_int32_t count, struct uio *stream)
+{
+ int ret, tmp;
+ if (!count) panic("feed_root: count == 0");
+ tmp = stream->uio_resid;
+ ret = uiomove(buffer, count, stream);
+ if (ret) panic("feed_root: uiomove failed");
+ tmp -= stream->uio_resid;
+ if (!tmp) panic("feed_root: uiomove didn't");
+ return tmp;
+}
+pcm_feeder feeder_root = { "root", NULL, NULL, feed_root };
+
+/*****************************************************************************/
+
+static int
+feed_8to16(pcm_feeder *f, u_int8_t *b, u_int32_t count, struct uio *stream)
+{
+ int i, j, k;
+ k = f->source->feed(f->source, b, count / 2, stream);
+ j = k - 1;
+ i = j * 2 + 1;
+ while (i > 0 && j >= 0) {
+ b[i--] = b[j--];
+ b[i--] = 0;
+ }
+ return k * 2;
+}
+static pcm_feeder feeder_8to16 = { "8to16", NULL, NULL, feed_8to16 };
+
+/*****************************************************************************/
+
+static int
+feed_16to8_init(pcm_feeder *f)
+{
+ f->data = malloc(FEEDBUFSZ, M_DEVBUF, M_NOWAIT);
+ return (f->data == NULL);
+}
+
+static int
+feed_16to8_free(pcm_feeder *f)
+{
+ if (f->data) free(f->data, M_DEVBUF);
+ f->data = NULL;
+ return 0;
+}
+
+static int
+feed_16to8le(pcm_feeder *f, u_int8_t *b, u_int32_t count, struct uio *stream)
+{
+ u_int32_t i = 0, toget = count * 2;
+ int j = 1, k;
+ k = f->source->feed(f->source, f->data, min(toget, FEEDBUFSZ), stream);
+ while (j < k) {
+ b[i++] = ((u_int8_t *)f->data)[j];
+ j += 2;
+ }
+ return i;
+}
+static pcm_feeder feeder_16to8le =
+ { "16to8le", feed_16to8_init, feed_16to8_free, feed_16to8le };
+
+/*****************************************************************************/
+
+static int
+feed_monotostereo8(pcm_feeder *f, u_int8_t *b, u_int32_t count, struct uio *stream)
+{
+ int i, j, k = f->source->feed(f->source, b, count / 2, stream);
+ j = k - 1;
+ i = j * 2 + 1;
+ while (i > 0 && j >= 0) {
+ b[i--] = b[j];
+ b[i--] = b[j];
+ j--;
+ }
+ return k * 2;
+}
+static pcm_feeder feeder_monotostereo8 =
+ { "monotostereo8", NULL, NULL, feed_monotostereo8 };
+
+/*****************************************************************************/
+
+static int
+feed_stereotomono8_init(pcm_feeder *f)
+{
+ f->data = malloc(FEEDBUFSZ, M_DEVBUF, M_NOWAIT);
+ return (f->data == NULL);
+}
+
+static int
+feed_stereotomono8_free(pcm_feeder *f)
+{
+ if (f->data) free(f->data, M_DEVBUF);
+ f->data = NULL;
+ return 0;
+}
+
+static int
+feed_stereotomono8(pcm_feeder *f, u_int8_t *b, u_int32_t count, struct uio *stream)
+{
+ u_int32_t i = 0, toget = count * 2;
+ int j = 0, k;
+ k = f->source->feed(f->source, f->data, min(toget, FEEDBUFSZ), stream);
+ while (j < k) {
+ b[i++] = ((u_int8_t *)f->data)[j];
+ j += 2;
+ }
+ return i;
+}
+static pcm_feeder feeder_stereotomono8 =
+ { "stereotomono8", feed_stereotomono8_init, feed_stereotomono8_free,
+ feed_stereotomono8 };
+
+/*****************************************************************************/
+
+static int
+feed_endian(pcm_feeder *f, u_int8_t *b, u_int32_t count, struct uio *stream)
+{
+ u_int8_t t;
+ int i = 0, j = f->source->feed(f->source, b, count, stream);
+ while (i < j) {
+ t = b[i];
+ b[i] = b[i + 1];
+ b[i + 1] = t;
+ i += 2;
+ }
+ return count;
+}
+static pcm_feeder feeder_endian = { "endian", NULL, NULL, feed_endian };
+
+/*****************************************************************************/
+
+static int
+feed_sign(pcm_feeder *f, u_int8_t *b, u_int32_t count, struct uio *stream)
+{
+ int i = 0, j = f->source->feed(f->source, b, count, stream);
+ int ssz = (int)f->data, ofs = ssz - 1;
+ while (i < j) {
+ b[i + ofs] ^= 0x80;
+ i += ssz;
+ }
+ return i;
+}
+static pcm_feeder feeder_sign8 =
+ { "sign8", NULL, NULL, feed_sign, (void *)1 };
+static pcm_feeder feeder_sign16 =
+ { "sign16", NULL, NULL, feed_sign, (void *)2 };
+
+/*****************************************************************************/
+
+static int
+feed_table(pcm_feeder *f, u_int8_t *b, u_int32_t count, struct uio *stream)
+{
+ int i = 0, j = f->source->feed(f->source, b, count, stream);
+ while (i < j) {
+ b[i] = ((u_int8_t *)f->data)[b[i]];
+ i++;
+ }
+ return i;
+}
+static pcm_feeder feeder_ulawtou8 =
+ { "ulawtou8", NULL, NULL, feed_table, ulaw_to_u8 };
+static pcm_feeder feeder_u8toulaw =
+ { "u8toulaw", NULL, NULL, feed_table, u8_to_ulaw };
+
+/*****************************************************************************/
+
+struct fmtspec {
+ int stereo;
+ int sign;
+ int bit16;
+ int bigendian;
+ int ulaw;
+ int bad;
+};
+
+struct fmtcvt {
+ pcm_feeder *f;
+ struct fmtspec ispec, ospec;
+};
+
+struct fmtcvt cvttab[] = {
+ {&feeder_ulawtou8, {-1, 0, 0, 0, 1}, {-1, 0, 0, 0, 0}},
+ {&feeder_u8toulaw, {-1, 0, 0, 0, 0}, {-1, 0, 0, 0, 1}},
+ {&feeder_sign8, {-1, 0, 0, 0, 0}, {-1, 1, 0, 0, 0}},
+ {&feeder_sign8, {-1, 1, 0, 0, 0}, {-1, 0, 0, 0, 0}},
+ {&feeder_monotostereo8, { 0, -1, 0, 0, -1}, { 1, -1, 0, 0, -1}},
+ {&feeder_stereotomono8, { 1, -1, 0, 0, -1}, { 0, -1, 0, 0, -1}},
+ {&feeder_sign16, {-1, 0, 1, 0, 0}, {-1, 1, 1, 0, 0}},
+ {&feeder_sign16, {-1, 1, 1, 0, 0}, {-1, 0, 1, 0, 0}},
+ {&feeder_8to16, {-1, -1, 0, 0, 0}, {-1, -1, 1, 0, 0}},
+ {&feeder_16to8le, {-1, -1, 1, 0, 0}, {-1, -1, 0, 0, 0}},
+ {&feeder_endian, {-1, -1, 1, 0, 0}, {-1, -1, 1, 1, 0}},
+ {&feeder_endian, {-1, -1, 1, 1, 0}, {-1, -1, 1, 0, 0}},
+};
+#define FEEDERTABSZ (sizeof(cvttab) / sizeof(struct fmtcvt))
+
+static int
+getspec(u_int32_t fmt, struct fmtspec *spec)
+{
+ spec->stereo = (fmt & AFMT_STEREO)? 1 : 0;
+ spec->sign = (fmt & AFMT_SIGNED)? 1 : 0;
+ spec->bit16 = (fmt & AFMT_16BIT)? 1 : 0;
+ spec->bigendian = (fmt & AFMT_BIGENDIAN)? 1 : 0;
+ spec->ulaw = (fmt & AFMT_MU_LAW)? 1 : 0;
+ spec->bad = (fmt & (AFMT_A_LAW | AFMT_MPEG))? 1 : 0;
+ return 0;
+}
+
+static int
+cmp(int x, int y)
+{
+ return (x == -1 || x == y || y == -1)? 1 : 0;
+}
+
+static int
+cmpspec(struct fmtspec *x, struct fmtspec *y)
+{
+ int i = 0;
+ if (cmp(x->stereo, y->stereo)) i |= 0x01;
+ if (cmp(x->sign, y->sign)) i |= 0x02;
+ if (cmp(x->bit16, y->bit16)) i |= 0x04;
+ if (cmp(x->bigendian, y->bigendian)) i |= 0x08;
+ if (cmp(x->ulaw, y->ulaw)) i |= 0x10;
+ return i;
+}
+
+static int
+cvtapply(pcm_channel *c, struct fmtcvt *cvt, struct fmtspec *s)
+{
+ int i = cmpspec(s, &cvt->ospec);
+ chn_addfeeder(c, cvt->f);
+ if (cvt->ospec.stereo != -1) s->stereo = cvt->ospec.stereo;
+ if (cvt->ospec.sign != -1) s->sign = cvt->ospec.sign;
+ if (cvt->ospec.bit16 != -1) s->bit16 = cvt->ospec.bit16;
+ if (cvt->ospec.bigendian != -1) s->bigendian = cvt->ospec.bigendian;
+ if (cvt->ospec.ulaw != -1) s->ulaw = cvt->ospec.ulaw;
+ return i;
+}
+
+int
+chn_feedchain(pcm_channel *c)
+{
+ int i, chosen, iter;
+ u_int32_t mask;
+ struct fmtspec s, t;
+ struct fmtcvt *e;
+
+ while (chn_removefeeder(c) != -1);
+ if ((c->format & chn_getcaps(c)->formats) == c->format)
+ return c->format;
+ getspec(c->format, &s);
+ if (s.bad) return -1;
+ getspec(chn_getcaps(c)->bestfmt, &t);
+ mask = (~cmpspec(&s, &t)) & 0x1f;
+ iter = 0;
+ do {
+ if (mask == 0 || iter >= 8) break;
+ chosen = -1;
+ for (i = 0; i < FEEDERTABSZ && chosen == -1; i++) {
+ e = &cvttab[i];
+ if ((cmpspec(&s, &e->ispec) == 0x1f) &&
+ ((~cmpspec(&e->ispec, &e->ospec)) & mask))
+ chosen = i;
+ }
+ if (chosen != -1) mask &= cvtapply(c, &cvttab[chosen], &s);
+ iter++;
+ } while (chosen != -1);
+ return (iter < 8)? chn_getcaps(c)->bestfmt : -1;
+}
+
+static int
+chn_addfeeder(pcm_channel *c, pcm_feeder *f)
+{
+ pcm_feeder *n;
+ n = malloc(sizeof(pcm_feeder), M_DEVBUF, M_NOWAIT);
+ *n = *f;
+ n->source = c->feeder;
+ c->feeder = n;
+ if (n->init) n->init(n);
+ return 0;
+}
+
+static int
+chn_removefeeder(pcm_channel *c)
+{
+ pcm_feeder *f;
+ if (c->feeder == &feeder_root) return -1;
+ f = c->feeder->source;
+ if (c->feeder->free) c->feeder->free(c->feeder);
+ free(c->feeder, M_DEVBUF);
+ c->feeder = f;
+ return 0;
+}
+
diff --git a/sys/dev/pcm/mixer.c b/sys/dev/pcm/mixer.c
new file mode 100644
index 0000000..0a7c918
--- /dev/null
+++ b/sys/dev/pcm/mixer.c
@@ -0,0 +1,202 @@
+/*
+ * Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk>
+ * 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$
+ */
+
+#include <dev/pcm/sound.h>
+
+static u_int16_t snd_mixerdefaults[SOUND_MIXER_NRDEVICES] = {
+ [SOUND_MIXER_VOLUME] = 75,
+ [SOUND_MIXER_BASS] = 50,
+ [SOUND_MIXER_TREBLE] = 50,
+ [SOUND_MIXER_PCM] = 75,
+ [SOUND_MIXER_SPEAKER] = 75,
+ [SOUND_MIXER_LINE] = 75,
+ [SOUND_MIXER_MIC] = 0,
+ [SOUND_MIXER_CD] = 75,
+ [SOUND_MIXER_LINE1] = 75,
+ [SOUND_MIXER_VIDEO] = 75,
+ [SOUND_MIXER_RECLEV] = 0,
+};
+
+int
+mixer_init(snddev_info *d, snd_mixer *m, void *devinfo)
+{
+ if (d == NULL) return -1;
+ d->mixer = *m;
+ d->mixer.devinfo = devinfo;
+ bzero(&d->mixer.level, sizeof d->mixer.level);
+ if (d->mixer.init != NULL && d->mixer.init(&d->mixer) == 0) {
+ int i;
+ for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) {
+ u_int16_t v = snd_mixerdefaults[i];
+ mixer_set(d, i, v | (v << 8));
+ }
+ mixer_setrecsrc(d, SOUND_MASK_MIC);
+ return 0;
+ } else return -1;
+}
+
+int
+mixer_set(snddev_info *d, unsigned dev, unsigned lev)
+{
+ if (d == NULL || d->mixer.set == NULL) return -1;
+ if ((dev < SOUND_MIXER_NRDEVICES) && (d->mixer.devs & (1 << dev))) {
+ unsigned l = min((lev & 0x00ff), 100);
+ unsigned r = min(((lev & 0xff00) >> 8), 100);
+ int v = d->mixer.set(&d->mixer, dev, l, r);
+ if (v >= 0) d->mixer.level[dev] = v;
+ return 0;
+ } else return -1;
+}
+
+int
+mixer_get(snddev_info *d, int dev)
+{
+ if (d == NULL) return -1;
+ if (dev < SOUND_MIXER_NRDEVICES && (d->mixer.devs & (1 << dev)))
+ return d->mixer.level[dev];
+ else return -1;
+}
+
+int
+mixer_setrecsrc(snddev_info *d, u_int32_t src)
+{
+ if (d == NULL || d->mixer.setrecsrc == NULL) return -1;
+ src &= d->mixer.recdevs;
+ if (src == 0) src = SOUND_MASK_MIC;
+ d->mixer.recsrc = d->mixer.setrecsrc(&d->mixer, src);
+ return 0;
+}
+
+int
+mixer_getrecsrc(snddev_info *d)
+{
+ if (d == NULL) return -1;
+ return d->mixer.recsrc;
+}
+
+int
+mixer_ioctl(snddev_info *d, u_long cmd, caddr_t arg)
+{
+ int ret, *arg_i = (int *)arg;
+
+ if ((cmd & MIXER_WRITE(0)) == MIXER_WRITE(0)) {
+ int j = cmd & 0xff;
+
+ if (j == SOUND_MIXER_RECSRC) ret = mixer_setrecsrc(d, *arg_i);
+ else ret = mixer_set(d, j, *arg_i);
+ return (ret == 0)? 0 : ENXIO;
+ }
+
+ if ((cmd & MIXER_READ(0)) == MIXER_READ(0)) {
+ int v = -1, j = cmd & 0xff;
+
+ switch (j) {
+ case SOUND_MIXER_DEVMASK:
+ case SOUND_MIXER_CAPS:
+ case SOUND_MIXER_STEREODEVS:
+ v = d->mixer.devs;
+ break;
+
+ case SOUND_MIXER_RECMASK:
+ v = d->mixer.recdevs;
+ break;
+
+ case SOUND_MIXER_RECSRC:
+ v = mixer_getrecsrc(d);
+ break;
+
+ default:
+ v = mixer_get(d, j);
+ }
+ *arg_i = v;
+ return (v != -1)? 0 : ENXIO;
+ }
+ return ENXIO;
+}
+
+void
+mix_setdevs(snd_mixer *m, u_int32_t v)
+{
+ m->devs = v;
+}
+
+void
+mix_setrecdevs(snd_mixer *m, u_int32_t v)
+{
+ m->recdevs = v;
+}
+
+u_int32_t
+mix_getdevs(snd_mixer *m)
+{
+ return m->devs;
+}
+
+u_int32_t
+mix_getrecdevs(snd_mixer *m)
+{
+ return m->recdevs;
+}
+
+void *
+mix_getdevinfo(snd_mixer *m)
+{
+ return m->devinfo;
+}
+
+/*
+ * The various mixers use a variety of bitmasks etc. The Voxware
+ * driver had a very nice technique to describe a mixer and interface
+ * to it. A table defines, for each channel, which register, bits,
+ * offset, polarity to use. This procedure creates the new value
+ * using the table and the old value.
+ */
+
+void
+change_bits(mixer_tab *t, u_char *regval, int dev, int chn, int newval)
+{
+ u_char mask;
+ int shift;
+
+ DEB(printf("ch_bits dev %d ch %d val %d old 0x%02x "
+ "r %d p %d bit %d off %d\n",
+ dev, chn, newval, *regval,
+ (*t)[dev][chn].regno, (*t)[dev][chn].polarity,
+ (*t)[dev][chn].nbits, (*t)[dev][chn].bitoffs ) );
+
+ if ( (*t)[dev][chn].polarity == 1) /* reverse */
+ newval = 100 - newval ;
+
+ mask = (1 << (*t)[dev][chn].nbits) - 1;
+ newval = (int) ((newval * mask) + 50) / 100; /* Scale it */
+ shift = (*t)[dev][chn].bitoffs /*- (*t)[dev][LEFT_CHN].nbits + 1*/;
+
+ *regval &= ~(mask << shift); /* Filter out the previous value */
+ *regval |= (newval & mask) << shift; /* Set the new value */
+}
+
diff --git a/sys/dev/pcm/mixer.h b/sys/dev/pcm/mixer.h
new file mode 100644
index 0000000..82a8f26
--- /dev/null
+++ b/sys/dev/pcm/mixer.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk>
+ * 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$
+ */
+
+extern int mixer_init(snddev_info *d, snd_mixer *m, void *devinfo);
+extern int mixer_set(snddev_info *d, unsigned dev, unsigned lev);
+extern int mixer_get(snddev_info *d, int dev);
+extern int mixer_setrecsrc(snddev_info *d, u_int32_t src);
+extern int mixer_getrecsrc(snddev_info *d);
+extern int mixer_ioctl(snddev_info *d, u_long cmd, caddr_t arg);
+
+extern void change_bits(mixer_tab *t, u_char *regval, int dev, int chn, int newval);
+
+void mix_setdevs(snd_mixer *m, u_int32_t v);
+void mix_setrecdevs(snd_mixer *m, u_int32_t v);
+u_int32_t mix_getdevs(snd_mixer *m);
+u_int32_t mix_getrecdevs(snd_mixer *m);
+void *mix_getdevinfo(snd_mixer *m);
diff --git a/sys/dev/pcm/pci/aureal.c b/sys/dev/pcm/pci/aureal.c
new file mode 100644
index 0000000..49900e6
--- /dev/null
+++ b/sys/dev/pcm/pci/aureal.c
@@ -0,0 +1,693 @@
+/*
+ * Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk>
+ * 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$
+ */
+
+#include "pci.h"
+#include "pcm.h"
+
+#include <dev/pcm/sound.h>
+#include <dev/pcm/ac97.h>
+#include <dev/pcm/pci/aureal.h>
+
+#include <pci/pcireg.h>
+#include <pci/pcivar.h>
+
+#if NPCI != 0
+
+/* PCI IDs of supported chips */
+#define AU8820_PCI_ID 0x000112eb
+
+/* channel interface */
+static void *auchan_init(void *devinfo, snd_dbuf *b, pcm_channel *c, int dir);
+static int auchan_setdir(void *data, int dir);
+static int auchan_setformat(void *data, u_int32_t format);
+static int auchan_setspeed(void *data, u_int32_t speed);
+static int auchan_setblocksize(void *data, u_int32_t blocksize);
+static int auchan_trigger(void *data, int go);
+static int auchan_getptr(void *data);
+static pcmchan_caps *auchan_getcaps(void *data);
+
+static pcmchan_caps au_playcaps = {
+ 4000, 48000,
+ AFMT_STEREO | AFMT_U8 | AFMT_S16_LE,
+ AFMT_STEREO | AFMT_S16_LE
+};
+
+static pcmchan_caps au_reccaps = {
+ 4000, 48000,
+ AFMT_STEREO | AFMT_U8 | AFMT_S16_LE,
+ AFMT_STEREO | AFMT_S16_LE
+};
+
+static pcm_channel au_chantemplate = {
+ auchan_init,
+ auchan_setdir,
+ auchan_setformat,
+ auchan_setspeed,
+ auchan_setblocksize,
+ auchan_trigger,
+ auchan_getptr,
+ auchan_getcaps,
+};
+
+/* -------------------------------------------------------------------- */
+
+static u_int32_t au_rdcd(void *arg, int regno);
+static void au_wrcd(void *arg, int regno, u_int32_t data);
+
+struct au_info;
+
+struct au_chinfo {
+ struct au_info *parent;
+ pcm_channel *channel;
+ snd_dbuf *buffer;
+ int dir;
+};
+
+struct au_info {
+ int unit;
+
+ bus_space_tag_t st[3];
+ bus_space_handle_t sh[3];
+
+ bus_dma_tag_t parent_dmat;
+
+ u_int32_t x[32], y[128];
+ char z[128];
+ u_int32_t routes[4], interrupts;
+ struct au_chinfo pch;
+};
+
+static int au_init(device_t dev, struct au_info *au);
+static void au_intr(void *);
+
+/* -------------------------------------------------------------------- */
+
+static u_int32_t
+au_rd(struct au_info *au, int mapno, int regno, int size)
+{
+ switch(size) {
+ case 1:
+ return bus_space_read_1(au->st[mapno], au->sh[mapno], regno);
+ case 2:
+ return bus_space_read_2(au->st[mapno], au->sh[mapno], regno);
+ case 4:
+ return bus_space_read_4(au->st[mapno], au->sh[mapno], regno);
+ default:
+ return 0xffffffff;
+ }
+}
+
+static void
+au_wr(struct au_info *au, int mapno, int regno, u_int32_t data, int size)
+{
+ switch(size) {
+ case 1:
+ bus_space_write_1(au->st[mapno], au->sh[mapno], regno, data);
+ break;
+ case 2:
+ bus_space_write_2(au->st[mapno], au->sh[mapno], regno, data);
+ break;
+ case 4:
+ bus_space_write_4(au->st[mapno], au->sh[mapno], regno, data);
+ break;
+ }
+}
+
+static u_int32_t
+au_rdcd(void *arg, int regno)
+{
+ struct au_info *au = (struct au_info *)arg;
+ int i=0, j=0;
+
+ regno<<=16;
+ au_wr(au, 0, AU_REG_CODECIO, regno, 4);
+ while (j<50) {
+ i=au_rd(au, 0, AU_REG_CODECIO, 4);
+ if ((i & 0x00ff0000) == (regno | 0x00800000)) break;
+ DELAY(j * 200 + 2000);
+ j++;
+ }
+ if (j==50) printf("pcm%d: codec timeout reading register %x (%x)\n",
+ au->unit, (regno & AU_CDC_REGMASK)>>16, i);
+ return i & AU_CDC_DATAMASK;
+}
+
+static void
+au_wrcd(void *arg, int regno, u_int32_t data)
+{
+ struct au_info *au = (struct au_info *)arg;
+ int i, j, tries;
+ i=j=tries=0;
+ do {
+ while (j<50 && (i & AU_CDC_WROK) == 0) {
+ i=au_rd(au, 0, AU_REG_CODECST, 4);
+ DELAY(2000);
+ j++;
+ }
+ if (j==50) printf("codec timeout during write of register %x, data %x\n",
+ regno, data);
+ au_wr(au, 0, AU_REG_CODECIO, (regno<<16) | AU_CDC_REGSET | data, 4);
+/* DELAY(20000);
+ i=au_rdcd(au, regno);
+*/ tries++;
+ } while (0); /* (i != data && tries < 3); */
+ /*
+ if (tries == 3) printf("giving up writing 0x%4x to codec reg %2x\n", data, regno);
+ */
+}
+
+static void
+au_setbit(u_int32_t *p, char bit, u_int32_t value)
+{
+ p += bit >> 5;
+ bit &= 0x1f;
+ *p &= ~ (1 << bit);
+ *p |= (value << bit);
+}
+
+static void
+au_addroute(struct au_info *au, int a, int b, int route)
+{
+ int j = 0x1099c+(a<<2);
+ if (au->x[a] != a+0x67) j = AU_REG_RTBASE+(au->x[a]<<2);
+
+ au_wr(au, 0, AU_REG_RTBASE+(route<<2), 0xffffffff, 4);
+ au_wr(au, 0, j, route | (b<<7), 4);
+ au->y[route]=au->x[a];
+ au->x[a]=route;
+ au->z[route]=a & 0x000000ff;
+ au_setbit(au->routes, route, 1);
+}
+
+static void
+au_delroute(struct au_info *au, int route)
+{
+ int i;
+ int j=au->z[route];
+
+ au_setbit(au->routes, route, 0);
+ au->z[route]=0x1f;
+ i=au_rd(au, 0, AU_REG_RTBASE+(route<<2), 4);
+ au_wr(au, 0, AU_REG_RTBASE+(au->y[route]<<2), i, 4);
+ au->y[i & 0x7f]=au->y[route];
+ au_wr(au, 0, AU_REG_RTBASE+(route<<2), 0xfffffffe, 4);
+ if (au->x[j] == route) au->x[j]=au->y[route];
+ au->y[route]=0x7f;
+}
+
+static void
+au_encodec(struct au_info *au, char channel)
+{
+ au_wr(au, 0, AU_REG_CODECEN,
+ au_rd(au, 0, AU_REG_CODECEN, 4) | (1 << (channel + 8)), 4);
+}
+
+static void
+au_clrfifo(struct au_info *au, u_int32_t c)
+{
+ u_int32_t i;
+
+ for (i=0; i<32; i++) au_wr(au, 0, AU_REG_FIFOBASE+(c<<7)+(i<<2), 0, 4);
+}
+
+static void
+au_setadb(struct au_info *au, u_int32_t c, u_int32_t enable)
+{
+ int x;
+
+ x = au_rd(au, 0, AU_REG_ADB, 4);
+ x &= ~(1 << c);
+ x |= (enable << c);
+ au_wr(au, 0, AU_REG_ADB, x, 4);
+}
+
+static void
+au_prepareoutput(struct au_chinfo *ch, u_int32_t format)
+{
+ struct au_info *au = ch->parent;
+ int i, stereo = (format & AFMT_STEREO)? 1 : 0;
+ u_int32_t baseaddr = vtophys(ch->buffer->buf);
+
+ au_wr(au, 0, 0x1061c, 0, 4);
+ au_wr(au, 0, 0x10620, 0, 4);
+ au_wr(au, 0, 0x10624, 0, 4);
+ switch(format & ~AFMT_STEREO) {
+ case 1:
+ i=0xb000;
+ break;
+ case 2:
+ i=0xf000;
+ break;
+ case 8:
+ i=0x7000;
+ break;
+ case 16:
+ i=0x23000;
+ break;
+ default:
+ i=0x3000;
+ }
+ au_wr(au, 0, 0x10200, baseaddr, 4);
+ au_wr(au, 0, 0x10204, baseaddr+0x1000, 4);
+ au_wr(au, 0, 0x10208, baseaddr+0x2000, 4);
+ au_wr(au, 0, 0x1020c, baseaddr+0x3000, 4);
+
+ au_wr(au, 0, 0x10400, 0xdeffffff, 4);
+ au_wr(au, 0, 0x10404, 0xfcffffff, 4);
+
+ au_wr(au, 0, 0x10580, i, 4);
+
+ au_wr(au, 0, 0x10210, baseaddr, 4);
+ au_wr(au, 0, 0x10214, baseaddr+0x1000, 4);
+ au_wr(au, 0, 0x10218, baseaddr+0x2000, 4);
+ au_wr(au, 0, 0x1021c, baseaddr+0x3000, 4);
+
+ au_wr(au, 0, 0x10408, 0x00fff000 | 0x56000000 | 0x00000fff, 4);
+ au_wr(au, 0, 0x1040c, 0x00fff000 | 0x74000000 | 0x00000fff, 4);
+
+ au_wr(au, 0, 0x10584, i, 4);
+
+ au_wr(au, 0, 0x0f800, stereo? 0x00030032 : 0x00030030, 4);
+ au_wr(au, 0, 0x0f804, stereo? 0x00030032 : 0x00030030, 4);
+
+ au_addroute(au, 0x11, 0, 0x58);
+ au_addroute(au, 0x11, stereo? 0 : 1, 0x59);
+}
+
+/* channel interface */
+static void *
+auchan_init(void *devinfo, snd_dbuf *b, pcm_channel *c, int dir)
+{
+ struct au_info *au = devinfo;
+ struct au_chinfo *ch = (dir == PCMDIR_PLAY)? &au->pch : NULL;
+
+ ch->parent = au;
+ ch->channel = c;
+ ch->buffer = b;
+ ch->buffer->bufsize = AU_BUFFSIZE;
+ if (chn_allocbuf(ch->buffer, au->parent_dmat) == -1) return NULL;
+ return ch;
+}
+
+static int
+auchan_setdir(void *data, int dir)
+{
+ struct au_chinfo *ch = data;
+ if (dir == PCMDIR_PLAY) {
+ } else {
+ }
+ ch->dir = dir;
+ return 0;
+}
+
+static int
+auchan_setformat(void *data, u_int32_t format)
+{
+ struct au_chinfo *ch = data;
+
+ if (ch->dir == PCMDIR_PLAY) au_prepareoutput(ch, format);
+ return 0;
+}
+
+static int
+auchan_setspeed(void *data, u_int32_t speed)
+{
+ struct au_chinfo *ch = data;
+ if (ch->dir == PCMDIR_PLAY) {
+ } else {
+ }
+ return speed;
+}
+
+static int
+auchan_setblocksize(void *data, u_int32_t blocksize)
+{
+ return blocksize;
+}
+
+static int
+auchan_trigger(void *data, int go)
+{
+ struct au_chinfo *ch = data;
+ struct au_info *au = ch->parent;
+ if (ch->dir == PCMDIR_PLAY) {
+ au_setadb(au, 0x11, (go)? 1 : 0);
+ if (!go) {
+ au_wr(au, 0, 0xf800, 0, 4);
+ au_wr(au, 0, 0xf804, 0, 4);
+ au_delroute(au, 0x58);
+ au_delroute(au, 0x59);
+ }
+ } else {
+ }
+ return 0;
+}
+
+static int
+auchan_getptr(void *data)
+{
+ struct au_chinfo *ch = data;
+ struct au_info *au = ch->parent;
+ if (ch->dir == PCMDIR_PLAY) {
+ return au_rd(au, 0, AU_REG_UNK2, 4) & (AU_BUFFSIZE-1);
+ } else {
+ return 0;
+ }
+}
+
+static pcmchan_caps *
+auchan_getcaps(void *data)
+{
+ struct au_chinfo *ch = data;
+ return (ch->dir == PCMDIR_PLAY)? &au_playcaps : &au_reccaps;
+}
+
+/* The interrupt handler */
+static void
+au_intr (void *p)
+{
+ struct au_info *au = p;
+ u_int32_t intsrc, i;
+
+ au->interrupts++;
+ intsrc=au_rd(au, 0, AU_REG_IRQSRC, 4);
+ printf("pcm%d: interrupt with src %x\n", au->unit, intsrc);
+ if (intsrc & AU_IRQ_FATAL) printf("pcm%d: fatal error irq\n", au->unit);
+ if (intsrc & AU_IRQ_PARITY) printf("pcm%d: parity error irq\n", au->unit);
+ if (intsrc & AU_IRQ_UNKNOWN) {
+ (void)au_rd(au, 0, AU_REG_UNK1, 4);
+ au_wr(au, 0, AU_REG_UNK1, 0, 4);
+ au_wr(au, 0, AU_REG_UNK1, 0x10000, 4);
+ }
+ if (intsrc & AU_IRQ_PCMOUT) {
+ i=au_rd(au, 0, AU_REG_UNK2, 4) & (AU_BUFFSIZE-1);
+ chn_intr(au->pch.channel);
+ (void)au_rd(au, 0, AU_REG_UNK3, 4);
+ (void)au_rd(au, 0, AU_REG_UNK4, 4);
+ (void)au_rd(au, 0, AU_REG_UNK5, 4);
+ }
+/* don't support midi
+ if (intsrc & AU_IRQ_MIDI) {
+ i=au_rd(au, 0, 0x11004, 4);
+ j=10;
+ while (i & 0xff) {
+ if (j-- <= 0) break;
+ i=au_rd(au, 0, 0x11000, 4);
+ if ((au->midi_stat & 1) && (au->midi_out))
+ au->midi_out(au->midi_devno, i);
+ i=au_rd(au, 0, 0x11004);
+ }
+ }
+*/
+ au_wr(au, 0, AU_REG_IRQSRC, intsrc & 0x7ff, 4);
+ au_rd(au, 0, AU_REG_IRQSRC, 4);
+}
+
+
+/* -------------------------------------------------------------------- */
+
+/* Probe and attach the card */
+
+static int
+au_init(device_t dev, struct au_info *au)
+{
+ u_int32_t i, j;
+
+ au_wr(au, 0, AU_REG_IRQGLOB, 0xffffffff, 4);
+ DELAY(100000);
+
+ /* init codec */
+ /* cold reset */
+ for (i=0; i<32; i++) {
+ au_wr(au, 0, AU_REG_CODECCHN+(i<<2), 0, 4);
+ DELAY(10000);
+ }
+ if (1) {
+ au_wr(au, 0, AU_REG_CODECST, 0x8068, 4);
+ DELAY(10000);
+ au_wr(au, 0, AU_REG_CODECST, 0x00e8, 4);
+ DELAY(10000);
+ } else {
+ au_wr(au, 0, AU_REG_CODECST, 0x00a8, 4);
+ DELAY(100000);
+ au_wr(au, 0, AU_REG_CODECST, 0x80a8, 4);
+ DELAY(100000);
+ au_wr(au, 0, AU_REG_CODECST, 0x80e8, 4);
+ DELAY(100000);
+ au_wr(au, 0, AU_REG_CODECST, 0x80a8, 4);
+ DELAY(100000);
+ au_wr(au, 0, AU_REG_CODECST, 0x00a8, 4);
+ DELAY(100000);
+ au_wr(au, 0, AU_REG_CODECST, 0x00e8, 4);
+ DELAY(100000);
+ }
+
+ /* init */
+ for (i=0; i<32; i++) {
+ au_wr(au, 0, AU_REG_CODECCHN+(i<<2), 0, 4);
+ DELAY(10000);
+ }
+ au_wr(au, 0, AU_REG_CODECST, 0xe8, 4);
+ DELAY(10000);
+ au_wr(au, 0, AU_REG_CODECEN, 0, 4);
+
+ /* setup codec */
+ i=j=0;
+ while (j<100 && (i & AU_CDC_READY)==0) {
+ i=au_rd(au, 0, AU_REG_CODECST, 4);
+ DELAY(1000);
+ j++;
+ }
+ if (j==100) device_printf(dev, "codec not ready, status 0x%x\n", i);
+
+ /* init adb */
+ /*au->x5c=0;*/
+ for (i=0; i<32; i++) au->x[i]=i+0x67;
+ for (i=0; i<128; i++) au->y[i]=0x7f;
+ for (i=0; i<128; i++) au->z[i]=0x1f;
+ au_wr(au, 0, AU_REG_ADB, 0, 4);
+ for (i=0; i<124; i++) au_wr(au, 0, AU_REG_RTBASE+(i<<2), 0xffffffff, 4);
+
+ /* test */
+ i=au_rd(au, 0, 0x107c0, 4);
+ if (i!=0xdeadbeef) device_printf(dev, "dma check failed: 0x%x\n", i);
+
+ /* install mixer */
+ au_wr(au, 0, AU_REG_IRQGLOB,
+ au_rd(au, 0, AU_REG_IRQGLOB, 4) | AU_IRQ_ENABLE, 4);
+ /* braindead but it's what the oss/linux driver does
+ * for (i=0; i<0x80000000; i++) au_wr(au, 0, i<<2, 0, 4);
+ */
+ au->routes[0]=au->routes[1]=au->routes[2]=au->routes[3]=0;
+ /*au->x1e4=0;*/
+
+ /* attach channel */
+ au_addroute(au, 0x11, 0x48, 0x02);
+ au_addroute(au, 0x11, 0x49, 0x03);
+ au_encodec(au, 0);
+ au_encodec(au, 1);
+
+ for (i=0; i<48; i++) au_wr(au, 0, 0xf800+(i<<2), 0x20, 4);
+ for (i=2; i<6; i++) au_wr(au, 0, 0xf800+(i<<2), 0, 4);
+ au_wr(au, 0, 0xf8c0, 0x0843, 4);
+ for (i=0; i<4; i++) au_clrfifo(au, i);
+
+ return (0);
+}
+
+static int
+au_testirq(struct au_info *au)
+{
+ au_wr(au, 0, AU_REG_UNK1, 0x80001000, 4);
+ au_wr(au, 0, AU_REG_IRQEN, 0x00001030, 4);
+ au_wr(au, 0, AU_REG_IRQSRC, 0x000007ff, 4);
+ DELAY(1000000);
+ if (au->interrupts==0) printf("pcm%d: irq test failed\n", au->unit);
+ /* this apparently generates an irq */
+ return 0;
+}
+
+static int
+au_pci_probe(device_t dev)
+{
+ if (pci_get_devid(dev) == AU8820_PCI_ID) {
+ device_set_desc(dev, "Aureal Vortex 8820");
+ return 0;
+ }
+
+ return ENXIO;
+}
+
+static int
+au_pci_attach(device_t dev)
+{
+ snddev_info *d;
+ u_int32_t data;
+ struct au_info *au;
+ int type[10];
+ int regid[10];
+ struct resource *reg[10];
+ int i, j, mapped = 0;
+ int irqid;
+ struct resource *irq = 0;
+ void *ih = 0;
+ struct ac97_info *codec;
+ char status[SND_STATUSLEN];
+
+ d = device_get_softc(dev);
+ if ((au = malloc(sizeof(*au), M_DEVBUF, M_NOWAIT)) == NULL) {
+ device_printf(dev, "cannot allocate softc\n");
+ return ENXIO;
+ }
+
+ bzero(au, sizeof(*au));
+ au->unit = device_get_unit(dev);
+
+ data = pci_read_config(dev, PCIR_COMMAND, 2);
+ data |= (PCIM_CMD_PORTEN|PCIM_CMD_MEMEN|PCIM_CMD_BUSMASTEREN);
+ pci_write_config(dev, PCIR_COMMAND, data, 2);
+ data = pci_read_config(dev, PCIR_COMMAND, 2);
+
+ j=0;
+ /* XXX dfr: is this strictly necessary? */
+ for (i=0; i<PCI_MAXMAPS_0; i++) {
+#if 0
+ /* Slapped wrist: config_id and map are private structures */
+ if (bootverbose) {
+ printf("pcm%d: map %d - allocating ", unit, i+1);
+ printf("0x%x bytes of ", 1<<config_id->map[i].ln2size);
+ printf("%s space ", (config_id->map[i].type & PCI_MAPPORT)?
+ "io" : "memory");
+ printf("at 0x%x...", config_id->map[i].base);
+ }
+#endif
+ regid[j] = PCIR_MAPS + i*4;
+ type[j] = SYS_RES_MEMORY;
+ reg[j] = bus_alloc_resource(dev, type[j], &regid[j],
+ 0, ~0, 1, RF_ACTIVE);
+ if (!reg[j]) {
+ type[j] = SYS_RES_IOPORT;
+ reg[j] = bus_alloc_resource(dev, type[j], &regid[j],
+ 0, ~0, 1, RF_ACTIVE);
+ }
+ if (reg[j]) {
+ au->st[i] = rman_get_bustag(reg[j]);
+ au->sh[i] = rman_get_bushandle(reg[j]);
+ mapped++;
+ }
+#if 0
+ if (bootverbose) printf("%s\n", mapped? "ok" : "failed");
+#endif
+ if (mapped) j++;
+ if (j == 10) {
+ /* XXX */
+ device_printf(dev, "too many resources");
+ goto bad;
+ }
+ }
+
+#if 0
+ if (j < config_id->nummaps) {
+ printf("pcm%d: unable to map a required resource\n", unit);
+ free(au, M_DEVBUF);
+ return;
+ }
+#endif
+
+ au_wr(au, 0, AU_REG_IRQEN, 0, 4);
+
+ irqid = 0;
+ irq = bus_alloc_resource(dev, SYS_RES_IRQ, &irqid,
+ 0, ~0, 1, RF_ACTIVE);
+ if (!irq
+ || bus_setup_intr(dev, irq, INTR_TYPE_TTY, au_intr, au, &ih)) {
+ device_printf(dev, "unable to map interrupt\n");
+ goto bad;
+ }
+
+ if (au_testirq(au)) device_printf(dev, "irq test failed\n");
+
+ if (au_init(dev, au) == -1) {
+ device_printf(dev, "unable to initialize the card\n");
+ goto bad;
+ }
+
+ codec = ac97_create(au, au_rdcd, au_wrcd);
+ if (codec == NULL) goto bad;
+ mixer_init(d, &ac97_mixer, codec);
+
+ if (bus_dma_tag_create(/*parent*/NULL, /*alignment*/2, /*boundary*/0,
+ /*lowaddr*/BUS_SPACE_MAXADDR_32BIT,
+ /*highaddr*/BUS_SPACE_MAXADDR,
+ /*filter*/NULL, /*filterarg*/NULL,
+ /*maxsize*/AU_BUFFSIZE, /*nsegments*/1, /*maxsegz*/0x3ffff,
+ /*flags*/0, &au->parent_dmat) != 0) {
+ device_printf(dev, "unable to create dma tag\n");
+ goto bad;
+ }
+
+ snprintf(status, SND_STATUSLEN, "at %s 0x%lx irq %ld",
+ (type[0] == SYS_RES_IOPORT)? "io" : "memory",
+ rman_get_start(reg[0]), rman_get_start(irq));
+
+ if (pcm_register(dev, au, 1, 1)) goto bad;
+ /* pcm_addchan(dev, PCMDIR_REC, &au_chantemplate, au); */
+ pcm_addchan(dev, PCMDIR_PLAY, &au_chantemplate, au);
+ pcm_setstatus(dev, status);
+
+ return 0;
+
+ bad:
+ if (au) free(au, M_DEVBUF);
+ for (i = 0; i < j; i++)
+ bus_release_resource(dev, type[i], regid[i], reg[i]);
+ if (ih) bus_teardown_intr(dev, irq, ih);
+ if (irq) bus_release_resource(dev, SYS_RES_IRQ, irqid, irq);
+ return ENXIO;
+}
+
+static device_method_t au_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, au_pci_probe),
+ DEVMETHOD(device_attach, au_pci_attach),
+
+ { 0, 0 }
+};
+
+static driver_t au_driver = {
+ "pcm",
+ au_methods,
+ sizeof(snddev_info),
+};
+
+static devclass_t pcm_devclass;
+
+DRIVER_MODULE(au, pci, au_driver, pcm_devclass, 0, 0);
+
+#endif /* NPCI != 0 */
+
diff --git a/sys/dev/pcm/pci/aureal.h b/sys/dev/pcm/pci/aureal.h
new file mode 100644
index 0000000..c5bcb29
--- /dev/null
+++ b/sys/dev/pcm/pci/aureal.h
@@ -0,0 +1,99 @@
+/*
+ * Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk>
+ * 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$
+ */
+
+#ifndef _AU8820_REG_H
+#define _AU8820_REG_H
+
+#define AU_BUFFSIZE 0x4000
+
+#define AU_REG_FIFOBASE 0x0e000
+
+#define AU_REG_UNK2 0x105c0
+#define AU_REG_UNK3 0x10600
+#define AU_REG_UNK4 0x10604
+#define AU_REG_UNK5 0x10608
+
+#define AU_REG_RTBASE 0x10800
+
+#define AU_REG_ADB 0x10a00
+
+#define AU_REG_CODECCHN 0x11880
+
+#define AU_REG_CODECST 0x11984
+#define AU_CDC_RUN 0x00000040
+#define AU_CDC_WROK 0x00000100
+#define AU_CDC_RESET 0x00008000
+
+#define AU_REG_CODECIO 0x11988
+#define AU_CDC_DATAMASK 0x0000ffff
+#define AU_CDC_REGMASK 0x007f0000
+#define AU_CDC_REGSET 0x00800000
+#define AU_CDC_READY 0x04000000
+
+#define AU_REG_CODECEN 0x11990
+#define AU_CDC_CHAN1EN 0x00000100
+#define AU_CDC_CHAN2EN 0x00000200
+
+#define AU_REG_UNK1 0x1199c
+
+#define AU_REG_IRQSRC 0x12800
+#define AU_IRQ_FATAL 0x0001
+#define AU_IRQ_PARITY 0x0002
+#define AU_IRQ_PCMOUT 0x0020
+#define AU_IRQ_UNKNOWN 0x1000
+#define AU_IRQ_MIDI 0x2000
+#define AU_REG_IRQEN 0x12804
+
+#define AU_REG_IRQGLOB 0x1280c
+#define AU_IRQ_ENABLE 0x4000
+
+#define AC97_MUTE 0x8000
+#define AC97_REG_RESET 0x00
+#define AC97_MIX_MASTER 0x02
+#define AC97_MIX_PHONES 0x04
+#define AC97_MIX_MONO 0x06
+#define AC97_MIX_TONE 0x08
+#define AC97_MIX_BEEP 0x0a
+#define AC97_MIX_PHONE 0x0c
+#define AC97_MIX_MIC 0x0e
+#define AC97_MIX_LINE 0x10
+#define AC97_MIX_CD 0x12
+#define AC97_MIX_VIDEO 0x14
+#define AC97_MIX_AUX 0x16
+#define AC97_MIX_PCM 0x18
+#define AC97_REG_RECSEL 0x1a
+#define AC97_MIX_RGAIN 0x1c
+#define AC97_MIX_MGAIN 0x1e
+#define AC97_REG_GEN 0x20
+#define AC97_REG_3D 0x22
+#define AC97_REG_POWER 0x26
+#define AC97_REG_ID1 0x7c
+#define AC97_REG_ID2 0x7e
+
+
+#endif
diff --git a/sys/dev/pcm/pci/es1370.c b/sys/dev/pcm/pci/es1370.c
new file mode 100644
index 0000000..f0f1413
--- /dev/null
+++ b/sys/dev/pcm/pci/es1370.c
@@ -0,0 +1,543 @@
+/*
+ * Support the ENSONIQ AudioPCI board based on the ES1370 and Codec
+ * AK4531.
+ *
+ * Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk>
+ * Copyright (c) 1998 by Joachim Kuebart. 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.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ * software must display the following acknowledgement:
+ * This product includes software developed by Joachim Kuebart.
+ *
+ * 4. The name of the author may not be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``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 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: es1370.c,v 1.4 1999/05/09 17:06:45 peter Exp $
+ */
+
+#include "pci.h"
+#include "pcm.h"
+
+#include <dev/pcm/sound.h>
+#include <dev/pcm/pci/es1370.h>
+
+#include <pci/pcireg.h>
+#include <pci/pcivar.h>
+
+#if NPCI != 0
+
+#define MEM_MAP_REG 0x14
+
+/* PCI IDs of supported chips */
+#define ES1370_PCI_ID 0x50001274
+
+/* device private data */
+struct es_info;
+
+struct es_chinfo {
+ struct es_info *parent;
+ pcm_channel *channel;
+ snd_dbuf *buffer;
+ int dir;
+ u_int32_t fmt;
+};
+
+struct es_info {
+ bus_space_tag_t st;
+ bus_space_handle_t sh;
+ bus_dma_tag_t parent_dmat;
+
+ /* Contents of board's registers */
+ u_long ctrl;
+ u_long sctrl;
+ struct es_chinfo pch, rch;
+};
+
+/* -------------------------------------------------------------------- */
+
+/* prototypes */
+static int es_init(struct es_info *);
+static void es_intr(void *);
+static int write_codec(struct es_info *, u_char, u_char);
+
+/* channel interface */
+static void *eschan_init(void *devinfo, snd_dbuf *b, pcm_channel *c, int dir);
+static int eschan_setdir(void *data, int dir);
+static int eschan_setformat(void *data, u_int32_t format);
+static int eschan_setspeed(void *data, u_int32_t speed);
+static int eschan_setblocksize(void *data, u_int32_t blocksize);
+static int eschan_trigger(void *data, int go);
+static int eschan_getptr(void *data);
+static pcmchan_caps *eschan_getcaps(void *data);
+
+static pcmchan_caps es_playcaps = {
+ 4000, 48000,
+ AFMT_STEREO | AFMT_U8 | AFMT_S16_LE,
+ AFMT_STEREO | AFMT_S16_LE
+};
+
+static pcmchan_caps es_reccaps = {
+ 4000, 48000,
+ AFMT_STEREO | AFMT_U8 | AFMT_S16_LE,
+ AFMT_STEREO | AFMT_S16_LE
+};
+
+static pcm_channel es_chantemplate = {
+ eschan_init,
+ eschan_setdir,
+ eschan_setformat,
+ eschan_setspeed,
+ eschan_setblocksize,
+ eschan_trigger,
+ eschan_getptr,
+ eschan_getcaps,
+};
+
+/* -------------------------------------------------------------------- */
+
+/* The mixer interface */
+
+static int es_mixinit(snd_mixer *m);
+static int es_mixset(snd_mixer *m, unsigned dev, unsigned left, unsigned right);
+static int es_mixsetrecsrc(snd_mixer *m, u_int32_t src);
+
+static snd_mixer es_mixer = {
+ "Ensoniq AudioPCI 1370 mixer",
+ es_mixinit,
+ es_mixset,
+ es_mixsetrecsrc,
+};
+
+static const struct {
+ unsigned volidx:4;
+ unsigned left:4;
+ unsigned right:4;
+ unsigned stereo:1;
+ unsigned recmask:13;
+ unsigned avail:1;
+} mixtable[SOUND_MIXER_NRDEVICES] = {
+ [SOUND_MIXER_VOLUME] = { 0, 0x0, 0x1, 1, 0x0000, 1 },
+ [SOUND_MIXER_PCM] = { 1, 0x2, 0x3, 1, 0x0400, 1 },
+ [SOUND_MIXER_SYNTH] = { 2, 0x4, 0x5, 1, 0x0060, 1 },
+ [SOUND_MIXER_CD] = { 3, 0x6, 0x7, 1, 0x0006, 1 },
+ [SOUND_MIXER_LINE] = { 4, 0x8, 0x9, 1, 0x0018, 1 },
+ [SOUND_MIXER_LINE1] = { 5, 0xa, 0xb, 1, 0x1800, 1 },
+ [SOUND_MIXER_LINE2] = { 6, 0xc, 0x0, 0, 0x0100, 1 },
+ [SOUND_MIXER_LINE3] = { 7, 0xd, 0x0, 0, 0x0200, 1 },
+ [SOUND_MIXER_MIC] = { 8, 0xe, 0x0, 0, 0x0001, 1 },
+ [SOUND_MIXER_OGAIN] = { 9, 0xf, 0x0, 0, 0x0000, 1 } };
+
+static int
+es_mixinit(snd_mixer *m)
+{
+ int i;
+ u_int32_t v;
+
+ v = 0;
+ for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
+ if (mixtable[i].avail) v |= (1 << i);
+ mix_setdevs(m, v);
+ v = 0;
+ for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
+ if (mixtable[i].recmask) v |= (1 << i);
+ mix_setrecdevs(m, v);
+ return 0;
+}
+
+static int
+es_mixset(snd_mixer *m, unsigned dev, unsigned left, unsigned right)
+{
+ int l, r, rl, rr;
+
+ if (!mixtable[dev].avail) return -1;
+ l = left;
+ r = mixtable[dev].stereo? right : l;
+ if (mixtable[dev].left == 0xf) {
+ rl = (l < 2)? 0x80 : 7 - (l - 2) / 14;
+ } else {
+ rl = (l < 10)? 0x80 : 15 - (l - 10) / 6;
+ }
+ if (mixtable[dev].stereo) {
+ rr = (r < 10)? 0x80 : 15 - (r - 10) / 6;
+ write_codec(mix_getdevinfo(m), mixtable[dev].right, rr);
+ }
+ write_codec(mix_getdevinfo(m), mixtable[dev].left, rl);
+ return l | (r << 8);
+}
+
+static int
+es_mixsetrecsrc(snd_mixer *m, u_int32_t src)
+{
+ int i, j = 0;
+
+ if (src == 0) src = 1 << SOUND_MIXER_MIC;
+ src &= mix_getrecdevs(m);
+ for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
+ if ((src & (1 << i)) != 0) j |= mixtable[i].recmask;
+
+ write_codec(mix_getdevinfo(m), CODEC_LIMIX1, j & 0x55);
+ write_codec(mix_getdevinfo(m), CODEC_RIMIX1, j & 0xaa);
+ write_codec(mix_getdevinfo(m), CODEC_LIMIX2, (j >> 8) & 0x17);
+ write_codec(mix_getdevinfo(m), CODEC_RIMIX2, (j >> 8) & 0x0f);
+ write_codec(mix_getdevinfo(m), CODEC_OMIX1, 0x7f);
+ write_codec(mix_getdevinfo(m), CODEC_OMIX2, 0x3f);
+ return src;
+}
+
+static int
+write_codec(struct es_info *es, u_char i, u_char data)
+{
+ int wait = 100; /* 100 msec timeout */
+
+ do {
+ if ((bus_space_read_4(es->st, es->sh, ES1370_REG_STATUS) &
+ STAT_CSTAT) == 0) {
+ bus_space_write_2(es->st, es->sh, ES1370_REG_CODEC,
+ ((u_short)i << CODEC_INDEX_SHIFT) | data);
+ return 0;
+ }
+ DELAY(1000);
+ } while (--wait);
+ printf("pcm: write_codec timed out\n");
+ return -1;
+}
+
+/* -------------------------------------------------------------------- */
+
+/* channel interface */
+static void *
+eschan_init(void *devinfo, snd_dbuf *b, pcm_channel *c, int dir)
+{
+ struct es_info *es = devinfo;
+ struct es_chinfo *ch = (dir == PCMDIR_PLAY)? &es->pch : &es->rch;
+
+ ch->parent = es;
+ ch->channel = c;
+ ch->buffer = b;
+ ch->buffer->bufsize = ES_BUFFSIZE;
+ if (chn_allocbuf(ch->buffer, es->parent_dmat) == -1) return NULL;
+ return ch;
+}
+
+static int
+eschan_setdir(void *data, int dir)
+{
+ struct es_chinfo *ch = data;
+ struct es_info *es = ch->parent;
+
+ if (dir == PCMDIR_PLAY) {
+ bus_space_write_1(es->st, es->sh, ES1370_REG_MEMPAGE,
+ ES1370_REG_DAC2_FRAMEADR >> 8);
+ bus_space_write_4(es->st, es->sh, ES1370_REG_DAC2_FRAMEADR & 0xff,
+ vtophys(ch->buffer->buf));
+ bus_space_write_4(es->st, es->sh, ES1370_REG_DAC2_FRAMECNT & 0xff,
+ (ch->buffer->bufsize >> 2) - 1);
+ } else {
+ bus_space_write_1(es->st, es->sh, ES1370_REG_MEMPAGE,
+ ES1370_REG_ADC_FRAMEADR >> 8);
+ bus_space_write_4(es->st, es->sh, ES1370_REG_ADC_FRAMEADR & 0xff,
+ vtophys(ch->buffer->buf));
+ bus_space_write_4(es->st, es->sh, ES1370_REG_ADC_FRAMECNT & 0xff,
+ (ch->buffer->bufsize >> 2) - 1);
+ }
+ ch->dir = dir;
+ return 0;
+}
+
+static int
+eschan_setformat(void *data, u_int32_t format)
+{
+ struct es_chinfo *ch = data;
+ struct es_info *es = ch->parent;
+
+ if (ch->dir == PCMDIR_PLAY) {
+ es->sctrl &= ~SCTRL_P2FMT;
+ if (format & AFMT_S16_LE) es->sctrl |= SCTRL_P2SEB;
+ if (format & AFMT_STEREO) es->sctrl |= SCTRL_P2SMB;
+ } else {
+ es->sctrl &= ~SCTRL_R1FMT;
+ if (format & AFMT_S16_LE) es->sctrl |= SCTRL_R1SEB;
+ if (format & AFMT_STEREO) es->sctrl |= SCTRL_R1SMB;
+ }
+ bus_space_write_4(es->st, es->sh, ES1370_REG_SERIAL_CONTROL, es->sctrl);
+ ch->fmt = format;
+ return 0;
+}
+
+static int
+eschan_setspeed(void *data, u_int32_t speed)
+{
+ struct es_chinfo *ch = data;
+ struct es_info *es = ch->parent;
+
+ es->ctrl &= ~CTRL_PCLKDIV;
+ es->ctrl |= DAC2_SRTODIV(speed) << CTRL_SH_PCLKDIV;
+ bus_space_write_4(es->st, es->sh, ES1370_REG_CONTROL, es->ctrl);
+ /* rec/play speeds locked together - should indicate in flags */
+#if 0
+ if (ch->direction == PCMDIR_PLAY) d->rec[0].speed = speed;
+ else d->play[0].speed = speed;
+#endif
+ return speed; /* XXX calc real speed */
+}
+
+static int
+eschan_setblocksize(void *data, u_int32_t blocksize)
+{
+ return blocksize;
+}
+
+static int
+eschan_trigger(void *data, int go)
+{
+ struct es_chinfo *ch = data;
+ struct es_info *es = ch->parent;
+ unsigned cnt = ch->buffer->dl / ch->buffer->sample_size - 1;
+
+ if (ch->dir == PCMDIR_PLAY) {
+ if (go == PCMTRIG_START) {
+ int b = (ch->fmt & AFMT_S16_LE)? 2 : 1;
+ es->ctrl |= CTRL_DAC2_EN;
+ es->sctrl &= ~(SCTRL_P2ENDINC | SCTRL_P2STINC |
+ SCTRL_P2LOOPSEL | SCTRL_P2PAUSE |
+ SCTRL_P2DACSEN);
+ es->sctrl |= SCTRL_P2INTEN | (b << SCTRL_SH_P2ENDINC);
+ bus_space_write_4(es->st, es->sh,
+ ES1370_REG_DAC2_SCOUNT, cnt);
+ } else es->ctrl &= ~CTRL_DAC2_EN;
+ } else {
+ if (go == PCMTRIG_START) {
+ es->ctrl |= CTRL_ADC_EN;
+ es->sctrl &= ~SCTRL_R1LOOPSEL;
+ es->sctrl |= SCTRL_R1INTEN;
+ bus_space_write_4(es->st, es->sh,
+ ES1370_REG_ADC_SCOUNT, cnt);
+ } else es->ctrl &= ~CTRL_ADC_EN;
+ }
+ bus_space_write_4(es->st, es->sh, ES1370_REG_SERIAL_CONTROL, es->sctrl);
+ bus_space_write_4(es->st, es->sh, ES1370_REG_CONTROL, es->ctrl);
+ return 0;
+}
+
+static int
+eschan_getptr(void *data)
+{
+ struct es_chinfo *ch = data;
+ struct es_info *es = ch->parent;
+ if (ch->dir == PCMDIR_PLAY) {
+ bus_space_write_4(es->st, es->sh, ES1370_REG_MEMPAGE,
+ ES1370_REG_DAC2_FRAMECNT >> 8);
+ return (bus_space_read_4(es->st, es->sh,
+ ES1370_REG_DAC2_FRAMECNT & 0xff) >> 14) & 0x3fffc;
+ } else {
+ bus_space_write_4(es->st, es->sh, ES1370_REG_MEMPAGE,
+ ES1370_REG_ADC_FRAMECNT >> 8);
+ return (bus_space_read_4(es->st, es->sh,
+ ES1370_REG_ADC_FRAMECNT & 0xff) >> 14) & 0x3fffc;
+ }
+}
+
+static pcmchan_caps *
+eschan_getcaps(void *data)
+{
+ struct es_chinfo *ch = data;
+ return (ch->dir == PCMDIR_PLAY)? &es_playcaps : &es_reccaps;
+}
+
+/* The interrupt handler */
+static void
+es_intr (void *p)
+{
+ struct es_info *es = p;
+ unsigned intsrc, sctrl;
+
+ intsrc = bus_space_read_4(es->st, es->sh, ES1370_REG_STATUS);
+ if ((intsrc & STAT_INTR) == 0) return;
+
+ sctrl = es->sctrl;
+ if (intsrc & STAT_ADC) sctrl &= ~SCTRL_R1INTEN;
+ if (intsrc & STAT_DAC1) sctrl &= ~SCTRL_P1INTEN;
+ if (intsrc & STAT_DAC2) sctrl &= ~SCTRL_P2INTEN;
+
+ bus_space_write_4(es->st, es->sh, ES1370_REG_SERIAL_CONTROL, sctrl);
+ bus_space_write_4(es->st, es->sh, ES1370_REG_SERIAL_CONTROL, es->sctrl);
+
+ if (intsrc & STAT_DAC2) chn_intr(es->pch.channel);
+ if (intsrc & STAT_ADC) chn_intr(es->rch.channel);
+}
+
+/* -------------------------------------------------------------------- */
+
+/*
+ * Probe and attach the card
+ */
+
+static int
+es_init(struct es_info *es)
+{
+ es->ctrl = CTRL_CDC_EN | CTRL_SERR_DIS |
+ (DAC2_SRTODIV(DSP_DEFAULT_SPEED) << CTRL_SH_PCLKDIV);
+ bus_space_write_4(es->st, es->sh, ES1370_REG_CONTROL, es->ctrl);
+
+ es->sctrl = 0;
+ bus_space_write_4(es->st, es->sh, ES1370_REG_SERIAL_CONTROL, es->sctrl);
+
+ write_codec(es, CODEC_RES_PD, 3);/* No RST, PD */
+ write_codec(es, CODEC_CSEL, 0); /* CODEC ADC and CODEC DAC use
+ * {LR,B}CLK2 and run off the LRCLK2
+ * PLL; program DAC_SYNC=0! */
+ write_codec(es, CODEC_ADSEL, 0);/* Recording source is mixer */
+ write_codec(es, CODEC_MGAIN, 0);/* MIC amp is 0db */
+
+ return 0;
+}
+
+static int
+es_pci_probe(device_t dev)
+{
+ if (pci_get_devid(dev) == ES1370_PCI_ID) {
+ device_set_desc(dev, "AudioPCI ES1370");
+ return 0;
+ }
+ return ENXIO;
+}
+
+static int
+es_pci_attach(device_t dev)
+{
+ snddev_info *d;
+ u_int32_t data;
+ struct es_info *es = 0;
+ int type = 0;
+ int regid;
+ struct resource *reg = 0;
+ int mapped;
+ int irqid;
+ struct resource *irq = 0;
+ void *ih = 0;
+ char status[SND_STATUSLEN];
+
+ d = device_get_softc(dev);
+ if ((es = malloc(sizeof *es, M_DEVBUF, M_NOWAIT)) == NULL) {
+ device_printf(dev, "cannot allocate softc\n");
+ return ENXIO;
+ }
+ bzero(es, sizeof *es);
+
+ mapped = 0;
+ data = pci_read_config(dev, PCIR_COMMAND, 2);
+ if (mapped == 0 && (data & PCIM_CMD_MEMEN)) {
+ regid = MEM_MAP_REG;
+ type = SYS_RES_MEMORY;
+ reg = bus_alloc_resource(dev, type, &regid,
+ 0, ~0, 1, RF_ACTIVE);
+ if (reg) {
+ es->st = rman_get_bustag(reg);
+ es->sh = rman_get_bushandle(reg);
+ mapped++;
+ }
+ }
+ if (mapped == 0 && (data & PCIM_CMD_PORTEN)) {
+ regid = PCI_MAP_REG_START;
+ type = SYS_RES_IOPORT;
+ reg = bus_alloc_resource(dev, type, &regid,
+ 0, ~0, 1, RF_ACTIVE);
+ if (reg) {
+ es->st = rman_get_bustag(reg);
+ es->sh = rman_get_bushandle(reg);
+ mapped++;
+ }
+ }
+ if (mapped == 0) {
+ device_printf(dev, "unable to map register space\n");
+ goto bad;
+ }
+
+ if (es_init(es) == -1) {
+ device_printf(dev, "unable to initialize the card\n");
+ goto bad;
+ }
+ mixer_init(d, &es_mixer, es);
+
+ irqid = 0;
+ irq = bus_alloc_resource(dev, SYS_RES_IRQ, &irqid,
+ 0, ~0, 1, RF_ACTIVE);
+ if (!irq
+ || bus_setup_intr(dev, irq, INTR_TYPE_TTY, es_intr, es, &ih)) {
+ device_printf(dev, "unable to map interrupt\n");
+ goto bad;
+ }
+
+ if (bus_dma_tag_create(/*parent*/NULL, /*alignment*/2, /*boundary*/0,
+ /*lowaddr*/BUS_SPACE_MAXADDR_32BIT,
+ /*highaddr*/BUS_SPACE_MAXADDR,
+ /*filter*/NULL, /*filterarg*/NULL,
+ /*maxsize*/ES_BUFFSIZE, /*nsegments*/1, /*maxsegz*/0x3ffff,
+ /*flags*/0, &es->parent_dmat) != 0) {
+ device_printf(dev, "unable to create dma tag\n");
+ goto bad;
+ }
+
+ snprintf(status, SND_STATUSLEN, "at %s 0x%lx irq %ld",
+ (type == SYS_RES_IOPORT)? "io" : "memory",
+ rman_get_start(reg), rman_get_start(irq));
+
+ if (pcm_register(dev, es, 1, 1)) goto bad;
+ pcm_addchan(dev, PCMDIR_REC, &es_chantemplate, es);
+ pcm_addchan(dev, PCMDIR_PLAY, &es_chantemplate, es);
+ pcm_setstatus(dev, status);
+
+ return 0;
+
+ bad:
+ if (es) free(es, M_DEVBUF);
+ if (reg) bus_release_resource(dev, type, regid, reg);
+ if (ih) bus_teardown_intr(dev, irq, ih);
+ if (irq) bus_release_resource(dev, SYS_RES_IRQ, irqid, irq);
+ return ENXIO;
+}
+
+static device_method_t es_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, es_pci_probe),
+ DEVMETHOD(device_attach, es_pci_attach),
+
+ { 0, 0 }
+};
+
+static driver_t es_driver = {
+ "pcm",
+ es_methods,
+ sizeof(snddev_info),
+};
+
+static devclass_t pcm_devclass;
+
+DRIVER_MODULE(es, pci, es_driver, pcm_devclass, 0, 0);
+
+#endif /* NPCI != 0 */
diff --git a/sys/dev/pcm/pci/es1370.h b/sys/dev/pcm/pci/es1370.h
new file mode 100644
index 0000000..6c981e3
--- /dev/null
+++ b/sys/dev/pcm/pci/es1370.h
@@ -0,0 +1,134 @@
+/*
+ * This supports the ENSONIQ AudioPCI board based on the ES1370.
+ *
+ * Copyright (c) 1998 Joachim Kuebart <joki@kuebart.stuttgart.netsurf.de>
+ * 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 immediately at the beginning of the file, without modification,
+ * 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.
+ * 3. Absolutely no warranty of function or purpose is made by the author
+ * Joachim Kuebart.
+ * 4. Modifications may be freely made to this file if the above conditions
+ * are met.
+ *
+ * $Id: es1370_reg.h,v 1.1 1998/12/31 08:14:27 luigi Exp $
+ */
+
+#ifndef _ES1370_REG_H
+#define _ES1370_REG_H
+
+#define ES1370_REG_CONTROL 0x00
+#define ES1370_REG_STATUS 0x04
+#define ES1370_REG_UART_DATA 0x08
+#define ES1370_REG_UART_STATUS 0x09
+#define ES1370_REG_UART_CONTROL 0x09
+#define ES1370_REG_UART_TEST 0x0a
+#define ES1370_REG_MEMPAGE 0x0c
+#define ES1370_REG_CODEC 0x10
+#define CODEC_INDEX_SHIFT 8
+#define ES1370_REG_SERIAL_CONTROL 0x20
+#define ES1370_REG_DAC1_SCOUNT 0x24
+#define ES1370_REG_DAC2_SCOUNT 0x28
+#define ES1370_REG_ADC_SCOUNT 0x2c
+
+#define ES1370_REG_DAC1_FRAMEADR 0xc30
+#define ES1370_REG_DAC1_FRAMECNT 0xc34
+#define ES1370_REG_DAC2_FRAMEADR 0xc38
+#define ES1370_REG_DAC2_FRAMECNT 0xc3c
+#define ES1370_REG_ADC_FRAMEADR 0xd30
+#define ES1370_REG_ADC_FRAMECNT 0xd34
+
+#define DAC2_SRTODIV(x) (((1411200 + (x) / 2) / (x) - 2) & 0x1fff)
+#define DAC2_DIVTOSR(x) (1411200 / ((x) + 2))
+
+#define CTRL_ADC_STOP 0x80000000 /* 1 = ADC stopped */
+#define CTRL_XCTL1 0x40000000 /* SERR pin if enabled */
+#define CTRL_OPEN 0x20000000 /* no function, can be read and
+ * written */
+#define CTRL_PCLKDIV 0x1fff0000 /* ADC/DAC2 clock divider */
+#define CTRL_SH_PCLKDIV 16
+#define CTRL_MSFMTSEL 0x00008000 /* MPEG serial data fmt: 0 = Sony, 1
+ * = I2S */
+#define CTRL_M_SBB 0x00004000 /* DAC2 clock: 0 = PCLKDIV, 1 = MPEG */
+#define CTRL_WTSRSEL 0x00003000 /* DAC1 clock freq: 0=5512, 1=11025,
+ * 2=22050, 3=44100 */
+#define CTRL_SH_WTSRSEL 12
+#define CTRL_DAC_SYNC 0x00000800 /* 1 = DAC2 runs off DAC1 clock */
+#define CTRL_CCB_INTRM 0x00000400 /* 1 = CCB "voice" ints enabled */
+#define CTRL_M_CB 0x00000200 /* recording source: 0 = ADC, 1 =
+ * MPEG */
+#define CTRL_XCTL0 0x00000100 /* 0 = Line in, 1 = Line out */
+#define CTRL_BREQ 0x00000080 /* 1 = test mode (internal mem test) */
+#define CTRL_DAC1_EN 0x00000040 /* enable DAC1 */
+#define CTRL_DAC2_EN 0x00000020 /* enable DAC2 */
+#define CTRL_ADC_EN 0x00000010 /* enable ADC */
+#define CTRL_UART_EN 0x00000008 /* enable MIDI uart */
+#define CTRL_JYSTK_EN 0x00000004 /* enable Joystick port (presumably
+ * at address 0x200) */
+#define CTRL_CDC_EN 0x00000002 /* enable serial (CODEC) interface */
+#define CTRL_SERR_DIS 0x00000001 /* 1 = disable PCI SERR signal */
+
+#define SCTRL_P2ENDINC 0x00380000 /* */
+#define SCTRL_SH_P2ENDINC 19
+#define SCTRL_P2STINC 0x00070000 /* */
+#define SCTRL_SH_P2STINC 16
+#define SCTRL_R1LOOPSEL 0x00008000 /* 0 = loop mode */
+#define SCTRL_P2LOOPSEL 0x00004000 /* 0 = loop mode */
+#define SCTRL_P1LOOPSEL 0x00002000 /* 0 = loop mode */
+#define SCTRL_P2PAUSE 0x00001000 /* 1 = pause mode */
+#define SCTRL_P1PAUSE 0x00000800 /* 1 = pause mode */
+#define SCTRL_R1INTEN 0x00000400 /* enable interrupt */
+#define SCTRL_P2INTEN 0x00000200 /* enable interrupt */
+#define SCTRL_P1INTEN 0x00000100 /* enable interrupt */
+#define SCTRL_P1SCTRLD 0x00000080 /* reload sample count register for
+ * DAC1 */
+#define SCTRL_P2DACSEN 0x00000040 /* 1 = DAC2 play back last sample
+ * when disabled */
+#define SCTRL_R1SEB 0x00000020 /* 1 = 16bit */
+#define SCTRL_R1SMB 0x00000010 /* 1 = stereo */
+#define SCTRL_R1FMT 0x00000030 /* format mask */
+#define SCTRL_SH_R1FMT 4
+#define SCTRL_P2SEB 0x00000008 /* 1 = 16bit */
+#define SCTRL_P2SMB 0x00000004 /* 1 = stereo */
+#define SCTRL_P2FMT 0x0000000c /* format mask */
+#define SCTRL_SH_P2FMT 2
+#define SCTRL_P1SEB 0x00000002 /* 1 = 16bit */
+#define SCTRL_P1SMB 0x00000001 /* 1 = stereo */
+#define SCTRL_P1FMT 0x00000003 /* format mask */
+#define SCTRL_SH_P1FMT 0
+
+#define STAT_INTR 0x80000000 /* wired or of all interrupt bits */
+#define STAT_CSTAT 0x00000400 /* 1 = codec busy or codec write in
+ * progress */
+#define STAT_CBUSY 0x00000200 /* 1 = codec busy */
+#define STAT_CWRIP 0x00000100 /* 1 = codec write in progress */
+#define STAT_VC 0x00000060 /* CCB int source, 0=DAC1, 1=DAC2,
+ * 2=ADC, 3=undef */
+#define STAT_SH_VC 5
+#define STAT_MCCB 0x00000010 /* CCB int pending */
+#define STAT_UART 0x00000008 /* UART int pending */
+#define STAT_DAC1 0x00000004 /* DAC1 int pending */
+#define STAT_DAC2 0x00000002 /* DAC2 int pending */
+#define STAT_ADC 0x00000001 /* ADC int pending */
+
+#define CODEC_OMIX1 0x10
+#define CODEC_OMIX2 0x11
+#define CODEC_LIMIX1 0x12
+#define CODEC_RIMIX1 0x13
+#define CODEC_LIMIX2 0x14
+#define CODEC_RIMIX2 0x15
+#define CODEC_RES_PD 0x16
+#define CODEC_CSEL 0x17
+#define CODEC_ADSEL 0x18
+#define CODEC_MGAIN 0x19
+
+#define ES_BUFFSIZE 0x20000 /* We're PCI! Use a large buffer */
+
+#endif
diff --git a/sys/dev/pcm/pci/es137x.c b/sys/dev/pcm/pci/es137x.c
new file mode 100644
index 0000000..f0f1413
--- /dev/null
+++ b/sys/dev/pcm/pci/es137x.c
@@ -0,0 +1,543 @@
+/*
+ * Support the ENSONIQ AudioPCI board based on the ES1370 and Codec
+ * AK4531.
+ *
+ * Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk>
+ * Copyright (c) 1998 by Joachim Kuebart. 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.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ * software must display the following acknowledgement:
+ * This product includes software developed by Joachim Kuebart.
+ *
+ * 4. The name of the author may not be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``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 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: es1370.c,v 1.4 1999/05/09 17:06:45 peter Exp $
+ */
+
+#include "pci.h"
+#include "pcm.h"
+
+#include <dev/pcm/sound.h>
+#include <dev/pcm/pci/es1370.h>
+
+#include <pci/pcireg.h>
+#include <pci/pcivar.h>
+
+#if NPCI != 0
+
+#define MEM_MAP_REG 0x14
+
+/* PCI IDs of supported chips */
+#define ES1370_PCI_ID 0x50001274
+
+/* device private data */
+struct es_info;
+
+struct es_chinfo {
+ struct es_info *parent;
+ pcm_channel *channel;
+ snd_dbuf *buffer;
+ int dir;
+ u_int32_t fmt;
+};
+
+struct es_info {
+ bus_space_tag_t st;
+ bus_space_handle_t sh;
+ bus_dma_tag_t parent_dmat;
+
+ /* Contents of board's registers */
+ u_long ctrl;
+ u_long sctrl;
+ struct es_chinfo pch, rch;
+};
+
+/* -------------------------------------------------------------------- */
+
+/* prototypes */
+static int es_init(struct es_info *);
+static void es_intr(void *);
+static int write_codec(struct es_info *, u_char, u_char);
+
+/* channel interface */
+static void *eschan_init(void *devinfo, snd_dbuf *b, pcm_channel *c, int dir);
+static int eschan_setdir(void *data, int dir);
+static int eschan_setformat(void *data, u_int32_t format);
+static int eschan_setspeed(void *data, u_int32_t speed);
+static int eschan_setblocksize(void *data, u_int32_t blocksize);
+static int eschan_trigger(void *data, int go);
+static int eschan_getptr(void *data);
+static pcmchan_caps *eschan_getcaps(void *data);
+
+static pcmchan_caps es_playcaps = {
+ 4000, 48000,
+ AFMT_STEREO | AFMT_U8 | AFMT_S16_LE,
+ AFMT_STEREO | AFMT_S16_LE
+};
+
+static pcmchan_caps es_reccaps = {
+ 4000, 48000,
+ AFMT_STEREO | AFMT_U8 | AFMT_S16_LE,
+ AFMT_STEREO | AFMT_S16_LE
+};
+
+static pcm_channel es_chantemplate = {
+ eschan_init,
+ eschan_setdir,
+ eschan_setformat,
+ eschan_setspeed,
+ eschan_setblocksize,
+ eschan_trigger,
+ eschan_getptr,
+ eschan_getcaps,
+};
+
+/* -------------------------------------------------------------------- */
+
+/* The mixer interface */
+
+static int es_mixinit(snd_mixer *m);
+static int es_mixset(snd_mixer *m, unsigned dev, unsigned left, unsigned right);
+static int es_mixsetrecsrc(snd_mixer *m, u_int32_t src);
+
+static snd_mixer es_mixer = {
+ "Ensoniq AudioPCI 1370 mixer",
+ es_mixinit,
+ es_mixset,
+ es_mixsetrecsrc,
+};
+
+static const struct {
+ unsigned volidx:4;
+ unsigned left:4;
+ unsigned right:4;
+ unsigned stereo:1;
+ unsigned recmask:13;
+ unsigned avail:1;
+} mixtable[SOUND_MIXER_NRDEVICES] = {
+ [SOUND_MIXER_VOLUME] = { 0, 0x0, 0x1, 1, 0x0000, 1 },
+ [SOUND_MIXER_PCM] = { 1, 0x2, 0x3, 1, 0x0400, 1 },
+ [SOUND_MIXER_SYNTH] = { 2, 0x4, 0x5, 1, 0x0060, 1 },
+ [SOUND_MIXER_CD] = { 3, 0x6, 0x7, 1, 0x0006, 1 },
+ [SOUND_MIXER_LINE] = { 4, 0x8, 0x9, 1, 0x0018, 1 },
+ [SOUND_MIXER_LINE1] = { 5, 0xa, 0xb, 1, 0x1800, 1 },
+ [SOUND_MIXER_LINE2] = { 6, 0xc, 0x0, 0, 0x0100, 1 },
+ [SOUND_MIXER_LINE3] = { 7, 0xd, 0x0, 0, 0x0200, 1 },
+ [SOUND_MIXER_MIC] = { 8, 0xe, 0x0, 0, 0x0001, 1 },
+ [SOUND_MIXER_OGAIN] = { 9, 0xf, 0x0, 0, 0x0000, 1 } };
+
+static int
+es_mixinit(snd_mixer *m)
+{
+ int i;
+ u_int32_t v;
+
+ v = 0;
+ for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
+ if (mixtable[i].avail) v |= (1 << i);
+ mix_setdevs(m, v);
+ v = 0;
+ for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
+ if (mixtable[i].recmask) v |= (1 << i);
+ mix_setrecdevs(m, v);
+ return 0;
+}
+
+static int
+es_mixset(snd_mixer *m, unsigned dev, unsigned left, unsigned right)
+{
+ int l, r, rl, rr;
+
+ if (!mixtable[dev].avail) return -1;
+ l = left;
+ r = mixtable[dev].stereo? right : l;
+ if (mixtable[dev].left == 0xf) {
+ rl = (l < 2)? 0x80 : 7 - (l - 2) / 14;
+ } else {
+ rl = (l < 10)? 0x80 : 15 - (l - 10) / 6;
+ }
+ if (mixtable[dev].stereo) {
+ rr = (r < 10)? 0x80 : 15 - (r - 10) / 6;
+ write_codec(mix_getdevinfo(m), mixtable[dev].right, rr);
+ }
+ write_codec(mix_getdevinfo(m), mixtable[dev].left, rl);
+ return l | (r << 8);
+}
+
+static int
+es_mixsetrecsrc(snd_mixer *m, u_int32_t src)
+{
+ int i, j = 0;
+
+ if (src == 0) src = 1 << SOUND_MIXER_MIC;
+ src &= mix_getrecdevs(m);
+ for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
+ if ((src & (1 << i)) != 0) j |= mixtable[i].recmask;
+
+ write_codec(mix_getdevinfo(m), CODEC_LIMIX1, j & 0x55);
+ write_codec(mix_getdevinfo(m), CODEC_RIMIX1, j & 0xaa);
+ write_codec(mix_getdevinfo(m), CODEC_LIMIX2, (j >> 8) & 0x17);
+ write_codec(mix_getdevinfo(m), CODEC_RIMIX2, (j >> 8) & 0x0f);
+ write_codec(mix_getdevinfo(m), CODEC_OMIX1, 0x7f);
+ write_codec(mix_getdevinfo(m), CODEC_OMIX2, 0x3f);
+ return src;
+}
+
+static int
+write_codec(struct es_info *es, u_char i, u_char data)
+{
+ int wait = 100; /* 100 msec timeout */
+
+ do {
+ if ((bus_space_read_4(es->st, es->sh, ES1370_REG_STATUS) &
+ STAT_CSTAT) == 0) {
+ bus_space_write_2(es->st, es->sh, ES1370_REG_CODEC,
+ ((u_short)i << CODEC_INDEX_SHIFT) | data);
+ return 0;
+ }
+ DELAY(1000);
+ } while (--wait);
+ printf("pcm: write_codec timed out\n");
+ return -1;
+}
+
+/* -------------------------------------------------------------------- */
+
+/* channel interface */
+static void *
+eschan_init(void *devinfo, snd_dbuf *b, pcm_channel *c, int dir)
+{
+ struct es_info *es = devinfo;
+ struct es_chinfo *ch = (dir == PCMDIR_PLAY)? &es->pch : &es->rch;
+
+ ch->parent = es;
+ ch->channel = c;
+ ch->buffer = b;
+ ch->buffer->bufsize = ES_BUFFSIZE;
+ if (chn_allocbuf(ch->buffer, es->parent_dmat) == -1) return NULL;
+ return ch;
+}
+
+static int
+eschan_setdir(void *data, int dir)
+{
+ struct es_chinfo *ch = data;
+ struct es_info *es = ch->parent;
+
+ if (dir == PCMDIR_PLAY) {
+ bus_space_write_1(es->st, es->sh, ES1370_REG_MEMPAGE,
+ ES1370_REG_DAC2_FRAMEADR >> 8);
+ bus_space_write_4(es->st, es->sh, ES1370_REG_DAC2_FRAMEADR & 0xff,
+ vtophys(ch->buffer->buf));
+ bus_space_write_4(es->st, es->sh, ES1370_REG_DAC2_FRAMECNT & 0xff,
+ (ch->buffer->bufsize >> 2) - 1);
+ } else {
+ bus_space_write_1(es->st, es->sh, ES1370_REG_MEMPAGE,
+ ES1370_REG_ADC_FRAMEADR >> 8);
+ bus_space_write_4(es->st, es->sh, ES1370_REG_ADC_FRAMEADR & 0xff,
+ vtophys(ch->buffer->buf));
+ bus_space_write_4(es->st, es->sh, ES1370_REG_ADC_FRAMECNT & 0xff,
+ (ch->buffer->bufsize >> 2) - 1);
+ }
+ ch->dir = dir;
+ return 0;
+}
+
+static int
+eschan_setformat(void *data, u_int32_t format)
+{
+ struct es_chinfo *ch = data;
+ struct es_info *es = ch->parent;
+
+ if (ch->dir == PCMDIR_PLAY) {
+ es->sctrl &= ~SCTRL_P2FMT;
+ if (format & AFMT_S16_LE) es->sctrl |= SCTRL_P2SEB;
+ if (format & AFMT_STEREO) es->sctrl |= SCTRL_P2SMB;
+ } else {
+ es->sctrl &= ~SCTRL_R1FMT;
+ if (format & AFMT_S16_LE) es->sctrl |= SCTRL_R1SEB;
+ if (format & AFMT_STEREO) es->sctrl |= SCTRL_R1SMB;
+ }
+ bus_space_write_4(es->st, es->sh, ES1370_REG_SERIAL_CONTROL, es->sctrl);
+ ch->fmt = format;
+ return 0;
+}
+
+static int
+eschan_setspeed(void *data, u_int32_t speed)
+{
+ struct es_chinfo *ch = data;
+ struct es_info *es = ch->parent;
+
+ es->ctrl &= ~CTRL_PCLKDIV;
+ es->ctrl |= DAC2_SRTODIV(speed) << CTRL_SH_PCLKDIV;
+ bus_space_write_4(es->st, es->sh, ES1370_REG_CONTROL, es->ctrl);
+ /* rec/play speeds locked together - should indicate in flags */
+#if 0
+ if (ch->direction == PCMDIR_PLAY) d->rec[0].speed = speed;
+ else d->play[0].speed = speed;
+#endif
+ return speed; /* XXX calc real speed */
+}
+
+static int
+eschan_setblocksize(void *data, u_int32_t blocksize)
+{
+ return blocksize;
+}
+
+static int
+eschan_trigger(void *data, int go)
+{
+ struct es_chinfo *ch = data;
+ struct es_info *es = ch->parent;
+ unsigned cnt = ch->buffer->dl / ch->buffer->sample_size - 1;
+
+ if (ch->dir == PCMDIR_PLAY) {
+ if (go == PCMTRIG_START) {
+ int b = (ch->fmt & AFMT_S16_LE)? 2 : 1;
+ es->ctrl |= CTRL_DAC2_EN;
+ es->sctrl &= ~(SCTRL_P2ENDINC | SCTRL_P2STINC |
+ SCTRL_P2LOOPSEL | SCTRL_P2PAUSE |
+ SCTRL_P2DACSEN);
+ es->sctrl |= SCTRL_P2INTEN | (b << SCTRL_SH_P2ENDINC);
+ bus_space_write_4(es->st, es->sh,
+ ES1370_REG_DAC2_SCOUNT, cnt);
+ } else es->ctrl &= ~CTRL_DAC2_EN;
+ } else {
+ if (go == PCMTRIG_START) {
+ es->ctrl |= CTRL_ADC_EN;
+ es->sctrl &= ~SCTRL_R1LOOPSEL;
+ es->sctrl |= SCTRL_R1INTEN;
+ bus_space_write_4(es->st, es->sh,
+ ES1370_REG_ADC_SCOUNT, cnt);
+ } else es->ctrl &= ~CTRL_ADC_EN;
+ }
+ bus_space_write_4(es->st, es->sh, ES1370_REG_SERIAL_CONTROL, es->sctrl);
+ bus_space_write_4(es->st, es->sh, ES1370_REG_CONTROL, es->ctrl);
+ return 0;
+}
+
+static int
+eschan_getptr(void *data)
+{
+ struct es_chinfo *ch = data;
+ struct es_info *es = ch->parent;
+ if (ch->dir == PCMDIR_PLAY) {
+ bus_space_write_4(es->st, es->sh, ES1370_REG_MEMPAGE,
+ ES1370_REG_DAC2_FRAMECNT >> 8);
+ return (bus_space_read_4(es->st, es->sh,
+ ES1370_REG_DAC2_FRAMECNT & 0xff) >> 14) & 0x3fffc;
+ } else {
+ bus_space_write_4(es->st, es->sh, ES1370_REG_MEMPAGE,
+ ES1370_REG_ADC_FRAMECNT >> 8);
+ return (bus_space_read_4(es->st, es->sh,
+ ES1370_REG_ADC_FRAMECNT & 0xff) >> 14) & 0x3fffc;
+ }
+}
+
+static pcmchan_caps *
+eschan_getcaps(void *data)
+{
+ struct es_chinfo *ch = data;
+ return (ch->dir == PCMDIR_PLAY)? &es_playcaps : &es_reccaps;
+}
+
+/* The interrupt handler */
+static void
+es_intr (void *p)
+{
+ struct es_info *es = p;
+ unsigned intsrc, sctrl;
+
+ intsrc = bus_space_read_4(es->st, es->sh, ES1370_REG_STATUS);
+ if ((intsrc & STAT_INTR) == 0) return;
+
+ sctrl = es->sctrl;
+ if (intsrc & STAT_ADC) sctrl &= ~SCTRL_R1INTEN;
+ if (intsrc & STAT_DAC1) sctrl &= ~SCTRL_P1INTEN;
+ if (intsrc & STAT_DAC2) sctrl &= ~SCTRL_P2INTEN;
+
+ bus_space_write_4(es->st, es->sh, ES1370_REG_SERIAL_CONTROL, sctrl);
+ bus_space_write_4(es->st, es->sh, ES1370_REG_SERIAL_CONTROL, es->sctrl);
+
+ if (intsrc & STAT_DAC2) chn_intr(es->pch.channel);
+ if (intsrc & STAT_ADC) chn_intr(es->rch.channel);
+}
+
+/* -------------------------------------------------------------------- */
+
+/*
+ * Probe and attach the card
+ */
+
+static int
+es_init(struct es_info *es)
+{
+ es->ctrl = CTRL_CDC_EN | CTRL_SERR_DIS |
+ (DAC2_SRTODIV(DSP_DEFAULT_SPEED) << CTRL_SH_PCLKDIV);
+ bus_space_write_4(es->st, es->sh, ES1370_REG_CONTROL, es->ctrl);
+
+ es->sctrl = 0;
+ bus_space_write_4(es->st, es->sh, ES1370_REG_SERIAL_CONTROL, es->sctrl);
+
+ write_codec(es, CODEC_RES_PD, 3);/* No RST, PD */
+ write_codec(es, CODEC_CSEL, 0); /* CODEC ADC and CODEC DAC use
+ * {LR,B}CLK2 and run off the LRCLK2
+ * PLL; program DAC_SYNC=0! */
+ write_codec(es, CODEC_ADSEL, 0);/* Recording source is mixer */
+ write_codec(es, CODEC_MGAIN, 0);/* MIC amp is 0db */
+
+ return 0;
+}
+
+static int
+es_pci_probe(device_t dev)
+{
+ if (pci_get_devid(dev) == ES1370_PCI_ID) {
+ device_set_desc(dev, "AudioPCI ES1370");
+ return 0;
+ }
+ return ENXIO;
+}
+
+static int
+es_pci_attach(device_t dev)
+{
+ snddev_info *d;
+ u_int32_t data;
+ struct es_info *es = 0;
+ int type = 0;
+ int regid;
+ struct resource *reg = 0;
+ int mapped;
+ int irqid;
+ struct resource *irq = 0;
+ void *ih = 0;
+ char status[SND_STATUSLEN];
+
+ d = device_get_softc(dev);
+ if ((es = malloc(sizeof *es, M_DEVBUF, M_NOWAIT)) == NULL) {
+ device_printf(dev, "cannot allocate softc\n");
+ return ENXIO;
+ }
+ bzero(es, sizeof *es);
+
+ mapped = 0;
+ data = pci_read_config(dev, PCIR_COMMAND, 2);
+ if (mapped == 0 && (data & PCIM_CMD_MEMEN)) {
+ regid = MEM_MAP_REG;
+ type = SYS_RES_MEMORY;
+ reg = bus_alloc_resource(dev, type, &regid,
+ 0, ~0, 1, RF_ACTIVE);
+ if (reg) {
+ es->st = rman_get_bustag(reg);
+ es->sh = rman_get_bushandle(reg);
+ mapped++;
+ }
+ }
+ if (mapped == 0 && (data & PCIM_CMD_PORTEN)) {
+ regid = PCI_MAP_REG_START;
+ type = SYS_RES_IOPORT;
+ reg = bus_alloc_resource(dev, type, &regid,
+ 0, ~0, 1, RF_ACTIVE);
+ if (reg) {
+ es->st = rman_get_bustag(reg);
+ es->sh = rman_get_bushandle(reg);
+ mapped++;
+ }
+ }
+ if (mapped == 0) {
+ device_printf(dev, "unable to map register space\n");
+ goto bad;
+ }
+
+ if (es_init(es) == -1) {
+ device_printf(dev, "unable to initialize the card\n");
+ goto bad;
+ }
+ mixer_init(d, &es_mixer, es);
+
+ irqid = 0;
+ irq = bus_alloc_resource(dev, SYS_RES_IRQ, &irqid,
+ 0, ~0, 1, RF_ACTIVE);
+ if (!irq
+ || bus_setup_intr(dev, irq, INTR_TYPE_TTY, es_intr, es, &ih)) {
+ device_printf(dev, "unable to map interrupt\n");
+ goto bad;
+ }
+
+ if (bus_dma_tag_create(/*parent*/NULL, /*alignment*/2, /*boundary*/0,
+ /*lowaddr*/BUS_SPACE_MAXADDR_32BIT,
+ /*highaddr*/BUS_SPACE_MAXADDR,
+ /*filter*/NULL, /*filterarg*/NULL,
+ /*maxsize*/ES_BUFFSIZE, /*nsegments*/1, /*maxsegz*/0x3ffff,
+ /*flags*/0, &es->parent_dmat) != 0) {
+ device_printf(dev, "unable to create dma tag\n");
+ goto bad;
+ }
+
+ snprintf(status, SND_STATUSLEN, "at %s 0x%lx irq %ld",
+ (type == SYS_RES_IOPORT)? "io" : "memory",
+ rman_get_start(reg), rman_get_start(irq));
+
+ if (pcm_register(dev, es, 1, 1)) goto bad;
+ pcm_addchan(dev, PCMDIR_REC, &es_chantemplate, es);
+ pcm_addchan(dev, PCMDIR_PLAY, &es_chantemplate, es);
+ pcm_setstatus(dev, status);
+
+ return 0;
+
+ bad:
+ if (es) free(es, M_DEVBUF);
+ if (reg) bus_release_resource(dev, type, regid, reg);
+ if (ih) bus_teardown_intr(dev, irq, ih);
+ if (irq) bus_release_resource(dev, SYS_RES_IRQ, irqid, irq);
+ return ENXIO;
+}
+
+static device_method_t es_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, es_pci_probe),
+ DEVMETHOD(device_attach, es_pci_attach),
+
+ { 0, 0 }
+};
+
+static driver_t es_driver = {
+ "pcm",
+ es_methods,
+ sizeof(snddev_info),
+};
+
+static devclass_t pcm_devclass;
+
+DRIVER_MODULE(es, pci, es_driver, pcm_devclass, 0, 0);
+
+#endif /* NPCI != 0 */
diff --git a/sys/dev/pcm/pci/es137x.h b/sys/dev/pcm/pci/es137x.h
new file mode 100644
index 0000000..6c981e3
--- /dev/null
+++ b/sys/dev/pcm/pci/es137x.h
@@ -0,0 +1,134 @@
+/*
+ * This supports the ENSONIQ AudioPCI board based on the ES1370.
+ *
+ * Copyright (c) 1998 Joachim Kuebart <joki@kuebart.stuttgart.netsurf.de>
+ * 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 immediately at the beginning of the file, without modification,
+ * 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.
+ * 3. Absolutely no warranty of function or purpose is made by the author
+ * Joachim Kuebart.
+ * 4. Modifications may be freely made to this file if the above conditions
+ * are met.
+ *
+ * $Id: es1370_reg.h,v 1.1 1998/12/31 08:14:27 luigi Exp $
+ */
+
+#ifndef _ES1370_REG_H
+#define _ES1370_REG_H
+
+#define ES1370_REG_CONTROL 0x00
+#define ES1370_REG_STATUS 0x04
+#define ES1370_REG_UART_DATA 0x08
+#define ES1370_REG_UART_STATUS 0x09
+#define ES1370_REG_UART_CONTROL 0x09
+#define ES1370_REG_UART_TEST 0x0a
+#define ES1370_REG_MEMPAGE 0x0c
+#define ES1370_REG_CODEC 0x10
+#define CODEC_INDEX_SHIFT 8
+#define ES1370_REG_SERIAL_CONTROL 0x20
+#define ES1370_REG_DAC1_SCOUNT 0x24
+#define ES1370_REG_DAC2_SCOUNT 0x28
+#define ES1370_REG_ADC_SCOUNT 0x2c
+
+#define ES1370_REG_DAC1_FRAMEADR 0xc30
+#define ES1370_REG_DAC1_FRAMECNT 0xc34
+#define ES1370_REG_DAC2_FRAMEADR 0xc38
+#define ES1370_REG_DAC2_FRAMECNT 0xc3c
+#define ES1370_REG_ADC_FRAMEADR 0xd30
+#define ES1370_REG_ADC_FRAMECNT 0xd34
+
+#define DAC2_SRTODIV(x) (((1411200 + (x) / 2) / (x) - 2) & 0x1fff)
+#define DAC2_DIVTOSR(x) (1411200 / ((x) + 2))
+
+#define CTRL_ADC_STOP 0x80000000 /* 1 = ADC stopped */
+#define CTRL_XCTL1 0x40000000 /* SERR pin if enabled */
+#define CTRL_OPEN 0x20000000 /* no function, can be read and
+ * written */
+#define CTRL_PCLKDIV 0x1fff0000 /* ADC/DAC2 clock divider */
+#define CTRL_SH_PCLKDIV 16
+#define CTRL_MSFMTSEL 0x00008000 /* MPEG serial data fmt: 0 = Sony, 1
+ * = I2S */
+#define CTRL_M_SBB 0x00004000 /* DAC2 clock: 0 = PCLKDIV, 1 = MPEG */
+#define CTRL_WTSRSEL 0x00003000 /* DAC1 clock freq: 0=5512, 1=11025,
+ * 2=22050, 3=44100 */
+#define CTRL_SH_WTSRSEL 12
+#define CTRL_DAC_SYNC 0x00000800 /* 1 = DAC2 runs off DAC1 clock */
+#define CTRL_CCB_INTRM 0x00000400 /* 1 = CCB "voice" ints enabled */
+#define CTRL_M_CB 0x00000200 /* recording source: 0 = ADC, 1 =
+ * MPEG */
+#define CTRL_XCTL0 0x00000100 /* 0 = Line in, 1 = Line out */
+#define CTRL_BREQ 0x00000080 /* 1 = test mode (internal mem test) */
+#define CTRL_DAC1_EN 0x00000040 /* enable DAC1 */
+#define CTRL_DAC2_EN 0x00000020 /* enable DAC2 */
+#define CTRL_ADC_EN 0x00000010 /* enable ADC */
+#define CTRL_UART_EN 0x00000008 /* enable MIDI uart */
+#define CTRL_JYSTK_EN 0x00000004 /* enable Joystick port (presumably
+ * at address 0x200) */
+#define CTRL_CDC_EN 0x00000002 /* enable serial (CODEC) interface */
+#define CTRL_SERR_DIS 0x00000001 /* 1 = disable PCI SERR signal */
+
+#define SCTRL_P2ENDINC 0x00380000 /* */
+#define SCTRL_SH_P2ENDINC 19
+#define SCTRL_P2STINC 0x00070000 /* */
+#define SCTRL_SH_P2STINC 16
+#define SCTRL_R1LOOPSEL 0x00008000 /* 0 = loop mode */
+#define SCTRL_P2LOOPSEL 0x00004000 /* 0 = loop mode */
+#define SCTRL_P1LOOPSEL 0x00002000 /* 0 = loop mode */
+#define SCTRL_P2PAUSE 0x00001000 /* 1 = pause mode */
+#define SCTRL_P1PAUSE 0x00000800 /* 1 = pause mode */
+#define SCTRL_R1INTEN 0x00000400 /* enable interrupt */
+#define SCTRL_P2INTEN 0x00000200 /* enable interrupt */
+#define SCTRL_P1INTEN 0x00000100 /* enable interrupt */
+#define SCTRL_P1SCTRLD 0x00000080 /* reload sample count register for
+ * DAC1 */
+#define SCTRL_P2DACSEN 0x00000040 /* 1 = DAC2 play back last sample
+ * when disabled */
+#define SCTRL_R1SEB 0x00000020 /* 1 = 16bit */
+#define SCTRL_R1SMB 0x00000010 /* 1 = stereo */
+#define SCTRL_R1FMT 0x00000030 /* format mask */
+#define SCTRL_SH_R1FMT 4
+#define SCTRL_P2SEB 0x00000008 /* 1 = 16bit */
+#define SCTRL_P2SMB 0x00000004 /* 1 = stereo */
+#define SCTRL_P2FMT 0x0000000c /* format mask */
+#define SCTRL_SH_P2FMT 2
+#define SCTRL_P1SEB 0x00000002 /* 1 = 16bit */
+#define SCTRL_P1SMB 0x00000001 /* 1 = stereo */
+#define SCTRL_P1FMT 0x00000003 /* format mask */
+#define SCTRL_SH_P1FMT 0
+
+#define STAT_INTR 0x80000000 /* wired or of all interrupt bits */
+#define STAT_CSTAT 0x00000400 /* 1 = codec busy or codec write in
+ * progress */
+#define STAT_CBUSY 0x00000200 /* 1 = codec busy */
+#define STAT_CWRIP 0x00000100 /* 1 = codec write in progress */
+#define STAT_VC 0x00000060 /* CCB int source, 0=DAC1, 1=DAC2,
+ * 2=ADC, 3=undef */
+#define STAT_SH_VC 5
+#define STAT_MCCB 0x00000010 /* CCB int pending */
+#define STAT_UART 0x00000008 /* UART int pending */
+#define STAT_DAC1 0x00000004 /* DAC1 int pending */
+#define STAT_DAC2 0x00000002 /* DAC2 int pending */
+#define STAT_ADC 0x00000001 /* ADC int pending */
+
+#define CODEC_OMIX1 0x10
+#define CODEC_OMIX2 0x11
+#define CODEC_LIMIX1 0x12
+#define CODEC_RIMIX1 0x13
+#define CODEC_LIMIX2 0x14
+#define CODEC_RIMIX2 0x15
+#define CODEC_RES_PD 0x16
+#define CODEC_CSEL 0x17
+#define CODEC_ADSEL 0x18
+#define CODEC_MGAIN 0x19
+
+#define ES_BUFFSIZE 0x20000 /* We're PCI! Use a large buffer */
+
+#endif
diff --git a/sys/dev/pcm/pci/t4dwave.c b/sys/dev/pcm/pci/t4dwave.c
new file mode 100644
index 0000000..5404fe2
--- /dev/null
+++ b/sys/dev/pcm/pci/t4dwave.c
@@ -0,0 +1,688 @@
+/*
+ * Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk>
+ * 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, WHETHERIN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THEPOSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id$
+ */
+
+#include "pci.h"
+#include "pcm.h"
+
+#include <dev/pcm/sound.h>
+#include <dev/pcm/ac97.h>
+#include <dev/pcm/pci/t4dwave.h>
+
+#include <pci/pcireg.h>
+#include <pci/pcivar.h>
+
+#if NPCI != 0
+
+/* -------------------------------------------------------------------- */
+
+struct tr_info;
+
+/* channel registers */
+struct tr_chinfo {
+ u_int32_t cso, alpha, fms, fmc, ec;
+ u_int32_t lba;
+ u_int32_t eso, delta;
+ u_int32_t rvol, cvol;
+ u_int32_t gvsel, pan, vol, ctrl;
+ int index;
+ snd_dbuf *buffer;
+ pcm_channel *channel;
+ struct tr_info *parent;
+};
+
+/* device private data */
+struct tr_info {
+ u_int32_t type;
+
+ bus_space_tag_t st;
+ bus_space_handle_t sh;
+ bus_dma_tag_t parent_dmat;
+
+ struct resource *reg, *irq;
+ int regtype, regid, irqid;
+ void *ih;
+
+ u_int32_t playchns;
+ struct tr_chinfo chinfo[TR_MAXPLAYCH];
+ struct tr_chinfo recchinfo;
+};
+
+/* -------------------------------------------------------------------- */
+
+/*
+ * prototypes
+ */
+
+/* channel interface */
+static void *trchan_init(void *devinfo, snd_dbuf *b, pcm_channel *c, int dir);
+static int trchan_setdir(void *data, int dir);
+static int trchan_setformat(void *data, u_int32_t format);
+static int trchan_setspeed(void *data, u_int32_t speed);
+static int trchan_setblocksize(void *data, u_int32_t blocksize);
+static int trchan_trigger(void *data, int go);
+static int trchan_getptr(void *data);
+static pcmchan_caps *trchan_getcaps(void *data);
+
+/* talk to the codec - called from ac97.c */
+static u_int32_t tr_rdcd(void *, int);
+static void tr_wrcd(void *, int, u_int32_t);
+
+/* stuff */
+static int tr_init(struct tr_info *);
+static void tr_intr(void *);
+
+/* talk to the card */
+static u_int32_t tr_rd(struct tr_info *, int, int);
+static void tr_wr(struct tr_info *, int, u_int32_t, int);
+
+/* manipulate playback channels */
+static void tr_clrint(struct tr_info *, char);
+static void tr_enaint(struct tr_info *, char, int);
+static u_int32_t tr_testint(struct tr_info *, char);
+static void tr_rdch(struct tr_info *, char, struct tr_chinfo *);
+static void tr_wrch(struct tr_info *, char, struct tr_chinfo *);
+static void tr_selch(struct tr_info *, char);
+static void tr_startch(struct tr_info *, char);
+static void tr_stopch(struct tr_info *, char);
+
+/* -------------------------------------------------------------------- */
+
+static pcmchan_caps tr_reccaps = {
+ 4000, 48000,
+ AFMT_STEREO | AFMT_U8 | AFMT_S8 | AFMT_S16_LE | AFMT_U16_LE,
+ AFMT_STEREO | AFMT_S16_LE
+};
+
+static pcmchan_caps tr_playcaps = {
+ 4000, 48000,
+ AFMT_STEREO | AFMT_U8 | AFMT_S8 | AFMT_S16_LE | AFMT_U16_LE,
+ AFMT_U16_LE
+};
+
+static pcm_channel tr_chantemplate = {
+ trchan_init,
+ trchan_setdir,
+ trchan_setformat,
+ trchan_setspeed,
+ trchan_setblocksize,
+ trchan_trigger,
+ trchan_getptr,
+ trchan_getcaps,
+};
+
+/* -------------------------------------------------------------------- */
+
+static u_int32_t
+tr_fmttobits(u_int32_t fmt)
+{
+ u_int32_t bits = 0;
+ bits |= (fmt & AFMT_STEREO)? 0x4 : 0;
+ bits |= (fmt & (AFMT_S8 | AFMT_S16_LE))? 0x2 : 0;
+ bits |= (fmt & (AFMT_S16_LE | AFMT_U16_LE))? 0x8 : 0;
+ return bits;
+}
+
+/* Hardware */
+
+static u_int32_t
+tr_rd(struct tr_info *tr, int regno, int size)
+{
+ switch(size) {
+ case 1:
+ return bus_space_read_1(tr->st, tr->sh, regno);
+ case 2:
+ return bus_space_read_2(tr->st, tr->sh, regno);
+ case 4:
+ return bus_space_read_4(tr->st, tr->sh, regno);
+ default:
+ return 0xffffffff;
+ }
+}
+
+static void
+tr_wr(struct tr_info *tr, int regno, u_int32_t data, int size)
+{
+ switch(size) {
+ case 1:
+ bus_space_write_1(tr->st, tr->sh, regno, data);
+ break;
+ case 2:
+ bus_space_write_2(tr->st, tr->sh, regno, data);
+ break;
+ case 4:
+ bus_space_write_4(tr->st, tr->sh, regno, data);
+ break;
+ }
+}
+
+/* ac97 codec */
+
+static u_int32_t
+tr_rdcd(void *devinfo, int regno)
+{
+ struct tr_info *tr = (struct tr_info *)devinfo;
+ int i, j, treg, trw;
+
+ switch (tr->type) {
+ case TDX_PCI_ID:
+ treg=TDX_REG_CODECRD;
+ trw=TDX_CDC_RWSTAT;
+ break;
+ case TNX_PCI_ID:
+ treg=(regno & 0x100)? TNX_REG_CODEC2RD : TNX_REG_CODEC1RD;
+ trw=TNX_CDC_RWSTAT;
+ break;
+ default:
+ printf("!!! tr_rdcd defaulted !!!\n");
+ return 0xffffffff;
+ }
+
+ regno &= 0x7f;
+ tr_wr(tr, treg, regno | trw, 4);
+ j=trw;
+ for (i=TR_TIMEOUT_CDC; (i > 0) && (j & trw); i--) j=tr_rd(tr, treg, 4);
+ if (i == 0) printf("codec timeout during read of register %x\n", regno);
+ return (j >> TR_CDC_DATA) & 0xffff;
+}
+
+static void
+tr_wrcd(void *devinfo, int regno, u_int32_t data)
+{
+ struct tr_info *tr = (struct tr_info *)devinfo;
+ int i, j, treg, trw;
+
+ switch (tr->type) {
+ case TDX_PCI_ID:
+ treg=TDX_REG_CODECWR;
+ trw=TDX_CDC_RWSTAT;
+ break;
+ case TNX_PCI_ID:
+ treg=TNX_REG_CODECWR;
+ trw=TNX_CDC_RWSTAT | ((regno & 0x100)? TNX_CDC_SEC : 0);
+ break;
+ default:
+ printf("!!! tr_wrcd defaulted !!!");
+ return;
+ }
+
+ regno &= 0x7f;
+#if 0
+ printf("tr_wrcd: reg %x was %x", regno, tr_rdcd(devinfo, regno));
+#endif
+ j=trw;
+ for (i=TR_TIMEOUT_CDC; (i>0) && (j & trw); i--) j=tr_rd(tr, treg, 4);
+ tr_wr(tr, treg, (data << TR_CDC_DATA) | regno | trw, 4);
+#if 0
+ printf(" - wrote %x, now %x\n", data, tr_rdcd(devinfo, regno));
+#endif
+ if (i==0) printf("codec timeout writing %x, data %x\n", regno, data);
+}
+
+/* playback channel interrupts */
+
+static u_int32_t
+tr_testint(struct tr_info *tr, char channel)
+{
+ return tr_rd(tr, (channel & 0x20)? TR_REG_ADDRINTB : TR_REG_ADDRINTA,
+ 4) & (1<<(channel & 0x1f));
+}
+
+static void
+tr_clrint(struct tr_info *tr, char channel)
+{
+ tr_wr(tr, (channel & 0x20)? TR_REG_ADDRINTB : TR_REG_ADDRINTA,
+ 1<<(channel & 0x1f), 4);
+}
+
+static void
+tr_enaint(struct tr_info *tr, char channel, int enable)
+{
+ u_int32_t reg = (channel & 0x20)? TR_REG_INTENB : TR_REG_INTENA;
+ u_int32_t i = tr_rd(tr, reg, 4);
+ channel &= 0x1f;
+ i &= ~(1 << channel);
+ i |= (enable? 1 : 0) << channel;
+ tr_clrint(tr, channel);
+ tr_wr(tr, reg, i, 4);
+}
+
+/* playback channels */
+
+static void
+tr_selch(struct tr_info *tr, char channel)
+{
+ int i=tr_rd(tr, TR_REG_CIR, 4);
+ i &= ~TR_CIR_MASK;
+ i |= channel & 0x3f;
+ tr_wr(tr, TR_REG_CIR, i, 4);
+}
+
+static void
+tr_startch(struct tr_info *tr, char channel)
+{
+ tr_wr(tr, (channel & 0x20)? TR_REG_STARTB : TR_REG_STARTA,
+ 1<<(channel & 0x1f), 4);
+}
+
+static void
+tr_stopch(struct tr_info *tr, char channel)
+{
+ tr_wr(tr, (channel & 0x20)? TR_REG_STOPB : TR_REG_STOPA,
+ 1<<(channel & 0x1f), 4);
+}
+
+static void
+tr_wrch(struct tr_info *tr, char channel, struct tr_chinfo *ch)
+{
+ u_int32_t cr[TR_CHN_REGS], i;
+
+ ch->gvsel &= 0x00000001;
+ ch->fmc &= 0x00000003;
+ ch->fms &= 0x0000000f;
+ ch->ctrl &= 0x0000000f;
+ ch->pan &= 0x0000007f;
+ ch->rvol &= 0x0000007f;
+ ch->cvol &= 0x0000007f;
+ ch->vol &= 0x000000ff;
+ ch->ec &= 0x00000fff;
+ ch->alpha &= 0x00000fff;
+ ch->delta &= 0x0000ffff;
+ ch->lba &= 0x3fffffff;
+
+ cr[1]=ch->lba;
+ cr[3]=(ch->rvol<<7) | (ch->cvol);
+ cr[4]=(ch->gvsel<<31)|(ch->pan<<24)|(ch->vol<<16)|(ch->ctrl<<12)|(ch->ec);
+
+ switch (tr->type) {
+ case TDX_PCI_ID:
+ ch->cso &= 0x0000ffff;
+ ch->eso &= 0x0000ffff;
+ cr[0]=(ch->cso<<16) | (ch->alpha<<4) | (ch->fms);
+ cr[2]=(ch->eso<<16) | (ch->delta);
+ cr[3]|=0x0000c000;
+ break;
+ case TNX_PCI_ID:
+ ch->cso &= 0x00ffffff;
+ ch->eso &= 0x00ffffff;
+ cr[0]=((ch->delta & 0xff)<<24) | (ch->cso);
+ cr[2]=((ch->delta>>16)<<24) | (ch->eso);
+ cr[3]|=(ch->alpha<<20) | (ch->fms<<16) | (ch->fmc<<14);
+ break;
+ }
+ tr_selch(tr, channel);
+ for (i=0; i<TR_CHN_REGS; i++)
+ tr_wr(tr, TR_REG_CHNBASE+(i<<2), cr[i], 4);
+}
+
+static void
+tr_rdch(struct tr_info *tr, char channel, struct tr_chinfo *ch)
+{
+ u_int32_t cr[5], i;
+ tr_selch(tr, channel);
+ for (i=0; i<5; i++) cr[i]=tr_rd(tr, TR_REG_CHNBASE+(i<<2), 4);
+ ch->lba= (cr[1] & 0x3fffffff);
+ ch->fmc= (cr[3] & 0x0000c000) >> 14;
+ ch->rvol= (cr[3] & 0x00003f80) >> 7;
+ ch->cvol= (cr[3] & 0x0000007f);
+ ch->gvsel= (cr[4] & 0x80000000) >> 31;
+ ch->pan= (cr[4] & 0x7f000000) >> 24;
+ ch->vol= (cr[4] & 0x00ff0000) >> 16;
+ ch->ctrl= (cr[4] & 0x0000f000) >> 12;
+ ch->ec= (cr[4] & 0x00000fff);
+ switch(tr->type) {
+ case TDX_PCI_ID:
+ ch->cso= (cr[0] & 0xffff0000) >> 16;
+ ch->alpha= (cr[0] & 0x0000fff0) >> 4;
+ ch->fms= (cr[0] & 0x0000000f);
+ ch->eso= (cr[2] & 0xffff0000) >> 16;
+ ch->delta= (cr[2] & 0x0000ffff);
+ break;
+ case TNX_PCI_ID:
+ ch->cso= (cr[0] & 0x00ffffff);
+ ch->eso= (cr[2] & 0x00ffffff);
+ ch->delta= ((cr[2] & 0xff000000) >> 16) |
+ ((cr[0] & 0xff000000) >> 24);
+ ch->alpha= (cr[3] & 0xfff00000) >> 20;
+ ch->fms= (cr[3] & 0x000f0000) >> 16;
+ break;
+ }
+}
+
+/* channel interface */
+
+void *
+trchan_init(void *devinfo, snd_dbuf *b, pcm_channel *c, int dir)
+{
+ struct tr_info *tr = devinfo;
+ struct tr_chinfo *ch;
+ if (dir == PCMDIR_PLAY) {
+ ch = &tr->chinfo[tr->playchns];
+ ch->index = tr->playchns++;
+ } else {
+ ch = &tr->recchinfo;
+ ch->index = -1;
+ }
+ ch->buffer = b;
+ ch->buffer->bufsize = TR_BUFFSIZE;
+ ch->parent = tr;
+ ch->channel = c;
+ if (chn_allocbuf(ch->buffer, tr->parent_dmat) == -1) return NULL;
+ else return ch;
+}
+
+static int
+trchan_setdir(void *data, int dir)
+{
+ struct tr_chinfo *ch = data;
+ struct tr_info *tr = ch->parent;
+ if (dir == PCMDIR_PLAY && ch->index >= 0) {
+ ch->fmc = ch->fms = ch->ec = ch->alpha = 0;
+ ch->lba = vtophys(ch->buffer->buf);
+ ch->cso = 0;
+ ch->eso = ch->buffer->bufsize - 1;
+ ch->rvol = ch->cvol = 0;
+ ch->gvsel = 0;
+ ch->pan = 0;
+ ch->vol = 0;
+ ch->ctrl = 0x01;
+ ch->delta = 0;
+ tr_wrch(tr, ch->index, ch);
+ tr_enaint(tr, ch->index, 1);
+ } else if (dir == PCMDIR_REC && ch->index == -1) {
+ /* set up dma mode regs */
+ u_int32_t i;
+ tr_wr(tr, TR_REG_DMAR15, 0, 1);
+ i = tr_rd(tr, TR_REG_DMAR11, 1) & 0x03;
+ tr_wr(tr, TR_REG_DMAR11, i | 0x54, 1);
+ /* set up base address */
+ tr_wr(tr, TR_REG_DMAR0, vtophys(ch->buffer->buf), 4);
+ /* set up buffer size */
+ i = tr_rd(tr, TR_REG_DMAR4, 4) & ~0x00ffffff;
+ tr_wr(tr, TR_REG_DMAR4, i | (ch->buffer->bufsize - 1), 4);
+ } else return -1;
+ return 0;
+}
+
+static int
+trchan_setformat(void *data, u_int32_t format)
+{
+ struct tr_chinfo *ch = data;
+ struct tr_info *tr = ch->parent;
+ u_int32_t bits = tr_fmttobits(format);
+
+ if (ch->index >= 0) {
+ tr_rdch(tr, ch->index, ch);
+ ch->eso = (ch->buffer->bufsize / ch->buffer->sample_size) - 1;
+ ch->ctrl = bits | 0x01;
+ tr_wrch(tr, ch->index, ch);
+ } else {
+ u_int32_t i;
+ /* set # of samples between interrupts */
+ i = (TR_INTSAMPLES >> ((bits & 0x08)? 1 : 0)) - 1;
+ tr_wr(tr, TR_REG_SBBL, i | (i << 16), 4);
+ /* set sample format */
+ i = 0x18 | (bits << 4);
+ tr_wr(tr, TR_REG_SBCTRL, i, 1);
+ }
+ return 0;
+}
+
+static int
+trchan_setspeed(void *data, u_int32_t speed)
+{
+ struct tr_chinfo *ch = data;
+ struct tr_info *tr = ch->parent;
+
+ if (ch->index >= 0) {
+ tr_rdch(tr, ch->index, ch);
+ ch->delta = (speed << 12) / 48000;
+ tr_wrch(tr, ch->index, ch);
+ return (ch->delta * 48000) >> 12;
+ } else {
+ /* setup speed */
+ ch->delta = (48000 << 12) / speed;
+ tr_wr(tr, TR_REG_SBDELTA, ch->delta, 2);
+ return (48000 << 12) / ch->delta;
+ }
+ return 0;
+}
+
+static int
+trchan_setblocksize(void *data, u_int32_t blocksize)
+{
+ struct tr_chinfo *ch = data;
+ return ch->buffer->bufsize / 2;
+}
+
+static int
+trchan_trigger(void *data, int go)
+{
+ struct tr_chinfo *ch = data;
+ struct tr_info *tr = ch->parent;
+ if (ch->index >= 0) {
+ if (go == PCMTRIG_START) tr_startch(tr, ch->index);
+ else tr_stopch(tr, ch->index);
+ } else {
+ u_int32_t i = tr_rd(tr, TR_REG_SBCTRL, 1) & ~7;
+ tr_wr(tr, TR_REG_SBCTRL, i | (go == PCMTRIG_START)? 1 : 0, 1);
+ }
+ return 0;
+}
+
+static int
+trchan_getptr(void *data)
+{
+ struct tr_chinfo *ch = data;
+ struct tr_info *tr = ch->parent;
+ if (ch->index >= 0) {
+ tr_rdch(tr, ch->index, ch);
+ return ch->cso * ch->buffer->sample_size;
+ } else return tr_rd(tr, TR_REG_DMAR0, 4) - vtophys(ch->buffer->buf);
+}
+
+static pcmchan_caps *
+trchan_getcaps(void *data)
+{
+ struct tr_chinfo *ch = data;
+ return (ch->index >= 0)? &tr_playcaps : &tr_reccaps;
+}
+
+/* The interrupt handler */
+
+static void
+tr_intr(void *p)
+{
+ struct tr_info *tr = (struct tr_info *)p;
+ u_int32_t intsrc = tr_rd(tr, TR_REG_MISCINT, 4);
+
+ if (intsrc & TR_INT_ADDR) {
+ int i;
+ for (i = 0; i < tr->playchns; i++) {
+ if (tr_testint(tr, i)) {
+ chn_intr(tr->chinfo[i].channel);
+ tr_clrint(tr, i);
+ }
+ }
+ }
+ if (intsrc & TR_INT_SB) {
+ chn_intr(tr->recchinfo.channel);
+ tr_rd(tr, TR_REG_SBR9, 1);
+ tr_rd(tr, TR_REG_SBR10, 1);
+ }
+}
+
+/* -------------------------------------------------------------------- */
+
+/*
+ * Probe and attach the card
+ */
+
+static int
+tr_init(struct tr_info *tr)
+{
+ if (tr->type == TDX_PCI_ID) {
+ tr_wr(tr, TDX_REG_CODECST, TDX_CDC_ON, 4);
+ } else tr_wr(tr, TNX_REG_CODECST, TNX_CDC_ON, 4);
+
+ tr_wr(tr, TR_REG_CIR, TR_CIR_MIDENA | TR_CIR_ADDRENA, 4);
+ tr->playchns = 0;
+ return 0;
+}
+
+static int
+tr_pci_probe(device_t dev)
+{
+ if (pci_get_devid(dev) == TDX_PCI_ID) {
+ device_set_desc(dev, "Trident 4DWave DX");
+ return 0;
+ }
+ if (pci_get_devid(dev) == TNX_PCI_ID) {
+ device_set_desc(dev, "Trident 4DWave NX");
+ return 0;
+ }
+
+ return ENXIO;
+}
+
+static int
+tr_pci_attach(device_t dev)
+{
+ snddev_info *d;
+ u_int32_t data;
+ struct tr_info *tr;
+ struct ac97_info *codec;
+ int i;
+ int mapped;
+ char status[SND_STATUSLEN];
+
+ d = device_get_softc(dev);
+ if ((tr = malloc(sizeof(*tr), M_DEVBUF, M_NOWAIT)) == NULL) {
+ device_printf(dev, "cannot allocate softc\n");
+ return ENXIO;
+ }
+
+ bzero(tr, sizeof(*tr));
+ tr->type = pci_get_devid(dev);
+
+ data = pci_read_config(dev, PCIR_COMMAND, 2);
+ data |= (PCIM_CMD_PORTEN|PCIM_CMD_MEMEN|PCIM_CMD_BUSMASTEREN);
+ pci_write_config(dev, PCIR_COMMAND, data, 2);
+ data = pci_read_config(dev, PCIR_COMMAND, 2);
+
+ mapped = 0;
+ /* XXX dfr: is this strictly necessary? */
+ for (i = 0; (mapped == 0) && (i < PCI_MAXMAPS_0); i++) {
+ tr->regid = PCIR_MAPS + i*4;
+ tr->regtype = SYS_RES_MEMORY;
+ tr->reg = bus_alloc_resource(dev, tr->regtype, &tr->regid,
+ 0, ~0, 1, RF_ACTIVE);
+ if (!tr->reg) {
+ tr->regtype = SYS_RES_IOPORT;
+ tr->reg = bus_alloc_resource(dev, tr->regtype,
+ &tr->regid, 0, ~0, 1,
+ RF_ACTIVE);
+ }
+ if (tr->reg) {
+ tr->st = rman_get_bustag(tr->reg);
+ tr->sh = rman_get_bushandle(tr->reg);
+ mapped++;
+ }
+ }
+
+ if (mapped == 0) {
+ device_printf(dev, "unable to map register space\n");
+ goto bad;
+ }
+
+ if (tr_init(tr) == -1) {
+ device_printf(dev, "unable to initialize the card\n");
+ goto bad;
+ }
+
+ codec = ac97_create(tr, tr_rdcd, tr_wrcd);
+ if (codec == NULL) goto bad;
+ mixer_init(d, &ac97_mixer, codec);
+
+ tr->irqid = 0;
+ tr->irq = bus_alloc_resource(dev, SYS_RES_IRQ, &tr->irqid,
+ 0, ~0, 1, RF_ACTIVE | RF_SHAREABLE);
+ if (!tr->irq ||
+ bus_setup_intr(dev, tr->irq, INTR_TYPE_TTY, tr_intr, tr, &tr->ih)) {
+ device_printf(dev, "unable to map interrupt\n");
+ goto bad;
+ }
+
+ if (bus_dma_tag_create(/*parent*/NULL, /*alignment*/2, /*boundary*/0,
+ /*lowaddr*/BUS_SPACE_MAXADDR_32BIT,
+ /*highaddr*/BUS_SPACE_MAXADDR,
+ /*filter*/NULL, /*filterarg*/NULL,
+ /*maxsize*/TR_BUFFSIZE, /*nsegments*/1, /*maxsegz*/0x3ffff,
+ /*flags*/0, &tr->parent_dmat) != 0) {
+ device_printf(dev, "unable to create dma tag\n");
+ goto bad;
+ }
+
+ snprintf(status, 64, "at %s 0x%lx irq %ld",
+ (tr->regtype == SYS_RES_IOPORT)? "io" : "memory",
+ rman_get_start(tr->reg), rman_get_start(tr->irq));
+
+ if (pcm_register(dev, tr, TR_MAXPLAYCH, 1)) goto bad;
+ pcm_addchan(dev, PCMDIR_REC, &tr_chantemplate, tr);
+ for (i = 0; i < TR_MAXPLAYCH; i++)
+ pcm_addchan(dev, PCMDIR_PLAY, &tr_chantemplate, tr);
+ pcm_setstatus(dev, status);
+
+ return 0;
+
+bad:
+ if (tr->reg) bus_release_resource(dev, tr->regtype, tr->regid, tr->reg);
+ if (tr->ih) bus_teardown_intr(dev, tr->irq, tr->ih);
+ if (tr->irq) bus_release_resource(dev, SYS_RES_IRQ, tr->irqid, tr->irq);
+ free(tr, M_DEVBUF);
+ return ENXIO;
+}
+
+static device_method_t tr_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, tr_pci_probe),
+ DEVMETHOD(device_attach, tr_pci_attach),
+
+ { 0, 0 }
+};
+
+static driver_t tr_driver = {
+ "pcm",
+ tr_methods,
+ sizeof(snddev_info),
+};
+
+static devclass_t pcm_devclass;
+
+DRIVER_MODULE(tr, pci, tr_driver, pcm_devclass, 0, 0);
+
+#endif /* NPCI != 0 */
diff --git a/sys/dev/pcm/pci/t4dwave.h b/sys/dev/pcm/pci/t4dwave.h
new file mode 100644
index 0000000..31f3340
--- /dev/null
+++ b/sys/dev/pcm/pci/t4dwave.h
@@ -0,0 +1,103 @@
+/*
+ * Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk>
+ * 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$
+ */
+
+#ifndef _T4DWAVE_REG_H
+#define _T4DWAVE_REG_H
+
+#define TDX_PCI_ID 0x20001023
+#define TNX_PCI_ID 0x20011023
+
+#define TR_BUFFSIZE 0x8000
+#define TR_TIMEOUT_CDC 0xffff
+#define TR_INTSAMPLES 0x2000
+#define TR_MAXPLAYCH 64
+
+#define TR_REG_CIR 0xa0
+#define TR_CIR_MASK 0x0000003f
+#define TR_CIR_ADDRENA 0x00001000
+#define TR_CIR_MIDENA 0x00002000
+#define TR_REG_MISCINT 0xb0
+#define TR_INT_ADDR 0x00000020
+#define TR_INT_SB 0x00000004
+
+#define TR_REG_DMAR0 0x00
+#define TR_REG_DMAR4 0x04
+#define TR_REG_DMAR11 0x0b
+#define TR_REG_DMAR15 0x0f
+#define TR_REG_SBR4 0x14
+#define TR_REG_SBR5 0x15
+#define TR_SB_INTSTATUS 0x82
+#define TR_REG_SBR9 0x1e
+#define TR_REG_SBR10 0x1f
+#define TR_REG_SBBL 0xc0
+#define TR_REG_SBCTRL 0xc4
+#define TR_REG_SBDELTA 0xac
+
+#define TR_CDC_DATA 16
+#define TDX_REG_CODECWR 0x40
+#define TDX_REG_CODECRD 0x44
+#define TDX_CDC_RWSTAT 0x00008000
+#define TDX_REG_CODECST 0x48
+#define TDX_CDC_SBCTRL 0x40
+#define TDX_CDC_ACTIVE 0x20
+#define TDX_CDC_READY 0x10
+#define TDX_CDC_ADCON 0x08
+#define TDX_CDC_DACON 0x02
+#define TDX_CDC_RESET 0x01
+#define TDX_CDC_ON (TDX_CDC_ADCON|TDX_CDC_DACON)
+
+#define TNX_REG_CODECWR 0x44
+#define TNX_REG_CODEC1RD 0x48
+#define TNX_REG_CODEC2RD 0x4c
+#define TNX_CDC_RWSTAT 0x00000c00
+#define TNX_CDC_SEC 0x00000100
+#define TNX_REG_CODECST 0x40
+#define TNX_CDC_READY2 0x40
+#define TNX_CDC_ADC2ON 0x20
+#define TNX_CDC_DAC2ON 0x10
+#define TNX_CDC_READY1 0x08
+#define TNX_CDC_ADC1ON 0x04
+#define TNX_CDC_DAC1ON 0x02
+#define TNX_CDC_RESET 0x01
+#define TNX_CDC_ON (TNX_CDC_ADC1ON|TNX_CDC_DAC1ON)
+
+
+#define TR_REG_STARTA 0x80
+#define TR_REG_STOPA 0x84
+#define TR_REG_ADDRINTA 0x98
+#define TR_REG_INTENA 0xa4
+
+#define TR_REG_STARTB 0xb4
+#define TR_REG_STOPB 0xb8
+#define TR_REG_ADDRINTB 0xd8
+#define TR_REG_INTENB 0xdc
+
+#define TR_REG_CHNBASE 0xe0
+#define TR_CHN_REGS 5
+
+#endif
diff --git a/sys/dev/pcm/sound.c b/sys/dev/pcm/sound.c
new file mode 100644
index 0000000..102b87a
--- /dev/null
+++ b/sys/dev/pcm/sound.c
@@ -0,0 +1,438 @@
+/*
+ * Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk>
+ * (C) 1997 Luigi Rizzo (luigi@iet.unipi.it)
+ * 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$
+ */
+
+#include "opt_devfs.h"
+
+#include <dev/pcm/sound.h>
+#ifdef DEVFS
+#include <sys/devfsext.h>
+#endif /* DEVFS */
+
+#if NPCM > 0 /* from "pcm.h" via disgusting #include in snd/sound.h */
+
+extern struct isa_driver pcmdriver;
+
+static int status_isopen = 0;
+static int status_init(char *buf, int size);
+static int status_read(struct uio *buf);
+
+static d_open_t sndopen;
+static d_close_t sndclose;
+static d_ioctl_t sndioctl;
+static d_read_t sndread;
+static d_write_t sndwrite;
+static d_mmap_t sndmmap;
+static d_poll_t sndpoll;
+
+#define CDEV_MAJOR 30
+static struct cdevsw snd_cdevsw = {
+ /* open */ sndopen,
+ /* close */ sndclose,
+ /* read */ sndread,
+ /* write */ sndwrite,
+ /* ioctl */ sndioctl,
+ /* stop */ nostop,
+ /* reset */ noreset,
+ /* devtotty */ nodevtotty,
+ /* poll */ sndpoll,
+ /* mmap */ sndmmap,
+ /* strategy */ nostrategy,
+ /* name */ "snd",
+ /* parms */ noparms,
+ /* maj */ CDEV_MAJOR,
+ /* dump */ nodump,
+ /* psize */ nopsize,
+ /* flags */ 0,
+ /* maxio */ 0,
+ /* bmaj */ -1
+};
+
+/* PROPOSAL:
+each unit needs:
+status, mixer, dsp, dspW, audio, sequencer, midi-in, seq2, sndproc = 9 devices
+dspW and audio are deprecated.
+dsp needs min 64 channels, will give it 256
+
+minor = (unit << 12) + (dev << 8) + channel
+currently minor = (channel << 8) + (unit << 4) + dev
+
+nomenclature:
+ /dev/pcmX/dsp.(0..255)
+ /dev/pcmX/dspW
+ /dev/pcmX/audio
+ /dev/pcmX/status
+ /dev/pcmX/mixer
+ [etc.]
+*/
+
+#define PCMMINOR(x) (minor(x))
+#define PCMCHAN(x) ((PCMMINOR(x) & 0x0000ff00) >> 8)
+#define PCMUNIT(x) ((PCMMINOR(x) & 0x000000f0) >> 4)
+#define PCMDEV(x) (PCMMINOR(x) & 0x0000000f)
+
+static devclass_t pcm_devclass;
+
+static snddev_info *
+gsd(int unit)
+{
+ return devclass_get_softc(pcm_devclass, unit);
+}
+
+int
+pcm_addchan(device_t dev, int dir, pcm_channel *templ, void *devinfo)
+{
+ snddev_info *d = device_get_softc(dev);
+ pcm_channel *ch;
+
+ ch = (dir == PCMDIR_PLAY)? &d->play[d->playcount++] : &d->rec[d->reccount++];
+ *ch = *templ;
+ chn_init(ch, devinfo, dir);
+ d->chancount++;
+ return 0;
+}
+
+int
+pcm_setstatus(device_t dev, char *str)
+{
+ snddev_info *d = device_get_softc(dev);
+ strncpy(d->status, str, SND_STATUSLEN);
+ return 0;
+}
+
+u_int32_t
+pcm_getflags(device_t dev)
+{
+ snddev_info *d = device_get_softc(dev);
+ return d->flags;
+}
+
+void
+pcm_setflags(device_t dev, u_int32_t val)
+{
+ snddev_info *d = device_get_softc(dev);
+ d->flags = val;
+}
+
+/* This is the generic init routine */
+int
+pcm_register(device_t dev, void *devinfo, int numplay, int numrec)
+{
+ int sz, unit = device_get_unit(dev);
+ snddev_info *d = device_get_softc(dev);
+
+ if (!pcm_devclass) {
+ pcm_devclass = device_get_devclass(dev);
+ cdevsw_add(&snd_cdevsw);
+ }
+ d->devinfo = devinfo;
+ d->chancount = d->playcount = d->reccount = 0;
+ sz = (numplay + numrec) * sizeof(pcm_channel *);
+ d->aplay = (pcm_channel **)malloc(sz, M_DEVBUF, M_NOWAIT);
+ if (!d->aplay) goto no;
+ d->arec = (pcm_channel **)malloc(sz, M_DEVBUF, M_NOWAIT);
+ if (!d->arec) goto no;
+ bzero(d->aplay, sz);
+ bzero(d->arec, sz);
+
+ d->play = (pcm_channel *)malloc(numplay * sizeof(pcm_channel),
+ M_DEVBUF, M_NOWAIT);
+ if (!d->play) goto no;
+ d->rec = (pcm_channel *)malloc(numrec * sizeof(pcm_channel),
+ M_DEVBUF, M_NOWAIT);
+ if (!d->rec) goto no;
+ bzero(d->play, numplay * sizeof(pcm_channel));
+ bzero(d->rec, numrec * sizeof(pcm_channel));
+
+ fkchan_setup(&d->fakechan);
+ chn_init(&d->fakechan, NULL, 0);
+ d->magic = MAGIC(unit); /* debugging... */
+
+ return 0;
+no:
+ if (d->aplay) free(d->aplay, M_DEVBUF);
+ if (d->play) free(d->play, M_DEVBUF);
+ if (d->arec) free(d->arec, M_DEVBUF);
+ if (d->rec) free(d->rec, M_DEVBUF);
+ return ENXIO;
+}
+
+/*
+ * a small utility function which, given a device number, returns
+ * a pointer to the associated snddev_info struct, and sets the unit
+ * number.
+ */
+static snddev_info *
+get_snddev_info(dev_t i_dev, int *unit, int *dev, int *chan)
+{
+ int u, d, c;
+
+ u = PCMUNIT(i_dev);
+ d = PCMDEV(i_dev);
+ c = PCMCHAN(i_dev);
+ if (u > devclass_get_maxunit(pcm_devclass)) u = -1;
+ if (unit) *unit = u;
+ if (dev) *dev = d;
+ if (chan) *chan = c;
+ if (u < 0) return NULL;
+
+ switch(d) {
+ case SND_DEV_CTL: /* /dev/mixer handled by pcm */
+ case SND_DEV_STATUS: /* /dev/sndstat handled by pcm */
+ case SND_DEV_DSP:
+ case SND_DEV_DSP16:
+ case SND_DEV_AUDIO:
+ return gsd(u);
+
+ case SND_DEV_SEQ: /* XXX when enabled... */
+ case SND_DEV_SEQ2:
+ case SND_DEV_MIDIN:
+ case SND_DEV_SNDPROC: /* /dev/sndproc handled by pcm */
+ default:
+ printf("unsupported subdevice %d\n", d);
+ return NULL;
+ }
+}
+
+static int
+sndopen(dev_t i_dev, int flags, int mode, struct proc *p)
+{
+ int dev, unit, chan;
+ snddev_info *d = get_snddev_info(i_dev, &unit, &dev, &chan);
+
+ DEB(printf("open snd%d subdev %d flags 0x%08x mode 0x%08x\n",
+ unit, dev, flags, mode));
+
+ switch(dev) {
+ case SND_DEV_STATUS:
+ if (status_isopen) return EBUSY;
+ status_isopen = 1;
+ return 0;
+
+ case SND_DEV_CTL:
+ return d? 0 : ENXIO;
+
+ case SND_DEV_AUDIO:
+ case SND_DEV_DSP:
+ case SND_DEV_DSP16:
+ return d? dsp_open(d, chan, flags, dev) : ENXIO;
+
+ default:
+ return ENXIO;
+ }
+}
+
+static int
+sndclose(dev_t i_dev, int flags, int mode, struct proc *p)
+{
+ int dev, unit, chan;
+ snddev_info *d = get_snddev_info(i_dev, &unit, &dev, &chan);
+
+ DEB(printf("close snd%d subdev %d\n", unit, dev));
+
+ switch(dev) { /* only those for which close makes sense */
+ case SND_DEV_STATUS:
+ if (!status_isopen) return EBADF;
+ status_isopen = 0;
+ return 0;
+
+ case SND_DEV_CTL:
+ return d? 0 : ENXIO;
+
+ case SND_DEV_AUDIO:
+ case SND_DEV_DSP:
+ case SND_DEV_DSP16:
+ return d? dsp_close(d, chan, dev) : ENXIO;
+
+ default:
+ return ENXIO;
+ }
+}
+
+static int
+sndread(dev_t i_dev, struct uio *buf, int flag)
+{
+ int dev, unit, chan;
+ snddev_info *d = get_snddev_info(i_dev, &unit, &dev, &chan);
+ DEB(printf("read snd%d subdev %d flag 0x%08x\n", unit, dev, flag));
+
+ switch(dev) {
+ case SND_DEV_STATUS:
+ return status_isopen? status_read(buf) : EBADF;
+
+ case SND_DEV_AUDIO:
+ case SND_DEV_DSP:
+ case SND_DEV_DSP16:
+ return d? dsp_read(d, chan, buf, flag) : EBADF;
+
+ default:
+ return ENXIO;
+ }
+}
+
+static int
+sndwrite(dev_t i_dev, struct uio *buf, int flag)
+{
+ int dev, unit, chan;
+ snddev_info *d = get_snddev_info(i_dev, &unit, &dev, &chan);
+
+ DEB(printf("write snd%d subdev %d flag 0x%08x\n", unit, dev & 0xf, flag));
+
+ switch(dev) { /* only writeable devices */
+ case SND_DEV_DSP:
+ case SND_DEV_DSP16:
+ case SND_DEV_AUDIO:
+ return d? dsp_write(d, chan, buf, flag) : EBADF;
+
+ default:
+ return EPERM; /* for non-writeable devices ; */
+ }
+}
+
+static int
+sndioctl(dev_t i_dev, u_long cmd, caddr_t arg, int mode, struct proc * p)
+{
+ int dev, chan;
+ snddev_info *d = get_snddev_info(i_dev, NULL, &dev, &chan);
+
+ if (d == NULL) return ENXIO;
+
+ switch(dev) {
+ case SND_DEV_CTL:
+ return mixer_ioctl(d, cmd, arg);
+
+ case SND_DEV_AUDIO:
+ case SND_DEV_DSP:
+ case SND_DEV_DSP16:
+ return dsp_ioctl(d, chan, cmd, arg);
+
+ default:
+ return ENXIO;
+ }
+}
+
+static int
+sndpoll(dev_t i_dev, int events, struct proc *p)
+{
+ int dev, chan;
+ snddev_info *d = get_snddev_info(i_dev, NULL, &dev, &chan);
+
+ DEB(printf("sndpoll dev 0x%04x events 0x%08x\n", i_dev, events));
+
+ if (d == NULL) return ENXIO;
+
+ switch(dev) {
+ case SND_DEV_AUDIO:
+ case SND_DEV_DSP:
+ case SND_DEV_DSP16:
+ return dsp_poll(d, chan, events, p);
+
+ default:
+ return (events &
+ (POLLIN | POLLOUT | POLLRDNORM | POLLWRNORM)) | POLLHUP;
+ }
+}
+
+/*
+ * The mmap interface allows access to the play and read buffer,
+ * plus the device descriptor.
+ * The various blocks are accessible at the following offsets:
+ *
+ * 0x00000000 ( 0 ) : write buffer ;
+ * 0x01000000 (16 MB) : read buffer ;
+ * 0x02000000 (32 MB) : device descriptor (dangerous!)
+ *
+ * WARNING: the mmap routines assume memory areas are aligned. This
+ * is true (probably) for the dma buffers, but likely false for the
+ * device descriptor. As a consequence, we do not know where it is
+ * located in the requested area.
+ */
+static int
+sndmmap(dev_t i_dev, vm_offset_t offset, int nprot)
+{
+ int unit, dev, chan;
+ snddev_info *d = get_snddev_info(i_dev, &unit, &dev, &chan);
+
+ DEB(printf("sndmmap d 0x%p dev 0x%04x ofs 0x%08x nprot 0x%08x\n",
+ d, dev, offset, nprot));
+
+ if (d == NULL || nprot & PROT_EXEC) return -1; /* forbidden */
+
+ switch(dev) {
+ case SND_DEV_AUDIO:
+ case SND_DEV_DSP:
+ case SND_DEV_DSP16:
+ return dsp_mmap(d, chan, offset, nprot);
+
+ default:
+ return -1;
+ }
+}
+
+static int
+status_init(char *buf, int size)
+{
+ int i;
+ device_t dev;
+ snddev_info *d;
+
+ snprintf(buf, size, "FreeBSD Audio Driver (newpcm) %s %s\n"
+ "Installed devices:\n", __DATE__, __TIME__);
+
+ for (i = 0; i <= devclass_get_maxunit(pcm_devclass); i++) {
+ d = gsd(i);
+ if (!d) continue;
+ dev = devclass_get_device(pcm_devclass, i);
+ if (1) snprintf(buf + strlen(buf), size - strlen(buf),
+ "pcm%d: <%s> %s (%d/%d channels%s)\n",
+ i, device_get_desc(dev), d->status,
+ d->playcount, d->reccount,
+ (!(d->flags & SD_F_SIMPLEX))? " duplex" : "");
+ }
+ return strlen(buf);
+}
+
+static int
+status_read(struct uio *buf)
+{
+ static char status_buf[4096];
+ static int bufptr = 0, buflen = 0;
+ int l;
+
+ if (status_isopen == 1) {
+ status_isopen++;
+ bufptr = 0;
+ buflen = status_init(status_buf, sizeof status_buf);
+ }
+
+ l = min(buf->uio_resid, buflen - bufptr);
+ bufptr += l;
+ return (l > 0)? uiomove(status_buf + bufptr - l, l, buf) : 0;
+}
+
+#endif /* NPCM > 0 */
diff --git a/sys/dev/pcm/sound.h b/sys/dev/pcm/sound.h
new file mode 100644
index 0000000..5d17624
--- /dev/null
+++ b/sys/dev/pcm/sound.h
@@ -0,0 +1,181 @@
+/*
+ * Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk>
+ * Copyright by Hannu Savolainen 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.
+ *
+ * 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$
+ */
+
+#ifdef KERNEL
+#include "pcm.h"
+#else
+#error why?
+#define NPCM 1
+#endif
+#if NPCM > 0
+
+/*
+ * first, include kernel header files.
+ */
+
+#ifndef _OS_H_
+#define _OS_H_
+
+#ifdef KERNEL
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/ioccom.h>
+
+#include <sys/filio.h>
+#include <sys/sockio.h>
+#include <sys/fcntl.h>
+#include <sys/tty.h>
+#include <sys/proc.h>
+
+#include <sys/kernel.h> /* for DATA_SET */
+
+#include <sys/module.h>
+#include <sys/conf.h>
+#include <sys/file.h>
+#include <sys/uio.h>
+#include <sys/syslog.h>
+#include <sys/errno.h>
+#include <sys/malloc.h>
+#include <sys/bus.h>
+#include <sys/buf.h>
+#include <machine/clock.h> /* for DELAY */
+#include <machine/resource.h>
+#include <machine/bus_memio.h>
+#include <machine/bus_pio.h>
+#include <machine/bus.h>
+#include <sys/rman.h>
+#include <sys/mman.h>
+#include <sys/poll.h>
+#include <sys/soundcard.h>
+#include <isa/isavar.h>
+#include <vm/vm.h>
+#include <vm/pmap.h>
+
+#else
+struct isa_device { int dummy; };
+#define d_open_t void
+#define d_close_t void
+#define d_read_t void
+#define d_write_t void
+#define d_ioctl_t void
+#define d_select_t void
+#endif /* KERNEL */
+
+#endif /* _OS_H_ */
+
+#include <dev/pcm/datatypes.h>
+#include <dev/pcm/channel.h>
+#include <dev/pcm/mixer.h>
+#include <dev/pcm/dsp.h>
+
+#define MAGIC(unit) (0xa4d10de0 + unit)
+
+#define SD_F_SIMPLEX 0x00000001
+#define SD_F_EVILSB16 0x00000002
+#define SD_F_PRIO_RD 0x10000000
+#define SD_F_PRIO_WR 0x20000000
+#define SD_F_PRIO_SET (SD_F_PRIO_RD | SD_F_PRIO_WR)
+#define SD_F_DIR_SET 0x40000000
+#define SD_F_TRANSIENT 0xf0000000
+
+/* many variables should be reduced to a range. Here define a macro */
+#define RANGE(var, low, high) (var) = \
+ (((var)<(low))? (low) : ((var)>(high))? (high) : (var))
+
+#define DSP_BUFFSIZE (65536 - 256) /* XXX */
+/* the last 256 bytes are room for buggy soundcard to overflow. */
+
+/* make figuring out what a format is easier. got AFMT_STEREO already */
+#define AFMT_16BIT (AFMT_S16_LE | AFMT_S16_BE | AFMT_U16_LE | AFMT_U16_BE)
+#define AFMT_SIGNED (AFMT_S16_LE | AFMT_S16_BE | AFMT_S8)
+#define AFMT_BIGENDIAN (AFMT_S16_BE | AFMT_U16_BE)
+
+int fkchan_setup(pcm_channel *c);
+
+#ifdef KERNEL
+#include "pnp.h"
+#endif /* KERNEL */
+
+/*
+ * Minor numbers for the sound driver.
+ *
+ * Unfortunately Creative called the codec chip of SB as a DSP. For this
+ * reason the /dev/dsp is reserved for digitized audio use. There is a
+ * device for true DSP processors but it will be called something else.
+ * In v3.0 it's /dev/sndproc but this could be a temporary solution.
+ */
+
+#define SND_DEV_CTL 0 /* Control port /dev/mixer */
+#define SND_DEV_SEQ 1 /* Sequencer /dev/sequencer */
+#define SND_DEV_MIDIN 2 /* Raw midi access */
+#define SND_DEV_DSP 3 /* Digitized voice /dev/dsp */
+#define SND_DEV_AUDIO 4 /* Sparc compatible /dev/audio */
+#define SND_DEV_DSP16 5 /* Like /dev/dsp but 16 bits/sample */
+#define SND_DEV_STATUS 6 /* /dev/sndstat */
+ /* #7 not in use now. */
+#define SND_DEV_SEQ2 8 /* /dev/sequencer, level 2 interface */
+#define SND_DEV_SNDPROC 9 /* /dev/sndproc for programmable devices */
+#define SND_DEV_PSS SND_DEV_SNDPROC /* ? */
+
+#define DSP_DEFAULT_SPEED 8000
+
+#define ON 1
+#define OFF 0
+
+#ifdef KERNEL
+
+/*
+ * some macros for debugging purposes
+ * DDB/DEB to enable/disable debugging stuff
+ * BVDDB to enable debugging when bootverbose
+ */
+#define DDB(x) x /* XXX */
+#define BVDDB(x) if (bootverbose) x
+
+#ifndef DEB
+#define DEB(x)
+#endif
+
+int pcm_addchan(device_t dev, int dir, pcm_channel *templ, void *devinfo);
+int pcm_register(device_t dev, void *devinfo, int numplay, int numrec);
+int pcm_setstatus(device_t dev, char *str);
+u_int32_t pcm_getflags(device_t dev);
+void pcm_setflags(device_t dev, u_int32_t val);
+
+#endif /* KERNEL */
+
+/* usage of flags in device config entry (config file) */
+#define DV_F_DRQ_MASK 0x00000007 /* mask for secondary drq */
+#define DV_F_DUAL_DMA 0x00000010 /* set to use secondary dma channel */
+
+/* ought to be made obsolete */
+#define DV_F_DEV_MASK 0x0000ff00 /* force device type/class */
+#define DV_F_DEV_SHIFT 8 /* force device type/class */
+
+#endif
diff --git a/sys/dev/sound/pci/aureal.c b/sys/dev/sound/pci/aureal.c
new file mode 100644
index 0000000..49900e6
--- /dev/null
+++ b/sys/dev/sound/pci/aureal.c
@@ -0,0 +1,693 @@
+/*
+ * Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk>
+ * 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$
+ */
+
+#include "pci.h"
+#include "pcm.h"
+
+#include <dev/pcm/sound.h>
+#include <dev/pcm/ac97.h>
+#include <dev/pcm/pci/aureal.h>
+
+#include <pci/pcireg.h>
+#include <pci/pcivar.h>
+
+#if NPCI != 0
+
+/* PCI IDs of supported chips */
+#define AU8820_PCI_ID 0x000112eb
+
+/* channel interface */
+static void *auchan_init(void *devinfo, snd_dbuf *b, pcm_channel *c, int dir);
+static int auchan_setdir(void *data, int dir);
+static int auchan_setformat(void *data, u_int32_t format);
+static int auchan_setspeed(void *data, u_int32_t speed);
+static int auchan_setblocksize(void *data, u_int32_t blocksize);
+static int auchan_trigger(void *data, int go);
+static int auchan_getptr(void *data);
+static pcmchan_caps *auchan_getcaps(void *data);
+
+static pcmchan_caps au_playcaps = {
+ 4000, 48000,
+ AFMT_STEREO | AFMT_U8 | AFMT_S16_LE,
+ AFMT_STEREO | AFMT_S16_LE
+};
+
+static pcmchan_caps au_reccaps = {
+ 4000, 48000,
+ AFMT_STEREO | AFMT_U8 | AFMT_S16_LE,
+ AFMT_STEREO | AFMT_S16_LE
+};
+
+static pcm_channel au_chantemplate = {
+ auchan_init,
+ auchan_setdir,
+ auchan_setformat,
+ auchan_setspeed,
+ auchan_setblocksize,
+ auchan_trigger,
+ auchan_getptr,
+ auchan_getcaps,
+};
+
+/* -------------------------------------------------------------------- */
+
+static u_int32_t au_rdcd(void *arg, int regno);
+static void au_wrcd(void *arg, int regno, u_int32_t data);
+
+struct au_info;
+
+struct au_chinfo {
+ struct au_info *parent;
+ pcm_channel *channel;
+ snd_dbuf *buffer;
+ int dir;
+};
+
+struct au_info {
+ int unit;
+
+ bus_space_tag_t st[3];
+ bus_space_handle_t sh[3];
+
+ bus_dma_tag_t parent_dmat;
+
+ u_int32_t x[32], y[128];
+ char z[128];
+ u_int32_t routes[4], interrupts;
+ struct au_chinfo pch;
+};
+
+static int au_init(device_t dev, struct au_info *au);
+static void au_intr(void *);
+
+/* -------------------------------------------------------------------- */
+
+static u_int32_t
+au_rd(struct au_info *au, int mapno, int regno, int size)
+{
+ switch(size) {
+ case 1:
+ return bus_space_read_1(au->st[mapno], au->sh[mapno], regno);
+ case 2:
+ return bus_space_read_2(au->st[mapno], au->sh[mapno], regno);
+ case 4:
+ return bus_space_read_4(au->st[mapno], au->sh[mapno], regno);
+ default:
+ return 0xffffffff;
+ }
+}
+
+static void
+au_wr(struct au_info *au, int mapno, int regno, u_int32_t data, int size)
+{
+ switch(size) {
+ case 1:
+ bus_space_write_1(au->st[mapno], au->sh[mapno], regno, data);
+ break;
+ case 2:
+ bus_space_write_2(au->st[mapno], au->sh[mapno], regno, data);
+ break;
+ case 4:
+ bus_space_write_4(au->st[mapno], au->sh[mapno], regno, data);
+ break;
+ }
+}
+
+static u_int32_t
+au_rdcd(void *arg, int regno)
+{
+ struct au_info *au = (struct au_info *)arg;
+ int i=0, j=0;
+
+ regno<<=16;
+ au_wr(au, 0, AU_REG_CODECIO, regno, 4);
+ while (j<50) {
+ i=au_rd(au, 0, AU_REG_CODECIO, 4);
+ if ((i & 0x00ff0000) == (regno | 0x00800000)) break;
+ DELAY(j * 200 + 2000);
+ j++;
+ }
+ if (j==50) printf("pcm%d: codec timeout reading register %x (%x)\n",
+ au->unit, (regno & AU_CDC_REGMASK)>>16, i);
+ return i & AU_CDC_DATAMASK;
+}
+
+static void
+au_wrcd(void *arg, int regno, u_int32_t data)
+{
+ struct au_info *au = (struct au_info *)arg;
+ int i, j, tries;
+ i=j=tries=0;
+ do {
+ while (j<50 && (i & AU_CDC_WROK) == 0) {
+ i=au_rd(au, 0, AU_REG_CODECST, 4);
+ DELAY(2000);
+ j++;
+ }
+ if (j==50) printf("codec timeout during write of register %x, data %x\n",
+ regno, data);
+ au_wr(au, 0, AU_REG_CODECIO, (regno<<16) | AU_CDC_REGSET | data, 4);
+/* DELAY(20000);
+ i=au_rdcd(au, regno);
+*/ tries++;
+ } while (0); /* (i != data && tries < 3); */
+ /*
+ if (tries == 3) printf("giving up writing 0x%4x to codec reg %2x\n", data, regno);
+ */
+}
+
+static void
+au_setbit(u_int32_t *p, char bit, u_int32_t value)
+{
+ p += bit >> 5;
+ bit &= 0x1f;
+ *p &= ~ (1 << bit);
+ *p |= (value << bit);
+}
+
+static void
+au_addroute(struct au_info *au, int a, int b, int route)
+{
+ int j = 0x1099c+(a<<2);
+ if (au->x[a] != a+0x67) j = AU_REG_RTBASE+(au->x[a]<<2);
+
+ au_wr(au, 0, AU_REG_RTBASE+(route<<2), 0xffffffff, 4);
+ au_wr(au, 0, j, route | (b<<7), 4);
+ au->y[route]=au->x[a];
+ au->x[a]=route;
+ au->z[route]=a & 0x000000ff;
+ au_setbit(au->routes, route, 1);
+}
+
+static void
+au_delroute(struct au_info *au, int route)
+{
+ int i;
+ int j=au->z[route];
+
+ au_setbit(au->routes, route, 0);
+ au->z[route]=0x1f;
+ i=au_rd(au, 0, AU_REG_RTBASE+(route<<2), 4);
+ au_wr(au, 0, AU_REG_RTBASE+(au->y[route]<<2), i, 4);
+ au->y[i & 0x7f]=au->y[route];
+ au_wr(au, 0, AU_REG_RTBASE+(route<<2), 0xfffffffe, 4);
+ if (au->x[j] == route) au->x[j]=au->y[route];
+ au->y[route]=0x7f;
+}
+
+static void
+au_encodec(struct au_info *au, char channel)
+{
+ au_wr(au, 0, AU_REG_CODECEN,
+ au_rd(au, 0, AU_REG_CODECEN, 4) | (1 << (channel + 8)), 4);
+}
+
+static void
+au_clrfifo(struct au_info *au, u_int32_t c)
+{
+ u_int32_t i;
+
+ for (i=0; i<32; i++) au_wr(au, 0, AU_REG_FIFOBASE+(c<<7)+(i<<2), 0, 4);
+}
+
+static void
+au_setadb(struct au_info *au, u_int32_t c, u_int32_t enable)
+{
+ int x;
+
+ x = au_rd(au, 0, AU_REG_ADB, 4);
+ x &= ~(1 << c);
+ x |= (enable << c);
+ au_wr(au, 0, AU_REG_ADB, x, 4);
+}
+
+static void
+au_prepareoutput(struct au_chinfo *ch, u_int32_t format)
+{
+ struct au_info *au = ch->parent;
+ int i, stereo = (format & AFMT_STEREO)? 1 : 0;
+ u_int32_t baseaddr = vtophys(ch->buffer->buf);
+
+ au_wr(au, 0, 0x1061c, 0, 4);
+ au_wr(au, 0, 0x10620, 0, 4);
+ au_wr(au, 0, 0x10624, 0, 4);
+ switch(format & ~AFMT_STEREO) {
+ case 1:
+ i=0xb000;
+ break;
+ case 2:
+ i=0xf000;
+ break;
+ case 8:
+ i=0x7000;
+ break;
+ case 16:
+ i=0x23000;
+ break;
+ default:
+ i=0x3000;
+ }
+ au_wr(au, 0, 0x10200, baseaddr, 4);
+ au_wr(au, 0, 0x10204, baseaddr+0x1000, 4);
+ au_wr(au, 0, 0x10208, baseaddr+0x2000, 4);
+ au_wr(au, 0, 0x1020c, baseaddr+0x3000, 4);
+
+ au_wr(au, 0, 0x10400, 0xdeffffff, 4);
+ au_wr(au, 0, 0x10404, 0xfcffffff, 4);
+
+ au_wr(au, 0, 0x10580, i, 4);
+
+ au_wr(au, 0, 0x10210, baseaddr, 4);
+ au_wr(au, 0, 0x10214, baseaddr+0x1000, 4);
+ au_wr(au, 0, 0x10218, baseaddr+0x2000, 4);
+ au_wr(au, 0, 0x1021c, baseaddr+0x3000, 4);
+
+ au_wr(au, 0, 0x10408, 0x00fff000 | 0x56000000 | 0x00000fff, 4);
+ au_wr(au, 0, 0x1040c, 0x00fff000 | 0x74000000 | 0x00000fff, 4);
+
+ au_wr(au, 0, 0x10584, i, 4);
+
+ au_wr(au, 0, 0x0f800, stereo? 0x00030032 : 0x00030030, 4);
+ au_wr(au, 0, 0x0f804, stereo? 0x00030032 : 0x00030030, 4);
+
+ au_addroute(au, 0x11, 0, 0x58);
+ au_addroute(au, 0x11, stereo? 0 : 1, 0x59);
+}
+
+/* channel interface */
+static void *
+auchan_init(void *devinfo, snd_dbuf *b, pcm_channel *c, int dir)
+{
+ struct au_info *au = devinfo;
+ struct au_chinfo *ch = (dir == PCMDIR_PLAY)? &au->pch : NULL;
+
+ ch->parent = au;
+ ch->channel = c;
+ ch->buffer = b;
+ ch->buffer->bufsize = AU_BUFFSIZE;
+ if (chn_allocbuf(ch->buffer, au->parent_dmat) == -1) return NULL;
+ return ch;
+}
+
+static int
+auchan_setdir(void *data, int dir)
+{
+ struct au_chinfo *ch = data;
+ if (dir == PCMDIR_PLAY) {
+ } else {
+ }
+ ch->dir = dir;
+ return 0;
+}
+
+static int
+auchan_setformat(void *data, u_int32_t format)
+{
+ struct au_chinfo *ch = data;
+
+ if (ch->dir == PCMDIR_PLAY) au_prepareoutput(ch, format);
+ return 0;
+}
+
+static int
+auchan_setspeed(void *data, u_int32_t speed)
+{
+ struct au_chinfo *ch = data;
+ if (ch->dir == PCMDIR_PLAY) {
+ } else {
+ }
+ return speed;
+}
+
+static int
+auchan_setblocksize(void *data, u_int32_t blocksize)
+{
+ return blocksize;
+}
+
+static int
+auchan_trigger(void *data, int go)
+{
+ struct au_chinfo *ch = data;
+ struct au_info *au = ch->parent;
+ if (ch->dir == PCMDIR_PLAY) {
+ au_setadb(au, 0x11, (go)? 1 : 0);
+ if (!go) {
+ au_wr(au, 0, 0xf800, 0, 4);
+ au_wr(au, 0, 0xf804, 0, 4);
+ au_delroute(au, 0x58);
+ au_delroute(au, 0x59);
+ }
+ } else {
+ }
+ return 0;
+}
+
+static int
+auchan_getptr(void *data)
+{
+ struct au_chinfo *ch = data;
+ struct au_info *au = ch->parent;
+ if (ch->dir == PCMDIR_PLAY) {
+ return au_rd(au, 0, AU_REG_UNK2, 4) & (AU_BUFFSIZE-1);
+ } else {
+ return 0;
+ }
+}
+
+static pcmchan_caps *
+auchan_getcaps(void *data)
+{
+ struct au_chinfo *ch = data;
+ return (ch->dir == PCMDIR_PLAY)? &au_playcaps : &au_reccaps;
+}
+
+/* The interrupt handler */
+static void
+au_intr (void *p)
+{
+ struct au_info *au = p;
+ u_int32_t intsrc, i;
+
+ au->interrupts++;
+ intsrc=au_rd(au, 0, AU_REG_IRQSRC, 4);
+ printf("pcm%d: interrupt with src %x\n", au->unit, intsrc);
+ if (intsrc & AU_IRQ_FATAL) printf("pcm%d: fatal error irq\n", au->unit);
+ if (intsrc & AU_IRQ_PARITY) printf("pcm%d: parity error irq\n", au->unit);
+ if (intsrc & AU_IRQ_UNKNOWN) {
+ (void)au_rd(au, 0, AU_REG_UNK1, 4);
+ au_wr(au, 0, AU_REG_UNK1, 0, 4);
+ au_wr(au, 0, AU_REG_UNK1, 0x10000, 4);
+ }
+ if (intsrc & AU_IRQ_PCMOUT) {
+ i=au_rd(au, 0, AU_REG_UNK2, 4) & (AU_BUFFSIZE-1);
+ chn_intr(au->pch.channel);
+ (void)au_rd(au, 0, AU_REG_UNK3, 4);
+ (void)au_rd(au, 0, AU_REG_UNK4, 4);
+ (void)au_rd(au, 0, AU_REG_UNK5, 4);
+ }
+/* don't support midi
+ if (intsrc & AU_IRQ_MIDI) {
+ i=au_rd(au, 0, 0x11004, 4);
+ j=10;
+ while (i & 0xff) {
+ if (j-- <= 0) break;
+ i=au_rd(au, 0, 0x11000, 4);
+ if ((au->midi_stat & 1) && (au->midi_out))
+ au->midi_out(au->midi_devno, i);
+ i=au_rd(au, 0, 0x11004);
+ }
+ }
+*/
+ au_wr(au, 0, AU_REG_IRQSRC, intsrc & 0x7ff, 4);
+ au_rd(au, 0, AU_REG_IRQSRC, 4);
+}
+
+
+/* -------------------------------------------------------------------- */
+
+/* Probe and attach the card */
+
+static int
+au_init(device_t dev, struct au_info *au)
+{
+ u_int32_t i, j;
+
+ au_wr(au, 0, AU_REG_IRQGLOB, 0xffffffff, 4);
+ DELAY(100000);
+
+ /* init codec */
+ /* cold reset */
+ for (i=0; i<32; i++) {
+ au_wr(au, 0, AU_REG_CODECCHN+(i<<2), 0, 4);
+ DELAY(10000);
+ }
+ if (1) {
+ au_wr(au, 0, AU_REG_CODECST, 0x8068, 4);
+ DELAY(10000);
+ au_wr(au, 0, AU_REG_CODECST, 0x00e8, 4);
+ DELAY(10000);
+ } else {
+ au_wr(au, 0, AU_REG_CODECST, 0x00a8, 4);
+ DELAY(100000);
+ au_wr(au, 0, AU_REG_CODECST, 0x80a8, 4);
+ DELAY(100000);
+ au_wr(au, 0, AU_REG_CODECST, 0x80e8, 4);
+ DELAY(100000);
+ au_wr(au, 0, AU_REG_CODECST, 0x80a8, 4);
+ DELAY(100000);
+ au_wr(au, 0, AU_REG_CODECST, 0x00a8, 4);
+ DELAY(100000);
+ au_wr(au, 0, AU_REG_CODECST, 0x00e8, 4);
+ DELAY(100000);
+ }
+
+ /* init */
+ for (i=0; i<32; i++) {
+ au_wr(au, 0, AU_REG_CODECCHN+(i<<2), 0, 4);
+ DELAY(10000);
+ }
+ au_wr(au, 0, AU_REG_CODECST, 0xe8, 4);
+ DELAY(10000);
+ au_wr(au, 0, AU_REG_CODECEN, 0, 4);
+
+ /* setup codec */
+ i=j=0;
+ while (j<100 && (i & AU_CDC_READY)==0) {
+ i=au_rd(au, 0, AU_REG_CODECST, 4);
+ DELAY(1000);
+ j++;
+ }
+ if (j==100) device_printf(dev, "codec not ready, status 0x%x\n", i);
+
+ /* init adb */
+ /*au->x5c=0;*/
+ for (i=0; i<32; i++) au->x[i]=i+0x67;
+ for (i=0; i<128; i++) au->y[i]=0x7f;
+ for (i=0; i<128; i++) au->z[i]=0x1f;
+ au_wr(au, 0, AU_REG_ADB, 0, 4);
+ for (i=0; i<124; i++) au_wr(au, 0, AU_REG_RTBASE+(i<<2), 0xffffffff, 4);
+
+ /* test */
+ i=au_rd(au, 0, 0x107c0, 4);
+ if (i!=0xdeadbeef) device_printf(dev, "dma check failed: 0x%x\n", i);
+
+ /* install mixer */
+ au_wr(au, 0, AU_REG_IRQGLOB,
+ au_rd(au, 0, AU_REG_IRQGLOB, 4) | AU_IRQ_ENABLE, 4);
+ /* braindead but it's what the oss/linux driver does
+ * for (i=0; i<0x80000000; i++) au_wr(au, 0, i<<2, 0, 4);
+ */
+ au->routes[0]=au->routes[1]=au->routes[2]=au->routes[3]=0;
+ /*au->x1e4=0;*/
+
+ /* attach channel */
+ au_addroute(au, 0x11, 0x48, 0x02);
+ au_addroute(au, 0x11, 0x49, 0x03);
+ au_encodec(au, 0);
+ au_encodec(au, 1);
+
+ for (i=0; i<48; i++) au_wr(au, 0, 0xf800+(i<<2), 0x20, 4);
+ for (i=2; i<6; i++) au_wr(au, 0, 0xf800+(i<<2), 0, 4);
+ au_wr(au, 0, 0xf8c0, 0x0843, 4);
+ for (i=0; i<4; i++) au_clrfifo(au, i);
+
+ return (0);
+}
+
+static int
+au_testirq(struct au_info *au)
+{
+ au_wr(au, 0, AU_REG_UNK1, 0x80001000, 4);
+ au_wr(au, 0, AU_REG_IRQEN, 0x00001030, 4);
+ au_wr(au, 0, AU_REG_IRQSRC, 0x000007ff, 4);
+ DELAY(1000000);
+ if (au->interrupts==0) printf("pcm%d: irq test failed\n", au->unit);
+ /* this apparently generates an irq */
+ return 0;
+}
+
+static int
+au_pci_probe(device_t dev)
+{
+ if (pci_get_devid(dev) == AU8820_PCI_ID) {
+ device_set_desc(dev, "Aureal Vortex 8820");
+ return 0;
+ }
+
+ return ENXIO;
+}
+
+static int
+au_pci_attach(device_t dev)
+{
+ snddev_info *d;
+ u_int32_t data;
+ struct au_info *au;
+ int type[10];
+ int regid[10];
+ struct resource *reg[10];
+ int i, j, mapped = 0;
+ int irqid;
+ struct resource *irq = 0;
+ void *ih = 0;
+ struct ac97_info *codec;
+ char status[SND_STATUSLEN];
+
+ d = device_get_softc(dev);
+ if ((au = malloc(sizeof(*au), M_DEVBUF, M_NOWAIT)) == NULL) {
+ device_printf(dev, "cannot allocate softc\n");
+ return ENXIO;
+ }
+
+ bzero(au, sizeof(*au));
+ au->unit = device_get_unit(dev);
+
+ data = pci_read_config(dev, PCIR_COMMAND, 2);
+ data |= (PCIM_CMD_PORTEN|PCIM_CMD_MEMEN|PCIM_CMD_BUSMASTEREN);
+ pci_write_config(dev, PCIR_COMMAND, data, 2);
+ data = pci_read_config(dev, PCIR_COMMAND, 2);
+
+ j=0;
+ /* XXX dfr: is this strictly necessary? */
+ for (i=0; i<PCI_MAXMAPS_0; i++) {
+#if 0
+ /* Slapped wrist: config_id and map are private structures */
+ if (bootverbose) {
+ printf("pcm%d: map %d - allocating ", unit, i+1);
+ printf("0x%x bytes of ", 1<<config_id->map[i].ln2size);
+ printf("%s space ", (config_id->map[i].type & PCI_MAPPORT)?
+ "io" : "memory");
+ printf("at 0x%x...", config_id->map[i].base);
+ }
+#endif
+ regid[j] = PCIR_MAPS + i*4;
+ type[j] = SYS_RES_MEMORY;
+ reg[j] = bus_alloc_resource(dev, type[j], &regid[j],
+ 0, ~0, 1, RF_ACTIVE);
+ if (!reg[j]) {
+ type[j] = SYS_RES_IOPORT;
+ reg[j] = bus_alloc_resource(dev, type[j], &regid[j],
+ 0, ~0, 1, RF_ACTIVE);
+ }
+ if (reg[j]) {
+ au->st[i] = rman_get_bustag(reg[j]);
+ au->sh[i] = rman_get_bushandle(reg[j]);
+ mapped++;
+ }
+#if 0
+ if (bootverbose) printf("%s\n", mapped? "ok" : "failed");
+#endif
+ if (mapped) j++;
+ if (j == 10) {
+ /* XXX */
+ device_printf(dev, "too many resources");
+ goto bad;
+ }
+ }
+
+#if 0
+ if (j < config_id->nummaps) {
+ printf("pcm%d: unable to map a required resource\n", unit);
+ free(au, M_DEVBUF);
+ return;
+ }
+#endif
+
+ au_wr(au, 0, AU_REG_IRQEN, 0, 4);
+
+ irqid = 0;
+ irq = bus_alloc_resource(dev, SYS_RES_IRQ, &irqid,
+ 0, ~0, 1, RF_ACTIVE);
+ if (!irq
+ || bus_setup_intr(dev, irq, INTR_TYPE_TTY, au_intr, au, &ih)) {
+ device_printf(dev, "unable to map interrupt\n");
+ goto bad;
+ }
+
+ if (au_testirq(au)) device_printf(dev, "irq test failed\n");
+
+ if (au_init(dev, au) == -1) {
+ device_printf(dev, "unable to initialize the card\n");
+ goto bad;
+ }
+
+ codec = ac97_create(au, au_rdcd, au_wrcd);
+ if (codec == NULL) goto bad;
+ mixer_init(d, &ac97_mixer, codec);
+
+ if (bus_dma_tag_create(/*parent*/NULL, /*alignment*/2, /*boundary*/0,
+ /*lowaddr*/BUS_SPACE_MAXADDR_32BIT,
+ /*highaddr*/BUS_SPACE_MAXADDR,
+ /*filter*/NULL, /*filterarg*/NULL,
+ /*maxsize*/AU_BUFFSIZE, /*nsegments*/1, /*maxsegz*/0x3ffff,
+ /*flags*/0, &au->parent_dmat) != 0) {
+ device_printf(dev, "unable to create dma tag\n");
+ goto bad;
+ }
+
+ snprintf(status, SND_STATUSLEN, "at %s 0x%lx irq %ld",
+ (type[0] == SYS_RES_IOPORT)? "io" : "memory",
+ rman_get_start(reg[0]), rman_get_start(irq));
+
+ if (pcm_register(dev, au, 1, 1)) goto bad;
+ /* pcm_addchan(dev, PCMDIR_REC, &au_chantemplate, au); */
+ pcm_addchan(dev, PCMDIR_PLAY, &au_chantemplate, au);
+ pcm_setstatus(dev, status);
+
+ return 0;
+
+ bad:
+ if (au) free(au, M_DEVBUF);
+ for (i = 0; i < j; i++)
+ bus_release_resource(dev, type[i], regid[i], reg[i]);
+ if (ih) bus_teardown_intr(dev, irq, ih);
+ if (irq) bus_release_resource(dev, SYS_RES_IRQ, irqid, irq);
+ return ENXIO;
+}
+
+static device_method_t au_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, au_pci_probe),
+ DEVMETHOD(device_attach, au_pci_attach),
+
+ { 0, 0 }
+};
+
+static driver_t au_driver = {
+ "pcm",
+ au_methods,
+ sizeof(snddev_info),
+};
+
+static devclass_t pcm_devclass;
+
+DRIVER_MODULE(au, pci, au_driver, pcm_devclass, 0, 0);
+
+#endif /* NPCI != 0 */
+
diff --git a/sys/dev/sound/pci/aureal.h b/sys/dev/sound/pci/aureal.h
new file mode 100644
index 0000000..c5bcb29
--- /dev/null
+++ b/sys/dev/sound/pci/aureal.h
@@ -0,0 +1,99 @@
+/*
+ * Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk>
+ * 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$
+ */
+
+#ifndef _AU8820_REG_H
+#define _AU8820_REG_H
+
+#define AU_BUFFSIZE 0x4000
+
+#define AU_REG_FIFOBASE 0x0e000
+
+#define AU_REG_UNK2 0x105c0
+#define AU_REG_UNK3 0x10600
+#define AU_REG_UNK4 0x10604
+#define AU_REG_UNK5 0x10608
+
+#define AU_REG_RTBASE 0x10800
+
+#define AU_REG_ADB 0x10a00
+
+#define AU_REG_CODECCHN 0x11880
+
+#define AU_REG_CODECST 0x11984
+#define AU_CDC_RUN 0x00000040
+#define AU_CDC_WROK 0x00000100
+#define AU_CDC_RESET 0x00008000
+
+#define AU_REG_CODECIO 0x11988
+#define AU_CDC_DATAMASK 0x0000ffff
+#define AU_CDC_REGMASK 0x007f0000
+#define AU_CDC_REGSET 0x00800000
+#define AU_CDC_READY 0x04000000
+
+#define AU_REG_CODECEN 0x11990
+#define AU_CDC_CHAN1EN 0x00000100
+#define AU_CDC_CHAN2EN 0x00000200
+
+#define AU_REG_UNK1 0x1199c
+
+#define AU_REG_IRQSRC 0x12800
+#define AU_IRQ_FATAL 0x0001
+#define AU_IRQ_PARITY 0x0002
+#define AU_IRQ_PCMOUT 0x0020
+#define AU_IRQ_UNKNOWN 0x1000
+#define AU_IRQ_MIDI 0x2000
+#define AU_REG_IRQEN 0x12804
+
+#define AU_REG_IRQGLOB 0x1280c
+#define AU_IRQ_ENABLE 0x4000
+
+#define AC97_MUTE 0x8000
+#define AC97_REG_RESET 0x00
+#define AC97_MIX_MASTER 0x02
+#define AC97_MIX_PHONES 0x04
+#define AC97_MIX_MONO 0x06
+#define AC97_MIX_TONE 0x08
+#define AC97_MIX_BEEP 0x0a
+#define AC97_MIX_PHONE 0x0c
+#define AC97_MIX_MIC 0x0e
+#define AC97_MIX_LINE 0x10
+#define AC97_MIX_CD 0x12
+#define AC97_MIX_VIDEO 0x14
+#define AC97_MIX_AUX 0x16
+#define AC97_MIX_PCM 0x18
+#define AC97_REG_RECSEL 0x1a
+#define AC97_MIX_RGAIN 0x1c
+#define AC97_MIX_MGAIN 0x1e
+#define AC97_REG_GEN 0x20
+#define AC97_REG_3D 0x22
+#define AC97_REG_POWER 0x26
+#define AC97_REG_ID1 0x7c
+#define AC97_REG_ID2 0x7e
+
+
+#endif
diff --git a/sys/dev/sound/pci/es137x.c b/sys/dev/sound/pci/es137x.c
new file mode 100644
index 0000000..f0f1413
--- /dev/null
+++ b/sys/dev/sound/pci/es137x.c
@@ -0,0 +1,543 @@
+/*
+ * Support the ENSONIQ AudioPCI board based on the ES1370 and Codec
+ * AK4531.
+ *
+ * Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk>
+ * Copyright (c) 1998 by Joachim Kuebart. 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.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ * software must display the following acknowledgement:
+ * This product includes software developed by Joachim Kuebart.
+ *
+ * 4. The name of the author may not be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``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 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: es1370.c,v 1.4 1999/05/09 17:06:45 peter Exp $
+ */
+
+#include "pci.h"
+#include "pcm.h"
+
+#include <dev/pcm/sound.h>
+#include <dev/pcm/pci/es1370.h>
+
+#include <pci/pcireg.h>
+#include <pci/pcivar.h>
+
+#if NPCI != 0
+
+#define MEM_MAP_REG 0x14
+
+/* PCI IDs of supported chips */
+#define ES1370_PCI_ID 0x50001274
+
+/* device private data */
+struct es_info;
+
+struct es_chinfo {
+ struct es_info *parent;
+ pcm_channel *channel;
+ snd_dbuf *buffer;
+ int dir;
+ u_int32_t fmt;
+};
+
+struct es_info {
+ bus_space_tag_t st;
+ bus_space_handle_t sh;
+ bus_dma_tag_t parent_dmat;
+
+ /* Contents of board's registers */
+ u_long ctrl;
+ u_long sctrl;
+ struct es_chinfo pch, rch;
+};
+
+/* -------------------------------------------------------------------- */
+
+/* prototypes */
+static int es_init(struct es_info *);
+static void es_intr(void *);
+static int write_codec(struct es_info *, u_char, u_char);
+
+/* channel interface */
+static void *eschan_init(void *devinfo, snd_dbuf *b, pcm_channel *c, int dir);
+static int eschan_setdir(void *data, int dir);
+static int eschan_setformat(void *data, u_int32_t format);
+static int eschan_setspeed(void *data, u_int32_t speed);
+static int eschan_setblocksize(void *data, u_int32_t blocksize);
+static int eschan_trigger(void *data, int go);
+static int eschan_getptr(void *data);
+static pcmchan_caps *eschan_getcaps(void *data);
+
+static pcmchan_caps es_playcaps = {
+ 4000, 48000,
+ AFMT_STEREO | AFMT_U8 | AFMT_S16_LE,
+ AFMT_STEREO | AFMT_S16_LE
+};
+
+static pcmchan_caps es_reccaps = {
+ 4000, 48000,
+ AFMT_STEREO | AFMT_U8 | AFMT_S16_LE,
+ AFMT_STEREO | AFMT_S16_LE
+};
+
+static pcm_channel es_chantemplate = {
+ eschan_init,
+ eschan_setdir,
+ eschan_setformat,
+ eschan_setspeed,
+ eschan_setblocksize,
+ eschan_trigger,
+ eschan_getptr,
+ eschan_getcaps,
+};
+
+/* -------------------------------------------------------------------- */
+
+/* The mixer interface */
+
+static int es_mixinit(snd_mixer *m);
+static int es_mixset(snd_mixer *m, unsigned dev, unsigned left, unsigned right);
+static int es_mixsetrecsrc(snd_mixer *m, u_int32_t src);
+
+static snd_mixer es_mixer = {
+ "Ensoniq AudioPCI 1370 mixer",
+ es_mixinit,
+ es_mixset,
+ es_mixsetrecsrc,
+};
+
+static const struct {
+ unsigned volidx:4;
+ unsigned left:4;
+ unsigned right:4;
+ unsigned stereo:1;
+ unsigned recmask:13;
+ unsigned avail:1;
+} mixtable[SOUND_MIXER_NRDEVICES] = {
+ [SOUND_MIXER_VOLUME] = { 0, 0x0, 0x1, 1, 0x0000, 1 },
+ [SOUND_MIXER_PCM] = { 1, 0x2, 0x3, 1, 0x0400, 1 },
+ [SOUND_MIXER_SYNTH] = { 2, 0x4, 0x5, 1, 0x0060, 1 },
+ [SOUND_MIXER_CD] = { 3, 0x6, 0x7, 1, 0x0006, 1 },
+ [SOUND_MIXER_LINE] = { 4, 0x8, 0x9, 1, 0x0018, 1 },
+ [SOUND_MIXER_LINE1] = { 5, 0xa, 0xb, 1, 0x1800, 1 },
+ [SOUND_MIXER_LINE2] = { 6, 0xc, 0x0, 0, 0x0100, 1 },
+ [SOUND_MIXER_LINE3] = { 7, 0xd, 0x0, 0, 0x0200, 1 },
+ [SOUND_MIXER_MIC] = { 8, 0xe, 0x0, 0, 0x0001, 1 },
+ [SOUND_MIXER_OGAIN] = { 9, 0xf, 0x0, 0, 0x0000, 1 } };
+
+static int
+es_mixinit(snd_mixer *m)
+{
+ int i;
+ u_int32_t v;
+
+ v = 0;
+ for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
+ if (mixtable[i].avail) v |= (1 << i);
+ mix_setdevs(m, v);
+ v = 0;
+ for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
+ if (mixtable[i].recmask) v |= (1 << i);
+ mix_setrecdevs(m, v);
+ return 0;
+}
+
+static int
+es_mixset(snd_mixer *m, unsigned dev, unsigned left, unsigned right)
+{
+ int l, r, rl, rr;
+
+ if (!mixtable[dev].avail) return -1;
+ l = left;
+ r = mixtable[dev].stereo? right : l;
+ if (mixtable[dev].left == 0xf) {
+ rl = (l < 2)? 0x80 : 7 - (l - 2) / 14;
+ } else {
+ rl = (l < 10)? 0x80 : 15 - (l - 10) / 6;
+ }
+ if (mixtable[dev].stereo) {
+ rr = (r < 10)? 0x80 : 15 - (r - 10) / 6;
+ write_codec(mix_getdevinfo(m), mixtable[dev].right, rr);
+ }
+ write_codec(mix_getdevinfo(m), mixtable[dev].left, rl);
+ return l | (r << 8);
+}
+
+static int
+es_mixsetrecsrc(snd_mixer *m, u_int32_t src)
+{
+ int i, j = 0;
+
+ if (src == 0) src = 1 << SOUND_MIXER_MIC;
+ src &= mix_getrecdevs(m);
+ for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
+ if ((src & (1 << i)) != 0) j |= mixtable[i].recmask;
+
+ write_codec(mix_getdevinfo(m), CODEC_LIMIX1, j & 0x55);
+ write_codec(mix_getdevinfo(m), CODEC_RIMIX1, j & 0xaa);
+ write_codec(mix_getdevinfo(m), CODEC_LIMIX2, (j >> 8) & 0x17);
+ write_codec(mix_getdevinfo(m), CODEC_RIMIX2, (j >> 8) & 0x0f);
+ write_codec(mix_getdevinfo(m), CODEC_OMIX1, 0x7f);
+ write_codec(mix_getdevinfo(m), CODEC_OMIX2, 0x3f);
+ return src;
+}
+
+static int
+write_codec(struct es_info *es, u_char i, u_char data)
+{
+ int wait = 100; /* 100 msec timeout */
+
+ do {
+ if ((bus_space_read_4(es->st, es->sh, ES1370_REG_STATUS) &
+ STAT_CSTAT) == 0) {
+ bus_space_write_2(es->st, es->sh, ES1370_REG_CODEC,
+ ((u_short)i << CODEC_INDEX_SHIFT) | data);
+ return 0;
+ }
+ DELAY(1000);
+ } while (--wait);
+ printf("pcm: write_codec timed out\n");
+ return -1;
+}
+
+/* -------------------------------------------------------------------- */
+
+/* channel interface */
+static void *
+eschan_init(void *devinfo, snd_dbuf *b, pcm_channel *c, int dir)
+{
+ struct es_info *es = devinfo;
+ struct es_chinfo *ch = (dir == PCMDIR_PLAY)? &es->pch : &es->rch;
+
+ ch->parent = es;
+ ch->channel = c;
+ ch->buffer = b;
+ ch->buffer->bufsize = ES_BUFFSIZE;
+ if (chn_allocbuf(ch->buffer, es->parent_dmat) == -1) return NULL;
+ return ch;
+}
+
+static int
+eschan_setdir(void *data, int dir)
+{
+ struct es_chinfo *ch = data;
+ struct es_info *es = ch->parent;
+
+ if (dir == PCMDIR_PLAY) {
+ bus_space_write_1(es->st, es->sh, ES1370_REG_MEMPAGE,
+ ES1370_REG_DAC2_FRAMEADR >> 8);
+ bus_space_write_4(es->st, es->sh, ES1370_REG_DAC2_FRAMEADR & 0xff,
+ vtophys(ch->buffer->buf));
+ bus_space_write_4(es->st, es->sh, ES1370_REG_DAC2_FRAMECNT & 0xff,
+ (ch->buffer->bufsize >> 2) - 1);
+ } else {
+ bus_space_write_1(es->st, es->sh, ES1370_REG_MEMPAGE,
+ ES1370_REG_ADC_FRAMEADR >> 8);
+ bus_space_write_4(es->st, es->sh, ES1370_REG_ADC_FRAMEADR & 0xff,
+ vtophys(ch->buffer->buf));
+ bus_space_write_4(es->st, es->sh, ES1370_REG_ADC_FRAMECNT & 0xff,
+ (ch->buffer->bufsize >> 2) - 1);
+ }
+ ch->dir = dir;
+ return 0;
+}
+
+static int
+eschan_setformat(void *data, u_int32_t format)
+{
+ struct es_chinfo *ch = data;
+ struct es_info *es = ch->parent;
+
+ if (ch->dir == PCMDIR_PLAY) {
+ es->sctrl &= ~SCTRL_P2FMT;
+ if (format & AFMT_S16_LE) es->sctrl |= SCTRL_P2SEB;
+ if (format & AFMT_STEREO) es->sctrl |= SCTRL_P2SMB;
+ } else {
+ es->sctrl &= ~SCTRL_R1FMT;
+ if (format & AFMT_S16_LE) es->sctrl |= SCTRL_R1SEB;
+ if (format & AFMT_STEREO) es->sctrl |= SCTRL_R1SMB;
+ }
+ bus_space_write_4(es->st, es->sh, ES1370_REG_SERIAL_CONTROL, es->sctrl);
+ ch->fmt = format;
+ return 0;
+}
+
+static int
+eschan_setspeed(void *data, u_int32_t speed)
+{
+ struct es_chinfo *ch = data;
+ struct es_info *es = ch->parent;
+
+ es->ctrl &= ~CTRL_PCLKDIV;
+ es->ctrl |= DAC2_SRTODIV(speed) << CTRL_SH_PCLKDIV;
+ bus_space_write_4(es->st, es->sh, ES1370_REG_CONTROL, es->ctrl);
+ /* rec/play speeds locked together - should indicate in flags */
+#if 0
+ if (ch->direction == PCMDIR_PLAY) d->rec[0].speed = speed;
+ else d->play[0].speed = speed;
+#endif
+ return speed; /* XXX calc real speed */
+}
+
+static int
+eschan_setblocksize(void *data, u_int32_t blocksize)
+{
+ return blocksize;
+}
+
+static int
+eschan_trigger(void *data, int go)
+{
+ struct es_chinfo *ch = data;
+ struct es_info *es = ch->parent;
+ unsigned cnt = ch->buffer->dl / ch->buffer->sample_size - 1;
+
+ if (ch->dir == PCMDIR_PLAY) {
+ if (go == PCMTRIG_START) {
+ int b = (ch->fmt & AFMT_S16_LE)? 2 : 1;
+ es->ctrl |= CTRL_DAC2_EN;
+ es->sctrl &= ~(SCTRL_P2ENDINC | SCTRL_P2STINC |
+ SCTRL_P2LOOPSEL | SCTRL_P2PAUSE |
+ SCTRL_P2DACSEN);
+ es->sctrl |= SCTRL_P2INTEN | (b << SCTRL_SH_P2ENDINC);
+ bus_space_write_4(es->st, es->sh,
+ ES1370_REG_DAC2_SCOUNT, cnt);
+ } else es->ctrl &= ~CTRL_DAC2_EN;
+ } else {
+ if (go == PCMTRIG_START) {
+ es->ctrl |= CTRL_ADC_EN;
+ es->sctrl &= ~SCTRL_R1LOOPSEL;
+ es->sctrl |= SCTRL_R1INTEN;
+ bus_space_write_4(es->st, es->sh,
+ ES1370_REG_ADC_SCOUNT, cnt);
+ } else es->ctrl &= ~CTRL_ADC_EN;
+ }
+ bus_space_write_4(es->st, es->sh, ES1370_REG_SERIAL_CONTROL, es->sctrl);
+ bus_space_write_4(es->st, es->sh, ES1370_REG_CONTROL, es->ctrl);
+ return 0;
+}
+
+static int
+eschan_getptr(void *data)
+{
+ struct es_chinfo *ch = data;
+ struct es_info *es = ch->parent;
+ if (ch->dir == PCMDIR_PLAY) {
+ bus_space_write_4(es->st, es->sh, ES1370_REG_MEMPAGE,
+ ES1370_REG_DAC2_FRAMECNT >> 8);
+ return (bus_space_read_4(es->st, es->sh,
+ ES1370_REG_DAC2_FRAMECNT & 0xff) >> 14) & 0x3fffc;
+ } else {
+ bus_space_write_4(es->st, es->sh, ES1370_REG_MEMPAGE,
+ ES1370_REG_ADC_FRAMECNT >> 8);
+ return (bus_space_read_4(es->st, es->sh,
+ ES1370_REG_ADC_FRAMECNT & 0xff) >> 14) & 0x3fffc;
+ }
+}
+
+static pcmchan_caps *
+eschan_getcaps(void *data)
+{
+ struct es_chinfo *ch = data;
+ return (ch->dir == PCMDIR_PLAY)? &es_playcaps : &es_reccaps;
+}
+
+/* The interrupt handler */
+static void
+es_intr (void *p)
+{
+ struct es_info *es = p;
+ unsigned intsrc, sctrl;
+
+ intsrc = bus_space_read_4(es->st, es->sh, ES1370_REG_STATUS);
+ if ((intsrc & STAT_INTR) == 0) return;
+
+ sctrl = es->sctrl;
+ if (intsrc & STAT_ADC) sctrl &= ~SCTRL_R1INTEN;
+ if (intsrc & STAT_DAC1) sctrl &= ~SCTRL_P1INTEN;
+ if (intsrc & STAT_DAC2) sctrl &= ~SCTRL_P2INTEN;
+
+ bus_space_write_4(es->st, es->sh, ES1370_REG_SERIAL_CONTROL, sctrl);
+ bus_space_write_4(es->st, es->sh, ES1370_REG_SERIAL_CONTROL, es->sctrl);
+
+ if (intsrc & STAT_DAC2) chn_intr(es->pch.channel);
+ if (intsrc & STAT_ADC) chn_intr(es->rch.channel);
+}
+
+/* -------------------------------------------------------------------- */
+
+/*
+ * Probe and attach the card
+ */
+
+static int
+es_init(struct es_info *es)
+{
+ es->ctrl = CTRL_CDC_EN | CTRL_SERR_DIS |
+ (DAC2_SRTODIV(DSP_DEFAULT_SPEED) << CTRL_SH_PCLKDIV);
+ bus_space_write_4(es->st, es->sh, ES1370_REG_CONTROL, es->ctrl);
+
+ es->sctrl = 0;
+ bus_space_write_4(es->st, es->sh, ES1370_REG_SERIAL_CONTROL, es->sctrl);
+
+ write_codec(es, CODEC_RES_PD, 3);/* No RST, PD */
+ write_codec(es, CODEC_CSEL, 0); /* CODEC ADC and CODEC DAC use
+ * {LR,B}CLK2 and run off the LRCLK2
+ * PLL; program DAC_SYNC=0! */
+ write_codec(es, CODEC_ADSEL, 0);/* Recording source is mixer */
+ write_codec(es, CODEC_MGAIN, 0);/* MIC amp is 0db */
+
+ return 0;
+}
+
+static int
+es_pci_probe(device_t dev)
+{
+ if (pci_get_devid(dev) == ES1370_PCI_ID) {
+ device_set_desc(dev, "AudioPCI ES1370");
+ return 0;
+ }
+ return ENXIO;
+}
+
+static int
+es_pci_attach(device_t dev)
+{
+ snddev_info *d;
+ u_int32_t data;
+ struct es_info *es = 0;
+ int type = 0;
+ int regid;
+ struct resource *reg = 0;
+ int mapped;
+ int irqid;
+ struct resource *irq = 0;
+ void *ih = 0;
+ char status[SND_STATUSLEN];
+
+ d = device_get_softc(dev);
+ if ((es = malloc(sizeof *es, M_DEVBUF, M_NOWAIT)) == NULL) {
+ device_printf(dev, "cannot allocate softc\n");
+ return ENXIO;
+ }
+ bzero(es, sizeof *es);
+
+ mapped = 0;
+ data = pci_read_config(dev, PCIR_COMMAND, 2);
+ if (mapped == 0 && (data & PCIM_CMD_MEMEN)) {
+ regid = MEM_MAP_REG;
+ type = SYS_RES_MEMORY;
+ reg = bus_alloc_resource(dev, type, &regid,
+ 0, ~0, 1, RF_ACTIVE);
+ if (reg) {
+ es->st = rman_get_bustag(reg);
+ es->sh = rman_get_bushandle(reg);
+ mapped++;
+ }
+ }
+ if (mapped == 0 && (data & PCIM_CMD_PORTEN)) {
+ regid = PCI_MAP_REG_START;
+ type = SYS_RES_IOPORT;
+ reg = bus_alloc_resource(dev, type, &regid,
+ 0, ~0, 1, RF_ACTIVE);
+ if (reg) {
+ es->st = rman_get_bustag(reg);
+ es->sh = rman_get_bushandle(reg);
+ mapped++;
+ }
+ }
+ if (mapped == 0) {
+ device_printf(dev, "unable to map register space\n");
+ goto bad;
+ }
+
+ if (es_init(es) == -1) {
+ device_printf(dev, "unable to initialize the card\n");
+ goto bad;
+ }
+ mixer_init(d, &es_mixer, es);
+
+ irqid = 0;
+ irq = bus_alloc_resource(dev, SYS_RES_IRQ, &irqid,
+ 0, ~0, 1, RF_ACTIVE);
+ if (!irq
+ || bus_setup_intr(dev, irq, INTR_TYPE_TTY, es_intr, es, &ih)) {
+ device_printf(dev, "unable to map interrupt\n");
+ goto bad;
+ }
+
+ if (bus_dma_tag_create(/*parent*/NULL, /*alignment*/2, /*boundary*/0,
+ /*lowaddr*/BUS_SPACE_MAXADDR_32BIT,
+ /*highaddr*/BUS_SPACE_MAXADDR,
+ /*filter*/NULL, /*filterarg*/NULL,
+ /*maxsize*/ES_BUFFSIZE, /*nsegments*/1, /*maxsegz*/0x3ffff,
+ /*flags*/0, &es->parent_dmat) != 0) {
+ device_printf(dev, "unable to create dma tag\n");
+ goto bad;
+ }
+
+ snprintf(status, SND_STATUSLEN, "at %s 0x%lx irq %ld",
+ (type == SYS_RES_IOPORT)? "io" : "memory",
+ rman_get_start(reg), rman_get_start(irq));
+
+ if (pcm_register(dev, es, 1, 1)) goto bad;
+ pcm_addchan(dev, PCMDIR_REC, &es_chantemplate, es);
+ pcm_addchan(dev, PCMDIR_PLAY, &es_chantemplate, es);
+ pcm_setstatus(dev, status);
+
+ return 0;
+
+ bad:
+ if (es) free(es, M_DEVBUF);
+ if (reg) bus_release_resource(dev, type, regid, reg);
+ if (ih) bus_teardown_intr(dev, irq, ih);
+ if (irq) bus_release_resource(dev, SYS_RES_IRQ, irqid, irq);
+ return ENXIO;
+}
+
+static device_method_t es_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, es_pci_probe),
+ DEVMETHOD(device_attach, es_pci_attach),
+
+ { 0, 0 }
+};
+
+static driver_t es_driver = {
+ "pcm",
+ es_methods,
+ sizeof(snddev_info),
+};
+
+static devclass_t pcm_devclass;
+
+DRIVER_MODULE(es, pci, es_driver, pcm_devclass, 0, 0);
+
+#endif /* NPCI != 0 */
diff --git a/sys/dev/sound/pci/es137x.h b/sys/dev/sound/pci/es137x.h
new file mode 100644
index 0000000..6c981e3
--- /dev/null
+++ b/sys/dev/sound/pci/es137x.h
@@ -0,0 +1,134 @@
+/*
+ * This supports the ENSONIQ AudioPCI board based on the ES1370.
+ *
+ * Copyright (c) 1998 Joachim Kuebart <joki@kuebart.stuttgart.netsurf.de>
+ * 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 immediately at the beginning of the file, without modification,
+ * 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.
+ * 3. Absolutely no warranty of function or purpose is made by the author
+ * Joachim Kuebart.
+ * 4. Modifications may be freely made to this file if the above conditions
+ * are met.
+ *
+ * $Id: es1370_reg.h,v 1.1 1998/12/31 08:14:27 luigi Exp $
+ */
+
+#ifndef _ES1370_REG_H
+#define _ES1370_REG_H
+
+#define ES1370_REG_CONTROL 0x00
+#define ES1370_REG_STATUS 0x04
+#define ES1370_REG_UART_DATA 0x08
+#define ES1370_REG_UART_STATUS 0x09
+#define ES1370_REG_UART_CONTROL 0x09
+#define ES1370_REG_UART_TEST 0x0a
+#define ES1370_REG_MEMPAGE 0x0c
+#define ES1370_REG_CODEC 0x10
+#define CODEC_INDEX_SHIFT 8
+#define ES1370_REG_SERIAL_CONTROL 0x20
+#define ES1370_REG_DAC1_SCOUNT 0x24
+#define ES1370_REG_DAC2_SCOUNT 0x28
+#define ES1370_REG_ADC_SCOUNT 0x2c
+
+#define ES1370_REG_DAC1_FRAMEADR 0xc30
+#define ES1370_REG_DAC1_FRAMECNT 0xc34
+#define ES1370_REG_DAC2_FRAMEADR 0xc38
+#define ES1370_REG_DAC2_FRAMECNT 0xc3c
+#define ES1370_REG_ADC_FRAMEADR 0xd30
+#define ES1370_REG_ADC_FRAMECNT 0xd34
+
+#define DAC2_SRTODIV(x) (((1411200 + (x) / 2) / (x) - 2) & 0x1fff)
+#define DAC2_DIVTOSR(x) (1411200 / ((x) + 2))
+
+#define CTRL_ADC_STOP 0x80000000 /* 1 = ADC stopped */
+#define CTRL_XCTL1 0x40000000 /* SERR pin if enabled */
+#define CTRL_OPEN 0x20000000 /* no function, can be read and
+ * written */
+#define CTRL_PCLKDIV 0x1fff0000 /* ADC/DAC2 clock divider */
+#define CTRL_SH_PCLKDIV 16
+#define CTRL_MSFMTSEL 0x00008000 /* MPEG serial data fmt: 0 = Sony, 1
+ * = I2S */
+#define CTRL_M_SBB 0x00004000 /* DAC2 clock: 0 = PCLKDIV, 1 = MPEG */
+#define CTRL_WTSRSEL 0x00003000 /* DAC1 clock freq: 0=5512, 1=11025,
+ * 2=22050, 3=44100 */
+#define CTRL_SH_WTSRSEL 12
+#define CTRL_DAC_SYNC 0x00000800 /* 1 = DAC2 runs off DAC1 clock */
+#define CTRL_CCB_INTRM 0x00000400 /* 1 = CCB "voice" ints enabled */
+#define CTRL_M_CB 0x00000200 /* recording source: 0 = ADC, 1 =
+ * MPEG */
+#define CTRL_XCTL0 0x00000100 /* 0 = Line in, 1 = Line out */
+#define CTRL_BREQ 0x00000080 /* 1 = test mode (internal mem test) */
+#define CTRL_DAC1_EN 0x00000040 /* enable DAC1 */
+#define CTRL_DAC2_EN 0x00000020 /* enable DAC2 */
+#define CTRL_ADC_EN 0x00000010 /* enable ADC */
+#define CTRL_UART_EN 0x00000008 /* enable MIDI uart */
+#define CTRL_JYSTK_EN 0x00000004 /* enable Joystick port (presumably
+ * at address 0x200) */
+#define CTRL_CDC_EN 0x00000002 /* enable serial (CODEC) interface */
+#define CTRL_SERR_DIS 0x00000001 /* 1 = disable PCI SERR signal */
+
+#define SCTRL_P2ENDINC 0x00380000 /* */
+#define SCTRL_SH_P2ENDINC 19
+#define SCTRL_P2STINC 0x00070000 /* */
+#define SCTRL_SH_P2STINC 16
+#define SCTRL_R1LOOPSEL 0x00008000 /* 0 = loop mode */
+#define SCTRL_P2LOOPSEL 0x00004000 /* 0 = loop mode */
+#define SCTRL_P1LOOPSEL 0x00002000 /* 0 = loop mode */
+#define SCTRL_P2PAUSE 0x00001000 /* 1 = pause mode */
+#define SCTRL_P1PAUSE 0x00000800 /* 1 = pause mode */
+#define SCTRL_R1INTEN 0x00000400 /* enable interrupt */
+#define SCTRL_P2INTEN 0x00000200 /* enable interrupt */
+#define SCTRL_P1INTEN 0x00000100 /* enable interrupt */
+#define SCTRL_P1SCTRLD 0x00000080 /* reload sample count register for
+ * DAC1 */
+#define SCTRL_P2DACSEN 0x00000040 /* 1 = DAC2 play back last sample
+ * when disabled */
+#define SCTRL_R1SEB 0x00000020 /* 1 = 16bit */
+#define SCTRL_R1SMB 0x00000010 /* 1 = stereo */
+#define SCTRL_R1FMT 0x00000030 /* format mask */
+#define SCTRL_SH_R1FMT 4
+#define SCTRL_P2SEB 0x00000008 /* 1 = 16bit */
+#define SCTRL_P2SMB 0x00000004 /* 1 = stereo */
+#define SCTRL_P2FMT 0x0000000c /* format mask */
+#define SCTRL_SH_P2FMT 2
+#define SCTRL_P1SEB 0x00000002 /* 1 = 16bit */
+#define SCTRL_P1SMB 0x00000001 /* 1 = stereo */
+#define SCTRL_P1FMT 0x00000003 /* format mask */
+#define SCTRL_SH_P1FMT 0
+
+#define STAT_INTR 0x80000000 /* wired or of all interrupt bits */
+#define STAT_CSTAT 0x00000400 /* 1 = codec busy or codec write in
+ * progress */
+#define STAT_CBUSY 0x00000200 /* 1 = codec busy */
+#define STAT_CWRIP 0x00000100 /* 1 = codec write in progress */
+#define STAT_VC 0x00000060 /* CCB int source, 0=DAC1, 1=DAC2,
+ * 2=ADC, 3=undef */
+#define STAT_SH_VC 5
+#define STAT_MCCB 0x00000010 /* CCB int pending */
+#define STAT_UART 0x00000008 /* UART int pending */
+#define STAT_DAC1 0x00000004 /* DAC1 int pending */
+#define STAT_DAC2 0x00000002 /* DAC2 int pending */
+#define STAT_ADC 0x00000001 /* ADC int pending */
+
+#define CODEC_OMIX1 0x10
+#define CODEC_OMIX2 0x11
+#define CODEC_LIMIX1 0x12
+#define CODEC_RIMIX1 0x13
+#define CODEC_LIMIX2 0x14
+#define CODEC_RIMIX2 0x15
+#define CODEC_RES_PD 0x16
+#define CODEC_CSEL 0x17
+#define CODEC_ADSEL 0x18
+#define CODEC_MGAIN 0x19
+
+#define ES_BUFFSIZE 0x20000 /* We're PCI! Use a large buffer */
+
+#endif
diff --git a/sys/dev/sound/pci/t4dwave.c b/sys/dev/sound/pci/t4dwave.c
new file mode 100644
index 0000000..5404fe2
--- /dev/null
+++ b/sys/dev/sound/pci/t4dwave.c
@@ -0,0 +1,688 @@
+/*
+ * Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk>
+ * 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, WHETHERIN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THEPOSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id$
+ */
+
+#include "pci.h"
+#include "pcm.h"
+
+#include <dev/pcm/sound.h>
+#include <dev/pcm/ac97.h>
+#include <dev/pcm/pci/t4dwave.h>
+
+#include <pci/pcireg.h>
+#include <pci/pcivar.h>
+
+#if NPCI != 0
+
+/* -------------------------------------------------------------------- */
+
+struct tr_info;
+
+/* channel registers */
+struct tr_chinfo {
+ u_int32_t cso, alpha, fms, fmc, ec;
+ u_int32_t lba;
+ u_int32_t eso, delta;
+ u_int32_t rvol, cvol;
+ u_int32_t gvsel, pan, vol, ctrl;
+ int index;
+ snd_dbuf *buffer;
+ pcm_channel *channel;
+ struct tr_info *parent;
+};
+
+/* device private data */
+struct tr_info {
+ u_int32_t type;
+
+ bus_space_tag_t st;
+ bus_space_handle_t sh;
+ bus_dma_tag_t parent_dmat;
+
+ struct resource *reg, *irq;
+ int regtype, regid, irqid;
+ void *ih;
+
+ u_int32_t playchns;
+ struct tr_chinfo chinfo[TR_MAXPLAYCH];
+ struct tr_chinfo recchinfo;
+};
+
+/* -------------------------------------------------------------------- */
+
+/*
+ * prototypes
+ */
+
+/* channel interface */
+static void *trchan_init(void *devinfo, snd_dbuf *b, pcm_channel *c, int dir);
+static int trchan_setdir(void *data, int dir);
+static int trchan_setformat(void *data, u_int32_t format);
+static int trchan_setspeed(void *data, u_int32_t speed);
+static int trchan_setblocksize(void *data, u_int32_t blocksize);
+static int trchan_trigger(void *data, int go);
+static int trchan_getptr(void *data);
+static pcmchan_caps *trchan_getcaps(void *data);
+
+/* talk to the codec - called from ac97.c */
+static u_int32_t tr_rdcd(void *, int);
+static void tr_wrcd(void *, int, u_int32_t);
+
+/* stuff */
+static int tr_init(struct tr_info *);
+static void tr_intr(void *);
+
+/* talk to the card */
+static u_int32_t tr_rd(struct tr_info *, int, int);
+static void tr_wr(struct tr_info *, int, u_int32_t, int);
+
+/* manipulate playback channels */
+static void tr_clrint(struct tr_info *, char);
+static void tr_enaint(struct tr_info *, char, int);
+static u_int32_t tr_testint(struct tr_info *, char);
+static void tr_rdch(struct tr_info *, char, struct tr_chinfo *);
+static void tr_wrch(struct tr_info *, char, struct tr_chinfo *);
+static void tr_selch(struct tr_info *, char);
+static void tr_startch(struct tr_info *, char);
+static void tr_stopch(struct tr_info *, char);
+
+/* -------------------------------------------------------------------- */
+
+static pcmchan_caps tr_reccaps = {
+ 4000, 48000,
+ AFMT_STEREO | AFMT_U8 | AFMT_S8 | AFMT_S16_LE | AFMT_U16_LE,
+ AFMT_STEREO | AFMT_S16_LE
+};
+
+static pcmchan_caps tr_playcaps = {
+ 4000, 48000,
+ AFMT_STEREO | AFMT_U8 | AFMT_S8 | AFMT_S16_LE | AFMT_U16_LE,
+ AFMT_U16_LE
+};
+
+static pcm_channel tr_chantemplate = {
+ trchan_init,
+ trchan_setdir,
+ trchan_setformat,
+ trchan_setspeed,
+ trchan_setblocksize,
+ trchan_trigger,
+ trchan_getptr,
+ trchan_getcaps,
+};
+
+/* -------------------------------------------------------------------- */
+
+static u_int32_t
+tr_fmttobits(u_int32_t fmt)
+{
+ u_int32_t bits = 0;
+ bits |= (fmt & AFMT_STEREO)? 0x4 : 0;
+ bits |= (fmt & (AFMT_S8 | AFMT_S16_LE))? 0x2 : 0;
+ bits |= (fmt & (AFMT_S16_LE | AFMT_U16_LE))? 0x8 : 0;
+ return bits;
+}
+
+/* Hardware */
+
+static u_int32_t
+tr_rd(struct tr_info *tr, int regno, int size)
+{
+ switch(size) {
+ case 1:
+ return bus_space_read_1(tr->st, tr->sh, regno);
+ case 2:
+ return bus_space_read_2(tr->st, tr->sh, regno);
+ case 4:
+ return bus_space_read_4(tr->st, tr->sh, regno);
+ default:
+ return 0xffffffff;
+ }
+}
+
+static void
+tr_wr(struct tr_info *tr, int regno, u_int32_t data, int size)
+{
+ switch(size) {
+ case 1:
+ bus_space_write_1(tr->st, tr->sh, regno, data);
+ break;
+ case 2:
+ bus_space_write_2(tr->st, tr->sh, regno, data);
+ break;
+ case 4:
+ bus_space_write_4(tr->st, tr->sh, regno, data);
+ break;
+ }
+}
+
+/* ac97 codec */
+
+static u_int32_t
+tr_rdcd(void *devinfo, int regno)
+{
+ struct tr_info *tr = (struct tr_info *)devinfo;
+ int i, j, treg, trw;
+
+ switch (tr->type) {
+ case TDX_PCI_ID:
+ treg=TDX_REG_CODECRD;
+ trw=TDX_CDC_RWSTAT;
+ break;
+ case TNX_PCI_ID:
+ treg=(regno & 0x100)? TNX_REG_CODEC2RD : TNX_REG_CODEC1RD;
+ trw=TNX_CDC_RWSTAT;
+ break;
+ default:
+ printf("!!! tr_rdcd defaulted !!!\n");
+ return 0xffffffff;
+ }
+
+ regno &= 0x7f;
+ tr_wr(tr, treg, regno | trw, 4);
+ j=trw;
+ for (i=TR_TIMEOUT_CDC; (i > 0) && (j & trw); i--) j=tr_rd(tr, treg, 4);
+ if (i == 0) printf("codec timeout during read of register %x\n", regno);
+ return (j >> TR_CDC_DATA) & 0xffff;
+}
+
+static void
+tr_wrcd(void *devinfo, int regno, u_int32_t data)
+{
+ struct tr_info *tr = (struct tr_info *)devinfo;
+ int i, j, treg, trw;
+
+ switch (tr->type) {
+ case TDX_PCI_ID:
+ treg=TDX_REG_CODECWR;
+ trw=TDX_CDC_RWSTAT;
+ break;
+ case TNX_PCI_ID:
+ treg=TNX_REG_CODECWR;
+ trw=TNX_CDC_RWSTAT | ((regno & 0x100)? TNX_CDC_SEC : 0);
+ break;
+ default:
+ printf("!!! tr_wrcd defaulted !!!");
+ return;
+ }
+
+ regno &= 0x7f;
+#if 0
+ printf("tr_wrcd: reg %x was %x", regno, tr_rdcd(devinfo, regno));
+#endif
+ j=trw;
+ for (i=TR_TIMEOUT_CDC; (i>0) && (j & trw); i--) j=tr_rd(tr, treg, 4);
+ tr_wr(tr, treg, (data << TR_CDC_DATA) | regno | trw, 4);
+#if 0
+ printf(" - wrote %x, now %x\n", data, tr_rdcd(devinfo, regno));
+#endif
+ if (i==0) printf("codec timeout writing %x, data %x\n", regno, data);
+}
+
+/* playback channel interrupts */
+
+static u_int32_t
+tr_testint(struct tr_info *tr, char channel)
+{
+ return tr_rd(tr, (channel & 0x20)? TR_REG_ADDRINTB : TR_REG_ADDRINTA,
+ 4) & (1<<(channel & 0x1f));
+}
+
+static void
+tr_clrint(struct tr_info *tr, char channel)
+{
+ tr_wr(tr, (channel & 0x20)? TR_REG_ADDRINTB : TR_REG_ADDRINTA,
+ 1<<(channel & 0x1f), 4);
+}
+
+static void
+tr_enaint(struct tr_info *tr, char channel, int enable)
+{
+ u_int32_t reg = (channel & 0x20)? TR_REG_INTENB : TR_REG_INTENA;
+ u_int32_t i = tr_rd(tr, reg, 4);
+ channel &= 0x1f;
+ i &= ~(1 << channel);
+ i |= (enable? 1 : 0) << channel;
+ tr_clrint(tr, channel);
+ tr_wr(tr, reg, i, 4);
+}
+
+/* playback channels */
+
+static void
+tr_selch(struct tr_info *tr, char channel)
+{
+ int i=tr_rd(tr, TR_REG_CIR, 4);
+ i &= ~TR_CIR_MASK;
+ i |= channel & 0x3f;
+ tr_wr(tr, TR_REG_CIR, i, 4);
+}
+
+static void
+tr_startch(struct tr_info *tr, char channel)
+{
+ tr_wr(tr, (channel & 0x20)? TR_REG_STARTB : TR_REG_STARTA,
+ 1<<(channel & 0x1f), 4);
+}
+
+static void
+tr_stopch(struct tr_info *tr, char channel)
+{
+ tr_wr(tr, (channel & 0x20)? TR_REG_STOPB : TR_REG_STOPA,
+ 1<<(channel & 0x1f), 4);
+}
+
+static void
+tr_wrch(struct tr_info *tr, char channel, struct tr_chinfo *ch)
+{
+ u_int32_t cr[TR_CHN_REGS], i;
+
+ ch->gvsel &= 0x00000001;
+ ch->fmc &= 0x00000003;
+ ch->fms &= 0x0000000f;
+ ch->ctrl &= 0x0000000f;
+ ch->pan &= 0x0000007f;
+ ch->rvol &= 0x0000007f;
+ ch->cvol &= 0x0000007f;
+ ch->vol &= 0x000000ff;
+ ch->ec &= 0x00000fff;
+ ch->alpha &= 0x00000fff;
+ ch->delta &= 0x0000ffff;
+ ch->lba &= 0x3fffffff;
+
+ cr[1]=ch->lba;
+ cr[3]=(ch->rvol<<7) | (ch->cvol);
+ cr[4]=(ch->gvsel<<31)|(ch->pan<<24)|(ch->vol<<16)|(ch->ctrl<<12)|(ch->ec);
+
+ switch (tr->type) {
+ case TDX_PCI_ID:
+ ch->cso &= 0x0000ffff;
+ ch->eso &= 0x0000ffff;
+ cr[0]=(ch->cso<<16) | (ch->alpha<<4) | (ch->fms);
+ cr[2]=(ch->eso<<16) | (ch->delta);
+ cr[3]|=0x0000c000;
+ break;
+ case TNX_PCI_ID:
+ ch->cso &= 0x00ffffff;
+ ch->eso &= 0x00ffffff;
+ cr[0]=((ch->delta & 0xff)<<24) | (ch->cso);
+ cr[2]=((ch->delta>>16)<<24) | (ch->eso);
+ cr[3]|=(ch->alpha<<20) | (ch->fms<<16) | (ch->fmc<<14);
+ break;
+ }
+ tr_selch(tr, channel);
+ for (i=0; i<TR_CHN_REGS; i++)
+ tr_wr(tr, TR_REG_CHNBASE+(i<<2), cr[i], 4);
+}
+
+static void
+tr_rdch(struct tr_info *tr, char channel, struct tr_chinfo *ch)
+{
+ u_int32_t cr[5], i;
+ tr_selch(tr, channel);
+ for (i=0; i<5; i++) cr[i]=tr_rd(tr, TR_REG_CHNBASE+(i<<2), 4);
+ ch->lba= (cr[1] & 0x3fffffff);
+ ch->fmc= (cr[3] & 0x0000c000) >> 14;
+ ch->rvol= (cr[3] & 0x00003f80) >> 7;
+ ch->cvol= (cr[3] & 0x0000007f);
+ ch->gvsel= (cr[4] & 0x80000000) >> 31;
+ ch->pan= (cr[4] & 0x7f000000) >> 24;
+ ch->vol= (cr[4] & 0x00ff0000) >> 16;
+ ch->ctrl= (cr[4] & 0x0000f000) >> 12;
+ ch->ec= (cr[4] & 0x00000fff);
+ switch(tr->type) {
+ case TDX_PCI_ID:
+ ch->cso= (cr[0] & 0xffff0000) >> 16;
+ ch->alpha= (cr[0] & 0x0000fff0) >> 4;
+ ch->fms= (cr[0] & 0x0000000f);
+ ch->eso= (cr[2] & 0xffff0000) >> 16;
+ ch->delta= (cr[2] & 0x0000ffff);
+ break;
+ case TNX_PCI_ID:
+ ch->cso= (cr[0] & 0x00ffffff);
+ ch->eso= (cr[2] & 0x00ffffff);
+ ch->delta= ((cr[2] & 0xff000000) >> 16) |
+ ((cr[0] & 0xff000000) >> 24);
+ ch->alpha= (cr[3] & 0xfff00000) >> 20;
+ ch->fms= (cr[3] & 0x000f0000) >> 16;
+ break;
+ }
+}
+
+/* channel interface */
+
+void *
+trchan_init(void *devinfo, snd_dbuf *b, pcm_channel *c, int dir)
+{
+ struct tr_info *tr = devinfo;
+ struct tr_chinfo *ch;
+ if (dir == PCMDIR_PLAY) {
+ ch = &tr->chinfo[tr->playchns];
+ ch->index = tr->playchns++;
+ } else {
+ ch = &tr->recchinfo;
+ ch->index = -1;
+ }
+ ch->buffer = b;
+ ch->buffer->bufsize = TR_BUFFSIZE;
+ ch->parent = tr;
+ ch->channel = c;
+ if (chn_allocbuf(ch->buffer, tr->parent_dmat) == -1) return NULL;
+ else return ch;
+}
+
+static int
+trchan_setdir(void *data, int dir)
+{
+ struct tr_chinfo *ch = data;
+ struct tr_info *tr = ch->parent;
+ if (dir == PCMDIR_PLAY && ch->index >= 0) {
+ ch->fmc = ch->fms = ch->ec = ch->alpha = 0;
+ ch->lba = vtophys(ch->buffer->buf);
+ ch->cso = 0;
+ ch->eso = ch->buffer->bufsize - 1;
+ ch->rvol = ch->cvol = 0;
+ ch->gvsel = 0;
+ ch->pan = 0;
+ ch->vol = 0;
+ ch->ctrl = 0x01;
+ ch->delta = 0;
+ tr_wrch(tr, ch->index, ch);
+ tr_enaint(tr, ch->index, 1);
+ } else if (dir == PCMDIR_REC && ch->index == -1) {
+ /* set up dma mode regs */
+ u_int32_t i;
+ tr_wr(tr, TR_REG_DMAR15, 0, 1);
+ i = tr_rd(tr, TR_REG_DMAR11, 1) & 0x03;
+ tr_wr(tr, TR_REG_DMAR11, i | 0x54, 1);
+ /* set up base address */
+ tr_wr(tr, TR_REG_DMAR0, vtophys(ch->buffer->buf), 4);
+ /* set up buffer size */
+ i = tr_rd(tr, TR_REG_DMAR4, 4) & ~0x00ffffff;
+ tr_wr(tr, TR_REG_DMAR4, i | (ch->buffer->bufsize - 1), 4);
+ } else return -1;
+ return 0;
+}
+
+static int
+trchan_setformat(void *data, u_int32_t format)
+{
+ struct tr_chinfo *ch = data;
+ struct tr_info *tr = ch->parent;
+ u_int32_t bits = tr_fmttobits(format);
+
+ if (ch->index >= 0) {
+ tr_rdch(tr, ch->index, ch);
+ ch->eso = (ch->buffer->bufsize / ch->buffer->sample_size) - 1;
+ ch->ctrl = bits | 0x01;
+ tr_wrch(tr, ch->index, ch);
+ } else {
+ u_int32_t i;
+ /* set # of samples between interrupts */
+ i = (TR_INTSAMPLES >> ((bits & 0x08)? 1 : 0)) - 1;
+ tr_wr(tr, TR_REG_SBBL, i | (i << 16), 4);
+ /* set sample format */
+ i = 0x18 | (bits << 4);
+ tr_wr(tr, TR_REG_SBCTRL, i, 1);
+ }
+ return 0;
+}
+
+static int
+trchan_setspeed(void *data, u_int32_t speed)
+{
+ struct tr_chinfo *ch = data;
+ struct tr_info *tr = ch->parent;
+
+ if (ch->index >= 0) {
+ tr_rdch(tr, ch->index, ch);
+ ch->delta = (speed << 12) / 48000;
+ tr_wrch(tr, ch->index, ch);
+ return (ch->delta * 48000) >> 12;
+ } else {
+ /* setup speed */
+ ch->delta = (48000 << 12) / speed;
+ tr_wr(tr, TR_REG_SBDELTA, ch->delta, 2);
+ return (48000 << 12) / ch->delta;
+ }
+ return 0;
+}
+
+static int
+trchan_setblocksize(void *data, u_int32_t blocksize)
+{
+ struct tr_chinfo *ch = data;
+ return ch->buffer->bufsize / 2;
+}
+
+static int
+trchan_trigger(void *data, int go)
+{
+ struct tr_chinfo *ch = data;
+ struct tr_info *tr = ch->parent;
+ if (ch->index >= 0) {
+ if (go == PCMTRIG_START) tr_startch(tr, ch->index);
+ else tr_stopch(tr, ch->index);
+ } else {
+ u_int32_t i = tr_rd(tr, TR_REG_SBCTRL, 1) & ~7;
+ tr_wr(tr, TR_REG_SBCTRL, i | (go == PCMTRIG_START)? 1 : 0, 1);
+ }
+ return 0;
+}
+
+static int
+trchan_getptr(void *data)
+{
+ struct tr_chinfo *ch = data;
+ struct tr_info *tr = ch->parent;
+ if (ch->index >= 0) {
+ tr_rdch(tr, ch->index, ch);
+ return ch->cso * ch->buffer->sample_size;
+ } else return tr_rd(tr, TR_REG_DMAR0, 4) - vtophys(ch->buffer->buf);
+}
+
+static pcmchan_caps *
+trchan_getcaps(void *data)
+{
+ struct tr_chinfo *ch = data;
+ return (ch->index >= 0)? &tr_playcaps : &tr_reccaps;
+}
+
+/* The interrupt handler */
+
+static void
+tr_intr(void *p)
+{
+ struct tr_info *tr = (struct tr_info *)p;
+ u_int32_t intsrc = tr_rd(tr, TR_REG_MISCINT, 4);
+
+ if (intsrc & TR_INT_ADDR) {
+ int i;
+ for (i = 0; i < tr->playchns; i++) {
+ if (tr_testint(tr, i)) {
+ chn_intr(tr->chinfo[i].channel);
+ tr_clrint(tr, i);
+ }
+ }
+ }
+ if (intsrc & TR_INT_SB) {
+ chn_intr(tr->recchinfo.channel);
+ tr_rd(tr, TR_REG_SBR9, 1);
+ tr_rd(tr, TR_REG_SBR10, 1);
+ }
+}
+
+/* -------------------------------------------------------------------- */
+
+/*
+ * Probe and attach the card
+ */
+
+static int
+tr_init(struct tr_info *tr)
+{
+ if (tr->type == TDX_PCI_ID) {
+ tr_wr(tr, TDX_REG_CODECST, TDX_CDC_ON, 4);
+ } else tr_wr(tr, TNX_REG_CODECST, TNX_CDC_ON, 4);
+
+ tr_wr(tr, TR_REG_CIR, TR_CIR_MIDENA | TR_CIR_ADDRENA, 4);
+ tr->playchns = 0;
+ return 0;
+}
+
+static int
+tr_pci_probe(device_t dev)
+{
+ if (pci_get_devid(dev) == TDX_PCI_ID) {
+ device_set_desc(dev, "Trident 4DWave DX");
+ return 0;
+ }
+ if (pci_get_devid(dev) == TNX_PCI_ID) {
+ device_set_desc(dev, "Trident 4DWave NX");
+ return 0;
+ }
+
+ return ENXIO;
+}
+
+static int
+tr_pci_attach(device_t dev)
+{
+ snddev_info *d;
+ u_int32_t data;
+ struct tr_info *tr;
+ struct ac97_info *codec;
+ int i;
+ int mapped;
+ char status[SND_STATUSLEN];
+
+ d = device_get_softc(dev);
+ if ((tr = malloc(sizeof(*tr), M_DEVBUF, M_NOWAIT)) == NULL) {
+ device_printf(dev, "cannot allocate softc\n");
+ return ENXIO;
+ }
+
+ bzero(tr, sizeof(*tr));
+ tr->type = pci_get_devid(dev);
+
+ data = pci_read_config(dev, PCIR_COMMAND, 2);
+ data |= (PCIM_CMD_PORTEN|PCIM_CMD_MEMEN|PCIM_CMD_BUSMASTEREN);
+ pci_write_config(dev, PCIR_COMMAND, data, 2);
+ data = pci_read_config(dev, PCIR_COMMAND, 2);
+
+ mapped = 0;
+ /* XXX dfr: is this strictly necessary? */
+ for (i = 0; (mapped == 0) && (i < PCI_MAXMAPS_0); i++) {
+ tr->regid = PCIR_MAPS + i*4;
+ tr->regtype = SYS_RES_MEMORY;
+ tr->reg = bus_alloc_resource(dev, tr->regtype, &tr->regid,
+ 0, ~0, 1, RF_ACTIVE);
+ if (!tr->reg) {
+ tr->regtype = SYS_RES_IOPORT;
+ tr->reg = bus_alloc_resource(dev, tr->regtype,
+ &tr->regid, 0, ~0, 1,
+ RF_ACTIVE);
+ }
+ if (tr->reg) {
+ tr->st = rman_get_bustag(tr->reg);
+ tr->sh = rman_get_bushandle(tr->reg);
+ mapped++;
+ }
+ }
+
+ if (mapped == 0) {
+ device_printf(dev, "unable to map register space\n");
+ goto bad;
+ }
+
+ if (tr_init(tr) == -1) {
+ device_printf(dev, "unable to initialize the card\n");
+ goto bad;
+ }
+
+ codec = ac97_create(tr, tr_rdcd, tr_wrcd);
+ if (codec == NULL) goto bad;
+ mixer_init(d, &ac97_mixer, codec);
+
+ tr->irqid = 0;
+ tr->irq = bus_alloc_resource(dev, SYS_RES_IRQ, &tr->irqid,
+ 0, ~0, 1, RF_ACTIVE | RF_SHAREABLE);
+ if (!tr->irq ||
+ bus_setup_intr(dev, tr->irq, INTR_TYPE_TTY, tr_intr, tr, &tr->ih)) {
+ device_printf(dev, "unable to map interrupt\n");
+ goto bad;
+ }
+
+ if (bus_dma_tag_create(/*parent*/NULL, /*alignment*/2, /*boundary*/0,
+ /*lowaddr*/BUS_SPACE_MAXADDR_32BIT,
+ /*highaddr*/BUS_SPACE_MAXADDR,
+ /*filter*/NULL, /*filterarg*/NULL,
+ /*maxsize*/TR_BUFFSIZE, /*nsegments*/1, /*maxsegz*/0x3ffff,
+ /*flags*/0, &tr->parent_dmat) != 0) {
+ device_printf(dev, "unable to create dma tag\n");
+ goto bad;
+ }
+
+ snprintf(status, 64, "at %s 0x%lx irq %ld",
+ (tr->regtype == SYS_RES_IOPORT)? "io" : "memory",
+ rman_get_start(tr->reg), rman_get_start(tr->irq));
+
+ if (pcm_register(dev, tr, TR_MAXPLAYCH, 1)) goto bad;
+ pcm_addchan(dev, PCMDIR_REC, &tr_chantemplate, tr);
+ for (i = 0; i < TR_MAXPLAYCH; i++)
+ pcm_addchan(dev, PCMDIR_PLAY, &tr_chantemplate, tr);
+ pcm_setstatus(dev, status);
+
+ return 0;
+
+bad:
+ if (tr->reg) bus_release_resource(dev, tr->regtype, tr->regid, tr->reg);
+ if (tr->ih) bus_teardown_intr(dev, tr->irq, tr->ih);
+ if (tr->irq) bus_release_resource(dev, SYS_RES_IRQ, tr->irqid, tr->irq);
+ free(tr, M_DEVBUF);
+ return ENXIO;
+}
+
+static device_method_t tr_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, tr_pci_probe),
+ DEVMETHOD(device_attach, tr_pci_attach),
+
+ { 0, 0 }
+};
+
+static driver_t tr_driver = {
+ "pcm",
+ tr_methods,
+ sizeof(snddev_info),
+};
+
+static devclass_t pcm_devclass;
+
+DRIVER_MODULE(tr, pci, tr_driver, pcm_devclass, 0, 0);
+
+#endif /* NPCI != 0 */
diff --git a/sys/dev/sound/pci/t4dwave.h b/sys/dev/sound/pci/t4dwave.h
new file mode 100644
index 0000000..31f3340
--- /dev/null
+++ b/sys/dev/sound/pci/t4dwave.h
@@ -0,0 +1,103 @@
+/*
+ * Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk>
+ * 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$
+ */
+
+#ifndef _T4DWAVE_REG_H
+#define _T4DWAVE_REG_H
+
+#define TDX_PCI_ID 0x20001023
+#define TNX_PCI_ID 0x20011023
+
+#define TR_BUFFSIZE 0x8000
+#define TR_TIMEOUT_CDC 0xffff
+#define TR_INTSAMPLES 0x2000
+#define TR_MAXPLAYCH 64
+
+#define TR_REG_CIR 0xa0
+#define TR_CIR_MASK 0x0000003f
+#define TR_CIR_ADDRENA 0x00001000
+#define TR_CIR_MIDENA 0x00002000
+#define TR_REG_MISCINT 0xb0
+#define TR_INT_ADDR 0x00000020
+#define TR_INT_SB 0x00000004
+
+#define TR_REG_DMAR0 0x00
+#define TR_REG_DMAR4 0x04
+#define TR_REG_DMAR11 0x0b
+#define TR_REG_DMAR15 0x0f
+#define TR_REG_SBR4 0x14
+#define TR_REG_SBR5 0x15
+#define TR_SB_INTSTATUS 0x82
+#define TR_REG_SBR9 0x1e
+#define TR_REG_SBR10 0x1f
+#define TR_REG_SBBL 0xc0
+#define TR_REG_SBCTRL 0xc4
+#define TR_REG_SBDELTA 0xac
+
+#define TR_CDC_DATA 16
+#define TDX_REG_CODECWR 0x40
+#define TDX_REG_CODECRD 0x44
+#define TDX_CDC_RWSTAT 0x00008000
+#define TDX_REG_CODECST 0x48
+#define TDX_CDC_SBCTRL 0x40
+#define TDX_CDC_ACTIVE 0x20
+#define TDX_CDC_READY 0x10
+#define TDX_CDC_ADCON 0x08
+#define TDX_CDC_DACON 0x02
+#define TDX_CDC_RESET 0x01
+#define TDX_CDC_ON (TDX_CDC_ADCON|TDX_CDC_DACON)
+
+#define TNX_REG_CODECWR 0x44
+#define TNX_REG_CODEC1RD 0x48
+#define TNX_REG_CODEC2RD 0x4c
+#define TNX_CDC_RWSTAT 0x00000c00
+#define TNX_CDC_SEC 0x00000100
+#define TNX_REG_CODECST 0x40
+#define TNX_CDC_READY2 0x40
+#define TNX_CDC_ADC2ON 0x20
+#define TNX_CDC_DAC2ON 0x10
+#define TNX_CDC_READY1 0x08
+#define TNX_CDC_ADC1ON 0x04
+#define TNX_CDC_DAC1ON 0x02
+#define TNX_CDC_RESET 0x01
+#define TNX_CDC_ON (TNX_CDC_ADC1ON|TNX_CDC_DAC1ON)
+
+
+#define TR_REG_STARTA 0x80
+#define TR_REG_STOPA 0x84
+#define TR_REG_ADDRINTA 0x98
+#define TR_REG_INTENA 0xa4
+
+#define TR_REG_STARTB 0xb4
+#define TR_REG_STOPB 0xb8
+#define TR_REG_ADDRINTB 0xd8
+#define TR_REG_INTENB 0xdc
+
+#define TR_REG_CHNBASE 0xe0
+#define TR_CHN_REGS 5
+
+#endif
diff --git a/sys/dev/sound/pcm/ac97.c b/sys/dev/sound/pcm/ac97.c
new file mode 100644
index 0000000..b3da9cb
--- /dev/null
+++ b/sys/dev/sound/pcm/ac97.c
@@ -0,0 +1,335 @@
+/*
+ * Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk>
+ * 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$
+ */
+
+#include <dev/pcm/sound.h>
+#include <dev/pcm/ac97.h>
+
+#define AC97_MUTE 0x8000
+
+#define AC97_REG_RESET 0x00
+#define AC97_MIX_MASTER 0x02
+#define AC97_MIX_PHONES 0x04
+#define AC97_MIX_MONO 0x06
+#define AC97_MIX_TONE 0x08
+#define AC97_MIX_BEEP 0x0a
+#define AC97_MIX_PHONE 0x0c
+#define AC97_MIX_MIC 0x0e
+#define AC97_MIX_LINE 0x10
+#define AC97_MIX_CD 0x12
+#define AC97_MIX_VIDEO 0x14
+#define AC97_MIX_AUX 0x16
+#define AC97_MIX_PCM 0x18
+#define AC97_REG_RECSEL 0x1a
+#define AC97_MIX_RGAIN 0x1c
+#define AC97_MIX_MGAIN 0x1e
+#define AC97_REG_GEN 0x20
+#define AC97_REG_3D 0x22
+#define AC97_REG_POWER 0x26
+#define AC97_REG_ID1 0x7c
+#define AC97_REG_ID2 0x7e
+
+struct ac97mixtable_entry {
+ int reg:8;
+ unsigned bits:4;
+ unsigned ofs:4;
+ unsigned stereo:1;
+ unsigned mute:1;
+ unsigned recidx:4;
+ unsigned mask:1;
+};
+
+struct ac97_info {
+ ac97_read *read;
+ ac97_write *write;
+ void *devinfo;
+ char id[4];
+ char rev;
+ unsigned caps, se;
+ struct ac97mixtable_entry mix[32];
+};
+
+struct ac97_codecid {
+ u_int32_t id;
+ char *name;
+};
+
+static const struct ac97mixtable_entry ac97mixtable_default[32] = {
+ [SOUND_MIXER_VOLUME] = { AC97_MIX_MASTER, 5, 0, 1, 1, 6, 0 },
+ [SOUND_MIXER_BASS] = { AC97_MIX_TONE, 4, 8, 0, 0, 0, 1 },
+ [SOUND_MIXER_TREBLE] = { AC97_MIX_TONE, 4, 0, 0, 0, 0, 1 },
+ [SOUND_MIXER_PCM] = { AC97_MIX_PCM, 5, 0, 1, 1, 0, 0 },
+ [SOUND_MIXER_SPEAKER] = { AC97_MIX_BEEP, 4, 1, 0, 1, 0, 0 },
+ [SOUND_MIXER_LINE] = { AC97_MIX_LINE, 5, 0, 1, 1, 5, 0 },
+ [SOUND_MIXER_MIC] = { AC97_MIX_MIC, 5, 0, 0, 1, 1, 0 },
+ [SOUND_MIXER_CD] = { AC97_MIX_CD, 5, 0, 1, 1, 2, 0 },
+ [SOUND_MIXER_LINE1] = { AC97_MIX_AUX, 5, 0, 1, 1, 4, 0 },
+ [SOUND_MIXER_VIDEO] = { AC97_MIX_VIDEO, 5, 0, 1, 1, 3, 0 },
+ [SOUND_MIXER_RECLEV] = { -AC97_MIX_RGAIN, 4, 0, 1, 1, 0, 0 }
+};
+
+static const unsigned ac97mixdevs =
+ SOUND_MASK_VOLUME |
+ SOUND_MASK_PCM | SOUND_MASK_SPEAKER | SOUND_MASK_LINE |
+ SOUND_MASK_MIC | SOUND_MASK_CD | SOUND_MASK_LINE1 |
+ SOUND_MASK_VIDEO | SOUND_MASK_RECLEV;
+
+static const unsigned ac97recdevs =
+ SOUND_MASK_VOLUME | SOUND_MASK_LINE | SOUND_MASK_MIC |
+ SOUND_MASK_CD | SOUND_MASK_LINE1 | SOUND_MASK_VIDEO;
+
+static struct ac97_codecid ac97codecid[] = {
+ { 0x414B4D00, "Asahi Kasei AK4540" },
+ { 0x43525900, "Cirrus Logic CS4297" },
+ { 0x83847600, "SigmaTel STAC????" },
+ { 0x83847604, "SigmaTel STAC9701/3/4/5" },
+ { 0x83847605, "SigmaTel STAC9704" },
+ { 0x83847608, "SigmaTel STAC9708" },
+ { 0x83847609, "SigmaTel STAC9721" },
+ { 0, NULL }
+};
+
+static char *ac97enhancement[] = {
+ "",
+ "Analog Devices Phat Stereo",
+ "Creative Stereo Enhancement",
+ "National Semi 3D Stereo Enhancement",
+ "Yamaha Ymersion",
+ "BBE 3D Stereo Enhancement",
+ "Crystal Semi 3D Stereo Enhancement",
+ "Qsound QXpander",
+ "Spatializer 3D Stereo Enhancement",
+ "SRS 3D Stereo Enhancement",
+ "Platform Tech 3D Stereo Enhancement",
+ "AKM 3D Audio",
+ "Aureal Stereo Enhancement",
+ "Aztech 3D Enhancement",
+ "Binaura 3D Audio Enhancement",
+ "ESS Technology Stereo Enhancement",
+ "Harman International VMAx",
+ "Nvidea 3D Stereo Enhancement",
+ "Philips Incredible Sound",
+ "Texas Instruments 3D Stereo Enhancement",
+ "VLSI Technology 3D Stereo Enhancement",
+ "TriTech 3D Stereo Enhancement",
+ "Realtek 3D Stereo Enhancement",
+ "Samsung 3D Stereo Enhancement",
+ "Wolfson Microelectronics 3D Enhancement",
+ "Delta Integration 3D Enhancement",
+ "SigmaTel 3D Enhancement",
+ "Reserved 27",
+ "Rockwell 3D Stereo Enhancement",
+ "Reserved 29",
+ "Reserved 30",
+ "Reserved 31"
+};
+
+static char *ac97feature[] = {
+ "mic channel",
+ "reserved",
+ "tone",
+ "simulated stereo",
+ "headphone",
+ "bass boost",
+ "18 bit DAC",
+ "20 bit DAC",
+ "18 bit ADC",
+ "20 bit ADC"
+};
+
+static int
+ac97_setrecsrc(struct ac97_info *codec, int channel)
+{
+ struct ac97mixtable_entry *e = &codec->mix[channel];
+ if (e->recidx > 0) {
+ int val = e->recidx - 1;
+ val |= val << 8;
+ codec->write(codec->devinfo, AC97_REG_RECSEL, val);
+ return 0;
+ } else return -1;
+}
+
+static int
+ac97_setmixer(struct ac97_info *codec, unsigned channel, unsigned left, unsigned right)
+{
+ struct ac97mixtable_entry *e = &codec->mix[channel];
+ if (e->reg != 0) {
+ int max, val;
+
+ if (!e->stereo) right = left;
+ if (e->reg > 0) {
+ left = 100 - left;
+ right = 100 - right;
+ }
+
+ max = (1 << e->bits) - 1;
+ left = (left * max) / 100;
+ right = (right * max) / 100;
+
+ val = (left << 8) | right;
+
+ left = (left * 100) / max;
+ right = (right * 100) / max;
+
+ if (e->reg > 0) {
+ left = 100 - left;
+ right = 100 - right;
+ }
+
+ if (!e->stereo) {
+ val &= max;
+ val <<= e->ofs;
+ if (e->mask) {
+ int cur = codec->read(codec->devinfo, e->reg);
+ val |= cur & ~(max << e->ofs);
+ }
+ }
+ if (left == 0 && right == 0 && e->mute == 1) val = AC97_MUTE;
+ codec->write(codec->devinfo, abs(e->reg), val);
+ return left | (right << 8);
+ } else return -1;
+}
+
+#if 0
+static int
+ac97_getmixer(struct ac97_info *codec, int channel)
+{
+ struct ac97mixtable_entry *e = &codec->mix[channel];
+ if (channel < SOUND_MIXER_NRDEVICES && e->reg != 0) {
+ int max, val, volume;
+
+ max = (1 << e->bits) - 1;
+ val = codec->read(codec->devinfo, e->reg);
+ if (val == AC97_MUTE && e->mute == 1) volume = 0;
+ else {
+ if (e->stereo == 0) val >>= e->ofs;
+ val &= max;
+ volume = (val * 100) / max;
+ if (e->reg > 0) volume = 100 - volume;
+ }
+ return volume;
+ } else return -1;
+}
+#endif
+
+static unsigned
+ac97_init(struct ac97_info *codec)
+{
+ unsigned i, j;
+ u_int32_t id;
+
+ for (i = 0; i < 32; i++) codec->mix[i] = ac97mixtable_default[i];
+
+ codec->write(codec->devinfo, AC97_REG_POWER, 0);
+ codec->write(codec->devinfo, AC97_REG_RESET, 0);
+ DELAY(10000);
+
+ i = codec->read(codec->devinfo, AC97_REG_RESET);
+ codec->caps = i & 0x03ff;
+ codec->se = (i & 0x7c00) >> 10;
+
+ id = (codec->read(codec->devinfo, AC97_REG_ID1) << 16) |
+ codec->read(codec->devinfo, AC97_REG_ID2);
+ codec->rev = id & 0x000000ff;
+
+ codec->write(codec->devinfo, AC97_MIX_MASTER, 0x20);
+ if ((codec->read(codec->devinfo, AC97_MIX_MASTER) & 0x20) == 0x20)
+ codec->mix[SOUND_MIXER_VOLUME].bits++;
+ codec->write(codec->devinfo, AC97_MIX_MASTER, 0x00);
+
+ if (bootverbose) {
+ printf("ac97: codec id 0x%8x", id);
+ for (i = 0; ac97codecid[i].id; i++) {
+ if (ac97codecid[i].id == id) printf(" (%s)", ac97codecid[i].name);
+ }
+ printf("\nac97: codec features ");
+ for (i = j = 0; i < 10; i++) {
+ if (codec->caps & (1 << i)) {
+ printf("%s%s", j? ", " : "", ac97feature[i]);
+ j++;
+ }
+ }
+ printf("%s%d bit master volume", j? ", " : "", codec->mix[SOUND_MIXER_VOLUME].bits);
+ printf("%s%s\n", j? ", " : "", ac97enhancement[codec->se]);
+ }
+
+ if ((codec->read(codec->devinfo, AC97_REG_POWER) & 2) == 0)
+ printf("ac97: dac not ready\n");
+ return 0;
+}
+
+struct ac97_info *
+ac97_create(void *devinfo, ac97_read *rd, ac97_write *wr)
+{
+ struct ac97_info *codec;
+
+ codec = (struct ac97_info *)malloc(sizeof *codec, M_DEVBUF, M_NOWAIT);
+ if (codec != NULL) {
+ codec->read = rd;
+ codec->write = wr;
+ codec->devinfo = devinfo;
+ }
+ return codec;
+}
+
+static int
+ac97mix_init(snd_mixer *m)
+{
+ struct ac97_info *codec = mix_getdevinfo(m);
+ if (codec == NULL) return -1;
+ ac97_init(codec);
+ mix_setdevs(m, ac97mixdevs | ((codec->caps & 4)? SOUND_MASK_BASS | SOUND_MASK_TREBLE : 0));
+ mix_setrecdevs(m, ac97recdevs);
+ return 0;
+}
+
+static int
+ac97mix_set(snd_mixer *m, unsigned dev, unsigned left, unsigned right)
+{
+ struct ac97_info *codec = mix_getdevinfo(m);
+ if (codec == NULL) return -1;
+ return ac97_setmixer(codec, dev, left, right);
+}
+
+static int
+ac97mix_setrecsrc(snd_mixer *m, u_int32_t src)
+{
+ int i;
+ struct ac97_info *codec = mix_getdevinfo(m);
+ if (codec == NULL) return -1;
+ for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
+ if ((src & (1 << i)) != 0) break;
+ return (ac97_setrecsrc(codec, i) == 0)? 1 << i : -1;
+}
+
+snd_mixer ac97_mixer = {
+ "AC97 mixer",
+ ac97mix_init,
+ ac97mix_set,
+ ac97mix_setrecsrc,
+};
+
diff --git a/sys/dev/sound/pcm/ac97.h b/sys/dev/sound/pcm/ac97.h
new file mode 100644
index 0000000..c3460a2
--- /dev/null
+++ b/sys/dev/sound/pcm/ac97.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk>
+ * 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$
+ */
+
+typedef u_int32_t (ac97_read)(void *devinfo, int regno);
+typedef void (ac97_write)(void *devinfo, int regno, u_int32_t data);
+
+extern snd_mixer ac97_mixer;
+struct ac97_info;
+
+struct ac97_info *ac97_create(void *devinfo, ac97_read *rd, ac97_write *wr);
diff --git a/sys/dev/sound/pcm/channel.c b/sys/dev/sound/pcm/channel.c
new file mode 100644
index 0000000..dd383e5
--- /dev/null
+++ b/sys/dev/sound/pcm/channel.c
@@ -0,0 +1,737 @@
+/*
+ * Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk>
+ * Portions Copyright by Luigi Rizzo - 1997-99
+ * 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$
+ */
+
+#include <dev/pcm/sound.h>
+
+#define MIN_CHUNK_SIZE 256 /* for uiomove etc. */
+#define DMA_ALIGN_THRESHOLD 4
+#define DMA_ALIGN_MASK (~(DMA_ALIGN_THRESHOLD - 1))
+
+#define ISA_DMA(b) (((b)->chan >= 0 && (b)->chan != 4 && (b)->chan < 8))
+#define CANCHANGE(c) (!(c)->buffer.dl)
+
+static int chn_reinit(pcm_channel *c);
+static void chn_stintr(pcm_channel *c);
+/*
+ * SOUND OUTPUT
+
+We use a circular buffer to store samples directed to the DAC.
+The buffer is split into two variable-size regions, each identified
+by an offset in the buffer (rp,fp) and a length (rl,fl):
+
+ 0 rp,rl fp,fl bufsize
+ |__________>____________>________|
+ FREE d READY w FREE
+
+ READY: data written from the process and ready to be sent to the DAC;
+ FREE: free part of the buffer.
+
+Both regions can wrap around the end of the buffer. At initialization,
+READY is empty, FREE takes all the available space, and dma is
+idle. dl contains the length of the current DMA transfer, dl=0
+means that the dma is idle.
+
+The two boundaries (rp,fp) in the buffers are advanced by DMA [d]
+and write() [w] operations. The first portion of the READY region
+is used for DMA transfers. The transfer is started at rp and with
+chunks of length dl. During DMA operations, dsp_wr_dmaupdate()
+updates rp, rl and fl tracking the ISA DMA engine as the transfer
+makes progress.
+When a new block is written, fp advances and rl,fl are updated
+accordingly.
+
+The code works as follows: the user write routine dsp_write_body()
+fills up the READY region with new data (reclaiming space from the
+FREE region) and starts the write DMA engine if inactive. When a
+DMA transfer is complete, an interrupt causes dsp_wrintr() to be
+called which extends the FREE region and possibly starts the next
+transfer.
+
+In some cases, the code tries to track the current status of DMA
+operations by calling dsp_wr_dmaupdate() which changes rp, rl and fl.
+
+The sistem tries to make all DMA transfers use the same size,
+play_blocksize or rec_blocksize. The size is either selected by
+the user, or computed by the system to correspond to about .25s of
+audio. The blocksize must be within a range which is currently:
+
+ min(5ms, 40 bytes) ... 1/2 buffer size.
+
+When there aren't enough data (write) or space (read), a transfer
+is started with a reduced size.
+
+To reduce problems in case of overruns, the routine which fills up
+the buffer should initialize (e.g. by repeating the last value) a
+reasonably long area after the last block so that no noise is
+produced on overruns.
+
+ *
+ */
+
+
+/* XXX this is broken: in the event a bounce buffer is used, data never
+ * gets copied in or out of the real buffer. fix requires mods to isa_dma.c
+ * and possibly fixes to other autodma mode clients
+ */
+static void
+chn_isadmabounce(pcm_channel *c)
+{
+ if (ISA_DMA(&c->buffer)) {
+ /* tell isa_dma to bounce data in/out */
+ } else panic("chn_isadmabounce called on invalid channel");
+}
+
+static int
+chn_polltrigger(pcm_channel *c)
+{
+ snd_dbuf *b = &c->buffer;
+ unsigned lim = (c->flags & CHN_F_HAS_SIZE)? c->blocksize : 1;
+ int trig = 0;
+
+ if (c->flags & CHN_F_MAPPED)
+ trig = ((b->int_count > b->prev_int_count) || b->first_poll);
+ else trig = (((c->direction == PCMDIR_PLAY)? b->fl : b->rl) >= lim);
+ return trig;
+}
+
+static int
+chn_pollreset(pcm_channel *c)
+{
+ snd_dbuf *b = &c->buffer;
+
+ if (c->flags & CHN_F_MAPPED) b->prev_int_count = b->int_count;
+ b->first_poll = 0;
+ return 1;
+}
+
+/*
+ * chn_dmadone() updates pointers and wakes up any process sleeping
+ * or waiting on a select().
+ * Must be called at spltty().
+ */
+static void
+chn_dmadone(pcm_channel *c)
+{
+ snd_dbuf *b = &c->buffer;
+
+ chn_dmaupdate(c);
+ if (ISA_DMA(b)) chn_isadmabounce(c); /* sync bounce buffer */
+ wakeup(b);
+ b->int_count++;
+ if (b->sel.si_pid && chn_polltrigger(c)) selwakeup(&b->sel);
+}
+
+/*
+ * chn_dmaupdate() tracks the status of a dma transfer,
+ * updating pointers. It must be called at spltty().
+ *
+ * NOTE: when we are using auto dma in the device, rl might become
+ * negative.
+ */
+void
+chn_dmaupdate(pcm_channel *c)
+{
+ snd_dbuf *b = &c->buffer;
+ int delta, hwptr = chn_getptr(c);
+
+ if (c->direction == PCMDIR_PLAY) {
+ delta = (b->bufsize + hwptr - b->rp) % b->bufsize;
+ b->rp = hwptr;
+ b->rl -= delta;
+ b->fl += delta;
+ } else {
+ delta = (b->bufsize + hwptr - b->fp) % b->bufsize;
+ b->fp = hwptr;
+ b->rl += delta;
+ b->fl -= delta;
+ }
+ b->total += delta;
+}
+
+/*
+ * Write interrupt routine. Can be called from other places (e.g.
+ * to start a paused transfer), but with interrupts disabled.
+ */
+static void
+chn_wrintr(pcm_channel *c)
+{
+ snd_dbuf *b = &c->buffer;
+ int start;
+
+ if (b->dl) chn_dmadone(c);
+
+ /*
+ * start another dma operation only if have ready data in the buffer,
+ * there is no pending abort, have a full-duplex device, or have a
+ * half duplex device and there is no pending op on the other side.
+ *
+ * Force transfers to be aligned to a boundary of 4, which is
+ * needed when doing stereo and 16-bit.
+ */
+ if (c->flags & CHN_F_MAPPED) start = c->flags & CHN_F_TRIGGERED;
+ else start = (b->rl >= DMA_ALIGN_THRESHOLD && !(c->flags & CHN_F_ABORTING));
+ if (start) {
+ int l;
+ chn_dmaupdate(c);
+ l = min(b->rl, c->blocksize) & DMA_ALIGN_MASK;
+ if (c->flags & CHN_F_MAPPED) l = c->blocksize;
+ /*
+ * check if we need to reprogram the DMA on the sound card.
+ * This happens if the size has changed _and_ the new size
+ * is smaller, or it matches the blocksize.
+ *
+ * 0 <= l <= blocksize
+ * 0 <= dl <= blocksize
+ * reprog if (dl == 0 || l != dl)
+ * was:
+ * l != b->dl && (b->dl == 0 || l < b->dl || l == c->blocksize)
+ */
+ if (b->dl == 0 || l != b->dl) {
+ /* size has changed. Stop and restart */
+ DEB(printf("wrintr: bsz %d -> %d, rp %d rl %d\n",
+ b->dl, l, b->rp, b->rl));
+ if (b->dl) chn_trigger(c, PCMTRIG_STOP);
+ b->dl = l; /* record new transfer size */
+ chn_trigger(c, PCMTRIG_START);
+ }
+ } else {
+ /* cannot start a new dma transfer */
+ DEB(printf("cannot start wr-dma flags 0x%08x rp %d rl %d\n",
+ c->flags, b->rp, b->rl));
+ if (b->dl) { /* was active */
+ b->dl = 0;
+ chn_trigger(c, PCMTRIG_STOP);
+#if 0
+ if (c->flags & CHN_F_WRITING)
+ DEB(printf("got wrint while reloading\n"));
+ else if (b->rl <= 0) /* XXX added 980110 lr */
+ chn_resetbuf(c);
+#endif
+ }
+ }
+}
+
+/*
+ * user write routine
+ *
+ * advance the boundary between READY and FREE, fill the space with
+ * uiomove(), and possibly start DMA. Do the above until the transfer
+ * is complete.
+ *
+ * To minimize latency in case a pending DMA transfer is about to end,
+ * we do the transfer in pieces of increasing sizes, extending the
+ * READY area at every checkpoint. In the (necessary) assumption that
+ * memory bandwidth is larger than the rate at which the dma consumes
+ * data, we reduce the latency to something proportional to the length
+ * of the first piece, while keeping the overhead low and being able
+ * to feed the DMA with large blocks.
+ */
+
+int
+chn_write(pcm_channel *c, struct uio *buf)
+{
+ int l, w, timeout, ret = 0;
+ long s;
+ snd_dbuf *b = &c->buffer;
+
+ if (c->flags & CHN_F_WRITING) {
+ /* This shouldn't happen and is actually silly
+ * - will never wake up, just timeout; why not sleep on b?
+ */
+ tsleep(&s, PZERO, "pcmwrW", hz);
+ return EBUSY;
+ }
+ c->flags |= CHN_F_WRITING;
+ while (buf->uio_resid >= DMA_ALIGN_THRESHOLD) {
+ s = spltty();
+ chn_dmaupdate(c);
+ splx(s);
+ if (b->fl < DMA_ALIGN_THRESHOLD) {
+ if (c->flags & CHN_F_NBIO) break;
+ timeout = (buf->uio_resid >= b->dl)? hz : 1;
+ ret = tsleep(b, PRIBIO | PCATCH, "pcmwr", timeout);
+ if (ret == EINTR) chn_abort(c);
+ if (ret == EINTR || ret == ERESTART) break;
+ ret = 0;
+ continue;
+ }
+ /* ensure we always have a whole number of samples */
+ l = min(b->fl, b->bufsize - b->fp) & DMA_ALIGN_MASK;
+ w = c->feeder->feed(c->feeder, b->buf + b->fp, l, buf);
+ s = spltty();
+ b->rl += w;
+ b->fl -= w;
+ b->fp = (b->fp + w) % b->bufsize;
+ splx(s);
+ if (b->rl && !b->dl) chn_stintr(c);
+ }
+ c->flags &= ~CHN_F_WRITING;
+ return ret;
+}
+
+/*
+ * SOUND INPUT
+ *
+
+The input part is similar to the output one, with a circular buffer
+split in two regions, and boundaries advancing because of read() calls
+[r] or dma operation [d]. At initialization, as for the write
+routine, READY is empty, and FREE takes all the space.
+
+ 0 rp,rl fp,fl bufsize
+ |__________>____________>________|
+ FREE r READY d FREE
+
+Operation is as follows: upon user read (dsp_read_body()) a DMA read
+is started if not already active (marked by b->dl > 0),
+then as soon as data are available in the READY region they are
+transferred to the user buffer, thus advancing the boundary between FREE
+and READY. Upon interrupts, caused by a completion of a DMA transfer,
+the READY region is extended and possibly a new transfer is started.
+
+When necessary, dsp_rd_dmaupdate() is called to advance fp (and update
+rl,fl accordingly). Upon user reads, rp is advanced and rl,fl are
+updated accordingly.
+
+The rules to choose the size of the new DMA area are similar to
+the other case, with a preferred constant transfer size equal to
+rec_blocksize, and fallback to smaller sizes if no space is available.
+
+ */
+
+/* read interrupt routine. Must be called with interrupts blocked. */
+static void
+chn_rdintr(pcm_channel *c)
+{
+ snd_dbuf *b = &c->buffer;
+ int start;
+
+ if (b->dl) chn_dmadone(c);
+
+ DEB(printf("rdintr: start dl %d, rp:rl %d:%d, fp:fl %d:%d\n",
+ b->dl, b->rp, b->rl, b->fp, b->fl));
+ /* Restart if have enough free space to absorb overruns */
+ if (c->flags & CHN_F_MAPPED) start = c->flags & CHN_F_TRIGGERED;
+ else start = (b->fl > 0x200 && !(c->flags & CHN_F_ABORTING));
+ if (start) {
+ int l = min(b->fl - 0x100, c->blocksize);
+ if (c->flags & CHN_F_MAPPED) l = c->blocksize;
+ l &= DMA_ALIGN_MASK ; /* realign sizes */
+
+ DEB(printf("rdintr: dl %d -> %d\n", b->dl, l);)
+ if (l != b->dl) {
+ /* size has changed. Stop and restart */
+ if (b->dl) {
+ chn_trigger(c, PCMTRIG_STOP);
+ chn_dmaupdate(c);
+ l = min(b->fl - 0x100, c->blocksize);
+ l &= DMA_ALIGN_MASK ; /* realign sizes */
+ }
+ b->dl = l;
+ chn_trigger(c, PCMTRIG_START);
+ }
+ } else {
+ if (b->dl) { /* was active */
+ b->dl = 0;
+ chn_dmaupdate(c);
+ chn_trigger(c, PCMTRIG_STOP);
+ }
+ }
+}
+
+/*
+ * body of user-read routine
+ *
+ * Start DMA if not active; wait for READY not empty.
+ * Transfer data from READY region using uiomove(), advance boundary
+ * between FREE and READY. Repeat until transfer is complete.
+ *
+ * To avoid excessive latency in freeing up space for the DMA
+ * engine, transfers are done in blocks of increasing size, so that
+ * the latency is proportional to the size of the smallest block, but
+ * we have a low overhead and are able to feed the dma engine with
+ * large blocks.
+ *
+ * NOTE: in the current version, read will not return more than
+ * blocksize bytes at once (unless more are already available), to
+ * avoid that requests using very large buffers block for too long.
+ */
+
+int
+chn_read(pcm_channel *c, struct uio *buf)
+{
+ int w, l, timeout, limit, ret = 0;
+ long s;
+ snd_dbuf *b = &c->buffer;
+
+ if (c->flags & CHN_F_READING) {
+ /* This shouldn't happen and is actually silly */
+ tsleep(&s, PZERO, "pcmrdR", hz);
+ return (EBUSY);
+ }
+
+ if (!b->rl & !b->dl) chn_stintr(c);
+ c->flags |= CHN_F_READING;
+ limit = buf->uio_resid - c->blocksize;
+ if (limit < 0) limit = 0;
+ while (buf->uio_resid > limit) {
+ s = spltty();
+ chn_dmaupdate(c);
+ splx(s);
+ if (b->rl < DMA_ALIGN_THRESHOLD) {
+ if (c->flags & CHN_F_NBIO) break;
+ timeout = (buf->uio_resid - limit >= b->dl)? hz : 1;
+ ret = tsleep(b, PRIBIO | PCATCH, "pcmrd", timeout);
+ if (ret == EINTR) chn_abort(c);
+ if (ret == EINTR || ret == ERESTART) break;
+ ret = 0;
+ continue;
+ }
+ /* ensure we always have a whole number of samples */
+ l = min(b->rl, b->bufsize - b->rp) & DMA_ALIGN_MASK;
+ w = c->feeder->feed(c->feeder, b->buf + b->rp, l, buf);
+ s = spltty();
+ b->rl -= w;
+ b->fl += w;
+ b->rp = (b->rp + w) % b->bufsize;
+ splx(s);
+ }
+ c->flags &= ~CHN_F_READING;
+ return ret;
+}
+
+void
+chn_intr(pcm_channel *c)
+{
+ if (!c->buffer.dl) chn_reinit(c);
+ if (c->direction == PCMDIR_PLAY) chn_wrintr(c); else chn_rdintr(c);
+}
+
+static void
+chn_stintr(pcm_channel *c)
+{
+ u_long s;
+ s = spltty();
+ chn_intr(c);
+ splx(s);
+}
+
+static void
+chn_dma_setmap(void *arg, bus_dma_segment_t *segs, int nseg, int error)
+{
+ snd_dbuf *b = (snd_dbuf *)arg;
+
+ if (bootverbose) {
+ printf("pcm: setmap %lx, %lx; ", (unsigned long)segs->ds_addr,
+ (unsigned long)segs->ds_len);
+ printf("%p -> %lx\n", b->buf, (unsigned long)vtophys(b->buf));
+ }
+}
+
+int
+chn_allocbuf(snd_dbuf *b, bus_dma_tag_t parent_dmat)
+{
+ if (bus_dmamem_alloc(parent_dmat, (void **)&b->buf,
+ BUS_DMA_NOWAIT, &b->dmamap)) return -1;
+ if (bus_dmamap_load(parent_dmat, b->dmamap, b->buf,
+ b->bufsize, chn_dma_setmap, b, 0)) return -1;
+ return 0;
+}
+
+void
+chn_resetbuf(pcm_channel *c)
+{
+ snd_dbuf *b = &c->buffer;
+ u_int16_t data, *p;
+ u_int32_t i;
+
+ c->buffer.sample_size = 1;
+ c->buffer.sample_size <<= (c->hwfmt & AFMT_STEREO)? 1 : 0;
+ c->buffer.sample_size <<= (c->hwfmt & AFMT_16BIT)? 1 : 0;
+ /* rely on bufsize & 3 == 0 */
+ if (c->hwfmt & AFMT_SIGNED) data = 0x00; else data = 0x80;
+ if (c->hwfmt & AFMT_16BIT) data <<= 8; else data |= data << 8;
+ if (c->hwfmt & AFMT_BIGENDIAN)
+ data = ((data >> 8) & 0x00ff) | ((data << 8) & 0xff00);
+ for (i = 0, p = (u_int16_t *)b->buf; i < b->bufsize; i += 2)
+ *p++ = data;
+ b->rp = b->fp = 0;
+ b->dl = b->rl = 0;
+ b->prev_total = b->total = 0;
+ b->prev_int_count = b->int_count = 0;
+ b->first_poll = 1;
+ b->fl = b->bufsize;
+}
+
+void
+buf_isadma(snd_dbuf *b, int go)
+{
+ if (ISA_DMA(b)) {
+ if (go == PCMTRIG_START) isa_dmastart(b->dir | B_RAW, b->buf,
+ b->bufsize, b->chan);
+ else {
+ isa_dmastop(b->chan);
+ isa_dmadone(b->dir | B_RAW, b->buf, b->bufsize,
+ b->chan);
+ }
+ } else panic("buf_isadma called on invalid channel");
+}
+
+int
+buf_isadmaptr(snd_dbuf *b)
+{
+ if (ISA_DMA(b)) {
+ int i = b->dl? isa_dmastatus(b->chan) : b->bufsize;
+ if (i < 0) i = 0;
+ return b->bufsize - i;
+ } else panic("buf_isadmaptr called on invalid channel");
+ return -1;
+}
+
+/*
+ * snd_sync waits until the space in the given channel goes above
+ * a threshold. The threshold is checked against fl or rl respectively.
+ * Assume that the condition can become true, do not check here...
+ */
+int
+chn_sync(pcm_channel *c, int threshold)
+{
+ u_long s, rdy;
+ int ret;
+ snd_dbuf *b = &c->buffer;
+
+ for (;;) {
+ s = spltty();
+ chn_dmaupdate(c);
+ rdy = (c->direction == PCMDIR_PLAY)? b->fl : b->rl;
+ if (rdy <= threshold) {
+ ret = tsleep((caddr_t)b, PRIBIO | PCATCH, "pcmsyn", 1);
+ splx(s);
+ if (ret == ERESTART || ret == EINTR) {
+ printf("tsleep returns %d\n", ret);
+ return -1;
+ }
+ } else break;
+ }
+ splx(s);
+ return 0;
+}
+
+int
+chn_poll(pcm_channel *c, int ev, struct proc *p)
+{
+ snd_dbuf *b = &c->buffer;
+ u_long s = spltty();
+ if (b->dl) chn_dmaupdate(c);
+ splx(s);
+ if (chn_polltrigger(c) && chn_pollreset(c)) return ev;
+ else {
+ selrecord(p, &b->sel);
+ return 0;
+ }
+}
+
+/*
+ * chn_abort is a non-blocking function which aborts a pending
+ * DMA transfer and flushes the buffers.
+ * It returns the number of bytes that have not been transferred.
+ */
+int
+chn_abort(pcm_channel *c)
+{
+ long s;
+ int missing = 0;
+ snd_dbuf *b = &c->buffer;
+
+ s = spltty();
+ if (b->dl) {
+ b->dl = 0;
+ c->flags &= ~((c->direction == PCMDIR_PLAY)? CHN_F_WRITING : CHN_F_READING);
+ chn_trigger(c, PCMTRIG_ABORT);
+ chn_dmadone(c);
+ }
+ missing = b->rl;
+ splx(s);
+ return missing;
+}
+
+/*
+ * this routine tries to flush the dma transfer. It is called
+ * on a close. We immediately abort any read DMA
+ * operation, and then wait for the play buffer to drain.
+ */
+
+int
+chn_flush(pcm_channel *c)
+{
+ int ret, count = 10;
+ snd_dbuf *b = &c->buffer;
+
+ DEB(printf("snd_flush c->flags 0x%08x\n", c->flags));
+ c->flags |= CHN_F_CLOSING;
+ if (c->direction != PCMDIR_PLAY) chn_abort(c);
+ else while (b->dl) {
+ /* still pending output data. */
+ ret = tsleep((caddr_t)b, PRIBIO | PCATCH, "pcmflu", hz);
+ chn_dmaupdate(c);
+ DEB(printf("snd_sync: now rl : fl %d : %d\n", b->rl, b->fl));
+ if (ret == EINTR) {
+ printf("tsleep returns %d\n", ret);
+ return -1;
+ }
+ if (ret && --count == 0) {
+ printf("timeout flushing dbuf_out, cnt 0x%x flags 0x%x\n",
+ b->rl, c->flags);
+ break;
+ }
+ }
+ c->flags &= ~CHN_F_CLOSING;
+ if (c->direction == PCMDIR_PLAY) chn_abort(c);
+ return 0;
+}
+
+int
+chn_reset(pcm_channel *c)
+{
+ chn_abort(c);
+ c->flags &= CHN_F_RESET;
+ chn_resetbuf(c);
+ c->flags |= CHN_F_INIT;
+ return 0;
+}
+
+static int
+chn_reinit(pcm_channel *c)
+{
+ if ((c->flags & CHN_F_INIT) && CANCHANGE(c)) {
+ chn_setformat(c, c->format);
+ chn_setspeed(c, c->speed);
+ chn_setblocksize(c, c->blocksize);
+ chn_setvolume(c, (c->volume >> 8) & 0xff, c->volume & 0xff);
+ c->flags &= ~CHN_F_INIT;
+ return 1;
+ }
+ return 0;
+}
+
+int
+chn_init(pcm_channel *c, void *devinfo, int dir)
+{
+ c->flags = 0;
+ c->feeder = &feeder_root;
+ c->buffer.chan = -1;
+ c->devinfo = c->init(devinfo, &c->buffer, c, dir);
+ chn_setdir(c, dir);
+ return 0;
+}
+
+int
+chn_setdir(pcm_channel *c, int dir)
+{
+ c->direction = dir;
+ if (ISA_DMA(&c->buffer))
+ c->buffer.dir = (dir == PCMDIR_PLAY)? B_WRITE : B_READ;
+ return c->setdir(c->devinfo, c->direction);
+}
+
+int
+chn_setvolume(pcm_channel *c, int left, int right)
+{
+ /* could add a feeder for volume changing if channel returns -1 */
+ if (CANCHANGE(c)) {
+ return -1;
+ }
+ c->volume = (left << 8) | right;
+ c->flags |= CHN_F_INIT;
+ return 0;
+}
+
+int
+chn_setspeed(pcm_channel *c, int speed)
+{
+ /* could add a feeder for rate conversion */
+ if (CANCHANGE(c)) {
+ c->speed = c->setspeed(c->devinfo, speed);
+ return c->speed;
+ }
+ c->speed = speed;
+ c->flags |= CHN_F_INIT;
+ return 0;
+}
+
+int
+chn_setformat(pcm_channel *c, u_int32_t fmt)
+{
+ if (CANCHANGE(c)) {
+ c->hwfmt = c->format = fmt;
+ c->hwfmt = chn_feedchain(c);
+ chn_resetbuf(c);
+ c->setformat(c->devinfo, c->hwfmt);
+ return fmt;
+ }
+ c->format = fmt;
+ c->flags |= CHN_F_INIT;
+ return 0;
+}
+
+int
+chn_setblocksize(pcm_channel *c, int blksz)
+{
+ if (CANCHANGE(c)) {
+ c->flags &= ~CHN_F_HAS_SIZE;
+ if (blksz >= 2) c->flags |= CHN_F_HAS_SIZE;
+ blksz = abs(blksz);
+ if (blksz < 2) blksz = (c->buffer.sample_size * c->speed) >> 2;
+ RANGE(blksz, 1024, c->buffer.bufsize / 4);
+ blksz &= ~3;
+ c->blocksize = c->setblocksize(c->devinfo, blksz);
+ return c->blocksize;
+ }
+ c->blocksize = blksz;
+ c->flags |= CHN_F_INIT;
+ return 0;
+}
+
+int
+chn_trigger(pcm_channel *c, int go)
+{
+ return c->trigger(c->devinfo, go);
+}
+
+int
+chn_getptr(pcm_channel *c)
+{
+ return c->getptr(c->devinfo);
+}
+
+pcmchan_caps *
+chn_getcaps(pcm_channel *c)
+{
+ return c->getcaps(c->devinfo);
+}
diff --git a/sys/dev/sound/pcm/channel.h b/sys/dev/sound/pcm/channel.h
new file mode 100644
index 0000000..8667a9f
--- /dev/null
+++ b/sys/dev/sound/pcm/channel.h
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk>
+ * 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$
+ */
+
+int chn_write(pcm_channel *c, struct uio *buf);
+int chn_read(pcm_channel *c, struct uio *buf);
+int chn_sync(pcm_channel *c, int threshold);
+int chn_flush(pcm_channel *c);
+int chn_poll(pcm_channel *c, int ev, struct proc *p);
+
+int chn_init(pcm_channel *c, void *devinfo, int dir);
+int chn_setdir(pcm_channel *c, int dir);
+int chn_reset(pcm_channel *c);
+int chn_setvolume(pcm_channel *c, int left, int right);
+int chn_setspeed(pcm_channel *c, int speed);
+int chn_setformat(pcm_channel *c, u_int32_t fmt);
+int chn_setblocksize(pcm_channel *c, int blksz);
+int chn_trigger(pcm_channel *c, int go);
+int chn_getptr(pcm_channel *c);
+pcmchan_caps *chn_getcaps(pcm_channel *c);
+
+int chn_allocbuf(snd_dbuf *b, bus_dma_tag_t parent_dmat);
+void chn_resetbuf(pcm_channel *c);
+void chn_intr(pcm_channel *c);
+void chn_dmaupdate(pcm_channel *c);
+int chn_abort(pcm_channel *c);
+
+void buf_isadma(snd_dbuf *b, int go);
+int buf_isadmaptr(snd_dbuf *b);
+int chn_feedchain(pcm_channel *c);
+
+extern pcm_feeder feeder_root;
+
+#define PCMDIR_PLAY 1
+#define PCMDIR_REC -1
+
+#define PCMTRIG_START 1
+#define PCMTRIG_STOP 0
+#define PCMTRIG_ABORT -1
+
+#define CHN_F_READING 0x00000001 /* have a pending read */
+#define CHN_F_WRITING 0x00000002 /* have a pending write */
+#define CHN_F_CLOSING 0x00000004 /* a pending close */
+#define CHN_F_ABORTING 0x00000008 /* a pending abort */
+#define CHN_F_PENDING_IO (CHN_F_READING | CHN_F_WRITING)
+#define CHN_F_RUNNING 0x00000010 /* dma is running */
+#define CHN_F_TRIGGERED 0x00000020
+
+#define CHN_F_BUSY 0x00001000 /* has been opened */
+#define CHN_F_HAS_SIZE 0x00002000 /* user set block size */
+#define CHN_F_NBIO 0x00004000 /* do non-blocking i/o */
+#define CHN_F_INIT 0x00008000 /* changed parameters. need init */
+#define CHN_F_MAPPED 0x00010000 /* has been mmap()ed */
+
+
+#define CHN_F_RESET (CHN_F_BUSY)
diff --git a/sys/dev/sound/pcm/datatypes.h b/sys/dev/sound/pcm/datatypes.h
new file mode 100644
index 0000000..50928f6
--- /dev/null
+++ b/sys/dev/sound/pcm/datatypes.h
@@ -0,0 +1,156 @@
+/*
+ * Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk>
+ * 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$
+ */
+
+typedef struct _snd_mixer snd_mixer;
+typedef struct _snd_dbuf snd_dbuf;
+typedef struct _snddev_info snddev_info;
+typedef struct _pcmchan_caps pcmchan_caps;
+typedef struct _pcm_feeder pcm_feeder;
+typedef struct _pcm_channel pcm_channel;
+
+typedef int (mix_set_t)(snd_mixer *m, unsigned dev, unsigned left, unsigned right);
+typedef int (mix_recsrc_t)(snd_mixer *m, u_int32_t src);
+typedef int (mix_init_t)(snd_mixer *m);
+
+struct _snd_mixer {
+ char name[64];
+ mix_init_t *init;
+ mix_set_t *set;
+ mix_recsrc_t *setrecsrc;
+
+ void *devinfo;
+ u_int32_t devs;
+ u_int32_t recdevs;
+ u_int32_t recsrc;
+ u_int16_t level[32];
+};
+
+/*
+ * descriptor of a dma buffer. See dmabuf.c for documentation.
+ * (rp,rl) and (fp,fl) identify the READY and FREE regions of the
+ * buffer. dl contains the length used for dma transfer, dl>0 also
+ * means that the channel is busy and there is a DMA transfer in progress.
+ */
+
+struct _snd_dbuf {
+ char *buf;
+ int bufsize;
+ volatile int rp, fp; /* pointers to the ready and free area */
+ volatile int dl; /* transfer size */
+ volatile int rl, fl; /* lenght of ready and free areas. */
+ volatile u_int32_t int_count, prev_int_count;
+ int chan, dir; /* dma channel */
+ int sample_size; /* 1, 2, 4 */
+ struct selinfo sel;
+ u_long total; /* total bytes processed */
+ u_long prev_total; /* copy of the above when GETxPTR called */
+ int first_poll;
+ bus_dmamap_t dmamap;
+};
+
+typedef int (pcmfeed_init_t)(pcm_feeder *feeder);
+typedef int (pcmfeed_free_t)(pcm_feeder *feeder);
+typedef int (pcmfeed_feed_t)(pcm_feeder *feeder, u_int8_t *buffer, u_int32_t count, struct uio *stream);
+
+struct _pcm_feeder {
+ char name[16];
+ pcmfeed_init_t *init;
+ pcmfeed_free_t *free;
+ pcmfeed_feed_t *feed;
+ void *data;
+ pcm_feeder *source;
+};
+
+struct _pcmchan_caps {
+ u_int32_t minspeed, maxspeed;
+ u_int32_t formats, bestfmt;
+};
+
+typedef void *(pcmchan_init_t)(void *devinfo, snd_dbuf *b, pcm_channel *c, int dir);
+typedef int (pcmchan_setdir_t)(void *data, int dir);
+typedef int (pcmchan_setformat_t)(void *data, u_int32_t format);
+typedef int (pcmchan_setspeed_t)(void *data, u_int32_t speed);
+typedef int (pcmchan_setblocksize_t)(void *data, u_int32_t blocksize);
+typedef int (pcmchan_trigger_t)(void *data, int go);
+typedef int (pcmchan_getptr_t)(void *data);
+typedef pcmchan_caps *(pcmchan_getcaps_t)(void *data);
+
+struct _pcm_channel {
+ pcmchan_init_t *init;
+ pcmchan_setdir_t *setdir;
+ pcmchan_setformat_t *setformat;
+ pcmchan_setspeed_t *setspeed;
+ pcmchan_setblocksize_t *setblocksize;
+ pcmchan_trigger_t *trigger;
+ pcmchan_getptr_t *getptr;
+ pcmchan_getcaps_t *getcaps;
+ pcm_feeder *feeder;
+
+ int volume;
+ u_int32_t speed;
+ u_int32_t flags;
+ u_int32_t format, hwfmt;
+ u_int32_t blocksize;
+
+ int direction;
+ snd_dbuf buffer;
+ void *devinfo;
+};
+
+#define SND_STATUSLEN 64
+/* descriptor of audio device */
+struct _snddev_info {
+ pcm_channel *play, *rec, **aplay, **arec, fakechan;
+ unsigned playcount, reccount, chancount;
+ snd_mixer mixer;
+ u_long magic;
+ unsigned flags;
+ void *devinfo;
+ char status[SND_STATUSLEN];
+};
+
+/* mixer description structure and macros - these should go away,
+ * only sb.[ch] and mss.[ch] use them
+ */
+struct mixer_def {
+ u_int regno:7;
+ u_int polarity:1; /* 1 means reversed */
+ u_int bitoffs:4;
+ u_int nbits:4;
+};
+typedef struct mixer_def mixer_ent;
+typedef struct mixer_def mixer_tab[32][2];
+
+#define MIX_ENT(name, reg_l, pol_l, pos_l, len_l, reg_r, pol_r, pos_r, len_r) \
+ {{reg_l, pol_l, pos_l, len_l}, {reg_r, pol_r, pos_r, len_r}}
+
+#define PMIX_ENT(name, reg_l, pos_l, len_l, reg_r, pos_r, len_r) \
+ {{reg_l, 0, pos_l, len_l}, {reg_r, 0, pos_r, len_r}}
+
+#define MIX_NONE(name) MIX_ENT(name, 0,0,0,0, 0,0,0,0)
+
diff --git a/sys/dev/sound/pcm/dsp.c b/sys/dev/sound/pcm/dsp.c
new file mode 100644
index 0000000..a868e92
--- /dev/null
+++ b/sys/dev/sound/pcm/dsp.c
@@ -0,0 +1,543 @@
+/*
+ * Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk>
+ * 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$
+ */
+
+#include <sys/param.h>
+#include <sys/queue.h>
+#include <sys/kernel.h>
+
+#include <dev/pcm/sound.h>
+
+static int getchns(snddev_info *d, int chan, pcm_channel **rdch, pcm_channel **wrch);
+
+static pcm_channel *
+allocchn(snddev_info *d, int direction)
+{
+ pcm_channel *chns = (direction == PCMDIR_PLAY)? d->play : d->rec;
+ int i, cnt = (direction == PCMDIR_PLAY)? d->playcount : d->reccount;
+ for (i = 0; i < cnt; i++) {
+ if (!(chns[i].flags & CHN_F_BUSY)) {
+ chns[i].flags |= CHN_F_BUSY;
+ return &chns[i];
+ }
+ }
+ return NULL;
+}
+
+static int
+getchns(snddev_info *d, int chan, pcm_channel **rdch, pcm_channel **wrch)
+{
+ if ((d->flags & SD_F_PRIO_SET) == SD_F_PRIO_SET)
+ panic("read and write both prioritised");
+ if (d->flags & SD_F_SIMPLEX) {
+ *rdch = (d->flags & SD_F_PRIO_RD)? d->arec[chan] : &d->fakechan;
+ *wrch = (d->flags & SD_F_PRIO_WR)? d->aplay[chan] : &d->fakechan;
+ } else {
+ *rdch = d->arec[chan];
+ *wrch = d->aplay[chan];
+ }
+ return 0;
+}
+
+static void
+setchns(snddev_info *d, int chan)
+{
+ if ((d->flags & SD_F_PRIO_SET) == SD_F_PRIO_SET)
+ panic("read and write both prioritised");
+ d->flags |= SD_F_DIR_SET;
+ if (d->flags & SD_F_EVILSB16) {
+ if ((d->flags & SD_F_PRIO_RD) && (d->aplay[chan])) {
+ pcm_channel *tmp;
+ tmp = d->arec[chan];
+ d->arec[chan] = d->aplay[chan];
+ d->aplay[chan] = tmp;
+ }
+ if (d->aplay[chan]) chn_setdir(d->aplay[chan], PCMDIR_PLAY);
+ if (d->arec[chan]) chn_setdir(d->arec[chan], PCMDIR_REC);
+ }
+}
+
+int
+dsp_open(snddev_info *d, int chan, int oflags, int devtype)
+{
+ pcm_channel *rdch = NULL, *wrch = NULL;
+ u_int32_t fmt;
+
+ if (chan >= d->chancount) return ENODEV;
+ if (d->aplay[chan] || d->arec[chan]) return EBUSY;
+ if (oflags & FREAD) {
+ rdch = allocchn(d, PCMDIR_REC);
+ if (!rdch) return EBUSY;
+ }
+ if (oflags & FWRITE) {
+ wrch = allocchn(d, PCMDIR_PLAY);
+ if (!wrch) {
+ if (rdch) rdch->flags &= ~CHN_F_BUSY;
+ return EBUSY;
+ }
+ }
+ d->aplay[chan] = wrch;
+ d->arec[chan] = rdch;
+ switch (devtype) {
+ case SND_DEV_DSP16:
+ fmt = AFMT_S16_LE;
+ break;
+
+ case SND_DEV_DSP:
+ fmt = AFMT_U8;
+ break;
+
+ case SND_DEV_AUDIO:
+ fmt = AFMT_MU_LAW;
+ break;
+
+ default:
+ return ENXIO;
+ }
+
+ if (rdch) {
+ chn_reset(rdch);
+ if (oflags & O_NONBLOCK) rdch->flags |= CHN_F_NBIO;
+ rdch->volume = (100 << 8) | 100;
+ rdch->format = fmt;
+ rdch->speed = DSP_DEFAULT_SPEED;
+ rdch->blocksize = 2048;
+ }
+ if (wrch) {
+ chn_reset(wrch);
+ if (oflags & O_NONBLOCK) wrch->flags |= CHN_F_NBIO;
+ wrch->volume = (100 << 8) | 100;
+ wrch->format = fmt;
+ wrch->speed = DSP_DEFAULT_SPEED;
+ wrch->blocksize = 2048;
+ }
+ return 0;
+}
+
+int
+dsp_close(snddev_info *d, int chan, int devtype)
+{
+ pcm_channel *rdch, *wrch;
+
+ d->flags &= ~SD_F_TRANSIENT;
+ getchns(d, chan, &rdch, &wrch);
+
+ if (rdch) {
+ chn_abort(rdch);
+ rdch->flags &= ~(CHN_F_BUSY | CHN_F_RUNNING | CHN_F_MAPPED);
+ }
+ if (wrch) wrch->flags &= ~(CHN_F_BUSY | CHN_F_RUNNING | CHN_F_MAPPED);
+ d->aplay[chan] = NULL;
+ d->arec[chan] = NULL;
+ return 0;
+}
+
+int
+dsp_read(snddev_info *d, int chan, struct uio *buf, int flag)
+{
+ pcm_channel *rdch, *wrch;
+
+ if (!(d->flags & SD_F_PRIO_SET)) d->flags |= SD_F_PRIO_RD;
+ if (!(d->flags & SD_F_DIR_SET)) setchns(d, chan);
+ getchns(d, chan, &rdch, &wrch);
+ if (!rdch || !(rdch->flags & CHN_F_BUSY))
+ panic("dsp_read: non%s channel", rdch? "busy" : "existant");
+ if (rdch->flags & CHN_F_MAPPED) return EINVAL;
+ if (!(rdch->flags & CHN_F_RUNNING)) rdch->flags |= CHN_F_RUNNING;
+ return chn_read(rdch, buf);
+}
+
+int
+dsp_write(snddev_info *d, int chan, struct uio *buf, int flag)
+{
+ pcm_channel *rdch, *wrch;
+
+ if (!(d->flags & SD_F_PRIO_SET)) d->flags |= SD_F_PRIO_WR;
+ if (!(d->flags & SD_F_DIR_SET)) setchns(d, chan);
+ getchns(d, chan, &rdch, &wrch);
+ if (!wrch || !(wrch->flags & CHN_F_BUSY))
+ panic("dsp_write: non%s channel", wrch? "busy" : "existant");
+ if (wrch->flags & CHN_F_MAPPED) return EINVAL;
+ if (!(wrch->flags & CHN_F_RUNNING)) wrch->flags |= CHN_F_RUNNING;
+ return chn_write(wrch, buf);
+}
+
+int
+dsp_ioctl(snddev_info *d, int chan, u_long cmd, caddr_t arg)
+{
+ int ret = 0, *arg_i = (int *)arg;
+ u_long s;
+ pcm_channel *wrch = NULL, *rdch = NULL;
+
+ getchns(d, chan, &rdch, &wrch);
+
+ /*
+ * all routines are called with int. blocked. Make sure that
+ * ints are re-enabled when calling slow or blocking functions!
+ */
+ s = spltty();
+ switch(cmd) {
+
+ /*
+ * we start with the new ioctl interface.
+ */
+ case AIONWRITE: /* how many bytes can write ? */
+ if (wrch && wrch->buffer.dl) chn_dmaupdate(wrch);
+ *arg_i = wrch? wrch->buffer.fl : 0;
+ break;
+
+ case AIOSSIZE: /* set the current blocksize */
+ {
+ struct snd_size *p = (struct snd_size *)arg;
+ splx(s);
+ if (wrch) chn_setblocksize(wrch, p->play_size);
+ if (rdch) chn_setblocksize(rdch, p->rec_size);
+ }
+ /* FALLTHROUGH */
+ case AIOGSIZE: /* get the current blocksize */
+ {
+ struct snd_size *p = (struct snd_size *)arg;
+ if (wrch) p->play_size = wrch->blocksize;
+ if (rdch) p->rec_size = rdch->blocksize;
+ }
+ break;
+
+ case AIOSFMT:
+ {
+ snd_chan_param *p = (snd_chan_param *)arg;
+ splx(s);
+ if (wrch) {
+ chn_setformat(wrch, p->play_format);
+ chn_setspeed(wrch, p->play_rate);
+ }
+ if (rdch) {
+ chn_setformat(rdch, p->rec_format);
+ chn_setspeed(rdch, p->rec_rate);
+ }
+ }
+ /* FALLTHROUGH */
+
+ case AIOGFMT:
+ {
+ snd_chan_param *p = (snd_chan_param *)arg;
+ p->play_rate = wrch? wrch->speed : 0;
+ p->rec_rate = rdch? rdch->speed : 0;
+ p->play_format = wrch? wrch->format : 0;
+ p->rec_format = rdch? rdch->format : 0;
+ }
+ break;
+
+ case AIOGCAP: /* get capabilities */
+ {
+ snd_capabilities *p = (snd_capabilities *)arg;
+ pcmchan_caps *pcaps = NULL, *rcaps = NULL;
+ if (rdch) rcaps = chn_getcaps(rdch);
+ if (wrch) pcaps = chn_getcaps(wrch);
+ p->rate_min = max(rcaps? rcaps->minspeed : 0,
+ pcaps? pcaps->minspeed : 0);
+ p->rate_max = min(rcaps? rcaps->maxspeed : 1000000,
+ pcaps? pcaps->maxspeed : 1000000);
+ p->bufsize = min(rdch? rdch->buffer.bufsize : 1000000,
+ wrch? wrch->buffer.bufsize : 1000000);
+ /* XXX bad on sb16 */
+ p->formats = (rcaps? rcaps->formats : 0xffffffff) &
+ (pcaps? pcaps->formats : 0xffffffff);
+ p->mixers = 1; /* default: one mixer */
+ p->inputs = d->mixer.devs;
+ p->left = p->right = 100;
+ }
+ break;
+
+ case AIOSTOP:
+ if (*arg_i == AIOSYNC_PLAY && wrch) *arg_i = chn_abort(wrch);
+ else if (*arg_i == AIOSYNC_CAPTURE && rdch) *arg_i = chn_abort(rdch);
+ else {
+ splx(s);
+ printf("AIOSTOP: bad channel 0x%x\n", *arg_i);
+ *arg_i = 0;
+ }
+ break;
+
+ case AIOSYNC:
+ printf("AIOSYNC chan 0x%03lx pos %lu unimplemented\n",
+ ((snd_sync_parm *)arg)->chan, ((snd_sync_parm *)arg)->pos);
+ break;
+ /*
+ * here follow the standard ioctls (filio.h etc.)
+ */
+ case FIONREAD: /* get # bytes to read */
+ if (rdch && rdch->buffer.dl) chn_dmaupdate(rdch);
+ *arg_i = rdch? rdch->buffer.rl : 0;
+ break;
+
+ case FIOASYNC: /*set/clear async i/o */
+ DEB( printf("FIOASYNC\n") ; )
+ break;
+
+ case SNDCTL_DSP_NONBLOCK:
+ case FIONBIO: /* set/clear non-blocking i/o */
+ if (rdch) rdch->flags &= ~CHN_F_NBIO;
+ if (wrch) wrch->flags &= ~CHN_F_NBIO;
+ if (*arg_i) {
+ if (rdch) rdch->flags |= CHN_F_NBIO;
+ if (wrch) wrch->flags |= CHN_F_NBIO;
+ }
+ break;
+
+ /*
+ * Finally, here is the linux-compatible ioctl interface
+ */
+ #define THE_REAL_SNDCTL_DSP_GETBLKSIZE _IOWR('P', 4, int)
+ case THE_REAL_SNDCTL_DSP_GETBLKSIZE:
+ case SNDCTL_DSP_GETBLKSIZE:
+ *arg_i = wrch? wrch->blocksize : 0; /* XXX rdch? */
+ break ;
+
+ case SNDCTL_DSP_SETBLKSIZE:
+ splx(s);
+ if (wrch) chn_setblocksize(wrch, *arg_i);
+ if (rdch) chn_setblocksize(rdch, *arg_i);
+ break;
+
+ case SNDCTL_DSP_RESET:
+ DEB(printf("dsp reset\n"));
+ if (wrch) chn_abort(wrch);
+ if (rdch) chn_abort(rdch);
+ break;
+
+ case SNDCTL_DSP_SYNC:
+ printf("dsp sync\n");
+ splx(s);
+ if (wrch) chn_sync(wrch, wrch->buffer.bufsize - 4);
+ break;
+
+ case SNDCTL_DSP_SPEED:
+ splx(s);
+ if (wrch) chn_setspeed(wrch, *arg_i);
+ if (rdch) chn_setspeed(rdch, *arg_i);
+ /* fallthru */
+
+ case SOUND_PCM_READ_RATE:
+ *arg_i = wrch? wrch->speed : rdch->speed;
+ break;
+
+ case SNDCTL_DSP_STEREO:
+ splx(s);
+ if (wrch) chn_setformat(wrch, (wrch->format & ~AFMT_STEREO) |
+ ((*arg_i)? AFMT_STEREO : 0));
+ if (rdch) chn_setformat(rdch, (rdch->format & ~AFMT_STEREO) |
+ ((*arg_i)? AFMT_STEREO : 0));
+ *arg_i = ((wrch? wrch->format : rdch->format) & AFMT_STEREO)? 1 : 0;
+ break;
+
+ case SOUND_PCM_WRITE_CHANNELS:
+ splx(s);
+ if (wrch) chn_setformat(wrch, (wrch->format & ~AFMT_STEREO) |
+ ((*arg_i == 2)? AFMT_STEREO : 0));
+ if (rdch) chn_setformat(rdch, (rdch->format & ~AFMT_STEREO) |
+ ((*arg_i == 2)? AFMT_STEREO : 0));
+ /* fallthru */
+
+ case SOUND_PCM_READ_CHANNELS:
+ *arg_i = ((wrch? wrch->format : rdch->format) & AFMT_STEREO)? 2 : 1;
+ break;
+
+ case SNDCTL_DSP_GETFMTS: /* returns a mask of supported fmts */
+ *arg_i = wrch? chn_getcaps(wrch)->formats : chn_getcaps(rdch)->formats;
+ break ;
+
+ case SNDCTL_DSP_SETFMT: /* sets _one_ format */
+ splx(s);
+ if (wrch) chn_setformat(wrch, *arg_i);
+ if (rdch) chn_setformat(rdch, *arg_i);
+ *arg_i = wrch? wrch->format : rdch->format;
+ break;
+
+ case SNDCTL_DSP_SUBDIVIDE:
+ /* XXX watch out, this is RW! */
+ DEB(printf("SNDCTL_DSP_SUBDIVIDE unimplemented\n");)
+ break;
+
+ case SNDCTL_DSP_SETFRAGMENT:
+ /* XXX watch out, this is RW! */
+ DEB(printf("SNDCTL_DSP_SETFRAGMENT 0x%08x\n", *(int *)arg));
+ {
+ int bytes = 1 << min(*arg_i & 0xffff, 16);
+ int count = (*arg_i >> 16) & 0xffff;
+ pcm_channel *c = wrch? wrch : rdch;
+ splx(s);
+ if (rdch) chn_setblocksize(rdch, bytes);
+ if (wrch) chn_setblocksize(wrch, bytes);
+
+ /* eg: 4dwave can only interrupt at buffer midpoint, so
+ * it will force blocksize == bufsize/2
+ */
+ count = c->buffer.bufsize / c->blocksize;
+ bytes = ffs(c->blocksize) - 1;
+ *arg_i = (count << 16) | bytes;
+ }
+ break;
+
+ case SNDCTL_DSP_GETISPACE:
+ /* return space available in the input queue */
+ {
+ audio_buf_info *a = (audio_buf_info *)arg;
+ if (rdch) {
+ snd_dbuf *b = &rdch->buffer;
+ if (b->dl) chn_dmaupdate(rdch);
+ a->bytes = b->fl;
+ a->fragments = 1;
+ a->fragstotal = b->bufsize / rdch->blocksize;
+ a->fragsize = rdch->blocksize;
+ }
+ }
+ break;
+
+ case SNDCTL_DSP_GETOSPACE:
+ /* return space available in the output queue */
+ {
+ audio_buf_info *a = (audio_buf_info *)arg;
+ if (wrch) {
+ snd_dbuf *b = &wrch->buffer;
+ if (b->dl) chn_dmaupdate(wrch);
+ a->bytes = b->fl;
+ a->fragments = 1;
+ a->fragstotal = b->bufsize / wrch->blocksize;
+ a->fragsize = wrch->blocksize;
+ }
+ }
+ break;
+
+ case SNDCTL_DSP_GETIPTR:
+ {
+ count_info *a = (count_info *)arg;
+ if (rdch) {
+ snd_dbuf *b = &rdch->buffer;
+ if (b->dl) chn_dmaupdate(rdch);
+ a->bytes = b->total;
+ a->blocks = (b->total - b->prev_total) / rdch->blocksize;
+ a->ptr = b->fp;
+ b->prev_total += a->blocks * rdch->blocksize;
+ } else ret = EINVAL;
+ }
+ break;
+
+ case SNDCTL_DSP_GETOPTR:
+ {
+ count_info *a = (count_info *)arg;
+ if (wrch) {
+ snd_dbuf *b = &wrch->buffer;
+ if (b->dl) chn_dmaupdate(wrch);
+ a->bytes = b->total;
+ a->blocks = (b->total - b->prev_total) / wrch->blocksize;
+ a->ptr = b->rp;
+ b->prev_total += a->blocks * wrch->blocksize;
+ } else ret = EINVAL;
+ }
+ break;
+
+ case SNDCTL_DSP_GETCAPS:
+ *arg_i = DSP_CAP_REALTIME | DSP_CAP_MMAP | DSP_CAP_TRIGGER;
+ if (rdch && wrch && !(d->flags & SD_F_SIMPLEX))
+ *arg_i |= DSP_CAP_DUPLEX;
+ break;
+
+ case SOUND_PCM_READ_BITS:
+ *arg_i = ((wrch? wrch->format : rdch->format) & AFMT_16BIT)? 16 : 8;
+ break;
+
+ case SNDCTL_DSP_SETTRIGGER:
+ if (rdch) {
+ rdch->flags &= ~CHN_F_TRIGGERED;
+ if (*arg_i & PCM_ENABLE_INPUT)
+ rdch->flags |= CHN_F_TRIGGERED;
+ chn_intr(rdch);
+ }
+ if (wrch) {
+ wrch->flags &= ~CHN_F_TRIGGERED;
+ if (*arg_i & PCM_ENABLE_OUTPUT)
+ wrch->flags |= CHN_F_TRIGGERED;
+ chn_intr(wrch);
+ }
+ break;
+
+ case SNDCTL_DSP_GETTRIGGER:
+ *arg_i = 0;
+ if (wrch && wrch->flags & CHN_F_TRIGGERED)
+ *arg_i |= PCM_ENABLE_OUTPUT;
+ if (rdch && rdch->flags & CHN_F_TRIGGERED)
+ *arg_i |= PCM_ENABLE_INPUT;
+ break;
+
+ case SNDCTL_DSP_MAPINBUF:
+ case SNDCTL_DSP_MAPOUTBUF:
+ case SNDCTL_DSP_SETSYNCRO:
+ /* undocumented */
+
+ case SNDCTL_DSP_POST:
+ case SOUND_PCM_WRITE_FILTER:
+ case SOUND_PCM_READ_FILTER:
+ /* dunno what these do, don't sound important */
+ default:
+ DEB(printf("default ioctl snd%d fn 0x%08x fail\n", unit, cmd));
+ ret = EINVAL;
+ break;
+ }
+ splx(s);
+ return ret;
+}
+
+int
+dsp_poll(snddev_info *d, int chan, int events, struct proc *p)
+{
+ int ret = 0, e;
+ pcm_channel *wrch = NULL, *rdch = NULL;
+
+ getchns(d, chan, &rdch, &wrch);
+ e = events & (POLLOUT | POLLWRNORM);
+ if (wrch && e) ret |= chn_poll(wrch, e, p);
+ e = events & (POLLIN | POLLRDNORM);
+ if (rdch && e) ret |= chn_poll(rdch, e, p);
+ return ret;
+}
+
+int
+dsp_mmap(snddev_info *d, int chan, vm_offset_t offset, int nprot)
+{
+ pcm_channel *wrch = NULL, *rdch = NULL, *c = NULL;
+
+ getchns(d, chan, &rdch, &wrch);
+ /* XXX this is broken by line 204 of vm/device_pager.c, so force write buffer */
+ if (1 || (wrch && (nprot & PROT_WRITE))) c = wrch;
+ else if (rdch && (nprot & PROT_READ)) c = rdch;
+ if (c) {
+ c->flags |= CHN_F_MAPPED;
+ return atop(vtophys(c->buffer.buf + offset));
+ }
+ return -1;
+}
+
diff --git a/sys/dev/sound/pcm/dsp.h b/sys/dev/sound/pcm/dsp.h
new file mode 100644
index 0000000..27b7e3d
--- /dev/null
+++ b/sys/dev/sound/pcm/dsp.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk>
+ * 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$
+ */
+
+int dsp_open(snddev_info *d, int chan, int oflags, int devtype);
+int dsp_close(snddev_info *d, int chan, int devtype);
+int dsp_read(snddev_info *d, int chan, struct uio *buf, int flag);
+int dsp_write(snddev_info *d, int chan, struct uio *buf, int flag);
+int dsp_ioctl(snddev_info *d, int chan, u_long cmd, caddr_t arg);
+int dsp_poll(snddev_info *d, int chan, int events, struct proc *p);
+int dsp_mmap(snddev_info *d, int chan, vm_offset_t offset, int nprot);
+
+
diff --git a/sys/dev/sound/pcm/fake.c b/sys/dev/sound/pcm/fake.c
new file mode 100644
index 0000000..d050eef
--- /dev/null
+++ b/sys/dev/sound/pcm/fake.c
@@ -0,0 +1,115 @@
+/*
+ * Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk>
+ * 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$
+ */
+
+#include "pcm.h"
+
+#include <dev/pcm/sound.h>
+
+/* channel interface */
+static void *fkchan_init(void *devinfo, snd_dbuf *b, pcm_channel *c, int dir);
+static int fkchan_setdir(void *data, int dir);
+static int fkchan_setformat(void *data, u_int32_t format);
+static int fkchan_setspeed(void *data, u_int32_t speed);
+static int fkchan_setblocksize(void *data, u_int32_t blocksize);
+static int fkchan_trigger(void *data, int go);
+static int fkchan_getptr(void *data);
+static pcmchan_caps *fkchan_getcaps(void *data);
+
+static pcmchan_caps fk_caps = {
+ 4000, 48000,
+ AFMT_STEREO | AFMT_U8 | AFMT_S8 | AFMT_S16_LE | AFMT_S16_BE | AFMT_U16_LE | AFMT_U16_BE
+};
+
+static pcm_channel fk_chantemplate = {
+ fkchan_init,
+ fkchan_setdir,
+ fkchan_setformat,
+ fkchan_setspeed,
+ fkchan_setblocksize,
+ fkchan_trigger,
+ fkchan_getptr,
+ fkchan_getcaps,
+};
+
+/* channel interface */
+static void *
+fkchan_init(void *devinfo, snd_dbuf *b, pcm_channel *c, int dir)
+{
+ b->bufsize = 16384;
+ b->buf = malloc(b->bufsize, M_DEVBUF, M_NOWAIT);
+ return (void *)0xbabef00d;
+}
+
+static int
+fkchan_setdir(void *data, int dir)
+{
+ return 0;
+}
+
+static int
+fkchan_setformat(void *data, u_int32_t format)
+{
+ return 0;
+}
+
+static int
+fkchan_setspeed(void *data, u_int32_t speed)
+{
+ return speed;
+}
+
+static int
+fkchan_setblocksize(void *data, u_int32_t blocksize)
+{
+ return blocksize;
+}
+
+static int
+fkchan_trigger(void *data, int go)
+{
+ return 0;
+}
+
+static int
+fkchan_getptr(void *data)
+{
+ return 0;
+}
+
+static pcmchan_caps *
+fkchan_getcaps(void *data)
+{
+ return &fk_caps;
+}
+
+int
+fkchan_setup(pcm_channel *c)
+{
+ *c = fk_chantemplate;
+ return 0;
+}
diff --git a/sys/dev/sound/pcm/feeder.c b/sys/dev/sound/pcm/feeder.c
new file mode 100644
index 0000000..3e51d74
--- /dev/null
+++ b/sys/dev/sound/pcm/feeder.c
@@ -0,0 +1,403 @@
+/*
+ * Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk>
+ * 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$
+ */
+
+#include <dev/pcm/sound.h>
+
+static int chn_addfeeder(pcm_channel *c, pcm_feeder *f);
+static int chn_removefeeder(pcm_channel *c);
+
+#define FEEDBUFSZ 8192
+
+static unsigned char ulaw_to_u8[] = {
+ 3, 7, 11, 15, 19, 23, 27, 31,
+ 35, 39, 43, 47, 51, 55, 59, 63,
+ 66, 68, 70, 72, 74, 76, 78, 80,
+ 82, 84, 86, 88, 90, 92, 94, 96,
+ 98, 99, 100, 101, 102, 103, 104, 105,
+ 106, 107, 108, 109, 110, 111, 112, 113,
+ 113, 114, 114, 115, 115, 116, 116, 117,
+ 117, 118, 118, 119, 119, 120, 120, 121,
+ 121, 121, 122, 122, 122, 122, 123, 123,
+ 123, 123, 124, 124, 124, 124, 125, 125,
+ 125, 125, 125, 125, 126, 126, 126, 126,
+ 126, 126, 126, 126, 127, 127, 127, 127,
+ 127, 127, 127, 127, 127, 127, 127, 127,
+ 128, 128, 128, 128, 128, 128, 128, 128,
+ 128, 128, 128, 128, 128, 128, 128, 128,
+ 128, 128, 128, 128, 128, 128, 128, 128,
+ 253, 249, 245, 241, 237, 233, 229, 225,
+ 221, 217, 213, 209, 205, 201, 197, 193,
+ 190, 188, 186, 184, 182, 180, 178, 176,
+ 174, 172, 170, 168, 166, 164, 162, 160,
+ 158, 157, 156, 155, 154, 153, 152, 151,
+ 150, 149, 148, 147, 146, 145, 144, 143,
+ 143, 142, 142, 141, 141, 140, 140, 139,
+ 139, 138, 138, 137, 137, 136, 136, 135,
+ 135, 135, 134, 134, 134, 134, 133, 133,
+ 133, 133, 132, 132, 132, 132, 131, 131,
+ 131, 131, 131, 131, 130, 130, 130, 130,
+ 130, 130, 130, 130, 129, 129, 129, 129,
+ 129, 129, 129, 129, 129, 129, 129, 129,
+ 128, 128, 128, 128, 128, 128, 128, 128,
+ 128, 128, 128, 128, 128, 128, 128, 128,
+ 128, 128, 128, 128, 128, 128, 128, 128,
+};
+
+static unsigned char u8_to_ulaw[] = {
+ 0, 0, 0, 0, 0, 1, 1, 1,
+ 1, 2, 2, 2, 2, 3, 3, 3,
+ 3, 4, 4, 4, 4, 5, 5, 5,
+ 5, 6, 6, 6, 6, 7, 7, 7,
+ 7, 8, 8, 8, 8, 9, 9, 9,
+ 9, 10, 10, 10, 10, 11, 11, 11,
+ 11, 12, 12, 12, 12, 13, 13, 13,
+ 13, 14, 14, 14, 14, 15, 15, 15,
+ 15, 16, 16, 17, 17, 18, 18, 19,
+ 19, 20, 20, 21, 21, 22, 22, 23,
+ 23, 24, 24, 25, 25, 26, 26, 27,
+ 27, 28, 28, 29, 29, 30, 30, 31,
+ 31, 32, 33, 34, 35, 36, 37, 38,
+ 39, 40, 41, 42, 43, 44, 45, 46,
+ 47, 49, 51, 53, 55, 57, 59, 61,
+ 63, 66, 70, 74, 78, 84, 92, 104,
+ 254, 231, 219, 211, 205, 201, 197, 193,
+ 190, 188, 186, 184, 182, 180, 178, 176,
+ 175, 174, 173, 172, 171, 170, 169, 168,
+ 167, 166, 165, 164, 163, 162, 161, 160,
+ 159, 159, 158, 158, 157, 157, 156, 156,
+ 155, 155, 154, 154, 153, 153, 152, 152,
+ 151, 151, 150, 150, 149, 149, 148, 148,
+ 147, 147, 146, 146, 145, 145, 144, 144,
+ 143, 143, 143, 143, 142, 142, 142, 142,
+ 141, 141, 141, 141, 140, 140, 140, 140,
+ 139, 139, 139, 139, 138, 138, 138, 138,
+ 137, 137, 137, 137, 136, 136, 136, 136,
+ 135, 135, 135, 135, 134, 134, 134, 134,
+ 133, 133, 133, 133, 132, 132, 132, 132,
+ 131, 131, 131, 131, 130, 130, 130, 130,
+ 129, 129, 129, 129, 128, 128, 128, 128,
+};
+
+/*****************************************************************************/
+
+static int
+feed_root(pcm_feeder *feeder, u_int8_t *buffer, u_int32_t count, struct uio *stream)
+{
+ int ret, tmp;
+ if (!count) panic("feed_root: count == 0");
+ tmp = stream->uio_resid;
+ ret = uiomove(buffer, count, stream);
+ if (ret) panic("feed_root: uiomove failed");
+ tmp -= stream->uio_resid;
+ if (!tmp) panic("feed_root: uiomove didn't");
+ return tmp;
+}
+pcm_feeder feeder_root = { "root", NULL, NULL, feed_root };
+
+/*****************************************************************************/
+
+static int
+feed_8to16(pcm_feeder *f, u_int8_t *b, u_int32_t count, struct uio *stream)
+{
+ int i, j, k;
+ k = f->source->feed(f->source, b, count / 2, stream);
+ j = k - 1;
+ i = j * 2 + 1;
+ while (i > 0 && j >= 0) {
+ b[i--] = b[j--];
+ b[i--] = 0;
+ }
+ return k * 2;
+}
+static pcm_feeder feeder_8to16 = { "8to16", NULL, NULL, feed_8to16 };
+
+/*****************************************************************************/
+
+static int
+feed_16to8_init(pcm_feeder *f)
+{
+ f->data = malloc(FEEDBUFSZ, M_DEVBUF, M_NOWAIT);
+ return (f->data == NULL);
+}
+
+static int
+feed_16to8_free(pcm_feeder *f)
+{
+ if (f->data) free(f->data, M_DEVBUF);
+ f->data = NULL;
+ return 0;
+}
+
+static int
+feed_16to8le(pcm_feeder *f, u_int8_t *b, u_int32_t count, struct uio *stream)
+{
+ u_int32_t i = 0, toget = count * 2;
+ int j = 1, k;
+ k = f->source->feed(f->source, f->data, min(toget, FEEDBUFSZ), stream);
+ while (j < k) {
+ b[i++] = ((u_int8_t *)f->data)[j];
+ j += 2;
+ }
+ return i;
+}
+static pcm_feeder feeder_16to8le =
+ { "16to8le", feed_16to8_init, feed_16to8_free, feed_16to8le };
+
+/*****************************************************************************/
+
+static int
+feed_monotostereo8(pcm_feeder *f, u_int8_t *b, u_int32_t count, struct uio *stream)
+{
+ int i, j, k = f->source->feed(f->source, b, count / 2, stream);
+ j = k - 1;
+ i = j * 2 + 1;
+ while (i > 0 && j >= 0) {
+ b[i--] = b[j];
+ b[i--] = b[j];
+ j--;
+ }
+ return k * 2;
+}
+static pcm_feeder feeder_monotostereo8 =
+ { "monotostereo8", NULL, NULL, feed_monotostereo8 };
+
+/*****************************************************************************/
+
+static int
+feed_stereotomono8_init(pcm_feeder *f)
+{
+ f->data = malloc(FEEDBUFSZ, M_DEVBUF, M_NOWAIT);
+ return (f->data == NULL);
+}
+
+static int
+feed_stereotomono8_free(pcm_feeder *f)
+{
+ if (f->data) free(f->data, M_DEVBUF);
+ f->data = NULL;
+ return 0;
+}
+
+static int
+feed_stereotomono8(pcm_feeder *f, u_int8_t *b, u_int32_t count, struct uio *stream)
+{
+ u_int32_t i = 0, toget = count * 2;
+ int j = 0, k;
+ k = f->source->feed(f->source, f->data, min(toget, FEEDBUFSZ), stream);
+ while (j < k) {
+ b[i++] = ((u_int8_t *)f->data)[j];
+ j += 2;
+ }
+ return i;
+}
+static pcm_feeder feeder_stereotomono8 =
+ { "stereotomono8", feed_stereotomono8_init, feed_stereotomono8_free,
+ feed_stereotomono8 };
+
+/*****************************************************************************/
+
+static int
+feed_endian(pcm_feeder *f, u_int8_t *b, u_int32_t count, struct uio *stream)
+{
+ u_int8_t t;
+ int i = 0, j = f->source->feed(f->source, b, count, stream);
+ while (i < j) {
+ t = b[i];
+ b[i] = b[i + 1];
+ b[i + 1] = t;
+ i += 2;
+ }
+ return count;
+}
+static pcm_feeder feeder_endian = { "endian", NULL, NULL, feed_endian };
+
+/*****************************************************************************/
+
+static int
+feed_sign(pcm_feeder *f, u_int8_t *b, u_int32_t count, struct uio *stream)
+{
+ int i = 0, j = f->source->feed(f->source, b, count, stream);
+ int ssz = (int)f->data, ofs = ssz - 1;
+ while (i < j) {
+ b[i + ofs] ^= 0x80;
+ i += ssz;
+ }
+ return i;
+}
+static pcm_feeder feeder_sign8 =
+ { "sign8", NULL, NULL, feed_sign, (void *)1 };
+static pcm_feeder feeder_sign16 =
+ { "sign16", NULL, NULL, feed_sign, (void *)2 };
+
+/*****************************************************************************/
+
+static int
+feed_table(pcm_feeder *f, u_int8_t *b, u_int32_t count, struct uio *stream)
+{
+ int i = 0, j = f->source->feed(f->source, b, count, stream);
+ while (i < j) {
+ b[i] = ((u_int8_t *)f->data)[b[i]];
+ i++;
+ }
+ return i;
+}
+static pcm_feeder feeder_ulawtou8 =
+ { "ulawtou8", NULL, NULL, feed_table, ulaw_to_u8 };
+static pcm_feeder feeder_u8toulaw =
+ { "u8toulaw", NULL, NULL, feed_table, u8_to_ulaw };
+
+/*****************************************************************************/
+
+struct fmtspec {
+ int stereo;
+ int sign;
+ int bit16;
+ int bigendian;
+ int ulaw;
+ int bad;
+};
+
+struct fmtcvt {
+ pcm_feeder *f;
+ struct fmtspec ispec, ospec;
+};
+
+struct fmtcvt cvttab[] = {
+ {&feeder_ulawtou8, {-1, 0, 0, 0, 1}, {-1, 0, 0, 0, 0}},
+ {&feeder_u8toulaw, {-1, 0, 0, 0, 0}, {-1, 0, 0, 0, 1}},
+ {&feeder_sign8, {-1, 0, 0, 0, 0}, {-1, 1, 0, 0, 0}},
+ {&feeder_sign8, {-1, 1, 0, 0, 0}, {-1, 0, 0, 0, 0}},
+ {&feeder_monotostereo8, { 0, -1, 0, 0, -1}, { 1, -1, 0, 0, -1}},
+ {&feeder_stereotomono8, { 1, -1, 0, 0, -1}, { 0, -1, 0, 0, -1}},
+ {&feeder_sign16, {-1, 0, 1, 0, 0}, {-1, 1, 1, 0, 0}},
+ {&feeder_sign16, {-1, 1, 1, 0, 0}, {-1, 0, 1, 0, 0}},
+ {&feeder_8to16, {-1, -1, 0, 0, 0}, {-1, -1, 1, 0, 0}},
+ {&feeder_16to8le, {-1, -1, 1, 0, 0}, {-1, -1, 0, 0, 0}},
+ {&feeder_endian, {-1, -1, 1, 0, 0}, {-1, -1, 1, 1, 0}},
+ {&feeder_endian, {-1, -1, 1, 1, 0}, {-1, -1, 1, 0, 0}},
+};
+#define FEEDERTABSZ (sizeof(cvttab) / sizeof(struct fmtcvt))
+
+static int
+getspec(u_int32_t fmt, struct fmtspec *spec)
+{
+ spec->stereo = (fmt & AFMT_STEREO)? 1 : 0;
+ spec->sign = (fmt & AFMT_SIGNED)? 1 : 0;
+ spec->bit16 = (fmt & AFMT_16BIT)? 1 : 0;
+ spec->bigendian = (fmt & AFMT_BIGENDIAN)? 1 : 0;
+ spec->ulaw = (fmt & AFMT_MU_LAW)? 1 : 0;
+ spec->bad = (fmt & (AFMT_A_LAW | AFMT_MPEG))? 1 : 0;
+ return 0;
+}
+
+static int
+cmp(int x, int y)
+{
+ return (x == -1 || x == y || y == -1)? 1 : 0;
+}
+
+static int
+cmpspec(struct fmtspec *x, struct fmtspec *y)
+{
+ int i = 0;
+ if (cmp(x->stereo, y->stereo)) i |= 0x01;
+ if (cmp(x->sign, y->sign)) i |= 0x02;
+ if (cmp(x->bit16, y->bit16)) i |= 0x04;
+ if (cmp(x->bigendian, y->bigendian)) i |= 0x08;
+ if (cmp(x->ulaw, y->ulaw)) i |= 0x10;
+ return i;
+}
+
+static int
+cvtapply(pcm_channel *c, struct fmtcvt *cvt, struct fmtspec *s)
+{
+ int i = cmpspec(s, &cvt->ospec);
+ chn_addfeeder(c, cvt->f);
+ if (cvt->ospec.stereo != -1) s->stereo = cvt->ospec.stereo;
+ if (cvt->ospec.sign != -1) s->sign = cvt->ospec.sign;
+ if (cvt->ospec.bit16 != -1) s->bit16 = cvt->ospec.bit16;
+ if (cvt->ospec.bigendian != -1) s->bigendian = cvt->ospec.bigendian;
+ if (cvt->ospec.ulaw != -1) s->ulaw = cvt->ospec.ulaw;
+ return i;
+}
+
+int
+chn_feedchain(pcm_channel *c)
+{
+ int i, chosen, iter;
+ u_int32_t mask;
+ struct fmtspec s, t;
+ struct fmtcvt *e;
+
+ while (chn_removefeeder(c) != -1);
+ if ((c->format & chn_getcaps(c)->formats) == c->format)
+ return c->format;
+ getspec(c->format, &s);
+ if (s.bad) return -1;
+ getspec(chn_getcaps(c)->bestfmt, &t);
+ mask = (~cmpspec(&s, &t)) & 0x1f;
+ iter = 0;
+ do {
+ if (mask == 0 || iter >= 8) break;
+ chosen = -1;
+ for (i = 0; i < FEEDERTABSZ && chosen == -1; i++) {
+ e = &cvttab[i];
+ if ((cmpspec(&s, &e->ispec) == 0x1f) &&
+ ((~cmpspec(&e->ispec, &e->ospec)) & mask))
+ chosen = i;
+ }
+ if (chosen != -1) mask &= cvtapply(c, &cvttab[chosen], &s);
+ iter++;
+ } while (chosen != -1);
+ return (iter < 8)? chn_getcaps(c)->bestfmt : -1;
+}
+
+static int
+chn_addfeeder(pcm_channel *c, pcm_feeder *f)
+{
+ pcm_feeder *n;
+ n = malloc(sizeof(pcm_feeder), M_DEVBUF, M_NOWAIT);
+ *n = *f;
+ n->source = c->feeder;
+ c->feeder = n;
+ if (n->init) n->init(n);
+ return 0;
+}
+
+static int
+chn_removefeeder(pcm_channel *c)
+{
+ pcm_feeder *f;
+ if (c->feeder == &feeder_root) return -1;
+ f = c->feeder->source;
+ if (c->feeder->free) c->feeder->free(c->feeder);
+ free(c->feeder, M_DEVBUF);
+ c->feeder = f;
+ return 0;
+}
+
diff --git a/sys/dev/sound/pcm/mixer.c b/sys/dev/sound/pcm/mixer.c
new file mode 100644
index 0000000..0a7c918
--- /dev/null
+++ b/sys/dev/sound/pcm/mixer.c
@@ -0,0 +1,202 @@
+/*
+ * Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk>
+ * 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$
+ */
+
+#include <dev/pcm/sound.h>
+
+static u_int16_t snd_mixerdefaults[SOUND_MIXER_NRDEVICES] = {
+ [SOUND_MIXER_VOLUME] = 75,
+ [SOUND_MIXER_BASS] = 50,
+ [SOUND_MIXER_TREBLE] = 50,
+ [SOUND_MIXER_PCM] = 75,
+ [SOUND_MIXER_SPEAKER] = 75,
+ [SOUND_MIXER_LINE] = 75,
+ [SOUND_MIXER_MIC] = 0,
+ [SOUND_MIXER_CD] = 75,
+ [SOUND_MIXER_LINE1] = 75,
+ [SOUND_MIXER_VIDEO] = 75,
+ [SOUND_MIXER_RECLEV] = 0,
+};
+
+int
+mixer_init(snddev_info *d, snd_mixer *m, void *devinfo)
+{
+ if (d == NULL) return -1;
+ d->mixer = *m;
+ d->mixer.devinfo = devinfo;
+ bzero(&d->mixer.level, sizeof d->mixer.level);
+ if (d->mixer.init != NULL && d->mixer.init(&d->mixer) == 0) {
+ int i;
+ for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) {
+ u_int16_t v = snd_mixerdefaults[i];
+ mixer_set(d, i, v | (v << 8));
+ }
+ mixer_setrecsrc(d, SOUND_MASK_MIC);
+ return 0;
+ } else return -1;
+}
+
+int
+mixer_set(snddev_info *d, unsigned dev, unsigned lev)
+{
+ if (d == NULL || d->mixer.set == NULL) return -1;
+ if ((dev < SOUND_MIXER_NRDEVICES) && (d->mixer.devs & (1 << dev))) {
+ unsigned l = min((lev & 0x00ff), 100);
+ unsigned r = min(((lev & 0xff00) >> 8), 100);
+ int v = d->mixer.set(&d->mixer, dev, l, r);
+ if (v >= 0) d->mixer.level[dev] = v;
+ return 0;
+ } else return -1;
+}
+
+int
+mixer_get(snddev_info *d, int dev)
+{
+ if (d == NULL) return -1;
+ if (dev < SOUND_MIXER_NRDEVICES && (d->mixer.devs & (1 << dev)))
+ return d->mixer.level[dev];
+ else return -1;
+}
+
+int
+mixer_setrecsrc(snddev_info *d, u_int32_t src)
+{
+ if (d == NULL || d->mixer.setrecsrc == NULL) return -1;
+ src &= d->mixer.recdevs;
+ if (src == 0) src = SOUND_MASK_MIC;
+ d->mixer.recsrc = d->mixer.setrecsrc(&d->mixer, src);
+ return 0;
+}
+
+int
+mixer_getrecsrc(snddev_info *d)
+{
+ if (d == NULL) return -1;
+ return d->mixer.recsrc;
+}
+
+int
+mixer_ioctl(snddev_info *d, u_long cmd, caddr_t arg)
+{
+ int ret, *arg_i = (int *)arg;
+
+ if ((cmd & MIXER_WRITE(0)) == MIXER_WRITE(0)) {
+ int j = cmd & 0xff;
+
+ if (j == SOUND_MIXER_RECSRC) ret = mixer_setrecsrc(d, *arg_i);
+ else ret = mixer_set(d, j, *arg_i);
+ return (ret == 0)? 0 : ENXIO;
+ }
+
+ if ((cmd & MIXER_READ(0)) == MIXER_READ(0)) {
+ int v = -1, j = cmd & 0xff;
+
+ switch (j) {
+ case SOUND_MIXER_DEVMASK:
+ case SOUND_MIXER_CAPS:
+ case SOUND_MIXER_STEREODEVS:
+ v = d->mixer.devs;
+ break;
+
+ case SOUND_MIXER_RECMASK:
+ v = d->mixer.recdevs;
+ break;
+
+ case SOUND_MIXER_RECSRC:
+ v = mixer_getrecsrc(d);
+ break;
+
+ default:
+ v = mixer_get(d, j);
+ }
+ *arg_i = v;
+ return (v != -1)? 0 : ENXIO;
+ }
+ return ENXIO;
+}
+
+void
+mix_setdevs(snd_mixer *m, u_int32_t v)
+{
+ m->devs = v;
+}
+
+void
+mix_setrecdevs(snd_mixer *m, u_int32_t v)
+{
+ m->recdevs = v;
+}
+
+u_int32_t
+mix_getdevs(snd_mixer *m)
+{
+ return m->devs;
+}
+
+u_int32_t
+mix_getrecdevs(snd_mixer *m)
+{
+ return m->recdevs;
+}
+
+void *
+mix_getdevinfo(snd_mixer *m)
+{
+ return m->devinfo;
+}
+
+/*
+ * The various mixers use a variety of bitmasks etc. The Voxware
+ * driver had a very nice technique to describe a mixer and interface
+ * to it. A table defines, for each channel, which register, bits,
+ * offset, polarity to use. This procedure creates the new value
+ * using the table and the old value.
+ */
+
+void
+change_bits(mixer_tab *t, u_char *regval, int dev, int chn, int newval)
+{
+ u_char mask;
+ int shift;
+
+ DEB(printf("ch_bits dev %d ch %d val %d old 0x%02x "
+ "r %d p %d bit %d off %d\n",
+ dev, chn, newval, *regval,
+ (*t)[dev][chn].regno, (*t)[dev][chn].polarity,
+ (*t)[dev][chn].nbits, (*t)[dev][chn].bitoffs ) );
+
+ if ( (*t)[dev][chn].polarity == 1) /* reverse */
+ newval = 100 - newval ;
+
+ mask = (1 << (*t)[dev][chn].nbits) - 1;
+ newval = (int) ((newval * mask) + 50) / 100; /* Scale it */
+ shift = (*t)[dev][chn].bitoffs /*- (*t)[dev][LEFT_CHN].nbits + 1*/;
+
+ *regval &= ~(mask << shift); /* Filter out the previous value */
+ *regval |= (newval & mask) << shift; /* Set the new value */
+}
+
diff --git a/sys/dev/sound/pcm/mixer.h b/sys/dev/sound/pcm/mixer.h
new file mode 100644
index 0000000..82a8f26
--- /dev/null
+++ b/sys/dev/sound/pcm/mixer.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk>
+ * 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$
+ */
+
+extern int mixer_init(snddev_info *d, snd_mixer *m, void *devinfo);
+extern int mixer_set(snddev_info *d, unsigned dev, unsigned lev);
+extern int mixer_get(snddev_info *d, int dev);
+extern int mixer_setrecsrc(snddev_info *d, u_int32_t src);
+extern int mixer_getrecsrc(snddev_info *d);
+extern int mixer_ioctl(snddev_info *d, u_long cmd, caddr_t arg);
+
+extern void change_bits(mixer_tab *t, u_char *regval, int dev, int chn, int newval);
+
+void mix_setdevs(snd_mixer *m, u_int32_t v);
+void mix_setrecdevs(snd_mixer *m, u_int32_t v);
+u_int32_t mix_getdevs(snd_mixer *m);
+u_int32_t mix_getrecdevs(snd_mixer *m);
+void *mix_getdevinfo(snd_mixer *m);
diff --git a/sys/dev/sound/pcm/sound.c b/sys/dev/sound/pcm/sound.c
new file mode 100644
index 0000000..102b87a
--- /dev/null
+++ b/sys/dev/sound/pcm/sound.c
@@ -0,0 +1,438 @@
+/*
+ * Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk>
+ * (C) 1997 Luigi Rizzo (luigi@iet.unipi.it)
+ * 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$
+ */
+
+#include "opt_devfs.h"
+
+#include <dev/pcm/sound.h>
+#ifdef DEVFS
+#include <sys/devfsext.h>
+#endif /* DEVFS */
+
+#if NPCM > 0 /* from "pcm.h" via disgusting #include in snd/sound.h */
+
+extern struct isa_driver pcmdriver;
+
+static int status_isopen = 0;
+static int status_init(char *buf, int size);
+static int status_read(struct uio *buf);
+
+static d_open_t sndopen;
+static d_close_t sndclose;
+static d_ioctl_t sndioctl;
+static d_read_t sndread;
+static d_write_t sndwrite;
+static d_mmap_t sndmmap;
+static d_poll_t sndpoll;
+
+#define CDEV_MAJOR 30
+static struct cdevsw snd_cdevsw = {
+ /* open */ sndopen,
+ /* close */ sndclose,
+ /* read */ sndread,
+ /* write */ sndwrite,
+ /* ioctl */ sndioctl,
+ /* stop */ nostop,
+ /* reset */ noreset,
+ /* devtotty */ nodevtotty,
+ /* poll */ sndpoll,
+ /* mmap */ sndmmap,
+ /* strategy */ nostrategy,
+ /* name */ "snd",
+ /* parms */ noparms,
+ /* maj */ CDEV_MAJOR,
+ /* dump */ nodump,
+ /* psize */ nopsize,
+ /* flags */ 0,
+ /* maxio */ 0,
+ /* bmaj */ -1
+};
+
+/* PROPOSAL:
+each unit needs:
+status, mixer, dsp, dspW, audio, sequencer, midi-in, seq2, sndproc = 9 devices
+dspW and audio are deprecated.
+dsp needs min 64 channels, will give it 256
+
+minor = (unit << 12) + (dev << 8) + channel
+currently minor = (channel << 8) + (unit << 4) + dev
+
+nomenclature:
+ /dev/pcmX/dsp.(0..255)
+ /dev/pcmX/dspW
+ /dev/pcmX/audio
+ /dev/pcmX/status
+ /dev/pcmX/mixer
+ [etc.]
+*/
+
+#define PCMMINOR(x) (minor(x))
+#define PCMCHAN(x) ((PCMMINOR(x) & 0x0000ff00) >> 8)
+#define PCMUNIT(x) ((PCMMINOR(x) & 0x000000f0) >> 4)
+#define PCMDEV(x) (PCMMINOR(x) & 0x0000000f)
+
+static devclass_t pcm_devclass;
+
+static snddev_info *
+gsd(int unit)
+{
+ return devclass_get_softc(pcm_devclass, unit);
+}
+
+int
+pcm_addchan(device_t dev, int dir, pcm_channel *templ, void *devinfo)
+{
+ snddev_info *d = device_get_softc(dev);
+ pcm_channel *ch;
+
+ ch = (dir == PCMDIR_PLAY)? &d->play[d->playcount++] : &d->rec[d->reccount++];
+ *ch = *templ;
+ chn_init(ch, devinfo, dir);
+ d->chancount++;
+ return 0;
+}
+
+int
+pcm_setstatus(device_t dev, char *str)
+{
+ snddev_info *d = device_get_softc(dev);
+ strncpy(d->status, str, SND_STATUSLEN);
+ return 0;
+}
+
+u_int32_t
+pcm_getflags(device_t dev)
+{
+ snddev_info *d = device_get_softc(dev);
+ return d->flags;
+}
+
+void
+pcm_setflags(device_t dev, u_int32_t val)
+{
+ snddev_info *d = device_get_softc(dev);
+ d->flags = val;
+}
+
+/* This is the generic init routine */
+int
+pcm_register(device_t dev, void *devinfo, int numplay, int numrec)
+{
+ int sz, unit = device_get_unit(dev);
+ snddev_info *d = device_get_softc(dev);
+
+ if (!pcm_devclass) {
+ pcm_devclass = device_get_devclass(dev);
+ cdevsw_add(&snd_cdevsw);
+ }
+ d->devinfo = devinfo;
+ d->chancount = d->playcount = d->reccount = 0;
+ sz = (numplay + numrec) * sizeof(pcm_channel *);
+ d->aplay = (pcm_channel **)malloc(sz, M_DEVBUF, M_NOWAIT);
+ if (!d->aplay) goto no;
+ d->arec = (pcm_channel **)malloc(sz, M_DEVBUF, M_NOWAIT);
+ if (!d->arec) goto no;
+ bzero(d->aplay, sz);
+ bzero(d->arec, sz);
+
+ d->play = (pcm_channel *)malloc(numplay * sizeof(pcm_channel),
+ M_DEVBUF, M_NOWAIT);
+ if (!d->play) goto no;
+ d->rec = (pcm_channel *)malloc(numrec * sizeof(pcm_channel),
+ M_DEVBUF, M_NOWAIT);
+ if (!d->rec) goto no;
+ bzero(d->play, numplay * sizeof(pcm_channel));
+ bzero(d->rec, numrec * sizeof(pcm_channel));
+
+ fkchan_setup(&d->fakechan);
+ chn_init(&d->fakechan, NULL, 0);
+ d->magic = MAGIC(unit); /* debugging... */
+
+ return 0;
+no:
+ if (d->aplay) free(d->aplay, M_DEVBUF);
+ if (d->play) free(d->play, M_DEVBUF);
+ if (d->arec) free(d->arec, M_DEVBUF);
+ if (d->rec) free(d->rec, M_DEVBUF);
+ return ENXIO;
+}
+
+/*
+ * a small utility function which, given a device number, returns
+ * a pointer to the associated snddev_info struct, and sets the unit
+ * number.
+ */
+static snddev_info *
+get_snddev_info(dev_t i_dev, int *unit, int *dev, int *chan)
+{
+ int u, d, c;
+
+ u = PCMUNIT(i_dev);
+ d = PCMDEV(i_dev);
+ c = PCMCHAN(i_dev);
+ if (u > devclass_get_maxunit(pcm_devclass)) u = -1;
+ if (unit) *unit = u;
+ if (dev) *dev = d;
+ if (chan) *chan = c;
+ if (u < 0) return NULL;
+
+ switch(d) {
+ case SND_DEV_CTL: /* /dev/mixer handled by pcm */
+ case SND_DEV_STATUS: /* /dev/sndstat handled by pcm */
+ case SND_DEV_DSP:
+ case SND_DEV_DSP16:
+ case SND_DEV_AUDIO:
+ return gsd(u);
+
+ case SND_DEV_SEQ: /* XXX when enabled... */
+ case SND_DEV_SEQ2:
+ case SND_DEV_MIDIN:
+ case SND_DEV_SNDPROC: /* /dev/sndproc handled by pcm */
+ default:
+ printf("unsupported subdevice %d\n", d);
+ return NULL;
+ }
+}
+
+static int
+sndopen(dev_t i_dev, int flags, int mode, struct proc *p)
+{
+ int dev, unit, chan;
+ snddev_info *d = get_snddev_info(i_dev, &unit, &dev, &chan);
+
+ DEB(printf("open snd%d subdev %d flags 0x%08x mode 0x%08x\n",
+ unit, dev, flags, mode));
+
+ switch(dev) {
+ case SND_DEV_STATUS:
+ if (status_isopen) return EBUSY;
+ status_isopen = 1;
+ return 0;
+
+ case SND_DEV_CTL:
+ return d? 0 : ENXIO;
+
+ case SND_DEV_AUDIO:
+ case SND_DEV_DSP:
+ case SND_DEV_DSP16:
+ return d? dsp_open(d, chan, flags, dev) : ENXIO;
+
+ default:
+ return ENXIO;
+ }
+}
+
+static int
+sndclose(dev_t i_dev, int flags, int mode, struct proc *p)
+{
+ int dev, unit, chan;
+ snddev_info *d = get_snddev_info(i_dev, &unit, &dev, &chan);
+
+ DEB(printf("close snd%d subdev %d\n", unit, dev));
+
+ switch(dev) { /* only those for which close makes sense */
+ case SND_DEV_STATUS:
+ if (!status_isopen) return EBADF;
+ status_isopen = 0;
+ return 0;
+
+ case SND_DEV_CTL:
+ return d? 0 : ENXIO;
+
+ case SND_DEV_AUDIO:
+ case SND_DEV_DSP:
+ case SND_DEV_DSP16:
+ return d? dsp_close(d, chan, dev) : ENXIO;
+
+ default:
+ return ENXIO;
+ }
+}
+
+static int
+sndread(dev_t i_dev, struct uio *buf, int flag)
+{
+ int dev, unit, chan;
+ snddev_info *d = get_snddev_info(i_dev, &unit, &dev, &chan);
+ DEB(printf("read snd%d subdev %d flag 0x%08x\n", unit, dev, flag));
+
+ switch(dev) {
+ case SND_DEV_STATUS:
+ return status_isopen? status_read(buf) : EBADF;
+
+ case SND_DEV_AUDIO:
+ case SND_DEV_DSP:
+ case SND_DEV_DSP16:
+ return d? dsp_read(d, chan, buf, flag) : EBADF;
+
+ default:
+ return ENXIO;
+ }
+}
+
+static int
+sndwrite(dev_t i_dev, struct uio *buf, int flag)
+{
+ int dev, unit, chan;
+ snddev_info *d = get_snddev_info(i_dev, &unit, &dev, &chan);
+
+ DEB(printf("write snd%d subdev %d flag 0x%08x\n", unit, dev & 0xf, flag));
+
+ switch(dev) { /* only writeable devices */
+ case SND_DEV_DSP:
+ case SND_DEV_DSP16:
+ case SND_DEV_AUDIO:
+ return d? dsp_write(d, chan, buf, flag) : EBADF;
+
+ default:
+ return EPERM; /* for non-writeable devices ; */
+ }
+}
+
+static int
+sndioctl(dev_t i_dev, u_long cmd, caddr_t arg, int mode, struct proc * p)
+{
+ int dev, chan;
+ snddev_info *d = get_snddev_info(i_dev, NULL, &dev, &chan);
+
+ if (d == NULL) return ENXIO;
+
+ switch(dev) {
+ case SND_DEV_CTL:
+ return mixer_ioctl(d, cmd, arg);
+
+ case SND_DEV_AUDIO:
+ case SND_DEV_DSP:
+ case SND_DEV_DSP16:
+ return dsp_ioctl(d, chan, cmd, arg);
+
+ default:
+ return ENXIO;
+ }
+}
+
+static int
+sndpoll(dev_t i_dev, int events, struct proc *p)
+{
+ int dev, chan;
+ snddev_info *d = get_snddev_info(i_dev, NULL, &dev, &chan);
+
+ DEB(printf("sndpoll dev 0x%04x events 0x%08x\n", i_dev, events));
+
+ if (d == NULL) return ENXIO;
+
+ switch(dev) {
+ case SND_DEV_AUDIO:
+ case SND_DEV_DSP:
+ case SND_DEV_DSP16:
+ return dsp_poll(d, chan, events, p);
+
+ default:
+ return (events &
+ (POLLIN | POLLOUT | POLLRDNORM | POLLWRNORM)) | POLLHUP;
+ }
+}
+
+/*
+ * The mmap interface allows access to the play and read buffer,
+ * plus the device descriptor.
+ * The various blocks are accessible at the following offsets:
+ *
+ * 0x00000000 ( 0 ) : write buffer ;
+ * 0x01000000 (16 MB) : read buffer ;
+ * 0x02000000 (32 MB) : device descriptor (dangerous!)
+ *
+ * WARNING: the mmap routines assume memory areas are aligned. This
+ * is true (probably) for the dma buffers, but likely false for the
+ * device descriptor. As a consequence, we do not know where it is
+ * located in the requested area.
+ */
+static int
+sndmmap(dev_t i_dev, vm_offset_t offset, int nprot)
+{
+ int unit, dev, chan;
+ snddev_info *d = get_snddev_info(i_dev, &unit, &dev, &chan);
+
+ DEB(printf("sndmmap d 0x%p dev 0x%04x ofs 0x%08x nprot 0x%08x\n",
+ d, dev, offset, nprot));
+
+ if (d == NULL || nprot & PROT_EXEC) return -1; /* forbidden */
+
+ switch(dev) {
+ case SND_DEV_AUDIO:
+ case SND_DEV_DSP:
+ case SND_DEV_DSP16:
+ return dsp_mmap(d, chan, offset, nprot);
+
+ default:
+ return -1;
+ }
+}
+
+static int
+status_init(char *buf, int size)
+{
+ int i;
+ device_t dev;
+ snddev_info *d;
+
+ snprintf(buf, size, "FreeBSD Audio Driver (newpcm) %s %s\n"
+ "Installed devices:\n", __DATE__, __TIME__);
+
+ for (i = 0; i <= devclass_get_maxunit(pcm_devclass); i++) {
+ d = gsd(i);
+ if (!d) continue;
+ dev = devclass_get_device(pcm_devclass, i);
+ if (1) snprintf(buf + strlen(buf), size - strlen(buf),
+ "pcm%d: <%s> %s (%d/%d channels%s)\n",
+ i, device_get_desc(dev), d->status,
+ d->playcount, d->reccount,
+ (!(d->flags & SD_F_SIMPLEX))? " duplex" : "");
+ }
+ return strlen(buf);
+}
+
+static int
+status_read(struct uio *buf)
+{
+ static char status_buf[4096];
+ static int bufptr = 0, buflen = 0;
+ int l;
+
+ if (status_isopen == 1) {
+ status_isopen++;
+ bufptr = 0;
+ buflen = status_init(status_buf, sizeof status_buf);
+ }
+
+ l = min(buf->uio_resid, buflen - bufptr);
+ bufptr += l;
+ return (l > 0)? uiomove(status_buf + bufptr - l, l, buf) : 0;
+}
+
+#endif /* NPCM > 0 */
diff --git a/sys/dev/sound/pcm/sound.h b/sys/dev/sound/pcm/sound.h
new file mode 100644
index 0000000..5d17624
--- /dev/null
+++ b/sys/dev/sound/pcm/sound.h
@@ -0,0 +1,181 @@
+/*
+ * Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk>
+ * Copyright by Hannu Savolainen 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.
+ *
+ * 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$
+ */
+
+#ifdef KERNEL
+#include "pcm.h"
+#else
+#error why?
+#define NPCM 1
+#endif
+#if NPCM > 0
+
+/*
+ * first, include kernel header files.
+ */
+
+#ifndef _OS_H_
+#define _OS_H_
+
+#ifdef KERNEL
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/ioccom.h>
+
+#include <sys/filio.h>
+#include <sys/sockio.h>
+#include <sys/fcntl.h>
+#include <sys/tty.h>
+#include <sys/proc.h>
+
+#include <sys/kernel.h> /* for DATA_SET */
+
+#include <sys/module.h>
+#include <sys/conf.h>
+#include <sys/file.h>
+#include <sys/uio.h>
+#include <sys/syslog.h>
+#include <sys/errno.h>
+#include <sys/malloc.h>
+#include <sys/bus.h>
+#include <sys/buf.h>
+#include <machine/clock.h> /* for DELAY */
+#include <machine/resource.h>
+#include <machine/bus_memio.h>
+#include <machine/bus_pio.h>
+#include <machine/bus.h>
+#include <sys/rman.h>
+#include <sys/mman.h>
+#include <sys/poll.h>
+#include <sys/soundcard.h>
+#include <isa/isavar.h>
+#include <vm/vm.h>
+#include <vm/pmap.h>
+
+#else
+struct isa_device { int dummy; };
+#define d_open_t void
+#define d_close_t void
+#define d_read_t void
+#define d_write_t void
+#define d_ioctl_t void
+#define d_select_t void
+#endif /* KERNEL */
+
+#endif /* _OS_H_ */
+
+#include <dev/pcm/datatypes.h>
+#include <dev/pcm/channel.h>
+#include <dev/pcm/mixer.h>
+#include <dev/pcm/dsp.h>
+
+#define MAGIC(unit) (0xa4d10de0 + unit)
+
+#define SD_F_SIMPLEX 0x00000001
+#define SD_F_EVILSB16 0x00000002
+#define SD_F_PRIO_RD 0x10000000
+#define SD_F_PRIO_WR 0x20000000
+#define SD_F_PRIO_SET (SD_F_PRIO_RD | SD_F_PRIO_WR)
+#define SD_F_DIR_SET 0x40000000
+#define SD_F_TRANSIENT 0xf0000000
+
+/* many variables should be reduced to a range. Here define a macro */
+#define RANGE(var, low, high) (var) = \
+ (((var)<(low))? (low) : ((var)>(high))? (high) : (var))
+
+#define DSP_BUFFSIZE (65536 - 256) /* XXX */
+/* the last 256 bytes are room for buggy soundcard to overflow. */
+
+/* make figuring out what a format is easier. got AFMT_STEREO already */
+#define AFMT_16BIT (AFMT_S16_LE | AFMT_S16_BE | AFMT_U16_LE | AFMT_U16_BE)
+#define AFMT_SIGNED (AFMT_S16_LE | AFMT_S16_BE | AFMT_S8)
+#define AFMT_BIGENDIAN (AFMT_S16_BE | AFMT_U16_BE)
+
+int fkchan_setup(pcm_channel *c);
+
+#ifdef KERNEL
+#include "pnp.h"
+#endif /* KERNEL */
+
+/*
+ * Minor numbers for the sound driver.
+ *
+ * Unfortunately Creative called the codec chip of SB as a DSP. For this
+ * reason the /dev/dsp is reserved for digitized audio use. There is a
+ * device for true DSP processors but it will be called something else.
+ * In v3.0 it's /dev/sndproc but this could be a temporary solution.
+ */
+
+#define SND_DEV_CTL 0 /* Control port /dev/mixer */
+#define SND_DEV_SEQ 1 /* Sequencer /dev/sequencer */
+#define SND_DEV_MIDIN 2 /* Raw midi access */
+#define SND_DEV_DSP 3 /* Digitized voice /dev/dsp */
+#define SND_DEV_AUDIO 4 /* Sparc compatible /dev/audio */
+#define SND_DEV_DSP16 5 /* Like /dev/dsp but 16 bits/sample */
+#define SND_DEV_STATUS 6 /* /dev/sndstat */
+ /* #7 not in use now. */
+#define SND_DEV_SEQ2 8 /* /dev/sequencer, level 2 interface */
+#define SND_DEV_SNDPROC 9 /* /dev/sndproc for programmable devices */
+#define SND_DEV_PSS SND_DEV_SNDPROC /* ? */
+
+#define DSP_DEFAULT_SPEED 8000
+
+#define ON 1
+#define OFF 0
+
+#ifdef KERNEL
+
+/*
+ * some macros for debugging purposes
+ * DDB/DEB to enable/disable debugging stuff
+ * BVDDB to enable debugging when bootverbose
+ */
+#define DDB(x) x /* XXX */
+#define BVDDB(x) if (bootverbose) x
+
+#ifndef DEB
+#define DEB(x)
+#endif
+
+int pcm_addchan(device_t dev, int dir, pcm_channel *templ, void *devinfo);
+int pcm_register(device_t dev, void *devinfo, int numplay, int numrec);
+int pcm_setstatus(device_t dev, char *str);
+u_int32_t pcm_getflags(device_t dev);
+void pcm_setflags(device_t dev, u_int32_t val);
+
+#endif /* KERNEL */
+
+/* usage of flags in device config entry (config file) */
+#define DV_F_DRQ_MASK 0x00000007 /* mask for secondary drq */
+#define DV_F_DUAL_DMA 0x00000010 /* set to use secondary dma channel */
+
+/* ought to be made obsolete */
+#define DV_F_DEV_MASK 0x0000ff00 /* force device type/class */
+#define DV_F_DEV_SHIFT 8 /* force device type/class */
+
+#endif
OpenPOWER on IntegriCloud