Merge branch 'topic/hda' into for-next
authorTakashi Iwai <tiwai@suse.de>
Wed, 18 Jul 2012 11:53:06 +0000 (13:53 +0200)
committerTakashi Iwai <tiwai@suse.de>
Wed, 18 Jul 2012 11:53:06 +0000 (13:53 +0200)
This is a large chunk merge for 3.6 HD-audio things

1  2 
sound/pci/hda/patch_realtek.c

index aa4c25e0f3277fce38520c72c9759bfc90b9022d,aef31392aee1b5ea4bbc8552156eea9f98a2226e..ec14224021aac76bd0a49c460b4ac6cab8f62a5c
@@@ -170,10 -170,10 +170,10 @@@ struct alc_spec 
        hda_nid_t imux_pins[HDA_MAX_NUM_INPUTS];
        unsigned int dyn_adc_idx[HDA_MAX_NUM_INPUTS];
        int int_mic_idx, ext_mic_idx, dock_mic_idx; /* for auto-mic */
+       hda_nid_t inv_dmic_pin;
  
        /* hooks */
        void (*init_hook)(struct hda_codec *codec);
-       void (*unsol_event)(struct hda_codec *codec, unsigned int res);
  #ifdef CONFIG_SND_HDA_POWER_SAVE
        void (*power_hook)(struct hda_codec *codec);
  #endif
        unsigned int vol_in_capsrc:1; /* use capsrc volume (ADC has no vol) */
        unsigned int parse_flags; /* passed to snd_hda_parse_pin_defcfg() */
        unsigned int shared_mic_hp:1; /* HP/Mic-in sharing */
+       unsigned int inv_dmic_fixup:1; /* has inverted digital-mic workaround */
+       unsigned int inv_dmic_muted:1; /* R-ch of inv d-mic is muted? */
  
        /* auto-mute control */
        int automute_mode;
@@@ -298,6 -300,39 +300,39 @@@ static inline hda_nid_t get_capsrc(stru
  }
  
  static void call_update_outputs(struct hda_codec *codec);
+ static void alc_inv_dmic_sync(struct hda_codec *codec, bool force);
+ /* for shared I/O, change the pin-control accordingly */
+ static void update_shared_mic_hp(struct hda_codec *codec, bool set_as_mic)
+ {
+       struct alc_spec *spec = codec->spec;
+       unsigned int val;
+       hda_nid_t pin = spec->autocfg.inputs[1].pin;
+       /* NOTE: this assumes that there are only two inputs, the
+        * first is the real internal mic and the second is HP/mic jack.
+        */
+       val = snd_hda_get_default_vref(codec, pin);
+       /* This pin does not have vref caps - let's enable vref on pin 0x18
+          instead, as suggested by Realtek */
+       if (val == AC_PINCTL_VREF_HIZ) {
+               const hda_nid_t vref_pin = 0x18;
+               /* Sanity check pin 0x18 */
+               if (get_wcaps_type(get_wcaps(codec, vref_pin)) == AC_WID_PIN &&
+                   get_defcfg_connect(snd_hda_codec_get_pincfg(codec, vref_pin)) == AC_JACK_PORT_NONE) {
+                       unsigned int vref_val = snd_hda_get_default_vref(codec, vref_pin);
+                       if (vref_val != AC_PINCTL_VREF_HIZ)
+                               snd_hda_set_pin_ctl(codec, vref_pin, PIN_IN | (set_as_mic ? vref_val : 0));
+               }
+       }
+       val = set_as_mic ? val | PIN_IN : PIN_HP;
+       snd_hda_set_pin_ctl(codec, pin, val);
+       spec->automute_speaker = !set_as_mic;
+       call_update_outputs(codec);
+ }
  
  /* select the given imux item; either unmute exclusively or select the route */
  static int alc_mux_select(struct hda_codec *codec, unsigned int adc_idx,
                return 0;
        spec->cur_mux[adc_idx] = idx;
  
-       /* for shared I/O, change the pin-control accordingly */
-       if (spec->shared_mic_hp) {
-               unsigned int val;
-               hda_nid_t pin = spec->autocfg.inputs[1].pin;
-               /* NOTE: this assumes that there are only two inputs, the
-                * first is the real internal mic and the second is HP jack.
-                */
-               if (spec->cur_mux[adc_idx])
-                       val = snd_hda_get_default_vref(codec, pin) | PIN_IN;
-               else
-                       val = PIN_HP;
-               snd_hda_set_pin_ctl(codec, pin, val);
-               spec->automute_speaker = !spec->cur_mux[adc_idx];
-               call_update_outputs(codec);
-       }
+       if (spec->shared_mic_hp)
+               update_shared_mic_hp(codec, spec->cur_mux[adc_idx]);
  
        if (spec->dyn_adc_switch) {
                alc_dyn_adc_pcm_resetup(codec, idx);
                                          AC_VERB_SET_CONNECT_SEL,
                                          imux->items[idx].index);
        }
