Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  alc269.c   Sprache: C

 
// SPDX-License-Identifier: GPL-2.0-or-later
//
// Realtek ALC269 and compatible codecs
//

#include <linux/init.h>
#include <linux/module.h>
#include "realtek.h"

/* keep halting ALC5505 DSP, for power saving */
#define HALT_REALTEK_ALC5505

static const struct hda_pcm_stream alc269_44k_pcm_analog_playback = {
 .rates = SNDRV_PCM_RATE_44100, /* fixed rate */
};

static const struct hda_pcm_stream alc269_44k_pcm_analog_capture = {
 .rates = SNDRV_PCM_RATE_44100, /* fixed rate */
};

/* different alc269-variants */
enum {
 ALC269_TYPE_ALC269VA,
 ALC269_TYPE_ALC269VB,
 ALC269_TYPE_ALC269VC,
 ALC269_TYPE_ALC269VD,
 ALC269_TYPE_ALC280,
 ALC269_TYPE_ALC282,
 ALC269_TYPE_ALC283,
 ALC269_TYPE_ALC284,
 ALC269_TYPE_ALC293,
 ALC269_TYPE_ALC286,
 ALC269_TYPE_ALC298,
 ALC269_TYPE_ALC255,
 ALC269_TYPE_ALC256,
 ALC269_TYPE_ALC257,
 ALC269_TYPE_ALC215,
 ALC269_TYPE_ALC225,
 ALC269_TYPE_ALC245,
 ALC269_TYPE_ALC287,
 ALC269_TYPE_ALC294,
 ALC269_TYPE_ALC300,
 ALC269_TYPE_ALC623,
 ALC269_TYPE_ALC700,
};

/*
 * BIOS auto configuration
 */

static int alc269_parse_auto_config(struct hda_codec *codec)
{
 static const hda_nid_t alc269_ignore[] = { 0x1d, 0 };
 static const hda_nid_t alc269_ssids[] = { 0, 0x1b, 0x14, 0x21 };
 static const hda_nid_t alc269va_ssids[] = { 0x15, 0x1b, 0x14, 0 };
 struct alc_spec *spec = codec->spec;
 const hda_nid_t *ssids;

 switch (spec->codec_variant) {
 case ALC269_TYPE_ALC269VA:
 case ALC269_TYPE_ALC269VC:
 case ALC269_TYPE_ALC280:
 case ALC269_TYPE_ALC284:
 case ALC269_TYPE_ALC293:
  ssids = alc269va_ssids;
  break;
 case ALC269_TYPE_ALC269VB:
 case ALC269_TYPE_ALC269VD:
 case ALC269_TYPE_ALC282:
 case ALC269_TYPE_ALC283:
 case ALC269_TYPE_ALC286:
 case ALC269_TYPE_ALC298:
 case ALC269_TYPE_ALC255:
 case ALC269_TYPE_ALC256:
 case ALC269_TYPE_ALC257:
 case ALC269_TYPE_ALC215:
 case ALC269_TYPE_ALC225:
 case ALC269_TYPE_ALC245:
 case ALC269_TYPE_ALC287:
 case ALC269_TYPE_ALC294:
 case ALC269_TYPE_ALC300:
 case ALC269_TYPE_ALC623:
 case ALC269_TYPE_ALC700:
  ssids = alc269_ssids;
  break;
 default:
  ssids = alc269_ssids;
  break;
 }

 return alc_parse_auto_config(codec, alc269_ignore, ssids);
}

static const struct hda_jack_keymap alc_headset_btn_keymap[] = {
 { SND_JACK_BTN_0, KEY_PLAYPAUSE },
 { SND_JACK_BTN_1, KEY_VOICECOMMAND },
 { SND_JACK_BTN_2, KEY_VOLUMEUP },
 { SND_JACK_BTN_3, KEY_VOLUMEDOWN },
 {}
};

static void alc_headset_btn_callback(struct hda_codec *codec,
         struct hda_jack_callback *jack)
{
 int report = 0;

 if (jack->unsol_res & (7 << 13))
  report |= SND_JACK_BTN_0;

 if (jack->unsol_res  & (1 << 16 | 3 << 8))
  report |= SND_JACK_BTN_1;

 /* Volume up key */
 if (jack->unsol_res & (7 << 23))
  report |= SND_JACK_BTN_2;

 /* Volume down key */
 if (jack->unsol_res & (7 << 10))
  report |= SND_JACK_BTN_3;

 snd_hda_jack_set_button_state(codec, jack->nid, report);
}

static void alc_disable_headset_jack_key(struct hda_codec *codec)
{
 struct alc_spec *spec = codec->spec;

 if (!spec->has_hs_key)
  return;

 switch (codec->core.vendor_id) {
 case 0x10ec0215:
 case 0x10ec0225:
 case 0x10ec0285:
 case 0x10ec0287:
 case 0x10ec0295:
 case 0x10ec0289:
 case 0x10ec0299:
  alc_write_coef_idx(codec, 0x48, 0x0);
  alc_update_coef_idx(codec, 0x49, 0x0045, 0x0);
  alc_update_coef_idx(codec, 0x44, 0x0045 << 8, 0x0);
  break;
 case 0x10ec0230:
 case 0x10ec0236:
 case 0x10ec0256:
 case 0x10ec0257:
 case 0x19e58326:
  alc_write_coef_idx(codec, 0x48, 0x0);
  alc_update_coef_idx(codec, 0x49, 0x0045, 0x0);
  break;
 }
}

static void alc_enable_headset_jack_key(struct hda_codec *codec)
{
 struct alc_spec *spec = codec->spec;

 if (!spec->has_hs_key)
  return;

 switch (codec->core.vendor_id) {
 case 0x10ec0215:
 case 0x10ec0225:
 case 0x10ec0285:
 case 0x10ec0287:
 case 0x10ec0295:
 case 0x10ec0289:
 case 0x10ec0299:
  alc_write_coef_idx(codec, 0x48, 0xd011);
  alc_update_coef_idx(codec, 0x49, 0x007f, 0x0045);
  alc_update_coef_idx(codec, 0x44, 0x007f << 8, 0x0045 << 8);
  break;
 case 0x10ec0230:
 case 0x10ec0236:
 case 0x10ec0256:
 case 0x10ec0257:
 case 0x19e58326:
  alc_write_coef_idx(codec, 0x48, 0xd011);
  alc_update_coef_idx(codec, 0x49, 0x007f, 0x0045);
  break;
 }
}

static void alc_fixup_headset_jack(struct hda_codec *codec,
        const struct hda_fixup *fix, int action)
{
 struct alc_spec *spec = codec->spec;
 hda_nid_t hp_pin;

 switch (action) {
 case HDA_FIXUP_ACT_PRE_PROBE:
  spec->has_hs_key = 1;
  snd_hda_jack_detect_enable_callback(codec, 0x55,
          alc_headset_btn_callback);
  break;
 case HDA_FIXUP_ACT_BUILD:
  hp_pin = alc_get_hp_pin(spec);
  if (!hp_pin || snd_hda_jack_bind_keymap(codec, 0x55,
       alc_headset_btn_keymap,
       hp_pin))
   snd_hda_jack_add_kctl(codec, 0x55, "Headset Jack",
           false, SND_JACK_HEADSET,
           alc_headset_btn_keymap);

  alc_enable_headset_jack_key(codec);
  break;
 }
}

static void alc269vb_toggle_power_output(struct hda_codec *codec, int power_up)
{
 alc_update_coef_idx(codec, 0x04, 1 << 11, power_up ? (1 << 11) : 0);
}

static void alc269_shutup(struct hda_codec *codec)
{
 struct alc_spec *spec = codec->spec;

 if (spec->codec_variant == ALC269_TYPE_ALC269VB)
  alc269vb_toggle_power_output(codec, 0);
 if (spec->codec_variant == ALC269_TYPE_ALC269VB &&
   (alc_get_coef0(codec) & 0x00ff) == 0x018) {
  msleep(150);
 }
 alc_shutup_pins(codec);
}

static const struct coef_fw alc282_coefs[] = {
 WRITE_COEF(0x03, 0x0002), /* Power Down Control */
 UPDATE_COEF(0x05, 0xff3f, 0x0700), /* FIFO and filter clock */
 WRITE_COEF(0x07, 0x0200), /* DMIC control */
 UPDATE_COEF(0x06, 0x00f0, 0), /* Analog clock */
 UPDATE_COEF(0x08, 0xfffc, 0x0c2c), /* JD */
 WRITE_COEF(0x0a, 0xcccc), /* JD offset1 */
 WRITE_COEF(0x0b, 0xcccc), /* JD offset2 */
 WRITE_COEF(0x0e, 0x6e00), /* LDO1/2/3, DAC/ADC */
 UPDATE_COEF(0x0f, 0xf800, 0x1000), /* JD */
 UPDATE_COEF(0x10, 0xfc00, 0x0c00), /* Capless */
 WRITE_COEF(0x6f, 0x0), /* Class D test 4 */
 UPDATE_COEF(0x0c, 0xfe00, 0), /* IO power down directly */
 WRITE_COEF(0x34, 0xa0c0), /* ANC */
 UPDATE_COEF(0x16, 0x0008, 0), /* AGC MUX */
 UPDATE_COEF(0x1d, 0x00e0, 0), /* DAC simple content protection */
 UPDATE_COEF(0x1f, 0x00e0, 0), /* ADC simple content protection */
 WRITE_COEF(0x21, 0x8804), /* DAC ADC Zero Detection */
 WRITE_COEF(0x63, 0x2902), /* PLL */
 WRITE_COEF(0x68, 0xa080), /* capless control 2 */
 WRITE_COEF(0x69, 0x3400), /* capless control 3 */
 WRITE_COEF(0x6a, 0x2f3e), /* capless control 4 */
 WRITE_COEF(0x6b, 0x0), /* capless control 5 */
 UPDATE_COEF(0x6d, 0x0fff, 0x0900), /* class D test 2 */
 WRITE_COEF(0x6e, 0x110a), /* class D test 3 */
 UPDATE_COEF(0x70, 0x00f8, 0x00d8), /* class D test 5 */
 WRITE_COEF(0x71, 0x0014), /* class D test 6 */
 WRITE_COEF(0x72, 0xc2ba), /* classD OCP */
 UPDATE_COEF(0x77, 0x0f80, 0), /* classD pure DC test */
 WRITE_COEF(0x6c, 0xfc06), /* Class D amp control */
 {}
};

static void alc282_restore_default_value(struct hda_codec *codec)
{
 alc_process_coef_fw(codec, alc282_coefs);
}

static void alc282_init(struct hda_codec *codec)
{
 struct alc_spec *spec = codec->spec;
 hda_nid_t hp_pin = alc_get_hp_pin(spec);
 bool hp_pin_sense;
 int coef78;

 alc282_restore_default_value(codec);

 if (!hp_pin)
  return;
 hp_pin_sense = snd_hda_jack_detect(codec, hp_pin);
 coef78 = alc_read_coef_idx(codec, 0x78);

 /* Index 0x78 Direct Drive HP AMP LPM Control 1 */
 /* Headphone capless set to high power mode */
 alc_write_coef_idx(codec, 0x78, 0x9004);

 if (hp_pin_sense)
  msleep(2);

 snd_hda_codec_write(codec, hp_pin, 0,
       AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE);

 if (hp_pin_sense)
  msleep(85);

 snd_hda_codec_write(codec, hp_pin, 0,
       AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);

 if (hp_pin_sense)
  msleep(100);

 /* Headphone capless set to normal mode */
 alc_write_coef_idx(codec, 0x78, coef78);
}

