11a59d1b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later 21da177e4SLinus Torvalds /* 3c1017a4cSJaroslav Kysela * Copyright (c) by Jaroslav Kysela <perex@perex.cz> 41da177e4SLinus Torvalds * Routines for control of MPU-401 in UART mode 51da177e4SLinus Torvalds * 61da177e4SLinus Torvalds * MPU-401 supports UART mode which is not capable generate transmit 7dba8b469SClemens Ladisch * interrupts thus output is done via polling. Without interrupt, 81da177e4SLinus Torvalds * input is done also via polling. Do not expect good performance. 91da177e4SLinus Torvalds * 101da177e4SLinus Torvalds * 13-03-2003: 111da177e4SLinus Torvalds * Added support for different kind of hardware I/O. Build in choices 121da177e4SLinus Torvalds * are port and mmio. For other kind of I/O, set mpu->read and 131da177e4SLinus Torvalds * mpu->write to your own I/O functions. 141da177e4SLinus Torvalds */ 151da177e4SLinus Torvalds 166cbbfe1cSTakashi Iwai #include <linux/io.h> 171da177e4SLinus Torvalds #include <linux/delay.h> 181da177e4SLinus Torvalds #include <linux/init.h> 191da177e4SLinus Torvalds #include <linux/slab.h> 201da177e4SLinus Torvalds #include <linux/ioport.h> 21da155d5bSPaul Gortmaker #include <linux/module.h> 221da177e4SLinus Torvalds #include <linux/interrupt.h> 231da177e4SLinus Torvalds #include <linux/errno.h> 241da177e4SLinus Torvalds #include <sound/core.h> 251da177e4SLinus Torvalds #include <sound/mpu401.h> 261da177e4SLinus Torvalds 27c1017a4cSJaroslav Kysela MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>"); 281da177e4SLinus Torvalds MODULE_DESCRIPTION("Routines for control of MPU-401 in UART mode"); 291da177e4SLinus Torvalds MODULE_LICENSE("GPL"); 301da177e4SLinus Torvalds 31e1fad17bSTakashi Iwai static void snd_mpu401_uart_input_read(struct snd_mpu401 * mpu); 32e1fad17bSTakashi Iwai static void snd_mpu401_uart_output_write(struct snd_mpu401 * mpu); 331da177e4SLinus Torvalds 341da177e4SLinus Torvalds /* 351da177e4SLinus Torvalds 361da177e4SLinus Torvalds */ 371da177e4SLinus Torvalds 38b415ed45STakashi Iwai #define snd_mpu401_input_avail(mpu) \ 39b415ed45STakashi Iwai (!(mpu->read(mpu, MPU401C(mpu)) & MPU401_RX_EMPTY)) 40b415ed45STakashi Iwai #define snd_mpu401_output_ready(mpu) \ 41b415ed45STakashi Iwai (!(mpu->read(mpu, MPU401C(mpu)) & MPU401_TX_FULL)) 421da177e4SLinus Torvalds 431da177e4SLinus Torvalds /* Build in lowlevel io */ 442851d963STakashi Iwai static void mpu401_write_port(struct snd_mpu401 *mpu, unsigned char data, 452851d963STakashi Iwai unsigned long addr) 461da177e4SLinus Torvalds { 471da177e4SLinus Torvalds outb(data, addr); 481da177e4SLinus Torvalds } 491da177e4SLinus Torvalds 502851d963STakashi Iwai static unsigned char mpu401_read_port(struct snd_mpu401 *mpu, 512851d963STakashi Iwai unsigned long addr) 521da177e4SLinus Torvalds { 531da177e4SLinus Torvalds return inb(addr); 541da177e4SLinus Torvalds } 551da177e4SLinus Torvalds 562851d963STakashi Iwai static void mpu401_write_mmio(struct snd_mpu401 *mpu, unsigned char data, 572851d963STakashi Iwai unsigned long addr) 581da177e4SLinus Torvalds { 591da177e4SLinus Torvalds writeb(data, (void __iomem *)addr); 601da177e4SLinus Torvalds } 611da177e4SLinus Torvalds 622851d963STakashi Iwai static unsigned char mpu401_read_mmio(struct snd_mpu401 *mpu, 632851d963STakashi Iwai unsigned long addr) 641da177e4SLinus Torvalds { 651da177e4SLinus Torvalds return readb((void __iomem *)addr); 661da177e4SLinus Torvalds } 671da177e4SLinus Torvalds /* */ 681da177e4SLinus Torvalds 69e1fad17bSTakashi Iwai static void snd_mpu401_uart_clear_rx(struct snd_mpu401 *mpu) 701da177e4SLinus Torvalds { 711da177e4SLinus Torvalds int timeout = 100000; 721da177e4SLinus Torvalds for (; timeout > 0 && snd_mpu401_input_avail(mpu); timeout--) 731da177e4SLinus Torvalds mpu->read(mpu, MPU401D(mpu)); 741da177e4SLinus Torvalds #ifdef CONFIG_SND_DEBUG 751da177e4SLinus Torvalds if (timeout <= 0) 762851d963STakashi Iwai snd_printk(KERN_ERR "cmd: clear rx timeout (status = 0x%x)\n", 772851d963STakashi Iwai mpu->read(mpu, MPU401C(mpu))); 781da177e4SLinus Torvalds #endif 791da177e4SLinus Torvalds } 801da177e4SLinus Torvalds 81302e4c2fSTakashi Iwai static void uart_interrupt_tx(struct snd_mpu401 *mpu) 821da177e4SLinus Torvalds { 8360fac85fSClemens Ladisch unsigned long flags; 8460fac85fSClemens Ladisch 851da177e4SLinus Torvalds if (test_bit(MPU401_MODE_BIT_OUTPUT, &mpu->mode) && 861da177e4SLinus Torvalds test_bit(MPU401_MODE_BIT_OUTPUT_TRIGGER, &mpu->mode)) { 8760fac85fSClemens Ladisch spin_lock_irqsave(&mpu->output_lock, flags); 881da177e4SLinus Torvalds snd_mpu401_uart_output_write(mpu); 8960fac85fSClemens Ladisch spin_unlock_irqrestore(&mpu->output_lock, flags); 901da177e4SLinus Torvalds } 911da177e4SLinus Torvalds } 921da177e4SLinus Torvalds 93302e4c2fSTakashi Iwai static void _snd_mpu401_uart_interrupt(struct snd_mpu401 *mpu) 94302e4c2fSTakashi Iwai { 9560fac85fSClemens Ladisch unsigned long flags; 9660fac85fSClemens Ladisch 97302e4c2fSTakashi Iwai if (mpu->info_flags & MPU401_INFO_INPUT) { 9860fac85fSClemens Ladisch spin_lock_irqsave(&mpu->input_lock, flags); 99302e4c2fSTakashi Iwai if (test_bit(MPU401_MODE_BIT_INPUT, &mpu->mode)) 100302e4c2fSTakashi Iwai snd_mpu401_uart_input_read(mpu); 101302e4c2fSTakashi Iwai else 102302e4c2fSTakashi Iwai snd_mpu401_uart_clear_rx(mpu); 10360fac85fSClemens Ladisch spin_unlock_irqrestore(&mpu->input_lock, flags); 104302e4c2fSTakashi Iwai } 105302e4c2fSTakashi Iwai if (! (mpu->info_flags & MPU401_INFO_TX_IRQ)) 106302e4c2fSTakashi Iwai /* ok. for better Tx performance try do some output 107302e4c2fSTakashi Iwai when input is done */ 108302e4c2fSTakashi Iwai uart_interrupt_tx(mpu); 109302e4c2fSTakashi Iwai } 110302e4c2fSTakashi Iwai 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 * 1161da177e4SLinus Torvalds * Processes the interrupt for MPU401-UART i/o. 117eb7c06e8SYacine Belkadi * 118eb7c06e8SYacine Belkadi * Return: %IRQ_HANDLED if the interrupt was handled. %IRQ_NONE otherwise. 1191da177e4SLinus Torvalds */ 1207d12e780SDavid Howells irqreturn_t snd_mpu401_uart_interrupt(int irq, void *dev_id) 1211da177e4SLinus Torvalds { 122e1fad17bSTakashi Iwai struct snd_mpu401 *mpu = dev_id; 1231da177e4SLinus Torvalds 124f1a77dc1SMarkus Elfring if (!mpu) 1251da177e4SLinus Torvalds return IRQ_NONE; 1261da177e4SLinus Torvalds _snd_mpu401_uart_interrupt(mpu); 1271da177e4SLinus Torvalds return IRQ_HANDLED; 1281da177e4SLinus Torvalds } 1291da177e4SLinus Torvalds 1302851d963STakashi Iwai EXPORT_SYMBOL(snd_mpu401_uart_interrupt); 1312851d963STakashi Iwai 132302e4c2fSTakashi Iwai /** 133302e4c2fSTakashi Iwai * snd_mpu401_uart_interrupt_tx - generic MPU401-UART transmit irq handler 134302e4c2fSTakashi Iwai * @irq: the irq number 135302e4c2fSTakashi Iwai * @dev_id: mpu401 instance 136302e4c2fSTakashi Iwai * 137302e4c2fSTakashi Iwai * Processes the interrupt for MPU401-UART output. 138eb7c06e8SYacine Belkadi * 139eb7c06e8SYacine Belkadi * Return: %IRQ_HANDLED if the interrupt was handled. %IRQ_NONE otherwise. 140302e4c2fSTakashi Iwai */ 1417d12e780SDavid Howells irqreturn_t snd_mpu401_uart_interrupt_tx(int irq, void *dev_id) 142302e4c2fSTakashi Iwai { 143302e4c2fSTakashi Iwai struct snd_mpu401 *mpu = dev_id; 144302e4c2fSTakashi Iwai 145f1a77dc1SMarkus Elfring if (!mpu) 146302e4c2fSTakashi Iwai return IRQ_NONE; 147302e4c2fSTakashi Iwai uart_interrupt_tx(mpu); 148302e4c2fSTakashi Iwai return IRQ_HANDLED; 149302e4c2fSTakashi Iwai } 150302e4c2fSTakashi Iwai 151302e4c2fSTakashi Iwai EXPORT_SYMBOL(snd_mpu401_uart_interrupt_tx); 152302e4c2fSTakashi Iwai 1531da177e4SLinus Torvalds /* 1541da177e4SLinus Torvalds * timer callback 1551da177e4SLinus Torvalds * reprogram the timer and call the interrupt job 1561da177e4SLinus Torvalds */ 157bc47ba90SKees Cook static void snd_mpu401_uart_timer(struct timer_list *t) 1581da177e4SLinus Torvalds { 159bc47ba90SKees Cook struct snd_mpu401 *mpu = from_timer(mpu, t, timer); 160b32425acSTakashi Iwai unsigned long flags; 1611da177e4SLinus Torvalds 162b32425acSTakashi Iwai spin_lock_irqsave(&mpu->timer_lock, flags); 1631da177e4SLinus Torvalds /*mpu->mode |= MPU401_MODE_TIMER;*/ 164b093ed23STakashi Iwai mod_timer(&mpu->timer, 1 + jiffies); 165b32425acSTakashi Iwai spin_unlock_irqrestore(&mpu->timer_lock, flags); 1661da177e4SLinus Torvalds if (mpu->rmidi) 1671da177e4SLinus Torvalds _snd_mpu401_uart_interrupt(mpu); 1681da177e4SLinus Torvalds } 1691da177e4SLinus Torvalds 1701da177e4SLinus Torvalds /* 1711da177e4SLinus Torvalds * initialize the timer callback if not programmed yet 1721da177e4SLinus Torvalds */ 173e1fad17bSTakashi Iwai static void snd_mpu401_uart_add_timer (struct snd_mpu401 *mpu, int input) 1741da177e4SLinus Torvalds { 1751da177e4SLinus Torvalds unsigned long flags; 1761da177e4SLinus Torvalds 1771da177e4SLinus Torvalds spin_lock_irqsave (&mpu->timer_lock, flags); 1781da177e4SLinus Torvalds if (mpu->timer_invoked == 0) { 179bc47ba90SKees Cook timer_setup(&mpu->timer, snd_mpu401_uart_timer, 0); 180b093ed23STakashi Iwai mod_timer(&mpu->timer, 1 + jiffies); 1811da177e4SLinus Torvalds } 1822851d963STakashi Iwai mpu->timer_invoked |= input ? MPU401_MODE_INPUT_TIMER : 1832851d963STakashi Iwai MPU401_MODE_OUTPUT_TIMER; 1841da177e4SLinus Torvalds spin_unlock_irqrestore (&mpu->timer_lock, flags); 1851da177e4SLinus Torvalds } 1861da177e4SLinus Torvalds 1871da177e4SLinus Torvalds /* 1881da177e4SLinus Torvalds * remove the timer callback if still active 1891da177e4SLinus Torvalds */ 190e1fad17bSTakashi Iwai static void snd_mpu401_uart_remove_timer (struct snd_mpu401 *mpu, int input) 1911da177e4SLinus Torvalds { 1921da177e4SLinus Torvalds unsigned long flags; 1931da177e4SLinus Torvalds 1941da177e4SLinus Torvalds spin_lock_irqsave (&mpu->timer_lock, flags); 1951da177e4SLinus Torvalds if (mpu->timer_invoked) { 1962851d963STakashi Iwai mpu->timer_invoked &= input ? ~MPU401_MODE_INPUT_TIMER : 1972851d963STakashi Iwai ~MPU401_MODE_OUTPUT_TIMER; 1981da177e4SLinus Torvalds if (! mpu->timer_invoked) 1991da177e4SLinus Torvalds del_timer(&mpu->timer); 2001da177e4SLinus Torvalds } 2011da177e4SLinus Torvalds spin_unlock_irqrestore (&mpu->timer_lock, flags); 2021da177e4SLinus Torvalds } 2031da177e4SLinus Torvalds 2041da177e4SLinus Torvalds /* 2052851d963STakashi Iwai * send a UART command 2062851d963STakashi Iwai * return zero if successful, non-zero for some errors 2071da177e4SLinus Torvalds */ 2081da177e4SLinus Torvalds 209962f831fSJon Masters static int snd_mpu401_uart_cmd(struct snd_mpu401 * mpu, unsigned char cmd, 210962f831fSJon Masters int ack) 2111da177e4SLinus Torvalds { 2121da177e4SLinus Torvalds unsigned long flags; 2131da177e4SLinus Torvalds int timeout, ok; 2141da177e4SLinus Torvalds 2151da177e4SLinus Torvalds spin_lock_irqsave(&mpu->input_lock, flags); 2161da177e4SLinus Torvalds if (mpu->hardware != MPU401_HW_TRID4DWAVE) { 2171da177e4SLinus Torvalds mpu->write(mpu, 0x00, MPU401D(mpu)); 2181da177e4SLinus Torvalds /*snd_mpu401_uart_clear_rx(mpu);*/ 2191da177e4SLinus Torvalds } 2201da177e4SLinus Torvalds /* ok. standard MPU-401 initialization */ 2211da177e4SLinus Torvalds if (mpu->hardware != MPU401_HW_SB) { 2222851d963STakashi Iwai for (timeout = 1000; timeout > 0 && 2232851d963STakashi Iwai !snd_mpu401_output_ready(mpu); timeout--) 2241da177e4SLinus Torvalds udelay(10); 2251da177e4SLinus Torvalds #ifdef CONFIG_SND_DEBUG 2261da177e4SLinus Torvalds if (!timeout) 2272851d963STakashi Iwai snd_printk(KERN_ERR "cmd: tx timeout (status = 0x%x)\n", 2282851d963STakashi Iwai mpu->read(mpu, MPU401C(mpu))); 2291da177e4SLinus Torvalds #endif 2301da177e4SLinus Torvalds } 2311da177e4SLinus Torvalds mpu->write(mpu, cmd, MPU401C(mpu)); 232df7e3fdfSTakashi Iwai if (ack && !(mpu->info_flags & MPU401_INFO_NO_ACK)) { 2331da177e4SLinus Torvalds ok = 0; 2341da177e4SLinus Torvalds timeout = 10000; 2351da177e4SLinus Torvalds while (!ok && timeout-- > 0) { 2361da177e4SLinus Torvalds if (snd_mpu401_input_avail(mpu)) { 2371da177e4SLinus Torvalds if (mpu->read(mpu, MPU401D(mpu)) == MPU401_ACK) 2381da177e4SLinus Torvalds ok = 1; 2391da177e4SLinus Torvalds } 2401da177e4SLinus Torvalds } 2411da177e4SLinus Torvalds if (!ok && mpu->read(mpu, MPU401D(mpu)) == MPU401_ACK) 2421da177e4SLinus Torvalds ok = 1; 2432851d963STakashi Iwai } else 2441da177e4SLinus Torvalds ok = 1; 2451da177e4SLinus Torvalds spin_unlock_irqrestore(&mpu->input_lock, flags); 246962f831fSJon Masters if (!ok) { 2472851d963STakashi Iwai snd_printk(KERN_ERR "cmd: 0x%x failed at 0x%lx " 2482851d963STakashi Iwai "(status = 0x%x, data = 0x%x)\n", cmd, mpu->port, 2492851d963STakashi Iwai mpu->read(mpu, MPU401C(mpu)), 2502851d963STakashi Iwai mpu->read(mpu, MPU401D(mpu))); 251962f831fSJon Masters return 1; 252962f831fSJon Masters } 253962f831fSJon Masters return 0; 2541da177e4SLinus Torvalds } 2551da177e4SLinus Torvalds 2568f7ba051STakashi Iwai static int snd_mpu401_do_reset(struct snd_mpu401 *mpu) 2578f7ba051STakashi Iwai { 2588f7ba051STakashi Iwai if (snd_mpu401_uart_cmd(mpu, MPU401_RESET, 1)) 2598f7ba051STakashi Iwai return -EIO; 260c1099fcbSClemens Ladisch if (snd_mpu401_uart_cmd(mpu, MPU401_ENTER_UART, 0)) 2618f7ba051STakashi Iwai return -EIO; 2628f7ba051STakashi Iwai return 0; 2638f7ba051STakashi Iwai } 2648f7ba051STakashi Iwai 2651da177e4SLinus Torvalds /* 2661da177e4SLinus Torvalds * input/output open/close - protected by open_mutex in rawmidi.c 2671da177e4SLinus Torvalds */ 268e1fad17bSTakashi Iwai static int snd_mpu401_uart_input_open(struct snd_rawmidi_substream *substream) 2691da177e4SLinus Torvalds { 270e1fad17bSTakashi Iwai struct snd_mpu401 *mpu; 2711da177e4SLinus Torvalds int err; 2721da177e4SLinus Torvalds 2731da177e4SLinus Torvalds mpu = substream->rmidi->private_data; 2741da177e4SLinus Torvalds if (mpu->open_input && (err = mpu->open_input(mpu)) < 0) 2751da177e4SLinus Torvalds return err; 2761da177e4SLinus Torvalds if (! test_bit(MPU401_MODE_BIT_OUTPUT, &mpu->mode)) { 2778f7ba051STakashi Iwai if (snd_mpu401_do_reset(mpu) < 0) 278962f831fSJon Masters goto error_out; 2791da177e4SLinus Torvalds } 2801da177e4SLinus Torvalds mpu->substream_input = substream; 2811da177e4SLinus Torvalds set_bit(MPU401_MODE_BIT_INPUT, &mpu->mode); 2821da177e4SLinus Torvalds return 0; 283962f831fSJon Masters 284962f831fSJon Masters error_out: 285962f831fSJon Masters if (mpu->open_input && mpu->close_input) 286962f831fSJon Masters mpu->close_input(mpu); 287962f831fSJon Masters return -EIO; 2881da177e4SLinus Torvalds } 2891da177e4SLinus Torvalds 290e1fad17bSTakashi Iwai static int snd_mpu401_uart_output_open(struct snd_rawmidi_substream *substream) 2911da177e4SLinus Torvalds { 292e1fad17bSTakashi Iwai struct snd_mpu401 *mpu; 2931da177e4SLinus Torvalds int err; 2941da177e4SLinus Torvalds 2951da177e4SLinus Torvalds mpu = substream->rmidi->private_data; 2961da177e4SLinus Torvalds if (mpu->open_output && (err = mpu->open_output(mpu)) < 0) 2971da177e4SLinus Torvalds return err; 2981da177e4SLinus Torvalds if (! test_bit(MPU401_MODE_BIT_INPUT, &mpu->mode)) { 2998f7ba051STakashi Iwai if (snd_mpu401_do_reset(mpu) < 0) 300962f831fSJon Masters goto error_out; 3011da177e4SLinus Torvalds } 3021da177e4SLinus Torvalds mpu->substream_output = substream; 3031da177e4SLinus Torvalds set_bit(MPU401_MODE_BIT_OUTPUT, &mpu->mode); 3041da177e4SLinus Torvalds return 0; 305962f831fSJon Masters 306962f831fSJon Masters error_out: 307962f831fSJon Masters if (mpu->open_output && mpu->close_output) 308962f831fSJon Masters mpu->close_output(mpu); 309962f831fSJon Masters return -EIO; 3101da177e4SLinus Torvalds } 3111da177e4SLinus Torvalds 312e1fad17bSTakashi Iwai static int snd_mpu401_uart_input_close(struct snd_rawmidi_substream *substream) 3131da177e4SLinus Torvalds { 314e1fad17bSTakashi Iwai struct snd_mpu401 *mpu; 315962f831fSJon Masters int err = 0; 3161da177e4SLinus Torvalds 3171da177e4SLinus Torvalds mpu = substream->rmidi->private_data; 3181da177e4SLinus Torvalds clear_bit(MPU401_MODE_BIT_INPUT, &mpu->mode); 3191da177e4SLinus Torvalds mpu->substream_input = NULL; 3201da177e4SLinus Torvalds if (! test_bit(MPU401_MODE_BIT_OUTPUT, &mpu->mode)) 321962f831fSJon Masters err = snd_mpu401_uart_cmd(mpu, MPU401_RESET, 0); 3221da177e4SLinus Torvalds if (mpu->close_input) 3231da177e4SLinus Torvalds mpu->close_input(mpu); 324962f831fSJon Masters if (err) 325962f831fSJon Masters return -EIO; 3261da177e4SLinus Torvalds return 0; 3271da177e4SLinus Torvalds } 3281da177e4SLinus Torvalds 329e1fad17bSTakashi Iwai static int snd_mpu401_uart_output_close(struct snd_rawmidi_substream *substream) 3301da177e4SLinus Torvalds { 331e1fad17bSTakashi Iwai struct snd_mpu401 *mpu; 332962f831fSJon Masters int err = 0; 3331da177e4SLinus Torvalds 3341da177e4SLinus Torvalds mpu = substream->rmidi->private_data; 3351da177e4SLinus Torvalds clear_bit(MPU401_MODE_BIT_OUTPUT, &mpu->mode); 3361da177e4SLinus Torvalds mpu->substream_output = NULL; 3371da177e4SLinus Torvalds if (! test_bit(MPU401_MODE_BIT_INPUT, &mpu->mode)) 338962f831fSJon Masters err = snd_mpu401_uart_cmd(mpu, MPU401_RESET, 0); 3391da177e4SLinus Torvalds if (mpu->close_output) 3401da177e4SLinus Torvalds mpu->close_output(mpu); 341962f831fSJon Masters if (err) 342962f831fSJon Masters return -EIO; 3431da177e4SLinus Torvalds return 0; 3441da177e4SLinus Torvalds } 3451da177e4SLinus Torvalds 3461da177e4SLinus Torvalds /* 3471da177e4SLinus Torvalds * trigger input callback 3481da177e4SLinus Torvalds */ 3492851d963STakashi Iwai static void 3502851d963STakashi Iwai snd_mpu401_uart_input_trigger(struct snd_rawmidi_substream *substream, int up) 3511da177e4SLinus Torvalds { 3521da177e4SLinus Torvalds unsigned long flags; 353e1fad17bSTakashi Iwai struct snd_mpu401 *mpu; 3541da177e4SLinus Torvalds int max = 64; 3551da177e4SLinus Torvalds 3561da177e4SLinus Torvalds mpu = substream->rmidi->private_data; 3571da177e4SLinus Torvalds if (up) { 3582851d963STakashi Iwai if (! test_and_set_bit(MPU401_MODE_BIT_INPUT_TRIGGER, 3592851d963STakashi Iwai &mpu->mode)) { 3601da177e4SLinus Torvalds /* first time - flush FIFO */ 3611da177e4SLinus Torvalds while (max-- > 0) 3621da177e4SLinus Torvalds mpu->read(mpu, MPU401D(mpu)); 363dba8b469SClemens Ladisch if (mpu->info_flags & MPU401_INFO_USE_TIMER) 3641da177e4SLinus Torvalds snd_mpu401_uart_add_timer(mpu, 1); 3651da177e4SLinus Torvalds } 3661da177e4SLinus Torvalds 3671da177e4SLinus Torvalds /* read data in advance */ 3681da177e4SLinus Torvalds spin_lock_irqsave(&mpu->input_lock, flags); 3691da177e4SLinus Torvalds snd_mpu401_uart_input_read(mpu); 3701da177e4SLinus Torvalds spin_unlock_irqrestore(&mpu->input_lock, flags); 3711da177e4SLinus Torvalds } else { 372dba8b469SClemens Ladisch if (mpu->info_flags & MPU401_INFO_USE_TIMER) 3731da177e4SLinus Torvalds snd_mpu401_uart_remove_timer(mpu, 1); 3741da177e4SLinus Torvalds clear_bit(MPU401_MODE_BIT_INPUT_TRIGGER, &mpu->mode); 3751da177e4SLinus Torvalds } 376962f831fSJon Masters 3771da177e4SLinus Torvalds } 3781da177e4SLinus Torvalds 3791da177e4SLinus Torvalds /* 3801da177e4SLinus Torvalds * transfer input pending data 3811da177e4SLinus Torvalds * call with input_lock spinlock held 3821da177e4SLinus Torvalds */ 383e1fad17bSTakashi Iwai static void snd_mpu401_uart_input_read(struct snd_mpu401 * mpu) 3841da177e4SLinus Torvalds { 3851da177e4SLinus Torvalds int max = 128; 3861da177e4SLinus Torvalds unsigned char byte; 3871da177e4SLinus Torvalds 3881da177e4SLinus Torvalds while (max-- > 0) { 3892851d963STakashi Iwai if (! snd_mpu401_input_avail(mpu)) 3902851d963STakashi Iwai break; /* input not available */ 3911da177e4SLinus Torvalds byte = mpu->read(mpu, MPU401D(mpu)); 3921da177e4SLinus Torvalds if (test_bit(MPU401_MODE_BIT_INPUT_TRIGGER, &mpu->mode)) 3931da177e4SLinus Torvalds snd_rawmidi_receive(mpu->substream_input, &byte, 1); 3941da177e4SLinus Torvalds } 3951da177e4SLinus Torvalds } 3961da177e4SLinus Torvalds 3971da177e4SLinus Torvalds /* 3981da177e4SLinus Torvalds * Tx FIFO sizes: 3991da177e4SLinus Torvalds * CS4237B - 16 bytes 4001da177e4SLinus Torvalds * AudioDrive ES1688 - 12 bytes 4011da177e4SLinus Torvalds * S3 SonicVibes - 8 bytes 4021da177e4SLinus Torvalds * SoundBlaster AWE 64 - 2 bytes (ugly hardware) 4031da177e4SLinus Torvalds */ 4041da177e4SLinus Torvalds 4051da177e4SLinus Torvalds /* 4061da177e4SLinus Torvalds * write output pending bytes 4071da177e4SLinus Torvalds * call with output_lock spinlock held 4081da177e4SLinus Torvalds */ 409e1fad17bSTakashi Iwai static void snd_mpu401_uart_output_write(struct snd_mpu401 * mpu) 4101da177e4SLinus Torvalds { 4111da177e4SLinus Torvalds unsigned char byte; 412ea6b5828SClemens Ladisch int max = 256; 4131da177e4SLinus Torvalds 4141da177e4SLinus Torvalds do { 4152851d963STakashi Iwai if (snd_rawmidi_transmit_peek(mpu->substream_output, 4162851d963STakashi Iwai &byte, 1) == 1) { 417ea6b5828SClemens Ladisch /* 418ea6b5828SClemens Ladisch * Try twice because there is hardware that insists on 419ea6b5828SClemens Ladisch * setting the output busy bit after each write. 420ea6b5828SClemens Ladisch */ 421ea6b5828SClemens Ladisch if (!snd_mpu401_output_ready(mpu) && 422ea6b5828SClemens Ladisch !snd_mpu401_output_ready(mpu)) 4231da177e4SLinus Torvalds break; /* Tx FIFO full - try again later */ 4242851d963STakashi Iwai mpu->write(mpu, byte, MPU401D(mpu)); 4252851d963STakashi Iwai snd_rawmidi_transmit_ack(mpu->substream_output, 1); 4261da177e4SLinus Torvalds } else { 4271da177e4SLinus Torvalds snd_mpu401_uart_remove_timer (mpu, 0); 4281da177e4SLinus Torvalds break; /* no other data - leave the tx loop */ 4291da177e4SLinus Torvalds } 4301da177e4SLinus Torvalds } while (--max > 0); 4311da177e4SLinus Torvalds } 4321da177e4SLinus Torvalds 4331da177e4SLinus Torvalds /* 4341da177e4SLinus Torvalds * output trigger callback 4351da177e4SLinus Torvalds */ 4362851d963STakashi Iwai static void 4372851d963STakashi Iwai snd_mpu401_uart_output_trigger(struct snd_rawmidi_substream *substream, int up) 4381da177e4SLinus Torvalds { 4391da177e4SLinus Torvalds unsigned long flags; 440e1fad17bSTakashi Iwai struct snd_mpu401 *mpu; 4411da177e4SLinus Torvalds 4421da177e4SLinus Torvalds mpu = substream->rmidi->private_data; 4431da177e4SLinus Torvalds if (up) { 4441da177e4SLinus Torvalds set_bit(MPU401_MODE_BIT_OUTPUT_TRIGGER, &mpu->mode); 4451da177e4SLinus Torvalds 4461da177e4SLinus Torvalds /* try to add the timer at each output trigger, 4471da177e4SLinus Torvalds * since the output timer might have been removed in 4481da177e4SLinus Torvalds * snd_mpu401_uart_output_write(). 4491da177e4SLinus Torvalds */ 450302e4c2fSTakashi Iwai if (! (mpu->info_flags & MPU401_INFO_TX_IRQ)) 4511da177e4SLinus Torvalds snd_mpu401_uart_add_timer(mpu, 0); 4521da177e4SLinus Torvalds 4531da177e4SLinus Torvalds /* output pending data */ 4541da177e4SLinus Torvalds spin_lock_irqsave(&mpu->output_lock, flags); 4551da177e4SLinus Torvalds snd_mpu401_uart_output_write(mpu); 4561da177e4SLinus Torvalds spin_unlock_irqrestore(&mpu->output_lock, flags); 4571da177e4SLinus Torvalds } else { 458302e4c2fSTakashi Iwai if (! (mpu->info_flags & MPU401_INFO_TX_IRQ)) 4591da177e4SLinus Torvalds snd_mpu401_uart_remove_timer(mpu, 0); 4601da177e4SLinus Torvalds clear_bit(MPU401_MODE_BIT_OUTPUT_TRIGGER, &mpu->mode); 4611da177e4SLinus Torvalds } 4621da177e4SLinus Torvalds } 4631da177e4SLinus Torvalds 4641da177e4SLinus Torvalds /* 4651da177e4SLinus Torvalds 4661da177e4SLinus Torvalds */ 4671da177e4SLinus Torvalds 468c36f486dSTakashi Iwai static const struct snd_rawmidi_ops snd_mpu401_uart_output = 4691da177e4SLinus Torvalds { 4701da177e4SLinus Torvalds .open = snd_mpu401_uart_output_open, 4711da177e4SLinus Torvalds .close = snd_mpu401_uart_output_close, 4721da177e4SLinus Torvalds .trigger = snd_mpu401_uart_output_trigger, 4731da177e4SLinus Torvalds }; 4741da177e4SLinus Torvalds 475c36f486dSTakashi Iwai static const struct snd_rawmidi_ops snd_mpu401_uart_input = 4761da177e4SLinus Torvalds { 4771da177e4SLinus Torvalds .open = snd_mpu401_uart_input_open, 4781da177e4SLinus Torvalds .close = snd_mpu401_uart_input_close, 4791da177e4SLinus Torvalds .trigger = snd_mpu401_uart_input_trigger, 4801da177e4SLinus Torvalds }; 4811da177e4SLinus Torvalds 482e1fad17bSTakashi Iwai static void snd_mpu401_uart_free(struct snd_rawmidi *rmidi) 4831da177e4SLinus Torvalds { 484e1fad17bSTakashi Iwai struct snd_mpu401 *mpu = rmidi->private_data; 485dba8b469SClemens Ladisch if (mpu->irq >= 0) 4861da177e4SLinus Torvalds free_irq(mpu->irq, (void *) mpu); 487b1d5776dSTakashi Iwai release_and_free_resource(mpu->res); 4881da177e4SLinus Torvalds kfree(mpu); 4891da177e4SLinus Torvalds } 4901da177e4SLinus Torvalds 4911da177e4SLinus Torvalds /** 4921da177e4SLinus Torvalds * snd_mpu401_uart_new - create an MPU401-UART instance 4931da177e4SLinus Torvalds * @card: the card instance 4941da177e4SLinus Torvalds * @device: the device index, zero-based 4951da177e4SLinus Torvalds * @hardware: the hardware type, MPU401_HW_XXXX 4961da177e4SLinus Torvalds * @port: the base address of MPU401 port 497302e4c2fSTakashi Iwai * @info_flags: bitflags MPU401_INFO_XXX 498dba8b469SClemens Ladisch * @irq: the ISA irq number, -1 if not to be allocated 4991da177e4SLinus Torvalds * @rrawmidi: the pointer to store the new rawmidi instance 5001da177e4SLinus Torvalds * 5011da177e4SLinus Torvalds * Creates a new MPU-401 instance. 5021da177e4SLinus Torvalds * 5031da177e4SLinus Torvalds * Note that the rawmidi instance is returned on the rrawmidi argument, 5041da177e4SLinus Torvalds * not the mpu401 instance itself. To access to the mpu401 instance, 505e1fad17bSTakashi Iwai * cast from rawmidi->private_data (with struct snd_mpu401 magic-cast). 5061da177e4SLinus Torvalds * 507eb7c06e8SYacine Belkadi * Return: Zero if successful, or a negative error code. 5081da177e4SLinus Torvalds */ 509e1fad17bSTakashi Iwai int snd_mpu401_uart_new(struct snd_card *card, int device, 5101da177e4SLinus Torvalds unsigned short hardware, 511302e4c2fSTakashi Iwai unsigned long port, 512302e4c2fSTakashi Iwai unsigned int info_flags, 513dba8b469SClemens Ladisch int irq, 514e1fad17bSTakashi Iwai struct snd_rawmidi ** rrawmidi) 5151da177e4SLinus Torvalds { 516e1fad17bSTakashi Iwai struct snd_mpu401 *mpu; 517e1fad17bSTakashi Iwai struct snd_rawmidi *rmidi; 518302e4c2fSTakashi Iwai int in_enable, out_enable; 5191da177e4SLinus Torvalds int err; 5201da177e4SLinus Torvalds 5211da177e4SLinus Torvalds if (rrawmidi) 5221da177e4SLinus Torvalds *rrawmidi = NULL; 523302e4c2fSTakashi Iwai if (! (info_flags & (MPU401_INFO_INPUT | MPU401_INFO_OUTPUT))) 524302e4c2fSTakashi Iwai info_flags |= MPU401_INFO_INPUT | MPU401_INFO_OUTPUT; 525302e4c2fSTakashi Iwai in_enable = (info_flags & MPU401_INFO_INPUT) ? 1 : 0; 526302e4c2fSTakashi Iwai out_enable = (info_flags & MPU401_INFO_OUTPUT) ? 1 : 0; 527302e4c2fSTakashi Iwai if ((err = snd_rawmidi_new(card, "MPU-401U", device, 528302e4c2fSTakashi Iwai out_enable, in_enable, &rmidi)) < 0) 5291da177e4SLinus Torvalds return err; 530561b220aSTakashi Iwai mpu = kzalloc(sizeof(*mpu), GFP_KERNEL); 531f1a77dc1SMarkus Elfring if (!mpu) { 532ec1f43c8SMarkus Elfring err = -ENOMEM; 533ec1f43c8SMarkus Elfring goto free_device; 5341da177e4SLinus Torvalds } 5351da177e4SLinus Torvalds rmidi->private_data = mpu; 5361da177e4SLinus Torvalds rmidi->private_free = snd_mpu401_uart_free; 5371da177e4SLinus Torvalds spin_lock_init(&mpu->input_lock); 5381da177e4SLinus Torvalds spin_lock_init(&mpu->output_lock); 5391da177e4SLinus Torvalds spin_lock_init(&mpu->timer_lock); 5401da177e4SLinus Torvalds mpu->hardware = hardware; 541bc733d49STakashi Iwai mpu->irq = -1; 542302e4c2fSTakashi Iwai if (! (info_flags & MPU401_INFO_INTEGRATED)) { 5431da177e4SLinus Torvalds int res_size = hardware == MPU401_HW_PC98II ? 4 : 2; 5442851d963STakashi Iwai mpu->res = request_region(port, res_size, "MPU401 UART"); 545f1a77dc1SMarkus Elfring if (!mpu->res) { 5462851d963STakashi Iwai snd_printk(KERN_ERR "mpu401_uart: " 5472851d963STakashi Iwai "unable to grab port 0x%lx size %d\n", 5482851d963STakashi Iwai port, res_size); 549ec1f43c8SMarkus Elfring err = -EBUSY; 550ec1f43c8SMarkus Elfring goto free_device; 5511da177e4SLinus Torvalds } 5521da177e4SLinus Torvalds } 553302e4c2fSTakashi Iwai if (info_flags & MPU401_INFO_MMIO) { 5541da177e4SLinus Torvalds mpu->write = mpu401_write_mmio; 5551da177e4SLinus Torvalds mpu->read = mpu401_read_mmio; 556302e4c2fSTakashi Iwai } else { 5571da177e4SLinus Torvalds mpu->write = mpu401_write_port; 5581da177e4SLinus Torvalds mpu->read = mpu401_read_port; 5591da177e4SLinus Torvalds } 5601da177e4SLinus Torvalds mpu->port = port; 5611da177e4SLinus Torvalds if (hardware == MPU401_HW_PC98II) 5621da177e4SLinus Torvalds mpu->cport = port + 2; 5631da177e4SLinus Torvalds else 5641da177e4SLinus Torvalds mpu->cport = port + 1; 565dba8b469SClemens Ladisch if (irq >= 0) { 56688e24c3aSYong Zhang if (request_irq(irq, snd_mpu401_uart_interrupt, 0, 5672851d963STakashi Iwai "MPU401 UART", (void *) mpu)) { 5682851d963STakashi Iwai snd_printk(KERN_ERR "mpu401_uart: " 5692851d963STakashi Iwai "unable to grab IRQ %d\n", irq); 570ec1f43c8SMarkus Elfring err = -EBUSY; 571ec1f43c8SMarkus Elfring goto free_device; 5721da177e4SLinus Torvalds } 5731da177e4SLinus Torvalds } 574dba8b469SClemens Ladisch if (irq < 0 && !(info_flags & MPU401_INFO_IRQ_HOOK)) 575dba8b469SClemens Ladisch info_flags |= MPU401_INFO_USE_TIMER; 576302e4c2fSTakashi Iwai mpu->info_flags = info_flags; 5771da177e4SLinus Torvalds mpu->irq = irq; 5781da177e4SLinus Torvalds if (card->shortname[0]) 5792851d963STakashi Iwai snprintf(rmidi->name, sizeof(rmidi->name), "%s MIDI", 5802851d963STakashi Iwai card->shortname); 5811da177e4SLinus Torvalds else 5821da177e4SLinus Torvalds sprintf(rmidi->name, "MPU-401 MIDI %d-%d",card->number, device); 583302e4c2fSTakashi Iwai if (out_enable) { 5842851d963STakashi Iwai snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, 5852851d963STakashi Iwai &snd_mpu401_uart_output); 586302e4c2fSTakashi Iwai rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT; 587302e4c2fSTakashi Iwai } 588302e4c2fSTakashi Iwai if (in_enable) { 5892851d963STakashi Iwai snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, 5902851d963STakashi Iwai &snd_mpu401_uart_input); 591302e4c2fSTakashi Iwai rmidi->info_flags |= SNDRV_RAWMIDI_INFO_INPUT; 592302e4c2fSTakashi Iwai if (out_enable) 593302e4c2fSTakashi Iwai rmidi->info_flags |= SNDRV_RAWMIDI_INFO_DUPLEX; 594302e4c2fSTakashi Iwai } 5951da177e4SLinus Torvalds mpu->rmidi = rmidi; 5961da177e4SLinus Torvalds if (rrawmidi) 5971da177e4SLinus Torvalds *rrawmidi = rmidi; 5981da177e4SLinus Torvalds return 0; 599ec1f43c8SMarkus Elfring free_device: 600ec1f43c8SMarkus Elfring snd_device_free(card, rmidi); 601ec1f43c8SMarkus Elfring return err; 6021da177e4SLinus Torvalds } 6031da177e4SLinus Torvalds 6041da177e4SLinus Torvalds EXPORT_SYMBOL(snd_mpu401_uart_new); 605