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