]>
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 LT |
4 | * Routines for control of MPU-401 in UART mode |
5 | * | |
6 | * MPU-401 supports UART mode which is not capable generate transmit | |
dba8b469 | 7 | * interrupts thus output is done via polling. Without interrupt, |
1da177e4 LT |
8 | * input is done also via polling. Do not expect good performance. |
9 | * | |
1da177e4 LT |
10 | * 13-03-2003: |
11 | * Added support for different kind of hardware I/O. Build in choices | |
12 | * are port and mmio. For other kind of I/O, set mpu->read and | |
13 | * mpu->write to your own I/O functions. | |
1da177e4 LT |
14 | */ |
15 | ||
6cbbfe1c | 16 | #include <linux/io.h> |
1da177e4 LT |
17 | #include <linux/delay.h> |
18 | #include <linux/init.h> | |
19 | #include <linux/slab.h> | |
20 | #include <linux/ioport.h> | |
da155d5b | 21 | #include <linux/module.h> |
1da177e4 LT |
22 | #include <linux/interrupt.h> |
23 | #include <linux/errno.h> | |
24 | #include <sound/core.h> | |
25 | #include <sound/mpu401.h> | |
26 | ||
c1017a4c | 27 | MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>"); |
1da177e4 LT |
28 | MODULE_DESCRIPTION("Routines for control of MPU-401 in UART mode"); |
29 | MODULE_LICENSE("GPL"); | |
30 | ||
e1fad17b TI |
31 | static void snd_mpu401_uart_input_read(struct snd_mpu401 * mpu); |
32 | static void snd_mpu401_uart_output_write(struct snd_mpu401 * mpu); | |
1da177e4 LT |
33 | |
34 | /* | |
35 | ||
36 | */ | |
37 | ||
b415ed45 TI |
38 | #define snd_mpu401_input_avail(mpu) \ |
39 | (!(mpu->read(mpu, MPU401C(mpu)) & MPU401_RX_EMPTY)) | |
40 | #define snd_mpu401_output_ready(mpu) \ | |
41 | (!(mpu->read(mpu, MPU401C(mpu)) & MPU401_TX_FULL)) | |
1da177e4 LT |
42 | |
43 | /* Build in lowlevel io */ | |
2851d963 TI |
44 | static void mpu401_write_port(struct snd_mpu401 *mpu, unsigned char data, |
45 | unsigned long addr) | |
1da177e4 LT |
46 | { |
47 | outb(data, addr); | |
48 | } | |
49 | ||
2851d963 TI |
50 | static unsigned char mpu401_read_port(struct snd_mpu401 *mpu, |
51 | unsigned long addr) | |
1da177e4 LT |
52 | { |
53 | return inb(addr); | |
54 | } | |
55 | ||
2851d963 TI |
56 | static void mpu401_write_mmio(struct snd_mpu401 *mpu, unsigned char data, |
57 | unsigned long addr) | |
1da177e4 LT |
58 | { |
59 | writeb(data, (void __iomem *)addr); | |
60 | } | |
61 | ||
2851d963 TI |
62 | static unsigned char mpu401_read_mmio(struct snd_mpu401 *mpu, |
63 | unsigned long addr) | |
1da177e4 LT |
64 | { |
65 | return readb((void __iomem *)addr); | |
66 | } | |
67 | /* */ | |
68 | ||
e1fad17b | 69 | static void snd_mpu401_uart_clear_rx(struct snd_mpu401 *mpu) |
1da177e4 LT |
70 | { |
71 | int timeout = 100000; | |
72 | for (; timeout > 0 && snd_mpu401_input_avail(mpu); timeout--) | |
73 | mpu->read(mpu, MPU401D(mpu)); | |
74 | #ifdef CONFIG_SND_DEBUG | |
75 | if (timeout <= 0) | |
2851d963 TI |
76 | snd_printk(KERN_ERR "cmd: clear rx timeout (status = 0x%x)\n", |
77 | mpu->read(mpu, MPU401C(mpu))); | |
1da177e4 LT |
78 | #endif |
79 | } | |
80 | ||
302e4c2f | 81 | static void uart_interrupt_tx(struct snd_mpu401 *mpu) |
1da177e4 | 82 | { |
60fac85f CL |
83 | unsigned long flags; |
84 | ||
1da177e4 LT |
85 | if (test_bit(MPU401_MODE_BIT_OUTPUT, &mpu->mode) && |
86 | test_bit(MPU401_MODE_BIT_OUTPUT_TRIGGER, &mpu->mode)) { | |
60fac85f | 87 | spin_lock_irqsave(&mpu->output_lock, flags); |
1da177e4 | 88 | snd_mpu401_uart_output_write(mpu); |
60fac85f | 89 | spin_unlock_irqrestore(&mpu->output_lock, flags); |
1da177e4 LT |
90 | } |
91 | } | |
92 | ||
302e4c2f TI |
93 | static void _snd_mpu401_uart_interrupt(struct snd_mpu401 *mpu) |
94 | { | |
60fac85f CL |
95 | unsigned long flags; |
96 | ||
302e4c2f | 97 | if (mpu->info_flags & MPU401_INFO_INPUT) { |
60fac85f | 98 | spin_lock_irqsave(&mpu->input_lock, flags); |
302e4c2f TI |
99 | if (test_bit(MPU401_MODE_BIT_INPUT, &mpu->mode)) |
100 | snd_mpu401_uart_input_read(mpu); | |
101 | else | |
102 | snd_mpu401_uart_clear_rx(mpu); | |
60fac85f | 103 | spin_unlock_irqrestore(&mpu->input_lock, flags); |
302e4c2f TI |
104 | } |
105 | if (! (mpu->info_flags & MPU401_INFO_TX_IRQ)) | |
106 | /* ok. for better Tx performance try do some output | |
107 | when input is done */ | |
108 | uart_interrupt_tx(mpu); | |
109 | } | |
110 | ||
1da177e4 LT |
111 | /** |
112 | * snd_mpu401_uart_interrupt - generic MPU401-UART interrupt handler | |
113 | * @irq: the irq number | |
114 | * @dev_id: mpu401 instance | |
1da177e4 LT |
115 | * |
116 | * Processes the interrupt for MPU401-UART i/o. | |
eb7c06e8 YB |
117 | * |
118 | * Return: %IRQ_HANDLED if the interrupt was handled. %IRQ_NONE otherwise. | |
1da177e4 | 119 | */ |
7d12e780 | 120 | irqreturn_t snd_mpu401_uart_interrupt(int irq, void *dev_id) |
1da177e4 | 121 | { |
e1fad17b | 122 | struct snd_mpu401 *mpu = dev_id; |
1da177e4 | 123 | |
f1a77dc1 | 124 | if (!mpu) |
1da177e4 LT |
125 | return IRQ_NONE; |
126 | _snd_mpu401_uart_interrupt(mpu); | |
127 | return IRQ_HANDLED; | |
128 | } | |
129 | ||
2851d963 TI |
130 | EXPORT_SYMBOL(snd_mpu401_uart_interrupt); |
131 | ||
302e4c2f TI |
132 | /** |
133 | * snd_mpu401_uart_interrupt_tx - generic MPU401-UART transmit irq handler | |
134 | * @irq: the irq number | |
135 | * @dev_id: mpu401 instance | |
302e4c2f TI |
136 | * |
137 | * Processes the interrupt for MPU401-UART output. | |
eb7c06e8 YB |
138 | * |
139 | * Return: %IRQ_HANDLED if the interrupt was handled. %IRQ_NONE otherwise. | |
302e4c2f | 140 | */ |
7d12e780 | 141 | irqreturn_t snd_mpu401_uart_interrupt_tx(int irq, void *dev_id) |
302e4c2f TI |
142 | { |
143 | struct snd_mpu401 *mpu = dev_id; | |
144 | ||
f1a77dc1 | 145 | if (!mpu) |
302e4c2f TI |
146 | return IRQ_NONE; |
147 | uart_interrupt_tx(mpu); | |
148 | return IRQ_HANDLED; | |
149 | } | |
150 | ||
151 | EXPORT_SYMBOL(snd_mpu401_uart_interrupt_tx); | |
152 | ||
1da177e4 LT |
153 | /* |
154 | * timer callback | |
155 | * reprogram the timer and call the interrupt job | |
156 | */ | |
bc47ba90 | 157 | static void snd_mpu401_uart_timer(struct timer_list *t) |
1da177e4 | 158 | { |
bc47ba90 | 159 | struct snd_mpu401 *mpu = from_timer(mpu, t, timer); |
b32425ac | 160 | unsigned long flags; |
1da177e4 | 161 | |
b32425ac | 162 | spin_lock_irqsave(&mpu->timer_lock, flags); |
1da177e4 | 163 | /*mpu->mode |= MPU401_MODE_TIMER;*/ |
b093ed23 | 164 | mod_timer(&mpu->timer, 1 + jiffies); |
b32425ac | 165 | spin_unlock_irqrestore(&mpu->timer_lock, flags); |
1da177e4 LT |
166 | if (mpu->rmidi) |
167 | _snd_mpu401_uart_interrupt(mpu); | |
168 | } | |
169 | ||
170 | /* | |
171 | * initialize the timer callback if not programmed yet | |
172 | */ | |
e1fad17b | 173 | static void snd_mpu401_uart_add_timer (struct snd_mpu401 *mpu, int input) |
1da177e4 LT |
174 | { |
175 | unsigned long flags; | |
176 | ||
177 | spin_lock_irqsave (&mpu->timer_lock, flags); | |
178 | if (mpu->timer_invoked == 0) { | |
bc47ba90 | 179 | timer_setup(&mpu->timer, snd_mpu401_uart_timer, 0); |
b093ed23 | 180 | mod_timer(&mpu->timer, 1 + jiffies); |
1da177e4 | 181 | } |
2851d963 TI |
182 | mpu->timer_invoked |= input ? MPU401_MODE_INPUT_TIMER : |
183 | MPU401_MODE_OUTPUT_TIMER; | |
1da177e4 LT |
184 | spin_unlock_irqrestore (&mpu->timer_lock, flags); |
185 | } | |
186 | ||
187 | /* | |
188 | * remove the timer callback if still active | |
189 | */ | |
e1fad17b | 190 | static void snd_mpu401_uart_remove_timer (struct snd_mpu401 *mpu, int input) |
1da177e4 LT |
191 | { |
192 | unsigned long flags; | |
193 | ||
194 | spin_lock_irqsave (&mpu->timer_lock, flags); | |
195 | if (mpu->timer_invoked) { | |
2851d963 TI |
196 | mpu->timer_invoked &= input ? ~MPU401_MODE_INPUT_TIMER : |
197 | ~MPU401_MODE_OUTPUT_TIMER; | |
1da177e4 LT |
198 | if (! mpu->timer_invoked) |
199 | del_timer(&mpu->timer); | |
200 | } | |
201 | spin_unlock_irqrestore (&mpu->timer_lock, flags); | |
202 | } | |
203 | ||
204 | /* | |
2851d963 TI |
205 | * send a UART command |
206 | * return zero if successful, non-zero for some errors | |
1da177e4 LT |
207 | */ |
208 | ||
962f831f | 209 | static int snd_mpu401_uart_cmd(struct snd_mpu401 * mpu, unsigned char cmd, |
2851d963 | 210 | int ack) |
1da177e4 LT |
211 | { |
212 | unsigned long flags; | |
213 | int timeout, ok; | |
214 | ||
215 | spin_lock_irqsave(&mpu->input_lock, flags); | |
216 | if (mpu->hardware != MPU401_HW_TRID4DWAVE) { | |
217 | mpu->write(mpu, 0x00, MPU401D(mpu)); | |
218 | /*snd_mpu401_uart_clear_rx(mpu);*/ | |
219 | } | |
220 | /* ok. standard MPU-401 initialization */ | |
221 | if (mpu->hardware != MPU401_HW_SB) { | |
2851d963 TI |
222 | for (timeout = 1000; timeout > 0 && |
223 | !snd_mpu401_output_ready(mpu); timeout--) | |
1da177e4 LT |
224 | udelay(10); |
225 | #ifdef CONFIG_SND_DEBUG | |
226 | if (!timeout) | |
2851d963 TI |
227 | snd_printk(KERN_ERR "cmd: tx timeout (status = 0x%x)\n", |
228 | mpu->read(mpu, MPU401C(mpu))); | |
1da177e4 LT |
229 | #endif |
230 | } | |
231 | mpu->write(mpu, cmd, MPU401C(mpu)); | |
df7e3fdf | 232 | if (ack && !(mpu->info_flags & MPU401_INFO_NO_ACK)) { |
1da177e4 LT |
233 | ok = 0; |
234 | timeout = 10000; | |
235 | while (!ok && timeout-- > 0) { | |
236 | if (snd_mpu401_input_avail(mpu)) { | |
237 | if (mpu->read(mpu, MPU401D(mpu)) == MPU401_ACK) | |
238 | ok = 1; | |
239 | } | |
240 | } | |
241 | if (!ok && mpu->read(mpu, MPU401D(mpu)) == MPU401_ACK) | |
242 | ok = 1; | |
2851d963 | 243 | } else |
1da177e4 | 244 | ok = 1; |
1da177e4 | 245 | spin_unlock_irqrestore(&mpu->input_lock, flags); |
962f831f | 246 | if (!ok) { |
2851d963 TI |
247 | snd_printk(KERN_ERR "cmd: 0x%x failed at 0x%lx " |
248 | "(status = 0x%x, data = 0x%x)\n", cmd, mpu->port, | |
249 | mpu->read(mpu, MPU401C(mpu)), | |
250 | mpu->read(mpu, MPU401D(mpu))); | |
962f831f JM |
251 | return 1; |
252 | } | |
253 | return 0; | |
1da177e4 LT |
254 | } |
255 | ||
8f7ba051 TI |
256 | static int snd_mpu401_do_reset(struct snd_mpu401 *mpu) |
257 | { | |
258 | if (snd_mpu401_uart_cmd(mpu, MPU401_RESET, 1)) | |
259 | return -EIO; | |
c1099fcb | 260 | if (snd_mpu401_uart_cmd(mpu, MPU401_ENTER_UART, 0)) |
8f7ba051 TI |
261 | return -EIO; |
262 | return 0; | |
263 | } | |
264 | ||
1da177e4 LT |
265 | /* |
266 | * input/output open/close - protected by open_mutex in rawmidi.c | |
267 | */ | |
e1fad17b | 268 | static int snd_mpu401_uart_input_open(struct snd_rawmidi_substream *substream) |
1da177e4 | 269 | { |
e1fad17b | 270 | struct snd_mpu401 *mpu; |
1da177e4 LT |
271 | int err; |
272 | ||
273 | mpu = substream->rmidi->private_data; | |
274 | if (mpu->open_input && (err = mpu->open_input(mpu)) < 0) | |
275 | return err; | |
276 | if (! test_bit(MPU401_MODE_BIT_OUTPUT, &mpu->mode)) { | |
8f7ba051 | 277 | if (snd_mpu401_do_reset(mpu) < 0) |
962f831f | 278 | goto error_out; |
1da177e4 LT |
279 | } |
280 | mpu->substream_input = substream; | |
281 | set_bit(MPU401_MODE_BIT_INPUT, &mpu->mode); | |
282 | return 0; | |
962f831f JM |
283 | |
284 | error_out: | |
285 | if (mpu->open_input && mpu->close_input) | |
286 | mpu->close_input(mpu); | |
287 | return -EIO; | |
1da177e4 LT |
288 | } |
289 | ||
e1fad17b | 290 | static int snd_mpu401_uart_output_open(struct snd_rawmidi_substream *substream) |
1da177e4 | 291 | { |
e1fad17b | 292 | struct snd_mpu401 *mpu; |
1da177e4 LT |
293 | int err; |
294 | ||
295 | mpu = substream->rmidi->private_data; | |
296 | if (mpu->open_output && (err = mpu->open_output(mpu)) < 0) | |
297 | return err; | |
298 | if (! test_bit(MPU401_MODE_BIT_INPUT, &mpu->mode)) { | |
8f7ba051 | 299 | if (snd_mpu401_do_reset(mpu) < 0) |
962f831f | 300 | goto error_out; |
1da177e4 LT |
301 | } |
302 | mpu->substream_output = substream; | |
303 | set_bit(MPU401_MODE_BIT_OUTPUT, &mpu->mode); | |
304 | return 0; | |
962f831f JM |
305 | |
306 | error_out: | |
307 | if (mpu->open_output && mpu->close_output) | |
308 | mpu->close_output(mpu); | |
309 | return -EIO; | |
1da177e4 LT |
310 | } |
311 | ||
e1fad17b | 312 | static int snd_mpu401_uart_input_close(struct snd_rawmidi_substream *substream) |
1da177e4 | 313 | { |
e1fad17b | 314 | struct snd_mpu401 *mpu; |
962f831f | 315 | int err = 0; |
1da177e4 LT |
316 | |
317 | mpu = substream->rmidi->private_data; | |
318 | clear_bit(MPU401_MODE_BIT_INPUT, &mpu->mode); | |
319 | mpu->substream_input = NULL; | |
320 | if (! test_bit(MPU401_MODE_BIT_OUTPUT, &mpu->mode)) | |
962f831f | 321 | err = snd_mpu401_uart_cmd(mpu, MPU401_RESET, 0); |
1da177e4 LT |
322 | if (mpu->close_input) |
323 | mpu->close_input(mpu); | |
962f831f JM |
324 | if (err) |
325 | return -EIO; | |
1da177e4 LT |
326 | return 0; |
327 | } | |
328 | ||
e1fad17b | 329 | static int snd_mpu401_uart_output_close(struct snd_rawmidi_substream *substream) |
1da177e4 | 330 | { |
e1fad17b | 331 | struct snd_mpu401 *mpu; |
962f831f | 332 | int err = 0; |
1da177e4 LT |
333 | |
334 | mpu = substream->rmidi->private_data; | |
335 | clear_bit(MPU401_MODE_BIT_OUTPUT, &mpu->mode); | |
336 | mpu->substream_output = NULL; | |
337 | if (! test_bit(MPU401_MODE_BIT_INPUT, &mpu->mode)) | |
962f831f | 338 | err = snd_mpu401_uart_cmd(mpu, MPU401_RESET, 0); |
1da177e4 LT |
339 | if (mpu->close_output) |
340 | mpu->close_output(mpu); | |
962f831f JM |
341 | if (err) |
342 | return -EIO; | |
1da177e4 LT |
343 | return 0; |
344 | } | |
345 | ||
346 | /* | |
347 | * trigger input callback | |
348 | */ | |
2851d963 TI |
349 | static void |
350 | snd_mpu401_uart_input_trigger(struct snd_rawmidi_substream *substream, int up) | |
1da177e4 LT |
351 | { |
352 | unsigned long flags; | |
e1fad17b | 353 | struct snd_mpu401 *mpu; |
1da177e4 LT |
354 | int max = 64; |
355 | ||
356 | mpu = substream->rmidi->private_data; | |
357 | if (up) { | |
2851d963 TI |
358 | if (! test_and_set_bit(MPU401_MODE_BIT_INPUT_TRIGGER, |
359 | &mpu->mode)) { | |
1da177e4 LT |
360 | /* first time - flush FIFO */ |
361 | while (max-- > 0) | |
362 | mpu->read(mpu, MPU401D(mpu)); | |
dba8b469 | 363 | if (mpu->info_flags & MPU401_INFO_USE_TIMER) |
1da177e4 LT |
364 | snd_mpu401_uart_add_timer(mpu, 1); |
365 | } | |
366 | ||
367 | /* read data in advance */ | |
368 | spin_lock_irqsave(&mpu->input_lock, flags); | |
369 | snd_mpu401_uart_input_read(mpu); | |
370 | spin_unlock_irqrestore(&mpu->input_lock, flags); | |
371 | } else { | |
dba8b469 | 372 | if (mpu->info_flags & MPU401_INFO_USE_TIMER) |
1da177e4 LT |
373 | snd_mpu401_uart_remove_timer(mpu, 1); |
374 | clear_bit(MPU401_MODE_BIT_INPUT_TRIGGER, &mpu->mode); | |
375 | } | |
962f831f | 376 | |
1da177e4 LT |
377 | } |
378 | ||
379 | /* | |
380 | * transfer input pending data | |
381 | * call with input_lock spinlock held | |
382 | */ | |
e1fad17b | 383 | static void snd_mpu401_uart_input_read(struct snd_mpu401 * mpu) |
1da177e4 LT |
384 | { |
385 | int max = 128; | |
386 | unsigned char byte; | |
387 | ||
388 | while (max-- > 0) { | |
2851d963 | 389 | if (! snd_mpu401_input_avail(mpu)) |
1da177e4 | 390 | break; /* input not available */ |
2851d963 TI |
391 | byte = mpu->read(mpu, MPU401D(mpu)); |
392 | if (test_bit(MPU401_MODE_BIT_INPUT_TRIGGER, &mpu->mode)) | |
393 | snd_rawmidi_receive(mpu->substream_input, &byte, 1); | |
1da177e4 LT |
394 | } |
395 | } | |
396 | ||
397 | /* | |
398 | * Tx FIFO sizes: | |
399 | * CS4237B - 16 bytes | |
400 | * AudioDrive ES1688 - 12 bytes | |
401 | * S3 SonicVibes - 8 bytes | |
402 | * SoundBlaster AWE 64 - 2 bytes (ugly hardware) | |
403 | */ | |
404 | ||
405 | /* | |
406 | * write output pending bytes | |
407 | * call with output_lock spinlock held | |
408 | */ | |
e1fad17b | 409 | static void snd_mpu401_uart_output_write(struct snd_mpu401 * mpu) |
1da177e4 LT |
410 | { |
411 | unsigned char byte; | |
ea6b5828 | 412 | int max = 256; |
1da177e4 LT |
413 | |
414 | do { | |
2851d963 TI |
415 | if (snd_rawmidi_transmit_peek(mpu->substream_output, |
416 | &byte, 1) == 1) { | |
ea6b5828 CL |
417 | /* |
418 | * Try twice because there is hardware that insists on | |
419 | * setting the output busy bit after each write. | |
420 | */ | |
421 | if (!snd_mpu401_output_ready(mpu) && | |
422 | !snd_mpu401_output_ready(mpu)) | |
1da177e4 | 423 | break; /* Tx FIFO full - try again later */ |
2851d963 TI |
424 | mpu->write(mpu, byte, MPU401D(mpu)); |
425 | snd_rawmidi_transmit_ack(mpu->substream_output, 1); | |
1da177e4 LT |
426 | } else { |
427 | snd_mpu401_uart_remove_timer (mpu, 0); | |
428 | break; /* no other data - leave the tx loop */ | |
429 | } | |
430 | } while (--max > 0); | |
431 | } | |
432 | ||
433 | /* | |
434 | * output trigger callback | |
435 | */ | |
2851d963 TI |
436 | static void |
437 | snd_mpu401_uart_output_trigger(struct snd_rawmidi_substream *substream, int up) | |
1da177e4 LT |
438 | { |
439 | unsigned long flags; | |
e1fad17b | 440 | struct snd_mpu401 *mpu; |
1da177e4 LT |
441 | |
442 | mpu = substream->rmidi->private_data; | |
443 | if (up) { | |
444 | set_bit(MPU401_MODE_BIT_OUTPUT_TRIGGER, &mpu->mode); | |
445 | ||
446 | /* try to add the timer at each output trigger, | |
447 | * since the output timer might have been removed in | |
448 | * snd_mpu401_uart_output_write(). | |
449 | */ | |
302e4c2f TI |
450 | if (! (mpu->info_flags & MPU401_INFO_TX_IRQ)) |
451 | snd_mpu401_uart_add_timer(mpu, 0); | |
1da177e4 LT |
452 | |
453 | /* output pending data */ | |
454 | spin_lock_irqsave(&mpu->output_lock, flags); | |
455 | snd_mpu401_uart_output_write(mpu); | |
456 | spin_unlock_irqrestore(&mpu->output_lock, flags); | |
457 | } else { | |
302e4c2f TI |
458 | if (! (mpu->info_flags & MPU401_INFO_TX_IRQ)) |
459 | snd_mpu401_uart_remove_timer(mpu, 0); | |
1da177e4 LT |
460 | clear_bit(MPU401_MODE_BIT_OUTPUT_TRIGGER, &mpu->mode); |
461 | } | |
462 | } | |
463 | ||
464 | /* | |
465 | ||
466 | */ | |
467 | ||
c36f486d | 468 | static const struct snd_rawmidi_ops snd_mpu401_uart_output = |
1da177e4 LT |
469 | { |
470 | .open = snd_mpu401_uart_output_open, | |
471 | .close = snd_mpu401_uart_output_close, | |
472 | .trigger = snd_mpu401_uart_output_trigger, | |
473 | }; | |
474 | ||
c36f486d | 475 | static const struct snd_rawmidi_ops snd_mpu401_uart_input = |
1da177e4 LT |
476 | { |
477 | .open = snd_mpu401_uart_input_open, | |
478 | .close = snd_mpu401_uart_input_close, | |
479 | .trigger = snd_mpu401_uart_input_trigger, | |
480 | }; | |
481 | ||
e1fad17b | 482 | static void snd_mpu401_uart_free(struct snd_rawmidi *rmidi) |
1da177e4 | 483 | { |
e1fad17b | 484 | struct snd_mpu401 *mpu = rmidi->private_data; |
dba8b469 | 485 | if (mpu->irq >= 0) |
1da177e4 | 486 | free_irq(mpu->irq, (void *) mpu); |
b1d5776d | 487 | release_and_free_resource(mpu->res); |
1da177e4 LT |
488 | kfree(mpu); |
489 | } | |
490 | ||
491 | /** | |
492 | * snd_mpu401_uart_new - create an MPU401-UART instance | |
493 | * @card: the card instance | |
494 | * @device: the device index, zero-based | |
495 | * @hardware: the hardware type, MPU401_HW_XXXX | |
496 | * @port: the base address of MPU401 port | |
302e4c2f | 497 | * @info_flags: bitflags MPU401_INFO_XXX |
dba8b469 | 498 | * @irq: the ISA irq number, -1 if not to be allocated |
1da177e4 LT |
499 | * @rrawmidi: the pointer to store the new rawmidi instance |
500 | * | |
501 | * Creates a new MPU-401 instance. | |
502 | * | |
503 | * Note that the rawmidi instance is returned on the rrawmidi argument, | |
504 | * not the mpu401 instance itself. To access to the mpu401 instance, | |
e1fad17b | 505 | * cast from rawmidi->private_data (with struct snd_mpu401 magic-cast). |
1da177e4 | 506 | * |
eb7c06e8 | 507 | * Return: Zero if successful, or a negative error code. |
1da177e4 | 508 | */ |
e1fad17b | 509 | int snd_mpu401_uart_new(struct snd_card *card, int device, |
1da177e4 | 510 | unsigned short hardware, |
302e4c2f TI |
511 | unsigned long port, |
512 | unsigned int info_flags, | |
dba8b469 | 513 | int irq, |
e1fad17b | 514 | struct snd_rawmidi ** rrawmidi) |
1da177e4 | 515 | { |
e1fad17b TI |
516 | struct snd_mpu401 *mpu; |
517 | struct snd_rawmidi *rmidi; | |
302e4c2f | 518 | int in_enable, out_enable; |
1da177e4 LT |
519 | int err; |
520 | ||
521 | if (rrawmidi) | |
522 | *rrawmidi = NULL; | |
302e4c2f TI |
523 | if (! (info_flags & (MPU401_INFO_INPUT | MPU401_INFO_OUTPUT))) |
524 | info_flags |= MPU401_INFO_INPUT | MPU401_INFO_OUTPUT; | |
525 | in_enable = (info_flags & MPU401_INFO_INPUT) ? 1 : 0; | |
526 | out_enable = (info_flags & MPU401_INFO_OUTPUT) ? 1 : 0; | |
527 | if ((err = snd_rawmidi_new(card, "MPU-401U", device, | |
528 | out_enable, in_enable, &rmidi)) < 0) | |
1da177e4 | 529 | return err; |
561b220a | 530 | mpu = kzalloc(sizeof(*mpu), GFP_KERNEL); |
f1a77dc1 | 531 | if (!mpu) { |
ec1f43c8 ME |
532 | err = -ENOMEM; |
533 | goto free_device; | |
1da177e4 LT |
534 | } |
535 | rmidi->private_data = mpu; | |
536 | rmidi->private_free = snd_mpu401_uart_free; | |
537 | spin_lock_init(&mpu->input_lock); | |
538 | spin_lock_init(&mpu->output_lock); | |
539 | spin_lock_init(&mpu->timer_lock); | |
540 | mpu->hardware = hardware; | |
bc733d49 | 541 | mpu->irq = -1; |
302e4c2f | 542 | if (! (info_flags & MPU401_INFO_INTEGRATED)) { |
1da177e4 | 543 | int res_size = hardware == MPU401_HW_PC98II ? 4 : 2; |
2851d963 | 544 | mpu->res = request_region(port, res_size, "MPU401 UART"); |
f1a77dc1 | 545 | if (!mpu->res) { |
2851d963 TI |
546 | snd_printk(KERN_ERR "mpu401_uart: " |
547 | "unable to grab port 0x%lx size %d\n", | |
548 | port, res_size); | |
ec1f43c8 ME |
549 | err = -EBUSY; |
550 | goto free_device; | |
1da177e4 LT |
551 | } |
552 | } | |
302e4c2f | 553 | if (info_flags & MPU401_INFO_MMIO) { |
1da177e4 LT |
554 | mpu->write = mpu401_write_mmio; |
555 | mpu->read = mpu401_read_mmio; | |
302e4c2f | 556 | } else { |
1da177e4 LT |
557 | mpu->write = mpu401_write_port; |
558 | mpu->read = mpu401_read_port; | |
1da177e4 LT |
559 | } |
560 | mpu->port = port; | |
561 | if (hardware == MPU401_HW_PC98II) | |
562 | mpu->cport = port + 2; | |
563 | else | |
564 | mpu->cport = port + 1; | |
dba8b469 | 565 | if (irq >= 0) { |
88e24c3a | 566 | if (request_irq(irq, snd_mpu401_uart_interrupt, 0, |
2851d963 TI |
567 | "MPU401 UART", (void *) mpu)) { |
568 | snd_printk(KERN_ERR "mpu401_uart: " | |
569 | "unable to grab IRQ %d\n", irq); | |
ec1f43c8 ME |
570 | err = -EBUSY; |
571 | goto free_device; | |
1da177e4 LT |
572 | } |
573 | } | |
dba8b469 CL |
574 | if (irq < 0 && !(info_flags & MPU401_INFO_IRQ_HOOK)) |
575 | info_flags |= MPU401_INFO_USE_TIMER; | |
302e4c2f | 576 | mpu->info_flags = info_flags; |
1da177e4 | 577 | mpu->irq = irq; |
1da177e4 | 578 | if (card->shortname[0]) |
2851d963 TI |
579 | snprintf(rmidi->name, sizeof(rmidi->name), "%s MIDI", |
580 | card->shortname); | |
1da177e4 | 581 | else |
2851d963 | 582 | sprintf(rmidi->name, "MPU-401 MIDI %d-%d",card->number, device); |
302e4c2f TI |
583 | if (out_enable) { |
584 | snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, | |
585 | &snd_mpu401_uart_output); | |
586 | rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT; | |
587 | } | |
588 | if (in_enable) { | |
589 | snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, | |
590 | &snd_mpu401_uart_input); | |
591 | rmidi->info_flags |= SNDRV_RAWMIDI_INFO_INPUT; | |
592 | if (out_enable) | |
593 | rmidi->info_flags |= SNDRV_RAWMIDI_INFO_DUPLEX; | |
594 | } | |
1da177e4 LT |
595 | mpu->rmidi = rmidi; |
596 | if (rrawmidi) | |
597 | *rrawmidi = rmidi; | |
598 | return 0; | |
ec1f43c8 ME |
599 | free_device: |
600 | snd_device_free(card, rmidi); | |
601 | return err; | |
1da177e4 LT |
602 | } |
603 | ||
1da177e4 | 604 | EXPORT_SYMBOL(snd_mpu401_uart_new); |