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 25*9436f015SOswald 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 } 32*9436f015SOswald Buddenhagen if (++timeout == 1000) { 33*9436f015SOswald Buddenhagen dev_info(emu->card->dev, "emu10k1 irq routine failure\n"); 34*9436f015SOswald Buddenhagen break; 35*9436f015SOswald Buddenhagen } 36*9436f015SOswald 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) { 501da177e4SLinus Torvalds int voice; 511da177e4SLinus Torvalds int voice_max = status & IPR_CHANNELNUMBERMASK; 521da177e4SLinus Torvalds u32 val; 53eb4698f3STakashi Iwai struct snd_emu10k1_voice *pvoice = emu->voices; 541da177e4SLinus Torvalds 551da177e4SLinus Torvalds val = snd_emu10k1_ptr_read(emu, CLIPL, 0); 561da177e4SLinus Torvalds for (voice = 0; voice <= voice_max; voice++) { 571da177e4SLinus Torvalds if (voice == 0x20) 581da177e4SLinus Torvalds val = snd_emu10k1_ptr_read(emu, CLIPH, 0); 591da177e4SLinus Torvalds if (val & 1) { 601da177e4SLinus Torvalds if (pvoice->use && pvoice->interrupt != NULL) { 611da177e4SLinus Torvalds pvoice->interrupt(emu, pvoice); 621da177e4SLinus Torvalds snd_emu10k1_voice_intr_ack(emu, voice); 631da177e4SLinus Torvalds } else { 641da177e4SLinus Torvalds snd_emu10k1_voice_intr_disable(emu, voice); 651da177e4SLinus Torvalds } 661da177e4SLinus Torvalds } 671da177e4SLinus Torvalds val >>= 1; 681da177e4SLinus Torvalds pvoice++; 691da177e4SLinus Torvalds } 701da177e4SLinus Torvalds val = snd_emu10k1_ptr_read(emu, HLIPL, 0); 711da177e4SLinus Torvalds for (voice = 0; voice <= voice_max; voice++) { 721da177e4SLinus Torvalds if (voice == 0x20) 731da177e4SLinus Torvalds val = snd_emu10k1_ptr_read(emu, HLIPH, 0); 741da177e4SLinus Torvalds if (val & 1) { 751da177e4SLinus Torvalds if (pvoice->use && pvoice->interrupt != NULL) { 761da177e4SLinus Torvalds pvoice->interrupt(emu, pvoice); 771da177e4SLinus Torvalds snd_emu10k1_voice_half_loop_intr_ack(emu, voice); 781da177e4SLinus Torvalds } else { 791da177e4SLinus Torvalds snd_emu10k1_voice_half_loop_intr_disable(emu, voice); 801da177e4SLinus Torvalds } 811da177e4SLinus Torvalds } 821da177e4SLinus Torvalds val >>= 1; 831da177e4SLinus Torvalds pvoice++; 841da177e4SLinus Torvalds } 85583307baSOswald Buddenhagen status &= ~(IPR_CHANNELLOOP | IPR_CHANNELNUMBERMASK); 861da177e4SLinus Torvalds } 871da177e4SLinus Torvalds if (status & (IPR_ADCBUFFULL|IPR_ADCBUFHALFFULL)) { 881da177e4SLinus Torvalds if (emu->capture_interrupt) 891da177e4SLinus Torvalds emu->capture_interrupt(emu, status); 901da177e4SLinus Torvalds else 911da177e4SLinus Torvalds snd_emu10k1_intr_disable(emu, INTE_ADCBUFENABLE); 921da177e4SLinus Torvalds status &= ~(IPR_ADCBUFFULL|IPR_ADCBUFHALFFULL); 931da177e4SLinus Torvalds } 941da177e4SLinus Torvalds if (status & (IPR_MICBUFFULL|IPR_MICBUFHALFFULL)) { 951da177e4SLinus Torvalds if (emu->capture_mic_interrupt) 961da177e4SLinus Torvalds emu->capture_mic_interrupt(emu, status); 971da177e4SLinus Torvalds else 981da177e4SLinus Torvalds snd_emu10k1_intr_disable(emu, INTE_MICBUFENABLE); 991da177e4SLinus Torvalds status &= ~(IPR_MICBUFFULL|IPR_MICBUFHALFFULL); 1001da177e4SLinus Torvalds } 1011da177e4SLinus Torvalds if (status & (IPR_EFXBUFFULL|IPR_EFXBUFHALFFULL)) { 1021da177e4SLinus Torvalds if (emu->capture_efx_interrupt) 1031da177e4SLinus Torvalds emu->capture_efx_interrupt(emu, status); 1041da177e4SLinus Torvalds else 1051da177e4SLinus Torvalds snd_emu10k1_intr_disable(emu, INTE_EFXBUFENABLE); 1061da177e4SLinus Torvalds status &= ~(IPR_EFXBUFFULL|IPR_EFXBUFHALFFULL); 1071da177e4SLinus Torvalds } 1081da177e4SLinus Torvalds if (status & (IPR_MIDITRANSBUFEMPTY|IPR_MIDIRECVBUFEMPTY)) { 1091da177e4SLinus Torvalds if (emu->midi.interrupt) 1101da177e4SLinus Torvalds emu->midi.interrupt(emu, status); 1111da177e4SLinus Torvalds else 1121da177e4SLinus Torvalds snd_emu10k1_intr_disable(emu, INTE_MIDITXENABLE|INTE_MIDIRXENABLE); 1131da177e4SLinus Torvalds status &= ~(IPR_MIDITRANSBUFEMPTY|IPR_MIDIRECVBUFEMPTY); 1141da177e4SLinus Torvalds } 1151da177e4SLinus Torvalds if (status & (IPR_A_MIDITRANSBUFEMPTY2|IPR_A_MIDIRECVBUFEMPTY2)) { 1161da177e4SLinus Torvalds if (emu->midi2.interrupt) 1171da177e4SLinus Torvalds emu->midi2.interrupt(emu, status); 1181da177e4SLinus Torvalds else 1191da177e4SLinus Torvalds snd_emu10k1_intr_disable(emu, INTE_A_MIDITXENABLE2|INTE_A_MIDIRXENABLE2); 1201da177e4SLinus Torvalds status &= ~(IPR_A_MIDITRANSBUFEMPTY2|IPR_A_MIDIRECVBUFEMPTY2); 1211da177e4SLinus Torvalds } 1221da177e4SLinus Torvalds if (status & IPR_INTERVALTIMER) { 1231da177e4SLinus Torvalds if (emu->timer) 1241da177e4SLinus Torvalds snd_timer_interrupt(emu->timer, emu->timer->sticks); 1251da177e4SLinus Torvalds else 1261da177e4SLinus Torvalds snd_emu10k1_intr_disable(emu, INTE_INTERVALTIMERENB); 1271da177e4SLinus Torvalds status &= ~IPR_INTERVALTIMER; 1281da177e4SLinus Torvalds } 1291da177e4SLinus Torvalds if (status & (IPR_GPSPDIFSTATUSCHANGE|IPR_CDROMSTATUSCHANGE)) { 1301da177e4SLinus Torvalds if (emu->spdif_interrupt) 1311da177e4SLinus Torvalds emu->spdif_interrupt(emu, status); 1321da177e4SLinus Torvalds else 1331da177e4SLinus Torvalds snd_emu10k1_intr_disable(emu, INTE_GPSPDIFENABLE|INTE_CDSPDIFENABLE); 1341da177e4SLinus Torvalds status &= ~(IPR_GPSPDIFSTATUSCHANGE|IPR_CDROMSTATUSCHANGE); 1351da177e4SLinus Torvalds } 1361da177e4SLinus Torvalds if (status & IPR_FXDSP) { 1371da177e4SLinus Torvalds if (emu->dsp_interrupt) 1381da177e4SLinus Torvalds emu->dsp_interrupt(emu); 1391da177e4SLinus Torvalds else 1401da177e4SLinus Torvalds snd_emu10k1_intr_disable(emu, INTE_FXDSPENABLE); 1411da177e4SLinus Torvalds status &= ~IPR_FXDSP; 1421da177e4SLinus Torvalds } 1436e4abc40SJames Courtier-Dutton if (status & IPR_P16V) { 14402a0d9c2SOswald Buddenhagen if (emu->p16v_interrupt) 14502a0d9c2SOswald Buddenhagen emu->p16v_interrupt(emu); 14602a0d9c2SOswald Buddenhagen else 14702a0d9c2SOswald Buddenhagen outl(0, emu->port + INTE2); 1486e4abc40SJames Courtier-Dutton status &= ~IPR_P16V; 1496e4abc40SJames Courtier-Dutton } 1506e4abc40SJames Courtier-Dutton 1511da177e4SLinus Torvalds if (status) { 1526f002b02STakashi Iwai dev_err(emu->card->dev, 1536f002b02STakashi Iwai "unhandled interrupt: 0x%08x\n", status); 1541da177e4SLinus Torvalds } 1551da177e4SLinus Torvalds outl(orig_status, emu->port + IPR); /* ack all */ 1561da177e4SLinus Torvalds } 157c94fa4c9SJames Courtier-Dutton 1581da177e4SLinus Torvalds return IRQ_RETVAL(handled); 1591da177e4SLinus Torvalds } 160