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