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