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