diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /sound/isa/gus/gus_uart.c |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'sound/isa/gus/gus_uart.c')
-rw-r--r-- | sound/isa/gus/gus_uart.c | 257 |
1 files changed, 257 insertions, 0 deletions
diff --git a/sound/isa/gus/gus_uart.c b/sound/isa/gus/gus_uart.c new file mode 100644 index 000000000000..1bc2da8784e0 --- /dev/null +++ b/sound/isa/gus/gus_uart.c | |||
@@ -0,0 +1,257 @@ | |||
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 | |||
29 | static void snd_gf1_interrupt_midi_in(snd_gus_card_t * 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 | |||
64 | static void snd_gf1_interrupt_midi_out(snd_gus_card_t * 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 | |||
84 | static void snd_gf1_uart_reset(snd_gus_card_t * 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 | |||
93 | static int snd_gf1_uart_output_open(snd_rawmidi_substream_t * substream) | ||
94 | { | ||
95 | unsigned long flags; | ||
96 | snd_gus_card_t *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("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 | |||
112 | static int snd_gf1_uart_input_open(snd_rawmidi_substream_t * substream) | ||
113 | { | ||
114 | unsigned long flags; | ||
115 | snd_gus_card_t *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("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 | |||
139 | static int snd_gf1_uart_output_close(snd_rawmidi_substream_t * substream) | ||
140 | { | ||
141 | unsigned long flags; | ||
142 | snd_gus_card_t *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 | |||
154 | static int snd_gf1_uart_input_close(snd_rawmidi_substream_t * substream) | ||
155 | { | ||
156 | unsigned long flags; | ||
157 | snd_gus_card_t *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 | |||
169 | static void snd_gf1_uart_input_trigger(snd_rawmidi_substream_t * substream, int up) | ||
170 | { | ||
171 | snd_gus_card_t *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 | |||
187 | static void snd_gf1_uart_output_trigger(snd_rawmidi_substream_t * substream, int up) | ||
188 | { | ||
189 | unsigned long flags; | ||
190 | snd_gus_card_t *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 | |||
225 | static snd_rawmidi_ops_t 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 | |||
232 | static snd_rawmidi_ops_t 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 | |||
239 | int snd_gf1_rawmidi_new(snd_gus_card_t * gus, int device, snd_rawmidi_t ** rrawmidi) | ||
240 | { | ||
241 | snd_rawmidi_t *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 | } | ||