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