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