Merge tag 'sound-6.4-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai...
[linux-block.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, orig_status;
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                         if (emu->p16v_interrupt)
143                                 emu->p16v_interrupt(emu);
144                         else
145                                 outl(0, emu->port + INTE2);
146                         status &= ~IPR_P16V;
147                 }
148
149                 if (status) {
150                         unsigned int bits;
151                         dev_err(emu->card->dev,
152                                 "unhandled interrupt: 0x%08x\n", status);
153                         //make sure any interrupts we don't handle are disabled:
154                         bits = INTE_FXDSPENABLE |
155                                 INTE_PCIERRORENABLE |
156                                 INTE_VOLINCRENABLE |
157                                 INTE_VOLDECRENABLE |
158                                 INTE_MUTEENABLE |
159                                 INTE_MICBUFENABLE |
160                                 INTE_ADCBUFENABLE |
161                                 INTE_EFXBUFENABLE |
162                                 INTE_GPSPDIFENABLE |
163                                 INTE_CDSPDIFENABLE |
164                                 INTE_INTERVALTIMERENB |
165                                 INTE_MIDITXENABLE |
166                                 INTE_MIDIRXENABLE;
167                         if (emu->audigy)
168                                 bits |= INTE_A_MIDITXENABLE2 | INTE_A_MIDIRXENABLE2;
169                         snd_emu10k1_intr_disable(emu, bits);
170                 }
171                 outl(orig_status, emu->port + IPR); /* ack all */
172         }
173         if (timeout == 1000)
174                 dev_info(emu->card->dev, "emu10k1 irq routine failure\n");
175
176         return IRQ_RETVAL(handled);
177 }