diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /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/Makefile | 36 | ||||
-rw-r--r-- | sound/isa/gus/gus_dma.c | 244 | ||||
-rw-r--r-- | sound/isa/gus/gus_dram.c | 103 | ||||
-rw-r--r-- | sound/isa/gus/gus_instr.c | 173 | ||||
-rw-r--r-- | sound/isa/gus/gus_io.c | 531 | ||||
-rw-r--r-- | sound/isa/gus/gus_irq.c | 142 | ||||
-rw-r--r-- | sound/isa/gus/gus_main.c | 514 | ||||
-rw-r--r-- | sound/isa/gus/gus_mem.c | 353 | ||||
-rw-r--r-- | sound/isa/gus/gus_mem_proc.c | 135 | ||||
-rw-r--r-- | sound/isa/gus/gus_mixer.c | 199 | ||||
-rw-r--r-- | sound/isa/gus/gus_pcm.c | 903 | ||||
-rw-r--r-- | sound/isa/gus/gus_reset.c | 413 | ||||
-rw-r--r-- | sound/isa/gus/gus_sample.c | 155 | ||||
-rw-r--r-- | sound/isa/gus/gus_simple.c | 634 | ||||
-rw-r--r-- | sound/isa/gus/gus_synth.c | 329 | ||||
-rw-r--r-- | sound/isa/gus/gus_tables.h | 86 | ||||
-rw-r--r-- | sound/isa/gus/gus_timer.c | 204 | ||||
-rw-r--r-- | sound/isa/gus/gus_uart.c | 257 | ||||
-rw-r--r-- | sound/isa/gus/gus_volume.c | 210 | ||||
-rw-r--r-- | sound/isa/gus/gusclassic.c | 260 | ||||
-rw-r--r-- | sound/isa/gus/gusextreme.c | 374 | ||||
-rw-r--r-- | sound/isa/gus/gusmax.c | 400 | ||||
-rw-r--r-- | sound/isa/gus/interwave-stb.c | 2 | ||||
-rw-r--r-- | sound/isa/gus/interwave.c | 969 |
24 files changed, 7626 insertions, 0 deletions
diff --git a/sound/isa/gus/Makefile b/sound/isa/gus/Makefile new file mode 100644 index 000000000000..bae5dbd6c8e5 --- /dev/null +++ b/sound/isa/gus/Makefile | |||
@@ -0,0 +1,36 @@ | |||
1 | # | ||
2 | # Makefile for ALSA | ||
3 | # Copyright (c) 2001 by Jaroslav Kysela <perex@suse.cz> | ||
4 | # | ||
5 | |||
6 | snd-gus-lib-objs := gus_main.o \ | ||
7 | gus_io.o gus_irq.o gus_timer.o \ | ||
8 | gus_mem.o gus_mem_proc.o gus_dram.o gus_dma.o gus_volume.o \ | ||
9 | gus_pcm.o gus_mixer.o \ | ||
10 | gus_uart.o \ | ||
11 | gus_reset.o | ||
12 | snd-gus-synth-objs := gus_synth.o gus_sample.o gus_simple.o gus_instr.o | ||
13 | |||
14 | snd-gusclassic-objs := gusclassic.o | ||
15 | snd-gusextreme-objs := gusextreme.o | ||
16 | snd-gusmax-objs := gusmax.o | ||
17 | snd-interwave-objs := interwave.o | ||
18 | snd-interwave-stb-objs := interwave-stb.o | ||
19 | |||
20 | # | ||
21 | # this function returns: | ||
22 | # "m" - CONFIG_SND_SEQUENCER is m | ||
23 | # <empty string> - CONFIG_SND_SEQUENCER is undefined | ||
24 | # otherwise parameter #1 value | ||
25 | # | ||
26 | sequencer = $(if $(subst y,,$(CONFIG_SND_SEQUENCER)),$(if $(1),m),$(if $(CONFIG_SND_SEQUENCER),$(1))) | ||
27 | |||
28 | # Toplevel Module Dependency | ||
29 | obj-$(CONFIG_SND_GUSCLASSIC) += snd-gusclassic.o snd-gus-lib.o | ||
30 | obj-$(CONFIG_SND_GUSMAX) += snd-gusmax.o snd-gus-lib.o | ||
31 | obj-$(CONFIG_SND_GUSEXTREME) += snd-gusextreme.o snd-gus-lib.o | ||
32 | obj-$(CONFIG_SND_INTERWAVE) += snd-interwave.o snd-gus-lib.o | ||
33 | obj-$(CONFIG_SND_INTERWAVE_STB) += snd-interwave-stb.o snd-gus-lib.o | ||
34 | obj-$(call sequencer,$(CONFIG_SND_GUS_SYNTH)) += snd-gus-synth.o | ||
35 | |||
36 | 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 000000000000..de4b56d80b35 --- /dev/null +++ b/sound/isa/gus/gus_dma.c | |||
@@ -0,0 +1,244 @@ | |||
1 | /* | ||
2 | * Routines for GF1 DMA control | ||
3 | * Copyright (c) by Jaroslav Kysela <perex@suse.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 <asm/dma.h> | ||
24 | #include <linux/slab.h> | ||
25 | #include <sound/core.h> | ||
26 | #include <sound/gus.h> | ||
27 | |||
28 | static void snd_gf1_dma_ack(snd_gus_card_t * gus) | ||
29 | { | ||
30 | unsigned long flags; | ||
31 | |||
32 | spin_lock_irqsave(&gus->reg_lock, flags); | ||
33 | snd_gf1_write8(gus, SNDRV_GF1_GB_DRAM_DMA_CONTROL, 0x00); | ||
34 | snd_gf1_look8(gus, SNDRV_GF1_GB_DRAM_DMA_CONTROL); | ||
35 | spin_unlock_irqrestore(&gus->reg_lock, flags); | ||
36 | } | ||
37 | |||
38 | static void snd_gf1_dma_program(snd_gus_card_t * gus, | ||
39 | unsigned int addr, | ||
40 | unsigned long buf_addr, | ||
41 | unsigned int count, | ||
42 | unsigned int cmd) | ||
43 | { | ||
44 | unsigned long flags; | ||
45 | unsigned int address; | ||
46 | unsigned char dma_cmd; | ||
47 | unsigned int address_high; | ||
48 | |||
49 | // snd_printk("dma_transfer: addr=0x%x, buf=0x%lx, count=0x%x\n", addr, (long) buf, count); | ||
50 | |||
51 | if (gus->gf1.dma1 > 3) { | ||
52 | if (gus->gf1.enh_mode) { | ||
53 | address = addr >> 1; | ||
54 | } else { | ||
55 | if (addr & 0x1f) { | ||
56 | snd_printd("snd_gf1_dma_transfer: unaligned address (0x%x)?\n", addr); | ||
57 | return; | ||
58 | } | ||
59 | address = (addr & 0x000c0000) | ((addr & 0x0003ffff) >> 1); | ||
60 | } | ||
61 | } else { | ||
62 | address = addr; | ||
63 | } | ||
64 | |||
65 | dma_cmd = SNDRV_GF1_DMA_ENABLE | (unsigned short) cmd; | ||
66 | #if 0 | ||
67 | dma_cmd |= 0x08; | ||
68 | #endif | ||
69 | if (dma_cmd & SNDRV_GF1_DMA_16BIT) { | ||
70 | count++; | ||
71 | count &= ~1; /* align */ | ||
72 | } | ||
73 | if (gus->gf1.dma1 > 3) { | ||
74 | dma_cmd |= SNDRV_GF1_DMA_WIDTH16; | ||
75 | count++; | ||
76 | count &= ~1; /* align */ | ||
77 | } | ||
78 | snd_gf1_dma_ack(gus); | ||
79 | snd_dma_program(gus->gf1.dma1, buf_addr, count, dma_cmd & SNDRV_GF1_DMA_READ ? DMA_MODE_READ : DMA_MODE_WRITE); | ||
80 | #if 0 | ||
81 | snd_printk("address = 0x%x, count = 0x%x, dma_cmd = 0x%x\n", address << 1, count, dma_cmd); | ||
82 | #endif | ||
83 | spin_lock_irqsave(&gus->reg_lock, flags); | ||
84 | if (gus->gf1.enh_mode) { | ||
85 | address_high = ((address >> 16) & 0x000000f0) | (address & 0x0000000f); | ||
86 | snd_gf1_write16(gus, SNDRV_GF1_GW_DRAM_DMA_LOW, (unsigned short) (address >> 4)); | ||
87 | snd_gf1_write8(gus, SNDRV_GF1_GB_DRAM_DMA_HIGH, (unsigned char) address_high); | ||
88 | } else | ||
89 | snd_gf1_write16(gus, SNDRV_GF1_GW_DRAM_DMA_LOW, (unsigned short) (address >> 4)); | ||
90 | snd_gf1_write8(gus, SNDRV_GF1_GB_DRAM_DMA_CONTROL, dma_cmd); | ||
91 | spin_unlock_irqrestore(&gus->reg_lock, flags); | ||
92 | } | ||
93 | |||
94 | static snd_gf1_dma_block_t *snd_gf1_dma_next_block(snd_gus_card_t * gus) | ||
95 | { | ||
96 | snd_gf1_dma_block_t *block; | ||
97 | |||
98 | /* PCM block have bigger priority than synthesizer one */ | ||
99 | if (gus->gf1.dma_data_pcm) { | ||
100 | block = gus->gf1.dma_data_pcm; | ||
101 | if (gus->gf1.dma_data_pcm_last == block) { | ||
102 | gus->gf1.dma_data_pcm = | ||
103 | gus->gf1.dma_data_pcm_last = NULL; | ||
104 | } else { | ||
105 | gus->gf1.dma_data_pcm = block->next; | ||
106 | } | ||
107 | } else if (gus->gf1.dma_data_synth) { | ||
108 | block = gus->gf1.dma_data_synth; | ||
109 | if (gus->gf1.dma_data_synth_last == block) { | ||
110 | gus->gf1.dma_data_synth = | ||
111 | gus->gf1.dma_data_synth_last = NULL; | ||
112 | } else { | ||
113 | gus->gf1.dma_data_synth = block->next; | ||
114 | } | ||
115 | } else { | ||
116 | block = NULL; | ||
117 | } | ||
118 | if (block) { | ||
119 | gus->gf1.dma_ack = block->ack; | ||
120 | gus->gf1.dma_private_data = block->private_data; | ||
121 | } | ||
122 | return block; | ||
123 | } | ||
124 | |||
125 | |||
126 | static void snd_gf1_dma_interrupt(snd_gus_card_t * gus) | ||
127 | { | ||
128 | snd_gf1_dma_block_t *block; | ||
129 | |||
130 | snd_gf1_dma_ack(gus); | ||
131 | if (gus->gf1.dma_ack) | ||
132 | gus->gf1.dma_ack(gus, gus->gf1.dma_private_data); | ||
133 | spin_lock(&gus->dma_lock); | ||
134 | if (gus->gf1.dma_data_pcm == NULL && | ||
135 | gus->gf1.dma_data_synth == NULL) { | ||
136 | gus->gf1.dma_ack = NULL; | ||
137 | gus->gf1.dma_flags &= ~SNDRV_GF1_DMA_TRIGGER; | ||
138 | spin_unlock(&gus->dma_lock); | ||
139 | return; | ||
140 | } | ||
141 | block = snd_gf1_dma_next_block(gus); | ||
142 | spin_unlock(&gus->dma_lock); | ||
143 | snd_gf1_dma_program(gus, block->addr, block->buf_addr, block->count, (unsigned short) block->cmd); | ||
144 | kfree(block); | ||
145 | #if 0 | ||
146 | printk("program dma (IRQ) - addr = 0x%x, buffer = 0x%lx, count = 0x%x, cmd = 0x%x\n", addr, (long) buffer, count, cmd); | ||
147 | #endif | ||
148 | } | ||
149 | |||
150 | int snd_gf1_dma_init(snd_gus_card_t * gus) | ||
151 | { | ||
152 | down(&gus->dma_mutex); | ||
153 | gus->gf1.dma_shared++; | ||
154 | if (gus->gf1.dma_shared > 1) { | ||
155 | up(&gus->dma_mutex); | ||
156 | return 0; | ||
157 | } | ||
158 | gus->gf1.interrupt_handler_dma_write = snd_gf1_dma_interrupt; | ||
159 | gus->gf1.dma_data_pcm = | ||
160 | gus->gf1.dma_data_pcm_last = | ||
161 | gus->gf1.dma_data_synth = | ||
162 | gus->gf1.dma_data_synth_last = NULL; | ||
163 | up(&gus->dma_mutex); | ||
164 | return 0; | ||
165 | } | ||
166 | |||
167 | int snd_gf1_dma_done(snd_gus_card_t * gus) | ||
168 | { | ||
169 | snd_gf1_dma_block_t *block; | ||
170 | |||
171 | down(&gus->dma_mutex); | ||
172 | gus->gf1.dma_shared--; | ||
173 | if (!gus->gf1.dma_shared) { | ||
174 | snd_dma_disable(gus->gf1.dma1); | ||
175 | snd_gf1_set_default_handlers(gus, SNDRV_GF1_HANDLER_DMA_WRITE); | ||
176 | snd_gf1_dma_ack(gus); | ||
177 | while ((block = gus->gf1.dma_data_pcm)) { | ||
178 | gus->gf1.dma_data_pcm = block->next; | ||
179 | kfree(block); | ||
180 | } | ||
181 | while ((block = gus->gf1.dma_data_synth)) { | ||
182 | gus->gf1.dma_data_synth = block->next; | ||
183 | kfree(block); | ||
184 | } | ||
185 | gus->gf1.dma_data_pcm_last = | ||
186 | gus->gf1.dma_data_synth_last = NULL; | ||
187 | } | ||
188 | up(&gus->dma_mutex); | ||
189 | return 0; | ||
190 | } | ||
191 | |||
192 | int snd_gf1_dma_transfer_block(snd_gus_card_t * gus, | ||
193 | snd_gf1_dma_block_t * __block, | ||
194 | int atomic, | ||
195 | int synth) | ||
196 | { | ||
197 | unsigned long flags; | ||
198 | snd_gf1_dma_block_t *block; | ||
199 | |||
200 | block = kmalloc(sizeof(*block), atomic ? GFP_ATOMIC : GFP_KERNEL); | ||
201 | if (block == NULL) { | ||
202 | snd_printk("gf1: DMA transfer failure; not enough memory\n"); | ||
203 | return -ENOMEM; | ||
204 | } | ||
205 | *block = *__block; | ||
206 | block->next = NULL; | ||
207 | #if 0 | ||
208 | printk("addr = 0x%x, buffer = 0x%lx, count = 0x%x, cmd = 0x%x\n", block->addr, (long) block->buffer, block->count, block->cmd); | ||
209 | #endif | ||
210 | #if 0 | ||
211 | printk("gus->gf1.dma_data_pcm_last = 0x%lx\n", (long)gus->gf1.dma_data_pcm_last); | ||
212 | printk("gus->gf1.dma_data_pcm = 0x%lx\n", (long)gus->gf1.dma_data_pcm); | ||
213 | #endif | ||
214 | spin_lock_irqsave(&gus->dma_lock, flags); | ||
215 | if (synth) { | ||
216 | if (gus->gf1.dma_data_synth_last) { | ||
217 | gus->gf1.dma_data_synth_last->next = block; | ||
218 | gus->gf1.dma_data_synth_last = block; | ||
219 | } else { | ||
220 | gus->gf1.dma_data_synth = | ||
221 | gus->gf1.dma_data_synth_last = block; | ||
222 | } | ||
223 | } else { | ||
224 | if (gus->gf1.dma_data_pcm_last) { | ||
225 | gus->gf1.dma_data_pcm_last->next = block; | ||
226 | gus->gf1.dma_data_pcm_last = block; | ||
227 | } else { | ||
228 | gus->gf1.dma_data_pcm = | ||
229 | gus->gf1.dma_data_pcm_last = block; | ||
230 | } | ||
231 | } | ||
232 | if (!(gus->gf1.dma_flags & SNDRV_GF1_DMA_TRIGGER)) { | ||
233 | gus->gf1.dma_flags |= SNDRV_GF1_DMA_TRIGGER; | ||
234 | block = snd_gf1_dma_next_block(gus); | ||
235 | spin_unlock_irqrestore(&gus->dma_lock, flags); | ||
236 | if (block == NULL) | ||
237 | return 0; | ||
238 | snd_gf1_dma_program(gus, block->addr, block->buf_addr, block->count, (unsigned short) block->cmd); | ||
239 | kfree(block); | ||
240 | return 0; | ||
241 | } | ||
242 | spin_unlock_irqrestore(&gus->dma_lock, flags); | ||
243 | return 0; | ||
244 | } | ||
diff --git a/sound/isa/gus/gus_dram.c b/sound/isa/gus/gus_dram.c new file mode 100644 index 000000000000..22120b868b5c --- /dev/null +++ b/sound/isa/gus/gus_dram.c | |||
@@ -0,0 +1,103 @@ | |||
1 | /* | ||
2 | * Copyright (c) by Jaroslav Kysela <perex@suse.cz> | ||
3 | * DRAM access routines | ||
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 <linux/time.h> | ||
24 | #include <sound/core.h> | ||
25 | #include <sound/gus.h> | ||
26 | #include <sound/info.h> | ||
27 | |||
28 | |||
29 | static int snd_gus_dram_poke(snd_gus_card_t *gus, char __user *_buffer, | ||
30 | unsigned int address, unsigned int size) | ||
31 | { | ||
32 | unsigned long flags; | ||
33 | unsigned int size1, size2; | ||
34 | char buffer[256], *pbuffer; | ||
35 | |||
36 | while (size > 0) { | ||
37 | size1 = size > sizeof(buffer) ? sizeof(buffer) : size; | ||
38 | if (copy_from_user(buffer, _buffer, size1)) | ||
39 | return -EFAULT; | ||
40 | if (gus->interwave) { | ||
41 | spin_lock_irqsave(&gus->reg_lock, flags); | ||
42 | snd_gf1_write8(gus, SNDRV_GF1_GB_MEMORY_CONTROL, 0x01); | ||
43 | snd_gf1_dram_addr(gus, address); | ||
44 | outsb(GUSP(gus, DRAM), buffer, size1); | ||
45 | spin_unlock_irqrestore(&gus->reg_lock, flags); | ||
46 | address += size1; | ||
47 | } else { | ||
48 | pbuffer = buffer; | ||
49 | size2 = size1; | ||
50 | while (size2--) | ||
51 | snd_gf1_poke(gus, address++, *pbuffer++); | ||
52 | } | ||
53 | size -= size1; | ||
54 | _buffer += size1; | ||
55 | } | ||
56 | return 0; | ||
57 | } | ||
58 | |||
59 | |||
60 | int snd_gus_dram_write(snd_gus_card_t *gus, char __user *buffer, | ||
61 | unsigned int address, unsigned int size) | ||
62 | { | ||
63 | return snd_gus_dram_poke(gus, buffer, address, size); | ||
64 | } | ||
65 | |||
66 | static int snd_gus_dram_peek(snd_gus_card_t *gus, char __user *_buffer, | ||
67 | unsigned int address, unsigned int size, | ||
68 | int rom) | ||
69 | { | ||
70 | unsigned long flags; | ||
71 | unsigned int size1, size2; | ||
72 | char buffer[256], *pbuffer; | ||
73 | |||
74 | while (size > 0) { | ||
75 | size1 = size > sizeof(buffer) ? sizeof(buffer) : size; | ||
76 | if (gus->interwave) { | ||
77 | spin_lock_irqsave(&gus->reg_lock, flags); | ||
78 | snd_gf1_write8(gus, SNDRV_GF1_GB_MEMORY_CONTROL, rom ? 0x03 : 0x01); | ||
79 | snd_gf1_dram_addr(gus, address); | ||
80 | insb(GUSP(gus, DRAM), buffer, size1); | ||
81 | snd_gf1_write8(gus, SNDRV_GF1_GB_MEMORY_CONTROL, 0x01); | ||
82 | spin_unlock_irqrestore(&gus->reg_lock, flags); | ||
83 | address += size1; | ||
84 | } else { | ||
85 | pbuffer = buffer; | ||
86 | size2 = size1; | ||
87 | while (size2--) | ||
88 | *pbuffer++ = snd_gf1_peek(gus, address++); | ||
89 | } | ||
90 | if (copy_to_user(_buffer, buffer, size1)) | ||
91 | return -EFAULT; | ||
92 | size -= size1; | ||
93 | _buffer += size1; | ||
94 | } | ||
95 | return 0; | ||
96 | } | ||
97 | |||
98 | int snd_gus_dram_read(snd_gus_card_t *gus, char __user *buffer, | ||
99 | unsigned int address, unsigned int size, | ||
100 | int rom) | ||
101 | { | ||
102 | return snd_gus_dram_peek(gus, buffer, address, size, rom); | ||
103 | } | ||
diff --git a/sound/isa/gus/gus_instr.c b/sound/isa/gus/gus_instr.c new file mode 100644 index 000000000000..591a9a17feb5 --- /dev/null +++ b/sound/isa/gus/gus_instr.c | |||
@@ -0,0 +1,173 @@ | |||
1 | /* | ||
2 | * Routines for Gravis UltraSound soundcards - Synthesizer | ||
3 | * Copyright (c) by Jaroslav Kysela <perex@suse.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 <linux/time.h> | ||
24 | #include <sound/core.h> | ||
25 | #include <sound/gus.h> | ||
26 | |||
27 | /* | ||
28 | * | ||
29 | */ | ||
30 | |||
31 | int snd_gus_iwffff_put_sample(void *private_data, iwffff_wave_t *wave, | ||
32 | char __user *data, long len, int atomic) | ||
33 | { | ||
34 | snd_gus_card_t *gus = private_data; | ||
35 | snd_gf1_mem_block_t *block; | ||
36 | int err; | ||
37 | |||
38 | if (wave->format & IWFFFF_WAVE_ROM) | ||
39 | return 0; /* it's probably ok - verify the address? */ | ||
40 | if (wave->format & IWFFFF_WAVE_STEREO) | ||
41 | return -EINVAL; /* not supported */ | ||
42 | block = snd_gf1_mem_alloc(&gus->gf1.mem_alloc, | ||
43 | SNDRV_GF1_MEM_OWNER_WAVE_IWFFFF, | ||
44 | NULL, wave->size, | ||
45 | wave->format & IWFFFF_WAVE_16BIT, 1, | ||
46 | wave->share_id); | ||
47 | if (block == NULL) | ||
48 | return -ENOMEM; | ||
49 | err = snd_gus_dram_write(gus, data, | ||
50 | block->ptr, wave->size); | ||
51 | if (err < 0) { | ||
52 | snd_gf1_mem_lock(&gus->gf1.mem_alloc, 0); | ||
53 | snd_gf1_mem_xfree(&gus->gf1.mem_alloc, block); | ||
54 | snd_gf1_mem_lock(&gus->gf1.mem_alloc, 1); | ||
55 | return err; | ||
56 | } | ||
57 | wave->address.memory = block->ptr; | ||
58 | return 0; | ||
59 | } | ||
60 | |||
61 | int snd_gus_iwffff_get_sample(void *private_data, iwffff_wave_t *wave, | ||
62 | char __user *data, long len, int atomic) | ||
63 | { | ||
64 | snd_gus_card_t *gus = private_data; | ||
65 | |||
66 | return snd_gus_dram_read(gus, data, wave->address.memory, wave->size, | ||
67 | wave->format & IWFFFF_WAVE_ROM ? 1 : 0); | ||
68 | } | ||
69 | |||
70 | int snd_gus_iwffff_remove_sample(void *private_data, iwffff_wave_t *wave, | ||
71 | int atomic) | ||
72 | { | ||
73 | snd_gus_card_t *gus = private_data; | ||
74 | |||
75 | if (wave->format & IWFFFF_WAVE_ROM) | ||
76 | return 0; /* it's probably ok - verify the address? */ | ||
77 | return snd_gf1_mem_free(&gus->gf1.mem_alloc, wave->address.memory); | ||
78 | } | ||
79 | |||
80 | /* | ||
81 | * | ||
82 | */ | ||
83 | |||
84 | int snd_gus_gf1_put_sample(void *private_data, gf1_wave_t *wave, | ||
85 | char __user *data, long len, int atomic) | ||
86 | { | ||
87 | snd_gus_card_t *gus = private_data; | ||
88 | snd_gf1_mem_block_t *block; | ||
89 | int err; | ||
90 | |||
91 | if (wave->format & GF1_WAVE_STEREO) | ||
92 | return -EINVAL; /* not supported */ | ||
93 | block = snd_gf1_mem_alloc(&gus->gf1.mem_alloc, | ||
94 | SNDRV_GF1_MEM_OWNER_WAVE_GF1, | ||
95 | NULL, wave->size, | ||
96 | wave->format & GF1_WAVE_16BIT, 1, | ||
97 | wave->share_id); | ||
98 | if (block == NULL) | ||
99 | return -ENOMEM; | ||
100 | err = snd_gus_dram_write(gus, data, | ||
101 | block->ptr, wave->size); | ||
102 | if (err < 0) { | ||
103 | snd_gf1_mem_lock(&gus->gf1.mem_alloc, 0); | ||
104 | snd_gf1_mem_xfree(&gus->gf1.mem_alloc, block); | ||
105 | snd_gf1_mem_lock(&gus->gf1.mem_alloc, 1); | ||
106 | return err; | ||
107 | } | ||
108 | wave->address.memory = block->ptr; | ||
109 | return 0; | ||
110 | } | ||
111 | |||
112 | int snd_gus_gf1_get_sample(void *private_data, gf1_wave_t *wave, | ||
113 | char __user *data, long len, int atomic) | ||
114 | { | ||
115 | snd_gus_card_t *gus = private_data; | ||
116 | |||
117 | return snd_gus_dram_read(gus, data, wave->address.memory, wave->size, 0); | ||
118 | } | ||
119 | |||
120 | int snd_gus_gf1_remove_sample(void *private_data, gf1_wave_t *wave, | ||
121 | int atomic) | ||
122 | { | ||
123 | snd_gus_card_t *gus = private_data; | ||
124 | |||
125 | return snd_gf1_mem_free(&gus->gf1.mem_alloc, wave->address.memory); | ||
126 | } | ||
127 | |||
128 | /* | ||
129 | * | ||
130 | */ | ||
131 | |||
132 | int snd_gus_simple_put_sample(void *private_data, simple_instrument_t *instr, | ||
133 | char __user *data, long len, int atomic) | ||
134 | { | ||
135 | snd_gus_card_t *gus = private_data; | ||
136 | snd_gf1_mem_block_t *block; | ||
137 | int err; | ||
138 | |||
139 | if (instr->format & SIMPLE_WAVE_STEREO) | ||
140 | return -EINVAL; /* not supported */ | ||
141 | block = snd_gf1_mem_alloc(&gus->gf1.mem_alloc, | ||
142 | SNDRV_GF1_MEM_OWNER_WAVE_SIMPLE, | ||
143 | NULL, instr->size, | ||
144 | instr->format & SIMPLE_WAVE_16BIT, 1, | ||
145 | instr->share_id); | ||
146 | if (block == NULL) | ||
147 | return -ENOMEM; | ||
148 | err = snd_gus_dram_write(gus, data, block->ptr, instr->size); | ||
149 | if (err < 0) { | ||
150 | snd_gf1_mem_lock(&gus->gf1.mem_alloc, 0); | ||
151 | snd_gf1_mem_xfree(&gus->gf1.mem_alloc, block); | ||
152 | snd_gf1_mem_lock(&gus->gf1.mem_alloc, 1); | ||
153 | return err; | ||
154 | } | ||
155 | instr->address.memory = block->ptr; | ||
156 | return 0; | ||
157 | } | ||
158 | |||
159 | int snd_gus_simple_get_sample(void *private_data, simple_instrument_t *instr, | ||
160 | char __user *data, long len, int atomic) | ||
161 | { | ||
162 | snd_gus_card_t *gus = private_data; | ||
163 | |||
164 | return snd_gus_dram_read(gus, data, instr->address.memory, instr->size, 0); | ||
165 | } | ||
166 | |||
167 | int snd_gus_simple_remove_sample(void *private_data, simple_instrument_t *instr, | ||
168 | int atomic) | ||
169 | { | ||
170 | snd_gus_card_t *gus = private_data; | ||
171 | |||
172 | return snd_gf1_mem_free(&gus->gf1.mem_alloc, instr->address.memory); | ||
173 | } | ||
diff --git a/sound/isa/gus/gus_io.c b/sound/isa/gus/gus_io.c new file mode 100644 index 000000000000..f0570f2bf75f --- /dev/null +++ b/sound/isa/gus/gus_io.c | |||
@@ -0,0 +1,531 @@ | |||
1 | /* | ||
2 | * Copyright (c) by Jaroslav Kysela <perex@suse.cz> | ||
3 | * I/O routines for GF1/InterWave synthesizer chips | ||
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 <linux/delay.h> | ||
24 | #include <linux/time.h> | ||
25 | #include <sound/core.h> | ||
26 | #include <sound/gus.h> | ||
27 | |||
28 | void snd_gf1_delay(snd_gus_card_t * gus) | ||
29 | { | ||
30 | int i; | ||
31 | |||
32 | for (i = 0; i < 6; i++) { | ||
33 | mb(); | ||
34 | inb(GUSP(gus, DRAM)); | ||
35 | } | ||
36 | } | ||
37 | |||
38 | /* | ||
39 | * ======================================================================= | ||
40 | */ | ||
41 | |||
42 | /* | ||
43 | * ok.. stop of control registers (wave & ramp) need some special things.. | ||
44 | * big UltraClick (tm) elimination... | ||
45 | */ | ||
46 | |||
47 | static inline void __snd_gf1_ctrl_stop(snd_gus_card_t * gus, unsigned char reg) | ||
48 | { | ||
49 | unsigned char value; | ||
50 | |||
51 | outb(reg | 0x80, gus->gf1.reg_regsel); | ||
52 | mb(); | ||
53 | value = inb(gus->gf1.reg_data8); | ||
54 | mb(); | ||
55 | outb(reg, gus->gf1.reg_regsel); | ||
56 | mb(); | ||
57 | outb((value | 0x03) & ~(0x80 | 0x20), gus->gf1.reg_data8); | ||
58 | mb(); | ||
59 | } | ||
60 | |||
61 | static inline void __snd_gf1_write8(snd_gus_card_t * gus, | ||
62 | unsigned char reg, | ||
63 | unsigned char data) | ||
64 | { | ||
65 | outb(reg, gus->gf1.reg_regsel); | ||
66 | mb(); | ||
67 | outb(data, gus->gf1.reg_data8); | ||
68 | mb(); | ||
69 | } | ||
70 | |||
71 | static inline unsigned char __snd_gf1_look8(snd_gus_card_t * gus, | ||
72 | unsigned char reg) | ||
73 | { | ||
74 | outb(reg, gus->gf1.reg_regsel); | ||
75 | mb(); | ||
76 | return inb(gus->gf1.reg_data8); | ||
77 | } | ||
78 | |||
79 | static inline void __snd_gf1_write16(snd_gus_card_t * gus, | ||
80 | unsigned char reg, unsigned int data) | ||
81 | { | ||
82 | outb(reg, gus->gf1.reg_regsel); | ||
83 | mb(); | ||
84 | outw((unsigned short) data, gus->gf1.reg_data16); | ||
85 | mb(); | ||
86 | } | ||
87 | |||
88 | static inline unsigned short __snd_gf1_look16(snd_gus_card_t * gus, | ||
89 | unsigned char reg) | ||
90 | { | ||
91 | outb(reg, gus->gf1.reg_regsel); | ||
92 | mb(); | ||
93 | return inw(gus->gf1.reg_data16); | ||
94 | } | ||
95 | |||
96 | static inline void __snd_gf1_adlib_write(snd_gus_card_t * gus, | ||
97 | unsigned char reg, unsigned char data) | ||
98 | { | ||
99 | outb(reg, gus->gf1.reg_timerctrl); | ||
100 | inb(gus->gf1.reg_timerctrl); | ||
101 | inb(gus->gf1.reg_timerctrl); | ||
102 | outb(data, gus->gf1.reg_timerdata); | ||
103 | inb(gus->gf1.reg_timerctrl); | ||
104 | inb(gus->gf1.reg_timerctrl); | ||
105 | } | ||
106 | |||
107 | static inline void __snd_gf1_write_addr(snd_gus_card_t * gus, unsigned char reg, | ||
108 | unsigned int addr, int w_16bit) | ||
109 | { | ||
110 | if (gus->gf1.enh_mode) { | ||
111 | if (w_16bit) | ||
112 | addr = ((addr >> 1) & ~0x0000000f) | (addr & 0x0000000f); | ||
113 | __snd_gf1_write8(gus, SNDRV_GF1_VB_UPPER_ADDRESS, (unsigned char) ((addr >> 26) & 0x03)); | ||
114 | } else if (w_16bit) | ||
115 | addr = (addr & 0x00c0000f) | ((addr & 0x003ffff0) >> 1); | ||
116 | __snd_gf1_write16(gus, reg, (unsigned short) (addr >> 11)); | ||
117 | __snd_gf1_write16(gus, reg + 1, (unsigned short) (addr << 5)); | ||
118 | } | ||
119 | |||
120 | static inline unsigned int __snd_gf1_read_addr(snd_gus_card_t * gus, | ||
121 | unsigned char reg, short w_16bit) | ||
122 | { | ||
123 | unsigned int res; | ||
124 | |||
125 | res = ((unsigned int) __snd_gf1_look16(gus, reg | 0x80) << 11) & 0xfff800; | ||
126 | res |= ((unsigned int) __snd_gf1_look16(gus, (reg + 1) | 0x80) >> 5) & 0x0007ff; | ||
127 | if (gus->gf1.enh_mode) { | ||
128 | res |= (unsigned int) __snd_gf1_look8(gus, SNDRV_GF1_VB_UPPER_ADDRESS | 0x80) << 26; | ||
129 | if (w_16bit) | ||
130 | res = ((res << 1) & 0xffffffe0) | (res & 0x0000000f); | ||
131 | } else if (w_16bit) | ||
132 | res = ((res & 0x001ffff0) << 1) | (res & 0x00c0000f); | ||
133 | return res; | ||
134 | } | ||
135 | |||
136 | |||
137 | /* | ||
138 | * ======================================================================= | ||
139 | */ | ||
140 | |||
141 | void snd_gf1_ctrl_stop(snd_gus_card_t * gus, unsigned char reg) | ||
142 | { | ||
143 | __snd_gf1_ctrl_stop(gus, reg); | ||
144 | } | ||
145 | |||
146 | void snd_gf1_write8(snd_gus_card_t * gus, | ||
147 | unsigned char reg, | ||
148 | unsigned char data) | ||
149 | { | ||
150 | __snd_gf1_write8(gus, reg, data); | ||
151 | } | ||
152 | |||
153 | unsigned char snd_gf1_look8(snd_gus_card_t * gus, unsigned char reg) | ||
154 | { | ||
155 | return __snd_gf1_look8(gus, reg); | ||
156 | } | ||
157 | |||
158 | void snd_gf1_write16(snd_gus_card_t * gus, | ||
159 | unsigned char reg, | ||
160 | unsigned int data) | ||
161 | { | ||
162 | __snd_gf1_write16(gus, reg, data); | ||
163 | } | ||
164 | |||
165 | unsigned short snd_gf1_look16(snd_gus_card_t * gus, unsigned char reg) | ||
166 | { | ||
167 | return __snd_gf1_look16(gus, reg); | ||
168 | } | ||
169 | |||
170 | void snd_gf1_adlib_write(snd_gus_card_t * gus, | ||
171 | unsigned char reg, | ||
172 | unsigned char data) | ||
173 | { | ||
174 | __snd_gf1_adlib_write(gus, reg, data); | ||
175 | } | ||
176 | |||
177 | void snd_gf1_write_addr(snd_gus_card_t * gus, unsigned char reg, | ||
178 | unsigned int addr, short w_16bit) | ||
179 | { | ||
180 | __snd_gf1_write_addr(gus, reg, addr, w_16bit); | ||
181 | } | ||
182 | |||
183 | unsigned int snd_gf1_read_addr(snd_gus_card_t * gus, | ||
184 | unsigned char reg, | ||
185 | short w_16bit) | ||
186 | { | ||
187 | return __snd_gf1_read_addr(gus, reg, w_16bit); | ||
188 | } | ||
189 | |||
190 | /* | ||
191 | |||
192 | */ | ||
193 | |||
194 | void snd_gf1_i_ctrl_stop(snd_gus_card_t * gus, unsigned char reg) | ||
195 | { | ||
196 | unsigned long flags; | ||
197 | |||
198 | spin_lock_irqsave(&gus->reg_lock, flags); | ||
199 | __snd_gf1_ctrl_stop(gus, reg); | ||
200 | spin_unlock_irqrestore(&gus->reg_lock, flags); | ||
201 | } | ||
202 | |||
203 | void snd_gf1_i_write8(snd_gus_card_t * gus, | ||
204 | unsigned char reg, | ||
205 | unsigned char data) | ||
206 | { | ||
207 | unsigned long flags; | ||
208 | |||
209 | spin_lock_irqsave(&gus->reg_lock, flags); | ||
210 | __snd_gf1_write8(gus, reg, data); | ||
211 | spin_unlock_irqrestore(&gus->reg_lock, flags); | ||
212 | } | ||
213 | |||
214 | unsigned char snd_gf1_i_look8(snd_gus_card_t * gus, unsigned char reg) | ||
215 | { | ||
216 | unsigned long flags; | ||
217 | unsigned char res; | ||
218 | |||
219 | spin_lock_irqsave(&gus->reg_lock, flags); | ||
220 | res = __snd_gf1_look8(gus, reg); | ||
221 | spin_unlock_irqrestore(&gus->reg_lock, flags); | ||
222 | return res; | ||
223 | } | ||
224 | |||
225 | void snd_gf1_i_write16(snd_gus_card_t * gus, | ||
226 | unsigned char reg, | ||
227 | unsigned int data) | ||
228 | { | ||
229 | unsigned long flags; | ||
230 | |||
231 | spin_lock_irqsave(&gus->reg_lock, flags); | ||
232 | __snd_gf1_write16(gus, reg, data); | ||
233 | spin_unlock_irqrestore(&gus->reg_lock, flags); | ||
234 | } | ||
235 | |||
236 | unsigned short snd_gf1_i_look16(snd_gus_card_t * gus, unsigned char reg) | ||
237 | { | ||
238 | unsigned long flags; | ||
239 | unsigned short res; | ||
240 | |||
241 | spin_lock_irqsave(&gus->reg_lock, flags); | ||
242 | res = __snd_gf1_look16(gus, reg); | ||
243 | spin_unlock_irqrestore(&gus->reg_lock, flags); | ||
244 | return res; | ||
245 | } | ||
246 | |||
247 | void snd_gf1_i_adlib_write(snd_gus_card_t * gus, | ||
248 | unsigned char reg, | ||
249 | unsigned char data) | ||
250 | { | ||
251 | unsigned long flags; | ||
252 | |||
253 | spin_lock_irqsave(&gus->reg_lock, flags); | ||
254 | __snd_gf1_adlib_write(gus, reg, data); | ||
255 | spin_unlock_irqrestore(&gus->reg_lock, flags); | ||
256 | } | ||
257 | |||
258 | void snd_gf1_i_write_addr(snd_gus_card_t * gus, unsigned char reg, | ||
259 | unsigned int addr, short w_16bit) | ||
260 | { | ||
261 | unsigned long flags; | ||
262 | |||
263 | spin_lock_irqsave(&gus->reg_lock, flags); | ||
264 | __snd_gf1_write_addr(gus, reg, addr, w_16bit); | ||
265 | spin_unlock_irqrestore(&gus->reg_lock, flags); | ||
266 | } | ||
267 | |||
268 | unsigned int snd_gf1_i_read_addr(snd_gus_card_t * gus, | ||
269 | unsigned char reg, short w_16bit) | ||
270 | { | ||
271 | unsigned int res; | ||
272 | unsigned long flags; | ||
273 | |||
274 | spin_lock_irqsave(&gus->reg_lock, flags); | ||
275 | res = __snd_gf1_read_addr(gus, reg, w_16bit); | ||
276 | spin_unlock_irqrestore(&gus->reg_lock, flags); | ||
277 | return res; | ||
278 | } | ||
279 | |||
280 | /* | ||
281 | |||
282 | */ | ||
283 | |||
284 | void snd_gf1_dram_addr(snd_gus_card_t * gus, unsigned int addr) | ||
285 | { | ||
286 | outb(0x43, gus->gf1.reg_regsel); | ||
287 | mb(); | ||
288 | outw((unsigned short) addr, gus->gf1.reg_data16); | ||
289 | mb(); | ||
290 | outb(0x44, gus->gf1.reg_regsel); | ||
291 | mb(); | ||
292 | outb((unsigned char) (addr >> 16), gus->gf1.reg_data8); | ||
293 | mb(); | ||
294 | } | ||
295 | |||
296 | void snd_gf1_poke(snd_gus_card_t * gus, unsigned int addr, unsigned char data) | ||
297 | { | ||
298 | unsigned long flags; | ||
299 | |||
300 | spin_lock_irqsave(&gus->reg_lock, flags); | ||
301 | outb(SNDRV_GF1_GW_DRAM_IO_LOW, gus->gf1.reg_regsel); | ||
302 | mb(); | ||
303 | outw((unsigned short) addr, gus->gf1.reg_data16); | ||
304 | mb(); | ||
305 | outb(SNDRV_GF1_GB_DRAM_IO_HIGH, gus->gf1.reg_regsel); | ||
306 | mb(); | ||
307 | outb((unsigned char) (addr >> 16), gus->gf1.reg_data8); | ||
308 | mb(); | ||
309 | outb(data, gus->gf1.reg_dram); | ||
310 | spin_unlock_irqrestore(&gus->reg_lock, flags); | ||
311 | } | ||
312 | |||
313 | unsigned char snd_gf1_peek(snd_gus_card_t * gus, unsigned int addr) | ||
314 | { | ||
315 | unsigned long flags; | ||
316 | unsigned char res; | ||
317 | |||
318 | spin_lock_irqsave(&gus->reg_lock, flags); | ||
319 | outb(SNDRV_GF1_GW_DRAM_IO_LOW, gus->gf1.reg_regsel); | ||
320 | mb(); | ||
321 | outw((unsigned short) addr, gus->gf1.reg_data16); | ||
322 | mb(); | ||
323 | outb(SNDRV_GF1_GB_DRAM_IO_HIGH, gus->gf1.reg_regsel); | ||
324 | mb(); | ||
325 | outb((unsigned char) (addr >> 16), gus->gf1.reg_data8); | ||
326 | mb(); | ||
327 | res = inb(gus->gf1.reg_dram); | ||
328 | spin_unlock_irqrestore(&gus->reg_lock, flags); | ||
329 | return res; | ||
330 | } | ||
331 | |||
332 | void snd_gf1_pokew(snd_gus_card_t * gus, unsigned int addr, unsigned short data) | ||
333 | { | ||
334 | unsigned long flags; | ||
335 | |||
336 | #ifdef CONFIG_SND_DEBUG | ||
337 | if (!gus->interwave) | ||
338 | snd_printk("snd_gf1_pokew - GF1!!!\n"); | ||
339 | #endif | ||
340 | spin_lock_irqsave(&gus->reg_lock, flags); | ||
341 | outb(SNDRV_GF1_GW_DRAM_IO_LOW, gus->gf1.reg_regsel); | ||
342 | mb(); | ||
343 | outw((unsigned short) addr, gus->gf1.reg_data16); | ||
344 | mb(); | ||
345 | outb(SNDRV_GF1_GB_DRAM_IO_HIGH, gus->gf1.reg_regsel); | ||
346 | mb(); | ||
347 | outb((unsigned char) (addr >> 16), gus->gf1.reg_data8); | ||
348 | mb(); | ||
349 | outb(SNDRV_GF1_GW_DRAM_IO16, gus->gf1.reg_regsel); | ||
350 | mb(); | ||
351 | outw(data, gus->gf1.reg_data16); | ||
352 | spin_unlock_irqrestore(&gus->reg_lock, flags); | ||
353 | } | ||
354 | |||
355 | unsigned short snd_gf1_peekw(snd_gus_card_t * gus, unsigned int addr) | ||
356 | { | ||
357 | unsigned long flags; | ||
358 | unsigned short res; | ||
359 | |||
360 | #ifdef CONFIG_SND_DEBUG | ||
361 | if (!gus->interwave) | ||
362 | snd_printk("snd_gf1_peekw - GF1!!!\n"); | ||
363 | #endif | ||
364 | spin_lock_irqsave(&gus->reg_lock, flags); | ||
365 | outb(SNDRV_GF1_GW_DRAM_IO_LOW, gus->gf1.reg_regsel); | ||
366 | mb(); | ||
367 | outw((unsigned short) addr, gus->gf1.reg_data16); | ||
368 | mb(); | ||
369 | outb(SNDRV_GF1_GB_DRAM_IO_HIGH, gus->gf1.reg_regsel); | ||
370 | mb(); | ||
371 | outb((unsigned char) (addr >> 16), gus->gf1.reg_data8); | ||
372 | mb(); | ||
373 | outb(SNDRV_GF1_GW_DRAM_IO16, gus->gf1.reg_regsel); | ||
374 | mb(); | ||
375 | res = inw(gus->gf1.reg_data16); | ||
376 | spin_unlock_irqrestore(&gus->reg_lock, flags); | ||
377 | return res; | ||
378 | } | ||
379 | |||
380 | void snd_gf1_dram_setmem(snd_gus_card_t * gus, unsigned int addr, | ||
381 | unsigned short value, unsigned int count) | ||
382 | { | ||
383 | unsigned long port; | ||
384 | unsigned long flags; | ||
385 | |||
386 | #ifdef CONFIG_SND_DEBUG | ||
387 | if (!gus->interwave) | ||
388 | snd_printk("snd_gf1_dram_setmem - GF1!!!\n"); | ||
389 | #endif | ||
390 | addr &= ~1; | ||
391 | count >>= 1; | ||
392 | port = GUSP(gus, GF1DATALOW); | ||
393 | spin_lock_irqsave(&gus->reg_lock, flags); | ||
394 | outb(SNDRV_GF1_GW_DRAM_IO_LOW, gus->gf1.reg_regsel); | ||
395 | mb(); | ||
396 | outw((unsigned short) addr, gus->gf1.reg_data16); | ||
397 | mb(); | ||
398 | outb(SNDRV_GF1_GB_DRAM_IO_HIGH, gus->gf1.reg_regsel); | ||
399 | mb(); | ||
400 | outb((unsigned char) (addr >> 16), gus->gf1.reg_data8); | ||
401 | mb(); | ||
402 | outb(SNDRV_GF1_GW_DRAM_IO16, gus->gf1.reg_regsel); | ||
403 | while (count--) | ||
404 | outw(value, port); | ||
405 | spin_unlock_irqrestore(&gus->reg_lock, flags); | ||
406 | } | ||
407 | |||
408 | /* | ||
409 | |||
410 | */ | ||
411 | |||
412 | void snd_gf1_select_active_voices(snd_gus_card_t * gus) | ||
413 | { | ||
414 | unsigned short voices; | ||
415 | |||
416 | static unsigned short voices_tbl[32 - 14 + 1] = | ||
417 | { | ||
418 | 44100, 41160, 38587, 36317, 34300, 32494, 30870, 29400, 28063, 26843, | ||
419 | 25725, 24696, 23746, 22866, 22050, 21289, 20580, 19916, 19293 | ||
420 | }; | ||
421 | |||
422 | voices = gus->gf1.active_voices; | ||
423 | if (voices > 32) | ||
424 | voices = 32; | ||
425 | if (voices < 14) | ||
426 | voices = 14; | ||
427 | if (gus->gf1.enh_mode) | ||
428 | voices = 32; | ||
429 | gus->gf1.active_voices = voices; | ||
430 | gus->gf1.playback_freq = | ||
431 | gus->gf1.enh_mode ? 44100 : voices_tbl[voices - 14]; | ||
432 | if (!gus->gf1.enh_mode) { | ||
433 | snd_gf1_i_write8(gus, SNDRV_GF1_GB_ACTIVE_VOICES, 0xc0 | (voices - 1)); | ||
434 | udelay(100); | ||
435 | } | ||
436 | } | ||
437 | |||
438 | #ifdef CONFIG_SND_DEBUG | ||
439 | |||
440 | void snd_gf1_print_voice_registers(snd_gus_card_t * gus) | ||
441 | { | ||
442 | unsigned char mode; | ||
443 | int voice, ctrl; | ||
444 | |||
445 | voice = gus->gf1.active_voice; | ||
446 | printk(" -%i- GF1 voice ctrl, ramp ctrl = 0x%x, 0x%x\n", voice, ctrl = snd_gf1_i_read8(gus, 0), snd_gf1_i_read8(gus, 0x0d)); | ||
447 | printk(" -%i- GF1 frequency = 0x%x\n", voice, snd_gf1_i_read16(gus, 1)); | ||
448 | printk(" -%i- GF1 loop start, end = 0x%x (0x%x), 0x%x (0x%x)\n", voice, snd_gf1_i_read_addr(gus, 2, ctrl & 4), snd_gf1_i_read_addr(gus, 2, (ctrl & 4) ^ 4), snd_gf1_i_read_addr(gus, 4, ctrl & 4), snd_gf1_i_read_addr(gus, 4, (ctrl & 4) ^ 4)); | ||
449 | printk(" -%i- GF1 ramp start, end, rate = 0x%x, 0x%x, 0x%x\n", voice, snd_gf1_i_read8(gus, 7), snd_gf1_i_read8(gus, 8), snd_gf1_i_read8(gus, 6)); | ||
450 | printk(" -%i- GF1 volume = 0x%x\n", voice, snd_gf1_i_read16(gus, 9)); | ||
451 | printk(" -%i- GF1 position = 0x%x (0x%x)\n", voice, snd_gf1_i_read_addr(gus, 0x0a, ctrl & 4), snd_gf1_i_read_addr(gus, 0x0a, (ctrl & 4) ^ 4)); | ||
452 | if (gus->interwave && snd_gf1_i_read8(gus, 0x19) & 0x01) { /* enhanced mode */ | ||
453 | mode = snd_gf1_i_read8(gus, 0x15); | ||
454 | printk(" -%i- GFA1 mode = 0x%x\n", voice, mode); | ||
455 | if (mode & 0x01) { /* Effect processor */ | ||
456 | printk(" -%i- GFA1 effect address = 0x%x\n", voice, snd_gf1_i_read_addr(gus, 0x11, ctrl & 4)); | ||
457 | printk(" -%i- GFA1 effect volume = 0x%x\n", voice, snd_gf1_i_read16(gus, 0x16)); | ||
458 | printk(" -%i- GFA1 effect volume final = 0x%x\n", voice, snd_gf1_i_read16(gus, 0x1d)); | ||
459 | printk(" -%i- GFA1 effect acumulator = 0x%x\n", voice, snd_gf1_i_read8(gus, 0x14)); | ||
460 | } | ||
461 | if (mode & 0x20) { | ||
462 | printk(" -%i- GFA1 left offset = 0x%x (%i)\n", voice, snd_gf1_i_read16(gus, 0x13), snd_gf1_i_read16(gus, 0x13) >> 4); | ||
463 | printk(" -%i- GFA1 left offset final = 0x%x (%i)\n", voice, snd_gf1_i_read16(gus, 0x1c), snd_gf1_i_read16(gus, 0x1c) >> 4); | ||
464 | printk(" -%i- GFA1 right offset = 0x%x (%i)\n", voice, snd_gf1_i_read16(gus, 0x0c), snd_gf1_i_read16(gus, 0x0c) >> 4); | ||
465 | printk(" -%i- GFA1 right offset final = 0x%x (%i)\n", voice, snd_gf1_i_read16(gus, 0x1b), snd_gf1_i_read16(gus, 0x1b) >> 4); | ||
466 | } else | ||
467 | printk(" -%i- GF1 pan = 0x%x\n", voice, snd_gf1_i_read8(gus, 0x0c)); | ||
468 | } else | ||
469 | printk(" -%i- GF1 pan = 0x%x\n", voice, snd_gf1_i_read8(gus, 0x0c)); | ||
470 | } | ||
471 | |||
472 | void snd_gf1_print_global_registers(snd_gus_card_t * gus) | ||
473 | { | ||
474 | unsigned char global_mode = 0x00; | ||
475 | |||
476 | printk(" -G- GF1 active voices = 0x%x\n", snd_gf1_i_look8(gus, SNDRV_GF1_GB_ACTIVE_VOICES)); | ||
477 | if (gus->interwave) { | ||
478 | global_mode = snd_gf1_i_read8(gus, SNDRV_GF1_GB_GLOBAL_MODE); | ||
479 | printk(" -G- GF1 global mode = 0x%x\n", global_mode); | ||
480 | } | ||
481 | if (global_mode & 0x02) /* LFO enabled? */ | ||
482 | printk(" -G- GF1 LFO base = 0x%x\n", snd_gf1_i_look16(gus, SNDRV_GF1_GW_LFO_BASE)); | ||
483 | printk(" -G- GF1 voices IRQ read = 0x%x\n", snd_gf1_i_look8(gus, SNDRV_GF1_GB_VOICES_IRQ_READ)); | ||
484 | printk(" -G- GF1 DRAM DMA control = 0x%x\n", snd_gf1_i_look8(gus, SNDRV_GF1_GB_DRAM_DMA_CONTROL)); | ||
485 | printk(" -G- GF1 DRAM DMA high/low = 0x%x/0x%x\n", snd_gf1_i_look8(gus, SNDRV_GF1_GB_DRAM_DMA_HIGH), snd_gf1_i_read16(gus, SNDRV_GF1_GW_DRAM_DMA_LOW)); | ||
486 | printk(" -G- GF1 DRAM IO high/low = 0x%x/0x%x\n", snd_gf1_i_look8(gus, SNDRV_GF1_GB_DRAM_IO_HIGH), snd_gf1_i_read16(gus, SNDRV_GF1_GW_DRAM_IO_LOW)); | ||
487 | if (!gus->interwave) | ||
488 | printk(" -G- GF1 record DMA control = 0x%x\n", snd_gf1_i_look8(gus, SNDRV_GF1_GB_REC_DMA_CONTROL)); | ||
489 | printk(" -G- GF1 DRAM IO 16 = 0x%x\n", snd_gf1_i_look16(gus, SNDRV_GF1_GW_DRAM_IO16)); | ||
490 | if (gus->gf1.enh_mode) { | ||
491 | printk(" -G- GFA1 memory config = 0x%x\n", snd_gf1_i_look16(gus, SNDRV_GF1_GW_MEMORY_CONFIG)); | ||
492 | printk(" -G- GFA1 memory control = 0x%x\n", snd_gf1_i_look8(gus, SNDRV_GF1_GB_MEMORY_CONTROL)); | ||
493 | printk(" -G- GFA1 FIFO record base = 0x%x\n", snd_gf1_i_look16(gus, SNDRV_GF1_GW_FIFO_RECORD_BASE_ADDR)); | ||
494 | printk(" -G- GFA1 FIFO playback base = 0x%x\n", snd_gf1_i_look16(gus, SNDRV_GF1_GW_FIFO_PLAY_BASE_ADDR)); | ||
495 | printk(" -G- GFA1 interleave control = 0x%x\n", snd_gf1_i_look16(gus, SNDRV_GF1_GW_INTERLEAVE)); | ||
496 | } | ||
497 | } | ||
498 | |||
499 | void snd_gf1_print_setup_registers(snd_gus_card_t * gus) | ||
500 | { | ||
501 | printk(" -S- mix control = 0x%x\n", inb(GUSP(gus, MIXCNTRLREG))); | ||
502 | printk(" -S- IRQ status = 0x%x\n", inb(GUSP(gus, IRQSTAT))); | ||
503 | printk(" -S- timer control = 0x%x\n", inb(GUSP(gus, TIMERCNTRL))); | ||
504 | printk(" -S- timer data = 0x%x\n", inb(GUSP(gus, TIMERDATA))); | ||
505 | printk(" -S- status read = 0x%x\n", inb(GUSP(gus, REGCNTRLS))); | ||
506 | printk(" -S- Sound Blaster control = 0x%x\n", snd_gf1_i_look8(gus, SNDRV_GF1_GB_SOUND_BLASTER_CONTROL)); | ||
507 | printk(" -S- AdLib timer 1/2 = 0x%x/0x%x\n", snd_gf1_i_look8(gus, SNDRV_GF1_GB_ADLIB_TIMER_1), snd_gf1_i_look8(gus, SNDRV_GF1_GB_ADLIB_TIMER_2)); | ||
508 | printk(" -S- reset = 0x%x\n", snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET)); | ||
509 | if (gus->interwave) { | ||
510 | printk(" -S- compatibility = 0x%x\n", snd_gf1_i_look8(gus, SNDRV_GF1_GB_COMPATIBILITY)); | ||
511 | printk(" -S- decode control = 0x%x\n", snd_gf1_i_look8(gus, SNDRV_GF1_GB_DECODE_CONTROL)); | ||
512 | printk(" -S- version number = 0x%x\n", snd_gf1_i_look8(gus, SNDRV_GF1_GB_VERSION_NUMBER)); | ||
513 | printk(" -S- MPU-401 emul. control A/B = 0x%x/0x%x\n", snd_gf1_i_look8(gus, SNDRV_GF1_GB_MPU401_CONTROL_A), snd_gf1_i_look8(gus, SNDRV_GF1_GB_MPU401_CONTROL_B)); | ||
514 | printk(" -S- emulation IRQ = 0x%x\n", snd_gf1_i_look8(gus, SNDRV_GF1_GB_EMULATION_IRQ)); | ||
515 | } | ||
516 | } | ||
517 | |||
518 | void snd_gf1_peek_print_block(snd_gus_card_t * gus, unsigned int addr, int count, int w_16bit) | ||
519 | { | ||
520 | if (!w_16bit) { | ||
521 | while (count-- > 0) | ||
522 | printk(count > 0 ? "%02x:" : "%02x", snd_gf1_peek(gus, addr++)); | ||
523 | } else { | ||
524 | while (count-- > 0) { | ||
525 | printk(count > 0 ? "%04x:" : "%04x", snd_gf1_peek(gus, addr) | (snd_gf1_peek(gus, addr + 1) << 8)); | ||
526 | addr += 2; | ||
527 | } | ||
528 | } | ||
529 | } | ||
530 | |||
531 | #endif | ||
diff --git a/sound/isa/gus/gus_irq.c b/sound/isa/gus/gus_irq.c new file mode 100644 index 000000000000..1e2a15eb8106 --- /dev/null +++ b/sound/isa/gus/gus_irq.c | |||
@@ -0,0 +1,142 @@ | |||
1 | /* | ||
2 | * Routine for IRQ handling from GF1/InterWave chip | ||
3 | * Copyright (c) by Jaroslav Kysela <perex@suse.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, struct pt_regs *regs) | ||
34 | { | ||
35 | snd_gus_card_t * 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 | gus->gf1.interrupt_handler_midi_in(gus); | ||
49 | } | ||
50 | if (status & 0x01) { | ||
51 | STAT_ADD(gus->gf1.interrupt_stat_midi_out); | ||
52 | gus->gf1.interrupt_handler_midi_out(gus); | ||
53 | } | ||
54 | if (status & (0x20 | 0x40)) { | ||
55 | unsigned int already, _current_; | ||
56 | unsigned char voice_status, voice; | ||
57 | snd_gus_voice_t *pvoice; | ||
58 | |||
59 | already = 0; | ||
60 | while (((voice_status = snd_gf1_i_read8(gus, SNDRV_GF1_GB_VOICES_IRQ)) & 0xc0) != 0xc0) { | ||
61 | voice = voice_status & 0x1f; | ||
62 | _current_ = 1 << voice; | ||
63 | if (already & _current_) | ||
64 | continue; /* multi request */ | ||
65 | already |= _current_; /* mark request */ | ||
66 | #if 0 | ||
67 | printk("voice = %i, voice_status = 0x%x, voice_verify = %i\n", voice, voice_status, inb(GUSP(gus, GF1PAGE))); | ||
68 | #endif | ||
69 | pvoice = &gus->gf1.voices[voice]; | ||
70 | if (pvoice->use) { | ||
71 | if (!(voice_status & 0x80)) { /* voice position IRQ */ | ||
72 | STAT_ADD(pvoice->interrupt_stat_wave); | ||
73 | pvoice->handler_wave(gus, pvoice); | ||
74 | } | ||
75 | if (!(voice_status & 0x40)) { /* volume ramp IRQ */ | ||
76 | STAT_ADD(pvoice->interrupt_stat_volume); | ||
77 | pvoice->handler_volume(gus, pvoice); | ||
78 | } | ||
79 | } else { | ||
80 | STAT_ADD(gus->gf1.interrupt_stat_voice_lost); | ||
81 | snd_gf1_i_ctrl_stop(gus, SNDRV_GF1_VB_ADDRESS_CONTROL); | ||
82 | snd_gf1_i_ctrl_stop(gus, SNDRV_GF1_VB_VOLUME_CONTROL); | ||
83 | } | ||
84 | } | ||
85 | } | ||
86 | if (status & 0x04) { | ||
87 | STAT_ADD(gus->gf1.interrupt_stat_timer1); | ||
88 | gus->gf1.interrupt_handler_timer1(gus); | ||
89 | } | ||
90 | if (status & 0x08) { | ||
91 | STAT_ADD(gus->gf1.interrupt_stat_timer2); | ||
92 | gus->gf1.interrupt_handler_timer2(gus); | ||
93 | } | ||
94 | if (status & 0x80) { | ||
95 | if (snd_gf1_i_look8(gus, SNDRV_GF1_GB_DRAM_DMA_CONTROL) & 0x40) { | ||
96 | STAT_ADD(gus->gf1.interrupt_stat_dma_write); | ||
97 | gus->gf1.interrupt_handler_dma_write(gus); | ||
98 | } | ||
99 | if (snd_gf1_i_look8(gus, SNDRV_GF1_GB_REC_DMA_CONTROL) & 0x40) { | ||
100 | STAT_ADD(gus->gf1.interrupt_stat_dma_read); | ||
101 | gus->gf1.interrupt_handler_dma_read(gus); | ||
102 | } | ||
103 | } | ||
104 | if (--loop > 0) | ||
105 | goto __again; | ||
106 | return IRQ_NONE; | ||
107 | } | ||
108 | |||
109 | #ifdef CONFIG_SND_DEBUG | ||
110 | static void snd_gus_irq_info_read(snd_info_entry_t *entry, | ||
111 | snd_info_buffer_t * buffer) | ||
112 | { | ||
113 | snd_gus_card_t *gus; | ||
114 | snd_gus_voice_t *pvoice; | ||
115 | int idx; | ||
116 | |||
117 | gus = entry->private_data; | ||
118 | snd_iprintf(buffer, "midi out = %u\n", gus->gf1.interrupt_stat_midi_out); | ||
119 | snd_iprintf(buffer, "midi in = %u\n", gus->gf1.interrupt_stat_midi_in); | ||
120 | snd_iprintf(buffer, "timer1 = %u\n", gus->gf1.interrupt_stat_timer1); | ||
121 | snd_iprintf(buffer, "timer2 = %u\n", gus->gf1.interrupt_stat_timer2); | ||
122 | snd_iprintf(buffer, "dma write = %u\n", gus->gf1.interrupt_stat_dma_write); | ||
123 | snd_iprintf(buffer, "dma read = %u\n", gus->gf1.interrupt_stat_dma_read); | ||
124 | snd_iprintf(buffer, "voice lost = %u\n", gus->gf1.interrupt_stat_voice_lost); | ||
125 | for (idx = 0; idx < 32; idx++) { | ||
126 | pvoice = &gus->gf1.voices[idx]; | ||
127 | snd_iprintf(buffer, "voice %i: wave = %u, volume = %u\n", | ||
128 | idx, | ||
129 | pvoice->interrupt_stat_wave, | ||
130 | pvoice->interrupt_stat_volume); | ||
131 | } | ||
132 | } | ||
133 | |||
134 | void snd_gus_irq_profile_init(snd_gus_card_t *gus) | ||
135 | { | ||
136 | snd_info_entry_t *entry; | ||
137 | |||
138 | if (! snd_card_proc_new(gus->card, "gusirq", &entry)) | ||
139 | snd_info_set_text_ops(entry, gus, 1024, snd_gus_irq_info_read); | ||
140 | } | ||
141 | |||
142 | #endif | ||
diff --git a/sound/isa/gus/gus_main.c b/sound/isa/gus/gus_main.c new file mode 100644 index 000000000000..73f81c14f768 --- /dev/null +++ b/sound/isa/gus/gus_main.c | |||
@@ -0,0 +1,514 @@ | |||
1 | /* | ||
2 | * Routines for Gravis UltraSound soundcards | ||
3 | * Copyright (c) by Jaroslav Kysela <perex@suse.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 <linux/init.h> | ||
24 | #include <linux/interrupt.h> | ||
25 | #include <linux/delay.h> | ||
26 | #include <linux/slab.h> | ||
27 | #include <linux/ioport.h> | ||
28 | #include <sound/core.h> | ||
29 | #include <sound/gus.h> | ||
30 | #include <sound/control.h> | ||
31 | |||
32 | #include <asm/dma.h> | ||
33 | |||
34 | MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>"); | ||
35 | MODULE_DESCRIPTION("Routines for Gravis UltraSound soundcards"); | ||
36 | MODULE_LICENSE("GPL"); | ||
37 | |||
38 | static int snd_gus_init_dma_irq(snd_gus_card_t * gus, int latches); | ||
39 | |||
40 | int snd_gus_use_inc(snd_gus_card_t * gus) | ||
41 | { | ||
42 | if (!try_module_get(gus->card->module)) | ||
43 | return 0; | ||
44 | return 1; | ||
45 | } | ||
46 | |||
47 | void snd_gus_use_dec(snd_gus_card_t * gus) | ||
48 | { | ||
49 | module_put(gus->card->module); | ||
50 | } | ||
51 | |||
52 | static int snd_gus_joystick_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) | ||
53 | { | ||
54 | uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; | ||
55 | uinfo->count = 1; | ||
56 | uinfo->value.integer.min = 0; | ||
57 | uinfo->value.integer.max = 31; | ||
58 | return 0; | ||
59 | } | ||
60 | |||
61 | static int snd_gus_joystick_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) | ||
62 | { | ||
63 | snd_gus_card_t *gus = snd_kcontrol_chip(kcontrol); | ||
64 | |||
65 | ucontrol->value.integer.value[0] = gus->joystick_dac & 31; | ||
66 | return 0; | ||
67 | } | ||
68 | |||
69 | static int snd_gus_joystick_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) | ||
70 | { | ||
71 | snd_gus_card_t *gus = snd_kcontrol_chip(kcontrol); | ||
72 | unsigned long flags; | ||
73 | int change; | ||
74 | unsigned char nval; | ||
75 | |||
76 | nval = ucontrol->value.integer.value[0] & 31; | ||
77 | spin_lock_irqsave(&gus->reg_lock, flags); | ||
78 | change = gus->joystick_dac != nval; | ||
79 | gus->joystick_dac = nval; | ||
80 | snd_gf1_write8(gus, SNDRV_GF1_GB_JOYSTICK_DAC_LEVEL, gus->joystick_dac); | ||
81 | spin_unlock_irqrestore(&gus->reg_lock, flags); | ||
82 | return change; | ||
83 | } | ||
84 | |||
85 | static snd_kcontrol_new_t snd_gus_joystick_control = { | ||
86 | .iface = SNDRV_CTL_ELEM_IFACE_CARD, | ||
87 | .name = "Joystick Speed", | ||
88 | .info = snd_gus_joystick_info, | ||
89 | .get = snd_gus_joystick_get, | ||
90 | .put = snd_gus_joystick_put | ||
91 | }; | ||
92 | |||
93 | static void snd_gus_init_control(snd_gus_card_t *gus) | ||
94 | { | ||
95 | if (!gus->ace_flag) | ||
96 | snd_ctl_add(gus->card, snd_ctl_new1(&snd_gus_joystick_control, gus)); | ||
97 | } | ||
98 | |||
99 | /* | ||
100 | * | ||
101 | */ | ||
102 | |||
103 | static int snd_gus_free(snd_gus_card_t *gus) | ||
104 | { | ||
105 | if (gus->gf1.res_port2 == NULL) | ||
106 | goto __hw_end; | ||
107 | #if defined(CONFIG_SND_SEQUENCER) || (defined(MODULE) && defined(CONFIG_SND_SEQUENCER_MODULE)) | ||
108 | if (gus->seq_dev) { | ||
109 | snd_device_free(gus->card, gus->seq_dev); | ||
110 | gus->seq_dev = NULL; | ||
111 | } | ||
112 | #endif | ||
113 | snd_gf1_stop(gus); | ||
114 | snd_gus_init_dma_irq(gus, 0); | ||
115 | __hw_end: | ||
116 | if (gus->gf1.res_port1) { | ||
117 | release_resource(gus->gf1.res_port1); | ||
118 | kfree_nocheck(gus->gf1.res_port1); | ||
119 | } | ||
120 | if (gus->gf1.res_port2) { | ||
121 | release_resource(gus->gf1.res_port2); | ||
122 | kfree_nocheck(gus->gf1.res_port2); | ||
123 | } | ||
124 | if (gus->gf1.irq >= 0) | ||
125 | free_irq(gus->gf1.irq, (void *) gus); | ||
126 | if (gus->gf1.dma1 >= 0) { | ||
127 | disable_dma(gus->gf1.dma1); | ||
128 | free_dma(gus->gf1.dma1); | ||
129 | } | ||
130 | if (!gus->equal_dma && gus->gf1.dma2 >= 0) { | ||
131 | disable_dma(gus->gf1.dma2); | ||
132 | free_dma(gus->gf1.dma2); | ||
133 | } | ||
134 | kfree(gus); | ||
135 | return 0; | ||
136 | } | ||
137 | |||
138 | static int snd_gus_dev_free(snd_device_t *device) | ||
139 | { | ||
140 | snd_gus_card_t *gus = device->device_data; | ||
141 | return snd_gus_free(gus); | ||
142 | } | ||
143 | |||
144 | int snd_gus_create(snd_card_t * card, | ||
145 | unsigned long port, | ||
146 | int irq, int dma1, int dma2, | ||
147 | int timer_dev, | ||
148 | int voices, | ||
149 | int pcm_channels, | ||
150 | int effect, | ||
151 | snd_gus_card_t **rgus) | ||
152 | { | ||
153 | snd_gus_card_t *gus; | ||
154 | int err; | ||
155 | static snd_device_ops_t ops = { | ||
156 | .dev_free = snd_gus_dev_free, | ||
157 | }; | ||
158 | |||
159 | *rgus = NULL; | ||
160 | gus = kcalloc(1, sizeof(*gus), GFP_KERNEL); | ||
161 | if (gus == NULL) | ||
162 | return -ENOMEM; | ||
163 | gus->gf1.irq = -1; | ||
164 | gus->gf1.dma1 = -1; | ||
165 | gus->gf1.dma2 = -1; | ||
166 | gus->card = card; | ||
167 | gus->gf1.port = port; | ||
168 | /* fill register variables for speedup */ | ||
169 | gus->gf1.reg_page = GUSP(gus, GF1PAGE); | ||
170 | gus->gf1.reg_regsel = GUSP(gus, GF1REGSEL); | ||
171 | gus->gf1.reg_data8 = GUSP(gus, GF1DATAHIGH); | ||
172 | gus->gf1.reg_data16 = GUSP(gus, GF1DATALOW); | ||
173 | gus->gf1.reg_irqstat = GUSP(gus, IRQSTAT); | ||
174 | gus->gf1.reg_dram = GUSP(gus, DRAM); | ||
175 | gus->gf1.reg_timerctrl = GUSP(gus, TIMERCNTRL); | ||
176 | gus->gf1.reg_timerdata = GUSP(gus, TIMERDATA); | ||
177 | /* allocate resources */ | ||
178 | if ((gus->gf1.res_port1 = request_region(port, 16, "GUS GF1 (Adlib/SB)")) == NULL) { | ||
179 | snd_printk(KERN_ERR "gus: can't grab SB port 0x%lx\n", port); | ||
180 | snd_gus_free(gus); | ||
181 | return -EBUSY; | ||
182 | } | ||
183 | if ((gus->gf1.res_port2 = request_region(port + 0x100, 12, "GUS GF1 (Synth)")) == NULL) { | ||
184 | snd_printk(KERN_ERR "gus: can't grab synth port 0x%lx\n", port + 0x100); | ||
185 | snd_gus_free(gus); | ||
186 | return -EBUSY; | ||
187 | } | ||
188 | if (irq >= 0 && request_irq(irq, snd_gus_interrupt, SA_INTERRUPT, "GUS GF1", (void *) gus)) { | ||
189 | snd_printk(KERN_ERR "gus: can't grab irq %d\n", irq); | ||
190 | snd_gus_free(gus); | ||
191 | return -EBUSY; | ||
192 | } | ||
193 | gus->gf1.irq = irq; | ||
194 | if (request_dma(dma1, "GUS - 1")) { | ||
195 | snd_printk(KERN_ERR "gus: can't grab DMA1 %d\n", dma1); | ||
196 | snd_gus_free(gus); | ||
197 | return -EBUSY; | ||
198 | } | ||
199 | gus->gf1.dma1 = dma1; | ||
200 | if (dma2 >= 0 && dma1 != dma2) { | ||
201 | if (request_dma(dma2, "GUS - 2")) { | ||
202 | snd_printk(KERN_ERR "gus: can't grab DMA2 %d\n", dma2); | ||
203 | snd_gus_free(gus); | ||
204 | return -EBUSY; | ||
205 | } | ||
206 | gus->gf1.dma2 = dma2; | ||
207 | } else { | ||
208 | gus->gf1.dma2 = gus->gf1.dma1; | ||
209 | gus->equal_dma = 1; | ||
210 | } | ||
211 | gus->timer_dev = timer_dev; | ||
212 | if (voices < 14) | ||
213 | voices = 14; | ||
214 | if (voices > 32) | ||
215 | voices = 32; | ||
216 | if (pcm_channels < 0) | ||
217 | pcm_channels = 0; | ||
218 | if (pcm_channels > 8) | ||
219 | pcm_channels = 8; | ||
220 | pcm_channels++; | ||
221 | pcm_channels &= ~1; | ||
222 | gus->gf1.effect = effect ? 1 : 0; | ||
223 | gus->gf1.active_voices = voices; | ||
224 | gus->gf1.pcm_channels = pcm_channels; | ||
225 | gus->gf1.volume_ramp = 25; | ||
226 | gus->gf1.smooth_pan = 1; | ||
227 | spin_lock_init(&gus->reg_lock); | ||
228 | spin_lock_init(&gus->voice_alloc); | ||
229 | spin_lock_init(&gus->active_voice_lock); | ||
230 | spin_lock_init(&gus->event_lock); | ||
231 | spin_lock_init(&gus->dma_lock); | ||
232 | spin_lock_init(&gus->pcm_volume_level_lock); | ||
233 | spin_lock_init(&gus->uart_cmd_lock); | ||
234 | init_MUTEX(&gus->dma_mutex); | ||
235 | if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, gus, &ops)) < 0) { | ||
236 | snd_gus_free(gus); | ||
237 | return err; | ||
238 | } | ||
239 | *rgus = gus; | ||
240 | return 0; | ||
241 | } | ||
242 | |||
243 | /* | ||
244 | * Memory detection routine for plain GF1 soundcards | ||
245 | */ | ||
246 | |||
247 | static int snd_gus_detect_memory(snd_gus_card_t * gus) | ||
248 | { | ||
249 | int l, idx, local; | ||
250 | unsigned char d; | ||
251 | |||
252 | snd_gf1_poke(gus, 0L, 0xaa); | ||
253 | snd_gf1_poke(gus, 1L, 0x55); | ||
254 | if (snd_gf1_peek(gus, 0L) != 0xaa || snd_gf1_peek(gus, 1L) != 0x55) { | ||
255 | snd_printk("plain GF1 card at 0x%lx without onboard DRAM?\n", gus->gf1.port); | ||
256 | return -ENOMEM; | ||
257 | } | ||
258 | for (idx = 1, d = 0xab; idx < 4; idx++, d++) { | ||
259 | local = idx << 18; | ||
260 | snd_gf1_poke(gus, local, d); | ||
261 | snd_gf1_poke(gus, local + 1, d + 1); | ||
262 | if (snd_gf1_peek(gus, local) != d || | ||
263 | snd_gf1_peek(gus, local + 1) != d + 1 || | ||
264 | snd_gf1_peek(gus, 0L) != 0xaa) | ||
265 | break; | ||
266 | } | ||
267 | #if 1 | ||
268 | gus->gf1.memory = idx << 18; | ||
269 | #else | ||
270 | gus->gf1.memory = 256 * 1024; | ||
271 | #endif | ||
272 | for (l = 0, local = gus->gf1.memory; l < 4; l++, local -= 256 * 1024) { | ||
273 | gus->gf1.mem_alloc.banks_8[l].address = | ||
274 | gus->gf1.mem_alloc.banks_8[l].size = 0; | ||
275 | gus->gf1.mem_alloc.banks_16[l].address = l << 18; | ||
276 | gus->gf1.mem_alloc.banks_16[l].size = local > 0 ? 256 * 1024 : 0; | ||
277 | } | ||
278 | gus->gf1.mem_alloc.banks_8[0].size = gus->gf1.memory; | ||
279 | return 0; /* some memory were detected */ | ||
280 | } | ||
281 | |||
282 | static int snd_gus_init_dma_irq(snd_gus_card_t * gus, int latches) | ||
283 | { | ||
284 | snd_card_t *card; | ||
285 | unsigned long flags; | ||
286 | int irq, dma1, dma2; | ||
287 | static unsigned char irqs[16] = | ||
288 | {0, 0, 1, 3, 0, 2, 0, 4, 0, 1, 0, 5, 6, 0, 0, 7}; | ||
289 | static unsigned char dmas[8] = | ||
290 | {6, 1, 0, 2, 0, 3, 4, 5}; | ||
291 | |||
292 | snd_assert(gus != NULL, return -EINVAL); | ||
293 | card = gus->card; | ||
294 | snd_assert(card != NULL, return -EINVAL); | ||
295 | |||
296 | gus->mix_cntrl_reg &= 0xf8; | ||
297 | gus->mix_cntrl_reg |= 0x01; /* disable MIC, LINE IN, enable LINE OUT */ | ||
298 | if (gus->codec_flag || gus->ess_flag) { | ||
299 | gus->mix_cntrl_reg &= ~1; /* enable LINE IN */ | ||
300 | gus->mix_cntrl_reg |= 4; /* enable MIC */ | ||
301 | } | ||
302 | dma1 = gus->gf1.dma1; | ||
303 | dma1 = dma1 < 0 ? -dma1 : dma1; | ||
304 | dma1 = dmas[dma1 & 7]; | ||
305 | dma2 = gus->gf1.dma2; | ||
306 | dma2 = dma2 < 0 ? -dma2 : dma2; | ||
307 | dma2 = dmas[dma2 & 7]; | ||
308 | #if 0 | ||
309 | printk("dma1 = %i, dma2 = %i\n", gus->gf1.dma1, gus->gf1.dma2); | ||
310 | #endif | ||
311 | dma1 |= gus->equal_dma ? 0x40 : (dma2 << 3); | ||
312 | |||
313 | if ((dma1 & 7) == 0 || (dma2 & 7) == 0) { | ||
314 | snd_printk("Error! DMA isn't defined.\n"); | ||
315 | return -EINVAL; | ||
316 | } | ||
317 | irq = gus->gf1.irq; | ||
318 | irq = irq < 0 ? -irq : irq; | ||
319 | irq = irqs[irq & 0x0f]; | ||
320 | if (irq == 0) { | ||
321 | snd_printk("Error! IRQ isn't defined.\n"); | ||
322 | return -EINVAL; | ||
323 | } | ||
324 | irq |= 0x40; | ||
325 | #if 0 | ||
326 | card->mixer.mix_ctrl_reg |= 0x10; | ||
327 | #endif | ||
328 | |||
329 | spin_lock_irqsave(&gus->reg_lock, flags); | ||
330 | outb(5, GUSP(gus, REGCNTRLS)); | ||
331 | outb(gus->mix_cntrl_reg, GUSP(gus, MIXCNTRLREG)); | ||
332 | outb(0x00, GUSP(gus, IRQDMACNTRLREG)); | ||
333 | outb(0, GUSP(gus, REGCNTRLS)); | ||
334 | spin_unlock_irqrestore(&gus->reg_lock, flags); | ||
335 | |||
336 | udelay(100); | ||
337 | |||
338 | spin_lock_irqsave(&gus->reg_lock, flags); | ||
339 | outb(0x00 | gus->mix_cntrl_reg, GUSP(gus, MIXCNTRLREG)); | ||
340 | outb(dma1, GUSP(gus, IRQDMACNTRLREG)); | ||
341 | if (latches) { | ||
342 | outb(0x40 | gus->mix_cntrl_reg, GUSP(gus, MIXCNTRLREG)); | ||
343 | outb(irq, GUSP(gus, IRQDMACNTRLREG)); | ||
344 | } | ||
345 | spin_unlock_irqrestore(&gus->reg_lock, flags); | ||
346 | |||
347 | udelay(100); | ||
348 | |||
349 | spin_lock_irqsave(&gus->reg_lock, flags); | ||
350 | outb(0x00 | gus->mix_cntrl_reg, GUSP(gus, MIXCNTRLREG)); | ||
351 | outb(dma1, GUSP(gus, IRQDMACNTRLREG)); | ||
352 | if (latches) { | ||
353 | outb(0x40 | gus->mix_cntrl_reg, GUSP(gus, MIXCNTRLREG)); | ||
354 | outb(irq, GUSP(gus, IRQDMACNTRLREG)); | ||
355 | } | ||
356 | spin_unlock_irqrestore(&gus->reg_lock, flags); | ||
357 | |||
358 | snd_gf1_delay(gus); | ||
359 | |||
360 | if (latches) | ||
361 | gus->mix_cntrl_reg |= 0x08; /* enable latches */ | ||
362 | else | ||
363 | gus->mix_cntrl_reg &= ~0x08; /* disable latches */ | ||
364 | spin_lock_irqsave(&gus->reg_lock, flags); | ||
365 | outb(gus->mix_cntrl_reg, GUSP(gus, MIXCNTRLREG)); | ||
366 | outb(0, GUSP(gus, GF1PAGE)); | ||
367 | spin_unlock_irqrestore(&gus->reg_lock, flags); | ||
368 | |||
369 | return 0; | ||
370 | } | ||
371 | |||
372 | static int snd_gus_check_version(snd_gus_card_t * gus) | ||
373 | { | ||
374 | unsigned long flags; | ||
375 | unsigned char val, rev; | ||
376 | snd_card_t *card; | ||
377 | |||
378 | card = gus->card; | ||
379 | spin_lock_irqsave(&gus->reg_lock, flags); | ||
380 | outb(0x20, GUSP(gus, REGCNTRLS)); | ||
381 | val = inb(GUSP(gus, REGCNTRLS)); | ||
382 | rev = inb(GUSP(gus, BOARDVERSION)); | ||
383 | spin_unlock_irqrestore(&gus->reg_lock, flags); | ||
384 | snd_printdd("GF1 [0x%lx] init - val = 0x%x, rev = 0x%x\n", gus->gf1.port, val, rev); | ||
385 | strcpy(card->driver, "GUS"); | ||
386 | strcpy(card->longname, "Gravis UltraSound Classic (2.4)"); | ||
387 | if ((val != 255 && (val & 0x06)) || (rev >= 5 && rev != 255)) { | ||
388 | if (rev >= 5 && rev <= 9) { | ||
389 | gus->ics_flag = 1; | ||
390 | if (rev == 5) | ||
391 | gus->ics_flipped = 1; | ||
392 | card->longname[27] = '3'; | ||
393 | card->longname[29] = rev == 5 ? '5' : '7'; | ||
394 | } | ||
395 | if (rev >= 10 && rev != 255) { | ||
396 | if (rev >= 10 && rev <= 11) { | ||
397 | strcpy(card->driver, "GUS MAX"); | ||
398 | strcpy(card->longname, "Gravis UltraSound MAX"); | ||
399 | gus->max_flag = 1; | ||
400 | } else if (rev == 0x30) { | ||
401 | strcpy(card->driver, "GUS ACE"); | ||
402 | strcpy(card->longname, "Gravis UltraSound Ace"); | ||
403 | gus->ace_flag = 1; | ||
404 | } else if (rev == 0x50) { | ||
405 | strcpy(card->driver, "GUS Extreme"); | ||
406 | strcpy(card->longname, "Gravis UltraSound Extreme"); | ||
407 | gus->ess_flag = 1; | ||
408 | } else { | ||
409 | snd_printk("unknown GF1 revision number at 0x%lx - 0x%x (0x%x)\n", gus->gf1.port, rev, val); | ||
410 | snd_printk(" please - report to <perex@suse.cz>\n"); | ||
411 | } | ||
412 | } | ||
413 | } | ||
414 | strcpy(card->shortname, card->longname); | ||
415 | gus->uart_enable = 1; /* standard GUSes doesn't have midi uart trouble */ | ||
416 | snd_gus_init_control(gus); | ||
417 | return 0; | ||
418 | } | ||
419 | |||
420 | static void snd_gus_seq_dev_free(snd_seq_device_t *seq_dev) | ||
421 | { | ||
422 | snd_gus_card_t *gus = seq_dev->private_data; | ||
423 | gus->seq_dev = NULL; | ||
424 | } | ||
425 | |||
426 | int snd_gus_initialize(snd_gus_card_t *gus) | ||
427 | { | ||
428 | int err; | ||
429 | |||
430 | if (!gus->interwave) { | ||
431 | if ((err = snd_gus_check_version(gus)) < 0) { | ||
432 | snd_printk("version check failed\n"); | ||
433 | return err; | ||
434 | } | ||
435 | if ((err = snd_gus_detect_memory(gus)) < 0) | ||
436 | return err; | ||
437 | } | ||
438 | if ((err = snd_gus_init_dma_irq(gus, 1)) < 0) | ||
439 | return err; | ||
440 | #if defined(CONFIG_SND_SEQUENCER) || (defined(MODULE) && defined(CONFIG_SND_SEQUENCER_MODULE)) | ||
441 | if (snd_seq_device_new(gus->card, 1, SNDRV_SEQ_DEV_ID_GUS, | ||
442 | sizeof(snd_gus_card_t*), &gus->seq_dev) >= 0) { | ||
443 | strcpy(gus->seq_dev->name, "GUS"); | ||
444 | *(snd_gus_card_t**)SNDRV_SEQ_DEVICE_ARGPTR(gus->seq_dev) = gus; | ||
445 | gus->seq_dev->private_data = gus; | ||
446 | gus->seq_dev->private_free = snd_gus_seq_dev_free; | ||
447 | } | ||
448 | #endif | ||
449 | snd_gf1_start(gus); | ||
450 | gus->initialized = 1; | ||
451 | return 0; | ||
452 | } | ||
453 | |||
454 | /* gus_io.c */ | ||
455 | EXPORT_SYMBOL(snd_gf1_delay); | ||
456 | EXPORT_SYMBOL(snd_gf1_write8); | ||
457 | EXPORT_SYMBOL(snd_gf1_look8); | ||
458 | EXPORT_SYMBOL(snd_gf1_write16); | ||
459 | EXPORT_SYMBOL(snd_gf1_look16); | ||
460 | EXPORT_SYMBOL(snd_gf1_i_write8); | ||
461 | EXPORT_SYMBOL(snd_gf1_i_look8); | ||
462 | EXPORT_SYMBOL(snd_gf1_i_write16); | ||
463 | EXPORT_SYMBOL(snd_gf1_i_look16); | ||
464 | EXPORT_SYMBOL(snd_gf1_dram_addr); | ||
465 | EXPORT_SYMBOL(snd_gf1_write_addr); | ||
466 | EXPORT_SYMBOL(snd_gf1_poke); | ||
467 | EXPORT_SYMBOL(snd_gf1_peek); | ||
468 | /* gus_reset.c */ | ||
469 | EXPORT_SYMBOL(snd_gf1_alloc_voice); | ||
470 | EXPORT_SYMBOL(snd_gf1_free_voice); | ||
471 | EXPORT_SYMBOL(snd_gf1_ctrl_stop); | ||
472 | EXPORT_SYMBOL(snd_gf1_stop_voice); | ||
473 | EXPORT_SYMBOL(snd_gf1_start); | ||
474 | EXPORT_SYMBOL(snd_gf1_stop); | ||
475 | /* gus_mixer.c */ | ||
476 | EXPORT_SYMBOL(snd_gf1_new_mixer); | ||
477 | /* gus_pcm.c */ | ||
478 | EXPORT_SYMBOL(snd_gf1_pcm_new); | ||
479 | /* gus.c */ | ||
480 | EXPORT_SYMBOL(snd_gus_use_inc); | ||
481 | EXPORT_SYMBOL(snd_gus_use_dec); | ||
482 | EXPORT_SYMBOL(snd_gus_create); | ||
483 | EXPORT_SYMBOL(snd_gus_initialize); | ||
484 | /* gus_irq.c */ | ||
485 | EXPORT_SYMBOL(snd_gus_interrupt); | ||
486 | /* gus_uart.c */ | ||
487 | EXPORT_SYMBOL(snd_gf1_rawmidi_new); | ||
488 | /* gus_dram.c */ | ||
489 | EXPORT_SYMBOL(snd_gus_dram_write); | ||
490 | EXPORT_SYMBOL(snd_gus_dram_read); | ||
491 | /* gus_volume.c */ | ||
492 | EXPORT_SYMBOL(snd_gf1_lvol_to_gvol_raw); | ||
493 | EXPORT_SYMBOL(snd_gf1_translate_freq); | ||
494 | /* gus_mem.c */ | ||
495 | EXPORT_SYMBOL(snd_gf1_mem_alloc); | ||
496 | EXPORT_SYMBOL(snd_gf1_mem_xfree); | ||
497 | EXPORT_SYMBOL(snd_gf1_mem_free); | ||
498 | EXPORT_SYMBOL(snd_gf1_mem_lock); | ||
499 | |||
500 | /* | ||
501 | * INIT part | ||
502 | */ | ||
503 | |||
504 | static int __init alsa_gus_init(void) | ||
505 | { | ||
506 | return 0; | ||
507 | } | ||
508 | |||
509 | static void __exit alsa_gus_exit(void) | ||
510 | { | ||
511 | } | ||
512 | |||
513 | module_init(alsa_gus_init) | ||
514 | module_exit(alsa_gus_exit) | ||
diff --git a/sound/isa/gus/gus_mem.c b/sound/isa/gus/gus_mem.c new file mode 100644 index 000000000000..bfc2b91001d5 --- /dev/null +++ b/sound/isa/gus/gus_mem.c | |||
@@ -0,0 +1,353 @@ | |||
1 | /* | ||
2 | * Copyright (c) by Jaroslav Kysela <perex@suse.cz> | ||
3 | * GUS's memory allocation routines / bottom layer | ||
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 <linux/slab.h> | ||
24 | #include <sound/core.h> | ||
25 | #include <sound/gus.h> | ||
26 | #include <sound/info.h> | ||
27 | |||
28 | #ifdef CONFIG_SND_DEBUG | ||
29 | static void snd_gf1_mem_info_read(snd_info_entry_t *entry, | ||
30 | snd_info_buffer_t * buffer); | ||
31 | #endif | ||
32 | |||
33 | void snd_gf1_mem_lock(snd_gf1_mem_t * alloc, int xup) | ||
34 | { | ||
35 | if (!xup) { | ||
36 | down(&alloc->memory_mutex); | ||
37 | } else { | ||
38 | up(&alloc->memory_mutex); | ||
39 | } | ||
40 | } | ||
41 | |||
42 | snd_gf1_mem_block_t *snd_gf1_mem_xalloc(snd_gf1_mem_t * alloc, | ||
43 | snd_gf1_mem_block_t * block) | ||
44 | { | ||
45 | snd_gf1_mem_block_t *pblock, *nblock; | ||
46 | |||
47 | nblock = (snd_gf1_mem_block_t *) kmalloc(sizeof(snd_gf1_mem_block_t), GFP_KERNEL); | ||
48 | if (nblock == NULL) | ||
49 | return NULL; | ||
50 | *nblock = *block; | ||
51 | pblock = alloc->first; | ||
52 | while (pblock) { | ||
53 | if (pblock->ptr > nblock->ptr) { | ||
54 | nblock->prev = pblock->prev; | ||
55 | nblock->next = pblock; | ||
56 | pblock->prev = nblock; | ||
57 | if (pblock == alloc->first) | ||
58 | alloc->first = nblock; | ||
59 | else | ||
60 | nblock->prev->next = nblock; | ||
61 | up(&alloc->memory_mutex); | ||
62 | return NULL; | ||
63 | } | ||
64 | pblock = pblock->next; | ||
65 | } | ||
66 | nblock->next = NULL; | ||
67 | if (alloc->last == NULL) { | ||
68 | nblock->prev = NULL; | ||
69 | alloc->first = alloc->last = nblock; | ||
70 | } else { | ||
71 | nblock->prev = alloc->last; | ||
72 | alloc->last->next = nblock; | ||
73 | alloc->last = nblock; | ||
74 | } | ||
75 | return nblock; | ||
76 | } | ||
77 | |||
78 | int snd_gf1_mem_xfree(snd_gf1_mem_t * alloc, snd_gf1_mem_block_t * block) | ||
79 | { | ||
80 | if (block->share) { /* ok.. shared block */ | ||
81 | block->share--; | ||
82 | up(&alloc->memory_mutex); | ||
83 | return 0; | ||
84 | } | ||
85 | if (alloc->first == block) { | ||
86 | alloc->first = block->next; | ||
87 | if (block->next) | ||
88 | block->next->prev = NULL; | ||
89 | } else { | ||
90 | block->prev->next = block->next; | ||
91 | if (block->next) | ||
92 | block->next->prev = block->prev; | ||
93 | } | ||
94 | if (alloc->last == block) { | ||
95 | alloc->last = block->prev; | ||
96 | if (block->prev) | ||
97 | block->prev->next = NULL; | ||
98 | } else { | ||
99 | block->next->prev = block->prev; | ||
100 | if (block->prev) | ||
101 | block->prev->next = block->next; | ||
102 | } | ||
103 | kfree(block->name); | ||
104 | kfree(block); | ||
105 | return 0; | ||
106 | } | ||
107 | |||
108 | snd_gf1_mem_block_t *snd_gf1_mem_look(snd_gf1_mem_t * alloc, | ||
109 | unsigned int address) | ||
110 | { | ||
111 | snd_gf1_mem_block_t *block; | ||
112 | |||
113 | for (block = alloc->first; block; block = block->next) { | ||
114 | if (block->ptr == address) { | ||
115 | return block; | ||
116 | } | ||
117 | } | ||
118 | return NULL; | ||
119 | } | ||
120 | |||
121 | snd_gf1_mem_block_t *snd_gf1_mem_share(snd_gf1_mem_t * alloc, | ||
122 | unsigned int *share_id) | ||
123 | { | ||
124 | snd_gf1_mem_block_t *block; | ||
125 | |||
126 | if (!share_id[0] && !share_id[1] && | ||
127 | !share_id[2] && !share_id[3]) | ||
128 | return NULL; | ||
129 | for (block = alloc->first; block; block = block->next) | ||
130 | if (!memcmp(share_id, block->share_id, sizeof(share_id))) | ||
131 | return block; | ||
132 | return NULL; | ||
133 | } | ||
134 | |||
135 | static int snd_gf1_mem_find(snd_gf1_mem_t * alloc, | ||
136 | snd_gf1_mem_block_t * block, | ||
137 | unsigned int size, int w_16, int align) | ||
138 | { | ||
139 | snd_gf1_bank_info_t *info = w_16 ? alloc->banks_16 : alloc->banks_8; | ||
140 | unsigned int idx, boundary; | ||
141 | int size1; | ||
142 | snd_gf1_mem_block_t *pblock; | ||
143 | unsigned int ptr1, ptr2; | ||
144 | |||
145 | align--; | ||
146 | if (w_16 && align < 1) | ||
147 | align = 1; | ||
148 | block->flags = w_16 ? SNDRV_GF1_MEM_BLOCK_16BIT : 0; | ||
149 | block->owner = SNDRV_GF1_MEM_OWNER_DRIVER; | ||
150 | block->share = 0; | ||
151 | block->share_id[0] = block->share_id[1] = | ||
152 | block->share_id[2] = block->share_id[3] = 0; | ||
153 | block->name = NULL; | ||
154 | block->prev = block->next = NULL; | ||
155 | for (pblock = alloc->first, idx = 0; pblock; pblock = pblock->next) { | ||
156 | while (pblock->ptr >= (boundary = info[idx].address + info[idx].size)) | ||
157 | idx++; | ||
158 | while (pblock->ptr + pblock->size >= (boundary = info[idx].address + info[idx].size)) | ||
159 | idx++; | ||
160 | ptr2 = boundary; | ||
161 | if (pblock->next) { | ||
162 | if (pblock->ptr + pblock->size == pblock->next->ptr) | ||
163 | continue; | ||
164 | if (pblock->next->ptr < boundary) | ||
165 | ptr2 = pblock->next->ptr; | ||
166 | } | ||
167 | ptr1 = (pblock->ptr + pblock->size + align) & ~align; | ||
168 | if (ptr1 >= ptr2) | ||
169 | continue; | ||
170 | size1 = ptr2 - ptr1; | ||
171 | if ((int)size <= size1) { | ||
172 | block->ptr = ptr1; | ||
173 | block->size = size; | ||
174 | return 0; | ||
175 | } | ||
176 | } | ||
177 | while (++idx < 4) { | ||
178 | if (size <= info[idx].size) { | ||
179 | /* I assume that bank address is already aligned.. */ | ||
180 | block->ptr = info[idx].address; | ||
181 | block->size = size; | ||
182 | return 0; | ||
183 | } | ||
184 | } | ||
185 | return -ENOMEM; | ||
186 | } | ||
187 | |||
188 | snd_gf1_mem_block_t *snd_gf1_mem_alloc(snd_gf1_mem_t * alloc, int owner, | ||
189 | char *name, int size, int w_16, int align, | ||
190 | unsigned int *share_id) | ||
191 | { | ||
192 | snd_gf1_mem_block_t block, *nblock; | ||
193 | |||
194 | snd_gf1_mem_lock(alloc, 0); | ||
195 | if (share_id != NULL) { | ||
196 | nblock = snd_gf1_mem_share(alloc, share_id); | ||
197 | if (nblock != NULL) { | ||
198 | if (size != (int)nblock->size) { | ||
199 | /* TODO: remove in the future */ | ||
200 | snd_printk("snd_gf1_mem_alloc - share: sizes differ\n"); | ||
201 | goto __std; | ||
202 | } | ||
203 | nblock->share++; | ||
204 | snd_gf1_mem_lock(alloc, 1); | ||
205 | return NULL; | ||
206 | } | ||
207 | } | ||
208 | __std: | ||
209 | if (snd_gf1_mem_find(alloc, &block, size, w_16, align) < 0) { | ||
210 | snd_gf1_mem_lock(alloc, 1); | ||
211 | return NULL; | ||
212 | } | ||
213 | if (share_id != NULL) | ||
214 | memcpy(&block.share_id, share_id, sizeof(block.share_id)); | ||
215 | block.owner = owner; | ||
216 | block.name = snd_kmalloc_strdup(name, GFP_KERNEL); | ||
217 | nblock = snd_gf1_mem_xalloc(alloc, &block); | ||
218 | snd_gf1_mem_lock(alloc, 1); | ||
219 | return nblock; | ||
220 | } | ||
221 | |||
222 | int snd_gf1_mem_free(snd_gf1_mem_t * alloc, unsigned int address) | ||
223 | { | ||
224 | int result; | ||
225 | snd_gf1_mem_block_t *block; | ||
226 | |||
227 | snd_gf1_mem_lock(alloc, 0); | ||
228 | if ((block = snd_gf1_mem_look(alloc, address)) != NULL) { | ||
229 | result = snd_gf1_mem_xfree(alloc, block); | ||
230 | snd_gf1_mem_lock(alloc, 1); | ||
231 | return result; | ||
232 | } | ||
233 | snd_gf1_mem_lock(alloc, 1); | ||
234 | return -EINVAL; | ||
235 | } | ||
236 | |||
237 | int snd_gf1_mem_init(snd_gus_card_t * gus) | ||
238 | { | ||
239 | snd_gf1_mem_t *alloc; | ||
240 | snd_gf1_mem_block_t block; | ||
241 | #ifdef CONFIG_SND_DEBUG | ||
242 | snd_info_entry_t *entry; | ||
243 | #endif | ||
244 | |||
245 | alloc = &gus->gf1.mem_alloc; | ||
246 | init_MUTEX(&alloc->memory_mutex); | ||
247 | alloc->first = alloc->last = NULL; | ||
248 | if (!gus->gf1.memory) | ||
249 | return 0; | ||
250 | |||
251 | memset(&block, 0, sizeof(block)); | ||
252 | block.owner = SNDRV_GF1_MEM_OWNER_DRIVER; | ||
253 | if (gus->gf1.enh_mode) { | ||
254 | block.ptr = 0; | ||
255 | block.size = 1024; | ||
256 | block.name = snd_kmalloc_strdup("InterWave LFOs", GFP_KERNEL); | ||
257 | if (snd_gf1_mem_xalloc(alloc, &block) == NULL) | ||
258 | return -ENOMEM; | ||
259 | } | ||
260 | block.ptr = gus->gf1.default_voice_address; | ||
261 | block.size = 4; | ||
262 | block.name = snd_kmalloc_strdup("Voice default (NULL's)", GFP_KERNEL); | ||
263 | if (snd_gf1_mem_xalloc(alloc, &block) == NULL) | ||
264 | return -ENOMEM; | ||
265 | #ifdef CONFIG_SND_DEBUG | ||
266 | if (! snd_card_proc_new(gus->card, "gusmem", &entry)) { | ||
267 | snd_info_set_text_ops(entry, gus, 1024, snd_gf1_mem_info_read); | ||
268 | entry->c.text.read_size = 256 * 1024; | ||
269 | } | ||
270 | #endif | ||
271 | return 0; | ||
272 | } | ||
273 | |||
274 | int snd_gf1_mem_done(snd_gus_card_t * gus) | ||
275 | { | ||
276 | snd_gf1_mem_t *alloc; | ||
277 | snd_gf1_mem_block_t *block, *nblock; | ||
278 | |||
279 | alloc = &gus->gf1.mem_alloc; | ||
280 | block = alloc->first; | ||
281 | while (block) { | ||
282 | nblock = block->next; | ||
283 | snd_gf1_mem_xfree(alloc, block); | ||
284 | block = nblock; | ||
285 | } | ||
286 | return 0; | ||
287 | } | ||
288 | |||
289 | #ifdef CONFIG_SND_DEBUG | ||
290 | static void snd_gf1_mem_info_read(snd_info_entry_t *entry, | ||
291 | snd_info_buffer_t * buffer) | ||
292 | { | ||
293 | snd_gus_card_t *gus; | ||
294 | snd_gf1_mem_t *alloc; | ||
295 | snd_gf1_mem_block_t *block; | ||
296 | unsigned int total, used; | ||
297 | int i; | ||
298 | |||
299 | gus = entry->private_data; | ||
300 | alloc = &gus->gf1.mem_alloc; | ||
301 | down(&alloc->memory_mutex); | ||
302 | snd_iprintf(buffer, "8-bit banks : \n "); | ||
303 | for (i = 0; i < 4; i++) | ||
304 | snd_iprintf(buffer, "0x%06x (%04ik)%s", alloc->banks_8[i].address, alloc->banks_8[i].size >> 10, i + 1 < 4 ? "," : ""); | ||
305 | snd_iprintf(buffer, "\n" | ||
306 | "16-bit banks : \n "); | ||
307 | for (i = total = 0; i < 4; i++) { | ||
308 | snd_iprintf(buffer, "0x%06x (%04ik)%s", alloc->banks_16[i].address, alloc->banks_16[i].size >> 10, i + 1 < 4 ? "," : ""); | ||
309 | total += alloc->banks_16[i].size; | ||
310 | } | ||
311 | snd_iprintf(buffer, "\n"); | ||
312 | used = 0; | ||
313 | for (block = alloc->first, i = 0; block; block = block->next, i++) { | ||
314 | used += block->size; | ||
315 | snd_iprintf(buffer, "Block %i at 0x%lx onboard 0x%x size %i (0x%x):\n", i, (long) block, block->ptr, block->size, block->size); | ||
316 | if (block->share || | ||
317 | block->share_id[0] || block->share_id[1] || | ||
318 | block->share_id[2] || block->share_id[3]) | ||
319 | snd_iprintf(buffer, " Share : %i [id0 0x%x] [id1 0x%x] [id2 0x%x] [id3 0x%x]\n", | ||
320 | block->share, | ||
321 | block->share_id[0], block->share_id[1], | ||
322 | block->share_id[2], block->share_id[3]); | ||
323 | snd_iprintf(buffer, " Flags :%s\n", | ||
324 | block->flags & SNDRV_GF1_MEM_BLOCK_16BIT ? " 16-bit" : ""); | ||
325 | snd_iprintf(buffer, " Owner : "); | ||
326 | switch (block->owner) { | ||
327 | case SNDRV_GF1_MEM_OWNER_DRIVER: | ||
328 | snd_iprintf(buffer, "driver - %s\n", block->name); | ||
329 | break; | ||
330 | case SNDRV_GF1_MEM_OWNER_WAVE_SIMPLE: | ||
331 | snd_iprintf(buffer, "SIMPLE wave\n"); | ||
332 | break; | ||
333 | case SNDRV_GF1_MEM_OWNER_WAVE_GF1: | ||
334 | snd_iprintf(buffer, "GF1 wave\n"); | ||
335 | break; | ||
336 | case SNDRV_GF1_MEM_OWNER_WAVE_IWFFFF: | ||
337 | snd_iprintf(buffer, "IWFFFF wave\n"); | ||
338 | break; | ||
339 | default: | ||
340 | snd_iprintf(buffer, "unknown\n"); | ||
341 | } | ||
342 | } | ||
343 | snd_iprintf(buffer, " Total: memory = %i, used = %i, free = %i\n", | ||
344 | total, used, total - used); | ||
345 | up(&alloc->memory_mutex); | ||
346 | #if 0 | ||
347 | ultra_iprintf(buffer, " Verify: free = %i, max 8-bit block = %i, max 16-bit block = %i\n", | ||
348 | ultra_memory_free_size(card, &card->gf1.mem_alloc), | ||
349 | ultra_memory_free_block(card, &card->gf1.mem_alloc, 0), | ||
350 | ultra_memory_free_block(card, &card->gf1.mem_alloc, 1)); | ||
351 | #endif | ||
352 | } | ||
353 | #endif | ||
diff --git a/sound/isa/gus/gus_mem_proc.c b/sound/isa/gus/gus_mem_proc.c new file mode 100644 index 000000000000..886763f12132 --- /dev/null +++ b/sound/isa/gus/gus_mem_proc.c | |||
@@ -0,0 +1,135 @@ | |||
1 | /* | ||
2 | * Copyright (c) by Jaroslav Kysela <perex@suse.cz> | ||
3 | * GUS's memory access via proc filesystem | ||
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 <linux/slab.h> | ||
24 | #include <sound/core.h> | ||
25 | #include <sound/gus.h> | ||
26 | #include <sound/info.h> | ||
27 | |||
28 | typedef struct gus_proc_private { | ||
29 | int rom; /* data are in ROM */ | ||
30 | unsigned int address; | ||
31 | unsigned int size; | ||
32 | snd_gus_card_t * gus; | ||
33 | } gus_proc_private_t; | ||
34 | |||
35 | static long snd_gf1_mem_proc_dump(snd_info_entry_t *entry, void *file_private_data, | ||
36 | struct file *file, char __user *buf, | ||
37 | unsigned long count, unsigned long pos) | ||
38 | { | ||
39 | long size; | ||
40 | gus_proc_private_t *priv = entry->private_data; | ||
41 | snd_gus_card_t *gus = priv->gus; | ||
42 | int err; | ||
43 | |||
44 | size = count; | ||
45 | if (pos + size > priv->size) | ||
46 | size = (long)priv->size - pos; | ||
47 | if (size > 0) { | ||
48 | if ((err = snd_gus_dram_read(gus, buf, pos, size, priv->rom)) < 0) | ||
49 | return err; | ||
50 | return size; | ||
51 | } | ||
52 | return 0; | ||
53 | } | ||
54 | |||
55 | static long long snd_gf1_mem_proc_llseek(snd_info_entry_t *entry, | ||
56 | void *private_file_data, | ||
57 | struct file *file, | ||
58 | long long offset, | ||
59 | int orig) | ||
60 | { | ||
61 | gus_proc_private_t *priv = entry->private_data; | ||
62 | |||
63 | switch (orig) { | ||
64 | case 0: /* SEEK_SET */ | ||
65 | file->f_pos = offset; | ||
66 | break; | ||
67 | case 1: /* SEEK_CUR */ | ||
68 | file->f_pos += offset; | ||
69 | break; | ||
70 | case 2: /* SEEK_END, offset is negative */ | ||
71 | file->f_pos = priv->size + offset; | ||
72 | break; | ||
73 | default: | ||
74 | return -EINVAL; | ||
75 | } | ||
76 | if (file->f_pos > priv->size) | ||
77 | file->f_pos = priv->size; | ||
78 | return file->f_pos; | ||
79 | } | ||
80 | |||
81 | static void snd_gf1_mem_proc_free(snd_info_entry_t *entry) | ||
82 | { | ||
83 | gus_proc_private_t *priv = entry->private_data; | ||
84 | kfree(priv); | ||
85 | } | ||
86 | |||
87 | static struct snd_info_entry_ops snd_gf1_mem_proc_ops = { | ||
88 | .read = snd_gf1_mem_proc_dump, | ||
89 | .llseek = snd_gf1_mem_proc_llseek, | ||
90 | }; | ||
91 | |||
92 | int snd_gf1_mem_proc_init(snd_gus_card_t * gus) | ||
93 | { | ||
94 | int idx; | ||
95 | char name[16]; | ||
96 | gus_proc_private_t *priv; | ||
97 | snd_info_entry_t *entry; | ||
98 | |||
99 | for (idx = 0; idx < 4; idx++) { | ||
100 | if (gus->gf1.mem_alloc.banks_8[idx].size > 0) { | ||
101 | priv = kcalloc(1, sizeof(*priv), GFP_KERNEL); | ||
102 | if (priv == NULL) | ||
103 | return -ENOMEM; | ||
104 | priv->gus = gus; | ||
105 | sprintf(name, "gus-ram-%i", idx); | ||
106 | if (! snd_card_proc_new(gus->card, name, &entry)) { | ||
107 | entry->content = SNDRV_INFO_CONTENT_DATA; | ||
108 | entry->private_data = priv; | ||
109 | entry->private_free = snd_gf1_mem_proc_free; | ||
110 | entry->c.ops = &snd_gf1_mem_proc_ops; | ||
111 | priv->address = gus->gf1.mem_alloc.banks_8[idx].address; | ||
112 | priv->size = entry->size = gus->gf1.mem_alloc.banks_8[idx].size; | ||
113 | } | ||
114 | } | ||
115 | } | ||
116 | for (idx = 0; idx < 4; idx++) { | ||
117 | if (gus->gf1.rom_present & (1 << idx)) { | ||
118 | priv = kcalloc(1, sizeof(*priv), GFP_KERNEL); | ||
119 | if (priv == NULL) | ||
120 | return -ENOMEM; | ||
121 | priv->rom = 1; | ||
122 | priv->gus = gus; | ||
123 | sprintf(name, "gus-rom-%i", idx); | ||
124 | if (! snd_card_proc_new(gus->card, name, &entry)) { | ||
125 | entry->content = SNDRV_INFO_CONTENT_DATA; | ||
126 | entry->private_data = priv; | ||
127 | entry->private_free = snd_gf1_mem_proc_free; | ||
128 | entry->c.ops = &snd_gf1_mem_proc_ops; | ||
129 | priv->address = idx * 4096 * 1024; | ||
130 | priv->size = entry->size = gus->gf1.rom_memory; | ||
131 | } | ||
132 | } | ||
133 | } | ||
134 | return 0; | ||
135 | } | ||
diff --git a/sound/isa/gus/gus_mixer.c b/sound/isa/gus/gus_mixer.c new file mode 100644 index 000000000000..a051094d510e --- /dev/null +++ b/sound/isa/gus/gus_mixer.c | |||
@@ -0,0 +1,199 @@ | |||
1 | /* | ||
2 | * Copyright (c) by Jaroslav Kysela <perex@suse.cz> | ||
3 | * Routines for control of ICS 2101 chip and "mixer" in GF1 chip | ||
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 <linux/time.h> | ||
24 | #include <linux/wait.h> | ||
25 | #include <sound/core.h> | ||
26 | #include <sound/control.h> | ||
27 | #include <sound/gus.h> | ||
28 | |||
29 | /* | ||
30 | * | ||
31 | */ | ||
32 | |||
33 | #define GF1_SINGLE(xname, xindex, shift, invert) \ | ||
34 | { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ | ||
35 | .info = snd_gf1_info_single, \ | ||
36 | .get = snd_gf1_get_single, .put = snd_gf1_put_single, \ | ||
37 | .private_value = shift | (invert << 8) } | ||
38 | |||
39 | static int snd_gf1_info_single(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) | ||
40 | { | ||
41 | uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; | ||
42 | uinfo->count = 1; | ||
43 | uinfo->value.integer.min = 0; | ||
44 | uinfo->value.integer.max = 1; | ||
45 | return 0; | ||
46 | } | ||
47 | |||
48 | static int snd_gf1_get_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) | ||
49 | { | ||
50 | snd_gus_card_t *gus = snd_kcontrol_chip(kcontrol); | ||
51 | int shift = kcontrol->private_value & 0xff; | ||
52 | int invert = (kcontrol->private_value >> 8) & 1; | ||
53 | |||
54 | ucontrol->value.integer.value[0] = (gus->mix_cntrl_reg >> shift) & 1; | ||
55 | if (invert) | ||
56 | ucontrol->value.integer.value[0] ^= 1; | ||
57 | return 0; | ||
58 | } | ||
59 | |||
60 | static int snd_gf1_put_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) | ||
61 | { | ||
62 | snd_gus_card_t *gus = snd_kcontrol_chip(kcontrol); | ||
63 | unsigned long flags; | ||
64 | int shift = kcontrol->private_value & 0xff; | ||
65 | int invert = (kcontrol->private_value >> 8) & 1; | ||
66 | int change; | ||
67 | unsigned char oval, nval; | ||
68 | |||
69 | nval = ucontrol->value.integer.value[0] & 1; | ||
70 | if (invert) | ||
71 | nval ^= 1; | ||
72 | nval <<= shift; | ||
73 | spin_lock_irqsave(&gus->reg_lock, flags); | ||
74 | oval = gus->mix_cntrl_reg; | ||
75 | nval = (oval & ~(1 << shift)) | nval; | ||
76 | change = nval != oval; | ||
77 | outb(gus->mix_cntrl_reg = nval, GUSP(gus, MIXCNTRLREG)); | ||
78 | outb(gus->gf1.active_voice = 0, GUSP(gus, GF1PAGE)); | ||
79 | spin_unlock_irqrestore(&gus->reg_lock, flags); | ||
80 | return change; | ||
81 | } | ||
82 | |||
83 | #define ICS_DOUBLE(xname, xindex, addr) \ | ||
84 | { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ | ||
85 | .info = snd_ics_info_double, \ | ||
86 | .get = snd_ics_get_double, .put = snd_ics_put_double, \ | ||
87 | .private_value = addr } | ||
88 | |||
89 | static int snd_ics_info_double(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) | ||
90 | { | ||
91 | uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; | ||
92 | uinfo->count = 2; | ||
93 | uinfo->value.integer.min = 0; | ||
94 | uinfo->value.integer.max = 127; | ||
95 | return 0; | ||
96 | } | ||
97 | |||
98 | static int snd_ics_get_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) | ||
99 | { | ||
100 | snd_gus_card_t *gus = snd_kcontrol_chip(kcontrol); | ||
101 | unsigned long flags; | ||
102 | int addr = kcontrol->private_value & 0xff; | ||
103 | unsigned char left, right; | ||
104 | |||
105 | spin_lock_irqsave(&gus->reg_lock, flags); | ||
106 | left = gus->gf1.ics_regs[addr][0]; | ||
107 | right = gus->gf1.ics_regs[addr][1]; | ||
108 | spin_unlock_irqrestore(&gus->reg_lock, flags); | ||
109 | ucontrol->value.integer.value[0] = left & 127; | ||
110 | ucontrol->value.integer.value[1] = right & 127; | ||
111 | return 0; | ||
112 | } | ||
113 | |||
114 | static int snd_ics_put_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) | ||
115 | { | ||
116 | snd_gus_card_t *gus = snd_kcontrol_chip(kcontrol); | ||
117 | unsigned long flags; | ||
118 | int addr = kcontrol->private_value & 0xff; | ||
119 | int change; | ||
120 | unsigned char val1, val2, oval1, oval2, tmp; | ||
121 | |||
122 | val1 = ucontrol->value.integer.value[0] & 127; | ||
123 | val2 = ucontrol->value.integer.value[1] & 127; | ||
124 | spin_lock_irqsave(&gus->reg_lock, flags); | ||
125 | oval1 = gus->gf1.ics_regs[addr][0]; | ||
126 | oval2 = gus->gf1.ics_regs[addr][1]; | ||
127 | change = val1 != oval1 || val2 != oval2; | ||
128 | gus->gf1.ics_regs[addr][0] = val1; | ||
129 | gus->gf1.ics_regs[addr][1] = val2; | ||
130 | if (gus->ics_flag && gus->ics_flipped && | ||
131 | (addr == SNDRV_ICS_GF1_DEV || addr == SNDRV_ICS_MASTER_DEV)) { | ||
132 | tmp = val1; | ||
133 | val1 = val2; | ||
134 | val2 = tmp; | ||
135 | } | ||
136 | addr <<= 3; | ||
137 | outb(addr | 0, GUSP(gus, MIXCNTRLPORT)); | ||
138 | outb(1, GUSP(gus, MIXDATAPORT)); | ||
139 | outb(addr | 2, GUSP(gus, MIXCNTRLPORT)); | ||
140 | outb((unsigned char) val1, GUSP(gus, MIXDATAPORT)); | ||
141 | outb(addr | 1, GUSP(gus, MIXCNTRLPORT)); | ||
142 | outb(2, GUSP(gus, MIXDATAPORT)); | ||
143 | outb(addr | 3, GUSP(gus, MIXCNTRLPORT)); | ||
144 | outb((unsigned char) val2, GUSP(gus, MIXDATAPORT)); | ||
145 | spin_unlock_irqrestore(&gus->reg_lock, flags); | ||
146 | return change; | ||
147 | } | ||
148 | |||
149 | static snd_kcontrol_new_t snd_gf1_controls[] = { | ||
150 | GF1_SINGLE("Master Playback Switch", 0, 1, 1), | ||
151 | GF1_SINGLE("Line Switch", 0, 0, 1), | ||
152 | GF1_SINGLE("Mic Switch", 0, 2, 0) | ||
153 | }; | ||
154 | |||
155 | static snd_kcontrol_new_t snd_ics_controls[] = { | ||
156 | GF1_SINGLE("Master Playback Switch", 0, 1, 1), | ||
157 | ICS_DOUBLE("Master Playback Volume", 0, SNDRV_ICS_MASTER_DEV), | ||
158 | ICS_DOUBLE("Synth Playback Volume", 0, SNDRV_ICS_GF1_DEV), | ||
159 | GF1_SINGLE("Line Switch", 0, 0, 1), | ||
160 | ICS_DOUBLE("Line Playback Volume", 0, SNDRV_ICS_LINE_DEV), | ||
161 | GF1_SINGLE("Mic Switch", 0, 2, 0), | ||
162 | ICS_DOUBLE("Mic Playback Volume", 0, SNDRV_ICS_MIC_DEV), | ||
163 | ICS_DOUBLE("CD Playback Volume", 0, SNDRV_ICS_CD_DEV) | ||
164 | }; | ||
165 | |||
166 | int snd_gf1_new_mixer(snd_gus_card_t * gus) | ||
167 | { | ||
168 | snd_card_t *card; | ||
169 | unsigned int idx, max; | ||
170 | int err; | ||
171 | |||
172 | snd_assert(gus != NULL, return -EINVAL); | ||
173 | card = gus->card; | ||
174 | snd_assert(card != NULL, return -EINVAL); | ||
175 | |||
176 | if (gus->ics_flag) | ||
177 | snd_component_add(card, "ICS2101"); | ||
178 | if (card->mixername[0] == '\0') { | ||
179 | strcpy(card->mixername, gus->ics_flag ? "GF1,ICS2101" : "GF1"); | ||
180 | } else { | ||
181 | if (gus->ics_flag) | ||
182 | strcat(card->mixername, ",ICS2101"); | ||
183 | strcat(card->mixername, ",GF1"); | ||
184 | } | ||
185 | |||
186 | if (!gus->ics_flag) { | ||
187 | max = gus->ess_flag ? 1 : ARRAY_SIZE(snd_gf1_controls); | ||
188 | for (idx = 0; idx < max; idx++) { | ||
189 | if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_gf1_controls[idx], gus))) < 0) | ||
190 | return err; | ||
191 | } | ||
192 | } else { | ||
193 | for (idx = 0; idx < ARRAY_SIZE(snd_ics_controls); idx++) { | ||
194 | if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_ics_controls[idx], gus))) < 0) | ||
195 | return err; | ||
196 | } | ||
197 | } | ||
198 | return 0; | ||
199 | } | ||
diff --git a/sound/isa/gus/gus_pcm.c b/sound/isa/gus/gus_pcm.c new file mode 100644 index 000000000000..8995ad9c516d --- /dev/null +++ b/sound/isa/gus/gus_pcm.c | |||
@@ -0,0 +1,903 @@ | |||
1 | /* | ||
2 | * Copyright (c) by Jaroslav Kysela <perex@suse.cz> | ||
3 | * Routines for control of GF1 chip (PCM things) | ||
4 | * | ||
5 | * InterWave chips supports interleaved DMA, but this feature isn't used in | ||
6 | * this code. | ||
7 | * | ||
8 | * This code emulates autoinit DMA transfer for playback, recording by GF1 | ||
9 | * chip doesn't support autoinit DMA. | ||
10 | * | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or modify | ||
13 | * it under the terms of the GNU General Public License as published by | ||
14 | * the Free Software Foundation; either version 2 of the License, or | ||
15 | * (at your option) any later version. | ||
16 | * | ||
17 | * This program is distributed in the hope that it will be useful, | ||
18 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
20 | * GNU General Public License for more details. | ||
21 | * | ||
22 | * You should have received a copy of the GNU General Public License | ||
23 | * along with this program; if not, write to the Free Software | ||
24 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
25 | * | ||
26 | */ | ||
27 | |||
28 | #include <sound/driver.h> | ||
29 | #include <asm/dma.h> | ||
30 | #include <linux/slab.h> | ||
31 | #include <sound/core.h> | ||
32 | #include <sound/control.h> | ||
33 | #include <sound/gus.h> | ||
34 | #include <sound/pcm_params.h> | ||
35 | #include "gus_tables.h" | ||
36 | |||
37 | /* maximum rate */ | ||
38 | |||
39 | #define SNDRV_GF1_PCM_RATE 48000 | ||
40 | |||
41 | #define SNDRV_GF1_PCM_PFLG_NONE 0 | ||
42 | #define SNDRV_GF1_PCM_PFLG_ACTIVE (1<<0) | ||
43 | #define SNDRV_GF1_PCM_PFLG_NEUTRAL (2<<0) | ||
44 | |||
45 | typedef struct { | ||
46 | snd_gus_card_t * gus; | ||
47 | snd_pcm_substream_t * substream; | ||
48 | spinlock_t lock; | ||
49 | unsigned int voices; | ||
50 | snd_gus_voice_t *pvoices[2]; | ||
51 | unsigned int memory; | ||
52 | unsigned short flags; | ||
53 | unsigned char voice_ctrl, ramp_ctrl; | ||
54 | unsigned int bpos; | ||
55 | unsigned int blocks; | ||
56 | unsigned int block_size; | ||
57 | unsigned int dma_size; | ||
58 | wait_queue_head_t sleep; | ||
59 | atomic_t dma_count; | ||
60 | int final_volume; | ||
61 | } gus_pcm_private_t; | ||
62 | |||
63 | static int snd_gf1_pcm_use_dma = 1; | ||
64 | |||
65 | static void snd_gf1_pcm_block_change_ack(snd_gus_card_t * gus, void *private_data) | ||
66 | { | ||
67 | gus_pcm_private_t *pcmp = private_data; | ||
68 | |||
69 | if (pcmp) { | ||
70 | atomic_dec(&pcmp->dma_count); | ||
71 | wake_up(&pcmp->sleep); | ||
72 | } | ||
73 | } | ||
74 | |||
75 | static int snd_gf1_pcm_block_change(snd_pcm_substream_t * substream, | ||
76 | unsigned int offset, | ||
77 | unsigned int addr, | ||
78 | unsigned int count) | ||
79 | { | ||
80 | snd_gf1_dma_block_t block; | ||
81 | snd_pcm_runtime_t *runtime = substream->runtime; | ||
82 | gus_pcm_private_t *pcmp = runtime->private_data; | ||
83 | |||
84 | count += offset & 31; | ||
85 | offset &= ~31; | ||
86 | // snd_printk("block change - offset = 0x%x, count = 0x%x\n", offset, count); | ||
87 | memset(&block, 0, sizeof(block)); | ||
88 | block.cmd = SNDRV_GF1_DMA_IRQ; | ||
89 | if (snd_pcm_format_unsigned(runtime->format)) | ||
90 | block.cmd |= SNDRV_GF1_DMA_UNSIGNED; | ||
91 | if (snd_pcm_format_width(runtime->format) == 16) | ||
92 | block.cmd |= SNDRV_GF1_DMA_16BIT; | ||
93 | block.addr = addr & ~31; | ||
94 | block.buffer = runtime->dma_area + offset; | ||
95 | block.buf_addr = runtime->dma_addr + offset; | ||
96 | block.count = count; | ||
97 | block.private_data = pcmp; | ||
98 | block.ack = snd_gf1_pcm_block_change_ack; | ||
99 | if (!snd_gf1_dma_transfer_block(pcmp->gus, &block, 0, 0)) | ||
100 | atomic_inc(&pcmp->dma_count); | ||
101 | return 0; | ||
102 | } | ||
103 | |||
104 | static void snd_gf1_pcm_trigger_up(snd_pcm_substream_t * substream) | ||
105 | { | ||
106 | snd_pcm_runtime_t *runtime = substream->runtime; | ||
107 | gus_pcm_private_t *pcmp = runtime->private_data; | ||
108 | snd_gus_card_t * gus = pcmp->gus; | ||
109 | unsigned long flags; | ||
110 | unsigned char voice_ctrl, ramp_ctrl; | ||
111 | unsigned short rate; | ||
112 | unsigned int curr, begin, end; | ||
113 | unsigned short vol; | ||
114 | unsigned char pan; | ||
115 | unsigned int voice; | ||
116 | |||
117 | if (substream == NULL) | ||
118 | return; | ||
119 | spin_lock_irqsave(&pcmp->lock, flags); | ||
120 | if (pcmp->flags & SNDRV_GF1_PCM_PFLG_ACTIVE) { | ||
121 | spin_unlock_irqrestore(&pcmp->lock, flags); | ||
122 | return; | ||
123 | } | ||
124 | pcmp->flags |= SNDRV_GF1_PCM_PFLG_ACTIVE; | ||
125 | pcmp->final_volume = 0; | ||
126 | spin_unlock_irqrestore(&pcmp->lock, flags); | ||
127 | rate = snd_gf1_translate_freq(gus, runtime->rate << 4); | ||
128 | /* enable WAVE IRQ */ | ||
129 | voice_ctrl = snd_pcm_format_width(runtime->format) == 16 ? 0x24 : 0x20; | ||
130 | /* enable RAMP IRQ + rollover */ | ||
131 | ramp_ctrl = 0x24; | ||
132 | if (pcmp->blocks == 1) { | ||
133 | voice_ctrl |= 0x08; /* loop enable */ | ||
134 | ramp_ctrl &= ~0x04; /* disable rollover */ | ||
135 | } | ||
136 | for (voice = 0; voice < pcmp->voices; voice++) { | ||
137 | begin = pcmp->memory + voice * (pcmp->dma_size / runtime->channels); | ||
138 | curr = begin + (pcmp->bpos * pcmp->block_size) / runtime->channels; | ||
139 | end = curr + (pcmp->block_size / runtime->channels); | ||
140 | end -= snd_pcm_format_width(runtime->format) == 16 ? 2 : 1; | ||
141 | // snd_printk("init: curr=0x%x, begin=0x%x, end=0x%x, ctrl=0x%x, ramp=0x%x, rate=0x%x\n", curr, begin, end, voice_ctrl, ramp_ctrl, rate); | ||
142 | pan = runtime->channels == 2 ? (!voice ? 1 : 14) : 8; | ||
143 | vol = !voice ? gus->gf1.pcm_volume_level_left : gus->gf1.pcm_volume_level_right; | ||
144 | spin_lock_irqsave(&gus->reg_lock, flags); | ||
145 | snd_gf1_select_voice(gus, pcmp->pvoices[voice]->number); | ||
146 | snd_gf1_write8(gus, SNDRV_GF1_VB_PAN, pan); | ||
147 | snd_gf1_write16(gus, SNDRV_GF1_VW_FREQUENCY, rate); | ||
148 | snd_gf1_write_addr(gus, SNDRV_GF1_VA_START, begin << 4, voice_ctrl & 4); | ||
149 | snd_gf1_write_addr(gus, SNDRV_GF1_VA_END, end << 4, voice_ctrl & 4); | ||
150 | snd_gf1_write_addr(gus, SNDRV_GF1_VA_CURRENT, curr << 4, voice_ctrl & 4); | ||
151 | snd_gf1_write16(gus, SNDRV_GF1_VW_VOLUME, SNDRV_GF1_MIN_VOLUME << 4); | ||
152 | snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_RATE, 0x2f); | ||
153 | snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_START, SNDRV_GF1_MIN_OFFSET); | ||
154 | snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_END, vol >> 8); | ||
155 | snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_CONTROL, ramp_ctrl); | ||
156 | if (!gus->gf1.enh_mode) { | ||
157 | snd_gf1_delay(gus); | ||
158 | snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_CONTROL, ramp_ctrl); | ||
159 | } | ||
160 | spin_unlock_irqrestore(&gus->reg_lock, flags); | ||
161 | } | ||
162 | spin_lock_irqsave(&gus->reg_lock, flags); | ||
163 | for (voice = 0; voice < pcmp->voices; voice++) { | ||
164 | snd_gf1_select_voice(gus, pcmp->pvoices[voice]->number); | ||
165 | if (gus->gf1.enh_mode) | ||
166 | snd_gf1_write8(gus, SNDRV_GF1_VB_MODE, 0x00); /* deactivate voice */ | ||
167 | snd_gf1_write8(gus, SNDRV_GF1_VB_ADDRESS_CONTROL, voice_ctrl); | ||
168 | voice_ctrl &= ~0x20; | ||
169 | } | ||
170 | voice_ctrl |= 0x20; | ||
171 | if (!gus->gf1.enh_mode) { | ||
172 | snd_gf1_delay(gus); | ||
173 | for (voice = 0; voice < pcmp->voices; voice++) { | ||
174 | snd_gf1_select_voice(gus, pcmp->pvoices[voice]->number); | ||
175 | snd_gf1_write8(gus, SNDRV_GF1_VB_ADDRESS_CONTROL, voice_ctrl); | ||
176 | voice_ctrl &= ~0x20; /* disable IRQ for next voice */ | ||
177 | } | ||
178 | } | ||
179 | spin_unlock_irqrestore(&gus->reg_lock, flags); | ||
180 | } | ||
181 | |||
182 | static void snd_gf1_pcm_interrupt_wave(snd_gus_card_t * gus, snd_gus_voice_t *pvoice) | ||
183 | { | ||
184 | gus_pcm_private_t * pcmp; | ||
185 | snd_pcm_runtime_t * runtime; | ||
186 | unsigned char voice_ctrl, ramp_ctrl; | ||
187 | unsigned int idx; | ||
188 | unsigned int end, step; | ||
189 | |||
190 | if (!pvoice->private_data) { | ||
191 | snd_printd("snd_gf1_pcm: unknown wave irq?\n"); | ||
192 | snd_gf1_smart_stop_voice(gus, pvoice->number); | ||
193 | return; | ||
194 | } | ||
195 | pcmp = pvoice->private_data; | ||
196 | if (pcmp == NULL) { | ||
197 | snd_printd("snd_gf1_pcm: unknown wave irq?\n"); | ||
198 | snd_gf1_smart_stop_voice(gus, pvoice->number); | ||
199 | return; | ||
200 | } | ||
201 | gus = pcmp->gus; | ||
202 | runtime = pcmp->substream->runtime; | ||
203 | |||
204 | spin_lock(&gus->reg_lock); | ||
205 | snd_gf1_select_voice(gus, pvoice->number); | ||
206 | voice_ctrl = snd_gf1_read8(gus, SNDRV_GF1_VB_ADDRESS_CONTROL) & ~0x8b; | ||
207 | ramp_ctrl = (snd_gf1_read8(gus, SNDRV_GF1_VB_VOLUME_CONTROL) & ~0xa4) | 0x03; | ||
208 | #if 0 | ||
209 | snd_gf1_select_voice(gus, pvoice->number); | ||
210 | printk("position = 0x%x\n", (snd_gf1_read_addr(gus, SNDRV_GF1_VA_CURRENT, voice_ctrl & 4) >> 4)); | ||
211 | snd_gf1_select_voice(gus, pcmp->pvoices[1]->number); | ||
212 | printk("position = 0x%x\n", (snd_gf1_read_addr(gus, SNDRV_GF1_VA_CURRENT, voice_ctrl & 4) >> 4)); | ||
213 | snd_gf1_select_voice(gus, pvoice->number); | ||
214 | #endif | ||
215 | pcmp->bpos++; | ||
216 | pcmp->bpos %= pcmp->blocks; | ||
217 | if (pcmp->bpos + 1 >= pcmp->blocks) { /* last block? */ | ||
218 | voice_ctrl |= 0x08; /* enable loop */ | ||
219 | } else { | ||
220 | ramp_ctrl |= 0x04; /* enable rollover */ | ||
221 | } | ||
222 | end = pcmp->memory + (((pcmp->bpos + 1) * pcmp->block_size) / runtime->channels); | ||
223 | end -= voice_ctrl & 4 ? 2 : 1; | ||
224 | step = pcmp->dma_size / runtime->channels; | ||
225 | voice_ctrl |= 0x20; | ||
226 | if (!pcmp->final_volume) { | ||
227 | ramp_ctrl |= 0x20; | ||
228 | ramp_ctrl &= ~0x03; | ||
229 | } | ||
230 | for (idx = 0; idx < pcmp->voices; idx++, end += step) { | ||
231 | snd_gf1_select_voice(gus, pcmp->pvoices[idx]->number); | ||
232 | snd_gf1_write_addr(gus, SNDRV_GF1_VA_END, end << 4, voice_ctrl & 4); | ||
233 | snd_gf1_write8(gus, SNDRV_GF1_VB_ADDRESS_CONTROL, voice_ctrl); | ||
234 | snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_CONTROL, ramp_ctrl); | ||
235 | voice_ctrl &= ~0x20; | ||
236 | } | ||
237 | if (!gus->gf1.enh_mode) { | ||
238 | snd_gf1_delay(gus); | ||
239 | voice_ctrl |= 0x20; | ||
240 | for (idx = 0; idx < pcmp->voices; idx++) { | ||
241 | snd_gf1_select_voice(gus, pcmp->pvoices[idx]->number); | ||
242 | snd_gf1_write8(gus, SNDRV_GF1_VB_ADDRESS_CONTROL, voice_ctrl); | ||
243 | snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_CONTROL, ramp_ctrl); | ||
244 | voice_ctrl &= ~0x20; | ||
245 | } | ||
246 | } | ||
247 | spin_unlock(&gus->reg_lock); | ||
248 | |||
249 | snd_pcm_period_elapsed(pcmp->substream); | ||
250 | #if 0 | ||
251 | if ((runtime->flags & SNDRV_PCM_FLG_MMAP) && | ||
252 | *runtime->state == SNDRV_PCM_STATE_RUNNING) { | ||
253 | end = pcmp->bpos * pcmp->block_size; | ||
254 | if (runtime->channels > 1) { | ||
255 | snd_gf1_pcm_block_change(pcmp->substream, end, pcmp->memory + (end / 2), pcmp->block_size / 2); | ||
256 | snd_gf1_pcm_block_change(pcmp->substream, end + (pcmp->block_size / 2), pcmp->memory + (pcmp->dma_size / 2) + (end / 2), pcmp->block_size / 2); | ||
257 | } else { | ||
258 | snd_gf1_pcm_block_change(pcmp->substream, end, pcmp->memory + end, pcmp->block_size); | ||
259 | } | ||
260 | } | ||
261 | #endif | ||
262 | } | ||
263 | |||
264 | static void snd_gf1_pcm_interrupt_volume(snd_gus_card_t * gus, snd_gus_voice_t * pvoice) | ||
265 | { | ||
266 | unsigned short vol; | ||
267 | int cvoice; | ||
268 | gus_pcm_private_t *pcmp = pvoice->private_data; | ||
269 | |||
270 | /* stop ramp, but leave rollover bit untouched */ | ||
271 | spin_lock(&gus->reg_lock); | ||
272 | snd_gf1_select_voice(gus, pvoice->number); | ||
273 | snd_gf1_ctrl_stop(gus, SNDRV_GF1_VB_VOLUME_CONTROL); | ||
274 | spin_unlock(&gus->reg_lock); | ||
275 | if (pcmp == NULL) | ||
276 | return; | ||
277 | /* are we active? */ | ||
278 | if (!(pcmp->flags & SNDRV_GF1_PCM_PFLG_ACTIVE)) | ||
279 | return; | ||
280 | /* load real volume - better precision */ | ||
281 | cvoice = pcmp->pvoices[0] == pvoice ? 0 : 1; | ||
282 | if (pcmp->substream == NULL) | ||
283 | return; | ||
284 | vol = !cvoice ? gus->gf1.pcm_volume_level_left : gus->gf1.pcm_volume_level_right; | ||
285 | spin_lock(&gus->reg_lock); | ||
286 | snd_gf1_select_voice(gus, pvoice->number); | ||
287 | snd_gf1_write16(gus, SNDRV_GF1_VW_VOLUME, vol); | ||
288 | pcmp->final_volume = 1; | ||
289 | spin_unlock(&gus->reg_lock); | ||
290 | } | ||
291 | |||
292 | static void snd_gf1_pcm_volume_change(snd_gus_card_t * gus) | ||
293 | { | ||
294 | } | ||
295 | |||
296 | static int snd_gf1_pcm_poke_block(snd_gus_card_t *gus, unsigned char *buf, | ||
297 | unsigned int pos, unsigned int count, | ||
298 | int w16, int invert) | ||
299 | { | ||
300 | unsigned int len; | ||
301 | unsigned long flags; | ||
302 | |||
303 | // printk("poke block; buf = 0x%x, pos = %i, count = %i, port = 0x%x\n", (int)buf, pos, count, gus->gf1.port); | ||
304 | while (count > 0) { | ||
305 | len = count; | ||
306 | if (len > 512) /* limit, to allow IRQ */ | ||
307 | len = 512; | ||
308 | count -= len; | ||
309 | if (gus->interwave) { | ||
310 | spin_lock_irqsave(&gus->reg_lock, flags); | ||
311 | snd_gf1_write8(gus, SNDRV_GF1_GB_MEMORY_CONTROL, 0x01 | (invert ? 0x08 : 0x00)); | ||
312 | snd_gf1_dram_addr(gus, pos); | ||
313 | if (w16) { | ||
314 | outb(SNDRV_GF1_GW_DRAM_IO16, GUSP(gus, GF1REGSEL)); | ||
315 | outsw(GUSP(gus, GF1DATALOW), buf, len >> 1); | ||
316 | } else { | ||
317 | outsb(GUSP(gus, DRAM), buf, len); | ||
318 | } | ||
319 | spin_unlock_irqrestore(&gus->reg_lock, flags); | ||
320 | buf += 512; | ||
321 | pos += 512; | ||
322 | } else { | ||
323 | invert = invert ? 0x80 : 0x00; | ||
324 | if (w16) { | ||
325 | len >>= 1; | ||
326 | while (len--) { | ||
327 | snd_gf1_poke(gus, pos++, *buf++); | ||
328 | snd_gf1_poke(gus, pos++, *buf++ ^ invert); | ||
329 | } | ||
330 | } else { | ||
331 | while (len--) | ||
332 | snd_gf1_poke(gus, pos++, *buf++ ^ invert); | ||
333 | } | ||
334 | } | ||
335 | if (count > 0 && !in_interrupt()) { | ||
336 | set_current_state(TASK_INTERRUPTIBLE); | ||
337 | schedule_timeout(1); | ||
338 | if (signal_pending(current)) | ||
339 | return -EAGAIN; | ||
340 | } | ||
341 | } | ||
342 | return 0; | ||
343 | } | ||
344 | |||
345 | static int snd_gf1_pcm_playback_copy(snd_pcm_substream_t *substream, | ||
346 | int voice, | ||
347 | snd_pcm_uframes_t pos, | ||
348 | void __user *src, | ||
349 | snd_pcm_uframes_t count) | ||
350 | { | ||
351 | snd_pcm_runtime_t *runtime = substream->runtime; | ||
352 | gus_pcm_private_t *pcmp = runtime->private_data; | ||
353 | unsigned int bpos, len; | ||
354 | |||
355 | bpos = samples_to_bytes(runtime, pos) + (voice * (pcmp->dma_size / 2)); | ||
356 | len = samples_to_bytes(runtime, count); | ||
357 | snd_assert(bpos <= pcmp->dma_size, return -EIO); | ||
358 | snd_assert(bpos + len <= pcmp->dma_size, return -EIO); | ||
359 | if (copy_from_user(runtime->dma_area + bpos, src, len)) | ||
360 | return -EFAULT; | ||
361 | if (snd_gf1_pcm_use_dma && len > 32) { | ||
362 | return snd_gf1_pcm_block_change(substream, bpos, pcmp->memory + bpos, len); | ||
363 | } else { | ||
364 | snd_gus_card_t *gus = pcmp->gus; | ||
365 | int err, w16, invert; | ||
366 | |||
367 | w16 = (snd_pcm_format_width(runtime->format) == 16); | ||
368 | invert = snd_pcm_format_unsigned(runtime->format); | ||
369 | if ((err = snd_gf1_pcm_poke_block(gus, runtime->dma_area + bpos, pcmp->memory + bpos, len, w16, invert)) < 0) | ||
370 | return err; | ||
371 | } | ||
372 | return 0; | ||
373 | } | ||
374 | |||
375 | static int snd_gf1_pcm_playback_silence(snd_pcm_substream_t *substream, | ||
376 | int voice, | ||
377 | snd_pcm_uframes_t pos, | ||
378 | snd_pcm_uframes_t count) | ||
379 | { | ||
380 | snd_pcm_runtime_t *runtime = substream->runtime; | ||
381 | gus_pcm_private_t *pcmp = runtime->private_data; | ||
382 | unsigned int bpos, len; | ||
383 | |||
384 | bpos = samples_to_bytes(runtime, pos) + (voice * (pcmp->dma_size / 2)); | ||
385 | len = samples_to_bytes(runtime, count); | ||
386 | snd_assert(bpos <= pcmp->dma_size, return -EIO); | ||
387 | snd_assert(bpos + len <= pcmp->dma_size, return -EIO); | ||
388 | snd_pcm_format_set_silence(runtime->format, runtime->dma_area + bpos, count); | ||
389 | if (snd_gf1_pcm_use_dma && len > 32) { | ||
390 | return snd_gf1_pcm_block_change(substream, bpos, pcmp->memory + bpos, len); | ||
391 | } else { | ||
392 | snd_gus_card_t *gus = pcmp->gus; | ||
393 | int err, w16, invert; | ||
394 | |||
395 | w16 = (snd_pcm_format_width(runtime->format) == 16); | ||
396 | invert = snd_pcm_format_unsigned(runtime->format); | ||
397 | if ((err = snd_gf1_pcm_poke_block(gus, runtime->dma_area + bpos, pcmp->memory + bpos, len, w16, invert)) < 0) | ||
398 | return err; | ||
399 | } | ||
400 | return 0; | ||
401 | } | ||
402 | |||
403 | static int snd_gf1_pcm_playback_hw_params(snd_pcm_substream_t * substream, | ||
404 | snd_pcm_hw_params_t * hw_params) | ||
405 | { | ||
406 | snd_gus_card_t *gus = snd_pcm_substream_chip(substream); | ||
407 | snd_pcm_runtime_t *runtime = substream->runtime; | ||
408 | gus_pcm_private_t *pcmp = runtime->private_data; | ||
409 | int err; | ||
410 | |||
411 | if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0) | ||
412 | return err; | ||
413 | if (err > 0) { /* change */ | ||
414 | snd_gf1_mem_block_t *block; | ||
415 | if (pcmp->memory > 0) { | ||
416 | snd_gf1_mem_free(&gus->gf1.mem_alloc, pcmp->memory); | ||
417 | pcmp->memory = 0; | ||
418 | } | ||
419 | if ((block = snd_gf1_mem_alloc(&gus->gf1.mem_alloc, | ||
420 | SNDRV_GF1_MEM_OWNER_DRIVER, | ||
421 | "GF1 PCM", | ||
422 | runtime->dma_bytes, 1, 32, | ||
423 | NULL)) == NULL) | ||
424 | return -ENOMEM; | ||
425 | pcmp->memory = block->ptr; | ||
426 | } | ||
427 | pcmp->voices = params_channels(hw_params); | ||
428 | if (pcmp->pvoices[0] == NULL) { | ||
429 | if ((pcmp->pvoices[0] = snd_gf1_alloc_voice(pcmp->gus, SNDRV_GF1_VOICE_TYPE_PCM, 0, 0)) == NULL) | ||
430 | return -ENOMEM; | ||
431 | pcmp->pvoices[0]->handler_wave = snd_gf1_pcm_interrupt_wave; | ||
432 | pcmp->pvoices[0]->handler_volume = snd_gf1_pcm_interrupt_volume; | ||
433 | pcmp->pvoices[0]->volume_change = snd_gf1_pcm_volume_change; | ||
434 | pcmp->pvoices[0]->private_data = pcmp; | ||
435 | } | ||
436 | if (pcmp->voices > 1 && pcmp->pvoices[1] == NULL) { | ||
437 | if ((pcmp->pvoices[1] = snd_gf1_alloc_voice(pcmp->gus, SNDRV_GF1_VOICE_TYPE_PCM, 0, 0)) == NULL) | ||
438 | return -ENOMEM; | ||
439 | pcmp->pvoices[1]->handler_wave = snd_gf1_pcm_interrupt_wave; | ||
440 | pcmp->pvoices[1]->handler_volume = snd_gf1_pcm_interrupt_volume; | ||
441 | pcmp->pvoices[1]->volume_change = snd_gf1_pcm_volume_change; | ||
442 | pcmp->pvoices[1]->private_data = pcmp; | ||
443 | } else if (pcmp->voices == 1) { | ||
444 | if (pcmp->pvoices[1]) { | ||
445 | snd_gf1_free_voice(pcmp->gus, pcmp->pvoices[1]); | ||
446 | pcmp->pvoices[1] = NULL; | ||
447 | } | ||
448 | } | ||
449 | return 0; | ||
450 | } | ||
451 | |||
452 | static int snd_gf1_pcm_playback_hw_free(snd_pcm_substream_t * substream) | ||
453 | { | ||
454 | snd_pcm_runtime_t *runtime = substream->runtime; | ||
455 | gus_pcm_private_t *pcmp = runtime->private_data; | ||
456 | |||
457 | snd_pcm_lib_free_pages(substream); | ||
458 | if (pcmp->pvoices[0]) { | ||
459 | snd_gf1_free_voice(pcmp->gus, pcmp->pvoices[0]); | ||
460 | pcmp->pvoices[0] = NULL; | ||
461 | } | ||
462 | if (pcmp->pvoices[1]) { | ||
463 | snd_gf1_free_voice(pcmp->gus, pcmp->pvoices[1]); | ||
464 | pcmp->pvoices[1] = NULL; | ||
465 | } | ||
466 | if (pcmp->memory > 0) { | ||
467 | snd_gf1_mem_free(&pcmp->gus->gf1.mem_alloc, pcmp->memory); | ||
468 | pcmp->memory = 0; | ||
469 | } | ||
470 | return 0; | ||
471 | } | ||
472 | |||
473 | static int snd_gf1_pcm_playback_prepare(snd_pcm_substream_t * substream) | ||
474 | { | ||
475 | snd_pcm_runtime_t *runtime = substream->runtime; | ||
476 | gus_pcm_private_t *pcmp = runtime->private_data; | ||
477 | |||
478 | pcmp->bpos = 0; | ||
479 | pcmp->dma_size = snd_pcm_lib_buffer_bytes(substream); | ||
480 | pcmp->block_size = snd_pcm_lib_period_bytes(substream); | ||
481 | pcmp->blocks = pcmp->dma_size / pcmp->block_size; | ||
482 | return 0; | ||
483 | } | ||
484 | |||
485 | static int snd_gf1_pcm_playback_trigger(snd_pcm_substream_t * substream, | ||
486 | int cmd) | ||
487 | { | ||
488 | snd_gus_card_t *gus = snd_pcm_substream_chip(substream); | ||
489 | snd_pcm_runtime_t *runtime = substream->runtime; | ||
490 | gus_pcm_private_t *pcmp = runtime->private_data; | ||
491 | int voice; | ||
492 | |||
493 | if (cmd == SNDRV_PCM_TRIGGER_START) { | ||
494 | snd_gf1_pcm_trigger_up(substream); | ||
495 | } else if (cmd == SNDRV_PCM_TRIGGER_STOP) { | ||
496 | spin_lock(&pcmp->lock); | ||
497 | pcmp->flags &= ~SNDRV_GF1_PCM_PFLG_ACTIVE; | ||
498 | spin_unlock(&pcmp->lock); | ||
499 | voice = pcmp->pvoices[0]->number; | ||
500 | snd_gf1_stop_voices(gus, voice, voice); | ||
501 | if (pcmp->pvoices[1]) { | ||
502 | voice = pcmp->pvoices[1]->number; | ||
503 | snd_gf1_stop_voices(gus, voice, voice); | ||
504 | } | ||
505 | } else { | ||
506 | return -EINVAL; | ||
507 | } | ||
508 | return 0; | ||
509 | } | ||
510 | |||
511 | static snd_pcm_uframes_t snd_gf1_pcm_playback_pointer(snd_pcm_substream_t * substream) | ||
512 | { | ||
513 | snd_gus_card_t *gus = snd_pcm_substream_chip(substream); | ||
514 | snd_pcm_runtime_t *runtime = substream->runtime; | ||
515 | gus_pcm_private_t *pcmp = runtime->private_data; | ||
516 | unsigned int pos; | ||
517 | unsigned char voice_ctrl; | ||
518 | |||
519 | pos = 0; | ||
520 | spin_lock(&gus->reg_lock); | ||
521 | if (pcmp->flags & SNDRV_GF1_PCM_PFLG_ACTIVE) { | ||
522 | snd_gf1_select_voice(gus, pcmp->pvoices[0]->number); | ||
523 | voice_ctrl = snd_gf1_read8(gus, SNDRV_GF1_VB_ADDRESS_CONTROL); | ||
524 | pos = (snd_gf1_read_addr(gus, SNDRV_GF1_VA_CURRENT, voice_ctrl & 4) >> 4) - pcmp->memory; | ||
525 | if (substream->runtime->channels > 1) | ||
526 | pos <<= 1; | ||
527 | pos = bytes_to_frames(runtime, pos); | ||
528 | } | ||
529 | spin_unlock(&gus->reg_lock); | ||
530 | return pos; | ||
531 | } | ||
532 | |||
533 | static ratnum_t clock = { | ||
534 | .num = 9878400/16, | ||
535 | .den_min = 2, | ||
536 | .den_max = 257, | ||
537 | .den_step = 1, | ||
538 | }; | ||
539 | |||
540 | static snd_pcm_hw_constraint_ratnums_t hw_constraints_clocks = { | ||
541 | .nrats = 1, | ||
542 | .rats = &clock, | ||
543 | }; | ||
544 | |||
545 | static int snd_gf1_pcm_capture_hw_params(snd_pcm_substream_t * substream, | ||
546 | snd_pcm_hw_params_t * hw_params) | ||
547 | { | ||
548 | snd_gus_card_t *gus = snd_pcm_substream_chip(substream); | ||
549 | |||
550 | gus->c_dma_size = params_buffer_bytes(hw_params); | ||
551 | gus->c_period_size = params_period_bytes(hw_params); | ||
552 | gus->c_pos = 0; | ||
553 | gus->gf1.pcm_rcntrl_reg = 0x21; /* IRQ at end, enable & start */ | ||
554 | if (params_channels(hw_params) > 1) | ||
555 | gus->gf1.pcm_rcntrl_reg |= 2; | ||
556 | if (gus->gf1.dma2 > 3) | ||
557 | gus->gf1.pcm_rcntrl_reg |= 4; | ||
558 | if (snd_pcm_format_unsigned(params_format(hw_params))) | ||
559 | gus->gf1.pcm_rcntrl_reg |= 0x80; | ||
560 | return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params)); | ||
561 | } | ||
562 | |||
563 | static int snd_gf1_pcm_capture_hw_free(snd_pcm_substream_t * substream) | ||
564 | { | ||
565 | return snd_pcm_lib_free_pages(substream); | ||
566 | } | ||
567 | |||
568 | static int snd_gf1_pcm_capture_prepare(snd_pcm_substream_t * substream) | ||
569 | { | ||
570 | snd_gus_card_t *gus = snd_pcm_substream_chip(substream); | ||
571 | snd_pcm_runtime_t *runtime = substream->runtime; | ||
572 | |||
573 | snd_gf1_i_write8(gus, SNDRV_GF1_GB_RECORD_RATE, runtime->rate_den - 2); | ||
574 | snd_gf1_i_write8(gus, SNDRV_GF1_GB_REC_DMA_CONTROL, 0); /* disable sampling */ | ||
575 | snd_gf1_i_look8(gus, SNDRV_GF1_GB_REC_DMA_CONTROL); /* Sampling Control Register */ | ||
576 | snd_dma_program(gus->gf1.dma2, runtime->dma_addr, gus->c_period_size, DMA_MODE_READ); | ||
577 | return 0; | ||
578 | } | ||
579 | |||
580 | static int snd_gf1_pcm_capture_trigger(snd_pcm_substream_t * substream, | ||
581 | int cmd) | ||
582 | { | ||
583 | snd_gus_card_t *gus = snd_pcm_substream_chip(substream); | ||
584 | int val; | ||
585 | |||
586 | if (cmd == SNDRV_PCM_TRIGGER_START) { | ||
587 | val = gus->gf1.pcm_rcntrl_reg; | ||
588 | } else if (cmd == SNDRV_PCM_TRIGGER_STOP) { | ||
589 | val = 0; | ||
590 | } else { | ||
591 | return -EINVAL; | ||
592 | } | ||
593 | |||
594 | spin_lock(&gus->reg_lock); | ||
595 | snd_gf1_write8(gus, SNDRV_GF1_GB_REC_DMA_CONTROL, val); | ||
596 | snd_gf1_look8(gus, SNDRV_GF1_GB_REC_DMA_CONTROL); | ||
597 | spin_unlock(&gus->reg_lock); | ||
598 | return 0; | ||
599 | } | ||
600 | |||
601 | static snd_pcm_uframes_t snd_gf1_pcm_capture_pointer(snd_pcm_substream_t * substream) | ||
602 | { | ||
603 | snd_gus_card_t *gus = snd_pcm_substream_chip(substream); | ||
604 | int pos = snd_dma_pointer(gus->gf1.dma2, gus->c_period_size); | ||
605 | pos = bytes_to_frames(substream->runtime, (gus->c_pos + pos) % gus->c_dma_size); | ||
606 | return pos; | ||
607 | } | ||
608 | |||
609 | static void snd_gf1_pcm_interrupt_dma_read(snd_gus_card_t * gus) | ||
610 | { | ||
611 | snd_gf1_i_write8(gus, SNDRV_GF1_GB_REC_DMA_CONTROL, 0); /* disable sampling */ | ||
612 | snd_gf1_i_look8(gus, SNDRV_GF1_GB_REC_DMA_CONTROL); /* Sampling Control Register */ | ||
613 | if (gus->pcm_cap_substream != NULL) { | ||
614 | snd_gf1_pcm_capture_prepare(gus->pcm_cap_substream); | ||
615 | snd_gf1_pcm_capture_trigger(gus->pcm_cap_substream, SNDRV_PCM_TRIGGER_START); | ||
616 | gus->c_pos += gus->c_period_size; | ||
617 | snd_pcm_period_elapsed(gus->pcm_cap_substream); | ||
618 | } | ||
619 | } | ||
620 | |||
621 | static snd_pcm_hardware_t snd_gf1_pcm_playback = | ||
622 | { | ||
623 | .info = SNDRV_PCM_INFO_NONINTERLEAVED, | ||
624 | .formats = (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U8 | | ||
625 | SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE), | ||
626 | .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, | ||
627 | .rate_min = 5510, | ||
628 | .rate_max = 48000, | ||
629 | .channels_min = 1, | ||
630 | .channels_max = 2, | ||
631 | .buffer_bytes_max = (128*1024), | ||
632 | .period_bytes_min = 64, | ||
633 | .period_bytes_max = (128*1024), | ||
634 | .periods_min = 1, | ||
635 | .periods_max = 1024, | ||
636 | .fifo_size = 0, | ||
637 | }; | ||
638 | |||
639 | static snd_pcm_hardware_t snd_gf1_pcm_capture = | ||
640 | { | ||
641 | .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | | ||
642 | SNDRV_PCM_INFO_MMAP_VALID), | ||
643 | .formats = SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U8, | ||
644 | .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_44100, | ||
645 | .rate_min = 5510, | ||
646 | .rate_max = 44100, | ||
647 | .channels_min = 1, | ||
648 | .channels_max = 2, | ||
649 | .buffer_bytes_max = (128*1024), | ||
650 | .period_bytes_min = 64, | ||
651 | .period_bytes_max = (128*1024), | ||
652 | .periods_min = 1, | ||
653 | .periods_max = 1024, | ||
654 | .fifo_size = 0, | ||
655 | }; | ||
656 | |||
657 | static void snd_gf1_pcm_playback_free(snd_pcm_runtime_t *runtime) | ||
658 | { | ||
659 | gus_pcm_private_t * pcmp = runtime->private_data; | ||
660 | kfree(pcmp); | ||
661 | } | ||
662 | |||
663 | static int snd_gf1_pcm_playback_open(snd_pcm_substream_t *substream) | ||
664 | { | ||
665 | gus_pcm_private_t *pcmp; | ||
666 | snd_gus_card_t *gus = snd_pcm_substream_chip(substream); | ||
667 | snd_pcm_runtime_t *runtime = substream->runtime; | ||
668 | int err; | ||
669 | |||
670 | pcmp = kcalloc(1, sizeof(*pcmp), GFP_KERNEL); | ||
671 | if (pcmp == NULL) | ||
672 | return -ENOMEM; | ||
673 | pcmp->gus = gus; | ||
674 | spin_lock_init(&pcmp->lock); | ||
675 | init_waitqueue_head(&pcmp->sleep); | ||
676 | atomic_set(&pcmp->dma_count, 0); | ||
677 | |||
678 | runtime->private_data = pcmp; | ||
679 | runtime->private_free = snd_gf1_pcm_playback_free; | ||
680 | |||
681 | #if 0 | ||
682 | printk("playback.buffer = 0x%lx, gf1.pcm_buffer = 0x%lx\n", (long) pcm->playback.buffer, (long) gus->gf1.pcm_buffer); | ||
683 | #endif | ||
684 | if ((err = snd_gf1_dma_init(gus)) < 0) | ||
685 | return err; | ||
686 | pcmp->flags = SNDRV_GF1_PCM_PFLG_NONE; | ||
687 | pcmp->substream = substream; | ||
688 | runtime->hw = snd_gf1_pcm_playback; | ||
689 | snd_pcm_limit_isa_dma_size(gus->gf1.dma1, &runtime->hw.buffer_bytes_max); | ||
690 | snd_pcm_limit_isa_dma_size(gus->gf1.dma1, &runtime->hw.period_bytes_max); | ||
691 | snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 64); | ||
692 | return 0; | ||
693 | } | ||
694 | |||
695 | static int snd_gf1_pcm_playback_close(snd_pcm_substream_t * substream) | ||
696 | { | ||
697 | snd_gus_card_t *gus = snd_pcm_substream_chip(substream); | ||
698 | snd_pcm_runtime_t *runtime = substream->runtime; | ||
699 | gus_pcm_private_t *pcmp = runtime->private_data; | ||
700 | |||
701 | if (!wait_event_timeout(pcmp->sleep, (atomic_read(&pcmp->dma_count) <= 0), 2*HZ)) | ||
702 | snd_printk("gf1 pcm - serious DMA problem\n"); | ||
703 | |||
704 | snd_gf1_dma_done(gus); | ||
705 | return 0; | ||
706 | } | ||
707 | |||
708 | static int snd_gf1_pcm_capture_open(snd_pcm_substream_t * substream) | ||
709 | { | ||
710 | snd_pcm_runtime_t *runtime = substream->runtime; | ||
711 | snd_gus_card_t *gus = snd_pcm_substream_chip(substream); | ||
712 | |||
713 | gus->gf1.interrupt_handler_dma_read = snd_gf1_pcm_interrupt_dma_read; | ||
714 | gus->pcm_cap_substream = substream; | ||
715 | substream->runtime->hw = snd_gf1_pcm_capture; | ||
716 | snd_pcm_limit_isa_dma_size(gus->gf1.dma2, &runtime->hw.buffer_bytes_max); | ||
717 | snd_pcm_limit_isa_dma_size(gus->gf1.dma2, &runtime->hw.period_bytes_max); | ||
718 | snd_pcm_hw_constraint_ratnums(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, | ||
719 | &hw_constraints_clocks); | ||
720 | return 0; | ||
721 | } | ||
722 | |||
723 | static int snd_gf1_pcm_capture_close(snd_pcm_substream_t * substream) | ||
724 | { | ||
725 | snd_gus_card_t *gus = snd_pcm_substream_chip(substream); | ||
726 | |||
727 | gus->pcm_cap_substream = NULL; | ||
728 | snd_gf1_set_default_handlers(gus, SNDRV_GF1_HANDLER_DMA_READ); | ||
729 | return 0; | ||
730 | } | ||
731 | |||
732 | static void snd_gf1_pcm_free(snd_pcm_t *pcm) | ||
733 | { | ||
734 | snd_gus_card_t *gus = pcm->private_data; | ||
735 | gus->pcm = NULL; | ||
736 | snd_pcm_lib_preallocate_free_for_all(pcm); | ||
737 | } | ||
738 | |||
739 | static int snd_gf1_pcm_volume_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) | ||
740 | { | ||
741 | uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; | ||
742 | uinfo->count = 2; | ||
743 | uinfo->value.integer.min = 0; | ||
744 | uinfo->value.integer.max = 127; | ||
745 | return 0; | ||
746 | } | ||
747 | |||
748 | static int snd_gf1_pcm_volume_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) | ||
749 | { | ||
750 | snd_gus_card_t *gus = snd_kcontrol_chip(kcontrol); | ||
751 | unsigned long flags; | ||
752 | |||
753 | spin_lock_irqsave(&gus->pcm_volume_level_lock, flags); | ||
754 | ucontrol->value.integer.value[0] = gus->gf1.pcm_volume_level_left1; | ||
755 | ucontrol->value.integer.value[1] = gus->gf1.pcm_volume_level_right1; | ||
756 | spin_unlock_irqrestore(&gus->pcm_volume_level_lock, flags); | ||
757 | return 0; | ||
758 | } | ||
759 | |||
760 | static int snd_gf1_pcm_volume_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) | ||
761 | { | ||
762 | snd_gus_card_t *gus = snd_kcontrol_chip(kcontrol); | ||
763 | unsigned long flags; | ||
764 | int change; | ||
765 | unsigned int idx; | ||
766 | unsigned short val1, val2, vol; | ||
767 | gus_pcm_private_t *pcmp; | ||
768 | snd_gus_voice_t *pvoice; | ||
769 | |||
770 | val1 = ucontrol->value.integer.value[0] & 127; | ||
771 | val2 = ucontrol->value.integer.value[1] & 127; | ||
772 | spin_lock_irqsave(&gus->pcm_volume_level_lock, flags); | ||
773 | change = val1 != gus->gf1.pcm_volume_level_left1 || | ||
774 | val2 != gus->gf1.pcm_volume_level_right1; | ||
775 | gus->gf1.pcm_volume_level_left1 = val1; | ||
776 | gus->gf1.pcm_volume_level_right1 = val2; | ||
777 | gus->gf1.pcm_volume_level_left = snd_gf1_lvol_to_gvol_raw(val1 << 9) << 4; | ||
778 | gus->gf1.pcm_volume_level_right = snd_gf1_lvol_to_gvol_raw(val2 << 9) << 4; | ||
779 | spin_unlock_irqrestore(&gus->pcm_volume_level_lock, flags); | ||
780 | /* are we active? */ | ||
781 | spin_lock_irqsave(&gus->voice_alloc, flags); | ||
782 | for (idx = 0; idx < 32; idx++) { | ||
783 | pvoice = &gus->gf1.voices[idx]; | ||
784 | if (!pvoice->pcm) | ||
785 | continue; | ||
786 | pcmp = pvoice->private_data; | ||
787 | if (!(pcmp->flags & SNDRV_GF1_PCM_PFLG_ACTIVE)) | ||
788 | continue; | ||
789 | /* load real volume - better precision */ | ||
790 | spin_lock_irqsave(&gus->reg_lock, flags); | ||
791 | snd_gf1_select_voice(gus, pvoice->number); | ||
792 | snd_gf1_ctrl_stop(gus, SNDRV_GF1_VB_VOLUME_CONTROL); | ||
793 | vol = pvoice == pcmp->pvoices[0] ? gus->gf1.pcm_volume_level_left : gus->gf1.pcm_volume_level_right; | ||
794 | snd_gf1_write16(gus, SNDRV_GF1_VW_VOLUME, vol); | ||
795 | pcmp->final_volume = 1; | ||
796 | spin_unlock_irqrestore(&gus->reg_lock, flags); | ||
797 | } | ||
798 | spin_unlock_irqrestore(&gus->voice_alloc, flags); | ||
799 | return change; | ||
800 | } | ||
801 | |||
802 | static snd_kcontrol_new_t snd_gf1_pcm_volume_control = | ||
803 | { | ||
804 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
805 | .name = "PCM Playback Volume", | ||
806 | .info = snd_gf1_pcm_volume_info, | ||
807 | .get = snd_gf1_pcm_volume_get, | ||
808 | .put = snd_gf1_pcm_volume_put | ||
809 | }; | ||
810 | |||
811 | static snd_kcontrol_new_t snd_gf1_pcm_volume_control1 = | ||
812 | { | ||
813 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
814 | .name = "GPCM Playback Volume", | ||
815 | .info = snd_gf1_pcm_volume_info, | ||
816 | .get = snd_gf1_pcm_volume_get, | ||
817 | .put = snd_gf1_pcm_volume_put | ||
818 | }; | ||
819 | |||
820 | static snd_pcm_ops_t snd_gf1_pcm_playback_ops = { | ||
821 | .open = snd_gf1_pcm_playback_open, | ||
822 | .close = snd_gf1_pcm_playback_close, | ||
823 | .ioctl = snd_pcm_lib_ioctl, | ||
824 | .hw_params = snd_gf1_pcm_playback_hw_params, | ||
825 | .hw_free = snd_gf1_pcm_playback_hw_free, | ||
826 | .prepare = snd_gf1_pcm_playback_prepare, | ||
827 | .trigger = snd_gf1_pcm_playback_trigger, | ||
828 | .pointer = snd_gf1_pcm_playback_pointer, | ||
829 | .copy = snd_gf1_pcm_playback_copy, | ||
830 | .silence = snd_gf1_pcm_playback_silence, | ||
831 | }; | ||
832 | |||
833 | static snd_pcm_ops_t snd_gf1_pcm_capture_ops = { | ||
834 | .open = snd_gf1_pcm_capture_open, | ||
835 | .close = snd_gf1_pcm_capture_close, | ||
836 | .ioctl = snd_pcm_lib_ioctl, | ||
837 | .hw_params = snd_gf1_pcm_capture_hw_params, | ||
838 | .hw_free = snd_gf1_pcm_capture_hw_free, | ||
839 | .prepare = snd_gf1_pcm_capture_prepare, | ||
840 | .trigger = snd_gf1_pcm_capture_trigger, | ||
841 | .pointer = snd_gf1_pcm_capture_pointer, | ||
842 | }; | ||
843 | |||
844 | int snd_gf1_pcm_new(snd_gus_card_t * gus, int pcm_dev, int control_index, snd_pcm_t ** rpcm) | ||
845 | { | ||
846 | snd_card_t *card; | ||
847 | snd_kcontrol_t *kctl; | ||
848 | snd_pcm_t *pcm; | ||
849 | snd_pcm_substream_t *substream; | ||
850 | int capture, err; | ||
851 | |||
852 | if (rpcm) | ||
853 | *rpcm = NULL; | ||
854 | card = gus->card; | ||
855 | capture = !gus->interwave && !gus->ess_flag && !gus->ace_flag ? 1 : 0; | ||
856 | err = snd_pcm_new(card, | ||
857 | gus->interwave ? "AMD InterWave" : "GF1", | ||
858 | pcm_dev, | ||
859 | gus->gf1.pcm_channels / 2, | ||
860 | capture, | ||
861 | &pcm); | ||
862 | if (err < 0) | ||
863 | return err; | ||
864 | pcm->private_data = gus; | ||
865 | pcm->private_free = snd_gf1_pcm_free; | ||
866 | /* playback setup */ | ||
867 | snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_gf1_pcm_playback_ops); | ||
868 | |||
869 | for (substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; substream; substream = substream->next) | ||
870 | snd_pcm_lib_preallocate_pages(substream, SNDRV_DMA_TYPE_DEV, | ||
871 | snd_dma_isa_data(), | ||
872 | 64*1024, gus->gf1.dma1 > 3 ? 128*1024 : 64*1024); | ||
873 | |||
874 | pcm->info_flags = 0; | ||
875 | pcm->dev_subclass = SNDRV_PCM_SUBCLASS_GENERIC_MIX; | ||
876 | if (capture) { | ||
877 | snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_gf1_pcm_capture_ops); | ||
878 | if (gus->gf1.dma2 == gus->gf1.dma1) | ||
879 | pcm->info_flags |= SNDRV_PCM_INFO_HALF_DUPLEX; | ||
880 | snd_pcm_lib_preallocate_pages(pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream, | ||
881 | SNDRV_DMA_TYPE_DEV, snd_dma_isa_data(), | ||
882 | 64*1024, gus->gf1.dma2 > 3 ? 128*1024 : 64*1024); | ||
883 | } | ||
884 | strcpy(pcm->name, pcm->id); | ||
885 | if (gus->interwave) { | ||
886 | sprintf(pcm->name + strlen(pcm->name), " rev %c", gus->revision + 'A'); | ||
887 | } | ||
888 | strcat(pcm->name, " (synth)"); | ||
889 | gus->pcm = pcm; | ||
890 | |||
891 | if (gus->codec_flag) | ||
892 | kctl = snd_ctl_new1(&snd_gf1_pcm_volume_control1, gus); | ||
893 | else | ||
894 | kctl = snd_ctl_new1(&snd_gf1_pcm_volume_control, gus); | ||
895 | if ((err = snd_ctl_add(card, kctl)) < 0) | ||
896 | return err; | ||
897 | kctl->id.index = control_index; | ||
898 | |||
899 | if (rpcm) | ||
900 | *rpcm = pcm; | ||
901 | return 0; | ||
902 | } | ||
903 | |||
diff --git a/sound/isa/gus/gus_reset.c b/sound/isa/gus/gus_reset.c new file mode 100644 index 000000000000..b4e66f6a10ae --- /dev/null +++ b/sound/isa/gus/gus_reset.c | |||
@@ -0,0 +1,413 @@ | |||
1 | /* | ||
2 | * Copyright (c) by Jaroslav Kysela <perex@suse.cz> | ||
3 | * | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or modify | ||
6 | * it under the terms of the GNU General Public License as published by | ||
7 | * the Free Software Foundation; either version 2 of the License, or | ||
8 | * (at your option) any later version. | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, | ||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | * GNU General Public License for more details. | ||
14 | * | ||
15 | * You should have received a copy of the GNU General Public License | ||
16 | * along with this program; if not, write to the Free Software | ||
17 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
18 | * | ||
19 | */ | ||
20 | |||
21 | #include <sound/driver.h> | ||
22 | #include <linux/delay.h> | ||
23 | #include <linux/interrupt.h> | ||
24 | #include <linux/time.h> | ||
25 | #include <sound/core.h> | ||
26 | #include <sound/gus.h> | ||
27 | |||
28 | extern void snd_gf1_timers_init(snd_gus_card_t * gus); | ||
29 | extern void snd_gf1_timers_done(snd_gus_card_t * gus); | ||
30 | extern int snd_gf1_synth_init(snd_gus_card_t * gus); | ||
31 | extern void snd_gf1_synth_done(snd_gus_card_t * gus); | ||
32 | |||
33 | /* | ||
34 | * ok.. default interrupt handlers... | ||
35 | */ | ||
36 | |||
37 | static void snd_gf1_default_interrupt_handler_midi_out(snd_gus_card_t * gus) | ||
38 | { | ||
39 | snd_gf1_uart_cmd(gus, gus->gf1.uart_cmd &= ~0x20); | ||
40 | } | ||
41 | |||
42 | static void snd_gf1_default_interrupt_handler_midi_in(snd_gus_card_t * gus) | ||
43 | { | ||
44 | snd_gf1_uart_cmd(gus, gus->gf1.uart_cmd &= ~0x80); | ||
45 | } | ||
46 | |||
47 | static void snd_gf1_default_interrupt_handler_timer1(snd_gus_card_t * gus) | ||
48 | { | ||
49 | snd_gf1_i_write8(gus, SNDRV_GF1_GB_SOUND_BLASTER_CONTROL, gus->gf1.timer_enabled &= ~4); | ||
50 | } | ||
51 | |||
52 | static void snd_gf1_default_interrupt_handler_timer2(snd_gus_card_t * gus) | ||
53 | { | ||
54 | snd_gf1_i_write8(gus, SNDRV_GF1_GB_SOUND_BLASTER_CONTROL, gus->gf1.timer_enabled &= ~8); | ||
55 | } | ||
56 | |||
57 | static void snd_gf1_default_interrupt_handler_wave_and_volume(snd_gus_card_t * gus, snd_gus_voice_t * voice) | ||
58 | { | ||
59 | snd_gf1_i_ctrl_stop(gus, 0x00); | ||
60 | snd_gf1_i_ctrl_stop(gus, 0x0d); | ||
61 | } | ||
62 | |||
63 | static void snd_gf1_default_interrupt_handler_dma_write(snd_gus_card_t * gus) | ||
64 | { | ||
65 | snd_gf1_i_write8(gus, 0x41, 0x00); | ||
66 | } | ||
67 | |||
68 | static void snd_gf1_default_interrupt_handler_dma_read(snd_gus_card_t * gus) | ||
69 | { | ||
70 | snd_gf1_i_write8(gus, 0x49, 0x00); | ||
71 | } | ||
72 | |||
73 | void snd_gf1_set_default_handlers(snd_gus_card_t * gus, unsigned int what) | ||
74 | { | ||
75 | if (what & SNDRV_GF1_HANDLER_MIDI_OUT) | ||
76 | gus->gf1.interrupt_handler_midi_out = snd_gf1_default_interrupt_handler_midi_out; | ||
77 | if (what & SNDRV_GF1_HANDLER_MIDI_IN) | ||
78 | gus->gf1.interrupt_handler_midi_in = snd_gf1_default_interrupt_handler_midi_in; | ||
79 | if (what & SNDRV_GF1_HANDLER_TIMER1) | ||
80 | gus->gf1.interrupt_handler_timer1 = snd_gf1_default_interrupt_handler_timer1; | ||
81 | if (what & SNDRV_GF1_HANDLER_TIMER2) | ||
82 | gus->gf1.interrupt_handler_timer2 = snd_gf1_default_interrupt_handler_timer2; | ||
83 | if (what & SNDRV_GF1_HANDLER_VOICE) { | ||
84 | snd_gus_voice_t *voice; | ||
85 | |||
86 | voice = &gus->gf1.voices[what & 0xffff]; | ||
87 | voice->handler_wave = | ||
88 | voice->handler_volume = snd_gf1_default_interrupt_handler_wave_and_volume; | ||
89 | voice->handler_effect = NULL; | ||
90 | voice->volume_change = NULL; | ||
91 | } | ||
92 | if (what & SNDRV_GF1_HANDLER_DMA_WRITE) | ||
93 | gus->gf1.interrupt_handler_dma_write = snd_gf1_default_interrupt_handler_dma_write; | ||
94 | if (what & SNDRV_GF1_HANDLER_DMA_READ) | ||
95 | gus->gf1.interrupt_handler_dma_read = snd_gf1_default_interrupt_handler_dma_read; | ||
96 | } | ||
97 | |||
98 | /* | ||
99 | |||
100 | */ | ||
101 | |||
102 | static void snd_gf1_clear_regs(snd_gus_card_t * gus) | ||
103 | { | ||
104 | unsigned long flags; | ||
105 | |||
106 | spin_lock_irqsave(&gus->reg_lock, flags); | ||
107 | inb(GUSP(gus, IRQSTAT)); | ||
108 | snd_gf1_write8(gus, 0x41, 0); /* DRAM DMA Control Register */ | ||
109 | snd_gf1_write8(gus, 0x45, 0); /* Timer Control */ | ||
110 | snd_gf1_write8(gus, 0x49, 0); /* Sampling Control Register */ | ||
111 | spin_unlock_irqrestore(&gus->reg_lock, flags); | ||
112 | } | ||
113 | |||
114 | static void snd_gf1_look_regs(snd_gus_card_t * gus) | ||
115 | { | ||
116 | unsigned long flags; | ||
117 | |||
118 | spin_lock_irqsave(&gus->reg_lock, flags); | ||
119 | snd_gf1_look8(gus, 0x41); /* DRAM DMA Control Register */ | ||
120 | snd_gf1_look8(gus, 0x49); /* Sampling Control Register */ | ||
121 | inb(GUSP(gus, IRQSTAT)); | ||
122 | snd_gf1_read8(gus, 0x0f); /* IRQ Source Register */ | ||
123 | spin_unlock_irqrestore(&gus->reg_lock, flags); | ||
124 | } | ||
125 | |||
126 | /* | ||
127 | * put selected GF1 voices to initial stage... | ||
128 | */ | ||
129 | |||
130 | void snd_gf1_smart_stop_voice(snd_gus_card_t * gus, unsigned short voice) | ||
131 | { | ||
132 | unsigned long flags; | ||
133 | |||
134 | spin_lock_irqsave(&gus->reg_lock, flags); | ||
135 | snd_gf1_select_voice(gus, voice); | ||
136 | #if 0 | ||
137 | printk(" -%i- smart stop voice - volume = 0x%x\n", voice, snd_gf1_i_read16(gus, SNDRV_GF1_VW_VOLUME)); | ||
138 | #endif | ||
139 | snd_gf1_ctrl_stop(gus, SNDRV_GF1_VB_ADDRESS_CONTROL); | ||
140 | snd_gf1_ctrl_stop(gus, SNDRV_GF1_VB_VOLUME_CONTROL); | ||
141 | spin_unlock_irqrestore(&gus->reg_lock, flags); | ||
142 | } | ||
143 | |||
144 | void snd_gf1_stop_voice(snd_gus_card_t * gus, unsigned short voice) | ||
145 | { | ||
146 | unsigned long flags; | ||
147 | |||
148 | spin_lock_irqsave(&gus->reg_lock, flags); | ||
149 | snd_gf1_select_voice(gus, voice); | ||
150 | #if 0 | ||
151 | printk(" -%i- stop voice - volume = 0x%x\n", voice, snd_gf1_i_read16(gus, SNDRV_GF1_VW_VOLUME)); | ||
152 | #endif | ||
153 | snd_gf1_ctrl_stop(gus, SNDRV_GF1_VB_ADDRESS_CONTROL); | ||
154 | snd_gf1_ctrl_stop(gus, SNDRV_GF1_VB_VOLUME_CONTROL); | ||
155 | if (gus->gf1.enh_mode) | ||
156 | snd_gf1_write8(gus, SNDRV_GF1_VB_ACCUMULATOR, 0); | ||
157 | spin_unlock_irqrestore(&gus->reg_lock, flags); | ||
158 | #if 0 | ||
159 | snd_gf1_lfo_shutdown(gus, voice, ULTRA_LFO_VIBRATO); | ||
160 | snd_gf1_lfo_shutdown(gus, voice, ULTRA_LFO_TREMOLO); | ||
161 | #endif | ||
162 | } | ||
163 | |||
164 | void snd_gf1_clear_voices(snd_gus_card_t * gus, unsigned short v_min, unsigned short v_max) | ||
165 | { | ||
166 | unsigned long flags; | ||
167 | unsigned int daddr; | ||
168 | unsigned short i, w_16; | ||
169 | |||
170 | daddr = gus->gf1.default_voice_address << 4; | ||
171 | for (i = v_min; i <= v_max; i++) { | ||
172 | #if 0 | ||
173 | if (gus->gf1.syn_voices) | ||
174 | gus->gf1.syn_voices[i].flags = ~VFLG_DYNAMIC; | ||
175 | #endif | ||
176 | spin_lock_irqsave(&gus->reg_lock, flags); | ||
177 | snd_gf1_select_voice(gus, i); | ||
178 | snd_gf1_ctrl_stop(gus, SNDRV_GF1_VB_ADDRESS_CONTROL); /* Voice Control Register = voice stop */ | ||
179 | snd_gf1_ctrl_stop(gus, SNDRV_GF1_VB_VOLUME_CONTROL); /* Volume Ramp Control Register = ramp off */ | ||
180 | if (gus->gf1.enh_mode) | ||
181 | snd_gf1_write8(gus, SNDRV_GF1_VB_MODE, gus->gf1.memory ? 0x02 : 0x82); /* Deactivate voice */ | ||
182 | w_16 = snd_gf1_read8(gus, SNDRV_GF1_VB_ADDRESS_CONTROL) & 0x04; | ||
183 | snd_gf1_write16(gus, SNDRV_GF1_VW_FREQUENCY, 0x400); | ||
184 | snd_gf1_write_addr(gus, SNDRV_GF1_VA_START, daddr, w_16); | ||
185 | snd_gf1_write_addr(gus, SNDRV_GF1_VA_END, daddr, w_16); | ||
186 | snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_START, 0); | ||
187 | snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_END, 0); | ||
188 | snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_RATE, 0); | ||
189 | snd_gf1_write16(gus, SNDRV_GF1_VW_VOLUME, 0); | ||
190 | snd_gf1_write_addr(gus, SNDRV_GF1_VA_CURRENT, daddr, w_16); | ||
191 | snd_gf1_write8(gus, SNDRV_GF1_VB_PAN, 7); | ||
192 | if (gus->gf1.enh_mode) { | ||
193 | snd_gf1_write8(gus, SNDRV_GF1_VB_ACCUMULATOR, 0); | ||
194 | snd_gf1_write16(gus, SNDRV_GF1_VW_EFFECT_VOLUME, 0); | ||
195 | snd_gf1_write16(gus, SNDRV_GF1_VW_EFFECT_VOLUME_FINAL, 0); | ||
196 | } | ||
197 | spin_unlock_irqrestore(&gus->reg_lock, flags); | ||
198 | #if 0 | ||
199 | snd_gf1_lfo_shutdown(gus, i, ULTRA_LFO_VIBRATO); | ||
200 | snd_gf1_lfo_shutdown(gus, i, ULTRA_LFO_TREMOLO); | ||
201 | #endif | ||
202 | } | ||
203 | } | ||
204 | |||
205 | void snd_gf1_stop_voices(snd_gus_card_t * gus, unsigned short v_min, unsigned short v_max) | ||
206 | { | ||
207 | unsigned long flags; | ||
208 | short i, ramp_ok; | ||
209 | unsigned short ramp_end; | ||
210 | |||
211 | if (!in_interrupt()) { /* this can't be done in interrupt */ | ||
212 | for (i = v_min, ramp_ok = 0; i <= v_max; i++) { | ||
213 | spin_lock_irqsave(&gus->reg_lock, flags); | ||
214 | snd_gf1_select_voice(gus, i); | ||
215 | ramp_end = snd_gf1_read16(gus, 9) >> 8; | ||
216 | if (ramp_end > SNDRV_GF1_MIN_OFFSET) { | ||
217 | ramp_ok++; | ||
218 | snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_RATE, 20); /* ramp rate */ | ||
219 | snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_START, SNDRV_GF1_MIN_OFFSET); /* ramp start */ | ||
220 | snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_END, ramp_end); /* ramp end */ | ||
221 | snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_CONTROL, 0x40); /* ramp down */ | ||
222 | if (gus->gf1.enh_mode) { | ||
223 | snd_gf1_delay(gus); | ||
224 | snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_CONTROL, 0x40); | ||
225 | } | ||
226 | } | ||
227 | spin_unlock_irqrestore(&gus->reg_lock, flags); | ||
228 | } | ||
229 | msleep_interruptible(50); | ||
230 | } | ||
231 | snd_gf1_clear_voices(gus, v_min, v_max); | ||
232 | } | ||
233 | |||
234 | static void snd_gf1_alloc_voice_use(snd_gus_card_t * gus, | ||
235 | snd_gus_voice_t * pvoice, | ||
236 | int type, int client, int port) | ||
237 | { | ||
238 | pvoice->use = 1; | ||
239 | switch (type) { | ||
240 | case SNDRV_GF1_VOICE_TYPE_PCM: | ||
241 | gus->gf1.pcm_alloc_voices++; | ||
242 | pvoice->pcm = 1; | ||
243 | break; | ||
244 | case SNDRV_GF1_VOICE_TYPE_SYNTH: | ||
245 | pvoice->synth = 1; | ||
246 | pvoice->client = client; | ||
247 | pvoice->port = port; | ||
248 | break; | ||
249 | case SNDRV_GF1_VOICE_TYPE_MIDI: | ||
250 | pvoice->midi = 1; | ||
251 | pvoice->client = client; | ||
252 | pvoice->port = port; | ||
253 | break; | ||
254 | } | ||
255 | } | ||
256 | |||
257 | snd_gus_voice_t *snd_gf1_alloc_voice(snd_gus_card_t * gus, int type, int client, int port) | ||
258 | { | ||
259 | snd_gus_voice_t *pvoice; | ||
260 | unsigned long flags; | ||
261 | int idx; | ||
262 | |||
263 | spin_lock_irqsave(&gus->voice_alloc, flags); | ||
264 | if (type == SNDRV_GF1_VOICE_TYPE_PCM) { | ||
265 | if (gus->gf1.pcm_alloc_voices >= gus->gf1.pcm_channels) { | ||
266 | spin_unlock_irqrestore(&gus->voice_alloc, flags); | ||
267 | return NULL; | ||
268 | } | ||
269 | } | ||
270 | for (idx = 0; idx < 32; idx++) { | ||
271 | pvoice = &gus->gf1.voices[idx]; | ||
272 | if (!pvoice->use) { | ||
273 | snd_gf1_alloc_voice_use(gus, pvoice, type, client, port); | ||
274 | spin_unlock_irqrestore(&gus->voice_alloc, flags); | ||
275 | return pvoice; | ||
276 | } | ||
277 | } | ||
278 | for (idx = 0; idx < 32; idx++) { | ||
279 | pvoice = &gus->gf1.voices[idx]; | ||
280 | if (pvoice->midi && !pvoice->client) { | ||
281 | snd_gf1_clear_voices(gus, pvoice->number, pvoice->number); | ||
282 | snd_gf1_alloc_voice_use(gus, pvoice, type, client, port); | ||
283 | spin_unlock_irqrestore(&gus->voice_alloc, flags); | ||
284 | return pvoice; | ||
285 | } | ||
286 | } | ||
287 | spin_unlock_irqrestore(&gus->voice_alloc, flags); | ||
288 | return NULL; | ||
289 | } | ||
290 | |||
291 | void snd_gf1_free_voice(snd_gus_card_t * gus, snd_gus_voice_t *voice) | ||
292 | { | ||
293 | unsigned long flags; | ||
294 | void (*private_free)(snd_gus_voice_t *voice); | ||
295 | void *private_data; | ||
296 | |||
297 | if (voice == NULL || !voice->use) | ||
298 | return; | ||
299 | snd_gf1_set_default_handlers(gus, SNDRV_GF1_HANDLER_VOICE | voice->number); | ||
300 | snd_gf1_clear_voices(gus, voice->number, voice->number); | ||
301 | spin_lock_irqsave(&gus->voice_alloc, flags); | ||
302 | private_free = voice->private_free; | ||
303 | private_data = voice->private_data; | ||
304 | voice->private_free = NULL; | ||
305 | voice->private_data = NULL; | ||
306 | if (voice->pcm) | ||
307 | gus->gf1.pcm_alloc_voices--; | ||
308 | voice->use = voice->pcm = 0; | ||
309 | voice->sample_ops = NULL; | ||
310 | spin_unlock_irqrestore(&gus->voice_alloc, flags); | ||
311 | if (private_free) | ||
312 | private_free(voice); | ||
313 | } | ||
314 | |||
315 | /* | ||
316 | * call this function only by start of driver | ||
317 | */ | ||
318 | |||
319 | int snd_gf1_start(snd_gus_card_t * gus) | ||
320 | { | ||
321 | unsigned long flags; | ||
322 | unsigned int i; | ||
323 | |||
324 | snd_gf1_i_write8(gus, SNDRV_GF1_GB_RESET, 0); /* reset GF1 */ | ||
325 | udelay(160); | ||
326 | snd_gf1_i_write8(gus, SNDRV_GF1_GB_RESET, 1); /* disable IRQ & DAC */ | ||
327 | udelay(160); | ||
328 | snd_gf1_i_write8(gus, SNDRV_GF1_GB_JOYSTICK_DAC_LEVEL, gus->joystick_dac); | ||
329 | |||
330 | snd_gf1_set_default_handlers(gus, SNDRV_GF1_HANDLER_ALL); | ||
331 | for (i = 0; i < 32; i++) { | ||
332 | gus->gf1.voices[i].number = i; | ||
333 | snd_gf1_set_default_handlers(gus, SNDRV_GF1_HANDLER_VOICE | i); | ||
334 | } | ||
335 | |||
336 | snd_gf1_uart_cmd(gus, 0x03); /* huh.. this cleanup took me some time... */ | ||
337 | |||
338 | if (gus->gf1.enh_mode) { /* enhanced mode !!!! */ | ||
339 | snd_gf1_i_write8(gus, SNDRV_GF1_GB_GLOBAL_MODE, snd_gf1_i_look8(gus, SNDRV_GF1_GB_GLOBAL_MODE) | 0x01); | ||
340 | snd_gf1_i_write8(gus, SNDRV_GF1_GB_MEMORY_CONTROL, 0x01); | ||
341 | } | ||
342 | snd_gf1_clear_regs(gus); | ||
343 | snd_gf1_select_active_voices(gus); | ||
344 | snd_gf1_delay(gus); | ||
345 | gus->gf1.default_voice_address = gus->gf1.memory > 0 ? 0 : 512 - 8; | ||
346 | /* initialize LFOs & clear LFOs memory */ | ||
347 | if (gus->gf1.enh_mode && gus->gf1.memory) { | ||
348 | gus->gf1.hw_lfo = 1; | ||
349 | gus->gf1.default_voice_address += 1024; | ||
350 | } else { | ||
351 | gus->gf1.sw_lfo = 1; | ||
352 | } | ||
353 | #if 0 | ||
354 | snd_gf1_lfo_init(gus); | ||
355 | #endif | ||
356 | if (gus->gf1.memory > 0) | ||
357 | for (i = 0; i < 4; i++) | ||
358 | snd_gf1_poke(gus, gus->gf1.default_voice_address + i, 0); | ||
359 | snd_gf1_clear_regs(gus); | ||
360 | snd_gf1_clear_voices(gus, 0, 31); | ||
361 | snd_gf1_look_regs(gus); | ||
362 | udelay(160); | ||
363 | snd_gf1_i_write8(gus, SNDRV_GF1_GB_RESET, 7); /* Reset Register = IRQ enable, DAC enable */ | ||
364 | udelay(160); | ||
365 | snd_gf1_i_write8(gus, SNDRV_GF1_GB_RESET, 7); /* Reset Register = IRQ enable, DAC enable */ | ||
366 | if (gus->gf1.enh_mode) { /* enhanced mode !!!! */ | ||
367 | snd_gf1_i_write8(gus, SNDRV_GF1_GB_GLOBAL_MODE, snd_gf1_i_look8(gus, SNDRV_GF1_GB_GLOBAL_MODE) | 0x01); | ||
368 | snd_gf1_i_write8(gus, SNDRV_GF1_GB_MEMORY_CONTROL, 0x01); | ||
369 | } | ||
370 | while ((snd_gf1_i_read8(gus, SNDRV_GF1_GB_VOICES_IRQ) & 0xc0) != 0xc0); | ||
371 | |||
372 | spin_lock_irqsave(&gus->reg_lock, flags); | ||
373 | outb(gus->gf1.active_voice = 0, GUSP(gus, GF1PAGE)); | ||
374 | outb(gus->mix_cntrl_reg, GUSP(gus, MIXCNTRLREG)); | ||
375 | spin_unlock_irqrestore(&gus->reg_lock, flags); | ||
376 | |||
377 | snd_gf1_timers_init(gus); | ||
378 | snd_gf1_look_regs(gus); | ||
379 | snd_gf1_mem_init(gus); | ||
380 | snd_gf1_mem_proc_init(gus); | ||
381 | #ifdef CONFIG_SND_DEBUG | ||
382 | snd_gus_irq_profile_init(gus); | ||
383 | #endif | ||
384 | |||
385 | #if 0 | ||
386 | if (gus->pnp_flag) { | ||
387 | if (gus->chip.playback_fifo_size > 0) | ||
388 | snd_gf1_i_write16(gus, SNDRV_GF1_GW_FIFO_RECORD_BASE_ADDR, gus->chip.playback_fifo_block->ptr >> 8); | ||
389 | if (gus->chip.record_fifo_size > 0) | ||
390 | snd_gf1_i_write16(gus, SNDRV_GF1_GW_FIFO_PLAY_BASE_ADDR, gus->chip.record_fifo_block->ptr >> 8); | ||
391 | snd_gf1_i_write16(gus, SNDRV_GF1_GW_FIFO_SIZE, gus->chip.interwave_fifo_reg); | ||
392 | } | ||
393 | #endif | ||
394 | |||
395 | return 0; | ||
396 | } | ||
397 | |||
398 | /* | ||
399 | * call this function only by shutdown of driver | ||
400 | */ | ||
401 | |||
402 | int snd_gf1_stop(snd_gus_card_t * gus) | ||
403 | { | ||
404 | snd_gf1_i_write8(gus, SNDRV_GF1_GB_SOUND_BLASTER_CONTROL, 0); /* stop all timers */ | ||
405 | snd_gf1_stop_voices(gus, 0, 31); /* stop all voices */ | ||
406 | snd_gf1_i_write8(gus, SNDRV_GF1_GB_RESET, 1); /* disable IRQ & DAC */ | ||
407 | snd_gf1_timers_done(gus); | ||
408 | snd_gf1_mem_done(gus); | ||
409 | #if 0 | ||
410 | snd_gf1_lfo_done(gus); | ||
411 | #endif | ||
412 | return 0; | ||
413 | } | ||
diff --git a/sound/isa/gus/gus_sample.c b/sound/isa/gus/gus_sample.c new file mode 100644 index 000000000000..4290e03acd51 --- /dev/null +++ b/sound/isa/gus/gus_sample.c | |||
@@ -0,0 +1,155 @@ | |||
1 | /* | ||
2 | * Routines for Gravis UltraSound soundcards - Sample support | ||
3 | * Copyright (c) by Jaroslav Kysela <perex@suse.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 <linux/time.h> | ||
24 | #include <sound/core.h> | ||
25 | #include <sound/gus.h> | ||
26 | |||
27 | /* | ||
28 | * | ||
29 | */ | ||
30 | |||
31 | static void select_instrument(snd_gus_card_t * gus, snd_gus_voice_t * v) | ||
32 | { | ||
33 | snd_seq_kinstr_t *instr; | ||
34 | |||
35 | #if 0 | ||
36 | printk("select instrument: cluster = %li, std = 0x%x, bank = %i, prg = %i\n", | ||
37 | v->instr.cluster, | ||
38 | v->instr.std, | ||
39 | v->instr.bank, | ||
40 | v->instr.prg); | ||
41 | #endif | ||
42 | instr = snd_seq_instr_find(gus->gf1.ilist, &v->instr, 0, 1); | ||
43 | if (instr != NULL) { | ||
44 | if (instr->ops) { | ||
45 | if (!strcmp(instr->ops->instr_type, SNDRV_SEQ_INSTR_ID_SIMPLE)) | ||
46 | snd_gf1_simple_init(v); | ||
47 | } | ||
48 | snd_seq_instr_free_use(gus->gf1.ilist, instr); | ||
49 | } | ||
50 | } | ||
51 | |||
52 | /* | ||
53 | * | ||
54 | */ | ||
55 | |||
56 | static void event_sample(snd_seq_event_t *ev, snd_gus_port_t *p, snd_gus_voice_t *v) | ||
57 | { | ||
58 | if (v->sample_ops && v->sample_ops->sample_stop) | ||
59 | v->sample_ops->sample_stop(p->gus, v, SAMPLE_STOP_IMMEDIATELY); | ||
60 | v->instr.std = ev->data.sample.param.sample.std; | ||
61 | if (v->instr.std & 0xff000000) { /* private instrument */ | ||
62 | v->instr.std &= 0x00ffffff; | ||
63 | v->instr.std |= (unsigned int)ev->source.client << 24; | ||
64 | } | ||
65 | v->instr.bank = ev->data.sample.param.sample.bank; | ||
66 | v->instr.prg = ev->data.sample.param.sample.prg; | ||
67 | select_instrument(p->gus, v); | ||
68 | } | ||
69 | |||
70 | static void event_cluster(snd_seq_event_t *ev, snd_gus_port_t *p, snd_gus_voice_t *v) | ||
71 | { | ||
72 | if (v->sample_ops && v->sample_ops->sample_stop) | ||
73 | v->sample_ops->sample_stop(p->gus, v, SAMPLE_STOP_IMMEDIATELY); | ||
74 | v->instr.cluster = ev->data.sample.param.cluster.cluster; | ||
75 | select_instrument(p->gus, v); | ||
76 | } | ||
77 | |||
78 | static void event_start(snd_seq_event_t *ev, snd_gus_port_t *p, snd_gus_voice_t *v) | ||
79 | { | ||
80 | if (v->sample_ops && v->sample_ops->sample_start) | ||
81 | v->sample_ops->sample_start(p->gus, v, ev->data.sample.param.position); | ||
82 | } | ||
83 | |||
84 | static void event_stop(snd_seq_event_t *ev, snd_gus_port_t *p, snd_gus_voice_t *v) | ||
85 | { | ||
86 | if (v->sample_ops && v->sample_ops->sample_stop) | ||
87 | v->sample_ops->sample_stop(p->gus, v, ev->data.sample.param.stop_mode); | ||
88 | } | ||
89 | |||
90 | static void event_freq(snd_seq_event_t *ev, snd_gus_port_t *p, snd_gus_voice_t *v) | ||
91 | { | ||
92 | if (v->sample_ops && v->sample_ops->sample_freq) | ||
93 | v->sample_ops->sample_freq(p->gus, v, ev->data.sample.param.frequency); | ||
94 | } | ||
95 | |||
96 | static void event_volume(snd_seq_event_t *ev, snd_gus_port_t *p, snd_gus_voice_t *v) | ||
97 | { | ||
98 | if (v->sample_ops && v->sample_ops->sample_volume) | ||
99 | v->sample_ops->sample_volume(p->gus, v, &ev->data.sample.param.volume); | ||
100 | } | ||
101 | |||
102 | static void event_loop(snd_seq_event_t *ev, snd_gus_port_t *p, snd_gus_voice_t *v) | ||
103 | { | ||
104 | if (v->sample_ops && v->sample_ops->sample_loop) | ||
105 | v->sample_ops->sample_loop(p->gus, v, &ev->data.sample.param.loop); | ||
106 | } | ||
107 | |||
108 | static void event_position(snd_seq_event_t *ev, snd_gus_port_t *p, snd_gus_voice_t *v) | ||
109 | { | ||
110 | if (v->sample_ops && v->sample_ops->sample_pos) | ||
111 | v->sample_ops->sample_pos(p->gus, v, ev->data.sample.param.position); | ||
112 | } | ||
113 | |||
114 | static void event_private1(snd_seq_event_t *ev, snd_gus_port_t *p, snd_gus_voice_t *v) | ||
115 | { | ||
116 | if (v->sample_ops && v->sample_ops->sample_private1) | ||
117 | v->sample_ops->sample_private1(p->gus, v, (unsigned char *)&ev->data.sample.param.raw8); | ||
118 | } | ||
119 | |||
120 | typedef void (gus_sample_event_handler_t)(snd_seq_event_t *ev, snd_gus_port_t *p, snd_gus_voice_t *v); | ||
121 | |||
122 | static gus_sample_event_handler_t *gus_sample_event_handlers[9] = { | ||
123 | event_sample, | ||
124 | event_cluster, | ||
125 | event_start, | ||
126 | event_stop, | ||
127 | event_freq, | ||
128 | event_volume, | ||
129 | event_loop, | ||
130 | event_position, | ||
131 | event_private1 | ||
132 | }; | ||
133 | |||
134 | void snd_gus_sample_event(snd_seq_event_t *ev, snd_gus_port_t *p) | ||
135 | { | ||
136 | int idx, voice; | ||
137 | snd_gus_card_t *gus = p->gus; | ||
138 | snd_gus_voice_t *v; | ||
139 | unsigned long flags; | ||
140 | |||
141 | idx = ev->type - SNDRV_SEQ_EVENT_SAMPLE; | ||
142 | if (idx < 0 || idx > 8) | ||
143 | return; | ||
144 | for (voice = 0; voice < 32; voice++) { | ||
145 | v = &gus->gf1.voices[voice]; | ||
146 | if (v->use && v->client == ev->source.client && | ||
147 | v->port == ev->source.port && | ||
148 | v->index == ev->data.sample.channel) { | ||
149 | spin_lock_irqsave(&gus->event_lock, flags); | ||
150 | gus_sample_event_handlers[idx](ev, p, v); | ||
151 | spin_unlock_irqrestore(&gus->event_lock, flags); | ||
152 | return; | ||
153 | } | ||
154 | } | ||
155 | } | ||
diff --git a/sound/isa/gus/gus_simple.c b/sound/isa/gus/gus_simple.c new file mode 100644 index 000000000000..c122e7be6ceb --- /dev/null +++ b/sound/isa/gus/gus_simple.c | |||
@@ -0,0 +1,634 @@ | |||
1 | /* | ||
2 | * Routines for Gravis UltraSound soundcards - Simple instrument handlers | ||
3 | * Copyright (c) by Jaroslav Kysela <perex@suse.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 <linux/time.h> | ||
24 | #include <sound/core.h> | ||
25 | #include <sound/gus.h> | ||
26 | #include "gus_tables.h" | ||
27 | |||
28 | /* | ||
29 | * | ||
30 | */ | ||
31 | |||
32 | static void interrupt_wave(snd_gus_card_t *gus, snd_gus_voice_t *voice); | ||
33 | static void interrupt_volume(snd_gus_card_t *gus, snd_gus_voice_t *voice); | ||
34 | static void interrupt_effect(snd_gus_card_t *gus, snd_gus_voice_t *voice); | ||
35 | |||
36 | static void sample_start(snd_gus_card_t *gus, snd_gus_voice_t *voice, snd_seq_position_t position); | ||
37 | static void sample_stop(snd_gus_card_t *gus, snd_gus_voice_t *voice, snd_seq_stop_mode_t mode); | ||
38 | static void sample_freq(snd_gus_card_t *gus, snd_gus_voice_t *voice, snd_seq_frequency_t freq); | ||
39 | static void sample_volume(snd_gus_card_t *card, snd_gus_voice_t *voice, snd_seq_ev_volume_t *volume); | ||
40 | static void sample_loop(snd_gus_card_t *card, snd_gus_voice_t *voice, snd_seq_ev_loop_t *loop); | ||
41 | static void sample_pos(snd_gus_card_t *card, snd_gus_voice_t *voice, snd_seq_position_t position); | ||
42 | static void sample_private1(snd_gus_card_t *card, snd_gus_voice_t *voice, unsigned char *data); | ||
43 | |||
44 | static snd_gus_sample_ops_t sample_ops = { | ||
45 | sample_start, | ||
46 | sample_stop, | ||
47 | sample_freq, | ||
48 | sample_volume, | ||
49 | sample_loop, | ||
50 | sample_pos, | ||
51 | sample_private1 | ||
52 | }; | ||
53 | |||
54 | #if 0 | ||
55 | |||
56 | static void note_stop(snd_gus_card_t *gus, snd_gus_voice_t *voice, int wait); | ||
57 | static void note_wait(snd_gus_card_t *gus, snd_gus_voice_t *voice); | ||
58 | static void note_off(snd_gus_card_t *gus, snd_gus_voice_t *voice); | ||
59 | static void note_volume(snd_gus_card_t *card, snd_gus_voice_t *voice); | ||
60 | static void note_pitchbend(snd_gus_card_t *card, snd_gus_voice_t *voice); | ||
61 | static void note_vibrato(snd_gus_card_t *card, snd_gus_voice_t *voice); | ||
62 | static void note_tremolo(snd_gus_card_t *card, snd_gus_voice_t *voice); | ||
63 | |||
64 | static struct snd_gus_note_handlers note_commands = { | ||
65 | note_stop, | ||
66 | note_wait, | ||
67 | note_off, | ||
68 | note_volume, | ||
69 | note_pitchbend, | ||
70 | note_vibrato, | ||
71 | note_tremolo | ||
72 | }; | ||
73 | |||
74 | static void chn_trigger_down(snd_gus_card_t *card, ultra_channel_t *channel, ultra_instrument_t *instrument, unsigned char note, unsigned char velocity, unsigned char priority ); | ||
75 | static void chn_trigger_up( ultra_card_t *card, ultra_note_t *note ); | ||
76 | static void chn_control( ultra_card_t *card, ultra_channel_t *channel, unsigned short p1, unsigned short p2 ); | ||
77 | |||
78 | static struct ULTRA_STRU_INSTRUMENT_CHANNEL_COMMANDS channel_commands = { | ||
79 | chn_trigger_down, | ||
80 | chn_trigger_up, | ||
81 | chn_control | ||
82 | }; | ||
83 | |||
84 | #endif | ||
85 | |||
86 | static void do_volume_envelope(snd_gus_card_t *card, snd_gus_voice_t *voice); | ||
87 | static void do_pan_envelope(snd_gus_card_t *card, snd_gus_voice_t *voice); | ||
88 | |||
89 | /* | ||
90 | * | ||
91 | */ | ||
92 | |||
93 | static void interrupt_wave(snd_gus_card_t *gus, snd_gus_voice_t *voice) | ||
94 | { | ||
95 | spin_lock(&gus->event_lock); | ||
96 | snd_gf1_stop_voice(gus, voice->number); | ||
97 | spin_lock(&gus->reg_lock); | ||
98 | snd_gf1_select_voice(gus, voice->number); | ||
99 | snd_gf1_write16(gus, SNDRV_GF1_VW_VOLUME, 0); | ||
100 | spin_unlock(&gus->reg_lock); | ||
101 | voice->flags &= ~SNDRV_GF1_VFLG_RUNNING; | ||
102 | spin_unlock(&gus->event_lock); | ||
103 | } | ||
104 | |||
105 | static void interrupt_volume(snd_gus_card_t *gus, snd_gus_voice_t *voice) | ||
106 | { | ||
107 | spin_lock(&gus->event_lock); | ||
108 | if (voice->flags & SNDRV_GF1_VFLG_RUNNING) | ||
109 | do_volume_envelope(gus, voice); | ||
110 | else | ||
111 | snd_gf1_stop_voice(gus, voice->number); | ||
112 | spin_unlock(&gus->event_lock); | ||
113 | } | ||
114 | |||
115 | static void interrupt_effect(snd_gus_card_t *gus, snd_gus_voice_t *voice) | ||
116 | { | ||
117 | spin_lock(&gus->event_lock); | ||
118 | if ((voice->flags & (SNDRV_GF1_VFLG_RUNNING|SNDRV_GF1_VFLG_EFFECT_TIMER1)) == | ||
119 | (SNDRV_GF1_VFLG_RUNNING|SNDRV_GF1_VFLG_EFFECT_TIMER1)) | ||
120 | do_pan_envelope(gus, voice); | ||
121 | spin_unlock(&gus->event_lock); | ||
122 | } | ||
123 | |||
124 | /* | ||
125 | * | ||
126 | */ | ||
127 | |||
128 | static void do_volume_envelope(snd_gus_card_t *gus, snd_gus_voice_t *voice) | ||
129 | { | ||
130 | unsigned short next, rate, old_volume; | ||
131 | int program_next_ramp; | ||
132 | unsigned long flags; | ||
133 | |||
134 | if (!gus->gf1.volume_ramp) { | ||
135 | spin_lock_irqsave(&gus->reg_lock, flags); | ||
136 | snd_gf1_select_voice(gus, voice->number); | ||
137 | snd_gf1_ctrl_stop(gus, SNDRV_GF1_VB_VOLUME_CONTROL); | ||
138 | snd_gf1_write16(gus, SNDRV_GF1_VW_VOLUME, voice->gf1_volume); | ||
139 | printk("gf1_volume = 0x%x\n", voice->gf1_volume); | ||
140 | spin_unlock_irqrestore(&gus->reg_lock, flags); | ||
141 | return; | ||
142 | } | ||
143 | program_next_ramp = 0; | ||
144 | rate = next = 0; | ||
145 | while (1) { | ||
146 | program_next_ramp = 0; | ||
147 | rate = next = 0; | ||
148 | switch (voice->venv_state) { | ||
149 | case VENV_BEFORE: | ||
150 | voice->venv_state = VENV_ATTACK; | ||
151 | voice->venv_value_next = 0; | ||
152 | spin_lock_irqsave(&gus->reg_lock, flags); | ||
153 | snd_gf1_select_voice(gus, voice->number); | ||
154 | snd_gf1_ctrl_stop(gus, SNDRV_GF1_VB_VOLUME_CONTROL); | ||
155 | snd_gf1_write16(gus, SNDRV_GF1_VW_VOLUME, SNDRV_GF1_MIN_VOLUME); | ||
156 | spin_unlock_irqrestore(&gus->reg_lock, flags); | ||
157 | break; | ||
158 | case VENV_ATTACK: | ||
159 | voice->venv_state = VENV_SUSTAIN; | ||
160 | program_next_ramp++; | ||
161 | next = 255; | ||
162 | rate = gus->gf1.volume_ramp; | ||
163 | break; | ||
164 | case VENV_SUSTAIN: | ||
165 | voice->venv_state = VENV_RELEASE; | ||
166 | spin_lock_irqsave(&gus->reg_lock, flags); | ||
167 | snd_gf1_select_voice(gus, voice->number); | ||
168 | snd_gf1_ctrl_stop(gus, SNDRV_GF1_VB_VOLUME_CONTROL); | ||
169 | snd_gf1_write16(gus, SNDRV_GF1_VW_VOLUME, ((int)voice->gf1_volume * (int)voice->venv_value_next) / 255); | ||
170 | spin_unlock_irqrestore(&gus->reg_lock, flags); | ||
171 | return; | ||
172 | case VENV_RELEASE: | ||
173 | voice->venv_state = VENV_DONE; | ||
174 | program_next_ramp++; | ||
175 | next = 0; | ||
176 | rate = gus->gf1.volume_ramp; | ||
177 | break; | ||
178 | case VENV_DONE: | ||
179 | snd_gf1_stop_voice(gus, voice->number); | ||
180 | voice->flags &= ~SNDRV_GF1_VFLG_RUNNING; | ||
181 | return; | ||
182 | case VENV_VOLUME: | ||
183 | program_next_ramp++; | ||
184 | next = voice->venv_value_next; | ||
185 | rate = gus->gf1.volume_ramp; | ||
186 | voice->venv_state = voice->venv_state_prev; | ||
187 | break; | ||
188 | } | ||
189 | voice->venv_value_next = next; | ||
190 | if (!program_next_ramp) | ||
191 | continue; | ||
192 | spin_lock_irqsave(&gus->reg_lock, flags); | ||
193 | snd_gf1_select_voice(gus, voice->number); | ||
194 | snd_gf1_ctrl_stop(gus, SNDRV_GF1_VB_VOLUME_CONTROL); | ||
195 | old_volume = snd_gf1_read16(gus, SNDRV_GF1_VW_VOLUME) >> 8; | ||
196 | if (!rate) { | ||
197 | spin_unlock_irqrestore(&gus->reg_lock, flags); | ||
198 | continue; | ||
199 | } | ||
200 | next = (((int)voice->gf1_volume * (int)next) / 255) >> 8; | ||
201 | if (old_volume < SNDRV_GF1_MIN_OFFSET) | ||
202 | old_volume = SNDRV_GF1_MIN_OFFSET; | ||
203 | if (next < SNDRV_GF1_MIN_OFFSET) | ||
204 | next = SNDRV_GF1_MIN_OFFSET; | ||
205 | if (next > SNDRV_GF1_MAX_OFFSET) | ||
206 | next = SNDRV_GF1_MAX_OFFSET; | ||
207 | if (old_volume == next) { | ||
208 | spin_unlock_irqrestore(&gus->reg_lock, flags); | ||
209 | continue; | ||
210 | } | ||
211 | voice->volume_control &= ~0xc3; | ||
212 | voice->volume_control |= 0x20; | ||
213 | if (old_volume > next) { | ||
214 | snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_START, next); | ||
215 | snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_END, old_volume); | ||
216 | voice->volume_control |= 0x40; | ||
217 | } else { | ||
218 | snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_START, old_volume); | ||
219 | snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_END, next); | ||
220 | } | ||
221 | snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_RATE, rate); | ||
222 | snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_CONTROL, voice->volume_control); | ||
223 | if (!gus->gf1.enh_mode) { | ||
224 | snd_gf1_delay(gus); | ||
225 | snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_CONTROL, voice->volume_control); | ||
226 | } | ||
227 | spin_unlock_irqrestore(&gus->reg_lock, flags); | ||
228 | return; | ||
229 | } | ||
230 | } | ||
231 | |||
232 | static void do_pan_envelope(snd_gus_card_t *gus, snd_gus_voice_t *voice) | ||
233 | { | ||
234 | unsigned long flags; | ||
235 | unsigned char old_pan; | ||
236 | |||
237 | #if 0 | ||
238 | snd_gf1_select_voice(gus, voice->number); | ||
239 | printk(" -%i- do_pan_envelope - flags = 0x%x (0x%x -> 0x%x)\n", | ||
240 | voice->number, | ||
241 | voice->flags, | ||
242 | voice->gf1_pan, | ||
243 | snd_gf1_i_read8(gus, SNDRV_GF1_VB_PAN) & 0x0f); | ||
244 | #endif | ||
245 | if (gus->gf1.enh_mode) { | ||
246 | voice->flags &= ~(SNDRV_GF1_VFLG_EFFECT_TIMER1|SNDRV_GF1_VFLG_PAN); | ||
247 | return; | ||
248 | } | ||
249 | if (!gus->gf1.smooth_pan) { | ||
250 | spin_lock_irqsave(&gus->reg_lock, flags); | ||
251 | snd_gf1_select_voice(gus, voice->number); | ||
252 | snd_gf1_write8(gus, SNDRV_GF1_VB_PAN, voice->gf1_pan); | ||
253 | spin_unlock_irqrestore(&gus->reg_lock, flags); | ||
254 | return; | ||
255 | } | ||
256 | if (!(voice->flags & SNDRV_GF1_VFLG_PAN)) /* before */ | ||
257 | voice->flags |= SNDRV_GF1_VFLG_EFFECT_TIMER1|SNDRV_GF1_VFLG_PAN; | ||
258 | spin_lock_irqsave(&gus->reg_lock, flags); | ||
259 | snd_gf1_select_voice(gus, voice->number); | ||
260 | old_pan = snd_gf1_read8(gus, SNDRV_GF1_VB_PAN) & 0x0f; | ||
261 | if (old_pan > voice->gf1_pan ) | ||
262 | old_pan--; | ||
263 | if (old_pan < voice->gf1_pan) | ||
264 | old_pan++; | ||
265 | snd_gf1_write8(gus, SNDRV_GF1_VB_PAN, old_pan); | ||
266 | spin_unlock_irqrestore(&gus->reg_lock, flags); | ||
267 | if (old_pan == voice->gf1_pan) /* the goal was reached */ | ||
268 | voice->flags &= ~(SNDRV_GF1_VFLG_EFFECT_TIMER1|SNDRV_GF1_VFLG_PAN); | ||
269 | #if 0 | ||
270 | snd_gf1_select_voice(gus, voice->number); | ||
271 | printk(" -%i- (1) do_pan_envelope - flags = 0x%x (0x%x -> 0x%x)\n", | ||
272 | voice->number, | ||
273 | voice->flags, | ||
274 | voice->gf1_pan, | ||
275 | snd_gf1_i_read8(gus, GF1_VB_PAN) & 0x0f); | ||
276 | #endif | ||
277 | } | ||
278 | |||
279 | static void set_enhanced_pan(snd_gus_card_t *gus, snd_gus_voice_t *voice, unsigned short pan) | ||
280 | { | ||
281 | unsigned long flags; | ||
282 | unsigned short vlo, vro; | ||
283 | |||
284 | vlo = SNDRV_GF1_ATTEN((SNDRV_GF1_ATTEN_TABLE_SIZE-1) - pan); | ||
285 | vro = SNDRV_GF1_ATTEN(pan); | ||
286 | if (pan != SNDRV_GF1_ATTEN_TABLE_SIZE - 1 && pan != 0) { | ||
287 | vlo >>= 1; | ||
288 | vro >>= 1; | ||
289 | } | ||
290 | vlo <<= 4; | ||
291 | vro <<= 4; | ||
292 | #if 0 | ||
293 | printk("vlo = 0x%x (0x%x), vro = 0x%x (0x%x)\n", | ||
294 | vlo, snd_gf1_i_read16(gus, GF1_VW_OFFSET_LEFT), | ||
295 | vro, snd_gf1_i_read16(gus, GF1_VW_OFFSET_RIGHT)); | ||
296 | #endif | ||
297 | spin_lock_irqsave(&gus->reg_lock, flags); | ||
298 | snd_gf1_select_voice(gus, voice->number); | ||
299 | snd_gf1_write16(gus, SNDRV_GF1_VW_OFFSET_LEFT_FINAL, vlo); | ||
300 | snd_gf1_write16(gus, SNDRV_GF1_VW_OFFSET_RIGHT_FINAL, vro); | ||
301 | spin_unlock_irqrestore(&gus->reg_lock, flags); | ||
302 | voice->vlo = vlo; | ||
303 | voice->vro = vro; | ||
304 | } | ||
305 | |||
306 | /* | ||
307 | * | ||
308 | */ | ||
309 | |||
310 | static void sample_start(snd_gus_card_t *gus, snd_gus_voice_t *voice, snd_seq_position_t position) | ||
311 | { | ||
312 | unsigned long flags; | ||
313 | unsigned int begin, addr, addr_end, addr_start; | ||
314 | int w_16; | ||
315 | simple_instrument_t *simple; | ||
316 | snd_seq_kinstr_t *instr; | ||
317 | |||
318 | instr = snd_seq_instr_find(gus->gf1.ilist, &voice->instr, 0, 1); | ||
319 | if (instr == NULL) | ||
320 | return; | ||
321 | voice->instr = instr->instr; /* copy ID to speedup aliases */ | ||
322 | simple = KINSTR_DATA(instr); | ||
323 | begin = simple->address.memory << 4; | ||
324 | w_16 = simple->format & SIMPLE_WAVE_16BIT ? 0x04 : 0; | ||
325 | addr_start = simple->loop_start; | ||
326 | if (simple->format & SIMPLE_WAVE_LOOP) { | ||
327 | addr_end = simple->loop_end; | ||
328 | } else { | ||
329 | addr_end = (simple->size << 4) - (w_16 ? 40 : 24); | ||
330 | } | ||
331 | if (simple->format & SIMPLE_WAVE_BACKWARD) { | ||
332 | addr = simple->loop_end; | ||
333 | if (position < simple->loop_end) | ||
334 | addr -= position; | ||
335 | } else { | ||
336 | addr = position; | ||
337 | } | ||
338 | voice->control = 0x00; | ||
339 | voice->mode = 0x20; /* enable offset registers */ | ||
340 | if (simple->format & SIMPLE_WAVE_16BIT) | ||
341 | voice->control |= 0x04; | ||
342 | if (simple->format & SIMPLE_WAVE_BACKWARD) | ||
343 | voice->control |= 0x40; | ||
344 | if (simple->format & SIMPLE_WAVE_LOOP) { | ||
345 | voice->control |= 0x08; | ||
346 | } else { | ||
347 | voice->control |= 0x20; | ||
348 | } | ||
349 | if (simple->format & SIMPLE_WAVE_BIDIR) | ||
350 | voice->control |= 0x10; | ||
351 | if (simple->format & SIMPLE_WAVE_ULAW) | ||
352 | voice->mode |= 0x40; | ||
353 | if (w_16) { | ||
354 | addr = ((addr << 1) & ~0x1f) | (addr & 0x0f); | ||
355 | addr_start = ((addr_start << 1) & ~0x1f) | (addr_start & 0x0f); | ||
356 | addr_end = ((addr_end << 1) & ~0x1f) | (addr_end & 0x0f); | ||
357 | } | ||
358 | addr += begin; | ||
359 | addr_start += begin; | ||
360 | addr_end += begin; | ||
361 | snd_gf1_stop_voice(gus, voice->number); | ||
362 | spin_lock_irqsave(&gus->reg_lock, flags); | ||
363 | snd_gf1_select_voice(gus, voice->number); | ||
364 | snd_gf1_write16(gus, SNDRV_GF1_VW_FREQUENCY, voice->fc_register + voice->fc_lfo); | ||
365 | voice->venv_state = VENV_BEFORE; | ||
366 | voice->volume_control = 0x03; | ||
367 | snd_gf1_write_addr(gus, SNDRV_GF1_VA_START, addr_start, w_16); | ||
368 | snd_gf1_write_addr(gus, SNDRV_GF1_VA_END, addr_end, w_16); | ||
369 | snd_gf1_write_addr(gus, SNDRV_GF1_VA_CURRENT, addr, w_16); | ||
370 | if (!gus->gf1.enh_mode) { | ||
371 | snd_gf1_write8(gus, SNDRV_GF1_VB_PAN, voice->gf1_pan); | ||
372 | } else { | ||
373 | snd_gf1_write16(gus, SNDRV_GF1_VW_OFFSET_LEFT, voice->vlo); | ||
374 | snd_gf1_write16(gus, SNDRV_GF1_VW_OFFSET_LEFT_FINAL, voice->vlo); | ||
375 | snd_gf1_write16(gus, SNDRV_GF1_VW_OFFSET_RIGHT, voice->vro); | ||
376 | snd_gf1_write16(gus, SNDRV_GF1_VW_OFFSET_RIGHT_FINAL, voice->vro); | ||
377 | snd_gf1_write8(gus, SNDRV_GF1_VB_ACCUMULATOR, voice->effect_accumulator); | ||
378 | snd_gf1_write16(gus, SNDRV_GF1_VW_EFFECT_VOLUME, voice->gf1_effect_volume); | ||
379 | snd_gf1_write16(gus, SNDRV_GF1_VW_EFFECT_VOLUME_FINAL, voice->gf1_effect_volume); | ||
380 | } | ||
381 | spin_unlock_irqrestore(&gus->reg_lock, flags); | ||
382 | do_volume_envelope(gus, voice); | ||
383 | spin_lock_irqsave(&gus->reg_lock, flags); | ||
384 | snd_gf1_select_voice(gus, voice->number); | ||
385 | if (gus->gf1.enh_mode) | ||
386 | snd_gf1_write8(gus, SNDRV_GF1_VB_MODE, voice->mode); | ||
387 | snd_gf1_write8(gus, SNDRV_GF1_VB_ADDRESS_CONTROL, voice->control); | ||
388 | if (!gus->gf1.enh_mode) { | ||
389 | snd_gf1_delay(gus); | ||
390 | snd_gf1_write8(gus, SNDRV_GF1_VB_ADDRESS_CONTROL, voice->control ); | ||
391 | } | ||
392 | spin_unlock_irqrestore(&gus->reg_lock, flags); | ||
393 | #if 0 | ||
394 | snd_gf1_print_voice_registers(gus); | ||
395 | #endif | ||
396 | voice->flags |= SNDRV_GF1_VFLG_RUNNING; | ||
397 | snd_seq_instr_free_use(gus->gf1.ilist, instr); | ||
398 | } | ||
399 | |||
400 | static void sample_stop(snd_gus_card_t *gus, snd_gus_voice_t *voice, snd_seq_stop_mode_t mode) | ||
401 | { | ||
402 | unsigned char control; | ||
403 | unsigned long flags; | ||
404 | |||
405 | if (!(voice->flags & SNDRV_GF1_VFLG_RUNNING)) | ||
406 | return; | ||
407 | switch (mode) { | ||
408 | default: | ||
409 | if (gus->gf1.volume_ramp > 0) { | ||
410 | if (voice->venv_state < VENV_RELEASE) { | ||
411 | voice->venv_state = VENV_RELEASE; | ||
412 | do_volume_envelope(gus, voice); | ||
413 | } | ||
414 | } | ||
415 | if (mode != SAMPLE_STOP_VENVELOPE) { | ||
416 | snd_gf1_stop_voice(gus, voice->number); | ||
417 | spin_lock_irqsave(&gus->reg_lock, flags); | ||
418 | snd_gf1_select_voice(gus, voice->number); | ||
419 | snd_gf1_write16(gus, SNDRV_GF1_VW_VOLUME, SNDRV_GF1_MIN_VOLUME); | ||
420 | spin_unlock_irqrestore(&gus->reg_lock, flags); | ||
421 | voice->flags &= ~SNDRV_GF1_VFLG_RUNNING; | ||
422 | } | ||
423 | break; | ||
424 | case SAMPLE_STOP_LOOP: /* disable loop only */ | ||
425 | spin_lock_irqsave(&gus->reg_lock, flags); | ||
426 | snd_gf1_select_voice(gus, voice->number); | ||
427 | control = snd_gf1_read8(gus, SNDRV_GF1_VB_ADDRESS_CONTROL); | ||
428 | control &= ~(0x83 | 0x04); | ||
429 | control |= 0x20; | ||
430 | snd_gf1_write8(gus, SNDRV_GF1_VB_ADDRESS_CONTROL, control); | ||
431 | spin_unlock_irqrestore(&gus->reg_lock, flags); | ||
432 | break; | ||
433 | } | ||
434 | } | ||
435 | |||
436 | static void sample_freq(snd_gus_card_t *gus, snd_gus_voice_t *voice, snd_seq_frequency_t freq) | ||
437 | { | ||
438 | unsigned long flags; | ||
439 | |||
440 | spin_lock_irqsave(&gus->reg_lock, flags); | ||
441 | voice->fc_register = snd_gf1_translate_freq(gus, freq); | ||
442 | snd_gf1_select_voice(gus, voice->number); | ||
443 | snd_gf1_write16(gus, SNDRV_GF1_VW_FREQUENCY, voice->fc_register + voice->fc_lfo); | ||
444 | spin_unlock_irqrestore(&gus->reg_lock, flags); | ||
445 | } | ||
446 | |||
447 | static void sample_volume(snd_gus_card_t *gus, snd_gus_voice_t *voice, snd_seq_ev_volume_t *volume) | ||
448 | { | ||
449 | if (volume->volume >= 0) { | ||
450 | volume->volume &= 0x3fff; | ||
451 | voice->gf1_volume = snd_gf1_lvol_to_gvol_raw(volume->volume << 2) << 4; | ||
452 | voice->venv_state_prev = VENV_SUSTAIN; | ||
453 | voice->venv_state = VENV_VOLUME; | ||
454 | do_volume_envelope(gus, voice); | ||
455 | } | ||
456 | if (volume->lr >= 0) { | ||
457 | volume->lr &= 0x3fff; | ||
458 | if (!gus->gf1.enh_mode) { | ||
459 | voice->gf1_pan = (volume->lr >> 10) & 15; | ||
460 | if (!gus->gf1.full_range_pan) { | ||
461 | if (voice->gf1_pan == 0) | ||
462 | voice->gf1_pan++; | ||
463 | if (voice->gf1_pan == 15) | ||
464 | voice->gf1_pan--; | ||
465 | } | ||
466 | voice->flags &= ~SNDRV_GF1_VFLG_PAN; /* before */ | ||
467 | do_pan_envelope(gus, voice); | ||
468 | } else { | ||
469 | set_enhanced_pan(gus, voice, volume->lr >> 7); | ||
470 | } | ||
471 | } | ||
472 | } | ||
473 | |||
474 | static void sample_loop(snd_gus_card_t *gus, snd_gus_voice_t *voice, snd_seq_ev_loop_t *loop) | ||
475 | { | ||
476 | unsigned long flags; | ||
477 | int w_16 = voice->control & 0x04; | ||
478 | unsigned int begin, addr_start, addr_end; | ||
479 | simple_instrument_t *simple; | ||
480 | snd_seq_kinstr_t *instr; | ||
481 | |||
482 | #if 0 | ||
483 | printk("voice_loop: start = 0x%x, end = 0x%x\n", loop->start, loop->end); | ||
484 | #endif | ||
485 | instr = snd_seq_instr_find(gus->gf1.ilist, &voice->instr, 0, 1); | ||
486 | if (instr == NULL) | ||
487 | return; | ||
488 | voice->instr = instr->instr; /* copy ID to speedup aliases */ | ||
489 | simple = KINSTR_DATA(instr); | ||
490 | begin = simple->address.memory; | ||
491 | addr_start = loop->start; | ||
492 | addr_end = loop->end; | ||
493 | addr_start = (((addr_start << 1) & ~0x1f) | (addr_start & 0x0f)) + begin; | ||
494 | addr_end = (((addr_end << 1) & ~0x1f) | (addr_end & 0x0f)) + begin; | ||
495 | spin_lock_irqsave(&gus->reg_lock, flags); | ||
496 | snd_gf1_select_voice(gus, voice->number); | ||
497 | snd_gf1_write_addr(gus, SNDRV_GF1_VA_START, addr_start, w_16); | ||
498 | snd_gf1_write_addr(gus, SNDRV_GF1_VA_END, addr_end, w_16); | ||
499 | spin_unlock_irqrestore(&gus->reg_lock, flags); | ||
500 | snd_seq_instr_free_use(gus->gf1.ilist, instr); | ||
501 | } | ||
502 | |||
503 | static void sample_pos(snd_gus_card_t *gus, snd_gus_voice_t *voice, snd_seq_position_t position) | ||
504 | { | ||
505 | unsigned long flags; | ||
506 | int w_16 = voice->control & 0x04; | ||
507 | unsigned int begin, addr; | ||
508 | simple_instrument_t *simple; | ||
509 | snd_seq_kinstr_t *instr; | ||
510 | |||
511 | #if 0 | ||
512 | printk("voice_loop: start = 0x%x, end = 0x%x\n", loop->start, loop->end); | ||
513 | #endif | ||
514 | instr = snd_seq_instr_find(gus->gf1.ilist, &voice->instr, 0, 1); | ||
515 | if (instr == NULL) | ||
516 | return; | ||
517 | voice->instr = instr->instr; /* copy ID to speedup aliases */ | ||
518 | simple = KINSTR_DATA(instr); | ||
519 | begin = simple->address.memory; | ||
520 | addr = (((position << 1) & ~0x1f) | (position & 0x0f)) + begin; | ||
521 | spin_lock_irqsave(&gus->reg_lock, flags); | ||
522 | snd_gf1_select_voice(gus, voice->number); | ||
523 | snd_gf1_write_addr(gus, SNDRV_GF1_VA_CURRENT, addr, w_16); | ||
524 | spin_unlock_irqrestore(&gus->reg_lock, flags); | ||
525 | snd_seq_instr_free_use(gus->gf1.ilist, instr); | ||
526 | } | ||
527 | |||
528 | #if 0 | ||
529 | |||
530 | static unsigned char get_effects_mask( ultra_card_t *card, int value ) | ||
531 | { | ||
532 | if ( value > 7 ) return 0; | ||
533 | if ( card -> gf1.effects && card -> gf1.effects -> chip_type == ULTRA_EFFECT_CHIP_INTERWAVE ) | ||
534 | return card -> gf1.effects -> chip.interwave.voice_output[ value ]; | ||
535 | return 0; | ||
536 | } | ||
537 | |||
538 | #endif | ||
539 | |||
540 | static void sample_private1(snd_gus_card_t *card, snd_gus_voice_t *voice, unsigned char *data) | ||
541 | { | ||
542 | #if 0 | ||
543 | unsigned long flags; | ||
544 | unsigned char uc; | ||
545 | |||
546 | switch ( *data ) { | ||
547 | case ULTRA_PRIV1_IW_EFFECT: | ||
548 | uc = get_effects_mask( card, ultra_get_byte( data, 4 ) ); | ||
549 | uc |= get_effects_mask( card, ultra_get_byte( data, 4 ) >> 4 ); | ||
550 | uc |= get_effects_mask( card, ultra_get_byte( data, 5 ) ); | ||
551 | uc |= get_effects_mask( card, ultra_get_byte( data, 5 ) >> 4 ); | ||
552 | voice -> data.simple.effect_accumulator = uc; | ||
553 | voice -> data.simple.effect_volume = ultra_translate_voice_volume( card, ultra_get_word( data, 2 ) ) << 4; | ||
554 | if ( !card -> gf1.enh_mode ) return; | ||
555 | if ( voice -> flags & VFLG_WAIT_FOR_START ) return; | ||
556 | if ( voice -> flags & VFLG_RUNNING ) | ||
557 | { | ||
558 | CLI( &flags ); | ||
559 | gf1_select_voice( card, voice -> number ); | ||
560 | ultra_write8( card, GF1_VB_ACCUMULATOR, voice -> data.simple.effect_accumulator ); | ||
561 | ultra_write16( card, GF1_VW_EFFECT_VOLUME_FINAL, voice -> data.simple.effect_volume ); | ||
562 | STI( &flags ); | ||
563 | } | ||
564 | break; | ||
565 | case ULTRA_PRIV1_IW_LFO: | ||
566 | ultra_lfo_command( card, voice -> number, data ); | ||
567 | } | ||
568 | #endif | ||
569 | } | ||
570 | |||
571 | #if 0 | ||
572 | |||
573 | /* | ||
574 | * | ||
575 | */ | ||
576 | |||
577 | static void note_stop( ultra_card_t *card, ultra_voice_t *voice, int wait ) | ||
578 | { | ||
579 | } | ||
580 | |||
581 | static void note_wait( ultra_card_t *card, ultra_voice_t *voice ) | ||
582 | { | ||
583 | } | ||
584 | |||
585 | static void note_off( ultra_card_t *card, ultra_voice_t *voice ) | ||
586 | { | ||
587 | } | ||
588 | |||
589 | static void note_volume( ultra_card_t *card, ultra_voice_t *voice ) | ||
590 | { | ||
591 | } | ||
592 | |||
593 | static void note_pitchbend( ultra_card_t *card, ultra_voice_t *voice ) | ||
594 | { | ||
595 | } | ||
596 | |||
597 | static void note_vibrato( ultra_card_t *card, ultra_voice_t *voice ) | ||
598 | { | ||
599 | } | ||
600 | |||
601 | static void note_tremolo( ultra_card_t *card, ultra_voice_t *voice ) | ||
602 | { | ||
603 | } | ||
604 | |||
605 | /* | ||
606 | * | ||
607 | */ | ||
608 | |||
609 | static void chn_trigger_down( ultra_card_t *card, ultra_channel_t *channel, ultra_instrument_t *instrument, unsigned char note, unsigned char velocity, unsigned char priority ) | ||
610 | { | ||
611 | } | ||
612 | |||
613 | static void chn_trigger_up( ultra_card_t *card, ultra_note_t *note ) | ||
614 | { | ||
615 | } | ||
616 | |||
617 | static void chn_control( ultra_card_t *card, ultra_channel_t *channel, unsigned short p1, unsigned short p2 ) | ||
618 | { | ||
619 | } | ||
620 | |||
621 | /* | ||
622 | * | ||
623 | */ | ||
624 | |||
625 | #endif | ||
626 | |||
627 | void snd_gf1_simple_init(snd_gus_voice_t *voice) | ||
628 | { | ||
629 | voice->handler_wave = interrupt_wave; | ||
630 | voice->handler_volume = interrupt_volume; | ||
631 | voice->handler_effect = interrupt_effect; | ||
632 | voice->volume_change = NULL; | ||
633 | voice->sample_ops = &sample_ops; | ||
634 | } | ||
diff --git a/sound/isa/gus/gus_synth.c b/sound/isa/gus/gus_synth.c new file mode 100644 index 000000000000..66552e6013a4 --- /dev/null +++ b/sound/isa/gus/gus_synth.c | |||
@@ -0,0 +1,329 @@ | |||
1 | /* | ||
2 | * Routines for Gravis UltraSound soundcards - Synthesizer | ||
3 | * Copyright (c) by Jaroslav Kysela <perex@suse.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 <linux/init.h> | ||
24 | #include <linux/time.h> | ||
25 | #include <sound/core.h> | ||
26 | #include <sound/gus.h> | ||
27 | #include <sound/seq_device.h> | ||
28 | |||
29 | MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>"); | ||
30 | MODULE_DESCRIPTION("Routines for Gravis UltraSound soundcards - Synthesizer"); | ||
31 | MODULE_LICENSE("GPL"); | ||
32 | |||
33 | /* | ||
34 | * | ||
35 | */ | ||
36 | |||
37 | static void snd_gus_synth_free_voices(snd_gus_card_t * gus, int client, int port) | ||
38 | { | ||
39 | int idx; | ||
40 | snd_gus_voice_t * voice; | ||
41 | |||
42 | for (idx = 0; idx < 32; idx++) { | ||
43 | voice = &gus->gf1.voices[idx]; | ||
44 | if (voice->use && voice->client == client && voice->port == port) | ||
45 | snd_gf1_free_voice(gus, voice); | ||
46 | } | ||
47 | } | ||
48 | |||
49 | static int snd_gus_synth_use(void *private_data, snd_seq_port_subscribe_t *info) | ||
50 | { | ||
51 | snd_gus_port_t * port = (snd_gus_port_t *)private_data; | ||
52 | snd_gus_card_t * gus = port->gus; | ||
53 | snd_gus_voice_t * voice; | ||
54 | unsigned int idx; | ||
55 | |||
56 | if (info->voices > 32) | ||
57 | return -EINVAL; | ||
58 | down(&gus->register_mutex); | ||
59 | if (!snd_gus_use_inc(gus)) { | ||
60 | up(&gus->register_mutex); | ||
61 | return -EFAULT; | ||
62 | } | ||
63 | for (idx = 0; idx < info->voices; idx++) { | ||
64 | voice = snd_gf1_alloc_voice(gus, SNDRV_GF1_VOICE_TYPE_SYNTH, info->sender.client, info->sender.port); | ||
65 | if (voice == NULL) { | ||
66 | snd_gus_synth_free_voices(gus, info->sender.client, info->sender.port); | ||
67 | snd_gus_use_dec(gus); | ||
68 | up(&gus->register_mutex); | ||
69 | return -EBUSY; | ||
70 | } | ||
71 | voice->index = idx; | ||
72 | } | ||
73 | up(&gus->register_mutex); | ||
74 | return 0; | ||
75 | } | ||
76 | |||
77 | static int snd_gus_synth_unuse(void *private_data, snd_seq_port_subscribe_t *info) | ||
78 | { | ||
79 | snd_gus_port_t * port = (snd_gus_port_t *)private_data; | ||
80 | snd_gus_card_t * gus = port->gus; | ||
81 | |||
82 | down(&gus->register_mutex); | ||
83 | snd_gus_synth_free_voices(gus, info->sender.client, info->sender.port); | ||
84 | snd_gus_use_dec(gus); | ||
85 | up(&gus->register_mutex); | ||
86 | return 0; | ||
87 | } | ||
88 | |||
89 | /* | ||
90 | * | ||
91 | */ | ||
92 | |||
93 | static void snd_gus_synth_free_private_instruments(snd_gus_port_t *p, int client) | ||
94 | { | ||
95 | snd_seq_instr_header_t ifree; | ||
96 | |||
97 | memset(&ifree, 0, sizeof(ifree)); | ||
98 | ifree.cmd = SNDRV_SEQ_INSTR_FREE_CMD_PRIVATE; | ||
99 | snd_seq_instr_list_free_cond(p->gus->gf1.ilist, &ifree, client, 0); | ||
100 | } | ||
101 | |||
102 | int snd_gus_synth_event_input(snd_seq_event_t *ev, int direct, void *private_data, int atomic, int hop) | ||
103 | { | ||
104 | snd_gus_port_t * p = (snd_gus_port_t *) private_data; | ||
105 | |||
106 | snd_assert(p != NULL, return -EINVAL); | ||
107 | if (ev->type >= SNDRV_SEQ_EVENT_SAMPLE && | ||
108 | ev->type <= SNDRV_SEQ_EVENT_SAMPLE_PRIVATE1) { | ||
109 | snd_gus_sample_event(ev, p); | ||
110 | return 0; | ||
111 | } | ||
112 | if (ev->source.client == SNDRV_SEQ_CLIENT_SYSTEM && | ||
113 | ev->source.port == SNDRV_SEQ_PORT_SYSTEM_ANNOUNCE) { | ||
114 | if (ev->type == SNDRV_SEQ_EVENT_CLIENT_EXIT) { | ||
115 | snd_gus_synth_free_private_instruments(p, ev->data.addr.client); | ||
116 | return 0; | ||
117 | } | ||
118 | } | ||
119 | if (direct) { | ||
120 | if (ev->type >= SNDRV_SEQ_EVENT_INSTR_BEGIN) { | ||
121 | snd_seq_instr_event(&p->gus->gf1.iwffff_ops.kops, | ||
122 | p->gus->gf1.ilist, | ||
123 | ev, | ||
124 | p->gus->gf1.seq_client, | ||
125 | atomic, hop); | ||
126 | return 0; | ||
127 | } | ||
128 | } | ||
129 | return 0; | ||
130 | } | ||
131 | |||
132 | static void snd_gus_synth_instr_notify(void *private_data, | ||
133 | snd_seq_kinstr_t *instr, | ||
134 | int what) | ||
135 | { | ||
136 | unsigned int idx; | ||
137 | snd_gus_card_t *gus = private_data; | ||
138 | snd_gus_voice_t *pvoice; | ||
139 | unsigned long flags; | ||
140 | |||
141 | spin_lock_irqsave(&gus->event_lock, flags); | ||
142 | for (idx = 0; idx < 32; idx++) { | ||
143 | pvoice = &gus->gf1.voices[idx]; | ||
144 | if (pvoice->use && !memcmp(&pvoice->instr, &instr->instr, sizeof(pvoice->instr))) { | ||
145 | if (pvoice->sample_ops && pvoice->sample_ops->sample_stop) { | ||
146 | pvoice->sample_ops->sample_stop(gus, pvoice, SAMPLE_STOP_IMMEDIATELY); | ||
147 | } else { | ||
148 | snd_gf1_stop_voice(gus, pvoice->number); | ||
149 | pvoice->flags &= ~SNDRV_GF1_VFLG_RUNNING; | ||
150 | } | ||
151 | } | ||
152 | } | ||
153 | spin_unlock_irqrestore(&gus->event_lock, flags); | ||
154 | } | ||
155 | |||
156 | /* | ||
157 | * | ||
158 | */ | ||
159 | |||
160 | static void snd_gus_synth_free_port(void *private_data) | ||
161 | { | ||
162 | snd_gus_port_t * p = (snd_gus_port_t *)private_data; | ||
163 | |||
164 | if (p) | ||
165 | snd_midi_channel_free_set(p->chset); | ||
166 | } | ||
167 | |||
168 | static int snd_gus_synth_create_port(snd_gus_card_t * gus, int idx) | ||
169 | { | ||
170 | snd_gus_port_t * p; | ||
171 | snd_seq_port_callback_t callbacks; | ||
172 | char name[32]; | ||
173 | int result; | ||
174 | |||
175 | p = &gus->gf1.seq_ports[idx]; | ||
176 | p->chset = snd_midi_channel_alloc_set(16); | ||
177 | if (p->chset == NULL) | ||
178 | return -ENOMEM; | ||
179 | p->chset->private_data = p; | ||
180 | p->gus = gus; | ||
181 | p->client = gus->gf1.seq_client; | ||
182 | |||
183 | memset(&callbacks, 0, sizeof(callbacks)); | ||
184 | callbacks.owner = THIS_MODULE; | ||
185 | callbacks.use = snd_gus_synth_use; | ||
186 | callbacks.unuse = snd_gus_synth_unuse; | ||
187 | callbacks.event_input = snd_gus_synth_event_input; | ||
188 | callbacks.private_free = snd_gus_synth_free_port; | ||
189 | callbacks.private_data = p; | ||
190 | |||
191 | sprintf(name, "%s port %i", gus->interwave ? "AMD InterWave" : "GF1", idx); | ||
192 | p->chset->port = snd_seq_event_port_attach(gus->gf1.seq_client, | ||
193 | &callbacks, | ||
194 | SNDRV_SEQ_PORT_CAP_WRITE | SNDRV_SEQ_PORT_CAP_SUBS_WRITE, | ||
195 | SNDRV_SEQ_PORT_TYPE_DIRECT_SAMPLE | | ||
196 | SNDRV_SEQ_PORT_TYPE_SYNTH, | ||
197 | 16, 0, | ||
198 | name); | ||
199 | if (p->chset->port < 0) { | ||
200 | result = p->chset->port; | ||
201 | snd_gus_synth_free_port(p); | ||
202 | return result; | ||
203 | } | ||
204 | p->port = p->chset->port; | ||
205 | return 0; | ||
206 | } | ||
207 | |||
208 | /* | ||
209 | * | ||
210 | */ | ||
211 | |||
212 | static int snd_gus_synth_new_device(snd_seq_device_t *dev) | ||
213 | { | ||
214 | snd_gus_card_t *gus; | ||
215 | int client, i; | ||
216 | snd_seq_client_callback_t callbacks; | ||
217 | snd_seq_client_info_t *cinfo; | ||
218 | snd_seq_port_subscribe_t sub; | ||
219 | snd_iwffff_ops_t *iwops; | ||
220 | snd_gf1_ops_t *gf1ops; | ||
221 | snd_simple_ops_t *simpleops; | ||
222 | |||
223 | gus = *(snd_gus_card_t**)SNDRV_SEQ_DEVICE_ARGPTR(dev); | ||
224 | if (gus == NULL) | ||
225 | return -EINVAL; | ||
226 | |||
227 | init_MUTEX(&gus->register_mutex); | ||
228 | gus->gf1.seq_client = -1; | ||
229 | |||
230 | cinfo = kmalloc(sizeof(*cinfo), GFP_KERNEL); | ||
231 | if (! cinfo) | ||
232 | return -ENOMEM; | ||
233 | |||
234 | /* allocate new client */ | ||
235 | memset(&callbacks, 0, sizeof(callbacks)); | ||
236 | callbacks.private_data = gus; | ||
237 | callbacks.allow_output = callbacks.allow_input = 1; | ||
238 | client = gus->gf1.seq_client = | ||
239 | snd_seq_create_kernel_client(gus->card, 1, &callbacks); | ||
240 | if (client < 0) { | ||
241 | kfree(cinfo); | ||
242 | return client; | ||
243 | } | ||
244 | |||
245 | /* change name of client */ | ||
246 | memset(cinfo, 0, sizeof(*cinfo)); | ||
247 | cinfo->client = client; | ||
248 | cinfo->type = KERNEL_CLIENT; | ||
249 | sprintf(cinfo->name, gus->interwave ? "AMD InterWave" : "GF1"); | ||
250 | snd_seq_kernel_client_ctl(client, SNDRV_SEQ_IOCTL_SET_CLIENT_INFO, cinfo); | ||
251 | kfree(cinfo); | ||
252 | |||
253 | for (i = 0; i < 4; i++) | ||
254 | snd_gus_synth_create_port(gus, i); | ||
255 | |||
256 | gus->gf1.ilist = snd_seq_instr_list_new(); | ||
257 | if (gus->gf1.ilist == NULL) { | ||
258 | snd_seq_delete_kernel_client(client); | ||
259 | gus->gf1.seq_client = -1; | ||
260 | return -ENOMEM; | ||
261 | } | ||
262 | gus->gf1.ilist->flags = SNDRV_SEQ_INSTR_FLG_DIRECT; | ||
263 | |||
264 | simpleops = &gus->gf1.simple_ops; | ||
265 | snd_seq_simple_init(simpleops, gus, NULL); | ||
266 | simpleops->put_sample = snd_gus_simple_put_sample; | ||
267 | simpleops->get_sample = snd_gus_simple_get_sample; | ||
268 | simpleops->remove_sample = snd_gus_simple_remove_sample; | ||
269 | simpleops->notify = snd_gus_synth_instr_notify; | ||
270 | |||
271 | gf1ops = &gus->gf1.gf1_ops; | ||
272 | snd_seq_gf1_init(gf1ops, gus, &simpleops->kops); | ||
273 | gf1ops->put_sample = snd_gus_gf1_put_sample; | ||
274 | gf1ops->get_sample = snd_gus_gf1_get_sample; | ||
275 | gf1ops->remove_sample = snd_gus_gf1_remove_sample; | ||
276 | gf1ops->notify = snd_gus_synth_instr_notify; | ||
277 | |||
278 | iwops = &gus->gf1.iwffff_ops; | ||
279 | snd_seq_iwffff_init(iwops, gus, &gf1ops->kops); | ||
280 | iwops->put_sample = snd_gus_iwffff_put_sample; | ||
281 | iwops->get_sample = snd_gus_iwffff_get_sample; | ||
282 | iwops->remove_sample = snd_gus_iwffff_remove_sample; | ||
283 | iwops->notify = snd_gus_synth_instr_notify; | ||
284 | |||
285 | memset(&sub, 0, sizeof(sub)); | ||
286 | sub.sender.client = SNDRV_SEQ_CLIENT_SYSTEM; | ||
287 | sub.sender.port = SNDRV_SEQ_PORT_SYSTEM_ANNOUNCE; | ||
288 | sub.dest.client = client; | ||
289 | sub.dest.port = 0; | ||
290 | snd_seq_kernel_client_ctl(client, SNDRV_SEQ_IOCTL_SUBSCRIBE_PORT, &sub); | ||
291 | |||
292 | return 0; | ||
293 | } | ||
294 | |||
295 | static int snd_gus_synth_delete_device(snd_seq_device_t *dev) | ||
296 | { | ||
297 | snd_gus_card_t *gus; | ||
298 | |||
299 | gus = *(snd_gus_card_t**)SNDRV_SEQ_DEVICE_ARGPTR(dev); | ||
300 | if (gus == NULL) | ||
301 | return -EINVAL; | ||
302 | |||
303 | if (gus->gf1.seq_client >= 0) { | ||
304 | snd_seq_delete_kernel_client(gus->gf1.seq_client); | ||
305 | gus->gf1.seq_client = -1; | ||
306 | } | ||
307 | if (gus->gf1.ilist) | ||
308 | snd_seq_instr_list_free(&gus->gf1.ilist); | ||
309 | return 0; | ||
310 | } | ||
311 | |||
312 | static int __init alsa_gus_synth_init(void) | ||
313 | { | ||
314 | static snd_seq_dev_ops_t ops = { | ||
315 | snd_gus_synth_new_device, | ||
316 | snd_gus_synth_delete_device | ||
317 | }; | ||
318 | |||
319 | return snd_seq_device_register_driver(SNDRV_SEQ_DEV_ID_GUS, &ops, | ||
320 | sizeof(snd_gus_card_t*)); | ||
321 | } | ||
322 | |||
323 | static void __exit alsa_gus_synth_exit(void) | ||
324 | { | ||
325 | snd_seq_device_unregister_driver(SNDRV_SEQ_DEV_ID_GUS); | ||
326 | } | ||
327 | |||
328 | module_init(alsa_gus_synth_init) | ||
329 | module_exit(alsa_gus_synth_exit) | ||
diff --git a/sound/isa/gus/gus_tables.h b/sound/isa/gus/gus_tables.h new file mode 100644 index 000000000000..ed8e9d85ad31 --- /dev/null +++ b/sound/isa/gus/gus_tables.h | |||
@@ -0,0 +1,86 @@ | |||
1 | /* | ||
2 | * Copyright (c) by Jaroslav Kysela <perex@suse.cz> | ||
3 | * | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or modify | ||
6 | * it under the terms of the GNU General Public License as published by | ||
7 | * the Free Software Foundation; either version 2 of the License, or | ||
8 | * (at your option) any later version. | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, | ||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | * GNU General Public License for more details. | ||
14 | * | ||
15 | * You should have received a copy of the GNU General Public License | ||
16 | * along with this program; if not, write to the Free Software | ||
17 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
18 | * | ||
19 | */ | ||
20 | |||
21 | #define SNDRV_GF1_SCALE_TABLE_SIZE 128 | ||
22 | #define SNDRV_GF1_ATTEN_TABLE_SIZE 128 | ||
23 | |||
24 | #ifdef __GUS_TABLES_ALLOC__ | ||
25 | |||
26 | unsigned int snd_gf1_scale_table[SNDRV_GF1_SCALE_TABLE_SIZE] = | ||
27 | { | ||
28 | 8372, 8870, 9397, 9956, 10548, 11175, | ||
29 | 11840, 12544, 13290, 14080, 14917, 15804, | ||
30 | 16744, 17740, 18795, 19912, 21096, 22351, | ||
31 | 23680, 25088, 26580, 28160, 29834, 31609, | ||
32 | 33488, 35479, 37589, 39824, 42192, 44701, | ||
33 | 47359, 50175, 53159, 56320, 59669, 63217, | ||
34 | 66976, 70959, 75178, 79649, 84385, 89402, | ||
35 | 94719, 100351, 106318, 112640, 119338, 126434, | ||
36 | 133952, 141918, 150356, 159297, 168769, 178805, | ||
37 | 189437, 200702, 212636, 225280, 238676, 252868, | ||
38 | 267905, 283835, 300713, 318594, 337539, 357610, | ||
39 | 378874, 401403, 425272, 450560, 477352, 505737, | ||
40 | 535809, 567670, 601425, 637188, 675077, 715219, | ||
41 | 757749, 802807, 850544, 901120, 954703, 1011473, | ||
42 | 1071618, 1135340, 1202851, 1274376, 1350154, 1430439, | ||
43 | 1515497, 1605613, 1701088, 1802240, 1909407, 2022946, | ||
44 | 2143237, 2270680, 2405702, 2548752, 2700309, 2860878, | ||
45 | 3030994, 3211227, 3402176, 3604480, 3818814, 4045892, | ||
46 | 4286473, 4541360, 4811404, 5097505, 5400618, 5721755, | ||
47 | 6061989, 6422453, 6804352, 7208960, 7637627, 8091784, | ||
48 | 8572947, 9082720, 9622807, 10195009, 10801236, 11443511, | ||
49 | 12123977, 12844906 | ||
50 | }; | ||
51 | |||
52 | unsigned short snd_gf1_atten_table[SNDRV_GF1_ATTEN_TABLE_SIZE] = { | ||
53 | 4095 /* 0 */,1789 /* 1 */,1533 /* 2 */,1383 /* 3 */,1277 /* 4 */, | ||
54 | 1195 /* 5 */,1127 /* 6 */,1070 /* 7 */,1021 /* 8 */,978 /* 9 */, | ||
55 | 939 /* 10 */,903 /* 11 */,871 /* 12 */,842 /* 13 */,814 /* 14 */, | ||
56 | 789 /* 15 */,765 /* 16 */,743 /* 17 */,722 /* 18 */,702 /* 19 */, | ||
57 | 683 /* 20 */,665 /* 21 */,647 /* 22 */,631 /* 23 */,615 /* 24 */, | ||
58 | 600 /* 25 */,586 /* 26 */,572 /* 27 */,558 /* 28 */,545 /* 29 */, | ||
59 | 533 /* 30 */,521 /* 31 */,509 /* 32 */,498 /* 33 */,487 /* 34 */, | ||
60 | 476 /* 35 */,466 /* 36 */,455 /* 37 */,446 /* 38 */,436 /* 39 */, | ||
61 | 427 /* 40 */,418 /* 41 */,409 /* 42 */,400 /* 43 */,391 /* 44 */, | ||
62 | 383 /* 45 */,375 /* 46 */,367 /* 47 */,359 /* 48 */,352 /* 49 */, | ||
63 | 344 /* 50 */,337 /* 51 */,330 /* 52 */,323 /* 53 */,316 /* 54 */, | ||
64 | 309 /* 55 */,302 /* 56 */,296 /* 57 */,289 /* 58 */,283 /* 59 */, | ||
65 | 277 /* 60 */,271 /* 61 */,265 /* 62 */,259 /* 63 */,253 /* 64 */, | ||
66 | 247 /* 65 */,242 /* 66 */,236 /* 67 */,231 /* 68 */,225 /* 69 */, | ||
67 | 220 /* 70 */,215 /* 71 */,210 /* 72 */,205 /* 73 */,199 /* 74 */, | ||
68 | 195 /* 75 */,190 /* 76 */,185 /* 77 */,180 /* 78 */,175 /* 79 */, | ||
69 | 171 /* 80 */,166 /* 81 */,162 /* 82 */,157 /* 83 */,153 /* 84 */, | ||
70 | 148 /* 85 */,144 /* 86 */,140 /* 87 */,135 /* 88 */,131 /* 89 */, | ||
71 | 127 /* 90 */,123 /* 91 */,119 /* 92 */,115 /* 93 */,111 /* 94 */, | ||
72 | 107 /* 95 */,103 /* 96 */,100 /* 97 */,96 /* 98 */,92 /* 99 */, | ||
73 | 88 /* 100 */,85 /* 101 */,81 /* 102 */,77 /* 103 */,74 /* 104 */, | ||
74 | 70 /* 105 */,67 /* 106 */,63 /* 107 */,60 /* 108 */,56 /* 109 */, | ||
75 | 53 /* 110 */,50 /* 111 */,46 /* 112 */,43 /* 113 */,40 /* 114 */, | ||
76 | 37 /* 115 */,33 /* 116 */,30 /* 117 */,27 /* 118 */,24 /* 119 */, | ||
77 | 21 /* 120 */,18 /* 121 */,15 /* 122 */,12 /* 123 */,9 /* 124 */, | ||
78 | 6 /* 125 */,3 /* 126 */,0 /* 127 */, | ||
79 | }; | ||
80 | |||
81 | #else | ||
82 | |||
83 | extern unsigned int snd_gf1_scale_table[SNDRV_GF1_SCALE_TABLE_SIZE]; | ||
84 | extern unsigned short snd_gf1_atten_table[SNDRV_GF1_ATTEN_TABLE_SIZE]; | ||
85 | |||
86 | #endif | ||
diff --git a/sound/isa/gus/gus_timer.c b/sound/isa/gus/gus_timer.c new file mode 100644 index 000000000000..9876603ff6c1 --- /dev/null +++ b/sound/isa/gus/gus_timer.c | |||
@@ -0,0 +1,204 @@ | |||
1 | /* | ||
2 | * Routines for Gravis UltraSound soundcards - Timers | ||
3 | * Copyright (c) by Jaroslav Kysela <perex@suse.cz> | ||
4 | * | ||
5 | * GUS have similar timers as AdLib (OPL2/OPL3 chips). | ||
6 | * | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License as published by | ||
10 | * the Free Software Foundation; either version 2 of the License, or | ||
11 | * (at your option) any later version. | ||
12 | * | ||
13 | * This program is distributed in the hope that it will be useful, | ||
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
16 | * GNU General Public License for more details. | ||
17 | * | ||
18 | * You should have received a copy of the GNU General Public License | ||
19 | * along with this program; if not, write to the Free Software | ||
20 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
21 | * | ||
22 | */ | ||
23 | |||
24 | #include <sound/driver.h> | ||
25 | #include <linux/time.h> | ||
26 | #include <sound/core.h> | ||
27 | #include <sound/gus.h> | ||
28 | |||
29 | /* | ||
30 | * Timer 1 - 80us | ||
31 | */ | ||
32 | |||
33 | static int snd_gf1_timer1_start(snd_timer_t * timer) | ||
34 | { | ||
35 | unsigned long flags; | ||
36 | unsigned char tmp; | ||
37 | unsigned int ticks; | ||
38 | snd_gus_card_t *gus; | ||
39 | |||
40 | gus = snd_timer_chip(timer); | ||
41 | spin_lock_irqsave(&gus->reg_lock, flags); | ||
42 | ticks = timer->sticks; | ||
43 | tmp = (gus->gf1.timer_enabled |= 4); | ||
44 | snd_gf1_write8(gus, SNDRV_GF1_GB_ADLIB_TIMER_1, 256 - ticks); /* timer 1 count */ | ||
45 | snd_gf1_write8(gus, SNDRV_GF1_GB_SOUND_BLASTER_CONTROL, tmp); /* enable timer 1 IRQ */ | ||
46 | snd_gf1_adlib_write(gus, 0x04, tmp >> 2); /* timer 2 start */ | ||
47 | spin_unlock_irqrestore(&gus->reg_lock, flags); | ||
48 | return 0; | ||
49 | } | ||
50 | |||
51 | static int snd_gf1_timer1_stop(snd_timer_t * timer) | ||
52 | { | ||
53 | unsigned long flags; | ||
54 | unsigned char tmp; | ||
55 | snd_gus_card_t *gus; | ||
56 | |||
57 | gus = snd_timer_chip(timer); | ||
58 | spin_lock_irqsave(&gus->reg_lock, flags); | ||
59 | tmp = (gus->gf1.timer_enabled &= ~4); | ||
60 | snd_gf1_write8(gus, SNDRV_GF1_GB_SOUND_BLASTER_CONTROL, tmp); /* disable timer #1 */ | ||
61 | spin_unlock_irqrestore(&gus->reg_lock, flags); | ||
62 | return 0; | ||
63 | } | ||
64 | |||
65 | /* | ||
66 | * Timer 2 - 320us | ||
67 | */ | ||
68 | |||
69 | static int snd_gf1_timer2_start(snd_timer_t * timer) | ||
70 | { | ||
71 | unsigned long flags; | ||
72 | unsigned char tmp; | ||
73 | unsigned int ticks; | ||
74 | snd_gus_card_t *gus; | ||
75 | |||
76 | gus = snd_timer_chip(timer); | ||
77 | spin_lock_irqsave(&gus->reg_lock, flags); | ||
78 | ticks = timer->sticks; | ||
79 | tmp = (gus->gf1.timer_enabled |= 8); | ||
80 | snd_gf1_write8(gus, SNDRV_GF1_GB_ADLIB_TIMER_2, 256 - ticks); /* timer 2 count */ | ||
81 | snd_gf1_write8(gus, SNDRV_GF1_GB_SOUND_BLASTER_CONTROL, tmp); /* enable timer 2 IRQ */ | ||
82 | snd_gf1_adlib_write(gus, 0x04, tmp >> 2); /* timer 2 start */ | ||
83 | spin_unlock_irqrestore(&gus->reg_lock, flags); | ||
84 | return 0; | ||
85 | } | ||
86 | |||
87 | static int snd_gf1_timer2_stop(snd_timer_t * timer) | ||
88 | { | ||
89 | unsigned long flags; | ||
90 | unsigned char tmp; | ||
91 | snd_gus_card_t *gus; | ||
92 | |||
93 | gus = snd_timer_chip(timer); | ||
94 | spin_lock_irqsave(&gus->reg_lock, flags); | ||
95 | tmp = (gus->gf1.timer_enabled &= ~8); | ||
96 | snd_gf1_write8(gus, SNDRV_GF1_GB_SOUND_BLASTER_CONTROL, tmp); /* disable timer #1 */ | ||
97 | spin_unlock_irqrestore(&gus->reg_lock, flags); | ||
98 | return 0; | ||
99 | } | ||
100 | |||
101 | /* | ||
102 | |||
103 | */ | ||
104 | |||
105 | static void snd_gf1_interrupt_timer1(snd_gus_card_t * gus) | ||
106 | { | ||
107 | snd_timer_t *timer = gus->gf1.timer1; | ||
108 | |||
109 | if (timer == NULL) | ||
110 | return; | ||
111 | snd_timer_interrupt(timer, timer->sticks); | ||
112 | } | ||
113 | |||
114 | static void snd_gf1_interrupt_timer2(snd_gus_card_t * gus) | ||
115 | { | ||
116 | snd_timer_t *timer = gus->gf1.timer2; | ||
117 | |||
118 | if (timer == NULL) | ||
119 | return; | ||
120 | snd_timer_interrupt(timer, timer->sticks); | ||
121 | } | ||
122 | |||
123 | /* | ||
124 | |||
125 | */ | ||
126 | |||
127 | static struct _snd_timer_hardware snd_gf1_timer1 = | ||
128 | { | ||
129 | .flags = SNDRV_TIMER_HW_STOP, | ||
130 | .resolution = 80000, | ||
131 | .ticks = 256, | ||
132 | .start = snd_gf1_timer1_start, | ||
133 | .stop = snd_gf1_timer1_stop, | ||
134 | }; | ||
135 | |||
136 | static struct _snd_timer_hardware snd_gf1_timer2 = | ||
137 | { | ||
138 | .flags = SNDRV_TIMER_HW_STOP, | ||
139 | .resolution = 320000, | ||
140 | .ticks = 256, | ||
141 | .start = snd_gf1_timer2_start, | ||
142 | .stop = snd_gf1_timer2_stop, | ||
143 | }; | ||
144 | |||
145 | static void snd_gf1_timer1_free(snd_timer_t *timer) | ||
146 | { | ||
147 | snd_gus_card_t *gus = timer->private_data; | ||
148 | gus->gf1.timer1 = NULL; | ||
149 | } | ||
150 | |||
151 | static void snd_gf1_timer2_free(snd_timer_t *timer) | ||
152 | { | ||
153 | snd_gus_card_t *gus = timer->private_data; | ||
154 | gus->gf1.timer2 = NULL; | ||
155 | } | ||
156 | |||
157 | void snd_gf1_timers_init(snd_gus_card_t * gus) | ||
158 | { | ||
159 | snd_timer_t *timer; | ||
160 | snd_timer_id_t tid; | ||
161 | |||
162 | if (gus->gf1.timer1 != NULL || gus->gf1.timer2 != NULL) | ||
163 | return; | ||
164 | |||
165 | gus->gf1.interrupt_handler_timer1 = snd_gf1_interrupt_timer1; | ||
166 | gus->gf1.interrupt_handler_timer2 = snd_gf1_interrupt_timer2; | ||
167 | |||
168 | tid.dev_class = SNDRV_TIMER_CLASS_CARD; | ||
169 | tid.dev_sclass = SNDRV_TIMER_SCLASS_NONE; | ||
170 | tid.card = gus->card->number; | ||
171 | tid.device = gus->timer_dev; | ||
172 | tid.subdevice = 0; | ||
173 | |||
174 | if (snd_timer_new(gus->card, "GF1 timer", &tid, &timer) >= 0) { | ||
175 | strcpy(timer->name, "GF1 timer #1"); | ||
176 | timer->private_data = gus; | ||
177 | timer->private_free = snd_gf1_timer1_free; | ||
178 | timer->hw = snd_gf1_timer1; | ||
179 | } | ||
180 | gus->gf1.timer1 = timer; | ||
181 | |||
182 | tid.device++; | ||
183 | |||
184 | if (snd_timer_new(gus->card, "GF1 timer", &tid, &timer) >= 0) { | ||
185 | strcpy(timer->name, "GF1 timer #2"); | ||
186 | timer->private_data = gus; | ||
187 | timer->private_free = snd_gf1_timer2_free; | ||
188 | timer->hw = snd_gf1_timer2; | ||
189 | } | ||
190 | gus->gf1.timer2 = timer; | ||
191 | } | ||
192 | |||
193 | void snd_gf1_timers_done(snd_gus_card_t * gus) | ||
194 | { | ||
195 | snd_gf1_set_default_handlers(gus, SNDRV_GF1_HANDLER_TIMER1 | SNDRV_GF1_HANDLER_TIMER2); | ||
196 | if (gus->gf1.timer1) { | ||
197 | snd_device_free(gus->card, gus->gf1.timer1); | ||
198 | gus->gf1.timer1 = NULL; | ||
199 | } | ||
200 | if (gus->gf1.timer2) { | ||
201 | snd_device_free(gus->card, gus->gf1.timer2); | ||
202 | gus->gf1.timer2 = NULL; | ||
203 | } | ||
204 | } | ||
diff --git a/sound/isa/gus/gus_uart.c b/sound/isa/gus/gus_uart.c new file mode 100644 index 000000000000..1bc2da8784e0 --- /dev/null +++ b/sound/isa/gus/gus_uart.c | |||
@@ -0,0 +1,257 @@ | |||
1 | /* | ||
2 | * Copyright (c) by Jaroslav Kysela <perex@suse.cz> | ||
3 | * Routines for the GF1 MIDI interface - like UART 6850 | ||
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 <linux/delay.h> | ||
24 | #include <linux/interrupt.h> | ||
25 | #include <linux/time.h> | ||
26 | #include <sound/core.h> | ||
27 | #include <sound/gus.h> | ||
28 | |||
29 | static void snd_gf1_interrupt_midi_in(snd_gus_card_t * gus) | ||
30 | { | ||
31 | int count; | ||
32 | unsigned char stat, data, byte; | ||
33 | unsigned long flags; | ||
34 | |||
35 | count = 10; | ||
36 | while (count) { | ||
37 | spin_lock_irqsave(&gus->uart_cmd_lock, flags); | ||
38 | stat = snd_gf1_uart_stat(gus); | ||
39 | if (!(stat & 0x01)) { /* data in Rx FIFO? */ | ||
40 | spin_unlock_irqrestore(&gus->uart_cmd_lock, flags); | ||
41 | count--; | ||
42 | continue; | ||
43 | } | ||
44 | count = 100; /* arm counter to new value */ | ||
45 | data = snd_gf1_uart_get(gus); | ||
46 | if (!(gus->gf1.uart_cmd & 0x80)) { | ||
47 | spin_unlock_irqrestore(&gus->uart_cmd_lock, flags); | ||
48 | continue; | ||
49 | } | ||
50 | if (stat & 0x10) { /* framing error */ | ||
51 | gus->gf1.uart_framing++; | ||
52 | spin_unlock_irqrestore(&gus->uart_cmd_lock, flags); | ||
53 | continue; | ||
54 | } | ||
55 | byte = snd_gf1_uart_get(gus); | ||
56 | spin_unlock_irqrestore(&gus->uart_cmd_lock, flags); | ||
57 | snd_rawmidi_receive(gus->midi_substream_input, &byte, 1); | ||
58 | if (stat & 0x20) { | ||
59 | gus->gf1.uart_overrun++; | ||
60 | } | ||
61 | } | ||
62 | } | ||
63 | |||
64 | static void snd_gf1_interrupt_midi_out(snd_gus_card_t * gus) | ||
65 | { | ||
66 | char byte; | ||
67 | unsigned long flags; | ||
68 | |||
69 | /* try unlock output */ | ||
70 | if (snd_gf1_uart_stat(gus) & 0x01) | ||
71 | snd_gf1_interrupt_midi_in(gus); | ||
72 | |||
73 | spin_lock_irqsave(&gus->uart_cmd_lock, flags); | ||
74 | if (snd_gf1_uart_stat(gus) & 0x02) { /* Tx FIFO free? */ | ||
75 | if (snd_rawmidi_transmit(gus->midi_substream_output, &byte, 1) != 1) { /* no other bytes or error */ | ||
76 | snd_gf1_uart_cmd(gus, gus->gf1.uart_cmd & ~0x20); /* disable Tx interrupt */ | ||
77 | } else { | ||
78 | snd_gf1_uart_put(gus, byte); | ||
79 | } | ||
80 | } | ||
81 | spin_unlock_irqrestore(&gus->uart_cmd_lock, flags); | ||
82 | } | ||
83 | |||
84 | static void snd_gf1_uart_reset(snd_gus_card_t * gus, int close) | ||
85 | { | ||
86 | snd_gf1_uart_cmd(gus, 0x03); /* reset */ | ||
87 | if (!close && gus->uart_enable) { | ||
88 | udelay(160); | ||
89 | snd_gf1_uart_cmd(gus, 0x00); /* normal operations */ | ||
90 | } | ||
91 | } | ||
92 | |||
93 | static int snd_gf1_uart_output_open(snd_rawmidi_substream_t * substream) | ||
94 | { | ||
95 | unsigned long flags; | ||
96 | snd_gus_card_t *gus; | ||
97 | |||
98 | gus = substream->rmidi->private_data; | ||
99 | spin_lock_irqsave(&gus->uart_cmd_lock, flags); | ||
100 | if (!(gus->gf1.uart_cmd & 0x80)) { /* input active? */ | ||
101 | snd_gf1_uart_reset(gus, 0); | ||
102 | } | ||
103 | gus->gf1.interrupt_handler_midi_out = snd_gf1_interrupt_midi_out; | ||
104 | gus->midi_substream_output = substream; | ||
105 | spin_unlock_irqrestore(&gus->uart_cmd_lock, flags); | ||
106 | #if 0 | ||
107 | snd_printk("write init - cmd = 0x%x, stat = 0x%x\n", gus->gf1.uart_cmd, snd_gf1_uart_stat(gus)); | ||
108 | #endif | ||
109 | return 0; | ||
110 | } | ||
111 | |||
112 | static int snd_gf1_uart_input_open(snd_rawmidi_substream_t * substream) | ||
113 | { | ||
114 | unsigned long flags; | ||
115 | snd_gus_card_t *gus; | ||
116 | int i; | ||
117 | |||
118 | gus = substream->rmidi->private_data; | ||
119 | spin_lock_irqsave(&gus->uart_cmd_lock, flags); | ||
120 | if (gus->gf1.interrupt_handler_midi_out != snd_gf1_interrupt_midi_out) { | ||
121 | snd_gf1_uart_reset(gus, 0); | ||
122 | } | ||
123 | gus->gf1.interrupt_handler_midi_in = snd_gf1_interrupt_midi_in; | ||
124 | gus->midi_substream_input = substream; | ||
125 | if (gus->uart_enable) { | ||
126 | for (i = 0; i < 1000 && (snd_gf1_uart_stat(gus) & 0x01); i++) | ||
127 | snd_gf1_uart_get(gus); /* clean Rx */ | ||
128 | if (i >= 1000) | ||
129 | snd_printk("gus midi uart init read - cleanup error\n"); | ||
130 | } | ||
131 | spin_unlock_irqrestore(&gus->uart_cmd_lock, flags); | ||
132 | #if 0 | ||
133 | snd_printk("read init - enable = %i, cmd = 0x%x, stat = 0x%x\n", gus->uart_enable, gus->gf1.uart_cmd, snd_gf1_uart_stat(gus)); | ||
134 | snd_printk("[0x%x] reg (ctrl/status) = 0x%x, reg (data) = 0x%x (page = 0x%x)\n", gus->gf1.port + 0x100, inb(gus->gf1.port + 0x100), inb(gus->gf1.port + 0x101), inb(gus->gf1.port + 0x102)); | ||
135 | #endif | ||
136 | return 0; | ||
137 | } | ||
138 | |||
139 | static int snd_gf1_uart_output_close(snd_rawmidi_substream_t * substream) | ||
140 | { | ||
141 | unsigned long flags; | ||
142 | snd_gus_card_t *gus; | ||
143 | |||
144 | gus = substream->rmidi->private_data; | ||
145 | spin_lock_irqsave(&gus->uart_cmd_lock, flags); | ||
146 | if (gus->gf1.interrupt_handler_midi_in != snd_gf1_interrupt_midi_in) | ||
147 | snd_gf1_uart_reset(gus, 1); | ||
148 | snd_gf1_set_default_handlers(gus, SNDRV_GF1_HANDLER_MIDI_OUT); | ||
149 | gus->midi_substream_output = NULL; | ||
150 | spin_unlock_irqrestore(&gus->uart_cmd_lock, flags); | ||
151 | return 0; | ||
152 | } | ||
153 | |||
154 | static int snd_gf1_uart_input_close(snd_rawmidi_substream_t * substream) | ||
155 | { | ||
156 | unsigned long flags; | ||
157 | snd_gus_card_t *gus; | ||
158 | |||
159 | gus = substream->rmidi->private_data; | ||
160 | spin_lock_irqsave(&gus->uart_cmd_lock, flags); | ||
161 | if (gus->gf1.interrupt_handler_midi_out != snd_gf1_interrupt_midi_out) | ||
162 | snd_gf1_uart_reset(gus, 1); | ||
163 | snd_gf1_set_default_handlers(gus, SNDRV_GF1_HANDLER_MIDI_IN); | ||
164 | gus->midi_substream_input = NULL; | ||
165 | spin_unlock_irqrestore(&gus->uart_cmd_lock, flags); | ||
166 | return 0; | ||
167 | } | ||
168 | |||
169 | static void snd_gf1_uart_input_trigger(snd_rawmidi_substream_t * substream, int up) | ||
170 | { | ||
171 | snd_gus_card_t *gus; | ||
172 | unsigned long flags; | ||
173 | |||
174 | gus = substream->rmidi->private_data; | ||
175 | |||
176 | spin_lock_irqsave(&gus->uart_cmd_lock, flags); | ||
177 | if (up) { | ||
178 | if ((gus->gf1.uart_cmd & 0x80) == 0) | ||
179 | snd_gf1_uart_cmd(gus, gus->gf1.uart_cmd | 0x80); /* enable Rx interrupts */ | ||
180 | } else { | ||
181 | if (gus->gf1.uart_cmd & 0x80) | ||
182 | snd_gf1_uart_cmd(gus, gus->gf1.uart_cmd & ~0x80); /* disable Rx interrupts */ | ||
183 | } | ||
184 | spin_unlock_irqrestore(&gus->uart_cmd_lock, flags); | ||
185 | } | ||
186 | |||
187 | static void snd_gf1_uart_output_trigger(snd_rawmidi_substream_t * substream, int up) | ||
188 | { | ||
189 | unsigned long flags; | ||
190 | snd_gus_card_t *gus; | ||
191 | char byte; | ||
192 | int timeout; | ||
193 | |||
194 | gus = substream->rmidi->private_data; | ||
195 | |||
196 | spin_lock_irqsave(&gus->uart_cmd_lock, flags); | ||
197 | if (up) { | ||
198 | if ((gus->gf1.uart_cmd & 0x20) == 0) { | ||
199 | spin_unlock_irqrestore(&gus->uart_cmd_lock, flags); | ||
200 | /* wait for empty Rx - Tx is probably unlocked */ | ||
201 | timeout = 10000; | ||
202 | while (timeout-- > 0 && snd_gf1_uart_stat(gus) & 0x01); | ||
203 | /* Tx FIFO free? */ | ||
204 | spin_lock_irqsave(&gus->uart_cmd_lock, flags); | ||
205 | if (gus->gf1.uart_cmd & 0x20) { | ||
206 | spin_unlock_irqrestore(&gus->uart_cmd_lock, flags); | ||
207 | return; | ||
208 | } | ||
209 | if (snd_gf1_uart_stat(gus) & 0x02) { | ||
210 | if (snd_rawmidi_transmit(substream, &byte, 1) != 1) { | ||
211 | spin_unlock_irqrestore(&gus->uart_cmd_lock, flags); | ||
212 | return; | ||
213 | } | ||
214 | snd_gf1_uart_put(gus, byte); | ||
215 | } | ||
216 | snd_gf1_uart_cmd(gus, gus->gf1.uart_cmd | 0x20); /* enable Tx interrupt */ | ||
217 | } | ||
218 | } else { | ||
219 | if (gus->gf1.uart_cmd & 0x20) | ||
220 | snd_gf1_uart_cmd(gus, gus->gf1.uart_cmd & ~0x20); | ||
221 | } | ||
222 | spin_unlock_irqrestore(&gus->uart_cmd_lock, flags); | ||
223 | } | ||
224 | |||
225 | static snd_rawmidi_ops_t snd_gf1_uart_output = | ||
226 | { | ||
227 | .open = snd_gf1_uart_output_open, | ||
228 | .close = snd_gf1_uart_output_close, | ||
229 | .trigger = snd_gf1_uart_output_trigger, | ||
230 | }; | ||
231 | |||
232 | static snd_rawmidi_ops_t snd_gf1_uart_input = | ||
233 | { | ||
234 | .open = snd_gf1_uart_input_open, | ||
235 | .close = snd_gf1_uart_input_close, | ||
236 | .trigger = snd_gf1_uart_input_trigger, | ||
237 | }; | ||
238 | |||
239 | int snd_gf1_rawmidi_new(snd_gus_card_t * gus, int device, snd_rawmidi_t ** rrawmidi) | ||
240 | { | ||
241 | snd_rawmidi_t *rmidi; | ||
242 | int err; | ||
243 | |||
244 | if (rrawmidi) | ||
245 | *rrawmidi = NULL; | ||
246 | if ((err = snd_rawmidi_new(gus->card, "GF1", device, 1, 1, &rmidi)) < 0) | ||
247 | return err; | ||
248 | strcpy(rmidi->name, gus->interwave ? "AMD InterWave" : "GF1"); | ||
249 | snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_gf1_uart_output); | ||
250 | snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_gf1_uart_input); | ||
251 | rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT | SNDRV_RAWMIDI_INFO_INPUT | SNDRV_RAWMIDI_INFO_DUPLEX; | ||
252 | rmidi->private_data = gus; | ||
253 | gus->midi_uart = rmidi; | ||
254 | if (rrawmidi) | ||
255 | *rrawmidi = rmidi; | ||
256 | return err; | ||
257 | } | ||
diff --git a/sound/isa/gus/gus_volume.c b/sound/isa/gus/gus_volume.c new file mode 100644 index 000000000000..b72bcfb28617 --- /dev/null +++ b/sound/isa/gus/gus_volume.c | |||
@@ -0,0 +1,210 @@ | |||
1 | /* | ||
2 | * Copyright (c) by Jaroslav Kysela <perex@suse.cz> | ||
3 | * | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or modify | ||
6 | * it under the terms of the GNU General Public License as published by | ||
7 | * the Free Software Foundation; either version 2 of the License, or | ||
8 | * (at your option) any later version. | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, | ||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | * GNU General Public License for more details. | ||
14 | * | ||
15 | * You should have received a copy of the GNU General Public License | ||
16 | * along with this program; if not, write to the Free Software | ||
17 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
18 | * | ||
19 | */ | ||
20 | |||
21 | #include <sound/driver.h> | ||
22 | #include <linux/time.h> | ||
23 | #include <sound/core.h> | ||
24 | #include <sound/gus.h> | ||
25 | #define __GUS_TABLES_ALLOC__ | ||
26 | #include "gus_tables.h" | ||
27 | |||
28 | EXPORT_SYMBOL(snd_gf1_atten_table); /* for snd-gus-synth module */ | ||
29 | |||
30 | unsigned short snd_gf1_lvol_to_gvol_raw(unsigned int vol) | ||
31 | { | ||
32 | unsigned short e, m, tmp; | ||
33 | |||
34 | if (vol > 65535) | ||
35 | vol = 65535; | ||
36 | tmp = vol; | ||
37 | e = 7; | ||
38 | if (tmp < 128) { | ||
39 | while (e > 0 && tmp < (1 << e)) | ||
40 | e--; | ||
41 | } else { | ||
42 | while (tmp > 255) { | ||
43 | tmp >>= 1; | ||
44 | e++; | ||
45 | } | ||
46 | } | ||
47 | m = vol - (1 << e); | ||
48 | if (m > 0) { | ||
49 | if (e > 8) | ||
50 | m >>= e - 8; | ||
51 | else if (e < 8) | ||
52 | m <<= 8 - e; | ||
53 | m &= 255; | ||
54 | } | ||
55 | return (e << 8) | m; | ||
56 | } | ||
57 | |||
58 | unsigned int snd_gf1_gvol_to_lvol_raw(unsigned short gf1_vol) | ||
59 | { | ||
60 | unsigned int rvol; | ||
61 | unsigned short e, m; | ||
62 | |||
63 | if (!gf1_vol) | ||
64 | return 0; | ||
65 | e = gf1_vol >> 8; | ||
66 | m = (unsigned char) gf1_vol; | ||
67 | rvol = 1 << e; | ||
68 | if (e > 8) | ||
69 | return rvol | (m << (e - 8)); | ||
70 | return rvol | (m >> (8 - e)); | ||
71 | } | ||
72 | |||
73 | unsigned int snd_gf1_calc_ramp_rate(snd_gus_card_t * gus, | ||
74 | unsigned short start, | ||
75 | unsigned short end, | ||
76 | unsigned int us) | ||
77 | { | ||
78 | static unsigned char vol_rates[19] = | ||
79 | { | ||
80 | 23, 24, 26, 28, 29, 31, 32, 34, | ||
81 | 36, 37, 39, 40, 42, 44, 45, 47, | ||
82 | 49, 50, 52 | ||
83 | }; | ||
84 | unsigned short range, increment, value, i; | ||
85 | |||
86 | start >>= 4; | ||
87 | end >>= 4; | ||
88 | if (start < end) | ||
89 | us /= end - start; | ||
90 | else | ||
91 | us /= start - end; | ||
92 | range = 4; | ||
93 | value = gus->gf1.enh_mode ? | ||
94 | vol_rates[0] : | ||
95 | vol_rates[gus->gf1.active_voices - 14]; | ||
96 | for (i = 0; i < 3; i++) { | ||
97 | if (us < value) { | ||
98 | range = i; | ||
99 | break; | ||
100 | } else | ||
101 | value <<= 3; | ||
102 | } | ||
103 | if (range == 4) { | ||
104 | range = 3; | ||
105 | increment = 1; | ||
106 | } else | ||
107 | increment = (value + (value >> 1)) / us; | ||
108 | return (range << 6) | (increment & 0x3f); | ||
109 | } | ||
110 | |||
111 | unsigned short snd_gf1_translate_freq(snd_gus_card_t * gus, unsigned int freq16) | ||
112 | { | ||
113 | freq16 >>= 3; | ||
114 | if (freq16 < 50) | ||
115 | freq16 = 50; | ||
116 | if (freq16 & 0xf8000000) { | ||
117 | freq16 = ~0xf8000000; | ||
118 | snd_printk("snd_gf1_translate_freq: overflow - freq = 0x%x\n", freq16); | ||
119 | } | ||
120 | return ((freq16 << 9) + (gus->gf1.playback_freq >> 1)) / gus->gf1.playback_freq; | ||
121 | } | ||
122 | |||
123 | short snd_gf1_compute_vibrato(short cents, unsigned short fc_register) | ||
124 | { | ||
125 | static short vibrato_table[] = | ||
126 | { | ||
127 | 0, 0, 32, 592, 61, 1175, 93, 1808, | ||
128 | 124, 2433, 152, 3007, 182, 3632, 213, 4290, | ||
129 | 241, 4834, 255, 5200 | ||
130 | }; | ||
131 | |||
132 | long depth; | ||
133 | short *vi1, *vi2, pcents, v1; | ||
134 | |||
135 | pcents = cents < 0 ? -cents : cents; | ||
136 | for (vi1 = vibrato_table, vi2 = vi1 + 2; pcents > *vi2; vi1 = vi2, vi2 += 2); | ||
137 | v1 = *(vi1 + 1); | ||
138 | /* The FC table above is a list of pairs. The first number in the pair */ | ||
139 | /* is the cents index from 0-255 cents, and the second number in the */ | ||
140 | /* pair is the FC adjustment needed to change the pitch by the indexed */ | ||
141 | /* number of cents. The table was created for an FC of 32768. */ | ||
142 | /* The following expression does a linear interpolation against the */ | ||
143 | /* approximated log curve in the table above, and then scales the number */ | ||
144 | /* by the FC before the LFO. This calculation also adjusts the output */ | ||
145 | /* value to produce the appropriate depth for the hardware. The depth */ | ||
146 | /* is 2 * desired FC + 1. */ | ||
147 | depth = (((int) (*(vi2 + 1) - *vi1) * (pcents - *vi1) / (*vi2 - *vi1)) + v1) * fc_register >> 14; | ||
148 | if (depth) | ||
149 | depth++; | ||
150 | if (depth > 255) | ||
151 | depth = 255; | ||
152 | return cents < 0 ? -(short) depth : (short) depth; | ||
153 | } | ||
154 | |||
155 | unsigned short snd_gf1_compute_pitchbend(unsigned short pitchbend, unsigned short sens) | ||
156 | { | ||
157 | static long log_table[] = {1024, 1085, 1149, 1218, 1290, 1367, 1448, 1534, 1625, 1722, 1825, 1933}; | ||
158 | int wheel, sensitivity; | ||
159 | unsigned int mantissa, f1, f2; | ||
160 | unsigned short semitones, f1_index, f2_index, f1_power, f2_power; | ||
161 | char bend_down = 0; | ||
162 | int bend; | ||
163 | |||
164 | if (!sens) | ||
165 | return 1024; | ||
166 | wheel = (int) pitchbend - 8192; | ||
167 | sensitivity = ((int) sens * wheel) / 128; | ||
168 | if (sensitivity < 0) { | ||
169 | bend_down = 1; | ||
170 | sensitivity = -sensitivity; | ||
171 | } | ||
172 | semitones = (unsigned int) (sensitivity >> 13); | ||
173 | mantissa = sensitivity % 8192; | ||
174 | f1_index = semitones % 12; | ||
175 | f2_index = (semitones + 1) % 12; | ||
176 | f1_power = semitones / 12; | ||
177 | f2_power = (semitones + 1) / 12; | ||
178 | f1 = log_table[f1_index] << f1_power; | ||
179 | f2 = log_table[f2_index] << f2_power; | ||
180 | bend = (int) ((((f2 - f1) * mantissa) >> 13) + f1); | ||
181 | if (bend_down) | ||
182 | bend = 1048576L / bend; | ||
183 | return bend; | ||
184 | } | ||
185 | |||
186 | unsigned short snd_gf1_compute_freq(unsigned int freq, | ||
187 | unsigned int rate, | ||
188 | unsigned short mix_rate) | ||
189 | { | ||
190 | unsigned int fc; | ||
191 | int scale = 0; | ||
192 | |||
193 | while (freq >= 4194304L) { | ||
194 | scale++; | ||
195 | freq >>= 1; | ||
196 | } | ||
197 | fc = (freq << 10) / rate; | ||
198 | if (fc > 97391L) { | ||
199 | fc = 97391; | ||
200 | snd_printk("patch: (1) fc frequency overflow - %u\n", fc); | ||
201 | } | ||
202 | fc = (fc * 44100UL) / mix_rate; | ||
203 | while (scale--) | ||
204 | fc <<= 1; | ||
205 | if (fc > 65535L) { | ||
206 | fc = 65535; | ||
207 | snd_printk("patch: (2) fc frequency overflow - %u\n", fc); | ||
208 | } | ||
209 | return (unsigned short) fc; | ||
210 | } | ||
diff --git a/sound/isa/gus/gusclassic.c b/sound/isa/gus/gusclassic.c new file mode 100644 index 000000000000..a99fa5040b46 --- /dev/null +++ b/sound/isa/gus/gusclassic.c | |||
@@ -0,0 +1,260 @@ | |||
1 | /* | ||
2 | * Driver for Gravis UltraSound Classic soundcard | ||
3 | * Copyright (c) by Jaroslav Kysela <perex@suse.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 <asm/dma.h> | ||
24 | #include <linux/init.h> | ||
25 | #include <linux/delay.h> | ||
26 | #include <linux/time.h> | ||
27 | #include <linux/moduleparam.h> | ||
28 | #include <sound/core.h> | ||
29 | #include <sound/gus.h> | ||
30 | #define SNDRV_LEGACY_AUTO_PROBE | ||
31 | #define SNDRV_LEGACY_FIND_FREE_IRQ | ||
32 | #define SNDRV_LEGACY_FIND_FREE_DMA | ||
33 | #include <sound/initval.h> | ||
34 | |||
35 | MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>"); | ||
36 | MODULE_DESCRIPTION("Gravis UltraSound Classic"); | ||
37 | MODULE_LICENSE("GPL"); | ||
38 | MODULE_SUPPORTED_DEVICE("{{Gravis,UltraSound Classic}}"); | ||
39 | |||
40 | static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ | ||
41 | static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ | ||
42 | static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE; /* Enable this card */ | ||
43 | static long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* 0x220,0x230,0x240,0x250,0x260 */ | ||
44 | static int irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* 3,5,9,11,12,15 */ | ||
45 | static int dma1[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* 1,3,5,6,7 */ | ||
46 | static int dma2[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* 1,3,5,6,7 */ | ||
47 | static int joystick_dac[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 29}; | ||
48 | /* 0 to 31, (0.59V-4.52V or 0.389V-2.98V) */ | ||
49 | static int channels[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 24}; | ||
50 | static int pcm_channels[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 2}; | ||
51 | |||
52 | module_param_array(index, int, NULL, 0444); | ||
53 | MODULE_PARM_DESC(index, "Index value for GUS Classic soundcard."); | ||
54 | module_param_array(id, charp, NULL, 0444); | ||
55 | MODULE_PARM_DESC(id, "ID string for GUS Classic soundcard."); | ||
56 | module_param_array(enable, bool, NULL, 0444); | ||
57 | MODULE_PARM_DESC(enable, "Enable GUS Classic soundcard."); | ||
58 | module_param_array(port, long, NULL, 0444); | ||
59 | MODULE_PARM_DESC(port, "Port # for GUS Classic driver."); | ||
60 | module_param_array(irq, int, NULL, 0444); | ||
61 | MODULE_PARM_DESC(irq, "IRQ # for GUS Classic driver."); | ||
62 | module_param_array(dma1, int, NULL, 0444); | ||
63 | MODULE_PARM_DESC(dma1, "DMA1 # for GUS Classic driver."); | ||
64 | module_param_array(dma2, int, NULL, 0444); | ||
65 | MODULE_PARM_DESC(dma2, "DMA2 # for GUS Classic driver."); | ||
66 | module_param_array(joystick_dac, int, NULL, 0444); | ||
67 | MODULE_PARM_DESC(joystick_dac, "Joystick DAC level 0.59V-4.52V or 0.389V-2.98V for GUS Classic driver."); | ||
68 | module_param_array(channels, int, NULL, 0444); | ||
69 | MODULE_PARM_DESC(channels, "GF1 channels for GUS Classic driver."); | ||
70 | module_param_array(pcm_channels, int, NULL, 0444); | ||
71 | MODULE_PARM_DESC(pcm_channels, "Reserved PCM channels for GUS Classic driver."); | ||
72 | |||
73 | static snd_card_t *snd_gusclassic_cards[SNDRV_CARDS] = SNDRV_DEFAULT_PTR; | ||
74 | |||
75 | |||
76 | static int __init snd_gusclassic_detect(snd_gus_card_t * gus) | ||
77 | { | ||
78 | snd_gf1_i_write8(gus, SNDRV_GF1_GB_RESET, 0); /* reset GF1 */ | ||
79 | #ifdef CONFIG_SND_DEBUG_DETECT | ||
80 | { | ||
81 | unsigned char d; | ||
82 | |||
83 | if (((d = snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET)) & 0x07) != 0) { | ||
84 | snd_printk("[0x%lx] check 1 failed - 0x%x\n", gus->gf1.port, d); | ||
85 | return -ENODEV; | ||
86 | } | ||
87 | } | ||
88 | #else | ||
89 | if ((snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET) & 0x07) != 0) | ||
90 | return -ENODEV; | ||
91 | #endif | ||
92 | udelay(160); | ||
93 | snd_gf1_i_write8(gus, SNDRV_GF1_GB_RESET, 1); /* release reset */ | ||
94 | udelay(160); | ||
95 | #ifdef CONFIG_SND_DEBUG_DETECT | ||
96 | { | ||
97 | unsigned char d; | ||
98 | |||
99 | if (((d = snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET)) & 0x07) != 1) { | ||
100 | snd_printk("[0x%lx] check 2 failed - 0x%x\n", gus->gf1.port, d); | ||
101 | return -ENODEV; | ||
102 | } | ||
103 | } | ||
104 | #else | ||
105 | if ((snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET) & 0x07) != 1) | ||
106 | return -ENODEV; | ||
107 | #endif | ||
108 | |||
109 | return 0; | ||
110 | } | ||
111 | |||
112 | static void __init snd_gusclassic_init(int dev, snd_gus_card_t * gus) | ||
113 | { | ||
114 | gus->equal_irq = 0; | ||
115 | gus->codec_flag = 0; | ||
116 | gus->max_flag = 0; | ||
117 | gus->joystick_dac = joystick_dac[dev]; | ||
118 | } | ||
119 | |||
120 | static int __init snd_gusclassic_probe(int dev) | ||
121 | { | ||
122 | static int possible_irqs[] = {5, 11, 12, 9, 7, 15, 3, 4, -1}; | ||
123 | static int possible_dmas[] = {5, 6, 7, 1, 3, -1}; | ||
124 | int xirq, xdma1, xdma2; | ||
125 | snd_card_t *card; | ||
126 | struct snd_gusclassic *guscard; | ||
127 | snd_gus_card_t *gus = NULL; | ||
128 | int err; | ||
129 | |||
130 | card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0); | ||
131 | if (card == NULL) | ||
132 | return -ENOMEM; | ||
133 | guscard = (struct snd_gusclassic *)card->private_data; | ||
134 | if (pcm_channels[dev] < 2) | ||
135 | pcm_channels[dev] = 2; | ||
136 | |||
137 | xirq = irq[dev]; | ||
138 | if (xirq == SNDRV_AUTO_IRQ) { | ||
139 | if ((xirq = snd_legacy_find_free_irq(possible_irqs)) < 0) { | ||
140 | snd_card_free(card); | ||
141 | snd_printk("unable to find a free IRQ\n"); | ||
142 | return -EBUSY; | ||
143 | } | ||
144 | } | ||
145 | xdma1 = dma1[dev]; | ||
146 | if (xdma1 == SNDRV_AUTO_DMA) { | ||
147 | if ((xdma1 = snd_legacy_find_free_dma(possible_dmas)) < 0) { | ||
148 | snd_card_free(card); | ||
149 | snd_printk("unable to find a free DMA1\n"); | ||
150 | return -EBUSY; | ||
151 | } | ||
152 | } | ||
153 | xdma2 = dma2[dev]; | ||
154 | if (xdma2 == SNDRV_AUTO_DMA) { | ||
155 | if ((xdma2 = snd_legacy_find_free_dma(possible_dmas)) < 0) { | ||
156 | snd_card_free(card); | ||
157 | snd_printk("unable to find a free DMA2\n"); | ||
158 | return -EBUSY; | ||
159 | } | ||
160 | } | ||
161 | |||
162 | |||
163 | if ((err = snd_gus_create(card, | ||
164 | port[dev], | ||
165 | xirq, xdma1, xdma2, | ||
166 | 0, channels[dev], pcm_channels[dev], | ||
167 | 0, &gus)) < 0) { | ||
168 | snd_card_free(card); | ||
169 | return err; | ||
170 | } | ||
171 | if ((err = snd_gusclassic_detect(gus)) < 0) { | ||
172 | snd_card_free(card); | ||
173 | return err; | ||
174 | } | ||
175 | snd_gusclassic_init(dev, gus); | ||
176 | if ((err = snd_gus_initialize(gus)) < 0) { | ||
177 | snd_card_free(card); | ||
178 | return err; | ||
179 | } | ||
180 | if (gus->max_flag || gus->ess_flag) { | ||
181 | snd_printdd("GUS Classic or ACE soundcard was not detected at 0x%lx\n", gus->gf1.port); | ||
182 | snd_card_free(card); | ||
183 | return -ENODEV; | ||
184 | } | ||
185 | if ((err = snd_gf1_new_mixer(gus)) < 0) { | ||
186 | snd_card_free(card); | ||
187 | return err; | ||
188 | } | ||
189 | if ((err = snd_gf1_pcm_new(gus, 0, 0, NULL)) < 0) { | ||
190 | snd_card_free(card); | ||
191 | return err; | ||
192 | } | ||
193 | if (!gus->ace_flag) { | ||
194 | if ((err = snd_gf1_rawmidi_new(gus, 0, NULL)) < 0) { | ||
195 | snd_card_free(card); | ||
196 | return err; | ||
197 | } | ||
198 | } | ||
199 | sprintf(card->longname + strlen(card->longname), " at 0x%lx, irq %d, dma %d", gus->gf1.port, xirq, xdma1); | ||
200 | if (dma2 >= 0) | ||
201 | sprintf(card->longname + strlen(card->longname), "&%d", xdma2); | ||
202 | if ((err = snd_card_register(card)) < 0) { | ||
203 | snd_card_free(card); | ||
204 | return err; | ||
205 | } | ||
206 | snd_gusclassic_cards[dev] = card; | ||
207 | return 0; | ||
208 | } | ||
209 | |||
210 | static int __init snd_gusclassic_legacy_auto_probe(unsigned long xport) | ||
211 | { | ||
212 | static int dev; | ||
213 | int res; | ||
214 | |||
215 | for ( ; dev < SNDRV_CARDS; dev++) { | ||
216 | if (!enable[dev] || port[dev] != SNDRV_AUTO_PORT) | ||
217 | continue; | ||
218 | port[dev] = xport; | ||
219 | res = snd_gusclassic_probe(dev); | ||
220 | if (res < 0) | ||
221 | port[dev] = SNDRV_AUTO_PORT; | ||
222 | return res; | ||
223 | } | ||
224 | return -ENODEV; | ||
225 | } | ||
226 | |||
227 | static int __init alsa_card_gusclassic_init(void) | ||
228 | { | ||
229 | static unsigned long possible_ports[] = {0x220, 0x230, 0x240, 0x250, 0x260, -1}; | ||
230 | int dev, cards, i; | ||
231 | |||
232 | for (dev = cards = 0; dev < SNDRV_CARDS && enable[dev]; dev++) { | ||
233 | if (port[dev] == SNDRV_AUTO_PORT) | ||
234 | continue; | ||
235 | if (snd_gusclassic_probe(dev) >= 0) | ||
236 | cards++; | ||
237 | } | ||
238 | i = snd_legacy_auto_probe(possible_ports, snd_gusclassic_legacy_auto_probe); | ||
239 | if (i > 0) | ||
240 | cards += i; | ||
241 | |||
242 | if (!cards) { | ||
243 | #ifdef MODULE | ||
244 | printk(KERN_ERR "GUS Classic soundcard not found or device busy\n"); | ||
245 | #endif | ||
246 | return -ENODEV; | ||
247 | } | ||
248 | return 0; | ||
249 | } | ||
250 | |||
251 | static void __exit alsa_card_gusclassic_exit(void) | ||
252 | { | ||
253 | int idx; | ||
254 | |||
255 | for (idx = 0; idx < SNDRV_CARDS; idx++) | ||
256 | snd_card_free(snd_gusclassic_cards[idx]); | ||
257 | } | ||
258 | |||
259 | module_init(alsa_card_gusclassic_init) | ||
260 | module_exit(alsa_card_gusclassic_exit) | ||
diff --git a/sound/isa/gus/gusextreme.c b/sound/isa/gus/gusextreme.c new file mode 100644 index 000000000000..bc6fecb18dcf --- /dev/null +++ b/sound/isa/gus/gusextreme.c | |||
@@ -0,0 +1,374 @@ | |||
1 | /* | ||
2 | * Driver for Gravis UltraSound Extreme soundcards | ||
3 | * Copyright (c) by Jaroslav Kysela <perex@suse.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 <asm/dma.h> | ||
24 | #include <linux/init.h> | ||
25 | #include <linux/delay.h> | ||
26 | #include <linux/time.h> | ||
27 | #include <linux/moduleparam.h> | ||
28 | #include <sound/core.h> | ||
29 | #include <sound/gus.h> | ||
30 | #include <sound/es1688.h> | ||
31 | #include <sound/mpu401.h> | ||
32 | #include <sound/opl3.h> | ||
33 | #define SNDRV_LEGACY_AUTO_PROBE | ||
34 | #define SNDRV_LEGACY_FIND_FREE_IRQ | ||
35 | #define SNDRV_LEGACY_FIND_FREE_DMA | ||
36 | #include <sound/initval.h> | ||
37 | |||
38 | MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>"); | ||
39 | MODULE_DESCRIPTION("Gravis UltraSound Extreme"); | ||
40 | MODULE_LICENSE("GPL"); | ||
41 | MODULE_SUPPORTED_DEVICE("{{Gravis,UltraSound Extreme}}"); | ||
42 | |||
43 | static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ | ||
44 | static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ | ||
45 | static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE; /* Enable this card */ | ||
46 | static long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* 0x220,0x240,0x260 */ | ||
47 | static long gf1_port[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS) - 1] = -1}; /* 0x210,0x220,0x230,0x240,0x250,0x260,0x270 */ | ||
48 | static long mpu_port[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS) - 1] = -1}; /* 0x300,0x310,0x320 */ | ||
49 | static int irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* 5,7,9,10 */ | ||
50 | static int mpu_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* 5,7,9,10 */ | ||
51 | static int gf1_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* 2,3,5,9,11,12,15 */ | ||
52 | static int dma8[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* 0,1,3 */ | ||
53 | static int dma1[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; | ||
54 | static int joystick_dac[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 29}; | ||
55 | /* 0 to 31, (0.59V-4.52V or 0.389V-2.98V) */ | ||
56 | static int channels[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 24}; | ||
57 | static int pcm_channels[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 2}; | ||
58 | |||
59 | module_param_array(index, int, NULL, 0444); | ||
60 | MODULE_PARM_DESC(index, "Index value for GUS Extreme soundcard."); | ||
61 | module_param_array(id, charp, NULL, 0444); | ||
62 | MODULE_PARM_DESC(id, "ID string for GUS Extreme soundcard."); | ||
63 | module_param_array(enable, bool, NULL, 0444); | ||
64 | MODULE_PARM_DESC(enable, "Enable GUS Extreme soundcard."); | ||
65 | module_param_array(port, long, NULL, 0444); | ||
66 | MODULE_PARM_DESC(port, "Port # for GUS Extreme driver."); | ||
67 | module_param_array(gf1_port, long, NULL, 0444); | ||
68 | MODULE_PARM_DESC(gf1_port, "GF1 port # for GUS Extreme driver (optional)."); | ||
69 | module_param_array(mpu_port, long, NULL, 0444); | ||
70 | MODULE_PARM_DESC(mpu_port, "MPU-401 port # for GUS Extreme driver."); | ||
71 | module_param_array(irq, int, NULL, 0444); | ||
72 | MODULE_PARM_DESC(irq, "IRQ # for GUS Extreme driver."); | ||
73 | module_param_array(mpu_irq, int, NULL, 0444); | ||
74 | MODULE_PARM_DESC(mpu_irq, "MPU-401 IRQ # for GUS Extreme driver."); | ||
75 | module_param_array(gf1_irq, int, NULL, 0444); | ||
76 | MODULE_PARM_DESC(gf1_irq, "GF1 IRQ # for GUS Extreme driver."); | ||
77 | module_param_array(dma8, int, NULL, 0444); | ||
78 | MODULE_PARM_DESC(dma8, "8-bit DMA # for GUS Extreme driver."); | ||
79 | module_param_array(dma1, int, NULL, 0444); | ||
80 | MODULE_PARM_DESC(dma1, "GF1 DMA # for GUS Extreme driver."); | ||
81 | module_param_array(joystick_dac, int, NULL, 0444); | ||
82 | MODULE_PARM_DESC(joystick_dac, "Joystick DAC level 0.59V-4.52V or 0.389V-2.98V for GUS Extreme driver."); | ||
83 | module_param_array(channels, int, NULL, 0444); | ||
84 | MODULE_PARM_DESC(channels, "GF1 channels for GUS Extreme driver."); | ||
85 | module_param_array(pcm_channels, int, NULL, 0444); | ||
86 | MODULE_PARM_DESC(pcm_channels, "Reserved PCM channels for GUS Extreme driver."); | ||
87 | |||
88 | static snd_card_t *snd_gusextreme_cards[SNDRV_CARDS] = SNDRV_DEFAULT_PTR; | ||
89 | |||
90 | |||
91 | static int __init snd_gusextreme_detect(int dev, | ||
92 | snd_card_t * card, | ||
93 | snd_gus_card_t * gus, | ||
94 | es1688_t *es1688) | ||
95 | { | ||
96 | unsigned long flags; | ||
97 | |||
98 | /* | ||
99 | * This is main stuff - enable access to GF1 chip... | ||
100 | * I'm not sure, if this will work for card which have | ||
101 | * ES1688 chip in another place than 0x220. | ||
102 | * | ||
103 | * I used reverse-engineering in DOSEMU. [--jk] | ||
104 | * | ||
105 | * ULTRINIT.EXE: | ||
106 | * 0x230 = 0,2,3 | ||
107 | * 0x240 = 2,0,1 | ||
108 | * 0x250 = 2,0,3 | ||
109 | * 0x260 = 2,2,1 | ||
110 | */ | ||
111 | |||
112 | spin_lock_irqsave(&es1688->mixer_lock, flags); | ||
113 | snd_es1688_mixer_write(es1688, 0x40, 0x0b); /* don't change!!! */ | ||
114 | spin_unlock_irqrestore(&es1688->mixer_lock, flags); | ||
115 | spin_lock_irqsave(&es1688->reg_lock, flags); | ||
116 | outb(gf1_port[dev] & 0x040 ? 2 : 0, ES1688P(es1688, INIT1)); | ||
117 | outb(0, 0x201); | ||
118 | outb(gf1_port[dev] & 0x020 ? 2 : 0, ES1688P(es1688, INIT1)); | ||
119 | outb(0, 0x201); | ||
120 | outb(gf1_port[dev] & 0x010 ? 3 : 1, ES1688P(es1688, INIT1)); | ||
121 | spin_unlock_irqrestore(&es1688->reg_lock, flags); | ||
122 | |||
123 | udelay(100); | ||
124 | |||
125 | snd_gf1_i_write8(gus, SNDRV_GF1_GB_RESET, 0); /* reset GF1 */ | ||
126 | #ifdef CONFIG_SND_DEBUG_DETECT | ||
127 | { | ||
128 | unsigned char d; | ||
129 | |||
130 | if (((d = snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET)) & 0x07) != 0) { | ||
131 | snd_printk("[0x%lx] check 1 failed - 0x%x\n", gus->gf1.port, d); | ||
132 | return -EIO; | ||
133 | } | ||
134 | } | ||
135 | #else | ||
136 | if ((snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET) & 0x07) != 0) | ||
137 | return -EIO; | ||
138 | #endif | ||
139 | udelay(160); | ||
140 | snd_gf1_i_write8(gus, SNDRV_GF1_GB_RESET, 1); /* release reset */ | ||
141 | udelay(160); | ||
142 | #ifdef CONFIG_SND_DEBUG_DETECT | ||
143 | { | ||
144 | unsigned char d; | ||
145 | |||
146 | if (((d = snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET)) & 0x07) != 1) { | ||
147 | snd_printk("[0x%lx] check 2 failed - 0x%x\n", gus->gf1.port, d); | ||
148 | return -EIO; | ||
149 | } | ||
150 | } | ||
151 | #else | ||
152 | if ((snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET) & 0x07) != 1) | ||
153 | return -EIO; | ||
154 | #endif | ||
155 | |||
156 | return 0; | ||
157 | } | ||
158 | |||
159 | static void __init snd_gusextreme_init(int dev, snd_gus_card_t * gus) | ||
160 | { | ||
161 | gus->joystick_dac = joystick_dac[dev]; | ||
162 | } | ||
163 | |||
164 | static int __init snd_gusextreme_mixer(es1688_t *chip) | ||
165 | { | ||
166 | snd_card_t *card = chip->card; | ||
167 | snd_ctl_elem_id_t id1, id2; | ||
168 | int err; | ||
169 | |||
170 | memset(&id1, 0, sizeof(id1)); | ||
171 | memset(&id2, 0, sizeof(id2)); | ||
172 | id1.iface = id2.iface = SNDRV_CTL_ELEM_IFACE_MIXER; | ||
173 | /* reassign AUX to SYNTHESIZER */ | ||
174 | strcpy(id1.name, "Aux Playback Volume"); | ||
175 | strcpy(id2.name, "Synth Playback Volume"); | ||
176 | if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0) | ||
177 | return err; | ||
178 | /* reassign Master Playback Switch to Synth Playback Switch */ | ||
179 | strcpy(id1.name, "Master Playback Switch"); | ||
180 | strcpy(id2.name, "Synth Playback Switch"); | ||
181 | if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0) | ||
182 | return err; | ||
183 | return 0; | ||
184 | } | ||
185 | |||
186 | static int __init snd_gusextreme_probe(int dev) | ||
187 | { | ||
188 | static int possible_ess_irqs[] = {5, 9, 10, 7, -1}; | ||
189 | static int possible_ess_dmas[] = {1, 3, 0, -1}; | ||
190 | static int possible_gf1_irqs[] = {5, 11, 12, 9, 7, 15, 3, -1}; | ||
191 | static int possible_gf1_dmas[] = {5, 6, 7, 1, 3, -1}; | ||
192 | int xgf1_irq, xgf1_dma, xess_irq, xmpu_irq, xess_dma; | ||
193 | snd_card_t *card; | ||
194 | struct snd_gusextreme *acard; | ||
195 | snd_gus_card_t *gus; | ||
196 | es1688_t *es1688; | ||
197 | opl3_t *opl3; | ||
198 | int err; | ||
199 | |||
200 | card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0); | ||
201 | if (card == NULL) | ||
202 | return -ENOMEM; | ||
203 | acard = (struct snd_gusextreme *)card->private_data; | ||
204 | |||
205 | xgf1_irq = gf1_irq[dev]; | ||
206 | if (xgf1_irq == SNDRV_AUTO_IRQ) { | ||
207 | if ((xgf1_irq = snd_legacy_find_free_irq(possible_gf1_irqs)) < 0) { | ||
208 | snd_printk("unable to find a free IRQ for GF1\n"); | ||
209 | err = -EBUSY; | ||
210 | goto out; | ||
211 | } | ||
212 | } | ||
213 | xess_irq = irq[dev]; | ||
214 | if (xess_irq == SNDRV_AUTO_IRQ) { | ||
215 | if ((xess_irq = snd_legacy_find_free_irq(possible_ess_irqs)) < 0) { | ||
216 | snd_printk("unable to find a free IRQ for ES1688\n"); | ||
217 | err = -EBUSY; | ||
218 | goto out; | ||
219 | } | ||
220 | } | ||
221 | if (mpu_port[dev] == SNDRV_AUTO_PORT) | ||
222 | mpu_port[dev] = 0; | ||
223 | xmpu_irq = mpu_irq[dev]; | ||
224 | if (xmpu_irq > 15) | ||
225 | xmpu_irq = -1; | ||
226 | xgf1_dma = dma1[dev]; | ||
227 | if (xgf1_dma == SNDRV_AUTO_DMA) { | ||
228 | if ((xgf1_dma = snd_legacy_find_free_dma(possible_gf1_dmas)) < 0) { | ||
229 | snd_printk("unable to find a free DMA for GF1\n"); | ||
230 | err = -EBUSY; | ||
231 | goto out; | ||
232 | } | ||
233 | } | ||
234 | xess_dma = dma8[dev]; | ||
235 | if (xess_dma == SNDRV_AUTO_DMA) { | ||
236 | if ((xess_dma = snd_legacy_find_free_dma(possible_ess_dmas)) < 0) { | ||
237 | snd_printk("unable to find a free DMA for ES1688\n"); | ||
238 | err = -EBUSY; | ||
239 | goto out; | ||
240 | } | ||
241 | } | ||
242 | |||
243 | if ((err = snd_es1688_create(card, port[dev], mpu_port[dev], | ||
244 | xess_irq, xmpu_irq, xess_dma, | ||
245 | ES1688_HW_1688, &es1688)) < 0) | ||
246 | goto out; | ||
247 | if (gf1_port[dev] < 0) | ||
248 | gf1_port[dev] = port[dev] + 0x20; | ||
249 | if ((err = snd_gus_create(card, | ||
250 | gf1_port[dev], | ||
251 | xgf1_irq, | ||
252 | xgf1_dma, | ||
253 | -1, | ||
254 | 0, channels[dev], | ||
255 | pcm_channels[dev], 0, | ||
256 | &gus)) < 0) | ||
257 | goto out; | ||
258 | |||
259 | if ((err = snd_gusextreme_detect(dev, card, gus, es1688)) < 0) | ||
260 | goto out; | ||
261 | |||
262 | snd_gusextreme_init(dev, gus); | ||
263 | if ((err = snd_gus_initialize(gus)) < 0) | ||
264 | goto out; | ||
265 | |||
266 | if (!gus->ess_flag) { | ||
267 | snd_printdd("GUS Extreme soundcard was not detected at 0x%lx\n", gus->gf1.port); | ||
268 | err = -ENODEV; | ||
269 | goto out; | ||
270 | } | ||
271 | if ((err = snd_es1688_pcm(es1688, 0, NULL)) < 0) | ||
272 | goto out; | ||
273 | |||
274 | if ((err = snd_es1688_mixer(es1688)) < 0) | ||
275 | goto out; | ||
276 | |||
277 | snd_component_add(card, "ES1688"); | ||
278 | if (pcm_channels[dev] > 0) { | ||
279 | if ((err = snd_gf1_pcm_new(gus, 1, 1, NULL)) < 0) | ||
280 | goto out; | ||
281 | } | ||
282 | if ((err = snd_gf1_new_mixer(gus)) < 0) | ||
283 | goto out; | ||
284 | |||
285 | if ((err = snd_gusextreme_mixer(es1688)) < 0) | ||
286 | goto out; | ||
287 | |||
288 | if (snd_opl3_create(card, es1688->port, es1688->port + 2, | ||
289 | OPL3_HW_OPL3, 0, &opl3) < 0) { | ||
290 | printk(KERN_ERR "gusextreme: opl3 not detected at 0x%lx\n", es1688->port); | ||
291 | } else { | ||
292 | if ((err = snd_opl3_hwdep_new(opl3, 0, 2, NULL)) < 0) | ||
293 | goto out; | ||
294 | } | ||
295 | |||
296 | if (es1688->mpu_port >= 0x300 && | ||
297 | (err = snd_mpu401_uart_new(card, 0, MPU401_HW_ES1688, | ||
298 | es1688->mpu_port, 0, | ||
299 | xmpu_irq, | ||
300 | SA_INTERRUPT, | ||
301 | NULL)) < 0) | ||
302 | goto out; | ||
303 | |||
304 | sprintf(card->longname, "Gravis UltraSound Extreme at 0x%lx, irq %i&%i, dma %i&%i", | ||
305 | es1688->port, xgf1_irq, xess_irq, xgf1_dma, xess_dma); | ||
306 | if ((err = snd_card_register(card)) < 0) | ||
307 | goto out; | ||
308 | |||
309 | snd_gusextreme_cards[dev] = card; | ||
310 | return 0; | ||
311 | |||
312 | out: | ||
313 | snd_card_free(card); | ||
314 | return err; | ||
315 | } | ||
316 | |||
317 | static int __init snd_gusextreme_legacy_auto_probe(unsigned long xport) | ||
318 | { | ||
319 | static int dev; | ||
320 | int res; | ||
321 | |||
322 | for ( ; dev < SNDRV_CARDS; dev++) { | ||
323 | if (!enable[dev] || port[dev] != SNDRV_AUTO_PORT) | ||
324 | continue; | ||
325 | port[dev] = xport; | ||
326 | res = snd_gusextreme_probe(dev); | ||
327 | if (res < 0) | ||
328 | port[dev] = SNDRV_AUTO_PORT; | ||
329 | return res; | ||
330 | } | ||
331 | return -ENODEV; | ||
332 | } | ||
333 | |||
334 | static int __init alsa_card_gusextreme_init(void) | ||
335 | { | ||
336 | static unsigned long possible_ports[] = {0x220, 0x240, 0x260, -1}; | ||
337 | int dev, cards, i; | ||
338 | |||
339 | for (dev = cards = 0; dev < SNDRV_CARDS && enable[dev] > 0; dev++) { | ||
340 | if (port[dev] == SNDRV_AUTO_PORT) | ||
341 | continue; | ||
342 | if (snd_gusextreme_probe(dev) >= 0) | ||
343 | cards++; | ||
344 | } | ||
345 | i = snd_legacy_auto_probe(possible_ports, snd_gusextreme_legacy_auto_probe); | ||
346 | if (i > 0) | ||
347 | cards += i; | ||
348 | |||
349 | if (!cards) { | ||
350 | #ifdef MODULE | ||
351 | printk(KERN_ERR "GUS Extreme soundcard not found or device busy\n"); | ||
352 | #endif | ||
353 | return -ENODEV; | ||
354 | } | ||
355 | return 0; | ||
356 | } | ||
357 | |||
358 | static void __exit alsa_card_gusextreme_exit(void) | ||
359 | { | ||
360 | int idx; | ||
361 | snd_card_t *card; | ||
362 | struct snd_gusextreme *acard; | ||
363 | |||
364 | for (idx = 0; idx < SNDRV_CARDS; idx++) { | ||
365 | card = snd_gusextreme_cards[idx]; | ||
366 | if (card == NULL) | ||
367 | continue; | ||
368 | acard = (struct snd_gusextreme *)card->private_data; | ||
369 | snd_card_free(snd_gusextreme_cards[idx]); | ||
370 | } | ||
371 | } | ||
372 | |||
373 | module_init(alsa_card_gusextreme_init) | ||
374 | module_exit(alsa_card_gusextreme_exit) | ||
diff --git a/sound/isa/gus/gusmax.c b/sound/isa/gus/gusmax.c new file mode 100644 index 000000000000..400ff34710fb --- /dev/null +++ b/sound/isa/gus/gusmax.c | |||
@@ -0,0 +1,400 @@ | |||
1 | /* | ||
2 | * Driver for Gravis UltraSound MAX soundcard | ||
3 | * Copyright (c) by Jaroslav Kysela <perex@suse.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 <asm/dma.h> | ||
24 | #include <linux/init.h> | ||
25 | #include <linux/delay.h> | ||
26 | #include <linux/time.h> | ||
27 | #include <linux/moduleparam.h> | ||
28 | #include <sound/core.h> | ||
29 | #include <sound/gus.h> | ||
30 | #include <sound/cs4231.h> | ||
31 | #define SNDRV_LEGACY_AUTO_PROBE | ||
32 | #define SNDRV_LEGACY_FIND_FREE_IRQ | ||
33 | #define SNDRV_LEGACY_FIND_FREE_DMA | ||
34 | #include <sound/initval.h> | ||
35 | |||
36 | MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>"); | ||
37 | MODULE_DESCRIPTION("Gravis UltraSound MAX"); | ||
38 | MODULE_LICENSE("GPL"); | ||
39 | MODULE_SUPPORTED_DEVICE("{{Gravis,UltraSound MAX}}"); | ||
40 | |||
41 | static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ | ||
42 | static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ | ||
43 | static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE; /* Enable this card */ | ||
44 | static long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* 0x220,0x230,0x240,0x250,0x260 */ | ||
45 | static int irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* 2,3,5,9,11,12,15 */ | ||
46 | static int dma1[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* 1,3,5,6,7 */ | ||
47 | static int dma2[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* 1,3,5,6,7 */ | ||
48 | static int joystick_dac[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 29}; | ||
49 | /* 0 to 31, (0.59V-4.52V or 0.389V-2.98V) */ | ||
50 | static int channels[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 24}; | ||
51 | static int pcm_channels[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 2}; | ||
52 | |||
53 | module_param_array(index, int, NULL, 0444); | ||
54 | MODULE_PARM_DESC(index, "Index value for GUS MAX soundcard."); | ||
55 | module_param_array(id, charp, NULL, 0444); | ||
56 | MODULE_PARM_DESC(id, "ID string for GUS MAX soundcard."); | ||
57 | module_param_array(enable, bool, NULL, 0444); | ||
58 | MODULE_PARM_DESC(enable, "Enable GUS MAX soundcard."); | ||
59 | module_param_array(port, long, NULL, 0444); | ||
60 | MODULE_PARM_DESC(port, "Port # for GUS MAX driver."); | ||
61 | module_param_array(irq, int, NULL, 0444); | ||
62 | MODULE_PARM_DESC(irq, "IRQ # for GUS MAX driver."); | ||
63 | module_param_array(dma1, int, NULL, 0444); | ||
64 | MODULE_PARM_DESC(dma1, "DMA1 # for GUS MAX driver."); | ||
65 | module_param_array(dma2, int, NULL, 0444); | ||
66 | MODULE_PARM_DESC(dma2, "DMA2 # for GUS MAX driver."); | ||
67 | module_param_array(joystick_dac, int, NULL, 0444); | ||
68 | MODULE_PARM_DESC(joystick_dac, "Joystick DAC level 0.59V-4.52V or 0.389V-2.98V for GUS MAX driver."); | ||
69 | module_param_array(channels, int, NULL, 0444); | ||
70 | MODULE_PARM_DESC(channels, "Used GF1 channels for GUS MAX driver."); | ||
71 | module_param_array(pcm_channels, int, NULL, 0444); | ||
72 | MODULE_PARM_DESC(pcm_channels, "Reserved PCM channels for GUS MAX driver."); | ||
73 | |||
74 | struct snd_gusmax { | ||
75 | int irq; | ||
76 | snd_card_t *card; | ||
77 | snd_gus_card_t *gus; | ||
78 | cs4231_t *cs4231; | ||
79 | unsigned short gus_status_reg; | ||
80 | unsigned short pcm_status_reg; | ||
81 | }; | ||
82 | |||
83 | static snd_card_t *snd_gusmax_cards[SNDRV_CARDS] = SNDRV_DEFAULT_PTR; | ||
84 | |||
85 | |||
86 | static int __init snd_gusmax_detect(snd_gus_card_t * gus) | ||
87 | { | ||
88 | snd_gf1_i_write8(gus, SNDRV_GF1_GB_RESET, 0); /* reset GF1 */ | ||
89 | #ifdef CONFIG_SND_DEBUG_DETECT | ||
90 | { | ||
91 | unsigned char d; | ||
92 | |||
93 | if (((d = snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET)) & 0x07) != 0) { | ||
94 | snd_printk("[0x%lx] check 1 failed - 0x%x\n", gus->gf1.port, d); | ||
95 | return -ENODEV; | ||
96 | } | ||
97 | } | ||
98 | #else | ||
99 | if ((snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET) & 0x07) != 0) | ||
100 | return -ENODEV; | ||
101 | #endif | ||
102 | udelay(160); | ||
103 | snd_gf1_i_write8(gus, SNDRV_GF1_GB_RESET, 1); /* release reset */ | ||
104 | udelay(160); | ||
105 | #ifdef CONFIG_SND_DEBUG_DETECT | ||
106 | { | ||
107 | unsigned char d; | ||
108 | |||
109 | if (((d = snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET)) & 0x07) != 1) { | ||
110 | snd_printk("[0x%lx] check 2 failed - 0x%x\n", gus->gf1.port, d); | ||
111 | return -ENODEV; | ||
112 | } | ||
113 | } | ||
114 | #else | ||
115 | if ((snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET) & 0x07) != 1) | ||
116 | return -ENODEV; | ||
117 | #endif | ||
118 | return 0; | ||
119 | } | ||
120 | |||
121 | static irqreturn_t snd_gusmax_interrupt(int irq, void *dev_id, struct pt_regs *regs) | ||
122 | { | ||
123 | struct snd_gusmax *maxcard = (struct snd_gusmax *) dev_id; | ||
124 | int loop, max = 5; | ||
125 | int handled = 0; | ||
126 | |||
127 | do { | ||
128 | loop = 0; | ||
129 | if (inb(maxcard->gus_status_reg)) { | ||
130 | handled = 1; | ||
131 | snd_gus_interrupt(irq, maxcard->gus, regs); | ||
132 | loop++; | ||
133 | } | ||
134 | if (inb(maxcard->pcm_status_reg) & 0x01) { /* IRQ bit is set? */ | ||
135 | handled = 1; | ||
136 | snd_cs4231_interrupt(irq, maxcard->cs4231, regs); | ||
137 | loop++; | ||
138 | } | ||
139 | } while (loop && --max > 0); | ||
140 | return IRQ_RETVAL(handled); | ||
141 | } | ||
142 | |||
143 | static void __init snd_gusmax_init(int dev, snd_card_t * card, snd_gus_card_t * gus) | ||
144 | { | ||
145 | gus->equal_irq = 1; | ||
146 | gus->codec_flag = 1; | ||
147 | gus->joystick_dac = joystick_dac[dev]; | ||
148 | /* init control register */ | ||
149 | gus->max_cntrl_val = (gus->gf1.port >> 4) & 0x0f; | ||
150 | if (gus->gf1.dma1 > 3) | ||
151 | gus->max_cntrl_val |= 0x10; | ||
152 | if (gus->gf1.dma2 > 3) | ||
153 | gus->max_cntrl_val |= 0x20; | ||
154 | gus->max_cntrl_val |= 0x40; | ||
155 | outb(gus->max_cntrl_val, GUSP(gus, MAXCNTRLPORT)); | ||
156 | } | ||
157 | |||
158 | #define CS4231_PRIVATE( left, right, shift, mute ) \ | ||
159 | ((left << 24)|(right << 16)|(shift<<8)|mute) | ||
160 | |||
161 | static int __init snd_gusmax_mixer(cs4231_t *chip) | ||
162 | { | ||
163 | snd_card_t *card = chip->card; | ||
164 | snd_ctl_elem_id_t id1, id2; | ||
165 | int err; | ||
166 | |||
167 | memset(&id1, 0, sizeof(id1)); | ||
168 | memset(&id2, 0, sizeof(id2)); | ||
169 | id1.iface = id2.iface = SNDRV_CTL_ELEM_IFACE_MIXER; | ||
170 | /* reassign AUXA to SYNTHESIZER */ | ||
171 | strcpy(id1.name, "Aux Playback Switch"); | ||
172 | strcpy(id2.name, "Synth Playback Switch"); | ||
173 | if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0) | ||
174 | return err; | ||
175 | strcpy(id1.name, "Aux Playback Volume"); | ||
176 | strcpy(id2.name, "Synth Playback Volume"); | ||
177 | if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0) | ||
178 | return err; | ||
179 | /* reassign AUXB to CD */ | ||
180 | strcpy(id1.name, "Aux Playback Switch"); id1.index = 1; | ||
181 | strcpy(id2.name, "CD Playback Switch"); | ||
182 | if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0) | ||
183 | return err; | ||
184 | strcpy(id1.name, "Aux Playback Volume"); | ||
185 | strcpy(id2.name, "CD Playback Volume"); | ||
186 | if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0) | ||
187 | return err; | ||
188 | #if 0 | ||
189 | /* reassign Mono Input to MIC */ | ||
190 | if (snd_mixer_group_rename(mixer, | ||
191 | SNDRV_MIXER_IN_MONO, 0, | ||
192 | SNDRV_MIXER_IN_MIC, 0) < 0) | ||
193 | goto __error; | ||
194 | if (snd_mixer_elem_rename(mixer, | ||
195 | SNDRV_MIXER_IN_MONO, 0, SNDRV_MIXER_ETYPE_INPUT, | ||
196 | SNDRV_MIXER_IN_MIC, 0) < 0) | ||
197 | goto __error; | ||
198 | if (snd_mixer_elem_rename(mixer, | ||
199 | "Mono Capture Volume", 0, SNDRV_MIXER_ETYPE_VOLUME1, | ||
200 | "Mic Capture Volume", 0) < 0) | ||
201 | goto __error; | ||
202 | if (snd_mixer_elem_rename(mixer, | ||
203 | "Mono Capture Switch", 0, SNDRV_MIXER_ETYPE_SWITCH1, | ||
204 | "Mic Capture Switch", 0) < 0) | ||
205 | goto __error; | ||
206 | #endif | ||
207 | return 0; | ||
208 | } | ||
209 | |||
210 | static void snd_gusmax_free(snd_card_t *card) | ||
211 | { | ||
212 | struct snd_gusmax *maxcard = (struct snd_gusmax *)card->private_data; | ||
213 | |||
214 | if (maxcard == NULL) | ||
215 | return; | ||
216 | if (maxcard->irq >= 0) | ||
217 | free_irq(maxcard->irq, (void *)maxcard); | ||
218 | } | ||
219 | |||
220 | static int __init snd_gusmax_probe(int dev) | ||
221 | { | ||
222 | static int possible_irqs[] = {5, 11, 12, 9, 7, 15, 3, -1}; | ||
223 | static int possible_dmas[] = {5, 6, 7, 1, 3, -1}; | ||
224 | int xirq, xdma1, xdma2, err; | ||
225 | snd_card_t *card; | ||
226 | snd_gus_card_t *gus = NULL; | ||
227 | cs4231_t *cs4231; | ||
228 | struct snd_gusmax *maxcard; | ||
229 | |||
230 | card = snd_card_new(index[dev], id[dev], THIS_MODULE, | ||
231 | sizeof(struct snd_gusmax)); | ||
232 | if (card == NULL) | ||
233 | return -ENOMEM; | ||
234 | card->private_free = snd_gusmax_free; | ||
235 | maxcard = (struct snd_gusmax *)card->private_data; | ||
236 | maxcard->card = card; | ||
237 | maxcard->irq = -1; | ||
238 | |||
239 | xirq = irq[dev]; | ||
240 | if (xirq == SNDRV_AUTO_IRQ) { | ||
241 | if ((xirq = snd_legacy_find_free_irq(possible_irqs)) < 0) { | ||
242 | snd_card_free(card); | ||
243 | snd_printk("unable to find a free IRQ\n"); | ||
244 | return -EBUSY; | ||
245 | } | ||
246 | } | ||
247 | xdma1 = dma1[dev]; | ||
248 | if (xdma1 == SNDRV_AUTO_DMA) { | ||
249 | if ((xdma1 = snd_legacy_find_free_dma(possible_dmas)) < 0) { | ||
250 | snd_card_free(card); | ||
251 | snd_printk("unable to find a free DMA1\n"); | ||
252 | return -EBUSY; | ||
253 | } | ||
254 | } | ||
255 | xdma2 = dma2[dev]; | ||
256 | if (xdma2 == SNDRV_AUTO_DMA) { | ||
257 | if ((xdma2 = snd_legacy_find_free_dma(possible_dmas)) < 0) { | ||
258 | snd_card_free(card); | ||
259 | snd_printk("unable to find a free DMA2\n"); | ||
260 | return -EBUSY; | ||
261 | } | ||
262 | } | ||
263 | |||
264 | if ((err = snd_gus_create(card, | ||
265 | port[dev], | ||
266 | -xirq, xdma1, xdma2, | ||
267 | 0, channels[dev], | ||
268 | pcm_channels[dev], | ||
269 | 0, &gus)) < 0) { | ||
270 | snd_card_free(card); | ||
271 | return err; | ||
272 | } | ||
273 | if ((err = snd_gusmax_detect(gus)) < 0) { | ||
274 | snd_card_free(card); | ||
275 | return err; | ||
276 | } | ||
277 | maxcard->gus_status_reg = gus->gf1.reg_irqstat; | ||
278 | maxcard->pcm_status_reg = gus->gf1.port + 0x10c + 2; | ||
279 | snd_gusmax_init(dev, card, gus); | ||
280 | if ((err = snd_gus_initialize(gus)) < 0) { | ||
281 | snd_card_free(card); | ||
282 | return err; | ||
283 | } | ||
284 | if (!gus->max_flag) { | ||
285 | printk(KERN_ERR "GUS MAX soundcard was not detected at 0x%lx\n", gus->gf1.port); | ||
286 | snd_card_free(card); | ||
287 | return -ENODEV; | ||
288 | } | ||
289 | |||
290 | if (request_irq(xirq, snd_gusmax_interrupt, SA_INTERRUPT, "GUS MAX", (void *)maxcard)) { | ||
291 | snd_card_free(card); | ||
292 | printk(KERN_ERR "gusmax: unable to grab IRQ %d\n", xirq); | ||
293 | return -EBUSY; | ||
294 | } | ||
295 | maxcard->irq = xirq; | ||
296 | |||
297 | if ((err = snd_cs4231_create(card, | ||
298 | gus->gf1.port + 0x10c, -1, xirq, | ||
299 | xdma2 < 0 ? xdma1 : xdma2, xdma1, | ||
300 | CS4231_HW_DETECT, | ||
301 | CS4231_HWSHARE_IRQ | | ||
302 | CS4231_HWSHARE_DMA1 | | ||
303 | CS4231_HWSHARE_DMA2, | ||
304 | &cs4231)) < 0) { | ||
305 | snd_card_free(card); | ||
306 | return err; | ||
307 | } | ||
308 | if ((err = snd_cs4231_pcm(cs4231, 0, NULL)) < 0) { | ||
309 | snd_card_free(card); | ||
310 | return err; | ||
311 | } | ||
312 | if ((err = snd_cs4231_mixer(cs4231)) < 0) { | ||
313 | snd_card_free(card); | ||
314 | return err; | ||
315 | } | ||
316 | if ((err = snd_cs4231_timer(cs4231, 2, NULL)) < 0) { | ||
317 | snd_card_free(card); | ||
318 | return err; | ||
319 | } | ||
320 | if (pcm_channels[dev] > 0) { | ||
321 | if ((err = snd_gf1_pcm_new(gus, 1, 1, NULL)) < 0) { | ||
322 | snd_card_free(card); | ||
323 | return err; | ||
324 | } | ||
325 | } | ||
326 | if ((err = snd_gusmax_mixer(cs4231)) < 0) { | ||
327 | snd_card_free(card); | ||
328 | return err; | ||
329 | } | ||
330 | |||
331 | if ((err = snd_gf1_rawmidi_new(gus, 0, NULL)) < 0) { | ||
332 | snd_card_free(card); | ||
333 | return err; | ||
334 | } | ||
335 | |||
336 | sprintf(card->longname + strlen(card->longname), " at 0x%lx, irq %i, dma %i", gus->gf1.port, xirq, xdma1); | ||
337 | if (xdma2 >= 0) | ||
338 | sprintf(card->longname + strlen(card->longname), "&%i", xdma2); | ||
339 | if ((err = snd_card_register(card)) < 0) { | ||
340 | snd_card_free(card); | ||
341 | return err; | ||
342 | } | ||
343 | |||
344 | maxcard->gus = gus; | ||
345 | maxcard->cs4231 = cs4231; | ||
346 | snd_gusmax_cards[dev] = card; | ||
347 | return 0; | ||
348 | } | ||
349 | |||
350 | static int __init snd_gusmax_legacy_auto_probe(unsigned long xport) | ||
351 | { | ||
352 | static int dev; | ||
353 | int res; | ||
354 | |||
355 | for ( ; dev < SNDRV_CARDS; dev++) { | ||
356 | if (!enable[dev] || port[dev] != SNDRV_AUTO_PORT) | ||
357 | continue; | ||
358 | port[dev] = xport; | ||
359 | res = snd_gusmax_probe(dev); | ||
360 | if (res < 0) | ||
361 | port[dev] = SNDRV_AUTO_PORT; | ||
362 | return res; | ||
363 | } | ||
364 | return -ENODEV; | ||
365 | } | ||
366 | |||
367 | static int __init alsa_card_gusmax_init(void) | ||
368 | { | ||
369 | static unsigned long possible_ports[] = {0x220, 0x230, 0x240, 0x250, 0x260, -1}; | ||
370 | int dev, cards, i; | ||
371 | |||
372 | for (dev = cards = 0; dev < SNDRV_CARDS && enable[dev] > 0; dev++) { | ||
373 | if (port[dev] == SNDRV_AUTO_PORT) | ||
374 | continue; | ||
375 | if (snd_gusmax_probe(dev) >= 0) | ||
376 | cards++; | ||
377 | } | ||
378 | i = snd_legacy_auto_probe(possible_ports, snd_gusmax_legacy_auto_probe); | ||
379 | if (i > 0) | ||
380 | cards += i; | ||
381 | |||
382 | if (!cards) { | ||
383 | #ifdef MODULE | ||
384 | printk(KERN_ERR "GUS MAX soundcard not found or device busy\n"); | ||
385 | #endif | ||
386 | return -ENODEV; | ||
387 | } | ||
388 | return 0; | ||
389 | } | ||
390 | |||
391 | static void __exit alsa_card_gusmax_exit(void) | ||
392 | { | ||
393 | int idx; | ||
394 | |||
395 | for (idx = 0; idx < SNDRV_CARDS; idx++) | ||
396 | snd_card_free(snd_gusmax_cards[idx]); | ||
397 | } | ||
398 | |||
399 | module_init(alsa_card_gusmax_init) | ||
400 | module_exit(alsa_card_gusmax_exit) | ||
diff --git a/sound/isa/gus/interwave-stb.c b/sound/isa/gus/interwave-stb.c new file mode 100644 index 000000000000..dbe4f48a9846 --- /dev/null +++ b/sound/isa/gus/interwave-stb.c | |||
@@ -0,0 +1,2 @@ | |||
1 | #define SNDRV_STB | ||
2 | #include "interwave.c" | ||
diff --git a/sound/isa/gus/interwave.c b/sound/isa/gus/interwave.c new file mode 100644 index 000000000000..46e867daba6a --- /dev/null +++ b/sound/isa/gus/interwave.c | |||
@@ -0,0 +1,969 @@ | |||
1 | /* | ||
2 | * Driver for AMD InterWave soundcard | ||
3 | * Copyright (c) by Jaroslav Kysela <perex@suse.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 | * 1999/07/22 Erik Inge Bolso <knan@mo.himolde.no> | ||
21 | * * mixer group handlers | ||
22 | * | ||
23 | */ | ||
24 | |||
25 | #include <sound/driver.h> | ||
26 | #include <asm/dma.h> | ||
27 | #include <linux/delay.h> | ||
28 | #include <linux/init.h> | ||
29 | #include <linux/slab.h> | ||
30 | #include <linux/pnp.h> | ||
31 | #include <linux/moduleparam.h> | ||
32 | #include <sound/core.h> | ||
33 | #include <sound/gus.h> | ||
34 | #include <sound/cs4231.h> | ||
35 | #ifdef SNDRV_STB | ||
36 | #include <sound/tea6330t.h> | ||
37 | #endif | ||
38 | #define SNDRV_LEGACY_AUTO_PROBE | ||
39 | #define SNDRV_LEGACY_FIND_FREE_IRQ | ||
40 | #define SNDRV_LEGACY_FIND_FREE_DMA | ||
41 | #include <sound/initval.h> | ||
42 | |||
43 | MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>"); | ||
44 | MODULE_LICENSE("GPL"); | ||
45 | #ifndef SNDRV_STB | ||
46 | MODULE_DESCRIPTION("AMD InterWave"); | ||
47 | MODULE_SUPPORTED_DEVICE("{{Gravis,UltraSound Plug & Play}," | ||
48 | "{STB,SoundRage32}," | ||
49 | "{MED,MED3210}," | ||
50 | "{Dynasonix,Dynasonix Pro}," | ||
51 | "{Panasonic,PCA761AW}}"); | ||
52 | #else | ||
53 | MODULE_DESCRIPTION("AMD InterWave STB with TEA6330T"); | ||
54 | MODULE_SUPPORTED_DEVICE("{{AMD,InterWave STB with TEA6330T}}"); | ||
55 | #endif | ||
56 | |||
57 | static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ | ||
58 | static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ | ||
59 | static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_ISAPNP; /* Enable this card */ | ||
60 | #ifdef CONFIG_PNP | ||
61 | static int isapnp[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1}; | ||
62 | #endif | ||
63 | static long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* 0x210,0x220,0x230,0x240,0x250,0x260 */ | ||
64 | #ifdef SNDRV_STB | ||
65 | static long port_tc[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* 0x350,0x360,0x370,0x380 */ | ||
66 | #endif | ||
67 | static int irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* 2,3,5,9,11,12,15 */ | ||
68 | static int dma1[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* 0,1,3,5,6,7 */ | ||
69 | static int dma2[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* 0,1,3,5,6,7 */ | ||
70 | static int joystick_dac[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 29}; | ||
71 | /* 0 to 31, (0.59V-4.52V or 0.389V-2.98V) */ | ||
72 | static int midi[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 0}; | ||
73 | static int pcm_channels[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 2}; | ||
74 | static int effect[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 0}; | ||
75 | |||
76 | module_param_array(index, int, NULL, 0444); | ||
77 | MODULE_PARM_DESC(index, "Index value for InterWave soundcard."); | ||
78 | module_param_array(id, charp, NULL, 0444); | ||
79 | MODULE_PARM_DESC(id, "ID string for InterWave soundcard."); | ||
80 | module_param_array(enable, bool, NULL, 0444); | ||
81 | MODULE_PARM_DESC(enable, "Enable InterWave soundcard."); | ||
82 | #ifdef CONFIG_PNP | ||
83 | module_param_array(isapnp, bool, NULL, 0444); | ||
84 | MODULE_PARM_DESC(isapnp, "ISA PnP detection for specified soundcard."); | ||
85 | #endif | ||
86 | module_param_array(port, long, NULL, 0444); | ||
87 | MODULE_PARM_DESC(port, "Port # for InterWave driver."); | ||
88 | #ifdef SNDRV_STB | ||
89 | module_param_array(port_tc, long, NULL, 0444); | ||
90 | MODULE_PARM_DESC(port_tc, "Tone control (TEA6330T - i2c bus) port # for InterWave driver."); | ||
91 | #endif | ||
92 | module_param_array(irq, int, NULL, 0444); | ||
93 | MODULE_PARM_DESC(irq, "IRQ # for InterWave driver."); | ||
94 | module_param_array(dma1, int, NULL, 0444); | ||
95 | MODULE_PARM_DESC(dma1, "DMA1 # for InterWave driver."); | ||
96 | module_param_array(dma2, int, NULL, 0444); | ||
97 | MODULE_PARM_DESC(dma2, "DMA2 # for InterWave driver."); | ||
98 | module_param_array(joystick_dac, int, NULL, 0444); | ||
99 | MODULE_PARM_DESC(joystick_dac, "Joystick DAC level 0.59V-4.52V or 0.389V-2.98V for InterWave driver."); | ||
100 | module_param_array(midi, int, NULL, 0444); | ||
101 | MODULE_PARM_DESC(midi, "MIDI UART enable for InterWave driver."); | ||
102 | module_param_array(pcm_channels, int, NULL, 0444); | ||
103 | MODULE_PARM_DESC(pcm_channels, "Reserved PCM channels for InterWave driver."); | ||
104 | module_param_array(effect, int, NULL, 0444); | ||
105 | MODULE_PARM_DESC(effect, "Effects enable for InterWave driver."); | ||
106 | |||
107 | struct snd_interwave { | ||
108 | int irq; | ||
109 | snd_card_t *card; | ||
110 | snd_gus_card_t *gus; | ||
111 | cs4231_t *cs4231; | ||
112 | #ifdef SNDRV_STB | ||
113 | struct resource *i2c_res; | ||
114 | #endif | ||
115 | unsigned short gus_status_reg; | ||
116 | unsigned short pcm_status_reg; | ||
117 | #ifdef CONFIG_PNP | ||
118 | struct pnp_dev *dev; | ||
119 | #ifdef SNDRV_STB | ||
120 | struct pnp_dev *devtc; | ||
121 | #endif | ||
122 | #endif | ||
123 | }; | ||
124 | |||
125 | static snd_card_t *snd_interwave_legacy[SNDRV_CARDS] = SNDRV_DEFAULT_PTR; | ||
126 | |||
127 | #ifdef CONFIG_PNP | ||
128 | |||
129 | static struct pnp_card_device_id snd_interwave_pnpids[] = { | ||
130 | #ifndef SNDRV_STB | ||
131 | /* Gravis UltraSound Plug & Play */ | ||
132 | { .id = "GRV0001", .devs = { { .id = "GRV0000" } } }, | ||
133 | /* STB SoundRage32 */ | ||
134 | { .id = "STB011a", .devs = { { .id = "STB0010" } } }, | ||
135 | /* MED3210 */ | ||
136 | { .id = "DXP3201", .devs = { { .id = "DXP0010" } } }, | ||
137 | /* Dynasonic Pro */ | ||
138 | /* This device also have CDC1117:DynaSonix Pro Audio Effects Processor */ | ||
139 | { .id = "CDC1111", .devs = { { .id = "CDC1112" } } }, | ||
140 | /* Panasonic PCA761AW Audio Card */ | ||
141 | { .id = "ADV55ff", .devs = { { .id = "ADV0010" } } }, | ||
142 | /* InterWave STB without TEA6330T */ | ||
143 | { .id = "ADV550a", .devs = { { .id = "ADV0010" } } }, | ||
144 | #else | ||
145 | /* InterWave STB with TEA6330T */ | ||
146 | { .id = "ADV550a", .devs = { { .id = "ADV0010" }, { .id = "ADV0015" } } }, | ||
147 | #endif | ||
148 | { .id = "" } | ||
149 | }; | ||
150 | |||
151 | MODULE_DEVICE_TABLE(pnp_card, snd_interwave_pnpids); | ||
152 | |||
153 | #endif /* CONFIG_PNP */ | ||
154 | |||
155 | |||
156 | #ifdef SNDRV_STB | ||
157 | static void snd_interwave_i2c_setlines(snd_i2c_bus_t *bus, int ctrl, int data) | ||
158 | { | ||
159 | unsigned long port = bus->private_value; | ||
160 | |||
161 | #if 0 | ||
162 | printk("i2c_setlines - 0x%lx <- %i,%i\n", port, ctrl, data); | ||
163 | #endif | ||
164 | outb((data << 1) | ctrl, port); | ||
165 | udelay(10); | ||
166 | } | ||
167 | |||
168 | static int snd_interwave_i2c_getclockline(snd_i2c_bus_t *bus) | ||
169 | { | ||
170 | unsigned long port = bus->private_value; | ||
171 | unsigned char res; | ||
172 | |||
173 | res = inb(port) & 1; | ||
174 | #if 0 | ||
175 | printk("i2c_getclockline - 0x%lx -> %i\n", port, res); | ||
176 | #endif | ||
177 | return res; | ||
178 | } | ||
179 | |||
180 | static int snd_interwave_i2c_getdataline(snd_i2c_bus_t *bus, int ack) | ||
181 | { | ||
182 | unsigned long port = bus->private_value; | ||
183 | unsigned char res; | ||
184 | |||
185 | if (ack) | ||
186 | udelay(10); | ||
187 | res = (inb(port) & 2) >> 1; | ||
188 | #if 0 | ||
189 | printk("i2c_getdataline - 0x%lx -> %i\n", port, res); | ||
190 | #endif | ||
191 | return res; | ||
192 | } | ||
193 | |||
194 | static snd_i2c_bit_ops_t snd_interwave_i2c_bit_ops = { | ||
195 | .setlines = snd_interwave_i2c_setlines, | ||
196 | .getclock = snd_interwave_i2c_getclockline, | ||
197 | .getdata = snd_interwave_i2c_getdataline, | ||
198 | }; | ||
199 | |||
200 | static int __devinit snd_interwave_detect_stb(struct snd_interwave *iwcard, | ||
201 | snd_gus_card_t * gus, int dev, | ||
202 | snd_i2c_bus_t **rbus) | ||
203 | { | ||
204 | unsigned long port; | ||
205 | snd_i2c_bus_t *bus; | ||
206 | snd_card_t *card = iwcard->card; | ||
207 | char name[32]; | ||
208 | int err; | ||
209 | |||
210 | *rbus = NULL; | ||
211 | port = port_tc[dev]; | ||
212 | if (port == SNDRV_AUTO_PORT) { | ||
213 | port = 0x350; | ||
214 | if (gus->gf1.port == 0x250) { | ||
215 | port = 0x360; | ||
216 | } | ||
217 | while (port <= 0x380) { | ||
218 | if ((iwcard->i2c_res = request_region(port, 1, "InterWave (I2C bus)")) != NULL) | ||
219 | break; | ||
220 | port += 0x10; | ||
221 | } | ||
222 | } else { | ||
223 | iwcard->i2c_res = request_region(port, 1, "InterWave (I2C bus)"); | ||
224 | } | ||
225 | if (iwcard->i2c_res == NULL) { | ||
226 | snd_printk(KERN_ERR "interwave: can't grab i2c bus port\n"); | ||
227 | return -ENODEV; | ||
228 | } | ||
229 | |||
230 | sprintf(name, "InterWave-%i", card->number); | ||
231 | if ((err = snd_i2c_bus_create(card, name, NULL, &bus)) < 0) | ||
232 | return err; | ||
233 | bus->private_value = port; | ||
234 | bus->hw_ops.bit = &snd_interwave_i2c_bit_ops; | ||
235 | if ((err = snd_tea6330t_detect(bus, 0)) < 0) | ||
236 | return err; | ||
237 | *rbus = bus; | ||
238 | return 0; | ||
239 | } | ||
240 | #endif | ||
241 | |||
242 | static int __devinit snd_interwave_detect(struct snd_interwave *iwcard, | ||
243 | snd_gus_card_t * gus, | ||
244 | int dev | ||
245 | #ifdef SNDRV_STB | ||
246 | , snd_i2c_bus_t **rbus | ||
247 | #endif | ||
248 | ) | ||
249 | { | ||
250 | unsigned long flags; | ||
251 | unsigned char rev1, rev2; | ||
252 | |||
253 | snd_gf1_i_write8(gus, SNDRV_GF1_GB_RESET, 0); /* reset GF1 */ | ||
254 | #ifdef CONFIG_SND_DEBUG_DETECT | ||
255 | { | ||
256 | int d; | ||
257 | |||
258 | if (((d = snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET)) & 0x07) != 0) { | ||
259 | snd_printk("[0x%lx] check 1 failed - 0x%x\n", gus->gf1.port, d); | ||
260 | return -ENODEV; | ||
261 | } | ||
262 | } | ||
263 | #else | ||
264 | if ((snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET) & 0x07) != 0) | ||
265 | return -ENODEV; | ||
266 | #endif | ||
267 | udelay(160); | ||
268 | snd_gf1_i_write8(gus, SNDRV_GF1_GB_RESET, 1); /* release reset */ | ||
269 | udelay(160); | ||
270 | #ifdef CONFIG_SND_DEBUG_DETECT | ||
271 | { | ||
272 | int d; | ||
273 | |||
274 | if (((d = snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET)) & 0x07) != 1) { | ||
275 | snd_printk("[0x%lx] check 2 failed - 0x%x\n", gus->gf1.port, d); | ||
276 | return -ENODEV; | ||
277 | } | ||
278 | } | ||
279 | #else | ||
280 | if ((snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET) & 0x07) != 1) | ||
281 | return -ENODEV; | ||
282 | #endif | ||
283 | |||
284 | spin_lock_irqsave(&gus->reg_lock, flags); | ||
285 | rev1 = snd_gf1_look8(gus, SNDRV_GF1_GB_VERSION_NUMBER); | ||
286 | snd_gf1_write8(gus, SNDRV_GF1_GB_VERSION_NUMBER, ~rev1); | ||
287 | rev2 = snd_gf1_look8(gus, SNDRV_GF1_GB_VERSION_NUMBER); | ||
288 | snd_gf1_write8(gus, SNDRV_GF1_GB_VERSION_NUMBER, rev1); | ||
289 | spin_unlock_irqrestore(&gus->reg_lock, flags); | ||
290 | snd_printdd("[0x%lx] InterWave check - rev1=0x%x, rev2=0x%x\n", gus->gf1.port, rev1, rev2); | ||
291 | if ((rev1 & 0xf0) == (rev2 & 0xf0) && | ||
292 | (rev1 & 0x0f) != (rev2 & 0x0f)) { | ||
293 | snd_printdd("[0x%lx] InterWave check - passed\n", gus->gf1.port); | ||
294 | gus->interwave = 1; | ||
295 | strcpy(gus->card->shortname, "AMD InterWave"); | ||
296 | gus->revision = rev1 >> 4; | ||
297 | #ifndef SNDRV_STB | ||
298 | return 0; /* ok.. We have an InterWave board */ | ||
299 | #else | ||
300 | return snd_interwave_detect_stb(iwcard, gus, dev, rbus); | ||
301 | #endif | ||
302 | } | ||
303 | snd_printdd("[0x%lx] InterWave check - failed\n", gus->gf1.port); | ||
304 | return -ENODEV; | ||
305 | } | ||
306 | |||
307 | static irqreturn_t snd_interwave_interrupt(int irq, void *dev_id, struct pt_regs *regs) | ||
308 | { | ||
309 | struct snd_interwave *iwcard = (struct snd_interwave *) dev_id; | ||
310 | int loop, max = 5; | ||
311 | int handled = 0; | ||
312 | |||
313 | do { | ||
314 | loop = 0; | ||
315 | if (inb(iwcard->gus_status_reg)) { | ||
316 | handled = 1; | ||
317 | snd_gus_interrupt(irq, iwcard->gus, regs); | ||
318 | loop++; | ||
319 | } | ||
320 | if (inb(iwcard->pcm_status_reg) & 0x01) { /* IRQ bit is set? */ | ||
321 | handled = 1; | ||
322 | snd_cs4231_interrupt(irq, iwcard->cs4231, regs); | ||
323 | loop++; | ||
324 | } | ||
325 | } while (loop && --max > 0); | ||
326 | return IRQ_RETVAL(handled); | ||
327 | } | ||
328 | |||
329 | static void __devinit snd_interwave_reset(snd_gus_card_t * gus) | ||
330 | { | ||
331 | snd_gf1_write8(gus, SNDRV_GF1_GB_RESET, 0x00); | ||
332 | udelay(160); | ||
333 | snd_gf1_write8(gus, SNDRV_GF1_GB_RESET, 0x01); | ||
334 | udelay(160); | ||
335 | } | ||
336 | |||
337 | static void __devinit snd_interwave_bank_sizes(snd_gus_card_t * gus, int *sizes) | ||
338 | { | ||
339 | unsigned int idx; | ||
340 | unsigned int local; | ||
341 | unsigned char d; | ||
342 | |||
343 | for (idx = 0; idx < 4; idx++) { | ||
344 | sizes[idx] = 0; | ||
345 | d = 0x55; | ||
346 | for (local = idx << 22; | ||
347 | local < (idx << 22) + 0x400000; | ||
348 | local += 0x40000, d++) { | ||
349 | snd_gf1_poke(gus, local, d); | ||
350 | snd_gf1_poke(gus, local + 1, d + 1); | ||
351 | #if 0 | ||
352 | printk("d = 0x%x, local = 0x%x, local + 1 = 0x%x, idx << 22 = 0x%x\n", | ||
353 | d, | ||
354 | snd_gf1_peek(gus, local), | ||
355 | snd_gf1_peek(gus, local + 1), | ||
356 | snd_gf1_peek(gus, idx << 22)); | ||
357 | #endif | ||
358 | if (snd_gf1_peek(gus, local) != d || | ||
359 | snd_gf1_peek(gus, local + 1) != d + 1 || | ||
360 | snd_gf1_peek(gus, idx << 22) != 0x55) | ||
361 | break; | ||
362 | sizes[idx]++; | ||
363 | } | ||
364 | } | ||
365 | #if 0 | ||
366 | printk("sizes: %i %i %i %i\n", sizes[0], sizes[1], sizes[2], sizes[3]); | ||
367 | #endif | ||
368 | } | ||
369 | |||
370 | struct rom_hdr { | ||
371 | /* 000 */ unsigned char iwave[8]; | ||
372 | /* 008 */ unsigned char rom_hdr_revision; | ||
373 | /* 009 */ unsigned char series_number; | ||
374 | /* 010 */ unsigned char series_name[16]; | ||
375 | /* 026 */ unsigned char date[10]; | ||
376 | /* 036 */ unsigned short vendor_revision_major; | ||
377 | /* 038 */ unsigned short vendor_revision_minor; | ||
378 | /* 040 */ unsigned int rom_size; | ||
379 | /* 044 */ unsigned char copyright[128]; | ||
380 | /* 172 */ unsigned char vendor_name[64]; | ||
381 | /* 236 */ unsigned char rom_description[128]; | ||
382 | /* 364 */ unsigned char pad[147]; | ||
383 | /* 511 */ unsigned char csum; | ||
384 | }; | ||
385 | |||
386 | static void __devinit snd_interwave_detect_memory(snd_gus_card_t * gus) | ||
387 | { | ||
388 | static unsigned int lmc[13] = | ||
389 | { | ||
390 | 0x00000001, 0x00000101, 0x01010101, 0x00000401, | ||
391 | 0x04040401, 0x00040101, 0x04040101, 0x00000004, | ||
392 | 0x00000404, 0x04040404, 0x00000010, 0x00001010, | ||
393 | 0x10101010 | ||
394 | }; | ||
395 | |||
396 | int bank_pos, pages; | ||
397 | unsigned int i, lmct; | ||
398 | int psizes[4]; | ||
399 | unsigned char iwave[8]; | ||
400 | unsigned char csum; | ||
401 | |||
402 | snd_interwave_reset(gus); | ||
403 | snd_gf1_write8(gus, SNDRV_GF1_GB_GLOBAL_MODE, snd_gf1_read8(gus, SNDRV_GF1_GB_GLOBAL_MODE) | 0x01); /* enhanced mode */ | ||
404 | snd_gf1_write8(gus, SNDRV_GF1_GB_MEMORY_CONTROL, 0x01); /* DRAM I/O cycles selected */ | ||
405 | snd_gf1_write16(gus, SNDRV_GF1_GW_MEMORY_CONFIG, (snd_gf1_look16(gus, SNDRV_GF1_GW_MEMORY_CONFIG) & 0xff10) | 0x004c); | ||
406 | /* ok.. simple test of memory size */ | ||
407 | pages = 0; | ||
408 | snd_gf1_poke(gus, 0, 0x55); | ||
409 | snd_gf1_poke(gus, 1, 0xaa); | ||
410 | #if 1 | ||
411 | if (snd_gf1_peek(gus, 0) == 0x55 && snd_gf1_peek(gus, 1) == 0xaa) | ||
412 | #else | ||
413 | if (0) /* ok.. for testing of 0k RAM */ | ||
414 | #endif | ||
415 | { | ||
416 | snd_interwave_bank_sizes(gus, psizes); | ||
417 | lmct = (psizes[3] << 24) | (psizes[2] << 16) | | ||
418 | (psizes[1] << 8) | psizes[0]; | ||
419 | #if 0 | ||
420 | printk("lmct = 0x%08x\n", lmct); | ||
421 | #endif | ||
422 | for (i = 0; i < ARRAY_SIZE(lmc); i++) | ||
423 | if (lmct == lmc[i]) { | ||
424 | #if 0 | ||
425 | printk("found !!! %i\n", i); | ||
426 | #endif | ||
427 | snd_gf1_write16(gus, SNDRV_GF1_GW_MEMORY_CONFIG, (snd_gf1_look16(gus, SNDRV_GF1_GW_MEMORY_CONFIG) & 0xfff0) | i); | ||
428 | snd_interwave_bank_sizes(gus, psizes); | ||
429 | break; | ||
430 | } | ||
431 | if (i >= ARRAY_SIZE(lmc) && !gus->gf1.enh_mode) | ||
432 | snd_gf1_write16(gus, SNDRV_GF1_GW_MEMORY_CONFIG, (snd_gf1_look16(gus, SNDRV_GF1_GW_MEMORY_CONFIG) & 0xfff0) | 2); | ||
433 | for (i = 0; i < 4; i++) { | ||
434 | gus->gf1.mem_alloc.banks_8[i].address = | ||
435 | gus->gf1.mem_alloc.banks_16[i].address = i << 22; | ||
436 | gus->gf1.mem_alloc.banks_8[i].size = | ||
437 | gus->gf1.mem_alloc.banks_16[i].size = psizes[i] << 18; | ||
438 | pages += psizes[i]; | ||
439 | } | ||
440 | } | ||
441 | pages <<= 18; | ||
442 | gus->gf1.memory = pages; | ||
443 | |||
444 | snd_gf1_write8(gus, SNDRV_GF1_GB_MEMORY_CONTROL, 0x03); /* select ROM */ | ||
445 | snd_gf1_write16(gus, SNDRV_GF1_GW_MEMORY_CONFIG, (snd_gf1_look16(gus, SNDRV_GF1_GW_MEMORY_CONFIG) & 0xff1f) | (4 << 5)); | ||
446 | gus->gf1.rom_banks = 0; | ||
447 | gus->gf1.rom_memory = 0; | ||
448 | for (bank_pos = 0; bank_pos < 16L * 1024L * 1024L; bank_pos += 4L * 1024L * 1024L) { | ||
449 | for (i = 0; i < 8; ++i) | ||
450 | iwave[i] = snd_gf1_peek(gus, bank_pos + i); | ||
451 | #ifdef CONFIG_SND_DEBUG_ROM | ||
452 | printk("ROM at 0x%06x = %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x\n", bank_pos, | ||
453 | iwave[0], iwave[1], iwave[2], iwave[3], | ||
454 | iwave[4], iwave[5], iwave[6], iwave[7]); | ||
455 | #endif | ||
456 | if (strncmp(iwave, "INTRWAVE", 8)) | ||
457 | continue; /* first check */ | ||
458 | csum = 0; | ||
459 | for (i = 0; i < sizeof(struct rom_hdr); i++) | ||
460 | csum += snd_gf1_peek(gus, bank_pos + i); | ||
461 | #ifdef CONFIG_SND_DEBUG_ROM | ||
462 | printk("ROM checksum = 0x%x (computed)\n", csum); | ||
463 | #endif | ||
464 | if (csum != 0) | ||
465 | continue; /* not valid rom */ | ||
466 | gus->gf1.rom_banks++; | ||
467 | gus->gf1.rom_present |= 1 << (bank_pos >> 22); | ||
468 | gus->gf1.rom_memory = snd_gf1_peek(gus, bank_pos + 40) | | ||
469 | (snd_gf1_peek(gus, bank_pos + 41) << 8) | | ||
470 | (snd_gf1_peek(gus, bank_pos + 42) << 16) | | ||
471 | (snd_gf1_peek(gus, bank_pos + 43) << 24); | ||
472 | } | ||
473 | #if 0 | ||
474 | if (gus->gf1.rom_memory > 0) { | ||
475 | if (gus->gf1.rom_banks == 1 && gus->gf1.rom_present == 8) | ||
476 | gus->card->type = SNDRV_CARD_TYPE_IW_DYNASONIC; | ||
477 | } | ||
478 | #endif | ||
479 | snd_gf1_write8(gus, SNDRV_GF1_GB_MEMORY_CONTROL, 0x00); /* select RAM */ | ||
480 | |||
481 | if (!gus->gf1.enh_mode) | ||
482 | snd_interwave_reset(gus); | ||
483 | } | ||
484 | |||
485 | static void __devinit snd_interwave_init(int dev, snd_gus_card_t * gus) | ||
486 | { | ||
487 | unsigned long flags; | ||
488 | |||
489 | /* ok.. some InterWave specific initialization */ | ||
490 | spin_lock_irqsave(&gus->reg_lock, flags); | ||
491 | snd_gf1_write8(gus, SNDRV_GF1_GB_SOUND_BLASTER_CONTROL, 0x00); | ||
492 | snd_gf1_write8(gus, SNDRV_GF1_GB_COMPATIBILITY, 0x1f); | ||
493 | snd_gf1_write8(gus, SNDRV_GF1_GB_DECODE_CONTROL, 0x49); | ||
494 | snd_gf1_write8(gus, SNDRV_GF1_GB_VERSION_NUMBER, 0x11); | ||
495 | snd_gf1_write8(gus, SNDRV_GF1_GB_MPU401_CONTROL_A, 0x00); | ||
496 | snd_gf1_write8(gus, SNDRV_GF1_GB_MPU401_CONTROL_B, 0x30); | ||
497 | snd_gf1_write8(gus, SNDRV_GF1_GB_EMULATION_IRQ, 0x00); | ||
498 | spin_unlock_irqrestore(&gus->reg_lock, flags); | ||
499 | gus->equal_irq = 1; | ||
500 | gus->codec_flag = 1; | ||
501 | gus->interwave = 1; | ||
502 | gus->max_flag = 1; | ||
503 | gus->joystick_dac = joystick_dac[dev]; | ||
504 | |||
505 | } | ||
506 | |||
507 | static snd_kcontrol_new_t snd_interwave_controls[] = { | ||
508 | CS4231_DOUBLE("Master Playback Switch", 0, CS4231_LINE_LEFT_OUTPUT, CS4231_LINE_RIGHT_OUTPUT, 7, 7, 1, 1), | ||
509 | CS4231_DOUBLE("Master Playback Volume", 0, CS4231_LINE_LEFT_OUTPUT, CS4231_LINE_RIGHT_OUTPUT, 0, 0, 31, 1), | ||
510 | CS4231_DOUBLE("Mic Playback Switch", 0, CS4231_LEFT_MIC_INPUT, CS4231_RIGHT_MIC_INPUT, 7, 7, 1, 1), | ||
511 | CS4231_DOUBLE("Mic Playback Volume", 0, CS4231_LEFT_MIC_INPUT, CS4231_RIGHT_MIC_INPUT, 0, 0, 31, 1) | ||
512 | }; | ||
513 | |||
514 | static int __devinit snd_interwave_mixer(cs4231_t *chip) | ||
515 | { | ||
516 | snd_card_t *card = chip->card; | ||
517 | snd_ctl_elem_id_t id1, id2; | ||
518 | unsigned int idx; | ||
519 | int err; | ||
520 | |||
521 | memset(&id1, 0, sizeof(id1)); | ||
522 | memset(&id2, 0, sizeof(id2)); | ||
523 | id1.iface = id2.iface = SNDRV_CTL_ELEM_IFACE_MIXER; | ||
524 | #if 0 | ||
525 | /* remove mono microphone controls */ | ||
526 | strcpy(id1.name, "Mic Playback Switch"); | ||
527 | if ((err = snd_ctl_remove_id(card, &id1)) < 0) | ||
528 | return err; | ||
529 | strcpy(id1.name, "Mic Playback Volume"); | ||
530 | if ((err = snd_ctl_remove_id(card, &id1)) < 0) | ||
531 | return err; | ||
532 | #endif | ||
533 | /* add new master and mic controls */ | ||
534 | for (idx = 0; idx < ARRAY_SIZE(snd_interwave_controls); idx++) | ||
535 | if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_interwave_controls[idx], chip))) < 0) | ||
536 | return err; | ||
537 | snd_cs4231_out(chip, CS4231_LINE_LEFT_OUTPUT, 0x9f); | ||
538 | snd_cs4231_out(chip, CS4231_LINE_RIGHT_OUTPUT, 0x9f); | ||
539 | snd_cs4231_out(chip, CS4231_LEFT_MIC_INPUT, 0x9f); | ||
540 | snd_cs4231_out(chip, CS4231_RIGHT_MIC_INPUT, 0x9f); | ||
541 | /* reassign AUXA to SYNTHESIZER */ | ||
542 | strcpy(id1.name, "Aux Playback Switch"); | ||
543 | strcpy(id2.name, "Synth Playback Switch"); | ||
544 | if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0) | ||
545 | return err; | ||
546 | strcpy(id1.name, "Aux Playback Volume"); | ||
547 | strcpy(id2.name, "Synth Playback Volume"); | ||
548 | if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0) | ||
549 | return err; | ||
550 | /* reassign AUXB to CD */ | ||
551 | strcpy(id1.name, "Aux Playback Switch"); id1.index = 1; | ||
552 | strcpy(id2.name, "CD Playback Switch"); | ||
553 | if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0) | ||
554 | return err; | ||
555 | strcpy(id1.name, "Aux Playback Volume"); | ||
556 | strcpy(id2.name, "CD Playback Volume"); | ||
557 | if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0) | ||
558 | return err; | ||
559 | return 0; | ||
560 | } | ||
561 | |||
562 | #ifdef CONFIG_PNP | ||
563 | |||
564 | static int __devinit snd_interwave_pnp(int dev, struct snd_interwave *iwcard, | ||
565 | struct pnp_card_link *card, | ||
566 | const struct pnp_card_device_id *id) | ||
567 | { | ||
568 | struct pnp_dev *pdev; | ||
569 | struct pnp_resource_table * cfg = kmalloc(sizeof(struct pnp_resource_table), GFP_KERNEL); | ||
570 | int err; | ||
571 | |||
572 | iwcard->dev = pnp_request_card_device(card, id->devs[0].id, NULL); | ||
573 | if (iwcard->dev == NULL) { | ||
574 | kfree(cfg); | ||
575 | return -EBUSY; | ||
576 | } | ||
577 | #ifdef SNDRV_STB | ||
578 | iwcard->devtc = pnp_request_card_device(card, id->devs[1].id, NULL); | ||
579 | if (iwcard->devtc == NULL) { | ||
580 | kfree(cfg); | ||
581 | return -EBUSY; | ||
582 | } | ||
583 | #endif | ||
584 | /* Synth & Codec initialization */ | ||
585 | pdev = iwcard->dev; | ||
586 | pnp_init_resource_table(cfg); | ||
587 | if (port[dev] != SNDRV_AUTO_PORT) { | ||
588 | pnp_resource_change(&cfg->port_resource[0], port[dev], 16); | ||
589 | pnp_resource_change(&cfg->port_resource[1], port[dev] + 0x100, 12); | ||
590 | pnp_resource_change(&cfg->port_resource[2], port[dev] + 0x10c, 4); | ||
591 | } | ||
592 | if (dma1[dev] != SNDRV_AUTO_DMA) | ||
593 | pnp_resource_change(&cfg->dma_resource[0], dma1[dev], 1); | ||
594 | if (dma2[dev] != SNDRV_AUTO_DMA) | ||
595 | pnp_resource_change(&cfg->dma_resource[1], dma2[dev], 1); | ||
596 | if (dma2[dev] < 0) | ||
597 | pnp_resource_change(&cfg->dma_resource[1], 4, 1); | ||
598 | if (irq[dev] != SNDRV_AUTO_IRQ) | ||
599 | pnp_resource_change(&cfg->irq_resource[0], irq[dev], 1); | ||
600 | if (pnp_manual_config_dev(pdev, cfg, 0) < 0) | ||
601 | snd_printk(KERN_ERR "InterWave - Synth - the requested resources are invalid, using auto config\n"); | ||
602 | err = pnp_activate_dev(pdev); | ||
603 | if (err < 0) { | ||
604 | kfree(cfg); | ||
605 | snd_printk(KERN_ERR "InterWave PnP configure failure (out of resources?)\n"); | ||
606 | return err; | ||
607 | } | ||
608 | if (pnp_port_start(pdev, 0) + 0x100 != pnp_port_start(pdev, 1) || | ||
609 | pnp_port_start(pdev, 0) + 0x10c != pnp_port_start(pdev, 2)) { | ||
610 | kfree(cfg); | ||
611 | snd_printk(KERN_ERR "PnP configure failure (wrong ports)\n"); | ||
612 | return -ENOENT; | ||
613 | } | ||
614 | port[dev] = pnp_port_start(pdev, 0); | ||
615 | dma1[dev] = pnp_dma(pdev, 0); | ||
616 | if (dma2[dev] >= 0) | ||
617 | dma2[dev] = pnp_dma(pdev, 1); | ||
618 | irq[dev] = pnp_irq(pdev, 0); | ||
619 | snd_printdd("isapnp IW: sb port=0x%lx, gf1 port=0x%lx, codec port=0x%lx\n", | ||
620 | pnp_port_start(pdev, 0), | ||
621 | pnp_port_start(pdev, 1), | ||
622 | pnp_port_start(pdev, 2)); | ||
623 | snd_printdd("isapnp IW: dma1=%i, dma2=%i, irq=%i\n", dma1[dev], dma2[dev], irq[dev]); | ||
624 | #ifdef SNDRV_STB | ||
625 | /* Tone Control initialization */ | ||
626 | pdev = iwcard->devtc; | ||
627 | pnp_init_resource_table(cfg); | ||
628 | if (port_tc[dev] != SNDRV_AUTO_PORT) | ||
629 | pnp_resource_change(&cfg->port_resource[0], port_tc[dev], 1); | ||
630 | if (pnp_manual_config_dev(pdev, cfg, 0) < 0) | ||
631 | snd_printk(KERN_ERR "InterWave - ToneControl - the requested resources are invalid, using auto config\n"); | ||
632 | err = pnp_activate_dev(pdev); | ||
633 | if (err < 0) { | ||
634 | kfree(cfg); | ||
635 | snd_printk(KERN_ERR "InterWave ToneControl PnP configure failure (out of resources?)\n"); | ||
636 | return err; | ||
637 | } | ||
638 | port_tc[dev] = pnp_port_start(pdev, 0); | ||
639 | snd_printdd("isapnp IW: tone control port=0x%lx\n", port_tc[dev]); | ||
640 | #endif | ||
641 | kfree(cfg); | ||
642 | return 0; | ||
643 | } | ||
644 | #endif /* CONFIG_PNP */ | ||
645 | |||
646 | static void snd_interwave_free(snd_card_t *card) | ||
647 | { | ||
648 | struct snd_interwave *iwcard = (struct snd_interwave *)card->private_data; | ||
649 | |||
650 | if (iwcard == NULL) | ||
651 | return; | ||
652 | #ifdef SNDRV_STB | ||
653 | if (iwcard->i2c_res) { | ||
654 | release_resource(iwcard->i2c_res); | ||
655 | kfree_nocheck(iwcard->i2c_res); | ||
656 | } | ||
657 | #endif | ||
658 | if (iwcard->irq >= 0) | ||
659 | free_irq(iwcard->irq, (void *)iwcard); | ||
660 | } | ||
661 | |||
662 | static int __devinit snd_interwave_probe(int dev, struct pnp_card_link *pcard, | ||
663 | const struct pnp_card_device_id *pid) | ||
664 | { | ||
665 | static int possible_irqs[] = {5, 11, 12, 9, 7, 15, 3, -1}; | ||
666 | static int possible_dmas[] = {0, 1, 3, 5, 6, 7, -1}; | ||
667 | int xirq, xdma1, xdma2; | ||
668 | snd_card_t *card; | ||
669 | struct snd_interwave *iwcard; | ||
670 | cs4231_t *cs4231; | ||
671 | snd_gus_card_t *gus; | ||
672 | #ifdef SNDRV_STB | ||
673 | snd_i2c_bus_t *i2c_bus; | ||
674 | #endif | ||
675 | snd_pcm_t *pcm; | ||
676 | char *str; | ||
677 | int err; | ||
678 | |||
679 | card = snd_card_new(index[dev], id[dev], THIS_MODULE, | ||
680 | sizeof(struct snd_interwave)); | ||
681 | if (card == NULL) | ||
682 | return -ENOMEM; | ||
683 | iwcard = (struct snd_interwave *)card->private_data; | ||
684 | iwcard->card = card; | ||
685 | iwcard->irq = -1; | ||
686 | card->private_free = snd_interwave_free; | ||
687 | #ifdef CONFIG_PNP | ||
688 | if (isapnp[dev]) { | ||
689 | if (snd_interwave_pnp(dev, iwcard, pcard, pid)) { | ||
690 | snd_card_free(card); | ||
691 | return -ENODEV; | ||
692 | } | ||
693 | snd_card_set_dev(card, &pcard->card->dev); | ||
694 | } | ||
695 | #endif | ||
696 | xirq = irq[dev]; | ||
697 | if (xirq == SNDRV_AUTO_IRQ) { | ||
698 | if ((xirq = snd_legacy_find_free_irq(possible_irqs)) < 0) { | ||
699 | snd_card_free(card); | ||
700 | snd_printk("unable to find a free IRQ\n"); | ||
701 | return -EBUSY; | ||
702 | } | ||
703 | } | ||
704 | xdma1 = dma1[dev]; | ||
705 | if (xdma1 == SNDRV_AUTO_DMA) { | ||
706 | if ((xdma1 = snd_legacy_find_free_dma(possible_dmas)) < 0) { | ||
707 | snd_card_free(card); | ||
708 | snd_printk("unable to find a free DMA1\n"); | ||
709 | return -EBUSY; | ||
710 | } | ||
711 | } | ||
712 | xdma2 = dma2[dev]; | ||
713 | if (xdma2 == SNDRV_AUTO_DMA) { | ||
714 | if ((xdma2 = snd_legacy_find_free_dma(possible_dmas)) < 0) { | ||
715 | snd_card_free(card); | ||
716 | snd_printk("unable to find a free DMA2\n"); | ||
717 | return -EBUSY; | ||
718 | } | ||
719 | } | ||
720 | |||
721 | if ((err = snd_gus_create(card, | ||
722 | port[dev], | ||
723 | -xirq, xdma1, xdma2, | ||
724 | 0, 32, | ||
725 | pcm_channels[dev], effect[dev], &gus)) < 0) { | ||
726 | snd_card_free(card); | ||
727 | return err; | ||
728 | } | ||
729 | if ((err = snd_interwave_detect(iwcard, gus, dev | ||
730 | #ifdef SNDRV_STB | ||
731 | , &i2c_bus | ||
732 | #endif | ||
733 | )) < 0) { | ||
734 | snd_card_free(card); | ||
735 | return err; | ||
736 | } | ||
737 | iwcard->gus_status_reg = gus->gf1.reg_irqstat; | ||
738 | iwcard->pcm_status_reg = gus->gf1.port + 0x10c + 2; | ||
739 | |||
740 | snd_interwave_init(dev, gus); | ||
741 | snd_interwave_detect_memory(gus); | ||
742 | if ((err = snd_gus_initialize(gus)) < 0) { | ||
743 | snd_card_free(card); | ||
744 | return err; | ||
745 | } | ||
746 | |||
747 | if (request_irq(xirq, snd_interwave_interrupt, SA_INTERRUPT, "InterWave", (void *)iwcard)) { | ||
748 | snd_card_free(card); | ||
749 | snd_printk("unable to grab IRQ %d\n", xirq); | ||
750 | return -EBUSY; | ||
751 | } | ||
752 | iwcard->irq = xirq; | ||
753 | |||
754 | if ((err = snd_cs4231_create(card, | ||
755 | gus->gf1.port + 0x10c, -1, xirq, | ||
756 | xdma2 < 0 ? xdma1 : xdma2, xdma1, | ||
757 | CS4231_HW_INTERWAVE, | ||
758 | CS4231_HWSHARE_IRQ | | ||
759 | CS4231_HWSHARE_DMA1 | | ||
760 | CS4231_HWSHARE_DMA2, | ||
761 | &cs4231)) < 0) { | ||
762 | snd_card_free(card); | ||
763 | return err; | ||
764 | } | ||
765 | if ((err = snd_cs4231_pcm(cs4231, 0, &pcm)) < 0) { | ||
766 | snd_card_free(card); | ||
767 | return err; | ||
768 | } | ||
769 | sprintf(pcm->name + strlen(pcm->name), " rev %c", gus->revision + 'A'); | ||
770 | strcat(pcm->name, " (codec)"); | ||
771 | if ((err = snd_cs4231_timer(cs4231, 2, NULL)) < 0) { | ||
772 | snd_card_free(card); | ||
773 | return err; | ||
774 | } | ||
775 | if ((err = snd_cs4231_mixer(cs4231)) < 0) { | ||
776 | snd_card_free(card); | ||
777 | return err; | ||
778 | } | ||
779 | if (pcm_channels[dev] > 0) { | ||
780 | if ((err = snd_gf1_pcm_new(gus, 1, 1, NULL)) < 0) { | ||
781 | snd_card_free(card); | ||
782 | return err; | ||
783 | } | ||
784 | } | ||
785 | if ((err = snd_interwave_mixer(cs4231)) < 0) { | ||
786 | snd_card_free(card); | ||
787 | return err; | ||
788 | } | ||
789 | #ifdef SNDRV_STB | ||
790 | { | ||
791 | snd_ctl_elem_id_t id1, id2; | ||
792 | memset(&id1, 0, sizeof(id1)); | ||
793 | memset(&id2, 0, sizeof(id2)); | ||
794 | id1.iface = id2.iface = SNDRV_CTL_ELEM_IFACE_MIXER; | ||
795 | strcpy(id1.name, "Master Playback Switch"); | ||
796 | strcpy(id2.name, id1.name); | ||
797 | id2.index = 1; | ||
798 | if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0) { | ||
799 | snd_card_free(card); | ||
800 | return err; | ||
801 | } | ||
802 | strcpy(id1.name, "Master Playback Volume"); | ||
803 | strcpy(id2.name, id1.name); | ||
804 | if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0) { | ||
805 | snd_card_free(card); | ||
806 | return err; | ||
807 | } | ||
808 | if ((err = snd_tea6330t_update_mixer(card, i2c_bus, 0, 1)) < 0) { | ||
809 | snd_card_free(card); | ||
810 | return err; | ||
811 | } | ||
812 | } | ||
813 | #endif | ||
814 | |||
815 | gus->uart_enable = midi[dev]; | ||
816 | if ((err = snd_gf1_rawmidi_new(gus, 0, NULL)) < 0) { | ||
817 | snd_card_free(card); | ||
818 | return err; | ||
819 | } | ||
820 | |||
821 | #ifndef SNDRV_STB | ||
822 | str = "AMD InterWave"; | ||
823 | if (gus->gf1.rom_banks == 1 && gus->gf1.rom_present == 8) | ||
824 | str = "Dynasonic 3-D"; | ||
825 | #else | ||
826 | str = "InterWave STB"; | ||
827 | #endif | ||
828 | strcpy(card->driver, str); | ||
829 | strcpy(card->shortname, str); | ||
830 | sprintf(card->longname, "%s at 0x%lx, irq %i, dma %d", | ||
831 | str, | ||
832 | gus->gf1.port, | ||
833 | xirq, | ||
834 | xdma1); | ||
835 | if (xdma2 >= 0) | ||
836 | sprintf(card->longname + strlen(card->longname), "&%d", xdma2); | ||
837 | |||
838 | if ((err = snd_card_register(card)) < 0) { | ||
839 | snd_card_free(card); | ||
840 | return err; | ||
841 | } | ||
842 | |||
843 | iwcard->cs4231 = cs4231; | ||
844 | iwcard->gus = gus; | ||
845 | if (pcard) | ||
846 | pnp_set_card_drvdata(pcard, card); | ||
847 | else | ||
848 | snd_interwave_legacy[dev++] = card; | ||
849 | return 0; | ||
850 | } | ||
851 | |||
852 | static int __devinit snd_interwave_probe_legacy_port(unsigned long xport) | ||
853 | { | ||
854 | static int dev; | ||
855 | int res; | ||
856 | |||
857 | for ( ; dev < SNDRV_CARDS; dev++) { | ||
858 | if (!enable[dev] || port[dev] != SNDRV_AUTO_PORT) | ||
859 | continue; | ||
860 | #ifdef CONFIG_PNP | ||
861 | if (isapnp[dev]) | ||
862 | continue; | ||
863 | #endif | ||
864 | port[dev] = xport; | ||
865 | res = snd_interwave_probe(dev, NULL, NULL); | ||
866 | if (res < 0) | ||
867 | port[dev] = SNDRV_AUTO_PORT; | ||
868 | return res; | ||
869 | } | ||
870 | return -ENODEV; | ||
871 | } | ||
872 | |||
873 | #ifdef CONFIG_PNP | ||
874 | |||
875 | static int __devinit snd_interwave_pnp_detect(struct pnp_card_link *card, | ||
876 | const struct pnp_card_device_id *id) | ||
877 | { | ||
878 | static int dev; | ||
879 | int res; | ||
880 | |||
881 | for ( ; dev < SNDRV_CARDS; dev++) { | ||
882 | if (!enable[dev] || !isapnp[dev]) | ||
883 | continue; | ||
884 | res = snd_interwave_probe(dev, card, id); | ||
885 | if (res < 0) | ||
886 | return res; | ||
887 | dev++; | ||
888 | return 0; | ||
889 | } | ||
890 | |||
891 | return -ENODEV; | ||
892 | } | ||
893 | |||
894 | static void __devexit snd_interwave_pnp_remove(struct pnp_card_link * pcard) | ||
895 | { | ||
896 | snd_card_t *card = (snd_card_t *) pnp_get_card_drvdata(pcard); | ||
897 | |||
898 | snd_card_disconnect(card); | ||
899 | snd_card_free_in_thread(card); | ||
900 | } | ||
901 | |||
902 | static struct pnp_card_driver interwave_pnpc_driver = { | ||
903 | .flags = PNP_DRIVER_RES_DISABLE, | ||
904 | .name = "interwave", | ||
905 | .id_table = snd_interwave_pnpids, | ||
906 | .probe = snd_interwave_pnp_detect, | ||
907 | .remove = __devexit_p(snd_interwave_pnp_remove), | ||
908 | }; | ||
909 | |||
910 | #endif /* CONFIG_PNP */ | ||
911 | |||
912 | static int __init alsa_card_interwave_init(void) | ||
913 | { | ||
914 | int cards = 0, i; | ||
915 | static long possible_ports[] = {0x210, 0x220, 0x230, 0x240, 0x250, 0x260, -1}; | ||
916 | int dev; | ||
917 | |||
918 | for (dev = 0; dev < SNDRV_CARDS; dev++) { | ||
919 | if (!enable[dev] || port[dev] == SNDRV_AUTO_PORT) | ||
920 | continue; | ||
921 | #ifdef CONFIG_PNP | ||
922 | if (isapnp[dev]) | ||
923 | continue; | ||
924 | #endif | ||
925 | if (!snd_interwave_probe(dev, NULL, NULL)) { | ||
926 | cards++; | ||
927 | continue; | ||
928 | } | ||
929 | #ifdef MODULE | ||
930 | printk(KERN_ERR "InterWave soundcard #%i not found at 0x%lx or device busy\n", dev, port[dev]); | ||
931 | #endif | ||
932 | } | ||
933 | /* legacy auto configured cards */ | ||
934 | i = snd_legacy_auto_probe(possible_ports, snd_interwave_probe_legacy_port); | ||
935 | if (i > 0) | ||
936 | cards += i; | ||
937 | #ifdef CONFIG_PNP | ||
938 | /* ISA PnP cards */ | ||
939 | i = pnp_register_card_driver(&interwave_pnpc_driver); | ||
940 | if (i > 0) | ||
941 | cards += i; | ||
942 | #endif | ||
943 | |||
944 | if (!cards) { | ||
945 | #ifdef CONFIG_PNP | ||
946 | pnp_unregister_card_driver(&interwave_pnpc_driver); | ||
947 | #endif | ||
948 | #ifdef MODULE | ||
949 | printk(KERN_ERR "InterWave soundcard not found or device busy\n"); | ||
950 | #endif | ||
951 | return -ENODEV; | ||
952 | } | ||
953 | return 0; | ||
954 | } | ||
955 | |||
956 | static void __exit alsa_card_interwave_exit(void) | ||
957 | { | ||
958 | int dev; | ||
959 | |||
960 | #ifdef CONFIG_PNP | ||
961 | /* PnP cards first */ | ||
962 | pnp_unregister_card_driver(&interwave_pnpc_driver); | ||
963 | #endif | ||
964 | for (dev = 0; dev < SNDRV_CARDS; dev++) | ||
965 | snd_card_free(snd_interwave_legacy[dev]); | ||
966 | } | ||
967 | |||
968 | module_init(alsa_card_interwave_init) | ||
969 | module_exit(alsa_card_interwave_exit) | ||