Stufen
Anforderungen
|
Konzepte
|
Entwurf
|
Entwicklung
|
Qualitätssicherung
|
Lebenszyklus
|
Steuerung
Ziele
Untersuchung
mit Columbo
Integrität von
Datenbanken
Interaktion und
Portierbarkeit
Ergonomie der
Schnittstellen
Angebot
Produkte
Projekt
Beratung
Mittel
Analytik
Modellierung
Sprachen
Algebra
Logik
Hardware
Denken
Kreativität
Zusammenhänge
Gesellschaft
Wirtschaft
Branche
Firma
Quellcodebibliothek
Statistik
Leitseite
products
/
Sources
/
formale Sprachen
/
C
/
LibreOffice
/
icon-themes
/
breeze_svg
/
cmd
/ (
Office von Apache
Version 25.8.3.2
©
) Datei vom 5.10.2025 mit Größe 1 kB
Bilddatei
gus_pcm.c
products/Sources/formale Sprachen/C/Linux/sound/isa/gus/gus_pcm.c
// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (c) by Jaroslav Kysela
* Routines for control of GF1 chip (PCM things) * * InterWave chips supports interleaved DMA, but this feature isn't used in * this code. * * This code emulates autoinit DMA transfer for playback, recording by GF1 * chip doesn't support autoinit DMA. */ #include
#include
#include
#include
#include
#include
#include
#include "gus_tables.h" /* maximum rate */ #define SNDRV_GF1_PCM_RATE 48000 #define SNDRV_GF1_PCM_PFLG_NONE 0 #define SNDRV_GF1_PCM_PFLG_ACTIVE (1<<0) #define SNDRV_GF1_PCM_PFLG_NEUTRAL (2<<0) struct gus_pcm_private { struct snd_gus_card * gus; struct snd_pcm_substream *substream; spinlock_t lock; unsigned int voices; struct snd_gus_voice *pvoices[2]; unsigned int memory; unsigned short flags; unsigned char voice_ctrl, ramp_ctrl; unsigned int bpos; unsigned int blocks; unsigned int block_size; unsigned int dma_size; wait_queue_head_t sleep; atomic_t dma_count; int final_volume; }; static void snd_gf1_pcm_block_change_ack(struct snd_gus_card * gus, void *private_data) { struct gus_pcm_private *pcmp = private_data; if (pcmp) { atomic_dec(&pcmp->dma_count); wake_up(&pcmp->sleep); } } static int snd_gf1_pcm_block_change(struct snd_pcm_substream *substream, unsigned int offset, unsigned int addr, unsigned int count) { struct snd_gf1_dma_block block; struct snd_pcm_runtime *runtime = substream->runtime; struct gus_pcm_private *pcmp = runtime->private_data; count += offset & 31; offset &= ~31; memset(&block, 0, sizeof(block)); block.cmd = SNDRV_GF1_DMA_IRQ; if (snd_pcm_format_unsigned(runtime->format)) block.cmd |= SNDRV_GF1_DMA_UNSIGNED; if (snd_pcm_format_width(runtime->format) == 16) block.cmd |= SNDRV_GF1_DMA_16BIT; block.addr = addr & ~31; block.buffer = runtime->dma_area + offset; block.buf_addr = runtime->dma_addr + offset; block.count = count; block.private_data = pcmp; block.ack = snd_gf1_pcm_block_change_ack; if (!snd_gf1_dma_transfer_block(pcmp->gus, &block, 0, 0)) atomic_inc(&pcmp->dma_count); return 0; } static void snd_gf1_pcm_trigger_up(struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; struct gus_pcm_private *pcmp = runtime->private_data; struct snd_gus_card * gus = pcmp->gus; unsigned long flags; unsigned char voice_ctrl, ramp_ctrl; unsigned short rate; unsigned int curr, begin, end; unsigned short vol; unsigned char pan; unsigned int voice; spin_lock_irqsave(&pcmp->lock, flags); if (pcmp->flags & SNDRV_GF1_PCM_PFLG_ACTIVE) { spin_unlock_irqrestore(&pcmp->lock, flags); return; } pcmp->flags |= SNDRV_GF1_PCM_PFLG_ACTIVE; pcmp->final_volume = 0; spin_unlock_irqrestore(&pcmp->lock, flags); rate = snd_gf1_translate_freq(gus, runtime->rate << 4); /* enable WAVE IRQ */ voice_ctrl = snd_pcm_format_width(runtime->format) == 16 ? 0x24 : 0x20; /* enable RAMP IRQ + rollover */ ramp_ctrl = 0x24; if (pcmp->blocks == 1) { voice_ctrl |= 0x08; /* loop enable */ ramp_ctrl &= ~0x04; /* disable rollover */ } for (voice = 0; voice < pcmp->voices; voice++) { begin = pcmp->memory + voice * (pcmp->dma_size / runtime->channels); curr = begin + (pcmp->bpos * pcmp->block_size) / runtime->channels; end = curr + (pcmp->block_size / runtime->channels); end -= snd_pcm_format_width(runtime->format) == 16 ? 2 : 1; pan = runtime->channels == 2 ? (!voice ? 1 : 14) : 8; vol = !voice ? gus->gf1.pcm_volume_level_left : gus->gf1.pcm_volume_level_right; spin_lock_irqsave(&gus->reg_lock, flags); snd_gf1_select_voice(gus, pcmp->pvoices[voice]->number); snd_gf1_write8(gus, SNDRV_GF1_VB_PAN, pan); snd_gf1_write16(gus, SNDRV_GF1_VW_FREQUENCY, rate); snd_gf1_write_addr(gus, SNDRV_GF1_VA_START, begin << 4, voice_ctrl & 4); snd_gf1_write_addr(gus, SNDRV_GF1_VA_END, end << 4, voice_ctrl & 4); snd_gf1_write_addr(gus, SNDRV_GF1_VA_CURRENT, curr << 4, voice_ctrl & 4); snd_gf1_write16(gus, SNDRV_GF1_VW_VOLUME, SNDRV_GF1_MIN_VOLUME << 4); snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_RATE, 0x2f); snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_START, SNDRV_GF1_MIN_OFFSET); snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_END, vol >> 8); snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_CONTROL, ramp_ctrl); if (!gus->gf1.enh_mode) { snd_gf1_delay(gus); snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_CONTROL, ramp_ctrl); } spin_unlock_irqrestore(&gus->reg_lock, flags); } spin_lock_irqsave(&gus->reg_lock, flags); for (voice = 0; voice < pcmp->voices; voice++) { snd_gf1_select_voice(gus, pcmp->pvoices[voice]->number); if (gus->gf1.enh_mode) snd_gf1_write8(gus, SNDRV_GF1_VB_MODE, 0x00); /* deactivate voice */ snd_gf1_write8(gus, SNDRV_GF1_VB_ADDRESS_CONTROL, voice_ctrl); voice_ctrl &= ~0x20; } voice_ctrl |= 0x20; if (!gus->gf1.enh_mode) { snd_gf1_delay(gus); for (voice = 0; voice < pcmp->voices; voice++) { snd_gf1_select_voice(gus, pcmp->pvoices[voice]->number); snd_gf1_write8(gus, SNDRV_GF1_VB_ADDRESS_CONTROL, voice_ctrl); voice_ctrl &= ~0x20; /* disable IRQ for next voice */ } } spin_unlock_irqrestore(&gus->reg_lock, flags); } static void snd_gf1_pcm_interrupt_wave(struct snd_gus_card * gus, struct snd_gus_voice *pvoice) { struct gus_pcm_private * pcmp; struct snd_pcm_runtime *runtime; unsigned char voice_ctrl, ramp_ctrl; unsigned int idx; unsigned int end, step; if (!pvoice->private_data) { dev_dbg(gus->card->dev, "%s: unknown wave irq?\n", __func__); snd_gf1_smart_stop_voice(gus, pvoice->number); return; } pcmp = pvoice->private_data; if (pcmp == NULL) { dev_dbg(gus->card->dev, "%s: unknown wave irq?\n", __func__); snd_gf1_smart_stop_voice(gus, pvoice->number); return; } gus = pcmp->gus; runtime = pcmp->substream->runtime; spin_lock(&gus->reg_lock); snd_gf1_select_voice(gus, pvoice->number); voice_ctrl = snd_gf1_read8(gus, SNDRV_GF1_VB_ADDRESS_CONTROL) & ~0x8b; ramp_ctrl = (snd_gf1_read8(gus, SNDRV_GF1_VB_VOLUME_CONTROL) & ~0xa4) | 0x03; #if 0 snd_gf1_select_voice(gus, pvoice->number); dev_dbg(gus->card->dev, "position = 0x%x\n", (snd_gf1_read_addr(gus, SNDRV_GF1_VA_CURRENT, voice_ctrl & 4) >> 4)); snd_gf1_select_voice(gus, pcmp->pvoices[1]->number); dev_dbg(gus->card->dev, "position = 0x%x\n", (snd_gf1_read_addr(gus, SNDRV_GF1_VA_CURRENT, voice_ctrl & 4) >> 4)); snd_gf1_select_voice(gus, pvoice->number); #endif pcmp->bpos++; pcmp->bpos %= pcmp->blocks; if (pcmp->bpos + 1 >= pcmp->blocks) { /* last block? */ voice_ctrl |= 0x08; /* enable loop */ } else { ramp_ctrl |= 0x04; /* enable rollover */ } end = pcmp->memory + (((pcmp->bpos + 1) * pcmp->block_size) / runtime->channels); end -= voice_ctrl & 4 ? 2 : 1; step = pcmp->dma_size / runtime->channels; voice_ctrl |= 0x20; if (!pcmp->final_volume) { ramp_ctrl |= 0x20; ramp_ctrl &= ~0x03; } for (idx = 0; idx < pcmp->voices; idx++, end += step) { snd_gf1_select_voice(gus, pcmp->pvoices[idx]->number); snd_gf1_write_addr(gus, SNDRV_GF1_VA_END, end << 4, voice_ctrl & 4); snd_gf1_write8(gus, SNDRV_GF1_VB_ADDRESS_CONTROL, voice_ctrl); snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_CONTROL, ramp_ctrl); voice_ctrl &= ~0x20; } if (!gus->gf1.enh_mode) { snd_gf1_delay(gus); voice_ctrl |= 0x20; for (idx = 0; idx < pcmp->voices; idx++) { snd_gf1_select_voice(gus, pcmp->pvoices[idx]->number); snd_gf1_write8(gus, SNDRV_GF1_VB_ADDRESS_CONTROL, voice_ctrl); snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_CONTROL, ramp_ctrl); voice_ctrl &= ~0x20; } } spin_unlock(&gus->reg_lock); snd_pcm_period_elapsed(pcmp->substream); #if 0 if ((runtime->flags & SNDRV_PCM_FLG_MMAP) && *runtime->state == SNDRV_PCM_STATE_RUNNING) { end = pcmp->bpos * pcmp->block_size; if (runtime->channels > 1) { snd_gf1_pcm_block_change(pcmp->substream, end, pcmp->memory + (end / 2), pcmp->block_size / 2); snd_gf1_pcm_block_change(pcmp->substream, end + (pcmp->block_size / 2), pcmp->memory + (pcmp->dma_size / 2) + (end / 2), pcmp->block_size / 2); } else { snd_gf1_pcm_block_change(pcmp->substream, end, pcmp->memory + end, pcmp->block_size); } } #endif } static void snd_gf1_pcm_interrupt_volume(struct snd_gus_card * gus, struct snd_gus_voice * pvoice) { unsigned short vol; int cvoice; struct gus_pcm_private *pcmp = pvoice->private_data; /* stop ramp, but leave rollover bit untouched */ spin_lock(&gus->reg_lock); snd_gf1_select_voice(gus, pvoice->number); snd_gf1_ctrl_stop(gus, SNDRV_GF1_VB_VOLUME_CONTROL); spin_unlock(&gus->reg_lock); if (pcmp == NULL) return; /* are we active? */ if (!(pcmp->flags & SNDRV_GF1_PCM_PFLG_ACTIVE)) return; /* load real volume - better precision */ cvoice = pcmp->pvoices[0] == pvoice ? 0 : 1; if (pcmp->substream == NULL) return; vol = !cvoice ? gus->gf1.pcm_volume_level_left : gus->gf1.pcm_volume_level_right; spin_lock(&gus->reg_lock); snd_gf1_select_voice(gus, pvoice->number); snd_gf1_write16(gus, SNDRV_GF1_VW_VOLUME, vol); pcmp->final_volume = 1; spin_unlock(&gus->reg_lock); } static void snd_gf1_pcm_volume_change(struct snd_gus_card * gus) { } static int snd_gf1_pcm_poke_block(struct snd_gus_card *gus, unsigned char *buf, unsigned int pos, unsigned int count, int w16, int invert) { unsigned int len; unsigned long flags; while (count > 0) { len = count; if (len > 512) /* limit, to allow IRQ */ len = 512; count -= len; if (gus->interwave) { spin_lock_irqsave(&gus->reg_lock, flags); snd_gf1_write8(gus, SNDRV_GF1_GB_MEMORY_CONTROL, 0x01 | (invert ? 0x08 : 0x00)); snd_gf1_dram_addr(gus, pos); if (w16) { outb(SNDRV_GF1_GW_DRAM_IO16, GUSP(gus, GF1REGSEL)); outsw(GUSP(gus, GF1DATALOW), buf, len >> 1); } else { outsb(GUSP(gus, DRAM), buf, len); } spin_unlock_irqrestore(&gus->reg_lock, flags); buf += 512; pos += 512; } else { invert = invert ? 0x80 : 0x00; if (w16) { len >>= 1; while (len--) { snd_gf1_poke(gus, pos++, *buf++); snd_gf1_poke(gus, pos++, *buf++ ^ invert); } } else { while (len--) snd_gf1_poke(gus, pos++, *buf++ ^ invert); } } if (count > 0 && !in_interrupt()) { schedule_timeout_interruptible(1); if (signal_pending(current)) return -EAGAIN; } } return 0; } static int get_bpos(struct gus_pcm_private *pcmp, int voice, unsigned int pos, unsigned int len) { unsigned int bpos = pos + (voice * (pcmp->dma_size / 2)); if (snd_BUG_ON(bpos > pcmp->dma_size)) return -EIO; if (snd_BUG_ON(bpos + len > pcmp->dma_size)) return -EIO; return bpos; } static int playback_copy_ack(struct snd_pcm_substream *substream, unsigned int bpos, unsigned int len) { struct snd_pcm_runtime *runtime = substream->runtime; struct gus_pcm_private *pcmp = runtime->private_data; struct snd_gus_card *gus = pcmp->gus; int w16, invert; if (len > 32) return snd_gf1_pcm_block_change(substream, bpos, pcmp->memory + bpos, len); w16 = (snd_pcm_format_width(runtime->format) == 16); invert = snd_pcm_format_unsigned(runtime->format); return snd_gf1_pcm_poke_block(gus, runtime->dma_area + bpos, pcmp->memory + bpos, len, w16, invert); } static int snd_gf1_pcm_playback_copy(struct snd_pcm_substream *substream, int voice, unsigned long pos, struct iov_iter *src, unsigned long count) { struct snd_pcm_runtime *runtime = substream->runtime; struct gus_pcm_private *pcmp = runtime->private_data; unsigned int len = count; int bpos; bpos = get_bpos(pcmp, voice, pos, len); if (bpos < 0) return bpos; if (copy_from_iter(runtime->dma_area + bpos, len, src) != len) return -EFAULT; return playback_copy_ack(substream, bpos, len); } static int snd_gf1_pcm_playback_silence(struct snd_pcm_substream *substream, int voice, unsigned long pos, unsigned long count) { struct snd_pcm_runtime *runtime = substream->runtime; struct gus_pcm_private *pcmp = runtime->private_data; unsigned int len = count; int bpos; bpos = get_bpos(pcmp, voice, pos, len); if (bpos < 0) return bpos; snd_pcm_format_set_silence(runtime->format, runtime->dma_area + bpos, bytes_to_samples(runtime, count)); return playback_copy_ack(substream, bpos, len); } static int snd_gf1_pcm_playback_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params) { struct snd_gus_card *gus = snd_pcm_substream_chip(substream); struct snd_pcm_runtime *runtime = substream->runtime; struct gus_pcm_private *pcmp = runtime->private_data; if (runtime->buffer_changed) { struct snd_gf1_mem_block *block; if (pcmp->memory > 0) { snd_gf1_mem_free(&gus->gf1.mem_alloc, pcmp->memory); pcmp->memory = 0; } block = snd_gf1_mem_alloc(&gus->gf1.mem_alloc, SNDRV_GF1_MEM_OWNER_DRIVER, "GF1 PCM", runtime->dma_bytes, 1, 32, NULL); if (!block) return -ENOMEM; pcmp->memory = block->ptr; } pcmp->voices = params_channels(hw_params); if (pcmp->pvoices[0] == NULL) { pcmp->pvoices[0] = snd_gf1_alloc_voice(pcmp->gus, SNDRV_GF1_VOICE_TYPE_PCM, 0, 0); if (!pcmp->pvoices[0]) return -ENOMEM; pcmp->pvoices[0]->handler_wave = snd_gf1_pcm_interrupt_wave; pcmp->pvoices[0]->handler_volume = snd_gf1_pcm_interrupt_volume; pcmp->pvoices[0]->volume_change = snd_gf1_pcm_volume_change; pcmp->pvoices[0]->private_data = pcmp; } if (pcmp->voices > 1 && pcmp->pvoices[1] == NULL) { pcmp->pvoices[1] = snd_gf1_alloc_voice(pcmp->gus, SNDRV_GF1_VOICE_TYPE_PCM, 0, 0); if (!pcmp->pvoices[1]) return -ENOMEM; pcmp->pvoices[1]->handler_wave = snd_gf1_pcm_interrupt_wave; pcmp->pvoices[1]->handler_volume = snd_gf1_pcm_interrupt_volume; pcmp->pvoices[1]->volume_change = snd_gf1_pcm_volume_change; pcmp->pvoices[1]->private_data = pcmp; } else if (pcmp->voices == 1) { if (pcmp->pvoices[1]) { snd_gf1_free_voice(pcmp->gus, pcmp->pvoices[1]); pcmp->pvoices[1] = NULL; } } return 0; } static int snd_gf1_pcm_playback_hw_free(struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; struct gus_pcm_private *pcmp = runtime->private_data; if (pcmp->pvoices[0]) { snd_gf1_free_voice(pcmp->gus, pcmp->pvoices[0]); pcmp->pvoices[0] = NULL; } if (pcmp->pvoices[1]) { snd_gf1_free_voice(pcmp->gus, pcmp->pvoices[1]); pcmp->pvoices[1] = NULL; } if (pcmp->memory > 0) { snd_gf1_mem_free(&pcmp->gus->gf1.mem_alloc, pcmp->memory); pcmp->memory = 0; } return 0; } static int snd_gf1_pcm_playback_prepare(struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; struct gus_pcm_private *pcmp = runtime->private_data; pcmp->bpos = 0; pcmp->dma_size = snd_pcm_lib_buffer_bytes(substream); pcmp->block_size = snd_pcm_lib_period_bytes(substream); pcmp->blocks = pcmp->dma_size / pcmp->block_size; return 0; } static int snd_gf1_pcm_playback_trigger(struct snd_pcm_substream *substream, int cmd) { struct snd_gus_card *gus = snd_pcm_substream_chip(substream); struct snd_pcm_runtime *runtime = substream->runtime; struct gus_pcm_private *pcmp = runtime->private_data; int voice; if (cmd == SNDRV_PCM_TRIGGER_START) { snd_gf1_pcm_trigger_up(substream); } else if (cmd == SNDRV_PCM_TRIGGER_STOP) { spin_lock(&pcmp->lock); pcmp->flags &= ~SNDRV_GF1_PCM_PFLG_ACTIVE; spin_unlock(&pcmp->lock); voice = pcmp->pvoices[0]->number; snd_gf1_stop_voices(gus, voice, voice); if (pcmp->pvoices[1]) { voice = pcmp->pvoices[1]->number; snd_gf1_stop_voices(gus, voice, voice); } } else { return -EINVAL; } return 0; } static snd_pcm_uframes_t snd_gf1_pcm_playback_pointer(struct snd_pcm_substream *substream) { struct snd_gus_card *gus = snd_pcm_substream_chip(substream); struct snd_pcm_runtime *runtime = substream->runtime; struct gus_pcm_private *pcmp = runtime->private_data; unsigned int pos; unsigned char voice_ctrl; pos = 0; spin_lock(&gus->reg_lock); if (pcmp->flags & SNDRV_GF1_PCM_PFLG_ACTIVE) { snd_gf1_select_voice(gus, pcmp->pvoices[0]->number); voice_ctrl = snd_gf1_read8(gus, SNDRV_GF1_VB_ADDRESS_CONTROL); pos = (snd_gf1_read_addr(gus, SNDRV_GF1_VA_CURRENT, voice_ctrl & 4) >> 4) - pcmp->memory; if (substream->runtime->channels > 1) pos <<= 1; pos = bytes_to_frames(runtime, pos); } spin_unlock(&gus->reg_lock); return pos; } static const struct snd_ratnum clock = { .num = 9878400/16, .den_min = 2, .den_max = 257, .den_step = 1, }; static const struct snd_pcm_hw_constraint_ratnums hw_constraints_clocks = { .nrats = 1, .rats = &clock, }; static int snd_gf1_pcm_capture_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params) { struct snd_gus_card *gus = snd_pcm_substream_chip(substream); gus->c_dma_size = params_buffer_bytes(hw_params); gus->c_period_size = params_period_bytes(hw_params); gus->c_pos = 0; gus->gf1.pcm_rcntrl_reg = 0x21; /* IRQ at end, enable & start */ if (params_channels(hw_params) > 1) gus->gf1.pcm_rcntrl_reg |= 2; if (gus->gf1.dma2 > 3) gus->gf1.pcm_rcntrl_reg |= 4; if (snd_pcm_format_unsigned(params_format(hw_params))) gus->gf1.pcm_rcntrl_reg |= 0x80; return 0; } static int snd_gf1_pcm_capture_prepare(struct snd_pcm_substream *substream) { struct snd_gus_card *gus = snd_pcm_substream_chip(substream); struct snd_pcm_runtime *runtime = substream->runtime; snd_gf1_i_write8(gus, SNDRV_GF1_GB_RECORD_RATE, runtime->rate_den - 2); snd_gf1_i_write8(gus, SNDRV_GF1_GB_REC_DMA_CONTROL, 0); /* disable sampling */ snd_gf1_i_look8(gus, SNDRV_GF1_GB_REC_DMA_CONTROL); /* Sampling Control Register */ snd_dma_program(gus->gf1.dma2, runtime->dma_addr, gus->c_period_size, DMA_MODE_READ); return 0; } static int snd_gf1_pcm_capture_trigger(struct snd_pcm_substream *substream, int cmd) { struct snd_gus_card *gus = snd_pcm_substream_chip(substream); int val; if (cmd == SNDRV_PCM_TRIGGER_START) { val = gus->gf1.pcm_rcntrl_reg; } else if (cmd == SNDRV_PCM_TRIGGER_STOP) { val = 0; } else { return -EINVAL; } spin_lock(&gus->reg_lock); snd_gf1_write8(gus, SNDRV_GF1_GB_REC_DMA_CONTROL, val); snd_gf1_look8(gus, SNDRV_GF1_GB_REC_DMA_CONTROL); spin_unlock(&gus->reg_lock); return 0; } static snd_pcm_uframes_t snd_gf1_pcm_capture_pointer(struct snd_pcm_substream *substream) { struct snd_gus_card *gus = snd_pcm_substream_chip(substream); int pos = snd_dma_pointer(gus->gf1.dma2, gus->c_period_size); pos = bytes_to_frames(substream->runtime, (gus->c_pos + pos) % gus->c_dma_size); return pos; } static void snd_gf1_pcm_interrupt_dma_read(struct snd_gus_card * gus) { snd_gf1_i_write8(gus, SNDRV_GF1_GB_REC_DMA_CONTROL, 0); /* disable sampling */ snd_gf1_i_look8(gus, SNDRV_GF1_GB_REC_DMA_CONTROL); /* Sampling Control Register */ if (gus->pcm_cap_substream != NULL) { snd_gf1_pcm_capture_prepare(gus->pcm_cap_substream); snd_gf1_pcm_capture_trigger(gus->pcm_cap_substream, SNDRV_PCM_TRIGGER_START); gus->c_pos += gus->c_period_size; snd_pcm_period_elapsed(gus->pcm_cap_substream); } } static const struct snd_pcm_hardware snd_gf1_pcm_playback = { .info = SNDRV_PCM_INFO_NONINTERLEAVED, .formats = (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE), .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, .rate_min = 5510, .rate_max = 48000, .channels_min = 1, .channels_max = 2, .buffer_bytes_max = (128*1024), .period_bytes_min = 64, .period_bytes_max = (128*1024), .periods_min = 1, .periods_max = 1024, .fifo_size = 0, }; static const struct snd_pcm_hardware snd_gf1_pcm_capture = { .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_MMAP_VALID), .formats = SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U8, .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_44100, .rate_min = 5510, .rate_max = 44100, .channels_min = 1, .channels_max = 2, .buffer_bytes_max = (128*1024), .period_bytes_min = 64, .period_bytes_max = (128*1024), .periods_min = 1, .periods_max = 1024, .fifo_size = 0, }; static void snd_gf1_pcm_playback_free(struct snd_pcm_runtime *runtime) { kfree(runtime->private_data); } static int snd_gf1_pcm_playback_open(struct snd_pcm_substream *substream) { struct gus_pcm_private *pcmp; struct snd_gus_card *gus = snd_pcm_substream_chip(substream); struct snd_pcm_runtime *runtime = substream->runtime; int err; pcmp = kzalloc(sizeof(*pcmp), GFP_KERNEL); if (pcmp == NULL) return -ENOMEM; pcmp->gus = gus; spin_lock_init(&pcmp->lock); init_waitqueue_head(&pcmp->sleep); atomic_set(&pcmp->dma_count, 0); runtime->private_data = pcmp; runtime->private_free = snd_gf1_pcm_playback_free; #if 0 dev_dbg(gus->card->dev, "playback.buffer = 0x%lx, gf1.pcm_buffer = 0x%lx\n", (long) pcm->playback.buffer, (long) gus->gf1.pcm_buffer); #endif err = snd_gf1_dma_init(gus); if (err < 0) return err; pcmp->flags = SNDRV_GF1_PCM_PFLG_NONE; pcmp->substream = substream; runtime->hw = snd_gf1_pcm_playback; snd_pcm_limit_isa_dma_size(gus->gf1.dma1, &runtime->hw.buffer_bytes_max); snd_pcm_limit_isa_dma_size(gus->gf1.dma1, &runtime->hw.period_bytes_max); snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 64); return 0; } static int snd_gf1_pcm_playback_close(struct snd_pcm_substream *substream) { struct snd_gus_card *gus = snd_pcm_substream_chip(substream); struct snd_pcm_runtime *runtime = substream->runtime; struct gus_pcm_private *pcmp = runtime->private_data; if (!wait_event_timeout(pcmp->sleep, (atomic_read(&pcmp->dma_count) <= 0), 2*HZ)) dev_err(gus->card->dev, "gf1 pcm - serious DMA problem\n"); snd_gf1_dma_done(gus); return 0; } static int snd_gf1_pcm_capture_open(struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; struct snd_gus_card *gus = snd_pcm_substream_chip(substream); gus->gf1.interrupt_handler_dma_read = snd_gf1_pcm_interrupt_dma_read; gus->pcm_cap_substream = substream; substream->runtime->hw = snd_gf1_pcm_capture; snd_pcm_limit_isa_dma_size(gus->gf1.dma2, &runtime->hw.buffer_bytes_max); snd_pcm_limit_isa_dma_size(gus->gf1.dma2, &runtime->hw.period_bytes_max); snd_pcm_hw_constraint_ratnums(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_clocks); return 0; } static int snd_gf1_pcm_capture_close(struct snd_pcm_substream *substream) { struct snd_gus_card *gus = snd_pcm_substream_chip(substream); gus->pcm_cap_substream = NULL; snd_gf1_set_default_handlers(gus, SNDRV_GF1_HANDLER_DMA_READ); return 0; } static int snd_gf1_pcm_volume_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; uinfo->count = 2; uinfo->value.integer.min = 0; uinfo->value.integer.max = 127; return 0; } static int snd_gf1_pcm_volume_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_gus_card *gus = snd_kcontrol_chip(kcontrol); unsigned long flags; spin_lock_irqsave(&gus->pcm_volume_level_lock, flags); ucontrol->value.integer.value[0] = gus->gf1.pcm_volume_level_left1; ucontrol->value.integer.value[1] = gus->gf1.pcm_volume_level_right1; spin_unlock_irqrestore(&gus->pcm_volume_level_lock, flags); return 0; } static int snd_gf1_pcm_volume_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_gus_card *gus = snd_kcontrol_chip(kcontrol); unsigned long flags; int change; unsigned int idx; unsigned short val1, val2, vol; struct gus_pcm_private *pcmp; struct snd_gus_voice *pvoice; val1 = ucontrol->value.integer.value[0] & 127; val2 = ucontrol->value.integer.value[1] & 127; spin_lock_irqsave(&gus->pcm_volume_level_lock, flags); change = val1 != gus->gf1.pcm_volume_level_left1 || val2 != gus->gf1.pcm_volume_level_right1; gus->gf1.pcm_volume_level_left1 = val1; gus->gf1.pcm_volume_level_right1 = val2; gus->gf1.pcm_volume_level_left = snd_gf1_lvol_to_gvol_raw(val1 << 9) << 4; gus->gf1.pcm_volume_level_right = snd_gf1_lvol_to_gvol_raw(val2 << 9) << 4; spin_unlock_irqrestore(&gus->pcm_volume_level_lock, flags); /* are we active? */ spin_lock_irqsave(&gus->voice_alloc, flags); for (idx = 0; idx < 32; idx++) { pvoice = &gus->gf1.voices[idx]; if (!pvoice->pcm) continue; pcmp = pvoice->private_data; if (!(pcmp->flags & SNDRV_GF1_PCM_PFLG_ACTIVE)) continue; /* load real volume - better precision */ spin_lock(&gus->reg_lock); snd_gf1_select_voice(gus, pvoice->number); snd_gf1_ctrl_stop(gus, SNDRV_GF1_VB_VOLUME_CONTROL); vol = pvoice == pcmp->pvoices[0] ? gus->gf1.pcm_volume_level_left : gus->gf1.pcm_volume_level_right; snd_gf1_write16(gus, SNDRV_GF1_VW_VOLUME, vol); pcmp->final_volume = 1; spin_unlock(&gus->reg_lock); } spin_unlock_irqrestore(&gus->voice_alloc, flags); return change; } static const struct snd_kcontrol_new snd_gf1_pcm_volume_control = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "PCM Playback Volume", .info = snd_gf1_pcm_volume_info, .get = snd_gf1_pcm_volume_get, .put = snd_gf1_pcm_volume_put }; static const struct snd_kcontrol_new snd_gf1_pcm_volume_control1 = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "GPCM Playback Volume", .info = snd_gf1_pcm_volume_info, .get = snd_gf1_pcm_volume_get, .put = snd_gf1_pcm_volume_put }; static const struct snd_pcm_ops snd_gf1_pcm_playback_ops = { .open = snd_gf1_pcm_playback_open, .close = snd_gf1_pcm_playback_close, .hw_params = snd_gf1_pcm_playback_hw_params, .hw_free = snd_gf1_pcm_playback_hw_free, .prepare = snd_gf1_pcm_playback_prepare, .trigger = snd_gf1_pcm_playback_trigger, .pointer = snd_gf1_pcm_playback_pointer, .copy = snd_gf1_pcm_playback_copy, .fill_silence = snd_gf1_pcm_playback_silence, }; static const struct snd_pcm_ops snd_gf1_pcm_capture_ops = { .open = snd_gf1_pcm_capture_open, .close = snd_gf1_pcm_capture_close, .hw_params = snd_gf1_pcm_capture_hw_params, .prepare = snd_gf1_pcm_capture_prepare, .trigger = snd_gf1_pcm_capture_trigger, .pointer = snd_gf1_pcm_capture_pointer, }; int snd_gf1_pcm_new(struct snd_gus_card *gus, int pcm_dev, int control_index) { struct snd_card *card; struct snd_kcontrol *kctl; struct snd_pcm *pcm; struct snd_pcm_substream *substream; int capture, err; card = gus->card; capture = !gus->interwave && !gus->ess_flag && !gus->ace_flag ? 1 : 0; err = snd_pcm_new(card, gus->interwave ? "AMD InterWave" : "GF1", pcm_dev, gus->gf1.pcm_channels / 2, capture, &pcm); if (err < 0) return err; pcm->private_data = gus; /* playback setup */ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_gf1_pcm_playback_ops); for (substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; substream; substream = substream->next) snd_pcm_set_managed_buffer(substream, SNDRV_DMA_TYPE_DEV, card->dev, 64*1024, gus->gf1.dma1 > 3 ? 128*1024 : 64*1024); pcm->info_flags = 0; pcm->dev_subclass = SNDRV_PCM_SUBCLASS_GENERIC_MIX; if (capture) { snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_gf1_pcm_capture_ops); if (gus->gf1.dma2 == gus->gf1.dma1) pcm->info_flags |= SNDRV_PCM_INFO_HALF_DUPLEX; snd_pcm_set_managed_buffer(pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream, SNDRV_DMA_TYPE_DEV, card->dev, 64*1024, gus->gf1.dma2 > 3 ? 128*1024 : 64*1024); } strscpy(pcm->name, pcm->id); if (gus->interwave) { sprintf(pcm->name + strlen(pcm->name), " rev %c", gus->revision + 'A'); } strcat(pcm->name, " (synth)"); gus->pcm = pcm; if (gus->codec_flag) kctl = snd_ctl_new1(&snd_gf1_pcm_volume_control1, gus); else kctl = snd_ctl_new1(&snd_gf1_pcm_volume_control, gus); kctl->id.index = control_index; err = snd_ctl_add(card, kctl); if (err < 0) return err; return 0; }
2026-04-07
Neuigkeiten
Aktuelles
Motto des Tages
Software
Produkte
Quellcodebibliothek
Aktivitäten
Artikel über Sicherheit
Anleitung zur Aktivierung von SSL
Muße
Gedichte
Musik
Bilder
Jenseits des Üblichen ....
Besucherstatistik
Monitoring
Impressum
|
Ethik und Gesetz
|
Haftungsausschluß
|
Kontakt
|
Seitenstruktur
|
©
2026 JDD
|