+       alc_inv_dmic_sync(codec, true);
        return 1;
  }
  
@@@ -664,7 -687,7 +687,7 @@@ static void alc_update_knob_master(stru
  }
  
  /* unsolicited event for HP jack sensing */
- static void alc_sku_unsol_event(struct hda_codec *codec, unsigned int res)
+ static void alc_unsol_event(struct hda_codec *codec, unsigned int res)
  {
        int action;
  
@@@ -1000,11 -1023,9 +1023,9 @@@ static void alc_init_automute(struct hd
        spec->automute_lo = spec->automute_lo_possible;
        spec->automute_speaker = spec->automute_speaker_possible;
  
-       if (spec->automute_speaker_possible || spec->automute_lo_possible) {
+       if (spec->automute_speaker_possible || spec->automute_lo_possible)
                /* create a control for automute mode */
                alc_add_automute_mode_enum(codec);
-               spec->unsol_event = alc_sku_unsol_event;
-       }
  }
  
  /* return the position of NID in the list, or -1 if not found */
@@@ -1167,7 -1188,6 +1188,6 @@@ static void alc_init_auto_mic(struct hd
  
        snd_printdd("realtek: Enable auto-mic switch on NID 0x%x/0x%x/0x%x\n",
                    ext, fixed, dock);
-       spec->unsol_event = alc_sku_unsol_event;
  }
  
  /* check the availabilities of auto-mute and auto-mic switches */
@@@ -1556,14 -1576,14 +1576,14 @@@ typedef int (*getput_call_t)(struct snd
  
  static int alc_cap_getput_caller(struct snd_kcontrol *kcontrol,
                                 struct snd_ctl_elem_value *ucontrol,
-                                getput_call_t func, bool check_adc_switch)
+                                getput_call_t func, bool is_put)
  {
        struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
        struct alc_spec *spec = codec->spec;
        int i, err = 0;
  
        mutex_lock(&codec->control_mutex);
-       if (check_adc_switch && spec->dyn_adc_switch) {
+       if (is_put && spec->dyn_adc_switch) {
                for (i = 0; i < spec->num_adc_nids; i++) {
                        kcontrol->private_value =
                                HDA_COMPOSE_AMP_VAL(spec->adc_nids[i],
                                                    3, 0, HDA_INPUT);
                err = func(kcontrol, ucontrol);
        }
+       if (err >= 0 && is_put)
+               alc_inv_dmic_sync(codec, false);
   error:
        mutex_unlock(&codec->control_mutex);
        return err;
@@@ -1675,6 -1697,116 +1697,116 @@@ DEFINE_CAPMIX_NOSRC(1)
  DEFINE_CAPMIX_NOSRC(2);
  DEFINE_CAPMIX_NOSRC(3);
  
+ /*
+  * Inverted digital-mic handling
+  *
+  * First off, it's a bit tricky.  The "Inverted Internal Mic Capture Switch"
+  * gives the additional mute only to the right channel of the digital mic
+  * capture stream.  This is a workaround for avoiding the almost silence
+  * by summing the stereo stream from some (known to be ForteMedia)
+  * digital mic unit.
+  *
+  * The logic is to call alc_inv_dmic_sync() after each action (possibly)
+  * modifying ADC amp.  When the mute flag is set, it mutes the R-channel
+  * without caching so that the cache can still keep the original value.
+  * The cached value is then restored when the flag is set off or any other
+  * than d-mic is used as the current input source.
+  */
+ static void alc_inv_dmic_sync(struct hda_codec *codec, bool force)
+ {
+       struct alc_spec *spec = codec->spec;
+       int i;
+       if (!spec->inv_dmic_fixup)
+               return;
+       if (!spec->inv_dmic_muted && !force)
+               return;
+       for (i = 0; i < spec->num_adc_nids; i++) {
+               int src = spec->dyn_adc_switch ? 0 : i;
+               bool dmic_fixup = false;
+               hda_nid_t nid;
+               int parm, dir, v;
+               if (spec->inv_dmic_muted &&
+                   spec->imux_pins[spec->cur_mux[src]] == spec->inv_dmic_pin)
+                       dmic_fixup = true;
+               if (!dmic_fixup && !force)
+                       continue;
+               if (spec->vol_in_capsrc) {
+                       nid = spec->capsrc_nids[i];
+                       parm = AC_AMP_SET_RIGHT | AC_AMP_SET_OUTPUT;
+                       dir = HDA_OUTPUT;
+               } else {
+                       nid = spec->adc_nids[i];
+                       parm = AC_AMP_SET_RIGHT | AC_AMP_SET_INPUT;
+                       dir = HDA_INPUT;
+               }
+               /* we care only right channel */
+               v = snd_hda_codec_amp_read(codec, nid, 1, dir, 0);
+               if (v & 0x80) /* if already muted, we don't need to touch */
+                       continue;
+               if (dmic_fixup) /* add mute for d-mic */
+                       v |= 0x80;
+               snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
+                                   parm | v);
+       }
+ }
+ static int alc_inv_dmic_sw_get(struct snd_kcontrol *kcontrol,
+                              struct snd_ctl_elem_value *ucontrol)
+ {
+       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct alc_spec *spec = codec->spec;
+       ucontrol->value.integer.value[0] = !spec->inv_dmic_muted;
+       return 0;
+ }
+ static int alc_inv_dmic_sw_put(struct snd_kcontrol *kcontrol,
+                              struct snd_ctl_elem_value *ucontrol)
+ {
+       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct alc_spec *spec = codec->spec;
+       unsigned int val = !ucontrol->value.integer.value[0];
+       if (val == spec->inv_dmic_muted)
+               return 0;
+       spec->inv_dmic_muted = val;
+       alc_inv_dmic_sync(codec, true);
+       return 0;
+ }
+ static const struct snd_kcontrol_new alc_inv_dmic_sw = {
+       .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+       .info = snd_ctl_boolean_mono_info,
+       .get = alc_inv_dmic_sw_get,
+       .put = alc_inv_dmic_sw_put,
+ };
+ static int alc_add_inv_dmic_mixer(struct hda_codec *codec, hda_nid_t nid)
+ {
+       struct alc_spec *spec = codec->spec;
+       struct snd_kcontrol_new *knew = alc_kcontrol_new(spec);
+       if (!knew)
+               return -ENOMEM;
+       *knew = alc_inv_dmic_sw;
+       knew->name = kstrdup("Inverted Internal Mic Capture Switch", GFP_KERNEL);
+       if (!knew->name)
+               return -ENOMEM;
+       spec->inv_dmic_fixup = 1;
+       spec->inv_dmic_muted = 0;
+       spec->inv_dmic_pin = nid;
+       return 0;
+ }
+ /* typically the digital mic is put at node 0x12 */
+ static void alc_fixup_inv_dmic_0x12(struct hda_codec *codec,
+                                   const struct alc_fixup *fix, int action)
+ {
+       if (action == ALC_FIXUP_ACT_PROBE)
+               alc_add_inv_dmic_mixer(codec, 0x12);
+ }
  /*
   * virtual master controls
   */
@@@ -1865,13 -1997,31 +1997,31 @@@ static int __alc_build_controls(struct 
        return 0;
  }
  
- static int alc_build_controls(struct hda_codec *codec)
+ static int alc_build_jacks(struct hda_codec *codec)
  {
        struct alc_spec *spec = codec->spec;
+       if (spec->shared_mic_hp) {
+               int err;
+               int nid = spec->autocfg.inputs[1].pin;
+               err = snd_hda_jack_add_kctl(codec, nid, "Headphone Mic", 0);
+               if (err < 0)
+                       return err;
+               err = snd_hda_jack_detect_enable(codec, nid, 0);
+               if (err < 0)
+                       return err;
+       }
+       return snd_hda_jack_add_kctls(codec, &spec->autocfg);
+ }
+ static int alc_build_controls(struct hda_codec *codec)
+ {
        int err = __alc_build_controls(codec);
        if (err < 0)
                return err;
-       err = snd_hda_jack_add_kctls(codec, &spec->autocfg);
+       err = alc_build_jacks(codec);
        if (err < 0)
                return err;
        alc_apply_fixup(codec, ALC_FIXUP_ACT_BUILD);
@@@ -1908,14 -2058,6 +2058,6 @@@ static int alc_init(struct hda_codec *c
        return 0;
  }
  
- static void alc_unsol_event(struct hda_codec *codec, unsigned int res)
- {
-       struct alc_spec *spec = codec->spec;
-       if (spec->unsol_event)
-               spec->unsol_event(codec, res);
- }
  #ifdef CONFIG_SND_HDA_POWER_SAVE
  static int alc_check_power_status(struct hda_codec *codec, hda_nid_t nid)
  {
@@@ -2317,6 -2459,7 +2459,7 @@@ static int alc_resume(struct hda_codec 
        codec->patch_ops.init(codec);
        snd_hda_codec_resume_amp(codec);
        snd_hda_codec_resume_cache(codec);
+       alc_inv_dmic_sync(codec, true);
        hda_call_check_power_status(codec, 0x01);
        return 0;
  }
@@@ -4116,14 -4259,12 +4259,12 @@@ static void set_capture_mixer(struct hd
   */
  static void alc_auto_init_std(struct hda_codec *codec)
  {
-       struct alc_spec *spec = codec->spec;
        alc_auto_init_multi_out(codec);
        alc_auto_init_extra_out(codec);
        alc_auto_init_analog_input(codec);
        alc_auto_init_input_src(codec);
        alc_auto_init_digital(codec);
-       if (spec->unsol_event)
-               alc_inithook(codec);
+       alc_inithook(codec);
  }
  
  /*
@@@ -4724,7 -4865,6 +4865,6 @@@ static void alc260_fixup_gpio1_toggle(s
                spec->automute_speaker = 1;
                spec->autocfg.hp_pins[0] = 0x0f; /* copy it for automute */
                snd_hda_jack_detect_enable(codec, 0x0f, ALC_HP_EVENT);
-               spec->unsol_event = alc_sku_unsol_event;
                snd_hda_gen_add_verbs(&spec->gen, alc_gpio1_init_verbs);
        }
  }
@@@ -4909,6 -5049,7 +5049,7 @@@ enum 
        ALC889_FIXUP_DAC_ROUTE,
        ALC889_FIXUP_MBP_VREF,
        ALC889_FIXUP_IMAC91_VREF,
+       ALC882_FIXUP_INV_DMIC,
  };
  
  static void alc889_fixup_coef(struct hda_codec *codec,
@@@ -5212,6 -5353,10 +5353,10 @@@ static const struct alc_fixup alc882_fi
                .chained = true,
                .chain_id = ALC882_FIXUP_GPIO1,
        },
+       [ALC882_FIXUP_INV_DMIC] = {
+               .type = ALC_FIXUP_FUNC,
+               .v.func = alc_fixup_inv_dmic_0x12,
+       },
  };
  
  static const struct snd_pci_quirk alc882_fixup_tbl[] = {
@@@ -5286,6 -5431,7 +5431,7 @@@ static const struct alc_model_fixup alc
        {.id = ALC882_FIXUP_ACER_ASPIRE_4930G, .name = "acer-aspire-4930g"},
        {.id = ALC882_FIXUP_ACER_ASPIRE_8930G, .name = "acer-aspire-8930g"},
        {.id = ALC883_FIXUP_ACER_EAPD, .name = "acer-aspire"},
+       {.id = ALC882_FIXUP_INV_DMIC, .name = "inv-dmic"},
        {}
  };
  
@@@ -5373,6 -5519,7 +5519,7 @@@ enum 
        ALC262_FIXUP_LENOVO_3000,
        ALC262_FIXUP_BENQ,
        ALC262_FIXUP_BENQ_T31,
+       ALC262_FIXUP_INV_DMIC,
  };
  
  static const struct alc_fixup alc262_fixups[] = {
                        {}
                }
        },
