xref: /linux/sound/isa/sb/sb8_midi.c (revision be54f8c558027a218423134dd9b8c7c46d92204a)
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  *  Copyright (c) by Jaroslav Kysela <perex@perex.cz>
4  *  Routines for control of SoundBlaster cards - MIDI interface
5  *
6  * --
7  *
8  * Sun May  9 22:54:38 BST 1999 George David Morrison <gdm@gedamo.demon.co.uk>
9  *   Fixed typo in snd_sb8dsp_midi_new_device which prevented midi from
10  *   working.
11  *
12  * Sun May 11 12:34:56 UTC 2003 Clemens Ladisch <clemens@ladisch.de>
13  *   Added full duplex UART mode for DSP version 2.0 and later.
14  */
15 
16 #include <linux/io.h>
17 #include <linux/string.h>
18 #include <linux/time.h>
19 #include <sound/core.h>
20 #include <sound/sb.h>
21 
22 
snd_sb8dsp_midi_interrupt(struct snd_sb * chip)23 irqreturn_t snd_sb8dsp_midi_interrupt(struct snd_sb *chip)
24 {
25 	struct snd_rawmidi *rmidi;
26 	int max = 64;
27 	char byte;
28 
29 	if (!chip)
30 		return IRQ_NONE;
31 
32 	rmidi = chip->rmidi;
33 	if (!rmidi) {
34 		inb(SBP(chip, DATA_AVAIL));	/* ack interrupt */
35 		return IRQ_NONE;
36 	}
37 
38 	spin_lock(&chip->midi_input_lock);
39 	while (max-- > 0) {
40 		if (inb(SBP(chip, DATA_AVAIL)) & 0x80) {
41 			byte = inb(SBP(chip, READ));
42 			if (chip->open & SB_OPEN_MIDI_INPUT_TRIGGER) {
43 				snd_rawmidi_receive(chip->midi_substream_input, &byte, 1);
44 			}
45 		}
46 	}
47 	spin_unlock(&chip->midi_input_lock);
48 	return IRQ_HANDLED;
49 }
50 
snd_sb8dsp_midi_input_open(struct snd_rawmidi_substream * substream)51 static int snd_sb8dsp_midi_input_open(struct snd_rawmidi_substream *substream)
52 {
53 	unsigned long flags;
54 	struct snd_sb *chip;
55 	unsigned int valid_open_flags;
56 
57 	chip = substream->rmidi->private_data;
58 	valid_open_flags = chip->hardware >= SB_HW_20
59 		? SB_OPEN_MIDI_OUTPUT | SB_OPEN_MIDI_OUTPUT_TRIGGER : 0;
60 	spin_lock_irqsave(&chip->open_lock, flags);
61 	if (chip->open & ~valid_open_flags) {
62 		spin_unlock_irqrestore(&chip->open_lock, flags);
63 		return -EAGAIN;
64 	}
65 	chip->open |= SB_OPEN_MIDI_INPUT;
66 	chip->midi_substream_input = substream;
67 	if (!(chip->open & SB_OPEN_MIDI_OUTPUT)) {
68 		spin_unlock_irqrestore(&chip->open_lock, flags);
69 		snd_sbdsp_reset(chip);		/* reset DSP */
70 		if (chip->hardware >= SB_HW_20)
71 			snd_sbdsp_command(chip, SB_DSP_MIDI_UART_IRQ);
72 	} else {
73 		spin_unlock_irqrestore(&chip->open_lock, flags);
74 	}
75 	return 0;
76 }
77 
snd_sb8dsp_midi_output_open(struct snd_rawmidi_substream * substream)78 static int snd_sb8dsp_midi_output_open(struct snd_rawmidi_substream *substream)
79 {
80 	unsigned long flags;
81 	struct snd_sb *chip;
82 	unsigned int valid_open_flags;
83 
84 	chip = substream->rmidi->private_data;
85 	valid_open_flags = chip->hardware >= SB_HW_20
86 		? SB_OPEN_MIDI_INPUT | SB_OPEN_MIDI_INPUT_TRIGGER : 0;
87 	spin_lock_irqsave(&chip->open_lock, flags);
88 	if (chip->open & ~valid_open_flags) {
89 		spin_unlock_irqrestore(&chip->open_lock, flags);
90 		return -EAGAIN;
91 	}
92 	chip->open |= SB_OPEN_MIDI_OUTPUT;
93 	chip->midi_substream_output = substream;
94 	if (!(chip->open & SB_OPEN_MIDI_INPUT)) {
95 		spin_unlock_irqrestore(&chip->open_lock, flags);
96 		snd_sbdsp_reset(chip);		/* reset DSP */
97 		if (chip->hardware >= SB_HW_20)
98 			snd_sbdsp_command(chip, SB_DSP_MIDI_UART_IRQ);
99 	} else {
100 		spin_unlock_irqrestore(&chip->open_lock, flags);
101 	}
102 	return 0;
103 }
104 
snd_sb8dsp_midi_input_close(struct snd_rawmidi_substream * substream)105 static int snd_sb8dsp_midi_input_close(struct snd_rawmidi_substream *substream)
106 {
107 	unsigned long flags;
108 	struct snd_sb *chip;
109 
110 	chip = substream->rmidi->private_data;
111 	spin_lock_irqsave(&chip->open_lock, flags);
112 	chip->open &= ~(SB_OPEN_MIDI_INPUT | SB_OPEN_MIDI_INPUT_TRIGGER);
113 	chip->midi_substream_input = NULL;
114 	if (!(chip->open & SB_OPEN_MIDI_OUTPUT)) {
115 		spin_unlock_irqrestore(&chip->open_lock, flags);
116 		snd_sbdsp_reset(chip);		/* reset DSP */
117 	} else {
118 		spin_unlock_irqrestore(&chip->open_lock, flags);
119 	}
120 	return 0;
121 }
122 
snd_sb8dsp_midi_output_close(struct snd_rawmidi_substream * substream)123 static int snd_sb8dsp_midi_output_close(struct snd_rawmidi_substream *substream)
124 {
125 	unsigned long flags;
126 	struct snd_sb *chip;
127 
128 	chip = substream->rmidi->private_data;
129 	timer_delete_sync(&chip->midi_timer);
130 	spin_lock_irqsave(&chip->open_lock, flags);
131 	chip->open &= ~(SB_OPEN_MIDI_OUTPUT | SB_OPEN_MIDI_OUTPUT_TRIGGER);
132 	chip->midi_substream_output = NULL;
133 	if (!(chip->open & SB_OPEN_MIDI_INPUT)) {
134 		spin_unlock_irqrestore(&chip->open_lock, flags);
135 		snd_sbdsp_reset(chip);		/* reset DSP */
136 	} else {
137 		spin_unlock_irqrestore(&chip->open_lock, flags);
138 	}
139 	return 0;
140 }
141 
snd_sb8dsp_midi_input_trigger(struct snd_rawmidi_substream * substream,int up)142 static void snd_sb8dsp_midi_input_trigger(struct snd_rawmidi_substream *substream, int up)
143 {
144 	unsigned long flags;
145 	struct snd_sb *chip;
146 
147 	chip = substream->rmidi->private_data;
148 	spin_lock_irqsave(&chip->open_lock, flags);
149 	if (up) {
150 		if (!(chip->open & SB_OPEN_MIDI_INPUT_TRIGGER)) {
151 			if (chip->hardware < SB_HW_20)
152 				snd_sbdsp_command(chip, SB_DSP_MIDI_INPUT_IRQ);
153 			chip->open |= SB_OPEN_MIDI_INPUT_TRIGGER;
154 		}
155 	} else {
156 		if (chip->open & SB_OPEN_MIDI_INPUT_TRIGGER) {
157 			if (chip->hardware < SB_HW_20)
158 				snd_sbdsp_command(chip, SB_DSP_MIDI_INPUT_IRQ);
159 			chip->open &= ~SB_OPEN_MIDI_INPUT_TRIGGER;
160 		}
161 	}
162 	spin_unlock_irqrestore(&chip->open_lock, flags);
163 }
164 
snd_sb8dsp_midi_output_write(struct snd_rawmidi_substream * substream)165 static void snd_sb8dsp_midi_output_write(struct snd_rawmidi_substream *substream)
166 {
167 	unsigned long flags;
168 	struct snd_sb *chip;
169 	char byte;
170 	int max = 32;
171 
172 	/* how big is Tx FIFO? */
173 	chip = substream->rmidi->private_data;
174 	while (max-- > 0) {
175 		spin_lock_irqsave(&chip->open_lock, flags);
176 		if (snd_rawmidi_transmit_peek(substream, &byte, 1) != 1) {
177 			chip->open &= ~SB_OPEN_MIDI_OUTPUT_TRIGGER;
178 			timer_delete(&chip->midi_timer);
179 			spin_unlock_irqrestore(&chip->open_lock, flags);
180 			break;
181 		}
182 		if (chip->hardware >= SB_HW_20) {
183 			int timeout = 8;
184 			while ((inb(SBP(chip, STATUS)) & 0x80) != 0 && --timeout > 0)
185 				;
186 			if (timeout == 0) {
187 				/* Tx FIFO full - try again later */
188 				spin_unlock_irqrestore(&chip->open_lock, flags);
189 				break;
190 			}
191 			outb(byte, SBP(chip, WRITE));
192 		} else {
193 			snd_sbdsp_command(chip, SB_DSP_MIDI_OUTPUT);
194 			snd_sbdsp_command(chip, byte);
195 		}
196 		snd_rawmidi_transmit_ack(substream, 1);
197 		spin_unlock_irqrestore(&chip->open_lock, flags);
198 	}
199 }
200 
snd_sb8dsp_midi_output_timer(struct timer_list * t)201 static void snd_sb8dsp_midi_output_timer(struct timer_list *t)
202 {
203 	struct snd_sb *chip = timer_container_of(chip, t, midi_timer);
204 	struct snd_rawmidi_substream *substream = chip->midi_substream_output;
205 	unsigned long flags;
206 
207 	spin_lock_irqsave(&chip->open_lock, flags);
208 	mod_timer(&chip->midi_timer, 1 + jiffies);
209 	spin_unlock_irqrestore(&chip->open_lock, flags);
210 	snd_sb8dsp_midi_output_write(substream);
211 }
212 
snd_sb8dsp_midi_output_trigger(struct snd_rawmidi_substream * substream,int up)213 static void snd_sb8dsp_midi_output_trigger(struct snd_rawmidi_substream *substream, int up)
214 {
215 	unsigned long flags;
216 	struct snd_sb *chip;
217 
218 	chip = substream->rmidi->private_data;
219 	spin_lock_irqsave(&chip->open_lock, flags);
220 	if (up) {
221 		if (!(chip->open & SB_OPEN_MIDI_OUTPUT_TRIGGER)) {
222 			mod_timer(&chip->midi_timer, 1 + jiffies);
223 			chip->open |= SB_OPEN_MIDI_OUTPUT_TRIGGER;
224 		}
225 	} else {
226 		if (chip->open & SB_OPEN_MIDI_OUTPUT_TRIGGER) {
227 			chip->open &= ~SB_OPEN_MIDI_OUTPUT_TRIGGER;
228 		}
229 	}
230 	spin_unlock_irqrestore(&chip->open_lock, flags);
231 
232 	if (up)
233 		snd_sb8dsp_midi_output_write(substream);
234 }
235 
236 static const struct snd_rawmidi_ops snd_sb8dsp_midi_output =
237 {
238 	.open =		snd_sb8dsp_midi_output_open,
239 	.close =	snd_sb8dsp_midi_output_close,
240 	.trigger =	snd_sb8dsp_midi_output_trigger,
241 };
242 
243 static const struct snd_rawmidi_ops snd_sb8dsp_midi_input =
244 {
245 	.open =		snd_sb8dsp_midi_input_open,
246 	.close =	snd_sb8dsp_midi_input_close,
247 	.trigger =	snd_sb8dsp_midi_input_trigger,
248 };
249 
snd_sb8dsp_midi(struct snd_sb * chip,int device)250 int snd_sb8dsp_midi(struct snd_sb *chip, int device)
251 {
252 	struct snd_rawmidi *rmidi;
253 	int err;
254 
255 	err = snd_rawmidi_new(chip->card, "SB8 MIDI", device, 1, 1, &rmidi);
256 	if (err < 0)
257 		return err;
258 	strscpy(rmidi->name, "SB8 MIDI");
259 	snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_sb8dsp_midi_output);
260 	snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_sb8dsp_midi_input);
261 	rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT | SNDRV_RAWMIDI_INFO_INPUT;
262 	if (chip->hardware >= SB_HW_20)
263 		rmidi->info_flags |= SNDRV_RAWMIDI_INFO_DUPLEX;
264 	rmidi->private_data = chip;
265 	timer_setup(&chip->midi_timer, snd_sb8dsp_midi_output_timer, 0);
266 	chip->rmidi = rmidi;
267 	return 0;
268 }
269