mirror of
				https://git.kernel.org/pub/scm/linux/kernel/git/chenhuacai/linux-loongson
				synced 2025-10-31 05:14:38 +00:00 
			
		
		
		
	 e930e99500
			
		
	
	
		e930e99500
		
	
	
	
	
		
			
			The base versions handle constant folding now. Signed-off-by: Harvey Harrison <harvey.harrison@gmail.com> Signed-off-by: Takashi Iwai <tiwai@suse.de>
		
			
				
	
	
		
			332 lines
		
	
	
		
			9.1 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			332 lines
		
	
	
		
			9.1 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /****************************************************************************
 | |
| 
 | |
|    Copyright Echo Digital Audio Corporation (c) 1998 - 2004
 | |
|    All rights reserved
 | |
|    www.echoaudio.com
 | |
| 
 | |
|    This file is part of Echo Digital Audio's generic driver library.
 | |
| 
 | |
|    Echo Digital Audio's generic driver library is free software;
 | |
|    you can redistribute it and/or modify it under the terms of
 | |
|    the GNU General Public License as published by the Free Software
 | |
|    Foundation.
 | |
| 
 | |
|    This program is distributed in the hope that it will be useful,
 | |
|    but WITHOUT ANY WARRANTY; without even the implied warranty of
 | |
|    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | |
|    GNU General Public License for more details.
 | |
| 
 | |
|    You should have received a copy of the GNU General Public License
 | |
|    along with this program; if not, write to the Free Software
 | |
|    Foundation, Inc., 59 Temple Place - Suite 330, Boston,
 | |
|    MA  02111-1307, USA.
 | |
| 
 | |
|    *************************************************************************
 | |
| 
 | |
|  Translation from C++ and adaptation for use in ALSA-Driver
 | |
|  were made by Giuliano Pochini <pochini@shiny.it>
 | |
| 
 | |
| ****************************************************************************/
 | |
| 
 | |
| 
 | |
| /******************************************************************************
 | |
| 	MIDI lowlevel code
 | |
| ******************************************************************************/
 | |
| 
 | |
| /* Start and stop Midi input */
 | |
| static int enable_midi_input(struct echoaudio *chip, char enable)
 | |
