| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1 | /* | 
| Jaroslav Kysela | c1017a4 | 2007-10-15 09:50:19 +0200 | [diff] [blame] | 2 |  *  Copyright (c) by Jaroslav Kysela <perex@perex.cz> | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 3 |  *  Routines for control of MPU-401 in UART mode | 
 | 4 |  * | 
 | 5 |  *  MPU-401 supports UART mode which is not capable generate transmit | 
 | 6 |  *  interrupts thus output is done via polling. Also, if irq < 0, then | 
 | 7 |  *  input is done also via polling. Do not expect good performance. | 
 | 8 |  * | 
 | 9 |  * | 
 | 10 |  *   This program is free software; you can redistribute it and/or modify | 
 | 11 |  *   it under the terms of the GNU General Public License as published by | 
 | 12 |  *   the Free Software Foundation; either version 2 of the License, or | 
 | 13 |  *   (at your option) any later version. | 
 | 14 |  * | 
 | 15 |  *   This program is distributed in the hope that it will be useful, | 
 | 16 |  *   but WITHOUT ANY WARRANTY; without even the implied warranty of | 
 | 17 |  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
 | 18 |  *   GNU General Public License for more details. | 
 | 19 |  * | 
 | 20 |  *   You should have received a copy of the GNU General Public License | 
 | 21 |  *   along with this program; if not, write to the Free Software | 
 | 22 |  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA | 
 | 23 |  * | 
 | 24 |  *   13-03-2003: | 
 | 25 |  *      Added support for different kind of hardware I/O. Build in choices | 
 | 26 |  *      are port and mmio. For other kind of I/O, set mpu->read and | 
 | 27 |  *      mpu->write to your own I/O functions. | 
 | 28 |  * | 
 | 29 |  */ | 
 | 30 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 31 | #include <asm/io.h> | 
 | 32 | #include <linux/delay.h> | 
 | 33 | #include <linux/init.h> | 
 | 34 | #include <linux/slab.h> | 
 | 35 | #include <linux/ioport.h> | 
 | 36 | #include <linux/interrupt.h> | 
 | 37 | #include <linux/errno.h> | 
 | 38 | #include <sound/core.h> | 
 | 39 | #include <sound/mpu401.h> | 
 | 40 |  | 
| Jaroslav Kysela | c1017a4 | 2007-10-15 09:50:19 +0200 | [diff] [blame] | 41 | MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>"); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 42 | MODULE_DESCRIPTION("Routines for control of MPU-401 in UART mode"); | 
 | 43 | MODULE_LICENSE("GPL"); | 
 | 44 |  | 
| Takashi Iwai | e1fad17 | 2005-11-17 14:12:45 +0100 | [diff] [blame] | 45 | static void snd_mpu401_uart_input_read(struct snd_mpu401 * mpu); | 
 | 46 | static void snd_mpu401_uart_output_write(struct snd_mpu401 * mpu); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 47 |  | 
 | 48 | /* | 
 | 49 |  | 
 | 50 |  */ | 
 | 51 |  | 
| Takashi Iwai | b415ed4 | 2008-04-23 17:47:28 +0200 | [diff] [blame] | 52 | #define snd_mpu401_input_avail(mpu) \ | 
 | 53 | 	(!(mpu->read(mpu, MPU401C(mpu)) & MPU401_RX_EMPTY)) | 
 | 54 | #define snd_mpu401_output_ready(mpu) \ | 
 | 55 | 	(!(mpu->read(mpu, MPU401C(mpu)) & MPU401_TX_FULL)) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 56 |  | 
 | 57 | /* Build in lowlevel io */ | 
| Takashi Iwai | 2851d96 | 2006-05-18 14:48:26 +0200 | [diff] [blame] | 58 | static void mpu401_write_port(struct snd_mpu401 *mpu, unsigned char data, | 
 | 59 | 			      unsigned long addr) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 60 | { | 
 | 61 | 	outb(data, addr); | 
 | 62 | } | 
 | 63 |  | 
| Takashi Iwai | 2851d96 | 2006-05-18 14:48:26 +0200 | [diff] [blame] | 64 | static unsigned char mpu401_read_port(struct snd_mpu401 *mpu, | 
 | 65 | 				      unsigned long addr) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 66 | { | 
 | 67 | 	return inb(addr); | 
 | 68 | } | 
 | 69 |  | 
| Takashi Iwai | 2851d96 | 2006-05-18 14:48:26 +0200 | [diff] [blame] | 70 | static void mpu401_write_mmio(struct snd_mpu401 *mpu, unsigned char data, | 
 | 71 | 			      unsigned long addr) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 72 | { | 
 | 73 | 	writeb(data, (void __iomem *)addr); | 
 | 74 | } | 
 | 75 |  | 
| Takashi Iwai | 2851d96 | 2006-05-18 14:48:26 +0200 | [diff] [blame] | 76 | static unsigned char mpu401_read_mmio(struct snd_mpu401 *mpu, | 
 | 77 | 				      unsigned long addr) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 78 | { | 
 | 79 | 	return readb((void __iomem *)addr); | 
 | 80 | } | 
 | 81 | /*  */ | 
 | 82 |  | 
| Takashi Iwai | e1fad17 | 2005-11-17 14:12:45 +0100 | [diff] [blame] | 83 | static void snd_mpu401_uart_clear_rx(struct snd_mpu401 *mpu) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 84 | { | 
 | 85 | 	int timeout = 100000; | 
 | 86 | 	for (; timeout > 0 && snd_mpu401_input_avail(mpu); timeout--) | 
 | 87 | 		mpu->read(mpu, MPU401D(mpu)); | 
 | 88 | #ifdef CONFIG_SND_DEBUG | 
 | 89 | 	if (timeout <= 0) | 
| Takashi Iwai | 2851d96 | 2006-05-18 14:48:26 +0200 | [diff] [blame] | 90 | 		snd_printk(KERN_ERR "cmd: clear rx timeout (status = 0x%x)\n", | 
 | 91 | 			   mpu->read(mpu, MPU401C(mpu))); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 92 | #endif | 
 | 93 | } | 
 | 94 |  | 
