]> pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - sound/pci/ac97/ac97_patch.c
Merge branch 'for-linus' of ssh://master.kernel.org/pub/scm/linux/kernel/git/ieee1394...
[linux-2.6-omap-h63xx.git] / sound / pci / ac97 / ac97_patch.c
index e813968e0cf8a91324e1822e2453396836cf9991..bfc2fed16da3a0208a6a5be3d146a81032ecffd1 100644 (file)
@@ -54,7 +54,7 @@ static int patch_build_controls(struct snd_ac97 * ac97, const struct snd_kcontro
 
 /* replace with a new TLV */
 static void reset_tlv(struct snd_ac97 *ac97, const char *name,
-                     unsigned int *tlv)
+                     const unsigned int *tlv)
 {
        struct snd_ctl_elem_id sid;
        struct snd_kcontrol *kctl;
@@ -190,14 +190,28 @@ static inline int is_clfe_on(struct snd_ac97 *ac97)
        return ac97->channel_mode >= 2;
 }
 
+/* system has shared jacks with surround out enabled */
+static inline int is_shared_surrout(struct snd_ac97 *ac97)
+{
+       return !ac97->indep_surround && is_surround_on(ac97);
+}
+
+/* system has shared jacks with center/lfe out enabled */
+static inline int is_shared_clfeout(struct snd_ac97 *ac97)
+{
+       return !ac97->indep_surround && is_clfe_on(ac97);
+}
+
+/* system has shared jacks with line in enabled */
 static inline int is_shared_linein(struct snd_ac97 *ac97)
 {
-       return ! ac97->indep_surround && is_surround_on(ac97);
+       return !ac97->indep_surround && !is_surround_on(ac97);
 }
 
+/* system has shared jacks with mic in enabled */
 static inline int is_shared_micin(struct snd_ac97 *ac97)
 {
-       return ! ac97->indep_surround && is_clfe_on(ac97);
+       return !ac97->indep_surround && !is_clfe_on(ac97);
 }
 
 
@@ -941,6 +955,9 @@ static int patch_sigmatel_stac9708_specific(struct snd_ac97 *ac97)
 {
        int err;
 
+       /* the register bit is writable, but the function is not implemented: */
+       snd_ac97_remove_ctl(ac97, "PCM Out Path & Mute", NULL);
+
        snd_ac97_rename_vol_ctl(ac97, "Headphone Playback", "Sigmatel Surround Playback");
        if ((err = patch_build_controls(ac97, &snd_ac97_stac9708_bias_control, 1)) < 0)
                return err;
@@ -1321,6 +1338,12 @@ int patch_conexant(struct snd_ac97 * ac97)
        return 0;
 }
 
+int patch_cx20551(struct snd_ac97 *ac97)
+{
+       snd_ac97_update_bits(ac97, 0x5c, 0x01, 0x01);
+       return 0;
+}
+
 /*
  * Analog Device AD18xx, AD19xx codecs
  */
@@ -1552,7 +1575,7 @@ static const struct snd_kcontrol_new snd_ac97_controls_ad1885[] = {
        AC97_SINGLE("Line Jack Sense", AC97_AD_JACK_SPDIF, 8, 1, 1), /* inverted */
 };
 
-static DECLARE_TLV_DB_SCALE(db_scale_6bit_6db_max, -8850, 150, 0);
+static const DECLARE_TLV_DB_SCALE(db_scale_6bit_6db_max, -8850, 150, 0);
 
 static int patch_ad1885_specific(struct snd_ac97 * ac97)
 {
@@ -1609,19 +1632,22 @@ int patch_ad1886(struct snd_ac97 * ac97)
        return 0;
 }
 
-/* MISC bits */
+/* MISC bits (AD1888/AD1980/AD1985 register 0x76) */
 #define AC97_AD198X_MBC                0x0003  /* mic boost */
 #define AC97_AD198X_MBC_20     0x0000  /* +20dB */
 #define AC97_AD198X_MBC_10     0x0001  /* +10dB */
 #define AC97_AD198X_MBC_30     0x0002  /* +30dB */
 #define AC97_AD198X_VREFD      0x0004  /* VREF high-Z */
-#define AC97_AD198X_VREFH      0x0008  /* 2.25V, 3.7V */
-#define AC97_AD198X_VREF_0     0x000c  /* 0V */
+#define AC97_AD198X_VREFH      0x0008  /* 0=2.25V, 1=3.7V */
+#define AC97_AD198X_VREF_0     0x000c  /* 0V (AD1985 only) */
+#define AC97_AD198X_VREF_MASK  (AC97_AD198X_VREFH | AC97_AD198X_VREFD)
+#define AC97_AD198X_VREF_SHIFT 2
 #define AC97_AD198X_SRU                0x0010  /* sample rate unlock */
 #define AC97_AD198X_LOSEL      0x0020  /* LINE_OUT amplifiers input select */
 #define AC97_AD198X_2MIC       0x0040  /* 2-channel mic select */
 #define AC97_AD198X_SPRD       0x0080  /* SPREAD enable */