| {
 | |
| 	DE_MID(("enable_midi_input(%d)\n", enable));
 | |
| 
 | |
| 	if (wait_handshake(chip))
 | |
| 		return -EIO;
 | |
| 
 | |
| 	if (enable) {
 | |
| 		chip->mtc_state = MIDI_IN_STATE_NORMAL;
 | |
| 		chip->comm_page->flags |=
 | |
| 			cpu_to_le32(DSP_FLAG_MIDI_INPUT);
 | |
| 	} else
 | |
| 		chip->comm_page->flags &=
 | |
| 			~cpu_to_le32(DSP_FLAG_MIDI_INPUT);
 | |
| 
 | |
| 	clear_handshake(chip);
 | |
| 	return send_vector(chip, DSP_VC_UPDATE_FLAGS);
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| /* Send a buffer full of MIDI data to the DSP
 | |
| Returns how many actually written or < 0 on error */
 | |
| static int write_midi(struct echoaudio *chip, u8 *data, int bytes)
 | |
| {
 | |
| 	if (snd_BUG_ON(bytes <= 0 || bytes >= MIDI_OUT_BUFFER_SIZE))
 | |
| 		return -EINVAL;
 | |
| 
 | |
| 	if (wait_handshake(chip))
 | |
| 		return -EIO;
 | |
| 
 | |
| 	/* HF4 indicates that it is safe to write MIDI output data */
 | |
| 	if (! (get_dsp_register(chip, CHI32_STATUS_REG) & CHI32_STATUS_REG_HF4))
 | |
| 		return 0;
 | |
| 
 | |
| 	chip->comm_page->midi_output[0] = bytes;
 | |
| 	memcpy(&chip->comm_page->midi_output[1], data, bytes);
 | |
| 	chip->comm_page->midi_out_free_count = 0;
 | |
| 	clear_handshake(chip);
 | |
| 	send_vector(chip, DSP_VC_MIDI_WRITE);
 | |
| 	DE_MID(("write_midi: %d\n", bytes));
 | |
| 	return bytes;
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| /* Run the state machine for MIDI input data
 | |
| MIDI time code sync isn't supported by this code right now, but you still need
 | |
| this state machine to parse the incoming MIDI data stream.  Every time the DSP
 | |
| sees a 0xF1 byte come in, it adds the DSP sample position to the MIDI data
 | |
| stream. The DSP sample position is represented as a 32 bit unsigned value,
 | |
| with the high 16 bits first, followed by the low 16 bits. Since these aren't
 | |
| real MIDI bytes, the following logic is needed to skip them. */
 | |
| static inline int mtc_process_data(struct echoaudio *chip, short midi_byte)
 | |
| {
 | |
| 	switch (chip->mtc_state) {
 | |
| 	case MIDI_IN_STATE_NORMAL:
 | |
| 		if (midi_byte == 0xF1)
 | |
| 			chip->mtc_state = MIDI_IN_STATE_TS_HIGH;
 | |
| 		break;
 | |
| 	case MIDI_IN_STATE_TS_HIGH:
 | |
| 		chip->mtc_state = MIDI_IN_STATE_TS_LOW;
 | |
| 		return MIDI_IN_SKIP_DATA;
 | |
| 		break;
 | |
| 	case MIDI_IN_STATE_TS_LOW:
 | |
| 		chip->mtc_state = MIDI_IN_STATE_F1_DATA;
 | |
| 		return MIDI_IN_SKIP_DATA;
 | |
| 		break;
 | |
| 	case MIDI_IN_STATE_F1_DATA:
 | |
| 		chip->mtc_state = MIDI_IN_STATE_NORMAL;
 | |
| 		break;
 | |
| 	}
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| /* This function is called from the IRQ handler and it reads the midi data
 | |
| from the DSP's buffer.  It returns the number of bytes received. */
 | |
| static int midi_service_irq(struct echoaudio *chip)
 | |
| {
 | |
| 	short int count, midi_byte, i, received;
 | |
| 
 | |
| 	/* The count is at index 0, followed by actual data */
 | |
| 	count = le16_to_cpu(chip->comm_page->midi_input[0]);
 | |
| 
 | |
| 	if (snd_BUG_ON(count >= MIDI_IN_BUFFER_SIZE))
 | |
| 		return 0;
 | |
| 
 | |
| 	/* Get the MIDI data from the comm page */
 | |
| 	i = 1;
 | |
| 	received = 0;
 | |
| 	for (i = 1; i <= count; i++) {
 | |
| 		/* Get the MIDI byte */
 | |
| 		midi_byte = le16_to_cpu(chip->comm_page->midi_input[i]);
 | |
| 
 | |
| 		/* Parse the incoming MIDI stream. The incoming MIDI data
 | |
| 		consists of MIDI bytes and timestamps for the MIDI time code
 | |
| 		0xF1 bytes. mtc_process_data() is a little state machine that
 | |
| 		parses the stream. If you get MIDI_IN_SKIP_DATA back, then
 | |
| 		this is a timestamp byte, not a MIDI byte, so don't store it
 | |
| 		in the MIDI input buffer. */
 | |
| 		if (mtc_process_data(chip, midi_byte) == MIDI_IN_SKIP_DATA)
 | |
| 			continue;
 | |
| 
 | |
| 		chip->midi_buffer[received++] = (u8)midi_byte;
 | |
| 	}
 | |
| 
 | |
| 	return received;
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| /******************************************************************************
 | |
| 	MIDI interface
 | |
| ******************************************************************************/
 | |
| 
 | |
| static int snd_echo_midi_input_open(struct snd_rawmidi_substream *substream)
 | |
| {
 | |
| 	struct echoaudio *chip = substream->rmidi->private_data;
 | |
| 
 | |
| 	chip->midi_in = substream;
 | |
| 	DE_MID(("rawmidi_iopen\n"));
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| static void snd_echo_midi_input_trigger(struct snd_rawmidi_substream *substream,
 | |
| 					int up)
 | |
| {
 | |
| 	struct echoaudio *chip = substream->rmidi->private_data;
 | |
| 
 | |
| 	if (up != chip->midi_input_enabled) {
 | |
| 		spin_lock_irq(&chip->lock);
 | |
| 		enable_midi_input(chip, up);
 | |
| 		spin_unlock_irq(&chip->lock);
 | |
| 		chip->midi_input_enabled = up;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| static int snd_echo_midi_input_close(struct snd_rawmidi_substream *substream)
 | |
| {
 | |
| 	struct echoaudio *chip = substream->rmidi->private_data;
 | |
| 
 | |
| 	chip->midi_in = NULL;
 | |
| 	DE_MID(("rawmidi_iclose\n"));
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| static int snd_echo_midi_output_open(struct snd_rawmidi_substream *substream)
 | |
| {
 | |
| 	struct echoaudio *chip = substream->rmidi->private_data;
 | |
| 
 | |
| 	chip->tinuse = 0;
 | |
| 	chip->midi_full = 0;
 | |
| 	chip->midi_out = substream;
 | |
| 	DE_MID(("rawmidi_oopen\n"));
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| static void snd_echo_midi_output_write(unsigned long data)
 | |
| {
 | |
| 	struct echoaudio *chip = (struct echoaudio *)data;
 | |
| 	unsigned long flags;
 | |
| 	int bytes, sent, time;
 | |
| 	unsigned char buf[MIDI_OUT_BUFFER_SIZE - 1];
 | |
| 
 | |
| 	DE_MID(("snd_echo_midi_output_write\n"));
 | |
| 	/* No interrupts are involved: we have to check at regular intervals
 | |
| 	if the card's output buffer has room for new data. */
 | |
| 	sent = bytes = 0;
 | |
| 	spin_lock_irqsave(&chip->lock, flags);
 | |
| 	chip->midi_full = 0;
 | |
| 	if (!snd_rawmidi_transmit_empty(chip->midi_out)) {
 | |
| 		bytes = snd_rawmidi_transmit_peek(chip->midi_out, buf,
 | |
| 						  MIDI_OUT_BUFFER_SIZE - 1);
 | |
| 		DE_MID(("Try to send %d bytes...\n", bytes));
 | |
| 		sent = write_midi(chip, buf, bytes);
 | |
| 		if (sent < 0) {
 | |
| 			snd_printk(KERN_ERR "write_midi() error %d\n", sent);
 | |
| 			/* retry later */
 | |
| 			sent = 9000;
 | |
| 			chip->midi_full = 1;
 | |
| 		} else if (sent > 0) {
 | |
| 			DE_MID(("%d bytes sent\n", sent));
 | |
| 			snd_rawmidi_transmit_ack(chip->midi_out, sent);
 | |
| 		} else {
 | |
| 			/* Buffer is full. DSP's internal buffer is 64 (128 ?)
 | |
| 			bytes long. Let's wait until half of them are sent */
 | |
| 			DE_MID(("Full\n"));
 | |
| 			sent = 32;
 | |
| 			chip->midi_full = 1;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	/* We restart the timer only if there is some data left to send */
 | |
| 	if (!snd_rawmidi_transmit_empty(chip->midi_out) && chip->tinuse) {
 | |
| 		/* The timer will expire slightly after the data has been
 | |
| 		   sent */
 | |
| 		time = (sent << 3) / 25 + 1;	/* 8/25=0.32ms to send a byte */
 | |
| 		mod_timer(&chip->timer, jiffies + (time * HZ + 999) / 1000);
 | |
| 		DE_MID(("Timer armed(%d)\n", ((time * HZ + 999) / 1000)));
 | |
| 	}
 | |
| 	spin_unlock_irqrestore(&chip->lock, flags);
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| static void snd_echo_midi_output_trigger(struct snd_rawmidi_substream *substream,
 | |
| 					 int up)
 | |
| {
 | |
| 	struct echoaudio *chip = substream->rmidi->private_data;
 | |
| 
 | |
| 	DE_MID(("snd_echo_midi_output_trigger(%d)\n", up));
 | |
| 	spin_lock_irq(&chip->lock);
 | |
| 	if (up) {
 | |
| 		if (!chip->tinuse) {
 | |
| 			init_timer(&chip->timer);
 | |
| 			chip->timer.function = snd_echo_midi_output_write;
 | |
| 			chip->timer.data = (unsigned long)chip;
 | |
| 			chip->tinuse = 1;
 | |
| 		}
 | |
| 	} else {
 | |
| 		if (chip->tinuse) {
 | |
| 			chip->tinuse = 0;
 | |
| 			spin_unlock_irq(&chip->lock);
 | |
| 			del_timer_sync(&chip->timer);
 | |
| 			DE_MID(("Timer removed\n"));
 | |
| 			return;
 | |
| 		}
 | |
| 	}
 | |
| 	spin_unlock_irq(&chip->lock);
 | |
| 
 | |
| 	if (up && !chip->midi_full)
 | |
| 		snd_echo_midi_output_write((unsigned long)chip);
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| static int snd_echo_midi_output_close(struct snd_rawmidi_substream *substream)
 | |
| {
 | |
| 	struct echoaudio *chip = substream->rmidi->private_data;
 | |
| 
 | |
| 	chip->midi_out = NULL;
 | |
| 	DE_MID(("rawmidi_oclose\n"));
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| static struct snd_rawmidi_ops snd_echo_midi_input = {
 | |
| 	.open = snd_echo_midi_input_open,
 | |
| 	.close = snd_echo_midi_input_close,
 | |
| 	.trigger = snd_echo_midi_input_trigger,
 | |
| };
 | |
| 
 | |
| static struct snd_rawmidi_ops snd_echo_midi_output = {
 | |
| 	.open = snd_echo_midi_output_open,
 | |
| 	.close = snd_echo_midi_output_close,
 | |
| 	.trigger = snd_echo_midi_output_trigger,
 | |
| };
 | |
| 
 | |
| 
 | |
| 
 | |
| /* <--snd_echo_probe() */
 | |
| static int __devinit snd_echo_midi_create(struct snd_card *card,
 | |
| 					  struct echoaudio *chip)
 | |
| {
 | |
| 	int err;
 | |
| 
 | |
| 	if ((err = snd_rawmidi_new(card, card->shortname, 0, 1, 1,
 | |
| 				   &chip->rmidi)) < 0)
 | |
| 		return err;
 | |
| 
 | |
| 	strcpy(chip->rmidi->name, card->shortname);
 | |
| 	chip->rmidi->private_data = chip;
 | |
| 
 | |
| 	snd_rawmidi_set_ops(chip->rmidi, SNDRV_RAWMIDI_STREAM_INPUT,
 | |
| 			    &snd_echo_midi_input);
 | |
| 	snd_rawmidi_set_ops(chip->rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT,
 | |
| 			    &snd_echo_midi_output);
 | |
| 
 | |
| 	chip->rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT |
 | |
| 		SNDRV_RAWMIDI_INFO_INPUT | SNDRV_RAWMIDI_INFO_DUPLEX;
 | |
| 	DE_INIT(("MIDI ok\n"));
 | |
| 	return 0;
 | |
| }
 |