Blame view

sound/drivers/mtpav.c 20 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
  /*
   *      MOTU Midi Timepiece ALSA Main routines
   *      Copyright by Michael T. Mayers (c) Jan 09, 2000
   *      mail: michael@tweakoz.com
   *      Thanks to John Galbraith
   *
   *      This program is free software; you can redistribute it and/or modify
   *      it under the terms of the GNU General Public License as published by
   *      the Free Software Foundation; either version 2 of the License, or
   *      (at your option) any later version.
   *
   *      This program is distributed in the hope that it will be useful,
   *      but WITHOUT ANY WARRANTY; without even the implied warranty of
   *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   *      GNU General Public License for more details.
   *
   *      You should have received a copy of the GNU General Public License
   *      along with this program; if not, write to the Free Software
   *      Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
   *
   *
   *      This driver is for the 'Mark Of The Unicorn' (MOTU)
   *      MidiTimePiece AV multiport MIDI interface 
   *
   *      IOPORTS
   *      -------
   *      8 MIDI Ins and 8 MIDI outs
   *      Video Sync In (BNC), Word Sync Out (BNC), 
   *      ADAT Sync Out (DB9)
   *      SMPTE in/out (1/4")
   *      2 programmable pedal/footswitch inputs and 4 programmable MIDI controller knobs.
   *      Macintosh RS422 serial port
   *      RS422 "network" port for ganging multiple MTP's
   *      PC Parallel Port ( which this driver currently uses )
   *
   *      MISC FEATURES
   *      -------------
   *      Hardware MIDI routing, merging, and filtering   
   *      MIDI Synchronization to Video, ADAT, SMPTE and other Clock sources
   *      128 'scene' memories, recallable from MIDI program change
   *
   *
   * ChangeLog
   * Jun 11 2001	Takashi Iwai <tiwai@suse.de>
   *      - Recoded & debugged
   *      - Added timer interrupt for midi outputs
   *      - hwports is between 1 and 8, which specifies the number of hardware ports.
   *        The three global ports, computer, adat and broadcast ports, are created
   *        always after h/w and remote ports.
   *
   */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
52
53
  #include <linux/init.h>
  #include <linux/interrupt.h>
da155d5b4   Paul Gortmaker   sound: Add module...
54
  #include <linux/module.h>
077d0ac5b   Takashi Iwai   [ALSA] mtpav - Us...
55
56
  #include <linux/err.h>
  #include <linux/platform_device.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
57
  #include <linux/ioport.h>
ddfb31992   Takashi Iwai   ALSA: use linux/i...
58
  #include <linux/io.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
59
60
61
62
63
  #include <linux/moduleparam.h>
  #include <sound/core.h>
  #include <sound/initval.h>
  #include <sound/rawmidi.h>
  #include <linux/delay.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
  /*
   *      globals
   */
  MODULE_AUTHOR("Michael T. Mayers");
  MODULE_DESCRIPTION("MOTU MidiTimePiece AV multiport MIDI");
  MODULE_LICENSE("GPL");
  MODULE_SUPPORTED_DEVICE("{{MOTU,MidiTimePiece AV multiport MIDI}}");
  
  // io resources
  #define MTPAV_IOBASE		0x378
  #define MTPAV_IRQ		7
  #define MTPAV_MAX_PORTS		8
  
  static int index = SNDRV_DEFAULT_IDX1;
  static char *id = SNDRV_DEFAULT_STR1;
  static long port = MTPAV_IOBASE;	/* 0x378, 0x278 */
  static int irq = MTPAV_IRQ;		/* 7, 5 */
  static int hwports = MTPAV_MAX_PORTS;	/* use hardware ports 1-8 */
  
  module_param(index, int, 0444);
  MODULE_PARM_DESC(index, "Index value for MotuMTPAV MIDI.");
  module_param(id, charp, 0444);
  MODULE_PARM_DESC(id, "ID string for MotuMTPAV MIDI.");
  module_param(port, long, 0444);
  MODULE_PARM_DESC(port, "Parallel port # for MotuMTPAV MIDI.");
  module_param(irq, int, 0444);
  MODULE_PARM_DESC(irq, "Parallel IRQ # for MotuMTPAV MIDI.");
  module_param(hwports, int, 0444);
  MODULE_PARM_DESC(hwports, "Hardware ports # for MotuMTPAV MIDI.");
f7a9275d9   Clemens Ladisch   [ALSA] unregister...
93
  static struct platform_device *device;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
  /*
   *      defines
   */
  //#define USE_FAKE_MTP //       don't actually read/write to MTP device (for debugging without an actual unit) (does not work yet)
  
  // parallel port usage masks
  #define SIGS_BYTE 0x08
  #define SIGS_RFD 0x80
  #define SIGS_IRQ 0x40
  #define SIGS_IN0 0x10
  #define SIGS_IN1 0x20
  
  #define SIGC_WRITE 0x04
  #define SIGC_READ 0x08
  #define SIGC_INTEN 0x10
  
  #define DREG 0
  #define SREG 1
  #define CREG 2
  
  //
  #define MTPAV_MODE_INPUT_OPENED		0x01
  #define MTPAV_MODE_OUTPUT_OPENED	0x02
  #define MTPAV_MODE_INPUT_TRIGGERED	0x04
  #define MTPAV_MODE_OUTPUT_TRIGGERED	0x08
  
  #define NUMPORTS (0x12+1)
  
  
  /*
   */
