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