unsigned int dc_enable;
unsigned int dc_input_bias; /* offset into cxt5066_olpc_dc_bias */
unsigned int mic_boost; /* offset into cxt5066_analog_mic_boost */
+
+ unsigned int beep_amp;
};
static int conexant_playback_pcm_open(struct hda_pcm_stream *hinfo,
{}
};
+#ifdef CONFIG_SND_HDA_INPUT_BEEP
+/* additional beep mixers; the actual parameters are overwritten at build */
+static struct snd_kcontrol_new cxt_beep_mixer[] = {
+ HDA_CODEC_VOLUME_MONO("Beep Playback Volume", 0, 1, 0, HDA_OUTPUT),
+ HDA_CODEC_MUTE_BEEP_MONO("Beep Playback Switch", 0, 1, 0, HDA_OUTPUT),
+ { } /* end */
+};
+#endif
+
static const char *slave_vols[] = {
"Headphone Playback Volume",
"Speaker Playback Volume",
return err;
}
+#ifdef CONFIG_SND_HDA_INPUT_BEEP
+ /* create beep controls if needed */
+ if (spec->beep_amp) {
+ struct snd_kcontrol_new *knew;
+ for (knew = cxt_beep_mixer; knew->name; knew++) {
+ struct snd_kcontrol *kctl;
+ kctl = snd_ctl_new1(knew, codec);
+ if (!kctl)
+ return -ENOMEM;
+ kctl->private_value = spec->beep_amp;
+ err = snd_hda_ctl_add(codec, 0, kctl);
+ if (err < 0)
+ return err;
+ }
+ }
+#endif
+
return 0;
}
.free = conexant_free,
};
+#ifdef CONFIG_SND_HDA_INPUT_BEEP
+#define set_beep_amp(spec, nid, idx, dir) \
+ ((spec)->beep_amp = HDA_COMPOSE_AMP_VAL(nid, 1, idx, dir))
+#else
+#define set_beep_amp(spec, nid, idx, dir) /* NOP */
+#endif
+
/*
* EAPD control
* the private value = nid | (invert << 8)
spec->num_init_verbs = 1;
spec->init_verbs[0] = cxt5045_init_verbs;
spec->spdif_route = 0;
- spec->num_channel_mode = ARRAY_SIZE(cxt5045_modes),
- spec->channel_mode = cxt5045_modes,
+ spec->num_channel_mode = ARRAY_SIZE(cxt5045_modes);
+ spec->channel_mode = cxt5045_modes;
+ set_beep_amp(spec, 0x16, 0, 1);
codec->patch_ops = conexant_patch_ops;
break;
}
+ if (spec->beep_amp)
+ snd_hda_attach_beep_device(codec, spec->beep_amp);
+
return 0;
}
pinctl = (!spec->hp_present && spec->cur_eapd) ? PIN_OUT : 0;
snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
pinctl);
+ /* on ideapad there is an aditional speaker (subwoofer) to mute */
+ if (spec->ideapad)
+ snd_hda_codec_write(codec, 0x1b, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL,
+ pinctl);
}
/* turn on/off EAPD (+ mute HP) as a master switch */
#endif
}
+static struct hda_verb cxt5051_ideapad_init_verbs[] = {
+ /* Subwoofer */
+ {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+ {0x1b, AC_VERB_SET_CONNECT_SEL, 0x00},
+ { } /* end */
+};
+
/* initialize jack-sensing, too */
static int cxt5051_init(struct hda_codec *codec)
{
CXT5051_LENOVO_X200, /* Lenovo X200 laptop, also used for Advanced Mini Dock 250410 */
CXT5051_F700, /* HP Compaq Presario F700 */
CXT5051_TOSHIBA, /* Toshiba M300 & co */
+ CXT5051_IDEAPAD, /* Lenovo IdeaPad Y430 */
CXT5051_MODELS
};
[CXT5051_LENOVO_X200] = "lenovo-x200",
[CXT5051_F700] = "hp-700",
[CXT5051_TOSHIBA] = "toshiba",
+ [CXT5051_IDEAPAD] = "ideapad",
};
static struct snd_pci_quirk cxt5051_cfg_tbl[] = {
CXT5051_LAPTOP),
SND_PCI_QUIRK(0x14f1, 0x5051, "HP Spartan 1.1", CXT5051_HP),
SND_PCI_QUIRK(0x17aa, 0x20f2, "Lenovo X200", CXT5051_LENOVO_X200),
+ SND_PCI_QUIRK(0x17aa, 0x3a0d, "Lenovo IdeaPad", CXT5051_IDEAPAD),
{}
};
spec->cur_adc = 0;
spec->cur_adc_idx = 0;
+ set_beep_amp(spec, 0x13, 0, HDA_OUTPUT);
+
codec->patch_ops.unsol_event = cxt5051_hp_unsol_event;
board_config = snd_hda_check_board_config(codec, CXT5051_MODELS,
spec->mixers[0] = cxt5051_toshiba_mixers;
spec->auto_mic = AUTO_MIC_PORTB;
break;
+ case CXT5051_IDEAPAD:
+ spec->init_verbs[spec->num_init_verbs++] =
+ cxt5051_ideapad_init_verbs;
+ spec->ideapad = 1;
+ break;
}
+ if (spec->beep_amp)
+ snd_hda_attach_beep_device(codec, spec->beep_amp);
+
return 0;
}
.put = cxt5066_mic_boost_mux_enum_put,
.private_value = 0x23 | 0x100,
},
- HDA_CODEC_VOLUME_MONO("Beep Playback Volume", 0x13, 1, 0x0, HDA_OUTPUT),
{}
};
SND_PCI_QUIRK(0x17aa, 0x21b2, "Thinkpad X100e", CXT5066_IDEAPAD),
SND_PCI_QUIRK(0x17aa, 0x21b3, "Thinkpad Edge 13 (197)", CXT5066_IDEAPAD),
SND_PCI_QUIRK(0x17aa, 0x21b4, "Thinkpad Edge", CXT5066_IDEAPAD),
+ SND_PCI_QUIRK(0x17aa, 0x215e, "Lenovo Thinkpad", CXT5066_THINKPAD),
+ SND_PCI_QUIRK(0x17aa, 0x38af, "Lenovo G series", CXT5066_IDEAPAD),
+ SND_PCI_QUIRK(0x17aa, 0x3938, "Lenovo G series (AMD)", CXT5066_IDEAPAD),
SND_PCI_QUIRK(0x17aa, 0x3a0d, "ideapad", CXT5066_IDEAPAD),
- SND_PCI_QUIRK(0x17aa, 0x215e, "Lenovo Thinkpad", CXT5066_THINKPAD),
{}
};
spec->cur_adc = 0;
spec->cur_adc_idx = 0;
+ set_beep_amp(spec, 0x13, 0, HDA_OUTPUT);
+
board_config = snd_hda_check_board_config(codec, CXT5066_MODELS,
cxt5066_models, cxt5066_cfg_tbl);
switch (board_config) {
spec->port_d_mode = 0;
spec->dell_vostro = 1;
spec->mic_boost = 3; /* default 30dB gain */
- snd_hda_attach_beep_device(codec, 0x13);
/* no S/PDIF out */
spec->multiout.dig_out_nid = 0;
break;
}
+ if (spec->beep_amp)
+ snd_hda_attach_beep_device(codec, spec->beep_amp);
+
return 0;
}
ALC882_MODEL_LAST,
};
+/* ALC680 models */
+enum {
+ ALC680_BASE,
+ ALC680_AUTO,
+ ALC680_MODEL_LAST,
+};
+
/* for GPIO Poll */
#define GPIO_MASK 0x03
hda_nid_t *capsrc_nids;
hda_nid_t dig_in_nid; /* digital-in NID; optional */
+ /* capture setup for dynamic dual-adc switch */
+ unsigned int cur_adc_idx;
+ hda_nid_t cur_adc;
+ unsigned int cur_adc_stream_tag;
+ unsigned int cur_adc_format;
+
/* capture source */
unsigned int num_mux_defs;
const struct hda_input_mux *input_mux;
/* other flags */
unsigned int no_analog :1; /* digital I/O only */
+ unsigned int dual_adc_switch:1; /* switch ADCs (for ALC275) */
int init_amp;
/* for virtual master */
return -1;
}
+/* switch the current ADC according to the jack state */
+static void alc_dual_mic_adc_auto_switch(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+ unsigned int present;
+ hda_nid_t new_adc;
+
+ present = snd_hda_jack_detect(codec, spec->ext_mic.pin);
+ if (present)
+ spec->cur_adc_idx = 1;
+ else
+ spec->cur_adc_idx = 0;
+ new_adc = spec->adc_nids[spec->cur_adc_idx];
+ if (spec->cur_adc && spec->cur_adc != new_adc) {
+ /* stream is running, let's swap the current ADC */
+ snd_hda_codec_cleanup_stream(codec, spec->cur_adc);
+ spec->cur_adc = new_adc;
+ snd_hda_codec_setup_stream(codec, new_adc,
+ spec->cur_adc_stream_tag, 0,
+ spec->cur_adc_format);
+ }
+}
+
static void alc_mic_automute(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
if (snd_BUG_ON(!spec->adc_nids))
return;
+ if (spec->dual_adc_switch) {
+ alc_dual_mic_adc_auto_switch(codec);
+ return;
+ }
+
cap_nid = spec->capsrc_nids ? spec->capsrc_nids[0] : spec->adc_nids[0];
present = snd_hda_jack_detect(codec, spec->ext_mic.pin);
return 0;
}
+/* analog capture with dynamic dual-adc changes */
+static int dualmic_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ unsigned int stream_tag,
+ unsigned int format,
+ struct snd_pcm_substream *substream)
+{
+ struct alc_spec *spec = codec->spec;
+ spec->cur_adc = spec->adc_nids[spec->cur_adc_idx];
+ spec->cur_adc_stream_tag = stream_tag;
+ spec->cur_adc_format = format;
+ snd_hda_codec_setup_stream(codec, spec->cur_adc, stream_tag, 0, format);
+ return 0;
+}
+
+static int dualmic_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
+ struct alc_spec *spec = codec->spec;
+ snd_hda_codec_cleanup_stream(codec, spec->cur_adc);
+ spec->cur_adc = 0;
+ return 0;
+}
+
+static struct hda_pcm_stream dualmic_pcm_analog_capture = {
+ .substreams = 1,
+ .channels_min = 2,
+ .channels_max = 2,
+ .nid = 0, /* fill later */
+ .ops = {
+ .prepare = dualmic_capture_pcm_prepare,
+ .cleanup = dualmic_capture_pcm_cleanup
+ },
+};
/*
*/
spec->auto_mic = 0; /* disable auto-mic to be sure */
}
-/* choose the ADC/MUX containing the input pin and initialize the setup */
-static void fixup_single_adc(struct hda_codec *codec)
+/* set the default connection to that pin */
+static int init_capsrc_for_pin(struct hda_codec *codec, hda_nid_t pin)
{
struct alc_spec *spec = codec->spec;
- hda_nid_t pin = 0;
int i;
- /* search for the input pin; there must be only one */
- for (i = 0; i < AUTO_PIN_LAST; i++) {
- if (spec->autocfg.input_pins[i]) {
- pin = spec->autocfg.input_pins[i];
- break;
- }
- }
- if (!pin)
- return;
-
- /* set the default connection to that pin */
for (i = 0; i < spec->num_adc_nids; i++) {
hda_nid_t cap = spec->capsrc_nids ?
spec->capsrc_nids[i] : spec->adc_nids[i];
idx = get_connection_index(codec, cap, pin);
if (idx < 0)
continue;
- /* use only this ADC */
- if (spec->capsrc_nids)
- spec->capsrc_nids += i;
- spec->adc_nids += i;
- spec->num_adc_nids = 1;
/* select or unmute this route */
if (get_wcaps_type(get_wcaps(codec, cap)) == AC_WID_AUD_MIX) {
snd_hda_codec_amp_stereo(codec, cap, HDA_INPUT, idx,
snd_hda_codec_write_cache(codec, cap, 0,
AC_VERB_SET_CONNECT_SEL, idx);
}
+ return i; /* return the found index */
+ }
+ return -1; /* not found */
+}
+
+/* choose the ADC/MUX containing the input pin and initialize the setup */
+static void fixup_single_adc(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+ hda_nid_t pin = 0;
+ int i;
+
+ /* search for the input pin; there must be only one */
+ for (i = 0; i < AUTO_PIN_LAST; i++) {
+ if (spec->autocfg.input_pins[i]) {
+ pin = spec->autocfg.input_pins[i];
+ break;
+ }
+ }
+ if (!pin)
return;
+ i = init_capsrc_for_pin(codec, pin);
+ if (i >= 0) {
+ /* use only this ADC */
+ if (spec->capsrc_nids)
+ spec->capsrc_nids += i;
+ spec->adc_nids += i;
+ spec->num_adc_nids = 1;
}
}
+/* initialize dual adcs */
+static void fixup_dual_adc_switch(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+ init_capsrc_for_pin(codec, spec->ext_mic.pin);
+ init_capsrc_for_pin(codec, spec->int_mic.pin);
+}
+
static void set_capture_mixer(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
};
if (spec->num_adc_nids > 0 && spec->num_adc_nids <= 3) {
int mux = 0;
- if (spec->auto_mic)
+ int num_adcs = spec->num_adc_nids;
+ if (spec->dual_adc_switch)
+ fixup_dual_adc_switch(codec);
+ else if (spec->auto_mic)
fixup_automic_adc(codec);
else if (spec->input_mux) {
if (spec->input_mux->num_items > 1)
else if (spec->input_mux->num_items == 1)
fixup_single_adc(codec);
}
- spec->cap_mixer = caps[mux][spec->num_adc_nids - 1];
+ if (spec->dual_adc_switch)
+ num_adcs = 1;
+ spec->cap_mixer = caps[mux][num_adcs - 1];
}
}
}
#endif /* CONFIG_SND_HDA_POWER_SAVE */
+static int alc275_setup_dual_adc(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+
+ if (codec->vendor_id != 0x10ec0275 || !spec->auto_mic)
+ return 0;
+ if ((spec->ext_mic.pin >= 0x18 && spec->int_mic.pin <= 0x13) ||
+ (spec->ext_mic.pin <= 0x12 && spec->int_mic.pin >= 0x18)) {
+ if (spec->ext_mic.pin <= 0x12) {
+ spec->private_adc_nids[0] = 0x08;
+ spec->private_adc_nids[1] = 0x11;
+ spec->private_capsrc_nids[0] = 0x23;
+ spec->private_capsrc_nids[1] = 0x22;
+ } else {
+ spec->private_adc_nids[0] = 0x11;
+ spec->private_adc_nids[1] = 0x08;
+ spec->private_capsrc_nids[0] = 0x22;
+ spec->private_capsrc_nids[1] = 0x23;
+ }
+ spec->adc_nids = spec->private_adc_nids;
+ spec->capsrc_nids = spec->private_capsrc_nids;
+ spec->num_adc_nids = 2;
+ spec->dual_adc_switch = 1;
+ snd_printdd("realtek: enabling dual ADC switchg (%02x:%02x)\n",
+ spec->adc_nids[0], spec->adc_nids[1]);
+ return 1;
+ }
+ return 0;
+}
+
/*
* BIOS auto configuration
*/
spec->num_mux_defs = 1;
spec->input_mux = &spec->private_imux[0];
- fillup_priv_adc_nids(codec, alc269_adc_candidates,
- sizeof(alc269_adc_candidates));
+
+ if (!alc275_setup_dual_adc(codec))
+ fillup_priv_adc_nids(codec, alc269_adc_candidates,
+ sizeof(alc269_adc_candidates));
/* set default input source */
- snd_hda_codec_write_cache(codec, spec->capsrc_nids[0],
+ if (!spec->dual_adc_switch)
+ snd_hda_codec_write_cache(codec, spec->capsrc_nids[0],
0, AC_VERB_SET_CONNECT_SEL,
spec->input_mux->items[0].index);
*/
spec->stream_analog_playback = &alc269_44k_pcm_analog_playback;
spec->stream_analog_capture = &alc269_44k_pcm_analog_capture;
+ } else if (spec->dual_adc_switch) {
+ spec->stream_analog_playback = &alc269_pcm_analog_playback;
+ /* switch ADC dynamically */
+ spec->stream_analog_capture = &dualmic_pcm_analog_capture;
} else {
spec->stream_analog_playback = &alc269_pcm_analog_playback;
spec->stream_analog_capture = &alc269_pcm_analog_capture;
add_verb(spec, alc662_init_verbs);
if (codec->vendor_id == 0x10ec0272 || codec->vendor_id == 0x10ec0663 ||
- codec->vendor_id == 0x10ec0665)
+ codec->vendor_id == 0x10ec0665 || codec->vendor_id == 0x10ec0670)
add_verb(spec, alc663_init_verbs);
if (codec->vendor_id == 0x10ec0272)
return patch_alc882(codec);
}
+/*
+ * ALC680 support
+ */
+#define ALC680_DIGOUT_NID ALC880_DIGOUT_NID
+#define alc680_modes alc260_modes
+
+static hda_nid_t alc680_dac_nids[3] = {
+ /* Lout1, Lout2, hp */
+ 0x02, 0x03, 0x04
+};
+
+static hda_nid_t alc680_adc_nids[3] = {
+ /* ADC0-2 */
+ /* DMIC, MIC, Line-in*/
+ 0x07, 0x08, 0x09
+};
+
+static struct snd_kcontrol_new alc680_base_mixer[] = {
+ /* output mixer control */
+ HDA_CODEC_VOLUME("Front Playback Volume", 0x2, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE("Front Playback Switch", 0x14, 0x0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME("Headphone Playback Volume", 0x4, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE("Headphone Playback Switch", 0x16, 0x0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
+ { }
+};
+
+static struct snd_kcontrol_new alc680_capture_mixer[] = {
+ HDA_CODEC_VOLUME("Capture Volume", 0x07, 0x0, HDA_INPUT),
+ HDA_CODEC_MUTE("Capture Switch", 0x07, 0x0, HDA_INPUT),
+ HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x08, 0x0, HDA_INPUT),
+ HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x08, 0x0, HDA_INPUT),
+ HDA_CODEC_VOLUME_IDX("Capture Volume", 2, 0x09, 0x0, HDA_INPUT),
+ HDA_CODEC_MUTE_IDX("Capture Switch", 2, 0x09, 0x0, HDA_INPUT),
+ { } /* end */
+};
+
+/*
+ * generic initialization of ADC, input mixers and output mixers
+ */
+static struct hda_verb alc680_init_verbs[] = {
+ /* Unmute DAC0-1 and set vol = 0 */
+ {0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+ {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+ {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+
+ {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
+ {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
+ {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0},
+ {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24},
+ {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
+
+ {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+ {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+ {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},
+ { }
+};
+
+/* create input playback/capture controls for the given pin */
+static int alc680_new_analog_output(struct alc_spec *spec, hda_nid_t nid,
+ const char *ctlname, int idx)
+{
+ hda_nid_t dac;
+ int err;
+
+ switch (nid) {
+ case 0x14:
+ dac = 0x02;
+ break;
+ case 0x15:
+ dac = 0x03;
+ break;
+ case 0x16:
+ dac = 0x04;
+ break;
+ default:
+ return 0;
+ }
+ if (spec->multiout.dac_nids[0] != dac &&
+ spec->multiout.dac_nids[1] != dac) {
+ err = add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, ctlname,
+ HDA_COMPOSE_AMP_VAL(dac, 3, idx,
+ HDA_OUTPUT));
+ if (err < 0)
+ return err;
+
+ err = add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, ctlname,
+ HDA_COMPOSE_AMP_VAL(nid, 3, idx, HDA_OUTPUT));
+
+ if (err < 0)
+ return err;
+ spec->multiout.dac_nids[spec->multiout.num_dacs++] = dac;
+ }
+
+ return 0;
+}
+
+/* add playback controls from the parsed DAC table */
+static int alc680_auto_create_multi_out_ctls(struct alc_spec *spec,
+ const struct auto_pin_cfg *cfg)
+{
+ hda_nid_t nid;
+ int err;
+
+ spec->multiout.dac_nids = spec->private_dac_nids;
+
+ nid = cfg->line_out_pins[0];
+ if (nid) {
+ const char *name;
+ if (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT)
+ name = "Speaker";
+ else
+ name = "Front";
+ err = alc680_new_analog_output(spec, nid, name, 0);
+ if (err < 0)
+ return err;
+ }
+
+ nid = cfg->speaker_pins[0];
+ if (nid) {
+ err = alc680_new_analog_output(spec, nid, "Speaker", 0);
+ if (err < 0)
+ return err;
+ }
+ nid = cfg->hp_pins[0];
+ if (nid) {
+ err = alc680_new_analog_output(spec, nid, "Headphone", 0);
+ if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
+
+static void alc680_auto_set_output_and_unmute(struct hda_codec *codec,
+ hda_nid_t nid, int pin_type)
+{
+ alc_set_pin_output(codec, nid, pin_type);
+}
+
+static void alc680_auto_init_multi_out(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+ hda_nid_t nid = spec->autocfg.line_out_pins[0];
+ if (nid) {
+ int pin_type = get_pin_type(spec->autocfg.line_out_type);
+ alc680_auto_set_output_and_unmute(codec, nid, pin_type);
+ }
+}
+
+static void alc680_auto_init_hp_out(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+ hda_nid_t pin;
+
+ pin = spec->autocfg.hp_pins[0];
+ if (pin)
+ alc680_auto_set_output_and_unmute(codec, pin, PIN_HP);
+ pin = spec->autocfg.speaker_pins[0];
+ if (pin)
+ alc680_auto_set_output_and_unmute(codec, pin, PIN_OUT);
+}
+
+/* pcm configuration: identical with ALC880 */
+#define alc680_pcm_analog_playback alc880_pcm_analog_playback
+#define alc680_pcm_analog_capture alc880_pcm_analog_capture
+#define alc680_pcm_analog_alt_capture alc880_pcm_analog_alt_capture
+#define alc680_pcm_digital_playback alc880_pcm_digital_playback
+
+static struct hda_input_mux alc680_capture_source = {
+ .num_items = 1,
+ .items = {
+ { "Mic", 0x0 },
+ },
+};
+
+/*
+ * BIOS auto configuration
+ */
+static int alc680_parse_auto_config(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+ int err;
+ static hda_nid_t alc680_ignore[] = { 0 };
+
+ err = snd_hda_parse_pin_def_config(codec, &spec->autocfg,
+ alc680_ignore);
+ if (err < 0)
+ return err;
+ if (!spec->autocfg.line_outs) {
+ if (spec->autocfg.dig_outs || spec->autocfg.dig_in_pin) {
+ spec->multiout.max_channels = 2;
+ spec->no_analog = 1;
+ goto dig_only;
+ }
+ return 0; /* can't find valid BIOS pin config */
+ }
+ err = alc680_auto_create_multi_out_ctls(spec, &spec->autocfg);
+ if (err < 0)
+ return err;
+
+ spec->multiout.max_channels = 2;
+
+ dig_only:
+ /* digital only support output */
+ if (spec->autocfg.dig_outs) {
+ spec->multiout.dig_out_nid = ALC680_DIGOUT_NID;
+ spec->dig_out_type = spec->autocfg.dig_out_type[0];
+ }
+ if (spec->kctls.list)
+ add_mixer(spec, spec->kctls.list);
+
+ add_verb(spec, alc680_init_verbs);
+ spec->num_mux_defs = 1;
+ spec->input_mux = &alc680_capture_source;
+
+ err = alc_auto_add_mic_boost(codec);
+ if (err < 0)
+ return err;
+
+ return 1;
+}
+
+#define alc680_auto_init_analog_input alc882_auto_init_analog_input
+
+/* init callback for auto-configuration model -- overriding the default init */
+static void alc680_auto_init(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+ alc680_auto_init_multi_out(codec);
+ alc680_auto_init_hp_out(codec);
+ alc680_auto_init_analog_input(codec);
+ if (spec->unsol_event)
+ alc_inithook(codec);
+}
+
+/*
+ * configuration and preset
+ */
+static const char *alc680_models[ALC680_MODEL_LAST] = {
+ [ALC680_BASE] = "base",
+ [ALC680_AUTO] = "auto",
+};
+
+static struct snd_pci_quirk alc680_cfg_tbl[] = {
+ SND_PCI_QUIRK(0x1043, 0x12f3, "ASUS NX90", ALC680_BASE),
+ {}
+};
+
+static struct alc_config_preset alc680_presets[] = {
+ [ALC680_BASE] = {
+ .mixers = { alc680_base_mixer },
+ .cap_mixer = alc680_capture_mixer,
+ .init_verbs = { alc680_init_verbs },
+ .num_dacs = ARRAY_SIZE(alc680_dac_nids),
+ .dac_nids = alc680_dac_nids,
+ .num_adc_nids = ARRAY_SIZE(alc680_adc_nids),
+ .adc_nids = alc680_adc_nids,
+ .hp_nid = 0x04,
+ .dig_out_nid = ALC680_DIGOUT_NID,
+ .num_channel_mode = ARRAY_SIZE(alc680_modes),
+ .channel_mode = alc680_modes,
+ .input_mux = &alc680_capture_source,
+ },
+};
+
+static int patch_alc680(struct hda_codec *codec)
+{
+ struct alc_spec *spec;
+ int board_config;
+ int err;
+
+ spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+ if (spec == NULL)
+ return -ENOMEM;
+
+ codec->spec = spec;
+
+ board_config = snd_hda_check_board_config(codec, ALC680_MODEL_LAST,
+ alc680_models,
+ alc680_cfg_tbl);
+
+ if (board_config < 0 || board_config >= ALC680_MODEL_LAST) {
+ printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
+ codec->chip_name);
+ board_config = ALC680_AUTO;
+ }
+
+ if (board_config == ALC680_AUTO) {
+ /* automatic parse from the BIOS config */
+ err = alc680_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 = ALC680_BASE;
+ }
+ }
+
+ if (board_config != ALC680_AUTO)
+ setup_preset(codec, &alc680_presets[board_config]);
+
+ spec->stream_analog_playback = &alc680_pcm_analog_playback;
+ spec->stream_analog_capture = &alc680_pcm_analog_capture;
+ spec->stream_analog_alt_capture = &alc680_pcm_analog_alt_capture;
+ spec->stream_digital_playback = &alc680_pcm_digital_playback;
+
+ if (!spec->adc_nids) {
+ spec->adc_nids = alc680_adc_nids;
+ spec->num_adc_nids = ARRAY_SIZE(alc680_adc_nids);
+ }
+
+ if (!spec->cap_mixer)
+ set_capture_mixer(codec);
+
+ spec->vmaster_nid = 0x02;
+
+ codec->patch_ops = alc_patch_ops;
+ if (board_config == ALC680_AUTO)
+ spec->init_hook = alc680_auto_init;
+
+ return 0;
+}
+
/*
* patch entries
*/
{ .id = 0x10ec0663, .name = "ALC663", .patch = patch_alc662 },
{ .id = 0x10ec0665, .name = "ALC665", .patch = patch_alc662 },
{ .id = 0x10ec0670, .name = "ALC670", .patch = patch_alc662 },
+ { .id = 0x10ec0680, .name = "ALC680", .patch = patch_alc680 },
{ .id = 0x10ec0880, .name = "ALC880", .patch = patch_alc880 },
{ .id = 0x10ec0882, .name = "ALC882", .patch = patch_alc882 },
{ .id = 0x10ec0883, .name = "ALC883", .patch = patch_alc882 },