static void alc282_shutup(struct hda_codec *codec)
{
 struct alc_spec *spec = codec->spec;
 hda_nid_t hp_pin = alc_get_hp_pin(spec);
 bool hp_pin_sense;
 int coef78;

 if (!hp_pin) {
  alc269_shutup(codec);
  return;
 }

 hp_pin_sense = snd_hda_jack_detect(codec, hp_pin);
 coef78 = alc_read_coef_idx(codec, 0x78);
 alc_write_coef_idx(codec, 0x78, 0x9004);

 if (hp_pin_sense)
  msleep(2);

 snd_hda_codec_write(codec, hp_pin, 0,
       AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE);

 if (hp_pin_sense)
  msleep(85);

 if (!spec->no_shutup_pins)
  snd_hda_codec_write(codec, hp_pin, 0,
        AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0);

 if (hp_pin_sense)
  msleep(100);

 alc_auto_setup_eapd(codec, false);
 alc_shutup_pins(codec);
 alc_write_coef_idx(codec, 0x78, coef78);
}

static const struct coef_fw alc283_coefs[] = {
 WRITE_COEF(0x03, 0x0002), /* Power Down Control */
 UPDATE_COEF(0x05, 0xff3f, 0x0700), /* FIFO and filter clock */
 WRITE_COEF(0x07, 0x0200), /* DMIC control */
 UPDATE_COEF(0x06, 0x00f0, 0), /* Analog clock */
 UPDATE_COEF(0x08, 0xfffc, 0x0c2c), /* JD */
 WRITE_COEF(0x0a, 0xcccc), /* JD offset1 */
 WRITE_COEF(0x0b, 0xcccc), /* JD offset2 */
 WRITE_COEF(0x0e, 0x6fc0), /* LDO1/2/3, DAC/ADC */
 UPDATE_COEF(0x0f, 0xf800, 0x1000), /* JD */
 UPDATE_COEF(0x10, 0xfc00, 0x0c00), /* Capless */
 WRITE_COEF(0x3a, 0x0), /* Class D test 4 */
 UPDATE_COEF(0x0c, 0xfe00, 0x0), /* IO power down directly */
 WRITE_COEF(0x22, 0xa0c0), /* ANC */
 UPDATE_COEFEX(0x53, 0x01, 0x000f, 0x0008), /* AGC MUX */
 UPDATE_COEF(0x1d, 0x00e0, 0), /* DAC simple content protection */
 UPDATE_COEF(0x1f, 0x00e0, 0), /* ADC simple content protection */
 WRITE_COEF(0x21, 0x8804), /* DAC ADC Zero Detection */
 WRITE_COEF(0x2e, 0x2902), /* PLL */
 WRITE_COEF(0x33, 0xa080), /* capless control 2 */
 WRITE_COEF(0x34, 0x3400), /* capless control 3 */
 WRITE_COEF(0x35, 0x2f3e), /* capless control 4 */
 WRITE_COEF(0x36, 0x0), /* capless control 5 */
 UPDATE_COEF(0x38, 0x0fff, 0x0900), /* class D test 2 */
 WRITE_COEF(0x39, 0x110a), /* class D test 3 */
 UPDATE_COEF(0x3b, 0x00f8, 0x00d8), /* class D test 5 */
 WRITE_COEF(0x3c, 0x0014), /* class D test 6 */
 WRITE_COEF(0x3d, 0xc2ba), /* classD OCP */
 UPDATE_COEF(0x42, 0x0f80, 0x0), /* classD pure DC test */
 WRITE_COEF(0x49, 0x0), /* test mode */
 UPDATE_COEF(0x40, 0xf800, 0x9800), /* Class D DC enable */
 UPDATE_COEF(0x42, 0xf000, 0x2000), /* DC offset */
 WRITE_COEF(0x37, 0xfc06), /* Class D amp control */
 UPDATE_COEF(0x1b, 0x8000, 0), /* HP JD control */
 {}
};

static void alc283_restore_default_value(struct hda_codec *codec)
{
 alc_process_coef_fw(codec, alc283_coefs);
}

static void alc283_init(struct hda_codec *codec)
{
 struct alc_spec *spec = codec->spec;
 hda_nid_t hp_pin = alc_get_hp_pin(spec);
 bool hp_pin_sense;

 alc283_restore_default_value(codec);

 if (!hp_pin)
  return;

 msleep(30);
 hp_pin_sense = snd_hda_jack_detect(codec, hp_pin);

 /* Index 0x43 Direct Drive HP AMP LPM Control 1 */
 /* Headphone capless set to high power mode */
 alc_write_coef_idx(codec, 0x43, 0x9004);

 snd_hda_codec_write(codec, hp_pin, 0,
       AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE);

 if (hp_pin_sense)
  msleep(85);

 snd_hda_codec_write(codec, hp_pin, 0,
       AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);

 if (hp_pin_sense)
  msleep(85);
 /* Index 0x46 Combo jack auto switch control 2 */
 /* 3k pull low control for Headset jack. */
 alc_update_coef_idx(codec, 0x46, 3 << 12, 0);
 /* Headphone capless set to normal mode */
 alc_write_coef_idx(codec, 0x43, 0x9614);
}

static void alc283_shutup(struct hda_codec *codec)
{
 struct alc_spec *spec = codec->spec;
 hda_nid_t hp_pin = alc_get_hp_pin(spec);
 bool hp_pin_sense;

 if (!hp_pin) {
  alc269_shutup(codec);
  return;
 }

 hp_pin_sense = snd_hda_jack_detect(codec, hp_pin);

 alc_write_coef_idx(codec, 0x43, 0x9004);

 /*depop hp during suspend*/
 alc_write_coef_idx(codec, 0x06, 0x2100);

 snd_hda_codec_write(codec, hp_pin, 0,
       AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE);

 if (hp_pin_sense)
  msleep(100);

 if (!spec->no_shutup_pins)
  snd_hda_codec_write(codec, hp_pin, 0,
        AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0);

 alc_update_coef_idx(codec, 0x46, 0, 3 << 12);

 if (hp_pin_sense)
  msleep(100);
 alc_auto_setup_eapd(codec, false);
 alc_shutup_pins(codec);
 alc_write_coef_idx(codec, 0x43, 0x9614);
}

static void alc256_init(struct hda_codec *codec)
{
 struct alc_spec *spec = codec->spec;
 hda_nid_t hp_pin = alc_get_hp_pin(spec);
 bool hp_pin_sense;

 if (spec->ultra_low_power) {
  alc_update_coef_idx(codec, 0x03, 1<<1, 1<<1);
  alc_update_coef_idx(codec, 0x08, 3<<2, 3<<2);
  alc_update_coef_idx(codec, 0x08, 7<<4, 0);
  alc_update_coef_idx(codec, 0x3b, 1<<15, 0);
  alc_update_coef_idx(codec, 0x0e, 7<<6, 7<<6);
  msleep(30);
 }

 if (!hp_pin)
  hp_pin = 0x21;

 msleep(30);

 hp_pin_sense = snd_hda_jack_detect(codec, hp_pin);

 if (hp_pin_sense) {
  msleep(2);
  alc_update_coefex_idx(codec, 0x57, 0x04, 0x0007, 0x1); /* Low power */

  snd_hda_codec_write(codec, hp_pin, 0,
       AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);

  msleep(75);

  snd_hda_codec_write(codec, hp_pin, 0,
       AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE);

  msleep(75);
  alc_update_coefex_idx(codec, 0x57, 0x04, 0x0007, 0x4); /* Hight power */
 }
 alc_update_coef_idx(codec, 0x46, 3 << 12, 0);
 alc_update_coefex_idx(codec, 0x53, 0x02, 0x8000, 1 << 15); /* Clear bit */
 alc_update_coefex_idx(codec, 0x53, 0x02, 0x8000, 0 << 15);
 /*
 * Expose headphone mic (or possibly Line In on some machines) instead
 * of PC Beep on 1Ah, and disable 1Ah loopback for all outputs. See
 * Documentation/sound/hd-audio/realtek-pc-beep.rst for details of
 * this register.
 */

 alc_write_coef_idx(codec, 0x36, 0x5757);
}

static void alc256_shutup(struct hda_codec *codec)
{
 struct alc_spec *spec = codec->spec;
 hda_nid_t hp_pin = alc_get_hp_pin(spec);
 bool hp_pin_sense;

 if (!hp_pin)
  hp_pin = 0x21;

 alc_update_coefex_idx(codec, 0x57, 0x04, 0x0007, 0x1); /* Low power */

 /* 3k pull low control for Headset jack. */
 /* NOTE: call this before clearing the pin, otherwise codec stalls */
 /* If disable 3k pulldown control for alc257, the Mic detection will not work correctly
 * when booting with headset plugged. So skip setting it for the codec alc257
 */

 if (spec->en_3kpull_low)
  alc_update_coef_idx(codec, 0x46, 0, 3 << 12);

 hp_pin_sense = snd_hda_jack_detect(codec, hp_pin);

 if (hp_pin_sense) {
  msleep(2);

  snd_hda_codec_write(codec, hp_pin, 0,
       AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE);

  msleep(75);

  if (!spec->no_shutup_pins)
   snd_hda_codec_write(codec, hp_pin, 0,
        AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0);

  msleep(75);
 }

 alc_auto_setup_eapd(codec, false);
 alc_shutup_pins(codec);
 if (spec->ultra_low_power) {
  msleep(50);
  alc_update_coef_idx(codec, 0x03, 1<<1, 0);
  alc_update_coef_idx(codec, 0x08, 7<<4, 7<<4);
  alc_update_coef_idx(codec, 0x08, 3<<2, 0);
  alc_update_coef_idx(codec, 0x3b, 1<<15, 1<<15);
  alc_update_coef_idx(codec, 0x0e, 7<<6, 0);
  msleep(30);
 }
}

