]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/blob - sound/pci/emu10k1/emumpu401.c
c4d76d16661eed2d5af5a49ec51e81cb22cef45c
[linux-2.6-omap-h63xx.git] / sound / pci / emu10k1 / emumpu401.c
1 /*
2  *  Copyright (c) by Jaroslav Kysela <perex@perex.cz>
3  *  Routines for control of EMU10K1 MPU-401 in UART mode
4  *
5  *
6  *   This program is free software; you can redistribute it and/or modify
7  *   it under the terms of the GNU General Public License as published by
8  *   the Free Software Foundation; either version 2 of the License, or
9  *   (at your option) any later version.
10  *
11  *   This program is distributed in the hope that it will be useful,
12  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *   GNU General Public License for more details.
15  *
16  *   You should have received a copy of the GNU General Public License
17  *   along with this program; if not, write to the Free Software
18  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
19  *
20  */
21
22 #include <linux/time.h>
23 #include <linux/init.h>
24 #include <sound/core.h>
25 #include <sound/emu10k1.h>
26
27 #define EMU10K1_MIDI_MODE_INPUT         (1<<0)
28 #define EMU10K1_MIDI_MODE_OUTPUT        (1<<1)
29
30 static inline unsigned char mpu401_read(struct snd_emu10k1 *emu,
31                                         struct snd_emu10k1_midi *mpu, int idx)
32 {
33         if (emu->audigy)
34                 return (unsigned char)snd_emu10k1_ptr_read(emu, mpu->port + idx, 0);
35         else
36                 return inb(emu->port + mpu->port + idx);
37 }
38
39 static inline void mpu401_write(struct snd_emu10k1 *emu,
40                                 struct snd_emu10k1_midi *mpu, int data, int idx)
41 {
42         if (emu->audigy)
43                 snd_emu10k1_ptr_write(emu, mpu->port + idx, 0, data);
44         else
45                 outb(data, emu->port + mpu->port + idx);
46 }
47
48 #define mpu401_write_data(emu, mpu, data)       mpu401_write(emu, mpu, data, 0)
49 #define mpu401_write_cmd(emu, mpu, data)        mpu401_write(emu, mpu, data, 1)
50 #define mpu401_read_data(emu, mpu)              mpu401_read(emu, mpu, 0)
51 #define mpu401_read_stat(emu, mpu)              mpu401_read(emu, mpu, 1)
52
53 #define mpu401_input_avail(emu,mpu)     (!(mpu401_read_stat(emu,mpu) & 0x80))
54 #define mpu401_output_ready(emu,mpu)    (!(mpu401_read_stat(emu,mpu) & 0x40))
55
56 #define MPU401_RESET            0xff
57 #define MPU401_ENTER_UART       0x3f
58 #define MPU401_ACK              0xfe
59
60 static void mpu401_clear_rx(struct snd_emu10k1 *emu, struct snd_emu10k1_midi *mpu)
61 {
62         int timeout = 100000;
63         for (; timeout > 0 && mpu401_input_avail(emu, mpu); timeout--)
64                 mpu401_read_data(emu, mpu);
65 #ifdef CONFIG_SND_DEBUG
66         if (timeout <= 0)
67                 snd_printk(KERN_ERR "cmd: clear rx timeout (status = 0x%x)\n", mpu401_read_stat(emu, mpu));
68 #endif
69 }
70
71 /*
72
73  */
74
75 static void do_emu10k1_midi_interrupt(struct snd_emu10k1 *emu, struct snd_emu10k1_midi *midi, unsigned int status)
76 {
77         unsigned char byte;
78
79         if (midi->rmidi == NULL) {
80                 snd_emu10k1_intr_disable(emu, midi->tx_enable | midi->rx_enable);
81                 return;
82         }
83
84         spin_lock(&midi->input_lock);
85         if ((status & midi->ipr_rx) && mpu401_input_avail(emu, midi)) {
86                 if (!(midi->midi_mode & EMU10K1_MIDI_MODE_INPUT)) {
87                         mpu401_clear_rx(emu, midi);
88                 } else {
89                         byte = mpu401_read_data(emu, midi);
90                         if (midi->substream_input)
91                                 snd_rawmidi_receive(midi->substream_input, &byte, 1);
92                 }
93         }
94         spin_unlock(&midi->input_lock);
95
96         spin_lock(&midi->output_lock);
97         if ((status & midi->ipr_tx) && mpu401_output_ready(emu, midi)) {
98                 if (midi->substream_output &&
99                     snd_rawmidi_transmit(midi->substream_output, &byte, 1) == 1) {
100                         mpu401_write_data(emu, midi, byte);
101                 } else {
102                         snd_emu10k1_intr_disable(emu, midi->tx_enable);
103                 }
104         }
105         spin_unlock(&midi->output_lock);
106 }
107
108 static void snd_emu10k1_midi_interrupt(struct snd_emu10k1 *emu, unsigned int status)
109 {
110         do_emu10k1_midi_interrupt(emu, &emu->midi, status);
111 }
112
113 static void snd_emu10k1_midi_interrupt2(struct snd_emu10k1 *emu, unsigned int status)
114 {
115         do_emu10k1_midi_interrupt(emu, &emu->midi2, status);
116 }
117
118 static int snd_emu10k1_midi_cmd(struct snd_emu10k1 * emu, struct snd_emu10k1_midi *midi, unsigned char cmd, int ack)
119 {
120         unsigned long flags;
121         int timeout, ok;
122
123         spin_lock_irqsave(&midi->input_lock, flags);
124         mpu401_write_data(emu, midi, 0x00);
125         /* mpu401_clear_rx(emu, midi); */
126
127         mpu401_write_cmd(emu, midi, cmd);
128         if (ack) {
129                 ok = 0;
130                 timeout = 10000;
131                 while (!ok && timeout-- > 0) {
132                         if (mpu401_input_avail(emu, midi)) {
133                                 if (mpu401_read_data(emu, midi) == MPU401_ACK)
134                                         ok = 1;
135                         }
136                 }
137                 if (!ok && mpu401_read_data(emu, midi) == MPU401_ACK)
138                         ok = 1;
139         } else {
140                 ok = 1;
141         }
142         spin_unlock_irqrestore(&midi->input_lock, flags);
143         if (!ok) {
144                 snd_printk(KERN_ERR "midi_cmd: 0x%x failed at 0x%lx (status = 0x%x, data = 0x%x)!!!\n",
145                            cmd, emu->port,
146                            mpu401_read_stat(emu, midi),
147                            mpu401_read_data(emu, midi));
148                 return 1;
149         }
150         return 0;
151 }
152
153 static int snd_emu10k1_midi_input_open(struct snd_rawmidi_substream *substream)
154 {
155         struct snd_emu10k1 *emu;
156         struct snd_emu10k1_midi *midi = (struct snd_emu10k1_midi *)substream->rmidi->private_data;
157         unsigned long flags;
158
159         emu = midi->emu;
160         snd_assert(emu, return -ENXIO);
161         spin_lock_irqsave(&midi->open_lock, flags);
162         midi->midi_mode |= EMU10K1_MIDI_MODE_INPUT;
163         midi->substream_input = substream;
164         if (!(midi->midi_mode & EMU10K1_MIDI_MODE_OUTPUT)) {
165                 spin_unlock_irqrestore(&midi->open_lock, flags);
166                 if (snd_emu10k1_midi_cmd(emu, midi, MPU401_RESET, 1))
167                         goto error_out;
168                 if (snd_emu10k1_midi_cmd(emu, midi, MPU401_ENTER_UART, 1))
169                         goto error_out;
170         } else {
171                 spin_unlock_irqrestore(&midi->open_lock, flags);
172         }
173         return 0;
174
175 error_out:
176         return -EIO;
177 }
178
179 static int snd_emu10k1_midi_output_open(struct snd_rawmidi_substream *substream)
180 {
181         struct snd_emu10k1 *emu;
182         struct snd_emu10k1_midi *midi = (struct snd_emu10k1_midi *)substream->rmidi->private_data;
183         unsigned long flags;
184
185         emu = midi->emu;
186         snd_assert(emu, return -ENXIO);
187         spin_lock_irqsave(&midi->open_lock, flags);
188         midi->midi_mode |= EMU10K1_MIDI_MODE_OUTPUT;
189         midi->substream_output = substream;
190         if (!(midi->midi_mode & EMU10K1_MIDI_MODE_INPUT)) {
191                 spin_unlock_irqrestore(&midi->open_lock, flags);
192                 if (snd_emu10k1_midi_cmd(emu, midi, MPU401_RESET, 1))
193                         goto error_out;
194                 if (snd_emu10k1_midi_cmd(emu, midi, MPU401_ENTER_UART, 1))
195                         goto error_out;
196         } else {
197                 spin_unlock_irqrestore(&midi->open_lock, flags);
198         }
199         return 0;
200
201 error_out:
202         return -EIO;
203 }
204
205 static int snd_emu10k1_midi_input_close(struct snd_rawmidi_substream *substream)
206 {
207         struct snd_emu10k1 *emu;
208         struct snd_emu10k1_midi *midi = (struct snd_emu10k1_midi *)substream->rmidi->private_data;
209         unsigned long flags;
210         int err = 0;
211
212         emu = midi->emu;
213         snd_assert(emu, return -ENXIO);
214         spin_lock_irqsave(&midi->open_lock, flags);
215         snd_emu10k1_intr_disable(emu, midi->rx_enable);
216         midi->midi_mode &= ~EMU10K1_MIDI_MODE_INPUT;
217         midi->substream_input = NULL;
218         if (!(midi->midi_mode & EMU10K1_MIDI_MODE_OUTPUT)) {
219                 spin_unlock_irqrestore(&midi->open_lock, flags);
220                 err = snd_emu10k1_midi_cmd(emu, midi, MPU401_RESET, 0);
221         } else {
222                 spin_unlock_irqrestore(&midi->open_lock, flags);
223         }
224         return err;
225 }
226
227 static int snd_emu10k1_midi_output_close(struct snd_rawmidi_substream *substream)
228 {
229         struct snd_emu10k1 *emu;
230         struct snd_emu10k1_midi *midi = (struct snd_emu10k1_midi *)substream->rmidi->private_data;
231         unsigned long flags;
232         int err = 0;
233
234         emu = midi->emu;
235         snd_assert(emu, return -ENXIO);
236         spin_lock_irqsave(&midi->open_lock, flags);
237         snd_emu10k1_intr_disable(emu, midi->tx_enable);
238         midi->midi_mode &= ~EMU10K1_MIDI_MODE_OUTPUT;
239         midi->substream_output = NULL;
240         if (!(midi->midi_mode & EMU10K1_MIDI_MODE_INPUT)) {
241                 spin_unlock_irqrestore(&midi->open_lock, flags);
242                 err = snd_emu10k1_midi_cmd(emu, midi, MPU401_RESET, 0);
243         } else {
244                 spin_unlock_irqrestore(&midi->open_lock, flags);
245         }
246         return err;
247 }
248
249 static void snd_emu10k1_midi_input_trigger(struct snd_rawmidi_substream *substream, int up)
250 {
251         struct snd_emu10k1 *emu;
252         struct snd_emu10k1_midi *midi = (struct snd_emu10k1_midi *)substream->rmidi->private_data;
253         emu = midi->emu;
254         snd_assert(emu, return);
255
256         if (up)
257                 snd_emu10k1_intr_enable(emu, midi->rx_enable);
258         else
259                 snd_emu10k1_intr_disable(emu, midi->rx_enable);
260 }
261
262 static void snd_emu10k1_midi_output_trigger(struct snd_rawmidi_substream *substream, int up)
263 {
264         struct snd_emu10k1 *emu;
265         struct snd_emu10k1_midi *midi = (struct snd_emu10k1_midi *)substream->rmidi->private_data;
266         unsigned long flags;
267
268         emu = midi->emu;
269         snd_assert(emu, return);
270
271         if (up) {
272                 int max = 4;
273                 unsigned char byte;
274         
275                 /* try to send some amount of bytes here before interrupts */
276                 spin_lock_irqsave(&midi->output_lock, flags);
277                 while (max > 0) {
278                         if (mpu401_output_ready(emu, midi)) {
279                                 if (!(midi->midi_mode & EMU10K1_MIDI_MODE_OUTPUT) ||
280                                     snd_rawmidi_transmit(substream, &byte, 1) != 1) {
281                                         /* no more data */
282                                         spin_unlock_irqrestore(&midi->output_lock, flags);
283                                         return;
284                                 }
285                                 mpu401_write_data(emu, midi, byte);
286                                 max--;
287                         } else {
288                                 break;
289                         }
290                 }
291                 spin_unlock_irqrestore(&midi->output_lock, flags);
292                 snd_emu10k1_intr_enable(emu, midi->tx_enable);
293         } else {
294                 snd_emu10k1_intr_disable(emu, midi->tx_enable);
295         }
296 }
297
298 /*
299
300  */
301
302 static struct snd_rawmidi_ops snd_emu10k1_midi_output =
303 {
304         .open =         snd_emu10k1_midi_output_open,
305         .close =        snd_emu10k1_midi_output_close,
306         .trigger =      snd_emu10k1_midi_output_trigger,
307 };
308
309 static struct snd_rawmidi_ops snd_emu10k1_midi_input =
310 {
311         .open =         snd_emu10k1_midi_input_open,
312         .close =        snd_emu10k1_midi_input_close,
313         .trigger =      snd_emu10k1_midi_input_trigger,
314 };
315
316 static void snd_emu10k1_midi_free(struct snd_rawmidi *rmidi)
317 {
318         struct snd_emu10k1_midi *midi = (struct snd_emu10k1_midi *)rmidi->private_data;
319         midi->interrupt = NULL;
320         midi->rmidi = NULL;
321 }
322
323 static int __devinit emu10k1_midi_init(struct snd_emu10k1 *emu, struct snd_emu10k1_midi *midi, int device, char *name)
324 {
325         struct snd_rawmidi *rmidi;
326         int err;
327
328         if ((err = snd_rawmidi_new(emu->card, name, device, 1, 1, &rmidi)) < 0)
329                 return err;
330         midi->emu = emu;
331         spin_lock_init(&midi->open_lock);
332         spin_lock_init(&midi->input_lock);
333         spin_lock_init(&midi->output_lock);
334         strcpy(rmidi->name, name);
335         snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_emu10k1_midi_output);
336         snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_emu10k1_midi_input);
337         rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT |
338                              SNDRV_RAWMIDI_INFO_INPUT |
339                              SNDRV_RAWMIDI_INFO_DUPLEX;
340         rmidi->private_data = midi;
341         rmidi->private_free = snd_emu10k1_midi_free;
342         midi->rmidi = rmidi;
343         return 0;
344 }
345
346 int __devinit snd_emu10k1_midi(struct snd_emu10k1 *emu)
347 {
348         struct snd_emu10k1_midi *midi = &emu->midi;
349         int err;
350
351         if ((err = emu10k1_midi_init(emu, midi, 0, "EMU10K1 MPU-401 (UART)")) < 0)
352                 return err;
353
354         midi->tx_enable = INTE_MIDITXENABLE;
355         midi->rx_enable = INTE_MIDIRXENABLE;
356         midi->port = MUDATA;
357         midi->ipr_tx = IPR_MIDITRANSBUFEMPTY;
358         midi->ipr_rx = IPR_MIDIRECVBUFEMPTY;
359         midi->interrupt = snd_emu10k1_midi_interrupt;
360         return 0;
361 }
362
363 int __devinit snd_emu10k1_audigy_midi(struct snd_emu10k1 *emu)
364 {
365         struct snd_emu10k1_midi *midi;
366         int err;
367
368         midi = &emu->midi;
369         if ((err = emu10k1_midi_init(emu, midi, 0, "Audigy MPU-401 (UART)")) < 0)
370                 return err;
371
372         midi->tx_enable = INTE_MIDITXENABLE;
373         midi->rx_enable = INTE_MIDIRXENABLE;
374         midi->port = A_MUDATA1;
375         midi->ipr_tx = IPR_MIDITRANSBUFEMPTY;
376         midi->ipr_rx = IPR_MIDIRECVBUFEMPTY;
377         midi->interrupt = snd_emu10k1_midi_interrupt;
378
379         midi = &emu->midi2;
380         if ((err = emu10k1_midi_init(emu, midi, 1, "Audigy MPU-401 #2")) < 0)
381                 return err;
382
383         midi->tx_enable = INTE_A_MIDITXENABLE2;
384         midi->rx_enable = INTE_A_MIDIRXENABLE2;
385         midi->port = A_MUDATA2;
386         midi->ipr_tx = IPR_A_MIDITRANSBUFEMPTY2;
387         midi->ipr_rx = IPR_A_MIDIRECVBUFEMPTY2;
388         midi->interrupt = snd_emu10k1_midi_interrupt2;
389         return 0;
390 }