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