linux/sound/drivers/opl3/opl3_seq.c
<<
>>
Prefs
   1/*
   2 *  Copyright (c) by Uros Bizjak <uros@kss-loka.si>
   3 *
   4 *  Midi Sequencer interface routines for OPL2/OPL3/OPL4 FM
   5 *
   6 *  OPL2/3 FM instrument loader:
   7 *   alsa-tools/seq/sbiload/
   8 *
   9 *   This program is free software; you can redistribute it and/or modify
  10 *   it under the terms of the GNU General Public License as published by
  11 *   the Free Software Foundation; either version 2 of the License, or
  12 *   (at your option) any later version.
  13 *
  14 *   This program is distributed in the hope that it will be useful,
  15 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
  16 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  17 *   GNU General Public License for more details.
  18 *
  19 *   You should have received a copy of the GNU General Public License
  20 *   along with this program; if not, write to the Free Software
  21 *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
  22 *
  23 */
  24
  25#include "opl3_voice.h"
  26#include <linux/init.h>
  27#include <linux/moduleparam.h>
  28#include <sound/initval.h>
  29
  30MODULE_AUTHOR("Uros Bizjak <uros@kss-loka.si>");
  31MODULE_LICENSE("GPL");
  32MODULE_DESCRIPTION("ALSA driver for OPL3 FM synth");
  33
  34int use_internal_drums = 0;
  35module_param(use_internal_drums, bool, 0444);
  36MODULE_PARM_DESC(use_internal_drums, "Enable internal OPL2/3 drums.");
  37
  38int snd_opl3_synth_use_inc(struct snd_opl3 * opl3)
  39{
  40        if (!try_module_get(opl3->card->module))
  41                return -EFAULT;
  42        return 0;
  43
  44}
  45
  46void snd_opl3_synth_use_dec(struct snd_opl3 * opl3)
  47{
  48        module_put(opl3->card->module);
  49}
  50
  51int snd_opl3_synth_setup(struct snd_opl3 * opl3)
  52{
  53        int idx;
  54        struct snd_hwdep *hwdep = opl3->hwdep;
  55
  56        mutex_lock(&hwdep->open_mutex);
  57        if (hwdep->used) {
  58                mutex_unlock(&hwdep->open_mutex);
  59                return -EBUSY;
  60        }
  61        hwdep->used++;
  62        mutex_unlock(&hwdep->open_mutex);
  63
  64        snd_opl3_reset(opl3);
  65
  66        for (idx = 0; idx < MAX_OPL3_VOICES; idx++) {
  67                opl3->voices[idx].state = SNDRV_OPL3_ST_OFF;
  68                opl3->voices[idx].time = 0;
  69                opl3->voices[idx].keyon_reg = 0x00;
  70        }
  71        opl3->use_time = 0;
  72        opl3->connection_reg = 0x00;
  73        if (opl3->hardware >= OPL3_HW_OPL3) {
  74                /* Clear 4-op connections */
  75                opl3->command(opl3, OPL3_RIGHT | OPL3_REG_CONNECTION_SELECT,
  76                                 opl3->connection_reg);
  77                opl3->max_voices = MAX_OPL3_VOICES;
  78        }
  79        return 0;
  80}
  81
  82void snd_opl3_synth_cleanup(struct snd_opl3 * opl3)
  83{
  84        unsigned long flags;
  85        struct snd_hwdep *hwdep;
  86
  87        /* Stop system timer */
  88        spin_lock_irqsave(&opl3->sys_timer_lock, flags);
  89        if (opl3->sys_timer_status) {
  90                del_timer(&opl3->tlist);
  91                opl3->sys_timer_status = 0;
  92        }
  93        spin_unlock_irqrestore(&opl3->sys_timer_lock, flags);
  94
  95        snd_opl3_reset(opl3);
  96        hwdep = opl3->hwdep;
  97        mutex_lock(&hwdep->open_mutex);
  98        hwdep->used--;
  99        mutex_unlock(&hwdep->open_mutex);
 100        wake_up(&hwdep->open_wait);
 101}
 102
 103static int snd_opl3_synth_use(void *private_data, struct snd_seq_port_subscribe * info)
 104{
 105        struct snd_opl3 *opl3 = private_data;
 106        int err;
 107
 108        if ((err = snd_opl3_synth_setup(opl3)) < 0)
 109                return err;
 110
 111        if (use_internal_drums) {
 112                /* Percussion mode */
 113                opl3->voices[6].state = opl3->voices[7].state = 
 114                        opl3->voices[8].state = SNDRV_OPL3_ST_NOT_AVAIL;
 115                snd_opl3_load_drums(opl3);
 116                opl3->drum_reg = OPL3_PERCUSSION_ENABLE;
 117                opl3->command(opl3, OPL3_LEFT | OPL3_REG_PERCUSSION, opl3->drum_reg);
 118        } else {
 119                opl3->drum_reg = 0x00;
 120        }
 121
 122        if (info->sender.client != SNDRV_SEQ_CLIENT_SYSTEM) {
 123                if ((err = snd_opl3_synth_use_inc(opl3)) < 0)
 124                        return err;
 125        }
 126        opl3->synth_mode = SNDRV_OPL3_MODE_SEQ;
 127        return 0;
 128}
 129
 130static int snd_opl3_synth_unuse(void *private_data, struct snd_seq_port_subscribe * info)
 131{
 132        struct snd_opl3 *opl3 = private_data;
 133
 134        snd_opl3_synth_cleanup(opl3);
 135
 136        if (info->sender.client != SNDRV_SEQ_CLIENT_SYSTEM)
 137                snd_opl3_synth_use_dec(opl3);
 138        return 0;
 139}
 140
 141/*
 142 * MIDI emulation operators
 143 */
 144struct snd_midi_op opl3_ops = {
 145        .note_on =              snd_opl3_note_on,
 146        .note_off =             snd_opl3_note_off,
 147        .key_press =            snd_opl3_key_press,
 148        .note_terminate =       snd_opl3_terminate_note,
 149        .control =              snd_opl3_control,
 150        .nrpn =                 snd_opl3_nrpn,
 151        .sysex =                snd_opl3_sysex,
 152};
 153
 154static int snd_opl3_synth_event_input(struct snd_seq_event * ev, int direct,
 155                                      void *private_data, int atomic, int hop)
 156{
 157        struct snd_opl3 *opl3 = private_data;
 158
 159        snd_midi_process_event(&opl3_ops, ev, opl3->chset);
 160        return 0;
 161}
 162
 163/* ------------------------------ */
 164
 165static void snd_opl3_synth_free_port(void *private_data)
 166{
 167        struct snd_opl3 *opl3 = private_data;
 168
 169        snd_midi_channel_free_set(opl3->chset);
 170}
 171
 172static int snd_opl3_synth_create_port(struct snd_opl3 * opl3)
 173{
 174        struct snd_seq_port_callback callbacks;
 175        char name[32];
 176        int voices, opl_ver;
 177
 178        voices = (opl3->hardware < OPL3_HW_OPL3) ?
 179                MAX_OPL2_VOICES : MAX_OPL3_VOICES;
 180        opl3->chset = snd_midi_channel_alloc_set(16);
 181        if (opl3->chset == NULL)
 182                return -ENOMEM;
 183        opl3->chset->private_data = opl3;
 184
 185        memset(&callbacks, 0, sizeof(callbacks));
 186        callbacks.owner = THIS_MODULE;
 187        callbacks.use = snd_opl3_synth_use;
 188        callbacks.unuse = snd_opl3_synth_unuse;
 189        callbacks.event_input = snd_opl3_synth_event_input;
 190        callbacks.private_free = snd_opl3_synth_free_port;
 191        callbacks.private_data = opl3;
 192
 193        opl_ver = (opl3->hardware & OPL3_HW_MASK) >> 8;
 194        sprintf(name, "OPL%i FM Port", opl_ver);
 195
 196        opl3->chset->client = opl3->seq_client;
 197        opl3->chset->port = snd_seq_event_port_attach(opl3->seq_client, &callbacks,
 198                                                      SNDRV_SEQ_PORT_CAP_WRITE |
 199                                                      SNDRV_SEQ_PORT_CAP_SUBS_WRITE,
 200                                                      SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC |
 201                                                      SNDRV_SEQ_PORT_TYPE_MIDI_GM |
 202                                                      SNDRV_SEQ_PORT_TYPE_DIRECT_SAMPLE |
 203                                                      SNDRV_SEQ_PORT_TYPE_HARDWARE |
 204                                                      SNDRV_SEQ_PORT_TYPE_SYNTHESIZER,
 205                                                      16, voices,
 206                                                      name);
 207        if (opl3->chset->port < 0) {
 208                int port;
 209                port = opl3->chset->port;
 210                snd_midi_channel_free_set(opl3->chset);
 211                return port;
 212        }
 213        return 0;
 214}
 215
 216/* ------------------------------ */
 217
 218static int snd_opl3_seq_new_device(struct snd_seq_device *dev)
 219{
 220        struct snd_opl3 *opl3;
 221        int client, err;
 222        char name[32];
 223        int opl_ver;
 224
 225        opl3 = *(struct snd_opl3 **)SNDRV_SEQ_DEVICE_ARGPTR(dev);
 226        if (opl3 == NULL)
 227                return -EINVAL;
 228
 229        spin_lock_init(&opl3->voice_lock);
 230
 231        opl3->seq_client = -1;
 232
 233        /* allocate new client */
 234        opl_ver = (opl3->hardware & OPL3_HW_MASK) >> 8;
 235        sprintf(name, "OPL%i FM synth", opl_ver);
 236        client = opl3->seq_client =
 237                snd_seq_create_kernel_client(opl3->card, opl3->seq_dev_num,
 238                                             name);
 239        if (client < 0)
 240                return client;
 241
 242        if ((err = snd_opl3_synth_create_port(opl3)) < 0) {
 243                snd_seq_delete_kernel_client(client);
 244                opl3->seq_client = -1;
 245                return err;
 246        }
 247
 248        /* setup system timer */
 249        init_timer(&opl3->tlist);
 250        opl3->tlist.function = snd_opl3_timer_func;
 251        opl3->tlist.data = (unsigned long) opl3;
 252        spin_lock_init(&opl3->sys_timer_lock);
 253        opl3->sys_timer_status = 0;
 254
 255#ifdef CONFIG_SND_SEQUENCER_OSS
 256        snd_opl3_init_seq_oss(opl3, name);
 257#endif
 258        return 0;
 259}
 260
 261static int snd_opl3_seq_delete_device(struct snd_seq_device *dev)
 262{
 263        struct snd_opl3 *opl3;
 264
 265        opl3 = *(struct snd_opl3 **)SNDRV_SEQ_DEVICE_ARGPTR(dev);
 266        if (opl3 == NULL)
 267                return -EINVAL;
 268
 269#ifdef CONFIG_SND_SEQUENCER_OSS
 270        snd_opl3_free_seq_oss(opl3);
 271#endif
 272        if (opl3->seq_client >= 0) {
 273                snd_seq_delete_kernel_client(opl3->seq_client);
 274                opl3->seq_client = -1;
 275        }
 276        return 0;
 277}
 278
 279static int __init alsa_opl3_seq_init(void)
 280{
 281        static struct snd_seq_dev_ops ops =
 282        {
 283                snd_opl3_seq_new_device,
 284                snd_opl3_seq_delete_device
 285        };
 286
 287        return snd_seq_device_register_driver(SNDRV_SEQ_DEV_ID_OPL3, &ops,
 288                                              sizeof(struct snd_opl3 *));
 289}
 290
 291static void __exit alsa_opl3_seq_exit(void)
 292{
 293        snd_seq_device_unregister_driver(SNDRV_SEQ_DEV_ID_OPL3);
 294}
 295
 296module_init(alsa_opl3_seq_init)
 297module_exit(alsa_opl3_seq_exit)
 298