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