077d0ac5b   Takashi Iwai   [ALSA] mtpav - Us...
125
  struct mtpav_port {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
126
127
128
129
  	u8 number;
  	u8 hwport;
  	u8 mode;
  	u8 running_status;
4a4d2cfd8   Takashi Iwai   [ALSA] Remove xxx...
130
131
  	struct snd_rawmidi_substream *input;
  	struct snd_rawmidi_substream *output;
077d0ac5b   Takashi Iwai   [ALSA] mtpav - Us...
132
  };
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
133

077d0ac5b   Takashi Iwai   [ALSA] mtpav - Us...
134
  struct mtpav {
4a4d2cfd8   Takashi Iwai   [ALSA] Remove xxx...
135
  	struct snd_card *card;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
136
137
138
139
140
141
142
  	unsigned long port;
  	struct resource *res_port;
  	int irq;			/* interrupt (for inputs) */
  	spinlock_t spinlock;
  	int share_irq;			/* number of accesses to input interrupts */
  	int istimer;			/* number of accesses to timer interrupts */
  	struct timer_list timer;	/* timer interrupts for outputs */
4a4d2cfd8   Takashi Iwai   [ALSA] Remove xxx...
143
  	struct snd_rawmidi *rmidi;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
144
  	int num_ports;		/* number of hw ports (1-8) */
077d0ac5b   Takashi Iwai   [ALSA] mtpav - Us...
145
  	struct mtpav_port ports[NUMPORTS];	/* all ports including computer, adat and bc */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
146
147
148
149
150
  
  	u32 inmidiport;		/* selected input midi port */
  	u32 inmidistate;	/* during midi command 0xf5 */
  
  	u32 outmidihwport;	/* selected output midi hw port */
077d0ac5b   Takashi Iwai   [ALSA] mtpav - Us...
151
  };
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
152
153
154
  
  
  /*
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
   * possible hardware ports (selected by 0xf5 port message)
   *      0x00		all ports
   *      0x01 .. 0x08    this MTP's ports 1..8
   *      0x09 .. 0x10    networked MTP's ports (9..16)
   *      0x11            networked MTP's computer port
   *      0x63            to ADAT
   *
   * mappig:
   *  subdevice 0 - (X-1)    ports
   *            X - (2*X-1)  networked ports
   *            X            computer
   *            X+1          ADAT
   *            X+2          all ports
   *
   *  where X = chip->num_ports
   */
  
  #define MTPAV_PIDX_COMPUTER	0
  #define MTPAV_PIDX_ADAT		1
  #define MTPAV_PIDX_BROADCAST	2
077d0ac5b   Takashi Iwai   [ALSA] mtpav - Us...
175
  static int translate_subdevice_to_hwport(struct mtpav *chip, int subdev)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
176
177
178
179
180
181
182
183
184
185
186
187
188
  {
  	if (subdev < 0)
  		return 0x01; /* invalid - use port 0 as default */
  	else if (subdev < chip->num_ports)
  		return subdev + 1; /* single mtp port */
  	else if (subdev < chip->num_ports * 2)
  		return subdev - chip->num_ports + 0x09; /* remote port */
  	else if (subdev == chip->num_ports * 2 + MTPAV_PIDX_COMPUTER)
  		return 0x11; /* computer port */
  	else if (subdev == chip->num_ports + MTPAV_PIDX_ADAT)
  		return 0x63;		/* ADAT */
  	return 0; /* all ports */
  }
077d0ac5b   Takashi Iwai   [ALSA] mtpav - Us...
189
  static int translate_hwport_to_subdevice(struct mtpav *chip, int hwport)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
  {
  	int p;
  	if (hwport <= 0x00) /* all ports */
  		return chip->num_ports + MTPAV_PIDX_BROADCAST;
  	else if (hwport <= 0x08) { /* single port */
  		p = hwport - 1;
  		if (p >= chip->num_ports)
  			p = 0;
  		return p;
  	} else if (hwport <= 0x10) { /* remote port */
  		p = hwport - 0x09 + chip->num_ports;
  		if (p >= chip->num_ports * 2)
  			p = chip->num_ports;
  		return p;
  	} else if (hwport == 0x11)  /* computer port */
  		return chip->num_ports + MTPAV_PIDX_COMPUTER;
  	else  /* ADAT */
  		return chip->num_ports + MTPAV_PIDX_ADAT;
  }
  
  
  /*
   */
