@ -12,6 +12,7 @@
# include <linux/init.h>
# include <linux/slab.h>
# include <sound/core.h>
# include <sound/control.h>
# include "hda_codec.h"
# include "hda_local.h"
# include "hda_jack.h"
@ -76,9 +77,13 @@ void snd_hda_jack_tbl_clear(struct hda_codec *codec)
static void jack_detect_update ( struct hda_codec * codec ,
struct hda_jack_tbl * jack )
{
if ( jack - > jack_dirty ) {
jack - > pin_sense = read_pin_sense ( codec , jack - > nid ) ;
if ( jack - > jack_dirty | | ! jack - > jack_cachable ) {
unsigned int val = read_pin_sense ( codec , jack - > nid ) ;
jack - > jack_dirty = 0 ;
if ( val ! = jack - > pin_sense ) {
jack - > need_notify = 1 ;
jack - > pin_sense = val ;
}
}
}
@ -141,8 +146,167 @@ int snd_hda_jack_detect_enable(struct hda_codec *codec, hda_nid_t nid,
struct hda_jack_tbl * jack = snd_hda_jack_tbl_new ( codec , nid ) ;
if ( ! jack )
return - ENOMEM ;
if ( jack - > jack_cachable )
return 0 ; /* already registered */
jack - > jack_cachable = 1 ;
return snd_hda_codec_write_cache ( codec , nid , 0 ,
AC_VERB_SET_UNSOLICITED_ENABLE ,
AC_USRSP_EN | tag ) ;
}
EXPORT_SYMBOL_HDA ( snd_hda_jack_detect_enable ) ;
/* queue the notification when needed */
static void jack_detect_report ( struct hda_codec * codec ,
struct hda_jack_tbl * jack )
{
jack_detect_update ( codec , jack ) ;
if ( jack - > need_notify ) {
snd_ctl_notify ( codec - > bus - > card , SNDRV_CTL_EVENT_MASK_VALUE ,
& jack - > kctl - > id ) ;
jack - > need_notify = 0 ;
}
}
/**
* snd_hda_jack_report - notify kctl when the jack state was changed
*/
void snd_hda_jack_report ( struct hda_codec * codec , hda_nid_t nid )
{
struct hda_jack_tbl * jack = snd_hda_jack_tbl_get ( codec , nid ) ;
if ( jack )
jack_detect_report ( codec , jack ) ;
}
EXPORT_SYMBOL_HDA ( snd_hda_jack_report ) ;
/**
* snd_hda_jack_report_sync - sync the states of all jacks and report if changed
*/
void snd_hda_jack_report_sync ( struct hda_codec * codec )
{
struct hda_jack_tbl * jack = codec - > jacktbl . list ;
int i ;
for ( i = 0 ; i < codec - > jacktbl . used ; i + + , jack + + )
if ( jack - > nid ) {
jack_detect_update ( codec , jack ) ;
jack_detect_report ( codec , jack ) ;
}
}
EXPORT_SYMBOL_HDA ( snd_hda_jack_report_sync ) ;
/*
* jack - detection kcontrols
*/
# define jack_detect_kctl_info snd_ctl_boolean_mono_info
static int jack_detect_kctl_get ( struct snd_kcontrol * kcontrol ,
struct snd_ctl_elem_value * ucontrol )
{
struct hda_codec * codec = snd_kcontrol_chip ( kcontrol ) ;
hda_nid_t nid = kcontrol - > private_value ;
ucontrol - > value . integer . value [ 0 ] = snd_hda_jack_detect ( codec , nid ) ;
return 0 ;
}
static struct snd_kcontrol_new jack_detect_kctl = {
/* name is filled later */
. iface = SNDRV_CTL_ELEM_IFACE_CARD ,
. access = SNDRV_CTL_ELEM_ACCESS_READ ,
. info = jack_detect_kctl_info ,
. get = jack_detect_kctl_get ,
} ;
/**
* snd_hda_jack_add_kctl - Add a kctl for the given pin
*
* This assigns a jack - detection kctl to the given pin . The kcontrol
* will have the given name and index .
*/
int snd_hda_jack_add_kctl ( struct hda_codec * codec , hda_nid_t nid ,
const char * name , int idx )
{
struct hda_jack_tbl * jack ;
struct snd_kcontrol * kctl ;
jack = snd_hda_jack_tbl_get ( codec , nid ) ;
if ( ! jack )
return 0 ;
if ( jack - > kctl )
return 0 ; /* already created */
kctl = snd_ctl_new1 ( & jack_detect_kctl , codec ) ;
if ( ! kctl )
return - ENOMEM ;
snprintf ( kctl - > id . name , sizeof ( kctl - > id . name ) , " %s Jack " , name ) ;
kctl - > id . index = idx ;
kctl - > private_value = nid ;
if ( snd_hda_ctl_add ( codec , nid , kctl ) < 0 )
return - ENOMEM ;
jack - > kctl = kctl ;
return 0 ;
}
static int add_jack_kctl ( struct hda_codec * codec , hda_nid_t nid , int idx ,
const struct auto_pin_cfg * cfg )
{
if ( ! nid )
return 0 ;
if ( ! is_jack_detectable ( codec , nid ) )
return 0 ;
return snd_hda_jack_add_kctl ( codec , nid ,
snd_hda_get_pin_label ( codec , nid , cfg ) ,
idx ) ;
}
/**
* snd_hda_jack_add_kctls - Add kctls for all pins included in the given pincfg
*
* As of now , it assigns only to the pins that enabled the detection .
* Usually this is called at the end of build_controls callback .
*/
int snd_hda_jack_add_kctls ( struct hda_codec * codec ,
const struct auto_pin_cfg * cfg )
{
const hda_nid_t * p ;
int i , err ;
for ( i = 0 , p = cfg - > line_out_pins ; i < cfg - > line_outs ; i + + , p + + ) {
err = add_jack_kctl ( codec , * p , i , cfg ) ;
if ( err < 0 )
return err ;
}
for ( i = 0 , p = cfg - > hp_pins ; i < cfg - > hp_outs ; i + + , p + + ) {
if ( * p = = * cfg - > line_out_pins ) /* might be duplicated */
break ;
err = add_jack_kctl ( codec , * p , i , cfg ) ;
if ( err < 0 )
return err ;
}
for ( i = 0 , p = cfg - > speaker_pins ; i < cfg - > speaker_outs ; i + + , p + + ) {
if ( * p = = * cfg - > line_out_pins ) /* might be duplicated */
break ;
err = add_jack_kctl ( codec , * p , i , cfg ) ;
if ( err < 0 )
return err ;
}
for ( i = 0 ; i < cfg - > num_inputs ; i + + ) {
err = add_jack_kctl ( codec , cfg - > inputs [ i ] . pin , 0 , cfg ) ;
if ( err < 0 )
return err ;
}
for ( i = 0 , p = cfg - > dig_out_pins ; i < cfg - > dig_outs ; i + + , p + + ) {
err = add_jack_kctl ( codec , * p , i , cfg ) ;
if ( err < 0 )
return err ;
}
err = add_jack_kctl ( codec , cfg - > dig_in_pin , 0 , cfg ) ;
if ( err < 0 )
return err ;
err = add_jack_kctl ( codec , cfg - > mono_out_pin , 0 , cfg ) ;
if ( err < 0 )
return err ;
return 0 ;
}
EXPORT_SYMBOL_HDA ( snd_hda_jack_add_kctls ) ;