18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (c) by Jaroslav Kysela <perex@perex.cz> 48c2ecf20Sopenharmony_ci * Creative Labs, Inc. 58c2ecf20Sopenharmony_ci * Routines for control of EMU10K1 chips 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * BUGS: 88c2ecf20Sopenharmony_ci * -- 98c2ecf20Sopenharmony_ci * 108c2ecf20Sopenharmony_ci * TODO: 118c2ecf20Sopenharmony_ci * -- 128c2ecf20Sopenharmony_ci */ 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#include <linux/time.h> 158c2ecf20Sopenharmony_ci#include <sound/core.h> 168c2ecf20Sopenharmony_ci#include <sound/emu10k1.h> 178c2ecf20Sopenharmony_ci#include <linux/delay.h> 188c2ecf20Sopenharmony_ci#include <linux/export.h> 198c2ecf20Sopenharmony_ci#include "p17v.h" 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ciunsigned int snd_emu10k1_ptr_read(struct snd_emu10k1 * emu, unsigned int reg, unsigned int chn) 228c2ecf20Sopenharmony_ci{ 238c2ecf20Sopenharmony_ci unsigned long flags; 248c2ecf20Sopenharmony_ci unsigned int regptr, val; 258c2ecf20Sopenharmony_ci unsigned int mask; 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci mask = emu->audigy ? A_PTR_ADDRESS_MASK : PTR_ADDRESS_MASK; 288c2ecf20Sopenharmony_ci regptr = ((reg << 16) & mask) | (chn & PTR_CHANNELNUM_MASK); 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci if (reg & 0xff000000) { 318c2ecf20Sopenharmony_ci unsigned char size, offset; 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci size = (reg >> 24) & 0x3f; 348c2ecf20Sopenharmony_ci offset = (reg >> 16) & 0x1f; 358c2ecf20Sopenharmony_ci mask = ((1 << size) - 1) << offset; 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci spin_lock_irqsave(&emu->emu_lock, flags); 388c2ecf20Sopenharmony_ci outl(regptr, emu->port + PTR); 398c2ecf20Sopenharmony_ci val = inl(emu->port + DATA); 408c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&emu->emu_lock, flags); 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci return (val & mask) >> offset; 438c2ecf20Sopenharmony_ci } else { 448c2ecf20Sopenharmony_ci spin_lock_irqsave(&emu->emu_lock, flags); 458c2ecf20Sopenharmony_ci outl(regptr, emu->port + PTR); 468c2ecf20Sopenharmony_ci val = inl(emu->port + DATA); 478c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&emu->emu_lock, flags); 488c2ecf20Sopenharmony_ci return val; 498c2ecf20Sopenharmony_ci } 508c2ecf20Sopenharmony_ci} 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ciEXPORT_SYMBOL(snd_emu10k1_ptr_read); 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_civoid snd_emu10k1_ptr_write(struct snd_emu10k1 *emu, unsigned int reg, unsigned int chn, unsigned int data) 558c2ecf20Sopenharmony_ci{ 568c2ecf20Sopenharmony_ci unsigned int regptr; 578c2ecf20Sopenharmony_ci unsigned long flags; 588c2ecf20Sopenharmony_ci unsigned int mask; 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci if (snd_BUG_ON(!emu)) 618c2ecf20Sopenharmony_ci return; 628c2ecf20Sopenharmony_ci mask = emu->audigy ? A_PTR_ADDRESS_MASK : PTR_ADDRESS_MASK; 638c2ecf20Sopenharmony_ci regptr = ((reg << 16) & mask) | (chn & PTR_CHANNELNUM_MASK); 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci if (reg & 0xff000000) { 668c2ecf20Sopenharmony_ci unsigned char size, offset; 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci size = (reg >> 24) & 0x3f; 698c2ecf20Sopenharmony_ci offset = (reg >> 16) & 0x1f; 708c2ecf20Sopenharmony_ci mask = ((1 << size) - 1) << offset; 718c2ecf20Sopenharmony_ci data = (data << offset) & mask; 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci spin_lock_irqsave(&emu->emu_lock, flags); 748c2ecf20Sopenharmony_ci outl(regptr, emu->port + PTR); 758c2ecf20Sopenharmony_ci data |= inl(emu->port + DATA) & ~mask; 768c2ecf20Sopenharmony_ci outl(data, emu->port + DATA); 778c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&emu->emu_lock, flags); 788c2ecf20Sopenharmony_ci } else { 798c2ecf20Sopenharmony_ci spin_lock_irqsave(&emu->emu_lock, flags); 808c2ecf20Sopenharmony_ci outl(regptr, emu->port + PTR); 818c2ecf20Sopenharmony_ci outl(data, emu->port + DATA); 828c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&emu->emu_lock, flags); 838c2ecf20Sopenharmony_ci } 848c2ecf20Sopenharmony_ci} 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ciEXPORT_SYMBOL(snd_emu10k1_ptr_write); 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ciunsigned int snd_emu10k1_ptr20_read(struct snd_emu10k1 * emu, 898c2ecf20Sopenharmony_ci unsigned int reg, 908c2ecf20Sopenharmony_ci unsigned int chn) 918c2ecf20Sopenharmony_ci{ 928c2ecf20Sopenharmony_ci unsigned long flags; 938c2ecf20Sopenharmony_ci unsigned int regptr, val; 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci regptr = (reg << 16) | chn; 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci spin_lock_irqsave(&emu->emu_lock, flags); 988c2ecf20Sopenharmony_ci outl(regptr, emu->port + 0x20 + PTR); 998c2ecf20Sopenharmony_ci val = inl(emu->port + 0x20 + DATA); 1008c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&emu->emu_lock, flags); 1018c2ecf20Sopenharmony_ci return val; 1028c2ecf20Sopenharmony_ci} 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_civoid snd_emu10k1_ptr20_write(struct snd_emu10k1 *emu, 1058c2ecf20Sopenharmony_ci unsigned int reg, 1068c2ecf20Sopenharmony_ci unsigned int chn, 1078c2ecf20Sopenharmony_ci unsigned int data) 1088c2ecf20Sopenharmony_ci{ 1098c2ecf20Sopenharmony_ci unsigned int regptr; 1108c2ecf20Sopenharmony_ci unsigned long flags; 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci regptr = (reg << 16) | chn; 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci spin_lock_irqsave(&emu->emu_lock, flags); 1158c2ecf20Sopenharmony_ci outl(regptr, emu->port + 0x20 + PTR); 1168c2ecf20Sopenharmony_ci outl(data, emu->port + 0x20 + DATA); 1178c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&emu->emu_lock, flags); 1188c2ecf20Sopenharmony_ci} 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ciint snd_emu10k1_spi_write(struct snd_emu10k1 * emu, 1218c2ecf20Sopenharmony_ci unsigned int data) 1228c2ecf20Sopenharmony_ci{ 1238c2ecf20Sopenharmony_ci unsigned int reset, set; 1248c2ecf20Sopenharmony_ci unsigned int reg, tmp; 1258c2ecf20Sopenharmony_ci int n, result; 1268c2ecf20Sopenharmony_ci int err = 0; 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci /* This function is not re-entrant, so protect against it. */ 1298c2ecf20Sopenharmony_ci spin_lock(&emu->spi_lock); 1308c2ecf20Sopenharmony_ci if (emu->card_capabilities->ca0108_chip) 1318c2ecf20Sopenharmony_ci reg = 0x3c; /* PTR20, reg 0x3c */ 1328c2ecf20Sopenharmony_ci else { 1338c2ecf20Sopenharmony_ci /* For other chip types the SPI register 1348c2ecf20Sopenharmony_ci * is currently unknown. */ 1358c2ecf20Sopenharmony_ci err = 1; 1368c2ecf20Sopenharmony_ci goto spi_write_exit; 1378c2ecf20Sopenharmony_ci } 1388c2ecf20Sopenharmony_ci if (data > 0xffff) { 1398c2ecf20Sopenharmony_ci /* Only 16bit values allowed */ 1408c2ecf20Sopenharmony_ci err = 1; 1418c2ecf20Sopenharmony_ci goto spi_write_exit; 1428c2ecf20Sopenharmony_ci } 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci tmp = snd_emu10k1_ptr20_read(emu, reg, 0); 1458c2ecf20Sopenharmony_ci reset = (tmp & ~0x3ffff) | 0x20000; /* Set xxx20000 */ 1468c2ecf20Sopenharmony_ci set = reset | 0x10000; /* Set xxx1xxxx */ 1478c2ecf20Sopenharmony_ci snd_emu10k1_ptr20_write(emu, reg, 0, reset | data); 1488c2ecf20Sopenharmony_ci tmp = snd_emu10k1_ptr20_read(emu, reg, 0); /* write post */ 1498c2ecf20Sopenharmony_ci snd_emu10k1_ptr20_write(emu, reg, 0, set | data); 1508c2ecf20Sopenharmony_ci result = 1; 1518c2ecf20Sopenharmony_ci /* Wait for status bit to return to 0 */ 1528c2ecf20Sopenharmony_ci for (n = 0; n < 100; n++) { 1538c2ecf20Sopenharmony_ci udelay(10); 1548c2ecf20Sopenharmony_ci tmp = snd_emu10k1_ptr20_read(emu, reg, 0); 1558c2ecf20Sopenharmony_ci if (!(tmp & 0x10000)) { 1568c2ecf20Sopenharmony_ci result = 0; 1578c2ecf20Sopenharmony_ci break; 1588c2ecf20Sopenharmony_ci } 1598c2ecf20Sopenharmony_ci } 1608c2ecf20Sopenharmony_ci if (result) { 1618c2ecf20Sopenharmony_ci /* Timed out */ 1628c2ecf20Sopenharmony_ci err = 1; 1638c2ecf20Sopenharmony_ci goto spi_write_exit; 1648c2ecf20Sopenharmony_ci } 1658c2ecf20Sopenharmony_ci snd_emu10k1_ptr20_write(emu, reg, 0, reset | data); 1668c2ecf20Sopenharmony_ci tmp = snd_emu10k1_ptr20_read(emu, reg, 0); /* Write post */ 1678c2ecf20Sopenharmony_ci err = 0; 1688c2ecf20Sopenharmony_cispi_write_exit: 1698c2ecf20Sopenharmony_ci spin_unlock(&emu->spi_lock); 1708c2ecf20Sopenharmony_ci return err; 1718c2ecf20Sopenharmony_ci} 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci/* The ADC does not support i2c read, so only write is implemented */ 1748c2ecf20Sopenharmony_ciint snd_emu10k1_i2c_write(struct snd_emu10k1 *emu, 1758c2ecf20Sopenharmony_ci u32 reg, 1768c2ecf20Sopenharmony_ci u32 value) 1778c2ecf20Sopenharmony_ci{ 1788c2ecf20Sopenharmony_ci u32 tmp; 1798c2ecf20Sopenharmony_ci int timeout = 0; 1808c2ecf20Sopenharmony_ci int status; 1818c2ecf20Sopenharmony_ci int retry; 1828c2ecf20Sopenharmony_ci int err = 0; 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci if ((reg > 0x7f) || (value > 0x1ff)) { 1858c2ecf20Sopenharmony_ci dev_err(emu->card->dev, "i2c_write: invalid values.\n"); 1868c2ecf20Sopenharmony_ci return -EINVAL; 1878c2ecf20Sopenharmony_ci } 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci /* This function is not re-entrant, so protect against it. */ 1908c2ecf20Sopenharmony_ci spin_lock(&emu->i2c_lock); 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci tmp = reg << 25 | value << 16; 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci /* This controls the I2C connected to the WM8775 ADC Codec */ 1958c2ecf20Sopenharmony_ci snd_emu10k1_ptr20_write(emu, P17V_I2C_1, 0, tmp); 1968c2ecf20Sopenharmony_ci tmp = snd_emu10k1_ptr20_read(emu, P17V_I2C_1, 0); /* write post */ 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci for (retry = 0; retry < 10; retry++) { 1998c2ecf20Sopenharmony_ci /* Send the data to i2c */ 2008c2ecf20Sopenharmony_ci tmp = 0; 2018c2ecf20Sopenharmony_ci tmp = tmp | (I2C_A_ADC_LAST|I2C_A_ADC_START|I2C_A_ADC_ADD); 2028c2ecf20Sopenharmony_ci snd_emu10k1_ptr20_write(emu, P17V_I2C_ADDR, 0, tmp); 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci /* Wait till the transaction ends */ 2058c2ecf20Sopenharmony_ci while (1) { 2068c2ecf20Sopenharmony_ci mdelay(1); 2078c2ecf20Sopenharmony_ci status = snd_emu10k1_ptr20_read(emu, P17V_I2C_ADDR, 0); 2088c2ecf20Sopenharmony_ci timeout++; 2098c2ecf20Sopenharmony_ci if ((status & I2C_A_ADC_START) == 0) 2108c2ecf20Sopenharmony_ci break; 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci if (timeout > 1000) { 2138c2ecf20Sopenharmony_ci dev_warn(emu->card->dev, 2148c2ecf20Sopenharmony_ci "emu10k1:I2C:timeout status=0x%x\n", 2158c2ecf20Sopenharmony_ci status); 2168c2ecf20Sopenharmony_ci break; 2178c2ecf20Sopenharmony_ci } 2188c2ecf20Sopenharmony_ci } 2198c2ecf20Sopenharmony_ci //Read back and see if the transaction is successful 2208c2ecf20Sopenharmony_ci if ((status & I2C_A_ADC_ABORT) == 0) 2218c2ecf20Sopenharmony_ci break; 2228c2ecf20Sopenharmony_ci } 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci if (retry == 10) { 2258c2ecf20Sopenharmony_ci dev_err(emu->card->dev, "Writing to ADC failed!\n"); 2268c2ecf20Sopenharmony_ci dev_err(emu->card->dev, "status=0x%x, reg=%d, value=%d\n", 2278c2ecf20Sopenharmony_ci status, reg, value); 2288c2ecf20Sopenharmony_ci /* dump_stack(); */ 2298c2ecf20Sopenharmony_ci err = -EINVAL; 2308c2ecf20Sopenharmony_ci } 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci spin_unlock(&emu->i2c_lock); 2338c2ecf20Sopenharmony_ci return err; 2348c2ecf20Sopenharmony_ci} 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ciint snd_emu1010_fpga_write(struct snd_emu10k1 * emu, u32 reg, u32 value) 2378c2ecf20Sopenharmony_ci{ 2388c2ecf20Sopenharmony_ci unsigned long flags; 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci if (reg > 0x3f) 2418c2ecf20Sopenharmony_ci return 1; 2428c2ecf20Sopenharmony_ci reg += 0x40; /* 0x40 upwards are registers. */ 2438c2ecf20Sopenharmony_ci if (value > 0x3f) /* 0 to 0x3f are values */ 2448c2ecf20Sopenharmony_ci return 1; 2458c2ecf20Sopenharmony_ci spin_lock_irqsave(&emu->emu_lock, flags); 2468c2ecf20Sopenharmony_ci outl(reg, emu->port + A_IOCFG); 2478c2ecf20Sopenharmony_ci udelay(10); 2488c2ecf20Sopenharmony_ci outl(reg | 0x80, emu->port + A_IOCFG); /* High bit clocks the value into the fpga. */ 2498c2ecf20Sopenharmony_ci udelay(10); 2508c2ecf20Sopenharmony_ci outl(value, emu->port + A_IOCFG); 2518c2ecf20Sopenharmony_ci udelay(10); 2528c2ecf20Sopenharmony_ci outl(value | 0x80 , emu->port + A_IOCFG); /* High bit clocks the value into the fpga. */ 2538c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&emu->emu_lock, flags); 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci return 0; 2568c2ecf20Sopenharmony_ci} 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ciint snd_emu1010_fpga_read(struct snd_emu10k1 * emu, u32 reg, u32 *value) 2598c2ecf20Sopenharmony_ci{ 2608c2ecf20Sopenharmony_ci unsigned long flags; 2618c2ecf20Sopenharmony_ci if (reg > 0x3f) 2628c2ecf20Sopenharmony_ci return 1; 2638c2ecf20Sopenharmony_ci reg += 0x40; /* 0x40 upwards are registers. */ 2648c2ecf20Sopenharmony_ci spin_lock_irqsave(&emu->emu_lock, flags); 2658c2ecf20Sopenharmony_ci outl(reg, emu->port + A_IOCFG); 2668c2ecf20Sopenharmony_ci udelay(10); 2678c2ecf20Sopenharmony_ci outl(reg | 0x80, emu->port + A_IOCFG); /* High bit clocks the value into the fpga. */ 2688c2ecf20Sopenharmony_ci udelay(10); 2698c2ecf20Sopenharmony_ci *value = ((inl(emu->port + A_IOCFG) >> 8) & 0x7f); 2708c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&emu->emu_lock, flags); 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci return 0; 2738c2ecf20Sopenharmony_ci} 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci/* Each Destination has one and only one Source, 2768c2ecf20Sopenharmony_ci * but one Source can feed any number of Destinations simultaneously. 2778c2ecf20Sopenharmony_ci */ 2788c2ecf20Sopenharmony_ciint snd_emu1010_fpga_link_dst_src_write(struct snd_emu10k1 * emu, u32 dst, u32 src) 2798c2ecf20Sopenharmony_ci{ 2808c2ecf20Sopenharmony_ci snd_emu1010_fpga_write(emu, 0x00, ((dst >> 8) & 0x3f) ); 2818c2ecf20Sopenharmony_ci snd_emu1010_fpga_write(emu, 0x01, (dst & 0x3f) ); 2828c2ecf20Sopenharmony_ci snd_emu1010_fpga_write(emu, 0x02, ((src >> 8) & 0x3f) ); 2838c2ecf20Sopenharmony_ci snd_emu1010_fpga_write(emu, 0x03, (src & 0x3f) ); 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci return 0; 2868c2ecf20Sopenharmony_ci} 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_civoid snd_emu10k1_intr_enable(struct snd_emu10k1 *emu, unsigned int intrenb) 2898c2ecf20Sopenharmony_ci{ 2908c2ecf20Sopenharmony_ci unsigned long flags; 2918c2ecf20Sopenharmony_ci unsigned int enable; 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci spin_lock_irqsave(&emu->emu_lock, flags); 2948c2ecf20Sopenharmony_ci enable = inl(emu->port + INTE) | intrenb; 2958c2ecf20Sopenharmony_ci outl(enable, emu->port + INTE); 2968c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&emu->emu_lock, flags); 2978c2ecf20Sopenharmony_ci} 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_civoid snd_emu10k1_intr_disable(struct snd_emu10k1 *emu, unsigned int intrenb) 3008c2ecf20Sopenharmony_ci{ 3018c2ecf20Sopenharmony_ci unsigned long flags; 3028c2ecf20Sopenharmony_ci unsigned int enable; 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci spin_lock_irqsave(&emu->emu_lock, flags); 3058c2ecf20Sopenharmony_ci enable = inl(emu->port + INTE) & ~intrenb; 3068c2ecf20Sopenharmony_ci outl(enable, emu->port + INTE); 3078c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&emu->emu_lock, flags); 3088c2ecf20Sopenharmony_ci} 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_civoid snd_emu10k1_voice_intr_enable(struct snd_emu10k1 *emu, unsigned int voicenum) 3118c2ecf20Sopenharmony_ci{ 3128c2ecf20Sopenharmony_ci unsigned long flags; 3138c2ecf20Sopenharmony_ci unsigned int val; 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci spin_lock_irqsave(&emu->emu_lock, flags); 3168c2ecf20Sopenharmony_ci /* voice interrupt */ 3178c2ecf20Sopenharmony_ci if (voicenum >= 32) { 3188c2ecf20Sopenharmony_ci outl(CLIEH << 16, emu->port + PTR); 3198c2ecf20Sopenharmony_ci val = inl(emu->port + DATA); 3208c2ecf20Sopenharmony_ci val |= 1 << (voicenum - 32); 3218c2ecf20Sopenharmony_ci } else { 3228c2ecf20Sopenharmony_ci outl(CLIEL << 16, emu->port + PTR); 3238c2ecf20Sopenharmony_ci val = inl(emu->port + DATA); 3248c2ecf20Sopenharmony_ci val |= 1 << voicenum; 3258c2ecf20Sopenharmony_ci } 3268c2ecf20Sopenharmony_ci outl(val, emu->port + DATA); 3278c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&emu->emu_lock, flags); 3288c2ecf20Sopenharmony_ci} 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_civoid snd_emu10k1_voice_intr_disable(struct snd_emu10k1 *emu, unsigned int voicenum) 3318c2ecf20Sopenharmony_ci{ 3328c2ecf20Sopenharmony_ci unsigned long flags; 3338c2ecf20Sopenharmony_ci unsigned int val; 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci spin_lock_irqsave(&emu->emu_lock, flags); 3368c2ecf20Sopenharmony_ci /* voice interrupt */ 3378c2ecf20Sopenharmony_ci if (voicenum >= 32) { 3388c2ecf20Sopenharmony_ci outl(CLIEH << 16, emu->port + PTR); 3398c2ecf20Sopenharmony_ci val = inl(emu->port + DATA); 3408c2ecf20Sopenharmony_ci val &= ~(1 << (voicenum - 32)); 3418c2ecf20Sopenharmony_ci } else { 3428c2ecf20Sopenharmony_ci outl(CLIEL << 16, emu->port + PTR); 3438c2ecf20Sopenharmony_ci val = inl(emu->port + DATA); 3448c2ecf20Sopenharmony_ci val &= ~(1 << voicenum); 3458c2ecf20Sopenharmony_ci } 3468c2ecf20Sopenharmony_ci outl(val, emu->port + DATA); 3478c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&emu->emu_lock, flags); 3488c2ecf20Sopenharmony_ci} 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_civoid snd_emu10k1_voice_intr_ack(struct snd_emu10k1 *emu, unsigned int voicenum) 3518c2ecf20Sopenharmony_ci{ 3528c2ecf20Sopenharmony_ci unsigned long flags; 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci spin_lock_irqsave(&emu->emu_lock, flags); 3558c2ecf20Sopenharmony_ci /* voice interrupt */ 3568c2ecf20Sopenharmony_ci if (voicenum >= 32) { 3578c2ecf20Sopenharmony_ci outl(CLIPH << 16, emu->port + PTR); 3588c2ecf20Sopenharmony_ci voicenum = 1 << (voicenum - 32); 3598c2ecf20Sopenharmony_ci } else { 3608c2ecf20Sopenharmony_ci outl(CLIPL << 16, emu->port + PTR); 3618c2ecf20Sopenharmony_ci voicenum = 1 << voicenum; 3628c2ecf20Sopenharmony_ci } 3638c2ecf20Sopenharmony_ci outl(voicenum, emu->port + DATA); 3648c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&emu->emu_lock, flags); 3658c2ecf20Sopenharmony_ci} 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_civoid snd_emu10k1_voice_half_loop_intr_enable(struct snd_emu10k1 *emu, unsigned int voicenum) 3688c2ecf20Sopenharmony_ci{ 3698c2ecf20Sopenharmony_ci unsigned long flags; 3708c2ecf20Sopenharmony_ci unsigned int val; 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci spin_lock_irqsave(&emu->emu_lock, flags); 3738c2ecf20Sopenharmony_ci /* voice interrupt */ 3748c2ecf20Sopenharmony_ci if (voicenum >= 32) { 3758c2ecf20Sopenharmony_ci outl(HLIEH << 16, emu->port + PTR); 3768c2ecf20Sopenharmony_ci val = inl(emu->port + DATA); 3778c2ecf20Sopenharmony_ci val |= 1 << (voicenum - 32); 3788c2ecf20Sopenharmony_ci } else { 3798c2ecf20Sopenharmony_ci outl(HLIEL << 16, emu->port + PTR); 3808c2ecf20Sopenharmony_ci val = inl(emu->port + DATA); 3818c2ecf20Sopenharmony_ci val |= 1 << voicenum; 3828c2ecf20Sopenharmony_ci } 3838c2ecf20Sopenharmony_ci outl(val, emu->port + DATA); 3848c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&emu->emu_lock, flags); 3858c2ecf20Sopenharmony_ci} 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_civoid snd_emu10k1_voice_half_loop_intr_disable(struct snd_emu10k1 *emu, unsigned int voicenum) 3888c2ecf20Sopenharmony_ci{ 3898c2ecf20Sopenharmony_ci unsigned long flags; 3908c2ecf20Sopenharmony_ci unsigned int val; 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci spin_lock_irqsave(&emu->emu_lock, flags); 3938c2ecf20Sopenharmony_ci /* voice interrupt */ 3948c2ecf20Sopenharmony_ci if (voicenum >= 32) { 3958c2ecf20Sopenharmony_ci outl(HLIEH << 16, emu->port + PTR); 3968c2ecf20Sopenharmony_ci val = inl(emu->port + DATA); 3978c2ecf20Sopenharmony_ci val &= ~(1 << (voicenum - 32)); 3988c2ecf20Sopenharmony_ci } else { 3998c2ecf20Sopenharmony_ci outl(HLIEL << 16, emu->port + PTR); 4008c2ecf20Sopenharmony_ci val = inl(emu->port + DATA); 4018c2ecf20Sopenharmony_ci val &= ~(1 << voicenum); 4028c2ecf20Sopenharmony_ci } 4038c2ecf20Sopenharmony_ci outl(val, emu->port + DATA); 4048c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&emu->emu_lock, flags); 4058c2ecf20Sopenharmony_ci} 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_civoid snd_emu10k1_voice_half_loop_intr_ack(struct snd_emu10k1 *emu, unsigned int voicenum) 4088c2ecf20Sopenharmony_ci{ 4098c2ecf20Sopenharmony_ci unsigned long flags; 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci spin_lock_irqsave(&emu->emu_lock, flags); 4128c2ecf20Sopenharmony_ci /* voice interrupt */ 4138c2ecf20Sopenharmony_ci if (voicenum >= 32) { 4148c2ecf20Sopenharmony_ci outl(HLIPH << 16, emu->port + PTR); 4158c2ecf20Sopenharmony_ci voicenum = 1 << (voicenum - 32); 4168c2ecf20Sopenharmony_ci } else { 4178c2ecf20Sopenharmony_ci outl(HLIPL << 16, emu->port + PTR); 4188c2ecf20Sopenharmony_ci voicenum = 1 << voicenum; 4198c2ecf20Sopenharmony_ci } 4208c2ecf20Sopenharmony_ci outl(voicenum, emu->port + DATA); 4218c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&emu->emu_lock, flags); 4228c2ecf20Sopenharmony_ci} 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_civoid snd_emu10k1_voice_set_loop_stop(struct snd_emu10k1 *emu, unsigned int voicenum) 4258c2ecf20Sopenharmony_ci{ 4268c2ecf20Sopenharmony_ci unsigned long flags; 4278c2ecf20Sopenharmony_ci unsigned int sol; 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci spin_lock_irqsave(&emu->emu_lock, flags); 4308c2ecf20Sopenharmony_ci /* voice interrupt */ 4318c2ecf20Sopenharmony_ci if (voicenum >= 32) { 4328c2ecf20Sopenharmony_ci outl(SOLEH << 16, emu->port + PTR); 4338c2ecf20Sopenharmony_ci sol = inl(emu->port + DATA); 4348c2ecf20Sopenharmony_ci sol |= 1 << (voicenum - 32); 4358c2ecf20Sopenharmony_ci } else { 4368c2ecf20Sopenharmony_ci outl(SOLEL << 16, emu->port + PTR); 4378c2ecf20Sopenharmony_ci sol = inl(emu->port + DATA); 4388c2ecf20Sopenharmony_ci sol |= 1 << voicenum; 4398c2ecf20Sopenharmony_ci } 4408c2ecf20Sopenharmony_ci outl(sol, emu->port + DATA); 4418c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&emu->emu_lock, flags); 4428c2ecf20Sopenharmony_ci} 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_civoid snd_emu10k1_voice_clear_loop_stop(struct snd_emu10k1 *emu, unsigned int voicenum) 4458c2ecf20Sopenharmony_ci{ 4468c2ecf20Sopenharmony_ci unsigned long flags; 4478c2ecf20Sopenharmony_ci unsigned int sol; 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ci spin_lock_irqsave(&emu->emu_lock, flags); 4508c2ecf20Sopenharmony_ci /* voice interrupt */ 4518c2ecf20Sopenharmony_ci if (voicenum >= 32) { 4528c2ecf20Sopenharmony_ci outl(SOLEH << 16, emu->port + PTR); 4538c2ecf20Sopenharmony_ci sol = inl(emu->port + DATA); 4548c2ecf20Sopenharmony_ci sol &= ~(1 << (voicenum - 32)); 4558c2ecf20Sopenharmony_ci } else { 4568c2ecf20Sopenharmony_ci outl(SOLEL << 16, emu->port + PTR); 4578c2ecf20Sopenharmony_ci sol = inl(emu->port + DATA); 4588c2ecf20Sopenharmony_ci sol &= ~(1 << voicenum); 4598c2ecf20Sopenharmony_ci } 4608c2ecf20Sopenharmony_ci outl(sol, emu->port + DATA); 4618c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&emu->emu_lock, flags); 4628c2ecf20Sopenharmony_ci} 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_civoid snd_emu10k1_wait(struct snd_emu10k1 *emu, unsigned int wait) 4658c2ecf20Sopenharmony_ci{ 4668c2ecf20Sopenharmony_ci volatile unsigned count; 4678c2ecf20Sopenharmony_ci unsigned int newtime = 0, curtime; 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_ci curtime = inl(emu->port + WC) >> 6; 4708c2ecf20Sopenharmony_ci while (wait-- > 0) { 4718c2ecf20Sopenharmony_ci count = 0; 4728c2ecf20Sopenharmony_ci while (count++ < 16384) { 4738c2ecf20Sopenharmony_ci newtime = inl(emu->port + WC) >> 6; 4748c2ecf20Sopenharmony_ci if (newtime != curtime) 4758c2ecf20Sopenharmony_ci break; 4768c2ecf20Sopenharmony_ci } 4778c2ecf20Sopenharmony_ci if (count > 16384) 4788c2ecf20Sopenharmony_ci break; 4798c2ecf20Sopenharmony_ci curtime = newtime; 4808c2ecf20Sopenharmony_ci } 4818c2ecf20Sopenharmony_ci} 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_ciunsigned short snd_emu10k1_ac97_read(struct snd_ac97 *ac97, unsigned short reg) 4848c2ecf20Sopenharmony_ci{ 4858c2ecf20Sopenharmony_ci struct snd_emu10k1 *emu = ac97->private_data; 4868c2ecf20Sopenharmony_ci unsigned long flags; 4878c2ecf20Sopenharmony_ci unsigned short val; 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ci spin_lock_irqsave(&emu->emu_lock, flags); 4908c2ecf20Sopenharmony_ci outb(reg, emu->port + AC97ADDRESS); 4918c2ecf20Sopenharmony_ci val = inw(emu->port + AC97DATA); 4928c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&emu->emu_lock, flags); 4938c2ecf20Sopenharmony_ci return val; 4948c2ecf20Sopenharmony_ci} 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_civoid snd_emu10k1_ac97_write(struct snd_ac97 *ac97, unsigned short reg, unsigned short data) 4978c2ecf20Sopenharmony_ci{ 4988c2ecf20Sopenharmony_ci struct snd_emu10k1 *emu = ac97->private_data; 4998c2ecf20Sopenharmony_ci unsigned long flags; 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_ci spin_lock_irqsave(&emu->emu_lock, flags); 5028c2ecf20Sopenharmony_ci outb(reg, emu->port + AC97ADDRESS); 5038c2ecf20Sopenharmony_ci outw(data, emu->port + AC97DATA); 5048c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&emu->emu_lock, flags); 5058c2ecf20Sopenharmony_ci} 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_ci/* 5088c2ecf20Sopenharmony_ci * convert rate to pitch 5098c2ecf20Sopenharmony_ci */ 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_ciunsigned int snd_emu10k1_rate_to_pitch(unsigned int rate) 5128c2ecf20Sopenharmony_ci{ 5138c2ecf20Sopenharmony_ci static const u32 logMagTable[128] = { 5148c2ecf20Sopenharmony_ci 0x00000, 0x02dfc, 0x05b9e, 0x088e6, 0x0b5d6, 0x0e26f, 0x10eb3, 0x13aa2, 5158c2ecf20Sopenharmony_ci 0x1663f, 0x1918a, 0x1bc84, 0x1e72e, 0x2118b, 0x23b9a, 0x2655d, 0x28ed5, 5168c2ecf20Sopenharmony_ci 0x2b803, 0x2e0e8, 0x30985, 0x331db, 0x359eb, 0x381b6, 0x3a93d, 0x3d081, 5178c2ecf20Sopenharmony_ci 0x3f782, 0x41e42, 0x444c1, 0x46b01, 0x49101, 0x4b6c4, 0x4dc49, 0x50191, 5188c2ecf20Sopenharmony_ci 0x5269e, 0x54b6f, 0x57006, 0x59463, 0x5b888, 0x5dc74, 0x60029, 0x623a7, 5198c2ecf20Sopenharmony_ci 0x646ee, 0x66a00, 0x68cdd, 0x6af86, 0x6d1fa, 0x6f43c, 0x7164b, 0x73829, 5208c2ecf20Sopenharmony_ci 0x759d4, 0x77b4f, 0x79c9a, 0x7bdb5, 0x7dea1, 0x7ff5e, 0x81fed, 0x8404e, 5218c2ecf20Sopenharmony_ci 0x86082, 0x88089, 0x8a064, 0x8c014, 0x8df98, 0x8fef1, 0x91e20, 0x93d26, 5228c2ecf20Sopenharmony_ci 0x95c01, 0x97ab4, 0x9993e, 0x9b79f, 0x9d5d9, 0x9f3ec, 0xa11d8, 0xa2f9d, 5238c2ecf20Sopenharmony_ci 0xa4d3c, 0xa6ab5, 0xa8808, 0xaa537, 0xac241, 0xadf26, 0xafbe7, 0xb1885, 5248c2ecf20Sopenharmony_ci 0xb3500, 0xb5157, 0xb6d8c, 0xb899f, 0xba58f, 0xbc15e, 0xbdd0c, 0xbf899, 5258c2ecf20Sopenharmony_ci 0xc1404, 0xc2f50, 0xc4a7b, 0xc6587, 0xc8073, 0xc9b3f, 0xcb5ed, 0xcd07c, 5268c2ecf20Sopenharmony_ci 0xceaec, 0xd053f, 0xd1f73, 0xd398a, 0xd5384, 0xd6d60, 0xd8720, 0xda0c3, 5278c2ecf20Sopenharmony_ci 0xdba4a, 0xdd3b4, 0xded03, 0xe0636, 0xe1f4e, 0xe384a, 0xe512c, 0xe69f3, 5288c2ecf20Sopenharmony_ci 0xe829f, 0xe9b31, 0xeb3a9, 0xecc08, 0xee44c, 0xefc78, 0xf148a, 0xf2c83, 5298c2ecf20Sopenharmony_ci 0xf4463, 0xf5c2a, 0xf73da, 0xf8b71, 0xfa2f0, 0xfba57, 0xfd1a7, 0xfe8df 5308c2ecf20Sopenharmony_ci }; 5318c2ecf20Sopenharmony_ci static const char logSlopeTable[128] = { 5328c2ecf20Sopenharmony_ci 0x5c, 0x5c, 0x5b, 0x5a, 0x5a, 0x59, 0x58, 0x58, 5338c2ecf20Sopenharmony_ci 0x57, 0x56, 0x56, 0x55, 0x55, 0x54, 0x53, 0x53, 5348c2ecf20Sopenharmony_ci 0x52, 0x52, 0x51, 0x51, 0x50, 0x50, 0x4f, 0x4f, 5358c2ecf20Sopenharmony_ci 0x4e, 0x4d, 0x4d, 0x4d, 0x4c, 0x4c, 0x4b, 0x4b, 5368c2ecf20Sopenharmony_ci 0x4a, 0x4a, 0x49, 0x49, 0x48, 0x48, 0x47, 0x47, 5378c2ecf20Sopenharmony_ci 0x47, 0x46, 0x46, 0x45, 0x45, 0x45, 0x44, 0x44, 5388c2ecf20Sopenharmony_ci 0x43, 0x43, 0x43, 0x42, 0x42, 0x42, 0x41, 0x41, 5398c2ecf20Sopenharmony_ci 0x41, 0x40, 0x40, 0x40, 0x3f, 0x3f, 0x3f, 0x3e, 5408c2ecf20Sopenharmony_ci 0x3e, 0x3e, 0x3d, 0x3d, 0x3d, 0x3c, 0x3c, 0x3c, 5418c2ecf20Sopenharmony_ci 0x3b, 0x3b, 0x3b, 0x3b, 0x3a, 0x3a, 0x3a, 0x39, 5428c2ecf20Sopenharmony_ci 0x39, 0x39, 0x39, 0x38, 0x38, 0x38, 0x38, 0x37, 5438c2ecf20Sopenharmony_ci 0x37, 0x37, 0x37, 0x36, 0x36, 0x36, 0x36, 0x35, 5448c2ecf20Sopenharmony_ci 0x35, 0x35, 0x35, 0x34, 0x34, 0x34, 0x34, 0x34, 5458c2ecf20Sopenharmony_ci 0x33, 0x33, 0x33, 0x33, 0x32, 0x32, 0x32, 0x32, 5468c2ecf20Sopenharmony_ci 0x32, 0x31, 0x31, 0x31, 0x31, 0x31, 0x30, 0x30, 5478c2ecf20Sopenharmony_ci 0x30, 0x30, 0x30, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f 5488c2ecf20Sopenharmony_ci }; 5498c2ecf20Sopenharmony_ci int i; 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_ci if (rate == 0) 5528c2ecf20Sopenharmony_ci return 0; /* Bail out if no leading "1" */ 5538c2ecf20Sopenharmony_ci rate *= 11185; /* Scale 48000 to 0x20002380 */ 5548c2ecf20Sopenharmony_ci for (i = 31; i > 0; i--) { 5558c2ecf20Sopenharmony_ci if (rate & 0x80000000) { /* Detect leading "1" */ 5568c2ecf20Sopenharmony_ci return (((unsigned int) (i - 15) << 20) + 5578c2ecf20Sopenharmony_ci logMagTable[0x7f & (rate >> 24)] + 5588c2ecf20Sopenharmony_ci (0x7f & (rate >> 17)) * 5598c2ecf20Sopenharmony_ci logSlopeTable[0x7f & (rate >> 24)]); 5608c2ecf20Sopenharmony_ci } 5618c2ecf20Sopenharmony_ci rate <<= 1; 5628c2ecf20Sopenharmony_ci } 5638c2ecf20Sopenharmony_ci 5648c2ecf20Sopenharmony_ci return 0; /* Should never reach this point */ 5658c2ecf20Sopenharmony_ci} 5668c2ecf20Sopenharmony_ci 567