1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * Copyright (c) by Jaroslav Kysela <perex@perex.cz> |
4 | * Routines for control of GF1 chip (PCM things) |
5 | * |
6 | * InterWave chips supports interleaved DMA, but this feature isn't used in |
7 | * this code. |
8 | * |
9 | * This code emulates autoinit DMA transfer for playback, recording by GF1 |
10 | * chip doesn't support autoinit DMA. |
11 | */ |
12 | |
13 | #include <asm/dma.h> |
14 | #include <linux/slab.h> |
15 | #include <linux/sched/signal.h> |
16 | |
17 | #include <sound/core.h> |
18 | #include <sound/control.h> |
19 | #include <sound/gus.h> |
20 | #include <sound/pcm_params.h> |
21 | #include "gus_tables.h" |
22 | |
23 | /* maximum rate */ |
24 | |
25 | #define SNDRV_GF1_PCM_RATE 48000 |
26 | |
27 | #define SNDRV_GF1_PCM_PFLG_NONE 0 |
28 | #define SNDRV_GF1_PCM_PFLG_ACTIVE (1<<0) |
29 | #define SNDRV_GF1_PCM_PFLG_NEUTRAL (2<<0) |
30 | |
31 | struct gus_pcm_private { |
32 | struct snd_gus_card * gus; |
33 | struct snd_pcm_substream *substream; |
34 | spinlock_t lock; |
35 | unsigned int voices; |
36 | struct snd_gus_voice *pvoices[2]; |
37 | unsigned int memory; |
38 | unsigned short flags; |
39 | unsigned char voice_ctrl, ramp_ctrl; |
40 | unsigned int bpos; |
41 | unsigned int blocks; |
42 | unsigned int block_size; |
43 | unsigned int dma_size; |
44 | wait_queue_head_t sleep; |
45 | atomic_t dma_count; |
46 | int final_volume; |
47 | }; |
48 | |
49 | static void snd_gf1_pcm_block_change_ack(struct snd_gus_card * gus, void *private_data) |
50 | { |
51 | struct gus_pcm_private *pcmp = private_data; |
52 | |
53 | if (pcmp) { |
54 | atomic_dec(v: &pcmp->dma_count); |
55 | wake_up(&pcmp->sleep); |
56 | } |
57 | } |
58 | |
59 | static int snd_gf1_pcm_block_change(struct snd_pcm_substream *substream, |
60 | unsigned int offset, |
61 | unsigned int addr, |
62 | unsigned int count) |
63 | { |
64 | struct snd_gf1_dma_block block; |
65 | struct snd_pcm_runtime *runtime = substream->runtime; |
66 | struct gus_pcm_private *pcmp = runtime->private_data; |
67 | |
68 | count += offset & 31; |
69 | offset &= ~31; |
70 | /* |
71 | snd_printk(KERN_DEBUG "block change - offset = 0x%x, count = 0x%x\n", |
72 | offset, count); |
73 | */ |
74 | memset(&block, 0, sizeof(block)); |
75 | block.cmd = SNDRV_GF1_DMA_IRQ; |
76 | if (snd_pcm_format_unsigned(format: runtime->format)) |
77 | block.cmd |= SNDRV_GF1_DMA_UNSIGNED; |
78 | if (snd_pcm_format_width(format: runtime->format) == 16) |
79 | block.cmd |= SNDRV_GF1_DMA_16BIT; |
80 | block.addr = addr & ~31; |
81 | block.buffer = runtime->dma_area + offset; |
82 | block.buf_addr = runtime->dma_addr + offset; |
83 | block.count = count; |
84 | block.private_data = pcmp; |
85 | block.ack = snd_gf1_pcm_block_change_ack; |
86 | if (!snd_gf1_dma_transfer_block(gus: pcmp->gus, block: &block, atomic: 0, synth: 0)) |
87 | atomic_inc(v: &pcmp->dma_count); |
88 | return 0; |
89 | } |
90 | |
91 | static void snd_gf1_pcm_trigger_up(struct snd_pcm_substream *substream) |
92 | { |
93 | struct snd_pcm_runtime *runtime = substream->runtime; |
94 | struct gus_pcm_private *pcmp = runtime->private_data; |
95 | struct snd_gus_card * gus = pcmp->gus; |
96 | unsigned long flags; |
97 | unsigned char voice_ctrl, ramp_ctrl; |
98 | unsigned short rate; |
99 | unsigned int curr, begin, end; |
100 | unsigned short vol; |
101 | unsigned char pan; |
102 | unsigned int voice; |
103 | |
104 | spin_lock_irqsave(&pcmp->lock, flags); |
105 | if (pcmp->flags & SNDRV_GF1_PCM_PFLG_ACTIVE) { |
106 | spin_unlock_irqrestore(lock: &pcmp->lock, flags); |
107 | return; |
108 | } |
109 | pcmp->flags |= SNDRV_GF1_PCM_PFLG_ACTIVE; |
110 | pcmp->final_volume = 0; |
111 | spin_unlock_irqrestore(lock: &pcmp->lock, flags); |
112 | rate = snd_gf1_translate_freq(gus, freq2: runtime->rate << 4); |
113 | /* enable WAVE IRQ */ |
114 | voice_ctrl = snd_pcm_format_width(format: runtime->format) == 16 ? 0x24 : 0x20; |
115 | /* enable RAMP IRQ + rollover */ |
116 | ramp_ctrl = 0x24; |
117 | if (pcmp->blocks == 1) { |
118 | voice_ctrl |= 0x08; /* loop enable */ |
119 | ramp_ctrl &= ~0x04; /* disable rollover */ |
120 | } |
121 | for (voice = 0; voice < pcmp->voices; voice++) { |
122 | begin = pcmp->memory + voice * (pcmp->dma_size / runtime->channels); |
123 | curr = begin + (pcmp->bpos * pcmp->block_size) / runtime->channels; |
124 | end = curr + (pcmp->block_size / runtime->channels); |
125 | end -= snd_pcm_format_width(format: runtime->format) == 16 ? 2 : 1; |
126 | /* |
127 | snd_printk(KERN_DEBUG "init: curr=0x%x, begin=0x%x, end=0x%x, " |
128 | "ctrl=0x%x, ramp=0x%x, rate=0x%x\n", |
129 | curr, begin, end, voice_ctrl, ramp_ctrl, rate); |
130 | */ |
131 | pan = runtime->channels == 2 ? (!voice ? 1 : 14) : 8; |
132 | vol = !voice ? gus->gf1.pcm_volume_level_left : gus->gf1.pcm_volume_level_right; |
133 | spin_lock_irqsave(&gus->reg_lock, flags); |
134 | snd_gf1_select_voice(gus, voice: pcmp->pvoices[voice]->number); |
135 | snd_gf1_write8(gus, SNDRV_GF1_VB_PAN, data: pan); |
136 | snd_gf1_write16(gus, SNDRV_GF1_VW_FREQUENCY, data: rate); |
137 | snd_gf1_write_addr(gus, SNDRV_GF1_VA_START, addr: begin << 4, w_16bit: voice_ctrl & 4); |
138 | snd_gf1_write_addr(gus, SNDRV_GF1_VA_END, addr: end << 4, w_16bit: voice_ctrl & 4); |
139 | snd_gf1_write_addr(gus, SNDRV_GF1_VA_CURRENT, addr: curr << 4, w_16bit: voice_ctrl & 4); |
140 | snd_gf1_write16(gus, SNDRV_GF1_VW_VOLUME, SNDRV_GF1_MIN_VOLUME << 4); |
141 | snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_RATE, data: 0x2f); |
142 | snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_START, SNDRV_GF1_MIN_OFFSET); |
143 | snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_END, data: vol >> 8); |
144 | snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_CONTROL, data: ramp_ctrl); |
145 | if (!gus->gf1.enh_mode) { |
146 | snd_gf1_delay(gus); |
147 | snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_CONTROL, data: ramp_ctrl); |
148 | } |
149 | spin_unlock_irqrestore(lock: &gus->reg_lock, flags); |
150 | } |
151 | spin_lock_irqsave(&gus->reg_lock, flags); |
152 | for (voice = 0; voice < pcmp->voices; voice++) { |
153 | snd_gf1_select_voice(gus, voice: pcmp->pvoices[voice]->number); |
154 | if (gus->gf1.enh_mode) |
155 | snd_gf1_write8(gus, SNDRV_GF1_VB_MODE, data: 0x00); /* deactivate voice */ |
156 | snd_gf1_write8(gus, SNDRV_GF1_VB_ADDRESS_CONTROL, data: voice_ctrl); |
157 | voice_ctrl &= ~0x20; |
158 | } |
159 | voice_ctrl |= 0x20; |
160 | if (!gus->gf1.enh_mode) { |
161 | snd_gf1_delay(gus); |
162 | for (voice = 0; voice < pcmp->voices; voice++) { |
163 | snd_gf1_select_voice(gus, voice: pcmp->pvoices[voice]->number); |
164 | snd_gf1_write8(gus, SNDRV_GF1_VB_ADDRESS_CONTROL, data: voice_ctrl); |
165 | voice_ctrl &= ~0x20; /* disable IRQ for next voice */ |
166 | } |
167 | } |
168 | spin_unlock_irqrestore(lock: &gus->reg_lock, flags); |
169 | } |
170 | |
171 | static void snd_gf1_pcm_interrupt_wave(struct snd_gus_card * gus, |
172 | struct snd_gus_voice *pvoice) |
173 | { |
174 | struct gus_pcm_private * pcmp; |
175 | struct snd_pcm_runtime *runtime; |
176 | unsigned char voice_ctrl, ramp_ctrl; |
177 | unsigned int idx; |
178 | unsigned int end, step; |
179 | |
180 | if (!pvoice->private_data) { |
181 | snd_printd("snd_gf1_pcm: unknown wave irq?\n" ); |
182 | snd_gf1_smart_stop_voice(gus, voice: pvoice->number); |
183 | return; |
184 | } |
185 | pcmp = pvoice->private_data; |
186 | if (pcmp == NULL) { |
187 | snd_printd("snd_gf1_pcm: unknown wave irq?\n" ); |
188 | snd_gf1_smart_stop_voice(gus, voice: pvoice->number); |
189 | return; |
190 | } |
191 | gus = pcmp->gus; |
192 | runtime = pcmp->substream->runtime; |
193 | |
194 | spin_lock(lock: &gus->reg_lock); |
195 | snd_gf1_select_voice(gus, voice: pvoice->number); |
196 | voice_ctrl = snd_gf1_read8(gus, SNDRV_GF1_VB_ADDRESS_CONTROL) & ~0x8b; |
197 | ramp_ctrl = (snd_gf1_read8(gus, SNDRV_GF1_VB_VOLUME_CONTROL) & ~0xa4) | 0x03; |
198 | #if 0 |
199 | snd_gf1_select_voice(gus, pvoice->number); |
200 | printk(KERN_DEBUG "position = 0x%x\n" , |
201 | (snd_gf1_read_addr(gus, SNDRV_GF1_VA_CURRENT, voice_ctrl & 4) >> 4)); |
202 | snd_gf1_select_voice(gus, pcmp->pvoices[1]->number); |
203 | printk(KERN_DEBUG "position = 0x%x\n" , |
204 | (snd_gf1_read_addr(gus, SNDRV_GF1_VA_CURRENT, voice_ctrl & 4) >> 4)); |
205 | snd_gf1_select_voice(gus, pvoice->number); |
206 | #endif |
207 | pcmp->bpos++; |
208 | pcmp->bpos %= pcmp->blocks; |
209 | if (pcmp->bpos + 1 >= pcmp->blocks) { /* last block? */ |
210 | voice_ctrl |= 0x08; /* enable loop */ |
211 | } else { |
212 | ramp_ctrl |= 0x04; /* enable rollover */ |
213 | } |
214 | end = pcmp->memory + (((pcmp->bpos + 1) * pcmp->block_size) / runtime->channels); |
215 | end -= voice_ctrl & 4 ? 2 : 1; |
216 | step = pcmp->dma_size / runtime->channels; |
217 | voice_ctrl |= 0x20; |
218 | if (!pcmp->final_volume) { |
219 | ramp_ctrl |= 0x20; |
220 | ramp_ctrl &= ~0x03; |
221 | } |
222 | for (idx = 0; idx < pcmp->voices; idx++, end += step) { |
223 | snd_gf1_select_voice(gus, voice: pcmp->pvoices[idx]->number); |
224 | snd_gf1_write_addr(gus, SNDRV_GF1_VA_END, addr: end << 4, w_16bit: voice_ctrl & 4); |
225 | snd_gf1_write8(gus, SNDRV_GF1_VB_ADDRESS_CONTROL, data: voice_ctrl); |
226 | snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_CONTROL, data: ramp_ctrl); |
227 | voice_ctrl &= ~0x20; |
228 | } |
229 | if (!gus->gf1.enh_mode) { |
230 | snd_gf1_delay(gus); |
231 | voice_ctrl |= 0x20; |
232 | for (idx = 0; idx < pcmp->voices; idx++) { |
233 | snd_gf1_select_voice(gus, voice: pcmp->pvoices[idx]->number); |
234 | snd_gf1_write8(gus, SNDRV_GF1_VB_ADDRESS_CONTROL, data: voice_ctrl); |
235 | snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_CONTROL, data: ramp_ctrl); |
236 | voice_ctrl &= ~0x20; |
237 | } |
238 | } |
239 | spin_unlock(lock: &gus->reg_lock); |
240 | |
241 | snd_pcm_period_elapsed(substream: pcmp->substream); |
242 | #if 0 |
243 | if ((runtime->flags & SNDRV_PCM_FLG_MMAP) && |
244 | *runtime->state == SNDRV_PCM_STATE_RUNNING) { |
245 | end = pcmp->bpos * pcmp->block_size; |
246 | if (runtime->channels > 1) { |
247 | snd_gf1_pcm_block_change(pcmp->substream, end, pcmp->memory + (end / 2), pcmp->block_size / 2); |
248 | snd_gf1_pcm_block_change(pcmp->substream, end + (pcmp->block_size / 2), pcmp->memory + (pcmp->dma_size / 2) + (end / 2), pcmp->block_size / 2); |
249 | } else { |
250 | snd_gf1_pcm_block_change(pcmp->substream, end, pcmp->memory + end, pcmp->block_size); |
251 | } |
252 | } |
253 | #endif |
254 | } |
255 | |
256 | static void snd_gf1_pcm_interrupt_volume(struct snd_gus_card * gus, |
257 | struct snd_gus_voice * pvoice) |
258 | { |
259 | unsigned short vol; |
260 | int cvoice; |
261 | struct gus_pcm_private *pcmp = pvoice->private_data; |
262 | |
263 | /* stop ramp, but leave rollover bit untouched */ |
264 | spin_lock(lock: &gus->reg_lock); |
265 | snd_gf1_select_voice(gus, voice: pvoice->number); |
266 | snd_gf1_ctrl_stop(gus, SNDRV_GF1_VB_VOLUME_CONTROL); |
267 | spin_unlock(lock: &gus->reg_lock); |
268 | if (pcmp == NULL) |
269 | return; |
270 | /* are we active? */ |
271 | if (!(pcmp->flags & SNDRV_GF1_PCM_PFLG_ACTIVE)) |
272 | return; |
273 | /* load real volume - better precision */ |
274 | cvoice = pcmp->pvoices[0] == pvoice ? 0 : 1; |
275 | if (pcmp->substream == NULL) |
276 | return; |
277 | vol = !cvoice ? gus->gf1.pcm_volume_level_left : gus->gf1.pcm_volume_level_right; |
278 | spin_lock(lock: &gus->reg_lock); |
279 | snd_gf1_select_voice(gus, voice: pvoice->number); |
280 | snd_gf1_write16(gus, SNDRV_GF1_VW_VOLUME, data: vol); |
281 | pcmp->final_volume = 1; |
282 | spin_unlock(lock: &gus->reg_lock); |
283 | } |
284 | |
285 | static void snd_gf1_pcm_volume_change(struct snd_gus_card * gus) |
286 | { |
287 | } |
288 | |
289 | static int snd_gf1_pcm_poke_block(struct snd_gus_card *gus, unsigned char *buf, |
290 | unsigned int pos, unsigned int count, |
291 | int w16, int invert) |
292 | { |
293 | unsigned int len; |
294 | unsigned long flags; |
295 | |
296 | /* |
297 | printk(KERN_DEBUG |
298 | "poke block; buf = 0x%x, pos = %i, count = %i, port = 0x%x\n", |
299 | (int)buf, pos, count, gus->gf1.port); |
300 | */ |
301 | while (count > 0) { |
302 | len = count; |
303 | if (len > 512) /* limit, to allow IRQ */ |
304 | len = 512; |
305 | count -= len; |
306 | if (gus->interwave) { |
307 | spin_lock_irqsave(&gus->reg_lock, flags); |
308 | snd_gf1_write8(gus, SNDRV_GF1_GB_MEMORY_CONTROL, data: 0x01 | (invert ? 0x08 : 0x00)); |
309 | snd_gf1_dram_addr(gus, addr: pos); |
310 | if (w16) { |
311 | outb(SNDRV_GF1_GW_DRAM_IO16, GUSP(gus, GF1REGSEL)); |
312 | outsw(GUSP(gus, GF1DATALOW), addr: buf, count: len >> 1); |
313 | } else { |
314 | outsb(GUSP(gus, DRAM), addr: buf, count: len); |
315 | } |
316 | spin_unlock_irqrestore(lock: &gus->reg_lock, flags); |
317 | buf += 512; |
318 | pos += 512; |
319 | } else { |
320 | invert = invert ? 0x80 : 0x00; |
321 | if (w16) { |
322 | len >>= 1; |
323 | while (len--) { |
324 | snd_gf1_poke(gus, addr: pos++, data: *buf++); |
325 | snd_gf1_poke(gus, addr: pos++, data: *buf++ ^ invert); |
326 | } |
327 | } else { |
328 | while (len--) |
329 | snd_gf1_poke(gus, addr: pos++, data: *buf++ ^ invert); |
330 | } |
331 | } |
332 | if (count > 0 && !in_interrupt()) { |
333 | schedule_timeout_interruptible(timeout: 1); |
334 | if (signal_pending(current)) |
335 | return -EAGAIN; |
336 | } |
337 | } |
338 | return 0; |
339 | } |
340 | |
341 | static int get_bpos(struct gus_pcm_private *pcmp, int voice, unsigned int pos, |
342 | unsigned int len) |
343 | { |
344 | unsigned int bpos = pos + (voice * (pcmp->dma_size / 2)); |
345 | if (snd_BUG_ON(bpos > pcmp->dma_size)) |
346 | return -EIO; |
347 | if (snd_BUG_ON(bpos + len > pcmp->dma_size)) |
348 | return -EIO; |
349 | return bpos; |
350 | } |
351 | |
352 | static int playback_copy_ack(struct snd_pcm_substream *substream, |
353 | unsigned int bpos, unsigned int len) |
354 | { |
355 | struct snd_pcm_runtime *runtime = substream->runtime; |
356 | struct gus_pcm_private *pcmp = runtime->private_data; |
357 | struct snd_gus_card *gus = pcmp->gus; |
358 | int w16, invert; |
359 | |
360 | if (len > 32) |
361 | return snd_gf1_pcm_block_change(substream, offset: bpos, |
362 | addr: pcmp->memory + bpos, count: len); |
363 | |
364 | w16 = (snd_pcm_format_width(format: runtime->format) == 16); |
365 | invert = snd_pcm_format_unsigned(format: runtime->format); |
366 | return snd_gf1_pcm_poke_block(gus, buf: runtime->dma_area + bpos, |
367 | pos: pcmp->memory + bpos, count: len, w16, invert); |
368 | } |
369 | |
370 | static int snd_gf1_pcm_playback_copy(struct snd_pcm_substream *substream, |
371 | int voice, unsigned long pos, |
372 | struct iov_iter *src, unsigned long count) |
373 | { |
374 | struct snd_pcm_runtime *runtime = substream->runtime; |
375 | struct gus_pcm_private *pcmp = runtime->private_data; |
376 | unsigned int len = count; |
377 | int bpos; |
378 | |
379 | bpos = get_bpos(pcmp, voice, pos, len); |
380 | if (bpos < 0) |
381 | return pos; |
382 | if (copy_from_iter(addr: runtime->dma_area + bpos, bytes: len, i: src) != len) |
383 | return -EFAULT; |
384 | return playback_copy_ack(substream, bpos, len); |
385 | } |
386 | |
387 | static int snd_gf1_pcm_playback_silence(struct snd_pcm_substream *substream, |
388 | int voice, unsigned long pos, |
389 | unsigned long count) |
390 | { |
391 | struct snd_pcm_runtime *runtime = substream->runtime; |
392 | struct gus_pcm_private *pcmp = runtime->private_data; |
393 | unsigned int len = count; |
394 | int bpos; |
395 | |
396 | bpos = get_bpos(pcmp, voice, pos, len); |
397 | if (bpos < 0) |
398 | return pos; |
399 | snd_pcm_format_set_silence(format: runtime->format, buf: runtime->dma_area + bpos, |
400 | frames: bytes_to_samples(runtime, size: count)); |
401 | return playback_copy_ack(substream, bpos, len); |
402 | } |
403 | |
404 | static int snd_gf1_pcm_playback_hw_params(struct snd_pcm_substream *substream, |
405 | struct snd_pcm_hw_params *hw_params) |
406 | { |
407 | struct snd_gus_card *gus = snd_pcm_substream_chip(substream); |
408 | struct snd_pcm_runtime *runtime = substream->runtime; |
409 | struct gus_pcm_private *pcmp = runtime->private_data; |
410 | |
411 | if (runtime->buffer_changed) { |
412 | struct snd_gf1_mem_block *block; |
413 | if (pcmp->memory > 0) { |
414 | snd_gf1_mem_free(alloc: &gus->gf1.mem_alloc, address: pcmp->memory); |
415 | pcmp->memory = 0; |
416 | } |
417 | block = snd_gf1_mem_alloc(alloc: &gus->gf1.mem_alloc, |
418 | SNDRV_GF1_MEM_OWNER_DRIVER, |
419 | name: "GF1 PCM" , |
420 | size: runtime->dma_bytes, w_16: 1, align: 32, |
421 | NULL); |
422 | if (!block) |
423 | return -ENOMEM; |
424 | pcmp->memory = block->ptr; |
425 | } |
426 | pcmp->voices = params_channels(p: hw_params); |
427 | if (pcmp->pvoices[0] == NULL) { |
428 | pcmp->pvoices[0] = snd_gf1_alloc_voice(gus: pcmp->gus, SNDRV_GF1_VOICE_TYPE_PCM, client: 0, port: 0); |
429 | if (!pcmp->pvoices[0]) |
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 | pcmp->pvoices[1] = snd_gf1_alloc_voice(gus: pcmp->gus, SNDRV_GF1_VOICE_TYPE_PCM, client: 0, port: 0); |
438 | if (!pcmp->pvoices[1]) |
439 | return -ENOMEM; |
440 | pcmp->pvoices[1]->handler_wave = snd_gf1_pcm_interrupt_wave; |
441 | pcmp->pvoices[1]->handler_volume = snd_gf1_pcm_interrupt_volume; |
442 | pcmp->pvoices[1]->volume_change = snd_gf1_pcm_volume_change; |
443 | pcmp->pvoices[1]->private_data = pcmp; |
444 | } else if (pcmp->voices == 1) { |
445 | if (pcmp->pvoices[1]) { |
446 | snd_gf1_free_voice(gus: pcmp->gus, voice: pcmp->pvoices[1]); |
447 | pcmp->pvoices[1] = NULL; |
448 | } |
449 | } |
450 | return 0; |
451 | } |
452 | |
453 | static int snd_gf1_pcm_playback_hw_free(struct snd_pcm_substream *substream) |
454 | { |
455 | struct snd_pcm_runtime *runtime = substream->runtime; |
456 | struct gus_pcm_private *pcmp = runtime->private_data; |
457 | |
458 | if (pcmp->pvoices[0]) { |
459 | snd_gf1_free_voice(gus: pcmp->gus, voice: pcmp->pvoices[0]); |
460 | pcmp->pvoices[0] = NULL; |
461 | } |
462 | if (pcmp->pvoices[1]) { |
463 | snd_gf1_free_voice(gus: pcmp->gus, voice: pcmp->pvoices[1]); |
464 | pcmp->pvoices[1] = NULL; |
465 | } |
466 | if (pcmp->memory > 0) { |
467 | snd_gf1_mem_free(alloc: &pcmp->gus->gf1.mem_alloc, address: pcmp->memory); |
468 | pcmp->memory = 0; |
469 | } |
470 | return 0; |
471 | } |
472 | |
473 | static int snd_gf1_pcm_playback_prepare(struct snd_pcm_substream *substream) |
474 | { |
475 | struct snd_pcm_runtime *runtime = substream->runtime; |
476 | struct gus_pcm_private *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(struct snd_pcm_substream *substream, |
486 | int cmd) |
487 | { |
488 | struct snd_gus_card *gus = snd_pcm_substream_chip(substream); |
489 | struct snd_pcm_runtime *runtime = substream->runtime; |
490 | struct gus_pcm_private *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(lock: &pcmp->lock); |
497 | pcmp->flags &= ~SNDRV_GF1_PCM_PFLG_ACTIVE; |
498 | spin_unlock(lock: &pcmp->lock); |
499 | voice = pcmp->pvoices[0]->number; |
500 | snd_gf1_stop_voices(gus, v_min: voice, v_max: voice); |
501 | if (pcmp->pvoices[1]) { |
502 | voice = pcmp->pvoices[1]->number; |
503 | snd_gf1_stop_voices(gus, v_min: voice, v_max: voice); |
504 | } |
505 | } else { |
506 | return -EINVAL; |
507 | } |
508 | return 0; |
509 | } |
510 | |
511 | static snd_pcm_uframes_t snd_gf1_pcm_playback_pointer(struct snd_pcm_substream *substream) |
512 | { |
513 | struct snd_gus_card *gus = snd_pcm_substream_chip(substream); |
514 | struct snd_pcm_runtime *runtime = substream->runtime; |
515 | struct gus_pcm_private *pcmp = runtime->private_data; |
516 | unsigned int pos; |
517 | unsigned char voice_ctrl; |
518 | |
519 | pos = 0; |
520 | spin_lock(lock: &gus->reg_lock); |
521 | if (pcmp->flags & SNDRV_GF1_PCM_PFLG_ACTIVE) { |
522 | snd_gf1_select_voice(gus, voice: 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, w_16bit: voice_ctrl & 4) >> 4) - pcmp->memory; |
525 | if (substream->runtime->channels > 1) |
526 | pos <<= 1; |
527 | pos = bytes_to_frames(runtime, size: pos); |
528 | } |
529 | spin_unlock(lock: &gus->reg_lock); |
530 | return pos; |
531 | } |
532 | |
533 | static const struct snd_ratnum clock = { |
534 | .num = 9878400/16, |
535 | .den_min = 2, |
536 | .den_max = 257, |
537 | .den_step = 1, |
538 | }; |
539 | |
540 | static const struct snd_pcm_hw_constraint_ratnums hw_constraints_clocks = { |
541 | .nrats = 1, |
542 | .rats = &clock, |
543 | }; |
544 | |
545 | static int snd_gf1_pcm_capture_hw_params(struct snd_pcm_substream *substream, |
546 | struct snd_pcm_hw_params *hw_params) |
547 | { |
548 | struct snd_gus_card *gus = snd_pcm_substream_chip(substream); |
549 | |
550 | gus->c_dma_size = params_buffer_bytes(p: hw_params); |
551 | gus->c_period_size = params_period_bytes(p: hw_params); |
552 | gus->c_pos = 0; |
553 | gus->gf1.pcm_rcntrl_reg = 0x21; /* IRQ at end, enable & start */ |
554 | if (params_channels(p: 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(format: params_format(p: hw_params))) |
559 | gus->gf1.pcm_rcntrl_reg |= 0x80; |
560 | return 0; |
561 | } |
562 | |
563 | static int snd_gf1_pcm_capture_prepare(struct snd_pcm_substream *substream) |
564 | { |
565 | struct snd_gus_card *gus = snd_pcm_substream_chip(substream); |
566 | struct snd_pcm_runtime *runtime = substream->runtime; |
567 | |
568 | snd_gf1_i_write8(gus, SNDRV_GF1_GB_RECORD_RATE, data: runtime->rate_den - 2); |
569 | snd_gf1_i_write8(gus, SNDRV_GF1_GB_REC_DMA_CONTROL, data: 0); /* disable sampling */ |
570 | snd_gf1_i_look8(gus, SNDRV_GF1_GB_REC_DMA_CONTROL); /* Sampling Control Register */ |
571 | snd_dma_program(dma: gus->gf1.dma2, addr: runtime->dma_addr, size: gus->c_period_size, DMA_MODE_READ); |
572 | return 0; |
573 | } |
574 | |
575 | static int snd_gf1_pcm_capture_trigger(struct snd_pcm_substream *substream, |
576 | int cmd) |
577 | { |
578 | struct snd_gus_card *gus = snd_pcm_substream_chip(substream); |
579 | int val; |
580 | |
581 | if (cmd == SNDRV_PCM_TRIGGER_START) { |
582 | val = gus->gf1.pcm_rcntrl_reg; |
583 | } else if (cmd == SNDRV_PCM_TRIGGER_STOP) { |
584 | val = 0; |
585 | } else { |
586 | return -EINVAL; |
587 | } |
588 | |
589 | spin_lock(lock: &gus->reg_lock); |
590 | snd_gf1_write8(gus, SNDRV_GF1_GB_REC_DMA_CONTROL, data: val); |
591 | snd_gf1_look8(gus, SNDRV_GF1_GB_REC_DMA_CONTROL); |
592 | spin_unlock(lock: &gus->reg_lock); |
593 | return 0; |
594 | } |
595 | |
596 | static snd_pcm_uframes_t snd_gf1_pcm_capture_pointer(struct snd_pcm_substream *substream) |
597 | { |
598 | struct snd_gus_card *gus = snd_pcm_substream_chip(substream); |
599 | int pos = snd_dma_pointer(dma: gus->gf1.dma2, size: gus->c_period_size); |
600 | pos = bytes_to_frames(runtime: substream->runtime, size: (gus->c_pos + pos) % gus->c_dma_size); |
601 | return pos; |
602 | } |
603 | |
604 | static void snd_gf1_pcm_interrupt_dma_read(struct snd_gus_card * gus) |
605 | { |
606 | snd_gf1_i_write8(gus, SNDRV_GF1_GB_REC_DMA_CONTROL, data: 0); /* disable sampling */ |
607 | snd_gf1_i_look8(gus, SNDRV_GF1_GB_REC_DMA_CONTROL); /* Sampling Control Register */ |
608 | if (gus->pcm_cap_substream != NULL) { |
609 | snd_gf1_pcm_capture_prepare(substream: gus->pcm_cap_substream); |
610 | snd_gf1_pcm_capture_trigger(substream: gus->pcm_cap_substream, SNDRV_PCM_TRIGGER_START); |
611 | gus->c_pos += gus->c_period_size; |
612 | snd_pcm_period_elapsed(substream: gus->pcm_cap_substream); |
613 | } |
614 | } |
615 | |
616 | static const struct snd_pcm_hardware snd_gf1_pcm_playback = |
617 | { |
618 | .info = SNDRV_PCM_INFO_NONINTERLEAVED, |
619 | .formats = (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U8 | |
620 | SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE), |
621 | .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, |
622 | .rate_min = 5510, |
623 | .rate_max = 48000, |
624 | .channels_min = 1, |
625 | .channels_max = 2, |
626 | .buffer_bytes_max = (128*1024), |
627 | .period_bytes_min = 64, |
628 | .period_bytes_max = (128*1024), |
629 | .periods_min = 1, |
630 | .periods_max = 1024, |
631 | .fifo_size = 0, |
632 | }; |
633 | |
634 | static const struct snd_pcm_hardware snd_gf1_pcm_capture = |
635 | { |
636 | .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | |
637 | SNDRV_PCM_INFO_MMAP_VALID), |
638 | .formats = SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U8, |
639 | .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_44100, |
640 | .rate_min = 5510, |
641 | .rate_max = 44100, |
642 | .channels_min = 1, |
643 | .channels_max = 2, |
644 | .buffer_bytes_max = (128*1024), |
645 | .period_bytes_min = 64, |
646 | .period_bytes_max = (128*1024), |
647 | .periods_min = 1, |
648 | .periods_max = 1024, |
649 | .fifo_size = 0, |
650 | }; |
651 | |
652 | static void snd_gf1_pcm_playback_free(struct snd_pcm_runtime *runtime) |
653 | { |
654 | kfree(objp: runtime->private_data); |
655 | } |
656 | |
657 | static int snd_gf1_pcm_playback_open(struct snd_pcm_substream *substream) |
658 | { |
659 | struct gus_pcm_private *pcmp; |
660 | struct snd_gus_card *gus = snd_pcm_substream_chip(substream); |
661 | struct snd_pcm_runtime *runtime = substream->runtime; |
662 | int err; |
663 | |
664 | pcmp = kzalloc(size: sizeof(*pcmp), GFP_KERNEL); |
665 | if (pcmp == NULL) |
666 | return -ENOMEM; |
667 | pcmp->gus = gus; |
668 | spin_lock_init(&pcmp->lock); |
669 | init_waitqueue_head(&pcmp->sleep); |
670 | atomic_set(v: &pcmp->dma_count, i: 0); |
671 | |
672 | runtime->private_data = pcmp; |
673 | runtime->private_free = snd_gf1_pcm_playback_free; |
674 | |
675 | #if 0 |
676 | printk(KERN_DEBUG "playback.buffer = 0x%lx, gf1.pcm_buffer = 0x%lx\n" , |
677 | (long) pcm->playback.buffer, (long) gus->gf1.pcm_buffer); |
678 | #endif |
679 | err = snd_gf1_dma_init(gus); |
680 | if (err < 0) |
681 | return err; |
682 | pcmp->flags = SNDRV_GF1_PCM_PFLG_NONE; |
683 | pcmp->substream = substream; |
684 | runtime->hw = snd_gf1_pcm_playback; |
685 | snd_pcm_limit_isa_dma_size(dma: gus->gf1.dma1, max: &runtime->hw.buffer_bytes_max); |
686 | snd_pcm_limit_isa_dma_size(dma: gus->gf1.dma1, max: &runtime->hw.period_bytes_max); |
687 | snd_pcm_hw_constraint_step(runtime, cond: 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, step: 64); |
688 | return 0; |
689 | } |
690 | |
691 | static int snd_gf1_pcm_playback_close(struct snd_pcm_substream *substream) |
692 | { |
693 | struct snd_gus_card *gus = snd_pcm_substream_chip(substream); |
694 | struct snd_pcm_runtime *runtime = substream->runtime; |
695 | struct gus_pcm_private *pcmp = runtime->private_data; |
696 | |
697 | if (!wait_event_timeout(pcmp->sleep, (atomic_read(&pcmp->dma_count) <= 0), 2*HZ)) |
698 | snd_printk(KERN_ERR "gf1 pcm - serious DMA problem\n" ); |
699 | |
700 | snd_gf1_dma_done(gus); |
701 | return 0; |
702 | } |
703 | |
704 | static int snd_gf1_pcm_capture_open(struct snd_pcm_substream *substream) |
705 | { |
706 | struct snd_pcm_runtime *runtime = substream->runtime; |
707 | struct snd_gus_card *gus = snd_pcm_substream_chip(substream); |
708 | |
709 | gus->gf1.interrupt_handler_dma_read = snd_gf1_pcm_interrupt_dma_read; |
710 | gus->pcm_cap_substream = substream; |
711 | substream->runtime->hw = snd_gf1_pcm_capture; |
712 | snd_pcm_limit_isa_dma_size(dma: gus->gf1.dma2, max: &runtime->hw.buffer_bytes_max); |
713 | snd_pcm_limit_isa_dma_size(dma: gus->gf1.dma2, max: &runtime->hw.period_bytes_max); |
714 | snd_pcm_hw_constraint_ratnums(runtime, cond: 0, SNDRV_PCM_HW_PARAM_RATE, |
715 | r: &hw_constraints_clocks); |
716 | return 0; |
717 | } |
718 | |
719 | static int snd_gf1_pcm_capture_close(struct snd_pcm_substream *substream) |
720 | { |
721 | struct snd_gus_card *gus = snd_pcm_substream_chip(substream); |
722 | |
723 | gus->pcm_cap_substream = NULL; |
724 | snd_gf1_set_default_handlers(gus, SNDRV_GF1_HANDLER_DMA_READ); |
725 | return 0; |
726 | } |
727 | |
728 | static int snd_gf1_pcm_volume_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) |
729 | { |
730 | uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; |
731 | uinfo->count = 2; |
732 | uinfo->value.integer.min = 0; |
733 | uinfo->value.integer.max = 127; |
734 | return 0; |
735 | } |
736 | |
737 | static int snd_gf1_pcm_volume_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) |
738 | { |
739 | struct snd_gus_card *gus = snd_kcontrol_chip(kcontrol); |
740 | unsigned long flags; |
741 | |
742 | spin_lock_irqsave(&gus->pcm_volume_level_lock, flags); |
743 | ucontrol->value.integer.value[0] = gus->gf1.pcm_volume_level_left1; |
744 | ucontrol->value.integer.value[1] = gus->gf1.pcm_volume_level_right1; |
745 | spin_unlock_irqrestore(lock: &gus->pcm_volume_level_lock, flags); |
746 | return 0; |
747 | } |
748 | |
749 | static int snd_gf1_pcm_volume_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) |
750 | { |
751 | struct snd_gus_card *gus = snd_kcontrol_chip(kcontrol); |
752 | unsigned long flags; |
753 | int change; |
754 | unsigned int idx; |
755 | unsigned short val1, val2, vol; |
756 | struct gus_pcm_private *pcmp; |
757 | struct snd_gus_voice *pvoice; |
758 | |
759 | val1 = ucontrol->value.integer.value[0] & 127; |
760 | val2 = ucontrol->value.integer.value[1] & 127; |
761 | spin_lock_irqsave(&gus->pcm_volume_level_lock, flags); |
762 | change = val1 != gus->gf1.pcm_volume_level_left1 || |
763 | val2 != gus->gf1.pcm_volume_level_right1; |
764 | gus->gf1.pcm_volume_level_left1 = val1; |
765 | gus->gf1.pcm_volume_level_right1 = val2; |
766 | gus->gf1.pcm_volume_level_left = snd_gf1_lvol_to_gvol_raw(vol: val1 << 9) << 4; |
767 | gus->gf1.pcm_volume_level_right = snd_gf1_lvol_to_gvol_raw(vol: val2 << 9) << 4; |
768 | spin_unlock_irqrestore(lock: &gus->pcm_volume_level_lock, flags); |
769 | /* are we active? */ |
770 | spin_lock_irqsave(&gus->voice_alloc, flags); |
771 | for (idx = 0; idx < 32; idx++) { |
772 | pvoice = &gus->gf1.voices[idx]; |
773 | if (!pvoice->pcm) |
774 | continue; |
775 | pcmp = pvoice->private_data; |
776 | if (!(pcmp->flags & SNDRV_GF1_PCM_PFLG_ACTIVE)) |
777 | continue; |
778 | /* load real volume - better precision */ |
779 | spin_lock(lock: &gus->reg_lock); |
780 | snd_gf1_select_voice(gus, voice: pvoice->number); |
781 | snd_gf1_ctrl_stop(gus, SNDRV_GF1_VB_VOLUME_CONTROL); |
782 | vol = pvoice == pcmp->pvoices[0] ? gus->gf1.pcm_volume_level_left : gus->gf1.pcm_volume_level_right; |
783 | snd_gf1_write16(gus, SNDRV_GF1_VW_VOLUME, data: vol); |
784 | pcmp->final_volume = 1; |
785 | spin_unlock(lock: &gus->reg_lock); |
786 | } |
787 | spin_unlock_irqrestore(lock: &gus->voice_alloc, flags); |
788 | return change; |
789 | } |
790 | |
791 | static const struct snd_kcontrol_new snd_gf1_pcm_volume_control = |
792 | { |
793 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, |
794 | .name = "PCM Playback Volume" , |
795 | .info = snd_gf1_pcm_volume_info, |
796 | .get = snd_gf1_pcm_volume_get, |
797 | .put = snd_gf1_pcm_volume_put |
798 | }; |
799 | |
800 | static const struct snd_kcontrol_new snd_gf1_pcm_volume_control1 = |
801 | { |
802 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, |
803 | .name = "GPCM Playback Volume" , |
804 | .info = snd_gf1_pcm_volume_info, |
805 | .get = snd_gf1_pcm_volume_get, |
806 | .put = snd_gf1_pcm_volume_put |
807 | }; |
808 | |
809 | static const struct snd_pcm_ops snd_gf1_pcm_playback_ops = { |
810 | .open = snd_gf1_pcm_playback_open, |
811 | .close = snd_gf1_pcm_playback_close, |
812 | .hw_params = snd_gf1_pcm_playback_hw_params, |
813 | .hw_free = snd_gf1_pcm_playback_hw_free, |
814 | .prepare = snd_gf1_pcm_playback_prepare, |
815 | .trigger = snd_gf1_pcm_playback_trigger, |
816 | .pointer = snd_gf1_pcm_playback_pointer, |
817 | .copy = snd_gf1_pcm_playback_copy, |
818 | .fill_silence = snd_gf1_pcm_playback_silence, |
819 | }; |
820 | |
821 | static const struct snd_pcm_ops snd_gf1_pcm_capture_ops = { |
822 | .open = snd_gf1_pcm_capture_open, |
823 | .close = snd_gf1_pcm_capture_close, |
824 | .hw_params = snd_gf1_pcm_capture_hw_params, |
825 | .prepare = snd_gf1_pcm_capture_prepare, |
826 | .trigger = snd_gf1_pcm_capture_trigger, |
827 | .pointer = snd_gf1_pcm_capture_pointer, |
828 | }; |
829 | |
830 | int snd_gf1_pcm_new(struct snd_gus_card *gus, int pcm_dev, int control_index) |
831 | { |
832 | struct snd_card *card; |
833 | struct snd_kcontrol *kctl; |
834 | struct snd_pcm *pcm; |
835 | struct snd_pcm_substream *substream; |
836 | int capture, err; |
837 | |
838 | card = gus->card; |
839 | capture = !gus->interwave && !gus->ess_flag && !gus->ace_flag ? 1 : 0; |
840 | err = snd_pcm_new(card, |
841 | id: gus->interwave ? "AMD InterWave" : "GF1" , |
842 | device: pcm_dev, |
843 | playback_count: gus->gf1.pcm_channels / 2, |
844 | capture_count: capture, |
845 | rpcm: &pcm); |
846 | if (err < 0) |
847 | return err; |
848 | pcm->private_data = gus; |
849 | /* playback setup */ |
850 | snd_pcm_set_ops(pcm, direction: SNDRV_PCM_STREAM_PLAYBACK, ops: &snd_gf1_pcm_playback_ops); |
851 | |
852 | for (substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; substream; substream = substream->next) |
853 | snd_pcm_set_managed_buffer(substream, SNDRV_DMA_TYPE_DEV, |
854 | data: card->dev, |
855 | size: 64*1024, max: gus->gf1.dma1 > 3 ? 128*1024 : 64*1024); |
856 | |
857 | pcm->info_flags = 0; |
858 | pcm->dev_subclass = SNDRV_PCM_SUBCLASS_GENERIC_MIX; |
859 | if (capture) { |
860 | snd_pcm_set_ops(pcm, direction: SNDRV_PCM_STREAM_CAPTURE, ops: &snd_gf1_pcm_capture_ops); |
861 | if (gus->gf1.dma2 == gus->gf1.dma1) |
862 | pcm->info_flags |= SNDRV_PCM_INFO_HALF_DUPLEX; |
863 | snd_pcm_set_managed_buffer(substream: pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream, |
864 | SNDRV_DMA_TYPE_DEV, data: card->dev, |
865 | size: 64*1024, max: gus->gf1.dma2 > 3 ? 128*1024 : 64*1024); |
866 | } |
867 | strcpy(p: pcm->name, q: pcm->id); |
868 | if (gus->interwave) { |
869 | sprintf(buf: pcm->name + strlen(pcm->name), fmt: " rev %c" , gus->revision + 'A'); |
870 | } |
871 | strcat(p: pcm->name, q: " (synth)" ); |
872 | gus->pcm = pcm; |
873 | |
874 | if (gus->codec_flag) |
875 | kctl = snd_ctl_new1(kcontrolnew: &snd_gf1_pcm_volume_control1, private_data: gus); |
876 | else |
877 | kctl = snd_ctl_new1(kcontrolnew: &snd_gf1_pcm_volume_control, private_data: gus); |
878 | kctl->id.index = control_index; |
879 | err = snd_ctl_add(card, kcontrol: kctl); |
880 | if (err < 0) |
881 | return err; |
882 | |
883 | return 0; |
884 | } |
885 | |
886 | |