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) { 44fb6a0d63SJames Courtier-Dutton snd_printk(KERN_INFO "snd-emu10k1: Suspected sound card removal\n"); 45fb6a0d63SJames Courtier-Dutton break; 46fb6a0d63SJames Courtier-Dutton } 471da177e4SLinus Torvalds if (status & IPR_PCIERROR) { 4899b359baSTakashi Iwai snd_printk(KERN_ERR "interrupt: PCI error\n"); 491da177e4SLinus Torvalds snd_emu10k1_intr_disable(emu, INTE_PCIERRORENABLE); 501da177e4SLinus Torvalds status &= ~IPR_PCIERROR; 511da177e4SLinus Torvalds } 521da177e4SLinus Torvalds if (status & (IPR_VOLINCR|IPR_VOLDECR|IPR_MUTE)) { 531da177e4SLinus Torvalds if (emu->hwvol_interrupt) 541da177e4SLinus Torvalds emu->hwvol_interrupt(emu, status); 551da177e4SLinus Torvalds else 561da177e4SLinus Torvalds snd_emu10k1_intr_disable(emu, INTE_VOLINCRENABLE|INTE_VOLDECRENABLE|INTE_MUTEENABLE); 571da177e4SLinus Torvalds status &= ~(IPR_VOLINCR|IPR_VOLDECR|IPR_MUTE); 581da177e4SLinus Torvalds } 591da177e4SLinus Torvalds if (status & IPR_CHANNELLOOP) { 601da177e4SLinus Torvalds int voice; 611da177e4SLinus Torvalds int voice_max = status & IPR_CHANNELNUMBERMASK; 621da177e4SLinus Torvalds u32 val; 63eb4698f3STakashi Iwai struct snd_emu10k1_voice *pvoice = emu->voices; 641da177e4SLinus Torvalds 651da177e4SLinus Torvalds val = snd_emu10k1_ptr_read(emu, CLIPL, 0); 661da177e4SLinus Torvalds for (voice = 0; voice <= voice_max; voice++) { 671da177e4SLinus Torvalds if (voice == 0x20) 681da177e4SLinus Torvalds val = snd_emu10k1_ptr_read(emu, CLIPH, 0); 691da177e4SLinus Torvalds if (val & 1) { 701da177e4SLinus Torvalds if (pvoice->use && pvoice->interrupt != NULL) { 711da177e4SLinus Torvalds pvoice->interrupt(emu, pvoice); 721da177e4SLinus Torvalds snd_emu10k1_voice_intr_ack(emu, voice); 731da177e4SLinus Torvalds } else { 741da177e4SLinus Torvalds snd_emu10k1_voice_intr_disable(emu, voice); 751da177e4SLinus Torvalds } 761da177e4SLinus Torvalds } 771da177e4SLinus Torvalds val >>= 1; 781da177e4SLinus Torvalds pvoice++; 791da177e4SLinus Torvalds } 801da177e4SLinus Torvalds val = snd_emu10k1_ptr_read(emu, HLIPL, 0); 811da177e4SLinus Torvalds for (voice = 0; voice <= voice_max; voice++) { 821da177e4SLinus Torvalds if (voice == 0x20) 831da177e4SLinus Torvalds val = snd_emu10k1_ptr_read(emu, HLIPH, 0); 841da177e4SLinus Torvalds if (val & 1) { 851da177e4SLinus Torvalds if (pvoice->use && pvoice->interrupt != NULL) { 861da177e4SLinus Torvalds pvoice->interrupt(emu, pvoice); 871da177e4SLinus Torvalds snd_emu10k1_voice_half_loop_intr_ack(emu, voice); 881da177e4SLinus Torvalds } else { 891da177e4SLinus Torvalds snd_emu10k1_voice_half_loop_intr_disable(emu, voice); 901da177e4SLinus Torvalds } 911da177e4SLinus Torvalds } 921da177e4SLinus Torvalds val >>= 1; 931da177e4SLinus Torvalds pvoice++; 941da177e4SLinus Torvalds } 951da177e4SLinus Torvalds status &= ~IPR_CHANNELLOOP; 961da177e4SLinus Torvalds } 971da177e4SLinus Torvalds status &= ~IPR_CHANNELNUMBERMASK; 981da177e4SLinus Torvalds if (status & (IPR_ADCBUFFULL|IPR_ADCBUFHALFFULL)) { 991da177e4SLinus Torvalds if (emu->capture_interrupt) 1001da177e4SLinus Torvalds emu->capture_interrupt(emu, status); 1011da177e4SLinus Torvalds else 1021da177e4SLinus Torvalds snd_emu10k1_intr_disable(emu, INTE_ADCBUFENABLE); 1031da177e4SLinus Torvalds status &= ~(IPR_ADCBUFFULL|IPR_ADCBUFHALFFULL); 1041da177e4SLinus Torvalds } 1051da177e4SLinus Torvalds if (status & (IPR_MICBUFFULL|IPR_MICBUFHALFFULL)) { 1061da177e4SLinus Torvalds if (emu->capture_mic_interrupt) 1071da177e4SLinus Torvalds emu->capture_mic_interrupt(emu, status); 1081da177e4SLinus Torvalds else 1091da177e4SLinus Torvalds snd_emu10k1_intr_disable(emu, INTE_MICBUFENABLE); 1101da177e4SLinus Torvalds status &= ~(IPR_MICBUFFULL|IPR_MICBUFHALFFULL); 1111da177e4SLinus Torvalds } 1121da177e4SLinus Torvalds if (status & (IPR_EFXBUFFULL|IPR_EFXBUFHALFFULL)) { 1131da177e4SLinus Torvalds if (emu->capture_efx_interrupt) 1141da177e4SLinus Torvalds emu->capture_efx_interrupt(emu, status); 1151da177e4SLinus Torvalds else 1161da177e4SLinus Torvalds snd_emu10k1_intr_disable(emu, INTE_EFXBUFENABLE); 1171da177e4SLinus Torvalds status &= ~(IPR_EFXBUFFULL|IPR_EFXBUFHALFFULL); 1181da177e4SLinus Torvalds } 1191da177e4SLinus Torvalds if (status & (IPR_MIDITRANSBUFEMPTY|IPR_MIDIRECVBUFEMPTY)) { 1201da177e4SLinus Torvalds if (emu->midi.interrupt) 1211da177e4SLinus Torvalds emu->midi.interrupt(emu, status); 1221da177e4SLinus Torvalds else 1231da177e4SLinus Torvalds snd_emu10k1_intr_disable(emu, INTE_MIDITXENABLE|INTE_MIDIRXENABLE); 1241da177e4SLinus Torvalds status &= ~(IPR_MIDITRANSBUFEMPTY|IPR_MIDIRECVBUFEMPTY); 1251da177e4SLinus Torvalds } 1261da177e4SLinus Torvalds if (status & (IPR_A_MIDITRANSBUFEMPTY2|IPR_A_MIDIRECVBUFEMPTY2)) { 1271da177e4SLinus Torvalds if (emu->midi2.interrupt) 1281da177e4SLinus Torvalds emu->midi2.interrupt(emu, status); 1291da177e4SLinus Torvalds else 1301da177e4SLinus Torvalds snd_emu10k1_intr_disable(emu, INTE_A_MIDITXENABLE2|INTE_A_MIDIRXENABLE2); 1311da177e4SLinus Torvalds status &= ~(IPR_A_MIDITRANSBUFEMPTY2|IPR_A_MIDIRECVBUFEMPTY2); 1321da177e4SLinus Torvalds } 1331da177e4SLinus Torvalds if (status & IPR_INTERVALTIMER) { 1341da177e4SLinus Torvalds if (emu->timer) 1351da177e4SLinus Torvalds snd_timer_interrupt(emu->timer, emu->timer->sticks); 1361da177e4SLinus Torvalds else 1371da177e4SLinus Torvalds snd_emu10k1_intr_disable(emu, INTE_INTERVALTIMERENB); 1381da177e4SLinus Torvalds status &= ~IPR_INTERVALTIMER; 1391da177e4SLinus Torvalds } 1401da177e4SLinus Torvalds if (status & (IPR_GPSPDIFSTATUSCHANGE|IPR_CDROMSTATUSCHANGE)) { 1411da177e4SLinus Torvalds if (emu->spdif_interrupt) 1421da177e4SLinus Torvalds emu->spdif_interrupt(emu, status); 1431da177e4SLinus Torvalds else 1441da177e4SLinus Torvalds snd_emu10k1_intr_disable(emu, INTE_GPSPDIFENABLE|INTE_CDSPDIFENABLE); 1451da177e4SLinus Torvalds status &= ~(IPR_GPSPDIFSTATUSCHANGE|IPR_CDROMSTATUSCHANGE); 1461da177e4SLinus Torvalds } 1471da177e4SLinus Torvalds if (status & IPR_FXDSP) { 1481da177e4SLinus Torvalds if (emu->dsp_interrupt) 1491da177e4SLinus Torvalds emu->dsp_interrupt(emu); 1501da177e4SLinus Torvalds else 1511da177e4SLinus Torvalds snd_emu10k1_intr_disable(emu, INTE_FXDSPENABLE); 1521da177e4SLinus Torvalds status &= ~IPR_FXDSP; 1531da177e4SLinus Torvalds } 1546e4abc40SJames Courtier-Dutton if (status & IPR_P16V) { 1556e4abc40SJames Courtier-Dutton while ((status2 = inl(emu->port + IPR2)) != 0) { 1566e4abc40SJames Courtier-Dutton u32 mask = INTE2_PLAYBACK_CH_0_LOOP; /* Full Loop */ 157eb4698f3STakashi Iwai struct snd_emu10k1_voice *pvoice = &(emu->p16v_voices[0]); 158eb4698f3STakashi Iwai struct snd_emu10k1_voice *cvoice = &(emu->p16v_capture_voice); 1596e4abc40SJames Courtier-Dutton 1606e4abc40SJames Courtier-Dutton //printk(KERN_INFO "status2=0x%x\n", status2); 1616e4abc40SJames Courtier-Dutton orig_status2 = status2; 1626e4abc40SJames Courtier-Dutton if(status2 & mask) { 1636e4abc40SJames Courtier-Dutton if(pvoice->use) { 1646e4abc40SJames Courtier-Dutton snd_pcm_period_elapsed(pvoice->epcm->substream); 1656e4abc40SJames Courtier-Dutton } else { 1666e4abc40SJames Courtier-Dutton snd_printk(KERN_ERR "p16v: status: 0x%08x, mask=0x%08x, pvoice=%p, use=%d\n", status2, mask, pvoice, pvoice->use); 1676e4abc40SJames Courtier-Dutton } 1686e4abc40SJames Courtier-Dutton } 1696e4abc40SJames Courtier-Dutton if(status2 & 0x110000) { 1706e4abc40SJames Courtier-Dutton //printk(KERN_INFO "capture int found\n"); 1716e4abc40SJames Courtier-Dutton if(cvoice->use) { 1726e4abc40SJames Courtier-Dutton //printk(KERN_INFO "capture period_elapsed\n"); 1736e4abc40SJames Courtier-Dutton snd_pcm_period_elapsed(cvoice->epcm->substream); 1746e4abc40SJames Courtier-Dutton } 1756e4abc40SJames Courtier-Dutton } 1766e4abc40SJames Courtier-Dutton outl(orig_status2, emu->port + IPR2); /* ack all */ 1776e4abc40SJames Courtier-Dutton } 1786e4abc40SJames Courtier-Dutton status &= ~IPR_P16V; 1796e4abc40SJames Courtier-Dutton } 1806e4abc40SJames Courtier-Dutton 1811da177e4SLinus Torvalds if (status) { 1821da177e4SLinus Torvalds unsigned int bits; 1836e4abc40SJames Courtier-Dutton snd_printk(KERN_ERR "emu10k1: unhandled interrupt: 0x%08x\n", status); 1841da177e4SLinus Torvalds //make sure any interrupts we don't handle are disabled: 1851da177e4SLinus Torvalds bits = INTE_FXDSPENABLE | 1861da177e4SLinus Torvalds INTE_PCIERRORENABLE | 1871da177e4SLinus Torvalds INTE_VOLINCRENABLE | 1881da177e4SLinus Torvalds INTE_VOLDECRENABLE | 1891da177e4SLinus Torvalds INTE_MUTEENABLE | 1901da177e4SLinus Torvalds INTE_MICBUFENABLE | 1911da177e4SLinus Torvalds INTE_ADCBUFENABLE | 1921da177e4SLinus Torvalds INTE_EFXBUFENABLE | 1931da177e4SLinus Torvalds INTE_GPSPDIFENABLE | 1941da177e4SLinus Torvalds INTE_CDSPDIFENABLE | 1951da177e4SLinus Torvalds INTE_INTERVALTIMERENB | 1961da177e4SLinus Torvalds INTE_MIDITXENABLE | 1971da177e4SLinus Torvalds INTE_MIDIRXENABLE; 1981da177e4SLinus Torvalds if (emu->audigy) 1991da177e4SLinus Torvalds bits |= INTE_A_MIDITXENABLE2 | INTE_A_MIDIRXENABLE2; 2001da177e4SLinus Torvalds snd_emu10k1_intr_disable(emu, bits); 2011da177e4SLinus Torvalds } 2021da177e4SLinus Torvalds outl(orig_status, emu->port + IPR); /* ack all */ 2031da177e4SLinus Torvalds } 204c94fa4c9SJames Courtier-Dutton if (timeout == 1000) 205c94fa4c9SJames Courtier-Dutton snd_printk(KERN_INFO "emu10k1 irq routine failure\n"); 206c94fa4c9SJames Courtier-Dutton 2071da177e4SLinus Torvalds return IRQ_RETVAL(handled); 2081da177e4SLinus Torvalds } 209