brintos

brintos / linux-shallow public Read only

0
0
Text · 15.0 KiB · ead8cbe Raw
582 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Copyright (C) by Paul Barton-Davis 1998-19994 */5 6/* The low level driver for the WaveFront ICS2115 MIDI interface(s)7 *8 * Note that there is also an MPU-401 emulation (actually, a UART-4019 * emulation) on the CS4232 on the Tropez and Tropez Plus. This code10 * has nothing to do with that interface at all.11 *12 * The interface is essentially just a UART-401, but is has the13 * interesting property of supporting what Turtle Beach called14 * "Virtual MIDI" mode. In this mode, there are effectively *two*15 * MIDI buses accessible via the interface, one that is routed16 * solely to/from the external WaveFront synthesizer and the other17 * corresponding to the pin/socket connector used to link external18 * MIDI devices to the board.19 *20 * This driver fully supports this mode, allowing two distinct MIDI21 * busses to be used completely independently, giving 32 channels of22 * MIDI routing, 16 to the WaveFront synth and 16 to the external MIDI23 * bus. The devices are named /dev/snd/midiCnD0 and /dev/snd/midiCnD1,24 * where `n' is the card number. Note that the device numbers may be25 * something other than 0 and 1 if the CS4232 UART/MPU-401 interface26 * is enabled.27 *28 * Switching between the two is accomplished externally by the driver29 * using the two otherwise unused MIDI bytes. See the code for more details.30 *31 * NOTE: VIRTUAL MIDI MODE IS ON BY DEFAULT (see lowlevel/isa/wavefront.c)32 *33 * The main reason to turn off Virtual MIDI mode is when you want to34 * tightly couple the WaveFront synth with an external MIDI35 * device. You won't be able to distinguish the source of any MIDI36 * data except via SysEx ID, but thats probably OK, since for the most37 * part, the WaveFront won't be sending any MIDI data at all.38 *  39 * The main reason to turn on Virtual MIDI Mode is to provide two40 * completely independent 16-channel MIDI buses, one to the41 * WaveFront and one to any external MIDI devices. Given the 3242 * voice nature of the WaveFront, its pretty easy to find a use43 * for all 16 channels driving just that synth.44 *  45 */46 47#include <linux/io.h>48#include <linux/init.h>49#include <linux/time.h>50#include <linux/wait.h>51#include <sound/core.h>52#include <sound/snd_wavefront.h>53 54static inline int 55wf_mpu_status (snd_wavefront_midi_t *midi)56 57{58	return inb (midi->mpu_status_port);59}60 61static inline int 62input_avail (snd_wavefront_midi_t *midi)63 64{65	return !(wf_mpu_status(midi) & INPUT_AVAIL);66}67 68static inline int69output_ready (snd_wavefront_midi_t *midi)70 71{72	return !(wf_mpu_status(midi) & OUTPUT_READY);73}74 75static inline int 76read_data (snd_wavefront_midi_t *midi)77 78{79	return inb (midi->mpu_data_port);80}81 82static inline void 83write_data (snd_wavefront_midi_t *midi, unsigned char byte)84 85{86	outb (byte, midi->mpu_data_port);87}88 89static snd_wavefront_midi_t *90get_wavefront_midi (struct snd_rawmidi_substream *substream)91 92{93	struct snd_card *card;94	snd_wavefront_card_t *acard;95 96	if (substream == NULL || substream->rmidi == NULL) 97	        return NULL;98 99	card = substream->rmidi->card;100 101	if (card == NULL) 102	        return NULL;103 104	if (card->private_data == NULL) 105 	        return NULL;106 107	acard = card->private_data;108 109	return &acard->wavefront.midi;110}111 112static void snd_wavefront_midi_output_write(snd_wavefront_card_t *card)113{114	snd_wavefront_midi_t *midi = &card->wavefront.midi;115	snd_wavefront_mpu_id  mpu;116	unsigned long flags;117	unsigned char midi_byte;118	int max = 256, mask = 1;119	int timeout;120 121	/* Its not OK to try to change the status of "virtuality" of122	   the MIDI interface while we're outputting stuff.  See123	   snd_wavefront_midi_{enable,disable}_virtual () for the124	   other half of this.  125 126	   The first loop attempts to flush any data from the127	   current output device, and then the second 128	   emits the switch byte (if necessary), and starts129	   outputting data for the output device currently in use.130	*/131 132	if (midi->substream_output[midi->output_mpu] == NULL) {133		goto __second;134	}135 136	while (max > 0) {137 138		/* XXX fix me - no hard timing loops allowed! */139 140		for (timeout = 30000; timeout > 0; timeout--) {141			if (output_ready (midi))142				break;143		}144	145		spin_lock_irqsave (&midi->virtual, flags);146		if ((midi->mode[midi->output_mpu] & MPU401_MODE_OUTPUT) == 0) {147			spin_unlock_irqrestore (&midi->virtual, flags);148			goto __second;149		}150		if (output_ready (midi)) {151			if (snd_rawmidi_transmit(midi->substream_output[midi->output_mpu], &midi_byte, 1) == 1) {152				if (!midi->isvirtual ||153					(midi_byte != WF_INTERNAL_SWITCH &&154					 midi_byte != WF_EXTERNAL_SWITCH))155					write_data(midi, midi_byte);156				max--;157			} else {158				if (midi->istimer) {159					if (--midi->istimer <= 0)160						del_timer(&midi->timer);161				}162				midi->mode[midi->output_mpu] &= ~MPU401_MODE_OUTPUT_TRIGGER;163				spin_unlock_irqrestore (&midi->virtual, flags);164				goto __second;165			}166		} else {167			spin_unlock_irqrestore (&midi->virtual, flags);168			return;169		}170		spin_unlock_irqrestore (&midi->virtual, flags);171	}172 173      __second:174 175	if (midi->substream_output[!midi->output_mpu] == NULL) {176		return;177	}178 179	while (max > 0) {180 181		/* XXX fix me - no hard timing loops allowed! */182 183		for (timeout = 30000; timeout > 0; timeout--) {184			if (output_ready (midi))185				break;186		}187	188		spin_lock_irqsave (&midi->virtual, flags);189		if (!midi->isvirtual)190			mask = 0;191		mpu = midi->output_mpu ^ mask;192		mask = 0;	/* don't invert the value from now */193		if ((midi->mode[mpu] & MPU401_MODE_OUTPUT) == 0) {194			spin_unlock_irqrestore (&midi->virtual, flags);195			return;196		}197		if (snd_rawmidi_transmit_empty(midi->substream_output[mpu]))198			goto __timer;199		if (output_ready (midi)) {200			if (mpu != midi->output_mpu) {201				write_data(midi, mpu == internal_mpu ?202							WF_INTERNAL_SWITCH :203							WF_EXTERNAL_SWITCH);204				midi->output_mpu = mpu;205			} else if (snd_rawmidi_transmit(midi->substream_output[mpu], &midi_byte, 1) == 1) {206				if (!midi->isvirtual ||207					(midi_byte != WF_INTERNAL_SWITCH &&208					 midi_byte != WF_EXTERNAL_SWITCH))209					write_data(midi, midi_byte);210				max--;211			} else {212			      __timer:213				if (midi->istimer) {214					if (--midi->istimer <= 0)215						del_timer(&midi->timer);216				}217				midi->mode[mpu] &= ~MPU401_MODE_OUTPUT_TRIGGER;218				spin_unlock_irqrestore (&midi->virtual, flags);219				return;220			}221		} else {222			spin_unlock_irqrestore (&midi->virtual, flags);223			return;224		}225		spin_unlock_irqrestore (&midi->virtual, flags);226	}227}228 229static int snd_wavefront_midi_input_open(struct snd_rawmidi_substream *substream)230{231	unsigned long flags;232	snd_wavefront_midi_t *midi;233	snd_wavefront_mpu_id mpu;234 235	if (snd_BUG_ON(!substream || !substream->rmidi))236		return -ENXIO;237	if (snd_BUG_ON(!substream->rmidi->private_data))238		return -ENXIO;239 240	mpu = *((snd_wavefront_mpu_id *) substream->rmidi->private_data);241 242	midi = get_wavefront_midi(substream);243	if (!midi)244	        return -EIO;245 246	spin_lock_irqsave (&midi->open, flags);247	midi->mode[mpu] |= MPU401_MODE_INPUT;248	midi->substream_input[mpu] = substream;249	spin_unlock_irqrestore (&midi->open, flags);250 251	return 0;252}253 254static int snd_wavefront_midi_output_open(struct snd_rawmidi_substream *substream)255{256	unsigned long flags;257	snd_wavefront_midi_t *midi;258	snd_wavefront_mpu_id mpu;259 260	if (snd_BUG_ON(!substream || !substream->rmidi))261		return -ENXIO;262	if (snd_BUG_ON(!substream->rmidi->private_data))263		return -ENXIO;264 265	mpu = *((snd_wavefront_mpu_id *) substream->rmidi->private_data);266 267	midi = get_wavefront_midi(substream);268	if (!midi)269	        return -EIO;270 271	spin_lock_irqsave (&midi->open, flags);272	midi->mode[mpu] |= MPU401_MODE_OUTPUT;273	midi->substream_output[mpu] = substream;274	spin_unlock_irqrestore (&midi->open, flags);275 276	return 0;277}278 279static int snd_wavefront_midi_input_close(struct snd_rawmidi_substream *substream)280{281	unsigned long flags;282	snd_wavefront_midi_t *midi;283	snd_wavefront_mpu_id mpu;284 285	if (snd_BUG_ON(!substream || !substream->rmidi))286		return -ENXIO;287	if (snd_BUG_ON(!substream->rmidi->private_data))288		return -ENXIO;289 290	mpu = *((snd_wavefront_mpu_id *) substream->rmidi->private_data);291 292	midi = get_wavefront_midi(substream);293	if (!midi)294	        return -EIO;295 296	spin_lock_irqsave (&midi->open, flags);297	midi->mode[mpu] &= ~MPU401_MODE_INPUT;298	spin_unlock_irqrestore (&midi->open, flags);299 300	return 0;301}302 303static int snd_wavefront_midi_output_close(struct snd_rawmidi_substream *substream)304{305	unsigned long flags;306	snd_wavefront_midi_t *midi;307	snd_wavefront_mpu_id mpu;308 309	if (snd_BUG_ON(!substream || !substream->rmidi))310		return -ENXIO;311	if (snd_BUG_ON(!substream->rmidi->private_data))312		return -ENXIO;313 314	mpu = *((snd_wavefront_mpu_id *) substream->rmidi->private_data);315 316	midi = get_wavefront_midi(substream);317	if (!midi)318	        return -EIO;319 320	spin_lock_irqsave (&midi->open, flags);321	midi->mode[mpu] &= ~MPU401_MODE_OUTPUT;322	spin_unlock_irqrestore (&midi->open, flags);323	return 0;324}325 326static void snd_wavefront_midi_input_trigger(struct snd_rawmidi_substream *substream, int up)327{328	unsigned long flags;329	snd_wavefront_midi_t *midi;330	snd_wavefront_mpu_id mpu;331 332	if (substream == NULL || substream->rmidi == NULL) 333	        return;334 335	if (substream->rmidi->private_data == NULL)336	        return;337 338	mpu = *((snd_wavefront_mpu_id *) substream->rmidi->private_data);339 340	midi = get_wavefront_midi(substream);341	if (!midi)342		return;343 344	spin_lock_irqsave (&midi->virtual, flags);345	if (up) {346		midi->mode[mpu] |= MPU401_MODE_INPUT_TRIGGER;347	} else {348		midi->mode[mpu] &= ~MPU401_MODE_INPUT_TRIGGER;349	}350	spin_unlock_irqrestore (&midi->virtual, flags);351}352 353static void snd_wavefront_midi_output_timer(struct timer_list *t)354{355	snd_wavefront_midi_t *midi = from_timer(midi, t, timer);356	snd_wavefront_card_t *card = midi->timer_card;357	unsigned long flags;358	359	spin_lock_irqsave (&midi->virtual, flags);360	mod_timer(&midi->timer, 1 + jiffies);361	spin_unlock_irqrestore (&midi->virtual, flags);362	snd_wavefront_midi_output_write(card);363}364 365static void snd_wavefront_midi_output_trigger(struct snd_rawmidi_substream *substream, int up)366{367	unsigned long flags;368	snd_wavefront_midi_t *midi;369	snd_wavefront_mpu_id mpu;370 371	if (substream == NULL || substream->rmidi == NULL) 372	        return;373 374	if (substream->rmidi->private_data == NULL)375	        return;376 377	mpu = *((snd_wavefront_mpu_id *) substream->rmidi->private_data);378 379	midi = get_wavefront_midi(substream);380	if (!midi)381		return;382 383	spin_lock_irqsave (&midi->virtual, flags);384	if (up) {385		if ((midi->mode[mpu] & MPU401_MODE_OUTPUT_TRIGGER) == 0) {386			if (!midi->istimer) {387				timer_setup(&midi->timer,388					    snd_wavefront_midi_output_timer,389					    0);390				mod_timer(&midi->timer, 1 + jiffies);391			}392			midi->istimer++;393			midi->mode[mpu] |= MPU401_MODE_OUTPUT_TRIGGER;394		}395	} else {396		midi->mode[mpu] &= ~MPU401_MODE_OUTPUT_TRIGGER;397	}398	spin_unlock_irqrestore (&midi->virtual, flags);399 400	if (up)401		snd_wavefront_midi_output_write((snd_wavefront_card_t *)substream->rmidi->card->private_data);402}403 404void405snd_wavefront_midi_interrupt (snd_wavefront_card_t *card)406 407{408	unsigned long flags;409	snd_wavefront_midi_t *midi;410	static struct snd_rawmidi_substream *substream = NULL;411	static int mpu = external_mpu; 412	int max = 128;413	unsigned char byte;414 415	midi = &card->wavefront.midi;416 417	if (!input_avail (midi)) { /* not for us */418		snd_wavefront_midi_output_write(card);419		return;420	}421 422	spin_lock_irqsave (&midi->virtual, flags);423	while (--max) {424 425		if (input_avail (midi)) {426			byte = read_data (midi);427 428			if (midi->isvirtual) {				429				if (byte == WF_EXTERNAL_SWITCH) {430					substream = midi->substream_input[external_mpu];431					mpu = external_mpu;432				} else if (byte == WF_INTERNAL_SWITCH) { 433					substream = midi->substream_output[internal_mpu];434					mpu = internal_mpu;435				} /* else just leave it as it is */436			} else {437				substream = midi->substream_input[internal_mpu];438				mpu = internal_mpu;439			}440 441			if (substream == NULL) {442				continue;443			}444 445			if (midi->mode[mpu] & MPU401_MODE_INPUT_TRIGGER) {446				snd_rawmidi_receive(substream, &byte, 1);447			}448		} else {449			break;450		}451	} 452	spin_unlock_irqrestore (&midi->virtual, flags);453 454	snd_wavefront_midi_output_write(card);455}456 457void458snd_wavefront_midi_enable_virtual (snd_wavefront_card_t *card)459 460{461	unsigned long flags;462 463	spin_lock_irqsave (&card->wavefront.midi.virtual, flags);464	card->wavefront.midi.isvirtual = 1;465	card->wavefront.midi.output_mpu = internal_mpu;466	card->wavefront.midi.input_mpu = internal_mpu;467	spin_unlock_irqrestore (&card->wavefront.midi.virtual, flags);468}469 470void471snd_wavefront_midi_disable_virtual (snd_wavefront_card_t *card)472 473{474	unsigned long flags;475 476	spin_lock_irqsave (&card->wavefront.midi.virtual, flags);477	// snd_wavefront_midi_input_close (card->ics2115_external_rmidi);478	// snd_wavefront_midi_output_close (card->ics2115_external_rmidi);479	card->wavefront.midi.isvirtual = 0;480	spin_unlock_irqrestore (&card->wavefront.midi.virtual, flags);481}482 483int484snd_wavefront_midi_start (snd_wavefront_card_t *card)485 486{487	int ok, i;488	unsigned char rbuf[4], wbuf[4];489	snd_wavefront_t *dev;490	snd_wavefront_midi_t *midi;491 492	dev = &card->wavefront;493	midi = &dev->midi;494 495	/* The ICS2115 MPU-401 interface doesn't do anything496	   until its set into UART mode.497	*/498 499	/* XXX fix me - no hard timing loops allowed! */500 501	for (i = 0; i < 30000 && !output_ready (midi); i++);502 503	if (!output_ready (midi)) {504		dev_err(card->wavefront.card->dev,505			"MIDI interface not ready for command\n");506		return -1;507	}508 509	/* Any interrupts received from now on510	   are owned by the MIDI side of things.511	*/512 513	dev->interrupts_are_midi = 1;514	515	outb (UART_MODE_ON, midi->mpu_command_port);516 517	for (ok = 0, i = 50000; i > 0 && !ok; i--) {518		if (input_avail (midi)) {519			if (read_data (midi) == MPU_ACK) {520				ok = 1;521				break;522			}523		}524	}525 526	if (!ok) {527		dev_err(card->wavefront.card->dev,528			"cannot set UART mode for MIDI interface");529		dev->interrupts_are_midi = 0;530		return -1;531	}532 533	/* Route external MIDI to WaveFront synth (by default) */534    535	if (snd_wavefront_cmd (dev, WFC_MISYNTH_ON, rbuf, wbuf)) {536		dev_warn(card->wavefront.card->dev,537			 "can't enable MIDI-IN-2-synth routing.\n");538		/* XXX error ? */539	}540 541	/* Turn on Virtual MIDI, but first *always* turn it off,542	   since otherwise consecutive reloads of the driver will543	   never cause the hardware to generate the initial "internal" or 544	   "external" source bytes in the MIDI data stream. This545	   is pretty important, since the internal hardware generally will546	   be used to generate none or very little MIDI output, and547	   thus the only source of MIDI data is actually external. Without548	   the switch bytes, the driver will think it all comes from549	   the internal interface. Duh.550	*/551 552	if (snd_wavefront_cmd (dev, WFC_VMIDI_OFF, rbuf, wbuf)) { 553		dev_warn(card->wavefront.card->dev,554			 "virtual MIDI mode not disabled\n");555		return 0; /* We're OK, but missing the external MIDI dev */556	}557 558	snd_wavefront_midi_enable_virtual (card);559 560	if (snd_wavefront_cmd (dev, WFC_VMIDI_ON, rbuf, wbuf)) {561		dev_warn(card->wavefront.card->dev,562			 "cannot enable virtual MIDI mode.\n");563		snd_wavefront_midi_disable_virtual (card);564	} 565	return 0;566}567 568const struct snd_rawmidi_ops snd_wavefront_midi_output =569{570	.open =		snd_wavefront_midi_output_open,571	.close =	snd_wavefront_midi_output_close,572	.trigger =	snd_wavefront_midi_output_trigger,573};574 575const struct snd_rawmidi_ops snd_wavefront_midi_input =576{577	.open =		snd_wavefront_midi_input_open,578	.close =	snd_wavefront_midi_input_close,579	.trigger =	snd_wavefront_midi_input_trigger,580};581 582