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