+       [ALC262_FIXUP_INV_DMIC] = {
+               .type = ALC_FIXUP_FUNC,
+               .v.func = alc_fixup_inv_dmic_0x12,
+       },
  };
  
  static const struct snd_pci_quirk alc262_fixup_tbl[] = {
        {}
  };
  
+ static const struct alc_model_fixup alc262_fixup_models[] = {
+       {.id = ALC262_FIXUP_INV_DMIC, .name = "inv-dmic"},
+       {}
+ };
  
  /*
   */
@@@ -5466,7 -5621,8 +5621,8 @@@ static int patch_alc262(struct hda_code
  #endif
        alc_fix_pll_init(codec, 0x20, 0x0a, 10);
  
-       alc_pick_fixup(codec, NULL, alc262_fixup_tbl, alc262_fixups);
+       alc_pick_fixup(codec, alc262_fixup_models, alc262_fixup_tbl,
+                      alc262_fixups);
        alc_apply_fixup(codec, ALC_FIXUP_ACT_PRE_PROBE);
  
        alc_auto_parse_customize_define(codec);
@@@ -5522,6 -5678,22 +5678,22 @@@ static const struct hda_verb alc268_bee
        { }
  };
  
+ enum {
+       ALC268_FIXUP_INV_DMIC,
+ };
+ static const struct alc_fixup alc268_fixups[] = {
+       [ALC268_FIXUP_INV_DMIC] = {
+               .type = ALC_FIXUP_FUNC,
+               .v.func = alc_fixup_inv_dmic_0x12,
+       },
+ };
+ static const struct alc_model_fixup alc268_fixup_models[] = {
+       {.id = ALC268_FIXUP_INV_DMIC, .name = "inv-dmic"},
+       {}
+ };
  /*
   * BIOS auto configuration
   */
