xref: /openbmc/linux/sound/pci/emu10k1/irq.c (revision 3ddc8b84)
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 
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