11da177e4SLinus Torvalds /* 21da177e4SLinus Torvalds * Copyright (c) by Jaroslav Kysela <perex@suse.cz> 31da177e4SLinus Torvalds * Routines for control of MPU-401 in UART mode 41da177e4SLinus Torvalds * 51da177e4SLinus Torvalds * MPU-401 supports UART mode which is not capable generate transmit 61da177e4SLinus Torvalds * interrupts thus output is done via polling. Also, if irq < 0, then 71da177e4SLinus Torvalds * input is done also via polling. Do not expect good performance. 81da177e4SLinus Torvalds * 91da177e4SLinus Torvalds * 101da177e4SLinus Torvalds * This program is free software; you can redistribute it and/or modify 111da177e4SLinus Torvalds * it under the terms of the GNU General Public License as published by 121da177e4SLinus Torvalds * the Free Software Foundation; either version 2 of the License, or 131da177e4SLinus Torvalds * (at your option) any later version. 141da177e4SLinus Torvalds * 151da177e4SLinus Torvalds * This program is distributed in the hope that it will be useful, 161da177e4SLinus Torvalds * but WITHOUT ANY WARRANTY; without even the implied warranty of 171da177e4SLinus Torvalds * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 181da177e4SLinus Torvalds * GNU General Public License for more details. 191da177e4SLinus Torvalds * 201da177e4SLinus Torvalds * You should have received a copy of the GNU General Public License 211da177e4SLinus Torvalds * along with this program; if not, write to the Free Software 221da177e4SLinus Torvalds * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 231da177e4SLinus Torvalds * 241da177e4SLinus Torvalds * 13-03-2003: 251da177e4SLinus Torvalds * Added support for different kind of hardware I/O. Build in choices 261da177e4SLinus Torvalds * are port and mmio. For other kind of I/O, set mpu->read and 271da177e4SLinus Torvalds * mpu->write to your own I/O functions. 281da177e4SLinus Torvalds * 291da177e4SLinus Torvalds */ 301da177e4SLinus Torvalds 311da177e4SLinus Torvalds #include <sound/driver.h> 321da177e4SLinus Torvalds #include <asm/io.h> 331da177e4SLinus Torvalds #include <linux/delay.h> 341da177e4SLinus Torvalds #include <linux/init.h> 351da177e4SLinus Torvalds #include <linux/slab.h> 361da177e4SLinus Torvalds #include <linux/ioport.h> 371da177e4SLinus Torvalds #include <linux/interrupt.h> 381da177e4SLinus Torvalds #include <linux/errno.h> 391da177e4SLinus Torvalds #include <sound/core.h> 401da177e4SLinus Torvalds #include <sound/mpu401.h> 411da177e4SLinus Torvalds 421da177e4SLinus Torvalds MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>"); 431da177e4SLinus Torvalds MODULE_DESCRIPTION("Routines for control of MPU-401 in UART mode"); 441da177e4SLinus Torvalds MODULE_LICENSE("GPL"); 451da177e4SLinus Torvalds 46e1fad17bSTakashi Iwai static void snd_mpu401_uart_input_read(struct snd_mpu401 * mpu); 47e1fad17bSTakashi Iwai static void snd_mpu401_uart_output_write(struct snd_mpu401 * mpu); 481da177e4SLinus Torvalds 491da177e4SLinus Torvalds /* 501da177e4SLinus Torvalds 511da177e4SLinus Torvalds */ 521da177e4SLinus Torvalds 531da177e4SLinus Torvalds #define snd_mpu401_input_avail(mpu) (!(mpu->read(mpu, MPU401C(mpu)) & 0x80)) 541da177e4SLinus Torvalds #define snd_mpu401_output_ready(mpu) (!(mpu->read(mpu, MPU401C(mpu)) & 0x40)) 551da177e4SLinus Torvalds 561da177e4SLinus Torvalds #define MPU401_RESET 0xff 571da177e4SLinus Torvalds #define MPU401_ENTER_UART 0x3f 581da177e4SLinus Torvalds #define MPU401_ACK 0xfe 591da177e4SLinus Torvalds 601da177e4SLinus Torvalds /* Build in lowlevel io */ 61e1fad17bSTakashi Iwai static void mpu401_write_port(struct snd_mpu401 *mpu, unsigned char data, unsigned long addr) 621da177e4SLinus Torvalds { 631da177e4SLinus Torvalds outb(data, addr); 641da177e4SLinus Torvalds } 651da177e4SLinus Torvalds 66e1fad17bSTakashi Iwai static unsigned char mpu401_read_port(struct snd_mpu401 *mpu, unsigned long addr) 671da177e4SLinus Torvalds { 681da177e4SLinus Torvalds return inb(addr); 691da177e4SLinus Torvalds } 701da177e4SLinus Torvalds 71e1fad17bSTakashi Iwai static void mpu401_write_mmio(struct snd_mpu401 *mpu, unsigned char data, unsigned long addr) 721da177e4SLinus Torvalds { 731da177e4SLinus Torvalds writeb(data, (void __iomem *)addr); 741da177e4SLinus Torvalds } 751da177e4SLinus Torvalds 76e1fad17bSTakashi Iwai static unsigned char mpu401_read_mmio(struct snd_mpu401 *mpu, unsigned long addr) 771da177e4SLinus Torvalds { 781da177e4SLinus Torvalds return readb((void __iomem *)addr); 791da177e4SLinus Torvalds } 801da177e4SLinus Torvalds /* */ 811da177e4SLinus Torvalds 82e1fad17bSTakashi Iwai static void snd_mpu401_uart_clear_rx(struct snd_mpu401 *mpu) 831da177e4SLinus Torvalds { 841da177e4SLinus Torvalds int timeout = 100000; 851da177e4SLinus Torvalds for (; timeout > 0 && snd_mpu401_input_avail(mpu); timeout--) 861da177e4SLinus Torvalds mpu->read(mpu, MPU401D(mpu)); 871da177e4SLinus Torvalds #ifdef CONFIG_SND_DEBUG 881da177e4SLinus Torvalds if (timeout <= 0) 891da177e4SLinus Torvalds snd_printk("cmd: clear rx timeout (status = 0x%x)\n", mpu->read(mpu, MPU401C(mpu))); 901da177e4SLinus Torvalds #endif 911da177e4SLinus Torvalds } 921da177e4SLinus Torvalds 93e1fad17bSTakashi Iwai static void _snd_mpu401_uart_interrupt(struct snd_mpu401 *mpu) 941da177e4SLinus Torvalds { 951da177e4SLinus Torvalds spin_lock(&mpu->input_lock); 961da177e4SLinus Torvalds if (test_bit(MPU401_MODE_BIT_INPUT, &mpu->mode)) { 971da177e4SLinus Torvalds snd_mpu401_uart_input_read(mpu); 981da177e4SLinus Torvalds } else { 991da177e4SLinus Torvalds snd_mpu401_uart_clear_rx(mpu); 1001da177e4SLinus Torvalds } 1011da177e4SLinus Torvalds spin_unlock(&mpu->input_lock); 1021da177e4SLinus Torvalds /* ok. for better Tx performance try do some output when input is done */ 1031da177e4SLinus Torvalds if (test_bit(MPU401_MODE_BIT_OUTPUT, &mpu->mode) && 1041da177e4SLinus Torvalds test_bit(MPU401_MODE_BIT_OUTPUT_TRIGGER, &mpu->mode)) { 1051da177e4SLinus Torvalds spin_lock(&mpu->output_lock); 1061da177e4SLinus Torvalds snd_mpu401_uart_output_write(mpu); 1071da177e4SLinus Torvalds spin_unlock(&mpu->output_lock); 1081da177e4SLinus Torvalds } 1091da177e4SLinus Torvalds } 1101da177e4SLinus Torvalds 1111da177e4SLinus Torvalds /** 1121da177e4SLinus Torvalds * snd_mpu401_uart_interrupt - generic MPU401-UART interrupt handler 1131da177e4SLinus Torvalds * @irq: the irq number 1141da177e4SLinus Torvalds * @dev_id: mpu401 instance 1151da177e4SLinus Torvalds * @regs: the reigster 1161da177e4SLinus Torvalds * 1171da177e4SLinus Torvalds * Processes the interrupt for MPU401-UART i/o. 1181da177e4SLinus Torvalds */ 1191da177e4SLinus Torvalds irqreturn_t snd_mpu401_uart_interrupt(int irq, void *dev_id, struct pt_regs *regs) 1201da177e4SLinus Torvalds { 121e1fad17bSTakashi Iwai struct snd_mpu401 *mpu = dev_id; 1221da177e4SLinus Torvalds 1231da177e4SLinus Torvalds if (mpu == NULL) 1241da177e4SLinus Torvalds return IRQ_NONE; 1251da177e4SLinus Torvalds _snd_mpu401_uart_interrupt(mpu); 1261da177e4SLinus Torvalds return IRQ_HANDLED; 1271da177e4SLinus Torvalds } 1281da177e4SLinus Torvalds 1291da177e4SLinus Torvalds /* 1301da177e4SLinus Torvalds * timer callback 1311da177e4SLinus Torvalds * reprogram the timer and call the interrupt job 1321da177e4SLinus Torvalds */ 1331da177e4SLinus Torvalds static void snd_mpu401_uart_timer(unsigned long data) 1341da177e4SLinus Torvalds { 135e1fad17bSTakashi Iwai struct snd_mpu401 *mpu = (struct snd_mpu401 *)data; 136b32425acSTakashi Iwai unsigned long flags; 1371da177e4SLinus Torvalds 138b32425acSTakashi Iwai spin_lock_irqsave(&mpu->timer_lock, flags); 1391da177e4SLinus Torvalds /*mpu->mode |= MPU401_MODE_TIMER;*/ 1401da177e4SLinus Torvalds mpu->timer.expires = 1 + jiffies; 1411da177e4SLinus Torvalds add_timer(&mpu->timer); 142b32425acSTakashi Iwai spin_unlock_irqrestore(&mpu->timer_lock, flags); 1431da177e4SLinus Torvalds if (mpu->rmidi) 1441da177e4SLinus Torvalds _snd_mpu401_uart_interrupt(mpu); 1451da177e4SLinus Torvalds } 1461da177e4SLinus Torvalds 1471da177e4SLinus Torvalds /* 1481da177e4SLinus Torvalds * initialize the timer callback if not programmed yet 1491da177e4SLinus Torvalds */ 150e1fad17bSTakashi Iwai static void snd_mpu401_uart_add_timer (struct snd_mpu401 *mpu, int input) 1511da177e4SLinus Torvalds { 1521da177e4SLinus Torvalds unsigned long flags; 1531da177e4SLinus Torvalds 1541da177e4SLinus Torvalds spin_lock_irqsave (&mpu->timer_lock, flags); 1551da177e4SLinus Torvalds if (mpu->timer_invoked == 0) { 1561da177e4SLinus Torvalds init_timer(&mpu->timer); 1571da177e4SLinus Torvalds mpu->timer.data = (unsigned long)mpu; 1581da177e4SLinus Torvalds mpu->timer.function = snd_mpu401_uart_timer; 1591da177e4SLinus Torvalds mpu->timer.expires = 1 + jiffies; 1601da177e4SLinus Torvalds add_timer(&mpu->timer); 1611da177e4SLinus Torvalds } 1621da177e4SLinus Torvalds mpu->timer_invoked |= input ? MPU401_MODE_INPUT_TIMER : MPU401_MODE_OUTPUT_TIMER; 1631da177e4SLinus Torvalds spin_unlock_irqrestore (&mpu->timer_lock, flags); 1641da177e4SLinus Torvalds } 1651da177e4SLinus Torvalds 1661da177e4SLinus Torvalds /* 1671da177e4SLinus Torvalds * remove the timer callback if still active 1681da177e4SLinus Torvalds */ 169e1fad17bSTakashi Iwai static void snd_mpu401_uart_remove_timer (struct snd_mpu401 *mpu, int input) 1701da177e4SLinus Torvalds { 1711da177e4SLinus Torvalds unsigned long flags; 1721da177e4SLinus Torvalds 1731da177e4SLinus Torvalds spin_lock_irqsave (&mpu->timer_lock, flags); 1741da177e4SLinus Torvalds if (mpu->timer_invoked) { 1751da177e4SLinus Torvalds mpu->timer_invoked &= input ? ~MPU401_MODE_INPUT_TIMER : ~MPU401_MODE_OUTPUT_TIMER; 1761da177e4SLinus Torvalds if (! mpu->timer_invoked) 1771da177e4SLinus Torvalds del_timer(&mpu->timer); 1781da177e4SLinus Torvalds } 1791da177e4SLinus Torvalds spin_unlock_irqrestore (&mpu->timer_lock, flags); 1801da177e4SLinus Torvalds } 1811da177e4SLinus Torvalds 1821da177e4SLinus Torvalds /* 1831da177e4SLinus Torvalds 1841da177e4SLinus Torvalds */ 1851da177e4SLinus Torvalds 186962f831fSJon Masters static int snd_mpu401_uart_cmd(struct snd_mpu401 * mpu, unsigned char cmd, 187962f831fSJon Masters int ack) 1881da177e4SLinus Torvalds { 1891da177e4SLinus Torvalds unsigned long flags; 1901da177e4SLinus Torvalds int timeout, ok; 1911da177e4SLinus Torvalds 1921da177e4SLinus Torvalds spin_lock_irqsave(&mpu->input_lock, flags); 1931da177e4SLinus Torvalds if (mpu->hardware != MPU401_HW_TRID4DWAVE) { 1941da177e4SLinus Torvalds mpu->write(mpu, 0x00, MPU401D(mpu)); 1951da177e4SLinus Torvalds /*snd_mpu401_uart_clear_rx(mpu);*/ 1961da177e4SLinus Torvalds } 1971da177e4SLinus Torvalds /* ok. standard MPU-401 initialization */ 1981da177e4SLinus Torvalds if (mpu->hardware != MPU401_HW_SB) { 1991da177e4SLinus Torvalds for (timeout = 1000; timeout > 0 && !snd_mpu401_output_ready(mpu); timeout--) 2001da177e4SLinus Torvalds udelay(10); 2011da177e4SLinus Torvalds #ifdef CONFIG_SND_DEBUG 2021da177e4SLinus Torvalds if (!timeout) 2031da177e4SLinus Torvalds snd_printk("cmd: tx timeout (status = 0x%x)\n", mpu->read(mpu, MPU401C(mpu))); 2041da177e4SLinus Torvalds #endif 2051da177e4SLinus Torvalds } 2061da177e4SLinus Torvalds mpu->write(mpu, cmd, MPU401C(mpu)); 2071da177e4SLinus Torvalds if (ack) { 2081da177e4SLinus Torvalds ok = 0; 2091da177e4SLinus Torvalds timeout = 10000; 2101da177e4SLinus Torvalds while (!ok && timeout-- > 0) { 2111da177e4SLinus Torvalds if (snd_mpu401_input_avail(mpu)) { 2121da177e4SLinus Torvalds if (mpu->read(mpu, MPU401D(mpu)) == MPU401_ACK) 2131da177e4SLinus Torvalds ok = 1; 2141da177e4SLinus Torvalds } 2151da177e4SLinus Torvalds } 2161da177e4SLinus Torvalds if (!ok && mpu->read(mpu, MPU401D(mpu)) == MPU401_ACK) 2171da177e4SLinus Torvalds ok = 1; 2181da177e4SLinus Torvalds } else { 2191da177e4SLinus Torvalds ok = 1; 2201da177e4SLinus Torvalds } 2211da177e4SLinus Torvalds spin_unlock_irqrestore(&mpu->input_lock, flags); 222962f831fSJon Masters if (!ok) { 2231da177e4SLinus Torvalds snd_printk("cmd: 0x%x failed at 0x%lx (status = 0x%x, data = 0x%x)\n", cmd, mpu->port, mpu->read(mpu, MPU401C(mpu)), mpu->read(mpu, MPU401D(mpu))); 224962f831fSJon Masters return 1; 225962f831fSJon Masters } 226962f831fSJon Masters return 0; 2271da177e4SLinus Torvalds } 2281da177e4SLinus Torvalds 2291da177e4SLinus Torvalds /* 2301da177e4SLinus Torvalds * input/output open/close - protected by open_mutex in rawmidi.c 2311da177e4SLinus Torvalds */ 232e1fad17bSTakashi Iwai static int snd_mpu401_uart_input_open(struct snd_rawmidi_substream *substream) 2331da177e4SLinus Torvalds { 234e1fad17bSTakashi Iwai struct snd_mpu401 *mpu; 2351da177e4SLinus Torvalds int err; 2361da177e4SLinus Torvalds 2371da177e4SLinus Torvalds mpu = substream->rmidi->private_data; 2381da177e4SLinus Torvalds if (mpu->open_input && (err = mpu->open_input(mpu)) < 0) 2391da177e4SLinus Torvalds return err; 2401da177e4SLinus Torvalds if (! test_bit(MPU401_MODE_BIT_OUTPUT, &mpu->mode)) { 241962f831fSJon Masters if (snd_mpu401_uart_cmd(mpu, MPU401_RESET, 1)) 242962f831fSJon Masters goto error_out; 243962f831fSJon Masters if (snd_mpu401_uart_cmd(mpu, MPU401_ENTER_UART, 1)) 244962f831fSJon Masters goto error_out; 2451da177e4SLinus Torvalds } 2461da177e4SLinus Torvalds mpu->substream_input = substream; 2471da177e4SLinus Torvalds set_bit(MPU401_MODE_BIT_INPUT, &mpu->mode); 2481da177e4SLinus Torvalds return 0; 249962f831fSJon Masters 250962f831fSJon Masters error_out: 251962f831fSJon Masters if (mpu->open_input && mpu->close_input) 252962f831fSJon Masters mpu->close_input(mpu); 253962f831fSJon Masters return -EIO; 2541da177e4SLinus Torvalds } 2551da177e4SLinus Torvalds 256e1fad17bSTakashi Iwai static int snd_mpu401_uart_output_open(struct snd_rawmidi_substream *substream) 2571da177e4SLinus Torvalds { 258e1fad17bSTakashi Iwai struct snd_mpu401 *mpu; 2591da177e4SLinus Torvalds int err; 2601da177e4SLinus Torvalds 2611da177e4SLinus Torvalds mpu = substream->rmidi->private_data; 2621da177e4SLinus Torvalds if (mpu->open_output && (err = mpu->open_output(mpu)) < 0) 2631da177e4SLinus Torvalds return err; 2641da177e4SLinus Torvalds if (! test_bit(MPU401_MODE_BIT_INPUT, &mpu->mode)) { 265962f831fSJon Masters if (snd_mpu401_uart_cmd(mpu, MPU401_RESET, 1)) 266962f831fSJon Masters goto error_out; 267962f831fSJon Masters if (snd_mpu401_uart_cmd(mpu, MPU401_ENTER_UART, 1)) 268962f831fSJon Masters goto error_out; 2691da177e4SLinus Torvalds } 2701da177e4SLinus Torvalds mpu->substream_output = substream; 2711da177e4SLinus Torvalds set_bit(MPU401_MODE_BIT_OUTPUT, &mpu->mode); 2721da177e4SLinus Torvalds return 0; 273962f831fSJon Masters 274962f831fSJon Masters error_out: 275962f831fSJon Masters if (mpu->open_output && mpu->close_output) 276962f831fSJon Masters mpu->close_output(mpu); 277962f831fSJon Masters return -EIO; 2781da177e4SLinus Torvalds } 2791da177e4SLinus Torvalds 280e1fad17bSTakashi Iwai static int snd_mpu401_uart_input_close(struct snd_rawmidi_substream *substream) 2811da177e4SLinus Torvalds { 282e1fad17bSTakashi Iwai struct snd_mpu401 *mpu; 283962f831fSJon Masters int err = 0; 2841da177e4SLinus Torvalds 2851da177e4SLinus Torvalds mpu = substream->rmidi->private_data; 2861da177e4SLinus Torvalds clear_bit(MPU401_MODE_BIT_INPUT, &mpu->mode); 2871da177e4SLinus Torvalds mpu->substream_input = NULL; 2881da177e4SLinus Torvalds if (! test_bit(MPU401_MODE_BIT_OUTPUT, &mpu->mode)) 289962f831fSJon Masters err = snd_mpu401_uart_cmd(mpu, MPU401_RESET, 0); 2901da177e4SLinus Torvalds if (mpu->close_input) 2911da177e4SLinus Torvalds mpu->close_input(mpu); 292962f831fSJon Masters if (err) 293962f831fSJon Masters return -EIO; 2941da177e4SLinus Torvalds return 0; 2951da177e4SLinus Torvalds } 2961da177e4SLinus Torvalds 297e1fad17bSTakashi Iwai static int snd_mpu401_uart_output_close(struct snd_rawmidi_substream *substream) 2981da177e4SLinus Torvalds { 299e1fad17bSTakashi Iwai struct snd_mpu401 *mpu; 300962f831fSJon Masters int err = 0; 3011da177e4SLinus Torvalds 3021da177e4SLinus Torvalds mpu = substream->rmidi->private_data; 3031da177e4SLinus Torvalds clear_bit(MPU401_MODE_BIT_OUTPUT, &mpu->mode); 3041da177e4SLinus Torvalds mpu->substream_output = NULL; 3051da177e4SLinus Torvalds if (! test_bit(MPU401_MODE_BIT_INPUT, &mpu->mode)) 306962f831fSJon Masters err = snd_mpu401_uart_cmd(mpu, MPU401_RESET, 0); 3071da177e4SLinus Torvalds if (mpu->close_output) 3081da177e4SLinus Torvalds mpu->close_output(mpu); 309962f831fSJon Masters if (err) 310962f831fSJon Masters return -EIO; 3111da177e4SLinus Torvalds return 0; 3121da177e4SLinus Torvalds } 3131da177e4SLinus Torvalds 3141da177e4SLinus Torvalds /* 3151da177e4SLinus Torvalds * trigger input callback 3161da177e4SLinus Torvalds */ 317e1fad17bSTakashi Iwai static void snd_mpu401_uart_input_trigger(struct snd_rawmidi_substream *substream, int up) 3181da177e4SLinus Torvalds { 3191da177e4SLinus Torvalds unsigned long flags; 320e1fad17bSTakashi Iwai struct snd_mpu401 *mpu; 3211da177e4SLinus Torvalds int max = 64; 3221da177e4SLinus Torvalds 3231da177e4SLinus Torvalds mpu = substream->rmidi->private_data; 3241da177e4SLinus Torvalds if (up) { 3251da177e4SLinus Torvalds if (! test_and_set_bit(MPU401_MODE_BIT_INPUT_TRIGGER, &mpu->mode)) { 3261da177e4SLinus Torvalds /* first time - flush FIFO */ 3271da177e4SLinus Torvalds while (max-- > 0) 3281da177e4SLinus Torvalds mpu->read(mpu, MPU401D(mpu)); 3291da177e4SLinus Torvalds if (mpu->irq < 0) 3301da177e4SLinus Torvalds snd_mpu401_uart_add_timer(mpu, 1); 3311da177e4SLinus Torvalds } 3321da177e4SLinus Torvalds 3331da177e4SLinus Torvalds /* read data in advance */ 3341da177e4SLinus Torvalds spin_lock_irqsave(&mpu->input_lock, flags); 3351da177e4SLinus Torvalds snd_mpu401_uart_input_read(mpu); 3361da177e4SLinus Torvalds spin_unlock_irqrestore(&mpu->input_lock, flags); 3371da177e4SLinus Torvalds } else { 3381da177e4SLinus Torvalds if (mpu->irq < 0) 3391da177e4SLinus Torvalds snd_mpu401_uart_remove_timer(mpu, 1); 3401da177e4SLinus Torvalds clear_bit(MPU401_MODE_BIT_INPUT_TRIGGER, &mpu->mode); 3411da177e4SLinus Torvalds } 342962f831fSJon Masters 3431da177e4SLinus Torvalds } 3441da177e4SLinus Torvalds 3451da177e4SLinus Torvalds /* 3461da177e4SLinus Torvalds * transfer input pending data 3471da177e4SLinus Torvalds * call with input_lock spinlock held 3481da177e4SLinus Torvalds */ 349e1fad17bSTakashi Iwai static void snd_mpu401_uart_input_read(struct snd_mpu401 * mpu) 3501da177e4SLinus Torvalds { 3511da177e4SLinus Torvalds int max = 128; 3521da177e4SLinus Torvalds unsigned char byte; 3531da177e4SLinus Torvalds 3541da177e4SLinus Torvalds while (max-- > 0) { 3551da177e4SLinus Torvalds if (snd_mpu401_input_avail(mpu)) { 3561da177e4SLinus Torvalds byte = mpu->read(mpu, MPU401D(mpu)); 3571da177e4SLinus Torvalds if (test_bit(MPU401_MODE_BIT_INPUT_TRIGGER, &mpu->mode)) 3581da177e4SLinus Torvalds snd_rawmidi_receive(mpu->substream_input, &byte, 1); 3591da177e4SLinus Torvalds } else { 3601da177e4SLinus Torvalds break; /* input not available */ 3611da177e4SLinus Torvalds } 3621da177e4SLinus Torvalds } 3631da177e4SLinus Torvalds } 3641da177e4SLinus Torvalds 3651da177e4SLinus Torvalds /* 3661da177e4SLinus Torvalds * Tx FIFO sizes: 3671da177e4SLinus Torvalds * CS4237B - 16 bytes 3681da177e4SLinus Torvalds * AudioDrive ES1688 - 12 bytes 3691da177e4SLinus Torvalds * S3 SonicVibes - 8 bytes 3701da177e4SLinus Torvalds * SoundBlaster AWE 64 - 2 bytes (ugly hardware) 3711da177e4SLinus Torvalds */ 3721da177e4SLinus Torvalds 3731da177e4SLinus Torvalds /* 3741da177e4SLinus Torvalds * write output pending bytes 3751da177e4SLinus Torvalds * call with output_lock spinlock held 3761da177e4SLinus Torvalds */ 377e1fad17bSTakashi Iwai static void snd_mpu401_uart_output_write(struct snd_mpu401 * mpu) 3781da177e4SLinus Torvalds { 3791da177e4SLinus Torvalds unsigned char byte; 3801da177e4SLinus Torvalds int max = 256, timeout; 3811da177e4SLinus Torvalds 3821da177e4SLinus Torvalds do { 3831da177e4SLinus Torvalds if (snd_rawmidi_transmit_peek(mpu->substream_output, &byte, 1) == 1) { 3841da177e4SLinus Torvalds for (timeout = 100; timeout > 0; timeout--) { 3851da177e4SLinus Torvalds if (snd_mpu401_output_ready(mpu)) { 3861da177e4SLinus Torvalds mpu->write(mpu, byte, MPU401D(mpu)); 3871da177e4SLinus Torvalds snd_rawmidi_transmit_ack(mpu->substream_output, 1); 3881da177e4SLinus Torvalds break; 3891da177e4SLinus Torvalds } 3901da177e4SLinus Torvalds } 3911da177e4SLinus Torvalds if (timeout == 0) 3921da177e4SLinus Torvalds break; /* Tx FIFO full - try again later */ 3931da177e4SLinus Torvalds } else { 3941da177e4SLinus Torvalds snd_mpu401_uart_remove_timer (mpu, 0); 3951da177e4SLinus Torvalds break; /* no other data - leave the tx loop */ 3961da177e4SLinus Torvalds } 3971da177e4SLinus Torvalds } while (--max > 0); 3981da177e4SLinus Torvalds } 3991da177e4SLinus Torvalds 4001da177e4SLinus Torvalds /* 4011da177e4SLinus Torvalds * output trigger callback 4021da177e4SLinus Torvalds */ 403e1fad17bSTakashi Iwai static void snd_mpu401_uart_output_trigger(struct snd_rawmidi_substream *substream, int up) 4041da177e4SLinus Torvalds { 4051da177e4SLinus Torvalds unsigned long flags; 406e1fad17bSTakashi Iwai struct snd_mpu401 *mpu; 4071da177e4SLinus Torvalds 4081da177e4SLinus Torvalds mpu = substream->rmidi->private_data; 4091da177e4SLinus Torvalds if (up) { 4101da177e4SLinus Torvalds set_bit(MPU401_MODE_BIT_OUTPUT_TRIGGER, &mpu->mode); 4111da177e4SLinus Torvalds 4121da177e4SLinus Torvalds /* try to add the timer at each output trigger, 4131da177e4SLinus Torvalds * since the output timer might have been removed in 4141da177e4SLinus Torvalds * snd_mpu401_uart_output_write(). 4151da177e4SLinus Torvalds */ 4161da177e4SLinus Torvalds snd_mpu401_uart_add_timer(mpu, 0); 4171da177e4SLinus Torvalds 4181da177e4SLinus Torvalds /* output pending data */ 4191da177e4SLinus Torvalds spin_lock_irqsave(&mpu->output_lock, flags); 4201da177e4SLinus Torvalds snd_mpu401_uart_output_write(mpu); 4211da177e4SLinus Torvalds spin_unlock_irqrestore(&mpu->output_lock, flags); 4221da177e4SLinus Torvalds } else { 4231da177e4SLinus Torvalds snd_mpu401_uart_remove_timer(mpu, 0); 4241da177e4SLinus Torvalds clear_bit(MPU401_MODE_BIT_OUTPUT_TRIGGER, &mpu->mode); 4251da177e4SLinus Torvalds } 4261da177e4SLinus Torvalds } 4271da177e4SLinus Torvalds 4281da177e4SLinus Torvalds /* 4291da177e4SLinus Torvalds 4301da177e4SLinus Torvalds */ 4311da177e4SLinus Torvalds 432e1fad17bSTakashi Iwai static struct snd_rawmidi_ops snd_mpu401_uart_output = 4331da177e4SLinus Torvalds { 4341da177e4SLinus Torvalds .open = snd_mpu401_uart_output_open, 4351da177e4SLinus Torvalds .close = snd_mpu401_uart_output_close, 4361da177e4SLinus Torvalds .trigger = snd_mpu401_uart_output_trigger, 4371da177e4SLinus Torvalds }; 4381da177e4SLinus Torvalds 439e1fad17bSTakashi Iwai static struct snd_rawmidi_ops snd_mpu401_uart_input = 4401da177e4SLinus Torvalds { 4411da177e4SLinus Torvalds .open = snd_mpu401_uart_input_open, 4421da177e4SLinus Torvalds .close = snd_mpu401_uart_input_close, 4431da177e4SLinus Torvalds .trigger = snd_mpu401_uart_input_trigger, 4441da177e4SLinus Torvalds }; 4451da177e4SLinus Torvalds 446e1fad17bSTakashi Iwai static void snd_mpu401_uart_free(struct snd_rawmidi *rmidi) 4471da177e4SLinus Torvalds { 448e1fad17bSTakashi Iwai struct snd_mpu401 *mpu = rmidi->private_data; 4491da177e4SLinus Torvalds if (mpu->irq_flags && mpu->irq >= 0) 4501da177e4SLinus Torvalds free_irq(mpu->irq, (void *) mpu); 451b1d5776dSTakashi Iwai release_and_free_resource(mpu->res); 4521da177e4SLinus Torvalds kfree(mpu); 4531da177e4SLinus Torvalds } 4541da177e4SLinus Torvalds 4551da177e4SLinus Torvalds /** 4561da177e4SLinus Torvalds * snd_mpu401_uart_new - create an MPU401-UART instance 4571da177e4SLinus Torvalds * @card: the card instance 4581da177e4SLinus Torvalds * @device: the device index, zero-based 4591da177e4SLinus Torvalds * @hardware: the hardware type, MPU401_HW_XXXX 4601da177e4SLinus Torvalds * @port: the base address of MPU401 port 4611da177e4SLinus Torvalds * @integrated: non-zero if the port was already reserved by the chip 4621da177e4SLinus Torvalds * @irq: the irq number, -1 if no interrupt for mpu 4631da177e4SLinus Torvalds * @irq_flags: the irq request flags (SA_XXX), 0 if irq was already reserved. 4641da177e4SLinus Torvalds * @rrawmidi: the pointer to store the new rawmidi instance 4651da177e4SLinus Torvalds * 4661da177e4SLinus Torvalds * Creates a new MPU-401 instance. 4671da177e4SLinus Torvalds * 4681da177e4SLinus Torvalds * Note that the rawmidi instance is returned on the rrawmidi argument, 4691da177e4SLinus Torvalds * not the mpu401 instance itself. To access to the mpu401 instance, 470e1fad17bSTakashi Iwai * cast from rawmidi->private_data (with struct snd_mpu401 magic-cast). 4711da177e4SLinus Torvalds * 4721da177e4SLinus Torvalds * Returns zero if successful, or a negative error code. 4731da177e4SLinus Torvalds */ 474e1fad17bSTakashi Iwai int snd_mpu401_uart_new(struct snd_card *card, int device, 4751da177e4SLinus Torvalds unsigned short hardware, 4761da177e4SLinus Torvalds unsigned long port, int integrated, 4771da177e4SLinus Torvalds int irq, int irq_flags, 478e1fad17bSTakashi Iwai struct snd_rawmidi ** rrawmidi) 4791da177e4SLinus Torvalds { 480e1fad17bSTakashi Iwai struct snd_mpu401 *mpu; 481e1fad17bSTakashi Iwai struct snd_rawmidi *rmidi; 4821da177e4SLinus Torvalds int err; 4831da177e4SLinus Torvalds 4841da177e4SLinus Torvalds if (rrawmidi) 4851da177e4SLinus Torvalds *rrawmidi = NULL; 4861da177e4SLinus Torvalds if ((err = snd_rawmidi_new(card, "MPU-401U", device, 1, 1, &rmidi)) < 0) 4871da177e4SLinus Torvalds return err; 488561b220aSTakashi Iwai mpu = kzalloc(sizeof(*mpu), GFP_KERNEL); 4891da177e4SLinus Torvalds if (mpu == NULL) { 49073e77ba0STakashi Iwai snd_printk(KERN_ERR "mpu401_uart: cannot allocate\n"); 4911da177e4SLinus Torvalds snd_device_free(card, rmidi); 4921da177e4SLinus Torvalds return -ENOMEM; 4931da177e4SLinus Torvalds } 4941da177e4SLinus Torvalds rmidi->private_data = mpu; 4951da177e4SLinus Torvalds rmidi->private_free = snd_mpu401_uart_free; 4961da177e4SLinus Torvalds spin_lock_init(&mpu->input_lock); 4971da177e4SLinus Torvalds spin_lock_init(&mpu->output_lock); 4981da177e4SLinus Torvalds spin_lock_init(&mpu->timer_lock); 4991da177e4SLinus Torvalds mpu->hardware = hardware; 5001da177e4SLinus Torvalds if (!integrated) { 5011da177e4SLinus Torvalds int res_size = hardware == MPU401_HW_PC98II ? 4 : 2; 5021da177e4SLinus Torvalds if ((mpu->res = request_region(port, res_size, "MPU401 UART")) == NULL) { 5031da177e4SLinus Torvalds snd_printk(KERN_ERR "mpu401_uart: unable to grab port 0x%lx size %d\n", port, res_size); 5041da177e4SLinus Torvalds snd_device_free(card, rmidi); 5051da177e4SLinus Torvalds return -EBUSY; 5061da177e4SLinus Torvalds } 5071da177e4SLinus Torvalds } 5081da177e4SLinus Torvalds switch (hardware) { 5091da177e4SLinus Torvalds case MPU401_HW_AUREAL: 5101da177e4SLinus Torvalds mpu->write = mpu401_write_mmio; 5111da177e4SLinus Torvalds mpu->read = mpu401_read_mmio; 5121da177e4SLinus Torvalds break; 5131da177e4SLinus Torvalds default: 5141da177e4SLinus Torvalds mpu->write = mpu401_write_port; 5151da177e4SLinus Torvalds mpu->read = mpu401_read_port; 5161da177e4SLinus Torvalds break; 5171da177e4SLinus Torvalds } 5181da177e4SLinus Torvalds mpu->port = port; 5191da177e4SLinus Torvalds if (hardware == MPU401_HW_PC98II) 5201da177e4SLinus Torvalds mpu->cport = port + 2; 5211da177e4SLinus Torvalds else 5221da177e4SLinus Torvalds mpu->cport = port + 1; 5231da177e4SLinus Torvalds if (irq >= 0 && irq_flags) { 5241da177e4SLinus Torvalds if (request_irq(irq, snd_mpu401_uart_interrupt, irq_flags, "MPU401 UART", (void *) mpu)) { 5251da177e4SLinus Torvalds snd_printk(KERN_ERR "mpu401_uart: unable to grab IRQ %d\n", irq); 5261da177e4SLinus Torvalds snd_device_free(card, rmidi); 5271da177e4SLinus Torvalds return -EBUSY; 5281da177e4SLinus Torvalds } 5291da177e4SLinus Torvalds } 5301da177e4SLinus Torvalds mpu->irq = irq; 5311da177e4SLinus Torvalds mpu->irq_flags = irq_flags; 5321da177e4SLinus Torvalds if (card->shortname[0]) 5331da177e4SLinus Torvalds snprintf(rmidi->name, sizeof(rmidi->name), "%s MIDI", card->shortname); 5341da177e4SLinus Torvalds else 5351da177e4SLinus Torvalds sprintf(rmidi->name, "MPU-401 MIDI %d-%d", card->number, device); 5361da177e4SLinus Torvalds snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_mpu401_uart_output); 5371da177e4SLinus Torvalds snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_mpu401_uart_input); 5381da177e4SLinus Torvalds rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT | 5391da177e4SLinus Torvalds SNDRV_RAWMIDI_INFO_INPUT | 5401da177e4SLinus Torvalds SNDRV_RAWMIDI_INFO_DUPLEX; 5411da177e4SLinus Torvalds mpu->rmidi = rmidi; 5421da177e4SLinus Torvalds if (rrawmidi) 5431da177e4SLinus Torvalds *rrawmidi = rmidi; 5441da177e4SLinus Torvalds return 0; 5451da177e4SLinus Torvalds } 5461da177e4SLinus Torvalds 5471da177e4SLinus Torvalds EXPORT_SYMBOL(snd_mpu401_uart_interrupt); 5481da177e4SLinus Torvalds EXPORT_SYMBOL(snd_mpu401_uart_new); 5491da177e4SLinus Torvalds 5501da177e4SLinus Torvalds /* 5511da177e4SLinus Torvalds * INIT part 5521da177e4SLinus Torvalds */ 5531da177e4SLinus Torvalds 5541da177e4SLinus Torvalds static int __init alsa_mpu401_uart_init(void) 5551da177e4SLinus Torvalds { 5561da177e4SLinus Torvalds return 0; 5571da177e4SLinus Torvalds } 5581da177e4SLinus Torvalds 5591da177e4SLinus Torvalds static void __exit alsa_mpu401_uart_exit(void) 5601da177e4SLinus Torvalds { 5611da177e4SLinus Torvalds } 5621da177e4SLinus Torvalds 5631da177e4SLinus Torvalds module_init(alsa_mpu401_uart_init) 5641da177e4SLinus Torvalds module_exit(alsa_mpu401_uart_exit) 565