2 * Copyright (c) by Jaroslav Kysela <perex@suse.cz>
3 * Routines for the GF1 MIDI interface - like UART 6850
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.
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.
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
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>
29 static void snd_gf1_interrupt_midi_in(snd_gus_card_t
* gus
)
32 unsigned char stat
, data
, byte
;
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
);
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
);
50 if (stat
& 0x10) { /* framing error */
51 gus
->gf1
.uart_framing
++;
52 spin_unlock_irqrestore(&gus
->uart_cmd_lock
, flags
);
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);
59 gus
->gf1
.uart_overrun
++;
64 static void snd_gf1_interrupt_midi_out(snd_gus_card_t
* gus
)
69 /* try unlock output */
70 if (snd_gf1_uart_stat(gus
) & 0x01)
71 snd_gf1_interrupt_midi_in(gus
);
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 */
78 snd_gf1_uart_put(gus
, byte
);
81 spin_unlock_irqrestore(&gus
->uart_cmd_lock
, flags
);
84 static void snd_gf1_uart_reset(snd_gus_card_t
* gus
, int close
)
86 snd_gf1_uart_cmd(gus
, 0x03); /* reset */
87 if (!close
&& gus
->uart_enable
) {
89 snd_gf1_uart_cmd(gus
, 0x00); /* normal operations */
93 static int snd_gf1_uart_output_open(snd_rawmidi_substream_t
* substream
)
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);
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
);
107 snd_printk("write init - cmd = 0x%x, stat = 0x%x\n", gus
->gf1
.uart_cmd
, snd_gf1_uart_stat(gus
));
112 static int snd_gf1_uart_input_open(snd_rawmidi_substream_t
* substream
)
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);
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 */
129 snd_printk("gus midi uart init read - cleanup error\n");
131 spin_unlock_irqrestore(&gus
->uart_cmd_lock
, flags
);
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));
139 static int snd_gf1_uart_output_close(snd_rawmidi_substream_t
* substream
)
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
);
154 static int snd_gf1_uart_input_close(snd_rawmidi_substream_t
* substream
)
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
);
169 static void snd_gf1_uart_input_trigger(snd_rawmidi_substream_t
* substream
, int up
)
174 gus
= substream
->rmidi
->private_data
;
176 spin_lock_irqsave(&gus
->uart_cmd_lock
, flags
);
178 if ((gus
->gf1
.uart_cmd
& 0x80) == 0)
179 snd_gf1_uart_cmd(gus
, gus
->gf1
.uart_cmd
| 0x80); /* enable Rx interrupts */
181 if (gus
->gf1
.uart_cmd
& 0x80)
182 snd_gf1_uart_cmd(gus
, gus
->gf1
.uart_cmd
& ~0x80); /* disable Rx interrupts */
184 spin_unlock_irqrestore(&gus
->uart_cmd_lock
, flags
);
187 static void snd_gf1_uart_output_trigger(snd_rawmidi_substream_t
* substream
, int up
)
194 gus
= substream
->rmidi
->private_data
;
196 spin_lock_irqsave(&gus
->uart_cmd_lock
, flags
);
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 */
202 while (timeout
-- > 0 && snd_gf1_uart_stat(gus
) & 0x01);
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
);
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
);
214 snd_gf1_uart_put(gus
, byte
);
216 snd_gf1_uart_cmd(gus
, gus
->gf1
.uart_cmd
| 0x20); /* enable Tx interrupt */
219 if (gus
->gf1
.uart_cmd
& 0x20)
220 snd_gf1_uart_cmd(gus
, gus
->gf1
.uart_cmd
& ~0x20);
222 spin_unlock_irqrestore(&gus
->uart_cmd_lock
, flags
);
225 static snd_rawmidi_ops_t snd_gf1_uart_output
=
227 .open
= snd_gf1_uart_output_open
,
228 .close
= snd_gf1_uart_output_close
,
229 .trigger
= snd_gf1_uart_output_trigger
,
232 static snd_rawmidi_ops_t snd_gf1_uart_input
=
234 .open
= snd_gf1_uart_input_open
,
235 .close
= snd_gf1_uart_input_close
,
236 .trigger
= snd_gf1_uart_input_trigger
,
239 int snd_gf1_rawmidi_new(snd_gus_card_t
* gus
, int device
, snd_rawmidi_t
** rrawmidi
)
241 snd_rawmidi_t
*rmidi
;
246 if ((err
= snd_rawmidi_new(gus
->card
, "GF1", device
, 1, 1, &rmidi
)) < 0)
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
;