aboutsummaryrefslogtreecommitdiff
path: root/sys/dev/sound/pci/emu10k1.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/dev/sound/pci/emu10k1.c')
-rw-r--r--sys/dev/sound/pci/emu10k1.c2249
1 files changed, 2249 insertions, 0 deletions
diff --git a/sys/dev/sound/pci/emu10k1.c b/sys/dev/sound/pci/emu10k1.c
new file mode 100644
index 000000000000..e4b2c22f4f07
--- /dev/null
+++ b/sys/dev/sound/pci/emu10k1.c
@@ -0,0 +1,2249 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2004 David O'Brien <obrien@FreeBSD.org>
+ * Copyright (c) 2003 Orlando Bassotto <orlando.bassotto@ieo-research.it>
+ * Copyright (c) 1999 Cameron Grant <cg@freebsd.org>
+ * 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 THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifdef HAVE_KERNEL_OPTION_HEADERS
+#include "opt_snd.h"
+#endif
+
+#include <dev/sound/pcm/sound.h>
+#include <dev/sound/pcm/ac97.h>
+#include <dev/sound/pci/emuxkireg.h>
+
+#include <dev/pci/pcireg.h>
+#include <dev/pci/pcivar.h>
+#include <sys/queue.h>
+
+#include <dev/sound/midi/mpu401.h>
+#include "mpufoi_if.h"
+
+/* -------------------------------------------------------------------- */
+
+#define NUM_G 64 /* use all channels */
+#define WAVEOUT_MAXBUFSIZE 32768
+#define EMUPAGESIZE 4096 /* don't change */
+#define EMUMAXPAGES (WAVEOUT_MAXBUFSIZE * NUM_G / EMUPAGESIZE)
+#define EMU10K1_PCI_ID 0x00021102 /* 1102 => Creative Labs Vendor ID */
+#define EMU10K2_PCI_ID 0x00041102
+#define EMU10K3_PCI_ID 0x00081102
+#define EMU_DEFAULT_BUFSZ 4096
+#define EMU_MAX_CHANS 8
+#define EMU_CHANS 4
+
+#define MAXREQVOICES 8
+#define RESERVED 0
+#define NUM_MIDI 16
+#define NUM_FXSENDS 4
+
+#define TMEMSIZE 256*1024
+#define TMEMSIZEREG 4
+
+#define ENABLE 0xffffffff
+#define DISABLE 0x00000000
+#define ENV_ON EMU_CHAN_DCYSUSV_CHANNELENABLE_MASK
+#define ENV_OFF 0x00 /* XXX: should this be 1? */
+
+#define EMU_A_IOCFG_GPOUT_A 0x40
+#define EMU_A_IOCFG_GPOUT_D 0x04
+#define EMU_A_IOCFG_GPOUT_AD (EMU_A_IOCFG_GPOUT_A|EMU_A_IOCFG_GPOUT_D) /* EMU_A_IOCFG_GPOUT0 */
+
+#define EMU_HCFG_GPOUT1 0x00000800
+
+/* instruction set */
+#define iACC3 0x06
+#define iMACINT0 0x04
+#define iINTERP 0x0e
+
+#define C_00000000 0x40
+#define C_00000001 0x41
+#define C_00000004 0x44
+#define C_40000000 0x4d
+/* Audigy constants */
+#define A_C_00000000 0xc0
+#define A_C_40000000 0xcd
+
+/* GPRs */
+#define FXBUS(x) (0x00 + (x))
+#define EXTIN(x) (0x10 + (x))
+#define EXTOUT(x) (0x20 + (x))
+
+#define GPR(x) (EMU_FXGPREGBASE + (x))
+#define A_EXTIN(x) (0x40 + (x))
+#define A_FXBUS(x) (0x00 + (x))
+#define A_EXTOUT(x) (0x60 + (x))
+#define A_GPR(x) (EMU_A_FXGPREGBASE + (x))
+
+/* FX buses */
+#define FXBUS_PCM_LEFT 0x00
+#define FXBUS_PCM_RIGHT 0x01
+#define FXBUS_MIDI_LEFT 0x04
+#define FXBUS_MIDI_RIGHT 0x05
+#define FXBUS_MIDI_REVERB 0x0c
+#define FXBUS_MIDI_CHORUS 0x0d
+
+/* Inputs */
+#define EXTIN_AC97_L 0x00
+#define EXTIN_AC97_R 0x01
+#define EXTIN_SPDIF_CD_L 0x02
+#define EXTIN_SPDIF_CD_R 0x03
+#define EXTIN_TOSLINK_L 0x06
+#define EXTIN_TOSLINK_R 0x07
+#define EXTIN_COAX_SPDIF_L 0x0a
+#define EXTIN_COAX_SPDIF_R 0x0b
+/* Audigy Inputs */
+#define A_EXTIN_AC97_L 0x00
+#define A_EXTIN_AC97_R 0x01
+
+/* Outputs */
+#define EXTOUT_AC97_L 0x00
+#define EXTOUT_AC97_R 0x01
+#define EXTOUT_TOSLINK_L 0x02
+#define EXTOUT_TOSLINK_R 0x03
+#define EXTOUT_AC97_CENTER 0x04
+#define EXTOUT_AC97_LFE 0x05
+#define EXTOUT_HEADPHONE_L 0x06
+#define EXTOUT_HEADPHONE_R 0x07
+#define EXTOUT_REAR_L 0x08
+#define EXTOUT_REAR_R 0x09
+#define EXTOUT_ADC_CAP_L 0x0a
+#define EXTOUT_ADC_CAP_R 0x0b
+#define EXTOUT_ACENTER 0x11
+#define EXTOUT_ALFE 0x12
+/* Audigy Outputs */
+#define A_EXTOUT_FRONT_L 0x00
+#define A_EXTOUT_FRONT_R 0x01
+#define A_EXTOUT_CENTER 0x02
+#define A_EXTOUT_LFE 0x03
+#define A_EXTOUT_HEADPHONE_L 0x04
+#define A_EXTOUT_HEADPHONE_R 0x05
+#define A_EXTOUT_REAR_L 0x06
+#define A_EXTOUT_REAR_R 0x07
+#define A_EXTOUT_AFRONT_L 0x08
+#define A_EXTOUT_AFRONT_R 0x09
+#define A_EXTOUT_ACENTER 0x0a
+#define A_EXTOUT_ALFE 0x0b
+#define A_EXTOUT_AREAR_L 0x0e
+#define A_EXTOUT_AREAR_R 0x0f
+#define A_EXTOUT_AC97_L 0x10
+#define A_EXTOUT_AC97_R 0x11
+#define A_EXTOUT_ADC_CAP_L 0x16
+#define A_EXTOUT_ADC_CAP_R 0x17
+
+struct emu_memblk {
+ SLIST_ENTRY(emu_memblk) link;
+ void *buf;
+ bus_addr_t buf_addr;
+ u_int32_t pte_start, pte_size;
+ bus_dmamap_t buf_map;
+};
+
+struct emu_mem {
+ u_int8_t bmap[EMUMAXPAGES / 8];
+ u_int32_t *ptb_pages;
+ void *silent_page;
+ bus_addr_t silent_page_addr;
+ bus_addr_t ptb_pages_addr;
+ bus_dmamap_t ptb_map;
+ bus_dmamap_t silent_map;
+ SLIST_HEAD(, emu_memblk) blocks;
+};
+
+struct emu_voice {
+ int vnum;
+ unsigned int b16:1, stereo:1, busy:1, running:1, ismaster:1;
+ int speed;
+ int start, end, vol;
+ int fxrt1; /* FX routing */
+ int fxrt2; /* FX routing (only for audigy) */
+ u_int32_t buf;
+ struct emu_voice *slave;
+ struct pcm_channel *channel;
+};
+
+struct sc_info;
+
+/* channel registers */
+struct sc_pchinfo {
+ int spd, fmt, blksz, run;
+ struct emu_voice *master, *slave;
+ struct snd_dbuf *buffer;
+ struct pcm_channel *channel;
+ struct sc_info *parent;
+};
+
+struct sc_rchinfo {
+ int spd, fmt, run, blksz, num;
+ u_int32_t idxreg, basereg, sizereg, setupreg, irqmask;
+ struct snd_dbuf *buffer;
+ struct pcm_channel *channel;
+ struct sc_info *parent;
+};
+
+/* device private data */
+struct sc_info {
+ device_t dev;
+ u_int32_t type, rev;
+ u_int32_t tos_link:1, APS:1, audigy:1, audigy2:1;
+ u_int32_t addrmask; /* wider if audigy */
+
+ bus_space_tag_t st;
+ bus_space_handle_t sh;
+ bus_dma_tag_t parent_dmat;
+
+ struct resource *reg, *irq;
+ void *ih;
+ struct mtx *lock;
+
+ unsigned int bufsz;
+ int timer, timerinterval;
+ int pnum, rnum;
+ int nchans;
+ struct emu_mem mem;
+ struct emu_voice voice[64];
+ struct sc_pchinfo pch[EMU_MAX_CHANS];
+ struct sc_rchinfo rch[3];
+ struct mpu401 *mpu;
+ mpu401_intr_t *mpu_intr;
+ int mputx;
+};
+
+/* -------------------------------------------------------------------- */
+
+/*
+ * prototypes
+ */
+
+/* stuff */
+static int emu_init(struct sc_info *);
+static void emu_intr(void *);
+static void *emu_malloc(struct sc_info *sc, u_int32_t sz, bus_addr_t *addr, bus_dmamap_t *map);
+static void *emu_memalloc(struct sc_info *sc, u_int32_t sz, bus_addr_t *addr);
+static int emu_memfree(struct sc_info *sc, void *buf);
+static int emu_memstart(struct sc_info *sc, void *buf);
+#ifdef EMUDEBUG
+static void emu_vdump(struct sc_info *sc, struct emu_voice *v);
+#endif
+
+/* talk to the card */
+static u_int32_t emu_rd(struct sc_info *, int, int);
+static void emu_wr(struct sc_info *, int, u_int32_t, int);
+
+/* -------------------------------------------------------------------- */
+
+static u_int32_t emu_rfmt_ac97[] = {
+ SND_FORMAT(AFMT_S16_LE, 1, 0),
+ SND_FORMAT(AFMT_S16_LE, 2, 0),
+ 0
+};
+
+static u_int32_t emu_rfmt_mic[] = {
+ SND_FORMAT(AFMT_U8, 1, 0),
+ 0
+};
+
+static u_int32_t emu_rfmt_efx[] = {
+ SND_FORMAT(AFMT_S16_LE, 2, 0),
+ 0
+};
+
+static struct pcmchan_caps emu_reccaps[3] = {
+ {8000, 48000, emu_rfmt_ac97, 0},
+ {8000, 8000, emu_rfmt_mic, 0},
+ {48000, 48000, emu_rfmt_efx, 0},
+};
+
+static u_int32_t emu_pfmt[] = {
+ SND_FORMAT(AFMT_U8, 1, 0),
+ SND_FORMAT(AFMT_U8, 2, 0),
+ SND_FORMAT(AFMT_S16_LE, 1, 0),
+ SND_FORMAT(AFMT_S16_LE, 2, 0),
+ 0
+};
+
+static struct pcmchan_caps emu_playcaps = {4000, 48000, emu_pfmt, 0};
+
+static int adcspeed[8] = {48000, 44100, 32000, 24000, 22050, 16000, 11025, 8000};
+/* audigy supports 12kHz. */
+static int audigy_adcspeed[9] = {
+ 48000, 44100, 32000, 24000, 22050, 16000, 12000, 11025, 8000
+};
+
+/* -------------------------------------------------------------------- */
+/* Hardware */
+static u_int32_t
+emu_rd(struct sc_info *sc, int regno, int size)
+{
+ switch (size) {
+ case 1:
+ return bus_space_read_1(sc->st, sc->sh, regno);
+ case 2:
+ return bus_space_read_2(sc->st, sc->sh, regno);
+ case 4:
+ return bus_space_read_4(sc->st, sc->sh, regno);
+ default:
+ return 0xffffffff;
+ }
+}
+
+static void
+emu_wr(struct sc_info *sc, int regno, u_int32_t data, int size)
+{
+ switch (size) {
+ case 1:
+ bus_space_write_1(sc->st, sc->sh, regno, data);
+ break;
+ case 2:
+ bus_space_write_2(sc->st, sc->sh, regno, data);
+ break;
+ case 4:
+ bus_space_write_4(sc->st, sc->sh, regno, data);
+ break;
+ }
+}
+
+static u_int32_t
+emu_rdptr(struct sc_info *sc, int chn, int reg)
+{
+ u_int32_t ptr, val, mask, size, offset;
+
+ ptr = ((reg << 16) & sc->addrmask) | (chn & EMU_PTR_CHNO_MASK);
+ emu_wr(sc, EMU_PTR, ptr, 4);
+ val = emu_rd(sc, EMU_DATA, 4);
+ if (reg & 0xff000000) {
+ size = (reg >> 24) & 0x3f;
+ offset = (reg >> 16) & 0x1f;
+ mask = ((1 << size) - 1) << offset;
+ val &= mask;
+ val >>= offset;
+ }
+ return val;
+}
+
+static void
+emu_wrptr(struct sc_info *sc, int chn, int reg, u_int32_t data)
+{
+ u_int32_t ptr, mask, size, offset;
+
+ ptr = ((reg << 16) & sc->addrmask) | (chn & EMU_PTR_CHNO_MASK);
+ emu_wr(sc, EMU_PTR, ptr, 4);
+ if (reg & 0xff000000) {
+ size = (reg >> 24) & 0x3f;
+ offset = (reg >> 16) & 0x1f;
+ mask = ((1 << size) - 1) << offset;
+ data <<= offset;
+ data &= mask;
+ data |= emu_rd(sc, EMU_DATA, 4) & ~mask;
+ }
+ emu_wr(sc, EMU_DATA, data, 4);
+}
+
+static void
+emu_wrefx(struct sc_info *sc, unsigned int pc, unsigned int data)
+{
+ pc += sc->audigy ? EMU_A_MICROCODEBASE : EMU_MICROCODEBASE;
+ emu_wrptr(sc, 0, pc, data);
+}
+
+/* -------------------------------------------------------------------- */
+/* ac97 codec */
+/* no locking needed */
+
+static int
+emu_rdcd(kobj_t obj, void *devinfo, int regno)
+{
+ struct sc_info *sc = (struct sc_info *)devinfo;
+
+ emu_wr(sc, EMU_AC97ADDR, regno, 1);
+ return emu_rd(sc, EMU_AC97DATA, 2);
+}
+
+static int
+emu_wrcd(kobj_t obj, void *devinfo, int regno, u_int32_t data)
+{
+ struct sc_info *sc = (struct sc_info *)devinfo;
+
+ emu_wr(sc, EMU_AC97ADDR, regno, 1);
+ emu_wr(sc, EMU_AC97DATA, data, 2);
+ return 0;
+}
+
+static kobj_method_t emu_ac97_methods[] = {
+ KOBJMETHOD(ac97_read, emu_rdcd),
+ KOBJMETHOD(ac97_write, emu_wrcd),
+ KOBJMETHOD_END
+};
+AC97_DECLARE(emu_ac97);
+
+/* -------------------------------------------------------------------- */
+/* stuff */
+static int
+emu_settimer(struct sc_info *sc)
+{
+ struct sc_pchinfo *pch;
+ struct sc_rchinfo *rch;
+ int i, tmp, rate;
+
+ rate = 0;
+ for (i = 0; i < sc->nchans; i++) {
+ pch = &sc->pch[i];
+ if (pch->buffer) {
+ tmp = (pch->spd * sndbuf_getalign(pch->buffer))
+ / pch->blksz;
+ if (tmp > rate)
+ rate = tmp;
+ }
+ }
+
+ for (i = 0; i < 3; i++) {
+ rch = &sc->rch[i];
+ if (rch->buffer) {
+ tmp = (rch->spd * sndbuf_getalign(rch->buffer))
+ / rch->blksz;
+ if (tmp > rate)
+ rate = tmp;
+ }
+ }
+ RANGE(rate, 48, 9600);
+ sc->timerinterval = 48000 / rate;
+ emu_wr(sc, EMU_TIMER, sc->timerinterval & 0x03ff, 2);
+
+ return sc->timerinterval;
+}
+
+static int
+emu_enatimer(struct sc_info *sc, int go)
+{
+ u_int32_t x;
+ if (go) {
+ if (sc->timer++ == 0) {
+ x = emu_rd(sc, EMU_INTE, 4);
+ x |= EMU_INTE_INTERTIMERENB;
+ emu_wr(sc, EMU_INTE, x, 4);
+ }
+ } else {
+ sc->timer = 0;
+ x = emu_rd(sc, EMU_INTE, 4);
+ x &= ~EMU_INTE_INTERTIMERENB;
+ emu_wr(sc, EMU_INTE, x, 4);
+ }
+ return 0;
+}
+
+static void
+emu_enastop(struct sc_info *sc, char channel, int enable)
+{
+ int reg = (channel & 0x20) ? EMU_SOLEH : EMU_SOLEL;
+ channel &= 0x1f;
+ reg |= 1 << 24;
+ reg |= channel << 16;
+ emu_wrptr(sc, 0, reg, enable);
+}
+
+static int
+emu_recval(int speed) {
+ int val;
+
+ val = 0;
+ while (val < 7 && speed < adcspeed[val])
+ val++;
+ return val;
+}
+
+static int
+audigy_recval(int speed) {
+ int val;
+
+ val = 0;
+ while (val < 8 && speed < audigy_adcspeed[val])
+ val++;
+ return val;
+}
+
+static u_int32_t
+emu_rate_to_pitch(u_int32_t rate)
+{
+ static u_int32_t logMagTable[128] = {
+ 0x00000, 0x02dfc, 0x05b9e, 0x088e6, 0x0b5d6, 0x0e26f, 0x10eb3, 0x13aa2,
+ 0x1663f, 0x1918a, 0x1bc84, 0x1e72e, 0x2118b, 0x23b9a, 0x2655d, 0x28ed5,
+ 0x2b803, 0x2e0e8, 0x30985, 0x331db, 0x359eb, 0x381b6, 0x3a93d, 0x3d081,
+ 0x3f782, 0x41e42, 0x444c1, 0x46b01, 0x49101, 0x4b6c4, 0x4dc49, 0x50191,
+ 0x5269e, 0x54b6f, 0x57006, 0x59463, 0x5b888, 0x5dc74, 0x60029, 0x623a7,
+ 0x646ee, 0x66a00, 0x68cdd, 0x6af86, 0x6d1fa, 0x6f43c, 0x7164b, 0x73829,
+ 0x759d4, 0x77b4f, 0x79c9a, 0x7bdb5, 0x7dea1, 0x7ff5e, 0x81fed, 0x8404e,
+ 0x86082, 0x88089, 0x8a064, 0x8c014, 0x8df98, 0x8fef1, 0x91e20, 0x93d26,
+ 0x95c01, 0x97ab4, 0x9993e, 0x9b79f, 0x9d5d9, 0x9f3ec, 0xa11d8, 0xa2f9d,
+ 0xa4d3c, 0xa6ab5, 0xa8808, 0xaa537, 0xac241, 0xadf26, 0xafbe7, 0xb1885,
+ 0xb3500, 0xb5157, 0xb6d8c, 0xb899f, 0xba58f, 0xbc15e, 0xbdd0c, 0xbf899,
+ 0xc1404, 0xc2f50, 0xc4a7b, 0xc6587, 0xc8073, 0xc9b3f, 0xcb5ed, 0xcd07c,
+ 0xceaec, 0xd053f, 0xd1f73, 0xd398a, 0xd5384, 0xd6d60, 0xd8720, 0xda0c3,
+ 0xdba4a, 0xdd3b4, 0xded03, 0xe0636, 0xe1f4e, 0xe384a, 0xe512c, 0xe69f3,
+ 0xe829f, 0xe9b31, 0xeb3a9, 0xecc08, 0xee44c, 0xefc78, 0xf148a, 0xf2c83,
+ 0xf4463, 0xf5c2a, 0xf73da, 0xf8b71, 0xfa2f0, 0xfba57, 0xfd1a7, 0xfe8df
+ };
+ static char logSlopeTable[128] = {
+ 0x5c, 0x5c, 0x5b, 0x5a, 0x5a, 0x59, 0x58, 0x58,
+ 0x57, 0x56, 0x56, 0x55, 0x55, 0x54, 0x53, 0x53,
+ 0x52, 0x52, 0x51, 0x51, 0x50, 0x50, 0x4f, 0x4f,
+ 0x4e, 0x4d, 0x4d, 0x4d, 0x4c, 0x4c, 0x4b, 0x4b,
+ 0x4a, 0x4a, 0x49, 0x49, 0x48, 0x48, 0x47, 0x47,
+ 0x47, 0x46, 0x46, 0x45, 0x45, 0x45, 0x44, 0x44,
+ 0x43, 0x43, 0x43, 0x42, 0x42, 0x42, 0x41, 0x41,
+ 0x41, 0x40, 0x40, 0x40, 0x3f, 0x3f, 0x3f, 0x3e,
+ 0x3e, 0x3e, 0x3d, 0x3d, 0x3d, 0x3c, 0x3c, 0x3c,
+ 0x3b, 0x3b, 0x3b, 0x3b, 0x3a, 0x3a, 0x3a, 0x39,
+ 0x39, 0x39, 0x39, 0x38, 0x38, 0x38, 0x38, 0x37,
+ 0x37, 0x37, 0x37, 0x36, 0x36, 0x36, 0x36, 0x35,
+ 0x35, 0x35, 0x35, 0x34, 0x34, 0x34, 0x34, 0x34,
+ 0x33, 0x33, 0x33, 0x33, 0x32, 0x32, 0x32, 0x32,
+ 0x32, 0x31, 0x31, 0x31, 0x31, 0x31, 0x30, 0x30,
+ 0x30, 0x30, 0x30, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f
+ };
+ int i;
+
+ if (rate == 0)
+ return 0; /* Bail out if no leading "1" */
+ rate *= 11185; /* Scale 48000 to 0x20002380 */
+ for (i = 31; i > 0; i--) {
+ if (rate & 0x80000000) { /* Detect leading "1" */
+ return (((u_int32_t) (i - 15) << 20) +
+ logMagTable[0x7f & (rate >> 24)] +
+ (0x7f & (rate >> 17)) *
+ logSlopeTable[0x7f & (rate >> 24)]);
+ }
+ rate <<= 1;
+ }
+
+ return 0; /* Should never reach this point */
+}
+
+static u_int32_t
+emu_rate_to_linearpitch(u_int32_t rate)
+{
+ rate = (rate << 8) / 375;
+ return (rate >> 1) + (rate & 1);
+}
+
+static struct emu_voice *
+emu_valloc(struct sc_info *sc)
+{
+ struct emu_voice *v;
+ int i;
+
+ v = NULL;
+ for (i = 0; i < 64 && sc->voice[i].busy; i++);
+ if (i < 64) {
+ v = &sc->voice[i];
+ v->busy = 1;
+ }
+ return v;
+}
+
+static int
+emu_vinit(struct sc_info *sc, struct emu_voice *m, struct emu_voice *s,
+ u_int32_t sz, struct snd_dbuf *b)
+{
+ void *buf;
+ bus_addr_t tmp_addr;
+
+ buf = emu_memalloc(sc, sz, &tmp_addr);
+ if (buf == NULL)
+ return -1;
+ if (b != NULL)
+ sndbuf_setup(b, buf, sz);
+ m->start = emu_memstart(sc, buf) * EMUPAGESIZE;
+ m->end = m->start + sz;
+ m->channel = NULL;
+ m->speed = 0;
+ m->b16 = 0;
+ m->stereo = 0;
+ m->running = 0;
+ m->ismaster = 1;
+ m->vol = 0xff;
+ m->buf = tmp_addr;
+ m->slave = s;
+ if (sc->audigy) {
+ m->fxrt1 = FXBUS_MIDI_CHORUS | FXBUS_PCM_RIGHT << 8 |
+ FXBUS_PCM_LEFT << 16 | FXBUS_MIDI_REVERB << 24;
+ m->fxrt2 = 0x3f3f3f3f; /* No effects on second route */
+ } else {
+ m->fxrt1 = FXBUS_MIDI_CHORUS | FXBUS_PCM_RIGHT << 4 |
+ FXBUS_PCM_LEFT << 8 | FXBUS_MIDI_REVERB << 12;
+ m->fxrt2 = 0;
+ }
+
+ if (s != NULL) {
+ s->start = m->start;
+ s->end = m->end;
+ s->channel = NULL;
+ s->speed = 0;
+ s->b16 = 0;
+ s->stereo = 0;
+ s->running = 0;
+ s->ismaster = 0;
+ s->vol = m->vol;
+ s->buf = m->buf;
+ s->fxrt1 = m->fxrt1;
+ s->fxrt2 = m->fxrt2;
+ s->slave = NULL;
+ }
+ return 0;
+}
+
+static void
+emu_vsetup(struct sc_pchinfo *ch)
+{
+ struct emu_voice *v = ch->master;
+
+ if (ch->fmt) {
+ v->b16 = (ch->fmt & AFMT_16BIT) ? 1 : 0;
+ v->stereo = (AFMT_CHANNEL(ch->fmt) > 1) ? 1 : 0;
+ if (v->slave != NULL) {
+ v->slave->b16 = v->b16;
+ v->slave->stereo = v->stereo;
+ }
+ }
+ if (ch->spd) {
+ v->speed = ch->spd;
+ if (v->slave != NULL)
+ v->slave->speed = v->speed;
+ }
+}
+
+static void
+emu_vwrite(struct sc_info *sc, struct emu_voice *v)
+{
+ int s;
+ int l, r, x, y;
+ u_int32_t sa, ea, start, val, silent_page;
+
+ s = (v->stereo ? 1 : 0) + (v->b16 ? 1 : 0);
+
+ sa = v->start >> s;
+ ea = v->end >> s;
+
+ l = r = x = y = v->vol;
+ if (v->stereo) {
+ l = v->ismaster ? l : 0;
+ r = v->ismaster ? 0 : r;
+ }
+
+ emu_wrptr(sc, v->vnum, EMU_CHAN_CPF, v->stereo ? EMU_CHAN_CPF_STEREO_MASK : 0);
+ val = v->stereo ? 28 : 30;
+ val *= v->b16 ? 1 : 2;
+ start = sa + val;
+
+ if (sc->audigy) {
+ emu_wrptr(sc, v->vnum, EMU_A_CHAN_FXRT1, v->fxrt1);
+ emu_wrptr(sc, v->vnum, EMU_A_CHAN_FXRT2, v->fxrt2);
+ emu_wrptr(sc, v->vnum, EMU_A_CHAN_SENDAMOUNTS, 0);
+ }
+ else
+ emu_wrptr(sc, v->vnum, EMU_CHAN_FXRT, v->fxrt1 << 16);
+
+ emu_wrptr(sc, v->vnum, EMU_CHAN_PTRX, (x << 8) | r);
+ emu_wrptr(sc, v->vnum, EMU_CHAN_DSL, ea | (y << 24));
+ emu_wrptr(sc, v->vnum, EMU_CHAN_PSST, sa | (l << 24));
+ emu_wrptr(sc, v->vnum, EMU_CHAN_CCCA, start | (v->b16 ? 0 : EMU_CHAN_CCCA_8BITSELECT));
+
+ emu_wrptr(sc, v->vnum, EMU_CHAN_Z1, 0);
+ emu_wrptr(sc, v->vnum, EMU_CHAN_Z2, 0);
+
+ silent_page = ((u_int32_t)(sc->mem.silent_page_addr) << 1)
+ | EMU_CHAN_MAP_PTI_MASK;
+ emu_wrptr(sc, v->vnum, EMU_CHAN_MAPA, silent_page);
+ emu_wrptr(sc, v->vnum, EMU_CHAN_MAPB, silent_page);
+
+ emu_wrptr(sc, v->vnum, EMU_CHAN_CVCF, EMU_CHAN_CVCF_CURRFILTER_MASK);
+ emu_wrptr(sc, v->vnum, EMU_CHAN_VTFT, EMU_CHAN_VTFT_FILTERTARGET_MASK);
+ emu_wrptr(sc, v->vnum, EMU_CHAN_ATKHLDM, 0);
+ emu_wrptr(sc, v->vnum, EMU_CHAN_DCYSUSM, EMU_CHAN_DCYSUSM_DECAYTIME_MASK);
+ emu_wrptr(sc, v->vnum, EMU_CHAN_LFOVAL1, 0x8000);
+ emu_wrptr(sc, v->vnum, EMU_CHAN_LFOVAL2, 0x8000);
+ emu_wrptr(sc, v->vnum, EMU_CHAN_FMMOD, 0);
+ emu_wrptr(sc, v->vnum, EMU_CHAN_TREMFRQ, 0);
+ emu_wrptr(sc, v->vnum, EMU_CHAN_FM2FRQ2, 0);
+ emu_wrptr(sc, v->vnum, EMU_CHAN_ENVVAL, 0x8000);
+
+ emu_wrptr(sc, v->vnum, EMU_CHAN_ATKHLDV,
+ EMU_CHAN_ATKHLDV_HOLDTIME_MASK | EMU_CHAN_ATKHLDV_ATTACKTIME_MASK);
+ emu_wrptr(sc, v->vnum, EMU_CHAN_ENVVOL, 0x8000);
+
+ emu_wrptr(sc, v->vnum, EMU_CHAN_PEFE_FILTERAMOUNT, 0x7f);
+ emu_wrptr(sc, v->vnum, EMU_CHAN_PEFE_PITCHAMOUNT, 0);
+
+ if (v->slave != NULL)
+ emu_vwrite(sc, v->slave);
+}
+
+static void
+emu_vtrigger(struct sc_info *sc, struct emu_voice *v, int go)
+{
+ u_int32_t pitch_target, initial_pitch;
+ u_int32_t cra, cs, ccis;
+ u_int32_t sample, i;
+
+ if (go) {
+ cra = 64;
+ cs = v->stereo ? 4 : 2;
+ ccis = v->stereo ? 28 : 30;
+ ccis *= v->b16 ? 1 : 2;
+ sample = v->b16 ? 0x00000000 : 0x80808080;
+
+ for (i = 0; i < cs; i++)
+ emu_wrptr(sc, v->vnum, EMU_CHAN_CD0 + i, sample);
+ emu_wrptr(sc, v->vnum, EMU_CHAN_CCR_CACHEINVALIDSIZE, 0);
+ emu_wrptr(sc, v->vnum, EMU_CHAN_CCR_READADDRESS, cra);
+ emu_wrptr(sc, v->vnum, EMU_CHAN_CCR_CACHEINVALIDSIZE, ccis);
+
+ emu_wrptr(sc, v->vnum, EMU_CHAN_IFATN, 0xff00);
+ emu_wrptr(sc, v->vnum, EMU_CHAN_VTFT, 0xffffffff);
+ emu_wrptr(sc, v->vnum, EMU_CHAN_CVCF, 0xffffffff);
+ emu_wrptr(sc, v->vnum, EMU_CHAN_DCYSUSV, 0x00007f7f);
+ emu_enastop(sc, v->vnum, 0);
+
+ pitch_target = emu_rate_to_linearpitch(v->speed);
+ initial_pitch = emu_rate_to_pitch(v->speed) >> 8;
+ emu_wrptr(sc, v->vnum, EMU_CHAN_PTRX_PITCHTARGET, pitch_target);
+ emu_wrptr(sc, v->vnum, EMU_CHAN_CPF_PITCH, pitch_target);
+ emu_wrptr(sc, v->vnum, EMU_CHAN_IP, initial_pitch);
+ } else {
+ emu_wrptr(sc, v->vnum, EMU_CHAN_PTRX_PITCHTARGET, 0);
+ emu_wrptr(sc, v->vnum, EMU_CHAN_CPF_PITCH, 0);
+ emu_wrptr(sc, v->vnum, EMU_CHAN_IFATN, 0xffff);
+ emu_wrptr(sc, v->vnum, EMU_CHAN_VTFT, 0x0000ffff);
+ emu_wrptr(sc, v->vnum, EMU_CHAN_CVCF, 0x0000ffff);
+ emu_wrptr(sc, v->vnum, EMU_CHAN_IP, 0);
+ emu_enastop(sc, v->vnum, 1);
+ }
+ if (v->slave != NULL)
+ emu_vtrigger(sc, v->slave, go);
+}
+
+static int
+emu_vpos(struct sc_info *sc, struct emu_voice *v)
+{
+ int s, ptr;
+
+ s = (v->b16 ? 1 : 0) + (v->stereo ? 1 : 0);
+ ptr = (emu_rdptr(sc, v->vnum, EMU_CHAN_CCCA_CURRADDR) - (v->start >> s)) << s;
+ return ptr & ~0x0000001f;
+}
+
+#ifdef EMUDEBUG
+static void
+emu_vdump(struct sc_info *sc, struct emu_voice *v)
+{
+ char *regname[] = {
+ "cpf", "ptrx", "cvcf", "vtft", "z2", "z1", "psst", "dsl",
+ "ccca", "ccr", "clp", "fxrt", "mapa", "mapb", NULL, NULL,
+ "envvol", "atkhldv", "dcysusv", "lfoval1",
+ "envval", "atkhldm", "dcysusm", "lfoval2",
+ "ip", "ifatn", "pefe", "fmmod", "tremfrq", "fmfrq2",
+ "tempenv"
+ };
+ char *regname2[] = {
+ "mudata1", "mustat1", "mudata2", "mustat2",
+ "fxwc1", "fxwc2", "spdrate", NULL, NULL,
+ NULL, NULL, NULL, "fxrt2", "sndamnt", "fxrt1",
+ NULL, NULL
+ };
+ int i, x;
+
+ printf("voice number %d\n", v->vnum);
+ for (i = 0, x = 0; i <= 0x1e; i++) {
+ if (regname[i] == NULL)
+ continue;
+ printf("%s\t[%08x]", regname[i], emu_rdptr(sc, v->vnum, i));
+ printf("%s", (x == 2) ? "\n" : "\t");
+ x++;
+ if (x > 2)
+ x = 0;
+ }
+
+ /* Print out audigy extra registers */
+ if (sc->audigy) {
+ for (i = 0; i <= 0xe; i++) {
+ if (regname2[i] == NULL)
+ continue;
+ printf("%s\t[%08x]", regname2[i],
+ emu_rdptr(sc, v->vnum, i + 0x70));
+ printf("%s", (x == 2)? "\n" : "\t");
+ x++;
+ if (x > 2)
+ x = 0;
+ }
+ }
+ printf("\n\n");
+}
+#endif
+
+/* channel interface */
+static void *
+emupchan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b,
+ struct pcm_channel *c, int dir)
+{
+ struct sc_info *sc = devinfo;
+ struct sc_pchinfo *ch;
+ void *r;
+
+ KASSERT(dir == PCMDIR_PLAY, ("emupchan_init: bad direction"));
+ ch = &sc->pch[sc->pnum++];
+ ch->buffer = b;
+ ch->parent = sc;
+ ch->channel = c;
+ ch->blksz = sc->bufsz / 2;
+ ch->fmt = SND_FORMAT(AFMT_U8, 1, 0);
+ ch->spd = 8000;
+ snd_mtxlock(sc->lock);
+ ch->master = emu_valloc(sc);
+ ch->slave = emu_valloc(sc);
+ snd_mtxunlock(sc->lock);
+ r = (emu_vinit(sc, ch->master, ch->slave, sc->bufsz, ch->buffer))
+ ? NULL : ch;
+
+ return r;
+}
+
+static int
+emupchan_free(kobj_t obj, void *data)
+{
+ struct sc_pchinfo *ch = data;
+ struct sc_info *sc = ch->parent;
+ int r;
+
+ snd_mtxlock(sc->lock);
+ r = emu_memfree(sc, sndbuf_getbuf(ch->buffer));
+ snd_mtxunlock(sc->lock);
+
+ return r;
+}
+
+static int
+emupchan_setformat(kobj_t obj, void *data, u_int32_t format)
+{
+ struct sc_pchinfo *ch = data;
+
+ ch->fmt = format;
+ return 0;
+}
+
+static u_int32_t
+emupchan_setspeed(kobj_t obj, void *data, u_int32_t speed)
+{
+ struct sc_pchinfo *ch = data;
+
+ ch->spd = speed;
+ return ch->spd;
+}
+
+static u_int32_t
+emupchan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize)
+{
+ struct sc_pchinfo *ch = data;
+ struct sc_info *sc = ch->parent;
+
+ ch->blksz = blocksize;
+ snd_mtxlock(sc->lock);
+ emu_settimer(sc);
+ snd_mtxunlock(sc->lock);
+ return blocksize;
+}
+
+static int
+emupchan_trigger(kobj_t obj, void *data, int go)
+{
+ struct sc_pchinfo *ch = data;
+ struct sc_info *sc = ch->parent;
+
+ if (!PCMTRIG_COMMON(go))
+ return 0;
+
+ snd_mtxlock(sc->lock);
+ if (go == PCMTRIG_START) {
+ emu_vsetup(ch);
+ emu_vwrite(sc, ch->master);
+ emu_settimer(sc);
+ emu_enatimer(sc, 1);
+#ifdef EMUDEBUG
+ printf("start [%d bit, %s, %d hz]\n",
+ ch->master->b16 ? 16 : 8,
+ ch->master->stereo ? "stereo" : "mono",
+ ch->master->speed);
+ emu_vdump(sc, ch->master);
+ emu_vdump(sc, ch->slave);
+#endif
+ }
+ ch->run = (go == PCMTRIG_START) ? 1 : 0;
+ emu_vtrigger(sc, ch->master, ch->run);
+ snd_mtxunlock(sc->lock);
+ return 0;
+}
+
+static u_int32_t
+emupchan_getptr(kobj_t obj, void *data)
+{
+ struct sc_pchinfo *ch = data;
+ struct sc_info *sc = ch->parent;
+ int r;
+
+ snd_mtxlock(sc->lock);
+ r = emu_vpos(sc, ch->master);
+ snd_mtxunlock(sc->lock);
+
+ return r;
+}
+
+static struct pcmchan_caps *
+emupchan_getcaps(kobj_t obj, void *data)
+{
+ return &emu_playcaps;
+}
+
+static kobj_method_t emupchan_methods[] = {
+ KOBJMETHOD(channel_init, emupchan_init),
+ KOBJMETHOD(channel_free, emupchan_free),
+ KOBJMETHOD(channel_setformat, emupchan_setformat),
+ KOBJMETHOD(channel_setspeed, emupchan_setspeed),
+ KOBJMETHOD(channel_setblocksize, emupchan_setblocksize),
+ KOBJMETHOD(channel_trigger, emupchan_trigger),
+ KOBJMETHOD(channel_getptr, emupchan_getptr),
+ KOBJMETHOD(channel_getcaps, emupchan_getcaps),
+ KOBJMETHOD_END
+};
+CHANNEL_DECLARE(emupchan);
+
+/* channel interface */
+static void *
+emurchan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b,
+ struct pcm_channel *c, int dir)
+{
+ struct sc_info *sc = devinfo;
+ struct sc_rchinfo *ch;
+
+ KASSERT(dir == PCMDIR_REC, ("emurchan_init: bad direction"));
+ ch = &sc->rch[sc->rnum];
+ ch->buffer = b;
+ ch->parent = sc;
+ ch->channel = c;
+ ch->blksz = sc->bufsz / 2;
+ ch->fmt = SND_FORMAT(AFMT_U8, 1, 0);
+ ch->spd = 8000;
+ ch->num = sc->rnum;
+ switch(sc->rnum) {
+ case 0:
+ ch->idxreg = sc->audigy ? EMU_A_ADCIDX : EMU_ADCIDX;
+ ch->basereg = EMU_ADCBA;
+ ch->sizereg = EMU_ADCBS;
+ ch->setupreg = EMU_ADCCR;
+ ch->irqmask = EMU_INTE_ADCBUFENABLE;
+ break;
+
+ case 1:
+ ch->idxreg = EMU_FXIDX;
+ ch->basereg = EMU_FXBA;
+ ch->sizereg = EMU_FXBS;
+ ch->setupreg = EMU_FXWC;
+ ch->irqmask = EMU_INTE_EFXBUFENABLE;
+ break;
+
+ case 2:
+ ch->idxreg = EMU_MICIDX;
+ ch->basereg = EMU_MICBA;
+ ch->sizereg = EMU_MICBS;
+ ch->setupreg = 0;
+ ch->irqmask = EMU_INTE_MICBUFENABLE;
+ break;
+ }
+ sc->rnum++;
+ if (sndbuf_alloc(ch->buffer, sc->parent_dmat, 0, sc->bufsz) != 0)
+ return NULL;
+ else {
+ snd_mtxlock(sc->lock);
+ emu_wrptr(sc, 0, ch->basereg, sndbuf_getbufaddr(ch->buffer));
+ emu_wrptr(sc, 0, ch->sizereg, 0); /* off */
+ snd_mtxunlock(sc->lock);
+ return ch;
+ }
+}
+
+static int
+emurchan_setformat(kobj_t obj, void *data, u_int32_t format)
+{
+ struct sc_rchinfo *ch = data;
+
+ ch->fmt = format;
+ return 0;
+}
+
+static u_int32_t
+emurchan_setspeed(kobj_t obj, void *data, u_int32_t speed)
+{
+ struct sc_rchinfo *ch = data;
+
+ if (ch->num == 0) {
+ if (ch->parent->audigy)
+ speed = audigy_adcspeed[audigy_recval(speed)];
+ else
+ speed = adcspeed[emu_recval(speed)];
+ }
+ if (ch->num == 1)
+ speed = 48000;
+ if (ch->num == 2)
+ speed = 8000;
+ ch->spd = speed;
+ return ch->spd;
+}
+
+static u_int32_t
+emurchan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize)
+{
+ struct sc_rchinfo *ch = data;
+ struct sc_info *sc = ch->parent;
+
+ ch->blksz = blocksize;
+ snd_mtxlock(sc->lock);
+ emu_settimer(sc);
+ snd_mtxunlock(sc->lock);
+ return blocksize;
+}
+
+/* semantic note: must start at beginning of buffer */
+static int
+emurchan_trigger(kobj_t obj, void *data, int go)
+{
+ struct sc_rchinfo *ch = data;
+ struct sc_info *sc = ch->parent;
+ u_int32_t val, sz;
+
+ if (!PCMTRIG_COMMON(go))
+ return 0;
+
+ switch(sc->bufsz) {
+ case 4096:
+ sz = EMU_RECBS_BUFSIZE_4096;
+ break;
+
+ case 8192:
+ sz = EMU_RECBS_BUFSIZE_8192;
+ break;
+
+ case 16384:
+ sz = EMU_RECBS_BUFSIZE_16384;
+ break;
+
+ case 32768:
+ sz = EMU_RECBS_BUFSIZE_32768;
+ break;
+
+ case 65536:
+ sz = EMU_RECBS_BUFSIZE_65536;
+ break;
+
+ default:
+ sz = EMU_RECBS_BUFSIZE_4096;
+ }
+
+ snd_mtxlock(sc->lock);
+ switch(go) {
+ case PCMTRIG_START:
+ ch->run = 1;
+ emu_wrptr(sc, 0, ch->sizereg, sz);
+ if (ch->num == 0) {
+ if (sc->audigy) {
+ val = EMU_A_ADCCR_LCHANENABLE;
+ if (AFMT_CHANNEL(ch->fmt) > 1)
+ val |= EMU_A_ADCCR_RCHANENABLE;
+ val |= audigy_recval(ch->spd);
+ } else {
+ val = EMU_ADCCR_LCHANENABLE;
+ if (AFMT_CHANNEL(ch->fmt) > 1)
+ val |= EMU_ADCCR_RCHANENABLE;
+ val |= emu_recval(ch->spd);
+ }
+
+ emu_wrptr(sc, 0, ch->setupreg, 0);
+ emu_wrptr(sc, 0, ch->setupreg, val);
+ }
+ val = emu_rd(sc, EMU_INTE, 4);
+ val |= ch->irqmask;
+ emu_wr(sc, EMU_INTE, val, 4);
+ break;
+
+ case PCMTRIG_STOP:
+ case PCMTRIG_ABORT:
+ ch->run = 0;
+ emu_wrptr(sc, 0, ch->sizereg, 0);
+ if (ch->setupreg)
+ emu_wrptr(sc, 0, ch->setupreg, 0);
+ val = emu_rd(sc, EMU_INTE, 4);
+ val &= ~ch->irqmask;
+ emu_wr(sc, EMU_INTE, val, 4);
+ break;
+
+ case PCMTRIG_EMLDMAWR:
+ case PCMTRIG_EMLDMARD:
+ default:
+ break;
+ }
+ snd_mtxunlock(sc->lock);
+
+ return 0;
+}
+
+static u_int32_t
+emurchan_getptr(kobj_t obj, void *data)
+{
+ struct sc_rchinfo *ch = data;
+ struct sc_info *sc = ch->parent;
+ int r;
+
+ snd_mtxlock(sc->lock);
+ r = emu_rdptr(sc, 0, ch->idxreg) & 0x0000ffff;
+ snd_mtxunlock(sc->lock);
+
+ return r;
+}
+
+static struct pcmchan_caps *
+emurchan_getcaps(kobj_t obj, void *data)
+{
+ struct sc_rchinfo *ch = data;
+
+ return &emu_reccaps[ch->num];
+}
+
+static kobj_method_t emurchan_methods[] = {
+ KOBJMETHOD(channel_init, emurchan_init),
+ KOBJMETHOD(channel_setformat, emurchan_setformat),
+ KOBJMETHOD(channel_setspeed, emurchan_setspeed),
+ KOBJMETHOD(channel_setblocksize, emurchan_setblocksize),
+ KOBJMETHOD(channel_trigger, emurchan_trigger),
+ KOBJMETHOD(channel_getptr, emurchan_getptr),
+ KOBJMETHOD(channel_getcaps, emurchan_getcaps),
+ KOBJMETHOD_END
+};
+CHANNEL_DECLARE(emurchan);
+
+static unsigned char
+emu_mread(struct mpu401 *arg, void *sc, int reg)
+{
+ unsigned int d;
+
+ d = emu_rd((struct sc_info *)sc, 0x18 + reg, 1);
+ return d;
+}
+
+static void
+emu_mwrite(struct mpu401 *arg, void *sc, int reg, unsigned char b)
+{
+
+ emu_wr((struct sc_info *)sc, 0x18 + reg, b, 1);
+}
+
+static int
+emu_muninit(struct mpu401 *arg, void *cookie)
+{
+ struct sc_info *sc = cookie;
+
+ snd_mtxlock(sc->lock);
+ sc->mpu_intr = NULL;
+ snd_mtxunlock(sc->lock);
+
+ return 0;
+}
+
+static kobj_method_t emu_mpu_methods[] = {
+ KOBJMETHOD(mpufoi_read, emu_mread),
+ KOBJMETHOD(mpufoi_write, emu_mwrite),
+ KOBJMETHOD(mpufoi_uninit, emu_muninit),
+ KOBJMETHOD_END
+};
+
+static DEFINE_CLASS(emu_mpu, emu_mpu_methods, 0);
+
+static void
+emu_intr2(void *p)
+{
+ struct sc_info *sc = (struct sc_info *)p;
+
+ if (sc->mpu_intr)
+ (sc->mpu_intr)(sc->mpu);
+}
+
+static void
+emu_midiattach(struct sc_info *sc)
+{
+ int i;
+
+ i = emu_rd(sc, EMU_INTE, 4);
+ i |= EMU_INTE_MIDIRXENABLE;
+ emu_wr(sc, EMU_INTE, i, 4);
+
+ sc->mpu = mpu401_init(&emu_mpu_class, sc, emu_intr2, &sc->mpu_intr);
+}
+/* -------------------------------------------------------------------- */
+/* The interrupt handler */
+
+static void
+emu_intr(void *data)
+{
+ struct sc_info *sc = data;
+ u_int32_t stat, ack, i, x;
+
+ snd_mtxlock(sc->lock);
+ while (1) {
+ stat = emu_rd(sc, EMU_IPR, 4);
+ if (stat == 0)
+ break;
+ ack = 0;
+
+ /* process irq */
+ if (stat & EMU_IPR_INTERVALTIMER)
+ ack |= EMU_IPR_INTERVALTIMER;
+
+ if (stat & (EMU_IPR_ADCBUFFULL | EMU_IPR_ADCBUFHALFFULL))
+ ack |= stat & (EMU_IPR_ADCBUFFULL | EMU_IPR_ADCBUFHALFFULL);
+
+ if (stat & (EMU_IPR_EFXBUFFULL | EMU_IPR_EFXBUFHALFFULL))
+ ack |= stat & (EMU_IPR_EFXBUFFULL | EMU_IPR_EFXBUFHALFFULL);
+
+ if (stat & (EMU_IPR_MICBUFFULL | EMU_IPR_MICBUFHALFFULL))
+ ack |= stat & (EMU_IPR_MICBUFFULL | EMU_IPR_MICBUFHALFFULL);
+
+ if (stat & EMU_PCIERROR) {
+ ack |= EMU_PCIERROR;
+ device_printf(sc->dev, "pci error\n");
+ /* we still get an nmi with ecc ram even if we ack this */
+ }
+ if (stat & EMU_IPR_RATETRCHANGE) {
+ ack |= EMU_IPR_RATETRCHANGE;
+#ifdef EMUDEBUG
+ device_printf(sc->dev,
+ "sample rate tracker lock status change\n");
+#endif
+ }
+
+ if (stat & EMU_IPR_MIDIRECVBUFE) {
+ if (sc->mpu_intr) {
+ (sc->mpu_intr)(sc->mpu);
+ ack |= EMU_IPR_MIDIRECVBUFE | EMU_IPR_MIDITRANSBUFE;
+ }
+ }
+ if (stat & ~ack)
+ device_printf(sc->dev, "dodgy irq: %x (harmless)\n",
+ stat & ~ack);
+
+ emu_wr(sc, EMU_IPR, stat, 4);
+
+ if (ack) {
+ snd_mtxunlock(sc->lock);
+
+ if (ack & EMU_IPR_INTERVALTIMER) {
+ x = 0;
+ for (i = 0; i < sc->nchans; i++) {
+ if (sc->pch[i].run) {
+ x = 1;
+ chn_intr(sc->pch[i].channel);
+ }
+ }
+ if (x == 0)
+ emu_enatimer(sc, 0);
+ }
+
+ if (ack & (EMU_IPR_ADCBUFFULL | EMU_IPR_ADCBUFHALFFULL)) {
+ if (sc->rch[0].channel)
+ chn_intr(sc->rch[0].channel);
+ }
+ if (ack & (EMU_IPR_EFXBUFFULL | EMU_IPR_EFXBUFHALFFULL)) {
+ if (sc->rch[1].channel)
+ chn_intr(sc->rch[1].channel);
+ }
+ if (ack & (EMU_IPR_MICBUFFULL | EMU_IPR_MICBUFHALFFULL)) {
+ if (sc->rch[2].channel)
+ chn_intr(sc->rch[2].channel);
+ }
+
+ snd_mtxlock(sc->lock);
+ }
+ }
+ snd_mtxunlock(sc->lock);
+}
+
+/* -------------------------------------------------------------------- */
+
+static void
+emu_setmap(void *arg, bus_dma_segment_t *segs, int nseg, int error)
+{
+ bus_addr_t *phys = arg;
+
+ *phys = error ? 0 : (bus_addr_t)segs->ds_addr;
+
+ if (bootverbose) {
+ printf("emu: setmap (%lx, %lx), nseg=%d, error=%d\n",
+ (unsigned long)segs->ds_addr, (unsigned long)segs->ds_len,
+ nseg, error);
+ }
+}
+
+static void *
+emu_malloc(struct sc_info *sc, u_int32_t sz, bus_addr_t *addr,
+ bus_dmamap_t *map)
+{
+ void *buf;
+
+ *addr = 0;
+ if (bus_dmamem_alloc(sc->parent_dmat, &buf, BUS_DMA_NOWAIT, map))
+ return NULL;
+ if (bus_dmamap_load(sc->parent_dmat, *map, buf, sz, emu_setmap, addr,
+ BUS_DMA_NOWAIT) || !*addr) {
+ bus_dmamem_free(sc->parent_dmat, buf, *map);
+ return NULL;
+ }
+ return buf;
+}
+
+static void
+emu_free(struct sc_info *sc, void *buf, bus_dmamap_t map)
+{
+ bus_dmamap_unload(sc->parent_dmat, map);
+ bus_dmamem_free(sc->parent_dmat, buf, map);
+}
+
+static void *
+emu_memalloc(struct sc_info *sc, u_int32_t sz, bus_addr_t *addr)
+{
+ u_int32_t blksz, start, idx, ofs, tmp, found;
+ struct emu_mem *mem = &sc->mem;
+ struct emu_memblk *blk;
+ void *buf;
+
+ blksz = sz / EMUPAGESIZE;
+ if (sz > (blksz * EMUPAGESIZE))
+ blksz++;
+ /* find a free block in the bitmap */
+ found = 0;
+ start = 1;
+ while (!found && start + blksz < EMUMAXPAGES) {
+ found = 1;
+ for (idx = start; idx < start + blksz; idx++)
+ if (mem->bmap[idx >> 3] & (1 << (idx & 7)))
+ found = 0;
+ if (!found)
+ start++;
+ }
+ if (!found)
+ return NULL;
+ blk = malloc(sizeof(*blk), M_DEVBUF, M_NOWAIT);
+ if (blk == NULL)
+ return NULL;
+ buf = emu_malloc(sc, sz, &blk->buf_addr, &blk->buf_map);
+ *addr = blk->buf_addr;
+ if (buf == NULL) {
+ free(blk, M_DEVBUF);
+ return NULL;
+ }
+ blk->buf = buf;
+ blk->pte_start = start;
+ blk->pte_size = blksz;
+#ifdef EMUDEBUG
+ printf("buf %p, pte_start %d, pte_size %d\n", blk->buf,
+ blk->pte_start, blk->pte_size);
+#endif
+ ofs = 0;
+ for (idx = start; idx < start + blksz; idx++) {
+ mem->bmap[idx >> 3] |= 1 << (idx & 7);
+ tmp = (uint32_t)(blk->buf_addr + ofs);
+#ifdef EMUDEBUG
+ printf("pte[%d] -> %x phys, %x virt\n", idx, tmp,
+ ((u_int32_t)buf) + ofs);
+#endif
+ mem->ptb_pages[idx] = (tmp << 1) | idx;
+ ofs += EMUPAGESIZE;
+ }
+ SLIST_INSERT_HEAD(&mem->blocks, blk, link);
+ return buf;
+}
+
+static int
+emu_memfree(struct sc_info *sc, void *buf)
+{
+ u_int32_t idx, tmp;
+ struct emu_mem *mem = &sc->mem;
+ struct emu_memblk *blk, *i;
+
+ blk = NULL;
+ SLIST_FOREACH(i, &mem->blocks, link) {
+ if (i->buf == buf)
+ blk = i;
+ }
+ if (blk == NULL)
+ return EINVAL;
+ SLIST_REMOVE(&mem->blocks, blk, emu_memblk, link);
+ emu_free(sc, buf, blk->buf_map);
+ tmp = (u_int32_t)(sc->mem.silent_page_addr) << 1;
+ for (idx = blk->pte_start; idx < blk->pte_start + blk->pte_size; idx++) {
+ mem->bmap[idx >> 3] &= ~(1 << (idx & 7));
+ mem->ptb_pages[idx] = tmp | idx;
+ }
+ free(blk, M_DEVBUF);
+ return 0;
+}
+
+static int
+emu_memstart(struct sc_info *sc, void *buf)
+{
+ struct emu_mem *mem = &sc->mem;
+ struct emu_memblk *blk, *i;
+
+ blk = NULL;
+ SLIST_FOREACH(i, &mem->blocks, link) {
+ if (i->buf == buf)
+ blk = i;
+ }
+ if (blk == NULL)
+ return -EINVAL;
+ return blk->pte_start;
+}
+
+static void
+emu_addefxop(struct sc_info *sc, int op, int z, int w, int x, int y,
+ u_int32_t *pc)
+{
+ emu_wrefx(sc, (*pc) * 2, (x << 10) | y);
+ emu_wrefx(sc, (*pc) * 2 + 1, (op << 20) | (z << 10) | w);
+ (*pc)++;
+}
+
+static void
+audigy_addefxop(struct sc_info *sc, int op, int z, int w, int x, int y,
+ u_int32_t *pc)
+{
+ emu_wrefx(sc, (*pc) * 2, (x << 12) | y);
+ emu_wrefx(sc, (*pc) * 2 + 1, (op << 24) | (z << 12) | w);
+ (*pc)++;
+}
+
+static void
+audigy_initefx(struct sc_info *sc)
+{
+ int i;
+ u_int32_t pc = 0;
+
+ /* skip 0, 0, -1, 0 - NOPs */
+ for (i = 0; i < 512; i++)
+ audigy_addefxop(sc, 0x0f, 0x0c0, 0x0c0, 0x0cf, 0x0c0, &pc);
+
+ for (i = 0; i < 512; i++)
+ emu_wrptr(sc, 0, EMU_A_FXGPREGBASE + i, 0x0);
+
+ pc = 16;
+
+ /* stop fx processor */
+ emu_wrptr(sc, 0, EMU_A_DBG, EMU_A_DBG_SINGLE_STEP);
+
+ /* Audigy 2 (EMU10K2) DSP Registers:
+ FX Bus
+ 0x000-0x00f : 16 registers (?)
+ Input
+ 0x040/0x041 : AC97 Codec (l/r)
+ 0x042/0x043 : ADC, S/PDIF (l/r)
+ 0x044/0x045 : Optical S/PDIF in (l/r)
+ 0x046/0x047 : ?
+ 0x048/0x049 : Line/Mic 2 (l/r)
+ 0x04a/0x04b : RCA S/PDIF (l/r)
+ 0x04c/0x04d : Aux 2 (l/r)
+ Output
+ 0x060/0x061 : Digital Front (l/r)
+ 0x062/0x063 : Digital Center/LFE
+ 0x064/0x065 : AudigyDrive Heaphone (l/r)
+ 0x066/0x067 : Digital Rear (l/r)
+ 0x068/0x069 : Analog Front (l/r)
+ 0x06a/0x06b : Analog Center/LFE
+ 0x06c/0x06d : ?
+ 0x06e/0x06f : Analog Rear (l/r)
+ 0x070/0x071 : AC97 Output (l/r)
+ 0x072/0x073 : ?
+ 0x074/0x075 : ?
+ 0x076/0x077 : ADC Recording Buffer (l/r)
+ Constants
+ 0x0c0 - 0x0c4 = 0 - 4
+ 0x0c5 = 0x8, 0x0c6 = 0x10, 0x0c7 = 0x20
+ 0x0c8 = 0x100, 0x0c9 = 0x10000, 0x0ca = 0x80000
+ 0x0cb = 0x10000000, 0x0cc = 0x20000000, 0x0cd = 0x40000000
+ 0x0ce = 0x80000000, 0x0cf = 0x7fffffff, 0x0d0 = 0xffffffff
+ 0x0d1 = 0xfffffffe, 0x0d2 = 0xc0000000, 0x0d3 = 0x41fbbcdc
+ 0x0d4 = 0x5a7ef9db, 0x0d5 = 0x00100000, 0x0dc = 0x00000001 (?)
+ Temporary Values
+ 0x0d6 : Accumulator (?)
+ 0x0d7 : Condition Register
+ 0x0d8 : Noise source
+ 0x0d9 : Noise source
+ Tank Memory Data Registers
+ 0x200 - 0x2ff
+ Tank Memory Address Registers
+ 0x300 - 0x3ff
+ General Purpose Registers
+ 0x400 - 0x5ff
+ */
+
+ /* AC97Output[l/r] = FXBus PCM[l/r] */
+ audigy_addefxop(sc, iACC3, A_EXTOUT(A_EXTOUT_AC97_L), A_C_00000000,
+ A_C_00000000, A_FXBUS(FXBUS_PCM_LEFT), &pc);
+ audigy_addefxop(sc, iACC3, A_EXTOUT(A_EXTOUT_AC97_R), A_C_00000000,
+ A_C_00000000, A_FXBUS(FXBUS_PCM_RIGHT), &pc);
+
+ /* GPR[0/1] = RCA S/PDIF[l/r] -- Master volume */
+ audigy_addefxop(sc, iACC3, A_GPR(0), A_C_00000000,
+ A_C_00000000, A_EXTIN(EXTIN_COAX_SPDIF_L), &pc);
+ audigy_addefxop(sc, iACC3, A_GPR(1), A_C_00000000,
+ A_C_00000000, A_EXTIN(EXTIN_COAX_SPDIF_R), &pc);
+
+ /* GPR[2] = GPR[0] (Left) / 2 + GPR[1] (Right) / 2 -- Central volume */
+ audigy_addefxop(sc, iINTERP, A_GPR(2), A_GPR(1),
+ A_C_40000000, A_GPR(0), &pc);
+
+ /* Headphones[l/r] = GPR[0/1] */
+ audigy_addefxop(sc, iACC3, A_EXTOUT(A_EXTOUT_HEADPHONE_L),
+ A_C_00000000, A_C_00000000, A_GPR(0), &pc);
+ audigy_addefxop(sc, iACC3, A_EXTOUT(A_EXTOUT_HEADPHONE_R),
+ A_C_00000000, A_C_00000000, A_GPR(1), &pc);
+
+ /* Analog Front[l/r] = GPR[0/1] */
+ audigy_addefxop(sc, iACC3, A_EXTOUT(A_EXTOUT_AFRONT_L), A_C_00000000,
+ A_C_00000000, A_GPR(0), &pc);
+ audigy_addefxop(sc, iACC3, A_EXTOUT(A_EXTOUT_AFRONT_R), A_C_00000000,
+ A_C_00000000, A_GPR(1), &pc);
+
+ /* Digital Front[l/r] = GPR[0/1] */
+ audigy_addefxop(sc, iACC3, A_EXTOUT(A_EXTOUT_FRONT_L), A_C_00000000,
+ A_C_00000000, A_GPR(0), &pc);
+ audigy_addefxop(sc, iACC3, A_EXTOUT(A_EXTOUT_FRONT_R), A_C_00000000,
+ A_C_00000000, A_GPR(1), &pc);
+
+ /* Center and Subwoofer configuration */
+ /* Analog Center = GPR[0] + GPR[2] */
+ audigy_addefxop(sc, iACC3, A_EXTOUT(A_EXTOUT_ACENTER), A_C_00000000,
+ A_GPR(0), A_GPR(2), &pc);
+ /* Analog Sub = GPR[1] + GPR[2] */
+ audigy_addefxop(sc, iACC3, A_EXTOUT(A_EXTOUT_ALFE), A_C_00000000,
+ A_GPR(1), A_GPR(2), &pc);
+
+ /* Digital Center = GPR[0] + GPR[2] */
+ audigy_addefxop(sc, iACC3, A_EXTOUT(A_EXTOUT_CENTER), A_C_00000000,
+ A_GPR(0), A_GPR(2), &pc);
+ /* Digital Sub = GPR[1] + GPR[2] */
+ audigy_addefxop(sc, iACC3, A_EXTOUT(A_EXTOUT_LFE), A_C_00000000,
+ A_GPR(1), A_GPR(2), &pc);
+
+#if 0
+ /* Analog Rear[l/r] = (GPR[0/1] * RearVolume[l/r]) >> 31 */
+ /* RearVolume = GPR[0x10/0x11] (Will this ever be implemented?) */
+ audigy_addefxop(sc, iMAC0, A_EXTOUT(A_EXTOUT_AREAR_L), A_C_00000000,
+ A_GPR(16), A_GPR(0), &pc);
+ audigy_addefxop(sc, iMAC0, A_EXTOUT(A_EXTOUT_AREAR_R), A_C_00000000,
+ A_GPR(17), A_GPR(1), &pc);
+
+ /* Digital Rear[l/r] = (GPR[0/1] * RearVolume[l/r]) >> 31 */
+ /* RearVolume = GPR[0x10/0x11] (Will this ever be implemented?) */
+ audigy_addefxop(sc, iMAC0, A_EXTOUT(A_EXTOUT_REAR_L), A_C_00000000,
+ A_GPR(16), A_GPR(0), &pc);
+ audigy_addefxop(sc, iMAC0, A_EXTOUT(A_EXTOUT_REAR_R), A_C_00000000,
+ A_GPR(17), A_GPR(1), &pc);
+#else
+ /* XXX This is just a copy to the channel, since we do not have
+ * a patch manager, it is useful for have another output enabled.
+ */
+
+ /* Analog Rear[l/r] = GPR[0/1] */
+ audigy_addefxop(sc, iACC3, A_EXTOUT(A_EXTOUT_AREAR_L), A_C_00000000,
+ A_C_00000000, A_GPR(0), &pc);
+ audigy_addefxop(sc, iACC3, A_EXTOUT(A_EXTOUT_AREAR_R), A_C_00000000,
+ A_C_00000000, A_GPR(1), &pc);
+
+ /* Digital Rear[l/r] = GPR[0/1] */
+ audigy_addefxop(sc, iACC3, A_EXTOUT(A_EXTOUT_REAR_L), A_C_00000000,
+ A_C_00000000, A_GPR(0), &pc);
+ audigy_addefxop(sc, iACC3, A_EXTOUT(A_EXTOUT_REAR_R), A_C_00000000,
+ A_C_00000000, A_GPR(1), &pc);
+#endif
+
+ /* ADC Recording buffer[l/r] = AC97Input[l/r] */
+ audigy_addefxop(sc, iACC3, A_EXTOUT(A_EXTOUT_ADC_CAP_L), A_C_00000000,
+ A_C_00000000, A_EXTIN(A_EXTIN_AC97_L), &pc);
+ audigy_addefxop(sc, iACC3, A_EXTOUT(A_EXTOUT_ADC_CAP_R), A_C_00000000,
+ A_C_00000000, A_EXTIN(A_EXTIN_AC97_R), &pc);
+
+ /* resume normal operations */
+ emu_wrptr(sc, 0, EMU_A_DBG, 0);
+}
+
+static void
+emu_initefx(struct sc_info *sc)
+{
+ int i;
+ u_int32_t pc = 16;
+
+ /* acc3 0,0,0,0 - NOPs */
+ for (i = 0; i < 512; i++) {
+ emu_wrefx(sc, i * 2, 0x10040);
+ emu_wrefx(sc, i * 2 + 1, 0x610040);
+ }
+
+ for (i = 0; i < 256; i++)
+ emu_wrptr(sc, 0, EMU_FXGPREGBASE + i, 0);
+
+ /* FX-8010 DSP Registers:
+ FX Bus
+ 0x000-0x00f : 16 registers
+ Input
+ 0x010/0x011 : AC97 Codec (l/r)
+ 0x012/0x013 : ADC, S/PDIF (l/r)
+ 0x014/0x015 : Mic(left), Zoom (l/r)
+ 0x016/0x017 : TOS link in (l/r)
+ 0x018/0x019 : Line/Mic 1 (l/r)
+ 0x01a/0x01b : COAX S/PDIF (l/r)
+ 0x01c/0x01d : Line/Mic 2 (l/r)
+ Output
+ 0x020/0x021 : AC97 Output (l/r)
+ 0x022/0x023 : TOS link out (l/r)
+ 0x024/0x025 : Center/LFE
+ 0x026/0x027 : LiveDrive Headphone (l/r)
+ 0x028/0x029 : Rear Channel (l/r)
+ 0x02a/0x02b : ADC Recording Buffer (l/r)
+ 0x02c : Mic Recording Buffer
+ 0x031/0x032 : Analog Center/LFE
+ Constants
+ 0x040 - 0x044 = 0 - 4
+ 0x045 = 0x8, 0x046 = 0x10, 0x047 = 0x20
+ 0x048 = 0x100, 0x049 = 0x10000, 0x04a = 0x80000
+ 0x04b = 0x10000000, 0x04c = 0x20000000, 0x04d = 0x40000000
+ 0x04e = 0x80000000, 0x04f = 0x7fffffff, 0x050 = 0xffffffff
+ 0x051 = 0xfffffffe, 0x052 = 0xc0000000, 0x053 = 0x41fbbcdc
+ 0x054 = 0x5a7ef9db, 0x055 = 0x00100000
+ Temporary Values
+ 0x056 : Accumulator
+ 0x057 : Condition Register
+ 0x058 : Noise source
+ 0x059 : Noise source
+ 0x05a : IRQ Register
+ 0x05b : TRAM Delay Base Address Count
+ General Purpose Registers
+ 0x100 - 0x1ff
+ Tank Memory Data Registers
+ 0x200 - 0x2ff
+ Tank Memory Address Registers
+ 0x300 - 0x3ff
+ */
+
+ /* Routing - this will be configurable in later version */
+
+ /* GPR[0/1] = FX * 4 + SPDIF-in */
+ emu_addefxop(sc, iMACINT0, GPR(0), EXTIN(EXTIN_SPDIF_CD_L),
+ FXBUS(FXBUS_PCM_LEFT), C_00000004, &pc);
+ emu_addefxop(sc, iMACINT0, GPR(1), EXTIN(EXTIN_SPDIF_CD_R),
+ FXBUS(FXBUS_PCM_RIGHT), C_00000004, &pc);
+
+ /* GPR[0/1] += APS-input */
+ emu_addefxop(sc, iACC3, GPR(0), GPR(0), C_00000000,
+ sc->APS ? EXTIN(EXTIN_TOSLINK_L) : C_00000000, &pc);
+ emu_addefxop(sc, iACC3, GPR(1), GPR(1), C_00000000,
+ sc->APS ? EXTIN(EXTIN_TOSLINK_R) : C_00000000, &pc);
+
+ /* FrontOut (AC97) = GPR[0/1] */
+ emu_addefxop(sc, iACC3, EXTOUT(EXTOUT_AC97_L), C_00000000,
+ C_00000000, GPR(0), &pc);
+ emu_addefxop(sc, iACC3, EXTOUT(EXTOUT_AC97_R), C_00000000,
+ C_00000001, GPR(1), &pc);
+
+ /* GPR[2] = GPR[0] (Left) / 2 + GPR[1] (Right) / 2 -- Central volume */
+ emu_addefxop(sc, iINTERP, GPR(2), GPR(1), C_40000000, GPR(0), &pc);
+
+#if 0
+ /* RearOut = (GPR[0/1] * RearVolume) >> 31 */
+ /* RearVolume = GPR[0x10/0x11] */
+ emu_addefxop(sc, iMAC0, EXTOUT(EXTOUT_REAR_L), C_00000000,
+ GPR(16), GPR(0), &pc);
+ emu_addefxop(sc, iMAC0, EXTOUT(EXTOUT_REAR_R), C_00000000,
+ GPR(17), GPR(1), &pc);
+#else
+ /* XXX This is just a copy to the channel, since we do not have
+ * a patch manager, it is useful for have another output enabled.
+ */
+
+ /* Rear[l/r] = GPR[0/1] */
+ emu_addefxop(sc, iACC3, EXTOUT(EXTOUT_REAR_L), C_00000000,
+ C_00000000, GPR(0), &pc);
+ emu_addefxop(sc, iACC3, EXTOUT(EXTOUT_REAR_R), C_00000000,
+ C_00000000, GPR(1), &pc);
+#endif
+
+ /* TOS out[l/r] = GPR[0/1] */
+ emu_addefxop(sc, iACC3, EXTOUT(EXTOUT_TOSLINK_L), C_00000000,
+ C_00000000, GPR(0), &pc);
+ emu_addefxop(sc, iACC3, EXTOUT(EXTOUT_TOSLINK_R), C_00000000,
+ C_00000000, GPR(1), &pc);
+
+ /* Center and Subwoofer configuration */
+ /* Analog Center = GPR[0] + GPR[2] */
+ emu_addefxop(sc, iACC3, EXTOUT(EXTOUT_ACENTER), C_00000000,
+ GPR(0), GPR(2), &pc);
+ /* Analog Sub = GPR[1] + GPR[2] */
+ emu_addefxop(sc, iACC3, EXTOUT(EXTOUT_ALFE), C_00000000,
+ GPR(1), GPR(2), &pc);
+ /* Digital Center = GPR[0] + GPR[2] */
+ emu_addefxop(sc, iACC3, EXTOUT(EXTOUT_AC97_CENTER), C_00000000,
+ GPR(0), GPR(2), &pc);
+ /* Digital Sub = GPR[1] + GPR[2] */
+ emu_addefxop(sc, iACC3, EXTOUT(EXTOUT_AC97_LFE), C_00000000,
+ GPR(1), GPR(2), &pc);
+
+ /* Headphones[l/r] = GPR[0/1] */
+ emu_addefxop(sc, iACC3, EXTOUT(EXTOUT_HEADPHONE_L), C_00000000,
+ C_00000000, GPR(0), &pc);
+ emu_addefxop(sc, iACC3, EXTOUT(EXTOUT_HEADPHONE_R), C_00000000,
+ C_00000000, GPR(1), &pc);
+
+ /* ADC Recording buffer[l/r] = AC97Input[l/r] */
+ emu_addefxop(sc, iACC3, EXTOUT(EXTOUT_ADC_CAP_L), C_00000000,
+ C_00000000, EXTIN(EXTIN_AC97_L), &pc);
+ emu_addefxop(sc, iACC3, EXTOUT(EXTOUT_ADC_CAP_R), C_00000000,
+ C_00000000, EXTIN(EXTIN_AC97_R), &pc);
+
+ /* resume normal operations */
+ emu_wrptr(sc, 0, EMU_DBG, 0);
+}
+
+/* Probe and attach the card */
+static int
+emu_init(struct sc_info *sc)
+{
+ u_int32_t spcs, ch, tmp, i;
+
+ if (sc->audigy) {
+ /* enable additional AC97 slots */
+ emu_wrptr(sc, 0, EMU_AC97SLOT, EMU_AC97SLOT_CENTER | EMU_AC97SLOT_LFE);
+ }
+
+ /* disable audio and lock cache */
+ emu_wr(sc, EMU_HCFG,
+ EMU_HCFG_LOCKSOUNDCACHE | EMU_HCFG_LOCKTANKCACHE_MASK | EMU_HCFG_MUTEBUTTONENABLE,
+ 4);
+
+ /* reset recording buffers */
+ emu_wrptr(sc, 0, EMU_MICBS, EMU_RECBS_BUFSIZE_NONE);
+ emu_wrptr(sc, 0, EMU_MICBA, 0);
+ emu_wrptr(sc, 0, EMU_FXBS, EMU_RECBS_BUFSIZE_NONE);
+ emu_wrptr(sc, 0, EMU_FXBA, 0);
+ emu_wrptr(sc, 0, EMU_ADCBS, EMU_RECBS_BUFSIZE_NONE);
+ emu_wrptr(sc, 0, EMU_ADCBA, 0);
+
+ /* disable channel interrupt */
+ emu_wr(sc, EMU_INTE,
+ EMU_INTE_INTERTIMERENB | EMU_INTE_SAMPLERATER | EMU_INTE_PCIERRENABLE,
+ 4);
+ emu_wrptr(sc, 0, EMU_CLIEL, 0);
+ emu_wrptr(sc, 0, EMU_CLIEH, 0);
+ emu_wrptr(sc, 0, EMU_SOLEL, 0);
+ emu_wrptr(sc, 0, EMU_SOLEH, 0);
+
+ /* wonder what these do... */
+ if (sc->audigy) {
+ emu_wrptr(sc, 0, EMU_SPBYPASS, 0xf00);
+ emu_wrptr(sc, 0, EMU_AC97SLOT, 0x3);
+ }
+
+ /* init envelope engine */
+ for (ch = 0; ch < NUM_G; ch++) {
+ emu_wrptr(sc, ch, EMU_CHAN_DCYSUSV, ENV_OFF);
+ emu_wrptr(sc, ch, EMU_CHAN_IP, 0);
+ emu_wrptr(sc, ch, EMU_CHAN_VTFT, 0xffff);
+ emu_wrptr(sc, ch, EMU_CHAN_CVCF, 0xffff);
+ emu_wrptr(sc, ch, EMU_CHAN_PTRX, 0);
+ emu_wrptr(sc, ch, EMU_CHAN_CPF, 0);
+ emu_wrptr(sc, ch, EMU_CHAN_CCR, 0);
+
+ emu_wrptr(sc, ch, EMU_CHAN_PSST, 0);
+ emu_wrptr(sc, ch, EMU_CHAN_DSL, 0x10);
+ emu_wrptr(sc, ch, EMU_CHAN_CCCA, 0);
+ emu_wrptr(sc, ch, EMU_CHAN_Z1, 0);
+ emu_wrptr(sc, ch, EMU_CHAN_Z2, 0);
+ emu_wrptr(sc, ch, EMU_CHAN_FXRT, 0xd01c0000);
+
+ emu_wrptr(sc, ch, EMU_CHAN_ATKHLDM, 0);
+ emu_wrptr(sc, ch, EMU_CHAN_DCYSUSM, 0);
+ emu_wrptr(sc, ch, EMU_CHAN_IFATN, 0xffff);
+ emu_wrptr(sc, ch, EMU_CHAN_PEFE, 0);
+ emu_wrptr(sc, ch, EMU_CHAN_FMMOD, 0);
+ emu_wrptr(sc, ch, EMU_CHAN_TREMFRQ, 24); /* 1 Hz */
+ emu_wrptr(sc, ch, EMU_CHAN_FM2FRQ2, 24); /* 1 Hz */
+ emu_wrptr(sc, ch, EMU_CHAN_TEMPENV, 0);
+
+ /*** these are last so OFF prevents writing ***/
+ emu_wrptr(sc, ch, EMU_CHAN_LFOVAL2, 0);
+ emu_wrptr(sc, ch, EMU_CHAN_LFOVAL1, 0);
+ emu_wrptr(sc, ch, EMU_CHAN_ATKHLDV, 0);
+ emu_wrptr(sc, ch, EMU_CHAN_ENVVOL, 0);
+ emu_wrptr(sc, ch, EMU_CHAN_ENVVAL, 0);
+
+ if (sc->audigy) {
+ /* audigy cards need this to initialize correctly */
+ emu_wrptr(sc, ch, 0x4c, 0);
+ emu_wrptr(sc, ch, 0x4d, 0);
+ emu_wrptr(sc, ch, 0x4e, 0);
+ emu_wrptr(sc, ch, 0x4f, 0);
+ /* set default routing */
+ emu_wrptr(sc, ch, EMU_A_CHAN_FXRT1, 0x03020100);
+ emu_wrptr(sc, ch, EMU_A_CHAN_FXRT2, 0x3f3f3f3f);
+ emu_wrptr(sc, ch, EMU_A_CHAN_SENDAMOUNTS, 0);
+ }
+
+ sc->voice[ch].vnum = ch;
+ sc->voice[ch].slave = NULL;
+ sc->voice[ch].busy = 0;
+ sc->voice[ch].ismaster = 0;
+ sc->voice[ch].running = 0;
+ sc->voice[ch].b16 = 0;
+ sc->voice[ch].stereo = 0;
+ sc->voice[ch].speed = 0;
+ sc->voice[ch].start = 0;
+ sc->voice[ch].end = 0;
+ sc->voice[ch].channel = NULL;
+ }
+ sc->pnum = sc->rnum = 0;
+
+ /*
+ * Init to 0x02109204 :
+ * Clock accuracy = 0 (1000ppm)
+ * Sample Rate = 2 (48kHz)
+ * Audio Channel = 1 (Left of 2)
+ * Source Number = 0 (Unspecified)
+ * Generation Status = 1 (Original for Cat Code 12)
+ * Cat Code = 12 (Digital Signal Mixer)
+ * Mode = 0 (Mode 0)
+ * Emphasis = 0 (None)
+ * CP = 1 (Copyright unasserted)
+ * AN = 0 (Audio data)
+ * P = 0 (Consumer)
+ */
+ spcs = EMU_SPCS_CLKACCY_1000PPM | EMU_SPCS_SAMPLERATE_48 |
+ EMU_SPCS_CHANNELNUM_LEFT | EMU_SPCS_SOURCENUM_UNSPEC |
+ EMU_SPCS_GENERATIONSTATUS | 0x00001200 | 0x00000000 |
+ EMU_SPCS_EMPHASIS_NONE | EMU_SPCS_COPYRIGHT;
+ emu_wrptr(sc, 0, EMU_SPCS0, spcs);
+ emu_wrptr(sc, 0, EMU_SPCS1, spcs);
+ emu_wrptr(sc, 0, EMU_SPCS2, spcs);
+
+ if (!sc->audigy)
+ emu_initefx(sc);
+ else if (sc->audigy2) { /* Audigy 2 */
+ /* from ALSA initialization code: */
+
+ /* Hack for Alice3 to work independent of haP16V driver */
+ u_int32_t tmp;
+
+ /* Setup SRCMulti_I2S SamplingRate */
+ tmp = emu_rdptr(sc, 0, EMU_A_SPDIF_SAMPLERATE) & 0xfffff1ff;
+ emu_wrptr(sc, 0, EMU_A_SPDIF_SAMPLERATE, tmp | 0x400);
+
+ /* Setup SRCSel (Enable SPDIF, I2S SRCMulti) */
+ emu_wr(sc, 0x20, 0x00600000, 4);
+ emu_wr(sc, 0x24, 0x00000014, 4);
+
+ /* Setup SRCMulti Input Audio Enable */
+ emu_wr(sc, 0x20, 0x006e0000, 4);
+ emu_wr(sc, 0x24, 0xff00ff00, 4);
+ }
+
+ SLIST_INIT(&sc->mem.blocks);
+ sc->mem.ptb_pages = emu_malloc(sc, EMUMAXPAGES * sizeof(u_int32_t),
+ &sc->mem.ptb_pages_addr, &sc->mem.ptb_map);
+ if (sc->mem.ptb_pages == NULL)
+ return -1;
+
+ sc->mem.silent_page = emu_malloc(sc, EMUPAGESIZE,
+ &sc->mem.silent_page_addr, &sc->mem.silent_map);
+ if (sc->mem.silent_page == NULL) {
+ emu_free(sc, sc->mem.ptb_pages, sc->mem.ptb_map);
+ return -1;
+ }
+ /* Clear page with silence & setup all pointers to this page */
+ bzero(sc->mem.silent_page, EMUPAGESIZE);
+ tmp = (u_int32_t)(sc->mem.silent_page_addr) << 1;
+ for (i = 0; i < EMUMAXPAGES; i++)
+ sc->mem.ptb_pages[i] = tmp | i;
+
+ emu_wrptr(sc, 0, EMU_PTB, (sc->mem.ptb_pages_addr));
+ emu_wrptr(sc, 0, EMU_TCB, 0); /* taken from original driver */
+ emu_wrptr(sc, 0, EMU_TCBS, 0); /* taken from original driver */
+
+ for (ch = 0; ch < NUM_G; ch++) {
+ emu_wrptr(sc, ch, EMU_CHAN_MAPA, tmp | EMU_CHAN_MAP_PTI_MASK);
+ emu_wrptr(sc, ch, EMU_CHAN_MAPB, tmp | EMU_CHAN_MAP_PTI_MASK);
+ }
+
+ /* emu_memalloc(sc, EMUPAGESIZE); */
+ /*
+ * Hokay, now enable the AUD bit
+ *
+ * Audigy
+ * Enable Audio = 0 (enabled after fx processor initialization)
+ * Mute Disable Audio = 0
+ * Joystick = 1
+ *
+ * Audigy 2
+ * Enable Audio = 1
+ * Mute Disable Audio = 0
+ * Joystick = 1
+ * GP S/PDIF AC3 Enable = 1
+ * CD S/PDIF AC3 Enable = 1
+ *
+ * EMU10K1
+ * Enable Audio = 1
+ * Mute Disable Audio = 0
+ * Lock Tank Memory = 1
+ * Lock Sound Memory = 0
+ * Auto Mute = 1
+ */
+
+ if (sc->audigy) {
+ tmp = EMU_HCFG_AUTOMUTE | EMU_HCFG_JOYENABLE;
+ if (sc->audigy2) /* Audigy 2 */
+ tmp = EMU_HCFG_AUDIOENABLE | EMU_HCFG_AC3ENABLE_CDSPDIF |
+ EMU_HCFG_AC3ENABLE_GPSPDIF;
+ emu_wr(sc, EMU_HCFG, tmp, 4);
+
+ audigy_initefx(sc);
+
+ /* from ALSA initialization code: */
+
+ /* enable audio and disable both audio/digital outputs */
+ emu_wr(sc, EMU_HCFG, emu_rd(sc, EMU_HCFG, 4) | EMU_HCFG_AUDIOENABLE, 4);
+ emu_wr(sc, EMU_A_IOCFG, emu_rd(sc, EMU_A_IOCFG, 4) & ~EMU_A_IOCFG_GPOUT_AD,
+ 4);
+ if (sc->audigy2) { /* Audigy 2 */
+ /* Unmute Analog.
+ * Set GPO6 to 1 for Apollo. This has to be done after
+ * init Alice3 I2SOut beyond 48kHz.
+ * So, sequence is important.
+ */
+ emu_wr(sc, EMU_A_IOCFG,
+ emu_rd(sc, EMU_A_IOCFG, 4) | EMU_A_IOCFG_GPOUT_A, 4);
+ }
+ } else {
+ /* EMU10K1 initialization code */
+ tmp = EMU_HCFG_AUDIOENABLE | EMU_HCFG_LOCKTANKCACHE_MASK
+ | EMU_HCFG_AUTOMUTE;
+ if (sc->rev >= 6)
+ tmp |= EMU_HCFG_JOYENABLE;
+
+ emu_wr(sc, EMU_HCFG, tmp, 4);
+
+ /* TOSLink detection */
+ sc->tos_link = 0;
+ tmp = emu_rd(sc, EMU_HCFG, 4);
+ if (tmp & (EMU_HCFG_GPINPUT0 | EMU_HCFG_GPINPUT1)) {
+ emu_wr(sc, EMU_HCFG, tmp | EMU_HCFG_GPOUT1, 4);
+ DELAY(50);
+ if (tmp != (emu_rd(sc, EMU_HCFG, 4) & ~EMU_HCFG_GPOUT1)) {
+ sc->tos_link = 1;
+ emu_wr(sc, EMU_HCFG, tmp, 4);
+ }
+ }
+ }
+
+ return 0;
+}
+
+static int
+emu_uninit(struct sc_info *sc)
+{
+ u_int32_t ch;
+
+ emu_wr(sc, EMU_INTE, 0, 4);
+ for (ch = 0; ch < NUM_G; ch++)
+ emu_wrptr(sc, ch, EMU_CHAN_DCYSUSV, ENV_OFF);
+ for (ch = 0; ch < NUM_G; ch++) {
+ emu_wrptr(sc, ch, EMU_CHAN_VTFT, 0);
+ emu_wrptr(sc, ch, EMU_CHAN_CVCF, 0);
+ emu_wrptr(sc, ch, EMU_CHAN_PTRX, 0);
+ emu_wrptr(sc, ch, EMU_CHAN_CPF, 0);
+ }
+
+ if (sc->audigy) { /* stop fx processor */
+ emu_wrptr(sc, 0, EMU_A_DBG, EMU_A_DBG_SINGLE_STEP);
+ }
+
+ /* disable audio and lock cache */
+ emu_wr(sc, EMU_HCFG,
+ EMU_HCFG_LOCKSOUNDCACHE | EMU_HCFG_LOCKTANKCACHE_MASK | EMU_HCFG_MUTEBUTTONENABLE,
+ 4);
+
+ emu_wrptr(sc, 0, EMU_PTB, 0);
+ /* reset recording buffers */
+ emu_wrptr(sc, 0, EMU_MICBS, EMU_RECBS_BUFSIZE_NONE);
+ emu_wrptr(sc, 0, EMU_MICBA, 0);
+ emu_wrptr(sc, 0, EMU_FXBS, EMU_RECBS_BUFSIZE_NONE);
+ emu_wrptr(sc, 0, EMU_FXBA, 0);
+ emu_wrptr(sc, 0, EMU_FXWC, 0);
+ emu_wrptr(sc, 0, EMU_ADCBS, EMU_RECBS_BUFSIZE_NONE);
+ emu_wrptr(sc, 0, EMU_ADCBA, 0);
+ emu_wrptr(sc, 0, EMU_TCB, 0);
+ emu_wrptr(sc, 0, EMU_TCBS, 0);
+
+ /* disable channel interrupt */
+ emu_wrptr(sc, 0, EMU_CLIEL, 0);
+ emu_wrptr(sc, 0, EMU_CLIEH, 0);
+ emu_wrptr(sc, 0, EMU_SOLEL, 0);
+ emu_wrptr(sc, 0, EMU_SOLEH, 0);
+
+ /* init envelope engine */
+ if (!SLIST_EMPTY(&sc->mem.blocks))
+ device_printf(sc->dev, "warning: memblock list not empty\n");
+ emu_free(sc, sc->mem.ptb_pages, sc->mem.ptb_map);
+ emu_free(sc, sc->mem.silent_page, sc->mem.silent_map);
+
+ if(sc->mpu)
+ mpu401_uninit(sc->mpu);
+ return 0;
+}
+
+static int
+emu_pci_probe(device_t dev)
+{
+ char *s = NULL;
+
+ switch (pci_get_devid(dev)) {
+ case EMU10K1_PCI_ID:
+ s = "Creative EMU10K1";
+ break;
+
+ case EMU10K2_PCI_ID:
+ if (pci_get_revid(dev) == 0x04)
+ s = "Creative Audigy 2 (EMU10K2)";
+ else
+ s = "Creative Audigy (EMU10K2)";
+ break;
+
+ case EMU10K3_PCI_ID:
+ s = "Creative Audigy 2 (EMU10K3)";
+ break;
+
+ default:
+ return ENXIO;
+ }
+
+ device_set_desc(dev, s);
+ return BUS_PROBE_LOW_PRIORITY;
+}
+
+static int
+emu_pci_attach(device_t dev)
+{
+ struct ac97_info *codec = NULL;
+ struct sc_info *sc;
+ int i, gotmic;
+ char status[SND_STATUSLEN];
+
+ sc = malloc(sizeof(*sc), M_DEVBUF, M_WAITOK | M_ZERO);
+ sc->lock = snd_mtxcreate(device_get_nameunit(dev), "snd_emu10k1 softc");
+ sc->dev = dev;
+ sc->type = pci_get_devid(dev);
+ sc->rev = pci_get_revid(dev);
+ sc->audigy = sc->type == EMU10K2_PCI_ID || sc->type == EMU10K3_PCI_ID;
+ sc->audigy2 = (sc->audigy && sc->rev == 0x04);
+ sc->nchans = sc->audigy ? 8 : 4;
+ sc->addrmask = sc->audigy ? EMU_A_PTR_ADDR_MASK : EMU_PTR_ADDR_MASK;
+
+ pci_enable_busmaster(dev);
+
+ i = PCIR_BAR(0);
+ sc->reg = bus_alloc_resource_any(dev, SYS_RES_IOPORT, &i, RF_ACTIVE);
+ if (sc->reg == NULL) {
+ device_printf(dev, "unable to map register space\n");
+ goto bad;
+ }
+ sc->st = rman_get_bustag(sc->reg);
+ sc->sh = rman_get_bushandle(sc->reg);
+
+ sc->bufsz = pcm_getbuffersize(dev, 4096, EMU_DEFAULT_BUFSZ, 65536);
+
+ if (bus_dma_tag_create(/*parent*/bus_get_dma_tag(dev), /*alignment*/2,
+ /*boundary*/0,
+ /*lowaddr*/(1U << 31) - 1, /* can only access 0-2gb */
+ /*highaddr*/BUS_SPACE_MAXADDR,
+ /*filter*/NULL, /*filterarg*/NULL,
+ /*maxsize*/sc->bufsz, /*nsegments*/1, /*maxsegz*/0x3ffff,
+ /*flags*/0, /*lockfunc*/NULL, /*lockarg*/NULL,
+ &sc->parent_dmat) != 0) {
+ device_printf(dev, "unable to create dma tag\n");
+ goto bad;
+ }
+
+ if (emu_init(sc) == -1) {
+ device_printf(dev, "unable to initialize the card\n");
+ goto bad;
+ }
+
+ codec = AC97_CREATE(dev, sc, emu_ac97);
+ if (codec == NULL) goto bad;
+ gotmic = (ac97_getcaps(codec) & AC97_CAP_MICCHANNEL) ? 1 : 0;
+ if (mixer_init(dev, ac97_getmixerclass(), codec) == -1) goto bad;
+
+ emu_midiattach(sc);
+
+ i = 0;
+ sc->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &i,
+ RF_ACTIVE | RF_SHAREABLE);
+ if (!sc->irq ||
+ snd_setup_intr(dev, sc->irq, INTR_MPSAFE, emu_intr, sc, &sc->ih)) {
+ device_printf(dev, "unable to map interrupt\n");
+ goto bad;
+ }
+
+ snprintf(status, SND_STATUSLEN, "port 0x%jx irq %jd on %s",
+ rman_get_start(sc->reg), rman_get_start(sc->irq),
+ device_get_nameunit(device_get_parent(dev)));
+
+ pcm_init(dev, sc);
+ for (i = 0; i < sc->nchans; i++)
+ pcm_addchan(dev, PCMDIR_PLAY, &emupchan_class, sc);
+ for (i = 0; i < (gotmic ? 3 : 2); i++)
+ pcm_addchan(dev, PCMDIR_REC, &emurchan_class, sc);
+
+ if (pcm_register(dev, status))
+ goto bad;
+
+ return 0;
+
+bad:
+ if (codec) ac97_destroy(codec);
+ if (sc->reg) bus_release_resource(dev, SYS_RES_IOPORT, PCIR_BAR(0), sc->reg);
+ if (sc->ih) bus_teardown_intr(dev, sc->irq, sc->ih);
+ if (sc->irq) bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq);
+ if (sc->parent_dmat) bus_dma_tag_destroy(sc->parent_dmat);
+ if (sc->lock) snd_mtxfree(sc->lock);
+ free(sc, M_DEVBUF);
+ return ENXIO;
+}
+
+static int
+emu_pci_detach(device_t dev)
+{
+ int r;
+ struct sc_info *sc;
+
+ r = pcm_unregister(dev);
+ if (r)
+ return r;
+
+ sc = pcm_getdevinfo(dev);
+ /* shutdown chip */
+ emu_uninit(sc);
+
+ bus_release_resource(dev, SYS_RES_IOPORT, PCIR_BAR(0), sc->reg);
+ bus_teardown_intr(dev, sc->irq, sc->ih);
+ bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq);
+ bus_dma_tag_destroy(sc->parent_dmat);
+ snd_mtxfree(sc->lock);
+ free(sc, M_DEVBUF);
+
+ return 0;
+}
+
+/* add suspend, resume */
+static device_method_t emu_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, emu_pci_probe),
+ DEVMETHOD(device_attach, emu_pci_attach),
+ DEVMETHOD(device_detach, emu_pci_detach),
+
+ DEVMETHOD_END
+};
+
+static driver_t emu_driver = {
+ "pcm",
+ emu_methods,
+ PCM_SOFTC_SIZE,
+};
+
+DRIVER_MODULE(snd_emu10k1, pci, emu_driver, NULL, NULL);
+MODULE_DEPEND(snd_emu10k1, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER);
+MODULE_VERSION(snd_emu10k1, 1);
+MODULE_DEPEND(snd_emu10k1, midi, 1, 1, 1);
+
+/* dummy driver to silence the joystick device */
+static int
+emujoy_pci_probe(device_t dev)
+{
+ char *s = NULL;
+
+ switch (pci_get_devid(dev)) {
+ case 0x70021102:
+ s = "Creative EMU10K1 Joystick";
+ device_quiet(dev);
+ break;
+ case 0x70031102:
+ s = "Creative EMU10K2 Joystick";
+ device_quiet(dev);
+ break;
+ }
+
+ if (s) device_set_desc(dev, s);
+ return s ? -1000 : ENXIO;
+}
+
+static int
+emujoy_pci_attach(device_t dev)
+{
+
+ return 0;
+}
+
+static int
+emujoy_pci_detach(device_t dev)
+{
+
+ return 0;
+}
+
+static device_method_t emujoy_methods[] = {
+ DEVMETHOD(device_probe, emujoy_pci_probe),
+ DEVMETHOD(device_attach, emujoy_pci_attach),
+ DEVMETHOD(device_detach, emujoy_pci_detach),
+
+ DEVMETHOD_END
+};
+
+static driver_t emujoy_driver = {
+ "emujoy",
+ emujoy_methods,
+ 1 /* no softc */
+};
+
+DRIVER_MODULE(emujoy, pci, emujoy_driver, NULL, NULL);