162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *  Copyright (c) by Jaroslav Kysela <perex@perex.cz>
462306a36Sopenharmony_ci *                   Lee Revell <rlrevell@joe-job.com>
562306a36Sopenharmony_ci *                   James Courtier-Dutton <James@superbug.co.uk>
662306a36Sopenharmony_ci *                   Oswald Buddenhagen <oswald.buddenhagen@gmx.de>
762306a36Sopenharmony_ci *                   Creative Labs, Inc.
862306a36Sopenharmony_ci *
962306a36Sopenharmony_ci *  Routines for control of EMU10K1 chips
1062306a36Sopenharmony_ci */
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci#include <linux/time.h>
1362306a36Sopenharmony_ci#include <sound/core.h>
1462306a36Sopenharmony_ci#include <sound/emu10k1.h>
1562306a36Sopenharmony_ci#include <linux/delay.h>
1662306a36Sopenharmony_ci#include <linux/export.h>
1762306a36Sopenharmony_ci#include "p17v.h"
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_cistatic inline bool check_ptr_reg(struct snd_emu10k1 *emu, unsigned int reg)
2062306a36Sopenharmony_ci{
2162306a36Sopenharmony_ci	if (snd_BUG_ON(!emu))
2262306a36Sopenharmony_ci		return false;
2362306a36Sopenharmony_ci	if (snd_BUG_ON(reg & (emu->audigy ? (0xffff0000 & ~A_PTR_ADDRESS_MASK)
2462306a36Sopenharmony_ci					  : (0xffff0000 & ~PTR_ADDRESS_MASK))))
2562306a36Sopenharmony_ci		return false;
2662306a36Sopenharmony_ci	if (snd_BUG_ON(reg & 0x0000ffff & ~PTR_CHANNELNUM_MASK))
2762306a36Sopenharmony_ci		return false;
2862306a36Sopenharmony_ci	return true;
2962306a36Sopenharmony_ci}
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ciunsigned int snd_emu10k1_ptr_read(struct snd_emu10k1 * emu, unsigned int reg, unsigned int chn)
3262306a36Sopenharmony_ci{
3362306a36Sopenharmony_ci	unsigned long flags;
3462306a36Sopenharmony_ci	unsigned int regptr, val;
3562306a36Sopenharmony_ci	unsigned int mask;
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci	regptr = (reg << 16) | chn;
3862306a36Sopenharmony_ci	if (!check_ptr_reg(emu, regptr))
3962306a36Sopenharmony_ci		return 0;
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci	spin_lock_irqsave(&emu->emu_lock, flags);
4262306a36Sopenharmony_ci	outl(regptr, emu->port + PTR);
4362306a36Sopenharmony_ci	val = inl(emu->port + DATA);
4462306a36Sopenharmony_ci	spin_unlock_irqrestore(&emu->emu_lock, flags);
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci	if (reg & 0xff000000) {
4762306a36Sopenharmony_ci		unsigned char size, offset;
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci		size = (reg >> 24) & 0x3f;
5062306a36Sopenharmony_ci		offset = (reg >> 16) & 0x1f;
5162306a36Sopenharmony_ci		mask = (1 << size) - 1;
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci		return (val >> offset) & mask;
5462306a36Sopenharmony_ci	} else {
5562306a36Sopenharmony_ci		return val;
5662306a36Sopenharmony_ci	}
5762306a36Sopenharmony_ci}
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ciEXPORT_SYMBOL(snd_emu10k1_ptr_read);
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_civoid snd_emu10k1_ptr_write(struct snd_emu10k1 *emu, unsigned int reg, unsigned int chn, unsigned int data)
6262306a36Sopenharmony_ci{
6362306a36Sopenharmony_ci	unsigned int regptr;
6462306a36Sopenharmony_ci	unsigned long flags;
6562306a36Sopenharmony_ci	unsigned int mask;
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci	regptr = (reg << 16) | chn;
6862306a36Sopenharmony_ci	if (!check_ptr_reg(emu, regptr))
6962306a36Sopenharmony_ci		return;
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci	if (reg & 0xff000000) {
7262306a36Sopenharmony_ci		unsigned char size, offset;
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci		size = (reg >> 24) & 0x3f;
7562306a36Sopenharmony_ci		offset = (reg >> 16) & 0x1f;
7662306a36Sopenharmony_ci		mask = (1 << size) - 1;
7762306a36Sopenharmony_ci		if (snd_BUG_ON(data & ~mask))
7862306a36Sopenharmony_ci			return;
7962306a36Sopenharmony_ci		mask <<= offset;
8062306a36Sopenharmony_ci		data <<= offset;
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci		spin_lock_irqsave(&emu->emu_lock, flags);
8362306a36Sopenharmony_ci		outl(regptr, emu->port + PTR);
8462306a36Sopenharmony_ci		data |= inl(emu->port + DATA) & ~mask;
8562306a36Sopenharmony_ci	} else {
8662306a36Sopenharmony_ci		spin_lock_irqsave(&emu->emu_lock, flags);
8762306a36Sopenharmony_ci		outl(regptr, emu->port + PTR);
8862306a36Sopenharmony_ci	}
8962306a36Sopenharmony_ci	outl(data, emu->port + DATA);
9062306a36Sopenharmony_ci	spin_unlock_irqrestore(&emu->emu_lock, flags);
9162306a36Sopenharmony_ci}
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ciEXPORT_SYMBOL(snd_emu10k1_ptr_write);
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_civoid snd_emu10k1_ptr_write_multiple(struct snd_emu10k1 *emu, unsigned int chn, ...)
9662306a36Sopenharmony_ci{
9762306a36Sopenharmony_ci	va_list va;
9862306a36Sopenharmony_ci	u32 addr_mask;
9962306a36Sopenharmony_ci	unsigned long flags;
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci	if (snd_BUG_ON(!emu))
10262306a36Sopenharmony_ci		return;
10362306a36Sopenharmony_ci	if (snd_BUG_ON(chn & ~PTR_CHANNELNUM_MASK))
10462306a36Sopenharmony_ci		return;
10562306a36Sopenharmony_ci	addr_mask = ~((emu->audigy ? A_PTR_ADDRESS_MASK : PTR_ADDRESS_MASK) >> 16);
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci	va_start(va, chn);
10862306a36Sopenharmony_ci	spin_lock_irqsave(&emu->emu_lock, flags);
10962306a36Sopenharmony_ci	for (;;) {
11062306a36Sopenharmony_ci		u32 data;
11162306a36Sopenharmony_ci		u32 reg = va_arg(va, u32);
11262306a36Sopenharmony_ci		if (reg == REGLIST_END)
11362306a36Sopenharmony_ci			break;
11462306a36Sopenharmony_ci		data = va_arg(va, u32);
11562306a36Sopenharmony_ci		if (snd_BUG_ON(reg & addr_mask))  // Only raw registers supported here
11662306a36Sopenharmony_ci			continue;
11762306a36Sopenharmony_ci		outl((reg << 16) | chn, emu->port + PTR);
11862306a36Sopenharmony_ci		outl(data, emu->port + DATA);
11962306a36Sopenharmony_ci	}
12062306a36Sopenharmony_ci	spin_unlock_irqrestore(&emu->emu_lock, flags);
12162306a36Sopenharmony_ci	va_end(va);
12262306a36Sopenharmony_ci}
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ciEXPORT_SYMBOL(snd_emu10k1_ptr_write_multiple);
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ciunsigned int snd_emu10k1_ptr20_read(struct snd_emu10k1 * emu,
12762306a36Sopenharmony_ci					  unsigned int reg,
12862306a36Sopenharmony_ci					  unsigned int chn)
12962306a36Sopenharmony_ci{
13062306a36Sopenharmony_ci	unsigned long flags;
13162306a36Sopenharmony_ci	unsigned int regptr, val;
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	regptr = (reg << 16) | chn;
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci	spin_lock_irqsave(&emu->emu_lock, flags);
13662306a36Sopenharmony_ci	outl(regptr, emu->port + PTR2);
13762306a36Sopenharmony_ci	val = inl(emu->port + DATA2);
13862306a36Sopenharmony_ci	spin_unlock_irqrestore(&emu->emu_lock, flags);
13962306a36Sopenharmony_ci	return val;
14062306a36Sopenharmony_ci}
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_civoid snd_emu10k1_ptr20_write(struct snd_emu10k1 *emu,
14362306a36Sopenharmony_ci				   unsigned int reg,
14462306a36Sopenharmony_ci				   unsigned int chn,
14562306a36Sopenharmony_ci				   unsigned int data)
14662306a36Sopenharmony_ci{
14762306a36Sopenharmony_ci	unsigned int regptr;
14862306a36Sopenharmony_ci	unsigned long flags;
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci	regptr = (reg << 16) | chn;
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci	spin_lock_irqsave(&emu->emu_lock, flags);
15362306a36Sopenharmony_ci	outl(regptr, emu->port + PTR2);
15462306a36Sopenharmony_ci	outl(data, emu->port + DATA2);
15562306a36Sopenharmony_ci	spin_unlock_irqrestore(&emu->emu_lock, flags);
15662306a36Sopenharmony_ci}
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ciint snd_emu10k1_spi_write(struct snd_emu10k1 * emu,
15962306a36Sopenharmony_ci				   unsigned int data)
16062306a36Sopenharmony_ci{
16162306a36Sopenharmony_ci	unsigned int reset, set;
16262306a36Sopenharmony_ci	unsigned int reg, tmp;
16362306a36Sopenharmony_ci	int n, result;
16462306a36Sopenharmony_ci	int err = 0;
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci	/* This function is not re-entrant, so protect against it. */
16762306a36Sopenharmony_ci	spin_lock(&emu->spi_lock);
16862306a36Sopenharmony_ci	if (emu->card_capabilities->ca0108_chip)
16962306a36Sopenharmony_ci		reg = P17V_SPI;
17062306a36Sopenharmony_ci	else {
17162306a36Sopenharmony_ci		/* For other chip types the SPI register
17262306a36Sopenharmony_ci		 * is currently unknown. */
17362306a36Sopenharmony_ci		err = 1;
17462306a36Sopenharmony_ci		goto spi_write_exit;
17562306a36Sopenharmony_ci	}
17662306a36Sopenharmony_ci	if (data > 0xffff) {
17762306a36Sopenharmony_ci		/* Only 16bit values allowed */
17862306a36Sopenharmony_ci		err = 1;
17962306a36Sopenharmony_ci		goto spi_write_exit;
18062306a36Sopenharmony_ci	}
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci	tmp = snd_emu10k1_ptr20_read(emu, reg, 0);
18362306a36Sopenharmony_ci	reset = (tmp & ~0x3ffff) | 0x20000; /* Set xxx20000 */
18462306a36Sopenharmony_ci	set = reset | 0x10000; /* Set xxx1xxxx */
18562306a36Sopenharmony_ci	snd_emu10k1_ptr20_write(emu, reg, 0, reset | data);
18662306a36Sopenharmony_ci	tmp = snd_emu10k1_ptr20_read(emu, reg, 0); /* write post */
18762306a36Sopenharmony_ci	snd_emu10k1_ptr20_write(emu, reg, 0, set | data);
18862306a36Sopenharmony_ci	result = 1;
18962306a36Sopenharmony_ci	/* Wait for status bit to return to 0 */
19062306a36Sopenharmony_ci	for (n = 0; n < 100; n++) {
19162306a36Sopenharmony_ci		udelay(10);
19262306a36Sopenharmony_ci		tmp = snd_emu10k1_ptr20_read(emu, reg, 0);
19362306a36Sopenharmony_ci		if (!(tmp & 0x10000)) {
19462306a36Sopenharmony_ci			result = 0;
19562306a36Sopenharmony_ci			break;
19662306a36Sopenharmony_ci		}
19762306a36Sopenharmony_ci	}
19862306a36Sopenharmony_ci	if (result) {
19962306a36Sopenharmony_ci		/* Timed out */
20062306a36Sopenharmony_ci		err = 1;
20162306a36Sopenharmony_ci		goto spi_write_exit;
20262306a36Sopenharmony_ci	}
20362306a36Sopenharmony_ci	snd_emu10k1_ptr20_write(emu, reg, 0, reset | data);
20462306a36Sopenharmony_ci	tmp = snd_emu10k1_ptr20_read(emu, reg, 0); /* Write post */
20562306a36Sopenharmony_ci	err = 0;
20662306a36Sopenharmony_cispi_write_exit:
20762306a36Sopenharmony_ci	spin_unlock(&emu->spi_lock);
20862306a36Sopenharmony_ci	return err;
20962306a36Sopenharmony_ci}
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci/* The ADC does not support i2c read, so only write is implemented */
21262306a36Sopenharmony_ciint snd_emu10k1_i2c_write(struct snd_emu10k1 *emu,
21362306a36Sopenharmony_ci				u32 reg,
21462306a36Sopenharmony_ci				u32 value)
21562306a36Sopenharmony_ci{
21662306a36Sopenharmony_ci	u32 tmp;
21762306a36Sopenharmony_ci	int timeout = 0;
21862306a36Sopenharmony_ci	int status;
21962306a36Sopenharmony_ci	int retry;
22062306a36Sopenharmony_ci	int err = 0;
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci	if ((reg > 0x7f) || (value > 0x1ff)) {
22362306a36Sopenharmony_ci		dev_err(emu->card->dev, "i2c_write: invalid values.\n");
22462306a36Sopenharmony_ci		return -EINVAL;
22562306a36Sopenharmony_ci	}
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_ci	/* This function is not re-entrant, so protect against it. */
22862306a36Sopenharmony_ci	spin_lock(&emu->i2c_lock);
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci	tmp = reg << 25 | value << 16;
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci	/* This controls the I2C connected to the WM8775 ADC Codec */
23362306a36Sopenharmony_ci	snd_emu10k1_ptr20_write(emu, P17V_I2C_1, 0, tmp);
23462306a36Sopenharmony_ci	tmp = snd_emu10k1_ptr20_read(emu, P17V_I2C_1, 0); /* write post */
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_ci	for (retry = 0; retry < 10; retry++) {
23762306a36Sopenharmony_ci		/* Send the data to i2c */
23862306a36Sopenharmony_ci		tmp = 0;
23962306a36Sopenharmony_ci		tmp = tmp | (I2C_A_ADC_LAST|I2C_A_ADC_START|I2C_A_ADC_ADD);
24062306a36Sopenharmony_ci		snd_emu10k1_ptr20_write(emu, P17V_I2C_ADDR, 0, tmp);
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci		/* Wait till the transaction ends */
24362306a36Sopenharmony_ci		while (1) {
24462306a36Sopenharmony_ci			mdelay(1);
24562306a36Sopenharmony_ci			status = snd_emu10k1_ptr20_read(emu, P17V_I2C_ADDR, 0);
24662306a36Sopenharmony_ci			timeout++;
24762306a36Sopenharmony_ci			if ((status & I2C_A_ADC_START) == 0)
24862306a36Sopenharmony_ci				break;
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ci			if (timeout > 1000) {
25162306a36Sopenharmony_ci				dev_warn(emu->card->dev,
25262306a36Sopenharmony_ci					   "emu10k1:I2C:timeout status=0x%x\n",
25362306a36Sopenharmony_ci					   status);
25462306a36Sopenharmony_ci				break;
25562306a36Sopenharmony_ci			}
25662306a36Sopenharmony_ci		}
25762306a36Sopenharmony_ci		//Read back and see if the transaction is successful
25862306a36Sopenharmony_ci		if ((status & I2C_A_ADC_ABORT) == 0)
25962306a36Sopenharmony_ci			break;
26062306a36Sopenharmony_ci	}
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ci	if (retry == 10) {
26362306a36Sopenharmony_ci		dev_err(emu->card->dev, "Writing to ADC failed!\n");
26462306a36Sopenharmony_ci		dev_err(emu->card->dev, "status=0x%x, reg=%d, value=%d\n",
26562306a36Sopenharmony_ci			status, reg, value);
26662306a36Sopenharmony_ci		/* dump_stack(); */
26762306a36Sopenharmony_ci		err = -EINVAL;
26862306a36Sopenharmony_ci	}
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci	spin_unlock(&emu->i2c_lock);
27162306a36Sopenharmony_ci	return err;
27262306a36Sopenharmony_ci}
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_cistatic void snd_emu1010_fpga_write_locked(struct snd_emu10k1 *emu, u32 reg, u32 value)
27562306a36Sopenharmony_ci{
27662306a36Sopenharmony_ci	if (snd_BUG_ON(reg > 0x3f))
27762306a36Sopenharmony_ci		return;
27862306a36Sopenharmony_ci	reg += 0x40; /* 0x40 upwards are registers. */
27962306a36Sopenharmony_ci	if (snd_BUG_ON(value > 0x3f)) /* 0 to 0x3f are values */
28062306a36Sopenharmony_ci		return;
28162306a36Sopenharmony_ci	outw(reg, emu->port + A_GPIO);
28262306a36Sopenharmony_ci	udelay(10);
28362306a36Sopenharmony_ci	outw(reg | 0x80, emu->port + A_GPIO);  /* High bit clocks the value into the fpga. */
28462306a36Sopenharmony_ci	udelay(10);
28562306a36Sopenharmony_ci	outw(value, emu->port + A_GPIO);
28662306a36Sopenharmony_ci	udelay(10);
28762306a36Sopenharmony_ci	outw(value | 0x80 , emu->port + A_GPIO);  /* High bit clocks the value into the fpga. */
28862306a36Sopenharmony_ci}
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_civoid snd_emu1010_fpga_write(struct snd_emu10k1 *emu, u32 reg, u32 value)
29162306a36Sopenharmony_ci{
29262306a36Sopenharmony_ci	unsigned long flags;
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_ci	spin_lock_irqsave(&emu->emu_lock, flags);
29562306a36Sopenharmony_ci	snd_emu1010_fpga_write_locked(emu, reg, value);
29662306a36Sopenharmony_ci	spin_unlock_irqrestore(&emu->emu_lock, flags);
29762306a36Sopenharmony_ci}
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_cistatic void snd_emu1010_fpga_read_locked(struct snd_emu10k1 *emu, u32 reg, u32 *value)
30062306a36Sopenharmony_ci{
30162306a36Sopenharmony_ci	// The higest input pin is used as the designated interrupt trigger,
30262306a36Sopenharmony_ci	// so it needs to be masked out.
30362306a36Sopenharmony_ci	// But note that any other input pin change will also cause an IRQ,
30462306a36Sopenharmony_ci	// so using this function often causes an IRQ as a side effect.
30562306a36Sopenharmony_ci	u32 mask = emu->card_capabilities->ca0108_chip ? 0x1f : 0x7f;
30662306a36Sopenharmony_ci	if (snd_BUG_ON(reg > 0x3f))
30762306a36Sopenharmony_ci		return;
30862306a36Sopenharmony_ci	reg += 0x40; /* 0x40 upwards are registers. */
30962306a36Sopenharmony_ci	outw(reg, emu->port + A_GPIO);
31062306a36Sopenharmony_ci	udelay(10);
31162306a36Sopenharmony_ci	outw(reg | 0x80, emu->port + A_GPIO);  /* High bit clocks the value into the fpga. */
31262306a36Sopenharmony_ci	udelay(10);
31362306a36Sopenharmony_ci	*value = ((inw(emu->port + A_GPIO) >> 8) & mask);
31462306a36Sopenharmony_ci}
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_civoid snd_emu1010_fpga_read(struct snd_emu10k1 *emu, u32 reg, u32 *value)
31762306a36Sopenharmony_ci{
31862306a36Sopenharmony_ci	unsigned long flags;
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_ci	spin_lock_irqsave(&emu->emu_lock, flags);
32162306a36Sopenharmony_ci	snd_emu1010_fpga_read_locked(emu, reg, value);
32262306a36Sopenharmony_ci	spin_unlock_irqrestore(&emu->emu_lock, flags);
32362306a36Sopenharmony_ci}
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_ci/* Each Destination has one and only one Source,
32662306a36Sopenharmony_ci * but one Source can feed any number of Destinations simultaneously.
32762306a36Sopenharmony_ci */
32862306a36Sopenharmony_civoid snd_emu1010_fpga_link_dst_src_write(struct snd_emu10k1 *emu, u32 dst, u32 src)
32962306a36Sopenharmony_ci{
33062306a36Sopenharmony_ci	unsigned long flags;
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_ci	if (snd_BUG_ON(dst & ~0x71f))
33362306a36Sopenharmony_ci		return;
33462306a36Sopenharmony_ci	if (snd_BUG_ON(src & ~0x71f))
33562306a36Sopenharmony_ci		return;
33662306a36Sopenharmony_ci	spin_lock_irqsave(&emu->emu_lock, flags);
33762306a36Sopenharmony_ci	snd_emu1010_fpga_write_locked(emu, EMU_HANA_DESTHI, dst >> 8);
33862306a36Sopenharmony_ci	snd_emu1010_fpga_write_locked(emu, EMU_HANA_DESTLO, dst & 0x1f);
33962306a36Sopenharmony_ci	snd_emu1010_fpga_write_locked(emu, EMU_HANA_SRCHI, src >> 8);
34062306a36Sopenharmony_ci	snd_emu1010_fpga_write_locked(emu, EMU_HANA_SRCLO, src & 0x1f);
34162306a36Sopenharmony_ci	spin_unlock_irqrestore(&emu->emu_lock, flags);
34262306a36Sopenharmony_ci}
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_ciu32 snd_emu1010_fpga_link_dst_src_read(struct snd_emu10k1 *emu, u32 dst)
34562306a36Sopenharmony_ci{
34662306a36Sopenharmony_ci	unsigned long flags;
34762306a36Sopenharmony_ci	u32 hi, lo;
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_ci	if (snd_BUG_ON(dst & ~0x71f))
35062306a36Sopenharmony_ci		return 0;
35162306a36Sopenharmony_ci	spin_lock_irqsave(&emu->emu_lock, flags);
35262306a36Sopenharmony_ci	snd_emu1010_fpga_write_locked(emu, EMU_HANA_DESTHI, dst >> 8);
35362306a36Sopenharmony_ci	snd_emu1010_fpga_write_locked(emu, EMU_HANA_DESTLO, dst & 0x1f);
35462306a36Sopenharmony_ci	snd_emu1010_fpga_read_locked(emu, EMU_HANA_SRCHI, &hi);
35562306a36Sopenharmony_ci	snd_emu1010_fpga_read_locked(emu, EMU_HANA_SRCLO, &lo);
35662306a36Sopenharmony_ci	spin_unlock_irqrestore(&emu->emu_lock, flags);
35762306a36Sopenharmony_ci	return (hi << 8) | lo;
35862306a36Sopenharmony_ci}
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_ciint snd_emu1010_get_raw_rate(struct snd_emu10k1 *emu, u8 src)
36162306a36Sopenharmony_ci{
36262306a36Sopenharmony_ci	u32 reg_lo, reg_hi, value, value2;
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_ci	switch (src) {
36562306a36Sopenharmony_ci	case EMU_HANA_WCLOCK_HANA_SPDIF_IN:
36662306a36Sopenharmony_ci		snd_emu1010_fpga_read(emu, EMU_HANA_SPDIF_MODE, &value);
36762306a36Sopenharmony_ci		if (value & EMU_HANA_SPDIF_MODE_RX_INVALID)
36862306a36Sopenharmony_ci			return 0;
36962306a36Sopenharmony_ci		reg_lo = EMU_HANA_WC_SPDIF_LO;
37062306a36Sopenharmony_ci		reg_hi = EMU_HANA_WC_SPDIF_HI;
37162306a36Sopenharmony_ci		break;
37262306a36Sopenharmony_ci	case EMU_HANA_WCLOCK_HANA_ADAT_IN:
37362306a36Sopenharmony_ci		reg_lo = EMU_HANA_WC_ADAT_LO;
37462306a36Sopenharmony_ci		reg_hi = EMU_HANA_WC_ADAT_HI;
37562306a36Sopenharmony_ci		break;
37662306a36Sopenharmony_ci	case EMU_HANA_WCLOCK_SYNC_BNC:
37762306a36Sopenharmony_ci		reg_lo = EMU_HANA_WC_BNC_LO;
37862306a36Sopenharmony_ci		reg_hi = EMU_HANA_WC_BNC_HI;
37962306a36Sopenharmony_ci		break;
38062306a36Sopenharmony_ci	case EMU_HANA_WCLOCK_2ND_HANA:
38162306a36Sopenharmony_ci		reg_lo = EMU_HANA2_WC_SPDIF_LO;
38262306a36Sopenharmony_ci		reg_hi = EMU_HANA2_WC_SPDIF_HI;
38362306a36Sopenharmony_ci		break;
38462306a36Sopenharmony_ci	default:
38562306a36Sopenharmony_ci		return 0;
38662306a36Sopenharmony_ci	}
38762306a36Sopenharmony_ci	snd_emu1010_fpga_read(emu, reg_hi, &value);
38862306a36Sopenharmony_ci	snd_emu1010_fpga_read(emu, reg_lo, &value2);
38962306a36Sopenharmony_ci	// FIXME: The /4 is valid for 0404b, but contradicts all other info.
39062306a36Sopenharmony_ci	return 0x1770000 / 4 / (((value << 5) | value2) + 1);
39162306a36Sopenharmony_ci}
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_civoid snd_emu1010_update_clock(struct snd_emu10k1 *emu)
39462306a36Sopenharmony_ci{
39562306a36Sopenharmony_ci	int clock;
39662306a36Sopenharmony_ci	u32 leds;
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_ci	switch (emu->emu1010.wclock) {
39962306a36Sopenharmony_ci	case EMU_HANA_WCLOCK_INT_44_1K | EMU_HANA_WCLOCK_1X:
40062306a36Sopenharmony_ci		clock = 44100;
40162306a36Sopenharmony_ci		leds = EMU_HANA_DOCK_LEDS_2_44K;
40262306a36Sopenharmony_ci		break;
40362306a36Sopenharmony_ci	case EMU_HANA_WCLOCK_INT_48K | EMU_HANA_WCLOCK_1X:
40462306a36Sopenharmony_ci		clock = 48000;
40562306a36Sopenharmony_ci		leds = EMU_HANA_DOCK_LEDS_2_48K;
40662306a36Sopenharmony_ci		break;
40762306a36Sopenharmony_ci	default:
40862306a36Sopenharmony_ci		clock = snd_emu1010_get_raw_rate(
40962306a36Sopenharmony_ci				emu, emu->emu1010.wclock & EMU_HANA_WCLOCK_SRC_MASK);
41062306a36Sopenharmony_ci		// The raw rate reading is rather coarse (it cannot accurately
41162306a36Sopenharmony_ci		// represent 44.1 kHz) and fluctuates slightly. Luckily, the
41262306a36Sopenharmony_ci		// clock comes from digital inputs, which use standardized rates.
41362306a36Sopenharmony_ci		// So we round to the closest standard rate and ignore discrepancies.
41462306a36Sopenharmony_ci		if (clock < 46000) {
41562306a36Sopenharmony_ci			clock = 44100;
41662306a36Sopenharmony_ci			leds = EMU_HANA_DOCK_LEDS_2_EXT | EMU_HANA_DOCK_LEDS_2_44K;
41762306a36Sopenharmony_ci		} else {
41862306a36Sopenharmony_ci			clock = 48000;
41962306a36Sopenharmony_ci			leds = EMU_HANA_DOCK_LEDS_2_EXT | EMU_HANA_DOCK_LEDS_2_48K;
42062306a36Sopenharmony_ci		}
42162306a36Sopenharmony_ci		break;
42262306a36Sopenharmony_ci	}
42362306a36Sopenharmony_ci	emu->emu1010.word_clock = clock;
42462306a36Sopenharmony_ci
42562306a36Sopenharmony_ci	// FIXME: this should probably represent the AND of all currently
42662306a36Sopenharmony_ci	// used sources' lock status. But we don't know how to get that ...
42762306a36Sopenharmony_ci	leds |= EMU_HANA_DOCK_LEDS_2_LOCK;
42862306a36Sopenharmony_ci
42962306a36Sopenharmony_ci	snd_emu1010_fpga_write(emu, EMU_HANA_DOCK_LEDS_2, leds);
43062306a36Sopenharmony_ci}
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_civoid snd_emu10k1_intr_enable(struct snd_emu10k1 *emu, unsigned int intrenb)
43362306a36Sopenharmony_ci{
43462306a36Sopenharmony_ci	unsigned long flags;
43562306a36Sopenharmony_ci	unsigned int enable;
43662306a36Sopenharmony_ci
43762306a36Sopenharmony_ci	spin_lock_irqsave(&emu->emu_lock, flags);
43862306a36Sopenharmony_ci	enable = inl(emu->port + INTE) | intrenb;
43962306a36Sopenharmony_ci	outl(enable, emu->port + INTE);
44062306a36Sopenharmony_ci	spin_unlock_irqrestore(&emu->emu_lock, flags);
44162306a36Sopenharmony_ci}
44262306a36Sopenharmony_ci
44362306a36Sopenharmony_civoid snd_emu10k1_intr_disable(struct snd_emu10k1 *emu, unsigned int intrenb)
44462306a36Sopenharmony_ci{
44562306a36Sopenharmony_ci	unsigned long flags;
44662306a36Sopenharmony_ci	unsigned int enable;
44762306a36Sopenharmony_ci
44862306a36Sopenharmony_ci	spin_lock_irqsave(&emu->emu_lock, flags);
44962306a36Sopenharmony_ci	enable = inl(emu->port + INTE) & ~intrenb;
45062306a36Sopenharmony_ci	outl(enable, emu->port + INTE);
45162306a36Sopenharmony_ci	spin_unlock_irqrestore(&emu->emu_lock, flags);
45262306a36Sopenharmony_ci}
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_civoid snd_emu10k1_voice_intr_enable(struct snd_emu10k1 *emu, unsigned int voicenum)
45562306a36Sopenharmony_ci{
45662306a36Sopenharmony_ci	unsigned long flags;
45762306a36Sopenharmony_ci	unsigned int val;
45862306a36Sopenharmony_ci
45962306a36Sopenharmony_ci	spin_lock_irqsave(&emu->emu_lock, flags);
46062306a36Sopenharmony_ci	if (voicenum >= 32) {
46162306a36Sopenharmony_ci		outl(CLIEH << 16, emu->port + PTR);
46262306a36Sopenharmony_ci		val = inl(emu->port + DATA);
46362306a36Sopenharmony_ci		val |= 1 << (voicenum - 32);
46462306a36Sopenharmony_ci	} else {
46562306a36Sopenharmony_ci		outl(CLIEL << 16, emu->port + PTR);
46662306a36Sopenharmony_ci		val = inl(emu->port + DATA);
46762306a36Sopenharmony_ci		val |= 1 << voicenum;
46862306a36Sopenharmony_ci	}
46962306a36Sopenharmony_ci	outl(val, emu->port + DATA);
47062306a36Sopenharmony_ci	spin_unlock_irqrestore(&emu->emu_lock, flags);
47162306a36Sopenharmony_ci}
47262306a36Sopenharmony_ci
47362306a36Sopenharmony_civoid snd_emu10k1_voice_intr_disable(struct snd_emu10k1 *emu, unsigned int voicenum)
47462306a36Sopenharmony_ci{
47562306a36Sopenharmony_ci	unsigned long flags;
47662306a36Sopenharmony_ci	unsigned int val;
47762306a36Sopenharmony_ci
47862306a36Sopenharmony_ci	spin_lock_irqsave(&emu->emu_lock, flags);
47962306a36Sopenharmony_ci	if (voicenum >= 32) {
48062306a36Sopenharmony_ci		outl(CLIEH << 16, emu->port + PTR);
48162306a36Sopenharmony_ci		val = inl(emu->port + DATA);
48262306a36Sopenharmony_ci		val &= ~(1 << (voicenum - 32));
48362306a36Sopenharmony_ci	} else {
48462306a36Sopenharmony_ci		outl(CLIEL << 16, emu->port + PTR);
48562306a36Sopenharmony_ci		val = inl(emu->port + DATA);
48662306a36Sopenharmony_ci		val &= ~(1 << voicenum);
48762306a36Sopenharmony_ci	}
48862306a36Sopenharmony_ci	outl(val, emu->port + DATA);
48962306a36Sopenharmony_ci	spin_unlock_irqrestore(&emu->emu_lock, flags);
49062306a36Sopenharmony_ci}
49162306a36Sopenharmony_ci
49262306a36Sopenharmony_civoid snd_emu10k1_voice_intr_ack(struct snd_emu10k1 *emu, unsigned int voicenum)
49362306a36Sopenharmony_ci{
49462306a36Sopenharmony_ci	unsigned long flags;
49562306a36Sopenharmony_ci
49662306a36Sopenharmony_ci	spin_lock_irqsave(&emu->emu_lock, flags);
49762306a36Sopenharmony_ci	if (voicenum >= 32) {
49862306a36Sopenharmony_ci		outl(CLIPH << 16, emu->port + PTR);
49962306a36Sopenharmony_ci		voicenum = 1 << (voicenum - 32);
50062306a36Sopenharmony_ci	} else {
50162306a36Sopenharmony_ci		outl(CLIPL << 16, emu->port + PTR);
50262306a36Sopenharmony_ci		voicenum = 1 << voicenum;
50362306a36Sopenharmony_ci	}
50462306a36Sopenharmony_ci	outl(voicenum, emu->port + DATA);
50562306a36Sopenharmony_ci	spin_unlock_irqrestore(&emu->emu_lock, flags);
50662306a36Sopenharmony_ci}
50762306a36Sopenharmony_ci
50862306a36Sopenharmony_civoid snd_emu10k1_voice_half_loop_intr_enable(struct snd_emu10k1 *emu, unsigned int voicenum)
50962306a36Sopenharmony_ci{
51062306a36Sopenharmony_ci	unsigned long flags;
51162306a36Sopenharmony_ci	unsigned int val;
51262306a36Sopenharmony_ci
51362306a36Sopenharmony_ci	spin_lock_irqsave(&emu->emu_lock, flags);
51462306a36Sopenharmony_ci	if (voicenum >= 32) {
51562306a36Sopenharmony_ci		outl(HLIEH << 16, emu->port + PTR);
51662306a36Sopenharmony_ci		val = inl(emu->port + DATA);
51762306a36Sopenharmony_ci		val |= 1 << (voicenum - 32);
51862306a36Sopenharmony_ci	} else {
51962306a36Sopenharmony_ci		outl(HLIEL << 16, emu->port + PTR);
52062306a36Sopenharmony_ci		val = inl(emu->port + DATA);
52162306a36Sopenharmony_ci		val |= 1 << voicenum;
52262306a36Sopenharmony_ci	}
52362306a36Sopenharmony_ci	outl(val, emu->port + DATA);
52462306a36Sopenharmony_ci	spin_unlock_irqrestore(&emu->emu_lock, flags);
52562306a36Sopenharmony_ci}
52662306a36Sopenharmony_ci
52762306a36Sopenharmony_civoid snd_emu10k1_voice_half_loop_intr_disable(struct snd_emu10k1 *emu, unsigned int voicenum)
52862306a36Sopenharmony_ci{
52962306a36Sopenharmony_ci	unsigned long flags;
53062306a36Sopenharmony_ci	unsigned int val;
53162306a36Sopenharmony_ci
53262306a36Sopenharmony_ci	spin_lock_irqsave(&emu->emu_lock, flags);
53362306a36Sopenharmony_ci	if (voicenum >= 32) {
53462306a36Sopenharmony_ci		outl(HLIEH << 16, emu->port + PTR);
53562306a36Sopenharmony_ci		val = inl(emu->port + DATA);
53662306a36Sopenharmony_ci		val &= ~(1 << (voicenum - 32));
53762306a36Sopenharmony_ci	} else {
53862306a36Sopenharmony_ci		outl(HLIEL << 16, emu->port + PTR);
53962306a36Sopenharmony_ci		val = inl(emu->port + DATA);
54062306a36Sopenharmony_ci		val &= ~(1 << voicenum);
54162306a36Sopenharmony_ci	}
54262306a36Sopenharmony_ci	outl(val, emu->port + DATA);
54362306a36Sopenharmony_ci	spin_unlock_irqrestore(&emu->emu_lock, flags);
54462306a36Sopenharmony_ci}
54562306a36Sopenharmony_ci
54662306a36Sopenharmony_civoid snd_emu10k1_voice_half_loop_intr_ack(struct snd_emu10k1 *emu, unsigned int voicenum)
54762306a36Sopenharmony_ci{
54862306a36Sopenharmony_ci	unsigned long flags;
54962306a36Sopenharmony_ci
55062306a36Sopenharmony_ci	spin_lock_irqsave(&emu->emu_lock, flags);
55162306a36Sopenharmony_ci	if (voicenum >= 32) {
55262306a36Sopenharmony_ci		outl(HLIPH << 16, emu->port + PTR);
55362306a36Sopenharmony_ci		voicenum = 1 << (voicenum - 32);
55462306a36Sopenharmony_ci	} else {
55562306a36Sopenharmony_ci		outl(HLIPL << 16, emu->port + PTR);
55662306a36Sopenharmony_ci		voicenum = 1 << voicenum;
55762306a36Sopenharmony_ci	}
55862306a36Sopenharmony_ci	outl(voicenum, emu->port + DATA);
55962306a36Sopenharmony_ci	spin_unlock_irqrestore(&emu->emu_lock, flags);
56062306a36Sopenharmony_ci}
56162306a36Sopenharmony_ci
56262306a36Sopenharmony_ci#if 0
56362306a36Sopenharmony_civoid snd_emu10k1_voice_set_loop_stop(struct snd_emu10k1 *emu, unsigned int voicenum)
56462306a36Sopenharmony_ci{
56562306a36Sopenharmony_ci	unsigned long flags;
56662306a36Sopenharmony_ci	unsigned int sol;
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_ci	spin_lock_irqsave(&emu->emu_lock, flags);
56962306a36Sopenharmony_ci	if (voicenum >= 32) {
57062306a36Sopenharmony_ci		outl(SOLEH << 16, emu->port + PTR);
57162306a36Sopenharmony_ci		sol = inl(emu->port + DATA);
57262306a36Sopenharmony_ci		sol |= 1 << (voicenum - 32);
57362306a36Sopenharmony_ci	} else {
57462306a36Sopenharmony_ci		outl(SOLEL << 16, emu->port + PTR);
57562306a36Sopenharmony_ci		sol = inl(emu->port + DATA);
57662306a36Sopenharmony_ci		sol |= 1 << voicenum;
57762306a36Sopenharmony_ci	}
57862306a36Sopenharmony_ci	outl(sol, emu->port + DATA);
57962306a36Sopenharmony_ci	spin_unlock_irqrestore(&emu->emu_lock, flags);
58062306a36Sopenharmony_ci}
58162306a36Sopenharmony_ci
58262306a36Sopenharmony_civoid snd_emu10k1_voice_clear_loop_stop(struct snd_emu10k1 *emu, unsigned int voicenum)
58362306a36Sopenharmony_ci{
58462306a36Sopenharmony_ci	unsigned long flags;
58562306a36Sopenharmony_ci	unsigned int sol;
58662306a36Sopenharmony_ci
58762306a36Sopenharmony_ci	spin_lock_irqsave(&emu->emu_lock, flags);
58862306a36Sopenharmony_ci	if (voicenum >= 32) {
58962306a36Sopenharmony_ci		outl(SOLEH << 16, emu->port + PTR);
59062306a36Sopenharmony_ci		sol = inl(emu->port + DATA);
59162306a36Sopenharmony_ci		sol &= ~(1 << (voicenum - 32));
59262306a36Sopenharmony_ci	} else {
59362306a36Sopenharmony_ci		outl(SOLEL << 16, emu->port + PTR);
59462306a36Sopenharmony_ci		sol = inl(emu->port + DATA);
59562306a36Sopenharmony_ci		sol &= ~(1 << voicenum);
59662306a36Sopenharmony_ci	}
59762306a36Sopenharmony_ci	outl(sol, emu->port + DATA);
59862306a36Sopenharmony_ci	spin_unlock_irqrestore(&emu->emu_lock, flags);
59962306a36Sopenharmony_ci}
60062306a36Sopenharmony_ci#endif
60162306a36Sopenharmony_ci
60262306a36Sopenharmony_civoid snd_emu10k1_voice_set_loop_stop_multiple(struct snd_emu10k1 *emu, u64 voices)
60362306a36Sopenharmony_ci{
60462306a36Sopenharmony_ci	unsigned long flags;
60562306a36Sopenharmony_ci
60662306a36Sopenharmony_ci	spin_lock_irqsave(&emu->emu_lock, flags);
60762306a36Sopenharmony_ci	outl(SOLEL << 16, emu->port + PTR);
60862306a36Sopenharmony_ci	outl(inl(emu->port + DATA) | (u32)voices, emu->port + DATA);
60962306a36Sopenharmony_ci	outl(SOLEH << 16, emu->port + PTR);
61062306a36Sopenharmony_ci	outl(inl(emu->port + DATA) | (u32)(voices >> 32), emu->port + DATA);
61162306a36Sopenharmony_ci	spin_unlock_irqrestore(&emu->emu_lock, flags);
61262306a36Sopenharmony_ci}
61362306a36Sopenharmony_ci
61462306a36Sopenharmony_civoid snd_emu10k1_voice_clear_loop_stop_multiple(struct snd_emu10k1 *emu, u64 voices)
61562306a36Sopenharmony_ci{
61662306a36Sopenharmony_ci	unsigned long flags;
61762306a36Sopenharmony_ci
61862306a36Sopenharmony_ci	spin_lock_irqsave(&emu->emu_lock, flags);
61962306a36Sopenharmony_ci	outl(SOLEL << 16, emu->port + PTR);
62062306a36Sopenharmony_ci	outl(inl(emu->port + DATA) & (u32)~voices, emu->port + DATA);
62162306a36Sopenharmony_ci	outl(SOLEH << 16, emu->port + PTR);
62262306a36Sopenharmony_ci	outl(inl(emu->port + DATA) & (u32)(~voices >> 32), emu->port + DATA);
62362306a36Sopenharmony_ci	spin_unlock_irqrestore(&emu->emu_lock, flags);
62462306a36Sopenharmony_ci}
62562306a36Sopenharmony_ci
62662306a36Sopenharmony_ciint snd_emu10k1_voice_clear_loop_stop_multiple_atomic(struct snd_emu10k1 *emu, u64 voices)
62762306a36Sopenharmony_ci{
62862306a36Sopenharmony_ci	unsigned long flags;
62962306a36Sopenharmony_ci	u32 soll, solh;
63062306a36Sopenharmony_ci	int ret = -EIO;
63162306a36Sopenharmony_ci
63262306a36Sopenharmony_ci	spin_lock_irqsave(&emu->emu_lock, flags);
63362306a36Sopenharmony_ci
63462306a36Sopenharmony_ci	outl(SOLEL << 16, emu->port + PTR);
63562306a36Sopenharmony_ci	soll = inl(emu->port + DATA);
63662306a36Sopenharmony_ci	outl(SOLEH << 16, emu->port + PTR);
63762306a36Sopenharmony_ci	solh = inl(emu->port + DATA);
63862306a36Sopenharmony_ci
63962306a36Sopenharmony_ci	soll &= (u32)~voices;
64062306a36Sopenharmony_ci	solh &= (u32)(~voices >> 32);
64162306a36Sopenharmony_ci
64262306a36Sopenharmony_ci	for (int tries = 0; tries < 1000; tries++) {
64362306a36Sopenharmony_ci		const u32 quart = 1U << (REG_SIZE(WC_CURRENTCHANNEL) - 2);
64462306a36Sopenharmony_ci		// First we wait for the third quarter of the sample cycle ...
64562306a36Sopenharmony_ci		u32 wc = inl(emu->port + WC);
64662306a36Sopenharmony_ci		u32 cc = REG_VAL_GET(WC_CURRENTCHANNEL, wc);
64762306a36Sopenharmony_ci		if (cc >= quart * 2 && cc < quart * 3) {
64862306a36Sopenharmony_ci			// ... and release the low voices, while the high ones are serviced.
64962306a36Sopenharmony_ci			outl(SOLEL << 16, emu->port + PTR);
65062306a36Sopenharmony_ci			outl(soll, emu->port + DATA);
65162306a36Sopenharmony_ci			// Then we wait for the first quarter of the next sample cycle ...
65262306a36Sopenharmony_ci			for (; tries < 1000; tries++) {
65362306a36Sopenharmony_ci				cc = REG_VAL_GET(WC_CURRENTCHANNEL, inl(emu->port + WC));
65462306a36Sopenharmony_ci				if (cc < quart)
65562306a36Sopenharmony_ci					goto good;
65662306a36Sopenharmony_ci				// We will block for 10+ us with interrupts disabled. This is
65762306a36Sopenharmony_ci				// not nice at all, but necessary for reasonable reliability.
65862306a36Sopenharmony_ci				udelay(1);
65962306a36Sopenharmony_ci			}
66062306a36Sopenharmony_ci			break;
66162306a36Sopenharmony_ci		good:
66262306a36Sopenharmony_ci			// ... and release the high voices, while the low ones are serviced.
66362306a36Sopenharmony_ci			outl(SOLEH << 16, emu->port + PTR);
66462306a36Sopenharmony_ci			outl(solh, emu->port + DATA);
66562306a36Sopenharmony_ci			// Finally we verify that nothing interfered in fact.
66662306a36Sopenharmony_ci			if (REG_VAL_GET(WC_SAMPLECOUNTER, inl(emu->port + WC)) ==
66762306a36Sopenharmony_ci			    ((REG_VAL_GET(WC_SAMPLECOUNTER, wc) + 1) & REG_MASK0(WC_SAMPLECOUNTER))) {
66862306a36Sopenharmony_ci				ret = 0;
66962306a36Sopenharmony_ci			} else {
67062306a36Sopenharmony_ci				ret = -EAGAIN;
67162306a36Sopenharmony_ci			}
67262306a36Sopenharmony_ci			break;
67362306a36Sopenharmony_ci		}
67462306a36Sopenharmony_ci		// Don't block for too long
67562306a36Sopenharmony_ci		spin_unlock_irqrestore(&emu->emu_lock, flags);
67662306a36Sopenharmony_ci		udelay(1);
67762306a36Sopenharmony_ci		spin_lock_irqsave(&emu->emu_lock, flags);
67862306a36Sopenharmony_ci	}
67962306a36Sopenharmony_ci
68062306a36Sopenharmony_ci	spin_unlock_irqrestore(&emu->emu_lock, flags);
68162306a36Sopenharmony_ci	return ret;
68262306a36Sopenharmony_ci}
68362306a36Sopenharmony_ci
68462306a36Sopenharmony_civoid snd_emu10k1_wait(struct snd_emu10k1 *emu, unsigned int wait)
68562306a36Sopenharmony_ci{
68662306a36Sopenharmony_ci	volatile unsigned count;
68762306a36Sopenharmony_ci	unsigned int newtime = 0, curtime;
68862306a36Sopenharmony_ci
68962306a36Sopenharmony_ci	curtime = inl(emu->port + WC) >> 6;
69062306a36Sopenharmony_ci	while (wait-- > 0) {
69162306a36Sopenharmony_ci		count = 0;
69262306a36Sopenharmony_ci		while (count++ < 16384) {
69362306a36Sopenharmony_ci			newtime = inl(emu->port + WC) >> 6;
69462306a36Sopenharmony_ci			if (newtime != curtime)
69562306a36Sopenharmony_ci				break;
69662306a36Sopenharmony_ci		}
69762306a36Sopenharmony_ci		if (count > 16384)
69862306a36Sopenharmony_ci			break;
69962306a36Sopenharmony_ci		curtime = newtime;
70062306a36Sopenharmony_ci	}
70162306a36Sopenharmony_ci}
70262306a36Sopenharmony_ci
70362306a36Sopenharmony_ciunsigned short snd_emu10k1_ac97_read(struct snd_ac97 *ac97, unsigned short reg)
70462306a36Sopenharmony_ci{
70562306a36Sopenharmony_ci	struct snd_emu10k1 *emu = ac97->private_data;
70662306a36Sopenharmony_ci	unsigned long flags;
70762306a36Sopenharmony_ci	unsigned short val;
70862306a36Sopenharmony_ci
70962306a36Sopenharmony_ci	spin_lock_irqsave(&emu->emu_lock, flags);
71062306a36Sopenharmony_ci	outb(reg, emu->port + AC97ADDRESS);
71162306a36Sopenharmony_ci	val = inw(emu->port + AC97DATA);
71262306a36Sopenharmony_ci	spin_unlock_irqrestore(&emu->emu_lock, flags);
71362306a36Sopenharmony_ci	return val;
71462306a36Sopenharmony_ci}
71562306a36Sopenharmony_ci
71662306a36Sopenharmony_civoid snd_emu10k1_ac97_write(struct snd_ac97 *ac97, unsigned short reg, unsigned short data)
71762306a36Sopenharmony_ci{
71862306a36Sopenharmony_ci	struct snd_emu10k1 *emu = ac97->private_data;
71962306a36Sopenharmony_ci	unsigned long flags;
72062306a36Sopenharmony_ci
72162306a36Sopenharmony_ci	spin_lock_irqsave(&emu->emu_lock, flags);
72262306a36Sopenharmony_ci	outb(reg, emu->port + AC97ADDRESS);
72362306a36Sopenharmony_ci	outw(data, emu->port + AC97DATA);
72462306a36Sopenharmony_ci	spin_unlock_irqrestore(&emu->emu_lock, flags);
72562306a36Sopenharmony_ci}
726