162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (c) by Jaroslav Kysela <perex@perex.cz> 462306a36Sopenharmony_ci * Routines for control of MPU-401 in UART mode 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * MPU-401 supports UART mode which is not capable generate transmit 762306a36Sopenharmony_ci * interrupts thus output is done via polling. Without interrupt, 862306a36Sopenharmony_ci * input is done also via polling. Do not expect good performance. 962306a36Sopenharmony_ci * 1062306a36Sopenharmony_ci * 13-03-2003: 1162306a36Sopenharmony_ci * Added support for different kind of hardware I/O. Build in choices 1262306a36Sopenharmony_ci * are port and mmio. For other kind of I/O, set mpu->read and 1362306a36Sopenharmony_ci * mpu->write to your own I/O functions. 1462306a36Sopenharmony_ci */ 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#include <linux/io.h> 1762306a36Sopenharmony_ci#include <linux/delay.h> 1862306a36Sopenharmony_ci#include <linux/init.h> 1962306a36Sopenharmony_ci#include <linux/slab.h> 2062306a36Sopenharmony_ci#include <linux/ioport.h> 2162306a36Sopenharmony_ci#include <linux/module.h> 2262306a36Sopenharmony_ci#include <linux/interrupt.h> 2362306a36Sopenharmony_ci#include <linux/errno.h> 2462306a36Sopenharmony_ci#include <sound/core.h> 2562306a36Sopenharmony_ci#include <sound/mpu401.h> 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ciMODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>"); 2862306a36Sopenharmony_ciMODULE_DESCRIPTION("Routines for control of MPU-401 in UART mode"); 2962306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_cistatic void snd_mpu401_uart_input_read(struct snd_mpu401 * mpu); 3262306a36Sopenharmony_cistatic void snd_mpu401_uart_output_write(struct snd_mpu401 * mpu); 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci/* 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci */ 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci#define snd_mpu401_input_avail(mpu) \ 3962306a36Sopenharmony_ci (!(mpu->read(mpu, MPU401C(mpu)) & MPU401_RX_EMPTY)) 4062306a36Sopenharmony_ci#define snd_mpu401_output_ready(mpu) \ 4162306a36Sopenharmony_ci (!(mpu->read(mpu, MPU401C(mpu)) & MPU401_TX_FULL)) 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci/* Build in lowlevel io */ 4462306a36Sopenharmony_cistatic void mpu401_write_port(struct snd_mpu401 *mpu, unsigned char data, 4562306a36Sopenharmony_ci unsigned long addr) 4662306a36Sopenharmony_ci{ 4762306a36Sopenharmony_ci outb(data, addr); 4862306a36Sopenharmony_ci} 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_cistatic unsigned char mpu401_read_port(struct snd_mpu401 *mpu, 5162306a36Sopenharmony_ci unsigned long addr) 5262306a36Sopenharmony_ci{ 5362306a36Sopenharmony_ci return inb(addr); 5462306a36Sopenharmony_ci} 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_cistatic void mpu401_write_mmio(struct snd_mpu401 *mpu, unsigned char data, 5762306a36Sopenharmony_ci unsigned long addr) 5862306a36Sopenharmony_ci{ 5962306a36Sopenharmony_ci writeb(data, (void __iomem *)addr); 6062306a36Sopenharmony_ci} 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_cistatic unsigned char mpu401_read_mmio(struct snd_mpu401 *mpu, 6362306a36Sopenharmony_ci unsigned long addr) 6462306a36Sopenharmony_ci{ 6562306a36Sopenharmony_ci return readb((void __iomem *)addr); 6662306a36Sopenharmony_ci} 6762306a36Sopenharmony_ci/* */ 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_cistatic void snd_mpu401_uart_clear_rx(struct snd_mpu401 *mpu) 7062306a36Sopenharmony_ci{ 7162306a36Sopenharmony_ci int timeout = 100000; 7262306a36Sopenharmony_ci for (; timeout > 0 && snd_mpu401_input_avail(mpu); timeout--) 7362306a36Sopenharmony_ci mpu->read(mpu, MPU401D(mpu)); 7462306a36Sopenharmony_ci#ifdef CONFIG_SND_DEBUG 7562306a36Sopenharmony_ci if (timeout <= 0) 7662306a36Sopenharmony_ci snd_printk(KERN_ERR "cmd: clear rx timeout (status = 0x%x)\n", 7762306a36Sopenharmony_ci mpu->read(mpu, MPU401C(mpu))); 7862306a36Sopenharmony_ci#endif 7962306a36Sopenharmony_ci} 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_cistatic void uart_interrupt_tx(struct snd_mpu401 *mpu) 8262306a36Sopenharmony_ci{ 8362306a36Sopenharmony_ci unsigned long flags; 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci if (test_bit(MPU401_MODE_BIT_OUTPUT, &mpu->mode) && 8662306a36Sopenharmony_ci test_bit(MPU401_MODE_BIT_OUTPUT_TRIGGER, &mpu->mode)) { 8762306a36Sopenharmony_ci spin_lock_irqsave(&mpu->output_lock, flags); 8862306a36Sopenharmony_ci snd_mpu401_uart_output_write(mpu); 8962306a36Sopenharmony_ci spin_unlock_irqrestore(&mpu->output_lock, flags); 9062306a36Sopenharmony_ci } 9162306a36Sopenharmony_ci} 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_cistatic void _snd_mpu401_uart_interrupt(struct snd_mpu401 *mpu) 9462306a36Sopenharmony_ci{ 9562306a36Sopenharmony_ci unsigned long flags; 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci if (mpu->info_flags & MPU401_INFO_INPUT) { 9862306a36Sopenharmony_ci spin_lock_irqsave(&mpu->input_lock, flags); 9962306a36Sopenharmony_ci if (test_bit(MPU401_MODE_BIT_INPUT, &mpu->mode)) 10062306a36Sopenharmony_ci snd_mpu401_uart_input_read(mpu); 10162306a36Sopenharmony_ci else 10262306a36Sopenharmony_ci snd_mpu401_uart_clear_rx(mpu); 10362306a36Sopenharmony_ci spin_unlock_irqrestore(&mpu->input_lock, flags); 10462306a36Sopenharmony_ci } 10562306a36Sopenharmony_ci if (! (mpu->info_flags & MPU401_INFO_TX_IRQ)) 10662306a36Sopenharmony_ci /* ok. for better Tx performance try do some output 10762306a36Sopenharmony_ci when input is done */ 10862306a36Sopenharmony_ci uart_interrupt_tx(mpu); 10962306a36Sopenharmony_ci} 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci/** 11262306a36Sopenharmony_ci * snd_mpu401_uart_interrupt - generic MPU401-UART interrupt handler 11362306a36Sopenharmony_ci * @irq: the irq number 11462306a36Sopenharmony_ci * @dev_id: mpu401 instance 11562306a36Sopenharmony_ci * 11662306a36Sopenharmony_ci * Processes the interrupt for MPU401-UART i/o. 11762306a36Sopenharmony_ci * 11862306a36Sopenharmony_ci * Return: %IRQ_HANDLED if the interrupt was handled. %IRQ_NONE otherwise. 11962306a36Sopenharmony_ci */ 12062306a36Sopenharmony_ciirqreturn_t snd_mpu401_uart_interrupt(int irq, void *dev_id) 12162306a36Sopenharmony_ci{ 12262306a36Sopenharmony_ci struct snd_mpu401 *mpu = dev_id; 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci if (!mpu) 12562306a36Sopenharmony_ci return IRQ_NONE; 12662306a36Sopenharmony_ci _snd_mpu401_uart_interrupt(mpu); 12762306a36Sopenharmony_ci return IRQ_HANDLED; 12862306a36Sopenharmony_ci} 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ciEXPORT_SYMBOL(snd_mpu401_uart_interrupt); 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci/** 13362306a36Sopenharmony_ci * snd_mpu401_uart_interrupt_tx - generic MPU401-UART transmit irq handler 13462306a36Sopenharmony_ci * @irq: the irq number 13562306a36Sopenharmony_ci * @dev_id: mpu401 instance 13662306a36Sopenharmony_ci * 13762306a36Sopenharmony_ci * Processes the interrupt for MPU401-UART output. 13862306a36Sopenharmony_ci * 13962306a36Sopenharmony_ci * Return: %IRQ_HANDLED if the interrupt was handled. %IRQ_NONE otherwise. 14062306a36Sopenharmony_ci */ 14162306a36Sopenharmony_ciirqreturn_t snd_mpu401_uart_interrupt_tx(int irq, void *dev_id) 14262306a36Sopenharmony_ci{ 14362306a36Sopenharmony_ci struct snd_mpu401 *mpu = dev_id; 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci if (!mpu) 14662306a36Sopenharmony_ci return IRQ_NONE; 14762306a36Sopenharmony_ci uart_interrupt_tx(mpu); 14862306a36Sopenharmony_ci return IRQ_HANDLED; 14962306a36Sopenharmony_ci} 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ciEXPORT_SYMBOL(snd_mpu401_uart_interrupt_tx); 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci/* 15462306a36Sopenharmony_ci * timer callback 15562306a36Sopenharmony_ci * reprogram the timer and call the interrupt job 15662306a36Sopenharmony_ci */ 15762306a36Sopenharmony_cistatic void snd_mpu401_uart_timer(struct timer_list *t) 15862306a36Sopenharmony_ci{ 15962306a36Sopenharmony_ci struct snd_mpu401 *mpu = from_timer(mpu, t, timer); 16062306a36Sopenharmony_ci unsigned long flags; 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci spin_lock_irqsave(&mpu->timer_lock, flags); 16362306a36Sopenharmony_ci /*mpu->mode |= MPU401_MODE_TIMER;*/ 16462306a36Sopenharmony_ci mod_timer(&mpu->timer, 1 + jiffies); 16562306a36Sopenharmony_ci spin_unlock_irqrestore(&mpu->timer_lock, flags); 16662306a36Sopenharmony_ci if (mpu->rmidi) 16762306a36Sopenharmony_ci _snd_mpu401_uart_interrupt(mpu); 16862306a36Sopenharmony_ci} 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci/* 17162306a36Sopenharmony_ci * initialize the timer callback if not programmed yet 17262306a36Sopenharmony_ci */ 17362306a36Sopenharmony_cistatic void snd_mpu401_uart_add_timer (struct snd_mpu401 *mpu, int input) 17462306a36Sopenharmony_ci{ 17562306a36Sopenharmony_ci unsigned long flags; 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci spin_lock_irqsave (&mpu->timer_lock, flags); 17862306a36Sopenharmony_ci if (mpu->timer_invoked == 0) { 17962306a36Sopenharmony_ci timer_setup(&mpu->timer, snd_mpu401_uart_timer, 0); 18062306a36Sopenharmony_ci mod_timer(&mpu->timer, 1 + jiffies); 18162306a36Sopenharmony_ci } 18262306a36Sopenharmony_ci mpu->timer_invoked |= input ? MPU401_MODE_INPUT_TIMER : 18362306a36Sopenharmony_ci MPU401_MODE_OUTPUT_TIMER; 18462306a36Sopenharmony_ci spin_unlock_irqrestore (&mpu->timer_lock, flags); 18562306a36Sopenharmony_ci} 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci/* 18862306a36Sopenharmony_ci * remove the timer callback if still active 18962306a36Sopenharmony_ci */ 19062306a36Sopenharmony_cistatic void snd_mpu401_uart_remove_timer (struct snd_mpu401 *mpu, int input) 19162306a36Sopenharmony_ci{ 19262306a36Sopenharmony_ci unsigned long flags; 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci spin_lock_irqsave (&mpu->timer_lock, flags); 19562306a36Sopenharmony_ci if (mpu->timer_invoked) { 19662306a36Sopenharmony_ci mpu->timer_invoked &= input ? ~MPU401_MODE_INPUT_TIMER : 19762306a36Sopenharmony_ci ~MPU401_MODE_OUTPUT_TIMER; 19862306a36Sopenharmony_ci if (! mpu->timer_invoked) 19962306a36Sopenharmony_ci del_timer(&mpu->timer); 20062306a36Sopenharmony_ci } 20162306a36Sopenharmony_ci spin_unlock_irqrestore (&mpu->timer_lock, flags); 20262306a36Sopenharmony_ci} 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci/* 20562306a36Sopenharmony_ci * send a UART command 20662306a36Sopenharmony_ci * return zero if successful, non-zero for some errors 20762306a36Sopenharmony_ci */ 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_cistatic int snd_mpu401_uart_cmd(struct snd_mpu401 * mpu, unsigned char cmd, 21062306a36Sopenharmony_ci int ack) 21162306a36Sopenharmony_ci{ 21262306a36Sopenharmony_ci unsigned long flags; 21362306a36Sopenharmony_ci int timeout, ok; 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci spin_lock_irqsave(&mpu->input_lock, flags); 21662306a36Sopenharmony_ci if (mpu->hardware != MPU401_HW_TRID4DWAVE) { 21762306a36Sopenharmony_ci mpu->write(mpu, 0x00, MPU401D(mpu)); 21862306a36Sopenharmony_ci /*snd_mpu401_uart_clear_rx(mpu);*/ 21962306a36Sopenharmony_ci } 22062306a36Sopenharmony_ci /* ok. standard MPU-401 initialization */ 22162306a36Sopenharmony_ci if (mpu->hardware != MPU401_HW_SB) { 22262306a36Sopenharmony_ci for (timeout = 1000; timeout > 0 && 22362306a36Sopenharmony_ci !snd_mpu401_output_ready(mpu); timeout--) 22462306a36Sopenharmony_ci udelay(10); 22562306a36Sopenharmony_ci#ifdef CONFIG_SND_DEBUG 22662306a36Sopenharmony_ci if (!timeout) 22762306a36Sopenharmony_ci snd_printk(KERN_ERR "cmd: tx timeout (status = 0x%x)\n", 22862306a36Sopenharmony_ci mpu->read(mpu, MPU401C(mpu))); 22962306a36Sopenharmony_ci#endif 23062306a36Sopenharmony_ci } 23162306a36Sopenharmony_ci mpu->write(mpu, cmd, MPU401C(mpu)); 23262306a36Sopenharmony_ci if (ack && !(mpu->info_flags & MPU401_INFO_NO_ACK)) { 23362306a36Sopenharmony_ci ok = 0; 23462306a36Sopenharmony_ci timeout = 10000; 23562306a36Sopenharmony_ci while (!ok && timeout-- > 0) { 23662306a36Sopenharmony_ci if (snd_mpu401_input_avail(mpu)) { 23762306a36Sopenharmony_ci if (mpu->read(mpu, MPU401D(mpu)) == MPU401_ACK) 23862306a36Sopenharmony_ci ok = 1; 23962306a36Sopenharmony_ci } 24062306a36Sopenharmony_ci } 24162306a36Sopenharmony_ci if (!ok && mpu->read(mpu, MPU401D(mpu)) == MPU401_ACK) 24262306a36Sopenharmony_ci ok = 1; 24362306a36Sopenharmony_ci } else 24462306a36Sopenharmony_ci ok = 1; 24562306a36Sopenharmony_ci spin_unlock_irqrestore(&mpu->input_lock, flags); 24662306a36Sopenharmony_ci if (!ok) { 24762306a36Sopenharmony_ci snd_printk(KERN_ERR "cmd: 0x%x failed at 0x%lx " 24862306a36Sopenharmony_ci "(status = 0x%x, data = 0x%x)\n", cmd, mpu->port, 24962306a36Sopenharmony_ci mpu->read(mpu, MPU401C(mpu)), 25062306a36Sopenharmony_ci mpu->read(mpu, MPU401D(mpu))); 25162306a36Sopenharmony_ci return 1; 25262306a36Sopenharmony_ci } 25362306a36Sopenharmony_ci return 0; 25462306a36Sopenharmony_ci} 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_cistatic int snd_mpu401_do_reset(struct snd_mpu401 *mpu) 25762306a36Sopenharmony_ci{ 25862306a36Sopenharmony_ci if (snd_mpu401_uart_cmd(mpu, MPU401_RESET, 1)) 25962306a36Sopenharmony_ci return -EIO; 26062306a36Sopenharmony_ci if (snd_mpu401_uart_cmd(mpu, MPU401_ENTER_UART, 0)) 26162306a36Sopenharmony_ci return -EIO; 26262306a36Sopenharmony_ci return 0; 26362306a36Sopenharmony_ci} 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci/* 26662306a36Sopenharmony_ci * input/output open/close - protected by open_mutex in rawmidi.c 26762306a36Sopenharmony_ci */ 26862306a36Sopenharmony_cistatic int snd_mpu401_uart_input_open(struct snd_rawmidi_substream *substream) 26962306a36Sopenharmony_ci{ 27062306a36Sopenharmony_ci struct snd_mpu401 *mpu; 27162306a36Sopenharmony_ci int err; 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci mpu = substream->rmidi->private_data; 27462306a36Sopenharmony_ci if (mpu->open_input) { 27562306a36Sopenharmony_ci err = mpu->open_input(mpu); 27662306a36Sopenharmony_ci if (err < 0) 27762306a36Sopenharmony_ci return err; 27862306a36Sopenharmony_ci } 27962306a36Sopenharmony_ci if (! test_bit(MPU401_MODE_BIT_OUTPUT, &mpu->mode)) { 28062306a36Sopenharmony_ci if (snd_mpu401_do_reset(mpu) < 0) 28162306a36Sopenharmony_ci goto error_out; 28262306a36Sopenharmony_ci } 28362306a36Sopenharmony_ci mpu->substream_input = substream; 28462306a36Sopenharmony_ci set_bit(MPU401_MODE_BIT_INPUT, &mpu->mode); 28562306a36Sopenharmony_ci return 0; 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_cierror_out: 28862306a36Sopenharmony_ci if (mpu->open_input && mpu->close_input) 28962306a36Sopenharmony_ci mpu->close_input(mpu); 29062306a36Sopenharmony_ci return -EIO; 29162306a36Sopenharmony_ci} 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_cistatic int snd_mpu401_uart_output_open(struct snd_rawmidi_substream *substream) 29462306a36Sopenharmony_ci{ 29562306a36Sopenharmony_ci struct snd_mpu401 *mpu; 29662306a36Sopenharmony_ci int err; 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci mpu = substream->rmidi->private_data; 29962306a36Sopenharmony_ci if (mpu->open_output) { 30062306a36Sopenharmony_ci err = mpu->open_output(mpu); 30162306a36Sopenharmony_ci if (err < 0) 30262306a36Sopenharmony_ci return err; 30362306a36Sopenharmony_ci } 30462306a36Sopenharmony_ci if (! test_bit(MPU401_MODE_BIT_INPUT, &mpu->mode)) { 30562306a36Sopenharmony_ci if (snd_mpu401_do_reset(mpu) < 0) 30662306a36Sopenharmony_ci goto error_out; 30762306a36Sopenharmony_ci } 30862306a36Sopenharmony_ci mpu->substream_output = substream; 30962306a36Sopenharmony_ci set_bit(MPU401_MODE_BIT_OUTPUT, &mpu->mode); 31062306a36Sopenharmony_ci return 0; 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_cierror_out: 31362306a36Sopenharmony_ci if (mpu->open_output && mpu->close_output) 31462306a36Sopenharmony_ci mpu->close_output(mpu); 31562306a36Sopenharmony_ci return -EIO; 31662306a36Sopenharmony_ci} 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_cistatic int snd_mpu401_uart_input_close(struct snd_rawmidi_substream *substream) 31962306a36Sopenharmony_ci{ 32062306a36Sopenharmony_ci struct snd_mpu401 *mpu; 32162306a36Sopenharmony_ci int err = 0; 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci mpu = substream->rmidi->private_data; 32462306a36Sopenharmony_ci clear_bit(MPU401_MODE_BIT_INPUT, &mpu->mode); 32562306a36Sopenharmony_ci mpu->substream_input = NULL; 32662306a36Sopenharmony_ci if (! test_bit(MPU401_MODE_BIT_OUTPUT, &mpu->mode)) 32762306a36Sopenharmony_ci err = snd_mpu401_uart_cmd(mpu, MPU401_RESET, 0); 32862306a36Sopenharmony_ci if (mpu->close_input) 32962306a36Sopenharmony_ci mpu->close_input(mpu); 33062306a36Sopenharmony_ci if (err) 33162306a36Sopenharmony_ci return -EIO; 33262306a36Sopenharmony_ci return 0; 33362306a36Sopenharmony_ci} 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_cistatic int snd_mpu401_uart_output_close(struct snd_rawmidi_substream *substream) 33662306a36Sopenharmony_ci{ 33762306a36Sopenharmony_ci struct snd_mpu401 *mpu; 33862306a36Sopenharmony_ci int err = 0; 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci mpu = substream->rmidi->private_data; 34162306a36Sopenharmony_ci clear_bit(MPU401_MODE_BIT_OUTPUT, &mpu->mode); 34262306a36Sopenharmony_ci mpu->substream_output = NULL; 34362306a36Sopenharmony_ci if (! test_bit(MPU401_MODE_BIT_INPUT, &mpu->mode)) 34462306a36Sopenharmony_ci err = snd_mpu401_uart_cmd(mpu, MPU401_RESET, 0); 34562306a36Sopenharmony_ci if (mpu->close_output) 34662306a36Sopenharmony_ci mpu->close_output(mpu); 34762306a36Sopenharmony_ci if (err) 34862306a36Sopenharmony_ci return -EIO; 34962306a36Sopenharmony_ci return 0; 35062306a36Sopenharmony_ci} 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci/* 35362306a36Sopenharmony_ci * trigger input callback 35462306a36Sopenharmony_ci */ 35562306a36Sopenharmony_cistatic void 35662306a36Sopenharmony_cisnd_mpu401_uart_input_trigger(struct snd_rawmidi_substream *substream, int up) 35762306a36Sopenharmony_ci{ 35862306a36Sopenharmony_ci unsigned long flags; 35962306a36Sopenharmony_ci struct snd_mpu401 *mpu; 36062306a36Sopenharmony_ci int max = 64; 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci mpu = substream->rmidi->private_data; 36362306a36Sopenharmony_ci if (up) { 36462306a36Sopenharmony_ci if (! test_and_set_bit(MPU401_MODE_BIT_INPUT_TRIGGER, 36562306a36Sopenharmony_ci &mpu->mode)) { 36662306a36Sopenharmony_ci /* first time - flush FIFO */ 36762306a36Sopenharmony_ci while (max-- > 0) 36862306a36Sopenharmony_ci mpu->read(mpu, MPU401D(mpu)); 36962306a36Sopenharmony_ci if (mpu->info_flags & MPU401_INFO_USE_TIMER) 37062306a36Sopenharmony_ci snd_mpu401_uart_add_timer(mpu, 1); 37162306a36Sopenharmony_ci } 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci /* read data in advance */ 37462306a36Sopenharmony_ci spin_lock_irqsave(&mpu->input_lock, flags); 37562306a36Sopenharmony_ci snd_mpu401_uart_input_read(mpu); 37662306a36Sopenharmony_ci spin_unlock_irqrestore(&mpu->input_lock, flags); 37762306a36Sopenharmony_ci } else { 37862306a36Sopenharmony_ci if (mpu->info_flags & MPU401_INFO_USE_TIMER) 37962306a36Sopenharmony_ci snd_mpu401_uart_remove_timer(mpu, 1); 38062306a36Sopenharmony_ci clear_bit(MPU401_MODE_BIT_INPUT_TRIGGER, &mpu->mode); 38162306a36Sopenharmony_ci } 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci} 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci/* 38662306a36Sopenharmony_ci * transfer input pending data 38762306a36Sopenharmony_ci * call with input_lock spinlock held 38862306a36Sopenharmony_ci */ 38962306a36Sopenharmony_cistatic void snd_mpu401_uart_input_read(struct snd_mpu401 * mpu) 39062306a36Sopenharmony_ci{ 39162306a36Sopenharmony_ci int max = 128; 39262306a36Sopenharmony_ci unsigned char byte; 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci while (max-- > 0) { 39562306a36Sopenharmony_ci if (! snd_mpu401_input_avail(mpu)) 39662306a36Sopenharmony_ci break; /* input not available */ 39762306a36Sopenharmony_ci byte = mpu->read(mpu, MPU401D(mpu)); 39862306a36Sopenharmony_ci if (test_bit(MPU401_MODE_BIT_INPUT_TRIGGER, &mpu->mode)) 39962306a36Sopenharmony_ci snd_rawmidi_receive(mpu->substream_input, &byte, 1); 40062306a36Sopenharmony_ci } 40162306a36Sopenharmony_ci} 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci/* 40462306a36Sopenharmony_ci * Tx FIFO sizes: 40562306a36Sopenharmony_ci * CS4237B - 16 bytes 40662306a36Sopenharmony_ci * AudioDrive ES1688 - 12 bytes 40762306a36Sopenharmony_ci * S3 SonicVibes - 8 bytes 40862306a36Sopenharmony_ci * SoundBlaster AWE 64 - 2 bytes (ugly hardware) 40962306a36Sopenharmony_ci */ 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci/* 41262306a36Sopenharmony_ci * write output pending bytes 41362306a36Sopenharmony_ci * call with output_lock spinlock held 41462306a36Sopenharmony_ci */ 41562306a36Sopenharmony_cistatic void snd_mpu401_uart_output_write(struct snd_mpu401 * mpu) 41662306a36Sopenharmony_ci{ 41762306a36Sopenharmony_ci unsigned char byte; 41862306a36Sopenharmony_ci int max = 256; 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_ci do { 42162306a36Sopenharmony_ci if (snd_rawmidi_transmit_peek(mpu->substream_output, 42262306a36Sopenharmony_ci &byte, 1) == 1) { 42362306a36Sopenharmony_ci /* 42462306a36Sopenharmony_ci * Try twice because there is hardware that insists on 42562306a36Sopenharmony_ci * setting the output busy bit after each write. 42662306a36Sopenharmony_ci */ 42762306a36Sopenharmony_ci if (!snd_mpu401_output_ready(mpu) && 42862306a36Sopenharmony_ci !snd_mpu401_output_ready(mpu)) 42962306a36Sopenharmony_ci break; /* Tx FIFO full - try again later */ 43062306a36Sopenharmony_ci mpu->write(mpu, byte, MPU401D(mpu)); 43162306a36Sopenharmony_ci snd_rawmidi_transmit_ack(mpu->substream_output, 1); 43262306a36Sopenharmony_ci } else { 43362306a36Sopenharmony_ci snd_mpu401_uart_remove_timer (mpu, 0); 43462306a36Sopenharmony_ci break; /* no other data - leave the tx loop */ 43562306a36Sopenharmony_ci } 43662306a36Sopenharmony_ci } while (--max > 0); 43762306a36Sopenharmony_ci} 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ci/* 44062306a36Sopenharmony_ci * output trigger callback 44162306a36Sopenharmony_ci */ 44262306a36Sopenharmony_cistatic void 44362306a36Sopenharmony_cisnd_mpu401_uart_output_trigger(struct snd_rawmidi_substream *substream, int up) 44462306a36Sopenharmony_ci{ 44562306a36Sopenharmony_ci unsigned long flags; 44662306a36Sopenharmony_ci struct snd_mpu401 *mpu; 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci mpu = substream->rmidi->private_data; 44962306a36Sopenharmony_ci if (up) { 45062306a36Sopenharmony_ci set_bit(MPU401_MODE_BIT_OUTPUT_TRIGGER, &mpu->mode); 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ci /* try to add the timer at each output trigger, 45362306a36Sopenharmony_ci * since the output timer might have been removed in 45462306a36Sopenharmony_ci * snd_mpu401_uart_output_write(). 45562306a36Sopenharmony_ci */ 45662306a36Sopenharmony_ci if (! (mpu->info_flags & MPU401_INFO_TX_IRQ)) 45762306a36Sopenharmony_ci snd_mpu401_uart_add_timer(mpu, 0); 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci /* output pending data */ 46062306a36Sopenharmony_ci spin_lock_irqsave(&mpu->output_lock, flags); 46162306a36Sopenharmony_ci snd_mpu401_uart_output_write(mpu); 46262306a36Sopenharmony_ci spin_unlock_irqrestore(&mpu->output_lock, flags); 46362306a36Sopenharmony_ci } else { 46462306a36Sopenharmony_ci if (! (mpu->info_flags & MPU401_INFO_TX_IRQ)) 46562306a36Sopenharmony_ci snd_mpu401_uart_remove_timer(mpu, 0); 46662306a36Sopenharmony_ci clear_bit(MPU401_MODE_BIT_OUTPUT_TRIGGER, &mpu->mode); 46762306a36Sopenharmony_ci } 46862306a36Sopenharmony_ci} 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci/* 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci */ 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_cistatic const struct snd_rawmidi_ops snd_mpu401_uart_output = 47562306a36Sopenharmony_ci{ 47662306a36Sopenharmony_ci .open = snd_mpu401_uart_output_open, 47762306a36Sopenharmony_ci .close = snd_mpu401_uart_output_close, 47862306a36Sopenharmony_ci .trigger = snd_mpu401_uart_output_trigger, 47962306a36Sopenharmony_ci}; 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_cistatic const struct snd_rawmidi_ops snd_mpu401_uart_input = 48262306a36Sopenharmony_ci{ 48362306a36Sopenharmony_ci .open = snd_mpu401_uart_input_open, 48462306a36Sopenharmony_ci .close = snd_mpu401_uart_input_close, 48562306a36Sopenharmony_ci .trigger = snd_mpu401_uart_input_trigger, 48662306a36Sopenharmony_ci}; 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_cistatic void snd_mpu401_uart_free(struct snd_rawmidi *rmidi) 48962306a36Sopenharmony_ci{ 49062306a36Sopenharmony_ci struct snd_mpu401 *mpu = rmidi->private_data; 49162306a36Sopenharmony_ci if (mpu->irq >= 0) 49262306a36Sopenharmony_ci free_irq(mpu->irq, (void *) mpu); 49362306a36Sopenharmony_ci release_and_free_resource(mpu->res); 49462306a36Sopenharmony_ci kfree(mpu); 49562306a36Sopenharmony_ci} 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_ci/** 49862306a36Sopenharmony_ci * snd_mpu401_uart_new - create an MPU401-UART instance 49962306a36Sopenharmony_ci * @card: the card instance 50062306a36Sopenharmony_ci * @device: the device index, zero-based 50162306a36Sopenharmony_ci * @hardware: the hardware type, MPU401_HW_XXXX 50262306a36Sopenharmony_ci * @port: the base address of MPU401 port 50362306a36Sopenharmony_ci * @info_flags: bitflags MPU401_INFO_XXX 50462306a36Sopenharmony_ci * @irq: the ISA irq number, -1 if not to be allocated 50562306a36Sopenharmony_ci * @rrawmidi: the pointer to store the new rawmidi instance 50662306a36Sopenharmony_ci * 50762306a36Sopenharmony_ci * Creates a new MPU-401 instance. 50862306a36Sopenharmony_ci * 50962306a36Sopenharmony_ci * Note that the rawmidi instance is returned on the rrawmidi argument, 51062306a36Sopenharmony_ci * not the mpu401 instance itself. To access to the mpu401 instance, 51162306a36Sopenharmony_ci * cast from rawmidi->private_data (with struct snd_mpu401 magic-cast). 51262306a36Sopenharmony_ci * 51362306a36Sopenharmony_ci * Return: Zero if successful, or a negative error code. 51462306a36Sopenharmony_ci */ 51562306a36Sopenharmony_ciint snd_mpu401_uart_new(struct snd_card *card, int device, 51662306a36Sopenharmony_ci unsigned short hardware, 51762306a36Sopenharmony_ci unsigned long port, 51862306a36Sopenharmony_ci unsigned int info_flags, 51962306a36Sopenharmony_ci int irq, 52062306a36Sopenharmony_ci struct snd_rawmidi ** rrawmidi) 52162306a36Sopenharmony_ci{ 52262306a36Sopenharmony_ci struct snd_mpu401 *mpu; 52362306a36Sopenharmony_ci struct snd_rawmidi *rmidi; 52462306a36Sopenharmony_ci int in_enable, out_enable; 52562306a36Sopenharmony_ci int err; 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci if (rrawmidi) 52862306a36Sopenharmony_ci *rrawmidi = NULL; 52962306a36Sopenharmony_ci if (! (info_flags & (MPU401_INFO_INPUT | MPU401_INFO_OUTPUT))) 53062306a36Sopenharmony_ci info_flags |= MPU401_INFO_INPUT | MPU401_INFO_OUTPUT; 53162306a36Sopenharmony_ci in_enable = (info_flags & MPU401_INFO_INPUT) ? 1 : 0; 53262306a36Sopenharmony_ci out_enable = (info_flags & MPU401_INFO_OUTPUT) ? 1 : 0; 53362306a36Sopenharmony_ci err = snd_rawmidi_new(card, "MPU-401U", device, 53462306a36Sopenharmony_ci out_enable, in_enable, &rmidi); 53562306a36Sopenharmony_ci if (err < 0) 53662306a36Sopenharmony_ci return err; 53762306a36Sopenharmony_ci mpu = kzalloc(sizeof(*mpu), GFP_KERNEL); 53862306a36Sopenharmony_ci if (!mpu) { 53962306a36Sopenharmony_ci err = -ENOMEM; 54062306a36Sopenharmony_ci goto free_device; 54162306a36Sopenharmony_ci } 54262306a36Sopenharmony_ci rmidi->private_data = mpu; 54362306a36Sopenharmony_ci rmidi->private_free = snd_mpu401_uart_free; 54462306a36Sopenharmony_ci spin_lock_init(&mpu->input_lock); 54562306a36Sopenharmony_ci spin_lock_init(&mpu->output_lock); 54662306a36Sopenharmony_ci spin_lock_init(&mpu->timer_lock); 54762306a36Sopenharmony_ci mpu->hardware = hardware; 54862306a36Sopenharmony_ci mpu->irq = -1; 54962306a36Sopenharmony_ci if (! (info_flags & MPU401_INFO_INTEGRATED)) { 55062306a36Sopenharmony_ci int res_size = hardware == MPU401_HW_PC98II ? 4 : 2; 55162306a36Sopenharmony_ci mpu->res = request_region(port, res_size, "MPU401 UART"); 55262306a36Sopenharmony_ci if (!mpu->res) { 55362306a36Sopenharmony_ci snd_printk(KERN_ERR "mpu401_uart: " 55462306a36Sopenharmony_ci "unable to grab port 0x%lx size %d\n", 55562306a36Sopenharmony_ci port, res_size); 55662306a36Sopenharmony_ci err = -EBUSY; 55762306a36Sopenharmony_ci goto free_device; 55862306a36Sopenharmony_ci } 55962306a36Sopenharmony_ci } 56062306a36Sopenharmony_ci if (info_flags & MPU401_INFO_MMIO) { 56162306a36Sopenharmony_ci mpu->write = mpu401_write_mmio; 56262306a36Sopenharmony_ci mpu->read = mpu401_read_mmio; 56362306a36Sopenharmony_ci } else { 56462306a36Sopenharmony_ci mpu->write = mpu401_write_port; 56562306a36Sopenharmony_ci mpu->read = mpu401_read_port; 56662306a36Sopenharmony_ci } 56762306a36Sopenharmony_ci mpu->port = port; 56862306a36Sopenharmony_ci if (hardware == MPU401_HW_PC98II) 56962306a36Sopenharmony_ci mpu->cport = port + 2; 57062306a36Sopenharmony_ci else 57162306a36Sopenharmony_ci mpu->cport = port + 1; 57262306a36Sopenharmony_ci if (irq >= 0) { 57362306a36Sopenharmony_ci if (request_irq(irq, snd_mpu401_uart_interrupt, 0, 57462306a36Sopenharmony_ci "MPU401 UART", (void *) mpu)) { 57562306a36Sopenharmony_ci snd_printk(KERN_ERR "mpu401_uart: " 57662306a36Sopenharmony_ci "unable to grab IRQ %d\n", irq); 57762306a36Sopenharmony_ci err = -EBUSY; 57862306a36Sopenharmony_ci goto free_device; 57962306a36Sopenharmony_ci } 58062306a36Sopenharmony_ci } 58162306a36Sopenharmony_ci if (irq < 0 && !(info_flags & MPU401_INFO_IRQ_HOOK)) 58262306a36Sopenharmony_ci info_flags |= MPU401_INFO_USE_TIMER; 58362306a36Sopenharmony_ci mpu->info_flags = info_flags; 58462306a36Sopenharmony_ci mpu->irq = irq; 58562306a36Sopenharmony_ci if (card->shortname[0]) 58662306a36Sopenharmony_ci snprintf(rmidi->name, sizeof(rmidi->name), "%s MIDI", 58762306a36Sopenharmony_ci card->shortname); 58862306a36Sopenharmony_ci else 58962306a36Sopenharmony_ci sprintf(rmidi->name, "MPU-401 MIDI %d-%d",card->number, device); 59062306a36Sopenharmony_ci if (out_enable) { 59162306a36Sopenharmony_ci snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, 59262306a36Sopenharmony_ci &snd_mpu401_uart_output); 59362306a36Sopenharmony_ci rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT; 59462306a36Sopenharmony_ci } 59562306a36Sopenharmony_ci if (in_enable) { 59662306a36Sopenharmony_ci snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, 59762306a36Sopenharmony_ci &snd_mpu401_uart_input); 59862306a36Sopenharmony_ci rmidi->info_flags |= SNDRV_RAWMIDI_INFO_INPUT; 59962306a36Sopenharmony_ci if (out_enable) 60062306a36Sopenharmony_ci rmidi->info_flags |= SNDRV_RAWMIDI_INFO_DUPLEX; 60162306a36Sopenharmony_ci } 60262306a36Sopenharmony_ci mpu->rmidi = rmidi; 60362306a36Sopenharmony_ci if (rrawmidi) 60462306a36Sopenharmony_ci *rrawmidi = rmidi; 60562306a36Sopenharmony_ci return 0; 60662306a36Sopenharmony_cifree_device: 60762306a36Sopenharmony_ci snd_device_free(card, rmidi); 60862306a36Sopenharmony_ci return err; 60962306a36Sopenharmony_ci} 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_ciEXPORT_SYMBOL(snd_mpu401_uart_new); 612