xref: /openbmc/linux/sound/isa/gus/gus_pcm.c (revision fac59652993f075d57860769c99045b3ca18780d)
11a59d1b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
21da177e4SLinus Torvalds /*
3c1017a4cSJaroslav Kysela  *  Copyright (c) by Jaroslav Kysela <perex@perex.cz>
41da177e4SLinus Torvalds  *  Routines for control of GF1 chip (PCM things)
51da177e4SLinus Torvalds  *
61da177e4SLinus Torvalds  *  InterWave chips supports interleaved DMA, but this feature isn't used in
71da177e4SLinus Torvalds  *  this code.
81da177e4SLinus Torvalds  *
91da177e4SLinus Torvalds  *  This code emulates autoinit DMA transfer for playback, recording by GF1
101da177e4SLinus Torvalds  *  chip doesn't support autoinit DMA.
111da177e4SLinus Torvalds  */
121da177e4SLinus Torvalds 
131da177e4SLinus Torvalds #include <asm/dma.h>
141da177e4SLinus Torvalds #include <linux/slab.h>
15174cd4b1SIngo Molnar #include <linux/sched/signal.h>
16174cd4b1SIngo Molnar 
171da177e4SLinus Torvalds #include <sound/core.h>
181da177e4SLinus Torvalds #include <sound/control.h>
191da177e4SLinus Torvalds #include <sound/gus.h>
201da177e4SLinus Torvalds #include <sound/pcm_params.h>
211da177e4SLinus Torvalds #include "gus_tables.h"
221da177e4SLinus Torvalds 
231da177e4SLinus Torvalds /* maximum rate */
241da177e4SLinus Torvalds 
251da177e4SLinus Torvalds #define SNDRV_GF1_PCM_RATE		48000
261da177e4SLinus Torvalds 
271da177e4SLinus Torvalds #define SNDRV_GF1_PCM_PFLG_NONE		0
281da177e4SLinus Torvalds #define SNDRV_GF1_PCM_PFLG_ACTIVE	(1<<0)
291da177e4SLinus Torvalds #define SNDRV_GF1_PCM_PFLG_NEUTRAL	(2<<0)
301da177e4SLinus Torvalds 
315e2da206STakashi Iwai struct gus_pcm_private {
325e2da206STakashi Iwai 	struct snd_gus_card * gus;
335e2da206STakashi Iwai 	struct snd_pcm_substream *substream;
341da177e4SLinus Torvalds 	spinlock_t lock;
351da177e4SLinus Torvalds 	unsigned int voices;
365e2da206STakashi Iwai 	struct snd_gus_voice *pvoices[2];
371da177e4SLinus Torvalds 	unsigned int memory;
381da177e4SLinus Torvalds 	unsigned short flags;
391da177e4SLinus Torvalds 	unsigned char voice_ctrl, ramp_ctrl;
401da177e4SLinus Torvalds 	unsigned int bpos;
411da177e4SLinus Torvalds 	unsigned int blocks;
421da177e4SLinus Torvalds 	unsigned int block_size;
431da177e4SLinus Torvalds 	unsigned int dma_size;
441da177e4SLinus Torvalds 	wait_queue_head_t sleep;
451da177e4SLinus Torvalds 	atomic_t dma_count;
461da177e4SLinus Torvalds 	int final_volume;
475e2da206STakashi Iwai };
481da177e4SLinus Torvalds 
snd_gf1_pcm_block_change_ack(struct snd_gus_card * gus,void * private_data)495e2da206STakashi Iwai static void snd_gf1_pcm_block_change_ack(struct snd_gus_card * gus, void *private_data)
501da177e4SLinus Torvalds {
515e2da206STakashi Iwai 	struct gus_pcm_private *pcmp = private_data;
521da177e4SLinus Torvalds 
531da177e4SLinus Torvalds 	if (pcmp) {
541da177e4SLinus Torvalds 		atomic_dec(&pcmp->dma_count);
551da177e4SLinus Torvalds 		wake_up(&pcmp->sleep);
561da177e4SLinus Torvalds 	}
571da177e4SLinus Torvalds }
581da177e4SLinus Torvalds 
snd_gf1_pcm_block_change(struct snd_pcm_substream * substream,unsigned int offset,unsigned int addr,unsigned int count)595e2da206STakashi Iwai static int snd_gf1_pcm_block_change(struct snd_pcm_substream *substream,
601da177e4SLinus Torvalds 				    unsigned int offset,
611da177e4SLinus Torvalds 				    unsigned int addr,
621da177e4SLinus Torvalds 				    unsigned int count)
631da177e4SLinus Torvalds {
645e2da206STakashi Iwai 	struct snd_gf1_dma_block block;
655e2da206STakashi Iwai 	struct snd_pcm_runtime *runtime = substream->runtime;
665e2da206STakashi Iwai 	struct gus_pcm_private *pcmp = runtime->private_data;
671da177e4SLinus Torvalds 
681da177e4SLinus Torvalds 	count += offset & 31;
691da177e4SLinus Torvalds 	offset &= ~31;
7091f05060STakashi Iwai 	/*
7191f05060STakashi Iwai 	snd_printk(KERN_DEBUG "block change - offset = 0x%x, count = 0x%x\n",
7291f05060STakashi Iwai 		   offset, count);
7391f05060STakashi Iwai 	*/
741da177e4SLinus Torvalds 	memset(&block, 0, sizeof(block));
751da177e4SLinus Torvalds 	block.cmd = SNDRV_GF1_DMA_IRQ;
761da177e4SLinus Torvalds 	if (snd_pcm_format_unsigned(runtime->format))
771da177e4SLinus Torvalds 		block.cmd |= SNDRV_GF1_DMA_UNSIGNED;
781da177e4SLinus Torvalds 	if (snd_pcm_format_width(runtime->format) == 16)
791da177e4SLinus Torvalds 		block.cmd |= SNDRV_GF1_DMA_16BIT;
801da177e4SLinus Torvalds 	block.addr = addr & ~31;
811da177e4SLinus Torvalds 	block.buffer = runtime->dma_area + offset;
821da177e4SLinus Torvalds 	block.buf_addr = runtime->dma_addr + offset;
831da177e4SLinus Torvalds 	block.count = count;
841da177e4SLinus Torvalds 	block.private_data = pcmp;
851da177e4SLinus Torvalds 	block.ack = snd_gf1_pcm_block_change_ack;
861da177e4SLinus Torvalds 	if (!snd_gf1_dma_transfer_block(pcmp->gus, &block, 0, 0))
871da177e4SLinus Torvalds 		atomic_inc(&pcmp->dma_count);
881da177e4SLinus Torvalds 	return 0;
891da177e4SLinus Torvalds }
901da177e4SLinus Torvalds 
snd_gf1_pcm_trigger_up(struct snd_pcm_substream * substream)915e2da206STakashi Iwai static void snd_gf1_pcm_trigger_up(struct snd_pcm_substream *substream)
921da177e4SLinus Torvalds {
935e2da206STakashi Iwai 	struct snd_pcm_runtime *runtime = substream->runtime;
945e2da206STakashi Iwai 	struct gus_pcm_private *pcmp = runtime->private_data;
955e2da206STakashi Iwai 	struct snd_gus_card * gus = pcmp->gus;
961da177e4SLinus Torvalds 	unsigned long flags;
971da177e4SLinus Torvalds 	unsigned char voice_ctrl, ramp_ctrl;
981da177e4SLinus Torvalds 	unsigned short rate;
991da177e4SLinus Torvalds 	unsigned int curr, begin, end;
1001da177e4SLinus Torvalds 	unsigned short vol;
1011da177e4SLinus Torvalds 	unsigned char pan;
1021da177e4SLinus Torvalds 	unsigned int voice;
1031da177e4SLinus Torvalds 
1041da177e4SLinus Torvalds 	spin_lock_irqsave(&pcmp->lock, flags);
1051da177e4SLinus Torvalds 	if (pcmp->flags & SNDRV_GF1_PCM_PFLG_ACTIVE) {
1061da177e4SLinus Torvalds 		spin_unlock_irqrestore(&pcmp->lock, flags);
1071da177e4SLinus Torvalds 		return;
1081da177e4SLinus Torvalds 	}
1091da177e4SLinus Torvalds 	pcmp->flags |= SNDRV_GF1_PCM_PFLG_ACTIVE;
1101da177e4SLinus Torvalds 	pcmp->final_volume = 0;
1111da177e4SLinus Torvalds 	spin_unlock_irqrestore(&pcmp->lock, flags);
1121da177e4SLinus Torvalds 	rate = snd_gf1_translate_freq(gus, runtime->rate << 4);
1131da177e4SLinus Torvalds 	/* enable WAVE IRQ */
1141da177e4SLinus Torvalds 	voice_ctrl = snd_pcm_format_width(runtime->format) == 16 ? 0x24 : 0x20;
1151da177e4SLinus Torvalds 	/* enable RAMP IRQ + rollover */
1161da177e4SLinus Torvalds 	ramp_ctrl = 0x24;
1171da177e4SLinus Torvalds 	if (pcmp->blocks == 1) {
1181da177e4SLinus Torvalds 		voice_ctrl |= 0x08;	/* loop enable */
1191da177e4SLinus Torvalds 		ramp_ctrl &= ~0x04;	/* disable rollover */
1201da177e4SLinus Torvalds 	}
1211da177e4SLinus Torvalds 	for (voice = 0; voice < pcmp->voices; voice++) {
1221da177e4SLinus Torvalds 		begin = pcmp->memory + voice * (pcmp->dma_size / runtime->channels);
1231da177e4SLinus Torvalds 		curr = begin + (pcmp->bpos * pcmp->block_size) / runtime->channels;
1241da177e4SLinus Torvalds 		end = curr + (pcmp->block_size / runtime->channels);
1251da177e4SLinus Torvalds 		end -= snd_pcm_format_width(runtime->format) == 16 ? 2 : 1;
12691f05060STakashi Iwai 		/*
12791f05060STakashi Iwai 		snd_printk(KERN_DEBUG "init: curr=0x%x, begin=0x%x, end=0x%x, "
12891f05060STakashi Iwai 			   "ctrl=0x%x, ramp=0x%x, rate=0x%x\n",
12991f05060STakashi Iwai 			   curr, begin, end, voice_ctrl, ramp_ctrl, rate);
13091f05060STakashi Iwai 		*/
1311da177e4SLinus Torvalds 		pan = runtime->channels == 2 ? (!voice ? 1 : 14) : 8;
1321da177e4SLinus Torvalds 		vol = !voice ? gus->gf1.pcm_volume_level_left : gus->gf1.pcm_volume_level_right;
1331da177e4SLinus Torvalds 		spin_lock_irqsave(&gus->reg_lock, flags);
1341da177e4SLinus Torvalds 		snd_gf1_select_voice(gus, pcmp->pvoices[voice]->number);
1351da177e4SLinus Torvalds 		snd_gf1_write8(gus, SNDRV_GF1_VB_PAN, pan);
1361da177e4SLinus Torvalds 		snd_gf1_write16(gus, SNDRV_GF1_VW_FREQUENCY, rate);
1371da177e4SLinus Torvalds 		snd_gf1_write_addr(gus, SNDRV_GF1_VA_START, begin << 4, voice_ctrl & 4);
1381da177e4SLinus Torvalds 		snd_gf1_write_addr(gus, SNDRV_GF1_VA_END, end << 4, voice_ctrl & 4);
1391da177e4SLinus Torvalds 		snd_gf1_write_addr(gus, SNDRV_GF1_VA_CURRENT, curr << 4, voice_ctrl & 4);
1401da177e4SLinus Torvalds 		snd_gf1_write16(gus, SNDRV_GF1_VW_VOLUME, SNDRV_GF1_MIN_VOLUME << 4);
1411da177e4SLinus Torvalds 		snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_RATE, 0x2f);
1421da177e4SLinus Torvalds 		snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_START, SNDRV_GF1_MIN_OFFSET);
1431da177e4SLinus Torvalds 		snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_END, vol >> 8);
1441da177e4SLinus Torvalds 		snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_CONTROL, ramp_ctrl);
1451da177e4SLinus Torvalds 		if (!gus->gf1.enh_mode) {
1461da177e4SLinus Torvalds 			snd_gf1_delay(gus);
1471da177e4SLinus Torvalds 			snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_CONTROL, ramp_ctrl);
1481da177e4SLinus Torvalds 		}
1491da177e4SLinus Torvalds 		spin_unlock_irqrestore(&gus->reg_lock, flags);
1501da177e4SLinus Torvalds 	}
1511da177e4SLinus Torvalds 	spin_lock_irqsave(&gus->reg_lock, flags);
1521da177e4SLinus Torvalds 	for (voice = 0; voice < pcmp->voices; voice++) {
1531da177e4SLinus Torvalds 		snd_gf1_select_voice(gus, pcmp->pvoices[voice]->number);
1541da177e4SLinus Torvalds 		if (gus->gf1.enh_mode)
1551da177e4SLinus Torvalds 			snd_gf1_write8(gus, SNDRV_GF1_VB_MODE, 0x00);	/* deactivate voice */
1561da177e4SLinus Torvalds 		snd_gf1_write8(gus, SNDRV_GF1_VB_ADDRESS_CONTROL, voice_ctrl);
1571da177e4SLinus Torvalds 		voice_ctrl &= ~0x20;
1581da177e4SLinus Torvalds 	}
1591da177e4SLinus Torvalds 	voice_ctrl |= 0x20;
1601da177e4SLinus Torvalds 	if (!gus->gf1.enh_mode) {
1611da177e4SLinus Torvalds 		snd_gf1_delay(gus);
1621da177e4SLinus Torvalds 		for (voice = 0; voice < pcmp->voices; voice++) {
1631da177e4SLinus Torvalds 			snd_gf1_select_voice(gus, pcmp->pvoices[voice]->number);
1641da177e4SLinus Torvalds 			snd_gf1_write8(gus, SNDRV_GF1_VB_ADDRESS_CONTROL, voice_ctrl);
1651da177e4SLinus Torvalds 			voice_ctrl &= ~0x20;	/* disable IRQ for next voice */
1661da177e4SLinus Torvalds 		}
1671da177e4SLinus Torvalds 	}
1681da177e4SLinus Torvalds 	spin_unlock_irqrestore(&gus->reg_lock, flags);
1691da177e4SLinus Torvalds }
1701da177e4SLinus Torvalds 
snd_gf1_pcm_interrupt_wave(struct snd_gus_card * gus,struct snd_gus_voice * pvoice)1715e2da206STakashi Iwai static void snd_gf1_pcm_interrupt_wave(struct snd_gus_card * gus,
1725e2da206STakashi Iwai 				       struct snd_gus_voice *pvoice)
1731da177e4SLinus Torvalds {
1745e2da206STakashi Iwai 	struct gus_pcm_private * pcmp;
1755e2da206STakashi Iwai 	struct snd_pcm_runtime *runtime;
1761da177e4SLinus Torvalds 	unsigned char voice_ctrl, ramp_ctrl;
1771da177e4SLinus Torvalds 	unsigned int idx;
1781da177e4SLinus Torvalds 	unsigned int end, step;
1791da177e4SLinus Torvalds 
1801da177e4SLinus Torvalds 	if (!pvoice->private_data) {
1811da177e4SLinus Torvalds 		snd_printd("snd_gf1_pcm: unknown wave irq?\n");
1821da177e4SLinus Torvalds 		snd_gf1_smart_stop_voice(gus, pvoice->number);
1831da177e4SLinus Torvalds 		return;
1841da177e4SLinus Torvalds 	}
1851da177e4SLinus Torvalds 	pcmp = pvoice->private_data;
1861da177e4SLinus Torvalds 	if (pcmp == NULL) {
1871da177e4SLinus Torvalds 		snd_printd("snd_gf1_pcm: unknown wave irq?\n");
1881da177e4SLinus Torvalds 		snd_gf1_smart_stop_voice(gus, pvoice->number);
1891da177e4SLinus Torvalds 		return;
1901da177e4SLinus Torvalds 	}
1911da177e4SLinus Torvalds 	gus = pcmp->gus;
1921da177e4SLinus Torvalds 	runtime = pcmp->substream->runtime;
1931da177e4SLinus Torvalds 
1941da177e4SLinus Torvalds 	spin_lock(&gus->reg_lock);
1951da177e4SLinus Torvalds 	snd_gf1_select_voice(gus, pvoice->number);
1961da177e4SLinus Torvalds 	voice_ctrl = snd_gf1_read8(gus, SNDRV_GF1_VB_ADDRESS_CONTROL) & ~0x8b;
1971da177e4SLinus Torvalds 	ramp_ctrl = (snd_gf1_read8(gus, SNDRV_GF1_VB_VOLUME_CONTROL) & ~0xa4) | 0x03;
1981da177e4SLinus Torvalds #if 0
1991da177e4SLinus Torvalds 	snd_gf1_select_voice(gus, pvoice->number);
20091f05060STakashi Iwai 	printk(KERN_DEBUG "position = 0x%x\n",
20191f05060STakashi Iwai 	       (snd_gf1_read_addr(gus, SNDRV_GF1_VA_CURRENT, voice_ctrl & 4) >> 4));
2021da177e4SLinus Torvalds 	snd_gf1_select_voice(gus, pcmp->pvoices[1]->number);
20391f05060STakashi Iwai 	printk(KERN_DEBUG "position = 0x%x\n",
20491f05060STakashi Iwai 	       (snd_gf1_read_addr(gus, SNDRV_GF1_VA_CURRENT, voice_ctrl & 4) >> 4));
2051da177e4SLinus Torvalds 	snd_gf1_select_voice(gus, pvoice->number);
2061da177e4SLinus Torvalds #endif
2071da177e4SLinus Torvalds 	pcmp->bpos++;
2081da177e4SLinus Torvalds 	pcmp->bpos %= pcmp->blocks;
2091da177e4SLinus Torvalds 	if (pcmp->bpos + 1 >= pcmp->blocks) {	/* last block? */
2101da177e4SLinus Torvalds 		voice_ctrl |= 0x08;	/* enable loop */
2111da177e4SLinus Torvalds 	} else {
2121da177e4SLinus Torvalds 		ramp_ctrl |= 0x04;	/* enable rollover */
2131da177e4SLinus Torvalds 	}
2141da177e4SLinus Torvalds 	end = pcmp->memory + (((pcmp->bpos + 1) * pcmp->block_size) / runtime->channels);
2151da177e4SLinus Torvalds 	end -= voice_ctrl & 4 ? 2 : 1;
2161da177e4SLinus Torvalds 	step = pcmp->dma_size / runtime->channels;
2171da177e4SLinus Torvalds 	voice_ctrl |= 0x20;
2181da177e4SLinus Torvalds 	if (!pcmp->final_volume) {
2191da177e4SLinus Torvalds 		ramp_ctrl |= 0x20;
2201da177e4SLinus Torvalds 		ramp_ctrl &= ~0x03;
2211da177e4SLinus Torvalds 	}
2221da177e4SLinus Torvalds 	for (idx = 0; idx < pcmp->voices; idx++, end += step) {
2231da177e4SLinus Torvalds 		snd_gf1_select_voice(gus, pcmp->pvoices[idx]->number);
2241da177e4SLinus Torvalds 		snd_gf1_write_addr(gus, SNDRV_GF1_VA_END, end << 4, voice_ctrl & 4);
2251da177e4SLinus Torvalds 		snd_gf1_write8(gus, SNDRV_GF1_VB_ADDRESS_CONTROL, voice_ctrl);
2261da177e4SLinus Torvalds 		snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_CONTROL, ramp_ctrl);
2271da177e4SLinus Torvalds 		voice_ctrl &= ~0x20;
2281da177e4SLinus Torvalds 	}
2291da177e4SLinus Torvalds 	if (!gus->gf1.enh_mode) {
2301da177e4SLinus Torvalds 		snd_gf1_delay(gus);
2311da177e4SLinus Torvalds 		voice_ctrl |= 0x20;
2321da177e4SLinus Torvalds 		for (idx = 0; idx < pcmp->voices; idx++) {
2331da177e4SLinus Torvalds 			snd_gf1_select_voice(gus, pcmp->pvoices[idx]->number);
2341da177e4SLinus Torvalds 			snd_gf1_write8(gus, SNDRV_GF1_VB_ADDRESS_CONTROL, voice_ctrl);
2351da177e4SLinus Torvalds 			snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_CONTROL, ramp_ctrl);
2361da177e4SLinus Torvalds 			voice_ctrl &= ~0x20;
2371da177e4SLinus Torvalds 		}
2381da177e4SLinus Torvalds 	}
2391da177e4SLinus Torvalds 	spin_unlock(&gus->reg_lock);
2401da177e4SLinus Torvalds 
2411da177e4SLinus Torvalds 	snd_pcm_period_elapsed(pcmp->substream);
2421da177e4SLinus Torvalds #if 0
2431da177e4SLinus Torvalds 	if ((runtime->flags & SNDRV_PCM_FLG_MMAP) &&
2441da177e4SLinus Torvalds 	    *runtime->state == SNDRV_PCM_STATE_RUNNING) {
2451da177e4SLinus Torvalds 		end = pcmp->bpos * pcmp->block_size;
2461da177e4SLinus Torvalds 		if (runtime->channels > 1) {
2471da177e4SLinus Torvalds 			snd_gf1_pcm_block_change(pcmp->substream, end, pcmp->memory + (end / 2), pcmp->block_size / 2);
2481da177e4SLinus Torvalds 			snd_gf1_pcm_block_change(pcmp->substream, end + (pcmp->block_size / 2), pcmp->memory + (pcmp->dma_size / 2) + (end / 2), pcmp->block_size / 2);
2491da177e4SLinus Torvalds 		} else {
2501da177e4SLinus Torvalds 			snd_gf1_pcm_block_change(pcmp->substream, end, pcmp->memory + end, pcmp->block_size);
2511da177e4SLinus Torvalds 		}
2521da177e4SLinus Torvalds 	}
2531da177e4SLinus Torvalds #endif
2541da177e4SLinus Torvalds }
2551da177e4SLinus Torvalds 
snd_gf1_pcm_interrupt_volume(struct snd_gus_card * gus,struct snd_gus_voice * pvoice)2565e2da206STakashi Iwai static void snd_gf1_pcm_interrupt_volume(struct snd_gus_card * gus,
2575e2da206STakashi Iwai 					 struct snd_gus_voice * pvoice)
2581da177e4SLinus Torvalds {
2591da177e4SLinus Torvalds 	unsigned short vol;
2601da177e4SLinus Torvalds 	int cvoice;
2615e2da206STakashi Iwai 	struct gus_pcm_private *pcmp = pvoice->private_data;
2621da177e4SLinus Torvalds 
2631da177e4SLinus Torvalds 	/* stop ramp, but leave rollover bit untouched */
2641da177e4SLinus Torvalds 	spin_lock(&gus->reg_lock);
2651da177e4SLinus Torvalds 	snd_gf1_select_voice(gus, pvoice->number);
2661da177e4SLinus Torvalds 	snd_gf1_ctrl_stop(gus, SNDRV_GF1_VB_VOLUME_CONTROL);
2671da177e4SLinus Torvalds 	spin_unlock(&gus->reg_lock);
2681da177e4SLinus Torvalds 	if (pcmp == NULL)
2691da177e4SLinus Torvalds 		return;
2701da177e4SLinus Torvalds 	/* are we active? */
2711da177e4SLinus Torvalds 	if (!(pcmp->flags & SNDRV_GF1_PCM_PFLG_ACTIVE))
2721da177e4SLinus Torvalds 		return;
2731da177e4SLinus Torvalds 	/* load real volume - better precision */
2741da177e4SLinus Torvalds 	cvoice = pcmp->pvoices[0] == pvoice ? 0 : 1;
2751da177e4SLinus Torvalds 	if (pcmp->substream == NULL)
2761da177e4SLinus Torvalds 		return;
2771da177e4SLinus Torvalds 	vol = !cvoice ? gus->gf1.pcm_volume_level_left : gus->gf1.pcm_volume_level_right;
2781da177e4SLinus Torvalds 	spin_lock(&gus->reg_lock);
2791da177e4SLinus Torvalds 	snd_gf1_select_voice(gus, pvoice->number);
2801da177e4SLinus Torvalds 	snd_gf1_write16(gus, SNDRV_GF1_VW_VOLUME, vol);
2811da177e4SLinus Torvalds 	pcmp->final_volume = 1;
2821da177e4SLinus Torvalds 	spin_unlock(&gus->reg_lock);
2831da177e4SLinus Torvalds }
2841da177e4SLinus Torvalds 
snd_gf1_pcm_volume_change(struct snd_gus_card * gus)2855e2da206STakashi Iwai static void snd_gf1_pcm_volume_change(struct snd_gus_card * gus)
2861da177e4SLinus Torvalds {
2871da177e4SLinus Torvalds }
2881da177e4SLinus Torvalds 
snd_gf1_pcm_poke_block(struct snd_gus_card * gus,unsigned char * buf,unsigned int pos,unsigned int count,int w16,int invert)2895e2da206STakashi Iwai static int snd_gf1_pcm_poke_block(struct snd_gus_card *gus, unsigned char *buf,
2901da177e4SLinus Torvalds 				  unsigned int pos, unsigned int count,
2911da177e4SLinus Torvalds 				  int w16, int invert)
2921da177e4SLinus Torvalds {
2931da177e4SLinus Torvalds 	unsigned int len;
2941da177e4SLinus Torvalds 	unsigned long flags;
2951da177e4SLinus Torvalds 
29691f05060STakashi Iwai 	/*
29791f05060STakashi Iwai 	printk(KERN_DEBUG
29891f05060STakashi Iwai 	       "poke block; buf = 0x%x, pos = %i, count = %i, port = 0x%x\n",
29991f05060STakashi Iwai 	       (int)buf, pos, count, gus->gf1.port);
30091f05060STakashi Iwai 	*/
3011da177e4SLinus Torvalds 	while (count > 0) {
3021da177e4SLinus Torvalds 		len = count;
3031da177e4SLinus Torvalds 		if (len > 512)		/* limit, to allow IRQ */
3041da177e4SLinus Torvalds 			len = 512;
3051da177e4SLinus Torvalds 		count -= len;
3061da177e4SLinus Torvalds 		if (gus->interwave) {
3071da177e4SLinus Torvalds 			spin_lock_irqsave(&gus->reg_lock, flags);
3081da177e4SLinus Torvalds 			snd_gf1_write8(gus, SNDRV_GF1_GB_MEMORY_CONTROL, 0x01 | (invert ? 0x08 : 0x00));
3091da177e4SLinus Torvalds 			snd_gf1_dram_addr(gus, pos);
3101da177e4SLinus Torvalds 			if (w16) {
3111da177e4SLinus Torvalds 				outb(SNDRV_GF1_GW_DRAM_IO16, GUSP(gus, GF1REGSEL));
3121da177e4SLinus Torvalds 				outsw(GUSP(gus, GF1DATALOW), buf, len >> 1);
3131da177e4SLinus Torvalds 			} else {
3141da177e4SLinus Torvalds 				outsb(GUSP(gus, DRAM), buf, len);
3151da177e4SLinus Torvalds 			}
3161da177e4SLinus Torvalds 			spin_unlock_irqrestore(&gus->reg_lock, flags);
3171da177e4SLinus Torvalds 			buf += 512;
3181da177e4SLinus Torvalds 			pos += 512;
3191da177e4SLinus Torvalds 		} else {
3201da177e4SLinus Torvalds 			invert = invert ? 0x80 : 0x00;
3211da177e4SLinus Torvalds 			if (w16) {
3221da177e4SLinus Torvalds 				len >>= 1;
3231da177e4SLinus Torvalds 				while (len--) {
3241da177e4SLinus Torvalds 					snd_gf1_poke(gus, pos++, *buf++);
3251da177e4SLinus Torvalds 					snd_gf1_poke(gus, pos++, *buf++ ^ invert);
3261da177e4SLinus Torvalds 				}
3271da177e4SLinus Torvalds 			} else {
3281da177e4SLinus Torvalds 				while (len--)
3291da177e4SLinus Torvalds 					snd_gf1_poke(gus, pos++, *buf++ ^ invert);
3301da177e4SLinus Torvalds 			}
3311da177e4SLinus Torvalds 		}
3321da177e4SLinus Torvalds 		if (count > 0 && !in_interrupt()) {
3338433a509SNishanth Aravamudan 			schedule_timeout_interruptible(1);
3341da177e4SLinus Torvalds 			if (signal_pending(current))
3351da177e4SLinus Torvalds 				return -EAGAIN;
3361da177e4SLinus Torvalds 		}
3371da177e4SLinus Torvalds 	}
3381da177e4SLinus Torvalds 	return 0;
3391da177e4SLinus Torvalds }
3401da177e4SLinus Torvalds 
get_bpos(struct gus_pcm_private * pcmp,int voice,unsigned int pos,unsigned int len)341a6970bb1STakashi Iwai static int get_bpos(struct gus_pcm_private *pcmp, int voice, unsigned int pos,
342a6970bb1STakashi Iwai 		    unsigned int len)
3431da177e4SLinus Torvalds {
344a6970bb1STakashi Iwai 	unsigned int bpos = pos + (voice * (pcmp->dma_size / 2));
345622207dcSTakashi Iwai 	if (snd_BUG_ON(bpos > pcmp->dma_size))
346622207dcSTakashi Iwai 		return -EIO;
347622207dcSTakashi Iwai 	if (snd_BUG_ON(bpos + len > pcmp->dma_size))
348622207dcSTakashi Iwai 		return -EIO;
349a6970bb1STakashi Iwai 	return bpos;
350a6970bb1STakashi Iwai }
351a6970bb1STakashi Iwai 
playback_copy_ack(struct snd_pcm_substream * substream,unsigned int bpos,unsigned int len)352a6970bb1STakashi Iwai static int playback_copy_ack(struct snd_pcm_substream *substream,
353a6970bb1STakashi Iwai 			     unsigned int bpos, unsigned int len)
354a6970bb1STakashi Iwai {
355a6970bb1STakashi Iwai 	struct snd_pcm_runtime *runtime = substream->runtime;
356a6970bb1STakashi Iwai 	struct gus_pcm_private *pcmp = runtime->private_data;
357a6970bb1STakashi Iwai 	struct snd_gus_card *gus = pcmp->gus;
358a6970bb1STakashi Iwai 	int w16, invert;
359a6970bb1STakashi Iwai 
360097a7fe3STakashi Sakamoto 	if (len > 32)
361097a7fe3STakashi Sakamoto 		return snd_gf1_pcm_block_change(substream, bpos,
362097a7fe3STakashi Sakamoto 						pcmp->memory + bpos, len);
3631da177e4SLinus Torvalds 
3641da177e4SLinus Torvalds 	w16 = (snd_pcm_format_width(runtime->format) == 16);
3651da177e4SLinus Torvalds 	invert = snd_pcm_format_unsigned(runtime->format);
366097a7fe3STakashi Sakamoto 	return snd_gf1_pcm_poke_block(gus, runtime->dma_area + bpos,
367097a7fe3STakashi Sakamoto 				      pcmp->memory + bpos, len, w16, invert);
3681da177e4SLinus Torvalds }
3691da177e4SLinus Torvalds 
snd_gf1_pcm_playback_copy(struct snd_pcm_substream * substream,int voice,unsigned long pos,struct iov_iter * src,unsigned long count)370a6970bb1STakashi Iwai static int snd_gf1_pcm_playback_copy(struct snd_pcm_substream *substream,
371a6970bb1STakashi Iwai 				     int voice, unsigned long pos,
372e2964cd7STakashi Iwai 				     struct iov_iter *src, unsigned long count)
3731da177e4SLinus Torvalds {
3745e2da206STakashi Iwai 	struct snd_pcm_runtime *runtime = substream->runtime;
3755e2da206STakashi Iwai 	struct gus_pcm_private *pcmp = runtime->private_data;
376a6970bb1STakashi Iwai 	unsigned int len = count;
377a6970bb1STakashi Iwai 	int bpos;
3781da177e4SLinus Torvalds 
379a6970bb1STakashi Iwai 	bpos = get_bpos(pcmp, voice, pos, len);
380a6970bb1STakashi Iwai 	if (bpos < 0)
381*b0f3c6a2SChristophe JAILLET 		return bpos;
382e2964cd7STakashi Iwai 	if (copy_from_iter(runtime->dma_area + bpos, len, src) != len)
383a6970bb1STakashi Iwai 		return -EFAULT;
384a6970bb1STakashi Iwai 	return playback_copy_ack(substream, bpos, len);
385a6970bb1STakashi Iwai }
386a6970bb1STakashi Iwai 
snd_gf1_pcm_playback_silence(struct snd_pcm_substream * substream,int voice,unsigned long pos,unsigned long count)387a6970bb1STakashi Iwai static int snd_gf1_pcm_playback_silence(struct snd_pcm_substream *substream,
388a6970bb1STakashi Iwai 					int voice, unsigned long pos,
389a6970bb1STakashi Iwai 					unsigned long count)
390a6970bb1STakashi Iwai {
391a6970bb1STakashi Iwai 	struct snd_pcm_runtime *runtime = substream->runtime;
392a6970bb1STakashi Iwai 	struct gus_pcm_private *pcmp = runtime->private_data;
393a6970bb1STakashi Iwai 	unsigned int len = count;
394a6970bb1STakashi Iwai 	int bpos;
395a6970bb1STakashi Iwai 
396a6970bb1STakashi Iwai 	bpos = get_bpos(pcmp, voice, pos, len);
397a6970bb1STakashi Iwai 	if (bpos < 0)
398*b0f3c6a2SChristophe JAILLET 		return bpos;
399097a7fe3STakashi Sakamoto 	snd_pcm_format_set_silence(runtime->format, runtime->dma_area + bpos,
400a6970bb1STakashi Iwai 				   bytes_to_samples(runtime, count));
401a6970bb1STakashi Iwai 	return playback_copy_ack(substream, bpos, len);
4021da177e4SLinus Torvalds }
4031da177e4SLinus Torvalds 
snd_gf1_pcm_playback_hw_params(struct snd_pcm_substream * substream,struct snd_pcm_hw_params * hw_params)4045e2da206STakashi Iwai static int snd_gf1_pcm_playback_hw_params(struct snd_pcm_substream *substream,
4055e2da206STakashi Iwai 					  struct snd_pcm_hw_params *hw_params)
4061da177e4SLinus Torvalds {
4075e2da206STakashi Iwai 	struct snd_gus_card *gus = snd_pcm_substream_chip(substream);
4085e2da206STakashi Iwai 	struct snd_pcm_runtime *runtime = substream->runtime;
4095e2da206STakashi Iwai 	struct gus_pcm_private *pcmp = runtime->private_data;
4101da177e4SLinus Torvalds 
411a57214e5STakashi Iwai 	if (runtime->buffer_changed) {
4125e2da206STakashi Iwai 		struct snd_gf1_mem_block *block;
4131da177e4SLinus Torvalds 		if (pcmp->memory > 0) {
4141da177e4SLinus Torvalds 			snd_gf1_mem_free(&gus->gf1.mem_alloc, pcmp->memory);
4151da177e4SLinus Torvalds 			pcmp->memory = 0;
4161da177e4SLinus Torvalds 		}
417310efd3aSTakashi Iwai 		block = snd_gf1_mem_alloc(&gus->gf1.mem_alloc,
4181da177e4SLinus Torvalds 					  SNDRV_GF1_MEM_OWNER_DRIVER,
4191da177e4SLinus Torvalds 					  "GF1 PCM",
4201da177e4SLinus Torvalds 					  runtime->dma_bytes, 1, 32,
421310efd3aSTakashi Iwai 					  NULL);
422310efd3aSTakashi Iwai 		if (!block)
4231da177e4SLinus Torvalds 			return -ENOMEM;
4241da177e4SLinus Torvalds 		pcmp->memory = block->ptr;
4251da177e4SLinus Torvalds 	}
4261da177e4SLinus Torvalds 	pcmp->voices = params_channels(hw_params);
4271da177e4SLinus Torvalds 	if (pcmp->pvoices[0] == NULL) {
428310efd3aSTakashi Iwai 		pcmp->pvoices[0] = snd_gf1_alloc_voice(pcmp->gus, SNDRV_GF1_VOICE_TYPE_PCM, 0, 0);
429310efd3aSTakashi Iwai 		if (!pcmp->pvoices[0])
4301da177e4SLinus Torvalds 			return -ENOMEM;
4311da177e4SLinus Torvalds 		pcmp->pvoices[0]->handler_wave = snd_gf1_pcm_interrupt_wave;
4321da177e4SLinus Torvalds 		pcmp->pvoices[0]->handler_volume = snd_gf1_pcm_interrupt_volume;
4331da177e4SLinus Torvalds 		pcmp->pvoices[0]->volume_change = snd_gf1_pcm_volume_change;
4341da177e4SLinus Torvalds 		pcmp->pvoices[0]->private_data = pcmp;
4351da177e4SLinus Torvalds 	}
4361da177e4SLinus Torvalds 	if (pcmp->voices > 1 && pcmp->pvoices[1] == NULL) {
437310efd3aSTakashi Iwai 		pcmp->pvoices[1] = snd_gf1_alloc_voice(pcmp->gus, SNDRV_GF1_VOICE_TYPE_PCM, 0, 0);
438310efd3aSTakashi Iwai 		if (!pcmp->pvoices[1])
4391da177e4SLinus Torvalds 			return -ENOMEM;
4401da177e4SLinus Torvalds 		pcmp->pvoices[1]->handler_wave = snd_gf1_pcm_interrupt_wave;
4411da177e4SLinus Torvalds 		pcmp->pvoices[1]->handler_volume = snd_gf1_pcm_interrupt_volume;
4421da177e4SLinus Torvalds 		pcmp->pvoices[1]->volume_change = snd_gf1_pcm_volume_change;
4431da177e4SLinus Torvalds 		pcmp->pvoices[1]->private_data = pcmp;
4441da177e4SLinus Torvalds 	} else if (pcmp->voices == 1) {
4451da177e4SLinus Torvalds 		if (pcmp->pvoices[1]) {
4461da177e4SLinus Torvalds 			snd_gf1_free_voice(pcmp->gus, pcmp->pvoices[1]);
4471da177e4SLinus Torvalds 			pcmp->pvoices[1] = NULL;
4481da177e4SLinus Torvalds 		}
4491da177e4SLinus Torvalds 	}
4501da177e4SLinus Torvalds 	return 0;
4511da177e4SLinus Torvalds }
4521da177e4SLinus Torvalds 
snd_gf1_pcm_playback_hw_free(struct snd_pcm_substream * substream)4535e2da206STakashi Iwai static int snd_gf1_pcm_playback_hw_free(struct snd_pcm_substream *substream)
4541da177e4SLinus Torvalds {
4555e2da206STakashi Iwai 	struct snd_pcm_runtime *runtime = substream->runtime;
4565e2da206STakashi Iwai 	struct gus_pcm_private *pcmp = runtime->private_data;
4571da177e4SLinus Torvalds 
4581da177e4SLinus Torvalds 	if (pcmp->pvoices[0]) {
4591da177e4SLinus Torvalds 		snd_gf1_free_voice(pcmp->gus, pcmp->pvoices[0]);
4601da177e4SLinus Torvalds 		pcmp->pvoices[0] = NULL;
4611da177e4SLinus Torvalds 	}
4621da177e4SLinus Torvalds 	if (pcmp->pvoices[1]) {
4631da177e4SLinus Torvalds 		snd_gf1_free_voice(pcmp->gus, pcmp->pvoices[1]);
4641da177e4SLinus Torvalds 		pcmp->pvoices[1] = NULL;
4651da177e4SLinus Torvalds 	}
4661da177e4SLinus Torvalds 	if (pcmp->memory > 0) {
4671da177e4SLinus Torvalds 		snd_gf1_mem_free(&pcmp->gus->gf1.mem_alloc, pcmp->memory);
4681da177e4SLinus Torvalds 		pcmp->memory = 0;
4691da177e4SLinus Torvalds 	}
4701da177e4SLinus Torvalds 	return 0;
4711da177e4SLinus Torvalds }
4721da177e4SLinus Torvalds 
snd_gf1_pcm_playback_prepare(struct snd_pcm_substream * substream)4735e2da206STakashi Iwai static int snd_gf1_pcm_playback_prepare(struct snd_pcm_substream *substream)
4741da177e4SLinus Torvalds {
4755e2da206STakashi Iwai 	struct snd_pcm_runtime *runtime = substream->runtime;
4765e2da206STakashi Iwai 	struct gus_pcm_private *pcmp = runtime->private_data;
4771da177e4SLinus Torvalds 
4781da177e4SLinus Torvalds 	pcmp->bpos = 0;
4791da177e4SLinus Torvalds 	pcmp->dma_size = snd_pcm_lib_buffer_bytes(substream);
4801da177e4SLinus Torvalds 	pcmp->block_size = snd_pcm_lib_period_bytes(substream);
4811da177e4SLinus Torvalds 	pcmp->blocks = pcmp->dma_size / pcmp->block_size;
4821da177e4SLinus Torvalds 	return 0;
4831da177e4SLinus Torvalds }
4841da177e4SLinus Torvalds 
snd_gf1_pcm_playback_trigger(struct snd_pcm_substream * substream,int cmd)4855e2da206STakashi Iwai static int snd_gf1_pcm_playback_trigger(struct snd_pcm_substream *substream,
4861da177e4SLinus Torvalds 					int cmd)
4871da177e4SLinus Torvalds {
4885e2da206STakashi Iwai 	struct snd_gus_card *gus = snd_pcm_substream_chip(substream);
4895e2da206STakashi Iwai 	struct snd_pcm_runtime *runtime = substream->runtime;
4905e2da206STakashi Iwai 	struct gus_pcm_private *pcmp = runtime->private_data;
4911da177e4SLinus Torvalds 	int voice;
4921da177e4SLinus Torvalds 
4931da177e4SLinus Torvalds 	if (cmd == SNDRV_PCM_TRIGGER_START) {
4941da177e4SLinus Torvalds 		snd_gf1_pcm_trigger_up(substream);
4951da177e4SLinus Torvalds 	} else if (cmd == SNDRV_PCM_TRIGGER_STOP) {
4961da177e4SLinus Torvalds 		spin_lock(&pcmp->lock);
4971da177e4SLinus Torvalds 		pcmp->flags &= ~SNDRV_GF1_PCM_PFLG_ACTIVE;
4981da177e4SLinus Torvalds 		spin_unlock(&pcmp->lock);
4991da177e4SLinus Torvalds 		voice = pcmp->pvoices[0]->number;
5001da177e4SLinus Torvalds 		snd_gf1_stop_voices(gus, voice, voice);
5011da177e4SLinus Torvalds 		if (pcmp->pvoices[1]) {
5021da177e4SLinus Torvalds 			voice = pcmp->pvoices[1]->number;
5031da177e4SLinus Torvalds 			snd_gf1_stop_voices(gus, voice, voice);
5041da177e4SLinus Torvalds 		}
5051da177e4SLinus Torvalds 	} else {
5061da177e4SLinus Torvalds 		return -EINVAL;
5071da177e4SLinus Torvalds 	}
5081da177e4SLinus Torvalds 	return 0;
5091da177e4SLinus Torvalds }
5101da177e4SLinus Torvalds 
snd_gf1_pcm_playback_pointer(struct snd_pcm_substream * substream)5115e2da206STakashi Iwai static snd_pcm_uframes_t snd_gf1_pcm_playback_pointer(struct snd_pcm_substream *substream)
5121da177e4SLinus Torvalds {
5135e2da206STakashi Iwai 	struct snd_gus_card *gus = snd_pcm_substream_chip(substream);
5145e2da206STakashi Iwai 	struct snd_pcm_runtime *runtime = substream->runtime;
5155e2da206STakashi Iwai 	struct gus_pcm_private *pcmp = runtime->private_data;
5161da177e4SLinus Torvalds 	unsigned int pos;
5171da177e4SLinus Torvalds 	unsigned char voice_ctrl;
5181da177e4SLinus Torvalds 
5191da177e4SLinus Torvalds 	pos = 0;
5201da177e4SLinus Torvalds 	spin_lock(&gus->reg_lock);
5211da177e4SLinus Torvalds 	if (pcmp->flags & SNDRV_GF1_PCM_PFLG_ACTIVE) {
5221da177e4SLinus Torvalds 		snd_gf1_select_voice(gus, pcmp->pvoices[0]->number);
5231da177e4SLinus Torvalds 		voice_ctrl = snd_gf1_read8(gus, SNDRV_GF1_VB_ADDRESS_CONTROL);
5241da177e4SLinus Torvalds 		pos = (snd_gf1_read_addr(gus, SNDRV_GF1_VA_CURRENT, voice_ctrl & 4) >> 4) - pcmp->memory;
5251da177e4SLinus Torvalds 		if (substream->runtime->channels > 1)
5261da177e4SLinus Torvalds 			pos <<= 1;
5271da177e4SLinus Torvalds 		pos = bytes_to_frames(runtime, pos);
5281da177e4SLinus Torvalds 	}
5291da177e4SLinus Torvalds 	spin_unlock(&gus->reg_lock);
5301da177e4SLinus Torvalds 	return pos;
5311da177e4SLinus Torvalds }
5321da177e4SLinus Torvalds 
533bfa516a1STakashi Iwai static const struct snd_ratnum clock = {
5341da177e4SLinus Torvalds 	.num = 9878400/16,
5351da177e4SLinus Torvalds 	.den_min = 2,
5361da177e4SLinus Torvalds 	.den_max = 257,
5371da177e4SLinus Torvalds 	.den_step = 1,
5381da177e4SLinus Torvalds };
5391da177e4SLinus Torvalds 
540bfa516a1STakashi Iwai static const struct snd_pcm_hw_constraint_ratnums hw_constraints_clocks  = {
5411da177e4SLinus Torvalds 	.nrats = 1,
5421da177e4SLinus Torvalds 	.rats = &clock,
5431da177e4SLinus Torvalds };
5441da177e4SLinus Torvalds 
snd_gf1_pcm_capture_hw_params(struct snd_pcm_substream * substream,struct snd_pcm_hw_params * hw_params)5455e2da206STakashi Iwai static int snd_gf1_pcm_capture_hw_params(struct snd_pcm_substream *substream,
5465e2da206STakashi Iwai 					 struct snd_pcm_hw_params *hw_params)
5471da177e4SLinus Torvalds {
5485e2da206STakashi Iwai 	struct snd_gus_card *gus = snd_pcm_substream_chip(substream);
5491da177e4SLinus Torvalds 
5501da177e4SLinus Torvalds 	gus->c_dma_size = params_buffer_bytes(hw_params);
5511da177e4SLinus Torvalds 	gus->c_period_size = params_period_bytes(hw_params);
5521da177e4SLinus Torvalds 	gus->c_pos = 0;
5531da177e4SLinus Torvalds 	gus->gf1.pcm_rcntrl_reg = 0x21;		/* IRQ at end, enable & start */
5541da177e4SLinus Torvalds 	if (params_channels(hw_params) > 1)
5551da177e4SLinus Torvalds 		gus->gf1.pcm_rcntrl_reg |= 2;
5561da177e4SLinus Torvalds 	if (gus->gf1.dma2 > 3)
5571da177e4SLinus Torvalds 		gus->gf1.pcm_rcntrl_reg |= 4;
5581da177e4SLinus Torvalds 	if (snd_pcm_format_unsigned(params_format(hw_params)))
5591da177e4SLinus Torvalds 		gus->gf1.pcm_rcntrl_reg |= 0x80;
560a57214e5STakashi Iwai 	return 0;
5611da177e4SLinus Torvalds }
5621da177e4SLinus Torvalds 
snd_gf1_pcm_capture_prepare(struct snd_pcm_substream * substream)5635e2da206STakashi Iwai static int snd_gf1_pcm_capture_prepare(struct snd_pcm_substream *substream)
5641da177e4SLinus Torvalds {
5655e2da206STakashi Iwai 	struct snd_gus_card *gus = snd_pcm_substream_chip(substream);
5665e2da206STakashi Iwai 	struct snd_pcm_runtime *runtime = substream->runtime;
5671da177e4SLinus Torvalds 
5681da177e4SLinus Torvalds 	snd_gf1_i_write8(gus, SNDRV_GF1_GB_RECORD_RATE, runtime->rate_den - 2);
5691da177e4SLinus Torvalds 	snd_gf1_i_write8(gus, SNDRV_GF1_GB_REC_DMA_CONTROL, 0);	/* disable sampling */
5701da177e4SLinus Torvalds 	snd_gf1_i_look8(gus, SNDRV_GF1_GB_REC_DMA_CONTROL);	/* Sampling Control Register */
5711da177e4SLinus Torvalds 	snd_dma_program(gus->gf1.dma2, runtime->dma_addr, gus->c_period_size, DMA_MODE_READ);
5721da177e4SLinus Torvalds 	return 0;
5731da177e4SLinus Torvalds }
5741da177e4SLinus Torvalds 
snd_gf1_pcm_capture_trigger(struct snd_pcm_substream * substream,int cmd)5755e2da206STakashi Iwai static int snd_gf1_pcm_capture_trigger(struct snd_pcm_substream *substream,
5761da177e4SLinus Torvalds 				       int cmd)
5771da177e4SLinus Torvalds {
5785e2da206STakashi Iwai 	struct snd_gus_card *gus = snd_pcm_substream_chip(substream);
5791da177e4SLinus Torvalds 	int val;
5801da177e4SLinus Torvalds 
5811da177e4SLinus Torvalds 	if (cmd == SNDRV_PCM_TRIGGER_START) {
5821da177e4SLinus Torvalds 		val = gus->gf1.pcm_rcntrl_reg;
5831da177e4SLinus Torvalds 	} else if (cmd == SNDRV_PCM_TRIGGER_STOP) {
5841da177e4SLinus Torvalds 		val = 0;
5851da177e4SLinus Torvalds 	} else {
5861da177e4SLinus Torvalds 		return -EINVAL;
5871da177e4SLinus Torvalds 	}
5881da177e4SLinus Torvalds 
5891da177e4SLinus Torvalds 	spin_lock(&gus->reg_lock);
5901da177e4SLinus Torvalds 	snd_gf1_write8(gus, SNDRV_GF1_GB_REC_DMA_CONTROL, val);
5911da177e4SLinus Torvalds 	snd_gf1_look8(gus, SNDRV_GF1_GB_REC_DMA_CONTROL);
5921da177e4SLinus Torvalds 	spin_unlock(&gus->reg_lock);
5931da177e4SLinus Torvalds 	return 0;
5941da177e4SLinus Torvalds }
5951da177e4SLinus Torvalds 
snd_gf1_pcm_capture_pointer(struct snd_pcm_substream * substream)5965e2da206STakashi Iwai static snd_pcm_uframes_t snd_gf1_pcm_capture_pointer(struct snd_pcm_substream *substream)
5971da177e4SLinus Torvalds {
5985e2da206STakashi Iwai 	struct snd_gus_card *gus = snd_pcm_substream_chip(substream);
5991da177e4SLinus Torvalds 	int pos = snd_dma_pointer(gus->gf1.dma2, gus->c_period_size);
6001da177e4SLinus Torvalds 	pos = bytes_to_frames(substream->runtime, (gus->c_pos + pos) % gus->c_dma_size);
6011da177e4SLinus Torvalds 	return pos;
6021da177e4SLinus Torvalds }
6031da177e4SLinus Torvalds 
snd_gf1_pcm_interrupt_dma_read(struct snd_gus_card * gus)6045e2da206STakashi Iwai static void snd_gf1_pcm_interrupt_dma_read(struct snd_gus_card * gus)
6051da177e4SLinus Torvalds {
6061da177e4SLinus Torvalds 	snd_gf1_i_write8(gus, SNDRV_GF1_GB_REC_DMA_CONTROL, 0);	/* disable sampling */
6071da177e4SLinus Torvalds 	snd_gf1_i_look8(gus, SNDRV_GF1_GB_REC_DMA_CONTROL);	/* Sampling Control Register */
6081da177e4SLinus Torvalds 	if (gus->pcm_cap_substream != NULL) {
6091da177e4SLinus Torvalds 		snd_gf1_pcm_capture_prepare(gus->pcm_cap_substream);
6101da177e4SLinus Torvalds 		snd_gf1_pcm_capture_trigger(gus->pcm_cap_substream, SNDRV_PCM_TRIGGER_START);
6111da177e4SLinus Torvalds 		gus->c_pos += gus->c_period_size;
6121da177e4SLinus Torvalds 		snd_pcm_period_elapsed(gus->pcm_cap_substream);
6131da177e4SLinus Torvalds 	}
6141da177e4SLinus Torvalds }
6151da177e4SLinus Torvalds 
616aec54654SBhumika Goyal static const struct snd_pcm_hardware snd_gf1_pcm_playback =
6171da177e4SLinus Torvalds {
6181da177e4SLinus Torvalds 	.info =			SNDRV_PCM_INFO_NONINTERLEAVED,
6191da177e4SLinus Torvalds 	.formats		= (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U8 |
6201da177e4SLinus Torvalds 				 SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE),
6211da177e4SLinus Torvalds 	.rates =		SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
6221da177e4SLinus Torvalds 	.rate_min =		5510,
6231da177e4SLinus Torvalds 	.rate_max =		48000,
6241da177e4SLinus Torvalds 	.channels_min =		1,
6251da177e4SLinus Torvalds 	.channels_max =		2,
6261da177e4SLinus Torvalds 	.buffer_bytes_max =	(128*1024),
6271da177e4SLinus Torvalds 	.period_bytes_min =	64,
6281da177e4SLinus Torvalds 	.period_bytes_max =	(128*1024),
6291da177e4SLinus Torvalds 	.periods_min =		1,
6301da177e4SLinus Torvalds 	.periods_max =		1024,
6311da177e4SLinus Torvalds 	.fifo_size =		0,
6321da177e4SLinus Torvalds };
6331da177e4SLinus Torvalds 
634aec54654SBhumika Goyal static const struct snd_pcm_hardware snd_gf1_pcm_capture =
6351da177e4SLinus Torvalds {
6361da177e4SLinus Torvalds 	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
6371da177e4SLinus Torvalds 				 SNDRV_PCM_INFO_MMAP_VALID),
6381da177e4SLinus Torvalds 	.formats =		SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U8,
6391da177e4SLinus Torvalds 	.rates =		SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_44100,
6401da177e4SLinus Torvalds 	.rate_min =		5510,
6411da177e4SLinus Torvalds 	.rate_max =		44100,
6421da177e4SLinus Torvalds 	.channels_min =		1,
6431da177e4SLinus Torvalds 	.channels_max =		2,
6441da177e4SLinus Torvalds 	.buffer_bytes_max =	(128*1024),
6451da177e4SLinus Torvalds 	.period_bytes_min =	64,
6461da177e4SLinus Torvalds 	.period_bytes_max =	(128*1024),
6471da177e4SLinus Torvalds 	.periods_min =		1,
6481da177e4SLinus Torvalds 	.periods_max =		1024,
6491da177e4SLinus Torvalds 	.fifo_size =		0,
6501da177e4SLinus Torvalds };
6511da177e4SLinus Torvalds 
snd_gf1_pcm_playback_free(struct snd_pcm_runtime * runtime)6525e2da206STakashi Iwai static void snd_gf1_pcm_playback_free(struct snd_pcm_runtime *runtime)
6531da177e4SLinus Torvalds {
6544d572776SJesper Juhl 	kfree(runtime->private_data);
6551da177e4SLinus Torvalds }
6561da177e4SLinus Torvalds 
snd_gf1_pcm_playback_open(struct snd_pcm_substream * substream)6575e2da206STakashi Iwai static int snd_gf1_pcm_playback_open(struct snd_pcm_substream *substream)
6581da177e4SLinus Torvalds {
6595e2da206STakashi Iwai 	struct gus_pcm_private *pcmp;
6605e2da206STakashi Iwai 	struct snd_gus_card *gus = snd_pcm_substream_chip(substream);
6615e2da206STakashi Iwai 	struct snd_pcm_runtime *runtime = substream->runtime;
6621da177e4SLinus Torvalds 	int err;
6631da177e4SLinus Torvalds 
6649e76a76eSTakashi Iwai 	pcmp = kzalloc(sizeof(*pcmp), GFP_KERNEL);
6651da177e4SLinus Torvalds 	if (pcmp == NULL)
6661da177e4SLinus Torvalds 		return -ENOMEM;
6671da177e4SLinus Torvalds 	pcmp->gus = gus;
6681da177e4SLinus Torvalds 	spin_lock_init(&pcmp->lock);
6691da177e4SLinus Torvalds 	init_waitqueue_head(&pcmp->sleep);
6701da177e4SLinus Torvalds 	atomic_set(&pcmp->dma_count, 0);
6711da177e4SLinus Torvalds 
6721da177e4SLinus Torvalds 	runtime->private_data = pcmp;
6731da177e4SLinus Torvalds 	runtime->private_free = snd_gf1_pcm_playback_free;
6741da177e4SLinus Torvalds 
6751da177e4SLinus Torvalds #if 0
67691f05060STakashi Iwai 	printk(KERN_DEBUG "playback.buffer = 0x%lx, gf1.pcm_buffer = 0x%lx\n",
67791f05060STakashi Iwai 	       (long) pcm->playback.buffer, (long) gus->gf1.pcm_buffer);
6781da177e4SLinus Torvalds #endif
679310efd3aSTakashi Iwai 	err = snd_gf1_dma_init(gus);
680310efd3aSTakashi Iwai 	if (err < 0)
6811da177e4SLinus Torvalds 		return err;
6821da177e4SLinus Torvalds 	pcmp->flags = SNDRV_GF1_PCM_PFLG_NONE;
6831da177e4SLinus Torvalds 	pcmp->substream = substream;
6841da177e4SLinus Torvalds 	runtime->hw = snd_gf1_pcm_playback;
6851da177e4SLinus Torvalds 	snd_pcm_limit_isa_dma_size(gus->gf1.dma1, &runtime->hw.buffer_bytes_max);
6861da177e4SLinus Torvalds 	snd_pcm_limit_isa_dma_size(gus->gf1.dma1, &runtime->hw.period_bytes_max);
6871da177e4SLinus Torvalds 	snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 64);
6881da177e4SLinus Torvalds 	return 0;
6891da177e4SLinus Torvalds }
6901da177e4SLinus Torvalds 
snd_gf1_pcm_playback_close(struct snd_pcm_substream * substream)6915e2da206STakashi Iwai static int snd_gf1_pcm_playback_close(struct snd_pcm_substream *substream)
6921da177e4SLinus Torvalds {
6935e2da206STakashi Iwai 	struct snd_gus_card *gus = snd_pcm_substream_chip(substream);
6945e2da206STakashi Iwai 	struct snd_pcm_runtime *runtime = substream->runtime;
6955e2da206STakashi Iwai 	struct gus_pcm_private *pcmp = runtime->private_data;
6961da177e4SLinus Torvalds 
6971da177e4SLinus Torvalds 	if (!wait_event_timeout(pcmp->sleep, (atomic_read(&pcmp->dma_count) <= 0), 2*HZ))
69899b359baSTakashi Iwai 		snd_printk(KERN_ERR "gf1 pcm - serious DMA problem\n");
6991da177e4SLinus Torvalds 
7001da177e4SLinus Torvalds 	snd_gf1_dma_done(gus);
7011da177e4SLinus Torvalds 	return 0;
7021da177e4SLinus Torvalds }
7031da177e4SLinus Torvalds 
snd_gf1_pcm_capture_open(struct snd_pcm_substream * substream)7045e2da206STakashi Iwai static int snd_gf1_pcm_capture_open(struct snd_pcm_substream *substream)
7051da177e4SLinus Torvalds {
7065e2da206STakashi Iwai 	struct snd_pcm_runtime *runtime = substream->runtime;
7075e2da206STakashi Iwai 	struct snd_gus_card *gus = snd_pcm_substream_chip(substream);
7081da177e4SLinus Torvalds 
7091da177e4SLinus Torvalds 	gus->gf1.interrupt_handler_dma_read = snd_gf1_pcm_interrupt_dma_read;
7101da177e4SLinus Torvalds 	gus->pcm_cap_substream = substream;
7111da177e4SLinus Torvalds 	substream->runtime->hw = snd_gf1_pcm_capture;
7121da177e4SLinus Torvalds 	snd_pcm_limit_isa_dma_size(gus->gf1.dma2, &runtime->hw.buffer_bytes_max);
7131da177e4SLinus Torvalds 	snd_pcm_limit_isa_dma_size(gus->gf1.dma2, &runtime->hw.period_bytes_max);
7141da177e4SLinus Torvalds 	snd_pcm_hw_constraint_ratnums(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
7151da177e4SLinus Torvalds 				      &hw_constraints_clocks);
7161da177e4SLinus Torvalds 	return 0;
7171da177e4SLinus Torvalds }
7181da177e4SLinus Torvalds 
snd_gf1_pcm_capture_close(struct snd_pcm_substream * substream)7195e2da206STakashi Iwai static int snd_gf1_pcm_capture_close(struct snd_pcm_substream *substream)
7201da177e4SLinus Torvalds {
7215e2da206STakashi Iwai 	struct snd_gus_card *gus = snd_pcm_substream_chip(substream);
7221da177e4SLinus Torvalds 
7231da177e4SLinus Torvalds 	gus->pcm_cap_substream = NULL;
7241da177e4SLinus Torvalds 	snd_gf1_set_default_handlers(gus, SNDRV_GF1_HANDLER_DMA_READ);
7251da177e4SLinus Torvalds 	return 0;
7261da177e4SLinus Torvalds }
7271da177e4SLinus Torvalds 
snd_gf1_pcm_volume_info(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_info * uinfo)7285e2da206STakashi Iwai static int snd_gf1_pcm_volume_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
7291da177e4SLinus Torvalds {
7301da177e4SLinus Torvalds 	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
7311da177e4SLinus Torvalds 	uinfo->count = 2;
7321da177e4SLinus Torvalds 	uinfo->value.integer.min = 0;
7331da177e4SLinus Torvalds 	uinfo->value.integer.max = 127;
7341da177e4SLinus Torvalds 	return 0;
7351da177e4SLinus Torvalds }
7361da177e4SLinus Torvalds 
snd_gf1_pcm_volume_get(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)7375e2da206STakashi Iwai static int snd_gf1_pcm_volume_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
7381da177e4SLinus Torvalds {
7395e2da206STakashi Iwai 	struct snd_gus_card *gus = snd_kcontrol_chip(kcontrol);
7401da177e4SLinus Torvalds 	unsigned long flags;
7411da177e4SLinus Torvalds 
7421da177e4SLinus Torvalds 	spin_lock_irqsave(&gus->pcm_volume_level_lock, flags);
7431da177e4SLinus Torvalds 	ucontrol->value.integer.value[0] = gus->gf1.pcm_volume_level_left1;
7441da177e4SLinus Torvalds 	ucontrol->value.integer.value[1] = gus->gf1.pcm_volume_level_right1;
7451da177e4SLinus Torvalds 	spin_unlock_irqrestore(&gus->pcm_volume_level_lock, flags);
7461da177e4SLinus Torvalds 	return 0;
7471da177e4SLinus Torvalds }
7481da177e4SLinus Torvalds 
snd_gf1_pcm_volume_put(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)7495e2da206STakashi Iwai static int snd_gf1_pcm_volume_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
7501da177e4SLinus Torvalds {
7515e2da206STakashi Iwai 	struct snd_gus_card *gus = snd_kcontrol_chip(kcontrol);
7521da177e4SLinus Torvalds 	unsigned long flags;
7531da177e4SLinus Torvalds 	int change;
7541da177e4SLinus Torvalds 	unsigned int idx;
7551da177e4SLinus Torvalds 	unsigned short val1, val2, vol;
7565e2da206STakashi Iwai 	struct gus_pcm_private *pcmp;
7575e2da206STakashi Iwai 	struct snd_gus_voice *pvoice;
7581da177e4SLinus Torvalds 
7591da177e4SLinus Torvalds 	val1 = ucontrol->value.integer.value[0] & 127;
7601da177e4SLinus Torvalds 	val2 = ucontrol->value.integer.value[1] & 127;
7611da177e4SLinus Torvalds 	spin_lock_irqsave(&gus->pcm_volume_level_lock, flags);
7621da177e4SLinus Torvalds 	change = val1 != gus->gf1.pcm_volume_level_left1 ||
7631da177e4SLinus Torvalds 	         val2 != gus->gf1.pcm_volume_level_right1;
7641da177e4SLinus Torvalds 	gus->gf1.pcm_volume_level_left1 = val1;
7651da177e4SLinus Torvalds 	gus->gf1.pcm_volume_level_right1 = val2;
7661da177e4SLinus Torvalds 	gus->gf1.pcm_volume_level_left = snd_gf1_lvol_to_gvol_raw(val1 << 9) << 4;
7671da177e4SLinus Torvalds 	gus->gf1.pcm_volume_level_right = snd_gf1_lvol_to_gvol_raw(val2 << 9) << 4;
7681da177e4SLinus Torvalds 	spin_unlock_irqrestore(&gus->pcm_volume_level_lock, flags);
7691da177e4SLinus Torvalds 	/* are we active? */
7701da177e4SLinus Torvalds 	spin_lock_irqsave(&gus->voice_alloc, flags);
7711da177e4SLinus Torvalds 	for (idx = 0; idx < 32; idx++) {
7721da177e4SLinus Torvalds 		pvoice = &gus->gf1.voices[idx];
7731da177e4SLinus Torvalds 		if (!pvoice->pcm)
7741da177e4SLinus Torvalds 			continue;
7751da177e4SLinus Torvalds 		pcmp = pvoice->private_data;
7761da177e4SLinus Torvalds 		if (!(pcmp->flags & SNDRV_GF1_PCM_PFLG_ACTIVE))
7771da177e4SLinus Torvalds 			continue;
7781da177e4SLinus Torvalds 		/* load real volume - better precision */
779fcb2954bSJulia Lawall 		spin_lock(&gus->reg_lock);
7801da177e4SLinus Torvalds 		snd_gf1_select_voice(gus, pvoice->number);
7811da177e4SLinus Torvalds 		snd_gf1_ctrl_stop(gus, SNDRV_GF1_VB_VOLUME_CONTROL);
7821da177e4SLinus Torvalds 		vol = pvoice == pcmp->pvoices[0] ? gus->gf1.pcm_volume_level_left : gus->gf1.pcm_volume_level_right;
7831da177e4SLinus Torvalds 		snd_gf1_write16(gus, SNDRV_GF1_VW_VOLUME, vol);
7841da177e4SLinus Torvalds 		pcmp->final_volume = 1;
785fcb2954bSJulia Lawall 		spin_unlock(&gus->reg_lock);
7861da177e4SLinus Torvalds 	}
7871da177e4SLinus Torvalds 	spin_unlock_irqrestore(&gus->voice_alloc, flags);
7881da177e4SLinus Torvalds 	return change;
7891da177e4SLinus Torvalds }
7901da177e4SLinus Torvalds 
7913a84d6c9SBhumika Goyal static const struct snd_kcontrol_new snd_gf1_pcm_volume_control =
7921da177e4SLinus Torvalds {
7931da177e4SLinus Torvalds 	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
7941da177e4SLinus Torvalds 	.name = "PCM Playback Volume",
7951da177e4SLinus Torvalds 	.info = snd_gf1_pcm_volume_info,
7961da177e4SLinus Torvalds 	.get = snd_gf1_pcm_volume_get,
7971da177e4SLinus Torvalds 	.put = snd_gf1_pcm_volume_put
7981da177e4SLinus Torvalds };
7991da177e4SLinus Torvalds 
8003a84d6c9SBhumika Goyal static const struct snd_kcontrol_new snd_gf1_pcm_volume_control1 =
8011da177e4SLinus Torvalds {
8021da177e4SLinus Torvalds 	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
8031da177e4SLinus Torvalds 	.name = "GPCM Playback Volume",
8041da177e4SLinus Torvalds 	.info = snd_gf1_pcm_volume_info,
8051da177e4SLinus Torvalds 	.get = snd_gf1_pcm_volume_get,
8061da177e4SLinus Torvalds 	.put = snd_gf1_pcm_volume_put
8071da177e4SLinus Torvalds };
8081da177e4SLinus Torvalds 
809ca58ba6cSArvind Yadav static const struct snd_pcm_ops snd_gf1_pcm_playback_ops = {
8101da177e4SLinus Torvalds 	.open =		snd_gf1_pcm_playback_open,
8111da177e4SLinus Torvalds 	.close =	snd_gf1_pcm_playback_close,
8121da177e4SLinus Torvalds 	.hw_params =	snd_gf1_pcm_playback_hw_params,
8131da177e4SLinus Torvalds 	.hw_free =	snd_gf1_pcm_playback_hw_free,
8141da177e4SLinus Torvalds 	.prepare =	snd_gf1_pcm_playback_prepare,
8151da177e4SLinus Torvalds 	.trigger =	snd_gf1_pcm_playback_trigger,
8161da177e4SLinus Torvalds 	.pointer =	snd_gf1_pcm_playback_pointer,
817e2964cd7STakashi Iwai 	.copy =		snd_gf1_pcm_playback_copy,
818a6970bb1STakashi Iwai 	.fill_silence =	snd_gf1_pcm_playback_silence,
8191da177e4SLinus Torvalds };
8201da177e4SLinus Torvalds 
821ca58ba6cSArvind Yadav static const struct snd_pcm_ops snd_gf1_pcm_capture_ops = {
8221da177e4SLinus Torvalds 	.open =		snd_gf1_pcm_capture_open,
8231da177e4SLinus Torvalds 	.close =	snd_gf1_pcm_capture_close,
8241da177e4SLinus Torvalds 	.hw_params =	snd_gf1_pcm_capture_hw_params,
8251da177e4SLinus Torvalds 	.prepare =	snd_gf1_pcm_capture_prepare,
8261da177e4SLinus Torvalds 	.trigger =	snd_gf1_pcm_capture_trigger,
8271da177e4SLinus Torvalds 	.pointer =	snd_gf1_pcm_capture_pointer,
8281da177e4SLinus Torvalds };
8291da177e4SLinus Torvalds 
snd_gf1_pcm_new(struct snd_gus_card * gus,int pcm_dev,int control_index)830db5abb3cSLars-Peter Clausen int snd_gf1_pcm_new(struct snd_gus_card *gus, int pcm_dev, int control_index)
8311da177e4SLinus Torvalds {
8325e2da206STakashi Iwai 	struct snd_card *card;
8335e2da206STakashi Iwai 	struct snd_kcontrol *kctl;
8345e2da206STakashi Iwai 	struct snd_pcm *pcm;
8355e2da206STakashi Iwai 	struct snd_pcm_substream *substream;
8361da177e4SLinus Torvalds 	int capture, err;
8371da177e4SLinus Torvalds 
8381da177e4SLinus Torvalds 	card = gus->card;
8391da177e4SLinus Torvalds 	capture = !gus->interwave && !gus->ess_flag && !gus->ace_flag ? 1 : 0;
8401da177e4SLinus Torvalds 	err = snd_pcm_new(card,
8411da177e4SLinus Torvalds 			  gus->interwave ? "AMD InterWave" : "GF1",
8421da177e4SLinus Torvalds 			  pcm_dev,
8431da177e4SLinus Torvalds 			  gus->gf1.pcm_channels / 2,
8441da177e4SLinus Torvalds 			  capture,
8451da177e4SLinus Torvalds 			  &pcm);
8461da177e4SLinus Torvalds 	if (err < 0)
8471da177e4SLinus Torvalds 		return err;
8481da177e4SLinus Torvalds 	pcm->private_data = gus;
8491da177e4SLinus Torvalds 	/* playback setup */
8501da177e4SLinus Torvalds 	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_gf1_pcm_playback_ops);
8511da177e4SLinus Torvalds 
8521da177e4SLinus Torvalds 	for (substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; substream; substream = substream->next)
853a57214e5STakashi Iwai 		snd_pcm_set_managed_buffer(substream, SNDRV_DMA_TYPE_DEV,
8540b6a2c9cSTakashi Iwai 					   card->dev,
8551da177e4SLinus Torvalds 					   64*1024, gus->gf1.dma1 > 3 ? 128*1024 : 64*1024);
8561da177e4SLinus Torvalds 
8571da177e4SLinus Torvalds 	pcm->info_flags = 0;
8581da177e4SLinus Torvalds 	pcm->dev_subclass = SNDRV_PCM_SUBCLASS_GENERIC_MIX;
8591da177e4SLinus Torvalds 	if (capture) {
8601da177e4SLinus Torvalds 		snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_gf1_pcm_capture_ops);
8611da177e4SLinus Torvalds 		if (gus->gf1.dma2 == gus->gf1.dma1)
8621da177e4SLinus Torvalds 			pcm->info_flags |= SNDRV_PCM_INFO_HALF_DUPLEX;
863a57214e5STakashi Iwai 		snd_pcm_set_managed_buffer(pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream,
8640b6a2c9cSTakashi Iwai 					   SNDRV_DMA_TYPE_DEV, card->dev,
8651da177e4SLinus Torvalds 					   64*1024, gus->gf1.dma2 > 3 ? 128*1024 : 64*1024);
8661da177e4SLinus Torvalds 	}
8671da177e4SLinus Torvalds 	strcpy(pcm->name, pcm->id);
8681da177e4SLinus Torvalds 	if (gus->interwave) {
8691da177e4SLinus Torvalds 		sprintf(pcm->name + strlen(pcm->name), " rev %c", gus->revision + 'A');
8701da177e4SLinus Torvalds 	}
8711da177e4SLinus Torvalds 	strcat(pcm->name, " (synth)");
8721da177e4SLinus Torvalds 	gus->pcm = pcm;
8731da177e4SLinus Torvalds 
8741da177e4SLinus Torvalds 	if (gus->codec_flag)
8751da177e4SLinus Torvalds 		kctl = snd_ctl_new1(&snd_gf1_pcm_volume_control1, gus);
8761da177e4SLinus Torvalds 	else
8771da177e4SLinus Torvalds 		kctl = snd_ctl_new1(&snd_gf1_pcm_volume_control, gus);
878c5ae57b1STakashi Iwai 	kctl->id.index = control_index;
879310efd3aSTakashi Iwai 	err = snd_ctl_add(card, kctl);
880310efd3aSTakashi Iwai 	if (err < 0)
8811da177e4SLinus Torvalds 		return err;
8821da177e4SLinus Torvalds 
8831da177e4SLinus Torvalds 	return 0;
8841da177e4SLinus Torvalds }
8851da177e4SLinus Torvalds 
886