-#define AC97_AD198X_DMIX0      0x0100  /* downmix mode: 0 = 6-to-4, 1 = 6-to-2 downmix */
+#define AC97_AD198X_DMIX0      0x0100  /* downmix mode: */
+                                       /*  0 = 6-to-4, 1 = 6-to-2 downmix */
 #define AC97_AD198X_DMIX1      0x0200  /* downmix mode: 1 = enabled */
 #define AC97_AD198X_HPSEL      0x0400  /* headphone amplifier input select */
 #define AC97_AD198X_CLDIS      0x0800  /* center/lfe disable */
@@ -1630,6 +1656,83 @@ int patch_ad1886(struct snd_ac97 * ac97)
 #define AC97_AD198X_AC97NC     0x4000  /* AC97 no compatible mode */
 #define AC97_AD198X_DACZ       0x8000  /* DAC zero-fill mode */
 
+/* MISC 1 bits (AD1986 register 0x76) */
+#define AC97_AD1986_MBC                0x0003  /* mic boost */
+#define AC97_AD1986_MBC_20     0x0000  /* +20dB */
+#define AC97_AD1986_MBC_10     0x0001  /* +10dB */
+#define AC97_AD1986_MBC_30     0x0002  /* +30dB */
+#define AC97_AD1986_LISEL0     0x0004  /* LINE_IN select bit 0 */
+#define AC97_AD1986_LISEL1     0x0008  /* LINE_IN select bit 1 */
+#define AC97_AD1986_LISEL_MASK (AC97_AD1986_LISEL1 | AC97_AD1986_LISEL0)
+#define AC97_AD1986_LISEL_LI   0x0000  /* LINE_IN pins as LINE_IN source */
+#define AC97_AD1986_LISEL_SURR 0x0004  /* SURROUND pins as LINE_IN source */
+#define AC97_AD1986_LISEL_MIC  0x0008  /* MIC_1/2 pins as LINE_IN source */
+#define AC97_AD1986_SRU                0x0010  /* sample rate unlock */
+#define AC97_AD1986_SOSEL      0x0020  /* SURROUND_OUT amplifiers input sel */
+#define AC97_AD1986_2MIC       0x0040  /* 2-channel mic select */
+#define AC97_AD1986_SPRD       0x0080  /* SPREAD enable */
+#define AC97_AD1986_DMIX0      0x0100  /* downmix mode: */
+                                       /*  0 = 6-to-4, 1 = 6-to-2 downmix */
+#define AC97_AD1986_DMIX1      0x0200  /* downmix mode: 1 = enabled */
+#define AC97_AD1986_CLDIS      0x0800  /* center/lfe disable */
+#define AC97_AD1986_SODIS      0x1000  /* SURROUND_OUT disable */
+#define AC97_AD1986_MSPLT      0x2000  /* mute split (read only 1) */
+#define AC97_AD1986_AC97NC     0x4000  /* AC97 no compatible mode (r/o 1) */
+#define AC97_AD1986_DACZ       0x8000  /* DAC zero-fill mode */
+
+/* MISC 2 bits (AD1986 register 0x70) */
+#define AC97_AD_MISC2          0x70    /* Misc Control Bits 2 (AD1986) */
+
+#define AC97_AD1986_CVREF0     0x0004  /* C/LFE VREF_OUT 2.25V */
+#define AC97_AD1986_CVREF1     0x0008  /* C/LFE VREF_OUT 0V */
+#define AC97_AD1986_CVREF2     0x0010  /* C/LFE VREF_OUT 3.7V */
+#define AC97_AD1986_CVREF_MASK \
+       (AC97_AD1986_CVREF2 | AC97_AD1986_CVREF1 | AC97_AD1986_CVREF0)
+#define AC97_AD1986_JSMAP      0x0020  /* Jack Sense Mapping 1 = alternate */
+#define AC97_AD1986_MMDIS      0x0080  /* Mono Mute Disable */
+#define AC97_AD1986_MVREF0     0x0400  /* MIC VREF_OUT 2.25V */
+#define AC97_AD1986_MVREF1     0x0800  /* MIC VREF_OUT 0V */
+#define AC97_AD1986_MVREF2     0x1000  /* MIC VREF_OUT 3.7V */
+#define AC97_AD1986_MVREF_MASK \
+       (AC97_AD1986_MVREF2 | AC97_AD1986_MVREF1 | AC97_AD1986_MVREF0)
+
+/* MISC 3 bits (AD1986 register 0x7a) */
+#define AC97_AD_MISC3          0x7a    /* Misc Control Bits 3 (AD1986) */
+
+#define AC97_AD1986_MMIX       0x0004  /* Mic Mix, left/right */
+#define AC97_AD1986_GPO                0x0008  /* General Purpose Out */
+#define AC97_AD1986_LOHPEN     0x0010  /* LINE_OUT headphone drive */
+#define AC97_AD1986_LVREF0     0x0100  /* LINE_OUT VREF_OUT 2.25V */
+#define AC97_AD1986_LVREF1     0x0200  /* LINE_OUT VREF_OUT 0V */
+#define AC97_AD1986_LVREF2     0x0400  /* LINE_OUT VREF_OUT 3.7V */
+#define AC97_AD1986_LVREF_MASK \
+       (AC97_AD1986_LVREF2 | AC97_AD1986_LVREF1 | AC97_AD1986_LVREF0)
+#define AC97_AD1986_JSINVA     0x0800  /* Jack Sense Invert SENSE_A */
+#define AC97_AD1986_LOSEL      0x1000  /* LINE_OUT amplifiers input select */
+#define AC97_AD1986_HPSEL0     0x2000  /* Headphone amplifiers */
+                                       /*   input select Surround DACs */
+#define AC97_AD1986_HPSEL1     0x4000  /* Headphone amplifiers input */
+                                       /*   select C/LFE DACs */
+#define AC97_AD1986_JSINVB     0x8000  /* Jack Sense Invert SENSE_B */
+
+/* Serial Config bits (AD1986 register 0x74) (incomplete) */
+#define AC97_AD1986_OMS0       0x0100  /* Optional Mic Selector bit 0 */
+#define AC97_AD1986_OMS1       0x0200  /* Optional Mic Selector bit 1 */
+#define AC97_AD1986_OMS2       0x0400  /* Optional Mic Selector bit 2 */
+#define AC97_AD1986_OMS_MASK \
+       (AC97_AD1986_OMS2 | AC97_AD1986_OMS1 | AC97_AD1986_OMS0)
+#define AC97_AD1986_OMS_M      0x0000  /* MIC_1/2 pins are MIC sources */
+#define AC97_AD1986_OMS_L      0x0100  /* LINE_IN pins are MIC sources */
+#define AC97_AD1986_OMS_C      0x0200  /* Center/LFE pins are MCI sources */
+#define AC97_AD1986_OMS_MC     0x0400  /* Mix of MIC and C/LFE pins */
+                                       /*   are MIC sources */
+#define AC97_AD1986_OMS_ML     0x0500  /* MIX of MIC and LINE_IN pins */
+                                       /*   are MIC sources */
+#define AC97_AD1986_OMS_LC     0x0600  /* MIX of LINE_IN and C/LFE pins */
+                                       /*   are MIC sources */
+#define AC97_AD1986_OMS_MLC    0x0700  /* MIX of MIC, LINE_IN, C/LFE pins */
+                                       /*   are MIC sources */
+
 
 static int snd_ac97_ad198x_spdif_source_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
 {
@@ -1952,8 +2055,80 @@ int patch_ad1980(struct snd_ac97 * ac97)
        return 0;
 }
 
