1 /* 2 * Copyright (c) by Jaroslav Kysela <perex@perex.cz> 3 * Routines for the GF1 MIDI interface - like UART 6850 4 * 5 * 6 * This program is free software; you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License as published by 8 * the Free Software Foundation; either version 2 of the License, or 9 * (at your option) any later version. 10 * 11 * This program is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15 * 16 * You should have received a copy of the GNU General Public License 17 * along with this program; if not, write to the Free Software 18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 19 * 20 */ 21 22 #include <linux/delay.h> 23 #include <linux/interrupt.h> 24 #include <linux/time.h> 25 #include <sound/core.h> 26 #include <sound/gus.h> 27 28 static void snd_gf1_interrupt_midi_in(struct snd_gus_card * gus) 29 { 30 int count; 31 unsigned char stat, data, byte; 32 unsigned long flags; 33 34 count = 10; 35 while (count) { 36 spin_lock_irqsave(&gus->uart_cmd_lock, flags); 37 stat = snd_gf1_uart_stat(gus); 38 if (!(stat & 0x01)) { /* data in Rx FIFO? */ 39 spin_unlock_irqrestore(&gus->uart_cmd_lock, flags); 40 count--; 41 continue; 42 } 43 count = 100; /* arm counter to new value */ 44 data = snd_gf1_uart_get(gus); 45 if (!(gus->gf1.uart_cmd & 0x80)) { 46 spin_unlock_irqrestore(&gus->uart_cmd_lock, flags); 47 continue; 48 } 49 if (stat & 0x10) { /* framing error */ 50 gus->gf1.uart_framing++; 51 spin_unlock_irqrestore(&gus->uart_cmd_lock, flags); 52 continue; 53 } 54 byte = snd_gf1_uart_get(gus); 55 spin_unlock_irqrestore(&gus->uart_cmd_lock, flags); 56 snd_rawmidi_receive(gus->midi_substream_input, &byte, 1); 57 if (stat & 0x20) { 58 gus->gf1.uart_overrun++; 59 } 60 } 61 } 62 63 static void snd_gf1_interrupt_midi_out(struct snd_gus_card * gus) 64 { 65 char byte; 66 unsigned long flags; 67 68 /* try unlock output */ 69 if (snd_gf1_uart_stat(gus) & 0x01) 70 snd_gf1_interrupt_midi_in(gus); 71 72 spin_lock_irqsave(&gus->uart_cmd_lock, flags); 73 if (snd_gf1_uart_stat(gus) & 0x02) { /* Tx FIFO free? */ 74 if (snd_rawmidi_transmit(gus->midi_substream_output, &byte, 1) != 1) { /* no other bytes or error */ 75 snd_gf1_uart_cmd(gus, gus->gf1.uart_cmd & ~0x20); /* disable Tx interrupt */ 76 } else { 77 snd_gf1_uart_put(gus, byte); 78 } 79 } 80 spin_unlock_irqrestore(&gus->uart_cmd_lock, flags); 81 } 82 83 static void snd_gf1_uart_reset(struct snd_gus_card * gus, int close) 84 { 85 snd_gf1_uart_cmd(gus, 0x03); /* reset */ 86 if (!close && gus->uart_enable) { 87 udelay(160); 88 snd_gf1_uart_cmd(gus, 0x00); /* normal operations */ 89 } 90 } 91 92 static int snd_gf1_uart_output_open(struct snd_rawmidi_substream *substream) 93 { 94 unsigned long flags; 95 struct snd_gus_card *gus; 96 97 gus = substream->rmidi->private_data; 98 spin_lock_irqsave(&gus->uart_cmd_lock, flags); 99 if (!(gus->gf1.uart_cmd & 0x80)) { /* input active? */ 100 snd_gf1_uart_reset(gus, 0); 101 } 102 gus->gf1.interrupt_handler_midi_out = snd_gf1_interrupt_midi_out; 103 gus->midi_substream_output = substream; 104 spin_unlock_irqrestore(&gus->uart_cmd_lock, flags); 105 #if 0 106 snd_printk(KERN_DEBUG "write init - cmd = 0x%x, stat = 0x%x\n", gus->gf1.uart_cmd, snd_gf1_uart_stat(gus)); 107 #endif 108 return 0; 109 } 110 111 static int snd_gf1_uart_input_open(struct snd_rawmidi_substream *substream) 112 { 113 unsigned long flags; 114 struct snd_gus_card *gus; 115 int i; 116 117 gus = substream->rmidi->private_data; 118 spin_lock_irqsave(&gus->uart_cmd_lock, flags); 119 if (gus->gf1.interrupt_handler_midi_out != snd_gf1_interrupt_midi_out) { 120 snd_gf1_uart_reset(gus, 0); 121 } 122 gus->gf1.interrupt_handler_midi_in = snd_gf1_interrupt_midi_in; 123 gus->midi_substream_input = substream; 124 if (gus->uart_enable) { 125 for (i = 0; i < 1000 && (snd_gf1_uart_stat(gus) & 0x01); i++) 126 snd_gf1_uart_get(gus); /* clean Rx */ 127 if (i >= 1000) 128 snd_printk(KERN_ERR "gus midi uart init read - cleanup error\n"); 129 } 130 spin_unlock_irqrestore(&gus->uart_cmd_lock, flags); 131 #if 0 132 snd_printk("read init - enable = %i, cmd = 0x%x, stat = 0x%x\n", gus->uart_enable, gus->gf1.uart_cmd, snd_gf1_uart_stat(gus)); 133 snd_printk("[0x%x] reg (ctrl/status) = 0x%x, reg (data) = 0x%x (page = 0x%x)\n", gus->gf1.port + 0x100, inb(gus->gf1.port + 0x100), inb(gus->gf1.port + 0x101), inb(gus->gf1.port + 0x102)); 134 #endif 135 return 0; 136 } 137 138 static int snd_gf1_uart_output_close(struct snd_rawmidi_substream *substream) 139 { 140 unsigned long flags; 141 struct snd_gus_card *gus; 142 143 gus = substream->rmidi->private_data; 144 spin_lock_irqsave(&gus->uart_cmd_lock, flags); 145 if (gus->gf1.interrupt_handler_midi_in != snd_gf1_interrupt_midi_in) 146 snd_gf1_uart_reset(gus, 1); 147 snd_gf1_set_default_handlers(gus, SNDRV_GF1_HANDLER_MIDI_OUT); 148 gus->midi_substream_output = NULL; 149 spin_unlock_irqrestore(&gus->uart_cmd_lock, flags); 150 return 0; 151 } 152 153 static int snd_gf1_uart_input_close(struct snd_rawmidi_substream *substream) 154 { 155 unsigned long flags; 156 struct snd_gus_card *gus; 157 158 gus = substream->rmidi->private_data; 159 spin_lock_irqsave(&gus->uart_cmd_lock, flags); 160 if (gus->gf1.interrupt_handler_midi_out != snd_gf1_interrupt_midi_out) 161 snd_gf1_uart_reset(gus, 1); 162 snd_gf1_set_default_handlers(gus, SNDRV_GF1_HANDLER_MIDI_IN); 163 gus->midi_substream_input = NULL; 164 spin_unlock_irqrestore(&gus->uart_cmd_lock, flags); 165 return 0; 166 } 167 168 static void snd_gf1_uart_input_trigger(struct snd_rawmidi_substream *substream, int up) 169 { 170 struct snd_gus_card *gus; 171 unsigned long flags; 172 173 gus = substream->rmidi->private_data; 174 175 spin_lock_irqsave(&gus->uart_cmd_lock, flags); 176 if (up) { 177 if ((gus->gf1.uart_cmd & 0x80) == 0) 178 snd_gf1_uart_cmd(gus, gus->gf1.uart_cmd | 0x80); /* enable Rx interrupts */ 179 } else { 180 if (gus->gf1.uart_cmd & 0x80) 181 snd_gf1_uart_cmd(gus, gus->gf1.uart_cmd & ~0x80); /* disable Rx interrupts */ 182 } 183 spin_unlock_irqrestore(&gus->uart_cmd_lock, flags); 184 } 185 186 static void snd_gf1_uart_output_trigger(struct snd_rawmidi_substream *substream, int up) 187 { 188 unsigned long flags; 189 struct snd_gus_card *gus; 190 char byte; 191 int timeout; 192 193 gus = substream->rmidi->private_data; 194 195 spin_lock_irqsave(&gus->uart_cmd_lock, flags); 196 if (up) { 197 if ((gus->gf1.uart_cmd & 0x20) == 0) { 198 spin_unlock_irqrestore(&gus->uart_cmd_lock, flags); 199 /* wait for empty Rx - Tx is probably unlocked */ 200 timeout = 10000; 201 while (timeout-- > 0 && snd_gf1_uart_stat(gus) & 0x01); 202 /* Tx FIFO free? */ 203 spin_lock_irqsave(&gus->uart_cmd_lock, flags); 204 if (gus->gf1.uart_cmd & 0x20) { 205 spin_unlock_irqrestore(&gus->uart_cmd_lock, flags); 206 return; 207 } 208 if (snd_gf1_uart_stat(gus) & 0x02) { 209 if (snd_rawmidi_transmit(substream, &byte, 1) != 1) { 210 spin_unlock_irqrestore(&gus->uart_cmd_lock, flags); 211 return; 212 } 213 snd_gf1_uart_put(gus, byte); 214 } 215 snd_gf1_uart_cmd(gus, gus->gf1.uart_cmd | 0x20); /* enable Tx interrupt */ 216 } 217 } else { 218 if (gus->gf1.uart_cmd & 0x20) 219 snd_gf1_uart_cmd(gus, gus->gf1.uart_cmd & ~0x20); 220 } 221 spin_unlock_irqrestore(&gus->uart_cmd_lock, flags); 222 } 223 224 static struct snd_rawmidi_ops snd_gf1_uart_output = 225 { 226 .open = snd_gf1_uart_output_open, 227 .close = snd_gf1_uart_output_close, 228 .trigger = snd_gf1_uart_output_trigger, 229 }; 230 231 static struct snd_rawmidi_ops snd_gf1_uart_input = 232 { 233 .open = snd_gf1_uart_input_open, 234 .close = snd_gf1_uart_input_close, 235 .trigger = snd_gf1_uart_input_trigger, 236 }; 237 238 int snd_gf1_rawmidi_new(struct snd_gus_card * gus, int device, struct snd_rawmidi ** rrawmidi) 239 { 240 struct snd_rawmidi *rmidi; 241 int err; 242 243 if (rrawmidi) 244 *rrawmidi = NULL; 245 if ((err = snd_rawmidi_new(gus->card, "GF1", device, 1, 1, &rmidi)) < 0) 246 return err; 247 strcpy(rmidi->name, gus->interwave ? "AMD InterWave" : "GF1"); 248 snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_gf1_uart_output); 249 snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_gf1_uart_input); 250 rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT | SNDRV_RAWMIDI_INFO_INPUT | SNDRV_RAWMIDI_INFO_DUPLEX; 251 rmidi->private_data = gus; 252 gus->midi_uart = rmidi; 253 if (rrawmidi) 254 *rrawmidi = rmidi; 255 return err; 256 } 257