2009-07-01 20:11:44 +04:00
/*
* HD audio interface patch for Cirrus Logic CS420x chip
*
* Copyright ( c ) 2009 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/slab.h>
# include <linux/pci.h>
2011-07-15 20:38:28 +04:00
# include <linux/module.h>
2009-07-01 20:11:44 +04:00
# include <sound/core.h>
2012-12-19 19:39:18 +04:00
# include <sound/tlv.h>
2009-07-01 20:11:44 +04:00
# include "hda_codec.h"
# include "hda_local.h"
2012-05-07 19:42:31 +04:00
# include "hda_auto_parser.h"
2011-10-28 00:12:46 +04:00
# include "hda_jack.h"
2012-12-19 19:39:18 +04:00
# include "hda_generic.h"
2009-07-01 20:11:44 +04:00
/*
*/
struct cs_spec {
2012-12-19 19:39:18 +04:00
struct hda_gen_spec gen ;
2009-07-01 20:11:44 +04:00
2009-07-07 11:04:26 +04:00
unsigned int gpio_mask ;
unsigned int gpio_dir ;
unsigned int gpio_data ;
2011-11-22 23:00:31 +04:00
unsigned int gpio_eapd_hp ; /* EAPD GPIO bit for headphones */
unsigned int gpio_eapd_speaker ; /* EAPD GPIO bit for speakers */
2009-07-07 11:04:26 +04:00
2011-07-23 01:41:00 +04:00
/* CS421x */
unsigned int spdif_detect : 1 ;
2012-12-19 19:39:18 +04:00
unsigned int spdif_present : 1 ;
2011-07-23 01:41:00 +04:00
unsigned int sense_b : 1 ;
hda_nid_t vendor_nid ;
2013-11-08 16:03:57 +04:00
/* for MBP SPDIF control */
int ( * spdif_sw_put ) ( struct snd_kcontrol * kcontrol ,
struct snd_ctl_elem_value * ucontrol ) ;
2009-07-01 20:11:44 +04:00
} ;
2011-07-23 01:41:00 +04:00
/* available models with CS420x */
2009-07-06 17:15:22 +04:00
enum {
2010-09-23 01:31:37 +04:00
CS420X_MBP53 ,
2009-07-06 17:15:22 +04:00
CS420X_MBP55 ,
2009-12-22 09:59:37 +03:00
CS420X_IMAC27 ,
2012-08-24 21:09:45 +04:00
CS420X_GPIO_13 ,
CS420X_GPIO_23 ,
2012-09-11 18:53:08 +04:00
CS420X_MBP101 ,
2012-11-23 11:34:13 +04:00
CS420X_MBP81 ,
2013-06-17 12:19:49 +04:00
CS420X_MBA42 ,
2009-07-06 17:15:22 +04:00
CS420X_AUTO ,
2012-09-13 11:56:57 +04:00
/* aliases */
CS420X_IMAC27_122 = CS420X_GPIO_23 ,
CS420X_APPLE = CS420X_GPIO_13 ,
2009-07-06 17:15:22 +04:00
} ;
2011-07-23 01:41:00 +04:00
/* CS421x boards */
enum {
CS421X_CDB4210 ,
2012-08-24 21:09:45 +04:00
CS421X_SENSE_B ,
2013-04-05 02:35:31 +04:00
CS421X_STUMPY ,
2011-07-23 01:41:00 +04:00
} ;
2009-07-06 15:00:57 +04:00
/* Vendor-specific processing widget */
# define CS420X_VENDOR_NID 0x11
# define CS_DIG_OUT1_PIN_NID 0x10
# define CS_DIG_OUT2_PIN_NID 0x15
2012-11-04 09:19:03 +04:00
# define CS_DMIC1_PIN_NID 0x0e
# define CS_DMIC2_PIN_NID 0x12
2009-07-06 15:00:57 +04:00
/* coef indices */
# define IDX_SPDIF_STAT 0x0000
# define IDX_SPDIF_CTL 0x0001
# define IDX_ADC_CFG 0x0002
/* SZC bitmask, 4 modes below:
* 0 = immediate ,
* 1 = digital immediate , analog zero - cross
* 2 = digtail & analog soft - ramp
* 3 = digital soft - ramp , analog zero - cross
*/
# define CS_COEF_ADC_SZC_MASK (3 << 0)
# define CS_COEF_ADC_MIC_SZC_MODE (3 << 0) /* SZC setup for mic */
# define CS_COEF_ADC_LI_SZC_MODE (3 << 0) /* SZC setup for line-in */
/* PGA mode: 0 = differential, 1 = signle-ended */
# define CS_COEF_ADC_MIC_PGA_MODE (1 << 5) /* PGA setup for mic */
# define CS_COEF_ADC_LI_PGA_MODE (1 << 6) /* PGA setup for line-in */
# define IDX_DAC_CFG 0x0003
/* SZC bitmask, 4 modes below:
* 0 = Immediate
* 1 = zero - cross
* 2 = soft - ramp
* 3 = soft - ramp on zero - cross
*/
# define CS_COEF_DAC_HP_SZC_MODE (3 << 0) /* nid 0x02 */
# define CS_COEF_DAC_LO_SZC_MODE (3 << 2) /* nid 0x03 */
# define CS_COEF_DAC_SPK_SZC_MODE (3 << 4) /* nid 0x04 */
# define IDX_BEEP_CFG 0x0004
/* 0x0008 - test reg key */
/* 0x0009 - 0x0014 -> 12 test regs */
/* 0x0015 - visibility reg */
2013-09-27 12:13:51 +04:00
/* Cirrus Logic CS4208 */
# define CS4208_VENDOR_NID 0x24
2011-07-23 01:41:00 +04:00
/*
* Cirrus Logic CS4210
*
* 1 DAC = > HP ( sense ) / Speakers ,
* 1 ADC < = LineIn ( sense ) / MicIn / DMicIn ,
* 1 SPDIF OUT = > SPDIF Trasmitter ( sense )
*/
# define CS4210_DAC_NID 0x02
# define CS4210_ADC_NID 0x03
2012-01-02 15:40:17 +04:00
# define CS4210_VENDOR_NID 0x0B
2011-07-23 01:41:00 +04:00
# define CS421X_DMIC_PIN_NID 0x09 /* Port E */
# define CS421X_SPDIF_PIN_NID 0x0A /* Port H */
2012-12-19 19:39:18 +04:00
# define CS421X_IDX_DEV_CFG 0x01
# define CS421X_IDX_ADC_CFG 0x02
# define CS421X_IDX_DAC_CFG 0x03
# define CS421X_IDX_SPK_CTL 0x04
2009-07-01 20:11:44 +04:00
2012-12-19 19:39:18 +04:00
# define SPDIF_EVENT 0x04
2010-10-11 17:16:20 +04:00
2012-12-19 19:39:18 +04:00
/* Cirrus Logic CS4213 is like CS4210 but does not have SPDIF input/output */
# define CS4213_VENDOR_NID 0x09
2009-07-01 20:11:44 +04:00
2009-07-06 14:55:46 +04:00
2012-12-19 19:39:18 +04:00
static inline int cs_vendor_coef_get ( struct hda_codec * codec , unsigned int idx )
2009-07-01 20:11:44 +04:00
{
struct cs_spec * spec = codec - > spec ;
2012-12-19 19:39:18 +04:00
snd_hda_codec_write ( codec , spec - > vendor_nid , 0 ,
AC_VERB_SET_COEF_INDEX , idx ) ;
return snd_hda_codec_read ( codec , spec - > vendor_nid , 0 ,
AC_VERB_GET_PROC_COEF , 0 ) ;
2009-07-01 20:11:44 +04:00
}
2012-12-19 19:39:18 +04:00
static inline void cs_vendor_coef_set ( struct hda_codec * codec , unsigned int idx ,
unsigned int coef )
2009-07-01 20:11:44 +04:00
{
struct cs_spec * spec = codec - > spec ;
2012-12-19 19:39:18 +04:00
snd_hda_codec_write ( codec , spec - > vendor_nid , 0 ,
AC_VERB_SET_COEF_INDEX , idx ) ;
snd_hda_codec_write ( codec , spec - > vendor_nid , 0 ,
AC_VERB_SET_PROC_COEF , coef ) ;
2009-07-01 20:11:44 +04:00
}
2009-07-06 14:55:46 +04:00
/*
* auto - mute and auto - mic switching
2011-07-23 01:41:00 +04:00
* CS421x auto - output redirecting
* HP / SPK / SPDIF
2009-07-06 14:55:46 +04:00
*/
2012-12-19 19:39:18 +04:00
static void cs_automute ( struct hda_codec * codec )
2009-07-01 20:11:44 +04:00
{
struct cs_spec * spec = codec - > spec ;
2012-12-19 19:39:18 +04:00
/* mute HPs if spdif jack (SENSE_B) is present */
spec - > gen . master_mute = ! ! ( spec - > spdif_present & & spec - > sense_b ) ;
2011-07-23 01:41:00 +04:00
2012-12-19 19:39:18 +04:00
snd_hda_gen_update_outputs ( codec ) ;
2012-01-02 15:40:16 +04:00
2013-09-09 15:57:57 +04:00
if ( spec - > gpio_eapd_hp | | spec - > gpio_eapd_speaker ) {
2013-03-18 19:55:49 +04:00
spec - > gpio_data = spec - > gen . hp_jack_present ?
2011-11-22 23:00:31 +04:00
spec - > gpio_eapd_hp : spec - > gpio_eapd_speaker ;
2009-07-30 16:44:27 +04:00
snd_hda_codec_write ( codec , 0x01 , 0 ,
2013-03-18 19:55:49 +04:00
AC_VERB_SET_GPIO_DATA , spec - > gpio_data ) ;
2009-07-30 16:44:27 +04:00
}
2009-07-01 20:11:44 +04:00
}
2012-12-19 19:39:18 +04:00
static bool is_active_pin ( struct hda_codec * codec , hda_nid_t nid )
2009-07-01 20:11:44 +04:00
{
2012-12-19 19:39:18 +04:00
unsigned int val ;
val = snd_hda_codec_get_pincfg ( codec , nid ) ;
return ( get_defcfg_connect ( val ) ! = AC_JACK_PORT_NONE ) ;
2009-07-01 20:11:44 +04:00
}
2012-12-19 19:39:18 +04:00
static void init_input_coef ( struct hda_codec * codec )
2009-07-01 20:11:44 +04:00
{
struct cs_spec * spec = codec - > spec ;
2009-07-06 15:00:57 +04:00
unsigned int coef ;
2009-07-01 20:11:44 +04:00
2012-01-02 15:40:17 +04:00
/* CS420x has multiple ADC, CS421x has single ADC */
if ( spec - > vendor_nid = = CS420X_VENDOR_NID ) {
2012-11-04 09:19:03 +04:00
coef = cs_vendor_coef_get ( codec , IDX_BEEP_CFG ) ;
2011-07-23 01:41:00 +04:00
if ( is_active_pin ( codec , CS_DMIC2_PIN_NID ) )
2012-11-04 09:19:03 +04:00
coef | = 1 < < 4 ; /* DMIC2 2 chan on, GPIO1 off */
2011-07-23 01:41:00 +04:00
if ( is_active_pin ( codec , CS_DMIC1_PIN_NID ) )
2012-11-04 09:19:03 +04:00
coef | = 1 < < 3 ; /* DMIC1 2 chan on, GPIO0 off
2011-07-23 01:41:00 +04:00
* No effect if SPDIF_OUT2 is
* selected in IDX_SPDIF_CTL .
*/
2012-11-04 09:19:03 +04:00
cs_vendor_coef_set ( codec , IDX_BEEP_CFG , coef ) ;
2011-07-23 01:41:00 +04:00
}
2009-07-06 15:00:57 +04:00
}
2011-05-02 13:36:09 +04:00
static const struct hda_verb cs_coef_init_verbs [ ] = {
2009-07-06 15:00:57 +04:00
{ 0x11 , AC_VERB_SET_PROC_STATE , 1 } ,
{ 0x11 , AC_VERB_SET_COEF_INDEX , IDX_DAC_CFG } ,
{ 0x11 , AC_VERB_SET_PROC_COEF ,
( 0x002a /* DAC1/2/3 SZCMode Soft Ramp */
| 0x0040 /* Mute DACs on FIFO error */
| 0x1000 /* Enable DACs High Pass Filter */
| 0x0400 /* Disable Coefficient Auto increment */
) } ,
2012-11-23 11:25:25 +04:00
/* ADC1/2 - Digital and Analog Soft Ramp */
{ 0x11 , AC_VERB_SET_COEF_INDEX , IDX_ADC_CFG } ,
{ 0x11 , AC_VERB_SET_PROC_COEF , 0x000a } ,
2009-07-06 15:00:57 +04:00
/* Beep */
2012-11-01 16:42:37 +04:00
{ 0x11 , AC_VERB_SET_COEF_INDEX , IDX_BEEP_CFG } ,
2009-07-06 15:00:57 +04:00
{ 0x11 , AC_VERB_SET_PROC_COEF , 0x0007 } , /* Enable Beep thru DAC1/2/3 */
{ } /* terminator */
} ;
2013-09-27 12:13:51 +04:00
static const struct hda_verb cs4208_coef_init_verbs [ ] = {
{ 0x01 , AC_VERB_SET_POWER_STATE , 0x00 } , /* AFG: D0 */
{ 0x24 , AC_VERB_SET_PROC_STATE , 0x01 } , /* VPW: processing on */
{ 0x24 , AC_VERB_SET_COEF_INDEX , 0x0033 } ,
{ 0x24 , AC_VERB_SET_PROC_COEF , 0x0001 } , /* A1 ICS */
{ 0x24 , AC_VERB_SET_COEF_INDEX , 0x0034 } ,
{ 0x24 , AC_VERB_SET_PROC_COEF , 0x1C01 } , /* A1 Enable, A Thresh = 300mV */
{ } /* terminator */
} ;
2010-09-07 23:36:22 +04:00
/* Errata: CS4207 rev C0/C1/C2 Silicon
*
* http : //www.cirrus.com/en/pubs/errata/ER880C3.pdf
*
* 6. At high temperature ( TA > + 85 ° C ) , the digital supply current ( IVD )
* may be excessive ( up to an additional 200 μ A ) , which is most easily
* observed while the part is being held in reset ( RESET # active low ) .
*
* Root Cause : At initial powerup of the device , the logic that drives
* the clock and write enable to the S / PDIF SRC RAMs is not properly
* initialized .
* Certain random patterns will cause a steady leakage current in those
* RAM cells . The issue will resolve once the SRCs are used ( turned on ) .
*
* Workaround : The following verb sequence briefly turns on the S / PDIF SRC
* blocks , which will alleviate the issue .
*/
2011-05-02 13:36:09 +04:00
static const struct hda_verb cs_errata_init_verbs [ ] = {
2010-09-07 23:36:22 +04:00
{ 0x01 , AC_VERB_SET_POWER_STATE , 0x00 } , /* AFG: D0 */
{ 0x11 , AC_VERB_SET_PROC_STATE , 0x01 } , /* VPW: processing on */
{ 0x11 , AC_VERB_SET_COEF_INDEX , 0x0008 } ,
{ 0x11 , AC_VERB_SET_PROC_COEF , 0x9999 } ,
{ 0x11 , AC_VERB_SET_COEF_INDEX , 0x0017 } ,
{ 0x11 , AC_VERB_SET_PROC_COEF , 0xa412 } ,
{ 0x11 , AC_VERB_SET_COEF_INDEX , 0x0001 } ,
{ 0x11 , AC_VERB_SET_PROC_COEF , 0x0009 } ,
{ 0x07 , AC_VERB_SET_POWER_STATE , 0x00 } , /* S/PDIF Rx: D0 */
{ 0x08 , AC_VERB_SET_POWER_STATE , 0x00 } , /* S/PDIF Tx: D0 */
{ 0x11 , AC_VERB_SET_COEF_INDEX , 0x0017 } ,
{ 0x11 , AC_VERB_SET_PROC_COEF , 0x2412 } ,
{ 0x11 , AC_VERB_SET_COEF_INDEX , 0x0008 } ,
{ 0x11 , AC_VERB_SET_PROC_COEF , 0x0000 } ,
{ 0x11 , AC_VERB_SET_COEF_INDEX , 0x0001 } ,
{ 0x11 , AC_VERB_SET_PROC_COEF , 0x0008 } ,
{ 0x11 , AC_VERB_SET_PROC_STATE , 0x00 } ,
2011-03-03 16:54:19 +03:00
#if 0 /* Don't to set to D3 as we are in power-up sequence */
2010-09-07 23:36:22 +04:00
{ 0x07 , AC_VERB_SET_POWER_STATE , 0x03 } , /* S/PDIF Rx: D3 */
{ 0x08 , AC_VERB_SET_POWER_STATE , 0x03 } , /* S/PDIF Tx: D3 */
/*{0x01, AC_VERB_SET_POWER_STATE, 0x03},*/ /* AFG: D3 This is already handled */
2011-03-03 16:54:19 +03:00
# endif
2010-09-07 23:36:22 +04:00
{ } /* terminator */
} ;
2009-07-06 15:00:57 +04:00
/* SPDIF setup */
2012-12-19 19:39:18 +04:00
static void init_digital_coef ( struct hda_codec * codec )
2009-07-06 15:00:57 +04:00
{
unsigned int coef ;
coef = 0x0002 ; /* SRC_MUTE soft-mute on SPDIF (if no lock) */
coef | = 0x0008 ; /* Replace with mute on error */
if ( is_active_pin ( codec , CS_DIG_OUT2_PIN_NID ) )
coef | = 0x4000 ; /* RX to TX1 or TX2 Loopthru / SPDIF2
* SPDIF_OUT2 is shared with GPIO1 and
* DMIC_SDA2 .
*/
cs_vendor_coef_set ( codec , IDX_SPDIF_CTL , coef ) ;
2009-07-01 20:11:44 +04:00
}
static int cs_init ( struct hda_codec * codec )
{
struct cs_spec * spec = codec - > spec ;
2013-09-09 15:57:57 +04:00
if ( spec - > vendor_nid = = CS420X_VENDOR_NID ) {
/* init_verb sequence for C0/C1/C2 errata*/
snd_hda_sequence_write ( codec , cs_errata_init_verbs ) ;
snd_hda_sequence_write ( codec , cs_coef_init_verbs ) ;
2013-09-27 12:13:51 +04:00
} else if ( spec - > vendor_nid = = CS4208_VENDOR_NID ) {
snd_hda_sequence_write ( codec , cs4208_coef_init_verbs ) ;
2013-09-09 15:57:57 +04:00
}
2009-07-07 11:04:26 +04:00
2012-12-19 19:39:18 +04:00
snd_hda_gen_init ( codec ) ;
2012-11-23 11:26:58 +04:00
2009-07-07 11:04:26 +04:00
if ( spec - > gpio_mask ) {
snd_hda_codec_write ( codec , 0x01 , 0 , AC_VERB_SET_GPIO_MASK ,
spec - > gpio_mask ) ;
snd_hda_codec_write ( codec , 0x01 , 0 , AC_VERB_SET_GPIO_DIRECTION ,
spec - > gpio_dir ) ;
snd_hda_codec_write ( codec , 0x01 , 0 , AC_VERB_SET_GPIO_DATA ,
spec - > gpio_data ) ;
}
2013-09-09 15:57:57 +04:00
if ( spec - > vendor_nid = = CS420X_VENDOR_NID ) {
init_input_coef ( codec ) ;
init_digital_coef ( codec ) ;
}
2011-10-28 02:03:22 +04:00
return 0 ;
2009-07-01 20:11:44 +04:00
}
2013-11-08 16:03:57 +04:00
static int cs_build_controls ( struct hda_codec * codec )
{
int err ;
err = snd_hda_gen_build_controls ( codec ) ;
if ( err < 0 )
return err ;
snd_hda_apply_fixup ( codec , HDA_FIXUP_ACT_BUILD ) ;
return 0 ;
}
2012-12-19 19:39:18 +04:00
# define cs_free snd_hda_gen_free
2009-07-01 20:11:44 +04:00
2011-05-02 13:36:09 +04:00
static const struct hda_codec_ops cs_patch_ops = {
2013-11-08 16:03:57 +04:00
. build_controls = cs_build_controls ,
2012-12-19 19:39:18 +04:00
. build_pcms = snd_hda_gen_build_pcms ,
2009-07-01 20:11:44 +04:00
. init = cs_init ,
. free = cs_free ,
2012-10-08 17:44:15 +04:00
. unsol_event = snd_hda_jack_unsol_event ,
2009-07-01 20:11:44 +04:00
} ;
static int cs_parse_auto_config ( struct hda_codec * codec )
{
struct cs_spec * spec = codec - > spec ;
int err ;
2012-12-19 19:39:18 +04:00
err = snd_hda_parse_pin_defcfg ( codec , & spec - > gen . autocfg , NULL , 0 ) ;
2009-07-07 11:04:26 +04:00
if ( err < 0 )
return err ;
2012-12-19 19:39:18 +04:00
err = snd_hda_gen_parse_auto_config ( codec , & spec - > gen . autocfg ) ;
2009-07-01 20:11:44 +04:00
if ( err < 0 )
return err ;
2012-12-19 19:39:18 +04:00
2009-07-01 20:11:44 +04:00
return 0 ;
}
2012-08-24 21:09:45 +04:00
static const struct hda_model_fixup cs420x_models [ ] = {
{ . id = CS420X_MBP53 , . name = " mbp53 " } ,
{ . id = CS420X_MBP55 , . name = " mbp55 " } ,
{ . id = CS420X_IMAC27 , . name = " imac27 " } ,
{ . id = CS420X_IMAC27_122 , . name = " imac27_122 " } ,
{ . id = CS420X_APPLE , . name = " apple " } ,
2012-09-11 18:53:08 +04:00
{ . id = CS420X_MBP101 , . name = " mbp101 " } ,
2012-11-23 11:34:13 +04:00
{ . id = CS420X_MBP81 , . name = " mbp81 " } ,
2013-06-17 12:19:49 +04:00
{ . id = CS420X_MBA42 , . name = " mba42 " } ,
2012-08-24 21:09:45 +04:00
{ }
2009-07-06 17:15:22 +04:00
} ;
2012-08-24 21:09:45 +04:00
static const struct snd_pci_quirk cs420x_fixup_tbl [ ] = {
2010-09-23 01:31:37 +04:00
SND_PCI_QUIRK ( 0x10de , 0x0ac0 , " MacBookPro 5,3 " , CS420X_MBP53 ) ,
2010-11-03 10:14:10 +03:00
SND_PCI_QUIRK ( 0x10de , 0x0d94 , " MacBookAir 3,1(2) " , CS420X_MBP55 ) ,
2009-07-06 17:15:22 +04:00
SND_PCI_QUIRK ( 0x10de , 0xcb79 , " MacBookPro 5,5 " , CS420X_MBP55 ) ,
2010-10-11 16:46:35 +04:00
SND_PCI_QUIRK ( 0x10de , 0xcb89 , " MacBookPro 7,1 " , CS420X_MBP55 ) ,
2011-11-22 23:00:31 +04:00
/* this conflicts with too many other models */
/*SND_PCI_QUIRK(0x8086, 0x7270, "IMac 27 Inch", CS420X_IMAC27),*/
2012-08-24 21:09:45 +04:00
/* codec SSID */
2012-11-23 11:34:13 +04:00
SND_PCI_QUIRK ( 0x106b , 0x1c00 , " MacBookPro 8,1 " , CS420X_MBP81 ) ,
2012-01-09 20:19:45 +04:00
SND_PCI_QUIRK ( 0x106b , 0x2000 , " iMac 12,2 " , CS420X_IMAC27_122 ) ,
2012-09-11 18:53:08 +04:00
SND_PCI_QUIRK ( 0x106b , 0x2800 , " MacBookPro 10,1 " , CS420X_MBP101 ) ,
2013-06-17 12:19:49 +04:00
SND_PCI_QUIRK ( 0x106b , 0x5b00 , " MacBookAir 4,2 " , CS420X_MBA42 ) ,
2011-11-22 23:00:31 +04:00
SND_PCI_QUIRK_VENDOR ( 0x106b , " Apple " , CS420X_APPLE ) ,
2009-07-06 17:15:22 +04:00
{ } /* terminator */
} ;
2012-08-24 21:09:45 +04:00
static const struct hda_pintbl mbp53_pincfgs [ ] = {
2010-09-23 01:31:37 +04:00
{ 0x09 , 0x012b4050 } ,
{ 0x0a , 0x90100141 } ,
{ 0x0b , 0x90100140 } ,
{ 0x0c , 0x018b3020 } ,
{ 0x0d , 0x90a00110 } ,
{ 0x0e , 0x400000f0 } ,
{ 0x0f , 0x01cbe030 } ,
{ 0x10 , 0x014be060 } ,
{ 0x12 , 0x400000f0 } ,
{ 0x15 , 0x400000f0 } ,
{ } /* terminator */
} ;
2012-08-24 21:09:45 +04:00
static const struct hda_pintbl mbp55_pincfgs [ ] = {
2009-07-06 17:15:22 +04:00
{ 0x09 , 0x012b4030 } ,
{ 0x0a , 0x90100121 } ,
{ 0x0b , 0x90100120 } ,
{ 0x0c , 0x400000f0 } ,
{ 0x0d , 0x90a00110 } ,
{ 0x0e , 0x400000f0 } ,
{ 0x0f , 0x400000f0 } ,
{ 0x10 , 0x014be040 } ,
{ 0x12 , 0x400000f0 } ,
{ 0x15 , 0x400000f0 } ,
{ } /* terminator */
} ;
2012-08-24 21:09:45 +04:00
static const struct hda_pintbl imac27_pincfgs [ ] = {
2009-12-22 09:59:37 +03:00
{ 0x09 , 0x012b4050 } ,
{ 0x0a , 0x90100140 } ,
{ 0x0b , 0x90100142 } ,
{ 0x0c , 0x018b3020 } ,
{ 0x0d , 0x90a00110 } ,
{ 0x0e , 0x400000f0 } ,
{ 0x0f , 0x01cbe030 } ,
{ 0x10 , 0x014be060 } ,
{ 0x12 , 0x01ab9070 } ,
{ 0x15 , 0x400000f0 } ,
{ } /* terminator */
} ;
2012-09-11 18:53:08 +04:00
static const struct hda_pintbl mbp101_pincfgs [ ] = {
{ 0x0d , 0x40ab90f0 } ,
{ 0x0e , 0x90a600f0 } ,
{ 0x12 , 0x50a600f0 } ,
{ } /* terminator */
} ;
2013-06-17 12:19:49 +04:00
static const struct hda_pintbl mba42_pincfgs [ ] = {
{ 0x09 , 0x012b4030 } , /* HP */
{ 0x0a , 0x400000f0 } ,
{ 0x0b , 0x90100120 } , /* speaker */
{ 0x0c , 0x400000f0 } ,
{ 0x0d , 0x90a00110 } , /* mic */
{ 0x0e , 0x400000f0 } ,
{ 0x0f , 0x400000f0 } ,
{ 0x10 , 0x400000f0 } ,
{ 0x12 , 0x400000f0 } ,
{ 0x15 , 0x400000f0 } ,
{ } /* terminator */
} ;
2013-09-27 12:13:51 +04:00
static const struct hda_pintbl mba6_pincfgs [ ] = {
{ 0x10 , 0x032120f0 } , /* HP */
{ 0x11 , 0x500000f0 } ,
{ 0x12 , 0x90100010 } , /* Speaker */
{ 0x13 , 0x500000f0 } ,
{ 0x14 , 0x500000f0 } ,
{ 0x15 , 0x770000f0 } ,
{ 0x16 , 0x770000f0 } ,
{ 0x17 , 0x430000f0 } ,
{ 0x18 , 0x43ab9030 } , /* Mic */
{ 0x19 , 0x770000f0 } ,
{ 0x1a , 0x770000f0 } ,
{ 0x1b , 0x770000f0 } ,
{ 0x1c , 0x90a00090 } ,
{ 0x1d , 0x500000f0 } ,
{ 0x1e , 0x500000f0 } ,
{ 0x1f , 0x500000f0 } ,
{ 0x20 , 0x500000f0 } ,
{ 0x21 , 0x430000f0 } ,
{ 0x22 , 0x430000f0 } ,
{ } /* terminator */
} ;
2012-08-24 21:09:45 +04:00
static void cs420x_fixup_gpio_13 ( struct hda_codec * codec ,
const struct hda_fixup * fix , int action )
{
if ( action = = HDA_FIXUP_ACT_PRE_PROBE ) {
struct cs_spec * spec = codec - > spec ;
spec - > gpio_eapd_hp = 2 ; /* GPIO1 = headphones */
spec - > gpio_eapd_speaker = 8 ; /* GPIO3 = speakers */
spec - > gpio_mask = spec - > gpio_dir =
spec - > gpio_eapd_hp | spec - > gpio_eapd_speaker ;
}
}
2009-07-06 17:15:22 +04:00
2012-08-24 21:09:45 +04:00
static void cs420x_fixup_gpio_23 ( struct hda_codec * codec ,
const struct hda_fixup * fix , int action )
2009-07-06 17:15:22 +04:00
{
2012-08-24 21:09:45 +04:00
if ( action = = HDA_FIXUP_ACT_PRE_PROBE ) {
struct cs_spec * spec = codec - > spec ;
spec - > gpio_eapd_hp = 4 ; /* GPIO2 = headphones */
spec - > gpio_eapd_speaker = 8 ; /* GPIO3 = speakers */
spec - > gpio_mask = spec - > gpio_dir =
spec - > gpio_eapd_hp | spec - > gpio_eapd_speaker ;
}
2009-07-06 17:15:22 +04:00
}
2012-08-24 21:09:45 +04:00
static const struct hda_fixup cs420x_fixups [ ] = {
[ CS420X_MBP53 ] = {
. type = HDA_FIXUP_PINS ,
. v . pins = mbp53_pincfgs ,
. chained = true ,
. chain_id = CS420X_APPLE ,
} ,
[ CS420X_MBP55 ] = {
. type = HDA_FIXUP_PINS ,
. v . pins = mbp55_pincfgs ,
. chained = true ,
. chain_id = CS420X_GPIO_13 ,
} ,
[ CS420X_IMAC27 ] = {
. type = HDA_FIXUP_PINS ,
. v . pins = imac27_pincfgs ,
. chained = true ,
. chain_id = CS420X_GPIO_13 ,
} ,
[ CS420X_GPIO_13 ] = {
. type = HDA_FIXUP_FUNC ,
. v . func = cs420x_fixup_gpio_13 ,
} ,
[ CS420X_GPIO_23 ] = {
. type = HDA_FIXUP_FUNC ,
. v . func = cs420x_fixup_gpio_23 ,
} ,
2012-09-11 18:53:08 +04:00
[ CS420X_MBP101 ] = {
. type = HDA_FIXUP_PINS ,
. v . pins = mbp101_pincfgs ,
. chained = true ,
. chain_id = CS420X_GPIO_13 ,
} ,
2012-11-23 11:34:13 +04:00
[ CS420X_MBP81 ] = {
. type = HDA_FIXUP_VERBS ,
. v . verbs = ( const struct hda_verb [ ] ) {
/* internal mic ADC2: right only, single ended */
{ 0x11 , AC_VERB_SET_COEF_INDEX , IDX_ADC_CFG } ,
{ 0x11 , AC_VERB_SET_PROC_COEF , 0x102a } ,
{ }
} ,
. chained = true ,
. chain_id = CS420X_GPIO_13 ,
} ,
2013-06-17 12:19:49 +04:00
[ CS420X_MBA42 ] = {
. type = HDA_FIXUP_PINS ,
. v . pins = mba42_pincfgs ,
. chained = true ,
. chain_id = CS420X_GPIO_13 ,
} ,
2012-08-24 21:09:45 +04:00
} ;
2012-12-19 19:39:18 +04:00
static struct cs_spec * cs_alloc_spec ( struct hda_codec * codec , int vendor_nid )
2009-07-01 20:11:44 +04:00
{
struct cs_spec * spec ;
spec = kzalloc ( sizeof ( * spec ) , GFP_KERNEL ) ;
if ( ! spec )
2012-12-19 19:39:18 +04:00
return NULL ;
2009-07-01 20:11:44 +04:00
codec - > spec = spec ;
2012-12-19 19:39:18 +04:00
spec - > vendor_nid = vendor_nid ;
snd_hda_gen_spec_init ( & spec - > gen ) ;
return spec ;
}
static int patch_cs420x ( struct hda_codec * codec )
{
struct cs_spec * spec ;
int err ;
2009-07-01 20:11:44 +04:00
2012-12-19 19:39:18 +04:00
spec = cs_alloc_spec ( codec , CS420X_VENDOR_NID ) ;
if ( ! spec )
return - ENOMEM ;
2011-07-23 01:41:00 +04:00
2013-03-15 17:23:32 +04:00
spec - > gen . automute_hook = cs_automute ;
2012-08-24 21:09:45 +04:00
snd_hda_pick_fixup ( codec , cs420x_models , cs420x_fixup_tbl ,
cs420x_fixups ) ;
snd_hda_apply_fixup ( codec , HDA_FIXUP_ACT_PRE_PROBE ) ;
2009-07-01 20:11:44 +04:00
2009-07-07 11:04:26 +04:00
err = cs_parse_auto_config ( codec ) ;
2009-07-06 14:55:46 +04:00
if ( err < 0 )
goto error ;
2009-07-01 20:11:44 +04:00
codec - > patch_ops = cs_patch_ops ;
2012-08-24 21:09:45 +04:00
snd_hda_apply_fixup ( codec , HDA_FIXUP_ACT_PROBE ) ;
2009-07-01 20:11:44 +04:00
return 0 ;
error :
2012-10-10 10:50:35 +04:00
cs_free ( codec ) ;
2009-07-01 20:11:44 +04:00
return err ;
}
2013-09-09 15:57:57 +04:00
/*
* CS4208 support :
2013-09-27 12:13:51 +04:00
* Its layout is no longer compatible with CS4206 / CS4207
2013-09-09 15:57:57 +04:00
*/
enum {
2013-11-08 15:50:31 +04:00
CS4208_MAC_AUTO ,
2013-09-27 12:13:51 +04:00
CS4208_MBA6 ,
2013-11-08 16:03:57 +04:00
CS4208_MBP11 ,
2013-09-09 15:57:57 +04:00
CS4208_GPIO0 ,
} ;
static const struct hda_model_fixup cs4208_models [ ] = {
{ . id = CS4208_GPIO0 , . name = " gpio0 " } ,
2013-09-27 12:13:51 +04:00
{ . id = CS4208_MBA6 , . name = " mba6 " } ,
2013-11-08 16:03:57 +04:00
{ . id = CS4208_MBP11 , . name = " mbp11 " } ,
2013-09-09 15:57:57 +04:00
{ }
} ;
static const struct snd_pci_quirk cs4208_fixup_tbl [ ] = {
2013-11-08 15:50:31 +04:00
SND_PCI_QUIRK_VENDOR ( 0x106b , " Apple " , CS4208_MAC_AUTO ) ,
{ } /* terminator */
} ;
/* codec SSID matching */
static const struct snd_pci_quirk cs4208_mac_fixup_tbl [ ] = {
2013-11-08 16:03:57 +04:00
SND_PCI_QUIRK ( 0x106b , 0x5e00 , " MacBookPro 11,2 " , CS4208_MBP11 ) ,
2013-09-27 12:13:51 +04:00
SND_PCI_QUIRK ( 0x106b , 0x7100 , " MacBookAir 6,1 " , CS4208_MBA6 ) ,
SND_PCI_QUIRK ( 0x106b , 0x7200 , " MacBookAir 6,2 " , CS4208_MBA6 ) ,
2013-09-09 15:57:57 +04:00
{ } /* terminator */
} ;
static void cs4208_fixup_gpio0 ( struct hda_codec * codec ,
const struct hda_fixup * fix , int action )
{
if ( action = = HDA_FIXUP_ACT_PRE_PROBE ) {
struct cs_spec * spec = codec - > spec ;
spec - > gpio_eapd_hp = 0 ;
spec - > gpio_eapd_speaker = 1 ;
spec - > gpio_mask = spec - > gpio_dir =
spec - > gpio_eapd_hp | spec - > gpio_eapd_speaker ;
}
}
2013-11-08 15:50:31 +04:00
static const struct hda_fixup cs4208_fixups [ ] ;
/* remap the fixup from codec SSID and apply it */
static void cs4208_fixup_mac ( struct hda_codec * codec ,
const struct hda_fixup * fix , int action )
{
if ( action ! = HDA_FIXUP_ACT_PRE_PROBE )
return ;
snd_hda_pick_fixup ( codec , NULL , cs4208_mac_fixup_tbl , cs4208_fixups ) ;
if ( codec - > fixup_id < 0 | | codec - > fixup_id = = CS4208_MAC_AUTO )
codec - > fixup_id = CS4208_GPIO0 ; /* default fixup */
snd_hda_apply_fixup ( codec , action ) ;
}
2013-11-08 16:03:57 +04:00
static int cs4208_spdif_sw_put ( struct snd_kcontrol * kcontrol ,
struct snd_ctl_elem_value * ucontrol )
{
struct hda_codec * codec = snd_kcontrol_chip ( kcontrol ) ;
struct cs_spec * spec = codec - > spec ;
hda_nid_t pin = spec - > gen . autocfg . dig_out_pins [ 0 ] ;
int pinctl = ucontrol - > value . integer . value [ 0 ] ? PIN_OUT : 0 ;
snd_hda_set_pin_ctl_cache ( codec , pin , pinctl ) ;
return spec - > spdif_sw_put ( kcontrol , ucontrol ) ;
}
/* hook the SPDIF switch */
static void cs4208_fixup_spdif_switch ( struct hda_codec * codec ,
const struct hda_fixup * fix , int action )
{
if ( action = = HDA_FIXUP_ACT_BUILD ) {
struct cs_spec * spec = codec - > spec ;
struct snd_kcontrol * kctl ;
if ( ! spec - > gen . autocfg . dig_out_pins [ 0 ] )
return ;
kctl = snd_hda_find_mixer_ctl ( codec , " IEC958 Playback Switch " ) ;
if ( ! kctl )
return ;
spec - > spdif_sw_put = kctl - > put ;
kctl - > put = cs4208_spdif_sw_put ;
}
}
2013-09-09 15:57:57 +04:00
static const struct hda_fixup cs4208_fixups [ ] = {
2013-09-27 12:13:51 +04:00
[ CS4208_MBA6 ] = {
. type = HDA_FIXUP_PINS ,
. v . pins = mba6_pincfgs ,
. chained = true ,
. chain_id = CS4208_GPIO0 ,
} ,
2013-11-08 16:03:57 +04:00
[ CS4208_MBP11 ] = {
. type = HDA_FIXUP_FUNC ,
. v . func = cs4208_fixup_spdif_switch ,
. chained = true ,
. chain_id = CS4208_GPIO0 ,
} ,
2013-09-09 15:57:57 +04:00
[ CS4208_GPIO0 ] = {
. type = HDA_FIXUP_FUNC ,
. v . func = cs4208_fixup_gpio0 ,
} ,
2013-11-08 15:50:31 +04:00
[ CS4208_MAC_AUTO ] = {
. type = HDA_FIXUP_FUNC ,
. v . func = cs4208_fixup_mac ,
} ,
2013-09-09 15:57:57 +04:00
} ;
2013-09-27 12:13:51 +04:00
/* correct the 0dB offset of input pins */
static void cs4208_fix_amp_caps ( struct hda_codec * codec , hda_nid_t adc )
{
unsigned int caps ;
caps = query_amp_caps ( codec , adc , HDA_INPUT ) ;
caps & = ~ ( AC_AMPCAP_OFFSET ) ;
caps | = 0x02 ;
snd_hda_override_amp_caps ( codec , adc , HDA_INPUT , caps ) ;
}
2013-09-09 15:57:57 +04:00
static int patch_cs4208 ( struct hda_codec * codec )
{
struct cs_spec * spec ;
int err ;
2013-09-27 12:13:51 +04:00
spec = cs_alloc_spec ( codec , CS4208_VENDOR_NID ) ;
2013-09-09 15:57:57 +04:00
if ( ! spec )
return - ENOMEM ;
spec - > gen . automute_hook = cs_automute ;
2013-11-04 19:32:01 +04:00
/* exclude NID 0x10 (HP) from output volumes due to different steps */
spec - > gen . out_vol_mask = 1ULL < < 0x10 ;
2013-09-09 15:57:57 +04:00
snd_hda_pick_fixup ( codec , cs4208_models , cs4208_fixup_tbl ,
cs4208_fixups ) ;
snd_hda_apply_fixup ( codec , HDA_FIXUP_ACT_PRE_PROBE ) ;
2013-09-27 12:13:51 +04:00
snd_hda_override_wcaps ( codec , 0x18 ,
get_wcaps ( codec , 0x18 ) | AC_WCAP_STEREO ) ;
cs4208_fix_amp_caps ( codec , 0x18 ) ;
cs4208_fix_amp_caps ( codec , 0x1b ) ;
cs4208_fix_amp_caps ( codec , 0x1c ) ;
2013-09-09 15:57:57 +04:00
err = cs_parse_auto_config ( codec ) ;
if ( err < 0 )
goto error ;
codec - > patch_ops = cs_patch_ops ;
snd_hda_apply_fixup ( codec , HDA_FIXUP_ACT_PROBE ) ;
return 0 ;
error :
cs_free ( codec ) ;
return err ;
}
2011-07-23 01:41:00 +04:00
/*
* Cirrus Logic CS4210
*
* 1 DAC = > HP ( sense ) / Speakers ,
* 1 ADC < = LineIn ( sense ) / MicIn / DMicIn ,
* 1 SPDIF OUT = > SPDIF Trasmitter ( sense )
*/
/* CS4210 board names */
2012-08-24 21:09:45 +04:00
static const struct hda_model_fixup cs421x_models [ ] = {
{ . id = CS421X_CDB4210 , . name = " cdb4210 " } ,
2013-04-05 02:35:31 +04:00
{ . id = CS421X_STUMPY , . name = " stumpy " } ,
2012-08-24 21:09:45 +04:00
{ }
2011-07-23 01:41:00 +04:00
} ;
2012-08-24 21:09:45 +04:00
static const struct snd_pci_quirk cs421x_fixup_tbl [ ] = {
2011-07-23 01:41:00 +04:00
/* Test Intel board + CDB2410 */
SND_PCI_QUIRK ( 0x8086 , 0x5001 , " DP45SG/CDB4210 " , CS421X_CDB4210 ) ,
{ } /* terminator */
} ;
/* CS4210 board pinconfigs */
/* Default CS4210 (CDB4210)*/
2012-08-24 21:09:45 +04:00
static const struct hda_pintbl cdb4210_pincfgs [ ] = {
2011-07-23 01:41:00 +04:00
{ 0x05 , 0x0321401f } ,
{ 0x06 , 0x90170010 } ,
{ 0x07 , 0x03813031 } ,
{ 0x08 , 0xb7a70037 } ,
{ 0x09 , 0xb7a6003e } ,
{ 0x0a , 0x034510f0 } ,
{ } /* terminator */
} ;
2013-04-05 02:35:31 +04:00
/* Stumpy ChromeBox */
static const struct hda_pintbl stumpy_pincfgs [ ] = {
{ 0x05 , 0x022120f0 } ,
{ 0x06 , 0x901700f0 } ,
{ 0x07 , 0x02a120f0 } ,
{ 0x08 , 0x77a70037 } ,
{ 0x09 , 0x77a6003e } ,
{ 0x0a , 0x434510f0 } ,
{ } /* terminator */
} ;
2012-08-24 21:09:45 +04:00
/* Setup GPIO/SENSE for each board (if used) */
static void cs421x_fixup_sense_b ( struct hda_codec * codec ,
const struct hda_fixup * fix , int action )
{
struct cs_spec * spec = codec - > spec ;
if ( action = = HDA_FIXUP_ACT_PRE_PROBE )
spec - > sense_b = 1 ;
}
static const struct hda_fixup cs421x_fixups [ ] = {
[ CS421X_CDB4210 ] = {
. type = HDA_FIXUP_PINS ,
. v . pins = cdb4210_pincfgs ,
. chained = true ,
. chain_id = CS421X_SENSE_B ,
} ,
[ CS421X_SENSE_B ] = {
. type = HDA_FIXUP_FUNC ,
. v . func = cs421x_fixup_sense_b ,
2013-04-05 02:35:31 +04:00
} ,
[ CS421X_STUMPY ] = {
. type = HDA_FIXUP_PINS ,
. v . pins = stumpy_pincfgs ,
} ,
2011-07-23 01:41:00 +04:00
} ;
static const struct hda_verb cs421x_coef_init_verbs [ ] = {
{ 0x0B , AC_VERB_SET_PROC_STATE , 1 } ,
{ 0x0B , AC_VERB_SET_COEF_INDEX , CS421X_IDX_DEV_CFG } ,
/*
Disable Coefficient Index Auto - Increment ( DAI ) = 1 ,
PDREF = 0
*/
{ 0x0B , AC_VERB_SET_PROC_COEF , 0x0001 } ,
{ 0x0B , AC_VERB_SET_COEF_INDEX , CS421X_IDX_ADC_CFG } ,
/* ADC SZCMode = Digital Soft Ramp */
{ 0x0B , AC_VERB_SET_PROC_COEF , 0x0002 } ,
{ 0x0B , AC_VERB_SET_COEF_INDEX , CS421X_IDX_DAC_CFG } ,
{ 0x0B , AC_VERB_SET_PROC_COEF ,
( 0x0002 /* DAC SZCMode = Digital Soft Ramp */
| 0x0004 /* Mute DAC on FIFO error */
| 0x0008 /* Enable DAC High Pass Filter */
) } ,
{ } /* terminator */
} ;
/* Errata: CS4210 rev A1 Silicon
*
* http : //www.cirrus.com/en/pubs/errata/
*
* Description :
* 1. Performance degredation is present in the ADC .
* 2. Speaker output is not completely muted upon HP detect .
* 3. Noise is present when clipping occurs on the amplified
* speaker outputs .
*
* Workaround :
* The following verb sequence written to the registers during
* initialization will correct the issues listed above .
*/
static const struct hda_verb cs421x_coef_init_verbs_A1_silicon_fixes [ ] = {
{ 0x0B , AC_VERB_SET_PROC_STATE , 0x01 } , /* VPW: processing on */
{ 0x0B , AC_VERB_SET_COEF_INDEX , 0x0006 } ,
{ 0x0B , AC_VERB_SET_PROC_COEF , 0x9999 } , /* Test mode: on */
{ 0x0B , AC_VERB_SET_COEF_INDEX , 0x000A } ,
{ 0x0B , AC_VERB_SET_PROC_COEF , 0x14CB } , /* Chop double */
{ 0x0B , AC_VERB_SET_COEF_INDEX , 0x0011 } ,
{ 0x0B , AC_VERB_SET_PROC_COEF , 0xA2D0 } , /* Increase ADC current */
{ 0x0B , AC_VERB_SET_COEF_INDEX , 0x001A } ,
{ 0x0B , AC_VERB_SET_PROC_COEF , 0x02A9 } , /* Mute speaker */
{ 0x0B , AC_VERB_SET_COEF_INDEX , 0x001B } ,
{ 0x0B , AC_VERB_SET_PROC_COEF , 0 X1006 } , /* Remove noise */
{ } /* terminator */
} ;
/* Speaker Amp Gain is controlled by the vendor widget's coef 4 */
static const DECLARE_TLV_DB_SCALE ( cs421x_speaker_boost_db_scale , 900 , 300 , 0 ) ;
static int cs421x_boost_vol_info ( struct snd_kcontrol * kcontrol ,
struct snd_ctl_elem_info * uinfo )
{
uinfo - > type = SNDRV_CTL_ELEM_TYPE_INTEGER ;
uinfo - > count = 1 ;
uinfo - > value . integer . min = 0 ;
uinfo - > value . integer . max = 3 ;
return 0 ;
}
static int cs421x_boost_vol_get ( struct snd_kcontrol * kcontrol ,
struct snd_ctl_elem_value * ucontrol )
{
struct hda_codec * codec = snd_kcontrol_chip ( kcontrol ) ;
ucontrol - > value . integer . value [ 0 ] =
cs_vendor_coef_get ( codec , CS421X_IDX_SPK_CTL ) & 0x0003 ;
return 0 ;
}
static int cs421x_boost_vol_put ( struct snd_kcontrol * kcontrol ,
struct snd_ctl_elem_value * ucontrol )
{
struct hda_codec * codec = snd_kcontrol_chip ( kcontrol ) ;
unsigned int vol = ucontrol - > value . integer . value [ 0 ] ;
unsigned int coef =
cs_vendor_coef_get ( codec , CS421X_IDX_SPK_CTL ) ;
unsigned int original_coef = coef ;
coef & = ~ 0x0003 ;
coef | = ( vol & 0x0003 ) ;
if ( original_coef = = coef )
return 0 ;
else {
cs_vendor_coef_set ( codec , CS421X_IDX_SPK_CTL , coef ) ;
return 1 ;
}
}
2012-12-19 19:39:18 +04:00
static const struct snd_kcontrol_new cs421x_speaker_boost_ctl = {
2011-07-23 01:41:00 +04:00
. iface = SNDRV_CTL_ELEM_IFACE_MIXER ,
. access = ( SNDRV_CTL_ELEM_ACCESS_READWRITE |
SNDRV_CTL_ELEM_ACCESS_TLV_READ ) ,
. name = " Speaker Boost Playback Volume " ,
. info = cs421x_boost_vol_info ,
. get = cs421x_boost_vol_get ,
. put = cs421x_boost_vol_put ,
. tlv = { . p = cs421x_speaker_boost_db_scale } ,
} ;
2012-01-02 15:40:17 +04:00
static void cs4210_pinmux_init ( struct hda_codec * codec )
2011-07-23 01:41:00 +04:00
{
struct cs_spec * spec = codec - > spec ;
unsigned int def_conf , coef ;
/* GPIO, DMIC_SCL, DMIC_SDA and SENSE_B are multiplexed */
coef = cs_vendor_coef_get ( codec , CS421X_IDX_DEV_CFG ) ;
if ( spec - > gpio_mask )
coef | = 0x0008 ; /* B1,B2 are GPIOs */
else
coef & = ~ 0x0008 ;
if ( spec - > sense_b )
coef | = 0x0010 ; /* B2 is SENSE_B, not inverted */
else
coef & = ~ 0x0010 ;
cs_vendor_coef_set ( codec , CS421X_IDX_DEV_CFG , coef ) ;
if ( ( spec - > gpio_mask | | spec - > sense_b ) & &
is_active_pin ( codec , CS421X_DMIC_PIN_NID ) ) {
/*
GPIO or SENSE_B forced - disconnect the DMIC pin .
*/
def_conf = snd_hda_codec_get_pincfg ( codec , CS421X_DMIC_PIN_NID ) ;
def_conf & = ~ AC_DEFCFG_PORT_CONN ;
def_conf | = ( AC_JACK_PORT_NONE < < AC_DEFCFG_PORT_CONN_SHIFT ) ;
snd_hda_codec_set_pincfg ( codec , CS421X_DMIC_PIN_NID , def_conf ) ;
}
}
2012-12-19 19:39:18 +04:00
static void cs4210_spdif_automute ( struct hda_codec * codec ,
struct hda_jack_tbl * tbl )
2011-07-23 01:41:00 +04:00
{
struct cs_spec * spec = codec - > spec ;
2012-12-19 19:39:18 +04:00
bool spdif_present = false ;
hda_nid_t spdif_pin = spec - > gen . autocfg . dig_out_pins [ 0 ] ;
/* detect on spdif is specific to CS4210 */
if ( ! spec - > spdif_detect | |
spec - > vendor_nid ! = CS4210_VENDOR_NID )
return ;
spdif_present = snd_hda_jack_detect ( codec , spdif_pin ) ;
if ( spdif_present = = spec - > spdif_present )
return ;
spec - > spdif_present = spdif_present ;
/* SPDIF TX on/off */
if ( spdif_present )
snd_hda_set_pin_ctl ( codec , spdif_pin ,
spdif_present ? PIN_OUT : 0 ) ;
2011-07-23 01:41:00 +04:00
2012-12-19 19:39:18 +04:00
cs_automute ( codec ) ;
}
static void parse_cs421x_digital ( struct hda_codec * codec )
{
struct cs_spec * spec = codec - > spec ;
struct auto_pin_cfg * cfg = & spec - > gen . autocfg ;
int i ;
2011-07-23 01:41:00 +04:00
for ( i = 0 ; i < cfg - > dig_outs ; i + + ) {
hda_nid_t nid = cfg - > dig_out_pins [ i ] ;
if ( get_wcaps ( codec , nid ) & AC_WCAP_UNSOL_CAP ) {
spec - > spdif_detect = 1 ;
2012-12-19 19:39:18 +04:00
snd_hda_jack_detect_enable_callback ( codec , nid ,
SPDIF_EVENT ,
cs4210_spdif_automute ) ;
2011-07-23 01:41:00 +04:00
}
}
}
static int cs421x_init ( struct hda_codec * codec )
{
struct cs_spec * spec = codec - > spec ;
2012-01-02 15:40:17 +04:00
if ( spec - > vendor_nid = = CS4210_VENDOR_NID ) {
snd_hda_sequence_write ( codec , cs421x_coef_init_verbs ) ;
snd_hda_sequence_write ( codec , cs421x_coef_init_verbs_A1_silicon_fixes ) ;
cs4210_pinmux_init ( codec ) ;
}
2011-07-23 01:41:00 +04:00
2012-12-19 19:39:18 +04:00
snd_hda_gen_init ( codec ) ;
2011-07-23 01:41:00 +04:00
if ( spec - > gpio_mask ) {
snd_hda_codec_write ( codec , 0x01 , 0 , AC_VERB_SET_GPIO_MASK ,
spec - > gpio_mask ) ;
snd_hda_codec_write ( codec , 0x01 , 0 , AC_VERB_SET_GPIO_DIRECTION ,
spec - > gpio_dir ) ;
snd_hda_codec_write ( codec , 0x01 , 0 , AC_VERB_SET_GPIO_DATA ,
spec - > gpio_data ) ;
}
2012-12-19 19:39:18 +04:00
init_input_coef ( codec ) ;
2011-07-23 01:41:00 +04:00
2012-12-19 19:39:18 +04:00
cs4210_spdif_automute ( codec , NULL ) ;
2011-07-23 01:41:00 +04:00
return 0 ;
}
2012-12-19 19:39:18 +04:00
static int cs421x_build_controls ( struct hda_codec * codec )
2011-07-23 01:41:00 +04:00
{
struct cs_spec * spec = codec - > spec ;
int err ;
2012-12-19 19:39:18 +04:00
err = snd_hda_gen_build_controls ( codec ) ;
2011-07-23 01:41:00 +04:00
if ( err < 0 )
return err ;
2012-12-19 19:39:18 +04:00
if ( spec - > gen . autocfg . speaker_outs & &
spec - > vendor_nid = = CS4210_VENDOR_NID ) {
2011-07-23 01:41:00 +04:00
err = snd_hda_ctl_add ( codec , 0 ,
2012-12-19 19:39:18 +04:00
snd_ctl_new1 ( & cs421x_speaker_boost_ctl , codec ) ) ;
2011-07-23 01:41:00 +04:00
if ( err < 0 )
return err ;
}
2011-10-28 02:03:22 +04:00
return 0 ;
2011-07-23 01:41:00 +04:00
}
2012-12-19 19:39:18 +04:00
static void fix_volume_caps ( struct hda_codec * codec , hda_nid_t dac )
2011-07-23 01:41:00 +04:00
{
2012-12-19 19:39:18 +04:00
unsigned int caps ;
2011-07-23 01:41:00 +04:00
2012-12-19 19:39:18 +04:00
/* set the upper-limit for mixer amp to 0dB */
caps = query_amp_caps ( codec , dac , HDA_OUTPUT ) ;
caps & = ~ ( 0x7f < < AC_AMPCAP_NUM_STEPS_SHIFT ) ;
caps | = ( ( caps > > AC_AMPCAP_OFFSET_SHIFT ) & 0x7f )
< < AC_AMPCAP_NUM_STEPS_SHIFT ;
snd_hda_override_amp_caps ( codec , dac , HDA_OUTPUT , caps ) ;
2011-07-23 01:41:00 +04:00
}
static int cs421x_parse_auto_config ( struct hda_codec * codec )
{
struct cs_spec * spec = codec - > spec ;
2012-12-19 19:39:18 +04:00
hda_nid_t dac = CS4210_DAC_NID ;
2011-07-23 01:41:00 +04:00
int err ;
2012-12-19 19:39:18 +04:00
fix_volume_caps ( codec , dac ) ;
err = snd_hda_parse_pin_defcfg ( codec , & spec - > gen . autocfg , NULL , 0 ) ;
2011-07-23 01:41:00 +04:00
if ( err < 0 )
return err ;
2012-12-19 19:39:18 +04:00
err = snd_hda_gen_parse_auto_config ( codec , & spec - > gen . autocfg ) ;
2011-07-23 01:41:00 +04:00
if ( err < 0 )
return err ;
2012-12-19 19:39:18 +04:00
parse_cs421x_digital ( codec ) ;
2011-07-23 01:41:00 +04:00
return 0 ;
}
# ifdef CONFIG_PM
/*
Manage PDREF , when transitioning to D3hot
( DAC , ADC ) - > D3 , PDREF = 1 , AFG - > D3
*/
2012-07-02 17:20:37 +04:00
static int cs421x_suspend ( struct hda_codec * codec )
2011-07-23 01:41:00 +04:00
{
2012-01-02 15:40:17 +04:00
struct cs_spec * spec = codec - > spec ;
2011-07-23 01:41:00 +04:00
unsigned int coef ;
snd_hda_shutup_pins ( codec ) ;
snd_hda_codec_write ( codec , CS4210_DAC_NID , 0 ,
AC_VERB_SET_POWER_STATE , AC_PWRST_D3 ) ;
snd_hda_codec_write ( codec , CS4210_ADC_NID , 0 ,
AC_VERB_SET_POWER_STATE , AC_PWRST_D3 ) ;
2012-01-02 15:40:17 +04:00
if ( spec - > vendor_nid = = CS4210_VENDOR_NID ) {
coef = cs_vendor_coef_get ( codec , CS421X_IDX_DEV_CFG ) ;
coef | = 0x0004 ; /* PDREF */
cs_vendor_coef_set ( codec , CS421X_IDX_DEV_CFG , coef ) ;
}
2011-07-23 01:41:00 +04:00
return 0 ;
}
# endif
2012-11-04 09:19:04 +04:00
static const struct hda_codec_ops cs421x_patch_ops = {
2011-07-23 01:41:00 +04:00
. build_controls = cs421x_build_controls ,
2012-12-19 19:39:18 +04:00
. build_pcms = snd_hda_gen_build_pcms ,
2011-07-23 01:41:00 +04:00
. init = cs421x_init ,
. free = cs_free ,
2012-10-08 17:44:15 +04:00
. unsol_event = snd_hda_jack_unsol_event ,
2011-07-23 01:41:00 +04:00
# ifdef CONFIG_PM
. suspend = cs421x_suspend ,
# endif
} ;
2012-01-02 15:40:17 +04:00
static int patch_cs4210 ( struct hda_codec * codec )
2011-07-23 01:41:00 +04:00
{
struct cs_spec * spec ;
int err ;
2012-12-19 19:39:18 +04:00
spec = cs_alloc_spec ( codec , CS4210_VENDOR_NID ) ;
2011-07-23 01:41:00 +04:00
if ( ! spec )
return - ENOMEM ;
2013-03-15 17:23:32 +04:00
spec - > gen . automute_hook = cs_automute ;
2012-08-24 21:09:45 +04:00
snd_hda_pick_fixup ( codec , cs421x_models , cs421x_fixup_tbl ,
cs421x_fixups ) ;
snd_hda_apply_fixup ( codec , HDA_FIXUP_ACT_PRE_PROBE ) ;
2011-07-23 01:41:00 +04:00
/*
Update the GPIO / DMIC / SENSE_B pinmux before the configuration
is auto - parsed . If GPIO or SENSE_B is forced , DMIC input
is disabled .
*/
2012-01-02 15:40:17 +04:00
cs4210_pinmux_init ( codec ) ;
2011-07-23 01:41:00 +04:00
err = cs421x_parse_auto_config ( codec ) ;
if ( err < 0 )
goto error ;
2012-01-02 15:40:17 +04:00
codec - > patch_ops = cs421x_patch_ops ;
2012-08-24 21:09:45 +04:00
snd_hda_apply_fixup ( codec , HDA_FIXUP_ACT_PROBE ) ;
2012-01-02 15:40:17 +04:00
return 0 ;
error :
2012-10-10 10:50:35 +04:00
cs_free ( codec ) ;
2012-01-02 15:40:17 +04:00
return err ;
}
static int patch_cs4213 ( struct hda_codec * codec )
{
struct cs_spec * spec ;
int err ;
2012-12-19 19:39:18 +04:00
spec = cs_alloc_spec ( codec , CS4213_VENDOR_NID ) ;
2012-01-02 15:40:17 +04:00
if ( ! spec )
return - ENOMEM ;
err = cs421x_parse_auto_config ( codec ) ;
if ( err < 0 )
goto error ;
2011-07-23 01:41:00 +04:00
2012-01-02 15:40:17 +04:00
codec - > patch_ops = cs421x_patch_ops ;
2011-07-23 01:41:00 +04:00
return 0 ;
error :
2012-10-10 10:50:35 +04:00
cs_free ( codec ) ;
2011-07-23 01:41:00 +04:00
return err ;
}
2009-07-01 20:11:44 +04:00
/*
* patch entries
*/
2011-05-02 13:36:09 +04:00
static const struct hda_codec_preset snd_hda_preset_cirrus [ ] = {
2009-07-01 20:11:44 +04:00
{ . id = 0x10134206 , . name = " CS4206 " , . patch = patch_cs420x } ,
{ . id = 0x10134207 , . name = " CS4207 " , . patch = patch_cs420x } ,
2013-09-09 15:57:57 +04:00
{ . id = 0x10134208 , . name = " CS4208 " , . patch = patch_cs4208 } ,
2012-01-02 15:40:17 +04:00
{ . id = 0x10134210 , . name = " CS4210 " , . patch = patch_cs4210 } ,
{ . id = 0x10134213 , . name = " CS4213 " , . patch = patch_cs4213 } ,
2009-07-01 20:11:44 +04:00
{ } /* terminator */
} ;
MODULE_ALIAS ( " snd-hda-codec-id:10134206 " ) ;
MODULE_ALIAS ( " snd-hda-codec-id:10134207 " ) ;
2013-09-09 15:57:57 +04:00
MODULE_ALIAS ( " snd-hda-codec-id:10134208 " ) ;
2011-07-23 01:41:00 +04:00
MODULE_ALIAS ( " snd-hda-codec-id:10134210 " ) ;
2012-01-02 15:40:17 +04:00
MODULE_ALIAS ( " snd-hda-codec-id:10134213 " ) ;
2009-07-01 20:11:44 +04:00
MODULE_LICENSE ( " GPL " ) ;
MODULE_DESCRIPTION ( " Cirrus Logic HD-audio codec " ) ;
static struct hda_codec_preset_list cirrus_list = {
. preset = snd_hda_preset_cirrus ,
. owner = THIS_MODULE ,
} ;
static int __init patch_cirrus_init ( void )
{
return snd_hda_add_codec_preset ( & cirrus_list ) ;
}
static void __exit patch_cirrus_exit ( void )
{
snd_hda_delete_codec_preset ( & cirrus_list ) ;
}
module_init ( patch_cirrus_init )
module_exit ( patch_cirrus_exit )