Merge branch 'master'
[linux-2.6] / sound / pci / emu10k1 / irq.c
1 /*
2  *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
3  *                   Creative Labs, Inc.
4  *  Routines for IRQ control of EMU10K1 chips
5  *
6  *  BUGS:
7  *    --
8  *
9  *  TODO:
10  *    --
11  *
12  *   This program is free software; you can redistribute it and/or modify
13  *   it under the terms of the GNU General Public License as published by
14  *   the Free Software Foundation; either version 2 of the License, or
15  *   (at your option) any later version.
16  *
17  *   This program is distributed in the hope that it will be useful,
18  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
19  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  *   GNU General Public License for more details.
21  *
22  *   You should have received a copy of the GNU General Public License
23  *   along with this program; if not, write to the Free Software
24  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
25  *
26  */
27
28 #include <sound/driver.h>
29 #include <linux/time.h>
30 #include <sound/core.h>
31 #include <sound/emu10k1.h>
32
33 irqreturn_t snd_emu10k1_interrupt(int irq, void *dev_id, struct pt_regs *regs)
34 {
35         emu10k1_t *emu = dev_id;
36         unsigned int status, status2, orig_status, orig_status2;
37         int handled = 0;
38
39         while ((status = inl(emu->port + IPR)) != 0) {
40                 //printk("emu10k1 irq - status = 0x%x\n", status);
41                 orig_status = status;
42                 handled = 1;
43                 if (status & IPR_PCIERROR) {
44                         snd_printk(KERN_ERR "interrupt: PCI error\n");
45                         snd_emu10k1_intr_disable(emu, INTE_PCIERRORENABLE);
46                         status &= ~IPR_PCIERROR;
47                 }
48                 if (status & (IPR_VOLINCR|IPR_VOLDECR|IPR_MUTE)) {
49                         if (emu->hwvol_interrupt)
50                                 emu->hwvol_interrupt(emu, status);
51                         else
52                                 snd_emu10k1_intr_disable(emu, INTE_VOLINCRENABLE|INTE_VOLDECRENABLE|INTE_MUTEENABLE);
53                         status &= ~(IPR_VOLINCR|IPR_VOLDECR|IPR_MUTE);
54                 }
55                 if (status & IPR_CHANNELLOOP) {
56                         int voice;
57                         int voice_max = status & IPR_CHANNELNUMBERMASK;
58                         u32 val;
59                         emu10k1_voice_t *pvoice = emu->voices;
60
61                         val = snd_emu10k1_ptr_read(emu, CLIPL, 0);
62                         for (voice = 0; voice <= voice_max; voice++) {
63                                 if (voice == 0x20)
64                                         val = snd_emu10k1_ptr_read(emu, CLIPH, 0);
65                                 if (val & 1) {
66                                         if (pvoice->use && pvoice->interrupt != NULL) {
67                                                 pvoice->interrupt(emu, pvoice);
68                                                 snd_emu10k1_voice_intr_ack(emu, voice);
69                                         } else {
70                                                 snd_emu10k1_voice_intr_disable(emu, voice);
71                                         }
72                                 }
73                                 val >>= 1;
74                                 pvoice++;
75                         }
76                         val = snd_emu10k1_ptr_read(emu, HLIPL, 0);
77                         for (voice = 0; voice <= voice_max; voice++) {
78                                 if (voice == 0x20)
79                                         val = snd_emu10k1_ptr_read(emu, HLIPH, 0);
80                                 if (val & 1) {
81                                         if (pvoice->use && pvoice->interrupt != NULL) {
82                                                 pvoice->interrupt(emu, pvoice);
83                                                 snd_emu10k1_voice_half_loop_intr_ack(emu, voice);
84                                         } else {
85                                                 snd_emu10k1_voice_half_loop_intr_disable(emu, voice);
86                                         }
87                                 }
88                                 val >>= 1;
89                                 pvoice++;
90                         }
91                         status &= ~IPR_CHANNELLOOP;
92                 }
93                 status &= ~IPR_CHANNELNUMBERMASK;
94                 if (status & (IPR_ADCBUFFULL|IPR_ADCBUFHALFFULL)) {
95                         if (emu->capture_interrupt)
96                                 emu->capture_interrupt(emu, status);
97                         else
98                                 snd_emu10k1_intr_disable(emu, INTE_ADCBUFENABLE);
99                         status &= ~(IPR_ADCBUFFULL|IPR_ADCBUFHALFFULL);
100                 }
101                 if (status & (IPR_MICBUFFULL|IPR_MICBUFHALFFULL)) {
102                         if (emu->capture_mic_interrupt)
103                                 emu->capture_mic_interrupt(emu, status);
104                         else
105                                 snd_emu10k1_intr_disable(emu, INTE_MICBUFENABLE);
106                         status &= ~(IPR_MICBUFFULL|IPR_MICBUFHALFFULL);
107                 }
108                 if (status & (IPR_EFXBUFFULL|IPR_EFXBUFHALFFULL)) {
109                         if (emu->capture_efx_interrupt)
110                                 emu->capture_efx_interrupt(emu, status);
111                         else
112                                 snd_emu10k1_intr_disable(emu, INTE_EFXBUFENABLE);
113                         status &= ~(IPR_EFXBUFFULL|IPR_EFXBUFHALFFULL);
114                 }
115                 if (status & (IPR_MIDITRANSBUFEMPTY|IPR_MIDIRECVBUFEMPTY)) {
116                         if (emu->midi.interrupt)
117                                 emu->midi.interrupt(emu, status);
118                         else
119                                 snd_emu10k1_intr_disable(emu, INTE_MIDITXENABLE|INTE_MIDIRXENABLE);
120                         status &= ~(IPR_MIDITRANSBUFEMPTY|IPR_MIDIRECVBUFEMPTY);
121                 }
122                 if (status & (IPR_A_MIDITRANSBUFEMPTY2|IPR_A_MIDIRECVBUFEMPTY2)) {
123                         if (emu->midi2.interrupt)
124                                 emu->midi2.interrupt(emu, status);
125                         else
126                                 snd_emu10k1_intr_disable(emu, INTE_A_MIDITXENABLE2|INTE_A_MIDIRXENABLE2);
127                         status &= ~(IPR_A_MIDITRANSBUFEMPTY2|IPR_A_MIDIRECVBUFEMPTY2);
128                 }
129                 if (status & IPR_INTERVALTIMER) {
130                         if (emu->timer)
131                                 snd_timer_interrupt(emu->timer, emu->timer->sticks);
132                         else
133                                 snd_emu10k1_intr_disable(emu, INTE_INTERVALTIMERENB);
134                         status &= ~IPR_INTERVALTIMER;
135                 }
136                 if (status & (IPR_GPSPDIFSTATUSCHANGE|IPR_CDROMSTATUSCHANGE)) {
137                         if (emu->spdif_interrupt)
138                                 emu->spdif_interrupt(emu, status);
139                         else
140                                 snd_emu10k1_intr_disable(emu, INTE_GPSPDIFENABLE|INTE_CDSPDIFENABLE);
141                         status &= ~(IPR_GPSPDIFSTATUSCHANGE|IPR_CDROMSTATUSCHANGE);
142                 }
143                 if (status & IPR_FXDSP) {
144                         if (emu->dsp_interrupt)
145                                 emu->dsp_interrupt(emu);
146                         else
147                                 snd_emu10k1_intr_disable(emu, INTE_FXDSPENABLE);
148                         status &= ~IPR_FXDSP;
149                 }
150                 if (status & IPR_P16V) {
151                         while ((status2 = inl(emu->port + IPR2)) != 0) {
152                                 u32 mask = INTE2_PLAYBACK_CH_0_LOOP;  /* Full Loop */
153                                 emu10k1_voice_t *pvoice = &(emu->p16v_voices[0]);
154                                 emu10k1_voice_t *cvoice = &(emu->p16v_capture_voice);
155
156                                 //printk(KERN_INFO "status2=0x%x\n", status2);
157                                 orig_status2 = status2;
158                                 if(status2 & mask) {
159                                         if(pvoice->use) {
160                                                 snd_pcm_period_elapsed(pvoice->epcm->substream);
161                                         } else { 
162                                                 snd_printk(KERN_ERR "p16v: status: 0x%08x, mask=0x%08x, pvoice=%p, use=%d\n", status2, mask, pvoice, pvoice->use);
163                                         }
164                                 }
165                                 if(status2 & 0x110000) {
166                                         //printk(KERN_INFO "capture int found\n");
167                                         if(cvoice->use) {
168                                                 //printk(KERN_INFO "capture period_elapsed\n");
169                                                 snd_pcm_period_elapsed(cvoice->epcm->substream);
170                                         }
171                                 }
172                                 outl(orig_status2, emu->port + IPR2); /* ack all */
173                         }
174                         status &= ~IPR_P16V;
175                 }
176
177                 if (status) {
178                         unsigned int bits;
179                         snd_printk(KERN_ERR "emu10k1: unhandled interrupt: 0x%08x\n", status);
180                         //make sure any interrupts we don't handle are disabled:
181                         bits = INTE_FXDSPENABLE |
182                                 INTE_PCIERRORENABLE |
183                                 INTE_VOLINCRENABLE |
184                                 INTE_VOLDECRENABLE |
185                                 INTE_MUTEENABLE |
186                                 INTE_MICBUFENABLE |
187                                 INTE_ADCBUFENABLE |
188                                 INTE_EFXBUFENABLE |
189                                 INTE_GPSPDIFENABLE |
190                                 INTE_CDSPDIFENABLE |
191                                 INTE_INTERVALTIMERENB |
192                                 INTE_MIDITXENABLE |
193                                 INTE_MIDIRXENABLE;
194                         if (emu->audigy)
195                                 bits |= INTE_A_MIDITXENABLE2 | INTE_A_MIDIRXENABLE2;
196                         snd_emu10k1_intr_disable(emu, bits);
197                 }
198                 outl(orig_status, emu->port + IPR); /* ack all */
199         }
200         return IRQ_RETVAL(handled);
201 }