summaryrefslogtreecommitdiffstats
path: root/sys/dev/sound/pci
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 /sys/dev/sound/pci
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.
Diffstat (limited to 'sys/dev/sound/pci')
-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
6 files changed, 2260 insertions, 0 deletions
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
OpenPOWER on IntegriCloud