Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/sound/pci/emu10k1/irq.c
26439 views
1
// SPDX-License-Identifier: GPL-2.0-or-later
2
/*
3
* Copyright (c) by Jaroslav Kysela <[email protected]>
4
* Creative Labs, Inc.
5
* Routines for IRQ control of EMU10K1 chips
6
*/
7
8
#include <linux/time.h>
9
#include <sound/core.h>
10
#include <sound/emu10k1.h>
11
12
irqreturn_t snd_emu10k1_interrupt(int irq, void *dev_id)
13
{
14
struct snd_emu10k1 *emu = dev_id;
15
unsigned int status, orig_status;
16
int handled = 0;
17
int timeout = 0;
18
19
while ((status = inl(emu->port + IPR)) != 0) {
20
handled = 1;
21
if ((status & 0xffffffff) == 0xffffffff) {
22
dev_info(emu->card->dev,
23
"Suspected sound card removal\n");
24
break;
25
}
26
if (++timeout == 1000) {
27
dev_info(emu->card->dev, "emu10k1 irq routine failure\n");
28
break;
29
}
30
orig_status = status;
31
if (status & IPR_PCIERROR) {
32
dev_err(emu->card->dev, "interrupt: PCI error\n");
33
snd_emu10k1_intr_disable(emu, INTE_PCIERRORENABLE);
34
status &= ~IPR_PCIERROR;
35
}
36
if (status & (IPR_VOLINCR|IPR_VOLDECR|IPR_MUTE)) {
37
if (emu->hwvol_interrupt)
38
emu->hwvol_interrupt(emu, status);
39
else
40
snd_emu10k1_intr_disable(emu, INTE_VOLINCRENABLE|INTE_VOLDECRENABLE|INTE_MUTEENABLE);
41
status &= ~(IPR_VOLINCR|IPR_VOLDECR|IPR_MUTE);
42
}
43
if (status & IPR_CHANNELLOOP) {
44
struct snd_emu10k1_voice *pvoice;
45
int voice;
46
int voice_max = status & IPR_CHANNELNUMBERMASK;
47
u32 val;
48
49
val = snd_emu10k1_ptr_read(emu, CLIPL, 0);
50
pvoice = emu->voices;
51
for (voice = 0; voice <= voice_max; voice++) {
52
if (voice == 0x20)
53
val = snd_emu10k1_ptr_read(emu, CLIPH, 0);
54
if (val & 1) {
55
if (pvoice->use && pvoice->interrupt != NULL) {
56
pvoice->interrupt(emu, pvoice);
57
snd_emu10k1_voice_intr_ack(emu, voice);
58
} else {
59
snd_emu10k1_voice_intr_disable(emu, voice);
60
}
61
}
62
val >>= 1;
63
pvoice++;
64
}
65
val = snd_emu10k1_ptr_read(emu, HLIPL, 0);
66
pvoice = emu->voices;
67
for (voice = 0; voice <= voice_max; voice++) {
68
if (voice == 0x20)
69
val = snd_emu10k1_ptr_read(emu, HLIPH, 0);
70
if (val & 1) {
71
if (pvoice->use && pvoice->interrupt != NULL) {
72
pvoice->interrupt(emu, pvoice);
73
snd_emu10k1_voice_half_loop_intr_ack(emu, voice);
74
} else {
75
snd_emu10k1_voice_half_loop_intr_disable(emu, voice);
76
}
77
}
78
val >>= 1;
79
pvoice++;
80
}
81
status &= ~(IPR_CHANNELLOOP | IPR_CHANNELNUMBERMASK);
82
}
83
if (status & (IPR_ADCBUFFULL|IPR_ADCBUFHALFFULL)) {
84
if (emu->capture_interrupt)
85
emu->capture_interrupt(emu, status);
86
else
87
snd_emu10k1_intr_disable(emu, INTE_ADCBUFENABLE);
88
status &= ~(IPR_ADCBUFFULL|IPR_ADCBUFHALFFULL);
89
}
90
if (status & (IPR_MICBUFFULL|IPR_MICBUFHALFFULL)) {
91
if (emu->capture_mic_interrupt)
92
emu->capture_mic_interrupt(emu, status);
93
else
94
snd_emu10k1_intr_disable(emu, INTE_MICBUFENABLE);
95
status &= ~(IPR_MICBUFFULL|IPR_MICBUFHALFFULL);
96
}
97
if (status & (IPR_EFXBUFFULL|IPR_EFXBUFHALFFULL)) {
98
if (emu->capture_efx_interrupt)
99
emu->capture_efx_interrupt(emu, status);
100
else
101
snd_emu10k1_intr_disable(emu, INTE_EFXBUFENABLE);
102
status &= ~(IPR_EFXBUFFULL|IPR_EFXBUFHALFFULL);
103
}
104
if (status & (IPR_MIDITRANSBUFEMPTY|IPR_MIDIRECVBUFEMPTY)) {
105
if (emu->midi.interrupt)
106
emu->midi.interrupt(emu, status);
107
else
108
snd_emu10k1_intr_disable(emu, INTE_MIDITXENABLE|INTE_MIDIRXENABLE);
109
status &= ~(IPR_MIDITRANSBUFEMPTY|IPR_MIDIRECVBUFEMPTY);
110
}
111
if (status & (IPR_A_MIDITRANSBUFEMPTY2|IPR_A_MIDIRECVBUFEMPTY2)) {
112
if (emu->midi2.interrupt)
113
emu->midi2.interrupt(emu, status);
114
else
115
snd_emu10k1_intr_disable(emu, INTE_A_MIDITXENABLE2|INTE_A_MIDIRXENABLE2);
116
status &= ~(IPR_A_MIDITRANSBUFEMPTY2|IPR_A_MIDIRECVBUFEMPTY2);
117
}
118
if (status & IPR_INTERVALTIMER) {
119
if (emu->timer)
120
snd_timer_interrupt(emu->timer, emu->timer->sticks);
121
else
122
snd_emu10k1_intr_disable(emu, INTE_INTERVALTIMERENB);
123
status &= ~IPR_INTERVALTIMER;
124
}
125
if (status & (IPR_GPSPDIFSTATUSCHANGE|IPR_CDROMSTATUSCHANGE)) {
126
if (emu->spdif_interrupt)
127
emu->spdif_interrupt(emu, status);
128
else
129
snd_emu10k1_intr_disable(emu, INTE_GPSPDIFENABLE|INTE_CDSPDIFENABLE);
130
status &= ~(IPR_GPSPDIFSTATUSCHANGE|IPR_CDROMSTATUSCHANGE);
131
}
132
if (status & IPR_FXDSP) {
133
if (emu->dsp_interrupt)
134
emu->dsp_interrupt(emu);
135
else
136
snd_emu10k1_intr_disable(emu, INTE_FXDSPENABLE);
137
status &= ~IPR_FXDSP;
138
}
139
if (status & IPR_P16V) {
140
if (emu->p16v_interrupt)
141
emu->p16v_interrupt(emu);
142
else
143
outl(0, emu->port + INTE2);
144
status &= ~IPR_P16V;
145
}
146
if (status & IPR_A_GPIO) {
147
if (emu->gpio_interrupt)
148
emu->gpio_interrupt(emu);
149
else
150
snd_emu10k1_intr_disable(emu, INTE_A_GPIOENABLE);
151
status &= ~IPR_A_GPIO;
152
}
153
154
if (status) {
155
dev_err(emu->card->dev,
156
"unhandled interrupt: 0x%08x\n", status);
157
}
158
outl(orig_status, emu->port + IPR); /* ack all */
159
}
160
161
return IRQ_RETVAL(handled);
162
}
163
164