Merge git://git.kernel.org/pub/scm/linux/kernel/git/sam/kbuild
[linux-2.6] / sound / isa / gus / gus_irq.c
1 /*
2  *  Routine for IRQ handling from GF1/InterWave chip
3  *  Copyright (c) by Jaroslav Kysela <perex@perex.cz>
4  *
5  *
6  *   This program is free software; you can redistribute it and/or modify
7  *   it under the terms of the GNU General Public License as published by
8  *   the Free Software Foundation; either version 2 of the License, or
9  *   (at your option) any later version.
10  *
11  *   This program is distributed in the hope that it will be useful,
12  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *   GNU General Public License for more details.
15  *
16  *   You should have received a copy of the GNU General Public License
17  *   along with this program; if not, write to the Free Software
18  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
19  *
20  */
21
22 #include <sound/driver.h>
23 #include <sound/core.h>
24 #include <sound/info.h>
25 #include <sound/gus.h>
26
27 #ifdef CONFIG_SND_DEBUG
28 #define STAT_ADD(x)     ((x)++)
29 #else
30 #define STAT_ADD(x)     while (0) { ; }
31 #endif
32
33 irqreturn_t snd_gus_interrupt(int irq, void *dev_id)
34 {
35         struct snd_gus_card * gus = dev_id;
36         unsigned char status;
37         int loop = 100;
38         int handled = 0;
39
40 __again:
41         status = inb(gus->gf1.reg_irqstat);
42         if (status == 0)
43                 return IRQ_RETVAL(handled);
44         handled = 1;
45         // snd_printk("IRQ: status = 0x%x\n", status);
46         if (status & 0x02) {
47                 STAT_ADD(gus->gf1.interrupt_stat_midi_in);
48                 if (gus->gf1.interrupt_handler_midi_in)
49                         gus->gf1.interrupt_handler_midi_in(gus);
50         }
51         if (status & 0x01) {
52                 STAT_ADD(gus->gf1.interrupt_stat_midi_out);
53                 if (gus->gf1.interrupt_handler_midi_out)
54                         gus->gf1.interrupt_handler_midi_out(gus);
55         }
56         if (status & (0x20 | 0x40)) {
57                 unsigned int already, _current_;
58                 unsigned char voice_status, voice;
59                 struct snd_gus_voice *pvoice;
60
61                 already = 0;
62                 while (((voice_status = snd_gf1_i_read8(gus, SNDRV_GF1_GB_VOICES_IRQ)) & 0xc0) != 0xc0) {
63                         voice = voice_status & 0x1f;
64                         _current_ = 1 << voice;
65                         if (already & _current_)
66                                 continue;       /* multi request */
67                         already |= _current_;   /* mark request */
68 #if 0
69                         printk("voice = %i, voice_status = 0x%x, voice_verify = %i\n", voice, voice_status, inb(GUSP(gus, GF1PAGE)));
70 #endif
71                         pvoice = &gus->gf1.voices[voice]; 
72                         if (pvoice->use) {
73                                 if (!(voice_status & 0x80)) {   /* voice position IRQ */
74                                         STAT_ADD(pvoice->interrupt_stat_wave);
75                                         pvoice->handler_wave(gus, pvoice);
76                                 }
77                                 if (!(voice_status & 0x40)) {   /* volume ramp IRQ */
78                                         STAT_ADD(pvoice->interrupt_stat_volume);
79                                         pvoice->handler_volume(gus, pvoice);
80                                 }
81                         } else {
82                                 STAT_ADD(gus->gf1.interrupt_stat_voice_lost);
83                                 snd_gf1_i_ctrl_stop(gus, SNDRV_GF1_VB_ADDRESS_CONTROL);
84                                 snd_gf1_i_ctrl_stop(gus, SNDRV_GF1_VB_VOLUME_CONTROL);
85                         }
86                 }
87         }
88         if (status & 0x04) {
89                 STAT_ADD(gus->gf1.interrupt_stat_timer1);
90                 if (gus->gf1.interrupt_handler_timer1)
91                         gus->gf1.interrupt_handler_timer1(gus);
92         }
93         if (status & 0x08) {
94                 STAT_ADD(gus->gf1.interrupt_stat_timer2);
95                 if (gus->gf1.interrupt_handler_timer2)
96                         gus->gf1.interrupt_handler_timer2(gus);
97         }
98         if (status & 0x80) {
99                 if (snd_gf1_i_look8(gus, SNDRV_GF1_GB_DRAM_DMA_CONTROL) & 0x40) {
100                         STAT_ADD(gus->gf1.interrupt_stat_dma_write);
101                         if (gus->gf1.interrupt_handler_dma_write)
102                                 gus->gf1.interrupt_handler_dma_write(gus);
103                 }
104                 if (snd_gf1_i_look8(gus, SNDRV_GF1_GB_REC_DMA_CONTROL) & 0x40) {
105                         STAT_ADD(gus->gf1.interrupt_stat_dma_read);
106                         if (gus->gf1.interrupt_handler_dma_read)
107                                 gus->gf1.interrupt_handler_dma_read(gus);
108                 }
109         }
110         if (--loop > 0)
111                 goto __again;
112         return IRQ_NONE;
113 }
114
115 #ifdef CONFIG_SND_DEBUG
116 static void snd_gus_irq_info_read(struct snd_info_entry *entry, 
117                                   struct snd_info_buffer *buffer)
118 {
119         struct snd_gus_card *gus;
120         struct snd_gus_voice *pvoice;
121         int idx;
122
123         gus = entry->private_data;
124         snd_iprintf(buffer, "midi out = %u\n", gus->gf1.interrupt_stat_midi_out);
125         snd_iprintf(buffer, "midi in = %u\n", gus->gf1.interrupt_stat_midi_in);
126         snd_iprintf(buffer, "timer1 = %u\n", gus->gf1.interrupt_stat_timer1);
127         snd_iprintf(buffer, "timer2 = %u\n", gus->gf1.interrupt_stat_timer2);
128         snd_iprintf(buffer, "dma write = %u\n", gus->gf1.interrupt_stat_dma_write);
129         snd_iprintf(buffer, "dma read = %u\n", gus->gf1.interrupt_stat_dma_read);
130         snd_iprintf(buffer, "voice lost = %u\n", gus->gf1.interrupt_stat_voice_lost);
131         for (idx = 0; idx < 32; idx++) {
132                 pvoice = &gus->gf1.voices[idx];
133                 snd_iprintf(buffer, "voice %i: wave = %u, volume = %u\n",
134                                         idx,
135                                         pvoice->interrupt_stat_wave,
136                                         pvoice->interrupt_stat_volume);
137         }
138 }
139
140 void snd_gus_irq_profile_init(struct snd_gus_card *gus)
141 {
142         struct snd_info_entry *entry;
143
144         if (! snd_card_proc_new(gus->card, "gusirq", &entry))
145                 snd_info_set_text_ops(entry, gus, snd_gus_irq_info_read);
146 }
147
148 #endif