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 SoundBlaster cards - MIDI interface 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * -- 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * Sun May 9 22:54:38 BST 1999 George David Morrison <gdm@gedamo.demon.co.uk> 962306a36Sopenharmony_ci * Fixed typo in snd_sb8dsp_midi_new_device which prevented midi from 1062306a36Sopenharmony_ci * working. 1162306a36Sopenharmony_ci * 1262306a36Sopenharmony_ci * Sun May 11 12:34:56 UTC 2003 Clemens Ladisch <clemens@ladisch.de> 1362306a36Sopenharmony_ci * Added full duplex UART mode for DSP version 2.0 and later. 1462306a36Sopenharmony_ci */ 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#include <linux/io.h> 1762306a36Sopenharmony_ci#include <linux/time.h> 1862306a36Sopenharmony_ci#include <sound/core.h> 1962306a36Sopenharmony_ci#include <sound/sb.h> 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ciirqreturn_t snd_sb8dsp_midi_interrupt(struct snd_sb *chip) 2362306a36Sopenharmony_ci{ 2462306a36Sopenharmony_ci struct snd_rawmidi *rmidi; 2562306a36Sopenharmony_ci int max = 64; 2662306a36Sopenharmony_ci char byte; 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci if (!chip) 2962306a36Sopenharmony_ci return IRQ_NONE; 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci rmidi = chip->rmidi; 3262306a36Sopenharmony_ci if (!rmidi) { 3362306a36Sopenharmony_ci inb(SBP(chip, DATA_AVAIL)); /* ack interrupt */ 3462306a36Sopenharmony_ci return IRQ_NONE; 3562306a36Sopenharmony_ci } 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci spin_lock(&chip->midi_input_lock); 3862306a36Sopenharmony_ci while (max-- > 0) { 3962306a36Sopenharmony_ci if (inb(SBP(chip, DATA_AVAIL)) & 0x80) { 4062306a36Sopenharmony_ci byte = inb(SBP(chip, READ)); 4162306a36Sopenharmony_ci if (chip->open & SB_OPEN_MIDI_INPUT_TRIGGER) { 4262306a36Sopenharmony_ci snd_rawmidi_receive(chip->midi_substream_input, &byte, 1); 4362306a36Sopenharmony_ci } 4462306a36Sopenharmony_ci } 4562306a36Sopenharmony_ci } 4662306a36Sopenharmony_ci spin_unlock(&chip->midi_input_lock); 4762306a36Sopenharmony_ci return IRQ_HANDLED; 4862306a36Sopenharmony_ci} 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_cistatic int snd_sb8dsp_midi_input_open(struct snd_rawmidi_substream *substream) 5162306a36Sopenharmony_ci{ 5262306a36Sopenharmony_ci unsigned long flags; 5362306a36Sopenharmony_ci struct snd_sb *chip; 5462306a36Sopenharmony_ci unsigned int valid_open_flags; 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci chip = substream->rmidi->private_data; 5762306a36Sopenharmony_ci valid_open_flags = chip->hardware >= SB_HW_20 5862306a36Sopenharmony_ci ? SB_OPEN_MIDI_OUTPUT | SB_OPEN_MIDI_OUTPUT_TRIGGER : 0; 5962306a36Sopenharmony_ci spin_lock_irqsave(&chip->open_lock, flags); 6062306a36Sopenharmony_ci if (chip->open & ~valid_open_flags) { 6162306a36Sopenharmony_ci spin_unlock_irqrestore(&chip->open_lock, flags); 6262306a36Sopenharmony_ci return -EAGAIN; 6362306a36Sopenharmony_ci } 6462306a36Sopenharmony_ci chip->open |= SB_OPEN_MIDI_INPUT; 6562306a36Sopenharmony_ci chip->midi_substream_input = substream; 6662306a36Sopenharmony_ci if (!(chip->open & SB_OPEN_MIDI_OUTPUT)) { 6762306a36Sopenharmony_ci spin_unlock_irqrestore(&chip->open_lock, flags); 6862306a36Sopenharmony_ci snd_sbdsp_reset(chip); /* reset DSP */ 6962306a36Sopenharmony_ci if (chip->hardware >= SB_HW_20) 7062306a36Sopenharmony_ci snd_sbdsp_command(chip, SB_DSP_MIDI_UART_IRQ); 7162306a36Sopenharmony_ci } else { 7262306a36Sopenharmony_ci spin_unlock_irqrestore(&chip->open_lock, flags); 7362306a36Sopenharmony_ci } 7462306a36Sopenharmony_ci return 0; 7562306a36Sopenharmony_ci} 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_cistatic int snd_sb8dsp_midi_output_open(struct snd_rawmidi_substream *substream) 7862306a36Sopenharmony_ci{ 7962306a36Sopenharmony_ci unsigned long flags; 8062306a36Sopenharmony_ci struct snd_sb *chip; 8162306a36Sopenharmony_ci unsigned int valid_open_flags; 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci chip = substream->rmidi->private_data; 8462306a36Sopenharmony_ci valid_open_flags = chip->hardware >= SB_HW_20 8562306a36Sopenharmony_ci ? SB_OPEN_MIDI_INPUT | SB_OPEN_MIDI_INPUT_TRIGGER : 0; 8662306a36Sopenharmony_ci spin_lock_irqsave(&chip->open_lock, flags); 8762306a36Sopenharmony_ci if (chip->open & ~valid_open_flags) { 8862306a36Sopenharmony_ci spin_unlock_irqrestore(&chip->open_lock, flags); 8962306a36Sopenharmony_ci return -EAGAIN; 9062306a36Sopenharmony_ci } 9162306a36Sopenharmony_ci chip->open |= SB_OPEN_MIDI_OUTPUT; 9262306a36Sopenharmony_ci chip->midi_substream_output = substream; 9362306a36Sopenharmony_ci if (!(chip->open & SB_OPEN_MIDI_INPUT)) { 9462306a36Sopenharmony_ci spin_unlock_irqrestore(&chip->open_lock, flags); 9562306a36Sopenharmony_ci snd_sbdsp_reset(chip); /* reset DSP */ 9662306a36Sopenharmony_ci if (chip->hardware >= SB_HW_20) 9762306a36Sopenharmony_ci snd_sbdsp_command(chip, SB_DSP_MIDI_UART_IRQ); 9862306a36Sopenharmony_ci } else { 9962306a36Sopenharmony_ci spin_unlock_irqrestore(&chip->open_lock, flags); 10062306a36Sopenharmony_ci } 10162306a36Sopenharmony_ci return 0; 10262306a36Sopenharmony_ci} 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_cistatic int snd_sb8dsp_midi_input_close(struct snd_rawmidi_substream *substream) 10562306a36Sopenharmony_ci{ 10662306a36Sopenharmony_ci unsigned long flags; 10762306a36Sopenharmony_ci struct snd_sb *chip; 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci chip = substream->rmidi->private_data; 11062306a36Sopenharmony_ci spin_lock_irqsave(&chip->open_lock, flags); 11162306a36Sopenharmony_ci chip->open &= ~(SB_OPEN_MIDI_INPUT | SB_OPEN_MIDI_INPUT_TRIGGER); 11262306a36Sopenharmony_ci chip->midi_substream_input = NULL; 11362306a36Sopenharmony_ci if (!(chip->open & SB_OPEN_MIDI_OUTPUT)) { 11462306a36Sopenharmony_ci spin_unlock_irqrestore(&chip->open_lock, flags); 11562306a36Sopenharmony_ci snd_sbdsp_reset(chip); /* reset DSP */ 11662306a36Sopenharmony_ci } else { 11762306a36Sopenharmony_ci spin_unlock_irqrestore(&chip->open_lock, flags); 11862306a36Sopenharmony_ci } 11962306a36Sopenharmony_ci return 0; 12062306a36Sopenharmony_ci} 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_cistatic int snd_sb8dsp_midi_output_close(struct snd_rawmidi_substream *substream) 12362306a36Sopenharmony_ci{ 12462306a36Sopenharmony_ci unsigned long flags; 12562306a36Sopenharmony_ci struct snd_sb *chip; 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci chip = substream->rmidi->private_data; 12862306a36Sopenharmony_ci del_timer_sync(&chip->midi_timer); 12962306a36Sopenharmony_ci spin_lock_irqsave(&chip->open_lock, flags); 13062306a36Sopenharmony_ci chip->open &= ~(SB_OPEN_MIDI_OUTPUT | SB_OPEN_MIDI_OUTPUT_TRIGGER); 13162306a36Sopenharmony_ci chip->midi_substream_output = NULL; 13262306a36Sopenharmony_ci if (!(chip->open & SB_OPEN_MIDI_INPUT)) { 13362306a36Sopenharmony_ci spin_unlock_irqrestore(&chip->open_lock, flags); 13462306a36Sopenharmony_ci snd_sbdsp_reset(chip); /* reset DSP */ 13562306a36Sopenharmony_ci } else { 13662306a36Sopenharmony_ci spin_unlock_irqrestore(&chip->open_lock, flags); 13762306a36Sopenharmony_ci } 13862306a36Sopenharmony_ci return 0; 13962306a36Sopenharmony_ci} 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_cistatic void snd_sb8dsp_midi_input_trigger(struct snd_rawmidi_substream *substream, int up) 14262306a36Sopenharmony_ci{ 14362306a36Sopenharmony_ci unsigned long flags; 14462306a36Sopenharmony_ci struct snd_sb *chip; 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci chip = substream->rmidi->private_data; 14762306a36Sopenharmony_ci spin_lock_irqsave(&chip->open_lock, flags); 14862306a36Sopenharmony_ci if (up) { 14962306a36Sopenharmony_ci if (!(chip->open & SB_OPEN_MIDI_INPUT_TRIGGER)) { 15062306a36Sopenharmony_ci if (chip->hardware < SB_HW_20) 15162306a36Sopenharmony_ci snd_sbdsp_command(chip, SB_DSP_MIDI_INPUT_IRQ); 15262306a36Sopenharmony_ci chip->open |= SB_OPEN_MIDI_INPUT_TRIGGER; 15362306a36Sopenharmony_ci } 15462306a36Sopenharmony_ci } else { 15562306a36Sopenharmony_ci if (chip->open & SB_OPEN_MIDI_INPUT_TRIGGER) { 15662306a36Sopenharmony_ci if (chip->hardware < SB_HW_20) 15762306a36Sopenharmony_ci snd_sbdsp_command(chip, SB_DSP_MIDI_INPUT_IRQ); 15862306a36Sopenharmony_ci chip->open &= ~SB_OPEN_MIDI_INPUT_TRIGGER; 15962306a36Sopenharmony_ci } 16062306a36Sopenharmony_ci } 16162306a36Sopenharmony_ci spin_unlock_irqrestore(&chip->open_lock, flags); 16262306a36Sopenharmony_ci} 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_cistatic void snd_sb8dsp_midi_output_write(struct snd_rawmidi_substream *substream) 16562306a36Sopenharmony_ci{ 16662306a36Sopenharmony_ci unsigned long flags; 16762306a36Sopenharmony_ci struct snd_sb *chip; 16862306a36Sopenharmony_ci char byte; 16962306a36Sopenharmony_ci int max = 32; 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci /* how big is Tx FIFO? */ 17262306a36Sopenharmony_ci chip = substream->rmidi->private_data; 17362306a36Sopenharmony_ci while (max-- > 0) { 17462306a36Sopenharmony_ci spin_lock_irqsave(&chip->open_lock, flags); 17562306a36Sopenharmony_ci if (snd_rawmidi_transmit_peek(substream, &byte, 1) != 1) { 17662306a36Sopenharmony_ci chip->open &= ~SB_OPEN_MIDI_OUTPUT_TRIGGER; 17762306a36Sopenharmony_ci del_timer(&chip->midi_timer); 17862306a36Sopenharmony_ci spin_unlock_irqrestore(&chip->open_lock, flags); 17962306a36Sopenharmony_ci break; 18062306a36Sopenharmony_ci } 18162306a36Sopenharmony_ci if (chip->hardware >= SB_HW_20) { 18262306a36Sopenharmony_ci int timeout = 8; 18362306a36Sopenharmony_ci while ((inb(SBP(chip, STATUS)) & 0x80) != 0 && --timeout > 0) 18462306a36Sopenharmony_ci ; 18562306a36Sopenharmony_ci if (timeout == 0) { 18662306a36Sopenharmony_ci /* Tx FIFO full - try again later */ 18762306a36Sopenharmony_ci spin_unlock_irqrestore(&chip->open_lock, flags); 18862306a36Sopenharmony_ci break; 18962306a36Sopenharmony_ci } 19062306a36Sopenharmony_ci outb(byte, SBP(chip, WRITE)); 19162306a36Sopenharmony_ci } else { 19262306a36Sopenharmony_ci snd_sbdsp_command(chip, SB_DSP_MIDI_OUTPUT); 19362306a36Sopenharmony_ci snd_sbdsp_command(chip, byte); 19462306a36Sopenharmony_ci } 19562306a36Sopenharmony_ci snd_rawmidi_transmit_ack(substream, 1); 19662306a36Sopenharmony_ci spin_unlock_irqrestore(&chip->open_lock, flags); 19762306a36Sopenharmony_ci } 19862306a36Sopenharmony_ci} 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_cistatic void snd_sb8dsp_midi_output_timer(struct timer_list *t) 20162306a36Sopenharmony_ci{ 20262306a36Sopenharmony_ci struct snd_sb *chip = from_timer(chip, t, midi_timer); 20362306a36Sopenharmony_ci struct snd_rawmidi_substream *substream = chip->midi_substream_output; 20462306a36Sopenharmony_ci unsigned long flags; 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci spin_lock_irqsave(&chip->open_lock, flags); 20762306a36Sopenharmony_ci mod_timer(&chip->midi_timer, 1 + jiffies); 20862306a36Sopenharmony_ci spin_unlock_irqrestore(&chip->open_lock, flags); 20962306a36Sopenharmony_ci snd_sb8dsp_midi_output_write(substream); 21062306a36Sopenharmony_ci} 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_cistatic void snd_sb8dsp_midi_output_trigger(struct snd_rawmidi_substream *substream, int up) 21362306a36Sopenharmony_ci{ 21462306a36Sopenharmony_ci unsigned long flags; 21562306a36Sopenharmony_ci struct snd_sb *chip; 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci chip = substream->rmidi->private_data; 21862306a36Sopenharmony_ci spin_lock_irqsave(&chip->open_lock, flags); 21962306a36Sopenharmony_ci if (up) { 22062306a36Sopenharmony_ci if (!(chip->open & SB_OPEN_MIDI_OUTPUT_TRIGGER)) { 22162306a36Sopenharmony_ci mod_timer(&chip->midi_timer, 1 + jiffies); 22262306a36Sopenharmony_ci chip->open |= SB_OPEN_MIDI_OUTPUT_TRIGGER; 22362306a36Sopenharmony_ci } 22462306a36Sopenharmony_ci } else { 22562306a36Sopenharmony_ci if (chip->open & SB_OPEN_MIDI_OUTPUT_TRIGGER) { 22662306a36Sopenharmony_ci chip->open &= ~SB_OPEN_MIDI_OUTPUT_TRIGGER; 22762306a36Sopenharmony_ci } 22862306a36Sopenharmony_ci } 22962306a36Sopenharmony_ci spin_unlock_irqrestore(&chip->open_lock, flags); 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci if (up) 23262306a36Sopenharmony_ci snd_sb8dsp_midi_output_write(substream); 23362306a36Sopenharmony_ci} 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_cistatic const struct snd_rawmidi_ops snd_sb8dsp_midi_output = 23662306a36Sopenharmony_ci{ 23762306a36Sopenharmony_ci .open = snd_sb8dsp_midi_output_open, 23862306a36Sopenharmony_ci .close = snd_sb8dsp_midi_output_close, 23962306a36Sopenharmony_ci .trigger = snd_sb8dsp_midi_output_trigger, 24062306a36Sopenharmony_ci}; 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_cistatic const struct snd_rawmidi_ops snd_sb8dsp_midi_input = 24362306a36Sopenharmony_ci{ 24462306a36Sopenharmony_ci .open = snd_sb8dsp_midi_input_open, 24562306a36Sopenharmony_ci .close = snd_sb8dsp_midi_input_close, 24662306a36Sopenharmony_ci .trigger = snd_sb8dsp_midi_input_trigger, 24762306a36Sopenharmony_ci}; 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ciint snd_sb8dsp_midi(struct snd_sb *chip, int device) 25062306a36Sopenharmony_ci{ 25162306a36Sopenharmony_ci struct snd_rawmidi *rmidi; 25262306a36Sopenharmony_ci int err; 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci err = snd_rawmidi_new(chip->card, "SB8 MIDI", device, 1, 1, &rmidi); 25562306a36Sopenharmony_ci if (err < 0) 25662306a36Sopenharmony_ci return err; 25762306a36Sopenharmony_ci strcpy(rmidi->name, "SB8 MIDI"); 25862306a36Sopenharmony_ci snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_sb8dsp_midi_output); 25962306a36Sopenharmony_ci snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_sb8dsp_midi_input); 26062306a36Sopenharmony_ci rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT | SNDRV_RAWMIDI_INFO_INPUT; 26162306a36Sopenharmony_ci if (chip->hardware >= SB_HW_20) 26262306a36Sopenharmony_ci rmidi->info_flags |= SNDRV_RAWMIDI_INFO_DUPLEX; 26362306a36Sopenharmony_ci rmidi->private_data = chip; 26462306a36Sopenharmony_ci timer_setup(&chip->midi_timer, snd_sb8dsp_midi_output_timer, 0); 26562306a36Sopenharmony_ci chip->rmidi = rmidi; 26662306a36Sopenharmony_ci return 0; 26762306a36Sopenharmony_ci} 268