/*
 *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
 *                   Creative Labs, Inc.
 *  Routines for IRQ control of EMU10K1 chips
 *
 *  BUGS:
 *    --
 *
 *  TODO:
 *    --
 *
 *   This program is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation; either version 2 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program; if not, write to the Free Software
 *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 */

#include "driver.h"
#include "emu10k1.h"

void snd_emu10k1_interrupt(emu10k1_t *emu, unsigned int status)
{
	do {
		// printk("irq - status = 0x%x\n", status);
		if (status & INT_PCI) {
			snd_printk("snd_emu10k1_interrupt: PCI error\n");
			snd_emu10k1_intr_disable(emu, ENB_PCI);
		}
		if (status & (INT_VINC|INT_VDEC|INT_MUTE)) {
			if (emu->hwvol_interrupt)
				emu->hwvol_interrupt(emu, status);
			else
				snd_emu10k1_intr_disable(emu, INT_VINC|INT_VDEC|INT_MUTE);
			outl(status & (INT_VINC|INT_VDEC|INT_MUTE), emu->port + IPR);
		}
		if (status & INT_LOOP) {
			int voice;
			emu10k1_voice_t *pvoice;

			pvoice = &emu->voices[voice = (status & 63)];
			if (pvoice->interrupt_handler != NULL) {
				pvoice->interrupt_handler(emu, pvoice);
				snd_emu10k1_voice_intr_ack(emu, voice);
			} else {
				snd_emu10k1_voice_intr_disable(emu, voice);
			}
			outl(INT_LOOP, emu->port + IPR);
		}
		if (status & (INT_FULL|INT_HALF)) {
			if (emu->capture_interrupt)
				emu->capture_interrupt(emu, status);
			else
				snd_emu10k1_intr_disable(emu, INT_FULL|INT_HALF);				
			outl(status & (INT_FULL|INT_HALF), emu->port + IPR);
		}
		if (status & (INT_MICFULL|INT_MICHALF)) {
			if (emu->capture_mic_interrupt)
				emu->capture_mic_interrupt(emu, status);
			else
				snd_emu10k1_intr_disable(emu, INT_MICFULL|INT_MICHALF);
			outl(status & (INT_MICFULL|INT_MICHALF), emu->port + IPR);
		}
		if (status & (INT_TX|INT_RX)) {
			if (emu->mpu401_interrupt)
				emu->mpu401_interrupt(emu, status);
			else
				snd_emu10k1_intr_disable(emu, INT_TX|INT_RX);
			outl(status & (INT_TX|INT_RX), emu->port + IPR);
		}
		if (status & INT_TIMER) {
			if (emu->timer_interrupt)
				emu->timer_interrupt(emu);
			else
				snd_emu10k1_intr_disable(emu, INT_TIMER);
			outl(INT_TIMER, emu->port + IPR);
		}
		if (status & (INT_GSPCS|INT_CDCS)) {
			if (emu->spdif_interrupt)
				emu->spdif_interrupt(emu, status);
			else
				snd_emu10k1_intr_disable(emu, INT_GSPCS|INT_CDCS);
			outl(status & (INT_GSPCS|INT_CDCS), emu->port + IPR);
		}
		if (status & INT_DSP) {
			if (emu->dsp_interrupt)
				emu->dsp_interrupt(emu);
			else
				snd_emu10k1_intr_disable(emu, INT_DSP);
			outl(INT_DSP, emu->port + IPR);
		}
	} while ((status = inl(emu->port + IPR)) != 0);
}