| Takashi Iwai | 302e4c2 | 2006-05-23 13:24:30 +0200 | [diff] [blame] | 95 | static void uart_interrupt_tx(struct snd_mpu401 *mpu) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 96 | { | 
| Clemens Ladisch | 60fac85 | 2007-11-12 08:47:57 +0100 | [diff] [blame] | 97 | 	unsigned long flags; | 
 | 98 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 99 | 	if (test_bit(MPU401_MODE_BIT_OUTPUT, &mpu->mode) && | 
 | 100 | 	    test_bit(MPU401_MODE_BIT_OUTPUT_TRIGGER, &mpu->mode)) { | 
| Clemens Ladisch | 60fac85 | 2007-11-12 08:47:57 +0100 | [diff] [blame] | 101 | 		spin_lock_irqsave(&mpu->output_lock, flags); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 102 | 		snd_mpu401_uart_output_write(mpu); | 
| Clemens Ladisch | 60fac85 | 2007-11-12 08:47:57 +0100 | [diff] [blame] | 103 | 		spin_unlock_irqrestore(&mpu->output_lock, flags); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 104 | 	} | 
 | 105 | } | 
 | 106 |  | 
| Takashi Iwai | 302e4c2 | 2006-05-23 13:24:30 +0200 | [diff] [blame] | 107 | static void _snd_mpu401_uart_interrupt(struct snd_mpu401 *mpu) | 
 | 108 | { | 
| Clemens Ladisch | 60fac85 | 2007-11-12 08:47:57 +0100 | [diff] [blame] | 109 | 	unsigned long flags; | 
 | 110 |  | 
| Takashi Iwai | 302e4c2 | 2006-05-23 13:24:30 +0200 | [diff] [blame] | 111 | 	if (mpu->info_flags & MPU401_INFO_INPUT) { | 
| Clemens Ladisch | 60fac85 | 2007-11-12 08:47:57 +0100 | [diff] [blame] | 112 | 		spin_lock_irqsave(&mpu->input_lock, flags); | 
| Takashi Iwai | 302e4c2 | 2006-05-23 13:24:30 +0200 | [diff] [blame] | 113 | 		if (test_bit(MPU401_MODE_BIT_INPUT, &mpu->mode)) | 
 | 114 | 			snd_mpu401_uart_input_read(mpu); | 
 | 115 | 		else | 
 | 116 | 			snd_mpu401_uart_clear_rx(mpu); | 
| Clemens Ladisch | 60fac85 | 2007-11-12 08:47:57 +0100 | [diff] [blame] | 117 | 		spin_unlock_irqrestore(&mpu->input_lock, flags); | 
| Takashi Iwai | 302e4c2 | 2006-05-23 13:24:30 +0200 | [diff] [blame] | 118 | 	} | 
 | 119 | 	if (! (mpu->info_flags & MPU401_INFO_TX_IRQ)) | 
 | 120 | 		/* ok. for better Tx performance try do some output | 
 | 121 | 		   when input is done */ | 
 | 122 | 		uart_interrupt_tx(mpu); | 
 | 123 | } | 
 | 124 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 125 | /** | 
 | 126 |  * snd_mpu401_uart_interrupt - generic MPU401-UART interrupt handler | 
 | 127 |  * @irq: the irq number | 
 | 128 |  * @dev_id: mpu401 instance | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 129 |  * | 
 | 130 |  * Processes the interrupt for MPU401-UART i/o. | 
 | 131 |  */ | 
| David Howells | 7d12e78 | 2006-10-05 14:55:46 +0100 | [diff] [blame] | 132 | irqreturn_t snd_mpu401_uart_interrupt(int irq, void *dev_id) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 133 | { | 
| Takashi Iwai | e1fad17 | 2005-11-17 14:12:45 +0100 | [diff] [blame] | 134 | 	struct snd_mpu401 *mpu = dev_id; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 135 | 	 | 
 | 136 | 	if (mpu == NULL) | 
 | 137 | 		return IRQ_NONE; | 
 | 138 | 	_snd_mpu401_uart_interrupt(mpu); | 
 | 139 | 	return IRQ_HANDLED; | 
 | 140 | } | 
 | 141 |  | 
| Takashi Iwai | 2851d96 | 2006-05-18 14:48:26 +0200 | [diff] [blame] | 142 | EXPORT_SYMBOL(snd_mpu401_uart_interrupt); | 
 | 143 |  | 
| Takashi Iwai | 302e4c2 | 2006-05-23 13:24:30 +0200 | [diff] [blame] | 144 | /** | 
 | 145 |  * snd_mpu401_uart_interrupt_tx - generic MPU401-UART transmit irq handler | 
 | 146 |  * @irq: the irq number | 
 | 147 |  * @dev_id: mpu401 instance | 
| Takashi Iwai | 302e4c2 | 2006-05-23 13:24:30 +0200 | [diff] [blame] | 148 |  * | 
 | 149 |  * Processes the interrupt for MPU401-UART output. | 
 | 150 |  */ | 
