xref: /openbmc/linux/sound/pci/emu10k1/io.c (revision 6d68d9cb)
11a59d1b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
21da177e4SLinus Torvalds /*
3c1017a4cSJaroslav Kysela  *  Copyright (c) by Jaroslav Kysela <perex@perex.cz>
4*6d68d9cbSOswald Buddenhagen  *                   Lee Revell <rlrevell@joe-job.com>
5*6d68d9cbSOswald Buddenhagen  *                   James Courtier-Dutton <James@superbug.co.uk>
6*6d68d9cbSOswald Buddenhagen  *                   Oswald Buddenhagen <oswald.buddenhagen@gmx.de>
71da177e4SLinus Torvalds  *                   Creative Labs, Inc.
8*6d68d9cbSOswald Buddenhagen  *
91da177e4SLinus Torvalds  *  Routines for control of EMU10K1 chips
101da177e4SLinus Torvalds  */
111da177e4SLinus Torvalds 
121da177e4SLinus Torvalds #include <linux/time.h>
131da177e4SLinus Torvalds #include <sound/core.h>
141da177e4SLinus Torvalds #include <sound/emu10k1.h>
1527fe864eSJames Courtier-Dutton #include <linux/delay.h>
16d81a6d71SPaul Gortmaker #include <linux/export.h>
17184c1e2cSJames Courtier-Dutton #include "p17v.h"
181da177e4SLinus Torvalds 
check_ptr_reg(struct snd_emu10k1 * emu,unsigned int reg)193676cd4bSOswald Buddenhagen static inline bool check_ptr_reg(struct snd_emu10k1 *emu, unsigned int reg)
203676cd4bSOswald Buddenhagen {
213676cd4bSOswald Buddenhagen 	if (snd_BUG_ON(!emu))
223676cd4bSOswald Buddenhagen 		return false;
233676cd4bSOswald Buddenhagen 	if (snd_BUG_ON(reg & (emu->audigy ? (0xffff0000 & ~A_PTR_ADDRESS_MASK)
243676cd4bSOswald Buddenhagen 					  : (0xffff0000 & ~PTR_ADDRESS_MASK))))
253676cd4bSOswald Buddenhagen 		return false;
263676cd4bSOswald Buddenhagen 	if (snd_BUG_ON(reg & 0x0000ffff & ~PTR_CHANNELNUM_MASK))
273676cd4bSOswald Buddenhagen 		return false;
283676cd4bSOswald Buddenhagen 	return true;
293676cd4bSOswald Buddenhagen }
303676cd4bSOswald Buddenhagen 
snd_emu10k1_ptr_read(struct snd_emu10k1 * emu,unsigned int reg,unsigned int chn)31eb4698f3STakashi Iwai unsigned int snd_emu10k1_ptr_read(struct snd_emu10k1 * emu, unsigned int reg, unsigned int chn)
321da177e4SLinus Torvalds {
331da177e4SLinus Torvalds 	unsigned long flags;
341da177e4SLinus Torvalds 	unsigned int regptr, val;
351da177e4SLinus Torvalds 	unsigned int mask;
361da177e4SLinus Torvalds 
373676cd4bSOswald Buddenhagen 	regptr = (reg << 16) | chn;
383676cd4bSOswald Buddenhagen 	if (!check_ptr_reg(emu, regptr))
393676cd4bSOswald Buddenhagen 		return 0;
401da177e4SLinus Torvalds 
412093dcfcSOswald Buddenhagen 	spin_lock_irqsave(&emu->emu_lock, flags);
422093dcfcSOswald Buddenhagen 	outl(regptr, emu->port + PTR);
432093dcfcSOswald Buddenhagen 	val = inl(emu->port + DATA);
442093dcfcSOswald Buddenhagen 	spin_unlock_irqrestore(&emu->emu_lock, flags);
452093dcfcSOswald Buddenhagen 
461da177e4SLinus Torvalds 	if (reg & 0xff000000) {
471da177e4SLinus Torvalds 		unsigned char size, offset;
481da177e4SLinus Torvalds 
491da177e4SLinus Torvalds 		size = (reg >> 24) & 0x3f;
501da177e4SLinus Torvalds 		offset = (reg >> 16) & 0x1f;
512e9bd50fSOswald Buddenhagen 		mask = (1 << size) - 1;
521da177e4SLinus Torvalds 
532e9bd50fSOswald Buddenhagen 		return (val >> offset) & mask;
541da177e4SLinus Torvalds 	} else {
551da177e4SLinus Torvalds 		return val;
561da177e4SLinus Torvalds 	}
571da177e4SLinus Torvalds }
581da177e4SLinus Torvalds 
592dd31deeSTakashi Iwai EXPORT_SYMBOL(snd_emu10k1_ptr_read);
602dd31deeSTakashi Iwai 
snd_emu10k1_ptr_write(struct snd_emu10k1 * emu,unsigned int reg,unsigned int chn,unsigned int data)61eb4698f3STakashi Iwai void snd_emu10k1_ptr_write(struct snd_emu10k1 *emu, unsigned int reg, unsigned int chn, unsigned int data)
621da177e4SLinus Torvalds {
631da177e4SLinus Torvalds 	unsigned int regptr;
641da177e4SLinus Torvalds 	unsigned long flags;
651da177e4SLinus Torvalds 	unsigned int mask;
661da177e4SLinus Torvalds 
673676cd4bSOswald Buddenhagen 	regptr = (reg << 16) | chn;
683676cd4bSOswald Buddenhagen 	if (!check_ptr_reg(emu, regptr))
69c94fa4c9SJames Courtier-Dutton 		return;
701da177e4SLinus Torvalds 
711da177e4SLinus Torvalds 	if (reg & 0xff000000) {
721da177e4SLinus Torvalds 		unsigned char size, offset;
731da177e4SLinus Torvalds 
741da177e4SLinus Torvalds 		size = (reg >> 24) & 0x3f;
751da177e4SLinus Torvalds 		offset = (reg >> 16) & 0x1f;
763676cd4bSOswald Buddenhagen 		mask = (1 << size) - 1;
773676cd4bSOswald Buddenhagen 		if (snd_BUG_ON(data & ~mask))
783676cd4bSOswald Buddenhagen 			return;
793676cd4bSOswald Buddenhagen 		mask <<= offset;
803676cd4bSOswald Buddenhagen 		data <<= offset;
811da177e4SLinus Torvalds 
821da177e4SLinus Torvalds 		spin_lock_irqsave(&emu->emu_lock, flags);
831da177e4SLinus Torvalds 		outl(regptr, emu->port + PTR);
841da177e4SLinus Torvalds 		data |= inl(emu->port + DATA) & ~mask;
851da177e4SLinus Torvalds 	} else {
861da177e4SLinus Torvalds 		spin_lock_irqsave(&emu->emu_lock, flags);
871da177e4SLinus Torvalds 		outl(regptr, emu->port + PTR);
882093dcfcSOswald Buddenhagen 	}
891da177e4SLinus Torvalds 	outl(data, emu->port + DATA);
901da177e4SLinus Torvalds 	spin_unlock_irqrestore(&emu->emu_lock, flags);
911da177e4SLinus Torvalds }
921da177e4SLinus Torvalds 
932dd31deeSTakashi Iwai EXPORT_SYMBOL(snd_emu10k1_ptr_write);
942dd31deeSTakashi Iwai 
snd_emu10k1_ptr_write_multiple(struct snd_emu10k1 * emu,unsigned int chn,...)9546055699SOswald Buddenhagen void snd_emu10k1_ptr_write_multiple(struct snd_emu10k1 *emu, unsigned int chn, ...)
9646055699SOswald Buddenhagen {
9746055699SOswald Buddenhagen 	va_list va;
9846055699SOswald Buddenhagen 	u32 addr_mask;
9946055699SOswald Buddenhagen 	unsigned long flags;
10046055699SOswald Buddenhagen 
10146055699SOswald Buddenhagen 	if (snd_BUG_ON(!emu))
10246055699SOswald Buddenhagen 		return;
10346055699SOswald Buddenhagen 	if (snd_BUG_ON(chn & ~PTR_CHANNELNUM_MASK))
10446055699SOswald Buddenhagen 		return;
10546055699SOswald Buddenhagen 	addr_mask = ~((emu->audigy ? A_PTR_ADDRESS_MASK : PTR_ADDRESS_MASK) >> 16);
10646055699SOswald Buddenhagen 
10746055699SOswald Buddenhagen 	va_start(va, chn);
10846055699SOswald Buddenhagen 	spin_lock_irqsave(&emu->emu_lock, flags);
10946055699SOswald Buddenhagen 	for (;;) {
11046055699SOswald Buddenhagen 		u32 data;
11146055699SOswald Buddenhagen 		u32 reg = va_arg(va, u32);
11246055699SOswald Buddenhagen 		if (reg == REGLIST_END)
11346055699SOswald Buddenhagen 			break;
11446055699SOswald Buddenhagen 		data = va_arg(va, u32);
11546055699SOswald Buddenhagen 		if (snd_BUG_ON(reg & addr_mask))  // Only raw registers supported here
11646055699SOswald Buddenhagen 			continue;
11746055699SOswald Buddenhagen 		outl((reg << 16) | chn, emu->port + PTR);
11846055699SOswald Buddenhagen 		outl(data, emu->port + DATA);
11946055699SOswald Buddenhagen 	}
12046055699SOswald Buddenhagen 	spin_unlock_irqrestore(&emu->emu_lock, flags);
12146055699SOswald Buddenhagen 	va_end(va);
12246055699SOswald Buddenhagen }
12346055699SOswald Buddenhagen 
12446055699SOswald Buddenhagen EXPORT_SYMBOL(snd_emu10k1_ptr_write_multiple);
12546055699SOswald Buddenhagen 
snd_emu10k1_ptr20_read(struct snd_emu10k1 * emu,unsigned int reg,unsigned int chn)126eb4698f3STakashi Iwai unsigned int snd_emu10k1_ptr20_read(struct snd_emu10k1 * emu,
1271da177e4SLinus Torvalds 					  unsigned int reg,
1281da177e4SLinus Torvalds 					  unsigned int chn)
1291da177e4SLinus Torvalds {
1301da177e4SLinus Torvalds 	unsigned long flags;
1311da177e4SLinus Torvalds 	unsigned int regptr, val;
1321da177e4SLinus Torvalds 
1331da177e4SLinus Torvalds 	regptr = (reg << 16) | chn;
1341da177e4SLinus Torvalds 
1351da177e4SLinus Torvalds 	spin_lock_irqsave(&emu->emu_lock, flags);
1369d2f3863SOswald Buddenhagen 	outl(regptr, emu->port + PTR2);
1379d2f3863SOswald Buddenhagen 	val = inl(emu->port + DATA2);
1381da177e4SLinus Torvalds 	spin_unlock_irqrestore(&emu->emu_lock, flags);
1391da177e4SLinus Torvalds 	return val;
1401da177e4SLinus Torvalds }
1411da177e4SLinus Torvalds 
snd_emu10k1_ptr20_write(struct snd_emu10k1 * emu,unsigned int reg,unsigned int chn,unsigned int data)142eb4698f3STakashi Iwai void snd_emu10k1_ptr20_write(struct snd_emu10k1 *emu,
1431da177e4SLinus Torvalds 				   unsigned int reg,
1441da177e4SLinus Torvalds 				   unsigned int chn,
1451da177e4SLinus Torvalds 				   unsigned int data)
1461da177e4SLinus Torvalds {
1471da177e4SLinus Torvalds 	unsigned int regptr;
1481da177e4SLinus Torvalds 	unsigned long flags;
1491da177e4SLinus Torvalds 
1501da177e4SLinus Torvalds 	regptr = (reg << 16) | chn;
1511da177e4SLinus Torvalds 
1521da177e4SLinus Torvalds 	spin_lock_irqsave(&emu->emu_lock, flags);
1539d2f3863SOswald Buddenhagen 	outl(regptr, emu->port + PTR2);
1549d2f3863SOswald Buddenhagen 	outl(data, emu->port + DATA2);
1551da177e4SLinus Torvalds 	spin_unlock_irqrestore(&emu->emu_lock, flags);
1561da177e4SLinus Torvalds }
1571da177e4SLinus Torvalds 
snd_emu10k1_spi_write(struct snd_emu10k1 * emu,unsigned int data)15827fe864eSJames Courtier-Dutton int snd_emu10k1_spi_write(struct snd_emu10k1 * emu,
15927fe864eSJames Courtier-Dutton 				   unsigned int data)
16027fe864eSJames Courtier-Dutton {
16127fe864eSJames Courtier-Dutton 	unsigned int reset, set;
16227fe864eSJames Courtier-Dutton 	unsigned int reg, tmp;
16327fe864eSJames Courtier-Dutton 	int n, result;
164c94fa4c9SJames Courtier-Dutton 	int err = 0;
165c94fa4c9SJames Courtier-Dutton 
166c94fa4c9SJames Courtier-Dutton 	/* This function is not re-entrant, so protect against it. */
167c94fa4c9SJames Courtier-Dutton 	spin_lock(&emu->spi_lock);
16828bcbdddSJames Courtier-Dutton 	if (emu->card_capabilities->ca0108_chip)
1699d2f3863SOswald Buddenhagen 		reg = P17V_SPI;
17028bcbdddSJames Courtier-Dutton 	else {
17128bcbdddSJames Courtier-Dutton 		/* For other chip types the SPI register
17228bcbdddSJames Courtier-Dutton 		 * is currently unknown. */
173c94fa4c9SJames Courtier-Dutton 		err = 1;
174c94fa4c9SJames Courtier-Dutton 		goto spi_write_exit;
17527fe864eSJames Courtier-Dutton 	}
176c94fa4c9SJames Courtier-Dutton 	if (data > 0xffff) {
177c94fa4c9SJames Courtier-Dutton 		/* Only 16bit values allowed */
178c94fa4c9SJames Courtier-Dutton 		err = 1;
179c94fa4c9SJames Courtier-Dutton 		goto spi_write_exit;
180c94fa4c9SJames Courtier-Dutton 	}
18127fe864eSJames Courtier-Dutton 
18227fe864eSJames Courtier-Dutton 	tmp = snd_emu10k1_ptr20_read(emu, reg, 0);
18327fe864eSJames Courtier-Dutton 	reset = (tmp & ~0x3ffff) | 0x20000; /* Set xxx20000 */
18427fe864eSJames Courtier-Dutton 	set = reset | 0x10000; /* Set xxx1xxxx */
18527fe864eSJames Courtier-Dutton 	snd_emu10k1_ptr20_write(emu, reg, 0, reset | data);
18627fe864eSJames Courtier-Dutton 	tmp = snd_emu10k1_ptr20_read(emu, reg, 0); /* write post */
18727fe864eSJames Courtier-Dutton 	snd_emu10k1_ptr20_write(emu, reg, 0, set | data);
18827fe864eSJames Courtier-Dutton 	result = 1;
18927fe864eSJames Courtier-Dutton 	/* Wait for status bit to return to 0 */
19027fe864eSJames Courtier-Dutton 	for (n = 0; n < 100; n++) {
19127fe864eSJames Courtier-Dutton 		udelay(10);
19227fe864eSJames Courtier-Dutton 		tmp = snd_emu10k1_ptr20_read(emu, reg, 0);
19327fe864eSJames Courtier-Dutton 		if (!(tmp & 0x10000)) {
19427fe864eSJames Courtier-Dutton 			result = 0;
19527fe864eSJames Courtier-Dutton 			break;
19627fe864eSJames Courtier-Dutton 		}
19727fe864eSJames Courtier-Dutton 	}
198c94fa4c9SJames Courtier-Dutton 	if (result) {
199c94fa4c9SJames Courtier-Dutton 		/* Timed out */
200c94fa4c9SJames Courtier-Dutton 		err = 1;
201c94fa4c9SJames Courtier-Dutton 		goto spi_write_exit;
202c94fa4c9SJames Courtier-Dutton 	}
20327fe864eSJames Courtier-Dutton 	snd_emu10k1_ptr20_write(emu, reg, 0, reset | data);
20427fe864eSJames Courtier-Dutton 	tmp = snd_emu10k1_ptr20_read(emu, reg, 0); /* Write post */
205c94fa4c9SJames Courtier-Dutton 	err = 0;
206c94fa4c9SJames Courtier-Dutton spi_write_exit:
207c94fa4c9SJames Courtier-Dutton 	spin_unlock(&emu->spi_lock);
208c94fa4c9SJames Courtier-Dutton 	return err;
20927fe864eSJames Courtier-Dutton }
21027fe864eSJames Courtier-Dutton 
211184c1e2cSJames Courtier-Dutton /* The ADC does not support i2c read, so only write is implemented */
snd_emu10k1_i2c_write(struct snd_emu10k1 * emu,u32 reg,u32 value)212184c1e2cSJames Courtier-Dutton int snd_emu10k1_i2c_write(struct snd_emu10k1 *emu,
213184c1e2cSJames Courtier-Dutton 				u32 reg,
214184c1e2cSJames Courtier-Dutton 				u32 value)
215184c1e2cSJames Courtier-Dutton {
216184c1e2cSJames Courtier-Dutton 	u32 tmp;
217184c1e2cSJames Courtier-Dutton 	int timeout = 0;
218184c1e2cSJames Courtier-Dutton 	int status;
219184c1e2cSJames Courtier-Dutton 	int retry;
220c94fa4c9SJames Courtier-Dutton 	int err = 0;
221c94fa4c9SJames Courtier-Dutton 
222184c1e2cSJames Courtier-Dutton 	if ((reg > 0x7f) || (value > 0x1ff)) {
2236f002b02STakashi Iwai 		dev_err(emu->card->dev, "i2c_write: invalid values.\n");
224184c1e2cSJames Courtier-Dutton 		return -EINVAL;
225184c1e2cSJames Courtier-Dutton 	}
226184c1e2cSJames Courtier-Dutton 
227c94fa4c9SJames Courtier-Dutton 	/* This function is not re-entrant, so protect against it. */
228c94fa4c9SJames Courtier-Dutton 	spin_lock(&emu->i2c_lock);
229c94fa4c9SJames Courtier-Dutton 
230184c1e2cSJames Courtier-Dutton 	tmp = reg << 25 | value << 16;
231184c1e2cSJames Courtier-Dutton 
232184c1e2cSJames Courtier-Dutton 	/* This controls the I2C connected to the WM8775 ADC Codec */
233184c1e2cSJames Courtier-Dutton 	snd_emu10k1_ptr20_write(emu, P17V_I2C_1, 0, tmp);
234184c1e2cSJames Courtier-Dutton 	tmp = snd_emu10k1_ptr20_read(emu, P17V_I2C_1, 0); /* write post */
235184c1e2cSJames Courtier-Dutton 
236184c1e2cSJames Courtier-Dutton 	for (retry = 0; retry < 10; retry++) {
237184c1e2cSJames Courtier-Dutton 		/* Send the data to i2c */
238184c1e2cSJames Courtier-Dutton 		tmp = 0;
239184c1e2cSJames Courtier-Dutton 		tmp = tmp | (I2C_A_ADC_LAST|I2C_A_ADC_START|I2C_A_ADC_ADD);
240184c1e2cSJames Courtier-Dutton 		snd_emu10k1_ptr20_write(emu, P17V_I2C_ADDR, 0, tmp);
241184c1e2cSJames Courtier-Dutton 
242184c1e2cSJames Courtier-Dutton 		/* Wait till the transaction ends */
243184c1e2cSJames Courtier-Dutton 		while (1) {
244c94fa4c9SJames Courtier-Dutton 			mdelay(1);
245184c1e2cSJames Courtier-Dutton 			status = snd_emu10k1_ptr20_read(emu, P17V_I2C_ADDR, 0);
246184c1e2cSJames Courtier-Dutton 			timeout++;
247184c1e2cSJames Courtier-Dutton 			if ((status & I2C_A_ADC_START) == 0)
248184c1e2cSJames Courtier-Dutton 				break;
249184c1e2cSJames Courtier-Dutton 
250184c1e2cSJames Courtier-Dutton 			if (timeout > 1000) {
2516f002b02STakashi Iwai 				dev_warn(emu->card->dev,
25228a97c19STakashi Iwai 					   "emu10k1:I2C:timeout status=0x%x\n",
25328a97c19STakashi Iwai 					   status);
254184c1e2cSJames Courtier-Dutton 				break;
255184c1e2cSJames Courtier-Dutton 			}
256184c1e2cSJames Courtier-Dutton 		}
257184c1e2cSJames Courtier-Dutton 		//Read back and see if the transaction is successful
258184c1e2cSJames Courtier-Dutton 		if ((status & I2C_A_ADC_ABORT) == 0)
259184c1e2cSJames Courtier-Dutton 			break;
260184c1e2cSJames Courtier-Dutton 	}
261184c1e2cSJames Courtier-Dutton 
262184c1e2cSJames Courtier-Dutton 	if (retry == 10) {
2636f002b02STakashi Iwai 		dev_err(emu->card->dev, "Writing to ADC failed!\n");
2646f002b02STakashi Iwai 		dev_err(emu->card->dev, "status=0x%x, reg=%d, value=%d\n",
265c94fa4c9SJames Courtier-Dutton 			status, reg, value);
266c94fa4c9SJames Courtier-Dutton 		/* dump_stack(); */
267c94fa4c9SJames Courtier-Dutton 		err = -EINVAL;
268184c1e2cSJames Courtier-Dutton 	}
269184c1e2cSJames Courtier-Dutton 
270c94fa4c9SJames Courtier-Dutton 	spin_unlock(&emu->i2c_lock);
271c94fa4c9SJames Courtier-Dutton 	return err;
272184c1e2cSJames Courtier-Dutton }
273184c1e2cSJames Courtier-Dutton 
snd_emu1010_fpga_write_locked(struct snd_emu10k1 * emu,u32 reg,u32 value)27435d1d582SOswald Buddenhagen static void snd_emu1010_fpga_write_locked(struct snd_emu10k1 *emu, u32 reg, u32 value)
2759f4bd5ddSJames Courtier-Dutton {
27610f212bdSOswald Buddenhagen 	if (snd_BUG_ON(reg > 0x3f))
27710f212bdSOswald Buddenhagen 		return;
2789f4bd5ddSJames Courtier-Dutton 	reg += 0x40; /* 0x40 upwards are registers. */
27910f212bdSOswald Buddenhagen 	if (snd_BUG_ON(value > 0x3f)) /* 0 to 0x3f are values */
28010f212bdSOswald Buddenhagen 		return;
281a1c87c0bSOswald Buddenhagen 	outw(reg, emu->port + A_GPIO);
2829f4bd5ddSJames Courtier-Dutton 	udelay(10);
283a1c87c0bSOswald Buddenhagen 	outw(reg | 0x80, emu->port + A_GPIO);  /* High bit clocks the value into the fpga. */
2849f4bd5ddSJames Courtier-Dutton 	udelay(10);
285a1c87c0bSOswald Buddenhagen 	outw(value, emu->port + A_GPIO);
2869f4bd5ddSJames Courtier-Dutton 	udelay(10);
287a1c87c0bSOswald Buddenhagen 	outw(value | 0x80 , emu->port + A_GPIO);  /* High bit clocks the value into the fpga. */
28835d1d582SOswald Buddenhagen }
28935d1d582SOswald Buddenhagen 
snd_emu1010_fpga_write(struct snd_emu10k1 * emu,u32 reg,u32 value)29035d1d582SOswald Buddenhagen void snd_emu1010_fpga_write(struct snd_emu10k1 *emu, u32 reg, u32 value)
29135d1d582SOswald Buddenhagen {
29235d1d582SOswald Buddenhagen 	unsigned long flags;
29335d1d582SOswald Buddenhagen 
29435d1d582SOswald Buddenhagen 	spin_lock_irqsave(&emu->emu_lock, flags);
29535d1d582SOswald Buddenhagen 	snd_emu1010_fpga_write_locked(emu, reg, value);
296190d2c46SJames Courtier-Dutton 	spin_unlock_irqrestore(&emu->emu_lock, flags);
2979f4bd5ddSJames Courtier-Dutton }
2989f4bd5ddSJames Courtier-Dutton 
snd_emu1010_fpga_read_locked(struct snd_emu10k1 * emu,u32 reg,u32 * value)2996ab13291SOswald Buddenhagen static void snd_emu1010_fpga_read_locked(struct snd_emu10k1 *emu, u32 reg, u32 *value)
3009f4bd5ddSJames Courtier-Dutton {
3016fb861bbSOswald Buddenhagen 	// The higest input pin is used as the designated interrupt trigger,
3026fb861bbSOswald Buddenhagen 	// so it needs to be masked out.
303fbb64eedSOswald Buddenhagen 	// But note that any other input pin change will also cause an IRQ,
304fbb64eedSOswald Buddenhagen 	// so using this function often causes an IRQ as a side effect.
3056fb861bbSOswald Buddenhagen 	u32 mask = emu->card_capabilities->ca0108_chip ? 0x1f : 0x7f;
30610f212bdSOswald Buddenhagen 	if (snd_BUG_ON(reg > 0x3f))
30710f212bdSOswald Buddenhagen 		return;
3089f4bd5ddSJames Courtier-Dutton 	reg += 0x40; /* 0x40 upwards are registers. */
309a1c87c0bSOswald Buddenhagen 	outw(reg, emu->port + A_GPIO);
3109f4bd5ddSJames Courtier-Dutton 	udelay(10);
311a1c87c0bSOswald Buddenhagen 	outw(reg | 0x80, emu->port + A_GPIO);  /* High bit clocks the value into the fpga. */
3129f4bd5ddSJames Courtier-Dutton 	udelay(10);
3136fb861bbSOswald Buddenhagen 	*value = ((inw(emu->port + A_GPIO) >> 8) & mask);
3146ab13291SOswald Buddenhagen }
3156ab13291SOswald Buddenhagen 
snd_emu1010_fpga_read(struct snd_emu10k1 * emu,u32 reg,u32 * value)3166ab13291SOswald Buddenhagen void snd_emu1010_fpga_read(struct snd_emu10k1 *emu, u32 reg, u32 *value)
3176ab13291SOswald Buddenhagen {
3186ab13291SOswald Buddenhagen 	unsigned long flags;
3196ab13291SOswald Buddenhagen 
3206ab13291SOswald Buddenhagen 	spin_lock_irqsave(&emu->emu_lock, flags);
3216ab13291SOswald Buddenhagen 	snd_emu1010_fpga_read_locked(emu, reg, value);
322190d2c46SJames Courtier-Dutton 	spin_unlock_irqrestore(&emu->emu_lock, flags);
3239f4bd5ddSJames Courtier-Dutton }
3249f4bd5ddSJames Courtier-Dutton 
3259f4bd5ddSJames Courtier-Dutton /* Each Destination has one and only one Source,
3269f4bd5ddSJames Courtier-Dutton  * but one Source can feed any number of Destinations simultaneously.
3279f4bd5ddSJames Courtier-Dutton  */
snd_emu1010_fpga_link_dst_src_write(struct snd_emu10k1 * emu,u32 dst,u32 src)32810f212bdSOswald Buddenhagen void snd_emu1010_fpga_link_dst_src_write(struct snd_emu10k1 *emu, u32 dst, u32 src)
3299f4bd5ddSJames Courtier-Dutton {
33035d1d582SOswald Buddenhagen 	unsigned long flags;
33135d1d582SOswald Buddenhagen 
33210f212bdSOswald Buddenhagen 	if (snd_BUG_ON(dst & ~0x71f))
33310f212bdSOswald Buddenhagen 		return;
33410f212bdSOswald Buddenhagen 	if (snd_BUG_ON(src & ~0x71f))
33510f212bdSOswald Buddenhagen 		return;
33635d1d582SOswald Buddenhagen 	spin_lock_irqsave(&emu->emu_lock, flags);
33735d1d582SOswald Buddenhagen 	snd_emu1010_fpga_write_locked(emu, EMU_HANA_DESTHI, dst >> 8);
33835d1d582SOswald Buddenhagen 	snd_emu1010_fpga_write_locked(emu, EMU_HANA_DESTLO, dst & 0x1f);
33935d1d582SOswald Buddenhagen 	snd_emu1010_fpga_write_locked(emu, EMU_HANA_SRCHI, src >> 8);
34035d1d582SOswald Buddenhagen 	snd_emu1010_fpga_write_locked(emu, EMU_HANA_SRCLO, src & 0x1f);
34135d1d582SOswald Buddenhagen 	spin_unlock_irqrestore(&emu->emu_lock, flags);
3429f4bd5ddSJames Courtier-Dutton }
3439f4bd5ddSJames Courtier-Dutton 
snd_emu1010_fpga_link_dst_src_read(struct snd_emu10k1 * emu,u32 dst)3446ab13291SOswald Buddenhagen u32 snd_emu1010_fpga_link_dst_src_read(struct snd_emu10k1 *emu, u32 dst)
3456ab13291SOswald Buddenhagen {
3466ab13291SOswald Buddenhagen 	unsigned long flags;
3476ab13291SOswald Buddenhagen 	u32 hi, lo;
3486ab13291SOswald Buddenhagen 
3496ab13291SOswald Buddenhagen 	if (snd_BUG_ON(dst & ~0x71f))
3506ab13291SOswald Buddenhagen 		return 0;
3516ab13291SOswald Buddenhagen 	spin_lock_irqsave(&emu->emu_lock, flags);
3526ab13291SOswald Buddenhagen 	snd_emu1010_fpga_write_locked(emu, EMU_HANA_DESTHI, dst >> 8);
3536ab13291SOswald Buddenhagen 	snd_emu1010_fpga_write_locked(emu, EMU_HANA_DESTLO, dst & 0x1f);
3546ab13291SOswald Buddenhagen 	snd_emu1010_fpga_read_locked(emu, EMU_HANA_SRCHI, &hi);
3556ab13291SOswald Buddenhagen 	snd_emu1010_fpga_read_locked(emu, EMU_HANA_SRCLO, &lo);
3566ab13291SOswald Buddenhagen 	spin_unlock_irqrestore(&emu->emu_lock, flags);
3576ab13291SOswald Buddenhagen 	return (hi << 8) | lo;
3586ab13291SOswald Buddenhagen }
3596ab13291SOswald Buddenhagen 
snd_emu1010_get_raw_rate(struct snd_emu10k1 * emu,u8 src)360e73b597eSOswald Buddenhagen int snd_emu1010_get_raw_rate(struct snd_emu10k1 *emu, u8 src)
361e73b597eSOswald Buddenhagen {
362e73b597eSOswald Buddenhagen 	u32 reg_lo, reg_hi, value, value2;
363e73b597eSOswald Buddenhagen 
364e73b597eSOswald Buddenhagen 	switch (src) {
365e73b597eSOswald Buddenhagen 	case EMU_HANA_WCLOCK_HANA_SPDIF_IN:
366e73b597eSOswald Buddenhagen 		snd_emu1010_fpga_read(emu, EMU_HANA_SPDIF_MODE, &value);
367e73b597eSOswald Buddenhagen 		if (value & EMU_HANA_SPDIF_MODE_RX_INVALID)
368e73b597eSOswald Buddenhagen 			return 0;
369e73b597eSOswald Buddenhagen 		reg_lo = EMU_HANA_WC_SPDIF_LO;
370e73b597eSOswald Buddenhagen 		reg_hi = EMU_HANA_WC_SPDIF_HI;
371e73b597eSOswald Buddenhagen 		break;
372e73b597eSOswald Buddenhagen 	case EMU_HANA_WCLOCK_HANA_ADAT_IN:
373e73b597eSOswald Buddenhagen 		reg_lo = EMU_HANA_WC_ADAT_LO;
374e73b597eSOswald Buddenhagen 		reg_hi = EMU_HANA_WC_ADAT_HI;
375e73b597eSOswald Buddenhagen 		break;
376e73b597eSOswald Buddenhagen 	case EMU_HANA_WCLOCK_SYNC_BNC:
377e73b597eSOswald Buddenhagen 		reg_lo = EMU_HANA_WC_BNC_LO;
378e73b597eSOswald Buddenhagen 		reg_hi = EMU_HANA_WC_BNC_HI;
379e73b597eSOswald Buddenhagen 		break;
380e73b597eSOswald Buddenhagen 	case EMU_HANA_WCLOCK_2ND_HANA:
381e73b597eSOswald Buddenhagen 		reg_lo = EMU_HANA2_WC_SPDIF_LO;
382e73b597eSOswald Buddenhagen 		reg_hi = EMU_HANA2_WC_SPDIF_HI;
383e73b597eSOswald Buddenhagen 		break;
384e73b597eSOswald Buddenhagen 	default:
385e73b597eSOswald Buddenhagen 		return 0;
386e73b597eSOswald Buddenhagen 	}
387e73b597eSOswald Buddenhagen 	snd_emu1010_fpga_read(emu, reg_hi, &value);
388e73b597eSOswald Buddenhagen 	snd_emu1010_fpga_read(emu, reg_lo, &value2);
389e73b597eSOswald Buddenhagen 	// FIXME: The /4 is valid for 0404b, but contradicts all other info.
390e73b597eSOswald Buddenhagen 	return 0x1770000 / 4 / (((value << 5) | value2) + 1);
391e73b597eSOswald Buddenhagen }
392e73b597eSOswald Buddenhagen 
snd_emu1010_update_clock(struct snd_emu10k1 * emu)39360985241SOswald Buddenhagen void snd_emu1010_update_clock(struct snd_emu10k1 *emu)
39460985241SOswald Buddenhagen {
395e73b597eSOswald Buddenhagen 	int clock;
39660985241SOswald Buddenhagen 	u32 leds;
39760985241SOswald Buddenhagen 
39860985241SOswald Buddenhagen 	switch (emu->emu1010.wclock) {
39960985241SOswald Buddenhagen 	case EMU_HANA_WCLOCK_INT_44_1K | EMU_HANA_WCLOCK_1X:
400e73b597eSOswald Buddenhagen 		clock = 44100;
40160985241SOswald Buddenhagen 		leds = EMU_HANA_DOCK_LEDS_2_44K;
40260985241SOswald Buddenhagen 		break;
40360985241SOswald Buddenhagen 	case EMU_HANA_WCLOCK_INT_48K | EMU_HANA_WCLOCK_1X:
404e73b597eSOswald Buddenhagen 		clock = 48000;
40560985241SOswald Buddenhagen 		leds = EMU_HANA_DOCK_LEDS_2_48K;
40660985241SOswald Buddenhagen 		break;
40760985241SOswald Buddenhagen 	default:
408e73b597eSOswald Buddenhagen 		clock = snd_emu1010_get_raw_rate(
409e73b597eSOswald Buddenhagen 				emu, emu->emu1010.wclock & EMU_HANA_WCLOCK_SRC_MASK);
410e73b597eSOswald Buddenhagen 		// The raw rate reading is rather coarse (it cannot accurately
411e73b597eSOswald Buddenhagen 		// represent 44.1 kHz) and fluctuates slightly. Luckily, the
412e73b597eSOswald Buddenhagen 		// clock comes from digital inputs, which use standardized rates.
413e73b597eSOswald Buddenhagen 		// So we round to the closest standard rate and ignore discrepancies.
414e73b597eSOswald Buddenhagen 		if (clock < 46000) {
415e73b597eSOswald Buddenhagen 			clock = 44100;
416e73b597eSOswald Buddenhagen 			leds = EMU_HANA_DOCK_LEDS_2_EXT | EMU_HANA_DOCK_LEDS_2_44K;
417e73b597eSOswald Buddenhagen 		} else {
418e73b597eSOswald Buddenhagen 			clock = 48000;
419e73b597eSOswald Buddenhagen 			leds = EMU_HANA_DOCK_LEDS_2_EXT | EMU_HANA_DOCK_LEDS_2_48K;
420e73b597eSOswald Buddenhagen 		}
42160985241SOswald Buddenhagen 		break;
42260985241SOswald Buddenhagen 	}
423e73b597eSOswald Buddenhagen 	emu->emu1010.word_clock = clock;
42460985241SOswald Buddenhagen 
42560985241SOswald Buddenhagen 	// FIXME: this should probably represent the AND of all currently
42660985241SOswald Buddenhagen 	// used sources' lock status. But we don't know how to get that ...
42760985241SOswald Buddenhagen 	leds |= EMU_HANA_DOCK_LEDS_2_LOCK;
42860985241SOswald Buddenhagen 
42960985241SOswald Buddenhagen 	snd_emu1010_fpga_write(emu, EMU_HANA_DOCK_LEDS_2, leds);
43060985241SOswald Buddenhagen }
43160985241SOswald Buddenhagen 
snd_emu10k1_intr_enable(struct snd_emu10k1 * emu,unsigned int intrenb)432eb4698f3STakashi Iwai void snd_emu10k1_intr_enable(struct snd_emu10k1 *emu, unsigned int intrenb)
4331da177e4SLinus Torvalds {
4341da177e4SLinus Torvalds 	unsigned long flags;
4351da177e4SLinus Torvalds 	unsigned int enable;
4361da177e4SLinus Torvalds 
4371da177e4SLinus Torvalds 	spin_lock_irqsave(&emu->emu_lock, flags);
4381da177e4SLinus Torvalds 	enable = inl(emu->port + INTE) | intrenb;
4391da177e4SLinus Torvalds 	outl(enable, emu->port + INTE);
4401da177e4SLinus Torvalds 	spin_unlock_irqrestore(&emu->emu_lock, flags);
4411da177e4SLinus Torvalds }
4421da177e4SLinus Torvalds 
snd_emu10k1_intr_disable(struct snd_emu10k1 * emu,unsigned int intrenb)443eb4698f3STakashi Iwai void snd_emu10k1_intr_disable(struct snd_emu10k1 *emu, unsigned int intrenb)
4441da177e4SLinus Torvalds {
4451da177e4SLinus Torvalds 	unsigned long flags;
4461da177e4SLinus Torvalds 	unsigned int enable;
4471da177e4SLinus Torvalds 
4481da177e4SLinus Torvalds 	spin_lock_irqsave(&emu->emu_lock, flags);
4491da177e4SLinus Torvalds 	enable = inl(emu->port + INTE) & ~intrenb;
4501da177e4SLinus Torvalds 	outl(enable, emu->port + INTE);
4511da177e4SLinus Torvalds 	spin_unlock_irqrestore(&emu->emu_lock, flags);
4521da177e4SLinus Torvalds }
4531da177e4SLinus Torvalds 
snd_emu10k1_voice_intr_enable(struct snd_emu10k1 * emu,unsigned int voicenum)454eb4698f3STakashi Iwai void snd_emu10k1_voice_intr_enable(struct snd_emu10k1 *emu, unsigned int voicenum)
4551da177e4SLinus Torvalds {
4561da177e4SLinus Torvalds 	unsigned long flags;
4571da177e4SLinus Torvalds 	unsigned int val;
4581da177e4SLinus Torvalds 
4591da177e4SLinus Torvalds 	spin_lock_irqsave(&emu->emu_lock, flags);
4601da177e4SLinus Torvalds 	if (voicenum >= 32) {
4611da177e4SLinus Torvalds 		outl(CLIEH << 16, emu->port + PTR);
4621da177e4SLinus Torvalds 		val = inl(emu->port + DATA);
4631da177e4SLinus Torvalds 		val |= 1 << (voicenum - 32);
4641da177e4SLinus Torvalds 	} else {
4651da177e4SLinus Torvalds 		outl(CLIEL << 16, emu->port + PTR);
4661da177e4SLinus Torvalds 		val = inl(emu->port + DATA);
4671da177e4SLinus Torvalds 		val |= 1 << voicenum;
4681da177e4SLinus Torvalds 	}
4691da177e4SLinus Torvalds 	outl(val, emu->port + DATA);
4701da177e4SLinus Torvalds 	spin_unlock_irqrestore(&emu->emu_lock, flags);
4711da177e4SLinus Torvalds }
4721da177e4SLinus Torvalds 
snd_emu10k1_voice_intr_disable(struct snd_emu10k1 * emu,unsigned int voicenum)473eb4698f3STakashi Iwai void snd_emu10k1_voice_intr_disable(struct snd_emu10k1 *emu, unsigned int voicenum)
4741da177e4SLinus Torvalds {
4751da177e4SLinus Torvalds 	unsigned long flags;
4761da177e4SLinus Torvalds 	unsigned int val;
4771da177e4SLinus Torvalds 
4781da177e4SLinus Torvalds 	spin_lock_irqsave(&emu->emu_lock, flags);
4791da177e4SLinus Torvalds 	if (voicenum >= 32) {
4801da177e4SLinus Torvalds 		outl(CLIEH << 16, emu->port + PTR);
4811da177e4SLinus Torvalds 		val = inl(emu->port + DATA);
4821da177e4SLinus Torvalds 		val &= ~(1 << (voicenum - 32));
4831da177e4SLinus Torvalds 	} else {
4841da177e4SLinus Torvalds 		outl(CLIEL << 16, emu->port + PTR);
4851da177e4SLinus Torvalds 		val = inl(emu->port + DATA);
4861da177e4SLinus Torvalds 		val &= ~(1 << voicenum);
4871da177e4SLinus Torvalds 	}
4881da177e4SLinus Torvalds 	outl(val, emu->port + DATA);
4891da177e4SLinus Torvalds 	spin_unlock_irqrestore(&emu->emu_lock, flags);
4901da177e4SLinus Torvalds }
4911da177e4SLinus Torvalds 
snd_emu10k1_voice_intr_ack(struct snd_emu10k1 * emu,unsigned int voicenum)492eb4698f3STakashi Iwai void snd_emu10k1_voice_intr_ack(struct snd_emu10k1 *emu, unsigned int voicenum)
4931da177e4SLinus Torvalds {
4941da177e4SLinus Torvalds 	unsigned long flags;
4951da177e4SLinus Torvalds 
4961da177e4SLinus Torvalds 	spin_lock_irqsave(&emu->emu_lock, flags);
4971da177e4SLinus Torvalds 	if (voicenum >= 32) {
4981da177e4SLinus Torvalds 		outl(CLIPH << 16, emu->port + PTR);
4991da177e4SLinus Torvalds 		voicenum = 1 << (voicenum - 32);
5001da177e4SLinus Torvalds 	} else {
5011da177e4SLinus Torvalds 		outl(CLIPL << 16, emu->port + PTR);
5021da177e4SLinus Torvalds 		voicenum = 1 << voicenum;
5031da177e4SLinus Torvalds 	}
5041da177e4SLinus Torvalds 	outl(voicenum, emu->port + DATA);
5051da177e4SLinus Torvalds 	spin_unlock_irqrestore(&emu->emu_lock, flags);
5061da177e4SLinus Torvalds }
5071da177e4SLinus Torvalds 
snd_emu10k1_voice_half_loop_intr_enable(struct snd_emu10k1 * emu,unsigned int voicenum)508eb4698f3STakashi Iwai void snd_emu10k1_voice_half_loop_intr_enable(struct snd_emu10k1 *emu, unsigned int voicenum)
5091da177e4SLinus Torvalds {
5101da177e4SLinus Torvalds 	unsigned long flags;
5111da177e4SLinus Torvalds 	unsigned int val;
5121da177e4SLinus Torvalds 
5131da177e4SLinus Torvalds 	spin_lock_irqsave(&emu->emu_lock, flags);
5141da177e4SLinus Torvalds 	if (voicenum >= 32) {
5151da177e4SLinus Torvalds 		outl(HLIEH << 16, emu->port + PTR);
5161da177e4SLinus Torvalds 		val = inl(emu->port + DATA);
5171da177e4SLinus Torvalds 		val |= 1 << (voicenum - 32);
5181da177e4SLinus Torvalds 	} else {
5191da177e4SLinus Torvalds 		outl(HLIEL << 16, emu->port + PTR);
5201da177e4SLinus Torvalds 		val = inl(emu->port + DATA);
5211da177e4SLinus Torvalds 		val |= 1 << voicenum;
5221da177e4SLinus Torvalds 	}
5231da177e4SLinus Torvalds 	outl(val, emu->port + DATA);
5241da177e4SLinus Torvalds 	spin_unlock_irqrestore(&emu->emu_lock, flags);
5251da177e4SLinus Torvalds }
5261da177e4SLinus Torvalds 
snd_emu10k1_voice_half_loop_intr_disable(struct snd_emu10k1 * emu,unsigned int voicenum)527eb4698f3STakashi Iwai void snd_emu10k1_voice_half_loop_intr_disable(struct snd_emu10k1 *emu, unsigned int voicenum)
5281da177e4SLinus Torvalds {
5291da177e4SLinus Torvalds 	unsigned long flags;
5301da177e4SLinus Torvalds 	unsigned int val;
5311da177e4SLinus Torvalds 
5321da177e4SLinus Torvalds 	spin_lock_irqsave(&emu->emu_lock, flags);
5331da177e4SLinus Torvalds 	if (voicenum >= 32) {
5341da177e4SLinus Torvalds 		outl(HLIEH << 16, emu->port + PTR);
5351da177e4SLinus Torvalds 		val = inl(emu->port + DATA);
5361da177e4SLinus Torvalds 		val &= ~(1 << (voicenum - 32));
5371da177e4SLinus Torvalds 	} else {
5381da177e4SLinus Torvalds 		outl(HLIEL << 16, emu->port + PTR);
5391da177e4SLinus Torvalds 		val = inl(emu->port + DATA);
5401da177e4SLinus Torvalds 		val &= ~(1 << voicenum);
5411da177e4SLinus Torvalds 	}
5421da177e4SLinus Torvalds 	outl(val, emu->port + DATA);
5431da177e4SLinus Torvalds 	spin_unlock_irqrestore(&emu->emu_lock, flags);
5441da177e4SLinus Torvalds }
5451da177e4SLinus Torvalds 
snd_emu10k1_voice_half_loop_intr_ack(struct snd_emu10k1 * emu,unsigned int voicenum)546eb4698f3STakashi Iwai void snd_emu10k1_voice_half_loop_intr_ack(struct snd_emu10k1 *emu, unsigned int voicenum)
5471da177e4SLinus Torvalds {
5481da177e4SLinus Torvalds 	unsigned long flags;
5491da177e4SLinus Torvalds 
5501da177e4SLinus Torvalds 	spin_lock_irqsave(&emu->emu_lock, flags);
5511da177e4SLinus Torvalds 	if (voicenum >= 32) {
5521da177e4SLinus Torvalds 		outl(HLIPH << 16, emu->port + PTR);
5531da177e4SLinus Torvalds 		voicenum = 1 << (voicenum - 32);
5541da177e4SLinus Torvalds 	} else {
5551da177e4SLinus Torvalds 		outl(HLIPL << 16, emu->port + PTR);
5561da177e4SLinus Torvalds 		voicenum = 1 << voicenum;
5571da177e4SLinus Torvalds 	}
5581da177e4SLinus Torvalds 	outl(voicenum, emu->port + DATA);
5591da177e4SLinus Torvalds 	spin_unlock_irqrestore(&emu->emu_lock, flags);
5601da177e4SLinus Torvalds }
5611da177e4SLinus Torvalds 
562a61c695aSOswald Buddenhagen #if 0
563eb4698f3STakashi Iwai void snd_emu10k1_voice_set_loop_stop(struct snd_emu10k1 *emu, unsigned int voicenum)
5641da177e4SLinus Torvalds {
5651da177e4SLinus Torvalds 	unsigned long flags;
5661da177e4SLinus Torvalds 	unsigned int sol;
5671da177e4SLinus Torvalds 
5681da177e4SLinus Torvalds 	spin_lock_irqsave(&emu->emu_lock, flags);
5691da177e4SLinus Torvalds 	if (voicenum >= 32) {
5701da177e4SLinus Torvalds 		outl(SOLEH << 16, emu->port + PTR);
5711da177e4SLinus Torvalds 		sol = inl(emu->port + DATA);
5721da177e4SLinus Torvalds 		sol |= 1 << (voicenum - 32);
5731da177e4SLinus Torvalds 	} else {
5741da177e4SLinus Torvalds 		outl(SOLEL << 16, emu->port + PTR);
5751da177e4SLinus Torvalds 		sol = inl(emu->port + DATA);
5761da177e4SLinus Torvalds 		sol |= 1 << voicenum;
5771da177e4SLinus Torvalds 	}
5781da177e4SLinus Torvalds 	outl(sol, emu->port + DATA);
5791da177e4SLinus Torvalds 	spin_unlock_irqrestore(&emu->emu_lock, flags);
5801da177e4SLinus Torvalds }
5811da177e4SLinus Torvalds 
582eb4698f3STakashi Iwai void snd_emu10k1_voice_clear_loop_stop(struct snd_emu10k1 *emu, unsigned int voicenum)
5831da177e4SLinus Torvalds {
5841da177e4SLinus Torvalds 	unsigned long flags;
5851da177e4SLinus Torvalds 	unsigned int sol;
5861da177e4SLinus Torvalds 
5871da177e4SLinus Torvalds 	spin_lock_irqsave(&emu->emu_lock, flags);
5881da177e4SLinus Torvalds 	if (voicenum >= 32) {
5891da177e4SLinus Torvalds 		outl(SOLEH << 16, emu->port + PTR);
5901da177e4SLinus Torvalds 		sol = inl(emu->port + DATA);
5911da177e4SLinus Torvalds 		sol &= ~(1 << (voicenum - 32));
5921da177e4SLinus Torvalds 	} else {
5931da177e4SLinus Torvalds 		outl(SOLEL << 16, emu->port + PTR);
5941da177e4SLinus Torvalds 		sol = inl(emu->port + DATA);
5951da177e4SLinus Torvalds 		sol &= ~(1 << voicenum);
5961da177e4SLinus Torvalds 	}
5971da177e4SLinus Torvalds 	outl(sol, emu->port + DATA);
5981da177e4SLinus Torvalds 	spin_unlock_irqrestore(&emu->emu_lock, flags);
5991da177e4SLinus Torvalds }
600a61c695aSOswald Buddenhagen #endif
6011da177e4SLinus Torvalds 
snd_emu10k1_voice_set_loop_stop_multiple(struct snd_emu10k1 * emu,u64 voices)60211ee59bdSOswald Buddenhagen void snd_emu10k1_voice_set_loop_stop_multiple(struct snd_emu10k1 *emu, u64 voices)
60311ee59bdSOswald Buddenhagen {
60411ee59bdSOswald Buddenhagen 	unsigned long flags;
60511ee59bdSOswald Buddenhagen 
60611ee59bdSOswald Buddenhagen 	spin_lock_irqsave(&emu->emu_lock, flags);
60711ee59bdSOswald Buddenhagen 	outl(SOLEL << 16, emu->port + PTR);
60811ee59bdSOswald Buddenhagen 	outl(inl(emu->port + DATA) | (u32)voices, emu->port + DATA);
60911ee59bdSOswald Buddenhagen 	outl(SOLEH << 16, emu->port + PTR);
61011ee59bdSOswald Buddenhagen 	outl(inl(emu->port + DATA) | (u32)(voices >> 32), emu->port + DATA);
61111ee59bdSOswald Buddenhagen 	spin_unlock_irqrestore(&emu->emu_lock, flags);
61211ee59bdSOswald Buddenhagen }
61311ee59bdSOswald Buddenhagen 
snd_emu10k1_voice_clear_loop_stop_multiple(struct snd_emu10k1 * emu,u64 voices)61411ee59bdSOswald Buddenhagen void snd_emu10k1_voice_clear_loop_stop_multiple(struct snd_emu10k1 *emu, u64 voices)
61511ee59bdSOswald Buddenhagen {
61611ee59bdSOswald Buddenhagen 	unsigned long flags;
61711ee59bdSOswald Buddenhagen 
61811ee59bdSOswald Buddenhagen 	spin_lock_irqsave(&emu->emu_lock, flags);
61911ee59bdSOswald Buddenhagen 	outl(SOLEL << 16, emu->port + PTR);
62011ee59bdSOswald Buddenhagen 	outl(inl(emu->port + DATA) & (u32)~voices, emu->port + DATA);
62111ee59bdSOswald Buddenhagen 	outl(SOLEH << 16, emu->port + PTR);
62211ee59bdSOswald Buddenhagen 	outl(inl(emu->port + DATA) & (u32)(~voices >> 32), emu->port + DATA);
62311ee59bdSOswald Buddenhagen 	spin_unlock_irqrestore(&emu->emu_lock, flags);
62411ee59bdSOswald Buddenhagen }
62511ee59bdSOswald Buddenhagen 
snd_emu10k1_voice_clear_loop_stop_multiple_atomic(struct snd_emu10k1 * emu,u64 voices)62611ee59bdSOswald Buddenhagen int snd_emu10k1_voice_clear_loop_stop_multiple_atomic(struct snd_emu10k1 *emu, u64 voices)
62711ee59bdSOswald Buddenhagen {
62811ee59bdSOswald Buddenhagen 	unsigned long flags;
62911ee59bdSOswald Buddenhagen 	u32 soll, solh;
63011ee59bdSOswald Buddenhagen 	int ret = -EIO;
63111ee59bdSOswald Buddenhagen 
63211ee59bdSOswald Buddenhagen 	spin_lock_irqsave(&emu->emu_lock, flags);
63311ee59bdSOswald Buddenhagen 
63411ee59bdSOswald Buddenhagen 	outl(SOLEL << 16, emu->port + PTR);
63511ee59bdSOswald Buddenhagen 	soll = inl(emu->port + DATA);
63611ee59bdSOswald Buddenhagen 	outl(SOLEH << 16, emu->port + PTR);
63711ee59bdSOswald Buddenhagen 	solh = inl(emu->port + DATA);
63811ee59bdSOswald Buddenhagen 
63911ee59bdSOswald Buddenhagen 	soll &= (u32)~voices;
64011ee59bdSOswald Buddenhagen 	solh &= (u32)(~voices >> 32);
64111ee59bdSOswald Buddenhagen 
64211ee59bdSOswald Buddenhagen 	for (int tries = 0; tries < 1000; tries++) {
64311ee59bdSOswald Buddenhagen 		const u32 quart = 1U << (REG_SIZE(WC_CURRENTCHANNEL) - 2);
64411ee59bdSOswald Buddenhagen 		// First we wait for the third quarter of the sample cycle ...
64511ee59bdSOswald Buddenhagen 		u32 wc = inl(emu->port + WC);
64611ee59bdSOswald Buddenhagen 		u32 cc = REG_VAL_GET(WC_CURRENTCHANNEL, wc);
64711ee59bdSOswald Buddenhagen 		if (cc >= quart * 2 && cc < quart * 3) {
64811ee59bdSOswald Buddenhagen 			// ... and release the low voices, while the high ones are serviced.
64911ee59bdSOswald Buddenhagen 			outl(SOLEL << 16, emu->port + PTR);
65011ee59bdSOswald Buddenhagen 			outl(soll, emu->port + DATA);
65111ee59bdSOswald Buddenhagen 			// Then we wait for the first quarter of the next sample cycle ...
65211ee59bdSOswald Buddenhagen 			for (; tries < 1000; tries++) {
65311ee59bdSOswald Buddenhagen 				cc = REG_VAL_GET(WC_CURRENTCHANNEL, inl(emu->port + WC));
65411ee59bdSOswald Buddenhagen 				if (cc < quart)
65511ee59bdSOswald Buddenhagen 					goto good;
65611ee59bdSOswald Buddenhagen 				// We will block for 10+ us with interrupts disabled. This is
65711ee59bdSOswald Buddenhagen 				// not nice at all, but necessary for reasonable reliability.
65811ee59bdSOswald Buddenhagen 				udelay(1);
65911ee59bdSOswald Buddenhagen 			}
66011ee59bdSOswald Buddenhagen 			break;
66111ee59bdSOswald Buddenhagen 		good:
66211ee59bdSOswald Buddenhagen 			// ... and release the high voices, while the low ones are serviced.
66311ee59bdSOswald Buddenhagen 			outl(SOLEH << 16, emu->port + PTR);
66411ee59bdSOswald Buddenhagen 			outl(solh, emu->port + DATA);
66511ee59bdSOswald Buddenhagen 			// Finally we verify that nothing interfered in fact.
66611ee59bdSOswald Buddenhagen 			if (REG_VAL_GET(WC_SAMPLECOUNTER, inl(emu->port + WC)) ==
66711ee59bdSOswald Buddenhagen 			    ((REG_VAL_GET(WC_SAMPLECOUNTER, wc) + 1) & REG_MASK0(WC_SAMPLECOUNTER))) {
66811ee59bdSOswald Buddenhagen 				ret = 0;
66911ee59bdSOswald Buddenhagen 			} else {
67011ee59bdSOswald Buddenhagen 				ret = -EAGAIN;
67111ee59bdSOswald Buddenhagen 			}
67211ee59bdSOswald Buddenhagen 			break;
67311ee59bdSOswald Buddenhagen 		}
67411ee59bdSOswald Buddenhagen 		// Don't block for too long
67511ee59bdSOswald Buddenhagen 		spin_unlock_irqrestore(&emu->emu_lock, flags);
67611ee59bdSOswald Buddenhagen 		udelay(1);
67711ee59bdSOswald Buddenhagen 		spin_lock_irqsave(&emu->emu_lock, flags);
67811ee59bdSOswald Buddenhagen 	}
67911ee59bdSOswald Buddenhagen 
68011ee59bdSOswald Buddenhagen 	spin_unlock_irqrestore(&emu->emu_lock, flags);
68111ee59bdSOswald Buddenhagen 	return ret;
68211ee59bdSOswald Buddenhagen }
68311ee59bdSOswald Buddenhagen 
snd_emu10k1_wait(struct snd_emu10k1 * emu,unsigned int wait)684eb4698f3STakashi Iwai void snd_emu10k1_wait(struct snd_emu10k1 *emu, unsigned int wait)
6851da177e4SLinus Torvalds {
6861da177e4SLinus Torvalds 	volatile unsigned count;
6871da177e4SLinus Torvalds 	unsigned int newtime = 0, curtime;
6881da177e4SLinus Torvalds 
6891da177e4SLinus Torvalds 	curtime = inl(emu->port + WC) >> 6;
6901da177e4SLinus Torvalds 	while (wait-- > 0) {
6911da177e4SLinus Torvalds 		count = 0;
6921da177e4SLinus Torvalds 		while (count++ < 16384) {
6931da177e4SLinus Torvalds 			newtime = inl(emu->port + WC) >> 6;
6941da177e4SLinus Torvalds 			if (newtime != curtime)
6951da177e4SLinus Torvalds 				break;
6961da177e4SLinus Torvalds 		}
6975a47fa3dSRoel Kluin 		if (count > 16384)
6981da177e4SLinus Torvalds 			break;
6991da177e4SLinus Torvalds 		curtime = newtime;
7001da177e4SLinus Torvalds 	}
7011da177e4SLinus Torvalds }
7021da177e4SLinus Torvalds 
snd_emu10k1_ac97_read(struct snd_ac97 * ac97,unsigned short reg)703eb4698f3STakashi Iwai unsigned short snd_emu10k1_ac97_read(struct snd_ac97 *ac97, unsigned short reg)
7041da177e4SLinus Torvalds {
705eb4698f3STakashi Iwai 	struct snd_emu10k1 *emu = ac97->private_data;
7061da177e4SLinus Torvalds 	unsigned long flags;
7071da177e4SLinus Torvalds 	unsigned short val;
7081da177e4SLinus Torvalds 
7091da177e4SLinus Torvalds 	spin_lock_irqsave(&emu->emu_lock, flags);
7101da177e4SLinus Torvalds 	outb(reg, emu->port + AC97ADDRESS);
7111da177e4SLinus Torvalds 	val = inw(emu->port + AC97DATA);
7121da177e4SLinus Torvalds 	spin_unlock_irqrestore(&emu->emu_lock, flags);
7131da177e4SLinus Torvalds 	return val;
7141da177e4SLinus Torvalds }
7151da177e4SLinus Torvalds 
snd_emu10k1_ac97_write(struct snd_ac97 * ac97,unsigned short reg,unsigned short data)716eb4698f3STakashi Iwai void snd_emu10k1_ac97_write(struct snd_ac97 *ac97, unsigned short reg, unsigned short data)
7171da177e4SLinus Torvalds {
718eb4698f3STakashi Iwai 	struct snd_emu10k1 *emu = ac97->private_data;
7191da177e4SLinus Torvalds 	unsigned long flags;
7201da177e4SLinus Torvalds 
7211da177e4SLinus Torvalds 	spin_lock_irqsave(&emu->emu_lock, flags);
7221da177e4SLinus Torvalds 	outb(reg, emu->port + AC97ADDRESS);
7231da177e4SLinus Torvalds 	outw(data, emu->port + AC97DATA);
7241da177e4SLinus Torvalds 	spin_unlock_irqrestore(&emu->emu_lock, flags);
7251da177e4SLinus Torvalds }
726