linux/sound/pci/emu10k1/irq.c
<<
>>
Prefs
   1/*
   2 *  Copyright (c) by Jaroslav Kysela <perex@perex.cz>
   3 *                   Creative Labs, Inc.
   4 *  Routines for IRQ control of EMU10K1 chips
   5 *
   6 *  BUGS:
   7 *    --
   8 *
   9 *  TODO:
  10 *    --
  11 *
  12 *   This program is free software; you can redistribute it and/or modify
  13 *   it under the terms of the GNU General Public License as published by
  14 *   the Free Software Foundation; either version 2 of the License, or
  15 *   (at your option) any later version.
  16 *
  17 *   This program is distributed in the hope that it will be useful,
  18 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
  19 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  20 *   GNU General Public License for more details.
  21 *
  22 *   You should have received a copy of the GNU General Public License
  23 *   along with this program; if not, write to the Free Software
  24 *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
  25 *
  26 */
  27
  28#include <linux/time.h>
  29#include <sound/core.h>
  30#include <sound/emu10k1.h>
  31
  32irqreturn_t snd_emu10k1_interrupt(int irq, void *dev_id)
  33{
  34        struct snd_emu10k1 *emu = dev_id;
  35        unsigned int status, status2, orig_status, orig_status2;
  36        int handled = 0;
  37        int timeout = 0;
  38
  39        while (((status = inl(emu->port + IPR)) != 0) && (timeout < 1000)) {
  40                timeout++;
  41                orig_status = status;
  42                handled = 1;
  43                if ((status & 0xffffffff) == 0xffffffff) {
  44                        dev_info(emu->card->dev,
  45                                 "Suspected sound card removal\n");
  46                        break;
  47                }
  48                if (status & IPR_PCIERROR) {
  49                        dev_err(emu->card->dev, "interrupt: PCI error\n");
  50                        snd_emu10k1_intr_disable(emu, INTE_PCIERRORENABLE);
  51                        status &= ~IPR_PCIERROR;
  52                }
  53                if (status & (IPR_VOLINCR|IPR_VOLDECR|IPR_MUTE)) {
  54                        if (emu->hwvol_interrupt)
  55                                emu->hwvol_interrupt(emu, status);
  56                        else
  57                                snd_emu10k1_intr_disable(emu, INTE_VOLINCRENABLE|INTE_VOLDECRENABLE|INTE_MUTEENABLE);
  58                        status &= ~(IPR_VOLINCR|IPR_VOLDECR|IPR_MUTE);
  59                }
  60                if (status & IPR_CHANNELLOOP) {
  61                        int voice;
  62                        int voice_max = status & IPR_CHANNELNUMBERMASK;
  63                        u32 val;
  64                        struct snd_emu10k1_voice *pvoice = emu->voices;
  65
  66                        val = snd_emu10k1_ptr_read(emu, CLIPL, 0);
  67                        for (voice = 0; voice <= voice_max; voice++) {
  68                                if (voice == 0x20)
  69                                        val = snd_emu10k1_ptr_read(emu, CLIPH, 0);
  70                                if (val & 1) {
  71                                        if (pvoice->use && pvoice->interrupt != NULL) {
  72                                                pvoice->interrupt(emu, pvoice);
  73                                                snd_emu10k1_voice_intr_ack(emu, voice);
  74                                        } else {
  75                                                snd_emu10k1_voice_intr_disable(emu, voice);
  76                                        }
  77                                }
  78                                val >>= 1;
  79                                pvoice++;
  80                        }
  81                        val = snd_emu10k1_ptr_read(emu, HLIPL, 0);
  82                        for (voice = 0; voice <= voice_max; voice++) {
  83                                if (voice == 0x20)
  84                                        val = snd_emu10k1_ptr_read(emu, HLIPH, 0);
  85                                if (val & 1) {
  86                                        if (pvoice->use && pvoice->interrupt != NULL) {
  87                                                pvoice->interrupt(emu, pvoice);
  88                                                snd_emu10k1_voice_half_loop_intr_ack(emu, voice);
  89                                        } else {
  90                                                snd_emu10k1_voice_half_loop_intr_disable(emu, voice);
  91                                        }
  92                                }
  93                                val >>= 1;
  94                                pvoice++;
  95                        }
  96                        status &= ~IPR_CHANNELLOOP;
  97                }
  98                status &= ~IPR_CHANNELNUMBERMASK;
  99                if (status & (IPR_ADCBUFFULL|IPR_ADCBUFHALFFULL)) {
 100                        if (emu->capture_interrupt)
 101                                emu->capture_interrupt(emu, status);
 102                        else
 103                                snd_emu10k1_intr_disable(emu, INTE_ADCBUFENABLE);
 104                        status &= ~(IPR_ADCBUFFULL|IPR_ADCBUFHALFFULL);
 105                }
 106                if (status & (IPR_MICBUFFULL|IPR_MICBUFHALFFULL)) {
 107                        if (emu->capture_mic_interrupt)
 108                                emu->capture_mic_interrupt(emu, status);
 109                        else
 110                                snd_emu10k1_intr_disable(emu, INTE_MICBUFENABLE);
 111                        status &= ~(IPR_MICBUFFULL|IPR_MICBUFHALFFULL);
 112                }
 113                if (status & (IPR_EFXBUFFULL|IPR_EFXBUFHALFFULL)) {
 114                        if (emu->capture_efx_interrupt)
 115                                emu->capture_efx_interrupt(emu, status);
 116                        else
 117                                snd_emu10k1_intr_disable(emu, INTE_EFXBUFENABLE);
 118                        status &= ~(IPR_EFXBUFFULL|IPR_EFXBUFHALFFULL);
 119                }
 120                if (status & (IPR_MIDITRANSBUFEMPTY|IPR_MIDIRECVBUFEMPTY)) {
 121                        if (emu->midi.interrupt)
 122                                emu->midi.interrupt(emu, status);
 123                        else
 124                                snd_emu10k1_intr_disable(emu, INTE_MIDITXENABLE|INTE_MIDIRXENABLE);
 125                        status &= ~(IPR_MIDITRANSBUFEMPTY|IPR_MIDIRECVBUFEMPTY);
 126                }
 127                if (status & (IPR_A_MIDITRANSBUFEMPTY2|IPR_A_MIDIRECVBUFEMPTY2)) {
 128                        if (emu->midi2.interrupt)
 129                                emu->midi2.interrupt(emu, status);
 130                        else
 131                                snd_emu10k1_intr_disable(emu, INTE_A_MIDITXENABLE2|INTE_A_MIDIRXENABLE2);
 132                        status &= ~(IPR_A_MIDITRANSBUFEMPTY2|IPR_A_MIDIRECVBUFEMPTY2);
 133                }
 134                if (status & IPR_INTERVALTIMER) {
 135                        if (emu->timer)
 136                                snd_timer_interrupt(emu->timer, emu->timer->sticks);
 137                        else
 138                                snd_emu10k1_intr_disable(emu, INTE_INTERVALTIMERENB);
 139                        status &= ~IPR_INTERVALTIMER;
 140                }
 141                if (status & (IPR_GPSPDIFSTATUSCHANGE|IPR_CDROMSTATUSCHANGE)) {
 142                        if (emu->spdif_interrupt)
 143                                emu->spdif_interrupt(emu, status);
 144                        else
 145                                snd_emu10k1_intr_disable(emu, INTE_GPSPDIFENABLE|INTE_CDSPDIFENABLE);
 146                        status &= ~(IPR_GPSPDIFSTATUSCHANGE|IPR_CDROMSTATUSCHANGE);
 147                }
 148                if (status & IPR_FXDSP) {
 149                        if (emu->dsp_interrupt)
 150                                emu->dsp_interrupt(emu);
 151                        else
 152                                snd_emu10k1_intr_disable(emu, INTE_FXDSPENABLE);
 153                        status &= ~IPR_FXDSP;
 154                }
 155                if (status & IPR_P16V) {
 156                        while ((status2 = inl(emu->port + IPR2)) != 0) {
 157                                u32 mask = INTE2_PLAYBACK_CH_0_LOOP;  /* Full Loop */
 158                                struct snd_emu10k1_voice *pvoice = &(emu->p16v_voices[0]);
 159                                struct snd_emu10k1_voice *cvoice = &(emu->p16v_capture_voice);
 160
 161                                /* dev_dbg(emu->card->dev, "status2=0x%x\n", status2); */
 162                                orig_status2 = status2;
 163                                if(status2 & mask) {
 164                                        if(pvoice->use) {
 165                                                snd_pcm_period_elapsed(pvoice->epcm->substream);
 166                                        } else { 
 167                                                dev_err(emu->card->dev,
 168                                                        "p16v: status: 0x%08x, mask=0x%08x, pvoice=%p, use=%d\n",
 169                                                        status2, mask, pvoice,
 170                                                        pvoice->use);
 171                                        }
 172                                }
 173                                if(status2 & 0x110000) {
 174                                        /* dev_info(emu->card->dev, "capture int found\n"); */
 175                                        if(cvoice->use) {
 176                                                /* dev_info(emu->card->dev, "capture period_elapsed\n"); */
 177                                                snd_pcm_period_elapsed(cvoice->epcm->substream);
 178                                        }
 179                                }
 180                                outl(orig_status2, emu->port + IPR2); /* ack all */
 181                        }
 182                        status &= ~IPR_P16V;
 183                }
 184
 185                if (status) {
 186                        unsigned int bits;
 187                        dev_err(emu->card->dev,
 188                                "unhandled interrupt: 0x%08x\n", status);
 189                        //make sure any interrupts we don't handle are disabled:
 190                        bits = INTE_FXDSPENABLE |
 191                                INTE_PCIERRORENABLE |
 192                                INTE_VOLINCRENABLE |
 193                                INTE_VOLDECRENABLE |
 194                                INTE_MUTEENABLE |
 195                                INTE_MICBUFENABLE |
 196                                INTE_ADCBUFENABLE |
 197                                INTE_EFXBUFENABLE |
 198                                INTE_GPSPDIFENABLE |
 199                                INTE_CDSPDIFENABLE |
 200                                INTE_INTERVALTIMERENB |
 201                                INTE_MIDITXENABLE |
 202                                INTE_MIDIRXENABLE;
 203                        if (emu->audigy)
 204                                bits |= INTE_A_MIDITXENABLE2 | INTE_A_MIDIRXENABLE2;
 205                        snd_emu10k1_intr_disable(emu, bits);
 206                }
 207                outl(orig_status, emu->port + IPR); /* ack all */
 208        }
 209        if (timeout == 1000)
 210                dev_info(emu->card->dev, "emu10k1 irq routine failure\n");
 211
 212        return IRQ_RETVAL(handled);
 213}
 214