2010-01-18 15:44:04 +01:00
/*
* card driver for models with WM8776 / WM8766 DACs ( Xonar DS )
*
* Copyright ( c ) Clemens Ladisch < clemens @ ladisch . de >
*
*
* This driver is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License , version 2.
*
* This driver is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this driver ; if not , see < http : //www.gnu.org/licenses/>.
*/
/*
* Xonar DS
* - - - - - - - -
*
* CMI8788 :
*
* SPI 0 - > WM8766 ( surround , center / LFE , back )
* SPI 1 - > WM8776 ( front , input )
*
2010-09-09 12:20:29 +02:00
* GPIO 4 < - headphone detect , 0 = plugged
* GPIO 6 - > route input jack to mic - in ( 0 ) or line - in ( 1 )
2010-09-09 12:23:06 +02:00
* GPIO 7 - > enable output to front L / R speaker channels
* GPIO 8 - > enable output to other speaker channels and front panel headphone
2010-09-09 12:19:21 +02:00
*
* WM8766 :
*
* input 1 < - line
* input 2 < - mic
* input 3 < - front mic
* input 4 < - aux
2010-01-18 15:44:04 +01:00
*/
# include <linux/pci.h>
# include <linux/delay.h>
# include <sound/control.h>
# include <sound/core.h>
2010-09-09 12:20:29 +02:00
# include <sound/jack.h>
2010-01-18 15:44:04 +01:00
# include <sound/pcm.h>
# include <sound/pcm_params.h>
# include <sound/tlv.h>
# include "xonar.h"
# include "wm8776.h"
# include "wm8766.h"
# define GPIO_DS_HP_DETECT 0x0010
# define GPIO_DS_INPUT_ROUTE 0x0040
2010-09-09 12:23:06 +02:00
# define GPIO_DS_OUTPUT_FRONTLR 0x0080
# define GPIO_DS_OUTPUT_ENABLE 0x0100
2010-01-18 15:44:04 +01:00
# define LC_CONTROL_LIMITER 0x40000000
# define LC_CONTROL_ALC 0x20000000
struct xonar_wm87x6 {
struct xonar_generic generic ;
u16 wm8776_regs [ 0x17 ] ;
u16 wm8766_regs [ 0x10 ] ;
2010-09-07 13:38:49 +02:00
struct snd_kcontrol * line_adcmux_control ;
struct snd_kcontrol * mic_adcmux_control ;
2010-01-18 15:44:04 +01:00
struct snd_kcontrol * lc_controls [ 13 ] ;
2010-09-09 12:20:29 +02:00
struct snd_jack * hp_jack ;
2010-01-18 15:44:04 +01:00
} ;
static void wm8776_write ( struct oxygen * chip ,
unsigned int reg , unsigned int value )
{
struct xonar_wm87x6 * data = chip - > model_data ;
oxygen_write_spi ( chip , OXYGEN_SPI_TRIGGER |
OXYGEN_SPI_DATA_LENGTH_2 |
OXYGEN_SPI_CLOCK_160 |
( 1 < < OXYGEN_SPI_CODEC_SHIFT ) |
OXYGEN_SPI_CEN_LATCH_CLOCK_LO ,
( reg < < 9 ) | value ) ;
if ( reg < ARRAY_SIZE ( data - > wm8776_regs ) ) {
2010-03-03 09:16:18 +01:00
if ( reg > = WM8776_HPLVOL & & reg < = WM8776_DACMASTER )
2010-01-18 15:44:04 +01:00
value & = ~ WM8776_UPDATE ;
data - > wm8776_regs [ reg ] = value ;
}
}
static void wm8776_write_cached ( struct oxygen * chip ,
unsigned int reg , unsigned int value )
{
struct xonar_wm87x6 * data = chip - > model_data ;
if ( reg > = ARRAY_SIZE ( data - > wm8776_regs ) | |
value ! = data - > wm8776_regs [ reg ] )
wm8776_write ( chip , reg , value ) ;
}
static void wm8766_write ( struct oxygen * chip ,
unsigned int reg , unsigned int value )
{
struct xonar_wm87x6 * data = chip - > model_data ;
oxygen_write_spi ( chip , OXYGEN_SPI_TRIGGER |
OXYGEN_SPI_DATA_LENGTH_2 |
OXYGEN_SPI_CLOCK_160 |
( 0 < < OXYGEN_SPI_CODEC_SHIFT ) |
OXYGEN_SPI_CEN_LATCH_CLOCK_LO ,
( reg < < 9 ) | value ) ;
2010-09-09 12:18:35 +02:00
if ( reg < ARRAY_SIZE ( data - > wm8766_regs ) ) {
if ( ( reg > = WM8766_LDA1 & & reg < = WM8766_RDA1 ) | |
( reg > = WM8766_LDA2 & & reg < = WM8766_MASTDA ) )
value & = ~ WM8766_UPDATE ;
2010-01-18 15:44:04 +01:00
data - > wm8766_regs [ reg ] = value ;
2010-09-09 12:18:35 +02:00
}
2010-01-18 15:44:04 +01:00
}
static void wm8766_write_cached ( struct oxygen * chip ,
unsigned int reg , unsigned int value )
{
struct xonar_wm87x6 * data = chip - > model_data ;
if ( reg > = ARRAY_SIZE ( data - > wm8766_regs ) | |
2010-09-09 12:18:35 +02:00
value ! = data - > wm8766_regs [ reg ] )
2010-01-18 15:44:04 +01:00
wm8766_write ( chip , reg , value ) ;
}
static void wm8776_registers_init ( struct oxygen * chip )
{
struct xonar_wm87x6 * data = chip - > model_data ;
wm8776_write ( chip , WM8776_RESET , 0 ) ;
wm8776_write ( chip , WM8776_DACCTRL1 , WM8776_DZCEN |
WM8776_PL_LEFT_LEFT | WM8776_PL_RIGHT_RIGHT ) ;
wm8776_write ( chip , WM8776_DACMUTE , chip - > dac_mute ? WM8776_DMUTE : 0 ) ;
wm8776_write ( chip , WM8776_DACIFCTRL ,
WM8776_DACFMT_LJUST | WM8776_DACWL_24 ) ;
wm8776_write ( chip , WM8776_ADCIFCTRL ,
data - > wm8776_regs [ WM8776_ADCIFCTRL ] ) ;
wm8776_write ( chip , WM8776_MSTRCTRL , data - > wm8776_regs [ WM8776_MSTRCTRL ] ) ;
wm8776_write ( chip , WM8776_PWRDOWN , data - > wm8776_regs [ WM8776_PWRDOWN ] ) ;
wm8776_write ( chip , WM8776_HPLVOL , data - > wm8776_regs [ WM8776_HPLVOL ] ) ;
wm8776_write ( chip , WM8776_HPRVOL , data - > wm8776_regs [ WM8776_HPRVOL ] |
WM8776_UPDATE ) ;
wm8776_write ( chip , WM8776_ADCLVOL , data - > wm8776_regs [ WM8776_ADCLVOL ] ) ;
wm8776_write ( chip , WM8776_ADCRVOL , data - > wm8776_regs [ WM8776_ADCRVOL ] ) ;
wm8776_write ( chip , WM8776_ADCMUX , data - > wm8776_regs [ WM8776_ADCMUX ] ) ;
wm8776_write ( chip , WM8776_DACLVOL , chip - > dac_volume [ 0 ] ) ;
wm8776_write ( chip , WM8776_DACRVOL , chip - > dac_volume [ 1 ] | WM8776_UPDATE ) ;
}
static void wm8766_registers_init ( struct oxygen * chip )
{
2010-09-09 12:23:06 +02:00
struct xonar_wm87x6 * data = chip - > model_data ;
2010-01-18 15:44:04 +01:00
wm8766_write ( chip , WM8766_RESET , 0 ) ;
2010-09-09 12:23:06 +02:00
wm8766_write ( chip , WM8766_DAC_CTRL , data - > wm8766_regs [ WM8766_DAC_CTRL ] ) ;
2010-01-18 15:44:04 +01:00
wm8766_write ( chip , WM8766_INT_CTRL , WM8766_FMT_LJUST | WM8766_IWL_24 ) ;
wm8766_write ( chip , WM8766_DAC_CTRL2 ,
WM8766_ZCD | ( chip - > dac_mute ? WM8766_DMUTE_MASK : 0 ) ) ;
wm8766_write ( chip , WM8766_LDA1 , chip - > dac_volume [ 2 ] ) ;
wm8766_write ( chip , WM8766_RDA1 , chip - > dac_volume [ 3 ] ) ;
wm8766_write ( chip , WM8766_LDA2 , chip - > dac_volume [ 4 ] ) ;
wm8766_write ( chip , WM8766_RDA2 , chip - > dac_volume [ 5 ] ) ;
wm8766_write ( chip , WM8766_LDA3 , chip - > dac_volume [ 6 ] ) ;
wm8766_write ( chip , WM8766_RDA3 , chip - > dac_volume [ 7 ] | WM8766_UPDATE ) ;
}
static void wm8776_init ( struct oxygen * chip )
{
struct xonar_wm87x6 * data = chip - > model_data ;
data - > wm8776_regs [ WM8776_HPLVOL ] = ( 0x79 - 60 ) | WM8776_HPZCEN ;
data - > wm8776_regs [ WM8776_HPRVOL ] = ( 0x79 - 60 ) | WM8776_HPZCEN ;
data - > wm8776_regs [ WM8776_ADCIFCTRL ] =
WM8776_ADCFMT_LJUST | WM8776_ADCWL_24 | WM8776_ADCMCLK ;
data - > wm8776_regs [ WM8776_MSTRCTRL ] =
WM8776_ADCRATE_256 | WM8776_DACRATE_256 ;
data - > wm8776_regs [ WM8776_PWRDOWN ] = WM8776_HPPD ;
data - > wm8776_regs [ WM8776_ADCLVOL ] = 0xa5 | WM8776_ZCA ;
data - > wm8776_regs [ WM8776_ADCRVOL ] = 0xa5 | WM8776_ZCA ;
data - > wm8776_regs [ WM8776_ADCMUX ] = 0x001 ;
wm8776_registers_init ( chip ) ;
}
2010-09-09 12:23:06 +02:00
static void wm8766_init ( struct oxygen * chip )
2010-09-09 12:20:29 +02:00
{
struct xonar_wm87x6 * data = chip - > model_data ;
2010-09-09 12:23:06 +02:00
data - > wm8766_regs [ WM8766_DAC_CTRL ] =
WM8766_PL_LEFT_LEFT | WM8766_PL_RIGHT_RIGHT ;
wm8766_registers_init ( chip ) ;
}
static void xonar_ds_handle_hp_jack ( struct oxygen * chip )
{
struct xonar_wm87x6 * data = chip - > model_data ;
bool hp_plugged ;
unsigned int reg ;
mutex_lock ( & chip - > mutex ) ;
hp_plugged = ! ( oxygen_read16 ( chip , OXYGEN_GPIO_DATA ) &
GPIO_DS_HP_DETECT ) ;
oxygen_write16_masked ( chip , OXYGEN_GPIO_DATA ,
hp_plugged ? 0 : GPIO_DS_OUTPUT_FRONTLR ,
GPIO_DS_OUTPUT_FRONTLR ) ;
reg = data - > wm8766_regs [ WM8766_DAC_CTRL ] & ~ WM8766_MUTEALL ;
if ( hp_plugged )
reg | = WM8766_MUTEALL ;
wm8766_write_cached ( chip , WM8766_DAC_CTRL , reg ) ;
snd_jack_report ( data - > hp_jack , hp_plugged ? SND_JACK_HEADPHONE : 0 ) ;
mutex_unlock ( & chip - > mutex ) ;
2010-09-09 12:20:29 +02:00
}
2010-01-18 15:44:04 +01:00
static void xonar_ds_init ( struct oxygen * chip )
{
struct xonar_wm87x6 * data = chip - > model_data ;
data - > generic . anti_pop_delay = 300 ;
data - > generic . output_enable_bit = GPIO_DS_OUTPUT_ENABLE ;
wm8776_init ( chip ) ;
2010-09-09 12:23:06 +02:00
wm8766_init ( chip ) ;
2010-01-18 15:44:04 +01:00
2010-09-09 12:23:06 +02:00
oxygen_set_bits16 ( chip , OXYGEN_GPIO_CONTROL ,
GPIO_DS_INPUT_ROUTE | GPIO_DS_OUTPUT_FRONTLR ) ;
oxygen_clear_bits16 ( chip , OXYGEN_GPIO_CONTROL ,
GPIO_DS_HP_DETECT ) ;
2010-01-18 15:44:04 +01:00
oxygen_set_bits16 ( chip , OXYGEN_GPIO_DATA , GPIO_DS_INPUT_ROUTE ) ;
oxygen_set_bits16 ( chip , OXYGEN_GPIO_INTERRUPT_MASK , GPIO_DS_HP_DETECT ) ;
chip - > interrupt_mask | = OXYGEN_INT_GPIO ;
xonar_enable_output ( chip ) ;
2010-09-09 12:20:29 +02:00
snd_jack_new ( chip - > card , " Headphone " ,
SND_JACK_HEADPHONE , & data - > hp_jack ) ;
2010-09-09 12:23:06 +02:00
xonar_ds_handle_hp_jack ( chip ) ;
2010-09-09 12:20:29 +02:00
2010-01-18 15:44:04 +01:00
snd_component_add ( chip - > card , " WM8776 " ) ;
snd_component_add ( chip - > card , " WM8766 " ) ;
}
static void xonar_ds_cleanup ( struct oxygen * chip )
{
xonar_disable_output ( chip ) ;
2010-09-07 13:37:10 +02:00
wm8776_write ( chip , WM8776_RESET , 0 ) ;
2010-01-18 15:44:04 +01:00
}
static void xonar_ds_suspend ( struct oxygen * chip )
{
xonar_ds_cleanup ( chip ) ;
}
static void xonar_ds_resume ( struct oxygen * chip )
{
wm8776_registers_init ( chip ) ;
wm8766_registers_init ( chip ) ;
xonar_enable_output ( chip ) ;
2010-09-09 12:23:06 +02:00
xonar_ds_handle_hp_jack ( chip ) ;
2010-01-18 15:44:04 +01:00
}
static void wm8776_adc_hardware_filter ( unsigned int channel ,
struct snd_pcm_hardware * hardware )
{
if ( channel = = PCM_A ) {
hardware - > rates = SNDRV_PCM_RATE_32000 |
SNDRV_PCM_RATE_44100 |
SNDRV_PCM_RATE_48000 |
SNDRV_PCM_RATE_64000 |
SNDRV_PCM_RATE_88200 |
SNDRV_PCM_RATE_96000 ;
hardware - > rate_max = 96000 ;
}
}
static void set_wm87x6_dac_params ( struct oxygen * chip ,
struct snd_pcm_hw_params * params )
{
}
static void set_wm8776_adc_params ( struct oxygen * chip ,
struct snd_pcm_hw_params * params )
{
u16 reg ;
reg = WM8776_ADCRATE_256 | WM8776_DACRATE_256 ;
if ( params_rate ( params ) > 48000 )
reg | = WM8776_ADCOSR ;
wm8776_write_cached ( chip , WM8776_MSTRCTRL , reg ) ;
}
static void update_wm8776_volume ( struct oxygen * chip )
{
struct xonar_wm87x6 * data = chip - > model_data ;
u8 to_change ;
if ( chip - > dac_volume [ 0 ] = = chip - > dac_volume [ 1 ] ) {
if ( chip - > dac_volume [ 0 ] ! = data - > wm8776_regs [ WM8776_DACLVOL ] | |
chip - > dac_volume [ 1 ] ! = data - > wm8776_regs [ WM8776_DACRVOL ] ) {
wm8776_write ( chip , WM8776_DACMASTER ,
chip - > dac_volume [ 0 ] | WM8776_UPDATE ) ;
data - > wm8776_regs [ WM8776_DACLVOL ] = chip - > dac_volume [ 0 ] ;
data - > wm8776_regs [ WM8776_DACRVOL ] = chip - > dac_volume [ 0 ] ;
}
} else {
to_change = ( chip - > dac_volume [ 0 ] ! =
data - > wm8776_regs [ WM8776_DACLVOL ] ) < < 0 ;
to_change | = ( chip - > dac_volume [ 1 ] ! =
data - > wm8776_regs [ WM8776_DACLVOL ] ) < < 1 ;
if ( to_change & 1 )
wm8776_write ( chip , WM8776_DACLVOL , chip - > dac_volume [ 0 ] |
( ( to_change & 2 ) ? 0 : WM8776_UPDATE ) ) ;
if ( to_change & 2 )
wm8776_write ( chip , WM8776_DACRVOL ,
chip - > dac_volume [ 1 ] | WM8776_UPDATE ) ;
}
}
static void update_wm87x6_volume ( struct oxygen * chip )
{
static const u8 wm8766_regs [ 6 ] = {
WM8766_LDA1 , WM8766_RDA1 ,
WM8766_LDA2 , WM8766_RDA2 ,
WM8766_LDA3 , WM8766_RDA3 ,
} ;
struct xonar_wm87x6 * data = chip - > model_data ;
unsigned int i ;
u8 to_change ;
update_wm8776_volume ( chip ) ;
if ( chip - > dac_volume [ 2 ] = = chip - > dac_volume [ 3 ] & &
chip - > dac_volume [ 2 ] = = chip - > dac_volume [ 4 ] & &
chip - > dac_volume [ 2 ] = = chip - > dac_volume [ 5 ] & &
chip - > dac_volume [ 2 ] = = chip - > dac_volume [ 6 ] & &
chip - > dac_volume [ 2 ] = = chip - > dac_volume [ 7 ] ) {
to_change = 0 ;
for ( i = 0 ; i < 6 ; + + i )
if ( chip - > dac_volume [ 2 ] ! =
data - > wm8766_regs [ wm8766_regs [ i ] ] )
to_change = 1 ;
if ( to_change ) {
wm8766_write ( chip , WM8766_MASTDA ,
chip - > dac_volume [ 2 ] | WM8766_UPDATE ) ;
for ( i = 0 ; i < 6 ; + + i )
data - > wm8766_regs [ wm8766_regs [ i ] ] =
chip - > dac_volume [ 2 ] ;
}
} else {
to_change = 0 ;
for ( i = 0 ; i < 6 ; + + i )
to_change | = ( chip - > dac_volume [ 2 + i ] ! =
data - > wm8766_regs [ wm8766_regs [ i ] ] ) < < i ;
for ( i = 0 ; i < 6 ; + + i )
if ( to_change & ( 1 < < i ) )
wm8766_write ( chip , wm8766_regs [ i ] ,
chip - > dac_volume [ 2 + i ] |
( ( to_change & ( 0x3e < < i ) )
? 0 : WM8766_UPDATE ) ) ;
}
}
static void update_wm8776_mute ( struct oxygen * chip )
{
wm8776_write_cached ( chip , WM8776_DACMUTE ,
chip - > dac_mute ? WM8776_DMUTE : 0 ) ;
}
static void update_wm87x6_mute ( struct oxygen * chip )
{
update_wm8776_mute ( chip ) ;
wm8766_write_cached ( chip , WM8766_DAC_CTRL2 , WM8766_ZCD |
( chip - > dac_mute ? WM8766_DMUTE_MASK : 0 ) ) ;
}
2010-09-09 12:24:35 +02:00
static void update_wm8766_center_lfe_mix ( struct oxygen * chip , bool mixed )
{
struct xonar_wm87x6 * data = chip - > model_data ;
unsigned int reg ;
/*
* The WM8766 can mix left and right channels , but this setting
* applies to all three stereo pairs .
*/
reg = data - > wm8766_regs [ WM8766_DAC_CTRL ] &
~ ( WM8766_PL_LEFT_MASK | WM8766_PL_RIGHT_MASK ) ;
if ( mixed )
reg | = WM8766_PL_LEFT_LRMIX | WM8766_PL_RIGHT_LRMIX ;
else
reg | = WM8766_PL_LEFT_LEFT | WM8766_PL_RIGHT_RIGHT ;
wm8766_write_cached ( chip , WM8766_DAC_CTRL , reg ) ;
}
2010-01-18 15:44:04 +01:00
static void xonar_ds_gpio_changed ( struct oxygen * chip )
{
2010-09-09 12:23:06 +02:00
xonar_ds_handle_hp_jack ( chip ) ;
2010-01-18 15:44:04 +01:00
}
static int wm8776_bit_switch_get ( struct snd_kcontrol * ctl ,
struct snd_ctl_elem_value * value )
{
struct oxygen * chip = ctl - > private_data ;
struct xonar_wm87x6 * data = chip - > model_data ;
u16 bit = ctl - > private_value & 0xffff ;
unsigned int reg_index = ( ctl - > private_value > > 16 ) & 0xff ;
bool invert = ( ctl - > private_value > > 24 ) & 1 ;
value - > value . integer . value [ 0 ] =
( ( data - > wm8776_regs [ reg_index ] & bit ) ! = 0 ) ^ invert ;
return 0 ;
}
static int wm8776_bit_switch_put ( struct snd_kcontrol * ctl ,
struct snd_ctl_elem_value * value )
{
struct oxygen * chip = ctl - > private_data ;
struct xonar_wm87x6 * data = chip - > model_data ;
u16 bit = ctl - > private_value & 0xffff ;
u16 reg_value ;
unsigned int reg_index = ( ctl - > private_value > > 16 ) & 0xff ;
bool invert = ( ctl - > private_value > > 24 ) & 1 ;
int changed ;
mutex_lock ( & chip - > mutex ) ;
reg_value = data - > wm8776_regs [ reg_index ] & ~ bit ;
if ( value - > value . integer . value [ 0 ] ^ invert )
reg_value | = bit ;
changed = reg_value ! = data - > wm8776_regs [ reg_index ] ;
if ( changed )
wm8776_write ( chip , reg_index , reg_value ) ;
mutex_unlock ( & chip - > mutex ) ;
return changed ;
}
static int wm8776_field_enum_info ( struct snd_kcontrol * ctl ,
struct snd_ctl_elem_info * info )
{
static const char * const hld [ 16 ] = {
" 0 ms " , " 2.67 ms " , " 5.33 ms " , " 10.6 ms " ,
" 21.3 ms " , " 42.7 ms " , " 85.3 ms " , " 171 ms " ,
" 341 ms " , " 683 ms " , " 1.37 s " , " 2.73 s " ,
" 5.46 s " , " 10.9 s " , " 21.8 s " , " 43.7 s " ,
} ;
static const char * const atk_lim [ 11 ] = {
" 0.25 ms " , " 0.5 ms " , " 1 ms " , " 2 ms " ,
" 4 ms " , " 8 ms " , " 16 ms " , " 32 ms " ,
" 64 ms " , " 128 ms " , " 256 ms " ,
} ;
static const char * const atk_alc [ 11 ] = {
" 8.40 ms " , " 16.8 ms " , " 33.6 ms " , " 67.2 ms " ,
" 134 ms " , " 269 ms " , " 538 ms " , " 1.08 s " ,
" 2.15 s " , " 4.3 s " , " 8.6 s " ,
} ;
static const char * const dcy_lim [ 11 ] = {
" 1.2 ms " , " 2.4 ms " , " 4.8 ms " , " 9.6 ms " ,
" 19.2 ms " , " 38.4 ms " , " 76.8 ms " , " 154 ms " ,
" 307 ms " , " 614 ms " , " 1.23 s " ,
} ;
static const char * const dcy_alc [ 11 ] = {
" 33.5 ms " , " 67.0 ms " , " 134 ms " , " 268 ms " ,
" 536 ms " , " 1.07 s " , " 2.14 s " , " 4.29 s " ,
" 8.58 s " , " 17.2 s " , " 34.3 s " ,
} ;
static const char * const tranwin [ 8 ] = {
" 0 us " , " 62.5 us " , " 125 us " , " 250 us " ,
" 500 us " , " 1 ms " , " 2 ms " , " 4 ms " ,
} ;
u8 max ;
const char * const * names ;
max = ( ctl - > private_value > > 12 ) & 0xf ;
info - > type = SNDRV_CTL_ELEM_TYPE_ENUMERATED ;
info - > count = 1 ;
info - > value . enumerated . items = max + 1 ;
if ( info - > value . enumerated . item > max )
info - > value . enumerated . item = max ;
switch ( ( ctl - > private_value > > 24 ) & 0x1f ) {
case WM8776_ALCCTRL2 :
names = hld ;
break ;
case WM8776_ALCCTRL3 :
if ( ( ( ctl - > private_value > > 20 ) & 0xf ) = = 0 ) {
if ( ctl - > private_value & LC_CONTROL_LIMITER )
names = atk_lim ;
else
names = atk_alc ;
} else {
if ( ctl - > private_value & LC_CONTROL_LIMITER )
names = dcy_lim ;
else
names = dcy_alc ;
}
break ;
case WM8776_LIMITER :
names = tranwin ;
break ;
default :
return - ENXIO ;
}
strcpy ( info - > value . enumerated . name , names [ info - > value . enumerated . item ] ) ;
return 0 ;
}
static int wm8776_field_volume_info ( struct snd_kcontrol * ctl ,
struct snd_ctl_elem_info * info )
{
info - > type = SNDRV_CTL_ELEM_TYPE_INTEGER ;
info - > count = 1 ;
info - > value . integer . min = ( ctl - > private_value > > 8 ) & 0xf ;
info - > value . integer . max = ( ctl - > private_value > > 12 ) & 0xf ;
return 0 ;
}
static void wm8776_field_set_from_ctl ( struct snd_kcontrol * ctl )
{
struct oxygen * chip = ctl - > private_data ;
struct xonar_wm87x6 * data = chip - > model_data ;
unsigned int value , reg_index , mode ;
u8 min , max , shift ;
u16 mask , reg_value ;
bool invert ;
if ( ( data - > wm8776_regs [ WM8776_ALCCTRL1 ] & WM8776_LCSEL_MASK ) = =
WM8776_LCSEL_LIMITER )
mode = LC_CONTROL_LIMITER ;
else
mode = LC_CONTROL_ALC ;
if ( ! ( ctl - > private_value & mode ) )
return ;
value = ctl - > private_value & 0xf ;
min = ( ctl - > private_value > > 8 ) & 0xf ;
max = ( ctl - > private_value > > 12 ) & 0xf ;
mask = ( ctl - > private_value > > 16 ) & 0xf ;
shift = ( ctl - > private_value > > 20 ) & 0xf ;
reg_index = ( ctl - > private_value > > 24 ) & 0x1f ;
invert = ( ctl - > private_value > > 29 ) & 0x1 ;
if ( invert )
value = max - ( value - min ) ;
reg_value = data - > wm8776_regs [ reg_index ] ;
reg_value & = ~ ( mask < < shift ) ;
reg_value | = value < < shift ;
wm8776_write_cached ( chip , reg_index , reg_value ) ;
}
static int wm8776_field_set ( struct snd_kcontrol * ctl , unsigned int value )
{
struct oxygen * chip = ctl - > private_data ;
u8 min , max ;
int changed ;
min = ( ctl - > private_value > > 8 ) & 0xf ;
max = ( ctl - > private_value > > 12 ) & 0xf ;
if ( value < min | | value > max )
return - EINVAL ;
mutex_lock ( & chip - > mutex ) ;
changed = value ! = ( ctl - > private_value & 0xf ) ;
if ( changed ) {
ctl - > private_value = ( ctl - > private_value & ~ 0xf ) | value ;
wm8776_field_set_from_ctl ( ctl ) ;
}
mutex_unlock ( & chip - > mutex ) ;
return changed ;
}
static int wm8776_field_enum_get ( struct snd_kcontrol * ctl ,
struct snd_ctl_elem_value * value )
{
value - > value . enumerated . item [ 0 ] = ctl - > private_value & 0xf ;
return 0 ;
}
static int wm8776_field_volume_get ( struct snd_kcontrol * ctl ,
struct snd_ctl_elem_value * value )
{
value - > value . integer . value [ 0 ] = ctl - > private_value & 0xf ;
return 0 ;
}
static int wm8776_field_enum_put ( struct snd_kcontrol * ctl ,
struct snd_ctl_elem_value * value )
{
return wm8776_field_set ( ctl , value - > value . enumerated . item [ 0 ] ) ;
}
static int wm8776_field_volume_put ( struct snd_kcontrol * ctl ,
struct snd_ctl_elem_value * value )
{
return wm8776_field_set ( ctl , value - > value . integer . value [ 0 ] ) ;
}
static int wm8776_hp_vol_info ( struct snd_kcontrol * ctl ,
struct snd_ctl_elem_info * info )
{
info - > type = SNDRV_CTL_ELEM_TYPE_INTEGER ;
info - > count = 2 ;
info - > value . integer . min = 0x79 - 60 ;
info - > value . integer . max = 0x7f ;
return 0 ;
}
static int wm8776_hp_vol_get ( struct snd_kcontrol * ctl ,
struct snd_ctl_elem_value * value )
{
struct oxygen * chip = ctl - > private_data ;
struct xonar_wm87x6 * data = chip - > model_data ;
mutex_lock ( & chip - > mutex ) ;
value - > value . integer . value [ 0 ] =
data - > wm8776_regs [ WM8776_HPLVOL ] & WM8776_HPATT_MASK ;
value - > value . integer . value [ 1 ] =
data - > wm8776_regs [ WM8776_HPRVOL ] & WM8776_HPATT_MASK ;
mutex_unlock ( & chip - > mutex ) ;
return 0 ;
}
static int wm8776_hp_vol_put ( struct snd_kcontrol * ctl ,
struct snd_ctl_elem_value * value )
{
struct oxygen * chip = ctl - > private_data ;
struct xonar_wm87x6 * data = chip - > model_data ;
u8 to_update ;
mutex_lock ( & chip - > mutex ) ;
to_update = ( value - > value . integer . value [ 0 ] ! =
( data - > wm8776_regs [ WM8776_HPLVOL ] & WM8776_HPATT_MASK ) )
< < 0 ;
to_update | = ( value - > value . integer . value [ 1 ] ! =
( data - > wm8776_regs [ WM8776_HPRVOL ] & WM8776_HPATT_MASK ) )
< < 1 ;
if ( value - > value . integer . value [ 0 ] = = value - > value . integer . value [ 1 ] ) {
if ( to_update ) {
wm8776_write ( chip , WM8776_HPMASTER ,
value - > value . integer . value [ 0 ] |
WM8776_HPZCEN | WM8776_UPDATE ) ;
data - > wm8776_regs [ WM8776_HPLVOL ] =
value - > value . integer . value [ 0 ] | WM8776_HPZCEN ;
data - > wm8776_regs [ WM8776_HPRVOL ] =
value - > value . integer . value [ 0 ] | WM8776_HPZCEN ;
}
} else {
if ( to_update & 1 )
wm8776_write ( chip , WM8776_HPLVOL ,
value - > value . integer . value [ 0 ] |
WM8776_HPZCEN |
( ( to_update & 2 ) ? 0 : WM8776_UPDATE ) ) ;
if ( to_update & 2 )
wm8776_write ( chip , WM8776_HPRVOL ,
value - > value . integer . value [ 1 ] |
WM8776_HPZCEN | WM8776_UPDATE ) ;
}
mutex_unlock ( & chip - > mutex ) ;
return to_update ! = 0 ;
}
static int wm8776_input_mux_get ( struct snd_kcontrol * ctl ,
struct snd_ctl_elem_value * value )
{
struct oxygen * chip = ctl - > private_data ;
struct xonar_wm87x6 * data = chip - > model_data ;
unsigned int mux_bit = ctl - > private_value ;
value - > value . integer . value [ 0 ] =
! ! ( data - > wm8776_regs [ WM8776_ADCMUX ] & mux_bit ) ;
return 0 ;
}
static int wm8776_input_mux_put ( struct snd_kcontrol * ctl ,
struct snd_ctl_elem_value * value )
{
struct oxygen * chip = ctl - > private_data ;
struct xonar_wm87x6 * data = chip - > model_data ;
2010-09-07 13:38:49 +02:00
struct snd_kcontrol * other_ctl ;
2010-01-18 15:44:04 +01:00
unsigned int mux_bit = ctl - > private_value ;
u16 reg ;
int changed ;
mutex_lock ( & chip - > mutex ) ;
reg = data - > wm8776_regs [ WM8776_ADCMUX ] ;
if ( value - > value . integer . value [ 0 ] ) {
reg | = mux_bit ;
2010-09-07 13:38:49 +02:00
/* line-in and mic-in are exclusive */
mux_bit ^ = 3 ;
if ( reg & mux_bit ) {
reg & = ~ mux_bit ;
if ( mux_bit = = 1 )
other_ctl = data - > line_adcmux_control ;
else
other_ctl = data - > mic_adcmux_control ;
snd_ctl_notify ( chip - > card , SNDRV_CTL_EVENT_MASK_VALUE ,
& other_ctl - > id ) ;
}
2010-01-18 15:44:04 +01:00
} else
reg & = ~ mux_bit ;
changed = reg ! = data - > wm8776_regs [ WM8776_ADCMUX ] ;
if ( changed ) {
oxygen_write16_masked ( chip , OXYGEN_GPIO_DATA ,
reg & 1 ? GPIO_DS_INPUT_ROUTE : 0 ,
GPIO_DS_INPUT_ROUTE ) ;
wm8776_write ( chip , WM8776_ADCMUX , reg ) ;
}
mutex_unlock ( & chip - > mutex ) ;
return changed ;
}
static int wm8776_input_vol_info ( struct snd_kcontrol * ctl ,
struct snd_ctl_elem_info * info )
{
info - > type = SNDRV_CTL_ELEM_TYPE_INTEGER ;
info - > count = 2 ;
info - > value . integer . min = 0xa5 ;
info - > value . integer . max = 0xff ;
return 0 ;
}
static int wm8776_input_vol_get ( struct snd_kcontrol * ctl ,
struct snd_ctl_elem_value * value )
{
struct oxygen * chip = ctl - > private_data ;
struct xonar_wm87x6 * data = chip - > model_data ;
mutex_lock ( & chip - > mutex ) ;
value - > value . integer . value [ 0 ] =
data - > wm8776_regs [ WM8776_ADCLVOL ] & WM8776_AGMASK ;
value - > value . integer . value [ 1 ] =
data - > wm8776_regs [ WM8776_ADCRVOL ] & WM8776_AGMASK ;
mutex_unlock ( & chip - > mutex ) ;
return 0 ;
}
static int wm8776_input_vol_put ( struct snd_kcontrol * ctl ,
struct snd_ctl_elem_value * value )
{
struct oxygen * chip = ctl - > private_data ;
struct xonar_wm87x6 * data = chip - > model_data ;
int changed = 0 ;
mutex_lock ( & chip - > mutex ) ;
changed = ( value - > value . integer . value [ 0 ] ! =
( data - > wm8776_regs [ WM8776_ADCLVOL ] & WM8776_AGMASK ) ) | |
( value - > value . integer . value [ 1 ] ! =
( data - > wm8776_regs [ WM8776_ADCRVOL ] & WM8776_AGMASK ) ) ;
wm8776_write_cached ( chip , WM8776_ADCLVOL ,
value - > value . integer . value [ 0 ] | WM8776_ZCA ) ;
wm8776_write_cached ( chip , WM8776_ADCRVOL ,
value - > value . integer . value [ 1 ] | WM8776_ZCA ) ;
mutex_unlock ( & chip - > mutex ) ;
return changed ;
}
static int wm8776_level_control_info ( struct snd_kcontrol * ctl ,
struct snd_ctl_elem_info * info )
{
static const char * const names [ 3 ] = {
" None " , " Peak Limiter " , " Automatic Level Control "
} ;
info - > type = SNDRV_CTL_ELEM_TYPE_ENUMERATED ;
info - > count = 1 ;
info - > value . enumerated . items = 3 ;
if ( info - > value . enumerated . item > = 3 )
info - > value . enumerated . item = 2 ;
strcpy ( info - > value . enumerated . name , names [ info - > value . enumerated . item ] ) ;
return 0 ;
}
static int wm8776_level_control_get ( struct snd_kcontrol * ctl ,
struct snd_ctl_elem_value * value )
{
struct oxygen * chip = ctl - > private_data ;
struct xonar_wm87x6 * data = chip - > model_data ;
if ( ! ( data - > wm8776_regs [ WM8776_ALCCTRL2 ] & WM8776_LCEN ) )
value - > value . enumerated . item [ 0 ] = 0 ;
else if ( ( data - > wm8776_regs [ WM8776_ALCCTRL1 ] & WM8776_LCSEL_MASK ) = =
WM8776_LCSEL_LIMITER )
value - > value . enumerated . item [ 0 ] = 1 ;
else
value - > value . enumerated . item [ 0 ] = 2 ;
return 0 ;
}
static void activate_control ( struct oxygen * chip ,
struct snd_kcontrol * ctl , unsigned int mode )
{
unsigned int access ;
if ( ctl - > private_value & mode )
access = 0 ;
else
access = SNDRV_CTL_ELEM_ACCESS_INACTIVE ;
if ( ( ctl - > vd [ 0 ] . access & SNDRV_CTL_ELEM_ACCESS_INACTIVE ) ! = access ) {
ctl - > vd [ 0 ] . access ^ = SNDRV_CTL_ELEM_ACCESS_INACTIVE ;
snd_ctl_notify ( chip - > card , SNDRV_CTL_EVENT_MASK_INFO , & ctl - > id ) ;
}
}
static int wm8776_level_control_put ( struct snd_kcontrol * ctl ,
struct snd_ctl_elem_value * value )
{
struct oxygen * chip = ctl - > private_data ;
struct xonar_wm87x6 * data = chip - > model_data ;
unsigned int mode = 0 , i ;
u16 ctrl1 , ctrl2 ;
int changed ;
if ( value - > value . enumerated . item [ 0 ] > = 3 )
return - EINVAL ;
mutex_lock ( & chip - > mutex ) ;
changed = value - > value . enumerated . item [ 0 ] ! = ctl - > private_value ;
if ( changed ) {
ctl - > private_value = value - > value . enumerated . item [ 0 ] ;
ctrl1 = data - > wm8776_regs [ WM8776_ALCCTRL1 ] ;
ctrl2 = data - > wm8776_regs [ WM8776_ALCCTRL2 ] ;
switch ( value - > value . enumerated . item [ 0 ] ) {
default :
wm8776_write_cached ( chip , WM8776_ALCCTRL2 ,
ctrl2 & ~ WM8776_LCEN ) ;
break ;
case 1 :
wm8776_write_cached ( chip , WM8776_ALCCTRL1 ,
( ctrl1 & ~ WM8776_LCSEL_MASK ) |
WM8776_LCSEL_LIMITER ) ;
wm8776_write_cached ( chip , WM8776_ALCCTRL2 ,
ctrl2 | WM8776_LCEN ) ;
mode = LC_CONTROL_LIMITER ;
break ;
case 2 :
wm8776_write_cached ( chip , WM8776_ALCCTRL1 ,
( ctrl1 & ~ WM8776_LCSEL_MASK ) |
WM8776_LCSEL_ALC_STEREO ) ;
wm8776_write_cached ( chip , WM8776_ALCCTRL2 ,
ctrl2 | WM8776_LCEN ) ;
mode = LC_CONTROL_ALC ;
break ;
}
for ( i = 0 ; i < ARRAY_SIZE ( data - > lc_controls ) ; + + i )
activate_control ( chip , data - > lc_controls [ i ] , mode ) ;
}
mutex_unlock ( & chip - > mutex ) ;
return changed ;
}
static int hpf_info ( struct snd_kcontrol * ctl , struct snd_ctl_elem_info * info )
{
static const char * const names [ 2 ] = {
" None " , " High-pass Filter "
} ;
info - > type = SNDRV_CTL_ELEM_TYPE_ENUMERATED ;
info - > count = 1 ;
info - > value . enumerated . items = 2 ;
if ( info - > value . enumerated . item > = 2 )
info - > value . enumerated . item = 1 ;
strcpy ( info - > value . enumerated . name , names [ info - > value . enumerated . item ] ) ;
return 0 ;
}
static int hpf_get ( struct snd_kcontrol * ctl , struct snd_ctl_elem_value * value )
{
struct oxygen * chip = ctl - > private_data ;
struct xonar_wm87x6 * data = chip - > model_data ;
value - > value . enumerated . item [ 0 ] =
! ( data - > wm8776_regs [ WM8776_ADCIFCTRL ] & WM8776_ADCHPD ) ;
return 0 ;
}
static int hpf_put ( struct snd_kcontrol * ctl , struct snd_ctl_elem_value * value )
{
struct oxygen * chip = ctl - > private_data ;
struct xonar_wm87x6 * data = chip - > model_data ;
unsigned int reg ;
int changed ;
mutex_lock ( & chip - > mutex ) ;
reg = data - > wm8776_regs [ WM8776_ADCIFCTRL ] & ~ WM8776_ADCHPD ;
if ( ! value - > value . enumerated . item [ 0 ] )
reg | = WM8776_ADCHPD ;
changed = reg ! = data - > wm8776_regs [ WM8776_ADCIFCTRL ] ;
if ( changed )
wm8776_write ( chip , WM8776_ADCIFCTRL , reg ) ;
mutex_unlock ( & chip - > mutex ) ;
return changed ;
}
# define WM8776_BIT_SWITCH(xname, reg, bit, invert, flags) { \
. iface = SNDRV_CTL_ELEM_IFACE_MIXER , \
. name = xname , \
. info = snd_ctl_boolean_mono_info , \
. get = wm8776_bit_switch_get , \
. put = wm8776_bit_switch_put , \
. private_value = ( ( reg ) < < 16 ) | ( bit ) | ( ( invert ) < < 24 ) | ( flags ) , \
}
# define _WM8776_FIELD_CTL(xname, reg, shift, initval, min, max, mask, flags) \
. iface = SNDRV_CTL_ELEM_IFACE_MIXER , \
. name = xname , \
. private_value = ( initval ) | ( ( min ) < < 8 ) | ( ( max ) < < 12 ) | \
( ( mask ) < < 16 ) | ( ( shift ) < < 20 ) | ( ( reg ) < < 24 ) | ( flags )
# define WM8776_FIELD_CTL_ENUM(xname, reg, shift, init, min, max, mask, flags) {\
_WM8776_FIELD_CTL ( xname " Capture Enum " , \
reg , shift , init , min , max , mask , flags ) , \
. access = SNDRV_CTL_ELEM_ACCESS_READWRITE | \
SNDRV_CTL_ELEM_ACCESS_INACTIVE , \
. info = wm8776_field_enum_info , \
. get = wm8776_field_enum_get , \
. put = wm8776_field_enum_put , \
}
# define WM8776_FIELD_CTL_VOLUME(a, b, c, d, e, f, g, h, tlv_p) { \
_WM8776_FIELD_CTL ( a " Capture Volume " , b , c , d , e , f , g , h ) , \
. access = SNDRV_CTL_ELEM_ACCESS_READWRITE | \
SNDRV_CTL_ELEM_ACCESS_INACTIVE | \
SNDRV_CTL_ELEM_ACCESS_TLV_READ , \
. info = wm8776_field_volume_info , \
. get = wm8776_field_volume_get , \
. put = wm8776_field_volume_put , \
. tlv = { . p = tlv_p } , \
}
static const DECLARE_TLV_DB_SCALE ( wm87x6_dac_db_scale , - 6000 , 50 , 0 ) ;
static const DECLARE_TLV_DB_SCALE ( wm8776_adc_db_scale , - 2100 , 50 , 0 ) ;
static const DECLARE_TLV_DB_SCALE ( wm8776_hp_db_scale , - 6000 , 100 , 0 ) ;
static const DECLARE_TLV_DB_SCALE ( wm8776_lct_db_scale , - 1600 , 100 , 0 ) ;
static const DECLARE_TLV_DB_SCALE ( wm8776_maxgain_db_scale , 0 , 400 , 0 ) ;
static const DECLARE_TLV_DB_SCALE ( wm8776_ngth_db_scale , - 7800 , 600 , 0 ) ;
static const DECLARE_TLV_DB_SCALE ( wm8776_maxatten_lim_db_scale , - 1200 , 100 , 0 ) ;
static const DECLARE_TLV_DB_SCALE ( wm8776_maxatten_alc_db_scale , - 2100 , 400 , 0 ) ;
static const struct snd_kcontrol_new ds_controls [ ] = {
{
. iface = SNDRV_CTL_ELEM_IFACE_MIXER ,
. name = " Headphone Playback Volume " ,
. info = wm8776_hp_vol_info ,
. get = wm8776_hp_vol_get ,
. put = wm8776_hp_vol_put ,
. tlv = { . p = wm8776_hp_db_scale } ,
} ,
WM8776_BIT_SWITCH ( " Headphone Playback Switch " ,
WM8776_PWRDOWN , WM8776_HPPD , 1 , 0 ) ,
{
. iface = SNDRV_CTL_ELEM_IFACE_MIXER ,
. name = " Input Capture Volume " ,
. info = wm8776_input_vol_info ,
. get = wm8776_input_vol_get ,
. put = wm8776_input_vol_put ,
. tlv = { . p = wm8776_adc_db_scale } ,
} ,
{
. iface = SNDRV_CTL_ELEM_IFACE_MIXER ,
. name = " Line Capture Switch " ,
. info = snd_ctl_boolean_mono_info ,
. get = wm8776_input_mux_get ,
. put = wm8776_input_mux_put ,
. private_value = 1 < < 0 ,
} ,
{
. iface = SNDRV_CTL_ELEM_IFACE_MIXER ,
. name = " Mic Capture Switch " ,
. info = snd_ctl_boolean_mono_info ,
. get = wm8776_input_mux_get ,
. put = wm8776_input_mux_put ,
. private_value = 1 < < 1 ,
} ,
2010-09-09 12:19:21 +02:00
WM8776_BIT_SWITCH ( " Front Mic Capture Switch " ,
WM8776_ADCMUX , 1 < < 2 , 0 , 0 ) ,
WM8776_BIT_SWITCH ( " Aux Capture Switch " ,
WM8776_ADCMUX , 1 < < 3 , 0 , 0 ) ,
2010-01-18 15:44:04 +01:00
{
. iface = SNDRV_CTL_ELEM_IFACE_MIXER ,
. name = " ADC Filter Capture Enum " ,
. info = hpf_info ,
. get = hpf_get ,
. put = hpf_put ,
} ,
{
. iface = SNDRV_CTL_ELEM_IFACE_MIXER ,
. name = " Level Control Capture Enum " ,
. info = wm8776_level_control_info ,
. get = wm8776_level_control_get ,
. put = wm8776_level_control_put ,
. private_value = 0 ,
} ,
} ;
static const struct snd_kcontrol_new lc_controls [ ] = {
WM8776_FIELD_CTL_VOLUME ( " Limiter Threshold " ,
WM8776_ALCCTRL1 , 0 , 11 , 0 , 15 , 0xf ,
LC_CONTROL_LIMITER , wm8776_lct_db_scale ) ,
WM8776_FIELD_CTL_ENUM ( " Limiter Attack Time " ,
WM8776_ALCCTRL3 , 0 , 2 , 0 , 10 , 0xf ,
LC_CONTROL_LIMITER ) ,
WM8776_FIELD_CTL_ENUM ( " Limiter Decay Time " ,
WM8776_ALCCTRL3 , 4 , 3 , 0 , 10 , 0xf ,
LC_CONTROL_LIMITER ) ,
WM8776_FIELD_CTL_ENUM ( " Limiter Transient Window " ,
WM8776_LIMITER , 4 , 2 , 0 , 7 , 0x7 ,
LC_CONTROL_LIMITER ) ,
WM8776_FIELD_CTL_VOLUME ( " Limiter Maximum Attenuation " ,
WM8776_LIMITER , 0 , 6 , 3 , 12 , 0xf ,
LC_CONTROL_LIMITER ,
wm8776_maxatten_lim_db_scale ) ,
WM8776_FIELD_CTL_VOLUME ( " ALC Target Level " ,
WM8776_ALCCTRL1 , 0 , 11 , 0 , 15 , 0xf ,
LC_CONTROL_ALC , wm8776_lct_db_scale ) ,
WM8776_FIELD_CTL_ENUM ( " ALC Attack Time " ,
WM8776_ALCCTRL3 , 0 , 2 , 0 , 10 , 0xf ,
LC_CONTROL_ALC ) ,
WM8776_FIELD_CTL_ENUM ( " ALC Decay Time " ,
WM8776_ALCCTRL3 , 4 , 3 , 0 , 10 , 0xf ,
LC_CONTROL_ALC ) ,
WM8776_FIELD_CTL_VOLUME ( " ALC Maximum Gain " ,
WM8776_ALCCTRL1 , 4 , 7 , 1 , 7 , 0x7 ,
LC_CONTROL_ALC , wm8776_maxgain_db_scale ) ,
WM8776_FIELD_CTL_VOLUME ( " ALC Maximum Attenuation " ,
WM8776_LIMITER , 0 , 10 , 10 , 15 , 0xf ,
LC_CONTROL_ALC , wm8776_maxatten_alc_db_scale ) ,
WM8776_FIELD_CTL_ENUM ( " ALC Hold Time " ,
WM8776_ALCCTRL2 , 0 , 0 , 0 , 15 , 0xf ,
LC_CONTROL_ALC ) ,
WM8776_BIT_SWITCH ( " Noise Gate Capture Switch " ,
WM8776_NOISEGATE , WM8776_NGAT , 0 ,
LC_CONTROL_ALC ) ,
WM8776_FIELD_CTL_VOLUME ( " Noise Gate Threshold " ,
WM8776_NOISEGATE , 2 , 0 , 0 , 7 , 0x7 ,
LC_CONTROL_ALC , wm8776_ngth_db_scale ) ,
} ;
static int xonar_ds_mixer_init ( struct oxygen * chip )
{
struct xonar_wm87x6 * data = chip - > model_data ;
unsigned int i ;
struct snd_kcontrol * ctl ;
int err ;
for ( i = 0 ; i < ARRAY_SIZE ( ds_controls ) ; + + i ) {
ctl = snd_ctl_new1 ( & ds_controls [ i ] , chip ) ;
if ( ! ctl )
return - ENOMEM ;
err = snd_ctl_add ( chip - > card , ctl ) ;
if ( err < 0 )
return err ;
2010-09-07 13:38:49 +02:00
if ( ! strcmp ( ctl - > id . name , " Line Capture Switch " ) )
data - > line_adcmux_control = ctl ;
else if ( ! strcmp ( ctl - > id . name , " Mic Capture Switch " ) )
data - > mic_adcmux_control = ctl ;
2010-01-18 15:44:04 +01:00
}
2010-09-07 13:38:49 +02:00
if ( ! data - > line_adcmux_control | | ! data - > mic_adcmux_control )
return - ENXIO ;
2010-01-18 15:44:04 +01:00
BUILD_BUG_ON ( ARRAY_SIZE ( lc_controls ) ! = ARRAY_SIZE ( data - > lc_controls ) ) ;
for ( i = 0 ; i < ARRAY_SIZE ( lc_controls ) ; + + i ) {
ctl = snd_ctl_new1 ( & lc_controls [ i ] , chip ) ;
if ( ! ctl )
return - ENOMEM ;
err = snd_ctl_add ( chip - > card , ctl ) ;
if ( err < 0 )
return err ;
data - > lc_controls [ i ] = ctl ;
}
return 0 ;
}
static const struct oxygen_model model_xonar_ds = {
. shortname = " Xonar DS " ,
2010-10-04 13:17:26 +02:00
. longname = " Asus Virtuoso 66 " ,
2010-01-18 15:44:04 +01:00
. chip = " AV200 " ,
. init = xonar_ds_init ,
. mixer_init = xonar_ds_mixer_init ,
. cleanup = xonar_ds_cleanup ,
. suspend = xonar_ds_suspend ,
. resume = xonar_ds_resume ,
. pcm_hardware_filter = wm8776_adc_hardware_filter ,
. get_i2s_mclk = oxygen_default_i2s_mclk ,
. set_dac_params = set_wm87x6_dac_params ,
. set_adc_params = set_wm8776_adc_params ,
. update_dac_volume = update_wm87x6_volume ,
. update_dac_mute = update_wm87x6_mute ,
2010-09-09 12:24:35 +02:00
. update_center_lfe_mix = update_wm8766_center_lfe_mix ,
2010-01-18 15:44:04 +01:00
. gpio_changed = xonar_ds_gpio_changed ,
. dac_tlv = wm87x6_dac_db_scale ,
. model_data_size = sizeof ( struct xonar_wm87x6 ) ,
. device_config = PLAYBACK_0_TO_I2S |
PLAYBACK_1_TO_SPDIF |
CAPTURE_0_FROM_I2S_1 ,
. dac_channels = 8 ,
. dac_volume_min = 255 - 2 * 60 ,
. dac_volume_max = 255 ,
. function_flags = OXYGEN_FUNCTION_SPI ,
. dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST ,
. adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST ,
} ;
int __devinit get_xonar_wm87x6_model ( struct oxygen * chip ,
const struct pci_device_id * id )
{
switch ( id - > subdevice ) {
case 0x838e :
chip - > model = model_xonar_ds ;
break ;
default :
return - EINVAL ;
}
return 0 ;
}