2005-04-16 15:20:36 -07:00
/*
* Universal Interface for Intel High Definition Audio Codec
*
* HD audio interface patch for C - Media CMI9880
*
* 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 <sound/driver.h>
# include <linux/init.h>
# include <linux/delay.h>
# include <linux/slab.h>
# include <linux/pci.h>
# include <sound/core.h>
# include "hda_codec.h"
# include "hda_local.h"
2005-04-08 15:05:13 +02:00
# define NUM_PINS 11
2005-04-16 15:20:36 -07:00
/* board config type */
enum {
CMI_MINIMAL , /* back 3-jack */
CMI_MIN_FP , /* back 3-jack + front-panel 2-jack */
CMI_FULL , /* back 6-jack + front-panel 2-jack */
CMI_FULL_DIG , /* back 6-jack + front-panel 2-jack + digital I/O */
CMI_ALLOUT , /* back 5-jack + front-panel 2-jack + digital out */
2005-04-08 15:05:13 +02:00
CMI_AUTO , /* let driver guess it */
2005-04-16 15:20:36 -07:00
} ;
struct cmi_spec {
int board_config ;
unsigned int surr_switch : 1 ; /* switchable line,mic */
unsigned int no_line_in : 1 ; /* no line-in (5-jack) */
unsigned int front_panel : 1 ; /* has front-panel 2-jack */
/* playback */
struct hda_multi_out multiout ;
2005-04-08 15:05:13 +02:00
hda_nid_t dac_nids [ 4 ] ; /* NID for each DAC */
2005-06-13 14:16:38 +02:00
int num_dacs ;
2005-04-16 15:20:36 -07:00
/* capture */
hda_nid_t * adc_nids ;
hda_nid_t dig_in_nid ;
/* capture source */
const struct hda_input_mux * input_mux ;
unsigned int cur_mux [ 2 ] ;
/* channel mode */
unsigned int num_ch_modes ;
unsigned int cur_ch_mode ;
const struct cmi_channel_mode * channel_modes ;
struct hda_pcm pcm_rec [ 2 ] ; /* PCM information */
2005-04-08 15:05:13 +02:00
/* pin deafault configuration */
hda_nid_t pin_nid [ NUM_PINS ] ;
unsigned int def_conf [ NUM_PINS ] ;
unsigned int pin_def_confs ;
/* multichannel pins */
hda_nid_t multich_pin [ 4 ] ; /* max 8-channel */
struct hda_verb multi_init [ 9 ] ; /* 2 verbs for each pin + terminator */
2005-04-16 15:20:36 -07:00
} ;
2005-06-13 14:16:38 +02:00
/* amp values */
# define AMP_IN_MUTE(idx) (0x7080 | ((idx)<<8))
# define AMP_IN_UNMUTE(idx) (0x7000 | ((idx)<<8))
# define AMP_OUT_MUTE 0xb080
# define AMP_OUT_UNMUTE 0xb000
# define AMP_OUT_ZERO 0xb000
/* pinctl values */
# define PIN_IN 0x20
# define PIN_VREF80 0x24
# define PIN_VREF50 0x21
# define PIN_OUT 0x40
# define PIN_HP 0xc0
2005-04-16 15:20:36 -07:00
/*
* input MUX
*/
static int cmi_mux_enum_info ( snd_kcontrol_t * kcontrol , snd_ctl_elem_info_t * uinfo )
{
struct hda_codec * codec = snd_kcontrol_chip ( kcontrol ) ;
struct cmi_spec * spec = codec - > spec ;
return snd_hda_input_mux_info ( spec - > input_mux , uinfo ) ;
}
static int cmi_mux_enum_get ( snd_kcontrol_t * kcontrol , snd_ctl_elem_value_t * ucontrol )
{
struct hda_codec * codec = snd_kcontrol_chip ( kcontrol ) ;
struct cmi_spec * spec = codec - > spec ;
unsigned int adc_idx = snd_ctl_get_ioffidx ( kcontrol , & ucontrol - > id ) ;
ucontrol - > value . enumerated . item [ 0 ] = spec - > cur_mux [ adc_idx ] ;
return 0 ;
}
static int cmi_mux_enum_put ( snd_kcontrol_t * kcontrol , snd_ctl_elem_value_t * ucontrol )
{
struct hda_codec * codec = snd_kcontrol_chip ( kcontrol ) ;
struct cmi_spec * spec = codec - > spec ;
unsigned int adc_idx = snd_ctl_get_ioffidx ( kcontrol , & ucontrol - > id ) ;
return snd_hda_input_mux_put ( codec , spec - > input_mux , ucontrol ,
spec - > adc_nids [ adc_idx ] , & spec - > cur_mux [ adc_idx ] ) ;
}
/*
* shared line - in , mic for surrounds
*/
/* 3-stack / 2 channel */
static struct hda_verb cmi9880_ch2_init [ ] = {
/* set line-in PIN for input */
2005-06-13 14:16:38 +02:00
{ 0x0c , AC_VERB_SET_PIN_WIDGET_CONTROL , PIN_IN } ,
2005-04-16 15:20:36 -07:00
/* set mic PIN for input, also enable vref */
2005-06-13 14:16:38 +02:00
{ 0x0d , AC_VERB_SET_PIN_WIDGET_CONTROL , PIN_VREF80 } ,
2005-04-16 15:20:36 -07:00
/* route front PCM (DAC1) to HP */
{ 0x0f , AC_VERB_SET_CONNECT_SEL , 0x00 } ,
{ }
} ;
/* 3-stack / 6 channel */
static struct hda_verb cmi9880_ch6_init [ ] = {
/* set line-in PIN for output */
2005-06-13 14:16:38 +02:00
{ 0x0c , AC_VERB_SET_PIN_WIDGET_CONTROL , PIN_OUT } ,
2005-04-16 15:20:36 -07:00
/* set mic PIN for output */
2005-06-13 14:16:38 +02:00
{ 0x0d , AC_VERB_SET_PIN_WIDGET_CONTROL , PIN_OUT } ,
2005-04-16 15:20:36 -07:00
/* route front PCM (DAC1) to HP */
{ 0x0f , AC_VERB_SET_CONNECT_SEL , 0x00 } ,
{ }
} ;
/* 3-stack+front / 8 channel */
static struct hda_verb cmi9880_ch8_init [ ] = {
/* set line-in PIN for output */
2005-06-13 14:16:38 +02:00
{ 0x0c , AC_VERB_SET_PIN_WIDGET_CONTROL , PIN_OUT } ,
2005-04-16 15:20:36 -07:00
/* set mic PIN for output */
2005-06-13 14:16:38 +02:00
{ 0x0d , AC_VERB_SET_PIN_WIDGET_CONTROL , PIN_OUT } ,
2005-04-16 15:20:36 -07:00
/* route rear-surround PCM (DAC4) to HP */
{ 0x0f , AC_VERB_SET_CONNECT_SEL , 0x03 } ,
{ }
} ;
struct cmi_channel_mode {
unsigned int channels ;
const struct hda_verb * sequence ;
} ;
static struct cmi_channel_mode cmi9880_channel_modes [ 3 ] = {
{ 2 , cmi9880_ch2_init } ,
{ 6 , cmi9880_ch6_init } ,
{ 8 , cmi9880_ch8_init } ,
} ;
static int cmi_ch_mode_info ( snd_kcontrol_t * kcontrol , snd_ctl_elem_info_t * uinfo )
{
struct hda_codec * codec = snd_kcontrol_chip ( kcontrol ) ;
struct cmi_spec * spec = codec - > spec ;
snd_assert ( spec - > channel_modes , return - EINVAL ) ;
uinfo - > type = SNDRV_CTL_ELEM_TYPE_ENUMERATED ;
uinfo - > count = 1 ;
uinfo - > value . enumerated . items = spec - > num_ch_modes ;
if ( uinfo - > value . enumerated . item > = uinfo - > value . enumerated . items )
uinfo - > value . enumerated . item = uinfo - > value . enumerated . items - 1 ;
sprintf ( uinfo - > value . enumerated . name , " %dch " ,
spec - > channel_modes [ uinfo - > value . enumerated . item ] . channels ) ;
return 0 ;
}
static int cmi_ch_mode_get ( snd_kcontrol_t * kcontrol , snd_ctl_elem_value_t * ucontrol )
{
struct hda_codec * codec = snd_kcontrol_chip ( kcontrol ) ;
struct cmi_spec * spec = codec - > spec ;
ucontrol - > value . enumerated . item [ 0 ] = spec - > cur_ch_mode ;
return 0 ;
}
static int cmi_ch_mode_put ( snd_kcontrol_t * kcontrol , snd_ctl_elem_value_t * ucontrol )
{
struct hda_codec * codec = snd_kcontrol_chip ( kcontrol ) ;
struct cmi_spec * spec = codec - > spec ;
snd_assert ( spec - > channel_modes , return - EINVAL ) ;
if ( ucontrol - > value . enumerated . item [ 0 ] > = spec - > num_ch_modes )
ucontrol - > value . enumerated . item [ 0 ] = spec - > num_ch_modes ;
if ( ucontrol - > value . enumerated . item [ 0 ] = = spec - > cur_ch_mode & &
! codec - > in_resume )
return 0 ;
spec - > cur_ch_mode = ucontrol - > value . enumerated . item [ 0 ] ;
snd_hda_sequence_write ( codec , spec - > channel_modes [ spec - > cur_ch_mode ] . sequence ) ;
spec - > multiout . max_channels = spec - > channel_modes [ spec - > cur_ch_mode ] . channels ;
return 1 ;
}
/*
*/
static snd_kcontrol_new_t cmi9880_basic_mixer [ ] = {
/* CMI9880 has no playback volumes! */
HDA_CODEC_MUTE ( " PCM Playback Switch " , 0x03 , 0x0 , HDA_OUTPUT ) , /* front */
HDA_CODEC_MUTE ( " Surround Playback Switch " , 0x04 , 0x0 , HDA_OUTPUT ) ,
HDA_CODEC_MUTE_MONO ( " Center Playback Switch " , 0x05 , 1 , 0x0 , HDA_OUTPUT ) ,
HDA_CODEC_MUTE_MONO ( " LFE Playback Switch " , 0x05 , 2 , 0x0 , HDA_OUTPUT ) ,
HDA_CODEC_MUTE ( " Side Playback Switch " , 0x06 , 0x0 , HDA_OUTPUT ) ,
{
. iface = SNDRV_CTL_ELEM_IFACE_MIXER ,
/* The multiple "Capture Source" controls confuse alsamixer
* So call somewhat different . .
* FIXME : the controls appear in the " playback " view !
*/
/* .name = "Capture Source", */
. name = " Input Source " ,
. count = 2 ,
. info = cmi_mux_enum_info ,
. get = cmi_mux_enum_get ,
. put = cmi_mux_enum_put ,
} ,
HDA_CODEC_VOLUME ( " Capture Volume " , 0x08 , 0 , HDA_INPUT ) ,
HDA_CODEC_VOLUME_IDX ( " Capture Volume " , 1 , 0x09 , 0 , HDA_INPUT ) ,
HDA_CODEC_MUTE ( " Capture Switch " , 0x08 , 0 , HDA_INPUT ) ,
HDA_CODEC_MUTE_IDX ( " Capture Switch " , 1 , 0x09 , 0 , HDA_INPUT ) ,
HDA_CODEC_VOLUME ( " PC Speaker Playback Volume " , 0x23 , 0 , HDA_OUTPUT ) ,
HDA_CODEC_MUTE ( " PC Speaker Playback Switch " , 0x23 , 0 , HDA_OUTPUT ) ,
{ } /* end */
} ;
/*
* shared I / O pins
*/
static snd_kcontrol_new_t cmi9880_ch_mode_mixer [ ] = {
{
. iface = SNDRV_CTL_ELEM_IFACE_MIXER ,
. name = " Channel Mode " ,
. info = cmi_ch_mode_info ,
. get = cmi_ch_mode_get ,
. put = cmi_ch_mode_put ,
} ,
{ } /* end */
} ;
/* AUD-in selections:
* 0x0b 0x0c 0x0d 0x0e 0x0f 0x10 0x11 0x1f 0x20
*/
static struct hda_input_mux cmi9880_basic_mux = {
. num_items = 4 ,
. items = {
{ " Front Mic " , 0x5 } ,
{ " Rear Mic " , 0x2 } ,
{ " Line " , 0x1 } ,
{ " CD " , 0x7 } ,
}
} ;
static struct hda_input_mux cmi9880_no_line_mux = {
. num_items = 3 ,
. items = {
{ " Front Mic " , 0x5 } ,
{ " Rear Mic " , 0x2 } ,
{ " CD " , 0x7 } ,
}
} ;
/* front, rear, clfe, rear_surr */
static hda_nid_t cmi9880_dac_nids [ 4 ] = {
0x03 , 0x04 , 0x05 , 0x06
} ;
/* ADC0, ADC1 */
static hda_nid_t cmi9880_adc_nids [ 2 ] = {
0x08 , 0x09
} ;
# define CMI_DIG_OUT_NID 0x07
# define CMI_DIG_IN_NID 0x0a
/*
*/
static struct hda_verb cmi9880_basic_init [ ] = {
/* port-D for line out (rear panel) */
2005-06-13 14:16:38 +02:00
{ 0x0b , AC_VERB_SET_PIN_WIDGET_CONTROL , PIN_HP } ,
2005-04-16 15:20:36 -07:00
/* port-E for HP out (front panel) */
2005-06-13 14:16:38 +02:00
{ 0x0f , AC_VERB_SET_PIN_WIDGET_CONTROL , PIN_HP } ,
2005-04-16 15:20:36 -07:00
/* route front PCM to HP */
{ 0x0f , AC_VERB_SET_CONNECT_SEL , 0x00 } ,
/* port-A for surround (rear panel) */
2005-06-13 14:16:38 +02:00
{ 0x0e , AC_VERB_SET_PIN_WIDGET_CONTROL , PIN_HP } ,
2005-04-16 15:20:36 -07:00
/* port-G for CLFE (rear panel) */
2005-06-13 14:16:38 +02:00
{ 0x1f , AC_VERB_SET_PIN_WIDGET_CONTROL , PIN_HP } ,
2005-03-24 20:47:35 +01:00
{ 0x1f , AC_VERB_SET_CONNECT_SEL , 0x02 } ,
2005-04-16 15:20:36 -07:00
/* port-H for side (rear panel) */
2005-06-13 14:16:38 +02:00
{ 0x20 , AC_VERB_SET_PIN_WIDGET_CONTROL , PIN_HP } ,
2005-03-24 20:47:35 +01:00
{ 0x20 , AC_VERB_SET_CONNECT_SEL , 0x01 } ,
2005-04-16 15:20:36 -07:00
/* port-C for line-in (rear panel) */
2005-06-13 14:16:38 +02:00
{ 0x0c , AC_VERB_SET_PIN_WIDGET_CONTROL , PIN_IN } ,
2005-04-16 15:20:36 -07:00
/* port-B for mic-in (rear panel) with vref */
2005-06-13 14:16:38 +02:00
{ 0x0d , AC_VERB_SET_PIN_WIDGET_CONTROL , PIN_VREF80 } ,
2005-04-16 15:20:36 -07:00
/* port-F for mic-in (front panel) with vref */
2005-06-13 14:16:38 +02:00
{ 0x10 , AC_VERB_SET_PIN_WIDGET_CONTROL , PIN_VREF80 } ,
2005-04-16 15:20:36 -07:00
/* CD-in */
2005-06-13 14:16:38 +02:00
{ 0x11 , AC_VERB_SET_PIN_WIDGET_CONTROL , PIN_IN } ,
2005-04-16 15:20:36 -07:00
/* route front mic to ADC1/2 */
{ 0x08 , AC_VERB_SET_CONNECT_SEL , 0x05 } ,
{ 0x09 , AC_VERB_SET_CONNECT_SEL , 0x05 } ,
{ } /* terminator */
} ;
static struct hda_verb cmi9880_allout_init [ ] = {
/* port-D for line out (rear panel) */
2005-06-13 14:16:38 +02:00
{ 0x0b , AC_VERB_SET_PIN_WIDGET_CONTROL , PIN_HP } ,
2005-04-16 15:20:36 -07:00
/* port-E for HP out (front panel) */
2005-06-13 14:16:38 +02:00
{ 0x0f , AC_VERB_SET_PIN_WIDGET_CONTROL , PIN_HP } ,
2005-04-16 15:20:36 -07:00
/* route front PCM to HP */
{ 0x0f , AC_VERB_SET_CONNECT_SEL , 0x00 } ,
/* port-A for side (rear panel) */
2005-06-13 14:16:38 +02:00
{ 0x0e , AC_VERB_SET_PIN_WIDGET_CONTROL , PIN_HP } ,
2005-04-16 15:20:36 -07:00
/* port-G for CLFE (rear panel) */
2005-06-13 14:16:38 +02:00
{ 0x1f , AC_VERB_SET_PIN_WIDGET_CONTROL , PIN_HP } ,
2005-03-24 20:47:35 +01:00
{ 0x1f , AC_VERB_SET_CONNECT_SEL , 0x02 } ,
/* port-H for side (rear panel) */
2005-06-13 14:16:38 +02:00
{ 0x20 , AC_VERB_SET_PIN_WIDGET_CONTROL , PIN_HP } ,
2005-03-24 20:47:35 +01:00
{ 0x20 , AC_VERB_SET_CONNECT_SEL , 0x01 } ,
2005-04-16 15:20:36 -07:00
/* port-C for surround (rear panel) */
2005-06-13 14:16:38 +02:00
{ 0x0c , AC_VERB_SET_PIN_WIDGET_CONTROL , PIN_HP } ,
2005-04-16 15:20:36 -07:00
/* port-B for mic-in (rear panel) with vref */
2005-06-13 14:16:38 +02:00
{ 0x0d , AC_VERB_SET_PIN_WIDGET_CONTROL , PIN_VREF80 } ,
2005-04-16 15:20:36 -07:00
/* port-F for mic-in (front panel) with vref */
2005-06-13 14:16:38 +02:00
{ 0x10 , AC_VERB_SET_PIN_WIDGET_CONTROL , PIN_VREF80 } ,
2005-04-16 15:20:36 -07:00
/* CD-in */
2005-06-13 14:16:38 +02:00
{ 0x11 , AC_VERB_SET_PIN_WIDGET_CONTROL , PIN_IN } ,
2005-04-16 15:20:36 -07:00
/* route front mic to ADC1/2 */
{ 0x08 , AC_VERB_SET_CONNECT_SEL , 0x05 } ,
{ 0x09 , AC_VERB_SET_CONNECT_SEL , 0x05 } ,
{ } /* terminator */
} ;
/*
*/
static int cmi9880_build_controls ( struct hda_codec * codec )
{
struct cmi_spec * spec = codec - > spec ;
int err ;
err = snd_hda_add_new_ctls ( codec , cmi9880_basic_mixer ) ;
if ( err < 0 )
return err ;
if ( spec - > surr_switch ) {
err = snd_hda_add_new_ctls ( codec , cmi9880_ch_mode_mixer ) ;
if ( err < 0 )
return err ;
}
if ( spec - > multiout . dig_out_nid ) {
err = snd_hda_create_spdif_out_ctls ( codec , spec - > multiout . dig_out_nid ) ;
if ( err < 0 )
return err ;
}
if ( spec - > dig_in_nid ) {
err = snd_hda_create_spdif_in_ctls ( codec , spec - > dig_in_nid ) ;
if ( err < 0 )
return err ;
}
return 0 ;
}
2005-04-08 15:05:13 +02:00
/* fill in the multi_dac_nids table, which will decide
which audio widget to use for each channel */
2005-06-13 14:16:38 +02:00
static int cmi9880_fill_multi_dac_nids ( struct hda_codec * codec , const struct auto_pin_cfg * cfg )
2005-04-08 15:05:13 +02:00
{
struct cmi_spec * spec = codec - > spec ;
hda_nid_t nid ;
int assigned [ 4 ] ;
int i , j ;
/* clear the table, only one c-media dac assumed here */
memset ( spec - > dac_nids , 0 , sizeof ( spec - > dac_nids ) ) ;
memset ( assigned , 0 , sizeof ( assigned ) ) ;
/* check the pins we found */
2005-06-13 14:16:38 +02:00
for ( i = 0 ; i < cfg - > line_outs ; i + + ) {
nid = cfg - > line_out_pins [ i ] ;
2005-04-08 15:05:13 +02:00
/* nid 0x0b~0x0e is hardwired to audio widget 0x3~0x6 */
2005-06-13 14:16:38 +02:00
if ( nid > = 0x0b & & nid < = 0x0e ) {
spec - > dac_nids [ i ] = ( nid - 0x0b ) + 0x03 ;
2005-04-08 15:05:13 +02:00
assigned [ nid - 0x0b ] = 1 ;
}
}
/* left pin can be connect to any audio widget */
2005-06-13 14:16:38 +02:00
for ( i = 0 ; i < cfg - > line_outs ; i + + ) {
nid = cfg - > line_out_pins [ i ] ;
if ( nid < = 0x0e )
continue ;
/* search for an empty channel */
for ( j = 0 ; j < cfg - > line_outs ; j + + ) {
if ( ! assigned [ j ] ) {
2005-07-27 17:25:08 +02:00
spec - > dac_nids [ i ] = j + 0x03 ;
2005-06-13 14:16:38 +02:00
assigned [ j ] = 1 ;
break ;
}
2005-04-08 15:05:13 +02:00
}
}
2005-06-13 14:16:38 +02:00
spec - > num_dacs = cfg - > line_outs ;
2005-04-08 15:05:13 +02:00
return 0 ;
}
/* create multi_init table, which is used for multichannel initialization */
2005-06-13 14:16:38 +02:00
static int cmi9880_fill_multi_init ( struct hda_codec * codec , const struct auto_pin_cfg * cfg )
2005-04-08 15:05:13 +02:00
{
struct cmi_spec * spec = codec - > spec ;
hda_nid_t nid ;
int i , j , k , len ;
/* clear the table, only one c-media dac assumed here */
memset ( spec - > multi_init , 0 , sizeof ( spec - > multi_init ) ) ;
2005-06-13 14:16:38 +02:00
for ( j = 0 , i = 0 ; i < cfg - > line_outs ; i + + ) {
2005-04-08 15:05:13 +02:00
hda_nid_t conn [ 4 ] ;
2005-06-13 14:16:38 +02:00
nid = cfg - > line_out_pins [ i ] ;
2005-04-08 15:05:13 +02:00
/* set as output */
spec - > multi_init [ j ] . nid = nid ;
spec - > multi_init [ j ] . verb = AC_VERB_SET_PIN_WIDGET_CONTROL ;
2005-06-13 14:16:38 +02:00
spec - > multi_init [ j ] . param = PIN_OUT ;
2005-04-08 15:05:13 +02:00
j + + ;
2005-06-13 14:16:38 +02:00
if ( nid > 0x0e ) {
2005-04-08 15:05:13 +02:00
/* set connection */
spec - > multi_init [ j ] . nid = nid ;
spec - > multi_init [ j ] . verb = AC_VERB_SET_CONNECT_SEL ;
2005-06-13 14:16:38 +02:00
spec - > multi_init [ j ] . param = 0 ;
2005-04-08 15:05:13 +02:00
/* find the index in connect list */
len = snd_hda_get_connections ( codec , nid , conn , 4 ) ;
for ( k = 0 ; k < len ; k + + )
2005-06-13 14:16:38 +02:00
if ( conn [ k ] = = spec - > dac_nids [ i ] ) {
2005-07-27 17:25:08 +02:00
spec - > multi_init [ j ] . param = k ;
2005-04-08 15:05:13 +02:00
break ;
2005-06-13 14:16:38 +02:00
}
2005-04-08 15:05:13 +02:00
j + + ;
}
}
return 0 ;
}
2005-04-16 15:20:36 -07:00
static int cmi9880_init ( struct hda_codec * codec )
{
struct cmi_spec * spec = codec - > spec ;
if ( spec - > board_config = = CMI_ALLOUT )
snd_hda_sequence_write ( codec , cmi9880_allout_init ) ;
else
snd_hda_sequence_write ( codec , cmi9880_basic_init ) ;
2005-04-08 15:05:13 +02:00
if ( spec - > board_config = = CMI_AUTO )
snd_hda_sequence_write ( codec , spec - > multi_init ) ;
2005-04-16 15:20:36 -07:00
return 0 ;
}
# ifdef CONFIG_PM
/*
* resume
*/
static int cmi9880_resume ( struct hda_codec * codec )
{
struct cmi_spec * spec = codec - > spec ;
cmi9880_init ( codec ) ;
snd_hda_resume_ctls ( codec , cmi9880_basic_mixer ) ;
if ( spec - > surr_switch )
snd_hda_resume_ctls ( codec , cmi9880_ch_mode_mixer ) ;
if ( spec - > multiout . dig_out_nid )
snd_hda_resume_spdif_out ( codec ) ;
if ( spec - > dig_in_nid )
snd_hda_resume_spdif_in ( codec ) ;
return 0 ;
}
# endif
/*
* Analog playback callbacks
*/
static int cmi9880_playback_pcm_open ( struct hda_pcm_stream * hinfo ,
struct hda_codec * codec ,
snd_pcm_substream_t * substream )
{
struct cmi_spec * spec = codec - > spec ;
return snd_hda_multi_out_analog_open ( codec , & spec - > multiout , substream ) ;
}
static int cmi9880_playback_pcm_prepare ( struct hda_pcm_stream * hinfo ,
struct hda_codec * codec ,
unsigned int stream_tag ,
unsigned int format ,
snd_pcm_substream_t * substream )
{
struct cmi_spec * spec = codec - > spec ;
return snd_hda_multi_out_analog_prepare ( codec , & spec - > multiout , stream_tag ,
format , substream ) ;
}
static int cmi9880_playback_pcm_cleanup ( struct hda_pcm_stream * hinfo ,
struct hda_codec * codec ,
snd_pcm_substream_t * substream )
{
struct cmi_spec * spec = codec - > spec ;
return snd_hda_multi_out_analog_cleanup ( codec , & spec - > multiout ) ;
}
/*
* Digital out
*/
static int cmi9880_dig_playback_pcm_open ( struct hda_pcm_stream * hinfo ,
struct hda_codec * codec ,
snd_pcm_substream_t * substream )
{
struct cmi_spec * spec = codec - > spec ;
return snd_hda_multi_out_dig_open ( codec , & spec - > multiout ) ;
}
static int cmi9880_dig_playback_pcm_close ( struct hda_pcm_stream * hinfo ,
struct hda_codec * codec ,
snd_pcm_substream_t * substream )
{
struct cmi_spec * spec = codec - > spec ;
return snd_hda_multi_out_dig_close ( codec , & spec - > multiout ) ;
}
/*
* Analog capture
*/
static int cmi9880_capture_pcm_prepare ( struct hda_pcm_stream * hinfo ,
struct hda_codec * codec ,
unsigned int stream_tag ,
unsigned int format ,
snd_pcm_substream_t * substream )
{
struct cmi_spec * spec = codec - > spec ;
snd_hda_codec_setup_stream ( codec , spec - > adc_nids [ substream - > number ] ,
stream_tag , 0 , format ) ;
return 0 ;
}
static int cmi9880_capture_pcm_cleanup ( struct hda_pcm_stream * hinfo ,
struct hda_codec * codec ,
snd_pcm_substream_t * substream )
{
struct cmi_spec * spec = codec - > spec ;
snd_hda_codec_setup_stream ( codec , spec - > adc_nids [ substream - > number ] , 0 , 0 , 0 ) ;
return 0 ;
}
/*
*/
static struct hda_pcm_stream cmi9880_pcm_analog_playback = {
. substreams = 1 ,
. channels_min = 2 ,
. channels_max = 8 ,
. nid = 0x03 , /* NID to query formats and rates */
. ops = {
. open = cmi9880_playback_pcm_open ,
. prepare = cmi9880_playback_pcm_prepare ,
. cleanup = cmi9880_playback_pcm_cleanup
} ,
} ;
static struct hda_pcm_stream cmi9880_pcm_analog_capture = {
. substreams = 2 ,
. channels_min = 2 ,
. channels_max = 2 ,
. nid = 0x08 , /* NID to query formats and rates */
. ops = {
. prepare = cmi9880_capture_pcm_prepare ,
. cleanup = cmi9880_capture_pcm_cleanup
} ,
} ;
static struct hda_pcm_stream cmi9880_pcm_digital_playback = {
. substreams = 1 ,
. channels_min = 2 ,
. channels_max = 2 ,
/* NID is set in cmi9880_build_pcms */
. ops = {
. open = cmi9880_dig_playback_pcm_open ,
. close = cmi9880_dig_playback_pcm_close
} ,
} ;
static struct hda_pcm_stream cmi9880_pcm_digital_capture = {
. substreams = 1 ,
. channels_min = 2 ,
. channels_max = 2 ,
/* NID is set in cmi9880_build_pcms */
} ;
static int cmi9880_build_pcms ( struct hda_codec * codec )
{
struct cmi_spec * spec = codec - > spec ;
struct hda_pcm * info = spec - > pcm_rec ;
codec - > num_pcms = 1 ;
codec - > pcm_info = info ;
info - > name = " CMI9880 " ;
info - > stream [ SNDRV_PCM_STREAM_PLAYBACK ] = cmi9880_pcm_analog_playback ;
info - > stream [ SNDRV_PCM_STREAM_CAPTURE ] = cmi9880_pcm_analog_capture ;
if ( spec - > multiout . dig_out_nid | | spec - > dig_in_nid ) {
codec - > num_pcms + + ;
info + + ;
info - > name = " CMI9880 Digital " ;
if ( spec - > multiout . dig_out_nid ) {
info - > stream [ SNDRV_PCM_STREAM_PLAYBACK ] = cmi9880_pcm_digital_playback ;
info - > stream [ SNDRV_PCM_STREAM_PLAYBACK ] . nid = spec - > multiout . dig_out_nid ;
}
if ( spec - > dig_in_nid ) {
info - > stream [ SNDRV_PCM_STREAM_CAPTURE ] = cmi9880_pcm_digital_capture ;
info - > stream [ SNDRV_PCM_STREAM_CAPTURE ] . nid = spec - > dig_in_nid ;
}
}
return 0 ;
}
static void cmi9880_free ( struct hda_codec * codec )
{
kfree ( codec - > spec ) ;
}
/*
*/
static struct hda_board_config cmi9880_cfg_tbl [ ] = {
{ . modelname = " minimal " , . config = CMI_MINIMAL } ,
{ . modelname = " min_fp " , . config = CMI_MIN_FP } ,
{ . modelname = " full " , . config = CMI_FULL } ,
{ . modelname = " full_dig " , . config = CMI_FULL_DIG } ,
2005-07-29 11:56:41 +02:00
{ . pci_subvendor = 0x1043 , . pci_subdevice = 0x813d , . config = CMI_FULL_DIG } , /* ASUS P5AD2 */
2005-04-16 15:20:36 -07:00
{ . modelname = " allout " , . config = CMI_ALLOUT } ,
2005-04-08 15:05:13 +02:00
{ . modelname = " auto " , . config = CMI_AUTO } ,
2005-04-16 15:20:36 -07:00
{ } /* terminator */
} ;
static struct hda_codec_ops cmi9880_patch_ops = {
. build_controls = cmi9880_build_controls ,
. build_pcms = cmi9880_build_pcms ,
. init = cmi9880_init ,
. free = cmi9880_free ,
# ifdef CONFIG_PM
. resume = cmi9880_resume ,
# endif
} ;
static int patch_cmi9880 ( struct hda_codec * codec )
{
struct cmi_spec * spec ;
[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 14:21:46 +02:00
spec = kzalloc ( sizeof ( * spec ) , GFP_KERNEL ) ;
2005-04-16 15:20:36 -07:00
if ( spec = = NULL )
return - ENOMEM ;
codec - > spec = spec ;
spec - > board_config = snd_hda_check_board_config ( codec , cmi9880_cfg_tbl ) ;
if ( spec - > board_config < 0 ) {
2005-04-08 15:05:13 +02:00
snd_printdd ( KERN_INFO " hda_codec: Unknown model for CMI9880 \n " ) ;
spec - > board_config = CMI_AUTO ; /* try everything */
2005-04-16 15:20:36 -07:00
}
2005-04-08 15:05:13 +02:00
/* copy default DAC NIDs */
memcpy ( spec - > dac_nids , cmi9880_dac_nids , sizeof ( spec - > dac_nids ) ) ;
2005-06-13 14:16:38 +02:00
spec - > num_dacs = 4 ;
2005-04-08 15:05:13 +02:00
2005-04-16 15:20:36 -07:00
switch ( spec - > board_config ) {
case CMI_MINIMAL :
case CMI_MIN_FP :
spec - > surr_switch = 1 ;
if ( spec - > board_config = = CMI_MINIMAL )
spec - > num_ch_modes = 2 ;
else {
spec - > front_panel = 1 ;
spec - > num_ch_modes = 3 ;
}
spec - > channel_modes = cmi9880_channel_modes ;
spec - > multiout . max_channels = cmi9880_channel_modes [ 0 ] . channels ;
spec - > input_mux = & cmi9880_basic_mux ;
break ;
case CMI_FULL :
case CMI_FULL_DIG :
spec - > front_panel = 1 ;
spec - > multiout . max_channels = 8 ;
spec - > input_mux = & cmi9880_basic_mux ;
if ( spec - > board_config = = CMI_FULL_DIG ) {
spec - > multiout . dig_out_nid = CMI_DIG_OUT_NID ;
spec - > dig_in_nid = CMI_DIG_IN_NID ;
}
break ;
case CMI_ALLOUT :
spec - > front_panel = 1 ;
spec - > multiout . max_channels = 8 ;
spec - > no_line_in = 1 ;
spec - > input_mux = & cmi9880_no_line_mux ;
spec - > multiout . dig_out_nid = CMI_DIG_OUT_NID ;
break ;
2005-04-08 15:05:13 +02:00
case CMI_AUTO :
{
unsigned int port_e , port_f , port_g , port_h ;
unsigned int port_spdifi , port_spdifo ;
2005-06-13 14:16:38 +02:00
struct auto_pin_cfg cfg ;
2005-04-08 15:05:13 +02:00
/* collect pin default configuration */
2005-06-13 14:16:38 +02:00
port_e = snd_hda_codec_read ( codec , 0x0f , 0 , AC_VERB_GET_CONFIG_DEFAULT , 0 ) ;
port_f = snd_hda_codec_read ( codec , 0x10 , 0 , AC_VERB_GET_CONFIG_DEFAULT , 0 ) ;
2005-04-08 15:05:13 +02:00
spec - > front_panel = 1 ;
2005-04-20 13:45:55 +02:00
if ( get_defcfg_connect ( port_e ) = = AC_JACK_PORT_NONE | |
get_defcfg_connect ( port_f ) = = AC_JACK_PORT_NONE ) {
2005-06-13 14:16:38 +02:00
port_g = snd_hda_codec_read ( codec , 0x1f , 0 , AC_VERB_GET_CONFIG_DEFAULT , 0 ) ;
port_h = snd_hda_codec_read ( codec , 0x20 , 0 , AC_VERB_GET_CONFIG_DEFAULT , 0 ) ;
2005-04-08 15:05:13 +02:00
spec - > surr_switch = 1 ;
/* no front panel */
2005-04-20 13:45:55 +02:00
if ( get_defcfg_connect ( port_g ) = = AC_JACK_PORT_NONE | |
get_defcfg_connect ( port_h ) = = AC_JACK_PORT_NONE ) {
2005-04-08 15:05:13 +02:00
/* no optional rear panel */
spec - > board_config = CMI_MINIMAL ;
spec - > front_panel = 0 ;
spec - > num_ch_modes = 2 ;
2005-04-20 13:45:55 +02:00
} else {
2005-04-08 15:05:13 +02:00
spec - > board_config = CMI_MIN_FP ;
spec - > num_ch_modes = 3 ;
2005-04-20 13:45:55 +02:00
}
2005-04-08 15:05:13 +02:00
spec - > channel_modes = cmi9880_channel_modes ;
spec - > input_mux = & cmi9880_basic_mux ;
2005-04-20 13:45:55 +02:00
spec - > multiout . max_channels = cmi9880_channel_modes [ 0 ] . channels ;
2005-04-08 15:05:13 +02:00
} else {
spec - > input_mux = & cmi9880_basic_mux ;
2005-06-13 14:16:38 +02:00
port_spdifi = snd_hda_codec_read ( codec , 0x13 , 0 , AC_VERB_GET_CONFIG_DEFAULT , 0 ) ;
port_spdifo = snd_hda_codec_read ( codec , 0x12 , 0 , AC_VERB_GET_CONFIG_DEFAULT , 0 ) ;
2005-04-20 13:45:55 +02:00
if ( get_defcfg_connect ( port_spdifo ) ! = AC_JACK_PORT_NONE )
2005-04-08 15:05:13 +02:00
spec - > multiout . dig_out_nid = CMI_DIG_OUT_NID ;
2005-04-20 13:45:55 +02:00
if ( get_defcfg_connect ( port_spdifi ) ! = AC_JACK_PORT_NONE )
2005-04-08 15:05:13 +02:00
spec - > dig_in_nid = CMI_DIG_IN_NID ;
2005-04-20 13:45:55 +02:00
spec - > multiout . max_channels = 8 ;
2005-04-08 15:05:13 +02:00
}
2005-06-13 14:16:38 +02:00
snd_hda_parse_pin_def_config ( codec , & cfg ) ;
if ( cfg . line_outs ) {
spec - > multiout . max_channels = cfg . line_outs * 2 ;
cmi9880_fill_multi_dac_nids ( codec , & cfg ) ;
cmi9880_fill_multi_init ( codec , & cfg ) ;
2005-04-20 13:45:55 +02:00
} else
snd_printd ( " patch_cmedia: cannot detect association in defcfg \n " ) ;
2005-04-08 15:05:13 +02:00
break ;
2005-04-20 13:45:55 +02:00
}
2005-04-16 15:20:36 -07:00
}
2005-06-13 14:16:38 +02:00
spec - > multiout . num_dacs = spec - > num_dacs ;
2005-04-08 15:05:13 +02:00
spec - > multiout . dac_nids = spec - > dac_nids ;
2005-04-16 15:20:36 -07:00
spec - > adc_nids = cmi9880_adc_nids ;
codec - > patch_ops = cmi9880_patch_ops ;
return 0 ;
}
/*
* patch entries
*/
struct hda_codec_preset snd_hda_preset_cmedia [ ] = {
{ . id = 0x13f69880 , . name = " CMI9880 " , . patch = patch_cmi9880 } ,
{ . id = 0x434d4980 , . name = " CMI9880 " , . patch = patch_cmi9880 } ,
{ } /* terminator */
} ;