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