11a59d1b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later 21da177e4SLinus Torvalds /* 3c1017a4cSJaroslav Kysela * Copyright (c) by Jaroslav Kysela <perex@perex.cz> 41da177e4SLinus Torvalds * Creative Labs, Inc. 51da177e4SLinus Torvalds * Routines for IRQ control of EMU10K1 chips 61da177e4SLinus Torvalds * 71da177e4SLinus Torvalds * BUGS: 81da177e4SLinus Torvalds * -- 91da177e4SLinus Torvalds * 101da177e4SLinus Torvalds * TODO: 111da177e4SLinus Torvalds * -- 121da177e4SLinus Torvalds */ 131da177e4SLinus Torvalds 141da177e4SLinus Torvalds #include <linux/time.h> 151da177e4SLinus Torvalds #include <sound/core.h> 161da177e4SLinus Torvalds #include <sound/emu10k1.h> 171da177e4SLinus Torvalds 187d12e780SDavid Howells irqreturn_t snd_emu10k1_interrupt(int irq, void *dev_id) 191da177e4SLinus Torvalds { 20eb4698f3STakashi Iwai struct snd_emu10k1 *emu = dev_id; 2102a0d9c2SOswald Buddenhagen unsigned int status, orig_status; 221da177e4SLinus Torvalds int handled = 0; 23c94fa4c9SJames Courtier-Dutton int timeout = 0; 241da177e4SLinus Torvalds 25c94fa4c9SJames Courtier-Dutton while (((status = inl(emu->port + IPR)) != 0) && (timeout < 1000)) { 26c94fa4c9SJames Courtier-Dutton timeout++; 271da177e4SLinus Torvalds orig_status = status; 281da177e4SLinus Torvalds handled = 1; 29fb6a0d63SJames Courtier-Dutton if ((status & 0xffffffff) == 0xffffffff) { 306f002b02STakashi Iwai dev_info(emu->card->dev, 316f002b02STakashi Iwai "Suspected sound card removal\n"); 32fb6a0d63SJames Courtier-Dutton break; 33fb6a0d63SJames Courtier-Dutton } 341da177e4SLinus Torvalds if (status & IPR_PCIERROR) { 356f002b02STakashi Iwai dev_err(emu->card->dev, "interrupt: PCI error\n"); 361da177e4SLinus Torvalds snd_emu10k1_intr_disable(emu, INTE_PCIERRORENABLE); 371da177e4SLinus Torvalds status &= ~IPR_PCIERROR; 381da177e4SLinus Torvalds } 391da177e4SLinus Torvalds if (status & (IPR_VOLINCR|IPR_VOLDECR|IPR_MUTE)) { 401da177e4SLinus Torvalds if (emu->hwvol_interrupt) 411da177e4SLinus Torvalds emu->hwvol_interrupt(emu, status); 421da177e4SLinus Torvalds else 431da177e4SLinus Torvalds snd_emu10k1_intr_disable(emu, INTE_VOLINCRENABLE|INTE_VOLDECRENABLE|INTE_MUTEENABLE); 441da177e4SLinus Torvalds status &= ~(IPR_VOLINCR|IPR_VOLDECR|IPR_MUTE); 451da177e4SLinus Torvalds } 461da177e4SLinus Torvalds if (status & IPR_CHANNELLOOP) { 471da177e4SLinus Torvalds int voice; 481da177e4SLinus Torvalds int voice_max = status & IPR_CHANNELNUMBERMASK; 491da177e4SLinus Torvalds u32 val; 50eb4698f3STakashi Iwai struct snd_emu10k1_voice *pvoice = emu->voices; 511da177e4SLinus Torvalds 521da177e4SLinus Torvalds val = snd_emu10k1_ptr_read(emu, CLIPL, 0); 531da177e4SLinus Torvalds for (voice = 0; voice <= voice_max; voice++) { 541da177e4SLinus Torvalds if (voice == 0x20) 551da177e4SLinus Torvalds val = snd_emu10k1_ptr_read(emu, CLIPH, 0); 561da177e4SLinus Torvalds if (val & 1) { 571da177e4SLinus Torvalds if (pvoice->use && pvoice->interrupt != NULL) { 581da177e4SLinus Torvalds pvoice->interrupt(emu, pvoice); 591da177e4SLinus Torvalds snd_emu10k1_voice_intr_ack(emu, voice); 601da177e4SLinus Torvalds } else { 611da177e4SLinus Torvalds snd_emu10k1_voice_intr_disable(emu, voice); 621da177e4SLinus Torvalds } 631da177e4SLinus Torvalds } 641da177e4SLinus Torvalds val >>= 1; 651da177e4SLinus Torvalds pvoice++; 661da177e4SLinus Torvalds } 671da177e4SLinus Torvalds val = snd_emu10k1_ptr_read(emu, HLIPL, 0); 681da177e4SLinus Torvalds for (voice = 0; voice <= voice_max; voice++) { 691da177e4SLinus Torvalds if (voice == 0x20) 701da177e4SLinus Torvalds val = snd_emu10k1_ptr_read(emu, HLIPH, 0); 711da177e4SLinus Torvalds if (val & 1) { 721da177e4SLinus Torvalds if (pvoice->use && pvoice->interrupt != NULL) { 731da177e4SLinus Torvalds pvoice->interrupt(emu, pvoice); 741da177e4SLinus Torvalds snd_emu10k1_voice_half_loop_intr_ack(emu, voice); 751da177e4SLinus Torvalds } else { 761da177e4SLinus Torvalds snd_emu10k1_voice_half_loop_intr_disable(emu, voice); 771da177e4SLinus Torvalds } 781da177e4SLinus Torvalds } 791da177e4SLinus Torvalds val >>= 1; 801da177e4SLinus Torvalds pvoice++; 811da177e4SLinus Torvalds } 82*583307baSOswald Buddenhagen status &= ~(IPR_CHANNELLOOP | IPR_CHANNELNUMBERMASK); 831da177e4SLinus Torvalds } 841da177e4SLinus Torvalds if (status & (IPR_ADCBUFFULL|IPR_ADCBUFHALFFULL)) { 851da177e4SLinus Torvalds if (emu->capture_interrupt) 861da177e4SLinus Torvalds emu->capture_interrupt(emu, status); 871da177e4SLinus Torvalds else 881da177e4SLinus Torvalds snd_emu10k1_intr_disable(emu, INTE_ADCBUFENABLE); 891da177e4SLinus Torvalds status &= ~(IPR_ADCBUFFULL|IPR_ADCBUFHALFFULL); 901da177e4SLinus Torvalds } 911da177e4SLinus Torvalds if (status & (IPR_MICBUFFULL|IPR_MICBUFHALFFULL)) { 921da177e4SLinus Torvalds if (emu->capture_mic_interrupt) 931da177e4SLinus Torvalds emu->capture_mic_interrupt(emu, status); 941da177e4SLinus Torvalds else 951da177e4SLinus Torvalds snd_emu10k1_intr_disable(emu, INTE_MICBUFENABLE); 961da177e4SLinus Torvalds status &= ~(IPR_MICBUFFULL|IPR_MICBUFHALFFULL); 971da177e4SLinus Torvalds } 981da177e4SLinus Torvalds if (status & (IPR_EFXBUFFULL|IPR_EFXBUFHALFFULL)) { 991da177e4SLinus Torvalds if (emu->capture_efx_interrupt) 1001da177e4SLinus Torvalds emu->capture_efx_interrupt(emu, status); 1011da177e4SLinus Torvalds else 1021da177e4SLinus Torvalds snd_emu10k1_intr_disable(emu, INTE_EFXBUFENABLE); 1031da177e4SLinus Torvalds status &= ~(IPR_EFXBUFFULL|IPR_EFXBUFHALFFULL); 1041da177e4SLinus Torvalds } 1051da177e4SLinus Torvalds if (status & (IPR_MIDITRANSBUFEMPTY|IPR_MIDIRECVBUFEMPTY)) { 1061da177e4SLinus Torvalds if (emu->midi.interrupt) 1071da177e4SLinus Torvalds emu->midi.interrupt(emu, status); 1081da177e4SLinus Torvalds else 1091da177e4SLinus Torvalds snd_emu10k1_intr_disable(emu, INTE_MIDITXENABLE|INTE_MIDIRXENABLE); 1101da177e4SLinus Torvalds status &= ~(IPR_MIDITRANSBUFEMPTY|IPR_MIDIRECVBUFEMPTY); 1111da177e4SLinus Torvalds } 1121da177e4SLinus Torvalds if (status & (IPR_A_MIDITRANSBUFEMPTY2|IPR_A_MIDIRECVBUFEMPTY2)) { 1131da177e4SLinus Torvalds if (emu->midi2.interrupt) 1141da177e4SLinus Torvalds emu->midi2.interrupt(emu, status); 1151da177e4SLinus Torvalds else 1161da177e4SLinus Torvalds snd_emu10k1_intr_disable(emu, INTE_A_MIDITXENABLE2|INTE_A_MIDIRXENABLE2); 1171da177e4SLinus Torvalds status &= ~(IPR_A_MIDITRANSBUFEMPTY2|IPR_A_MIDIRECVBUFEMPTY2); 1181da177e4SLinus Torvalds } 1191da177e4SLinus Torvalds if (status & IPR_INTERVALTIMER) { 1201da177e4SLinus Torvalds if (emu->timer) 1211da177e4SLinus Torvalds snd_timer_interrupt(emu->timer, emu->timer->sticks); 1221da177e4SLinus Torvalds else 1231da177e4SLinus Torvalds snd_emu10k1_intr_disable(emu, INTE_INTERVALTIMERENB); 1241da177e4SLinus Torvalds status &= ~IPR_INTERVALTIMER; 1251da177e4SLinus Torvalds } 1261da177e4SLinus Torvalds if (status & (IPR_GPSPDIFSTATUSCHANGE|IPR_CDROMSTATUSCHANGE)) { 1271da177e4SLinus Torvalds if (emu->spdif_interrupt) 1281da177e4SLinus Torvalds emu->spdif_interrupt(emu, status); 1291da177e4SLinus Torvalds else 1301da177e4SLinus Torvalds snd_emu10k1_intr_disable(emu, INTE_GPSPDIFENABLE|INTE_CDSPDIFENABLE); 1311da177e4SLinus Torvalds status &= ~(IPR_GPSPDIFSTATUSCHANGE|IPR_CDROMSTATUSCHANGE); 1321da177e4SLinus Torvalds } 1331da177e4SLinus Torvalds if (status & IPR_FXDSP) { 1341da177e4SLinus Torvalds if (emu->dsp_interrupt) 1351da177e4SLinus Torvalds emu->dsp_interrupt(emu); 1361da177e4SLinus Torvalds else 1371da177e4SLinus Torvalds snd_emu10k1_intr_disable(emu, INTE_FXDSPENABLE); 1381da177e4SLinus Torvalds status &= ~IPR_FXDSP; 1391da177e4SLinus Torvalds } 1406e4abc40SJames Courtier-Dutton if (status & IPR_P16V) { 14102a0d9c2SOswald Buddenhagen if (emu->p16v_interrupt) 14202a0d9c2SOswald Buddenhagen emu->p16v_interrupt(emu); 14302a0d9c2SOswald Buddenhagen else 14402a0d9c2SOswald Buddenhagen outl(0, emu->port + INTE2); 1456e4abc40SJames Courtier-Dutton status &= ~IPR_P16V; 1466e4abc40SJames Courtier-Dutton } 1476e4abc40SJames Courtier-Dutton 1481da177e4SLinus Torvalds if (status) { 1491da177e4SLinus Torvalds unsigned int bits; 1506f002b02STakashi Iwai dev_err(emu->card->dev, 1516f002b02STakashi Iwai "unhandled interrupt: 0x%08x\n", status); 1521da177e4SLinus Torvalds //make sure any interrupts we don't handle are disabled: 1531da177e4SLinus Torvalds bits = INTE_FXDSPENABLE | 1541da177e4SLinus Torvalds INTE_PCIERRORENABLE | 1551da177e4SLinus Torvalds INTE_VOLINCRENABLE | 1561da177e4SLinus Torvalds INTE_VOLDECRENABLE | 1571da177e4SLinus Torvalds INTE_MUTEENABLE | 1581da177e4SLinus Torvalds INTE_MICBUFENABLE | 1591da177e4SLinus Torvalds INTE_ADCBUFENABLE | 1601da177e4SLinus Torvalds INTE_EFXBUFENABLE | 1611da177e4SLinus Torvalds INTE_GPSPDIFENABLE | 1621da177e4SLinus Torvalds INTE_CDSPDIFENABLE | 1631da177e4SLinus Torvalds INTE_INTERVALTIMERENB | 1641da177e4SLinus Torvalds INTE_MIDITXENABLE | 1651da177e4SLinus Torvalds INTE_MIDIRXENABLE; 1661da177e4SLinus Torvalds if (emu->audigy) 1671da177e4SLinus Torvalds bits |= INTE_A_MIDITXENABLE2 | INTE_A_MIDIRXENABLE2; 1681da177e4SLinus Torvalds snd_emu10k1_intr_disable(emu, bits); 1691da177e4SLinus Torvalds } 1701da177e4SLinus Torvalds outl(orig_status, emu->port + IPR); /* ack all */ 1711da177e4SLinus Torvalds } 172c94fa4c9SJames Courtier-Dutton if (timeout == 1000) 1736f002b02STakashi Iwai dev_info(emu->card->dev, "emu10k1 irq routine failure\n"); 174c94fa4c9SJames Courtier-Dutton 1751da177e4SLinus Torvalds return IRQ_RETVAL(handled); 1761da177e4SLinus Torvalds } 177