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