Merge with Linus' kernel.
[sfrench/cifs-2.6.git] / sound / isa / sb / sb8_midi.c
1 /*
2  *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
3  *  Routines for control of SoundBlaster cards - MIDI interface
4  *
5  *   This program is free software; you can redistribute it and/or modify
6  *   it under the terms of the GNU General Public License as published by
7  *   the Free Software Foundation; either version 2 of the License, or
8  *   (at your option) any later version.
9  *
10  *   This program is distributed in the hope that it will be useful,
11  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *   GNU General Public License for more details.
14  *
15  *   You should have received a copy of the GNU General Public License
16  *   along with this program; if not, write to the Free Software
17  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
18  *
19  * --
20  *
21  * Sun May  9 22:54:38 BST 1999 George David Morrison <gdm@gedamo.demon.co.uk>
22  *   Fixed typo in snd_sb8dsp_midi_new_device which prevented midi from 
23  *   working.
24  *
25  * Sun May 11 12:34:56 UTC 2003 Clemens Ladisch <clemens@ladisch.de>
26  *   Added full duplex UART mode for DSP version 2.0 and later.
27  */
28
29 #include <sound/driver.h>
30 #include <asm/io.h>
31 #include <linux/time.h>
32 #include <sound/core.h>
33 #include <sound/sb.h>
34
35 /*
36
37  */
38
39 irqreturn_t snd_sb8dsp_midi_interrupt(struct snd_sb * chip)
40 {
41         struct snd_rawmidi *rmidi;
42         int max = 64;
43         char byte;
44
45         if (chip == NULL || (rmidi = chip->rmidi) == NULL) {
46                 inb(SBP(chip, DATA_AVAIL));     /* ack interrupt */
47                 return IRQ_NONE;
48         }
49         spin_lock(&chip->midi_input_lock);
50         while (max-- > 0) {
51                 if (inb(SBP(chip, DATA_AVAIL)) & 0x80) {
52                         byte = inb(SBP(chip, READ));
53                         if (chip->open & SB_OPEN_MIDI_INPUT_TRIGGER) {
54                                 snd_rawmidi_receive(chip->midi_substream_input, &byte, 1);
55                         }
56                 }
57         }
58         spin_unlock(&chip->midi_input_lock);
59         return IRQ_HANDLED;
60 }
61
62 /*
63
64  */
65
66 static int snd_sb8dsp_midi_input_open(struct snd_rawmidi_substream *substream)
67 {
68         unsigned long flags;
69         struct snd_sb *chip;
70         unsigned int valid_open_flags;
71
72         chip = substream->rmidi->private_data;
73         valid_open_flags = chip->hardware >= SB_HW_20
74                 ? SB_OPEN_MIDI_OUTPUT | SB_OPEN_MIDI_OUTPUT_TRIGGER : 0;
75         spin_lock_irqsave(&chip->open_lock, flags);
76         if (chip->open & ~valid_open_flags) {
77                 spin_unlock_irqrestore(&chip->open_lock, flags);
78                 return -EAGAIN;
79         }
80         chip->open |= SB_OPEN_MIDI_INPUT;
81         chip->midi_substream_input = substream;
82         if (!(chip->open & SB_OPEN_MIDI_OUTPUT)) {
83                 spin_unlock_irqrestore(&chip->open_lock, flags);
84                 snd_sbdsp_reset(chip);          /* reset DSP */
85                 if (chip->hardware >= SB_HW_20)
86                         snd_sbdsp_command(chip, SB_DSP_MIDI_UART_IRQ);
87         } else {
88                 spin_unlock_irqrestore(&chip->open_lock, flags);
89         }
90         return 0;
91 }
92
93 static int snd_sb8dsp_midi_output_open(struct snd_rawmidi_substream *substream)
94 {
95         unsigned long flags;
96         struct snd_sb *chip;
97         unsigned int valid_open_flags;
98
99         chip = substream->rmidi->private_data;
100         valid_open_flags = chip->hardware >= SB_HW_20
101                 ? SB_OPEN_MIDI_INPUT | SB_OPEN_MIDI_INPUT_TRIGGER : 0;
102         spin_lock_irqsave(&chip->open_lock, flags);
103         if (chip->open & ~valid_open_flags) {
104                 spin_unlock_irqrestore(&chip->open_lock, flags);
105                 return -EAGAIN;
106         }
107         chip->open |= SB_OPEN_MIDI_OUTPUT;
108         chip->midi_substream_output = substream;
109         if (!(chip->open & SB_OPEN_MIDI_INPUT)) {
110                 spin_unlock_irqrestore(&chip->open_lock, flags);
111                 snd_sbdsp_reset(chip);          /* reset DSP */
112                 if (chip->hardware >= SB_HW_20)
113                         snd_sbdsp_command(chip, SB_DSP_MIDI_UART_IRQ);
114         } else {
115                 spin_unlock_irqrestore(&chip->open_lock, flags);
116         }
117         return 0;
118 }
119
120 static int snd_sb8dsp_midi_input_close(struct snd_rawmidi_substream *substream)
121 {
122         unsigned long flags;
123         struct snd_sb *chip;
124
125         chip = substream->rmidi->private_data;
126         spin_lock_irqsave(&chip->open_lock, flags);
127         chip->open &= ~(SB_OPEN_MIDI_INPUT | SB_OPEN_MIDI_INPUT_TRIGGER);
128         chip->midi_substream_input = NULL;
129         if (!(chip->open & SB_OPEN_MIDI_OUTPUT)) {
130                 spin_unlock_irqrestore(&chip->open_lock, flags);
131                 snd_sbdsp_reset(chip);          /* reset DSP */
132         } else {
133                 spin_unlock_irqrestore(&chip->open_lock, flags);
134         }
135         return 0;
136 }
137
138 static int snd_sb8dsp_midi_output_close(struct snd_rawmidi_substream *substream)
139 {
140         unsigned long flags;
141         struct snd_sb *chip;
142
143         chip = substream->rmidi->private_data;
144         spin_lock_irqsave(&chip->open_lock, flags);
145         chip->open &= ~(SB_OPEN_MIDI_OUTPUT | SB_OPEN_MIDI_OUTPUT_TRIGGER);
146         chip->midi_substream_output = NULL;
147         if (!(chip->open & SB_OPEN_MIDI_INPUT)) {
148                 spin_unlock_irqrestore(&chip->open_lock, flags);
149                 snd_sbdsp_reset(chip);          /* reset DSP */
150         } else {
151                 spin_unlock_irqrestore(&chip->open_lock, flags);
152         }
153         return 0;
154 }
155
156 static void snd_sb8dsp_midi_input_trigger(struct snd_rawmidi_substream *substream, int up)
157 {
158         unsigned long flags;
159         struct snd_sb *chip;
160
161         chip = substream->rmidi->private_data;
162         spin_lock_irqsave(&chip->open_lock, flags);
163         if (up) {
164                 if (!(chip->open & SB_OPEN_MIDI_INPUT_TRIGGER)) {
165                         if (chip->hardware < SB_HW_20)
166                                 snd_sbdsp_command(chip, SB_DSP_MIDI_INPUT_IRQ);
167                         chip->open |= SB_OPEN_MIDI_INPUT_TRIGGER;
168                 }
169         } else {
170                 if (chip->open & SB_OPEN_MIDI_INPUT_TRIGGER) {
171                         if (chip->hardware < SB_HW_20)
172                                 snd_sbdsp_command(chip, SB_DSP_MIDI_INPUT_IRQ);
173                         chip->open &= ~SB_OPEN_MIDI_INPUT_TRIGGER;
174                 }
175         }
176         spin_unlock_irqrestore(&chip->open_lock, flags);
177 }
178
179 static void snd_sb8dsp_midi_output_write(struct snd_rawmidi_substream *substream)
180 {
181         unsigned long flags;
182         struct snd_sb *chip;
183         char byte;
184         int max = 32;
185
186         /* how big is Tx FIFO? */
187         chip = substream->rmidi->private_data;
188         while (max-- > 0) {
189                 spin_lock_irqsave(&chip->open_lock, flags);
190                 if (snd_rawmidi_transmit_peek(substream, &byte, 1) != 1) {
191                         chip->open &= ~SB_OPEN_MIDI_OUTPUT_TRIGGER;
192                         del_timer(&chip->midi_timer);
193                         spin_unlock_irqrestore(&chip->open_lock, flags);
194                         break;
195                 }
196                 if (chip->hardware >= SB_HW_20) {
197                         int timeout = 8;
198                         while ((inb(SBP(chip, STATUS)) & 0x80) != 0 && --timeout > 0)
199                                 ;
200                         if (timeout == 0) {
201                                 /* Tx FIFO full - try again later */
202                                 spin_unlock_irqrestore(&chip->open_lock, flags);
203                                 break;
204                         }
205                         outb(byte, SBP(chip, WRITE));
206                 } else {
207                         snd_sbdsp_command(chip, SB_DSP_MIDI_OUTPUT);
208                         snd_sbdsp_command(chip, byte);
209                 }
210                 snd_rawmidi_transmit_ack(substream, 1);
211                 spin_unlock_irqrestore(&chip->open_lock, flags);
212         }
213 }
214
215 static void snd_sb8dsp_midi_output_timer(unsigned long data)
216 {
217         struct snd_rawmidi_substream *substream = (struct snd_rawmidi_substream *) data;
218         struct snd_sb * chip = substream->rmidi->private_data;
219         unsigned long flags;
220
221         spin_lock_irqsave(&chip->open_lock, flags);
222         chip->midi_timer.expires = 1 + jiffies;
223         add_timer(&chip->midi_timer);
224         spin_unlock_irqrestore(&chip->open_lock, flags);        
225         snd_sb8dsp_midi_output_write(substream);
226 }
227
228 static void snd_sb8dsp_midi_output_trigger(struct snd_rawmidi_substream *substream, int up)
229 {
230         unsigned long flags;
231         struct snd_sb *chip;
232
233         chip = substream->rmidi->private_data;
234         spin_lock_irqsave(&chip->open_lock, flags);
235         if (up) {
236                 if (!(chip->open & SB_OPEN_MIDI_OUTPUT_TRIGGER)) {
237                         init_timer(&chip->midi_timer);
238                         chip->midi_timer.function = snd_sb8dsp_midi_output_timer;
239                         chip->midi_timer.data = (unsigned long) substream;
240                         chip->midi_timer.expires = 1 + jiffies;
241                         add_timer(&chip->midi_timer);
242                         chip->open |= SB_OPEN_MIDI_OUTPUT_TRIGGER;
243                 }
244         } else {
245                 if (chip->open & SB_OPEN_MIDI_OUTPUT_TRIGGER) {
246                         chip->open &= ~SB_OPEN_MIDI_OUTPUT_TRIGGER;
247                 }
248         }
249         spin_unlock_irqrestore(&chip->open_lock, flags);
250
251         if (up)
252                 snd_sb8dsp_midi_output_write(substream);
253 }
254
255 /*
256
257  */
258
259 static struct snd_rawmidi_ops snd_sb8dsp_midi_output =
260 {
261         .open =         snd_sb8dsp_midi_output_open,
262         .close =        snd_sb8dsp_midi_output_close,
263         .trigger =      snd_sb8dsp_midi_output_trigger,
264 };
265
266 static struct snd_rawmidi_ops snd_sb8dsp_midi_input =
267 {
268         .open =         snd_sb8dsp_midi_input_open,
269         .close =        snd_sb8dsp_midi_input_close,
270         .trigger =      snd_sb8dsp_midi_input_trigger,
271 };
272
273 int snd_sb8dsp_midi(struct snd_sb *chip, int device, struct snd_rawmidi ** rrawmidi)
274 {
275         struct snd_rawmidi *rmidi;
276         int err;
277
278         if (rrawmidi)
279                 *rrawmidi = NULL;
280         if ((err = snd_rawmidi_new(chip->card, "SB8 MIDI", device, 1, 1, &rmidi)) < 0)
281                 return err;
282         strcpy(rmidi->name, "SB8 MIDI");
283         snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_sb8dsp_midi_output);
284         snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_sb8dsp_midi_input);
285         rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT | SNDRV_RAWMIDI_INFO_INPUT;
286         if (chip->hardware >= SB_HW_20)
287                 rmidi->info_flags |= SNDRV_RAWMIDI_INFO_DUPLEX;
288         rmidi->private_data = chip;
289         chip->rmidi = rmidi;
290         if (rrawmidi)
291                 *rrawmidi = rmidi;
292         return 0;
293 }