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