]>
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 control of EMU10K1 MPU-401 in UART mode |
1da177e4 LT |
5 | */ |
6 | ||
1da177e4 LT |
7 | #include <linux/time.h> |
8 | #include <linux/init.h> | |
9 | #include <sound/core.h> | |
10 | #include <sound/emu10k1.h> | |
11 | ||
12 | #define EMU10K1_MIDI_MODE_INPUT (1<<0) | |
13 | #define EMU10K1_MIDI_MODE_OUTPUT (1<<1) | |
14 | ||
eb4698f3 TI |
15 | static inline unsigned char mpu401_read(struct snd_emu10k1 *emu, |
16 | struct snd_emu10k1_midi *mpu, int idx) | |
1da177e4 LT |
17 | { |
18 | if (emu->audigy) | |
19 | return (unsigned char)snd_emu10k1_ptr_read(emu, mpu->port + idx, 0); | |
20 | else | |
21 | return inb(emu->port + mpu->port + idx); | |
22 | } | |
23 | ||
eb4698f3 TI |
24 | static inline void mpu401_write(struct snd_emu10k1 *emu, |
25 | struct snd_emu10k1_midi *mpu, int data, int idx) | |
1da177e4 LT |
26 | { |
27 | if (emu->audigy) | |
28 | snd_emu10k1_ptr_write(emu, mpu->port + idx, 0, data); | |
29 | else | |
30 | outb(data, emu->port + mpu->port + idx); | |
31 | } | |
32 | ||
33 | #define mpu401_write_data(emu, mpu, data) mpu401_write(emu, mpu, data, 0) | |
34 | #define mpu401_write_cmd(emu, mpu, data) mpu401_write(emu, mpu, data, 1) | |
35 | #define mpu401_read_data(emu, mpu) mpu401_read(emu, mpu, 0) | |
36 | #define mpu401_read_stat(emu, mpu) mpu401_read(emu, mpu, 1) | |
37 | ||
38 | #define mpu401_input_avail(emu,mpu) (!(mpu401_read_stat(emu,mpu) & 0x80)) | |
39 | #define mpu401_output_ready(emu,mpu) (!(mpu401_read_stat(emu,mpu) & 0x40)) | |
40 | ||
41 | #define MPU401_RESET 0xff | |
42 | #define MPU401_ENTER_UART 0x3f | |
43 | #define MPU401_ACK 0xfe | |
44 | ||
eb4698f3 | 45 | static void mpu401_clear_rx(struct snd_emu10k1 *emu, struct snd_emu10k1_midi *mpu) |
1da177e4 LT |
46 | { |
47 | int timeout = 100000; | |
48 | for (; timeout > 0 && mpu401_input_avail(emu, mpu); timeout--) | |
49 | mpu401_read_data(emu, mpu); | |
50 | #ifdef CONFIG_SND_DEBUG | |
51 | if (timeout <= 0) | |
6f002b02 TI |
52 | dev_err(emu->card->dev, |
53 | "cmd: clear rx timeout (status = 0x%x)\n", | |
54 | mpu401_read_stat(emu, mpu)); | |
1da177e4 LT |
55 | #endif |
56 | } | |
57 | ||
58 | /* | |
59 | ||
60 | */ | |
61 | ||
eb4698f3 | 62 | static void do_emu10k1_midi_interrupt(struct snd_emu10k1 *emu, struct snd_emu10k1_midi *midi, unsigned int status) |
1da177e4 LT |
63 | { |
64 | unsigned char byte; | |
65 | ||
66 | if (midi->rmidi == NULL) { | |
67 | snd_emu10k1_intr_disable(emu, midi->tx_enable | midi->rx_enable); | |
68 | return; | |
69 | } | |
70 | ||
71 | spin_lock(&midi->input_lock); | |
72 | if ((status & midi->ipr_rx) && mpu401_input_avail(emu, midi)) { | |
73 | if (!(midi->midi_mode & EMU10K1_MIDI_MODE_INPUT)) { | |
74 | mpu401_clear_rx(emu, midi); | |
75 | } else { | |
76 | byte = mpu401_read_data(emu, midi); | |
77 | if (midi->substream_input) | |
78 | snd_rawmidi_receive(midi->substream_input, &byte, 1); | |
79 | } | |
80 | } | |
81 | spin_unlock(&midi->input_lock); | |
82 | ||
83 | spin_lock(&midi->output_lock); | |
84 | if ((status & midi->ipr_tx) && mpu401_output_ready(emu, midi)) { | |
85 | if (midi->substream_output && | |
86 | snd_rawmidi_transmit(midi->substream_output, &byte, 1) == 1) { | |
87 | mpu401_write_data(emu, midi, byte); | |
88 | } else { | |
89 | snd_emu10k1_intr_disable(emu, midi->tx_enable); | |
90 | } | |
91 | } | |
92 | spin_unlock(&midi->output_lock); | |
93 | } | |
94 | ||
eb4698f3 | 95 | static void snd_emu10k1_midi_interrupt(struct snd_emu10k1 *emu, unsigned int status) |
1da177e4 LT |
96 | { |
97 | do_emu10k1_midi_interrupt(emu, &emu->midi, status); | |
98 | } | |
99 | ||
eb4698f3 | 100 | static void snd_emu10k1_midi_interrupt2(struct snd_emu10k1 *emu, unsigned int status) |
1da177e4 LT |
101 | { |
102 | do_emu10k1_midi_interrupt(emu, &emu->midi2, status); | |
103 | } | |
104 | ||
b130807d | 105 | static int snd_emu10k1_midi_cmd(struct snd_emu10k1 * emu, struct snd_emu10k1_midi *midi, unsigned char cmd, int ack) |
1da177e4 LT |
106 | { |
107 | unsigned long flags; | |
108 | int timeout, ok; | |
109 | ||
110 | spin_lock_irqsave(&midi->input_lock, flags); | |
111 | mpu401_write_data(emu, midi, 0x00); | |
112 | /* mpu401_clear_rx(emu, midi); */ | |
113 | ||
114 | mpu401_write_cmd(emu, midi, cmd); | |
115 | if (ack) { | |
116 | ok = 0; | |
117 | timeout = 10000; | |
118 | while (!ok && timeout-- > 0) { | |
119 | if (mpu401_input_avail(emu, midi)) { | |
120 | if (mpu401_read_data(emu, midi) == MPU401_ACK) | |
121 | ok = 1; | |
122 | } | |
123 | } | |
124 | if (!ok && mpu401_read_data(emu, midi) == MPU401_ACK) | |
125 | ok = 1; | |
126 | } else { | |
127 | ok = 1; | |
128 | } | |
129 | spin_unlock_irqrestore(&midi->input_lock, flags); | |
b130807d | 130 | if (!ok) { |
6f002b02 TI |
131 | dev_err(emu->card->dev, |
132 | "midi_cmd: 0x%x failed at 0x%lx (status = 0x%x, data = 0x%x)!!!\n", | |
1da177e4 LT |
133 | cmd, emu->port, |
134 | mpu401_read_stat(emu, midi), | |
135 | mpu401_read_data(emu, midi)); | |
b130807d RD |
136 | return 1; |
137 | } | |
138 | return 0; | |
1da177e4 LT |
139 | } |
140 | ||
eb4698f3 | 141 | static int snd_emu10k1_midi_input_open(struct snd_rawmidi_substream *substream) |
1da177e4 | 142 | { |
eb4698f3 TI |
143 | struct snd_emu10k1 *emu; |
144 | struct snd_emu10k1_midi *midi = (struct snd_emu10k1_midi *)substream->rmidi->private_data; | |
1da177e4 LT |
145 | unsigned long flags; |
146 | ||
147 | emu = midi->emu; | |
da3cec35 TI |
148 | if (snd_BUG_ON(!emu)) |
149 | return -ENXIO; | |
1da177e4 LT |
150 | spin_lock_irqsave(&midi->open_lock, flags); |
151 | midi->midi_mode |= EMU10K1_MIDI_MODE_INPUT; | |
152 | midi->substream_input = substream; | |
153 | if (!(midi->midi_mode & EMU10K1_MIDI_MODE_OUTPUT)) { | |
154 | spin_unlock_irqrestore(&midi->open_lock, flags); | |
b130807d RD |
155 | if (snd_emu10k1_midi_cmd(emu, midi, MPU401_RESET, 1)) |
156 | goto error_out; | |
157 | if (snd_emu10k1_midi_cmd(emu, midi, MPU401_ENTER_UART, 1)) | |
158 | goto error_out; | |
1da177e4 LT |
159 | } else { |
160 | spin_unlock_irqrestore(&midi->open_lock, flags); | |
161 | } | |
162 | return 0; | |
b130807d RD |
163 | |
164 | error_out: | |
165 | return -EIO; | |
1da177e4 LT |
166 | } |
167 | ||
eb4698f3 | 168 | static int snd_emu10k1_midi_output_open(struct snd_rawmidi_substream *substream) |
1da177e4 | 169 | { |
eb4698f3 TI |
170 | struct snd_emu10k1 *emu; |
171 | struct snd_emu10k1_midi *midi = (struct snd_emu10k1_midi *)substream->rmidi->private_data; | |
1da177e4 LT |
172 | unsigned long flags; |
173 | ||
174 | emu = midi->emu; | |
da3cec35 TI |
175 | if (snd_BUG_ON(!emu)) |
176 | return -ENXIO; | |
1da177e4 LT |
177 | spin_lock_irqsave(&midi->open_lock, flags); |
178 | midi->midi_mode |= EMU10K1_MIDI_MODE_OUTPUT; | |
179 | midi->substream_output = substream; | |
180 | if (!(midi->midi_mode & EMU10K1_MIDI_MODE_INPUT)) { | |
181 | spin_unlock_irqrestore(&midi->open_lock, flags); | |
b130807d RD |
182 | if (snd_emu10k1_midi_cmd(emu, midi, MPU401_RESET, 1)) |
183 | goto error_out; | |
184 | if (snd_emu10k1_midi_cmd(emu, midi, MPU401_ENTER_UART, 1)) | |
185 | goto error_out; | |
1da177e4 LT |
186 | } else { |
187 | spin_unlock_irqrestore(&midi->open_lock, flags); | |
188 | } | |
189 | return 0; | |
b130807d RD |
190 | |
191 | error_out: | |
192 | return -EIO; | |
1da177e4 LT |
193 | } |
194 | ||
eb4698f3 | 195 | static int snd_emu10k1_midi_input_close(struct snd_rawmidi_substream *substream) |
1da177e4 | 196 | { |
eb4698f3 TI |
197 | struct snd_emu10k1 *emu; |
198 | struct snd_emu10k1_midi *midi = (struct snd_emu10k1_midi *)substream->rmidi->private_data; | |
1da177e4 | 199 | unsigned long flags; |
b130807d | 200 | int err = 0; |
1da177e4 LT |
201 | |
202 | emu = midi->emu; | |
da3cec35 TI |
203 | if (snd_BUG_ON(!emu)) |
204 | return -ENXIO; | |
1da177e4 LT |
205 | spin_lock_irqsave(&midi->open_lock, flags); |
206 | snd_emu10k1_intr_disable(emu, midi->rx_enable); | |
207 | midi->midi_mode &= ~EMU10K1_MIDI_MODE_INPUT; | |
208 | midi->substream_input = NULL; | |
209 | if (!(midi->midi_mode & EMU10K1_MIDI_MODE_OUTPUT)) { | |
210 | spin_unlock_irqrestore(&midi->open_lock, flags); | |
b130807d | 211 | err = snd_emu10k1_midi_cmd(emu, midi, MPU401_RESET, 0); |
1da177e4 LT |
212 | } else { |
213 | spin_unlock_irqrestore(&midi->open_lock, flags); | |
214 | } | |
b130807d | 215 | return err; |
1da177e4 LT |
216 | } |
217 | ||
eb4698f3 | 218 | static int snd_emu10k1_midi_output_close(struct snd_rawmidi_substream *substream) |
1da177e4 | 219 | { |
eb4698f3 TI |
220 | struct snd_emu10k1 *emu; |
221 | struct snd_emu10k1_midi *midi = (struct snd_emu10k1_midi *)substream->rmidi->private_data; | |
1da177e4 | 222 | unsigned long flags; |
b130807d | 223 | int err = 0; |
1da177e4 LT |
224 | |
225 | emu = midi->emu; | |
da3cec35 TI |
226 | if (snd_BUG_ON(!emu)) |
227 | return -ENXIO; | |
1da177e4 LT |
228 | spin_lock_irqsave(&midi->open_lock, flags); |
229 | snd_emu10k1_intr_disable(emu, midi->tx_enable); | |
230 | midi->midi_mode &= ~EMU10K1_MIDI_MODE_OUTPUT; | |
231 | midi->substream_output = NULL; | |
232 | if (!(midi->midi_mode & EMU10K1_MIDI_MODE_INPUT)) { | |
233 | spin_unlock_irqrestore(&midi->open_lock, flags); | |
b130807d | 234 | err = snd_emu10k1_midi_cmd(emu, midi, MPU401_RESET, 0); |
1da177e4 LT |
235 | } else { |
236 | spin_unlock_irqrestore(&midi->open_lock, flags); | |
237 | } | |
b130807d | 238 | return err; |
1da177e4 LT |
239 | } |
240 | ||
eb4698f3 | 241 | static void snd_emu10k1_midi_input_trigger(struct snd_rawmidi_substream *substream, int up) |
1da177e4 | 242 | { |
eb4698f3 TI |
243 | struct snd_emu10k1 *emu; |
244 | struct snd_emu10k1_midi *midi = (struct snd_emu10k1_midi *)substream->rmidi->private_data; | |
1da177e4 | 245 | emu = midi->emu; |
da3cec35 TI |
246 | if (snd_BUG_ON(!emu)) |
247 | return; | |
1da177e4 LT |
248 | |
249 | if (up) | |
250 | snd_emu10k1_intr_enable(emu, midi->rx_enable); | |
251 | else | |
252 | snd_emu10k1_intr_disable(emu, midi->rx_enable); | |
253 | } | |
254 | ||
eb4698f3 | 255 | static void snd_emu10k1_midi_output_trigger(struct snd_rawmidi_substream *substream, int up) |
1da177e4 | 256 | { |
eb4698f3 TI |
257 | struct snd_emu10k1 *emu; |
258 | struct snd_emu10k1_midi *midi = (struct snd_emu10k1_midi *)substream->rmidi->private_data; | |
1da177e4 LT |
259 | unsigned long flags; |
260 | ||
261 | emu = midi->emu; | |
da3cec35 TI |
262 | if (snd_BUG_ON(!emu)) |
263 | return; | |
1da177e4 LT |
264 | |
265 | if (up) { | |
266 | int max = 4; | |
267 | unsigned char byte; | |
268 | ||
269 | /* try to send some amount of bytes here before interrupts */ | |
270 | spin_lock_irqsave(&midi->output_lock, flags); | |
271 | while (max > 0) { | |
272 | if (mpu401_output_ready(emu, midi)) { | |
273 | if (!(midi->midi_mode & EMU10K1_MIDI_MODE_OUTPUT) || | |
274 | snd_rawmidi_transmit(substream, &byte, 1) != 1) { | |
275 | /* no more data */ | |
276 | spin_unlock_irqrestore(&midi->output_lock, flags); | |
277 | return; | |
278 | } | |
279 | mpu401_write_data(emu, midi, byte); | |
280 | max--; | |
281 | } else { | |
282 | break; | |
283 | } | |
284 | } | |
285 | spin_unlock_irqrestore(&midi->output_lock, flags); | |
286 | snd_emu10k1_intr_enable(emu, midi->tx_enable); | |
287 | } else { | |
288 | snd_emu10k1_intr_disable(emu, midi->tx_enable); | |
289 | } | |
290 | } | |
291 | ||
292 | /* | |
293 | ||
294 | */ | |
295 | ||
485885b9 | 296 | static const struct snd_rawmidi_ops snd_emu10k1_midi_output = |
1da177e4 LT |
297 | { |
298 | .open = snd_emu10k1_midi_output_open, | |
299 | .close = snd_emu10k1_midi_output_close, | |
300 | .trigger = snd_emu10k1_midi_output_trigger, | |
301 | }; | |
302 | ||
485885b9 | 303 | static const struct snd_rawmidi_ops snd_emu10k1_midi_input = |
1da177e4 LT |
304 | { |
305 | .open = snd_emu10k1_midi_input_open, | |
306 | .close = snd_emu10k1_midi_input_close, | |
307 | .trigger = snd_emu10k1_midi_input_trigger, | |
308 | }; | |
309 | ||
eb4698f3 | 310 | static void snd_emu10k1_midi_free(struct snd_rawmidi *rmidi) |
1da177e4 | 311 | { |
9fe856e4 | 312 | struct snd_emu10k1_midi *midi = rmidi->private_data; |
1da177e4 LT |
313 | midi->interrupt = NULL; |
314 | midi->rmidi = NULL; | |
315 | } | |
316 | ||
e23e7a14 | 317 | static int emu10k1_midi_init(struct snd_emu10k1 *emu, struct snd_emu10k1_midi *midi, int device, char *name) |
1da177e4 | 318 | { |
eb4698f3 | 319 | struct snd_rawmidi *rmidi; |
1da177e4 LT |
320 | int err; |
321 | ||
322 | if ((err = snd_rawmidi_new(emu->card, name, device, 1, 1, &rmidi)) < 0) | |
323 | return err; | |
324 | midi->emu = emu; | |
325 | spin_lock_init(&midi->open_lock); | |
326 | spin_lock_init(&midi->input_lock); | |
327 | spin_lock_init(&midi->output_lock); | |
328 | strcpy(rmidi->name, name); | |
329 | snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_emu10k1_midi_output); | |
330 | snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_emu10k1_midi_input); | |
331 | rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT | | |
332 | SNDRV_RAWMIDI_INFO_INPUT | | |
333 | SNDRV_RAWMIDI_INFO_DUPLEX; | |
334 | rmidi->private_data = midi; | |
335 | rmidi->private_free = snd_emu10k1_midi_free; | |
336 | midi->rmidi = rmidi; | |
337 | return 0; | |
338 | } | |
339 | ||
e23e7a14 | 340 | int snd_emu10k1_midi(struct snd_emu10k1 *emu) |
1da177e4 | 341 | { |
eb4698f3 | 342 | struct snd_emu10k1_midi *midi = &emu->midi; |
1da177e4 LT |
343 | int err; |
344 | ||
345 | if ((err = emu10k1_midi_init(emu, midi, 0, "EMU10K1 MPU-401 (UART)")) < 0) | |
346 | return err; | |
347 | ||
348 | midi->tx_enable = INTE_MIDITXENABLE; | |
349 | midi->rx_enable = INTE_MIDIRXENABLE; | |
350 | midi->port = MUDATA; | |
351 | midi->ipr_tx = IPR_MIDITRANSBUFEMPTY; | |
352 | midi->ipr_rx = IPR_MIDIRECVBUFEMPTY; | |
353 | midi->interrupt = snd_emu10k1_midi_interrupt; | |
354 | return 0; | |
355 | } | |
356 | ||
e23e7a14 | 357 | int snd_emu10k1_audigy_midi(struct snd_emu10k1 *emu) |
1da177e4 | 358 | { |
eb4698f3 | 359 | struct snd_emu10k1_midi *midi; |
1da177e4 LT |
360 | int err; |
361 | ||
362 | midi = &emu->midi; | |
363 | if ((err = emu10k1_midi_init(emu, midi, 0, "Audigy MPU-401 (UART)")) < 0) | |
364 | return err; | |
365 | ||
366 | midi->tx_enable = INTE_MIDITXENABLE; | |
367 | midi->rx_enable = INTE_MIDIRXENABLE; | |
368 | midi->port = A_MUDATA1; | |
369 | midi->ipr_tx = IPR_MIDITRANSBUFEMPTY; | |
370 | midi->ipr_rx = IPR_MIDIRECVBUFEMPTY; | |
371 | midi->interrupt = snd_emu10k1_midi_interrupt; | |
372 | ||
373 | midi = &emu->midi2; | |
374 | if ((err = emu10k1_midi_init(emu, midi, 1, "Audigy MPU-401 #2")) < 0) | |
375 | return err; | |
376 | ||
377 | midi->tx_enable = INTE_A_MIDITXENABLE2; | |
378 | midi->rx_enable = INTE_A_MIDIRXENABLE2; | |
379 | midi->port = A_MUDATA2; | |
380 | midi->ipr_tx = IPR_A_MIDITRANSBUFEMPTY2; | |
381 | midi->ipr_rx = IPR_A_MIDIRECVBUFEMPTY2; | |
382 | midi->interrupt = snd_emu10k1_midi_interrupt2; | |
383 | return 0; | |
384 | } |