xref: /linux/sound/isa/gus/gus_uart.c (revision 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2)
1*1da177e4SLinus Torvalds /*
2*1da177e4SLinus Torvalds  *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
3*1da177e4SLinus Torvalds  *  Routines for the GF1 MIDI interface - like UART 6850
4*1da177e4SLinus Torvalds  *
5*1da177e4SLinus Torvalds  *
6*1da177e4SLinus Torvalds  *   This program is free software; you can redistribute it and/or modify
7*1da177e4SLinus Torvalds  *   it under the terms of the GNU General Public License as published by
8*1da177e4SLinus Torvalds  *   the Free Software Foundation; either version 2 of the License, or
9*1da177e4SLinus Torvalds  *   (at your option) any later version.
10*1da177e4SLinus Torvalds  *
11*1da177e4SLinus Torvalds  *   This program is distributed in the hope that it will be useful,
12*1da177e4SLinus Torvalds  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
13*1da177e4SLinus Torvalds  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14*1da177e4SLinus Torvalds  *   GNU General Public License for more details.
15*1da177e4SLinus Torvalds  *
16*1da177e4SLinus Torvalds  *   You should have received a copy of the GNU General Public License
17*1da177e4SLinus Torvalds  *   along with this program; if not, write to the Free Software
18*1da177e4SLinus Torvalds  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
19*1da177e4SLinus Torvalds  *
20*1da177e4SLinus Torvalds  */
21*1da177e4SLinus Torvalds 
22*1da177e4SLinus Torvalds #include <sound/driver.h>
23*1da177e4SLinus Torvalds #include <linux/delay.h>
24*1da177e4SLinus Torvalds #include <linux/interrupt.h>
25*1da177e4SLinus Torvalds #include <linux/time.h>
26*1da177e4SLinus Torvalds #include <sound/core.h>
27*1da177e4SLinus Torvalds #include <sound/gus.h>
28*1da177e4SLinus Torvalds 
29*1da177e4SLinus Torvalds static void snd_gf1_interrupt_midi_in(snd_gus_card_t * gus)
30*1da177e4SLinus Torvalds {
31*1da177e4SLinus Torvalds 	int count;
32*1da177e4SLinus Torvalds 	unsigned char stat, data, byte;
33*1da177e4SLinus Torvalds 	unsigned long flags;
34*1da177e4SLinus Torvalds 
35*1da177e4SLinus Torvalds 	count = 10;
36*1da177e4SLinus Torvalds 	while (count) {
37*1da177e4SLinus Torvalds 		spin_lock_irqsave(&gus->uart_cmd_lock, flags);
38*1da177e4SLinus Torvalds 		stat = snd_gf1_uart_stat(gus);
39*1da177e4SLinus Torvalds 		if (!(stat & 0x01)) {	/* data in Rx FIFO? */
40*1da177e4SLinus Torvalds 			spin_unlock_irqrestore(&gus->uart_cmd_lock, flags);
41*1da177e4SLinus Torvalds 			count--;
42*1da177e4SLinus Torvalds 			continue;
43*1da177e4SLinus Torvalds 		}
44*1da177e4SLinus Torvalds 		count = 100;	/* arm counter to new value */
45*1da177e4SLinus Torvalds 		data = snd_gf1_uart_get(gus);
46*1da177e4SLinus Torvalds 		if (!(gus->gf1.uart_cmd & 0x80)) {
47*1da177e4SLinus Torvalds 			spin_unlock_irqrestore(&gus->uart_cmd_lock, flags);
48*1da177e4SLinus Torvalds 			continue;
49*1da177e4SLinus Torvalds 		}
50*1da177e4SLinus Torvalds 		if (stat & 0x10) {	/* framing error */
51*1da177e4SLinus Torvalds 			gus->gf1.uart_framing++;
52*1da177e4SLinus Torvalds 			spin_unlock_irqrestore(&gus->uart_cmd_lock, flags);
53*1da177e4SLinus Torvalds 			continue;
54*1da177e4SLinus Torvalds 		}
55*1da177e4SLinus Torvalds 		byte = snd_gf1_uart_get(gus);
56*1da177e4SLinus Torvalds 		spin_unlock_irqrestore(&gus->uart_cmd_lock, flags);
57*1da177e4SLinus Torvalds 		snd_rawmidi_receive(gus->midi_substream_input, &byte, 1);
58*1da177e4SLinus Torvalds 		if (stat & 0x20) {
59*1da177e4SLinus Torvalds 			gus->gf1.uart_overrun++;
60*1da177e4SLinus Torvalds 		}
61*1da177e4SLinus Torvalds 	}
62*1da177e4SLinus Torvalds }
63*1da177e4SLinus Torvalds 
64*1da177e4SLinus Torvalds static void snd_gf1_interrupt_midi_out(snd_gus_card_t * gus)
65*1da177e4SLinus Torvalds {
66*1da177e4SLinus Torvalds 	char byte;
67*1da177e4SLinus Torvalds 	unsigned long flags;
68*1da177e4SLinus Torvalds 
69*1da177e4SLinus Torvalds 	/* try unlock output */
70*1da177e4SLinus Torvalds 	if (snd_gf1_uart_stat(gus) & 0x01)
71*1da177e4SLinus Torvalds 		snd_gf1_interrupt_midi_in(gus);
72*1da177e4SLinus Torvalds 
73*1da177e4SLinus Torvalds 	spin_lock_irqsave(&gus->uart_cmd_lock, flags);
74*1da177e4SLinus Torvalds 	if (snd_gf1_uart_stat(gus) & 0x02) {	/* Tx FIFO free? */
75*1da177e4SLinus Torvalds 		if (snd_rawmidi_transmit(gus->midi_substream_output, &byte, 1) != 1) {	/* no other bytes or error */
76*1da177e4SLinus Torvalds 			snd_gf1_uart_cmd(gus, gus->gf1.uart_cmd & ~0x20); /* disable Tx interrupt */
77*1da177e4SLinus Torvalds 		} else {
78*1da177e4SLinus Torvalds 			snd_gf1_uart_put(gus, byte);
79*1da177e4SLinus Torvalds 		}
80*1da177e4SLinus Torvalds 	}
81*1da177e4SLinus Torvalds 	spin_unlock_irqrestore(&gus->uart_cmd_lock, flags);
82*1da177e4SLinus Torvalds }
83*1da177e4SLinus Torvalds 
84*1da177e4SLinus Torvalds static void snd_gf1_uart_reset(snd_gus_card_t * gus, int close)
85*1da177e4SLinus Torvalds {
86*1da177e4SLinus Torvalds 	snd_gf1_uart_cmd(gus, 0x03);	/* reset */
87*1da177e4SLinus Torvalds 	if (!close && gus->uart_enable) {
88*1da177e4SLinus Torvalds 		udelay(160);
89*1da177e4SLinus Torvalds 		snd_gf1_uart_cmd(gus, 0x00);	/* normal operations */
90*1da177e4SLinus Torvalds 	}
91*1da177e4SLinus Torvalds }
92*1da177e4SLinus Torvalds 
93*1da177e4SLinus Torvalds static int snd_gf1_uart_output_open(snd_rawmidi_substream_t * substream)
94*1da177e4SLinus Torvalds {
95*1da177e4SLinus Torvalds 	unsigned long flags;
96*1da177e4SLinus Torvalds 	snd_gus_card_t *gus;
97*1da177e4SLinus Torvalds 
98*1da177e4SLinus Torvalds 	gus = substream->rmidi->private_data;
99*1da177e4SLinus Torvalds 	spin_lock_irqsave(&gus->uart_cmd_lock, flags);
100*1da177e4SLinus Torvalds 	if (!(gus->gf1.uart_cmd & 0x80)) {	/* input active? */
101*1da177e4SLinus Torvalds 		snd_gf1_uart_reset(gus, 0);
102*1da177e4SLinus Torvalds 	}
103*1da177e4SLinus Torvalds 	gus->gf1.interrupt_handler_midi_out = snd_gf1_interrupt_midi_out;
104*1da177e4SLinus Torvalds 	gus->midi_substream_output = substream;
105*1da177e4SLinus Torvalds 	spin_unlock_irqrestore(&gus->uart_cmd_lock, flags);
106*1da177e4SLinus Torvalds #if 0
107*1da177e4SLinus Torvalds 	snd_printk("write init - cmd = 0x%x, stat = 0x%x\n", gus->gf1.uart_cmd, snd_gf1_uart_stat(gus));
108*1da177e4SLinus Torvalds #endif
109*1da177e4SLinus Torvalds 	return 0;
110*1da177e4SLinus Torvalds }
111*1da177e4SLinus Torvalds 
112*1da177e4SLinus Torvalds static int snd_gf1_uart_input_open(snd_rawmidi_substream_t * substream)
113*1da177e4SLinus Torvalds {
114*1da177e4SLinus Torvalds 	unsigned long flags;
115*1da177e4SLinus Torvalds 	snd_gus_card_t *gus;
116*1da177e4SLinus Torvalds 	int i;
117*1da177e4SLinus Torvalds 
118*1da177e4SLinus Torvalds 	gus = substream->rmidi->private_data;
119*1da177e4SLinus Torvalds 	spin_lock_irqsave(&gus->uart_cmd_lock, flags);
120*1da177e4SLinus Torvalds 	if (gus->gf1.interrupt_handler_midi_out != snd_gf1_interrupt_midi_out) {
121*1da177e4SLinus Torvalds 		snd_gf1_uart_reset(gus, 0);
122*1da177e4SLinus Torvalds 	}
123*1da177e4SLinus Torvalds 	gus->gf1.interrupt_handler_midi_in = snd_gf1_interrupt_midi_in;
124*1da177e4SLinus Torvalds 	gus->midi_substream_input = substream;
125*1da177e4SLinus Torvalds 	if (gus->uart_enable) {
126*1da177e4SLinus Torvalds 		for (i = 0; i < 1000 && (snd_gf1_uart_stat(gus) & 0x01); i++)
127*1da177e4SLinus Torvalds 			snd_gf1_uart_get(gus);	/* clean Rx */
128*1da177e4SLinus Torvalds 		if (i >= 1000)
129*1da177e4SLinus Torvalds 			snd_printk("gus midi uart init read - cleanup error\n");
130*1da177e4SLinus Torvalds 	}
131*1da177e4SLinus Torvalds 	spin_unlock_irqrestore(&gus->uart_cmd_lock, flags);
132*1da177e4SLinus Torvalds #if 0
133*1da177e4SLinus Torvalds 	snd_printk("read init - enable = %i, cmd = 0x%x, stat = 0x%x\n", gus->uart_enable, gus->gf1.uart_cmd, snd_gf1_uart_stat(gus));
134*1da177e4SLinus Torvalds 	snd_printk("[0x%x] reg (ctrl/status) = 0x%x, reg (data) = 0x%x (page = 0x%x)\n", gus->gf1.port + 0x100, inb(gus->gf1.port + 0x100), inb(gus->gf1.port + 0x101), inb(gus->gf1.port + 0x102));
135*1da177e4SLinus Torvalds #endif
136*1da177e4SLinus Torvalds 	return 0;
137*1da177e4SLinus Torvalds }
138*1da177e4SLinus Torvalds 
139*1da177e4SLinus Torvalds static int snd_gf1_uart_output_close(snd_rawmidi_substream_t * substream)
140*1da177e4SLinus Torvalds {
141*1da177e4SLinus Torvalds 	unsigned long flags;
142*1da177e4SLinus Torvalds 	snd_gus_card_t *gus;
143*1da177e4SLinus Torvalds 
144*1da177e4SLinus Torvalds 	gus = substream->rmidi->private_data;
145*1da177e4SLinus Torvalds 	spin_lock_irqsave(&gus->uart_cmd_lock, flags);
146*1da177e4SLinus Torvalds 	if (gus->gf1.interrupt_handler_midi_in != snd_gf1_interrupt_midi_in)
147*1da177e4SLinus Torvalds 		snd_gf1_uart_reset(gus, 1);
148*1da177e4SLinus Torvalds 	snd_gf1_set_default_handlers(gus, SNDRV_GF1_HANDLER_MIDI_OUT);
149*1da177e4SLinus Torvalds 	gus->midi_substream_output = NULL;
150*1da177e4SLinus Torvalds 	spin_unlock_irqrestore(&gus->uart_cmd_lock, flags);
151*1da177e4SLinus Torvalds 	return 0;
152*1da177e4SLinus Torvalds }
153*1da177e4SLinus Torvalds 
154*1da177e4SLinus Torvalds static int snd_gf1_uart_input_close(snd_rawmidi_substream_t * substream)
155*1da177e4SLinus Torvalds {
156*1da177e4SLinus Torvalds 	unsigned long flags;
157*1da177e4SLinus Torvalds 	snd_gus_card_t *gus;
158*1da177e4SLinus Torvalds 
159*1da177e4SLinus Torvalds 	gus = substream->rmidi->private_data;
160*1da177e4SLinus Torvalds 	spin_lock_irqsave(&gus->uart_cmd_lock, flags);
161*1da177e4SLinus Torvalds 	if (gus->gf1.interrupt_handler_midi_out != snd_gf1_interrupt_midi_out)
162*1da177e4SLinus Torvalds 		snd_gf1_uart_reset(gus, 1);
163*1da177e4SLinus Torvalds 	snd_gf1_set_default_handlers(gus, SNDRV_GF1_HANDLER_MIDI_IN);
164*1da177e4SLinus Torvalds 	gus->midi_substream_input = NULL;
165*1da177e4SLinus Torvalds 	spin_unlock_irqrestore(&gus->uart_cmd_lock, flags);
166*1da177e4SLinus Torvalds 	return 0;
167*1da177e4SLinus Torvalds }
168*1da177e4SLinus Torvalds 
169*1da177e4SLinus Torvalds static void snd_gf1_uart_input_trigger(snd_rawmidi_substream_t * substream, int up)
170*1da177e4SLinus Torvalds {
171*1da177e4SLinus Torvalds 	snd_gus_card_t *gus;
172*1da177e4SLinus Torvalds 	unsigned long flags;
173*1da177e4SLinus Torvalds 
174*1da177e4SLinus Torvalds 	gus = substream->rmidi->private_data;
175*1da177e4SLinus Torvalds 
176*1da177e4SLinus Torvalds 	spin_lock_irqsave(&gus->uart_cmd_lock, flags);
177*1da177e4SLinus Torvalds 	if (up) {
178*1da177e4SLinus Torvalds 		if ((gus->gf1.uart_cmd & 0x80) == 0)
179*1da177e4SLinus Torvalds 			snd_gf1_uart_cmd(gus, gus->gf1.uart_cmd | 0x80); /* enable Rx interrupts */
180*1da177e4SLinus Torvalds 	} else {
181*1da177e4SLinus Torvalds 		if (gus->gf1.uart_cmd & 0x80)
182*1da177e4SLinus Torvalds 			snd_gf1_uart_cmd(gus, gus->gf1.uart_cmd & ~0x80); /* disable Rx interrupts */
183*1da177e4SLinus Torvalds 	}
184*1da177e4SLinus Torvalds 	spin_unlock_irqrestore(&gus->uart_cmd_lock, flags);
185*1da177e4SLinus Torvalds }
186*1da177e4SLinus Torvalds 
187*1da177e4SLinus Torvalds static void snd_gf1_uart_output_trigger(snd_rawmidi_substream_t * substream, int up)
188*1da177e4SLinus Torvalds {
189*1da177e4SLinus Torvalds 	unsigned long flags;
190*1da177e4SLinus Torvalds 	snd_gus_card_t *gus;
191*1da177e4SLinus Torvalds 	char byte;
192*1da177e4SLinus Torvalds 	int timeout;
193*1da177e4SLinus Torvalds 
194*1da177e4SLinus Torvalds 	gus = substream->rmidi->private_data;
195*1da177e4SLinus Torvalds 
196*1da177e4SLinus Torvalds 	spin_lock_irqsave(&gus->uart_cmd_lock, flags);
197*1da177e4SLinus Torvalds 	if (up) {
198*1da177e4SLinus Torvalds 		if ((gus->gf1.uart_cmd & 0x20) == 0) {
199*1da177e4SLinus Torvalds 			spin_unlock_irqrestore(&gus->uart_cmd_lock, flags);
200*1da177e4SLinus Torvalds 			/* wait for empty Rx - Tx is probably unlocked */
201*1da177e4SLinus Torvalds 			timeout = 10000;
202*1da177e4SLinus Torvalds 			while (timeout-- > 0 && snd_gf1_uart_stat(gus) & 0x01);
203*1da177e4SLinus Torvalds 			/* Tx FIFO free? */
204*1da177e4SLinus Torvalds 			spin_lock_irqsave(&gus->uart_cmd_lock, flags);
205*1da177e4SLinus Torvalds 			if (gus->gf1.uart_cmd & 0x20) {
206*1da177e4SLinus Torvalds 				spin_unlock_irqrestore(&gus->uart_cmd_lock, flags);
207*1da177e4SLinus Torvalds 				return;
208*1da177e4SLinus Torvalds 			}
209*1da177e4SLinus Torvalds 			if (snd_gf1_uart_stat(gus) & 0x02) {
210*1da177e4SLinus Torvalds 				if (snd_rawmidi_transmit(substream, &byte, 1) != 1) {
211*1da177e4SLinus Torvalds 					spin_unlock_irqrestore(&gus->uart_cmd_lock, flags);
212*1da177e4SLinus Torvalds 					return;
213*1da177e4SLinus Torvalds 				}
214*1da177e4SLinus Torvalds 				snd_gf1_uart_put(gus, byte);
215*1da177e4SLinus Torvalds 			}
216*1da177e4SLinus Torvalds 			snd_gf1_uart_cmd(gus, gus->gf1.uart_cmd | 0x20);	/* enable Tx interrupt */
217*1da177e4SLinus Torvalds 		}
218*1da177e4SLinus Torvalds 	} else {
219*1da177e4SLinus Torvalds 		if (gus->gf1.uart_cmd & 0x20)
220*1da177e4SLinus Torvalds 			snd_gf1_uart_cmd(gus, gus->gf1.uart_cmd & ~0x20);
221*1da177e4SLinus Torvalds 	}
222*1da177e4SLinus Torvalds 	spin_unlock_irqrestore(&gus->uart_cmd_lock, flags);
223*1da177e4SLinus Torvalds }
224*1da177e4SLinus Torvalds 
225*1da177e4SLinus Torvalds static snd_rawmidi_ops_t snd_gf1_uart_output =
226*1da177e4SLinus Torvalds {
227*1da177e4SLinus Torvalds 	.open =		snd_gf1_uart_output_open,
228*1da177e4SLinus Torvalds 	.close =	snd_gf1_uart_output_close,
229*1da177e4SLinus Torvalds 	.trigger =	snd_gf1_uart_output_trigger,
230*1da177e4SLinus Torvalds };
231*1da177e4SLinus Torvalds 
232*1da177e4SLinus Torvalds static snd_rawmidi_ops_t snd_gf1_uart_input =
233*1da177e4SLinus Torvalds {
234*1da177e4SLinus Torvalds 	.open =		snd_gf1_uart_input_open,
235*1da177e4SLinus Torvalds 	.close =	snd_gf1_uart_input_close,
236*1da177e4SLinus Torvalds 	.trigger =	snd_gf1_uart_input_trigger,
237*1da177e4SLinus Torvalds };
238*1da177e4SLinus Torvalds 
239*1da177e4SLinus Torvalds int snd_gf1_rawmidi_new(snd_gus_card_t * gus, int device, snd_rawmidi_t ** rrawmidi)
240*1da177e4SLinus Torvalds {
241*1da177e4SLinus Torvalds 	snd_rawmidi_t *rmidi;
242*1da177e4SLinus Torvalds 	int err;
243*1da177e4SLinus Torvalds 
244*1da177e4SLinus Torvalds 	if (rrawmidi)
245*1da177e4SLinus Torvalds 		*rrawmidi = NULL;
246*1da177e4SLinus Torvalds 	if ((err = snd_rawmidi_new(gus->card, "GF1", device, 1, 1, &rmidi)) < 0)
247*1da177e4SLinus Torvalds 		return err;
248*1da177e4SLinus Torvalds 	strcpy(rmidi->name, gus->interwave ? "AMD InterWave" : "GF1");
249*1da177e4SLinus Torvalds 	snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_gf1_uart_output);
250*1da177e4SLinus Torvalds 	snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_gf1_uart_input);
251*1da177e4SLinus Torvalds 	rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT | SNDRV_RAWMIDI_INFO_INPUT | SNDRV_RAWMIDI_INFO_DUPLEX;
252*1da177e4SLinus Torvalds 	rmidi->private_data = gus;
253*1da177e4SLinus Torvalds 	gus->midi_uart = rmidi;
254*1da177e4SLinus Torvalds 	if (rrawmidi)
255*1da177e4SLinus Torvalds 		*rrawmidi = rmidi;
256*1da177e4SLinus Torvalds 	return err;
257*1da177e4SLinus Torvalds }
258