xref: /openbmc/linux/sound/pci/emu10k1/irq.c (revision c4c3c32d)
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) {
26 		handled = 1;
27 		if ((status & 0xffffffff) == 0xffffffff) {
28 			dev_info(emu->card->dev,
29 				 "Suspected sound card removal\n");
30 			break;
31 		}
32 		if (++timeout == 1000) {
33 			dev_info(emu->card->dev, "emu10k1 irq routine failure\n");
34 			break;
35 		}
36 		orig_status = status;
37 		if (status & IPR_PCIERROR) {
38 			dev_err(emu->card->dev, "interrupt: PCI error\n");
39 			snd_emu10k1_intr_disable(emu, INTE_PCIERRORENABLE);
40 			status &= ~IPR_PCIERROR;
41 		}
42 		if (status & (IPR_VOLINCR|IPR_VOLDECR|IPR_MUTE)) {
43 			if (emu->hwvol_interrupt)
44 				emu->hwvol_interrupt(emu, status);
45 			else
46 				snd_emu10k1_intr_disable(emu, INTE_VOLINCRENABLE|INTE_VOLDECRENABLE|INTE_MUTEENABLE);
47 			status &= ~(IPR_VOLINCR|IPR_VOLDECR|IPR_MUTE);
48 		}
49 		if (status & IPR_CHANNELLOOP) {
50 			struct snd_emu10k1_voice *pvoice;
51 			int voice;
52 			int voice_max = status & IPR_CHANNELNUMBERMASK;
53 			u32 val;
54 
55 			val = snd_emu10k1_ptr_read(emu, CLIPL, 0);
56 			pvoice = emu->voices;
57 			for (voice = 0; voice <= voice_max; voice++) {
58 				if (voice == 0x20)
59 					val = snd_emu10k1_ptr_read(emu, CLIPH, 0);
60 				if (val & 1) {
61 					if (pvoice->use && pvoice->interrupt != NULL) {
62 						pvoice->interrupt(emu, pvoice);
63 						snd_emu10k1_voice_intr_ack(emu, voice);
64 					} else {
65 						snd_emu10k1_voice_intr_disable(emu, voice);
66 					}
67 				}
68 				val >>= 1;
69 				pvoice++;
70 			}
71 			val = snd_emu10k1_ptr_read(emu, HLIPL, 0);
72 			pvoice = emu->voices;
73 			for (voice = 0; voice <= voice_max; voice++) {
74 				if (voice == 0x20)
75 					val = snd_emu10k1_ptr_read(emu, HLIPH, 0);
76 				if (val & 1) {
77 					if (pvoice->use && pvoice->interrupt != NULL) {
78 						pvoice->interrupt(emu, pvoice);
79 						snd_emu10k1_voice_half_loop_intr_ack(emu, voice);
80 					} else {
81 						snd_emu10k1_voice_half_loop_intr_disable(emu, voice);
82 					}
83 				}
84 				val >>= 1;
85 				pvoice++;
86 			}
87 			status &= ~(IPR_CHANNELLOOP | IPR_CHANNELNUMBERMASK);
88 		}
89 		if (status & (IPR_ADCBUFFULL|IPR_ADCBUFHALFFULL)) {
90 			if (emu->capture_interrupt)
91 				emu->capture_interrupt(emu, status);
92 			else
93 				snd_emu10k1_intr_disable(emu, INTE_ADCBUFENABLE);
94 			status &= ~(IPR_ADCBUFFULL|IPR_ADCBUFHALFFULL);
95 		}
96 		if (status & (IPR_MICBUFFULL|IPR_MICBUFHALFFULL)) {
97 			if (emu->capture_mic_interrupt)
98 				emu->capture_mic_interrupt(emu, status);
99 			else
100 				snd_emu10k1_intr_disable(emu, INTE_MICBUFENABLE);
101 			status &= ~(IPR_MICBUFFULL|IPR_MICBUFHALFFULL);
102 		}
103 		if (status & (IPR_EFXBUFFULL|IPR_EFXBUFHALFFULL)) {
104 			if (emu->capture_efx_interrupt)
105 				emu->capture_efx_interrupt(emu, status);
106 			else
107 				snd_emu10k1_intr_disable(emu, INTE_EFXBUFENABLE);
108 			status &= ~(IPR_EFXBUFFULL|IPR_EFXBUFHALFFULL);
109 		}
110 		if (status & (IPR_MIDITRANSBUFEMPTY|IPR_MIDIRECVBUFEMPTY)) {
111 			if (emu->midi.interrupt)
112 				emu->midi.interrupt(emu, status);
113 			else
114 				snd_emu10k1_intr_disable(emu, INTE_MIDITXENABLE|INTE_MIDIRXENABLE);
115 			status &= ~(IPR_MIDITRANSBUFEMPTY|IPR_MIDIRECVBUFEMPTY);
116 		}
117 		if (status & (IPR_A_MIDITRANSBUFEMPTY2|IPR_A_MIDIRECVBUFEMPTY2)) {
118 			if (emu->midi2.interrupt)
119 				emu->midi2.interrupt(emu, status);
120 			else
121 				snd_emu10k1_intr_disable(emu, INTE_A_MIDITXENABLE2|INTE_A_MIDIRXENABLE2);
122 			status &= ~(IPR_A_MIDITRANSBUFEMPTY2|IPR_A_MIDIRECVBUFEMPTY2);
123 		}
124 		if (status & IPR_INTERVALTIMER) {
125 			if (emu->timer)
126 				snd_timer_interrupt(emu->timer, emu->timer->sticks);
127 			else
128 				snd_emu10k1_intr_disable(emu, INTE_INTERVALTIMERENB);
129 			status &= ~IPR_INTERVALTIMER;
130 		}
131 		if (status & (IPR_GPSPDIFSTATUSCHANGE|IPR_CDROMSTATUSCHANGE)) {
132 			if (emu->spdif_interrupt)
133 				emu->spdif_interrupt(emu, status);
134 			else
135 				snd_emu10k1_intr_disable(emu, INTE_GPSPDIFENABLE|INTE_CDSPDIFENABLE);
136 			status &= ~(IPR_GPSPDIFSTATUSCHANGE|IPR_CDROMSTATUSCHANGE);
137 		}
138 		if (status & IPR_FXDSP) {
139 			if (emu->dsp_interrupt)
140 				emu->dsp_interrupt(emu);
141 			else
142 				snd_emu10k1_intr_disable(emu, INTE_FXDSPENABLE);
143 			status &= ~IPR_FXDSP;
144 		}
145 		if (status & IPR_P16V) {
146 			if (emu->p16v_interrupt)
147 				emu->p16v_interrupt(emu);
148 			else
149 				outl(0, emu->port + INTE2);
150 			status &= ~IPR_P16V;
151 		}
152 
153 		if (status) {
154 			dev_err(emu->card->dev,
155 				"unhandled interrupt: 0x%08x\n", status);
156 		}
157 		outl(orig_status, emu->port + IPR); /* ack all */
158 	}
159 
160 	return IRQ_RETVAL(handled);
161 }
162