1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * Copyright (c) by Jaroslav Kysela <perex@perex.cz> 4 * Creative Labs, Inc. 5 * Routines for IRQ control of EMU10K1 chips 6 * 7 * BUGS: 8 * -- 9 * 10 * TODO: 11 * -- 12 */ 13 14 #include <linux/time.h> 15 #include <sound/core.h> 16 #include <sound/emu10k1.h> 17 18 irqreturn_t snd_emu10k1_interrupt(int irq, void *dev_id) 19 { 20 struct snd_emu10k1 *emu = dev_id; 21 unsigned int status, orig_status; 22 int handled = 0; 23 int timeout = 0; 24 25 while ((status = inl(emu->port + IPR)) != 0) { 26 handled = 1; 27 if ((status & 0xffffffff) == 0xffffffff) { 28 dev_info(emu->card->dev, 29 "Suspected sound card removal\n"); 30 break; 31 } 32 if (++timeout == 1000) { 33 dev_info(emu->card->dev, "emu10k1 irq routine failure\n"); 34 break; 35 } 36 orig_status = status; 37 if (status & IPR_PCIERROR) { 38 dev_err(emu->card->dev, "interrupt: PCI error\n"); 39 snd_emu10k1_intr_disable(emu, INTE_PCIERRORENABLE); 40 status &= ~IPR_PCIERROR; 41 } 42 if (status & (IPR_VOLINCR|IPR_VOLDECR|IPR_MUTE)) { 43 if (emu->hwvol_interrupt) 44 emu->hwvol_interrupt(emu, status); 45 else 46 snd_emu10k1_intr_disable(emu, INTE_VOLINCRENABLE|INTE_VOLDECRENABLE|INTE_MUTEENABLE); 47 status &= ~(IPR_VOLINCR|IPR_VOLDECR|IPR_MUTE); 48 } 49 if (status & IPR_CHANNELLOOP) { 50 struct snd_emu10k1_voice *pvoice; 51 int voice; 52 int voice_max = status & IPR_CHANNELNUMBERMASK; 53 u32 val; 54 55 val = snd_emu10k1_ptr_read(emu, CLIPL, 0); 56 pvoice = emu->voices; 57 for (voice = 0; voice <= voice_max; voice++) { 58 if (voice == 0x20) 59 val = snd_emu10k1_ptr_read(emu, CLIPH, 0); 60 if (val & 1) { 61 if (pvoice->use && pvoice->interrupt != NULL) { 62 pvoice->interrupt(emu, pvoice); 63 snd_emu10k1_voice_intr_ack(emu, voice); 64 } else { 65 snd_emu10k1_voice_intr_disable(emu, voice); 66 } 67 } 68 val >>= 1; 69 pvoice++; 70 } 71 val = snd_emu10k1_ptr_read(emu, HLIPL, 0); 72 pvoice = emu->voices; 73 for (voice = 0; voice <= voice_max; voice++) { 74 if (voice == 0x20) 75 val = snd_emu10k1_ptr_read(emu, HLIPH, 0); 76 if (val & 1) { 77 if (pvoice->use && pvoice->interrupt != NULL) { 78 pvoice->interrupt(emu, pvoice); 79 snd_emu10k1_voice_half_loop_intr_ack(emu, voice); 80 } else { 81 snd_emu10k1_voice_half_loop_intr_disable(emu, voice); 82 } 83 } 84 val >>= 1; 85 pvoice++; 86 } 87 status &= ~(IPR_CHANNELLOOP | IPR_CHANNELNUMBERMASK); 88 } 89 if (status & (IPR_ADCBUFFULL|IPR_ADCBUFHALFFULL)) { 90 if (emu->capture_interrupt) 91 emu->capture_interrupt(emu, status); 92 else 93 snd_emu10k1_intr_disable(emu, INTE_ADCBUFENABLE); 94 status &= ~(IPR_ADCBUFFULL|IPR_ADCBUFHALFFULL); 95 } 96 if (status & (IPR_MICBUFFULL|IPR_MICBUFHALFFULL)) { 97 if (emu->capture_mic_interrupt) 98 emu->capture_mic_interrupt(emu, status); 99 else 100 snd_emu10k1_intr_disable(emu, INTE_MICBUFENABLE); 101 status &= ~(IPR_MICBUFFULL|IPR_MICBUFHALFFULL); 102 } 103 if (status & (IPR_EFXBUFFULL|IPR_EFXBUFHALFFULL)) { 104 if (emu->capture_efx_interrupt) 105 emu->capture_efx_interrupt(emu, status); 106 else 107 snd_emu10k1_intr_disable(emu, INTE_EFXBUFENABLE); 108 status &= ~(IPR_EFXBUFFULL|IPR_EFXBUFHALFFULL); 109 } 110 if (status & (IPR_MIDITRANSBUFEMPTY|IPR_MIDIRECVBUFEMPTY)) { 111 if (emu->midi.interrupt) 112 emu->midi.interrupt(emu, status); 113 else 114 snd_emu10k1_intr_disable(emu, INTE_MIDITXENABLE|INTE_MIDIRXENABLE); 115 status &= ~(IPR_MIDITRANSBUFEMPTY|IPR_MIDIRECVBUFEMPTY); 116 } 117 if (status & (IPR_A_MIDITRANSBUFEMPTY2|IPR_A_MIDIRECVBUFEMPTY2)) { 118 if (emu->midi2.interrupt) 119 emu->midi2.interrupt(emu, status); 120 else 121 snd_emu10k1_intr_disable(emu, INTE_A_MIDITXENABLE2|INTE_A_MIDIRXENABLE2); 122 status &= ~(IPR_A_MIDITRANSBUFEMPTY2|IPR_A_MIDIRECVBUFEMPTY2); 123 } 124 if (status & IPR_INTERVALTIMER) { 125 if (emu->timer) 126 snd_timer_interrupt(emu->timer, emu->timer->sticks); 127 else 128 snd_emu10k1_intr_disable(emu, INTE_INTERVALTIMERENB); 129 status &= ~IPR_INTERVALTIMER; 130 } 131 if (status & (IPR_GPSPDIFSTATUSCHANGE|IPR_CDROMSTATUSCHANGE)) { 132 if (emu->spdif_interrupt) 133 emu->spdif_interrupt(emu, status); 134 else 135 snd_emu10k1_intr_disable(emu, INTE_GPSPDIFENABLE|INTE_CDSPDIFENABLE); 136 status &= ~(IPR_GPSPDIFSTATUSCHANGE|IPR_CDROMSTATUSCHANGE); 137 } 138 if (status & IPR_FXDSP) { 139 if (emu->dsp_interrupt) 140 emu->dsp_interrupt(emu); 141 else 142 snd_emu10k1_intr_disable(emu, INTE_FXDSPENABLE); 143 status &= ~IPR_FXDSP; 144 } 145 if (status & IPR_P16V) { 146 if (emu->p16v_interrupt) 147 emu->p16v_interrupt(emu); 148 else 149 outl(0, emu->port + INTE2); 150 status &= ~IPR_P16V; 151 } 152 153 if (status) { 154 dev_err(emu->card->dev, 155 "unhandled interrupt: 0x%08x\n", status); 156 } 157 outl(orig_status, emu->port + IPR); /* ack all */ 158 } 159 160 return IRQ_RETVAL(handled); 161 } 162