| David Howells | 7d12e78 | 2006-10-05 14:55:46 +0100 | [diff] [blame] | 151 | irqreturn_t snd_mpu401_uart_interrupt_tx(int irq, void *dev_id) | 
| Takashi Iwai | 302e4c2 | 2006-05-23 13:24:30 +0200 | [diff] [blame] | 152 | { | 
 | 153 | 	struct snd_mpu401 *mpu = dev_id; | 
 | 154 | 	 | 
 | 155 | 	if (mpu == NULL) | 
 | 156 | 		return IRQ_NONE; | 
 | 157 | 	uart_interrupt_tx(mpu); | 
 | 158 | 	return IRQ_HANDLED; | 
 | 159 | } | 
 | 160 |  | 
 | 161 | EXPORT_SYMBOL(snd_mpu401_uart_interrupt_tx); | 
 | 162 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 163 | /* | 
 | 164 |  * timer callback | 
 | 165 |  * reprogram the timer and call the interrupt job | 
 | 166 |  */ | 
 | 167 | static void snd_mpu401_uart_timer(unsigned long data) | 
 | 168 | { | 
| Takashi Iwai | e1fad17 | 2005-11-17 14:12:45 +0100 | [diff] [blame] | 169 | 	struct snd_mpu401 *mpu = (struct snd_mpu401 *)data; | 
| Takashi Iwai | b32425a | 2005-11-18 18:52:14 +0100 | [diff] [blame] | 170 | 	unsigned long flags; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 171 |  | 
| Takashi Iwai | b32425a | 2005-11-18 18:52:14 +0100 | [diff] [blame] | 172 | 	spin_lock_irqsave(&mpu->timer_lock, flags); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 173 | 	/*mpu->mode |= MPU401_MODE_TIMER;*/ | 
 | 174 | 	mpu->timer.expires = 1 + jiffies; | 
 | 175 | 	add_timer(&mpu->timer); | 
| Takashi Iwai | b32425a | 2005-11-18 18:52:14 +0100 | [diff] [blame] | 176 | 	spin_unlock_irqrestore(&mpu->timer_lock, flags); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 177 | 	if (mpu->rmidi) | 
 | 178 | 		_snd_mpu401_uart_interrupt(mpu); | 
 | 179 | } | 
 | 180 |  | 
 | 181 | /* | 
 | 182 |  * initialize the timer callback if not programmed yet | 
 | 183 |  */ | 
| Takashi Iwai | e1fad17 | 2005-11-17 14:12:45 +0100 | [diff] [blame] | 184 | static void snd_mpu401_uart_add_timer (struct snd_mpu401 *mpu, int input) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 185 | { | 
 | 186 | 	unsigned long flags; | 
 | 187 |  | 
 | 188 | 	spin_lock_irqsave (&mpu->timer_lock, flags); | 
 | 189 | 	if (mpu->timer_invoked == 0) { | 
 | 190 | 		init_timer(&mpu->timer); | 
 | 191 | 		mpu->timer.data = (unsigned long)mpu; | 
 | 192 | 		mpu->timer.function = snd_mpu401_uart_timer; | 
 | 193 | 		mpu->timer.expires = 1 + jiffies; | 
 | 194 | 		add_timer(&mpu->timer); | 
 | 195 | 	}  | 
| Takashi Iwai | 2851d96 | 2006-05-18 14:48:26 +0200 | [diff] [blame] | 196 | 	mpu->timer_invoked |= input ? MPU401_MODE_INPUT_TIMER : | 
 | 197 | 		MPU401_MODE_OUTPUT_TIMER; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 198 | 	spin_unlock_irqrestore (&mpu->timer_lock, flags); | 
 | 199 | } | 
 | 200 |  | 
 | 201 | /* | 
 | 202 |  * remove the timer callback if still active | 
 | 203 |  */ | 
| Takashi Iwai | e1fad17 | 2005-11-17 14:12:45 +0100 | [diff] [blame] | 204 | static void snd_mpu401_uart_remove_timer (struct snd_mpu401 *mpu, int input) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 205 | { | 
 | 206 | 	unsigned long flags; | 
 | 207 |  | 
 | 208 | 	spin_lock_irqsave (&mpu->timer_lock, flags); | 
 | 209 | 	if (mpu->timer_invoked) { | 
| Takashi Iwai | 2851d96 | 2006-05-18 14:48:26 +0200 | [diff] [blame] | 210 | 		mpu->timer_invoked &= input ? ~MPU401_MODE_INPUT_TIMER : | 
 | 211 | 			~MPU401_MODE_OUTPUT_TIMER; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 212 | 		if (! mpu->timer_invoked) | 
 | 213 | 			del_timer(&mpu->timer); | 
 | 214 | 	} | 
 | 215 | 	spin_unlock_irqrestore (&mpu->timer_lock, flags); | 
 | 216 | } | 
 | 217 |  | 
 | 218 | /* | 
| Takashi Iwai | 2851d96 | 2006-05-18 14:48:26 +0200 | [diff] [blame] | 219 |  * send a UART command | 
 | 220 |  * return zero if successful, non-zero for some errors | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 221 |  */ | 
 | 222 |  | 
