11da177e4SLinus Torvalds /* 21da177e4SLinus Torvalds * Copyright (c) by Jaroslav Kysela <perex@suse.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 <sound/driver.h> 291da177e4SLinus Torvalds #include <linux/time.h> 301da177e4SLinus Torvalds #include <sound/core.h> 311da177e4SLinus Torvalds #include <sound/emu10k1.h> 321da177e4SLinus Torvalds 331da177e4SLinus Torvalds irqreturn_t snd_emu10k1_interrupt(int irq, void *dev_id, struct pt_regs *regs) 341da177e4SLinus Torvalds { 351da177e4SLinus Torvalds emu10k1_t *emu = dev_id; 361da177e4SLinus Torvalds unsigned int status, status2, orig_status, orig_status2; 371da177e4SLinus Torvalds int handled = 0; 381da177e4SLinus Torvalds 391da177e4SLinus Torvalds while ((status = inl(emu->port + IPR)) != 0) { 406e4abc40SJames Courtier-Dutton //printk("emu10k1 irq - status = 0x%x\n", status); 411da177e4SLinus Torvalds orig_status = status; 421da177e4SLinus Torvalds handled = 1; 431da177e4SLinus Torvalds if (status & IPR_PCIERROR) { 441da177e4SLinus Torvalds snd_printk("interrupt: PCI error\n"); 451da177e4SLinus Torvalds snd_emu10k1_intr_disable(emu, INTE_PCIERRORENABLE); 461da177e4SLinus Torvalds status &= ~IPR_PCIERROR; 471da177e4SLinus Torvalds } 481da177e4SLinus Torvalds if (status & (IPR_VOLINCR|IPR_VOLDECR|IPR_MUTE)) { 491da177e4SLinus Torvalds if (emu->hwvol_interrupt) 501da177e4SLinus Torvalds emu->hwvol_interrupt(emu, status); 511da177e4SLinus Torvalds else 521da177e4SLinus Torvalds snd_emu10k1_intr_disable(emu, INTE_VOLINCRENABLE|INTE_VOLDECRENABLE|INTE_MUTEENABLE); 531da177e4SLinus Torvalds status &= ~(IPR_VOLINCR|IPR_VOLDECR|IPR_MUTE); 541da177e4SLinus Torvalds } 551da177e4SLinus Torvalds if (status & IPR_CHANNELLOOP) { 561da177e4SLinus Torvalds int voice; 571da177e4SLinus Torvalds int voice_max = status & IPR_CHANNELNUMBERMASK; 581da177e4SLinus Torvalds u32 val; 591da177e4SLinus Torvalds emu10k1_voice_t *pvoice = emu->voices; 601da177e4SLinus Torvalds 611da177e4SLinus Torvalds val = snd_emu10k1_ptr_read(emu, CLIPL, 0); 621da177e4SLinus Torvalds for (voice = 0; voice <= voice_max; voice++) { 631da177e4SLinus Torvalds if (voice == 0x20) 641da177e4SLinus Torvalds val = snd_emu10k1_ptr_read(emu, CLIPH, 0); 651da177e4SLinus Torvalds if (val & 1) { 661da177e4SLinus Torvalds if (pvoice->use && pvoice->interrupt != NULL) { 671da177e4SLinus Torvalds pvoice->interrupt(emu, pvoice); 681da177e4SLinus Torvalds snd_emu10k1_voice_intr_ack(emu, voice); 691da177e4SLinus Torvalds } else { 701da177e4SLinus Torvalds snd_emu10k1_voice_intr_disable(emu, voice); 711da177e4SLinus Torvalds } 721da177e4SLinus Torvalds } 731da177e4SLinus Torvalds val >>= 1; 741da177e4SLinus Torvalds pvoice++; 751da177e4SLinus Torvalds } 761da177e4SLinus Torvalds val = snd_emu10k1_ptr_read(emu, HLIPL, 0); 771da177e4SLinus Torvalds for (voice = 0; voice <= voice_max; voice++) { 781da177e4SLinus Torvalds if (voice == 0x20) 791da177e4SLinus Torvalds val = snd_emu10k1_ptr_read(emu, HLIPH, 0); 801da177e4SLinus Torvalds if (val & 1) { 811da177e4SLinus Torvalds if (pvoice->use && pvoice->interrupt != NULL) { 821da177e4SLinus Torvalds pvoice->interrupt(emu, pvoice); 831da177e4SLinus Torvalds snd_emu10k1_voice_half_loop_intr_ack(emu, voice); 841da177e4SLinus Torvalds } else { 851da177e4SLinus Torvalds snd_emu10k1_voice_half_loop_intr_disable(emu, voice); 861da177e4SLinus Torvalds } 871da177e4SLinus Torvalds } 881da177e4SLinus Torvalds val >>= 1; 891da177e4SLinus Torvalds pvoice++; 901da177e4SLinus Torvalds } 911da177e4SLinus Torvalds status &= ~IPR_CHANNELLOOP; 921da177e4SLinus Torvalds } 931da177e4SLinus Torvalds status &= ~IPR_CHANNELNUMBERMASK; 941da177e4SLinus Torvalds if (status & (IPR_ADCBUFFULL|IPR_ADCBUFHALFFULL)) { 951da177e4SLinus Torvalds if (emu->capture_interrupt) 961da177e4SLinus Torvalds emu->capture_interrupt(emu, status); 971da177e4SLinus Torvalds else 981da177e4SLinus Torvalds snd_emu10k1_intr_disable(emu, INTE_ADCBUFENABLE); 991da177e4SLinus Torvalds status &= ~(IPR_ADCBUFFULL|IPR_ADCBUFHALFFULL); 1001da177e4SLinus Torvalds } 1011da177e4SLinus Torvalds if (status & (IPR_MICBUFFULL|IPR_MICBUFHALFFULL)) { 1021da177e4SLinus Torvalds if (emu->capture_mic_interrupt) 1031da177e4SLinus Torvalds emu->capture_mic_interrupt(emu, status); 1041da177e4SLinus Torvalds else 1051da177e4SLinus Torvalds snd_emu10k1_intr_disable(emu, INTE_MICBUFENABLE); 1061da177e4SLinus Torvalds status &= ~(IPR_MICBUFFULL|IPR_MICBUFHALFFULL); 1071da177e4SLinus Torvalds } 1081da177e4SLinus Torvalds if (status & (IPR_EFXBUFFULL|IPR_EFXBUFHALFFULL)) { 1091da177e4SLinus Torvalds if (emu->capture_efx_interrupt) 1101da177e4SLinus Torvalds emu->capture_efx_interrupt(emu, status); 1111da177e4SLinus Torvalds else 1121da177e4SLinus Torvalds snd_emu10k1_intr_disable(emu, INTE_EFXBUFENABLE); 1131da177e4SLinus Torvalds status &= ~(IPR_EFXBUFFULL|IPR_EFXBUFHALFFULL); 1141da177e4SLinus Torvalds } 1151da177e4SLinus Torvalds if (status & (IPR_MIDITRANSBUFEMPTY|IPR_MIDIRECVBUFEMPTY)) { 1161da177e4SLinus Torvalds if (emu->midi.interrupt) 1171da177e4SLinus Torvalds emu->midi.interrupt(emu, status); 1181da177e4SLinus Torvalds else 1191da177e4SLinus Torvalds snd_emu10k1_intr_disable(emu, INTE_MIDITXENABLE|INTE_MIDIRXENABLE); 1201da177e4SLinus Torvalds status &= ~(IPR_MIDITRANSBUFEMPTY|IPR_MIDIRECVBUFEMPTY); 1211da177e4SLinus Torvalds } 1221da177e4SLinus Torvalds if (status & (IPR_A_MIDITRANSBUFEMPTY2|IPR_A_MIDIRECVBUFEMPTY2)) { 1231da177e4SLinus Torvalds if (emu->midi2.interrupt) 1241da177e4SLinus Torvalds emu->midi2.interrupt(emu, status); 1251da177e4SLinus Torvalds else 1261da177e4SLinus Torvalds snd_emu10k1_intr_disable(emu, INTE_A_MIDITXENABLE2|INTE_A_MIDIRXENABLE2); 1271da177e4SLinus Torvalds status &= ~(IPR_A_MIDITRANSBUFEMPTY2|IPR_A_MIDIRECVBUFEMPTY2); 1281da177e4SLinus Torvalds } 1291da177e4SLinus Torvalds if (status & IPR_INTERVALTIMER) { 1301da177e4SLinus Torvalds if (emu->timer) 1311da177e4SLinus Torvalds snd_timer_interrupt(emu->timer, emu->timer->sticks); 1321da177e4SLinus Torvalds else 1331da177e4SLinus Torvalds snd_emu10k1_intr_disable(emu, INTE_INTERVALTIMERENB); 1341da177e4SLinus Torvalds status &= ~IPR_INTERVALTIMER; 1351da177e4SLinus Torvalds } 1361da177e4SLinus Torvalds if (status & (IPR_GPSPDIFSTATUSCHANGE|IPR_CDROMSTATUSCHANGE)) { 1371da177e4SLinus Torvalds if (emu->spdif_interrupt) 1381da177e4SLinus Torvalds emu->spdif_interrupt(emu, status); 1391da177e4SLinus Torvalds else 1401da177e4SLinus Torvalds snd_emu10k1_intr_disable(emu, INTE_GPSPDIFENABLE|INTE_CDSPDIFENABLE); 1411da177e4SLinus Torvalds status &= ~(IPR_GPSPDIFSTATUSCHANGE|IPR_CDROMSTATUSCHANGE); 1421da177e4SLinus Torvalds } 1431da177e4SLinus Torvalds if (status & IPR_FXDSP) { 1441da177e4SLinus Torvalds if (emu->dsp_interrupt) 1451da177e4SLinus Torvalds emu->dsp_interrupt(emu); 1461da177e4SLinus Torvalds else 1471da177e4SLinus Torvalds snd_emu10k1_intr_disable(emu, INTE_FXDSPENABLE); 1481da177e4SLinus Torvalds status &= ~IPR_FXDSP; 1491da177e4SLinus Torvalds } 1506e4abc40SJames Courtier-Dutton if (status & IPR_P16V) { 1516e4abc40SJames Courtier-Dutton while ((status2 = inl(emu->port + IPR2)) != 0) { 1526e4abc40SJames Courtier-Dutton u32 mask = INTE2_PLAYBACK_CH_0_LOOP; /* Full Loop */ 1536e4abc40SJames Courtier-Dutton emu10k1_voice_t *pvoice = &(emu->p16v_voices[0]); 1546e4abc40SJames Courtier-Dutton emu10k1_voice_t *cvoice = &(emu->p16v_capture_voice); 1556e4abc40SJames Courtier-Dutton 1566e4abc40SJames Courtier-Dutton //printk(KERN_INFO "status2=0x%x\n", status2); 1576e4abc40SJames Courtier-Dutton orig_status2 = status2; 1586e4abc40SJames Courtier-Dutton if(status2 & mask) { 1596e4abc40SJames Courtier-Dutton if(pvoice->use) { 1606e4abc40SJames Courtier-Dutton snd_pcm_period_elapsed(pvoice->epcm->substream); 1616e4abc40SJames Courtier-Dutton } else { 1626e4abc40SJames Courtier-Dutton snd_printk(KERN_ERR "p16v: status: 0x%08x, mask=0x%08x, pvoice=%p, use=%d\n", status2, mask, pvoice, pvoice->use); 1636e4abc40SJames Courtier-Dutton } 1646e4abc40SJames Courtier-Dutton } 1656e4abc40SJames Courtier-Dutton if(status2 & 0x110000) { 1666e4abc40SJames Courtier-Dutton //printk(KERN_INFO "capture int found\n"); 1676e4abc40SJames Courtier-Dutton if(cvoice->use) { 1686e4abc40SJames Courtier-Dutton //printk(KERN_INFO "capture period_elapsed\n"); 1696e4abc40SJames Courtier-Dutton snd_pcm_period_elapsed(cvoice->epcm->substream); 1706e4abc40SJames Courtier-Dutton } 1716e4abc40SJames Courtier-Dutton } 1726e4abc40SJames Courtier-Dutton outl(orig_status2, emu->port + IPR2); /* ack all */ 1736e4abc40SJames Courtier-Dutton } 1746e4abc40SJames Courtier-Dutton status &= ~IPR_P16V; 1756e4abc40SJames Courtier-Dutton } 1766e4abc40SJames Courtier-Dutton 1771da177e4SLinus Torvalds if (status) { 1781da177e4SLinus Torvalds unsigned int bits; 1796e4abc40SJames Courtier-Dutton snd_printk(KERN_ERR "emu10k1: unhandled interrupt: 0x%08x\n", status); 1801da177e4SLinus Torvalds //make sure any interrupts we don't handle are disabled: 1811da177e4SLinus Torvalds bits = INTE_FXDSPENABLE | 1821da177e4SLinus Torvalds INTE_PCIERRORENABLE | 1831da177e4SLinus Torvalds INTE_VOLINCRENABLE | 1841da177e4SLinus Torvalds INTE_VOLDECRENABLE | 1851da177e4SLinus Torvalds INTE_MUTEENABLE | 1861da177e4SLinus Torvalds INTE_MICBUFENABLE | 1871da177e4SLinus Torvalds INTE_ADCBUFENABLE | 1881da177e4SLinus Torvalds INTE_EFXBUFENABLE | 1891da177e4SLinus Torvalds INTE_GPSPDIFENABLE | 1901da177e4SLinus Torvalds INTE_CDSPDIFENABLE | 1911da177e4SLinus Torvalds INTE_INTERVALTIMERENB | 1921da177e4SLinus Torvalds INTE_MIDITXENABLE | 1931da177e4SLinus Torvalds INTE_MIDIRXENABLE; 1941da177e4SLinus Torvalds if (emu->audigy) 1951da177e4SLinus Torvalds bits |= INTE_A_MIDITXENABLE2 | INTE_A_MIDIRXENABLE2; 1961da177e4SLinus Torvalds snd_emu10k1_intr_disable(emu, bits); 1971da177e4SLinus Torvalds } 1981da177e4SLinus Torvalds outl(orig_status, emu->port + IPR); /* ack all */ 1991da177e4SLinus Torvalds } 2001da177e4SLinus Torvalds return IRQ_RETVAL(handled); 2011da177e4SLinus Torvalds } 202