opl3_seq.c 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273
  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. #include "opl3_voice.h"
  11. #include <linux/init.h>
  12. #include <linux/moduleparam.h>
  13. #include <linux/module.h>
  14. #include <sound/initval.h>
  15. MODULE_AUTHOR("Uros Bizjak <uros@kss-loka.si>");
  16. MODULE_LICENSE("GPL");
  17. MODULE_DESCRIPTION("ALSA driver for OPL3 FM synth");
  18. bool use_internal_drums = 0;
  19. module_param(use_internal_drums, bool, 0444);
  20. MODULE_PARM_DESC(use_internal_drums, "Enable internal OPL2/3 drums.");
  21. int snd_opl3_synth_use_inc(struct snd_opl3 * opl3)
  22. {
  23. if (!try_module_get(opl3->card->module))
  24. return -EFAULT;
  25. return 0;
  26. }
  27. void snd_opl3_synth_use_dec(struct snd_opl3 * opl3)
  28. {
  29. module_put(opl3->card->module);
  30. }
  31. int snd_opl3_synth_setup(struct snd_opl3 * opl3)
  32. {
  33. int idx;
  34. struct snd_hwdep *hwdep = opl3->hwdep;
  35. scoped_guard(mutex, &hwdep->open_mutex) {
  36. if (hwdep->used)
  37. return -EBUSY;
  38. hwdep->used++;
  39. }
  40. snd_opl3_reset(opl3);
  41. for (idx = 0; idx < MAX_OPL3_VOICES; idx++) {
  42. opl3->voices[idx].state = SNDRV_OPL3_ST_OFF;
  43. opl3->voices[idx].time = 0;
  44. opl3->voices[idx].keyon_reg = 0x00;
  45. }
  46. opl3->use_time = 0;
  47. opl3->connection_reg = 0x00;
  48. if (opl3->hardware >= OPL3_HW_OPL3) {
  49. /* Clear 4-op connections */
  50. opl3->command(opl3, OPL3_RIGHT | OPL3_REG_CONNECTION_SELECT,
  51. opl3->connection_reg);
  52. opl3->max_voices = MAX_OPL3_VOICES;
  53. }
  54. return 0;
  55. }
  56. void snd_opl3_synth_cleanup(struct snd_opl3 * opl3)
  57. {
  58. struct snd_hwdep *hwdep;
  59. /* Stop system timer */
  60. scoped_guard(spinlock_irq, &opl3->sys_timer_lock) {
  61. if (opl3->sys_timer_status) {
  62. timer_delete(&opl3->tlist);
  63. opl3->sys_timer_status = 0;
  64. }
  65. }
  66. snd_opl3_reset(opl3);
  67. hwdep = opl3->hwdep;
  68. scoped_guard(mutex, &hwdep->open_mutex) {
  69. hwdep->used--;
  70. }
  71. wake_up(&hwdep->open_wait);
  72. }
  73. static int snd_opl3_synth_use(void *private_data, struct snd_seq_port_subscribe * info)
  74. {
  75. struct snd_opl3 *opl3 = private_data;
  76. int err;
  77. err = snd_opl3_synth_setup(opl3);
  78. if (err < 0)
  79. return err;
  80. if (use_internal_drums) {
  81. /* Percussion mode */
  82. opl3->voices[6].state = opl3->voices[7].state =
  83. opl3->voices[8].state = SNDRV_OPL3_ST_NOT_AVAIL;
  84. snd_opl3_load_drums(opl3);
  85. opl3->drum_reg = OPL3_PERCUSSION_ENABLE;
  86. opl3->command(opl3, OPL3_LEFT | OPL3_REG_PERCUSSION, opl3->drum_reg);
  87. } else {
  88. opl3->drum_reg = 0x00;
  89. }
  90. if (info->sender.client != SNDRV_SEQ_CLIENT_SYSTEM) {
  91. err = snd_opl3_synth_use_inc(opl3);
  92. if (err < 0)
  93. return err;
  94. }
  95. opl3->synth_mode = SNDRV_OPL3_MODE_SEQ;
  96. return 0;
  97. }
  98. static int snd_opl3_synth_unuse(void *private_data, struct snd_seq_port_subscribe * info)
  99. {
  100. struct snd_opl3 *opl3 = private_data;
  101. snd_opl3_synth_cleanup(opl3);
  102. if (info->sender.client != SNDRV_SEQ_CLIENT_SYSTEM)
  103. snd_opl3_synth_use_dec(opl3);
  104. return 0;
  105. }
  106. /*
  107. * MIDI emulation operators
  108. */
  109. const struct snd_midi_op opl3_ops = {
  110. .note_on = snd_opl3_note_on,
  111. .note_off = snd_opl3_note_off,
  112. .key_press = snd_opl3_key_press,
  113. .note_terminate = snd_opl3_terminate_note,
  114. .control = snd_opl3_control,
  115. .nrpn = snd_opl3_nrpn,
  116. .sysex = snd_opl3_sysex,
  117. };
  118. static int snd_opl3_synth_event_input(struct snd_seq_event * ev, int direct,
  119. void *private_data, int atomic, int hop)
  120. {
  121. struct snd_opl3 *opl3 = private_data;
  122. snd_midi_process_event(&opl3_ops, ev, opl3->chset);
  123. return 0;
  124. }
  125. /* ------------------------------ */
  126. static void snd_opl3_synth_free_port(void *private_data)
  127. {
  128. struct snd_opl3 *opl3 = private_data;
  129. snd_midi_channel_free_set(opl3->chset);
  130. }
  131. static int snd_opl3_synth_create_port(struct snd_opl3 * opl3)
  132. {
  133. struct snd_seq_port_callback callbacks;
  134. char name[32];
  135. int voices, opl_ver;
  136. voices = (opl3->hardware < OPL3_HW_OPL3) ?
  137. MAX_OPL2_VOICES : MAX_OPL3_VOICES;
  138. opl3->chset = snd_midi_channel_alloc_set(16);
  139. if (opl3->chset == NULL)
  140. return -ENOMEM;
  141. opl3->chset->private_data = opl3;
  142. memset(&callbacks, 0, sizeof(callbacks));
  143. callbacks.owner = THIS_MODULE;
  144. callbacks.use = snd_opl3_synth_use;
  145. callbacks.unuse = snd_opl3_synth_unuse;
  146. callbacks.event_input = snd_opl3_synth_event_input;
  147. callbacks.private_free = snd_opl3_synth_free_port;
  148. callbacks.private_data = opl3;
  149. opl_ver = (opl3->hardware & OPL3_HW_MASK) >> 8;
  150. sprintf(name, "OPL%i FM Port", opl_ver);
  151. opl3->chset->client = opl3->seq_client;
  152. opl3->chset->port = snd_seq_event_port_attach(opl3->seq_client, &callbacks,
  153. SNDRV_SEQ_PORT_CAP_WRITE |
  154. SNDRV_SEQ_PORT_CAP_SUBS_WRITE,
  155. SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC |
  156. SNDRV_SEQ_PORT_TYPE_MIDI_GM |
  157. SNDRV_SEQ_PORT_TYPE_DIRECT_SAMPLE |
  158. SNDRV_SEQ_PORT_TYPE_HARDWARE |
  159. SNDRV_SEQ_PORT_TYPE_SYNTHESIZER,
  160. 16, voices,
  161. name);
  162. if (opl3->chset->port < 0) {
  163. int port;
  164. port = opl3->chset->port;
  165. snd_midi_channel_free_set(opl3->chset);
  166. return port;
  167. }
  168. return 0;
  169. }
  170. /* ------------------------------ */
  171. static int snd_opl3_seq_probe(struct snd_seq_device *dev)
  172. {
  173. struct snd_opl3 *opl3;
  174. int client, err;
  175. char name[32];
  176. int opl_ver;
  177. opl3 = *(struct snd_opl3 **)SNDRV_SEQ_DEVICE_ARGPTR(dev);
  178. if (opl3 == NULL)
  179. return -EINVAL;
  180. spin_lock_init(&opl3->voice_lock);
  181. opl3->seq_client = -1;
  182. /* allocate new client */
  183. opl_ver = (opl3->hardware & OPL3_HW_MASK) >> 8;
  184. sprintf(name, "OPL%i FM synth", opl_ver);
  185. client = opl3->seq_client =
  186. snd_seq_create_kernel_client(opl3->card, opl3->seq_dev_num,
  187. name);
  188. if (client < 0)
  189. return client;
  190. err = snd_opl3_synth_create_port(opl3);
  191. if (err < 0) {
  192. snd_seq_delete_kernel_client(client);
  193. opl3->seq_client = -1;
  194. return err;
  195. }
  196. /* setup system timer */
  197. timer_setup(&opl3->tlist, snd_opl3_timer_func, 0);
  198. spin_lock_init(&opl3->sys_timer_lock);
  199. opl3->sys_timer_status = 0;
  200. #if IS_ENABLED(CONFIG_SND_SEQUENCER_OSS)
  201. snd_opl3_init_seq_oss(opl3, name);
  202. #endif
  203. return 0;
  204. }
  205. static void snd_opl3_seq_remove(struct snd_seq_device *dev)
  206. {
  207. struct snd_opl3 *opl3;
  208. opl3 = *(struct snd_opl3 **)SNDRV_SEQ_DEVICE_ARGPTR(dev);
  209. if (opl3 == NULL)
  210. return;
  211. #if IS_ENABLED(CONFIG_SND_SEQUENCER_OSS)
  212. snd_opl3_free_seq_oss(opl3);
  213. #endif
  214. if (opl3->seq_client >= 0) {
  215. snd_seq_delete_kernel_client(opl3->seq_client);
  216. opl3->seq_client = -1;
  217. }
  218. }
  219. static struct snd_seq_driver opl3_seq_driver = {
  220. .probe = snd_opl3_seq_probe,
  221. .remove = snd_opl3_seq_remove,
  222. .driver = {
  223. .name = KBUILD_MODNAME,
  224. },
  225. .id = SNDRV_SEQ_DEV_ID_OPL3,
  226. .argsize = sizeof(struct snd_opl3 *),
  227. };
  228. module_snd_seq_driver(opl3_seq_driver);