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