diff options
Diffstat (limited to 'sound/drivers/mtpav.c')
-rw-r--r-- | sound/drivers/mtpav.c | 795 |
1 files changed, 795 insertions, 0 deletions
diff --git a/sound/drivers/mtpav.c b/sound/drivers/mtpav.c new file mode 100644 index 000000000000..1280a57c49eb --- /dev/null +++ b/sound/drivers/mtpav.c | |||
@@ -0,0 +1,795 @@ | |||
1 | /* | ||
2 | * MOTU Midi Timepiece ALSA Main routines | ||
3 | * Copyright by Michael T. Mayers (c) Jan 09, 2000 | ||
4 | * mail: michael@tweakoz.com | ||
5 | * Thanks to John Galbraith | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License as published by | ||
9 | * the Free Software Foundation; either version 2 of the License, or | ||
10 | * (at your option) any later version. | ||
11 | * | ||
12 | * This program is distributed in the hope that it will be useful, | ||
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
15 | * GNU General Public License for more details. | ||
16 | * | ||
17 | * You should have received a copy of the GNU General Public License | ||
18 | * along with this program; if not, write to the Free Software | ||
19 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
20 | * | ||
21 | * | ||
22 | * This driver is for the 'Mark Of The Unicorn' (MOTU) | ||
23 | * MidiTimePiece AV multiport MIDI interface | ||
24 | * | ||
25 | * IOPORTS | ||
26 | * ------- | ||
27 | * 8 MIDI Ins and 8 MIDI outs | ||
28 | * Video Sync In (BNC), Word Sync Out (BNC), | ||
29 | * ADAT Sync Out (DB9) | ||
30 | * SMPTE in/out (1/4") | ||
31 | * 2 programmable pedal/footswitch inputs and 4 programmable MIDI controller knobs. | ||
32 | * Macintosh RS422 serial port | ||
33 | * RS422 "network" port for ganging multiple MTP's | ||
34 | * PC Parallel Port ( which this driver currently uses ) | ||
35 | * | ||
36 | * MISC FEATURES | ||
37 | * ------------- | ||
38 | * Hardware MIDI routing, merging, and filtering | ||
39 | * MIDI Synchronization to Video, ADAT, SMPTE and other Clock sources | ||
40 | * 128 'scene' memories, recallable from MIDI program change | ||
41 | * | ||
42 | * | ||
43 | * ChangeLog | ||
44 | * Jun 11 2001 Takashi Iwai <tiwai@suse.de> | ||
45 | * - Recoded & debugged | ||
46 | * - Added timer interrupt for midi outputs | ||
47 | * - hwports is between 1 and 8, which specifies the number of hardware ports. | ||
48 | * The three global ports, computer, adat and broadcast ports, are created | ||
49 | * always after h/w and remote ports. | ||
50 | * | ||
51 | */ | ||
52 | |||
53 | #include <sound/driver.h> | ||
54 | #include <linux/init.h> | ||
55 | #include <linux/interrupt.h> | ||
56 | #include <linux/slab.h> | ||
57 | #include <linux/ioport.h> | ||
58 | #include <linux/moduleparam.h> | ||
59 | #include <sound/core.h> | ||
60 | #include <sound/initval.h> | ||
61 | #include <sound/rawmidi.h> | ||
62 | #include <linux/delay.h> | ||
63 | |||
64 | #include <asm/io.h> | ||
65 | |||
66 | /* | ||
67 | * globals | ||
68 | */ | ||
69 | MODULE_AUTHOR("Michael T. Mayers"); | ||
70 | MODULE_DESCRIPTION("MOTU MidiTimePiece AV multiport MIDI"); | ||
71 | MODULE_LICENSE("GPL"); | ||
72 | MODULE_SUPPORTED_DEVICE("{{MOTU,MidiTimePiece AV multiport MIDI}}"); | ||
73 | |||
74 | // io resources | ||
75 | #define MTPAV_IOBASE 0x378 | ||
76 | #define MTPAV_IRQ 7 | ||
77 | #define MTPAV_MAX_PORTS 8 | ||
78 | |||
79 | static int index = SNDRV_DEFAULT_IDX1; | ||
80 | static char *id = SNDRV_DEFAULT_STR1; | ||
81 | static long port = MTPAV_IOBASE; /* 0x378, 0x278 */ | ||
82 | static int irq = MTPAV_IRQ; /* 7, 5 */ | ||
83 | static int hwports = MTPAV_MAX_PORTS; /* use hardware ports 1-8 */ | ||
84 | |||
85 | module_param(index, int, 0444); | ||
86 | MODULE_PARM_DESC(index, "Index value for MotuMTPAV MIDI."); | ||
87 | module_param(id, charp, 0444); | ||
88 | MODULE_PARM_DESC(id, "ID string for MotuMTPAV MIDI."); | ||
89 | module_param(port, long, 0444); | ||
90 | MODULE_PARM_DESC(port, "Parallel port # for MotuMTPAV MIDI."); | ||
91 | module_param(irq, int, 0444); | ||
92 | MODULE_PARM_DESC(irq, "Parallel IRQ # for MotuMTPAV MIDI."); | ||
93 | module_param(hwports, int, 0444); | ||
94 | MODULE_PARM_DESC(hwports, "Hardware ports # for MotuMTPAV MIDI."); | ||
95 | |||
96 | /* | ||
97 | * defines | ||
98 | */ | ||
99 | //#define USE_FAKE_MTP // don't actually read/write to MTP device (for debugging without an actual unit) (does not work yet) | ||
100 | |||
101 | // parallel port usage masks | ||
102 | #define SIGS_BYTE 0x08 | ||
103 | #define SIGS_RFD 0x80 | ||
104 | #define SIGS_IRQ 0x40 | ||
105 | #define SIGS_IN0 0x10 | ||
106 | #define SIGS_IN1 0x20 | ||
107 | |||
108 | #define SIGC_WRITE 0x04 | ||
109 | #define SIGC_READ 0x08 | ||
110 | #define SIGC_INTEN 0x10 | ||
111 | |||
112 | #define DREG 0 | ||
113 | #define SREG 1 | ||
114 | #define CREG 2 | ||
115 | |||
116 | // | ||
117 | #define MTPAV_MODE_INPUT_OPENED 0x01 | ||
118 | #define MTPAV_MODE_OUTPUT_OPENED 0x02 | ||
119 | #define MTPAV_MODE_INPUT_TRIGGERED 0x04 | ||
120 | #define MTPAV_MODE_OUTPUT_TRIGGERED 0x08 | ||
121 | |||
122 | #define NUMPORTS (0x12+1) | ||
123 | |||
124 | |||
125 | /* | ||
126 | */ | ||
127 | |||
128 | typedef struct mtpav_port { | ||
129 | u8 number; | ||
130 | u8 hwport; | ||
131 | u8 mode; | ||
132 | u8 running_status; | ||
133 | snd_rawmidi_substream_t *input; | ||
134 | snd_rawmidi_substream_t *output; | ||
135 | } mtpav_port_t; | ||
136 | |||
137 | typedef struct mtpav { | ||
138 | snd_card_t *card; | ||
139 | unsigned long port; | ||
140 | struct resource *res_port; | ||
141 | int irq; /* interrupt (for inputs) */ | ||
142 | spinlock_t spinlock; | ||
143 | int share_irq; /* number of accesses to input interrupts */ | ||
144 | int istimer; /* number of accesses to timer interrupts */ | ||
145 | struct timer_list timer; /* timer interrupts for outputs */ | ||
146 | snd_rawmidi_t *rmidi; | ||
147 | int num_ports; /* number of hw ports (1-8) */ | ||
148 | mtpav_port_t ports[NUMPORTS]; /* all ports including computer, adat and bc */ | ||
149 | |||
150 | u32 inmidiport; /* selected input midi port */ | ||
151 | u32 inmidistate; /* during midi command 0xf5 */ | ||
152 | |||
153 | u32 outmidihwport; /* selected output midi hw port */ | ||
154 | } mtpav_t; | ||
155 | |||
156 | |||
157 | /* | ||
158 | * global instance | ||
159 | * hey, we handle at most only one card.. | ||
160 | */ | ||
161 | static mtpav_t *mtp_card; | ||
162 | |||
163 | /* | ||
164 | * possible hardware ports (selected by 0xf5 port message) | ||
165 | * 0x00 all ports | ||
166 | * 0x01 .. 0x08 this MTP's ports 1..8 | ||
167 | * 0x09 .. 0x10 networked MTP's ports (9..16) | ||
168 | * 0x11 networked MTP's computer port | ||
169 | * 0x63 to ADAT | ||
170 | * | ||
171 | * mappig: | ||
172 | * subdevice 0 - (X-1) ports | ||
173 | * X - (2*X-1) networked ports | ||
174 | * X computer | ||
175 | * X+1 ADAT | ||
176 | * X+2 all ports | ||
177 | * | ||
178 | * where X = chip->num_ports | ||
179 | */ | ||
180 | |||
181 | #define MTPAV_PIDX_COMPUTER 0 | ||
182 | #define MTPAV_PIDX_ADAT 1 | ||
183 | #define MTPAV_PIDX_BROADCAST 2 | ||
184 | |||
185 | |||
186 | static int translate_subdevice_to_hwport(mtpav_t *chip, int subdev) | ||
187 | { | ||
188 | if (subdev < 0) | ||
189 | return 0x01; /* invalid - use port 0 as default */ | ||
190 | else if (subdev < chip->num_ports) | ||
191 | return subdev + 1; /* single mtp port */ | ||
192 | else if (subdev < chip->num_ports * 2) | ||
193 | return subdev - chip->num_ports + 0x09; /* remote port */ | ||
194 | else if (subdev == chip->num_ports * 2 + MTPAV_PIDX_COMPUTER) | ||
195 | return 0x11; /* computer port */ | ||
196 | else if (subdev == chip->num_ports + MTPAV_PIDX_ADAT) | ||
197 | return 0x63; /* ADAT */ | ||
198 | return 0; /* all ports */ | ||
199 | } | ||
200 | |||
201 | static int translate_hwport_to_subdevice(mtpav_t *chip, int hwport) | ||
202 | { | ||
203 | int p; | ||
204 | if (hwport <= 0x00) /* all ports */ | ||
205 | return chip->num_ports + MTPAV_PIDX_BROADCAST; | ||
206 | else if (hwport <= 0x08) { /* single port */ | ||
207 | p = hwport - 1; | ||
208 | if (p >= chip->num_ports) | ||
209 | p = 0; | ||
210 | return p; | ||
211 | } else if (hwport <= 0x10) { /* remote port */ | ||
212 | p = hwport - 0x09 + chip->num_ports; | ||
213 | if (p >= chip->num_ports * 2) | ||
214 | p = chip->num_ports; | ||
215 | return p; | ||
216 | } else if (hwport == 0x11) /* computer port */ | ||
217 | return chip->num_ports + MTPAV_PIDX_COMPUTER; | ||
218 | else /* ADAT */ | ||
219 | return chip->num_ports + MTPAV_PIDX_ADAT; | ||
220 | } | ||
221 | |||
222 | |||
223 | /* | ||
224 | */ | ||
225 | |||
226 | static u8 snd_mtpav_getreg(mtpav_t *chip, u16 reg) | ||
227 | { | ||
228 | u8 rval = 0; | ||
229 | |||
230 | if (reg == SREG) { | ||
231 | rval = inb(chip->port + SREG); | ||
232 | rval = (rval & 0xf8); | ||
233 | } else if (reg == CREG) { | ||
234 | rval = inb(chip->port + CREG); | ||
235 | rval = (rval & 0x1c); | ||
236 | } | ||
237 | |||
238 | return rval; | ||
239 | } | ||
240 | |||
241 | /* | ||
242 | */ | ||
243 | |||
244 | static void snd_mtpav_mputreg(mtpav_t *chip, u16 reg, u8 val) | ||
245 | { | ||
246 | if (reg == DREG) { | ||
247 | outb(val, chip->port + DREG); | ||
248 | } else if (reg == CREG) { | ||
249 | outb(val, chip->port + CREG); | ||
250 | } | ||
251 | } | ||
252 | |||
253 | /* | ||
254 | */ | ||
255 | |||
256 | static void snd_mtpav_wait_rfdhi(mtpav_t *chip) | ||
257 | { | ||
258 | int counts = 10000; | ||
259 | u8 sbyte; | ||
260 | |||
261 | sbyte = snd_mtpav_getreg(chip, SREG); | ||
262 | while (!(sbyte & SIGS_RFD) && counts--) { | ||
263 | sbyte = snd_mtpav_getreg(chip, SREG); | ||
264 | udelay(10); | ||
265 | } | ||
266 | } | ||
267 | |||
268 | static void snd_mtpav_send_byte(mtpav_t *chip, u8 byte) | ||
269 | { | ||
270 | u8 tcbyt; | ||
271 | u8 clrwrite; | ||
272 | u8 setwrite; | ||
273 | |||
274 | snd_mtpav_wait_rfdhi(chip); | ||
275 | |||
276 | ///////////////// | ||
277 | |||
278 | tcbyt = snd_mtpav_getreg(chip, CREG); | ||
279 | clrwrite = tcbyt & (SIGC_WRITE ^ 0xff); | ||
280 | setwrite = tcbyt | SIGC_WRITE; | ||
281 | |||
282 | snd_mtpav_mputreg(chip, DREG, byte); | ||
283 | snd_mtpav_mputreg(chip, CREG, clrwrite); // clear write bit | ||
284 | |||
285 | snd_mtpav_mputreg(chip, CREG, setwrite); // set write bit | ||
286 | |||
287 | } | ||
288 | |||
289 | |||
290 | /* | ||
291 | */ | ||
292 | |||
293 | /* call this with spin lock held */ | ||
294 | static void snd_mtpav_output_port_write(mtpav_port_t *port, | ||
295 | snd_rawmidi_substream_t *substream) | ||
296 | { | ||
297 | u8 outbyte; | ||
298 | |||
299 | // Get the outbyte first, so we can emulate running status if | ||
300 | // necessary | ||
301 | if (snd_rawmidi_transmit(substream, &outbyte, 1) != 1) | ||
302 | return; | ||
303 | |||
304 | // send port change command if necessary | ||
305 | |||
306 | if (port->hwport != mtp_card->outmidihwport) { | ||
307 | mtp_card->outmidihwport = port->hwport; | ||
308 | |||
309 | snd_mtpav_send_byte(mtp_card, 0xf5); | ||
310 | snd_mtpav_send_byte(mtp_card, port->hwport); | ||
311 | //snd_printk("new outport: 0x%x\n", (unsigned int) port->hwport); | ||
312 | |||
313 | if (!(outbyte & 0x80) && port->running_status) | ||
314 | snd_mtpav_send_byte(mtp_card, port->running_status); | ||
315 | } | ||
316 | |||
317 | // send data | ||
318 | |||
319 | do { | ||
320 | if (outbyte & 0x80) | ||
321 | port->running_status = outbyte; | ||
322 | |||
323 | snd_mtpav_send_byte(mtp_card, outbyte); | ||
324 | } while (snd_rawmidi_transmit(substream, &outbyte, 1) == 1); | ||
325 | } | ||
326 | |||
327 | static void snd_mtpav_output_write(snd_rawmidi_substream_t * substream) | ||
328 | { | ||
329 | mtpav_port_t *port = &mtp_card->ports[substream->number]; | ||
330 | unsigned long flags; | ||
331 | |||
332 | spin_lock_irqsave(&mtp_card->spinlock, flags); | ||
333 | snd_mtpav_output_port_write(port, substream); | ||
334 | spin_unlock_irqrestore(&mtp_card->spinlock, flags); | ||
335 | } | ||
336 | |||
337 | |||
338 | /* | ||
339 | * mtpav control | ||
340 | */ | ||
341 | |||
342 | static void snd_mtpav_portscan(mtpav_t *chip) // put mtp into smart routing mode | ||
343 | { | ||
344 | u8 p; | ||
345 | |||
346 | for (p = 0; p < 8; p++) { | ||
347 | snd_mtpav_send_byte(chip, 0xf5); | ||
348 | snd_mtpav_send_byte(chip, p); | ||
349 | snd_mtpav_send_byte(chip, 0xfe); | ||
350 | } | ||
351 | } | ||
352 | |||
353 | /* | ||
354 | */ | ||
355 | |||
356 | static int snd_mtpav_input_open(snd_rawmidi_substream_t * substream) | ||
357 | { | ||
358 | unsigned long flags; | ||
359 | mtpav_port_t *portp = &mtp_card->ports[substream->number]; | ||
360 | |||
361 | //printk("mtpav port: %d opened\n", (int) substream->number); | ||
362 | spin_lock_irqsave(&mtp_card->spinlock, flags); | ||
363 | portp->mode |= MTPAV_MODE_INPUT_OPENED; | ||
364 | portp->input = substream; | ||
365 | if (mtp_card->share_irq++ == 0) | ||
366 | snd_mtpav_mputreg(mtp_card, CREG, (SIGC_INTEN | SIGC_WRITE)); // enable pport interrupts | ||
367 | spin_unlock_irqrestore(&mtp_card->spinlock, flags); | ||
368 | return 0; | ||
369 | } | ||
370 | |||
371 | /* | ||
372 | */ | ||
373 | |||
374 | static int snd_mtpav_input_close(snd_rawmidi_substream_t *substream) | ||
375 | { | ||
376 | unsigned long flags; | ||
377 | mtpav_port_t *portp = &mtp_card->ports[substream->number]; | ||
378 | |||
379 | //printk("mtpav port: %d closed\n", (int) portp); | ||
380 | |||
381 | spin_lock_irqsave(&mtp_card->spinlock, flags); | ||
382 | |||
383 | portp->mode &= (~MTPAV_MODE_INPUT_OPENED); | ||
384 | portp->input = NULL; | ||
385 | if (--mtp_card->share_irq == 0) | ||
386 | snd_mtpav_mputreg(mtp_card, CREG, 0); // disable pport interrupts | ||
387 | |||
388 | spin_unlock_irqrestore(&mtp_card->spinlock, flags); | ||
389 | return 0; | ||
390 | } | ||
391 | |||
392 | /* | ||
393 | */ | ||
394 | |||
395 | static void snd_mtpav_input_trigger(snd_rawmidi_substream_t * substream, int up) | ||
396 | { | ||
397 | unsigned long flags; | ||
398 | mtpav_port_t *portp = &mtp_card->ports[substream->number]; | ||
399 | |||
400 | spin_lock_irqsave(&mtp_card->spinlock, flags); | ||
401 | if (up) | ||
402 | portp->mode |= MTPAV_MODE_INPUT_TRIGGERED; | ||
403 | else | ||
404 | portp->mode &= ~MTPAV_MODE_INPUT_TRIGGERED; | ||
405 | spin_unlock_irqrestore(&mtp_card->spinlock, flags); | ||
406 | |||
407 | } | ||
408 | |||
409 | |||
410 | /* | ||
411 | * timer interrupt for outputs | ||
412 | */ | ||
413 | |||
414 | static void snd_mtpav_output_timer(unsigned long data) | ||
415 | { | ||
416 | unsigned long flags; | ||
417 | mtpav_t *chip = (mtpav_t *)data; | ||
418 | int p; | ||
419 | |||
420 | spin_lock_irqsave(&chip->spinlock, flags); | ||
421 | /* reprogram timer */ | ||
422 | chip->timer.expires = 1 + jiffies; | ||
423 | add_timer(&chip->timer); | ||
424 | /* process each port */ | ||
425 | for (p = 0; p <= chip->num_ports * 2 + MTPAV_PIDX_BROADCAST; p++) { | ||
426 | mtpav_port_t *portp = &mtp_card->ports[p]; | ||
427 | if ((portp->mode & MTPAV_MODE_OUTPUT_TRIGGERED) && portp->output) | ||
428 | snd_mtpav_output_port_write(portp, portp->output); | ||
429 | } | ||
430 | spin_unlock_irqrestore(&chip->spinlock, flags); | ||
431 | } | ||
432 | |||
433 | /* spinlock held! */ | ||
434 | static void snd_mtpav_add_output_timer(mtpav_t *chip) | ||
435 | { | ||
436 | init_timer(&chip->timer); | ||
437 | chip->timer.function = snd_mtpav_output_timer; | ||
438 | chip->timer.data = (unsigned long) mtp_card; | ||
439 | chip->timer.expires = 1 + jiffies; | ||
440 | add_timer(&chip->timer); | ||
441 | } | ||
442 | |||
443 | /* spinlock held! */ | ||
444 | static void snd_mtpav_remove_output_timer(mtpav_t *chip) | ||
445 | { | ||
446 | del_timer(&chip->timer); | ||
447 | } | ||
448 | |||
449 | /* | ||
450 | */ | ||
451 | |||
452 | static int snd_mtpav_output_open(snd_rawmidi_substream_t * substream) | ||
453 | { | ||
454 | unsigned long flags; | ||
455 | mtpav_port_t *portp = &mtp_card->ports[substream->number]; | ||
456 | |||
457 | spin_lock_irqsave(&mtp_card->spinlock, flags); | ||
458 | portp->mode |= MTPAV_MODE_OUTPUT_OPENED; | ||
459 | portp->output = substream; | ||
460 | spin_unlock_irqrestore(&mtp_card->spinlock, flags); | ||
461 | return 0; | ||
462 | }; | ||
463 | |||
464 | /* | ||
465 | */ | ||
466 | |||
467 | static int snd_mtpav_output_close(snd_rawmidi_substream_t * substream) | ||
468 | { | ||
469 | unsigned long flags; | ||
470 | mtpav_port_t *portp = &mtp_card->ports[substream->number]; | ||
471 | |||
472 | spin_lock_irqsave(&mtp_card->spinlock, flags); | ||
473 | portp->mode &= (~MTPAV_MODE_OUTPUT_OPENED); | ||
474 | portp->output = NULL; | ||
475 | spin_unlock_irqrestore(&mtp_card->spinlock, flags); | ||
476 | return 0; | ||
477 | }; | ||
478 | |||
479 | /* | ||
480 | */ | ||
481 | |||
482 | static void snd_mtpav_output_trigger(snd_rawmidi_substream_t * substream, int up) | ||
483 | { | ||
484 | unsigned long flags; | ||
485 | mtpav_port_t *portp = &mtp_card->ports[substream->number]; | ||
486 | |||
487 | spin_lock_irqsave(&mtp_card->spinlock, flags); | ||
488 | if (up) { | ||
489 | if (! (portp->mode & MTPAV_MODE_OUTPUT_TRIGGERED)) { | ||
490 | if (mtp_card->istimer++ == 0) | ||
491 | snd_mtpav_add_output_timer(mtp_card); | ||
492 | portp->mode |= MTPAV_MODE_OUTPUT_TRIGGERED; | ||
493 | } | ||
494 | } else { | ||
495 | portp->mode &= ~MTPAV_MODE_OUTPUT_TRIGGERED; | ||
496 | if (--mtp_card->istimer == 0) | ||
497 | snd_mtpav_remove_output_timer(mtp_card); | ||
498 | } | ||
499 | spin_unlock_irqrestore(&mtp_card->spinlock, flags); | ||
500 | |||
501 | if (up) | ||
502 | snd_mtpav_output_write(substream); | ||
503 | } | ||
504 | |||
505 | /* | ||
506 | * midi interrupt for inputs | ||
507 | */ | ||
508 | |||
509 | static void snd_mtpav_inmidi_process(mtpav_t *mcrd, u8 inbyte) | ||
510 | { | ||
511 | mtpav_port_t *portp; | ||
512 | |||
513 | if ((int)mcrd->inmidiport > mcrd->num_ports * 2 + MTPAV_PIDX_BROADCAST) | ||
514 | return; | ||
515 | |||
516 | portp = &mcrd->ports[mcrd->inmidiport]; | ||
517 | if (portp->mode & MTPAV_MODE_INPUT_TRIGGERED) { | ||
518 | snd_rawmidi_receive(portp->input, &inbyte, 1); | ||
519 | } | ||
520 | } | ||
521 | |||
522 | static void snd_mtpav_inmidi_h(mtpav_t * mcrd, u8 inbyte) | ||
523 | { | ||
524 | snd_assert(mcrd, return); | ||
525 | |||
526 | if (inbyte >= 0xf8) { | ||
527 | /* real-time midi code */ | ||
528 | snd_mtpav_inmidi_process(mcrd, inbyte); | ||
529 | return; | ||
530 | } | ||
531 | |||
532 | if (mcrd->inmidistate == 0) { // awaiting command | ||
533 | if (inbyte == 0xf5) // MTP port # | ||
534 | mcrd->inmidistate = 1; | ||
535 | else | ||
536 | snd_mtpav_inmidi_process(mcrd, inbyte); | ||
537 | } else if (mcrd->inmidistate) { | ||
538 | mcrd->inmidiport = translate_hwport_to_subdevice(mcrd, inbyte); | ||
539 | mcrd->inmidistate = 0; | ||
540 | } | ||
541 | } | ||
542 | |||
543 | static void snd_mtpav_read_bytes(mtpav_t * mcrd) | ||
544 | { | ||
545 | u8 clrread, setread; | ||
546 | u8 mtp_read_byte; | ||
547 | u8 sr, cbyt; | ||
548 | int i; | ||
549 | |||
550 | u8 sbyt = snd_mtpav_getreg(mcrd, SREG); | ||
551 | |||
552 | //printk("snd_mtpav_read_bytes() sbyt: 0x%x\n", sbyt); | ||
553 | |||
554 | if (!(sbyt & SIGS_BYTE)) | ||
555 | return; | ||
556 | |||
557 | cbyt = snd_mtpav_getreg(mcrd, CREG); | ||
558 | clrread = cbyt & (SIGC_READ ^ 0xff); | ||
559 | setread = cbyt | SIGC_READ; | ||
560 | |||
561 | do { | ||
562 | |||
563 | mtp_read_byte = 0; | ||
564 | for (i = 0; i < 4; i++) { | ||
565 | snd_mtpav_mputreg(mcrd, CREG, setread); | ||
566 | sr = snd_mtpav_getreg(mcrd, SREG); | ||
567 | snd_mtpav_mputreg(mcrd, CREG, clrread); | ||
568 | |||
569 | sr &= SIGS_IN0 | SIGS_IN1; | ||
570 | sr >>= 4; | ||
571 | mtp_read_byte |= sr << (i * 2); | ||
572 | } | ||
573 | |||
574 | snd_mtpav_inmidi_h(mcrd, mtp_read_byte); | ||
575 | |||
576 | sbyt = snd_mtpav_getreg(mcrd, SREG); | ||
577 | |||
578 | } while (sbyt & SIGS_BYTE); | ||
579 | } | ||
580 | |||
581 | static irqreturn_t snd_mtpav_irqh(int irq, void *dev_id, struct pt_regs *regs) | ||
582 | { | ||
583 | mtpav_t *mcard = dev_id; | ||
584 | |||
585 | //printk("irqh()\n"); | ||
586 | spin_lock(&mcard->spinlock); | ||
587 | snd_mtpav_read_bytes(mcard); | ||
588 | spin_unlock(&mcard->spinlock); | ||
589 | return IRQ_HANDLED; | ||
590 | } | ||
591 | |||
592 | /* | ||
593 | * get ISA resources | ||
594 | */ | ||
595 | static int snd_mtpav_get_ISA(mtpav_t * mcard) | ||
596 | { | ||
597 | if ((mcard->res_port = request_region(port, 3, "MotuMTPAV MIDI")) == NULL) { | ||
598 | snd_printk("MTVAP port 0x%lx is busy\n", port); | ||
599 | return -EBUSY; | ||
600 | } | ||
601 | mcard->port = port; | ||
602 | if (request_irq(irq, snd_mtpav_irqh, SA_INTERRUPT, "MOTU MTPAV", (void *)mcard)) { | ||
603 | snd_printk("MTVAP IRQ %d busy\n", irq); | ||
604 | return -EBUSY; | ||
605 | } | ||
606 | mcard->irq = irq; | ||
607 | return 0; | ||
608 | } | ||
609 | |||
610 | |||
611 | /* | ||
612 | */ | ||
613 | |||
614 | static snd_rawmidi_ops_t snd_mtpav_output = { | ||
615 | .open = snd_mtpav_output_open, | ||
616 | .close = snd_mtpav_output_close, | ||
617 | .trigger = snd_mtpav_output_trigger, | ||
618 | }; | ||
619 | |||
620 | static snd_rawmidi_ops_t snd_mtpav_input = { | ||
621 | .open = snd_mtpav_input_open, | ||
622 | .close = snd_mtpav_input_close, | ||
623 | .trigger = snd_mtpav_input_trigger, | ||
624 | }; | ||
625 | |||
626 | |||
627 | /* | ||
628 | * get RAWMIDI resources | ||
629 | */ | ||
630 | |||
631 | static void snd_mtpav_set_name(mtpav_t *chip, snd_rawmidi_substream_t *substream) | ||
632 | { | ||
633 | if (substream->number >= 0 && substream->number < chip->num_ports) | ||
634 | sprintf(substream->name, "MTP direct %d", (substream->number % chip->num_ports) + 1); | ||
635 | else if (substream->number >= 8 && substream->number < chip->num_ports * 2) | ||
636 | sprintf(substream->name, "MTP remote %d", (substream->number % chip->num_ports) + 1); | ||
637 | else if (substream->number == chip->num_ports * 2) | ||
638 | strcpy(substream->name, "MTP computer"); | ||
639 | else if (substream->number == chip->num_ports * 2 + 1) | ||
640 | strcpy(substream->name, "MTP ADAT"); | ||
641 | else | ||
642 | strcpy(substream->name, "MTP broadcast"); | ||
643 | } | ||
644 | |||
645 | static int snd_mtpav_get_RAWMIDI(mtpav_t * mcard) | ||
646 | { | ||
647 | int rval = 0; | ||
648 | snd_rawmidi_t *rawmidi; | ||
649 | snd_rawmidi_substream_t *substream; | ||
650 | struct list_head *list; | ||
651 | |||
652 | //printk("entering snd_mtpav_get_RAWMIDI\n"); | ||
653 | |||
654 | if (hwports < 1) | ||
655 | mcard->num_ports = 1; | ||
656 | else if (hwports > 8) | ||
657 | mcard->num_ports = 8; | ||
658 | else | ||
659 | mcard->num_ports = hwports; | ||
660 | |||
661 | if ((rval = snd_rawmidi_new(mcard->card, "MotuMIDI", 0, | ||
662 | mcard->num_ports * 2 + MTPAV_PIDX_BROADCAST + 1, | ||
663 | mcard->num_ports * 2 + MTPAV_PIDX_BROADCAST + 1, | ||
664 | &mcard->rmidi)) < 0) | ||
665 | return rval; | ||
666 | rawmidi = mcard->rmidi; | ||
667 | |||
668 | list_for_each(list, &rawmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT].substreams) { | ||
669 | substream = list_entry(list, snd_rawmidi_substream_t, list); | ||
670 | snd_mtpav_set_name(mcard, substream); | ||
671 | substream->ops = &snd_mtpav_input; | ||
672 | } | ||
673 | list_for_each(list, &rawmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT].substreams) { | ||
674 | substream = list_entry(list, snd_rawmidi_substream_t, list); | ||
675 | snd_mtpav_set_name(mcard, substream); | ||
676 | substream->ops = &snd_mtpav_output; | ||
677 | mcard->ports[substream->number].hwport = translate_subdevice_to_hwport(mcard, substream->number); | ||
678 | } | ||
679 | rawmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT | SNDRV_RAWMIDI_INFO_INPUT | | ||
680 | SNDRV_RAWMIDI_INFO_DUPLEX; | ||
681 | sprintf(rawmidi->name, "MTP AV MIDI"); | ||
682 | //printk("exiting snd_mtpav_get_RAWMIDI() \n"); | ||
683 | return 0; | ||
684 | } | ||
685 | |||
686 | /* | ||
687 | */ | ||
688 | |||
689 | static mtpav_t *new_mtpav(void) | ||
690 | { | ||
691 | mtpav_t *ncrd = kcalloc(1, sizeof(*ncrd), GFP_KERNEL); | ||
692 | if (ncrd != NULL) { | ||
693 | spin_lock_init(&ncrd->spinlock); | ||
694 | |||
695 | init_timer(&ncrd->timer); | ||
696 | ncrd->card = NULL; | ||
697 | ncrd->irq = -1; | ||
698 | ncrd->share_irq = 0; | ||
699 | |||
700 | ncrd->inmidiport = 0xffffffff; | ||
701 | ncrd->inmidistate = 0; | ||
702 | ncrd->outmidihwport = 0xffffffff; | ||
703 | } | ||
704 | return ncrd; | ||
705 | } | ||
706 | |||
707 | /* | ||
708 | */ | ||
709 | |||
710 | static void free_mtpav(mtpav_t * crd) | ||
711 | { | ||
712 | unsigned long flags; | ||
713 | |||
714 | spin_lock_irqsave(&crd->spinlock, flags); | ||
715 | if (crd->istimer > 0) | ||
716 | snd_mtpav_remove_output_timer(crd); | ||
717 | spin_unlock_irqrestore(&crd->spinlock, flags); | ||
718 | if (crd->irq >= 0) | ||
719 | free_irq(crd->irq, (void *)crd); | ||
720 | if (crd->res_port) { | ||
721 | release_resource(crd->res_port); | ||
722 | kfree_nocheck(crd->res_port); | ||
723 | } | ||
724 | kfree(crd); | ||
725 | } | ||
726 | |||
727 | /* | ||
728 | */ | ||
729 | |||
730 | static int __init alsa_card_mtpav_init(void) | ||
731 | { | ||
732 | int err = 0; | ||
733 | char longname_buffer[80]; | ||
734 | |||
735 | mtp_card = new_mtpav(); | ||
736 | if (mtp_card == NULL) | ||
737 | return -ENOMEM; | ||
738 | |||
739 | mtp_card->card = snd_card_new(index, id, THIS_MODULE, 0); | ||
740 | if (mtp_card->card == NULL) { | ||
741 | free_mtpav(mtp_card); | ||
742 | return -ENOMEM; | ||
743 | } | ||
744 | |||
745 | err = snd_mtpav_get_ISA(mtp_card); | ||
746 | //printk("snd_mtpav_get_ISA returned: %d\n", err); | ||
747 | if (err < 0) | ||
748 | goto __error; | ||
749 | |||
750 | strcpy(mtp_card->card->driver, "MTPAV"); | ||
751 | strcpy(mtp_card->card->shortname, "MTPAV on parallel port"); | ||
752 | memset(longname_buffer, 0, sizeof(longname_buffer)); | ||
753 | sprintf(longname_buffer, "MTPAV on parallel port at"); | ||
754 | |||
755 | err = snd_mtpav_get_RAWMIDI(mtp_card); | ||
756 | //snd_printk("snd_mtapv_get_RAWMIDI returned: %d\n", err); | ||
757 | if (err < 0) | ||
758 | goto __error; | ||
759 | |||
760 | err = snd_card_register(mtp_card->card); // don't snd_card_register until AFTER all cards reources done! | ||
761 | |||
762 | //printk("snd_card_register returned %d\n", err); | ||
763 | if (err < 0) | ||
764 | goto __error; | ||
765 | |||
766 | |||
767 | snd_mtpav_portscan(mtp_card); | ||
768 | |||
769 | printk(KERN_INFO "Motu MidiTimePiece on parallel port irq: %d ioport: 0x%lx\n", irq, port); | ||
770 | |||
771 | return 0; | ||
772 | |||
773 | __error: | ||
774 | snd_card_free(mtp_card->card); | ||
775 | free_mtpav(mtp_card); | ||
776 | return err; | ||
777 | } | ||
778 | |||
779 | /* | ||
780 | */ | ||
781 | |||
782 | static void __exit alsa_card_mtpav_exit(void) | ||
783 | { | ||
784 | if (mtp_card == NULL) | ||
785 | return; | ||
786 | if (mtp_card->card) | ||
787 | snd_card_free(mtp_card->card); | ||
788 | free_mtpav(mtp_card); | ||
789 | } | ||
790 | |||
791 | /* | ||
792 | */ | ||
793 | |||
794 | module_init(alsa_card_mtpav_init) | ||
795 | module_exit(alsa_card_mtpav_exit) | ||