Blame view

sound/drivers/mtpav.c 19.1 KB
1a59d1b8e   Thomas Gleixner   treewide: Replace...
1
  // SPDX-License-Identifier: GPL-2.0-or-later
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2
3
4
5
6
7
  /*
   *      MOTU Midi Timepiece ALSA Main routines
   *      Copyright by Michael T. Mayers (c) Jan 09, 2000
   *      mail: michael@tweakoz.com
   *      Thanks to John Galbraith
   *
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
   *      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
   *
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
28
29
30
31
32
33
34
   * 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
35
   */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
36
37
  #include <linux/init.h>
  #include <linux/interrupt.h>
da155d5b4   Paul Gortmaker   sound: Add module...
38
  #include <linux/module.h>
077d0ac5b   Takashi Iwai   [ALSA] mtpav - Us...
39
40
  #include <linux/err.h>
  #include <linux/platform_device.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
41
  #include <linux/ioport.h>
ddfb31992   Takashi Iwai   ALSA: use linux/i...
42
  #include <linux/io.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
43
44
45
46
47
  #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
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
  /*
   *      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.");
b11ce420c   David Howells   Annotate hardware...
71
  module_param_hw(port, long, ioport, 0444);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
72
  MODULE_PARM_DESC(port, "Parallel port # for MotuMTPAV MIDI.");
b11ce420c   David Howells   Annotate hardware...
73
  module_param_hw(irq, int, irq, 0444);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
74
75
76
  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...
77
  static struct platform_device *device;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
  /*
   *      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...
109
  struct mtpav_port {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
110
111
112
113
  	u8 number;
  	u8 hwport;
  	u8 mode;
  	u8 running_status;
4a4d2cfd8   Takashi Iwai   [ALSA] Remove xxx...
114
115
  	struct snd_rawmidi_substream *input;
  	struct snd_rawmidi_substream *output;
077d0ac5b   Takashi Iwai   [ALSA] mtpav - Us...
116
  };
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
117

077d0ac5b   Takashi Iwai   [ALSA] mtpav - Us...
118
  struct mtpav {
4a4d2cfd8   Takashi Iwai   [ALSA] Remove xxx...
119
  	struct snd_card *card;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
120
121
122
123
124
125
126
  	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...
127
  	struct snd_rawmidi *rmidi;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
128
  	int num_ports;		/* number of hw ports (1-8) */
077d0ac5b   Takashi Iwai   [ALSA] mtpav - Us...
129
  	struct mtpav_port ports[NUMPORTS];	/* all ports including computer, adat and bc */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
130
131
132
133
134
  
  	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...
135
  };
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
136
137
138
  
  
  /*
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
   * 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...
159
  static int translate_subdevice_to_hwport(struct mtpav *chip, int subdev)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
160
161
162
163
164
165
166
167
168
169
170
171
172
  {
  	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...
173
  static int translate_hwport_to_subdevice(struct mtpav *chip, int hwport)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
  {
  	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...
197
  static u8 snd_mtpav_getreg(struct mtpav *chip, u16 reg)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
  {
  	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...
214
  static inline void snd_mtpav_mputreg(struct mtpav *chip, u16 reg, u8 val)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
215
  {
077d0ac5b   Takashi Iwai   [ALSA] mtpav - Us...
216
217
  	if (reg == DREG || reg == CREG)
  		outb(val, chip->port + reg);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
218
219
220
221
  }
  
  /*
   */
077d0ac5b   Takashi Iwai   [ALSA] mtpav - Us...
222
  static void snd_mtpav_wait_rfdhi(struct mtpav *chip)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
