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/gus_pcm.c |
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/gus_pcm.c')
-rw-r--r-- | sound/isa/gus/gus_pcm.c | 903 |
1 files changed, 903 insertions, 0 deletions
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 | |||