+static int snd_ac97_ad1985_vrefout_info(struct snd_kcontrol *kcontrol,
+                                       struct snd_ctl_elem_info *uinfo)
+{
+       static char *texts[4] = {"High-Z", "3.7 V", "2.25 V", "0 V"};
+
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+       uinfo->count = 1;
+       uinfo->value.enumerated.items = 4;
+       if (uinfo->value.enumerated.item > 3)
+               uinfo->value.enumerated.item = 3;
+       strcpy(uinfo->value.enumerated.name,
+              texts[uinfo->value.enumerated.item]);
+       return 0;
+}
+
+static int snd_ac97_ad1985_vrefout_get(struct snd_kcontrol *kcontrol,
+                                      struct snd_ctl_elem_value *ucontrol)
+{
+       static const int reg2ctrl[4] = {2, 0, 1, 3};
+       struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol);
+       unsigned short val;
+       val = (ac97->regs[AC97_AD_MISC] & AC97_AD198X_VREF_MASK)
+             >> AC97_AD198X_VREF_SHIFT;
+       ucontrol->value.enumerated.item[0] = reg2ctrl[val];
+       return 0;
+}
+
+static int snd_ac97_ad1985_vrefout_put(struct snd_kcontrol *kcontrol, 
+                                      struct snd_ctl_elem_value *ucontrol)
+{
+       static const int ctrl2reg[4] = {1, 2, 0, 3};
+       struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol);
+       unsigned short val;
+
+       if (ucontrol->value.enumerated.item[0] > 3
+           || ucontrol->value.enumerated.item[0] < 0)
+               return -EINVAL;
+       val = ctrl2reg[ucontrol->value.enumerated.item[0]]
+             << AC97_AD198X_VREF_SHIFT;
+       return snd_ac97_update_bits(ac97, AC97_AD_MISC,
+                                   AC97_AD198X_VREF_MASK, val);
+}
+
 static const struct snd_kcontrol_new snd_ac97_ad1985_controls[] = {
-       AC97_SINGLE("Exchange Center/LFE", AC97_AD_SERIAL_CFG, 3, 1, 0)
+       AC97_SINGLE("Exchange Center/LFE", AC97_AD_SERIAL_CFG, 3, 1, 0),
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .name = "Exchange Front/Surround",
+               .info = snd_ac97_ad1888_lohpsel_info,
+               .get = snd_ac97_ad1888_lohpsel_get,
+               .put = snd_ac97_ad1888_lohpsel_put
+       },
+       AC97_SINGLE("High Pass Filter Enable", AC97_AD_TEST2, 12, 1, 1),
+       AC97_SINGLE("Spread Front to Surround and Center/LFE",
+                   AC97_AD_MISC, 7, 1, 0),
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .name = "Downmix",
+               .info = snd_ac97_ad1888_downmix_info,
+               .get = snd_ac97_ad1888_downmix_get,
+               .put = snd_ac97_ad1888_downmix_put
+       },
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .name = "V_REFOUT",
+               .info = snd_ac97_ad1985_vrefout_info,
+               .get = snd_ac97_ad1985_vrefout_get,
+               .put = snd_ac97_ad1985_vrefout_put
+       },
+       AC97_SURROUND_JACK_MODE_CTL,
+       AC97_CHANNEL_MODE_CTL,
+
+       AC97_SINGLE("Headphone Jack Sense", AC97_AD_JACK_SPDIF, 10, 1, 0),
+       AC97_SINGLE("Line Jack Sense", AC97_AD_JACK_SPDIF, 12, 1, 0),
 };
 
 static void ad1985_update_jacks(struct snd_ac97 *ac97)
