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 return 0; 106} 107 108static int snd_gf1_uart_input_open(struct snd_rawmidi_substream *substream) 109{ 110 unsigned long flags; 111 struct snd_gus_card *gus; 112 int i; 113 114 gus = substream->rmidi->private_data; 115 spin_lock_irqsave(&gus->uart_cmd_lock, flags); 116 if (gus->gf1.interrupt_handler_midi_out != snd_gf1_interrupt_midi_out) { 117 snd_gf1_uart_reset(gus, 0); 118 } 119 gus->gf1.interrupt_handler_midi_in = snd_gf1_interrupt_midi_in; 120 gus->midi_substream_input = substream; 121 if (gus->uart_enable) { 122 for (i = 0; i < 1000 && (snd_gf1_uart_stat(gus) & 0x01); i++) 123 snd_gf1_uart_get(gus); /* clean Rx */ 124 if (i >= 1000) 125 snd_printk(KERN_ERR "gus midi uart init read - cleanup error\n"); 126 } 127 spin_unlock_irqrestore(&gus->uart_cmd_lock, flags); 128 return 0; 129} 130 131static int snd_gf1_uart_output_close(struct snd_rawmidi_substream *substream) 132{ 133 unsigned long flags; 134 struct snd_gus_card *gus; 135 136 gus = substream->rmidi->private_data; 137 spin_lock_irqsave(&gus->uart_cmd_lock, flags); 138 if (gus->gf1.interrupt_handler_midi_in != snd_gf1_interrupt_midi_in) 139 snd_gf1_uart_reset(gus, 1); 140 snd_gf1_set_default_handlers(gus, SNDRV_GF1_HANDLER_MIDI_OUT); 141 gus->midi_substream_output = NULL; 142 spin_unlock_irqrestore(&gus->uart_cmd_lock, flags); 143 return 0; 144} 145 146static int snd_gf1_uart_input_close(struct snd_rawmidi_substream *substream) 147{ 148 unsigned long flags; 149 struct snd_gus_card *gus; 150 151 gus = substream->rmidi->private_data; 152 spin_lock_irqsave(&gus->uart_cmd_lock, flags); 153 if (gus->gf1.interrupt_handler_midi_out != snd_gf1_interrupt_midi_out) 154 snd_gf1_uart_reset(gus, 1); 155 snd_gf1_set_default_handlers(gus, SNDRV_GF1_HANDLER_MIDI_IN); 156 gus->midi_substream_input = NULL; 157 spin_unlock_irqrestore(&gus->uart_cmd_lock, flags); 158 return 0; 159} 160 161static void snd_gf1_uart_input_trigger(struct snd_rawmidi_substream *substream, int up) 162{ 163 struct snd_gus_card *gus; 164 unsigned long flags; 165 166 gus = substream->rmidi->private_data; 167 168 spin_lock_irqsave(&gus->uart_cmd_lock, flags); 169 if (up) { 170 if ((gus->gf1.uart_cmd & 0x80) == 0) 171 snd_gf1_uart_cmd(gus, gus->gf1.uart_cmd | 0x80); /* enable Rx interrupts */ 172 } else { 173 if (gus->gf1.uart_cmd & 0x80) 174 snd_gf1_uart_cmd(gus, gus->gf1.uart_cmd & ~0x80); /* disable Rx interrupts */ 175 } 176 spin_unlock_irqrestore(&gus->uart_cmd_lock, flags); 177} 178 179static void snd_gf1_uart_output_trigger(struct snd_rawmidi_substream *substream, int up) 180{ 181 unsigned long flags; 182 struct snd_gus_card *gus; 183 char byte; 184 int timeout; 185 186 gus = substream->rmidi->private_data; 187 188 spin_lock_irqsave(&gus->uart_cmd_lock, flags); 189 if (up) { 190 if ((gus->gf1.uart_cmd & 0x20) == 0) { 191 spin_unlock_irqrestore(&gus->uart_cmd_lock, flags); 192 /* wait for empty Rx - Tx is probably unlocked */ 193 timeout = 10000; 194 while (timeout-- > 0 && snd_gf1_uart_stat(gus) & 0x01); 195 /* Tx FIFO free? */ 196 spin_lock_irqsave(&gus->uart_cmd_lock, flags); 197 if (gus->gf1.uart_cmd & 0x20) { 198 spin_unlock_irqrestore(&gus->uart_cmd_lock, flags); 199 return; 200 } 201 if (snd_gf1_uart_stat(gus) & 0x02) { 202 if (snd_rawmidi_transmit(substream, &byte, 1) != 1) { 203 spin_unlock_irqrestore(&gus->uart_cmd_lock, flags); 204 return; 205 } 206 snd_gf1_uart_put(gus, byte); 207 } 208 snd_gf1_uart_cmd(gus, gus->gf1.uart_cmd | 0x20); /* enable Tx interrupt */ 209 } 210 } else { 211 if (gus->gf1.uart_cmd & 0x20) 212 snd_gf1_uart_cmd(gus, gus->gf1.uart_cmd & ~0x20); 213 } 214 spin_unlock_irqrestore(&gus->uart_cmd_lock, flags); 215} 216 217static struct snd_rawmidi_ops snd_gf1_uart_output = 218{ 219 .open = snd_gf1_uart_output_open, 220 .close = snd_gf1_uart_output_close, 221 .trigger = snd_gf1_uart_output_trigger, 222}; 223 224static struct snd_rawmidi_ops snd_gf1_uart_input = 225{ 226 .open = snd_gf1_uart_input_open, 227 .close = snd_gf1_uart_input_close, 228 .trigger = snd_gf1_uart_input_trigger, 229}; 230 231int snd_gf1_rawmidi_new(struct snd_gus_card * gus, int device, struct snd_rawmidi ** rrawmidi) 232{ 233 struct snd_rawmidi *rmidi; 234 int err; 235 236 if (rrawmidi) 237 *rrawmidi = NULL; 238 if ((err = snd_rawmidi_new(gus->card, "GF1", device, 1, 1, &rmidi)) < 0) 239 return err; 240 strcpy(rmidi->name, gus->interwave ? "AMD InterWave" : "GF1"); 241 snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_gf1_uart_output); 242 snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_gf1_uart_input); 243 rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT | SNDRV_RAWMIDI_INFO_INPUT | SNDRV_RAWMIDI_INFO_DUPLEX; 244 rmidi->private_data = gus; 245 gus->midi_uart = rmidi; 246 if (rrawmidi) 247 *rrawmidi = rmidi; 248 return err; 249} 250