| Jon Masters | 962f831 | 2006-04-20 02:43:20 -0700 | [diff] [blame] | 223 | static int snd_mpu401_uart_cmd(struct snd_mpu401 * mpu, unsigned char cmd, | 
| Takashi Iwai | 2851d96 | 2006-05-18 14:48:26 +0200 | [diff] [blame] | 224 | 			       int ack) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 225 | { | 
 | 226 | 	unsigned long flags; | 
 | 227 | 	int timeout, ok; | 
 | 228 |  | 
 | 229 | 	spin_lock_irqsave(&mpu->input_lock, flags); | 
 | 230 | 	if (mpu->hardware != MPU401_HW_TRID4DWAVE) { | 
 | 231 | 		mpu->write(mpu, 0x00, MPU401D(mpu)); | 
 | 232 | 		/*snd_mpu401_uart_clear_rx(mpu);*/ | 
 | 233 | 	} | 
 | 234 | 	/* ok. standard MPU-401 initialization */ | 
 | 235 | 	if (mpu->hardware != MPU401_HW_SB) { | 
| Takashi Iwai | 2851d96 | 2006-05-18 14:48:26 +0200 | [diff] [blame] | 236 | 		for (timeout = 1000; timeout > 0 && | 
 | 237 | 			     !snd_mpu401_output_ready(mpu); timeout--) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 238 | 			udelay(10); | 
 | 239 | #ifdef CONFIG_SND_DEBUG | 
 | 240 | 		if (!timeout) | 
| Takashi Iwai | 2851d96 | 2006-05-18 14:48:26 +0200 | [diff] [blame] | 241 | 			snd_printk(KERN_ERR "cmd: tx timeout (status = 0x%x)\n", | 
 | 242 | 				   mpu->read(mpu, MPU401C(mpu))); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 243 | #endif | 
 | 244 | 	} | 
 | 245 | 	mpu->write(mpu, cmd, MPU401C(mpu)); | 
| Takashi Iwai | df7e3fd | 2008-04-25 09:13:45 +0200 | [diff] [blame] | 246 | 	if (ack && !(mpu->info_flags & MPU401_INFO_NO_ACK)) { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 247 | 		ok = 0; | 
 | 248 | 		timeout = 10000; | 
 | 249 | 		while (!ok && timeout-- > 0) { | 
 | 250 | 			if (snd_mpu401_input_avail(mpu)) { | 
 | 251 | 				if (mpu->read(mpu, MPU401D(mpu)) == MPU401_ACK) | 
 | 252 | 					ok = 1; | 
 | 253 | 			} | 
 | 254 | 		} | 
 | 255 | 		if (!ok && mpu->read(mpu, MPU401D(mpu)) == MPU401_ACK) | 
 | 256 | 			ok = 1; | 
| Takashi Iwai | 2851d96 | 2006-05-18 14:48:26 +0200 | [diff] [blame] | 257 | 	} else | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 258 | 		ok = 1; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 259 | 	spin_unlock_irqrestore(&mpu->input_lock, flags); | 
| Jon Masters | 962f831 | 2006-04-20 02:43:20 -0700 | [diff] [blame] | 260 | 	if (!ok) { | 
| Takashi Iwai | 2851d96 | 2006-05-18 14:48:26 +0200 | [diff] [blame] | 261 | 		snd_printk(KERN_ERR "cmd: 0x%x failed at 0x%lx " | 
 | 262 | 			   "(status = 0x%x, data = 0x%x)\n", cmd, mpu->port, | 
 | 263 | 			   mpu->read(mpu, MPU401C(mpu)), | 
 | 264 | 			   mpu->read(mpu, MPU401D(mpu))); | 
| Jon Masters | 962f831 | 2006-04-20 02:43:20 -0700 | [diff] [blame] | 265 | 		return 1; | 
 | 266 | 	} | 
 | 267 | 	return 0; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 268 | } | 
 | 269 |  | 
| Takashi Iwai | 8f7ba05 | 2007-02-22 16:07:21 +0100 | [diff] [blame] | 270 | static int snd_mpu401_do_reset(struct snd_mpu401 *mpu) | 
 | 271 | { | 
 | 272 | 	if (snd_mpu401_uart_cmd(mpu, MPU401_RESET, 1)) | 
 | 273 | 		return -EIO; | 
| Clemens Ladisch | c1099fc | 2007-10-11 14:42:23 +0200 | [diff] [blame] | 274 | 	if (snd_mpu401_uart_cmd(mpu, MPU401_ENTER_UART, 0)) | 
| Takashi Iwai | 8f7ba05 | 2007-02-22 16:07:21 +0100 | [diff] [blame] | 275 | 		return -EIO; | 
 | 276 | 	return 0; | 
 | 277 | } | 
 | 278 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 279 | /* | 
 | 280 |  * input/output open/close - protected by open_mutex in rawmidi.c | 
 | 281 |  */ | 
| Takashi Iwai | e1fad17 | 2005-11-17 14:12:45 +0100 | [diff] [blame] | 282 | static int snd_mpu401_uart_input_open(struct snd_rawmidi_substream *substream) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 283 | { | 
| Takashi Iwai | e1fad17 | 2005-11-17 14:12:45 +0100 | [diff] [blame] | 284 | 	struct snd_mpu401 *mpu; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 285 | 	int err; | 
 | 286 |  | 
 | 287 | 	mpu = substream->rmidi->private_data; | 
 | 288 | 	if (mpu->open_input && (err = mpu->open_input(mpu)) < 0) | 
 | 289 | 		return err; | 
 | 290 | 	if (! test_bit(MPU401_MODE_BIT_OUTPUT, &mpu->mode)) { | 
| Takashi Iwai | 8f7ba05 | 2007-02-22 16:07:21 +0100 | [diff] [blame] | 291 | 		if (snd_mpu401_do_reset(mpu) < 0) | 
| Jon Masters | 962f831 | 2006-04-20 02:43:20 -0700 | [diff] [blame] | 292 | 			goto error_out; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 293 | 	} | 
 | 294 | 	mpu->substream_input = substream; | 
 | 295 | 	set_bit(MPU401_MODE_BIT_INPUT, &mpu->mode); | 
 | 296 | 	return 0; | 
| Jon Masters | 962f831 | 2006-04-20 02:43:20 -0700 | [diff] [blame] | 297 |  | 
 | 298 | error_out: | 
 | 299 | 	if (mpu->open_input && mpu->close_input) | 
 | 300 | 		mpu->close_input(mpu); | 
 | 301 | 	return -EIO; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 302 | } | 
 | 303 |  | 
