1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3 * Routine for IRQ handling from GF1/InterWave chip
4 * Copyright (c) by Jaroslav Kysela <perex@perex.cz>
5 */
6
7 #include <sound/core.h>
8 #include <sound/info.h>
9 #include <sound/gus.h>
10
11 #ifdef CONFIG_SND_DEBUG
12 #define STAT_ADD(x) ((x)++)
13 #else
14 #define STAT_ADD(x) while (0) { ; }
15 #endif
16
snd_gus_interrupt(int irq,void * dev_id)17 irqreturn_t snd_gus_interrupt(int irq, void *dev_id)
18 {
19 struct snd_gus_card * gus = dev_id;
20 unsigned char status;
21 int loop = 100;
22 int handled = 0;
23
24 __again:
25 status = inb(gus->gf1.reg_irqstat);
26 if (status == 0)
27 return IRQ_RETVAL(handled);
28 handled = 1;
29 if (status & 0x02) {
30 STAT_ADD(gus->gf1.interrupt_stat_midi_in);
31 if (gus->gf1.interrupt_handler_midi_in)
32 gus->gf1.interrupt_handler_midi_in(gus);
33 }
34 if (status & 0x01) {
35 STAT_ADD(gus->gf1.interrupt_stat_midi_out);
36 if (gus->gf1.interrupt_handler_midi_out)
37 gus->gf1.interrupt_handler_midi_out(gus);
38 }
39 if (status & (0x20 | 0x40)) {
40 unsigned int already, _current_;
41 unsigned char voice_status, voice;
42 struct snd_gus_voice *pvoice;
43
44 already = 0;
45 while (((voice_status = snd_gf1_i_read8(gus, SNDRV_GF1_GB_VOICES_IRQ)) & 0xc0) != 0xc0) {
46 voice = voice_status & 0x1f;
47 _current_ = 1 << voice;
48 if (already & _current_)
49 continue; /* multi request */
50 already |= _current_; /* mark request */
51 #if 0
52 dev_dbg(gus->card->dev,
53 "voice = %i, voice_status = 0x%x, voice_verify = %i\n",
54 voice, voice_status, inb(GUSP(gus, GF1PAGE)));
55 #endif
56 pvoice = &gus->gf1.voices[voice];
57 if (pvoice->use) {
58 if (!(voice_status & 0x80)) { /* voice position IRQ */
59 STAT_ADD(pvoice->interrupt_stat_wave);
60 pvoice->handler_wave(gus, pvoice);
61 }
62 if (!(voice_status & 0x40)) { /* volume ramp IRQ */
63 STAT_ADD(pvoice->interrupt_stat_volume);
64 pvoice->handler_volume(gus, pvoice);
65 }
66 } else {
67 STAT_ADD(gus->gf1.interrupt_stat_voice_lost);
68 snd_gf1_i_ctrl_stop(gus, SNDRV_GF1_VB_ADDRESS_CONTROL);
69 snd_gf1_i_ctrl_stop(gus, SNDRV_GF1_VB_VOLUME_CONTROL);
70 }
71 }
72 }
73 if (status & 0x04) {
74 STAT_ADD(gus->gf1.interrupt_stat_timer1);
75 if (gus->gf1.interrupt_handler_timer1)
76 gus->gf1.interrupt_handler_timer1(gus);
77 }
78 if (status & 0x08) {
79 STAT_ADD(gus->gf1.interrupt_stat_timer2);
80 if (gus->gf1.interrupt_handler_timer2)
81 gus->gf1.interrupt_handler_timer2(gus);
82 }
83 if (status & 0x80) {
84 if (snd_gf1_i_look8(gus, SNDRV_GF1_GB_DRAM_DMA_CONTROL) & 0x40) {
85 STAT_ADD(gus->gf1.interrupt_stat_dma_write);
86 if (gus->gf1.interrupt_handler_dma_write)
87 gus->gf1.interrupt_handler_dma_write(gus);
88 }
89 if (snd_gf1_i_look8(gus, SNDRV_GF1_GB_REC_DMA_CONTROL) & 0x40) {
90 STAT_ADD(gus->gf1.interrupt_stat_dma_read);
91 if (gus->gf1.interrupt_handler_dma_read)
92 gus->gf1.interrupt_handler_dma_read(gus);
93 }
94 }
95 if (--loop > 0)
96 goto __again;
97 return IRQ_NONE;
98 }
99
100 #ifdef CONFIG_SND_DEBUG
snd_gus_irq_info_read(struct snd_info_entry * entry,struct snd_info_buffer * buffer)101 static void snd_gus_irq_info_read(struct snd_info_entry *entry,
102 struct snd_info_buffer *buffer)
103 {
104 struct snd_gus_card *gus;
105 struct snd_gus_voice *pvoice;
106 int idx;
107
108 gus = entry->private_data;
109 snd_iprintf(buffer, "midi out = %u\n", gus->gf1.interrupt_stat_midi_out);
110 snd_iprintf(buffer, "midi in = %u\n", gus->gf1.interrupt_stat_midi_in);
111 snd_iprintf(buffer, "timer1 = %u\n", gus->gf1.interrupt_stat_timer1);
112 snd_iprintf(buffer, "timer2 = %u\n", gus->gf1.interrupt_stat_timer2);
113 snd_iprintf(buffer, "dma write = %u\n", gus->gf1.interrupt_stat_dma_write);
114 snd_iprintf(buffer, "dma read = %u\n", gus->gf1.interrupt_stat_dma_read);
115 snd_iprintf(buffer, "voice lost = %u\n", gus->gf1.interrupt_stat_voice_lost);
116 for (idx = 0; idx < 32; idx++) {
117 pvoice = &gus->gf1.voices[idx];
118 snd_iprintf(buffer, "voice %i: wave = %u, volume = %u\n",
119 idx,
120 pvoice->interrupt_stat_wave,
121 pvoice->interrupt_stat_volume);
122 }
123 }
124
snd_gus_irq_profile_init(struct snd_gus_card * gus)125 void snd_gus_irq_profile_init(struct snd_gus_card *gus)
126 {
127 snd_card_ro_proc_new(gus->card, "gusirq", gus, snd_gus_irq_info_read);
128 }
129
130 #endif
131