static void alc285_hp_init(struct hda_codec *codec)
{
 struct alc_spec *spec = codec->spec;
 hda_nid_t hp_pin = alc_get_hp_pin(spec);
 int i, val;
 int coef38, coef0d, coef36;

 alc_write_coefex_idx(codec, 0x58, 0x00, 0x1888); /* write default value */
 alc_update_coef_idx(codec, 0x4a, 1<<15, 1<<15); /* Reset HP JD */
 coef38 = alc_read_coef_idx(codec, 0x38); /* Amp control */
 coef0d = alc_read_coef_idx(codec, 0x0d); /* Digital Misc control */
 coef36 = alc_read_coef_idx(codec, 0x36); /* Passthrough Control */
 alc_update_coef_idx(codec, 0x38, 1<<4, 0x0);
 alc_update_coef_idx(codec, 0x0d, 0x110, 0x0);

 alc_update_coef_idx(codec, 0x67, 0xf000, 0x3000);

 if (hp_pin)
  snd_hda_codec_write(codec, hp_pin, 0,
       AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE);

 msleep(130);
 alc_update_coef_idx(codec, 0x36, 1<<14, 1<<14);
 alc_update_coef_idx(codec, 0x36, 1<<13, 0x0);

 if (hp_pin)
  snd_hda_codec_write(codec, hp_pin, 0,
       AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0);
 msleep(10);
 alc_write_coef_idx(codec, 0x67, 0x0); /* Set HP depop to manual mode */
 alc_write_coefex_idx(codec, 0x58, 0x00, 0x7880);
 alc_write_coefex_idx(codec, 0x58, 0x0f, 0xf049);
 alc_update_coefex_idx(codec, 0x58, 0x03, 0x00f0, 0x00c0);

 alc_write_coefex_idx(codec, 0x58, 0x00, 0xf888); /* HP depop procedure start */
 val = alc_read_coefex_idx(codec, 0x58, 0x00);
 for (i = 0; i < 20 && val & 0x8000; i++) {
  msleep(50);
  val = alc_read_coefex_idx(codec, 0x58, 0x00);
 } /* Wait for depop procedure finish  */

 alc_write_coefex_idx(codec, 0x58, 0x00, val); /* write back the result */
 alc_update_coef_idx(codec, 0x38, 1<<4, coef38);
 alc_update_coef_idx(codec, 0x0d, 0x110, coef0d);
 alc_update_coef_idx(codec, 0x36, 3<<13, coef36);

 msleep(50);
 alc_update_coef_idx(codec, 0x4a, 1<<15, 0);
}