223
224
225
226
227
228
229
230
231
232
  {
  	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...
233
  static void snd_mtpav_send_byte(struct mtpav *chip, u8 byte)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
  {
  	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...
259
260
  static void snd_mtpav_output_port_write(struct mtpav *mtp_card,
  					struct mtpav_port *portp,
4a4d2cfd8   Takashi Iwai   [ALSA] Remove xxx...
261
  					struct snd_rawmidi_substream *substream)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
262
263
264
265
266
267
268
269
270
  {
  	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...
271
272
  	if (portp->hwport != mtp_card->outmidihwport) {
  		mtp_card->outmidihwport = portp->hwport;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
273
274
  
  		snd_mtpav_send_byte(mtp_card, 0xf5);
077d0ac5b   Takashi Iwai   [ALSA] mtpav - Us...
275
  		snd_mtpav_send_byte(mtp_card, portp->hwport);
45203832d   Takashi Iwai   ALSA: Add missing...
276
277
278
279
280
  		/*
  		snd_printk(KERN_DEBUG "new outport: 0x%x
  ",
  			   (unsigned int) portp->hwport);
  		*/
077d0ac5b   Takashi Iwai   [ALSA] mtpav - Us...
281
282
  		if (!(outbyte & 0x80) && portp->running_status)
  			snd_mtpav_send_byte(mtp_card, portp->running_status);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
283
284
285
286
287
288
  	}
  
  	// send data
  
  	do {
  		if (outbyte & 0x80)
077d0ac5b   Takashi Iwai   [ALSA] mtpav - Us...
289
  			portp->running_status = outbyte;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
290
291
292
293
  		
  		snd_mtpav_send_byte(mtp_card, outbyte);
  	} while (snd_rawmidi_transmit(substream, &outbyte, 1) == 1);
  }
4a4d2cfd8   Takashi Iwai   [ALSA] Remove xxx...
294
  static void snd_mtpav_output_write(struct snd_rawmidi_substream *substream)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
295
  {
077d0ac5b   Takashi Iwai   [ALSA] mtpav - Us...
296
297
  	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
298
299
300
  	unsigned long flags;
  
  	spin_lock_irqsave(&mtp_card->spinlock, flags);
077d0ac5b   Takashi Iwai   [ALSA] mtpav - Us...
301
  	snd_mtpav_output_port_write(mtp_card, portp, substream);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
302
303
304
305
306
307
308
  	spin_unlock_irqrestore(&mtp_card->spinlock, flags);
  }
  
  
  /*
   *      mtpav control
   */
077d0ac5b   Takashi Iwai   [ALSA] mtpav - Us...
309
  static void snd_mtpav_portscan(struct mtpav *chip)	// put mtp into smart routing mode
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
310
311
312
313
314
315
316
317
318
319
320
321
  {
  	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...
322
  static int snd_mtpav_input_open(struct snd_rawmidi_substream *substream)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
323
  {
077d0ac5b   Takashi Iwai   [ALSA] mtpav - Us...
324
325
  	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
326
  	unsigned long flags;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
327

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
328
329
330
331
332
333
334
335
336
337
338
  	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...
339
  static int snd_mtpav_input_close(struct snd_rawmidi_substream *substream)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
340
  {
077d0ac5b   Takashi Iwai   [ALSA] mtpav - Us...
341
342
  	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
343
  	unsigned long flags;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
344
345
  
  	spin_lock_irqsave(&mtp_card->spinlock, flags);
077d0ac5b   Takashi Iwai   [ALSA] mtpav - Us...
346
  	portp->mode &= ~MTPAV_MODE_INPUT_OPENED;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
347
348
349
  	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
350
351
352
353
354
355
  	spin_unlock_irqrestore(&mtp_card->spinlock, flags);
  	return 0;
  }
  
  /*
   */
4a4d2cfd8   Takashi Iwai   [ALSA] Remove xxx...
356
  static void snd_mtpav_input_trigger(struct snd_rawmidi_substream *substream, int up)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
357
  {
077d0ac5b   Takashi Iwai   [ALSA] mtpav - Us...
358
359
  	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
360
  	unsigned long flags;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
361
362
363
364
365
366
367
368
369
370
371
372
373
374
  
  	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
   */
bc47ba90b   Kees Cook   ALSA: drivers: Co...
375
  static void snd_mtpav_output_timer(struct timer_list *t)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
376
377
  {
  	unsigned long flags;
bc47ba90b   Kees Cook   ALSA: drivers: Co...
378
  	struct mtpav *chip = from_timer(chip, t, timer);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
379
380
381
382
  	int p;
  
  	spin_lock_irqsave(&chip->spinlock, flags);
  	/* reprogram timer */
2b1b78114   Takashi Iwai   ALSA: mtpav: Use ...
383
  	mod_timer(&chip->timer, 1 + jiffies);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
384
385
  	/* process each port */
  	for (p = 0; p <= chip->num_ports * 2 + MTPAV_PIDX_BROADCAST; p++) {
077d0ac5b   Takashi Iwai   [ALSA] mtpav - Us...
386
  		struct mtpav_port *portp = &chip->ports[p];
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
387
  		if ((portp->mode & MTPAV_MODE_OUTPUT_TRIGGERED) && portp->output)
077d0ac5b   Takashi Iwai   [ALSA] mtpav - Us...
388
  			snd_mtpav_output_port_write(chip, portp, portp->output);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
389
390
391
392
393
  	}
  	spin_unlock_irqrestore(&chip->spinlock, flags);
  }
  
  /* spinlock held! */
077d0ac5b   Takashi Iwai   [ALSA] mtpav - Us...
394
  static void snd_mtpav_add_output_timer(struct mtpav *chip)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
395
  {
2b1b78114   Takashi Iwai   ALSA: mtpav: Use ...
396
  	mod_timer(&chip->timer, 1 + jiffies);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
397
398
399
  }
  
  /* spinlock held! */
077d0ac5b   Takashi Iwai   [ALSA] mtpav - Us...
400
  static void snd_mtpav_remove_output_timer(struct mtpav *chip)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
401
402
403
404
405
406
  {
  	del_timer(&chip->timer);
  }
  
  /*
   */
4a4d2cfd8   Takashi Iwai   [ALSA] Remove xxx...
407
  static int snd_mtpav_output_open(struct snd_rawmidi_substream *substream)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
408
  {
077d0ac5b   Takashi Iwai   [ALSA] mtpav - Us...
409
410
  	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
411
  	unsigned long flags;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
412
413
414
415
416
417
418
419
420
421
  
  	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...
422
  static int snd_mtpav_output_close(struct snd_rawmidi_substream *substream)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
423
  {
077d0ac5b   Takashi Iwai   [ALSA] mtpav - Us...
424
425
  	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
426
  	unsigned long flags;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
427
428
  
  	spin_lock_irqsave(&mtp_card->spinlock, flags);
077d0ac5b   Takashi Iwai   [ALSA] mtpav - Us...
429
  	portp->mode &= ~MTPAV_MODE_OUTPUT_OPENED;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
430
431
432
433
434
435
436
  	portp->output = NULL;
  	spin_unlock_irqrestore(&mtp_card->spinlock, flags);
  	return 0;
  };
  
  /*
   */
4a4d2cfd8   Takashi Iwai   [ALSA] Remove xxx...
437
  static void snd_mtpav_output_trigger(struct snd_rawmidi_substream *substream, int up)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
438
  {
077d0ac5b   Takashi Iwai   [ALSA] mtpav - Us...
439
440
  	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
441
  	unsigned long flags;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
442
443
444
  
  	spin_lock_irqsave(&mtp_card->spinlock, flags);
  	if (up) {
077d0ac5b   Takashi Iwai   [ALSA] mtpav - Us...
445
  		if (! (portp->mode & MTPAV_MODE_OUTPUT_TRIGGERED)) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
  			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...
464
  static void snd_mtpav_inmidi_process(struct mtpav *mcrd, u8 inbyte)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
465
  {
077d0ac5b   Takashi Iwai   [ALSA] mtpav - Us...
466
  	struct mtpav_port *portp;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
467
468
469
470
471
  
  	if ((int)mcrd->inmidiport > mcrd->num_ports * 2 + MTPAV_PIDX_BROADCAST)
  		return;
  
  	portp = &mcrd->ports[mcrd->inmidiport];
077d0ac5b   Takashi Iwai   [ALSA] mtpav - Us...
472
  	if (portp->mode & MTPAV_MODE_INPUT_TRIGGERED)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
473
  		snd_rawmidi_receive(portp->input, &inbyte, 1);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
474
  }
077d0ac5b   Takashi Iwai   [ALSA] mtpav - Us...
475
  static void snd_mtpav_inmidi_h(struct mtpav *mcrd, u8 inbyte)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
476
  {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
  	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...
493
  static void snd_mtpav_read_bytes(struct mtpav *mcrd)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
494
495
496
497
498
499
500
  {
  	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...
501
502
  	/* printk(KERN_DEBUG "snd_mtpav_read_bytes() sbyt: 0x%x
  ", sbyt); */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
  
  	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...
530
  static irqreturn_t snd_mtpav_irqh(int irq, void *dev_id)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
531
  {
077d0ac5b   Takashi Iwai   [ALSA] mtpav - Us...
532
  	struct mtpav *mcard = dev_id;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
533

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
534
535
536
537
538
539
540
541
542
  	spin_lock(&mcard->spinlock);
  	snd_mtpav_read_bytes(mcard);
  	spin_unlock(&mcard->spinlock);
  	return IRQ_HANDLED;
  }
  
  /*
   * get ISA resources
   */
fbbb01a12   Bill Pemberton   ALSA: drivers: re...
543
  static int snd_mtpav_get_ISA(struct mtpav *mcard)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
544
545
  {
  	if ((mcard->res_port = request_region(port, 3, "MotuMTPAV MIDI")) == NULL) {
45203832d   Takashi Iwai   ALSA: Add missing...
546
547
  		snd_printk(KERN_ERR "MTVAP port 0x%lx is busy
  ", port);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
548
549
550
  		return -EBUSY;
  	}
  	mcard->port = port;
88e24c3a4   Yong Zhang   sound: irq: Remov...
551
  	if (request_irq(irq, snd_mtpav_irqh, 0, "MOTU MTPAV", mcard)) {
45203832d   Takashi Iwai   ALSA: Add missing...
552
553
  		snd_printk(KERN_ERR "MTVAP IRQ %d busy
  ", irq);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
554
555
556
557
558
559
560
561
562
  		return -EBUSY;
  	}
  	mcard->irq = irq;
  	return 0;
  }
  
  
  /*
   */
c36f486d7   Takashi Iwai   ALSA: drivers: Co...
563
  static const struct snd_rawmidi_ops snd_mtpav_output = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
564
565
566
567
  	.open =		snd_mtpav_output_open,
  	.close =	snd_mtpav_output_close,
  	.trigger =	snd_mtpav_output_trigger,
  };
c36f486d7   Takashi Iwai   ALSA: drivers: Co...
568
  static const struct snd_rawmidi_ops snd_mtpav_input = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
569
570
571
572
573
574
575
576
577
  	.open =		snd_mtpav_input_open,
  	.close =	snd_mtpav_input_close,
  	.trigger =	snd_mtpav_input_trigger,
  };
  
  
  /*
   * get RAWMIDI resources
   */
fbbb01a12   Bill Pemberton   ALSA: drivers: re...
578
579
  static void snd_mtpav_set_name(struct mtpav *chip,
  			       struct snd_rawmidi_substream *substream)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
580
581
582
583
584
585
586
587
588
589
590
591
  {
  	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");
  }
fbbb01a12   Bill Pemberton   ALSA: drivers: re...
592
  static int snd_mtpav_get_RAWMIDI(struct mtpav *mcard)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
593
  {
077d0ac5b   Takashi Iwai   [ALSA] mtpav - Us...
594
  	int rval;
4a4d2cfd8   Takashi Iwai   [ALSA] Remove xxx...
595
596
  	struct snd_rawmidi *rawmidi;
  	struct snd_rawmidi_substream *substream;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
597
  	struct list_head *list;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
598
  	if (hwports < 1)
077d0ac5b   Takashi Iwai   [ALSA] mtpav - Us...
599
  		hwports = 1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
600
  	else if (hwports > 8)
077d0ac5b   Takashi Iwai   [ALSA] mtpav - Us...
601
602
  		hwports = 8;
  	mcard->num_ports = hwports;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
603
604
605
606
607
608
609
  
  	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...
610
  	rawmidi->private_data = mcard;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
611
612
  
  	list_for_each(list, &rawmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT].substreams) {
4a4d2cfd8   Takashi Iwai   [ALSA] Remove xxx...
613
  		substream = list_entry(list, struct snd_rawmidi_substream, list);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
614
615
616
617
  		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...
618
  		substream = list_entry(list, struct snd_rawmidi_substream, list);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
619
620
621
622
623
624
625
  		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
626
627
628
629
630
  	return 0;
  }
  
  /*
   */
077d0ac5b   Takashi Iwai   [ALSA] mtpav - Us...
631
  static void snd_mtpav_free(struct snd_card *card)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
632
  {
077d0ac5b   Takashi Iwai   [ALSA] mtpav - Us...
633
  	struct mtpav *crd = card->private_data;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
634
635
636
637
638
639
640
641
  	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...
642
  	release_and_free_resource(crd->res_port);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
643
644
645
646
  }
  
  /*
   */
fbbb01a12   Bill Pemberton   ALSA: drivers: re...
647
  static int snd_mtpav_probe(struct platform_device *dev)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
648
  {
077d0ac5b   Takashi Iwai   [ALSA] mtpav - Us...
649
650
651
  	struct snd_card *card;
  	int err;
  	struct mtpav *mtp_card;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
652

5872f3f62   Takashi Iwai   ALSA: drivers: Co...
653
654
  	err = snd_card_new(&dev->dev, index, id, THIS_MODULE,
  			   sizeof(*mtp_card), &card);
bd7dd77c2   Takashi Iwai   ALSA: Convert to ...
655
656
  	if (err < 0)
  		return err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
657

077d0ac5b   Takashi Iwai   [ALSA] mtpav - Us...
658
659
  	mtp_card = card->private_data;
  	spin_lock_init(&mtp_card->spinlock);
077d0ac5b   Takashi Iwai   [ALSA] mtpav - Us...
660
661
662
  	mtp_card->card = card;
  	mtp_card->irq = -1;
  	mtp_card->share_irq = 0;
077d0ac5b   Takashi Iwai   [ALSA] mtpav - Us...
663
664
  	mtp_card->inmidistate = 0;
  	mtp_card->outmidihwport = 0xffffffff;
bc47ba90b   Kees Cook   ALSA: drivers: Co...
665
  	timer_setup(&mtp_card->timer, snd_mtpav_output_timer, 0);
077d0ac5b   Takashi Iwai   [ALSA] mtpav - Us...
666
667
  
  	card->private_free = snd_mtpav_free;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
668

32fe61426   Takashi Iwai   ALSA: mtpav - Fix...
669
670
671
  	err = snd_mtpav_get_RAWMIDI(mtp_card);
  	if (err < 0)
  		goto __error;
32cf9a16f   Takashi Iwai   ALSA: mtpav - Fix...
672
  	mtp_card->inmidiport = mtp_card->num_ports + MTPAV_PIDX_BROADCAST;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
673
  	err = snd_mtpav_get_ISA(mtp_card);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
674
675
  	if (err < 0)
  		goto __error;
077d0ac5b   Takashi Iwai   [ALSA] mtpav - Us...
676
677
678
679
  	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
680

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

077d0ac5b   Takashi Iwai   [ALSA] mtpav - Us...
683
  	err = snd_card_register(mtp_card->card);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
684
685
  	if (err < 0)
  		goto __error;
077d0ac5b   Takashi Iwai   [ALSA] mtpav - Us...
686
  	platform_set_drvdata(dev, card);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
687
688
  	printk(KERN_INFO "Motu MidiTimePiece on parallel port irq: %d ioport: 0x%lx
  ", irq, port);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
689
  	return 0;
077d0ac5b   Takashi Iwai   [ALSA] mtpav - Us...
690
691
   __error:
  	snd_card_free(card);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
692
693
  	return err;
  }
fbbb01a12   Bill Pemberton   ALSA: drivers: re...
694
  static int snd_mtpav_remove(struct platform_device *devptr)
077d0ac5b   Takashi Iwai   [ALSA] mtpav - Us...
695
696
  {
  	snd_card_free(platform_get_drvdata(devptr));
077d0ac5b   Takashi Iwai   [ALSA] mtpav - Us...
697
698
  	return 0;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
699

077d0ac5b   Takashi Iwai   [ALSA] mtpav - Us...
700
701
702
703
  #define SND_MTPAV_DRIVER	"snd_mtpav"
  
  static struct platform_driver snd_mtpav_driver = {
  	.probe		= snd_mtpav_probe,
fbbb01a12   Bill Pemberton   ALSA: drivers: re...
704
  	.remove		= snd_mtpav_remove,
077d0ac5b   Takashi Iwai   [ALSA] mtpav - Us...
705
  	.driver		= {
8bf01d8ab   Takashi Iwai   ALSA: Add missing...
706
  		.name	= SND_MTPAV_DRIVER,
077d0ac5b   Takashi Iwai   [ALSA] mtpav - Us...
707
708
709
710
  	},
  };
  
  static int __init alsa_card_mtpav_init(void)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
711
  {
077d0ac5b   Takashi Iwai   [ALSA] mtpav - Us...
712
  	int err;
077d0ac5b   Takashi Iwai   [ALSA] mtpav - Us...
713
714
715
716
717
  
  	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...
718
719
720
721
722
723
724
725
726
  	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
727
  }
077d0ac5b   Takashi Iwai   [ALSA] mtpav - Us...
728
729
  static void __exit alsa_card_mtpav_exit(void)
  {
f7a9275d9   Clemens Ladisch   [ALSA] unregister...
730
  	platform_device_unregister(device);
077d0ac5b   Takashi Iwai   [ALSA] mtpav - Us...
731
732
  	platform_driver_unregister(&snd_mtpav_driver);
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
733
734
735
  
  module_init(alsa_card_mtpav_init)
  module_exit(alsa_card_mtpav_exit)