18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (c) by Jaroslav Kysela <perex@perex.cz> 48c2ecf20Sopenharmony_ci * Routines for control of SoundBlaster cards - MIDI interface 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * -- 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * Sun May 9 22:54:38 BST 1999 George David Morrison <gdm@gedamo.demon.co.uk> 98c2ecf20Sopenharmony_ci * Fixed typo in snd_sb8dsp_midi_new_device which prevented midi from 108c2ecf20Sopenharmony_ci * working. 118c2ecf20Sopenharmony_ci * 128c2ecf20Sopenharmony_ci * Sun May 11 12:34:56 UTC 2003 Clemens Ladisch <clemens@ladisch.de> 138c2ecf20Sopenharmony_ci * Added full duplex UART mode for DSP version 2.0 and later. 148c2ecf20Sopenharmony_ci */ 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#include <linux/io.h> 178c2ecf20Sopenharmony_ci#include <linux/time.h> 188c2ecf20Sopenharmony_ci#include <sound/core.h> 198c2ecf20Sopenharmony_ci#include <sound/sb.h> 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ciirqreturn_t snd_sb8dsp_midi_interrupt(struct snd_sb *chip) 238c2ecf20Sopenharmony_ci{ 248c2ecf20Sopenharmony_ci struct snd_rawmidi *rmidi; 258c2ecf20Sopenharmony_ci int max = 64; 268c2ecf20Sopenharmony_ci char byte; 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci if (!chip) 298c2ecf20Sopenharmony_ci return IRQ_NONE; 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci rmidi = chip->rmidi; 328c2ecf20Sopenharmony_ci if (!rmidi) { 338c2ecf20Sopenharmony_ci inb(SBP(chip, DATA_AVAIL)); /* ack interrupt */ 348c2ecf20Sopenharmony_ci return IRQ_NONE; 358c2ecf20Sopenharmony_ci } 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci spin_lock(&chip->midi_input_lock); 388c2ecf20Sopenharmony_ci while (max-- > 0) { 398c2ecf20Sopenharmony_ci if (inb(SBP(chip, DATA_AVAIL)) & 0x80) { 408c2ecf20Sopenharmony_ci byte = inb(SBP(chip, READ)); 418c2ecf20Sopenharmony_ci if (chip->open & SB_OPEN_MIDI_INPUT_TRIGGER) { 428c2ecf20Sopenharmony_ci snd_rawmidi_receive(chip->midi_substream_input, &byte, 1); 438c2ecf20Sopenharmony_ci } 448c2ecf20Sopenharmony_ci } 458c2ecf20Sopenharmony_ci } 468c2ecf20Sopenharmony_ci spin_unlock(&chip->midi_input_lock); 478c2ecf20Sopenharmony_ci return IRQ_HANDLED; 488c2ecf20Sopenharmony_ci} 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_cistatic int snd_sb8dsp_midi_input_open(struct snd_rawmidi_substream *substream) 518c2ecf20Sopenharmony_ci{ 528c2ecf20Sopenharmony_ci unsigned long flags; 538c2ecf20Sopenharmony_ci struct snd_sb *chip; 548c2ecf20Sopenharmony_ci unsigned int valid_open_flags; 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci chip = substream->rmidi->private_data; 578c2ecf20Sopenharmony_ci valid_open_flags = chip->hardware >= SB_HW_20 588c2ecf20Sopenharmony_ci ? SB_OPEN_MIDI_OUTPUT | SB_OPEN_MIDI_OUTPUT_TRIGGER : 0; 598c2ecf20Sopenharmony_ci spin_lock_irqsave(&chip->open_lock, flags); 608c2ecf20Sopenharmony_ci if (chip->open & ~valid_open_flags) { 618c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&chip->open_lock, flags); 628c2ecf20Sopenharmony_ci return -EAGAIN; 638c2ecf20Sopenharmony_ci } 648c2ecf20Sopenharmony_ci chip->open |= SB_OPEN_MIDI_INPUT; 658c2ecf20Sopenharmony_ci chip->midi_substream_input = substream; 668c2ecf20Sopenharmony_ci if (!(chip->open & SB_OPEN_MIDI_OUTPUT)) { 678c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&chip->open_lock, flags); 688c2ecf20Sopenharmony_ci snd_sbdsp_reset(chip); /* reset DSP */ 698c2ecf20Sopenharmony_ci if (chip->hardware >= SB_HW_20) 708c2ecf20Sopenharmony_ci snd_sbdsp_command(chip, SB_DSP_MIDI_UART_IRQ); 718c2ecf20Sopenharmony_ci } else { 728c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&chip->open_lock, flags); 738c2ecf20Sopenharmony_ci } 748c2ecf20Sopenharmony_ci return 0; 758c2ecf20Sopenharmony_ci} 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_cistatic int snd_sb8dsp_midi_output_open(struct snd_rawmidi_substream *substream) 788c2ecf20Sopenharmony_ci{ 798c2ecf20Sopenharmony_ci unsigned long flags; 808c2ecf20Sopenharmony_ci struct snd_sb *chip; 818c2ecf20Sopenharmony_ci unsigned int valid_open_flags; 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci chip = substream->rmidi->private_data; 848c2ecf20Sopenharmony_ci valid_open_flags = chip->hardware >= SB_HW_20 858c2ecf20Sopenharmony_ci ? SB_OPEN_MIDI_INPUT | SB_OPEN_MIDI_INPUT_TRIGGER : 0; 868c2ecf20Sopenharmony_ci spin_lock_irqsave(&chip->open_lock, flags); 878c2ecf20Sopenharmony_ci if (chip->open & ~valid_open_flags) { 888c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&chip->open_lock, flags); 898c2ecf20Sopenharmony_ci return -EAGAIN; 908c2ecf20Sopenharmony_ci } 918c2ecf20Sopenharmony_ci chip->open |= SB_OPEN_MIDI_OUTPUT; 928c2ecf20Sopenharmony_ci chip->midi_substream_output = substream; 938c2ecf20Sopenharmony_ci if (!(chip->open & SB_OPEN_MIDI_INPUT)) { 948c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&chip->open_lock, flags); 958c2ecf20Sopenharmony_ci snd_sbdsp_reset(chip); /* reset DSP */ 968c2ecf20Sopenharmony_ci if (chip->hardware >= SB_HW_20) 978c2ecf20Sopenharmony_ci snd_sbdsp_command(chip, SB_DSP_MIDI_UART_IRQ); 988c2ecf20Sopenharmony_ci } else { 998c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&chip->open_lock, flags); 1008c2ecf20Sopenharmony_ci } 1018c2ecf20Sopenharmony_ci return 0; 1028c2ecf20Sopenharmony_ci} 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_cistatic int snd_sb8dsp_midi_input_close(struct snd_rawmidi_substream *substream) 1058c2ecf20Sopenharmony_ci{ 1068c2ecf20Sopenharmony_ci unsigned long flags; 1078c2ecf20Sopenharmony_ci struct snd_sb *chip; 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci chip = substream->rmidi->private_data; 1108c2ecf20Sopenharmony_ci spin_lock_irqsave(&chip->open_lock, flags); 1118c2ecf20Sopenharmony_ci chip->open &= ~(SB_OPEN_MIDI_INPUT | SB_OPEN_MIDI_INPUT_TRIGGER); 1128c2ecf20Sopenharmony_ci chip->midi_substream_input = NULL; 1138c2ecf20Sopenharmony_ci if (!(chip->open & SB_OPEN_MIDI_OUTPUT)) { 1148c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&chip->open_lock, flags); 1158c2ecf20Sopenharmony_ci snd_sbdsp_reset(chip); /* reset DSP */ 1168c2ecf20Sopenharmony_ci } else { 1178c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&chip->open_lock, flags); 1188c2ecf20Sopenharmony_ci } 1198c2ecf20Sopenharmony_ci return 0; 1208c2ecf20Sopenharmony_ci} 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_cistatic int snd_sb8dsp_midi_output_close(struct snd_rawmidi_substream *substream) 1238c2ecf20Sopenharmony_ci{ 1248c2ecf20Sopenharmony_ci unsigned long flags; 1258c2ecf20Sopenharmony_ci struct snd_sb *chip; 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci chip = substream->rmidi->private_data; 1288c2ecf20Sopenharmony_ci del_timer_sync(&chip->midi_timer); 1298c2ecf20Sopenharmony_ci spin_lock_irqsave(&chip->open_lock, flags); 1308c2ecf20Sopenharmony_ci chip->open &= ~(SB_OPEN_MIDI_OUTPUT | SB_OPEN_MIDI_OUTPUT_TRIGGER); 1318c2ecf20Sopenharmony_ci chip->midi_substream_output = NULL; 1328c2ecf20Sopenharmony_ci if (!(chip->open & SB_OPEN_MIDI_INPUT)) { 1338c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&chip->open_lock, flags); 1348c2ecf20Sopenharmony_ci snd_sbdsp_reset(chip); /* reset DSP */ 1358c2ecf20Sopenharmony_ci } else { 1368c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&chip->open_lock, flags); 1378c2ecf20Sopenharmony_ci } 1388c2ecf20Sopenharmony_ci return 0; 1398c2ecf20Sopenharmony_ci} 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_cistatic void snd_sb8dsp_midi_input_trigger(struct snd_rawmidi_substream *substream, int up) 1428c2ecf20Sopenharmony_ci{ 1438c2ecf20Sopenharmony_ci unsigned long flags; 1448c2ecf20Sopenharmony_ci struct snd_sb *chip; 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci chip = substream->rmidi->private_data; 1478c2ecf20Sopenharmony_ci spin_lock_irqsave(&chip->open_lock, flags); 1488c2ecf20Sopenharmony_ci if (up) { 1498c2ecf20Sopenharmony_ci if (!(chip->open & SB_OPEN_MIDI_INPUT_TRIGGER)) { 1508c2ecf20Sopenharmony_ci if (chip->hardware < SB_HW_20) 1518c2ecf20Sopenharmony_ci snd_sbdsp_command(chip, SB_DSP_MIDI_INPUT_IRQ); 1528c2ecf20Sopenharmony_ci chip->open |= SB_OPEN_MIDI_INPUT_TRIGGER; 1538c2ecf20Sopenharmony_ci } 1548c2ecf20Sopenharmony_ci } else { 1558c2ecf20Sopenharmony_ci if (chip->open & SB_OPEN_MIDI_INPUT_TRIGGER) { 1568c2ecf20Sopenharmony_ci if (chip->hardware < SB_HW_20) 1578c2ecf20Sopenharmony_ci snd_sbdsp_command(chip, SB_DSP_MIDI_INPUT_IRQ); 1588c2ecf20Sopenharmony_ci chip->open &= ~SB_OPEN_MIDI_INPUT_TRIGGER; 1598c2ecf20Sopenharmony_ci } 1608c2ecf20Sopenharmony_ci } 1618c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&chip->open_lock, flags); 1628c2ecf20Sopenharmony_ci} 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_cistatic void snd_sb8dsp_midi_output_write(struct snd_rawmidi_substream *substream) 1658c2ecf20Sopenharmony_ci{ 1668c2ecf20Sopenharmony_ci unsigned long flags; 1678c2ecf20Sopenharmony_ci struct snd_sb *chip; 1688c2ecf20Sopenharmony_ci char byte; 1698c2ecf20Sopenharmony_ci int max = 32; 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci /* how big is Tx FIFO? */ 1728c2ecf20Sopenharmony_ci chip = substream->rmidi->private_data; 1738c2ecf20Sopenharmony_ci while (max-- > 0) { 1748c2ecf20Sopenharmony_ci spin_lock_irqsave(&chip->open_lock, flags); 1758c2ecf20Sopenharmony_ci if (snd_rawmidi_transmit_peek(substream, &byte, 1) != 1) { 1768c2ecf20Sopenharmony_ci chip->open &= ~SB_OPEN_MIDI_OUTPUT_TRIGGER; 1778c2ecf20Sopenharmony_ci del_timer(&chip->midi_timer); 1788c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&chip->open_lock, flags); 1798c2ecf20Sopenharmony_ci break; 1808c2ecf20Sopenharmony_ci } 1818c2ecf20Sopenharmony_ci if (chip->hardware >= SB_HW_20) { 1828c2ecf20Sopenharmony_ci int timeout = 8; 1838c2ecf20Sopenharmony_ci while ((inb(SBP(chip, STATUS)) & 0x80) != 0 && --timeout > 0) 1848c2ecf20Sopenharmony_ci ; 1858c2ecf20Sopenharmony_ci if (timeout == 0) { 1868c2ecf20Sopenharmony_ci /* Tx FIFO full - try again later */ 1878c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&chip->open_lock, flags); 1888c2ecf20Sopenharmony_ci break; 1898c2ecf20Sopenharmony_ci } 1908c2ecf20Sopenharmony_ci outb(byte, SBP(chip, WRITE)); 1918c2ecf20Sopenharmony_ci } else { 1928c2ecf20Sopenharmony_ci snd_sbdsp_command(chip, SB_DSP_MIDI_OUTPUT); 1938c2ecf20Sopenharmony_ci snd_sbdsp_command(chip, byte); 1948c2ecf20Sopenharmony_ci } 1958c2ecf20Sopenharmony_ci snd_rawmidi_transmit_ack(substream, 1); 1968c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&chip->open_lock, flags); 1978c2ecf20Sopenharmony_ci } 1988c2ecf20Sopenharmony_ci} 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_cistatic void snd_sb8dsp_midi_output_timer(struct timer_list *t) 2018c2ecf20Sopenharmony_ci{ 2028c2ecf20Sopenharmony_ci struct snd_sb *chip = from_timer(chip, t, midi_timer); 2038c2ecf20Sopenharmony_ci struct snd_rawmidi_substream *substream = chip->midi_substream_output; 2048c2ecf20Sopenharmony_ci unsigned long flags; 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci spin_lock_irqsave(&chip->open_lock, flags); 2078c2ecf20Sopenharmony_ci mod_timer(&chip->midi_timer, 1 + jiffies); 2088c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&chip->open_lock, flags); 2098c2ecf20Sopenharmony_ci snd_sb8dsp_midi_output_write(substream); 2108c2ecf20Sopenharmony_ci} 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_cistatic void snd_sb8dsp_midi_output_trigger(struct snd_rawmidi_substream *substream, int up) 2138c2ecf20Sopenharmony_ci{ 2148c2ecf20Sopenharmony_ci unsigned long flags; 2158c2ecf20Sopenharmony_ci struct snd_sb *chip; 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci chip = substream->rmidi->private_data; 2188c2ecf20Sopenharmony_ci spin_lock_irqsave(&chip->open_lock, flags); 2198c2ecf20Sopenharmony_ci if (up) { 2208c2ecf20Sopenharmony_ci if (!(chip->open & SB_OPEN_MIDI_OUTPUT_TRIGGER)) { 2218c2ecf20Sopenharmony_ci mod_timer(&chip->midi_timer, 1 + jiffies); 2228c2ecf20Sopenharmony_ci chip->open |= SB_OPEN_MIDI_OUTPUT_TRIGGER; 2238c2ecf20Sopenharmony_ci } 2248c2ecf20Sopenharmony_ci } else { 2258c2ecf20Sopenharmony_ci if (chip->open & SB_OPEN_MIDI_OUTPUT_TRIGGER) { 2268c2ecf20Sopenharmony_ci chip->open &= ~SB_OPEN_MIDI_OUTPUT_TRIGGER; 2278c2ecf20Sopenharmony_ci } 2288c2ecf20Sopenharmony_ci } 2298c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&chip->open_lock, flags); 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci if (up) 2328c2ecf20Sopenharmony_ci snd_sb8dsp_midi_output_write(substream); 2338c2ecf20Sopenharmony_ci} 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_cistatic const struct snd_rawmidi_ops snd_sb8dsp_midi_output = 2368c2ecf20Sopenharmony_ci{ 2378c2ecf20Sopenharmony_ci .open = snd_sb8dsp_midi_output_open, 2388c2ecf20Sopenharmony_ci .close = snd_sb8dsp_midi_output_close, 2398c2ecf20Sopenharmony_ci .trigger = snd_sb8dsp_midi_output_trigger, 2408c2ecf20Sopenharmony_ci}; 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_cistatic const struct snd_rawmidi_ops snd_sb8dsp_midi_input = 2438c2ecf20Sopenharmony_ci{ 2448c2ecf20Sopenharmony_ci .open = snd_sb8dsp_midi_input_open, 2458c2ecf20Sopenharmony_ci .close = snd_sb8dsp_midi_input_close, 2468c2ecf20Sopenharmony_ci .trigger = snd_sb8dsp_midi_input_trigger, 2478c2ecf20Sopenharmony_ci}; 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ciint snd_sb8dsp_midi(struct snd_sb *chip, int device) 2508c2ecf20Sopenharmony_ci{ 2518c2ecf20Sopenharmony_ci struct snd_rawmidi *rmidi; 2528c2ecf20Sopenharmony_ci int err; 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci if ((err = snd_rawmidi_new(chip->card, "SB8 MIDI", device, 1, 1, &rmidi)) < 0) 2558c2ecf20Sopenharmony_ci return err; 2568c2ecf20Sopenharmony_ci strcpy(rmidi->name, "SB8 MIDI"); 2578c2ecf20Sopenharmony_ci snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_sb8dsp_midi_output); 2588c2ecf20Sopenharmony_ci snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_sb8dsp_midi_input); 2598c2ecf20Sopenharmony_ci rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT | SNDRV_RAWMIDI_INFO_INPUT; 2608c2ecf20Sopenharmony_ci if (chip->hardware >= SB_HW_20) 2618c2ecf20Sopenharmony_ci rmidi->info_flags |= SNDRV_RAWMIDI_INFO_DUPLEX; 2628c2ecf20Sopenharmony_ci rmidi->private_data = chip; 2638c2ecf20Sopenharmony_ci timer_setup(&chip->midi_timer, snd_sb8dsp_midi_output_timer, 0); 2648c2ecf20Sopenharmony_ci chip->rmidi = rmidi; 2658c2ecf20Sopenharmony_ci return 0; 2668c2ecf20Sopenharmony_ci} 267