xref: /openbmc/linux/sound/pci/emu10k1/irq.c (revision eb4698f3)
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 	struct snd_emu10k1 *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 			struct snd_emu10k1_voice *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 				struct snd_emu10k1_voice *pvoice = &(emu->p16v_voices[0]);
154 				struct snd_emu10k1_voice *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 }
202