2019-05-27 09:55:05 +03:00
// SPDX-License-Identifier: GPL-2.0-or-later
2005-04-17 02:20:36 +04:00
/*
2007-10-15 11:50:19 +04:00
* Copyright ( c ) by Jaroslav Kysela < perex @ perex . cz >
2023-07-15 19:08:39 +03:00
* Lee Revell < rlrevell @ joe - job . com >
* James Courtier - Dutton < James @ superbug . co . uk >
* Oswald Buddenhagen < oswald . buddenhagen @ gmx . de >
2005-04-17 02:20:36 +04:00
* Creative Labs , Inc .
2023-07-15 19:08:39 +03:00
*
2005-04-17 02:20:36 +04:00
* Routines for control of EMU10K1 chips
*/
# include <linux/time.h>
# include <sound/core.h>
# include <sound/emu10k1.h>
2005-12-21 17:06:08 +03:00
# include <linux/delay.h>
2011-09-22 17:34:58 +04:00
# include <linux/export.h>
2006-12-06 18:58:02 +03:00
# include "p17v.h"
2005-04-17 02:20:36 +04:00
2023-05-14 20:03:21 +03:00
static inline bool check_ptr_reg ( struct snd_emu10k1 * emu , unsigned int reg )
{
if ( snd_BUG_ON ( ! emu ) )
return false ;
if ( snd_BUG_ON ( reg & ( emu - > audigy ? ( 0xffff0000 & ~ A_PTR_ADDRESS_MASK )
: ( 0xffff0000 & ~ PTR_ADDRESS_MASK ) ) ) )
return false ;
if ( snd_BUG_ON ( reg & 0x0000ffff & ~ PTR_CHANNELNUM_MASK ) )
return false ;
return true ;
}
2005-11-17 16:50:13 +03:00
unsigned int snd_emu10k1_ptr_read ( struct snd_emu10k1 * emu , unsigned int reg , unsigned int chn )
2005-04-17 02:20:36 +04:00
{
unsigned long flags ;
unsigned int regptr , val ;
unsigned int mask ;
2023-05-14 20:03:21 +03:00
regptr = ( reg < < 16 ) | chn ;
if ( ! check_ptr_reg ( emu , regptr ) )
return 0 ;
2005-04-17 02:20:36 +04:00
2023-05-14 20:03:22 +03:00
spin_lock_irqsave ( & emu - > emu_lock , flags ) ;
outl ( regptr , emu - > port + PTR ) ;
val = inl ( emu - > port + DATA ) ;
spin_unlock_irqrestore ( & emu - > emu_lock , flags ) ;
2005-04-17 02:20:36 +04:00
if ( reg & 0xff000000 ) {
unsigned char size , offset ;
size = ( reg > > 24 ) & 0x3f ;
offset = ( reg > > 16 ) & 0x1f ;
2023-05-14 20:03:23 +03:00
mask = ( 1 < < size ) - 1 ;
2005-04-17 02:20:36 +04:00
2023-05-14 20:03:23 +03:00
return ( val > > offset ) & mask ;
2005-04-17 02:20:36 +04:00
} else {
return val ;
}
}
2006-04-28 17:13:39 +04:00
EXPORT_SYMBOL ( snd_emu10k1_ptr_read ) ;
2005-11-17 16:50:13 +03:00
void snd_emu10k1_ptr_write ( struct snd_emu10k1 * emu , unsigned int reg , unsigned int chn , unsigned int data )
2005-04-17 02:20:36 +04:00
{
unsigned int regptr ;
unsigned long flags ;
unsigned int mask ;
2023-05-14 20:03:21 +03:00
regptr = ( reg < < 16 ) | chn ;
if ( ! check_ptr_reg ( emu , regptr ) )
2007-11-10 20:55:14 +03:00
return ;
2005-04-17 02:20:36 +04:00
if ( reg & 0xff000000 ) {
unsigned char size , offset ;
size = ( reg > > 24 ) & 0x3f ;
offset = ( reg > > 16 ) & 0x1f ;
2023-05-14 20:03:21 +03:00
mask = ( 1 < < size ) - 1 ;
if ( snd_BUG_ON ( data & ~ mask ) )
return ;
mask < < = offset ;
data < < = offset ;
2005-04-17 02:20:36 +04:00
spin_lock_irqsave ( & emu - > emu_lock , flags ) ;
outl ( regptr , emu - > port + PTR ) ;
data | = inl ( emu - > port + DATA ) & ~ mask ;
} else {
spin_lock_irqsave ( & emu - > emu_lock , flags ) ;
outl ( regptr , emu - > port + PTR ) ;
}
2023-05-14 20:03:22 +03:00
outl ( data , emu - > port + DATA ) ;
spin_unlock_irqrestore ( & emu - > emu_lock , flags ) ;
2005-04-17 02:20:36 +04:00
}
2006-04-28 17:13:39 +04:00
EXPORT_SYMBOL ( snd_emu10k1_ptr_write ) ;
2023-05-18 12:31:34 +03:00
void snd_emu10k1_ptr_write_multiple ( struct snd_emu10k1 * emu , unsigned int chn , . . . )
{
va_list va ;
u32 addr_mask ;
unsigned long flags ;
if ( snd_BUG_ON ( ! emu ) )
return ;
if ( snd_BUG_ON ( chn & ~ PTR_CHANNELNUM_MASK ) )
return ;
addr_mask = ~ ( ( emu - > audigy ? A_PTR_ADDRESS_MASK : PTR_ADDRESS_MASK ) > > 16 ) ;
va_start ( va , chn ) ;
spin_lock_irqsave ( & emu - > emu_lock , flags ) ;
for ( ; ; ) {
u32 data ;
u32 reg = va_arg ( va , u32 ) ;
if ( reg = = REGLIST_END )
break ;
data = va_arg ( va , u32 ) ;
if ( snd_BUG_ON ( reg & addr_mask ) ) // Only raw registers supported here
continue ;
outl ( ( reg < < 16 ) | chn , emu - > port + PTR ) ;
outl ( data , emu - > port + DATA ) ;
}
spin_unlock_irqrestore ( & emu - > emu_lock , flags ) ;
va_end ( va ) ;
}
EXPORT_SYMBOL ( snd_emu10k1_ptr_write_multiple ) ;
2005-11-17 16:50:13 +03:00
unsigned int snd_emu10k1_ptr20_read ( struct snd_emu10k1 * emu ,
2005-04-17 02:20:36 +04:00
unsigned int reg ,
unsigned int chn )
{
unsigned long flags ;
unsigned int regptr , val ;
regptr = ( reg < < 16 ) | chn ;
spin_lock_irqsave ( & emu - > emu_lock , flags ) ;
2023-04-28 11:07:32 +03:00
outl ( regptr , emu - > port + PTR2 ) ;
val = inl ( emu - > port + DATA2 ) ;
2005-04-17 02:20:36 +04:00
spin_unlock_irqrestore ( & emu - > emu_lock , flags ) ;
return val ;
}
2005-11-17 16:50:13 +03:00
void snd_emu10k1_ptr20_write ( struct snd_emu10k1 * emu ,
2005-04-17 02:20:36 +04:00
unsigned int reg ,
unsigned int chn ,
unsigned int data )
{
unsigned int regptr ;
unsigned long flags ;
regptr = ( reg < < 16 ) | chn ;
spin_lock_irqsave ( & emu - > emu_lock , flags ) ;
2023-04-28 11:07:32 +03:00
outl ( regptr , emu - > port + PTR2 ) ;
outl ( data , emu - > port + DATA2 ) ;
2005-04-17 02:20:36 +04:00
spin_unlock_irqrestore ( & emu - > emu_lock , flags ) ;
}
2005-12-21 17:06:08 +03:00
int snd_emu10k1_spi_write ( struct snd_emu10k1 * emu ,
unsigned int data )
{
unsigned int reset , set ;
unsigned int reg , tmp ;
int n , result ;
2007-11-10 20:55:14 +03:00
int err = 0 ;
/* This function is not re-entrant, so protect against it. */
spin_lock ( & emu - > spi_lock ) ;
2005-12-21 17:41:50 +03:00
if ( emu - > card_capabilities - > ca0108_chip )
2023-04-28 11:07:32 +03:00
reg = P17V_SPI ;
2005-12-21 17:41:50 +03:00
else {
/* For other chip types the SPI register
* is currently unknown . */
2007-11-10 20:55:14 +03:00
err = 1 ;
goto spi_write_exit ;
}
if ( data > 0xffff ) {
/* Only 16bit values allowed */
err = 1 ;
goto spi_write_exit ;
2005-12-21 17:06:08 +03:00
}
tmp = snd_emu10k1_ptr20_read ( emu , reg , 0 ) ;
2005-12-21 17:41:50 +03:00
reset = ( tmp & ~ 0x3ffff ) | 0x20000 ; /* Set xxx20000 */
2005-12-21 17:06:08 +03:00
set = reset | 0x10000 ; /* Set xxx1xxxx */
snd_emu10k1_ptr20_write ( emu , reg , 0 , reset | data ) ;
tmp = snd_emu10k1_ptr20_read ( emu , reg , 0 ) ; /* write post */
snd_emu10k1_ptr20_write ( emu , reg , 0 , set | data ) ;
result = 1 ;
/* Wait for status bit to return to 0 */
2005-12-21 17:56:01 +03:00
for ( n = 0 ; n < 100 ; n + + ) {
2005-12-21 17:06:08 +03:00
udelay ( 10 ) ;
tmp = snd_emu10k1_ptr20_read ( emu , reg , 0 ) ;
if ( ! ( tmp & 0x10000 ) ) {
2005-12-21 17:56:01 +03:00
result = 0 ;
2005-12-21 17:06:08 +03:00
break ;
}
}
2007-11-10 20:55:14 +03:00
if ( result ) {
/* Timed out */
err = 1 ;
goto spi_write_exit ;
}
2005-12-21 17:06:08 +03:00
snd_emu10k1_ptr20_write ( emu , reg , 0 , reset | data ) ;
tmp = snd_emu10k1_ptr20_read ( emu , reg , 0 ) ; /* Write post */
2007-11-10 20:55:14 +03:00
err = 0 ;
spi_write_exit :
spin_unlock ( & emu - > spi_lock ) ;
return err ;
2005-12-21 17:06:08 +03:00
}
2006-12-06 18:58:02 +03:00
/* The ADC does not support i2c read, so only write is implemented */
int snd_emu10k1_i2c_write ( struct snd_emu10k1 * emu ,
u32 reg ,
u32 value )
{
u32 tmp ;
int timeout = 0 ;
int status ;
int retry ;
2007-11-10 20:55:14 +03:00
int err = 0 ;
2006-12-06 18:58:02 +03:00
if ( ( reg > 0x7f ) | | ( value > 0x1ff ) ) {
2014-02-25 20:02:09 +04:00
dev_err ( emu - > card - > dev , " i2c_write: invalid values. \n " ) ;
2006-12-06 18:58:02 +03:00
return - EINVAL ;
}
2007-11-10 20:55:14 +03:00
/* This function is not re-entrant, so protect against it. */
spin_lock ( & emu - > i2c_lock ) ;
2006-12-06 18:58:02 +03:00
tmp = reg < < 25 | value < < 16 ;
/* This controls the I2C connected to the WM8775 ADC Codec */
snd_emu10k1_ptr20_write ( emu , P17V_I2C_1 , 0 , tmp ) ;
tmp = snd_emu10k1_ptr20_read ( emu , P17V_I2C_1 , 0 ) ; /* write post */
for ( retry = 0 ; retry < 10 ; retry + + ) {
/* Send the data to i2c */
tmp = 0 ;
tmp = tmp | ( I2C_A_ADC_LAST | I2C_A_ADC_START | I2C_A_ADC_ADD ) ;
snd_emu10k1_ptr20_write ( emu , P17V_I2C_ADDR , 0 , tmp ) ;
/* Wait till the transaction ends */
while ( 1 ) {
2007-11-10 20:55:14 +03:00
mdelay ( 1 ) ;
2006-12-06 18:58:02 +03:00
status = snd_emu10k1_ptr20_read ( emu , P17V_I2C_ADDR , 0 ) ;
timeout + + ;
if ( ( status & I2C_A_ADC_START ) = = 0 )
break ;
if ( timeout > 1000 ) {
2014-02-25 20:02:09 +04:00
dev_warn ( emu - > card - > dev ,
2009-02-05 18:08:14 +03:00
" emu10k1:I2C:timeout status=0x%x \n " ,
status ) ;
2006-12-06 18:58:02 +03:00
break ;
}
}
//Read back and see if the transaction is successful
if ( ( status & I2C_A_ADC_ABORT ) = = 0 )
break ;
}
if ( retry = = 10 ) {
2014-02-25 20:02:09 +04:00
dev_err ( emu - > card - > dev , " Writing to ADC failed! \n " ) ;
dev_err ( emu - > card - > dev , " status=0x%x, reg=%d, value=%d \n " ,
2007-11-10 20:55:14 +03:00
status , reg , value ) ;
/* dump_stack(); */
err = - EINVAL ;
2006-12-06 18:58:02 +03:00
}
2007-11-10 20:55:14 +03:00
spin_unlock ( & emu - > i2c_lock ) ;
return err ;
2006-12-06 18:58:02 +03:00
}
2023-04-28 12:59:39 +03:00
static void snd_emu1010_fpga_write_locked ( struct snd_emu10k1 * emu , u32 reg , u32 value )
2006-10-01 13:48:04 +04:00
{
2023-04-21 17:10:00 +03:00
if ( snd_BUG_ON ( reg > 0x3f ) )
return ;
2006-10-01 13:48:04 +04:00
reg + = 0x40 ; /* 0x40 upwards are registers. */
2023-04-21 17:10:00 +03:00
if ( snd_BUG_ON ( value > 0x3f ) ) /* 0 to 0x3f are values */
return ;
2023-04-21 17:10:01 +03:00
outw ( reg , emu - > port + A_GPIO ) ;
2006-10-01 13:48:04 +04:00
udelay ( 10 ) ;
2023-04-21 17:10:01 +03:00
outw ( reg | 0x80 , emu - > port + A_GPIO ) ; /* High bit clocks the value into the fpga. */
2006-10-01 13:48:04 +04:00
udelay ( 10 ) ;
2023-04-21 17:10:01 +03:00
outw ( value , emu - > port + A_GPIO ) ;
2006-10-01 13:48:04 +04:00
udelay ( 10 ) ;
2023-04-21 17:10:01 +03:00
outw ( value | 0x80 , emu - > port + A_GPIO ) ; /* High bit clocks the value into the fpga. */
2023-04-28 12:59:39 +03:00
}
void snd_emu1010_fpga_write ( struct snd_emu10k1 * emu , u32 reg , u32 value )
{
unsigned long flags ;
spin_lock_irqsave ( & emu - > emu_lock , flags ) ;
snd_emu1010_fpga_write_locked ( emu , reg , value ) ;
2007-11-04 17:08:26 +03:00
spin_unlock_irqrestore ( & emu - > emu_lock , flags ) ;
2006-10-01 13:48:04 +04:00
}
2023-05-26 13:16:58 +03:00
static void snd_emu1010_fpga_read_locked ( struct snd_emu10k1 * emu , u32 reg , u32 * value )
2006-10-01 13:48:04 +04:00
{
2023-04-22 16:24:30 +03:00
// The higest input pin is used as the designated interrupt trigger,
// so it needs to be masked out.
2023-07-10 09:59:55 +03:00
// But note that any other input pin change will also cause an IRQ,
// so using this function often causes an IRQ as a side effect.
2023-04-22 16:24:30 +03:00
u32 mask = emu - > card_capabilities - > ca0108_chip ? 0x1f : 0x7f ;
2023-04-21 17:10:00 +03:00
if ( snd_BUG_ON ( reg > 0x3f ) )
return ;
2006-10-01 13:48:04 +04:00
reg + = 0x40 ; /* 0x40 upwards are registers. */
2023-04-21 17:10:01 +03:00
outw ( reg , emu - > port + A_GPIO ) ;
2006-10-01 13:48:04 +04:00
udelay ( 10 ) ;
2023-04-21 17:10:01 +03:00
outw ( reg | 0x80 , emu - > port + A_GPIO ) ; /* High bit clocks the value into the fpga. */
2006-10-01 13:48:04 +04:00
udelay ( 10 ) ;
2023-04-22 16:24:30 +03:00
* value = ( ( inw ( emu - > port + A_GPIO ) > > 8 ) & mask ) ;
2023-05-26 13:16:58 +03:00
}
void snd_emu1010_fpga_read ( struct snd_emu10k1 * emu , u32 reg , u32 * value )
{
unsigned long flags ;
spin_lock_irqsave ( & emu - > emu_lock , flags ) ;
snd_emu1010_fpga_read_locked ( emu , reg , value ) ;
2007-11-04 17:08:26 +03:00
spin_unlock_irqrestore ( & emu - > emu_lock , flags ) ;
2006-10-01 13:48:04 +04:00
}
/* Each Destination has one and only one Source,
* but one Source can feed any number of Destinations simultaneously .
*/
2023-04-21 17:10:00 +03:00
void snd_emu1010_fpga_link_dst_src_write ( struct snd_emu10k1 * emu , u32 dst , u32 src )
2006-10-01 13:48:04 +04:00
{
2023-04-28 12:59:39 +03:00
unsigned long flags ;
2023-04-21 17:10:00 +03:00
if ( snd_BUG_ON ( dst & ~ 0x71f ) )
return ;
if ( snd_BUG_ON ( src & ~ 0x71f ) )
return ;
2023-04-28 12:59:39 +03:00
spin_lock_irqsave ( & emu - > emu_lock , flags ) ;
snd_emu1010_fpga_write_locked ( emu , EMU_HANA_DESTHI , dst > > 8 ) ;
snd_emu1010_fpga_write_locked ( emu , EMU_HANA_DESTLO , dst & 0x1f ) ;
snd_emu1010_fpga_write_locked ( emu , EMU_HANA_SRCHI , src > > 8 ) ;
snd_emu1010_fpga_write_locked ( emu , EMU_HANA_SRCLO , src & 0x1f ) ;
spin_unlock_irqrestore ( & emu - > emu_lock , flags ) ;
2006-10-01 13:48:04 +04:00
}
2023-05-26 13:16:58 +03:00
u32 snd_emu1010_fpga_link_dst_src_read ( struct snd_emu10k1 * emu , u32 dst )
{
unsigned long flags ;
u32 hi , lo ;
if ( snd_BUG_ON ( dst & ~ 0x71f ) )
return 0 ;
spin_lock_irqsave ( & emu - > emu_lock , flags ) ;
snd_emu1010_fpga_write_locked ( emu , EMU_HANA_DESTHI , dst > > 8 ) ;
snd_emu1010_fpga_write_locked ( emu , EMU_HANA_DESTLO , dst & 0x1f ) ;
snd_emu1010_fpga_read_locked ( emu , EMU_HANA_SRCHI , & hi ) ;
snd_emu1010_fpga_read_locked ( emu , EMU_HANA_SRCLO , & lo ) ;
spin_unlock_irqrestore ( & emu - > emu_lock , flags ) ;
return ( hi < < 8 ) | lo ;
}
2023-06-12 22:13:19 +03:00
int snd_emu1010_get_raw_rate ( struct snd_emu10k1 * emu , u8 src )
{
u32 reg_lo , reg_hi , value , value2 ;
switch ( src ) {
case EMU_HANA_WCLOCK_HANA_SPDIF_IN :
snd_emu1010_fpga_read ( emu , EMU_HANA_SPDIF_MODE , & value ) ;
if ( value & EMU_HANA_SPDIF_MODE_RX_INVALID )
return 0 ;
reg_lo = EMU_HANA_WC_SPDIF_LO ;
reg_hi = EMU_HANA_WC_SPDIF_HI ;
break ;
case EMU_HANA_WCLOCK_HANA_ADAT_IN :
reg_lo = EMU_HANA_WC_ADAT_LO ;
reg_hi = EMU_HANA_WC_ADAT_HI ;
break ;
case EMU_HANA_WCLOCK_SYNC_BNC :
reg_lo = EMU_HANA_WC_BNC_LO ;
reg_hi = EMU_HANA_WC_BNC_HI ;
break ;
case EMU_HANA_WCLOCK_2ND_HANA :
reg_lo = EMU_HANA2_WC_SPDIF_LO ;
reg_hi = EMU_HANA2_WC_SPDIF_HI ;
break ;
default :
return 0 ;
}
snd_emu1010_fpga_read ( emu , reg_hi , & value ) ;
snd_emu1010_fpga_read ( emu , reg_lo , & value2 ) ;
// FIXME: The /4 is valid for 0404b, but contradicts all other info.
return 0x1770000 / 4 / ( ( ( value < < 5 ) | value2 ) + 1 ) ;
}
2023-06-12 22:13:18 +03:00
void snd_emu1010_update_clock ( struct snd_emu10k1 * emu )
{
2023-06-12 22:13:19 +03:00
int clock ;
2023-06-12 22:13:18 +03:00
u32 leds ;
switch ( emu - > emu1010 . wclock ) {
case EMU_HANA_WCLOCK_INT_44_1K | EMU_HANA_WCLOCK_1X :
2023-06-12 22:13:19 +03:00
clock = 44100 ;
2023-06-12 22:13:18 +03:00
leds = EMU_HANA_DOCK_LEDS_2_44K ;
break ;
case EMU_HANA_WCLOCK_INT_48K | EMU_HANA_WCLOCK_1X :
2023-06-12 22:13:19 +03:00
clock = 48000 ;
2023-06-12 22:13:18 +03:00
leds = EMU_HANA_DOCK_LEDS_2_48K ;
break ;
default :
2023-06-12 22:13:19 +03:00
clock = snd_emu1010_get_raw_rate (
emu , emu - > emu1010 . wclock & EMU_HANA_WCLOCK_SRC_MASK ) ;
// The raw rate reading is rather coarse (it cannot accurately
// represent 44.1 kHz) and fluctuates slightly. Luckily, the
// clock comes from digital inputs, which use standardized rates.
// So we round to the closest standard rate and ignore discrepancies.
if ( clock < 46000 ) {
clock = 44100 ;
leds = EMU_HANA_DOCK_LEDS_2_EXT | EMU_HANA_DOCK_LEDS_2_44K ;
} else {
clock = 48000 ;
leds = EMU_HANA_DOCK_LEDS_2_EXT | EMU_HANA_DOCK_LEDS_2_48K ;
}
2023-06-12 22:13:18 +03:00
break ;
}
2023-06-12 22:13:19 +03:00
emu - > emu1010 . word_clock = clock ;
2023-06-12 22:13:18 +03:00
// FIXME: this should probably represent the AND of all currently
// used sources' lock status. But we don't know how to get that ...
leds | = EMU_HANA_DOCK_LEDS_2_LOCK ;
snd_emu1010_fpga_write ( emu , EMU_HANA_DOCK_LEDS_2 , leds ) ;
}
2005-11-17 16:50:13 +03:00
void snd_emu10k1_intr_enable ( struct snd_emu10k1 * emu , unsigned int intrenb )
2005-04-17 02:20:36 +04:00
{
unsigned long flags ;
unsigned int enable ;
spin_lock_irqsave ( & emu - > emu_lock , flags ) ;
enable = inl ( emu - > port + INTE ) | intrenb ;
outl ( enable , emu - > port + INTE ) ;
spin_unlock_irqrestore ( & emu - > emu_lock , flags ) ;
}
2005-11-17 16:50:13 +03:00
void snd_emu10k1_intr_disable ( struct snd_emu10k1 * emu , unsigned int intrenb )
2005-04-17 02:20:36 +04:00
{
unsigned long flags ;
unsigned int enable ;
spin_lock_irqsave ( & emu - > emu_lock , flags ) ;
enable = inl ( emu - > port + INTE ) & ~ intrenb ;
outl ( enable , emu - > port + INTE ) ;
spin_unlock_irqrestore ( & emu - > emu_lock , flags ) ;
}
2005-11-17 16:50:13 +03:00
void snd_emu10k1_voice_intr_enable ( struct snd_emu10k1 * emu , unsigned int voicenum )
2005-04-17 02:20:36 +04:00
{
unsigned long flags ;
unsigned int val ;
spin_lock_irqsave ( & emu - > emu_lock , flags ) ;
if ( voicenum > = 32 ) {
outl ( CLIEH < < 16 , emu - > port + PTR ) ;
val = inl ( emu - > port + DATA ) ;
val | = 1 < < ( voicenum - 32 ) ;
} else {
outl ( CLIEL < < 16 , emu - > port + PTR ) ;
val = inl ( emu - > port + DATA ) ;
val | = 1 < < voicenum ;
}
outl ( val , emu - > port + DATA ) ;
spin_unlock_irqrestore ( & emu - > emu_lock , flags ) ;
}
2005-11-17 16:50:13 +03:00
void snd_emu10k1_voice_intr_disable ( struct snd_emu10k1 * emu , unsigned int voicenum )
2005-04-17 02:20:36 +04:00
{
unsigned long flags ;
unsigned int val ;
spin_lock_irqsave ( & emu - > emu_lock , flags ) ;
if ( voicenum > = 32 ) {
outl ( CLIEH < < 16 , emu - > port + PTR ) ;
val = inl ( emu - > port + DATA ) ;
val & = ~ ( 1 < < ( voicenum - 32 ) ) ;
} else {
outl ( CLIEL < < 16 , emu - > port + PTR ) ;
val = inl ( emu - > port + DATA ) ;
val & = ~ ( 1 < < voicenum ) ;
}
outl ( val , emu - > port + DATA ) ;
spin_unlock_irqrestore ( & emu - > emu_lock , flags ) ;
}
2005-11-17 16:50:13 +03:00
void snd_emu10k1_voice_intr_ack ( struct snd_emu10k1 * emu , unsigned int voicenum )
2005-04-17 02:20:36 +04:00
{
unsigned long flags ;
spin_lock_irqsave ( & emu - > emu_lock , flags ) ;
if ( voicenum > = 32 ) {
outl ( CLIPH < < 16 , emu - > port + PTR ) ;
voicenum = 1 < < ( voicenum - 32 ) ;
} else {
outl ( CLIPL < < 16 , emu - > port + PTR ) ;
voicenum = 1 < < voicenum ;
}
outl ( voicenum , emu - > port + DATA ) ;
spin_unlock_irqrestore ( & emu - > emu_lock , flags ) ;
}
2005-11-17 16:50:13 +03:00
void snd_emu10k1_voice_half_loop_intr_enable ( struct snd_emu10k1 * emu , unsigned int voicenum )
2005-04-17 02:20:36 +04:00
{
unsigned long flags ;
unsigned int val ;
spin_lock_irqsave ( & emu - > emu_lock , flags ) ;
if ( voicenum > = 32 ) {
outl ( HLIEH < < 16 , emu - > port + PTR ) ;
val = inl ( emu - > port + DATA ) ;
val | = 1 < < ( voicenum - 32 ) ;
} else {
outl ( HLIEL < < 16 , emu - > port + PTR ) ;
val = inl ( emu - > port + DATA ) ;
val | = 1 < < voicenum ;
}
outl ( val , emu - > port + DATA ) ;
spin_unlock_irqrestore ( & emu - > emu_lock , flags ) ;
}
2005-11-17 16:50:13 +03:00
void snd_emu10k1_voice_half_loop_intr_disable ( struct snd_emu10k1 * emu , unsigned int voicenum )
2005-04-17 02:20:36 +04:00
{
unsigned long flags ;
unsigned int val ;
spin_lock_irqsave ( & emu - > emu_lock , flags ) ;
if ( voicenum > = 32 ) {
outl ( HLIEH < < 16 , emu - > port + PTR ) ;
val = inl ( emu - > port + DATA ) ;
val & = ~ ( 1 < < ( voicenum - 32 ) ) ;
} else {
outl ( HLIEL < < 16 , emu - > port + PTR ) ;
val = inl ( emu - > port + DATA ) ;
val & = ~ ( 1 < < voicenum ) ;
}
outl ( val , emu - > port + DATA ) ;
spin_unlock_irqrestore ( & emu - > emu_lock , flags ) ;
}
2005-11-17 16:50:13 +03:00
void snd_emu10k1_voice_half_loop_intr_ack ( struct snd_emu10k1 * emu , unsigned int voicenum )
2005-04-17 02:20:36 +04:00
{
unsigned long flags ;
spin_lock_irqsave ( & emu - > emu_lock , flags ) ;
if ( voicenum > = 32 ) {
outl ( HLIPH < < 16 , emu - > port + PTR ) ;
voicenum = 1 < < ( voicenum - 32 ) ;
} else {
outl ( HLIPL < < 16 , emu - > port + PTR ) ;
voicenum = 1 < < voicenum ;
}
outl ( voicenum , emu - > port + DATA ) ;
spin_unlock_irqrestore ( & emu - > emu_lock , flags ) ;
}
2023-05-16 12:36:09 +03:00
#if 0
2005-11-17 16:50:13 +03:00
void snd_emu10k1_voice_set_loop_stop ( struct snd_emu10k1 * emu , unsigned int voicenum )
2005-04-17 02:20:36 +04:00
{
unsigned long flags ;
unsigned int sol ;
spin_lock_irqsave ( & emu - > emu_lock , flags ) ;
if ( voicenum > = 32 ) {
outl ( SOLEH < < 16 , emu - > port + PTR ) ;
sol = inl ( emu - > port + DATA ) ;
sol | = 1 < < ( voicenum - 32 ) ;
} else {
outl ( SOLEL < < 16 , emu - > port + PTR ) ;
sol = inl ( emu - > port + DATA ) ;
sol | = 1 < < voicenum ;
}
outl ( sol , emu - > port + DATA ) ;
spin_unlock_irqrestore ( & emu - > emu_lock , flags ) ;
}
2005-11-17 16:50:13 +03:00
void snd_emu10k1_voice_clear_loop_stop ( struct snd_emu10k1 * emu , unsigned int voicenum )
2005-04-17 02:20:36 +04:00
{
unsigned long flags ;
unsigned int sol ;
spin_lock_irqsave ( & emu - > emu_lock , flags ) ;
if ( voicenum > = 32 ) {
outl ( SOLEH < < 16 , emu - > port + PTR ) ;
sol = inl ( emu - > port + DATA ) ;
sol & = ~ ( 1 < < ( voicenum - 32 ) ) ;
} else {
outl ( SOLEL < < 16 , emu - > port + PTR ) ;
sol = inl ( emu - > port + DATA ) ;
sol & = ~ ( 1 < < voicenum ) ;
}
outl ( sol , emu - > port + DATA ) ;
spin_unlock_irqrestore ( & emu - > emu_lock , flags ) ;
}
2023-05-16 12:36:09 +03:00
# endif
2005-04-17 02:20:36 +04:00
2023-05-23 23:07:08 +03:00
void snd_emu10k1_voice_set_loop_stop_multiple ( struct snd_emu10k1 * emu , u64 voices )
{
unsigned long flags ;
spin_lock_irqsave ( & emu - > emu_lock , flags ) ;
outl ( SOLEL < < 16 , emu - > port + PTR ) ;
outl ( inl ( emu - > port + DATA ) | ( u32 ) voices , emu - > port + DATA ) ;
outl ( SOLEH < < 16 , emu - > port + PTR ) ;
outl ( inl ( emu - > port + DATA ) | ( u32 ) ( voices > > 32 ) , emu - > port + DATA ) ;
spin_unlock_irqrestore ( & emu - > emu_lock , flags ) ;
}
void snd_emu10k1_voice_clear_loop_stop_multiple ( struct snd_emu10k1 * emu , u64 voices )
{
unsigned long flags ;
spin_lock_irqsave ( & emu - > emu_lock , flags ) ;
outl ( SOLEL < < 16 , emu - > port + PTR ) ;
outl ( inl ( emu - > port + DATA ) & ( u32 ) ~ voices , emu - > port + DATA ) ;
outl ( SOLEH < < 16 , emu - > port + PTR ) ;
outl ( inl ( emu - > port + DATA ) & ( u32 ) ( ~ voices > > 32 ) , emu - > port + DATA ) ;
spin_unlock_irqrestore ( & emu - > emu_lock , flags ) ;
}
int snd_emu10k1_voice_clear_loop_stop_multiple_atomic ( struct snd_emu10k1 * emu , u64 voices )
{
unsigned long flags ;
u32 soll , solh ;
int ret = - EIO ;
spin_lock_irqsave ( & emu - > emu_lock , flags ) ;
outl ( SOLEL < < 16 , emu - > port + PTR ) ;
soll = inl ( emu - > port + DATA ) ;
outl ( SOLEH < < 16 , emu - > port + PTR ) ;
solh = inl ( emu - > port + DATA ) ;
soll & = ( u32 ) ~ voices ;
solh & = ( u32 ) ( ~ voices > > 32 ) ;
for ( int tries = 0 ; tries < 1000 ; tries + + ) {
const u32 quart = 1U < < ( REG_SIZE ( WC_CURRENTCHANNEL ) - 2 ) ;
// First we wait for the third quarter of the sample cycle ...
u32 wc = inl ( emu - > port + WC ) ;
u32 cc = REG_VAL_GET ( WC_CURRENTCHANNEL , wc ) ;
if ( cc > = quart * 2 & & cc < quart * 3 ) {
// ... and release the low voices, while the high ones are serviced.
outl ( SOLEL < < 16 , emu - > port + PTR ) ;
outl ( soll , emu - > port + DATA ) ;
// Then we wait for the first quarter of the next sample cycle ...
for ( ; tries < 1000 ; tries + + ) {
cc = REG_VAL_GET ( WC_CURRENTCHANNEL , inl ( emu - > port + WC ) ) ;
if ( cc < quart )
goto good ;
// We will block for 10+ us with interrupts disabled. This is
// not nice at all, but necessary for reasonable reliability.
udelay ( 1 ) ;
}
break ;
good :
// ... and release the high voices, while the low ones are serviced.
outl ( SOLEH < < 16 , emu - > port + PTR ) ;
outl ( solh , emu - > port + DATA ) ;
// Finally we verify that nothing interfered in fact.
if ( REG_VAL_GET ( WC_SAMPLECOUNTER , inl ( emu - > port + WC ) ) = =
( ( REG_VAL_GET ( WC_SAMPLECOUNTER , wc ) + 1 ) & REG_MASK0 ( WC_SAMPLECOUNTER ) ) ) {
ret = 0 ;
} else {
ret = - EAGAIN ;
}
break ;
}
// Don't block for too long
spin_unlock_irqrestore ( & emu - > emu_lock , flags ) ;
udelay ( 1 ) ;
spin_lock_irqsave ( & emu - > emu_lock , flags ) ;
}
spin_unlock_irqrestore ( & emu - > emu_lock , flags ) ;
return ret ;
}
2005-11-17 16:50:13 +03:00
void snd_emu10k1_wait ( struct snd_emu10k1 * emu , unsigned int wait )
2005-04-17 02:20:36 +04:00
{
volatile unsigned count ;
unsigned int newtime = 0 , curtime ;
curtime = inl ( emu - > port + WC ) > > 6 ;
while ( wait - - > 0 ) {
count = 0 ;
while ( count + + < 16384 ) {
newtime = inl ( emu - > port + WC ) > > 6 ;
if ( newtime ! = curtime )
break ;
}
2009-04-17 01:54:04 +04:00
if ( count > 16384 )
2005-04-17 02:20:36 +04:00
break ;
curtime = newtime ;
}
}
2005-11-17 16:50:13 +03:00
unsigned short snd_emu10k1_ac97_read ( struct snd_ac97 * ac97 , unsigned short reg )
2005-04-17 02:20:36 +04:00
{
2005-11-17 16:50:13 +03:00
struct snd_emu10k1 * emu = ac97 - > private_data ;
2005-04-17 02:20:36 +04:00
unsigned long flags ;
unsigned short val ;
spin_lock_irqsave ( & emu - > emu_lock , flags ) ;
outb ( reg , emu - > port + AC97ADDRESS ) ;
val = inw ( emu - > port + AC97DATA ) ;
spin_unlock_irqrestore ( & emu - > emu_lock , flags ) ;
return val ;
}
2005-11-17 16:50:13 +03:00
void snd_emu10k1_ac97_write ( struct snd_ac97 * ac97 , unsigned short reg , unsigned short data )
2005-04-17 02:20:36 +04:00
{
2005-11-17 16:50:13 +03:00
struct snd_emu10k1 * emu = ac97 - > private_data ;
2005-04-17 02:20:36 +04:00
unsigned long flags ;
spin_lock_irqsave ( & emu - > emu_lock , flags ) ;
outb ( reg , emu - > port + AC97ADDRESS ) ;
outw ( data , emu - > port + AC97DATA ) ;
spin_unlock_irqrestore ( & emu - > emu_lock , flags ) ;
}