131 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * Routine for IRQ handling from GF1/InterWave chip4 * 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_DEBUG12#define STAT_ADD(x) ((x)++)13#else14#define STAT_ADD(x) while (0) { ; }15#endif16 17irqreturn_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 052 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#endif56 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_DEBUG101static 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 125void 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#endif131