@@@ -5553,6 -5725,9 +5725,9 @@@ static int patch_alc268(struct hda_code
  
        spec = codec->spec;
  
+       alc_pick_fixup(codec, alc268_fixup_models, NULL, alc268_fixups);
+       alc_apply_fixup(codec, ALC_FIXUP_ACT_PRE_PROBE);
        /* automatic parse from the BIOS config */
        err = alc268_parse_auto_config(codec);
        if (err < 0)
        codec->patch_ops = alc_patch_ops;
        spec->shutup = alc_eapd_shutup;
  
+       alc_apply_fixup(codec, ALC_FIXUP_ACT_PROBE);
        return 0;
  
   error:
@@@ -5810,6 -5987,7 +5987,7 @@@ static void alc269_fixup_mic2_mute(stru
        }
  }
  
  enum {
        ALC269_FIXUP_SONY_VAIO,
        ALC275_FIXUP_SONY_VAIO_GPIO2,
        ALC269VB_FIXUP_AMIC,
        ALC269VB_FIXUP_DMIC,
        ALC269_FIXUP_MIC2_MUTE_LED,
+       ALC269_FIXUP_INV_DMIC,
  };
  
  static const struct alc_fixup alc269_fixups[] = {
                .type = ALC_FIXUP_FUNC,
                .v.func = alc269_fixup_mic2_mute,
        },
