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
snd_gf1_interrupt_midi_in(struct snd_gus_card * gus)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
snd_gf1_interrupt_midi_out(struct snd_gus_card * gus)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
snd_gf1_uart_reset(struct snd_gus_card * gus,int close)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
snd_gf1_uart_output_open(struct snd_rawmidi_substream * substream)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
snd_gf1_uart_input_open(struct snd_rawmidi_substream * substream)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
snd_gf1_uart_output_close(struct snd_rawmidi_substream * substream)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
snd_gf1_uart_input_close(struct snd_rawmidi_substream * substream)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
snd_gf1_uart_input_trigger(struct snd_rawmidi_substream * substream,int up)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
snd_gf1_uart_output_trigger(struct snd_rawmidi_substream * substream,int up)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
snd_gf1_rawmidi_new(struct snd_gus_card * gus,int device)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