// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright ( c ) by Jaroslav Kysela < perex @ perex . cz >
* and ( c ) 1999 Steve Ratcliffe < steve @ parabola . demon . co . uk >
* Copyright ( C ) 1999 - 2000 Takashi Iwai < tiwai @ suse . de >
*
* Routines for control of EMU8000 chip
*/
#include <linux/wait.h>
#include <linux/sched/signal.h>
#include <linux/slab.h>
#include <linux/ioport.h>
#include <linux/export.h>
#include <linux/delay.h>
#include <linux/io.h>
#include <linux/string.h>
#include <sound/core.h>
#include <sound/emu8000.h>
#include <sound/emu8000_reg.h>
#include <linux/uaccess.h>
#include <linux/init.h>
#include <sound/control.h>
#include <sound/initval.h>
/*
* emu8000 register controls
*/
/*
* The following routines read and write registers on the emu8000 . They
* should always be called via the EMU8000 * READ / WRITE macros and never
* directly . The macros handle the port number and command word .
*/
/* Write a word */
void snd_emu8000_poke(struct snd_emu8000 *emu, unsigned int port, unsigned int reg, unsigned int val)
{
unsigned long flags;
spin_lock_irqsave(&emu->reg_lock, flags);
if (reg != emu->last_reg) {
outw((unsigned short )reg, EMU8000_PTR(emu)); /* Set register */
emu->last_reg = reg;
}
outw((unsigned short )val, port); /* Send data */
spin_unlock_irqrestore(&emu->reg_lock, flags);
}
/* Read a word */
unsigned short snd_emu8000_peek(struct snd_emu8000 *emu, unsigned int port, unsigned int reg)
{
unsigned short res;
unsigned long flags;
spin_lock_irqsave(&emu->reg_lock, flags);
if (reg != emu->last_reg) {
outw((unsigned short )reg, EMU8000_PTR(emu)); /* Set register */
emu->last_reg = reg;
}
res = inw(port); /* Read data */
spin_unlock_irqrestore(&emu->reg_lock, flags);
return res;
}
/* Write a double word */
void snd_emu8000_poke_dw(struct snd_emu8000 *emu, unsigned int port, unsigned int reg, unsigned int val)
{
unsigned long flags;
spin_lock_irqsave(&emu->reg_lock, flags);
if (reg != emu->last_reg) {
outw((unsigned short )reg, EMU8000_PTR(emu)); /* Set register */
emu->last_reg = reg;
}
outw((unsigned short )val, port); /* Send low word of data */
outw((unsigned short )(val>>16 ), port+2 ); /* Send high word of data */
spin_unlock_irqrestore(&emu->reg_lock, flags);
}
/* Read a double word */
unsigned int snd_emu8000_peek_dw(struct snd_emu8000 *emu, unsigned int port, unsigned int reg)
{
unsigned short low;
unsigned int res;
unsigned long flags;
spin_lock_irqsave(&emu->reg_lock, flags);
if (reg != emu->last_reg) {
outw((unsigned short )reg, EMU8000_PTR(emu)); /* Set register */
emu->last_reg = reg;
}
low = inw(port); /* Read low word of data */
res = low + (inw(port+2 ) << 16 );
spin_unlock_irqrestore(&emu->reg_lock, flags);
return res;
}
/*
* Set up / close a channel to be used for DMA .
*/
/*exported*/ void
snd_emu8000_dma_chan(struct snd_emu8000 *emu, int ch, int mode)
{
unsigned right_bit = (mode & EMU8000_RAM_RIGHT) ? 0 x01000000 : 0 ;
mode &= EMU8000_RAM_MODE_MASK;
if (mode == EMU8000_RAM_CLOSE) {
EMU8000_CCCA_WRITE(emu, ch, 0 );
EMU8000_DCYSUSV_WRITE(emu, ch, 0 x807F);
return ;
}
EMU8000_DCYSUSV_WRITE(emu, ch, 0 x80);
EMU8000_VTFT_WRITE(emu, ch, 0 );
EMU8000_CVCF_WRITE(emu, ch, 0 );
EMU8000_PTRX_WRITE(emu, ch, 0 x40000000);
EMU8000_CPF_WRITE(emu, ch, 0 x40000000);
EMU8000_PSST_WRITE(emu, ch, 0 );
EMU8000_CSL_WRITE(emu, ch, 0 );
if (mode == EMU8000_RAM_WRITE) /* DMA write */
EMU8000_CCCA_WRITE(emu, ch, 0 x06000000 | right_bit);
else /* DMA read */
EMU8000_CCCA_WRITE(emu, ch, 0 x04000000 | right_bit);
}
/*
*/
static void
snd_emu8000_read_wait(struct snd_emu8000 *emu)
{
while ((EMU8000_SMALR_READ(emu) & 0 x80000000) != 0 ) {
schedule_timeout_interruptible(1 );
if (signal_pending(current))
break ;
}
}
/*
*/
static void
snd_emu8000_write_wait(struct snd_emu8000 *emu)
{
while ((EMU8000_SMALW_READ(emu) & 0 x80000000) != 0 ) {
schedule_timeout_interruptible(1 );
if (signal_pending(current))
break ;
}
}
/*
* detect a card at the given port
*/
static int
snd_emu8000_detect(struct snd_emu8000 *emu)
{
/* Initialise */
EMU8000_HWCF1_WRITE(emu, 0 x0059);
EMU8000_HWCF2_WRITE(emu, 0 x0020);
EMU8000_HWCF3_WRITE(emu, 0 x0000);
/* Check for a recognisable emu8000 */
/*
if ( ( EMU8000_U1_READ ( emu ) & 0 x000f ) ! = 0 x000c )
return - ENODEV ;
*/
if ((EMU8000_HWCF1_READ(emu) & 0 x007e) != 0 x0058)
return -ENODEV;
if ((EMU8000_HWCF2_READ(emu) & 0 x0003) != 0 x0003)
return -ENODEV;
dev_dbg(emu->card->dev, "EMU8000 [0x%lx]: Synth chip found\n" ,
emu->port1);
return 0 ;
}
/*
* intiailize audio channels
*/
static void
init_audio(struct snd_emu8000 *emu)
{
int ch;
/* turn off envelope engines */
for (ch = 0 ; ch < EMU8000_CHANNELS; ch++)
EMU8000_DCYSUSV_WRITE(emu, ch, 0 x80);
/* reset all other parameters to zero */
for (ch = 0 ; ch < EMU8000_CHANNELS; ch++) {
EMU8000_ENVVOL_WRITE(emu, ch, 0 );
EMU8000_ENVVAL_WRITE(emu, ch, 0 );
EMU8000_DCYSUS_WRITE(emu, ch, 0 );
EMU8000_ATKHLDV_WRITE(emu, ch, 0 );
EMU8000_LFO1VAL_WRITE(emu, ch, 0 );
EMU8000_ATKHLD_WRITE(emu, ch, 0 );
EMU8000_LFO2VAL_WRITE(emu, ch, 0 );
EMU8000_IP_WRITE(emu, ch, 0 );
EMU8000_IFATN_WRITE(emu, ch, 0 );
EMU8000_PEFE_WRITE(emu, ch, 0 );
EMU8000_FMMOD_WRITE(emu, ch, 0 );
EMU8000_TREMFRQ_WRITE(emu, ch, 0 );
EMU8000_FM2FRQ2_WRITE(emu, ch, 0 );
EMU8000_PTRX_WRITE(emu, ch, 0 );
EMU8000_VTFT_WRITE(emu, ch, 0 );
EMU8000_PSST_WRITE(emu, ch, 0 );
EMU8000_CSL_WRITE(emu, ch, 0 );
EMU8000_CCCA_WRITE(emu, ch, 0 );
}
for (ch = 0 ; ch < EMU8000_CHANNELS; ch++) {
EMU8000_CPF_WRITE(emu, ch, 0 );
EMU8000_CVCF_WRITE(emu, ch, 0 );
}
}
/*
* initialize DMA address
*/
static void
init_dma(struct snd_emu8000 *emu)
{
EMU8000_SMALR_WRITE(emu, 0 );
EMU8000_SMARR_WRITE(emu, 0 );
EMU8000_SMALW_WRITE(emu, 0 );
EMU8000_SMARW_WRITE(emu, 0 );
}
/*
* initialization arrays ; from ADIP
*/
static const unsigned short init1[128 ] = {
0 x03ff, 0 x0030, 0 x07ff, 0 x0130, 0 x0bff, 0 x0230, 0 x0fff, 0 x0330,
0 x13ff, 0 x0430, 0 x17ff, 0 x0530, 0 x1bff, 0 x0630, 0 x1fff, 0 x0730,
0 x23ff, 0 x0830, 0 x27ff, 0 x0930, 0 x2bff, 0 x0a30, 0 x2fff, 0 x0b30,
0 x33ff, 0 x0c30, 0 x37ff, 0 x0d30, 0 x3bff, 0 x0e30, 0 x3fff, 0 x0f30,
0 x43ff, 0 x0030, 0 x47ff, 0 x0130, 0 x4bff, 0 x0230, 0 x4fff, 0 x0330,
0 x53ff, 0 x0430, 0 x57ff, 0 x0530, 0 x5bff, 0 x0630, 0 x5fff, 0 x0730,
0 x63ff, 0 x0830, 0 x67ff, 0 x0930, 0 x6bff, 0 x0a30, 0 x6fff, 0 x0b30,
0 x73ff, 0 x0c30, 0 x77ff, 0 x0d30, 0 x7bff, 0 x0e30, 0 x7fff, 0 x0f30,
0 x83ff, 0 x0030, 0 x87ff, 0 x0130, 0 x8bff, 0 x0230, 0 x8fff, 0 x0330,
0 x93ff, 0 x0430, 0 x97ff, 0 x0530, 0 x9bff, 0 x0630, 0 x9fff, 0 x0730,
0 xa3ff, 0 x0830, 0 xa7ff, 0 x0930, 0 xabff, 0 x0a30, 0 xafff, 0 x0b30,
0 xb3ff, 0 x0c30, 0 xb7ff, 0 x0d30, 0 xbbff, 0 x0e30, 0 xbfff, 0 x0f30,
0 xc3ff, 0 x0030, 0 xc7ff, 0 x0130, 0 xcbff, 0 x0230, 0 xcfff, 0 x0330,
0 xd3ff, 0 x0430, 0 xd7ff, 0 x0530, 0 xdbff, 0 x0630, 0 xdfff, 0 x0730,
0 xe3ff, 0 x0830, 0 xe7ff, 0 x0930, 0 xebff, 0 x0a30, 0 xefff, 0 x0b30,
0 xf3ff, 0 x0c30, 0 xf7ff, 0 x0d30, 0 xfbff, 0 x0e30, 0 xffff, 0 x0f30,
};
static const unsigned short init2[128 ] = {
0 x03ff, 0 x8030, 0 x07ff, 0 x8130, 0 x0bff, 0 x8230, 0 x0fff, 0 x8330,
0 x13ff, 0 x8430, 0 x17ff, 0 x8530, 0 x1bff, 0 x8630, 0 x1fff, 0 x8730,
0 x23ff, 0 x8830, 0 x27ff, 0 x8930, 0 x2bff, 0 x8a30, 0 x2fff, 0 x8b30,
0 x33ff, 0 x8c30, 0 x37ff, 0 x8d30, 0 x3bff, 0 x8e30, 0 x3fff, 0 x8f30,
0 x43ff, 0 x8030, 0 x47ff, 0 x8130, 0 x4bff, 0 x8230, 0 x4fff, 0 x8330,
0 x53ff, 0 x8430, 0 x57ff, 0 x8530, 0 x5bff, 0 x8630, 0 x5fff, 0 x8730,
0 x63ff, 0 x8830, 0 x67ff, 0 x8930, 0 x6bff, 0 x8a30, 0 x6fff, 0 x8b30,
0 x73ff, 0 x8c30, 0 x77ff, 0 x8d30, 0 x7bff, 0 x8e30, 0 x7fff, 0 x8f30,
0 x83ff, 0 x8030, 0 x87ff, 0 x8130, 0 x8bff, 0 x8230, 0 x8fff, 0 x8330,
0 x93ff, 0 x8430, 0 x97ff, 0 x8530, 0 x9bff, 0 x8630, 0 x9fff, 0 x8730,
0 xa3ff, 0 x8830, 0 xa7ff, 0 x8930, 0 xabff, 0 x8a30, 0 xafff, 0 x8b30,
0 xb3ff, 0 x8c30, 0 xb7ff, 0 x8d30, 0 xbbff, 0 x8e30, 0 xbfff, 0 x8f30,
0 xc3ff, 0 x8030, 0 xc7ff, 0 x8130, 0 xcbff, 0 x8230, 0 xcfff, 0 x8330,
0 xd3ff, 0 x8430, 0 xd7ff, 0 x8530, 0 xdbff, 0 x8630, 0 xdfff, 0 x8730,
0 xe3ff, 0 x8830, 0 xe7ff, 0 x8930, 0 xebff, 0 x8a30, 0 xefff, 0 x8b30,
0 xf3ff, 0 x8c30, 0 xf7ff, 0 x8d30, 0 xfbff, 0 x8e30, 0 xffff, 0 x8f30,
};
static const unsigned short init3[128 ] = {
0 x0C10, 0 x8470, 0 x14FE, 0 xB488, 0 x167F, 0 xA470, 0 x18E7, 0 x84B5,
0 x1B6E, 0 x842A, 0 x1F1D, 0 x852A, 0 x0DA3, 0 x8F7C, 0 x167E, 0 xF254,
0 x0000, 0 x842A, 0 x0001, 0 x852A, 0 x18E6, 0 x8BAA, 0 x1B6D, 0 xF234,
0 x229F, 0 x8429, 0 x2746, 0 x8529, 0 x1F1C, 0 x86E7, 0 x229E, 0 xF224,
0 x0DA4, 0 x8429, 0 x2C29, 0 x8529, 0 x2745, 0 x87F6, 0 x2C28, 0 xF254,
0 x383B, 0 x8428, 0 x320F, 0 x8528, 0 x320E, 0 x8F02, 0 x1341, 0 xF264,
0 x3EB6, 0 x8428, 0 x3EB9, 0 x8528, 0 x383A, 0 x8FA9, 0 x3EB5, 0 xF294,
0 x3EB7, 0 x8474, 0 x3EBA, 0 x8575, 0 x3EB8, 0 xC4C3, 0 x3EBB, 0 xC5C3,
0 x0000, 0 xA404, 0 x0001, 0 xA504, 0 x141F, 0 x8671, 0 x14FD, 0 x8287,
0 x3EBC, 0 xE610, 0 x3EC8, 0 x8C7B, 0 x031A, 0 x87E6, 0 x3EC8, 0 x86F7,
0 x3EC0, 0 x821E, 0 x3EBE, 0 xD208, 0 x3EBD, 0 x821F, 0 x3ECA, 0 x8386,
0 x3EC1, 0 x8C03, 0 x3EC9, 0 x831E, 0 x3ECA, 0 x8C4C, 0 x3EBF, 0 x8C55,
0 x3EC9, 0 xC208, 0 x3EC4, 0 xBC84, 0 x3EC8, 0 x8EAD, 0 x3EC8, 0 xD308,
0 x3EC2, 0 x8F7E, 0 x3ECB, 0 x8219, 0 x3ECB, 0 xD26E, 0 x3EC5, 0 x831F,
0 x3EC6, 0 xC308, 0 x3EC3, 0 xB2FF, 0 x3EC9, 0 x8265, 0 x3EC9, 0 x8319,
0 x1342, 0 xD36E, 0 x3EC7, 0 xB3FF, 0 x0000, 0 x8365, 0 x1420, 0 x9570,
};
static const unsigned short init4[128 ] = {
0 x0C10, 0 x8470, 0 x14FE, 0 xB488, 0 x167F, 0 xA470, 0 x18E7, 0 x84B5,
0 x1B6E, 0 x842A, 0 x1F1D, 0 x852A, 0 x0DA3, 0 x0F7C, 0 x167E, 0 x7254,
0 x0000, 0 x842A, 0 x0001, 0 x852A, 0 x18E6, 0 x0BAA, 0 x1B6D, 0 x7234,
0 x229F, 0 x8429, 0 x2746, 0 x8529, 0 x1F1C, 0 x06E7, 0 x229E, 0 x7224,
0 x0DA4, 0 x8429, 0 x2C29, 0 x8529, 0 x2745, 0 x07F6, 0 x2C28, 0 x7254,
0 x383B, 0 x8428, 0 x320F, 0 x8528, 0 x320E, 0 x0F02, 0 x1341, 0 x7264,
0 x3EB6, 0 x8428, 0 x3EB9, 0 x8528, 0 x383A, 0 x0FA9, 0 x3EB5, 0 x7294,
0 x3EB7, 0 x8474, 0 x3EBA, 0 x8575, 0 x3EB8, 0 x44C3, 0 x3EBB, 0 x45C3,
0 x0000, 0 xA404, 0 x0001, 0 xA504, 0 x141F, 0 x0671, 0 x14FD, 0 x0287,
0 x3EBC, 0 xE610, 0 x3EC8, 0 x0C7B, 0 x031A, 0 x07E6, 0 x3EC8, 0 x86F7,
0 x3EC0, 0 x821E, 0 x3EBE, 0 xD208, 0 x3EBD, 0 x021F, 0 x3ECA, 0 x0386,
0 x3EC1, 0 x0C03, 0 x3EC9, 0 x031E, 0 x3ECA, 0 x8C4C, 0 x3EBF, 0 x0C55,
0 x3EC9, 0 xC208, 0 x3EC4, 0 xBC84, 0 x3EC8, 0 x0EAD, 0 x3EC8, 0 xD308,
0 x3EC2, 0 x8F7E, 0 x3ECB, 0 x0219, 0 x3ECB, 0 xD26E, 0 x3EC5, 0 x031F,
0 x3EC6, 0 xC308, 0 x3EC3, 0 x32FF, 0 x3EC9, 0 x0265, 0 x3EC9, 0 x8319,
0 x1342, 0 xD36E, 0 x3EC7, 0 x33FF, 0 x0000, 0 x8365, 0 x1420, 0 x9570,
};
/* send an initialization array
* Taken from the oss driver , not obvious from the doc how this
* is meant to work
*/
static void
send_array(struct snd_emu8000 *emu, const unsigned short *data, int size)
{
int i;
const unsigned short *p;
p = data;
for (i = 0 ; i < size; i++, p++)
EMU8000_INIT1_WRITE(emu, i, *p);
for (i = 0 ; i < size; i++, p++)
EMU8000_INIT2_WRITE(emu, i, *p);
for (i = 0 ; i < size; i++, p++)
EMU8000_INIT3_WRITE(emu, i, *p);
for (i = 0 ; i < size; i++, p++)
EMU8000_INIT4_WRITE(emu, i, *p);
}
/*
* Send initialization arrays to start up , this just follows the
* initialisation sequence in the adip .
*/
static void
init_arrays(struct snd_emu8000 *emu)
{
send_array(emu, init1, ARRAY_SIZE(init1)/4 );
msleep((1024 * 1000 ) / 44100 ); /* wait for 1024 clocks */
send_array(emu, init2, ARRAY_SIZE(init2)/4 );
send_array(emu, init3, ARRAY_SIZE(init3)/4 );
EMU8000_HWCF4_WRITE(emu, 0 );
EMU8000_HWCF5_WRITE(emu, 0 x83);
EMU8000_HWCF6_WRITE(emu, 0 x8000);
send_array(emu, init4, ARRAY_SIZE(init4)/4 );
}
#define UNIQUE_ID1 0 xa5b9
#define UNIQUE_ID2 0 x9d53
/*
* Size the onboard memory .
* This is written so as not to need arbitrary delays after the write . It
* seems that the only way to do this is to use the one channel and keep
* reallocating between read and write .
*/
static void
size_dram(struct snd_emu8000 *emu)
{
int i, size;
if (emu->dram_checked)
return ;
size = 0 ;
/* write out a magic number */
snd_emu8000_dma_chan(emu, 0 , EMU8000_RAM_WRITE);
snd_emu8000_dma_chan(emu, 1 , EMU8000_RAM_READ);
EMU8000_SMALW_WRITE(emu, EMU8000_DRAM_OFFSET);
EMU8000_SMLD_WRITE(emu, UNIQUE_ID1);
snd_emu8000_init_fm(emu); /* This must really be here and not 2 lines back even */
snd_emu8000_write_wait(emu);
/*
* Detect first 512 KiB . If a write succeeds at the beginning of a
* 512 KiB page we assume that the whole page is there .
*/
EMU8000_SMALR_WRITE(emu, EMU8000_DRAM_OFFSET);
EMU8000_SMLD_READ(emu); /* discard stale data */
if (EMU8000_SMLD_READ(emu) != UNIQUE_ID1)
goto skip_detect; /* No RAM */
snd_emu8000_read_wait(emu);
for (size = 512 * 1024 ; size < EMU8000_MAX_DRAM; size += 512 * 1024 ) {
/* Write a unique data on the test address.
* if the address is out of range , the data is written on
* 0 x200000 ( = EMU8000_DRAM_OFFSET ) . Then the id word is
* changed by this data .
*/
/*snd_emu8000_dma_chan(emu, 0, EMU8000_RAM_WRITE);*/
EMU8000_SMALW_WRITE(emu, EMU8000_DRAM_OFFSET + (size>>1 ));
EMU8000_SMLD_WRITE(emu, UNIQUE_ID2);
snd_emu8000_write_wait(emu);
/*
* read the data on the just written DRAM address
* if not the same then we have reached the end of ram .
*/
/*snd_emu8000_dma_chan(emu, 0, EMU8000_RAM_READ);*/
EMU8000_SMALR_WRITE(emu, EMU8000_DRAM_OFFSET + (size>>1 ));
/*snd_emu8000_read_wait(emu);*/
EMU8000_SMLD_READ(emu); /* discard stale data */
if (EMU8000_SMLD_READ(emu) != UNIQUE_ID2)
break ; /* no memory at this address */
snd_emu8000_read_wait(emu);
/*
* If it is the same it could be that the address just
* wraps back to the beginning ; so check to see if the
* initial value has been overwritten .
*/
EMU8000_SMALR_WRITE(emu, EMU8000_DRAM_OFFSET);
EMU8000_SMLD_READ(emu); /* discard stale data */
if (EMU8000_SMLD_READ(emu) != UNIQUE_ID1)
break ; /* we must have wrapped around */
snd_emu8000_read_wait(emu);
/* Otherwise, it's valid memory. */
}
skip_detect:
/* wait until FULL bit in SMAxW register is false */
for (i = 0 ; i < 10000 ; i++) {
if ((EMU8000_SMALW_READ(emu) & 0 x80000000) == 0 )
break ;
schedule_timeout_interruptible(1 );
if (signal_pending(current))
break ;
}
snd_emu8000_dma_chan(emu, 0 , EMU8000_RAM_CLOSE);
snd_emu8000_dma_chan(emu, 1 , EMU8000_RAM_CLOSE);
pr_info("EMU8000 [0x%lx]: %d KiB on-board DRAM detected\n" ,
emu->port1, size/1024 );
emu->mem_size = size;
emu->dram_checked = 1 ;
}
/*
* Initiailise the FM section . You have to do this to use sample RAM
* and therefore lose 2 voices .
*/
/*exported*/ void
snd_emu8000_init_fm(struct snd_emu8000 *emu)
{
unsigned long flags;
/* Initialize the last two channels for DRAM refresh and producing
the reverb and chorus effects for Yamaha OPL-3 synthesizer */
/* 31: FM left channel, 0xffffe0-0xffffe8 */
EMU8000_DCYSUSV_WRITE(emu, 30 , 0 x80);
EMU8000_PSST_WRITE(emu, 30 , 0 xFFFFFFE0); /* full left */
EMU8000_CSL_WRITE(emu, 30 , 0 x00FFFFE8 | (emu->fm_chorus_depth << 24 ));
EMU8000_PTRX_WRITE(emu, 30 , (emu->fm_reverb_depth << 8 ));
EMU8000_CPF_WRITE(emu, 30 , 0 );
EMU8000_CCCA_WRITE(emu, 30 , 0 x00FFFFE3);
/* 32: FM right channel, 0xfffff0-0xfffff8 */
EMU8000_DCYSUSV_WRITE(emu, 31 , 0 x80);
EMU8000_PSST_WRITE(emu, 31 , 0 x00FFFFF0); /* full right */
EMU8000_CSL_WRITE(emu, 31 , 0 x00FFFFF8 | (emu->fm_chorus_depth << 24 ));
EMU8000_PTRX_WRITE(emu, 31 , (emu->fm_reverb_depth << 8 ));
EMU8000_CPF_WRITE(emu, 31 , 0 x8000);
EMU8000_CCCA_WRITE(emu, 31 , 0 x00FFFFF3);
snd_emu8000_poke((emu), EMU8000_DATA0(emu), EMU8000_CMD(1 , (30 )), 0 );
spin_lock_irqsave(&emu->reg_lock, flags);
while (!(inw(EMU8000_PTR(emu)) & 0 x1000))
;
while ((inw(EMU8000_PTR(emu)) & 0 x1000))
;
spin_unlock_irqrestore(&emu->reg_lock, flags);
snd_emu8000_poke((emu), EMU8000_DATA0(emu), EMU8000_CMD(1 , (30 )), 0 x4828);
/* this is really odd part.. */
outb(0 x3C, EMU8000_PTR(emu));
outb(0 , EMU8000_DATA1(emu));
/* skew volume & cutoff */
EMU8000_VTFT_WRITE(emu, 30 , 0 x8000FFFF);
EMU8000_VTFT_WRITE(emu, 31 , 0 x8000FFFF);
}
/*
* The main initialization routine .
*/
static void
snd_emu8000_init_hw(struct snd_emu8000 *emu)
{
int i;
emu->last_reg = 0 xffff; /* reset the last register index */
/* initialize hardware configuration */
EMU8000_HWCF1_WRITE(emu, 0 x0059);
EMU8000_HWCF2_WRITE(emu, 0 x0020);
/* disable audio; this seems to reduce a clicking noise a bit.. */
EMU8000_HWCF3_WRITE(emu, 0 );
/* initialize audio channels */
init_audio(emu);
/* initialize DMA */
init_dma(emu);
/* initialize init arrays */
init_arrays(emu);
/*
* Initialize the FM section of the AWE32 , this is needed
* for DRAM refresh as well
*/
snd_emu8000_init_fm(emu);
/* terminate all voices */
for (i = 0 ; i < EMU8000_DRAM_VOICES; i++)
EMU8000_DCYSUSV_WRITE(emu, 0 , 0 x807F);
/* check DRAM memory size */
size_dram(emu);
/* enable audio */
EMU8000_HWCF3_WRITE(emu, 0 x4);
/* set equzlier, chorus and reverb modes */
snd_emu8000_update_equalizer(emu);
snd_emu8000_update_chorus_mode(emu);
snd_emu8000_update_reverb_mode(emu);
}
/*----------------------------------------------------------------
* Bass / Treble Equalizer
*----------------------------------------------------------------*/
static const unsigned short bass_parm[12 ][3 ] = {
{0 xD26A, 0 xD36A, 0 x0000}, /* -12 dB */
{0 xD25B, 0 xD35B, 0 x0000}, /* -8 */
{0 xD24C, 0 xD34C, 0 x0000}, /* -6 */
{0 xD23D, 0 xD33D, 0 x0000}, /* -4 */
{0 xD21F, 0 xD31F, 0 x0000}, /* -2 */
{0 xC208, 0 xC308, 0 x0001}, /* 0 (HW default) */
{0 xC219, 0 xC319, 0 x0001}, /* +2 */
{0 xC22A, 0 xC32A, 0 x0001}, /* +4 */
{0 xC24C, 0 xC34C, 0 x0001}, /* +6 */
{0 xC26E, 0 xC36E, 0 x0001}, /* +8 */
{0 xC248, 0 xC384, 0 x0002}, /* +10 */
{0 xC26A, 0 xC36A, 0 x0002}, /* +12 dB */
};
static const unsigned short treble_parm[12 ][9 ] = {
{0 x821E, 0 xC26A, 0 x031E, 0 xC36A, 0 x021E, 0 xD208, 0 x831E, 0 xD308, 0 x0001}, /* -12 dB */
{0 x821E, 0 xC25B, 0 x031E, 0 xC35B, 0 x021E, 0 xD208, 0 x831E, 0 xD308, 0 x0001},
{0 x821E, 0 xC24C, 0 x031E, 0 xC34C, 0 x021E, 0 xD208, 0 x831E, 0 xD308, 0 x0001},
{0 x821E, 0 xC23D, 0 x031E, 0 xC33D, 0 x021E, 0 xD208, 0 x831E, 0 xD308, 0 x0001},
{0 x821E, 0 xC21F, 0 x031E, 0 xC31F, 0 x021E, 0 xD208, 0 x831E, 0 xD308, 0 x0001},
{0 x821E, 0 xD208, 0 x031E, 0 xD308, 0 x021E, 0 xD208, 0 x831E, 0 xD308, 0 x0002},
{0 x821E, 0 xD208, 0 x031E, 0 xD308, 0 x021D, 0 xD219, 0 x831D, 0 xD319, 0 x0002},
{0 x821E, 0 xD208, 0 x031E, 0 xD308, 0 x021C, 0 xD22A, 0 x831C, 0 xD32A, 0 x0002},
{0 x821E, 0 xD208, 0 x031E, 0 xD308, 0 x021A, 0 xD24C, 0 x831A, 0 xD34C, 0 x0002},
{0 x821E, 0 xD208, 0 x031E, 0 xD308, 0 x0219, 0 xD26E, 0 x8319, 0 xD36E, 0 x0002}, /* +8 (HW default) */
{0 x821D, 0 xD219, 0 x031D, 0 xD319, 0 x0219, 0 xD26E, 0 x8319, 0 xD36E, 0 x0002},
{0 x821C, 0 xD22A, 0 x031C, 0 xD32A, 0 x0219, 0 xD26E, 0 x8319, 0 xD36E, 0 x0002} /* +12 dB */
};
/*
* set Emu8000 digital equalizer ; from 0 to 11 [ - 12 dB - 12 dB ]
*/
/*exported*/ void
snd_emu8000_update_equalizer(struct snd_emu8000 *emu)
{
unsigned short w;
int bass = emu->bass_level;
int treble = emu->treble_level;
if (bass < 0 || bass > 11 || treble < 0 || treble > 11 )
return ;
EMU8000_INIT4_WRITE(emu, 0 x01, bass_parm[bass][0 ]);
EMU8000_INIT4_WRITE(emu, 0 x11, bass_parm[bass][1 ]);
EMU8000_INIT3_WRITE(emu, 0 x11, treble_parm[treble][0 ]);
EMU8000_INIT3_WRITE(emu, 0 x13, treble_parm[treble][1 ]);
EMU8000_INIT3_WRITE(emu, 0 x1b, treble_parm[treble][2 ]);
EMU8000_INIT4_WRITE(emu, 0 x07, treble_parm[treble][3 ]);
EMU8000_INIT4_WRITE(emu, 0 x0b, treble_parm[treble][4 ]);
EMU8000_INIT4_WRITE(emu, 0 x0d, treble_parm[treble][5 ]);
EMU8000_INIT4_WRITE(emu, 0 x17, treble_parm[treble][6 ]);
EMU8000_INIT4_WRITE(emu, 0 x19, treble_parm[treble][7 ]);
w = bass_parm[bass][2 ] + treble_parm[treble][8 ];
EMU8000_INIT4_WRITE(emu, 0 x15, (unsigned short )(w + 0 x0262));
EMU8000_INIT4_WRITE(emu, 0 x1d, (unsigned short )(w + 0 x8362));
}
/*----------------------------------------------------------------
* Chorus mode control
*----------------------------------------------------------------*/
/*
* chorus mode parameters
*/
#define SNDRV_EMU8000_CHORUS_1 0
#define SNDRV_EMU8000_CHORUS_2 1
#define SNDRV_EMU8000_CHORUS_3 2
#define SNDRV_EMU8000_CHORUS_4 3
#define SNDRV_EMU8000_CHORUS_FEEDBACK 4
#define SNDRV_EMU8000_CHORUS_FLANGER 5
#define SNDRV_EMU8000_CHORUS_SHORTDELAY 6
#define SNDRV_EMU8000_CHORUS_SHORTDELAY2 7
#define SNDRV_EMU8000_CHORUS_PREDEFINED 8
/* user can define chorus modes up to 32 */
#define SNDRV_EMU8000_CHORUS_NUMBERS 32
struct soundfont_chorus_fx {
unsigned short feedback; /* feedback level (0xE600-0xE6FF) */
unsigned short delay_offset; /* delay (0-0x0DA3) [1/44100 sec] */
unsigned short lfo_depth; /* LFO depth (0xBC00-0xBCFF) */
unsigned int delay; /* right delay (0-0xFFFFFFFF) [1/256/44100 sec] */
unsigned int lfo_freq; /* LFO freq LFO freq (0-0xFFFFFFFF) */
};
/* 5 parameters for each chorus mode; 3 x 16bit, 2 x 32bit */
static char chorus_defined[SNDRV_EMU8000_CHORUS_NUMBERS];
static struct soundfont_chorus_fx chorus_parm[SNDRV_EMU8000_CHORUS_NUMBERS] = {
{0 xE600, 0 x03F6, 0 xBC2C ,0 x00000000, 0 x0000006D}, /* chorus 1 */
{0 xE608, 0 x031A, 0 xBC6E, 0 x00000000, 0 x0000017C}, /* chorus 2 */
{0 xE610, 0 x031A, 0 xBC84, 0 x00000000, 0 x00000083}, /* chorus 3 */
{0 xE620, 0 x0269, 0 xBC6E, 0 x00000000, 0 x0000017C}, /* chorus 4 */
{0 xE680, 0 x04D3, 0 xBCA6, 0 x00000000, 0 x0000005B}, /* feedback */
{0 xE6E0, 0 x044E, 0 xBC37, 0 x00000000, 0 x00000026}, /* flanger */
{0 xE600, 0 x0B06, 0 xBC00, 0 x0006E000, 0 x00000083}, /* short delay */
{0 xE6C0, 0 x0B06, 0 xBC00, 0 x0006E000, 0 x00000083}, /* short delay + feedback */
};
/*exported*/ int
snd_emu8000_load_chorus_fx(struct snd_emu8000 *emu, int mode, const void __user *buf, long len)
{
struct soundfont_chorus_fx rec;
if (mode < SNDRV_EMU8000_CHORUS_PREDEFINED || mode >= SNDRV_EMU8000_CHORUS_NUMBERS) {
dev_warn(emu->card->dev, "invalid chorus mode %d for uploading\n" , mode);
return -EINVAL;
}
if (len < (long )sizeof (rec) || copy_from_user(&rec, buf, sizeof (rec)))
return -EFAULT;
chorus_parm[mode] = rec;
chorus_defined[mode] = 1 ;
return 0 ;
}
/*exported*/ void
snd_emu8000_update_chorus_mode(struct snd_emu8000 *emu)
{
int effect = emu->chorus_mode;
if (effect < 0 || effect >= SNDRV_EMU8000_CHORUS_NUMBERS ||
(effect >= SNDRV_EMU8000_CHORUS_PREDEFINED && !chorus_defined[effect]))
return ;
EMU8000_INIT3_WRITE(emu, 0 x09, chorus_parm[effect].feedback);
EMU8000_INIT3_WRITE(emu, 0 x0c, chorus_parm[effect].delay_offset);
EMU8000_INIT4_WRITE(emu, 0 x03, chorus_parm[effect].lfo_depth);
EMU8000_HWCF4_WRITE(emu, chorus_parm[effect].delay);
EMU8000_HWCF5_WRITE(emu, chorus_parm[effect].lfo_freq);
EMU8000_HWCF6_WRITE(emu, 0 x8000);
EMU8000_HWCF7_WRITE(emu, 0 x0000);
}
/*----------------------------------------------------------------
* Reverb mode control
*----------------------------------------------------------------*/
/*
* reverb mode parameters
*/
#define SNDRV_EMU8000_REVERB_ROOM1 0
#define SNDRV_EMU8000_REVERB_ROOM2 1
#define SNDRV_EMU8000_REVERB_ROOM3 2
#define SNDRV_EMU8000_REVERB_HALL1 3
#define SNDRV_EMU8000_REVERB_HALL2 4
#define SNDRV_EMU8000_REVERB_PLATE 5
#define SNDRV_EMU8000_REVERB_DELAY 6
#define SNDRV_EMU8000_REVERB_PANNINGDELAY 7
#define SNDRV_EMU8000_REVERB_PREDEFINED 8
/* user can define reverb modes up to 32 */
#define SNDRV_EMU8000_REVERB_NUMBERS 32
struct soundfont_reverb_fx {
unsigned short parms[28 ];
};
/* reverb mode settings; write the following 28 data of 16 bit length
* on the corresponding ports in the reverb_cmds array
*/
static char reverb_defined[SNDRV_EMU8000_CHORUS_NUMBERS];
static struct soundfont_reverb_fx reverb_parm[SNDRV_EMU8000_REVERB_NUMBERS] = {
{{ /* room 1 */
0 xB488, 0 xA450, 0 x9550, 0 x84B5, 0 x383A, 0 x3EB5, 0 x72F4,
0 x72A4, 0 x7254, 0 x7204, 0 x7204, 0 x7204, 0 x4416, 0 x4516,
0 xA490, 0 xA590, 0 x842A, 0 x852A, 0 x842A, 0 x852A, 0 x8429,
0 x8529, 0 x8429, 0 x8529, 0 x8428, 0 x8528, 0 x8428, 0 x8528,
}},
{{ /* room 2 */
0 xB488, 0 xA458, 0 x9558, 0 x84B5, 0 x383A, 0 x3EB5, 0 x7284,
0 x7254, 0 x7224, 0 x7224, 0 x7254, 0 x7284, 0 x4448, 0 x4548,
0 xA440, 0 xA540, 0 x842A, 0 x852A, 0 x842A, 0 x852A, 0 x8429,
0 x8529, 0 x8429, 0 x8529, 0 x8428, 0 x8528, 0 x8428, 0 x8528,
}},
{{ /* room 3 */
0 xB488, 0 xA460, 0 x9560, 0 x84B5, 0 x383A, 0 x3EB5, 0 x7284,
0 x7254, 0 x7224, 0 x7224, 0 x7254, 0 x7284, 0 x4416, 0 x4516,
0 xA490, 0 xA590, 0 x842C, 0 x852C, 0 x842C, 0 x852C, 0 x842B,
0 x852B, 0 x842B, 0 x852B, 0 x842A, 0 x852A, 0 x842A, 0 x852A,
}},
{{ /* hall 1 */
0 xB488, 0 xA470, 0 x9570, 0 x84B5, 0 x383A, 0 x3EB5, 0 x7284,
0 x7254, 0 x7224, 0 x7224, 0 x7254, 0 x7284, 0 x4448, 0 x4548,
0 xA440, 0 xA540, 0 x842B, 0 x852B, 0 x842B, 0 x852B, 0 x842A,
0 x852A, 0 x842A, 0 x852A, 0 x8429, 0 x8529, 0 x8429, 0 x8529,
}},
{{ /* hall 2 */
0 xB488, 0 xA470, 0 x9570, 0 x84B5, 0 x383A, 0 x3EB5, 0 x7254,
0 x7234, 0 x7224, 0 x7254, 0 x7264, 0 x7294, 0 x44C3, 0 x45C3,
0 xA404, 0 xA504, 0 x842A, 0 x852A, 0 x842A, 0 x852A, 0 x8429,
0 x8529, 0 x8429, 0 x8529, 0 x8428, 0 x8528, 0 x8428, 0 x8528,
}},
{{ /* plate */
0 xB4FF, 0 xA470, 0 x9570, 0 x84B5, 0 x383A, 0 x3EB5, 0 x7234,
0 x7234, 0 x7234, 0 x7234, 0 x7234, 0 x7234, 0 x4448, 0 x4548,
0 xA440, 0 xA540, 0 x842A, 0 x852A, 0 x842A, 0 x852A, 0 x8429,
0 x8529, 0 x8429, 0 x8529, 0 x8428, 0 x8528, 0 x8428, 0 x8528,
}},
{{ /* delay */
0 xB4FF, 0 xA470, 0 x9500, 0 x84B5, 0 x333A, 0 x39B5, 0 x7204,
0 x7204, 0 x7204, 0 x7204, 0 x7204, 0 x72F4, 0 x4400, 0 x4500,
0 xA4FF, 0 xA5FF, 0 x8420, 0 x8520, 0 x8420, 0 x8520, 0 x8420,
0 x8520, 0 x8420, 0 x8520, 0 x8420, 0 x8520, 0 x8420, 0 x8520,
}},
{{ /* panning delay */
0 xB4FF, 0 xA490, 0 x9590, 0 x8474, 0 x333A, 0 x39B5, 0 x7204,
0 x7204, 0 x7204, 0 x7204, 0 x7204, 0 x72F4, 0 x4400, 0 x4500,
0 xA4FF, 0 xA5FF, 0 x8420, 0 x8520, 0 x8420, 0 x8520, 0 x8420,
0 x8520, 0 x8420, 0 x8520, 0 x8420, 0 x8520, 0 x8420, 0 x8520,
}},
};
enum { DATA1, DATA2 };
#define AWE_INIT1(c) EMU8000_CMD(2 ,c), DATA1
#define AWE_INIT2(c) EMU8000_CMD(2 ,c), DATA2
#define AWE_INIT3(c) EMU8000_CMD(3 ,c), DATA1
#define AWE_INIT4(c) EMU8000_CMD(3 ,c), DATA2
static struct reverb_cmd_pair {
unsigned short cmd, port;
} reverb_cmds[28 ] = {
{AWE_INIT1(0 x03)}, {AWE_INIT1(0 x05)}, {AWE_INIT4(0 x1F)}, {AWE_INIT1(0 x07)},
{AWE_INIT2(0 x14)}, {AWE_INIT2(0 x16)}, {AWE_INIT1(0 x0F)}, {AWE_INIT1(0 x17)},
{AWE_INIT1(0 x1F)}, {AWE_INIT2(0 x07)}, {AWE_INIT2(0 x0F)}, {AWE_INIT2(0 x17)},
{AWE_INIT2(0 x1D)}, {AWE_INIT2(0 x1F)}, {AWE_INIT3(0 x01)}, {AWE_INIT3(0 x03)},
{AWE_INIT1(0 x09)}, {AWE_INIT1(0 x0B)}, {AWE_INIT1(0 x11)}, {AWE_INIT1(0 x13)},
{AWE_INIT1(0 x19)}, {AWE_INIT1(0 x1B)}, {AWE_INIT2(0 x01)}, {AWE_INIT2(0 x03)},
{AWE_INIT2(0 x09)}, {AWE_INIT2(0 x0B)}, {AWE_INIT2(0 x11)}, {AWE_INIT2(0 x13)},
};
/*exported*/ int
snd_emu8000_load_reverb_fx(struct snd_emu8000 *emu, int mode, const void __user *buf, long len)
{
struct soundfont_reverb_fx rec;
if (mode < SNDRV_EMU8000_REVERB_PREDEFINED || mode >= SNDRV_EMU8000_REVERB_NUMBERS) {
dev_warn(emu->card->dev, "invalid reverb mode %d for uploading\n" , mode);
return -EINVAL;
}
if (len < (long )sizeof (rec) || copy_from_user(&rec, buf, sizeof (rec)))
return -EFAULT;
reverb_parm[mode] = rec;
reverb_defined[mode] = 1 ;
return 0 ;
}
/*exported*/ void
snd_emu8000_update_reverb_mode(struct snd_emu8000 *emu)
{
int effect = emu->reverb_mode;
int i;
if (effect < 0 || effect >= SNDRV_EMU8000_REVERB_NUMBERS ||
(effect >= SNDRV_EMU8000_REVERB_PREDEFINED && !reverb_defined[effect]))
return ;
for (i = 0 ; i < 28 ; i++) {
int port;
if (reverb_cmds[i].port == DATA1)
port = EMU8000_DATA1(emu);
else
port = EMU8000_DATA2(emu);
snd_emu8000_poke(emu, port, reverb_cmds[i].cmd, reverb_parm[effect].parms[i]);
}
}
/*----------------------------------------------------------------
* mixer interface
*----------------------------------------------------------------*/
/*
* bass / treble
*/
static int mixer_bass_treble_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
uinfo->count = 1 ;
uinfo->value.integer.min = 0 ;
uinfo->value.integer.max = 11 ;
return 0 ;
}
static int mixer_bass_treble_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_emu8000 *emu = snd_kcontrol_chip(kcontrol);
ucontrol->value.integer.value[0 ] = kcontrol->private_value ? emu->treble_level : emu->bass_level;
return 0 ;
}
static int mixer_bass_treble_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_emu8000 *emu = snd_kcontrol_chip(kcontrol);
unsigned long flags;
int change;
unsigned short val1;
val1 = ucontrol->value.integer.value[0 ] % 12 ;
spin_lock_irqsave(&emu->control_lock, flags);
if (kcontrol->private_value) {
change = val1 != emu->treble_level;
emu->treble_level = val1;
} else {
change = val1 != emu->bass_level;
emu->bass_level = val1;
}
spin_unlock_irqrestore(&emu->control_lock, flags);
snd_emu8000_update_equalizer(emu);
return change;
}
static const struct snd_kcontrol_new mixer_bass_control =
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Synth Tone Control - Bass" ,
.info = mixer_bass_treble_info,
.get = mixer_bass_treble_get,
.put = mixer_bass_treble_put,
.private_value = 0 ,
};
static const struct snd_kcontrol_new mixer_treble_control =
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Synth Tone Control - Treble" ,
.info = mixer_bass_treble_info,
.get = mixer_bass_treble_get,
.put = mixer_bass_treble_put,
.private_value = 1 ,
};
/*
* chorus / reverb mode
*/
static int mixer_chorus_reverb_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
uinfo->count = 1 ;
uinfo->value.integer.min = 0 ;
uinfo->value.integer.max = kcontrol->private_value ? (SNDRV_EMU8000_CHORUS_NUMBERS-1 ) : (SNDRV_EMU8000_REVERB_NUMBERS-1 );
return 0 ;
}
static int mixer_chorus_reverb_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_emu8000 *emu = snd_kcontrol_chip(kcontrol);
ucontrol->value.integer.value[0 ] = kcontrol->private_value ? emu->chorus_mode : emu->reverb_mode;
return 0 ;
}
static int mixer_chorus_reverb_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_emu8000 *emu = snd_kcontrol_chip(kcontrol);
unsigned long flags;
int change;
unsigned short val1;
spin_lock_irqsave(&emu->control_lock, flags);
if (kcontrol->private_value) {
val1 = ucontrol->value.integer.value[0 ] % SNDRV_EMU8000_CHORUS_NUMBERS;
change = val1 != emu->chorus_mode;
emu->chorus_mode = val1;
} else {
val1 = ucontrol->value.integer.value[0 ] % SNDRV_EMU8000_REVERB_NUMBERS;
change = val1 != emu->reverb_mode;
emu->reverb_mode = val1;
}
spin_unlock_irqrestore(&emu->control_lock, flags);
if (change) {
if (kcontrol->private_value)
snd_emu8000_update_chorus_mode(emu);
else
snd_emu8000_update_reverb_mode(emu);
}
return change;
}
static const struct snd_kcontrol_new mixer_chorus_mode_control =
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Chorus Mode" ,
.info = mixer_chorus_reverb_info,
.get = mixer_chorus_reverb_get,
.put = mixer_chorus_reverb_put,
.private_value = 1 ,
};
static const struct snd_kcontrol_new mixer_reverb_mode_control =
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Reverb Mode" ,
.info = mixer_chorus_reverb_info,
.get = mixer_chorus_reverb_get,
.put = mixer_chorus_reverb_put,
.private_value = 0 ,
};
/*
* FM OPL3 chorus / reverb depth
*/
static int mixer_fm_depth_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
uinfo->count = 1 ;
uinfo->value.integer.min = 0 ;
uinfo->value.integer.max = 255 ;
return 0 ;
}
static int mixer_fm_depth_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_emu8000 *emu = snd_kcontrol_chip(kcontrol);
ucontrol->value.integer.value[0 ] = kcontrol->private_value ? emu->fm_chorus_depth : emu->fm_reverb_depth;
return 0 ;
}
static int mixer_fm_depth_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_emu8000 *emu = snd_kcontrol_chip(kcontrol);
unsigned long flags;
int change;
unsigned short val1;
val1 = ucontrol->value.integer.value[0 ] % 256 ;
spin_lock_irqsave(&emu->control_lock, flags);
if (kcontrol->private_value) {
change = val1 != emu->fm_chorus_depth;
emu->fm_chorus_depth = val1;
} else {
change = val1 != emu->fm_reverb_depth;
emu->fm_reverb_depth = val1;
}
spin_unlock_irqrestore(&emu->control_lock, flags);
if (change)
snd_emu8000_init_fm(emu);
return change;
}
static const struct snd_kcontrol_new mixer_fm_chorus_depth_control =
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "FM Chorus Depth" ,
.info = mixer_fm_depth_info,
.get = mixer_fm_depth_get,
.put = mixer_fm_depth_put,
.private_value = 1 ,
};
static const struct snd_kcontrol_new mixer_fm_reverb_depth_control =
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "FM Reverb Depth" ,
.info = mixer_fm_depth_info,
.get = mixer_fm_depth_get,
.put = mixer_fm_depth_put,
.private_value = 0 ,
};
static const struct snd_kcontrol_new *mixer_defs[EMU8000_NUM_CONTROLS] = {
&mixer_bass_control,
&mixer_treble_control,
&mixer_chorus_mode_control,
&mixer_reverb_mode_control,
&mixer_fm_chorus_depth_control,
&mixer_fm_reverb_depth_control,
};
/*
* create and attach mixer elements for WaveTable treble / bass controls
*/
static int
snd_emu8000_create_mixer(struct snd_card *card, struct snd_emu8000 *emu)
{
struct snd_kcontrol *kctl;
int i, err = 0 ;
if (snd_BUG_ON(!emu || !card))
return -EINVAL;
spin_lock_init(&emu->control_lock);
memset(emu->controls, 0 , sizeof (emu->controls));
for (i = 0 ; i < EMU8000_NUM_CONTROLS; i++) {
kctl = snd_ctl_new1(mixer_defs[i], emu);
err = snd_ctl_add(card, kctl);
if (err < 0 )
goto __error;
emu->controls[i] = kctl;
}
return 0 ;
__error:
for (i = 0 ; i < EMU8000_NUM_CONTROLS; i++)
snd_ctl_remove(card, emu->controls[i]);
return err;
}
/*
* initialize and register emu8000 synth device .
*/
int
snd_emu8000_new(struct snd_card *card, int index, long port, int seq_ports,
struct snd_seq_device **awe_ret)
{
struct snd_seq_device *awe;
struct snd_emu8000 *hw;
int err;
if (awe_ret)
*awe_ret = NULL;
if (seq_ports <= 0 )
return 0 ;
hw = devm_kzalloc(card->dev, sizeof (*hw), GFP_KERNEL);
if (hw == NULL)
return -ENOMEM;
spin_lock_init(&hw->reg_lock);
hw->index = index;
hw->port1 = port;
hw->port2 = port + 0 x400;
hw->port3 = port + 0 x800;
if (!devm_request_region(card->dev, hw->port1, 4 , "Emu8000-1" ) ||
!devm_request_region(card->dev, hw->port2, 4 , "Emu8000-2" ) ||
!devm_request_region(card->dev, hw->port3, 4 , "Emu8000-3" )) {
dev_err(card->dev, "sbawe: can't grab ports 0x%lx, 0x%lx, 0x%lx\n" ,
hw->port1, hw->port2, hw->port3);
return -EBUSY;
}
hw->mem_size = 0 ;
hw->card = card;
hw->seq_ports = seq_ports;
hw->bass_level = 5 ;
hw->treble_level = 9 ;
hw->chorus_mode = 2 ;
hw->reverb_mode = 4 ;
hw->fm_chorus_depth = 0 ;
hw->fm_reverb_depth = 0 ;
if (snd_emu8000_detect(hw) < 0 )
return -ENODEV;
snd_emu8000_init_hw(hw);
err = snd_emu8000_create_mixer(card, hw);
if (err < 0 )
return err;
#if IS_ENABLED(CONFIG_SND_SEQUENCER)
if (snd_seq_device_new(card, index, SNDRV_SEQ_DEV_ID_EMU8000,
sizeof (struct snd_emu8000*), &awe) >= 0 ) {
strscpy(awe->name, "EMU-8000" );
*(struct snd_emu8000 **)SNDRV_SEQ_DEVICE_ARGPTR(awe) = hw;
}
#else
awe = NULL;
#endif
if (awe_ret)
*awe_ret = awe;
return 0 ;
}
/*
* exported stuff
*/
EXPORT_SYMBOL(snd_emu8000_poke);
EXPORT_SYMBOL(snd_emu8000_peek);
EXPORT_SYMBOL(snd_emu8000_poke_dw);
EXPORT_SYMBOL(snd_emu8000_peek_dw);
EXPORT_SYMBOL(snd_emu8000_dma_chan);
EXPORT_SYMBOL(snd_emu8000_init_fm);
EXPORT_SYMBOL(snd_emu8000_load_chorus_fx);
EXPORT_SYMBOL(snd_emu8000_load_reverb_fx);
EXPORT_SYMBOL(snd_emu8000_update_chorus_mode);
EXPORT_SYMBOL(snd_emu8000_update_reverb_mode);
EXPORT_SYMBOL(snd_emu8000_update_equalizer);
Messung V0.5 in Prozent C=93 H=91 G=91
¤ Dauer der Verarbeitung: 0.22 Sekunden
¤
*© Formatika GbR, Deutschland