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