xref: /openbmc/linux/sound/isa/gus/gus_pcm.c (revision 0b6a2c9cf4a00f54a0916499ece8a5cf3cced385)
11da177e4SLinus Torvalds /*
2c1017a4cSJaroslav Kysela  *  Copyright (c) by Jaroslav Kysela <perex@perex.cz>
31da177e4SLinus Torvalds  *  Routines for control of GF1 chip (PCM things)
41da177e4SLinus Torvalds  *
51da177e4SLinus Torvalds  *  InterWave chips supports interleaved DMA, but this feature isn't used in
61da177e4SLinus Torvalds  *  this code.
71da177e4SLinus Torvalds  *
81da177e4SLinus Torvalds  *  This code emulates autoinit DMA transfer for playback, recording by GF1
91da177e4SLinus Torvalds  *  chip doesn't support autoinit DMA.
101da177e4SLinus Torvalds  *
111da177e4SLinus Torvalds  *
121da177e4SLinus Torvalds  *   This program is free software; you can redistribute it and/or modify
131da177e4SLinus Torvalds  *   it under the terms of the GNU General Public License as published by
141da177e4SLinus Torvalds  *   the Free Software Foundation; either version 2 of the License, or
151da177e4SLinus Torvalds  *   (at your option) any later version.
161da177e4SLinus Torvalds  *
171da177e4SLinus Torvalds  *   This program is distributed in the hope that it will be useful,
181da177e4SLinus Torvalds  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
191da177e4SLinus Torvalds  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
201da177e4SLinus Torvalds  *   GNU General Public License for more details.
211da177e4SLinus Torvalds  *
221da177e4SLinus Torvalds  *   You should have received a copy of the GNU General Public License
231da177e4SLinus Torvalds  *   along with this program; if not, write to the Free Software
241da177e4SLinus Torvalds  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
251da177e4SLinus Torvalds  *
261da177e4SLinus Torvalds  */
271da177e4SLinus Torvalds 
281da177e4SLinus Torvalds #include <asm/dma.h>
291da177e4SLinus Torvalds #include <linux/slab.h>
30174cd4b1SIngo Molnar #include <linux/sched/signal.h>
31174cd4b1SIngo Molnar 
321da177e4SLinus Torvalds #include <sound/core.h>
331da177e4SLinus Torvalds #include <sound/control.h>
341da177e4SLinus Torvalds #include <sound/gus.h>
351da177e4SLinus Torvalds #include <sound/pcm_params.h>
361da177e4SLinus Torvalds #include "gus_tables.h"
371da177e4SLinus Torvalds 
381da177e4SLinus Torvalds /* maximum rate */
391da177e4SLinus Torvalds 
401da177e4SLinus Torvalds #define SNDRV_GF1_PCM_RATE		48000
411da177e4SLinus Torvalds 
421da177e4SLinus Torvalds #define SNDRV_GF1_PCM_PFLG_NONE		0
431da177e4SLinus Torvalds #define SNDRV_GF1_PCM_PFLG_ACTIVE	(1<<0)
441da177e4SLinus Torvalds #define SNDRV_GF1_PCM_PFLG_NEUTRAL	(2<<0)
451da177e4SLinus Torvalds 
465e2da206STakashi Iwai struct gus_pcm_private {
475e2da206STakashi Iwai 	struct snd_gus_card * gus;
485e2da206STakashi Iwai 	struct snd_pcm_substream *substream;
491da177e4SLinus Torvalds 	spinlock_t lock;
501da177e4SLinus Torvalds 	unsigned int voices;
515e2da206STakashi Iwai 	struct snd_gus_voice *pvoices[2];
521da177e4SLinus Torvalds 	unsigned int memory;
531da177e4SLinus Torvalds 	unsigned short flags;
541da177e4SLinus Torvalds 	unsigned char voice_ctrl, ramp_ctrl;
551da177e4SLinus Torvalds 	unsigned int bpos;
561da177e4SLinus Torvalds 	unsigned int blocks;
571da177e4SLinus Torvalds 	unsigned int block_size;
581da177e4SLinus Torvalds 	unsigned int dma_size;
591da177e4SLinus Torvalds 	wait_queue_head_t sleep;
601da177e4SLinus Torvalds 	atomic_t dma_count;
611da177e4SLinus Torvalds 	int final_volume;
625e2da206STakashi Iwai };
631da177e4SLinus Torvalds 
645e2da206STakashi Iwai static void snd_gf1_pcm_block_change_ack(struct snd_gus_card * gus, void *private_data)
651da177e4SLinus Torvalds {
665e2da206STakashi Iwai 	struct gus_pcm_private *pcmp = private_data;
671da177e4SLinus Torvalds 
681da177e4SLinus Torvalds 	if (pcmp) {
691da177e4SLinus Torvalds 		atomic_dec(&pcmp->dma_count);
701da177e4SLinus Torvalds 		wake_up(&pcmp->sleep);
711da177e4SLinus Torvalds 	}
721da177e4SLinus Torvalds }
731da177e4SLinus Torvalds 
745e2da206STakashi Iwai static int snd_gf1_pcm_block_change(struct snd_pcm_substream *substream,
751da177e4SLinus Torvalds 				    unsigned int offset,
761da177e4SLinus Torvalds 				    unsigned int addr,
771da177e4SLinus Torvalds 				    unsigned int count)
781da177e4SLinus Torvalds {
795e2da206STakashi Iwai 	struct snd_gf1_dma_block block;
805e2da206STakashi Iwai 	struct snd_pcm_runtime *runtime = substream->runtime;
815e2da206STakashi Iwai 	struct gus_pcm_private *pcmp = runtime->private_data;
821da177e4SLinus Torvalds 
831da177e4SLinus Torvalds 	count += offset & 31;
841da177e4SLinus Torvalds 	offset &= ~31;
8591f05060STakashi Iwai 	/*
8691f05060STakashi Iwai 	snd_printk(KERN_DEBUG "block change - offset = 0x%x, count = 0x%x\n",
8791f05060STakashi Iwai 		   offset, count);
8891f05060STakashi Iwai 	*/
891da177e4SLinus Torvalds 	memset(&block, 0, sizeof(block));
901da177e4SLinus Torvalds 	block.cmd = SNDRV_GF1_DMA_IRQ;
911da177e4SLinus Torvalds 	if (snd_pcm_format_unsigned(runtime->format))
921da177e4SLinus Torvalds 		block.cmd |= SNDRV_GF1_DMA_UNSIGNED;
931da177e4SLinus Torvalds 	if (snd_pcm_format_width(runtime->format) == 16)
941da177e4SLinus Torvalds 		block.cmd |= SNDRV_GF1_DMA_16BIT;
951da177e4SLinus Torvalds 	block.addr = addr & ~31;
961da177e4SLinus Torvalds 	block.buffer = runtime->dma_area + offset;
971da177e4SLinus Torvalds 	block.buf_addr = runtime->dma_addr + offset;
981da177e4SLinus Torvalds 	block.count = count;
991da177e4SLinus Torvalds 	block.private_data = pcmp;
1001da177e4SLinus Torvalds 	block.ack = snd_gf1_pcm_block_change_ack;
1011da177e4SLinus Torvalds 	if (!snd_gf1_dma_transfer_block(pcmp->gus, &block, 0, 0))
1021da177e4SLinus Torvalds 		atomic_inc(&pcmp->dma_count);
1031da177e4SLinus Torvalds 	return 0;
1041da177e4SLinus Torvalds }
1051da177e4SLinus Torvalds 
1065e2da206STakashi Iwai static void snd_gf1_pcm_trigger_up(struct snd_pcm_substream *substream)
1071da177e4SLinus Torvalds {
1085e2da206STakashi Iwai 	struct snd_pcm_runtime *runtime = substream->runtime;
1095e2da206STakashi Iwai 	struct gus_pcm_private *pcmp = runtime->private_data;
1105e2da206STakashi Iwai 	struct snd_gus_card * gus = pcmp->gus;
1111da177e4SLinus Torvalds 	unsigned long flags;
1121da177e4SLinus Torvalds 	unsigned char voice_ctrl, ramp_ctrl;
1131da177e4SLinus Torvalds 	unsigned short rate;
1141da177e4SLinus Torvalds 	unsigned int curr, begin, end;
1151da177e4SLinus Torvalds 	unsigned short vol;
1161da177e4SLinus Torvalds 	unsigned char pan;
1171da177e4SLinus Torvalds 	unsigned int voice;
1181da177e4SLinus Torvalds 
1191da177e4SLinus Torvalds 	spin_lock_irqsave(&pcmp->lock, flags);
1201da177e4SLinus Torvalds 	if (pcmp->flags & SNDRV_GF1_PCM_PFLG_ACTIVE) {
1211da177e4SLinus Torvalds 		spin_unlock_irqrestore(&pcmp->lock, flags);
1221da177e4SLinus Torvalds 		return;
1231da177e4SLinus Torvalds 	}
1241da177e4SLinus Torvalds 	pcmp->flags |= SNDRV_GF1_PCM_PFLG_ACTIVE;
1251da177e4SLinus Torvalds 	pcmp->final_volume = 0;
1261da177e4SLinus Torvalds 	spin_unlock_irqrestore(&pcmp->lock, flags);
1271da177e4SLinus Torvalds 	rate = snd_gf1_translate_freq(gus, runtime->rate << 4);
1281da177e4SLinus Torvalds 	/* enable WAVE IRQ */
1291da177e4SLinus Torvalds 	voice_ctrl = snd_pcm_format_width(runtime->format) == 16 ? 0x24 : 0x20;
1301da177e4SLinus Torvalds 	/* enable RAMP IRQ + rollover */
1311da177e4SLinus Torvalds 	ramp_ctrl = 0x24;
1321da177e4SLinus Torvalds 	if (pcmp->blocks == 1) {
1331da177e4SLinus Torvalds 		voice_ctrl |= 0x08;	/* loop enable */
1341da177e4SLinus Torvalds 		ramp_ctrl &= ~0x04;	/* disable rollover */
1351da177e4SLinus Torvalds 	}
1361da177e4SLinus Torvalds 	for (voice = 0; voice < pcmp->voices; voice++) {
1371da177e4SLinus Torvalds 		begin = pcmp->memory + voice * (pcmp->dma_size / runtime->channels);
1381da177e4SLinus Torvalds 		curr = begin + (pcmp->bpos * pcmp->block_size) / runtime->channels;
1391da177e4SLinus Torvalds 		end = curr + (pcmp->block_size / runtime->channels);
1401da177e4SLinus Torvalds 		end -= snd_pcm_format_width(runtime->format) == 16 ? 2 : 1;
14191f05060STakashi Iwai 		/*
14291f05060STakashi Iwai 		snd_printk(KERN_DEBUG "init: curr=0x%x, begin=0x%x, end=0x%x, "
14391f05060STakashi Iwai 			   "ctrl=0x%x, ramp=0x%x, rate=0x%x\n",
14491f05060STakashi Iwai 			   curr, begin, end, voice_ctrl, ramp_ctrl, rate);
14591f05060STakashi Iwai 		*/
1461da177e4SLinus Torvalds 		pan = runtime->channels == 2 ? (!voice ? 1 : 14) : 8;
1471da177e4SLinus Torvalds 		vol = !voice ? gus->gf1.pcm_volume_level_left : gus->gf1.pcm_volume_level_right;
1481da177e4SLinus Torvalds 		spin_lock_irqsave(&gus->reg_lock, flags);
1491da177e4SLinus Torvalds 		snd_gf1_select_voice(gus, pcmp->pvoices[voice]->number);
1501da177e4SLinus Torvalds 		snd_gf1_write8(gus, SNDRV_GF1_VB_PAN, pan);
1511da177e4SLinus Torvalds 		snd_gf1_write16(gus, SNDRV_GF1_VW_FREQUENCY, rate);
1521da177e4SLinus Torvalds 		snd_gf1_write_addr(gus, SNDRV_GF1_VA_START, begin << 4, voice_ctrl & 4);
1531da177e4SLinus Torvalds 		snd_gf1_write_addr(gus, SNDRV_GF1_VA_END, end << 4, voice_ctrl & 4);
1541da177e4SLinus Torvalds 		snd_gf1_write_addr(gus, SNDRV_GF1_VA_CURRENT, curr << 4, voice_ctrl & 4);
1551da177e4SLinus Torvalds 		snd_gf1_write16(gus, SNDRV_GF1_VW_VOLUME, SNDRV_GF1_MIN_VOLUME << 4);
1561da177e4SLinus Torvalds 		snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_RATE, 0x2f);
1571da177e4SLinus Torvalds 		snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_START, SNDRV_GF1_MIN_OFFSET);
1581da177e4SLinus Torvalds 		snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_END, vol >> 8);
1591da177e4SLinus Torvalds 		snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_CONTROL, ramp_ctrl);
1601da177e4SLinus Torvalds 		if (!gus->gf1.enh_mode) {
1611da177e4SLinus Torvalds 			snd_gf1_delay(gus);
1621da177e4SLinus Torvalds 			snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_CONTROL, ramp_ctrl);
1631da177e4SLinus Torvalds 		}
1641da177e4SLinus Torvalds 		spin_unlock_irqrestore(&gus->reg_lock, flags);
1651da177e4SLinus Torvalds 	}
1661da177e4SLinus Torvalds 	spin_lock_irqsave(&gus->reg_lock, flags);
1671da177e4SLinus Torvalds 	for (voice = 0; voice < pcmp->voices; voice++) {
1681da177e4SLinus Torvalds 		snd_gf1_select_voice(gus, pcmp->pvoices[voice]->number);
1691da177e4SLinus Torvalds 		if (gus->gf1.enh_mode)
1701da177e4SLinus Torvalds 			snd_gf1_write8(gus, SNDRV_GF1_VB_MODE, 0x00);	/* deactivate voice */
1711da177e4SLinus Torvalds 		snd_gf1_write8(gus, SNDRV_GF1_VB_ADDRESS_CONTROL, voice_ctrl);
1721da177e4SLinus Torvalds 		voice_ctrl &= ~0x20;
1731da177e4SLinus Torvalds 	}
1741da177e4SLinus Torvalds 	voice_ctrl |= 0x20;
1751da177e4SLinus Torvalds 	if (!gus->gf1.enh_mode) {
1761da177e4SLinus Torvalds 		snd_gf1_delay(gus);
1771da177e4SLinus Torvalds 		for (voice = 0; voice < pcmp->voices; voice++) {
1781da177e4SLinus Torvalds 			snd_gf1_select_voice(gus, pcmp->pvoices[voice]->number);
1791da177e4SLinus Torvalds 			snd_gf1_write8(gus, SNDRV_GF1_VB_ADDRESS_CONTROL, voice_ctrl);
1801da177e4SLinus Torvalds 			voice_ctrl &= ~0x20;	/* disable IRQ for next voice */
1811da177e4SLinus Torvalds 		}
1821da177e4SLinus Torvalds 	}
1831da177e4SLinus Torvalds 	spin_unlock_irqrestore(&gus->reg_lock, flags);
1841da177e4SLinus Torvalds }
1851da177e4SLinus Torvalds 
1865e2da206STakashi Iwai static void snd_gf1_pcm_interrupt_wave(struct snd_gus_card * gus,
1875e2da206STakashi Iwai 				       struct snd_gus_voice *pvoice)
1881da177e4SLinus Torvalds {
1895e2da206STakashi Iwai 	struct gus_pcm_private * pcmp;
1905e2da206STakashi Iwai 	struct snd_pcm_runtime *runtime;
1911da177e4SLinus Torvalds 	unsigned char voice_ctrl, ramp_ctrl;
1921da177e4SLinus Torvalds 	unsigned int idx;
1931da177e4SLinus Torvalds 	unsigned int end, step;
1941da177e4SLinus Torvalds 
1951da177e4SLinus Torvalds 	if (!pvoice->private_data) {
1961da177e4SLinus Torvalds 		snd_printd("snd_gf1_pcm: unknown wave irq?\n");
1971da177e4SLinus Torvalds 		snd_gf1_smart_stop_voice(gus, pvoice->number);
1981da177e4SLinus Torvalds 		return;
1991da177e4SLinus Torvalds 	}
2001da177e4SLinus Torvalds 	pcmp = pvoice->private_data;
2011da177e4SLinus Torvalds 	if (pcmp == NULL) {
2021da177e4SLinus Torvalds 		snd_printd("snd_gf1_pcm: unknown wave irq?\n");
2031da177e4SLinus Torvalds 		snd_gf1_smart_stop_voice(gus, pvoice->number);
2041da177e4SLinus Torvalds 		return;
2051da177e4SLinus Torvalds 	}
2061da177e4SLinus Torvalds 	gus = pcmp->gus;
2071da177e4SLinus Torvalds 	runtime = pcmp->substream->runtime;
2081da177e4SLinus Torvalds 
2091da177e4SLinus Torvalds 	spin_lock(&gus->reg_lock);
2101da177e4SLinus Torvalds 	snd_gf1_select_voice(gus, pvoice->number);
2111da177e4SLinus Torvalds 	voice_ctrl = snd_gf1_read8(gus, SNDRV_GF1_VB_ADDRESS_CONTROL) & ~0x8b;
2121da177e4SLinus Torvalds 	ramp_ctrl = (snd_gf1_read8(gus, SNDRV_GF1_VB_VOLUME_CONTROL) & ~0xa4) | 0x03;
2131da177e4SLinus Torvalds #if 0
2141da177e4SLinus Torvalds 	snd_gf1_select_voice(gus, pvoice->number);
21591f05060STakashi Iwai 	printk(KERN_DEBUG "position = 0x%x\n",
21691f05060STakashi Iwai 	       (snd_gf1_read_addr(gus, SNDRV_GF1_VA_CURRENT, voice_ctrl & 4) >> 4));
2171da177e4SLinus Torvalds 	snd_gf1_select_voice(gus, pcmp->pvoices[1]->number);
21891f05060STakashi Iwai 	printk(KERN_DEBUG "position = 0x%x\n",
21991f05060STakashi Iwai 	       (snd_gf1_read_addr(gus, SNDRV_GF1_VA_CURRENT, voice_ctrl & 4) >> 4));
2201da177e4SLinus Torvalds 	snd_gf1_select_voice(gus, pvoice->number);
2211da177e4SLinus Torvalds #endif
2221da177e4SLinus Torvalds 	pcmp->bpos++;
2231da177e4SLinus Torvalds 	pcmp->bpos %= pcmp->blocks;
2241da177e4SLinus Torvalds 	if (pcmp->bpos + 1 >= pcmp->blocks) {	/* last block? */
2251da177e4SLinus Torvalds 		voice_ctrl |= 0x08;	/* enable loop */
2261da177e4SLinus Torvalds 	} else {
2271da177e4SLinus Torvalds 		ramp_ctrl |= 0x04;	/* enable rollover */
2281da177e4SLinus Torvalds 	}
2291da177e4SLinus Torvalds 	end = pcmp->memory + (((pcmp->bpos + 1) * pcmp->block_size) / runtime->channels);
2301da177e4SLinus Torvalds 	end -= voice_ctrl & 4 ? 2 : 1;
2311da177e4SLinus Torvalds 	step = pcmp->dma_size / runtime->channels;
2321da177e4SLinus Torvalds 	voice_ctrl |= 0x20;
2331da177e4SLinus Torvalds 	if (!pcmp->final_volume) {
2341da177e4SLinus Torvalds 		ramp_ctrl |= 0x20;
2351da177e4SLinus Torvalds 		ramp_ctrl &= ~0x03;
2361da177e4SLinus Torvalds 	}
2371da177e4SLinus Torvalds 	for (idx = 0; idx < pcmp->voices; idx++, end += step) {
2381da177e4SLinus Torvalds 		snd_gf1_select_voice(gus, pcmp->pvoices[idx]->number);
2391da177e4SLinus Torvalds 		snd_gf1_write_addr(gus, SNDRV_GF1_VA_END, end << 4, voice_ctrl & 4);
2401da177e4SLinus Torvalds 		snd_gf1_write8(gus, SNDRV_GF1_VB_ADDRESS_CONTROL, voice_ctrl);
2411da177e4SLinus Torvalds 		snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_CONTROL, ramp_ctrl);
2421da177e4SLinus Torvalds 		voice_ctrl &= ~0x20;
2431da177e4SLinus Torvalds 	}
2441da177e4SLinus Torvalds 	if (!gus->gf1.enh_mode) {
2451da177e4SLinus Torvalds 		snd_gf1_delay(gus);
2461da177e4SLinus Torvalds 		voice_ctrl |= 0x20;
2471da177e4SLinus Torvalds 		for (idx = 0; idx < pcmp->voices; idx++) {
2481da177e4SLinus Torvalds 			snd_gf1_select_voice(gus, pcmp->pvoices[idx]->number);
2491da177e4SLinus Torvalds 			snd_gf1_write8(gus, SNDRV_GF1_VB_ADDRESS_CONTROL, voice_ctrl);
2501da177e4SLinus Torvalds 			snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_CONTROL, ramp_ctrl);
2511da177e4SLinus Torvalds 			voice_ctrl &= ~0x20;
2521da177e4SLinus Torvalds 		}
2531da177e4SLinus Torvalds 	}
2541da177e4SLinus Torvalds 	spin_unlock(&gus->reg_lock);
2551da177e4SLinus Torvalds 
2561da177e4SLinus Torvalds 	snd_pcm_period_elapsed(pcmp->substream);
2571da177e4SLinus Torvalds #if 0
2581da177e4SLinus Torvalds 	if ((runtime->flags & SNDRV_PCM_FLG_MMAP) &&
2591da177e4SLinus Torvalds 	    *runtime->state == SNDRV_PCM_STATE_RUNNING) {
2601da177e4SLinus Torvalds 		end = pcmp->bpos * pcmp->block_size;
2611da177e4SLinus Torvalds 		if (runtime->channels > 1) {
2621da177e4SLinus Torvalds 			snd_gf1_pcm_block_change(pcmp->substream, end, pcmp->memory + (end / 2), pcmp->block_size / 2);
2631da177e4SLinus 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);
2641da177e4SLinus Torvalds 		} else {
2651da177e4SLinus Torvalds 			snd_gf1_pcm_block_change(pcmp->substream, end, pcmp->memory + end, pcmp->block_size);
2661da177e4SLinus Torvalds 		}
2671da177e4SLinus Torvalds 	}
2681da177e4SLinus Torvalds #endif
2691da177e4SLinus Torvalds }
2701da177e4SLinus Torvalds 
2715e2da206STakashi Iwai static void snd_gf1_pcm_interrupt_volume(struct snd_gus_card * gus,
2725e2da206STakashi Iwai 					 struct snd_gus_voice * pvoice)
2731da177e4SLinus Torvalds {
2741da177e4SLinus Torvalds 	unsigned short vol;
2751da177e4SLinus Torvalds 	int cvoice;
2765e2da206STakashi Iwai 	struct gus_pcm_private *pcmp = pvoice->private_data;
2771da177e4SLinus Torvalds 
2781da177e4SLinus Torvalds 	/* stop ramp, but leave rollover bit untouched */
2791da177e4SLinus Torvalds 	spin_lock(&gus->reg_lock);
2801da177e4SLinus Torvalds 	snd_gf1_select_voice(gus, pvoice->number);
2811da177e4SLinus Torvalds 	snd_gf1_ctrl_stop(gus, SNDRV_GF1_VB_VOLUME_CONTROL);
2821da177e4SLinus Torvalds 	spin_unlock(&gus->reg_lock);
2831da177e4SLinus Torvalds 	if (pcmp == NULL)
2841da177e4SLinus Torvalds 		return;
2851da177e4SLinus Torvalds 	/* are we active? */
2861da177e4SLinus Torvalds 	if (!(pcmp->flags & SNDRV_GF1_PCM_PFLG_ACTIVE))
2871da177e4SLinus Torvalds 		return;
2881da177e4SLinus Torvalds 	/* load real volume - better precision */
2891da177e4SLinus Torvalds 	cvoice = pcmp->pvoices[0] == pvoice ? 0 : 1;
2901da177e4SLinus Torvalds 	if (pcmp->substream == NULL)
2911da177e4SLinus Torvalds 		return;
2921da177e4SLinus Torvalds 	vol = !cvoice ? gus->gf1.pcm_volume_level_left : gus->gf1.pcm_volume_level_right;
2931da177e4SLinus Torvalds 	spin_lock(&gus->reg_lock);
2941da177e4SLinus Torvalds 	snd_gf1_select_voice(gus, pvoice->number);
2951da177e4SLinus Torvalds 	snd_gf1_write16(gus, SNDRV_GF1_VW_VOLUME, vol);
2961da177e4SLinus Torvalds 	pcmp->final_volume = 1;
2971da177e4SLinus Torvalds 	spin_unlock(&gus->reg_lock);
2981da177e4SLinus Torvalds }
2991da177e4SLinus Torvalds 
3005e2da206STakashi Iwai static void snd_gf1_pcm_volume_change(struct snd_gus_card * gus)
3011da177e4SLinus Torvalds {
3021da177e4SLinus Torvalds }
3031da177e4SLinus Torvalds 
3045e2da206STakashi Iwai static int snd_gf1_pcm_poke_block(struct snd_gus_card *gus, unsigned char *buf,
3051da177e4SLinus Torvalds 				  unsigned int pos, unsigned int count,
3061da177e4SLinus Torvalds 				  int w16, int invert)
3071da177e4SLinus Torvalds {
3081da177e4SLinus Torvalds 	unsigned int len;
3091da177e4SLinus Torvalds 	unsigned long flags;
3101da177e4SLinus Torvalds 
31191f05060STakashi Iwai 	/*
31291f05060STakashi Iwai 	printk(KERN_DEBUG
31391f05060STakashi Iwai 	       "poke block; buf = 0x%x, pos = %i, count = %i, port = 0x%x\n",
31491f05060STakashi Iwai 	       (int)buf, pos, count, gus->gf1.port);
31591f05060STakashi Iwai 	*/
3161da177e4SLinus Torvalds 	while (count > 0) {
3171da177e4SLinus Torvalds 		len = count;
3181da177e4SLinus Torvalds 		if (len > 512)		/* limit, to allow IRQ */
3191da177e4SLinus Torvalds 			len = 512;
3201da177e4SLinus Torvalds 		count -= len;
3211da177e4SLinus Torvalds 		if (gus->interwave) {
3221da177e4SLinus Torvalds 			spin_lock_irqsave(&gus->reg_lock, flags);
3231da177e4SLinus Torvalds 			snd_gf1_write8(gus, SNDRV_GF1_GB_MEMORY_CONTROL, 0x01 | (invert ? 0x08 : 0x00));
3241da177e4SLinus Torvalds 			snd_gf1_dram_addr(gus, pos);
3251da177e4SLinus Torvalds 			if (w16) {
3261da177e4SLinus Torvalds 				outb(SNDRV_GF1_GW_DRAM_IO16, GUSP(gus, GF1REGSEL));
3271da177e4SLinus Torvalds 				outsw(GUSP(gus, GF1DATALOW), buf, len >> 1);
3281da177e4SLinus Torvalds 			} else {
3291da177e4SLinus Torvalds 				outsb(GUSP(gus, DRAM), buf, len);
3301da177e4SLinus Torvalds 			}
3311da177e4SLinus Torvalds 			spin_unlock_irqrestore(&gus->reg_lock, flags);
3321da177e4SLinus Torvalds 			buf += 512;
3331da177e4SLinus Torvalds 			pos += 512;
3341da177e4SLinus Torvalds 		} else {
3351da177e4SLinus Torvalds 			invert = invert ? 0x80 : 0x00;
3361da177e4SLinus Torvalds 			if (w16) {
3371da177e4SLinus Torvalds 				len >>= 1;
3381da177e4SLinus Torvalds 				while (len--) {
3391da177e4SLinus Torvalds 					snd_gf1_poke(gus, pos++, *buf++);
3401da177e4SLinus Torvalds 					snd_gf1_poke(gus, pos++, *buf++ ^ invert);
3411da177e4SLinus Torvalds 				}
3421da177e4SLinus Torvalds 			} else {
3431da177e4SLinus Torvalds 				while (len--)
3441da177e4SLinus Torvalds 					snd_gf1_poke(gus, pos++, *buf++ ^ invert);
3451da177e4SLinus Torvalds 			}
3461da177e4SLinus Torvalds 		}
3471da177e4SLinus Torvalds 		if (count > 0 && !in_interrupt()) {
3488433a509SNishanth Aravamudan 			schedule_timeout_interruptible(1);
3491da177e4SLinus Torvalds 			if (signal_pending(current))
3501da177e4SLinus Torvalds 				return -EAGAIN;
3511da177e4SLinus Torvalds 		}
3521da177e4SLinus Torvalds 	}
3531da177e4SLinus Torvalds 	return 0;
3541da177e4SLinus Torvalds }
3551da177e4SLinus Torvalds 
356a6970bb1STakashi Iwai static int get_bpos(struct gus_pcm_private *pcmp, int voice, unsigned int pos,
357a6970bb1STakashi Iwai 		    unsigned int len)
3581da177e4SLinus Torvalds {
359a6970bb1STakashi Iwai 	unsigned int bpos = pos + (voice * (pcmp->dma_size / 2));
360622207dcSTakashi Iwai 	if (snd_BUG_ON(bpos > pcmp->dma_size))
361622207dcSTakashi Iwai 		return -EIO;
362622207dcSTakashi Iwai 	if (snd_BUG_ON(bpos + len > pcmp->dma_size))
363622207dcSTakashi Iwai 		return -EIO;
364a6970bb1STakashi Iwai 	return bpos;
365a6970bb1STakashi Iwai }
366a6970bb1STakashi Iwai 
367a6970bb1STakashi Iwai static int playback_copy_ack(struct snd_pcm_substream *substream,
368a6970bb1STakashi Iwai 			     unsigned int bpos, unsigned int len)
369a6970bb1STakashi Iwai {
370a6970bb1STakashi Iwai 	struct snd_pcm_runtime *runtime = substream->runtime;
371a6970bb1STakashi Iwai 	struct gus_pcm_private *pcmp = runtime->private_data;
372a6970bb1STakashi Iwai 	struct snd_gus_card *gus = pcmp->gus;
373a6970bb1STakashi Iwai 	int w16, invert;
374a6970bb1STakashi Iwai 
375097a7fe3STakashi Sakamoto 	if (len > 32)
376097a7fe3STakashi Sakamoto 		return snd_gf1_pcm_block_change(substream, bpos,
377097a7fe3STakashi Sakamoto 						pcmp->memory + bpos, len);
3781da177e4SLinus Torvalds 
3791da177e4SLinus Torvalds 	w16 = (snd_pcm_format_width(runtime->format) == 16);
3801da177e4SLinus Torvalds 	invert = snd_pcm_format_unsigned(runtime->format);
381097a7fe3STakashi Sakamoto 	return snd_gf1_pcm_poke_block(gus, runtime->dma_area + bpos,
382097a7fe3STakashi Sakamoto 				      pcmp->memory + bpos, len, w16, invert);
3831da177e4SLinus Torvalds }
3841da177e4SLinus Torvalds 
385a6970bb1STakashi Iwai static int snd_gf1_pcm_playback_copy(struct snd_pcm_substream *substream,
386a6970bb1STakashi Iwai 				     int voice, unsigned long pos,
387a6970bb1STakashi Iwai 				     void __user *src, unsigned long count)
3881da177e4SLinus Torvalds {
3895e2da206STakashi Iwai 	struct snd_pcm_runtime *runtime = substream->runtime;
3905e2da206STakashi Iwai 	struct gus_pcm_private *pcmp = runtime->private_data;
391a6970bb1STakashi Iwai 	unsigned int len = count;
392a6970bb1STakashi Iwai 	int bpos;
3931da177e4SLinus Torvalds 
394a6970bb1STakashi Iwai 	bpos = get_bpos(pcmp, voice, pos, len);
395a6970bb1STakashi Iwai 	if (bpos < 0)
396a6970bb1STakashi Iwai 		return pos;
397a6970bb1STakashi Iwai 	if (copy_from_user(runtime->dma_area + bpos, src, len))
398a6970bb1STakashi Iwai 		return -EFAULT;
399a6970bb1STakashi Iwai 	return playback_copy_ack(substream, bpos, len);
400a6970bb1STakashi Iwai }
401a6970bb1STakashi Iwai 
402a6970bb1STakashi Iwai static int snd_gf1_pcm_playback_copy_kernel(struct snd_pcm_substream *substream,
403a6970bb1STakashi Iwai 					    int voice, unsigned long pos,
404a6970bb1STakashi Iwai 					    void *src, unsigned long count)
405a6970bb1STakashi Iwai {
406a6970bb1STakashi Iwai 	struct snd_pcm_runtime *runtime = substream->runtime;
407a6970bb1STakashi Iwai 	struct gus_pcm_private *pcmp = runtime->private_data;
408a6970bb1STakashi Iwai 	unsigned int len = count;
409a6970bb1STakashi Iwai 	int bpos;
410a6970bb1STakashi Iwai 
411a6970bb1STakashi Iwai 	bpos = get_bpos(pcmp, voice, pos, len);
412a6970bb1STakashi Iwai 	if (bpos < 0)
413a6970bb1STakashi Iwai 		return pos;
414a6970bb1STakashi Iwai 	memcpy(runtime->dma_area + bpos, src, len);
415a6970bb1STakashi Iwai 	return playback_copy_ack(substream, bpos, len);
416a6970bb1STakashi Iwai }
417a6970bb1STakashi Iwai 
418a6970bb1STakashi Iwai static int snd_gf1_pcm_playback_silence(struct snd_pcm_substream *substream,
419a6970bb1STakashi Iwai 					int voice, unsigned long pos,
420a6970bb1STakashi Iwai 					unsigned long count)
421a6970bb1STakashi Iwai {
422a6970bb1STakashi Iwai 	struct snd_pcm_runtime *runtime = substream->runtime;
423a6970bb1STakashi Iwai 	struct gus_pcm_private *pcmp = runtime->private_data;
424a6970bb1STakashi Iwai 	unsigned int len = count;
425a6970bb1STakashi Iwai 	int bpos;
426a6970bb1STakashi Iwai 
427a6970bb1STakashi Iwai 	bpos = get_bpos(pcmp, voice, pos, len);
428a6970bb1STakashi Iwai 	if (bpos < 0)
429a6970bb1STakashi Iwai 		return pos;
430097a7fe3STakashi Sakamoto 	snd_pcm_format_set_silence(runtime->format, runtime->dma_area + bpos,
431a6970bb1STakashi Iwai 				   bytes_to_samples(runtime, count));
432a6970bb1STakashi Iwai 	return playback_copy_ack(substream, bpos, len);
4331da177e4SLinus Torvalds }
4341da177e4SLinus Torvalds 
4355e2da206STakashi Iwai static int snd_gf1_pcm_playback_hw_params(struct snd_pcm_substream *substream,
4365e2da206STakashi Iwai 					  struct snd_pcm_hw_params *hw_params)
4371da177e4SLinus Torvalds {
4385e2da206STakashi Iwai 	struct snd_gus_card *gus = snd_pcm_substream_chip(substream);
4395e2da206STakashi Iwai 	struct snd_pcm_runtime *runtime = substream->runtime;
4405e2da206STakashi Iwai 	struct gus_pcm_private *pcmp = runtime->private_data;
4411da177e4SLinus Torvalds 	int err;
4421da177e4SLinus Torvalds 
4431da177e4SLinus Torvalds 	if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0)
4441da177e4SLinus Torvalds 		return err;
4451da177e4SLinus Torvalds 	if (err > 0) {	/* change */
4465e2da206STakashi Iwai 		struct snd_gf1_mem_block *block;
4471da177e4SLinus Torvalds 		if (pcmp->memory > 0) {
4481da177e4SLinus Torvalds 			snd_gf1_mem_free(&gus->gf1.mem_alloc, pcmp->memory);
4491da177e4SLinus Torvalds 			pcmp->memory = 0;
4501da177e4SLinus Torvalds 		}
4511da177e4SLinus Torvalds 		if ((block = snd_gf1_mem_alloc(&gus->gf1.mem_alloc,
4521da177e4SLinus Torvalds 		                               SNDRV_GF1_MEM_OWNER_DRIVER,
4531da177e4SLinus Torvalds 					       "GF1 PCM",
4541da177e4SLinus Torvalds 		                               runtime->dma_bytes, 1, 32,
4551da177e4SLinus Torvalds 		                               NULL)) == NULL)
4561da177e4SLinus Torvalds 			return -ENOMEM;
4571da177e4SLinus Torvalds 		pcmp->memory = block->ptr;
4581da177e4SLinus Torvalds 	}
4591da177e4SLinus Torvalds 	pcmp->voices = params_channels(hw_params);
4601da177e4SLinus Torvalds 	if (pcmp->pvoices[0] == NULL) {
4611da177e4SLinus Torvalds 		if ((pcmp->pvoices[0] = snd_gf1_alloc_voice(pcmp->gus, SNDRV_GF1_VOICE_TYPE_PCM, 0, 0)) == NULL)
4621da177e4SLinus Torvalds 			return -ENOMEM;
4631da177e4SLinus Torvalds 		pcmp->pvoices[0]->handler_wave = snd_gf1_pcm_interrupt_wave;
4641da177e4SLinus Torvalds 		pcmp->pvoices[0]->handler_volume = snd_gf1_pcm_interrupt_volume;
4651da177e4SLinus Torvalds 		pcmp->pvoices[0]->volume_change = snd_gf1_pcm_volume_change;
4661da177e4SLinus Torvalds 		pcmp->pvoices[0]->private_data = pcmp;
4671da177e4SLinus Torvalds 	}
4681da177e4SLinus Torvalds 	if (pcmp->voices > 1 && pcmp->pvoices[1] == NULL) {
4691da177e4SLinus Torvalds 		if ((pcmp->pvoices[1] = snd_gf1_alloc_voice(pcmp->gus, SNDRV_GF1_VOICE_TYPE_PCM, 0, 0)) == NULL)
4701da177e4SLinus Torvalds 			return -ENOMEM;
4711da177e4SLinus Torvalds 		pcmp->pvoices[1]->handler_wave = snd_gf1_pcm_interrupt_wave;
4721da177e4SLinus Torvalds 		pcmp->pvoices[1]->handler_volume = snd_gf1_pcm_interrupt_volume;
4731da177e4SLinus Torvalds 		pcmp->pvoices[1]->volume_change = snd_gf1_pcm_volume_change;
4741da177e4SLinus Torvalds 		pcmp->pvoices[1]->private_data = pcmp;
4751da177e4SLinus Torvalds 	} else if (pcmp->voices == 1) {
4761da177e4SLinus Torvalds 		if (pcmp->pvoices[1]) {
4771da177e4SLinus Torvalds 			snd_gf1_free_voice(pcmp->gus, pcmp->pvoices[1]);
4781da177e4SLinus Torvalds 			pcmp->pvoices[1] = NULL;
4791da177e4SLinus Torvalds 		}
4801da177e4SLinus Torvalds 	}
4811da177e4SLinus Torvalds 	return 0;
4821da177e4SLinus Torvalds }
4831da177e4SLinus Torvalds 
4845e2da206STakashi Iwai static int snd_gf1_pcm_playback_hw_free(struct snd_pcm_substream *substream)
4851da177e4SLinus Torvalds {
4865e2da206STakashi Iwai 	struct snd_pcm_runtime *runtime = substream->runtime;
4875e2da206STakashi Iwai 	struct gus_pcm_private *pcmp = runtime->private_data;
4881da177e4SLinus Torvalds 
4891da177e4SLinus Torvalds 	snd_pcm_lib_free_pages(substream);
4901da177e4SLinus Torvalds 	if (pcmp->pvoices[0]) {
4911da177e4SLinus Torvalds 		snd_gf1_free_voice(pcmp->gus, pcmp->pvoices[0]);
4921da177e4SLinus Torvalds 		pcmp->pvoices[0] = NULL;
4931da177e4SLinus Torvalds 	}
4941da177e4SLinus Torvalds 	if (pcmp->pvoices[1]) {
4951da177e4SLinus Torvalds 		snd_gf1_free_voice(pcmp->gus, pcmp->pvoices[1]);
4961da177e4SLinus Torvalds 		pcmp->pvoices[1] = NULL;
4971da177e4SLinus Torvalds 	}
4981da177e4SLinus Torvalds 	if (pcmp->memory > 0) {
4991da177e4SLinus Torvalds 		snd_gf1_mem_free(&pcmp->gus->gf1.mem_alloc, pcmp->memory);
5001da177e4SLinus Torvalds 		pcmp->memory = 0;
5011da177e4SLinus Torvalds 	}
5021da177e4SLinus Torvalds 	return 0;
5031da177e4SLinus Torvalds }
5041da177e4SLinus Torvalds 
5055e2da206STakashi Iwai static int snd_gf1_pcm_playback_prepare(struct snd_pcm_substream *substream)
5061da177e4SLinus Torvalds {
5075e2da206STakashi Iwai 	struct snd_pcm_runtime *runtime = substream->runtime;
5085e2da206STakashi Iwai 	struct gus_pcm_private *pcmp = runtime->private_data;
5091da177e4SLinus Torvalds 
5101da177e4SLinus Torvalds 	pcmp->bpos = 0;
5111da177e4SLinus Torvalds 	pcmp->dma_size = snd_pcm_lib_buffer_bytes(substream);
5121da177e4SLinus Torvalds 	pcmp->block_size = snd_pcm_lib_period_bytes(substream);
5131da177e4SLinus Torvalds 	pcmp->blocks = pcmp->dma_size / pcmp->block_size;
5141da177e4SLinus Torvalds 	return 0;
5151da177e4SLinus Torvalds }
5161da177e4SLinus Torvalds 
5175e2da206STakashi Iwai static int snd_gf1_pcm_playback_trigger(struct snd_pcm_substream *substream,
5181da177e4SLinus Torvalds 					int cmd)
5191da177e4SLinus Torvalds {
5205e2da206STakashi Iwai 	struct snd_gus_card *gus = snd_pcm_substream_chip(substream);
5215e2da206STakashi Iwai 	struct snd_pcm_runtime *runtime = substream->runtime;
5225e2da206STakashi Iwai 	struct gus_pcm_private *pcmp = runtime->private_data;
5231da177e4SLinus Torvalds 	int voice;
5241da177e4SLinus Torvalds 
5251da177e4SLinus Torvalds 	if (cmd == SNDRV_PCM_TRIGGER_START) {
5261da177e4SLinus Torvalds 		snd_gf1_pcm_trigger_up(substream);
5271da177e4SLinus Torvalds 	} else if (cmd == SNDRV_PCM_TRIGGER_STOP) {
5281da177e4SLinus Torvalds 		spin_lock(&pcmp->lock);
5291da177e4SLinus Torvalds 		pcmp->flags &= ~SNDRV_GF1_PCM_PFLG_ACTIVE;
5301da177e4SLinus Torvalds 		spin_unlock(&pcmp->lock);
5311da177e4SLinus Torvalds 		voice = pcmp->pvoices[0]->number;
5321da177e4SLinus Torvalds 		snd_gf1_stop_voices(gus, voice, voice);
5331da177e4SLinus Torvalds 		if (pcmp->pvoices[1]) {
5341da177e4SLinus Torvalds 			voice = pcmp->pvoices[1]->number;
5351da177e4SLinus Torvalds 			snd_gf1_stop_voices(gus, voice, voice);
5361da177e4SLinus Torvalds 		}
5371da177e4SLinus Torvalds 	} else {
5381da177e4SLinus Torvalds 		return -EINVAL;
5391da177e4SLinus Torvalds 	}
5401da177e4SLinus Torvalds 	return 0;
5411da177e4SLinus Torvalds }
5421da177e4SLinus Torvalds 
5435e2da206STakashi Iwai static snd_pcm_uframes_t snd_gf1_pcm_playback_pointer(struct snd_pcm_substream *substream)
5441da177e4SLinus Torvalds {
5455e2da206STakashi Iwai 	struct snd_gus_card *gus = snd_pcm_substream_chip(substream);
5465e2da206STakashi Iwai 	struct snd_pcm_runtime *runtime = substream->runtime;
5475e2da206STakashi Iwai 	struct gus_pcm_private *pcmp = runtime->private_data;
5481da177e4SLinus Torvalds 	unsigned int pos;
5491da177e4SLinus Torvalds 	unsigned char voice_ctrl;
5501da177e4SLinus Torvalds 
5511da177e4SLinus Torvalds 	pos = 0;
5521da177e4SLinus Torvalds 	spin_lock(&gus->reg_lock);
5531da177e4SLinus Torvalds 	if (pcmp->flags & SNDRV_GF1_PCM_PFLG_ACTIVE) {
5541da177e4SLinus Torvalds 		snd_gf1_select_voice(gus, pcmp->pvoices[0]->number);
5551da177e4SLinus Torvalds 		voice_ctrl = snd_gf1_read8(gus, SNDRV_GF1_VB_ADDRESS_CONTROL);
5561da177e4SLinus Torvalds 		pos = (snd_gf1_read_addr(gus, SNDRV_GF1_VA_CURRENT, voice_ctrl & 4) >> 4) - pcmp->memory;
5571da177e4SLinus Torvalds 		if (substream->runtime->channels > 1)
5581da177e4SLinus Torvalds 			pos <<= 1;
5591da177e4SLinus Torvalds 		pos = bytes_to_frames(runtime, pos);
5601da177e4SLinus Torvalds 	}
5611da177e4SLinus Torvalds 	spin_unlock(&gus->reg_lock);
5621da177e4SLinus Torvalds 	return pos;
5631da177e4SLinus Torvalds }
5641da177e4SLinus Torvalds 
565bfa516a1STakashi Iwai static const struct snd_ratnum clock = {
5661da177e4SLinus Torvalds 	.num = 9878400/16,
5671da177e4SLinus Torvalds 	.den_min = 2,
5681da177e4SLinus Torvalds 	.den_max = 257,
5691da177e4SLinus Torvalds 	.den_step = 1,
5701da177e4SLinus Torvalds };
5711da177e4SLinus Torvalds 
572bfa516a1STakashi Iwai static const struct snd_pcm_hw_constraint_ratnums hw_constraints_clocks  = {
5731da177e4SLinus Torvalds 	.nrats = 1,
5741da177e4SLinus Torvalds 	.rats = &clock,
5751da177e4SLinus Torvalds };
5761da177e4SLinus Torvalds 
5775e2da206STakashi Iwai static int snd_gf1_pcm_capture_hw_params(struct snd_pcm_substream *substream,
5785e2da206STakashi Iwai 					 struct snd_pcm_hw_params *hw_params)
5791da177e4SLinus Torvalds {
5805e2da206STakashi Iwai 	struct snd_gus_card *gus = snd_pcm_substream_chip(substream);
5811da177e4SLinus Torvalds 
5821da177e4SLinus Torvalds 	gus->c_dma_size = params_buffer_bytes(hw_params);
5831da177e4SLinus Torvalds 	gus->c_period_size = params_period_bytes(hw_params);
5841da177e4SLinus Torvalds 	gus->c_pos = 0;
5851da177e4SLinus Torvalds 	gus->gf1.pcm_rcntrl_reg = 0x21;		/* IRQ at end, enable & start */
5861da177e4SLinus Torvalds 	if (params_channels(hw_params) > 1)
5871da177e4SLinus Torvalds 		gus->gf1.pcm_rcntrl_reg |= 2;
5881da177e4SLinus Torvalds 	if (gus->gf1.dma2 > 3)
5891da177e4SLinus Torvalds 		gus->gf1.pcm_rcntrl_reg |= 4;
5901da177e4SLinus Torvalds 	if (snd_pcm_format_unsigned(params_format(hw_params)))
5911da177e4SLinus Torvalds 		gus->gf1.pcm_rcntrl_reg |= 0x80;
5921da177e4SLinus Torvalds 	return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
5931da177e4SLinus Torvalds }
5941da177e4SLinus Torvalds 
5955e2da206STakashi Iwai static int snd_gf1_pcm_capture_hw_free(struct snd_pcm_substream *substream)
5961da177e4SLinus Torvalds {
5971da177e4SLinus Torvalds 	return snd_pcm_lib_free_pages(substream);
5981da177e4SLinus Torvalds }
5991da177e4SLinus Torvalds 
6005e2da206STakashi Iwai static int snd_gf1_pcm_capture_prepare(struct snd_pcm_substream *substream)
6011da177e4SLinus Torvalds {
6025e2da206STakashi Iwai 	struct snd_gus_card *gus = snd_pcm_substream_chip(substream);
6035e2da206STakashi Iwai 	struct snd_pcm_runtime *runtime = substream->runtime;
6041da177e4SLinus Torvalds 
6051da177e4SLinus Torvalds 	snd_gf1_i_write8(gus, SNDRV_GF1_GB_RECORD_RATE, runtime->rate_den - 2);
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 	snd_dma_program(gus->gf1.dma2, runtime->dma_addr, gus->c_period_size, DMA_MODE_READ);
6091da177e4SLinus Torvalds 	return 0;
6101da177e4SLinus Torvalds }
6111da177e4SLinus Torvalds 
6125e2da206STakashi Iwai static int snd_gf1_pcm_capture_trigger(struct snd_pcm_substream *substream,
6131da177e4SLinus Torvalds 				       int cmd)
6141da177e4SLinus Torvalds {
6155e2da206STakashi Iwai 	struct snd_gus_card *gus = snd_pcm_substream_chip(substream);
6161da177e4SLinus Torvalds 	int val;
6171da177e4SLinus Torvalds 
6181da177e4SLinus Torvalds 	if (cmd == SNDRV_PCM_TRIGGER_START) {
6191da177e4SLinus Torvalds 		val = gus->gf1.pcm_rcntrl_reg;
6201da177e4SLinus Torvalds 	} else if (cmd == SNDRV_PCM_TRIGGER_STOP) {
6211da177e4SLinus Torvalds 		val = 0;
6221da177e4SLinus Torvalds 	} else {
6231da177e4SLinus Torvalds 		return -EINVAL;
6241da177e4SLinus Torvalds 	}
6251da177e4SLinus Torvalds 
6261da177e4SLinus Torvalds 	spin_lock(&gus->reg_lock);
6271da177e4SLinus Torvalds 	snd_gf1_write8(gus, SNDRV_GF1_GB_REC_DMA_CONTROL, val);
6281da177e4SLinus Torvalds 	snd_gf1_look8(gus, SNDRV_GF1_GB_REC_DMA_CONTROL);
6291da177e4SLinus Torvalds 	spin_unlock(&gus->reg_lock);
6301da177e4SLinus Torvalds 	return 0;
6311da177e4SLinus Torvalds }
6321da177e4SLinus Torvalds 
6335e2da206STakashi Iwai static snd_pcm_uframes_t snd_gf1_pcm_capture_pointer(struct snd_pcm_substream *substream)
6341da177e4SLinus Torvalds {
6355e2da206STakashi Iwai 	struct snd_gus_card *gus = snd_pcm_substream_chip(substream);
6361da177e4SLinus Torvalds 	int pos = snd_dma_pointer(gus->gf1.dma2, gus->c_period_size);
6371da177e4SLinus Torvalds 	pos = bytes_to_frames(substream->runtime, (gus->c_pos + pos) % gus->c_dma_size);
6381da177e4SLinus Torvalds 	return pos;
6391da177e4SLinus Torvalds }
6401da177e4SLinus Torvalds 
6415e2da206STakashi Iwai static void snd_gf1_pcm_interrupt_dma_read(struct snd_gus_card * gus)
6421da177e4SLinus Torvalds {
6431da177e4SLinus Torvalds 	snd_gf1_i_write8(gus, SNDRV_GF1_GB_REC_DMA_CONTROL, 0);	/* disable sampling */
6441da177e4SLinus Torvalds 	snd_gf1_i_look8(gus, SNDRV_GF1_GB_REC_DMA_CONTROL);	/* Sampling Control Register */
6451da177e4SLinus Torvalds 	if (gus->pcm_cap_substream != NULL) {
6461da177e4SLinus Torvalds 		snd_gf1_pcm_capture_prepare(gus->pcm_cap_substream);
6471da177e4SLinus Torvalds 		snd_gf1_pcm_capture_trigger(gus->pcm_cap_substream, SNDRV_PCM_TRIGGER_START);
6481da177e4SLinus Torvalds 		gus->c_pos += gus->c_period_size;
6491da177e4SLinus Torvalds 		snd_pcm_period_elapsed(gus->pcm_cap_substream);
6501da177e4SLinus Torvalds 	}
6511da177e4SLinus Torvalds }
6521da177e4SLinus Torvalds 
653aec54654SBhumika Goyal static const struct snd_pcm_hardware snd_gf1_pcm_playback =
6541da177e4SLinus Torvalds {
6551da177e4SLinus Torvalds 	.info =			SNDRV_PCM_INFO_NONINTERLEAVED,
6561da177e4SLinus Torvalds 	.formats		= (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U8 |
6571da177e4SLinus Torvalds 				 SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE),
6581da177e4SLinus Torvalds 	.rates =		SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
6591da177e4SLinus Torvalds 	.rate_min =		5510,
6601da177e4SLinus Torvalds 	.rate_max =		48000,
6611da177e4SLinus Torvalds 	.channels_min =		1,
6621da177e4SLinus Torvalds 	.channels_max =		2,
6631da177e4SLinus Torvalds 	.buffer_bytes_max =	(128*1024),
6641da177e4SLinus Torvalds 	.period_bytes_min =	64,
6651da177e4SLinus Torvalds 	.period_bytes_max =	(128*1024),
6661da177e4SLinus Torvalds 	.periods_min =		1,
6671da177e4SLinus Torvalds 	.periods_max =		1024,
6681da177e4SLinus Torvalds 	.fifo_size =		0,
6691da177e4SLinus Torvalds };
6701da177e4SLinus Torvalds 
671aec54654SBhumika Goyal static const struct snd_pcm_hardware snd_gf1_pcm_capture =
6721da177e4SLinus Torvalds {
6731da177e4SLinus Torvalds 	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
6741da177e4SLinus Torvalds 				 SNDRV_PCM_INFO_MMAP_VALID),
6751da177e4SLinus Torvalds 	.formats =		SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U8,
6761da177e4SLinus Torvalds 	.rates =		SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_44100,
6771da177e4SLinus Torvalds 	.rate_min =		5510,
6781da177e4SLinus Torvalds 	.rate_max =		44100,
6791da177e4SLinus Torvalds 	.channels_min =		1,
6801da177e4SLinus Torvalds 	.channels_max =		2,
6811da177e4SLinus Torvalds 	.buffer_bytes_max =	(128*1024),
6821da177e4SLinus Torvalds 	.period_bytes_min =	64,
6831da177e4SLinus Torvalds 	.period_bytes_max =	(128*1024),
6841da177e4SLinus Torvalds 	.periods_min =		1,
6851da177e4SLinus Torvalds 	.periods_max =		1024,
6861da177e4SLinus Torvalds 	.fifo_size =		0,
6871da177e4SLinus Torvalds };
6881da177e4SLinus Torvalds 
6895e2da206STakashi Iwai static void snd_gf1_pcm_playback_free(struct snd_pcm_runtime *runtime)
6901da177e4SLinus Torvalds {
6914d572776SJesper Juhl 	kfree(runtime->private_data);
6921da177e4SLinus Torvalds }
6931da177e4SLinus Torvalds 
6945e2da206STakashi Iwai static int snd_gf1_pcm_playback_open(struct snd_pcm_substream *substream)
6951da177e4SLinus Torvalds {
6965e2da206STakashi Iwai 	struct gus_pcm_private *pcmp;
6975e2da206STakashi Iwai 	struct snd_gus_card *gus = snd_pcm_substream_chip(substream);
6985e2da206STakashi Iwai 	struct snd_pcm_runtime *runtime = substream->runtime;
6991da177e4SLinus Torvalds 	int err;
7001da177e4SLinus Torvalds 
7019e76a76eSTakashi Iwai 	pcmp = kzalloc(sizeof(*pcmp), GFP_KERNEL);
7021da177e4SLinus Torvalds 	if (pcmp == NULL)
7031da177e4SLinus Torvalds 		return -ENOMEM;
7041da177e4SLinus Torvalds 	pcmp->gus = gus;
7051da177e4SLinus Torvalds 	spin_lock_init(&pcmp->lock);
7061da177e4SLinus Torvalds 	init_waitqueue_head(&pcmp->sleep);
7071da177e4SLinus Torvalds 	atomic_set(&pcmp->dma_count, 0);
7081da177e4SLinus Torvalds 
7091da177e4SLinus Torvalds 	runtime->private_data = pcmp;
7101da177e4SLinus Torvalds 	runtime->private_free = snd_gf1_pcm_playback_free;
7111da177e4SLinus Torvalds 
7121da177e4SLinus Torvalds #if 0
71391f05060STakashi Iwai 	printk(KERN_DEBUG "playback.buffer = 0x%lx, gf1.pcm_buffer = 0x%lx\n",
71491f05060STakashi Iwai 	       (long) pcm->playback.buffer, (long) gus->gf1.pcm_buffer);
7151da177e4SLinus Torvalds #endif
7161da177e4SLinus Torvalds 	if ((err = snd_gf1_dma_init(gus)) < 0)
7171da177e4SLinus Torvalds 		return err;
7181da177e4SLinus Torvalds 	pcmp->flags = SNDRV_GF1_PCM_PFLG_NONE;
7191da177e4SLinus Torvalds 	pcmp->substream = substream;
7201da177e4SLinus Torvalds 	runtime->hw = snd_gf1_pcm_playback;
7211da177e4SLinus Torvalds 	snd_pcm_limit_isa_dma_size(gus->gf1.dma1, &runtime->hw.buffer_bytes_max);
7221da177e4SLinus Torvalds 	snd_pcm_limit_isa_dma_size(gus->gf1.dma1, &runtime->hw.period_bytes_max);
7231da177e4SLinus Torvalds 	snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 64);
7241da177e4SLinus Torvalds 	return 0;
7251da177e4SLinus Torvalds }
7261da177e4SLinus Torvalds 
7275e2da206STakashi Iwai static int snd_gf1_pcm_playback_close(struct snd_pcm_substream *substream)
7281da177e4SLinus Torvalds {
7295e2da206STakashi Iwai 	struct snd_gus_card *gus = snd_pcm_substream_chip(substream);
7305e2da206STakashi Iwai 	struct snd_pcm_runtime *runtime = substream->runtime;
7315e2da206STakashi Iwai 	struct gus_pcm_private *pcmp = runtime->private_data;
7321da177e4SLinus Torvalds 
7331da177e4SLinus Torvalds 	if (!wait_event_timeout(pcmp->sleep, (atomic_read(&pcmp->dma_count) <= 0), 2*HZ))
73499b359baSTakashi Iwai 		snd_printk(KERN_ERR "gf1 pcm - serious DMA problem\n");
7351da177e4SLinus Torvalds 
7361da177e4SLinus Torvalds 	snd_gf1_dma_done(gus);
7371da177e4SLinus Torvalds 	return 0;
7381da177e4SLinus Torvalds }
7391da177e4SLinus Torvalds 
7405e2da206STakashi Iwai static int snd_gf1_pcm_capture_open(struct snd_pcm_substream *substream)
7411da177e4SLinus Torvalds {
7425e2da206STakashi Iwai 	struct snd_pcm_runtime *runtime = substream->runtime;
7435e2da206STakashi Iwai 	struct snd_gus_card *gus = snd_pcm_substream_chip(substream);
7441da177e4SLinus Torvalds 
7451da177e4SLinus Torvalds 	gus->gf1.interrupt_handler_dma_read = snd_gf1_pcm_interrupt_dma_read;
7461da177e4SLinus Torvalds 	gus->pcm_cap_substream = substream;
7471da177e4SLinus Torvalds 	substream->runtime->hw = snd_gf1_pcm_capture;
7481da177e4SLinus Torvalds 	snd_pcm_limit_isa_dma_size(gus->gf1.dma2, &runtime->hw.buffer_bytes_max);
7491da177e4SLinus Torvalds 	snd_pcm_limit_isa_dma_size(gus->gf1.dma2, &runtime->hw.period_bytes_max);
7501da177e4SLinus Torvalds 	snd_pcm_hw_constraint_ratnums(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
7511da177e4SLinus Torvalds 				      &hw_constraints_clocks);
7521da177e4SLinus Torvalds 	return 0;
7531da177e4SLinus Torvalds }
7541da177e4SLinus Torvalds 
7555e2da206STakashi Iwai static int snd_gf1_pcm_capture_close(struct snd_pcm_substream *substream)
7561da177e4SLinus Torvalds {
7575e2da206STakashi Iwai 	struct snd_gus_card *gus = snd_pcm_substream_chip(substream);
7581da177e4SLinus Torvalds 
7591da177e4SLinus Torvalds 	gus->pcm_cap_substream = NULL;
7601da177e4SLinus Torvalds 	snd_gf1_set_default_handlers(gus, SNDRV_GF1_HANDLER_DMA_READ);
7611da177e4SLinus Torvalds 	return 0;
7621da177e4SLinus Torvalds }
7631da177e4SLinus Torvalds 
7645e2da206STakashi Iwai static int snd_gf1_pcm_volume_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
7651da177e4SLinus Torvalds {
7661da177e4SLinus Torvalds 	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
7671da177e4SLinus Torvalds 	uinfo->count = 2;
7681da177e4SLinus Torvalds 	uinfo->value.integer.min = 0;
7691da177e4SLinus Torvalds 	uinfo->value.integer.max = 127;
7701da177e4SLinus Torvalds 	return 0;
7711da177e4SLinus Torvalds }
7721da177e4SLinus Torvalds 
7735e2da206STakashi Iwai static int snd_gf1_pcm_volume_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
7741da177e4SLinus Torvalds {
7755e2da206STakashi Iwai 	struct snd_gus_card *gus = snd_kcontrol_chip(kcontrol);
7761da177e4SLinus Torvalds 	unsigned long flags;
7771da177e4SLinus Torvalds 
7781da177e4SLinus Torvalds 	spin_lock_irqsave(&gus->pcm_volume_level_lock, flags);
7791da177e4SLinus Torvalds 	ucontrol->value.integer.value[0] = gus->gf1.pcm_volume_level_left1;
7801da177e4SLinus Torvalds 	ucontrol->value.integer.value[1] = gus->gf1.pcm_volume_level_right1;
7811da177e4SLinus Torvalds 	spin_unlock_irqrestore(&gus->pcm_volume_level_lock, flags);
7821da177e4SLinus Torvalds 	return 0;
7831da177e4SLinus Torvalds }
7841da177e4SLinus Torvalds 
7855e2da206STakashi Iwai static int snd_gf1_pcm_volume_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
7861da177e4SLinus Torvalds {
7875e2da206STakashi Iwai 	struct snd_gus_card *gus = snd_kcontrol_chip(kcontrol);
7881da177e4SLinus Torvalds 	unsigned long flags;
7891da177e4SLinus Torvalds 	int change;
7901da177e4SLinus Torvalds 	unsigned int idx;
7911da177e4SLinus Torvalds 	unsigned short val1, val2, vol;
7925e2da206STakashi Iwai 	struct gus_pcm_private *pcmp;
7935e2da206STakashi Iwai 	struct snd_gus_voice *pvoice;
7941da177e4SLinus Torvalds 
7951da177e4SLinus Torvalds 	val1 = ucontrol->value.integer.value[0] & 127;
7961da177e4SLinus Torvalds 	val2 = ucontrol->value.integer.value[1] & 127;
7971da177e4SLinus Torvalds 	spin_lock_irqsave(&gus->pcm_volume_level_lock, flags);
7981da177e4SLinus Torvalds 	change = val1 != gus->gf1.pcm_volume_level_left1 ||
7991da177e4SLinus Torvalds 	         val2 != gus->gf1.pcm_volume_level_right1;
8001da177e4SLinus Torvalds 	gus->gf1.pcm_volume_level_left1 = val1;
8011da177e4SLinus Torvalds 	gus->gf1.pcm_volume_level_right1 = val2;
8021da177e4SLinus Torvalds 	gus->gf1.pcm_volume_level_left = snd_gf1_lvol_to_gvol_raw(val1 << 9) << 4;
8031da177e4SLinus Torvalds 	gus->gf1.pcm_volume_level_right = snd_gf1_lvol_to_gvol_raw(val2 << 9) << 4;
8041da177e4SLinus Torvalds 	spin_unlock_irqrestore(&gus->pcm_volume_level_lock, flags);
8051da177e4SLinus Torvalds 	/* are we active? */
8061da177e4SLinus Torvalds 	spin_lock_irqsave(&gus->voice_alloc, flags);
8071da177e4SLinus Torvalds 	for (idx = 0; idx < 32; idx++) {
8081da177e4SLinus Torvalds 		pvoice = &gus->gf1.voices[idx];
8091da177e4SLinus Torvalds 		if (!pvoice->pcm)
8101da177e4SLinus Torvalds 			continue;
8111da177e4SLinus Torvalds 		pcmp = pvoice->private_data;
8121da177e4SLinus Torvalds 		if (!(pcmp->flags & SNDRV_GF1_PCM_PFLG_ACTIVE))
8131da177e4SLinus Torvalds 			continue;
8141da177e4SLinus Torvalds 		/* load real volume - better precision */
815fcb2954bSJulia Lawall 		spin_lock(&gus->reg_lock);
8161da177e4SLinus Torvalds 		snd_gf1_select_voice(gus, pvoice->number);
8171da177e4SLinus Torvalds 		snd_gf1_ctrl_stop(gus, SNDRV_GF1_VB_VOLUME_CONTROL);
8181da177e4SLinus Torvalds 		vol = pvoice == pcmp->pvoices[0] ? gus->gf1.pcm_volume_level_left : gus->gf1.pcm_volume_level_right;
8191da177e4SLinus Torvalds 		snd_gf1_write16(gus, SNDRV_GF1_VW_VOLUME, vol);
8201da177e4SLinus Torvalds 		pcmp->final_volume = 1;
821fcb2954bSJulia Lawall 		spin_unlock(&gus->reg_lock);
8221da177e4SLinus Torvalds 	}
8231da177e4SLinus Torvalds 	spin_unlock_irqrestore(&gus->voice_alloc, flags);
8241da177e4SLinus Torvalds 	return change;
8251da177e4SLinus Torvalds }
8261da177e4SLinus Torvalds 
8273a84d6c9SBhumika Goyal static const struct snd_kcontrol_new snd_gf1_pcm_volume_control =
8281da177e4SLinus Torvalds {
8291da177e4SLinus Torvalds 	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
8301da177e4SLinus Torvalds 	.name = "PCM Playback Volume",
8311da177e4SLinus Torvalds 	.info = snd_gf1_pcm_volume_info,
8321da177e4SLinus Torvalds 	.get = snd_gf1_pcm_volume_get,
8331da177e4SLinus Torvalds 	.put = snd_gf1_pcm_volume_put
8341da177e4SLinus Torvalds };
8351da177e4SLinus Torvalds 
8363a84d6c9SBhumika Goyal static const struct snd_kcontrol_new snd_gf1_pcm_volume_control1 =
8371da177e4SLinus Torvalds {
8381da177e4SLinus Torvalds 	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
8391da177e4SLinus Torvalds 	.name = "GPCM Playback Volume",
8401da177e4SLinus Torvalds 	.info = snd_gf1_pcm_volume_info,
8411da177e4SLinus Torvalds 	.get = snd_gf1_pcm_volume_get,
8421da177e4SLinus Torvalds 	.put = snd_gf1_pcm_volume_put
8431da177e4SLinus Torvalds };
8441da177e4SLinus Torvalds 
845ca58ba6cSArvind Yadav static const struct snd_pcm_ops snd_gf1_pcm_playback_ops = {
8461da177e4SLinus Torvalds 	.open =		snd_gf1_pcm_playback_open,
8471da177e4SLinus Torvalds 	.close =	snd_gf1_pcm_playback_close,
8481da177e4SLinus Torvalds 	.ioctl =	snd_pcm_lib_ioctl,
8491da177e4SLinus Torvalds 	.hw_params =	snd_gf1_pcm_playback_hw_params,
8501da177e4SLinus Torvalds 	.hw_free =	snd_gf1_pcm_playback_hw_free,
8511da177e4SLinus Torvalds 	.prepare =	snd_gf1_pcm_playback_prepare,
8521da177e4SLinus Torvalds 	.trigger =	snd_gf1_pcm_playback_trigger,
8531da177e4SLinus Torvalds 	.pointer =	snd_gf1_pcm_playback_pointer,
854a6970bb1STakashi Iwai 	.copy_user =	snd_gf1_pcm_playback_copy,
855a6970bb1STakashi Iwai 	.copy_kernel =	snd_gf1_pcm_playback_copy_kernel,
856a6970bb1STakashi Iwai 	.fill_silence =	snd_gf1_pcm_playback_silence,
8571da177e4SLinus Torvalds };
8581da177e4SLinus Torvalds 
859ca58ba6cSArvind Yadav static const struct snd_pcm_ops snd_gf1_pcm_capture_ops = {
8601da177e4SLinus Torvalds 	.open =		snd_gf1_pcm_capture_open,
8611da177e4SLinus Torvalds 	.close =	snd_gf1_pcm_capture_close,
8621da177e4SLinus Torvalds 	.ioctl =	snd_pcm_lib_ioctl,
8631da177e4SLinus Torvalds 	.hw_params =	snd_gf1_pcm_capture_hw_params,
8641da177e4SLinus Torvalds 	.hw_free =	snd_gf1_pcm_capture_hw_free,
8651da177e4SLinus Torvalds 	.prepare =	snd_gf1_pcm_capture_prepare,
8661da177e4SLinus Torvalds 	.trigger =	snd_gf1_pcm_capture_trigger,
8671da177e4SLinus Torvalds 	.pointer =	snd_gf1_pcm_capture_pointer,
8681da177e4SLinus Torvalds };
8691da177e4SLinus Torvalds 
870db5abb3cSLars-Peter Clausen int snd_gf1_pcm_new(struct snd_gus_card *gus, int pcm_dev, int control_index)
8711da177e4SLinus Torvalds {
8725e2da206STakashi Iwai 	struct snd_card *card;
8735e2da206STakashi Iwai 	struct snd_kcontrol *kctl;
8745e2da206STakashi Iwai 	struct snd_pcm *pcm;
8755e2da206STakashi Iwai 	struct snd_pcm_substream *substream;
8761da177e4SLinus Torvalds 	int capture, err;
8771da177e4SLinus Torvalds 
8781da177e4SLinus Torvalds 	card = gus->card;
8791da177e4SLinus Torvalds 	capture = !gus->interwave && !gus->ess_flag && !gus->ace_flag ? 1 : 0;
8801da177e4SLinus Torvalds 	err = snd_pcm_new(card,
8811da177e4SLinus Torvalds 			  gus->interwave ? "AMD InterWave" : "GF1",
8821da177e4SLinus Torvalds 			  pcm_dev,
8831da177e4SLinus Torvalds 			  gus->gf1.pcm_channels / 2,
8841da177e4SLinus Torvalds 			  capture,
8851da177e4SLinus Torvalds 			  &pcm);
8861da177e4SLinus Torvalds 	if (err < 0)
8871da177e4SLinus Torvalds 		return err;
8881da177e4SLinus Torvalds 	pcm->private_data = gus;
8891da177e4SLinus Torvalds 	/* playback setup */
8901da177e4SLinus Torvalds 	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_gf1_pcm_playback_ops);
8911da177e4SLinus Torvalds 
8921da177e4SLinus Torvalds 	for (substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; substream; substream = substream->next)
8931da177e4SLinus Torvalds 		snd_pcm_lib_preallocate_pages(substream, SNDRV_DMA_TYPE_DEV,
894*0b6a2c9cSTakashi Iwai 					      card->dev,
8951da177e4SLinus Torvalds 					      64*1024, gus->gf1.dma1 > 3 ? 128*1024 : 64*1024);
8961da177e4SLinus Torvalds 
8971da177e4SLinus Torvalds 	pcm->info_flags = 0;
8981da177e4SLinus Torvalds 	pcm->dev_subclass = SNDRV_PCM_SUBCLASS_GENERIC_MIX;
8991da177e4SLinus Torvalds 	if (capture) {
9001da177e4SLinus Torvalds 		snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_gf1_pcm_capture_ops);
9011da177e4SLinus Torvalds 		if (gus->gf1.dma2 == gus->gf1.dma1)
9021da177e4SLinus Torvalds 			pcm->info_flags |= SNDRV_PCM_INFO_HALF_DUPLEX;
9031da177e4SLinus Torvalds 		snd_pcm_lib_preallocate_pages(pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream,
904*0b6a2c9cSTakashi Iwai 					      SNDRV_DMA_TYPE_DEV, card->dev,
9051da177e4SLinus Torvalds 					      64*1024, gus->gf1.dma2 > 3 ? 128*1024 : 64*1024);
9061da177e4SLinus Torvalds 	}
9071da177e4SLinus Torvalds 	strcpy(pcm->name, pcm->id);
9081da177e4SLinus Torvalds 	if (gus->interwave) {
9091da177e4SLinus Torvalds 		sprintf(pcm->name + strlen(pcm->name), " rev %c", gus->revision + 'A');
9101da177e4SLinus Torvalds 	}
9111da177e4SLinus Torvalds 	strcat(pcm->name, " (synth)");
9121da177e4SLinus Torvalds 	gus->pcm = pcm;
9131da177e4SLinus Torvalds 
9141da177e4SLinus Torvalds 	if (gus->codec_flag)
9151da177e4SLinus Torvalds 		kctl = snd_ctl_new1(&snd_gf1_pcm_volume_control1, gus);
9161da177e4SLinus Torvalds 	else
9171da177e4SLinus Torvalds 		kctl = snd_ctl_new1(&snd_gf1_pcm_volume_control, gus);
9181da177e4SLinus Torvalds 	if ((err = snd_ctl_add(card, kctl)) < 0)
9191da177e4SLinus Torvalds 		return err;
9201da177e4SLinus Torvalds 	kctl->id.index = control_index;
9211da177e4SLinus Torvalds 
9221da177e4SLinus Torvalds 	return 0;
9231da177e4SLinus Torvalds }
9241da177e4SLinus Torvalds 
925