| Takashi Iwai | e1fad17 | 2005-11-17 14:12:45 +0100 | [diff] [blame] | 304 | static int snd_mpu401_uart_output_open(struct snd_rawmidi_substream *substream) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 305 | { | 
| Takashi Iwai | e1fad17 | 2005-11-17 14:12:45 +0100 | [diff] [blame] | 306 | 	struct snd_mpu401 *mpu; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 307 | 	int err; | 
 | 308 |  | 
 | 309 | 	mpu = substream->rmidi->private_data; | 
 | 310 | 	if (mpu->open_output && (err = mpu->open_output(mpu)) < 0) | 
 | 311 | 		return err; | 
 | 312 | 	if (! test_bit(MPU401_MODE_BIT_INPUT, &mpu->mode)) { | 
| Takashi Iwai | 8f7ba05 | 2007-02-22 16:07:21 +0100 | [diff] [blame] | 313 | 		if (snd_mpu401_do_reset(mpu) < 0) | 
| Jon Masters | 962f831 | 2006-04-20 02:43:20 -0700 | [diff] [blame] | 314 | 			goto error_out; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 315 | 	} | 
 | 316 | 	mpu->substream_output = substream; | 
 | 317 | 	set_bit(MPU401_MODE_BIT_OUTPUT, &mpu->mode); | 
 | 318 | 	return 0; | 
| Jon Masters | 962f831 | 2006-04-20 02:43:20 -0700 | [diff] [blame] | 319 |  | 
 | 320 | error_out: | 
 | 321 | 	if (mpu->open_output && mpu->close_output) | 
 | 322 | 		mpu->close_output(mpu); | 
 | 323 | 	return -EIO; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 324 | } | 
 | 325 |  | 
| Takashi Iwai | e1fad17 | 2005-11-17 14:12:45 +0100 | [diff] [blame] | 326 | static int snd_mpu401_uart_input_close(struct snd_rawmidi_substream *substream) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 327 | { | 
| Takashi Iwai | e1fad17 | 2005-11-17 14:12:45 +0100 | [diff] [blame] | 328 | 	struct snd_mpu401 *mpu; | 
| Jon Masters | 962f831 | 2006-04-20 02:43:20 -0700 | [diff] [blame] | 329 | 	int err = 0; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 330 |  | 
 | 331 | 	mpu = substream->rmidi->private_data; | 
 | 332 | 	clear_bit(MPU401_MODE_BIT_INPUT, &mpu->mode); | 
 | 333 | 	mpu->substream_input = NULL; | 
 | 334 | 	if (! test_bit(MPU401_MODE_BIT_OUTPUT, &mpu->mode)) | 
| Jon Masters | 962f831 | 2006-04-20 02:43:20 -0700 | [diff] [blame] | 335 | 		err = snd_mpu401_uart_cmd(mpu, MPU401_RESET, 0); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 336 | 	if (mpu->close_input) | 
 | 337 | 		mpu->close_input(mpu); | 
| Jon Masters | 962f831 | 2006-04-20 02:43:20 -0700 | [diff] [blame] | 338 | 	if (err) | 
 | 339 | 		return -EIO; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 340 | 	return 0; | 
 | 341 | } | 
 | 342 |  | 
| Takashi Iwai | e1fad17 | 2005-11-17 14:12:45 +0100 | [diff] [blame] | 343 | static int snd_mpu401_uart_output_close(struct snd_rawmidi_substream *substream) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 344 | { | 
| Takashi Iwai | e1fad17 | 2005-11-17 14:12:45 +0100 | [diff] [blame] | 345 | 	struct snd_mpu401 *mpu; | 
| Jon Masters | 962f831 | 2006-04-20 02:43:20 -0700 | [diff] [blame] | 346 | 	int err = 0; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 347 |  | 
 | 348 | 	mpu = substream->rmidi->private_data; | 
 | 349 | 	clear_bit(MPU401_MODE_BIT_OUTPUT, &mpu->mode); | 
 | 350 | 	mpu->substream_output = NULL; | 
 | 351 | 	if (! test_bit(MPU401_MODE_BIT_INPUT, &mpu->mode)) | 
| Jon Masters | 962f831 | 2006-04-20 02:43:20 -0700 | [diff] [blame] | 352 | 		err = snd_mpu401_uart_cmd(mpu, MPU401_RESET, 0); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 353 | 	if (mpu->close_output) | 
 | 354 | 		mpu->close_output(mpu); | 
| Jon Masters | 962f831 | 2006-04-20 02:43:20 -0700 | [diff] [blame] | 355 | 	if (err) | 
 | 356 | 		return -EIO; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 357 | 	return 0; | 
 | 358 | } | 
 | 359 |  | 
 | 360 | /* | 
 | 361 |  * trigger input callback | 
 | 362 |  */ | 
| Takashi Iwai | 2851d96 | 2006-05-18 14:48:26 +0200 | [diff] [blame] | 363 | static void | 
 | 364 | snd_mpu401_uart_input_trigger(struct snd_rawmidi_substream *substream, int up) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 365 | { | 
 | 366 | 	unsigned long flags; | 
| Takashi Iwai | e1fad17 | 2005-11-17 14:12:45 +0100 | [diff] [blame] | 367 | 	struct snd_mpu401 *mpu; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 368 | 	int max = 64; | 
 | 369 |  | 
 | 370 | 	mpu = substream->rmidi->private_data; | 
 | 371 | 	if (up) { | 
| Takashi Iwai | 2851d96 | 2006-05-18 14:48:26 +0200 | [diff] [blame] | 372 | 		if (! test_and_set_bit(MPU401_MODE_BIT_INPUT_TRIGGER, | 
 | 373 | 				       &mpu->mode)) { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 374 | 			/* first time - flush FIFO */ | 
 | 375 | 			while (max-- > 0) | 
 | 376 | 				mpu->read(mpu, MPU401D(mpu)); | 
 | 377 | 			if (mpu->irq < 0) | 
 | 378 | 				snd_mpu401_uart_add_timer(mpu, 1); | 
 | 379 | 		} | 
 | 380 | 		 | 
 | 381 | 		/* read data in advance */ | 
 | 382 | 		spin_lock_irqsave(&mpu->input_lock, flags); | 
 | 383 | 		snd_mpu401_uart_input_read(mpu); | 
 | 384 | 		spin_unlock_irqrestore(&mpu->input_lock, flags); | 
 | 385 | 	} else { | 
 | 386 | 		if (mpu->irq < 0) | 
 | 387 | 			snd_mpu401_uart_remove_timer(mpu, 1); | 
 | 388 | 		clear_bit(MPU401_MODE_BIT_INPUT_TRIGGER, &mpu->mode); | 
 | 389 | 	} | 
| Jon Masters | 962f831 | 2006-04-20 02:43:20 -0700 | [diff] [blame] | 390 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 391 | } | 
 | 392 |  | 
 | 393 | /* | 
 | 394 |  * transfer input pending data | 
 | 395 |  * call with input_lock spinlock held | 
 | 396 |  */ | 
