diff options
Diffstat (limited to 'sys/dev/sound/isa/emu8000.c')
-rw-r--r-- | sys/dev/sound/isa/emu8000.c | 2037 |
1 files changed, 2037 insertions, 0 deletions
diff --git a/sys/dev/sound/isa/emu8000.c b/sys/dev/sound/isa/emu8000.c new file mode 100644 index 0000000..d69d67c --- /dev/null +++ b/sys/dev/sound/isa/emu8000.c @@ -0,0 +1,2037 @@ +/* + * Low level EMU8000 chip driver for FreeBSD. This handles io against + * /dev/midi, the midi {in, out}put event queues and the event/message + * operation to the EMU8000 chip. + * + * (C) 1999 Seigo Tanimura + * + * 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. + * + * $FreeBSD$ + * + */ + + +#include "opt_devfs.h" + +#include <dev/sound/midi/midi.h> + +#include <isa/isavar.h> + +static devclass_t midi_devclass; + +#ifndef DDB +#undef DDB +#define DDB(x) +#endif /* DDB */ + +/* These are the specs of EMU8000. */ +#define EMU8K_MAXVOICE 32 +#define EMU8K_MAXINFO 256 + +#define EMU8K_IDX_DATA0 0 +#define EMU8K_IDX_DATA1 1 +#define EMU8K_IDX_DATA2 1 +#define EMU8K_IDX_DATA3 2 +#define EMU8K_IDX_PTR 2 + +#define EMU8K_PORT_DATA0 0 +#define EMU8K_PORT_DATA1 0 +#define EMU8K_PORT_DATA2 2 +#define EMU8K_PORT_DATA3 0 +#define EMU8K_PORT_PTR 2 + +#define EMU8K_DRAM_RAM 0x200000 +#define EMU8K_DRAM_MAX 0xffffe0 + +/* And some convinient macros. */ +#define EMU8K_DMA_LEFT 0x00 +#define EMU8K_DMA_RIGHT 0x01 +#define EMU8K_DMA_LR 0x01 +#define EMU8K_DMA_READ 0x00 +#define EMU8K_DMA_WRITE 0x02 +#define EMU8K_DMA_RW 0x02 +#define EMU8K_DMA_MASK 0x03 + +/* The followings are the init array for EMU8000, originally in ADIP. */ + +/* Set 1 */ +static u_short init1_1[32] = +{ + 0x03ff, 0x0030, 0x07ff, 0x0130, 0x0bff, 0x0230, 0x0fff, 0x0330, + 0x13ff, 0x0430, 0x17ff, 0x0530, 0x1bff, 0x0630, 0x1fff, 0x0730, + 0x23ff, 0x0830, 0x27ff, 0x0930, 0x2bff, 0x0a30, 0x2fff, 0x0b30, + 0x33ff, 0x0c30, 0x37ff, 0x0d30, 0x3bff, 0x0e30, 0x3fff, 0x0f30, +}; + +static u_short init1_2[32] = +{ + 0x43ff, 0x0030, 0x47ff, 0x0130, 0x4bff, 0x0230, 0x4fff, 0x0330, + 0x53ff, 0x0430, 0x57ff, 0x0530, 0x5bff, 0x0630, 0x5fff, 0x0730, + 0x63ff, 0x0830, 0x67ff, 0x0930, 0x6bff, 0x0a30, 0x6fff, 0x0b30, + 0x73ff, 0x0c30, 0x77ff, 0x0d30, 0x7bff, 0x0e30, 0x7fff, 0x0f30, +}; + +static u_short init1_3[32] = +{ + 0x83ff, 0x0030, 0x87ff, 0x0130, 0x8bff, 0x0230, 0x8fff, 0x0330, + 0x93ff, 0x0430, 0x97ff, 0x0530, 0x9bff, 0x0630, 0x9fff, 0x0730, + 0xa3ff, 0x0830, 0xa7ff, 0x0930, 0xabff, 0x0a30, 0xafff, 0x0b30, + 0xb3ff, 0x0c30, 0xb7ff, 0x0d30, 0xbbff, 0x0e30, 0xbfff, 0x0f30, +}; + +static u_short init1_4[32] = +{ + 0xc3ff, 0x0030, 0xc7ff, 0x0130, 0xcbff, 0x0230, 0xcfff, 0x0330, + 0xd3ff, 0x0430, 0xd7ff, 0x0530, 0xdbff, 0x0630, 0xdfff, 0x0730, + 0xe3ff, 0x0830, 0xe7ff, 0x0930, 0xebff, 0x0a30, 0xefff, 0x0b30, + 0xf3ff, 0x0c30, 0xf7ff, 0x0d30, 0xfbff, 0x0e30, 0xffff, 0x0f30, +}; + +/* Set 2 */ + +static u_short init2_1[32] = +{ + 0x03ff, 0x8030, 0x07ff, 0x8130, 0x0bff, 0x8230, 0x0fff, 0x8330, + 0x13ff, 0x8430, 0x17ff, 0x8530, 0x1bff, 0x8630, 0x1fff, 0x8730, + 0x23ff, 0x8830, 0x27ff, 0x8930, 0x2bff, 0x8a30, 0x2fff, 0x8b30, + 0x33ff, 0x8c30, 0x37ff, 0x8d30, 0x3bff, 0x8e30, 0x3fff, 0x8f30, +}; + +static u_short init2_2[32] = +{ + 0x43ff, 0x8030, 0x47ff, 0x8130, 0x4bff, 0x8230, 0x4fff, 0x8330, + 0x53ff, 0x8430, 0x57ff, 0x8530, 0x5bff, 0x8630, 0x5fff, 0x8730, + 0x63ff, 0x8830, 0x67ff, 0x8930, 0x6bff, 0x8a30, 0x6fff, 0x8b30, + 0x73ff, 0x8c30, 0x77ff, 0x8d30, 0x7bff, 0x8e30, 0x7fff, 0x8f30, +}; + +static u_short init2_3[32] = +{ + 0x83ff, 0x8030, 0x87ff, 0x8130, 0x8bff, 0x8230, 0x8fff, 0x8330, + 0x93ff, 0x8430, 0x97ff, 0x8530, 0x9bff, 0x8630, 0x9fff, 0x8730, + 0xa3ff, 0x8830, 0xa7ff, 0x8930, 0xabff, 0x8a30, 0xafff, 0x8b30, + 0xb3ff, 0x8c30, 0xb7ff, 0x8d30, 0xbbff, 0x8e30, 0xbfff, 0x8f30, +}; + +static u_short init2_4[32] = +{ + 0xc3ff, 0x8030, 0xc7ff, 0x8130, 0xcbff, 0x8230, 0xcfff, 0x8330, + 0xd3ff, 0x8430, 0xd7ff, 0x8530, 0xdbff, 0x8630, 0xdfff, 0x8730, + 0xe3ff, 0x8830, 0xe7ff, 0x8930, 0xebff, 0x8a30, 0xefff, 0x8b30, + 0xf3ff, 0x8c30, 0xf7ff, 0x8d30, 0xfbff, 0x8e30, 0xffff, 0x8f30, +}; + +/* Set 3 */ + +static u_short init3_1[32] = +{ + 0x0C10, 0x8470, 0x14FE, 0xB488, 0x167F, 0xA470, 0x18E7, 0x84B5, + 0x1B6E, 0x842A, 0x1F1D, 0x852A, 0x0DA3, 0x8F7C, 0x167E, 0xF254, + 0x0000, 0x842A, 0x0001, 0x852A, 0x18E6, 0x8BAA, 0x1B6D, 0xF234, + 0x229F, 0x8429, 0x2746, 0x8529, 0x1F1C, 0x86E7, 0x229E, 0xF224, +}; + +static u_short init3_2[32] = +{ + 0x0DA4, 0x8429, 0x2C29, 0x8529, 0x2745, 0x87F6, 0x2C28, 0xF254, + 0x383B, 0x8428, 0x320F, 0x8528, 0x320E, 0x8F02, 0x1341, 0xF264, + 0x3EB6, 0x8428, 0x3EB9, 0x8528, 0x383A, 0x8FA9, 0x3EB5, 0xF294, + 0x3EB7, 0x8474, 0x3EBA, 0x8575, 0x3EB8, 0xC4C3, 0x3EBB, 0xC5C3, +}; + +static u_short init3_3[32] = +{ + 0x0000, 0xA404, 0x0001, 0xA504, 0x141F, 0x8671, 0x14FD, 0x8287, + 0x3EBC, 0xE610, 0x3EC8, 0x8C7B, 0x031A, 0x87E6, 0x3EC8, 0x86F7, + 0x3EC0, 0x821E, 0x3EBE, 0xD208, 0x3EBD, 0x821F, 0x3ECA, 0x8386, + 0x3EC1, 0x8C03, 0x3EC9, 0x831E, 0x3ECA, 0x8C4C, 0x3EBF, 0x8C55, +}; + +static u_short init3_4[32] = +{ + 0x3EC9, 0xC208, 0x3EC4, 0xBC84, 0x3EC8, 0x8EAD, 0x3EC8, 0xD308, + 0x3EC2, 0x8F7E, 0x3ECB, 0x8219, 0x3ECB, 0xD26E, 0x3EC5, 0x831F, + 0x3EC6, 0xC308, 0x3EC3, 0xB2FF, 0x3EC9, 0x8265, 0x3EC9, 0x8319, + 0x1342, 0xD36E, 0x3EC7, 0xB3FF, 0x0000, 0x8365, 0x1420, 0x9570, +}; + +/* Set 4 */ + +static u_short init4_1[32] = +{ + 0x0C10, 0x8470, 0x14FE, 0xB488, 0x167F, 0xA470, 0x18E7, 0x84B5, + 0x1B6E, 0x842A, 0x1F1D, 0x852A, 0x0DA3, 0x0F7C, 0x167E, 0x7254, + 0x0000, 0x842A, 0x0001, 0x852A, 0x18E6, 0x0BAA, 0x1B6D, 0x7234, + 0x229F, 0x8429, 0x2746, 0x8529, 0x1F1C, 0x06E7, 0x229E, 0x7224, +}; + +static u_short init4_2[32] = +{ + 0x0DA4, 0x8429, 0x2C29, 0x8529, 0x2745, 0x07F6, 0x2C28, 0x7254, + 0x383B, 0x8428, 0x320F, 0x8528, 0x320E, 0x0F02, 0x1341, 0x7264, + 0x3EB6, 0x8428, 0x3EB9, 0x8528, 0x383A, 0x0FA9, 0x3EB5, 0x7294, + 0x3EB7, 0x8474, 0x3EBA, 0x8575, 0x3EB8, 0x44C3, 0x3EBB, 0x45C3, +}; + +static u_short init4_3[32] = +{ + 0x0000, 0xA404, 0x0001, 0xA504, 0x141F, 0x0671, 0x14FD, 0x0287, + 0x3EBC, 0xE610, 0x3EC8, 0x0C7B, 0x031A, 0x07E6, 0x3EC8, 0x86F7, + 0x3EC0, 0x821E, 0x3EBE, 0xD208, 0x3EBD, 0x021F, 0x3ECA, 0x0386, + 0x3EC1, 0x0C03, 0x3EC9, 0x031E, 0x3ECA, 0x8C4C, 0x3EBF, 0x0C55, +}; + +static u_short init4_4[32] = +{ + 0x3EC9, 0xC208, 0x3EC4, 0xBC84, 0x3EC8, 0x0EAD, 0x3EC8, 0xD308, + 0x3EC2, 0x8F7E, 0x3ECB, 0x0219, 0x3ECB, 0xD26E, 0x3EC5, 0x031F, + 0x3EC6, 0xC308, 0x3EC3, 0x32FF, 0x3EC9, 0x0265, 0x3EC9, 0x8319, + 0x1342, 0xD36E, 0x3EC7, 0x33FF, 0x0000, 0x8365, 0x1420, 0x9570, +}; + +/* The followings are the register, the channel and the port for the EMU8000 registers. */ +struct _emu_register { + int reg; /* Register */ + int index; /* Index */ + int port; /* Port */ + int chn; /* Channel */ + int size; /* Size, 0 == word, 1 == double word */ +}; + +#define EMU8K_CHN_ANY (-1) + +static struct _emu_register emu_regs[] = +{ + /* Reg, Index, Port, Channel, Size */ + { 0, EMU8K_IDX_DATA0, EMU8K_PORT_DATA0, EMU8K_CHN_ANY, 1}, /* CPF */ + { 1, EMU8K_IDX_DATA0, EMU8K_PORT_DATA0, EMU8K_CHN_ANY, 1}, /* PTRX */ + { 2, EMU8K_IDX_DATA0, EMU8K_PORT_DATA0, EMU8K_CHN_ANY, 1}, /* CVCF */ + { 3, EMU8K_IDX_DATA0, EMU8K_PORT_DATA0, EMU8K_CHN_ANY, 1}, /* VTFT */ + { 6, EMU8K_IDX_DATA0, EMU8K_PORT_DATA0, EMU8K_CHN_ANY, 1}, /* PSST */ + { 7, EMU8K_IDX_DATA0, EMU8K_PORT_DATA0, EMU8K_CHN_ANY, 1}, /* CSL */ + { 0, EMU8K_IDX_DATA1, EMU8K_PORT_DATA1, EMU8K_CHN_ANY, 1}, /* CCCA */ + { 1, EMU8K_IDX_DATA1, EMU8K_PORT_DATA1, 9, 1}, /* HWCF4 */ + { 1, EMU8K_IDX_DATA1, EMU8K_PORT_DATA1, 10, 1}, /* HWCF5 */ + { 1, EMU8K_IDX_DATA1, EMU8K_PORT_DATA1, 13, 1}, /* HWCF6 */ + { 1, EMU8K_IDX_DATA1, EMU8K_PORT_DATA1, 20, 1}, /* SMALR */ + { 1, EMU8K_IDX_DATA1, EMU8K_PORT_DATA1, 21, 1}, /* SMARR */ + { 1, EMU8K_IDX_DATA1, EMU8K_PORT_DATA1, 22, 1}, /* SMALW */ + { 1, EMU8K_IDX_DATA1, EMU8K_PORT_DATA1, 23, 1}, /* SMARW */ + { 1, EMU8K_IDX_DATA1, EMU8K_PORT_DATA1, 26, 0}, /* SMLD */ + { 1, EMU8K_IDX_DATA2, EMU8K_PORT_DATA2, 26, 0}, /* SMRD */ + { 1, EMU8K_IDX_DATA2, EMU8K_PORT_DATA2, 27, 0}, /* WC */ + { 1, EMU8K_IDX_DATA1, EMU8K_PORT_DATA1, 29, 0}, /* HWCF1 */ + { 1, EMU8K_IDX_DATA1, EMU8K_PORT_DATA1, 30, 0}, /* HWCF2 */ + { 1, EMU8K_IDX_DATA1, EMU8K_PORT_DATA1, 31, 0}, /* HWCF3 */ + { 2, EMU8K_IDX_DATA1, EMU8K_PORT_DATA1, EMU8K_CHN_ANY, 0}, /* INIT1 */ + { 2, EMU8K_IDX_DATA2, EMU8K_PORT_DATA2, EMU8K_CHN_ANY, 0}, /* INIT2 */ + { 3, EMU8K_IDX_DATA1, EMU8K_PORT_DATA1, EMU8K_CHN_ANY, 0}, /* INIT3 */ + { 3, EMU8K_IDX_DATA2, EMU8K_PORT_DATA2, EMU8K_CHN_ANY, 0}, /* INIT4 */ + { 4, EMU8K_IDX_DATA1, EMU8K_PORT_DATA1, EMU8K_CHN_ANY, 0}, /* ENVVOL */ + { 5, EMU8K_IDX_DATA1, EMU8K_PORT_DATA1, EMU8K_CHN_ANY, 0}, /* DCYSUSV */ + { 6, EMU8K_IDX_DATA1, EMU8K_PORT_DATA1, EMU8K_CHN_ANY, 0}, /* ENVVAL */ + { 7, EMU8K_IDX_DATA1, EMU8K_PORT_DATA1, EMU8K_CHN_ANY, 0}, /* DCYSUS */ + { 4, EMU8K_IDX_DATA2, EMU8K_PORT_DATA2, EMU8K_CHN_ANY, 0}, /* ATKHLDV */ + { 5, EMU8K_IDX_DATA2, EMU8K_PORT_DATA2, EMU8K_CHN_ANY, 0}, /* LFO1VAL */ + { 6, EMU8K_IDX_DATA2, EMU8K_PORT_DATA2, EMU8K_CHN_ANY, 0}, /* ATKHLD */ + { 7, EMU8K_IDX_DATA2, EMU8K_PORT_DATA2, EMU8K_CHN_ANY, 0}, /* LFO2VAL */ + { 0, EMU8K_IDX_DATA3, EMU8K_PORT_DATA3, EMU8K_CHN_ANY, 0}, /* IP */ + { 1, EMU8K_IDX_DATA3, EMU8K_PORT_DATA3, EMU8K_CHN_ANY, 0}, /* IFATN */ + { 2, EMU8K_IDX_DATA3, EMU8K_PORT_DATA3, EMU8K_CHN_ANY, 0}, /* PEFE */ + { 3, EMU8K_IDX_DATA3, EMU8K_PORT_DATA3, EMU8K_CHN_ANY, 0}, /* FMMOD */ + { 4, EMU8K_IDX_DATA3, EMU8K_PORT_DATA3, EMU8K_CHN_ANY, 0}, /* TREMFRQ */ + { 5, EMU8K_IDX_DATA3, EMU8K_PORT_DATA3, EMU8K_CHN_ANY, 0}, /* FM2FRQ2 */ + { 7, EMU8K_IDX_DATA3, EMU8K_PORT_DATA3, 0, 0}, /* PROBE */ +}; + +/* These are the EMU8000 register names. */ +enum { + EMU8K_CPF = 0, + EMU8K_PTRX, + EMU8K_CVCF, + EMU8K_VTFT, + EMU8K_PSST, + EMU8K_CSL, + EMU8K_CCCA, + EMU8K_HWCF4, + EMU8K_HWCF5, + EMU8K_HWCF6, + EMU8K_SMALR, + EMU8K_SMARR, + EMU8K_SMALW, + EMU8K_SMARW, + EMU8K_SMLD, + EMU8K_SMRD, + EMU8K_WC, + EMU8K_HWCF1, + EMU8K_HWCF2, + EMU8K_HWCF3, + EMU8K_INIT1, + EMU8K_INIT2, + EMU8K_INIT3, + EMU8K_INIT4, + EMU8K_ENVVOL, + EMU8K_DCYSUSV, + EMU8K_ENVVAL, + EMU8K_DCYSUS, + EMU8K_ATKHLDV, + EMU8K_LFO1VAL, + EMU8K_ATKHLD, + EMU8K_LFO2VAL, + EMU8K_IP, + EMU8K_IFATN, + EMU8K_PEFE, + EMU8K_FMMOD, + EMU8K_TREMFRQ, + EMU8K_FM2FRQ2, + EMU8K_PROBE, + EMU8K_REGLAST, /* keep this! */ +}; +#define EMU8K_REGNUM (EMU8K_REGLAST) + +/* These are the synthesizer and the midi device information. */ +static struct synth_info emu_synthinfo = { + "EMU8000 Wavetable Synth", + 0, + SYNTH_TYPE_SAMPLE, + SAMPLE_TYPE_AWE32, + 0, + EMU8K_MAXVOICE, + 0, + EMU8K_MAXINFO, + 0, +}; + +static struct midi_info emu_midiinfo = { + "EMU8000 Wavetable Synth", + 0, + 0, + 0, +}; + +#if notyet +/* + * These functions goes into emusynthdev_op_desc. + */ +static mdsy_killnote_t emu_killnote; +static mdsy_setinstr_t emu_setinstr; +static mdsy_startnote_t emu_startnote; +static mdsy_reset_t emu_reset; +static mdsy_hwcontrol_t emu_hwcontrol; +static mdsy_loadpatch_t emu_loadpatch; +static mdsy_panning_t emu_panning; +static mdsy_aftertouch_t emu_aftertouch; +static mdsy_controller_t emu_controller; +static mdsy_patchmgr_t emu_patchmgr; +static mdsy_bender_t emu_bender; +static mdsy_allocvoice_t emu_allocvoice; +static mdsy_setupvoice_t emu_setupvoice; +static mdsy_sendsysex_t emu_sendsysex; +static mdsy_prefixcmd_t emu_prefixcmd; +static mdsy_volumemethod_t emu_volumemethod; + +/* + * This is the synthdev_info for an EMU8000 chip. + */ +static synthdev_info emusynth_op_desc = { + emu_killnote, + emu_setinstr, + emu_startnote, + emu_reset, + emu_hwcontrol, + emu_loadpatch, + emu_panning, + emu_aftertouch, + emu_controller, + emu_patchmgr, + emu_bender, + emu_allocvoice, + emu_setupvoice, + emu_sendsysex, + emu_prefixcmd, + emu_volumemethod, +}; +#endif /* notyet */ + +/* + * These functions goes into emu_op_desc to get called + * from sound.c. + */ + +static int emu_probe(device_t dev); +static int emu_attach(device_t dev); + +static d_open_t emu_open; +static d_close_t emu_close; +static d_read_t emu_read; +static d_write_t emu_write; +static d_ioctl_t emu_ioctl; +static midi_callback_t emu_callback; + +/* These go to mididev_info. */ +static mdsy_readraw_t emu_readraw; +static mdsy_writeraw_t emu_writeraw; + +/* Here is the parameter structure per a device. */ +struct emu_softc { + device_t dev; /* device information */ + mididev_info *devinfo; /* midi device information */ + + struct resource *io[3]; /* Base of io port */ + int io_rid[3]; /* Io resource ID */ + + u_int dramsize; /* DRAM size */ + struct synth_info synthinfo; /* Synthesizer information */ + + int fflags; /* File flags */ +}; + +typedef struct emu_softc *sc_p; + +/* These functions are local. */ +static u_int emu_dramsize(sc_p scp); +static void emu_allocdmachn(sc_p scp, int chn, int mode); +static void emu_dmaaddress(sc_p scp, int mode, u_int addr); +static void emu_waitstream(sc_p scp, int mode); +static void emu_readblkstream(sc_p scp, int mode, u_short *data, size_t len); +static void emu_writeblkstream(sc_p scp, int mode, u_short *data, size_t len); +static void emu_readstream(sc_p scp, int mode, u_short *data); +static void emu_writestream(sc_p scp, int mode, u_short data); +static void emu_releasedmachn(sc_p scp, int chn, int mode); +static void emu_delay(sc_p scp, short n); +static void emu_readcpf(sc_p scp, int chn, u_int *cp, u_int *f); +static void emu_writecpf(sc_p scp, int chn, u_int cp, u_int f); +static void emu_readptrx(sc_p scp, int chn, u_int *pt, u_int *rs, u_int *auxd); +static void emu_writeptrx(sc_p scp, int chn, u_int pt, u_int rs, u_int auxd); +static void emu_readcvcf(sc_p scp, int chn, u_int *cv, u_int *cf); +static void emu_writecvcf(sc_p scp, int chn, u_int cv, u_int cf); +static void emu_readvtft(sc_p scp, int chn, u_int *vt, u_int *ft); +static void emu_writevtft(sc_p scp, int chn, u_int vt, u_int ft); +static void emu_readpsst(sc_p scp, int chn, u_int *pan, u_int *st); +static void emu_writepsst(sc_p scp, int chn, u_int pan, u_int st); +static void emu_readcsl(sc_p scp, int chn, u_int *cs, u_int *lp); +static void emu_writecsl(sc_p scp, int chn, u_int cs, u_int lp); +static void emu_readccca(sc_p scp, int chn, u_int *q, u_int *dma, u_int *wr, u_int *right, u_int *ca); +static void emu_writeccca(sc_p scp, int chn, u_int q, u_int dma, u_int wr, u_int right, u_int ca); +static void emu_readhwcf4(sc_p scp, u_int *val); +static void emu_writehwcf4(sc_p scp, u_int val); +static void emu_readhwcf5(sc_p scp, u_int *val); +static void emu_writehwcf5(sc_p scp, u_int val); +static void emu_readhwcf6(sc_p scp, u_int *val); +static void emu_writehwcf6(sc_p scp, u_int val); +static void emu_readsmalr(sc_p scp, u_int *mt, u_int *smalr); +static void emu_writesmalr(sc_p scp, u_int mt, u_int smalr); +static void emu_readsmarr(sc_p scp, u_int *mt, u_int *smarr); +static void emu_writesmarr(sc_p scp, u_int mt, u_int smarr); +static void emu_readsmalw(sc_p scp, u_int *full, u_int *smalw); +static void emu_writesmalw(sc_p scp, u_int full, u_int smalw); +static void emu_readsmarw(sc_p scp, u_int *full, u_int *smarw); +static void emu_writesmarw(sc_p scp, u_int full, u_int smarw); +static void emu_readsmld(sc_p scp, u_short *smld); +static void emu_writesmld(sc_p scp, u_short smld); +static void emu_readsmrd(sc_p scp, u_short *smrd); +static void emu_writesmrd(sc_p scp, u_short smrd); +static void emu_readwc(sc_p scp, u_int *wc); +static void emu_writewc(sc_p scp, u_int wc); +static void emu_readhwcf1(sc_p scp, u_int *val); +static void emu_writehwcf1(sc_p scp, u_int val); +static void emu_readhwcf2(sc_p scp, u_int *val); +static void emu_writehwcf2(sc_p scp, u_int val); +static void emu_readhwcf3(sc_p scp, u_int *val); +static void emu_writehwcf3(sc_p scp, u_int val); +static void emu_readinit1(sc_p scp, int chn, u_int *val); +static void emu_writeinit1(sc_p scp, int chn, u_int val); +static void emu_readinit2(sc_p scp, int chn, u_int *val); +static void emu_writeinit2(sc_p scp, int chn, u_int val); +static void emu_readinit3(sc_p scp, int chn, u_int *val); +static void emu_writeinit3(sc_p scp, int chn, u_int val); +static void emu_readinit4(sc_p scp, int chn, u_int *val); +static void emu_writeinit4(sc_p scp, int chn, u_int val); +static void emu_readenvvol(sc_p scp, int chn, u_int *envvol); +static void emu_writeenvvol(sc_p scp, int chn, u_int envvol); +static void emu_readdcysusv(sc_p scp, int chn, u_int *ph1v, u_int *susv, u_int *off, u_int *dcyv); +static void emu_writedcysusv(sc_p scp, int chn, u_int ph1v, u_int susv, u_int off, u_int dcyv); +static void emu_readenvval(sc_p scp, int chn, u_int *envval); +static void emu_writeenvval(sc_p scp, int chn, u_int envval); +static void emu_readdcysus(sc_p scp, int chn, u_int *ph1, u_int *sus, u_int *dcy); +static void emu_writedcysus(sc_p scp, int chn, u_int ph1, u_int sus, u_int dcy); +static void emu_readatkhldv(sc_p scp, int chn, u_int *atkhldv); +static void emu_writeatkhldv(sc_p scp, int chn, u_int atkhldv); +static void emu_readlfo1val(sc_p scp, int chn, u_int *lfo1val); +static void emu_writelfo1val(sc_p scp, int chn, u_int lfo1val); +static void emu_readatkhld(sc_p scp, int chn, u_int *atkhld); +static void emu_writeatkhld(sc_p scp, int chn, u_int atkhld); +static void emu_readlfo2val(sc_p scp, int chn, u_int *lfo2val); +static void emu_writelfo2val(sc_p scp, int chn, u_int lfo2val); +static void emu_readip(sc_p scp, int chn, u_int *ip); +static void emu_writeip(sc_p scp, int chn, u_int ip); +static void emu_readifatn(sc_p scp, int chn, u_int *ifc, u_int *atn); +static void emu_writeifatn(sc_p scp, int chn, u_int ifc, u_int atn); +static void emu_readpefe(sc_p scp, int chn, u_int *pe, u_int *fe); +static void emu_writepefe(sc_p scp, int chn, u_int pe, u_int fe); +static void emu_readfmmod(sc_p scp, int chn, u_int *fm, u_int *mod); +static void emu_writefmmod(sc_p scp, int chn, u_int fm, u_int mod); +static void emu_readtremfrq(sc_p scp, int chn, u_int *trem, u_int *frq); +static void emu_writetremfrq(sc_p scp, int chn, u_int trem, u_int frq); +static void emu_readfm2frq2(sc_p scp, int chn, u_int *fm2, u_int *frq2); +static void emu_writefm2frq2(sc_p scp, int chn, u_int fm2, u_int frq2); +static void emu_readprobe(sc_p scp, u_int *val); +static void emu_writeprobe(sc_p scp, u_int val); +static void emu_command(sc_p scp, int reg, int chn, u_long val); +static u_long emu_status(sc_p scp, int reg, int chn); +static int emu_allocres(sc_p scp, device_t dev); +static void emu_releaseres(sc_p scp, device_t dev); + +/* PnP IDs */ +static struct isa_pnp_id emu_ids[] = { + {0x21008c0e, "CTL0021 WaveTable Synthesizer"}, /* CTL0021 */ + {0x22008c0e, "CTL0022 WaveTable Synthesizer"}, /* CTL0022 */ +}; + +/* + * This is the device descriptor for the midi device. + */ +mididev_info emu_op_desc = { + "EMU8000 Wavetable Synth", + + SNDCARD_AWE32, + + emu_open, + emu_close, + emu_read, + emu_write, + emu_ioctl, + NULL, + + emu_callback, + + MIDI_BUFFSIZE, /* Queue Length */ + + 0, /* XXX This is not an *audio* device! */ +}; + +/* + * Here are the main functions to interact to the user process. + */ + +static int +emu_probe(device_t dev) +{ + sc_p scp; + int unit; + u_int probe, hwcf1, hwcf2; + + /* Check isapnp ids */ + if (isa_get_logicalid(dev) != 0) + return (ISA_PNP_PROBE(device_get_parent(dev), dev, emu_ids)); + /* XXX non-pnp emu? */ + + unit = device_get_unit(dev); + scp = device_get_softc(dev); + + device_set_desc(dev, "EMU8000 Wavetable Synth"); + bzero(scp, sizeof(*scp)); + + DEB(printf("emu%d: probing.\n", unit)); + + if (emu_allocres(scp, dev)) { + emu_releaseres(scp, dev); + return (ENXIO); + } + + emu_readprobe(scp, &probe); + emu_readhwcf1(scp, &hwcf1); + emu_readhwcf2(scp, &hwcf2); + if ((probe & 0x000f) != 0x000c + || (hwcf1 & 0x007e) != 0x0058 + || (hwcf2 & 0x0003) != 0x0003) { + emu_releaseres(scp, dev); + return (ENXIO); + } + + DEB(printf("emu%d: probed.\n", unit)); + + return (0); +} + +extern synthdev_info midisynth_op_desc; + +static int +emu_attach(device_t dev) +{ + sc_p scp; + mididev_info *devinfo; + int unit, i; + + unit = device_get_unit(dev); + scp = device_get_softc(dev); + + DEB(printf("emu%d: attaching.\n", unit)); + + if (emu_allocres(scp, dev)) { + emu_releaseres(scp, dev); + return (ENXIO); + } + + /* EMU8000 needs some initialization processes. */ + + /* 1. Write HWCF{1,2}. */ + emu_writehwcf1(scp, 0x0059); + emu_writehwcf2(scp, 0x0020); + + /* Disable the audio. */ + emu_writehwcf3(scp, 0); + + /* 2. Initialize the channels. */ + + /* 2a. Write DCYSUSV. */ + for (i = 0 ; i < EMU8K_MAXVOICE ; i++) + emu_writedcysusv(scp, i, 0, 0, 1, 0); + + /* 2b. Clear the envelope and sound engine registers. */ + for (i = 0 ; i < EMU8K_MAXVOICE ; i++) { + emu_writeenvvol(scp, i, 0); + emu_writeenvval(scp, i, 0); + emu_writedcysus(scp, i, 0, 0, 0); + emu_writeatkhldv(scp, i, 0); + emu_writelfo1val(scp, i, 0); + emu_writeatkhld(scp, i, 0); + emu_writelfo2val(scp, i, 0); + emu_writeip(scp, i, 0); + emu_writeifatn(scp, i, 0, 0); + emu_writepefe(scp, i, 0, 0); + emu_writefmmod(scp, i, 0, 0); + emu_writetremfrq(scp, i, 0, 0); + emu_writefm2frq2(scp, i, 0, 0); + emu_writeptrx(scp, i, 0, 0, 0); + emu_writevtft(scp, i, 0, 0); + emu_writepsst(scp, i, 0, 0); + emu_writecsl(scp, i, 0, 0); + emu_writeccca(scp, i, 0, 0, 0, 0, 0); + } + + /* 2c. Clear the current registers. */ + for (i = 0 ; i < EMU8K_MAXVOICE ; i++) { + emu_writecpf(scp, i, 0, 0); + emu_writecvcf(scp, i, 0, 0); + } + + /* 3. Initialize the sound memory DMA registers. */ + emu_writesmalr(scp, 0, 0); + emu_writesmarr(scp, 0, 0); + emu_writesmalw(scp, 0, 0); + emu_writesmarw(scp, 0, 0); + + /* 4. Fill the array. */ + + /* 4a. Set 1. */ + for (i = 0 ; i < EMU8K_MAXVOICE ; i++) + emu_writeinit1(scp, i, init1_1[i]); + for (i = 0 ; i < EMU8K_MAXVOICE ; i++) + emu_writeinit2(scp, i, init1_2[i]); + for (i = 0 ; i < EMU8K_MAXVOICE ; i++) + emu_writeinit3(scp, i, init1_3[i]); + for (i = 0 ; i < EMU8K_MAXVOICE ; i++) + emu_writeinit4(scp, i, init1_4[i]); + + /* 4b. Have a rest. */ + emu_delay(scp, 1024); /* 1024 samples. */ + + /* 4c. Set 2. */ + for (i = 0 ; i < EMU8K_MAXVOICE ; i++) + emu_writeinit1(scp, i, init2_1[i]); + for (i = 0 ; i < EMU8K_MAXVOICE ; i++) + emu_writeinit2(scp, i, init2_2[i]); + for (i = 0 ; i < EMU8K_MAXVOICE ; i++) + emu_writeinit3(scp, i, init2_3[i]); + for (i = 0 ; i < EMU8K_MAXVOICE ; i++) + emu_writeinit4(scp, i, init2_4[i]); + + /* 4d. Set 3. */ + for (i = 0 ; i < EMU8K_MAXVOICE ; i++) + emu_writeinit1(scp, i, init3_1[i]); + for (i = 0 ; i < EMU8K_MAXVOICE ; i++) + emu_writeinit2(scp, i, init3_2[i]); + for (i = 0 ; i < EMU8K_MAXVOICE ; i++) + emu_writeinit3(scp, i, init3_3[i]); + for (i = 0 ; i < EMU8K_MAXVOICE ; i++) + emu_writeinit4(scp, i, init3_4[i]); + + /* 4e. Write to HWCF{4,5,6}. */ + emu_writehwcf4(scp, 0); + emu_writehwcf5(scp, 0x00000083); + emu_writehwcf6(scp, 0x00008000); + + /* 4f. Set 4. */ + for (i = 0 ; i < EMU8K_MAXVOICE ; i++) + emu_writeinit1(scp, i, init4_1[i]); + for (i = 0 ; i < EMU8K_MAXVOICE ; i++) + emu_writeinit2(scp, i, init4_2[i]); + for (i = 0 ; i < EMU8K_MAXVOICE ; i++) + emu_writeinit3(scp, i, init4_3[i]); + for (i = 0 ; i < EMU8K_MAXVOICE ; i++) + emu_writeinit4(scp, i, init4_4[i]); + + /* 5. Determine the size of DRAM. */ + scp->dev = dev; + scp->dramsize = emu_dramsize(scp); + printf("emu%d: DRAM size = %dKB\n", unit, scp->dramsize / 1024); + + /* We have inited the EMU8000. Now work on FM. */ + + /* Write parameters for the left channel. */ + emu_writedcysusv(scp, 30, 0, 0, 1, 0); + emu_writepsst(scp, 30, 0x80, 0xffffe0); /* full left */ + emu_writecsl(scp, 30, 0, 0xfffff8); /* chorus */ + emu_writeptrx(scp, 30, 0, 0, 0); /* reverb */ + emu_writecpf(scp, 30, 0, 0); + emu_writeccca(scp, 30, 0, 0, 0, 0, 0xffffe3); + + /* Then the right channel. */ + emu_writedcysusv(scp, 31, 0, 0, 1, 0); + emu_writepsst(scp, 31, 0x80, 0xfffff0); /* full right */ + emu_writecsl(scp, 31, 0, 0xfffff8); /* chorus */ + emu_writeptrx(scp, 31, 0, 0, 0xff); /* reverb */ + emu_writecpf(scp, 31, 0, 0); + emu_writeccca(scp, 31, 0, 0, 0, 0, 0xfffff3); + + /* Skew volume and cutoff. */ + emu_writevtft(scp, 30, 0x8000, 0xffff); + emu_writevtft(scp, 31, 0x8000, 0xffff); + + /* Ready to sound. */ + emu_writehwcf3(scp, 0x0004); + + /* Fill the softc for this unit. */ + scp->devinfo = devinfo = &midi_info[unit]; + bcopy(&emu_synthinfo, &scp->synthinfo, sizeof(emu_synthinfo)); + + /* Fill the midi info. */ + bcopy(&emu_op_desc, devinfo, sizeof(emu_op_desc)); + midiinit(devinfo, dev); + devinfo->flags = 0; + bcopy(&midisynth_op_desc, &devinfo->synth, sizeof(midisynth_op_desc)); + devinfo->synth.readraw = emu_readraw; + devinfo->synth.writeraw = emu_writeraw; + snprintf(devinfo->midistat, sizeof(devinfo->midistat), "at 0x%x, 0x%x, 0x%x", + (u_int)rman_get_start(scp->io[0]), (u_int)rman_get_start(scp->io[1]), (u_int)rman_get_start(scp->io[2])); + + /* Init the queue. */ + devinfo->midi_dbuf_in.unit_size = devinfo->midi_dbuf_out.unit_size = 1; + midibuf_init(&devinfo->midi_dbuf_in); + midibuf_init(&devinfo->midi_dbuf_out); + + /* Increase the number of the synthesizers. */ + nsynth++; + + DEB(printf("emu%d: attached.\n", unit)); + + return (0); +} + +static int +emupnp_attach(device_t dev) +{ + return (emu_attach(dev)); +} + +static int +emu_open(dev_t i_dev, int flags, int mode, struct proc *p) +{ + return (0); +} + +static int +emu_close(dev_t i_dev, int flags, int mode, struct proc *p) +{ + return (0); +} + +static int +emu_read(dev_t i_dev, struct uio *buf, int flag) +{ + sc_p scp; + mididev_info *devinfo; + int unit/*, s, len, ret*/; + + unit = MIDIUNIT(i_dev); + + if (unit >= nmidi + nsynth) { + DEB(printf("emu_read: unit %d does not exist.\n", unit)); + return (ENXIO); + } + + devinfo = get_mididev_info(i_dev, &unit); + if (devinfo == NULL) { + DEB(printf("emu_read: unit %d is not configured.\n", unit)); + return (ENXIO); + } + scp = devinfo->softc; + if ((devinfo->fflags & FREAD) == 0) { + DEB(printf("emu_read: unit %d is not for reading.\n", unit)); + return (EIO); + } + + /* Drain the data. */ + midibuf_init(&devinfo->midi_dbuf_in); + + return (0); +} + +static int +emu_write(dev_t i_dev, struct uio *buf, int flag) +{ + sc_p scp; + mididev_info *devinfo; + int unit/*, s, len, ret*/; + + unit = MIDIUNIT(i_dev); + + if (unit >= nmidi + nsynth) { + DEB(printf("emu_write: unit %d does not exist.\n", unit)); + return (ENXIO); + } + + devinfo = get_mididev_info(i_dev, &unit); + if (devinfo == NULL) { + DEB(printf("emu_write: unit %d is not configured.\n", unit)); + return (ENXIO); + } + scp = devinfo->softc; + if ((devinfo->fflags & FWRITE) == 0) { + DEB(printf("emu_write: unit %d is not for writing.\n", unit)); + return (EIO); + } + + /* Drain the data. */ + midibuf_init(&devinfo->midi_dbuf_out); + midibuf_init(&devinfo->midi_dbuf_passthru); + + return (0); +} + +static int +emu_ioctl(dev_t i_dev, u_long cmd, caddr_t arg, int mode, struct proc *p) +{ + sc_p scp; + mididev_info *devinfo; + int unit; + struct synth_info *synthinfo; + struct midi_info *midiinfo; + + unit = MIDIUNIT(i_dev); + + DEB(printf("emu%d: ioctlling, cmd 0x%x.\n", unit, (int)cmd)); + + if (unit >= nmidi + nsynth) { + DEB(printf("emu_ioctl: unit %d does not exist.\n", unit)); + return (ENXIO); + } + + devinfo = get_mididev_info(i_dev, &unit); + if (devinfo == NULL) { + DEB(printf("emu_ioctl: unit %d is not configured.\n", unit)); + return (ENXIO); + } + scp = devinfo->softc; + + switch (cmd) { + case SNDCTL_SYNTH_INFO: + synthinfo = (struct synth_info *)arg; + if (synthinfo->device >= nmidi + nsynth || synthinfo->device != unit) + return (ENXIO); + bcopy(&scp->synthinfo, synthinfo, sizeof(scp->synthinfo)); + synthinfo->device = unit; + return (0); + break; + case SNDCTL_MIDI_INFO: + midiinfo = (struct midi_info *)arg; + if (midiinfo->device >= nmidi + nsynth || midiinfo->device != unit) + return (ENXIO); + bcopy(&emu_midiinfo, midiinfo, sizeof(emu_midiinfo)); + strcpy(midiinfo->name, scp->synthinfo.name); + midiinfo->device = unit; + return (0); + break; + case SNDCTL_SYNTH_MEMAVL: + return 0x7fffffff; + break; + default: + return (ENOSYS); + } + /* NOTREACHED */ + return (EINVAL); +} + +static int +emu_callback(mididev_info *devinfo, int reason) +{ + return (0); +} + +static int +emu_readraw(mididev_info *md, u_char *buf, int len, int nonblock) +{ + sc_p scp; + int unit; + + if (md == NULL) + return (ENXIO); + unit = md->unit; + scp = md->softc; + if ((md->fflags & FREAD) == 0) { + DEB(printf("emu_readraw: unit %d is not for reading.\n", unit)); + return (EIO); + } + + /* NOP. */ + return (0); +} + +static int +emu_writeraw(mididev_info *md, u_char *buf, int len, int nonblock) +{ + sc_p scp; + int unit; + + if (md == NULL) + return (ENXIO); + unit = md->unit; + scp = md->softc; + if ((md->fflags & FWRITE) == 0) { + DEB(printf("emu_writeraw: unit %d is not for writing.\n", unit)); + return (EIO); + } + + /* NOP. */ + return (0); +} + +/* + * The functions below here are the synthesizer interfaces. + */ + +/* + * The functions below here are the libraries for the above ones. + */ + +/* Determine the size of DRAM. */ +static u_int +emu_dramsize(sc_p scp) +{ + u_int dramsize; + static u_short magiccode[] = {0x386d, 0xbd2a, 0x73df, 0xf2d8}; + static u_short magiccode2[] = {0x5ef3, 0x2b90, 0xa4c8, 0x6a13}; + u_short buf[sizeof(magiccode) / sizeof(*magiccode)]; + + /* + * Write the magic code to the bottom of DRAM. + * Writing to a wrapped address clobbers the code. + */ + emu_allocdmachn(scp, 31, EMU8K_DMA_LEFT | EMU8K_DMA_WRITE); + emu_dmaaddress(scp, EMU8K_DMA_LEFT | EMU8K_DMA_WRITE, EMU8K_DRAM_RAM); + emu_writeblkstream(scp, EMU8K_DMA_LEFT | EMU8K_DMA_WRITE, magiccode, sizeof(magiccode) / sizeof(*magiccode)); + emu_releasedmachn(scp, 31, EMU8K_DMA_LEFT | EMU8K_DMA_WRITE); + + for (dramsize = 0 ; dramsize + EMU8K_DRAM_RAM < EMU8K_DRAM_MAX ; ) { + + /* Read the magic code. */ + emu_allocdmachn(scp, 31, EMU8K_DMA_LEFT | EMU8K_DMA_READ); + emu_dmaaddress(scp, EMU8K_DMA_LEFT | EMU8K_DMA_READ, EMU8K_DRAM_RAM); + emu_readblkstream(scp, EMU8K_DMA_LEFT | EMU8K_DMA_READ, buf, sizeof(buf) / sizeof(*buf)); + emu_releasedmachn(scp, 31, EMU8K_DMA_LEFT | EMU8K_DMA_READ); + + /* Compare the code. */ + if (bcmp(magiccode, buf, sizeof(magiccode))) + break; + + /* Increase the DRAM size. */ + dramsize += 0x8000; + + /* Try writing a different magic code to dramsize. */ + emu_allocdmachn(scp, 31, EMU8K_DMA_LEFT | EMU8K_DMA_WRITE); + emu_dmaaddress(scp, EMU8K_DMA_LEFT | EMU8K_DMA_WRITE, dramsize + EMU8K_DRAM_RAM); + emu_writeblkstream(scp, EMU8K_DMA_LEFT | EMU8K_DMA_WRITE, magiccode2, sizeof(magiccode2) / sizeof(*magiccode2)); + emu_releasedmachn(scp, 31, EMU8K_DMA_LEFT | EMU8K_DMA_WRITE); + + /* Then read the magic code. */ + emu_allocdmachn(scp, 31, EMU8K_DMA_LEFT | EMU8K_DMA_READ); + emu_dmaaddress(scp, EMU8K_DMA_LEFT | EMU8K_DMA_READ, dramsize + EMU8K_DRAM_RAM); + emu_readblkstream(scp, EMU8K_DMA_LEFT | EMU8K_DMA_READ, buf, sizeof(buf) / sizeof(*buf)); + emu_releasedmachn(scp, 31, EMU8K_DMA_LEFT | EMU8K_DMA_READ); + + /* Compare the code. */ + if (bcmp(magiccode2, buf, sizeof(magiccode2))) + break; + } + if (dramsize + EMU8K_DRAM_RAM > EMU8K_DRAM_MAX) + dramsize = EMU8K_DRAM_MAX - EMU8K_DRAM_RAM; + + return dramsize * 2; /* dramsize is in words. */ +} + +/* Allocates a channel to a DMA stream. */ +static void +emu_allocdmachn(sc_p scp, int chn, int mode) +{ + /* Turn off the sound, prepare for a DMA stream. */ + emu_writedcysusv(scp, chn, 0, 0, 1, 0); + emu_writevtft(scp, chn, 0, 0); + emu_writecvcf(scp, chn, 0, 0); + emu_writeptrx(scp, chn, 0x4000, 0, 0); + emu_writecpf(scp, chn, 0x4000, 0); + emu_writepsst(scp, chn, 0, 0); + emu_writecsl(scp, chn, 0, 0); + + /* Enter DMA mode. */ + emu_writeccca(scp, chn, 0, 1, + ((mode & EMU8K_DMA_WRITE) > 0) ? 1 : 0, + ((mode & EMU8K_DMA_RIGHT) > 0) ? 1 : 0, + 0); +} + +/* Programs the initial address to a DMA. */ +static void +emu_dmaaddress(sc_p scp, int mode, u_int addr) +{ + /* Wait until the stream comes ready. */ + emu_waitstream(scp, mode); + + switch(mode & EMU8K_DMA_MASK) + { + case EMU8K_DMA_LEFT | EMU8K_DMA_READ: + emu_writesmalr(scp, 0, addr); + emu_readsmld(scp, NULL); /* Read the stale data. */ + break; + case EMU8K_DMA_RIGHT | EMU8K_DMA_READ: + emu_writesmarr(scp, 0, addr); + emu_readsmrd(scp, NULL); /* Read the stale data. */ + break; + case EMU8K_DMA_LEFT | EMU8K_DMA_WRITE: + emu_writesmalw(scp, 0, addr); + break; + case EMU8K_DMA_RIGHT | EMU8K_DMA_WRITE: + emu_writesmarw(scp, 0, addr); + break; + } +} + +/* Waits until a stream gets ready. */ +static void +emu_waitstream(sc_p scp, int mode) +{ + int i; + u_int busy; + + for (i = 0 ; i < 100000 ; i++) { + switch(mode & EMU8K_DMA_MASK) + { + case EMU8K_DMA_LEFT | EMU8K_DMA_READ: + emu_readsmalr(scp, &busy, NULL); + break; + case EMU8K_DMA_RIGHT | EMU8K_DMA_READ: + emu_readsmarr(scp, &busy, NULL); + break; + case EMU8K_DMA_LEFT | EMU8K_DMA_WRITE: + emu_readsmalw(scp, &busy, NULL); + break; + case EMU8K_DMA_RIGHT | EMU8K_DMA_WRITE: + emu_readsmarw(scp, &busy, NULL); + break; + } + if (!busy) + break; + emu_delay(scp, 1); + } + if (busy) + printf("emu%d: stream data still busy, timed out.\n", device_get_unit(scp->dev)); +} + +/* Reads a word block from a stream. */ +static void +emu_readblkstream(sc_p scp, int mode, u_short *data, size_t len) +{ + while((len--) > 0) + emu_readstream(scp, mode, data++); +} + +/* Writes a word block stream to a stream. */ +static void +emu_writeblkstream(sc_p scp, int mode, u_short *data, size_t len) +{ + while((len--) > 0) + emu_writestream(scp, mode, *(data++)); +} + +/* Reads a word from a stream. */ +static void +emu_readstream(sc_p scp, int mode, u_short *data) +{ + if ((mode & EMU8K_DMA_RW) != EMU8K_DMA_READ) + return; + + switch(mode & EMU8K_DMA_MASK) + { + case EMU8K_DMA_LEFT | EMU8K_DMA_READ: + emu_readsmld(scp, data); + break; + case EMU8K_DMA_RIGHT | EMU8K_DMA_READ: + emu_readsmrd(scp, data); + break; + } +} + +/* Writes a word to a stream. */ +static void +emu_writestream(sc_p scp, int mode, u_short data) +{ + if ((mode & EMU8K_DMA_RW) != EMU8K_DMA_WRITE) + return; + + switch(mode & EMU8K_DMA_MASK) + { + case EMU8K_DMA_LEFT | EMU8K_DMA_WRITE: + emu_writesmld(scp, data); + break; + case EMU8K_DMA_RIGHT | EMU8K_DMA_WRITE: + emu_writesmrd(scp, data); + break; + } +} + +/* Releases a channel from a DMA stream. */ +static void +emu_releasedmachn(sc_p scp, int chn, int mode) +{ + /* Wait until the stream comes ready. */ + emu_waitstream(scp, mode); + + /* Leave DMA mode. */ + emu_writeccca(scp, chn, 0, 0, 0, 0, 0); +} + +/* + * Waits cycles. + * Idea-stolen-from: sys/i386/isa/clock.c:DELAY() + */ +static void +emu_delay(sc_p scp, short n) +{ + int wc_prev, wc, wc_left, wc_delta; + + emu_readwc(scp, &wc_prev); + wc_left = n; + + while (wc_left > 0) { + emu_readwc(scp, &wc); + wc_delta = wc - wc_prev; /* The counter increases. */ + wc_prev = wc; + if (wc_delta < 0) + wc_delta += 0xffff; + wc_left -= wc_delta; + } +} + +/* The followings provide abstruct access to the registers. */ +#define DECBIT(sts, shift, len) (((sts) >> (shift))) & (0xffffffff >> (32 - len)) +#define GENBIT(val, shift, len) (((val) & (0xffffffff >> (32 - len))) << (shift)) + +/* CPF: Current Pitch and Fractional Address */ +static void +emu_readcpf(sc_p scp, int chn, u_int *cp, u_int *f) +{ + u_long sts; + + sts = emu_status(scp, EMU8K_CPF, chn); + if (cp != NULL) + *cp = DECBIT(sts, 16, 16); + if (f != NULL) + *f = DECBIT(sts, 0, 16); +} + +static void +emu_writecpf(sc_p scp, int chn, u_int cp, u_int f) +{ + emu_command(scp, EMU8K_CPF, chn, + GENBIT(cp, 16, 16) + | GENBIT(f, 0, 16)); +} + +/* PTRX: Pitch Target, Rvb Send and Aux Byte */ +static void +emu_readptrx(sc_p scp, int chn, u_int *pt, u_int *rs, u_int *auxd) +{ + u_long sts; + + sts = emu_status(scp, EMU8K_PTRX, chn); + if (pt != NULL) + *pt = DECBIT(sts, 16, 16); + if (rs != NULL) + *rs = DECBIT(sts, 8, 8); + if (auxd != NULL) + *auxd = DECBIT(sts, 0, 8); +} + +static void +emu_writeptrx(sc_p scp, int chn, u_int pt, u_int rs, u_int auxd) +{ + emu_command(scp, EMU8K_PTRX, chn, + GENBIT(pt, 16, 16) + | GENBIT(rs, 8, 8) + | GENBIT(auxd, 0, 8)); +} + +/* CVCF: Current Volume and Filter Cutoff */ +static void +emu_readcvcf(sc_p scp, int chn, u_int *cv, u_int *cf) +{ + u_long sts; + + sts = emu_status(scp, EMU8K_CVCF, chn); + if (cv != NULL) + *cv = DECBIT(sts, 16, 16); + if (cf != NULL) + *cf = DECBIT(sts, 0, 16); +} + +static void +emu_writecvcf(sc_p scp, int chn, u_int cv, u_int cf) +{ + emu_command(scp, EMU8K_CVCF, chn, + GENBIT(cv, 16, 16) + | GENBIT(cf, 0, 16)); +} + +/* VTFT: Volume and Filter Cutoff Targets */ +static void +emu_readvtft(sc_p scp, int chn, u_int *vt, u_int *ft) +{ + u_long sts; + + sts = emu_status(scp, EMU8K_VTFT, chn); + if (vt != NULL) + *vt = DECBIT(sts, 16, 16); + if (ft != NULL) + *ft = DECBIT(sts, 0, 16); +} + +static void +emu_writevtft(sc_p scp, int chn, u_int vt, u_int ft) +{ + emu_command(scp, EMU8K_VTFT, chn, + GENBIT(vt, 16, 16) + | GENBIT(ft, 0, 16)); +} + +/* PSST: Pan Send and Loop Start Address */ +static void +emu_readpsst(sc_p scp, int chn, u_int *pan, u_int *st) +{ + u_long sts; + + sts = emu_status(scp, EMU8K_PSST, chn); + if (pan != NULL) + *pan = DECBIT(sts, 24, 8); + if (st != NULL) + *st = DECBIT(sts, 0, 24); +} + +static void +emu_writepsst(sc_p scp, int chn, u_int pan, u_int st) +{ + emu_command(scp, EMU8K_PSST, chn, + GENBIT(pan, 24, 8) + | GENBIT(st, 0, 24)); +} + +/* CSL: Chorus Send and Loop End Address */ +static void +emu_readcsl(sc_p scp, int chn, u_int *cs, u_int *lp) +{ + u_long sts; + + sts = emu_status(scp, EMU8K_CSL, chn); + if (cs != NULL) + *cs = DECBIT(sts, 24, 8); + if (lp != NULL) + *lp = DECBIT(sts, 0, 24); +} + +static void +emu_writecsl(sc_p scp, int chn, u_int cs, u_int lp) +{ + emu_command(scp, EMU8K_CSL, chn, + GENBIT(cs, 24, 8) + | GENBIT(lp, 0, 24)); +} + +/* CCCA: Q, Control Bits and Current Address */ +static void +emu_readccca(sc_p scp, int chn, u_int *q, u_int *dma, u_int *wr, u_int *right, u_int *ca) +{ + u_long sts; + + sts = emu_status(scp, EMU8K_CCCA, chn); + if (q != NULL) + *q = DECBIT(sts, 28, 4); + if (dma != NULL) + *dma = DECBIT(sts, 26, 1); + if (wr != NULL) + *wr = DECBIT(sts, 25, 1); + if (right != NULL) + *right = DECBIT(sts, 24, 1); + if (ca != NULL) + *ca = DECBIT(sts, 0, 24); +} + +static void +emu_writeccca(sc_p scp, int chn, u_int q, u_int dma, u_int wr, u_int right, u_int ca) +{ + emu_command(scp, EMU8K_CCCA, chn, + GENBIT(q, 28, 4) + | GENBIT(dma, 26, 1) + | GENBIT(wr, 25, 1) + | GENBIT(right, 24, 1) + | GENBIT(ca, 0, 24)); +} + +/* HWCF4: Configuration Double Word 4 */ +static void +emu_readhwcf4(sc_p scp, u_int *val) +{ + u_long sts; + + sts = emu_status(scp, EMU8K_HWCF4, 0); + if (val != NULL) + *val = sts; +} + +static void +emu_writehwcf4(sc_p scp, u_int val) +{ + if (val != 0) + printf("emu%d: writing value 0x%x to HWCF4.\n", device_get_unit(scp->dev), val); + emu_command(scp, EMU8K_HWCF4, 0, val); +} + +/* HWCF5: Configuration Double Word 5 */ +static void +emu_readhwcf5(sc_p scp, u_int *val) +{ + u_long sts; + + sts = emu_status(scp, EMU8K_HWCF5, 0); + if (val != NULL) + *val = sts; +} + +static void +emu_writehwcf5(sc_p scp, u_int val) +{ + if (val != 0x00000083) + printf("emu%d: writing value 0x%x to HWCF5.\n", device_get_unit(scp->dev), val); + emu_command(scp, EMU8K_HWCF5, 0, val); +} + +/* HWCF6: Configuration Double Word 6 */ +static void +emu_readhwcf6(sc_p scp, u_int *val) +{ + u_long sts; + + sts = emu_status(scp, EMU8K_HWCF6, 0); + if (val != NULL) + *val = sts; +} + +static void +emu_writehwcf6(sc_p scp, u_int val) +{ + if (val != 0x00008000) + printf("emu%d: writing value 0x%x to HWCF6.\n", device_get_unit(scp->dev), val); + emu_command(scp, EMU8K_HWCF6, 0, val); +} + +/* SMALR: Sound Memory Address for Left SM Reads */ +static void +emu_readsmalr(sc_p scp, u_int *mt, u_int *smalr) +{ + u_long sts; + + sts = emu_status(scp, EMU8K_SMALR, 0); + if (mt != NULL) + *mt = DECBIT(sts, 31, 1); + if (smalr != NULL) + *smalr = DECBIT(sts, 0, 24); +} + +static void +emu_writesmalr(sc_p scp, u_int mt, u_int smalr) +{ + emu_command(scp, EMU8K_SMALR, 0, + GENBIT(mt, 31, 1) + | GENBIT(smalr, 0, 24)); +} + +/* SMARR: Sound Memory Address for Right SM Reads */ +static void +emu_readsmarr(sc_p scp, u_int *mt, u_int *smarr) +{ + u_long sts; + + sts = emu_status(scp, EMU8K_SMARR, 0); + if (mt != NULL) + *mt = DECBIT(sts, 31, 1); + if (smarr != NULL) + *smarr = DECBIT(sts, 0, 24); +} + +static void +emu_writesmarr(sc_p scp, u_int mt, u_int smarr) +{ + emu_command(scp, EMU8K_SMARR, 0, + GENBIT(mt, 31, 1) + | GENBIT(smarr, 0, 24)); +} + +/* SMALW: Sound Memory Address for Left SM Writes */ +static void +emu_readsmalw(sc_p scp, u_int *full, u_int *smalw) +{ + u_long sts; + + sts = emu_status(scp, EMU8K_SMALW, 0); + if (full != NULL) + *full = DECBIT(sts, 31, 1); + if (smalw != NULL) + *smalw = DECBIT(sts, 0, 24); +} + +static void +emu_writesmalw(sc_p scp, u_int full, u_int smalw) +{ + emu_command(scp, EMU8K_SMALW, 0, + GENBIT(full, 31, 1) + | GENBIT(smalw, 0, 24)); +} + +/* SMARW: Sound Memory Address for Right SM Writes */ +static void +emu_readsmarw(sc_p scp, u_int *full, u_int *smarw) +{ + u_long sts; + + sts = emu_status(scp, EMU8K_SMARW, 0); + if (full != NULL) + *full = DECBIT(sts, 31, 1); + if (smarw != NULL) + *smarw = DECBIT(sts, 0, 24); +} + +static void +emu_writesmarw(sc_p scp, u_int full, u_int smarw) +{ + emu_command(scp, EMU8K_SMARW, 0, + GENBIT(full, 31, 1) + | GENBIT(smarw, 0, 24)); +} + +/* SMLD: Sound Memory Left Data */ +static void +emu_readsmld(sc_p scp, u_short *smld) +{ + u_long sts; + + sts = emu_status(scp, EMU8K_SMLD, 0); + if (smld != NULL) + *smld = sts; +} + +static void +emu_writesmld(sc_p scp, u_short smld) +{ + emu_command(scp, EMU8K_SMLD, 0, smld); +} + +/* SMRD: Sound Memory Right Data */ +static void +emu_readsmrd(sc_p scp, u_short *smrd) +{ + u_long sts; + + sts = emu_status(scp, EMU8K_SMRD, 0); + if (smrd != NULL) + *smrd = sts; +} + +static void +emu_writesmrd(sc_p scp, u_short smrd) +{ + emu_command(scp, EMU8K_SMRD, 0, smrd); +} + +/* WC: Sample COunter */ +static void +emu_readwc(sc_p scp, u_int *wc) +{ + u_long sts; + + sts = emu_status(scp, EMU8K_WC, 0); + if (wc != NULL) + *wc = sts; +} + +static void +emu_writewc(sc_p scp, u_int wc) +{ + emu_command(scp, EMU8K_WC, 0, wc); +} + +/* HWCF1: Configuration Double Word 1 */ +static void +emu_readhwcf1(sc_p scp, u_int *val) +{ + u_long sts; + + sts = emu_status(scp, EMU8K_HWCF1, 0); + if (val != NULL) + *val = sts; +} + +static void +emu_writehwcf1(sc_p scp, u_int val) +{ + if (val != 0x0059) + printf("emu%d: writing value 0x%x to HWCF1.\n", device_get_unit(scp->dev), val); + emu_command(scp, EMU8K_HWCF1, 0, val); +} + +/* HWCF2: Configuration Double Word 2 */ +static void +emu_readhwcf2(sc_p scp, u_int *val) +{ + u_long sts; + + sts = emu_status(scp, EMU8K_HWCF2, 0); + if (val != NULL) + *val = sts; +} + +static void +emu_writehwcf2(sc_p scp, u_int val) +{ + if (val != 0x0020) + printf("emu%d: writing value 0x%x to HWCF2.\n", device_get_unit(scp->dev), val); + emu_command(scp, EMU8K_HWCF2, 0, val); +} + +/* HWCF3: Configuration Double Word 3 */ +static void +emu_readhwcf3(sc_p scp, u_int *val) +{ + u_long sts; + + sts = emu_status(scp, EMU8K_HWCF3, 0); + if (val != NULL) + *val = sts; +} + +static void +emu_writehwcf3(sc_p scp, u_int val) +{ + if (val != 0x0004 && val != 0) + printf("emu%d: writing value 0x%x to HWCF3.\n", device_get_unit(scp->dev), val); + emu_command(scp, EMU8K_HWCF3, 0, val); +} + +/* INIT1: Initialization Array 1 */ +static void +emu_readinit1(sc_p scp, int chn, u_int *val) +{ + u_long sts; + + sts = emu_status(scp, EMU8K_INIT1, chn); + if (val != NULL) + *val = sts; +} + +static void +emu_writeinit1(sc_p scp, int chn, u_int val) +{ + emu_command(scp, EMU8K_INIT1, chn, val); +} + +/* INIT2: Initialization Array 2 */ +static void +emu_readinit2(sc_p scp, int chn, u_int *val) +{ + u_long sts; + + sts = emu_status(scp, EMU8K_INIT2, chn); + if (val != NULL) + *val = sts; +} + +static void +emu_writeinit2(sc_p scp, int chn, u_int val) +{ + emu_command(scp, EMU8K_INIT2, chn, val); +} + +/* INIT3: Initialization Array 3 */ +static void +emu_readinit3(sc_p scp, int chn, u_int *val) +{ + u_long sts; + + sts = emu_status(scp, EMU8K_INIT3, chn); + if (val != NULL) + *val = sts; +} + +static void +emu_writeinit3(sc_p scp, int chn, u_int val) +{ + emu_command(scp, EMU8K_INIT3, chn, val); +} + +/* INIT4: Initialization Array 4 */ +static void +emu_readinit4(sc_p scp, int chn, u_int *val) +{ + u_long sts; + + sts = emu_status(scp, EMU8K_INIT4, chn); + if (val != NULL) + *val = sts; +} + +static void +emu_writeinit4(sc_p scp, int chn, u_int val) +{ + emu_command(scp, EMU8K_INIT4, chn, val); +} + +/* ENVVOL: Volume Envelope Decay */ +static void +emu_readenvvol(sc_p scp, int chn, u_int *envvol) +{ + u_long sts; + + sts = emu_status(scp, EMU8K_ENVVOL, chn); + if (envvol != NULL) + *envvol = sts; +} + +static void +emu_writeenvvol(sc_p scp, int chn, u_int envvol) +{ + emu_command(scp, EMU8K_ENVVOL, chn, envvol); +} + +/* DCYSUSV: Volume Envelope Sustain and Decay */ +static void +emu_readdcysusv(sc_p scp, int chn, u_int *ph1v, u_int *susv, u_int *off, u_int *dcyv) +{ + u_long sts; + + sts = emu_status(scp, EMU8K_DCYSUSV, chn); + if (ph1v != NULL) + *ph1v = DECBIT(sts, 15, 1); + if (susv != NULL) + *susv = DECBIT(sts, 8, 7); + if (off != NULL) + *off = DECBIT(sts, 7, 1); + if (dcyv != NULL) + *dcyv = DECBIT(sts, 0, 7); +} + +static void +emu_writedcysusv(sc_p scp, int chn, u_int ph1v, u_int susv, u_int off, u_int dcyv) +{ + emu_command(scp, EMU8K_DCYSUSV, chn, + GENBIT(ph1v, 15, 1) + | GENBIT(susv, 8, 7) + | GENBIT(off, 7, 1) + | GENBIT(dcyv, 0, 7)); +} + +/* ENVVAL: Modulation Envelope Decay */ +static void +emu_readenvval(sc_p scp, int chn, u_int *envval) +{ + u_long sts; + + sts = emu_status(scp, EMU8K_ENVVAL, chn); + if (envval != NULL) + *envval = sts; +} + +static void +emu_writeenvval(sc_p scp, int chn, u_int envval) +{ + emu_command(scp, EMU8K_ENVVAL, chn, envval); +} + +/* DCYSUS: Modulation Envelope Sustain and Decay */ +static void +emu_readdcysus(sc_p scp, int chn, u_int *ph1, u_int *sus, u_int *dcy) +{ + u_long sts; + + sts = emu_status(scp, EMU8K_DCYSUS, chn); + if (ph1 != NULL) + *ph1 = DECBIT(sts, 15, 1); + if (sus != NULL) + *sus = DECBIT(sts, 8, 7); + if (dcy != NULL) + *dcy = DECBIT(sts, 0, 7); +} + +static void +emu_writedcysus(sc_p scp, int chn, u_int ph1, u_int sus, u_int dcy) +{ + emu_command(scp, EMU8K_DCYSUS, chn, + GENBIT(ph1, 15, 1) + | GENBIT(sus, 8, 7) + | GENBIT(dcy, 0, 7)); +} + +/* ATKHLDV: Volume Envelope Hold and Attack */ +static void +emu_readatkhldv(sc_p scp, int chn, u_int *atkhldv) +{ + u_long sts; + + sts = emu_status(scp, EMU8K_ATKHLDV, chn); + if (atkhldv != NULL) + *atkhldv = sts; +} + +static void +emu_writeatkhldv(sc_p scp, int chn, u_int atkhldv) +{ + emu_command(scp, EMU8K_ATKHLDV, chn, atkhldv); +} + +/* LFO1VAL: LFO #1 Delay */ +static void +emu_readlfo1val(sc_p scp, int chn, u_int *lfo1val) +{ + u_long sts; + + sts = emu_status(scp, EMU8K_LFO1VAL, chn); + if (lfo1val != NULL) + *lfo1val = sts; +} + +static void +emu_writelfo1val(sc_p scp, int chn, u_int lfo1val) +{ + emu_command(scp, EMU8K_LFO1VAL, chn, lfo1val); +} + +/* ATKHLD: Modulation Envelope Hold and Attack */ +static void +emu_readatkhld(sc_p scp, int chn, u_int *atkhld) +{ + u_long sts; + + sts = emu_status(scp, EMU8K_ATKHLD, chn); + if (atkhld != NULL) + *atkhld = sts; +} + +static void +emu_writeatkhld(sc_p scp, int chn, u_int atkhld) +{ + emu_command(scp, EMU8K_ATKHLD, chn, atkhld); +} + +/* LFO2VAL: LFO #2 Delay */ +static void +emu_readlfo2val(sc_p scp, int chn, u_int *lfo2val) +{ + u_long sts; + + sts = emu_status(scp, EMU8K_LFO2VAL, chn); + if (lfo2val != NULL) + *lfo2val = sts; +} + +static void +emu_writelfo2val(sc_p scp, int chn, u_int lfo2val) +{ + emu_command(scp, EMU8K_LFO2VAL, chn, lfo2val); +} + +/* IP: Initial Pitch */ +static void +emu_readip(sc_p scp, int chn, u_int *ip) +{ + u_long sts; + + sts = emu_status(scp, EMU8K_IP, chn); + if (ip != NULL) + *ip = sts; +} + +static void +emu_writeip(sc_p scp, int chn, u_int ip) +{ + emu_command(scp, EMU8K_IP, chn, ip); +} + +/* IFATN: Initial Filter Cutoff and Attenuation */ +static void +emu_readifatn(sc_p scp, int chn, u_int *ifc, u_int *atn) +{ + u_long sts; + + sts = emu_status(scp, EMU8K_IFATN, chn); + if (ifc != NULL) + *ifc = DECBIT(sts, 8, 8); + if (atn != NULL) + *atn = DECBIT(sts, 0, 8); +} + +static void +emu_writeifatn(sc_p scp, int chn, u_int ifc, u_int atn) +{ + emu_command(scp, EMU8K_IFATN, chn, + GENBIT(ifc, 8, 8) + | GENBIT(atn, 0, 8)); +} + +/* PEFE: Pitch and Filter Envelope Heights */ +static void +emu_readpefe(sc_p scp, int chn, u_int *pe, u_int *fe) +{ + u_long sts; + + sts = emu_status(scp, EMU8K_PEFE, chn); + if (pe != NULL) + *pe = DECBIT(sts, 8, 8); + if (fe != NULL) + *fe = DECBIT(sts, 0, 8); +} + +static void +emu_writepefe(sc_p scp, int chn, u_int pe, u_int fe) +{ + emu_command(scp, EMU8K_PEFE, chn, + GENBIT(pe, 8, 8) + | GENBIT(fe, 0, 8)); +} + +/* FMMOD: Vibrato and Filter Modulation from LFO #1 */ +static void +emu_readfmmod(sc_p scp, int chn, u_int *fm, u_int *mod) +{ + u_long sts; + + sts = emu_status(scp, EMU8K_FMMOD, chn); + if (fm != NULL) + *fm = DECBIT(sts, 8, 8); + if (mod != NULL) + *mod = DECBIT(sts, 0, 8); +} + +static void +emu_writefmmod(sc_p scp, int chn, u_int fm, u_int mod) +{ + emu_command(scp, EMU8K_FMMOD, chn, + GENBIT(fm, 8, 8) + | GENBIT(mod, 0, 8)); +} + +/* TREMFRQ: LFO #1 Tremolo Amount and Frequency */ +static void +emu_readtremfrq(sc_p scp, int chn, u_int *trem, u_int *frq) +{ + u_long sts; + + sts = emu_status(scp, EMU8K_TREMFRQ, chn); + if (trem != NULL) + *trem = DECBIT(sts, 8, 8); + if (frq != NULL) + *frq = DECBIT(sts, 0, 8); +} + +static void +emu_writetremfrq(sc_p scp, int chn, u_int trem, u_int frq) +{ + emu_command(scp, EMU8K_TREMFRQ, chn, + GENBIT(trem, 8, 8) + | GENBIT(frq, 0, 8)); +} + +/* FM2FRQ2: LFO #2 Vibrato Amount and Frequency */ +static void +emu_readfm2frq2(sc_p scp, int chn, u_int *fm2, u_int *frq2) +{ + u_long sts; + + sts = emu_status(scp, EMU8K_FM2FRQ2, chn); + if (fm2 != NULL) + *fm2 = DECBIT(sts, 8, 8); + if (frq2 != NULL) + *frq2 = DECBIT(sts, 0, 8); +} + +static void +emu_writefm2frq2(sc_p scp, int chn, u_int fm2, u_int frq2) +{ + emu_command(scp, EMU8K_FM2FRQ2, chn, + GENBIT(fm2, 8, 8) + | GENBIT(frq2, 0, 8)); +} + +/* PROBE: Probe Register */ +static void +emu_readprobe(sc_p scp, u_int *val) +{ + u_long sts; + + sts = emu_status(scp, EMU8K_PROBE, 0); + if (val != NULL) + *val = sts; +} + +static void +emu_writeprobe(sc_p scp, u_int val) +{ + emu_command(scp, EMU8K_PROBE, 0, val); +} + +/* Writes to a register. */ +static void +emu_command(sc_p scp, int reg, int chn, u_long val) +{ + if (chn < 0 || chn >= EMU8K_MAXVOICE || reg < 0 || reg >= EMU8K_REGNUM) + return; + + /* Override the channel if necessary. */ + if (emu_regs[reg].chn != EMU8K_CHN_ANY) + chn = emu_regs[reg].chn; + + /* Select the register first. */ + bus_space_write_2(rman_get_bustag(scp->io[EMU8K_IDX_PTR]), rman_get_bushandle(scp->io[EMU8K_IDX_PTR]), EMU8K_PORT_PTR, (chn & 0x1f) | ((emu_regs[reg].reg & 0x07) << 5)); + + /* Then we write the data. */ + bus_space_write_2(rman_get_bustag(scp->io[emu_regs[reg].index]), rman_get_bushandle(scp->io[emu_regs[reg].index]), emu_regs[reg].port, val & 0xffff); + if (emu_regs[reg].size) + /* double word */ + bus_space_write_2(rman_get_bustag(scp->io[emu_regs[reg].index]), rman_get_bushandle(scp->io[emu_regs[reg].index]), emu_regs[reg].port + 2, (val >> 16) & 0xffff); +} + +/* Reads from a register. */ +static u_long +emu_status(sc_p scp, int reg, int chn) +{ + u_long status; + + if (chn < 0 || chn >= EMU8K_MAXVOICE || reg < 0 || reg >= EMU8K_REGNUM) + return (0xffffffff); + + /* Override the channel if necessary. */ + if (emu_regs[reg].chn != EMU8K_CHN_ANY) + chn = emu_regs[reg].chn; + + /* Select the register first. */ + bus_space_write_2(rman_get_bustag(scp->io[EMU8K_IDX_PTR]), rman_get_bushandle(scp->io[EMU8K_IDX_PTR]), EMU8K_PORT_PTR, (chn & 0x1f) | ((emu_regs[reg].reg & 0x07) << 5)); + + /* Then we read the data. */ + status = bus_space_read_2(rman_get_bustag(scp->io[emu_regs[reg].index]), rman_get_bushandle(scp->io[emu_regs[reg].index]), emu_regs[reg].port) & 0xffff; + if (emu_regs[reg].size) + /* double word */ + status |= (bus_space_read_2(rman_get_bustag(scp->io[emu_regs[reg].index]), rman_get_bushandle(scp->io[emu_regs[reg].index]), emu_regs[reg].port + 2) & 0xffff) << 16; + + return (status); +} + +/* Allocates resources. */ +static int +emu_allocres(sc_p scp, device_t dev) +{ + int iobase; + + if (scp->io[0] == NULL) { + scp->io_rid[0] = 0; + scp->io[0] = bus_alloc_resource(dev, SYS_RES_IOPORT, &scp->io_rid[0], 0, ~0, 4, RF_ACTIVE); + } + if (scp->io[0] == NULL) + return (1); + iobase = rman_get_start(scp->io[0]); + if (scp->io[1] == NULL) { + scp->io_rid[1] = 1; + scp->io[1] = bus_alloc_resource(dev, SYS_RES_IOPORT, &scp->io_rid[1], iobase + 0x400, iobase + 0x400 + 3, 4, RF_ACTIVE); + } + if (scp->io[2] == NULL) { + scp->io_rid[2] = 2; + scp->io[2] = bus_alloc_resource(dev, SYS_RES_IOPORT, &scp->io_rid[2], iobase + 0x800, iobase + 0x800 + 3, 4, RF_ACTIVE); + } + + if (scp->io[0] == NULL || scp->io[1] == NULL || scp->io[2] == NULL) { + printf("emu_allocres: failed.\n"); + return (1); + } + + return (0); +} + +/* Releases resources. */ +static void +emu_releaseres(sc_p scp, device_t dev) +{ + if (scp->io[0] != NULL) { + bus_release_resource(dev, SYS_RES_IOPORT, scp->io_rid[0], scp->io[0]); + scp->io[0] = NULL; + } + if (scp->io[1] != NULL) { + bus_release_resource(dev, SYS_RES_IOPORT, scp->io_rid[1], scp->io[1]); + scp->io[1] = NULL; + } + if (scp->io[2] != NULL) { + bus_release_resource(dev, SYS_RES_IOPORT, scp->io_rid[2], scp->io[2]); + scp->io[2] = NULL; + } +} + +static device_method_t emu_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe , emu_probe ), + DEVMETHOD(device_attach, emu_attach), + + { 0, 0 }, +}; + +static driver_t emu_driver = { + "midi", + emu_methods, + sizeof(struct emu_softc), +}; + +DRIVER_MODULE(emu, isa, emu_driver, midi_devclass, 0, 0); |