+#define alc268_pcm_analog_playback alc880_pcm_analog_playback
+#define alc268_pcm_analog_capture alc880_pcm_analog_capture
+#define alc268_pcm_analog_alt_capture alc880_pcm_analog_alt_capture
+#define alc268_pcm_digital_playback alc880_pcm_digital_playback
+
+/*
+ * BIOS auto configuration
+ */
+static int alc268_parse_auto_config(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+ int err;
+ static hda_nid_t alc268_ignore[] = { 0 };
+
+ err = snd_hda_parse_pin_def_config(codec, &spec->autocfg,
+ alc268_ignore);
+ if (err < 0)
+ return err;
+ if (!spec->autocfg.line_outs)
+ return 0; /* can't find valid BIOS pin config */
+
+ err = alc268_auto_create_multi_out_ctls(spec, &spec->autocfg);
+ if (err < 0)
+ return err;
+ err = alc268_auto_create_analog_input_ctls(spec, &spec->autocfg);
+ if (err < 0)
+ return err;
+
+ spec->multiout.max_channels = 2;
+
+ /* digital only support output */
+ if (spec->autocfg.dig_out_pin)
+ spec->multiout.dig_out_nid = ALC268_DIGOUT_NID;
+
+ if (spec->kctl_alloc)
+ spec->mixers[spec->num_mixers++] = spec->kctl_alloc;
+
+ spec->init_verbs[spec->num_init_verbs++] = alc268_volume_init_verbs;
+ spec->num_mux_defs = 1;
+ spec->input_mux = &spec->private_imux;
+
+ err = alc_auto_add_mic_boost(codec);
+ if (err < 0)
+ return err;
+
+ return 1;
+}
+
+#define alc268_auto_init_multi_out alc882_auto_init_multi_out
+#define alc268_auto_init_hp_out alc882_auto_init_hp_out
+#define alc268_auto_init_analog_input alc882_auto_init_analog_input
+
+/* init callback for auto-configuration model -- overriding the default init */
+static void alc268_auto_init(struct hda_codec *codec)
+{
+ alc268_auto_init_multi_out(codec);
+ alc268_auto_init_hp_out(codec);
+ alc268_auto_init_mono_speaker_out(codec);
+ alc268_auto_init_analog_input(codec);
+}
+
+/*
+ * configuration and preset
+ */
+static const char *alc268_models[ALC268_MODEL_LAST] = {
+ [ALC268_3ST] = "3stack",
+ [ALC268_TOSHIBA] = "toshiba",
+ [ALC268_ACER] = "acer",
+ [ALC268_DELL] = "dell",
+#ifdef CONFIG_SND_DEBUG
+ [ALC268_TEST] = "test",
+#endif
+ [ALC268_AUTO] = "auto",
+};
+
+static struct snd_pci_quirk alc268_cfg_tbl[] = {
+ SND_PCI_QUIRK(0x1025, 0x0126, "Acer", ALC268_ACER),
+ SND_PCI_QUIRK(0x1025, 0x012e, "Acer Aspire 5310", ALC268_ACER),
+ SND_PCI_QUIRK(0x1025, 0x0130, "Acer Extensa 5210", ALC268_ACER),
+ SND_PCI_QUIRK(0x1025, 0x0136, "Acer Aspire 5315", ALC268_ACER),
+ SND_PCI_QUIRK(0x1028, 0x0253, "Dell OEM", ALC268_DELL),
+ SND_PCI_QUIRK(0x103c, 0x30cc, "TOSHIBA", ALC268_TOSHIBA),
+ SND_PCI_QUIRK(0x1043, 0x1205, "ASUS W7J", ALC268_3ST),
+ SND_PCI_QUIRK(0x1179, 0xff10, "TOSHIBA A205", ALC268_TOSHIBA),
+ SND_PCI_QUIRK(0x1179, 0xff50, "TOSHIBA A305", ALC268_TOSHIBA),
+ SND_PCI_QUIRK(0x152d, 0x0763, "Diverse (CPR2000)", ALC268_ACER),
+ {}
+};
+
+static struct alc_config_preset alc268_presets[] = {
+ [ALC268_3ST] = {
+ .mixers = { alc268_base_mixer, alc268_capture_alt_mixer },
+ .init_verbs = { alc268_base_init_verbs },
+ .num_dacs = ARRAY_SIZE(alc268_dac_nids),
+ .dac_nids = alc268_dac_nids,
+ .num_adc_nids = ARRAY_SIZE(alc268_adc_nids_alt),
+ .adc_nids = alc268_adc_nids_alt,
+ .hp_nid = 0x03,
+ .dig_out_nid = ALC268_DIGOUT_NID,
+ .num_channel_mode = ARRAY_SIZE(alc268_modes),
+ .channel_mode = alc268_modes,
+ .input_mux = &alc268_capture_source,
+ },
+ [ALC268_TOSHIBA] = {
+ .mixers = { alc268_base_mixer, alc268_capture_alt_mixer },
+ .init_verbs = { alc268_base_init_verbs, alc268_eapd_verbs,
+ alc268_toshiba_verbs },
+ .num_dacs = ARRAY_SIZE(alc268_dac_nids),
+ .dac_nids = alc268_dac_nids,
+ .num_adc_nids = ARRAY_SIZE(alc268_adc_nids_alt),
+ .adc_nids = alc268_adc_nids_alt,
+ .hp_nid = 0x03,
+ .num_channel_mode = ARRAY_SIZE(alc268_modes),
+ .channel_mode = alc268_modes,
+ .input_mux = &alc268_capture_source,
+ .unsol_event = alc268_toshiba_unsol_event,
+ .init_hook = alc268_toshiba_automute,
+ },
+ [ALC268_ACER] = {
+ .mixers = { alc268_acer_mixer, alc268_capture_alt_mixer },
+ .init_verbs = { alc268_base_init_verbs, alc268_eapd_verbs,
+ alc268_acer_verbs },
+ .num_dacs = ARRAY_SIZE(alc268_dac_nids),
+ .dac_nids = alc268_dac_nids,
+ .num_adc_nids = ARRAY_SIZE(alc268_adc_nids_alt),
+ .adc_nids = alc268_adc_nids_alt,
+ .hp_nid = 0x02,
+ .num_channel_mode = ARRAY_SIZE(alc268_modes),
+ .channel_mode = alc268_modes,
+ .input_mux = &alc268_capture_source,
+ .unsol_event = alc268_acer_unsol_event,
+ .init_hook = alc268_acer_init_hook,
+ },
+ [ALC268_DELL] = {
+ .mixers = { alc268_dell_mixer },
+ .init_verbs = { alc268_base_init_verbs, alc268_eapd_verbs,
+ alc268_dell_verbs },
+ .num_dacs = ARRAY_SIZE(alc268_dac_nids),
+ .dac_nids = alc268_dac_nids,
+ .hp_nid = 0x02,
+ .num_channel_mode = ARRAY_SIZE(alc268_modes),
+ .channel_mode = alc268_modes,
+ .unsol_event = alc268_dell_unsol_event,
+ .init_hook = alc268_dell_init_hook,
+ .input_mux = &alc268_capture_source,
+ },
+#ifdef CONFIG_SND_DEBUG
+ [ALC268_TEST] = {
+ .mixers = { alc268_test_mixer, alc268_capture_mixer },
+ .init_verbs = { alc268_base_init_verbs, alc268_eapd_verbs,
+ alc268_volume_init_verbs },
+ .num_dacs = ARRAY_SIZE(alc268_dac_nids),
+ .dac_nids = alc268_dac_nids,
+ .num_adc_nids = ARRAY_SIZE(alc268_adc_nids_alt),
+ .adc_nids = alc268_adc_nids_alt,
+ .hp_nid = 0x03,
+ .dig_out_nid = ALC268_DIGOUT_NID,
+ .num_channel_mode = ARRAY_SIZE(alc268_modes),
+ .channel_mode = alc268_modes,
+ .input_mux = &alc268_capture_source,
+ },
+#endif
+};
+
+static int patch_alc268(struct hda_codec *codec)
+{
+ struct alc_spec *spec;
+ int board_config;
+ int err;
+
+ spec = kcalloc(1, sizeof(*spec), GFP_KERNEL);
+ if (spec == NULL)
+ return -ENOMEM;
+
+ codec->spec = spec;
+
+ board_config = snd_hda_check_board_config(codec, ALC268_MODEL_LAST,
+ alc268_models,
+ alc268_cfg_tbl);
+
+ if (board_config < 0 || board_config >= ALC268_MODEL_LAST) {
+ printk(KERN_INFO "hda_codec: Unknown model for ALC268, "
+ "trying auto-probe from BIOS...\n");
+ board_config = ALC268_AUTO;
+ }
+
+ if (board_config == ALC268_AUTO) {
+ /* automatic parse from the BIOS config */
+ err = alc268_parse_auto_config(codec);
+ if (err < 0) {
+ alc_free(codec);
+ return err;
+ } else if (!err) {
+ printk(KERN_INFO
+ "hda_codec: Cannot set up configuration "
+ "from BIOS. Using base mode...\n");
+ board_config = ALC268_3ST;
+ }
+ }
+
+ if (board_config != ALC268_AUTO)
+ setup_preset(spec, &alc268_presets[board_config]);
+
+ spec->stream_name_analog = "ALC268 Analog";
+ spec->stream_analog_playback = &alc268_pcm_analog_playback;
+ spec->stream_analog_capture = &alc268_pcm_analog_capture;
+ spec->stream_analog_alt_capture = &alc268_pcm_analog_alt_capture;
+
+ spec->stream_name_digital = "ALC268 Digital";
+ spec->stream_digital_playback = &alc268_pcm_digital_playback;
+
+ if (!spec->adc_nids && spec->input_mux) {
+ /* check whether NID 0x07 is valid */
+ unsigned int wcap = get_wcaps(codec, 0x07);
+
+ /* get type */
+ wcap = (wcap & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT;
+ if (wcap != AC_WID_AUD_IN) {
+ spec->adc_nids = alc268_adc_nids_alt;
+ spec->num_adc_nids = ARRAY_SIZE(alc268_adc_nids_alt);
+ spec->mixers[spec->num_mixers] =
+ alc268_capture_alt_mixer;
+ spec->num_mixers++;
+ } else {
+ spec->adc_nids = alc268_adc_nids;
+ spec->num_adc_nids = ARRAY_SIZE(alc268_adc_nids);
+ spec->mixers[spec->num_mixers] =
+ alc268_capture_mixer;
+ spec->num_mixers++;
+ }
+ }
+
+ spec->vmaster_nid = 0x02;
+
+ codec->patch_ops = alc_patch_ops;
+ if (board_config == ALC268_AUTO)
+ spec->init_hook = alc268_auto_init;
+
+ return 0;
+}
+
+/*
+ * ALC269 channel source setting (2 channel)
+ */
+#define ALC269_DIGOUT_NID ALC880_DIGOUT_NID
+
+#define alc269_dac_nids alc260_dac_nids
+
+static hda_nid_t alc269_adc_nids[1] = {
+ /* ADC1 */
+ 0x07,
+};
+
+#define alc269_modes alc260_modes
+#define alc269_capture_source alc880_lg_lw_capture_source
+
+static struct snd_kcontrol_new alc269_base_mixer[] = {
+ HDA_CODEC_VOLUME("Front Playback Volume", 0x02, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE("Front Playback Switch", 0x14, 0x0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
+ HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
+ HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
+ HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
+ HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
+ HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x01, HDA_INPUT),
+ HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x01, HDA_INPUT),
+ HDA_CODEC_VOLUME("Front Mic Boost", 0x19, 0, HDA_INPUT),
+ HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x16, 2, 0x0, HDA_OUTPUT),
+ { } /* end */
+};
+
+/* capture mixer elements */
+static struct snd_kcontrol_new alc269_capture_mixer[] = {
+ HDA_CODEC_VOLUME("Capture Volume", 0x07, 0x0, HDA_INPUT),
+ HDA_CODEC_MUTE("Capture Switch", 0x07, 0x0, HDA_INPUT),
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ /* The multiple "Capture Source" controls confuse alsamixer
+ * So call somewhat different..
+ */
+ /* .name = "Capture Source", */
+ .name = "Input Source",
+ .count = 1,
+ .info = alc_mux_enum_info,
+ .get = alc_mux_enum_get,
+ .put = alc_mux_enum_put,
+ },
+ { } /* end */
+};
+
+/*
+ * generic initialization of ADC, input mixers and output mixers
+ */
+static struct hda_verb alc269_init_verbs[] = {
+ /*
+ * Unmute ADC0 and set the default input to mic-in
+ */
+ {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+
+ /* Mute input amps (PCBeep, Line In, Mic 1 & Mic 2) of the
+ * analog-loopback mixer widget
+ * Note: PASD motherboards uses the Line In 2 as the input for
+ * front panel mic (mic 2)
+ */
+ /* Amp Indices: Mic1 = 0, Mic2 = 1, Line1 = 2, Line2 = 3, CD = 4 */
+ {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+ {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+ {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
+ {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
+ {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
+
+ /*
+ * Set up output mixers (0x0c - 0x0e)
+ */
+ /* set vol=0 to output mixers */
+ {0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+ {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+
+ /* set up input amps for analog loopback */
+ /* Amp Indices: DAC = 0, mixer = 1 */
+ {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+ {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+ {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+
+ {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+ {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+ {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+ {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+ {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+ {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+ {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+
+ {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+ {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+ {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+ {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+ {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+ {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+ {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+
+ {0x14, AC_VERB_SET_CONNECT_SEL, 0x00},
+ {0x15, AC_VERB_SET_CONNECT_SEL, 0x00},
+
+ /* FIXME: use matrix-type input source selection */
+ /* Mixer elements: 0x18, 19, 1a, 1b, 1d, 0b */
+ /* Input mixer1: unmute Mic, F-Mic, Line, CD inputs */
+ {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+ {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
+ {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
+
+ /* set EAPD */
+ {0x14, AC_VERB_SET_EAPD_BTLENABLE, 2},
+ {0x15, AC_VERB_SET_EAPD_BTLENABLE, 2},
+ { }
+};
+
+/* add playback controls from the parsed DAC table */
+static int alc269_auto_create_multi_out_ctls(struct alc_spec *spec,
+ const struct auto_pin_cfg *cfg)
+{
+ hda_nid_t nid;
+ int err;
+
+ spec->multiout.num_dacs = 1; /* only use one dac */
+ spec->multiout.dac_nids = spec->private_dac_nids;
+ spec->multiout.dac_nids[0] = 2;
+
+ nid = cfg->line_out_pins[0];
+ if (nid) {
+ err = add_control(spec, ALC_CTL_WIDGET_VOL,
+ "Front Playback Volume",
+ HDA_COMPOSE_AMP_VAL(0x02, 3, 0, HDA_OUTPUT));
+ if (err < 0)
+ return err;
+ err = add_control(spec, ALC_CTL_WIDGET_MUTE,
+ "Front Playback Switch",
+ HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT));
+ if (err < 0)
+ return err;
+ }
+
+ nid = cfg->speaker_pins[0];
+ if (nid) {
+ if (!cfg->line_out_pins[0]) {
+ err = add_control(spec, ALC_CTL_WIDGET_VOL,
+ "Speaker Playback Volume",
+ HDA_COMPOSE_AMP_VAL(0x02, 3, 0,
+ HDA_OUTPUT));
+ if (err < 0)
+ return err;
+ }
+ if (nid == 0x16) {
+ err = add_control(spec, ALC_CTL_WIDGET_MUTE,
+ "Speaker Playback Switch",
+ HDA_COMPOSE_AMP_VAL(nid, 2, 0,
+ HDA_OUTPUT));
+ if (err < 0)
+ return err;
+ } else {
+ err = add_control(spec, ALC_CTL_WIDGET_MUTE,
+ "Speaker Playback Switch",
+ HDA_COMPOSE_AMP_VAL(nid, 3, 0,
+ HDA_OUTPUT));
+ if (err < 0)
+ return err;
+ }
+ }
+ nid = cfg->hp_pins[0];
+ if (nid) {
+ /* spec->multiout.hp_nid = 2; */
+ if (!cfg->line_out_pins[0] && !cfg->speaker_pins[0]) {
+ err = add_control(spec, ALC_CTL_WIDGET_VOL,
+ "Headphone Playback Volume",
+ HDA_COMPOSE_AMP_VAL(0x02, 3, 0,
+ HDA_OUTPUT));
+ if (err < 0)
+ return err;
+ }
+ if (nid == 0x16) {
+ err = add_control(spec, ALC_CTL_WIDGET_MUTE,
+ "Headphone Playback Switch",
+ HDA_COMPOSE_AMP_VAL(nid, 2, 0,
+ HDA_OUTPUT));
+ if (err < 0)
+ return err;
+ } else {
+ err = add_control(spec, ALC_CTL_WIDGET_MUTE,
+ "Headphone Playback Switch",
+ HDA_COMPOSE_AMP_VAL(nid, 3, 0,
+ HDA_OUTPUT));
+ if (err < 0)
+ return err;
+ }
+ }
+ return 0;
+}
+
+#define alc269_auto_create_analog_input_ctls \
+ alc880_auto_create_analog_input_ctls
+
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+#define alc269_loopbacks alc880_loopbacks
+#endif
+
+/* pcm configuration: identiacal with ALC880 */
+#define alc269_pcm_analog_playback alc880_pcm_analog_playback
+#define alc269_pcm_analog_capture alc880_pcm_analog_capture
+#define alc269_pcm_digital_playback alc880_pcm_digital_playback
+#define alc269_pcm_digital_capture alc880_pcm_digital_capture