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