static void alc225_init(struct hda_codec *codec)
{
 struct alc_spec *spec = codec->spec;
 hda_nid_t hp_pin = alc_get_hp_pin(spec);
 bool hp1_pin_sense, hp2_pin_sense;

 if (spec->ultra_low_power) {
  alc_update_coef_idx(codec, 0x08, 0x0f << 2, 3<<2);
  alc_update_coef_idx(codec, 0x0e, 7<<6, 7<<6);
  alc_update_coef_idx(codec, 0x33, 1<<11, 0);
  msleep(30);
 }

 if (spec->codec_variant != ALC269_TYPE_ALC287 &&
  spec->codec_variant != ALC269_TYPE_ALC245)
  /* required only at boot or S3 and S4 resume time */
  if (!spec->done_hp_init ||
   is_s3_resume(codec) ||
   is_s4_resume(codec)) {
   alc285_hp_init(codec);
   spec->done_hp_init = true;
  }

 if (!hp_pin)
  hp_pin = 0x21;
 msleep(30);

 hp1_pin_sense = snd_hda_jack_detect(codec, hp_pin);
 hp2_pin_sense = snd_hda_jack_detect(codec, 0x16);

 if (hp1_pin_sense || hp2_pin_sense) {
  msleep(2);
  alc_update_coefex_idx(codec, 0x57, 0x04, 0x0007, 0x1); /* Low power */

  if (hp1_pin_sense)
   snd_hda_codec_write(codec, hp_pin, 0,
        AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
  if (hp2_pin_sense)
   snd_hda_codec_write(codec, 0x16, 0,
        AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
  msleep(75);

  if (hp1_pin_sense)
   snd_hda_codec_write(codec, hp_pin, 0,
        AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE);
  if (hp2_pin_sense)
   snd_hda_codec_write(codec, 0x16, 0,
        AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE);

  msleep(75);
  alc_update_coef_idx(codec, 0x4a, 3 << 10, 0);
  alc_update_coefex_idx(codec, 0x57, 0x04, 0x0007, 0x4); /* Hight power */
 }
}

static void alc225_shutup(struct hda_codec *codec)
{
 struct alc_spec *spec = codec->spec;
 hda_nid_t hp_pin = alc_get_hp_pin(spec);
 bool hp1_pin_sense, hp2_pin_sense;

 if (!hp_pin)
  hp_pin = 0x21;

 hp1_pin_sense = snd_hda_jack_detect(codec, hp_pin);
 hp2_pin_sense = snd_hda_jack_detect(codec, 0x16);

 if (hp1_pin_sense || hp2_pin_sense) {
  alc_disable_headset_jack_key(codec);
  /* 3k pull low control for Headset jack. */
  alc_update_coef_idx(codec, 0x4a, 0, 3 << 10);
  msleep(2);

  if (hp1_pin_sense)
   snd_hda_codec_write(codec, hp_pin, 0,
        AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE);
  if (hp2_pin_sense)
   snd_hda_codec_write(codec, 0x16, 0,
        AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE);

  msleep(75);

  if (hp1_pin_sense)
   snd_hda_codec_write(codec, hp_pin, 0,
        AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0);
  if (hp2_pin_sense)
   snd_hda_codec_write(codec, 0x16, 0,
        AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0);

  msleep(75);
  alc_update_coef_idx(codec, 0x4a, 3 << 10, 0);
  alc_enable_headset_jack_key(codec);
 }
 alc_auto_setup_eapd(codec, false);
 alc_shutup_pins(codec);
 if (spec->ultra_low_power) {
  msleep(50);
  alc_update_coef_idx(codec, 0x08, 0x0f << 2, 0x0c << 2);
  alc_update_coef_idx(codec, 0x0e, 7<<6, 0);
  alc_update_coef_idx(codec, 0x33, 1<<11, 1<<11);
  alc_update_coef_idx(codec, 0x4a, 3<<4, 2<<4);
  msleep(30);
 }
}

static void alc222_init(struct hda_codec *codec)
{
 struct alc_spec *spec = codec->spec;
 hda_nid_t hp_pin = alc_get_hp_pin(spec);
 bool hp1_pin_sense, hp2_pin_sense;

 if (!hp_pin)
  return;

 msleep(30);

 hp1_pin_sense = snd_hda_jack_detect(codec, hp_pin);
 hp2_pin_sense = snd_hda_jack_detect(codec, 0x14);

 if (hp1_pin_sense || hp2_pin_sense) {
  msleep(2);

  if (hp1_pin_sense)
   snd_hda_codec_write(codec, hp_pin, 0,
        AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
  if (hp2_pin_sense)
   snd_hda_codec_write(codec, 0x14, 0,
        AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
  msleep(75);

  if (hp1_pin_sense)
   snd_hda_codec_write(codec, hp_pin, 0,
        AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE);
  if (hp2_pin_sense)
   snd_hda_codec_write(codec, 0x14, 0,
        AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE);

  msleep(75);
 }
}

static void alc222_shutup(struct hda_codec *codec)
{
 struct alc_spec *spec = codec->spec;
 hda_nid_t hp_pin = alc_get_hp_pin(spec);
 bool hp1_pin_sense, hp2_pin_sense;

 if (!hp_pin)
  hp_pin = 0x21;

 hp1_pin_sense = snd_hda_jack_detect(codec, hp_pin);
 hp2_pin_sense = snd_hda_jack_detect(codec, 0x14);

 if (hp1_pin_sense || hp2_pin_sense) {
  msleep(2);

  if (hp1_pin_sense)
   snd_hda_codec_write(codec, hp_pin, 0,
        AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE);
  if (hp2_pin_sense)
   snd_hda_codec_write(codec, 0x14, 0,
        AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE);

  msleep(75);

  if (hp1_pin_sense)
   snd_hda_codec_write(codec, hp_pin, 0,
        AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0);
  if (hp2_pin_sense)
   snd_hda_codec_write(codec, 0x14, 0,
        AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0);

  msleep(75);
 }
 alc_auto_setup_eapd(codec, false);
 alc_shutup_pins(codec);
}

static void alc_default_init(struct hda_codec *codec)
{
 struct alc_spec *spec = codec->spec;
 hda_nid_t hp_pin = alc_get_hp_pin(spec);
 bool hp_pin_sense;

 if (!hp_pin)
  return;

 msleep(30);

 hp_pin_sense = snd_hda_jack_detect(codec, hp_pin);

 if (hp_pin_sense) {
  msleep(2);

  snd_hda_codec_write(codec, hp_pin, 0,
        AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);

  msleep(75);

  snd_hda_codec_write(codec, hp_pin, 0,
        AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE);
  msleep(75);
 }
}

static void alc_default_shutup(struct hda_codec *codec)
{
 struct alc_spec *spec = codec->spec;
 hda_nid_t hp_pin = alc_get_hp_pin(spec);
 bool hp_pin_sense;

 if (!hp_pin) {
  alc269_shutup(codec);
  return;
 }

 hp_pin_sense = snd_hda_jack_detect(codec, hp_pin);

 if (hp_pin_sense) {
  msleep(2);

  snd_hda_codec_write(codec, hp_pin, 0,
        AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE);

  msleep(75);

  if (!spec->no_shutup_pins)
   snd_hda_codec_write(codec, hp_pin, 0,
         AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0);

  msleep(75);
 }
 alc_auto_setup_eapd(codec, false);
 alc_shutup_pins(codec);
}

static void alc294_hp_init(struct hda_codec *codec)
{
 struct alc_spec *spec = codec->spec;
 hda_nid_t hp_pin = alc_get_hp_pin(spec);
 int i, val;

 if (!hp_pin)
  return;

 snd_hda_codec_write(codec, hp_pin, 0,
       AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE);

 msleep(100);

 if (!spec->no_shutup_pins)
  snd_hda_codec_write(codec, hp_pin, 0,
        AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0);

 alc_update_coef_idx(codec, 0x6f, 0x000f, 0);/* Set HP depop to manual mode */
 alc_update_coefex_idx(codec, 0x58, 0x00, 0x8000, 0x8000); /* HP depop procedure start */

 /* Wait for depop procedure finish  */
 val = alc_read_coefex_idx(codec, 0x58, 0x01);
 for (i = 0; i < 20 && val & 0x0080; i++) {
  msleep(50);
  val = alc_read_coefex_idx(codec, 0x58, 0x01);
 }
 /* Set HP depop to auto mode */
 alc_update_coef_idx(codec, 0x6f, 0x000f, 0x000b);
 msleep(50);
}

static void alc294_init(struct hda_codec *codec)
{
 struct alc_spec *spec = codec->spec;

 /* required only at boot or S4 resume time */
 if (!spec->done_hp_init ||
     codec->core.dev.power.power_state.event == PM_EVENT_RESTORE) {
  alc294_hp_init(codec);
  spec->done_hp_init = true;
 }
 alc_default_init(codec);
}

static void alc5505_coef_set(struct hda_codec *codec, unsigned int index_reg,
        unsigned int val)
{
 snd_hda_codec_write(codec, 0x51, 0, AC_VERB_SET_COEF_INDEX, index_reg >> 1);
 snd_hda_codec_write(codec, 0x51, 0, AC_VERB_SET_PROC_COEF, val & 0xffff); /* LSB */
 snd_hda_codec_write(codec, 0x51, 0, AC_VERB_SET_PROC_COEF, val >> 16); /* MSB */
}

static int alc5505_coef_get(struct hda_codec *codec, unsigned int index_reg)
{
 unsigned int val;

 snd_hda_codec_write(codec, 0x51, 0, AC_VERB_SET_COEF_INDEX, index_reg >> 1);
 val = snd_hda_codec_read(codec, 0x51, 0, AC_VERB_GET_PROC_COEF, 0)
  & 0xffff;
 val |= snd_hda_codec_read(codec, 0x51, 0, AC_VERB_GET_PROC_COEF, 0)
  << 16;
 return val;
}

static void alc5505_dsp_halt(struct hda_codec *codec)
{
 unsigned int val;

 alc5505_coef_set(codec, 0x3000, 0x000c); /* DSP CPU stop */
 alc5505_coef_set(codec, 0x880c, 0x0008); /* DDR enter self refresh */
 alc5505_coef_set(codec, 0x61c0, 0x11110080); /* Clock control for PLL and CPU */
 alc5505_coef_set(codec, 0x6230, 0xfc0d4011); /* Disable Input OP */
 alc5505_coef_set(codec, 0x61b4, 0x040a2b03); /* Stop PLL2 */
 alc5505_coef_set(codec, 0x61b0, 0x00005b17); /* Stop PLL1 */
 alc5505_coef_set(codec, 0x61b8, 0x04133303); /* Stop PLL3 */
 val = alc5505_coef_get(codec, 0x6220);
 alc5505_coef_set(codec, 0x6220, (val | 0x3000)); /* switch Ringbuffer clock to DBUS clock */
}

static void alc5505_dsp_back_from_halt(struct hda_codec *codec)
{
 alc5505_coef_set(codec, 0x61b8, 0x04133302);
 alc5505_coef_set(codec, 0x61b0, 0x00005b16);
 alc5505_coef_set(codec, 0x61b4, 0x040a2b02);
 alc5505_coef_set(codec, 0x6230, 0xf80d4011);
 alc5505_coef_set(codec, 0x6220, 0x2002010f);
 alc5505_coef_set(codec, 0x880c, 0x00000004);
}

static void alc5505_dsp_init(struct hda_codec *codec)
{
 unsigned int val;

 alc5505_dsp_halt(codec);
 alc5505_dsp_back_from_halt(codec);
 alc5505_coef_set(codec, 0x61b0, 0x5b14); /* PLL1 control */
 alc5505_coef_set(codec, 0x61b0, 0x5b16);
 alc5505_coef_set(codec, 0x61b4, 0x04132b00); /* PLL2 control */
 alc5505_coef_set(codec, 0x61b4, 0x04132b02);
 alc5505_coef_set(codec, 0x61b8, 0x041f3300); /* PLL3 control*/
 alc5505_coef_set(codec, 0x61b8, 0x041f3302);
 snd_hda_codec_write(codec, 0x51, 0, AC_VERB_SET_CODEC_RESET, 0); /* Function reset */
 alc5505_coef_set(codec, 0x61b8, 0x041b3302);
 alc5505_coef_set(codec, 0x61b8, 0x04173302);
 alc5505_coef_set(codec, 0x61b8, 0x04163302);
 alc5505_coef_set(codec, 0x8800, 0x348b328b); /* DRAM control */
 alc5505_coef_set(codec, 0x8808, 0x00020022); /* DRAM control */
 alc5505_coef_set(codec, 0x8818, 0x00000400); /* DRAM control */

 val = alc5505_coef_get(codec, 0x6200) >> 16; /* Read revision ID */
 if (val <= 3)
  alc5505_coef_set(codec, 0x6220, 0x2002010f); /* I/O PAD Configuration */
 else
  alc5505_coef_set(codec, 0x6220, 0x6002018f);

 alc5505_coef_set(codec, 0x61ac, 0x055525f0); /**/
 alc5505_coef_set(codec, 0x61c0, 0x12230080); /* Clock control */
 alc5505_coef_set(codec, 0x61b4, 0x040e2b02); /* PLL2 control */
 alc5505_coef_set(codec, 0x61bc, 0x010234f8); /* OSC Control */
 alc5505_coef_set(codec, 0x880c, 0x00000004); /* DRAM Function control */
 alc5505_coef_set(codec, 0x880c, 0x00000003);
 alc5505_coef_set(codec, 0x880c, 0x00000010);

#ifdef HALT_REALTEK_ALC5505
 alc5505_dsp_halt(codec);
#endif
}

#ifdef HALT_REALTEK_ALC5505
#define alc5505_dsp_suspend(codec) do { } while (0) /* NOP */
#define alc5505_dsp_resume(codec) do { } while (0) /* NOP */
#else
#define alc5505_dsp_suspend(codec) alc5505_dsp_halt(codec)
#define alc5505_dsp_resume(codec) alc5505_dsp_back_from_halt(codec)
#endif

static int alc269_suspend(struct hda_codec *codec)
{
 struct alc_spec *spec = codec->spec;

 if (spec->has_alc5505_dsp)
  alc5505_dsp_suspend(codec);

 return alc_suspend(codec);
}

static int alc269_resume(struct hda_codec *codec)
{
 struct alc_spec *spec = codec->spec;

 if (spec->codec_variant == ALC269_TYPE_ALC269VB)
  alc269vb_toggle_power_output(codec, 0);
 if (spec->codec_variant == ALC269_TYPE_ALC269VB &&
   (alc_get_coef0(codec) & 0x00ff) == 0x018) {
  msleep(150);
 }

 snd_hda_codec_init(codec);

 if (spec->codec_variant == ALC269_TYPE_ALC269VB)
  alc269vb_toggle_power_output(codec, 1);
 if (spec->codec_variant == ALC269_TYPE_ALC269VB &&
   (alc_get_coef0(codec) & 0x00ff) == 0x017) {
  msleep(200);
 }

 snd_hda_regmap_sync(codec);
 hda_call_check_power_status(codec, 0x01);

 /* on some machine, the BIOS will clear the codec gpio data when enter
 * suspend, and won't restore the data after resume, so we restore it
 * in the driver.
 */

 if (spec->gpio_data)
  alc_write_gpio_data(codec);

 if (spec->has_alc5505_dsp)
  alc5505_dsp_resume(codec);

 return 0;
}

static void alc269_fixup_pincfg_no_hp_to_lineout(struct hda_codec *codec,
       const struct hda_fixup *fix, int action)
{
 struct alc_spec *spec = codec->spec;

 if (action == HDA_FIXUP_ACT_PRE_PROBE)
  spec->parse_flags = HDA_PINCFG_NO_HP_FIXUP;
}

static void alc269_fixup_pincfg_U7x7_headset_mic(struct hda_codec *codec,
       const struct hda_fixup *fix,
       int action)
{
 unsigned int cfg_headphone = snd_hda_codec_get_pincfg(codec, 0x21);
 unsigned int cfg_headset_mic = snd_hda_codec_get_pincfg(codec, 0x19);

 if (cfg_headphone && cfg_headset_mic == 0x411111f0)
  snd_hda_codec_set_pincfg(codec, 0x19,
   (cfg_headphone & ~AC_DEFCFG_DEVICE) |
   (AC_JACK_MIC_IN << AC_DEFCFG_DEVICE_SHIFT));
}

static void alc269_fixup_hweq(struct hda_codec *codec,
          const struct hda_fixup *fix, int action)
{
 if (action == HDA_FIXUP_ACT_INIT)
  alc_update_coef_idx(codec, 0x1e, 0, 0x80);
}

static void alc271_fixup_dmic(struct hda_codec *codec,
         const struct hda_fixup *fix, int action)
{
 static const struct hda_verb verbs[] = {
  {0x20, AC_VERB_SET_COEF_INDEX, 0x0d},
  {0x20, AC_VERB_SET_PROC_COEF, 0x4000},
  {}
 };
 unsigned int cfg;

 if (strcmp(codec->core.chip_name, "ALC271X") &&
     strcmp(codec->core.chip_name, "ALC269VB"))
  return;
 cfg = snd_hda_codec_get_pincfg(codec, 0x12);
 if (get_defcfg_connect(cfg) == AC_JACK_PORT_FIXED)
  snd_hda_sequence_write(codec, verbs);
}

/* Fix the speaker amp after resume, etc */
static void alc269vb_fixup_aspire_e1_coef(struct hda_codec *codec,
       const struct hda_fixup *fix,
       int action)
{
 if (action == HDA_FIXUP_ACT_INIT)
  alc_update_coef_idx(codec, 0x0d, 0x6000, 0x6000);
}

static void alc269_fixup_pcm_44k(struct hda_codec *codec,
     const struct hda_fixup *fix, int action)
{
 struct alc_spec *spec = codec->spec;

 if (action != HDA_FIXUP_ACT_PROBE)
  return;

 /* Due to a hardware problem on Lenovo Ideadpad, we need to
 * fix the sample rate of analog I/O to 44.1kHz
 */

 spec->gen.stream_analog_playback = &alc269_44k_pcm_analog_playback;
 spec->gen.stream_analog_capture = &alc269_44k_pcm_analog_capture;
}

static void alc269_fixup_stereo_dmic(struct hda_codec *codec,
         const struct hda_fixup *fix, int action)
{
 /* The digital-mic unit sends PDM (differential signal) instead of
 * the standard PCM, thus you can't record a valid mono stream as is.
 * Below is a workaround specific to ALC269 to control the dmic
 * signal source as mono.
 */

 if (action == HDA_FIXUP_ACT_INIT)
  alc_update_coef_idx(codec, 0x07, 0, 0x80);
}

static void alc269_quanta_automute(struct hda_codec *codec)
{
 snd_hda_gen_update_outputs(codec);

 alc_write_coef_idx(codec, 0x0c, 0x680);
 alc_write_coef_idx(codec, 0x0c, 0x480);
}

static void alc269_fixup_quanta_mute(struct hda_codec *codec,
         const struct hda_fixup *fix, int action)
{
 struct alc_spec *spec = codec->spec;
 if (action != HDA_FIXUP_ACT_PROBE)
  return;
 spec->gen.automute_hook = alc269_quanta_automute;
}

static void alc269_x101_hp_automute_hook(struct hda_codec *codec,
      struct hda_jack_callback *jack)
{
 struct alc_spec *spec = codec->spec;
 int vref;
 msleep(200);
 snd_hda_gen_hp_automute(codec, jack);

 vref = spec->gen.hp_jack_present ? PIN_VREF80 : 0;
 msleep(100);
 snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
       vref);
 msleep(500);
 snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
       vref);
}

/*
 * Magic sequence to make Huawei Matebook X right speaker working (bko#197801)
 */

struct hda_alc298_mbxinit {
 unsigned char value_0x23;
 unsigned char value_0x25;
};

static void alc298_huawei_mbx_stereo_seq(struct hda_codec *codec,
      const struct hda_alc298_mbxinit *initval,
      bool first)
{
 snd_hda_codec_write(codec, 0x06, 0, AC_VERB_SET_DIGI_CONVERT_3, 0x0);
 alc_write_coef_idx(codec, 0x26, 0xb000);

 if (first)
  snd_hda_codec_write(codec, 0x21, 0, AC_VERB_GET_PIN_SENSE, 0x0);

 snd_hda_codec_write(codec, 0x6, 0, AC_VERB_SET_DIGI_CONVERT_3, 0x80);
 alc_write_coef_idx(codec, 0x26, 0xf000);
 alc_write_coef_idx(codec, 0x23, initval->value_0x23);

 if (initval->value_0x23 != 0x1e)
  alc_write_coef_idx(codec, 0x25, initval->value_0x25);

 snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_COEF_INDEX, 0x26);
 snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_PROC_COEF, 0xb010);
}

