162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (C) by Paul Barton-Davis 1998-1999
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci/* The low level driver for the WaveFront ICS2115 MIDI interface(s)
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci * Note that there is also an MPU-401 emulation (actually, a UART-401
962306a36Sopenharmony_ci * emulation) on the CS4232 on the Tropez and Tropez Plus. This code
1062306a36Sopenharmony_ci * has nothing to do with that interface at all.
1162306a36Sopenharmony_ci *
1262306a36Sopenharmony_ci * The interface is essentially just a UART-401, but is has the
1362306a36Sopenharmony_ci * interesting property of supporting what Turtle Beach called
1462306a36Sopenharmony_ci * "Virtual MIDI" mode. In this mode, there are effectively *two*
1562306a36Sopenharmony_ci * MIDI buses accessible via the interface, one that is routed
1662306a36Sopenharmony_ci * solely to/from the external WaveFront synthesizer and the other
1762306a36Sopenharmony_ci * corresponding to the pin/socket connector used to link external
1862306a36Sopenharmony_ci * MIDI devices to the board.
1962306a36Sopenharmony_ci *
2062306a36Sopenharmony_ci * This driver fully supports this mode, allowing two distinct MIDI
2162306a36Sopenharmony_ci * busses to be used completely independently, giving 32 channels of
2262306a36Sopenharmony_ci * MIDI routing, 16 to the WaveFront synth and 16 to the external MIDI
2362306a36Sopenharmony_ci * bus. The devices are named /dev/snd/midiCnD0 and /dev/snd/midiCnD1,
2462306a36Sopenharmony_ci * where `n' is the card number. Note that the device numbers may be
2562306a36Sopenharmony_ci * something other than 0 and 1 if the CS4232 UART/MPU-401 interface
2662306a36Sopenharmony_ci * is enabled.
2762306a36Sopenharmony_ci *
2862306a36Sopenharmony_ci * Switching between the two is accomplished externally by the driver
2962306a36Sopenharmony_ci * using the two otherwise unused MIDI bytes. See the code for more details.
3062306a36Sopenharmony_ci *
3162306a36Sopenharmony_ci * NOTE: VIRTUAL MIDI MODE IS ON BY DEFAULT (see lowlevel/isa/wavefront.c)
3262306a36Sopenharmony_ci *
3362306a36Sopenharmony_ci * The main reason to turn off Virtual MIDI mode is when you want to
3462306a36Sopenharmony_ci * tightly couple the WaveFront synth with an external MIDI
3562306a36Sopenharmony_ci * device. You won't be able to distinguish the source of any MIDI
3662306a36Sopenharmony_ci * data except via SysEx ID, but thats probably OK, since for the most
3762306a36Sopenharmony_ci * part, the WaveFront won't be sending any MIDI data at all.
3862306a36Sopenharmony_ci *
3962306a36Sopenharmony_ci * The main reason to turn on Virtual MIDI Mode is to provide two
4062306a36Sopenharmony_ci * completely independent 16-channel MIDI buses, one to the
4162306a36Sopenharmony_ci * WaveFront and one to any external MIDI devices. Given the 32
4262306a36Sopenharmony_ci * voice nature of the WaveFront, its pretty easy to find a use
4362306a36Sopenharmony_ci * for all 16 channels driving just that synth.
4462306a36Sopenharmony_ci *
4562306a36Sopenharmony_ci */
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci#include <linux/io.h>
4862306a36Sopenharmony_ci#include <linux/init.h>
4962306a36Sopenharmony_ci#include <linux/time.h>
5062306a36Sopenharmony_ci#include <linux/wait.h>
5162306a36Sopenharmony_ci#include <sound/core.h>
5262306a36Sopenharmony_ci#include <sound/snd_wavefront.h>
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_cistatic inline int
5562306a36Sopenharmony_ciwf_mpu_status (snd_wavefront_midi_t *midi)
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci{
5862306a36Sopenharmony_ci	return inb (midi->mpu_status_port);
5962306a36Sopenharmony_ci}
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_cistatic inline int
6262306a36Sopenharmony_ciinput_avail (snd_wavefront_midi_t *midi)
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci{
6562306a36Sopenharmony_ci	return !(wf_mpu_status(midi) & INPUT_AVAIL);
6662306a36Sopenharmony_ci}
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_cistatic inline int
6962306a36Sopenharmony_cioutput_ready (snd_wavefront_midi_t *midi)
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci{
7262306a36Sopenharmony_ci	return !(wf_mpu_status(midi) & OUTPUT_READY);
7362306a36Sopenharmony_ci}
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_cistatic inline int
7662306a36Sopenharmony_ciread_data (snd_wavefront_midi_t *midi)
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci{
7962306a36Sopenharmony_ci	return inb (midi->mpu_data_port);
8062306a36Sopenharmony_ci}
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_cistatic inline void
8362306a36Sopenharmony_ciwrite_data (snd_wavefront_midi_t *midi, unsigned char byte)
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci{
8662306a36Sopenharmony_ci	outb (byte, midi->mpu_data_port);
8762306a36Sopenharmony_ci}
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_cistatic snd_wavefront_midi_t *
9062306a36Sopenharmony_ciget_wavefront_midi (struct snd_rawmidi_substream *substream)
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci{
9362306a36Sopenharmony_ci	struct snd_card *card;
9462306a36Sopenharmony_ci	snd_wavefront_card_t *acard;
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci	if (substream == NULL || substream->rmidi == NULL)
9762306a36Sopenharmony_ci	        return NULL;
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci	card = substream->rmidi->card;
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci	if (card == NULL)
10262306a36Sopenharmony_ci	        return NULL;
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci	if (card->private_data == NULL)
10562306a36Sopenharmony_ci 	        return NULL;
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci	acard = card->private_data;
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci	return &acard->wavefront.midi;
11062306a36Sopenharmony_ci}
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_cistatic void snd_wavefront_midi_output_write(snd_wavefront_card_t *card)
11362306a36Sopenharmony_ci{
11462306a36Sopenharmony_ci	snd_wavefront_midi_t *midi = &card->wavefront.midi;
11562306a36Sopenharmony_ci	snd_wavefront_mpu_id  mpu;
11662306a36Sopenharmony_ci	unsigned long flags;
11762306a36Sopenharmony_ci	unsigned char midi_byte;
11862306a36Sopenharmony_ci	int max = 256, mask = 1;
11962306a36Sopenharmony_ci	int timeout;
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci	/* Its not OK to try to change the status of "virtuality" of
12262306a36Sopenharmony_ci	   the MIDI interface while we're outputting stuff.  See
12362306a36Sopenharmony_ci	   snd_wavefront_midi_{enable,disable}_virtual () for the
12462306a36Sopenharmony_ci	   other half of this.
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci	   The first loop attempts to flush any data from the
12762306a36Sopenharmony_ci	   current output device, and then the second
12862306a36Sopenharmony_ci	   emits the switch byte (if necessary), and starts
12962306a36Sopenharmony_ci	   outputting data for the output device currently in use.
13062306a36Sopenharmony_ci	*/
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci	if (midi->substream_output[midi->output_mpu] == NULL) {
13362306a36Sopenharmony_ci		goto __second;
13462306a36Sopenharmony_ci	}
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci	while (max > 0) {
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci		/* XXX fix me - no hard timing loops allowed! */
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci		for (timeout = 30000; timeout > 0; timeout--) {
14162306a36Sopenharmony_ci			if (output_ready (midi))
14262306a36Sopenharmony_ci				break;
14362306a36Sopenharmony_ci		}
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci		spin_lock_irqsave (&midi->virtual, flags);
14662306a36Sopenharmony_ci		if ((midi->mode[midi->output_mpu] & MPU401_MODE_OUTPUT) == 0) {
14762306a36Sopenharmony_ci			spin_unlock_irqrestore (&midi->virtual, flags);
14862306a36Sopenharmony_ci			goto __second;
14962306a36Sopenharmony_ci		}
15062306a36Sopenharmony_ci		if (output_ready (midi)) {
15162306a36Sopenharmony_ci			if (snd_rawmidi_transmit(midi->substream_output[midi->output_mpu], &midi_byte, 1) == 1) {
15262306a36Sopenharmony_ci				if (!midi->isvirtual ||
15362306a36Sopenharmony_ci					(midi_byte != WF_INTERNAL_SWITCH &&
15462306a36Sopenharmony_ci					 midi_byte != WF_EXTERNAL_SWITCH))
15562306a36Sopenharmony_ci					write_data(midi, midi_byte);
15662306a36Sopenharmony_ci				max--;
15762306a36Sopenharmony_ci			} else {
15862306a36Sopenharmony_ci				if (midi->istimer) {
15962306a36Sopenharmony_ci					if (--midi->istimer <= 0)
16062306a36Sopenharmony_ci						del_timer(&midi->timer);
16162306a36Sopenharmony_ci				}
16262306a36Sopenharmony_ci				midi->mode[midi->output_mpu] &= ~MPU401_MODE_OUTPUT_TRIGGER;
16362306a36Sopenharmony_ci				spin_unlock_irqrestore (&midi->virtual, flags);
16462306a36Sopenharmony_ci				goto __second;
16562306a36Sopenharmony_ci			}
16662306a36Sopenharmony_ci		} else {
16762306a36Sopenharmony_ci			spin_unlock_irqrestore (&midi->virtual, flags);
16862306a36Sopenharmony_ci			return;
16962306a36Sopenharmony_ci		}
17062306a36Sopenharmony_ci		spin_unlock_irqrestore (&midi->virtual, flags);
17162306a36Sopenharmony_ci	}
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci      __second:
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci	if (midi->substream_output[!midi->output_mpu] == NULL) {
17662306a36Sopenharmony_ci		return;
17762306a36Sopenharmony_ci	}
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci	while (max > 0) {
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci		/* XXX fix me - no hard timing loops allowed! */
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci		for (timeout = 30000; timeout > 0; timeout--) {
18462306a36Sopenharmony_ci			if (output_ready (midi))
18562306a36Sopenharmony_ci				break;
18662306a36Sopenharmony_ci		}
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci		spin_lock_irqsave (&midi->virtual, flags);
18962306a36Sopenharmony_ci		if (!midi->isvirtual)
19062306a36Sopenharmony_ci			mask = 0;
19162306a36Sopenharmony_ci		mpu = midi->output_mpu ^ mask;
19262306a36Sopenharmony_ci		mask = 0;	/* don't invert the value from now */
19362306a36Sopenharmony_ci		if ((midi->mode[mpu] & MPU401_MODE_OUTPUT) == 0) {
19462306a36Sopenharmony_ci			spin_unlock_irqrestore (&midi->virtual, flags);
19562306a36Sopenharmony_ci			return;
19662306a36Sopenharmony_ci		}
19762306a36Sopenharmony_ci		if (snd_rawmidi_transmit_empty(midi->substream_output[mpu]))
19862306a36Sopenharmony_ci			goto __timer;
19962306a36Sopenharmony_ci		if (output_ready (midi)) {
20062306a36Sopenharmony_ci			if (mpu != midi->output_mpu) {
20162306a36Sopenharmony_ci				write_data(midi, mpu == internal_mpu ?
20262306a36Sopenharmony_ci							WF_INTERNAL_SWITCH :
20362306a36Sopenharmony_ci							WF_EXTERNAL_SWITCH);
20462306a36Sopenharmony_ci				midi->output_mpu = mpu;
20562306a36Sopenharmony_ci			} else if (snd_rawmidi_transmit(midi->substream_output[mpu], &midi_byte, 1) == 1) {
20662306a36Sopenharmony_ci				if (!midi->isvirtual ||
20762306a36Sopenharmony_ci					(midi_byte != WF_INTERNAL_SWITCH &&
20862306a36Sopenharmony_ci					 midi_byte != WF_EXTERNAL_SWITCH))
20962306a36Sopenharmony_ci					write_data(midi, midi_byte);
21062306a36Sopenharmony_ci				max--;
21162306a36Sopenharmony_ci			} else {
21262306a36Sopenharmony_ci			      __timer:
21362306a36Sopenharmony_ci				if (midi->istimer) {
21462306a36Sopenharmony_ci					if (--midi->istimer <= 0)
21562306a36Sopenharmony_ci						del_timer(&midi->timer);
21662306a36Sopenharmony_ci				}
21762306a36Sopenharmony_ci				midi->mode[mpu] &= ~MPU401_MODE_OUTPUT_TRIGGER;
21862306a36Sopenharmony_ci				spin_unlock_irqrestore (&midi->virtual, flags);
21962306a36Sopenharmony_ci				return;
22062306a36Sopenharmony_ci			}
22162306a36Sopenharmony_ci		} else {
22262306a36Sopenharmony_ci			spin_unlock_irqrestore (&midi->virtual, flags);
22362306a36Sopenharmony_ci			return;
22462306a36Sopenharmony_ci		}
22562306a36Sopenharmony_ci		spin_unlock_irqrestore (&midi->virtual, flags);
22662306a36Sopenharmony_ci	}
22762306a36Sopenharmony_ci}
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_cistatic int snd_wavefront_midi_input_open(struct snd_rawmidi_substream *substream)
23062306a36Sopenharmony_ci{
23162306a36Sopenharmony_ci	unsigned long flags;
23262306a36Sopenharmony_ci	snd_wavefront_midi_t *midi;
23362306a36Sopenharmony_ci	snd_wavefront_mpu_id mpu;
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci	if (snd_BUG_ON(!substream || !substream->rmidi))
23662306a36Sopenharmony_ci		return -ENXIO;
23762306a36Sopenharmony_ci	if (snd_BUG_ON(!substream->rmidi->private_data))
23862306a36Sopenharmony_ci		return -ENXIO;
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci	mpu = *((snd_wavefront_mpu_id *) substream->rmidi->private_data);
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci	midi = get_wavefront_midi(substream);
24362306a36Sopenharmony_ci	if (!midi)
24462306a36Sopenharmony_ci	        return -EIO;
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci	spin_lock_irqsave (&midi->open, flags);
24762306a36Sopenharmony_ci	midi->mode[mpu] |= MPU401_MODE_INPUT;
24862306a36Sopenharmony_ci	midi->substream_input[mpu] = substream;
24962306a36Sopenharmony_ci	spin_unlock_irqrestore (&midi->open, flags);
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci	return 0;
25262306a36Sopenharmony_ci}
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_cistatic int snd_wavefront_midi_output_open(struct snd_rawmidi_substream *substream)
25562306a36Sopenharmony_ci{
25662306a36Sopenharmony_ci	unsigned long flags;
25762306a36Sopenharmony_ci	snd_wavefront_midi_t *midi;
25862306a36Sopenharmony_ci	snd_wavefront_mpu_id mpu;
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_ci	if (snd_BUG_ON(!substream || !substream->rmidi))
26162306a36Sopenharmony_ci		return -ENXIO;
26262306a36Sopenharmony_ci	if (snd_BUG_ON(!substream->rmidi->private_data))
26362306a36Sopenharmony_ci		return -ENXIO;
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ci	mpu = *((snd_wavefront_mpu_id *) substream->rmidi->private_data);
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_ci	midi = get_wavefront_midi(substream);
26862306a36Sopenharmony_ci	if (!midi)
26962306a36Sopenharmony_ci	        return -EIO;
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_ci	spin_lock_irqsave (&midi->open, flags);
27262306a36Sopenharmony_ci	midi->mode[mpu] |= MPU401_MODE_OUTPUT;
27362306a36Sopenharmony_ci	midi->substream_output[mpu] = substream;
27462306a36Sopenharmony_ci	spin_unlock_irqrestore (&midi->open, flags);
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_ci	return 0;
27762306a36Sopenharmony_ci}
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_cistatic int snd_wavefront_midi_input_close(struct snd_rawmidi_substream *substream)
28062306a36Sopenharmony_ci{
28162306a36Sopenharmony_ci	unsigned long flags;
28262306a36Sopenharmony_ci	snd_wavefront_midi_t *midi;
28362306a36Sopenharmony_ci	snd_wavefront_mpu_id mpu;
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci	if (snd_BUG_ON(!substream || !substream->rmidi))
28662306a36Sopenharmony_ci		return -ENXIO;
28762306a36Sopenharmony_ci	if (snd_BUG_ON(!substream->rmidi->private_data))
28862306a36Sopenharmony_ci		return -ENXIO;
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_ci	mpu = *((snd_wavefront_mpu_id *) substream->rmidi->private_data);
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci	midi = get_wavefront_midi(substream);
29362306a36Sopenharmony_ci	if (!midi)
29462306a36Sopenharmony_ci	        return -EIO;
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci	spin_lock_irqsave (&midi->open, flags);
29762306a36Sopenharmony_ci	midi->mode[mpu] &= ~MPU401_MODE_INPUT;
29862306a36Sopenharmony_ci	spin_unlock_irqrestore (&midi->open, flags);
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ci	return 0;
30162306a36Sopenharmony_ci}
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_cistatic int snd_wavefront_midi_output_close(struct snd_rawmidi_substream *substream)
30462306a36Sopenharmony_ci{
30562306a36Sopenharmony_ci	unsigned long flags;
30662306a36Sopenharmony_ci	snd_wavefront_midi_t *midi;
30762306a36Sopenharmony_ci	snd_wavefront_mpu_id mpu;
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_ci	if (snd_BUG_ON(!substream || !substream->rmidi))
31062306a36Sopenharmony_ci		return -ENXIO;
31162306a36Sopenharmony_ci	if (snd_BUG_ON(!substream->rmidi->private_data))
31262306a36Sopenharmony_ci		return -ENXIO;
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_ci	mpu = *((snd_wavefront_mpu_id *) substream->rmidi->private_data);
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci	midi = get_wavefront_midi(substream);
31762306a36Sopenharmony_ci	if (!midi)
31862306a36Sopenharmony_ci	        return -EIO;
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_ci	spin_lock_irqsave (&midi->open, flags);
32162306a36Sopenharmony_ci	midi->mode[mpu] &= ~MPU401_MODE_OUTPUT;
32262306a36Sopenharmony_ci	spin_unlock_irqrestore (&midi->open, flags);
32362306a36Sopenharmony_ci	return 0;
32462306a36Sopenharmony_ci}
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_cistatic void snd_wavefront_midi_input_trigger(struct snd_rawmidi_substream *substream, int up)
32762306a36Sopenharmony_ci{
32862306a36Sopenharmony_ci	unsigned long flags;
32962306a36Sopenharmony_ci	snd_wavefront_midi_t *midi;
33062306a36Sopenharmony_ci	snd_wavefront_mpu_id mpu;
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_ci	if (substream == NULL || substream->rmidi == NULL)
33362306a36Sopenharmony_ci	        return;
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_ci	if (substream->rmidi->private_data == NULL)
33662306a36Sopenharmony_ci	        return;
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ci	mpu = *((snd_wavefront_mpu_id *) substream->rmidi->private_data);
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ci	midi = get_wavefront_midi(substream);
34162306a36Sopenharmony_ci	if (!midi)
34262306a36Sopenharmony_ci		return;
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_ci	spin_lock_irqsave (&midi->virtual, flags);
34562306a36Sopenharmony_ci	if (up) {
34662306a36Sopenharmony_ci		midi->mode[mpu] |= MPU401_MODE_INPUT_TRIGGER;
34762306a36Sopenharmony_ci	} else {
34862306a36Sopenharmony_ci		midi->mode[mpu] &= ~MPU401_MODE_INPUT_TRIGGER;
34962306a36Sopenharmony_ci	}
35062306a36Sopenharmony_ci	spin_unlock_irqrestore (&midi->virtual, flags);
35162306a36Sopenharmony_ci}
35262306a36Sopenharmony_ci
35362306a36Sopenharmony_cistatic void snd_wavefront_midi_output_timer(struct timer_list *t)
35462306a36Sopenharmony_ci{
35562306a36Sopenharmony_ci	snd_wavefront_midi_t *midi = from_timer(midi, t, timer);
35662306a36Sopenharmony_ci	snd_wavefront_card_t *card = midi->timer_card;
35762306a36Sopenharmony_ci	unsigned long flags;
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ci	spin_lock_irqsave (&midi->virtual, flags);
36062306a36Sopenharmony_ci	mod_timer(&midi->timer, 1 + jiffies);
36162306a36Sopenharmony_ci	spin_unlock_irqrestore (&midi->virtual, flags);
36262306a36Sopenharmony_ci	snd_wavefront_midi_output_write(card);
36362306a36Sopenharmony_ci}
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_cistatic void snd_wavefront_midi_output_trigger(struct snd_rawmidi_substream *substream, int up)
36662306a36Sopenharmony_ci{
36762306a36Sopenharmony_ci	unsigned long flags;
36862306a36Sopenharmony_ci	snd_wavefront_midi_t *midi;
36962306a36Sopenharmony_ci	snd_wavefront_mpu_id mpu;
37062306a36Sopenharmony_ci
37162306a36Sopenharmony_ci	if (substream == NULL || substream->rmidi == NULL)
37262306a36Sopenharmony_ci	        return;
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_ci	if (substream->rmidi->private_data == NULL)
37562306a36Sopenharmony_ci	        return;
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_ci	mpu = *((snd_wavefront_mpu_id *) substream->rmidi->private_data);
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_ci	midi = get_wavefront_midi(substream);
38062306a36Sopenharmony_ci	if (!midi)
38162306a36Sopenharmony_ci		return;
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_ci	spin_lock_irqsave (&midi->virtual, flags);
38462306a36Sopenharmony_ci	if (up) {
38562306a36Sopenharmony_ci		if ((midi->mode[mpu] & MPU401_MODE_OUTPUT_TRIGGER) == 0) {
38662306a36Sopenharmony_ci			if (!midi->istimer) {
38762306a36Sopenharmony_ci				timer_setup(&midi->timer,
38862306a36Sopenharmony_ci					    snd_wavefront_midi_output_timer,
38962306a36Sopenharmony_ci					    0);
39062306a36Sopenharmony_ci				mod_timer(&midi->timer, 1 + jiffies);
39162306a36Sopenharmony_ci			}
39262306a36Sopenharmony_ci			midi->istimer++;
39362306a36Sopenharmony_ci			midi->mode[mpu] |= MPU401_MODE_OUTPUT_TRIGGER;
39462306a36Sopenharmony_ci		}
39562306a36Sopenharmony_ci	} else {
39662306a36Sopenharmony_ci		midi->mode[mpu] &= ~MPU401_MODE_OUTPUT_TRIGGER;
39762306a36Sopenharmony_ci	}
39862306a36Sopenharmony_ci	spin_unlock_irqrestore (&midi->virtual, flags);
39962306a36Sopenharmony_ci
40062306a36Sopenharmony_ci	if (up)
40162306a36Sopenharmony_ci		snd_wavefront_midi_output_write((snd_wavefront_card_t *)substream->rmidi->card->private_data);
40262306a36Sopenharmony_ci}
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_civoid
40562306a36Sopenharmony_cisnd_wavefront_midi_interrupt (snd_wavefront_card_t *card)
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_ci{
40862306a36Sopenharmony_ci	unsigned long flags;
40962306a36Sopenharmony_ci	snd_wavefront_midi_t *midi;
41062306a36Sopenharmony_ci	static struct snd_rawmidi_substream *substream = NULL;
41162306a36Sopenharmony_ci	static int mpu = external_mpu;
41262306a36Sopenharmony_ci	int max = 128;
41362306a36Sopenharmony_ci	unsigned char byte;
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ci	midi = &card->wavefront.midi;
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_ci	if (!input_avail (midi)) { /* not for us */
41862306a36Sopenharmony_ci		snd_wavefront_midi_output_write(card);
41962306a36Sopenharmony_ci		return;
42062306a36Sopenharmony_ci	}
42162306a36Sopenharmony_ci
42262306a36Sopenharmony_ci	spin_lock_irqsave (&midi->virtual, flags);
42362306a36Sopenharmony_ci	while (--max) {
42462306a36Sopenharmony_ci
42562306a36Sopenharmony_ci		if (input_avail (midi)) {
42662306a36Sopenharmony_ci			byte = read_data (midi);
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_ci			if (midi->isvirtual) {
42962306a36Sopenharmony_ci				if (byte == WF_EXTERNAL_SWITCH) {
43062306a36Sopenharmony_ci					substream = midi->substream_input[external_mpu];
43162306a36Sopenharmony_ci					mpu = external_mpu;
43262306a36Sopenharmony_ci				} else if (byte == WF_INTERNAL_SWITCH) {
43362306a36Sopenharmony_ci					substream = midi->substream_output[internal_mpu];
43462306a36Sopenharmony_ci					mpu = internal_mpu;
43562306a36Sopenharmony_ci				} /* else just leave it as it is */
43662306a36Sopenharmony_ci			} else {
43762306a36Sopenharmony_ci				substream = midi->substream_input[internal_mpu];
43862306a36Sopenharmony_ci				mpu = internal_mpu;
43962306a36Sopenharmony_ci			}
44062306a36Sopenharmony_ci
44162306a36Sopenharmony_ci			if (substream == NULL) {
44262306a36Sopenharmony_ci				continue;
44362306a36Sopenharmony_ci			}
44462306a36Sopenharmony_ci
44562306a36Sopenharmony_ci			if (midi->mode[mpu] & MPU401_MODE_INPUT_TRIGGER) {
44662306a36Sopenharmony_ci				snd_rawmidi_receive(substream, &byte, 1);
44762306a36Sopenharmony_ci			}
44862306a36Sopenharmony_ci		} else {
44962306a36Sopenharmony_ci			break;
45062306a36Sopenharmony_ci		}
45162306a36Sopenharmony_ci	}
45262306a36Sopenharmony_ci	spin_unlock_irqrestore (&midi->virtual, flags);
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_ci	snd_wavefront_midi_output_write(card);
45562306a36Sopenharmony_ci}
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_civoid
45862306a36Sopenharmony_cisnd_wavefront_midi_enable_virtual (snd_wavefront_card_t *card)
45962306a36Sopenharmony_ci
46062306a36Sopenharmony_ci{
46162306a36Sopenharmony_ci	unsigned long flags;
46262306a36Sopenharmony_ci
46362306a36Sopenharmony_ci	spin_lock_irqsave (&card->wavefront.midi.virtual, flags);
46462306a36Sopenharmony_ci	card->wavefront.midi.isvirtual = 1;
46562306a36Sopenharmony_ci	card->wavefront.midi.output_mpu = internal_mpu;
46662306a36Sopenharmony_ci	card->wavefront.midi.input_mpu = internal_mpu;
46762306a36Sopenharmony_ci	spin_unlock_irqrestore (&card->wavefront.midi.virtual, flags);
46862306a36Sopenharmony_ci}
46962306a36Sopenharmony_ci
47062306a36Sopenharmony_civoid
47162306a36Sopenharmony_cisnd_wavefront_midi_disable_virtual (snd_wavefront_card_t *card)
47262306a36Sopenharmony_ci
47362306a36Sopenharmony_ci{
47462306a36Sopenharmony_ci	unsigned long flags;
47562306a36Sopenharmony_ci
47662306a36Sopenharmony_ci	spin_lock_irqsave (&card->wavefront.midi.virtual, flags);
47762306a36Sopenharmony_ci	// snd_wavefront_midi_input_close (card->ics2115_external_rmidi);
47862306a36Sopenharmony_ci	// snd_wavefront_midi_output_close (card->ics2115_external_rmidi);
47962306a36Sopenharmony_ci	card->wavefront.midi.isvirtual = 0;
48062306a36Sopenharmony_ci	spin_unlock_irqrestore (&card->wavefront.midi.virtual, flags);
48162306a36Sopenharmony_ci}
48262306a36Sopenharmony_ci
48362306a36Sopenharmony_ciint
48462306a36Sopenharmony_cisnd_wavefront_midi_start (snd_wavefront_card_t *card)
48562306a36Sopenharmony_ci
48662306a36Sopenharmony_ci{
48762306a36Sopenharmony_ci	int ok, i;
48862306a36Sopenharmony_ci	unsigned char rbuf[4], wbuf[4];
48962306a36Sopenharmony_ci	snd_wavefront_t *dev;
49062306a36Sopenharmony_ci	snd_wavefront_midi_t *midi;
49162306a36Sopenharmony_ci
49262306a36Sopenharmony_ci	dev = &card->wavefront;
49362306a36Sopenharmony_ci	midi = &dev->midi;
49462306a36Sopenharmony_ci
49562306a36Sopenharmony_ci	/* The ICS2115 MPU-401 interface doesn't do anything
49662306a36Sopenharmony_ci	   until its set into UART mode.
49762306a36Sopenharmony_ci	*/
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_ci	/* XXX fix me - no hard timing loops allowed! */
50062306a36Sopenharmony_ci
50162306a36Sopenharmony_ci	for (i = 0; i < 30000 && !output_ready (midi); i++);
50262306a36Sopenharmony_ci
50362306a36Sopenharmony_ci	if (!output_ready (midi)) {
50462306a36Sopenharmony_ci		snd_printk ("MIDI interface not ready for command\n");
50562306a36Sopenharmony_ci		return -1;
50662306a36Sopenharmony_ci	}
50762306a36Sopenharmony_ci
50862306a36Sopenharmony_ci	/* Any interrupts received from now on
50962306a36Sopenharmony_ci	   are owned by the MIDI side of things.
51062306a36Sopenharmony_ci	*/
51162306a36Sopenharmony_ci
51262306a36Sopenharmony_ci	dev->interrupts_are_midi = 1;
51362306a36Sopenharmony_ci
51462306a36Sopenharmony_ci	outb (UART_MODE_ON, midi->mpu_command_port);
51562306a36Sopenharmony_ci
51662306a36Sopenharmony_ci	for (ok = 0, i = 50000; i > 0 && !ok; i--) {
51762306a36Sopenharmony_ci		if (input_avail (midi)) {
51862306a36Sopenharmony_ci			if (read_data (midi) == MPU_ACK) {
51962306a36Sopenharmony_ci				ok = 1;
52062306a36Sopenharmony_ci				break;
52162306a36Sopenharmony_ci			}
52262306a36Sopenharmony_ci		}
52362306a36Sopenharmony_ci	}
52462306a36Sopenharmony_ci
52562306a36Sopenharmony_ci	if (!ok) {
52662306a36Sopenharmony_ci		snd_printk ("cannot set UART mode for MIDI interface");
52762306a36Sopenharmony_ci		dev->interrupts_are_midi = 0;
52862306a36Sopenharmony_ci		return -1;
52962306a36Sopenharmony_ci	}
53062306a36Sopenharmony_ci
53162306a36Sopenharmony_ci	/* Route external MIDI to WaveFront synth (by default) */
53262306a36Sopenharmony_ci
53362306a36Sopenharmony_ci	if (snd_wavefront_cmd (dev, WFC_MISYNTH_ON, rbuf, wbuf)) {
53462306a36Sopenharmony_ci		snd_printk ("can't enable MIDI-IN-2-synth routing.\n");
53562306a36Sopenharmony_ci		/* XXX error ? */
53662306a36Sopenharmony_ci	}
53762306a36Sopenharmony_ci
53862306a36Sopenharmony_ci	/* Turn on Virtual MIDI, but first *always* turn it off,
53962306a36Sopenharmony_ci	   since otherwise consecutive reloads of the driver will
54062306a36Sopenharmony_ci	   never cause the hardware to generate the initial "internal" or
54162306a36Sopenharmony_ci	   "external" source bytes in the MIDI data stream. This
54262306a36Sopenharmony_ci	   is pretty important, since the internal hardware generally will
54362306a36Sopenharmony_ci	   be used to generate none or very little MIDI output, and
54462306a36Sopenharmony_ci	   thus the only source of MIDI data is actually external. Without
54562306a36Sopenharmony_ci	   the switch bytes, the driver will think it all comes from
54662306a36Sopenharmony_ci	   the internal interface. Duh.
54762306a36Sopenharmony_ci	*/
54862306a36Sopenharmony_ci
54962306a36Sopenharmony_ci	if (snd_wavefront_cmd (dev, WFC_VMIDI_OFF, rbuf, wbuf)) {
55062306a36Sopenharmony_ci		snd_printk ("virtual MIDI mode not disabled\n");
55162306a36Sopenharmony_ci		return 0; /* We're OK, but missing the external MIDI dev */
55262306a36Sopenharmony_ci	}
55362306a36Sopenharmony_ci
55462306a36Sopenharmony_ci	snd_wavefront_midi_enable_virtual (card);
55562306a36Sopenharmony_ci
55662306a36Sopenharmony_ci	if (snd_wavefront_cmd (dev, WFC_VMIDI_ON, rbuf, wbuf)) {
55762306a36Sopenharmony_ci		snd_printk ("cannot enable virtual MIDI mode.\n");
55862306a36Sopenharmony_ci		snd_wavefront_midi_disable_virtual (card);
55962306a36Sopenharmony_ci	}
56062306a36Sopenharmony_ci	return 0;
56162306a36Sopenharmony_ci}
56262306a36Sopenharmony_ci
56362306a36Sopenharmony_ciconst struct snd_rawmidi_ops snd_wavefront_midi_output =
56462306a36Sopenharmony_ci{
56562306a36Sopenharmony_ci	.open =		snd_wavefront_midi_output_open,
56662306a36Sopenharmony_ci	.close =	snd_wavefront_midi_output_close,
56762306a36Sopenharmony_ci	.trigger =	snd_wavefront_midi_output_trigger,
56862306a36Sopenharmony_ci};
56962306a36Sopenharmony_ci
57062306a36Sopenharmony_ciconst struct snd_rawmidi_ops snd_wavefront_midi_input =
57162306a36Sopenharmony_ci{
57262306a36Sopenharmony_ci	.open =		snd_wavefront_midi_input_open,
57362306a36Sopenharmony_ci	.close =	snd_wavefront_midi_input_close,
57462306a36Sopenharmony_ci	.trigger =	snd_wavefront_midi_input_trigger,
57562306a36Sopenharmony_ci};
57662306a36Sopenharmony_ci
577