| Takashi Iwai | e1fad17 | 2005-11-17 14:12:45 +0100 | [diff] [blame] | 397 | static void snd_mpu401_uart_input_read(struct snd_mpu401 * mpu) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 398 | { | 
 | 399 | 	int max = 128; | 
 | 400 | 	unsigned char byte; | 
 | 401 |  | 
 | 402 | 	while (max-- > 0) { | 
| Takashi Iwai | 2851d96 | 2006-05-18 14:48:26 +0200 | [diff] [blame] | 403 | 		if (! snd_mpu401_input_avail(mpu)) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 404 | 			break; /* input not available */ | 
| Takashi Iwai | 2851d96 | 2006-05-18 14:48:26 +0200 | [diff] [blame] | 405 | 		byte = mpu->read(mpu, MPU401D(mpu)); | 
 | 406 | 		if (test_bit(MPU401_MODE_BIT_INPUT_TRIGGER, &mpu->mode)) | 
 | 407 | 			snd_rawmidi_receive(mpu->substream_input, &byte, 1); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 408 | 	} | 
 | 409 | } | 
 | 410 |  | 
 | 411 | /* | 
 | 412 |  *  Tx FIFO sizes: | 
 | 413 |  *    CS4237B			- 16 bytes | 
 | 414 |  *    AudioDrive ES1688         - 12 bytes | 
 | 415 |  *    S3 SonicVibes             -  8 bytes | 
 | 416 |  *    SoundBlaster AWE 64       -  2 bytes (ugly hardware) | 
 | 417 |  */ | 
 | 418 |  | 
 | 419 | /* | 
 | 420 |  * write output pending bytes | 
 | 421 |  * call with output_lock spinlock held | 
 | 422 |  */ | 
| Takashi Iwai | e1fad17 | 2005-11-17 14:12:45 +0100 | [diff] [blame] | 423 | static void snd_mpu401_uart_output_write(struct snd_mpu401 * mpu) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 424 | { | 
 | 425 | 	unsigned char byte; | 
| Clemens Ladisch | ea6b582 | 2008-02-25 10:59:52 +0100 | [diff] [blame] | 426 | 	int max = 256; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 427 |  | 
 | 428 | 	do { | 
| Takashi Iwai | 2851d96 | 2006-05-18 14:48:26 +0200 | [diff] [blame] | 429 | 		if (snd_rawmidi_transmit_peek(mpu->substream_output, | 
 | 430 | 					      &byte, 1) == 1) { | 
| Clemens Ladisch | ea6b582 | 2008-02-25 10:59:52 +0100 | [diff] [blame] | 431 | 			/* | 
 | 432 | 			 * Try twice because there is hardware that insists on | 
 | 433 | 			 * setting the output busy bit after each write. | 
 | 434 | 			 */ | 
 | 435 | 			if (!snd_mpu401_output_ready(mpu) && | 
 | 436 | 			    !snd_mpu401_output_ready(mpu)) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 437 | 				break;	/* Tx FIFO full - try again later */ | 
| Takashi Iwai | 2851d96 | 2006-05-18 14:48:26 +0200 | [diff] [blame] | 438 | 			mpu->write(mpu, byte, MPU401D(mpu)); | 
 | 439 | 			snd_rawmidi_transmit_ack(mpu->substream_output, 1); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 440 | 		} else { | 
 | 441 | 			snd_mpu401_uart_remove_timer (mpu, 0); | 
 | 442 | 			break;	/* no other data - leave the tx loop */ | 
 | 443 | 		} | 
 | 444 | 	} while (--max > 0); | 
 | 445 | } | 
 | 446 |  | 
 | 447 | /* | 
 | 448 |  * output trigger callback | 
 | 449 |  */ | 
| Takashi Iwai | 2851d96 | 2006-05-18 14:48:26 +0200 | [diff] [blame] | 450 | static void | 
 | 451 | snd_mpu401_uart_output_trigger(struct snd_rawmidi_substream *substream, int up) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 452 | { | 
 | 453 | 	unsigned long flags; | 
| Takashi Iwai | e1fad17 | 2005-11-17 14:12:45 +0100 | [diff] [blame] | 454 | 	struct snd_mpu401 *mpu; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 455 |  | 
 | 456 | 	mpu = substream->rmidi->private_data; | 
 | 457 | 	if (up) { | 
 | 458 | 		set_bit(MPU401_MODE_BIT_OUTPUT_TRIGGER, &mpu->mode); | 
 | 459 |  | 
 | 460 | 		/* try to add the timer at each output trigger, | 
 | 461 | 		 * since the output timer might have been removed in | 
 | 462 | 		 * snd_mpu401_uart_output_write(). | 
 | 463 | 		 */ | 
| Takashi Iwai | 302e4c2 | 2006-05-23 13:24:30 +0200 | [diff] [blame] | 464 | 		if (! (mpu->info_flags & MPU401_INFO_TX_IRQ)) | 
 | 465 | 			snd_mpu401_uart_add_timer(mpu, 0); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 466 |  | 
 | 467 | 		/* output pending data */ | 
 | 468 | 		spin_lock_irqsave(&mpu->output_lock, flags); | 
 | 469 | 		snd_mpu401_uart_output_write(mpu); | 
 | 470 | 		spin_unlock_irqrestore(&mpu->output_lock, flags); | 
 | 471 | 	} else { | 
| Takashi Iwai | 302e4c2 | 2006-05-23 13:24:30 +0200 | [diff] [blame] | 472 | 		if (! (mpu->info_flags & MPU401_INFO_TX_IRQ)) | 
 | 473 | 			snd_mpu401_uart_remove_timer(mpu, 0); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 474 | 		clear_bit(MPU401_MODE_BIT_OUTPUT_TRIGGER, &mpu->mode); | 
 | 475 | 	} | 
 | 476 | } | 
 | 477 |  | 
 | 478 | /* | 
 | 479 |  | 
 | 480 |  */ | 
 | 481 |  | 
