File Index
Files and directories
Sign in
FILE

gus_uart.c

File preview with syntax highlighting.

linux-6.19.14/sound/isa/gus/gus_uart.c C
  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
 13static 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
 49static 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
 67static 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
 76static 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
 95static 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
125static 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
138static 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
151static 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
167static 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
205static 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
212static 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
219int 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}
~/ Read-only file preview.