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
8#include <linux/time.h>
9#include <sound/core.h>
10#include <sound/emu10k1.h>
11
12irqreturn_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(port: 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, chn: 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, chn: 0);
54 if (val & 1) {
55 if (pvoice->use && pvoice->interrupt != NULL) {
56 pvoice->interrupt(emu, pvoice);
57 snd_emu10k1_voice_intr_ack(emu, voicenum: voice);
58 } else {
59 snd_emu10k1_voice_intr_disable(emu, voicenum: voice);
60 }
61 }
62 val >>= 1;
63 pvoice++;
64 }
65 val = snd_emu10k1_ptr_read(emu, HLIPL, chn: 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, chn: 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, voicenum: voice);
74 } else {
75 snd_emu10k1_voice_half_loop_intr_disable(emu, voicenum: 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(timer: emu->timer, ticks_left: 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(value: 0, port: 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(value: orig_status, port: emu->port + IPR); /* ack all */
159 }
160
161 return IRQ_RETVAL(handled);
162}
163

source code of linux/sound/pci/emu10k1/irq.c