077d0ac5b   Takashi Iwai   [ALSA] mtpav - Us...
213
  static u8 snd_mtpav_getreg(struct mtpav *chip, u16 reg)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
  {
  	u8 rval = 0;
  
  	if (reg == SREG) {
  		rval = inb(chip->port + SREG);
  		rval = (rval & 0xf8);
  	} else if (reg == CREG) {
  		rval = inb(chip->port + CREG);
  		rval = (rval & 0x1c);
  	}
  
  	return rval;
  }
  
  /*
   */
077d0ac5b   Takashi Iwai   [ALSA] mtpav - Us...
230
  static inline void snd_mtpav_mputreg(struct mtpav *chip, u16 reg, u8 val)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
231
  {
077d0ac5b   Takashi Iwai   [ALSA] mtpav - Us...
232
233
  	if (reg == DREG || reg == CREG)
  		outb(val, chip->port + reg);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
234
235
236
237
  }
  
  /*
   */
077d0ac5b   Takashi Iwai   [ALSA] mtpav - Us...
238
  static void snd_mtpav_wait_rfdhi(struct mtpav *chip)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
239
240
241
242
243
244
245
246
247
248
  {
  	int counts = 10000;
  	u8 sbyte;
  
  	sbyte = snd_mtpav_getreg(chip, SREG);
  	while (!(sbyte & SIGS_RFD) && counts--) {
  		sbyte = snd_mtpav_getreg(chip, SREG);
  		udelay(10);
  	}
  }
077d0ac5b   Takashi Iwai   [ALSA] mtpav - Us...
249
  static void snd_mtpav_send_byte(struct mtpav *chip, u8 byte)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
  {
  	u8 tcbyt;
  	u8 clrwrite;
  	u8 setwrite;
  
  	snd_mtpav_wait_rfdhi(chip);
  
  	/////////////////
  
  	tcbyt = snd_mtpav_getreg(chip, CREG);
  	clrwrite = tcbyt & (SIGC_WRITE ^ 0xff);
  	setwrite = tcbyt | SIGC_WRITE;
  
  	snd_mtpav_mputreg(chip, DREG, byte);
  	snd_mtpav_mputreg(chip, CREG, clrwrite);	// clear write bit
  
  	snd_mtpav_mputreg(chip, CREG, setwrite);	// set write bit
  
  }
  
  
  /*
   */
  
  /* call this with spin lock held */
077d0ac5b   Takashi Iwai   [ALSA] mtpav - Us...
275
276
  static void snd_mtpav_output_port_write(struct mtpav *mtp_card,
  					struct mtpav_port *portp,
4a4d2cfd8   Takashi Iwai   [ALSA] Remove xxx...
277
  					struct snd_rawmidi_substream *substream)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
278
279
280
281
282
283
284
285
286
  {
  	u8 outbyte;
  
  	// Get the outbyte first, so we can emulate running status if
  	// necessary
  	if (snd_rawmidi_transmit(substream, &outbyte, 1) != 1)
  		return;
  
  	// send port change command if necessary
077d0ac5b   Takashi Iwai   [ALSA] mtpav - Us...
287
288
  	if (portp->hwport != mtp_card->outmidihwport) {
  		mtp_card->outmidihwport = portp->hwport;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
289
290
  
  		snd_mtpav_send_byte(mtp_card, 0xf5);
077d0ac5b   Takashi Iwai   [ALSA] mtpav - Us...
291
  		snd_mtpav_send_byte(mtp_card, portp->hwport);
45203832d   Takashi Iwai   ALSA: Add missing...
292
293
294
295
296
  		/*
  		snd_printk(KERN_DEBUG "new outport: 0x%x
  ",
  			   (unsigned int) portp->hwport);
  		*/
077d0ac5b   Takashi Iwai   [ALSA] mtpav - Us...
297
298
  		if (!(outbyte & 0x80) && portp->running_status)
  			snd_mtpav_send_byte(mtp_card, portp->running_status);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
299
300
301
302
303
304
  	}
  
  	// send data
  
  	do {
  		if (outbyte & 0x80)
077d0ac5b   Takashi Iwai   [ALSA] mtpav - Us...
305
  			portp->running_status = outbyte;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
306
307
308
309
  		
  		snd_mtpav_send_byte(mtp_card, outbyte);
  	} while (snd_rawmidi_transmit(substream, &outbyte, 1) == 1);
  }
4a4d2cfd8   Takashi Iwai   [ALSA] Remove xxx...
310
  static void snd_mtpav_output_write(struct snd_rawmidi_substream *substream)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
311
  {
077d0ac5b   Takashi Iwai   [ALSA] mtpav - Us...
312
313
  	struct mtpav *mtp_card = substream->rmidi->private_data;
  	struct mtpav_port *portp = &mtp_card->ports[substream->number];
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
314
315
316
  	unsigned long flags;
  
  	spin_lock_irqsave(&mtp_card->spinlock, flags);
077d0ac5b   Takashi Iwai   [ALSA] mtpav - Us...
317
  	snd_mtpav_output_port_write(mtp_card, portp, substream);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
318
319
320
321
322
323
324
  	spin_unlock_irqrestore(&mtp_card->spinlock, flags);
  }
  
  
  /*
   *      mtpav control
   */