static void alc298_fixup_huawei_mbx_stereo(struct hda_codec *codec,
        const struct hda_fixup *fix,
        int action)
{
 /* Initialization magic */
 static const struct hda_alc298_mbxinit dac_init[] = {
  {0x0c, 0x00}, {0x0d, 0x00}, {0x0e, 0x00}, {0x0f, 0x00},
  {0x10, 0x00}, {0x1a, 0x40}, {0x1b, 0x82}, {0x1c, 0x00},
  {0x1d, 0x00}, {0x1e, 0x00}, {0x1f, 0x00},
  {0x20, 0xc2}, {0x21, 0xc8}, {0x22, 0x26}, {0x23, 0x24},
  {0x27, 0xff}, {0x28, 0xff}, {0x29, 0xff}, {0x2a, 0x8f},
  {0x2b, 0x02}, {0x2c, 0x48}, {0x2d, 0x34}, {0x2e, 0x00},
  {0x2f, 0x00},
  {0x30, 0x00}, {0x31, 0x00}, {0x32, 0x00}, {0x33, 0x00},
  {0x34, 0x00}, {0x35, 0x01}, {0x36, 0x93}, {0x37, 0x0c},
  {0x38, 0x00}, {0x39, 0x00}, {0x3a, 0xf8}, {0x38, 0x80},
  {}
 };
 const struct hda_alc298_mbxinit *seq;

 if (action != HDA_FIXUP_ACT_INIT)
  return;

 /* Start */
 snd_hda_codec_write(codec, 0x06, 0, AC_VERB_SET_DIGI_CONVERT_3, 0x00);
 snd_hda_codec_write(codec, 0x06, 0, AC_VERB_SET_DIGI_CONVERT_3, 0x80);
 alc_write_coef_idx(codec, 0x26, 0xf000);
 alc_write_coef_idx(codec, 0x22, 0x31);
 alc_write_coef_idx(codec, 0x23, 0x0b);
 alc_write_coef_idx(codec, 0x25, 0x00);
 snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_COEF_INDEX, 0x26);
 snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_PROC_COEF, 0xb010);

 for (seq = dac_init; seq->value_0x23; seq++)
  alc298_huawei_mbx_stereo_seq(codec, seq, seq == dac_init);
}

static void alc269_fixup_x101_headset_mic(struct hda_codec *codec,
         const struct hda_fixup *fix, int action)
{
 struct alc_spec *spec = codec->spec;
 if (action == HDA_FIXUP_ACT_PRE_PROBE) {
  spec->parse_flags |= HDA_PINCFG_HEADSET_MIC;
  spec->gen.hp_automute_hook = alc269_x101_hp_automute_hook;
 }
}

static void alc_update_vref_led(struct hda_codec *codec, hda_nid_t pin,
    bool polarity, bool on)
{
 unsigned int pinval;

 if (!pin)
  return;
 if (polarity)
  on = !on;
 pinval = snd_hda_codec_get_pin_target(codec, pin);
 pinval &= ~AC_PINCTL_VREFEN;
 pinval |= on ? AC_PINCTL_VREF_80 : AC_PINCTL_VREF_HIZ;
 /* temporarily power up/down for setting VREF */
 snd_hda_power_up_pm(codec);
 snd_hda_set_pin_ctl_cache(codec, pin, pinval);
 snd_hda_power_down_pm(codec);
}

/* update mute-LED according to the speaker mute state via mic VREF pin */
static int vref_mute_led_set(struct led_classdev *led_cdev,
        enum led_brightness brightness)
{
 struct hda_codec *codec = dev_to_hda_codec(led_cdev->dev->parent);
 struct alc_spec *spec = codec->spec;

 alc_update_vref_led(codec, spec->mute_led_nid,
       spec->mute_led_polarity, brightness);
 return 0;
}

/* Make sure the led works even in runtime suspend */
static unsigned int led_power_filter(struct hda_codec *codec,
        hda_nid_t nid,
        unsigned int power_state)
{
 struct alc_spec *spec = codec->spec;

 if (power_state != AC_PWRST_D3 || nid == 0 ||
     (nid != spec->mute_led_nid && nid != spec->cap_mute_led_nid))
  return power_state;

 /* Set pin ctl again, it might have just been set to 0 */
 snd_hda_set_pin_ctl(codec, nid,
       snd_hda_codec_get_pin_target(codec, nid));

 return snd_hda_gen_path_power_filter(codec, nid, power_state);
}

static void alc269_fixup_hp_mute_led(struct hda_codec *codec,
         const struct hda_fixup *fix, int action)
{
 struct alc_spec *spec = codec->spec;
 const struct dmi_device *dev = NULL;

 if (action != HDA_FIXUP_ACT_PRE_PROBE)
  return;

 while ((dev = dmi_find_device(DMI_DEV_TYPE_OEM_STRING, NULL, dev))) {
  int pol, pin;
  if (sscanf(dev->name, "HP_Mute_LED_%d_%x", &pol, &pin) != 2)
   continue;
  if (pin < 0x0a || pin >= 0x10)
   break;
  spec->mute_led_polarity = pol;
  spec->mute_led_nid = pin - 0x0a + 0x18;
  snd_hda_gen_add_mute_led_cdev(codec, vref_mute_led_set);
  codec->power_filter = led_power_filter;
  codec_dbg(codec,
     "Detected mute LED for %x:%d\n", spec->mute_led_nid,
      spec->mute_led_polarity);
  break;
 }
}

static void alc269_fixup_hp_mute_led_micx(struct hda_codec *codec,
       const struct hda_fixup *fix,
       int action, hda_nid_t pin)
{
 struct alc_spec *spec = codec->spec;

 if (action == HDA_FIXUP_ACT_PRE_PROBE) {
  spec->mute_led_polarity = 0;
  spec->mute_led_nid = pin;
  snd_hda_gen_add_mute_led_cdev(codec, vref_mute_led_set);
  codec->power_filter = led_power_filter;
 }
}

static void alc269_fixup_hp_mute_led_mic1(struct hda_codec *codec,
    const struct hda_fixup *fix, int action)
{
 alc269_fixup_hp_mute_led_micx(codec, fix, action, 0x18);
}

static void alc269_fixup_hp_mute_led_mic2(struct hda_codec *codec,
    const struct hda_fixup *fix, int action)
{
 alc269_fixup_hp_mute_led_micx(codec, fix, action, 0x19);
}

static void alc269_fixup_hp_mute_led_mic3(struct hda_codec *codec,
    const struct hda_fixup *fix, int action)
{
 alc269_fixup_hp_mute_led_micx(codec, fix, action, 0x1b);
}

static void alc236_fixup_hp_gpio_led(struct hda_codec *codec,
    const struct hda_fixup *fix, int action)
{
 alc_fixup_hp_gpio_led(codec, action, 0x02, 0x01);
}

static void alc269_fixup_hp_gpio_led(struct hda_codec *codec,
    const struct hda_fixup *fix, int action)
{
 alc_fixup_hp_gpio_led(codec, action, 0x08, 0x10);
}

static void alc285_fixup_hp_gpio_led(struct hda_codec *codec,
    const struct hda_fixup *fix, int action)
{
 alc_fixup_hp_gpio_led(codec, action, 0x04, 0x01);
}

static void alc286_fixup_hp_gpio_led(struct hda_codec *codec,
    const struct hda_fixup *fix, int action)
{
 alc_fixup_hp_gpio_led(codec, action, 0x02, 0x20);
}

static void alc287_fixup_hp_gpio_led(struct hda_codec *codec,
    const struct hda_fixup *fix, int action)
{
 alc_fixup_hp_gpio_led(codec, action, 0x10, 0);
}

static void alc245_fixup_hp_gpio_led(struct hda_codec *codec,
    const struct hda_fixup *fix, int action)
{
 struct alc_spec *spec = codec->spec;

 if (action == HDA_FIXUP_ACT_PRE_PROBE)
  spec->micmute_led_polarity = 1;
 alc_fixup_hp_gpio_led(codec, action, 0, 0x04);
}

/* turn on/off mic-mute LED per capture hook via VREF change */
static int vref_micmute_led_set(struct led_classdev *led_cdev,
    enum led_brightness brightness)
{
 struct hda_codec *codec = dev_to_hda_codec(led_cdev->dev->parent);
 struct alc_spec *spec = codec->spec;

 alc_update_vref_led(codec, spec->cap_mute_led_nid,
       spec->micmute_led_polarity, brightness);
 return 0;
}

static void alc269_fixup_hp_gpio_mic1_led(struct hda_codec *codec,
    const struct hda_fixup *fix, int action)
{
 struct alc_spec *spec = codec->spec;

 alc_fixup_hp_gpio_led(codec, action, 0x08, 0);
 if (action == HDA_FIXUP_ACT_PRE_PROBE) {
  /* Like hp_gpio_mic1_led, but also needs GPIO4 low to
 * enable headphone amp
 */

  spec->gpio_mask |= 0x10;
  spec->gpio_dir |= 0x10;
  spec->cap_mute_led_nid = 0x18;
  snd_hda_gen_add_micmute_led_cdev(codec, vref_micmute_led_set);
  codec->power_filter = led_power_filter;
 }
}

static void alc280_fixup_hp_gpio4(struct hda_codec *codec,
       const struct hda_fixup *fix, int action)
{
 struct alc_spec *spec = codec->spec;

 alc_fixup_hp_gpio_led(codec, action, 0x08, 0);
 if (action == HDA_FIXUP_ACT_PRE_PROBE) {
  spec->cap_mute_led_nid = 0x18;
  snd_hda_gen_add_micmute_led_cdev(codec, vref_micmute_led_set);
  codec->power_filter = led_power_filter;
 }
}

/* HP Spectre x360 14 model needs a unique workaround for enabling the amp;
 * it needs to toggle the GPIO0 once on and off at each time (bko#210633)
 */

static void alc245_fixup_hp_x360_amp(struct hda_codec *codec,
         const struct hda_fixup *fix, int action)
{
 struct alc_spec *spec = codec->spec;

 switch (action) {
 case HDA_FIXUP_ACT_PRE_PROBE:
  spec->gpio_mask |= 0x01;
  spec->gpio_dir |= 0x01;
  break;
 case HDA_FIXUP_ACT_INIT:
  /* need to toggle GPIO to enable the amp */
  alc_update_gpio_data(codec, 0x01, true);
  msleep(100);
  alc_update_gpio_data(codec, 0x01, false);
  break;
 }
}

/* toggle GPIO2 at each time stream is started; we use PREPARE state instead */
static void alc274_hp_envy_pcm_hook(struct hda_pcm_stream *hinfo,
        struct hda_codec *codec,
        struct snd_pcm_substream *substream,
        int action)
{
 switch (action) {
 case HDA_GEN_PCM_ACT_PREPARE:
  alc_update_gpio_data(codec, 0x04, true);
  break;
 case HDA_GEN_PCM_ACT_CLEANUP:
  alc_update_gpio_data(codec, 0x04, false);
  break;
 }
}

static void alc274_fixup_hp_envy_gpio(struct hda_codec *codec,
          const struct hda_fixup *fix,
          int action)
{
 struct alc_spec *spec = codec->spec;

 if (action == HDA_FIXUP_ACT_PROBE) {
  spec->gpio_mask |= 0x04;
  spec->gpio_dir |= 0x04;
  spec->gen.pcm_playback_hook = alc274_hp_envy_pcm_hook;
 }
}

