18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (C) by Paul Barton-Davis 1998-1999
48c2ecf20Sopenharmony_ci */
58c2ecf20Sopenharmony_ci
68c2ecf20Sopenharmony_ci/* The low level driver for the WaveFront ICS2115 MIDI interface(s)
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci * Note that there is also an MPU-401 emulation (actually, a UART-401
98c2ecf20Sopenharmony_ci * emulation) on the CS4232 on the Tropez and Tropez Plus. This code
108c2ecf20Sopenharmony_ci * has nothing to do with that interface at all.
118c2ecf20Sopenharmony_ci *
128c2ecf20Sopenharmony_ci * The interface is essentially just a UART-401, but is has the
138c2ecf20Sopenharmony_ci * interesting property of supporting what Turtle Beach called
148c2ecf20Sopenharmony_ci * "Virtual MIDI" mode. In this mode, there are effectively *two*
158c2ecf20Sopenharmony_ci * MIDI buses accessible via the interface, one that is routed
168c2ecf20Sopenharmony_ci * solely to/from the external WaveFront synthesizer and the other
178c2ecf20Sopenharmony_ci * corresponding to the pin/socket connector used to link external
188c2ecf20Sopenharmony_ci * MIDI devices to the board.
198c2ecf20Sopenharmony_ci *
208c2ecf20Sopenharmony_ci * This driver fully supports this mode, allowing two distinct MIDI
218c2ecf20Sopenharmony_ci * busses to be used completely independently, giving 32 channels of
228c2ecf20Sopenharmony_ci * MIDI routing, 16 to the WaveFront synth and 16 to the external MIDI
238c2ecf20Sopenharmony_ci * bus. The devices are named /dev/snd/midiCnD0 and /dev/snd/midiCnD1,
248c2ecf20Sopenharmony_ci * where `n' is the card number. Note that the device numbers may be
258c2ecf20Sopenharmony_ci * something other than 0 and 1 if the CS4232 UART/MPU-401 interface
268c2ecf20Sopenharmony_ci * is enabled.
278c2ecf20Sopenharmony_ci *
288c2ecf20Sopenharmony_ci * Switching between the two is accomplished externally by the driver
298c2ecf20Sopenharmony_ci * using the two otherwise unused MIDI bytes. See the code for more details.
308c2ecf20Sopenharmony_ci *
318c2ecf20Sopenharmony_ci * NOTE: VIRTUAL MIDI MODE IS ON BY DEFAULT (see lowlevel/isa/wavefront.c)
328c2ecf20Sopenharmony_ci *
338c2ecf20Sopenharmony_ci * The main reason to turn off Virtual MIDI mode is when you want to
348c2ecf20Sopenharmony_ci * tightly couple the WaveFront synth with an external MIDI
358c2ecf20Sopenharmony_ci * device. You won't be able to distinguish the source of any MIDI
368c2ecf20Sopenharmony_ci * data except via SysEx ID, but thats probably OK, since for the most
378c2ecf20Sopenharmony_ci * part, the WaveFront won't be sending any MIDI data at all.
388c2ecf20Sopenharmony_ci *
398c2ecf20Sopenharmony_ci * The main reason to turn on Virtual MIDI Mode is to provide two
408c2ecf20Sopenharmony_ci * completely independent 16-channel MIDI buses, one to the
418c2ecf20Sopenharmony_ci * WaveFront and one to any external MIDI devices. Given the 32
428c2ecf20Sopenharmony_ci * voice nature of the WaveFront, its pretty easy to find a use
438c2ecf20Sopenharmony_ci * for all 16 channels driving just that synth.
448c2ecf20Sopenharmony_ci *
458c2ecf20Sopenharmony_ci */
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci#include <linux/io.h>
488c2ecf20Sopenharmony_ci#include <linux/init.h>
498c2ecf20Sopenharmony_ci#include <linux/time.h>
508c2ecf20Sopenharmony_ci#include <linux/wait.h>
518c2ecf20Sopenharmony_ci#include <sound/core.h>
528c2ecf20Sopenharmony_ci#include <sound/snd_wavefront.h>
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_cistatic inline int
558c2ecf20Sopenharmony_ciwf_mpu_status (snd_wavefront_midi_t *midi)
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci{
588c2ecf20Sopenharmony_ci	return inb (midi->mpu_status_port);
598c2ecf20Sopenharmony_ci}
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_cistatic inline int
628c2ecf20Sopenharmony_ciinput_avail (snd_wavefront_midi_t *midi)
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci{
658c2ecf20Sopenharmony_ci	return !(wf_mpu_status(midi) & INPUT_AVAIL);
668c2ecf20Sopenharmony_ci}
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_cistatic inline int
698c2ecf20Sopenharmony_cioutput_ready (snd_wavefront_midi_t *midi)
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci{
728c2ecf20Sopenharmony_ci	return !(wf_mpu_status(midi) & OUTPUT_READY);
738c2ecf20Sopenharmony_ci}
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_cistatic inline int
768c2ecf20Sopenharmony_ciread_data (snd_wavefront_midi_t *midi)
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci{
798c2ecf20Sopenharmony_ci	return inb (midi->mpu_data_port);
808c2ecf20Sopenharmony_ci}
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_cistatic inline void
838c2ecf20Sopenharmony_ciwrite_data (snd_wavefront_midi_t *midi, unsigned char byte)
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci{
868c2ecf20Sopenharmony_ci	outb (byte, midi->mpu_data_port);
878c2ecf20Sopenharmony_ci}
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_cistatic snd_wavefront_midi_t *
908c2ecf20Sopenharmony_ciget_wavefront_midi (struct snd_rawmidi_substream *substream)
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci{
938c2ecf20Sopenharmony_ci	struct snd_card *card;
948c2ecf20Sopenharmony_ci	snd_wavefront_card_t *acard;
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci	if (substream == NULL || substream->rmidi == NULL)
978c2ecf20Sopenharmony_ci	        return NULL;
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci	card = substream->rmidi->card;
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci	if (card == NULL)
1028c2ecf20Sopenharmony_ci	        return NULL;
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci	if (card->private_data == NULL)
1058c2ecf20Sopenharmony_ci 	        return NULL;
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci	acard = card->private_data;
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci	return &acard->wavefront.midi;
1108c2ecf20Sopenharmony_ci}
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_cistatic void snd_wavefront_midi_output_write(snd_wavefront_card_t *card)
1138c2ecf20Sopenharmony_ci{
1148c2ecf20Sopenharmony_ci	snd_wavefront_midi_t *midi = &card->wavefront.midi;
1158c2ecf20Sopenharmony_ci	snd_wavefront_mpu_id  mpu;
1168c2ecf20Sopenharmony_ci	unsigned long flags;
1178c2ecf20Sopenharmony_ci	unsigned char midi_byte;
1188c2ecf20Sopenharmony_ci	int max = 256, mask = 1;
1198c2ecf20Sopenharmony_ci	int timeout;
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci	/* Its not OK to try to change the status of "virtuality" of
1228c2ecf20Sopenharmony_ci	   the MIDI interface while we're outputting stuff.  See
1238c2ecf20Sopenharmony_ci	   snd_wavefront_midi_{enable,disable}_virtual () for the
1248c2ecf20Sopenharmony_ci	   other half of this.
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci	   The first loop attempts to flush any data from the
1278c2ecf20Sopenharmony_ci	   current output device, and then the second
1288c2ecf20Sopenharmony_ci	   emits the switch byte (if necessary), and starts
1298c2ecf20Sopenharmony_ci	   outputting data for the output device currently in use.
1308c2ecf20Sopenharmony_ci	*/
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci	if (midi->substream_output[midi->output_mpu] == NULL) {
1338c2ecf20Sopenharmony_ci		goto __second;
1348c2ecf20Sopenharmony_ci	}
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci	while (max > 0) {
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci		/* XXX fix me - no hard timing loops allowed! */
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_ci		for (timeout = 30000; timeout > 0; timeout--) {
1418c2ecf20Sopenharmony_ci			if (output_ready (midi))
1428c2ecf20Sopenharmony_ci				break;
1438c2ecf20Sopenharmony_ci		}
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci		spin_lock_irqsave (&midi->virtual, flags);
1468c2ecf20Sopenharmony_ci		if ((midi->mode[midi->output_mpu] & MPU401_MODE_OUTPUT) == 0) {
1478c2ecf20Sopenharmony_ci			spin_unlock_irqrestore (&midi->virtual, flags);
1488c2ecf20Sopenharmony_ci			goto __second;
1498c2ecf20Sopenharmony_ci		}
1508c2ecf20Sopenharmony_ci		if (output_ready (midi)) {
1518c2ecf20Sopenharmony_ci			if (snd_rawmidi_transmit(midi->substream_output[midi->output_mpu], &midi_byte, 1) == 1) {
1528c2ecf20Sopenharmony_ci				if (!midi->isvirtual ||
1538c2ecf20Sopenharmony_ci					(midi_byte != WF_INTERNAL_SWITCH &&
1548c2ecf20Sopenharmony_ci					 midi_byte != WF_EXTERNAL_SWITCH))
1558c2ecf20Sopenharmony_ci					write_data(midi, midi_byte);
1568c2ecf20Sopenharmony_ci				max--;
1578c2ecf20Sopenharmony_ci			} else {
1588c2ecf20Sopenharmony_ci				if (midi->istimer) {
1598c2ecf20Sopenharmony_ci					if (--midi->istimer <= 0)
1608c2ecf20Sopenharmony_ci						del_timer(&midi->timer);
1618c2ecf20Sopenharmony_ci				}
1628c2ecf20Sopenharmony_ci				midi->mode[midi->output_mpu] &= ~MPU401_MODE_OUTPUT_TRIGGER;
1638c2ecf20Sopenharmony_ci				spin_unlock_irqrestore (&midi->virtual, flags);
1648c2ecf20Sopenharmony_ci				goto __second;
1658c2ecf20Sopenharmony_ci			}
1668c2ecf20Sopenharmony_ci		} else {
1678c2ecf20Sopenharmony_ci			spin_unlock_irqrestore (&midi->virtual, flags);
1688c2ecf20Sopenharmony_ci			return;
1698c2ecf20Sopenharmony_ci		}
1708c2ecf20Sopenharmony_ci		spin_unlock_irqrestore (&midi->virtual, flags);
1718c2ecf20Sopenharmony_ci	}
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_ci      __second:
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci	if (midi->substream_output[!midi->output_mpu] == NULL) {
1768c2ecf20Sopenharmony_ci		return;
1778c2ecf20Sopenharmony_ci	}
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_ci	while (max > 0) {
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ci		/* XXX fix me - no hard timing loops allowed! */
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci		for (timeout = 30000; timeout > 0; timeout--) {
1848c2ecf20Sopenharmony_ci			if (output_ready (midi))
1858c2ecf20Sopenharmony_ci				break;
1868c2ecf20Sopenharmony_ci		}
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_ci		spin_lock_irqsave (&midi->virtual, flags);
1898c2ecf20Sopenharmony_ci		if (!midi->isvirtual)
1908c2ecf20Sopenharmony_ci			mask = 0;
1918c2ecf20Sopenharmony_ci		mpu = midi->output_mpu ^ mask;
1928c2ecf20Sopenharmony_ci		mask = 0;	/* don't invert the value from now */
1938c2ecf20Sopenharmony_ci		if ((midi->mode[mpu] & MPU401_MODE_OUTPUT) == 0) {
1948c2ecf20Sopenharmony_ci			spin_unlock_irqrestore (&midi->virtual, flags);
1958c2ecf20Sopenharmony_ci			return;
1968c2ecf20Sopenharmony_ci		}
1978c2ecf20Sopenharmony_ci		if (snd_rawmidi_transmit_empty(midi->substream_output[mpu]))
1988c2ecf20Sopenharmony_ci			goto __timer;
1998c2ecf20Sopenharmony_ci		if (output_ready (midi)) {
2008c2ecf20Sopenharmony_ci			if (mpu != midi->output_mpu) {
2018c2ecf20Sopenharmony_ci				write_data(midi, mpu == internal_mpu ?
2028c2ecf20Sopenharmony_ci							WF_INTERNAL_SWITCH :
2038c2ecf20Sopenharmony_ci							WF_EXTERNAL_SWITCH);
2048c2ecf20Sopenharmony_ci				midi->output_mpu = mpu;
2058c2ecf20Sopenharmony_ci			} else if (snd_rawmidi_transmit(midi->substream_output[mpu], &midi_byte, 1) == 1) {
2068c2ecf20Sopenharmony_ci				if (!midi->isvirtual ||
2078c2ecf20Sopenharmony_ci					(midi_byte != WF_INTERNAL_SWITCH &&
2088c2ecf20Sopenharmony_ci					 midi_byte != WF_EXTERNAL_SWITCH))
2098c2ecf20Sopenharmony_ci					write_data(midi, midi_byte);
2108c2ecf20Sopenharmony_ci				max--;
2118c2ecf20Sopenharmony_ci			} else {
2128c2ecf20Sopenharmony_ci			      __timer:
2138c2ecf20Sopenharmony_ci				if (midi->istimer) {
2148c2ecf20Sopenharmony_ci					if (--midi->istimer <= 0)
2158c2ecf20Sopenharmony_ci						del_timer(&midi->timer);
2168c2ecf20Sopenharmony_ci				}
2178c2ecf20Sopenharmony_ci				midi->mode[mpu] &= ~MPU401_MODE_OUTPUT_TRIGGER;
2188c2ecf20Sopenharmony_ci				spin_unlock_irqrestore (&midi->virtual, flags);
2198c2ecf20Sopenharmony_ci				return;
2208c2ecf20Sopenharmony_ci			}
2218c2ecf20Sopenharmony_ci		} else {
2228c2ecf20Sopenharmony_ci			spin_unlock_irqrestore (&midi->virtual, flags);
2238c2ecf20Sopenharmony_ci			return;
2248c2ecf20Sopenharmony_ci		}
2258c2ecf20Sopenharmony_ci		spin_unlock_irqrestore (&midi->virtual, flags);
2268c2ecf20Sopenharmony_ci	}
2278c2ecf20Sopenharmony_ci}
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_cistatic int snd_wavefront_midi_input_open(struct snd_rawmidi_substream *substream)
2308c2ecf20Sopenharmony_ci{
2318c2ecf20Sopenharmony_ci	unsigned long flags;
2328c2ecf20Sopenharmony_ci	snd_wavefront_midi_t *midi;
2338c2ecf20Sopenharmony_ci	snd_wavefront_mpu_id mpu;
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_ci	if (snd_BUG_ON(!substream || !substream->rmidi))
2368c2ecf20Sopenharmony_ci		return -ENXIO;
2378c2ecf20Sopenharmony_ci	if (snd_BUG_ON(!substream->rmidi->private_data))
2388c2ecf20Sopenharmony_ci		return -ENXIO;
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci	mpu = *((snd_wavefront_mpu_id *) substream->rmidi->private_data);
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ci	if ((midi = get_wavefront_midi (substream)) == NULL)
2438c2ecf20Sopenharmony_ci	        return -EIO;
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_ci	spin_lock_irqsave (&midi->open, flags);
2468c2ecf20Sopenharmony_ci	midi->mode[mpu] |= MPU401_MODE_INPUT;
2478c2ecf20Sopenharmony_ci	midi->substream_input[mpu] = substream;
2488c2ecf20Sopenharmony_ci	spin_unlock_irqrestore (&midi->open, flags);
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_ci	return 0;
2518c2ecf20Sopenharmony_ci}
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_cistatic int snd_wavefront_midi_output_open(struct snd_rawmidi_substream *substream)
2548c2ecf20Sopenharmony_ci{
2558c2ecf20Sopenharmony_ci	unsigned long flags;
2568c2ecf20Sopenharmony_ci	snd_wavefront_midi_t *midi;
2578c2ecf20Sopenharmony_ci	snd_wavefront_mpu_id mpu;
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_ci	if (snd_BUG_ON(!substream || !substream->rmidi))
2608c2ecf20Sopenharmony_ci		return -ENXIO;
2618c2ecf20Sopenharmony_ci	if (snd_BUG_ON(!substream->rmidi->private_data))
2628c2ecf20Sopenharmony_ci		return -ENXIO;
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_ci	mpu = *((snd_wavefront_mpu_id *) substream->rmidi->private_data);
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_ci	if ((midi = get_wavefront_midi (substream)) == NULL)
2678c2ecf20Sopenharmony_ci	        return -EIO;
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_ci	spin_lock_irqsave (&midi->open, flags);
2708c2ecf20Sopenharmony_ci	midi->mode[mpu] |= MPU401_MODE_OUTPUT;
2718c2ecf20Sopenharmony_ci	midi->substream_output[mpu] = substream;
2728c2ecf20Sopenharmony_ci	spin_unlock_irqrestore (&midi->open, flags);
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_ci	return 0;
2758c2ecf20Sopenharmony_ci}
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_cistatic int snd_wavefront_midi_input_close(struct snd_rawmidi_substream *substream)
2788c2ecf20Sopenharmony_ci{
2798c2ecf20Sopenharmony_ci	unsigned long flags;
2808c2ecf20Sopenharmony_ci	snd_wavefront_midi_t *midi;
2818c2ecf20Sopenharmony_ci	snd_wavefront_mpu_id mpu;
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_ci	if (snd_BUG_ON(!substream || !substream->rmidi))
2848c2ecf20Sopenharmony_ci		return -ENXIO;
2858c2ecf20Sopenharmony_ci	if (snd_BUG_ON(!substream->rmidi->private_data))
2868c2ecf20Sopenharmony_ci		return -ENXIO;
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_ci	mpu = *((snd_wavefront_mpu_id *) substream->rmidi->private_data);
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_ci	if ((midi = get_wavefront_midi (substream)) == NULL)
2918c2ecf20Sopenharmony_ci	        return -EIO;
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_ci	spin_lock_irqsave (&midi->open, flags);
2948c2ecf20Sopenharmony_ci	midi->mode[mpu] &= ~MPU401_MODE_INPUT;
2958c2ecf20Sopenharmony_ci	spin_unlock_irqrestore (&midi->open, flags);
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_ci	return 0;
2988c2ecf20Sopenharmony_ci}
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_cistatic int snd_wavefront_midi_output_close(struct snd_rawmidi_substream *substream)
3018c2ecf20Sopenharmony_ci{
3028c2ecf20Sopenharmony_ci	unsigned long flags;
3038c2ecf20Sopenharmony_ci	snd_wavefront_midi_t *midi;
3048c2ecf20Sopenharmony_ci	snd_wavefront_mpu_id mpu;
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_ci	if (snd_BUG_ON(!substream || !substream->rmidi))
3078c2ecf20Sopenharmony_ci		return -ENXIO;
3088c2ecf20Sopenharmony_ci	if (snd_BUG_ON(!substream->rmidi->private_data))
3098c2ecf20Sopenharmony_ci		return -ENXIO;
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_ci	mpu = *((snd_wavefront_mpu_id *) substream->rmidi->private_data);
3128c2ecf20Sopenharmony_ci
3138c2ecf20Sopenharmony_ci	if ((midi = get_wavefront_midi (substream)) == NULL)
3148c2ecf20Sopenharmony_ci	        return -EIO;
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_ci	spin_lock_irqsave (&midi->open, flags);
3178c2ecf20Sopenharmony_ci	midi->mode[mpu] &= ~MPU401_MODE_OUTPUT;
3188c2ecf20Sopenharmony_ci	spin_unlock_irqrestore (&midi->open, flags);
3198c2ecf20Sopenharmony_ci	return 0;
3208c2ecf20Sopenharmony_ci}
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_cistatic void snd_wavefront_midi_input_trigger(struct snd_rawmidi_substream *substream, int up)
3238c2ecf20Sopenharmony_ci{
3248c2ecf20Sopenharmony_ci	unsigned long flags;
3258c2ecf20Sopenharmony_ci	snd_wavefront_midi_t *midi;
3268c2ecf20Sopenharmony_ci	snd_wavefront_mpu_id mpu;
3278c2ecf20Sopenharmony_ci
3288c2ecf20Sopenharmony_ci	if (substream == NULL || substream->rmidi == NULL)
3298c2ecf20Sopenharmony_ci	        return;
3308c2ecf20Sopenharmony_ci
3318c2ecf20Sopenharmony_ci	if (substream->rmidi->private_data == NULL)
3328c2ecf20Sopenharmony_ci	        return;
3338c2ecf20Sopenharmony_ci
3348c2ecf20Sopenharmony_ci	mpu = *((snd_wavefront_mpu_id *) substream->rmidi->private_data);
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_ci	if ((midi = get_wavefront_midi (substream)) == NULL) {
3378c2ecf20Sopenharmony_ci		return;
3388c2ecf20Sopenharmony_ci	}
3398c2ecf20Sopenharmony_ci
3408c2ecf20Sopenharmony_ci	spin_lock_irqsave (&midi->virtual, flags);
3418c2ecf20Sopenharmony_ci	if (up) {
3428c2ecf20Sopenharmony_ci		midi->mode[mpu] |= MPU401_MODE_INPUT_TRIGGER;
3438c2ecf20Sopenharmony_ci	} else {
3448c2ecf20Sopenharmony_ci		midi->mode[mpu] &= ~MPU401_MODE_INPUT_TRIGGER;
3458c2ecf20Sopenharmony_ci	}
3468c2ecf20Sopenharmony_ci	spin_unlock_irqrestore (&midi->virtual, flags);
3478c2ecf20Sopenharmony_ci}
3488c2ecf20Sopenharmony_ci
3498c2ecf20Sopenharmony_cistatic void snd_wavefront_midi_output_timer(struct timer_list *t)
3508c2ecf20Sopenharmony_ci{
3518c2ecf20Sopenharmony_ci	snd_wavefront_midi_t *midi = from_timer(midi, t, timer);
3528c2ecf20Sopenharmony_ci	snd_wavefront_card_t *card = midi->timer_card;
3538c2ecf20Sopenharmony_ci	unsigned long flags;
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_ci	spin_lock_irqsave (&midi->virtual, flags);
3568c2ecf20Sopenharmony_ci	mod_timer(&midi->timer, 1 + jiffies);
3578c2ecf20Sopenharmony_ci	spin_unlock_irqrestore (&midi->virtual, flags);
3588c2ecf20Sopenharmony_ci	snd_wavefront_midi_output_write(card);
3598c2ecf20Sopenharmony_ci}
3608c2ecf20Sopenharmony_ci
3618c2ecf20Sopenharmony_cistatic void snd_wavefront_midi_output_trigger(struct snd_rawmidi_substream *substream, int up)
3628c2ecf20Sopenharmony_ci{
3638c2ecf20Sopenharmony_ci	unsigned long flags;
3648c2ecf20Sopenharmony_ci	snd_wavefront_midi_t *midi;
3658c2ecf20Sopenharmony_ci	snd_wavefront_mpu_id mpu;
3668c2ecf20Sopenharmony_ci
3678c2ecf20Sopenharmony_ci	if (substream == NULL || substream->rmidi == NULL)
3688c2ecf20Sopenharmony_ci	        return;
3698c2ecf20Sopenharmony_ci
3708c2ecf20Sopenharmony_ci	if (substream->rmidi->private_data == NULL)
3718c2ecf20Sopenharmony_ci	        return;
3728c2ecf20Sopenharmony_ci
3738c2ecf20Sopenharmony_ci	mpu = *((snd_wavefront_mpu_id *) substream->rmidi->private_data);
3748c2ecf20Sopenharmony_ci
3758c2ecf20Sopenharmony_ci	if ((midi = get_wavefront_midi (substream)) == NULL) {
3768c2ecf20Sopenharmony_ci		return;
3778c2ecf20Sopenharmony_ci	}
3788c2ecf20Sopenharmony_ci
3798c2ecf20Sopenharmony_ci	spin_lock_irqsave (&midi->virtual, flags);
3808c2ecf20Sopenharmony_ci	if (up) {
3818c2ecf20Sopenharmony_ci		if ((midi->mode[mpu] & MPU401_MODE_OUTPUT_TRIGGER) == 0) {
3828c2ecf20Sopenharmony_ci			if (!midi->istimer) {
3838c2ecf20Sopenharmony_ci				timer_setup(&midi->timer,
3848c2ecf20Sopenharmony_ci					    snd_wavefront_midi_output_timer,
3858c2ecf20Sopenharmony_ci					    0);
3868c2ecf20Sopenharmony_ci				mod_timer(&midi->timer, 1 + jiffies);
3878c2ecf20Sopenharmony_ci			}
3888c2ecf20Sopenharmony_ci			midi->istimer++;
3898c2ecf20Sopenharmony_ci			midi->mode[mpu] |= MPU401_MODE_OUTPUT_TRIGGER;
3908c2ecf20Sopenharmony_ci		}
3918c2ecf20Sopenharmony_ci	} else {
3928c2ecf20Sopenharmony_ci		midi->mode[mpu] &= ~MPU401_MODE_OUTPUT_TRIGGER;
3938c2ecf20Sopenharmony_ci	}
3948c2ecf20Sopenharmony_ci	spin_unlock_irqrestore (&midi->virtual, flags);
3958c2ecf20Sopenharmony_ci
3968c2ecf20Sopenharmony_ci	if (up)
3978c2ecf20Sopenharmony_ci		snd_wavefront_midi_output_write((snd_wavefront_card_t *)substream->rmidi->card->private_data);
3988c2ecf20Sopenharmony_ci}
3998c2ecf20Sopenharmony_ci
4008c2ecf20Sopenharmony_civoid
4018c2ecf20Sopenharmony_cisnd_wavefront_midi_interrupt (snd_wavefront_card_t *card)
4028c2ecf20Sopenharmony_ci
4038c2ecf20Sopenharmony_ci{
4048c2ecf20Sopenharmony_ci	unsigned long flags;
4058c2ecf20Sopenharmony_ci	snd_wavefront_midi_t *midi;
4068c2ecf20Sopenharmony_ci	static struct snd_rawmidi_substream *substream = NULL;
4078c2ecf20Sopenharmony_ci	static int mpu = external_mpu;
4088c2ecf20Sopenharmony_ci	int max = 128;
4098c2ecf20Sopenharmony_ci	unsigned char byte;
4108c2ecf20Sopenharmony_ci
4118c2ecf20Sopenharmony_ci	midi = &card->wavefront.midi;
4128c2ecf20Sopenharmony_ci
4138c2ecf20Sopenharmony_ci	if (!input_avail (midi)) { /* not for us */
4148c2ecf20Sopenharmony_ci		snd_wavefront_midi_output_write(card);
4158c2ecf20Sopenharmony_ci		return;
4168c2ecf20Sopenharmony_ci	}
4178c2ecf20Sopenharmony_ci
4188c2ecf20Sopenharmony_ci	spin_lock_irqsave (&midi->virtual, flags);
4198c2ecf20Sopenharmony_ci	while (--max) {
4208c2ecf20Sopenharmony_ci
4218c2ecf20Sopenharmony_ci		if (input_avail (midi)) {
4228c2ecf20Sopenharmony_ci			byte = read_data (midi);
4238c2ecf20Sopenharmony_ci
4248c2ecf20Sopenharmony_ci			if (midi->isvirtual) {
4258c2ecf20Sopenharmony_ci				if (byte == WF_EXTERNAL_SWITCH) {
4268c2ecf20Sopenharmony_ci					substream = midi->substream_input[external_mpu];
4278c2ecf20Sopenharmony_ci					mpu = external_mpu;
4288c2ecf20Sopenharmony_ci				} else if (byte == WF_INTERNAL_SWITCH) {
4298c2ecf20Sopenharmony_ci					substream = midi->substream_output[internal_mpu];
4308c2ecf20Sopenharmony_ci					mpu = internal_mpu;
4318c2ecf20Sopenharmony_ci				} /* else just leave it as it is */
4328c2ecf20Sopenharmony_ci			} else {
4338c2ecf20Sopenharmony_ci				substream = midi->substream_input[internal_mpu];
4348c2ecf20Sopenharmony_ci				mpu = internal_mpu;
4358c2ecf20Sopenharmony_ci			}
4368c2ecf20Sopenharmony_ci
4378c2ecf20Sopenharmony_ci			if (substream == NULL) {
4388c2ecf20Sopenharmony_ci				continue;
4398c2ecf20Sopenharmony_ci			}
4408c2ecf20Sopenharmony_ci
4418c2ecf20Sopenharmony_ci			if (midi->mode[mpu] & MPU401_MODE_INPUT_TRIGGER) {
4428c2ecf20Sopenharmony_ci				snd_rawmidi_receive(substream, &byte, 1);
4438c2ecf20Sopenharmony_ci			}
4448c2ecf20Sopenharmony_ci		} else {
4458c2ecf20Sopenharmony_ci			break;
4468c2ecf20Sopenharmony_ci		}
4478c2ecf20Sopenharmony_ci	}
4488c2ecf20Sopenharmony_ci	spin_unlock_irqrestore (&midi->virtual, flags);
4498c2ecf20Sopenharmony_ci
4508c2ecf20Sopenharmony_ci	snd_wavefront_midi_output_write(card);
4518c2ecf20Sopenharmony_ci}
4528c2ecf20Sopenharmony_ci
4538c2ecf20Sopenharmony_civoid
4548c2ecf20Sopenharmony_cisnd_wavefront_midi_enable_virtual (snd_wavefront_card_t *card)
4558c2ecf20Sopenharmony_ci
4568c2ecf20Sopenharmony_ci{
4578c2ecf20Sopenharmony_ci	unsigned long flags;
4588c2ecf20Sopenharmony_ci
4598c2ecf20Sopenharmony_ci	spin_lock_irqsave (&card->wavefront.midi.virtual, flags);
4608c2ecf20Sopenharmony_ci	card->wavefront.midi.isvirtual = 1;
4618c2ecf20Sopenharmony_ci	card->wavefront.midi.output_mpu = internal_mpu;
4628c2ecf20Sopenharmony_ci	card->wavefront.midi.input_mpu = internal_mpu;
4638c2ecf20Sopenharmony_ci	spin_unlock_irqrestore (&card->wavefront.midi.virtual, flags);
4648c2ecf20Sopenharmony_ci}
4658c2ecf20Sopenharmony_ci
4668c2ecf20Sopenharmony_civoid
4678c2ecf20Sopenharmony_cisnd_wavefront_midi_disable_virtual (snd_wavefront_card_t *card)
4688c2ecf20Sopenharmony_ci
4698c2ecf20Sopenharmony_ci{
4708c2ecf20Sopenharmony_ci	unsigned long flags;
4718c2ecf20Sopenharmony_ci
4728c2ecf20Sopenharmony_ci	spin_lock_irqsave (&card->wavefront.midi.virtual, flags);
4738c2ecf20Sopenharmony_ci	// snd_wavefront_midi_input_close (card->ics2115_external_rmidi);
4748c2ecf20Sopenharmony_ci	// snd_wavefront_midi_output_close (card->ics2115_external_rmidi);
4758c2ecf20Sopenharmony_ci	card->wavefront.midi.isvirtual = 0;
4768c2ecf20Sopenharmony_ci	spin_unlock_irqrestore (&card->wavefront.midi.virtual, flags);
4778c2ecf20Sopenharmony_ci}
4788c2ecf20Sopenharmony_ci
4798c2ecf20Sopenharmony_ciint
4808c2ecf20Sopenharmony_cisnd_wavefront_midi_start (snd_wavefront_card_t *card)
4818c2ecf20Sopenharmony_ci
4828c2ecf20Sopenharmony_ci{
4838c2ecf20Sopenharmony_ci	int ok, i;
4848c2ecf20Sopenharmony_ci	unsigned char rbuf[4], wbuf[4];
4858c2ecf20Sopenharmony_ci	snd_wavefront_t *dev;
4868c2ecf20Sopenharmony_ci	snd_wavefront_midi_t *midi;
4878c2ecf20Sopenharmony_ci
4888c2ecf20Sopenharmony_ci	dev = &card->wavefront;
4898c2ecf20Sopenharmony_ci	midi = &dev->midi;
4908c2ecf20Sopenharmony_ci
4918c2ecf20Sopenharmony_ci	/* The ICS2115 MPU-401 interface doesn't do anything
4928c2ecf20Sopenharmony_ci	   until its set into UART mode.
4938c2ecf20Sopenharmony_ci	*/
4948c2ecf20Sopenharmony_ci
4958c2ecf20Sopenharmony_ci	/* XXX fix me - no hard timing loops allowed! */
4968c2ecf20Sopenharmony_ci
4978c2ecf20Sopenharmony_ci	for (i = 0; i < 30000 && !output_ready (midi); i++);
4988c2ecf20Sopenharmony_ci
4998c2ecf20Sopenharmony_ci	if (!output_ready (midi)) {
5008c2ecf20Sopenharmony_ci		snd_printk ("MIDI interface not ready for command\n");
5018c2ecf20Sopenharmony_ci		return -1;
5028c2ecf20Sopenharmony_ci	}
5038c2ecf20Sopenharmony_ci
5048c2ecf20Sopenharmony_ci	/* Any interrupts received from now on
5058c2ecf20Sopenharmony_ci	   are owned by the MIDI side of things.
5068c2ecf20Sopenharmony_ci	*/
5078c2ecf20Sopenharmony_ci
5088c2ecf20Sopenharmony_ci	dev->interrupts_are_midi = 1;
5098c2ecf20Sopenharmony_ci
5108c2ecf20Sopenharmony_ci	outb (UART_MODE_ON, midi->mpu_command_port);
5118c2ecf20Sopenharmony_ci
5128c2ecf20Sopenharmony_ci	for (ok = 0, i = 50000; i > 0 && !ok; i--) {
5138c2ecf20Sopenharmony_ci		if (input_avail (midi)) {
5148c2ecf20Sopenharmony_ci			if (read_data (midi) == MPU_ACK) {
5158c2ecf20Sopenharmony_ci				ok = 1;
5168c2ecf20Sopenharmony_ci				break;
5178c2ecf20Sopenharmony_ci			}
5188c2ecf20Sopenharmony_ci		}
5198c2ecf20Sopenharmony_ci	}
5208c2ecf20Sopenharmony_ci
5218c2ecf20Sopenharmony_ci	if (!ok) {
5228c2ecf20Sopenharmony_ci		snd_printk ("cannot set UART mode for MIDI interface");
5238c2ecf20Sopenharmony_ci		dev->interrupts_are_midi = 0;
5248c2ecf20Sopenharmony_ci		return -1;
5258c2ecf20Sopenharmony_ci	}
5268c2ecf20Sopenharmony_ci
5278c2ecf20Sopenharmony_ci	/* Route external MIDI to WaveFront synth (by default) */
5288c2ecf20Sopenharmony_ci
5298c2ecf20Sopenharmony_ci	if (snd_wavefront_cmd (dev, WFC_MISYNTH_ON, rbuf, wbuf)) {
5308c2ecf20Sopenharmony_ci		snd_printk ("can't enable MIDI-IN-2-synth routing.\n");
5318c2ecf20Sopenharmony_ci		/* XXX error ? */
5328c2ecf20Sopenharmony_ci	}
5338c2ecf20Sopenharmony_ci
5348c2ecf20Sopenharmony_ci	/* Turn on Virtual MIDI, but first *always* turn it off,
5358c2ecf20Sopenharmony_ci	   since otherwise consecutive reloads of the driver will
5368c2ecf20Sopenharmony_ci	   never cause the hardware to generate the initial "internal" or
5378c2ecf20Sopenharmony_ci	   "external" source bytes in the MIDI data stream. This
5388c2ecf20Sopenharmony_ci	   is pretty important, since the internal hardware generally will
5398c2ecf20Sopenharmony_ci	   be used to generate none or very little MIDI output, and
5408c2ecf20Sopenharmony_ci	   thus the only source of MIDI data is actually external. Without
5418c2ecf20Sopenharmony_ci	   the switch bytes, the driver will think it all comes from
5428c2ecf20Sopenharmony_ci	   the internal interface. Duh.
5438c2ecf20Sopenharmony_ci	*/
5448c2ecf20Sopenharmony_ci
5458c2ecf20Sopenharmony_ci	if (snd_wavefront_cmd (dev, WFC_VMIDI_OFF, rbuf, wbuf)) {
5468c2ecf20Sopenharmony_ci		snd_printk ("virtual MIDI mode not disabled\n");
5478c2ecf20Sopenharmony_ci		return 0; /* We're OK, but missing the external MIDI dev */
5488c2ecf20Sopenharmony_ci	}
5498c2ecf20Sopenharmony_ci
5508c2ecf20Sopenharmony_ci	snd_wavefront_midi_enable_virtual (card);
5518c2ecf20Sopenharmony_ci
5528c2ecf20Sopenharmony_ci	if (snd_wavefront_cmd (dev, WFC_VMIDI_ON, rbuf, wbuf)) {
5538c2ecf20Sopenharmony_ci		snd_printk ("cannot enable virtual MIDI mode.\n");
5548c2ecf20Sopenharmony_ci		snd_wavefront_midi_disable_virtual (card);
5558c2ecf20Sopenharmony_ci	}
5568c2ecf20Sopenharmony_ci	return 0;
5578c2ecf20Sopenharmony_ci}
5588c2ecf20Sopenharmony_ci
5598c2ecf20Sopenharmony_ciconst struct snd_rawmidi_ops snd_wavefront_midi_output =
5608c2ecf20Sopenharmony_ci{
5618c2ecf20Sopenharmony_ci	.open =		snd_wavefront_midi_output_open,
5628c2ecf20Sopenharmony_ci	.close =	snd_wavefront_midi_output_close,
5638c2ecf20Sopenharmony_ci	.trigger =	snd_wavefront_midi_output_trigger,
5648c2ecf20Sopenharmony_ci};
5658c2ecf20Sopenharmony_ci
5668c2ecf20Sopenharmony_ciconst struct snd_rawmidi_ops snd_wavefront_midi_input =
5678c2ecf20Sopenharmony_ci{
5688c2ecf20Sopenharmony_ci	.open =		snd_wavefront_midi_input_open,
5698c2ecf20Sopenharmony_ci	.close =	snd_wavefront_midi_input_close,
5708c2ecf20Sopenharmony_ci	.trigger =	snd_wavefront_midi_input_trigger,
5718c2ecf20Sopenharmony_ci};
5728c2ecf20Sopenharmony_ci
573