077d0ac5b   Takashi Iwai   [ALSA] mtpav - Us...
325
  static void snd_mtpav_portscan(struct mtpav *chip)	// put mtp into smart routing mode
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
326
327
328
329
330
331
332
333
334
335
336
337
  {
  	u8 p;
  
  	for (p = 0; p < 8; p++) {
  		snd_mtpav_send_byte(chip, 0xf5);
  		snd_mtpav_send_byte(chip, p);
  		snd_mtpav_send_byte(chip, 0xfe);
  	}
  }
  
  /*
   */
4a4d2cfd8   Takashi Iwai   [ALSA] Remove xxx...
338
  static int snd_mtpav_input_open(struct snd_rawmidi_substream *substream)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
339
  {
077d0ac5b   Takashi Iwai   [ALSA] mtpav - Us...
340
341
  	struct mtpav *mtp_card = substream->rmidi->private_data;
  	struct mtpav_port *portp = &mtp_card->ports[substream->number];
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
342
  	unsigned long flags;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
343

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
344
345
346
347
348
349
350
351
352
353
354
  	spin_lock_irqsave(&mtp_card->spinlock, flags);
  	portp->mode |= MTPAV_MODE_INPUT_OPENED;
  	portp->input = substream;
  	if (mtp_card->share_irq++ == 0)
  		snd_mtpav_mputreg(mtp_card, CREG, (SIGC_INTEN | SIGC_WRITE));	// enable pport interrupts
  	spin_unlock_irqrestore(&mtp_card->spinlock, flags);
  	return 0;
  }
  
  /*
   */
4a4d2cfd8   Takashi Iwai   [ALSA] Remove xxx...
355
  static int snd_mtpav_input_close(struct snd_rawmidi_substream *substream)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
356
  {
077d0ac5b   Takashi Iwai   [ALSA] mtpav - Us...
357
358
  	struct mtpav *mtp_card = substream->rmidi->private_data;
  	struct mtpav_port *portp = &mtp_card->ports[substream->number];
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
359
  	unsigned long flags;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
360
361
  
  	spin_lock_irqsave(&mtp_card->spinlock, flags);
077d0ac5b   Takashi Iwai   [ALSA] mtpav - Us...
362
  	portp->mode &= ~MTPAV_MODE_INPUT_OPENED;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
363
364
365
  	portp->input = NULL;
  	if (--mtp_card->share_irq == 0)
  		snd_mtpav_mputreg(mtp_card, CREG, 0);	// disable pport interrupts
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
366
367
368
369
370
371
  	spin_unlock_irqrestore(&mtp_card->spinlock, flags);
  	return 0;
  }
  
  /*
   */
4a4d2cfd8   Takashi Iwai   [ALSA] Remove xxx...
372
  static void snd_mtpav_input_trigger(struct snd_rawmidi_substream *substream, int up)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
373
  {
077d0ac5b   Takashi Iwai   [ALSA] mtpav - Us...
374
375
  	struct mtpav *mtp_card = substream->rmidi->private_data;
  	struct mtpav_port *portp = &mtp_card->ports[substream->number];
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
376
  	unsigned long flags;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
  
  	spin_lock_irqsave(&mtp_card->spinlock, flags);
  	if (up)
  		portp->mode |= MTPAV_MODE_INPUT_TRIGGERED;
  	else
  		portp->mode &= ~MTPAV_MODE_INPUT_TRIGGERED;
  	spin_unlock_irqrestore(&mtp_card->spinlock, flags);
  
  }
  
  
  /*
   * timer interrupt for outputs
   */
  
  static void snd_mtpav_output_timer(unsigned long data)
  {
  	unsigned long flags;
077d0ac5b   Takashi Iwai   [ALSA] mtpav - Us...
395
  	struct mtpav *chip = (struct mtpav *)data;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
396
397
398
399
400
401
402
403
  	int p;
  
  	spin_lock_irqsave(&chip->spinlock, flags);
  	/* reprogram timer */
  	chip->timer.expires = 1 + jiffies;
  	add_timer(&chip->timer);
  	/* process each port */
  	for (p = 0; p <= chip->num_ports * 2 + MTPAV_PIDX_BROADCAST; p++) {
077d0ac5b   Takashi Iwai   [ALSA] mtpav - Us...
404
  		struct mtpav_port *portp = &chip->ports[p];
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
405
  		if ((portp->mode & MTPAV_MODE_OUTPUT_TRIGGERED) && portp->output)
077d0ac5b   Takashi Iwai   [ALSA] mtpav - Us...
406
  			snd_mtpav_output_port_write(chip, portp, portp->output);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
407
408
409
410
411
  	}
  	spin_unlock_irqrestore(&chip->spinlock, flags);
  }
  
  /* spinlock held! */