| Takashi Iwai | e1fad17 | 2005-11-17 14:12:45 +0100 | [diff] [blame] | 482 | static struct snd_rawmidi_ops snd_mpu401_uart_output = | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 483 | { | 
 | 484 | 	.open =		snd_mpu401_uart_output_open, | 
 | 485 | 	.close =	snd_mpu401_uart_output_close, | 
 | 486 | 	.trigger =	snd_mpu401_uart_output_trigger, | 
 | 487 | }; | 
 | 488 |  | 
| Takashi Iwai | e1fad17 | 2005-11-17 14:12:45 +0100 | [diff] [blame] | 489 | static struct snd_rawmidi_ops snd_mpu401_uart_input = | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 490 | { | 
 | 491 | 	.open =		snd_mpu401_uart_input_open, | 
 | 492 | 	.close =	snd_mpu401_uart_input_close, | 
 | 493 | 	.trigger =	snd_mpu401_uart_input_trigger, | 
 | 494 | }; | 
 | 495 |  | 
| Takashi Iwai | e1fad17 | 2005-11-17 14:12:45 +0100 | [diff] [blame] | 496 | static void snd_mpu401_uart_free(struct snd_rawmidi *rmidi) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 497 | { | 
| Takashi Iwai | e1fad17 | 2005-11-17 14:12:45 +0100 | [diff] [blame] | 498 | 	struct snd_mpu401 *mpu = rmidi->private_data; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 499 | 	if (mpu->irq_flags && mpu->irq >= 0) | 
 | 500 | 		free_irq(mpu->irq, (void *) mpu); | 
| Takashi Iwai | b1d5776 | 2005-10-10 11:56:31 +0200 | [diff] [blame] | 501 | 	release_and_free_resource(mpu->res); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 502 | 	kfree(mpu); | 
 | 503 | } | 
 | 504 |  | 
 | 505 | /** | 
 | 506 |  * snd_mpu401_uart_new - create an MPU401-UART instance | 
 | 507 |  * @card: the card instance | 
 | 508 |  * @device: the device index, zero-based | 
 | 509 |  * @hardware: the hardware type, MPU401_HW_XXXX | 
 | 510 |  * @port: the base address of MPU401 port | 
| Takashi Iwai | 302e4c2 | 2006-05-23 13:24:30 +0200 | [diff] [blame] | 511 |  * @info_flags: bitflags MPU401_INFO_XXX | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 512 |  * @irq: the irq number, -1 if no interrupt for mpu | 
 | 513 |  * @irq_flags: the irq request flags (SA_XXX), 0 if irq was already reserved. | 
 | 514 |  * @rrawmidi: the pointer to store the new rawmidi instance | 
 | 515 |  * | 
 | 516 |  * Creates a new MPU-401 instance. | 
 | 517 |  * | 
 | 518 |  * Note that the rawmidi instance is returned on the rrawmidi argument, | 
 | 519 |  * not the mpu401 instance itself.  To access to the mpu401 instance, | 
| Takashi Iwai | e1fad17 | 2005-11-17 14:12:45 +0100 | [diff] [blame] | 520 |  * cast from rawmidi->private_data (with struct snd_mpu401 magic-cast). | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 521 |  * | 
 | 522 |  * Returns zero if successful, or a negative error code. | 
 | 523 |  */ | 
