18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *      MOTU Midi Timepiece ALSA Main routines
48c2ecf20Sopenharmony_ci *      Copyright by Michael T. Mayers (c) Jan 09, 2000
58c2ecf20Sopenharmony_ci *      mail: michael@tweakoz.com
68c2ecf20Sopenharmony_ci *      Thanks to John Galbraith
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci *      This driver is for the 'Mark Of The Unicorn' (MOTU)
98c2ecf20Sopenharmony_ci *      MidiTimePiece AV multiport MIDI interface
108c2ecf20Sopenharmony_ci *
118c2ecf20Sopenharmony_ci *      IOPORTS
128c2ecf20Sopenharmony_ci *      -------
138c2ecf20Sopenharmony_ci *      8 MIDI Ins and 8 MIDI outs
148c2ecf20Sopenharmony_ci *      Video Sync In (BNC), Word Sync Out (BNC),
158c2ecf20Sopenharmony_ci *      ADAT Sync Out (DB9)
168c2ecf20Sopenharmony_ci *      SMPTE in/out (1/4")
178c2ecf20Sopenharmony_ci *      2 programmable pedal/footswitch inputs and 4 programmable MIDI controller knobs.
188c2ecf20Sopenharmony_ci *      Macintosh RS422 serial port
198c2ecf20Sopenharmony_ci *      RS422 "network" port for ganging multiple MTP's
208c2ecf20Sopenharmony_ci *      PC Parallel Port ( which this driver currently uses )
218c2ecf20Sopenharmony_ci *
228c2ecf20Sopenharmony_ci *      MISC FEATURES
238c2ecf20Sopenharmony_ci *      -------------
248c2ecf20Sopenharmony_ci *      Hardware MIDI routing, merging, and filtering
258c2ecf20Sopenharmony_ci *      MIDI Synchronization to Video, ADAT, SMPTE and other Clock sources
268c2ecf20Sopenharmony_ci *      128 'scene' memories, recallable from MIDI program change
278c2ecf20Sopenharmony_ci *
288c2ecf20Sopenharmony_ci * ChangeLog
298c2ecf20Sopenharmony_ci * Jun 11 2001	Takashi Iwai <tiwai@suse.de>
308c2ecf20Sopenharmony_ci *      - Recoded & debugged
318c2ecf20Sopenharmony_ci *      - Added timer interrupt for midi outputs
328c2ecf20Sopenharmony_ci *      - hwports is between 1 and 8, which specifies the number of hardware ports.
338c2ecf20Sopenharmony_ci *        The three global ports, computer, adat and broadcast ports, are created
348c2ecf20Sopenharmony_ci *        always after h/w and remote ports.
358c2ecf20Sopenharmony_ci */
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci#include <linux/init.h>
388c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
398c2ecf20Sopenharmony_ci#include <linux/module.h>
408c2ecf20Sopenharmony_ci#include <linux/err.h>
418c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
428c2ecf20Sopenharmony_ci#include <linux/ioport.h>
438c2ecf20Sopenharmony_ci#include <linux/io.h>
448c2ecf20Sopenharmony_ci#include <linux/moduleparam.h>
458c2ecf20Sopenharmony_ci#include <sound/core.h>
468c2ecf20Sopenharmony_ci#include <sound/initval.h>
478c2ecf20Sopenharmony_ci#include <sound/rawmidi.h>
488c2ecf20Sopenharmony_ci#include <linux/delay.h>
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci/*
518c2ecf20Sopenharmony_ci *      globals
528c2ecf20Sopenharmony_ci */
538c2ecf20Sopenharmony_ciMODULE_AUTHOR("Michael T. Mayers");
548c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("MOTU MidiTimePiece AV multiport MIDI");
558c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
568c2ecf20Sopenharmony_ciMODULE_SUPPORTED_DEVICE("{{MOTU,MidiTimePiece AV multiport MIDI}}");
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci// io resources
598c2ecf20Sopenharmony_ci#define MTPAV_IOBASE		0x378
608c2ecf20Sopenharmony_ci#define MTPAV_IRQ		7
618c2ecf20Sopenharmony_ci#define MTPAV_MAX_PORTS		8
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_cistatic int index = SNDRV_DEFAULT_IDX1;
648c2ecf20Sopenharmony_cistatic char *id = SNDRV_DEFAULT_STR1;
658c2ecf20Sopenharmony_cistatic long port = MTPAV_IOBASE;	/* 0x378, 0x278 */
668c2ecf20Sopenharmony_cistatic int irq = MTPAV_IRQ;		/* 7, 5 */
678c2ecf20Sopenharmony_cistatic int hwports = MTPAV_MAX_PORTS;	/* use hardware ports 1-8 */
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_cimodule_param(index, int, 0444);
708c2ecf20Sopenharmony_ciMODULE_PARM_DESC(index, "Index value for MotuMTPAV MIDI.");
718c2ecf20Sopenharmony_cimodule_param(id, charp, 0444);
728c2ecf20Sopenharmony_ciMODULE_PARM_DESC(id, "ID string for MotuMTPAV MIDI.");
738c2ecf20Sopenharmony_cimodule_param_hw(port, long, ioport, 0444);
748c2ecf20Sopenharmony_ciMODULE_PARM_DESC(port, "Parallel port # for MotuMTPAV MIDI.");
758c2ecf20Sopenharmony_cimodule_param_hw(irq, int, irq, 0444);
768c2ecf20Sopenharmony_ciMODULE_PARM_DESC(irq, "Parallel IRQ # for MotuMTPAV MIDI.");
778c2ecf20Sopenharmony_cimodule_param(hwports, int, 0444);
788c2ecf20Sopenharmony_ciMODULE_PARM_DESC(hwports, "Hardware ports # for MotuMTPAV MIDI.");
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_cistatic struct platform_device *device;
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci/*
838c2ecf20Sopenharmony_ci *      defines
848c2ecf20Sopenharmony_ci */
858c2ecf20Sopenharmony_ci//#define USE_FAKE_MTP //       don't actually read/write to MTP device (for debugging without an actual unit) (does not work yet)
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci// parallel port usage masks
888c2ecf20Sopenharmony_ci#define SIGS_BYTE 0x08
898c2ecf20Sopenharmony_ci#define SIGS_RFD 0x80
908c2ecf20Sopenharmony_ci#define SIGS_IRQ 0x40
918c2ecf20Sopenharmony_ci#define SIGS_IN0 0x10
928c2ecf20Sopenharmony_ci#define SIGS_IN1 0x20
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci#define SIGC_WRITE 0x04
958c2ecf20Sopenharmony_ci#define SIGC_READ 0x08
968c2ecf20Sopenharmony_ci#define SIGC_INTEN 0x10
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci#define DREG 0
998c2ecf20Sopenharmony_ci#define SREG 1
1008c2ecf20Sopenharmony_ci#define CREG 2
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci//
1038c2ecf20Sopenharmony_ci#define MTPAV_MODE_INPUT_OPENED		0x01
1048c2ecf20Sopenharmony_ci#define MTPAV_MODE_OUTPUT_OPENED	0x02
1058c2ecf20Sopenharmony_ci#define MTPAV_MODE_INPUT_TRIGGERED	0x04
1068c2ecf20Sopenharmony_ci#define MTPAV_MODE_OUTPUT_TRIGGERED	0x08
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci#define NUMPORTS (0x12+1)
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci/*
1128c2ecf20Sopenharmony_ci */
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_cistruct mtpav_port {
1158c2ecf20Sopenharmony_ci	u8 number;
1168c2ecf20Sopenharmony_ci	u8 hwport;
1178c2ecf20Sopenharmony_ci	u8 mode;
1188c2ecf20Sopenharmony_ci	u8 running_status;
1198c2ecf20Sopenharmony_ci	struct snd_rawmidi_substream *input;
1208c2ecf20Sopenharmony_ci	struct snd_rawmidi_substream *output;
1218c2ecf20Sopenharmony_ci};
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_cistruct mtpav {
1248c2ecf20Sopenharmony_ci	struct snd_card *card;
1258c2ecf20Sopenharmony_ci	unsigned long port;
1268c2ecf20Sopenharmony_ci	struct resource *res_port;
1278c2ecf20Sopenharmony_ci	int irq;			/* interrupt (for inputs) */
1288c2ecf20Sopenharmony_ci	spinlock_t spinlock;
1298c2ecf20Sopenharmony_ci	int share_irq;			/* number of accesses to input interrupts */
1308c2ecf20Sopenharmony_ci	int istimer;			/* number of accesses to timer interrupts */
1318c2ecf20Sopenharmony_ci	struct timer_list timer;	/* timer interrupts for outputs */
1328c2ecf20Sopenharmony_ci	struct snd_rawmidi *rmidi;
1338c2ecf20Sopenharmony_ci	int num_ports;		/* number of hw ports (1-8) */
1348c2ecf20Sopenharmony_ci	struct mtpav_port ports[NUMPORTS];	/* all ports including computer, adat and bc */
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci	u32 inmidiport;		/* selected input midi port */
1378c2ecf20Sopenharmony_ci	u32 inmidistate;	/* during midi command 0xf5 */
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci	u32 outmidihwport;	/* selected output midi hw port */
1408c2ecf20Sopenharmony_ci};
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci/*
1448c2ecf20Sopenharmony_ci * possible hardware ports (selected by 0xf5 port message)
1458c2ecf20Sopenharmony_ci *      0x00		all ports
1468c2ecf20Sopenharmony_ci *      0x01 .. 0x08    this MTP's ports 1..8
1478c2ecf20Sopenharmony_ci *      0x09 .. 0x10    networked MTP's ports (9..16)
1488c2ecf20Sopenharmony_ci *      0x11            networked MTP's computer port
1498c2ecf20Sopenharmony_ci *      0x63            to ADAT
1508c2ecf20Sopenharmony_ci *
1518c2ecf20Sopenharmony_ci * mappig:
1528c2ecf20Sopenharmony_ci *  subdevice 0 - (X-1)    ports
1538c2ecf20Sopenharmony_ci *            X - (2*X-1)  networked ports
1548c2ecf20Sopenharmony_ci *            X            computer
1558c2ecf20Sopenharmony_ci *            X+1          ADAT
1568c2ecf20Sopenharmony_ci *            X+2          all ports
1578c2ecf20Sopenharmony_ci *
1588c2ecf20Sopenharmony_ci *  where X = chip->num_ports
1598c2ecf20Sopenharmony_ci */
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci#define MTPAV_PIDX_COMPUTER	0
1628c2ecf20Sopenharmony_ci#define MTPAV_PIDX_ADAT		1
1638c2ecf20Sopenharmony_ci#define MTPAV_PIDX_BROADCAST	2
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_cistatic int translate_subdevice_to_hwport(struct mtpav *chip, int subdev)
1678c2ecf20Sopenharmony_ci{
1688c2ecf20Sopenharmony_ci	if (subdev < 0)
1698c2ecf20Sopenharmony_ci		return 0x01; /* invalid - use port 0 as default */
1708c2ecf20Sopenharmony_ci	else if (subdev < chip->num_ports)
1718c2ecf20Sopenharmony_ci		return subdev + 1; /* single mtp port */
1728c2ecf20Sopenharmony_ci	else if (subdev < chip->num_ports * 2)
1738c2ecf20Sopenharmony_ci		return subdev - chip->num_ports + 0x09; /* remote port */
1748c2ecf20Sopenharmony_ci	else if (subdev == chip->num_ports * 2 + MTPAV_PIDX_COMPUTER)
1758c2ecf20Sopenharmony_ci		return 0x11; /* computer port */
1768c2ecf20Sopenharmony_ci	else if (subdev == chip->num_ports + MTPAV_PIDX_ADAT)
1778c2ecf20Sopenharmony_ci		return 0x63;		/* ADAT */
1788c2ecf20Sopenharmony_ci	return 0; /* all ports */
1798c2ecf20Sopenharmony_ci}
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_cistatic int translate_hwport_to_subdevice(struct mtpav *chip, int hwport)
1828c2ecf20Sopenharmony_ci{
1838c2ecf20Sopenharmony_ci	int p;
1848c2ecf20Sopenharmony_ci	if (hwport <= 0x00) /* all ports */
1858c2ecf20Sopenharmony_ci		return chip->num_ports + MTPAV_PIDX_BROADCAST;
1868c2ecf20Sopenharmony_ci	else if (hwport <= 0x08) { /* single port */
1878c2ecf20Sopenharmony_ci		p = hwport - 1;
1888c2ecf20Sopenharmony_ci		if (p >= chip->num_ports)
1898c2ecf20Sopenharmony_ci			p = 0;
1908c2ecf20Sopenharmony_ci		return p;
1918c2ecf20Sopenharmony_ci	} else if (hwport <= 0x10) { /* remote port */
1928c2ecf20Sopenharmony_ci		p = hwport - 0x09 + chip->num_ports;
1938c2ecf20Sopenharmony_ci		if (p >= chip->num_ports * 2)
1948c2ecf20Sopenharmony_ci			p = chip->num_ports;
1958c2ecf20Sopenharmony_ci		return p;
1968c2ecf20Sopenharmony_ci	} else if (hwport == 0x11)  /* computer port */
1978c2ecf20Sopenharmony_ci		return chip->num_ports + MTPAV_PIDX_COMPUTER;
1988c2ecf20Sopenharmony_ci	else  /* ADAT */
1998c2ecf20Sopenharmony_ci		return chip->num_ports + MTPAV_PIDX_ADAT;
2008c2ecf20Sopenharmony_ci}
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_ci/*
2048c2ecf20Sopenharmony_ci */
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_cistatic u8 snd_mtpav_getreg(struct mtpav *chip, u16 reg)
2078c2ecf20Sopenharmony_ci{
2088c2ecf20Sopenharmony_ci	u8 rval = 0;
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_ci	if (reg == SREG) {
2118c2ecf20Sopenharmony_ci		rval = inb(chip->port + SREG);
2128c2ecf20Sopenharmony_ci		rval = (rval & 0xf8);
2138c2ecf20Sopenharmony_ci	} else if (reg == CREG) {
2148c2ecf20Sopenharmony_ci		rval = inb(chip->port + CREG);
2158c2ecf20Sopenharmony_ci		rval = (rval & 0x1c);
2168c2ecf20Sopenharmony_ci	}
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_ci	return rval;
2198c2ecf20Sopenharmony_ci}
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_ci/*
2228c2ecf20Sopenharmony_ci */
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_cistatic inline void snd_mtpav_mputreg(struct mtpav *chip, u16 reg, u8 val)
2258c2ecf20Sopenharmony_ci{
2268c2ecf20Sopenharmony_ci	if (reg == DREG || reg == CREG)
2278c2ecf20Sopenharmony_ci		outb(val, chip->port + reg);
2288c2ecf20Sopenharmony_ci}
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_ci/*
2318c2ecf20Sopenharmony_ci */
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_cistatic void snd_mtpav_wait_rfdhi(struct mtpav *chip)
2348c2ecf20Sopenharmony_ci{
2358c2ecf20Sopenharmony_ci	int counts = 10000;
2368c2ecf20Sopenharmony_ci	u8 sbyte;
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_ci	sbyte = snd_mtpav_getreg(chip, SREG);
2398c2ecf20Sopenharmony_ci	while (!(sbyte & SIGS_RFD) && counts--) {
2408c2ecf20Sopenharmony_ci		sbyte = snd_mtpav_getreg(chip, SREG);
2418c2ecf20Sopenharmony_ci		udelay(10);
2428c2ecf20Sopenharmony_ci	}
2438c2ecf20Sopenharmony_ci}
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_cistatic void snd_mtpav_send_byte(struct mtpav *chip, u8 byte)
2468c2ecf20Sopenharmony_ci{
2478c2ecf20Sopenharmony_ci	u8 tcbyt;
2488c2ecf20Sopenharmony_ci	u8 clrwrite;
2498c2ecf20Sopenharmony_ci	u8 setwrite;
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_ci	snd_mtpav_wait_rfdhi(chip);
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_ci	/////////////////
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_ci	tcbyt = snd_mtpav_getreg(chip, CREG);
2568c2ecf20Sopenharmony_ci	clrwrite = tcbyt & (SIGC_WRITE ^ 0xff);
2578c2ecf20Sopenharmony_ci	setwrite = tcbyt | SIGC_WRITE;
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_ci	snd_mtpav_mputreg(chip, DREG, byte);
2608c2ecf20Sopenharmony_ci	snd_mtpav_mputreg(chip, CREG, clrwrite);	// clear write bit
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_ci	snd_mtpav_mputreg(chip, CREG, setwrite);	// set write bit
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_ci}
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_ci/*
2688c2ecf20Sopenharmony_ci */
2698c2ecf20Sopenharmony_ci
2708c2ecf20Sopenharmony_ci/* call this with spin lock held */
2718c2ecf20Sopenharmony_cistatic void snd_mtpav_output_port_write(struct mtpav *mtp_card,
2728c2ecf20Sopenharmony_ci					struct mtpav_port *portp,
2738c2ecf20Sopenharmony_ci					struct snd_rawmidi_substream *substream)
2748c2ecf20Sopenharmony_ci{
2758c2ecf20Sopenharmony_ci	u8 outbyte;
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_ci	// Get the outbyte first, so we can emulate running status if
2788c2ecf20Sopenharmony_ci	// necessary
2798c2ecf20Sopenharmony_ci	if (snd_rawmidi_transmit(substream, &outbyte, 1) != 1)
2808c2ecf20Sopenharmony_ci		return;
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_ci	// send port change command if necessary
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_ci	if (portp->hwport != mtp_card->outmidihwport) {
2858c2ecf20Sopenharmony_ci		mtp_card->outmidihwport = portp->hwport;
2868c2ecf20Sopenharmony_ci
2878c2ecf20Sopenharmony_ci		snd_mtpav_send_byte(mtp_card, 0xf5);
2888c2ecf20Sopenharmony_ci		snd_mtpav_send_byte(mtp_card, portp->hwport);
2898c2ecf20Sopenharmony_ci		/*
2908c2ecf20Sopenharmony_ci		snd_printk(KERN_DEBUG "new outport: 0x%x\n",
2918c2ecf20Sopenharmony_ci			   (unsigned int) portp->hwport);
2928c2ecf20Sopenharmony_ci		*/
2938c2ecf20Sopenharmony_ci		if (!(outbyte & 0x80) && portp->running_status)
2948c2ecf20Sopenharmony_ci			snd_mtpav_send_byte(mtp_card, portp->running_status);
2958c2ecf20Sopenharmony_ci	}
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_ci	// send data
2988c2ecf20Sopenharmony_ci
2998c2ecf20Sopenharmony_ci	do {
3008c2ecf20Sopenharmony_ci		if (outbyte & 0x80)
3018c2ecf20Sopenharmony_ci			portp->running_status = outbyte;
3028c2ecf20Sopenharmony_ci
3038c2ecf20Sopenharmony_ci		snd_mtpav_send_byte(mtp_card, outbyte);
3048c2ecf20Sopenharmony_ci	} while (snd_rawmidi_transmit(substream, &outbyte, 1) == 1);
3058c2ecf20Sopenharmony_ci}
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_cistatic void snd_mtpav_output_write(struct snd_rawmidi_substream *substream)
3088c2ecf20Sopenharmony_ci{
3098c2ecf20Sopenharmony_ci	struct mtpav *mtp_card = substream->rmidi->private_data;
3108c2ecf20Sopenharmony_ci	struct mtpav_port *portp = &mtp_card->ports[substream->number];
3118c2ecf20Sopenharmony_ci	unsigned long flags;
3128c2ecf20Sopenharmony_ci
3138c2ecf20Sopenharmony_ci	spin_lock_irqsave(&mtp_card->spinlock, flags);
3148c2ecf20Sopenharmony_ci	snd_mtpav_output_port_write(mtp_card, portp, substream);
3158c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&mtp_card->spinlock, flags);
3168c2ecf20Sopenharmony_ci}
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_ci
3198c2ecf20Sopenharmony_ci/*
3208c2ecf20Sopenharmony_ci *      mtpav control
3218c2ecf20Sopenharmony_ci */
3228c2ecf20Sopenharmony_ci
3238c2ecf20Sopenharmony_cistatic void snd_mtpav_portscan(struct mtpav *chip)	// put mtp into smart routing mode
3248c2ecf20Sopenharmony_ci{
3258c2ecf20Sopenharmony_ci	u8 p;
3268c2ecf20Sopenharmony_ci
3278c2ecf20Sopenharmony_ci	for (p = 0; p < 8; p++) {
3288c2ecf20Sopenharmony_ci		snd_mtpav_send_byte(chip, 0xf5);
3298c2ecf20Sopenharmony_ci		snd_mtpav_send_byte(chip, p);
3308c2ecf20Sopenharmony_ci		snd_mtpav_send_byte(chip, 0xfe);
3318c2ecf20Sopenharmony_ci	}
3328c2ecf20Sopenharmony_ci}
3338c2ecf20Sopenharmony_ci
3348c2ecf20Sopenharmony_ci/*
3358c2ecf20Sopenharmony_ci */
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_cistatic int snd_mtpav_input_open(struct snd_rawmidi_substream *substream)
3388c2ecf20Sopenharmony_ci{
3398c2ecf20Sopenharmony_ci	struct mtpav *mtp_card = substream->rmidi->private_data;
3408c2ecf20Sopenharmony_ci	struct mtpav_port *portp = &mtp_card->ports[substream->number];
3418c2ecf20Sopenharmony_ci	unsigned long flags;
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_ci	spin_lock_irqsave(&mtp_card->spinlock, flags);
3448c2ecf20Sopenharmony_ci	portp->mode |= MTPAV_MODE_INPUT_OPENED;
3458c2ecf20Sopenharmony_ci	portp->input = substream;
3468c2ecf20Sopenharmony_ci	if (mtp_card->share_irq++ == 0)
3478c2ecf20Sopenharmony_ci		snd_mtpav_mputreg(mtp_card, CREG, (SIGC_INTEN | SIGC_WRITE));	// enable pport interrupts
3488c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&mtp_card->spinlock, flags);
3498c2ecf20Sopenharmony_ci	return 0;
3508c2ecf20Sopenharmony_ci}
3518c2ecf20Sopenharmony_ci
3528c2ecf20Sopenharmony_ci/*
3538c2ecf20Sopenharmony_ci */
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_cistatic int snd_mtpav_input_close(struct snd_rawmidi_substream *substream)
3568c2ecf20Sopenharmony_ci{
3578c2ecf20Sopenharmony_ci	struct mtpav *mtp_card = substream->rmidi->private_data;
3588c2ecf20Sopenharmony_ci	struct mtpav_port *portp = &mtp_card->ports[substream->number];
3598c2ecf20Sopenharmony_ci	unsigned long flags;
3608c2ecf20Sopenharmony_ci
3618c2ecf20Sopenharmony_ci	spin_lock_irqsave(&mtp_card->spinlock, flags);
3628c2ecf20Sopenharmony_ci	portp->mode &= ~MTPAV_MODE_INPUT_OPENED;
3638c2ecf20Sopenharmony_ci	portp->input = NULL;
3648c2ecf20Sopenharmony_ci	if (--mtp_card->share_irq == 0)
3658c2ecf20Sopenharmony_ci		snd_mtpav_mputreg(mtp_card, CREG, 0);	// disable pport interrupts
3668c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&mtp_card->spinlock, flags);
3678c2ecf20Sopenharmony_ci	return 0;
3688c2ecf20Sopenharmony_ci}
3698c2ecf20Sopenharmony_ci
3708c2ecf20Sopenharmony_ci/*
3718c2ecf20Sopenharmony_ci */
3728c2ecf20Sopenharmony_ci
3738c2ecf20Sopenharmony_cistatic void snd_mtpav_input_trigger(struct snd_rawmidi_substream *substream, int up)
3748c2ecf20Sopenharmony_ci{
3758c2ecf20Sopenharmony_ci	struct mtpav *mtp_card = substream->rmidi->private_data;
3768c2ecf20Sopenharmony_ci	struct mtpav_port *portp = &mtp_card->ports[substream->number];
3778c2ecf20Sopenharmony_ci	unsigned long flags;
3788c2ecf20Sopenharmony_ci
3798c2ecf20Sopenharmony_ci	spin_lock_irqsave(&mtp_card->spinlock, flags);
3808c2ecf20Sopenharmony_ci	if (up)
3818c2ecf20Sopenharmony_ci		portp->mode |= MTPAV_MODE_INPUT_TRIGGERED;
3828c2ecf20Sopenharmony_ci	else
3838c2ecf20Sopenharmony_ci		portp->mode &= ~MTPAV_MODE_INPUT_TRIGGERED;
3848c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&mtp_card->spinlock, flags);
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_ci}
3878c2ecf20Sopenharmony_ci
3888c2ecf20Sopenharmony_ci
3898c2ecf20Sopenharmony_ci/*
3908c2ecf20Sopenharmony_ci * timer interrupt for outputs
3918c2ecf20Sopenharmony_ci */
3928c2ecf20Sopenharmony_ci
3938c2ecf20Sopenharmony_cistatic void snd_mtpav_output_timer(struct timer_list *t)
3948c2ecf20Sopenharmony_ci{
3958c2ecf20Sopenharmony_ci	unsigned long flags;
3968c2ecf20Sopenharmony_ci	struct mtpav *chip = from_timer(chip, t, timer);
3978c2ecf20Sopenharmony_ci	int p;
3988c2ecf20Sopenharmony_ci
3998c2ecf20Sopenharmony_ci	spin_lock_irqsave(&chip->spinlock, flags);
4008c2ecf20Sopenharmony_ci	/* reprogram timer */
4018c2ecf20Sopenharmony_ci	mod_timer(&chip->timer, 1 + jiffies);
4028c2ecf20Sopenharmony_ci	/* process each port */
4038c2ecf20Sopenharmony_ci	for (p = 0; p <= chip->num_ports * 2 + MTPAV_PIDX_BROADCAST; p++) {
4048c2ecf20Sopenharmony_ci		struct mtpav_port *portp = &chip->ports[p];
4058c2ecf20Sopenharmony_ci		if ((portp->mode & MTPAV_MODE_OUTPUT_TRIGGERED) && portp->output)
4068c2ecf20Sopenharmony_ci			snd_mtpav_output_port_write(chip, portp, portp->output);
4078c2ecf20Sopenharmony_ci	}
4088c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&chip->spinlock, flags);
4098c2ecf20Sopenharmony_ci}
4108c2ecf20Sopenharmony_ci
4118c2ecf20Sopenharmony_ci/* spinlock held! */
4128c2ecf20Sopenharmony_cistatic void snd_mtpav_add_output_timer(struct mtpav *chip)
4138c2ecf20Sopenharmony_ci{
4148c2ecf20Sopenharmony_ci	mod_timer(&chip->timer, 1 + jiffies);
4158c2ecf20Sopenharmony_ci}
4168c2ecf20Sopenharmony_ci
4178c2ecf20Sopenharmony_ci/* spinlock held! */
4188c2ecf20Sopenharmony_cistatic void snd_mtpav_remove_output_timer(struct mtpav *chip)
4198c2ecf20Sopenharmony_ci{
4208c2ecf20Sopenharmony_ci	del_timer(&chip->timer);
4218c2ecf20Sopenharmony_ci}
4228c2ecf20Sopenharmony_ci
4238c2ecf20Sopenharmony_ci/*
4248c2ecf20Sopenharmony_ci */
4258c2ecf20Sopenharmony_ci
4268c2ecf20Sopenharmony_cistatic int snd_mtpav_output_open(struct snd_rawmidi_substream *substream)
4278c2ecf20Sopenharmony_ci{
4288c2ecf20Sopenharmony_ci	struct mtpav *mtp_card = substream->rmidi->private_data;
4298c2ecf20Sopenharmony_ci	struct mtpav_port *portp = &mtp_card->ports[substream->number];
4308c2ecf20Sopenharmony_ci	unsigned long flags;
4318c2ecf20Sopenharmony_ci
4328c2ecf20Sopenharmony_ci	spin_lock_irqsave(&mtp_card->spinlock, flags);
4338c2ecf20Sopenharmony_ci	portp->mode |= MTPAV_MODE_OUTPUT_OPENED;
4348c2ecf20Sopenharmony_ci	portp->output = substream;
4358c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&mtp_card->spinlock, flags);
4368c2ecf20Sopenharmony_ci	return 0;
4378c2ecf20Sopenharmony_ci};
4388c2ecf20Sopenharmony_ci
4398c2ecf20Sopenharmony_ci/*
4408c2ecf20Sopenharmony_ci */
4418c2ecf20Sopenharmony_ci
4428c2ecf20Sopenharmony_cistatic int snd_mtpav_output_close(struct snd_rawmidi_substream *substream)
4438c2ecf20Sopenharmony_ci{
4448c2ecf20Sopenharmony_ci	struct mtpav *mtp_card = substream->rmidi->private_data;
4458c2ecf20Sopenharmony_ci	struct mtpav_port *portp = &mtp_card->ports[substream->number];
4468c2ecf20Sopenharmony_ci	unsigned long flags;
4478c2ecf20Sopenharmony_ci
4488c2ecf20Sopenharmony_ci	spin_lock_irqsave(&mtp_card->spinlock, flags);
4498c2ecf20Sopenharmony_ci	portp->mode &= ~MTPAV_MODE_OUTPUT_OPENED;
4508c2ecf20Sopenharmony_ci	portp->output = NULL;
4518c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&mtp_card->spinlock, flags);
4528c2ecf20Sopenharmony_ci	return 0;
4538c2ecf20Sopenharmony_ci};
4548c2ecf20Sopenharmony_ci
4558c2ecf20Sopenharmony_ci/*
4568c2ecf20Sopenharmony_ci */
4578c2ecf20Sopenharmony_ci
4588c2ecf20Sopenharmony_cistatic void snd_mtpav_output_trigger(struct snd_rawmidi_substream *substream, int up)
4598c2ecf20Sopenharmony_ci{
4608c2ecf20Sopenharmony_ci	struct mtpav *mtp_card = substream->rmidi->private_data;
4618c2ecf20Sopenharmony_ci	struct mtpav_port *portp = &mtp_card->ports[substream->number];
4628c2ecf20Sopenharmony_ci	unsigned long flags;
4638c2ecf20Sopenharmony_ci
4648c2ecf20Sopenharmony_ci	spin_lock_irqsave(&mtp_card->spinlock, flags);
4658c2ecf20Sopenharmony_ci	if (up) {
4668c2ecf20Sopenharmony_ci		if (! (portp->mode & MTPAV_MODE_OUTPUT_TRIGGERED)) {
4678c2ecf20Sopenharmony_ci			if (mtp_card->istimer++ == 0)
4688c2ecf20Sopenharmony_ci				snd_mtpav_add_output_timer(mtp_card);
4698c2ecf20Sopenharmony_ci			portp->mode |= MTPAV_MODE_OUTPUT_TRIGGERED;
4708c2ecf20Sopenharmony_ci		}
4718c2ecf20Sopenharmony_ci	} else {
4728c2ecf20Sopenharmony_ci		portp->mode &= ~MTPAV_MODE_OUTPUT_TRIGGERED;
4738c2ecf20Sopenharmony_ci		if (--mtp_card->istimer == 0)
4748c2ecf20Sopenharmony_ci			snd_mtpav_remove_output_timer(mtp_card);
4758c2ecf20Sopenharmony_ci	}
4768c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&mtp_card->spinlock, flags);
4778c2ecf20Sopenharmony_ci
4788c2ecf20Sopenharmony_ci	if (up)
4798c2ecf20Sopenharmony_ci		snd_mtpav_output_write(substream);
4808c2ecf20Sopenharmony_ci}
4818c2ecf20Sopenharmony_ci
4828c2ecf20Sopenharmony_ci/*
4838c2ecf20Sopenharmony_ci * midi interrupt for inputs
4848c2ecf20Sopenharmony_ci */
4858c2ecf20Sopenharmony_ci
4868c2ecf20Sopenharmony_cistatic void snd_mtpav_inmidi_process(struct mtpav *mcrd, u8 inbyte)
4878c2ecf20Sopenharmony_ci{
4888c2ecf20Sopenharmony_ci	struct mtpav_port *portp;
4898c2ecf20Sopenharmony_ci
4908c2ecf20Sopenharmony_ci	if ((int)mcrd->inmidiport > mcrd->num_ports * 2 + MTPAV_PIDX_BROADCAST)
4918c2ecf20Sopenharmony_ci		return;
4928c2ecf20Sopenharmony_ci
4938c2ecf20Sopenharmony_ci	portp = &mcrd->ports[mcrd->inmidiport];
4948c2ecf20Sopenharmony_ci	if (portp->mode & MTPAV_MODE_INPUT_TRIGGERED)
4958c2ecf20Sopenharmony_ci		snd_rawmidi_receive(portp->input, &inbyte, 1);
4968c2ecf20Sopenharmony_ci}
4978c2ecf20Sopenharmony_ci
4988c2ecf20Sopenharmony_cistatic void snd_mtpav_inmidi_h(struct mtpav *mcrd, u8 inbyte)
4998c2ecf20Sopenharmony_ci{
5008c2ecf20Sopenharmony_ci	if (inbyte >= 0xf8) {
5018c2ecf20Sopenharmony_ci		/* real-time midi code */
5028c2ecf20Sopenharmony_ci		snd_mtpav_inmidi_process(mcrd, inbyte);
5038c2ecf20Sopenharmony_ci		return;
5048c2ecf20Sopenharmony_ci	}
5058c2ecf20Sopenharmony_ci
5068c2ecf20Sopenharmony_ci	if (mcrd->inmidistate == 0) {	// awaiting command
5078c2ecf20Sopenharmony_ci		if (inbyte == 0xf5)	// MTP port #
5088c2ecf20Sopenharmony_ci			mcrd->inmidistate = 1;
5098c2ecf20Sopenharmony_ci		else
5108c2ecf20Sopenharmony_ci			snd_mtpav_inmidi_process(mcrd, inbyte);
5118c2ecf20Sopenharmony_ci	} else if (mcrd->inmidistate) {
5128c2ecf20Sopenharmony_ci		mcrd->inmidiport = translate_hwport_to_subdevice(mcrd, inbyte);
5138c2ecf20Sopenharmony_ci		mcrd->inmidistate = 0;
5148c2ecf20Sopenharmony_ci	}
5158c2ecf20Sopenharmony_ci}
5168c2ecf20Sopenharmony_ci
5178c2ecf20Sopenharmony_cistatic void snd_mtpav_read_bytes(struct mtpav *mcrd)
5188c2ecf20Sopenharmony_ci{
5198c2ecf20Sopenharmony_ci	u8 clrread, setread;
5208c2ecf20Sopenharmony_ci	u8 mtp_read_byte;
5218c2ecf20Sopenharmony_ci	u8 sr, cbyt;
5228c2ecf20Sopenharmony_ci	int i;
5238c2ecf20Sopenharmony_ci
5248c2ecf20Sopenharmony_ci	u8 sbyt = snd_mtpav_getreg(mcrd, SREG);
5258c2ecf20Sopenharmony_ci
5268c2ecf20Sopenharmony_ci	/* printk(KERN_DEBUG "snd_mtpav_read_bytes() sbyt: 0x%x\n", sbyt); */
5278c2ecf20Sopenharmony_ci
5288c2ecf20Sopenharmony_ci	if (!(sbyt & SIGS_BYTE))
5298c2ecf20Sopenharmony_ci		return;
5308c2ecf20Sopenharmony_ci
5318c2ecf20Sopenharmony_ci	cbyt = snd_mtpav_getreg(mcrd, CREG);
5328c2ecf20Sopenharmony_ci	clrread = cbyt & (SIGC_READ ^ 0xff);
5338c2ecf20Sopenharmony_ci	setread = cbyt | SIGC_READ;
5348c2ecf20Sopenharmony_ci
5358c2ecf20Sopenharmony_ci	do {
5368c2ecf20Sopenharmony_ci
5378c2ecf20Sopenharmony_ci		mtp_read_byte = 0;
5388c2ecf20Sopenharmony_ci		for (i = 0; i < 4; i++) {
5398c2ecf20Sopenharmony_ci			snd_mtpav_mputreg(mcrd, CREG, setread);
5408c2ecf20Sopenharmony_ci			sr = snd_mtpav_getreg(mcrd, SREG);
5418c2ecf20Sopenharmony_ci			snd_mtpav_mputreg(mcrd, CREG, clrread);
5428c2ecf20Sopenharmony_ci
5438c2ecf20Sopenharmony_ci			sr &= SIGS_IN0 | SIGS_IN1;
5448c2ecf20Sopenharmony_ci			sr >>= 4;
5458c2ecf20Sopenharmony_ci			mtp_read_byte |= sr << (i * 2);
5468c2ecf20Sopenharmony_ci		}
5478c2ecf20Sopenharmony_ci
5488c2ecf20Sopenharmony_ci		snd_mtpav_inmidi_h(mcrd, mtp_read_byte);
5498c2ecf20Sopenharmony_ci
5508c2ecf20Sopenharmony_ci		sbyt = snd_mtpav_getreg(mcrd, SREG);
5518c2ecf20Sopenharmony_ci
5528c2ecf20Sopenharmony_ci	} while (sbyt & SIGS_BYTE);
5538c2ecf20Sopenharmony_ci}
5548c2ecf20Sopenharmony_ci
5558c2ecf20Sopenharmony_cistatic irqreturn_t snd_mtpav_irqh(int irq, void *dev_id)
5568c2ecf20Sopenharmony_ci{
5578c2ecf20Sopenharmony_ci	struct mtpav *mcard = dev_id;
5588c2ecf20Sopenharmony_ci
5598c2ecf20Sopenharmony_ci	spin_lock(&mcard->spinlock);
5608c2ecf20Sopenharmony_ci	snd_mtpav_read_bytes(mcard);
5618c2ecf20Sopenharmony_ci	spin_unlock(&mcard->spinlock);
5628c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
5638c2ecf20Sopenharmony_ci}
5648c2ecf20Sopenharmony_ci
5658c2ecf20Sopenharmony_ci/*
5668c2ecf20Sopenharmony_ci * get ISA resources
5678c2ecf20Sopenharmony_ci */
5688c2ecf20Sopenharmony_cistatic int snd_mtpav_get_ISA(struct mtpav *mcard)
5698c2ecf20Sopenharmony_ci{
5708c2ecf20Sopenharmony_ci	if ((mcard->res_port = request_region(port, 3, "MotuMTPAV MIDI")) == NULL) {
5718c2ecf20Sopenharmony_ci		snd_printk(KERN_ERR "MTVAP port 0x%lx is busy\n", port);
5728c2ecf20Sopenharmony_ci		return -EBUSY;
5738c2ecf20Sopenharmony_ci	}
5748c2ecf20Sopenharmony_ci	mcard->port = port;
5758c2ecf20Sopenharmony_ci	if (request_irq(irq, snd_mtpav_irqh, 0, "MOTU MTPAV", mcard)) {
5768c2ecf20Sopenharmony_ci		snd_printk(KERN_ERR "MTVAP IRQ %d busy\n", irq);
5778c2ecf20Sopenharmony_ci		return -EBUSY;
5788c2ecf20Sopenharmony_ci	}
5798c2ecf20Sopenharmony_ci	mcard->irq = irq;
5808c2ecf20Sopenharmony_ci	return 0;
5818c2ecf20Sopenharmony_ci}
5828c2ecf20Sopenharmony_ci
5838c2ecf20Sopenharmony_ci
5848c2ecf20Sopenharmony_ci/*
5858c2ecf20Sopenharmony_ci */
5868c2ecf20Sopenharmony_ci
5878c2ecf20Sopenharmony_cistatic const struct snd_rawmidi_ops snd_mtpav_output = {
5888c2ecf20Sopenharmony_ci	.open =		snd_mtpav_output_open,
5898c2ecf20Sopenharmony_ci	.close =	snd_mtpav_output_close,
5908c2ecf20Sopenharmony_ci	.trigger =	snd_mtpav_output_trigger,
5918c2ecf20Sopenharmony_ci};
5928c2ecf20Sopenharmony_ci
5938c2ecf20Sopenharmony_cistatic const struct snd_rawmidi_ops snd_mtpav_input = {
5948c2ecf20Sopenharmony_ci	.open =		snd_mtpav_input_open,
5958c2ecf20Sopenharmony_ci	.close =	snd_mtpav_input_close,
5968c2ecf20Sopenharmony_ci	.trigger =	snd_mtpav_input_trigger,
5978c2ecf20Sopenharmony_ci};
5988c2ecf20Sopenharmony_ci
5998c2ecf20Sopenharmony_ci
6008c2ecf20Sopenharmony_ci/*
6018c2ecf20Sopenharmony_ci * get RAWMIDI resources
6028c2ecf20Sopenharmony_ci */
6038c2ecf20Sopenharmony_ci
6048c2ecf20Sopenharmony_cistatic void snd_mtpav_set_name(struct mtpav *chip,
6058c2ecf20Sopenharmony_ci			       struct snd_rawmidi_substream *substream)
6068c2ecf20Sopenharmony_ci{
6078c2ecf20Sopenharmony_ci	if (substream->number >= 0 && substream->number < chip->num_ports)
6088c2ecf20Sopenharmony_ci		sprintf(substream->name, "MTP direct %d", (substream->number % chip->num_ports) + 1);
6098c2ecf20Sopenharmony_ci	else if (substream->number >= 8 && substream->number < chip->num_ports * 2)
6108c2ecf20Sopenharmony_ci		sprintf(substream->name, "MTP remote %d", (substream->number % chip->num_ports) + 1);
6118c2ecf20Sopenharmony_ci	else if (substream->number == chip->num_ports * 2)
6128c2ecf20Sopenharmony_ci		strcpy(substream->name, "MTP computer");
6138c2ecf20Sopenharmony_ci	else if (substream->number == chip->num_ports * 2 + 1)
6148c2ecf20Sopenharmony_ci		strcpy(substream->name, "MTP ADAT");
6158c2ecf20Sopenharmony_ci	else
6168c2ecf20Sopenharmony_ci		strcpy(substream->name, "MTP broadcast");
6178c2ecf20Sopenharmony_ci}
6188c2ecf20Sopenharmony_ci
6198c2ecf20Sopenharmony_cistatic int snd_mtpav_get_RAWMIDI(struct mtpav *mcard)
6208c2ecf20Sopenharmony_ci{
6218c2ecf20Sopenharmony_ci	int rval;
6228c2ecf20Sopenharmony_ci	struct snd_rawmidi *rawmidi;
6238c2ecf20Sopenharmony_ci	struct snd_rawmidi_substream *substream;
6248c2ecf20Sopenharmony_ci	struct list_head *list;
6258c2ecf20Sopenharmony_ci
6268c2ecf20Sopenharmony_ci	if (hwports < 1)
6278c2ecf20Sopenharmony_ci		hwports = 1;
6288c2ecf20Sopenharmony_ci	else if (hwports > 8)
6298c2ecf20Sopenharmony_ci		hwports = 8;
6308c2ecf20Sopenharmony_ci	mcard->num_ports = hwports;
6318c2ecf20Sopenharmony_ci
6328c2ecf20Sopenharmony_ci	if ((rval = snd_rawmidi_new(mcard->card, "MotuMIDI", 0,
6338c2ecf20Sopenharmony_ci				    mcard->num_ports * 2 + MTPAV_PIDX_BROADCAST + 1,
6348c2ecf20Sopenharmony_ci				    mcard->num_ports * 2 + MTPAV_PIDX_BROADCAST + 1,
6358c2ecf20Sopenharmony_ci				    &mcard->rmidi)) < 0)
6368c2ecf20Sopenharmony_ci		return rval;
6378c2ecf20Sopenharmony_ci	rawmidi = mcard->rmidi;
6388c2ecf20Sopenharmony_ci	rawmidi->private_data = mcard;
6398c2ecf20Sopenharmony_ci
6408c2ecf20Sopenharmony_ci	list_for_each(list, &rawmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT].substreams) {
6418c2ecf20Sopenharmony_ci		substream = list_entry(list, struct snd_rawmidi_substream, list);
6428c2ecf20Sopenharmony_ci		snd_mtpav_set_name(mcard, substream);
6438c2ecf20Sopenharmony_ci		substream->ops = &snd_mtpav_input;
6448c2ecf20Sopenharmony_ci	}
6458c2ecf20Sopenharmony_ci	list_for_each(list, &rawmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT].substreams) {
6468c2ecf20Sopenharmony_ci		substream = list_entry(list, struct snd_rawmidi_substream, list);
6478c2ecf20Sopenharmony_ci		snd_mtpav_set_name(mcard, substream);
6488c2ecf20Sopenharmony_ci		substream->ops = &snd_mtpav_output;
6498c2ecf20Sopenharmony_ci		mcard->ports[substream->number].hwport = translate_subdevice_to_hwport(mcard, substream->number);
6508c2ecf20Sopenharmony_ci	}
6518c2ecf20Sopenharmony_ci	rawmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT | SNDRV_RAWMIDI_INFO_INPUT |
6528c2ecf20Sopenharmony_ci			       SNDRV_RAWMIDI_INFO_DUPLEX;
6538c2ecf20Sopenharmony_ci	sprintf(rawmidi->name, "MTP AV MIDI");
6548c2ecf20Sopenharmony_ci	return 0;
6558c2ecf20Sopenharmony_ci}
6568c2ecf20Sopenharmony_ci
6578c2ecf20Sopenharmony_ci/*
6588c2ecf20Sopenharmony_ci */
6598c2ecf20Sopenharmony_ci
6608c2ecf20Sopenharmony_cistatic void snd_mtpav_free(struct snd_card *card)
6618c2ecf20Sopenharmony_ci{
6628c2ecf20Sopenharmony_ci	struct mtpav *crd = card->private_data;
6638c2ecf20Sopenharmony_ci	unsigned long flags;
6648c2ecf20Sopenharmony_ci
6658c2ecf20Sopenharmony_ci	spin_lock_irqsave(&crd->spinlock, flags);
6668c2ecf20Sopenharmony_ci	if (crd->istimer > 0)
6678c2ecf20Sopenharmony_ci		snd_mtpav_remove_output_timer(crd);
6688c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&crd->spinlock, flags);
6698c2ecf20Sopenharmony_ci	if (crd->irq >= 0)
6708c2ecf20Sopenharmony_ci		free_irq(crd->irq, (void *)crd);
6718c2ecf20Sopenharmony_ci	release_and_free_resource(crd->res_port);
6728c2ecf20Sopenharmony_ci}
6738c2ecf20Sopenharmony_ci
6748c2ecf20Sopenharmony_ci/*
6758c2ecf20Sopenharmony_ci */
6768c2ecf20Sopenharmony_cistatic int snd_mtpav_probe(struct platform_device *dev)
6778c2ecf20Sopenharmony_ci{
6788c2ecf20Sopenharmony_ci	struct snd_card *card;
6798c2ecf20Sopenharmony_ci	int err;
6808c2ecf20Sopenharmony_ci	struct mtpav *mtp_card;
6818c2ecf20Sopenharmony_ci
6828c2ecf20Sopenharmony_ci	err = snd_card_new(&dev->dev, index, id, THIS_MODULE,
6838c2ecf20Sopenharmony_ci			   sizeof(*mtp_card), &card);
6848c2ecf20Sopenharmony_ci	if (err < 0)
6858c2ecf20Sopenharmony_ci		return err;
6868c2ecf20Sopenharmony_ci
6878c2ecf20Sopenharmony_ci	mtp_card = card->private_data;
6888c2ecf20Sopenharmony_ci	spin_lock_init(&mtp_card->spinlock);
6898c2ecf20Sopenharmony_ci	mtp_card->card = card;
6908c2ecf20Sopenharmony_ci	mtp_card->irq = -1;
6918c2ecf20Sopenharmony_ci	mtp_card->share_irq = 0;
6928c2ecf20Sopenharmony_ci	mtp_card->inmidistate = 0;
6938c2ecf20Sopenharmony_ci	mtp_card->outmidihwport = 0xffffffff;
6948c2ecf20Sopenharmony_ci	timer_setup(&mtp_card->timer, snd_mtpav_output_timer, 0);
6958c2ecf20Sopenharmony_ci
6968c2ecf20Sopenharmony_ci	card->private_free = snd_mtpav_free;
6978c2ecf20Sopenharmony_ci
6988c2ecf20Sopenharmony_ci	err = snd_mtpav_get_RAWMIDI(mtp_card);
6998c2ecf20Sopenharmony_ci	if (err < 0)
7008c2ecf20Sopenharmony_ci		goto __error;
7018c2ecf20Sopenharmony_ci
7028c2ecf20Sopenharmony_ci	mtp_card->inmidiport = mtp_card->num_ports + MTPAV_PIDX_BROADCAST;
7038c2ecf20Sopenharmony_ci
7048c2ecf20Sopenharmony_ci	err = snd_mtpav_get_ISA(mtp_card);
7058c2ecf20Sopenharmony_ci	if (err < 0)
7068c2ecf20Sopenharmony_ci		goto __error;
7078c2ecf20Sopenharmony_ci
7088c2ecf20Sopenharmony_ci	strcpy(card->driver, "MTPAV");
7098c2ecf20Sopenharmony_ci	strcpy(card->shortname, "MTPAV on parallel port");
7108c2ecf20Sopenharmony_ci	snprintf(card->longname, sizeof(card->longname),
7118c2ecf20Sopenharmony_ci		 "MTPAV on parallel port at 0x%lx", port);
7128c2ecf20Sopenharmony_ci
7138c2ecf20Sopenharmony_ci	snd_mtpav_portscan(mtp_card);
7148c2ecf20Sopenharmony_ci
7158c2ecf20Sopenharmony_ci	err = snd_card_register(mtp_card->card);
7168c2ecf20Sopenharmony_ci	if (err < 0)
7178c2ecf20Sopenharmony_ci		goto __error;
7188c2ecf20Sopenharmony_ci
7198c2ecf20Sopenharmony_ci	platform_set_drvdata(dev, card);
7208c2ecf20Sopenharmony_ci	printk(KERN_INFO "Motu MidiTimePiece on parallel port irq: %d ioport: 0x%lx\n", irq, port);
7218c2ecf20Sopenharmony_ci	return 0;
7228c2ecf20Sopenharmony_ci
7238c2ecf20Sopenharmony_ci __error:
7248c2ecf20Sopenharmony_ci	snd_card_free(card);
7258c2ecf20Sopenharmony_ci	return err;
7268c2ecf20Sopenharmony_ci}
7278c2ecf20Sopenharmony_ci
7288c2ecf20Sopenharmony_cistatic int snd_mtpav_remove(struct platform_device *devptr)
7298c2ecf20Sopenharmony_ci{
7308c2ecf20Sopenharmony_ci	snd_card_free(platform_get_drvdata(devptr));
7318c2ecf20Sopenharmony_ci	return 0;
7328c2ecf20Sopenharmony_ci}
7338c2ecf20Sopenharmony_ci
7348c2ecf20Sopenharmony_ci#define SND_MTPAV_DRIVER	"snd_mtpav"
7358c2ecf20Sopenharmony_ci
7368c2ecf20Sopenharmony_cistatic struct platform_driver snd_mtpav_driver = {
7378c2ecf20Sopenharmony_ci	.probe		= snd_mtpav_probe,
7388c2ecf20Sopenharmony_ci	.remove		= snd_mtpav_remove,
7398c2ecf20Sopenharmony_ci	.driver		= {
7408c2ecf20Sopenharmony_ci		.name	= SND_MTPAV_DRIVER,
7418c2ecf20Sopenharmony_ci	},
7428c2ecf20Sopenharmony_ci};
7438c2ecf20Sopenharmony_ci
7448c2ecf20Sopenharmony_cistatic int __init alsa_card_mtpav_init(void)
7458c2ecf20Sopenharmony_ci{
7468c2ecf20Sopenharmony_ci	int err;
7478c2ecf20Sopenharmony_ci
7488c2ecf20Sopenharmony_ci	if ((err = platform_driver_register(&snd_mtpav_driver)) < 0)
7498c2ecf20Sopenharmony_ci		return err;
7508c2ecf20Sopenharmony_ci
7518c2ecf20Sopenharmony_ci	device = platform_device_register_simple(SND_MTPAV_DRIVER, -1, NULL, 0);
7528c2ecf20Sopenharmony_ci	if (!IS_ERR(device)) {
7538c2ecf20Sopenharmony_ci		if (platform_get_drvdata(device))
7548c2ecf20Sopenharmony_ci			return 0;
7558c2ecf20Sopenharmony_ci		platform_device_unregister(device);
7568c2ecf20Sopenharmony_ci		err = -ENODEV;
7578c2ecf20Sopenharmony_ci	} else
7588c2ecf20Sopenharmony_ci		err = PTR_ERR(device);
7598c2ecf20Sopenharmony_ci	platform_driver_unregister(&snd_mtpav_driver);
7608c2ecf20Sopenharmony_ci	return err;
7618c2ecf20Sopenharmony_ci}
7628c2ecf20Sopenharmony_ci
7638c2ecf20Sopenharmony_cistatic void __exit alsa_card_mtpav_exit(void)
7648c2ecf20Sopenharmony_ci{
7658c2ecf20Sopenharmony_ci	platform_device_unregister(device);
7668c2ecf20Sopenharmony_ci	platform_driver_unregister(&snd_mtpav_driver);
7678c2ecf20Sopenharmony_ci}
7688c2ecf20Sopenharmony_ci
7698c2ecf20Sopenharmony_cimodule_init(alsa_card_mtpav_init)
7708c2ecf20Sopenharmony_cimodule_exit(alsa_card_mtpav_exit)
771