static void alc_update_coef_led(struct hda_codec *codec,
    struct alc_coef_led *led,
    bool polarity, bool on)
{
 if (polarity)
  on = !on;
 /* temporarily power up/down for setting COEF bit */
 alc_update_coef_idx(codec, led->idx, led->mask,
       on ? led->on : led->off);
}

/* update mute-LED according to the speaker mute state via COEF bit */
static int coef_mute_led_set(struct led_classdev *led_cdev,
        enum led_brightness brightness)
{
 struct hda_codec *codec = dev_to_hda_codec(led_cdev->dev->parent);
 struct alc_spec *spec = codec->spec;

 alc_update_coef_led(codec, &spec->mute_led_coef,
       spec->mute_led_polarity, brightness);
 return 0;
}

static void alc285_fixup_hp_mute_led_coefbit(struct hda_codec *codec,
       const struct hda_fixup *fix,
       int action)
{
 struct alc_spec *spec = codec->spec;

 if (action == HDA_FIXUP_ACT_PRE_PROBE) {
  spec->mute_led_polarity = 0;
  spec->mute_led_coef.idx = 0x0b;
  spec->mute_led_coef.mask = 1 << 3;
  spec->mute_led_coef.on = 1 << 3;
  spec->mute_led_coef.off = 0;
  snd_hda_gen_add_mute_led_cdev(codec, coef_mute_led_set);
 }
}

static void alc236_fixup_hp_mute_led_coefbit(struct hda_codec *codec,
       const struct hda_fixup *fix,
       int action)
{
 struct alc_spec *spec = codec->spec;

 if (action == HDA_FIXUP_ACT_PRE_PROBE) {
  spec->mute_led_polarity = 0;
  spec->mute_led_coef.idx = 0x34;
  spec->mute_led_coef.mask = 1 << 5;
  spec->mute_led_coef.on = 0;
  spec->mute_led_coef.off = 1 << 5;
  snd_hda_gen_add_mute_led_cdev(codec, coef_mute_led_set);
 }
}

static void alc236_fixup_hp_mute_led_coefbit2(struct hda_codec *codec,
       const struct hda_fixup *fix, int action)
{
 struct alc_spec *spec = codec->spec;

 if (action == HDA_FIXUP_ACT_PRE_PROBE) {
  spec->mute_led_polarity = 0;
  spec->mute_led_coef.idx = 0x07;
  spec->mute_led_coef.mask = 1;
  spec->mute_led_coef.on = 1;
  spec->mute_led_coef.off = 0;
  snd_hda_gen_add_mute_led_cdev(codec, coef_mute_led_set);
 }
}

static void alc245_fixup_hp_mute_led_coefbit(struct hda_codec *codec,
       const struct hda_fixup *fix,
       int action)
{
 struct alc_spec *spec = codec->spec;

 if (action == HDA_FIXUP_ACT_PRE_PROBE) {
  spec->mute_led_polarity = 0;
  spec->mute_led_coef.idx = 0x0b;
  spec->mute_led_coef.mask = 3 << 2;
  spec->mute_led_coef.on = 2 << 2;
  spec->mute_led_coef.off = 1 << 2;
  snd_hda_gen_add_mute_led_cdev(codec, coef_mute_led_set);
 }
}

static void alc245_fixup_hp_mute_led_v1_coefbit(struct hda_codec *codec,
       const struct hda_fixup *fix,
       int action)
{
 struct alc_spec *spec = codec->spec;

 if (action == HDA_FIXUP_ACT_PRE_PROBE) {
  spec->mute_led_polarity = 0;
  spec->mute_led_coef.idx = 0x0b;
  spec->mute_led_coef.mask = 3 << 2;
  spec->mute_led_coef.on = 1 << 3;
  spec->mute_led_coef.off = 0;
  snd_hda_gen_add_mute_led_cdev(codec, coef_mute_led_set);
 }
}

/* turn on/off mic-mute LED per capture hook by coef bit */
static int coef_micmute_led_set(struct led_classdev *led_cdev,
    enum led_brightness brightness)
{
 struct hda_codec *codec = dev_to_hda_codec(led_cdev->dev->parent);
 struct alc_spec *spec = codec->spec;

 alc_update_coef_led(codec, &spec->mic_led_coef,
       spec->micmute_led_polarity, brightness);
 return 0;
}

static void alc285_fixup_hp_coef_micmute_led(struct hda_codec *codec,
    const struct hda_fixup *fix, int action)
{
 struct alc_spec *spec = codec->spec;

 if (action == HDA_FIXUP_ACT_PRE_PROBE) {
  spec->mic_led_coef.idx = 0x19;
  spec->mic_led_coef.mask = 1 << 13;
  spec->mic_led_coef.on = 1 << 13;
  spec->mic_led_coef.off = 0;
  snd_hda_gen_add_micmute_led_cdev(codec, coef_micmute_led_set);
 }
}

static void alc285_fixup_hp_gpio_micmute_led(struct hda_codec *codec,
    const struct hda_fixup *fix, int action)
{
 struct alc_spec *spec = codec->spec;

 if (action == HDA_FIXUP_ACT_PRE_PROBE)
  spec->micmute_led_polarity = 1;
 alc_fixup_hp_gpio_led(codec, action, 0, 0x04);
}

static void alc236_fixup_hp_coef_micmute_led(struct hda_codec *codec,
    const struct hda_fixup *fix, int action)
{
 struct alc_spec *spec = codec->spec;

 if (action == HDA_FIXUP_ACT_PRE_PROBE) {
  spec->mic_led_coef.idx = 0x35;
  spec->mic_led_coef.mask = 3 << 2;
  spec->mic_led_coef.on = 2 << 2;
  spec->mic_led_coef.off = 1 << 2;
  snd_hda_gen_add_micmute_led_cdev(codec, coef_micmute_led_set);
 }
}

static void alc295_fixup_hp_mute_led_coefbit11(struct hda_codec *codec,
    const struct hda_fixup *fix, int action)
{
 struct alc_spec *spec = codec->spec;

 if (action == HDA_FIXUP_ACT_PRE_PROBE) {
  spec->mute_led_polarity = 0;
  spec->mute_led_coef.idx = 0xb;
  spec->mute_led_coef.mask = 3 << 3;
  spec->mute_led_coef.on = 1 << 3;
  spec->mute_led_coef.off = 1 << 4;
  snd_hda_gen_add_mute_led_cdev(codec, coef_mute_led_set);
 }
}

static void alc285_fixup_hp_mute_led(struct hda_codec *codec,
    const struct hda_fixup *fix, int action)
{
 alc285_fixup_hp_mute_led_coefbit(codec, fix, action);
 alc285_fixup_hp_coef_micmute_led(codec, fix, action);
}

static void alc285_fixup_hp_spectre_x360_mute_led(struct hda_codec *codec,
    const struct hda_fixup *fix, int action)
{
 alc285_fixup_hp_mute_led_coefbit(codec, fix, action);
 alc285_fixup_hp_gpio_micmute_led(codec, fix, action);
}

static void alc236_fixup_hp_mute_led(struct hda_codec *codec,
    const struct hda_fixup *fix, int action)
{
 alc236_fixup_hp_mute_led_coefbit(codec, fix, action);
 alc236_fixup_hp_coef_micmute_led(codec, fix, action);
}

static void alc236_fixup_hp_micmute_led_vref(struct hda_codec *codec,
    const struct hda_fixup *fix, int action)
{
 struct alc_spec *spec = codec->spec;

 if (action == HDA_FIXUP_ACT_PRE_PROBE) {
  spec->cap_mute_led_nid = 0x1a;
  snd_hda_gen_add_micmute_led_cdev(codec, vref_micmute_led_set);
  codec->power_filter = led_power_filter;
 }
}

static void alc236_fixup_hp_mute_led_micmute_vref(struct hda_codec *codec,
    const struct hda_fixup *fix, int action)
{
 alc236_fixup_hp_mute_led_coefbit(codec, fix, action);
 alc236_fixup_hp_micmute_led_vref(codec, fix, action);
}

static inline void alc298_samsung_write_coef_pack(struct hda_codec *codec,
        const unsigned short coefs[2])
{
 alc_write_coef_idx(codec, 0x23, coefs[0]);
 alc_write_coef_idx(codec, 0x25, coefs[1]);
 alc_write_coef_idx(codec, 0x26, 0xb011);
}

struct alc298_samsung_amp_desc {
 unsigned char nid;
 unsigned short init_seq[2][2];
};

static void alc298_fixup_samsung_amp(struct hda_codec *codec,
         const struct hda_fixup *fix, int action)
{
 int i, j;
 static const unsigned short init_seq[][2] = {
  { 0x19, 0x00 }, { 0x20, 0xc0 }, { 0x22, 0x44 }, { 0x23, 0x08 },
  { 0x24, 0x85 }, { 0x25, 0x41 }, { 0x35, 0x40 }, { 0x36, 0x01 },
  { 0x38, 0x81 }, { 0x3a, 0x03 }, { 0x3b, 0x81 }, { 0x40, 0x3e },
  { 0x41, 0x07 }, { 0x400, 0x1 }
 };
 static const struct alc298_samsung_amp_desc amps[] = {
  { 0x3a, { { 0x18, 0x1 }, { 0x26, 0x0 } } },
  { 0x39, { { 0x18, 0x2 }, { 0x26, 0x1 } } }
 };

 if (action != HDA_FIXUP_ACT_INIT)
  return;

 for (i = 0; i < ARRAY_SIZE(amps); i++) {
  alc_write_coef_idx(codec, 0x22, amps[i].nid);

  for (j = 0; j < ARRAY_SIZE(amps[i].init_seq); j++)
   alc298_samsung_write_coef_pack(codec, amps[i].init_seq[j]);

  for (j = 0; j < ARRAY_SIZE(init_seq); j++)
   alc298_samsung_write_coef_pack(codec, init_seq[j]);
 }
}

struct alc298_samsung_v2_amp_desc {
 unsigned short nid;
 int init_seq_size;
 unsigned short init_seq[18][2];
};