077d0ac5b   Takashi Iwai   [ALSA] mtpav - Us...
412
  static void snd_mtpav_add_output_timer(struct mtpav *chip)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
413
  {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
414
415
416
417
418
  	chip->timer.expires = 1 + jiffies;
  	add_timer(&chip->timer);
  }
  
  /* spinlock held! */
077d0ac5b   Takashi Iwai   [ALSA] mtpav - Us...
419
  static void snd_mtpav_remove_output_timer(struct mtpav *chip)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
420
421
422
423
424
425
  {
  	del_timer(&chip->timer);
  }
  
  /*
   */
4a4d2cfd8   Takashi Iwai   [ALSA] Remove xxx...
426
  static int snd_mtpav_output_open(struct snd_rawmidi_substream *substream)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
427
  {
077d0ac5b   Takashi Iwai   [ALSA] mtpav - Us...
428
429
  	struct mtpav *mtp_card = substream->rmidi->private_data;
  	struct mtpav_port *portp = &mtp_card->ports[substream->number];
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
430
  	unsigned long flags;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
431
432
433
434
435
436
437
438
439
440
  
  	spin_lock_irqsave(&mtp_card->spinlock, flags);
  	portp->mode |= MTPAV_MODE_OUTPUT_OPENED;
  	portp->output = substream;
  	spin_unlock_irqrestore(&mtp_card->spinlock, flags);
  	return 0;
  };
  
  /*
   */
4a4d2cfd8   Takashi Iwai   [ALSA] Remove xxx...
441
  static int snd_mtpav_output_close(struct snd_rawmidi_substream *substream)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
442
  {
077d0ac5b   Takashi Iwai   [ALSA] mtpav - Us...
443
444
  	struct mtpav *mtp_card = substream->rmidi->private_data;
  	struct mtpav_port *portp = &mtp_card->ports[substream->number];
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
445
  	unsigned long flags;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
446
447
  
  	spin_lock_irqsave(&mtp_card->spinlock, flags);
077d0ac5b   Takashi Iwai   [ALSA] mtpav - Us...
448
  	portp->mode &= ~MTPAV_MODE_OUTPUT_OPENED;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
449
450
451
452
453
454
455
  	portp->output = NULL;
  	spin_unlock_irqrestore(&mtp_card->spinlock, flags);
  	return 0;
  };
  
  /*
   */
4a4d2cfd8   Takashi Iwai   [ALSA] Remove xxx...
456
  static void snd_mtpav_output_trigger(struct snd_rawmidi_substream *substream, int up)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
457
  {
077d0ac5b   Takashi Iwai   [ALSA] mtpav - Us...
458
459
  	struct mtpav *mtp_card = substream->rmidi->private_data;
  	struct mtpav_port *portp = &mtp_card->ports[substream->number];
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
460
  	unsigned long flags;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
461
462
463
  
  	spin_lock_irqsave(&mtp_card->spinlock, flags);
  	if (up) {
077d0ac5b   Takashi Iwai   [ALSA] mtpav - Us...
464
  		if (! (portp->mode & MTPAV_MODE_OUTPUT_TRIGGERED)) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
  			if (mtp_card->istimer++ == 0)
  				snd_mtpav_add_output_timer(mtp_card);
  			portp->mode |= MTPAV_MODE_OUTPUT_TRIGGERED;
  		}
  	} else {
  		portp->mode &= ~MTPAV_MODE_OUTPUT_TRIGGERED;
  		if (--mtp_card->istimer == 0)
  			snd_mtpav_remove_output_timer(mtp_card);
  	}
  	spin_unlock_irqrestore(&mtp_card->spinlock, flags);
  
  	if (up)
  		snd_mtpav_output_write(substream);
  }
  
  /*
   * midi interrupt for inputs
   */
077d0ac5b   Takashi Iwai   [ALSA] mtpav - Us...
483
  static void snd_mtpav_inmidi_process(struct mtpav *mcrd, u8 inbyte)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
484
  {
077d0ac5b   Takashi Iwai   [ALSA] mtpav - Us...
485
  	struct mtpav_port *portp;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
486
487
488
489
490
  
  	if ((int)mcrd->inmidiport > mcrd->num_ports * 2 + MTPAV_PIDX_BROADCAST)
  		return;
  
  	portp = &mcrd->ports[mcrd->inmidiport];
077d0ac5b   Takashi Iwai   [ALSA] mtpav - Us...
491
  	if (portp->mode & MTPAV_MODE_INPUT_TRIGGERED)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
492
  		snd_rawmidi_receive(portp->input, &inbyte, 1);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
493
  }
077d0ac5b   Takashi Iwai   [ALSA] mtpav - Us...
494
  static void snd_mtpav_inmidi_h(struct mtpav *mcrd, u8 inbyte)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
