summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLuigi Rizzo <luigi@FreeBSD.org>1998-12-31 08:14:27 +0000
committerLuigi Rizzo <luigi@FreeBSD.org>1998-12-31 08:14:27 +0000
commite987b015bd01c858f583ad6ec23b844784cb625f (patch)
treef95284c386a88161b15323f671bd25516728ddca
parent68ec4eb602ad64325fca17c83b81432450931084 (diff)
Notes
-rw-r--r--sys/pci/es1370.c1127
-rw-r--r--sys/pci/es1370_reg.h134
2 files changed, 1261 insertions, 0 deletions
diff --git a/sys/pci/es1370.c b/sys/pci/es1370.c
new file mode 100644
index 0000000000000..7277e7ab14811
--- /dev/null
+++ b/sys/pci/es1370.c
@@ -0,0 +1,1127 @@
+/*
+ * Support the ENSONIQ AudioPCI board based on the ES1370 and Codec
+ * AK4531.
+ *
+ * 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$
+ */
+
+#include "pci.h"
+#include "pcm.h"
+
+#include <sys/param.h>
+#include <sys/queue.h>
+#include <sys/kernel.h>
+#include <machine/bus_pio.h>
+#include <machine/bus_memio.h>
+#include <machine/bus.h>
+#include <pci/pcireg.h>
+#include <pci/pcivar.h>
+
+#include <pci/es1370_reg.h>
+#include <i386/isa/snd/sound.h>
+#include <i386/isa/snd/ulaw.h>
+
+#if NPCI != 0
+
+
+/* -------------------------------------------------------------------- */
+
+/*
+ * #defines
+ */
+
+#ifdef __alpha__
+#define IO_SPACE_MAPPING ALPHA_BUS_SPACE_IO
+#define MEM_SPACE_MAPPING ALPHA_BUS_SPACE_MEM
+#else /* not __alpha__ */
+#define IO_SPACE_MAPPING I386_BUS_SPACE_IO
+#define MEM_SPACE_MAPPING I386_BUS_SPACE_MEM
+#endif /* not __alpha__ */
+
+#define DMA_ALIGN_THRESHOLD 4
+#define DMA_ALIGN_MASK (~(DMA_ALIGN_THRESHOLD - 1))
+#define DMA_READ_THRESHOLD 0x200
+
+#define MEM_MAP_REG 0x14
+
+#define UNIT(minor) ((minor) >> 4)
+#define DEV(minor) ((minor) & 0xf)
+
+
+/* -------------------------------------------------------------------- */
+
+/*
+ * PCI IDs of supported chips
+ */
+
+#define ES1370_PCI_ID 0x50001274
+
+
+/* -------------------------------------------------------------------- */
+
+/*
+ * device private data
+ */
+
+struct es_info {
+ bus_space_tag_t st;
+ bus_space_handle_t sh;
+
+ bus_dma_tag_t parent_dmat;
+ bus_dmamap_t dmam_in, dmam_out;
+
+ /* Contents of board's registers */
+ u_long ctrl;
+ u_long sctrl;
+};
+
+
+/* -------------------------------------------------------------------- */
+
+/*
+ * prototypes
+ */
+
+static void dma_wrintr(snddev_info *);
+static void dma_rdintr(snddev_info *);
+static int es_init(snddev_info *);
+static snd_callback_t es_callback;
+static d_open_t es_dsp_open;
+static d_close_t es_dsp_close;
+static d_ioctl_t es_dsp_ioctl;
+static d_read_t es_dsp_read;
+static d_write_t es_dsp_write;
+static void es_intr(void *);
+static int es_rdabort(snddev_info *);
+static void es_rd_map(void *, bus_dma_segment_t *, int, int);
+static int es_wrabort(snddev_info *);
+static void es_wr_map(void *, bus_dma_segment_t *, int, int);
+static char *es_pci_probe __P((pcici_t, pcidi_t));
+static void es_pci_attach __P((pcici_t, int));
+static int es_rd_dmaupdate(snddev_info *);
+static d_select_t es_select;
+static int es_wr_dmaupdate(snddev_info *);
+static int alloc_dmabuf(snddev_info *, int);
+static int write_codec(snddev_info *, u_char, u_char);
+
+
+/* -------------------------------------------------------------------- */
+
+/*
+ * PCI driver and PCM driver method tables
+ */
+
+static struct pci_device es_pci_driver = {
+ "es",
+ es_pci_probe,
+ es_pci_attach,
+ &nsnd,
+ NULL
+};
+
+DATA_SET(pcidevice_set, es_pci_driver);
+
+static snddev_info es_op_desc = {
+ "ENSONIQ AudioPCI",
+
+ 0, /* type, apparently unused */
+ NULL, /* ISA probe */
+ NULL, /* ISA attach */
+
+ es_dsp_open,
+ es_dsp_close,
+ es_dsp_read,
+ es_dsp_write,
+ es_dsp_ioctl,
+ es_select,
+
+ NULL, /* Interrupt Service Routine */
+ es_callback,
+
+ ES_BUFFSIZE,
+
+ AFMT_FULLDUPLEX | AFMT_STEREO | AFMT_U8 | AFMT_S16_LE, /* brag :-) */
+};
+
+
+/* -------------------------------------------------------------------- */
+
+/*
+ * The mixer interface
+ */
+
+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
+mixer_ioctl(snddev_info *d, u_long cmd, caddr_t data, int fflag, struct proc *p)
+{
+ int i, j, *val, ret = 0;
+
+ val = (int *)data;
+ i = cmd & 0xff;
+
+ switch (cmd & IOC_DIRMASK) {
+ case IOC_IN | IOC_OUT: /* _IOWR */
+ switch (i) {
+ case SOUND_MIXER_RECSRC:
+ for (i = j = 0; i != SOUND_MIXER_NRDEVICES; i++)
+ if ((*val & (1 << i)) != 0) {
+ if (!mixtable[i].recmask)
+ *val &= ~(1 << i);
+ else
+ j |= mixtable[i].recmask;
+ }
+ d->mix_recsrc = *val;
+ write_codec(d, CODEC_LIMIX1, j & 0x55);
+ write_codec(d, CODEC_RIMIX1, j & 0xaa);
+ write_codec(d, CODEC_LIMIX2, (j >> 8) & 0x17);
+ write_codec(d, CODEC_RIMIX2, (j >> 8) & 0x0f);
+ write_codec(d, CODEC_OMIX1, 0x7f);
+ write_codec(d, CODEC_OMIX2, 0x3f);
+ break;
+
+ default:
+ if (i >= SOUND_MIXER_NRDEVICES || !mixtable[i].avail)
+ ret = EINVAL;
+ else {
+ int l, r, rl, rr;
+
+ l = *val & 0xff;
+ if (l > 100)
+ l = 100;
+ if (mixtable[i].left == 0xf) {
+ if (l < 2)
+ rl = 0x80;
+ else
+ rl = 7 - (l - 2) / 14;
+ } else {
+ if (l < 10)
+ rl = 0x80;
+ else
+ rl = 15 - (l - 10) / 6;
+ }
+ if (mixtable[i].stereo) {
+ r = (*val >> 8) & 0xff;
+ if (r > 100)
+ r = 100;
+ if (r < 10)
+ rr = 0x80;
+ else
+ rr = 15 - (r - 10) / 6;
+ write_codec(d, mixtable[i].right, rr);
+ } else
+ r = l;
+ write_codec(d, mixtable[i].left, rl);
+ *val = d->mix_levels[i] = ((u_int) r << 8) | l;
+ }
+ break;
+ }
+ break;
+
+ default:
+ ret = ENOSYS;
+ break;
+ }
+
+ return (ret);
+}
+
+
+/* -------------------------------------------------------------------- */
+
+/*
+ * File operations
+ */
+
+static int
+es_dsp_open(dev_t dev, int oflags, int devtype, struct proc *p)
+{
+ int unit = UNIT(minor(dev));
+ snddev_info *d = &pcm_info[unit];
+
+ if (d->flags & SND_F_BUSY)
+ return (EBUSY);
+ d->flags = 0;
+
+ d->dbuf_out.total = d->dbuf_out.prev_total =
+ d->dbuf_in.total = d->dbuf_in.prev_total = 0;
+
+ switch (DEV(minor(dev))) {
+ case SND_DEV_DSP16:
+ d->play_fmt = d->rec_fmt = AFMT_S16_LE;
+ break;
+
+ case SND_DEV_DSP:
+ d->play_fmt = d->rec_fmt = AFMT_U8;
+ break;
+
+ case SND_DEV_AUDIO:
+ d->play_fmt = d->rec_fmt = AFMT_MU_LAW;
+ break;
+
+ default:
+ return (ENXIO);
+ }
+
+ if ((oflags & FREAD) == 0)
+ d->rec_fmt = 0;
+ else if ((oflags & FWRITE) == 0)
+ d->play_fmt = 0;
+
+ d->play_speed = d->rec_speed = DSP_DEFAULT_SPEED;
+
+ d->flags |= SND_F_BUSY;
+ if (oflags & O_NONBLOCK)
+ d->flags |= SND_F_NBIO;
+
+ ask_init(d);
+
+ return (0);
+}
+
+static int
+es_dsp_close(dev_t dev, int cflags, int devtype, struct proc *p)
+{
+ int unit = UNIT(minor(dev));
+ snddev_info *d = &pcm_info[unit];
+
+ d->flags &= ~SND_F_BUSY;
+
+ es_rdabort(d);
+
+ return (0);
+}
+
+static int
+es_dsp_read(dev_t dev, struct uio *buf, int flag)
+{
+ int l, l1, limit, ret = 0, unit = UNIT(minor(dev));
+ long s;
+ snddev_info *d = &pcm_info[unit];
+ snd_dbuf *b = &d->dbuf_in;
+
+ if (d->flags & SND_F_READING) {
+ /* This shouldn't happen and is actually silly */
+ tsleep(&s, PZERO, "sndar", hz);
+ return (EBUSY);
+ }
+ d->flags |= SND_F_READING;
+
+ /*
+ * XXX Check for SND_F_INIT. If set, wait for DMA to run empty and
+ * re-initialize the board
+ */
+
+ if (buf->uio_resid - d->rec_blocksize > 0)
+ limit = buf->uio_resid - d->rec_blocksize;
+ else
+ limit = 0;
+
+ while ((l = buf->uio_resid) > limit) {
+ s = spltty();
+ es_rd_dmaupdate(d);
+ if ((l = min(l, b->rl)) == 0) {
+ int timeout;
+ if (b->dl == 0)
+ dma_rdintr(d);
+ if (d->flags & SND_F_NBIO) {
+ splx(s);
+ break;
+ }
+ if (buf->uio_resid - limit > b->dl)
+ timeout = hz;
+ else
+ timeout = 1;
+ splx(s);
+ switch (ret = tsleep((caddr_t)b, PRIBIO | PCATCH,
+ "dsprd", timeout)) {
+ case EINTR:
+ es_rdabort(d);
+ /* FALLTHROUGH */
+
+ case ERESTART:
+ break;
+
+ default:
+ continue;
+ }
+ break;
+ }
+ splx(s);
+
+ if ((l1 = b->bufsize - b->rp) < l) {
+ if (d->flags & SND_F_XLAT8) {
+ translate_bytes(ulaw_dsp, b->buf + b->rp, l1);
+ translate_bytes(ulaw_dsp, b->buf, l - l1);
+ }
+ uiomove(b->buf + b->rp, l1, buf);
+ uiomove(b->buf, l - l1, buf);
+ } else {
+ if (d->flags & SND_F_XLAT8)
+ translate_bytes(ulaw_dsp, b->buf + b->rp, l);
+ uiomove(b->buf + b->rp, l, buf);
+ }
+
+ s = spltty();
+ b->fl += l;
+ b->rl -= l;
+ b->rp = (b->rp + l) % b->bufsize;
+ splx(s);
+ }
+
+ d->flags &= ~SND_F_READING;
+
+ return (ret);
+}
+
+static int
+es_dsp_write(dev_t dev, struct uio *buf, int flag)
+{
+ int l, l1, ret = 0, unit = UNIT(minor(dev));
+ long s;
+ snddev_info *d = &pcm_info[unit];
+ snd_dbuf *b = &d->dbuf_out;
+
+ if (d->flags & SND_F_WRITING) {
+ /* This shouldn't happen and is actually silly */
+ tsleep(&s, PZERO, "sndaw", hz);
+ return (EBUSY);
+ }
+ d->flags |= SND_F_WRITING;
+
+ /*
+ * XXX Check for SND_F_INIT. If set, wait for DMA to run empty and
+ * re-initialize the board
+ */
+
+ while ((l = buf->uio_resid) != 0) {
+ s = spltty();
+ es_wr_dmaupdate(d);
+ if ((l = min(l, b->fl)) == 0) {
+ int timeout;
+ if (d->flags & SND_F_NBIO) {
+ splx(s);
+ break;
+ }
+ if (buf->uio_resid >= b->dl)
+ timeout = hz;
+ else
+ timeout = 1;
+ splx(s);
+ switch (ret = tsleep((caddr_t)b, PRIBIO | PCATCH,
+ "dspwr", timeout)) {
+ case EINTR:
+ es_wrabort(d);
+ /* FALLTHROUGH */
+
+ case ERESTART:
+ break;
+
+ default:
+ continue;
+ }
+ break;
+ }
+ splx(s);
+
+ if ((l1 = b->bufsize - b->fp) < l) {
+ uiomove(b->buf + b->fp, l1, buf);
+ uiomove(b->buf, l - l1, buf);
+ if (d->flags & SND_F_XLAT8) {
+ translate_bytes(ulaw_dsp, b->buf + b->fp, l1);
+ translate_bytes(ulaw_dsp, b->buf, l - l1);
+ }
+ } else {
+ uiomove(b->buf + b->fp, l, buf);
+ if (d->flags & SND_F_XLAT8)
+ translate_bytes(ulaw_dsp, b->buf + b->fp, l);
+ }
+
+ s = spltty();
+ b->rl += l;
+ b->fl -= l;
+ b->fp = (b->fp + l) % b->bufsize;
+ if (b->dl == 0)
+ dma_wrintr(d);
+ splx(s);
+ }
+
+ d->flags &= ~SND_F_WRITING;
+
+ return (ret);
+}
+
+static int
+es_dsp_ioctl(dev_t dev, u_long cmd, caddr_t data, int fflag, struct proc *p)
+{
+ int ret = 0, unit = UNIT(minor(dev));
+ snddev_info *d = &pcm_info[unit];
+ long s;
+
+ if ((cmd & MIXER_WRITE(0)) == MIXER_WRITE(0))
+ return mixer_ioctl(d, cmd, data, fflag, p);
+
+ switch(cmd) {
+ case AIONWRITE:
+ if (d->dbuf_out.dl != 0) {
+ s = spltty();
+ es_wr_dmaupdate(d);
+ splx(s);
+ }
+ *(int *)data = d->dbuf_out.fl;
+ break;
+
+ case FIONREAD:
+ if (d->dbuf_in.dl != 0) {
+ s = spltty();
+ es_rd_dmaupdate(d);
+ splx(s);
+ }
+ *(int *)data = d->dbuf_in.rl;
+ break;
+
+ case SNDCTL_DSP_GETISPACE:
+ {
+ audio_buf_info *a = (audio_buf_info *)data;
+ snd_dbuf *b = &d->dbuf_in;
+ if (b->dl != 0) {
+ s = spltty();
+ es_rd_dmaupdate(d);
+ splx(s);
+ }
+ a->bytes = b->fl;
+ a->fragments = b->fl / d->rec_blocksize;
+ a->fragstotal = b->bufsize / d->rec_blocksize;
+ a->fragsize = d->rec_blocksize;
+ }
+ break;
+
+ case SNDCTL_DSP_GETOSPACE:
+ {
+ audio_buf_info *a = (audio_buf_info *)data;
+ snd_dbuf *b = &d->dbuf_out;
+ if (b->dl != 0) {
+ s = spltty();
+ es_wr_dmaupdate(d);
+ splx(s);
+ }
+ a->bytes = b->fl;
+ a->fragments = b->fl / d->rec_blocksize;
+ a->fragstotal = b->bufsize / d->play_blocksize;
+ a->fragsize = d->play_blocksize;
+ }
+ break;
+
+ case SNDCTL_DSP_GETIPTR:
+ {
+ count_info *c = (count_info *)data;
+ snd_dbuf *b = &d->dbuf_in;
+ if (b->dl != 0) {
+ s = spltty();
+ es_rd_dmaupdate(d);
+ splx(s);
+ }
+ c->bytes = b->total;
+ c->blocks = (b->total - b->prev_total +
+ d->rec_blocksize - 1) / d->rec_blocksize;
+ c->ptr = b->fp;
+ b->prev_total = b->total;
+ }
+ break;
+
+ case SNDCTL_DSP_GETOPTR:
+ {
+ count_info *c = (count_info *)data;
+ snd_dbuf *b = &d->dbuf_out;
+ if (b->dl != 0) {
+ s = spltty();
+ es_wr_dmaupdate(d);
+ splx(s);
+ }
+ c->bytes = b->total;
+ c->blocks = (b->total - b->prev_total +
+ d->play_blocksize - 1) / d->play_blocksize;
+ c->ptr = b->rp;
+ b->prev_total = b->total;
+ }
+ break;
+
+ case AIOSTOP:
+ case SNDCTL_DSP_RESET:
+ case SNDCTL_DSP_SYNC:
+ ret = EINVAL;
+ break;
+
+ default:
+ ret = ENOSYS;
+ break;
+ }
+ return (ret);
+}
+
+static int
+es_select(dev_t i_dev, int rw, struct proc * p)
+{
+ return (ENOSYS);
+}
+
+
+/* -------------------------------------------------------------------- */
+
+/*
+ * The interrupt handler
+ */
+
+static void
+es_intr (void *p)
+{
+ snddev_info *d = (snddev_info *)p;
+ struct es_info *es = (struct es_info *)d->device_data;
+ 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)
+ dma_wrintr(d);
+ if (intsrc & STAT_ADC)
+ dma_rdintr(d);
+}
+
+
+/* -------------------------------------------------------------------- */
+
+/*
+ * DMA hassle
+ */
+
+static int
+alloc_dmabuf(snddev_info *d, int rd)
+{
+ struct es_info *es = (struct es_info *)d->device_data;
+ snd_dbuf *b = rd ? &d->dbuf_in : &d->dbuf_out;
+ bus_dmamap_t *dmam = rd ? &es->dmam_in : &es->dmam_out;
+
+ if (bus_dmamem_alloc(es->parent_dmat, (void **)&b->buf, BUS_DMA_NOWAIT,
+ dmam) != 0 ||
+ bus_dmamap_load(es->parent_dmat, *dmam, b->buf, d->bufsize,
+ rd ? es_rd_map : es_wr_map, es, 0) != 0)
+ return -1;
+
+ b->rp = b->fp = b->dl = b->rl = 0;
+ b->fl = b->bufsize = d->bufsize;
+ return (0);
+}
+
+static int
+es_wr_dmaupdate(snddev_info *d)
+{
+ struct es_info *es = (struct es_info *)d->device_data;
+ unsigned hwptr, delta;
+
+ bus_space_write_4(es->st, es->sh, ES1370_REG_MEMPAGE,
+ ES1370_REG_DAC2_FRAMECNT >> 8);
+ hwptr = (bus_space_read_4(es->st, es->sh,
+ ES1370_REG_DAC2_FRAMECNT & 0xff) >> 14) & 0x3fffc;
+ delta = (d->dbuf_out.bufsize + hwptr - d->dbuf_out.rp) %
+ d->dbuf_out.bufsize;
+ d->dbuf_out.rp = hwptr;
+ d->dbuf_out.rl -= delta;
+ d->dbuf_out.fl += delta;
+ d->dbuf_out.total += delta;
+
+ return delta;
+}
+
+static int
+es_rd_dmaupdate(snddev_info *d)
+{
+ struct es_info *es = (struct es_info *)d->device_data;
+ unsigned hwptr, delta;
+
+ bus_space_write_4(es->st, es->sh, ES1370_REG_MEMPAGE,
+ ES1370_REG_ADC_FRAMECNT >> 8);
+ hwptr = (bus_space_read_4(es->st, es->sh,
+ ES1370_REG_ADC_FRAMECNT & 0xff) >> 14) & 0x3fffc;
+ delta = (d->dbuf_in.bufsize + hwptr - d->dbuf_in.fp) %
+ d->dbuf_in.bufsize;
+ d->dbuf_in.fp = hwptr;
+ d->dbuf_in.rl += delta;
+ d->dbuf_in.fl -= delta;
+ d->dbuf_in.total += delta;
+ return delta;
+}
+
+
+/* -------------------------------------------------------------------- */
+
+/*
+ * Hardware
+ */
+
+static int
+es_callback(snddev_info *d, int reason)
+{
+ struct es_info *es = (struct es_info *)d->device_data;
+ int rd = reason & SND_CB_RD;
+
+ switch(reason & SND_CB_REASON_MASK) {
+ case SND_CB_INIT:
+ es->ctrl = (es->ctrl & ~CTRL_PCLKDIV) |
+ (DAC2_SRTODIV(d->play_speed) << CTRL_SH_PCLKDIV);
+ snd_set_blocksize(d);
+
+ es->sctrl &= ~(SCTRL_R1FMT | SCTRL_P2FMT);
+ d->flags &= ~SND_F_XLAT8;
+ switch(d->play_fmt) {
+ case 0:
+ case AFMT_U8:
+ break;
+
+ case AFMT_S16_LE:
+ es->sctrl |= SCTRL_P2SEB;
+ break;
+
+ case AFMT_MU_LAW:
+ d->flags |= SND_F_XLAT8;
+ break;
+
+ default:
+ return (-1);
+ }
+
+ switch(d->rec_fmt) {
+ case 0:
+ case AFMT_U8:
+ break;
+
+ case AFMT_S16_LE:
+ es->sctrl |= SCTRL_R1SEB;
+ break;
+
+ case AFMT_MU_LAW:
+ d->flags |= SND_F_XLAT8;
+ break;
+
+ default:
+ return (-1);
+ }
+
+ if (d->flags & SND_F_STEREO)
+ es->sctrl |= SCTRL_P2SMB | SCTRL_R1SMB;
+
+ bus_space_write_4(es->st, es->sh, ES1370_REG_CONTROL,
+ es->ctrl);
+ bus_space_write_4(es->st, es->sh, ES1370_REG_SERIAL_CONTROL,
+ es->sctrl);
+ break;
+
+ case SND_CB_START:
+ if (rd) {
+ es->ctrl |= CTRL_ADC_EN;
+ es->sctrl = (es->sctrl & ~SCTRL_R1LOOPSEL) |
+ SCTRL_R1INTEN;
+ bus_space_write_4(es->st, es->sh, ES1370_REG_ADC_SCOUNT,
+ d->dbuf_in.dl / d->dbuf_in.sample_size - 1);
+ } else {
+ es->ctrl |= CTRL_DAC2_EN;
+ es->sctrl = (es->sctrl & ~(SCTRL_P2ENDINC |
+ SCTRL_P2STINC | SCTRL_P2LOOPSEL | SCTRL_P2PAUSE |
+ SCTRL_P2DACSEN)) | SCTRL_P2INTEN |
+ (((d->play_fmt == AFMT_S16_LE) ? 2 : 1)
+ << SCTRL_SH_P2ENDINC);
+ bus_space_write_4(es->st, es->sh,
+ ES1370_REG_DAC2_SCOUNT,
+ d->dbuf_out.dl / d->dbuf_out.sample_size - 1);
+ }
+ 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);
+ break;
+
+ case SND_CB_ABORT:
+ case SND_CB_STOP:
+ if (rd)
+ es->ctrl &= ~CTRL_ADC_EN;
+ else
+ es->ctrl &= ~CTRL_DAC2_EN;
+ bus_space_write_4(es->st, es->sh, ES1370_REG_CONTROL, es->ctrl);
+ break;
+
+ default:
+ return (-1);
+ }
+ return (0);
+}
+
+static int
+write_codec(snddev_info *d, u_char i, u_char data)
+{
+ struct es_info *es = (struct es_info *)d->device_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);
+ /* tsleep(&wait, PZERO, "sndaw", hz / 1000); */
+ } while (--wait);
+ printf("pcm: write_codec timed out\n");
+ return (-1);
+}
+
+static void
+es_wr_map(void *arg, bus_dma_segment_t *segs, int nseg, int error)
+{
+ struct es_info *es = (struct es_info *)arg;
+
+ 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,
+ segs->ds_addr);
+ bus_space_write_4(es->st, es->sh, ES1370_REG_DAC2_FRAMECNT & 0xff,
+ (segs->ds_len >> 2) - 1);
+}
+
+static void
+es_rd_map(void *arg, bus_dma_segment_t *segs, int nseg, int error)
+{
+ struct es_info *es = (struct es_info *)arg;
+
+ 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,
+ segs->ds_addr);
+ bus_space_write_4(es->st, es->sh, ES1370_REG_ADC_FRAMECNT & 0xff,
+ (segs->ds_len >> 2) - 1);
+}
+
+static void
+dma_wrintr(snddev_info *d)
+{
+ snd_dbuf *b = &d->dbuf_out;
+
+ /*
+ * According to Linux driver:
+ * dmaupdate()
+ * Bei underrun error++
+ * wake_up(dac2.wait)
+ */
+
+ if (b->dl != 0) {
+ es_wr_dmaupdate(d);
+ wakeup(b);
+ }
+
+ if (b->rl >= DMA_ALIGN_THRESHOLD &&
+ !(d->flags & SND_F_ABORTING)) {
+ int l = min(b->rl, d->play_blocksize);
+ l &= DMA_ALIGN_MASK;
+
+ if (l != b->dl) {
+ if (b->dl != 0) {
+ d->callback(d, SND_CB_WR | SND_CB_STOP);
+ es_wr_dmaupdate(d);
+ l = min(b->rl, d->play_blocksize);
+ l &= DMA_ALIGN_MASK;
+ }
+ b->dl = l;
+ d->callback(d, SND_CB_WR | SND_CB_START);
+ }
+ } else if (b->dl != 0) {
+ b->dl = 0;
+ d->callback(d, SND_CB_WR | SND_CB_STOP);
+ es_wr_dmaupdate(d);
+ }
+}
+
+static void
+dma_rdintr(snddev_info *d)
+{
+ snd_dbuf *b = &d->dbuf_in;
+
+ if (b->dl != 0) {
+ es_rd_dmaupdate(d);
+ wakeup(b);
+ }
+
+ if (b->fl >= DMA_READ_THRESHOLD &&
+ !(d->flags & SND_F_ABORTING)) {
+ int l = min(b->fl, d->rec_blocksize);
+ l &= DMA_ALIGN_MASK;
+
+ if (l != b->dl) {
+ if (b->dl != 0) {
+ d->callback(d, SND_CB_RD | SND_CB_STOP);
+ es_rd_dmaupdate(d);
+ l = min(b->fl, d->rec_blocksize);
+ l &= DMA_ALIGN_MASK;
+ }
+ b->dl = l;
+ d->callback(d, SND_CB_RD | SND_CB_START);
+ }
+ } else {
+ if (b->dl != 0) {
+ b->dl = 0;
+ d->callback(d, SND_CB_RD | SND_CB_STOP);
+ es_rd_dmaupdate(d);
+ }
+ }
+}
+
+static int
+es_wrabort(snddev_info *d)
+{
+ snd_dbuf *b = &d->dbuf_out;
+ long s;
+ int missing;
+
+ s = spltty();
+ if (b->dl != 0) {
+ wakeup(b);
+ b->dl = 0;
+ d->callback(d, SND_CB_WR | SND_CB_ABORT);
+ }
+ es_wr_dmaupdate(d);
+ missing = b->rl;
+ b->rl = 0;
+ b->fp = b->rp;
+ b->fl = b->bufsize;
+ splx(s);
+ return missing;
+}
+
+static int
+es_rdabort(snddev_info *d)
+{
+ snd_dbuf *b = &d->dbuf_in;
+ long s;
+ int missing;
+
+ s = spltty();
+ if (b->dl != 0) {
+ wakeup(b);
+ b->dl = 0;
+ d->callback(d, SND_CB_RD | SND_CB_ABORT);
+ es_rd_dmaupdate(d);
+ }
+ missing = b->rl;
+ b->rl = 0;
+ b->fp = b->rp;
+ b->fl = b->bufsize;
+ splx(s);
+ return missing;
+}
+
+
+/* -------------------------------------------------------------------- */
+
+/*
+ * Probe and attach the card
+ */
+
+static int
+es_init(snddev_info *d)
+{
+ struct es_info *es = (struct es_info *)d->device_data;
+ u_int i;
+
+ 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(d, CODEC_RES_PD, 3);/* No RST, PD */
+ write_codec(d, CODEC_CSEL, 0); /* CODEC ADC and CODEC DAC use
+ * {LR,B}CLK2 and run off the LRCLK2
+ * PLL; program DAC_SYNC=0! */
+ write_codec(d, CODEC_ADSEL, 0); /* Recording source is mixer */
+ write_codec(d, CODEC_MGAIN, 0); /* MIC amp is 0db */
+
+ i = SOUND_MASK_MIC;
+ mixer_ioctl(d, SOUND_MIXER_WRITE_RECSRC, (caddr_t) &i, 0, NULL);
+ i = 0;
+ mixer_ioctl(d, SOUND_MIXER_WRITE_VOLUME, (caddr_t) &i, 0, NULL);
+ mixer_ioctl(d, SOUND_MIXER_WRITE_PCM, (caddr_t) &i, 0, NULL);
+ mixer_ioctl(d, SOUND_MIXER_WRITE_SYNTH, (caddr_t) &i, 0, NULL);
+ mixer_ioctl(d, SOUND_MIXER_WRITE_CD, (caddr_t) &i, 0, NULL);
+ mixer_ioctl(d, SOUND_MIXER_WRITE_LINE, (caddr_t) &i, 0, NULL);
+ mixer_ioctl(d, SOUND_MIXER_WRITE_LINE1, (caddr_t) &i, 0, NULL);
+ mixer_ioctl(d, SOUND_MIXER_WRITE_LINE2, (caddr_t) &i, 0, NULL);
+ mixer_ioctl(d, SOUND_MIXER_WRITE_LINE3, (caddr_t) &i, 0, NULL);
+ mixer_ioctl(d, SOUND_MIXER_WRITE_MIC, (caddr_t) &i, 0, NULL);
+
+ return (0);
+}
+
+static char *
+es_pci_probe(pcici_t tag, pcidi_t type)
+{
+ if (type == ES1370_PCI_ID)
+ return ("AudioPCI ES1370");
+
+ return (NULL);
+}
+
+static void
+es_pci_attach(pcici_t config_id, int unit)
+{
+ snddev_info *d;
+ u_int32_t data;
+ struct es_info *es;
+ pci_port_t io_port;
+ int i, mapped;
+ vm_offset_t vaddr, paddr;
+
+ if (unit > NPCM_MAX)
+ return;
+
+ d = &pcm_info[unit];
+ *d = es_op_desc;
+ if ((es = malloc(sizeof(*es), M_DEVBUF, M_NOWAIT)) == NULL) {
+ printf("pcm%d: cannot allocate softc\n", unit);
+ return;
+ }
+ bzero(es, sizeof(*es));
+ d->device_data = es;
+
+ vaddr = paddr = NULL;
+ mapped = 0;
+ data = pci_conf_read(config_id, PCI_COMMAND_STATUS_REG);
+ if (mapped == 0 && (data & PCI_COMMAND_MEM_ENABLE)) {
+ if (pci_map_mem(config_id, MEM_MAP_REG, &vaddr, &paddr)) {
+ es->st = MEM_SPACE_MAPPING;
+ es->sh = vaddr;
+ mapped++;
+ }
+ }
+ if (mapped == 0 && (data & PCI_COMMAND_IO_ENABLE)) {
+ if (pci_map_port(config_id, PCI_MAP_REG_START, &io_port)) {
+ es->st = IO_SPACE_MAPPING;
+ es->sh = io_port;
+ mapped++;
+ }
+ }
+ if (mapped == 0) {
+ printf("pcm%d: unable to map any ports\n", unit);
+ free(es, M_DEVBUF);
+ return;
+ }
+ printf("pcm%d: using %s space register mapping at %#x\n", unit,
+ es->st == IO_SPACE_MAPPING ? "I/O" : "Memory", es->sh);
+
+ d->io_base = es->sh;
+ d->mix_devs = 0;
+ for (i = 0; i != SOUND_MIXER_NRDEVICES; i++)
+ if (mixtable[i].avail)
+ d->mix_devs |= (1 << i);
+ d->mix_rec_devs = 0;
+ for (i = 0; i != SOUND_MIXER_NRDEVICES; i++)
+ if (mixtable[i].recmask)
+ d->mix_rec_devs |= (1 << i);
+
+ if (es_init(d) == -1) {
+ printf("pcm%d: unable to initialize the card\n", unit);
+ free(es, M_DEVBUF);
+ d->io_base = 0;
+ return;
+ }
+ if (pci_map_int(config_id, es_intr, d, &tty_imask) == 0) {
+ printf("pcm%d: unable to map interrupt\n", unit);
+ free(es, M_DEVBUF);
+ d->io_base = 0;
+ return;
+ }
+ 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*/d->bufsize, /*nsegments*/1, /*maxsegz*/0x3ffff,
+ /*flags*/0, &es->parent_dmat) != 0) {
+ printf("pcm%d: unable to create dma tag\n", unit);
+ free(es, M_DEVBUF);
+ d->io_base = 0;
+ return;
+ }
+
+ if (alloc_dmabuf(d, 0) == -1 ||
+ alloc_dmabuf(d, 1) == -1) {
+ printf("pcm%d: unable to allocate dma buffers\n", unit);
+ free(es, M_DEVBUF);
+ d->io_base = 0;
+ return;
+ }
+
+ pcminit(d, unit);
+
+ return;
+}
+
+#endif /* NPCI != 0 */
diff --git a/sys/pci/es1370_reg.h b/sys/pci/es1370_reg.h
new file mode 100644
index 0000000000000..72546c61449a2
--- /dev/null
+++ b/sys/pci/es1370_reg.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$
+ */
+
+#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