2005-04-17 02:20:36 +04:00
/*
2006-10-04 01:01:26 +04:00
* sound / oss / opl3 . c
2005-04-17 02:20:36 +04:00
*
* A low level driver for Yamaha YM3812 and OPL - 3 - chips
*
*
* Copyright ( C ) by Hannu Savolainen 1993 - 1997
*
* OSS / Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE ( GPL )
* Version 2 ( June 1991 ) . See the " COPYING " file distributed with this software
* for more info .
*
*
* Changes
* Thomas Sailer ioctl code reworked ( vmalloc / vfree removed )
* Alan Cox modularisation , fixed sound_mem allocs .
* Christoph Hellwig Adapted to module_init / module_exit
* Arnaldo C . de Melo get rid of check_region , use request_region for
* OPL4 , release it on exit , some cleanups .
*
* Status
* Believed to work . Badly needs rewriting a bit to support multiple
* OPL3 devices .
*/
# include <linux/init.h>
# include <linux/module.h>
# include <linux/delay.h>
/*
* Major improvements to the FM handling 30 AUG92 by Rob Hooft ,
* hooft @ chem . ruu . nl
*/
# include "sound_config.h"
# include "opl3_hw.h"
# define MAX_VOICE 18
# define OFFS_4OP 11
struct voice_info
{
unsigned char keyon_byte ;
long bender ;
long bender_range ;
unsigned long orig_freq ;
unsigned long current_freq ;
int volume ;
int mode ;
int panning ; /* 0xffff means not set */
} ;
typedef struct opl_devinfo
{
int base ;
int left_io , right_io ;
int nr_voice ;
int lv_map [ MAX_VOICE ] ;
struct voice_info voc [ MAX_VOICE ] ;
struct voice_alloc_info * v_alloc ;
struct channel_info * chn_info ;
struct sbi_instrument i_map [ SBFM_MAXINSTR ] ;
struct sbi_instrument * act_i [ MAX_VOICE ] ;
struct synth_info fm_info ;
int busy ;
int model ;
unsigned char cmask ;
int is_opl4 ;
} opl_devinfo ;
static struct opl_devinfo * devc = NULL ;
static int detected_model ;
static int store_instr ( int instr_no , struct sbi_instrument * instr ) ;
static void freq_to_fnum ( int freq , int * block , int * fnum ) ;
static void opl3_command ( int io_addr , unsigned int addr , unsigned int val ) ;
static int opl3_kill_note ( int dev , int voice , int note , int velocity ) ;
static void enter_4op_mode ( void )
{
int i ;
static int v4op [ MAX_VOICE ] = {
0 , 1 , 2 , 9 , 10 , 11 , 6 , 7 , 8 , 15 , 16 , 17
} ;
devc - > cmask = 0x3f ; /* Connect all possible 4 OP voice operators */
opl3_command ( devc - > right_io , CONNECTION_SELECT_REGISTER , 0x3f ) ;
for ( i = 0 ; i < 3 ; i + + )
pv_map [ i ] . voice_mode = 4 ;
for ( i = 3 ; i < 6 ; i + + )
pv_map [ i ] . voice_mode = 0 ;
for ( i = 9 ; i < 12 ; i + + )
pv_map [ i ] . voice_mode = 4 ;
for ( i = 12 ; i < 15 ; i + + )
pv_map [ i ] . voice_mode = 0 ;
for ( i = 0 ; i < 12 ; i + + )
devc - > lv_map [ i ] = v4op [ i ] ;
devc - > v_alloc - > max_voice = devc - > nr_voice = 12 ;
}
static int opl3_ioctl ( int dev , unsigned int cmd , void __user * arg )
{
struct sbi_instrument ins ;
switch ( cmd ) {
case SNDCTL_FM_LOAD_INSTR :
printk ( KERN_WARNING " Warning: Obsolete ioctl(SNDCTL_FM_LOAD_INSTR) used. Fix the program. \n " ) ;
if ( copy_from_user ( & ins , arg , sizeof ( ins ) ) )
return - EFAULT ;
if ( ins . channel < 0 | | ins . channel > = SBFM_MAXINSTR ) {
printk ( KERN_WARNING " FM Error: Invalid instrument number %d \n " , ins . channel ) ;
return - EINVAL ;
}
return store_instr ( ins . channel , & ins ) ;
case SNDCTL_SYNTH_INFO :
devc - > fm_info . nr_voices = ( devc - > nr_voice = = 12 ) ? 6 : devc - > nr_voice ;
if ( copy_to_user ( arg , & devc - > fm_info , sizeof ( devc - > fm_info ) ) )
return - EFAULT ;
return 0 ;
case SNDCTL_SYNTH_MEMAVL :
return 0x7fffffff ;
case SNDCTL_FM_4OP_ENABLE :
if ( devc - > model = = 2 )
enter_4op_mode ( ) ;
return 0 ;
default :
return - EINVAL ;
}
}
2007-07-16 10:39:01 +04:00
static int opl3_detect ( int ioaddr )
2005-04-17 02:20:36 +04:00
{
/*
* This function returns 1 if the FM chip is present at the given I / O port
* The detection algorithm plays with the timer built in the FM chip and
* looks for a change in the status register .
*
* Note ! The timers of the FM chip are not connected to AdLib ( and compatible )
* boards .
*
* Note2 ! The chip is initialized if detected .
*/
unsigned char stat1 , signature ;
int i ;
if ( devc ! = NULL )
{
printk ( KERN_ERR " opl3: Only one OPL3 supported. \n " ) ;
return 0 ;
}
2007-02-14 11:33:16 +03:00
devc = kzalloc ( sizeof ( * devc ) , GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
if ( devc = = NULL )
{
printk ( KERN_ERR " opl3: Can't allocate memory for the device control "
" structure \n " ) ;
return 0 ;
}
strcpy ( devc - > fm_info . name , " OPL2 " ) ;
if ( ! request_region ( ioaddr , 4 , devc - > fm_info . name ) ) {
printk ( KERN_WARNING " opl3: I/O port 0x%x already in use \n " , ioaddr ) ;
goto cleanup_devc ;
}
devc - > base = ioaddr ;
/* Reset timers 1 and 2 */
opl3_command ( ioaddr , TIMER_CONTROL_REGISTER , TIMER1_MASK | TIMER2_MASK ) ;
/* Reset the IRQ of the FM chip */
opl3_command ( ioaddr , TIMER_CONTROL_REGISTER , IRQ_RESET ) ;
signature = stat1 = inb ( ioaddr ) ; /* Status register */
if ( signature ! = 0x00 & & signature ! = 0x06 & & signature ! = 0x02 & &
signature ! = 0x0f )
{
MDB ( printk ( KERN_INFO " OPL3 not detected %x \n " , signature ) ) ;
goto cleanup_region ;
}
if ( signature = = 0x06 ) /* OPL2 */
{
detected_model = 2 ;
}
else if ( signature = = 0x00 | | signature = = 0x0f ) /* OPL3 or OPL4 */
{
unsigned char tmp ;
detected_model = 3 ;
/*
* Detect availability of OPL4 ( _experimental_ ) . Works probably
* only after a cold boot . In addition the OPL4 port
* of the chip may not be connected to the PC bus at all .
*/
opl3_command ( ioaddr + 2 , OPL3_MODE_REGISTER , 0x00 ) ;
opl3_command ( ioaddr + 2 , OPL3_MODE_REGISTER , OPL3_ENABLE | OPL4_ENABLE ) ;
if ( ( tmp = inb ( ioaddr ) ) = = 0x02 ) /* Have a OPL4 */
{
detected_model = 4 ;
}
if ( request_region ( ioaddr - 8 , 2 , " OPL4 " ) ) /* OPL4 port was free */
{
int tmp ;
outb ( ( 0x02 ) , ioaddr - 8 ) ; /* Select OPL4 ID register */
udelay ( 10 ) ;
tmp = inb ( ioaddr - 7 ) ; /* Read it */
udelay ( 10 ) ;
if ( tmp = = 0x20 ) /* OPL4 should return 0x20 here */
{
detected_model = 4 ;
outb ( ( 0xF8 ) , ioaddr - 8 ) ; /* Select OPL4 FM mixer control */
udelay ( 10 ) ;
outb ( ( 0x1B ) , ioaddr - 7 ) ; /* Write value */
udelay ( 10 ) ;
}
else
{ /* release OPL4 port */
release_region ( ioaddr - 8 , 2 ) ;
detected_model = 3 ;
}
}
opl3_command ( ioaddr + 2 , OPL3_MODE_REGISTER , 0 ) ;
}
for ( i = 0 ; i < 9 ; i + + )
opl3_command ( ioaddr , KEYON_BLOCK + i , 0 ) ; /*
* Note off
*/
opl3_command ( ioaddr , TEST_REGISTER , ENABLE_WAVE_SELECT ) ;
opl3_command ( ioaddr , PERCOSSION_REGISTER , 0x00 ) ; /*
* Melodic mode .
*/
return 1 ;
cleanup_region :
release_region ( ioaddr , 4 ) ;
cleanup_devc :
kfree ( devc ) ;
devc = NULL ;
return 0 ;
}
static int opl3_kill_note ( int devno , int voice , int note , int velocity )
{
struct physical_voice_info * map ;
if ( voice < 0 | | voice > = devc - > nr_voice )
return 0 ;
devc - > v_alloc - > map [ voice ] = 0 ;
map = & pv_map [ devc - > lv_map [ voice ] ] ;
DEB ( printk ( " Kill note %d \n " , voice ) ) ;
if ( map - > voice_mode = = 0 )
return 0 ;
opl3_command ( map - > ioaddr , KEYON_BLOCK + map - > voice_num , devc - > voc [ voice ] . keyon_byte & ~ 0x20 ) ;
devc - > voc [ voice ] . keyon_byte = 0 ;
devc - > voc [ voice ] . bender = 0 ;
devc - > voc [ voice ] . volume = 64 ;
devc - > voc [ voice ] . panning = 0xffff ; /* Not set */
devc - > voc [ voice ] . bender_range = 200 ;
devc - > voc [ voice ] . orig_freq = 0 ;
devc - > voc [ voice ] . current_freq = 0 ;
devc - > voc [ voice ] . mode = 0 ;
return 0 ;
}
# define HIHAT 0
# define CYMBAL 1
# define TOMTOM 2
# define SNARE 3
# define BDRUM 4
# define UNDEFINED TOMTOM
# define DEFAULT TOMTOM
static int store_instr ( int instr_no , struct sbi_instrument * instr )
{
if ( instr - > key ! = FM_PATCH & & ( instr - > key ! = OPL3_PATCH | | devc - > model ! = 2 ) )
printk ( KERN_WARNING " FM warning: Invalid patch format field (key) 0x%x \n " , instr - > key ) ;
memcpy ( ( char * ) & ( devc - > i_map [ instr_no ] ) , ( char * ) instr , sizeof ( * instr ) ) ;
return 0 ;
}
static int opl3_set_instr ( int dev , int voice , int instr_no )
{
if ( voice < 0 | | voice > = devc - > nr_voice )
return 0 ;
if ( instr_no < 0 | | instr_no > = SBFM_MAXINSTR )
instr_no = 0 ; /* Acoustic piano (usually) */
devc - > act_i [ voice ] = & devc - > i_map [ instr_no ] ;
return 0 ;
}
/*
* The next table looks magical , but it certainly is not . Its values have
* been calculated as table [ i ] = 8 * log ( i / 64 ) / log ( 2 ) with an obvious exception
* for i = 0. This log - table converts a linear volume - scaling ( 0. .127 ) to a
* logarithmic scaling as present in the FM - synthesizer chips . so : Volume
* 64 = 0 db = relative volume 0 and : Volume 32 = - 6 db = relative
* volume - 8 it was implemented as a table because it is only 128 bytes and
* it saves a lot of log ( ) calculations . ( RH )
*/
static char fm_volume_table [ 128 ] =
{
- 64 , - 48 , - 40 , - 35 , - 32 , - 29 , - 27 , - 26 ,
- 24 , - 23 , - 21 , - 20 , - 19 , - 18 , - 18 , - 17 ,
- 16 , - 15 , - 15 , - 14 , - 13 , - 13 , - 12 , - 12 ,
- 11 , - 11 , - 10 , - 10 , - 10 , - 9 , - 9 , - 8 ,
- 8 , - 8 , - 7 , - 7 , - 7 , - 6 , - 6 , - 6 ,
- 5 , - 5 , - 5 , - 5 , - 4 , - 4 , - 4 , - 4 ,
- 3 , - 3 , - 3 , - 3 , - 2 , - 2 , - 2 , - 2 ,
- 2 , - 1 , - 1 , - 1 , - 1 , 0 , 0 , 0 ,
0 , 0 , 0 , 1 , 1 , 1 , 1 , 1 ,
1 , 2 , 2 , 2 , 2 , 2 , 2 , 2 ,
3 , 3 , 3 , 3 , 3 , 3 , 3 , 4 ,
4 , 4 , 4 , 4 , 4 , 4 , 4 , 5 ,
5 , 5 , 5 , 5 , 5 , 5 , 5 , 5 ,
6 , 6 , 6 , 6 , 6 , 6 , 6 , 6 ,
6 , 7 , 7 , 7 , 7 , 7 , 7 , 7 ,
7 , 7 , 7 , 8 , 8 , 8 , 8 , 8
} ;
static void calc_vol ( unsigned char * regbyte , int volume , int main_vol )
{
int level = ( ~ * regbyte & 0x3f ) ;
if ( main_vol > 127 )
main_vol = 127 ;
volume = ( volume * main_vol ) / 127 ;
if ( level )
level + = fm_volume_table [ volume ] ;
if ( level > 0x3f )
level = 0x3f ;
if ( level < 0 )
level = 0 ;
* regbyte = ( * regbyte & 0xc0 ) | ( ~ level & 0x3f ) ;
}
static void set_voice_volume ( int voice , int volume , int main_vol )
{
unsigned char vol1 , vol2 , vol3 , vol4 ;
struct sbi_instrument * instr ;
struct physical_voice_info * map ;
if ( voice < 0 | | voice > = devc - > nr_voice )
return ;
map = & pv_map [ devc - > lv_map [ voice ] ] ;
instr = devc - > act_i [ voice ] ;
if ( ! instr )
instr = & devc - > i_map [ 0 ] ;
if ( instr - > channel < 0 )
return ;
if ( devc - > voc [ voice ] . mode = = 0 )
return ;
if ( devc - > voc [ voice ] . mode = = 2 )
{
vol1 = instr - > operators [ 2 ] ;
vol2 = instr - > operators [ 3 ] ;
if ( ( instr - > operators [ 10 ] & 0x01 ) )
{
calc_vol ( & vol1 , volume , main_vol ) ;
calc_vol ( & vol2 , volume , main_vol ) ;
}
else
{
calc_vol ( & vol2 , volume , main_vol ) ;
}
opl3_command ( map - > ioaddr , KSL_LEVEL + map - > op [ 0 ] , vol1 ) ;
opl3_command ( map - > ioaddr , KSL_LEVEL + map - > op [ 1 ] , vol2 ) ;
}
else
{ /*
* 4 OP voice
*/
int connection ;
vol1 = instr - > operators [ 2 ] ;
vol2 = instr - > operators [ 3 ] ;
vol3 = instr - > operators [ OFFS_4OP + 2 ] ;
vol4 = instr - > operators [ OFFS_4OP + 3 ] ;
/*
* The connection method for 4 OP devc - > voc is defined by the rightmost
* bits at the offsets 10 and 10 + OFFS_4OP
*/
connection = ( ( instr - > operators [ 10 ] & 0x01 ) < < 1 ) | ( instr - > operators [ 10 + OFFS_4OP ] & 0x01 ) ;
switch ( connection )
{
case 0 :
calc_vol ( & vol4 , volume , main_vol ) ;
break ;
case 1 :
calc_vol ( & vol2 , volume , main_vol ) ;
calc_vol ( & vol4 , volume , main_vol ) ;
break ;
case 2 :
calc_vol ( & vol1 , volume , main_vol ) ;
calc_vol ( & vol4 , volume , main_vol ) ;
break ;
case 3 :
calc_vol ( & vol1 , volume , main_vol ) ;
calc_vol ( & vol3 , volume , main_vol ) ;
calc_vol ( & vol4 , volume , main_vol ) ;
break ;
default :
;
}
opl3_command ( map - > ioaddr , KSL_LEVEL + map - > op [ 0 ] , vol1 ) ;
opl3_command ( map - > ioaddr , KSL_LEVEL + map - > op [ 1 ] , vol2 ) ;
opl3_command ( map - > ioaddr , KSL_LEVEL + map - > op [ 2 ] , vol3 ) ;
opl3_command ( map - > ioaddr , KSL_LEVEL + map - > op [ 3 ] , vol4 ) ;
}
}
static int opl3_start_note ( int dev , int voice , int note , int volume )
{
unsigned char data , fpc ;
int block , fnum , freq , voice_mode , pan ;
struct sbi_instrument * instr ;
struct physical_voice_info * map ;
if ( voice < 0 | | voice > = devc - > nr_voice )
return 0 ;
map = & pv_map [ devc - > lv_map [ voice ] ] ;
pan = devc - > voc [ voice ] . panning ;
if ( map - > voice_mode = = 0 )
return 0 ;
if ( note = = 255 ) /*
* Just change the volume
*/
{
set_voice_volume ( voice , volume , devc - > voc [ voice ] . volume ) ;
return 0 ;
}
/*
* Kill previous note before playing
*/
opl3_command ( map - > ioaddr , KSL_LEVEL + map - > op [ 1 ] , 0xff ) ; /*
* Carrier
* volume to
* min
*/
opl3_command ( map - > ioaddr , KSL_LEVEL + map - > op [ 0 ] , 0xff ) ; /*
* Modulator
* volume to
*/
if ( map - > voice_mode = = 4 )
{
opl3_command ( map - > ioaddr , KSL_LEVEL + map - > op [ 2 ] , 0xff ) ;
opl3_command ( map - > ioaddr , KSL_LEVEL + map - > op [ 3 ] , 0xff ) ;
}
opl3_command ( map - > ioaddr , KEYON_BLOCK + map - > voice_num , 0x00 ) ; /*
* Note
* off
*/
instr = devc - > act_i [ voice ] ;
if ( ! instr )
instr = & devc - > i_map [ 0 ] ;
if ( instr - > channel < 0 )
{
printk ( KERN_WARNING " opl3: Initializing voice %d with undefined instrument \n " , voice ) ;
return 0 ;
}
if ( map - > voice_mode = = 2 & & instr - > key = = OPL3_PATCH )
return 0 ; /*
* Cannot play
*/
voice_mode = map - > voice_mode ;
if ( voice_mode = = 4 )
{
int voice_shift ;
voice_shift = ( map - > ioaddr = = devc - > left_io ) ? 0 : 3 ;
voice_shift + = map - > voice_num ;
if ( instr - > key ! = OPL3_PATCH ) /*
* Just 2 OP patch
*/
{
voice_mode = 2 ;
devc - > cmask & = ~ ( 1 < < voice_shift ) ;
}
else
{
devc - > cmask | = ( 1 < < voice_shift ) ;
}
opl3_command ( devc - > right_io , CONNECTION_SELECT_REGISTER , devc - > cmask ) ;
}
/*
* Set Sound Characteristics
*/
opl3_command ( map - > ioaddr , AM_VIB + map - > op [ 0 ] , instr - > operators [ 0 ] ) ;
opl3_command ( map - > ioaddr , AM_VIB + map - > op [ 1 ] , instr - > operators [ 1 ] ) ;
/*
* Set Attack / Decay
*/
opl3_command ( map - > ioaddr , ATTACK_DECAY + map - > op [ 0 ] , instr - > operators [ 4 ] ) ;
opl3_command ( map - > ioaddr , ATTACK_DECAY + map - > op [ 1 ] , instr - > operators [ 5 ] ) ;
/*
* Set Sustain / Release
*/
opl3_command ( map - > ioaddr , SUSTAIN_RELEASE + map - > op [ 0 ] , instr - > operators [ 6 ] ) ;
opl3_command ( map - > ioaddr , SUSTAIN_RELEASE + map - > op [ 1 ] , instr - > operators [ 7 ] ) ;
/*
* Set Wave Select
*/
opl3_command ( map - > ioaddr , WAVE_SELECT + map - > op [ 0 ] , instr - > operators [ 8 ] ) ;
opl3_command ( map - > ioaddr , WAVE_SELECT + map - > op [ 1 ] , instr - > operators [ 9 ] ) ;
/*
* Set Feedback / Connection
*/
fpc = instr - > operators [ 10 ] ;
if ( pan ! = 0xffff )
{
fpc & = ~ STEREO_BITS ;
if ( pan < - 64 )
fpc | = VOICE_TO_LEFT ;
else
if ( pan > 64 )
fpc | = VOICE_TO_RIGHT ;
else
fpc | = ( VOICE_TO_LEFT | VOICE_TO_RIGHT ) ;
}
if ( ! ( fpc & 0x30 ) )
fpc | = 0x30 ; /*
* Ensure that at least one chn is enabled
*/
opl3_command ( map - > ioaddr , FEEDBACK_CONNECTION + map - > voice_num , fpc ) ;
/*
* If the voice is a 4 OP one , initialize the operators 3 and 4 also
*/
if ( voice_mode = = 4 )
{
/*
* Set Sound Characteristics
*/
opl3_command ( map - > ioaddr , AM_VIB + map - > op [ 2 ] , instr - > operators [ OFFS_4OP + 0 ] ) ;
opl3_command ( map - > ioaddr , AM_VIB + map - > op [ 3 ] , instr - > operators [ OFFS_4OP + 1 ] ) ;
/*
* Set Attack / Decay
*/
opl3_command ( map - > ioaddr , ATTACK_DECAY + map - > op [ 2 ] , instr - > operators [ OFFS_4OP + 4 ] ) ;
opl3_command ( map - > ioaddr , ATTACK_DECAY + map - > op [ 3 ] , instr - > operators [ OFFS_4OP + 5 ] ) ;
/*
* Set Sustain / Release
*/
opl3_command ( map - > ioaddr , SUSTAIN_RELEASE + map - > op [ 2 ] , instr - > operators [ OFFS_4OP + 6 ] ) ;
opl3_command ( map - > ioaddr , SUSTAIN_RELEASE + map - > op [ 3 ] , instr - > operators [ OFFS_4OP + 7 ] ) ;
/*
* Set Wave Select
*/
opl3_command ( map - > ioaddr , WAVE_SELECT + map - > op [ 2 ] , instr - > operators [ OFFS_4OP + 8 ] ) ;
opl3_command ( map - > ioaddr , WAVE_SELECT + map - > op [ 3 ] , instr - > operators [ OFFS_4OP + 9 ] ) ;
/*
* Set Feedback / Connection
*/
fpc = instr - > operators [ OFFS_4OP + 10 ] ;
if ( ! ( fpc & 0x30 ) )
fpc | = 0x30 ; /*
* Ensure that at least one chn is enabled
*/
opl3_command ( map - > ioaddr , FEEDBACK_CONNECTION + map - > voice_num + 3 , fpc ) ;
}
devc - > voc [ voice ] . mode = voice_mode ;
set_voice_volume ( voice , volume , devc - > voc [ voice ] . volume ) ;
freq = devc - > voc [ voice ] . orig_freq = note_to_freq ( note ) / 1000 ;
/*
* Since the pitch bender may have been set before playing the note , we
* have to calculate the bending now .
*/
freq = compute_finetune ( devc - > voc [ voice ] . orig_freq , devc - > voc [ voice ] . bender , devc - > voc [ voice ] . bender_range , 0 ) ;
devc - > voc [ voice ] . current_freq = freq ;
freq_to_fnum ( freq , & block , & fnum ) ;
/*
* Play note
*/
data = fnum & 0xff ; /*
* Least significant bits of fnumber
*/
opl3_command ( map - > ioaddr , FNUM_LOW + map - > voice_num , data ) ;
data = 0x20 | ( ( block & 0x7 ) < < 2 ) | ( ( fnum > > 8 ) & 0x3 ) ;
devc - > voc [ voice ] . keyon_byte = data ;
opl3_command ( map - > ioaddr , KEYON_BLOCK + map - > voice_num , data ) ;
if ( voice_mode = = 4 )
opl3_command ( map - > ioaddr , KEYON_BLOCK + map - > voice_num + 3 , data ) ;
return 0 ;
}
static void freq_to_fnum ( int freq , int * block , int * fnum )
{
int f , octave ;
/*
* Converts the note frequency to block and fnum values for the FM chip
*/
/*
* First try to compute the block - value ( octave ) where the note belongs
*/
f = freq ;
octave = 5 ;
if ( f = = 0 )
octave = 0 ;
else if ( f < 261 )
{
while ( f < 261 )
{
octave - - ;
f < < = 1 ;
}
}
else if ( f > 493 )
{
while ( f > 493 )
{
octave + + ;
f > > = 1 ;
}
}
if ( octave > 7 )
octave = 7 ;
* fnum = freq * ( 1 < < ( 20 - octave ) ) / 49716 ;
* block = octave ;
}
static void opl3_command ( int io_addr , unsigned int addr , unsigned int val )
{
int i ;
/*
* The original 2 - OP synth requires a quite long delay after writing to a
* register . The OPL - 3 survives with just two INBs
*/
outb ( ( ( unsigned char ) ( addr & 0xff ) ) , io_addr ) ;
if ( devc - > model ! = 2 )
udelay ( 10 ) ;
else
for ( i = 0 ; i < 2 ; i + + )
inb ( io_addr ) ;
outb ( ( ( unsigned char ) ( val & 0xff ) ) , io_addr + 1 ) ;
if ( devc - > model ! = 2 )
udelay ( 30 ) ;
else
for ( i = 0 ; i < 2 ; i + + )
inb ( io_addr ) ;
}
static void opl3_reset ( int devno )
{
int i ;
for ( i = 0 ; i < 18 ; i + + )
devc - > lv_map [ i ] = i ;
for ( i = 0 ; i < devc - > nr_voice ; i + + )
{
opl3_command ( pv_map [ devc - > lv_map [ i ] ] . ioaddr ,
KSL_LEVEL + pv_map [ devc - > lv_map [ i ] ] . op [ 0 ] , 0xff ) ;
opl3_command ( pv_map [ devc - > lv_map [ i ] ] . ioaddr ,
KSL_LEVEL + pv_map [ devc - > lv_map [ i ] ] . op [ 1 ] , 0xff ) ;
if ( pv_map [ devc - > lv_map [ i ] ] . voice_mode = = 4 )
{
opl3_command ( pv_map [ devc - > lv_map [ i ] ] . ioaddr ,
KSL_LEVEL + pv_map [ devc - > lv_map [ i ] ] . op [ 2 ] , 0xff ) ;
opl3_command ( pv_map [ devc - > lv_map [ i ] ] . ioaddr ,
KSL_LEVEL + pv_map [ devc - > lv_map [ i ] ] . op [ 3 ] , 0xff ) ;
}
opl3_kill_note ( devno , i , 0 , 64 ) ;
}
if ( devc - > model = = 2 )
{
devc - > v_alloc - > max_voice = devc - > nr_voice = 18 ;
for ( i = 0 ; i < 18 ; i + + )
pv_map [ i ] . voice_mode = 2 ;
}
}
static int opl3_open ( int dev , int mode )
{
int i ;
if ( devc - > busy )
return - EBUSY ;
devc - > busy = 1 ;
devc - > v_alloc - > max_voice = devc - > nr_voice = ( devc - > model = = 2 ) ? 18 : 9 ;
devc - > v_alloc - > timestamp = 0 ;
for ( i = 0 ; i < 18 ; i + + )
{
devc - > v_alloc - > map [ i ] = 0 ;
devc - > v_alloc - > alloc_times [ i ] = 0 ;
}
devc - > cmask = 0x00 ; /*
* Just 2 OP mode
*/
if ( devc - > model = = 2 )
opl3_command ( devc - > right_io , CONNECTION_SELECT_REGISTER , devc - > cmask ) ;
return 0 ;
}
static void opl3_close ( int dev )
{
devc - > busy = 0 ;
devc - > v_alloc - > max_voice = devc - > nr_voice = ( devc - > model = = 2 ) ? 18 : 9 ;
devc - > fm_info . nr_drums = 0 ;
devc - > fm_info . perc_mode = 0 ;
opl3_reset ( dev ) ;
}
static void opl3_hw_control ( int dev , unsigned char * event )
{
}
static int opl3_load_patch ( int dev , int format , const char __user * addr ,
int offs , int count , int pmgr_flag )
{
struct sbi_instrument ins ;
if ( count < sizeof ( ins ) )
{
printk ( KERN_WARNING " FM Error: Patch record too short \n " ) ;
return - EINVAL ;
}
/*
* What the fuck is going on here ? We leave junk in the beginning
* of ins and then check the field pretty close to that beginning ?
*/
if ( copy_from_user ( & ( ( char * ) & ins ) [ offs ] , addr + offs , sizeof ( ins ) - offs ) )
return - EFAULT ;
if ( ins . channel < 0 | | ins . channel > = SBFM_MAXINSTR )
{
printk ( KERN_WARNING " FM Error: Invalid instrument number %d \n " , ins . channel ) ;
return - EINVAL ;
}
ins . key = format ;
return store_instr ( ins . channel , & ins ) ;
}
static void opl3_panning ( int dev , int voice , int value )
{
devc - > voc [ voice ] . panning = value ;
}
static void opl3_volume_method ( int dev , int mode )
{
}
# define SET_VIBRATO(cell) { \
tmp = instr - > operators [ ( cell - 1 ) + ( ( ( cell - 1 ) / 2 ) * OFFS_4OP ) ] ; \
if ( pressure > 110 ) \
tmp | = 0x40 ; /* Vibrato on */ \
opl3_command ( map - > ioaddr , AM_VIB + map - > op [ cell - 1 ] , tmp ) ; }
static void opl3_aftertouch ( int dev , int voice , int pressure )
{
int tmp ;
struct sbi_instrument * instr ;
struct physical_voice_info * map ;
if ( voice < 0 | | voice > = devc - > nr_voice )
return ;
map = & pv_map [ devc - > lv_map [ voice ] ] ;
DEB ( printk ( " Aftertouch %d \n " , voice ) ) ;
if ( map - > voice_mode = = 0 )
return ;
/*
* Adjust the amount of vibrato depending the pressure
*/
instr = devc - > act_i [ voice ] ;
if ( ! instr )
instr = & devc - > i_map [ 0 ] ;
if ( devc - > voc [ voice ] . mode = = 4 )
{
int connection = ( ( instr - > operators [ 10 ] & 0x01 ) < < 1 ) | ( instr - > operators [ 10 + OFFS_4OP ] & 0x01 ) ;
switch ( connection )
{
case 0 :
SET_VIBRATO ( 4 ) ;
break ;
case 1 :
SET_VIBRATO ( 2 ) ;
SET_VIBRATO ( 4 ) ;
break ;
case 2 :
SET_VIBRATO ( 1 ) ;
SET_VIBRATO ( 4 ) ;
break ;
case 3 :
SET_VIBRATO ( 1 ) ;
SET_VIBRATO ( 3 ) ;
SET_VIBRATO ( 4 ) ;
break ;
}
/*
* Not implemented yet
*/
}
else
{
SET_VIBRATO ( 1 ) ;
if ( ( instr - > operators [ 10 ] & 0x01 ) ) /*
* Additive synthesis
*/
SET_VIBRATO ( 2 ) ;
}
}
# undef SET_VIBRATO
static void bend_pitch ( int dev , int voice , int value )
{
unsigned char data ;
int block , fnum , freq ;
struct physical_voice_info * map ;
map = & pv_map [ devc - > lv_map [ voice ] ] ;
if ( map - > voice_mode = = 0 )
return ;
devc - > voc [ voice ] . bender = value ;
if ( ! value )
return ;
if ( ! ( devc - > voc [ voice ] . keyon_byte & 0x20 ) )
return ; /*
* Not keyed on
*/
freq = compute_finetune ( devc - > voc [ voice ] . orig_freq , devc - > voc [ voice ] . bender , devc - > voc [ voice ] . bender_range , 0 ) ;
devc - > voc [ voice ] . current_freq = freq ;
freq_to_fnum ( freq , & block , & fnum ) ;
data = fnum & 0xff ; /*
* Least significant bits of fnumber
*/
opl3_command ( map - > ioaddr , FNUM_LOW + map - > voice_num , data ) ;
data = 0x20 | ( ( block & 0x7 ) < < 2 ) | ( ( fnum > > 8 ) & 0x3 ) ;
devc - > voc [ voice ] . keyon_byte = data ;
opl3_command ( map - > ioaddr , KEYON_BLOCK + map - > voice_num , data ) ;
}
static void opl3_controller ( int dev , int voice , int ctrl_num , int value )
{
if ( voice < 0 | | voice > = devc - > nr_voice )
return ;
switch ( ctrl_num )
{
case CTRL_PITCH_BENDER :
bend_pitch ( dev , voice , value ) ;
break ;
case CTRL_PITCH_BENDER_RANGE :
devc - > voc [ voice ] . bender_range = value ;
break ;
case CTL_MAIN_VOLUME :
devc - > voc [ voice ] . volume = value / 128 ;
break ;
case CTL_PAN :
devc - > voc [ voice ] . panning = ( value * 2 ) - 128 ;
break ;
}
}
static void opl3_bender ( int dev , int voice , int value )
{
if ( voice < 0 | | voice > = devc - > nr_voice )
return ;
bend_pitch ( dev , voice , value - 8192 ) ;
}
static int opl3_alloc_voice ( int dev , int chn , int note , struct voice_alloc_info * alloc )
{
int i , p , best , first , avail , best_time = 0x7fffffff ;
struct sbi_instrument * instr ;
int is4op ;
int instr_no ;
if ( chn < 0 | | chn > 15 )
instr_no = 0 ;
else
instr_no = devc - > chn_info [ chn ] . pgm_num ;
instr = & devc - > i_map [ instr_no ] ;
if ( instr - > channel < 0 | | /* Instrument not loaded */
devc - > nr_voice ! = 12 ) /* Not in 4 OP mode */
is4op = 0 ;
else if ( devc - > nr_voice = = 12 ) /* 4 OP mode */
is4op = ( instr - > key = = OPL3_PATCH ) ;
else
is4op = 0 ;
if ( is4op )
{
first = p = 0 ;
avail = 6 ;
}
else
{
if ( devc - > nr_voice = = 12 ) /* 4 OP mode. Use the '2 OP only' operators first */
first = p = 6 ;
else
first = p = 0 ;
avail = devc - > nr_voice ;
}
/*
* Now try to find a free voice
*/
best = first ;
for ( i = 0 ; i < avail ; i + + )
{
if ( alloc - > map [ p ] = = 0 )
{
return p ;
}
if ( alloc - > alloc_times [ p ] < best_time ) /* Find oldest playing note */
{
best_time = alloc - > alloc_times [ p ] ;
best = p ;
}
p = ( p + 1 ) % avail ;
}
/*
* Insert some kind of priority mechanism here .
*/
if ( best < 0 )
best = 0 ;
if ( best > devc - > nr_voice )
best - = devc - > nr_voice ;
return best ; /* All devc->voc in use. Select the first one. */
}
static void opl3_setup_voice ( int dev , int voice , int chn )
{
struct channel_info * info =
& synth_devs [ dev ] - > chn_info [ chn ] ;
opl3_set_instr ( dev , voice , info - > pgm_num ) ;
devc - > voc [ voice ] . bender = 0 ;
devc - > voc [ voice ] . bender_range = info - > bender_range ;
devc - > voc [ voice ] . volume = info - > controllers [ CTL_MAIN_VOLUME ] ;
devc - > voc [ voice ] . panning = ( info - > controllers [ CTL_PAN ] * 2 ) - 128 ;
}
static struct synth_operations opl3_operations =
{
. owner = THIS_MODULE ,
. id = " OPL " ,
. info = NULL ,
. midi_dev = 0 ,
. synth_type = SYNTH_TYPE_FM ,
. synth_subtype = FM_TYPE_ADLIB ,
. open = opl3_open ,
. close = opl3_close ,
. ioctl = opl3_ioctl ,
. kill_note = opl3_kill_note ,
. start_note = opl3_start_note ,
. set_instr = opl3_set_instr ,
. reset = opl3_reset ,
. hw_control = opl3_hw_control ,
. load_patch = opl3_load_patch ,
. aftertouch = opl3_aftertouch ,
. controller = opl3_controller ,
. panning = opl3_panning ,
. volume_method = opl3_volume_method ,
. bender = opl3_bender ,
. alloc_voice = opl3_alloc_voice ,
. setup_voice = opl3_setup_voice
} ;
2007-07-16 10:39:01 +04:00
static int opl3_init ( int ioaddr , struct module * owner )
2005-04-17 02:20:36 +04:00
{
int i ;
int me ;
if ( devc = = NULL )
{
printk ( KERN_ERR " opl3: Device control structure not initialized. \n " ) ;
return - 1 ;
}
if ( ( me = sound_alloc_synthdev ( ) ) = = - 1 )
{
printk ( KERN_WARNING " opl3: Too many synthesizers \n " ) ;
return - 1 ;
}
devc - > nr_voice = 9 ;
devc - > fm_info . device = 0 ;
devc - > fm_info . synth_type = SYNTH_TYPE_FM ;
devc - > fm_info . synth_subtype = FM_TYPE_ADLIB ;
devc - > fm_info . perc_mode = 0 ;
devc - > fm_info . nr_voices = 9 ;
devc - > fm_info . nr_drums = 0 ;
devc - > fm_info . instr_bank_size = SBFM_MAXINSTR ;
devc - > fm_info . capabilities = 0 ;
devc - > left_io = ioaddr ;
devc - > right_io = ioaddr + 2 ;
if ( detected_model < = 2 )
devc - > model = 1 ;
else
{
devc - > model = 2 ;
if ( detected_model = = 4 )
devc - > is_opl4 = 1 ;
}
opl3_operations . info = & devc - > fm_info ;
synth_devs [ me ] = & opl3_operations ;
if ( owner )
synth_devs [ me ] - > owner = owner ;
sequencer_init ( ) ;
devc - > v_alloc = & opl3_operations . alloc ;
devc - > chn_info = & opl3_operations . chn_info [ 0 ] ;
if ( devc - > model = = 2 )
{
if ( devc - > is_opl4 )
strcpy ( devc - > fm_info . name , " Yamaha OPL4/OPL3 FM " ) ;
else
strcpy ( devc - > fm_info . name , " Yamaha OPL3 " ) ;
devc - > v_alloc - > max_voice = devc - > nr_voice = 18 ;
devc - > fm_info . nr_drums = 0 ;
devc - > fm_info . synth_subtype = FM_TYPE_OPL3 ;
devc - > fm_info . capabilities | = SYNTH_CAP_OPL3 ;
for ( i = 0 ; i < 18 ; i + + )
{
if ( pv_map [ i ] . ioaddr = = USE_LEFT )
pv_map [ i ] . ioaddr = devc - > left_io ;
else
pv_map [ i ] . ioaddr = devc - > right_io ;
}
opl3_command ( devc - > right_io , OPL3_MODE_REGISTER , OPL3_ENABLE ) ;
opl3_command ( devc - > right_io , CONNECTION_SELECT_REGISTER , 0x00 ) ;
}
else
{
strcpy ( devc - > fm_info . name , " Yamaha OPL2 " ) ;
devc - > v_alloc - > max_voice = devc - > nr_voice = 9 ;
devc - > fm_info . nr_drums = 0 ;
for ( i = 0 ; i < 18 ; i + + )
pv_map [ i ] . ioaddr = devc - > left_io ;
} ;
conf_printf2 ( devc - > fm_info . name , ioaddr , 0 , - 1 , - 1 ) ;
for ( i = 0 ; i < SBFM_MAXINSTR ; i + + )
devc - > i_map [ i ] . channel = - 1 ;
return me ;
}
static int me ;
static int io = - 1 ;
module_param ( io , int , 0 ) ;
static int __init init_opl3 ( void )
{
printk ( KERN_INFO " YM3812 and OPL-3 driver Copyright (C) by Hannu Savolainen, Rob Hooft 1993-1996 \n " ) ;
if ( io ! = - 1 ) /* User loading pure OPL3 module */
{
2007-07-16 10:39:01 +04:00
if ( ! opl3_detect ( io ) )
2005-04-17 02:20:36 +04:00
{
return - ENODEV ;
}
2007-07-16 10:39:01 +04:00
me = opl3_init ( io , THIS_MODULE ) ;
2005-04-17 02:20:36 +04:00
}
return 0 ;
}
static void __exit cleanup_opl3 ( void )
{
if ( devc & & io ! = - 1 )
{
if ( devc - > base ) {
release_region ( devc - > base , 4 ) ;
if ( devc - > is_opl4 )
release_region ( devc - > base - 8 , 2 ) ;
}
kfree ( devc ) ;
devc = NULL ;
sound_unload_synthdev ( me ) ;
}
}
module_init ( init_opl3 ) ;
module_exit ( cleanup_opl3 ) ;
# ifndef MODULE
static int __init setup_opl3 ( char * str )
{
/* io */
int ints [ 2 ] ;
str = get_options ( str , ARRAY_SIZE ( ints ) , ints ) ;
io = ints [ 1 ] ;
return 1 ;
}
__setup ( " opl3= " , setup_opl3 ) ;
# endif
MODULE_LICENSE ( " GPL " ) ;