| Takashi Iwai | e1fad17 | 2005-11-17 14:12:45 +0100 | [diff] [blame] | 524 | int snd_mpu401_uart_new(struct snd_card *card, int device, | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 525 | 			unsigned short hardware, | 
| Takashi Iwai | 302e4c2 | 2006-05-23 13:24:30 +0200 | [diff] [blame] | 526 | 			unsigned long port, | 
 | 527 | 			unsigned int info_flags, | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 528 | 			int irq, int irq_flags, | 
| Takashi Iwai | e1fad17 | 2005-11-17 14:12:45 +0100 | [diff] [blame] | 529 | 			struct snd_rawmidi ** rrawmidi) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 530 | { | 
| Takashi Iwai | e1fad17 | 2005-11-17 14:12:45 +0100 | [diff] [blame] | 531 | 	struct snd_mpu401 *mpu; | 
 | 532 | 	struct snd_rawmidi *rmidi; | 
| Takashi Iwai | 302e4c2 | 2006-05-23 13:24:30 +0200 | [diff] [blame] | 533 | 	int in_enable, out_enable; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 534 | 	int err; | 
 | 535 |  | 
 | 536 | 	if (rrawmidi) | 
 | 537 | 		*rrawmidi = NULL; | 
| Takashi Iwai | 302e4c2 | 2006-05-23 13:24:30 +0200 | [diff] [blame] | 538 | 	if (! (info_flags & (MPU401_INFO_INPUT | MPU401_INFO_OUTPUT))) | 
 | 539 | 		info_flags |= MPU401_INFO_INPUT | MPU401_INFO_OUTPUT; | 
 | 540 | 	in_enable = (info_flags & MPU401_INFO_INPUT) ? 1 : 0; | 
 | 541 | 	out_enable = (info_flags & MPU401_INFO_OUTPUT) ? 1 : 0; | 
 | 542 | 	if ((err = snd_rawmidi_new(card, "MPU-401U", device, | 
 | 543 | 				   out_enable, in_enable, &rmidi)) < 0) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 544 | 		return err; | 
| Takashi Iwai | 561b220 | 2005-09-09 14:22:34 +0200 | [diff] [blame] | 545 | 	mpu = kzalloc(sizeof(*mpu), GFP_KERNEL); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 546 | 	if (mpu == NULL) { | 
| Takashi Iwai | 73e77ba | 2005-11-17 17:44:01 +0100 | [diff] [blame] | 547 | 		snd_printk(KERN_ERR "mpu401_uart: cannot allocate\n"); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 548 | 		snd_device_free(card, rmidi); | 
 | 549 | 		return -ENOMEM; | 
 | 550 | 	} | 
 | 551 | 	rmidi->private_data = mpu; | 
 | 552 | 	rmidi->private_free = snd_mpu401_uart_free; | 
 | 553 | 	spin_lock_init(&mpu->input_lock); | 
 | 554 | 	spin_lock_init(&mpu->output_lock); | 
 | 555 | 	spin_lock_init(&mpu->timer_lock); | 
 | 556 | 	mpu->hardware = hardware; | 
| Takashi Iwai | 302e4c2 | 2006-05-23 13:24:30 +0200 | [diff] [blame] | 557 | 	if (! (info_flags & MPU401_INFO_INTEGRATED)) { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 558 | 		int res_size = hardware == MPU401_HW_PC98II ? 4 : 2; | 
| Takashi Iwai | 2851d96 | 2006-05-18 14:48:26 +0200 | [diff] [blame] | 559 | 		mpu->res = request_region(port, res_size, "MPU401 UART"); | 
 | 560 | 		if (mpu->res == NULL) { | 
 | 561 | 			snd_printk(KERN_ERR "mpu401_uart: " | 
 | 562 | 				   "unable to grab port 0x%lx size %d\n", | 
 | 563 | 				   port, res_size); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 564 | 			snd_device_free(card, rmidi); | 
 | 565 | 			return -EBUSY; | 
 | 566 | 		} | 
 | 567 | 	} | 
| Takashi Iwai | 302e4c2 | 2006-05-23 13:24:30 +0200 | [diff] [blame] | 568 | 	if (info_flags & MPU401_INFO_MMIO) { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 569 | 		mpu->write = mpu401_write_mmio; | 
 | 570 | 		mpu->read = mpu401_read_mmio; | 
| Takashi Iwai | 302e4c2 | 2006-05-23 13:24:30 +0200 | [diff] [blame] | 571 | 	} else { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 572 | 		mpu->write = mpu401_write_port; | 
 | 573 | 		mpu->read = mpu401_read_port; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 574 | 	} | 
 | 575 | 	mpu->port = port; | 
 | 576 | 	if (hardware == MPU401_HW_PC98II) | 
 | 577 | 		mpu->cport = port + 2; | 
 | 578 | 	else | 
 | 579 | 		mpu->cport = port + 1; | 
 | 580 | 	if (irq >= 0 && irq_flags) { | 
| Takashi Iwai | 2851d96 | 2006-05-18 14:48:26 +0200 | [diff] [blame] | 581 | 		if (request_irq(irq, snd_mpu401_uart_interrupt, irq_flags, | 
 | 582 | 				"MPU401 UART", (void *) mpu)) { | 
 | 583 | 			snd_printk(KERN_ERR "mpu401_uart: " | 
 | 584 | 				   "unable to grab IRQ %d\n", irq); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 585 | 			snd_device_free(card, rmidi); | 
 | 586 | 			return -EBUSY; | 
 | 587 | 		} | 
 | 588 | 	} | 
| Takashi Iwai | 302e4c2 | 2006-05-23 13:24:30 +0200 | [diff] [blame] | 589 | 	mpu->info_flags = info_flags; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 590 | 	mpu->irq = irq; | 
 | 591 | 	mpu->irq_flags = irq_flags; | 
 | 592 | 	if (card->shortname[0]) | 
| Takashi Iwai | 2851d96 | 2006-05-18 14:48:26 +0200 | [diff] [blame] | 593 | 		snprintf(rmidi->name, sizeof(rmidi->name), "%s MIDI", | 
 | 594 | 			 card->shortname); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 595 | 	else | 
| Takashi Iwai | 2851d96 | 2006-05-18 14:48:26 +0200 | [diff] [blame] | 596 | 		sprintf(rmidi->name, "MPU-401 MIDI %d-%d",card->number, device); | 
| Takashi Iwai | 302e4c2 | 2006-05-23 13:24:30 +0200 | [diff] [blame] | 597 | 	if (out_enable) { | 
 | 598 | 		snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, | 
 | 599 | 				    &snd_mpu401_uart_output); | 
 | 600 | 		rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT; | 
 | 601 | 	} | 
 | 602 | 	if (in_enable) { | 
 | 603 | 		snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, | 
 | 604 | 				    &snd_mpu401_uart_input); | 
 | 605 | 		rmidi->info_flags |= SNDRV_RAWMIDI_INFO_INPUT; | 
 | 606 | 		if (out_enable) | 
 | 607 | 			rmidi->info_flags |= SNDRV_RAWMIDI_INFO_DUPLEX; | 
 | 608 | 	} | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 609 | 	mpu->rmidi = rmidi; | 
 | 610 | 	if (rrawmidi) | 
 | 611 | 		*rrawmidi = rmidi; | 
 | 612 | 	return 0; | 
 | 613 | } | 
 | 614 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 615 | EXPORT_SYMBOL(snd_mpu401_uart_new); | 
 | 616 |  | 
 | 617 | /* | 
 | 618 |  *  INIT part | 
 | 619 |  */ | 
 | 620 |  | 
 | 621 | static int __init alsa_mpu401_uart_init(void) | 
 | 622 | { | 
 | 623 | 	return 0; | 
 | 624 | } | 
 | 625 |  | 
 | 626 | static void __exit alsa_mpu401_uart_exit(void) | 
 | 627 | { | 
 | 628 | } | 
 | 629 |  | 
 | 630 | module_init(alsa_mpu401_uart_init) | 
 | 631 | module_exit(alsa_mpu401_uart_exit) |