gus_uart.c 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234
  1. // SPDX-License-Identifier: GPL-2.0-or-later
  2. /*
  3. * Copyright (c) by Jaroslav Kysela <perex@perex.cz>
  4. * Routines for the GF1 MIDI interface - like UART 6850
  5. */
  6. #include <linux/delay.h>
  7. #include <linux/interrupt.h>
  8. #include <linux/time.h>
  9. #include <sound/core.h>
  10. #include <sound/gus.h>
  11. static void snd_gf1_interrupt_midi_in(struct snd_gus_card * gus)
  12. {
  13. int count;
  14. unsigned char stat, byte;
  15. __always_unused unsigned char data;
  16. unsigned long flags;
  17. count = 10;
  18. while (count) {
  19. spin_lock_irqsave(&gus->uart_cmd_lock, flags);
  20. stat = snd_gf1_uart_stat(gus);
  21. if (!(stat & 0x01)) { /* data in Rx FIFO? */
  22. spin_unlock_irqrestore(&gus->uart_cmd_lock, flags);
  23. count--;
  24. continue;
  25. }
  26. count = 100; /* arm counter to new value */
  27. data = snd_gf1_uart_get(gus);
  28. if (!(gus->gf1.uart_cmd & 0x80)) {
  29. spin_unlock_irqrestore(&gus->uart_cmd_lock, flags);
  30. continue;
  31. }
  32. if (stat & 0x10) { /* framing error */
  33. gus->gf1.uart_framing++;
  34. spin_unlock_irqrestore(&gus->uart_cmd_lock, flags);
  35. continue;
  36. }
  37. byte = snd_gf1_uart_get(gus);
  38. spin_unlock_irqrestore(&gus->uart_cmd_lock, flags);
  39. snd_rawmidi_receive(gus->midi_substream_input, &byte, 1);
  40. if (stat & 0x20) {
  41. gus->gf1.uart_overrun++;
  42. }
  43. }
  44. }
  45. static void snd_gf1_interrupt_midi_out(struct snd_gus_card * gus)
  46. {
  47. char byte;
  48. /* try unlock output */
  49. if (snd_gf1_uart_stat(gus) & 0x01)
  50. snd_gf1_interrupt_midi_in(gus);
  51. guard(spinlock_irqsave)(&gus->uart_cmd_lock);
  52. if (snd_gf1_uart_stat(gus) & 0x02) { /* Tx FIFO free? */
  53. if (snd_rawmidi_transmit(gus->midi_substream_output, &byte, 1) != 1) { /* no other bytes or error */
  54. snd_gf1_uart_cmd(gus, gus->gf1.uart_cmd & ~0x20); /* disable Tx interrupt */
  55. } else {
  56. snd_gf1_uart_put(gus, byte);
  57. }
  58. }
  59. }
  60. static void snd_gf1_uart_reset(struct snd_gus_card * gus, int close)
  61. {
  62. snd_gf1_uart_cmd(gus, 0x03); /* reset */
  63. if (!close && gus->uart_enable) {
  64. udelay(160);
  65. snd_gf1_uart_cmd(gus, 0x00); /* normal operations */
  66. }
  67. }
  68. static int snd_gf1_uart_output_open(struct snd_rawmidi_substream *substream)
  69. {
  70. struct snd_gus_card *gus;
  71. gus = substream->rmidi->private_data;
  72. guard(spinlock_irqsave)(&gus->uart_cmd_lock);
  73. if (!(gus->gf1.uart_cmd & 0x80)) { /* input active? */
  74. snd_gf1_uart_reset(gus, 0);
  75. }
  76. gus->gf1.interrupt_handler_midi_out = snd_gf1_interrupt_midi_out;
  77. gus->midi_substream_output = substream;
  78. #if 0
  79. dev_dbg(gus->card->dev,
  80. "write init - cmd = 0x%x, stat = 0x%x\n",
  81. gus->gf1.uart_cmd, snd_gf1_uart_stat(gus));
  82. #endif
  83. return 0;
  84. }
  85. static int snd_gf1_uart_input_open(struct snd_rawmidi_substream *substream)
  86. {
  87. struct snd_gus_card *gus;
  88. int i;
  89. gus = substream->rmidi->private_data;
  90. guard(spinlock_irqsave)(&gus->uart_cmd_lock);
  91. if (gus->gf1.interrupt_handler_midi_out != snd_gf1_interrupt_midi_out) {
  92. snd_gf1_uart_reset(gus, 0);
  93. }
  94. gus->gf1.interrupt_handler_midi_in = snd_gf1_interrupt_midi_in;
  95. gus->midi_substream_input = substream;
  96. if (gus->uart_enable) {
  97. for (i = 0; i < 1000 && (snd_gf1_uart_stat(gus) & 0x01); i++)
  98. snd_gf1_uart_get(gus); /* clean Rx */
  99. if (i >= 1000)
  100. dev_err(gus->card->dev, "gus midi uart init read - cleanup error\n");
  101. }
  102. #if 0
  103. dev_dbg(gus->card->dev,
  104. "read init - enable = %i, cmd = 0x%x, stat = 0x%x\n",
  105. gus->uart_enable, gus->gf1.uart_cmd, snd_gf1_uart_stat(gus));
  106. dev_dbg(gus->card->dev,
  107. "[0x%x] reg (ctrl/status) = 0x%x, reg (data) = 0x%x (page = 0x%x)\n",
  108. gus->gf1.port + 0x100, inb(gus->gf1.port + 0x100),
  109. inb(gus->gf1.port + 0x101), inb(gus->gf1.port + 0x102));
  110. #endif
  111. return 0;
  112. }
  113. static int snd_gf1_uart_output_close(struct snd_rawmidi_substream *substream)
  114. {
  115. struct snd_gus_card *gus;
  116. gus = substream->rmidi->private_data;
  117. guard(spinlock_irqsave)(&gus->uart_cmd_lock);
  118. if (gus->gf1.interrupt_handler_midi_in != snd_gf1_interrupt_midi_in)
  119. snd_gf1_uart_reset(gus, 1);
  120. snd_gf1_set_default_handlers(gus, SNDRV_GF1_HANDLER_MIDI_OUT);
  121. gus->midi_substream_output = NULL;
  122. return 0;
  123. }
  124. static int snd_gf1_uart_input_close(struct snd_rawmidi_substream *substream)
  125. {
  126. struct snd_gus_card *gus;
  127. gus = substream->rmidi->private_data;
  128. guard(spinlock_irqsave)(&gus->uart_cmd_lock);
  129. if (gus->gf1.interrupt_handler_midi_out != snd_gf1_interrupt_midi_out)
  130. snd_gf1_uart_reset(gus, 1);
  131. snd_gf1_set_default_handlers(gus, SNDRV_GF1_HANDLER_MIDI_IN);
  132. gus->midi_substream_input = NULL;
  133. return 0;
  134. }
  135. static void snd_gf1_uart_input_trigger(struct snd_rawmidi_substream *substream, int up)
  136. {
  137. struct snd_gus_card *gus;
  138. gus = substream->rmidi->private_data;
  139. guard(spinlock_irqsave)(&gus->uart_cmd_lock);
  140. if (up) {
  141. if ((gus->gf1.uart_cmd & 0x80) == 0)
  142. snd_gf1_uart_cmd(gus, gus->gf1.uart_cmd | 0x80); /* enable Rx interrupts */
  143. } else {
  144. if (gus->gf1.uart_cmd & 0x80)
  145. snd_gf1_uart_cmd(gus, gus->gf1.uart_cmd & ~0x80); /* disable Rx interrupts */
  146. }
  147. }
  148. static void snd_gf1_uart_output_trigger(struct snd_rawmidi_substream *substream, int up)
  149. {
  150. unsigned long flags;
  151. struct snd_gus_card *gus;
  152. char byte;
  153. int timeout;
  154. gus = substream->rmidi->private_data;
  155. spin_lock_irqsave(&gus->uart_cmd_lock, flags);
  156. if (up) {
  157. if ((gus->gf1.uart_cmd & 0x20) == 0) {
  158. spin_unlock_irqrestore(&gus->uart_cmd_lock, flags);
  159. /* wait for empty Rx - Tx is probably unlocked */
  160. timeout = 10000;
  161. while (timeout-- > 0 && snd_gf1_uart_stat(gus) & 0x01);
  162. /* Tx FIFO free? */
  163. spin_lock_irqsave(&gus->uart_cmd_lock, flags);
  164. if (gus->gf1.uart_cmd & 0x20) {
  165. spin_unlock_irqrestore(&gus->uart_cmd_lock, flags);
  166. return;
  167. }
  168. if (snd_gf1_uart_stat(gus) & 0x02) {
  169. if (snd_rawmidi_transmit(substream, &byte, 1) != 1) {
  170. spin_unlock_irqrestore(&gus->uart_cmd_lock, flags);
  171. return;
  172. }
  173. snd_gf1_uart_put(gus, byte);
  174. }
  175. snd_gf1_uart_cmd(gus, gus->gf1.uart_cmd | 0x20); /* enable Tx interrupt */
  176. }
  177. } else {
  178. if (gus->gf1.uart_cmd & 0x20)
  179. snd_gf1_uart_cmd(gus, gus->gf1.uart_cmd & ~0x20);
  180. }
  181. spin_unlock_irqrestore(&gus->uart_cmd_lock, flags);
  182. }
  183. static const struct snd_rawmidi_ops snd_gf1_uart_output =
  184. {
  185. .open = snd_gf1_uart_output_open,
  186. .close = snd_gf1_uart_output_close,
  187. .trigger = snd_gf1_uart_output_trigger,
  188. };
  189. static const struct snd_rawmidi_ops snd_gf1_uart_input =
  190. {
  191. .open = snd_gf1_uart_input_open,
  192. .close = snd_gf1_uart_input_close,
  193. .trigger = snd_gf1_uart_input_trigger,
  194. };
  195. int snd_gf1_rawmidi_new(struct snd_gus_card *gus, int device)
  196. {
  197. struct snd_rawmidi *rmidi;
  198. int err;
  199. err = snd_rawmidi_new(gus->card, "GF1", device, 1, 1, &rmidi);
  200. if (err < 0)
  201. return err;
  202. strscpy(rmidi->name, gus->interwave ? "AMD InterWave" : "GF1");
  203. snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_gf1_uart_output);
  204. snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_gf1_uart_input);
  205. rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT | SNDRV_RAWMIDI_INFO_INPUT | SNDRV_RAWMIDI_INFO_DUPLEX;
  206. rmidi->private_data = gus;
  207. gus->midi_uart = rmidi;
  208. return err;
  209. }