2006-11-29 17:29:40 +03:00
/*
* Universal Interface for Intel High Definition Audio Codec
*
2009-10-10 15:08:52 +04:00
* HD audio interface patch for VIA VT17xx / VT18xx / VT20xx codec
2006-11-29 17:29:40 +03:00
*
2009-10-10 15:08:52 +04:00
* ( C ) 2006 - 2009 VIA Technology , Inc .
* ( C ) 2006 - 2008 Takashi Iwai < tiwai @ suse . de >
2006-11-29 17:29:40 +03:00
*
* This driver is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 2 of the License , or
* ( at your option ) any later version .
*
* 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 program ; if not , write to the Free Software
* Foundation , Inc . , 59 Temple Place , Suite 330 , Boston , MA 02111 - 1307 USA
*/
/* * * * * * * * * * * * * * Release History * * * * * * * * * * * * * * * * */
2009-10-10 15:08:55 +04:00
/* */
2006-11-29 17:29:40 +03:00
/* 2006-03-03 Lydia Wang Create the basic patch to support VT1708 codec */
2009-10-10 15:08:55 +04:00
/* 2006-03-14 Lydia Wang Modify hard code for some pin widget nid */
/* 2006-08-02 Lydia Wang Add support to VT1709 codec */
2006-11-29 17:29:40 +03:00
/* 2006-09-08 Lydia Wang Fix internal loopback recording source select bug */
2009-10-10 15:08:55 +04:00
/* 2007-09-12 Lydia Wang Add EAPD enable during driver initialization */
/* 2007-09-17 Lydia Wang Add VT1708B codec support */
2008-09-09 11:50:37 +04:00
/* 2007-11-14 Lydia Wang Add VT1708A codec HP and CD pin connect config */
2008-09-09 11:53:36 +04:00
/* 2008-02-03 Lydia Wang Fix Rear channels and Back channels inverse issue */
2009-10-10 15:08:55 +04:00
/* 2008-03-06 Lydia Wang Add VT1702 codec and VT1708S codec support */
/* 2008-04-09 Lydia Wang Add mute front speaker when HP plugin */
/* 2008-04-09 Lydia Wang Add Independent HP feature */
2008-09-09 12:02:09 +04:00
/* 2008-05-28 Lydia Wang Add second S/PDIF Out support for VT1702 */
2009-10-10 15:08:55 +04:00
/* 2008-09-15 Logan Li Add VT1708S Mic Boost workaround/backdoor */
2009-10-10 15:08:52 +04:00
/* 2009-02-16 Logan Li Add support for VT1718S */
/* 2009-03-13 Logan Li Add support for VT1716S */
/* 2009-04-14 Lydai Wang Add support for VT1828S and VT2020 */
/* 2009-07-08 Lydia Wang Add support for VT2002P */
/* 2009-07-21 Lydia Wang Add support for VT1812 */
2009-10-20 09:18:04 +04:00
/* 2009-09-19 Lydia Wang Add support for VT1818S */
2009-10-10 15:08:55 +04:00
/* */
2006-11-29 17:29:40 +03:00
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
# include <linux/init.h>
# include <linux/delay.h>
# include <linux/slab.h>
2011-07-15 20:38:28 +04:00
# include <linux/module.h>
2006-11-29 17:29:40 +03:00
# include <sound/core.h>
2008-09-09 11:58:27 +04:00
# include <sound/asoundef.h>
2006-11-29 17:29:40 +03:00
# include "hda_codec.h"
# include "hda_local.h"
2012-05-07 19:42:31 +04:00
# include "hda_auto_parser.h"
2011-10-28 00:12:46 +04:00
# include "hda_jack.h"
2013-01-07 15:27:36 +04:00
# include "hda_generic.h"
2006-11-29 17:29:40 +03:00
/* Pin Widget NID */
2008-09-09 11:56:01 +04:00
# define VT1708_HP_PIN_NID 0x20
# define VT1708_CD_PIN_NID 0x24
2006-11-29 17:29:40 +03:00
2008-09-15 18:43:23 +04:00
enum VIA_HDA_CODEC {
UNKNOWN = - 1 ,
VT1708 ,
VT1709_10CH ,
VT1709_6CH ,
VT1708B_8CH ,
VT1708B_4CH ,
VT1708S ,
2009-10-10 15:07:29 +04:00
VT1708BCE ,
2008-09-15 18:43:23 +04:00
VT1702 ,
2009-10-10 15:08:34 +04:00
VT1718S ,
2009-10-10 15:08:41 +04:00
VT1716S ,
2009-10-10 15:08:43 +04:00
VT2002P ,
2009-10-10 15:08:46 +04:00
VT1812 ,
2011-03-23 12:57:34 +03:00
VT1802 ,
2012-12-10 14:28:11 +04:00
VT1705CF ,
2012-12-10 15:03:26 +04:00
VT1808 ,
2008-09-15 18:43:23 +04:00
CODEC_TYPES ,
} ;
2011-03-23 12:57:34 +03:00
# define VT2002P_COMPATIBLE(spec) \
( ( spec ) - > codec_type = = VT2002P | | \
( spec ) - > codec_type = = VT1812 | | \
( spec ) - > codec_type = = VT1802 )
2009-10-10 15:08:17 +04:00
struct via_spec {
2013-01-07 15:27:36 +04:00
struct hda_gen_spec gen ;
2009-10-10 15:08:17 +04:00
/* codec parameterization */
2011-05-02 14:38:19 +04:00
const struct snd_kcontrol_new * mixers [ 6 ] ;
2009-10-10 15:08:17 +04:00
unsigned int num_mixers ;
2011-05-02 14:38:19 +04:00
const struct hda_verb * init_verbs [ 5 ] ;
2009-10-10 15:08:17 +04:00
unsigned int num_iverbs ;
/* HP mode source */
2009-10-10 15:08:41 +04:00
unsigned int dmic_enabled ;
2009-10-10 15:08:17 +04:00
enum VIA_HDA_CODEC codec_type ;
2012-02-01 13:33:23 +04:00
/* analog low-power control */
bool alc_mode ;
2009-10-10 15:08:17 +04:00
/* work to check hp jack state */
2011-11-24 19:33:09 +04:00
int hp_work_active ;
2011-06-17 17:46:13 +04:00
int vt1708_jack_detect ;
ALSA: hda/via - Add beep controls to VIA codecs
My codec has a beep-generating node:
$ cat /proc/asound/card1/codec#0
Codec: VIA VT1802
...
Vendor Id: 0x11068446
Subsystem Id: 0x15587410
Revision Id: 0x100000
...
Node 0x22 [Beep Generator Widget] wcaps 0x70040c: Mono Amp-Out
Amp-Out caps: ofs=0x0a, nsteps=0x12, stepsize=0x05, mute=1
Amp-Out vals: [0x0a]
Power states: D0 D1 D2 D3
Power: setting=D0, actual=D0
...
But I was missing the:
Control: name=...
entries that I need to manage this widget from alsamixer. With this
patch (based on the similar Mono Amp-Out handling in
patch_conexant.c), I get a new:
input: HDA Digital PCBeep as /devices/pci0000:00/0000:00:1b.0/sound/card1/hdaudioC1D0/input15
entry in dmesg and controls to manage that beep:
$ cat /proc/asound/card1/codec#0 | grep -A5 Beep
Node 0x22 [Beep Generator Widget] wcaps 0x70040c: Mono Amp-Out
Control: name="Beep Playback Volume", index=0, device=0
ControlAmp: chs=1, dir=Out, idx=0, ofs=0
Control: name="Beep Playback Switch", index=0, device=0
ControlAmp: chs=1, dir=Out, idx=0, ofs=0
Amp-Out caps: ofs=0x0a, nsteps=0x12, stepsize=0x05, mute=1
Amp-Out vals: [0x12]
Power states: D0 D1 D2 D3
Power: setting=D0, actual=D0
[rebased and modified for the latest tree by tiwai]
Signed-off-by: W. Trevor King <wking@tremily.us>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
2015-03-25 10:43:42 +03:00
unsigned int beep_amp ;
2009-10-10 15:08:17 +04:00
} ;
2011-03-22 11:25:03 +03:00
static enum VIA_HDA_CODEC get_codec_type ( struct hda_codec * codec ) ;
2013-01-07 15:27:36 +04:00
static void via_playback_pcm_hook ( struct hda_pcm_stream * hinfo ,
struct hda_codec * codec ,
struct snd_pcm_substream * substream ,
int action ) ;
2015-05-29 11:42:14 +03:00
static const struct hda_codec_ops via_patch_ops ; /* defined below */
2013-01-07 15:27:36 +04:00
static struct via_spec * via_new_spec ( struct hda_codec * codec )
2009-12-08 18:13:32 +03:00
{
struct via_spec * spec ;
spec = kzalloc ( sizeof ( * spec ) , GFP_KERNEL ) ;
if ( spec = = NULL )
return NULL ;
codec - > spec = spec ;
2013-01-07 15:27:36 +04:00
snd_hda_gen_spec_init ( & spec - > gen ) ;
2011-03-22 11:25:03 +03:00
spec - > codec_type = get_codec_type ( codec ) ;
/* VT1708BCE & VT1708S are almost same */
if ( spec - > codec_type = = VT1708BCE )
spec - > codec_type = VT1708S ;
2013-01-21 18:15:55 +04:00
spec - > gen . indep_hp = 1 ;
2013-05-31 21:55:54 +04:00
spec - > gen . keep_eapd_on = 1 ;
2013-01-07 15:27:36 +04:00
spec - > gen . pcm_playback_hook = via_playback_pcm_hook ;
2014-12-15 15:43:59 +03:00
spec - > gen . add_stereo_mix_input = HDA_HINT_STEREO_MIX_AUTO ;
2015-03-20 20:21:03 +03:00
codec - > power_save_node = 1 ;
2015-03-17 17:56:05 +03:00
spec - > gen . power_down_unused = 1 ;
2015-05-29 11:42:14 +03:00
codec - > patch_ops = via_patch_ops ;
2009-12-08 18:13:32 +03:00
return spec ;
}
2009-10-10 15:07:26 +04:00
static enum VIA_HDA_CODEC get_codec_type ( struct hda_codec * codec )
2008-09-15 18:43:23 +04:00
{
2015-03-03 12:07:24 +03:00
u32 vendor_id = codec - > core . vendor_id ;
2008-09-15 18:43:23 +04:00
u16 ven_id = vendor_id > > 16 ;
u16 dev_id = vendor_id & 0xffff ;
enum VIA_HDA_CODEC codec_type ;
/* get codec type */
if ( ven_id ! = 0x1106 )
codec_type = UNKNOWN ;
else if ( dev_id > = 0x1708 & & dev_id < = 0x170b )
codec_type = VT1708 ;
else if ( dev_id > = 0xe710 & & dev_id < = 0xe713 )
codec_type = VT1709_10CH ;
else if ( dev_id > = 0xe714 & & dev_id < = 0xe717 )
codec_type = VT1709_6CH ;
2009-10-10 15:07:29 +04:00
else if ( dev_id > = 0xe720 & & dev_id < = 0xe723 ) {
2008-09-15 18:43:23 +04:00
codec_type = VT1708B_8CH ;
2009-10-10 15:07:29 +04:00
if ( snd_hda_param_read ( codec , 0x16 , AC_PAR_CONNLIST_LEN ) = = 0x7 )
codec_type = VT1708BCE ;
} else if ( dev_id > = 0xe724 & & dev_id < = 0xe727 )
2008-09-15 18:43:23 +04:00
codec_type = VT1708B_4CH ;
else if ( ( dev_id & 0xfff ) = = 0x397
& & ( dev_id > > 12 ) < 8 )
codec_type = VT1708S ;
else if ( ( dev_id & 0xfff ) = = 0x398
& & ( dev_id > > 12 ) < 8 )
codec_type = VT1702 ;
2009-10-10 15:08:34 +04:00
else if ( ( dev_id & 0xfff ) = = 0x428
& & ( dev_id > > 12 ) < 8 )
codec_type = VT1718S ;
2009-10-10 15:08:41 +04:00
else if ( dev_id = = 0x0433 | | dev_id = = 0xa721 )
codec_type = VT1716S ;
2009-10-10 15:08:39 +04:00
else if ( dev_id = = 0x0441 | | dev_id = = 0x4441 )
codec_type = VT1718S ;
2009-10-10 15:08:43 +04:00
else if ( dev_id = = 0x0438 | | dev_id = = 0x4438 )
codec_type = VT2002P ;
2009-10-10 15:08:46 +04:00
else if ( dev_id = = 0x0448 )
codec_type = VT1812 ;
2009-10-20 09:18:04 +04:00
else if ( dev_id = = 0x0440 )
codec_type = VT1708S ;
2011-03-23 12:57:34 +03:00
else if ( ( dev_id & 0xfff ) = = 0x446 )
codec_type = VT1802 ;
2012-12-10 14:28:11 +04:00
else if ( dev_id = = 0x4760 )
codec_type = VT1705CF ;
2012-12-10 15:03:26 +04:00
else if ( dev_id = = 0x4761 | | dev_id = = 0x4762 )
codec_type = VT1808 ;
2008-09-15 18:43:23 +04:00
else
codec_type = UNKNOWN ;
return codec_type ;
} ;
2011-06-20 17:40:19 +04:00
static void analog_low_current_mode ( struct hda_codec * codec ) ;
static bool is_aa_path_mute ( struct hda_codec * codec ) ;
2009-10-10 15:08:17 +04:00
2011-11-24 19:33:09 +04:00
# define hp_detect_with_aa(codec) \
( snd_hda_get_bool_hint ( codec , " analog_loopback_hp_detect " ) = = 1 & & \
! is_aa_path_mute ( codec ) )
2013-01-07 15:27:36 +04:00
static void vt1708_stop_hp_work ( struct hda_codec * codec )
2009-10-10 15:08:17 +04:00
{
2013-01-07 15:27:36 +04:00
struct via_spec * spec = codec - > spec ;
if ( spec - > codec_type ! = VT1708 | | ! spec - > gen . autocfg . hp_outs )
2009-10-10 15:08:17 +04:00
return ;
2011-11-24 19:33:09 +04:00
if ( spec - > hp_work_active ) {
2013-01-07 15:27:36 +04:00
snd_hda_codec_write ( codec , 0x1 , 0 , 0xf81 , 1 ) ;
2013-07-26 07:34:44 +04:00
codec - > jackpoll_interval = 0 ;
2013-01-07 15:27:36 +04:00
cancel_delayed_work_sync ( & codec - > jackpoll_work ) ;
spec - > hp_work_active = false ;
2011-11-24 19:33:09 +04:00
}
2009-10-10 15:08:17 +04:00
}
2013-01-07 15:27:36 +04:00
static void vt1708_update_hp_work ( struct hda_codec * codec )
2009-10-10 15:08:17 +04:00
{
2013-01-07 15:27:36 +04:00
struct via_spec * spec = codec - > spec ;
if ( spec - > codec_type ! = VT1708 | | ! spec - > gen . autocfg . hp_outs )
2009-10-10 15:08:17 +04:00
return ;
2013-02-05 15:06:03 +04:00
if ( spec - > vt1708_jack_detect ) {
2011-11-24 19:33:09 +04:00
if ( ! spec - > hp_work_active ) {
2013-01-07 15:27:36 +04:00
codec - > jackpoll_interval = msecs_to_jiffies ( 100 ) ;
snd_hda_codec_write ( codec , 0x1 , 0 , 0xf81 , 0 ) ;
2015-02-28 00:43:26 +03:00
schedule_delayed_work ( & codec - > jackpoll_work , 0 ) ;
2013-01-07 15:27:36 +04:00
spec - > hp_work_active = true ;
2011-11-24 19:33:09 +04:00
}
2013-01-07 15:27:36 +04:00
} else if ( ! hp_detect_with_aa ( codec ) )
vt1708_stop_hp_work ( codec ) ;
2009-10-10 15:08:17 +04:00
}
2009-10-10 15:07:35 +04:00
2011-06-17 18:59:21 +04:00
static int via_pin_power_ctl_info ( struct snd_kcontrol * kcontrol ,
struct snd_ctl_elem_info * uinfo )
{
2012-11-30 21:34:38 +04:00
return snd_hda_enum_bool_helper_info ( kcontrol , uinfo ) ;
2011-06-17 18:59:21 +04:00
}
static int via_pin_power_ctl_get ( struct snd_kcontrol * kcontrol ,
struct snd_ctl_elem_value * ucontrol )
{
struct hda_codec * codec = snd_kcontrol_chip ( kcontrol ) ;
2015-06-25 09:48:54 +03:00
struct via_spec * spec = codec - > spec ;
ucontrol - > value . enumerated . item [ 0 ] = spec - > gen . power_down_unused ;
2011-06-17 18:59:21 +04:00
return 0 ;
}
static int via_pin_power_ctl_put ( struct snd_kcontrol * kcontrol ,
struct snd_ctl_elem_value * ucontrol )
{
struct hda_codec * codec = snd_kcontrol_chip ( kcontrol ) ;
struct via_spec * spec = codec - > spec ;
2015-03-17 17:56:05 +03:00
bool val = ! ! ucontrol - > value . enumerated . item [ 0 ] ;
2011-06-17 18:59:21 +04:00
2015-06-25 09:48:54 +03:00
if ( val = = spec - > gen . power_down_unused )
2011-06-17 18:59:21 +04:00
return 0 ;
2015-06-25 09:48:54 +03:00
/* codec->power_save_node = val; */ /* widget PM seems yet broken */
2015-03-17 17:56:05 +03:00
spec - > gen . power_down_unused = val ;
2012-02-01 13:33:23 +04:00
analog_low_current_mode ( codec ) ;
2011-06-17 18:59:21 +04:00
return 1 ;
}
2013-01-07 15:27:36 +04:00
static const struct snd_kcontrol_new via_pin_power_ctl_enum [ ] = {
{
2011-06-17 18:59:21 +04:00
. iface = SNDRV_CTL_ELEM_IFACE_MIXER ,
. name = " Dynamic Power-Control " ,
. info = via_pin_power_ctl_info ,
. get = via_pin_power_ctl_get ,
. put = via_pin_power_ctl_put ,
2013-01-07 15:27:36 +04:00
} ,
{ } /* terminator */
2011-06-17 18:59:21 +04:00
} ;
ALSA: hda/via - Add beep controls to VIA codecs
My codec has a beep-generating node:
$ cat /proc/asound/card1/codec#0
Codec: VIA VT1802
...
Vendor Id: 0x11068446
Subsystem Id: 0x15587410
Revision Id: 0x100000
...
Node 0x22 [Beep Generator Widget] wcaps 0x70040c: Mono Amp-Out
Amp-Out caps: ofs=0x0a, nsteps=0x12, stepsize=0x05, mute=1
Amp-Out vals: [0x0a]
Power states: D0 D1 D2 D3
Power: setting=D0, actual=D0
...
But I was missing the:
Control: name=...
entries that I need to manage this widget from alsamixer. With this
patch (based on the similar Mono Amp-Out handling in
patch_conexant.c), I get a new:
input: HDA Digital PCBeep as /devices/pci0000:00/0000:00:1b.0/sound/card1/hdaudioC1D0/input15
entry in dmesg and controls to manage that beep:
$ cat /proc/asound/card1/codec#0 | grep -A5 Beep
Node 0x22 [Beep Generator Widget] wcaps 0x70040c: Mono Amp-Out
Control: name="Beep Playback Volume", index=0, device=0
ControlAmp: chs=1, dir=Out, idx=0, ofs=0
Control: name="Beep Playback Switch", index=0, device=0
ControlAmp: chs=1, dir=Out, idx=0, ofs=0
Amp-Out caps: ofs=0x0a, nsteps=0x12, stepsize=0x05, mute=1
Amp-Out vals: [0x12]
Power states: D0 D1 D2 D3
Power: setting=D0, actual=D0
[rebased and modified for the latest tree by tiwai]
Signed-off-by: W. Trevor King <wking@tremily.us>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
2015-03-25 10:43:42 +03:00
# ifdef CONFIG_SND_HDA_INPUT_BEEP
static inline void set_beep_amp ( struct via_spec * spec , hda_nid_t nid ,
int idx , int dir )
{
spec - > gen . beep_nid = nid ;
spec - > beep_amp = HDA_COMPOSE_AMP_VAL ( nid , 1 , idx , dir ) ;
}
/* additional beep mixers; the actual parameters are overwritten at build */
static const struct snd_kcontrol_new cxt_beep_mixer [ ] = {
HDA_CODEC_VOLUME_MONO ( " Beep Playback Volume " , 0 , 1 , 0 , HDA_OUTPUT ) ,
HDA_CODEC_MUTE_BEEP_MONO ( " Beep Playback Switch " , 0 , 1 , 0 , HDA_OUTPUT ) ,
{ } /* end */
} ;
/* create beep controls if needed */
static int add_beep_ctls ( struct hda_codec * codec )
{
struct via_spec * spec = codec - > spec ;
int err ;
if ( spec - > beep_amp ) {
const struct snd_kcontrol_new * knew ;
for ( knew = cxt_beep_mixer ; knew - > name ; knew + + ) {
struct snd_kcontrol * kctl ;
kctl = snd_ctl_new1 ( knew , codec ) ;
if ( ! kctl )
return - ENOMEM ;
kctl - > private_value = spec - > beep_amp ;
err = snd_hda_ctl_add ( codec , 0 , kctl ) ;
if ( err < 0 )
return err ;
}
}
return 0 ;
}
static void auto_parse_beep ( struct hda_codec * codec )
{
struct via_spec * spec = codec - > spec ;
hda_nid_t nid ;
for_each_hda_codec_node ( nid , codec )
if ( get_wcaps_type ( get_wcaps ( codec , nid ) ) = = AC_WID_BEEP ) {
set_beep_amp ( spec , nid , 0 , HDA_OUTPUT ) ;
break ;
}
}
# else
# define set_beep_amp(spec, nid, idx, dir) /* NOP */
# define add_beep_ctls(codec) 0
# define auto_parse_beep(codec)
# endif
2011-06-17 18:59:21 +04:00
2013-01-07 15:27:36 +04:00
/* check AA path's mute status */
static bool is_aa_path_mute ( struct hda_codec * codec )
2008-09-09 11:58:27 +04:00
{
2009-10-10 15:07:47 +04:00
struct via_spec * spec = codec - > spec ;
2013-01-07 15:27:36 +04:00
const struct hda_amp_list * p ;
2013-02-07 13:45:11 +04:00
int ch , v ;
2009-10-10 15:07:47 +04:00
2013-02-07 13:45:11 +04:00
p = spec - > gen . loopback . amplist ;
if ( ! p )
return true ;
for ( ; p - > nid ; p + + ) {
2013-01-07 15:27:36 +04:00
for ( ch = 0 ; ch < 2 ; ch + + ) {
v = snd_hda_codec_amp_read ( codec , p - > nid , ch , p - > dir ,
p - > idx ) ;
if ( ! ( v & HDA_AMP_MUTE ) & & v > 0 )
return false ;
}
2011-07-18 18:54:40 +04:00
}
2013-01-07 15:27:36 +04:00
return true ;
2011-07-18 18:54:40 +04:00
}
2013-01-07 15:27:36 +04:00
/* enter/exit analog low-current mode */
static void __analog_low_current_mode ( struct hda_codec * codec , bool force )
2011-07-18 18:54:40 +04:00
{
struct via_spec * spec = codec - > spec ;
2013-01-07 15:27:36 +04:00
bool enable ;
unsigned int verb , parm ;
2011-07-18 18:54:40 +04:00
2015-03-20 20:21:03 +03:00
if ( ! codec - > power_save_node )
2013-01-07 15:27:36 +04:00
enable = false ;
else
enable = is_aa_path_mute ( codec ) & & ! spec - > gen . active_streams ;
if ( enable = = spec - > alc_mode & & ! force )
2011-07-18 18:54:40 +04:00
return ;
2013-01-07 15:27:36 +04:00
spec - > alc_mode = enable ;
2011-07-18 18:54:40 +04:00
2013-01-07 15:27:36 +04:00
/* decide low current mode's verb & parameter */
switch ( spec - > codec_type ) {
case VT1708B_8CH :
case VT1708B_4CH :
verb = 0xf70 ;
parm = enable ? 0x02 : 0x00 ; /* 0x02: 2/3x, 0x00: 1x */
break ;
case VT1708S :
case VT1718S :
case VT1716S :
verb = 0xf73 ;
parm = enable ? 0x51 : 0xe1 ; /* 0x51: 4/28x, 0xe1: 1x */
break ;
case VT1702 :
verb = 0xf73 ;
parm = enable ? 0x01 : 0x1d ; /* 0x01: 4/40x, 0x1d: 1x */
break ;
case VT2002P :
case VT1812 :
case VT1802 :
verb = 0xf93 ;
parm = enable ? 0x00 : 0xe0 ; /* 0x00: 4/40x, 0xe0: 1x */
break ;
case VT1705CF :
case VT1808 :
verb = 0xf82 ;
parm = enable ? 0x00 : 0xe0 ; /* 0x00: 4/40x, 0xe0: 1x */
break ;
default :
return ; /* other codecs are not supported */
2009-10-10 15:07:35 +04:00
}
/* send verb */
2015-03-03 12:07:24 +03:00
snd_hda_codec_write ( codec , codec - > core . afg , 0 , verb , parm ) ;
2011-06-22 17:23:25 +04:00
}
2013-01-07 15:27:36 +04:00
static void analog_low_current_mode ( struct hda_codec * codec )
{
return __analog_low_current_mode ( codec , false ) ;
}
2011-06-22 17:23:25 +04:00
2013-01-07 15:27:36 +04:00
static int via_build_controls ( struct hda_codec * codec )
2011-06-22 17:23:25 +04:00
{
struct via_spec * spec = codec - > spec ;
2013-01-07 15:27:36 +04:00
int err , i ;
2011-06-17 11:01:29 +04:00
2013-01-07 15:27:36 +04:00
err = snd_hda_gen_build_controls ( codec ) ;
2011-06-17 11:01:29 +04:00
if ( err < 0 )
return err ;
2006-11-29 17:29:40 +03:00
ALSA: hda/via - Add beep controls to VIA codecs
My codec has a beep-generating node:
$ cat /proc/asound/card1/codec#0
Codec: VIA VT1802
...
Vendor Id: 0x11068446
Subsystem Id: 0x15587410
Revision Id: 0x100000
...
Node 0x22 [Beep Generator Widget] wcaps 0x70040c: Mono Amp-Out
Amp-Out caps: ofs=0x0a, nsteps=0x12, stepsize=0x05, mute=1
Amp-Out vals: [0x0a]
Power states: D0 D1 D2 D3
Power: setting=D0, actual=D0
...
But I was missing the:
Control: name=...
entries that I need to manage this widget from alsamixer. With this
patch (based on the similar Mono Amp-Out handling in
patch_conexant.c), I get a new:
input: HDA Digital PCBeep as /devices/pci0000:00/0000:00:1b.0/sound/card1/hdaudioC1D0/input15
entry in dmesg and controls to manage that beep:
$ cat /proc/asound/card1/codec#0 | grep -A5 Beep
Node 0x22 [Beep Generator Widget] wcaps 0x70040c: Mono Amp-Out
Control: name="Beep Playback Volume", index=0, device=0
ControlAmp: chs=1, dir=Out, idx=0, ofs=0
Control: name="Beep Playback Switch", index=0, device=0
ControlAmp: chs=1, dir=Out, idx=0, ofs=0
Amp-Out caps: ofs=0x0a, nsteps=0x12, stepsize=0x05, mute=1
Amp-Out vals: [0x12]
Power states: D0 D1 D2 D3
Power: setting=D0, actual=D0
[rebased and modified for the latest tree by tiwai]
Signed-off-by: W. Trevor King <wking@tremily.us>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
2015-03-25 10:43:42 +03:00
err = add_beep_ctls ( codec ) ;
if ( err < 0 )
return err ;
2015-03-17 17:56:05 +03:00
spec - > mixers [ spec - > num_mixers + + ] = via_pin_power_ctl_enum ;
2006-11-29 17:29:40 +03:00
2013-01-07 15:27:36 +04:00
for ( i = 0 ; i < spec - > num_mixers ; i + + ) {
err = snd_hda_add_new_ctls ( codec , spec - > mixers [ i ] ) ;
if ( err < 0 )
return err ;
}
2011-06-22 17:23:25 +04:00
return 0 ;
}
2013-01-07 15:27:36 +04:00
static void via_playback_pcm_hook ( struct hda_pcm_stream * hinfo ,
struct hda_codec * codec ,
struct snd_pcm_substream * substream ,
int action )
2011-06-22 17:23:25 +04:00
{
2013-01-07 15:27:36 +04:00
analog_low_current_mode ( codec ) ;
vt1708_update_hp_work ( codec ) ;
2011-06-22 17:23:25 +04:00
}
2013-01-07 15:27:36 +04:00
static void via_free ( struct hda_codec * codec )
2011-06-22 17:23:25 +04:00
{
2013-01-07 15:27:36 +04:00
vt1708_stop_hp_work ( codec ) ;
2014-02-10 21:23:57 +04:00
snd_hda_gen_free ( codec ) ;
2011-06-22 17:23:25 +04:00
}
2013-01-07 15:27:36 +04:00
# ifdef CONFIG_PM
static int via_suspend ( struct hda_codec * codec )
2011-06-22 17:23:25 +04:00
{
struct via_spec * spec = codec - > spec ;
2013-01-07 15:27:36 +04:00
vt1708_stop_hp_work ( codec ) ;
2011-06-18 19:24:46 +04:00
2013-06-05 17:25:18 +04:00
/* Fix pop noise on headphones */
if ( spec - > codec_type = = VT1802 )
snd_hda_shutup_pins ( codec ) ;
2011-06-18 19:24:46 +04:00
2011-06-22 17:23:25 +04:00
return 0 ;
}
2015-05-31 10:27:29 +03:00
static int via_resume ( struct hda_codec * codec )
{
/* some delay here to make jack detection working (bko#98921) */
msleep ( 10 ) ;
codec - > patch_ops . init ( codec ) ;
regcache_sync ( codec - > core . regmap ) ;
return 0 ;
}
2013-01-07 15:27:36 +04:00
# endif
2011-06-22 17:23:25 +04:00
2013-01-07 15:27:36 +04:00
# ifdef CONFIG_PM
static int via_check_power_status ( struct hda_codec * codec , hda_nid_t nid )
2011-06-22 17:23:25 +04:00
{
struct via_spec * spec = codec - > spec ;
2013-01-07 15:27:36 +04:00
analog_low_current_mode ( codec ) ;
vt1708_update_hp_work ( codec ) ;
return snd_hda_check_amp_list_power ( codec , & spec - > gen . loopback , nid ) ;
}
# endif
2011-06-22 17:23:25 +04:00
2013-01-07 15:27:36 +04:00
/*
*/
2011-06-22 17:23:25 +04:00
2013-01-07 15:27:36 +04:00
static int via_init ( struct hda_codec * codec ) ;
2011-06-22 17:23:25 +04:00
2013-01-07 15:27:36 +04:00
static const struct hda_codec_ops via_patch_ops = {
. build_controls = via_build_controls ,
. build_pcms = snd_hda_gen_build_pcms ,
. init = via_init ,
. free = via_free ,
. unsol_event = snd_hda_jack_unsol_event ,
# ifdef CONFIG_PM
. suspend = via_suspend ,
2015-05-31 10:27:29 +03:00
. resume = via_resume ,
2013-01-07 15:27:36 +04:00
. check_power_status = via_check_power_status ,
# endif
} ;
2011-06-22 17:23:25 +04:00
2006-11-29 17:29:40 +03:00
2013-01-07 15:27:36 +04:00
static const struct hda_verb vt1708_init_verbs [ ] = {
/* power down jack detect function */
{ 0x1 , 0xf81 , 0x1 } ,
{ }
} ;
2008-09-09 11:50:37 +04:00
static void vt1708_set_pinconfig_connect ( struct hda_codec * codec , hda_nid_t nid )
{
unsigned int def_conf ;
unsigned char seqassoc ;
2009-02-20 16:37:42 +03:00
def_conf = snd_hda_codec_get_pincfg ( codec , nid ) ;
2008-09-09 11:50:37 +04:00
seqassoc = ( unsigned char ) get_defcfg_association ( def_conf ) ;
seqassoc = ( seqassoc < < 4 ) | get_defcfg_sequence ( def_conf ) ;
2009-10-10 15:08:19 +04:00
if ( get_defcfg_connect ( def_conf ) = = AC_JACK_PORT_NONE
& & ( seqassoc = = 0xf0 | | seqassoc = = 0xff ) ) {
def_conf = def_conf & ( ~ ( AC_JACK_PORT_BOTH < < 30 ) ) ;
snd_hda_codec_set_pincfg ( codec , nid , def_conf ) ;
2008-09-09 11:50:37 +04:00
}
return ;
}
2011-06-17 17:46:13 +04:00
static int vt1708_jack_detect_get ( struct snd_kcontrol * kcontrol ,
2009-10-10 15:08:17 +04:00
struct snd_ctl_elem_value * ucontrol )
{
struct hda_codec * codec = snd_kcontrol_chip ( kcontrol ) ;
struct via_spec * spec = codec - > spec ;
if ( spec - > codec_type ! = VT1708 )
return 0 ;
2011-06-17 17:46:13 +04:00
ucontrol - > value . integer . value [ 0 ] = spec - > vt1708_jack_detect ;
2009-10-10 15:08:17 +04:00
return 0 ;
}
2011-06-17 17:46:13 +04:00
static int vt1708_jack_detect_put ( struct snd_kcontrol * kcontrol ,
2009-10-10 15:08:17 +04:00
struct snd_ctl_elem_value * ucontrol )
{
struct hda_codec * codec = snd_kcontrol_chip ( kcontrol ) ;
struct via_spec * spec = codec - > spec ;
2011-11-24 19:33:09 +04:00
int val ;
2009-10-10 15:08:17 +04:00
if ( spec - > codec_type ! = VT1708 )
return 0 ;
2011-11-24 19:33:09 +04:00
val = ! ! ucontrol - > value . integer . value [ 0 ] ;
if ( spec - > vt1708_jack_detect = = val )
return 0 ;
spec - > vt1708_jack_detect = val ;
2013-01-07 15:27:36 +04:00
vt1708_update_hp_work ( codec ) ;
2011-11-24 19:33:09 +04:00
return 1 ;
2009-10-10 15:08:17 +04:00
}
2013-01-07 15:27:36 +04:00
static const struct snd_kcontrol_new vt1708_jack_detect_ctl [ ] = {
{
2011-06-17 17:46:13 +04:00
. iface = SNDRV_CTL_ELEM_IFACE_MIXER ,
. name = " Jack Detect " ,
. count = 1 ,
. info = snd_ctl_boolean_mono_info ,
. get = vt1708_jack_detect_get ,
. put = vt1708_jack_detect_put ,
2013-01-07 15:27:36 +04:00
} ,
{ } /* terminator */
2009-10-10 15:08:17 +04:00
} ;
2013-03-22 18:11:07 +04:00
static const struct badness_table via_main_out_badness = {
. no_primary_dac = 0x10000 ,
. no_dac = 0x4000 ,
. shared_primary = 0x10000 ,
. shared_surr = 0x20 ,
. shared_clfe = 0x20 ,
. shared_surr_main = 0x20 ,
} ;
static const struct badness_table via_extra_out_badness = {
. no_primary_dac = 0x4000 ,
. no_dac = 0x4000 ,
. shared_primary = 0x12 ,
. shared_surr = 0x20 ,
. shared_clfe = 0x20 ,
. shared_surr_main = 0x10 ,
} ;
2013-01-07 15:27:36 +04:00
static int via_parse_auto_config ( struct hda_codec * codec )
{
struct via_spec * spec = codec - > spec ;
int err ;
2013-03-22 18:11:07 +04:00
spec - > gen . main_out_badness = & via_main_out_badness ;
spec - > gen . extra_out_badness = & via_extra_out_badness ;
2013-01-07 15:27:36 +04:00
err = snd_hda_parse_pin_defcfg ( codec , & spec - > gen . autocfg , NULL , 0 ) ;
if ( err < 0 )
return err ;
ALSA: hda/via - Add beep controls to VIA codecs
My codec has a beep-generating node:
$ cat /proc/asound/card1/codec#0
Codec: VIA VT1802
...
Vendor Id: 0x11068446
Subsystem Id: 0x15587410
Revision Id: 0x100000
...
Node 0x22 [Beep Generator Widget] wcaps 0x70040c: Mono Amp-Out
Amp-Out caps: ofs=0x0a, nsteps=0x12, stepsize=0x05, mute=1
Amp-Out vals: [0x0a]
Power states: D0 D1 D2 D3
Power: setting=D0, actual=D0
...
But I was missing the:
Control: name=...
entries that I need to manage this widget from alsamixer. With this
patch (based on the similar Mono Amp-Out handling in
patch_conexant.c), I get a new:
input: HDA Digital PCBeep as /devices/pci0000:00/0000:00:1b.0/sound/card1/hdaudioC1D0/input15
entry in dmesg and controls to manage that beep:
$ cat /proc/asound/card1/codec#0 | grep -A5 Beep
Node 0x22 [Beep Generator Widget] wcaps 0x70040c: Mono Amp-Out
Control: name="Beep Playback Volume", index=0, device=0
ControlAmp: chs=1, dir=Out, idx=0, ofs=0
Control: name="Beep Playback Switch", index=0, device=0
ControlAmp: chs=1, dir=Out, idx=0, ofs=0
Amp-Out caps: ofs=0x0a, nsteps=0x12, stepsize=0x05, mute=1
Amp-Out vals: [0x12]
Power states: D0 D1 D2 D3
Power: setting=D0, actual=D0
[rebased and modified for the latest tree by tiwai]
Signed-off-by: W. Trevor King <wking@tremily.us>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
2015-03-25 10:43:42 +03:00
auto_parse_beep ( codec ) ;
2013-01-07 15:27:36 +04:00
err = snd_hda_gen_parse_auto_config ( codec , & spec - > gen . autocfg ) ;
if ( err < 0 )
return err ;
2015-03-17 17:56:05 +03:00
/* disable widget PM at start for compatibility */
2015-03-20 20:21:03 +03:00
codec - > power_save_node = 0 ;
2015-03-17 17:56:05 +03:00
spec - > gen . power_down_unused = 0 ;
2013-01-07 15:27:36 +04:00
return 0 ;
}
2011-06-20 13:32:27 +04:00
static int via_init ( struct hda_codec * codec )
{
struct via_spec * spec = codec - > spec ;
int i ;
for ( i = 0 ; i < spec - > num_iverbs ; i + + )
snd_hda_sequence_write ( codec , spec - > init_verbs [ i ] ) ;
2009-10-10 15:08:43 +04:00
2012-02-01 13:33:23 +04:00
/* init power states */
__analog_low_current_mode ( codec , true ) ;
2013-01-07 15:27:36 +04:00
snd_hda_gen_init ( codec ) ;
2011-06-20 14:39:26 +04:00
2013-01-07 15:27:36 +04:00
vt1708_update_hp_work ( codec ) ;
2009-10-10 15:08:43 +04:00
2006-11-29 17:29:40 +03:00
return 0 ;
}
2013-02-05 15:06:02 +04:00
static int vt1708_build_controls ( struct hda_codec * codec )
{
/* In order not to create "Phantom Jack" controls,
temporary enable jackpoll */
int err ;
int old_interval = codec - > jackpoll_interval ;
codec - > jackpoll_interval = msecs_to_jiffies ( 100 ) ;
err = via_build_controls ( codec ) ;
codec - > jackpoll_interval = old_interval ;
return err ;
}
2013-01-07 15:27:36 +04:00
static int vt1708_build_pcms ( struct hda_codec * codec )
2009-07-07 20:18:59 +04:00
{
struct via_spec * spec = codec - > spec ;
2013-01-07 15:27:36 +04:00
int i , err ;
err = snd_hda_gen_build_pcms ( codec ) ;
2015-03-03 12:07:24 +03:00
if ( err < 0 | | codec - > core . vendor_id ! = 0x11061708 )
2013-01-07 15:27:36 +04:00
return err ;
/* We got noisy outputs on the right channel on VT1708 when
* 24 bit samples are used . Until any workaround is found ,
* disable the 24 bit format , so far .
*/
2015-02-27 19:43:19 +03:00
for ( i = 0 ; i < ARRAY_SIZE ( spec - > gen . pcm_rec ) ; i + + ) {
struct hda_pcm * info = spec - > gen . pcm_rec [ i ] ;
if ( ! info )
continue ;
2013-01-07 15:27:36 +04:00
if ( ! info - > stream [ SNDRV_PCM_STREAM_PLAYBACK ] . substreams | |
info - > pcm_type ! = HDA_PCM_TYPE_AUDIO )
continue ;
info - > stream [ SNDRV_PCM_STREAM_PLAYBACK ] . formats =
SNDRV_PCM_FMTBIT_S16_LE ;
2009-07-07 20:18:59 +04:00
}
2013-01-07 15:27:36 +04:00
2009-07-08 09:45:46 +04:00
return 0 ;
2009-07-07 20:18:59 +04:00
}
2006-11-29 17:29:40 +03:00
static int patch_vt1708 ( struct hda_codec * codec )
{
struct via_spec * spec ;
int err ;
/* create a codec specific record */
2009-12-08 18:13:32 +03:00
spec = via_new_spec ( codec ) ;
2006-11-29 17:29:40 +03:00
if ( spec = = NULL )
return - ENOMEM ;
2015-05-29 11:42:14 +03:00
/* override some patch_ops */
codec - > patch_ops . build_controls = vt1708_build_controls ;
codec - > patch_ops . build_pcms = vt1708_build_pcms ;
2013-01-07 15:27:36 +04:00
spec - > gen . mixer_nid = 0x17 ;
/* set jackpoll_interval while parsing the codec */
codec - > jackpoll_interval = msecs_to_jiffies ( 100 ) ;
spec - > vt1708_jack_detect = 1 ;
/* don't support the input jack switching due to lack of unsol event */
/* (it may work with polling, though, but it needs testing) */
spec - > gen . suppress_auto_mic = 1 ;
2013-06-24 18:18:10 +04:00
/* Some machines show the broken speaker mute */
spec - > gen . auto_mute_via_amp = 1 ;
2011-06-17 19:19:19 +04:00
2011-06-18 19:45:49 +04:00
/* Add HP and CD pin config connect bit re-config action */
vt1708_set_pinconfig_connect ( codec , VT1708_HP_PIN_NID ) ;
vt1708_set_pinconfig_connect ( codec , VT1708_CD_PIN_NID ) ;
2006-11-29 17:29:40 +03:00
/* automatic parse from the BIOS config */
2011-06-18 19:45:49 +04:00
err = via_parse_auto_config ( codec ) ;
2006-11-29 17:29:40 +03:00
if ( err < 0 ) {
via_free ( codec ) ;
return err ;
}
2011-06-18 19:45:49 +04:00
/* add jack detect on/off control */
2013-01-07 15:27:36 +04:00
spec - > mixers [ spec - > num_mixers + + ] = vt1708_jack_detect_ctl ;
2006-11-29 17:29:40 +03:00
2011-06-29 09:52:02 +04:00
spec - > init_verbs [ spec - > num_iverbs + + ] = vt1708_init_verbs ;
2013-01-07 15:27:36 +04:00
/* clear jackpoll_interval again; it's set dynamically */
codec - > jackpoll_interval = 0 ;
2006-11-29 17:29:40 +03:00
return 0 ;
}
2011-06-21 18:33:55 +04:00
static int patch_vt1709 ( struct hda_codec * codec )
2006-11-29 17:29:40 +03:00
{
struct via_spec * spec ;
int err ;
/* create a codec specific record */
2009-12-08 18:13:32 +03:00
spec = via_new_spec ( codec ) ;
2006-11-29 17:29:40 +03:00
if ( spec = = NULL )
return - ENOMEM ;
2013-01-07 15:27:36 +04:00
spec - > gen . mixer_nid = 0x18 ;
2011-06-17 19:19:19 +04:00
2011-06-18 19:45:49 +04:00
err = via_parse_auto_config ( codec ) ;
2006-11-29 17:29:40 +03:00
if ( err < 0 ) {
via_free ( codec ) ;
return err ;
}
2007-12-13 18:40:40 +03:00
return 0 ;
}
2009-10-10 15:07:29 +04:00
static int patch_vt1708S ( struct hda_codec * codec ) ;
2011-06-21 18:33:55 +04:00
static int patch_vt1708B ( struct hda_codec * codec )
2007-12-13 18:40:40 +03:00
{
struct via_spec * spec ;
int err ;
2009-10-10 15:07:29 +04:00
if ( get_codec_type ( codec ) = = VT1708BCE )
return patch_vt1708S ( codec ) ;
2011-06-21 18:33:55 +04:00
2007-12-13 18:40:40 +03:00
/* create a codec specific record */
2009-12-08 18:13:32 +03:00
spec = via_new_spec ( codec ) ;
2007-12-13 18:40:40 +03:00
if ( spec = = NULL )
return - ENOMEM ;
2013-01-07 15:27:36 +04:00
spec - > gen . mixer_nid = 0x16 ;
2011-06-17 19:19:19 +04:00
2007-12-13 18:40:40 +03:00
/* automatic parse from the BIOS config */
2011-06-18 19:45:49 +04:00
err = via_parse_auto_config ( codec ) ;
2007-12-13 18:40:40 +03:00
if ( err < 0 ) {
via_free ( codec ) ;
return err ;
}
return 0 ;
}
2008-09-09 11:56:01 +04:00
/* Patch for VT1708S */
2011-06-20 14:09:02 +04:00
static const struct hda_verb vt1708S_init_verbs [ ] = {
2008-09-15 18:43:23 +04:00
/* Enable Mic Boost Volume backdoor */
{ 0x1 , 0xf98 , 0x1 } ,
2009-10-10 15:08:32 +04:00
/* don't bybass mixer */
{ 0x1 , 0xf88 , 0xc0 } ,
2008-09-09 11:56:01 +04:00
{ }
} ;
2009-10-10 15:08:31 +04:00
static void override_mic_boost ( struct hda_codec * codec , hda_nid_t pin ,
int offset , int num_steps , int step_size )
{
2013-06-19 09:54:09 +04:00
snd_hda_override_wcaps ( codec , pin ,
get_wcaps ( codec , pin ) | AC_WCAP_IN_AMP ) ;
2009-10-10 15:08:31 +04:00
snd_hda_override_amp_caps ( codec , pin , HDA_INPUT ,
( offset < < AC_AMPCAP_OFFSET_SHIFT ) |
( num_steps < < AC_AMPCAP_NUM_STEPS_SHIFT ) |
( step_size < < AC_AMPCAP_STEP_SIZE_SHIFT ) |
( 0 < < AC_AMPCAP_MUTE_SHIFT ) ) ;
}
2008-09-09 11:56:01 +04:00
static int patch_vt1708S ( struct hda_codec * codec )
{
struct via_spec * spec ;
int err ;
/* create a codec specific record */
2009-12-08 18:13:32 +03:00
spec = via_new_spec ( codec ) ;
2008-09-09 11:56:01 +04:00
if ( spec = = NULL )
return - ENOMEM ;
2013-01-07 15:27:36 +04:00
spec - > gen . mixer_nid = 0x16 ;
2011-06-18 19:24:46 +04:00
override_mic_boost ( codec , 0x1a , 0 , 3 , 40 ) ;
override_mic_boost ( codec , 0x1e , 0 , 3 , 40 ) ;
2011-06-17 19:19:19 +04:00
2009-10-10 15:07:29 +04:00
/* correct names for VT1708BCE */
2015-10-01 18:59:43 +03:00
if ( get_codec_type ( codec ) = = VT1708BCE )
snd_hda_codec_set_name ( codec , " VT1708BCE " ) ;
2011-03-23 12:56:05 +03:00
/* correct names for VT1705 */
2015-10-01 18:59:43 +03:00
if ( codec - > core . vendor_id = = 0x11064397 )
snd_hda_codec_set_name ( codec , " VT1705 " ) ;
2013-01-07 15:27:36 +04:00
/* automatic parse from the BIOS config */
err = via_parse_auto_config ( codec ) ;
if ( err < 0 ) {
via_free ( codec ) ;
return err ;
}
spec - > init_verbs [ spec - > num_iverbs + + ] = vt1708S_init_verbs ;
2008-09-09 11:56:01 +04:00
return 0 ;
}
/* Patch for VT1702 */
2011-06-20 14:09:02 +04:00
static const struct hda_verb vt1702_init_verbs [ ] = {
2009-10-10 15:08:32 +04:00
/* mixer enable */
{ 0x1 , 0xF88 , 0x3 } ,
/* GPIO 0~2 */
{ 0x1 , 0xF82 , 0x3F } ,
2008-09-09 11:56:01 +04:00
{ }
} ;
static int patch_vt1702 ( struct hda_codec * codec )
{
struct via_spec * spec ;
int err ;
/* create a codec specific record */
2009-12-08 18:13:32 +03:00
spec = via_new_spec ( codec ) ;
2008-09-09 11:56:01 +04:00
if ( spec = = NULL )
return - ENOMEM ;
2013-01-07 15:27:36 +04:00
spec - > gen . mixer_nid = 0x1a ;
2011-06-17 19:19:19 +04:00
2011-06-18 19:45:49 +04:00
/* limit AA path volume to 0 dB */
snd_hda_override_amp_caps ( codec , 0x1A , HDA_INPUT ,
( 0x17 < < AC_AMPCAP_OFFSET_SHIFT ) |
( 0x17 < < AC_AMPCAP_NUM_STEPS_SHIFT ) |
( 0x5 < < AC_AMPCAP_STEP_SIZE_SHIFT ) |
( 1 < < AC_AMPCAP_MUTE_SHIFT ) ) ;
2008-09-09 11:56:01 +04:00
/* automatic parse from the BIOS config */
2011-06-18 19:45:49 +04:00
err = via_parse_auto_config ( codec ) ;
2008-09-09 11:56:01 +04:00
if ( err < 0 ) {
via_free ( codec ) ;
return err ;
}
2011-06-20 14:09:02 +04:00
spec - > init_verbs [ spec - > num_iverbs + + ] = vt1702_init_verbs ;
2008-09-09 11:56:01 +04:00
return 0 ;
}
2009-10-10 15:08:34 +04:00
/* Patch for VT1718S */
2011-06-20 14:09:02 +04:00
static const struct hda_verb vt1718S_init_verbs [ ] = {
2011-03-24 07:42:03 +03:00
/* Enable MW0 adjust Gain 5 */
{ 0x1 , 0xfb2 , 0x10 } ,
2009-10-10 15:08:34 +04:00
/* Enable Boost Volume backdoor */
{ 0x1 , 0xf88 , 0x8 } ,
2011-06-20 13:32:27 +04:00
2009-10-10 15:08:34 +04:00
{ }
} ;
2011-07-11 19:05:04 +04:00
/* Add a connection to the primary DAC from AA-mixer for some codecs
* This isn ' t listed from the raw info , but the chip has a secret connection .
*/
static int add_secret_dac_path ( struct hda_codec * codec )
{
struct via_spec * spec = codec - > spec ;
int i , nums ;
hda_nid_t conn [ 8 ] ;
hda_nid_t nid ;
2013-01-07 15:27:36 +04:00
if ( ! spec - > gen . mixer_nid )
2011-07-11 19:05:04 +04:00
return 0 ;
2013-01-07 15:27:36 +04:00
nums = snd_hda_get_connections ( codec , spec - > gen . mixer_nid , conn ,
2011-07-11 19:05:04 +04:00
ARRAY_SIZE ( conn ) - 1 ) ;
for ( i = 0 ; i < nums ; i + + ) {
if ( get_wcaps_type ( get_wcaps ( codec , conn [ i ] ) ) = = AC_WID_AUD_OUT )
return 0 ;
}
/* find the primary DAC and add to the connection list */
2015-03-03 12:07:24 +03:00
for_each_hda_codec_node ( nid , codec ) {
2011-07-11 19:05:04 +04:00
unsigned int caps = get_wcaps ( codec , nid ) ;
if ( get_wcaps_type ( caps ) = = AC_WID_AUD_OUT & &
! ( caps & AC_WCAP_DIGITAL ) ) {
conn [ nums + + ] = nid ;
return snd_hda_override_conn_list ( codec ,
2013-01-07 15:27:36 +04:00
spec - > gen . mixer_nid ,
2011-07-11 19:05:04 +04:00
nums , conn ) ;
}
}
return 0 ;
}
2009-10-10 15:08:34 +04:00
static int patch_vt1718S ( struct hda_codec * codec )
{
struct via_spec * spec ;
int err ;
/* create a codec specific record */
2009-12-08 18:13:32 +03:00
spec = via_new_spec ( codec ) ;
2009-10-10 15:08:34 +04:00
if ( spec = = NULL )
return - ENOMEM ;
2013-01-07 15:27:36 +04:00
spec - > gen . mixer_nid = 0x21 ;
2011-06-18 19:24:46 +04:00
override_mic_boost ( codec , 0x2b , 0 , 3 , 40 ) ;
override_mic_boost ( codec , 0x29 , 0 , 3 , 40 ) ;
2011-07-11 19:05:04 +04:00
add_secret_dac_path ( codec ) ;
2011-06-17 19:19:19 +04:00
2009-10-10 15:08:34 +04:00
/* automatic parse from the BIOS config */
2011-06-18 19:45:49 +04:00
err = via_parse_auto_config ( codec ) ;
2009-10-10 15:08:34 +04:00
if ( err < 0 ) {
via_free ( codec ) ;
return err ;
}
2011-06-20 14:09:02 +04:00
spec - > init_verbs [ spec - > num_iverbs + + ] = vt1718S_init_verbs ;
2009-10-10 15:08:34 +04:00
return 0 ;
}
2009-10-10 15:08:41 +04:00
/* Patch for VT1716S */
static int vt1716s_dmic_info ( struct snd_kcontrol * kcontrol ,
struct snd_ctl_elem_info * uinfo )
{
uinfo - > type = SNDRV_CTL_ELEM_TYPE_BOOLEAN ;
uinfo - > count = 1 ;
uinfo - > value . integer . min = 0 ;
uinfo - > value . integer . max = 1 ;
return 0 ;
}
static int vt1716s_dmic_get ( struct snd_kcontrol * kcontrol ,
struct snd_ctl_elem_value * ucontrol )
{
struct hda_codec * codec = snd_kcontrol_chip ( kcontrol ) ;
int index = 0 ;
index = snd_hda_codec_read ( codec , 0x26 , 0 ,
AC_VERB_GET_CONNECT_SEL , 0 ) ;
if ( index ! = - 1 )
* ucontrol - > value . integer . value = index ;
return 0 ;
}
static int vt1716s_dmic_put ( struct snd_kcontrol * kcontrol ,
struct snd_ctl_elem_value * ucontrol )
{
struct hda_codec * codec = snd_kcontrol_chip ( kcontrol ) ;
struct via_spec * spec = codec - > spec ;
int index = * ucontrol - > value . integer . value ;
snd_hda_codec_write ( codec , 0x26 , 0 ,
AC_VERB_SET_CONNECT_SEL , index ) ;
spec - > dmic_enabled = index ;
return 1 ;
}
2011-05-02 14:38:19 +04:00
static const struct snd_kcontrol_new vt1716s_dmic_mixer [ ] = {
2009-10-10 15:08:41 +04:00
HDA_CODEC_VOLUME ( " Digital Mic Capture Volume " , 0x22 , 0x0 , HDA_INPUT ) ,
{
. iface = SNDRV_CTL_ELEM_IFACE_MIXER ,
. name = " Digital Mic Capture Switch " ,
2009-12-08 18:13:32 +03:00
. subdevice = HDA_SUBDEV_NID_FLAG | 0x26 ,
2009-10-10 15:08:41 +04:00
. count = 1 ,
. info = vt1716s_dmic_info ,
. get = vt1716s_dmic_get ,
. put = vt1716s_dmic_put ,
} ,
{ } /* end */
} ;
/* mono-out mixer elements */
2011-05-02 14:38:19 +04:00
static const struct snd_kcontrol_new vt1716S_mono_out_mixer [ ] = {
2009-10-10 15:08:41 +04:00
HDA_CODEC_MUTE ( " Mono Playback Switch " , 0x2a , 0x0 , HDA_OUTPUT ) ,
{ } /* end */
} ;
2011-06-20 14:09:02 +04:00
static const struct hda_verb vt1716S_init_verbs [ ] = {
2009-10-10 15:08:41 +04:00
/* Enable Boost Volume backdoor */
{ 0x1 , 0xf8a , 0x80 } ,
/* don't bybass mixer */
{ 0x1 , 0xf88 , 0xc0 } ,
/* Enable mono output */
{ 0x1 , 0xf90 , 0x08 } ,
{ }
} ;
static int patch_vt1716S ( struct hda_codec * codec )
{
struct via_spec * spec ;
int err ;
/* create a codec specific record */
2009-12-08 18:13:32 +03:00
spec = via_new_spec ( codec ) ;
2009-10-10 15:08:41 +04:00
if ( spec = = NULL )
return - ENOMEM ;
2013-01-07 15:27:36 +04:00
spec - > gen . mixer_nid = 0x16 ;
2011-06-18 19:24:46 +04:00
override_mic_boost ( codec , 0x1a , 0 , 3 , 40 ) ;
override_mic_boost ( codec , 0x1e , 0 , 3 , 40 ) ;
2011-06-17 19:19:19 +04:00
2009-10-10 15:08:41 +04:00
/* automatic parse from the BIOS config */
2011-06-18 19:45:49 +04:00
err = via_parse_auto_config ( codec ) ;
2009-10-10 15:08:41 +04:00
if ( err < 0 ) {
via_free ( codec ) ;
return err ;
}
2011-06-20 14:09:02 +04:00
spec - > init_verbs [ spec - > num_iverbs + + ] = vt1716S_init_verbs ;
2009-10-10 15:08:41 +04:00
2013-01-07 15:27:36 +04:00
spec - > mixers [ spec - > num_mixers + + ] = vt1716s_dmic_mixer ;
2009-10-10 15:08:41 +04:00
spec - > mixers [ spec - > num_mixers + + ] = vt1716S_mono_out_mixer ;
return 0 ;
}
2009-10-10 15:08:43 +04:00
/* for vt2002P */
2011-06-20 14:09:02 +04:00
static const struct hda_verb vt2002P_init_verbs [ ] = {
2011-03-24 07:43:02 +03:00
/* Class-D speaker related verbs */
{ 0x1 , 0xfe0 , 0x4 } ,
{ 0x1 , 0xfe9 , 0x80 } ,
{ 0x1 , 0xfe2 , 0x22 } ,
2009-10-10 15:08:43 +04:00
/* Enable Boost Volume backdoor */
{ 0x1 , 0xfb9 , 0x24 } ,
/* Enable AOW0 to MW9 */
{ 0x1 , 0xfb8 , 0x88 } ,
{ }
} ;
2011-06-20 14:39:26 +04:00
2011-06-20 14:09:02 +04:00
static const struct hda_verb vt1802_init_verbs [ ] = {
2011-03-23 12:57:34 +03:00
/* Enable Boost Volume backdoor */
{ 0x1 , 0xfb9 , 0x24 } ,
/* Enable AOW0 to MW9 */
{ 0x1 , 0xfb8 , 0x88 } ,
{ }
} ;
2009-10-10 15:08:43 +04:00
2012-09-18 16:26:59 +04:00
/*
* pin fix - up
*/
enum {
VIA_FIXUP_INTMIC_BOOST ,
2012-11-07 13:40:36 +04:00
VIA_FIXUP_ASUS_G75 ,
2012-09-18 16:26:59 +04:00
} ;
static void via_fixup_intmic_boost ( struct hda_codec * codec ,
const struct hda_fixup * fix , int action )
{
if ( action = = HDA_FIXUP_ACT_PRE_PROBE )
override_mic_boost ( codec , 0x30 , 0 , 2 , 40 ) ;
}
static const struct hda_fixup via_fixups [ ] = {
[ VIA_FIXUP_INTMIC_BOOST ] = {
. type = HDA_FIXUP_FUNC ,
. v . func = via_fixup_intmic_boost ,
} ,
2012-11-07 13:40:36 +04:00
[ VIA_FIXUP_ASUS_G75 ] = {
. type = HDA_FIXUP_PINS ,
. v . pins = ( const struct hda_pintbl [ ] ) {
/* set 0x24 and 0x33 as speakers */
{ 0x24 , 0x991301f0 } ,
{ 0x33 , 0x991301f1 } , /* subwoofer */
{ }
}
} ,
2012-09-18 16:26:59 +04:00
} ;
static const struct snd_pci_quirk vt2002p_fixups [ ] = {
2012-11-07 13:40:36 +04:00
SND_PCI_QUIRK ( 0x1043 , 0x1487 , " Asus G75 " , VIA_FIXUP_ASUS_G75 ) ,
2012-09-18 16:26:59 +04:00
SND_PCI_QUIRK ( 0x1043 , 0x8532 , " Asus X202E " , VIA_FIXUP_INTMIC_BOOST ) ,
{ }
} ;
2012-11-07 13:37:48 +04:00
/* NIDs 0x24 and 0x33 on VT1802 have connections to non-existing NID 0x3e
* Replace this with mixer NID 0x1c
*/
static void fix_vt1802_connections ( struct hda_codec * codec )
{
static hda_nid_t conn_24 [ ] = { 0x14 , 0x1c } ;
static hda_nid_t conn_33 [ ] = { 0x1c } ;
snd_hda_override_conn_list ( codec , 0x24 , ARRAY_SIZE ( conn_24 ) , conn_24 ) ;
snd_hda_override_conn_list ( codec , 0x33 , ARRAY_SIZE ( conn_33 ) , conn_33 ) ;
}
2009-10-10 15:08:43 +04:00
/* patch for vt2002P */
static int patch_vt2002P ( struct hda_codec * codec )
{
struct via_spec * spec ;
int err ;
/* create a codec specific record */
2009-12-08 18:13:32 +03:00
spec = via_new_spec ( codec ) ;
2009-10-10 15:08:43 +04:00
if ( spec = = NULL )
return - ENOMEM ;
2013-01-07 15:27:36 +04:00
spec - > gen . mixer_nid = 0x21 ;
2011-06-18 19:24:46 +04:00
override_mic_boost ( codec , 0x2b , 0 , 3 , 40 ) ;
override_mic_boost ( codec , 0x29 , 0 , 3 , 40 ) ;
2012-11-07 13:37:48 +04:00
if ( spec - > codec_type = = VT1802 )
fix_vt1802_connections ( codec ) ;
2011-07-11 19:05:04 +04:00
add_secret_dac_path ( codec ) ;
2011-06-17 19:19:19 +04:00
2012-09-18 16:26:59 +04:00
snd_hda_pick_fixup ( codec , NULL , vt2002p_fixups , via_fixups ) ;
snd_hda_apply_fixup ( codec , HDA_FIXUP_ACT_PRE_PROBE ) ;
2009-10-10 15:08:43 +04:00
/* automatic parse from the BIOS config */
2011-06-18 19:45:49 +04:00
err = via_parse_auto_config ( codec ) ;
2009-10-10 15:08:43 +04:00
if ( err < 0 ) {
via_free ( codec ) ;
return err ;
}
2011-03-23 12:57:34 +03:00
if ( spec - > codec_type = = VT1802 )
2011-06-20 14:39:26 +04:00
spec - > init_verbs [ spec - > num_iverbs + + ] = vt1802_init_verbs ;
2011-03-23 12:57:34 +03:00
else
2011-06-20 14:39:26 +04:00
spec - > init_verbs [ spec - > num_iverbs + + ] = vt2002P_init_verbs ;
2011-03-23 12:57:34 +03:00
2009-10-10 15:08:43 +04:00
return 0 ;
}
2009-10-10 15:08:46 +04:00
/* for vt1812 */
2011-06-20 14:09:02 +04:00
static const struct hda_verb vt1812_init_verbs [ ] = {
2009-10-10 15:08:46 +04:00
/* Enable Boost Volume backdoor */
{ 0x1 , 0xfb9 , 0x24 } ,
/* Enable AOW0 to MW9 */
{ 0x1 , 0xfb8 , 0xa8 } ,
{ }
} ;
/* patch for vt1812 */
static int patch_vt1812 ( struct hda_codec * codec )
{
struct via_spec * spec ;
int err ;
/* create a codec specific record */
2009-12-08 18:13:32 +03:00
spec = via_new_spec ( codec ) ;
2009-10-10 15:08:46 +04:00
if ( spec = = NULL )
return - ENOMEM ;
2013-01-07 15:27:36 +04:00
spec - > gen . mixer_nid = 0x21 ;
2011-06-18 19:24:46 +04:00
override_mic_boost ( codec , 0x2b , 0 , 3 , 40 ) ;
override_mic_boost ( codec , 0x29 , 0 , 3 , 40 ) ;
2011-07-11 19:05:04 +04:00
add_secret_dac_path ( codec ) ;
2011-06-17 19:19:19 +04:00
2009-10-10 15:08:46 +04:00
/* automatic parse from the BIOS config */
2011-06-18 19:45:49 +04:00
err = via_parse_auto_config ( codec ) ;
2009-10-10 15:08:46 +04:00
if ( err < 0 ) {
via_free ( codec ) ;
return err ;
}
2011-06-20 14:09:02 +04:00
spec - > init_verbs [ spec - > num_iverbs + + ] = vt1812_init_verbs ;
2009-10-10 15:08:46 +04:00
return 0 ;
}
2012-12-10 14:28:11 +04:00
/* patch for vt3476 */
static const struct hda_verb vt3476_init_verbs [ ] = {
/* Enable DMic 8/16/32K */
{ 0x1 , 0xF7B , 0x30 } ,
/* Enable Boost Volume backdoor */
{ 0x1 , 0xFB9 , 0x20 } ,
/* Enable AOW-MW9 path */
{ 0x1 , 0xFB8 , 0x10 } ,
{ }
} ;
static int patch_vt3476 ( struct hda_codec * codec )
{
struct via_spec * spec ;
int err ;
/* create a codec specific record */
spec = via_new_spec ( codec ) ;
if ( spec = = NULL )
return - ENOMEM ;
2013-01-07 15:27:36 +04:00
spec - > gen . mixer_nid = 0x3f ;
2012-12-10 14:28:11 +04:00
add_secret_dac_path ( codec ) ;
/* automatic parse from the BIOS config */
err = via_parse_auto_config ( codec ) ;
if ( err < 0 ) {
via_free ( codec ) ;
return err ;
}
spec - > init_verbs [ spec - > num_iverbs + + ] = vt3476_init_verbs ;
return 0 ;
}
2006-11-29 17:29:40 +03:00
/*
* patch entries
*/
2015-10-01 17:20:04 +03:00
static const struct hda_device_id snd_hda_id_via [ ] = {
HDA_CODEC_ENTRY ( 0x11061708 , " VT1708 " , patch_vt1708 ) ,
HDA_CODEC_ENTRY ( 0x11061709 , " VT1708 " , patch_vt1708 ) ,
HDA_CODEC_ENTRY ( 0x1106170a , " VT1708 " , patch_vt1708 ) ,
HDA_CODEC_ENTRY ( 0x1106170b , " VT1708 " , patch_vt1708 ) ,
HDA_CODEC_ENTRY ( 0x1106e710 , " VT1709 10-Ch " , patch_vt1709 ) ,
HDA_CODEC_ENTRY ( 0x1106e711 , " VT1709 10-Ch " , patch_vt1709 ) ,
HDA_CODEC_ENTRY ( 0x1106e712 , " VT1709 10-Ch " , patch_vt1709 ) ,
HDA_CODEC_ENTRY ( 0x1106e713 , " VT1709 10-Ch " , patch_vt1709 ) ,
HDA_CODEC_ENTRY ( 0x1106e714 , " VT1709 6-Ch " , patch_vt1709 ) ,
HDA_CODEC_ENTRY ( 0x1106e715 , " VT1709 6-Ch " , patch_vt1709 ) ,
HDA_CODEC_ENTRY ( 0x1106e716 , " VT1709 6-Ch " , patch_vt1709 ) ,
HDA_CODEC_ENTRY ( 0x1106e717 , " VT1709 6-Ch " , patch_vt1709 ) ,
HDA_CODEC_ENTRY ( 0x1106e720 , " VT1708B 8-Ch " , patch_vt1708B ) ,
HDA_CODEC_ENTRY ( 0x1106e721 , " VT1708B 8-Ch " , patch_vt1708B ) ,
HDA_CODEC_ENTRY ( 0x1106e722 , " VT1708B 8-Ch " , patch_vt1708B ) ,
HDA_CODEC_ENTRY ( 0x1106e723 , " VT1708B 8-Ch " , patch_vt1708B ) ,
HDA_CODEC_ENTRY ( 0x1106e724 , " VT1708B 4-Ch " , patch_vt1708B ) ,
HDA_CODEC_ENTRY ( 0x1106e725 , " VT1708B 4-Ch " , patch_vt1708B ) ,
HDA_CODEC_ENTRY ( 0x1106e726 , " VT1708B 4-Ch " , patch_vt1708B ) ,
HDA_CODEC_ENTRY ( 0x1106e727 , " VT1708B 4-Ch " , patch_vt1708B ) ,
HDA_CODEC_ENTRY ( 0x11060397 , " VT1708S " , patch_vt1708S ) ,
HDA_CODEC_ENTRY ( 0x11061397 , " VT1708S " , patch_vt1708S ) ,
HDA_CODEC_ENTRY ( 0x11062397 , " VT1708S " , patch_vt1708S ) ,
HDA_CODEC_ENTRY ( 0x11063397 , " VT1708S " , patch_vt1708S ) ,
HDA_CODEC_ENTRY ( 0x11064397 , " VT1705 " , patch_vt1708S ) ,
HDA_CODEC_ENTRY ( 0x11065397 , " VT1708S " , patch_vt1708S ) ,
HDA_CODEC_ENTRY ( 0x11066397 , " VT1708S " , patch_vt1708S ) ,
HDA_CODEC_ENTRY ( 0x11067397 , " VT1708S " , patch_vt1708S ) ,
HDA_CODEC_ENTRY ( 0x11060398 , " VT1702 " , patch_vt1702 ) ,
HDA_CODEC_ENTRY ( 0x11061398 , " VT1702 " , patch_vt1702 ) ,
HDA_CODEC_ENTRY ( 0x11062398 , " VT1702 " , patch_vt1702 ) ,
HDA_CODEC_ENTRY ( 0x11063398 , " VT1702 " , patch_vt1702 ) ,
HDA_CODEC_ENTRY ( 0x11064398 , " VT1702 " , patch_vt1702 ) ,
HDA_CODEC_ENTRY ( 0x11065398 , " VT1702 " , patch_vt1702 ) ,
HDA_CODEC_ENTRY ( 0x11066398 , " VT1702 " , patch_vt1702 ) ,
HDA_CODEC_ENTRY ( 0x11067398 , " VT1702 " , patch_vt1702 ) ,
HDA_CODEC_ENTRY ( 0x11060428 , " VT1718S " , patch_vt1718S ) ,
HDA_CODEC_ENTRY ( 0x11064428 , " VT1718S " , patch_vt1718S ) ,
HDA_CODEC_ENTRY ( 0x11060441 , " VT2020 " , patch_vt1718S ) ,
HDA_CODEC_ENTRY ( 0x11064441 , " VT1828S " , patch_vt1718S ) ,
HDA_CODEC_ENTRY ( 0x11060433 , " VT1716S " , patch_vt1716S ) ,
HDA_CODEC_ENTRY ( 0x1106a721 , " VT1716S " , patch_vt1716S ) ,
HDA_CODEC_ENTRY ( 0x11060438 , " VT2002P " , patch_vt2002P ) ,
HDA_CODEC_ENTRY ( 0x11064438 , " VT2002P " , patch_vt2002P ) ,
HDA_CODEC_ENTRY ( 0x11060448 , " VT1812 " , patch_vt1812 ) ,
HDA_CODEC_ENTRY ( 0x11060440 , " VT1818S " , patch_vt1708S ) ,
HDA_CODEC_ENTRY ( 0x11060446 , " VT1802 " , patch_vt2002P ) ,
HDA_CODEC_ENTRY ( 0x11068446 , " VT1802 " , patch_vt2002P ) ,
HDA_CODEC_ENTRY ( 0x11064760 , " VT1705CF " , patch_vt3476 ) ,
HDA_CODEC_ENTRY ( 0x11064761 , " VT1708SCE " , patch_vt3476 ) ,
HDA_CODEC_ENTRY ( 0x11064762 , " VT1808 " , patch_vt3476 ) ,
2006-11-29 17:29:40 +03:00
{ } /* terminator */
} ;
2015-10-01 17:20:04 +03:00
MODULE_DEVICE_TABLE ( hdaudio , snd_hda_id_via ) ;
2008-11-27 17:47:11 +03:00
2015-02-17 17:25:37 +03:00
static struct hda_codec_driver via_driver = {
2015-10-01 17:20:04 +03:00
. id = snd_hda_id_via ,
2008-11-27 17:47:11 +03:00
} ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_DESCRIPTION ( " VIA HD-audio codec " ) ;
2015-02-17 17:25:37 +03:00
module_hda_codec_driver ( via_driver ) ;