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 259436f015SOswald Buddenhagen while ((status = inl(emu->port + IPR)) != 0) { 261da177e4SLinus Torvalds handled = 1; 27fb6a0d63SJames Courtier-Dutton if ((status & 0xffffffff) == 0xffffffff) { 286f002b02STakashi Iwai dev_info(emu->card->dev, 296f002b02STakashi Iwai "Suspected sound card removal\n"); 30fb6a0d63SJames Courtier-Dutton break; 31fb6a0d63SJames Courtier-Dutton } 329436f015SOswald Buddenhagen if (++timeout == 1000) { 339436f015SOswald Buddenhagen dev_info(emu->card->dev, "emu10k1 irq routine failure\n"); 349436f015SOswald Buddenhagen break; 359436f015SOswald Buddenhagen } 369436f015SOswald Buddenhagen orig_status = status; 371da177e4SLinus Torvalds if (status & IPR_PCIERROR) { 386f002b02STakashi Iwai dev_err(emu->card->dev, "interrupt: PCI error\n"); 391da177e4SLinus Torvalds snd_emu10k1_intr_disable(emu, INTE_PCIERRORENABLE); 401da177e4SLinus Torvalds status &= ~IPR_PCIERROR; 411da177e4SLinus Torvalds } 421da177e4SLinus Torvalds if (status & (IPR_VOLINCR|IPR_VOLDECR|IPR_MUTE)) { 431da177e4SLinus Torvalds if (emu->hwvol_interrupt) 441da177e4SLinus Torvalds emu->hwvol_interrupt(emu, status); 451da177e4SLinus Torvalds else 461da177e4SLinus Torvalds snd_emu10k1_intr_disable(emu, INTE_VOLINCRENABLE|INTE_VOLDECRENABLE|INTE_MUTEENABLE); 471da177e4SLinus Torvalds status &= ~(IPR_VOLINCR|IPR_VOLDECR|IPR_MUTE); 481da177e4SLinus Torvalds } 491da177e4SLinus Torvalds if (status & IPR_CHANNELLOOP) { 50*6797400eSOswald Buddenhagen struct snd_emu10k1_voice *pvoice; 511da177e4SLinus Torvalds int voice; 521da177e4SLinus Torvalds int voice_max = status & IPR_CHANNELNUMBERMASK; 531da177e4SLinus Torvalds u32 val; 541da177e4SLinus Torvalds 551da177e4SLinus Torvalds val = snd_emu10k1_ptr_read(emu, CLIPL, 0); 56*6797400eSOswald Buddenhagen pvoice = emu->voices; 571da177e4SLinus Torvalds for (voice = 0; voice <= voice_max; voice++) { 581da177e4SLinus Torvalds if (voice == 0x20) 591da177e4SLinus Torvalds val = snd_emu10k1_ptr_read(emu, CLIPH, 0); 601da177e4SLinus Torvalds if (val & 1) { 611da177e4SLinus Torvalds if (pvoice->use && pvoice->interrupt != NULL) { 621da177e4SLinus Torvalds pvoice->interrupt(emu, pvoice); 631da177e4SLinus Torvalds snd_emu10k1_voice_intr_ack(emu, voice); 641da177e4SLinus Torvalds } else { 651da177e4SLinus Torvalds snd_emu10k1_voice_intr_disable(emu, voice); 661da177e4SLinus Torvalds } 671da177e4SLinus Torvalds } 681da177e4SLinus Torvalds val >>= 1; 691da177e4SLinus Torvalds pvoice++; 701da177e4SLinus Torvalds } 711da177e4SLinus Torvalds val = snd_emu10k1_ptr_read(emu, HLIPL, 0); 72*6797400eSOswald Buddenhagen pvoice = emu->voices; 731da177e4SLinus Torvalds for (voice = 0; voice <= voice_max; voice++) { 741da177e4SLinus Torvalds if (voice == 0x20) 751da177e4SLinus Torvalds val = snd_emu10k1_ptr_read(emu, HLIPH, 0); 761da177e4SLinus Torvalds if (val & 1) { 771da177e4SLinus Torvalds if (pvoice->use && pvoice->interrupt != NULL) { 781da177e4SLinus Torvalds pvoice->interrupt(emu, pvoice); 791da177e4SLinus Torvalds snd_emu10k1_voice_half_loop_intr_ack(emu, voice); 801da177e4SLinus Torvalds } else { 811da177e4SLinus Torvalds snd_emu10k1_voice_half_loop_intr_disable(emu, voice); 821da177e4SLinus Torvalds } 831da177e4SLinus Torvalds } 841da177e4SLinus Torvalds val >>= 1; 851da177e4SLinus Torvalds pvoice++; 861da177e4SLinus Torvalds } 87583307baSOswald Buddenhagen status &= ~(IPR_CHANNELLOOP | IPR_CHANNELNUMBERMASK); 881da177e4SLinus Torvalds } 891da177e4SLinus Torvalds if (status & (IPR_ADCBUFFULL|IPR_ADCBUFHALFFULL)) { 901da177e4SLinus Torvalds if (emu->capture_interrupt) 911da177e4SLinus Torvalds emu->capture_interrupt(emu, status); 921da177e4SLinus Torvalds else 931da177e4SLinus Torvalds snd_emu10k1_intr_disable(emu, INTE_ADCBUFENABLE); 941da177e4SLinus Torvalds status &= ~(IPR_ADCBUFFULL|IPR_ADCBUFHALFFULL); 951da177e4SLinus Torvalds } 961da177e4SLinus Torvalds if (status & (IPR_MICBUFFULL|IPR_MICBUFHALFFULL)) { 971da177e4SLinus Torvalds if (emu->capture_mic_interrupt) 981da177e4SLinus Torvalds emu->capture_mic_interrupt(emu, status); 991da177e4SLinus Torvalds else 1001da177e4SLinus Torvalds snd_emu10k1_intr_disable(emu, INTE_MICBUFENABLE); 1011da177e4SLinus Torvalds status &= ~(IPR_MICBUFFULL|IPR_MICBUFHALFFULL); 1021da177e4SLinus Torvalds } 1031da177e4SLinus Torvalds if (status & (IPR_EFXBUFFULL|IPR_EFXBUFHALFFULL)) { 1041da177e4SLinus Torvalds if (emu->capture_efx_interrupt) 1051da177e4SLinus Torvalds emu->capture_efx_interrupt(emu, status); 1061da177e4SLinus Torvalds else 1071da177e4SLinus Torvalds snd_emu10k1_intr_disable(emu, INTE_EFXBUFENABLE); 1081da177e4SLinus Torvalds status &= ~(IPR_EFXBUFFULL|IPR_EFXBUFHALFFULL); 1091da177e4SLinus Torvalds } 1101da177e4SLinus Torvalds if (status & (IPR_MIDITRANSBUFEMPTY|IPR_MIDIRECVBUFEMPTY)) { 1111da177e4SLinus Torvalds if (emu->midi.interrupt) 1121da177e4SLinus Torvalds emu->midi.interrupt(emu, status); 1131da177e4SLinus Torvalds else 1141da177e4SLinus Torvalds snd_emu10k1_intr_disable(emu, INTE_MIDITXENABLE|INTE_MIDIRXENABLE); 1151da177e4SLinus Torvalds status &= ~(IPR_MIDITRANSBUFEMPTY|IPR_MIDIRECVBUFEMPTY); 1161da177e4SLinus Torvalds } 1171da177e4SLinus Torvalds if (status & (IPR_A_MIDITRANSBUFEMPTY2|IPR_A_MIDIRECVBUFEMPTY2)) { 1181da177e4SLinus Torvalds if (emu->midi2.interrupt) 1191da177e4SLinus Torvalds emu->midi2.interrupt(emu, status); 1201da177e4SLinus Torvalds else 1211da177e4SLinus Torvalds snd_emu10k1_intr_disable(emu, INTE_A_MIDITXENABLE2|INTE_A_MIDIRXENABLE2); 1221da177e4SLinus Torvalds status &= ~(IPR_A_MIDITRANSBUFEMPTY2|IPR_A_MIDIRECVBUFEMPTY2); 1231da177e4SLinus Torvalds } 1241da177e4SLinus Torvalds if (status & IPR_INTERVALTIMER) { 1251da177e4SLinus Torvalds if (emu->timer) 1261da177e4SLinus Torvalds snd_timer_interrupt(emu->timer, emu->timer->sticks); 1271da177e4SLinus Torvalds else 1281da177e4SLinus Torvalds snd_emu10k1_intr_disable(emu, INTE_INTERVALTIMERENB); 1291da177e4SLinus Torvalds status &= ~IPR_INTERVALTIMER; 1301da177e4SLinus Torvalds } 1311da177e4SLinus Torvalds if (status & (IPR_GPSPDIFSTATUSCHANGE|IPR_CDROMSTATUSCHANGE)) { 1321da177e4SLinus Torvalds if (emu->spdif_interrupt) 1331da177e4SLinus Torvalds emu->spdif_interrupt(emu, status); 1341da177e4SLinus Torvalds else 1351da177e4SLinus Torvalds snd_emu10k1_intr_disable(emu, INTE_GPSPDIFENABLE|INTE_CDSPDIFENABLE); 1361da177e4SLinus Torvalds status &= ~(IPR_GPSPDIFSTATUSCHANGE|IPR_CDROMSTATUSCHANGE); 1371da177e4SLinus Torvalds } 1381da177e4SLinus Torvalds if (status & IPR_FXDSP) { 1391da177e4SLinus Torvalds if (emu->dsp_interrupt) 1401da177e4SLinus Torvalds emu->dsp_interrupt(emu); 1411da177e4SLinus Torvalds else 1421da177e4SLinus Torvalds snd_emu10k1_intr_disable(emu, INTE_FXDSPENABLE); 1431da177e4SLinus Torvalds status &= ~IPR_FXDSP; 1441da177e4SLinus Torvalds } 1456e4abc40SJames Courtier-Dutton if (status & IPR_P16V) { 14602a0d9c2SOswald Buddenhagen if (emu->p16v_interrupt) 14702a0d9c2SOswald Buddenhagen emu->p16v_interrupt(emu); 14802a0d9c2SOswald Buddenhagen else 14902a0d9c2SOswald Buddenhagen outl(0, emu->port + INTE2); 1506e4abc40SJames Courtier-Dutton status &= ~IPR_P16V; 1516e4abc40SJames Courtier-Dutton } 1526e4abc40SJames Courtier-Dutton 1531da177e4SLinus Torvalds if (status) { 1546f002b02STakashi Iwai dev_err(emu->card->dev, 1556f002b02STakashi Iwai "unhandled interrupt: 0x%08x\n", status); 1561da177e4SLinus Torvalds } 1571da177e4SLinus Torvalds outl(orig_status, emu->port + IPR); /* ack all */ 1581da177e4SLinus Torvalds } 159c94fa4c9SJames Courtier-Dutton 1601da177e4SLinus Torvalds return IRQ_RETVAL(handled); 1611da177e4SLinus Torvalds } 162