static const struct alc298_samsung_v2_amp_desc
alc298_samsung_v2_amp_desc_tbl[] = {
 { 0x38, 18, {
  { 0x23e1, 0x0000 }, { 0x2012, 0x006f }, { 0x2014, 0x0000 },
  { 0x201b, 0x0001 }, { 0x201d, 0x0001 }, { 0x201f, 0x00fe },
  { 0x2021, 0x0000 }, { 0x2022, 0x0010 }, { 0x203d, 0x0005 },
  { 0x203f, 0x0003 }, { 0x2050, 0x002c }, { 0x2076, 0x000e },
  { 0x207c, 0x004a }, { 0x2081, 0x0003 }, { 0x2399, 0x0003 },
  { 0x23a4, 0x00b5 }, { 0x23a5, 0x0001 }, { 0x23ba, 0x0094 }
 }},
 { 0x39, 18, {
  { 0x23e1, 0x0000 }, { 0x2012, 0x006f }, { 0x2014, 0x0000 },
  { 0x201b, 0x0002 }, { 0x201d, 0x0002 }, { 0x201f, 0x00fd },
  { 0x2021, 0x0001 }, { 0x2022, 0x0010 }, { 0x203d, 0x0005 },
  { 0x203f, 0x0003 }, { 0x2050, 0x002c }, { 0x2076, 0x000e },
  { 0x207c, 0x004a }, { 0x2081, 0x0003 }, { 0x2399, 0x0003 },
  { 0x23a4, 0x00b5 }, { 0x23a5, 0x0001 }, { 0x23ba, 0x0094 }
 }},
 { 0x3c, 15, {
  { 0x23e1, 0x0000 }, { 0x2012, 0x006f }, { 0x2014, 0x0000 },
  { 0x201b, 0x0001 }, { 0x201d, 0x0001 }, { 0x201f, 0x00fe },
  { 0x2021, 0x0000 }, { 0x2022, 0x0010 }, { 0x203d, 0x0005 },
  { 0x203f, 0x0003 }, { 0x2050, 0x002c }, { 0x2076, 0x000e },
  { 0x207c, 0x004a }, { 0x2081, 0x0003 }, { 0x23ba, 0x008d }
 }},
 { 0x3d, 15, {
  { 0x23e1, 0x0000 }, { 0x2012, 0x006f }, { 0x2014, 0x0000 },
  { 0x201b, 0x0002 }, { 0x201d, 0x0002 }, { 0x201f, 0x00fd },
  { 0x2021, 0x0001 }, { 0x2022, 0x0010 }, { 0x203d, 0x0005 },
  { 0x203f, 0x0003 }, { 0x2050, 0x002c }, { 0x2076, 0x000e },
  { 0x207c, 0x004a }, { 0x2081, 0x0003 }, { 0x23ba, 0x008d }
 }}
};

static void alc298_samsung_v2_enable_amps(struct hda_codec *codec)
{
 struct alc_spec *spec = codec->spec;
 static const unsigned short enable_seq[][2] = {
  { 0x203a, 0x0081 }, { 0x23ff, 0x0001 },
 };
 int i, j;

 for (i = 0; i < spec->num_speaker_amps; i++) {
  alc_write_coef_idx(codec, 0x22, alc298_samsung_v2_amp_desc_tbl[i].nid);
  for (j = 0; j < ARRAY_SIZE(enable_seq); j++)
   alc298_samsung_write_coef_pack(codec, enable_seq[j]);
  codec_dbg(codec, "alc298_samsung_v2: Enabled speaker amp 0x%02x\n",
    alc298_samsung_v2_amp_desc_tbl[i].nid);
 }
}

static void alc298_samsung_v2_disable_amps(struct hda_codec *codec)
{
 struct alc_spec *spec = codec->spec;
 static const unsigned short disable_seq[][2] = {
  { 0x23ff, 0x0000 }, { 0x203a, 0x0080 },
 };
 int i, j;

 for (i = 0; i < spec->num_speaker_amps; i++) {
  alc_write_coef_idx(codec, 0x22, alc298_samsung_v2_amp_desc_tbl[i].nid);
  for (j = 0; j < ARRAY_SIZE(disable_seq); j++)
   alc298_samsung_write_coef_pack(codec, disable_seq[j]);
  codec_dbg(codec, "alc298_samsung_v2: Disabled speaker amp 0x%02x\n",
    alc298_samsung_v2_amp_desc_tbl[i].nid);
 }
}

static void alc298_samsung_v2_playback_hook(struct hda_pcm_stream *hinfo,
    struct hda_codec *codec,
    struct snd_pcm_substream *substream,
    int action)
{
 /* Dynamically enable/disable speaker amps before and after playback */
 if (action == HDA_GEN_PCM_ACT_OPEN)
  alc298_samsung_v2_enable_amps(codec);
 if (action == HDA_GEN_PCM_ACT_CLOSE)
  alc298_samsung_v2_disable_amps(codec);
}

static void alc298_samsung_v2_init_amps(struct hda_codec *codec,
    int num_speaker_amps)
{
 struct alc_spec *spec = codec->spec;
 int i, j;

 /* Set spec's num_speaker_amps before doing anything else */
 spec->num_speaker_amps = num_speaker_amps;

 /* Disable speaker amps before init to prevent any physical damage */
 alc298_samsung_v2_disable_amps(codec);

 /* Initialize the speaker amps */
 for (i = 0; i < spec->num_speaker_amps; i++) {
  alc_write_coef_idx(codec, 0x22, alc298_samsung_v2_amp_desc_tbl[i].nid);
  for (j = 0; j < alc298_samsung_v2_amp_desc_tbl[i].init_seq_size; j++) {
   alc298_samsung_write_coef_pack(codec,
     alc298_samsung_v2_amp_desc_tbl[i].init_seq[j]);
  }
  alc_write_coef_idx(codec, 0x89, 0x0);
  codec_dbg(codec, "alc298_samsung_v2: Initialized speaker amp 0x%02x\n",
    alc298_samsung_v2_amp_desc_tbl[i].nid);
 }

 /* register hook to enable speaker amps only when they are needed */
 spec->gen.pcm_playback_hook = alc298_samsung_v2_playback_hook;
}

static void alc298_fixup_samsung_amp_v2_2_amps(struct hda_codec *codec,
    const struct hda_fixup *fix, int action)
{
 if (action == HDA_FIXUP_ACT_PROBE)
  alc298_samsung_v2_init_amps(codec, 2);
}

static void alc298_fixup_samsung_amp_v2_4_amps(struct hda_codec *codec,
    const struct hda_fixup *fix, int action)
{
 if (action == HDA_FIXUP_ACT_PROBE)
  alc298_samsung_v2_init_amps(codec, 4);
}

static void gpio2_mic_hotkey_event(struct hda_codec *codec,
       struct hda_jack_callback *event)
{
 struct alc_spec *spec = codec->spec;

 /* GPIO2 just toggles on a keypress/keyrelease cycle. Therefore
   send both key on and key off event for every interrupt. */

 input_report_key(spec->kb_dev, spec->alc_mute_keycode_map[ALC_KEY_MICMUTE_INDEX], 1);
 input_sync(spec->kb_dev);
 input_report_key(spec->kb_dev, spec->alc_mute_keycode_map[ALC_KEY_MICMUTE_INDEX], 0);
 input_sync(spec->kb_dev);
}

static int alc_register_micmute_input_device(struct hda_codec *codec)
{
 struct alc_spec *spec = codec->spec;
 int i;

 spec->kb_dev = input_allocate_device();
 if (!spec->kb_dev) {
  codec_err(codec, "Out of memory (input_allocate_device)\n");
  return -ENOMEM;
 }

 spec->alc_mute_keycode_map[ALC_KEY_MICMUTE_INDEX] = KEY_MICMUTE;

 spec->kb_dev->name = "Microphone Mute Button";
 spec->kb_dev->evbit[0] = BIT_MASK(EV_KEY);
 spec->kb_dev->keycodesize = sizeof(spec->alc_mute_keycode_map[0]);
 spec->kb_dev->keycodemax = ARRAY_SIZE(spec->alc_mute_keycode_map);
 spec->kb_dev->keycode = spec->alc_mute_keycode_map;
 for (i = 0; i < ARRAY_SIZE(spec->alc_mute_keycode_map); i++)
  set_bit(spec->alc_mute_keycode_map[i], spec->kb_dev->keybit);

 if (input_register_device(spec->kb_dev)) {
  codec_err(codec, "input_register_device failed\n");
  input_free_device(spec->kb_dev);
  spec->kb_dev = NULL;
  return -ENOMEM;
 }

 return 0;
}

/* GPIO1 = set according to SKU external amp
 * GPIO2 = mic mute hotkey
 * GPIO3 = mute LED
 * GPIO4 = mic mute LED
 */

static void alc280_fixup_hp_gpio2_mic_hotkey(struct hda_codec *codec,
          const struct hda_fixup *fix, int action)
{
 struct alc_spec *spec = codec->spec;

 alc_fixup_hp_gpio_led(codec, action, 0x08, 0x10);
 if (action == HDA_FIXUP_ACT_PRE_PROBE) {
  spec->init_amp = ALC_INIT_DEFAULT;
  if (alc_register_micmute_input_device(codec) != 0)
   return;

  spec->gpio_mask |= 0x06;
  spec->gpio_dir |= 0x02;
  spec->gpio_data |= 0x02;
  snd_hda_codec_write_cache(codec, codec->core.afg, 0,
       AC_VERB_SET_GPIO_UNSOLICITED_RSP_MASK, 0x04);
  snd_hda_jack_detect_enable_callback(codec, codec->core.afg,
          gpio2_mic_hotkey_event);
  return;
 }

 if (!spec->kb_dev)
  return;

 switch (action) {
 case HDA_FIXUP_ACT_FREE:
  input_unregister_device(spec->kb_dev);
  spec->kb_dev = NULL;
 }
}

/* Line2 = mic mute hotkey
 * GPIO2 = mic mute LED
 */

static void alc233_fixup_lenovo_line2_mic_hotkey(struct hda_codec *codec,
          const struct hda_fixup *fix, int action)
{
 struct alc_spec *spec = codec->spec;

 alc_fixup_hp_gpio_led(codec, action, 0, 0x04);
 if (action == HDA_FIXUP_ACT_PRE_PROBE) {
  spec->init_amp = ALC_INIT_DEFAULT;
  if (alc_register_micmute_input_device(codec) != 0)
   return;

  snd_hda_jack_detect_enable_callback(codec, 0x1b,
          gpio2_mic_hotkey_event);
  return;
 }

 if (!spec->kb_dev)
  return;

 switch (action) {
 case HDA_FIXUP_ACT_FREE:
  input_unregister_device(spec->kb_dev);
  spec->kb_dev = NULL;
 }
}

static void alc269_fixup_hp_line1_mic1_led(struct hda_codec *codec,
    const struct hda_fixup *fix, int action)
{
 struct alc_spec *spec = codec->spec;

 alc269_fixup_hp_mute_led_micx(codec, fix, action, 0x1a);
 if (action == HDA_FIXUP_ACT_PRE_PROBE) {
  spec->cap_mute_led_nid = 0x18;
  snd_hda_gen_add_micmute_led_cdev(codec, vref_micmute_led_set);
 }
}

static void alc233_fixup_lenovo_low_en_micmute_led(struct hda_codec *codec,
    const struct hda_fixup *fix, int action)
{
 struct alc_spec *spec = codec->spec;

 if (action == HDA_FIXUP_ACT_PRE_PROBE)
  spec->micmute_led_polarity = 1;
 alc233_fixup_lenovo_line2_mic_hotkey(codec, fix, action);
}

static void alc255_set_default_jack_type(struct hda_codec *codec)
{
 /* Set to iphone type */
 static const struct coef_fw alc255fw[] = {
  WRITE_COEF(0x1b, 0x880b),
  WRITE_COEF(0x45, 0xd089),
  WRITE_COEF(0x1b, 0x080b),
  WRITE_COEF(0x46, 0x0004),
  WRITE_COEF(0x1b, 0x0c0b),
  {}
 };
 static const struct coef_fw alc256fw[] = {
  WRITE_COEF(0x1b, 0x884b),
  WRITE_COEF(0x45, 0xd089),
  WRITE_COEF(0x1b, 0x084b),
  WRITE_COEF(0x46, 0x0004),
  WRITE_COEF(0x1b, 0x0c4b),
  {}
 };
 switch (codec->core.vendor_id) {
 case 0x10ec0255:
  alc_process_coef_fw(codec, alc255fw);
  break;
 case 0x10ec0230:
 case 0x10ec0236:
 case 0x10ec0256:
 case 0x19e58326:
  alc_process_coef_fw(codec, alc256fw);
  break;
 }
 msleep(30);
}

