brintos

brintos / linux-shallow public Read only

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