File Index
Files and directories
Sign in
FILE

mtpav.c

File preview with syntax highlighting.

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