1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * Copyright (c) by Uros Bizjak <uros@kss-loka.si>
5 * Midi Sequencer interface routines for OPL2/OPL3/OPL4 FM
7 * OPL2/3 FM instrument loader:
8 * alsa-tools/seq/sbiload/
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>
17 MODULE_AUTHOR("Uros Bizjak <uros@kss-loka.si>");
18 MODULE_LICENSE("GPL");
19 MODULE_DESCRIPTION("ALSA driver for OPL3 FM synth");
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.");
25 int snd_opl3_synth_use_inc(struct snd_opl3
* opl3
)
27 if (!try_module_get(opl3
->card
->module
))
33 void snd_opl3_synth_use_dec(struct snd_opl3
* opl3
)
35 module_put(opl3
->card
->module
);
38 int snd_opl3_synth_setup(struct snd_opl3
* opl3
)
41 struct snd_hwdep
*hwdep
= opl3
->hwdep
;
43 mutex_lock(&hwdep
->open_mutex
);
45 mutex_unlock(&hwdep
->open_mutex
);
49 mutex_unlock(&hwdep
->open_mutex
);
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;
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
;
69 void snd_opl3_synth_cleanup(struct snd_opl3
* opl3
)
72 struct snd_hwdep
*hwdep
;
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;
80 spin_unlock_irqrestore(&opl3
->sys_timer_lock
, flags
);
84 mutex_lock(&hwdep
->open_mutex
);
86 mutex_unlock(&hwdep
->open_mutex
);
87 wake_up(&hwdep
->open_wait
);
90 static int snd_opl3_synth_use(void *private_data
, struct snd_seq_port_subscribe
* info
)
92 struct snd_opl3
*opl3
= private_data
;
95 if ((err
= snd_opl3_synth_setup(opl3
)) < 0)
98 if (use_internal_drums
) {
100 opl3
->voices
[6].state
= opl3
->voices
[7].state
=
101 opl3
->voices
[8].state
= SNDRV_OPL3_ST_NOT_AVAIL
;
102 snd_opl3_load_drums(opl3
);
103 opl3
->drum_reg
= OPL3_PERCUSSION_ENABLE
;
104 opl3
->command(opl3
, OPL3_LEFT
| OPL3_REG_PERCUSSION
, opl3
->drum_reg
);
106 opl3
->drum_reg
= 0x00;
109 if (info
->sender
.client
!= SNDRV_SEQ_CLIENT_SYSTEM
) {
110 if ((err
= snd_opl3_synth_use_inc(opl3
)) < 0)
113 opl3
->synth_mode
= SNDRV_OPL3_MODE_SEQ
;
117 static int snd_opl3_synth_unuse(void *private_data
, struct snd_seq_port_subscribe
* info
)
119 struct snd_opl3
*opl3
= private_data
;
121 snd_opl3_synth_cleanup(opl3
);
123 if (info
->sender
.client
!= SNDRV_SEQ_CLIENT_SYSTEM
)
124 snd_opl3_synth_use_dec(opl3
);
129 * MIDI emulation operators
131 const struct snd_midi_op opl3_ops
= {
132 .note_on
= snd_opl3_note_on
,
133 .note_off
= snd_opl3_note_off
,
134 .key_press
= snd_opl3_key_press
,
135 .note_terminate
= snd_opl3_terminate_note
,
136 .control
= snd_opl3_control
,
137 .nrpn
= snd_opl3_nrpn
,
138 .sysex
= snd_opl3_sysex
,
141 static int snd_opl3_synth_event_input(struct snd_seq_event
* ev
, int direct
,
142 void *private_data
, int atomic
, int hop
)
144 struct snd_opl3
*opl3
= private_data
;
146 snd_midi_process_event(&opl3_ops
, ev
, opl3
->chset
);
150 /* ------------------------------ */
152 static void snd_opl3_synth_free_port(void *private_data
)
154 struct snd_opl3
*opl3
= private_data
;
156 snd_midi_channel_free_set(opl3
->chset
);
159 static int snd_opl3_synth_create_port(struct snd_opl3
* opl3
)
161 struct snd_seq_port_callback callbacks
;
165 voices
= (opl3
->hardware
< OPL3_HW_OPL3
) ?
166 MAX_OPL2_VOICES
: MAX_OPL3_VOICES
;
167 opl3
->chset
= snd_midi_channel_alloc_set(16);
168 if (opl3
->chset
== NULL
)
170 opl3
->chset
->private_data
= opl3
;
172 memset(&callbacks
, 0, sizeof(callbacks
));
173 callbacks
.owner
= THIS_MODULE
;
174 callbacks
.use
= snd_opl3_synth_use
;
175 callbacks
.unuse
= snd_opl3_synth_unuse
;
176 callbacks
.event_input
= snd_opl3_synth_event_input
;
177 callbacks
.private_free
= snd_opl3_synth_free_port
;
178 callbacks
.private_data
= opl3
;
180 opl_ver
= (opl3
->hardware
& OPL3_HW_MASK
) >> 8;
181 sprintf(name
, "OPL%i FM Port", opl_ver
);
183 opl3
->chset
->client
= opl3
->seq_client
;
184 opl3
->chset
->port
= snd_seq_event_port_attach(opl3
->seq_client
, &callbacks
,
185 SNDRV_SEQ_PORT_CAP_WRITE
|
186 SNDRV_SEQ_PORT_CAP_SUBS_WRITE
,
187 SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC
|
188 SNDRV_SEQ_PORT_TYPE_MIDI_GM
|
189 SNDRV_SEQ_PORT_TYPE_DIRECT_SAMPLE
|
190 SNDRV_SEQ_PORT_TYPE_HARDWARE
|
191 SNDRV_SEQ_PORT_TYPE_SYNTHESIZER
,
194 if (opl3
->chset
->port
< 0) {
196 port
= opl3
->chset
->port
;
197 snd_midi_channel_free_set(opl3
->chset
);
203 /* ------------------------------ */
205 static int snd_opl3_seq_probe(struct device
*_dev
)
207 struct snd_seq_device
*dev
= to_seq_dev(_dev
);
208 struct snd_opl3
*opl3
;
213 opl3
= *(struct snd_opl3
**)SNDRV_SEQ_DEVICE_ARGPTR(dev
);
217 spin_lock_init(&opl3
->voice_lock
);
219 opl3
->seq_client
= -1;
221 /* allocate new client */
222 opl_ver
= (opl3
->hardware
& OPL3_HW_MASK
) >> 8;
223 sprintf(name
, "OPL%i FM synth", opl_ver
);
224 client
= opl3
->seq_client
=
225 snd_seq_create_kernel_client(opl3
->card
, opl3
->seq_dev_num
,
230 if ((err
= snd_opl3_synth_create_port(opl3
)) < 0) {
231 snd_seq_delete_kernel_client(client
);
232 opl3
->seq_client
= -1;
236 /* setup system timer */
237 timer_setup(&opl3
->tlist
, snd_opl3_timer_func
, 0);
238 spin_lock_init(&opl3
->sys_timer_lock
);
239 opl3
->sys_timer_status
= 0;
241 #if IS_ENABLED(CONFIG_SND_SEQUENCER_OSS)
242 snd_opl3_init_seq_oss(opl3
, name
);
247 static int snd_opl3_seq_remove(struct device
*_dev
)
249 struct snd_seq_device
*dev
= to_seq_dev(_dev
);
250 struct snd_opl3
*opl3
;
252 opl3
= *(struct snd_opl3
**)SNDRV_SEQ_DEVICE_ARGPTR(dev
);
256 #if IS_ENABLED(CONFIG_SND_SEQUENCER_OSS)
257 snd_opl3_free_seq_oss(opl3
);
259 if (opl3
->seq_client
>= 0) {
260 snd_seq_delete_kernel_client(opl3
->seq_client
);
261 opl3
->seq_client
= -1;
266 static struct snd_seq_driver opl3_seq_driver
= {
268 .name
= KBUILD_MODNAME
,
269 .probe
= snd_opl3_seq_probe
,
270 .remove
= snd_opl3_seq_remove
,
272 .id
= SNDRV_SEQ_DEV_ID_OPL3
,
273 .argsize
= sizeof(struct snd_opl3
*),
276 module_snd_seq_driver(opl3_seq_driver
);