xref: /kernel/linux/linux-6.6/sound/pci/emu10k1/irq.c (revision 62306a36)
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(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