sound/isa/gus/gus_pcm.c

Source file repositories/reference/linux-study-clean/sound/isa/gus/gus_pcm.c

File Facts

System
Linux kernel
Corpus path
sound/isa/gus/gus_pcm.c
Extension
.c
Size
27500 bytes
Lines
863
Domain
Driver Families
Bucket
sound/isa
Inferred role
Driver Families: implementation source
Status
source implementation candidate

Why This File Exists

Repeatable hardware-adapter layer. Deep compatibility for every driver is out of scope; this atlas records patterns, probe lifecycles, bus glue, IRQ/DMA usage, and links back to core abstractions.

Dependency Surface

Detected Declarations

Annotated Snippet

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 char voice_ctrl, ramp_ctrl;
	unsigned short rate;
	unsigned int curr, begin, end;
	unsigned short vol;
	unsigned char pan;
	unsigned int voice;

	scoped_guard(spinlock_irqsave, &pcmp->lock) {
		if (pcmp->flags & SNDRV_GF1_PCM_PFLG_ACTIVE)
			return;
		pcmp->flags |= SNDRV_GF1_PCM_PFLG_ACTIVE;
		pcmp->final_volume = 0;
	}
	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;

Annotation

Implementation Notes