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