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