11da177e4SLinus Torvalds /* 2c1017a4cSJaroslav Kysela * Copyright (c) by Jaroslav Kysela <perex@perex.cz> 31da177e4SLinus Torvalds * Creative Labs, Inc. 41da177e4SLinus Torvalds * Routines for IRQ control of EMU10K1 chips 51da177e4SLinus Torvalds * 61da177e4SLinus Torvalds * BUGS: 71da177e4SLinus Torvalds * -- 81da177e4SLinus Torvalds * 91da177e4SLinus Torvalds * TODO: 101da177e4SLinus Torvalds * -- 111da177e4SLinus Torvalds * 121da177e4SLinus Torvalds * This program is free software; you can redistribute it and/or modify 131da177e4SLinus Torvalds * it under the terms of the GNU General Public License as published by 141da177e4SLinus Torvalds * the Free Software Foundation; either version 2 of the License, or 151da177e4SLinus Torvalds * (at your option) any later version. 161da177e4SLinus Torvalds * 171da177e4SLinus Torvalds * This program is distributed in the hope that it will be useful, 181da177e4SLinus Torvalds * but WITHOUT ANY WARRANTY; without even the implied warranty of 191da177e4SLinus Torvalds * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 201da177e4SLinus Torvalds * GNU General Public License for more details. 211da177e4SLinus Torvalds * 221da177e4SLinus Torvalds * You should have received a copy of the GNU General Public License 231da177e4SLinus Torvalds * along with this program; if not, write to the Free Software 241da177e4SLinus Torvalds * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 251da177e4SLinus Torvalds * 261da177e4SLinus Torvalds */ 271da177e4SLinus Torvalds 281da177e4SLinus Torvalds #include <linux/time.h> 291da177e4SLinus Torvalds #include <sound/core.h> 301da177e4SLinus Torvalds #include <sound/emu10k1.h> 311da177e4SLinus Torvalds 327d12e780SDavid Howells irqreturn_t snd_emu10k1_interrupt(int irq, void *dev_id) 331da177e4SLinus Torvalds { 34eb4698f3STakashi Iwai struct snd_emu10k1 *emu = dev_id; 351da177e4SLinus Torvalds unsigned int status, status2, orig_status, orig_status2; 361da177e4SLinus Torvalds int handled = 0; 37c94fa4c9SJames Courtier-Dutton int timeout = 0; 381da177e4SLinus Torvalds 39c94fa4c9SJames Courtier-Dutton while (((status = inl(emu->port + IPR)) != 0) && (timeout < 1000)) { 40c94fa4c9SJames Courtier-Dutton timeout++; 411da177e4SLinus Torvalds orig_status = status; 421da177e4SLinus Torvalds handled = 1; 43fb6a0d63SJames Courtier-Dutton if ((status & 0xffffffff) == 0xffffffff) { 446f002b02STakashi Iwai dev_info(emu->card->dev, 456f002b02STakashi Iwai "Suspected sound card removal\n"); 46fb6a0d63SJames Courtier-Dutton break; 47fb6a0d63SJames Courtier-Dutton } 481da177e4SLinus Torvalds if (status & IPR_PCIERROR) { 496f002b02STakashi Iwai dev_err(emu->card->dev, "interrupt: PCI error\n"); 501da177e4SLinus Torvalds snd_emu10k1_intr_disable(emu, INTE_PCIERRORENABLE); 511da177e4SLinus Torvalds status &= ~IPR_PCIERROR; 521da177e4SLinus Torvalds } 531da177e4SLinus Torvalds if (status & (IPR_VOLINCR|IPR_VOLDECR|IPR_MUTE)) { 541da177e4SLinus Torvalds if (emu->hwvol_interrupt) 551da177e4SLinus Torvalds emu->hwvol_interrupt(emu, status); 561da177e4SLinus Torvalds else 571da177e4SLinus Torvalds snd_emu10k1_intr_disable(emu, INTE_VOLINCRENABLE|INTE_VOLDECRENABLE|INTE_MUTEENABLE); 581da177e4SLinus Torvalds status &= ~(IPR_VOLINCR|IPR_VOLDECR|IPR_MUTE); 591da177e4SLinus Torvalds } 601da177e4SLinus Torvalds if (status & IPR_CHANNELLOOP) { 611da177e4SLinus Torvalds int voice; 621da177e4SLinus Torvalds int voice_max = status & IPR_CHANNELNUMBERMASK; 631da177e4SLinus Torvalds u32 val; 64eb4698f3STakashi Iwai struct snd_emu10k1_voice *pvoice = emu->voices; 651da177e4SLinus Torvalds 661da177e4SLinus Torvalds val = snd_emu10k1_ptr_read(emu, CLIPL, 0); 671da177e4SLinus Torvalds for (voice = 0; voice <= voice_max; voice++) { 681da177e4SLinus Torvalds if (voice == 0x20) 691da177e4SLinus Torvalds val = snd_emu10k1_ptr_read(emu, CLIPH, 0); 701da177e4SLinus Torvalds if (val & 1) { 711da177e4SLinus Torvalds if (pvoice->use && pvoice->interrupt != NULL) { 721da177e4SLinus Torvalds pvoice->interrupt(emu, pvoice); 731da177e4SLinus Torvalds snd_emu10k1_voice_intr_ack(emu, voice); 741da177e4SLinus Torvalds } else { 751da177e4SLinus Torvalds snd_emu10k1_voice_intr_disable(emu, voice); 761da177e4SLinus Torvalds } 771da177e4SLinus Torvalds } 781da177e4SLinus Torvalds val >>= 1; 791da177e4SLinus Torvalds pvoice++; 801da177e4SLinus Torvalds } 811da177e4SLinus Torvalds val = snd_emu10k1_ptr_read(emu, HLIPL, 0); 821da177e4SLinus Torvalds for (voice = 0; voice <= voice_max; voice++) { 831da177e4SLinus Torvalds if (voice == 0x20) 841da177e4SLinus Torvalds val = snd_emu10k1_ptr_read(emu, HLIPH, 0); 851da177e4SLinus Torvalds if (val & 1) { 861da177e4SLinus Torvalds if (pvoice->use && pvoice->interrupt != NULL) { 871da177e4SLinus Torvalds pvoice->interrupt(emu, pvoice); 881da177e4SLinus Torvalds snd_emu10k1_voice_half_loop_intr_ack(emu, voice); 891da177e4SLinus Torvalds } else { 901da177e4SLinus Torvalds snd_emu10k1_voice_half_loop_intr_disable(emu, voice); 911da177e4SLinus Torvalds } 921da177e4SLinus Torvalds } 931da177e4SLinus Torvalds val >>= 1; 941da177e4SLinus Torvalds pvoice++; 951da177e4SLinus Torvalds } 961da177e4SLinus Torvalds status &= ~IPR_CHANNELLOOP; 971da177e4SLinus Torvalds } 981da177e4SLinus Torvalds status &= ~IPR_CHANNELNUMBERMASK; 991da177e4SLinus Torvalds if (status & (IPR_ADCBUFFULL|IPR_ADCBUFHALFFULL)) { 1001da177e4SLinus Torvalds if (emu->capture_interrupt) 1011da177e4SLinus Torvalds emu->capture_interrupt(emu, status); 1021da177e4SLinus Torvalds else 1031da177e4SLinus Torvalds snd_emu10k1_intr_disable(emu, INTE_ADCBUFENABLE); 1041da177e4SLinus Torvalds status &= ~(IPR_ADCBUFFULL|IPR_ADCBUFHALFFULL); 1051da177e4SLinus Torvalds } 1061da177e4SLinus Torvalds if (status & (IPR_MICBUFFULL|IPR_MICBUFHALFFULL)) { 1071da177e4SLinus Torvalds if (emu->capture_mic_interrupt) 1081da177e4SLinus Torvalds emu->capture_mic_interrupt(emu, status); 1091da177e4SLinus Torvalds else 1101da177e4SLinus Torvalds snd_emu10k1_intr_disable(emu, INTE_MICBUFENABLE); 1111da177e4SLinus Torvalds status &= ~(IPR_MICBUFFULL|IPR_MICBUFHALFFULL); 1121da177e4SLinus Torvalds } 1131da177e4SLinus Torvalds if (status & (IPR_EFXBUFFULL|IPR_EFXBUFHALFFULL)) { 1141da177e4SLinus Torvalds if (emu->capture_efx_interrupt) 1151da177e4SLinus Torvalds emu->capture_efx_interrupt(emu, status); 1161da177e4SLinus Torvalds else 1171da177e4SLinus Torvalds snd_emu10k1_intr_disable(emu, INTE_EFXBUFENABLE); 1181da177e4SLinus Torvalds status &= ~(IPR_EFXBUFFULL|IPR_EFXBUFHALFFULL); 1191da177e4SLinus Torvalds } 1201da177e4SLinus Torvalds if (status & (IPR_MIDITRANSBUFEMPTY|IPR_MIDIRECVBUFEMPTY)) { 1211da177e4SLinus Torvalds if (emu->midi.interrupt) 1221da177e4SLinus Torvalds emu->midi.interrupt(emu, status); 1231da177e4SLinus Torvalds else 1241da177e4SLinus Torvalds snd_emu10k1_intr_disable(emu, INTE_MIDITXENABLE|INTE_MIDIRXENABLE); 1251da177e4SLinus Torvalds status &= ~(IPR_MIDITRANSBUFEMPTY|IPR_MIDIRECVBUFEMPTY); 1261da177e4SLinus Torvalds } 1271da177e4SLinus Torvalds if (status & (IPR_A_MIDITRANSBUFEMPTY2|IPR_A_MIDIRECVBUFEMPTY2)) { 1281da177e4SLinus Torvalds if (emu->midi2.interrupt) 1291da177e4SLinus Torvalds emu->midi2.interrupt(emu, status); 1301da177e4SLinus Torvalds else 1311da177e4SLinus Torvalds snd_emu10k1_intr_disable(emu, INTE_A_MIDITXENABLE2|INTE_A_MIDIRXENABLE2); 1321da177e4SLinus Torvalds status &= ~(IPR_A_MIDITRANSBUFEMPTY2|IPR_A_MIDIRECVBUFEMPTY2); 1331da177e4SLinus Torvalds } 1341da177e4SLinus Torvalds if (status & IPR_INTERVALTIMER) { 1351da177e4SLinus Torvalds if (emu->timer) 1361da177e4SLinus Torvalds snd_timer_interrupt(emu->timer, emu->timer->sticks); 1371da177e4SLinus Torvalds else 1381da177e4SLinus Torvalds snd_emu10k1_intr_disable(emu, INTE_INTERVALTIMERENB); 1391da177e4SLinus Torvalds status &= ~IPR_INTERVALTIMER; 1401da177e4SLinus Torvalds } 1411da177e4SLinus Torvalds if (status & (IPR_GPSPDIFSTATUSCHANGE|IPR_CDROMSTATUSCHANGE)) { 1421da177e4SLinus Torvalds if (emu->spdif_interrupt) 1431da177e4SLinus Torvalds emu->spdif_interrupt(emu, status); 1441da177e4SLinus Torvalds else 1451da177e4SLinus Torvalds snd_emu10k1_intr_disable(emu, INTE_GPSPDIFENABLE|INTE_CDSPDIFENABLE); 1461da177e4SLinus Torvalds status &= ~(IPR_GPSPDIFSTATUSCHANGE|IPR_CDROMSTATUSCHANGE); 1471da177e4SLinus Torvalds } 1481da177e4SLinus Torvalds if (status & IPR_FXDSP) { 1491da177e4SLinus Torvalds if (emu->dsp_interrupt) 1501da177e4SLinus Torvalds emu->dsp_interrupt(emu); 1511da177e4SLinus Torvalds else 1521da177e4SLinus Torvalds snd_emu10k1_intr_disable(emu, INTE_FXDSPENABLE); 1531da177e4SLinus Torvalds status &= ~IPR_FXDSP; 1541da177e4SLinus Torvalds } 1556e4abc40SJames Courtier-Dutton if (status & IPR_P16V) { 1566e4abc40SJames Courtier-Dutton while ((status2 = inl(emu->port + IPR2)) != 0) { 1576e4abc40SJames Courtier-Dutton u32 mask = INTE2_PLAYBACK_CH_0_LOOP; /* Full Loop */ 158eb4698f3STakashi Iwai struct snd_emu10k1_voice *pvoice = &(emu->p16v_voices[0]); 159eb4698f3STakashi Iwai struct snd_emu10k1_voice *cvoice = &(emu->p16v_capture_voice); 1606e4abc40SJames Courtier-Dutton 1616f002b02STakashi Iwai /* dev_dbg(emu->card->dev, "status2=0x%x\n", status2); */ 1626e4abc40SJames Courtier-Dutton orig_status2 = status2; 1636e4abc40SJames Courtier-Dutton if(status2 & mask) { 1646e4abc40SJames Courtier-Dutton if(pvoice->use) { 1656e4abc40SJames Courtier-Dutton snd_pcm_period_elapsed(pvoice->epcm->substream); 1666e4abc40SJames Courtier-Dutton } else { 1676f002b02STakashi Iwai dev_err(emu->card->dev, 1686f002b02STakashi Iwai "p16v: status: 0x%08x, mask=0x%08x, pvoice=%p, use=%d\n", 1696f002b02STakashi Iwai status2, mask, pvoice, 1706f002b02STakashi Iwai pvoice->use); 1716e4abc40SJames Courtier-Dutton } 1726e4abc40SJames Courtier-Dutton } 1736e4abc40SJames Courtier-Dutton if(status2 & 0x110000) { 1746f002b02STakashi Iwai /* dev_info(emu->card->dev, "capture int found\n"); */ 1756e4abc40SJames Courtier-Dutton if(cvoice->use) { 1766f002b02STakashi Iwai /* dev_info(emu->card->dev, "capture period_elapsed\n"); */ 1776e4abc40SJames Courtier-Dutton snd_pcm_period_elapsed(cvoice->epcm->substream); 1786e4abc40SJames Courtier-Dutton } 1796e4abc40SJames Courtier-Dutton } 1806e4abc40SJames Courtier-Dutton outl(orig_status2, emu->port + IPR2); /* ack all */ 1816e4abc40SJames Courtier-Dutton } 1826e4abc40SJames Courtier-Dutton status &= ~IPR_P16V; 1836e4abc40SJames Courtier-Dutton } 1846e4abc40SJames Courtier-Dutton 1851da177e4SLinus Torvalds if (status) { 1861da177e4SLinus Torvalds unsigned int bits; 1876f002b02STakashi Iwai dev_err(emu->card->dev, 1886f002b02STakashi Iwai "unhandled interrupt: 0x%08x\n", status); 1891da177e4SLinus Torvalds //make sure any interrupts we don't handle are disabled: 1901da177e4SLinus Torvalds bits = INTE_FXDSPENABLE | 1911da177e4SLinus Torvalds INTE_PCIERRORENABLE | 1921da177e4SLinus Torvalds INTE_VOLINCRENABLE | 1931da177e4SLinus Torvalds INTE_VOLDECRENABLE | 1941da177e4SLinus Torvalds INTE_MUTEENABLE | 1951da177e4SLinus Torvalds INTE_MICBUFENABLE | 1961da177e4SLinus Torvalds INTE_ADCBUFENABLE | 1971da177e4SLinus Torvalds INTE_EFXBUFENABLE | 1981da177e4SLinus Torvalds INTE_GPSPDIFENABLE | 1991da177e4SLinus Torvalds INTE_CDSPDIFENABLE | 2001da177e4SLinus Torvalds INTE_INTERVALTIMERENB | 2011da177e4SLinus Torvalds INTE_MIDITXENABLE | 2021da177e4SLinus Torvalds INTE_MIDIRXENABLE; 2031da177e4SLinus Torvalds if (emu->audigy) 2041da177e4SLinus Torvalds bits |= INTE_A_MIDITXENABLE2 | INTE_A_MIDIRXENABLE2; 2051da177e4SLinus Torvalds snd_emu10k1_intr_disable(emu, bits); 2061da177e4SLinus Torvalds } 2071da177e4SLinus Torvalds outl(orig_status, emu->port + IPR); /* ack all */ 2081da177e4SLinus Torvalds } 209c94fa4c9SJames Courtier-Dutton if (timeout == 1000) 2106f002b02STakashi Iwai dev_info(emu->card->dev, "emu10k1 irq routine failure\n"); 211c94fa4c9SJames Courtier-Dutton 2121da177e4SLinus Torvalds return IRQ_RETVAL(handled); 2131da177e4SLinus Torvalds } 214