aboutsummaryrefslogtreecommitdiff
path: root/sound/isa/gus
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 15:20:36 -0700
committerLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 15:20:36 -0700
commit1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch)
tree0bba044c4ce775e45a88a51686b5d9f90697ea9d /sound/isa/gus
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history, even though we have it. We can create a separate "historical" git archive of that later if we want to, and in the meantime it's about 3.2GB when imported into git - space that would just make the early git days unnecessarily complicated, when we don't have a lot of good infrastructure for it. Let it rip!
Diffstat (limited to 'sound/isa/gus')
-rw-r--r--sound/isa/gus/Makefile36
-rw-r--r--sound/isa/gus/gus_dma.c244
-rw-r--r--sound/isa/gus/gus_dram.c103
-rw-r--r--sound/isa/gus/gus_instr.c173
-rw-r--r--sound/isa/gus/gus_io.c531
-rw-r--r--sound/isa/gus/gus_irq.c142
-rw-r--r--sound/isa/gus/gus_main.c514
-rw-r--r--sound/isa/gus/gus_mem.c353
-rw-r--r--sound/isa/gus/gus_mem_proc.c135
-rw-r--r--sound/isa/gus/gus_mixer.c199
-rw-r--r--sound/isa/gus/gus_pcm.c903
-rw-r--r--sound/isa/gus/gus_reset.c413
-rw-r--r--sound/isa/gus/gus_sample.c155
-rw-r--r--sound/isa/gus/gus_simple.c634
-rw-r--r--sound/isa/gus/gus_synth.c329
-rw-r--r--sound/isa/gus/gus_tables.h86
-rw-r--r--sound/isa/gus/gus_timer.c204
-rw-r--r--sound/isa/gus/gus_uart.c257
-rw-r--r--sound/isa/gus/gus_volume.c210
-rw-r--r--sound/isa/gus/gusclassic.c260
-rw-r--r--sound/isa/gus/gusextreme.c374
-rw-r--r--sound/isa/gus/gusmax.c400
-rw-r--r--sound/isa/gus/interwave-stb.c2
-rw-r--r--sound/isa/gus/interwave.c969
24 files changed, 7626 insertions, 0 deletions
diff --git a/sound/isa/gus/Makefile b/sound/isa/gus/Makefile
new file mode 100644
index 00000000000..bae5dbd6c8e
--- /dev/null
+++ b/sound/isa/gus/Makefile
@@ -0,0 +1,36 @@
+#
+# Makefile for ALSA
+# Copyright (c) 2001 by Jaroslav Kysela <perex@suse.cz>
+#
+
+snd-gus-lib-objs := gus_main.o \
+ gus_io.o gus_irq.o gus_timer.o \
+ gus_mem.o gus_mem_proc.o gus_dram.o gus_dma.o gus_volume.o \
+ gus_pcm.o gus_mixer.o \
+ gus_uart.o \
+ gus_reset.o
+snd-gus-synth-objs := gus_synth.o gus_sample.o gus_simple.o gus_instr.o
+
+snd-gusclassic-objs := gusclassic.o
+snd-gusextreme-objs := gusextreme.o
+snd-gusmax-objs := gusmax.o
+snd-interwave-objs := interwave.o
+snd-interwave-stb-objs := interwave-stb.o
+
+#
+# this function returns:
+# "m" - CONFIG_SND_SEQUENCER is m
+# <empty string> - CONFIG_SND_SEQUENCER is undefined
+# otherwise parameter #1 value
+#
+sequencer = $(if $(subst y,,$(CONFIG_SND_SEQUENCER)),$(if $(1),m),$(if $(CONFIG_SND_SEQUENCER),$(1)))
+
+# Toplevel Module Dependency
+obj-$(CONFIG_SND_GUSCLASSIC) += snd-gusclassic.o snd-gus-lib.o
+obj-$(CONFIG_SND_GUSMAX) += snd-gusmax.o snd-gus-lib.o
+obj-$(CONFIG_SND_GUSEXTREME) += snd-gusextreme.o snd-gus-lib.o
+obj-$(CONFIG_SND_INTERWAVE) += snd-interwave.o snd-gus-lib.o
+obj-$(CONFIG_SND_INTERWAVE_STB) += snd-interwave-stb.o snd-gus-lib.o
+obj-$(call sequencer,$(CONFIG_SND_GUS_SYNTH)) += snd-gus-synth.o
+
+obj-m := $(sort $(obj-m))
diff --git a/sound/isa/gus/gus_dma.c b/sound/isa/gus/gus_dma.c
new file mode 100644
index 00000000000..de4b56d80b3
--- /dev/null
+++ b/sound/isa/gus/gus_dma.c
@@ -0,0 +1,244 @@
+/*
+ * Routines for GF1 DMA control
+ * Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <sound/driver.h>
+#include <asm/dma.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/gus.h>
+
+static void snd_gf1_dma_ack(snd_gus_card_t * gus)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&gus->reg_lock, flags);
+ snd_gf1_write8(gus, SNDRV_GF1_GB_DRAM_DMA_CONTROL, 0x00);
+ snd_gf1_look8(gus, SNDRV_GF1_GB_DRAM_DMA_CONTROL);
+ spin_unlock_irqrestore(&gus->reg_lock, flags);
+}
+
+static void snd_gf1_dma_program(snd_gus_card_t * gus,
+ unsigned int addr,
+ unsigned long buf_addr,
+ unsigned int count,
+ unsigned int cmd)
+{
+ unsigned long flags;
+ unsigned int address;
+ unsigned char dma_cmd;
+ unsigned int address_high;
+
+ // snd_printk("dma_transfer: addr=0x%x, buf=0x%lx, count=0x%x\n", addr, (long) buf, count);
+
+ if (gus->gf1.dma1 > 3) {
+ if (gus->gf1.enh_mode) {
+ address = addr >> 1;
+ } else {
+ if (addr & 0x1f) {
+ snd_printd("snd_gf1_dma_transfer: unaligned address (0x%x)?\n", addr);
+ return;
+ }
+ address = (addr & 0x000c0000) | ((addr & 0x0003ffff) >> 1);
+ }
+ } else {
+ address = addr;
+ }
+
+ dma_cmd = SNDRV_GF1_DMA_ENABLE | (unsigned short) cmd;
+#if 0
+ dma_cmd |= 0x08;
+#endif
+ if (dma_cmd & SNDRV_GF1_DMA_16BIT) {
+ count++;
+ count &= ~1; /* align */
+ }
+ if (gus->gf1.dma1 > 3) {
+ dma_cmd |= SNDRV_GF1_DMA_WIDTH16;
+ count++;
+ count &= ~1; /* align */
+ }
+ snd_gf1_dma_ack(gus);
+ snd_dma_program(gus->gf1.dma1, buf_addr, count, dma_cmd & SNDRV_GF1_DMA_READ ? DMA_MODE_READ : DMA_MODE_WRITE);
+#if 0
+ snd_printk("address = 0x%x, count = 0x%x, dma_cmd = 0x%x\n", address << 1, count, dma_cmd);
+#endif
+ spin_lock_irqsave(&gus->reg_lock, flags);
+ if (gus->gf1.enh_mode) {
+ address_high = ((address >> 16) & 0x000000f0) | (address & 0x0000000f);
+ snd_gf1_write16(gus, SNDRV_GF1_GW_DRAM_DMA_LOW, (unsigned short) (address >> 4));
+ snd_gf1_write8(gus, SNDRV_GF1_GB_DRAM_DMA_HIGH, (unsigned char) address_high);
+ } else
+ snd_gf1_write16(gus, SNDRV_GF1_GW_DRAM_DMA_LOW, (unsigned short) (address >> 4));
+ snd_gf1_write8(gus, SNDRV_GF1_GB_DRAM_DMA_CONTROL, dma_cmd);
+ spin_unlock_irqrestore(&gus->reg_lock, flags);
+}
+
+static snd_gf1_dma_block_t *snd_gf1_dma_next_block(snd_gus_card_t * gus)
+{
+ snd_gf1_dma_block_t *block;
+
+ /* PCM block have bigger priority than synthesizer one */
+ if (gus->gf1.dma_data_pcm) {
+ block = gus->gf1.dma_data_pcm;
+ if (gus->gf1.dma_data_pcm_last == block) {
+ gus->gf1.dma_data_pcm =
+ gus->gf1.dma_data_pcm_last = NULL;
+ } else {
+ gus->gf1.dma_data_pcm = block->next;
+ }
+ } else if (gus->gf1.dma_data_synth) {
+ block = gus->gf1.dma_data_synth;
+ if (gus->gf1.dma_data_synth_last == block) {
+ gus->gf1.dma_data_synth =
+ gus->gf1.dma_data_synth_last = NULL;
+ } else {
+ gus->gf1.dma_data_synth = block->next;
+ }
+ } else {
+ block = NULL;
+ }
+ if (block) {
+ gus->gf1.dma_ack = block->ack;
+ gus->gf1.dma_private_data = block->private_data;
+ }
+ return block;
+}
+
+
+static void snd_gf1_dma_interrupt(snd_gus_card_t * gus)
+{
+ snd_gf1_dma_block_t *block;
+
+ snd_gf1_dma_ack(gus);
+ if (gus->gf1.dma_ack)
+ gus->gf1.dma_ack(gus, gus->gf1.dma_private_data);
+ spin_lock(&gus->dma_lock);
+ if (gus->gf1.dma_data_pcm == NULL &&
+ gus->gf1.dma_data_synth == NULL) {
+ gus->gf1.dma_ack = NULL;
+ gus->gf1.dma_flags &= ~SNDRV_GF1_DMA_TRIGGER;
+ spin_unlock(&gus->dma_lock);
+ return;
+ }
+ block = snd_gf1_dma_next_block(gus);
+ spin_unlock(&gus->dma_lock);
+ snd_gf1_dma_program(gus, block->addr, block->buf_addr, block->count, (unsigned short) block->cmd);
+ kfree(block);
+#if 0
+ printk("program dma (IRQ) - addr = 0x%x, buffer = 0x%lx, count = 0x%x, cmd = 0x%x\n", addr, (long) buffer, count, cmd);
+#endif
+}
+
+int snd_gf1_dma_init(snd_gus_card_t * gus)
+{
+ down(&gus->dma_mutex);
+ gus->gf1.dma_shared++;
+ if (gus->gf1.dma_shared > 1) {
+ up(&gus->dma_mutex);
+ return 0;
+ }
+ gus->gf1.interrupt_handler_dma_write = snd_gf1_dma_interrupt;
+ gus->gf1.dma_data_pcm =
+ gus->gf1.dma_data_pcm_last =
+ gus->gf1.dma_data_synth =
+ gus->gf1.dma_data_synth_last = NULL;
+ up(&gus->dma_mutex);
+ return 0;
+}
+
+int snd_gf1_dma_done(snd_gus_card_t * gus)
+{
+ snd_gf1_dma_block_t *block;
+
+ down(&gus->dma_mutex);
+ gus->gf1.dma_shared--;
+ if (!gus->gf1.dma_shared) {
+ snd_dma_disable(gus->gf1.dma1);
+ snd_gf1_set_default_handlers(gus, SNDRV_GF1_HANDLER_DMA_WRITE);
+ snd_gf1_dma_ack(gus);
+ while ((block = gus->gf1.dma_data_pcm)) {
+ gus->gf1.dma_data_pcm = block->next;
+ kfree(block);
+ }
+ while ((block = gus->gf1.dma_data_synth)) {
+ gus->gf1.dma_data_synth = block->next;
+ kfree(block);
+ }
+ gus->gf1.dma_data_pcm_last =
+ gus->gf1.dma_data_synth_last = NULL;
+ }
+ up(&gus->dma_mutex);
+ return 0;
+}
+
+int snd_gf1_dma_transfer_block(snd_gus_card_t * gus,
+ snd_gf1_dma_block_t * __block,
+ int atomic,
+ int synth)
+{
+ unsigned long flags;
+ snd_gf1_dma_block_t *block;
+
+ block = kmalloc(sizeof(*block), atomic ? GFP_ATOMIC : GFP_KERNEL);
+ if (block == NULL) {
+ snd_printk("gf1: DMA transfer failure; not enough memory\n");
+ return -ENOMEM;
+ }
+ *block = *__block;
+ block->next = NULL;
+#if 0
+ printk("addr = 0x%x, buffer = 0x%lx, count = 0x%x, cmd = 0x%x\n", block->addr, (long) block->buffer, block->count, block->cmd);
+#endif
+#if 0
+ printk("gus->gf1.dma_data_pcm_last = 0x%lx\n", (long)gus->gf1.dma_data_pcm_last);
+ printk("gus->gf1.dma_data_pcm = 0x%lx\n", (long)gus->gf1.dma_data_pcm);
+#endif
+ spin_lock_irqsave(&gus->dma_lock, flags);
+ if (synth) {
+ if (gus->gf1.dma_data_synth_last) {
+ gus->gf1.dma_data_synth_last->next = block;
+ gus->gf1.dma_data_synth_last = block;
+ } else {
+ gus->gf1.dma_data_synth =
+ gus->gf1.dma_data_synth_last = block;
+ }
+ } else {
+ if (gus->gf1.dma_data_pcm_last) {
+ gus->gf1.dma_data_pcm_last->next = block;
+ gus->gf1.dma_data_pcm_last = block;
+ } else {
+ gus->gf1.dma_data_pcm =
+ gus->gf1.dma_data_pcm_last = block;
+ }
+ }
+ if (!(gus->gf1.dma_flags & SNDRV_GF1_DMA_TRIGGER)) {
+ gus->gf1.dma_flags |= SNDRV_GF1_DMA_TRIGGER;
+ block = snd_gf1_dma_next_block(gus);
+ spin_unlock_irqrestore(&gus->dma_lock, flags);
+ if (block == NULL)
+ return 0;
+ snd_gf1_dma_program(gus, block->addr, block->buf_addr, block->count, (unsigned short) block->cmd);
+ kfree(block);
+ return 0;
+ }
+ spin_unlock_irqrestore(&gus->dma_lock, flags);
+ return 0;
+}
diff --git a/sound/isa/gus/gus_dram.c b/sound/isa/gus/gus_dram.c
new file mode 100644
index 00000000000..22120b868b5
--- /dev/null
+++ b/sound/isa/gus/gus_dram.c
@@ -0,0 +1,103 @@
+/*
+ * Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ * DRAM access routines
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <sound/driver.h>
+#include <linux/time.h>
+#include <sound/core.h>
+#include <sound/gus.h>
+#include <sound/info.h>
+
+
+static int snd_gus_dram_poke(snd_gus_card_t *gus, char __user *_buffer,
+ unsigned int address, unsigned int size)
+{
+ unsigned long flags;
+ unsigned int size1, size2;
+ char buffer[256], *pbuffer;
+
+ while (size > 0) {
+ size1 = size > sizeof(buffer) ? sizeof(buffer) : size;
+ if (copy_from_user(buffer, _buffer, size1))
+ return -EFAULT;
+ if (gus->interwave) {
+ spin_lock_irqsave(&gus->reg_lock, flags);
+ snd_gf1_write8(gus, SNDRV_GF1_GB_MEMORY_CONTROL, 0x01);
+ snd_gf1_dram_addr(gus, address);
+ outsb(GUSP(gus, DRAM), buffer, size1);
+ spin_unlock_irqrestore(&gus->reg_lock, flags);
+ address += size1;
+ } else {
+ pbuffer = buffer;
+ size2 = size1;
+ while (size2--)
+ snd_gf1_poke(gus, address++, *pbuffer++);
+ }
+ size -= size1;
+ _buffer += size1;
+ }
+ return 0;
+}
+
+
+int snd_gus_dram_write(snd_gus_card_t *gus, char __user *buffer,
+ unsigned int address, unsigned int size)
+{
+ return snd_gus_dram_poke(gus, buffer, address, size);
+}
+
+static int snd_gus_dram_peek(snd_gus_card_t *gus, char __user *_buffer,
+ unsigned int address, unsigned int size,
+ int rom)
+{
+ unsigned long flags;
+ unsigned int size1, size2;
+ char buffer[256], *pbuffer;
+
+ while (size > 0) {
+ size1 = size > sizeof(buffer) ? sizeof(buffer) : size;
+ if (gus->interwave) {
+ spin_lock_irqsave(&gus->reg_lock, flags);
+ snd_gf1_write8(gus, SNDRV_GF1_GB_MEMORY_CONTROL, rom ? 0x03 : 0x01);
+ snd_gf1_dram_addr(gus, address);
+ insb(GUSP(gus, DRAM), buffer, size1);
+ snd_gf1_write8(gus, SNDRV_GF1_GB_MEMORY_CONTROL, 0x01);
+ spin_unlock_irqrestore(&gus->reg_lock, flags);
+ address += size1;
+ } else {
+ pbuffer = buffer;
+ size2 = size1;
+ while (size2--)
+ *pbuffer++ = snd_gf1_peek(gus, address++);
+ }
+ if (copy_to_user(_buffer, buffer, size1))
+ return -EFAULT;
+ size -= size1;
+ _buffer += size1;
+ }
+ return 0;
+}
+
+int snd_gus_dram_read(snd_gus_card_t *gus, char __user *buffer,
+ unsigned int address, unsigned int size,
+ int rom)
+{
+ return snd_gus_dram_peek(gus, buffer, address, size, rom);
+}
diff --git a/sound/isa/gus/gus_instr.c b/sound/isa/gus/gus_instr.c
new file mode 100644
index 00000000000..591a9a17feb
--- /dev/null
+++ b/sound/isa/gus/gus_instr.c
@@ -0,0 +1,173 @@
+/*
+ * Routines for Gravis UltraSound soundcards - Synthesizer
+ * Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <sound/driver.h>
+#include <linux/time.h>
+#include <sound/core.h>
+#include <sound/gus.h>
+
+/*
+ *
+ */
+
+int snd_gus_iwffff_put_sample(void *private_data, iwffff_wave_t *wave,
+ char __user *data, long len, int atomic)
+{
+ snd_gus_card_t *gus = private_data;
+ snd_gf1_mem_block_t *block;
+ int err;
+
+ if (wave->format & IWFFFF_WAVE_ROM)
+ return 0; /* it's probably ok - verify the address? */
+ if (wave->format & IWFFFF_WAVE_STEREO)
+ return -EINVAL; /* not supported */
+ block = snd_gf1_mem_alloc(&gus->gf1.mem_alloc,
+ SNDRV_GF1_MEM_OWNER_WAVE_IWFFFF,
+ NULL, wave->size,
+ wave->format & IWFFFF_WAVE_16BIT, 1,
+ wave->share_id);
+ if (block == NULL)
+ return -ENOMEM;
+ err = snd_gus_dram_write(gus, data,
+ block->ptr, wave->size);
+ if (err < 0) {
+ snd_gf1_mem_lock(&gus->gf1.mem_alloc, 0);
+ snd_gf1_mem_xfree(&gus->gf1.mem_alloc, block);
+ snd_gf1_mem_lock(&gus->gf1.mem_alloc, 1);
+ return err;
+ }
+ wave->address.memory = block->ptr;
+ return 0;
+}
+
+int snd_gus_iwffff_get_sample(void *private_data, iwffff_wave_t *wave,
+ char __user *data, long len, int atomic)
+{
+ snd_gus_card_t *gus = private_data;
+
+ return snd_gus_dram_read(gus, data, wave->address.memory, wave->size,
+ wave->format & IWFFFF_WAVE_ROM ? 1 : 0);
+}
+
+int snd_gus_iwffff_remove_sample(void *private_data, iwffff_wave_t *wave,
+ int atomic)
+{
+ snd_gus_card_t *gus = private_data;
+
+ if (wave->format & IWFFFF_WAVE_ROM)
+ return 0; /* it's probably ok - verify the address? */
+ return snd_gf1_mem_free(&gus->gf1.mem_alloc, wave->address.memory);
+}
+
+/*
+ *
+ */
+
+int snd_gus_gf1_put_sample(void *private_data, gf1_wave_t *wave,
+ char __user *data, long len, int atomic)
+{
+ snd_gus_card_t *gus = private_data;
+ snd_gf1_mem_block_t *block;
+ int err;
+
+ if (wave->format & GF1_WAVE_STEREO)
+ return -EINVAL; /* not supported */
+ block = snd_gf1_mem_alloc(&gus->gf1.mem_alloc,
+ SNDRV_GF1_MEM_OWNER_WAVE_GF1,
+ NULL, wave->size,
+ wave->format & GF1_WAVE_16BIT, 1,
+ wave->share_id);
+ if (block == NULL)
+ return -ENOMEM;
+ err = snd_gus_dram_write(gus, data,
+ block->ptr, wave->size);
+ if (err < 0) {
+ snd_gf1_mem_lock(&gus->gf1.mem_alloc, 0);
+ snd_gf1_mem_xfree(&gus->gf1.mem_alloc, block);
+ snd_gf1_mem_lock(&gus->gf1.mem_alloc, 1);
+ return err;
+ }
+ wave->address.memory = block->ptr;
+ return 0;
+}
+
+int snd_gus_gf1_get_sample(void *private_data, gf1_wave_t *wave,
+ char __user *data, long len, int atomic)
+{
+ snd_gus_card_t *gus = private_data;
+
+ return snd_gus_dram_read(gus, data, wave->address.memory, wave->size, 0);
+}
+
+int snd_gus_gf1_remove_sample(void *private_data, gf1_wave_t *wave,
+ int atomic)
+{
+ snd_gus_card_t *gus = private_data;
+
+ return snd_gf1_mem_free(&gus->gf1.mem_alloc, wave->address.memory);
+}
+
+/*
+ *
+ */
+
+int snd_gus_simple_put_sample(void *private_data, simple_instrument_t *instr,
+ char __user *data, long len, int atomic)
+{
+ snd_gus_card_t *gus = private_data;
+ snd_gf1_mem_block_t *block;
+ int err;
+
+ if (instr->format & SIMPLE_WAVE_STEREO)
+ return -EINVAL; /* not supported */
+ block = snd_gf1_mem_alloc(&gus->gf1.mem_alloc,
+ SNDRV_GF1_MEM_OWNER_WAVE_SIMPLE,
+ NULL, instr->size,
+ instr->format & SIMPLE_WAVE_16BIT, 1,
+ instr->share_id);
+ if (block == NULL)
+ return -ENOMEM;
+ err = snd_gus_dram_write(gus, data, block->ptr, instr->size);
+ if (err < 0) {
+ snd_gf1_mem_lock(&gus->gf1.mem_alloc, 0);
+ snd_gf1_mem_xfree(&gus->gf1.mem_alloc, block);
+ snd_gf1_mem_lock(&gus->gf1.mem_alloc, 1);
+ return err;
+ }
+ instr->address.memory = block->ptr;
+ return 0;
+}
+
+int snd_gus_simple_get_sample(void *private_data, simple_instrument_t *instr,
+ char __user *data, long len, int atomic)
+{
+ snd_gus_card_t *gus = private_data;
+
+ return snd_gus_dram_read(gus, data, instr->address.memory, instr->size, 0);
+}
+
+int snd_gus_simple_remove_sample(void *private_data, simple_instrument_t *instr,
+ int atomic)
+{
+ snd_gus_card_t *gus = private_data;
+
+ return snd_gf1_mem_free(&gus->gf1.mem_alloc, instr->address.memory);
+}
diff --git a/sound/isa/gus/gus_io.c b/sound/isa/gus/gus_io.c
new file mode 100644
index 00000000000..f0570f2bf75
--- /dev/null
+++ b/sound/isa/gus/gus_io.c
@@ -0,0 +1,531 @@
+/*
+ * Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ * I/O routines for GF1/InterWave synthesizer chips
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <sound/driver.h>
+#include <linux/delay.h>
+#include <linux/time.h>
+#include <sound/core.h>
+#include <sound/gus.h>
+
+void snd_gf1_delay(snd_gus_card_t * gus)
+{
+ int i;
+
+ for (i = 0; i < 6; i++) {
+ mb();
+ inb(GUSP(gus, DRAM));
+ }
+}
+
+/*
+ * =======================================================================
+ */
+
+/*
+ * ok.. stop of control registers (wave & ramp) need some special things..
+ * big UltraClick (tm) elimination...
+ */
+
+static inline void __snd_gf1_ctrl_stop(snd_gus_card_t * gus, unsigned char reg)
+{
+ unsigned char value;
+
+ outb(reg | 0x80, gus->gf1.reg_regsel);
+ mb();
+ value = inb(gus->gf1.reg_data8);
+ mb();
+ outb(reg, gus->gf1.reg_regsel);
+ mb();
+ outb((value | 0x03) & ~(0x80 | 0x20), gus->gf1.reg_data8);
+ mb();
+}
+
+static inline void __snd_gf1_write8(snd_gus_card_t * gus,
+ unsigned char reg,
+ unsigned char data)
+{
+ outb(reg, gus->gf1.reg_regsel);
+ mb();
+ outb(data, gus->gf1.reg_data8);
+ mb();
+}
+
+static inline unsigned char __snd_gf1_look8(snd_gus_card_t * gus,
+ unsigned char reg)
+{
+ outb(reg, gus->gf1.reg_regsel);
+ mb();
+ return inb(gus->gf1.reg_data8);
+}
+
+static inline void __snd_gf1_write16(snd_gus_card_t * gus,
+ unsigned char reg, unsigned int data)
+{
+ outb(reg, gus->gf1.reg_regsel);
+ mb();
+ outw((unsigned short) data, gus->gf1.reg_data16);
+ mb();
+}
+
+static inline unsigned short __snd_gf1_look16(snd_gus_card_t * gus,
+ unsigned char reg)
+{
+ outb(reg, gus->gf1.reg_regsel);
+ mb();
+ return inw(gus->gf1.reg_data16);
+}
+
+static inline void __snd_gf1_adlib_write(snd_gus_card_t * gus,
+ unsigned char reg, unsigned char data)
+{
+ outb(reg, gus->gf1.reg_timerctrl);
+ inb(gus->gf1.reg_timerctrl);
+ inb(gus->gf1.reg_timerctrl);
+ outb(data, gus->gf1.reg_timerdata);
+ inb(gus->gf1.reg_timerctrl);
+ inb(gus->gf1.reg_timerctrl);
+}
+
+static inline void __snd_gf1_write_addr(snd_gus_card_t * gus, unsigned char reg,
+ unsigned int addr, int w_16bit)
+{
+ if (gus->gf1.enh_mode) {
+ if (w_16bit)
+ addr = ((addr >> 1) & ~0x0000000f) | (addr & 0x0000000f);
+ __snd_gf1_write8(gus, SNDRV_GF1_VB_UPPER_ADDRESS, (unsigned char) ((addr >> 26) & 0x03));
+ } else if (w_16bit)
+ addr = (addr & 0x00c0000f) | ((addr & 0x003ffff0) >> 1);
+ __snd_gf1_write16(gus, reg, (unsigned short) (addr >> 11));
+ __snd_gf1_write16(gus, reg + 1, (unsigned short) (addr << 5));
+}
+
+static inline unsigned int __snd_gf1_read_addr(snd_gus_card_t * gus,
+ unsigned char reg, short w_16bit)
+{
+ unsigned int res;
+
+ res = ((unsigned int) __snd_gf1_look16(gus, reg | 0x80) << 11) & 0xfff800;
+ res |= ((unsigned int) __snd_gf1_look16(gus, (reg + 1) | 0x80) >> 5) & 0x0007ff;
+ if (gus->gf1.enh_mode) {
+ res |= (unsigned int) __snd_gf1_look8(gus, SNDRV_GF1_VB_UPPER_ADDRESS | 0x80) << 26;
+ if (w_16bit)
+ res = ((res << 1) & 0xffffffe0) | (res & 0x0000000f);
+ } else if (w_16bit)
+ res = ((res & 0x001ffff0) << 1) | (res & 0x00c0000f);
+ return res;
+}
+
+
+/*
+ * =======================================================================
+ */
+
+void snd_gf1_ctrl_stop(snd_gus_card_t * gus, unsigned char reg)
+{
+ __snd_gf1_ctrl_stop(gus, reg);
+}
+
+void snd_gf1_write8(snd_gus_card_t * gus,
+ unsigned char reg,
+ unsigned char data)
+{
+ __snd_gf1_write8(gus, reg, data);
+}
+
+unsigned char snd_gf1_look8(snd_gus_card_t * gus, unsigned char reg)
+{
+ return __snd_gf1_look8(gus, reg);
+}
+
+void snd_gf1_write16(snd_gus_card_t * gus,
+ unsigned char reg,
+ unsigned int data)
+{
+ __snd_gf1_write16(gus, reg, data);
+}
+
+unsigned short snd_gf1_look16(snd_gus_card_t * gus, unsigned char reg)
+{
+ return __snd_gf1_look16(gus, reg);
+}
+
+void snd_gf1_adlib_write(snd_gus_card_t * gus,
+ unsigned char reg,
+ unsigned char data)
+{
+ __snd_gf1_adlib_write(gus, reg, data);
+}
+
+void snd_gf1_write_addr(snd_gus_card_t * gus, unsigned char reg,
+ unsigned int addr, short w_16bit)
+{
+ __snd_gf1_write_addr(gus, reg, addr, w_16bit);
+}
+
+unsigned int snd_gf1_read_addr(snd_gus_card_t * gus,
+ unsigned char reg,
+ short w_16bit)
+{
+ return __snd_gf1_read_addr(gus, reg, w_16bit);
+}
+
+/*
+
+ */
+
+void snd_gf1_i_ctrl_stop(snd_gus_card_t * gus, unsigned char reg)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&gus->reg_lock, flags);
+ __snd_gf1_ctrl_stop(gus, reg);
+ spin_unlock_irqrestore(&gus->reg_lock, flags);
+}
+
+void snd_gf1_i_write8(snd_gus_card_t * gus,
+ unsigned char reg,
+ unsigned char data)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&gus->reg_lock, flags);
+ __snd_gf1_write8(gus, reg, data);
+ spin_unlock_irqrestore(&gus->reg_lock, flags);
+}
+
+unsigned char snd_gf1_i_look8(snd_gus_card_t * gus, unsigned char reg)
+{
+ unsigned long flags;
+ unsigned char res;
+
+ spin_lock_irqsave(&gus->reg_lock, flags);
+ res = __snd_gf1_look8(gus, reg);
+ spin_unlock_irqrestore(&gus->reg_lock, flags);
+ return res;
+}
+
+void snd_gf1_i_write16(snd_gus_card_t * gus,
+ unsigned char reg,
+ unsigned int data)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&gus->reg_lock, flags);
+ __snd_gf1_write16(gus, reg, data);
+ spin_unlock_irqrestore(&gus->reg_lock, flags);
+}
+
+unsigned short snd_gf1_i_look16(snd_gus_card_t * gus, unsigned char reg)
+{
+ unsigned long flags;
+ unsigned short res;
+
+ spin_lock_irqsave(&gus->reg_lock, flags);
+ res = __snd_gf1_look16(gus, reg);
+ spin_unlock_irqrestore(&gus->reg_lock, flags);
+ return res;
+}
+
+void snd_gf1_i_adlib_write(snd_gus_card_t * gus,
+ unsigned char reg,
+ unsigned char data)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&gus->reg_lock, flags);
+ __snd_gf1_adlib_write(gus, reg, data);
+ spin_unlock_irqrestore(&gus->reg_lock, flags);
+}
+
+void snd_gf1_i_write_addr(snd_gus_card_t * gus, unsigned char reg,
+ unsigned int addr, short w_16bit)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&gus->reg_lock, flags);
+ __snd_gf1_write_addr(gus, reg, addr, w_16bit);
+ spin_unlock_irqrestore(&gus->reg_lock, flags);
+}
+
+unsigned int snd_gf1_i_read_addr(snd_gus_card_t * gus,
+ unsigned char reg, short w_16bit)
+{
+ unsigned int res;
+ unsigned long flags;
+
+ spin_lock_irqsave(&gus->reg_lock, flags);
+ res = __snd_gf1_read_addr(gus, reg, w_16bit);
+ spin_unlock_irqrestore(&gus->reg_lock, flags);
+ return res;
+}
+
+/*
+
+ */
+
+void snd_gf1_dram_addr(snd_gus_card_t * gus, unsigned int addr)
+{
+ outb(0x43, gus->gf1.reg_regsel);
+ mb();
+ outw((unsigned short) addr, gus->gf1.reg_data16);
+ mb();
+ outb(0x44, gus->gf1.reg_regsel);
+ mb();
+ outb((unsigned char) (addr >> 16), gus->gf1.reg_data8);
+ mb();
+}
+
+void snd_gf1_poke(snd_gus_card_t * gus, unsigned int addr, unsigned char data)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&gus->reg_lock, flags);
+ outb(SNDRV_GF1_GW_DRAM_IO_LOW, gus->gf1.reg_regsel);
+ mb();
+ outw((unsigned short) addr, gus->gf1.reg_data16);
+ mb();
+ outb(SNDRV_GF1_GB_DRAM_IO_HIGH, gus->gf1.reg_regsel);
+ mb();
+ outb((unsigned char) (addr >> 16), gus->gf1.reg_data8);
+ mb();
+ outb(data, gus->gf1.reg_dram);
+ spin_unlock_irqrestore(&gus->reg_lock, flags);
+}
+
+unsigned char snd_gf1_peek(snd_gus_card_t * gus, unsigned int addr)
+{
+ unsigned long flags;
+ unsigned char res;
+
+ spin_lock_irqsave(&gus->reg_lock, flags);
+ outb(SNDRV_GF1_GW_DRAM_IO_LOW, gus->gf1.reg_regsel);
+ mb();
+ outw((unsigned short) addr, gus->gf1.reg_data16);
+ mb();
+ outb(SNDRV_GF1_GB_DRAM_IO_HIGH, gus->gf1.reg_regsel);
+ mb();
+ outb((unsigned char) (addr >> 16), gus->gf1.reg_data8);
+ mb();
+ res = inb(gus->gf1.reg_dram);
+ spin_unlock_irqrestore(&gus->reg_lock, flags);
+ return res;
+}
+
+void snd_gf1_pokew(snd_gus_card_t * gus, unsigned int addr, unsigned short data)
+{
+ unsigned long flags;
+
+#ifdef CONFIG_SND_DEBUG
+ if (!gus->interwave)
+ snd_printk("snd_gf1_pokew - GF1!!!\n");
+#endif
+ spin_lock_irqsave(&gus->reg_lock, flags);
+ outb(SNDRV_GF1_GW_DRAM_IO_LOW, gus->gf1.reg_regsel);
+ mb();
+ outw((unsigned short) addr, gus->gf1.reg_data16);
+ mb();
+ outb(SNDRV_GF1_GB_DRAM_IO_HIGH, gus->gf1.reg_regsel);
+ mb();
+ outb((unsigned char) (addr >> 16), gus->gf1.reg_data8);
+ mb();
+ outb(SNDRV_GF1_GW_DRAM_IO16, gus->gf1.reg_regsel);
+ mb();
+ outw(data, gus->gf1.reg_data16);
+ spin_unlock_irqrestore(&