static void alc_fixup_headset_mode_alc255(struct hda_codec *codec,
    const struct hda_fixup *fix, int action)
{
 if (action == HDA_FIXUP_ACT_PRE_PROBE) {
  alc255_set_default_jack_type(codec);
 }
 alc_fixup_headset_mode(codec, fix, action);
}

static void alc_fixup_headset_mode_alc255_no_hp_mic(struct hda_codec *codec,
    const struct hda_fixup *fix, int action)
{
 if (action == HDA_FIXUP_ACT_PRE_PROBE) {
  struct alc_spec *spec = codec->spec;
  spec->parse_flags |= HDA_PINCFG_HEADSET_MIC;
  alc255_set_default_jack_type(codec);
 }
 else
  alc_fixup_headset_mode(codec, fix, action);
}

static void alc288_update_headset_jack_cb(struct hda_codec *codec,
           struct hda_jack_callback *jack)
{
 struct alc_spec *spec = codec->spec;

 alc_update_headset_jack_cb(codec, jack);
 /* Headset Mic enable or disable, only for Dell Dino */
 alc_update_gpio_data(codec, 0x40, spec->gen.hp_jack_present);
}

static void alc_fixup_headset_mode_dell_alc288(struct hda_codec *codec,
    const struct hda_fixup *fix, int action)
{
 alc_fixup_headset_mode(codec, fix, action);
 if (action == HDA_FIXUP_ACT_PROBE) {
  struct alc_spec *spec = codec->spec;
  /* toggled via hp_automute_hook */
  spec->gpio_mask |= 0x40;
  spec->gpio_dir |= 0x40;
  spec->gen.hp_automute_hook = alc288_update_headset_jack_cb;
 }
}

static void alc_fixup_no_shutup(struct hda_codec *codec,
    const struct hda_fixup *fix, int action)
{
 if (action == HDA_FIXUP_ACT_PRE_PROBE) {
  struct alc_spec *spec = codec->spec;
  spec->no_shutup_pins = 1;
 }
}

/* fixup for Thinkpad docks: add dock pins, avoid HP parser fixup */
static void alc_fixup_tpt440_dock(struct hda_codec *codec,
      const struct hda_fixup *fix, int action)
{
 static const struct hda_pintbl pincfgs[] = {
  { 0x16, 0x21211010 }, /* dock headphone */
  { 0x19, 0x21a11010 }, /* dock mic */
  { }
 };
 struct alc_spec *spec = codec->spec;

 if (action == HDA_FIXUP_ACT_PRE_PROBE) {
  spec->parse_flags = HDA_PINCFG_NO_HP_FIXUP;
  codec->power_save_node = 0; /* avoid click noises */
  snd_hda_apply_pincfgs(codec, pincfgs);
 }
}

static void alc_fixup_tpt470_dock(struct hda_codec *codec,
      const struct hda_fixup *fix, int action)
{
 static const struct hda_pintbl pincfgs[] = {
  { 0x17, 0x21211010 }, /* dock headphone */
  { 0x19, 0x21a11010 }, /* dock mic */
  { }
 };
 struct alc_spec *spec = codec->spec;

 if (action == HDA_FIXUP_ACT_PRE_PROBE) {
  spec->parse_flags = HDA_PINCFG_NO_HP_FIXUP;
  snd_hda_apply_pincfgs(codec, pincfgs);
 } else if (action == HDA_FIXUP_ACT_INIT) {
  /* Enable DOCK device */
  snd_hda_codec_write(codec, 0x17, 0,
       AC_VERB_SET_CONFIG_DEFAULT_BYTES_3, 0);
  /* Enable DOCK device */
  snd_hda_codec_write(codec, 0x19, 0,
       AC_VERB_SET_CONFIG_DEFAULT_BYTES_3, 0);
 }
}

static void alc_fixup_tpt470_dacs(struct hda_codec *codec,
      const struct hda_fixup *fix, int action)
{
 /* Assure the speaker pin to be coupled with DAC NID 0x03; otherwise
 * the speaker output becomes too low by some reason on Thinkpads with
 * ALC298 codec
 */

 static const hda_nid_t preferred_pairs[] = {
  0x14, 0x03, 0x17, 0x02, 0x21, 0x02,
  0
 };
 struct alc_spec *spec = codec->spec;

 if (action == HDA_FIXUP_ACT_PRE_PROBE)
  spec->gen.preferred_dacs = preferred_pairs;
}

static void alc295_fixup_asus_dacs(struct hda_codec *codec,
       const struct hda_fixup *fix, int action)
{
 static const hda_nid_t preferred_pairs[] = {
  0x17, 0x02, 0x21, 0x03, 0
 };
 struct alc_spec *spec = codec->spec;

 if (action == HDA_FIXUP_ACT_PRE_PROBE)
  spec->gen.preferred_dacs = preferred_pairs;
}

static void alc271_hp_gate_mic_jack(struct hda_codec *codec,
        const struct hda_fixup *fix,
        int action)
{
 struct alc_spec *spec = codec->spec;

 if (action == HDA_FIXUP_ACT_PROBE) {
  int mic_pin = alc_find_ext_mic_pin(codec);
  int hp_pin = alc_get_hp_pin(spec);

  if (snd_BUG_ON(!mic_pin || !hp_pin))
   return;
  snd_hda_jack_set_gating_jack(codec, mic_pin, hp_pin);
 }
}

static void alc269_fixup_limit_int_mic_boost(struct hda_codec *codec,
          const struct hda_fixup *fix,
          int action)
{
 struct alc_spec *spec = codec->spec;
 struct auto_pin_cfg *cfg = &spec->gen.autocfg;
 int i;

 /* The mic boosts on level 2 and 3 are too noisy
   on the internal mic input.
   Therefore limit the boost to 0 or 1. */


 if (action != HDA_FIXUP_ACT_PROBE)
  return;

 for (i = 0; i < cfg->num_inputs; i++) {
  hda_nid_t nid = cfg->inputs[i].pin;
  unsigned int defcfg;
  if (cfg->inputs[i].type != AUTO_PIN_MIC)
   continue;
  defcfg = snd_hda_codec_get_pincfg(codec, nid);
  if (snd_hda_get_input_pin_attr(defcfg) != INPUT_PIN_ATTR_INT)
   continue;

  snd_hda_override_amp_caps(codec, nid, HDA_INPUT,
       (0x00 << AC_AMPCAP_OFFSET_SHIFT) |
       (0x01 << AC_AMPCAP_NUM_STEPS_SHIFT) |
       (0x2f << AC_AMPCAP_STEP_SIZE_SHIFT) |
       (0 << AC_AMPCAP_MUTE_SHIFT));
 }
}

static void alc283_hp_automute_hook(struct hda_codec *codec,
        struct hda_jack_callback *jack)
{
 struct alc_spec *spec = codec->spec;
 int vref;

 msleep(200);
 snd_hda_gen_hp_automute(codec, jack);

 vref = spec->gen.hp_jack_present ? PIN_VREF80 : 0;

 msleep(600);
 snd_hda_codec_write(codec, 0x19, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
       vref);
}

static void alc283_fixup_chromebook(struct hda_codec *codec,
        const struct hda_fixup *fix, int action)
{
 struct alc_spec *spec = codec->spec;

 switch (action) {
 case HDA_FIXUP_ACT_PRE_PROBE:
  snd_hda_override_wcaps(codec, 0x03, 0);
  /* Disable AA-loopback as it causes white noise */
  spec->gen.mixer_nid = 0;
  break;
 case HDA_FIXUP_ACT_INIT:
  /* MIC2-VREF control */
  /* Set to manual mode */
  alc_update_coef_idx(codec, 0x06, 0x000c, 0);
  /* Enable Line1 input control by verb */
  alc_update_coef_idx(codec, 0x1a, 0, 1 << 4);
  break;
 }
}

static void alc283_fixup_sense_combo_jack(struct hda_codec *codec,
        const struct hda_fixup *fix, int action)
{
 struct alc_spec *spec = codec->spec;

 switch (action) {
 case HDA_FIXUP_ACT_PRE_PROBE:
  spec->gen.hp_automute_hook = alc283_hp_automute_hook;
  break;
 case HDA_FIXUP_ACT_INIT:
  /* MIC2-VREF control */
  /* Set to manual mode */
  alc_update_coef_idx(codec, 0x06, 0x000c, 0);
  break;
 }
}

/* mute tablet speaker pin (0x14) via dock plugging in addition */
static void asus_tx300_automute(struct hda_codec *codec)
{
 struct alc_spec *spec = codec->spec;
 snd_hda_gen_update_outputs(codec);
 if (snd_hda_jack_detect(codec, 0x1b))
  spec->gen.mute_bits |= (1ULL << 0x14);
}

static void alc282_fixup_asus_tx300(struct hda_codec *codec,
        const struct hda_fixup *fix, int action)
{
 struct alc_spec *spec = codec->spec;
 static const struct hda_pintbl dock_pins[] = {
  { 0x1b, 0x21114000 }, /* dock speaker pin */
  {}
 };

 switch (action) {
 case HDA_FIXUP_ACT_PRE_PROBE:
  spec->init_amp = ALC_INIT_DEFAULT;
  /* TX300 needs to set up GPIO2 for the speaker amp */
  alc_setup_gpio(codec, 0x04);
  snd_hda_apply_pincfgs(codec, dock_pins);
  spec->gen.auto_mute_via_amp = 1;
  spec->gen.automute_hook = asus_tx300_automute;
  snd_hda_jack_detect_enable_callback(codec, 0x1b,
          snd_hda_gen_hp_automute);
  break;
 case HDA_FIXUP_ACT_PROBE:
  spec->init_amp = ALC_INIT_DEFAULT;
  break;
 case HDA_FIXUP_ACT_BUILD:
  /* this is a bit tricky; give more sane names for the main
 * (tablet) speaker and the dock speaker, respectively
 */

  rename_ctl(codec, "Speaker Playback Switch",
      "Dock Speaker Playback Switch");
  rename_ctl(codec, "Bass Speaker Playback Switch",
      "Speaker Playback Switch");
  break;
 }
}

static void alc290_fixup_mono_speakers(struct hda_codec *codec,
           const struct hda_fixup *fix, int action)
{
 if (action == HDA_FIXUP_ACT_PRE_PROBE) {
  /* DAC node 0x03 is giving mono output. We therefore want to
   make sure 0x14 (front speaker) and 0x15 (headphones) use the
   stereo DAC, while leaving 0x17 (bass speaker) for node 0x03. */

  static const hda_nid_t conn1[] = { 0x0c };
  snd_hda_override_conn_list(codec, 0x14, ARRAY_SIZE(conn1), conn1);
  snd_hda_override_conn_list(codec, 0x15, ARRAY_SIZE(conn1), conn1);
 }
}

--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=92 H=97 G=94

¤ Dauer der Verarbeitung: 0.28 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge