0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016
0017
0018
0019
0020
0021
0022
0023
0024
0025
0026
0027
0028
0029
0030
0031
0032
0033
0034 #include "opl4_local.h"
0035 #include <linux/init.h>
0036 #include <linux/moduleparam.h>
0037 #include <linux/module.h>
0038 #include <sound/initval.h>
0039
0040 MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>");
0041 MODULE_DESCRIPTION("OPL4 wavetable synth driver");
0042 MODULE_LICENSE("Dual BSD/GPL");
0043
0044 int volume_boost = 8;
0045
0046 module_param(volume_boost, int, 0644);
0047 MODULE_PARM_DESC(volume_boost, "Additional volume for OPL4 wavetable sounds.");
0048
0049 static int snd_opl4_seq_use_inc(struct snd_opl4 *opl4)
0050 {
0051 if (!try_module_get(opl4->card->module))
0052 return -EFAULT;
0053 return 0;
0054 }
0055
0056 static void snd_opl4_seq_use_dec(struct snd_opl4 *opl4)
0057 {
0058 module_put(opl4->card->module);
0059 }
0060
0061 static int snd_opl4_seq_use(void *private_data, struct snd_seq_port_subscribe *info)
0062 {
0063 struct snd_opl4 *opl4 = private_data;
0064 int err;
0065
0066 mutex_lock(&opl4->access_mutex);
0067
0068 if (opl4->used) {
0069 mutex_unlock(&opl4->access_mutex);
0070 return -EBUSY;
0071 }
0072 opl4->used++;
0073
0074 if (info->sender.client != SNDRV_SEQ_CLIENT_SYSTEM) {
0075 err = snd_opl4_seq_use_inc(opl4);
0076 if (err < 0) {
0077 mutex_unlock(&opl4->access_mutex);
0078 return err;
0079 }
0080 }
0081
0082 mutex_unlock(&opl4->access_mutex);
0083
0084 snd_opl4_synth_reset(opl4);
0085 return 0;
0086 }
0087
0088 static int snd_opl4_seq_unuse(void *private_data, struct snd_seq_port_subscribe *info)
0089 {
0090 struct snd_opl4 *opl4 = private_data;
0091
0092 snd_opl4_synth_shutdown(opl4);
0093
0094 mutex_lock(&opl4->access_mutex);
0095 opl4->used--;
0096 mutex_unlock(&opl4->access_mutex);
0097
0098 if (info->sender.client != SNDRV_SEQ_CLIENT_SYSTEM)
0099 snd_opl4_seq_use_dec(opl4);
0100 return 0;
0101 }
0102
0103 static const struct snd_midi_op opl4_ops = {
0104 .note_on = snd_opl4_note_on,
0105 .note_off = snd_opl4_note_off,
0106 .note_terminate = snd_opl4_terminate_note,
0107 .control = snd_opl4_control,
0108 .sysex = snd_opl4_sysex,
0109 };
0110
0111 static int snd_opl4_seq_event_input(struct snd_seq_event *ev, int direct,
0112 void *private_data, int atomic, int hop)
0113 {
0114 struct snd_opl4 *opl4 = private_data;
0115
0116 snd_midi_process_event(&opl4_ops, ev, opl4->chset);
0117 return 0;
0118 }
0119
0120 static void snd_opl4_seq_free_port(void *private_data)
0121 {
0122 struct snd_opl4 *opl4 = private_data;
0123
0124 snd_midi_channel_free_set(opl4->chset);
0125 }
0126
0127 static int snd_opl4_seq_probe(struct device *_dev)
0128 {
0129 struct snd_seq_device *dev = to_seq_dev(_dev);
0130 struct snd_opl4 *opl4;
0131 int client;
0132 struct snd_seq_port_callback pcallbacks;
0133
0134 opl4 = *(struct snd_opl4 **)SNDRV_SEQ_DEVICE_ARGPTR(dev);
0135 if (!opl4)
0136 return -EINVAL;
0137
0138 if (snd_yrw801_detect(opl4) < 0)
0139 return -ENODEV;
0140
0141 opl4->chset = snd_midi_channel_alloc_set(16);
0142 if (!opl4->chset)
0143 return -ENOMEM;
0144 opl4->chset->private_data = opl4;
0145
0146
0147 client = snd_seq_create_kernel_client(opl4->card, opl4->seq_dev_num,
0148 "OPL4 Wavetable");
0149 if (client < 0) {
0150 snd_midi_channel_free_set(opl4->chset);
0151 return client;
0152 }
0153 opl4->seq_client = client;
0154 opl4->chset->client = client;
0155
0156
0157 memset(&pcallbacks, 0, sizeof(pcallbacks));
0158 pcallbacks.owner = THIS_MODULE;
0159 pcallbacks.use = snd_opl4_seq_use;
0160 pcallbacks.unuse = snd_opl4_seq_unuse;
0161 pcallbacks.event_input = snd_opl4_seq_event_input;
0162 pcallbacks.private_free = snd_opl4_seq_free_port;
0163 pcallbacks.private_data = opl4;
0164
0165 opl4->chset->port = snd_seq_event_port_attach(client, &pcallbacks,
0166 SNDRV_SEQ_PORT_CAP_WRITE |
0167 SNDRV_SEQ_PORT_CAP_SUBS_WRITE,
0168 SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC |
0169 SNDRV_SEQ_PORT_TYPE_MIDI_GM |
0170 SNDRV_SEQ_PORT_TYPE_HARDWARE |
0171 SNDRV_SEQ_PORT_TYPE_SYNTHESIZER,
0172 16, 24,
0173 "OPL4 Wavetable Port");
0174 if (opl4->chset->port < 0) {
0175 int err = opl4->chset->port;
0176 snd_midi_channel_free_set(opl4->chset);
0177 snd_seq_delete_kernel_client(client);
0178 opl4->seq_client = -1;
0179 return err;
0180 }
0181 return 0;
0182 }
0183
0184 static int snd_opl4_seq_remove(struct device *_dev)
0185 {
0186 struct snd_seq_device *dev = to_seq_dev(_dev);
0187 struct snd_opl4 *opl4;
0188
0189 opl4 = *(struct snd_opl4 **)SNDRV_SEQ_DEVICE_ARGPTR(dev);
0190 if (!opl4)
0191 return -EINVAL;
0192
0193 if (opl4->seq_client >= 0) {
0194 snd_seq_delete_kernel_client(opl4->seq_client);
0195 opl4->seq_client = -1;
0196 }
0197 return 0;
0198 }
0199
0200 static struct snd_seq_driver opl4_seq_driver = {
0201 .driver = {
0202 .name = KBUILD_MODNAME,
0203 .probe = snd_opl4_seq_probe,
0204 .remove = snd_opl4_seq_remove,
0205 },
0206 .id = SNDRV_SEQ_DEV_ID_OPL4,
0207 .argsize = sizeof(struct snd_opl4 *),
0208 };
0209
0210 module_snd_seq_driver(opl4_seq_driver);