495
  {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
  	if (inbyte >= 0xf8) {
  		/* real-time midi code */
  		snd_mtpav_inmidi_process(mcrd, inbyte);
  		return;
  	}
  
  	if (mcrd->inmidistate == 0) {	// awaiting command
  		if (inbyte == 0xf5)	// MTP port #
  			mcrd->inmidistate = 1;
  		else
  			snd_mtpav_inmidi_process(mcrd, inbyte);
  	} else if (mcrd->inmidistate) {
  		mcrd->inmidiport = translate_hwport_to_subdevice(mcrd, inbyte);
  		mcrd->inmidistate = 0;
  	}
  }
077d0ac5b   Takashi Iwai   [ALSA] mtpav - Us...
512
  static void snd_mtpav_read_bytes(struct mtpav *mcrd)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
513
514
515
516
517
518
519
  {
  	u8 clrread, setread;
  	u8 mtp_read_byte;
  	u8 sr, cbyt;
  	int i;
  
  	u8 sbyt = snd_mtpav_getreg(mcrd, SREG);
45203832d   Takashi Iwai   ALSA: Add missing...
520
521
  	/* printk(KERN_DEBUG "snd_mtpav_read_bytes() sbyt: 0x%x
  ", sbyt); */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
  
  	if (!(sbyt & SIGS_BYTE))
  		return;
  
  	cbyt = snd_mtpav_getreg(mcrd, CREG);
  	clrread = cbyt & (SIGC_READ ^ 0xff);
  	setread = cbyt | SIGC_READ;
  
  	do {
  
  		mtp_read_byte = 0;
  		for (i = 0; i < 4; i++) {
  			snd_mtpav_mputreg(mcrd, CREG, setread);
  			sr = snd_mtpav_getreg(mcrd, SREG);
  			snd_mtpav_mputreg(mcrd, CREG, clrread);
  
  			sr &= SIGS_IN0 | SIGS_IN1;
  			sr >>= 4;
  			mtp_read_byte |= sr << (i * 2);
  		}
  
  		snd_mtpav_inmidi_h(mcrd, mtp_read_byte);
  
  		sbyt = snd_mtpav_getreg(mcrd, SREG);
  
  	} while (sbyt & SIGS_BYTE);
  }
7d12e780e   David Howells   IRQ: Maintain reg...
549
  static irqreturn_t snd_mtpav_irqh(int irq, void *dev_id)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
550
  {
077d0ac5b   Takashi Iwai   [ALSA] mtpav - Us...
551
  	struct mtpav *mcard = dev_id;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
552

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
553
554
555
556
557
558
559
560
561
  	spin_lock(&mcard->spinlock);
  	snd_mtpav_read_bytes(mcard);
  	spin_unlock(&mcard->spinlock);
  	return IRQ_HANDLED;
  }
  
  /*
   * get ISA resources
   */
788c60433   Prarit Bhargava   [ALSA] Fix __devi...
562
  static int __devinit snd_mtpav_get_ISA(struct mtpav * mcard)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
563
564
  {
  	if ((mcard->res_port = request_region(port, 3, "MotuMTPAV MIDI")) == NULL) {
45203832d   Takashi Iwai   ALSA: Add missing...
565
566
  		snd_printk(KERN_ERR "MTVAP port 0x%lx is busy
  ", port);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
567
568
569
  		return -EBUSY;
  	}
  	mcard->port = port;
88e24c3a4   Yong Zhang   sound: irq: Remov...
570
  	if (request_irq(irq, snd_mtpav_irqh, 0, "MOTU MTPAV", mcard)) {
45203832d   Takashi Iwai   ALSA: Add missing...
571
572
  		snd_printk(KERN_ERR "MTVAP IRQ %d busy
  ", irq);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
573
574
575
576
577
578
579
580
581
  		return -EBUSY;
  	}
  	mcard->irq = irq;
  	return 0;
  }
  
  
  /*
   */
4a4d2cfd8   Takashi Iwai   [ALSA] Remove xxx...
582
  static struct snd_rawmidi_ops snd_mtpav_output = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
583
584
585
586
  	.open =		snd_mtpav_output_open,
  	.close =	snd_mtpav_output_close,
  	.trigger =	snd_mtpav_output_trigger,
  };
4a4d2cfd8   Takashi Iwai   [ALSA] Remove xxx...
587
  static struct snd_rawmidi_ops snd_mtpav_input = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
588
589
590
591
592
593
594
595
596
  	.open =		snd_mtpav_input_open,
  	.close =	snd_mtpav_input_close,
  	.trigger =	snd_mtpav_input_trigger,
  };
  
  
  /*
   * get RAWMIDI resources
   */
788c60433   Prarit Bhargava   [ALSA] Fix __devi...
597
  static void __devinit snd_mtpav_set_name(struct mtpav *chip,
077d0ac5b   Takashi Iwai   [ALSA] mtpav - Us...
598
  				      struct snd_rawmidi_substream *substream)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