+       [ALC269_FIXUP_INV_DMIC] = {
+               .type = ALC_FIXUP_FUNC,
+               .v.func = alc_fixup_inv_dmic_0x12,
+       },
  };
  
  static const struct snd_pci_quirk alc269_fixup_tbl[] = {
+       SND_PCI_QUIRK(0x1025, 0x029b, "Acer 1810TZ", ALC269_FIXUP_INV_DMIC),
+       SND_PCI_QUIRK(0x1025, 0x0349, "Acer AOD260", ALC269_FIXUP_INV_DMIC),
        SND_PCI_QUIRK(0x103c, 0x1586, "HP", ALC269_FIXUP_MIC2_MUTE_LED),
        SND_PCI_QUIRK(0x1043, 0x1427, "Asus Zenbook UX31E", ALC269VB_FIXUP_DMIC),
        SND_PCI_QUIRK(0x1043, 0x1a13, "Asus G73Jw", ALC269_FIXUP_ASUS_G73JW),
+       SND_PCI_QUIRK(0x1043, 0x1b13, "Asus U41SV", ALC269_FIXUP_INV_DMIC),
        SND_PCI_QUIRK(0x1043, 0x16e3, "ASUS UX50", ALC269_FIXUP_STEREO_DMIC),
        SND_PCI_QUIRK(0x1043, 0x831a, "ASUS P901", ALC269_FIXUP_STEREO_DMIC),
        SND_PCI_QUIRK(0x1043, 0x834a, "ASUS S101", ALC269_FIXUP_STEREO_DMIC),
  static const struct alc_model_fixup alc269_fixup_models[] = {
        {.id = ALC269_FIXUP_AMIC, .name = "laptop-amic"},
        {.id = ALC269_FIXUP_DMIC, .name = "laptop-dmic"},
+       {.id = ALC269_FIXUP_STEREO_DMIC, .name = "alc269-dmic"},
+       {.id = ALC271_FIXUP_DMIC, .name = "alc271-dmic"},
+       {.id = ALC269_FIXUP_INV_DMIC, .name = "inv-dmic"},
        {}
  };
  
@@@ -6329,12 -6518,6 +6518,6 @@@ static const struct snd_pci_quirk alc86
        {}
  };
  
