5596676e6c
Note ALL MODULES MUST BE RECOMPILED make the kernel aware that there are smaller units of scheduling than the process. (but only allow one thread per process at this time). This is functionally equivalent to teh previousl -current except that there is a thread associated with each process. Sorry john! (your next MFC will be a doosie!) Reviewed by: peter@freebsd.org, dillon@freebsd.org X-MFC after: ha ha ha ha
1957 lines
51 KiB
C
1957 lines
51 KiB
C
/*
|
|
* 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 <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 int emupnp_attach(device_t dev) __unused;
|
|
|
|
static d_open_t emu_open;
|
|
static d_close_t emu_close;
|
|
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 mtx mtx; /* Mutex to protect a device */
|
|
|
|
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) __unused;
|
|
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) __unused;
|
|
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) __unused;
|
|
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) __unused;
|
|
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) __unused;
|
|
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) __unused;
|
|
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) __unused;
|
|
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) __unused;
|
|
static void emu_writehwcf4(sc_p scp, u_int val);
|
|
static void emu_readhwcf5(sc_p scp, u_int *val) __unused;
|
|
static void emu_writehwcf5(sc_p scp, u_int val);
|
|
static void emu_readhwcf6(sc_p scp, u_int *val) __unused;
|
|
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) __unused;
|
|
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) __unused;
|
|
static void emu_writehwcf3(sc_p scp, u_int val);
|
|
static void emu_readinit1(sc_p scp, int chn, u_int *val) __unused;
|
|
static void emu_writeinit1(sc_p scp, int chn, u_int val);
|
|
static void emu_readinit2(sc_p scp, int chn, u_int *val) __unused;
|
|
static void emu_writeinit2(sc_p scp, int chn, u_int val);
|
|
static void emu_readinit3(sc_p scp, int chn, u_int *val) __unused;
|
|
static void emu_writeinit3(sc_p scp, int chn, u_int val);
|
|
static void emu_readinit4(sc_p scp, int chn, u_int *val) __unused;
|
|
static void emu_writeinit4(sc_p scp, int chn, u_int val);
|
|
static void emu_readenvvol(sc_p scp, int chn, u_int *envvol) __unused;
|
|
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) __unused;
|
|
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) __unused;
|
|
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) __unused;
|
|
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) __unused;
|
|
static void emu_writeatkhldv(sc_p scp, int chn, u_int atkhldv);
|
|
static void emu_readlfo1val(sc_p scp, int chn, u_int *lfo1val) __unused;
|
|
static void emu_writelfo1val(sc_p scp, int chn, u_int lfo1val);
|
|
static void emu_readatkhld(sc_p scp, int chn, u_int *atkhld) __unused;
|
|
static void emu_writeatkhld(sc_p scp, int chn, u_int atkhld);
|
|
static void emu_readlfo2val(sc_p scp, int chn, u_int *lfo2val) __unused;
|
|
static void emu_writelfo2val(sc_p scp, int chn, u_int lfo2val);
|
|
static void emu_readip(sc_p scp, int chn, u_int *ip) __unused;
|
|
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) __unused;
|
|
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) __unused;
|
|
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) __unused;
|
|
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) __unused;
|
|
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) __unused;
|
|
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) __unused;
|
|
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_ioctl,
|
|
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. */
|
|
bcopy(&emu_synthinfo, &scp->synthinfo, sizeof(emu_synthinfo));
|
|
mtx_init(&scp->mtx, "emumid", MTX_DEF);
|
|
scp->devinfo = devinfo = create_mididev_info_unit(MDT_SYNTH, &emu_op_desc, &midisynth_op_desc);
|
|
|
|
/* Fill the midi info. */
|
|
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]));
|
|
|
|
midiinit(devinfo, dev);
|
|
|
|
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 thread *td)
|
|
{
|
|
return (0);
|
|
}
|
|
|
|
static int
|
|
emu_close(dev_t i_dev, int flags, int mode, struct thread *td)
|
|
{
|
|
return (0);
|
|
}
|
|
|
|
static int
|
|
emu_ioctl(dev_t i_dev, u_long cmd, caddr_t arg, int mode, struct thread *td)
|
|
{
|
|
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));
|
|
|
|
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 != 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 != 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)
|
|
{
|
|
mtx_assert(&devinfo->flagqueue_mtx, MA_OWNED);
|
|
|
|
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 abstract 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);
|