599
600
601
602
603
604
605
606
607
608
609
610
  {
  	if (substream->number >= 0 && substream->number < chip->num_ports)
  		sprintf(substream->name, "MTP direct %d", (substream->number % chip->num_ports) + 1);
  	else if (substream->number >= 8 && substream->number < chip->num_ports * 2)
  		sprintf(substream->name, "MTP remote %d", (substream->number % chip->num_ports) + 1);
  	else if (substream->number == chip->num_ports * 2)
  		strcpy(substream->name, "MTP computer");
  	else if (substream->number == chip->num_ports * 2 + 1)
  		strcpy(substream->name, "MTP ADAT");
  	else
  		strcpy(substream->name, "MTP broadcast");
  }
788c60433   Prarit Bhargava   [ALSA] Fix __devi...
611
  static int __devinit snd_mtpav_get_RAWMIDI(struct mtpav *mcard)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
612
  {
077d0ac5b   Takashi Iwai   [ALSA] mtpav - Us...
613
  	int rval;
4a4d2cfd8   Takashi Iwai   [ALSA] Remove xxx...
614
615
  	struct snd_rawmidi *rawmidi;
  	struct snd_rawmidi_substream *substream;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
616
  	struct list_head *list;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
617
  	if (hwports < 1)
077d0ac5b   Takashi Iwai   [ALSA] mtpav - Us...
618
  		hwports = 1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
619
  	else if (hwports > 8)
077d0ac5b   Takashi Iwai   [ALSA] mtpav - Us...
620
621
  		hwports = 8;
  	mcard->num_ports = hwports;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
622
623
624
625
626
627
628
  
  	if ((rval = snd_rawmidi_new(mcard->card, "MotuMIDI", 0,
  				    mcard->num_ports * 2 + MTPAV_PIDX_BROADCAST + 1,
  				    mcard->num_ports * 2 + MTPAV_PIDX_BROADCAST + 1,
  				    &mcard->rmidi)) < 0)
  		return rval;
  	rawmidi = mcard->rmidi;
077d0ac5b   Takashi Iwai   [ALSA] mtpav - Us...
629
  	rawmidi->private_data = mcard;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
630
631
  
  	list_for_each(list, &rawmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT].substreams) {
4a4d2cfd8   Takashi Iwai   [ALSA] Remove xxx...
632
  		substream = list_entry(list, struct snd_rawmidi_substream, list);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
633
634
635
636
  		snd_mtpav_set_name(mcard, substream);
  		substream->ops = &snd_mtpav_input;
  	}
  	list_for_each(list, &rawmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT].substreams) {
4a4d2cfd8   Takashi Iwai   [ALSA] Remove xxx...
637
  		substream = list_entry(list, struct snd_rawmidi_substream, list);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
638
639
640
641
642
643
644
  		snd_mtpav_set_name(mcard, substream);
  		substream->ops = &snd_mtpav_output;
  		mcard->ports[substream->number].hwport = translate_subdevice_to_hwport(mcard, substream->number);
  	}
  	rawmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT | SNDRV_RAWMIDI_INFO_INPUT |
  			       SNDRV_RAWMIDI_INFO_DUPLEX;
  	sprintf(rawmidi->name, "MTP AV MIDI");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
645
646
647
648
649
  	return 0;
  }
  
  /*
   */
077d0ac5b   Takashi Iwai   [ALSA] mtpav - Us...
650
  static void snd_mtpav_free(struct snd_card *card)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
651
  {
077d0ac5b   Takashi Iwai   [ALSA] mtpav - Us...
652
  	struct mtpav *crd = card->private_data;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
653
654
655
656
657
658
659
660
  	unsigned long flags;
  
  	spin_lock_irqsave(&crd->spinlock, flags);
  	if (crd->istimer > 0)
  		snd_mtpav_remove_output_timer(crd);
  	spin_unlock_irqrestore(&crd->spinlock, flags);
  	if (crd->irq >= 0)
  		free_irq(crd->irq, (void *)crd);
b1d5776d8   Takashi Iwai   [ALSA] Remove vma...
661
  	release_and_free_resource(crd->res_port);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
662
663
664
665
  }
  
  /*
   */
788c60433   Prarit Bhargava   [ALSA] Fix __devi...
666
  static int __devinit snd_mtpav_probe(struct platform_device *dev)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
