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