@@ -1967,9 +2142,16 @@ static int patch_ad1985_specific(struct snd_ac97 *ac97)
 {
        int err;
 
-       if ((err = patch_ad1980_specific(ac97)) < 0)
+       /* rename 0x04 as "Master" and 0x02 as "Master Surround" */
+       snd_ac97_rename_vol_ctl(ac97, "Master Playback",
+                               "Master Surround Playback");
+       snd_ac97_rename_vol_ctl(ac97, "Headphone Playback", "Master Playback");
+
+       if ((err = patch_build_controls(ac97, &snd_ac97_ad198x_2cmic, 1)) < 0)
                return err;
-       return patch_build_controls(ac97, snd_ac97_ad1985_controls, ARRAY_SIZE(snd_ac97_ad1985_controls));
+
+       return patch_build_controls(ac97, snd_ac97_ad1985_controls,
+                                   ARRAY_SIZE(snd_ac97_ad1985_controls));
 }
 
 static struct snd_ac97_build_ops patch_ad1985_build_ops = {
@@ -1989,24 +2171,311 @@ int patch_ad1985(struct snd_ac97 * ac97)
        ac97->build_ops = &patch_ad1985_build_ops;
        misc = snd_ac97_read(ac97, AC97_AD_MISC);
        /* switch front/surround line-out/hp-out */
-       /* center/LFE, mic in 3.75V mode */
        /* AD-compatible mode */
        /* Stereo mutes enabled */
-       /* in accordance with ADI driver: misc | 0x5c28 */
        snd_ac97_write_cache(ac97, AC97_AD_MISC, misc |
-                            AC97_AD198X_VREFH |
                             AC97_AD198X_LOSEL |
                             AC97_AD198X_HPSEL |
-                            AC97_AD198X_CLDIS |
-                            AC97_AD198X_LODIS |
                             AC97_AD198X_MSPLT |
                             AC97_AD198X_AC97NC);
        ac97->flags |= AC97_STEREO_MUTES;
+
+       /* update current jack configuration */
+       ad1985_update_jacks(ac97);
+
        /* on AD1985 rev. 3, AC'97 revision bits are zero */
        ac97->ext_id = (ac97->ext_id & ~AC97_EI_REV_MASK) | AC97_EI_REV_23;
        return 0;
 }
 