- static const struct hda_verb alc660vd_eapd_verbs[] = {
-       {0x14, AC_VERB_SET_EAPD_BTLENABLE, 2},
-       {0x15, AC_VERB_SET_EAPD_BTLENABLE, 2},
-       { }
- };
  /*
   */
  static int patch_alc861vd(struct hda_codec *codec)
        if (err < 0)
                goto error;
  
-       if (codec->vendor_id == 0x10ec0660) {
-               /* always turn on EAPD */
-               snd_hda_gen_add_verbs(&spec->gen, alc660vd_eapd_verbs);
-       }
        if (!spec->no_analog) {
                err = snd_hda_attach_beep_device(codec, 0x23);
                if (err < 0)
@@@ -6443,6 -6621,7 +6621,7 @@@ enum 
        ALC662_FIXUP_ASUS_MODE8,
        ALC662_FIXUP_NO_JACK_DETECT,
        ALC662_FIXUP_ZOTAC_Z68,
+       ALC662_FIXUP_INV_DMIC,
  };
  
  static const struct alc_fixup alc662_fixups[] = {
                        { }
                }
        },
+       [ALC662_FIXUP_INV_DMIC] = {
+               .type = ALC_FIXUP_FUNC,
+               .v.func = alc_fixup_inv_dmic_0x12,
+       },
  };
  
  static const struct snd_pci_quirk alc662_fixup_tbl[] = {
        SND_PCI_QUIRK(0x1019, 0x9087, "ECS", ALC662_FIXUP_ASUS_MODE2),
        SND_PCI_QUIRK(0x1025, 0x0308, "Acer Aspire 8942G", ALC662_FIXUP_ASPIRE),
        SND_PCI_QUIRK(0x1025, 0x031c, "Gateway NV79", ALC662_FIXUP_SKU_IGNORE),
+       SND_PCI_QUIRK(0x1025, 0x0349, "eMachines eM250", ALC662_FIXUP_INV_DMIC),
        SND_PCI_QUIRK(0x1025, 0x038b, "Acer Aspire 8943G", ALC662_FIXUP_ASPIRE),
        SND_PCI_QUIRK(0x103c, 0x1632, "HP RP5800", ALC662_FIXUP_HP_RP5800),
        SND_PCI_QUIRK(0x1043, 0x8469, "ASUS mobo", ALC662_FIXUP_NO_JACK_DETECT),
@@@ -6685,34 -6869,10 +6869,35 @@@ static const struct alc_model_fixup alc
        {.id = ALC662_FIXUP_ASUS_MODE6, .name = "asus-mode6"},
        {.id = ALC662_FIXUP_ASUS_MODE7, .name = "asus-mode7"},
        {.id = ALC662_FIXUP_ASUS_MODE8, .name = "asus-mode8"},
+       {.id = ALC662_FIXUP_INV_DMIC, .name = "inv-dmic"},
        {}
  };
  
 +static void alc662_fill_coef(struct hda_codec *codec)
 +{
 +      int val, coef;
 +
 +      coef = alc_get_coef0(codec);
 +
 +      switch (codec->vendor_id) {
 +      case 0x10ec0662:
 +              if ((coef & 0x00f0) == 0x0030) {
 +                      val = alc_read_coef_idx(codec, 0x4); /* EAPD Ctrl */
 +                      alc_write_coef_idx(codec, 0x4, val & ~(1<<10));
 +              }
 +              break;
 +      case 0x10ec0272:
 +      case 0x10ec0273:
 +      case 0x10ec0663:
 +      case 0x10ec0665:
 +      case 0x10ec0670:
 +      case 0x10ec0671:
 +      case 0x10ec0672:
 +              val = alc_read_coef_idx(codec, 0xd); /* EAPD Ctrl */
 +              alc_write_coef_idx(codec, 0xd, val | (1<<14));
 +              break;
 +      }
 +}
  
  /*
   */
@@@ -6732,9 -6892,6 +6917,9 @@@ static int patch_alc662(struct hda_code
  
        alc_fix_pll_init(codec, 0x20, 0x04, 15);
  
 +      spec->init_hook = alc662_fill_coef;
 +      alc662_fill_coef(codec);
 +
        alc_pick_fixup(codec, alc662_fixup_models,
                       alc662_fixup_tbl, alc662_fixups);
        alc_apply_fixup(codec, ALC_FIXUP_ACT_PRE_PROBE);
@@@ -6831,6 -6988,7 +7016,7 @@@ static const struct hda_codec_preset sn
        { .id = 0x10ec0275, .name = "ALC275", .patch = patch_alc269 },
        { .id = 0x10ec0276, .name = "ALC276", .patch = patch_alc269 },
        { .id = 0x10ec0280, .name = "ALC280", .patch = patch_alc269 },
+       { .id = 0x10ec0282, .name = "ALC282", .patch = patch_alc269 },
        { .id = 0x10ec0861, .rev = 0x100340, .name = "ALC660",
          .patch = patch_alc861 },
        { .id = 0x10ec0660, .name = "ALC660-VD", .patch = patch_alc861vd },