xref: /linux/sound/isa/gus/gus_uart.c (revision 05a54fa773284d1a7923cdfdd8f0c8dabb98bd26)
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