xref: /linux/sound/isa/gus/gus_uart.c (revision 2f27fce67173bbb05d5a0ee03dae5c021202c912)
11a59d1b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
21da177e4SLinus Torvalds /*
3c1017a4cSJaroslav Kysela  *  Copyright (c) by Jaroslav Kysela <perex@perex.cz>
41da177e4SLinus Torvalds  *  Routines for the GF1 MIDI interface - like UART 6850
51da177e4SLinus Torvalds  */
61da177e4SLinus Torvalds 
71da177e4SLinus Torvalds #include <linux/delay.h>
81da177e4SLinus Torvalds #include <linux/interrupt.h>
91da177e4SLinus Torvalds #include <linux/time.h>
101da177e4SLinus Torvalds #include <sound/core.h>
111da177e4SLinus Torvalds #include <sound/gus.h>
121da177e4SLinus Torvalds 
135e2da206STakashi Iwai static void snd_gf1_interrupt_midi_in(struct snd_gus_card * gus)
141da177e4SLinus Torvalds {
151da177e4SLinus Torvalds 	int count;
166a4f2b69SPierre-Louis Bossart 	unsigned char stat, byte;
176a4f2b69SPierre-Louis Bossart 	__always_unused unsigned char data;
181da177e4SLinus Torvalds 	unsigned long flags;
191da177e4SLinus Torvalds 
201da177e4SLinus Torvalds 	count = 10;
211da177e4SLinus Torvalds 	while (count) {
221da177e4SLinus Torvalds 		spin_lock_irqsave(&gus->uart_cmd_lock, flags);
231da177e4SLinus Torvalds 		stat = snd_gf1_uart_stat(gus);
241da177e4SLinus Torvalds 		if (!(stat & 0x01)) {	/* data in Rx FIFO? */
251da177e4SLinus Torvalds 			spin_unlock_irqrestore(&gus->uart_cmd_lock, flags);
261da177e4SLinus Torvalds 			count--;
271da177e4SLinus Torvalds 			continue;
281da177e4SLinus Torvalds 		}
291da177e4SLinus Torvalds 		count = 100;	/* arm counter to new value */
301da177e4SLinus Torvalds 		data = snd_gf1_uart_get(gus);
311da177e4SLinus Torvalds 		if (!(gus->gf1.uart_cmd & 0x80)) {
321da177e4SLinus Torvalds 			spin_unlock_irqrestore(&gus->uart_cmd_lock, flags);
331da177e4SLinus Torvalds 			continue;
341da177e4SLinus Torvalds 		}
351da177e4SLinus Torvalds 		if (stat & 0x10) {	/* framing error */
361da177e4SLinus Torvalds 			gus->gf1.uart_framing++;
371da177e4SLinus Torvalds 			spin_unlock_irqrestore(&gus->uart_cmd_lock, flags);
381da177e4SLinus Torvalds 			continue;
391da177e4SLinus Torvalds 		}
401da177e4SLinus Torvalds 		byte = snd_gf1_uart_get(gus);
411da177e4SLinus Torvalds 		spin_unlock_irqrestore(&gus->uart_cmd_lock, flags);
421da177e4SLinus Torvalds 		snd_rawmidi_receive(gus->midi_substream_input, &byte, 1);
431da177e4SLinus Torvalds 		if (stat & 0x20) {
441da177e4SLinus Torvalds 			gus->gf1.uart_overrun++;
451da177e4SLinus Torvalds 		}
461da177e4SLinus Torvalds 	}
471da177e4SLinus Torvalds }
481da177e4SLinus Torvalds 
495e2da206STakashi Iwai static void snd_gf1_interrupt_midi_out(struct snd_gus_card * gus)
501da177e4SLinus Torvalds {
511da177e4SLinus Torvalds 	char byte;
521da177e4SLinus Torvalds 	unsigned long flags;
531da177e4SLinus Torvalds 
541da177e4SLinus Torvalds 	/* try unlock output */
551da177e4SLinus Torvalds 	if (snd_gf1_uart_stat(gus) & 0x01)
561da177e4SLinus Torvalds 		snd_gf1_interrupt_midi_in(gus);
571da177e4SLinus Torvalds 
581da177e4SLinus Torvalds 	spin_lock_irqsave(&gus->uart_cmd_lock, flags);
591da177e4SLinus Torvalds 	if (snd_gf1_uart_stat(gus) & 0x02) {	/* Tx FIFO free? */
601da177e4SLinus Torvalds 		if (snd_rawmidi_transmit(gus->midi_substream_output, &byte, 1) != 1) {	/* no other bytes or error */
611da177e4SLinus Torvalds 			snd_gf1_uart_cmd(gus, gus->gf1.uart_cmd & ~0x20); /* disable Tx interrupt */
621da177e4SLinus Torvalds 		} else {
631da177e4SLinus Torvalds 			snd_gf1_uart_put(gus, byte);
641da177e4SLinus Torvalds 		}
651da177e4SLinus Torvalds 	}
661da177e4SLinus Torvalds 	spin_unlock_irqrestore(&gus->uart_cmd_lock, flags);
671da177e4SLinus Torvalds }
681da177e4SLinus Torvalds 
695e2da206STakashi Iwai static void snd_gf1_uart_reset(struct snd_gus_card * gus, int close)
701da177e4SLinus Torvalds {
711da177e4SLinus Torvalds 	snd_gf1_uart_cmd(gus, 0x03);	/* reset */
721da177e4SLinus Torvalds 	if (!close && gus->uart_enable) {
731da177e4SLinus Torvalds 		udelay(160);
741da177e4SLinus Torvalds 		snd_gf1_uart_cmd(gus, 0x00);	/* normal operations */
751da177e4SLinus Torvalds 	}
761da177e4SLinus Torvalds }
771da177e4SLinus Torvalds 
785e2da206STakashi Iwai static int snd_gf1_uart_output_open(struct snd_rawmidi_substream *substream)
791da177e4SLinus Torvalds {
801da177e4SLinus Torvalds 	unsigned long flags;
815e2da206STakashi Iwai 	struct snd_gus_card *gus;
821da177e4SLinus Torvalds 
831da177e4SLinus Torvalds 	gus = substream->rmidi->private_data;
841da177e4SLinus Torvalds 	spin_lock_irqsave(&gus->uart_cmd_lock, flags);
851da177e4SLinus Torvalds 	if (!(gus->gf1.uart_cmd & 0x80)) {	/* input active? */
861da177e4SLinus Torvalds 		snd_gf1_uart_reset(gus, 0);
871da177e4SLinus Torvalds 	}
881da177e4SLinus Torvalds 	gus->gf1.interrupt_handler_midi_out = snd_gf1_interrupt_midi_out;
891da177e4SLinus Torvalds 	gus->midi_substream_output = substream;
901da177e4SLinus Torvalds 	spin_unlock_irqrestore(&gus->uart_cmd_lock, flags);
911da177e4SLinus Torvalds #if 0
92*a6676811STakashi Iwai 	dev_dbg(gus->card->dev,
93*a6676811STakashi Iwai 		"write init - cmd = 0x%x, stat = 0x%x\n",
94*a6676811STakashi Iwai 		gus->gf1.uart_cmd, snd_gf1_uart_stat(gus));
951da177e4SLinus Torvalds #endif
961da177e4SLinus Torvalds 	return 0;
971da177e4SLinus Torvalds }
981da177e4SLinus Torvalds 
995e2da206STakashi Iwai static int snd_gf1_uart_input_open(struct snd_rawmidi_substream *substream)
1001da177e4SLinus Torvalds {
1011da177e4SLinus Torvalds 	unsigned long flags;
1025e2da206STakashi Iwai 	struct snd_gus_card *gus;
1031da177e4SLinus Torvalds 	int i;
1041da177e4SLinus Torvalds 
1051da177e4SLinus Torvalds 	gus = substream->rmidi->private_data;
1061da177e4SLinus Torvalds 	spin_lock_irqsave(&gus->uart_cmd_lock, flags);
1071da177e4SLinus Torvalds 	if (gus->gf1.interrupt_handler_midi_out != snd_gf1_interrupt_midi_out) {
1081da177e4SLinus Torvalds 		snd_gf1_uart_reset(gus, 0);
1091da177e4SLinus Torvalds 	}
1101da177e4SLinus Torvalds 	gus->gf1.interrupt_handler_midi_in = snd_gf1_interrupt_midi_in;
1111da177e4SLinus Torvalds 	gus->midi_substream_input = substream;
1121da177e4SLinus Torvalds 	if (gus->uart_enable) {
1131da177e4SLinus Torvalds 		for (i = 0; i < 1000 && (snd_gf1_uart_stat(gus) & 0x01); i++)
1141da177e4SLinus Torvalds 			snd_gf1_uart_get(gus);	/* clean Rx */
1151da177e4SLinus Torvalds 		if (i >= 1000)
116*a6676811STakashi Iwai 			dev_err(gus->card->dev, "gus midi uart init read - cleanup error\n");
1171da177e4SLinus Torvalds 	}
1181da177e4SLinus Torvalds 	spin_unlock_irqrestore(&gus->uart_cmd_lock, flags);
1191da177e4SLinus Torvalds #if 0
120*a6676811STakashi Iwai 	dev_dbg(gus->card->dev,
12191f05060STakashi Iwai 		"read init - enable = %i, cmd = 0x%x, stat = 0x%x\n",
12291f05060STakashi Iwai 		gus->uart_enable, gus->gf1.uart_cmd, snd_gf1_uart_stat(gus));
123*a6676811STakashi Iwai 	dev_dbg(gus->card->dev,
124*a6676811STakashi Iwai 		"[0x%x] reg (ctrl/status) = 0x%x, reg (data) = 0x%x (page = 0x%x)\n",
12591f05060STakashi Iwai 		gus->gf1.port + 0x100, inb(gus->gf1.port + 0x100),
12691f05060STakashi Iwai 		inb(gus->gf1.port + 0x101), inb(gus->gf1.port + 0x102));
1271da177e4SLinus Torvalds #endif
1281da177e4SLinus Torvalds 	return 0;
1291da177e4SLinus Torvalds }
1301da177e4SLinus Torvalds 
1315e2da206STakashi Iwai static int snd_gf1_uart_output_close(struct snd_rawmidi_substream *substream)
1321da177e4SLinus Torvalds {
1331da177e4SLinus Torvalds 	unsigned long flags;
1345e2da206STakashi Iwai 	struct snd_gus_card *gus;
1351da177e4SLinus Torvalds 
1361da177e4SLinus Torvalds 	gus = substream->rmidi->private_data;
1371da177e4SLinus Torvalds 	spin_lock_irqsave(&gus->uart_cmd_lock, flags);
1381da177e4SLinus Torvalds 	if (gus->gf1.interrupt_handler_midi_in != snd_gf1_interrupt_midi_in)
1391da177e4SLinus Torvalds 		snd_gf1_uart_reset(gus, 1);
1401da177e4SLinus Torvalds 	snd_gf1_set_default_handlers(gus, SNDRV_GF1_HANDLER_MIDI_OUT);
1411da177e4SLinus Torvalds 	gus->midi_substream_output = NULL;
1421da177e4SLinus Torvalds 	spin_unlock_irqrestore(&gus->uart_cmd_lock, flags);
1431da177e4SLinus Torvalds 	return 0;
1441da177e4SLinus Torvalds }
1451da177e4SLinus Torvalds 
1465e2da206STakashi Iwai static int snd_gf1_uart_input_close(struct snd_rawmidi_substream *substream)
1471da177e4SLinus Torvalds {
1481da177e4SLinus Torvalds 	unsigned long flags;
1495e2da206STakashi Iwai 	struct snd_gus_card *gus;
1501da177e4SLinus Torvalds 
1511da177e4SLinus Torvalds 	gus = substream->rmidi->private_data;
1521da177e4SLinus Torvalds 	spin_lock_irqsave(&gus->uart_cmd_lock, flags);
1531da177e4SLinus Torvalds 	if (gus->gf1.interrupt_handler_midi_out != snd_gf1_interrupt_midi_out)
1541da177e4SLinus Torvalds 		snd_gf1_uart_reset(gus, 1);
1551da177e4SLinus Torvalds 	snd_gf1_set_default_handlers(gus, SNDRV_GF1_HANDLER_MIDI_IN);
1561da177e4SLinus Torvalds 	gus->midi_substream_input = NULL;
1571da177e4SLinus Torvalds 	spin_unlock_irqrestore(&gus->uart_cmd_lock, flags);
1581da177e4SLinus Torvalds 	return 0;
1591da177e4SLinus Torvalds }
1601da177e4SLinus Torvalds 
1615e2da206STakashi Iwai static void snd_gf1_uart_input_trigger(struct snd_rawmidi_substream *substream, int up)
1621da177e4SLinus Torvalds {
1635e2da206STakashi Iwai 	struct snd_gus_card *gus;
1641da177e4SLinus Torvalds 	unsigned long flags;
1651da177e4SLinus Torvalds 
1661da177e4SLinus Torvalds 	gus = substream->rmidi->private_data;
1671da177e4SLinus Torvalds 
1681da177e4SLinus Torvalds 	spin_lock_irqsave(&gus->uart_cmd_lock, flags);
1691da177e4SLinus Torvalds 	if (up) {
1701da177e4SLinus Torvalds 		if ((gus->gf1.uart_cmd & 0x80) == 0)
1711da177e4SLinus Torvalds 			snd_gf1_uart_cmd(gus, gus->gf1.uart_cmd | 0x80); /* enable Rx interrupts */
1721da177e4SLinus Torvalds 	} else {
1731da177e4SLinus Torvalds 		if (gus->gf1.uart_cmd & 0x80)
1741da177e4SLinus Torvalds 			snd_gf1_uart_cmd(gus, gus->gf1.uart_cmd & ~0x80); /* disable Rx interrupts */
1751da177e4SLinus Torvalds 	}
1761da177e4SLinus Torvalds 	spin_unlock_irqrestore(&gus->uart_cmd_lock, flags);
1771da177e4SLinus Torvalds }
1781da177e4SLinus Torvalds 
1795e2da206STakashi Iwai static void snd_gf1_uart_output_trigger(struct snd_rawmidi_substream *substream, int up)
1801da177e4SLinus Torvalds {
1811da177e4SLinus Torvalds 	unsigned long flags;
1825e2da206STakashi Iwai 	struct snd_gus_card *gus;
1831da177e4SLinus Torvalds 	char byte;
1841da177e4SLinus Torvalds 	int timeout;
1851da177e4SLinus Torvalds 
1861da177e4SLinus Torvalds 	gus = substream->rmidi->private_data;
1871da177e4SLinus Torvalds 
1881da177e4SLinus Torvalds 	spin_lock_irqsave(&gus->uart_cmd_lock, flags);
1891da177e4SLinus Torvalds 	if (up) {
1901da177e4SLinus Torvalds 		if ((gus->gf1.uart_cmd & 0x20) == 0) {
1911da177e4SLinus Torvalds 			spin_unlock_irqrestore(&gus->uart_cmd_lock, flags);
1921da177e4SLinus Torvalds 			/* wait for empty Rx - Tx is probably unlocked */
1931da177e4SLinus Torvalds 			timeout = 10000;
1941da177e4SLinus Torvalds 			while (timeout-- > 0 && snd_gf1_uart_stat(gus) & 0x01);
1951da177e4SLinus Torvalds 			/* Tx FIFO free? */
1961da177e4SLinus Torvalds 			spin_lock_irqsave(&gus->uart_cmd_lock, flags);
1971da177e4SLinus Torvalds 			if (gus->gf1.uart_cmd & 0x20) {
1981da177e4SLinus Torvalds 				spin_unlock_irqrestore(&gus->uart_cmd_lock, flags);
1991da177e4SLinus Torvalds 				return;
2001da177e4SLinus Torvalds 			}
2011da177e4SLinus Torvalds 			if (snd_gf1_uart_stat(gus) & 0x02) {
2021da177e4SLinus Torvalds 				if (snd_rawmidi_transmit(substream, &byte, 1) != 1) {
2031da177e4SLinus Torvalds 					spin_unlock_irqrestore(&gus->uart_cmd_lock, flags);
2041da177e4SLinus Torvalds 					return;
2051da177e4SLinus Torvalds 				}
2061da177e4SLinus Torvalds 				snd_gf1_uart_put(gus, byte);
2071da177e4SLinus Torvalds 			}
2081da177e4SLinus Torvalds 			snd_gf1_uart_cmd(gus, gus->gf1.uart_cmd | 0x20);	/* enable Tx interrupt */
2091da177e4SLinus Torvalds 		}
2101da177e4SLinus Torvalds 	} else {
2111da177e4SLinus Torvalds 		if (gus->gf1.uart_cmd & 0x20)
2121da177e4SLinus Torvalds 			snd_gf1_uart_cmd(gus, gus->gf1.uart_cmd & ~0x20);
2131da177e4SLinus Torvalds 	}
2141da177e4SLinus Torvalds 	spin_unlock_irqrestore(&gus->uart_cmd_lock, flags);
2151da177e4SLinus Torvalds }
2161da177e4SLinus Torvalds 
2179021b2b8STakashi Iwai static const struct snd_rawmidi_ops snd_gf1_uart_output =
2181da177e4SLinus Torvalds {
2191da177e4SLinus Torvalds 	.open =		snd_gf1_uart_output_open,
2201da177e4SLinus Torvalds 	.close =	snd_gf1_uart_output_close,
2211da177e4SLinus Torvalds 	.trigger =	snd_gf1_uart_output_trigger,
2221da177e4SLinus Torvalds };
2231da177e4SLinus Torvalds 
2249021b2b8STakashi Iwai static const struct snd_rawmidi_ops snd_gf1_uart_input =
2251da177e4SLinus Torvalds {
2261da177e4SLinus Torvalds 	.open =		snd_gf1_uart_input_open,
2271da177e4SLinus Torvalds 	.close =	snd_gf1_uart_input_close,
2281da177e4SLinus Torvalds 	.trigger =	snd_gf1_uart_input_trigger,
2291da177e4SLinus Torvalds };
2301da177e4SLinus Torvalds 
231db5abb3cSLars-Peter Clausen int snd_gf1_rawmidi_new(struct snd_gus_card *gus, int device)
2321da177e4SLinus Torvalds {
2335e2da206STakashi Iwai 	struct snd_rawmidi *rmidi;
2341da177e4SLinus Torvalds 	int err;
2351da177e4SLinus Torvalds 
236310efd3aSTakashi Iwai 	err = snd_rawmidi_new(gus->card, "GF1", device, 1, 1, &rmidi);
237310efd3aSTakashi Iwai 	if (err < 0)
2381da177e4SLinus Torvalds 		return err;
2391da177e4SLinus Torvalds 	strcpy(rmidi->name, gus->interwave ? "AMD InterWave" : "GF1");
2401da177e4SLinus Torvalds 	snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_gf1_uart_output);
2411da177e4SLinus Torvalds 	snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_gf1_uart_input);
2421da177e4SLinus Torvalds 	rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT | SNDRV_RAWMIDI_INFO_INPUT | SNDRV_RAWMIDI_INFO_DUPLEX;
2431da177e4SLinus Torvalds 	rmidi->private_data = gus;
2441da177e4SLinus Torvalds 	gus->midi_uart = rmidi;
2451da177e4SLinus Torvalds 	return err;
2461da177e4SLinus Torvalds }
247