2005-04-17 02:20:36 +04:00
/*
* Universal Interface for Intel High Definition Audio Codec
*
* Copyright ( c ) 2004 Takashi Iwai < tiwai @ suse . de >
*
*
* 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
*/
# include <linux/init.h>
# include <linux/delay.h>
# include <linux/slab.h>
# include <linux/pci.h>
2006-01-16 18:34:20 +03:00
# include <linux/mutex.h>
2005-04-17 02:20:36 +04:00
# include <sound/core.h>
# include "hda_codec.h"
# include <sound/asoundef.h>
2006-07-05 19:39:49 +04:00
# include <sound/tlv.h>
2005-04-17 02:20:36 +04:00
# include <sound/initval.h>
2011-03-03 16:40:14 +03:00
# include <sound/jack.h>
2005-04-17 02:20:36 +04:00
# include "hda_local.h"
2009-10-21 16:48:23 +04:00
# include "hda_beep.h"
2007-07-27 20:58:06 +04:00
# include <sound/hda_hwdep.h>
2005-04-17 02:20:36 +04:00
/*
* vendor / preset table
*/
struct hda_vendor_id {
unsigned int id ;
const char * name ;
} ;
/* codec vendor labels */
static struct hda_vendor_id hda_vendor_ids [ ] = {
2008-02-13 18:59:29 +03:00
{ 0x1002 , " ATI " } ,
2009-07-01 20:11:44 +04:00
{ 0x1013 , " Cirrus Logic " } ,
2006-09-18 00:05:54 +04:00
{ 0x1057 , " Motorola " } ,
2008-02-13 18:59:29 +03:00
{ 0x1095 , " Silicon Image " } ,
2008-12-16 16:43:21 +03:00
{ 0x10de , " Nvidia " } ,
2008-02-13 18:59:29 +03:00
{ 0x10ec , " Realtek " } ,
2009-04-16 10:53:34 +04:00
{ 0x1102 , " Creative " } ,
2006-11-29 17:29:40 +03:00
{ 0x1106 , " VIA " } ,
2007-10-18 19:38:17 +04:00
{ 0x111d , " IDT " } ,
2008-02-13 18:59:29 +03:00
{ 0x11c1 , " LSI " } ,
2005-05-15 16:30:10 +04:00
{ 0x11d4 , " Analog Devices " } ,
2005-04-17 02:20:36 +04:00
{ 0x13f6 , " C-Media " } ,
2006-09-18 00:05:54 +04:00
{ 0x14f1 , " Conexant " } ,
2008-02-13 18:59:29 +03:00
{ 0x17e8 , " Chrontel " } ,
{ 0x1854 , " LG " } ,
2008-10-28 17:50:13 +03:00
{ 0x1aec , " Wolfson Microelectronics " } ,
2005-04-17 02:20:36 +04:00
{ 0x434d , " C-Media " } ,
2008-12-18 11:11:33 +03:00
{ 0x8086 , " Intel " } ,
2005-04-13 16:45:30 +04:00
{ 0x8384 , " SigmaTel " } ,
2005-04-17 02:20:36 +04:00
{ } /* terminator */
} ;
2008-11-27 17:47:11 +03:00
static DEFINE_MUTEX ( preset_mutex ) ;
static LIST_HEAD ( hda_preset_tables ) ;
int snd_hda_add_codec_preset ( struct hda_codec_preset_list * preset )
{
mutex_lock ( & preset_mutex ) ;
list_add_tail ( & preset - > list , & hda_preset_tables ) ;
mutex_unlock ( & preset_mutex ) ;
return 0 ;
}
2008-11-28 17:17:06 +03:00
EXPORT_SYMBOL_HDA ( snd_hda_add_codec_preset ) ;
2008-11-27 17:47:11 +03:00
int snd_hda_delete_codec_preset ( struct hda_codec_preset_list * preset )
{
mutex_lock ( & preset_mutex ) ;
list_del ( & preset - > list ) ;
mutex_unlock ( & preset_mutex ) ;
return 0 ;
}
2008-11-28 17:17:06 +03:00
EXPORT_SYMBOL_HDA ( snd_hda_delete_codec_preset ) ;
2005-04-17 02:20:36 +04:00
2007-08-10 19:21:45 +04:00
# ifdef CONFIG_SND_HDA_POWER_SAVE
static void hda_power_work ( struct work_struct * work ) ;
static void hda_keep_power_on ( struct hda_codec * codec ) ;
# else
static inline void hda_keep_power_on ( struct hda_codec * codec ) { }
# endif
2009-11-16 16:58:17 +03:00
/**
* snd_hda_get_jack_location - Give a location string of the jack
* @ cfg : pin default config value
*
* Parse the pin default config value and returns the string of the
* jack location , e . g . " Rear " , " Front " , etc .
*/
2008-10-25 09:05:45 +04:00
const char * snd_hda_get_jack_location ( u32 cfg )
{
static char * bases [ 7 ] = {
" N/A " , " Rear " , " Front " , " Left " , " Right " , " Top " , " Bottom " ,
} ;
static unsigned char specials_idx [ ] = {
0x07 , 0x08 ,
0x17 , 0x18 , 0x19 ,
0x37 , 0x38
} ;
static char * specials [ ] = {
" Rear Panel " , " Drive Bar " ,
" Riser " , " HDMI " , " ATAPI " ,
" Mobile-In " , " Mobile-Out "
} ;
int i ;
cfg = ( cfg & AC_DEFCFG_LOCATION ) > > AC_DEFCFG_LOCATION_SHIFT ;
if ( ( cfg & 0x0f ) < 7 )
return bases [ cfg & 0x0f ] ;
for ( i = 0 ; i < ARRAY_SIZE ( specials_idx ) ; i + + ) {
if ( cfg = = specials_idx [ i ] )
return specials [ i ] ;
}
return " UNKNOWN " ;
}
2008-11-28 17:17:06 +03:00
EXPORT_SYMBOL_HDA ( snd_hda_get_jack_location ) ;
2008-10-25 09:05:45 +04:00
2009-11-16 16:58:17 +03:00
/**
* snd_hda_get_jack_connectivity - Give a connectivity string of the jack
* @ cfg : pin default config value
*
* Parse the pin default config value and returns the string of the
* jack connectivity , i . e . external or internal connection .
*/
2008-10-25 09:05:45 +04:00
const char * snd_hda_get_jack_connectivity ( u32 cfg )
{
static char * jack_locations [ 4 ] = { " Ext " , " Int " , " Sep " , " Oth " } ;
return jack_locations [ ( cfg > > ( AC_DEFCFG_LOCATION_SHIFT + 4 ) ) & 3 ] ;
}
2008-11-28 17:17:06 +03:00
EXPORT_SYMBOL_HDA ( snd_hda_get_jack_connectivity ) ;
2008-10-25 09:05:45 +04:00
2009-11-16 16:58:17 +03:00
/**
* snd_hda_get_jack_type - Give a type string of the jack
* @ cfg : pin default config value
*
* Parse the pin default config value and returns the string of the
* jack type , i . e . the purpose of the jack , such as Line - Out or CD .
*/
2008-10-25 09:05:45 +04:00
const char * snd_hda_get_jack_type ( u32 cfg )
{
static char * jack_types [ 16 ] = {
" Line Out " , " Speaker " , " HP Out " , " CD " ,
" SPDIF Out " , " Digital Out " , " Modem Line " , " Modem Hand " ,
" Line In " , " Aux " , " Mic " , " Telephony " ,
" SPDIF In " , " Digitial In " , " Reserved " , " Other "
} ;
return jack_types [ ( cfg & AC_DEFCFG_DEVICE )
> > AC_DEFCFG_DEVICE_SHIFT ] ;
}
2008-11-28 17:17:06 +03:00
EXPORT_SYMBOL_HDA ( snd_hda_get_jack_type ) ;
2008-10-25 09:05:45 +04:00
2008-11-06 18:50:40 +03:00
/*
* Compose a 32 bit command word to be sent to the HD - audio controller
*/
static inline unsigned int
make_codec_cmd ( struct hda_codec * codec , hda_nid_t nid , int direct ,
unsigned int verb , unsigned int parm )
{
u32 val ;
2009-07-17 14:47:34 +04:00
if ( ( codec - > addr & ~ 0xf ) | | ( direct & ~ 1 ) | | ( nid & ~ 0x7f ) | |
( verb & ~ 0xfff ) | | ( parm & ~ 0xffff ) ) {
2009-07-17 12:49:19 +04:00
printk ( KERN_ERR " hda-codec: out of range cmd %x:%x:%x:%x:%x \n " ,
codec - > addr , direct , nid , verb , parm ) ;
return ~ 0 ;
}
val = ( u32 ) codec - > addr < < 28 ;
2008-11-06 18:50:40 +03:00
val | = ( u32 ) direct < < 27 ;
val | = ( u32 ) nid < < 20 ;
val | = verb < < 8 ;
val | = parm ;
return val ;
}
2009-05-26 18:07:57 +04:00
/*
* Send and receive a verb
*/
static int codec_exec_verb ( struct hda_codec * codec , unsigned int cmd ,
unsigned int * res )
{
struct hda_bus * bus = codec - > bus ;
2009-06-02 03:16:07 +04:00
int err ;
2009-05-26 18:07:57 +04:00
2009-07-17 12:49:19 +04:00
if ( cmd = = ~ 0 )
return - 1 ;
2009-05-26 18:07:57 +04:00
if ( res )
* res = - 1 ;
2009-06-02 03:16:07 +04:00
again :
2009-05-26 18:07:57 +04:00
snd_hda_power_up ( codec ) ;
mutex_lock ( & bus - > cmd_mutex ) ;
err = bus - > ops . command ( bus , cmd ) ;
2009-06-02 03:16:07 +04:00
if ( ! err & & res )
2009-08-01 14:45:16 +04:00
* res = bus - > ops . get_response ( bus , codec - > addr ) ;
2009-05-26 18:07:57 +04:00
mutex_unlock ( & bus - > cmd_mutex ) ;
snd_hda_power_down ( codec ) ;
2009-06-02 03:16:07 +04:00
if ( res & & * res = = - 1 & & bus - > rirb_error ) {
if ( bus - > response_reset ) {
snd_printd ( " hda_codec: resetting BUS due to "
" fatal communication error \n " ) ;
bus - > ops . bus_reset ( bus ) ;
}
goto again ;
}
/* clear reset-flag when the communication gets recovered */
if ( ! err )
bus - > response_reset = 0 ;
2009-05-26 18:07:57 +04:00
return err ;
}
2005-04-17 02:20:36 +04:00
/**
* snd_hda_codec_read - send a command and get the response
* @ codec : the HDA codec
* @ nid : NID to send the command
* @ direct : direct flag
* @ verb : the verb to send
* @ parm : the parameter for the verb
*
* Send a single command and read the corresponding response .
*
* Returns the obtained response value , or - 1 for an error .
*/
2007-04-16 13:29:14 +04:00
unsigned int snd_hda_codec_read ( struct hda_codec * codec , hda_nid_t nid ,
int direct ,
2005-04-17 02:20:36 +04:00
unsigned int verb , unsigned int parm )
{
2009-05-26 18:07:57 +04:00
unsigned cmd = make_codec_cmd ( codec , nid , direct , verb , parm ) ;
unsigned int res ;
codec_exec_verb ( codec , cmd , & res ) ;
2005-04-17 02:20:36 +04:00
return res ;
}
2008-11-28 17:17:06 +03:00
EXPORT_SYMBOL_HDA ( snd_hda_codec_read ) ;
2005-04-17 02:20:36 +04:00
/**
* snd_hda_codec_write - send a single command without waiting for response
* @ codec : the HDA codec
* @ nid : NID to send the command
* @ direct : direct flag
* @ verb : the verb to send
* @ parm : the parameter for the verb
*
* Send a single command without waiting for response .
*
* Returns 0 if successful , or a negative error code .
*/
int snd_hda_codec_write ( struct hda_codec * codec , hda_nid_t nid , int direct ,
unsigned int verb , unsigned int parm )
{
2009-05-26 18:07:57 +04:00
unsigned int cmd = make_codec_cmd ( codec , nid , direct , verb , parm ) ;
2008-11-06 18:50:40 +03:00
unsigned int res ;
2009-06-02 03:20:22 +04:00
return codec_exec_verb ( codec , cmd ,
codec - > bus - > sync_write ? & res : NULL ) ;
2005-04-17 02:20:36 +04:00
}
2008-11-28 17:17:06 +03:00
EXPORT_SYMBOL_HDA ( snd_hda_codec_write ) ;
2005-04-17 02:20:36 +04:00
/**
* snd_hda_sequence_write - sequence writes
* @ codec : the HDA codec
* @ seq : VERB array to send
*
* Send the commands sequentially from the given array .
* The array must be terminated with NID = 0.
*/
void snd_hda_sequence_write ( struct hda_codec * codec , const struct hda_verb * seq )
{
for ( ; seq - > nid ; seq + + )
snd_hda_codec_write ( codec , seq - > nid , 0 , seq - > verb , seq - > param ) ;
}
2008-11-28 17:17:06 +03:00
EXPORT_SYMBOL_HDA ( snd_hda_sequence_write ) ;
2005-04-17 02:20:36 +04:00
/**
* snd_hda_get_sub_nodes - get the range of sub nodes
* @ codec : the HDA codec
* @ nid : NID to parse
* @ start_id : the pointer to store the start NID
*
* Parse the NID and store the start NID of its sub - nodes .
* Returns the number of sub - nodes .
*/
2007-04-16 13:29:14 +04:00
int snd_hda_get_sub_nodes ( struct hda_codec * codec , hda_nid_t nid ,
hda_nid_t * start_id )
2005-04-17 02:20:36 +04:00
{
unsigned int parm ;
parm = snd_hda_param_read ( codec , nid , AC_PAR_NODE_COUNT ) ;
2007-09-11 23:41:56 +04:00
if ( parm = = - 1 )
return 0 ;
2005-04-17 02:20:36 +04:00
* start_id = ( parm > > 16 ) & 0x7fff ;
return ( int ) ( parm & 0x7fff ) ;
}
2008-11-28 17:17:06 +03:00
EXPORT_SYMBOL_HDA ( snd_hda_get_sub_nodes ) ;
2005-04-17 02:20:36 +04:00
/**
* snd_hda_get_connections - get connection list
* @ codec : the HDA codec
* @ nid : NID to parse
* @ conn_list : connection list array
* @ max_conns : max . number of connections to store
*
* Parses the connection list of the given widget and stores the list
* of NIDs .
*
* Returns the number of connections , or a negative error code .
*/
int snd_hda_get_connections ( struct hda_codec * codec , hda_nid_t nid ,
hda_nid_t * conn_list , int max_conns )
{
unsigned int parm ;
2005-11-21 18:33:22 +03:00
int i , conn_len , conns ;
2005-04-17 02:20:36 +04:00
unsigned int shift , num_elems , mask ;
2009-07-27 14:56:26 +04:00
unsigned int wcaps ;
2005-11-21 18:33:22 +03:00
hda_nid_t prev_nid ;
2005-04-17 02:20:36 +04:00
2008-08-08 19:12:14 +04:00
if ( snd_BUG_ON ( ! conn_list | | max_conns < = 0 ) )
return - EINVAL ;
2005-04-17 02:20:36 +04:00
2009-07-27 14:56:26 +04:00
wcaps = get_wcaps ( codec , nid ) ;
if ( ! ( wcaps & AC_WCAP_CONN_LIST ) & &
get_wcaps_type ( wcaps ) ! = AC_WID_VOL_KNB ) {
2009-07-22 18:20:40 +04:00
snd_printk ( KERN_WARNING " hda_codec: "
" connection list not available for 0x%x \n " , nid ) ;
return - EINVAL ;
}
2005-04-17 02:20:36 +04:00
parm = snd_hda_param_read ( codec , nid , AC_PAR_CONNLIST_LEN ) ;
if ( parm & AC_CLIST_LONG ) {
/* long form */
shift = 16 ;
num_elems = 2 ;
} else {
/* short form */
shift = 8 ;
num_elems = 4 ;
}
conn_len = parm & AC_CLIST_LENGTH ;
mask = ( 1 < < ( shift - 1 ) ) - 1 ;
2007-04-16 13:29:14 +04:00
if ( ! conn_len )
2005-04-17 02:20:36 +04:00
return 0 ; /* no connection */
if ( conn_len = = 1 ) {
/* single connection */
2007-04-16 13:29:14 +04:00
parm = snd_hda_codec_read ( codec , nid , 0 ,
AC_VERB_GET_CONNECT_LIST , 0 ) ;
2009-07-10 14:52:27 +04:00
if ( parm = = - 1 & & codec - > bus - > rirb_error )
return - EIO ;
2005-04-17 02:20:36 +04:00
conn_list [ 0 ] = parm & mask ;
return 1 ;
}
/* multi connection */
conns = 0 ;
2005-11-21 18:33:22 +03:00
prev_nid = 0 ;
for ( i = 0 ; i < conn_len ; i + + ) {
int range_val ;
hda_nid_t val , n ;
2009-07-10 14:52:27 +04:00
if ( i % num_elems = = 0 ) {
2005-11-21 18:33:22 +03:00
parm = snd_hda_codec_read ( codec , nid , 0 ,
AC_VERB_GET_CONNECT_LIST , i ) ;
2009-07-10 14:52:27 +04:00
if ( parm = = - 1 & & codec - > bus - > rirb_error )
return - EIO ;
}
2007-04-16 13:29:14 +04:00
range_val = ! ! ( parm & ( 1 < < ( shift - 1 ) ) ) ; /* ranges */
2005-11-21 18:33:22 +03:00
val = parm & mask ;
2009-07-18 13:48:19 +04:00
if ( val = = 0 ) {
snd_printk ( KERN_WARNING " hda_codec: "
" invalid CONNECT_LIST verb %x[%i]:%x \n " ,
nid , i , parm ) ;
return 0 ;
}
2005-11-21 18:33:22 +03:00
parm > > = shift ;
if ( range_val ) {
/* ranges between the previous and this one */
2007-04-16 13:29:14 +04:00
if ( ! prev_nid | | prev_nid > = val ) {
snd_printk ( KERN_WARNING " hda_codec: "
" invalid dep_range_val %x:%x \n " ,
prev_nid , val ) ;
2005-11-21 18:33:22 +03:00
continue ;
}
for ( n = prev_nid + 1 ; n < = val ; n + + ) {
if ( conns > = max_conns ) {
2010-07-30 12:36:29 +04:00
snd_printk ( KERN_ERR " hda_codec: "
" Too many connections %d for NID 0x%x \n " ,
conns , nid ) ;
2005-04-17 02:20:36 +04:00
return - EINVAL ;
2005-11-21 18:33:22 +03:00
}
conn_list [ conns + + ] = n ;
2005-04-17 02:20:36 +04:00
}
2005-11-21 18:33:22 +03:00
} else {
if ( conns > = max_conns ) {
2010-07-30 12:36:29 +04:00
snd_printk ( KERN_ERR " hda_codec: "
" Too many connections %d for NID 0x%x \n " ,
conns , nid ) ;
2005-11-21 18:33:22 +03:00
return - EINVAL ;
}
conn_list [ conns + + ] = val ;
2005-04-17 02:20:36 +04:00
}
2005-11-21 18:33:22 +03:00
prev_nid = val ;
2005-04-17 02:20:36 +04:00
}
return conns ;
}
2008-11-28 17:17:06 +03:00
EXPORT_SYMBOL_HDA ( snd_hda_get_connections ) ;
2005-04-17 02:20:36 +04:00
/**
* snd_hda_queue_unsol_event - add an unsolicited event to queue
* @ bus : the BUS
* @ res : unsolicited event ( lower 32 bit of RIRB entry )
* @ res_ex : codec addr and flags ( upper 32 bit or RIRB entry )
*
* Adds the given event to the queue . The events are processed in
* the workqueue asynchronously . Call this function in the interrupt
* hanlder when RIRB receives an unsolicited event .
*
* Returns 0 if successful , or a negative error code .
*/
int snd_hda_queue_unsol_event ( struct hda_bus * bus , u32 res , u32 res_ex )
{
struct hda_bus_unsolicited * unsol ;
unsigned int wp ;
2007-04-16 13:29:14 +04:00
unsol = bus - > unsol ;
if ( ! unsol )
2005-04-17 02:20:36 +04:00
return 0 ;
wp = ( unsol - > wp + 1 ) % HDA_UNSOL_QUEUE_SIZE ;
unsol - > wp = wp ;
wp < < = 1 ;
unsol - > queue [ wp ] = res ;
unsol - > queue [ wp + 1 ] = res_ex ;
2009-01-12 12:09:24 +03:00
queue_work ( bus - > workq , & unsol - > work ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
2008-11-28 17:17:06 +03:00
EXPORT_SYMBOL_HDA ( snd_hda_queue_unsol_event ) ;
2005-04-17 02:20:36 +04:00
/*
2008-10-07 10:17:53 +04:00
* process queued unsolicited events
2005-04-17 02:20:36 +04:00
*/
2006-11-22 17:57:56 +03:00
static void process_unsol_events ( struct work_struct * work )
2005-04-17 02:20:36 +04:00
{
2006-11-22 17:57:56 +03:00
struct hda_bus_unsolicited * unsol =
container_of ( work , struct hda_bus_unsolicited , work ) ;
struct hda_bus * bus = unsol - > bus ;
2005-04-17 02:20:36 +04:00
struct hda_codec * codec ;
unsigned int rp , caddr , res ;
while ( unsol - > rp ! = unsol - > wp ) {
rp = ( unsol - > rp + 1 ) % HDA_UNSOL_QUEUE_SIZE ;
unsol - > rp = rp ;
rp < < = 1 ;
res = unsol - > queue [ rp ] ;
caddr = unsol - > queue [ rp + 1 ] ;
2007-04-16 13:29:14 +04:00
if ( ! ( caddr & ( 1 < < 4 ) ) ) /* no unsolicited event? */
2005-04-17 02:20:36 +04:00
continue ;
codec = bus - > caddr_tbl [ caddr & 0x0f ] ;
if ( codec & & codec - > patch_ops . unsol_event )
codec - > patch_ops . unsol_event ( codec , res ) ;
}
}
/*
* initialize unsolicited queue
*/
2008-07-30 17:01:45 +04:00
static int init_unsol_queue ( struct hda_bus * bus )
2005-04-17 02:20:36 +04:00
{
struct hda_bus_unsolicited * unsol ;
2005-11-17 13:07:49 +03:00
if ( bus - > unsol ) /* already initialized */
return 0 ;
[ALSA] Replace with kzalloc() - pci stuff
AD1889 driver,ATIIXP driver,ATIIXP-modem driver,AZT3328 driver
BT87x driver,CMIPCI driver,CS4281 driver,ENS1370/1+ driver
ES1938 driver,ES1968 driver,FM801 driver,Intel8x0 driver
Intel8x0-modem driver,Maestro3 driver,SonicVibes driver,VIA82xx driver
VIA82xx-modem driver,AC97 Codec,AK4531 codec,au88x0 driver
CA0106 driver,CS46xx driver,EMU10K1/EMU10K2 driver,HDA Codec driver
HDA generic driver,HDA Intel driver,ICE1712 driver,ICE1724 driver
KORG1212 driver,MIXART driver,NM256 driver,Trident driver,YMFPCI driver
Replace kcalloc(1,..) with kzalloc().
Signed-off-by: Takashi Iwai <tiwai@suse.de>
2005-09-09 16:21:46 +04:00
unsol = kzalloc ( sizeof ( * unsol ) , GFP_KERNEL ) ;
2007-04-16 13:29:14 +04:00
if ( ! unsol ) {
snd_printk ( KERN_ERR " hda_codec: "
" can't allocate unsolicited queue \n " ) ;
2005-04-17 02:20:36 +04:00
return - ENOMEM ;
}
2006-11-22 17:57:56 +03:00
INIT_WORK ( & unsol - > work , process_unsol_events ) ;
unsol - > bus = bus ;
2005-04-17 02:20:36 +04:00
bus - > unsol = unsol ;
return 0 ;
}
/*
* destructor
*/
static void snd_hda_codec_free ( struct hda_codec * codec ) ;
static int snd_hda_bus_free ( struct hda_bus * bus )
{
2007-04-16 13:29:14 +04:00
struct hda_codec * codec , * n ;
2005-04-17 02:20:36 +04:00
2007-04-16 13:29:14 +04:00
if ( ! bus )
2005-04-17 02:20:36 +04:00
return 0 ;
2009-01-12 12:09:24 +03:00
if ( bus - > workq )
flush_workqueue ( bus - > workq ) ;
if ( bus - > unsol )
2005-04-17 02:20:36 +04:00
kfree ( bus - > unsol ) ;
2007-04-16 13:29:14 +04:00
list_for_each_entry_safe ( codec , n , & bus - > codec_list , list ) {
2005-04-17 02:20:36 +04:00
snd_hda_codec_free ( codec ) ;
}
if ( bus - > ops . private_free )
bus - > ops . private_free ( bus ) ;
2009-01-12 12:09:24 +03:00
if ( bus - > workq )
destroy_workqueue ( bus - > workq ) ;
2005-04-17 02:20:36 +04:00
kfree ( bus ) ;
return 0 ;
}
2005-11-17 16:57:47 +03:00
static int snd_hda_bus_dev_free ( struct snd_device * device )
2005-04-17 02:20:36 +04:00
{
struct hda_bus * bus = device - > device_data ;
2008-11-21 11:08:06 +03:00
bus - > shutdown = 1 ;
2005-04-17 02:20:36 +04:00
return snd_hda_bus_free ( bus ) ;
}
2008-07-30 17:01:46 +04:00
# ifdef CONFIG_SND_HDA_HWDEP
static int snd_hda_bus_dev_register ( struct snd_device * device )
{
struct hda_bus * bus = device - > device_data ;
struct hda_codec * codec ;
list_for_each_entry ( codec , & bus - > codec_list , list ) {
snd_hda_hwdep_add_sysfs ( codec ) ;
2009-11-11 11:34:25 +03:00
snd_hda_hwdep_add_power_sysfs ( codec ) ;
2008-07-30 17:01:46 +04:00
}
return 0 ;
}
# else
# define snd_hda_bus_dev_register NULL
# endif
2005-04-17 02:20:36 +04:00
/**
* snd_hda_bus_new - create a HDA bus
* @ card : the card entry
* @ temp : the template for hda_bus information
* @ busp : the pointer to store the created bus instance
*
* Returns 0 if successful , or a negative error code .
*/
2008-11-27 17:47:11 +03:00
int /*__devinit*/ snd_hda_bus_new ( struct snd_card * card ,
2007-04-16 13:27:07 +04:00
const struct hda_bus_template * temp ,
struct hda_bus * * busp )
2005-04-17 02:20:36 +04:00
{
struct hda_bus * bus ;
int err ;
2005-11-17 16:57:47 +03:00
static struct snd_device_ops dev_ops = {
2008-07-30 17:01:46 +04:00
. dev_register = snd_hda_bus_dev_register ,
2005-04-17 02:20:36 +04:00
. dev_free = snd_hda_bus_dev_free ,
} ;
2008-08-08 19:12:14 +04:00
if ( snd_BUG_ON ( ! temp ) )
return - EINVAL ;
if ( snd_BUG_ON ( ! temp - > ops . command | | ! temp - > ops . get_response ) )
return - EINVAL ;
2005-04-17 02:20:36 +04:00
if ( busp )
* busp = NULL ;
[ALSA] Replace with kzalloc() - pci stuff
AD1889 driver,ATIIXP driver,ATIIXP-modem driver,AZT3328 driver
BT87x driver,CMIPCI driver,CS4281 driver,ENS1370/1+ driver
ES1938 driver,ES1968 driver,FM801 driver,Intel8x0 driver
Intel8x0-modem driver,Maestro3 driver,SonicVibes driver,VIA82xx driver
VIA82xx-modem driver,AC97 Codec,AK4531 codec,au88x0 driver
CA0106 driver,CS46xx driver,EMU10K1/EMU10K2 driver,HDA Codec driver
HDA generic driver,HDA Intel driver,ICE1712 driver,ICE1724 driver
KORG1212 driver,MIXART driver,NM256 driver,Trident driver,YMFPCI driver
Replace kcalloc(1,..) with kzalloc().
Signed-off-by: Takashi Iwai <tiwai@suse.de>
2005-09-09 16:21:46 +04:00
bus = kzalloc ( sizeof ( * bus ) , GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
if ( bus = = NULL ) {
snd_printk ( KERN_ERR " can't allocate struct hda_bus \n " ) ;
return - ENOMEM ;
}
bus - > card = card ;
bus - > private_data = temp - > private_data ;
bus - > pci = temp - > pci ;
bus - > modelname = temp - > modelname ;
2008-11-27 14:43:28 +03:00
bus - > power_save = temp - > power_save ;
2005-04-17 02:20:36 +04:00
bus - > ops = temp - > ops ;
2006-01-16 18:34:20 +03:00
mutex_init ( & bus - > cmd_mutex ) ;
2010-08-20 11:44:36 +04:00
mutex_init ( & bus - > prepare_mutex ) ;
2005-04-17 02:20:36 +04:00
INIT_LIST_HEAD ( & bus - > codec_list ) ;
2009-02-05 09:34:28 +03:00
snprintf ( bus - > workq_name , sizeof ( bus - > workq_name ) ,
" hd-audio%d " , card - > number ) ;
bus - > workq = create_singlethread_workqueue ( bus - > workq_name ) ;
2009-01-12 12:09:24 +03:00
if ( ! bus - > workq ) {
2009-02-05 09:34:28 +03:00
snd_printk ( KERN_ERR " cannot create workqueue %s \n " ,
bus - > workq_name ) ;
2009-01-12 12:09:24 +03:00
kfree ( bus ) ;
return - ENOMEM ;
}
2007-04-16 13:29:14 +04:00
err = snd_device_new ( card , SNDRV_DEV_BUS , bus , & dev_ops ) ;
if ( err < 0 ) {
2005-04-17 02:20:36 +04:00
snd_hda_bus_free ( bus ) ;
return err ;
}
if ( busp )
* busp = bus ;
return 0 ;
}
2008-11-28 17:17:06 +03:00
EXPORT_SYMBOL_HDA ( snd_hda_bus_new ) ;
2005-04-17 02:20:36 +04:00
2007-07-27 21:15:54 +04:00
# ifdef CONFIG_SND_HDA_GENERIC
# define is_generic_config(codec) \
2008-07-30 17:01:45 +04:00
( codec - > modelname & & ! strcmp ( codec - > modelname , " generic " ) )
2007-07-27 21:15:54 +04:00
# else
# define is_generic_config(codec) 0
# endif
2008-11-28 17:07:37 +03:00
# ifdef MODULE
2008-11-27 17:47:11 +03:00
# define HDA_MODREQ_MAX_COUNT 2 /* two request_modules()'s */
# else
2008-11-28 17:07:37 +03:00
# define HDA_MODREQ_MAX_COUNT 0 /* all presets are statically linked */
2008-11-27 17:47:11 +03:00
# endif
2005-04-17 02:20:36 +04:00
/*
* find a matching codec preset
*/
2008-07-30 17:01:45 +04:00
static const struct hda_codec_preset *
2007-04-16 13:27:07 +04:00
find_codec_preset ( struct hda_codec * codec )
2005-04-17 02:20:36 +04:00
{
2008-11-27 17:47:11 +03:00
struct hda_codec_preset_list * tbl ;
const struct hda_codec_preset * preset ;
int mod_requested = 0 ;
2005-04-17 02:20:36 +04:00
2007-07-27 21:15:54 +04:00
if ( is_generic_config ( codec ) )
2007-03-07 17:55:59 +03:00
return NULL ; /* use the generic parser */
2008-11-27 17:47:11 +03:00
again :
mutex_lock ( & preset_mutex ) ;
list_for_each_entry ( tbl , & hda_preset_tables , list ) {
if ( ! try_module_get ( tbl - > owner ) ) {
snd_printk ( KERN_ERR " hda_codec: cannot module_get \n " ) ;
continue ;
}
for ( preset = tbl - > preset ; preset - > id ; preset + + ) {
2005-04-17 02:20:36 +04:00
u32 mask = preset - > mask ;
2008-01-22 17:32:25 +03:00
if ( preset - > afg & & preset - > afg ! = codec - > afg )
continue ;
if ( preset - > mfg & & preset - > mfg ! = codec - > mfg )
continue ;
2007-04-16 13:29:14 +04:00
if ( ! mask )
2005-04-17 02:20:36 +04:00
mask = ~ 0 ;
2006-06-28 17:08:22 +04:00
if ( preset - > id = = ( codec - > vendor_id & mask ) & &
2007-04-16 13:29:14 +04:00
( ! preset - > rev | |
2008-11-27 17:47:11 +03:00
preset - > rev = = codec - > revision_id ) ) {
mutex_unlock ( & preset_mutex ) ;
codec - > owner = tbl - > owner ;
2005-04-17 02:20:36 +04:00
return preset ;
2008-11-27 17:47:11 +03:00
}
2005-04-17 02:20:36 +04:00
}
2008-11-27 17:47:11 +03:00
module_put ( tbl - > owner ) ;
}
mutex_unlock ( & preset_mutex ) ;
if ( mod_requested < HDA_MODREQ_MAX_COUNT ) {
char name [ 32 ] ;
if ( ! mod_requested )
snprintf ( name , sizeof ( name ) , " snd-hda-codec-id:%08x " ,
codec - > vendor_id ) ;
else
snprintf ( name , sizeof ( name ) , " snd-hda-codec-id:%04x* " ,
( codec - > vendor_id > > 16 ) & 0xffff ) ;
request_module ( name ) ;
mod_requested + + ;
goto again ;
2005-04-17 02:20:36 +04:00
}
return NULL ;
}
/*
2008-07-30 17:01:45 +04:00
* get_codec_name - store the codec name
2005-04-17 02:20:36 +04:00
*/
2008-07-30 17:01:45 +04:00
static int get_codec_name ( struct hda_codec * codec )
2005-04-17 02:20:36 +04:00
{
const struct hda_vendor_id * c ;
const char * vendor = NULL ;
u16 vendor_id = codec - > vendor_id > > 16 ;
2009-05-16 12:00:49 +04:00
char tmp [ 16 ] ;
if ( codec - > vendor_name )
goto get_chip_name ;
2005-04-17 02:20:36 +04:00
for ( c = hda_vendor_ids ; c - > id ; c + + ) {
if ( c - > id = = vendor_id ) {
vendor = c - > name ;
break ;
}
}
2007-04-16 13:29:14 +04:00
if ( ! vendor ) {
2005-04-17 02:20:36 +04:00
sprintf ( tmp , " Generic %04x " , vendor_id ) ;
vendor = tmp ;
}
2009-05-16 12:00:49 +04:00
codec - > vendor_name = kstrdup ( vendor , GFP_KERNEL ) ;
if ( ! codec - > vendor_name )
return - ENOMEM ;
get_chip_name :
if ( codec - > chip_name )
return 0 ;
2005-04-17 02:20:36 +04:00
if ( codec - > preset & & codec - > preset - > name )
2009-05-16 12:00:49 +04:00
codec - > chip_name = kstrdup ( codec - > preset - > name , GFP_KERNEL ) ;
else {
sprintf ( tmp , " ID %x " , codec - > vendor_id & 0xffff ) ;
codec - > chip_name = kstrdup ( tmp , GFP_KERNEL ) ;
}
if ( ! codec - > chip_name )
2008-07-30 17:01:45 +04:00
return - ENOMEM ;
return 0 ;
2005-04-17 02:20:36 +04:00
}
/*
2005-08-11 13:00:16 +04:00
* look for an AFG and MFG nodes
2005-04-17 02:20:36 +04:00
*/
2008-11-27 17:47:11 +03:00
static void /*__devinit*/ setup_fg_nodes ( struct hda_codec * codec )
2005-04-17 02:20:36 +04:00
{
2009-04-17 20:04:41 +04:00
int i , total_nodes , function_id ;
2005-04-17 02:20:36 +04:00
hda_nid_t nid ;
total_nodes = snd_hda_get_sub_nodes ( codec , AC_NODE_ROOT , & nid ) ;
for ( i = 0 ; i < total_nodes ; i + + , nid + + ) {
2009-04-17 20:04:41 +04:00
function_id = snd_hda_param_read ( codec , nid ,
2010-07-19 17:52:39 +04:00
AC_PAR_FUNCTION_TYPE ) ;
2010-07-20 14:11:25 +04:00
switch ( function_id & 0xff ) {
2005-08-11 13:00:16 +04:00
case AC_GRP_AUDIO_FUNCTION :
codec - > afg = nid ;
2010-07-19 17:52:39 +04:00
codec - > afg_function_id = function_id & 0xff ;
codec - > afg_unsol = ( function_id > > 8 ) & 1 ;
2005-08-11 13:00:16 +04:00
break ;
case AC_GRP_MODEM_FUNCTION :
codec - > mfg = nid ;
2010-07-19 17:52:39 +04:00
codec - > mfg_function_id = function_id & 0xff ;
codec - > mfg_unsol = ( function_id > > 8 ) & 1 ;
2005-08-11 13:00:16 +04:00
break ;
default :
break ;
}
2005-04-17 02:20:36 +04:00
}
}
2005-11-21 18:33:22 +03:00
/*
* read widget caps for each widget and store in cache
*/
static int read_widget_caps ( struct hda_codec * codec , hda_nid_t fg_node )
{
int i ;
hda_nid_t nid ;
codec - > num_nodes = snd_hda_get_sub_nodes ( codec , fg_node ,
& codec - > start_nid ) ;
codec - > wcaps = kmalloc ( codec - > num_nodes * 4 , GFP_KERNEL ) ;
2007-04-16 13:29:14 +04:00
if ( ! codec - > wcaps )
2005-11-21 18:33:22 +03:00
return - ENOMEM ;
nid = codec - > start_nid ;
for ( i = 0 ; i < codec - > num_nodes ; i + + , nid + + )
codec - > wcaps [ i ] = snd_hda_param_read ( codec , nid ,
AC_PAR_AUDIO_WIDGET_CAP ) ;
return 0 ;
}
2009-02-20 16:11:16 +03:00
/* read all pin default configurations and save codec->init_pins */
static int read_pin_defaults ( struct hda_codec * codec )
{
int i ;
hda_nid_t nid = codec - > start_nid ;
for ( i = 0 ; i < codec - > num_nodes ; i + + , nid + + ) {
struct hda_pincfg * pin ;
unsigned int wcaps = get_wcaps ( codec , nid ) ;
2009-07-27 14:54:26 +04:00
unsigned int wid_type = get_wcaps_type ( wcaps ) ;
2009-02-20 16:11:16 +03:00
if ( wid_type ! = AC_WID_PIN )
continue ;
pin = snd_array_new ( & codec - > init_pins ) ;
if ( ! pin )
return - ENOMEM ;
pin - > nid = nid ;
pin - > cfg = snd_hda_codec_read ( codec , nid , 0 ,
AC_VERB_GET_CONFIG_DEFAULT , 0 ) ;
2010-07-05 18:50:13 +04:00
pin - > ctrl = snd_hda_codec_read ( codec , nid , 0 ,
AC_VERB_GET_PIN_WIDGET_CONTROL ,
0 ) ;
2009-02-20 16:11:16 +03:00
}
return 0 ;
}
/* look up the given pin config list and return the item matching with NID */
static struct hda_pincfg * look_up_pincfg ( struct hda_codec * codec ,
struct snd_array * array ,
hda_nid_t nid )
{
int i ;
for ( i = 0 ; i < array - > used ; i + + ) {
struct hda_pincfg * pin = snd_array_elem ( array , i ) ;
if ( pin - > nid = = nid )
return pin ;
}
return NULL ;
}
/* write a config value for the given NID */
static void set_pincfg ( struct hda_codec * codec , hda_nid_t nid ,
unsigned int cfg )
{
int i ;
for ( i = 0 ; i < 4 ; i + + ) {
snd_hda_codec_write ( codec , nid , 0 ,
AC_VERB_SET_CONFIG_DEFAULT_BYTES_0 + i ,
cfg & 0xff ) ;
cfg > > = 8 ;
}
}
/* set the current pin config value for the given NID.
* the value is cached , and read via snd_hda_codec_get_pincfg ( )
*/
int snd_hda_add_pincfg ( struct hda_codec * codec , struct snd_array * list ,
hda_nid_t nid , unsigned int cfg )
{
struct hda_pincfg * pin ;
2009-02-23 11:45:59 +03:00
unsigned int oldcfg ;
2009-02-20 16:11:16 +03:00
2009-12-27 13:24:56 +03:00
if ( get_wcaps_type ( get_wcaps ( codec , nid ) ) ! = AC_WID_PIN )
return - EINVAL ;
2009-02-23 11:45:59 +03:00
oldcfg = snd_hda_codec_get_pincfg ( codec , nid ) ;
2009-02-20 16:11:16 +03:00
pin = look_up_pincfg ( codec , list , nid ) ;
if ( ! pin ) {
pin = snd_array_new ( list ) ;
if ( ! pin )
return - ENOMEM ;
pin - > nid = nid ;
}
pin - > cfg = cfg ;
2009-02-23 11:45:59 +03:00
/* change only when needed; e.g. if the pincfg is already present
* in user_pins [ ] , don ' t write it
*/
cfg = snd_hda_codec_get_pincfg ( codec , nid ) ;
if ( oldcfg ! = cfg )
set_pincfg ( codec , nid , cfg ) ;
2009-02-20 16:11:16 +03:00
return 0 ;
}
2009-11-16 16:58:17 +03:00
/**
* snd_hda_codec_set_pincfg - Override a pin default configuration
* @ codec : the HDA codec
* @ nid : NID to set the pin config
* @ cfg : the pin default config value
*
* Override a pin default configuration value in the cache .
* This value can be read by snd_hda_codec_get_pincfg ( ) in a higher
* priority than the real hardware value .
*/
2009-02-20 16:11:16 +03:00
int snd_hda_codec_set_pincfg ( struct hda_codec * codec ,
hda_nid_t nid , unsigned int cfg )
{
2009-02-23 11:42:57 +03:00
return snd_hda_add_pincfg ( codec , & codec - > driver_pins , nid , cfg ) ;
2009-02-20 16:11:16 +03:00
}
EXPORT_SYMBOL_HDA ( snd_hda_codec_set_pincfg ) ;
2009-11-16 16:58:17 +03:00
/**
* snd_hda_codec_get_pincfg - Obtain a pin - default configuration
* @ codec : the HDA codec
* @ nid : NID to get the pin config
*
* Get the current pin config value of the given pin NID .
* If the pincfg value is cached or overridden via sysfs or driver ,
* returns the cached value .
*/
2009-02-20 16:11:16 +03:00
unsigned int snd_hda_codec_get_pincfg ( struct hda_codec * codec , hda_nid_t nid )
{
struct hda_pincfg * pin ;
# ifdef CONFIG_SND_HDA_HWDEP
2009-02-23 11:42:57 +03:00
pin = look_up_pincfg ( codec , & codec - > user_pins , nid ) ;
2009-02-20 16:11:16 +03:00
if ( pin )
return pin - > cfg ;
# endif
2009-02-23 11:45:59 +03:00
pin = look_up_pincfg ( codec , & codec - > driver_pins , nid ) ;
if ( pin )
return pin - > cfg ;
2009-02-20 16:11:16 +03:00
pin = look_up_pincfg ( codec , & codec - > init_pins , nid ) ;
if ( pin )
return pin - > cfg ;
return 0 ;
}
EXPORT_SYMBOL_HDA ( snd_hda_codec_get_pincfg ) ;
/* restore all current pin configs */
static void restore_pincfgs ( struct hda_codec * codec )
{
int i ;
for ( i = 0 ; i < codec - > init_pins . used ; i + + ) {
struct hda_pincfg * pin = snd_array_elem ( & codec - > init_pins , i ) ;
set_pincfg ( codec , pin - > nid ,
snd_hda_codec_get_pincfg ( codec , pin - > nid ) ) ;
}
}
2005-11-21 18:33:22 +03:00
2009-12-27 13:18:59 +03:00
/**
* snd_hda_shutup_pins - Shut up all pins
* @ codec : the HDA codec
*
* Clear all pin controls to shup up before suspend for avoiding click noise .
* The controls aren ' t cached so that they can be resumed properly .
*/
void snd_hda_shutup_pins ( struct hda_codec * codec )
{
int i ;
2010-07-05 18:50:13 +04:00
/* don't shut up pins when unloading the driver; otherwise it breaks
* the default pin setup at the next load of the driver
*/
if ( codec - > bus - > shutdown )
return ;
2009-12-27 13:18:59 +03:00
for ( i = 0 ; i < codec - > init_pins . used ; i + + ) {
struct hda_pincfg * pin = snd_array_elem ( & codec - > init_pins , i ) ;
/* use read here for syncing after issuing each verb */
snd_hda_codec_read ( codec , pin - > nid , 0 ,
AC_VERB_SET_PIN_WIDGET_CONTROL , 0 ) ;
}
2010-07-05 18:50:13 +04:00
codec - > pins_shutup = 1 ;
2009-12-27 13:18:59 +03:00
}
EXPORT_SYMBOL_HDA ( snd_hda_shutup_pins ) ;
2011-04-20 23:04:36 +04:00
# ifdef SND_HDA_NEEDS_RESUME
2010-07-05 18:50:13 +04:00
/* Restore the pin controls cleared previously via snd_hda_shutup_pins() */
static void restore_shutup_pins ( struct hda_codec * codec )
{
int i ;
if ( ! codec - > pins_shutup )
return ;
if ( codec - > bus - > shutdown )
return ;
for ( i = 0 ; i < codec - > init_pins . used ; i + + ) {
struct hda_pincfg * pin = snd_array_elem ( & codec - > init_pins , i ) ;
snd_hda_codec_write ( codec , pin - > nid , 0 ,
AC_VERB_SET_PIN_WIDGET_CONTROL ,
pin - > ctrl ) ;
}
codec - > pins_shutup = 0 ;
}
2011-04-20 23:04:36 +04:00
# endif
2010-07-05 18:50:13 +04:00
2007-08-10 18:59:39 +04:00
static void init_hda_cache ( struct hda_cache_rec * cache ,
unsigned int record_size ) ;
2007-08-23 02:01:09 +04:00
static void free_hda_cache ( struct hda_cache_rec * cache ) ;
2007-08-10 18:59:39 +04:00
2009-02-20 16:11:16 +03:00
/* restore the initial pin cfgs and release all pincfg lists */
static void restore_init_pincfgs ( struct hda_codec * codec )
{
2009-02-23 11:42:57 +03:00
/* first free driver_pins and user_pins, then call restore_pincfg
2009-02-20 16:11:16 +03:00
* so that only the values in init_pins are restored
*/
2009-02-23 11:42:57 +03:00
snd_array_free ( & codec - > driver_pins ) ;
2009-02-20 16:11:16 +03:00
# ifdef CONFIG_SND_HDA_HWDEP
2009-02-23 11:42:57 +03:00
snd_array_free ( & codec - > user_pins ) ;
2009-02-20 16:11:16 +03:00
# endif
restore_pincfgs ( codec ) ;
snd_array_free ( & codec - > init_pins ) ;
}
2010-08-06 15:48:11 +04:00
/*
* audio - converter setup caches
*/
struct hda_cvt_setup {
hda_nid_t nid ;
u8 stream_tag ;
u8 channel_id ;
u16 format_id ;
unsigned char active ; /* cvt is currently used */
unsigned char dirty ; /* setups should be cleared */
} ;
/* get or create a cache entry for the given audio converter NID */
static struct hda_cvt_setup *
get_hda_cvt_setup ( struct hda_codec * codec , hda_nid_t nid )
{
struct hda_cvt_setup * p ;
int i ;
for ( i = 0 ; i < codec - > cvt_setups . used ; i + + ) {
p = snd_array_elem ( & codec - > cvt_setups , i ) ;
if ( p - > nid = = nid )
return p ;
}
p = snd_array_new ( & codec - > cvt_setups ) ;
if ( p )
p - > nid = nid ;
return p ;
}
2005-04-17 02:20:36 +04:00
/*
* codec destructor
*/
static void snd_hda_codec_free ( struct hda_codec * codec )
{
2007-04-16 13:29:14 +04:00
if ( ! codec )
2005-04-17 02:20:36 +04:00
return ;
2009-02-20 16:11:16 +03:00
restore_init_pincfgs ( codec ) ;
2007-08-10 19:21:45 +04:00
# ifdef CONFIG_SND_HDA_POWER_SAVE
cancel_delayed_work ( & codec - > power_work ) ;
2009-01-12 12:09:24 +03:00
flush_workqueue ( codec - > bus - > workq ) ;
2007-08-10 19:21:45 +04:00
# endif
2005-04-17 02:20:36 +04:00
list_del ( & codec - > list ) ;
2008-07-30 17:01:45 +04:00
snd_array_free ( & codec - > mixers ) ;
2009-12-08 18:13:32 +03:00
snd_array_free ( & codec - > nids ) ;
2005-04-17 02:20:36 +04:00
codec - > bus - > caddr_tbl [ codec - > addr ] = NULL ;
if ( codec - > patch_ops . free )
codec - > patch_ops . free ( codec ) ;
2008-11-27 17:47:11 +03:00
module_put ( codec - > owner ) ;
2007-08-10 18:59:39 +04:00
free_hda_cache ( & codec - > amp_cache ) ;
2007-08-10 19:03:40 +04:00
free_hda_cache ( & codec - > cmd_cache ) ;
2009-05-16 12:00:49 +04:00
kfree ( codec - > vendor_name ) ;
kfree ( codec - > chip_name ) ;
2008-07-30 17:01:45 +04:00
kfree ( codec - > modelname ) ;
2005-11-21 18:33:22 +03:00
kfree ( codec - > wcaps ) ;
2005-04-17 02:20:36 +04:00
kfree ( codec ) ;
}
2009-03-13 11:02:42 +03:00
static void hda_set_power_state ( struct hda_codec * codec , hda_nid_t fg ,
unsigned int power_state ) ;
2005-04-17 02:20:36 +04:00
/**
* snd_hda_codec_new - create a HDA codec
* @ bus : the bus to assign
* @ codec_addr : the codec address
* @ codecp : the pointer to store the generated codec
*
* Returns 0 if successful , or a negative error code .
*/
2010-02-28 22:16:53 +03:00
int /*__devinit*/ snd_hda_codec_new ( struct hda_bus * bus ,
unsigned int codec_addr ,
struct hda_codec * * codecp )
2005-04-17 02:20:36 +04:00
{
struct hda_codec * codec ;
2008-08-13 22:55:32 +04:00
char component [ 31 ] ;
2005-04-17 02:20:36 +04:00
int err ;
2008-08-08 19:12:14 +04:00
if ( snd_BUG_ON ( ! bus ) )
return - EINVAL ;
if ( snd_BUG_ON ( codec_addr > HDA_MAX_CODEC_ADDRESS ) )
return - EINVAL ;
2005-04-17 02:20:36 +04:00
if ( bus - > caddr_tbl [ codec_addr ] ) {
2007-04-16 13:29:14 +04:00
snd_printk ( KERN_ERR " hda_codec: "
" address 0x%x is already occupied \n " , codec_addr ) ;
2005-04-17 02:20:36 +04:00
return - EBUSY ;
}
[ALSA] Replace with kzalloc() - pci stuff
AD1889 driver,ATIIXP driver,ATIIXP-modem driver,AZT3328 driver
BT87x driver,CMIPCI driver,CS4281 driver,ENS1370/1+ driver
ES1938 driver,ES1968 driver,FM801 driver,Intel8x0 driver
Intel8x0-modem driver,Maestro3 driver,SonicVibes driver,VIA82xx driver
VIA82xx-modem driver,AC97 Codec,AK4531 codec,au88x0 driver
CA0106 driver,CS46xx driver,EMU10K1/EMU10K2 driver,HDA Codec driver
HDA generic driver,HDA Intel driver,ICE1712 driver,ICE1724 driver
KORG1212 driver,MIXART driver,NM256 driver,Trident driver,YMFPCI driver
Replace kcalloc(1,..) with kzalloc().
Signed-off-by: Takashi Iwai <tiwai@suse.de>
2005-09-09 16:21:46 +04:00
codec = kzalloc ( sizeof ( * codec ) , GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
if ( codec = = NULL ) {
snd_printk ( KERN_ERR " can't allocate struct hda_codec \n " ) ;
return - ENOMEM ;
}
codec - > bus = bus ;
codec - > addr = codec_addr ;
2006-01-16 18:34:20 +03:00
mutex_init ( & codec - > spdif_mutex ) ;
2009-01-09 11:45:24 +03:00
mutex_init ( & codec - > control_mutex ) ;
2007-08-10 18:59:39 +04:00
init_hda_cache ( & codec - > amp_cache , sizeof ( struct hda_amp_info ) ) ;
2007-08-10 19:03:40 +04:00
init_hda_cache ( & codec - > cmd_cache , sizeof ( struct hda_cache_head ) ) ;
2009-12-08 18:13:32 +03:00
snd_array_init ( & codec - > mixers , sizeof ( struct hda_nid_item ) , 32 ) ;
snd_array_init ( & codec - > nids , sizeof ( struct hda_nid_item ) , 32 ) ;
2009-02-20 16:11:16 +03:00
snd_array_init ( & codec - > init_pins , sizeof ( struct hda_pincfg ) , 16 ) ;
2009-02-23 11:42:57 +03:00
snd_array_init ( & codec - > driver_pins , sizeof ( struct hda_pincfg ) , 16 ) ;
2010-08-06 15:48:11 +04:00
snd_array_init ( & codec - > cvt_setups , sizeof ( struct hda_cvt_setup ) , 8 ) ;
2008-07-30 17:01:45 +04:00
if ( codec - > bus - > modelname ) {
codec - > modelname = kstrdup ( codec - > bus - > modelname , GFP_KERNEL ) ;
if ( ! codec - > modelname ) {
snd_hda_codec_free ( codec ) ;
return - ENODEV ;
}
}
2005-04-17 02:20:36 +04:00
2007-08-10 19:21:45 +04:00
# ifdef CONFIG_SND_HDA_POWER_SAVE
INIT_DELAYED_WORK ( & codec - > power_work , hda_power_work ) ;
/* snd_hda_codec_new() marks the codec as power-up, and leave it as is.
* the caller has to power down appropriatley after initialization
* phase .
*/
hda_keep_power_on ( codec ) ;
# endif
2005-04-17 02:20:36 +04:00
list_add_tail ( & codec - > list , & bus - > codec_list ) ;
bus - > caddr_tbl [ codec_addr ] = codec ;
2007-04-16 13:29:14 +04:00
codec - > vendor_id = snd_hda_param_read ( codec , AC_NODE_ROOT ,
AC_PAR_VENDOR_ID ) ;
2006-02-16 20:17:58 +03:00
if ( codec - > vendor_id = = - 1 )
/* read again, hopefully the access method was corrected
* in the last read . . .
*/
codec - > vendor_id = snd_hda_param_read ( codec , AC_NODE_ROOT ,
AC_PAR_VENDOR_ID ) ;
2007-04-16 13:29:14 +04:00
codec - > subsystem_id = snd_hda_param_read ( codec , AC_NODE_ROOT ,
AC_PAR_SUBSYSTEM_ID ) ;
codec - > revision_id = snd_hda_param_read ( codec , AC_NODE_ROOT ,
AC_PAR_REV_ID ) ;
2005-04-17 02:20:36 +04:00
2005-08-11 13:00:16 +04:00
setup_fg_nodes ( codec ) ;
2007-04-16 13:29:14 +04:00
if ( ! codec - > afg & & ! codec - > mfg ) {
2005-08-11 13:00:16 +04:00
snd_printdd ( " hda_codec: no AFG or MFG node found \n " ) ;
2009-02-20 16:11:16 +03:00
err = - ENODEV ;
goto error ;
2005-04-17 02:20:36 +04:00
}
2009-02-20 16:11:16 +03:00
err = read_widget_caps ( codec , codec - > afg ? codec - > afg : codec - > mfg ) ;
if ( err < 0 ) {
2005-11-21 18:33:22 +03:00
snd_printk ( KERN_ERR " hda_codec: cannot malloc \n " ) ;
2009-02-20 16:11:16 +03:00
goto error ;
2005-11-21 18:33:22 +03:00
}
2009-02-20 16:11:16 +03:00
err = read_pin_defaults ( codec ) ;
if ( err < 0 )
goto error ;
2005-11-21 18:33:22 +03:00
2007-04-16 13:29:14 +04:00
if ( ! codec - > subsystem_id ) {
2005-10-11 17:05:54 +04:00
hda_nid_t nid = codec - > afg ? codec - > afg : codec - > mfg ;
2007-04-16 13:29:14 +04:00
codec - > subsystem_id =
snd_hda_codec_read ( codec , nid , 0 ,
AC_VERB_GET_SUBSYSTEM_ID , 0 ) ;
2005-10-11 17:05:54 +04:00
}
2009-03-13 11:02:42 +03:00
/* power-up all before initialization */
hda_set_power_state ( codec ,
codec - > afg ? codec - > afg : codec - > mfg ,
AC_PWRST_D0 ) ;
2008-07-30 17:01:45 +04:00
snd_hda_codec_proc_new ( codec ) ;
snd_hda_create_hwdep ( codec ) ;
sprintf ( component , " HDA:%08x,%08x,%08x " , codec - > vendor_id ,
codec - > subsystem_id , codec - > revision_id ) ;
snd_component_add ( codec - > bus - > card , component ) ;
if ( codecp )
* codecp = codec ;
return 0 ;
2009-02-20 16:11:16 +03:00
error :
snd_hda_codec_free ( codec ) ;
return err ;
2008-07-30 17:01:45 +04:00
}
2008-11-28 17:17:06 +03:00
EXPORT_SYMBOL_HDA ( snd_hda_codec_new ) ;
2008-07-30 17:01:45 +04:00
2009-11-16 16:58:17 +03:00
/**
* snd_hda_codec_configure - ( Re - ) configure the HD - audio codec
* @ codec : the HDA codec
*
* Start parsing of the given codec tree and ( re - ) initialize the whole
* patch instance .
*
* Returns 0 if successful or a negative error code .
*/
2008-07-30 17:01:45 +04:00
int snd_hda_codec_configure ( struct hda_codec * codec )
{
int err ;
2007-03-07 17:55:59 +03:00
codec - > preset = find_codec_preset ( codec ) ;
2009-05-16 12:00:49 +04:00
if ( ! codec - > vendor_name | | ! codec - > chip_name ) {
2008-07-30 17:01:45 +04:00
err = get_codec_name ( codec ) ;
if ( err < 0 )
return err ;
}
2005-04-17 02:20:36 +04:00
2007-07-27 21:15:54 +04:00
if ( is_generic_config ( codec ) ) {
2005-04-17 02:20:36 +04:00
err = snd_hda_parse_generic_codec ( codec ) ;
2007-07-27 21:15:54 +04:00
goto patched ;
}
if ( codec - > preset & & codec - > preset - > patch ) {
err = codec - > preset - > patch ( codec ) ;
goto patched ;
}
/* call the default parser */
err = snd_hda_parse_generic_codec ( codec ) ;
2007-10-19 10:13:40 +04:00
if ( err < 0 )
printk ( KERN_ERR " hda-codec: No codec parser is available \n " ) ;
2007-07-27 21:15:54 +04:00
patched :
2008-07-30 17:01:45 +04:00
if ( ! err & & codec - > patch_ops . unsol_event )
err = init_unsol_queue ( codec - > bus ) ;
2009-12-23 11:27:51 +03:00
/* audio codec should override the mixer name */
if ( ! err & & ( codec - > afg | | ! * codec - > bus - > card - > mixername ) )
snprintf ( codec - > bus - > card - > mixername ,
sizeof ( codec - > bus - > card - > mixername ) ,
" %s %s " , codec - > vendor_name , codec - > chip_name ) ;
2008-07-30 17:01:45 +04:00
return err ;
2005-04-17 02:20:36 +04:00
}
2009-06-17 11:33:52 +04:00
EXPORT_SYMBOL_HDA ( snd_hda_codec_configure ) ;
2005-04-17 02:20:36 +04:00
/**
* snd_hda_codec_setup_stream - set up the codec for streaming
* @ codec : the CODEC to set up
* @ nid : the NID to set up
* @ stream_tag : stream tag to pass , it ' s between 0x1 and 0xf .
* @ channel_id : channel id to pass , zero based .
* @ format : stream format .
*/
2007-04-16 13:29:14 +04:00
void snd_hda_codec_setup_stream ( struct hda_codec * codec , hda_nid_t nid ,
u32 stream_tag ,
2005-04-17 02:20:36 +04:00
int channel_id , int format )
{
2010-08-20 11:44:36 +04:00
struct hda_codec * c ;
2010-08-06 15:48:11 +04:00
struct hda_cvt_setup * p ;
unsigned int oldval , newval ;
2010-10-22 19:15:47 +04:00
int type ;
2010-08-06 15:48:11 +04:00
int i ;
2007-04-16 13:29:14 +04:00
if ( ! nid )
2005-04-20 15:45:55 +04:00
return ;
2007-04-16 13:29:14 +04:00
snd_printdd ( " hda_codec_setup_stream: "
" NID=0x%x, stream=0x%x, channel=%d, format=0x%x \n " ,
2005-04-17 02:20:36 +04:00
nid , stream_tag , channel_id , format ) ;
2010-08-06 15:48:11 +04:00
p = get_hda_cvt_setup ( codec , nid ) ;
if ( ! p )
return ;
/* update the stream-id if changed */
if ( p - > stream_tag ! = stream_tag | | p - > channel_id ! = channel_id ) {
oldval = snd_hda_codec_read ( codec , nid , 0 , AC_VERB_GET_CONV , 0 ) ;
newval = ( stream_tag < < 4 ) | channel_id ;
if ( oldval ! = newval )
snd_hda_codec_write ( codec , nid , 0 ,
AC_VERB_SET_CHANNEL_STREAMID ,
newval ) ;
p - > stream_tag = stream_tag ;
p - > channel_id = channel_id ;
}
/* update the format-id if changed */
if ( p - > format_id ! = format ) {
oldval = snd_hda_codec_read ( codec , nid , 0 ,
AC_VERB_GET_STREAM_FORMAT , 0 ) ;
if ( oldval ! = format ) {
msleep ( 1 ) ;
snd_hda_codec_write ( codec , nid , 0 ,
AC_VERB_SET_STREAM_FORMAT ,
format ) ;
}
p - > format_id = format ;
}
p - > active = 1 ;
p - > dirty = 0 ;
/* make other inactive cvts with the same stream-tag dirty */
2010-10-22 19:15:47 +04:00
type = get_wcaps_type ( get_wcaps ( codec , nid ) ) ;
2010-08-20 11:44:36 +04:00
list_for_each_entry ( c , & codec - > bus - > codec_list , list ) {
for ( i = 0 ; i < c - > cvt_setups . used ; i + + ) {
p = snd_array_elem ( & c - > cvt_setups , i ) ;
2010-10-22 19:15:47 +04:00
if ( ! p - > active & & p - > stream_tag = = stream_tag & &
get_wcaps_type ( get_wcaps ( codec , p - > nid ) ) = = type )
2010-08-20 11:44:36 +04:00
p - > dirty = 1 ;
}
2010-08-06 15:48:11 +04:00
}
2005-04-17 02:20:36 +04:00
}
2008-11-28 17:17:06 +03:00
EXPORT_SYMBOL_HDA ( snd_hda_codec_setup_stream ) ;
2005-04-17 02:20:36 +04:00
2010-08-13 13:56:53 +04:00
static void really_cleanup_stream ( struct hda_codec * codec ,
struct hda_cvt_setup * q ) ;
2009-11-16 16:58:17 +03:00
/**
2010-08-13 13:56:53 +04:00
* __snd_hda_codec_cleanup_stream - clean up the codec for closing
2009-11-16 16:58:17 +03:00
* @ codec : the CODEC to clean up
* @ nid : the NID to clean up
2010-08-13 13:56:53 +04:00
* @ do_now : really clean up the stream instead of clearing the active flag
2009-11-16 16:58:17 +03:00
*/
2010-08-13 13:56:53 +04:00
void __snd_hda_codec_cleanup_stream ( struct hda_codec * codec , hda_nid_t nid ,
int do_now )
2008-03-18 11:57:50 +03:00
{
2010-08-06 15:48:11 +04:00
struct hda_cvt_setup * p ;
2008-03-18 11:57:50 +03:00
if ( ! nid )
return ;
2010-10-25 12:37:11 +04:00
if ( codec - > no_sticky_stream )
do_now = 1 ;
2008-03-18 11:57:50 +03:00
snd_printdd ( " hda_codec_cleanup_stream: NID=0x%x \n " , nid ) ;
2010-08-06 15:48:11 +04:00
p = get_hda_cvt_setup ( codec , nid ) ;
2010-08-13 13:56:53 +04:00
if ( p ) {
/* here we just clear the active flag when do_now isn't set;
* actual clean - ups will be done later in
* purify_inactive_streams ( ) called from snd_hda_codec_prpapre ( )
*/
if ( do_now )
really_cleanup_stream ( codec , p ) ;
else
p - > active = 0 ;
}
2010-08-06 15:48:11 +04:00
}
2010-08-13 13:56:53 +04:00
EXPORT_SYMBOL_HDA ( __snd_hda_codec_cleanup_stream ) ;
2010-08-06 15:48:11 +04:00
static void really_cleanup_stream ( struct hda_codec * codec ,
struct hda_cvt_setup * q )
{
hda_nid_t nid = q - > nid ;
2008-03-18 11:57:50 +03:00
snd_hda_codec_write ( codec , nid , 0 , AC_VERB_SET_CHANNEL_STREAMID , 0 ) ;
snd_hda_codec_write ( codec , nid , 0 , AC_VERB_SET_STREAM_FORMAT , 0 ) ;
2010-08-06 15:48:11 +04:00
memset ( q , 0 , sizeof ( * q ) ) ;
q - > nid = nid ;
}
/* clean up the all conflicting obsolete streams */
static void purify_inactive_streams ( struct hda_codec * codec )
{
2010-08-20 11:44:36 +04:00
struct hda_codec * c ;
2010-08-06 15:48:11 +04:00
int i ;
2010-08-20 11:44:36 +04:00
list_for_each_entry ( c , & codec - > bus - > codec_list , list ) {
for ( i = 0 ; i < c - > cvt_setups . used ; i + + ) {
struct hda_cvt_setup * p ;
p = snd_array_elem ( & c - > cvt_setups , i ) ;
if ( p - > dirty )
really_cleanup_stream ( c , p ) ;
}
2010-08-06 15:48:11 +04:00
}
}
2011-04-20 23:04:36 +04:00
# ifdef SND_HDA_NEEDS_RESUME
2010-08-06 15:48:11 +04:00
/* clean up all streams; called from suspend */
static void hda_cleanup_all_streams ( struct hda_codec * codec )
{
int i ;
for ( i = 0 ; i < codec - > cvt_setups . used ; i + + ) {
struct hda_cvt_setup * p = snd_array_elem ( & codec - > cvt_setups , i ) ;
if ( p - > stream_tag )
really_cleanup_stream ( codec , p ) ;
}
2008-03-18 11:57:50 +03:00
}
2011-04-20 23:04:36 +04:00
# endif
2008-03-18 11:57:50 +03:00
2005-04-17 02:20:36 +04:00
/*
* amp access functions
*/
2005-06-08 16:43:58 +04:00
/* FIXME: more better hash key? */
2010-02-28 22:16:53 +03:00
# define HDA_HASH_KEY(nid, dir, idx) (u32)((nid) + ((idx) << 16) + ((dir) << 24))
2009-03-23 15:07:47 +03:00
# define HDA_HASH_PINCAP_KEY(nid) (u32)((nid) + (0x02 << 24))
2009-03-24 09:32:14 +03:00
# define HDA_HASH_PARPCM_KEY(nid) (u32)((nid) + (0x03 << 24))
# define HDA_HASH_PARSTR_KEY(nid) (u32)((nid) + (0x04 << 24))
2005-04-17 02:20:36 +04:00
# define INFO_AMP_CAPS (1<<0)
2005-06-08 16:43:58 +04:00
# define INFO_AMP_VOL(ch) (1 << (1 + (ch)))
2005-04-17 02:20:36 +04:00
/* initialize the hash table */
2008-11-27 17:47:11 +03:00
static void /*__devinit*/ init_hda_cache ( struct hda_cache_rec * cache ,
2007-08-10 18:59:39 +04:00
unsigned int record_size )
{
memset ( cache , 0 , sizeof ( * cache ) ) ;
memset ( cache - > hash , 0xff , sizeof ( cache - > hash ) ) ;
2008-07-30 17:01:44 +04:00
snd_array_init ( & cache - > buf , record_size , 64 ) ;
2007-08-10 18:59:39 +04:00
}
2007-08-23 02:01:09 +04:00
static void free_hda_cache ( struct hda_cache_rec * cache )
2005-04-17 02:20:36 +04:00
{
2008-07-30 17:01:44 +04:00
snd_array_free ( & cache - > buf ) ;
2005-04-17 02:20:36 +04:00
}
/* query the hash. allocate an entry if not found. */
2010-03-30 20:03:44 +04:00
static struct hda_cache_head * get_hash ( struct hda_cache_rec * cache , u32 key )
2005-04-17 02:20:36 +04:00
{
2007-08-10 18:59:39 +04:00
u16 idx = key % ( u16 ) ARRAY_SIZE ( cache - > hash ) ;
u16 cur = cache - > hash [ idx ] ;
struct hda_cache_head * info ;
2005-04-17 02:20:36 +04:00
while ( cur ! = 0xffff ) {
2008-11-10 18:24:26 +03:00
info = snd_array_elem ( & cache - > buf , cur ) ;
2005-04-17 02:20:36 +04:00
if ( info - > key = = key )
return info ;
cur = info - > next ;
}
2010-03-30 20:03:44 +04:00
return NULL ;
}
2005-04-17 02:20:36 +04:00
2010-03-30 20:03:44 +04:00
/* query the hash. allocate an entry if not found. */
static struct hda_cache_head * get_alloc_hash ( struct hda_cache_rec * cache ,
u32 key )
{
struct hda_cache_head * info = get_hash ( cache , key ) ;
if ( ! info ) {
u16 idx , cur ;
/* add a new hash entry */
info = snd_array_new ( & cache - > buf ) ;
if ( ! info )
return NULL ;
cur = snd_array_index ( & cache - > buf , info ) ;
info - > key = key ;
info - > val = 0 ;
idx = key % ( u16 ) ARRAY_SIZE ( cache - > hash ) ;
info - > next = cache - > hash [ idx ] ;
cache - > hash [ idx ] = cur ;
}
2005-04-17 02:20:36 +04:00
return info ;
}
2007-08-10 18:59:39 +04:00
/* query and allocate an amp hash entry */
static inline struct hda_amp_info *
get_alloc_amp_hash ( struct hda_codec * codec , u32 key )
{
return ( struct hda_amp_info * ) get_alloc_hash ( & codec - > amp_cache , key ) ;
}
2009-11-16 16:58:17 +03:00
/**
* query_amp_caps - query AMP capabilities
* @ codec : the HD - auio codec
* @ nid : the NID to query
* @ direction : either # HDA_INPUT or # HDA_OUTPUT
*
* Query AMP capabilities for the given widget and direction .
* Returns the obtained capability bits .
*
* When cap bits have been already read , this doesn ' t read again but
* returns the cached value .
2005-04-17 02:20:36 +04:00
*/
2008-01-24 13:49:21 +03:00
u32 query_amp_caps ( struct hda_codec * codec , hda_nid_t nid , int direction )
2005-04-17 02:20:36 +04:00
{
2007-04-16 13:29:14 +04:00
struct hda_amp_info * info ;
2005-04-17 02:20:36 +04:00
2007-04-16 13:29:14 +04:00
info = get_alloc_amp_hash ( codec , HDA_HASH_KEY ( nid , direction , 0 ) ) ;
if ( ! info )
2005-04-17 02:20:36 +04:00
return 0 ;
2007-08-10 18:59:39 +04:00
if ( ! ( info - > head . val & INFO_AMP_CAPS ) ) {
2007-04-16 13:29:14 +04:00
if ( ! ( get_wcaps ( codec , nid ) & AC_WCAP_AMP_OVRD ) )
2005-04-17 02:20:36 +04:00
nid = codec - > afg ;
2007-04-16 13:29:14 +04:00
info - > amp_caps = snd_hda_param_read ( codec , nid ,
direction = = HDA_OUTPUT ?
AC_PAR_AMP_OUT_CAP :
AC_PAR_AMP_IN_CAP ) ;
2007-05-10 18:56:09 +04:00
if ( info - > amp_caps )
2007-08-10 18:59:39 +04:00
info - > head . val | = INFO_AMP_CAPS ;
2005-04-17 02:20:36 +04:00
}
return info - > amp_caps ;
}
2008-11-28 17:17:06 +03:00
EXPORT_SYMBOL_HDA ( query_amp_caps ) ;
2005-04-17 02:20:36 +04:00
2009-11-16 16:58:17 +03:00
/**
* snd_hda_override_amp_caps - Override the AMP capabilities
* @ codec : the CODEC to clean up
* @ nid : the NID to clean up
* @ direction : either # HDA_INPUT or # HDA_OUTPUT
* @ caps : the capability bits to set
*
* Override the cached AMP caps bits value by the given one .
* This function is useful if the driver needs to adjust the AMP ranges ,
* e . g . limit to 0 dB , etc .
*
* Returns zero if successful or a negative error code .
*/
2007-05-29 21:01:37 +04:00
int snd_hda_override_amp_caps ( struct hda_codec * codec , hda_nid_t nid , int dir ,
unsigned int caps )
{
struct hda_amp_info * info ;
info = get_alloc_amp_hash ( codec , HDA_HASH_KEY ( nid , dir , 0 ) ) ;
if ( ! info )
return - EINVAL ;
info - > amp_caps = caps ;
2007-08-10 18:59:39 +04:00
info - > head . val | = INFO_AMP_CAPS ;
2007-05-29 21:01:37 +04:00
return 0 ;
}
2008-11-28 17:17:06 +03:00
EXPORT_SYMBOL_HDA ( snd_hda_override_amp_caps ) ;
2009-03-23 15:07:47 +03:00
2009-03-24 09:32:14 +03:00
static unsigned int
query_caps_hash ( struct hda_codec * codec , hda_nid_t nid , u32 key ,
unsigned int ( * func ) ( struct hda_codec * , hda_nid_t ) )
2009-03-23 15:07:47 +03:00
{
struct hda_amp_info * info ;
2009-03-24 09:32:14 +03:00
info = get_alloc_amp_hash ( codec , key ) ;
2009-03-23 15:07:47 +03:00
if ( ! info )
return 0 ;
if ( ! info - > head . val ) {
info - > head . val | = INFO_AMP_CAPS ;
2009-03-24 09:32:14 +03:00
info - > amp_caps = func ( codec , nid ) ;
2009-03-23 15:07:47 +03:00
}
return info - > amp_caps ;
}
2009-03-24 09:32:14 +03:00
static unsigned int read_pin_cap ( struct hda_codec * codec , hda_nid_t nid )
{
return snd_hda_param_read ( codec , nid , AC_PAR_PIN_CAP ) ;
}
2009-11-16 16:58:17 +03:00
/**
* snd_hda_query_pin_caps - Query PIN capabilities
* @ codec : the HD - auio codec
* @ nid : the NID to query
*
* Query PIN capabilities for the given widget .
* Returns the obtained capability bits .
*
* When cap bits have been already read , this doesn ' t read again but
* returns the cached value .
*/
2009-03-24 09:32:14 +03:00
u32 snd_hda_query_pin_caps ( struct hda_codec * codec , hda_nid_t nid )
{
return query_caps_hash ( codec , nid , HDA_HASH_PINCAP_KEY ( nid ) ,
read_pin_cap ) ;
}
2009-03-23 15:07:47 +03:00
EXPORT_SYMBOL_HDA ( snd_hda_query_pin_caps ) ;
2007-05-29 21:01:37 +04:00
2009-11-18 07:38:02 +03:00
/**
* snd_hda_pin_sense - execute pin sense measurement
* @ codec : the CODEC to sense
* @ nid : the pin NID to sense
*
* Execute necessary pin sense measurement and return its Presence Detect ,
* Impedance , ELD Valid etc . status bits .
*/
u32 snd_hda_pin_sense ( struct hda_codec * codec , hda_nid_t nid )
{
2009-12-26 00:49:01 +03:00
u32 pincap ;
2009-11-18 07:38:02 +03:00
2009-12-26 00:49:01 +03:00
if ( ! codec - > no_trigger_sense ) {
pincap = snd_hda_query_pin_caps ( codec , nid ) ;
if ( pincap & AC_PINCAP_TRIG_REQ ) /* need trigger? */
2010-02-28 22:16:53 +03:00
snd_hda_codec_read ( codec , nid , 0 ,
AC_VERB_SET_PIN_SENSE , 0 ) ;
2009-12-26 00:49:01 +03:00
}
2009-11-18 07:38:02 +03:00
return snd_hda_codec_read ( codec , nid , 0 ,
AC_VERB_GET_PIN_SENSE , 0 ) ;
}
EXPORT_SYMBOL_HDA ( snd_hda_pin_sense ) ;
/**
* snd_hda_jack_detect - query pin Presence Detect status
* @ codec : the CODEC to sense
* @ nid : the pin NID to sense
*
* Query and return the pin ' s Presence Detect status .
*/
int snd_hda_jack_detect ( struct hda_codec * codec , hda_nid_t nid )
{
2010-02-28 22:16:53 +03:00
u32 sense = snd_hda_pin_sense ( codec , nid ) ;
return ! ! ( sense & AC_PINSENSE_PRESENCE ) ;
2009-11-18 07:38:02 +03:00
}
EXPORT_SYMBOL_HDA ( snd_hda_jack_detect ) ;
2005-04-17 02:20:36 +04:00
/*
* read the current volume to info
2005-06-08 16:43:58 +04:00
* if the cache exists , read the cache value .
2005-04-17 02:20:36 +04:00
*/
2007-04-16 13:29:14 +04:00
static unsigned int get_vol_mute ( struct hda_codec * codec ,
struct hda_amp_info * info , hda_nid_t nid ,
int ch , int direction , int index )
2005-04-17 02:20:36 +04:00
{
u32 val , parm ;
2007-08-10 18:59:39 +04:00
if ( info - > head . val & INFO_AMP_VOL ( ch ) )
2005-06-08 16:43:58 +04:00
return info - > vol [ ch ] ;
2005-04-17 02:20:36 +04:00
parm = ch ? AC_AMP_GET_RIGHT : AC_AMP_GET_LEFT ;
parm | = direction = = HDA_OUTPUT ? AC_AMP_GET_OUTPUT : AC_AMP_GET_INPUT ;
parm | = index ;
2007-04-16 13:29:14 +04:00
val = snd_hda_codec_read ( codec , nid , 0 ,
AC_VERB_GET_AMP_GAIN_MUTE , parm ) ;
2005-04-17 02:20:36 +04:00
info - > vol [ ch ] = val & 0xff ;
2007-08-10 18:59:39 +04:00
info - > head . val | = INFO_AMP_VOL ( ch ) ;
2005-06-08 16:43:58 +04:00
return info - > vol [ ch ] ;
2005-04-17 02:20:36 +04:00
}
/*
2005-06-08 16:43:58 +04:00
* write the current volume in info to the h / w and update the cache
2005-04-17 02:20:36 +04:00
*/
2005-06-08 16:43:58 +04:00
static void put_vol_mute ( struct hda_codec * codec , struct hda_amp_info * info ,
2007-04-16 13:29:14 +04:00
hda_nid_t nid , int ch , int direction , int index ,
int val )
2005-04-17 02:20:36 +04:00
{
u32 parm ;
parm = ch ? AC_AMP_SET_RIGHT : AC_AMP_SET_LEFT ;
parm | = direction = = HDA_OUTPUT ? AC_AMP_SET_OUTPUT : AC_AMP_SET_INPUT ;
parm | = index < < AC_AMP_SET_INDEX_SHIFT ;
parm | = val ;
snd_hda_codec_write ( codec , nid , 0 , AC_VERB_SET_AMP_GAIN_MUTE , parm ) ;
2005-06-08 16:43:58 +04:00
info - > vol [ ch ] = val ;
2005-04-17 02:20:36 +04:00
}
2009-11-16 16:58:17 +03:00
/**
* snd_hda_codec_amp_read - Read AMP value
* @ codec : HD - audio codec
* @ nid : NID to read the AMP value
* @ ch : channel ( left = 0 or right = 1 )
* @ direction : # HDA_INPUT or # HDA_OUTPUT
* @ index : the index value ( only for input direction )
*
* Read AMP value . The volume is between 0 to 0x7f , 0x80 = mute bit .
2005-04-17 02:20:36 +04:00
*/
2006-03-01 16:16:17 +03:00
int snd_hda_codec_amp_read ( struct hda_codec * codec , hda_nid_t nid , int ch ,
int direction , int index )
2005-04-17 02:20:36 +04:00
{
2007-04-16 13:29:14 +04:00
struct hda_amp_info * info ;
info = get_alloc_amp_hash ( codec , HDA_HASH_KEY ( nid , direction , index ) ) ;
if ( ! info )
2005-04-17 02:20:36 +04:00
return 0 ;
2005-06-08 16:43:58 +04:00
return get_vol_mute ( codec , info , nid , ch , direction , index ) ;
2005-04-17 02:20:36 +04:00
}
2008-11-28 17:17:06 +03:00
EXPORT_SYMBOL_HDA ( snd_hda_codec_amp_read ) ;
2005-04-17 02:20:36 +04:00
2009-11-16 16:58:17 +03:00
/**
* snd_hda_codec_amp_update - update the AMP value
* @ codec : HD - audio codec
* @ nid : NID to read the AMP value
* @ ch : channel ( left = 0 or right = 1 )
* @ direction : # HDA_INPUT or # HDA_OUTPUT
* @ idx : the index value ( only for input direction )
* @ mask : bit mask to set
* @ val : the bits value to set
*
* Update the AMP value with a bit mask .
* Returns 0 if the value is unchanged , 1 if changed .
2005-06-08 16:43:58 +04:00
*/
2006-03-01 16:16:17 +03:00
int snd_hda_codec_amp_update ( struct hda_codec * codec , hda_nid_t nid , int ch ,
int direction , int idx , int mask , int val )
2005-04-17 02:20:36 +04:00
{
2007-04-16 13:29:14 +04:00
struct hda_amp_info * info ;
2005-06-08 16:43:58 +04:00
2007-04-16 13:29:14 +04:00
info = get_alloc_amp_hash ( codec , HDA_HASH_KEY ( nid , direction , idx ) ) ;
if ( ! info )
2005-04-17 02:20:36 +04:00
return 0 ;
2010-03-29 11:19:38 +04:00
if ( snd_BUG_ON ( mask & ~ 0xff ) )
mask & = 0xff ;
2005-06-08 16:43:58 +04:00
val & = mask ;
val | = get_vol_mute ( codec , info , nid , ch , direction , idx ) & ~ mask ;
2007-08-10 19:09:26 +04:00
if ( info - > vol [ ch ] = = val )
2005-04-17 02:20:36 +04:00
return 0 ;
2005-06-08 16:43:58 +04:00
put_vol_mute ( codec , info , nid , ch , direction , idx , val ) ;
2005-04-17 02:20:36 +04:00
return 1 ;
}
2008-11-28 17:17:06 +03:00
EXPORT_SYMBOL_HDA ( snd_hda_codec_amp_update ) ;
2005-04-17 02:20:36 +04:00
2009-11-16 16:58:17 +03:00
/**
* snd_hda_codec_amp_stereo - update the AMP stereo values
* @ codec : HD - audio codec
* @ nid : NID to read the AMP value
* @ direction : # HDA_INPUT or # HDA_OUTPUT
* @ idx : the index value ( only for input direction )
* @ mask : bit mask to set
* @ val : the bits value to set
*
* Update the AMP values like snd_hda_codec_amp_update ( ) , but for a
* stereo widget with the same mask and value .
2007-08-10 19:11:07 +04:00
*/
int snd_hda_codec_amp_stereo ( struct hda_codec * codec , hda_nid_t nid ,
int direction , int idx , int mask , int val )
{
int ch , ret = 0 ;
2010-03-29 11:19:38 +04:00
if ( snd_BUG_ON ( mask & ~ 0xff ) )
mask & = 0xff ;
2007-08-10 19:11:07 +04:00
for ( ch = 0 ; ch < 2 ; ch + + )
ret | = snd_hda_codec_amp_update ( codec , nid , ch , direction ,
idx , mask , val ) ;
return ret ;
}
2008-11-28 17:17:06 +03:00
EXPORT_SYMBOL_HDA ( snd_hda_codec_amp_stereo ) ;
2007-08-10 19:11:07 +04:00
2007-08-10 19:21:45 +04:00
# ifdef SND_HDA_NEEDS_RESUME
2009-11-16 16:58:17 +03:00
/**
* snd_hda_codec_resume_amp - Resume all AMP commands from the cache
* @ codec : HD - audio codec
*
* Resume the all amp commands from the cache .
*/
2007-08-10 19:03:40 +04:00
void snd_hda_codec_resume_amp ( struct hda_codec * codec )
{
2008-07-30 17:01:44 +04:00
struct hda_amp_info * buffer = codec - > amp_cache . buf . list ;
2007-08-10 19:03:40 +04:00
int i ;
2008-07-30 17:01:44 +04:00
for ( i = 0 ; i < codec - > amp_cache . buf . used ; i + + , buffer + + ) {
2007-08-10 19:03:40 +04:00
u32 key = buffer - > head . key ;
hda_nid_t nid ;
unsigned int idx , dir , ch ;
if ( ! key )
continue ;
nid = key & 0xff ;
idx = ( key > > 16 ) & 0xff ;
dir = ( key > > 24 ) & 0xff ;
for ( ch = 0 ; ch < 2 ; ch + + ) {
if ( ! ( buffer - > head . val & INFO_AMP_VOL ( ch ) ) )
continue ;
put_vol_mute ( codec , buffer , nid , ch , dir , idx ,
buffer - > vol [ ch ] ) ;
}
}
}
2008-11-28 17:17:06 +03:00
EXPORT_SYMBOL_HDA ( snd_hda_codec_resume_amp ) ;
2007-08-10 19:21:45 +04:00
# endif /* SND_HDA_NEEDS_RESUME */
2005-04-17 02:20:36 +04:00
2010-07-08 20:40:37 +04:00
static u32 get_amp_max_value ( struct hda_codec * codec , hda_nid_t nid , int dir ,
unsigned int ofs )
{
u32 caps = query_amp_caps ( codec , nid , dir ) ;
/* get num steps */
caps = ( caps & AC_AMPCAP_NUM_STEPS ) > > AC_AMPCAP_NUM_STEPS_SHIFT ;
if ( ofs < caps )
caps - = ofs ;
return caps ;
}
2009-11-16 16:58:17 +03:00
/**
* snd_hda_mixer_amp_volume_info - Info callback for a standard AMP mixer
*
* The control element is supposed to have the private_value field
* set up via HDA_COMPOSE_AMP_VAL * ( ) or related macros .
*/
2007-04-16 13:29:14 +04:00
int snd_hda_mixer_amp_volume_info ( struct snd_kcontrol * kcontrol ,
struct snd_ctl_elem_info * uinfo )
2005-04-17 02:20:36 +04:00
{
struct hda_codec * codec = snd_kcontrol_chip ( kcontrol ) ;
u16 nid = get_amp_nid ( kcontrol ) ;
u8 chs = get_amp_channels ( kcontrol ) ;
int dir = get_amp_direction ( kcontrol ) ;
2009-01-20 15:07:55 +03:00
unsigned int ofs = get_amp_offset ( kcontrol ) ;
2005-04-17 02:20:36 +04:00
2010-07-08 20:40:37 +04:00
uinfo - > type = SNDRV_CTL_ELEM_TYPE_INTEGER ;
uinfo - > count = chs = = 3 ? 2 : 1 ;
uinfo - > value . integer . min = 0 ;
uinfo - > value . integer . max = get_amp_max_value ( codec , nid , dir , ofs ) ;
if ( ! uinfo - > value . integer . max ) {
2007-04-16 13:29:14 +04:00
printk ( KERN_WARNING " hda_codec: "
2008-01-11 18:12:23 +03:00
" num_steps = 0 for NID=0x%x (ctl = %s) \n " , nid ,
kcontrol - > id . name ) ;
2005-04-17 02:20:36 +04:00
return - EINVAL ;
}
return 0 ;
}
2008-11-28 17:17:06 +03:00
EXPORT_SYMBOL_HDA ( snd_hda_mixer_amp_volume_info ) ;
2005-04-17 02:20:36 +04:00
2009-01-20 15:07:55 +03:00
static inline unsigned int
read_amp_value ( struct hda_codec * codec , hda_nid_t nid ,
int ch , int dir , int idx , unsigned int ofs )
{
unsigned int val ;
val = snd_hda_codec_amp_read ( codec , nid , ch , dir , idx ) ;
val & = HDA_AMP_VOLMASK ;
if ( val > = ofs )
val - = ofs ;
else
val = 0 ;
return val ;
}
static inline int
update_amp_value ( struct hda_codec * codec , hda_nid_t nid ,
int ch , int dir , int idx , unsigned int ofs ,
unsigned int val )
{
2010-07-08 20:40:37 +04:00
unsigned int maxval ;
2009-01-20 15:07:55 +03:00
if ( val > 0 )
val + = ofs ;
2010-07-26 19:00:15 +04:00
/* ofs = 0: raw max value */
maxval = get_amp_max_value ( codec , nid , dir , 0 ) ;
2010-07-08 20:40:37 +04:00
if ( val > maxval )
val = maxval ;
2009-01-20 15:07:55 +03:00
return snd_hda_codec_amp_update ( codec , nid , ch , dir , idx ,
HDA_AMP_VOLMASK , val ) ;
}
2009-11-16 16:58:17 +03:00
/**
* snd_hda_mixer_amp_volume_get - Get callback for a standard AMP mixer volume
*
* The control element is supposed to have the private_value field
* set up via HDA_COMPOSE_AMP_VAL * ( ) or related macros .
*/
2007-04-16 13:29:14 +04:00
int snd_hda_mixer_amp_volume_get ( struct snd_kcontrol * kcontrol ,
struct snd_ctl_elem_value * ucontrol )
2005-04-17 02:20:36 +04:00
{
struct hda_codec * codec = snd_kcontrol_chip ( kcontrol ) ;
hda_nid_t nid = get_amp_nid ( kcontrol ) ;
int chs = get_amp_channels ( kcontrol ) ;
int dir = get_amp_direction ( kcontrol ) ;
int idx = get_amp_index ( kcontrol ) ;
2009-01-20 15:07:55 +03:00
unsigned int ofs = get_amp_offset ( kcontrol ) ;
2005-04-17 02:20:36 +04:00
long * valp = ucontrol - > value . integer . value ;
if ( chs & 1 )
2009-01-20 15:07:55 +03:00
* valp + + = read_amp_value ( codec , nid , 0 , dir , idx , ofs ) ;
2005-04-17 02:20:36 +04:00
if ( chs & 2 )
2009-01-20 15:07:55 +03:00
* valp = read_amp_value ( codec , nid , 1 , dir , idx , ofs ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
2008-11-28 17:17:06 +03:00
EXPORT_SYMBOL_HDA ( snd_hda_mixer_amp_volume_get ) ;
2005-04-17 02:20:36 +04:00
2009-11-16 16:58:17 +03:00
/**
* snd_hda_mixer_amp_volume_put - Put callback for a standard AMP mixer volume
*
* The control element is supposed to have the private_value field
* set up via HDA_COMPOSE_AMP_VAL * ( ) or related macros .
*/
2007-04-16 13:29:14 +04:00
int snd_hda_mixer_amp_volume_put ( struct snd_kcontrol * kcontrol ,
struct snd_ctl_elem_value * ucontrol )
2005-04-17 02:20:36 +04:00
{
struct hda_codec * codec = snd_kcontrol_chip ( kcontrol ) ;
hda_nid_t nid = get_amp_nid ( kcontrol ) ;
int chs = get_amp_channels ( kcontrol ) ;
int dir = get_amp_direction ( kcontrol ) ;
int idx = get_amp_index ( kcontrol ) ;
2009-01-20 15:07:55 +03:00
unsigned int ofs = get_amp_offset ( kcontrol ) ;
2005-04-17 02:20:36 +04:00
long * valp = ucontrol - > value . integer . value ;
int change = 0 ;
2007-08-10 19:21:45 +04:00
snd_hda_power_up ( codec ) ;
2005-07-29 14:17:20 +04:00
if ( chs & 1 ) {
2009-01-20 15:07:55 +03:00
change = update_amp_value ( codec , nid , 0 , dir , idx , ofs , * valp ) ;
2005-07-29 14:17:20 +04:00
valp + + ;
}
2005-06-08 16:43:58 +04:00
if ( chs & 2 )
2009-01-20 15:07:55 +03:00
change | = update_amp_value ( codec , nid , 1 , dir , idx , ofs , * valp ) ;
2007-08-10 19:21:45 +04:00
snd_hda_power_down ( codec ) ;
2005-04-17 02:20:36 +04:00
return change ;
}
2008-11-28 17:17:06 +03:00
EXPORT_SYMBOL_HDA ( snd_hda_mixer_amp_volume_put ) ;
2005-04-17 02:20:36 +04:00
2009-11-16 16:58:17 +03:00
/**
* snd_hda_mixer_amp_volume_put - TLV callback for a standard AMP mixer volume
*
* The control element is supposed to have the private_value field
* set up via HDA_COMPOSE_AMP_VAL * ( ) or related macros .
*/
2006-07-05 19:39:49 +04:00
int snd_hda_mixer_amp_tlv ( struct snd_kcontrol * kcontrol , int op_flag ,
unsigned int size , unsigned int __user * _tlv )
{
struct hda_codec * codec = snd_kcontrol_chip ( kcontrol ) ;
hda_nid_t nid = get_amp_nid ( kcontrol ) ;
int dir = get_amp_direction ( kcontrol ) ;
2009-01-20 15:07:55 +03:00
unsigned int ofs = get_amp_offset ( kcontrol ) ;
2010-10-15 12:32:50 +04:00
bool min_mute = get_amp_min_mute ( kcontrol ) ;
2006-07-05 19:39:49 +04:00
u32 caps , val1 , val2 ;
if ( size < 4 * sizeof ( unsigned int ) )
return - ENOMEM ;
caps = query_amp_caps ( codec , nid , dir ) ;
2007-04-16 13:29:14 +04:00
val2 = ( caps & AC_AMPCAP_STEP_SIZE ) > > AC_AMPCAP_STEP_SIZE_SHIFT ;
val2 = ( val2 + 1 ) * 25 ;
2006-07-05 19:39:49 +04:00
val1 = - ( ( caps & AC_AMPCAP_OFFSET ) > > AC_AMPCAP_OFFSET_SHIFT ) ;
2009-01-20 15:07:55 +03:00
val1 + = ofs ;
2006-07-05 19:39:49 +04:00
val1 = ( ( int ) val1 ) * ( ( int ) val2 ) ;
2010-10-15 12:32:50 +04:00
if ( min_mute )
2010-10-17 12:40:53 +04:00
val2 | = TLV_DB_SCALE_MUTE ;
2006-07-05 19:39:49 +04:00
if ( put_user ( SNDRV_CTL_TLVT_DB_SCALE , _tlv ) )
return - EFAULT ;
if ( put_user ( 2 * sizeof ( unsigned int ) , _tlv + 1 ) )
return - EFAULT ;
if ( put_user ( val1 , _tlv + 2 ) )
return - EFAULT ;
if ( put_user ( val2 , _tlv + 3 ) )
return - EFAULT ;
return 0 ;
}
2008-11-28 17:17:06 +03:00
EXPORT_SYMBOL_HDA ( snd_hda_mixer_amp_tlv ) ;
2006-07-05 19:39:49 +04:00
2009-11-16 16:58:17 +03:00
/**
* snd_hda_set_vmaster_tlv - Set TLV for a virtual master control
* @ codec : HD - audio codec
* @ nid : NID of a reference widget
* @ dir : # HDA_INPUT or # HDA_OUTPUT
* @ tlv : TLV data to be stored , at least 4 elements
*
* Set ( static ) TLV data for a virtual master volume using the AMP caps
* obtained from the reference NID .
* The volume range is recalculated as if the max volume is 0 dB .
2008-01-10 18:53:55 +03:00
*/
void snd_hda_set_vmaster_tlv ( struct hda_codec * codec , hda_nid_t nid , int dir ,
unsigned int * tlv )
{
u32 caps ;
int nums , step ;
caps = query_amp_caps ( codec , nid , dir ) ;
nums = ( caps & AC_AMPCAP_NUM_STEPS ) > > AC_AMPCAP_NUM_STEPS_SHIFT ;
step = ( caps & AC_AMPCAP_STEP_SIZE ) > > AC_AMPCAP_STEP_SIZE_SHIFT ;
step = ( step + 1 ) * 25 ;
tlv [ 0 ] = SNDRV_CTL_TLVT_DB_SCALE ;
tlv [ 1 ] = 2 * sizeof ( unsigned int ) ;
tlv [ 2 ] = - nums * step ;
tlv [ 3 ] = step ;
}
2008-11-28 17:17:06 +03:00
EXPORT_SYMBOL_HDA ( snd_hda_set_vmaster_tlv ) ;
2008-01-10 18:53:55 +03:00
/* find a mixer control element with the given name */
2008-02-04 14:31:13 +03:00
static struct snd_kcontrol *
_snd_hda_find_mixer_ctl ( struct hda_codec * codec ,
const char * name , int idx )
2008-01-10 18:53:55 +03:00
{
struct snd_ctl_elem_id id ;
memset ( & id , 0 , sizeof ( id ) ) ;
id . iface = SNDRV_CTL_ELEM_IFACE_MIXER ;
2008-02-04 14:31:13 +03:00
id . index = idx ;
2009-04-16 12:22:24 +04:00
if ( snd_BUG_ON ( strlen ( name ) > = sizeof ( id . name ) ) )
return NULL ;
2008-01-10 18:53:55 +03:00
strcpy ( id . name , name ) ;
return snd_ctl_find_id ( codec - > bus - > card , & id ) ;
}
2009-11-16 16:58:17 +03:00
/**
* snd_hda_find_mixer_ctl - Find a mixer control element with the given name
* @ codec : HD - audio codec
* @ name : ctl id name string
*
* Get the control element with the given id string and IFACE_MIXER .
*/
2008-02-04 14:31:13 +03:00
struct snd_kcontrol * snd_hda_find_mixer_ctl ( struct hda_codec * codec ,
const char * name )
{
return _snd_hda_find_mixer_ctl ( codec , name , 0 ) ;
}
2008-11-28 17:17:06 +03:00
EXPORT_SYMBOL_HDA ( snd_hda_find_mixer_ctl ) ;
2008-02-04 14:31:13 +03:00
2010-12-23 12:17:52 +03:00
static int find_empty_mixer_ctl_idx ( struct hda_codec * codec , const char * name )
{
int idx ;
for ( idx = 0 ; idx < 16 ; idx + + ) { /* 16 ctlrs should be large enough */
if ( ! _snd_hda_find_mixer_ctl ( codec , name , idx ) )
return idx ;
}
return - EBUSY ;
}
2009-11-16 16:58:17 +03:00
/**
2009-12-08 18:13:32 +03:00
* snd_hda_ctl_add - Add a control element and assign to the codec
2009-11-16 16:58:17 +03:00
* @ codec : HD - audio codec
* @ nid : corresponding NID ( optional )
* @ kctl : the control element to assign
*
* Add the given control element to an array inside the codec instance .
* All control elements belonging to a codec are supposed to be added
* by this function so that a proper clean - up works at the free or
* reconfiguration time .
*
* If non - zero @ nid is passed , the NID is assigned to the control element .
* The assignment is shown in the codec proc file .
*
* snd_hda_ctl_add ( ) checks the control subdev id field whether
* # HDA_SUBDEV_NID_FLAG bit is set . If set ( and @ nid is zero ) , the lower
ALSA: hda - introduce HDA_SUBDEV_AMP_FLAG (ControlAmp in proc)
The purpose of this changeset is to show information about amplifier
setting in the codec proc file. Something like:
Control: name="Front Playback Volume", index=0, device=0
ControlAmp: chs=3, dir=Out, idx=0, ofs=0
Control: name="Front Playback Switch", index=0, device=0
ControlAmp: chs=3, dir=In, idx=2, ofs=0
Signed-off-by: Jaroslav Kysela <perex@perex.cz>
2009-12-08 19:45:25 +03:00
* bits value is taken as the NID to assign . The # HDA_NID_ITEM_AMP bit
* specifies if kctl - > private_value is a HDA amplifier value .
2009-11-16 16:58:17 +03:00
*/
2009-11-11 15:43:01 +03:00
int snd_hda_ctl_add ( struct hda_codec * codec , hda_nid_t nid ,
struct snd_kcontrol * kctl )
2008-07-30 17:01:45 +04:00
{
int err ;
ALSA: hda - introduce HDA_SUBDEV_AMP_FLAG (ControlAmp in proc)
The purpose of this changeset is to show information about amplifier
setting in the codec proc file. Something like:
Control: name="Front Playback Volume", index=0, device=0
ControlAmp: chs=3, dir=Out, idx=0, ofs=0
Control: name="Front Playback Switch", index=0, device=0
ControlAmp: chs=3, dir=In, idx=2, ofs=0
Signed-off-by: Jaroslav Kysela <perex@perex.cz>
2009-12-08 19:45:25 +03:00
unsigned short flags = 0 ;
2009-11-11 15:43:01 +03:00
struct hda_nid_item * item ;
2008-07-30 17:01:45 +04:00
2009-12-10 15:57:01 +03:00
if ( kctl - > id . subdevice & HDA_SUBDEV_AMP_FLAG ) {
ALSA: hda - introduce HDA_SUBDEV_AMP_FLAG (ControlAmp in proc)
The purpose of this changeset is to show information about amplifier
setting in the codec proc file. Something like:
Control: name="Front Playback Volume", index=0, device=0
ControlAmp: chs=3, dir=Out, idx=0, ofs=0
Control: name="Front Playback Switch", index=0, device=0
ControlAmp: chs=3, dir=In, idx=2, ofs=0
Signed-off-by: Jaroslav Kysela <perex@perex.cz>
2009-12-08 19:45:25 +03:00
flags | = HDA_NID_ITEM_AMP ;
2009-12-10 15:57:01 +03:00
if ( nid = = 0 )
nid = get_amp_nid_ ( kctl - > private_value ) ;
}
ALSA: hda - introduce HDA_SUBDEV_AMP_FLAG (ControlAmp in proc)
The purpose of this changeset is to show information about amplifier
setting in the codec proc file. Something like:
Control: name="Front Playback Volume", index=0, device=0
ControlAmp: chs=3, dir=Out, idx=0, ofs=0
Control: name="Front Playback Switch", index=0, device=0
ControlAmp: chs=3, dir=In, idx=2, ofs=0
Signed-off-by: Jaroslav Kysela <perex@perex.cz>
2009-12-08 19:45:25 +03:00
if ( ( kctl - > id . subdevice & HDA_SUBDEV_NID_FLAG ) ! = 0 & & nid = = 0 )
nid = kctl - > id . subdevice & 0xffff ;
2009-12-10 15:57:01 +03:00
if ( kctl - > id . subdevice & ( HDA_SUBDEV_NID_FLAG | HDA_SUBDEV_AMP_FLAG ) )
2009-11-12 12:15:48 +03:00
kctl - > id . subdevice = 0 ;
2008-07-30 17:01:45 +04:00
err = snd_ctl_add ( codec - > bus - > card , kctl ) ;
if ( err < 0 )
return err ;
2009-11-11 15:43:01 +03:00
item = snd_array_new ( & codec - > mixers ) ;
if ( ! item )
2008-07-30 17:01:45 +04:00
return - ENOMEM ;
2009-11-11 15:43:01 +03:00
item - > kctl = kctl ;
item - > nid = nid ;
ALSA: hda - introduce HDA_SUBDEV_AMP_FLAG (ControlAmp in proc)
The purpose of this changeset is to show information about amplifier
setting in the codec proc file. Something like:
Control: name="Front Playback Volume", index=0, device=0
ControlAmp: chs=3, dir=Out, idx=0, ofs=0
Control: name="Front Playback Switch", index=0, device=0
ControlAmp: chs=3, dir=In, idx=2, ofs=0
Signed-off-by: Jaroslav Kysela <perex@perex.cz>
2009-12-08 19:45:25 +03:00
item - > flags = flags ;
2008-07-30 17:01:45 +04:00
return 0 ;
}
2008-11-28 17:17:06 +03:00
EXPORT_SYMBOL_HDA ( snd_hda_ctl_add ) ;
2008-07-30 17:01:45 +04:00
2009-12-08 18:13:32 +03:00
/**
* snd_hda_add_nid - Assign a NID to a control element
* @ codec : HD - audio codec
* @ nid : corresponding NID ( optional )
* @ kctl : the control element to assign
* @ index : index to kctl
*
* Add the given control element to an array inside the codec instance .
* This function is used when # snd_hda_ctl_add cannot be used for 1 : 1
* NID : KCTL mapping - for example " Capture Source " selector .
*/
int snd_hda_add_nid ( struct hda_codec * codec , struct snd_kcontrol * kctl ,
unsigned int index , hda_nid_t nid )
{
struct hda_nid_item * item ;
if ( nid > 0 ) {
item = snd_array_new ( & codec - > nids ) ;
if ( ! item )
return - ENOMEM ;
item - > kctl = kctl ;
item - > index = index ;
item - > nid = nid ;
return 0 ;
}
2010-03-15 11:05:46 +03:00
printk ( KERN_ERR " hda-codec: no NID for mapping control %s:%d:%d \n " ,
kctl - > id . name , kctl - > id . index , index ) ;
2009-12-08 18:13:32 +03:00
return - EINVAL ;
}
EXPORT_SYMBOL_HDA ( snd_hda_add_nid ) ;
2009-11-16 16:58:17 +03:00
/**
* snd_hda_ctls_clear - Clear all controls assigned to the given codec
* @ codec : HD - audio codec
*/
2008-07-30 17:01:45 +04:00
void snd_hda_ctls_clear ( struct hda_codec * codec )
{
int i ;
2009-11-11 15:43:01 +03:00
struct hda_nid_item * items = codec - > mixers . list ;
2008-07-30 17:01:45 +04:00
for ( i = 0 ; i < codec - > mixers . used ; i + + )
2009-11-11 15:43:01 +03:00
snd_ctl_remove ( codec - > bus - > card , items [ i ] . kctl ) ;
2008-07-30 17:01:45 +04:00
snd_array_free ( & codec - > mixers ) ;
2009-12-08 18:13:32 +03:00
snd_array_free ( & codec - > nids ) ;
2008-07-30 17:01:45 +04:00
}
2009-02-23 18:57:04 +03:00
/* pseudo device locking
* toggle card - > shutdown to allow / disallow the device access ( as a hack )
*/
static int hda_lock_devices ( struct snd_card * card )
2008-07-30 17:01:45 +04:00
{
2009-02-23 18:57:04 +03:00
spin_lock ( & card - > files_lock ) ;
if ( card - > shutdown ) {
spin_unlock ( & card - > files_lock ) ;
return - EINVAL ;
}
card - > shutdown = 1 ;
spin_unlock ( & card - > files_lock ) ;
return 0 ;
}
static void hda_unlock_devices ( struct snd_card * card )
{
spin_lock ( & card - > files_lock ) ;
card - > shutdown = 0 ;
spin_unlock ( & card - > files_lock ) ;
}
2009-11-16 16:58:17 +03:00
/**
* snd_hda_codec_reset - Clear all objects assigned to the codec
* @ codec : HD - audio codec
*
* This frees the all PCM and control elements assigned to the codec , and
* clears the caches and restores the pin default configurations .
*
* When a device is being used , it returns - EBSY . If successfully freed ,
* returns zero .
*/
2009-02-23 18:57:04 +03:00
int snd_hda_codec_reset ( struct hda_codec * codec )
{
struct snd_card * card = codec - > bus - > card ;
int i , pcm ;
if ( hda_lock_devices ( card ) < 0 )
return - EBUSY ;
/* check whether the codec isn't used by any mixer or PCM streams */
if ( ! list_empty ( & card - > ctl_files ) ) {
hda_unlock_devices ( card ) ;
return - EBUSY ;
}
for ( pcm = 0 ; pcm < codec - > num_pcms ; pcm + + ) {
struct hda_pcm * cpcm = & codec - > pcm_info [ pcm ] ;
if ( ! cpcm - > pcm )
continue ;
if ( cpcm - > pcm - > streams [ 0 ] . substream_opened | |
cpcm - > pcm - > streams [ 1 ] . substream_opened ) {
hda_unlock_devices ( card ) ;
return - EBUSY ;
}
}
/* OK, let it free */
2008-07-30 17:01:45 +04:00
# ifdef CONFIG_SND_HDA_POWER_SAVE
cancel_delayed_work ( & codec - > power_work ) ;
2009-01-12 12:09:24 +03:00
flush_workqueue ( codec - > bus - > workq ) ;
2008-07-30 17:01:45 +04:00
# endif
snd_hda_ctls_clear ( codec ) ;
/* relase PCMs */
for ( i = 0 ; i < codec - > num_pcms ; i + + ) {
2008-11-27 16:17:01 +03:00
if ( codec - > pcm_info [ i ] . pcm ) {
2009-02-23 18:57:04 +03:00
snd_device_free ( card , codec - > pcm_info [ i ] . pcm ) ;
2008-11-27 16:17:01 +03:00
clear_bit ( codec - > pcm_info [ i ] . device ,
codec - > bus - > pcm_dev_bits ) ;
}
2008-07-30 17:01:45 +04:00
}
if ( codec - > patch_ops . free )
codec - > patch_ops . free ( codec ) ;
2008-11-28 16:36:23 +03:00
codec - > proc_widget_hook = NULL ;
2008-07-30 17:01:45 +04:00
codec - > spec = NULL ;
free_hda_cache ( & codec - > amp_cache ) ;
free_hda_cache ( & codec - > cmd_cache ) ;
2008-12-19 12:12:02 +03:00
init_hda_cache ( & codec - > amp_cache , sizeof ( struct hda_amp_info ) ) ;
init_hda_cache ( & codec - > cmd_cache , sizeof ( struct hda_cache_head ) ) ;
2009-02-23 11:42:57 +03:00
/* free only driver_pins so that init_pins + user_pins are restored */
snd_array_free ( & codec - > driver_pins ) ;
2009-02-20 16:11:16 +03:00
restore_pincfgs ( codec ) ;
2008-07-30 17:01:45 +04:00
codec - > num_pcms = 0 ;
codec - > pcm_info = NULL ;
codec - > preset = NULL ;
2009-03-02 12:35:29 +03:00
memset ( & codec - > patch_ops , 0 , sizeof ( codec - > patch_ops ) ) ;
codec - > slave_dig_outs = NULL ;
codec - > spdif_status_reset = 0 ;
2008-11-27 17:47:11 +03:00
module_put ( codec - > owner ) ;
codec - > owner = NULL ;
2009-02-23 18:57:04 +03:00
/* allow device access again */
hda_unlock_devices ( card ) ;
return 0 ;
2008-07-30 17:01:45 +04:00
}
2009-11-16 16:58:17 +03:00
/**
* snd_hda_add_vmaster - create a virtual master control and add slaves
* @ codec : HD - audio codec
* @ name : vmaster control name
* @ tlv : TLV data ( optional )
* @ slaves : slave control names ( optional )
*
* Create a virtual master control with the given name . The TLV data
* must be either NULL or a valid data .
*
* @ slaves is a NULL - terminated array of strings , each of which is a
* slave control name . All controls with these names are assigned to
* the new virtual master control .
*
* This function returns zero if successful or a negative error code .
*/
2008-01-10 18:53:55 +03:00
int snd_hda_add_vmaster ( struct hda_codec * codec , char * name ,
2011-01-17 13:29:34 +03:00
unsigned int * tlv , const char * const * slaves )
2008-01-10 18:53:55 +03:00
{
struct snd_kcontrol * kctl ;
2011-01-17 13:29:34 +03:00
const char * const * s ;
2008-01-10 18:53:55 +03:00
int err ;
2008-02-22 20:43:50 +03:00
for ( s = slaves ; * s & & ! snd_hda_find_mixer_ctl ( codec , * s ) ; s + + )
;
if ( ! * s ) {
snd_printdd ( " No slave found for %s \n " , name ) ;
return 0 ;
}
2008-01-10 18:53:55 +03:00
kctl = snd_ctl_make_virtual_master ( name , tlv ) ;
if ( ! kctl )
return - ENOMEM ;
2009-11-11 15:43:01 +03:00
err = snd_hda_ctl_add ( codec , 0 , kctl ) ;
2008-01-10 18:53:55 +03:00
if ( err < 0 )
return err ;
2010-02-28 22:16:53 +03:00
2008-01-10 18:53:55 +03:00
for ( s = slaves ; * s ; s + + ) {
struct snd_kcontrol * sctl ;
2009-03-06 12:08:14 +03:00
int i = 0 ;
for ( ; ; ) {
sctl = _snd_hda_find_mixer_ctl ( codec , * s , i ) ;
if ( ! sctl ) {
if ( ! i )
snd_printdd ( " Cannot find slave %s, "
" skipped \n " , * s ) ;
break ;
}
err = snd_ctl_add_slave ( kctl , sctl ) ;
if ( err < 0 )
return err ;
i + + ;
2008-01-10 18:53:55 +03:00
}
}
return 0 ;
}
2008-11-28 17:17:06 +03:00
EXPORT_SYMBOL_HDA ( snd_hda_add_vmaster ) ;
2008-01-10 18:53:55 +03:00
2009-11-16 16:58:17 +03:00
/**
* snd_hda_mixer_amp_switch_info - Info callback for a standard AMP mixer switch
*
* The control element is supposed to have the private_value field
* set up via HDA_COMPOSE_AMP_VAL * ( ) or related macros .
*/
2007-04-16 13:29:14 +04:00
int snd_hda_mixer_amp_switch_info ( struct snd_kcontrol * kcontrol ,
struct snd_ctl_elem_info * uinfo )
2005-04-17 02:20:36 +04:00
{
int chs = get_amp_channels ( kcontrol ) ;
uinfo - > type = SNDRV_CTL_ELEM_TYPE_BOOLEAN ;
uinfo - > count = chs = = 3 ? 2 : 1 ;
uinfo - > value . integer . min = 0 ;
uinfo - > value . integer . max = 1 ;
return 0 ;
}
2008-11-28 17:17:06 +03:00
EXPORT_SYMBOL_HDA ( snd_hda_mixer_amp_switch_info ) ;
2005-04-17 02:20:36 +04:00
2009-11-16 16:58:17 +03:00
/**
* snd_hda_mixer_amp_switch_get - Get callback for a standard AMP mixer switch
*
* The control element is supposed to have the private_value field
* set up via HDA_COMPOSE_AMP_VAL * ( ) or related macros .
*/
2007-04-16 13:29:14 +04:00
int snd_hda_mixer_amp_switch_get ( struct snd_kcontrol * kcontrol ,
struct snd_ctl_elem_value * ucontrol )
2005-04-17 02:20:36 +04:00
{
struct hda_codec * codec = snd_kcontrol_chip ( kcontrol ) ;
hda_nid_t nid = get_amp_nid ( kcontrol ) ;
int chs = get_amp_channels ( kcontrol ) ;
int dir = get_amp_direction ( kcontrol ) ;
int idx = get_amp_index ( kcontrol ) ;
long * valp = ucontrol - > value . integer . value ;
if ( chs & 1 )
2007-04-16 13:29:14 +04:00
* valp + + = ( snd_hda_codec_amp_read ( codec , nid , 0 , dir , idx ) &
2007-08-10 19:11:07 +04:00
HDA_AMP_MUTE ) ? 0 : 1 ;
2005-04-17 02:20:36 +04:00
if ( chs & 2 )
2007-04-16 13:29:14 +04:00
* valp = ( snd_hda_codec_amp_read ( codec , nid , 1 , dir , idx ) &
2007-08-10 19:11:07 +04:00
HDA_AMP_MUTE ) ? 0 : 1 ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
2008-11-28 17:17:06 +03:00
EXPORT_SYMBOL_HDA ( snd_hda_mixer_amp_switch_get ) ;
2005-04-17 02:20:36 +04:00
2009-11-16 16:58:17 +03:00
/**
* snd_hda_mixer_amp_switch_put - Put callback for a standard AMP mixer switch
*
* The control element is supposed to have the private_value field
* set up via HDA_COMPOSE_AMP_VAL * ( ) or related macros .
*/
2007-04-16 13:29:14 +04:00
int snd_hda_mixer_amp_switch_put ( struct snd_kcontrol * kcontrol ,
struct snd_ctl_elem_value * ucontrol )
2005-04-17 02:20:36 +04:00
{
struct hda_codec * codec = snd_kcontrol_chip ( kcontrol ) ;
hda_nid_t nid = get_amp_nid ( kcontrol ) ;
int chs = get_amp_channels ( kcontrol ) ;
int dir = get_amp_direction ( kcontrol ) ;
int idx = get_amp_index ( kcontrol ) ;
long * valp = ucontrol - > value . integer . value ;
int change = 0 ;
2007-08-10 19:21:45 +04:00
snd_hda_power_up ( codec ) ;
2005-07-29 14:17:20 +04:00
if ( chs & 1 ) {
2005-06-08 16:43:58 +04:00
change = snd_hda_codec_amp_update ( codec , nid , 0 , dir , idx ,
2007-08-10 19:11:07 +04:00
HDA_AMP_MUTE ,
* valp ? 0 : HDA_AMP_MUTE ) ;
2005-07-29 14:17:20 +04:00
valp + + ;
}
2005-06-08 16:43:58 +04:00
if ( chs & 2 )
change | = snd_hda_codec_amp_update ( codec , nid , 1 , dir , idx ,
2007-08-10 19:11:07 +04:00
HDA_AMP_MUTE ,
* valp ? 0 : HDA_AMP_MUTE ) ;
2010-09-21 11:57:06 +04:00
hda_call_check_power_status ( codec , nid ) ;
2007-08-10 19:21:45 +04:00
snd_hda_power_down ( codec ) ;
2005-04-17 02:20:36 +04:00
return change ;
}
2008-11-28 17:17:06 +03:00
EXPORT_SYMBOL_HDA ( snd_hda_mixer_amp_switch_put ) ;
2005-04-17 02:20:36 +04:00
2009-11-16 17:35:59 +03:00
# ifdef CONFIG_SND_HDA_INPUT_BEEP
2009-11-16 16:58:17 +03:00
/**
* snd_hda_mixer_amp_switch_put_beep - Put callback for a beep AMP switch
*
* This function calls snd_hda_enable_beep_device ( ) , which behaves differently
* depending on beep_mode option .
*/
2009-10-21 16:48:23 +04:00
int snd_hda_mixer_amp_switch_put_beep ( struct snd_kcontrol * kcontrol ,
struct snd_ctl_elem_value * ucontrol )
{
struct hda_codec * codec = snd_kcontrol_chip ( kcontrol ) ;
long * valp = ucontrol - > value . integer . value ;
snd_hda_enable_beep_device ( codec , * valp ) ;
return snd_hda_mixer_amp_switch_put ( kcontrol , ucontrol ) ;
}
EXPORT_SYMBOL_HDA ( snd_hda_mixer_amp_switch_put_beep ) ;
2009-11-16 17:35:59 +03:00
# endif /* CONFIG_SND_HDA_INPUT_BEEP */
2009-10-21 16:48:23 +04:00
2005-11-02 20:26:49 +03:00
/*
* bound volume controls
*
* bind multiple volumes ( # indices , from 0 )
*/
# define AMP_VAL_IDX_SHIFT 19
# define AMP_VAL_IDX_MASK (0x0f<<19)
2009-11-16 16:58:17 +03:00
/**
* snd_hda_mixer_bind_switch_get - Get callback for a bound volume control
*
* The control element is supposed to have the private_value field
* set up via HDA_BIND_MUTE * ( ) macros .
*/
2007-04-16 13:29:14 +04:00
int snd_hda_mixer_bind_switch_get ( struct snd_kcontrol * kcontrol ,
struct snd_ctl_elem_value * ucontrol )
2005-11-02 20:26:49 +03:00
{
struct hda_codec * codec = snd_kcontrol_chip ( kcontrol ) ;
unsigned long pval ;
int err ;
2009-01-09 11:45:24 +03:00
mutex_lock ( & codec - > control_mutex ) ;
2005-11-02 20:26:49 +03:00
pval = kcontrol - > private_value ;
kcontrol - > private_value = pval & ~ AMP_VAL_IDX_MASK ; /* index 0 */
err = snd_hda_mixer_amp_switch_get ( kcontrol , ucontrol ) ;
kcontrol - > private_value = pval ;
2009-01-09 11:45:24 +03:00
mutex_unlock ( & codec - > control_mutex ) ;
2005-11-02 20:26:49 +03:00
return err ;
}
2008-11-28 17:17:06 +03:00
EXPORT_SYMBOL_HDA ( snd_hda_mixer_bind_switch_get ) ;
2005-11-02 20:26:49 +03:00
2009-11-16 16:58:17 +03:00
/**
* snd_hda_mixer_bind_switch_put - Put callback for a bound volume control
*
* The control element is supposed to have the private_value field
* set up via HDA_BIND_MUTE * ( ) macros .
*/
2007-04-16 13:29:14 +04:00
int snd_hda_mixer_bind_switch_put ( struct snd_kcontrol * kcontrol ,
struct snd_ctl_elem_value * ucontrol )
2005-11-02 20:26:49 +03:00
{
struct hda_codec * codec = snd_kcontrol_chip ( kcontrol ) ;
unsigned long pval ;
int i , indices , err = 0 , change = 0 ;
2009-01-09 11:45:24 +03:00
mutex_lock ( & codec - > control_mutex ) ;
2005-11-02 20:26:49 +03:00
pval = kcontrol - > private_value ;
indices = ( pval & AMP_VAL_IDX_MASK ) > > AMP_VAL_IDX_SHIFT ;
for ( i = 0 ; i < indices ; i + + ) {
2007-04-16 13:29:14 +04:00
kcontrol - > private_value = ( pval & ~ AMP_VAL_IDX_MASK ) |
( i < < AMP_VAL_IDX_SHIFT ) ;
2005-11-02 20:26:49 +03:00
err = snd_hda_mixer_amp_switch_put ( kcontrol , ucontrol ) ;
if ( err < 0 )
break ;
change | = err ;
}
kcontrol - > private_value = pval ;
2009-01-09 11:45:24 +03:00
mutex_unlock ( & codec - > control_mutex ) ;
2005-11-02 20:26:49 +03:00
return err < 0 ? err : change ;
}
2008-11-28 17:17:06 +03:00
EXPORT_SYMBOL_HDA ( snd_hda_mixer_bind_switch_put ) ;
2005-11-02 20:26:49 +03:00
2009-11-16 16:58:17 +03:00
/**
* snd_hda_mixer_bind_ctls_info - Info callback for a generic bound control
*
* The control element is supposed to have the private_value field
* set up via HDA_BIND_VOL ( ) or HDA_BIND_SW ( ) macros .
2007-07-27 21:02:40 +04:00
*/
int snd_hda_mixer_bind_ctls_info ( struct snd_kcontrol * kcontrol ,
struct snd_ctl_elem_info * uinfo )
{
struct hda_codec * codec = snd_kcontrol_chip ( kcontrol ) ;
struct hda_bind_ctls * c ;
int err ;
2009-01-09 11:45:24 +03:00
mutex_lock ( & codec - > control_mutex ) ;
2008-02-22 20:43:16 +03:00
c = ( struct hda_bind_ctls * ) kcontrol - > private_value ;
2007-07-27 21:02:40 +04:00
kcontrol - > private_value = * c - > values ;
err = c - > ops - > info ( kcontrol , uinfo ) ;
kcontrol - > private_value = ( long ) c ;
2009-01-09 11:45:24 +03:00
mutex_unlock ( & codec - > control_mutex ) ;
2007-07-27 21:02:40 +04:00
return err ;
}
2008-11-28 17:17:06 +03:00
EXPORT_SYMBOL_HDA ( snd_hda_mixer_bind_ctls_info ) ;
2007-07-27 21:02:40 +04:00
2009-11-16 16:58:17 +03:00
/**
* snd_hda_mixer_bind_ctls_get - Get callback for a generic bound control
*
* The control element is supposed to have the private_value field
* set up via HDA_BIND_VOL ( ) or HDA_BIND_SW ( ) macros .
*/
2007-07-27 21:02:40 +04:00
int snd_hda_mixer_bind_ctls_get ( struct snd_kcontrol * kcontrol ,
struct snd_ctl_elem_value * ucontrol )
{
struct hda_codec * codec = snd_kcontrol_chip ( kcontrol ) ;
struct hda_bind_ctls * c ;
int err ;
2009-01-09 11:45:24 +03:00
mutex_lock ( & codec - > control_mutex ) ;
2008-02-22 20:43:16 +03:00
c = ( struct hda_bind_ctls * ) kcontrol - > private_value ;
2007-07-27 21:02:40 +04:00
kcontrol - > private_value = * c - > values ;
err = c - > ops - > get ( kcontrol , ucontrol ) ;
kcontrol - > private_value = ( long ) c ;
2009-01-09 11:45:24 +03:00
mutex_unlock ( & codec - > control_mutex ) ;
2007-07-27 21:02:40 +04:00
return err ;
}
2008-11-28 17:17:06 +03:00
EXPORT_SYMBOL_HDA ( snd_hda_mixer_bind_ctls_get ) ;
2007-07-27 21:02:40 +04:00
2009-11-16 16:58:17 +03:00
/**
* snd_hda_mixer_bind_ctls_put - Put callback for a generic bound control
*
* The control element is supposed to have the private_value field
* set up via HDA_BIND_VOL ( ) or HDA_BIND_SW ( ) macros .
*/
2007-07-27 21:02:40 +04:00
int snd_hda_mixer_bind_ctls_put ( struct snd_kcontrol * kcontrol ,
struct snd_ctl_elem_value * ucontrol )
{
struct hda_codec * codec = snd_kcontrol_chip ( kcontrol ) ;
struct hda_bind_ctls * c ;
unsigned long * vals ;
int err = 0 , change = 0 ;
2009-01-09 11:45:24 +03:00
mutex_lock ( & codec - > control_mutex ) ;
2008-02-22 20:43:16 +03:00
c = ( struct hda_bind_ctls * ) kcontrol - > private_value ;
2007-07-27 21:02:40 +04:00
for ( vals = c - > values ; * vals ; vals + + ) {
kcontrol - > private_value = * vals ;
err = c - > ops - > put ( kcontrol , ucontrol ) ;
if ( err < 0 )
break ;
change | = err ;
}
kcontrol - > private_value = ( long ) c ;
2009-01-09 11:45:24 +03:00
mutex_unlock ( & codec - > control_mutex ) ;
2007-07-27 21:02:40 +04:00
return err < 0 ? err : change ;
}
2008-11-28 17:17:06 +03:00
EXPORT_SYMBOL_HDA ( snd_hda_mixer_bind_ctls_put ) ;
2007-07-27 21:02:40 +04:00
2009-11-16 16:58:17 +03:00
/**
* snd_hda_mixer_bind_tlv - TLV callback for a generic bound control
*
* The control element is supposed to have the private_value field
* set up via HDA_BIND_VOL ( ) macro .
*/
2007-07-27 21:02:40 +04:00
int snd_hda_mixer_bind_tlv ( struct snd_kcontrol * kcontrol , int op_flag ,
unsigned int size , unsigned int __user * tlv )
{
struct hda_codec * codec = snd_kcontrol_chip ( kcontrol ) ;
struct hda_bind_ctls * c ;
int err ;
2009-01-09 11:45:24 +03:00
mutex_lock ( & codec - > control_mutex ) ;
2008-02-22 20:43:16 +03:00
c = ( struct hda_bind_ctls * ) kcontrol - > private_value ;
2007-07-27 21:02:40 +04:00
kcontrol - > private_value = * c - > values ;
err = c - > ops - > tlv ( kcontrol , op_flag , size , tlv ) ;
kcontrol - > private_value = ( long ) c ;
2009-01-09 11:45:24 +03:00
mutex_unlock ( & codec - > control_mutex ) ;
2007-07-27 21:02:40 +04:00
return err ;
}
2008-11-28 17:17:06 +03:00
EXPORT_SYMBOL_HDA ( snd_hda_mixer_bind_tlv ) ;
2007-07-27 21:02:40 +04:00
struct hda_ctl_ops snd_hda_bind_vol = {
. info = snd_hda_mixer_amp_volume_info ,
. get = snd_hda_mixer_amp_volume_get ,
. put = snd_hda_mixer_amp_volume_put ,
. tlv = snd_hda_mixer_amp_tlv
} ;
2008-11-28 17:17:06 +03:00
EXPORT_SYMBOL_HDA ( snd_hda_bind_vol ) ;
2007-07-27 21:02:40 +04:00
struct hda_ctl_ops snd_hda_bind_sw = {
. info = snd_hda_mixer_amp_switch_info ,
. get = snd_hda_mixer_amp_switch_get ,
. put = snd_hda_mixer_amp_switch_put ,
. tlv = snd_hda_mixer_amp_tlv
} ;
2008-11-28 17:17:06 +03:00
EXPORT_SYMBOL_HDA ( snd_hda_bind_sw ) ;
2007-07-27 21:02:40 +04:00
2005-04-17 02:20:36 +04:00
/*
* SPDIF out controls
*/
2007-04-16 13:29:14 +04:00
static int snd_hda_spdif_mask_info ( struct snd_kcontrol * kcontrol ,
struct snd_ctl_elem_info * uinfo )
2005-04-17 02:20:36 +04:00
{
uinfo - > type = SNDRV_CTL_ELEM_TYPE_IEC958 ;
uinfo - > count = 1 ;
return 0 ;
}
2007-04-16 13:29:14 +04:00
static int snd_hda_spdif_cmask_get ( struct snd_kcontrol * kcontrol ,
struct snd_ctl_elem_value * ucontrol )
2005-04-17 02:20:36 +04:00
{
ucontrol - > value . iec958 . status [ 0 ] = IEC958_AES0_PROFESSIONAL |
IEC958_AES0_NONAUDIO |
IEC958_AES0_CON_EMPHASIS_5015 |
IEC958_AES0_CON_NOT_COPYRIGHT ;
ucontrol - > value . iec958 . status [ 1 ] = IEC958_AES1_CON_CATEGORY |
IEC958_AES1_CON_ORIGINAL ;
return 0 ;
}
2007-04-16 13:29:14 +04:00
static int snd_hda_spdif_pmask_get ( struct snd_kcontrol * kcontrol ,
struct snd_ctl_elem_value * ucontrol )
2005-04-17 02:20:36 +04:00
{
ucontrol - > value . iec958 . status [ 0 ] = IEC958_AES0_PROFESSIONAL |
IEC958_AES0_NONAUDIO |
IEC958_AES0_PRO_EMPHASIS_5015 ;
return 0 ;
}
2007-04-16 13:29:14 +04:00
static int snd_hda_spdif_default_get ( struct snd_kcontrol * kcontrol ,
struct snd_ctl_elem_value * ucontrol )
2005-04-17 02:20:36 +04:00
{
struct hda_codec * codec = snd_kcontrol_chip ( kcontrol ) ;
ucontrol - > value . iec958 . status [ 0 ] = codec - > spdif_status & 0xff ;
ucontrol - > value . iec958 . status [ 1 ] = ( codec - > spdif_status > > 8 ) & 0xff ;
ucontrol - > value . iec958 . status [ 2 ] = ( codec - > spdif_status > > 16 ) & 0xff ;
ucontrol - > value . iec958 . status [ 3 ] = ( codec - > spdif_status > > 24 ) & 0xff ;
return 0 ;
}
/* convert from SPDIF status bits to HDA SPDIF bits
* bit 0 ( DigEn ) is always set zero ( to be filled later )
*/
static unsigned short convert_from_spdif_status ( unsigned int sbits )
{
unsigned short val = 0 ;
if ( sbits & IEC958_AES0_PROFESSIONAL )
2007-04-16 13:29:14 +04:00
val | = AC_DIG1_PROFESSIONAL ;
2005-04-17 02:20:36 +04:00
if ( sbits & IEC958_AES0_NONAUDIO )
2007-04-16 13:29:14 +04:00
val | = AC_DIG1_NONAUDIO ;
2005-04-17 02:20:36 +04:00
if ( sbits & IEC958_AES0_PROFESSIONAL ) {
2007-04-16 13:29:14 +04:00
if ( ( sbits & IEC958_AES0_PRO_EMPHASIS ) = =
IEC958_AES0_PRO_EMPHASIS_5015 )
val | = AC_DIG1_EMPHASIS ;
2005-04-17 02:20:36 +04:00
} else {
2007-04-16 13:29:14 +04:00
if ( ( sbits & IEC958_AES0_CON_EMPHASIS ) = =
IEC958_AES0_CON_EMPHASIS_5015 )
val | = AC_DIG1_EMPHASIS ;
if ( ! ( sbits & IEC958_AES0_CON_NOT_COPYRIGHT ) )
val | = AC_DIG1_COPYRIGHT ;
2005-04-17 02:20:36 +04:00
if ( sbits & ( IEC958_AES1_CON_ORIGINAL < < 8 ) )
2007-04-16 13:29:14 +04:00
val | = AC_DIG1_LEVEL ;
2005-04-17 02:20:36 +04:00
val | = sbits & ( IEC958_AES1_CON_CATEGORY < < 8 ) ;
}
return val ;
}
/* convert to SPDIF status bits from HDA SPDIF bits
*/
static unsigned int convert_to_spdif_status ( unsigned short val )
{
unsigned int sbits = 0 ;
2007-04-16 13:29:14 +04:00
if ( val & AC_DIG1_NONAUDIO )
2005-04-17 02:20:36 +04:00
sbits | = IEC958_AES0_NONAUDIO ;
2007-04-16 13:29:14 +04:00
if ( val & AC_DIG1_PROFESSIONAL )
2005-04-17 02:20:36 +04:00
sbits | = IEC958_AES0_PROFESSIONAL ;
if ( sbits & IEC958_AES0_PROFESSIONAL ) {
2007-04-16 13:29:14 +04:00
if ( sbits & AC_DIG1_EMPHASIS )
2005-04-17 02:20:36 +04:00
sbits | = IEC958_AES0_PRO_EMPHASIS_5015 ;
} else {
2007-04-16 13:29:14 +04:00
if ( val & AC_DIG1_EMPHASIS )
2005-04-17 02:20:36 +04:00
sbits | = IEC958_AES0_CON_EMPHASIS_5015 ;
2007-04-16 13:29:14 +04:00
if ( ! ( val & AC_DIG1_COPYRIGHT ) )
2005-04-17 02:20:36 +04:00
sbits | = IEC958_AES0_CON_NOT_COPYRIGHT ;
2007-04-16 13:29:14 +04:00
if ( val & AC_DIG1_LEVEL )
2005-04-17 02:20:36 +04:00
sbits | = ( IEC958_AES1_CON_ORIGINAL < < 8 ) ;
sbits | = val & ( 0x7f < < 8 ) ;
}
return sbits ;
}
2008-09-25 18:32:41 +04:00
/* set digital convert verbs both for the given NID and its slaves */
static void set_dig_out ( struct hda_codec * codec , hda_nid_t nid ,
int verb , int val )
{
hda_nid_t * d ;
2008-11-25 10:17:20 +03:00
snd_hda_codec_write_cache ( codec , nid , 0 , verb , val ) ;
2008-09-25 18:32:41 +04:00
d = codec - > slave_dig_outs ;
if ( ! d )
return ;
for ( ; * d ; d + + )
2008-11-25 10:17:20 +03:00
snd_hda_codec_write_cache ( codec , * d , 0 , verb , val ) ;
2008-09-25 18:32:41 +04:00
}
static inline void set_dig_out_convert ( struct hda_codec * codec , hda_nid_t nid ,
int dig1 , int dig2 )
{
if ( dig1 ! = - 1 )
set_dig_out ( codec , nid , AC_VERB_SET_DIGI_CONVERT_1 , dig1 ) ;
if ( dig2 ! = - 1 )
set_dig_out ( codec , nid , AC_VERB_SET_DIGI_CONVERT_2 , dig2 ) ;
}
2007-04-16 13:29:14 +04:00
static int snd_hda_spdif_default_put ( struct snd_kcontrol * kcontrol ,
struct snd_ctl_elem_value * ucontrol )
2005-04-17 02:20:36 +04:00
{
struct hda_codec * codec = snd_kcontrol_chip ( kcontrol ) ;
hda_nid_t nid = kcontrol - > private_value ;
unsigned short val ;
int change ;
2006-01-16 18:34:20 +03:00
mutex_lock ( & codec - > spdif_mutex ) ;
2005-04-17 02:20:36 +04:00
codec - > spdif_status = ucontrol - > value . iec958 . status [ 0 ] |
( ( unsigned int ) ucontrol - > value . iec958 . status [ 1 ] < < 8 ) |
( ( unsigned int ) ucontrol - > value . iec958 . status [ 2 ] < < 16 ) |
( ( unsigned int ) ucontrol - > value . iec958 . status [ 3 ] < < 24 ) ;
val = convert_from_spdif_status ( codec - > spdif_status ) ;
val | = codec - > spdif_ctls & 1 ;
change = codec - > spdif_ctls ! = val ;
codec - > spdif_ctls = val ;
2008-09-25 18:32:41 +04:00
if ( change )
set_dig_out_convert ( codec , nid , val & 0xff , ( val > > 8 ) & 0xff ) ;
2005-04-17 02:20:36 +04:00
2006-01-16 18:34:20 +03:00
mutex_unlock ( & codec - > spdif_mutex ) ;
2005-04-17 02:20:36 +04:00
return change ;
}
2007-07-23 17:42:26 +04:00
# define snd_hda_spdif_out_switch_info snd_ctl_boolean_mono_info
2005-04-17 02:20:36 +04:00
2007-04-16 13:29:14 +04:00
static int snd_hda_spdif_out_switch_get ( struct snd_kcontrol * kcontrol ,
struct snd_ctl_elem_value * ucontrol )
2005-04-17 02:20:36 +04:00
{
struct hda_codec * codec = snd_kcontrol_chip ( kcontrol ) ;
2007-04-16 13:29:14 +04:00
ucontrol - > value . integer . value [ 0 ] = codec - > spdif_ctls & AC_DIG1_ENABLE ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
2007-04-16 13:29:14 +04:00
static int snd_hda_spdif_out_switch_put ( struct snd_kcontrol * kcontrol ,
struct snd_ctl_elem_value * ucontrol )
2005-04-17 02:20:36 +04:00
{
struct hda_codec * codec = snd_kcontrol_chip ( kcontrol ) ;
hda_nid_t nid = kcontrol - > private_value ;
unsigned short val ;
int change ;
2006-01-16 18:34:20 +03:00
mutex_lock ( & codec - > spdif_mutex ) ;
2007-04-16 13:29:14 +04:00
val = codec - > spdif_ctls & ~ AC_DIG1_ENABLE ;
2005-04-17 02:20:36 +04:00
if ( ucontrol - > value . integer . value [ 0 ] )
2007-04-16 13:29:14 +04:00
val | = AC_DIG1_ENABLE ;
2005-04-17 02:20:36 +04:00
change = codec - > spdif_ctls ! = val ;
2007-08-10 19:09:26 +04:00
if ( change ) {
2005-04-17 02:20:36 +04:00
codec - > spdif_ctls = val ;
2008-09-25 18:32:41 +04:00
set_dig_out_convert ( codec , nid , val & 0xff , - 1 ) ;
2007-04-16 13:29:14 +04:00
/* unmute amp switch (if any) */
if ( ( get_wcaps ( codec , nid ) & AC_WCAP_OUT_AMP ) & &
2007-08-10 19:11:07 +04:00
( val & AC_DIG1_ENABLE ) )
snd_hda_codec_amp_stereo ( codec , nid , HDA_OUTPUT , 0 ,
HDA_AMP_MUTE , 0 ) ;
2005-04-17 02:20:36 +04:00
}
2006-01-16 18:34:20 +03:00
mutex_unlock ( & codec - > spdif_mutex ) ;
2005-04-17 02:20:36 +04:00
return change ;
}
2005-11-17 16:57:47 +03:00
static struct snd_kcontrol_new dig_mixes [ ] = {
2005-04-17 02:20:36 +04:00
{
. access = SNDRV_CTL_ELEM_ACCESS_READ ,
. iface = SNDRV_CTL_ELEM_IFACE_MIXER ,
2010-02-28 22:16:53 +03:00
. name = SNDRV_CTL_NAME_IEC958 ( " " , PLAYBACK , CON_MASK ) ,
2005-04-17 02:20:36 +04:00
. info = snd_hda_spdif_mask_info ,
. get = snd_hda_spdif_cmask_get ,
} ,
{
. access = SNDRV_CTL_ELEM_ACCESS_READ ,
. iface = SNDRV_CTL_ELEM_IFACE_MIXER ,
2010-02-28 22:16:53 +03:00
. name = SNDRV_CTL_NAME_IEC958 ( " " , PLAYBACK , PRO_MASK ) ,
2005-04-17 02:20:36 +04:00
. info = snd_hda_spdif_mask_info ,
. get = snd_hda_spdif_pmask_get ,
} ,
{
. iface = SNDRV_CTL_ELEM_IFACE_MIXER ,
2010-02-28 22:16:53 +03:00
. name = SNDRV_CTL_NAME_IEC958 ( " " , PLAYBACK , DEFAULT ) ,
2005-04-17 02:20:36 +04:00
. info = snd_hda_spdif_mask_info ,
. get = snd_hda_spdif_default_get ,
. put = snd_hda_spdif_default_put ,
} ,
{
. iface = SNDRV_CTL_ELEM_IFACE_MIXER ,
2010-02-28 22:16:53 +03:00
. name = SNDRV_CTL_NAME_IEC958 ( " " , PLAYBACK , SWITCH ) ,
2005-04-17 02:20:36 +04:00
. info = snd_hda_spdif_out_switch_info ,
. get = snd_hda_spdif_out_switch_get ,
. put = snd_hda_spdif_out_switch_put ,
} ,
{ } /* end */
} ;
/**
* snd_hda_create_spdif_out_ctls - create Output SPDIF - related controls
* @ codec : the HDA codec
* @ nid : audio out widget NID
*
* Creates controls related with the SPDIF output .
* Called from each patch supporting the SPDIF out .
*
* Returns 0 if successful , or a negative error code .
*/
2007-08-02 17:51:59 +04:00
int snd_hda_create_spdif_out_ctls ( struct hda_codec * codec , hda_nid_t nid )
2005-04-17 02:20:36 +04:00
{
int err ;
2005-11-17 16:57:47 +03:00
struct snd_kcontrol * kctl ;
struct snd_kcontrol_new * dig_mix ;
2008-02-04 14:31:13 +03:00
int idx ;
2005-04-17 02:20:36 +04:00
2010-12-23 12:17:52 +03:00
idx = find_empty_mixer_ctl_idx ( codec , " IEC958 Playback Switch " ) ;
if ( idx < 0 ) {
2008-02-04 14:31:13 +03:00
printk ( KERN_ERR " hda_codec: too many IEC958 outputs \n " ) ;
return - EBUSY ;
}
2005-04-17 02:20:36 +04:00
for ( dig_mix = dig_mixes ; dig_mix - > name ; dig_mix + + ) {
kctl = snd_ctl_new1 ( dig_mix , codec ) ;
2008-11-04 10:43:08 +03:00
if ( ! kctl )
return - ENOMEM ;
2008-02-04 14:31:13 +03:00
kctl - > id . index = idx ;
2005-04-17 02:20:36 +04:00
kctl - > private_value = nid ;
2009-11-11 15:43:01 +03:00
err = snd_hda_ctl_add ( codec , nid , kctl ) ;
2007-04-16 13:29:14 +04:00
if ( err < 0 )
2005-04-17 02:20:36 +04:00
return err ;
}
2007-04-16 13:29:14 +04:00
codec - > spdif_ctls =
2007-12-19 14:13:44 +03:00
snd_hda_codec_read ( codec , nid , 0 ,
AC_VERB_GET_DIGI_CONVERT_1 , 0 ) ;
2005-04-17 02:20:36 +04:00
codec - > spdif_status = convert_to_spdif_status ( codec - > spdif_ctls ) ;
return 0 ;
}
2008-11-28 17:17:06 +03:00
EXPORT_SYMBOL_HDA ( snd_hda_create_spdif_out_ctls ) ;
2005-04-17 02:20:36 +04:00
2008-02-12 20:37:26 +03:00
/*
* SPDIF sharing with analog output
*/
static int spdif_share_sw_get ( struct snd_kcontrol * kcontrol ,
struct snd_ctl_elem_value * ucontrol )
{
struct hda_multi_out * mout = snd_kcontrol_chip ( kcontrol ) ;
ucontrol - > value . integer . value [ 0 ] = mout - > share_spdif ;
return 0 ;
}
static int spdif_share_sw_put ( struct snd_kcontrol * kcontrol ,
struct snd_ctl_elem_value * ucontrol )
{
struct hda_multi_out * mout = snd_kcontrol_chip ( kcontrol ) ;
mout - > share_spdif = ! ! ucontrol - > value . integer . value [ 0 ] ;
return 0 ;
}
static struct snd_kcontrol_new spdif_share_sw = {
. iface = SNDRV_CTL_ELEM_IFACE_MIXER ,
. name = " IEC958 Default PCM Playback Switch " ,
. info = snd_ctl_boolean_mono_info ,
. get = spdif_share_sw_get ,
. put = spdif_share_sw_put ,
} ;
2009-11-16 16:58:17 +03:00
/**
* snd_hda_create_spdif_share_sw - create Default PCM switch
* @ codec : the HDA codec
* @ mout : multi - out instance
*/
2008-02-12 20:37:26 +03:00
int snd_hda_create_spdif_share_sw ( struct hda_codec * codec ,
struct hda_multi_out * mout )
{
if ( ! mout - > dig_out_nid )
return 0 ;
/* ATTENTION: here mout is passed as private_data, instead of codec */
2009-11-11 15:43:01 +03:00
return snd_hda_ctl_add ( codec , mout - > dig_out_nid ,
snd_ctl_new1 ( & spdif_share_sw , mout ) ) ;
2008-02-12 20:37:26 +03:00
}
2008-11-28 17:17:06 +03:00
EXPORT_SYMBOL_HDA ( snd_hda_create_spdif_share_sw ) ;
2008-02-12 20:37:26 +03:00
2005-04-17 02:20:36 +04:00
/*
* SPDIF input
*/
# define snd_hda_spdif_in_switch_info snd_hda_spdif_out_switch_info
2007-04-16 13:29:14 +04:00
static int snd_hda_spdif_in_switch_get ( struct snd_kcontrol * kcontrol ,
struct snd_ctl_elem_value * ucontrol )
2005-04-17 02:20:36 +04:00
{
struct hda_codec * codec = snd_kcontrol_chip ( kcontrol ) ;
ucontrol - > value . integer . value [ 0 ] = codec - > spdif_in_enable ;
return 0 ;
}
2007-04-16 13:29:14 +04:00
static int snd_hda_spdif_in_switch_put ( struct snd_kcontrol * kcontrol ,
struct snd_ctl_elem_value * ucontrol )
2005-04-17 02:20:36 +04:00
{
struct hda_codec * codec = snd_kcontrol_chip ( kcontrol ) ;
hda_nid_t nid = kcontrol - > private_value ;
unsigned int val = ! ! ucontrol - > value . integer . value [ 0 ] ;
int change ;
2006-01-16 18:34:20 +03:00
mutex_lock ( & codec - > spdif_mutex ) ;
2005-04-17 02:20:36 +04:00
change = codec - > spdif_in_enable ! = val ;
2007-08-10 19:09:26 +04:00
if ( change ) {
2005-04-17 02:20:36 +04:00
codec - > spdif_in_enable = val ;
2007-08-10 19:09:26 +04:00
snd_hda_codec_write_cache ( codec , nid , 0 ,
AC_VERB_SET_DIGI_CONVERT_1 , val ) ;
2005-04-17 02:20:36 +04:00
}
2006-01-16 18:34:20 +03:00
mutex_unlock ( & codec - > spdif_mutex ) ;
2005-04-17 02:20:36 +04:00
return change ;
}
2007-04-16 13:29:14 +04:00
static int snd_hda_spdif_in_status_get ( struct snd_kcontrol * kcontrol ,
struct snd_ctl_elem_value * ucontrol )
2005-04-17 02:20:36 +04:00
{
struct hda_codec * codec = snd_kcontrol_chip ( kcontrol ) ;
hda_nid_t nid = kcontrol - > private_value ;
unsigned short val ;
unsigned int sbits ;
2007-12-19 14:13:44 +03:00
val = snd_hda_codec_read ( codec , nid , 0 , AC_VERB_GET_DIGI_CONVERT_1 , 0 ) ;
2005-04-17 02:20:36 +04:00
sbits = convert_to_spdif_status ( val ) ;
ucontrol - > value . iec958 . status [ 0 ] = sbits ;
ucontrol - > value . iec958 . status [ 1 ] = sbits > > 8 ;
ucontrol - > value . iec958 . status [ 2 ] = sbits > > 16 ;
ucontrol - > value . iec958 . status [ 3 ] = sbits > > 24 ;
return 0 ;
}
2005-11-17 16:57:47 +03:00
static struct snd_kcontrol_new dig_in_ctls [ ] = {
2005-04-17 02:20:36 +04:00
{
. iface = SNDRV_CTL_ELEM_IFACE_MIXER ,
2010-02-28 22:16:53 +03:00
. name = SNDRV_CTL_NAME_IEC958 ( " " , CAPTURE , SWITCH ) ,
2005-04-17 02:20:36 +04:00
. info = snd_hda_spdif_in_switch_info ,
. get = snd_hda_spdif_in_switch_get ,
. put = snd_hda_spdif_in_switch_put ,
} ,
{
. access = SNDRV_CTL_ELEM_ACCESS_READ ,
. iface = SNDRV_CTL_ELEM_IFACE_MIXER ,
2010-02-28 22:16:53 +03:00
. name = SNDRV_CTL_NAME_IEC958 ( " " , CAPTURE , DEFAULT ) ,
2005-04-17 02:20:36 +04:00
. info = snd_hda_spdif_mask_info ,
. get = snd_hda_spdif_in_status_get ,
} ,
{ } /* end */
} ;
/**
* snd_hda_create_spdif_in_ctls - create Input SPDIF - related controls
* @ codec : the HDA codec
* @ nid : audio in widget NID
*
* Creates controls related with the SPDIF input .
* Called from each patch supporting the SPDIF in .
*
* Returns 0 if successful , or a negative error code .
*/
2007-08-02 17:51:59 +04:00
int snd_hda_create_spdif_in_ctls ( struct hda_codec * codec , hda_nid_t nid )
2005-04-17 02:20:36 +04:00
{
int err ;
2005-11-17 16:57:47 +03:00
struct snd_kcontrol * kctl ;
struct snd_kcontrol_new * dig_mix ;
2008-02-04 14:31:13 +03:00
int idx ;
2005-04-17 02:20:36 +04:00
2010-12-23 12:17:52 +03:00
idx = find_empty_mixer_ctl_idx ( codec , " IEC958 Capture Switch " ) ;
if ( idx < 0 ) {
2008-02-04 14:31:13 +03:00
printk ( KERN_ERR " hda_codec: too many IEC958 inputs \n " ) ;
return - EBUSY ;
}
2005-04-17 02:20:36 +04:00
for ( dig_mix = dig_in_ctls ; dig_mix - > name ; dig_mix + + ) {
kctl = snd_ctl_new1 ( dig_mix , codec ) ;
2009-02-06 18:21:20 +03:00
if ( ! kctl )
return - ENOMEM ;
2005-04-17 02:20:36 +04:00
kctl - > private_value = nid ;
2009-11-11 15:43:01 +03:00
err = snd_hda_ctl_add ( codec , nid , kctl ) ;
2007-04-16 13:29:14 +04:00
if ( err < 0 )
2005-04-17 02:20:36 +04:00
return err ;
}
2007-04-16 13:29:14 +04:00
codec - > spdif_in_enable =
2007-12-19 14:13:44 +03:00
snd_hda_codec_read ( codec , nid , 0 ,
AC_VERB_GET_DIGI_CONVERT_1 , 0 ) &
2007-04-16 13:29:14 +04:00
AC_DIG1_ENABLE ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
2008-11-28 17:17:06 +03:00
EXPORT_SYMBOL_HDA ( snd_hda_create_spdif_in_ctls ) ;
2005-04-17 02:20:36 +04:00
2007-08-10 19:21:45 +04:00
# ifdef SND_HDA_NEEDS_RESUME
2007-08-10 19:09:26 +04:00
/*
* command cache
*/
2005-04-17 02:20:36 +04:00
2007-08-10 19:03:40 +04:00
/* build a 32bit cache key with the widget id and the command parameter */
# define build_cmd_cache_key(nid, verb) ((verb << 8) | nid)
# define get_cmd_cache_nid(key) ((key) & 0xff)
# define get_cmd_cache_cmd(key) (((key) >> 8) & 0xffff)
/**
* snd_hda_codec_write_cache - send a single command with caching
* @ codec : the HDA codec
* @ nid : NID to send the command
* @ direct : direct flag
* @ verb : the verb to send
* @ parm : the parameter for the verb
*
* Send a single command without waiting for response .
*
* Returns 0 if successful , or a negative error code .
*/
int snd_hda_codec_write_cache ( struct hda_codec * codec , hda_nid_t nid ,
int direct , unsigned int verb , unsigned int parm )
{
2009-05-26 18:07:57 +04:00
int err = snd_hda_codec_write ( codec , nid , direct , verb , parm ) ;
struct hda_cache_head * c ;
u32 key ;
2008-11-06 18:50:40 +03:00
2009-05-26 18:07:57 +04:00
if ( err < 0 )
return err ;
/* parm may contain the verb stuff for get/set amp */
verb = verb | ( parm > > 8 ) ;
parm & = 0xff ;
key = build_cmd_cache_key ( nid , verb ) ;
mutex_lock ( & codec - > bus - > cmd_mutex ) ;
c = get_alloc_hash ( & codec - > cmd_cache , key ) ;
if ( c )
c - > val = parm ;
mutex_unlock ( & codec - > bus - > cmd_mutex ) ;
return 0 ;
2007-08-10 19:03:40 +04:00
}
2008-11-28 17:17:06 +03:00
EXPORT_SYMBOL_HDA ( snd_hda_codec_write_cache ) ;
2007-08-10 19:03:40 +04:00
2010-03-30 20:03:44 +04:00
/**
* snd_hda_codec_update_cache - check cache and write the cmd only when needed
* @ codec : the HDA codec
* @ nid : NID to send the command
* @ direct : direct flag
* @ verb : the verb to send
* @ parm : the parameter for the verb
*
* This function works like snd_hda_codec_write_cache ( ) , but it doesn ' t send
* command if the parameter is already identical with the cached value .
* If not , it sends the command and refreshes the cache .
*
* Returns 0 if successful , or a negative error code .
*/
int snd_hda_codec_update_cache ( struct hda_codec * codec , hda_nid_t nid ,
int direct , unsigned int verb , unsigned int parm )
{
struct hda_cache_head * c ;
u32 key ;
/* parm may contain the verb stuff for get/set amp */
verb = verb | ( parm > > 8 ) ;
parm & = 0xff ;
key = build_cmd_cache_key ( nid , verb ) ;
mutex_lock ( & codec - > bus - > cmd_mutex ) ;
c = get_hash ( & codec - > cmd_cache , key ) ;
if ( c & & c - > val = = parm ) {
mutex_unlock ( & codec - > bus - > cmd_mutex ) ;
return 0 ;
}
mutex_unlock ( & codec - > bus - > cmd_mutex ) ;
return snd_hda_codec_write_cache ( codec , nid , direct , verb , parm ) ;
}
EXPORT_SYMBOL_HDA ( snd_hda_codec_update_cache ) ;
2009-11-16 16:58:17 +03:00
/**
* snd_hda_codec_resume_cache - Resume the all commands from the cache
* @ codec : HD - audio codec
*
* Execute all verbs recorded in the command caches to resume .
*/
2007-08-10 19:03:40 +04:00
void snd_hda_codec_resume_cache ( struct hda_codec * codec )
{
2008-07-30 17:01:44 +04:00
struct hda_cache_head * buffer = codec - > cmd_cache . buf . list ;
2007-08-10 19:03:40 +04:00
int i ;
2008-07-30 17:01:44 +04:00
for ( i = 0 ; i < codec - > cmd_cache . buf . used ; i + + , buffer + + ) {
2007-08-10 19:03:40 +04:00
u32 key = buffer - > key ;
if ( ! key )
continue ;
snd_hda_codec_write ( codec , get_cmd_cache_nid ( key ) , 0 ,
get_cmd_cache_cmd ( key ) , buffer - > val ) ;
}
}
2008-11-28 17:17:06 +03:00
EXPORT_SYMBOL_HDA ( snd_hda_codec_resume_cache ) ;
2007-08-10 19:03:40 +04:00
/**
* snd_hda_sequence_write_cache - sequence writes with caching
* @ codec : the HDA codec
* @ seq : VERB array to send
*
* Send the commands sequentially from the given array .
* Thte commands are recorded on cache for power - save and resume .
* The array must be terminated with NID = 0.
*/
void snd_hda_sequence_write_cache ( struct hda_codec * codec ,
const struct hda_verb * seq )
{
for ( ; seq - > nid ; seq + + )
snd_hda_codec_write_cache ( codec , seq - > nid , 0 , seq - > verb ,
seq - > param ) ;
}
2008-11-28 17:17:06 +03:00
EXPORT_SYMBOL_HDA ( snd_hda_sequence_write_cache ) ;
2007-08-10 19:21:45 +04:00
# endif /* SND_HDA_NEEDS_RESUME */
2007-08-10 19:03:40 +04:00
2005-11-21 18:33:22 +03:00
/*
* set power state of the codec
*/
static void hda_set_power_state ( struct hda_codec * codec , hda_nid_t fg ,
unsigned int power_state )
{
2007-08-10 19:21:45 +04:00
hda_nid_t nid ;
int i ;
2005-11-21 18:33:22 +03:00
2009-07-22 14:39:24 +04:00
/* this delay seems necessary to avoid click noise at power-down */
if ( power_state = = AC_PWRST_D3 )
msleep ( 100 ) ;
snd_hda_codec_read ( codec , fg , 0 , AC_VERB_SET_POWER_STATE ,
2005-11-21 18:33:22 +03:00
power_state ) ;
2009-07-22 14:39:24 +04:00
/* partial workaround for "azx_get_response timeout" */
2010-02-24 04:38:49 +03:00
if ( power_state = = AC_PWRST_D0 & &
( codec - > vendor_id & 0xffff0000 ) = = 0x14f10000 )
2009-07-22 14:39:24 +04:00
msleep ( 10 ) ;
2005-11-21 18:33:22 +03:00
2007-08-10 19:21:45 +04:00
nid = codec - > start_nid ;
for ( i = 0 ; i < codec - > num_nodes ; i + + , nid + + ) {
2007-11-14 16:53:42 +03:00
unsigned int wcaps = get_wcaps ( codec , nid ) ;
if ( wcaps & AC_WCAP_POWER ) {
2009-07-27 14:54:26 +04:00
unsigned int wid_type = get_wcaps_type ( wcaps ) ;
2009-04-21 15:37:29 +04:00
if ( power_state = = AC_PWRST_D3 & &
wid_type = = AC_WID_PIN ) {
2007-11-14 16:53:42 +03:00
unsigned int pincap ;
/*
* don ' t power down the widget if it controls
* eapd and EAPD_BTLENABLE is set .
*/
2009-03-23 18:35:39 +03:00
pincap = snd_hda_query_pin_caps ( codec , nid ) ;
2007-11-14 16:53:42 +03:00
if ( pincap & AC_PINCAP_EAPD ) {
int eapd = snd_hda_codec_read ( codec ,
nid , 0 ,
AC_VERB_GET_EAPD_BTLENABLE , 0 ) ;
eapd & = 0x02 ;
2009-04-21 15:37:29 +04:00
if ( eapd )
2007-11-14 16:53:42 +03:00
continue ;
}
2007-10-10 12:04:26 +04:00
}
2005-11-21 18:33:22 +03:00
snd_hda_codec_write ( codec , nid , 0 ,
AC_VERB_SET_POWER_STATE ,
power_state ) ;
2007-10-10 12:04:26 +04:00
}
2005-11-21 18:33:22 +03:00
}
2007-08-10 19:21:45 +04:00
if ( power_state = = AC_PWRST_D0 ) {
unsigned long end_time ;
int state ;
/* wait until the codec reachs to D0 */
end_time = jiffies + msecs_to_jiffies ( 500 ) ;
do {
state = snd_hda_codec_read ( codec , fg , 0 ,
AC_VERB_GET_POWER_STATE , 0 ) ;
if ( state = = power_state )
break ;
msleep ( 1 ) ;
} while ( time_after_eq ( end_time , jiffies ) ) ;
}
}
2008-07-30 17:01:46 +04:00
# ifdef CONFIG_SND_HDA_HWDEP
/* execute additional init verbs */
static void hda_exec_init_verbs ( struct hda_codec * codec )
{
if ( codec - > init_verbs . list )
snd_hda_sequence_write ( codec , codec - > init_verbs . list ) ;
}
# else
static inline void hda_exec_init_verbs ( struct hda_codec * codec ) { }
# endif
2007-08-10 19:21:45 +04:00
# ifdef SND_HDA_NEEDS_RESUME
/*
* call suspend and power - down ; used both from PM and power - save
*/
static void hda_call_codec_suspend ( struct hda_codec * codec )
{
if ( codec - > patch_ops . suspend )
codec - > patch_ops . suspend ( codec , PMSG_SUSPEND ) ;
2010-08-06 15:48:11 +04:00
hda_cleanup_all_streams ( codec ) ;
2007-08-10 19:21:45 +04:00
hda_set_power_state ( codec ,
codec - > afg ? codec - > afg : codec - > mfg ,
AC_PWRST_D3 ) ;
# ifdef CONFIG_SND_HDA_POWER_SAVE
2009-11-11 11:34:25 +03:00
snd_hda_update_power_acct ( codec ) ;
2007-08-10 19:21:45 +04:00
cancel_delayed_work ( & codec - > power_work ) ;
2007-08-13 17:29:04 +04:00
codec - > power_on = 0 ;
2007-08-16 18:35:33 +04:00
codec - > power_transition = 0 ;
2009-11-11 11:34:25 +03:00
codec - > power_jiffies = jiffies ;
2007-08-10 19:21:45 +04:00
# endif
2005-11-21 18:33:22 +03:00
}
2007-08-10 19:21:45 +04:00
/*
* kick up codec ; used both from PM and power - save
*/
static void hda_call_codec_resume ( struct hda_codec * codec )
{
hda_set_power_state ( codec ,
codec - > afg ? codec - > afg : codec - > mfg ,
AC_PWRST_D0 ) ;
2009-02-20 16:11:16 +03:00
restore_pincfgs ( codec ) ; /* restore all current pin configs */
2010-07-05 18:50:13 +04:00
restore_shutup_pins ( codec ) ;
2008-07-30 17:01:46 +04:00
hda_exec_init_verbs ( codec ) ;
2007-08-10 19:21:45 +04:00
if ( codec - > patch_ops . resume )
codec - > patch_ops . resume ( codec ) ;
else {
2007-08-14 17:15:52 +04:00
if ( codec - > patch_ops . init )
codec - > patch_ops . init ( codec ) ;
2007-08-10 19:21:45 +04:00
snd_hda_codec_resume_amp ( codec ) ;
snd_hda_codec_resume_cache ( codec ) ;
}
}
# endif /* SND_HDA_NEEDS_RESUME */
2005-11-21 18:33:22 +03:00
2005-04-17 02:20:36 +04:00
/**
* snd_hda_build_controls - build mixer controls
* @ bus : the BUS
*
* Creates mixer controls for each codec included in the bus .
*
* Returns 0 if successful , otherwise a negative error code .
*/
2008-11-27 17:47:11 +03:00
int /*__devinit*/ snd_hda_build_controls ( struct hda_bus * bus )
2005-04-17 02:20:36 +04:00
{
2007-04-16 13:29:14 +04:00
struct hda_codec * codec ;
2005-04-17 02:20:36 +04:00
2007-04-16 13:29:14 +04:00
list_for_each_entry ( codec , & bus - > codec_list , list ) {
2008-07-30 17:01:45 +04:00
int err = snd_hda_codec_build_controls ( codec ) ;
2009-03-02 12:44:15 +03:00
if ( err < 0 ) {
2010-03-15 11:05:46 +03:00
printk ( KERN_ERR " hda_codec: cannot build controls "
2010-02-28 22:16:53 +03:00
" for #%d (error %d) \n " , codec - > addr , err ) ;
2009-03-02 12:44:15 +03:00
err = snd_hda_codec_reset ( codec ) ;
if ( err < 0 ) {
printk ( KERN_ERR
" hda_codec: cannot revert codec \n " ) ;
return err ;
}
}
2005-04-17 02:20:36 +04:00
}
2008-07-30 17:01:45 +04:00
return 0 ;
}
2008-11-28 17:17:06 +03:00
EXPORT_SYMBOL_HDA ( snd_hda_build_controls ) ;
2007-08-10 19:21:45 +04:00
2008-07-30 17:01:45 +04:00
int snd_hda_codec_build_controls ( struct hda_codec * codec )
{
int err = 0 ;
2008-07-30 17:01:46 +04:00
hda_exec_init_verbs ( codec ) ;
2008-07-30 17:01:45 +04:00
/* continue to initialize... */
if ( codec - > patch_ops . init )
err = codec - > patch_ops . init ( codec ) ;
if ( ! err & & codec - > patch_ops . build_controls )
err = codec - > patch_ops . build_controls ( codec ) ;
if ( err < 0 )
return err ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
/*
* stream formats
*/
2005-08-22 15:57:55 +04:00
struct hda_rate_tbl {
unsigned int hz ;
unsigned int alsa_bits ;
unsigned int hda_fmt ;
} ;
2010-08-03 16:21:00 +04:00
/* rate = base * mult / div */
# define HDA_RATE(base, mult, div) \
( AC_FMT_BASE_ # # base # # K | ( ( ( mult ) - 1 ) < < AC_FMT_MULT_SHIFT ) | \
( ( ( div ) - 1 ) < < AC_FMT_DIV_SHIFT ) )
2005-08-22 15:57:55 +04:00
static struct hda_rate_tbl rate_bits [ ] = {
2005-04-17 02:20:36 +04:00
/* rate in Hz, ALSA rate bitmask, HDA format value */
2005-08-22 15:47:16 +04:00
/* autodetected value used in snd_hda_query_supported_pcm */
2010-08-03 16:21:00 +04:00
{ 8000 , SNDRV_PCM_RATE_8000 , HDA_RATE ( 48 , 1 , 6 ) } ,
{ 11025 , SNDRV_PCM_RATE_11025 , HDA_RATE ( 44 , 1 , 4 ) } ,
{ 16000 , SNDRV_PCM_RATE_16000 , HDA_RATE ( 48 , 1 , 3 ) } ,
{ 22050 , SNDRV_PCM_RATE_22050 , HDA_RATE ( 44 , 1 , 2 ) } ,
{ 32000 , SNDRV_PCM_RATE_32000 , HDA_RATE ( 48 , 2 , 3 ) } ,
{ 44100 , SNDRV_PCM_RATE_44100 , HDA_RATE ( 44 , 1 , 1 ) } ,
{ 48000 , SNDRV_PCM_RATE_48000 , HDA_RATE ( 48 , 1 , 1 ) } ,
{ 88200 , SNDRV_PCM_RATE_88200 , HDA_RATE ( 44 , 2 , 1 ) } ,
{ 96000 , SNDRV_PCM_RATE_96000 , HDA_RATE ( 48 , 2 , 1 ) } ,
{ 176400 , SNDRV_PCM_RATE_176400 , HDA_RATE ( 44 , 4 , 1 ) } ,
{ 192000 , SNDRV_PCM_RATE_192000 , HDA_RATE ( 48 , 4 , 1 ) } ,
2007-04-12 15:08:09 +04:00
# define AC_PAR_PCM_RATE_BITS 11
/* up to bits 10, 384kHZ isn't supported properly */
/* not autodetected value */
2010-08-03 16:21:00 +04:00
{ 9600 , SNDRV_PCM_RATE_KNOT , HDA_RATE ( 48 , 1 , 5 ) } ,
2005-08-22 15:47:16 +04:00
2005-08-22 15:57:55 +04:00
{ 0 } /* terminator */
2005-04-17 02:20:36 +04:00
} ;
/**
* snd_hda_calc_stream_format - calculate format bitset
* @ rate : the sample rate
* @ channels : the number of channels
* @ format : the PCM format ( SNDRV_PCM_FORMAT_XXX )
* @ maxbps : the max . bps
*
* Calculate the format bitset from the given rate , channels and th PCM format .
*
* Return zero if invalid .
*/
unsigned int snd_hda_calc_stream_format ( unsigned int rate ,
unsigned int channels ,
unsigned int format ,
2010-08-03 14:28:57 +04:00
unsigned int maxbps ,
unsigned short spdif_ctls )
2005-04-17 02:20:36 +04:00
{
int i ;
unsigned int val = 0 ;
2005-08-22 15:57:55 +04:00
for ( i = 0 ; rate_bits [ i ] . hz ; i + + )
if ( rate_bits [ i ] . hz = = rate ) {
val = rate_bits [ i ] . hda_fmt ;
2005-04-17 02:20:36 +04:00
break ;
}
2007-04-16 13:29:14 +04:00
if ( ! rate_bits [ i ] . hz ) {
2005-04-17 02:20:36 +04:00
snd_printdd ( " invalid rate %d \n " , rate ) ;
return 0 ;
}
if ( channels = = 0 | | channels > 8 ) {
snd_printdd ( " invalid channels %d \n " , channels ) ;
return 0 ;
}
val | = channels - 1 ;
switch ( snd_pcm_format_width ( format ) ) {
2010-02-28 22:16:53 +03:00
case 8 :
2010-08-03 16:21:00 +04:00
val | = AC_FMT_BITS_8 ;
2010-02-28 22:16:53 +03:00
break ;
case 16 :
2010-08-03 16:21:00 +04:00
val | = AC_FMT_BITS_16 ;
2010-02-28 22:16:53 +03:00
break ;
2005-04-17 02:20:36 +04:00
case 20 :
case 24 :
case 32 :
2009-07-04 01:25:37 +04:00
if ( maxbps > = 32 | | format = = SNDRV_PCM_FORMAT_FLOAT_LE )
2010-08-03 16:21:00 +04:00
val | = AC_FMT_BITS_32 ;
2005-04-17 02:20:36 +04:00
else if ( maxbps > = 24 )
2010-08-03 16:21:00 +04:00
val | = AC_FMT_BITS_24 ;
2005-04-17 02:20:36 +04:00
else
2010-08-03 16:21:00 +04:00
val | = AC_FMT_BITS_20 ;
2005-04-17 02:20:36 +04:00
break ;
default :
2007-04-16 13:29:14 +04:00
snd_printdd ( " invalid format width %d \n " ,
snd_pcm_format_width ( format ) ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
2010-08-03 14:28:57 +04:00
if ( spdif_ctls & AC_DIG1_NONAUDIO )
2010-08-03 16:21:00 +04:00
val | = AC_FMT_TYPE_NON_PCM ;
2010-08-03 14:28:57 +04:00
2005-04-17 02:20:36 +04:00
return val ;
}
2008-11-28 17:17:06 +03:00
EXPORT_SYMBOL_HDA ( snd_hda_calc_stream_format ) ;
2005-04-17 02:20:36 +04:00
2009-03-24 09:32:14 +03:00
static unsigned int get_pcm_param ( struct hda_codec * codec , hda_nid_t nid )
{
unsigned int val = 0 ;
if ( nid ! = codec - > afg & &
( get_wcaps ( codec , nid ) & AC_WCAP_FORMAT_OVRD ) )
val = snd_hda_param_read ( codec , nid , AC_PAR_PCM ) ;
if ( ! val | | val = = - 1 )
val = snd_hda_param_read ( codec , codec - > afg , AC_PAR_PCM ) ;
if ( ! val | | val = = - 1 )
return 0 ;
return val ;
}
static unsigned int query_pcm_param ( struct hda_codec * codec , hda_nid_t nid )
{
return query_caps_hash ( codec , nid , HDA_HASH_PARPCM_KEY ( nid ) ,
get_pcm_param ) ;
}
static unsigned int get_stream_param ( struct hda_codec * codec , hda_nid_t nid )
{
unsigned int streams = snd_hda_param_read ( codec , nid , AC_PAR_STREAM ) ;
if ( ! streams | | streams = = - 1 )
streams = snd_hda_param_read ( codec , codec - > afg , AC_PAR_STREAM ) ;
if ( ! streams | | streams = = - 1 )
return 0 ;
return streams ;
}
static unsigned int query_stream_param ( struct hda_codec * codec , hda_nid_t nid )
{
return query_caps_hash ( codec , nid , HDA_HASH_PARSTR_KEY ( nid ) ,
get_stream_param ) ;
}
2005-04-17 02:20:36 +04:00
/**
* snd_hda_query_supported_pcm - query the supported PCM rates and formats
* @ codec : the HDA codec
* @ nid : NID to query
* @ ratesp : the pointer to store the detected rate bitflags
* @ formatsp : the pointer to store the detected formats
* @ bpsp : the pointer to store the detected format widths
*
* Queries the supported PCM rates and formats . The NULL @ ratesp , @ formatsp
* or @ bsps argument is ignored .
*
* Returns 0 if successful , otherwise a negative error code .
*/
2008-11-27 14:40:13 +03:00
static int snd_hda_query_supported_pcm ( struct hda_codec * codec , hda_nid_t nid ,
2005-04-17 02:20:36 +04:00
u32 * ratesp , u64 * formatsp , unsigned int * bpsp )
{
2009-03-17 16:30:31 +03:00
unsigned int i , val , wcaps ;
2005-04-17 02:20:36 +04:00
2009-03-17 16:30:31 +03:00
wcaps = get_wcaps ( codec , nid ) ;
2009-03-24 09:32:14 +03:00
val = query_pcm_param ( codec , nid ) ;
2005-04-17 02:20:36 +04:00
if ( ratesp ) {
u32 rates = 0 ;
2007-04-12 15:08:09 +04:00
for ( i = 0 ; i < AC_PAR_PCM_RATE_BITS ; i + + ) {
2005-04-17 02:20:36 +04:00
if ( val & ( 1 < < i ) )
2005-08-22 15:57:55 +04:00
rates | = rate_bits [ i ] . alsa_bits ;
2005-04-17 02:20:36 +04:00
}
2009-03-17 16:30:31 +03:00
if ( rates = = 0 ) {
snd_printk ( KERN_ERR " hda_codec: rates == 0 "
" (nid=0x%x, val=0x%x, ovrd=%i) \n " ,
nid , val ,
( wcaps & AC_WCAP_FORMAT_OVRD ) ? 1 : 0 ) ;
return - EIO ;
}
2005-04-17 02:20:36 +04:00
* ratesp = rates ;
}
if ( formatsp | | bpsp ) {
u64 formats = 0 ;
2009-03-17 16:30:31 +03:00
unsigned int streams , bps ;
2005-04-17 02:20:36 +04:00
2009-03-24 09:32:14 +03:00
streams = query_stream_param ( codec , nid ) ;
if ( ! streams )
2005-04-17 02:20:36 +04:00
return - EIO ;
bps = 0 ;
if ( streams & AC_SUPFMT_PCM ) {
if ( val & AC_SUPPCM_BITS_8 ) {
formats | = SNDRV_PCM_FMTBIT_U8 ;
bps = 8 ;
}
if ( val & AC_SUPPCM_BITS_16 ) {
formats | = SNDRV_PCM_FMTBIT_S16_LE ;
bps = 16 ;
}
if ( wcaps & AC_WCAP_DIGITAL ) {
if ( val & AC_SUPPCM_BITS_32 )
formats | = SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE ;
if ( val & ( AC_SUPPCM_BITS_20 | AC_SUPPCM_BITS_24 ) )
formats | = SNDRV_PCM_FMTBIT_S32_LE ;
if ( val & AC_SUPPCM_BITS_24 )
bps = 24 ;
else if ( val & AC_SUPPCM_BITS_20 )
bps = 20 ;
2007-04-16 13:29:14 +04:00
} else if ( val & ( AC_SUPPCM_BITS_20 | AC_SUPPCM_BITS_24 |
AC_SUPPCM_BITS_32 ) ) {
2005-04-17 02:20:36 +04:00
formats | = SNDRV_PCM_FMTBIT_S32_LE ;
if ( val & AC_SUPPCM_BITS_32 )
bps = 32 ;
else if ( val & AC_SUPPCM_BITS_24 )
bps = 24 ;
2006-09-19 16:23:14 +04:00
else if ( val & AC_SUPPCM_BITS_20 )
bps = 20 ;
2005-04-17 02:20:36 +04:00
}
}
2009-07-01 20:05:27 +04:00
if ( streams & AC_SUPFMT_FLOAT32 ) {
2005-04-17 02:20:36 +04:00
formats | = SNDRV_PCM_FMTBIT_FLOAT_LE ;
2009-07-04 01:25:37 +04:00
if ( ! bps )
bps = 32 ;
2009-07-01 20:05:27 +04:00
}
if ( streams = = AC_SUPFMT_AC3 ) {
2007-04-16 13:29:14 +04:00
/* should be exclusive */
2005-04-17 02:20:36 +04:00
/* temporary hack: we have still no proper support
* for the direct AC3 stream . . .
*/
formats | = SNDRV_PCM_FMTBIT_U8 ;
bps = 8 ;
}
2009-03-17 16:30:31 +03:00
if ( formats = = 0 ) {
snd_printk ( KERN_ERR " hda_codec: formats == 0 "
" (nid=0x%x, val=0x%x, ovrd=%i, "
" streams=0x%x) \n " ,
nid , val ,
( wcaps & AC_WCAP_FORMAT_OVRD ) ? 1 : 0 ,
streams ) ;
return - EIO ;
}
2005-04-17 02:20:36 +04:00
if ( formatsp )
* formatsp = formats ;
if ( bpsp )
* bpsp = bps ;
}
return 0 ;
}
/**
2009-11-16 16:58:17 +03:00
* snd_hda_is_supported_format - Check the validity of the format
* @ codec : HD - audio codec
* @ nid : NID to check
* @ format : the HD - audio format value to check
*
* Check whether the given node supports the format value .
2005-04-17 02:20:36 +04:00
*
* Returns 1 if supported , 0 if not .
*/
int snd_hda_is_supported_format ( struct hda_codec * codec , hda_nid_t nid ,
unsigned int format )
{
int i ;
unsigned int val = 0 , rate , stream ;
2009-03-24 09:32:14 +03:00
val = query_pcm_param ( codec , nid ) ;
if ( ! val )
return 0 ;
2005-04-17 02:20:36 +04:00
rate = format & 0xff00 ;
2007-04-12 15:08:09 +04:00
for ( i = 0 ; i < AC_PAR_PCM_RATE_BITS ; i + + )
2005-08-22 15:57:55 +04:00
if ( rate_bits [ i ] . hda_fmt = = rate ) {
2005-04-17 02:20:36 +04:00
if ( val & ( 1 < < i ) )
break ;
return 0 ;
}
2007-04-12 15:08:09 +04:00
if ( i > = AC_PAR_PCM_RATE_BITS )
2005-04-17 02:20:36 +04:00
return 0 ;
2009-03-24 09:32:14 +03:00
stream = query_stream_param ( codec , nid ) ;
if ( ! stream )
2005-04-17 02:20:36 +04:00
return 0 ;
if ( stream & AC_SUPFMT_PCM ) {
switch ( format & 0xf0 ) {
case 0x00 :
2007-04-16 13:29:14 +04:00
if ( ! ( val & AC_SUPPCM_BITS_8 ) )
2005-04-17 02:20:36 +04:00
return 0 ;
break ;
case 0x10 :
2007-04-16 13:29:14 +04:00
if ( ! ( val & AC_SUPPCM_BITS_16 ) )
2005-04-17 02:20:36 +04:00
return 0 ;
break ;
case 0x20 :
2007-04-16 13:29:14 +04:00
if ( ! ( val & AC_SUPPCM_BITS_20 ) )
2005-04-17 02:20:36 +04:00
return 0 ;
break ;
case 0x30 :
2007-04-16 13:29:14 +04:00
if ( ! ( val & AC_SUPPCM_BITS_24 ) )
2005-04-17 02:20:36 +04:00
return 0 ;
break ;
case 0x40 :
2007-04-16 13:29:14 +04:00
if ( ! ( val & AC_SUPPCM_BITS_32 ) )
2005-04-17 02:20:36 +04:00
return 0 ;
break ;
default :
return 0 ;
}
} else {
/* FIXME: check for float32 and AC3? */
}
return 1 ;
}
2008-11-28 17:17:06 +03:00
EXPORT_SYMBOL_HDA ( snd_hda_is_supported_format ) ;
2005-04-17 02:20:36 +04:00
/*
* PCM stuff
*/
static int hda_pcm_default_open_close ( struct hda_pcm_stream * hinfo ,
struct hda_codec * codec ,
2005-11-17 16:57:47 +03:00
struct snd_pcm_substream * substream )
2005-04-17 02:20:36 +04:00
{
return 0 ;
}
static int hda_pcm_default_prepare ( struct hda_pcm_stream * hinfo ,
struct hda_codec * codec ,
unsigned int stream_tag ,
unsigned int format ,
2005-11-17 16:57:47 +03:00
struct snd_pcm_substream * substream )
2005-04-17 02:20:36 +04:00
{
snd_hda_codec_setup_stream ( codec , hinfo - > nid , stream_tag , 0 , format ) ;
return 0 ;
}
static int hda_pcm_default_cleanup ( struct hda_pcm_stream * hinfo ,
struct hda_codec * codec ,
2005-11-17 16:57:47 +03:00
struct snd_pcm_substream * substream )
2005-04-17 02:20:36 +04:00
{
2008-03-18 11:57:50 +03:00
snd_hda_codec_cleanup_stream ( codec , hinfo - > nid ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
2008-07-30 17:01:45 +04:00
static int set_pcm_default_values ( struct hda_codec * codec ,
struct hda_pcm_stream * info )
2005-04-17 02:20:36 +04:00
{
2009-03-17 16:30:31 +03:00
int err ;
2007-04-16 13:29:14 +04:00
/* query support PCM information from the given NID */
if ( info - > nid & & ( ! info - > rates | | ! info - > formats ) ) {
2009-03-17 16:30:31 +03:00
err = snd_hda_query_supported_pcm ( codec , info - > nid ,
2007-04-16 13:29:14 +04:00
info - > rates ? NULL : & info - > rates ,
info - > formats ? NULL : & info - > formats ,
info - > maxbps ? NULL : & info - > maxbps ) ;
2009-03-17 16:30:31 +03:00
if ( err < 0 )
return err ;
2005-04-17 02:20:36 +04:00
}
if ( info - > ops . open = = NULL )
info - > ops . open = hda_pcm_default_open_close ;
if ( info - > ops . close = = NULL )
info - > ops . close = hda_pcm_default_open_close ;
if ( info - > ops . prepare = = NULL ) {
2008-08-08 19:12:14 +04:00
if ( snd_BUG_ON ( ! info - > nid ) )
return - EINVAL ;
2005-04-17 02:20:36 +04:00
info - > ops . prepare = hda_pcm_default_prepare ;
}
if ( info - > ops . cleanup = = NULL ) {
2008-08-08 19:12:14 +04:00
if ( snd_BUG_ON ( ! info - > nid ) )
return - EINVAL ;
2005-04-17 02:20:36 +04:00
info - > ops . cleanup = hda_pcm_default_cleanup ;
}
return 0 ;
}
2010-08-06 15:48:11 +04:00
/*
* codec prepare / cleanup entries
*/
int snd_hda_codec_prepare ( struct hda_codec * codec ,
struct hda_pcm_stream * hinfo ,
unsigned int stream ,
unsigned int format ,
struct snd_pcm_substream * substream )
{
int ret ;
2010-08-20 11:44:36 +04:00
mutex_lock ( & codec - > bus - > prepare_mutex ) ;
2010-08-06 15:48:11 +04:00
ret = hinfo - > ops . prepare ( hinfo , codec , stream , format , substream ) ;
if ( ret > = 0 )
purify_inactive_streams ( codec ) ;
2010-08-20 11:44:36 +04:00
mutex_unlock ( & codec - > bus - > prepare_mutex ) ;
2010-08-06 15:48:11 +04:00
return ret ;
}
EXPORT_SYMBOL_HDA ( snd_hda_codec_prepare ) ;
void snd_hda_codec_cleanup ( struct hda_codec * codec ,
struct hda_pcm_stream * hinfo ,
struct snd_pcm_substream * substream )
{
2010-08-20 11:44:36 +04:00
mutex_lock ( & codec - > bus - > prepare_mutex ) ;
2010-08-06 15:48:11 +04:00
hinfo - > ops . cleanup ( hinfo , codec , substream ) ;
2010-08-20 11:44:36 +04:00
mutex_unlock ( & codec - > bus - > prepare_mutex ) ;
2010-08-06 15:48:11 +04:00
}
EXPORT_SYMBOL_HDA ( snd_hda_codec_cleanup ) ;
2009-11-16 16:58:17 +03:00
/* global */
2009-11-10 16:53:02 +03:00
const char * snd_hda_pcm_type_name [ HDA_PCM_NTYPES ] = {
" Audio " , " SPDIF " , " HDMI " , " Modem "
} ;
2008-11-27 16:17:01 +03:00
/*
* get the empty PCM device number to assign
2010-01-28 19:08:53 +03:00
*
* note the max device number is limited by HDA_MAX_PCMS , currently 10
2008-11-27 16:17:01 +03:00
*/
static int get_empty_pcm_device ( struct hda_bus * bus , int type )
{
2009-10-30 13:38:26 +03:00
/* audio device indices; not linear to keep compatibility */
static int audio_idx [ HDA_PCM_NTYPES ] [ 5 ] = {
[ HDA_PCM_TYPE_AUDIO ] = { 0 , 2 , 4 , 5 , - 1 } ,
[ HDA_PCM_TYPE_SPDIF ] = { 1 , - 1 } ,
2009-10-30 13:40:03 +03:00
[ HDA_PCM_TYPE_HDMI ] = { 3 , 7 , 8 , 9 , - 1 } ,
2009-10-30 13:38:26 +03:00
[ HDA_PCM_TYPE_MODEM ] = { 6 , - 1 } ,
2008-11-27 16:17:01 +03:00
} ;
2009-10-30 13:38:26 +03:00
int i ;
if ( type > = HDA_PCM_NTYPES ) {
2008-11-27 16:17:01 +03:00
snd_printk ( KERN_WARNING " Invalid PCM type %d \n " , type ) ;
return - EINVAL ;
}
2009-10-30 13:38:26 +03:00
for ( i = 0 ; audio_idx [ type ] [ i ] > = 0 ; i + + )
if ( ! test_and_set_bit ( audio_idx [ type ] [ i ] , bus - > pcm_dev_bits ) )
return audio_idx [ type ] [ i ] ;
2010-02-28 22:16:53 +03:00
snd_printk ( KERN_WARNING " Too many %s devices \n " ,
snd_hda_pcm_type_name [ type ] ) ;
2009-10-30 13:38:26 +03:00
return - EAGAIN ;
2008-11-27 16:17:01 +03:00
}
2008-07-30 17:01:44 +04:00
/*
* attach a new PCM stream
*/
2008-11-27 16:17:01 +03:00
static int snd_hda_attach_pcm ( struct hda_codec * codec , struct hda_pcm * pcm )
2008-07-30 17:01:44 +04:00
{
2008-11-06 18:50:40 +03:00
struct hda_bus * bus = codec - > bus ;
2008-07-30 17:01:44 +04:00
struct hda_pcm_stream * info ;
int stream , err ;
2008-11-04 10:43:08 +03:00
if ( snd_BUG_ON ( ! pcm - > name ) )
2008-07-30 17:01:44 +04:00
return - EINVAL ;
for ( stream = 0 ; stream < 2 ; stream + + ) {
info = & pcm - > stream [ stream ] ;
if ( info - > substreams ) {
err = set_pcm_default_values ( codec , info ) ;
if ( err < 0 )
return err ;
}
}
2008-11-06 18:50:40 +03:00
return bus - > ops . attach_pcm ( bus , codec , pcm ) ;
2008-07-30 17:01:44 +04:00
}
2008-11-27 16:17:01 +03:00
/* assign all PCMs of the given codec */
int snd_hda_codec_build_pcms ( struct hda_codec * codec )
{
unsigned int pcm ;
int err ;
if ( ! codec - > num_pcms ) {
if ( ! codec - > patch_ops . build_pcms )
return 0 ;
err = codec - > patch_ops . build_pcms ( codec ) ;
2009-03-02 12:46:03 +03:00
if ( err < 0 ) {
printk ( KERN_ERR " hda_codec: cannot build PCMs "
2010-02-28 22:16:53 +03:00
" for #%d (error %d) \n " , codec - > addr , err ) ;
2009-03-02 12:46:03 +03:00
err = snd_hda_codec_reset ( codec ) ;
if ( err < 0 ) {
printk ( KERN_ERR
" hda_codec: cannot revert codec \n " ) ;
return err ;
}
}
2008-11-27 16:17:01 +03:00
}
for ( pcm = 0 ; pcm < codec - > num_pcms ; pcm + + ) {
struct hda_pcm * cpcm = & codec - > pcm_info [ pcm ] ;
int dev ;
if ( ! cpcm - > stream [ 0 ] . substreams & & ! cpcm - > stream [ 1 ] . substreams )
2009-01-20 20:21:23 +03:00
continue ; /* no substreams assigned */
2008-11-27 16:17:01 +03:00
if ( ! cpcm - > pcm ) {
dev = get_empty_pcm_device ( codec - > bus , cpcm - > pcm_type ) ;
if ( dev < 0 )
2009-03-02 12:46:03 +03:00
continue ; /* no fatal error */
2008-11-27 16:17:01 +03:00
cpcm - > device = dev ;
err = snd_hda_attach_pcm ( codec , cpcm ) ;
2009-03-02 12:46:03 +03:00
if ( err < 0 ) {
printk ( KERN_ERR " hda_codec: cannot attach "
" PCM stream %d for codec #%d \n " ,
dev , codec - > addr ) ;
continue ; /* no fatal error */
}
2008-11-27 16:17:01 +03:00
}
}
return 0 ;
}
2005-04-17 02:20:36 +04:00
/**
* snd_hda_build_pcms - build PCM information
* @ bus : the BUS
*
* Create PCM information for each codec included in the bus .
*
* The build_pcms codec patch is requested to set up codec - > num_pcms and
* codec - > pcm_info properly . The array is referred by the top - level driver
* to create its PCM instances .
* The allocated codec - > pcm_info should be released in codec - > patch_ops . free
* callback .
*
* At least , substreams , channels_min and channels_max must be filled for
* each stream . substreams = 0 indicates that the stream doesn ' t exist .
* When rates and / or formats are zero , the supported values are queried
* from the given nid . The nid is used also by the default ops . prepare
* and ops . cleanup callbacks .
*
* The driver needs to call ops . open in its open callback . Similarly ,
* ops . close is supposed to be called in the close callback .
* ops . prepare should be called in the prepare or hw_params callback
* with the proper parameters for set up .
* ops . cleanup should be called in hw_free for clean up of streams .
*
* This function returns 0 if successfull , or a negative error code .
*/
2008-11-27 16:17:01 +03:00
int __devinit snd_hda_build_pcms ( struct hda_bus * bus )
2005-04-17 02:20:36 +04:00
{
2007-04-16 13:29:14 +04:00
struct hda_codec * codec ;
2005-04-17 02:20:36 +04:00
2007-04-16 13:29:14 +04:00
list_for_each_entry ( codec , & bus - > codec_list , list ) {
2008-11-27 16:17:01 +03:00
int err = snd_hda_codec_build_pcms ( codec ) ;
if ( err < 0 )
return err ;
2005-04-17 02:20:36 +04:00
}
return 0 ;
}
2008-11-28 17:17:06 +03:00
EXPORT_SYMBOL_HDA ( snd_hda_build_pcms ) ;
2005-04-17 02:20:36 +04:00
/**
* snd_hda_check_board_config - compare the current codec with the config table
* @ codec : the HDA codec
2006-11-24 19:07:44 +03:00
* @ num_configs : number of config enums
* @ models : array of model name strings
2005-04-17 02:20:36 +04:00
* @ tbl : configuration table , terminated by null entries
*
* Compares the modelname or PCI subsystem id of the current codec with the
* given configuration table . If a matching entry is found , returns its
* config value ( supposed to be 0 or positive ) .
*
* If no entries are matching , the function returns a negative value .
*/
2007-08-02 17:51:59 +04:00
int snd_hda_check_board_config ( struct hda_codec * codec ,
2011-01-17 13:29:34 +03:00
int num_configs , const char * const * models ,
2007-08-02 17:51:59 +04:00
const struct snd_pci_quirk * tbl )
2005-04-17 02:20:36 +04:00
{
2008-07-30 17:01:45 +04:00
if ( codec - > modelname & & models ) {
2006-11-24 19:07:44 +03:00
int i ;
for ( i = 0 ; i < num_configs ; i + + ) {
if ( models [ i ] & &
2008-07-30 17:01:45 +04:00
! strcmp ( codec - > modelname , models [ i ] ) ) {
2006-11-24 19:07:44 +03:00
snd_printd ( KERN_INFO " hda_codec: model '%s' is "
" selected \n " , models [ i ] ) ;
return i ;
2005-04-17 02:20:36 +04:00
}
}
}
2006-11-24 19:07:44 +03:00
if ( ! codec - > bus - > pci | | ! tbl )
return - 1 ;
tbl = snd_pci_quirk_lookup ( codec - > bus - > pci , tbl ) ;
if ( ! tbl )
return - 1 ;
if ( tbl - > value > = 0 & & tbl - > value < num_configs ) {
2008-05-20 14:15:15 +04:00
# ifdef CONFIG_SND_DEBUG_VERBOSE
2006-11-24 19:07:44 +03:00
char tmp [ 10 ] ;
const char * model = NULL ;
if ( models )
model = models [ tbl - > value ] ;
if ( ! model ) {
sprintf ( tmp , " #%d " , tbl - > value ) ;
model = tmp ;
2005-04-17 02:20:36 +04:00
}
2006-11-24 19:07:44 +03:00
snd_printdd ( KERN_INFO " hda_codec: model '%s' is selected "
" for config %x:%x (%s) \n " ,
model , tbl - > subvendor , tbl - > subdevice ,
( tbl - > name ? tbl - > name : " Unknown device " ) ) ;
# endif
return tbl - > value ;
2005-04-17 02:20:36 +04:00
}
return - 1 ;
}
2008-11-28 17:17:06 +03:00
EXPORT_SYMBOL_HDA ( snd_hda_check_board_config ) ;
2005-04-17 02:20:36 +04:00
2008-08-11 12:18:39 +04:00
/**
* snd_hda_check_board_codec_sid_config - compare the current codec
2010-02-28 22:16:53 +03:00
subsystem ID with the
config table
2008-08-11 12:18:39 +04:00
This is important for Gateway notebooks with SB450 HDA Audio
where the vendor ID of the PCI device is :
ATI Technologies Inc SB450 HDA Audio [ 1002 : 437 b ]
and the vendor / subvendor are found only at the codec .
* @ codec : the HDA codec
* @ num_configs : number of config enums
* @ models : array of model name strings
* @ tbl : configuration table , terminated by null entries
*
* Compares the modelname or PCI subsystem id of the current codec with the
* given configuration table . If a matching entry is found , returns its
* config value ( supposed to be 0 or positive ) .
*
* If no entries are matching , the function returns a negative value .
*/
int snd_hda_check_board_codec_sid_config ( struct hda_codec * codec ,
2011-01-17 13:29:34 +03:00
int num_configs , const char * const * models ,
2008-08-11 12:18:39 +04:00
const struct snd_pci_quirk * tbl )
{
const struct snd_pci_quirk * q ;
/* Search for codec ID */
for ( q = tbl ; q - > subvendor ; q + + ) {
unsigned long vendorid = ( q - > subdevice ) | ( q - > subvendor < < 16 ) ;
if ( vendorid = = codec - > subsystem_id )
break ;
}
if ( ! q - > subvendor )
return - 1 ;
tbl = q ;
if ( tbl - > value > = 0 & & tbl - > value < num_configs ) {
2009-09-02 02:20:21 +04:00
# ifdef CONFIG_SND_DEBUG_VERBOSE
2008-08-11 12:18:39 +04:00
char tmp [ 10 ] ;
const char * model = NULL ;
if ( models )
model = models [ tbl - > value ] ;
if ( ! model ) {
sprintf ( tmp , " #%d " , tbl - > value ) ;
model = tmp ;
}
snd_printdd ( KERN_INFO " hda_codec: model '%s' is selected "
" for config %x:%x (%s) \n " ,
model , tbl - > subvendor , tbl - > subdevice ,
( tbl - > name ? tbl - > name : " Unknown device " ) ) ;
# endif
return tbl - > value ;
}
return - 1 ;
}
EXPORT_SYMBOL_HDA ( snd_hda_check_board_codec_sid_config ) ;
2005-04-17 02:20:36 +04:00
/**
* snd_hda_add_new_ctls - create controls from the array
* @ codec : the HDA codec
2005-11-17 16:57:47 +03:00
* @ knew : the array of struct snd_kcontrol_new
2005-04-17 02:20:36 +04:00
*
* This helper function creates and add new controls in the given array .
* The array must be terminated with an empty entry as terminator .
*
* Returns 0 if successful , or a negative error code .
*/
2007-08-02 17:51:59 +04:00
int snd_hda_add_new_ctls ( struct hda_codec * codec , struct snd_kcontrol_new * knew )
2005-04-17 02:20:36 +04:00
{
2009-11-12 12:15:48 +03:00
int err ;
2005-04-17 02:20:36 +04:00
for ( ; knew - > name ; knew + + ) {
2005-11-21 18:33:22 +03:00
struct snd_kcontrol * kctl ;
2010-12-23 12:17:52 +03:00
int addr = 0 , idx = 0 ;
2009-12-08 18:13:32 +03:00
if ( knew - > iface = = - 1 ) /* skip this codec private value */
continue ;
2010-12-23 12:17:52 +03:00
for ( ; ; ) {
2005-11-21 18:33:22 +03:00
kctl = snd_ctl_new1 ( knew , codec ) ;
2007-04-16 13:29:14 +04:00
if ( ! kctl )
2005-11-21 18:33:22 +03:00
return - ENOMEM ;
2010-12-23 12:17:52 +03:00
if ( addr > 0 )
kctl - > id . device = addr ;
if ( idx > 0 )
kctl - > id . index = idx ;
2009-11-11 15:43:01 +03:00
err = snd_hda_ctl_add ( codec , 0 , kctl ) ;
2010-12-23 12:17:52 +03:00
if ( ! err )
break ;
/* try first with another device index corresponding to
* the codec addr ; if it still fails ( or it ' s the
* primary codec ) , then try another control index
*/
if ( ! addr & & codec - > addr )
addr = codec - > addr ;
else if ( ! idx & & ! knew - > index ) {
idx = find_empty_mixer_ctl_idx ( codec ,
knew - > name ) ;
if ( idx < = 0 )
return err ;
} else
2005-11-21 18:33:22 +03:00
return err ;
}
2005-04-17 02:20:36 +04:00
}
return 0 ;
}
2008-11-28 17:17:06 +03:00
EXPORT_SYMBOL_HDA ( snd_hda_add_new_ctls ) ;
2005-04-17 02:20:36 +04:00
2007-08-10 19:21:45 +04:00
# ifdef CONFIG_SND_HDA_POWER_SAVE
static void hda_set_power_state ( struct hda_codec * codec , hda_nid_t fg ,
unsigned int power_state ) ;
static void hda_power_work ( struct work_struct * work )
{
struct hda_codec * codec =
container_of ( work , struct hda_codec , power_work . work ) ;
2008-11-06 18:50:40 +03:00
struct hda_bus * bus = codec - > bus ;
2007-08-10 19:21:45 +04:00
2007-09-03 17:26:57 +04:00
if ( ! codec - > power_on | | codec - > power_count ) {
codec - > power_transition = 0 ;
2007-08-10 19:21:45 +04:00
return ;
2007-09-03 17:26:57 +04:00
}
2007-08-10 19:21:45 +04:00
hda_call_codec_suspend ( codec ) ;
2008-11-06 18:50:40 +03:00
if ( bus - > ops . pm_notify )
bus - > ops . pm_notify ( bus ) ;
2007-08-10 19:21:45 +04:00
}
static void hda_keep_power_on ( struct hda_codec * codec )
{
codec - > power_count + + ;
codec - > power_on = 1 ;
2009-11-11 11:34:25 +03:00
codec - > power_jiffies = jiffies ;
}
2009-11-16 16:58:17 +03:00
/* update the power on/off account with the current jiffies */
2009-11-11 11:34:25 +03:00
void snd_hda_update_power_acct ( struct hda_codec * codec )
{
unsigned long delta = jiffies - codec - > power_jiffies ;
if ( codec - > power_on )
codec - > power_on_acct + = delta ;
else
codec - > power_off_acct + = delta ;
codec - > power_jiffies + = delta ;
2007-08-10 19:21:45 +04:00
}
2009-11-16 16:58:17 +03:00
/**
* snd_hda_power_up - Power - up the codec
* @ codec : HD - audio codec
*
* Increment the power - up counter and power up the hardware really when
* not turned on yet .
2010-02-28 22:16:53 +03:00
*/
2007-08-10 19:21:45 +04:00
void snd_hda_power_up ( struct hda_codec * codec )
{
2008-11-06 18:50:40 +03:00
struct hda_bus * bus = codec - > bus ;
2007-08-10 19:21:45 +04:00
codec - > power_count + + ;
2007-08-16 18:35:33 +04:00
if ( codec - > power_on | | codec - > power_transition )
2007-08-10 19:21:45 +04:00
return ;
2009-11-11 11:34:25 +03:00
snd_hda_update_power_acct ( codec ) ;
2007-08-10 19:21:45 +04:00
codec - > power_on = 1 ;
2009-11-11 11:34:25 +03:00
codec - > power_jiffies = jiffies ;
2008-11-06 18:50:40 +03:00
if ( bus - > ops . pm_notify )
bus - > ops . pm_notify ( bus ) ;
2007-08-10 19:21:45 +04:00
hda_call_codec_resume ( codec ) ;
cancel_delayed_work ( & codec - > power_work ) ;
2007-08-16 18:35:33 +04:00
codec - > power_transition = 0 ;
2007-08-10 19:21:45 +04:00
}
2008-11-28 17:17:06 +03:00
EXPORT_SYMBOL_HDA ( snd_hda_power_up ) ;
2008-11-27 17:47:11 +03:00
# define power_save(codec) \
( ( codec ) - > bus - > power_save ? * ( codec ) - > bus - > power_save : 0 )
2007-08-10 19:21:45 +04:00
2009-11-16 16:58:17 +03:00
/**
* snd_hda_power_down - Power - down the codec
* @ codec : HD - audio codec
*
* Decrement the power - up counter and schedules the power - off work if
* the counter rearches to zero .
2010-02-28 22:16:53 +03:00
*/
2007-08-10 19:21:45 +04:00
void snd_hda_power_down ( struct hda_codec * codec )
{
- - codec - > power_count ;
2007-08-16 18:35:33 +04:00
if ( ! codec - > power_on | | codec - > power_count | | codec - > power_transition )
2007-08-10 19:21:45 +04:00
return ;
2008-11-27 14:43:28 +03:00
if ( power_save ( codec ) ) {
2007-08-16 18:35:33 +04:00
codec - > power_transition = 1 ; /* avoid reentrance */
2009-01-13 19:46:37 +03:00
queue_delayed_work ( codec - > bus - > workq , & codec - > power_work ,
2008-11-27 14:43:28 +03:00
msecs_to_jiffies ( power_save ( codec ) * 1000 ) ) ;
2007-08-16 18:35:33 +04:00
}
2007-08-10 19:21:45 +04:00
}
2008-11-28 17:17:06 +03:00
EXPORT_SYMBOL_HDA ( snd_hda_power_down ) ;
2007-08-10 19:21:45 +04:00
2009-11-16 16:58:17 +03:00
/**
* snd_hda_check_amp_list_power - Check the amp list and update the power
* @ codec : HD - audio codec
* @ check : the object containing an AMP list and the status
* @ nid : NID to check / update
*
* Check whether the given NID is in the amp list . If it ' s in the list ,
* check the current AMP status , and update the the power - status according
* to the mute status .
*
* This function is supposed to be set or called from the check_power_status
* patch ops .
2010-02-28 22:16:53 +03:00
*/
2007-08-10 19:21:45 +04:00
int snd_hda_check_amp_list_power ( struct hda_codec * codec ,
struct hda_loopback_check * check ,
hda_nid_t nid )
{
struct hda_amp_list * p ;
int ch , v ;
if ( ! check - > amplist )
return 0 ;
for ( p = check - > amplist ; p - > nid ; p + + ) {
if ( p - > nid = = nid )
break ;
}
if ( ! p - > nid )
return 0 ; /* nothing changed */
for ( p = check - > amplist ; p - > nid ; p + + ) {
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 ) {
if ( ! check - > power_on ) {
check - > power_on = 1 ;
snd_hda_power_up ( codec ) ;
}
return 1 ;
}
}
}
if ( check - > power_on ) {
check - > power_on = 0 ;
snd_hda_power_down ( codec ) ;
}
return 0 ;
}
2008-11-28 17:17:06 +03:00
EXPORT_SYMBOL_HDA ( snd_hda_check_amp_list_power ) ;
2007-08-10 19:21:45 +04:00
# endif
2005-04-17 02:20:36 +04:00
2005-11-17 16:57:47 +03:00
/*
2005-11-17 13:06:29 +03:00
* Channel mode helper
*/
2009-11-16 16:58:17 +03:00
/**
* snd_hda_ch_mode_info - Info callback helper for the channel mode enum
*/
2007-04-16 13:29:14 +04:00
int snd_hda_ch_mode_info ( struct hda_codec * codec ,
struct snd_ctl_elem_info * uinfo ,
const struct hda_channel_mode * chmode ,
int num_chmodes )
2005-11-17 13:06:29 +03:00
{
uinfo - > type = SNDRV_CTL_ELEM_TYPE_ENUMERATED ;
uinfo - > count = 1 ;
uinfo - > value . enumerated . items = num_chmodes ;
if ( uinfo - > value . enumerated . item > = num_chmodes )
uinfo - > value . enumerated . item = num_chmodes - 1 ;
sprintf ( uinfo - > value . enumerated . name , " %dch " ,
chmode [ uinfo - > value . enumerated . item ] . channels ) ;
return 0 ;
}
2008-11-28 17:17:06 +03:00
EXPORT_SYMBOL_HDA ( snd_hda_ch_mode_info ) ;
2005-11-17 13:06:29 +03:00
2009-11-16 16:58:17 +03:00
/**
* snd_hda_ch_mode_get - Get callback helper for the channel mode enum
*/
2007-04-16 13:29:14 +04:00
int snd_hda_ch_mode_get ( struct hda_codec * codec ,
struct snd_ctl_elem_value * ucontrol ,
const struct hda_channel_mode * chmode ,
int num_chmodes ,
2005-11-17 13:06:29 +03:00
int max_channels )
{
int i ;
for ( i = 0 ; i < num_chmodes ; i + + ) {
if ( max_channels = = chmode [ i ] . channels ) {
ucontrol - > value . enumerated . item [ 0 ] = i ;
break ;
}
}
return 0 ;
}
2008-11-28 17:17:06 +03:00
EXPORT_SYMBOL_HDA ( snd_hda_ch_mode_get ) ;
2005-11-17 13:06:29 +03:00
2009-11-16 16:58:17 +03:00
/**
* snd_hda_ch_mode_put - Put callback helper for the channel mode enum
*/
2007-04-16 13:29:14 +04:00
int snd_hda_ch_mode_put ( struct hda_codec * codec ,
struct snd_ctl_elem_value * ucontrol ,
const struct hda_channel_mode * chmode ,
int num_chmodes ,
2005-11-17 13:06:29 +03:00
int * max_channelsp )
{
unsigned int mode ;
mode = ucontrol - > value . enumerated . item [ 0 ] ;
2007-11-15 17:54:38 +03:00
if ( mode > = num_chmodes )
return - EINVAL ;
2007-08-10 19:09:26 +04:00
if ( * max_channelsp = = chmode [ mode ] . channels )
2005-11-17 13:06:29 +03:00
return 0 ;
/* change the current channel setting */
* max_channelsp = chmode [ mode ] . channels ;
if ( chmode [ mode ] . sequence )
2007-08-10 19:09:26 +04:00
snd_hda_sequence_write_cache ( codec , chmode [ mode ] . sequence ) ;
2005-11-17 13:06:29 +03:00
return 1 ;
}
2008-11-28 17:17:06 +03:00
EXPORT_SYMBOL_HDA ( snd_hda_ch_mode_put ) ;
2005-11-17 13:06:29 +03:00
2005-04-17 02:20:36 +04:00
/*
* input MUX helper
*/
2009-11-16 16:58:17 +03:00
/**
* snd_hda_input_mux_info_info - Info callback helper for the input - mux enum
*/
2007-04-16 13:29:14 +04:00
int snd_hda_input_mux_info ( const struct hda_input_mux * imux ,
struct snd_ctl_elem_info * uinfo )
2005-04-17 02:20:36 +04:00
{
unsigned int index ;
uinfo - > type = SNDRV_CTL_ELEM_TYPE_ENUMERATED ;
uinfo - > count = 1 ;
uinfo - > value . enumerated . items = imux - > num_items ;
2007-10-09 13:58:41 +04:00
if ( ! imux - > num_items )
return 0 ;
2005-04-17 02:20:36 +04:00
index = uinfo - > value . enumerated . item ;
if ( index > = imux - > num_items )
index = imux - > num_items - 1 ;
strcpy ( uinfo - > value . enumerated . name , imux - > items [ index ] . label ) ;
return 0 ;
}
2008-11-28 17:17:06 +03:00
EXPORT_SYMBOL_HDA ( snd_hda_input_mux_info ) ;
2005-04-17 02:20:36 +04:00
2009-11-16 16:58:17 +03:00
/**
* snd_hda_input_mux_info_put - Put callback helper for the input - mux enum
*/
2007-04-16 13:29:14 +04:00
int snd_hda_input_mux_put ( struct hda_codec * codec ,
const struct hda_input_mux * imux ,
struct snd_ctl_elem_value * ucontrol ,
hda_nid_t nid ,
2005-04-17 02:20:36 +04:00
unsigned int * cur_val )
{
unsigned int idx ;
2007-10-09 13:58:41 +04:00
if ( ! imux - > num_items )
return 0 ;
2005-04-17 02:20:36 +04:00
idx = ucontrol - > value . enumerated . item [ 0 ] ;
if ( idx > = imux - > num_items )
idx = imux - > num_items - 1 ;
2007-08-10 19:09:26 +04:00
if ( * cur_val = = idx )
2005-04-17 02:20:36 +04:00
return 0 ;
2007-08-10 19:09:26 +04:00
snd_hda_codec_write_cache ( codec , nid , 0 , AC_VERB_SET_CONNECT_SEL ,
imux - > items [ idx ] . index ) ;
2005-04-17 02:20:36 +04:00
* cur_val = idx ;
return 1 ;
}
2008-11-28 17:17:06 +03:00
EXPORT_SYMBOL_HDA ( snd_hda_input_mux_put ) ;
2005-04-17 02:20:36 +04:00
/*
* Multi - channel / digital - out PCM helper functions
*/
2007-04-05 16:51:48 +04:00
/* setup SPDIF output stream */
static void setup_dig_out_stream ( struct hda_codec * codec , hda_nid_t nid ,
unsigned int stream_tag , unsigned int format )
{
/* turn off SPDIF once; otherwise the IEC958 bits won't be updated */
2008-09-25 18:32:41 +04:00
if ( codec - > spdif_status_reset & & ( codec - > spdif_ctls & AC_DIG1_ENABLE ) )
2010-02-28 22:16:53 +03:00
set_dig_out_convert ( codec , nid ,
2008-09-25 18:32:41 +04:00
codec - > spdif_ctls & ~ AC_DIG1_ENABLE & 0xff ,
- 1 ) ;
2007-04-05 16:51:48 +04:00
snd_hda_codec_setup_stream ( codec , nid , stream_tag , 0 , format ) ;
2008-09-25 18:32:41 +04:00
if ( codec - > slave_dig_outs ) {
hda_nid_t * d ;
for ( d = codec - > slave_dig_outs ; * d ; d + + )
snd_hda_codec_setup_stream ( codec , * d , stream_tag , 0 ,
format ) ;
}
2007-04-05 16:51:48 +04:00
/* turn on again (if needed) */
2008-09-25 18:32:41 +04:00
if ( codec - > spdif_status_reset & & ( codec - > spdif_ctls & AC_DIG1_ENABLE ) )
set_dig_out_convert ( codec , nid ,
codec - > spdif_ctls & 0xff , - 1 ) ;
}
2008-09-07 22:31:40 +04:00
2008-09-25 18:32:41 +04:00
static void cleanup_dig_out_stream ( struct hda_codec * codec , hda_nid_t nid )
{
snd_hda_codec_cleanup_stream ( codec , nid ) ;
if ( codec - > slave_dig_outs ) {
hda_nid_t * d ;
for ( d = codec - > slave_dig_outs ; * d ; d + + )
snd_hda_codec_cleanup_stream ( codec , * d ) ;
2008-09-07 22:31:40 +04:00
}
2007-04-05 16:51:48 +04:00
}
2009-11-16 16:58:17 +03:00
/**
* snd_hda_bus_reboot_notify - call the reboot notifier of each codec
* @ bus : HD - audio bus
*/
2009-11-10 18:02:29 +03:00
void snd_hda_bus_reboot_notify ( struct hda_bus * bus )
{
struct hda_codec * codec ;
if ( ! bus )
return ;
list_for_each_entry ( codec , & bus - > codec_list , list ) {
# ifdef CONFIG_SND_HDA_POWER_SAVE
if ( ! codec - > power_on )
continue ;
# endif
if ( codec - > patch_ops . reboot_notify )
codec - > patch_ops . reboot_notify ( codec ) ;
}
}
2009-11-10 20:26:12 +03:00
EXPORT_SYMBOL_HDA ( snd_hda_bus_reboot_notify ) ;
2009-11-10 18:02:29 +03:00
2009-11-16 16:58:17 +03:00
/**
* snd_hda_multi_out_dig_open - open the digital out in the exclusive mode
2005-04-17 02:20:36 +04:00
*/
2007-04-16 13:29:14 +04:00
int snd_hda_multi_out_dig_open ( struct hda_codec * codec ,
struct hda_multi_out * mout )
2005-04-17 02:20:36 +04:00
{
2006-01-16 18:34:20 +03:00
mutex_lock ( & codec - > spdif_mutex ) ;
2007-04-16 13:23:56 +04:00
if ( mout - > dig_out_used = = HDA_DIG_ANALOG_DUP )
/* already opened as analog dup; reset it once */
2008-09-25 18:32:41 +04:00
cleanup_dig_out_stream ( codec , mout - > dig_out_nid ) ;
2005-04-17 02:20:36 +04:00
mout - > dig_out_used = HDA_DIG_EXCLUSIVE ;
2006-01-16 18:34:20 +03:00
mutex_unlock ( & codec - > spdif_mutex ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
2008-11-28 17:17:06 +03:00
EXPORT_SYMBOL_HDA ( snd_hda_multi_out_dig_open ) ;
2005-04-17 02:20:36 +04:00
2009-11-16 16:58:17 +03:00
/**
* snd_hda_multi_out_dig_prepare - prepare the digital out stream
*/
2007-04-05 16:51:48 +04:00
int snd_hda_multi_out_dig_prepare ( struct hda_codec * codec ,
struct hda_multi_out * mout ,
unsigned int stream_tag ,
unsigned int format ,
struct snd_pcm_substream * substream )
{
mutex_lock ( & codec - > spdif_mutex ) ;
setup_dig_out_stream ( codec , mout - > dig_out_nid , stream_tag , format ) ;
mutex_unlock ( & codec - > spdif_mutex ) ;
return 0 ;
}
2008-11-28 17:17:06 +03:00
EXPORT_SYMBOL_HDA ( snd_hda_multi_out_dig_prepare ) ;
2007-04-05 16:51:48 +04:00
2009-11-16 16:58:17 +03:00
/**
* snd_hda_multi_out_dig_cleanup - clean - up the digital out stream
*/
2009-02-13 13:32:28 +03:00
int snd_hda_multi_out_dig_cleanup ( struct hda_codec * codec ,
struct hda_multi_out * mout )
{
mutex_lock ( & codec - > spdif_mutex ) ;
cleanup_dig_out_stream ( codec , mout - > dig_out_nid ) ;
mutex_unlock ( & codec - > spdif_mutex ) ;
return 0 ;
}
EXPORT_SYMBOL_HDA ( snd_hda_multi_out_dig_cleanup ) ;
2009-11-16 16:58:17 +03:00
/**
* snd_hda_multi_out_dig_close - release the digital out stream
2005-04-17 02:20:36 +04:00
*/
2007-04-16 13:29:14 +04:00
int snd_hda_multi_out_dig_close ( struct hda_codec * codec ,
struct hda_multi_out * mout )
2005-04-17 02:20:36 +04:00
{
2006-01-16 18:34:20 +03:00
mutex_lock ( & codec - > spdif_mutex ) ;
2005-04-17 02:20:36 +04:00
mout - > dig_out_used = 0 ;
2006-01-16 18:34:20 +03:00
mutex_unlock ( & codec - > spdif_mutex ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
2008-11-28 17:17:06 +03:00
EXPORT_SYMBOL_HDA ( snd_hda_multi_out_dig_close ) ;
2005-04-17 02:20:36 +04:00
2009-11-16 16:58:17 +03:00
/**
* snd_hda_multi_out_analog_open - open analog outputs
*
* Open analog outputs and set up the hw - constraints .
* If the digital outputs can be opened as slave , open the digital
* outputs , too .
2005-04-17 02:20:36 +04:00
*/
2007-04-16 13:29:14 +04:00
int snd_hda_multi_out_analog_open ( struct hda_codec * codec ,
struct hda_multi_out * mout ,
2008-02-12 20:37:26 +03:00
struct snd_pcm_substream * substream ,
struct hda_pcm_stream * hinfo )
{
struct snd_pcm_runtime * runtime = substream - > runtime ;
runtime - > hw . channels_max = mout - > max_channels ;
if ( mout - > dig_out_nid ) {
if ( ! mout - > analog_rates ) {
mout - > analog_rates = hinfo - > rates ;
mout - > analog_formats = hinfo - > formats ;
mout - > analog_maxbps = hinfo - > maxbps ;
} else {
runtime - > hw . rates = mout - > analog_rates ;
runtime - > hw . formats = mout - > analog_formats ;
hinfo - > maxbps = mout - > analog_maxbps ;
}
if ( ! mout - > spdif_rates ) {
snd_hda_query_supported_pcm ( codec , mout - > dig_out_nid ,
& mout - > spdif_rates ,
& mout - > spdif_formats ,
& mout - > spdif_maxbps ) ;
}
mutex_lock ( & codec - > spdif_mutex ) ;
if ( mout - > share_spdif ) {
2009-07-04 01:03:30 +04:00
if ( ( runtime - > hw . rates & mout - > spdif_rates ) & &
( runtime - > hw . formats & mout - > spdif_formats ) ) {
runtime - > hw . rates & = mout - > spdif_rates ;
runtime - > hw . formats & = mout - > spdif_formats ;
if ( mout - > spdif_maxbps < hinfo - > maxbps )
hinfo - > maxbps = mout - > spdif_maxbps ;
} else {
mout - > share_spdif = 0 ;
/* FIXME: need notify? */
}
2008-02-12 20:37:26 +03:00
}
2008-04-14 15:11:44 +04:00
mutex_unlock ( & codec - > spdif_mutex ) ;
2008-02-12 20:37:26 +03:00
}
2005-04-17 02:20:36 +04:00
return snd_pcm_hw_constraint_step ( substream - > runtime , 0 ,
SNDRV_PCM_HW_PARAM_CHANNELS , 2 ) ;
}
2008-11-28 17:17:06 +03:00
EXPORT_SYMBOL_HDA ( snd_hda_multi_out_analog_open ) ;
2005-04-17 02:20:36 +04:00
2009-11-16 16:58:17 +03:00
/**
* snd_hda_multi_out_analog_prepare - Preapre the analog outputs .
*
* Set up the i / o for analog out .
* When the digital out is available , copy the front out to digital out , too .
2005-04-17 02:20:36 +04:00
*/
2007-04-16 13:29:14 +04:00
int snd_hda_multi_out_analog_prepare ( struct hda_codec * codec ,
struct hda_multi_out * mout ,
2005-04-17 02:20:36 +04:00
unsigned int stream_tag ,
unsigned int format ,
2005-11-17 16:57:47 +03:00
struct snd_pcm_substream * substream )
2005-04-17 02:20:36 +04:00
{
hda_nid_t * nids = mout - > dac_nids ;
int chs = substream - > runtime - > channels ;
int i ;
2006-01-16 18:34:20 +03:00
mutex_lock ( & codec - > spdif_mutex ) ;
2008-02-12 20:37:26 +03:00
if ( mout - > dig_out_nid & & mout - > share_spdif & &
mout - > dig_out_used ! = HDA_DIG_EXCLUSIVE ) {
2005-04-17 02:20:36 +04:00
if ( chs = = 2 & &
2007-04-16 13:29:14 +04:00
snd_hda_is_supported_format ( codec , mout - > dig_out_nid ,
format ) & &
! ( codec - > spdif_status & IEC958_AES0_NONAUDIO ) ) {
2005-04-17 02:20:36 +04:00
mout - > dig_out_used = HDA_DIG_ANALOG_DUP ;
2007-04-05 16:51:48 +04:00
setup_dig_out_stream ( codec , mout - > dig_out_nid ,
stream_tag , format ) ;
2005-04-17 02:20:36 +04:00
} else {
mout - > dig_out_used = 0 ;
2008-09-25 18:32:41 +04:00
cleanup_dig_out_stream ( codec , mout - > dig_out_nid ) ;
2005-04-17 02:20:36 +04:00
}
}
2006-01-16 18:34:20 +03:00
mutex_unlock ( & codec - > spdif_mutex ) ;
2005-04-17 02:20:36 +04:00
/* front */
2007-04-16 13:29:14 +04:00
snd_hda_codec_setup_stream ( codec , nids [ HDA_FRONT ] , stream_tag ,
0 , format ) ;
2007-10-26 14:35:56 +04:00
if ( ! mout - > no_share_stream & &
mout - > hp_nid & & mout - > hp_nid ! = nids [ HDA_FRONT ] )
2005-04-17 02:20:36 +04:00
/* headphone out will just decode front left/right (stereo) */
2007-04-16 13:29:14 +04:00
snd_hda_codec_setup_stream ( codec , mout - > hp_nid , stream_tag ,
0 , format ) ;
2006-03-21 13:24:42 +03:00
/* extra outputs copied from front */
for ( i = 0 ; i < ARRAY_SIZE ( mout - > extra_out_nid ) ; i + + )
2007-10-26 14:35:56 +04:00
if ( ! mout - > no_share_stream & & mout - > extra_out_nid [ i ] )
2006-03-21 13:24:42 +03:00
snd_hda_codec_setup_stream ( codec ,
mout - > extra_out_nid [ i ] ,
stream_tag , 0 , format ) ;
2005-04-17 02:20:36 +04:00
/* surrounds */
for ( i = 1 ; i < mout - > num_dacs ; i + + ) {
2005-06-10 21:48:10 +04:00
if ( chs > = ( i + 1 ) * 2 ) /* independent out */
2007-04-16 13:29:14 +04:00
snd_hda_codec_setup_stream ( codec , nids [ i ] , stream_tag ,
i * 2 , format ) ;
2007-10-26 14:35:56 +04:00
else if ( ! mout - > no_share_stream ) /* copy front */
2007-04-16 13:29:14 +04:00
snd_hda_codec_setup_stream ( codec , nids [ i ] , stream_tag ,
0 , format ) ;
2005-04-17 02:20:36 +04:00
}
return 0 ;
}
2008-11-28 17:17:06 +03:00
EXPORT_SYMBOL_HDA ( snd_hda_multi_out_analog_prepare ) ;
2005-04-17 02:20:36 +04:00
2009-11-16 16:58:17 +03:00
/**
* snd_hda_multi_out_analog_cleanup - clean up the setting for analog out
2005-04-17 02:20:36 +04:00
*/
2007-04-16 13:29:14 +04:00
int snd_hda_multi_out_analog_cleanup ( struct hda_codec * codec ,
struct hda_multi_out * mout )
2005-04-17 02:20:36 +04:00
{
hda_nid_t * nids = mout - > dac_nids ;
int i ;
for ( i = 0 ; i < mout - > num_dacs ; i + + )
2008-03-18 11:57:50 +03:00
snd_hda_codec_cleanup_stream ( codec , nids [ i ] ) ;
2005-04-17 02:20:36 +04:00
if ( mout - > hp_nid )
2008-03-18 11:57:50 +03:00
snd_hda_codec_cleanup_stream ( codec , mout - > hp_nid ) ;
2006-03-21 13:24:42 +03:00
for ( i = 0 ; i < ARRAY_SIZE ( mout - > extra_out_nid ) ; i + + )
if ( mout - > extra_out_nid [ i ] )
2008-03-18 11:57:50 +03:00
snd_hda_codec_cleanup_stream ( codec ,
mout - > extra_out_nid [ i ] ) ;
2006-01-16 18:34:20 +03:00
mutex_lock ( & codec - > spdif_mutex ) ;
2005-04-17 02:20:36 +04:00
if ( mout - > dig_out_nid & & mout - > dig_out_used = = HDA_DIG_ANALOG_DUP ) {
2008-09-25 18:32:41 +04:00
cleanup_dig_out_stream ( codec , mout - > dig_out_nid ) ;
2005-04-17 02:20:36 +04:00
mout - > dig_out_used = 0 ;
}
2006-01-16 18:34:20 +03:00
mutex_unlock ( & codec - > spdif_mutex ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
2008-11-28 17:17:06 +03:00
EXPORT_SYMBOL_HDA ( snd_hda_multi_out_analog_cleanup ) ;
2005-04-17 02:20:36 +04:00
2005-06-13 16:16:38 +04:00
/*
2008-10-07 10:21:41 +04:00
* Helper for automatic pin configuration
2005-06-13 16:16:38 +04:00
*/
2005-12-05 21:42:22 +03:00
2007-08-02 17:51:59 +04:00
static int is_in_nid_list ( hda_nid_t nid , hda_nid_t * list )
2005-12-05 21:42:22 +03:00
{
for ( ; * list ; list + + )
if ( * list = = nid )
return 1 ;
return 0 ;
}
2007-05-08 17:33:03 +04:00
/*
* Sort an associated group of pins according to their sequence numbers .
*/
2010-02-28 22:16:53 +03:00
static void sort_pins_by_sequence ( hda_nid_t * pins , short * sequences ,
2007-05-08 17:33:03 +04:00
int num_pins )
{
int i , j ;
short seq ;
hda_nid_t nid ;
2010-02-28 22:16:53 +03:00
2007-05-08 17:33:03 +04:00
for ( i = 0 ; i < num_pins ; i + + ) {
for ( j = i + 1 ; j < num_pins ; j + + ) {
if ( sequences [ i ] > sequences [ j ] ) {
seq = sequences [ i ] ;
sequences [ i ] = sequences [ j ] ;
sequences [ j ] = seq ;
nid = pins [ i ] ;
pins [ i ] = pins [ j ] ;
pins [ j ] = nid ;
}
}
}
}
2010-08-30 14:56:55 +04:00
/* add the found input-pin to the cfg->inputs[] table */
static void add_auto_cfg_input_pin ( struct auto_pin_cfg * cfg , hda_nid_t nid ,
int type )
{
if ( cfg - > num_inputs < AUTO_CFG_MAX_INS ) {
cfg - > inputs [ cfg - > num_inputs ] . pin = nid ;
cfg - > inputs [ cfg - > num_inputs ] . type = type ;
cfg - > num_inputs + + ;
}
}
2010-09-10 00:22:02 +04:00
/* sort inputs in the order of AUTO_PIN_* type */
static void sort_autocfg_input_pins ( struct auto_pin_cfg * cfg )
{
int i , j ;
for ( i = 0 ; i < cfg - > num_inputs ; i + + ) {
for ( j = i + 1 ; j < cfg - > num_inputs ; j + + ) {
if ( cfg - > inputs [ i ] . type > cfg - > inputs [ j ] . type ) {
struct auto_pin_cfg_item tmp ;
tmp = cfg - > inputs [ i ] ;
cfg - > inputs [ i ] = cfg - > inputs [ j ] ;
cfg - > inputs [ j ] = tmp ;
}
}
}
}
2006-03-21 13:24:42 +03:00
/*
* Parse all pin widgets and store the useful pin nids to cfg
*
* The number of line - outs or any primary output is stored in line_outs ,
* and the corresponding output pins are assigned to line_out_pins [ ] ,
* in the order of front , rear , CLFE , side , . . .
*
* If more extra outputs ( speaker and headphone ) are found , the pins are
2006-09-20 19:10:27 +04:00
* assisnged to hp_pins [ ] and speaker_pins [ ] , respectively . If no line - out jack
2006-03-21 13:24:42 +03:00
* is detected , one of speaker of HP pins is assigned as the primary
* output , i . e . to line_out_pins [ 0 ] . So , line_outs is always positive
* if any analog output exists .
2010-02-28 22:16:53 +03:00
*
2010-09-09 16:50:17 +04:00
* The analog input pins are assigned to inputs array .
2006-03-21 13:24:42 +03:00
* The digital input / output pins are assigned to dig_in_pin and dig_out_pin ,
* respectively .
*/
2007-08-02 17:51:59 +04:00
int snd_hda_parse_pin_def_config ( struct hda_codec * codec ,
struct auto_pin_cfg * cfg ,
hda_nid_t * ignore_nids )
2005-06-13 16:16:38 +04:00
{
2008-01-22 17:35:37 +03:00
hda_nid_t nid , end_nid ;
2007-05-08 17:33:03 +04:00
short seq , assoc_line_out , assoc_speaker ;
short sequences_line_out [ ARRAY_SIZE ( cfg - > line_out_pins ) ] ;
short sequences_speaker [ ARRAY_SIZE ( cfg - > speaker_pins ) ] ;
2007-10-31 17:49:32 +03:00
short sequences_hp [ ARRAY_SIZE ( cfg - > hp_pins ) ] ;
2010-08-30 14:56:55 +04:00
int i ;
2005-06-13 16:16:38 +04:00
memset ( cfg , 0 , sizeof ( * cfg ) ) ;
2007-05-08 17:33:03 +04:00
memset ( sequences_line_out , 0 , sizeof ( sequences_line_out ) ) ;
memset ( sequences_speaker , 0 , sizeof ( sequences_speaker ) ) ;
2007-10-31 17:49:32 +03:00
memset ( sequences_hp , 0 , sizeof ( sequences_hp ) ) ;
2007-05-08 17:33:03 +04:00
assoc_line_out = assoc_speaker = 0 ;
2005-06-13 16:16:38 +04:00
2008-01-22 17:35:37 +03:00
end_nid = codec - > start_nid + codec - > num_nodes ;
for ( nid = codec - > start_nid ; nid < end_nid ; nid + + ) {
2005-11-21 18:33:22 +03:00
unsigned int wid_caps = get_wcaps ( codec , nid ) ;
2009-07-27 14:54:26 +04:00
unsigned int wid_type = get_wcaps_type ( wid_caps ) ;
2005-06-13 16:16:38 +04:00
unsigned int def_conf ;
short assoc , loc ;
/* read all default configuration for pin complex */
if ( wid_type ! = AC_WID_PIN )
continue ;
2005-12-05 21:42:22 +03:00
/* ignore the given nids (e.g. pc-beep returns error) */
if ( ignore_nids & & is_in_nid_list ( nid , ignore_nids ) )
continue ;
2009-02-23 11:28:12 +03:00
def_conf = snd_hda_codec_get_pincfg ( codec , nid ) ;
2005-06-13 16:16:38 +04:00
if ( get_defcfg_connect ( def_conf ) = = AC_JACK_PORT_NONE )
continue ;
loc = get_defcfg_location ( def_conf ) ;
switch ( get_defcfg_device ( def_conf ) ) {
case AC_JACK_LINE_OUT :
seq = get_defcfg_sequence ( def_conf ) ;
assoc = get_defcfg_association ( def_conf ) ;
2008-01-24 13:48:01 +03:00
if ( ! ( wid_caps & AC_WCAP_STEREO ) )
if ( ! cfg - > mono_out_pin )
cfg - > mono_out_pin = nid ;
2007-04-16 13:29:14 +04:00
if ( ! assoc )
2005-06-13 16:16:38 +04:00
continue ;
2007-04-16 13:29:14 +04:00
if ( ! assoc_line_out )
2005-06-13 16:16:38 +04:00
assoc_line_out = assoc ;
else if ( assoc_line_out ! = assoc )
continue ;
if ( cfg - > line_outs > = ARRAY_SIZE ( cfg - > line_out_pins ) )
continue ;
cfg - > line_out_pins [ cfg - > line_outs ] = nid ;
2007-05-08 17:33:03 +04:00
sequences_line_out [ cfg - > line_outs ] = seq ;
2005-06-13 16:16:38 +04:00
cfg - > line_outs + + ;
break ;
2005-11-17 13:09:23 +03:00
case AC_JACK_SPEAKER :
2007-05-08 17:33:03 +04:00
seq = get_defcfg_sequence ( def_conf ) ;
assoc = get_defcfg_association ( def_conf ) ;
2010-02-28 22:16:53 +03:00
if ( ! assoc )
2007-05-08 17:33:03 +04:00
continue ;
2010-02-28 22:16:53 +03:00
if ( ! assoc_speaker )
2007-05-08 17:33:03 +04:00
assoc_speaker = assoc ;
else if ( assoc_speaker ! = assoc )
continue ;
2006-03-21 13:24:42 +03:00
if ( cfg - > speaker_outs > = ARRAY_SIZE ( cfg - > speaker_pins ) )
continue ;
cfg - > speaker_pins [ cfg - > speaker_outs ] = nid ;
2007-05-08 17:33:03 +04:00
sequences_speaker [ cfg - > speaker_outs ] = seq ;
2006-03-21 13:24:42 +03:00
cfg - > speaker_outs + + ;
2005-11-17 13:09:23 +03:00
break ;
2005-06-13 16:16:38 +04:00
case AC_JACK_HP_OUT :
2007-10-31 17:49:32 +03:00
seq = get_defcfg_sequence ( def_conf ) ;
assoc = get_defcfg_association ( def_conf ) ;
2006-09-20 19:10:27 +04:00
if ( cfg - > hp_outs > = ARRAY_SIZE ( cfg - > hp_pins ) )
continue ;
cfg - > hp_pins [ cfg - > hp_outs ] = nid ;
2007-10-31 17:49:32 +03:00
sequences_hp [ cfg - > hp_outs ] = ( assoc < < 4 ) | seq ;
2006-09-20 19:10:27 +04:00
cfg - > hp_outs + + ;
2005-06-13 16:16:38 +04:00
break ;
2010-09-09 16:50:17 +04:00
case AC_JACK_MIC_IN :
add_auto_cfg_input_pin ( cfg , nid , AUTO_PIN_MIC ) ;
2005-06-13 16:16:38 +04:00
break ;
2010-09-09 16:50:17 +04:00
case AC_JACK_LINE_IN :
add_auto_cfg_input_pin ( cfg , nid , AUTO_PIN_LINE_IN ) ;
2005-06-13 16:16:38 +04:00
break ;
case AC_JACK_CD :
2010-08-30 14:56:55 +04:00
add_auto_cfg_input_pin ( cfg , nid , AUTO_PIN_CD ) ;
2005-06-13 16:16:38 +04:00
break ;
case AC_JACK_AUX :
2010-08-30 14:56:55 +04:00
add_auto_cfg_input_pin ( cfg , nid , AUTO_PIN_AUX ) ;
2005-06-13 16:16:38 +04:00
break ;
case AC_JACK_SPDIF_OUT :
2009-01-20 19:17:29 +03:00
case AC_JACK_DIG_OTHER_OUT :
2009-02-11 13:35:15 +03:00
if ( cfg - > dig_outs > = ARRAY_SIZE ( cfg - > dig_out_pins ) )
continue ;
cfg - > dig_out_pins [ cfg - > dig_outs ] = nid ;
cfg - > dig_out_type [ cfg - > dig_outs ] =
( loc = = AC_JACK_LOC_HDMI ) ?
HDA_PCM_TYPE_HDMI : HDA_PCM_TYPE_SPDIF ;
cfg - > dig_outs + + ;
2005-06-13 16:16:38 +04:00
break ;
case AC_JACK_SPDIF_IN :
2009-01-20 19:17:29 +03:00
case AC_JACK_DIG_OTHER_IN :
2005-06-13 16:16:38 +04:00
cfg - > dig_in_pin = nid ;
2009-01-20 20:24:13 +03:00
if ( loc = = AC_JACK_LOC_HDMI )
cfg - > dig_in_type = HDA_PCM_TYPE_HDMI ;
else
cfg - > dig_in_type = HDA_PCM_TYPE_SPDIF ;
2005-06-13 16:16:38 +04:00
break ;
}
}
2008-02-12 20:30:12 +03:00
/* FIX-UP:
* If no line - out is defined but multiple HPs are found ,
* some of them might be the real line - outs .
*/
if ( ! cfg - > line_outs & & cfg - > hp_outs > 1 ) {
int i = 0 ;
while ( i < cfg - > hp_outs ) {
/* The real HPs should have the sequence 0x0f */
if ( ( sequences_hp [ i ] & 0x0f ) = = 0x0f ) {
i + + ;
continue ;
}
/* Move it to the line-out table */
cfg - > line_out_pins [ cfg - > line_outs ] = cfg - > hp_pins [ i ] ;
sequences_line_out [ cfg - > line_outs ] = sequences_hp [ i ] ;
cfg - > line_outs + + ;
cfg - > hp_outs - - ;
memmove ( cfg - > hp_pins + i , cfg - > hp_pins + i + 1 ,
sizeof ( cfg - > hp_pins [ 0 ] ) * ( cfg - > hp_outs - i ) ) ;
2010-09-08 16:57:04 +04:00
memmove ( sequences_hp + i , sequences_hp + i + 1 ,
2008-02-12 20:30:12 +03:00
sizeof ( sequences_hp [ 0 ] ) * ( cfg - > hp_outs - i ) ) ;
}
2010-09-08 17:28:19 +04:00
memset ( cfg - > hp_pins + cfg - > hp_outs , 0 ,
sizeof ( hda_nid_t ) * ( AUTO_CFG_MAX_OUTS - cfg - > hp_outs ) ) ;
2011-01-10 16:47:35 +03:00
if ( ! cfg - > hp_outs )
cfg - > line_out_type = AUTO_PIN_HP_OUT ;
2008-02-12 20:30:12 +03:00
}
2005-06-13 16:16:38 +04:00
/* sort by sequence */
2007-05-08 17:33:03 +04:00
sort_pins_by_sequence ( cfg - > line_out_pins , sequences_line_out ,
cfg - > line_outs ) ;
sort_pins_by_sequence ( cfg - > speaker_pins , sequences_speaker ,
cfg - > speaker_outs ) ;
2007-10-31 17:49:32 +03:00
sort_pins_by_sequence ( cfg - > hp_pins , sequences_hp ,
cfg - > hp_outs ) ;
2010-02-28 22:16:53 +03:00
2007-05-08 17:33:03 +04:00
/*
* FIX - UP : if no line - outs are detected , try to use speaker or HP pin
* as a primary output
*/
if ( ! cfg - > line_outs ) {
if ( cfg - > speaker_outs ) {
cfg - > line_outs = cfg - > speaker_outs ;
memcpy ( cfg - > line_out_pins , cfg - > speaker_pins ,
sizeof ( cfg - > speaker_pins ) ) ;
cfg - > speaker_outs = 0 ;
memset ( cfg - > speaker_pins , 0 , sizeof ( cfg - > speaker_pins ) ) ;
cfg - > line_out_type = AUTO_PIN_SPEAKER_OUT ;
} else if ( cfg - > hp_outs ) {
cfg - > line_outs = cfg - > hp_outs ;
memcpy ( cfg - > line_out_pins , cfg - > hp_pins ,
sizeof ( cfg - > hp_pins ) ) ;
cfg - > hp_outs = 0 ;
memset ( cfg - > hp_pins , 0 , sizeof ( cfg - > hp_pins ) ) ;
cfg - > line_out_type = AUTO_PIN_HP_OUT ;
}
}
2005-06-13 16:16:38 +04:00
2005-07-29 13:54:32 +04:00
/* Reorder the surround channels
* ALSA sequence is front / surr / clfe / side
* HDA sequence is :
* 4 - ch : front / surr = > OK as it is
* 6 - ch : front / clfe / surr
2007-04-20 18:11:43 +04:00
* 8 - ch : front / clfe / rear / side | fc
2005-07-29 13:54:32 +04:00
*/
switch ( cfg - > line_outs ) {
case 3 :
case 4 :
nid = cfg - > line_out_pins [ 1 ] ;
2007-04-20 18:11:43 +04:00
cfg - > line_out_pins [ 1 ] = cfg - > line_out_pins [ 2 ] ;
2005-07-29 13:54:32 +04:00
cfg - > line_out_pins [ 2 ] = nid ;
break ;
2005-06-13 16:16:38 +04:00
}
2010-09-10 00:22:02 +04:00
sort_autocfg_input_pins ( cfg ) ;
2006-03-21 13:24:42 +03:00
/*
* debug prints of the parsed results
*/
snd_printd ( " autoconfig: line_outs=%d (0x%x/0x%x/0x%x/0x%x/0x%x) \n " ,
cfg - > line_outs , cfg - > line_out_pins [ 0 ] , cfg - > line_out_pins [ 1 ] ,
cfg - > line_out_pins [ 2 ] , cfg - > line_out_pins [ 3 ] ,
cfg - > line_out_pins [ 4 ] ) ;
snd_printd ( " speaker_outs=%d (0x%x/0x%x/0x%x/0x%x/0x%x) \n " ,
cfg - > speaker_outs , cfg - > speaker_pins [ 0 ] ,
cfg - > speaker_pins [ 1 ] , cfg - > speaker_pins [ 2 ] ,
cfg - > speaker_pins [ 3 ] , cfg - > speaker_pins [ 4 ] ) ;
2006-09-20 19:10:27 +04:00
snd_printd ( " hp_outs=%d (0x%x/0x%x/0x%x/0x%x/0x%x) \n " ,
cfg - > hp_outs , cfg - > hp_pins [ 0 ] ,
cfg - > hp_pins [ 1 ] , cfg - > hp_pins [ 2 ] ,
cfg - > hp_pins [ 3 ] , cfg - > hp_pins [ 4 ] ) ;
2008-01-24 13:48:01 +03:00
snd_printd ( " mono: mono_out=0x%x \n " , cfg - > mono_out_pin ) ;
2009-02-11 13:35:15 +03:00
if ( cfg - > dig_outs )
snd_printd ( " dig-out=0x%x/0x%x \n " ,
cfg - > dig_out_pins [ 0 ] , cfg - > dig_out_pins [ 1 ] ) ;
2010-08-30 14:56:55 +04:00
snd_printd ( " inputs: " ) ;
for ( i = 0 ; i < cfg - > num_inputs ; i + + ) {
snd_printdd ( " %s=0x%x " ,
2010-09-09 18:28:02 +04:00
hda_get_autocfg_input_label ( codec , cfg , i ) ,
2010-08-30 14:56:55 +04:00
cfg - > inputs [ i ] . pin ) ;
}
snd_printd ( " \n " ) ;
2009-02-11 13:33:13 +03:00
if ( cfg - > dig_in_pin )
2009-01-20 19:15:57 +03:00
snd_printd ( " dig-in=0x%x \n " , cfg - > dig_in_pin ) ;
2006-03-21 13:24:42 +03:00
2005-06-13 16:16:38 +04:00
return 0 ;
}
2008-11-28 17:17:06 +03:00
EXPORT_SYMBOL_HDA ( snd_hda_parse_pin_def_config ) ;
2005-06-13 16:16:38 +04:00
2010-09-17 16:42:34 +04:00
int snd_hda_get_input_pin_attr ( unsigned int def_conf )
2010-09-09 23:36:27 +04:00
{
unsigned int loc = get_defcfg_location ( def_conf ) ;
2010-09-17 12:26:37 +04:00
unsigned int conn = get_defcfg_connect ( def_conf ) ;
2010-09-17 16:42:34 +04:00
if ( conn = = AC_JACK_PORT_NONE )
return INPUT_PIN_ATTR_UNUSED ;
2010-09-17 12:26:37 +04:00
/* Windows may claim the internal mic to be BOTH, too */
if ( conn = = AC_JACK_PORT_FIXED | | conn = = AC_JACK_PORT_BOTH )
2010-09-17 16:42:34 +04:00
return INPUT_PIN_ATTR_INT ;
2010-09-17 12:26:37 +04:00
if ( ( loc & 0x30 ) = = AC_JACK_LOC_INTERNAL )
2010-09-17 16:42:34 +04:00
return INPUT_PIN_ATTR_INT ;
2010-09-09 23:36:27 +04:00
if ( ( loc & 0x30 ) = = AC_JACK_LOC_SEPARATE )
2010-09-17 16:42:34 +04:00
return INPUT_PIN_ATTR_DOCK ;
2010-09-09 23:36:27 +04:00
if ( loc = = AC_JACK_LOC_REAR )
2010-09-17 16:42:34 +04:00
return INPUT_PIN_ATTR_REAR ;
2010-09-09 23:36:27 +04:00
if ( loc = = AC_JACK_LOC_FRONT )
2010-09-17 16:42:34 +04:00
return INPUT_PIN_ATTR_FRONT ;
return INPUT_PIN_ATTR_NORMAL ;
2010-09-09 23:36:27 +04:00
}
2010-09-17 16:42:34 +04:00
EXPORT_SYMBOL_HDA ( snd_hda_get_input_pin_attr ) ;
2010-09-09 23:36:27 +04:00
2010-09-10 00:08:44 +04:00
/**
* hda_get_input_pin_label - Give a label for the given input pin
*
* When check_location is true , the function checks the pin location
* for mic and line - in pins , and set an appropriate prefix like " Front " ,
* " Rear " , " Internal " .
*/
2010-09-09 18:28:02 +04:00
const char * hda_get_input_pin_label ( struct hda_codec * codec , hda_nid_t pin ,
int check_location )
{
2010-09-09 23:36:27 +04:00
unsigned int def_conf ;
2011-01-17 13:29:34 +03:00
static const char * const mic_names [ ] = {
2010-09-09 23:36:27 +04:00
" Internal Mic " , " Dock Mic " , " Mic " , " Front Mic " , " Rear Mic " ,
} ;
2010-09-17 16:42:34 +04:00
int attr ;
2010-09-09 18:28:02 +04:00
def_conf = snd_hda_codec_get_pincfg ( codec , pin ) ;
switch ( get_defcfg_device ( def_conf ) ) {
case AC_JACK_MIC_IN :
if ( ! check_location )
return " Mic " ;
2010-09-17 16:42:34 +04:00
attr = snd_hda_get_input_pin_attr ( def_conf ) ;
if ( ! attr )
return " None " ;
return mic_names [ attr - 1 ] ;
2010-09-09 18:28:02 +04:00
case AC_JACK_LINE_IN :
if ( ! check_location )
return " Line " ;
2010-09-17 16:42:34 +04:00
attr = snd_hda_get_input_pin_attr ( def_conf ) ;
if ( ! attr )
return " None " ;
if ( attr = = INPUT_PIN_ATTR_DOCK )
return " Dock Line " ;
return " Line " ;
2010-09-09 18:28:02 +04:00
case AC_JACK_AUX :
return " Aux " ;
case AC_JACK_CD :
return " CD " ;
case AC_JACK_SPDIF_IN :
return " SPDIF In " ;
case AC_JACK_DIG_OTHER_IN :
return " Digital In " ;
default :
return " Misc " ;
}
}
EXPORT_SYMBOL_HDA ( hda_get_input_pin_label ) ;
2005-12-07 15:56:29 +03:00
2010-09-09 23:36:27 +04:00
/* Check whether the location prefix needs to be added to the label.
* If all mic - jacks are in the same location ( e . g . rear panel ) , we don ' t
* have to put " Front " prefix to each label . In such a case , returns false .
*/
static int check_mic_location_need ( struct hda_codec * codec ,
const struct auto_pin_cfg * cfg ,
int input )
{
unsigned int defc ;
int i , attr , attr2 ;
defc = snd_hda_codec_get_pincfg ( codec , cfg - > inputs [ input ] . pin ) ;
2010-09-17 16:42:34 +04:00
attr = snd_hda_get_input_pin_attr ( defc ) ;
2010-09-09 23:36:27 +04:00
/* for internal or docking mics, we need locations */
2010-09-17 16:42:34 +04:00
if ( attr < = INPUT_PIN_ATTR_NORMAL )
2010-09-09 23:36:27 +04:00
return 1 ;
attr = 0 ;
for ( i = 0 ; i < cfg - > num_inputs ; i + + ) {
defc = snd_hda_codec_get_pincfg ( codec , cfg - > inputs [ i ] . pin ) ;
2010-09-17 16:42:34 +04:00
attr2 = snd_hda_get_input_pin_attr ( defc ) ;
if ( attr2 > = INPUT_PIN_ATTR_NORMAL ) {
2010-09-09 23:36:27 +04:00
if ( attr & & attr ! = attr2 )
return 1 ; /* different locations found */
attr = attr2 ;
}
}
return 0 ;
}
2010-09-10 00:08:44 +04:00
/**
* hda_get_autocfg_input_label - Get a label for the given input
*
* Get a label for the given input pin defined by the autocfg item .
* Unlike hda_get_input_pin_label ( ) , this function checks all inputs
* defined in autocfg and avoids the redundant mic / line prefix as much as
* possible .
*/
2010-09-09 18:28:02 +04:00
const char * hda_get_autocfg_input_label ( struct hda_codec * codec ,
const struct auto_pin_cfg * cfg ,
int input )
2010-08-30 15:00:16 +04:00
{
int type = cfg - > inputs [ input ] . type ;
2010-09-09 18:28:02 +04:00
int has_multiple_pins = 0 ;
2010-08-30 15:00:16 +04:00
2010-09-09 18:28:02 +04:00
if ( ( input > 0 & & cfg - > inputs [ input - 1 ] . type = = type ) | |
( input < cfg - > num_inputs - 1 & & cfg - > inputs [ input + 1 ] . type = = type ) )
has_multiple_pins = 1 ;
2010-09-09 23:36:27 +04:00
if ( has_multiple_pins & & type = = AUTO_PIN_MIC )
has_multiple_pins & = check_mic_location_need ( codec , cfg , input ) ;
2010-09-09 18:28:02 +04:00
return hda_get_input_pin_label ( codec , cfg - > inputs [ input ] . pin ,
has_multiple_pins ) ;
}
EXPORT_SYMBOL_HDA ( hda_get_autocfg_input_label ) ;
2010-09-10 00:08:44 +04:00
/**
* snd_hda_add_imux_item - Add an item to input_mux
*
* When the same label is used already in the existing items , the number
* suffix is appended to the label . This label index number is stored
* to type_idx when non - NULL pointer is given .
*/
2010-09-09 18:28:02 +04:00
int snd_hda_add_imux_item ( struct hda_input_mux * imux , const char * label ,
int index , int * type_idx )
{
int i , label_idx = 0 ;
if ( imux - > num_items > = HDA_MAX_NUM_INPUTS ) {
snd_printd ( KERN_ERR " hda_codec: Too many imux items! \n " ) ;
return - EINVAL ;
}
for ( i = 0 ; i < imux - > num_items ; i + + ) {
if ( ! strncmp ( label , imux - > items [ i ] . label , strlen ( label ) ) )
label_idx + + ;
2010-08-30 15:00:16 +04:00
}
2010-09-09 18:28:02 +04:00
if ( type_idx )
* type_idx = label_idx ;
if ( label_idx > 0 )
snprintf ( imux - > items [ imux - > num_items ] . label ,
sizeof ( imux - > items [ imux - > num_items ] . label ) ,
" %s %d " , label , label_idx ) ;
2010-09-09 16:21:17 +04:00
else
2010-09-09 18:28:02 +04:00
strlcpy ( imux - > items [ imux - > num_items ] . label , label ,
sizeof ( imux - > items [ imux - > num_items ] . label ) ) ;
imux - > items [ imux - > num_items ] . index = index ;
imux - > num_items + + ;
return 0 ;
2010-08-30 15:00:16 +04:00
}
2010-09-09 18:28:02 +04:00
EXPORT_SYMBOL_HDA ( snd_hda_add_imux_item ) ;
2010-08-30 15:00:16 +04:00
2005-12-07 15:56:29 +03:00
2005-04-17 02:20:36 +04:00
# ifdef CONFIG_PM
/*
* power management
*/
/**
* snd_hda_suspend - suspend the codecs
* @ bus : the HDA bus
*
* Returns 0 if successful .
*/
2009-06-02 03:16:07 +04:00
int snd_hda_suspend ( struct hda_bus * bus )
2005-04-17 02:20:36 +04:00
{
2007-04-16 13:29:14 +04:00
struct hda_codec * codec ;
2005-04-17 02:20:36 +04:00
2007-04-16 13:29:14 +04:00
list_for_each_entry ( codec , & bus - > codec_list , list ) {
2007-08-14 17:18:26 +04:00
# ifdef CONFIG_SND_HDA_POWER_SAVE
if ( ! codec - > power_on )
continue ;
# endif
2007-08-10 19:21:45 +04:00
hda_call_codec_suspend ( codec ) ;
2005-04-17 02:20:36 +04:00
}
return 0 ;
}
2008-11-28 17:17:06 +03:00
EXPORT_SYMBOL_HDA ( snd_hda_suspend ) ;
2005-04-17 02:20:36 +04:00
/**
* snd_hda_resume - resume the codecs
* @ bus : the HDA bus
*
* Returns 0 if successful .
2007-08-10 19:21:45 +04:00
*
* This fucntion is defined only when POWER_SAVE isn ' t set .
* In the power - save mode , the codec is resumed dynamically .
2005-04-17 02:20:36 +04:00
*/
int snd_hda_resume ( struct hda_bus * bus )
{
2007-04-16 13:29:14 +04:00
struct hda_codec * codec ;
2005-04-17 02:20:36 +04:00
2007-04-16 13:29:14 +04:00
list_for_each_entry ( codec , & bus - > codec_list , list ) {
2007-09-03 17:28:04 +04:00
if ( snd_hda_codec_needs_resume ( codec ) )
hda_call_codec_resume ( codec ) ;
2005-04-17 02:20:36 +04:00
}
return 0 ;
}
2008-11-28 17:17:06 +03:00
EXPORT_SYMBOL_HDA ( snd_hda_resume ) ;
2008-11-27 17:47:11 +03:00
# endif /* CONFIG_PM */
2008-07-30 17:01:44 +04:00
/*
* generic arrays
*/
2009-11-16 16:58:17 +03:00
/**
* snd_array_new - get a new element from the given array
* @ array : the array object
2010-02-28 22:16:53 +03:00
*
2009-11-16 16:58:17 +03:00
* Get a new element from the given array . If it exceeds the
* pre - allocated array size , re - allocate the array .
*
* Returns NULL if allocation failed .
2008-07-30 17:01:44 +04:00
*/
void * snd_array_new ( struct snd_array * array )
{
if ( array - > used > = array - > alloced ) {
int num = array - > alloced + array - > alloc_align ;
2008-11-07 02:26:52 +03:00
void * nlist ;
if ( snd_BUG_ON ( num > = 4096 ) )
return NULL ;
nlist = kcalloc ( num + 1 , array - > elem_size , GFP_KERNEL ) ;
2008-07-30 17:01:44 +04:00
if ( ! nlist )
return NULL ;
if ( array - > list ) {
memcpy ( nlist , array - > list ,
array - > elem_size * array - > alloced ) ;
kfree ( array - > list ) ;
}
array - > list = nlist ;
array - > alloced = num ;
}
2008-11-10 18:24:26 +03:00
return snd_array_elem ( array , array - > used + + ) ;
2008-07-30 17:01:44 +04:00
}
2008-11-28 17:17:06 +03:00
EXPORT_SYMBOL_HDA ( snd_array_new ) ;
2008-07-30 17:01:44 +04:00
2009-11-16 16:58:17 +03:00
/**
* snd_array_free - free the given array elements
* @ array : the array object
*/
2008-07-30 17:01:44 +04:00
void snd_array_free ( struct snd_array * array )
{
kfree ( array - > list ) ;
array - > used = 0 ;
array - > alloced = 0 ;
array - > list = NULL ;
}
2008-11-28 17:17:06 +03:00
EXPORT_SYMBOL_HDA ( snd_array_free ) ;
2008-11-21 23:24:03 +03:00
2009-11-16 16:58:17 +03:00
/**
* snd_print_pcm_rates - Print the supported PCM rates to the string buffer
* @ pcm : PCM caps bits
* @ buf : the string buffer to write
* @ buflen : the max buffer length
*
2008-11-21 23:24:03 +03:00
* used by hda_proc . c and hda_eld . c
*/
void snd_print_pcm_rates ( int pcm , char * buf , int buflen )
{
static unsigned int rates [ ] = {
8000 , 11025 , 16000 , 22050 , 32000 , 44100 , 48000 , 88200 ,
96000 , 176400 , 192000 , 384000
} ;
int i , j ;
for ( i = 0 , j = 0 ; i < ARRAY_SIZE ( rates ) ; i + + )
if ( pcm & ( 1 < < i ) )
j + = snprintf ( buf + j , buflen - j , " %d " , rates [ i ] ) ;
buf [ j ] = ' \0 ' ; /* necessary when j == 0 */
}
2008-11-28 17:17:06 +03:00
EXPORT_SYMBOL_HDA ( snd_print_pcm_rates ) ;
2008-11-21 23:24:03 +03:00
2009-11-16 16:58:17 +03:00
/**
* snd_print_pcm_bits - Print the supported PCM fmt bits to the string buffer
* @ pcm : PCM caps bits
* @ buf : the string buffer to write
* @ buflen : the max buffer length
*
* used by hda_proc . c and hda_eld . c
*/
2008-11-21 23:24:03 +03:00
void snd_print_pcm_bits ( int pcm , char * buf , int buflen )
{
static unsigned int bits [ ] = { 8 , 16 , 20 , 24 , 32 } ;
int i , j ;
for ( i = 0 , j = 0 ; i < ARRAY_SIZE ( bits ) ; i + + )
if ( pcm & ( AC_SUPPCM_BITS_8 < < i ) )
j + = snprintf ( buf + j , buflen - j , " %d " , bits [ i ] ) ;
buf [ j ] = ' \0 ' ; /* necessary when j == 0 */
}
2008-11-28 17:17:06 +03:00
EXPORT_SYMBOL_HDA ( snd_print_pcm_bits ) ;
2008-11-27 17:47:11 +03:00
2011-03-03 16:40:14 +03:00
# ifdef CONFIG_SND_HDA_INPUT_JACK
/*
* Input - jack notification support
*/
struct hda_jack_item {
hda_nid_t nid ;
int type ;
struct snd_jack * jack ;
} ;
static const char * get_jack_default_name ( struct hda_codec * codec , hda_nid_t nid ,
int type )
{
switch ( type ) {
case SND_JACK_HEADPHONE :
return " Headphone " ;
case SND_JACK_MICROPHONE :
return " Mic " ;
case SND_JACK_LINEOUT :
return " Line-out " ;
case SND_JACK_HEADSET :
return " Headset " ;
default :
return " Misc " ;
}
}
static void hda_free_jack_priv ( struct snd_jack * jack )
{
struct hda_jack_item * jacks = jack - > private_data ;
jacks - > nid = 0 ;
jacks - > jack = NULL ;
}
int snd_hda_input_jack_add ( struct hda_codec * codec , hda_nid_t nid , int type ,
const char * name )
{
struct hda_jack_item * jack ;
int err ;
snd_array_init ( & codec - > jacks , sizeof ( * jack ) , 32 ) ;
jack = snd_array_new ( & codec - > jacks ) ;
if ( ! jack )
return - ENOMEM ;
jack - > nid = nid ;
jack - > type = type ;
if ( ! name )
name = get_jack_default_name ( codec , nid , type ) ;
err = snd_jack_new ( codec - > bus - > card , name , type , & jack - > jack ) ;
if ( err < 0 ) {
jack - > nid = 0 ;
return err ;
}
jack - > jack - > private_data = jack ;
jack - > jack - > private_free = hda_free_jack_priv ;
return 0 ;
}
EXPORT_SYMBOL_HDA ( snd_hda_input_jack_add ) ;
void snd_hda_input_jack_report ( struct hda_codec * codec , hda_nid_t nid )
{
struct hda_jack_item * jacks = codec - > jacks . list ;
int i ;
if ( ! jacks )
return ;
for ( i = 0 ; i < codec - > jacks . used ; i + + , jacks + + ) {
unsigned int pin_ctl ;
unsigned int present ;
int type ;
if ( jacks - > nid ! = nid )
continue ;
present = snd_hda_jack_detect ( codec , nid ) ;
type = jacks - > type ;
if ( type = = ( SND_JACK_HEADPHONE | SND_JACK_LINEOUT ) ) {
pin_ctl = snd_hda_codec_read ( codec , nid , 0 ,
AC_VERB_GET_PIN_WIDGET_CONTROL , 0 ) ;
type = ( pin_ctl & AC_PINCTL_HP_EN ) ?
SND_JACK_HEADPHONE : SND_JACK_LINEOUT ;
}
snd_jack_report ( jacks - > jack , present ? type : 0 ) ;
}
}
EXPORT_SYMBOL_HDA ( snd_hda_input_jack_report ) ;
/* free jack instances manually when clearing/reconfiguring */
void snd_hda_input_jack_free ( struct hda_codec * codec )
{
if ( ! codec - > bus - > shutdown & & codec - > jacks . list ) {
struct hda_jack_item * jacks = codec - > jacks . list ;
int i ;
for ( i = 0 ; i < codec - > jacks . used ; i + + , jacks + + ) {
if ( jacks - > jack )
snd_device_free ( codec - > bus - > card , jacks - > jack ) ;
}
}
snd_array_free ( & codec - > jacks ) ;
}
EXPORT_SYMBOL_HDA ( snd_hda_input_jack_free ) ;
# endif /* CONFIG_SND_HDA_INPUT_JACK */
2008-11-27 17:47:11 +03:00
MODULE_DESCRIPTION ( " HDA codec core " ) ;
MODULE_LICENSE ( " GPL " ) ;