+static int snd_ac97_ad1986_bool_info(struct snd_kcontrol *kcontrol,
+                                    struct snd_ctl_elem_info *uinfo)
+{
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+       uinfo->count = 1;
+       uinfo->value.integer.min = 0;
+       uinfo->value.integer.max = 1;
+       return 0;
+}
+
+static int snd_ac97_ad1986_lososel_get(struct snd_kcontrol *kcontrol,
+                                      struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol);
+       unsigned short val;
+
+       val = ac97->regs[AC97_AD_MISC3];
+       ucontrol->value.integer.value[0] = (val & AC97_AD1986_LOSEL) != 0;
+       return 0;
+}
+
+static int snd_ac97_ad1986_lososel_put(struct snd_kcontrol *kcontrol,
+                                      struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol);
+       int ret0;
+       int ret1;
+       int sprd = (ac97->regs[AC97_AD_MISC] & AC97_AD1986_SPRD) != 0;
+
+       ret0 = snd_ac97_update_bits(ac97, AC97_AD_MISC3, AC97_AD1986_LOSEL,
+                                       ucontrol->value.integer.value[0] != 0
+                                   ? AC97_AD1986_LOSEL : 0);
+       if (ret0 < 0)
+               return ret0;
+
+       /* SOSEL is set to values of "Spread" or "Exchange F/S" controls */
+       ret1 = snd_ac97_update_bits(ac97, AC97_AD_MISC, AC97_AD1986_SOSEL,
+                                   (ucontrol->value.integer.value[0] != 0
+                                    || sprd)
+                                   ? AC97_AD1986_SOSEL : 0);
+       if (ret1 < 0)
+               return ret1;
+
+       return (ret0 > 0 || ret1 > 0) ? 1 : 0;
+}
+
+static int snd_ac97_ad1986_spread_get(struct snd_kcontrol *kcontrol,
+                                     struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol);
+       unsigned short val;
+
+       val = ac97->regs[AC97_AD_MISC];
+       ucontrol->value.integer.value[0] = (val & AC97_AD1986_SPRD) != 0;
+       return 0;
+}
+
+static int snd_ac97_ad1986_spread_put(struct snd_kcontrol *kcontrol,
+                                     struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol);
+       int ret0;
+       int ret1;
+       int sprd = (ac97->regs[AC97_AD_MISC3] & AC97_AD1986_LOSEL) != 0;
+
+       ret0 = snd_ac97_update_bits(ac97, AC97_AD_MISC, AC97_AD1986_SPRD,
+                                       ucontrol->value.integer.value[0] != 0
+                                   ? AC97_AD1986_SPRD : 0);
+       if (ret0 < 0)
+               return ret0;
+
+       /* SOSEL is set to values of "Spread" or "Exchange F/S" controls */
+       ret1 = snd_ac97_update_bits(ac97, AC97_AD_MISC, AC97_AD1986_SOSEL,
+                                   (ucontrol->value.integer.value[0] != 0
+                                    || sprd)
+                                   ? AC97_AD1986_SOSEL : 0);
+       if (ret1 < 0)
+               return ret1;
+
+       return (ret0 > 0 || ret1 > 0) ? 1 : 0;
+}
+
+static int snd_ac97_ad1986_miclisel_get(struct snd_kcontrol *kcontrol,
+                                       struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol);
+
+       ucontrol->value.integer.value[0] = ac97->spec.ad18xx.swap_mic_linein;
+       return 0;
+}
+
+static int snd_ac97_ad1986_miclisel_put(struct snd_kcontrol *kcontrol,
+                                       struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol);
+       unsigned char swap = ucontrol->value.integer.value[0] != 0;
+
+       if (swap != ac97->spec.ad18xx.swap_mic_linein) {
+               ac97->spec.ad18xx.swap_mic_linein = swap;
+               if (ac97->build_ops->update_jacks)
+                       ac97->build_ops->update_jacks(ac97);
+               return 1;
+       }
+       return 0;
+}
+
+static int snd_ac97_ad1986_vrefout_get(struct snd_kcontrol *kcontrol,
+                                      struct snd_ctl_elem_value *ucontrol)
+{
+       /* Use MIC_1/2 V_REFOUT as the "get" value */
+       struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol);
+       unsigned short val;
+       unsigned short reg = ac97->regs[AC97_AD_MISC2];
+       if ((reg & AC97_AD1986_MVREF0) != 0)
+               val = 2;
+       else if ((reg & AC97_AD1986_MVREF1) != 0)
+               val = 3;
+       else if ((reg & AC97_AD1986_MVREF2) != 0)
+               val = 1;
+       else
+               val = 0;
+       ucontrol->value.enumerated.item[0] = val;
+       return 0;
+}
+
+static int snd_ac97_ad1986_vrefout_put(struct snd_kcontrol *kcontrol,
+                                      struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol);
+       unsigned short cval;
+       unsigned short lval;
+       unsigned short mval;
+       int cret;
+       int lret;
+       int mret;
+
+       switch (ucontrol->value.enumerated.item[0])
+       {
+       case 0: /* High-Z */
+               cval = 0;
+               lval = 0;
+               mval = 0;
+               break;
+       case 1: /* 3.7 V */
+               cval = AC97_AD1986_CVREF2;
+               lval = AC97_AD1986_LVREF2;
+               mval = AC97_AD1986_MVREF2;
+               break;
+       case 2: /* 2.25 V */
+               cval = AC97_AD1986_CVREF0;
+               lval = AC97_AD1986_LVREF0;
+               mval = AC97_AD1986_MVREF0;
+               break;
+       case 3: /* 0 V */
+               cval = AC97_AD1986_CVREF1;
+               lval = AC97_AD1986_LVREF1;
+               mval = AC97_AD1986_MVREF1;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       cret = snd_ac97_update_bits(ac97, AC97_AD_MISC2,
+                                   AC97_AD1986_CVREF_MASK, cval);
+       if (cret < 0)
+               return cret;
+       lret = snd_ac97_update_bits(ac97, AC97_AD_MISC3,
+                                   AC97_AD1986_LVREF_MASK, lval);
+       if (lret < 0)
+               return lret;
+       mret = snd_ac97_update_bits(ac97, AC97_AD_MISC2,
+                                   AC97_AD1986_MVREF_MASK, mval);
+       if (mret < 0)
+               return mret;
+
+       return (cret > 0 || lret > 0 || mret > 0) ? 1 : 0;
+}
+
+static const struct snd_kcontrol_new snd_ac97_ad1986_controls[] = {
+       AC97_SINGLE("Exchange Center/LFE", AC97_AD_SERIAL_CFG, 3, 1, 0),
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .name = "Exchange Front/Surround",
+               .info = snd_ac97_ad1986_bool_info,
+               .get = snd_ac97_ad1986_lososel_get,
+               .put = snd_ac97_ad1986_lososel_put
+       },
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .name = "Exchange Mic/Line In",
+               .info = snd_ac97_ad1986_bool_info,
+               .get = snd_ac97_ad1986_miclisel_get,
+               .put = snd_ac97_ad1986_miclisel_put
+       },
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .name = "Spread Front to Surround and Center/LFE",
+               .info = snd_ac97_ad1986_bool_info,
+               .get = snd_ac97_ad1986_spread_get,
+               .put = snd_ac97_ad1986_spread_put
+       },
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .name = "Downmix",
+               .info = snd_ac97_ad1888_downmix_info,
+               .get = snd_ac97_ad1888_downmix_get,
+               .put = snd_ac97_ad1888_downmix_put
+       },
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .name = "V_REFOUT",
+               .info = snd_ac97_ad1985_vrefout_info,
+               .get = snd_ac97_ad1986_vrefout_get,
+               .put = snd_ac97_ad1986_vrefout_put
+       },
+       AC97_SURROUND_JACK_MODE_CTL,
+       AC97_CHANNEL_MODE_CTL,
+
+       AC97_SINGLE("Headphone Jack Sense", AC97_AD_JACK_SPDIF, 10, 1, 0),
+       AC97_SINGLE("Line Jack Sense", AC97_AD_JACK_SPDIF, 12, 1, 0)
+};
+
+static void ad1986_update_jacks(struct snd_ac97 *ac97)
+{
+       unsigned short misc_val = 0;
+       unsigned short ser_val;
+
+       /* disable SURROUND and CENTER/LFE if not surround mode */
+       if (! is_surround_on(ac97))
+               misc_val |= AC97_AD1986_SODIS;
+       if (! is_clfe_on(ac97))
+               misc_val |= AC97_AD1986_CLDIS;
+
+       /* select line input (default=LINE_IN, SURROUND or MIC_1/2) */
+       if (is_shared_linein(ac97))
+               misc_val |= AC97_AD1986_LISEL_SURR;
+       else if (ac97->spec.ad18xx.swap_mic_linein != 0)
+               misc_val |= AC97_AD1986_LISEL_MIC;
+       snd_ac97_update_bits(ac97, AC97_AD_MISC,
+                            AC97_AD1986_SODIS | AC97_AD1986_CLDIS |
+                            AC97_AD1986_LISEL_MASK,
+                            misc_val);
+
+       /* select microphone input (MIC_1/2, Center/LFE or LINE_IN) */
+       if (is_shared_micin(ac97))
+               ser_val = AC97_AD1986_OMS_C;
+       else if (ac97->spec.ad18xx.swap_mic_linein != 0)
+               ser_val = AC97_AD1986_OMS_L;
+       else
+               ser_val = AC97_AD1986_OMS_M;
+       snd_ac97_update_bits(ac97, AC97_AD_SERIAL_CFG,
+                            AC97_AD1986_OMS_MASK,
+                            ser_val);
+}
+
+static int patch_ad1986_specific(struct snd_ac97 *ac97)
+{
+       int err;
+
+       if ((err = patch_build_controls(ac97, &snd_ac97_ad198x_2cmic, 1)) < 0)
+               return err;
+
+       return patch_build_controls(ac97, snd_ac97_ad1986_controls,
+                                   ARRAY_SIZE(snd_ac97_ad1985_controls));
+}
+
+static struct snd_ac97_build_ops patch_ad1986_build_ops = {
+       .build_post_spdif = patch_ad198x_post_spdif,
+       .build_specific = patch_ad1986_specific,
+#ifdef CONFIG_PM
+       .resume = ad18xx_resume,
+#endif
+       .update_jacks = ad1986_update_jacks,
+};
+
+int patch_ad1986(struct snd_ac97 * ac97)
+{
+       patch_ad1881(ac97);
+       ac97->build_ops = &patch_ad1986_build_ops;
+       ac97->flags |= AC97_STEREO_MUTES;
+
+       /* update current jack configuration */
+       ad1986_update_jacks(ac97);
+
+       return 0;
+}
+
+
 /*
  * realtek ALC65x/850 codecs
  */
