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