2005-04-17 02:20:36 +04:00
/*
* Copyright ( c ) by Uros Bizjak < uros @ kss - loka . si >
*
* Midi Sequencer interface routines for OPL2 / OPL3 / OPL4 FM
*
* OPL2 / 3 FM instrument loader :
* alsa - tools / seq / sbiload /
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 2 of the License , or
* ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 59 Temple Place , Suite 330 , Boston , MA 02111 - 1307 USA
*
*/
# include "opl3_voice.h"
# include <linux/init.h>
# include <linux/moduleparam.h>
# include <sound/initval.h>
MODULE_AUTHOR ( " Uros Bizjak <uros@kss-loka.si> " ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_DESCRIPTION ( " ALSA driver for OPL3 FM synth " ) ;
int use_internal_drums = 0 ;
module_param ( use_internal_drums , bool , 0444 ) ;
MODULE_PARM_DESC ( use_internal_drums , " Enable internal OPL2/3 drums. " ) ;
2005-11-17 16:13:14 +03:00
int snd_opl3_synth_use_inc ( struct snd_opl3 * opl3 )
2005-04-17 02:20:36 +04:00
{
if ( ! try_module_get ( opl3 - > card - > module ) )
return - EFAULT ;
return 0 ;
}
2005-11-17 16:13:14 +03:00
void snd_opl3_synth_use_dec ( struct snd_opl3 * opl3 )
2005-04-17 02:20:36 +04:00
{
module_put ( opl3 - > card - > module ) ;
}
2005-11-17 16:13:14 +03:00
int snd_opl3_synth_setup ( struct snd_opl3 * opl3 )
2005-04-17 02:20:36 +04:00
{
int idx ;
2007-10-30 13:59:15 +03:00
struct snd_hwdep * hwdep = opl3 - > hwdep ;
2005-04-17 02:20:36 +04:00
2007-10-30 13:59:15 +03:00
mutex_lock ( & hwdep - > open_mutex ) ;
if ( hwdep - > used ) {
mutex_unlock ( & hwdep - > open_mutex ) ;
2005-04-17 02:20:36 +04:00
return - EBUSY ;
}
2007-10-30 13:59:15 +03:00
hwdep - > used + + ;
mutex_unlock ( & hwdep - > open_mutex ) ;
2005-04-17 02:20:36 +04:00
snd_opl3_reset ( opl3 ) ;
for ( idx = 0 ; idx < MAX_OPL3_VOICES ; idx + + ) {
opl3 - > voices [ idx ] . state = SNDRV_OPL3_ST_OFF ;
opl3 - > voices [ idx ] . time = 0 ;
opl3 - > voices [ idx ] . keyon_reg = 0x00 ;
}
opl3 - > use_time = 0 ;
opl3 - > connection_reg = 0x00 ;
if ( opl3 - > hardware > = OPL3_HW_OPL3 ) {
/* Clear 4-op connections */
opl3 - > command ( opl3 , OPL3_RIGHT | OPL3_REG_CONNECTION_SELECT ,
opl3 - > connection_reg ) ;
opl3 - > max_voices = MAX_OPL3_VOICES ;
}
return 0 ;
}
2005-11-17 16:13:14 +03:00
void snd_opl3_synth_cleanup ( struct snd_opl3 * opl3 )
2005-04-17 02:20:36 +04:00
{
unsigned long flags ;
2007-10-30 14:43:40 +03:00
struct snd_hwdep * hwdep ;
2005-04-17 02:20:36 +04:00
/* Stop system timer */
spin_lock_irqsave ( & opl3 - > sys_timer_lock , flags ) ;
if ( opl3 - > sys_timer_status ) {
del_timer ( & opl3 - > tlist ) ;
opl3 - > sys_timer_status = 0 ;
}
spin_unlock_irqrestore ( & opl3 - > sys_timer_lock , flags ) ;
snd_opl3_reset ( opl3 ) ;
2007-10-30 13:59:15 +03:00
hwdep = opl3 - > hwdep ;
mutex_lock ( & hwdep - > open_mutex ) ;
hwdep - > used - - ;
mutex_unlock ( & hwdep - > open_mutex ) ;
wake_up ( & hwdep - > open_wait ) ;
2005-04-17 02:20:36 +04:00
}
2005-11-17 16:13:14 +03:00
static int snd_opl3_synth_use ( void * private_data , struct snd_seq_port_subscribe * info )
2005-04-17 02:20:36 +04:00
{
2005-11-17 16:13:14 +03:00
struct snd_opl3 * opl3 = private_data ;
2005-04-17 02:20:36 +04:00
int err ;
if ( ( err = snd_opl3_synth_setup ( opl3 ) ) < 0 )
return err ;
if ( use_internal_drums ) {
/* Percussion mode */
opl3 - > voices [ 6 ] . state = opl3 - > voices [ 7 ] . state =
opl3 - > voices [ 8 ] . state = SNDRV_OPL3_ST_NOT_AVAIL ;
snd_opl3_load_drums ( opl3 ) ;
opl3 - > drum_reg = OPL3_PERCUSSION_ENABLE ;
opl3 - > command ( opl3 , OPL3_LEFT | OPL3_REG_PERCUSSION , opl3 - > drum_reg ) ;
} else {
opl3 - > drum_reg = 0x00 ;
}
if ( info - > sender . client ! = SNDRV_SEQ_CLIENT_SYSTEM ) {
if ( ( err = snd_opl3_synth_use_inc ( opl3 ) ) < 0 )
return err ;
}
opl3 - > synth_mode = SNDRV_OPL3_MODE_SEQ ;
return 0 ;
}
2005-11-17 16:13:14 +03:00
static int snd_opl3_synth_unuse ( void * private_data , struct snd_seq_port_subscribe * info )
2005-04-17 02:20:36 +04:00
{
2005-11-17 16:13:14 +03:00
struct snd_opl3 * opl3 = private_data ;
2005-04-17 02:20:36 +04:00
snd_opl3_synth_cleanup ( opl3 ) ;
if ( info - > sender . client ! = SNDRV_SEQ_CLIENT_SYSTEM )
snd_opl3_synth_use_dec ( opl3 ) ;
return 0 ;
}
/*
* MIDI emulation operators
*/
2005-11-17 16:13:14 +03:00
struct snd_midi_op opl3_ops = {
2005-04-17 02:20:36 +04:00
. note_on = snd_opl3_note_on ,
. note_off = snd_opl3_note_off ,
. key_press = snd_opl3_key_press ,
. note_terminate = snd_opl3_terminate_note ,
. control = snd_opl3_control ,
. nrpn = snd_opl3_nrpn ,
. sysex = snd_opl3_sysex ,
} ;
2005-11-17 16:13:14 +03:00
static int snd_opl3_synth_event_input ( struct snd_seq_event * ev , int direct ,
2005-04-17 02:20:36 +04:00
void * private_data , int atomic , int hop )
{
2005-11-17 16:13:14 +03:00
struct snd_opl3 * opl3 = private_data ;
2005-04-17 02:20:36 +04:00
2007-10-30 13:49:22 +03:00
snd_midi_process_event ( & opl3_ops , ev , opl3 - > chset ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
/* ------------------------------ */
static void snd_opl3_synth_free_port ( void * private_data )
{
2005-11-17 16:13:14 +03:00
struct snd_opl3 * opl3 = private_data ;
2005-04-17 02:20:36 +04:00
snd_midi_channel_free_set ( opl3 - > chset ) ;
}
2005-11-17 16:13:14 +03:00
static int snd_opl3_synth_create_port ( struct snd_opl3 * opl3 )
2005-04-17 02:20:36 +04:00
{
2005-11-17 16:13:14 +03:00
struct snd_seq_port_callback callbacks ;
2005-04-17 02:20:36 +04:00
char name [ 32 ] ;
int voices , opl_ver ;
voices = ( opl3 - > hardware < OPL3_HW_OPL3 ) ?
MAX_OPL2_VOICES : MAX_OPL3_VOICES ;
opl3 - > chset = snd_midi_channel_alloc_set ( 16 ) ;
if ( opl3 - > chset = = NULL )
return - ENOMEM ;
opl3 - > chset - > private_data = opl3 ;
memset ( & callbacks , 0 , sizeof ( callbacks ) ) ;
callbacks . owner = THIS_MODULE ;
callbacks . use = snd_opl3_synth_use ;
callbacks . unuse = snd_opl3_synth_unuse ;
callbacks . event_input = snd_opl3_synth_event_input ;
callbacks . private_free = snd_opl3_synth_free_port ;
callbacks . private_data = opl3 ;
opl_ver = ( opl3 - > hardware & OPL3_HW_MASK ) > > 8 ;
sprintf ( name , " OPL%i FM Port " , opl_ver ) ;
opl3 - > chset - > client = opl3 - > seq_client ;
opl3 - > chset - > port = snd_seq_event_port_attach ( opl3 - > seq_client , & callbacks ,
SNDRV_SEQ_PORT_CAP_WRITE |
SNDRV_SEQ_PORT_CAP_SUBS_WRITE ,
SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC |
SNDRV_SEQ_PORT_TYPE_MIDI_GM |
2006-05-02 18:08:41 +04:00
SNDRV_SEQ_PORT_TYPE_DIRECT_SAMPLE |
SNDRV_SEQ_PORT_TYPE_HARDWARE |
SNDRV_SEQ_PORT_TYPE_SYNTHESIZER ,
2005-04-17 02:20:36 +04:00
16 , voices ,
name ) ;
if ( opl3 - > chset - > port < 0 ) {
2006-03-06 16:03:37 +03:00
int port ;
port = opl3 - > chset - > port ;
2005-04-17 02:20:36 +04:00
snd_midi_channel_free_set ( opl3 - > chset ) ;
2006-03-06 16:03:37 +03:00
return port ;
2005-04-17 02:20:36 +04:00
}
return 0 ;
}
/* ------------------------------ */
2005-11-17 16:13:14 +03:00
static int snd_opl3_seq_new_device ( struct snd_seq_device * dev )
2005-04-17 02:20:36 +04:00
{
2005-11-17 16:13:14 +03:00
struct snd_opl3 * opl3 ;
2006-03-06 16:03:37 +03:00
int client , err ;
2005-12-12 11:33:37 +03:00
char name [ 32 ] ;
2005-04-17 02:20:36 +04:00
int opl_ver ;
2005-11-17 16:13:14 +03:00
opl3 = * ( struct snd_opl3 * * ) SNDRV_SEQ_DEVICE_ARGPTR ( dev ) ;
2005-04-17 02:20:36 +04:00
if ( opl3 = = NULL )
return - EINVAL ;
spin_lock_init ( & opl3 - > voice_lock ) ;
opl3 - > seq_client = - 1 ;
/* allocate new client */
2005-12-12 11:33:37 +03:00
opl_ver = ( opl3 - > hardware & OPL3_HW_MASK ) > > 8 ;
sprintf ( name , " OPL%i FM synth " , opl_ver ) ;
2005-04-17 02:20:36 +04:00
client = opl3 - > seq_client =
2005-12-12 11:33:37 +03:00
snd_seq_create_kernel_client ( opl3 - > card , opl3 - > seq_dev_num ,
name ) ;
2005-04-17 02:20:36 +04:00
if ( client < 0 )
return client ;
2006-03-06 16:03:37 +03:00
if ( ( err = snd_opl3_synth_create_port ( opl3 ) ) < 0 ) {
snd_seq_delete_kernel_client ( client ) ;
opl3 - > seq_client = - 1 ;
return err ;
}
2005-04-17 02:20:36 +04:00
/* setup system timer */
init_timer ( & opl3 - > tlist ) ;
opl3 - > tlist . function = snd_opl3_timer_func ;
opl3 - > tlist . data = ( unsigned long ) opl3 ;
spin_lock_init ( & opl3 - > sys_timer_lock ) ;
opl3 - > sys_timer_status = 0 ;
# ifdef CONFIG_SND_SEQUENCER_OSS
2005-12-12 11:33:37 +03:00
snd_opl3_init_seq_oss ( opl3 , name ) ;
2005-04-17 02:20:36 +04:00
# endif
return 0 ;
}
2005-11-17 16:13:14 +03:00
static int snd_opl3_seq_delete_device ( struct snd_seq_device * dev )
2005-04-17 02:20:36 +04:00
{
2005-11-17 16:13:14 +03:00
struct snd_opl3 * opl3 ;
2005-04-17 02:20:36 +04:00
2005-11-17 16:13:14 +03:00
opl3 = * ( struct snd_opl3 * * ) SNDRV_SEQ_DEVICE_ARGPTR ( dev ) ;
2005-04-17 02:20:36 +04:00
if ( opl3 = = NULL )
return - EINVAL ;
# ifdef CONFIG_SND_SEQUENCER_OSS
snd_opl3_free_seq_oss ( opl3 ) ;
# endif
if ( opl3 - > seq_client > = 0 ) {
snd_seq_delete_kernel_client ( opl3 - > seq_client ) ;
opl3 - > seq_client = - 1 ;
}
return 0 ;
}
static int __init alsa_opl3_seq_init ( void )
{
2005-11-17 16:13:14 +03:00
static struct snd_seq_dev_ops ops =
2005-04-17 02:20:36 +04:00
{
snd_opl3_seq_new_device ,
snd_opl3_seq_delete_device
} ;
return snd_seq_device_register_driver ( SNDRV_SEQ_DEV_ID_OPL3 , & ops ,
2005-11-17 16:13:14 +03:00
sizeof ( struct snd_opl3 * ) ) ;
2005-04-17 02:20:36 +04:00
}
static void __exit alsa_opl3_seq_exit ( void )
{
snd_seq_device_unregister_driver ( SNDRV_SEQ_DEV_ID_OPL3 ) ;
}
module_init ( alsa_opl3_seq_init )
module_exit ( alsa_opl3_seq_exit )