@@ -2014,12 +2483,12 @@ static void alc650_update_jacks(struct snd_ac97 *ac97)
 {
        int shared;
        
-       /* shared Line-In */
-       shared = is_shared_linein(ac97);
+       /* shared Line-In / Surround Out */
+       shared = is_shared_surrout(ac97);
        snd_ac97_update_bits(ac97, AC97_ALC650_MULTICH, 1 << 9,
                             shared ? (1 << 9) : 0);
-       /* update shared Mic */
-       shared = is_shared_micin(ac97);
+       /* update shared Mic In / Center/LFE Out */
+       shared = is_shared_clfeout(ac97);
        /* disable/enable vref */
        snd_ac97_update_bits(ac97, AC97_ALC650_CLOCK, 1 << 12,
                             shared ? (1 << 12) : 0);
@@ -2064,7 +2533,7 @@ static const struct snd_kcontrol_new snd_ac97_spdif_controls_alc650[] = {
        /* AC97_SINGLE("IEC958 Input Monitor", AC97_ALC650_MULTICH, 13, 1, 0), */
 };
 
-static DECLARE_TLV_DB_SCALE(db_scale_5bit_3db_max, -4350, 150, 0);
+static const DECLARE_TLV_DB_SCALE(db_scale_5bit_3db_max, -4350, 150, 0);
 
 static int patch_alc650_specific(struct snd_ac97 * ac97)
 {
@@ -2149,12 +2618,12 @@ static void alc655_update_jacks(struct snd_ac97 *ac97)
 {
        int shared;
        
-       /* shared Line-In */
-       shared = is_shared_linein(ac97);
+       /* shared Line-In / Surround Out */
+       shared = is_shared_surrout(ac97);
        ac97_update_bits_page(ac97, AC97_ALC650_MULTICH, 1 << 9,
                              shared ? (1 << 9) : 0, 0);
-       /* update shared mic */
-       shared = is_shared_micin(ac97);
+       /* update shared Mic In / Center/LFE Out */
+       shared = is_shared_clfeout(ac97);
        /* misc control; vrefout disable */
        snd_ac97_update_bits(ac97, AC97_ALC650_CLOCK, 1 << 12,
                             shared ? (1 << 12) : 0);
@@ -2264,7 +2733,8 @@ int patch_alc655(struct snd_ac97 * ac97)
                if (ac97->subsystem_vendor == 0x1462 &&
                    (ac97->subsystem_device == 0x0131 || /* MSI S270 laptop */
                     ac97->subsystem_device == 0x0161 || /* LG K1 Express */
-                    ac97->subsystem_device == 0x0351))  /* MSI L725 laptop */
+                    ac97->subsystem_device == 0x0351 || /* MSI L725 laptop */
+                    ac97->subsystem_device == 0x0061))  /* MSI S250 laptop */
                        val &= ~(1 << 1); /* Pin 47 is EAPD (for internal speaker) */
                else
                        val |= (1 << 1); /* Pin 47 is spdif input pin */
@@ -2297,16 +2767,16 @@ static void alc850_update_jacks(struct snd_ac97 *ac97)
 {
        int shared;
        
-       /* shared Line-In */
-       shared = is_shared_linein(ac97);
+       /* shared Line-In / Surround Out */
+       shared = is_shared_surrout(ac97);
        /* SURR 1kOhm (bit4), Amp (bit5) */
        snd_ac97_update_bits(ac97, AC97_ALC850_MISC1, (1<<4)|(1<<5),
                             shared ? (1<<5) : (1<<4));
        /* LINE-IN = 0, SURROUND = 2 */
        snd_ac97_update_bits(ac97, AC97_ALC850_JACK_SELECT, 7 << 12,
                             shared ? (2<<12) : (0<<12));
-       /* update shared mic */
-       shared = is_shared_micin(ac97);
+       /* update shared Mic In / Center/LFE Out */
+       shared = is_shared_clfeout(ac97);
        /* Vref disable (bit12), 1kOhm (bit13) */
        snd_ac97_update_bits(ac97, AC97_ALC850_MISC1, (1<<12)|(1<<13),
                             shared ? (1<<12) : (1<<13));
@@ -2379,9 +2849,9 @@ int patch_alc850(struct snd_ac97 *ac97)
  */
 static void cm9738_update_jacks(struct snd_ac97 *ac97)
 {
-       /* shared Line-In */
+       /* shared Line-In / Surround Out */
        snd_ac97_update_bits(ac97, AC97_CM9738_VENDOR_CTRL, 1 << 10,
-                            is_shared_linein(ac97) ? (1 << 10) : 0);
+                            is_shared_surrout(ac97) ? (1 << 10) : 0);
 }
 
 static const struct snd_kcontrol_new snd_ac97_cm9738_controls[] = {
@@ -2463,12 +2933,12 @@ static const struct snd_kcontrol_new snd_ac97_cm9739_controls_spdif[] = {
 
 static void cm9739_update_jacks(struct snd_ac97 *ac97)
 {
-       /* shared Line-In */
+       /* shared Line-In / Surround Out */
        snd_ac97_update_bits(ac97, AC97_CM9739_MULTI_CHAN, 1 << 10,
-                            is_shared_linein(ac97) ? (1 << 10) : 0);
-       /* shared Mic */
+                            is_shared_surrout(ac97) ? (1 << 10) : 0);
+       /* shared Mic In / Center/LFE Out **/
        snd_ac97_update_bits(ac97, AC97_CM9739_MULTI_CHAN, 0x3000,
-                            is_shared_micin(ac97) ? 0x1000 : 0x2000);
+                            is_shared_clfeout(ac97) ? 0x1000 : 0x2000);
 }
 
 static const struct snd_kcontrol_new snd_ac97_cm9739_controls[] = {
@@ -2580,8 +3050,8 @@ static void cm9761_update_jacks(struct snd_ac97 *ac97)
 
        val |= surr_on[ac97->spec.dev_flags][is_surround_on(ac97)];
        val |= clfe_on[ac97->spec.dev_flags][is_clfe_on(ac97)];
-       val |= surr_shared[ac97->spec.dev_flags][is_shared_linein(ac97)];
-       val |= clfe_shared[ac97->spec.dev_flags][is_shared_micin(ac97)];
+       val |= surr_shared[ac97->spec.dev_flags][is_shared_surrout(ac97)];
+       val |= clfe_shared[ac97->spec.dev_flags][is_shared_clfeout(ac97)];
 
        snd_ac97_update_bits(ac97, AC97_CM9761_MULTI_CHAN, 0x3c88, val);
 }
@@ -2821,6 +3291,7 @@ int patch_vt1617a(struct snd_ac97 * ac97)
        snd_ac97_write_cache(ac97, 0x5c, 0x20);
        ac97->ext_id |= AC97_EI_SPDIF;  /* force the detection of spdif */
        ac97->rates[AC97_RATES_SPDIF] = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000;
+       ac97->build_ops = &patch_vt1616_ops;
        return 0;
 }
 
@@ -2828,12 +3299,12 @@ int patch_vt1617a(struct snd_ac97 * ac97)
  */
 static void it2646_update_jacks(struct snd_ac97 *ac97)
 {
-       /* shared Line-In */
+       /* shared Line-In / Surround Out */
        snd_ac97_update_bits(ac97, 0x76, 1 << 9,
-                            is_shared_linein(ac97) ? (1<<9) : 0);
-       /* shared Mic */
+                            is_shared_surrout(ac97) ? (1<<9) : 0);
+       /* shared Mic / Center/LFE Out */
        snd_ac97_update_bits(ac97, 0x76, 1 << 10,
-                            is_shared_micin(ac97) ? (1<<10) : 0);
+                            is_shared_clfeout(ac97) ? (1<<10) : 0);
 }
 
 static const struct snd_kcontrol_new snd_ac97_controls_it2646[] = {