667
  {
077d0ac5b   Takashi Iwai   [ALSA] mtpav - Us...
668
669
670
  	struct snd_card *card;
  	int err;
  	struct mtpav *mtp_card;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
671

bd7dd77c2   Takashi Iwai   ALSA: Convert to ...
672
673
674
  	err = snd_card_create(index, id, THIS_MODULE, sizeof(*mtp_card), &card);
  	if (err < 0)
  		return err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
675

077d0ac5b   Takashi Iwai   [ALSA] mtpav - Us...
676
677
678
679
680
681
  	mtp_card = card->private_data;
  	spin_lock_init(&mtp_card->spinlock);
  	init_timer(&mtp_card->timer);
  	mtp_card->card = card;
  	mtp_card->irq = -1;
  	mtp_card->share_irq = 0;
077d0ac5b   Takashi Iwai   [ALSA] mtpav - Us...
682
683
684
685
686
687
688
  	mtp_card->inmidistate = 0;
  	mtp_card->outmidihwport = 0xffffffff;
  	init_timer(&mtp_card->timer);
  	mtp_card->timer.function = snd_mtpav_output_timer;
  	mtp_card->timer.data = (unsigned long) mtp_card;
  
  	card->private_free = snd_mtpav_free;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
689

32fe61426   Takashi Iwai   ALSA: mtpav - Fix...
690
691
692
  	err = snd_mtpav_get_RAWMIDI(mtp_card);
  	if (err < 0)
  		goto __error;
32cf9a16f   Takashi Iwai   ALSA: mtpav - Fix...
693
  	mtp_card->inmidiport = mtp_card->num_ports + MTPAV_PIDX_BROADCAST;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
694
  	err = snd_mtpav_get_ISA(mtp_card);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
695
696
  	if (err < 0)
  		goto __error;
077d0ac5b   Takashi Iwai   [ALSA] mtpav - Us...
697
698
699
700
  	strcpy(card->driver, "MTPAV");
  	strcpy(card->shortname, "MTPAV on parallel port");
  	snprintf(card->longname, sizeof(card->longname),
  		 "MTPAV on parallel port at 0x%lx", port);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
701

077d0ac5b   Takashi Iwai   [ALSA] mtpav - Us...
702
  	snd_mtpav_portscan(mtp_card);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
703

077d0ac5b   Takashi Iwai   [ALSA] mtpav - Us...
704
705
  	snd_card_set_dev(card, &dev->dev);
  	err = snd_card_register(mtp_card->card);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
706
707
  	if (err < 0)
  		goto __error;
077d0ac5b   Takashi Iwai   [ALSA] mtpav - Us...
708
  	platform_set_drvdata(dev, card);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
709
710
  	printk(KERN_INFO "Motu MidiTimePiece on parallel port irq: %d ioport: 0x%lx
  ", irq, port);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
711
  	return 0;
077d0ac5b   Takashi Iwai   [ALSA] mtpav - Us...
712
713
   __error:
  	snd_card_free(card);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
714
715
  	return err;
  }
788c60433   Prarit Bhargava   [ALSA] Fix __devi...
716
  static int __devexit snd_mtpav_remove(struct platform_device *devptr)
077d0ac5b   Takashi Iwai   [ALSA] mtpav - Us...
717
718
719
720
721
  {
  	snd_card_free(platform_get_drvdata(devptr));
  	platform_set_drvdata(devptr, NULL);
  	return 0;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
722

077d0ac5b   Takashi Iwai   [ALSA] mtpav - Us...
723
724
725
726
  #define SND_MTPAV_DRIVER	"snd_mtpav"
  
  static struct platform_driver snd_mtpav_driver = {
  	.probe		= snd_mtpav_probe,
788c60433   Prarit Bhargava   [ALSA] Fix __devi...
727
  	.remove		= __devexit_p(snd_mtpav_remove),
077d0ac5b   Takashi Iwai   [ALSA] mtpav - Us...
728
729
730
731
732
733
  	.driver		= {
  		.name	= SND_MTPAV_DRIVER
  	},
  };
  
  static int __init alsa_card_mtpav_init(void)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
734
  {
077d0ac5b   Takashi Iwai   [ALSA] mtpav - Us...
735
  	int err;
077d0ac5b   Takashi Iwai   [ALSA] mtpav - Us...
736
737
738
739
740
  
  	if ((err = platform_driver_register(&snd_mtpav_driver)) < 0)
  		return err;
  
  	device = platform_device_register_simple(SND_MTPAV_DRIVER, -1, NULL, 0);
7152447df   Rene Herman   [ALSA] unregister...
741
742
743
744
745
746
747
748
749
  	if (!IS_ERR(device)) {
  		if (platform_get_drvdata(device))
  			return 0;
  		platform_device_unregister(device);
  		err = -ENODEV;
  	} else
  		err = PTR_ERR(device);
  	platform_driver_unregister(&snd_mtpav_driver);
  	return err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
750
  }
077d0ac5b   Takashi Iwai   [ALSA] mtpav - Us...
751
752
  static void __exit alsa_card_mtpav_exit(void)
  {
f7a9275d9   Clemens Ladisch   [ALSA] unregister...
753
  	platform_device_unregister(device);
077d0ac5b   Takashi Iwai   [ALSA] mtpav - Us...
754
755
  	platform_driver_unregister(&snd_mtpav_driver);
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
756
757
758
  
  module_init(alsa_card_mtpav_init)
  module_exit(alsa_card_mtpav_exit)