1 /*
2  *  Copyright (c) by Jaroslav Kysela <perex@perex.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 <linux/time.h>
29 #include <sound/core.h>
30 #include <sound/emu10k1.h>
31 
snd_emu10k1_interrupt(int irq,void * dev_id)32 irqreturn_t snd_emu10k1_interrupt(int irq, void *dev_id)
33 {
34 	struct snd_emu10k1 *emu = dev_id;
35 	unsigned int status, status2, orig_status, orig_status2;
36 	int handled = 0;
37 	int timeout = 0;
38 
39 	while (((status = inl(emu->port + IPR)) != 0) && (timeout < 1000)) {
40 		timeout++;
41 		orig_status = status;
42 		handled = 1;
43 		if ((status & 0xffffffff) == 0xffffffff) {
44 			snd_printk(KERN_INFO "snd-emu10k1: Suspected sound card removal\n");
45 			break;
46 		}
47 		if (status & IPR_PCIERROR) {
48 			snd_printk(KERN_ERR "interrupt: PCI error\n");
49 			snd_emu10k1_intr_disable(emu, INTE_PCIERRORENABLE);
50 			status &= ~IPR_PCIERROR;
51 		}
52 		if (status & (IPR_VOLINCR|IPR_VOLDECR|IPR_MUTE)) {
53 			if (emu->hwvol_interrupt)
54 				emu->hwvol_interrupt(emu, status);
55 			else
56 				snd_emu10k1_intr_disable(emu, INTE_VOLINCRENABLE|INTE_VOLDECRENABLE|INTE_MUTEENABLE);
57 			status &= ~(IPR_VOLINCR|IPR_VOLDECR|IPR_MUTE);
58 		}
59 		if (status & IPR_CHANNELLOOP) {
60 			int voice;
61 			int voice_max = status & IPR_CHANNELNUMBERMASK;
62 			u32 val;
63 			struct snd_emu10k1_voice *pvoice = emu->voices;
64 
65 			val = snd_emu10k1_ptr_read(emu, CLIPL, 0);
66 			for (voice = 0; voice <= voice_max; voice++) {
67 				if (voice == 0x20)
68 					val = snd_emu10k1_ptr_read(emu, CLIPH, 0);
69 				if (val & 1) {
70 					if (pvoice->use && pvoice->interrupt != NULL) {
71 						pvoice->interrupt(emu, pvoice);
72 						snd_emu10k1_voice_intr_ack(emu, voice);
73 					} else {
74 						snd_emu10k1_voice_intr_disable(emu, voice);
75 					}
76 				}
77 				val >>= 1;
78 				pvoice++;
79 			}
80 			val = snd_emu10k1_ptr_read(emu, HLIPL, 0);
81 			for (voice = 0; voice <= voice_max; voice++) {
82 				if (voice == 0x20)
83 					val = snd_emu10k1_ptr_read(emu, HLIPH, 0);
84 				if (val & 1) {
85 					if (pvoice->use && pvoice->interrupt != NULL) {
86 						pvoice->interrupt(emu, pvoice);
87 						snd_emu10k1_voice_half_loop_intr_ack(emu, voice);
88 					} else {
89 						snd_emu10k1_voice_half_loop_intr_disable(emu, voice);
90 					}
91 				}
92 				val >>= 1;
93 				pvoice++;
94 			}
95 			status &= ~IPR_CHANNELLOOP;
96 		}
97 		status &= ~IPR_CHANNELNUMBERMASK;
98 		if (status & (IPR_ADCBUFFULL|IPR_ADCBUFHALFFULL)) {
99 			if (emu->capture_interrupt)
100 				emu->capture_interrupt(emu, status);
101 			else
102 				snd_emu10k1_intr_disable(emu, INTE_ADCBUFENABLE);
103 			status &= ~(IPR_ADCBUFFULL|IPR_ADCBUFHALFFULL);
104 		}
105 		if (status & (IPR_MICBUFFULL|IPR_MICBUFHALFFULL)) {
106 			if (emu->capture_mic_interrupt)
107 				emu->capture_mic_interrupt(emu, status);
108 			else
109 				snd_emu10k1_intr_disable(emu, INTE_MICBUFENABLE);
110 			status &= ~(IPR_MICBUFFULL|IPR_MICBUFHALFFULL);
111 		}
112 		if (status & (IPR_EFXBUFFULL|IPR_EFXBUFHALFFULL)) {
113 			if (emu->capture_efx_interrupt)
114 				emu->capture_efx_interrupt(emu, status);
115 			else
116 				snd_emu10k1_intr_disable(emu, INTE_EFXBUFENABLE);
117 			status &= ~(IPR_EFXBUFFULL|IPR_EFXBUFHALFFULL);
118 		}
119 		if (status & (IPR_MIDITRANSBUFEMPTY|IPR_MIDIRECVBUFEMPTY)) {
120 			if (emu->midi.interrupt)
121 				emu->midi.interrupt(emu, status);
122 			else
123 				snd_emu10k1_intr_disable(emu, INTE_MIDITXENABLE|INTE_MIDIRXENABLE);
124 			status &= ~(IPR_MIDITRANSBUFEMPTY|IPR_MIDIRECVBUFEMPTY);
125 		}
126 		if (status & (IPR_A_MIDITRANSBUFEMPTY2|IPR_A_MIDIRECVBUFEMPTY2)) {
127 			if (emu->midi2.interrupt)
128 				emu->midi2.interrupt(emu, status);
129 			else
130 				snd_emu10k1_intr_disable(emu, INTE_A_MIDITXENABLE2|INTE_A_MIDIRXENABLE2);
131 			status &= ~(IPR_A_MIDITRANSBUFEMPTY2|IPR_A_MIDIRECVBUFEMPTY2);
132 		}
133 		if (status & IPR_INTERVALTIMER) {
134 			if (emu->timer)
135 				snd_timer_interrupt(emu->timer, emu->timer->sticks);
136 			else
137 				snd_emu10k1_intr_disable(emu, INTE_INTERVALTIMERENB);
138 			status &= ~IPR_INTERVALTIMER;
139 		}
140 		if (status & (IPR_GPSPDIFSTATUSCHANGE|IPR_CDROMSTATUSCHANGE)) {
141 			if (emu->spdif_interrupt)
142 				emu->spdif_interrupt(emu, status);
143 			else
144 				snd_emu10k1_intr_disable(emu, INTE_GPSPDIFENABLE|INTE_CDSPDIFENABLE);
145 			status &= ~(IPR_GPSPDIFSTATUSCHANGE|IPR_CDROMSTATUSCHANGE);
146 		}
147 		if (status & IPR_FXDSP) {
148 			if (emu->dsp_interrupt)
149 				emu->dsp_interrupt(emu);
150 			else
151 				snd_emu10k1_intr_disable(emu, INTE_FXDSPENABLE);
152 			status &= ~IPR_FXDSP;
153 		}
154 		if (status & IPR_P16V) {
155 			while ((status2 = inl(emu->port + IPR2)) != 0) {
156 				u32 mask = INTE2_PLAYBACK_CH_0_LOOP;  /* Full Loop */
157 				struct snd_emu10k1_voice *pvoice = &(emu->p16v_voices[0]);
158 				struct snd_emu10k1_voice *cvoice = &(emu->p16v_capture_voice);
159 
160 				//printk(KERN_INFO "status2=0x%x\n", status2);
161 				orig_status2 = status2;
162 				if(status2 & mask) {
163 					if(pvoice->use) {
164 						snd_pcm_period_elapsed(pvoice->epcm->substream);
165 					} else {
166 						snd_printk(KERN_ERR "p16v: status: 0x%08x, mask=0x%08x, pvoice=%p, use=%d\n", status2, mask, pvoice, pvoice->use);
167 					}
168 				}
169 				if(status2 & 0x110000) {
170 					//printk(KERN_INFO "capture int found\n");
171 					if(cvoice->use) {
172 						//printk(KERN_INFO "capture period_elapsed\n");
173 						snd_pcm_period_elapsed(cvoice->epcm->substream);
174 					}
175 				}
176 				outl(orig_status2, emu->port + IPR2); /* ack all */
177 			}
178 			status &= ~IPR_P16V;
179 		}
180 
181 		if (status) {
182 			unsigned int bits;
183 			snd_printk(KERN_ERR "emu10k1: unhandled interrupt: 0x%08x\n", status);
184 			//make sure any interrupts we don't handle are disabled:
185 			bits = INTE_FXDSPENABLE |
186 				INTE_PCIERRORENABLE |
187 				INTE_VOLINCRENABLE |
188 				INTE_VOLDECRENABLE |
189 				INTE_MUTEENABLE |
190 				INTE_MICBUFENABLE |
191 				INTE_ADCBUFENABLE |
192 				INTE_EFXBUFENABLE |
193 				INTE_GPSPDIFENABLE |
194 				INTE_CDSPDIFENABLE |
195 				INTE_INTERVALTIMERENB |
196 				INTE_MIDITXENABLE |
197 				INTE_MIDIRXENABLE;
198 			if (emu->audigy)
199 				bits |= INTE_A_MIDITXENABLE2 | INTE_A_MIDIRXENABLE2;
200 			snd_emu10k1_intr_disable(emu, bits);
201 		}
202 		outl(orig_status, emu->port + IPR); /* ack all */
203 	}
204 	if (timeout == 1000)
205 		snd_printk(KERN_INFO "emu10k1 irq routine failure\n");
206 
207 	return IRQ_RETVAL(handled);
208 }
209