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 28static 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 63static 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 83static 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 92static 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 111static 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(KERN_DEBUG 133 "read init - enable = %i, cmd = 0x%x, stat = 0x%x\n", 134 gus->uart_enable, gus->gf1.uart_cmd, snd_gf1_uart_stat(gus)); 135 snd_printk(KERN_DEBUG 136 "[0x%x] reg (ctrl/status) = 0x%x, reg (data) = 0x%x " 137 "(page = 0x%x)\n", 138 gus->gf1.port + 0x100, inb(gus->gf1.port + 0x100), 139 inb(gus->gf1.port + 0x101), inb(gus->gf1.port + 0x102)); 140#endif 141 return 0; 142} 143 144static int snd_gf1_uart_output_close(struct snd_rawmidi_substream *substream) 145{ 146 unsigned long flags; 147 struct snd_gus_card *gus; 148 149 gus = substream->rmidi->private_data; 150 spin_lock_irqsave(&gus->uart_cmd_lock, flags); 151 if (gus->gf1.interrupt_handler_midi_in != snd_gf1_interrupt_midi_in) 152 snd_gf1_uart_reset(gus, 1); 153 snd_gf1_set_default_handlers(gus, SNDRV_GF1_HANDLER_MIDI_OUT); 154 gus->midi_substream_output = NULL; 155 spin_unlock_irqrestore(&gus->uart_cmd_lock, flags); 156 return 0; 157} 158 159static int snd_gf1_uart_input_close(struct snd_rawmidi_substream *substream) 160{ 161 unsigned long flags; 162 struct snd_gus_card *gus; 163 164 gus = substream->rmidi->private_data; 165 spin_lock_irqsave(&gus->uart_cmd_lock, flags); 166 if (gus->gf1.interrupt_handler_midi_out != snd_gf1_interrupt_midi_out) 167 snd_gf1_uart_reset(gus, 1); 168 snd_gf1_set_default_handlers(gus, SNDRV_GF1_HANDLER_MIDI_IN); 169 gus->midi_substream_input = NULL; 170 spin_unlock_irqrestore(&gus->uart_cmd_lock, flags); 171 return 0; 172} 173 174static void snd_gf1_uart_input_trigger(struct snd_rawmidi_substream *substream, int up) 175{ 176 struct snd_gus_card *gus; 177 unsigned long flags; 178 179 gus = substream->rmidi->private_data; 180 181 spin_lock_irqsave(&gus->uart_cmd_lock, flags); 182 if (up) { 183 if ((gus->gf1.uart_cmd & 0x80) == 0) 184 snd_gf1_uart_cmd(gus, gus->gf1.uart_cmd | 0x80); /* enable Rx interrupts */ 185 } else { 186 if (gus->gf1.uart_cmd & 0x80) 187 snd_gf1_uart_cmd(gus, gus->gf1.uart_cmd & ~0x80); /* disable Rx interrupts */ 188 } 189 spin_unlock_irqrestore(&gus->uart_cmd_lock, flags); 190} 191 192static void snd_gf1_uart_output_trigger(struct snd_rawmidi_substream *substream, int up) 193{ 194 unsigned long flags; 195 struct snd_gus_card *gus; 196 char byte; 197 int timeout; 198 199 gus = substream->rmidi->private_data; 200 201 spin_lock_irqsave(&gus->uart_cmd_lock, flags); 202 if (up) { 203 if ((gus->gf1.uart_cmd & 0x20) == 0) { 204 spin_unlock_irqrestore(&gus->uart_cmd_lock, flags); 205 /* wait for empty Rx - Tx is probably unlocked */ 206 timeout = 10000; 207 while (timeout-- > 0 && snd_gf1_uart_stat(gus) & 0x01); 208 /* Tx FIFO free? */ 209 spin_lock_irqsave(&gus->uart_cmd_lock, flags); 210 if (gus->gf1.uart_cmd & 0x20) { 211 spin_unlock_irqrestore(&gus->uart_cmd_lock, flags); 212 return; 213 } 214 if (snd_gf1_uart_stat(gus) & 0x02) { 215 if (snd_rawmidi_transmit(substream, &byte, 1) != 1) { 216 spin_unlock_irqrestore(&gus->uart_cmd_lock, flags); 217 return; 218 } 219 snd_gf1_uart_put(gus, byte); 220 } 221 snd_gf1_uart_cmd(gus, gus->gf1.uart_cmd | 0x20); /* enable Tx interrupt */ 222 } 223 } else { 224 if (gus->gf1.uart_cmd & 0x20) 225 snd_gf1_uart_cmd(gus, gus->gf1.uart_cmd & ~0x20); 226 } 227 spin_unlock_irqrestore(&gus->uart_cmd_lock, flags); 228} 229 230static struct snd_rawmidi_ops snd_gf1_uart_output = 231{ 232 .open = snd_gf1_uart_output_open, 233 .close = snd_gf1_uart_output_close, 234 .trigger = snd_gf1_uart_output_trigger, 235}; 236 237static struct snd_rawmidi_ops snd_gf1_uart_input = 238{ 239 .open = snd_gf1_uart_input_open, 240 .close = snd_gf1_uart_input_close, 241 .trigger = snd_gf1_uart_input_trigger, 242}; 243 244int snd_gf1_rawmidi_new(struct snd_gus_card *gus, int device) 245{ 246 struct snd_rawmidi *rmidi; 247 int err; 248 249 if ((err = snd_rawmidi_new(gus->card, "GF1", device, 1, 1, &rmidi)) < 0) 250 return err; 251 strcpy(rmidi->name, gus->interwave ? "AMD InterWave" : "GF1"); 252 snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_gf1_uart_output); 253 snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_gf1_uart_input); 254 rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT | SNDRV_RAWMIDI_INFO_INPUT | SNDRV_RAWMIDI_INFO_DUPLEX; 255 rmidi->private_data = gus; 256 gus->midi_uart = rmidi; 257 return err; 258} 259