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