1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * Copyright (c) by Uros Bizjak <uros@kss-loka.si> 4 * 5 * Midi Sequencer interface routines for OPL2/OPL3/OPL4 FM 6 * 7 * OPL2/3 FM instrument loader: 8 * alsa-tools/seq/sbiload/ 9 */ 10 11 #include "opl3_voice.h" 12 #include <linux/init.h> 13 #include <linux/moduleparam.h> 14 #include <linux/module.h> 15 #include <sound/initval.h> 16 17 MODULE_AUTHOR("Uros Bizjak <uros@kss-loka.si>"); 18 MODULE_LICENSE("GPL"); 19 MODULE_DESCRIPTION("ALSA driver for OPL3 FM synth"); 20 21 bool use_internal_drums = 0; 22 module_param(use_internal_drums, bool, 0444); 23 MODULE_PARM_DESC(use_internal_drums, "Enable internal OPL2/3 drums."); 24 25 int snd_opl3_synth_use_inc(struct snd_opl3 * opl3) 26 { 27 if (!try_module_get(opl3->card->module)) 28 return -EFAULT; 29 return 0; 30 31 } 32 33 void snd_opl3_synth_use_dec(struct snd_opl3 * opl3) 34 { 35 module_put(opl3->card->module); 36 } 37 38 int snd_opl3_synth_setup(struct snd_opl3 * opl3) 39 { 40 int idx; 41 struct snd_hwdep *hwdep = opl3->hwdep; 42 43 mutex_lock(&hwdep->open_mutex); 44 if (hwdep->used) { 45 mutex_unlock(&hwdep->open_mutex); 46 return -EBUSY; 47 } 48 hwdep->used++; 49 mutex_unlock(&hwdep->open_mutex); 50 51 snd_opl3_reset(opl3); 52 53 for (idx = 0; idx < MAX_OPL3_VOICES; idx++) { 54 opl3->voices[idx].state = SNDRV_OPL3_ST_OFF; 55 opl3->voices[idx].time = 0; 56 opl3->voices[idx].keyon_reg = 0x00; 57 } 58 opl3->use_time = 0; 59 opl3->connection_reg = 0x00; 60 if (opl3->hardware >= OPL3_HW_OPL3) { 61 /* Clear 4-op connections */ 62 opl3->command(opl3, OPL3_RIGHT | OPL3_REG_CONNECTION_SELECT, 63 opl3->connection_reg); 64 opl3->max_voices = MAX_OPL3_VOICES; 65 } 66 return 0; 67 } 68 69 void snd_opl3_synth_cleanup(struct snd_opl3 * opl3) 70 { 71 unsigned long flags; 72 struct snd_hwdep *hwdep; 73 74 /* Stop system timer */ 75 spin_lock_irqsave(&opl3->sys_timer_lock, flags); 76 if (opl3->sys_timer_status) { 77 del_timer(&opl3->tlist); 78 opl3->sys_timer_status = 0; 79 } 80 spin_unlock_irqrestore(&opl3->sys_timer_lock, flags); 81 82 snd_opl3_reset(opl3); 83 hwdep = opl3->hwdep; 84 mutex_lock(&hwdep->open_mutex); 85 hwdep->used--; 86 mutex_unlock(&hwdep->open_mutex); 87 wake_up(&hwdep->open_wait); 88 } 89 90 static int snd_opl3_synth_use(void *private_data, struct snd_seq_port_subscribe * info) 91 { 92 struct snd_opl3 *opl3 = private_data; 93 int err; 94 95 err = snd_opl3_synth_setup(opl3); 96 if (err < 0) 97 return err; 98 99 if (use_internal_drums) { 100 /* Percussion mode */ 101 opl3->voices[6].state = opl3->voices[7].state = 102 opl3->voices[8].state = SNDRV_OPL3_ST_NOT_AVAIL; 103 snd_opl3_load_drums(opl3); 104 opl3->drum_reg = OPL3_PERCUSSION_ENABLE; 105 opl3->command(opl3, OPL3_LEFT | OPL3_REG_PERCUSSION, opl3->drum_reg); 106 } else { 107 opl3->drum_reg = 0x00; 108 } 109 110 if (info->sender.client != SNDRV_SEQ_CLIENT_SYSTEM) { 111 err = snd_opl3_synth_use_inc(opl3); 112 if (err < 0) 113 return err; 114 } 115 opl3->synth_mode = SNDRV_OPL3_MODE_SEQ; 116 return 0; 117 } 118 119 static int snd_opl3_synth_unuse(void *private_data, struct snd_seq_port_subscribe * info) 120 { 121 struct snd_opl3 *opl3 = private_data; 122 123 snd_opl3_synth_cleanup(opl3); 124 125 if (info->sender.client != SNDRV_SEQ_CLIENT_SYSTEM) 126 snd_opl3_synth_use_dec(opl3); 127 return 0; 128 } 129 130 /* 131 * MIDI emulation operators 132 */ 133 const struct snd_midi_op opl3_ops = { 134 .note_on = snd_opl3_note_on, 135 .note_off = snd_opl3_note_off, 136 .key_press = snd_opl3_key_press, 137 .note_terminate = snd_opl3_terminate_note, 138 .control = snd_opl3_control, 139 .nrpn = snd_opl3_nrpn, 140 .sysex = snd_opl3_sysex, 141 }; 142 143 static int snd_opl3_synth_event_input(struct snd_seq_event * ev, int direct, 144 void *private_data, int atomic, int hop) 145 { 146 struct snd_opl3 *opl3 = private_data; 147 148 snd_midi_process_event(&opl3_ops, ev, opl3->chset); 149 return 0; 150 } 151 152 /* ------------------------------ */ 153 154 static void snd_opl3_synth_free_port(void *private_data) 155 { 156 struct snd_opl3 *opl3 = private_data; 157 158 snd_midi_channel_free_set(opl3->chset); 159 } 160 161 static int snd_opl3_synth_create_port(struct snd_opl3 * opl3) 162 { 163 struct snd_seq_port_callback callbacks; 164 char name[32]; 165 int voices, opl_ver; 166 167 voices = (opl3->hardware < OPL3_HW_OPL3) ? 168 MAX_OPL2_VOICES : MAX_OPL3_VOICES; 169 opl3->chset = snd_midi_channel_alloc_set(16); 170 if (opl3->chset == NULL) 171 return -ENOMEM; 172 opl3->chset->private_data = opl3; 173 174 memset(&callbacks, 0, sizeof(callbacks)); 175 callbacks.owner = THIS_MODULE; 176 callbacks.use = snd_opl3_synth_use; 177 callbacks.unuse = snd_opl3_synth_unuse; 178 callbacks.event_input = snd_opl3_synth_event_input; 179 callbacks.private_free = snd_opl3_synth_free_port; 180 callbacks.private_data = opl3; 181 182 opl_ver = (opl3->hardware & OPL3_HW_MASK) >> 8; 183 sprintf(name, "OPL%i FM Port", opl_ver); 184 185 opl3->chset->client = opl3->seq_client; 186 opl3->chset->port = snd_seq_event_port_attach(opl3->seq_client, &callbacks, 187 SNDRV_SEQ_PORT_CAP_WRITE | 188 SNDRV_SEQ_PORT_CAP_SUBS_WRITE, 189 SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC | 190 SNDRV_SEQ_PORT_TYPE_MIDI_GM | 191 SNDRV_SEQ_PORT_TYPE_DIRECT_SAMPLE | 192 SNDRV_SEQ_PORT_TYPE_HARDWARE | 193 SNDRV_SEQ_PORT_TYPE_SYNTHESIZER, 194 16, voices, 195 name); 196 if (opl3->chset->port < 0) { 197 int port; 198 port = opl3->chset->port; 199 snd_midi_channel_free_set(opl3->chset); 200 return port; 201 } 202 return 0; 203 } 204 205 /* ------------------------------ */ 206 207 static int snd_opl3_seq_probe(struct device *_dev) 208 { 209 struct snd_seq_device *dev = to_seq_dev(_dev); 210 struct snd_opl3 *opl3; 211 int client, err; 212 char name[32]; 213 int opl_ver; 214 215 opl3 = *(struct snd_opl3 **)SNDRV_SEQ_DEVICE_ARGPTR(dev); 216 if (opl3 == NULL) 217 return -EINVAL; 218 219 spin_lock_init(&opl3->voice_lock); 220 221 opl3->seq_client = -1; 222 223 /* allocate new client */ 224 opl_ver = (opl3->hardware & OPL3_HW_MASK) >> 8; 225 sprintf(name, "OPL%i FM synth", opl_ver); 226 client = opl3->seq_client = 227 snd_seq_create_kernel_client(opl3->card, opl3->seq_dev_num, 228 name); 229 if (client < 0) 230 return client; 231 232 err = snd_opl3_synth_create_port(opl3); 233 if (err < 0) { 234 snd_seq_delete_kernel_client(client); 235 opl3->seq_client = -1; 236 return err; 237 } 238 239 /* setup system timer */ 240 timer_setup(&opl3->tlist, snd_opl3_timer_func, 0); 241 spin_lock_init(&opl3->sys_timer_lock); 242 opl3->sys_timer_status = 0; 243 244 #if IS_ENABLED(CONFIG_SND_SEQUENCER_OSS) 245 snd_opl3_init_seq_oss(opl3, name); 246 #endif 247 return 0; 248 } 249 250 static int snd_opl3_seq_remove(struct device *_dev) 251 { 252 struct snd_seq_device *dev = to_seq_dev(_dev); 253 struct snd_opl3 *opl3; 254 255 opl3 = *(struct snd_opl3 **)SNDRV_SEQ_DEVICE_ARGPTR(dev); 256 if (opl3 == NULL) 257 return -EINVAL; 258 259 #if IS_ENABLED(CONFIG_SND_SEQUENCER_OSS) 260 snd_opl3_free_seq_oss(opl3); 261 #endif 262 if (opl3->seq_client >= 0) { 263 snd_seq_delete_kernel_client(opl3->seq_client); 264 opl3->seq_client = -1; 265 } 266 return 0; 267 } 268 269 static struct snd_seq_driver opl3_seq_driver = { 270 .driver = { 271 .name = KBUILD_MODNAME, 272 .probe = snd_opl3_seq_probe, 273 .remove = snd_opl3_seq_remove, 274 }, 275 .id = SNDRV_SEQ_DEV_ID_OPL3, 276 .argsize = sizeof(struct snd_opl3 *), 277 }; 278 279 module_snd_seq_driver(opl3_seq_driver); 280
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.