162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * MOTU Midi Timepiece ALSA Main routines 462306a36Sopenharmony_ci * Copyright by Michael T. Mayers (c) Jan 09, 2000 562306a36Sopenharmony_ci * mail: michael@tweakoz.com 662306a36Sopenharmony_ci * Thanks to John Galbraith 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * This driver is for the 'Mark Of The Unicorn' (MOTU) 962306a36Sopenharmony_ci * MidiTimePiece AV multiport MIDI interface 1062306a36Sopenharmony_ci * 1162306a36Sopenharmony_ci * IOPORTS 1262306a36Sopenharmony_ci * ------- 1362306a36Sopenharmony_ci * 8 MIDI Ins and 8 MIDI outs 1462306a36Sopenharmony_ci * Video Sync In (BNC), Word Sync Out (BNC), 1562306a36Sopenharmony_ci * ADAT Sync Out (DB9) 1662306a36Sopenharmony_ci * SMPTE in/out (1/4") 1762306a36Sopenharmony_ci * 2 programmable pedal/footswitch inputs and 4 programmable MIDI controller knobs. 1862306a36Sopenharmony_ci * Macintosh RS422 serial port 1962306a36Sopenharmony_ci * RS422 "network" port for ganging multiple MTP's 2062306a36Sopenharmony_ci * PC Parallel Port ( which this driver currently uses ) 2162306a36Sopenharmony_ci * 2262306a36Sopenharmony_ci * MISC FEATURES 2362306a36Sopenharmony_ci * ------------- 2462306a36Sopenharmony_ci * Hardware MIDI routing, merging, and filtering 2562306a36Sopenharmony_ci * MIDI Synchronization to Video, ADAT, SMPTE and other Clock sources 2662306a36Sopenharmony_ci * 128 'scene' memories, recallable from MIDI program change 2762306a36Sopenharmony_ci * 2862306a36Sopenharmony_ci * ChangeLog 2962306a36Sopenharmony_ci * Jun 11 2001 Takashi Iwai <tiwai@suse.de> 3062306a36Sopenharmony_ci * - Recoded & debugged 3162306a36Sopenharmony_ci * - Added timer interrupt for midi outputs 3262306a36Sopenharmony_ci * - hwports is between 1 and 8, which specifies the number of hardware ports. 3362306a36Sopenharmony_ci * The three global ports, computer, adat and broadcast ports, are created 3462306a36Sopenharmony_ci * always after h/w and remote ports. 3562306a36Sopenharmony_ci */ 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci#include <linux/init.h> 3862306a36Sopenharmony_ci#include <linux/interrupt.h> 3962306a36Sopenharmony_ci#include <linux/module.h> 4062306a36Sopenharmony_ci#include <linux/err.h> 4162306a36Sopenharmony_ci#include <linux/platform_device.h> 4262306a36Sopenharmony_ci#include <linux/ioport.h> 4362306a36Sopenharmony_ci#include <linux/io.h> 4462306a36Sopenharmony_ci#include <linux/moduleparam.h> 4562306a36Sopenharmony_ci#include <sound/core.h> 4662306a36Sopenharmony_ci#include <sound/initval.h> 4762306a36Sopenharmony_ci#include <sound/rawmidi.h> 4862306a36Sopenharmony_ci#include <linux/delay.h> 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci/* 5162306a36Sopenharmony_ci * globals 5262306a36Sopenharmony_ci */ 5362306a36Sopenharmony_ciMODULE_AUTHOR("Michael T. Mayers"); 5462306a36Sopenharmony_ciMODULE_DESCRIPTION("MOTU MidiTimePiece AV multiport MIDI"); 5562306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci// io resources 5862306a36Sopenharmony_ci#define MTPAV_IOBASE 0x378 5962306a36Sopenharmony_ci#define MTPAV_IRQ 7 6062306a36Sopenharmony_ci#define MTPAV_MAX_PORTS 8 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_cistatic int index = SNDRV_DEFAULT_IDX1; 6362306a36Sopenharmony_cistatic char *id = SNDRV_DEFAULT_STR1; 6462306a36Sopenharmony_cistatic long port = MTPAV_IOBASE; /* 0x378, 0x278 */ 6562306a36Sopenharmony_cistatic int irq = MTPAV_IRQ; /* 7, 5 */ 6662306a36Sopenharmony_cistatic int hwports = MTPAV_MAX_PORTS; /* use hardware ports 1-8 */ 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_cimodule_param(index, int, 0444); 6962306a36Sopenharmony_ciMODULE_PARM_DESC(index, "Index value for MotuMTPAV MIDI."); 7062306a36Sopenharmony_cimodule_param(id, charp, 0444); 7162306a36Sopenharmony_ciMODULE_PARM_DESC(id, "ID string for MotuMTPAV MIDI."); 7262306a36Sopenharmony_cimodule_param_hw(port, long, ioport, 0444); 7362306a36Sopenharmony_ciMODULE_PARM_DESC(port, "Parallel port # for MotuMTPAV MIDI."); 7462306a36Sopenharmony_cimodule_param_hw(irq, int, irq, 0444); 7562306a36Sopenharmony_ciMODULE_PARM_DESC(irq, "Parallel IRQ # for MotuMTPAV MIDI."); 7662306a36Sopenharmony_cimodule_param(hwports, int, 0444); 7762306a36Sopenharmony_ciMODULE_PARM_DESC(hwports, "Hardware ports # for MotuMTPAV MIDI."); 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_cistatic struct platform_device *device; 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci/* 8262306a36Sopenharmony_ci * defines 8362306a36Sopenharmony_ci */ 8462306a36Sopenharmony_ci//#define USE_FAKE_MTP // don't actually read/write to MTP device (for debugging without an actual unit) (does not work yet) 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci// parallel port usage masks 8762306a36Sopenharmony_ci#define SIGS_BYTE 0x08 8862306a36Sopenharmony_ci#define SIGS_RFD 0x80 8962306a36Sopenharmony_ci#define SIGS_IRQ 0x40 9062306a36Sopenharmony_ci#define SIGS_IN0 0x10 9162306a36Sopenharmony_ci#define SIGS_IN1 0x20 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci#define SIGC_WRITE 0x04 9462306a36Sopenharmony_ci#define SIGC_READ 0x08 9562306a36Sopenharmony_ci#define SIGC_INTEN 0x10 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci#define DREG 0 9862306a36Sopenharmony_ci#define SREG 1 9962306a36Sopenharmony_ci#define CREG 2 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci// 10262306a36Sopenharmony_ci#define MTPAV_MODE_INPUT_OPENED 0x01 10362306a36Sopenharmony_ci#define MTPAV_MODE_OUTPUT_OPENED 0x02 10462306a36Sopenharmony_ci#define MTPAV_MODE_INPUT_TRIGGERED 0x04 10562306a36Sopenharmony_ci#define MTPAV_MODE_OUTPUT_TRIGGERED 0x08 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci#define NUMPORTS (0x12+1) 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci/* 11162306a36Sopenharmony_ci */ 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_cistruct mtpav_port { 11462306a36Sopenharmony_ci u8 number; 11562306a36Sopenharmony_ci u8 hwport; 11662306a36Sopenharmony_ci u8 mode; 11762306a36Sopenharmony_ci u8 running_status; 11862306a36Sopenharmony_ci struct snd_rawmidi_substream *input; 11962306a36Sopenharmony_ci struct snd_rawmidi_substream *output; 12062306a36Sopenharmony_ci}; 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_cistruct mtpav { 12362306a36Sopenharmony_ci struct snd_card *card; 12462306a36Sopenharmony_ci unsigned long port; 12562306a36Sopenharmony_ci struct resource *res_port; 12662306a36Sopenharmony_ci int irq; /* interrupt (for inputs) */ 12762306a36Sopenharmony_ci spinlock_t spinlock; 12862306a36Sopenharmony_ci int share_irq; /* number of accesses to input interrupts */ 12962306a36Sopenharmony_ci int istimer; /* number of accesses to timer interrupts */ 13062306a36Sopenharmony_ci struct timer_list timer; /* timer interrupts for outputs */ 13162306a36Sopenharmony_ci struct snd_rawmidi *rmidi; 13262306a36Sopenharmony_ci int num_ports; /* number of hw ports (1-8) */ 13362306a36Sopenharmony_ci struct mtpav_port ports[NUMPORTS]; /* all ports including computer, adat and bc */ 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci u32 inmidiport; /* selected input midi port */ 13662306a36Sopenharmony_ci u32 inmidistate; /* during midi command 0xf5 */ 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci u32 outmidihwport; /* selected output midi hw port */ 13962306a36Sopenharmony_ci}; 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci/* 14362306a36Sopenharmony_ci * possible hardware ports (selected by 0xf5 port message) 14462306a36Sopenharmony_ci * 0x00 all ports 14562306a36Sopenharmony_ci * 0x01 .. 0x08 this MTP's ports 1..8 14662306a36Sopenharmony_ci * 0x09 .. 0x10 networked MTP's ports (9..16) 14762306a36Sopenharmony_ci * 0x11 networked MTP's computer port 14862306a36Sopenharmony_ci * 0x63 to ADAT 14962306a36Sopenharmony_ci * 15062306a36Sopenharmony_ci * mappig: 15162306a36Sopenharmony_ci * subdevice 0 - (X-1) ports 15262306a36Sopenharmony_ci * X - (2*X-1) networked ports 15362306a36Sopenharmony_ci * X computer 15462306a36Sopenharmony_ci * X+1 ADAT 15562306a36Sopenharmony_ci * X+2 all ports 15662306a36Sopenharmony_ci * 15762306a36Sopenharmony_ci * where X = chip->num_ports 15862306a36Sopenharmony_ci */ 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci#define MTPAV_PIDX_COMPUTER 0 16162306a36Sopenharmony_ci#define MTPAV_PIDX_ADAT 1 16262306a36Sopenharmony_ci#define MTPAV_PIDX_BROADCAST 2 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_cistatic int translate_subdevice_to_hwport(struct mtpav *chip, int subdev) 16662306a36Sopenharmony_ci{ 16762306a36Sopenharmony_ci if (subdev < 0) 16862306a36Sopenharmony_ci return 0x01; /* invalid - use port 0 as default */ 16962306a36Sopenharmony_ci else if (subdev < chip->num_ports) 17062306a36Sopenharmony_ci return subdev + 1; /* single mtp port */ 17162306a36Sopenharmony_ci else if (subdev < chip->num_ports * 2) 17262306a36Sopenharmony_ci return subdev - chip->num_ports + 0x09; /* remote port */ 17362306a36Sopenharmony_ci else if (subdev == chip->num_ports * 2 + MTPAV_PIDX_COMPUTER) 17462306a36Sopenharmony_ci return 0x11; /* computer port */ 17562306a36Sopenharmony_ci else if (subdev == chip->num_ports + MTPAV_PIDX_ADAT) 17662306a36Sopenharmony_ci return 0x63; /* ADAT */ 17762306a36Sopenharmony_ci return 0; /* all ports */ 17862306a36Sopenharmony_ci} 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_cistatic int translate_hwport_to_subdevice(struct mtpav *chip, int hwport) 18162306a36Sopenharmony_ci{ 18262306a36Sopenharmony_ci int p; 18362306a36Sopenharmony_ci if (hwport <= 0x00) /* all ports */ 18462306a36Sopenharmony_ci return chip->num_ports + MTPAV_PIDX_BROADCAST; 18562306a36Sopenharmony_ci else if (hwport <= 0x08) { /* single port */ 18662306a36Sopenharmony_ci p = hwport - 1; 18762306a36Sopenharmony_ci if (p >= chip->num_ports) 18862306a36Sopenharmony_ci p = 0; 18962306a36Sopenharmony_ci return p; 19062306a36Sopenharmony_ci } else if (hwport <= 0x10) { /* remote port */ 19162306a36Sopenharmony_ci p = hwport - 0x09 + chip->num_ports; 19262306a36Sopenharmony_ci if (p >= chip->num_ports * 2) 19362306a36Sopenharmony_ci p = chip->num_ports; 19462306a36Sopenharmony_ci return p; 19562306a36Sopenharmony_ci } else if (hwport == 0x11) /* computer port */ 19662306a36Sopenharmony_ci return chip->num_ports + MTPAV_PIDX_COMPUTER; 19762306a36Sopenharmony_ci else /* ADAT */ 19862306a36Sopenharmony_ci return chip->num_ports + MTPAV_PIDX_ADAT; 19962306a36Sopenharmony_ci} 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci/* 20362306a36Sopenharmony_ci */ 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_cistatic u8 snd_mtpav_getreg(struct mtpav *chip, u16 reg) 20662306a36Sopenharmony_ci{ 20762306a36Sopenharmony_ci u8 rval = 0; 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci if (reg == SREG) { 21062306a36Sopenharmony_ci rval = inb(chip->port + SREG); 21162306a36Sopenharmony_ci rval = (rval & 0xf8); 21262306a36Sopenharmony_ci } else if (reg == CREG) { 21362306a36Sopenharmony_ci rval = inb(chip->port + CREG); 21462306a36Sopenharmony_ci rval = (rval & 0x1c); 21562306a36Sopenharmony_ci } 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci return rval; 21862306a36Sopenharmony_ci} 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci/* 22162306a36Sopenharmony_ci */ 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_cistatic inline void snd_mtpav_mputreg(struct mtpav *chip, u16 reg, u8 val) 22462306a36Sopenharmony_ci{ 22562306a36Sopenharmony_ci if (reg == DREG || reg == CREG) 22662306a36Sopenharmony_ci outb(val, chip->port + reg); 22762306a36Sopenharmony_ci} 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci/* 23062306a36Sopenharmony_ci */ 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_cistatic void snd_mtpav_wait_rfdhi(struct mtpav *chip) 23362306a36Sopenharmony_ci{ 23462306a36Sopenharmony_ci int counts = 10000; 23562306a36Sopenharmony_ci u8 sbyte; 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci sbyte = snd_mtpav_getreg(chip, SREG); 23862306a36Sopenharmony_ci while (!(sbyte & SIGS_RFD) && counts--) { 23962306a36Sopenharmony_ci sbyte = snd_mtpav_getreg(chip, SREG); 24062306a36Sopenharmony_ci udelay(10); 24162306a36Sopenharmony_ci } 24262306a36Sopenharmony_ci} 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_cistatic void snd_mtpav_send_byte(struct mtpav *chip, u8 byte) 24562306a36Sopenharmony_ci{ 24662306a36Sopenharmony_ci u8 tcbyt; 24762306a36Sopenharmony_ci u8 clrwrite; 24862306a36Sopenharmony_ci u8 setwrite; 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci snd_mtpav_wait_rfdhi(chip); 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci ///////////////// 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci tcbyt = snd_mtpav_getreg(chip, CREG); 25562306a36Sopenharmony_ci clrwrite = tcbyt & (SIGC_WRITE ^ 0xff); 25662306a36Sopenharmony_ci setwrite = tcbyt | SIGC_WRITE; 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci snd_mtpav_mputreg(chip, DREG, byte); 25962306a36Sopenharmony_ci snd_mtpav_mputreg(chip, CREG, clrwrite); // clear write bit 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci snd_mtpav_mputreg(chip, CREG, setwrite); // set write bit 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci} 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci/* 26762306a36Sopenharmony_ci */ 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci/* call this with spin lock held */ 27062306a36Sopenharmony_cistatic void snd_mtpav_output_port_write(struct mtpav *mtp_card, 27162306a36Sopenharmony_ci struct mtpav_port *portp, 27262306a36Sopenharmony_ci struct snd_rawmidi_substream *substream) 27362306a36Sopenharmony_ci{ 27462306a36Sopenharmony_ci u8 outbyte; 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci // Get the outbyte first, so we can emulate running status if 27762306a36Sopenharmony_ci // necessary 27862306a36Sopenharmony_ci if (snd_rawmidi_transmit(substream, &outbyte, 1) != 1) 27962306a36Sopenharmony_ci return; 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci // send port change command if necessary 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci if (portp->hwport != mtp_card->outmidihwport) { 28462306a36Sopenharmony_ci mtp_card->outmidihwport = portp->hwport; 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci snd_mtpav_send_byte(mtp_card, 0xf5); 28762306a36Sopenharmony_ci snd_mtpav_send_byte(mtp_card, portp->hwport); 28862306a36Sopenharmony_ci /* 28962306a36Sopenharmony_ci snd_printk(KERN_DEBUG "new outport: 0x%x\n", 29062306a36Sopenharmony_ci (unsigned int) portp->hwport); 29162306a36Sopenharmony_ci */ 29262306a36Sopenharmony_ci if (!(outbyte & 0x80) && portp->running_status) 29362306a36Sopenharmony_ci snd_mtpav_send_byte(mtp_card, portp->running_status); 29462306a36Sopenharmony_ci } 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci // send data 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci do { 29962306a36Sopenharmony_ci if (outbyte & 0x80) 30062306a36Sopenharmony_ci portp->running_status = outbyte; 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci snd_mtpav_send_byte(mtp_card, outbyte); 30362306a36Sopenharmony_ci } while (snd_rawmidi_transmit(substream, &outbyte, 1) == 1); 30462306a36Sopenharmony_ci} 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_cistatic void snd_mtpav_output_write(struct snd_rawmidi_substream *substream) 30762306a36Sopenharmony_ci{ 30862306a36Sopenharmony_ci struct mtpav *mtp_card = substream->rmidi->private_data; 30962306a36Sopenharmony_ci struct mtpav_port *portp = &mtp_card->ports[substream->number]; 31062306a36Sopenharmony_ci unsigned long flags; 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci spin_lock_irqsave(&mtp_card->spinlock, flags); 31362306a36Sopenharmony_ci snd_mtpav_output_port_write(mtp_card, portp, substream); 31462306a36Sopenharmony_ci spin_unlock_irqrestore(&mtp_card->spinlock, flags); 31562306a36Sopenharmony_ci} 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci/* 31962306a36Sopenharmony_ci * mtpav control 32062306a36Sopenharmony_ci */ 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_cistatic void snd_mtpav_portscan(struct mtpav *chip) // put mtp into smart routing mode 32362306a36Sopenharmony_ci{ 32462306a36Sopenharmony_ci u8 p; 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci for (p = 0; p < 8; p++) { 32762306a36Sopenharmony_ci snd_mtpav_send_byte(chip, 0xf5); 32862306a36Sopenharmony_ci snd_mtpav_send_byte(chip, p); 32962306a36Sopenharmony_ci snd_mtpav_send_byte(chip, 0xfe); 33062306a36Sopenharmony_ci } 33162306a36Sopenharmony_ci} 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci/* 33462306a36Sopenharmony_ci */ 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_cistatic int snd_mtpav_input_open(struct snd_rawmidi_substream *substream) 33762306a36Sopenharmony_ci{ 33862306a36Sopenharmony_ci struct mtpav *mtp_card = substream->rmidi->private_data; 33962306a36Sopenharmony_ci struct mtpav_port *portp = &mtp_card->ports[substream->number]; 34062306a36Sopenharmony_ci unsigned long flags; 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci spin_lock_irqsave(&mtp_card->spinlock, flags); 34362306a36Sopenharmony_ci portp->mode |= MTPAV_MODE_INPUT_OPENED; 34462306a36Sopenharmony_ci portp->input = substream; 34562306a36Sopenharmony_ci if (mtp_card->share_irq++ == 0) 34662306a36Sopenharmony_ci snd_mtpav_mputreg(mtp_card, CREG, (SIGC_INTEN | SIGC_WRITE)); // enable pport interrupts 34762306a36Sopenharmony_ci spin_unlock_irqrestore(&mtp_card->spinlock, flags); 34862306a36Sopenharmony_ci return 0; 34962306a36Sopenharmony_ci} 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci/* 35262306a36Sopenharmony_ci */ 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_cistatic int snd_mtpav_input_close(struct snd_rawmidi_substream *substream) 35562306a36Sopenharmony_ci{ 35662306a36Sopenharmony_ci struct mtpav *mtp_card = substream->rmidi->private_data; 35762306a36Sopenharmony_ci struct mtpav_port *portp = &mtp_card->ports[substream->number]; 35862306a36Sopenharmony_ci unsigned long flags; 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci spin_lock_irqsave(&mtp_card->spinlock, flags); 36162306a36Sopenharmony_ci portp->mode &= ~MTPAV_MODE_INPUT_OPENED; 36262306a36Sopenharmony_ci portp->input = NULL; 36362306a36Sopenharmony_ci if (--mtp_card->share_irq == 0) 36462306a36Sopenharmony_ci snd_mtpav_mputreg(mtp_card, CREG, 0); // disable pport interrupts 36562306a36Sopenharmony_ci spin_unlock_irqrestore(&mtp_card->spinlock, flags); 36662306a36Sopenharmony_ci return 0; 36762306a36Sopenharmony_ci} 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci/* 37062306a36Sopenharmony_ci */ 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_cistatic void snd_mtpav_input_trigger(struct snd_rawmidi_substream *substream, int up) 37362306a36Sopenharmony_ci{ 37462306a36Sopenharmony_ci struct mtpav *mtp_card = substream->rmidi->private_data; 37562306a36Sopenharmony_ci struct mtpav_port *portp = &mtp_card->ports[substream->number]; 37662306a36Sopenharmony_ci unsigned long flags; 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci spin_lock_irqsave(&mtp_card->spinlock, flags); 37962306a36Sopenharmony_ci if (up) 38062306a36Sopenharmony_ci portp->mode |= MTPAV_MODE_INPUT_TRIGGERED; 38162306a36Sopenharmony_ci else 38262306a36Sopenharmony_ci portp->mode &= ~MTPAV_MODE_INPUT_TRIGGERED; 38362306a36Sopenharmony_ci spin_unlock_irqrestore(&mtp_card->spinlock, flags); 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci} 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci/* 38962306a36Sopenharmony_ci * timer interrupt for outputs 39062306a36Sopenharmony_ci */ 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_cistatic void snd_mtpav_output_timer(struct timer_list *t) 39362306a36Sopenharmony_ci{ 39462306a36Sopenharmony_ci unsigned long flags; 39562306a36Sopenharmony_ci struct mtpav *chip = from_timer(chip, t, timer); 39662306a36Sopenharmony_ci int p; 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci spin_lock_irqsave(&chip->spinlock, flags); 39962306a36Sopenharmony_ci /* reprogram timer */ 40062306a36Sopenharmony_ci mod_timer(&chip->timer, 1 + jiffies); 40162306a36Sopenharmony_ci /* process each port */ 40262306a36Sopenharmony_ci for (p = 0; p <= chip->num_ports * 2 + MTPAV_PIDX_BROADCAST; p++) { 40362306a36Sopenharmony_ci struct mtpav_port *portp = &chip->ports[p]; 40462306a36Sopenharmony_ci if ((portp->mode & MTPAV_MODE_OUTPUT_TRIGGERED) && portp->output) 40562306a36Sopenharmony_ci snd_mtpav_output_port_write(chip, portp, portp->output); 40662306a36Sopenharmony_ci } 40762306a36Sopenharmony_ci spin_unlock_irqrestore(&chip->spinlock, flags); 40862306a36Sopenharmony_ci} 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci/* spinlock held! */ 41162306a36Sopenharmony_cistatic void snd_mtpav_add_output_timer(struct mtpav *chip) 41262306a36Sopenharmony_ci{ 41362306a36Sopenharmony_ci mod_timer(&chip->timer, 1 + jiffies); 41462306a36Sopenharmony_ci} 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci/* spinlock held! */ 41762306a36Sopenharmony_cistatic void snd_mtpav_remove_output_timer(struct mtpav *chip) 41862306a36Sopenharmony_ci{ 41962306a36Sopenharmony_ci del_timer(&chip->timer); 42062306a36Sopenharmony_ci} 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci/* 42362306a36Sopenharmony_ci */ 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_cistatic int snd_mtpav_output_open(struct snd_rawmidi_substream *substream) 42662306a36Sopenharmony_ci{ 42762306a36Sopenharmony_ci struct mtpav *mtp_card = substream->rmidi->private_data; 42862306a36Sopenharmony_ci struct mtpav_port *portp = &mtp_card->ports[substream->number]; 42962306a36Sopenharmony_ci unsigned long flags; 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci spin_lock_irqsave(&mtp_card->spinlock, flags); 43262306a36Sopenharmony_ci portp->mode |= MTPAV_MODE_OUTPUT_OPENED; 43362306a36Sopenharmony_ci portp->output = substream; 43462306a36Sopenharmony_ci spin_unlock_irqrestore(&mtp_card->spinlock, flags); 43562306a36Sopenharmony_ci return 0; 43662306a36Sopenharmony_ci}; 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci/* 43962306a36Sopenharmony_ci */ 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_cistatic int snd_mtpav_output_close(struct snd_rawmidi_substream *substream) 44262306a36Sopenharmony_ci{ 44362306a36Sopenharmony_ci struct mtpav *mtp_card = substream->rmidi->private_data; 44462306a36Sopenharmony_ci struct mtpav_port *portp = &mtp_card->ports[substream->number]; 44562306a36Sopenharmony_ci unsigned long flags; 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci spin_lock_irqsave(&mtp_card->spinlock, flags); 44862306a36Sopenharmony_ci portp->mode &= ~MTPAV_MODE_OUTPUT_OPENED; 44962306a36Sopenharmony_ci portp->output = NULL; 45062306a36Sopenharmony_ci spin_unlock_irqrestore(&mtp_card->spinlock, flags); 45162306a36Sopenharmony_ci return 0; 45262306a36Sopenharmony_ci}; 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci/* 45562306a36Sopenharmony_ci */ 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_cistatic void snd_mtpav_output_trigger(struct snd_rawmidi_substream *substream, int up) 45862306a36Sopenharmony_ci{ 45962306a36Sopenharmony_ci struct mtpav *mtp_card = substream->rmidi->private_data; 46062306a36Sopenharmony_ci struct mtpav_port *portp = &mtp_card->ports[substream->number]; 46162306a36Sopenharmony_ci unsigned long flags; 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci spin_lock_irqsave(&mtp_card->spinlock, flags); 46462306a36Sopenharmony_ci if (up) { 46562306a36Sopenharmony_ci if (! (portp->mode & MTPAV_MODE_OUTPUT_TRIGGERED)) { 46662306a36Sopenharmony_ci if (mtp_card->istimer++ == 0) 46762306a36Sopenharmony_ci snd_mtpav_add_output_timer(mtp_card); 46862306a36Sopenharmony_ci portp->mode |= MTPAV_MODE_OUTPUT_TRIGGERED; 46962306a36Sopenharmony_ci } 47062306a36Sopenharmony_ci } else { 47162306a36Sopenharmony_ci portp->mode &= ~MTPAV_MODE_OUTPUT_TRIGGERED; 47262306a36Sopenharmony_ci if (--mtp_card->istimer == 0) 47362306a36Sopenharmony_ci snd_mtpav_remove_output_timer(mtp_card); 47462306a36Sopenharmony_ci } 47562306a36Sopenharmony_ci spin_unlock_irqrestore(&mtp_card->spinlock, flags); 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ci if (up) 47862306a36Sopenharmony_ci snd_mtpav_output_write(substream); 47962306a36Sopenharmony_ci} 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci/* 48262306a36Sopenharmony_ci * midi interrupt for inputs 48362306a36Sopenharmony_ci */ 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_cistatic void snd_mtpav_inmidi_process(struct mtpav *mcrd, u8 inbyte) 48662306a36Sopenharmony_ci{ 48762306a36Sopenharmony_ci struct mtpav_port *portp; 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci if ((int)mcrd->inmidiport > mcrd->num_ports * 2 + MTPAV_PIDX_BROADCAST) 49062306a36Sopenharmony_ci return; 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci portp = &mcrd->ports[mcrd->inmidiport]; 49362306a36Sopenharmony_ci if (portp->mode & MTPAV_MODE_INPUT_TRIGGERED) 49462306a36Sopenharmony_ci snd_rawmidi_receive(portp->input, &inbyte, 1); 49562306a36Sopenharmony_ci} 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_cistatic void snd_mtpav_inmidi_h(struct mtpav *mcrd, u8 inbyte) 49862306a36Sopenharmony_ci{ 49962306a36Sopenharmony_ci if (inbyte >= 0xf8) { 50062306a36Sopenharmony_ci /* real-time midi code */ 50162306a36Sopenharmony_ci snd_mtpav_inmidi_process(mcrd, inbyte); 50262306a36Sopenharmony_ci return; 50362306a36Sopenharmony_ci } 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci if (mcrd->inmidistate == 0) { // awaiting command 50662306a36Sopenharmony_ci if (inbyte == 0xf5) // MTP port # 50762306a36Sopenharmony_ci mcrd->inmidistate = 1; 50862306a36Sopenharmony_ci else 50962306a36Sopenharmony_ci snd_mtpav_inmidi_process(mcrd, inbyte); 51062306a36Sopenharmony_ci } else if (mcrd->inmidistate) { 51162306a36Sopenharmony_ci mcrd->inmidiport = translate_hwport_to_subdevice(mcrd, inbyte); 51262306a36Sopenharmony_ci mcrd->inmidistate = 0; 51362306a36Sopenharmony_ci } 51462306a36Sopenharmony_ci} 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_cistatic void snd_mtpav_read_bytes(struct mtpav *mcrd) 51762306a36Sopenharmony_ci{ 51862306a36Sopenharmony_ci u8 clrread, setread; 51962306a36Sopenharmony_ci u8 mtp_read_byte; 52062306a36Sopenharmony_ci u8 sr, cbyt; 52162306a36Sopenharmony_ci int i; 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ci u8 sbyt = snd_mtpav_getreg(mcrd, SREG); 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_ci /* printk(KERN_DEBUG "snd_mtpav_read_bytes() sbyt: 0x%x\n", sbyt); */ 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci if (!(sbyt & SIGS_BYTE)) 52862306a36Sopenharmony_ci return; 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ci cbyt = snd_mtpav_getreg(mcrd, CREG); 53162306a36Sopenharmony_ci clrread = cbyt & (SIGC_READ ^ 0xff); 53262306a36Sopenharmony_ci setread = cbyt | SIGC_READ; 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci do { 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci mtp_read_byte = 0; 53762306a36Sopenharmony_ci for (i = 0; i < 4; i++) { 53862306a36Sopenharmony_ci snd_mtpav_mputreg(mcrd, CREG, setread); 53962306a36Sopenharmony_ci sr = snd_mtpav_getreg(mcrd, SREG); 54062306a36Sopenharmony_ci snd_mtpav_mputreg(mcrd, CREG, clrread); 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_ci sr &= SIGS_IN0 | SIGS_IN1; 54362306a36Sopenharmony_ci sr >>= 4; 54462306a36Sopenharmony_ci mtp_read_byte |= sr << (i * 2); 54562306a36Sopenharmony_ci } 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ci snd_mtpav_inmidi_h(mcrd, mtp_read_byte); 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_ci sbyt = snd_mtpav_getreg(mcrd, SREG); 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_ci } while (sbyt & SIGS_BYTE); 55262306a36Sopenharmony_ci} 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_cistatic irqreturn_t snd_mtpav_irqh(int irq, void *dev_id) 55562306a36Sopenharmony_ci{ 55662306a36Sopenharmony_ci struct mtpav *mcard = dev_id; 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_ci spin_lock(&mcard->spinlock); 55962306a36Sopenharmony_ci snd_mtpav_read_bytes(mcard); 56062306a36Sopenharmony_ci spin_unlock(&mcard->spinlock); 56162306a36Sopenharmony_ci return IRQ_HANDLED; 56262306a36Sopenharmony_ci} 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_ci/* 56562306a36Sopenharmony_ci * get ISA resources 56662306a36Sopenharmony_ci */ 56762306a36Sopenharmony_cistatic int snd_mtpav_get_ISA(struct mtpav *mcard) 56862306a36Sopenharmony_ci{ 56962306a36Sopenharmony_ci mcard->res_port = devm_request_region(mcard->card->dev, port, 3, 57062306a36Sopenharmony_ci "MotuMTPAV MIDI"); 57162306a36Sopenharmony_ci if (!mcard->res_port) { 57262306a36Sopenharmony_ci snd_printk(KERN_ERR "MTVAP port 0x%lx is busy\n", port); 57362306a36Sopenharmony_ci return -EBUSY; 57462306a36Sopenharmony_ci } 57562306a36Sopenharmony_ci mcard->port = port; 57662306a36Sopenharmony_ci if (devm_request_irq(mcard->card->dev, irq, snd_mtpav_irqh, 0, 57762306a36Sopenharmony_ci "MOTU MTPAV", mcard)) { 57862306a36Sopenharmony_ci snd_printk(KERN_ERR "MTVAP IRQ %d busy\n", irq); 57962306a36Sopenharmony_ci return -EBUSY; 58062306a36Sopenharmony_ci } 58162306a36Sopenharmony_ci mcard->irq = irq; 58262306a36Sopenharmony_ci return 0; 58362306a36Sopenharmony_ci} 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_ci/* 58762306a36Sopenharmony_ci */ 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_cistatic const struct snd_rawmidi_ops snd_mtpav_output = { 59062306a36Sopenharmony_ci .open = snd_mtpav_output_open, 59162306a36Sopenharmony_ci .close = snd_mtpav_output_close, 59262306a36Sopenharmony_ci .trigger = snd_mtpav_output_trigger, 59362306a36Sopenharmony_ci}; 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_cistatic const struct snd_rawmidi_ops snd_mtpav_input = { 59662306a36Sopenharmony_ci .open = snd_mtpav_input_open, 59762306a36Sopenharmony_ci .close = snd_mtpav_input_close, 59862306a36Sopenharmony_ci .trigger = snd_mtpav_input_trigger, 59962306a36Sopenharmony_ci}; 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_ci/* 60362306a36Sopenharmony_ci * get RAWMIDI resources 60462306a36Sopenharmony_ci */ 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_cistatic void snd_mtpav_set_name(struct mtpav *chip, 60762306a36Sopenharmony_ci struct snd_rawmidi_substream *substream) 60862306a36Sopenharmony_ci{ 60962306a36Sopenharmony_ci if (substream->number >= 0 && substream->number < chip->num_ports) 61062306a36Sopenharmony_ci sprintf(substream->name, "MTP direct %d", (substream->number % chip->num_ports) + 1); 61162306a36Sopenharmony_ci else if (substream->number >= 8 && substream->number < chip->num_ports * 2) 61262306a36Sopenharmony_ci sprintf(substream->name, "MTP remote %d", (substream->number % chip->num_ports) + 1); 61362306a36Sopenharmony_ci else if (substream->number == chip->num_ports * 2) 61462306a36Sopenharmony_ci strcpy(substream->name, "MTP computer"); 61562306a36Sopenharmony_ci else if (substream->number == chip->num_ports * 2 + 1) 61662306a36Sopenharmony_ci strcpy(substream->name, "MTP ADAT"); 61762306a36Sopenharmony_ci else 61862306a36Sopenharmony_ci strcpy(substream->name, "MTP broadcast"); 61962306a36Sopenharmony_ci} 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_cistatic int snd_mtpav_get_RAWMIDI(struct mtpav *mcard) 62262306a36Sopenharmony_ci{ 62362306a36Sopenharmony_ci int rval; 62462306a36Sopenharmony_ci struct snd_rawmidi *rawmidi; 62562306a36Sopenharmony_ci struct snd_rawmidi_substream *substream; 62662306a36Sopenharmony_ci struct list_head *list; 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_ci if (hwports < 1) 62962306a36Sopenharmony_ci hwports = 1; 63062306a36Sopenharmony_ci else if (hwports > 8) 63162306a36Sopenharmony_ci hwports = 8; 63262306a36Sopenharmony_ci mcard->num_ports = hwports; 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_ci rval = snd_rawmidi_new(mcard->card, "MotuMIDI", 0, 63562306a36Sopenharmony_ci mcard->num_ports * 2 + MTPAV_PIDX_BROADCAST + 1, 63662306a36Sopenharmony_ci mcard->num_ports * 2 + MTPAV_PIDX_BROADCAST + 1, 63762306a36Sopenharmony_ci &mcard->rmidi); 63862306a36Sopenharmony_ci if (rval < 0) 63962306a36Sopenharmony_ci return rval; 64062306a36Sopenharmony_ci rawmidi = mcard->rmidi; 64162306a36Sopenharmony_ci rawmidi->private_data = mcard; 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_ci list_for_each(list, &rawmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT].substreams) { 64462306a36Sopenharmony_ci substream = list_entry(list, struct snd_rawmidi_substream, list); 64562306a36Sopenharmony_ci snd_mtpav_set_name(mcard, substream); 64662306a36Sopenharmony_ci substream->ops = &snd_mtpav_input; 64762306a36Sopenharmony_ci } 64862306a36Sopenharmony_ci list_for_each(list, &rawmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT].substreams) { 64962306a36Sopenharmony_ci substream = list_entry(list, struct snd_rawmidi_substream, list); 65062306a36Sopenharmony_ci snd_mtpav_set_name(mcard, substream); 65162306a36Sopenharmony_ci substream->ops = &snd_mtpav_output; 65262306a36Sopenharmony_ci mcard->ports[substream->number].hwport = translate_subdevice_to_hwport(mcard, substream->number); 65362306a36Sopenharmony_ci } 65462306a36Sopenharmony_ci rawmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT | SNDRV_RAWMIDI_INFO_INPUT | 65562306a36Sopenharmony_ci SNDRV_RAWMIDI_INFO_DUPLEX; 65662306a36Sopenharmony_ci sprintf(rawmidi->name, "MTP AV MIDI"); 65762306a36Sopenharmony_ci return 0; 65862306a36Sopenharmony_ci} 65962306a36Sopenharmony_ci 66062306a36Sopenharmony_ci/* 66162306a36Sopenharmony_ci */ 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_cistatic void snd_mtpav_free(struct snd_card *card) 66462306a36Sopenharmony_ci{ 66562306a36Sopenharmony_ci struct mtpav *crd = card->private_data; 66662306a36Sopenharmony_ci unsigned long flags; 66762306a36Sopenharmony_ci 66862306a36Sopenharmony_ci spin_lock_irqsave(&crd->spinlock, flags); 66962306a36Sopenharmony_ci if (crd->istimer > 0) 67062306a36Sopenharmony_ci snd_mtpav_remove_output_timer(crd); 67162306a36Sopenharmony_ci spin_unlock_irqrestore(&crd->spinlock, flags); 67262306a36Sopenharmony_ci} 67362306a36Sopenharmony_ci 67462306a36Sopenharmony_ci/* 67562306a36Sopenharmony_ci */ 67662306a36Sopenharmony_cistatic int snd_mtpav_probe(struct platform_device *dev) 67762306a36Sopenharmony_ci{ 67862306a36Sopenharmony_ci struct snd_card *card; 67962306a36Sopenharmony_ci int err; 68062306a36Sopenharmony_ci struct mtpav *mtp_card; 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_ci err = snd_devm_card_new(&dev->dev, index, id, THIS_MODULE, 68362306a36Sopenharmony_ci sizeof(*mtp_card), &card); 68462306a36Sopenharmony_ci if (err < 0) 68562306a36Sopenharmony_ci return err; 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_ci mtp_card = card->private_data; 68862306a36Sopenharmony_ci spin_lock_init(&mtp_card->spinlock); 68962306a36Sopenharmony_ci mtp_card->card = card; 69062306a36Sopenharmony_ci mtp_card->irq = -1; 69162306a36Sopenharmony_ci mtp_card->share_irq = 0; 69262306a36Sopenharmony_ci mtp_card->inmidistate = 0; 69362306a36Sopenharmony_ci mtp_card->outmidihwport = 0xffffffff; 69462306a36Sopenharmony_ci timer_setup(&mtp_card->timer, snd_mtpav_output_timer, 0); 69562306a36Sopenharmony_ci 69662306a36Sopenharmony_ci err = snd_mtpav_get_RAWMIDI(mtp_card); 69762306a36Sopenharmony_ci if (err < 0) 69862306a36Sopenharmony_ci return err; 69962306a36Sopenharmony_ci 70062306a36Sopenharmony_ci mtp_card->inmidiport = mtp_card->num_ports + MTPAV_PIDX_BROADCAST; 70162306a36Sopenharmony_ci 70262306a36Sopenharmony_ci err = snd_mtpav_get_ISA(mtp_card); 70362306a36Sopenharmony_ci if (err < 0) 70462306a36Sopenharmony_ci return err; 70562306a36Sopenharmony_ci 70662306a36Sopenharmony_ci strcpy(card->driver, "MTPAV"); 70762306a36Sopenharmony_ci strcpy(card->shortname, "MTPAV on parallel port"); 70862306a36Sopenharmony_ci snprintf(card->longname, sizeof(card->longname), 70962306a36Sopenharmony_ci "MTPAV on parallel port at 0x%lx", port); 71062306a36Sopenharmony_ci 71162306a36Sopenharmony_ci snd_mtpav_portscan(mtp_card); 71262306a36Sopenharmony_ci 71362306a36Sopenharmony_ci err = snd_card_register(mtp_card->card); 71462306a36Sopenharmony_ci if (err < 0) 71562306a36Sopenharmony_ci return err; 71662306a36Sopenharmony_ci 71762306a36Sopenharmony_ci card->private_free = snd_mtpav_free; 71862306a36Sopenharmony_ci 71962306a36Sopenharmony_ci platform_set_drvdata(dev, card); 72062306a36Sopenharmony_ci printk(KERN_INFO "Motu MidiTimePiece on parallel port irq: %d ioport: 0x%lx\n", irq, port); 72162306a36Sopenharmony_ci return 0; 72262306a36Sopenharmony_ci} 72362306a36Sopenharmony_ci 72462306a36Sopenharmony_ci#define SND_MTPAV_DRIVER "snd_mtpav" 72562306a36Sopenharmony_ci 72662306a36Sopenharmony_cistatic struct platform_driver snd_mtpav_driver = { 72762306a36Sopenharmony_ci .probe = snd_mtpav_probe, 72862306a36Sopenharmony_ci .driver = { 72962306a36Sopenharmony_ci .name = SND_MTPAV_DRIVER, 73062306a36Sopenharmony_ci }, 73162306a36Sopenharmony_ci}; 73262306a36Sopenharmony_ci 73362306a36Sopenharmony_cistatic int __init alsa_card_mtpav_init(void) 73462306a36Sopenharmony_ci{ 73562306a36Sopenharmony_ci int err; 73662306a36Sopenharmony_ci 73762306a36Sopenharmony_ci err = platform_driver_register(&snd_mtpav_driver); 73862306a36Sopenharmony_ci if (err < 0) 73962306a36Sopenharmony_ci return err; 74062306a36Sopenharmony_ci 74162306a36Sopenharmony_ci device = platform_device_register_simple(SND_MTPAV_DRIVER, -1, NULL, 0); 74262306a36Sopenharmony_ci if (!IS_ERR(device)) { 74362306a36Sopenharmony_ci if (platform_get_drvdata(device)) 74462306a36Sopenharmony_ci return 0; 74562306a36Sopenharmony_ci platform_device_unregister(device); 74662306a36Sopenharmony_ci err = -ENODEV; 74762306a36Sopenharmony_ci } else 74862306a36Sopenharmony_ci err = PTR_ERR(device); 74962306a36Sopenharmony_ci platform_driver_unregister(&snd_mtpav_driver); 75062306a36Sopenharmony_ci return err; 75162306a36Sopenharmony_ci} 75262306a36Sopenharmony_ci 75362306a36Sopenharmony_cistatic void __exit alsa_card_mtpav_exit(void) 75462306a36Sopenharmony_ci{ 75562306a36Sopenharmony_ci platform_device_unregister(device); 75662306a36Sopenharmony_ci platform_driver_unregister(&snd_mtpav_driver); 75762306a36Sopenharmony_ci} 75862306a36Sopenharmony_ci 75962306a36Sopenharmony_cimodule_init(alsa_card_mtpav_init) 76062306a36Sopenharmony_cimodule_exit(alsa_card_mtpav_exit) 761